Commit 17b8f456 by 李君

优化

1 parent a379a142
......@@ -62,7 +62,9 @@ const queueManagementApi = {
getDistributionDetail(params, config) {
return req('get', `/queuing/distribution/detail`, params, config)
},
getCountData(params, config) {
return req('get', `/queuing/countData`, params, config)
},
}
......
......@@ -124,6 +124,16 @@
>{{$t('button.unbind')}}</el-button>
</template>
</el-table-column>
<el-table-column prop="mall.name" v-if="isShowConfig" :label="$t('dialog.video')" align="center">
<template slot-scope="scope">
<el-button
@click="openDialogVideo(scope.row)"
type="text"
size="small"
class="tab-btn"
>{{$t('dialog.video')}}</el-button>
</template>
</el-table-column>
<el-table-column :label="$t('table.operate')" align="center">
<template slot-scope="scope">
<el-button
......@@ -187,7 +197,7 @@
<template-manage ref="tmanage" @refresh="searchFun"></template-manage>
<table-config ref="tabConfig" @confirm="confirmTable"></table-config>
<el-dialog
title="设备"
:title="$t('dialog.deviceDetail')"
:visible.sync="dialogVisible"
v-if="dialogVisible"
top="50px"
......@@ -197,6 +207,17 @@
<el-button @click="dialogVisible = false" class="dialog-btn">{{$t('dialog.close')}}</el-button>
</div>
</el-dialog>
<el-dialog
:title="$t('dialog.video')"
:visible.sync="dialogVideoVisible"
v-if="dialogVideoVisible"
top="50px"
width="90%">
<iframe :style="{height:tableHeight+'px'}" style="width: 100%;margin-top: 10px;margin-top: -10px;" :src="openVideoDialogUrl"></iframe>
<div slot="footer" class="dialog-footer">
<el-button @click="dialogVideoVisible = false" class="dialog-btn">{{$t('dialog.close')}}</el-button>
</div>
</el-dialog>
</div>
</template>
......@@ -269,7 +290,9 @@ export default {
data() {
return {
dialogVisible:false,
dialogVideoVisible:false,
openDialogUrl:'',
openVideoDialogUrl:'',
propsObj:{},
mallListForTerm: [],
floorListForTerm: [],
......@@ -359,7 +382,10 @@ export default {
openDialog(row){
this.dialogVisible = true;
this.openDialogUrl = window._vionConfig.aiotUrl+"/#/vionIndex?id=" +this.toCode(row.serialnum)
console.log(this.openDialogUrl)
},
openDialogVideo(row){
this.dialogVideoVisible = true;
this.openVideoDialogUrl = window._vionConfig.aiotUrl+"/#/videoIndex?id=" +row.serialnum
},
toCode(str1) {
let str = window.btoa(str1);
......
<template>
<el-dialog :title="$t('button.positionCalibration')" width="80%" class="manage-dialog" v-if="bindVisible" :visible.sync="bindVisible" :close-on-click-modal="false" @close="closeBindDialog">
<div class="draw_desks">
<div class="gateWrap" id="gateManageChannel">
<img :src="floorImg" class="fimg" @load="loadGateData">
<div class="grender" ref="gRender"></div>
</div>
</div>
<div slot="footer" class="dialog-footer">
<el-button type="primary" class="dialog-btn dialog-confirm-btn" @click="bindVisible = false">
{{$t('button.closed')}}
</el-button>
</div>
</el-dialog>
</template>
<script>
import drawUtils from './drawUtils';
import _ from 'underscore';
export default {
extends:drawUtils,
data() {
return {
bindVisible: false,
floorImg: '',
width:0,
height:0,
groupList:[],
// zr:'',
areaId:'',
areaObj:{}
}
},
methods: {
dialogInit(row) {
this.areaObj = {...row}
this.areaId = row.id
this.floorImg = window._vionConfig.picUrl+row.pic
this.bindVisible = true;
},
loadGateData() {
let {width,height} = document.getElementById('gateManageChannel').getBoundingClientRect();
this.width = width;
this.height = height;
this.groupList = [];
this.zr = zrender.init(this.$refs.gRender, {
renderer: 'canvas',
devicePixelRatio: 2,
width: width,
height: height
});
window.zr = this.zr;
this.$api.queueManagementApi.getDevices({
areaId: this.areaId
}).then(res=>{
let data = res.data.data
data.forEach(item=>{
item.name = this.areaObj.name;
// item.id = this.areaObj.id;
item.id = item.serialnum;
item.picUrl = window._vionConfig.picUrl + "/snapshot/real/" + item.serialnum + "-01.jpg?t=" + Date.parse(new Date()) / 1000;
item.coordinate&&(item.coordinate=JSON.parse(item.coordinate))
})
this.gateList =data;
this.drawJoinMap()
});
},
drawJoinMap(){
let that = this;
console.log(this.gateList)
_.each(this.gateList,(item,index)=>{
if(item.picUrl){
let img = new Image();
img.src = item.picUrl;
img.onload = function(){
item.width = this.width;
item.height = this.height;
that.createGroup(item,index+1);
}
}
})
},
createGroup(data,index){
let left = 0;
let top = 0;
let that = this;
let group = new zrender.Group({
z:1,
draggable:true,
gateId:data.id,
gateName:data.name,
picUrl:data.picUrl,
position:[left,top]
}).on('mouseup', function (evt) {
that.saveGateData(data.id);
})
this.groupList.push(group);
this.zr.add(group);
window.group = group;
this.drawPolyImage(data,group);
},
closeBindDialog() {
this.bindVisible = false
},
}
}
</script>
<style lang="less" scoped="scoped">
.draw_desks {
position: relative;
width: 100%;
height: 700px;
}
.gateWrap{
position: relative;
width: 100%;
box-shadow: 0px 3px 12px 0px rgba(196, 200, 207, 0.8);
}
.gateWrap .fimg{
display: block;
width: 100%;
}
.gateWrap .grender{
position: absolute;
left: 0;
top: 0;
width:100%;
height: 100%;
opacity: 0.7;
}
</style>
const bwidth = 200;
const count = 2;
const picList = {}; //
const hasRect = false;
import _ from 'underscore';
export default {
methods: {
/**
* 初始化绘制列表
*/
drawPolyImage(data, group) {
let that = this;
picList[data.id] = {};
picList[data.id].imgRatio = bwidth / data.width;
picList[data.id].width = bwidth;
picList[data.id].height = data.height * picList[data.id].imgRatio;
let dots = [
{ x: 1, y: 1 },
{ x: 1 + bwidth, y: 1 },
{ x: 1 + bwidth, y: 1 + picList[data.id].height },
{ x: 1, y: 1 + picList[data.id].height }
];
picList[data.id].dots = zrender.util.clone(dots);
picList[data.id].fdots = dots;
picList[data.id].cameraPoints = [{
x: 0,
y: 0
}, {
x: data.width,
y: 0
}, {
x: data.width,
y: data.height
}, {
x: 0,
y: data.height
}];
picList[data.id].idots = this.rectsplit(count, dots[0], dots[1], dots[2], dots[3]);
let saveDots;
if (data.coordinate && data.coordinate.mapPoints) {
saveDots = _.map(data.coordinate.mapPoints, dot => {
return {
x: 0.01 * dot.x * that.width,
y: 0.01 * dot.y * that.height,
}
})
}
this.drawCircle(data.id, saveDots);
this.setText(data.id);
this.renderPartList(data.id);
},
drawCircle(gateId, sdots) {
let that = this;
let group = _.findWhere(this.groupList, { gateId });
let fdots = picList[gateId].fdots;
let dots = zrender.util.clone(fdots);
let postGateData = _.debounce(that.saveGateData, 100);
let tempZ = group.z + 2;
_.each(dots, (item, index) => {
let circle = new zrender.Circle({
type: 'circle',
cursor: index % 2 ? 'sw-resize' : 'se-resize',
z: tempZ,
shape: {
cx: item.x,
cy: item.y,
r: 20, //(dots[1].x - dots[0].x) / 8
},
style: {
fill: 'transparent',
//stroke: '#f00',
stroke: 'transparent',
}
});
circle.on('mousedown', function(evt1) {
evt1.cancelBubble = true;
let dragable = true;
let downPoint = [evt1.event.zrX, evt1.event.zrY];
let initPoint = zrender.util.clone(this.position);
this.attr('z', 99999);
this.attr('shape', { r: 120 });
this.on('mousemove', function(evt2) {
if (dragable) {
let pos = [evt2.event.zrX - downPoint[0], evt2.event.zrY - downPoint[1]];
let position = [initPoint[0] + pos[0], initPoint[1] + pos[1]];
this.attr('position', position);
picList[gateId].dots[index].x = fdots[index].x + position[0];
picList[gateId].dots[index].y = fdots[index].y + position[1];
that.setText(gateId);
that.renderPartList(gateId);
}
});
this.on('mouseup', function(evt3) {
dragable = false;
postGateData(gateId);
this.attr('z', tempZ);
this.attr('shape', { r: 20 });
//that.saveGateData(gateId)
})
this.on('mouseout', function(evt3) {
dragable = false;
this.attr('z', tempZ);
this.attr('shape', { r: 20 });
})
})
if (sdots) {
let position = [sdots[index].x - circle.shape.cx, sdots[index].y - circle.shape.cy];
circle.attr('position', position);
picList[gateId].dots[index].x = fdots[index].x + position[0];
picList[gateId].dots[index].y = fdots[index].y + position[1];
}
group.add(circle)
});
if (sdots) {
that.setText(gateId);
that.renderPartList(gateId);
}
},
saveGateData(gateId) {
let that = this;
if (this.ajaxing && this.ajaxing == gateId) {
console.info('正在请求')
//return false;
}
this.ajaxing = gateId;
let gateObj = _.findWhere(this.gateList, { id: gateId });
let group = _.findWhere(this.groupList, { gateId });
let mapPoints = gateObj.coordinate && gateObj.coordinate.mapPoints;
let upload = mapPoints ? false : true;
let dots = _.map(picList[gateId].dots, (dot, index) => {
let x = Math.round((group.position[0] + dot.x) * 100 / that.width);
let y = Math.round((group.position[1] + dot.y) * 100 / that.height);
if (!upload && (x != mapPoints[index].x || y != mapPoints[index].y)) {
upload = true
}
return {
x,
y
}
});
gateObj.coordinate = JSON.stringify({
centerPoint: { x: 0, y: 0 },
marginPoint: { x: 0, y: 0 },
cameraPoints: picList[gateId].cameraPoints,
mapPoints: dots
});
let parmas = {
areaId:gateObj.areaId,
serialnum:gateObj.serialnum,
coordinate:gateObj.coordinate,
}
this.$api.queueManagementApi.demarcateFun(parmas).then(res => {
this.ajaxing = false;
});
},
setText(gateId) {
let that = this;
let group = _.findWhere(this.groupList, { gateId });
let gateObj = _.findWhere(this.gateList, { id: gateId });
let dots = picList[gateId].dots;
/*****************************************/
if (group.polygon) group.remove(group.polygon);
group.polygon = new zrender.Polygon({
z: group.z + 1,
style: {
stroke: '#f00',
fill: 'transparent'
},
shape: {
points: dots.map(item => [item.x, item.y])
},
})
group.add(group.polygon)
/*****************************************/
let xAis = [dots[0].x, dots[1].x, dots[2].x, dots[3].x];
let yAis = [dots[0].y, dots[1].y, dots[2].y, dots[3].y];
let [x, y] = [_.max(xAis) - _.min(xAis), _.max(yAis) - _.min(yAis)];
let text = picList[gateId].text;
let name = gateObj.name+'\n' + gateObj.serialnum; //+ '\n' + gateObj.serialnum;
if (text) {
let width = text.getBoundingRect().width;
text.attr('position', [_.min(xAis), _.min(yAis)]);
text.attr('style', {
x: (x - width) / 2,
y: y / 2
});
} else {
text = new zrender.Text({
position: [_.min(xAis), _.min(yAis)],
z: group.z + 1,
//silent: true,
style: {
x: x / 2,
y: y / 2,
textAlign: 'center',
verticalAlign: 'middle',
text: name,
fill: '#f00',
fontSize: 13,
}
})
let width = text.getBoundingRect().width;
text.attr('style', {
x: (x - width) / 2
});
picList[gateId].text = text;
group.add(text);
}
},
/**
* 绘制监控点图像
*/
renderPartList(gateId) {
let group = _.findWhere(this.groupList, { gateId });
group.eachChild(function(item) {
if (item.type == 'image') {
group.remove(item);
}
})
let dots = picList[gateId].dots;
let idots = picList[gateId].idots;
let ndots = this.rectsplit(count, dots[0], dots[1], dots[2], dots[3]);
_.each(ndots, (d, i) => {
//获取平行四边形的四个点
let dot1 = ndots[i];
let dot2 = ndots[i + 1];
let dot3 = ndots[i + count + 2];
let dot4 = ndots[i + count + 1];
//获取初始平行四边形的四个点
let idot1 = idots[i];
let idot2 = idots[i + 1];
let idot3 = idots[i + count + 2];
let idot4 = idots[i + count + 1];
// if ((i + 1) % (count + 1) != 0 && i < (count + 1) * count) {
// this.renderImage(idot3, dot3, idot2, dot2, idot4, dot4, idot1, group);
// }
if (dot2 && dot3 && i % (count + 1) < count) {
//绘制矩形的下半部分
this.renderImage(idot3, dot3, idot2, dot2, idot4, dot4, idot1, group);
//绘制三角形的上半部分
this.renderImage(idot1, dot1, idot2, dot2, idot4, dot4, idot1, group);
}
// if (hasDot) {
// ctx.fillStyle = 'red';
// ctx.fillRect(d.x - 1, d.y - 1, 2, 2);
// }
})
// _.each(group._children, item => {
// if (item.type == 'image') {
// item.attr('style', {
// opacity: 0.9
// })
// }
// })
},
/**
* 计算矩阵,同时渲染图片
* @param arg_1
* @param _arg_1
* @param arg_2
* @param _arg_2
* @param arg_3
* @param _arg_3
*/
renderImage(arg_1, _arg_1, arg_2, _arg_2, arg_3, _arg_3, vertex, group) {
//传入变换前后的点坐标,计算变换矩阵
let result = this.getMatrix.apply(this, arguments);
let imgRatio = picList[group.gateId].imgRatio;
let idots = picList[group.gateId].idots;
let width = picList[group.gateId].width;
let height = picList[group.gateId].height;
let w = width / count;
let h = height / count;
let style = {
image: group.picUrl,
sx: (vertex.x - idots[0].x) / imgRatio - 1,
sy: (vertex.y - idots[0].y) / imgRatio - 1,
sWidth: w / imgRatio + 2,
sHeight: h / imgRatio + 2,
x: vertex.x - 1,
y: vertex.y - 1,
//opacity: 0.1,
width: w + 2,
height: h + 2
};
let image = new zrender.Image({
//invisible: true,
z: group.z,
//silent: true,
type: 'image',
//position: [vertex.x, vertex.y],
style
});
image.setClipPath(new zrender.Polygon({
shape: {
points: [
[arg_1.x, arg_1.y],
[arg_2.x, arg_2.y],
[arg_3.x, arg_3.y]
]
}
}));
image.setLocalTransform([result.a, result.b, result.c, result.d, result.e, result.f]);
//image.setLocalTransform([1, 0.0174551, 0.0559414, 0.8, 79, 8])
image.updateTransform();
group.add(image);
this.zr.add(group);
// //变形
// ctx.transform(result.a, result.b, result.c, result.d, result.e, result.f);
// var w = img.width / count;
// var h = img.height / count;
// //绘制图片
// ctx.drawImage(
// img,
// (vertex.x - idots[0].x) / imgRatio - 1,
// (vertex.y - idots[0].y) / imgRatio - 1,
// w / imgRatio + 2,
// h / imgRatio + 2,
// vertex.x - 1,
// vertex.y - 1,
// w + 2,
// h + 2
// );
},
/**
* 根据变化前后的点坐标,计算矩阵
* @param arg_1 变化前坐标1
* @param _arg_1 变化后坐标1
* @param arg_2 变化前坐标2
* @param _arg_2 变化后坐标2
* @param arg_3 变化前坐标3
* @param _arg_3 变化后坐标3
* @returns {{a: number, b: number, c: number, d: number, e: number, f: number}}
*/
getMatrix(arg_1, _arg_1, arg_2, _arg_2, arg_3, _arg_3) {
//传入x值解第一个方程 即 X = ax + cy + e 求ace
//传入的四个参数,对应三元一次方程:ax+by+cz=d的四个参数:a、b、c、d,跟矩阵方程对比c为1
var arr1 = [arg_1.x, arg_1.y, 1, _arg_1.x];
var arr2 = [arg_2.x, arg_2.y, 1, _arg_2.x];
var arr3 = [arg_3.x, arg_3.y, 1, _arg_3.x];
var result = this.equation(arr1, arr2, arr3);
//传入y值解第二个方程 即 Y = bx + dy + f 求 bdf
arr1[3] = _arg_1.y;
arr2[3] = _arg_2.y;
arr3[3] = _arg_3.y;
var result2 = this.equation(arr1, arr2, arr3);
//获得a、c、e
var a = result.x;
var c = result.y;
var e = result.z;
//获得b、d、f
var b = result2.x;
var d = result2.y;
var f = result2.z;
return {
a: a,
b: b,
c: c,
d: d,
e: e,
f: f
};
},
/**
* 解三元一次方程,需要传入三组方程参数
* @param arr1 第一组参数
* @param arr2 第二组参数
* @param arr3 第三组参数
* @returns {{x: number, y: number, z: number}}
*/
equation(arr1, arr2, arr3) {
var a1 = +arr1[0];
var b1 = +arr1[1];
var c1 = +arr1[2];
var d1 = +arr1[3];
var a2 = +arr2[0];
var b2 = +arr2[1];
var c2 = +arr2[2];
var d2 = +arr2[3];
var a3 = +arr3[0];
var b3 = +arr3[1];
var c3 = +arr3[2];
var d3 = +arr3[3];
//分离计算单元
var m1 = c1 - (b1 * c2 / b2);
var m2 = c2 - (b2 * c3 / b3);
var m3 = d2 - (b2 * d3 / b3);
var m4 = a2 - (b2 * a3 / b3);
var m5 = d1 - (b1 * d2 / b2);
var m6 = a1 - (b1 * a2 / b2);
//计算xyz
var x = ((m1 / m2) * m3 - m5) / ((m1 / m2) * m4 - m6);
var z = (m3 - m4 * x) / m2;
var y = (d1 - a1 * x - c1 * z) / b1;
return {
x: x,
y: y,
z: z
}
},
/**
* 将 abcd 四边形分割成 n 的 n 次方份,获取 n 等分后的所有点坐标
* @param n 多少等分
* @param a a 点坐标
* @param b b 点坐标
* @param c c 点坐标
* @param d d 点坐标
* @returns {Array}
*/
rectsplit(n, a, b, c, d) {
// ad 向量方向 n 等分
var ad_x = (d.x - a.x) / n;
var ad_y = (d.y - a.y) / n;
// bc 向量方向 n 等分
var bc_x = (c.x - b.x) / n;
var bc_y = (c.y - b.y) / n;
var ndots = [];
var x1, y1, x2, y2, ab_x, ab_y;
//左边点递增,右边点递增,获取每一次递增后的新的向量,继续 n 等分,从而获取所有点坐标
for (var i = 0; i <= n; i++) {
//获得 ad 向量 n 等分后的坐标
x1 = a.x + ad_x * i;
y1 = a.y + ad_y * i;
//获得 bc 向量 n 等分后的坐标
x2 = b.x + bc_x * i;
y2 = b.y + bc_y * i;
for (var j = 0; j <= n; j++) {
// ab 向量为:[x2 - x1 , y2 - y1],所以 n 等分后的增量为除于 n
ab_x = (x2 - x1) / n;
ab_y = (y2 - y1) / n;
ndots.push({
x: x1 + ab_x * j,
y: y1 + ab_y * j,
});
}
}
return ndots;
}
}
}
\ No newline at end of file
<template>
<div class="clerk-wrapper queueManagementContainer">
<div class="header manage-head-wrapper">
<el-form ref="form" label-width="100px" inline class="searchForm boxShadow searchFormSocial">
<el-form-item :label="$t('table.mall')">
<el-select v-model="searchForm.mallId" filterable :placeholder="$t('pholder.select')" @change="mallChange">
<el-option v-for="item in mallListForTerm" :key="item.id" :label="item.name" :value="item.id" />
</el-select>
</el-form-item>
<el-form-item :label="$t('table.areaName')">
<el-input v-model="searchForm.name" class="search-inp" :placeholder="$t('pholder.input')" clearable></el-input>
</el-form-item>
<el-form-item>
<el-button type="primary" class="search-btn" size="mini" plain @click="searchFun">{{$t('button.search')}}</el-button>
</el-form-item>
</el-form>
</div>
<div class="manage-content">
<div class="asis-table-content boxShadow" v-loading="loading">
<el-table
:data="tableData"
:max-height="tableHeight"
style="width: 100%;"
ref="row_table"
v-loading="loading"
class="clerk-manage-table"
header-row-class-name="manage-tab-head"
>
<el-table-column :label="$t('table.order')" align="center" type="index" width="100"></el-table-column>
<el-table-column :label="$t('table.areaName')" align="center" prop="name"></el-table-column>
<el-table-column :label="$t('table.areaNumber')" align="center" prop="code"></el-table-column>
<el-table-column :label="$t('table.areaType')" align="center" prop="type">
<template slot-scope="scope">
<span>{{scope.row.type==1?$t('pholder.selfService'):$t('pholder.manualCashier')}}</span>
</template>
</el-table-column>
<el-table-column :label="$t('table.boundDevices')" align="center" prop="count"></el-table-column>
<el-table-column :label="$t('table.operate')" align="center" width="150">
<template slot-scope="scope">
<el-button type="text" class="tab-btn" @click="bindRow(scope.row)">{{$t('button.positionCalibration')}}</el-button>
</template>
</el-table-column>
</el-table>
<el-pagination
class="manage-pagination-box"
background
:current-page="currentPage"
:page-sizes="[10, 20, 50, 100]"
:page-size="pageSize"
@size-change="sizeChange"
@current-change="cerrentChange"
layout="sizes, prev, pager, next, slot"
:total="total"
>
<span
class="slot-ele-total"
>{{$t('table.pageHead')}} {{ total }} {{$t('table.pageTail')}}</span>
</el-pagination>
</div>
</div>
<bind-dev-dialog ref="bindDevModel"></bind-dev-dialog>
</div>
</template>
<script>
import bindDevDialog from "./bindDev";
export default {
data() {
return {
mallListForTerm:[],
tableData: [],
total: 0,
pageSize: 10,
currentPage: 1,
pagesizeArr: [10, 20, 50, 100],
loading: false,
searchForm:{
mallId:'',
name:''
},
tableData:[]
}
},
components:{
bindDevDialog
},
mounted() {
this.getMallListForTerm()
},
computed: {
tableHeight() {
const windowInnerHeight = window.innerHeight;
return windowInnerHeight - windowInnerHeight * 0.24;
},
},
methods: {
getMallListForTerm() {
this.mallListForTerm = [];
this.searchForm.mallId = "";
this.$api.base.mall({
accountId: this.$cookie.get('accountId'),
status_arr: "1,3"
}).then(data => {
let result = data.data;
if (result.data.length) {
if (this.getSessionLocal("mallId")) {
this.searchForm.mallId = Number(this.getSessionLocal("mallId"));
} else {
this.searchForm.mallId = result.data[0].id;
this.setSessionLocal("mallId", this.searchForm.mallId);
}
this.mallListForTerm = result.data;
}
this.getTableData();
})
},
mallChange(val) {
this.setSessionLocal("mallId", val);
this.getTableData();
},
bindRow(row){
this.$refs.bindDevModel.dialogInit(row);
},
searchFun(){
this.currentPage = 1;
this.getTableData()
},
getTableData() {
this.loading = true;
this.tableData = [];
this.$api.queueManagementApi.getAreaList({
name: this.searchForm.name,
mallId: this.searchForm.mallId,
pageNum: this.currentPage,
pageSize: this.pageSize
}).then(res => {
let result = res.data;
if(result.code==200){
this.tableData = result.data.list;
this.total = result.data.total;
}
this.loading = false;
})
},
sizeChange(val) {
this.pageSize = val;
this.getTableData();
},
cerrentChange(val) {
if (this.currentPage != val) {
this.currentPage = val;
this.getTableData();
}
},
}
}
</script>
<style scoped="scoped" lang="less">
.face-img {
width: 50px;
height: 50px;
cursor: zoom-in;
}
.zoomout-img {
width: auto;
height: 300px;
}
</style>
<template>
<div class="painter" ref="painter"></div>
</template>
<script>
import _ from 'underscore';
import zrender from 'zrender';
export default {
props: {
isEdit:Boolean,
cdata: {
type: Object,
default: {}
},
list:Array,
desk:Object
},
watch: {
cdata: {
immediate: true,
handler(nval) {
if(JSON.stringify(nval) == '{}'){
return false
}
setTimeout(()=>{
if(!_.isEmpty(nval))this.initZrender();
},500);
},
deep: true
}
},
data() {
return {
pointList: [],//当前绘制的图形点位列表
groupList: [], //图形数组
zr: {}, //绘图实例对象
baseColor: '#ff000075',
strokeColor: '#f00',
/*********************************/
fillColor: '#f00',
scaleVal: 10, //缩放
rotateVal: 0, //旋转角度
tempData: {},
/*********************************/
groupObj: {}
}
},
methods: {
initZrender() {
this.$nextTick(() => {
let client = document.querySelector('.painter').getBoundingClientRect();
this.zr = zrender.init(this.$refs.painter, {
renderer: 'canvas',
devicePixelRatio: 2,
width: client.width,
height: client.height
});
this.bindingEvt();
this.initialGroup();
})
},
initialGroup(){
let list = this.list
_.each(list,item=>{
let gdata = zrender.util.clone(item);
if(gdata.coordinate){
gdata.points = _.map(JSON.parse(gdata.coordinate),point=>{
let x = point.x*this.zr.getWidth()/1920;
let y = point.y*this.zr.getHeight()/1080;
return [x,y];
});
gdata.desk = item;
this.createGroup(gdata,true);
}
})
},
bindingEvt() {
let that = this;
let group;
this.zr.on('click', evt => {
if(!this.desk||_.isEmpty(this.desk)){
return this.$message({
showClose: true,
type: "warning",
message: '请先点击选择通道'
})
}
this.$emit('start',this.desk);
let point = [evt.event.zrX, evt.event.zrY];
this.pointList.push(point);
if (this.pointList.length == 1) { //第一次点击,创建Group
let points = zrender.util.clone(this.pointList);
group = this.createGroup({ points, fill: that.baseColor,desk:this.desk});
}
this.drawPolyline(group, this.pointList);
})
this.zr.on('mousemove', evt => {
if (this.pointList.length == 0) return false;
let point = [evt.event.zrX, evt.event.zrY];
this.drawPolyline(group, this.pointList.concat([point]));
});
},
drawPolyline(group, points) {
group.polyline.setShape({
points: points
});
group.points = zrender.util.clone(points);
},
createGroup(gdata,isFull=false) {
let that = this;
let points = gdata.points;
let fill = gdata.fill;
let position = gdata.position;
/*******************************/
let group = new zrender.Group({
complete: false,
position: position || [0, 0]
});
if(isFull){
group.points = points;
}
group.desk = gdata.desk;
group.cdata = this.cdata;
group.zr = this.zr;
let polyline = this.createPolyline({
stroke: that.strokeColor,
fill: fill,
points: points
})
group.id = gdata.id || Date.now().toString(36);
group.add(polyline);
group.polyline = polyline;
group.fill = gdata.fill;
group.data = gdata;
if (this.isEdit && points.length == 1) {
let circle = that.createCircle(group, points[0]);
group.add(circle)
} else if(isFull){
this.drawText(group, false);
}
group.on('click', function (evt) {
if (!that.isEdit) {
evt.cancelBubble = true;
return false;
}
if(group.complete){
that.$emit('gclick',group);
evt.cancelBubble = true;
return false;
}
if (that.pointList.length == 1) {
//evt.cancelBubble = true;
}
});
let action = this.$refs.action;
/*action.onmouseleave = ()=>{
action.style.display = 'none'
}*/
group.on('contextmenu', function (evt) {
if (!that.isEdit) {
evt.stop();
return false
}
if (that.pointList.length > 0) {
evt.stop();
return false;
}
action.style.display = 'block';
action.style.left = (evt.offsetX - 5) + 'px'
action.style.top = (evt.offsetY - 5) + 'px'
evt.stop();
that.setCurrentGroup(this);
return false;
})
this.zr.add(group);
this.groupList.push(group);
return group;
},
createCircle(group, point) {
let that = this;
let circle = new zrender.Circle({
style: {
fill: 'transparent',
stroke: '#f00',
lineWidth: 2
},
shape: {
cx: point[0],
cy: point[1],
r: 10
}
});
circle.on('click', evt => {
evt.cancelBubble = true;
that.pointList.push(that.pointList[0]);
group.remove(circle);
if (that.pointList.length > 3) {
this.drawPolyline(group, that.pointList);
this.drawText(group,true);
} else {
this.zr.remove(group)
}
that.pointList = [];
});
return circle;
},
createPolyline(params) {
let that = this;
return new zrender.Polyline({
x: 0,
y: 0,
origin: [0, 0],
//cursor:'move',
draggable: false,
style: {
stroke: params.stroke,
fill: params.fill || that.baseColor,
lineWidth: 3
},
shape: {
points: params.points,
smooth: 0.01
}
});
},
drawText(group,finish) {
let that = this,xPoints=[],yPoints=[];
_.each(group.points,item=>{
xPoints.push(item[0]);
yPoints.push(item[1]);
})
let [x, y] = [_.max(xPoints) - _.min(xPoints), _.max(yPoints) - _.min(yPoints)];
let client = group.getBoundingRect();
let content = `{name|${group.desk.name || ''}}`;
let text = new zrender.Text({
position: [client.x, client.y],
style: {
x: client.width / 2,
y: client.height / 2,
width: client.width,
height: client.height,
textAlign: 'center',
textVerticalAlign: 'middle',
//textShadowBlur: 3,
textShadowColor: '#893e95',
//textShadowOffsetX: 3,
//textShadowOffsetY: 3,
text: content,
fill: '#fff',
fontSize: 14,
fontWeight:600,
rich: {
name: {
textLineHeight: 16,
textFill: '#fff'
}
}
}
});
//text.setClipPath(curGroup.polyline)
//this.setDragaAble(text)
group.add(text);
group.text = text;
group.complete = true;//图像已经绘制完毕
group.polyline.attr({
origin: [client.x + client.width / 2, client.y + client.height / 2]
});
if (that.isEdit) {
that.setDragaAble(group);
}
this.$emit('finish',group);
},
setCurrentGroup(group) {
this.groupObj = _.find(this.groupList, item => {
return item.id == group.id;
});
this.fillColor = this.groupObj.polyline.style.fill;
},
setDragaAble(el, popup) {
let isDown = false,
downPoint = [],
initPoint = [];
el.on('mousedown', evt => {
isDown = true;
evt.cancelBubble = true;
downPoint = [evt.event.zrX, evt.event.zrY];
initPoint = zrender.util.clone(el.position);
evt.cancelBubble = true;
});
el.on('mousemove', evt => {
if (isDown) {
evt.cancelBubble = true;
let pos = [evt.event.zrX - downPoint[0], evt.event.zrY - downPoint[1]];
el.attr('position', [initPoint[0] + pos[0], initPoint[1] + pos[1]]);
}
});
el.on('mouseup', evt => {
evt.cancelBubble = true;
isDown = false;
});
this.zr.on('mouseup', function () {
isDown = false;
})
},
}
}
</script>
<style scoped lang="less">
.painter {
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
}
</style>
<template>
<div>
<el-dialog :title="isEdit=='add'?$t('button.groupAdd'):$t('button.groupEdit')" class="manage-dialog dialog_lj" :visible.sync="addDialogVisible"
:close-on-click-modal="false" v-if="addDialogVisible" @close="addDialogClose()">
<el-form :model="addForm" label-width="100px" status-icon :rules="rules" ref="addForm">
<!-- 区域名称 -->
<el-form-item :label="$t('table.laneName')" prop="name">
<el-input v-model="addForm.name" :placeholder="$t('pholder.input')"></el-input>
<i class="error-tip">*</i>
</el-form-item>
<el-form-item :label="$t('table.channelCode')" prop="code">
<el-input v-model="addForm.code" :placeholder="$t('pholder.input')"></el-input>
<i class="error-tip">*</i>
</el-form-item>
<el-form-item :label="$t('table.counterType')" prop="counterType">
<el-select v-model="addForm.counterType" :placeholder="$t('pholder.select')">
<el-option v-for="item in counterTypeData" :key="item.key" :label="item.value" :value="item.key" />
</el-select>
<i class="error-tip">*</i>
</el-form-item>
<el-form-item :label="$t('table.areaName')" prop="areaId">
<el-select v-model="addForm.areaId" :placeholder="$t('pholder.areaSelect')">
<el-option v-for="item in areaListData" :key="item.id" :label="item.name" :value="item.id" />
</el-select>
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button @click="addDialogVisible = false" class="dialog-btn">{{ $t('dialog.cancel') }}</el-button>
<el-button type="primary" @click="addSubmit('addForm')" class="dialog-btn dialog-confirm-btn">
{{ $t('dialog.confirm') }}</el-button>
</div>
</el-dialog>
</div>
</template>
<script>
export default {
data() {
var checkName = (rule, value, callback) => {
//var flag = new RegExp("[`’\"·_ ~!%\\-@#$^&*=|{}':;',\\[\\].<>《》/?~!@#¥……&*——|{}【】‘;:”“'。,、? ]");
var flag = new RegExp("'");
if (flag.test(value) || /\\/.test(value)) {
callback(new Error("名称包含特殊字符,请重新输入!"));
} else {
callback();
}
}
return {
isEn: localStorage.getItem('lang') == 'en_US',
counterTypeData:[],
addDialogVisible: false,
areaListData:[],
addForm: {
name: '',
areaId:"",
code:'',
counterType:''
},
isEdit:false,
rules: {
name: [{
required: true,
message: this.$t('pholder.input'),
trigger: 'blur'
},{
validator: checkName,
trigger: "blur"
}],
code:[{
required: true,
message: this.$t('pholder.input'),
trigger: 'blur'
}],
counterType:[{
required: true,
message: this.$t('pholder.select'),
trigger: 'blur'
}]
}
}
},
methods: {
dialogInit(type,data) {
this.getCounterType()
this.addForm = {
name: '',
areaId:"",
code:'',
counterType:''
};
this.isEdit = type
if(data){
this.addForm = Object.assign({},data);
this.$forceUpdate()
}
this.getAreaList()
this.addDialogVisible = true;
},
getCounterType(){
this.$api.base.dataDic({
type: 'counterType'
}).then(res => {
this.counterTypeData = res.data.data.map(item=>{
if(this.isEn){
item.value = item.valueEn;
}
return item;
});
})
},
getAreaList(){
this.areaListData = [];
this.$api.queueManagementApi.getAreaList({
mallId: Number(this.getSessionLocal("mallId")),
pageNum: 1,
pageSize: 999999
}).then(res => {
let result = res.data;
if(result.code==200){
if(result.data.list&& result.data.list.length>0){
if(this.isEdit=='add'){
this.addForm.areaId = result.data.list[0].id
}
this.areaListData = result.data.list;
}
}
})
},
addSubmit(formName) {
this.$refs[formName].validate((valid) => {
if (valid) {
if(this.isEdit=='add'){
this.$api.queueManagementApi.addChannel(this.addForm)
.then((res) => {
let result = res.data;
if(result.code==200){
this.$message({
message: result.msg,
type: 'success'
});
this.$parent.getTableData();
this.$refs.addForm.resetFields();
this.addForm = {}
this.addDialogVisible = false
}else{
this.$message({
message: result.msg,
type: 'error'
});
}
})
}else{
this.$api.queueManagementApi.updateChannel(this.addForm).then((res) => {
let result = res.data;
if(result.code==200){
this.$message({
message: result.msg,
type: 'success'
});
this.$parent.getTableData();
this.$refs.addForm.resetFields();
this.addForm = {}
this.addDialogVisible = false
}else{
this.$message({
message: result.msg,
type: 'error'
});
}
})
}
// this.$emit('submit',this.addForm)
} else {
return false;
}
});
},
addDialogClose() {
this.addDialogVisible = false;
this.$refs.addForm.resetFields();
},
}
}
</script>
<style scoped lang="less">
.dialog_lj{
/deep/.el-dialog__body{
height: 270px;
}
/deep/.el-form-item__label{
line-height: 30px;
height: 30px;
text-align:left
}
/deep/.el-select{
width: 100%;
}
/deep/.el-form-item__label+.el-form-item__content{
float: none !important;
line-height:40px !important;
}
/deep/.el-form-item__error{
padding-top: 0px !important;
}
}
</style>
<template>
<div class="queueManagementContainer clerk-wrapper">
<div class="header manage-head-wrapper">
<el-form ref="form" label-width="100px" inline class="searchForm boxShadow searchFormSocial">
<el-form-item :label="$t('table.mall')">
<el-select v-model="searchForm.mallId" filterable :placeholder="$t('pholder.select')" @change="mallChange">
<el-option v-for="item in mallListForTerm" :key="item.id" :label="item.name" :value="item.id" />
</el-select>
</el-form-item>
<el-form-item :label="$t('table.areaName')">
<el-select v-model="searchForm.areaId" :placeholder="$t('pholder.areaSelect')" @change="areaChange">
<el-option v-for="item in areaListData" :key="item.id" :label="item.name" :value="item.id" />
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" class="search-btn" plain size="mini" @click="searchFun">{{ $t('button.search')}}</el-button>
</el-form-item>
<div class="btns">
<el-button type="primary" size="mini" class="manage-add-btn fl-btn" @click="addDesk">{{$t('button.groupAdd')}}</el-button>
</div>
</el-form>
</div>
<el-row class="manage-content">
<el-col :span="24" class="treeMenu boxShadow">
<el-table ref="singleTable" @row-click="setCurrent" :data="tableData" :max-height="tableHeight" :highlight-current-row='true'
style="width: 100%;" v-loading="loading" header-row-class-name="manage-tab-head" >
<el-table-column :label="$t('table.order')" align="center" type="index" width="100"></el-table-column>
<el-table-column :label="$t('table.laneName')" align="center" prop="name"></el-table-column>
<el-table-column :label="$t('table.channelCode')" align="center" prop="code"></el-table-column>
<el-table-column :label="$t('table.counterType')" :formatter="formatCounterType" align="center" prop="counterType"></el-table-column>
<el-table-column :label="$t('table.areaName')" :formatter="formatArea" align="center" prop="areaId"></el-table-column>
<el-table-column :label="$t('table.operate')" align="center" width="200">
<template slot-scope="scope">
<el-button type="text" class="tab-btn" @click="drawDesk(scope.row)">{{$t('button.draw')}}</el-button>
<el-button type="text" class="tab-btn" @click="editDesk(scope.row)">{{$t('button.edit')}}</el-button>
<el-button type="text" class="tab-btn" @click="delDesk(scope.row)">{{$t('button.delete')}}</el-button>
<!-- <span class="bind" @click="drawDesk(scope.row)">{{$t('button.draw')}}</span>
<span class="bind" @click="editDesk(scope.row)">{{$t('button.edit')}}</span>
<span class="unbind" @click="delDesk(scope.row)">{{$t('button.delete')}}</span> -->
</template>
</el-table-column>
</el-table>
</el-col>
<!-- <el-col :span="14" class="personInfo">
<ul class="legends">
<el-button size="mini" type="danger" @click="clearDesk">{{$t('button.clear')}}</el-button>
<el-button size="mini" type="primary" @click="saveData">{{$t('button.saveDrawing')}}</el-button>
</ul>
<div class="draw_desks">
<el-row :loading="loading" class="pwraper" :style="{ maxHeight: tableHeight + 'px' }">
<ul class="dlist" ref="dlist">
<li class="ditem" style="height:0;margin:0;">
<div class="fwrap"></div>
</li>
<li class="ditem" v-for="(item, index) in canvasList" :key="index">
<div class="fwrap">
<img class="dimg" :src="item.picUrl" :style="{ maxHeight: tableHeight-60 + 'px' }"/>
<div class="pview">
<Painter :cdata="item" :isEdit="true" :desk="currentRow" :list="tableData" @gclick="groupClick" @start="startDesk" @finish="finishDesk"></Painter>
</div>
</div>
</li>
</ul>
</el-row>
</div>
</el-col> -->
</el-row>
<add-desk-dialog ref="addDeskDialog"></add-desk-dialog>
<el-dialog :title="$t('button.draw')" class="manage-dialog dialog_lj" width="80%" :visible.sync="drawDialogVisible" :close-on-click-modal="false" v-if="drawDialogVisible" @close="drawDialogVisible=false">
<!-- <ul class="legends">
<el-button size="mini" type="danger" @click="clearDesk">{{$t('button.clear')}}</el-button>
<el-button size="mini" type="primary" @click="saveData">{{$t('button.saveDrawing')}}</el-button>
</ul> -->
<div class="draw_desks boxShadow">
<el-row :loading="loading" class="pwraper" :style="{ maxHeight: tableHeight + 'px' }">
<ul class="dlist" ref="dlist">
<li class="ditem" style="height:0;margin:0;">
<div class="fwrap"></div>
</li>
<li class="ditem" v-for="(item, index) in canvasList" :key="index">
<div class="fwrap">
<img class="dimg" :src="item.picUrl" :style="{ maxHeight: tableHeight-60 + 'px' }"/>
<div class="pview">
<Painter :cdata="item" :isEdit="true" :desk="currentRow" :list="tableData" @gclick="groupClick" @start="startDesk" @finish="finishDesk"></Painter>
</div>
</div>
</li>
</ul>
</el-row>
</div>
<div slot="footer" class="dialog-footer">
<el-button @click="drawDialogVisible = false" class="dialog-btn">{{ $t('dialog.cancel') }}</el-button>
<el-button size="mini" type="danger" @click="clearDesk">{{$t('button.clear')}}</el-button>
<el-button type="primary" @click="saveData()" class="dialog-btn dialog-confirm-btn">
{{ $t('button.saveDrawing') }}</el-button>
</div>
</el-dialog>
</div>
</template>
<script>
import _ from 'underscore';
import Painter from './Painter';
import addDeskDialog from "./addDesk";
export default {
data() {
return {
mallListForTerm:[],
searchForm:{
mallId:'',
areaId:'',
},
areaListData:[],
ptype:1,
floorImg:'',
tableData: [],
loading: true,
canvasList:[],
currentRow:{},
groupList:[],
isPainter:false,
chooseList:[],
counterTypeData:[],
// 画图
drawDialogVisible:false,
};
},
components: {
"add-desk-dialog": addDeskDialog,
'Painter': Painter
},
computed: {
tableHeight() {
const windowInnerHeight = window.innerHeight;
return windowInnerHeight - windowInnerHeight * 0.24;
}
},
mounted() {
this.getCounterType()
this.getMallListForTerm();
},
methods: {
getCounterType(){
this.$api.base.dataDic({
type: 'counterType'
}).then(res => {
this.counterTypeData = res.data.data.map(item=>{
if(localStorage.getItem('lang') == 'en_US'){
item.value = item.valueEn;
}
return item;
});
})
},
// 广场
getMallListForTerm() {
this.mallListForTerm = [];
this.searchForm.mallId = "";
this.$api.base.mall({
accountId: this.$cookie.get('accountId'),
status_arr: "1,3"
}).then(data => {
let result = data.data;
if (result.data.length) {
if (this.getSessionLocal("mallId")) {
this.searchForm.mallId = Number(this.getSessionLocal("mallId"));
} else {
this.searchForm.mallId = result.data[0].id;
this.setSessionLocal("mallId", this.searchForm.mallId);
}
this.mallListForTerm = result.data;
}
this.getAreaList(this.searchForm.mallId);
})
},
mallChange(val){
this.setSessionLocal("mallId", val);
this.getAreaList(val)
},
// 区域
getAreaList(val){
this.areaListData = [];
this.$api.queueManagementApi.getAreaList({
mallId: val,
pageNum: 1,
pageSize: 999999
}).then(res => {
let result = res.data;
if(result.code==200){
if(result.data.list&& result.data.list.length>0){
this.searchForm.areaId = result.data.list[0].id
this.floorImg =window._vionConfig.picUrl + result.data.list[0].pic
this.areaListData = result.data.list;
}
this.getTableData()
}
})
},
formatArea(row, column, cellValue){
let result = ''
this.areaListData.forEach(item=>{
if(item.id==cellValue){
result = item.name
}
})
return result
},
formatCounterType(row, column, cellValue){
let result = ''
this.counterTypeData.forEach(item=>{
if(item.key==cellValue){
result = item.value
}
})
return result
},
areaChange(val){
this.getTableData(this.searchForm.mallId)
this.areaListData.forEach(item=>{
if(item.id==val){
this.floorImg =window._vionConfig.picUrl + item.pic
}
})
},
// 列表数据
getTableData() {
this.loading = true;
this.tableData = [];
this.chooseList = []
this.$api.queueManagementApi.getChannelList({
areaId: this.searchForm.areaId,
pageNum: 1,
pageSize: 9999999
}).then(res => {
let result = res.data;
this.loading = false;
if(result.code==200){
this.tableData = result.data.list;
if(this.tableData.length>0){
this.tableData.forEach(item=>{
item.picUrl = this.floorImg;
})
this.chooseList.push(result.data.list[0])
}
}
})
},
searchFun() {
this.getTableData();
},
// 新增
addDesk() {
this.$refs.addDeskDialog.dialogInit('add');
},
editDesk(data){
this.$refs.addDeskDialog.dialogInit('edit',data);
},
delDesk(data){
this.$confirm(this.$t("message.delete"), this.$t("message.prompt"), {
confirmButtonText: this.$t("message.confirm"),
cancelButtonText: this.$t("message.cancel"),
type: "warning",
}).then(() => {
this.$api.queueManagementApi.deleteChannel({id:data.id}).then(res=>{
let result = res.data;
if(result.code==200){
this.$message({
message: result.msg,
type: 'success'
});
this.getTableData();
}else{
this.$message({
message: result.msg,
type: 'error'
});
}
})
})
},
handleCurrentChange(val) {
this.currentRow = val;
},
setCurrent(row,column,event) {
this.$refs.singleTable.setCurrentRow(row);
},
drawDesk(row){
this.drawDialogVisible = true;
this.currentRow = row;
this.drawFloorMap()
},
drawFloorMap(){
this.canvasList = [];
let that = this;
let canvasList = [];
_.each(this.chooseList,(item,index)=>{
let img = new Image();
img.src = item.picUrl;
img.onload = function(){
item.width = this.width;
item.height = this.height;
canvasList.push(item);
that.$nextTick(()=>{
item.cwidth = document.querySelector('.fwrap').getBoundingClientRect().width;
item.cheight = item.cwidth*item.height/item.width;
that.canvasList = canvasList;
});
}
})
},
startDesk(data){
let index = _.findIndex(this.groupList,(item=>{
return item.desk.id == data.id;
}));
if(index>-1){
this.groupList[index].zr.remove(this.groupList[index]);
this.groupList.splice(index,1);
if(data.coordinate){
this.delCoord(data);
}
}else{
return false;
}
},
clearDesk(){
if(_.isEmpty(this.currentRow)){
return this.$message({
showClose: true,
type: "warning",
message: this.$t('tips.selectChannel')
})
}
this.startDesk(this.currentRow);
},
finishDesk(data){
let index = _.findIndex(this.groupList,(item=>{
return item.desk.id == data.desk.id;
}));
if(index>-1){
this.groupList.splice(index,1);
}
this.groupList.push(data);
},
groupClick(group){
this.$refs.singleTable.setCurrentRow(group.desk);
},
// 保存
saveData() {
let ajaxs = [];
if(this.groupList.length==0){
return this.$message({
showClose: true,
type: "warning",
message: this.$t('tips.drawChannelMap')
})
}
_.each(this.groupList,group=>{
ajaxs.push(this.editCoord(group));
});
Promise.all(ajaxs).then(res=>{
this.drawDialogVisible = false;
this.getTableData();
this.$message({
showClose: true,
type: "success",
message: this.$t('message.labelSuccess')
})
})
},
delCoord(data){
data.coordinate='';
this.$api.queueManagementApi.updateChannel(data).then(res=>{
let result = res.data;
if(result.code==200){
this.$message({
message: result.msg,
type: 'success'
});
this.getTableData();
}else{
this.$message({
message: result.msg,
type: 'error'
});
}
})
},
editCoord(group){
let {position,points,__zr,cdata,desk} = group;
let [width,height] = [__zr.getWidth(),__zr.getHeight()];
let coordinate = _.map(points,point=>{
return {
'x':parseInt(1920*(point[0]+position[0])/width),
'y':parseInt(1080*(point[1]+position[1])/height),
}
});
desk.coordinate = JSON.stringify(coordinate);
let params = Object.assign({},desk);
return this.$api.queueManagementApi.updateChannel(params);
},
}
};
</script>
<style scoped lang="less">
.queueManagementContainer{
height: calc(100% - 120px);
}
.manage-content {
height: calc(100% - 100px);
}
.bind {
color: #0069ff;
margin-right: 5px;
cursor: pointer;
}
.manage-container {
height: 100%;
}
.treeMenu {
padding-right: 5px;
border-right: 3px solid #f0f3f9;
height: 100%;
}
.unbind {
color: #0069ff;
cursor: pointer;
}
.legends {
text-align: right;
}
.draw_desks {
position: relative;
width: 100%;
margin-bottom: 20px;
}
.pwraper {
position: relative;
overflow: auto;
padding-left: 10px;
min-height: 300px;
margin-top: 10px;
}
.fwrap{
position: relative;
width: 100%;
padding-top: 10px;
}
.fwrap .dimg{
width: 100%;
height: 400px;
}
.pview {
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
}
.btns{
margin-right: 20px;
margin-top: 7px;
}
</style>
......@@ -18,37 +18,97 @@
<el-form-item>
<el-time-picker is-range v-model="searchForm.time" :range-separator="$t('dialog.to')" :start-placeholder="$t('pholder.startDate')" :end-placeholder="$t('pholder.endDate')" placeholder="选择时间范围"> </el-time-picker>
</el-form-item>
<!-- <el-form-item :label="$t('table.granularity')">
<el-select v-model="searchForm.granularity" :placeholder="$t('pholder.select')">
<el-option v-for="item in granularityListData" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
</el-form-item> -->
<el-form-item>
<el-button type="primary" class="search-btn" size="mini" plain @click="searchFun">{{$t('button.search')}}</el-button>
<el-button type="primary" class="search-btn" v-if="!playBtnShow" size="mini" plain @click="playFun">{{$t('button.play')}}</el-button>
<el-button type="primary" class="search-btn" v-if="playBtnShow" size="mini" plain @click="pauseFun">{{$t('button.pause')}}</el-button>
</el-form-item>
<el-form-item v-show="progressShow">
<span style="margin-left: 50px;">{{progressTime.substring(11)}}</span>
</el-form-item>
</el-form>
</div>
<div class="manage-content">
<div style="position:relative">
<div class="aiot_title">
<div class="aiot_title_item">
<div class="aiot_title_item_text">Customers in Queue</div>
<div class="aiot_title_item_num">{{topData.customersInQueue}}</div>
<div class="aiot_title_item_unit">Customers</div>
</div>
<div class="aiot_title_item">
<div class="aiot_title_item_text">Average Waiting</div>
<div class="aiot_title_item_num">{{topData.averageWaitingTime[0]}}<small style="marginRight:5px">min</small>{{topData.averageWaitingTime[1]}}<small style="marginLeft:4px">s</small></div>
</div>
<div class="aiot_title_item">
<div class="aiot_title_item_text">Predicted Waiting</div>
<div class="aiot_title_item_num">{{topData.predictedWaiting[0]}}<small style="marginRight:5px">min</small>{{topData.predictedWaiting[1]}}<small style="marginLeft:4px">s</small></div>
</div>
<div class="aiot_title_item">
<div class="aiot_title_item_text">Customers Served</div>
<div class="aiot_title_item_num">{{topData.customersServedTillNow}}</div>
<div class="aiot_title_item_unit">Customers</div>
</div>
<div class="aiot_title_item">
<div class="aiot_title_item_text">Throughput</div>
<div class="aiot_title_item_num">{{topData.throughput}}</div>
<div class="aiot_title_item_unit">Customers/hour</div>
</div>
<div class="aiot_title_item">
<div class="aiot_title_item_text">Open Counters</div>
<div class="aiot_title_item_num">{{topData.openCounters}}/{{topData.totalCounters}}</div>
</div>
<div class="aiot_title_item">
<div class="aiot_title_item_text">Store Entry</div>
<div class="aiot_title_item_num">{{topData.storeEntry}}</div>
<div class="aiot_title_item_unit">Customers</div>
</div>
<div class="aiot_title_item">
<div class="aiot_title_item_text">Store Exit</div>
<div class="aiot_title_item_num">{{topData.storeExit}}</div>
<div class="aiot_title_item_unit">Customers</div>
</div>
</div>
</div>
<div class="asis-table-content" v-loading="loading">
<canvas id='container' class="boxShadow"></canvas>
<div class="time-box1" v-show="progressShow">
<!-- <div class="time-box1" v-show="progressShow">
<span style="float: left;">{{startTime}}</span>
<span style="color: #2774e9;">{{progressTime.substring(11)}}</span>
<span style="float: right;">{{endTime}}</span>
</div>
</div> -->
<!-- <div id="colorBox" v-show="progressShow"></div> -->
</div>
<el-row :gutter="10" class="chart">
<el-col :span="12">
<div class="chart1">
<div class="tabel_title">
<span>Time Predicted</span>
</div>
<div id="chart_left" class="chartLine"></div>
</div>
</el-col>
<el-col :span="12">
<div class="chart1 chart2">
<div class="tabel_title">
<span>Customers Overview</span>
</div>
<div id="chart_right" class="chartLine"></div>
</div>
</el-col>
</el-row>
</div>
</div>
</template>
<script>
import * as echarts from "echarts";
import moment from 'moment'
export default {
data() {
return {
chart_left: null,
chart_right: null,
startTime:'',
endTime:'',
mallListForTerm: [],
......@@ -75,42 +135,112 @@
colorBoxWidth:0,
channelNum:0,
pauseBtn:false,
granularityListData:[
{
value: 1,
label: '1min'
}, {
value: 2,
label: '2min'
}, {
value: 3,
label: '3min'
}, {
value: 5,
label: '5min'
}, {
value: 10,
label: '10min'
},{
value: 20,
label: '20min'
},{
value: 30,
label: '30min'
topData: {
customersInQueue: 0,
averageWaitingTime: [0, 0],
predictedWaiting: [0, 0],
customersServedTillNow: 0,
},
optionTopConfig: {
tooltip: {
trigger: 'axis',
backgroundColor: "rgba(255,255,255,0.8)",
borderWidth: "2", //边框宽度设置1
borderColor: "gray", //设置边框颜色
textStyle: {
color: "#333" //设置文字颜色
},
formatter:this.tooltipFormat
},
legend: {
data: [],
padding:[10,0,0,80],
x:'100',
},
grid: {
left: '3%',
right: '4%',
bottom: '3%',
top:'40',
containLabel: true
},
]
color: ['#3BB8FF', '#FFC62E', '#97c05c'],
xAxis: [{
type: 'category',
data: [],
boundaryGap: false,
axisPointer: {
type: 'shadow'
}
}],
yAxis: [
{
type: 'value',
name: 'Time(s)',
},
{
type: 'value',
name: 'Customers'
},
],
series: []
},
optionBottomConfig: {
tooltip: {
trigger: 'axis',
backgroundColor: "rgba(255,255,255,0.8)",
borderWidth: "2", //边框宽度设置1
borderColor: "gray", //设置边框颜色
textStyle: {
color: "#333" //设置文字颜色
},
formatter:this.tooltipFormat1
},
legend: {
data: [],
padding:[10,0,0,150],
x:'100',
},
grid: {
left: '3%',
right: '4%',
bottom: '3%',
top:'40',
containLabel: true
},
color: ['#3BB8FF', '#FFC62E', '#97c05c'],
xAxis: [{
type: 'category',
data: [],
boundaryGap: false,
axisPointer: {
type: 'shadow'
}
}],
yAxis: [
{
type: 'value',
name: 'Customers'
}
],
series: []
}
}
},
mounted() {
this.getMallListForTerm();
this.chart_left = echarts.init(document.getElementById("chart_left"));
this.chart_right = echarts.init(document.getElementById("chart_right"));
this.canvas = document.getElementById("container");
this.ctx = this.canvas.getContext("2d");
this.canvas.width = document.body.clientWidth - 248;
this.canvas.height = 400;
this.canvas.height = 350;
this.widthX = (1920 / this.canvas.width).toFixed(2);
this.heightX = (1080 / this.canvas.height).toFixed(2);
window.addEventListener("resize", () => {
this.setCanvasWidth();
this.chart_left.resize();
this.chart_right.resize();
});
this.loading = true;
},
......@@ -126,6 +256,97 @@
}
},
methods: {
tooltipFormat(parmas){
let htmls = "";
htmls = `<div><div style="font-size:14px;height:31px;line-height:31px;padding-left:15px;padding-right:15px;">${parmas[0].name}</div>`;
parmas.forEach((item,index) => {
if(item.seriesName=='Queue Length'){
htmls += `<p style="font-size:13px;padding:4px 15px;">${item.marker} ${item.seriesName}: ${item.data }${this.$t('format.perNum')}</p>`;
}else{
let count = item.data
if(count>60){
count = parseInt(count/60) + 'min' + count%60+'s'
}else{
count = count +'s'
}
htmls += `<p style="font-size:13px;padding:4px 15px;">${item.marker} ${item.seriesName}: ${count}</p>`;
}
});
htmls += "</div>";
return htmls;
},
tooltipFormat1(parmas){
let htmls = "";
htmls = `<div><div style="font-size:14px;height:31px;line-height:31px;padding-left:15px;padding-right:15px;">${parmas[0].name}</div>`;
parmas.forEach((item,index) => {
// if(item.data){
htmls += `<p style="font-size:13px;padding:4px 15px;">${item.marker} ${item.seriesName}: ${item.data }${this.$t('format.perNum')}</p>`;
// }
});
htmls += "</div>";
return htmls;
},
getCountData(time){
this.$api.queueManagementApi.getCountData({
cashierAreaId: this.searchForm.cashierAreaId,
countDate: moment(this.searchForm.countDate).format('YYYY-MM-DD'),
startCountTime: moment(this.searchForm.countDate).format('YYYY-MM-DD')+' ' + time
}).then(res => {
let result = res.data;
if (result.code == 200) {
let data = result.data
this.topData = data;
this.topData.averageWaitingTime = [ Math.floor(data.averageWaitingTime / 60), data.averageWaitingTime % 60]
this.topData.predictedWaiting = [ Math.floor(data.predictedWaiting / 60), data.predictedWaiting % 60 ]
}
})
},
// 获取数据回放图表
async getchart() {
let res = await this.$api.queueManagementApi.getchart({
cashierAreaId: this.searchForm.cashierAreaId,
mallId: this.searchForm.mallId,
chartType: "line",
countDate: moment(this.searchForm.countDate).format('YYYY-MM-DD'),
});
this.isloading = false;
let { code, data } = res.data;
if (code == 200) {
this.optionTopConfig.xAxis[0].data = data.xaxis.data;
this.optionTopConfig.series = data.series;
this.optionTopConfig.legend.data = []
data.series.forEach((item,index)=>{
this.optionTopConfig.legend.data.push(item.name)
item.smooth=0.6;
item.symbol='none';
if(index==0){
item.yAxisIndex = 1;
}
})
this.chart_left.setOption(this.optionTopConfig, true)
}
},
// 获取进出客流
getCustomersOverview(){
this.$api.queueManagementApi.getCustomersOverview({
mallId: this.searchForm.mallId,
chartType: "line",
countDate: moment(this.searchForm.countDate).format('YYYY-MM-DD'),
}).then(res => {
if(res.data.code==200){
let resData = res.data.data;
this.optionBottomConfig.xAxis[0].data = resData.xaxis.data;
this.optionBottomConfig.series = resData.series;
this.optionBottomConfig.legend.data = []
resData.series.forEach((item,index)=>{
item.smooth=0.6;
item.symbol='none';
this.optionBottomConfig.legend.data.push(item.name)
})
this.chart_right.setOption(this.optionBottomConfig, true)
}
})
},
// 广场
getMallListForTerm() {
this.mallListForTerm = [];
......@@ -229,6 +450,7 @@
closeT = new Date(this.searchForm.time[1]).getTime();
}
this.getCountData(moment(openT).format('HH:mm:ss'))
this.getDistribution(moment(openT).format('HH:mm:ss'))
// document.getElementById("colorBox").innerHTML='';
this.playBtnShow = true
......@@ -246,6 +468,7 @@
return false
}else{
this.progressTime = moment(openT).format('YYYY-MM-DD HH:mm:ss')
this.getCountData(moment(openT).format('HH:mm:ss'))
this.getDistribution(moment(openT).format('HH:mm:ss'))
}
}, 1500);
......@@ -266,7 +489,14 @@
},
searchFun() {
clearInterval(this.timeInterval);
this.playBtnShow = false;
this.progressShow = false;
this.pauseBtn = false
this.progressTime = ''
this.getDistribution()
this.getCountData(moment(this.searchForm.time[0]).format('HH:mm:ss'))
this.getCustomersOverview()
this.getchart()
},
// 获取主页大屏人员点位分布
async getDistribution(time) {
......@@ -305,13 +535,8 @@
},
//动态设置canvas的宽度
setCanvasWidth() {
if (this.fullscreen) {
this.canvas.width = document.body.clientWidth - 20;
this.canvas.height = 480;
} else {
this.canvas.width = document.body.clientWidth - 248;
this.canvas.height = 300;
}
this.canvas.width = document.body.clientWidth - 248;
this.canvas.height = 350;
this.widthX = (1920 / this.canvas.width).toFixed(2);
this.heightX = (1080 / this.canvas.height).toFixed(2);
this.drawCirlce();
......@@ -343,6 +568,61 @@
</script>
<style scoped="scoped" lang="less">
.chart {
.chartLine{
width: 100%;
height: 300px;
}
.chart1{
width: calc(100% - 5px);
box-shadow: 0px 3px 12px 0px rgba(196, 200, 207, 0.8);
}
}
.tabel_title {
height: 30px;
color: #fff;
padding-left: 10px;
line-height: 30px;
background-color: #2774e9;
position: relative;
span {
display: inline-block;
font-size: 14px;
font-weight: 700;
}
}
.aiot_title {
display: flex;
justify-content: space-around;
}
.aiot_title_item {
text-align: center;
padding: 5px 12px;
box-shadow: 0px 3px 12px 0px rgba(196, 200, 207, 0.8);
margin-right: 10px;
// margin-bottom: 10px;
flex: 1;
&:last-child{
margin-right: 5px;
}
.aiot_title_item_text {
font-size: 14px;
font-weight: 700;
margin-right: 10px;
}
.aiot_title_item_num {
font-size: 26px;
font-weight: 700;
color: #2774e9;
min-width: 110px;
margin: 0 0 5px 0;
}
.aiot_title_item_unit{
font-size: 12px;
font-weight: 700;
color: #2774e9;
}
}
/deep/.el-select{
width: 180px;
}
......@@ -372,7 +652,7 @@
}
#container{
margin-top: 10px;
margin-bottom: 10px;
margin-bottom: 7px;
}
#colorBox{
border: 1px solid #ccc;
......
......@@ -333,7 +333,7 @@ export default {
let htmls = "";
htmls = `<div><div style="font-size:14px;height:31px;line-height:31px;padding-left:15px;padding-right:15px;">${parmas[0].name}</div>`;
parmas.forEach((item,index) => {
if(index==0){
if(item.seriesName=='Queue Length'){
htmls += `<p style="font-size:13px;padding:4px 15px;">${item.marker} ${item.seriesName}: ${item.data }${this.$t('format.perNum')}</p>`;
}else{
let count = item.data
......
......@@ -148,7 +148,7 @@
let htmls = "";
htmls = `<div><div style="font-size:14px;height:31px;line-height:31px;padding-left:15px;padding-right:15px;">${parmas[0].name}</div>`;
parmas.forEach((item,index) => {
if(index==0){
if(item.seriesName=='Queue Length'){
htmls += `<p style="font-size:13px;padding:4px 15px;">${item.marker} ${item.seriesName}: ${item.data }${this.$t('format.perNum')}</p>`;
}else{
let count = item.data
......
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!