Compare commits

...

157 Commits
master ... iscs

Author SHA1 Message Date
joylink_cuiweidong
34a20d3475 代码调整 2021-04-29 10:06:12 +08:00
joylink_cuiweidong
d8af8fb70a 代码调整 2021-04-22 17:11:54 +08:00
joylink_cuiweidong
85de91411e 状态显示 代码调整 2021-04-22 16:28:57 +08:00
joylink_cuiweidong
c1356549ec 代码调整 2021-04-22 14:55:11 +08:00
joylink_cuiweidong
f34617b7db 代码调整 2021-04-21 10:06:12 +08:00
joylink_cuiweidong
71628dcdd2 代码调整 2021-04-20 18:11:37 +08:00
joylink_cuiweidong
2ef2d01fc0 iscs 代码调整 2021-04-20 16:35:42 +08:00
joylink_cuiweidong
bc9757f7b9 代码调整 2021-04-19 17:45:41 +08:00
joylink_cuiweidong
3341a29421 代码调整 2021-04-19 16:24:18 +08:00
joylink_cuiweidong
5dc7de9ded 代码调整 2021-04-19 14:54:21 +08:00
joylink_cuiweidong
00359591b5 iscs 新版预览界面调整 2021-04-19 11:00:34 +08:00
ival
624b06fe85 添加优化 2021-04-15 14:29:54 +08:00
ival
4921b43e1b 删除无用代码 2021-04-13 15:46:15 +08:00
ival
7b9bb93103 增加大铁仿真display 2021-04-13 14:58:23 +08:00
ival
932b2e1d79 Merge branch 'iscs' of https://git.code.tencent.com/lian-cbtc/jl-client into iscs
# Conflicts:
#	src/views/iscs_new/iscsDraw/index.vue
2021-04-13 10:57:16 +08:00
ival
9b01aada15 修改设置shape的bug 2021-04-13 10:56:32 +08:00
joylink_cuiweidong
da059a3c9c 代码调整 2021-04-13 10:42:28 +08:00
joylink_cuiweidong
acefe20fc0 Merge branch 'iscs' of https://git.code.tencent.com/lian-cbtc/jl-client into iscs 2021-04-13 09:11:50 +08:00
joylink_cuiweidong
efac958ea0 代码调整 2021-04-13 09:11:33 +08:00
ival
ae686de0ab Merge branch 'iscs' of https://git.code.tencent.com/lian-cbtc/jl-client into iscs
# Conflicts:
#	src/views/iscs_new/iscsDraw/index.vue
2021-04-12 18:44:16 +08:00
ival
dc855fa6ef 修改代码 2021-04-12 18:43:20 +08:00
joylink_cuiweidong
97f606a314 代码调整 2021-04-12 18:29:23 +08:00
joylink_cuiweidong
2e45db73e5 iscs 代码调整 2021-04-12 17:55:29 +08:00
joylink_cuiweidong
1a017ceed0 Merge branch 'iscs' of https://git.code.tencent.com/lian-cbtc/jl-client into iscs 2021-04-12 15:45:01 +08:00
joylink_cuiweidong
a686b8939e iscs 代码调整 2021-04-12 15:43:39 +08:00
ival
e03b492c1d 修改页面 滚动条 2021-04-12 14:57:53 +08:00
ival
c3587b4411 1. 修改界面复制粘贴的问题 2021-04-12 14:50:17 +08:00
ival
b3fbfbb3ad Merge branch 'iscs' of https://git.code.tencent.com/lian-cbtc/jl-client into iscs 2021-04-12 09:23:23 +08:00
ival
83a0e74a88 修改代码 2021-04-12 09:20:11 +08:00
joylink_cuiweidong
467d3e48cd iscs 代码调整 2021-04-12 09:07:05 +08:00
joylink_cuiweidong
45f9b1d9e2 Merge branch 'iscs' of https://git.code.tencent.com/lian-cbtc/jl-client into iscs 2021-04-09 18:36:25 +08:00
joylink_cuiweidong
da024251de 代码调整 2021-04-09 18:36:09 +08:00
joylink_cuiweidong
00887b7409 代码调整 2021-04-09 18:35:54 +08:00
ival
1985b51f98 修改复制粘贴的问题 2021-04-09 18:33:27 +08:00
ival
fce6f58eb0 增加代码 2021-04-09 17:54:01 +08:00
ival
18a51e7c26 修改代码 2021-04-09 17:48:42 +08:00
ival
5f62a24f08 修改代码 2021-04-09 17:43:04 +08:00
ival
1375fc4fbc 修改代码 2021-04-09 17:36:07 +08:00
ival
3739fcc4ac Merge branch 'iscs' of https://git.code.tencent.com/lian-cbtc/jl-client into iscs
# Conflicts:
#	src/views/iscs_new/iscsDraw/index.vue
2021-04-09 17:14:09 +08:00
ival
2c69d7e145 修改代码 2021-04-09 17:12:35 +08:00
joylink_cuiweidong
173643eb37 代码调整 2021-04-09 17:11:36 +08:00
ival
6c2b475213 提交文件 2021-04-09 17:09:58 +08:00
joylink_cuiweidong
14ecacf811 Merge branch 'iscs' of https://git.code.tencent.com/lian-cbtc/jl-client into iscs
# Conflicts:
#	src/views/iscs_new/iscsDraw/index.vue
2021-04-09 17:05:09 +08:00
joylink_cuiweidong
c9ec52a7c9 iscs 代码调整 2021-04-09 17:02:08 +08:00
ival
5c19aab68c 修改 创建iscs创建元素的方式 2021-04-09 16:50:26 +08:00
ival
f78fa04842 修改代码 2021-04-09 14:37:43 +08:00
ival
aab9cb8bed Merge branch 'iscs' of https://git.code.tencent.com/lian-cbtc/jl-client into iscs
# Conflicts:
#	src/views/iscs_new/iscsDraw/index.vue
2021-04-09 13:58:19 +08:00
ival
b5582126ba 优化iscs_new 代码 2021-04-09 13:57:39 +08:00
joylink_cuiweidong
4d37cc9267 Merge branch 'iscs' of https://git.code.tencent.com/lian-cbtc/jl-client into iscs
# Conflicts:
#	src/views/iscs_new/iscsDraw/index.vue
2021-04-09 11:30:04 +08:00
joylink_cuiweidong
760c683e3e iscs 代码调整 2021-04-09 11:27:47 +08:00
ival
1be38bdb76 修改代码 2021-04-09 09:39:57 +08:00
ival
a253ec5de5 Merge branch 'iscs' of https://git.code.tencent.com/lian-cbtc/jl-client into iscs
# Conflicts:
#	src/views/iscs_new/iscsDraw/index.vue
2021-04-08 18:41:21 +08:00
ival
bfaf8ea7d8 iscs前端绘制增加indexDb存储 2021-04-08 18:40:12 +08:00
joylink_cuiweidong
001a963b04 代码调整 2021-04-08 18:04:08 +08:00
joylink_cuiweidong
e5249cf4b3 代码调整 2021-04-08 17:56:45 +08:00
ival
04a0560339 修改代码 2021-04-08 17:34:47 +08:00
ival
6eb2ca001c 修改路由配置 2021-04-08 17:33:49 +08:00
ival
1677cfc318 Merge branch 'iscs' of https://git.code.tencent.com/lian-cbtc/jl-client into iscs 2021-04-08 17:31:47 +08:00
ival
a7b5057f9e 修改代码 2021-04-08 17:31:39 +08:00
joylink_cuiweidong
35d4ab4cfc 代码调整 2021-04-08 17:29:18 +08:00
joylink_cuiweidong
4c3a511551 代码调整 2021-04-08 15:33:59 +08:00
joylink_cuiweidong
bd70771cf5 Merge branch 'iscs' of https://git.code.tencent.com/lian-cbtc/jl-client into iscs 2021-04-08 15:27:43 +08:00
joylink_cuiweidong
73d21dbca9 代码调整 2021-04-08 15:27:26 +08:00
ival
67f62970d7 Merge branch 'iscs' of https://git.code.tencent.com/lian-cbtc/jl-client into iscs 2021-04-08 15:25:50 +08:00
ival
54eef21574 修改代码 2021-04-08 15:25:44 +08:00
joylink_cuiweidong
bbba502ba7 Merge branch 'iscs' of https://git.code.tencent.com/lian-cbtc/jl-client into iscs 2021-04-08 15:18:01 +08:00
joylink_cuiweidong
9ceeb48562 代码调整 2021-04-08 15:17:45 +08:00
ival
8ad81f244f 增加 elem 默认 stateList 2021-04-08 14:00:35 +08:00
ival
cfed96504b 修改代码 2021-04-08 13:55:07 +08:00
ival
1fb3c04c37 修改代码 2021-04-08 13:10:10 +08:00
ival
edb841c38c Merge branch 'iscs' of https://git.code.tencent.com/lian-cbtc/jl-client into iscs
# Conflicts:
#	src/views/iscs_new/iscsDraw/index.vue
2021-04-08 13:00:41 +08:00
ival
9a7d6a007b 修改代码 2021-04-08 13:00:03 +08:00
ival
e58d229abc Merge branch 'iscs' of https://git.code.tencent.com/lian-cbtc/jl-client into iscs 2021-04-08 11:24:09 +08:00
ival
f84d221857 修改代码 2021-04-08 11:24:01 +08:00
joylink_cuiweidong
26ed24d339 代码调整 2021-04-08 11:23:24 +08:00
joylink_cuiweidong
73caf6074b Merge branch 'iscs' of https://git.code.tencent.com/lian-cbtc/jl-client into iscs
# Conflicts:
#	src/iscs_new/core/form/form2Base.js
2021-04-08 11:22:37 +08:00
joylink_cuiweidong
89f7d38a77 代码调整 2021-04-08 11:20:51 +08:00
ival
d1281b0346 Merge branch 'iscs' of https://git.code.tencent.com/lian-cbtc/jl-client into iscs
# Conflicts:
#	src/iscs_new/core/form/elementConst.js
#	src/views/iscs_new/components/dataForm.vue
#	src/views/iscs_new/iscsDraw/index.vue
2021-04-08 11:20:45 +08:00
ival
53008131c6 提交代码 2021-04-08 11:18:01 +08:00
joylink_cuiweidong
c9b5c908a0 代码调整 2021-04-08 11:15:39 +08:00
ival
dcefdfab33 修改代码 2021-04-08 09:50:07 +08:00
ival
4357eec04f 修改代码 2021-04-08 09:36:25 +08:00
ival
9d8ccd5a67 提交代码 2021-04-08 09:24:30 +08:00
ival
d24f91afc1 修改代码 2021-04-08 09:23:42 +08:00
ival
84d8a396f0 修改代码 2021-04-08 09:07:12 +08:00
ival
9917926a43 优化代码 2021-04-08 09:04:15 +08:00
ival
b7d2f79462 提交代码 2021-04-08 08:55:36 +08:00
ival
27bcdc52a0 修改代码 2021-04-07 18:39:27 +08:00
ival
50cb5b1ece Merge branch 'iscs' of https://git.code.tencent.com/lian-cbtc/jl-client into iscs 2021-04-07 18:14:27 +08:00
ival
eda8b8b42c 提交代码 2021-04-07 18:14:18 +08:00
joylink_cuiweidong
62eb86e5ec iscs 代码调整 2021-04-07 18:10:51 +08:00
joylink_cuiweidong
d7ac074608 iscs 代码调整 2021-04-07 18:10:24 +08:00
joylink_cuiweidong
c123a04fd7 Merge branch 'iscs' of https://git.code.tencent.com/lian-cbtc/jl-client into iscs 2021-04-07 17:56:59 +08:00
joylink_cuiweidong
156a63ea02 iscs 代码调整 2021-04-07 17:56:40 +08:00
ival
97c4aaf20c Merge branch 'iscs' of https://git.code.tencent.com/lian-cbtc/jl-client into iscs 2021-04-07 15:39:41 +08:00
ival
fc2e4137f5 修改代码 2021-04-07 15:39:32 +08:00
joylink_cuiweidong
7ed95d2701 代码调整 2021-04-07 15:35:35 +08:00
joylink_cuiweidong
8e13a2190b iscs 代码调整 2021-04-07 13:54:55 +08:00
ival
e1a75e68a1 修改代码 2021-04-07 11:17:41 +08:00
ival
3240ca7760 修改代码 2021-04-07 10:57:19 +08:00
ival
31cc8f75c5 修改代码 2021-04-07 10:55:38 +08:00
ival
74d82f5872 优化皮肤配置 2021-04-07 10:05:17 +08:00
ival
cad7ff2129 修改代码 2021-04-06 18:24:51 +08:00
ival
1639866515 修改代码 2021-04-06 11:29:22 +08:00
ival
c735f4dd50 调整结构 2021-04-06 10:35:12 +08:00
ival
2508d084d0 Merge branch 'iscs' of https://git.code.tencent.com/lian-cbtc/jl-client into iscs 2021-04-06 10:21:47 +08:00
ival
1bf4583525 删除无用文件 2021-04-06 10:21:40 +08:00
joylink_cuiweidong
e6f7d8a207 iscs 代码调整 2021-04-06 10:19:23 +08:00
ival
4322001225 修改代码 2021-04-04 21:49:20 +08:00
ival
ae050fdc5b 修改动画参数 2021-04-04 21:42:23 +08:00
ival
16fcf932ff 修改代码 2021-04-04 21:19:17 +08:00
ival
b8ef4c33da 修改代码 2021-04-04 21:00:42 +08:00
ival
e3900b6f9e 优化代码 2021-04-04 20:37:32 +08:00
ival
5870a3814c 修改代码 2021-04-04 20:34:50 +08:00
ival
333fe16661 删除无用引入 2021-04-04 02:22:27 +08:00
ival
3026eed365 修改代码 2021-04-04 02:16:22 +08:00
ival
f16c7e0b11 修改代码 2021-04-04 01:10:37 +08:00
ival
eb429506f5 增加动画处理 2021-04-04 00:44:29 +08:00
ival
870852b97a 优化代码 2021-04-03 20:42:51 +08:00
ival
ca4c840da4 修改bug 2021-04-03 01:05:51 +08:00
ival
bf1818da69 增加代码 2021-04-02 18:14:30 +08:00
ival
0c87981d43 修改代码逻辑 2021-04-02 18:02:06 +08:00
ival
cb9aa77312 修改代码 2021-04-02 16:19:14 +08:00
ival
e2ffe1ca72 修改代码 2021-04-02 11:30:19 +08:00
ival
832513c750 修改缩放变化的问题 2021-04-01 23:15:01 +08:00
ival
6ebbdec414 修改函数名称 2021-04-01 22:11:01 +08:00
ival
2dbd0d7764 提交代码 2021-04-01 22:05:14 +08:00
ival
0a0ff63fec 修改代码 2021-04-01 18:35:44 +08:00
ival
28242e5a2c 优化界面手标操作 2021-04-01 13:33:03 +08:00
ival
67c5425b67 修改代码 2021-04-01 13:05:46 +08:00
ival
a56598888d 组合可以嵌套处理组合 2021-04-01 12:54:03 +08:00
ival
abbc3b96d2 删除 字段 2021-04-01 12:18:14 +08:00
ival
60cc0d06cb 模板处理 2021-04-01 10:52:41 +08:00
ival
71367cf330 调整代码结构 2021-04-01 09:34:44 +08:00
ival
cc6f67dcc0 优化代码 2021-03-31 21:52:13 +08:00
ival
82f432136e 增加代码 2021-03-31 18:29:17 +08:00
ival
143d6a67cc 修改代码 2021-03-31 18:16:58 +08:00
ival
91fd06b695 删除文件 2021-03-31 18:15:12 +08:00
ival
8ae7ff5a8c Merge branch 'iscs' of https://git.code.tencent.com/lian-cbtc/jl-client into iscs 2021-03-31 18:14:09 +08:00
ival
62cd098bdd 优化代码 2021-03-31 18:13:58 +08:00
joylink_cuiweidong
6a72265a7b Merge branch 'iscs' of https://git.code.tencent.com/lian-cbtc/jl-client into iscs 2021-03-31 13:55:32 +08:00
joylink_cuiweidong
3be099bdf8 iscs 绘图form表单调整 2021-03-31 13:53:18 +08:00
ival
134ccdaded 增加 新iscs_new 功能 2021-03-31 13:03:06 +08:00
ival
d9a8baeb93 调整目录结构 2021-03-31 08:53:05 +08:00
ival
0381242401 增加模块 2021-03-30 18:39:59 +08:00
ival
7e66128000 修改 2021-03-30 18:10:31 +08:00
ival
15c1463f5a Merge branch 'iscs' of https://git.code.tencent.com/lian-cbtc/jl-client into iscs 2021-03-30 18:07:13 +08:00
ival
56aebd69e7 增加iscs_new 代码 2021-03-30 18:06:53 +08:00
joylink_cuiweidong
72fadaa866 iscs新版绘图界面调整 2021-03-30 10:29:48 +08:00
ival
9277754f5e 修改代码 2021-03-29 18:43:51 +08:00
ival
e15e5703b4 Merge branch 'iscs' of https://git.code.tencent.com/lian-cbtc/jl-client into iscs 2021-03-29 13:12:13 +08:00
ival
ad7b55704e 增加 门禁iscs背景图,以及iscs_new的部分代码 2021-03-29 13:12:04 +08:00
ival
68a01f94f2 修改代码 2021-03-29 10:59:57 +08:00
joylink_cuiweidong
5150fe4651 Merge branch 'iscs' of https://git.code.tencent.com/lian-cbtc/jl-client into iscs 2021-03-29 10:04:17 +08:00
joylink_cuiweidong
df44298137 运行图编制 代码调整 2021-03-29 10:03:47 +08:00
ival
742d89f08a 增加部分 iscs_new 代码 2021-03-29 09:46:33 +08:00
ival
6b16e3d488 增加 iscs 2021-03-26 17:09:11 +08:00
85 changed files with 11187 additions and 32 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

View File

@ -78,6 +78,8 @@ import bgStationC from '@/assets/iscs_picture/bg-station-C.png'
import bgStationD from '@/assets/iscs_picture/bg-station-D.png' import bgStationD from '@/assets/iscs_picture/bg-station-D.png'
import bgStationE from '@/assets/iscs_picture/bg-station-E.png' import bgStationE from '@/assets/iscs_picture/bg-station-E.png'
import bgStationF from '@/assets/iscs_picture/bg-station-F.png' import bgStationF from '@/assets/iscs_picture/bg-station-F.png'
import bgDoorStationA from '@/assets/iscs_picture/bg-door-station-A.png';
import bgDoorStandA from '@/assets/iscs_picture/bg-door-stand-A.png';
const pictureObj = { const pictureObj = {
'psdLeft': psdLeft, 'psdLeft': psdLeft,
@ -155,7 +157,9 @@ const pictureObj = {
bgStationC, bgStationC,
bgStationD, bgStationD,
bgStationE, bgStationE,
bgStationF bgStationF,
bgDoorStationA,
bgDoorStandA
}; };
export default class Picture extends Group { export default class Picture extends Group {
constructor(device) { constructor(device) {

View File

@ -0,0 +1,118 @@
import Group from 'zrender/src/container/Group';
function traverse(group, map) {
group.eachChild(el => {
if (el instanceof Group) {
traverse(el, map);
} else {
map[el.name] = el;
}
});
return map;
}
class Animate {
constructor(state) {
this._state = {...state};
this._sum = 0;
this._count = 0;
this._first = true;
this._dispose = false;
}
run(interval) {
this._sum = this._sum + interval;
const total = this._state.time + (this._first ? this._state.delay : 0);
if (this._sum > total) {
this.animate(this);
this._count = this._count + 1;
this._sum = 0;
this._first = false;
}
}
isLoop() {
return this._state.loop || this._count < this._state.frameList.length;
}
isNeedDefault() {
return this._state.needDefault;
}
isDispose() {
return this._dispose;
}
isEqual(code) {
return this._state.code == code;
}
getIndex(index = 0) {
return index || this._count % this._state.frameList.length;
}
getFrame(index = 0) {
return this._state.frameList[index];
}
animate() {
const shape = this._state.shape;
const mapShape = this._state.mapShape;
const index = this.getIndex();
const frame = this.getFrame(index);
if (shape && mapShape && frame) {
const mapView = traverse(shape, {});
if (this.isNeedDefault()) {
Object.keys(mapShape).forEach(name => {
const state = mapShape[name];
const view = mapView[name];
if (view) {
shape.setInvisible(shape.model.base.hide);
view.attr({shape: state.default.shape, style: state.default.style});
view.dirty();
}
});
}
Object.keys(frame).forEach(name => {
const view = mapView[name];
const model = frame[name];
if (view && model) {
shape.setInvisible(model.hide);
view.attr({shape: model.shape, style: model.style});
view.dirty();
}
});
}
}
dispose() {
this._dispose = true;
}
}
class AnimateHandle {
constructor(painter) {
this._animates = [];
}
onframe(delay) {
const animate = this._animates.shift();
if (animate) {
animate.run(delay);
if (!animate.isDispose() && animate.isLoop()) {
this._animates.push(animate);
}
}
}
animate(state) {
const animate = this._animates.find(el => el.isEqual(state.code));
if (animate) {
animate.dispose();
}
this._animates.push(new Animate(state));
}
}
export default AnimateHandle;

View File

@ -0,0 +1,3 @@
export default {
}

View File

@ -0,0 +1,30 @@
const defaultStyle = {
/** 透明填充 颜色*/
transparentColor : 'transparent',
/** 默认背景 颜色*/
backgroundColor : '#000000',
/** 默认字体 族类*/
fontFamily : 'SimSun,Times New Roman',
/** 默认字体 大小*/
fontSize : 12,
/** 选中透明度*/
opacity : 0.5,
/** 提亮度*/
liftLevel : 0.8,
/** 边框宽度*/
borderWidth : 1,
/** 边框颜色*/
borderColor : '#000',
/** 边框Dash*/
borderDash: [4, 4]
}
export default defaultStyle;

View File

@ -0,0 +1,6 @@
export default {
ShowBorder: 'showBorder',
HideBorder: 'hideBorder',
ShowTips: 'showTips',
HideTips: 'hideTips'
}

View File

@ -0,0 +1,5 @@
export default {
HightLight: `__hightLight__`,
Selecting: `___selecting__`,
Tips: `__tips__`,
}

View File

@ -0,0 +1,8 @@
export default {
zlevel: 1,
z: 9,
scale: [1, 1],
position: [0, 0],
rotation: 0,
origin: [0, 0]
}

View File

@ -0,0 +1,6 @@
const shapeType = {
Compose: 'Compose',
Element: 'Element'
};
export default shapeType;

351
src/iscs_new/controller.js Normal file
View File

@ -0,0 +1,351 @@
import * as eventTool from 'zrender/src/core/event';
import Storage from './utils/storage';
import Eventful from 'zrender/src/mixin/Eventful';
import DragHandle from './dragHandle';
import SelectingHandle from './selectingHandle';
import SelectHandle from './selectHandle';
import KeyBoardHandle from './keyboardHandle';
import events from './utils/events';
class MouseEvent {
constructor(controller, e) {
const shapeFactory = controller.$map.getShapeFactory();
const shape = e.target;
this.clientX = e.event.clientX;
this.clientY = e.event.clientY;
if (shape && !['@ignore', '@selecting', '@border', '@drag'].includes(shape.subType)) {
if (shape.code) {
let composeCode = shape.composeCode;
let compose = null;
while(composeCode) {
compose = shapeFactory.getShapeByCode(composeCode);
composeCode = compose.model.composeCode;
}
this.code = compose? compose.code: shape.code;
this.type = compose? compose.type: shape.type;
}
if (shape.subType) {
this.subType = shape.subType;
}
if (shape.val) {
this.val = shape.val;
}
}
}
}
export default class Controller extends Eventful {
constructor(map) {
super();
this.$map = map;
this.option = map.getOption();
this.events = map.getEvents();
this._pan =false;
this._isNotLeftMouse = false;
this._shortcuts = '';
this._distance = 0;
this._target = null;
this.initModule(map);
this.initHandler(map);
}
initModule(map) {
this.dragHandle = new DragHandle(map, this);
this.selectingHandle = new SelectingHandle(map, this);
this.selectHandle = new SelectHandle(map, this);
this.keyBoardHandle = new KeyBoardHandle(map, this);
this._storage = new Storage();
}
initHandler(map) {
const that = this;
const zr = map.getZr();
const keyupHandle = this.keyup.bind(this);
const keydownHandle = this.keydown.bind(this);
const dragStartHandle = this.dragHandle.onDragStart;
const draggingHandle = this.dragHandle.onDragging;
const dragEndHandle = this.dragHandle.onDragEnd;
const selectStartHandle = this.selectingHandle.onSelectStart;
const selectingHandle = this.selectingHandle.onSelecting;
const selectEndHandle = this.selectingHandle.onSelectEnd;
const selectedHandle = this.selectHandle.onSelected;
const boardKeyupHandle = this.keyBoardHandle.onKeyup;
const boardKeydownHandle = this.keyBoardHandle.onKeydown;
this.on(this.events.__DragStart, dragStartHandle, this.dragHandle); // 单个元素拖拽
this.on(this.events.__Dragging, draggingHandle, this.dragHandle);
this.on(this.events.__DragEnd, dragEndHandle, this.dragHandle);
this.on(this.events.__SelectStart, selectStartHandle, this.selectingHandle);
this.on(this.events.__Selecting, selectingHandle, this.selectingHandle);
this.on(this.events.__SelectEnd, selectEndHandle, this.selectingHandle);
this.on(this.events.__Selected, selectedHandle, this.selectHandle);
zr.on('click', this.click, this);
zr.on('contextmenu', this.contextmenu, this);
this.enable = function (opts={}) {
that._panEnable = opts.panEnable || that._panEnable || false;
that._zoomEnable = opts.zoomEnable || that._zoomEnable || false;
that._keyEnable = opts.keyEnable || that._keyEnable || false;
that._dragEnable = opts.draggle || that._dragEnable || false;
that._areaSelectEnable = opts.selecting || that._areaSelectEnable || false;
that._selectEnable = opts.selectable || that._selectEnable || false;
that._reflectEnable = opts.reflect || that._reflectEnable || false;
that._preventDefaultMouseMove = opts.preventDefaultMouseMove || that._preventDefaultMouseMove|| true;
that.disable();
zr.on('mousedown', that.mousedown, that);
zr.on('mousemove', that.mousemove, that);
zr.on('mouseup', that.mouseup, that);
zr.on('globalout', that.mouseup, that);
zr.on('mousewheel', that.mousewheel, that);
zr.dom.addEventListener('keyup', keyupHandle, false);
zr.dom.addEventListener('keydown', keydownHandle, false);
zr.dom.focus();
that.on(that.events.__Keyup, boardKeyupHandle, that.keyBoardHandle);
that.on(that.events.__Keydown, boardKeydownHandle, that.keyBoardHandle);
};
this.disable = function () {
zr.off('mousedown', that.mousedown);
zr.off('mousemove', that.mousemove);
zr.off('mouseup', that.mouseup);
zr.off('globalout', that.mouseup);
zr.off('mousewheel', that.mousewheel);
zr.dom.removeEventListener('keyup', keyupHandle, false);
zr.dom.removeEventListener('keydown', keydownHandle, false);
that.off(that.events.__Keyup, boardKeyupHandle);
that.off(that.events.__Keydown, boardKeydownHandle);
};
this.dispose = function () {
zr.off('click', that.click);
zr.off('contextmenu', that.contextmenu);
that.off(that.events.__DragStart, dragStartHandle);
that.off(that.events.__Dragging, draggingHandle);
that.off(that.events.__DragEnd, dragEndHandle);
that.off(that.events.__SelectStart, selectStartHandle);
that.off(that.events.__Selecting, selectingHandle);
that.off(that.events.__SelectEnd, selectEndHandle);
that.off(that.events.__Selected, selectedHandle);
that.disable();
};
}
isSpecialSubType(e) {
return ['@ignore', '@selecting', '@drag', '@border'].includes(e.subType);
}
isSelected(code) {
return this._storage.has(code)
}
setTarget(target) {
this._target = target;
}
setCursorStyle(cursorStyle) {
this.$map.setCursorStyle(cursorStyle);
}
limitDrag(e) {
const dx2 = Math.pow(e.dx, 2);
const dy2 = Math.pow(e.dy, 2);
const scale = this.option.getScaleRate();
const diff = Math.ceil(Math.sqrt(dx2+dy2));
return (scale > 1) || (diff > 2/scale);
}
mousedown(e) {
const event = new MouseEvent(this, e);
const zr = this.$map.getZr();
this._x = e.offsetX;
this._y = e.offsetY;
this._pan = false;
this._target = this.$map.getShapeByCode(event.code);
zr.dom.focus();
this._isNotLeftMouse = eventTool.isMiddleOrRightButtonOnMouseUpDown(e);
this.trigger(events.Click, this._target);
this.selectingHandle.clear();
if (this._isNotLeftMouse) {
this.setCursorStyle('grab');
} else {
if (this.isSpecialSubType(event)) { return; }
if (this._dragEnable) {
this.trigger(this.events.__DragStart, { x: e.offsetX, y: e.offsetY, code: event.code });
}
if (this._areaSelectEnable) {
this.trigger(this.events.__SelectStart, { x: e.offsetX, y: e.offsetY});
}
}
}
mousemove(e) {
const oldX = this._x;
const oldY = this._y;
const target = this._target||{}
const dx = Math.round(e.offsetX - this._x);
const dy = Math.round(e.offsetY - this._y);
this._x = e.offsetX;
this._y = e.offsetY;
this._preventDefaultMouseMove && eventTool.stop(e.event);
if (this._isNotLeftMouse) {
if (this._panEnable) {
if (dx**2 + dy**2 > 8) {
this._pan = true;
}
this.trigger(this.events.__Pan, { dx, dy, oldX, oldY, newX: this._x, newY: this._y });
this.setCursorStyle('grabbing');
}
} else {
if (this._dragEnable && this.dragHandle.isDragging()) {
this.selectingHandle.clear();
if (this.limitDrag({dx, dy})) {
this.trigger(this.events.__Dragging, { dx, dy });
if (this._reflectEnable) {
this.trigger(events.Reflect, {dx, dy, code: target.code});
}
} else {
this._x = oldX;
this._y = oldY;
}
} else if (this._areaSelectEnable && this.selectingHandle.isSelecting()) {
this.trigger(this.events.__Selecting, { x: e.offsetX, y: e.offsetY });
}
}
}
mouseup(e) {
const target = this._target;
if (this._isNotLeftMouse) {
this._isNotLeftMouse = false;
this.setCursorStyle('auto');
} else {
if (this._selectEnable && target) {
this.trigger(this.events.__Selected, { target });
}
if (this._dragEnable && this.dragHandle.isDragging()) {
this.selectingHandle.clear();
this.trigger(this.events.__DragEnd, {x: e.offsetX, y: e.offsetY});
} else if (this._areaSelectEnable && this.selectingHandle.isSelecting()) {
this.trigger(this.events.__SelectEnd, { x: e.offsetX, y: e.offsetY });
}
}
this._target = null;
}
mousewheel(e) {
const zoomEnable = this._zoomEnable;
const wheelDelta = e.wheelDelta;
const originX = Math.ceil(e.offsetX);
const originY = Math.ceil(e.offsetY);
if (wheelDelta === 0 || !zoomEnable) {
return;
}
if (zoomEnable) {
eventTool.stop(e.event);
let scale = 1;
if (wheelDelta > 0) {
scale = 1;
this.setCursorStyle('zoom-in');
} else if (wheelDelta < 0) {
scale = -1;
this.setCursorStyle('zoom-out');
}
this.trigger(this.events.__Zoom, {type: this.events.__Zoom, scale, originX, originY });
}
}
click(e) {
const event = new MouseEvent(this, e);
if (!event.code) {
this.selectHandle.clear();
this.selectingHandle.clear();
this.clear();
}
}
contextmenu(e) {
eventTool.stop(e.event);
const event = new MouseEvent(this, e);
if (!this._pan) {
this.trigger(events.ContextMenu, event);
}
this._pan = false;
}
keydown(e) {
let shortcuts = e.key;
if (e.altKey && e.key != 'Alt') {
shortcuts = `Alt_${shortcuts}`;
}
if (e.shiftKey && e.key != 'Shift') {
shortcuts = `Shift_${shortcuts}`;
}
if (e.ctrlKey && e.key != 'Control') {
shortcuts = `Control_${shortcuts}`;
}
this._shortcuts = shortcuts;
this.trigger(this.events.__Keydown, {key: shortcuts});
this.trigger(events.Keydown, {key: shortcuts});
}
keyup(e) {
this.trigger(this.events.__Keyup, {key: this._shortcuts});
this.trigger(events.Keyup, {key: this._shortcuts})
this._shortcuts = '';
}
clear() {
const shapeFactory = this.$map.getShapeFactory();
this._storage.values().forEach(shape => shapeFactory.hideHightLight(shape));
this._storage.clear();
this._storage.clearClipboard();
this._target = null;
this._pan = false;
}
getStorage() {
return this._storage;
}
getKeyStr() {
return this._shortcuts;
}
getTarget() {
return this._target;
}
destroy () {
this.dispose();
}
}

View File

@ -0,0 +1,100 @@
import * as graphic from '../core/graphic';
import * as utils from '../utils/utils';
import * as color from 'zrender/src/tool/color';
import Group from 'zrender/src/container/Group';
import shapeRender from '../constant/shapeRender';
import defaultStyle from '../config/defaultStyle';
function shapeStyleBuilder({subType, model}) {
return {
subType: subType,
...shapeRender,
code: model.code,
type: model.type,
z: 9998,
cursor: 'pointer',
shape: {
x: 0,
y: 0,
width: 0,
height: 0
},
style: {
lineWidth: defaultStyle.borderWidth,
stroke: color.lift(defaultStyle.borderColor, defaultStyle.liftLevel),
fill: `rgba(200,200,200,0.5)`
}
}
}
// 图形抽象层
// 继承Group是为了直接获取的到包围框不需要进行坐标变化。
class AbstractShape extends Group {
constructor({model, shapeType, shapeFactory}) {
super({...shapeRender, code: model.code, type: model.type});
this.model = model;
this.shapeType = shapeType;
this.shapeFactory = shapeFactory;
this.instanceHightLight = new graphic.Rect(shapeStyleBuilder({subType: '@align', model: this.model}));
}
// 设置高亮
active() {}
// 取消高亮
inactive() {}
// 设置获取焦点
focus() {}
// 设置取消焦点
blur() {}
// 绑定数据
combine() {}
// 解除绑定
uncouple() {}
// 获取依赖图形
getDepShapes() {}
// 设置状态
setState(state) {}
// 拖动
drift({dx, dy}) {
this.model.base.position[0] = this.model.base.position[0] + dx;
this.model.base.position[1] = this.model.base.position[1] + dy;
this.instance.scale = this.model.base.scale;
this.instance.position = this.model.base.position;
this.instance.rotation = this.model.base.rotation*Math.PI/180;
this.instance.origin = utils.createOrigin(this.instance);
this.instance.transform = utils.createTransform({scale: this.model.base.scale, position: this.model.base.position, rotation: this.model.base.rotation*Math.PI/180});
this.setInvisible(this.model.base.hide)
}
// 修改状态
attr(attrs) {
this.instance? this.instance.attr(attrs): super.attr(attrs);
}
// 设置显隐
setInvisible(hide) {
hide ? super.hide() : super.show();
super.dirty();
}
// 遍历执行
traverse(cb) {
this.eachChild(el => {
if (el.traverse) {
el.traverse(cb, el);
} else {
cb.call(el);
}
});
}
}
export default AbstractShape;

View File

@ -0,0 +1,859 @@
import * as graphic from '../../core/graphic';
import form2ShapeStyle from './form2ShapeStyle';
import form2TextStyle from './form2TextStyle';
import types from './types';
const graphicType = Object.fromEntries(Object.keys(graphic).map(type => [type, type]));
const elementConst = {
[graphicType.Line]: {
type: graphicType.Line,
name:'线段',
formList: {
style: [
...form2ShapeStyle
],
shape: [
{
prop: 'x1',
label: '起始点横坐标',
type: types.Number,
precision: 0,
step:1,
rules:[
{ required: true, message:'请输入起始点横坐标', trigger: 'blur' }
],
value: 0,
description: ''
},
{
prop: 'y1',
label: '起始点纵坐标',
type: types.Number,
precision: 0,
step:1,
rules:[
{ required: true, message:'请输入起始点纵坐标', trigger: 'blur' }
],
value: 100,
description: ''
},
{
prop: 'x2',
label: '终止点横坐标',
type: types.Number,
precision: 0,
step:1,
rules:[
{ required: true, message:'请输入终止点横坐标', trigger: 'blur' }
],
value: 100,
description: ''
},
{
prop: 'y2',
label: '终止点纵坐标',
type: types.Number,
precision: 0,
step:1,
rules:[
{ required: true, message:'请输入终止点纵坐标', trigger: 'blur' }
],
value: 0,
description: ''}
// opts.shape.percent number 1 已显示的百分比,用于绘制动画。
]
}
},
[graphicType.Text]: {
type: graphicType.Text,
name:'文字',
formList: {
style: [
...form2TextStyle
],
shape: [
]
}
},
[graphicType.Rect]: {
type: graphicType.Rect,
name:'矩形',
formList: {
style: [
...form2ShapeStyle,
{
prop: 'fill',
label: '填充样式',
type: types.Color,
rules:[
{ required: true, message:'请输入填充样式', trigger: 'blur' }
],
value: '#fff',
description: '填充样式。'
}
],
shape: [
{
prop: 'x',
label: '左上角横坐标',
type: types.Number,
precision: 0,
step:1,
rules:[
{ required: true, message:'请输入左上角横坐标', trigger: 'blur' }
],
value: 0,
description: ''
},
{
prop: 'y',
label: '左上角纵坐标',
type: types.Number,
precision: 0,
step:1,
rules:[
{ required: true, message:'请输入左上角纵坐标', trigger: 'blur' }
],
value: 0,
description: ''
},
{
prop: 'width',
label: '宽度',
type: types.Number,
precision: 0,
step:1,
rules:[
{ required: true, message:'请输入宽度', trigger: 'blur' }
],
value: 50,
description: ''
},
{
prop: 'height',
label: '高度',
type: types.Number,
precision: 0,
step:1,
rules:[
{ required: true, message:'请输入高度', trigger: 'blur' }
],
value: 50,
description: ''
},
{
prop: 'r',
label: '圆角矩形半径',
type: types.NumberArray,
precision: 0,
step:1,
length:4,
rules:[
{ required: true, message:'请输入圆角矩形半径', trigger: 'blur' }
],
value: [0, 0, 0, 0],
description: '用于创建圆角矩形。左上、右上、右下、左下角的半径依次为 r1、 r2、 r3、 r4。[1, 1, 1, 1]'
}]
}
},
[graphicType.Circle]: {
type: graphicType.Circle,
name:'圆形',
formList:{
style: [
...form2ShapeStyle,
{
prop: 'fill',
label: '填充样式',
type: types.Color,
rules:[
{ required: true, message:'请输入填充样式', trigger: 'blur' }
],
value: '#fff',
description: '填充样式。'
}
],
shape: [
{
prop: 'cx',
label: '圆心横坐标',
type: types.Number,
precision: 0,
step:1,
rules:[
{ required: true, message:'请输入圆心横坐标', trigger: 'blur' }
],
value: 0,
description: '圆心横坐标'},
{
prop: 'cy',
label: '圆心纵坐标',
type: types.Number,
precision: 0,
step:1,
rules:[
{ required: true, message:'请输入圆心纵坐标', trigger: 'blur' }
],
value: 0,
description: '圆心纵坐标'
},
{
prop: 'r',
label: '半径',
type: types.Number,
precision: 0,
step:1,
rules:[
{ required: true, message:'请输入半径', trigger: 'blur' }
],
value: 50,
description: '半径'
}
]
}
},
[graphicType.Polygon]: {
type: graphicType.Polygon,
name:'多边形',
formList: {
style: [
...form2ShapeStyle,
{
prop: 'fill',
label: '填充样式',
type: types.Color,
rules:[
{ required: true, message:'请输入填充样式', trigger: 'blur' }
],
value: '#fff',
description: '填充样式。'
}
],
shape: [
{
prop: 'points',
label: '坐标集合',
type: types.Points,
precision: 0, step:1,
rules:[
{ required: true, message:'请输入坐标集合', trigger: 'blur' }
],
value: [[0, 0], [100, 100], [100, 200]],
description: '每个元素是一个横纵坐标的数组'
},
{
prop: 'smooth',
label: '圆滑程度',
type: types.Number,
min: 0,
max: 1,
step: 0.05,
precision: 2,
rules:[
{ required: true, message:'请输入坐标集合', trigger: 'blur' }
],
value: 0,
description: '取值范围为 0 到 1 之间的数字0 表示不圆滑'
}
]
}
},
// [graphicType.Arrow]: {
// type: graphicType.Arrow,
// name:'箭头',
// formList: {
// style: [
// ],
// shape: [
// ]
// }
// },
[graphicType.Polyline]: {
type: graphicType.Polyline,
name:'多边形折线段',
formList: {
style: [
...form2ShapeStyle
],
shape: [
{
prop: 'points',
label: '坐标集合',
type: types.Points,
precision: 0, step:1,
rules:[
{ required: true, message:'请输入坐标集合', trigger: 'blur' }
],
value: [[0, 0], [100, 100], [100, 200]],
description: ''
},
{
prop: 'smooth',
label: '圆滑程度',
type: types.Number,
min: 0,
max: 1,
step: 0.05,
precision: 2,
rules:[
{ required: true, message:'请输入坐标集合', trigger: 'blur' }
],
value: 10,
description: '取值范围为 0 到 1 之间的数字0 表示不圆滑'
}
]
}
},
[graphicType.Isogon]: {
type: graphicType.Isogon,
name:'正多边形',
formList: {
style: [
...form2ShapeStyle,
{
prop: 'fill',
label: '填充样式',
type: types.Color,
rules:[
{ required: true, message:'请输入填充样式', trigger: 'blur' }
],
value: '#fff',
description: '填充样式。'
}
],
shape:[
{
prop: 'x',
label: '圆心横坐标',
type: types.Number,
precision: 0,
step:1,
rules:[
{ required: true, message:'请输入圆心横坐标', trigger: 'blur' }
],
value: 0,
description: '圆心横坐标'
},
{
prop: 'y',
label: '圆心纵坐标',
type: types.Number,
precision: 0,
step:1,
rules:[
{ required: true, message:'请输入圆心纵坐标', trigger: 'blur' }
],
value: 0,
description: '圆心纵坐标'
},
{
prop: 'r',
label: '半径',
type: types.Number,
precision: 0,
step:1,
rules:[
{ required: true, message:'请输入半径', trigger: 'blur' }
],
value: 40,
description: '半径'
},
{
prop: 'n',
label: '边数',
type: types.Number,
precision: 0,
step:1,
rules:[
{ required: true, message:'请输入边数', trigger: 'blur' }
],
value: 5,
description: '边数'
}
]
}
},
[graphicType.Ellipse]: {
type: graphicType.Ellipse,
name:'椭圆',
formList: {
style: [
...form2ShapeStyle,
{
prop: 'fill',
label: '填充样式',
type: types.Color,
rules:[
{ required: true, message:'请输入填充样式', trigger: 'blur' }
],
value: '#fff',
description: '填充样式。'
}
],
shape:[
{
prop: 'cx',
label: '圆心横坐标',
type: types.Number,
precision: 0,
step:1,
rules:[
{ required: true, message:'请输入圆心横坐标', trigger: 'blur' }
],
value: 0,
description: '圆心横坐标'
},
{
prop: 'cy',
label: '圆心纵坐标',
type: types.Number,
precision: 0,
step:1,
rules:[
{ required: true, message:'请输入圆心纵坐标', trigger: 'blur' }
],
value: 0,
description: '圆心纵坐标'
},
{
prop: 'rx',
label: '横向半径',
type: types.Number,
precision: 0,
step:1,
rules:[
{ required: true, message:'请输入横向半径', trigger: 'blur' }
],
value: 50,
description: '横向半径'
},
{
prop: 'ry',
label: '纵向半径',
type: types.Number,
precision: 0,
step:1,
rules:[
{ required: true, message:'请输入纵向半径', trigger: 'blur' }
],
value: 30,
description: '纵向半径'
}
]
}
},
[graphicType.Arc]: {
type: graphicType.Arc,
name:'圆弧',
formList: {
style: [
...form2ShapeStyle
],
shape: [
{
prop: 'cx',
label: '圆心横坐标',
type: types.Number,
precision: 0,
step:1,
rules:[
{ required: true, message:'请输入圆心横坐标', trigger: 'blur' }
],
value: 0,
description: '圆心横坐标'
},
{
prop: 'cy',
label: '圆心纵坐标',
type: types.Number,
precision: 0,
step:1,
rules:[
{ required: true, message:'请输入圆心纵坐标', trigger: 'blur' }
],
value: 0,
description: '圆心纵坐标'
},
{
prop: 'r',
label: '半径',
type: types.Number,
precision: 0,
step:1,
rules:[
{ required: true, message:'请输入半径', trigger: 'blur' }
],
value: 50,
description: '半径'
},
{
prop: 'startAngle',
label: '起始弧度',
type: types.Number,
precision: 0,
step:1,
min: 0,
max: 360,
rules:[
{ required: true, message:'请输入起始弧度', trigger: 'blur' }
],
value: 0,
description: '起始弧度'
},
{
prop: 'endAngle',
label: '终止弧度',
type: types.Number,
precision: 0,
step:1,
min: 0,
max: 360,
rules:[
{ required: true, message:'请输入终止弧度', trigger: 'blur' }
],
value: 180,
description: '终止弧度'
},
{
prop: 'clockwise',
label: '顺时针方向',
type: types.Boolean,
rules:[
{ required: true, message:'请选择图形是否不可见', trigger: 'change' }
],
value: true,
description: '顺时针方向。'
}
]
}
},
[graphicType.Sector]: {
type: graphicType.Sector,
name:'扇形',
formList: {
style: [
...form2ShapeStyle,
{
prop: 'fill',
label: '填充样式',
type: types.Color,
rules:[
{ required: true, message:'请输入填充样式', trigger: 'blur' }
],
value: '#fff',
description: '填充样式。'
}
],
shape: [
{
prop: 'cx',
label: '圆心横坐标',
type: types.Number,
precision: 0,
step:1,
rules:[
{ required: true, message:'请输入圆心横坐标', trigger: 'blur' }
],
value: 0,
description: '圆心横坐标'
},
{
prop: 'cy',
label: '圆心纵坐标',
type: types.Number,
precision: 0,
step:1,
rules:[
{ required: true, message:'请输入圆心纵坐标', trigger: 'blur' }
],
value: 0,
description: '圆心纵坐标'
},
{
prop: 'r',
label: '半径',
type: types.Number,
precision: 0,
step:1,
rules:[
{ required: true, message:'请输入半径', trigger: 'blur' }
],
value: 50,
description: '半径'
},
{
prop: 'r0',
label: '内半径',
type: types.Number,
precision: 0,
step:1,
rules:[
{ required: true, message:'请输入内半径', trigger: 'blur' }
],
value: 30,
description: '内半径'
},
{
prop: 'startAngle',
label: '起始弧度',
type: types.Number,
precision: 0,
step:1,
min: 0,
max: 360,
rules:[
{ required: true, message:'请输入起始弧度', trigger: 'blur' }
],
value: 0,
description: '起始弧度'
},
{
prop: 'endAngle',
label: '终止弧度',
type: types.Number,
precision: 0,
step:1,
min: 0,
max: 360,
rules:[
{ required: true, message:'请输入终止弧度', trigger: 'blur' }
],
value: 180,
description: '终止弧度'
},
{
prop: 'clockwise',
label: '顺时针方向',
type: types.Boolean,
rules:[
{ required: true, message:'请选择图形是否不可见', trigger: 'change' }
],
value: true,
description: '顺时针方向。'
}
]
}
},
[graphicType.Heart]: {
type: graphicType.Heart,
name:'心形',
formList: {
style: [
...form2ShapeStyle,
{
prop: 'fill',
label: '填充样式',
type: types.Color,
rules:[
{ required: true, message:'请输入填充样式', trigger: 'blur' }
],
value: '#fff',
description: '填充样式。'
}
],
shape: [
{
prop: 'cx',
label: '圆心横坐标',
type: types.Number,
precision: 0,
step:1,
rules:[
{ required: true, message:'请输入圆心横坐标', trigger: 'blur' }
],
value: 0,
description: '圆心横坐标'
},
{
prop: 'cy',
label: '圆心纵坐标',
type: types.Number,
precision: 0,
step:1,
rules:[
{ required: true, message:'请输入圆心纵坐标', trigger: 'blur' }
],
value: 0,
description: '圆心纵坐标'
},
{
prop: 'width',
label: '宽度',
type: types.Number,
precision: 0,
step:1,
rules:[
{ required: true, message:'请输入宽度', trigger: 'blur' }
],
value: 50,
description: '宽度'
},
{
prop: 'height',
label: '高度',
type: types.Number,
precision: 0,
step:1,
rules:[
{ required: true, message:'请输入高度', trigger: 'blur' }
],
value: 50,
description: '高度'
}
]
}
},
[graphicType.Droplet]: {
type: graphicType.Droplet,
name:'水滴',
formList: {
style: [
...form2ShapeStyle,
{
prop: 'fill',
label: '填充样式',
type: types.Color,
rules:[
{ required: true, message:'请输入填充样式', trigger: 'blur' }
],
value: '#fff',
description: '填充样式。'
}
],
shape: [
{
prop: 'cx',
label: '圆心横坐标',
type: types.Number,
precision: 0,
step:1,
rules:[
{ required: true, message:'请输入圆心横坐标', trigger: 'blur' }
],
value: 0,
description: '圆心横坐标'},
{
prop: 'cy',
label: '圆心纵坐标',
type: types.Number,
precision: 0,
step:1,
rules:[
{ required: true, message:'请输入圆心纵坐标', trigger: 'blur' }
],
value: 0,
description: '圆心纵坐标'
},
{
prop: 'width',
label: '宽度',
type: types.Number,
precision: 0,
step:1,
rules:[
{ required: true, message:'请输入宽度', trigger: 'blur' }
],
value: 50,
description: '宽度'
},
{
prop: 'height',
label: '高度',
type: types.Number,
precision: 0,
step:1,
rules:[
{ required: true, message:'请输入高度', trigger: 'blur' }
],
value: 50,
description: '高度'
}
]
}
},
[graphicType.Star]: {
type: graphicType.Star,
name:'星形',
formList: {
style: [
...form2ShapeStyle,
{
prop: 'fill',
label: '填充样式',
type: types.Color,
rules:[
{ required: true, message:'请输入填充样式', trigger: 'blur' }
],
value: '#fff',
description: '填充样式。'
}
],
shape: [
{
prop: 'cx',
label: '圆心横坐标',
type: types.Number,
precision: 0,
step:1,
rules:[
{ required: true, message:'请输入圆心横坐标', trigger: 'blur' }
],
value: 0,
description: ''
},
{
prop: 'cy',
label: '圆心纵坐标',
type: types.Number,
precision: 0,
step:1,
rules:[
{ required: true, message:'请输入圆心纵坐标', trigger: 'blur' }
],
value: 0,
description: ''
},
{
prop: 'n',
label: '瓣数',
type: types.Number,
precision: 0,
step:1,
rules:[
{ required: true, message:'请输入瓣数', trigger: 'blur' }
],
value: 5,
description: '如瓣数等于 5 时,是我们熟悉的五角星。'
},
{
prop: 'r',
label: '半径',
type: types.Number,
precision: 0,
step:1,
rules:[
{ required: true, message:'请输入半径', trigger: 'blur' }
],
value: 10,
description: ''
},
{
prop: 'r0',
label: '内半径',
type: types.Number,
precision: 0,
step:1,
rules:[
{ required: true, message:'请输入内半径', trigger: 'blur' }
],
value: 5,
description: ''
}
]
}
}
};
export default elementConst;

View File

@ -0,0 +1,86 @@
import types from './types';
export default [
{
prop: 'z1',
label: '层级z1',
type: types.Number,
precision: 0,
min:0,
max:20,
step:1,
rules:[
{ required: true, message:'请输入层级z', trigger: 'blur' }
],
value: 10,
description: '控制图形的前后顺序。'
},
{
prop: 'z2',
label: '层级z2',
type: types.Number,
precision: 0,
min: 0,
max: 100,
step: 1,
rules:[
{ required: true, message:'请输入层级z2', trigger: 'blur' }
],
value: 50,
description: '控制图形的前后顺序。z2 值小的图形会被 z2 值大的图形覆盖。z2 相比 z1 优先级更低。'
},
{
prop: 'hide',
label: '是否隐藏',
type: types.Boolean,
rules:[
{ required: true, message:'请选择图形是否不可见', trigger: 'blur' }
],
value: false,
description: '图形是否不可见,选中时不绘制图形。'
},
{
prop: 'position',
label: '位置',
type: types.NumberArray,
length:2,
precision: 0,
min:0,
max:20000,
step:1,
rules:[
{ required: true, message:'请输入位置', trigger: 'blur' }
],
value: [0, 0],
description: '控制图形的位置。'
},
{
prop: 'scale',
label: '缩放',
type: types.NumberArray,
length:2,
precision: 0,
min:0.1,
max:1,
step:1,
rules:[
{ required: true, message:'请输入缩放', trigger: 'blur' }
],
value: [1, 1],
description: '控制图形的缩放。'
},
{
prop: 'rotation',
label: '旋转',
type: types.Number,
precision: 0,
min:0,
max:360,
step:1,
rules:[
{ required: true, message:'请输入旋转', trigger: 'blur' }
],
value: 0,
description: '控制图形的旋转。'
}
];

View File

@ -0,0 +1,120 @@
import types from './types';
export default [
{
prop: 'lineWidth',
label: '线宽',
type: types.Number,
min: 0,
max: 100,
step: 1,
precision: 0,
rules:[
{ required: true, message:'请输入线宽', trigger: 'blur' }
],
value: 1,
description: ''
},
{
prop: 'opacity',
label: '不透明度',
type: types.Number,
min: 0,
max: 1,
step: 0.05,
precision: 2,
rules:[
{ required: true, message:'请输入不透明度', trigger: 'blur' }
],
value: 1,
description: ''
},
{
prop: 'stroke',
label: '描边样式',
type: types.Color,
value: '#000',
description: ''
},
{
prop: 'lineDash',
label: '描边虚线',
type: types.NumberArray,
length:2,
min: 0,
max: 100,
step: 1,
precision: 0,
value: [0, 0],
description: '描边虚线样式,参考 SVG stroke-dasharray。'
},
{
prop: 'lineDashOffset',
label: '描边虚线偏移',
type: types.Number,
min: 0,
max: 100,
step: 1,
precision: 0,
value: [0, 0],
description: '描边虚线偏移,参考 SVG stroke-dashoffset。'
},
{
prop: 'shadowColor',
label: '阴影颜色',
type: types.Color,
value: 'rgba(0,0,0,0)',
description: ''
},
{
prop: 'shadowOffsetX',
label: '阴影横向偏移',
type: types.Number,
min: 0,
max: 100,
step: 1,
precision: 0,
value: 0,
description: ''
},
{
prop: 'shadowOffsetY',
label: '阴影纵向偏移',
type: types.Number,
min: 0,
max: 100,
step: 1,
precision: 0,
value: 0,
description: ''
},
{
prop: 'shadowBlur',
label: '阴影模糊大小',
type: types.Number,
min: 0,
max: 100,
step: 1,
precision: 0,
value: 0,
description: ''
},
{
prop: 'strokeNoScale',
label: '描边不缩放',
type: types.Boolean,
value: false,
description: '不选中时则会根据缩放同比例缩放描边粗细。'
// 描边粗细不随缩放而改变,
}
// {
// prop: 'blend',
// label: '混合模式',
// type: types.String,
// rules:[
// { required: true, message:'请输入混合模式', trigger: 'blur' }
// ],
// value: '',
// description: '混合模式,同 Canvas globalCompositeOperation。'
// }
];

View File

@ -0,0 +1,416 @@
import types from './types';
export default [
{
prop: 'text',
label: '内容',
type: types.String,
rules:[
{ required: true, message:'请输入内容', trigger: 'blur' }
],
value: '内容',
maxlength:1000,
description: ''
},
// {
// prop: 'font',
// label: '文字样式',
// type: types.String,
// rules:[
// { required: true, message:'请输入文字样式', trigger: 'blur' }
// ],
// value: '',
// description: '文字样式,由 fontSize、 fontFamily、 fontStyle、 fontWeight 组成,建议分别设置这几项而非直接设置 font。'
// },
// {
// prop: 'fontStyle',
// label: '',
// type: types.String,
// rules:[
// { required: true, message:'请输入文字样式', trigger: 'blur' }
// ],
// value: 'normal',
// description: '同 CSS font-style。'
// },
{
prop: 'fontWeight',
label: '粗细',
type: types.Number,
rules:[
{ required: true, message:'请输入文字粗细', trigger: 'blur' }
],
min: 300,
max: 800,
step: 100,
precisionFlag: 0,
value: 300,
description: ''
},
{
prop: 'fontSize',
label: '大小',
type: types.Number,
min: 8,
max: 30,
step: 1,
precisionFlag: 0,
rules:[
{ required: true, message:'请输入文字大小', trigger: 'blur' }
],
value: 16,
description: ''
},
// {
// prop: 'fontFamily',
// label: '文字族',
// type: types.String,
// rules:[
// { required: true, message:'请输入文字族', trigger: 'blur' }
// ],
// value: '文字样式',
// description: '同 CSS font-family。'
// },
{
prop: 'textFill',
label: '填充样式',
type: types.Color,
rules:[
{ required: true, message:'请输入文字填充样式', trigger: 'blur' }
],
value: '#000',
description: ''
},
{
prop: 'textStroke',
label: '描边样式',
type: types.Color,
rules:[
{ required: true, message:'请输入文字描边样式', trigger: 'blur' }
],
value: '#fff',
description: ''
},
{
prop: 'textStrokeWidth',
label: '描边宽度',
type: types.Number,
min: 0,
max: 100,
step: 1,
precisionFlag: 0,
rules:[
{ required: true, message:'请输入文字描边宽度', trigger: 'blur' }
],
value: 2,
description: ''
},
// {
// prop: 'textWidth',
// label: '文字宽度',
// type: types.Number,
// min: 0,
// max: 10000,
// step: 1,
// precisionFlag: 0,
// rules:[
// { required: true, message:'请输入文字宽度', trigger: 'blur' }
// ],
// value: 0,
// description: '文字宽度。'
// },
// {
// prop: 'textHeight',
// label: '文字高度',
// type: types.Number,
// min: 0,
// max: 10000,
// step: 1,
// precisionFlag: 0,
// rules:[
// { required: true, message:'请输入文字高度', trigger: 'blur' }
// ],
// value: 0,
// description: '文字高度,仅用于设置背景色时需要设置。'
// },
// {
// prop: 'textLineHeight',
// label: '文字行高',
// type: types.Number,
// min: 0,
// max: 10000,
// step: 1,
// precisionFlag: 0,
// rules:[
// { required: true, message:'请输入文字行高', trigger: 'blur' }
// ],
// value: 0,
// description: '文字行高。'
// },
// {
// prop: 'textPosition',
// label: '文字位置',
// type: types.Select,
// rules:[
// { required: true, message:'请输入文字位置', trigger: 'blur' }
// ],
// value: '',
// description: "文字位置,可以为 'inside'、 'left'、 'right'、 'top'、 'bottom',或一个二维数组 [x, y] 表示相对形状的位置。"
// },
// {
// prop: 'textOffset',
// label: '文字行高',
// type: types.Point,
// min: 0,
// max: 10000,
// step: 1,
// precisionFlag: 0,
// rules:[
// { required: true, message:'请输入文字位置偏移', trigger: 'blur' }
// ],
// value: 0,
// description: '文字位置偏移,包括 x、 y。'
// },
{
prop: 'textAlign',
label: '水平对齐方式',
type: types.Select,
rules:[
{ required: true, message:'请输入文字水平对齐方式', trigger: 'blur' }
],
value: 'left',
optionList:[{value:'left', label:'左对齐'}, {value:'center', label:'居中对齐'}, {value:'right', label:'右对齐'}],
// 文字水平对齐方式,可取值:'left'、 'center'、 'right',默认根据 textPosition 计算。
description: ''
},
{
prop: 'textVerticalAlign',
label: '垂直对齐方式',
type: types.Select,
rules:[
{ required: true, message:'请输入文字垂直对齐方式', trigger: 'blur' }
],
value: 'top',
optionList:[{value:'top', label:'顶对齐'}, {value:'middle', label:'居中对齐'}, {value:'bottom', label:'底对齐'}],
// 文字垂直对齐方式,可取值:'top'、 'middle'、 'bottom',默认根据 textPosition 计算。
description: ''
},
// {
// prop: 'textDistance',
// label: '文字与其对齐的边缘的距离',
// type: types.Number,
// min: 0,
// max: 10000,
// step: 1,
// precisionFlag: 0,
// rules:[
// { required: true, message:'请输入文字与其对齐的边缘的距离', trigger: 'blur' }
// ],
// value: 0,
// description: '文字与其对齐的边缘的距离,如 textPosition 为 top 时textDistance 表示与形状上方的距离。'
// },
{
prop: 'textShadowColor',
label: '阴影颜色',
type: types.Color,
rules:[
{ required: true, message:'请输入文字阴影颜色', trigger: 'blur' }
],
value: 'rgba(0,0,0,0)',
description: '文字阴影颜色。'
},
{
prop: 'textShadowBlur',
label: '阴影模糊大小',
type: types.Number,
min: 0,
max: 10000,
step: 1,
precisionFlag: 0,
rules:[
{ required: true, message:'请输入文字阴影模糊大小', trigger: 'blur' }
],
value: 0,
description: '文字阴影模糊大小。'
},
{
prop: 'textShadowOffsetX',
label: '阴影水平偏移',
type: types.Number,
min: 0,
max: 1000,
step: 1,
precisionFlag: 0,
rules:[
{ required: true, message:'请输入文字阴影水平偏移', trigger: 'blur' }
],
value: 0,
description: '文字阴影水平偏移。'
},
{
prop: 'textShadowOffsetY',
label: '阴影垂直偏移',
type: types.Number,
min: 0,
max: 1000,
step: 1,
precisionFlag: 0,
rules:[
{ required: true, message:'请输入文字阴影垂直偏移', trigger: 'blur' }
],
value: 0,
description: '文字阴影垂直偏移。'
},
{
prop: 'textBoxShadowColor',
label: '包围盒阴影颜色',
type: types.Color,
rules:[
{ required: true, message:'请输入文字包围盒阴影颜色', trigger: 'blur' }
],
value: 'rgba(0,0,0,0)',
description: '文字包围盒阴影颜色。'
},
{
prop: 'textBoxShadowBlur',
label: '包围盒阴影模糊大小',
type: types.Number,
min: 0,
max: 1000,
step: 1,
precisionFlag: 0,
rules:[
{ required: true, message:'请输入文字包围盒阴影模糊大小', trigger: 'blur' }
],
value: 0,
description: '文字包围盒阴影模糊大小。'
},
{
prop: 'textBoxShadowOffsetX',
label: '包围盒阴影水平偏移',
type: types.Number,
min: 0,
max: 1000,
step: 1,
precisionFlag: 0,
rules:[
{ required: true, message:'请输入文字包围盒阴影水平偏移', trigger: 'blur' }
],
value: 0,
description: '文字包围盒阴影水平偏移。'
},
{
prop: 'textBoxShadowOffsetY',
label: '包围盒阴影垂直偏移',
type: types.Number,
min: 0,
max: 1000,
step: 1,
precisionFlag: 0,
rules:[
{ required: true, message:'请输入文字包围盒阴影垂直偏移。', trigger: 'blur' }
],
value: 0,
description: '文字包围盒阴影垂直偏移。'
},
// {
// prop: 'transformText',
// label: '文字是否跟随变换效果',
// type: types.Boolean,
// rules:[
// { required: true, message:'请选择文字是否跟随变换效果', trigger: 'blur' }
// ],
// value: false,
// description: '文字是否跟随变换效果,仅对 Path 或 Image 元素有效'
// },
// {
// prop: 'textRotation',
// label: '文字旋转角度',
// type: types.Number,
// min: 0,
// max: 360,
// step: 1,
// precisionFlag: 0,
// rules:[
// { required: true, message:'请输入文字旋转角度', trigger: 'blur' }
// ],
// value: 0,
// description: '文字旋转角度,仅对 Path 或 Image 元素有效,并且 transformText 应设置为 false。'
// },
// {
// prop: 'textOrigin',
// label: '文字变换中心',
// type: types.Point,
// min: 0,
// max: 10000,
// step: 1,
// precisionFlag: 0,
// rules:[
// { required: true, message:'请输入文字变换中心', trigger: 'blur' }
// ],
// value: 0,
// description: "文字变换中心,可以是 'center' 或一个二维数组 [x, y] 表示相对形状的位置,默认值是 textPosition。"
// },
{
prop: 'textBackgroundColor',
label: '包围盒颜色',
type: types.Color,
rules:[
{ required: true, message:'请输入文字包围盒颜色', trigger: 'blur' }
],
value: 'rgba(0,0,0,0)',
description: '文字包围盒颜色。'
},
{
prop: 'textBorderColor',
label: '文字包围盒描边颜色',
type: types.Color,
rules:[
{ required: true, message:'请输入文字包围盒描边颜色', trigger: 'blur' }
],
value: 'rgba(0,0,0,0)',
description: '文字包围盒描边颜色。'
},
{
prop: 'textBorderWidth',
label: '包围盒描边宽度',
type: types.Number,
min: 0,
max: 1000,
step: 1,
precisionFlag: 0,
rules:[
{ required: true, message:'请输入文字包围盒描边宽度', trigger: 'blur' }
],
value: 0,
description: '文字包围盒描边宽度。'
},
{
prop: 'textBorderRadius',
label: '圆角大小',
type: types.Number,
min: 0,
max: 360,
step: 1,
precisionFlag: 0,
rules:[
{ required: true, message:'请输入文字圆角大小', trigger: 'blur' }
],
value: 0,
description: ''
},
{
prop: 'textPadding',
label: '文字内边距',
type: types.NumberArray,
length:4,
min: 0,
max: 1000,
step: 1,
precisionFlag: 0,
rules:[
{ required: true, message:'请输入文字内边距', trigger: 'blur' }
],
value: [0, 0, 0, 0],
description: '文字内边距,[2, 3, 4, 5] 的形式,单位是像素。'
}
];

View File

@ -0,0 +1,49 @@
export default [
{
prop: 'outerWidth',
label: '外部宽度',
type: types.Number,
min: 2,
max: 100,
step: 1,
rules:[
{ required: true, message:'请输入外部宽度', trigger: 'blur' }
],
value: 0,
description: '包含了 textPadding 的宽度,超出这个范围就裁剪。'
},
{
prop: 'outerHeight',
label: '外部高度',
type: types.Number,
min: 2,
max: 100,
step: 1,
rules:[
{ required: true, message:'请输入外部高度', trigger: 'blur' }
],
value: 0,
description: '包含了 textPadding 的高度,超出这个范围就裁剪。'
},
{
prop: 'ellipsis',
label: '截断超出部分',
type: types.String,
rules:[
{ required: true, message:'请输入截断超出部分', trigger: 'blur' }
],
value: '',
description: '默认用省略号表示超出部分,也可以对其进行自定义。'
},
{
prop: 'placeholder',
label: '截断文字',
type: types.String,
rules:[
{ required: true, message:'请输入截断文字', trigger: 'blur' }
],
value: '',
description: '如果空间过小,导致省略号也显示不下,但是又不想空着,可能得有个什么标记表示这里是有字符的,就用个 “点”,就是在这个 placeholder 里设置。'
}
]

View File

@ -0,0 +1,40 @@
import elementConst from './elementConst';
import form2Base from './form2Base';
const formBuilder = {
buildForm(el) {
const form = {};
form.type = el.type;
form.name = el.name;
form.formGroup = [];
form.model = {};
// 添加通用配置
form.model['base'] = {};
form2Base.forEach(each=>{
form.model['base'][each.prop] = each.value;
});
form.formGroup.push({name:'通用配置', type:'base', styleList:form2Base});
const list = [{name:'绘图配置', type:'shape'}, {name:'样式配置', type:'style'}];
list.forEach(eachType=>{
form.model[eachType.type] = {};
const eachList = el.formList[eachType.type];
eachList.forEach(each=>{
form.model[eachType.type][each.prop] = each.value;
});
form.formGroup.push({name:eachType.name, type: eachType.type, styleList:eachList});
});
return form;
},
buildFormList() {
const formList = [];
const elementList = Object.values(elementConst);
elementList.forEach(el=>{
formList.push(this.buildForm(el));
});
return formList;
}
};
export default formBuilder;

View File

@ -0,0 +1,10 @@
export default {
String: 'String',
Number: 'Number',
Select: 'Select',
Boolean: 'Boolean',
Color: 'Color',
Point: 'Point',
Points: 'Points',
NumberArray: 'NumberArray'
};

View File

@ -0,0 +1,20 @@
export {default as Group} from 'zrender/src/container/Group';
export {default as Image} from 'zrender/src/graphic/Image';
export {default as Text} from 'zrender/src/graphic/Text';
export {default as Arc} from './shape/ArcShape';
export {default as BezierCurve} from 'zrender/src/graphic/shape/BezierCurve';
export {default as Circle} from 'zrender/src/graphic/shape/Circle';
export {default as Droplet} from 'zrender/src/graphic/shape/Droplet';
export {default as Ellipse} from 'zrender/src/graphic/shape/Ellipse';
export {default as Heart} from 'zrender/src/graphic/shape/Heart';
export {default as Isogon} from 'zrender/src/graphic/shape/Isogon';
export {default as Line} from 'zrender/src/graphic/shape/Line';
export {default as Polygon} from 'zrender/src/graphic/shape/Polygon';
export {default as Polyline} from 'zrender/src/graphic/shape/Polyline';
export {default as Rect} from 'zrender/src/graphic/shape/Rect';
export {default as Ring} from 'zrender/src/graphic/shape/Ring';
export {default as Rose} from 'zrender/src/graphic/shape/Rose';
export {default as Sector} from './shape/SectorShape';
export {default as Star} from 'zrender/src/graphic/shape/Star';
export {default as Trochoid} from 'zrender/src/graphic/shape/Trochoid';
export {default as Svg} from './shape/Svg';

View File

@ -0,0 +1,17 @@
import Arc from 'zrender/src/graphic/shape/Arc';
export default class ArcShape extends Arc {
constructor(opts) {
opts.shape.startAngle = Math.PI / 180 * opts.shape.startAngle;
opts.shape.endAngle = Math.PI / 180 * opts.shape.endAngle;
super(opts);
}
setStyle(style) {
super.setStyle(style);
}
attr(key, value) {
super.attr(key, value);
}
}

View File

@ -0,0 +1,17 @@
import Sector from 'zrender/src/graphic/shape/Sector';
export default class ArcShape extends Sector {
constructor(opts) {
opts.shape.startAngle = Math.PI / 180 * opts.shape.startAngle;
opts.shape.endAngle = Math.PI / 180 * opts.shape.endAngle;
super(opts);
}
setStyle(style) {
super.setStyle(style);
}
attr(key, value) {
super.attr(key, value);
}
}

View File

@ -0,0 +1,26 @@
import Path from 'zrender/src/graphic/Path';
import * as pathTool from 'zrender/src/tool/path';
export default class Svg extends Path {
constructor(opts) {
super(pathTool.createFromString(opts.shape.path, {
...opts,
type: 'Svg'
}));
super.attr('position', [opts.shape.x, opts.shape.y]);
}
setShape(shape) {
super.setShape(shape);
this.attr('position', [shape.x, shape.y]);
}
setStyle(style) {
super.setStyle(style);
}
attr(key, value) {
super.attr(key, value);
}
}

View File

@ -0,0 +1,45 @@
import shapeLayer from './constant/shapeLayer';
export default class DragHandle {
constructor(map, controller) {
this.$zr = map.getZr();
this.$controller = controller;
this.$option = map.getOption();
this.$painter = map.getPainter();
this._dragging = false;
}
isDragging() {
return this._dragging;
}
onDragStart(e) {
const storage = this.$controller.getStorage();
if (e.code && storage.has(e.code)) {
this._dragging = true;
}
}
onDragging(e) {
const dx = e.dx;
const dy = e.dy;
const scaleRate = this.$option.getScaleRate();
const storage = this.$controller.getStorage();
e.dx = dx / scaleRate;
e.dy = dy / scaleRate;
if (this._dragging) {
storage.values().forEach(target => {
if (target) {
target.shapeFactory.hideHightLight(target);;
target.drift(e);
target.shapeFactory.showHightLight(target);
}
});
}
}
onDragEnd(e) {
this._dragging = false;
}
}

View File

@ -0,0 +1,114 @@
import * as utils from '../utils/utils';
import Group from 'zrender/src/container/Group';
import AbstractShape from '../core/abstractShape';
import shapeRender from '../constant/shapeRender';
import shapeEvent from '../constant/shapeEvent';
class Compose extends AbstractShape {
constructor(args) {
super(args);
this.create();
}
create() {
const that = this;
// mouse进入事件
function onmouseover(e) {
that.shapeFactory.trigger(shapeEvent.ShowTips, e, 'text for test');
}
// mouse移动事件
function onmousemove(e) {
onmouseover(e);
}
// mouse离开事件
function onmouseout(e) {
that.shapeFactory.trigger(shapeEvent.HideTips, e);
}
this.instance = new Group({
...shapeRender,
...this.model.base,
code: this.model.code,
type: this.model.type,
onmouseover,
onmousemove,
onmouseout
});
this.model.elementCodes.forEach(code => {
const el = this.shapeFactory.getShapeByCode(code);
if (el) {
this.shapeFactory.removeFromLayer(el.type, el);
this.instance.add(el);
}
})
this.instance.scale = this.model.base.scale;
this.instance.position = this.model.base.position;
this.instance.rotation = this.model.base.rotation*Math.PI/180;
this.instance.origin = utils.createOrigin(this.instance);
this.instance.transform = utils.createTransform({scale: this.model.base.scale, position: this.model.base.position, rotation: this.model.base.rotation*Math.PI/180});
this.add(this.instance);
this.setInvisible(this.model.base.hide)
}
// 设置高亮
active() {
}
// 取消高亮
inactive() {
}
// 设置获取焦点
focus() {
}
// 设置取消焦点
blur() {
}
// 绑定数据
combine() {
this.inactive();
this.shapeFactory.hideHightLight(this);
this.model.elementCodes.forEach(code => {
const el = this.shapeFactory.getShapeByCode(code);
if (el && el.model) {
this.shapeFactory.hideHightLight(el);
this.shapeFactory.removeFromLayer(el.type, el);
this.instance.add(el);
el.model.composeCode = this.code;
el.attr(el.model);
}
})
}
// 解除绑定
uncouple() {
this.inactive();
this.shapeFactory.hideHightLight(this);
this.model.elementCodes.forEach(code => {
const el = this.shapeFactory.getShapeByCode(code);
if (el && el.model) {
this.shapeFactory.hideHightLight(el);
this.instance.remove(el);
this.shapeFactory.addToLayer(el.type, el);
el.model.composeCode = '';
el.attr(el.model);
}
})
this.model.elementCodes = [];
}
// 获取依赖图形
getDepShapes() {
return this.model.elementCodes.reduce((shapes, code) => shapes.concat(this.shapeFactory.getShapeByCode(code).getDepShapes()), [this]);
}
// 设置状态
setState(state) {}
}
export default Compose;

View File

@ -0,0 +1,118 @@
import * as graphic from '../core/graphic';
import * as utils from '../utils/utils';
import AbstractShape from '../core/abstractShape';
import shapeEvent from '../constant/shapeEvent';
import shapeRender from '../constant/shapeRender';
import shapeLayer from '../constant/shapeLayer';
class Element extends AbstractShape {
constructor(args) {
super(args);
this.create();
}
create() {
const that = this;
const elementBuilder = graphic[this.type];
if (elementBuilder) {
// mouse进入事件
function onmouseover(e) {
that.shapeFactory.trigger(shapeEvent.ShowTips, e, 'text for test');
}
// mouse移动事件
function onmousemove(e) {
onmouseover(e);
}
// mouse离开事件
function onmouseout(e) {
that.shapeFactory.trigger(shapeEvent.HideTips, e);
}
this.instance = new elementBuilder({
...shapeRender,
...this.model.base,
code: this.model.code,
type: this.model.type,
shape: {...this.model.shape},
style: {...this.model.style},
onmouseover,
onmousemove,
onmouseout
});
this.instance.scale = this.model.base.scale;
this.instance.position = this.model.base.position;
this.instance.rotation = this.model.base.rotation*Math.PI/180;
this.instance.origin = utils.createOrigin(this.instance);
this.instance.transform = utils.createTransform({scale: this.model.base.scale, position: this.model.base.position, rotation: this.model.base.rotation*Math.PI/180});
this.add(this.instance);
this.setInvisible(this.model.base.hide);
}
}
// 设置高亮
active() {
}
// 取消高亮
inactive() {
}
// 设置获取焦点
focus() {
}
// 设置取消焦点
blur() {
}
// 绑定数据
combine() {
this.inactive();
this.shapeFactory.hideHightLight(this);
const compose = this.shapeFactory.getShapeByCode(this.model.composeCode);
if (compose &&
compose.model &&
compose.model.elementCodes) {
this.shapeFactory.hideHightLight(compose);
const index = compose.model.elementCodes.findIndex(this.code);
if (index < 0) {
compose.model.elementCodes.push(this.code);
this.shapeFactory.removeFormLayer(this.type, this);
compose.instance.add(this);
compose.attr(compose.model);
}
}
}
// 解除绑定
uncouple() {
this.inactive();
this.shapeFactory.hideHightLight(this);
const compose = this.shapeFactory.getShapeByCode(this.model.composeCode);
if (compose &&
compose.model &&
compose.model.elementCodes) {
this.shapeFactory.hideHightLight(compose);
const index = compose.model.elementCodes.findIndex(this.code);
if (index >= 0) {
compose.model.elementCodes.splice(index, 1);
compose.instance.remove(this);
compose.attr(compose.model);
this.shapeFactory.addToLayer(this.type, this);
}
}
}
// 获取依赖图形
getDepShapes() {
return [this];
}
// 设置状态
setState(state) {}
}
export default Element;

View File

@ -0,0 +1,267 @@
import * as zrUtil from 'zrender/src/core/util';
import * as graphic from '../core/graphic';
import Eventful from 'zrender/src/mixin/Eventful';
import TipsHandle from './tipsHandle';
import Compose from './compose';
import Element from './element';
import shapeType from '../constant/shapeType';
import shapeRender from '../constant/shapeRender';
import shapeEvent from '../constant/shapeEvent';
import orders from '../utils/orders';
import shapeLayer from '../constant/shapeLayer';
import templateParser from './templateParser';
import defaultStyle from '../config/defaultStyle';
const None = e => null;
const shapeBuilderMap = {
[shapeType.Element]: Element,
[shapeType.Compose]: Compose
}
const shapeSensitizeStyle = {
border: {
borderStyle: {
lineDash: [4, 4],
lineWidth: 1,
stroke: '#000',
fill: 'transparent'
}
}
}
class Perspective extends graphic.Group {
constructor(args) {
super(args);
}
_createBorder(rect) {
this._border = new graphic.Rect({
...shapeRender,
subType: '@border',
z: 9999,
silent: true,
shape: {
...rect
},
style: {
...shapeSensitizeStyle.border.borderStyle
}
})
this.add(this._border);
}
_createAngles() {
}
render(rect) {
this.removeAll();
this._createBorder(rect);
this._createAngles();
}
setShape(rect) {
this.render(rect);
}
}
class ShapeFactory extends Eventful {
constructor(map) {
super();
this.$map = map;
this.$painter = map.getPainter();
this.$controller = map.getController();
this.sensitize = new Perspective({subType: '@ignore'});
this.tipsHandle = new TipsHandle(map);
this.source = {};
this.mapTemplate = {};
this.mapShape = {};
this.initEventHandle();
}
initEventHandle() {
const that = this;
const showBorderHandle = this.showBorder;
const hideBorderHandle = this.hideBorder;
const showTipsHandle = this.tipsHandle.onShow;
const hideTipsHandle = this.tipsHandle.onHide;
this.on(shapeEvent.ShowBorder, showBorderHandle, this);
this.on(shapeEvent.HideBorder, hideBorderHandle, this);
this.on(shapeEvent.ShowTips, showTipsHandle, this.tipsHandle);
this.on(shapeEvent.HideTips, hideTipsHandle, this.tipsHandle);
this.dispose = function() {
that.off(shapeEvent.ShowBorder, showBorderHandle);
that.off(shapeEvent.HideBorder, showBorderHandle);
that.off(shapeEvent.ShowTips, showTipsHandle);
that.off(shapeEvent.HideTips, hideTipsHandle);
}
}
parseTemplates(list=[]) {
list.forEach(el => {
this.mapTemplate[el.type] = templateParser.parse(el);
})
}
parse(source={}, take=None) {
try {
this.source = source;
zrUtil.each(source.elementList ||[], model => {
take(this.addShape(this.createShape(model, shapeType.Element)));
}, this);
zrUtil.each(source.composeList ||[], model => {
take(this.addShape(this.createShape(model, shapeType.Compose)));
}, this);
} catch (error) {
console.error('[ERROR] ', error);
}
}
createShape(model={}, shapeType) {
let shape = null;
const shapeBuilder = shapeBuilderMap[shapeType];
if (shapeBuilder) {
shape = new shapeBuilder({model, shapeType, shapeFactory: this});
}
return shape;
}
addShape(shape) {
if (shape && shape.code) {
this.mapShape[shape.code] = shape;
shape.combine();
}
return shape;
}
removeShape(shape) {
if (shape && shape.code) {
shape.uncouple();
delete this.mapShape[shape.code];
}
return shape;
}
updateSource(model={}, action={}) {
switch (action.shapeType) {
case shapeType.Compose: return update2List(this.source, model, action, 'composeList');
case shapeType.Element: return update2List(this.source, model, action, 'elementList');
}
return null;
}
showHightLight(shape) {
const target = this.$controller.getTarget();
if (shape.instanceHightLight) {
shape.instanceHightLight.setShape(shape.getBoundingRect())
this.$painter.addToLayer(shapeLayer.HightLight, shape.instanceHightLight);
}
if (target == shape) {
this.showBorder(shape);
}
shape.traverse(el => {
el.attr({ z: shapeRender.z + 9 })
});
shape.active();
}
hideHightLight(shape) {
const target = this.$controller.getTarget();
if (shape.instanceHightLight) {
this.$painter.removeFromLayer(shapeLayer.HightLight, shape.instanceHightLight);
}
if (target != shape) {
this.hideBorder(shape);
}
shape.traverse(el => {
el.attr({ z: shapeRender.z })
});
shape.inactive();
}
showBorder(shape) {
this.sensitize.setShape(shape.getBoundingRect());
this.$painter.addToLayer(shapeLayer.HightLight, this.sensitize);
}
hideBorder(shape) {
this.$painter.removeFromLayer(shapeLayer.HightLight, this.sensitize);
}
addToLayer(level, shape) {
return this.$painter.addToLayer(level, shape);
}
removeFromLayer(level, shape) {
return this.$painter.removeFromLayer(level, shape);
}
isDrawing() {
this.$map.isDrawing();
}
getSource() {
return this.source;
}
getMapTemplate() {
return this.mapTemplate;
}
getMapShape() {
return this.mapShape;
}
getShapeByCode(code) {
return this.mapShape[code];
}
clear() {
this.source = {};
this.mapTemplate = {};
this.mapShape = {};
}
destroy() {
this.clear();
this.dispose();
}
}
function update2List(source, model, action, name='') {
const list = source[name];
if (!list) { source[name] = []; }
let updateModel = model;
const i = list.findIndex(elem => { return elem.code == model.code; })
switch(action.order) {
case orders.Binding:
case orders.Add:
i < 0 && list.push(model);
break;
case orders.Unbinding:
case orders.Delete:
i >= 0 && list.splice(i, 1);
break;
case orders.Update:
updateModel = Object.assign(list[i]||{}, model)
break;
}
return updateModel;
}
export default ShapeFactory;

View File

@ -0,0 +1,45 @@
import * as utils from '../utils/utils';
class TemplateParser {
constructor() {
}
parse(template) {
return {
type: template.type,
name: template.name,
isActive: template.isActive,
isFocus: template.isFocus,
mapShape: this.parseShape(template.shapeList),
mapState: this.parseState(template.stateList),
mapEvent: this.parseEvent(template.eventList),
}
}
parseShape(list=[], map={}) {
list.forEach(el => {
const mapShape = map[el.name] = {};
mapShape[utils.defStatus] = { status: utils.defStatus, style: el.style, shape: el.shape };
(el.stateList||[]).forEach(it => {
mapShape[it.status] = it;
})
})
return map;
}
parseState(list=[], map={}) {
list.forEach(el => {
map[el.status] = el;
})
return map;
}
parseEvent(list=[], map={}) {
list.forEach(el => {
map[el.status] = el;
})
return map;
}
}
export default new TemplateParser();

View File

@ -0,0 +1,55 @@
import * as graphic from '../core/graphic';
import shapeLayer from '../constant/shapeLayer';
import shapeRender from '../constant/shapeRender';
function shapeStyleBuilder() {
return {
...shapeRender,
z: 10000,
silent: true,
style: {
x: 0,
y: 0,
fontSize: 16,
fontWeight: 'normal',
textBackgroundColor: '#feffc8',
text: '',
textFill: '#000',
textPadding: [7, 14],
textAlign: 'left',
textVerticalAlign: 'bottom',
textBorderColor: '#666666',
textBorderWidth: 0,
textBorderRadius: 4,
textLineHeight: 22,
textBoxShadowColor: 'rgba(0,0,0,.3)',
textBoxShadowOffsetX: 0,
textBoxShadowOffsetY: 1,
textBoxShadowBlur: 3,
opacity: 0.8
}
}
}
export default class TipsHandle {
constructor(map) {
this.$map = map;
this.message = new graphic.Text(shapeStyleBuilder());
}
onShow(e, text) {
const {offsetX, offsetY} = e;
const painter = this.$map.getPainter();
const option = this.$map.getOption();
const x = (offsetX + option.offsetX) / option.scaleRate;
const y = (offsetY + option.offsetY) / option.scaleRate;
this.message.setStyle({ x, y, text });
painter.addToLayer(shapeLayer.Tips, this.message);
}
onHide(e) {
const painter = this.$map.getPainter();
painter.removeFromLayer(shapeLayer.Tips, this.message);
this.message.setStyle('text', '');
}
}

View File

@ -0,0 +1,32 @@
export default class KeyBoardHandle {
constructor(map, controller) {
this.$map = map;
this.$controller = controller;
}
execFunc(moduleName, funcName, args) {
const module = this.$controller[moduleName]||{};
const func = module[funcName];
if (func instanceof Function) {
func.apply(module, args);
}
}
onKeydown(e) {
switch (e.key) {
case 'v':
this.execFunc('selectHandle', 'setDraggable', ['vertical']);
break;
case 'h':
this.execFunc('selectHandle', 'setDraggable', ['horizontal']);
break;
case 'Escape':
this.execFunc('selectHandle', 'setDraggable', [true]);
break;
}
}
onKeyup(e) {
}
}

414
src/iscs_new/map.js Normal file
View File

@ -0,0 +1,414 @@
import _ from 'lodash';
import * as utils from './utils/utils';
import zrender from 'zrender';
import events from './utils/events';
import Eventful from 'zrender/src/mixin/Eventful';
import Painter from './painter';
import Option from './option';
import Controller from './controller';
import StateHandle from './stateHandle';
import ShapeFactory from './factory';
import orders from './utils/orders';
import shapeType from './constant/shapeType';
const renderer = 'canvas';
const devicePixelRatio = 1;
class JMap {
constructor(opts) {
// 内部鼠标事件
this.events = {
__Pan: '__pan__',
__Zoom: '__zoom__',
__DragStart: '__dragStart__',
__Dragging: '__dragging__',
__DragEnd: '__dragEnd__',
__SelectStart: '__selectStart__',
__Selecting: '__selecting__',
__SelectEnd: '__selectEnd__',
__Selected: '__selected__',
__Keyup: '__keyup__',
__Keydown: '__keydown__'
};
// 初始化Map实例
this.initMapInstance(opts);
}
// 初始化属性有鼠标事件 缩放等
initMapInstance(opts) {
const width = opts.dom.clientWidth;
const height = opts.dom.clientHeight;
const optionHandler = this.setOption.bind(this);
this.draw = opts.draw;
// 实例化zr
this.$zr = zrender.init(opts.dom, { renderer, devicePixelRatio, width, height, ...utils.deepClone(opts.config || {})});
this.$zr.dom.setAttribute('tabIndex', -1);
this.$zr.dom.style.cursor = 'auto';
// 实例化缩放偏移缩放参数
this.$option = new Option({ scaleRate: 1, offsetX: 0, offsetY: 0, ...utils.deepClone(opts.option || {})}, (dataZoom) => { this.$controller.trigger(events.DataZoom, dataZoom); }); // 缩放
// 实例化绘图模块
this.$painter = new Painter(this);
this.$painter.updateZrSize({width: this.$zr.getWidth(), height: this.$zr.getHeight()});
this.$painter.updateTransform(this.$option);
// 实例化事件分发模块
this.$controller = new Controller(this);
this.$controller.enable();
this.$controller.on(this.events.__Pan, optionHandler);
this.$controller.on(this.events.__Zoom, optionHandler);
// 名声周期发射器
this.$eventEmitter = new Eventful({});
// 数据容器工厂
this.$shapeFactory = new ShapeFactory(this);
// 状态处理器
this.$stateHandle = new StateHandle(this);
this.disable = function() {
this.off(this.events.Pan, optionHandler);
this.off(this.events.Zoom, optionHandler);
};
// 加载插件
this.plugins = opts.plugins || [];
this.plugins.forEach(el => { this.use(el); });
}
setMap(templates = [], source = {}, eventOpts = {}) {
// 清楚数据
this.$shapeFactory.clear();
this.$painter.clear();
// 绑定事件
this.$controller.enable(eventOpts);
// 更新视图位置
this.$painter.updateTransform(this.$option);
// 解析模板
this.$shapeFactory.parseTemplates(templates);
// 数据加载完成 回调
this.$eventEmitter.trigger(events.DataLoaded);
// 初次渲染视图
this.repaint(source);
// 设置默认状态
this.setDefaultState();
// 视图加载完成 回调
this.$eventEmitter.trigger(events.ViewLoaded);
// 返回视图缩放偏移
this.$option.notice(this.$option);
return this;
}
setDefaultState() {
this.$eventEmitter.trigger(events.StateLoaded);
return this;
}
setOption(opts = {}) {
this.$option.update(opts.type == this.events.__Zoom ? pullBack(this.$zr, this.$option, opts) : opts);
this.$painter.updateTransform(this.$option);
this.$controller.disable();
this.$controller.enable();
this.$eventEmitter.trigger(events.OptionUpdate);
return this;
}
setCenter(code) {
const shape = this.$shapeFactory.getShapeByCode(code);
if (shape && shape) {
var rect = utils.createBoundingRect(shape);
var center = utils.calculateDCenter(rect, { width: this.$zr.getWidth(), height: this.$zr.getHeight() });
this.setOption(center);
}
return this;
}
setBackgroundColor(color) {
this.$zr.setBackgroundColor(color);
return this;
}
setCursorStyle(cursorStyle = 'auto') {
this.$zr.setCursorStyle(cursorStyle);
return this;
}
repaint(source = {}) {
this.$shapeFactory.parse(source, shape => {
if (shape) {
this.$painter.add(shape);
}
});
return this;
}
render(list = []) {
list.forEach(({model, action}) => {
let updateModel = this.isDrawing() ? this.$shapeFactory.updateSource(model, action) : model;
let curShape = this.$shapeFactory.getShapeByCode(updateModel.code);
let deps = null;
let oldShape = null;
let newShape = null;
if (updateModel) {
this.$controller.clear();
switch (action.order) {
case orders.Binding:
case orders.Add:
newShape = this.$shapeFactory.createShape(updateModel, action.shapeType);
this.$shapeFactory.addShape(newShape);
this.$painter.add(newShape);
break;
case orders.Delete:
deps = curShape.getDepShapes();
deps.forEach(el => {
updateModel = this.isDrawing() ? this.$shapeFactory.updateSource(el, {...action, shapeType: el.shapeType}) : el;
if (updateModel) {
curShape = this.$shapeFactory.getShapeByCode(updateModel.code);
oldShape = this.$shapeFactory.removeShape(curShape);
this.$painter.remove(oldShape);
}
});
break;
case orders.Update:
oldShape = this.$shapeFactory.removeShape(curShape);
this.$painter.remove(oldShape);
newShape = this.$shapeFactory.createShape(updateModel, action.shapeType);
this.$shapeFactory.addShape(newShape);
this.$painter.add(newShape);
break;
case orders.Unbinding:
oldShape = this.$shapeFactory.removeShape(curShape);
this.$painter.remove(oldShape);
this.$shapeFactory.addShape(oldShape);
this.$painter.add(oldShape);
break;
// case orders.changeStatus:
// // model
// this.$painter.update(this.$stateHandle.update(this.$shapeFactory, []));
// debugger;
// break;
// case orders.ResetStatus:
// debugger;
// break;
}
}
});
this.$eventEmitter.trigger(events.ViewUpdate, list);
return this;
}
update(list = []) {
this.$painter.update(this.$stateHandle.update(this.$shapeFactory, list));
this.$eventEmitter.trigger(events.StateUpdate, list);
return this;
}
getEvents() {
return this.events;
}
getZr() {
return this.$zr;
}
getOption() {
return this.$option;
}
getPainter() {
return this.$painter;
}
getController() {
return this.$controller;
}
getStorage() {
return this.$controller.getStorage();
}
getShapeFactory() {
return this.$shapeFactory;
}
getShapeByCode(code) {
return this.$shapeFactory.getShapeByCode(code);
}
getSource() {
return this.$shapeFactory.getSource();
}
getMapShape() {
return this.$shapeFactory.getMapShape();
}
getMapTemplate() {
return this.$shapeFactory.getMapTemplate();
}
resize(opt) {
this.$zr.resize(opt);
this.$painter.updateZrSize(opt);
}
refresh() {
this.$painter.refresh();
}
isDrawing() {
return this.draw;
}
clear() {
this.$shapeFactory.clear();
this.$controller.clear();
this.$painter.clear();
this.$zr.refresh();
}
use(el) {
this.plugins.includes(el) || this.plugins.push(el);
el.install(this);
}
destroy() {
this.disable();
this.clear();
this.$shapeFactory.destroy();
this.$controller.destroy();
this.$painter.destroy();
this.$zr && this.$zr.dispose();
this.plugins.forEach(el => {
el.uninstall(this);
});
this.plugins = [];
}
on(name, cb, context) {
const idx = Object.values(events).indexOf(name);
if (idx >= 0) {
switch (name) {
case events.DataLoaded:
this.$eventEmitter.on(events.DataLoaded, cb, context);
break;
case events.ViewLoaded:
this.$eventEmitter.on(events.ViewLoaded, cb, context);
break;
case events.StateLoaded:
this.$eventEmitter.on(events.StateLoaded, cb, context);
break;
case events.ViewUpdate:
this.$eventEmitter.on(events.ViewUpdate, cb, context);
break;
case events.StateUpdate:
this.$eventEmitter.on(events.StateUpdate, cb, context);
break;
case events.OptionUpdate:
this.$eventEmitter.on(events.OptionUpdate, cb, context);
break;
case events.Click:
this.$controller.on(events.Click, cb, context);
break;
case events.ContextMenu:
this.$controller.on(events.ContextMenu, cb, context);
break;
case events.Reflect:
this.$controller.on(events.Reflect, _.throttle(cb, 200), context);
break;
case events.DataZoom:
this.$controller.on(events.DataZoom, cb, context);
break;
case events.Keydown:
this.$controller.on(events.Keydown, cb, context);
break;
case events.Keypress:
this.$controller.on(events.Keypress, cb, context);
break;
case events.Keyup:
this.$controller.on(events.Keyup, cb, context);
break;
}
}
}
off(name, cb) {
const idx = Object.values(events).indexOf(name);
if (idx >= 0) {
switch (name) {
case events.DataLoaded:
this.$eventEmitter.off(events.DataLoaded, cb, context);
break;
case events.ViewLoaded:
this.$eventEmitter.off(events.ViewLoaded, cb, context);
break;
case events.StateLoaded:
this.$eventEmitter.off(events.StateLoaded, cb, context);
break;
case events.ViewUpdate:
this.$eventEmitter.off(events.ViewUpdate, cb, context);
break;
case events.StateUpdate:
this.$eventEmitter.off(events.StateUpdate, cb, context);
break;
case events.OptionUpdate:
this.$eventEmitter.off(events.OptionUpdate, cb, context);
break;
case events.Click:
this.$controller.off(events.Click, cb);
break;
case events.ContextMenu:
this.$controller.off(events.ContextMenu, cb);
break;
case events.Reflect:
this.$controller.off(events.Reflect, _.throttle(cb, 200));
break;
case events.DataZoom:
this.$controller.off(events.DataZoom, cb);
break;
case events.Keydown:
this.$controller.off(events.Keydown, cb);
break;
case events.Keypress:
this.$controller.off(events.Keypress, cb);
break;
case events.Keyup:
this.$controller.off(events.Keyup, cb);
break;
}
}
}
}
function pullBack(zr, option = {}, payload = {}) {
const zrWidth = zr.getWidth();
const zrHeight = zr.getHeight();
const originX = payload.originX || zrWidth / 2;
const originY = payload.originY || zrHeight / 2;
const x = (option.offsetX + originX) / option.scaleRate;
const y = (option.offsetY + originY) / option.scaleRate;
const newScaleRate = option.getScaleRate(payload.scale);
const dx = originX - (x * newScaleRate - option.offsetX);
const dy = originY - (y * newScaleRate - option.offsetY);
return {...payload, dx, dy};
}
export default JMap;

84
src/iscs_new/maps.js Normal file
View File

@ -0,0 +1,84 @@
import * as utils from './utils/utils';
import * as Dom from './utils/dom';
import JMap from './map';
const DOM_ATTRIBUTE_KEY = '_maps_instance_';
class JMaps {
constructor() {
this._version = '1.0.0';
this.instances = {};
}
init(opts) {
if (!opts) {
throw new Error('Initialize failed: invalid params.');
}
if (!opts.dom && opts.el) {
opts['dom'] = document.getElementById(opts.el);
}
if (!opts.dom) {
throw new Error('Initialize failed: invalid dom and el.');
}
const existInstance = this.getInstanceByDom(opts.dom);
if (existInstance) {
console.warn('There is a chart instance already initialized on the dom.');
return existInstance;
}
if (Dom.isDom(opts.dom) && opts.dom.nodeName.toUpperCase() !== 'CANVAS' &&
(
(!opts.dom.clientWidth && (!opts || opts.width == null)) ||
(!opts.dom.clientHeight && (!opts || opts.height == null))
)
) {
console.warn('Can\'t get DOM width or height. Please check ' +
'dom.clientWidth and dom.clientHeight. They should not be 0.' +
'For example, you may need to call this in the callback ' +
'of window.onload.');
}
const map = new JMap(opts);
this.instances[opts.el] = map;
Dom.setAttribute(opts.dom, DOM_ATTRIBUTE_KEY, opts.el);
return map;
}
getVersion() {
return this._version;
}
getInstanceByDom(dom) {
return this.instances[Dom.getAttribute(dom, DOM_ATTRIBUTE_KEY)];
}
getInstanceById(el) {
return this.instances[el];
}
dispose(map) {
if (utils.isString(map)) {
map = this.instances[map];
} else if (!utils.isInstanceOf(map, map)) {
map = this.getInstanceByDom(map.getDom());
}
if (map) {
map.destroy();
}
}
destroy() {
Object.values(this.instances).forEach(map => {
this.dispose(map);
})
this.instances = []
}
}
export default JMaps;

62
src/iscs_new/option.js Normal file
View File

@ -0,0 +1,62 @@
class Option {
constructor(opts, notice) {
this.ratio = 100;
this.step = 0.1;
this.scaleMin = 0.3;
this.scaleMax = 10;
this.scaleRate = opts.scaleRate || 1; // 缩放
this.offsetX = opts.offsetX || 0; // x偏移
this.offsetY = opts.offsetY || 0; // y偏移
this.rotation = opts.rotation || 0; // 旋转弧度
this.throttle = opts.throttle || 100; // 刷新频率
this.preventDefaultMouseMove = true;
this.notice = notice;
}
update(payload) {
if (Number.isFinite(payload.dx)) {
this.offsetX -= payload.dx;
}
if (Number.isFinite(payload.dy)) {
this.offsetY -= payload.dy;
}
if (Number.isFinite(payload.offsetX)) {
this.offsetX = payload.offsetX;
}
if (Number.isFinite(payload.offsetY)) {
this.offsetY = payload.offsetY;
}
if (Number.isFinite(payload.scale)) {
this.scaleRate = this.getScaleRate(payload.scale);
}
if (Number.isFinite(payload.scaleRate)) {
this.scaleRate = payload.scaleRate;
}
if (this.notice instanceof Function) { this.notice(this); }
}
getScaleRate(scale) {
if (Number.isFinite(scale)) {
const sumScaleNum = parseInt((this.scaleRate - this.scaleMin + scale * this.step) * this.ratio);
const maxScaleNum = this.scaleMax * this.ratio;
return sumScaleNum > 0? ((sumScaleNum%maxScaleNum) / this.ratio + this.scaleMin).toFixed(1): this.scaleRate;
}
return this.scaleRate;
}
}
export default Option;

117
src/iscs_new/painter.js Normal file
View File

@ -0,0 +1,117 @@
import * as graphic from './core/graphic';
import shapeLayer from './constant/shapeLayer';
import Group from 'zrender/src/container/Group';
import TransformHandle from './transformHandle';
import AnimateHandle from './animateHandle';
class Painter extends Group {
constructor(map) {
super({name: `__Container__` });
// 添加父级图层
const zr = map.getZr();
zr.add(this);
// 初始图层
this.initLevels(map);
// 视图控制器
this.$transformHandle = new TransformHandle(this);
// 动画处理器
this.$animateHandle = new AnimateHandle(this);
// 重写onFrame加入钩子
this.onframe(zr);
}
onframe(zr) {
const onframe = zr.animation.onframe;
const animateHandle = this.$animateHandle;
zr.animation.onframe = interval => {
onframe.call(zr.animation, interval);
animateHandle.onframe(interval);
}
}
initLevels(map) {
// 初始化图层对象
this.mapShapeLayer = {};
// 创建select图层
this.mapShapeLayer[shapeLayer.Selecting] = new Group({ name: shapeLayer.Selecting });
// 创建hover图层
this.mapShapeLayer[shapeLayer.HightLight] = new Group({ name: shapeLayer.HightLight });
// 创建tips图层
this.mapShapeLayer[shapeLayer.Tips] = new Group({name: shapeLayer.Tips });
// 创建元素图层
Object.keys(graphic).forEach(key => {
this.mapShapeLayer[key] = new Group({
name: `__${key}__`
});
})
// 添加图层
Object.values(this.mapShapeLayer).forEach(el => {
super.add(el);
})
}
add(shape) {
shape && this.removeFromLayer(shapeLayer.HightLight, shape.instanceHightLight);
shape && this.addToLayer(shape.type, shape);
}
remove(shape) {
shape && this.removeFromLayer(shapeLayer.HightLight, shape.instanceHightLight);
shape && this.removeFromLayer(shape.type, shape);
}
update(stateList=[]) {
stateList
.sort((a,b) => a.weight - b.weight)
.forEach(state => {
this.$animateHandle.animate(state);
})
}
updateTransform(opt) {
this.$transformHandle.updateTransform(opt);
}
updateZrSize(opt) {
this.$transformHandle.updateZrSize(opt);
}
addToLayer(name, shape) {
name = Object.keys(graphic).includes(name)? name: `Group`;
this.mapShapeLayer[name].add(shape);
return shape;
}
removeFromLayer(name, shape) {
name = Object.keys(graphic).includes(name)? name: `Group`;
this.mapShapeLayer[name].remove(shape);
return shape;
}
getLevelByName(name) {
return this.mapShapeLayer[name];
}
clear() {
Object.values(this.mapShapeLayer).forEach(el => {
el.removeAll();
})
}
destroy() {
this.clear();
this.mapShapeLayer = {};
this.mapAnimate = {};
}
}
export default Painter;

View File

@ -0,0 +1,111 @@
<template>
<div class="builder" v-show="visible">
<div class="container">
<el-button-group>
<el-button v-for="(type,i) in graphicType" :key="i" @click="doBuilder(type)">
{{type}}
</el-button>
</el-button-group>
</div>
</div>
</template>
<script>
import * as graphic from '../../core/graphic';
import * as utils from '@/iscs_new/utils/utils';
import shapeType from '@/iscs_new/constant/shapeType.js';
import orders from '@/iscs_new/utils/orders';
import elementConst from '@/iscs_new/core/form/elementConst';
import formBuilder from '@/iscs_new/core/form/formBuilder';
import { EventBus } from '@/scripts/event-bus';
const exec = (fm, name, ...args) => fm && fm[name] && fm[name](...args);
export default {
data () {
return {
visible: true,
enum: 'Compose',
graphicType: [],
strategyMap: {
'Compose': {
init: () => {
this.graphicType = Object.fromEntries(Object.keys(graphic).filter(type => !['Group'].includes(type)).map(type => [type, type]))
},
doBuilder: (type) => {
const form = formBuilder.buildForm(elementConst[type]);
const model = utils.deepClone(form.model);
model.code = utils.getUID(type);
model.type = type;
model.name = '<名称>';
model.base.position = [300, 100];
model.stateList = [];
this.$iscs && this.$iscs.render([{model, action: {shapeType: shapeType.Element, order: orders.Add}}]);
EventBus.$emit('getComposeElemList');
}
},
'Map': {
init: () =>{
},
doBuilder: (type) => {
}
}
}
};
},
mounted() {
this.init();
},
methods: {
init() {
exec(this.strategyMap[this.enum], 'init')
},
doBuilder(type) {
exec(this.strategyMap[this.enum], 'doBuilder', type)
}
}
};
</script>
<style lang="scss" scoped>
.builder {
background: #f1f1f1;
height: 100%;
width: 130px;
position: absolute;
top: 0;
overflow-x: hidden;
overflow-y: auto;
&::-webkit-scrollbar {
/*滚动条整体样式*/
width : 5px;
/*高宽分别对应横竖滚动条的尺寸*/
height: 1px;
}
&::-webkit-scrollbar-thumb {
/*滚动条里面小方块*/
border-radius : 10px;
background-color: lightskyblue;
background-image: -webkit-linear-gradient(
45deg,
rgba(255, 255, 255, 0.2) 25%,
transparent 25%,
transparent 50%,
rgba(255, 255, 255, 0.2) 50%,
rgba(255, 255, 255, 0.2) 75%,
transparent 75%,
transparent
);
}
&::-webkit-scrollbar-track {
/*滚动条里面轨道*/
box-shadow : inset 0 0 5px rgba(0, 0, 0, 0.2);
background : #ededed;
border-radius: 7px;
}
}
</style>

View File

@ -0,0 +1,67 @@
import Vue from 'vue';
import events from '@/iscs_new/utils/events';
import entry from './entry.vue';
const VuePage = Vue.extend(entry);
const handle = {
onClick(e) {
if (!e) {
this.page.type = '';
}
},
onContextMenu(e) {
if (!e) {
this.page.type = '';
}
}
}
class ShapeBuilder {
constructor(map) {
this.zr = map.getZr();
this.map = map;
this.page = this.initUI();
}
initUI() {
const el = this.zr.dom;
const page = new VuePage({
el: document.createElement('div'),
data () {}
});
el.page = page;
el.dom = page.$el;
el.grSelectStyle = {};
el.appendChild(el.dom);
return page;
}
addEventListener() {
if (this.map) {
this.map.on(events.Click, handle.onClick, this);
this.map.on(events.ContextMenu, handle.onContextMenu, this);
}
}
removeEventListener() {
if (this.map) {
this.map.off(events.Click, handle.onClick, this);
this.map.off(events.ContextMenu, handle.onContextMenu, this);
}
}
}
export default {
el: null,
install(map) {
this.el = new ShapeBuilder(map);
this.el.addEventListener();
},
uninstall(map) {
if (this.el) {
this.el.removeEventListener();
this.el = null;
}
}
}

View File

@ -0,0 +1,208 @@
<template>
<div class="container">
<div v-if="visible" class="menus" :style="{ left: position.x+'px', top: position.y+'px' }">
<menu-item v-for="(el, i) in menus" :key="i" :option="el" @close="doClose" />
</div>
</div>
</template>
<script>
import MenuItem from './menu-item';
import shapeType from '@/iscs_new/constant/shapeType.js';
import orders from '@/iscs_new/utils/orders';
import * as utils from '@/iscs_new/utils/utils';
export default {
components: {
MenuItem
},
data () {
return {
visible: true,
selected: null,
breforePosition: {
x: 0,
y: 0
},
position: {
x: 0,
y: 0
},
clipboardList: [],
menus: [],
menusMap: {
Normal: [
{
label: '组合',
handler: this.doBinding,
disabledCb: e => {
const storage = this.$iscs.getController().getStorage();
return storage.values().length <= 1;
}
},
{
label: '解组',
handler: this.doUnBinding,
disabledCb: e => {
return this.selected.shapeType != shapeType.Compose;
}
},
{
label: '复制',
handler: this.doCopy,
disabledCb: e => {
const storage = this.$iscs.getController().getStorage();
return !storage.values().length;
}
},
{
label: '删除',
handler: this.doDelete,
disabledCb: e => {
const storage = this.$iscs.getController().getStorage();
return !storage.values().length;
}
}
],
None: [
{
label: '粘贴',
handler: this.doPaste,
disabledCb: e => {
return !this.clipboardList.length;
}
}
]
}
};
},
methods: {
doShow(point, code) {
const list = code ? this.menusMap.Normal: this.menusMap.None;
this.selected= this.$iscs.getShapeByCode(code);
this.menus = list.map(el => { el.disabled = el.disabledCb({}); return el} );
if (this.menus &&
this.menus.length) {
this.position = {...point},
this.visible = true;
}
},
doClose() {
this.selected = null;
this.visible = false;
},
doBinding(el) {
const controller = this.$iscs.getController();
const storage = controller.getStorage()
const values = storage.values();
if (values.length > 1) {
const elem = {
model: {
code: utils.getUID('compose'),
type: 'Device',
elementCodes: values.map(el => el.model.code),
base: {
scale: [1, 1],
position: [0, 0],
rotation: 0,
hide: false,
},
composeCode: '',
},
action: { order: orders.Binding, shapeType: shapeType.Compose }
}
this.$iscs.render([elem]);
} else {
this.$message.info('请选择两个及其以上数目元素');
}
},
doUnBinding(el) {
if (this.selected) {
this.$iscs.render([
{
model: this.selected.model,
action: { order: orders.Unbinding, shapeType: shapeType.Compose }
}
]);
}
},
doCopy(el) {
const option = this.$iscs.getOption();
const storage = this.$iscs.getController().getStorage();
this.clipboardList = [...storage.values()];
this.breforePosition = {
x: this.position.x + option.offsetX,
y: this.position.y + option.offsetY
}
},
doPaste(el) {
const option = this.$iscs.getOption();
const renderList = [];
const clipboardList = [...this.clipboardList];
const diffPosition = [
(this.position.x + option.offsetX - this.breforePosition.x)/option.scaleRate,
(this.position.y + option.offsetY - this.breforePosition.y)/option.scaleRate
]
clipboardList.forEach(el => {
this.clone(el, utils.getUID(el.type), '', el => {
const shapeType = el.shapeType;
const model = el.model;
const position = model.base.position||[0, 0];
model.base.position = model.composeCode? position: position.map((el,i) => el+diffPosition[i]);
renderList.push({ model, action: { order: orders.Add, shapeType } });
});
})
this.$iscs.render(renderList);
},
doDelete(el) {
const storage = this.$iscs.getController().getStorage();
const renderList = storage.values().map(el => {
return {
model: el.model,
action: { order: orders.Delete, shapeType: el.shapeType }
}
});
this.$iscs.render(renderList);
},
clone(el, code='', composeCode='', cb) {
const model = utils.deepClone(el.model);
model.code = code;
model.composeCode = composeCode;
if (el.shapeType == shapeType.Compose) {
model.elementCodes = model.elementCodes.map(code => {
const shape = this.$iscs.getShapeByCode(code);
const newCode = utils.getUID(shape.type)
this.clone(shape, newCode, code, cb);
return newCode;
})
}
cb({...el, model});
}
}
};
</script>
<style lang="scss" scoped>
.container {
position: absolute;
height: 100%;
width: 100%;
z-index: 2000;
top: 0;
left: 0;
pointer-events: none;
overflow: hidden;
.menus {
position: absolute;
pointer-events: all;
background: #f1f1f1;
box-shadow: 0 2px 12px rgba(0,0,0,0.3);
}
&::-webkit-scrollbar {
display:none;
}
}
</style>

View File

@ -0,0 +1,67 @@
import Vue from 'vue';
import events from '@/iscs_new/utils/events';
import entry from './entry.vue';
const elPage = Vue.extend(entry);
const handle = {
onClick(e) {
if (this.page) {
this.page.doClose()
}
},
onContextMenu(e) {
if (this.page) {
this.page.doShow({x: e.clientX, y: e.clientY}, e.code);
}
}
}
class ShapeBuilder {
constructor(map) {
this.zr = map.getZr();
this.map = map;
this.page = this.initUI();
}
initUI() {
const el = this.zr.dom;
const page = new elPage({
el: document.createElement('div'),
data () {}
});
el.page = page;
el.dom = page.$el;
el.grSelectStyle = {};
el.appendChild(el.dom);
return page;
}
addEventListener() {
if (this.map) {
this.map.on(events.Click, handle.onClick, this);
this.map.on(events.ContextMenu, handle.onContextMenu, this);
}
}
removeEventListener() {
if (this.map) {
this.map.off(events.Click, handle.onClick, this);
this.map.off(events.ContextMenu, handle.onContextMenu, this);
}
}
}
export default {
el: null,
install(map) {
this.el = new ShapeBuilder(map);
this.el.addEventListener();
},
uninstall(map) {
if (this.el) {
this.el.removeEventListener();
this.el = null;
}
}
}

View File

@ -0,0 +1,202 @@
<template>
<li v-if="option.children && option.children.length" class="menu-item" @mouseenter="enter($vnode.key)" @mouseleave="leave">
<div ref="flexBox" class="flex-box">
<el-button type="text" class="item" :disabled="checkDisabled(option)">
<el-link v-if="option.tipsType" :type="option.tipsType" :underline="false">{{ option.label }}</el-link>
<span v-else :style="{color: textColor(option) }">{{ option.label }}</span>
</el-button>
<i class="el-icon-arrow-right" />
</div>
<ul v-if="isPopup" ref="popup" class="menu" :style="{display: isShow? 'block': 'table', marginLeft: marginLeft+'px'}">
<div class="menu-pop pop-menu">
<div v-show="isShow" class="arrow el-icon-arrow-down" />
<pop-menu-item v-for="(el, i) in option.children" :key="i" :option="el" :allowedColor="allowedColor" :disabledColor="disabledColor" @close="close" />
<div v-show="isShow" class="arrow el-icon-arrow-up" />
</div>
</ul>
</li>
<li v-else-if="checkVisible(option)" class="menu-item" >
<div v-if="option.type === 'separator'" class="separator">&ensp;</div>
<el-button v-else-if="option.type ==='file'" type="text" class="uploadDemo item" :disabled="checkDisabled(option)">
<input :ref="option.label" type="file" class="file_box" accept=".csv, application/vnd.openxmlformats-officedocument.spreadsheetml.sheet, application/vnd.ms-excel" @change="doHandle(option, true)">
<el-link v-if="option.tipsType" :type="option.tipsType" :underline="false">{{ option.label }} </el-link>
<span v-else :style="{color: textColor(option) }">{{ option.label }}</span>
</el-button>
<el-button v-else type="text" class="item" :disabled="checkDisabled(option)" @click="doHandle(option, false)">
<el-link v-if="option.tipsType" :type="option.tipsType" :underline="false">{{ option.label }}</el-link>
<span v-else :style="{color: textColor(option) }">{{ option.label }}</span>
</el-button>
</li>
</template>
<script>
export default {
name: 'PopMenuItem',
props: {
option: {
type: Object,
default() {
return [];
}
},
disabled: {
type: Boolean,
default: false
},
disabledColor: {
type: String,
default: '#ccc',
},
allowedColor: {
type: String,
default: '#000'
}
},
data() {
return {
active: -1
};
},
computed: {
isPopup() {
return this.active == this.$vnode.key;
},
isShow() {
return this.option.children.length > 12;
},
marginLeft() {
return this.$refs.flexBox.offsetWidth;
}
},
methods: {
checkDisabled(option) {
return option.disabled || this.disabled;
},
checkVisible(option) {
if (typeof (option.show) === 'undefined') {
return true;
} else {
return option.show;
}
},
textColor(option) {
return this.checkDisabled(option) ? this.disabledColor : this.allowedColor;
},
doHandle(option, isFile) {
if (option.handler && !this.checkDisabled(option)) {
if (isFile) {
const obj = this.$refs[option.label][0];
if (obj.files) {
const file = obj.files[0];
option.handler(file);
obj.value = '';
}
} else {
option.handler(option);
}
this.close();
}
},
close() {
this.$emit('close');
},
enter(active) {
this.active = active;
},
leave(e) {
this.active = -1;
}
}
};
</script>
<style rel="stylesheet/scss" lang="scss" scoped>
$bg: #FFFFFF;
$item-hover: #CFE8FC;
$item-text: #606266;
$item-separator: #666;
$item-border: #909399;
$item-disabled: #cccccc;
$item-height: 30px;
/deep/ {
.el-button--text {
padding: 6px 15px;
text-align: left;
}
}
.menu {
padding: 0;
position: absolute;
margin-top: -$item-height - 1;
max-height: 360px;
overflow: auto;
&-pop {
display:table;
border: 1px solid $item-border;
.arrow {
display: flex;
justify-content: center;
background: $bg;
}
}
&::-webkit-scrollbar {
display:none;
}
}
.flex-box{
display: flex;
justify-content: space-between;
align-items: center;
flex-direction: row;
padding-right: 3px;
}
.menu-item {
list-style: none;
min-width: 110px;
border-bottom: 1px solid #ebeef5;
box-sizing: content-box;
}
.menu-item:hover {
background: $item-hover;
}
.separator {
background: $item-separator;
width: 100%;
height: 1px;
position: relative;
}
.item {
color: $item-text;
height: $item-height;
width: 100%;
}
.uploadDemo {
position: relative;
overflow: hidden;
width: 100%;
cursor: pointer;
border: 1px solid transparent;
color: $item-text;
input {
opacity: 0;
cursor: pointer;
position: absolute;
width: 100%;
height: 100%;
}
}
</style>

View File

@ -0,0 +1,36 @@
<template>
<el-drawer
class="property"
title="我是标题"
:modal="false"
:visible.sync="visible"
:with-header="false">
<div class="container">
<span>我来啦!</span>
</div>
</el-drawer>
</template>
<script>
export default {
data () {
return {
visible: true
};
},
methods: {
}
};
</script>
<style lang="scss" scoped>
.property {
pointer-events: none;
position: absolute;
top: 0;
.container {
padding: 30px;
}
}
</style>

View File

@ -0,0 +1,77 @@
import Vue from 'vue';
import events from '@/iscs_new/utils/events';
import entry from './entry.vue';
const elPage = Vue.extend(entry);
const toggling = (page, e) => {
if (!!e) {
Vue.nextTick(() => {
page.visible = true;
});
} else {
page.visible = false;
}
};
const handle = {
onClick(e) {
toggling(this.page, e);
},
onContextMenu(e) {
},
onModify(e) {
},
onDelete(e) {
}
}
class ShapeBuilder {
constructor(map) {
this.zr = map.getZr();
this.map = map;
this.page = this.initUI();
}
initUI() {
const el = this.zr.dom;
const page = new elPage({
el: document.createElement('div'),
data () {}
});
el.page = page;
el.dom = page.$el;
el.grSelectStyle = {};
el.appendChild(el.dom);
return page;
}
addEventListener() {
if (this.map) {
this.map.on(events.Click, handle.onClick, this);
this.map.on(events.ContextMenu, handle.onContextMenu, this);
}
}
removeEventListener() {
if (this.map) {
this.map.off(events.Click, handle.onClick, this);
this.map.off(events.ContextMenu, handle.onContextMenu, this);
}
}
}
export default {
el: null,
install(map) {
this.el = new ShapeBuilder(map);
this.el.addEventListener();
},
uninstall(map) {
if (this.el) {
this.el.removeEventListener();
this.el = null;
}
}
}

View File

@ -0,0 +1,79 @@
<template>
<div class="builder" v-show="visible">
<div class="container">
////
</div>
</div>
</template>
<script>
import * as graphic from '../../core/graphic';
import * as utils from '@/iscs_new/utils/utils';
import shapeType from '@/iscs_new/constant/shapeType.js';
import orders from '@/iscs_new/utils/orders';
import elementConst from '@/iscs_new/core/form/elementConst';
import formBuilder from '@/iscs_new/core/form/formBuilder';
import { EventBus } from '@/scripts/event-bus';
export default {
data () {
return {
visible: true,
graphicType: Object.fromEntries(Object.keys(graphic).filter(type => !['Group'].includes(type)).map(type => [type, type]))
};
},
methods: {
doBuilder(type) {
const form = formBuilder.buildForm(elementConst[type]);
const model = utils.deepClone(form.model);
model.code = utils.getUID(type);
model.type = type;
model.name = '<名称>';
model.base.position = [300, 100];
model.stateList = [];
this.$iscs && this.$iscs.render([{model, action: {shapeType: shapeType.Element, order: orders.Add}}]);
EventBus.$emit('getComposeElemList');
}
}
};
</script>
<style lang="scss" scoped>
.builder {
background: #f1f1f1;
height: 100%;
width: 130px;
position: absolute;
top: 0;
overflow-x: hidden;
overflow-y: auto;
&::-webkit-scrollbar {
/*滚动条整体样式*/
width : 5px;
/*高宽分别对应横竖滚动条的尺寸*/
height: 1px;
}
&::-webkit-scrollbar-thumb {
/*滚动条里面小方块*/
border-radius : 10px;
background-color: lightskyblue;
background-image: -webkit-linear-gradient(
45deg,
rgba(255, 255, 255, 0.2) 25%,
transparent 25%,
transparent 50%,
rgba(255, 255, 255, 0.2) 50%,
rgba(255, 255, 255, 0.2) 75%,
transparent 75%,
transparent
);
}
&::-webkit-scrollbar-track {
/*滚动条里面轨道*/
box-shadow : inset 0 0 5px rgba(0, 0, 0, 0.2);
background : #ededed;
border-radius: 7px;
}
}
</style>

View File

@ -0,0 +1,67 @@
import Vue from 'vue';
import events from '@/iscs_new/utils/events';
import entry from './entry.vue';
const VuePage = Vue.extend(entry);
const handle = {
onClick(e) {
if (!e) {
this.page.type = '';
}
},
onContextMenu(e) {
if (!e) {
this.page.type = '';
}
}
}
class ShapeBuilder {
constructor(map) {
this.zr = map.getZr();
this.map = map;
this.page = this.initUI();
}
initUI() {
const el = this.zr.dom;
const page = new VuePage({
el: document.createElement('div'),
data () {}
});
el.page = page;
el.dom = page.$el;
el.grSelectStyle = {};
el.appendChild(el.dom);
return page;
}
addEventListener() {
if (this.map) {
this.map.on(events.Click, handle.onClick, this);
this.map.on(events.ContextMenu, handle.onContextMenu, this);
}
}
removeEventListener() {
if (this.map) {
this.map.off(events.Click, handle.onClick, this);
this.map.off(events.ContextMenu, handle.onContextMenu, this);
}
}
}
export default {
el: null,
install(map) {
this.el = new ShapeBuilder(map);
this.el.addEventListener();
},
uninstall(map) {
if (this.el) {
this.el.removeEventListener();
this.el = null;
}
}
}

View File

@ -0,0 +1,58 @@
import shapeLayer from './constant/shapeLayer';
export default class SelectHandle {
constructor(map, controller) {
this.$map = map;
this.$zr = map.getZr();
this.$controller = controller;
this.$painter = map.getPainter();
this.e = {};
}
onSelected(e) {
if (e.target) {
const storage = this.$controller.getStorage();
this.e = {...e};
if ([`Control`].includes(this.$controller.getKeyStr())) {
if (this.$controller.isSelected(e.target.code)) {
this.$controller.setTarget(null);
this.delSelected(e.target);
} else {
this.addSelected(e.target);
}
} else if (!storage.has(e.target.code)){
this.clear();
this.addSelected(e.target);
} else {
this.addSelected(e.target);
}
}
}
addSelected(target) {
const storage = this.$controller.getStorage();
storage.set(target.code, target);
target.shapeFactory.showHightLight(target);
}
delSelected(target) {
const storage = this.$controller.getStorage();
target.shapeFactory.hideHightLight(target);;
storage.delete(target.code);
}
clear() {
const storage = this.$controller.getStorage();
storage.values().forEach(target => {
this.delSelected(target);
});
}
setDraggable(draggable) {
const target = this.e.target;
if (target &&
target.highLightInstance) {
target.highLightInstance.setDraggable(draggable);
}
}
}

View File

@ -0,0 +1,293 @@
import * as graphic from './core/graphic.js';
import shapeRender from './constant/shapeRender';
import shapeLayer from './constant/shapeLayer';
import events from './utils/events.js';
import defaultStyle from './config/defaultStyle';
const vernierStyle = {
area: {
areaStyle: {
fill: `rgba(200,200,200,0.3)`
}
},
axisLine: {
lineStyle: {
strokeNoScale: true,
lineWidth: 30,
stroke: 'rgba(255,255,255,0.6)'
}
},
axisTick: {
distance: 18,
length: 5,
lineStyle: {
strokeNoScale: true,
lineWidth: 2,
stroke: '#000000'
}
},
axisLabel: {
distance: 5,
textStyle: {
strokeNoScale: true,
textStroke: '#000',
textAlign: 'center',
textVerticalAlign: 'center'
}
}
}
class Vernier extends graphic.Group {
constructor(args) {
super(args);
this._scaleRate = 1;
this._area = null;
this._axisLineX = null;
this._axisLineY = null;
}
_createArea(rect) {
this._area = new graphic.Rect({
subType: '@ignore',
...shapeRender,
z: 9999,
silent: true,
shape: {
...rect
},
style: {
...vernierStyle.area.areaStyle
}
})
this.add(this._area);
}
_createLineAxisX(rect) {
const minX = rect.x;
const minY = rect.y;
const maxX = rect.x + rect.width;
const maxY = rect.y + rect.height;
const directionX = (Math.abs(rect.width)/rect.width);
const directionY = (Math.abs(rect.height)/rect.height);
const axisLineWidthHalf = vernierStyle.axisLine.lineStyle.lineWidth/this._scaleRate/2;
this._axisLineX = new graphic.Line({
...shapeRender,
z: 9999,
silent: true,
position: [0, -axisLineWidthHalf*directionY],
shape: {
x1: minX,
y1: minY,
x2: maxX,
y2: minY
},
style: {
...vernierStyle.axisLine.lineStyle
}
})
const step = vernierStyle.axisTick.lineStyle.lineWidth*2/this._scaleRate;
const count = parseInt(Math.abs(rect.width/step));
for(var i = 0; i in new Array(count+1).fill(0); i++) {
const offset = step * i * directionX;
const tick = i % vernierStyle.axisTick.length == 0
const len = tick? vernierStyle.axisTick.distance*0.7: vernierStyle.axisTick.distance*0.4;
if (tick) {
this.add(new graphic.Line({
...shapeRender,
z: 10000,
silent: true,
shape: {
x1: minX + offset,
y1: minY,
x2: minX + offset,
y2: minY - len * directionY / this._scaleRate
},
style: {
...vernierStyle.axisTick.lineStyle,
...vernierStyle.axisLabel.textStyle,
text: i%2? '': i/vernierStyle.axisTick.length/2,
textPosition: directionY > 0? 'top': 'bottom'
}
}))
} else {
this.add(new graphic.Line({
...shapeRender,
z: 10000,
silent: true,
shape: {
x1: minX + offset,
y1: minY,
x2: minX + offset,
y2: minY - len * directionY / this._scaleRate
},
style: {
...vernierStyle.axisTick.lineStyle
}
}))
}
}
this.add(this._axisLineX);
}
_createLineAxisY(rect) {
const minX = rect.x;
const minY = rect.y;
const maxX = rect.x + rect.width;
const maxY = rect.y + rect.height;
const directionX = (Math.abs(rect.width)/rect.width);
const directionY = (Math.abs(rect.height)/rect.height);
const axisLineWidthHalf = vernierStyle.axisLine.lineStyle.lineWidth/this._scaleRate/2;
this._axisLineY = new graphic.Line({
...shapeRender,
z: 9999,
silent: true,
position: [-axisLineWidthHalf*directionX, 0],
shape: {
x1: minX,
y1: minY,
x2: minX,
y2: maxY
},
style: {
...vernierStyle.axisLine.lineStyle
}
})
this.add(this._axisLineY);
const step = vernierStyle.axisTick.lineStyle.lineWidth*2 /this._scaleRate;
const count = parseInt(Math.abs(rect.height/step));
for(var i = 0; i in new Array(count+1).fill(0); i++) {
const offset = step * i * directionY;
const tick = i % vernierStyle.axisTick.length == 0;
const len = tick? vernierStyle.axisTick.distance*0.7: vernierStyle.axisTick.distance*0.4;
if (tick) {
this.add(new graphic.Line({
...shapeRender,
z: 10000,
silent: true,
shape: {
x1: minX,
y1: minY + offset,
x2: minX - len * directionX / this._scaleRate,
y2: minY + offset
},
style: {
...vernierStyle.axisTick.lineStyle,
...vernierStyle.axisLabel.textStyle,
text: i%2? '': i/vernierStyle.axisTick.length/2,
textPosition: directionX > 0? 'left': 'right'
}
}))
} else {
this.add(new graphic.Line({
...shapeRender,
z: 10000,
silent: true,
shape: {
x1: minX,
y1: minY + offset,
x2: minX - len * directionX / this._scaleRate,
y2: minY + offset
},
style: {
...vernierStyle.axisTick.lineStyle
}
}))
}
}
}
render(rect) {
this.removeAll();
this._scaleRate = this.parent? this.parent.transform[0]: 1;
this._createArea(rect);
this._createLineAxisX(rect);
this._createLineAxisY(rect);
}
setShape(rect) {
this.render(rect);
}
}
export default class SelectingHandle {
constructor(map, controller) {
this.$map = map;
this.$zr = map.getZr();
this.$controller = controller;
this.$painter = map.getPainter();
this.begPoint = null;
this.endPoint = null;
this.selecting = new Vernier({subType: '@selecting'});
}
isSelecting() {
return this.begPoint;
}
onSelectStart(e) {
this.selecting.setShape({ x: e.x, y: e.y, width: 0, height: 0 });
this.$painter.addToLayer(shapeLayer.Selecting, this.selecting);
this.begPoint = { x: e.x, y: e.y };
this.endPoint = null;
}
onSelecting(e) {
this.endPoint = { x: e.x, y: e.y };
this.selecting.setShape(this.normalizedArea(this.begPoint, this.endPoint));
this.$painter.addToLayer(shapeLayer.Selecting, this.selecting);
}
onSelectEnd(e) {
this.endPoint = { x: e.x, y: e.y };
this.selecting.setShape(this.normalizedArea(this.begPoint, this.endPoint));
this.$painter.addToLayer(shapeLayer.Selecting, this.selecting);
const selectingRect = this.selecting.getBoundingRect();
Object.values(this.$map.getMapShape()).forEach(el => {
if (el.model && !el.model.composeCode && this.checkSelectingRectContainShape(selectingRect, el)) {
this.setSelected(el);
}
});
this.clear();
}
setSelected(target) {
const storage = this.$controller.getStorage();
storage.set(target.code, target);
target.shapeFactory.showHightLight(target);
}
clear() {
this.$painter.removeFromLayer(shapeLayer.Selecting, this.selecting);
this.selecting.setShape({ x: 0, y: 0, width: 0, height: 0 });
this.begPoint = this.endPoint = null;
}
checkSelectingRectContainShape(rect, shape) {
const shapeRect = shape.getBoundingRect();
if (shapeRect) {
return rect.contain(shapeRect.x, shapeRect.y) && rect.contain(shapeRect.x+shapeRect.width, shapeRect.y+shapeRect.height);
}
return false;
}
normalizedArea(begin, end) {
const options = this.$map.getOption();
const x = (begin.x + options.offsetX) / options.scaleRate;
const y = (begin.y + options.offsetY) / options.scaleRate;
const width = (end.x - begin.x) / options.scaleRate;
const height = (end.y - begin.y) / options.scaleRate;
return {x, y, width, height};
}
}

View File

@ -0,0 +1,63 @@
import * as utils from './utils/utils';
export default class StateHandle {
constructor(map) {
this.$map = map;
}
parse(shapeFactory, state) {
const mapTemplate = shapeFactory.getMapTemplate();
const template = mapTemplate[state.type];
const templateState = template.mapState[state.status] || {};
// const frameList = templateState.frameList||[];
const frameList = Object.values(templateState.covertStatusList) || [];
// 增加第一帧初始数据
// if (templateState &&
// templateState.needDefault) {
// templateState.frameList.unshift(
// Object.entries(template.mapShape).map(el => {
// return { name: el[0], ...el[1][utils.defStatus]};
// })
// );
// }
return {
...state,
...templateState,
mapShape: template.mapShape,
shape: shapeFactory.getShapeByCode(state.code),
frameList: frameList.map(frame => {
// return Object.fromEntries(frame.map(el => {
return Object.fromEntries(frame.frameList.map((el, index) => {
const mapState = template.mapShape[el.name] || {};
const state = mapState[el.status] || {};
return [el.name || index, {
loop:frame.loop,
...el,
...state
}];
}));
})
};
}
update(shapeFactory, states = []) {
return states.reduce((list, state) => {
return [
...list,
this.parse(shapeFactory, state) // 测试只有自身
// this.updateState(this.parse(shapeFactory, state)), // 处理自身
// this.updateState(this.parse(shapeFactory, state)) // 处理依赖
];
}, []);
}
updateState(state = {}) {
return {};
}
updateDepState(state = {}) {
return {};
}
}

View File

@ -0,0 +1,64 @@
import * as utils from './utils/utils';
export default class TransformHandle {
constructor(painter) {
this.$painter = painter;
this.rect = { x: 0, y: 0, width: 0, height: 0 };
this.transform = utils.createTransform({scale:[1,1], position:[0,0], rotation:0 });
}
getTransform() {
return this.transform;
}
// 检查显隐
checkVisible(shape) {
return (!shape.model || !shape.model.base.hide || !shape.model.composeCode) && utils.createBoundingRectCheckVisible(shape, this.transform).intersect(this.rect);
}
// 重新计算显隐
visibleView(shape) {
this.checkVisible(shape)
? shape.show()
: shape.hide();
shape.dirty();
}
// 缩放/平移图层
transformAll() {
this.traverse(this.visibleView, this);
}
// 更新偏移量
updateTransform(opt) {
this.transform = utils.createTransform({scale: [opt.scaleRate, opt.scaleRate], position: [-opt.offsetX/opt.scaleRate, -opt.offsetY/opt.scaleRate], rotation: 0});
this.transformAll();
}
// 更新画布尺寸
updateZrSize(opt) {
this.rect = { x: 0, y: 0, width: opt.width, height: opt.height };
this.transformAll();
}
// 遍历view执行回调
traverse(cb, context) {
debugger
this.traverseLayer(layer => {
layer.eachChild(shape => {
cb.call(context, shape);
})
}, context)
}
// 遍历图层执行回调
traverseLayer(cb, context) {
this.$painter.eachChild(layer => {
layer.transform = this.transform;
cb.call(context, layer);
});
}
}

17
src/iscs_new/utils/dom.js Normal file
View File

@ -0,0 +1,17 @@
export function isDom(dom) {
return typeof dom === 'object' &&
typeof dom.nodeType === 'number' &&
typeof dom.ownerDocument === 'object';
}
export function setAttribute(dom, key, value) {
dom.setAttribute
? dom.setAttribute(key, value)
: (dom[key] = value);
}
export function getAttribute(dom, key) {
return dom.getAttribute
? dom.getAttribute(key)
: dom[key];
}

View File

@ -0,0 +1,15 @@
export default {
Reflect: 'reflect',
Click: 'click',
ContextMenu: 'contextmenu',
DataZoom: 'dataZoom',
Keydown: 'keydown',
Keyup: 'keyup',
Keypress: 'keypress',
DataLoaded: 'dataLoaded',
ViewLoaded: 'viewLoaded',
StateLoaded: 'stateLoaded',
ViewUpdate: 'viewUpdate',
StateUpdate: 'stateUpdate',
OptionUpdate: 'optionUpdate'
}

View File

@ -0,0 +1,9 @@
export default {
Add: '&Add',
Delete: '&DEL',
Update: '&UPT',
Binding: '&Binding',
Unbinding: '&Unbinding',
ChangeStatus:'&ChangeStatus',
ResetStatus:'&ResetStatus'
};

View File

@ -0,0 +1,63 @@
export default class Storage {
constructor() {
this.map = new Map();
this.lst = new Set();
this.clipboard = [];
}
has(code) {
return code ? this.map.has(code) : false;
}
set(code, target) {
if (!this.has(code)) {
this.lst.add(code);
this.map.set(code, target);
}
}
get(code) {
return this.map.get(code);
}
delete(code) {
if (this.has(code)) {
this.map.delete(code);
this.lst.delete(code);
}
}
values() {
return Array.from(this.lst).map(code => { return this.get(code); });
}
clear() {
this.lst.clear();
this.map.clear();
}
isSelectExist(code) {
return this.has(code);
}
forEach() {
return this.map.forEach;
}
setClipboard(lst) {
this.clipboard = Array.from(lst);
}
clearClipboard() {
this.clipboard = [];
}
getClipboard() {
return this.clipboard;
}
getClipboardSize() {
return this.clipboard.length;
}
}

111
src/iscs_new/utils/utils.js Normal file
View File

@ -0,0 +1,111 @@
import BoundingRect from 'zrender/src/core/BoundingRect';
import * as matrix from 'zrender/src/core/matrix';
// 默认状态标识
export const defStatus = 'default';
// 获取一个UID
export const getUID =(function(base=0) {
return function(type) { return [(type || ''), base++, Math.random().toFixed(5)].join('_');}
})();
// 克隆一个对象
export function deepClone(obj) {
return JSON.parse(JSON.stringify(obj))
}
// 克隆一个对象到另一个对象
export function deepAssign(tag, obj) {
return JSON.parse(JSON.stringify({...tag, ...obj}));
}
// 拷贝对象指定层次的引用
export function assignByDepth(source, target, depth) {
Object.keys(target).forEach(key => {
if (depth) {
Object.keys(target).forEach(key => {
source[key] = assignByDepth(source[key]||{}, target[key]||{}, depth-1);
});
} else {
source[key] = target[key];
}
});
return source;
}
// 创建一个transform
export function createTransform({scale=[1,1], position=[0,0], rotation=0}) {
let transform = matrix.create();
transform = matrix.translate(matrix.create(), transform, position);
transform = matrix.rotate(matrix.create(), transform, rotation);
transform = matrix.scale(matrix.create(), transform, scale);
return transform;
}
// 计算自身缩放和偏移后的包围框
export function createBoundingRect(shape) {
const rect = shape.getBoundingRect().clone();
const scaleX = shape.scale[0];
const scaleY = shape.scale[1];
const offsetX = shape.position[0];
const offsetY = shape.position[1];
rect.x = rect.x * scaleX + offsetX;
rect.y = rect.y * scaleY + offsetY;
rect.width = rect.width * scaleX;
rect.height = rect.height * scaleY;
return rect;
}
// 计算变化计算包围框
export function createBoundingRectCheckVisible(shape, transform) {
const rect = shape.getBoundingRect();
const scaleX = transform[0];
const scaleY = transform[3];
const offsetX = transform[4];
const offsetY = transform[5];
rect.x = rect.x * scaleX + offsetX;
rect.y = rect.y * scaleY + offsetY;
rect.width = rect.width * scaleX;
rect.height = rect.height * scaleY;
return rect;
}
// 计算图形的旋转中心
export function createOrigin(shape) {
const rect = shape.getBoundingRect();
return [rect.x + rect.width/2, rect.y + rect.height/2];
}
// 计算视图中心
export function calculateDCenter(viewRect, bound) {
var dx = (bound.width - viewRect.width) / 2 - viewRect.x;
var dy = (bound.height - viewRect.height) / 2 - viewRect.y;
return { dx, dy };
}
// 矩形碰撞检测
export function checkRectCollision(rect1, rect2) {
const center1 = { x: rect1.x + rect1.width / 2, y: rect1.y + rect1.height / 2 };
const center2 = { x: rect2.x + rect2.width / 2, y: rect2.y + rect2.height / 2 };
return Math.abs(center1.x - center2.x) < rect1.width / 2 + rect2.width / 2 && Math.abs(center1.y - center2.y) < rect1.height / 2 + rect2.height / 2
}
// 通过两点计算弧度
export function getRadian(point1, point2) {
return Math.atan2(point2.y-point1.y, point2.x-point1.x);
}
// 类型检测
const checkJsType = (type) => { return (obj) => { return Object.prototype.toString.call(obj) === `[object ${type.name}]`; }; };
export function isFunction() { return checkJsType(Function); }
export function isArray() { return checkJsType(Array); }
export function isString() { return checkJsType(String); }
export function isInstanceOf(obj, target) {
return obj instanceof target;
}

View File

@ -27,7 +27,7 @@ class Signal extends Group {
this._code = model.code; this._code = model.code;
this._type = model._type; this._type = model._type;
this.model = model; this.model = model;
this.mapDevice = mapDevice; this.mapDevice = mapDevice;
this.style = style; this.style = style;
this.count = parseInt(model.lampPositionType); this.count = parseInt(model.lampPositionType);
this.lamps = new Array(this.count); this.lamps = new Array(this.count);
@ -322,12 +322,12 @@ class Signal extends Group {
const drict = this.model.right ? 1 : -1; // 朝向 左:右 const drict = this.model.right ? 1 : -1; // 朝向 左:右
const posit = this.model.positionType == '01' ? -1 : 1; // 位置 上:下 const posit = this.model.positionType == '01' ? -1 : 1; // 位置 上:下
// 信号机名称 // 信号机名称
const actual = this.mapDevice[model.linkSignalCode]; const actual = this.mapDevice[model.linkSignalCode];
const sigNameX = model.position.x + model.positionPoint.x + drict * 0.433 * style.Signal.transmission.sideLength + model.namePosition.x; const sigNameX = model.position.x + model.positionPoint.x + drict * 0.433 * style.Signal.transmission.sideLength + model.namePosition.x;
const sigNameY = model.position.y + model.positionPoint.y + posit * (style.Signal.distance + style.Section.line.width + style.Signal.lamp.radiusR * 2 + model.namePosition.y + style.Signal.text.distance); const sigNameY = model.position.y + model.positionPoint.y + posit * (style.Signal.distance + style.Section.line.width + style.Signal.lamp.radiusR * 2 + model.namePosition.y + style.Signal.text.distance);
const textAlign = style.Signal.text.isAlignCenter ? 'middle' : this.model.right ? 'left' : 'right'; const textAlign = style.Signal.text.isAlignCenter ? 'middle' : this.model.right ? 'left' : 'right';
const textVerticalAlign = posit == 1 ? 'top' : 'bottom'; const textVerticalAlign = posit == 1 ? 'top' : 'bottom';
const fillColor = actual.virtual? style.Signal.transmission.fillColorVirtual: style.Signal.transmission.fillColor; const fillColor = actual.virtual ? style.Signal.transmission.fillColorVirtual : style.Signal.transmission.fillColor;
this.sigName = new ESigName({ this.sigName = new ESigName({
zlevel: this.zlevel, zlevel: this.zlevel,
z: this.z, z: this.z,
@ -511,12 +511,12 @@ class Signal extends Group {
} }
if (this.style.Signal.lamp.guidName !== 'singleRY') { if (this.style.Signal.lamp.guidName !== 'singleRY') {
if (this.lamps.length == 1) { if (this.lamps.length == 1) {
this.lamps[0] && this.lamps[0].setColor(this.style.Signal.lamp.redColor); this.lamps[0] && this.lamps[0].setColor(this.style.Signal.lamp.redColor);
} else { } else {
this.lamps[0] && this.lamps[0].setColor(this.style.Signal.lamp.redColor); this.lamps[0] && this.lamps[0].setColor(this.style.Signal.lamp.redColor);
this.lamps[1] && this.lamps[1].setColor(this.style.Signal.lamp.yellowColor); this.lamps[1] && this.lamps[1].setColor(this.style.Signal.lamp.yellowColor);
} }
} }
} }
@ -859,8 +859,8 @@ class Signal extends Group {
default: default:
var drict = this.model.right ? 1 : -1; // 朝向 右:左 var drict = this.model.right ? 1 : -1; // 朝向 右:左
var offsetY = this.model.positionType == '01' ? this.style.Signal.text.fontSize : 0; // 位置 上:下 var offsetY = this.model.positionType == '01' ? this.style.Signal.text.fontSize : 0; // 位置 上:下
var shape = this.model.type == 'TRANSMISSION'? this.transmission: this.sigPost; var shape = this.model.type == 'TRANSMISSION' ? this.transmission : this.sigPost;
rect = shape.getBoundingRect().clone(); rect = shape.getBoundingRect().clone();
rect.x = rect.x + drict * this.style.Signal.post.standardVerticalWidth; rect.x = rect.x + drict * this.style.Signal.post.standardVerticalWidth;
rect.y = rect.y - offsetY; rect.y = rect.y - offsetY;
break; break;

View File

@ -30,6 +30,7 @@ const Jlmap3dOtherVR = () => import('@/views/jlmap3d/maintainer/jl3dothervr');
// const Jl3dMaintainer = () => import('@/views/jlmap3d/maintainer/jl3dmaintainer'); // const Jl3dMaintainer = () => import('@/views/jlmap3d/maintainer/jl3dmaintainer');
const DisplayNew = () => import('@/views/newMap/displayNew/index'); const DisplayNew = () => import('@/views/newMap/displayNew/index');
const DisplayCity = () => import('@/views/newMap/displayCity/index');
const DesignDisplayNew = () => import('@/views/newMap/displayNew/scriptDisplay/scriptPreview/index'); const DesignDisplayNew = () => import('@/views/newMap/displayNew/scriptDisplay/scriptPreview/index');
const PracticeDisplay = () => import('@/views/newMap/displayNew/practiceDisplay'); const PracticeDisplay = () => import('@/views/newMap/displayNew/practiceDisplay');
const BigLPFStrategy = () => import('@/views/newMap/displayNew/bigLPFStrategy'); const BigLPFStrategy = () => import('@/views/newMap/displayNew/bigLPFStrategy');
@ -59,6 +60,11 @@ const IscsDesign = () => import('@/views/iscs/iscsDesign/index');
const IscsConfig = () => import('@/views/iscs/iscsSystem/config/index'); const IscsConfig = () => import('@/views/iscs/iscsSystem/config/index');
const IscsStationConfig = () => import('@/views/iscs/iscsSystem/stationConfig/index'); const IscsStationConfig = () => import('@/views/iscs/iscsSystem/stationConfig/index');
const IscsNewDesign = () => import('@/views/iscs_new/iscsDesign/index');
const IscsNewDraw = () => import('@/views/iscs_new/iscsDraw/index');
const IscsNewPreview = () => import('@/views/iscs_new/iscsPreview/index');
const NewMapDraft = () => import('@/views/newMap/newMapdraft/index'); const NewMapDraft = () => import('@/views/newMap/newMapdraft/index');
const NewDesignPlatformUser = () => import('@/views/newMap/newDesignUser/index'); const NewDesignPlatformUser = () => import('@/views/newMap/newDesignUser/index');
@ -162,6 +168,7 @@ const RunPlanViewWindow = () => import('@/views/newMap/displayNew/demon/runPlanV
const SecondaryHome = () => import('@/views/trainingPlatform/secondaryHome'); const SecondaryHome = () => import('@/views/trainingPlatform/secondaryHome');
const Demo = () => import('@/views/demo'); const Demo = () => import('@/views/demo');
const DemoTraining = () => import('@/views/newMap/displayNew/demoTraining'); const DemoTraining = () => import('@/views/newMap/displayNew/demoTraining');
const Test = () => import('@/views/test');
// import { GenerateRouteProjectList } from '@/scripts/ProjectConfig'; // import { GenerateRouteProjectList } from '@/scripts/ProjectConfig';
// import { getSessionStorage } from '@/utils/auth'; // import { getSessionStorage } from '@/utils/auth';
@ -207,6 +214,38 @@ export const userTrainingPlatform = '016'; // 实训系统
// export const refereePlatform = '017'; // 裁判系统 // export const refereePlatform = '017'; // 裁判系统
export const constantRoutes = [ export const constantRoutes = [
{
path: '/test',
component: Test,
hidden: true
},
{
path:'/iscs_new/design/compose/edit',
hidden: true,
component: IscsNewDraw,
meta: {
i18n: 'router.iscsDraw',
roles: [admin]
}
},
{
path:'/iscs_new/design/compose/preview',
hidden: true,
component: IscsNewPreview,
meta: {
i18n: 'router.iscsDraw',
roles: [admin]
}
},
{
path:'/iscs_new/design/map/edit',
hidden: true,
component: IscsNewDesign,
meta: {
i18n: 'router.iscsDraw',
roles: [admin]
}
},
{ {
path: '/demo', path: '/demo',
component: Demo, component: Demo,
@ -362,6 +401,11 @@ export const publicAsyncRoute = [
component: DisplayNew, component: DisplayNew,
hidden: true hidden: true
}, },
{
path: '/displayCity/:mode',
component: DisplayCity,
hidden: true
},
{ {
path: '/design/displayNew/:mode', path: '/design/displayNew/:mode',
component: DesignDisplayNew, component: DesignDisplayNew,

View File

@ -1,8 +1,8 @@
export function getBaseUrl() { export function getBaseUrl() {
let BASE_API; let BASE_API;
if (process.env.NODE_ENV === 'development') { if (process.env.NODE_ENV === 'development') {
BASE_API = 'https://joylink.club/jlcloud'; // BASE_API = 'https://joylink.club/jlcloud';
// BASE_API = 'https://test.joylink.club/jlcloud'; BASE_API = 'https://test.joylink.club/jlcloud';
// BASE_API = 'http://192.168.8.107:9000'; // 袁琪 // BASE_API = 'http://192.168.8.107:9000'; // 袁琪
// BASE_API = 'http://192.168.8.129:9000'; // 旭强 // BASE_API = 'http://192.168.8.129:9000'; // 旭强
// BASE_API = 'http://192.168.8.119:9000'; // 张赛 // BASE_API = 'http://192.168.8.119:9000'; // 张赛

View File

@ -1,10 +1,10 @@
import Vue from 'vue'; import Vue from 'vue';
import { getBaseUrl } from '@/utils/baseUrl' import { getBaseUrl } from '@/utils/baseUrl';
// 创建或者打开数据库 // 创建或者打开数据库
export function openIndexedDB() { export function openIndexedDB() {
const baseUrl = getBaseUrl(); const baseUrl = getBaseUrl();
const indexedDBName = baseUrl.replace(/http.?:\/\/(.*)[\/|:].*/, "$1"); const indexedDBName = baseUrl.replace(/http.?:\/\/(.*)[\/|:].*/, '$1');
const request = window.indexedDB.open(indexedDBName, 1); const request = window.indexedDB.open(indexedDBName, 1);
request.onerror = function (event) { request.onerror = function (event) {
console.log('数据库打开报错'); console.log('数据库打开报错');
@ -18,6 +18,7 @@ export function openIndexedDB() {
Vue.prototype.$db = event.target.result; Vue.prototype.$db = event.target.result;
Vue.prototype.$db.createObjectStore('mapData', { keyPath: 'id' }); Vue.prototype.$db.createObjectStore('mapData', { keyPath: 'id' });
Vue.prototype.$db.createObjectStore('runPlan', { keyPath: 'templateId' }); Vue.prototype.$db.createObjectStore('runPlan', { keyPath: 'templateId' });
Vue.prototype.$db.createObjectStore('composeTemplateList', { keyPath: 'id' });
}; };
} }
// 新增数据 // 新增数据

View File

@ -132,6 +132,8 @@ export default {
{ name: '站厅D', value: 'bgStationD' }, { name: '站厅D', value: 'bgStationD' },
{ name: '站厅E', value: 'bgStationE' }, { name: '站厅E', value: 'bgStationE' },
{ name: '站厅F', value: 'bgStationF' }, { name: '站厅F', value: 'bgStationF' },
{ name: '门禁站厅A', value: 'bgDoorStationA' },
{ name: '门禁站台A', value: 'bgDoorStandA' }
], ],
rules: { rules: {

View File

@ -233,10 +233,8 @@ export default {
}, },
// //
onSelected(em) { onSelected(em) {
console.log(em);
}, },
onDblclick(em) { onDblclick(em) {
console.log(em);
}, },
// //
onContextMenu(em) { onContextMenu(em) {

View File

@ -0,0 +1,192 @@
<template>
<el-form ref="form" :model="formModel" class="composeForm" label-width="110px">
<!-- name -->
<el-form-item prop="name" label="元素名称" class="formName" :rules="[ { required: true, message:'请输入名称', trigger: 'blur' }]">
<el-input
v-model="formModel.name"
size="small"
type="text"
style="width:200px"
:maxlength="200"
/>
</el-form-item>
<template v-for="(styleGroup,index) in form.formGroup">
<div v-if="styleGroup.styleList.length>0" :key="index" class="styleGroup">
<div class="styleGroupName">
{{ styleGroup.name }}
</div>
<template v-for="item in styleGroup.styleList">
<el-form-item :key="item.prop" :prop="styleGroup.type+'.'+item.prop" :label="item.label" class="formName" :rules="item.rules?item.rules:[]">
<!-- {{ tempModel=styleGroup.type?formModel[styleGroup.type]:formModel }} -->
<el-tooltip
v-if="item.description"
:id="item.prop"
effect="dark"
:content="item.description"
:popper-options="{removeOnDestroy:true}"
placement="bottom-start"
popper-class="composeItemTooltip"
>
<span class="el-icon-info itemTips" />
</el-tooltip>
<template v-if="checkFieldType(item, 'Number')">
<el-input-number
v-if="item.precision!=undefined"
v-model="formModel[styleGroup.type][item.prop]"
size="small"
:min="isNaN(item.min) ? -Infinity : item.min"
:max="isNaN(item.max)? Infinity : item.max"
:step="isNaN(item.step) ? -Infinity : item.step"
:precision="item.precision"
/>
<el-input-number
v-else
v-model="formModel[styleGroup.type][item.prop]"
size="small"
:min="isNaN(item.min) ? -Infinity : item.min"
:max="isNaN(item.max)? Infinity : item.max"
:step="isNaN(item.step) ? -Infinity : item.step"
/>
</template>
<template v-else-if="checkFieldType(item, 'Boolean')">
<el-switch
v-model="formModel[styleGroup.type][item.prop]"
style="vertical-align: top;"
size="small"
:active-color="item.activeColor || '#409eff'"
:inactive-color="item.inactiveColor || '#dcdfe6'"
/>
</template>
<template v-else-if="checkFieldType(item, 'Color')">
<el-color-picker
v-model="formModel[styleGroup.type][item.prop]"
show-alpha
size="small"
/>
</template>
<template v-else-if="checkFieldType(item, 'NumberArray')">
<el-input-number
v-for="count in item.length"
:key="count"
v-model="formModel[styleGroup.type][item.prop][count-1]"
style="margin:6px 0px 0px 5px"
size="small"
:min="isNaN(item.min) ? -Infinity : item.min"
:max="isNaN(item.max)? Infinity : item.max"
:step="isNaN(item.step) ? -Infinity : item.step"
:precision="item.precision"
/>
</template>
<template v-else-if="checkFieldType(item, 'Points')">
<div class="point-section">
<template v-for="(point, j) in formModel[styleGroup.type][item.prop]">
<div :key="j" style="overflow: hidden;">
<el-input-number v-model="point[0]" size="mini" @blur="changeNumber(0,j,formModel[styleGroup.type],item.prop)" />
<span class="pointSplice">, </span>
<el-input-number v-model="point[1]" size="mini" @blur="changeNumber(1,j,formModel[styleGroup.type],item.prop)" />
<el-button
icon="el-icon-plus"
circle
class="point-button"
@click="addPoint(j,formModel[styleGroup.type],item.prop)"
/>
<el-button
icon="el-icon-minus"
:disabled="j <3"
circle
class="point-button"
style="margin-left: 4px;"
@click="delPoint(j,formModel[styleGroup.type],item.prop)"
/>
</div>
</template>
</div>
</template>
<template v-else-if="checkFieldType(item, 'Select')">
<el-select
:ref="'select_'+item.prop"
v-model="formModel[styleGroup.type][item.prop]"
size="small"
>
<el-option
v-for="option in item.optionList"
:key="option['value']"
:label="option['label']"
:value="option['value']"
/>
</el-select>
</template>
<template v-else-if="checkFieldType(item, 'String')">
<el-input
v-model="formModel[styleGroup.type][item.prop]"
size="small"
type="text"
style="width:200px"
:maxlength="item.maxlength"
/>
</template>
</el-form-item>
</template>
</div>
</template>
</el-form>
</template>
<script>
export default {
name:'DataForm',
props: {
form: {
type: Object,
required: true
},
formModel: {
type: Object,
required: true
}
},
data() {
return {
};
},
methods: {
checkFieldType(field, type) {
return field.type === type;
},
init() {
this.$refs.form.resetFields();
},
changeNumber(type, index, form, prop) {
if (form[prop][index][type] == undefined || parseFloat(form[prop][index][type])) {
const newForm = Object.assign([], form[prop]);
newForm[index][type] = parseFloat(form[prop][index][type]) || 0;
this.$set(form, prop, newForm);
}
},
addPoint(index, form, prop) {
const data = [0, 0];
form[prop].splice(index + 1, 0, data);
},
delPoint(index, form, prop) {
form[prop].splice(index, 1);
}
}
};
</script>
<style lang="scss" scoped>
.composeForm{padding: 10px 15px;}
.styleGroup{border: 1px #ccc solid;padding:25px 20px 20px 20px;position: relative;margin-bottom: 30px;}
.formName .el-form-item__label {font-size:14px;}
.formName.el-form-item {margin-bottom:25px}
.point-section {float:left;display: inline-block;margin-top:8px}
.pointSplice{display: inline-block;margin-left: 4px;margin-right:4px;line-height: 28px;font-size:14px}
.styleGroupName{font-size: 14px;position: absolute;max-width: 100px;height: 20px;background: #fff;left: 10px;top: -9px;text-align: center;padding: 0px 10px;}
.point-button {width:28px;height:28px;display:inline-block;margin-left:5px;text-align:center;padding:0px;}
.itemTips{display: inline-block;font-size: 18px;vertical-align: top;margin-left: -8px;margin-right: 5px;cursor: pointer;color: #dde0e3;}
// align-items: center;justify-content: center;
</style>
<style lang="scss">
.composeForm .el-form-item__label{line-height:20px;margin-top: 20px;transform: translateY(-50%);}
.composeForm .el-form-item__content{font-size: 0px;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align: center;-ms-flex-align: center;align-items: center;min-height: 40px;flex-wrap: wrap;justify-content: flex-start;}
.composeItemTooltip{max-width:300px}
</style>

View File

@ -0,0 +1,163 @@
<template>
<el-form-item :label="item.label" class="formName" :rules="item.rules?item.rules:[]" :prop="parentProp+'.'+item.prop">
<!-- :prop="styleGroup.type+'.'+item.prop" -->
<template v-if="checkFieldType(item, 'Number')">
<el-input-number
v-if="item.precision!=undefined"
v-model="data[item.prop]"
size="small"
:min="isNaN(item.min) ? -Infinity : item.min"
:max="isNaN(item.max)? Infinity : item.max"
:step="isNaN(item.step) ? -Infinity : item.step"
:precision="item.precision"
/>
<el-input-number
v-else
v-model="data[item.prop]"
size="small"
:min="isNaN(item.min) ? -Infinity : item.min"
:max="isNaN(item.max)? Infinity : item.max"
:step="isNaN(item.step) ? -Infinity : item.step"
/>
</template>
<template v-else-if="checkFieldType(item, 'Boolean')">
<el-switch
v-model="data[item.prop]"
style="vertical-align: top;"
size="small"
:active-color="item.activeColor || '#409eff'"
:inactive-color="item.inactiveColor || '#dcdfe6'"
/>
</template>
<template v-else-if="checkFieldType(item, 'Color')">
<el-color-picker
v-model="data[item.prop]"
show-alpha
size="small"
/>
</template>
<template v-else-if="checkFieldType(item, 'NumberArray')">
<el-input-number
v-for="count in item.length"
:key="parentProp+'.'+item.prop+''+count"
v-model="data[item.prop][count-1]"
style="margin:6px 0px 0px 5px"
size="small"
:min="isNaN(item.min) ? -Infinity : item.min"
:max="isNaN(item.max)? Infinity : item.max"
:step="isNaN(item.step) ? -Infinity : item.step"
:precision="item.precision"
/>
</template>
<!-- <template v-else-if="checkFieldType(item, 'Points')">
<div class="point-section">
<template v-for="(point, j) in formModel[styleGroup.type][item.prop]">
<div :key="j" style="overflow: hidden;">
<el-input-number v-model="point[0]" size="mini" @blur="changeNumber(0,j,formModel[styleGroup.type],item.prop)" />
<span class="pointSplice">, </span>
<el-input-number v-model="point[1]" size="mini" @blur="changeNumber(1,j,formModel[styleGroup.type],item.prop)" />
<el-button
icon="el-icon-plus"
circle
class="point-button"
@click="addPoint(j,formModel[styleGroup.type],item.prop)"
/>
<el-button
icon="el-icon-minus"
:disabled="j <3"
circle
class="point-button"
style="margin-left: 4px;"
@click="delPoint(j,formModel[styleGroup.type],item.prop)"
/>
</div>
</template>
</div>
</template> -->
<template v-else-if="checkFieldType(item, 'Select')">
<el-select
:ref="'select_'+item.prop"
v-model="data[item.prop]"
size="small"
>
<el-option
v-for="option in item.optionList"
:key="option['value']"
:label="option['label']"
:value="option['value']"
/>
</el-select>
</template>
<template v-else-if="checkFieldType(item, 'String')">
<el-input
v-model="data[item.prop]"
size="small"
type="text"
style="width:200px"
:maxlength="item.maxlength"
/>
</template>
<span class="el-icon-error deleteCurrent" @click="deleteCurrent(item.prop)" />
</el-form-item>
</template>
<script>
import elementConst from '@/iscs_new/core/form/elementConst';
export default {
name:'EachFormItem',
props: {
prop: {
type: String,
required: true
},
data: {
type: Object,
required: true
},
type:{
type: String,
required: true
},
styleType: {
type: String,
required: true
},
parentProp:{
type: String,
required: true
}
},
data() {
return {
item:{label:'', type:'' }
};
},
mounted() {
const list = elementConst[this.type]['formList'][this.styleType];
const data = list.find(each=>{ return each.prop == this.prop; });
if (data) {
this.item = data;
}
},
methods:{
checkFieldType(field, type) {
return field.type === type;
},
deleteCurrent(prop) {
this.$emit('deleteShape', {styleType:this.styleType, data:this.data, prop:prop});
}
}
};
</script>
<style lang="scss" scoped>
.deleteCurrent{
font-size: 20px;
color: #f00;
margin-left: 15px;
vertical-align: top;
display: inline-flex;
float: right;
margin-right: 10px;
margin-top: 8px;
cursor: pointer;
}
</style>

View File

@ -0,0 +1,225 @@
<template>
<div>
<el-button type="primary" size="small" class="addStatus" @click="addStatus">添加</el-button>
<el-form ref="form" class="tableForm" :model="formModel">
<!-- label-width="110px" -->
<el-table
:data="formModel.stateList"
stripe
class="eachStatusTable"
size="mini"
row-key="id"
:expand-row-keys="expandKeys"
@expand-change="expandChange"
>
<el-table-column type="expand">
<template slot-scope="props">
<div class="styleList">
<div class="styleListName">样式</div>
<div class="addStyleForm">
<el-select v-model="props.row.defaultStyleSelect" size="mini">
<el-option v-for="(eachStyle) in styleSelectList" :key="eachStyle.value" :label="eachStyle.label" :value="eachStyle.value" />
</el-select>
<el-button type="primary" size="mini" class="addStyle" @click="addStyle(props.$index,'style')">添加</el-button>
</div>
<div v-if="props.row.style" class="styleInList">
<div v-for="(eachStyleInfo,index) in Object.keys(props.row.style)" :key="index" class="eachStyleInfo">
<each-form-item
:prop="eachStyleInfo"
:data="formModel.stateList[props.$index].style"
:type="formModel.type"
style-type="style"
:parent-prop="'stateList.'+props.$index+'.style'"
@deleteShape="deleteShape"
/>
</div>
</div>
</div>
<div class="shapeList">
<div class="shapeListName">绘图</div>
<div class="addStyleForm">
<el-select v-model="props.row.defaultShapeSelect" size="mini">
<el-option v-for="(eachShape) in shapeSelectList" :key="eachShape.value" :label="eachShape.label" :value="eachShape.value" />
</el-select>
<el-button type="primary" size="mini" class="addStyle" @click="addStyle(props.$index,'shape')">添加</el-button>
</div>
<div v-if="props.row.shape" class="styleInList">
<div v-for="(eachShapeInfo,index) in Object.keys(props.row.shape)" :key="index" class="eachStyleInfo">
<each-form-item
:prop="eachShapeInfo"
:data="formModel.stateList[props.$index].shape"
:type="formModel.type"
style-type="shape"
:parent-prop="'stateList.'+props.$index+'.shape'"
@deleteShape="deleteShape"
/>
</div>
</div>
</div>
</template>
</el-table-column>
<el-table-column label="状态" width="178">
<!-- align="center" -->
<template slot-scope="scope">
<el-form-item
:prop="'stateList.'+scope.$index+'.status'"
:rules="[{required: true, message:'请输入状态', trigger: 'blur'}]"
>
<el-input v-model="scope.row.status" size="mini" type="text" style="width:158px" :maxlength="100" />
</el-form-item>
</template>
</el-table-column>
<el-table-column label="描述" width="200">
<template slot-scope="scope">
<el-form-item
:prop="'stateList.'+scope.$index+'.description'"
:rules="[{required: true, message:'请输入描述', trigger: 'blur'}]"
>
<el-input v-model="scope.row.description" size="mini" type="text" style="width:170px" :maxlength="100" />
</el-form-item>
</template>
</el-table-column>
<el-table-column label="操作" width="100">
<template slot-scope="scope">
<el-button type="danger" size="mini" @click="deleteStatus(scope.$index,scope.row)">删除</el-button>
<!-- <el-button type="success" size="mini" @click="previewStatus(scope.$index,scope.row)">预览</el-button> -->
</template>
</el-table-column>
</el-table>
<!-- :model="formModel" -->
</el-form>
</div>
</template>
<script>
import elementConst from '@/iscs_new/core/form/elementConst';
import EachFormItem from './eachFormItem';
import * as utils from '@/iscs_new/utils/utils';
export default {
name:'TableForm',
components:{
EachFormItem
},
props: {
composeElem: {
type: Object,
required: true
}
},
data() {
return {
formModel:this.composeElem,
// {stateList:this.composeElem.stateList, style:this.composeElem.style, type:this.composeElem.type, shape:this.composeElem.shape}
styleSelectList:[],
shapeSelectList:[],
expandKeys:[]
};
},
mounted() {
const styleNameList = Object.keys(this.formModel.style);
const style = elementConst[this.formModel.type]['formList']['style'];
styleNameList.forEach(eachStyleName=>{
const eachStyle = style.find(each=>{ return each.prop == eachStyleName; });
this.styleSelectList.push({value:eachStyleName, label:eachStyle.label});
});
const shapeNameList = Object.keys(this.formModel.shape);
const shape = elementConst[this.formModel.type]['formList']['shape'];
shapeNameList.forEach(eachShapeName=>{
const eachShape = shape.find(each=>{ return each.prop == eachShapeName; });
this.shapeSelectList.push({value:eachShapeName, label:eachShape.label});
});
this.formModel.stateList.forEach(each=>{
each.defaultStyleSelect = this.styleSelectList[0].value;
each.defaultShapeSelect = this.shapeSelectList[0].value;
});
},
methods:{
addStatus() {
const length = this.formModel.stateList.length;
this.formModel.stateList.push({id:length + 1, status:'', description:'', defaultStyleSelect:this.styleSelectList[0].value,
defaultShapeSelect:this.shapeSelectList[0].value});
this.expandKeys.push(length + 1);
},
deleteStatus(index, row) {
this.formModel.stateList.splice(index, 1);
const key = this.expandKeys.findIndex(each=>{ return each == row.id; });
if (key > 0) { this.expandKeys.splice(key, 1); }
},
// previewStatus(index, row) {
// const styleLength = Object.keys(row.style || {}).length;
// const shapeLength = Object.keys(row.shape || {}).length;
// if (styleLength > 0 || shapeLength > 0 ) {
// const data = {style:row.style || {}, shape:row.shape || {}};
// debugger;
// // this.$iscs
// }
// },
addStyle(index, type) {
const style = this.formModel.stateList[index][type];
if (!style) {
this.$set(this.formModel.stateList[index], type, {});
}
const data = this.formModel.stateList[index];
const dataName = 'default' + type.replace(type[0], type[0].toUpperCase()) + 'Select';
if (!data[type][data[dataName]] && data[dataName]) {
const other = utils.deepClone(this.formModel[type][data[dataName]]);
this.$set(this.formModel.stateList[index][type], data[dataName], other);
}
},
expandChange(row, expandedRows) {
this.expandKeys = [];
expandedRows.forEach(each=>{
this.expandKeys.push(each.id);
});
},
deleteShape({styleType, data, prop}) {
this.$delete(data, prop);
}
}
};
</script>
<style lang="scss" scoped>
.eachStatusTable{
width: 530px;
margin-left: 10px;
margin-top: 10px;
border: 1px #dedede solid;
}
.addStatus{
float: right;
display: inline-block;
margin-bottom: 10px;
margin-right: 10px;
}
.styleList,.shapeList{
padding: 15px 10px;
border: 1px #ccc solid;
position: relative;
margin-bottom:20px;
display: inline-block;
width: 100%;
}
.styleListName,.shapeListName{
font-size: 14px;
position: absolute;
left: 6px;
top: -8px;
width: 40px;
background: #fff;
text-align: center;
}
.addStyle,.addShape{
float: right;
display: inline-block;
}
.styleInList{
margin-top: 15px;
padding-left: 30px;
}
// .eachStyleInfo .el-form-item{border-bottom: 1px #dedede solid;}
</style>
<style lang="sass">
.eachStatusTable .el-table__expanded-cell{padding: 20px;}
</style>

View File

@ -0,0 +1,323 @@
<template>
<transition name="el-zoom-in-center">
<div class="mapPaint">
<div class="map-view">
<iscs-canvas ref="iscsCanvas" @selected="onSelected" />
</div>
<div class="right-card" :class="{'hide': draftShow}">
<div class="btn_draft_box" @click="draftShow = !draftShow"><i :class="draftShow?'el-icon-arrow-right':'el-icon-arrow-left'" /></div>
<el-card type="border-card" class="heightClass">
<div slot="header" class="clearfix">
<el-button
type="text"
style="float: right; padding: 3px 0; margin-right: 5px;"
@click="onSave"
>保存</el-button>
</div>
<el-tabs id="cardTab" v-model="cardTab" class="card" type="border-card" @tab-click="onSelectCardTab">
<el-tab-pane label="元素绘制" name="first">
<el-tabs v-model="enabledTab" class="card" type="card" @tab-click="onSelectTab">
<el-tab-pane v-for="(element,index) in elementList" :key="index" :label="element.name" :name="element.type" :lazy="true">
<data-form :ref="'dataform'+element.type" :form="element" :form-model="element.model" />
</el-tab-pane>
</el-tabs>
<div class="bottomBtnGroup">
<el-button v-show="!selected" type="primary" size="small" @click="onSubmit">添加</el-button>
<el-button v-show="selected" type="warning" size="small" @click="onModify">修改</el-button>
<el-button v-show="selected" type="danger" size="small" @click="onDelete">删除</el-button>
</div>
</el-tab-pane>
<el-tab-pane label="状态编辑" name="second">
<el-tabs v-model="statusTab" class="card" type="card" @tab-click="onSelectTab">
<el-tab-pane v-for="(composeElem,index) in composeElemList" :key="index" :label="composeElem.name" :name="composeElem.code" :lazy="true">
<table-form :ref="'tableform'+composeElem.code" :state-list="composeElem.stateList" />
</el-tab-pane>
</el-tabs>
</el-tab-pane>
</el-tabs>
</el-card>
</div>
</div>
</transition>
</template>
<script>
import localStore from 'storejs';
import iscsCanvas from './iscsCanvas';
import formBuilder from '@/iscs_new/core/form/formBuilder';
import DataForm from '../components/dataForm';
import TableForm from '../components/tableForm';
import orders from '@/iscs_new/utils/orders';
import * as utils from '@/iscs_new/utils/utils';
import Idb from '../utils/indexedDb.js';
import shapeType from '@/iscs_new/constant/shapeType.js';
export default {
name: 'IscsView',
components: {
iscsCanvas,
DataForm,
TableForm
},
data() {
return {
size: {
width: this.$store.state.app.width - 521,
height: this.$store.state.app.height - 60
},
widthLeft: Number(localStore.get('LeftWidth')) || 450,
draftShow: false,
selected: null,
enabledTab:'',
cardTab:'first',
statusTab:'',
showDeleteButton:false,
elementList:[],
composeElemList:[]
};
},
computed:{
iscsMode() {
return this.$route.query.mode;
}
},
watch: {
$route(val) {
this.onIscsChange(this.$route.query.mode, this.$route.query.system, this.$route.query.part);
}
},
mounted() {
this.composeName = this.$route.query.composeName;
this.elementList = formBuilder.buildFormList();
this.enabledTab = this.elementList[0].type;
this.getComposeElemList();
},
methods: {
onIscsChange(mode, system, part) {
// this.$refs.iscsPlate.show(mode, system, part);
// this.$refs.iscsPlate.drawIscsInit();
},
onSave() {
const id = this.$route.query.id;
const name = this.$route.query.name||"<模型名称>";
const type = this.$route.query.type||"<模型类型>";
const source = this.$iscs.getSource();
if (id && source) {
const elementList = source.elementList.map(el => {
return this.$iscs.getShapeByCode(el.code).model;
});
const composeList = source.composeList.map(el => {
return this.$iscs.getShapeByCode(el.code).model;
});
const rect = elementList.reduce((temp,el) => {
const shape = this.$iscs.getShapeByCode(el.code);
return shape&&temp? temp.union(shape.getBoundingRect().clone()): shape.getBoundingRect()
}, null);
const position = rect? [(rect.x + rect.width)/2, (rect.y + rect.height)/2]: [0,0];
const model = { id, name, type, elementList, composeList, position };
Idb.delete('composeList', model.id);
Idb.write('composeList', model);
Idb.list('composeList').then(list => {
console.log(list)
})
}
},
onSelectTab() {
this.selected = null;
},
onSelectCardTab() {
},
onSelected(em) {
if (em.model) {
this.selected = JSON.parse(JSON.stringify(em.model));
const elem = this.elementList.find(el => el.type == this.selected.type);
if (elem) {
elem.model = this.selected;
this.enabledTab = this.selected.type;
}
} else {
this.selected = null;
this.clear(this.enabledTab);
}
},
onSubmit() {
this.$refs['dataform' + this.enabledTab][0].$refs['form'].validate((valid) => {
if (valid) {
const formModel = this.$refs['dataform' + this.enabledTab][0].formModel;
const newModel = utils.deepClone(formModel);
newModel.code = utils.getUID(this.enabledTab);
newModel.type = this.enabledTab;
newModel.name = '<名称>';
newModel.stateList = [];
this.$refs.iscsCanvas.doAction([{model: newModel, action: {shapeType: shapeType.Element, order: orders.Add}}]);
this.clear(this.enabledTab);
this.getComposeElemList();
}
});
},
onModify() {
this.$refs['dataform' + this.enabledTab][0].$refs['form'].validate((valid) => {
if (valid) {
const model = utils.deepClone(this.$refs['dataform' + this.enabledTab][0].formModel);
model.code = this.selected.code;
model.type = this.selected.type;
model.name = this.selected.name;
this.$refs.iscsCanvas.doAction([{model, action: {shapeType: shapeType.Element, order: orders.Update}}]);
this.clear(this.enabledTab);
}
});
},
onDelete() {
this.$refs['dataform' + this.enabledTab][0].$refs['form'].validate((valid) => {
if (valid) {
const model = utils.deepClone(this.$refs['dataform' + this.enabledTab][0].formModel);
model.code = this.selected.code;
model.type = this.selected.type;
model.name = this.selected.name;
this.$refs.iscsCanvas.doAction([{model, action: {shapeType: shapeType.Element, order: orders.Delete}}]);
this.clear(this.enabledTab);
this.getComposeElemList();
}
});
},
clear(enabledTab) {
this.$refs['dataform' + enabledTab][0].init();
this.selected = null;
},
getComposeElemList() {
const source = this.$iscs.getSource();
if (source &&
source.elementList &&
source.elementList.length) {
this.composeElemList = source.elementList;
this.statusTab = this.composeElemList[0].code;
} else {
this.composeElemList = [];
}
}
}
};
</script>
<style rel="stylesheet/scss" lang="scss" scoped>
@import "src/styles/mixin.scss";
.card{
height: 100%;
display:flex;width: 100%;flex-direction: column
}
.card .el-tab-pane{
flex:1;
height: 100%;
overflow: auto;
padding-bottom:30px;
&::-webkit-scrollbar {
width: 6px;
height: 6px;
// height: 110px;
background-color: #FFFFFF;
}
&::-webkit-scrollbar-track {
// box-shadow: inset 0 0 6px rgba(0,0,0,0.3);
border-radius: 10px;
background-color: #FFFFFF;;
}
&::-webkit-scrollbar-thumb {
border-radius: 10px;
// box-shadow: inset 0 0 6px rgba(0,0,0,.3);
background-color: #eaeaea;
}
&::-webkit-scrollbar-thumb:hover {
border-radius: 5px;
// box-shadow: inset 0 0 5px rgba(0,0,0,0.2);
background: rgba(0,0,0,0.4);
}
}
.map-view {
float: left;
width: 100%;
}
.heightClass{height:100%;overflow:hidden;display:flex;width: 100%;flex-direction: column;}
.mapPaint{
height: 100%;
overflow: hidden;
width:100%;
position:absolute;
left:0;top:0;
}
.right-card{
width: 550px;
height: 100%;
position: absolute;
right: 0;
transform: translateX(550px);
transition: all 0.5s;
background: #fff;
z-index: 9;
/deep/{
.v-modal{
opacity: 0;
}
}
&.hide{
transform: translateX(0);
}
.btn_draft_box{
position: absolute;
left: 0;
top: 50%;
padding: 8px 3px;
background: #fff;
z-index: 10;
transform: translateX(-22px);
cursor: pointer;
border-radius: 5px 0 0 5px;
box-shadow: -2px 0px 2px #000000;
}
.btn_table_box {
position: absolute;
left: 0;
top: calc(50% + 50px);
padding: 8px 3px;
background: #fff;
z-index: 10;
transform: translateX(-22px);
cursor: pointer;
border-radius: 5px 0 0 5px;
box-shadow: -2px 0px 2px #000000;
}
}
.bottomBtnGroup{
position: absolute;
bottom: 0;
width: 100%;
height: 43px;
text-align: right;
right: 0px;
background: #fff;
z-index: 2;
padding-top: 5px;
border-bottom: 1px #dedede solid;
box-shadow: 2px -3px 5px #dedede;
}
.bottomBtnGroup button{
display: inline-block;
margin-right:10px;
}
</style>
<style lang="scss">
.heightClass .el-card__body{
flex:1;
height: 100%;
overflow: hidden;
}
#cardTab .el-tabs__content{
padding:0px;
}
</style>

View File

@ -0,0 +1,183 @@
<template>
<div>
<div :id="iscsId" v-loading="loading" :style="{ width: width +'px', height: height +'px',background:'#425a74' }" class="iscs-canvas" />
</div>
</template>
<script>
import Vue from 'vue';
import Iscs from '@/iscs_new/map';
import Idb from '../utils/indexedDb.js';
import ShapeBuilder from '@/iscs_new/plugins/shapeBuilder';
import ShapeProperty from '@/iscs_new/plugins/shapeProperty';
import ShapeContextMenu from '@/iscs_new/plugins/shapeContextMenu';
import { mapGetters } from 'vuex';
export default {
data() {
return {
dataZoom: {
offsetX: '0',
offsetY: '0',
scaleRate: '1'
},
config: {
scaleRate: '1',
origin: {
x: 0,
y: 0
}
},
loading: false
};
},
computed: {
...mapGetters('iscs', [
'iscs'
]),
iscsId() {
return ['iscs', (Math.random().toFixed(5)) * 100000].join('_');
},
width() {
return document.documentElement.clientWidth;
},
height() {
return document.documentElement.clientHeight;
}
},
watch: {
'$store.state.config.canvasSizeCount': function (val) {
this.resize();
},
'$store.state.socket.equipmentStatus': function (val) {
if (val.length) {
this.stateMessage(val);
}
}
},
mounted() {
this.init();
},
beforeDestroy() {
this.destroy();
},
methods: {
//
init() {
document.getElementById(this.iscsId).oncontextmenu = function (e) {
return false;
};
this.$iscs = new Iscs({
dom: document.getElementById(this.iscsId),
draw: true,
config: {
renderer: 'canvas',
width: this.width,
height: this.height
},
options: {
scaleRate: 1,
offsetX: 0,
offsetY: 0
},
plugins: [
ShapeBuilder,
// ShapeProperty,
ShapeContextMenu
]
});
const option = {
panEnable: true,
zoomEnable: true,
keyEnable: true,
draggle: true,
selecting: true,
selectable: true,
reflect: true
}
if (this.$route.query.id) {
setTimeout(_ => {
Idb.select('composeList', this.$route.query.id).then(resp => {
this.$iscs.setMap([], {
elementList: resp.elementList||[],
composeList: resp.composeList||[]
}, option);
}).catch(error => {
this.$iscs.setMap([], {
elementList: [],
composeList: []
}, option);
})
}, 1000)
} else {
this.$iscs.setMap([], {
elementList: [],
composeList: []
}, option);
}
Vue.prototype.$iscs = this.$iscs;
this.$iscs.on('viewLoaded', this.onViewLoaded, this);
this.$iscs.on('contextmenu', this.onContextMenu, this);
this.$iscs.on('click', this.onClick, this);
this.$iscs.on('reflect', this.onReflect, this);
this.$iscs.on('keyboard', this.onKeyboard, this);
window.document.oncontextmenu = function () {
return false;
};
},
//
onViewLoaded(e) {
},
//
onKeyboard(hook) {
console.log(hook);
},
//
onClick(em={}) {
this.$emit('selected', em);
},
onReflect(em={}) {
this.$emit('selected', this.$iscs.getShapeByCode(em.code));
},
//
onContextMenu(em={}) {
this.$emit('contextMenu', em.model);
},
//
doAction(list) {
this.$iscs && this.$iscs.render(list);
},
//
stateMessage(val) {
this.$iscs && this.$iscs.setDeviceStatus(val);
},
//
resize() {
this.$nextTick(() => {
this.$iscs && this.$iscs.resize({ width: this.width, height: this.height });
});
},
//
destroy() {
if (this.$iscs) {
this.$iscs.destroy();
this.$iscs = null;
Vue.prototype.$iscs = null;
}
},
}
};
</script>
<style rel="stylesheet/scss" lang="scss" scoped>
.iscs-button{
position: absolute;
float: right;
right: 20px;
bottom: 15px;
}
.iscs-canvas{
}
</style>

View File

@ -0,0 +1,386 @@
<template>
<transition name="el-zoom-in-center">
<div class="mapPaint">
<div class="map-view">
<iscs-canvas ref="iscsCanvas" @selected="onSelected" @setStateList="setStateList" />
</div>
<div class="right-card" :class="{'hide': draftShow}">
<div class="btn_draft_box" @click="draftShow = !draftShow"><i :class="draftShow?'el-icon-arrow-right':'el-icon-arrow-left'" /></div>
<el-card type="border-card" class="heightClass">
<div slot="header" class="clearfix">
<el-button
type="text"
style="float: right; padding: 3px 0; margin-right: 5px;"
@click="onSave"
>保存</el-button>
<el-button
v-if=" composeElemList.length>0"
type="text"
style="float: right; padding: 3px 0; margin-right: 5px;"
@click="onPreview"
>预览</el-button>
<el-button
v-if=" composeElemList.length>0"
type="text"
style="float: right; padding: 3px 0; margin-right: 5px;"
@click="onModifyStatus"
>状态编辑</el-button>
</div>
<el-tabs id="cardTab" v-model="cardTab" class="card" type="border-card" @tab-click="onSelectCardTab">
<el-tab-pane label="元素绘制" name="first">
<el-tabs v-model="enabledTab" class="card" type="card" @tab-click="onSelectTab">
<el-tab-pane v-for="(element,index) in elementList" :key="index" :label="element.name" :name="element.type" :lazy="true">
<data-form :ref="'dataform'+element.type" :form="element" :form-model="element.model" />
</el-tab-pane>
</el-tabs>
<div class="bottomBtnGroup">
<el-button v-show="!selected" type="primary" size="small" @click="onSubmit">添加</el-button>
<el-button v-show="selected" type="warning" size="small" @click="onModify">修改</el-button>
<el-button v-show="selected" type="danger" size="small" @click="onDelete">删除</el-button>
</div>
</el-tab-pane>
<el-tab-pane label="单元素状态编辑" name="second">
<el-tabs v-model="statusTab" class="card" type="card" @tab-click="onSelectTab">
<el-tab-pane v-for="(composeElem,index) in composeElemList" :key="index" :label="composeElem.name" :name="composeElem.code" :lazy="true">
<table-form :ref="'tableform'+composeElem.code" :compose-elem="composeElem" />
</el-tab-pane>
</el-tabs>
<div class="bottomBtnGroup">
<el-button type="primary" size="small" @click="onSaveStatus">保存</el-button>
</div>
</el-tab-pane>
<!-- <el-tab-pane label="状态编辑" name="third">
<status-combine-edit ref="editStatusForm" :state-list="stateList" @saveStateList="saveStateList" />
<div class="bottomBtnGroup">
<el-button type="primary" size="small" @click="onEditStatusSave">保存</el-button>
</div>
</el-tab-pane> -->
</el-tabs>
</el-card>
</div>
<status-list ref="statusList" @setStateList="setStateList" />
</div>
</transition>
</template>
<script>
import localStore from 'storejs';
import iscsCanvas from './iscsCanvas';
import formBuilder from '@/iscs_new/core/form/formBuilder';
import DataForm from '../components/dataForm';
import TableForm from '../components/tableForm';
import orders from '@/iscs_new/utils/orders';
import * as utils from '@/iscs_new/utils/utils';
import Idb from '../utils/indexedDb.js';
import shapeType from '@/iscs_new/constant/shapeType.js';
// import StatusCombineEdit from './statusCombineEdit';
import StatusList from './statusList';
import { EventBus } from '@/scripts/event-bus';
export default {
name: 'IscsView',
components: {
iscsCanvas,
DataForm,
TableForm,
StatusList
// StatusCombineEdit
},
data() {
return {
size: {
width: this.$store.state.app.width - 521,
height: this.$store.state.app.height - 60
},
widthLeft: Number(localStore.get('LeftWidth')) || 450,
draftShow: false,
selected: null,
enabledTab:'',
cardTab:'first',
statusTab:'',
showDeleteButton:false,
elementList:[],
composeElemList:[],
stateList:[]
};
},
computed:{
iscsMode() {
return this.$route.query.mode;
}
},
watch: {
$route(val) {
this.onIscsChange(this.$route.query.mode, this.$route.query.system, this.$route.query.part);
}
},
mounted() {
this.composeName = this.$route.query.composeName;
this.elementList = formBuilder.buildFormList();
this.enabledTab = this.elementList[0].type;
this.getComposeElemList();
EventBus.$on('getComposeElemList', () => {
this.getComposeElemList();
});
},
methods: {
onIscsChange(mode, system, part) {
// this.$refs.iscsPlate.show(mode, system, part);
// this.$refs.iscsPlate.drawIscsInit();
},
onSave() {
const id = this.$route.query.id;
const name = this.$route.query.name || '<模型名称>';
const type = this.$route.query.type || '<模型类型>';
const source = this.$iscs.getSource();
if (id && source) {
const shapeList = source.elementList;
shapeList.forEach(el => {
return this.$iscs.getShapeByCode(el.code).model;
});
// const composeList = source.composeList.map(el => {
// return this.$iscs.getShapeByCode(el.code).model;
// });
// const rect = shapeList.reduce((temp, el) => {
// const shape = this.$iscs.getShapeByCode(el.code);
// return shape && temp ? temp.union(shape.getBoundingRect().clone()) : shape.getBoundingRect();
// }, null);
// const position = rect ? [(rect.x + rect.width) / 2, (rect.y + rect.height) / 2] : [0, 0];
const position = [0, 0];
const stateList = this.stateList;
const model = { id, name, type, shapeList, stateList, position };
Idb.delete('composeTemplateList', model.id);
Idb.write('composeTemplateList', model);
Idb.list('composeTemplateList').then(list => {
console.log(list);
});
}
},
onSelectTab() {
this.selected = null;
},
onSelectCardTab() {
},
setStateList(stateList) {
this.stateList = stateList;
},
onSelected(em) {
if (em.model) {
this.selected = JSON.parse(JSON.stringify(em.model));
const elem = this.elementList.find(el => el.type == this.selected.type);
if (elem) {
elem.model = this.selected;
this.enabledTab = this.selected.type;
this.cardTab = 'first';
}
} else {
this.selected = null;
this.clear(this.enabledTab);
}
},
onSubmit() {
this.$refs['dataform' + this.enabledTab][0].$refs['form'].validate((valid) => {
if (valid) {
const formModel = this.$refs['dataform' + this.enabledTab][0].formModel;
const newModel = utils.deepClone(formModel);
newModel.code = utils.getUID(this.enabledTab);
newModel.type = this.enabledTab;
newModel.name = '<名称>';
newModel.stateList = [];
this.$refs.iscsCanvas.doAction([{model: newModel, action: {shapeType: shapeType.Element, order: orders.Add}}]);
this.clear(this.enabledTab);
this.getComposeElemList();
}
});
},
onModify() {
this.$refs['dataform' + this.enabledTab][0].$refs['form'].validate((valid) => {
if (valid) {
const model = utils.deepClone(this.$refs['dataform' + this.enabledTab][0].formModel);
model.code = this.selected.code;
model.type = this.selected.type;
model.name = this.selected.name;
this.$refs.iscsCanvas.doAction([{model, action: {shapeType: shapeType.Element, order: orders.Update}}]);
this.clear(this.enabledTab);
}
});
},
onDelete() {
this.$refs['dataform' + this.enabledTab][0].$refs['form'].validate((valid) => {
if (valid) {
const model = utils.deepClone(this.$refs['dataform' + this.enabledTab][0].formModel);
model.code = this.selected.code;
model.type = this.selected.type;
model.name = this.selected.name;
this.$refs.iscsCanvas.doAction([{model, action: {shapeType: shapeType.Element, order: orders.Delete}}]);
this.clear(this.enabledTab);
this.getComposeElemList();
}
});
},
clear(enabledTab) {
this.$refs['dataform' + enabledTab][0].init();
this.selected = null;
},
getComposeElemList() {
const source = this.$iscs.getSource();
if (source &&
source.elementList &&
source.elementList.length) {
this.composeElemList = source.elementList;
this.statusTab = this.composeElemList[0].code;
} else {
this.composeElemList = [];
}
},
onSaveStatus() {
this.$refs['tableform' + this.statusTab][0].$refs['form'].validate((valid) => {
if (valid) {
const model = utils.deepClone(this.$refs['tableform' + this.statusTab][0].formModel);
model.stateList.map(state=>{ delete state.defaultStyleSelect; delete state.defaultShapeSelect; });
this.$refs.iscsCanvas.doAction([{model, action: {shapeType: shapeType.Element, order: orders.Update}}]);
this.onSave();
}
});
},
onEditStatusSave() {
this.$refs['editStatusForm'].$refs['form'].validate((valid) => {
if (valid) {
this.onSave();
}
});
},
onPreview() {
this.$router.push({ path: `/iscs_new/design/compose/preview`, query:{id:this.$route.query.id} });
},
onModifyStatus() {
this.$refs.statusList.doShow();
}
}
};
</script>
<style rel="stylesheet/scss" lang="scss" scoped>
@import "src/styles/mixin.scss";
.card{
height: 100%;
display:flex;width: 100%;flex-direction: column
}
.card .el-tab-pane{
flex:1;
height: 100%;
overflow: auto;
padding-bottom:30px;
&::-webkit-scrollbar {
width: 6px;
height: 6px;
// height: 110px;
background-color: #FFFFFF;
}
&::-webkit-scrollbar-track {
// box-shadow: inset 0 0 6px rgba(0,0,0,0.3);
border-radius: 10px;
background-color: #FFFFFF;;
}
&::-webkit-scrollbar-thumb {
border-radius: 10px;
// box-shadow: inset 0 0 6px rgba(0,0,0,.3);
background-color: #eaeaea;
}
&::-webkit-scrollbar-thumb:hover {
border-radius: 5px;
// box-shadow: inset 0 0 5px rgba(0,0,0,0.2);
background: rgba(0,0,0,0.4);
}
}
.map-view {
float: left;
width: 100%;
}
.heightClass{height:100%;overflow:hidden;display:flex;width: 100%;flex-direction: column;}
.mapPaint{
height: 100%;
overflow: hidden;
width:100%;
position:absolute;
left:0;top:0;
}
.right-card{
width: 550px;
height: 100%;
position: absolute;
right: 0;
transform: translateX(550px);
transition: all 0.5s;
background: #fff;
z-index: 9;
/deep/{
.v-modal{
opacity: 0;
}
}
&.hide{
transform: translateX(0);
}
.btn_draft_box{
position: absolute;
left: 0;
top: 50%;
padding: 8px 3px;
background: #fff;
z-index: 10;
transform: translateX(-22px);
cursor: pointer;
border-radius: 5px 0 0 5px;
box-shadow: -2px 0px 2px #000000;
}
.btn_table_box {
position: absolute;
left: 0;
top: calc(50% + 50px);
padding: 8px 3px;
background: #fff;
z-index: 10;
transform: translateX(-22px);
cursor: pointer;
border-radius: 5px 0 0 5px;
box-shadow: -2px 0px 2px #000000;
}
}
.bottomBtnGroup{
position: absolute;
bottom: 0;
width: 100%;
height: 43px;
text-align: right;
right: 0px;
background: #fff;
z-index: 2;
padding-top: 5px;
border-bottom: 1px #dedede solid;
box-shadow: 2px -3px 5px #dedede;
}
.bottomBtnGroup button{
display: inline-block;
margin-right:10px;
}
</style>
<style lang="scss">
.heightClass .el-card__body{
flex:1;
height: 100%;
overflow: hidden;
}
#cardTab .el-tabs__content{
padding:0px;
height: 100%;
}
</style>

View File

@ -0,0 +1,186 @@
<template>
<div>
<div :id="iscsId" v-loading="loading" :style="{ width: width +'px', height: height +'px',background:'#425a74' }" class="iscs-canvas" />
</div>
</template>
<script>
import Vue from 'vue';
import Iscs from '@/iscs_new/map';
import Idb from '../utils/indexedDb.js';
import ShapeBuilder from '@/iscs_new/plugins/shapeBuilder';
import ShapeProperty from '@/iscs_new/plugins/shapeProperty';
import { EventBus } from '@/scripts/event-bus';
import ShapeContextMenu from '@/iscs_new/plugins/shapeContextMenu';
import { mapGetters } from 'vuex';
export default {
data() {
return {
dataZoom: {
offsetX: '0',
offsetY: '0',
scaleRate: '1'
},
config: {
scaleRate: '1',
origin: {
x: 0,
y: 0
}
},
loading: false
};
},
computed: {
...mapGetters('iscs', [
'iscs'
]),
iscsId() {
return ['iscs', (Math.random().toFixed(5)) * 100000].join('_');
},
width() {
return document.documentElement.clientWidth;
},
height() {
return document.documentElement.clientHeight;
}
},
watch: {
'$store.state.config.canvasSizeCount': function (val) {
this.resize();
},
'$store.state.socket.equipmentStatus': function (val) {
if (val.length) {
this.stateMessage(val);
}
}
},
mounted() {
this.init();
},
beforeDestroy() {
this.destroy();
},
methods: {
//
init() {
document.getElementById(this.iscsId).oncontextmenu = function (e) {
return false;
};
this.$iscs = new Iscs({
dom: document.getElementById(this.iscsId),
draw: true,
config: {
renderer: 'canvas',
width: this.width,
height: this.height
},
options: {
scaleRate: 1,
offsetX: 0,
offsetY: 0
},
plugins: [
ShapeBuilder,
// ShapeProperty,
ShapeContextMenu
]
});
const option = {
panEnable: true,
zoomEnable: true,
keyEnable: true,
draggle: true,
selecting: true,
selectable: true,
reflect: true
};
if (this.$route.query.id) {
setTimeout(_ => {
Idb.select('composeTemplateList', this.$route.query.id).then(resp => {
this.$iscs.setMap([], {
elementList: resp.shapeList || [],
composeList: resp.composeList || []
}, option);
EventBus.$emit('getComposeElemList');
this.$emit('setStateList', resp.stateList);
}).catch(error => {
this.$iscs.setMap([], {
elementList: [],
composeList: []
}, option);
});
}, 1000);
} else {
this.$iscs.setMap([], {
elementList: [],
composeList: []
}, option);
}
Vue.prototype.$iscs = this.$iscs;
this.$iscs.on('viewLoaded', this.onViewLoaded, this);
this.$iscs.on('contextmenu', this.onContextMenu, this);
this.$iscs.on('click', this.onClick, this);
this.$iscs.on('reflect', this.onReflect, this);
this.$iscs.on('keyboard', this.onKeyboard, this);
window.document.oncontextmenu = function () {
return false;
};
},
//
onViewLoaded(e) {
},
//
onKeyboard(hook) {
console.log(hook);
},
//
onClick(em = {}) {
this.$emit('selected', em);
},
onReflect(em = {}) {
this.$emit('selected', this.$iscs.getShapeByCode(em.code));
},
//
onContextMenu(em = {}) {
this.$emit('contextMenu', em.model);
},
//
doAction(list) {
this.$iscs && this.$iscs.render(list);
},
//
stateMessage(val) {
this.$iscs && this.$iscs.setDeviceStatus(val);
},
//
resize() {
this.$nextTick(() => {
this.$iscs && this.$iscs.resize({ width: this.width, height: this.height });
});
},
//
destroy() {
if (this.$iscs) {
this.$iscs.destroy();
this.$iscs = null;
Vue.prototype.$iscs = null;
}
}
}
};
</script>
<style rel="stylesheet/scss" lang="scss" scoped>
.iscs-button{
position: absolute;
float: right;
right: 20px;
bottom: 15px;
}
.iscs-canvas{
}
</style>

View File

@ -0,0 +1,153 @@
<template>
<div>
<el-button type="primary" size="small" class="addComposeStatus" @click="addComposeStatus">添加</el-button>
<el-form ref="form" class="composeStatusForm" :model="formModel">
<!-- label-width="110px" -->
<el-table
:data="formModel.stateList"
stripe
class="composeStatusTable"
size="mini"
row-key="id"
:expand-row-keys="expandKeys"
@expand-change="expandChange"
>
<!-- covertStatusList: -->
<el-table-column label="状态" width="98">
<template slot-scope="scope">
<el-form-item
:prop="'stateList.'+scope.$index+'.status'"
:rules="[{required: true, message:'请输入状态', trigger: 'blur'}]"
>
<el-input v-model="scope.row.status" size="mini" type="text" style="width:88px" :maxlength="100" />
</el-form-item>
</template>
</el-table-column>
<el-table-column label="描述" width="100">
<template slot-scope="scope">
<el-form-item
:prop="'stateList.'+scope.$index+'.description'"
:rules="[{required: true, message:'请输入描述', trigger: 'blur'}]"
>
<el-input v-model="scope.row.description" size="mini" type="text" style="width:90px" :maxlength="100" />
</el-form-item>
</template>
</el-table-column>
<el-table-column label="权重" width="120">
<template slot-scope="scope">
<el-form-item
:prop="'stateList.'+scope.$index+'.weight'"
:rules="[{required: true, message:'请输入权重', trigger: 'blur'}]"
>
<el-input-number
v-model="scope.row.weight"
size="mini"
style="width:110px"
:step="1"
:min="1"
:max="100"
:precision="0"
/>
</el-form-item>
</template>
</el-table-column>
<el-table-column label="是否可重置" width="80">
<template slot-scope="scope">
<el-form-item :prop="'stateList.'+scope.$index+'.needDefault'">
<el-switch
v-model="scope.row.needDefault"
size="small"
active-color="#409eff"
inactive-color="#dcdfe6"
/>
</el-form-item>
</template>
</el-table-column>
<el-table-column label="操作" width="115">
<template slot-scope="scope">
<el-button type="success" size="mini" @click="modifyStatus(scope.$index,scope.row)">编辑状态组合</el-button>
<el-button type="danger" size="mini" style="margin-top:5px;" @click="deleteStatus(scope.$index,scope.row)">删除</el-button>
</template>
</el-table-column>
</el-table>
</el-form>
</div>
</template>
<script>
export default {
name:'StatusCombineEdit',
props:{
stateList: {
type: Array,
required: true
}
},
data() {
return {
expandKeys:[],
formModel:{
stateList:this.stateList
}
};
},
mounted() {
this.init();
},
methods:{
init() {
},
expandChange(row, expandedRows) {
},
addComposeStatus() {
const length = this.formModel.stateList.length;
this.formModel.stateList.push({id:length + 1, status:'', description:'', weight:1, needDefault:false,
covertStatusList:[]});
// defaultLoop:false
this.expandKeys.push(length + 1);
},
deleteStatus(index, row) {
this.formModel.stateList.splice(index, 1);
const key = this.expandKeys.findIndex(each=>{ return each == row.id; });
if (key > 0) { this.expandKeys.splice(key, 1); }
},
modifyStatus(index, row) {
this.$refs.eachStatusTable.doShow(index, row);
}
// addCovertStatus(index) {
// if (this.formModel.stateList[index].defaultLoop) {
// const covertStatusList = this.formModel.stateList[index].covertStatusList;
// const length = covertStatusList.length;
// covertStatusList.push({id:length + 1, loop:this.formModel.stateList[index].defaultLoop,
// delay:2, animateTime:10, frameList:[]});
// // ,times:1,
// // this.formModel.stateList[index].covertStatusList = covertStatusList;
// this.$set(this.formModel.stateList[index], 'covertStatusList', covertStatusList);
// } else {
// this.formModel.stateList[index].covertStatusList.push({id:length + 1, loop:this.formModel.stateList[index].defaultLoop,
// frameList:[]});
// }
// }
}
};
</script>
<style lang="scss" scoped>
.combineStatusListName{
font-size: 14px;
width: 200px;
background: #fff;
margin-bottom:10px;
}
.addComposeStatus{
float: right;
display: inline-block;
margin:10px 10px 10px 0px;
}
.composeStatusTable{}
.composeStatusForm{padding:15px;}
.composeStatusTable .eachStatusTable{margin-top:10px;}
</style>
<style lang="sass">
.composeStatusTable .el-table__expanded-cell{padding: 20px;}
</style>

View File

@ -0,0 +1,370 @@
<template>
<el-dialog
v-dialogDrag
class="composeStatusList"
title="状态编辑"
:visible.sync="show"
width="800px"
:before-close="doClose"
:close-on-click-modal="false"
append-to-body
>
<el-button type="primary" size="small" class="addComposeStatus" @click="addComposeStatus">添加</el-button>
<el-form ref="form" class="composeStatusForm" :model="formModel">
<el-table
:data="formModel.stateList"
stripe
border
class="composeStatusTable"
height="360"
size="mini"
row-key="id"
>
<el-table-column label="状态" width="169">
<template slot-scope="scope">
<el-form-item
:prop="'stateList.'+scope.$index+'.status'"
:rules="[{required: true, message:'请输入状态', trigger: 'blur'}]"
>
<el-input v-model="scope.row.status" size="mini" type="text" style="width:149px" :maxlength="100" />
</el-form-item>
</template>
</el-table-column>
<el-table-column label="描述" width="170">
<template slot-scope="scope">
<el-form-item
:prop="'stateList.'+scope.$index+'.description'"
:rules="[{required: true, message:'请输入描述', trigger: 'blur'}]"
>
<el-input v-model="scope.row.description" size="mini" type="text" style="width:150px" :maxlength="100" />
</el-form-item>
</template>
</el-table-column>
<el-table-column label="权重" width="120">
<template slot-scope="scope">
<el-form-item
:prop="'stateList.'+scope.$index+'.weight'"
:rules="[{required: true, message:'请输入权重', trigger: 'blur'}]"
>
<el-input-number v-model="scope.row.weight" size="mini" style="width:100px" :step="1" :min="1" :max="100" :precision="0" />
</el-form-item>
</template>
</el-table-column>
<el-table-column label="是否可重置" width="100">
<template slot-scope="scope">
<el-form-item :prop="'stateList.'+scope.$index+'.needDefault'">
<el-switch
v-model="scope.row.needDefault"
size="small"
active-color="#409eff"
inactive-color="#dcdfe6"
/>
</el-form-item>
</template>
</el-table-column>
<el-table-column label="操作" width="200">
<template slot-scope="scope">
<el-button type="success" size="mini" @click="modifyStatus(scope.$index,scope.row)">编辑状态组合</el-button>
<el-button type="danger" size="mini" style="margin-top:5px;" @click="deleteStatus(scope.$index,scope.row)">删除</el-button>
<!-- <el-button type="primary" size="mini" style="margin-top:5px;" @click="previewStatus(scope.$index,scope.row)">预览</el-button> -->
</template>
</el-table-column>
</el-table>
</el-form>
<div class="bottomBtnGroup">
<el-button type="primary" size="medium" @click="onEditStatusSave">保存</el-button>
</div>
<el-dialog
v-dialogDrag
class="composeStatus"
title="状态组合"
:visible.sync="innerShow"
width="750px"
:before-close="doInnerClose"
:close-on-click-modal="false"
append-to-body
>
<div class="loopTitle">是否动画</div>
<el-select v-model="defaultLoop" size="mini">
<el-option label="是" :value="true" />
<el-option label="否" :value="false" />
</el-select>
<el-button type="primary" size="mini" class="addStyle" @click="addCovertStatus">添加</el-button>
<el-form ref="Innerform" class="composeStatusForm" :model="statusModel">
<el-table
:data="statusModel.covertStatusList"
stripe
border
style="margin-top:10px;"
class="eachStatusTable"
size="mini"
row-key="id"
>
<el-table-column label="是否动画" width="100" align="center">
<template slot-scope="scope">
<!-- <el-form-item prop="loop"> -->
<div style="">{{ scope.row.loop?'是':'否' }}</div>
<!-- </el-form-item> -->
</template>
</el-table-column>
<el-table-column label="动画时间" width="120" align="center">
<template slot-scope="scope">
<el-form-item v-if="scope.row.loop" :prop="'covertStatusList.'+scope.$index+'.animateTime'" :rules="[{required: true, message:'请输入动画时间', trigger: 'blur'}]">
<el-input-number v-model="scope.row.animateTime" size="mini" style="width:100px" :step="1" :min="1" :max="100" :precision="0" />
</el-form-item>
<div v-else></div>
</template>
</el-table-column>
<el-table-column label="动画延时" width="120" align="center">
<template slot-scope="scope">
<el-form-item
v-if="scope.row.loop"
:prop="'covertStatusList.'+scope.$index+'.delay'"
:rules="[{required: true, message:'请输入动画延时', trigger: 'blur'}]"
>
<el-input-number v-model="scope.row.delay" size="mini" style="width:100px" :step="1" :min="1" :max="100" :precision="0" />
</el-form-item>
<div v-else></div>
</template>
</el-table-column>
<el-table-column label="状态组合" width="319">
<template slot-scope="scope">
<!-- {{ scope.row.frameList }} -->
<!-- <div v-if="scope.row.frameList.length<=0"> -->
<!-- 元素 -->
<!-- <div /> -->
<div v-if="scope.row.loop" style="margin-bottom:10px">
<!-- <div style="display:inline-block">当前动画帧{{ scope.row.frameId }}</div> -->
<el-button type="primary" size="mini" class="addStyle" @click="addElementFrameId(scope.$index,scope.row.frameId)">添加帧</el-button>
<!-- <el-input-number
v-model="scope.row.frameId"
:min="1"
:max="10"
size="mini"
:step="1"
:precision="0"
/> -->
</div>
<el-select
v-model="scope.row.selectedElement"
size="mini"
style="width:115px;display:inline-block"
placeholder="请选择元素"
@change="changeCurrentStatus(scope.$index,scope.row.selectedElement)"
>
<el-option v-for="(element,index) in elementList" :key="index" :label="element.name" :value="element.name" />
</el-select>
<el-select v-model="scope.row.selectedElementStatus" size="mini" placeholder="请选择状态" style="width:115px;display:inline-block">
<el-option v-for="(status,index) in scope.row.statusList" :key="index" :label="status.name" :value="status.name" />
</el-select>
<el-button type="primary" size="mini" class="addStyle" @click="addElementStatus(scope.$index,scope.row.loop)">添加</el-button>
<div v-if="scope.row.loop">
<div v-for="(frame,index) in scope.row.frameList" :key="index">
{{ index+1 }}
<span v-for="(frameIn,indexIn) in frame" :key="indexIn">
元素名称{{ frameIn.name }} 状态{{ frameIn.status }}
</span>
</div>
</div>
<div v-else>
<div v-for="(frame,index) in scope.row.frameList" :key="index">
元素名称{{ frame.name }} 状态{{ frame.status }}
</div>
</div>
<!-- </div> -->
</template>
</el-table-column>
<el-table-column label="操作" width="50">
<template slot-scope="scope">
<el-button type="danger" icon="el-icon-delete" circle size="mini" @click="deleteCovertStatus(scope.$index,scope.row)" />
</template>
</el-table-column>
</el-table>
</el-form>
<div class="bottomBtnGroup">
<el-button type="primary" size="medium" @click="onEditComposeStatus">保存</el-button>
</div>
</el-dialog>
</el-dialog>
</template>
<script>
import { deepAssign } from '@/utils/index';
import Idb from '../utils/indexedDb.js';
export default {
name:'StatusList',
data() {
return {
show:false,
defaultLoop:false,
// covertStatusList:[],
innerShow:false,
formModel:{
stateList:[]
},
statusModel:{
covertStatusList:[]
},
elementList:[],
currentIndex:0,
model:{}
// statusList:[]
};
},
methods:{
doClose() {
this.show = false;
},
doShow() {
this.formModel.stateList = [];
this.show = true;
Idb.select('composeTemplateList', this.$route.query.id).then(resp => {
this.formModel.stateList = resp.stateList;
this.model = resp;
}).catch(() => {
});
},
addComposeStatus() {
const length = this.formModel.stateList.length;
this.formModel.stateList.push({id:length + 1, status:'', description:'', weight:1, needDefault:false,
covertStatusList:[]});
},
deleteStatus(index, row) {
this.formModel.stateList.splice(index, 1);
},
onEditStatusSave() {
const that = this;
that.$refs['form'].validate((valid) => {
if (valid) {
// that.formModel;
// // const id = this.$route.query.id;
this.model.stateList = that.formModel.stateList;
Idb.delete('composeTemplateList', this.model.id);
Idb.write('composeTemplateList', this.model);
that.show = false;
this.$emit('setStateList', that.formModel.stateList);
}
});
},
modifyStatus(index, row) {
this.currentIndex = index;
this.innerShow = true;
this.statusModel.covertStatusList = Object.values(row.covertStatusList);
const source = this.$iscs.getSource();
const composeElemList = source.elementList;
this.elementList = [];
composeElemList.forEach(each=>{
this.elementList.push({name:each.name, stateList:each.stateList});
});
},
doInnerClose() {
this.innerShow = false;
},
addCovertStatus() {
if (this.defaultLoop) {
this.statusModel.covertStatusList.push({loop:this.defaultLoop, delay:2, animateTime:10, frameList:[], statusList:[], selectedElement:'', selectedElementStatus:'', frameId:null });
} else {
this.statusModel.covertStatusList.push({loop:this.defaultLoop, frameList:[], statusList:[], selectedElementStatus:'', selectedElement:'' });
}
},
onEditComposeStatus() {
this.innerShow = false;
this.statusModel.covertStatusList.forEach(covert=>{
delete covert.selectedElement;
delete covert.selectedElementStatus;
delete covert.statusList;
delete covert.frameId;
});
this.formModel.stateList[this.currentIndex].covertStatusList = deepAssign({}, this.statusModel.covertStatusList);
},
deleteCovertStatus(index, row) {
this.statusModel.covertStatusList.splice(index, 1);
},
addElementFrameId(index, frameId) {
if (frameId) {
this.statusModel.covertStatusList[index].frameId = frameId + 1;
} else {
this.statusModel.covertStatusList[index].frameId = 1;
}
this.statusModel.covertStatusList[index].frameList.push([]);
},
addElementStatus(index, loop) {
const frameList = this.statusModel.covertStatusList[index].frameList || [];
const temp = this.statusModel.covertStatusList[index];
if (loop) {
if (temp.selectedElement && temp.selectedElementStatus && temp.frameId) {
const curFrame = frameList[temp.frameId];
let curFrameIndex = -1;
if (curFrame && curFrame.length > 0) {
curFrameIndex = curFrame.findIndex(frame=>{
return frame.name == this.statusModel.covertStatusList[index].selectedElement &&
frame.status == this.statusModel.covertStatusList[index].selectedElementStatus;
});
}
if (curFrameIndex < 0) {
// this.statusModel.covertStatusList[index].frameList.push([]);
this.statusModel.covertStatusList[index].frameList[temp.frameId - 1].push({name:this.statusModel.covertStatusList[index].selectedElement,
status:this.statusModel.covertStatusList[index].selectedElementStatus});
}
}
} else {
if (temp.selectedElement && temp.selectedElementStatus) {
const indexFrame = frameList.findIndex(frame=>{
return frame.name == this.statusModel.covertStatusList[index].selectedElement &&
frame.status == this.statusModel.covertStatusList[index].selectedElementStatus;
});
if (indexFrame < 0) {
this.statusModel.covertStatusList[index].frameList.push({name:this.statusModel.covertStatusList[index].selectedElement,
status:this.statusModel.covertStatusList[index].selectedElementStatus});
}
}
}
this.statusModel.covertStatusList[index].selectedElementStatus = '';
this.statusModel.covertStatusList[index].selectedElement = '';
},
changeCurrentStatus(index, selectedElement) {
this.statusModel.covertStatusList[index].selectedElementStatus = '';
this.statusModel.covertStatusList[index].statusList = [];
const list = this.elementList.find(each=>{ return each.name == selectedElement; });
if (list && list.stateList && list.stateList.length > 0) {
const temp = list.stateList;
temp.forEach(each=>{
this.statusModel.covertStatusList[index].statusList.push({name:each.status});
});
}
}
// previewStatus(index, row) {
// // const that = this;
// // if(needDefault)
// const list = Object.values(row.covertStatusList);
// list.forEach(each=>{
// if (each.loop) {
// } else {
// const frameList = each.frameList;
// frameList.forEach(frame=>{
// // frame.name;
// // frame.status;
// // this.$iscs.
// });
// }
// });
// // this.show = false;
// }
}
};
</script>
<style lang="scss" scoped>
.composeStatusTable{margin-top:10px;}
.bottomBtnGroup{
width: 100%;
margin-top:10px;
text-align: center;
display: inline-block
}
.loopTitle{display:inline-block;}
</style>
<style lang="scss">
.composeStatusList .el-dialog__body,.composeStatus .el-dialog__body{padding: 15px 20px;}
</style>

View File

@ -0,0 +1,192 @@
<template>
<transition name="el-zoom-in-center">
<div class="mapPaint">
<div class="map-view">
<iscs-canvas ref="iscsCanvas" @selected="onSelected" @setData="setData" />
</div>
<div class="right-card">
<!-- :class="{'hide': draftShow}" -->
<el-card type="border-card" class="heightClass">
<div slot="header" class="clearfix">
<!-- 组件id为{{ $route.query.id }} -->
状态预览
<!-- -->
<el-button
type="text"
style="float: right; padding: 3px 0; margin-right: 5px;"
@click="resetDefaultStatus"
>重置状态</el-button>
</div>
<div class="stateList">
<!-- <el-card style="margin-top:10px;height:100%"> -->
<div v-for="(state,index) in stateList" :key="index" class="eachStatus">
<div>状态属性{{ state.status }}</div>
<div>状态描述{{ state.description }}</div>
<div>状态权重{{ state.weight }}</div>
<div>状态是否可以初始化{{ state.needDefault?'是':'否' }}</div>
<el-button type="primary" size="mini" style="margin-top:5px;" @click="previewStatus(state)">预览</el-button>
</div>
<!-- </el-card> -->
<!-- <el-tabs v-model="statusTab" class="card" type="card" @tab-click="onSelectTab"> -->
<!-- <el-tab-pane v-for="(composeElem,index) in composeElemList" :key="index" :label="composeElem.name" :name="composeElem.code" :lazy="true"> -->
<!-- <table-form :ref="'tableform'+composeElem.code" :compose-elem="composeElem" /> -->
<!-- stateList -->
<!-- </el-tab-pane> -->
<!-- </el-tabs> -->
</div>
</el-card>
</div>
</div>
</transition>
</template>
<script>
import * as utils from '@/iscs_new/utils/utils';
import iscsCanvas from './iscsCanvas';
import orders from '@/iscs_new/utils/orders';
import shapeType from '@/iscs_new/constant/shapeType.js';
export default {
name:'IscsPreview',
components: {
iscsCanvas
},
data() {
return {
draftShow: false,
selected: null,
statusTab:'',
stateList:[],
elementList:[],
type:''
};
},
mounted() {
},
methods:{
onSelectTab() {
},
setData(data) {
this.stateList = data.stateList;
this.elementList = utils.deepClone(data.shapeList);
this.type = data.type;
},
previewStatus(data) {
// this.resetDefaultStatus();
// covertStatusList: Object
// description: ""
// id: 1
// needDefault: true
// status: "close"
// weight: 1
this.$iscs.update([
{ status: data.status, code: '100', type: this.type }
]);
// const that = this;
// const list = Object.values(data.covertStatusList);
// list.forEach(each=>{
// debugger;
// if (each.loop) {
// // ChangeStatus
// } else {
// const frameList = each.frameList;
// // <>
// // this.$iscs.update([
// // { status: 's1', code: '100', type: 'Device' },
// // { status: 's2', code: '101', type: 'Device' }
// // ]);
// frameList.forEach(frame=>{
// const element = that.elementList.find(ele=>{ return ele.name == frame.name; });
// if (element) {
// const elementStyle = element.stateList.find(state=>{ return state.status == frame.status; });
// if (elementStyle) {
// const model = utils.deepClone(element);
// // this.$iscs.update([
// // { status: 's1', code: '100', type: 'Device' },
// // { status: 's2', code: '101', type: 'Device' }
// // ]);
// // model.frameList = [];
// // const style = elementStyle.style || {};
// // // model.changeStyle =
// // if (style && JSON.stringify(style) != '{}') {
// // const keys = Object.keys(style);
// // keys.forEach(eachKey=>{
// // model.style[eachKey] = style[eachKey];
// // });
// // }
// // const shape = elementStyle.shape || {};
// // if (shape && JSON.stringify(shape) != '{}') {
// // const keys = Object.keys(shape);
// // keys.forEach(eachShape=>{
// // model.shape[eachShape] = style[eachShape];
// // });
// // }
// // { status: 's1', code: '100', type: 'Device' },
// // this.$refs.iscsCanvas.doAction([{model, action: {shapeType: shapeType.Element, order: orders.Update}}]);
// this.$refs.iscsCanvas.doAction([{model, action: {shapeType: shapeType.Element, order: orders.ChangeStatus}}]);
// }
// }
// });
// }
// });
},
resetDefaultStatus() {
this.elementList.forEach(element=>{
const model = utils.deepClone(element);
this.$refs.iscsCanvas.doAction([{model, action: {shapeType: shapeType.Element, order: orders.Update}}]);
// this.$refs.iscsCanvas.doAction([{model, action: {shapeType: shapeType.Element, order: orders.ResetStatus}}]);
});
},
// getComposeElemList() {
// const source = this.$iscs.getSource();
// if (source &&
// source.elementList &&
// source.elementList.length) {
// this.composeElemList = source.elementList;
// this.statusTab = this.composeElemList[0].code;
// } else {
// this.composeElemList = [];
// }
// },
onSelected(em) {
}
}
};
</script>
<style lang="scss" scoped>
.mapPaint{
height: 100%;
overflow: hidden;
width:100%;
position:absolute;
left:0;top:0;
}
.right-card{
width: 500px;
height: 100%;
position: absolute;
right: 0;
top:0;
transition: all 0.5s;
background: #fff;
z-index: 9;
}
.heightClass{height:100%;overflow:hidden;display:flex;width: 100%;flex-direction: column;}
.eachStatus{
padding: 15px 30px;
border-bottom: 1px #ccc solid;
line-height: 150%;
}
.statelist{
height: 100%;
overflow: auto;
}
</style>
<style lang="scss">
.heightClass .el-card__body{
flex:1;
height: 100%;
overflow: hidden;
}
</style>

View File

@ -0,0 +1,185 @@
<template>
<div>
<div :id="iscsId" v-loading="loading" :style="{ width: width +'px', height: height +'px',background:'#425a74' }" class="iscs-canvas" />
</div>
</template>
<script>
import Vue from 'vue';
import Iscs from '@/iscs_new/map';
import Idb from '../utils/indexedDb.js';
import ShapeBuilder from '@/iscs_new/plugins/shapeBuilder';
import ShapeProperty from '@/iscs_new/plugins/shapeProperty';
import { EventBus } from '@/scripts/event-bus';
import ShapeContextMenu from '@/iscs_new/plugins/shapeContextMenu';
import { mapGetters } from 'vuex';
export default {
data() {
return {
dataZoom: {
offsetX: '0',
offsetY: '0',
scaleRate: '1'
},
config: {
scaleRate: '1',
origin: {
x: 0,
y: 0
}
},
loading: false
};
},
computed: {
...mapGetters('iscs', [
'iscs'
]),
iscsId() {
return ['iscs', (Math.random().toFixed(5)) * 100000].join('_');
},
width() {
return document.documentElement.clientWidth;
},
height() {
return document.documentElement.clientHeight;
}
},
watch: {
'$store.state.config.canvasSizeCount': function (val) {
this.resize();
},
'$store.state.socket.equipmentStatus': function (val) {
if (val.length) {
this.stateMessage(val);
}
}
},
mounted() {
this.init();
},
beforeDestroy() {
this.destroy();
},
methods: {
//
init() {
document.getElementById(this.iscsId).oncontextmenu = function (e) {
return false;
};
this.$iscs = new Iscs({
dom: document.getElementById(this.iscsId),
draw: true,
config: {
renderer: 'canvas',
width: this.width,
height: this.height
},
options: {
scaleRate: 1,
offsetX: 0,
offsetY: 0
},
plugins: [
// ShapeBuilder,
// ShapeProperty,
// ShapeContextMenu
]
});
const option = {
panEnable: true,
zoomEnable: true,
keyEnable: true,
draggle: false,
selecting: false,
selectable: false,
reflect: true
};
if (this.$route.query.id) {
setTimeout(_ => {
Idb.select('composeTemplateList', this.$route.query.id).then(resp => {
this.$iscs.setMap([resp], {
elementList: resp.shapeList || [],
composeList: resp.composeList || []
}, option);
this.$emit('setData', resp);
}).catch(error => {
this.$iscs.setMap([], {
elementList: [],
composeList: []
}, option);
});
}, 1000);
} else {
this.$iscs.setMap([], {
elementList: [],
composeList: []
}, option);
}
Vue.prototype.$iscs = this.$iscs;
this.$iscs.on('viewLoaded', this.onViewLoaded, this);
this.$iscs.on('contextmenu', this.onContextMenu, this);
// this.$iscs.on('click', this.onClick, this);
this.$iscs.on('reflect', this.onReflect, this);
this.$iscs.on('keyboard', this.onKeyboard, this);
window.document.oncontextmenu = function () {
return false;
};
},
//
onViewLoaded(e) {
},
//
onKeyboard(hook) {
console.log(hook);
},
//
onClick(em = {}) {
this.$emit('selected', em);
},
onReflect(em = {}) {
this.$emit('selected', this.$iscs.getShapeByCode(em.code));
},
//
onContextMenu(em = {}) {
this.$emit('contextMenu', em.model);
},
//
doAction(list) {
this.$iscs && this.$iscs.render(list);
},
//
stateMessage(val) {
this.$iscs && this.$iscs.setDeviceStatus(val);
},
//
resize() {
this.$nextTick(() => {
this.$iscs && this.$iscs.resize({ width: this.width, height: this.height });
});
},
//
destroy() {
if (this.$iscs) {
this.$iscs.destroy();
this.$iscs = null;
Vue.prototype.$iscs = null;
}
}
}
};
</script>
<style rel="stylesheet/scss" lang="scss" scoped>
.iscs-button{
position: absolute;
float: right;
right: 20px;
bottom: 15px;
}
.iscs-canvas{
}
</style>

View File

@ -0,0 +1,140 @@
import { getBaseUrl } from '@/utils/baseUrl'
let db = null;
class IndexedDb {
constructor(nameList) {
this.open(nameList);
}
open(nameList) {
const baseUrl = getBaseUrl();
const indexedDBName = baseUrl.replace(/http.?:\/\/(.*)[\/|:].*/, "$1");
const request = window.indexedDB.open(indexedDBName, 1);
request.onerror = function (e) {
console.log('数据库打开报错');
};
request.onsuccess = function (e) {
db = request.result;
console.log('数据库打开成功');
};
request.onupgradeneeded = function (e) {
db = e.target.result;
nameList.forEach(name => {
db.createObjectStore(name, { keyPath: 'id' });
console.log(name);
})
};
}
write(tableName, data) {
return new Promise((resolve, reject) => {
if (db) {
const request = db.transaction([tableName], 'readwrite').objectStore(tableName).add(data);
request.onsuccess = function(e) {
console.log('数据写入成功');
resolve(request.result);
};
request.onerror = function(e) {
console.log('数据写入失败');
reject(e);
};
} else {
this.open();
reject({message: '数据库未打开!'});
}
});
}
delete(tableName, key) {
return new Promise((resolve, reject) => {
if (db) {
const request = db.transaction([tableName], 'readwrite').objectStore(tableName).delete(key);
request.onsuccess = function(event) {
console.log('数据删除成功');
resolve(request.result);
};
request.onerror = function(event) {
console.log('数据删除失败');
reject(event);
};
} else {
this.open();
reject({message: '数据库未打开!'});
}
});
}
modify(tableName, data) {
return new Promise((resolve, reject) => {
if (db) {
const request = db.transaction([tableName], 'readwrite').objectStore(tableName).put(data);
request.onsuccess = function(e) {
console.log('数据修改成功');
resolve(request.result);
};
request.onerror = function(e) {
console.log('数据修改失败');
reject(e);
};
} else {
this.open();
reject({message: '数据库未打开!'});
}
});
}
select(tableName, key) {
return new Promise((resolve, reject) => {
if (db) {
const request = db.transaction([tableName]).objectStore(tableName).get(key);
request.onsuccess = function(e) {
console.log('数据查询成功');
resolve(request.result);
};
request.onerror = function(e) {
console.log('数据查询失败');
reject(e);
};
} else {
this.open();
reject({message: '数据库未打开!'});
}
});
}
list(tableName) {
return new Promise((resolve, reject) => {
if (db) {
const store = db.transaction([tableName]).objectStore(tableName);
var request = store.openCursor();
var list = [];
request.onsuccess = function(e) {
var c = e.target.result;
if (c) {
list.push(c.value);
c.continue();
} else {
resolve(list);
}
};
request.onerror = function(e) {
console.log('数据读取失败');
reject(e);
};
} else {
this.open();
reject({message: '数据库未打开!'});
}
});
}
destroy() {
}
}
export default new IndexedDb(['composeList']);

View File

@ -0,0 +1,284 @@
<template>
<div class="addRules">
<div class="addRulesInner">
<div class="addRulesInnerTitle">新增故障</div>
<div class="closeAddRulesInner" @click="addRulesClose">
<span class="el-icon-close closeAddRulesIn" />
</div>
<el-form ref="form" :model="faultRule" label-width="100px" style="margin-left:15px;">
<el-form-item label="目标设备">
<!-- faultRule.targetDeviceCode -->
<el-input v-model="targetDevice.name" disabled size="small" class="inputModelClass" />
<!--<el-button :type="field === 'targetActive' ? 'danger' : 'primary'" size="small" @click="hover('targetActive')">{{ $t('map.activate') }}</el-button>-->
</el-form-item>
<el-form-item label="触发方式">
<el-select v-model="triggerMode" @change="changeTriggerMode">
<el-option
v-for="item in triggerModeList"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</el-form-item>
<el-form-item label="故障类型">
<el-select v-model="faultRule.faultType" placeholder="请选择" class="inputModelClass">
<el-option
v-for="item in faultTypeList"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</el-form-item>
<el-form-item v-if="triggerMode === 'DEVICE'" label="触发设备">
<!-- faultRule.condition.triggerDeviceCode -->
<el-input v-model="triggerDevice" size="small" disabled class="inputModelClass" />
<el-button :type="field === 'triggerActive' ? 'danger' : 'primary'" size="small" @click="hover('triggerActive')">{{ $t('map.activate') }}</el-button>
</el-form-item>
<el-form-item v-if="triggerMode === 'DEVICE'" label="触发状态">
<!-- <el-input v-model="faultRule.condition.triggerDeviceStatus" size="small" class="inputModelClass" /> -->
<el-select v-model="faultRule.condition.triggerDeviceStatus" placeholder="请选择" class="inputModelClass">
<el-option
v-for="item in triggerStatusList"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</el-form-item>
<el-form-item v-if="triggerMode === 'DEVICE'" label="关联设备">
<el-input v-model="triggerAssociatedDevice" size="small" disabled class="inputModelClass" />
<el-button :type="field === 'triggerAssociated'? 'danger': 'primary'" size="small" @click="hover('triggerAssociated')">{{ $t('map.activate') }}</el-button>
</el-form-item>
<el-form-item v-if="triggerMode === 'TIME'" label="触发时间">
<el-date-picker
v-model="faultRule.condition.triggeringTime"
type="datetime"
placeholder="选择日期时间"
/>
</el-form-item>
</el-form>
<span class="addRulesFooter">
<el-button size="medium" type="primary" @click="addRulesCreate">{{ $t('global.confirm') }}</el-button>
<el-button size="medium" @click="addRulesClose">{{ $t('global.cancel') }}</el-button>
</span>
</div>
</div>
</template>
<script>
import { FaultStatusEnum } from '@/scripts/FaultDicNew';
import { setFailureModeNew} from '@/api/simulation';
import { deviceFaultType} from '@/scripts/cmdPlugin/Config';
//
export default {
name: 'AddChoose',
props: {
targetDevice: {
type: Object,
required: true
}
},
data() {
return {
field:'',
triggerStatusList:[],
faultTypeList:[],
faultRule:{
targetDeviceCode:'',
targetDeviceType:'',
faultType:'',
condition:{
triggerDeviceCode:'',
triggerDeviceStatus:'',
triggerDeviceType :'',
type:'DEVICE',
triggeringTime: '',
triggerAssociatedDeviceCode: ''
}
},
// targetDevice:'',
triggerDevice:'',
triggerAssociatedDevice: '',
triggerMode: 'DEVICE',
triggerModeList: [
{label: '设备触发', value: 'DEVICE'},
{label: '时间触发', value: 'TIME'}
]
// rules:{
// targetDevice:[
// { required: true, message:'', trigger: 'blur' }
// ],
// triggerDevice:[
// { required: true, message:'', trigger: 'blur' }
// ],
// faultRule:{
// faultType:[
// { required: true, message:'', trigger: 'blur' },
// { required: true, message:'', trigger: 'change' }
// ]
// }
// }
};
},
watch: {
'$store.state.menuOperation.selectedCount':function(em) {
const device = this.$store.state.menuOperation.selected;
if (device.code) {
this.deviceSelect(device);
}
}
},
mounted() {
},
methods:{
deviceSelect(em) {
if (this.field.toUpperCase() === 'triggerAssociated'.toUpperCase()) {
// if (em._type == 'Station') {
// em = this.$store.getters['map/getDeviceByCode'](em.zcCode);
// }
// this.faultRule.targetDeviceType = this.covertType(em._type);
// this.faultRule.targetDeviceCode = em.code;
if (em._type.toUpperCase() === 'Section'.toUpperCase() && em.parentName) {
this.triggerAssociatedDevice = em._type + '-' + em.parentName + '-' + em.name;
} else {
this.triggerAssociatedDevice = em._type + '-' + em.name;
}
this.faultRule.condition.triggerAssociatedDeviceCode = em.code;
} else if (this.field.toUpperCase() === 'triggerActive'.toUpperCase()) {
this.faultRule.condition.triggerDeviceType = this.covertType(em._type);
this.faultRule.condition.triggerDeviceCode = em.code;
if (em._type.toUpperCase() === 'Section'.toUpperCase() && em.parentName) {
this.triggerDevice = em._type + '-' + em.parentName + '-' + em.name;
} else if (em._type.toUpperCase() === 'Train'.toUpperCase()) {
this.triggerDevice = em._type + '-' + em.code;
} else {
this.triggerDevice = em._type + '-' + em.name;
}
this.triggerStatusList = [];
const faultStatus = FaultStatusEnum[this.faultRule.condition.triggerDeviceType];
if (faultStatus) {
const list = Object.keys(faultStatus);
list.forEach(key=>{
this.triggerStatusList.push({label:faultStatus[key], value:key});
});
}
this.field = '';
this.faultRule.condition.triggerDeviceStatus = '';
}
},
covertType(type) {
switch (type) {
case 'Section':return 'SECTION';
case 'Signal':return 'SIGNAL';
case 'Switch':return 'SWITCH';
case 'Station':return 'STATION';
case 'ZcControl':return 'ZC';
case 'StationStand':return 'STAND';
case 'Train':return 'TRAIN';
}
},
hover(field) {
if (this.field == '') {
this.field = field;
} else {
this.field = '';
}
},
addRulesCreate() {
if (this.triggerDevice == '') {
this.$messageBox('请选择触发设备');
return;
}
if (this.faultRule.condition.triggerDeviceStatus == '') {
this.$messageBox('请选择触发状态');
return;
}
if (this.faultRule.faultType == '') {
this.$messageBox('请选择故障类型');
return;
}
console.log(this.faultRule, '-------------');
setFailureModeNew(this.faultRule, this.$route.query.group).then(res=>{
this.$emit('closeAddRules');
this.$emit('reload');
}).catch((error)=>{
this.$messageBox('创建故障失败: ' + error.message);
});
},
addRulesClose() {
this.resetForm();
this.$emit('closeAddRules');
},
initValue() {
this.faultTypeList = deviceFaultType[this.targetDevice._type];
this.faultRule.targetDeviceCode = this.targetDevice.code;
this.faultRule.targetDeviceType = this.covertType(this.targetDevice._type);
},
resetForm() {
this.triggerDevice = '';
this.faultRule = {
targetDeviceCode:'',
targetDeviceType:'',
faultType:'',
condition:{
triggerDeviceCode:'',
triggerDeviceStatus:'',
triggerDeviceType :'',
type:'DEVICE',
triggeringTime: '',
triggerAssociatedDeviceCode: ''
}
};
}
}
};
</script>
<style lang="scss" scoped>
.addRules{
position: absolute;
width: 100%;
left: 0;
top: 40px;
}
.addRulesInner{
position: relative;
width: 380px;
margin-top:6px;
background: #fff;
border-radius: 10px;
border: 1px #ccc solid;
margin-left: auto;
margin-right: auto;
box-shadow: 3px 3px 3px #a0a0a0;
z-index:2;
}
.queryList{
height: 300px;
overflow: auto;
}
.inputModelClass{
width:150px;
}
.addRulesInnerTitle{
padding: 10px;
font-size: 14px;
}
.closeAddRulesInner{
position: absolute;
right: 10px;
top: 9px;
width: 19px;
height: 19px;
cursor: pointer;
}
.closeAddRulesIn{
font-size: 19px;
}
.addRulesFooter{
margin-left: 116px;
display: inline-block;
margin-bottom: 20px;
}
</style>

View File

@ -0,0 +1,196 @@
<template>
<div>
<el-drawer
title="设备管理"
:visible.sync="show"
direction="ltr"
:before-close="doClose"
custom-class="dialog_box"
size="43%"
>
<div style="margin-bottom: 3px; overflow: hidden; padding: 0 10px;">
<div class="plc_box">名称: {{ plcInfo.name }}</div>
<div class="plc_box">状态: <span :style="{'color': plcInfo.status ? 'green' : 'red'}">{{ plcInfo.status ? '在线' : '不在线' }}</span></div>
<el-button type="text" size="small" class="freshen_box" @click="getList">刷新</el-button>
</div>
<el-table :data="tableData" border style="width: 100%; max-height: calc(100% - 24px); overflow: auto;">
<el-table-column prop="code" label="设备编号" width="180" />
<el-table-column prop="typeName" label="设备类型" width="180" />
<el-table-column prop="vrDeviceCode" label="连接设备编码">
<template slot-scope="scope">
<div v-if="scope.row.vrDeviceCode" class="flex_box">{{ scope.row.vrDeviceName }}</div>
<div v-if="!scope.row.vrDeviceCode" class="flex_box">()</div>
</template>
</el-table-column>
<el-table-column label="操作">
<template slot-scope="scope">
<el-button :type="scope.row.buttonShowType ? 'danger' : 'primary'" size="small" @click="handleClick(scope.row, scope.$index)">连接</el-button>
<el-button size="small" @click="cancel(scope.row, scope.$index)">断开</el-button>
</template>
</el-table-column>
</el-table>
</el-drawer>
</div>
</template>
<script>
import { getAllSimulationList, postSimulationConnectById, putSimulationDisconnectById } from '@/api/simulation.js';
import ConstConfig from '@/scripts/ConstConfig';
import { EventBus } from '@/scripts/event-bus';
import { getSessionStorage } from '@/utils/auth';
export default {
name: 'Equipment',
data() {
return {
show: false,
group: '',
tableData: [],
typeList: ConstConfig.ConstSelect.projectDeviceTypeList,
plcInfo: {
name: '',
status: ''
},
index: 0,
selected: {},
typeObj: {
Section: '区段',
Signal: '信号机',
Switch: '道岔',
Psd: '屏蔽门',
StationStand: '站台'
}
};
},
computed: {
project() {
return getSessionStorage('project');
}
},
mounted() {
EventBus.$on('selectDevice', (data) => {
this.selected = data;
this.tableData[this.index]['buttonShowType'] = false;
this.tableData.splice(this.index, 1, this.tableData[this.index]);
this.$store.dispatch('map/selectDeviceCode', {flag: false, type: ''});
this.connect(this.tableData[this.index]);
});
},
async beforeDestroy() {
EventBus.$off('selectDevice');
},
methods: {
async doShow() {
this.show = true;
this.group = this.$route.query.group;
this.getList();
},
async getList() {
try {
const res = await getAllSimulationList(this.group);
this.tableData = [];
res.data.forEach((item, index) => {
if (item.type == 'PLC_GATEWAY') {
this.plcInfo = {
name: 'PLC网关',
status: item.online
};
} else {
item.buttonShowType = false;
item.typeName = this.typeList.find(el => el.value == item.type).label;
if (item.vrDeviceCode) {
item.vrDeviceName = this.$store.getters['map/getDeviceByCode'](item.vrDeviceCode).name;
}
if (this.project == 'heb' || this.project == 'designheb') {
if (item.type == 'SWITCH') {
this.tableData.push(item);
}
} else {
this.tableData.push(item);
}
}
});
} catch (error) {
console.error(error);
}
},
doClose() {
this.show = false;
},
handleClick(row, index) {
row.buttonShowType = !row.buttonShowType;
this.index = index;
this.tableData.splice(index, 1, row);
this.$store.dispatch('map/selectDeviceCode', {flag: row.buttonShowType, type: row.type});
},
async connect(row) {
this.$confirm(`您确定连接${this.typeObj[this.selected._type] || '设备'}: ${this.selected.name}, 是否继续?`, '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(async () => {
try {
const res = await postSimulationConnectById(this.group, row.id, this.selected.code);
if (res && res.code == 200) {
this.$message.success('连接成功!');
this.tableData[this.index]['vrDeviceCode'] = this.selected.code;
this.tableData[this.index]['vrDeviceName'] = this.selected.name;
this.tableData.splice(this.index, 1, this.tableData[this.index]);
}
} catch (error) {
console.error(error);
}
});
},
async cancel(row, index) {
this.$confirm('您确定将次设备断开, 是否继续?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(async () => {
if (row.vrDeviceCode) {
row.vrDeviceCode = '';
row.vrDeviceName = '';
this.tableData.splice(index, 1, row);
try {
const res = await putSimulationDisconnectById(this.group, row.id);
if (res && res.code == 200) {
this.$message.success('断开成功!');
}
} catch (error) {
console.error(error);
}
}
});
}
}
};
</script>
<style rel="stylesheet/scss" lang="scss" scoped>
.plc_box{
float: left;
margin-right: 40px;
padding: 9px;
}
.freshen_box{
float: right;
}
.flex_box{
float: left;
margin-right: 5px;
line-height: 30px;
}
/deep/ {
.el-dialog__wrapper{
width: 800px;
}
.dialog_box{
height: 100%;
}
.el-dialog__body{
padding-top: 6px;
height: calc(100% - 54px);
}
}
</style>

View File

@ -0,0 +1,600 @@
<template>
<div v-if="dialogShow" id="faultChoose">
<div class="falutChooseTitle">{{ title }}</div>
<div class="closeFalutChoose" @click="closeFaultChoose">
<span class="el-icon-close closeFalutChooseIn" />
</div>
<el-card class="triggerFaultInfo">
<el-button type="primary" size="small" style="margin-bottom:10px;" @click="isTableShow=!isTableShow">{{ isTableShow?'隐藏列表':'显示列表' }}</el-button>
<el-table v-if="isTableShow" :data="faultList" height="300" border style="width: 100%;font-size:13px;">
<el-table-column prop="targetDeviceCode" label="目标设备" width="220">
<template slot-scope="scope">
<span>{{ `${deviceMap[scope.row.targetDeviceType]}${formatNameByCode(scope.row.targetDeviceCode)}` }}</span>
</template>
</el-table-column>
<el-table-column prop="faultType" label="故障类型">
<template slot-scope="scope">
<span>{{ covertFaultType(scope.row) }}</span>
</template>
</el-table-column>
<el-table-column prop="condition" label="列车" width="100">
<template slot-scope="scope">
<span>{{ `${deviceMap[scope.row.condition.triggerDeviceType] || ''}${formatNameByCode(scope.row.condition.triggerDeviceCode)}` }}</span>
</template>
</el-table-column>
<el-table-column prop="condition" label="到达区段" width="180">
<template slot-scope="scope">
<span>{{ `${formatNameByCode(scope.row.condition.triggerAssociatedDeviceCode)}` }}</span>
</template>
</el-table-column>
<el-table-column prop="condition.condition.triggerTime" label="触发时间" width="150" />
<el-table-column prop="triggeringTime" label="故障状态">
<template slot-scope="scope">
<span>{{ scope.row.triggeringTime? '已触发': '未触发' }}</span>
</template>
</el-table-column>
<el-table-column label="操作">
<template slot-scope="scope">
<el-button v-if="!scope.row.triggeringTime" type="text" size="small" @click="handleDelete(scope.row)">取消</el-button>
</template>
</el-table-column>
</el-table>
</el-card>
<div class="targetCard">
<div class="targetCardHead">
<el-button style="padding:3px 0" type="text" @click="addRulesCreate">新增</el-button>
</div>
<div>
<el-form ref="form" :model="faultRule" :inline="true" label-width="100px" style="margin-left:15px;">
<el-form-item label="目标设备" class="targetFormItem">
<el-input v-model="targetDeviceName" disabled size="small" class="inputModelClass" style="width: 300px;" />
</el-form-item>
<el-form-item label="故障类型" class="targetFormItem">
<el-select v-model="faultRule.faultType" placeholder="请选择" class="inputModelClass" size="small" style="width: 300px;">
<el-option
v-for="item in faultTypeList"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</el-form-item>
<el-form-item v-if="triggerMode === 'DEVICE'" label="列车" class="targetFormItem">
<el-input v-model="triggerDevice" size="small" disabled class="inputModelClass" style="width: 240px;" />
<el-button :type="field === 'triggerActive' ? 'danger' : 'primary'" size="small" @click="hover('triggerActive')">{{ $t('map.activate') }}</el-button>
</el-form-item>
<el-form-item v-if="triggerMode === 'DEVICE'" label="到达区段" class="targetFormItem">
<el-input v-model="triggerAssociatedDevice" size="small" disabled class="inputModelClass" style="width: 240px;" />
<el-button :type="field === 'triggerAssociated'? 'danger': 'primary'" size="small" @click="hover('triggerAssociated')">{{ $t('map.activate') }}</el-button>
</el-form-item>
<el-form-item v-if="triggerMode === 'TIME'" label="触发时间" class="targetFormItem">
<el-date-picker
v-model="faultRule.condition.triggerTime"
size="small"
format="yyyy-MM-dd HH:mm:ss"
value-format="yyyy-MM-dd HH:mm:ss"
type="datetime"
placeholder="选择日期时间"
/>
</el-form-item>
</el-form>
</div>
</div>
</div>
</template>
<script>
import { getSimulationFaultRules, setFailureModeNew, deleteFailureRule, cancelFailureModeNew } from '@/api/simulation';
import ConstConfig from '@/scripts/ConstConfig';
import { FaultStatusEnum } from '@/scripts/FaultDicNew';
import ModelType from '@/jmapNew/constant/deviceType';
import { deviceFaultType } from '@/scripts/cmdPlugin/Config';
//
export default {
name: 'FaultChoose',
props: {
group: {
type: String,
required: true
},
offset: {
type: Number,
required: true
}
},
data() {
return {
dialogShow: false,
loading: false,
isAdd:false,
isTableShow:true,
deviceMap: {},
simulationFault:{},
faultList: [],
faultRule:{
targetDeviceCode:'',
targetDeviceType:'',
faultType:'',
condition:{
triggerDeviceCode:'',
triggerDeviceStatus:'',
triggerDeviceType :'',
type:'DEVICE',
triggerTime: '',
triggerAssociatedDeviceCode: ''
}
},
triggerDevice:'',
triggerAssociatedDevice: '',
triggerMode: 'DEVICE',
triggerModeList: [
{label: '设备触发', value: 'DEVICE'},
{label: '时间触发', value: 'TIME'}
],
field:'',
triggerStatusList:[],
faultTypeList:[],
targetDeviceName: ''
};
},
computed: {
title() {
return '自动故障设置';
},
lineCode() {
return this.$route.query.lineCode;
},
targetDevice() {
return this.$store.state.training.triggerFaultDevice;
}
},
watch:{
'$store.state.socket.autoFaultTrigger':function(val) {
this.dialogShow && this.getSimulationFaultRules();
},
'$store.state.menuOperation.selectedCount':function(em) {
const device = this.$store.state.menuOperation.selected;
if (device && device.code) {
this.deviceSelect(device);
}
},
targetDevice: function(device) {
this.targetDeviceName = this.targetDevice.name || this.targetDevice.code;
}
},
mounted() {
this.deviceMap = [];
ConstConfig.ConstSelect.simulationDeviceList.forEach(elem => {
this.deviceMap[elem.value] = elem.label;
});
},
methods: {
formatNameByCode(code) {
let name = '';
const device = this.$store.getters['map/getDeviceByCode'](code);
if (device) {
switch (device._type) {
case ModelType.Signal:
case ModelType.Switch:
case ModelType.Station:
case ModelType.Section: {
if (device.parentName) {
name += device.parentName + '-' + device.name;
} else {
name += device.name;
}
break;
}
case ModelType.Train:
name = device.code;
break;
}
if (device.stationCode) {
const station = this.$store.getters['map/getDeviceByCode'](device.stationCode);
if (station) {
name += '【' + station.name + '】';
}
}
}
return name;
},
hover(field) {
if (this.field == '') {
this.field = field;
} else {
this.field = '';
}
},
handleDelete(row) {
cancelFailureModeNew(row.id, this.$route.query.group).then(resp => {
this.getSimulationFaultRules();
}).catch(() => {
this.$message.error('取消故障失败!');
});
},
covertType(type) {
switch (type) {
case 'SECTION':return 'Section';
case 'SIGNAL':return 'Signal';
case 'SWITCH':return 'Switch';
case 'STATION':return 'Station';
case 'ZC':return 'ZcControl';
case 'STAND':return 'StationStand';
case 'TRAIN':return 'Train';
case 'Section':return 'SECTION';
case 'Signal':return 'SIGNAL';
case 'Switch':return 'SWITCH';
case 'Station':return 'STATION';
case 'ZcControl':return 'ZC';
case 'StationStand':return 'STAND';
case 'Train':return 'TRAIN';
}
},
covertFaultType(row) {
let faultType = '';
if (row && row.id) {
let type = this.covertType(row.targetDeviceType);
if (type == 'Station') {
type = 'ZcControl';
}
const currentList = deviceFaultType[type];
currentList.forEach(temp=>{
if (temp.value === row.faultType) {
faultType = temp.label;
}
});
}
return faultType;
},
closeFaultChoose() {
this.dialogShow = false;
this.isAdd = false;
if (this.$refs.addFault) {
this.$refs.addFault.resetForm();
}
},
closeAddRules() {
this.isAdd = false;
},
getSimulationFaultRules() {
if (this.dialogShow) {
getSimulationFaultRules(this.$route.query.group).then(resp => {
this.faultList = resp.data;
}).catch(() => {
this.$messageBox('获取数据异常');
});
}
},
changeTriggerMode(val) {
this.resetForm();
this.triggerMode = val;
this.faultRule.condition.type = val;
},
doShow() {
this.dialogShow = true;
this.getSimulationFaultRules();
this.resetForm();
this.faultTypeList = deviceFaultType[this.targetDevice._type];
this.faultRule.targetDeviceCode = this.targetDevice.code;
this.faultRule.targetDeviceType = this.covertType(this.targetDevice._type);
this.faultRule.faultType = (this.faultTypeList[0] || {}).value;
this.$nextTick(()=>{
this.dragEvent();
});
},
resetForm() {
this.getSimulationFaultRules();
this.field = '';
this.triggerDevice = '';
this.triggerAssociatedDevice = '';
this.faultRule = {
targetDeviceCode:this.targetDevice.code,
targetDeviceType:this.covertType(this.targetDevice._type),
faultType:'',
condition:{
triggerDeviceCode:'',
triggerDeviceStatus:'',
triggerDeviceType :'',
type:'DEVICE',
triggerTime: '',
triggerAssociatedDeviceCode: ''
}
};
},
doClose() {
this.dialogShow = false;
},
handleAdd() {
this.isAdd = true;
this.$nextTick(() => {
this.$refs.addFault.initValue();
});
},
deleteFailure(index, row) {
event.cancelBubble = true;
this.$confirm('删除故障规则,是否继续?', '提 示', {
confirmButtonText: '确 定',
cancelButtonText: '取 消',
type: 'warning'
}).then(() => {
deleteFailureRule(row.id).then(resp => {
}).catch(error => {
this.$message.error(`删除故障规则失败: ${error.message}`);
});
}).catch( () => { });
},
settingFailure(index, row, idx) {
const faultModel = {ruleId: row.id, auto:true};
// row['loading' + idx] = true;
setFailureModeNew(faultModel, this.group).then(() => {
this.getSimulationFaultRules();
this.$message.success('设置自动故障成功');
}).catch(() => {
this.$messageBox('设置自动故障失败');
});
},
cancleAutoFault() {
const faultModel = {auto:false};
setFailureModeNew(faultModel, this.group).then(() => {
this.getSimulationFaultRules();
this.$message.success('取消自动故障成功');
}).catch(() => {
this.$messageBox('取消自动故障失败');
});
},
addRulesCreate() {
if (this.triggerMode === 'DEVICE' && this.triggerDevice == '') {
this.$messageBox('请选择触发设备');
return;
}
if (this.triggerMode === 'DEVICE' && this.faultRule.condition.triggerDeviceStatus == '') {
this.$messageBox('请选择触发状态');
return;
}
if (this.faultRule.faultType == '') {
this.$messageBox('请选择故障类型');
return;
}
if (this.triggerMode === 'TIME' && this.faultRule.condition.triggerTime == '') {
this.$messageBox('请选择触发时间');
return;
}
if (this.triggerMode === 'DEVICE' && this.faultRule.condition.triggerDeviceType === 'TRAIN' && this.faultRule.condition.triggerDeviceStatus === 'Section' && this.faultRule.condition.triggerAssociatedDeviceCode == '') {
this.$messageBox('请选择关联设备');
return;
}
const param = {
targetDeviceCode:this.targetDevice.code,
targetDeviceType:this.covertType(this.targetDevice._type),
faultType: this.faultRule.faultType,
condition:{
triggerDeviceCode: this.faultRule.condition.triggerDeviceCode || null,
triggerDeviceStatus:this.faultRule.condition.triggerDeviceStatus || null,
triggerDeviceType :this.faultRule.condition.triggerDeviceType || null,
type:this.faultRule.condition.type,
triggerTime: this.faultRule.condition.triggerTime || null,
triggerAssociatedDeviceCode: this.faultRule.condition.triggerAssociatedDeviceCode || null
}
};
setFailureModeNew(param, this.$route.query.group).then(res=>{
this.$message.success('创建故障成功!');
this.resetForm();
}).catch((error)=>{
this.$messageBox('创建故障失败: ' + error.message);
});
},
deviceSelect(em) {
if (this.field.toUpperCase() === 'triggerAssociated'.toUpperCase() && em._type.toUpperCase() === 'Section'.toUpperCase()) {
if (em._type.toUpperCase() === 'Section'.toUpperCase() && em.parentName) {
this.triggerAssociatedDevice = em._type + '-' + em.parentName + '-' + em.name;
} else {
this.triggerAssociatedDevice = em._type + '-' + em.name;
}
this.faultRule.condition.triggerAssociatedDeviceCode = em.code;
} else if (this.field.toUpperCase() === 'triggerActive'.toUpperCase() && em._type.toUpperCase() === 'Train'.toUpperCase()) {
this.faultRule.condition.triggerDeviceType = this.covertType(em._type);
this.faultRule.condition.triggerDeviceCode = em.code;
if (em._type.toUpperCase() === 'Section'.toUpperCase() && em.parentName) {
this.triggerDevice = em._type + '-' + em.parentName + '-' + em.name;
} else if (em._type.toUpperCase() === 'Train'.toUpperCase()) {
this.triggerDevice = em._type + '-' + em.code;
} else {
this.triggerDevice = em._type + '-' + em.name;
}
this.triggerStatusList = [];
const faultStatus = FaultStatusEnum[this.faultRule.condition.triggerDeviceType];
if (faultStatus) {
const list = Object.keys(faultStatus);
list.forEach(key=>{
this.triggerStatusList.push({label:faultStatus[key], value:key});
});
}
this.field = '';
this.faultRule.condition.triggerDeviceStatus = (this.triggerStatusList[0] || {}).value;
}
},
dragEvent() {
const offset = this.offset;
const dialogHeaderEl = document.querySelector('.falutChooseTitle');
const dragDom = document.querySelector('#faultChoose');
dialogHeaderEl.style.cursor = 'move';
/** 获取原有属性 ie dom元素.currentStyle 火狐谷歌 window.getComputedStyle(dom元素, null);*/
const sty = dragDom.currentStyle || window.getComputedStyle(dragDom, null);
dialogHeaderEl.onmousedown = (e) => {
/** 鼠标按下,计算当前元素距离可视区的距离*/
const disX = e.clientX - dialogHeaderEl.offsetLeft;
const disY = e.clientY - dialogHeaderEl.offsetTop;
/** 获取到的值带px 正则匹配替换*/
let styL, styT;
/** 注意在ie中 第一次获取到的值为组件自带50% 移动之后赋值为px*/
if (sty.left.includes('%')) {
// eslint-disable-next-line no-useless-escape
styL = +document.body.clientWidth * (+sty.left.replace(/\%/g, '') / 100);
styT = +document.body.clientHeight * (+sty.top.replace(/\%/g, '') / 100);
} else {
// eslint-disable-next-line no-useless-escape
styL = +sty.left.replace(/\px/g, '');
// eslint-disable-next-line no-useless-escape
styT = +sty.top.replace(/\px/g, '');
}
document.onmousemove = function (e) {
/** 通过事件委托,计算移动的距离*/
const l = e.clientX - disX;
const t = e.clientY - disY;
/** 移动当前元素*/
// dragDom.style.left = `${l + styL}px`;
// dragDom.style.top = `${t + styT}px`;
/** 移动当前元素*/
if (l + styL < 0) {
dragDom.style.left = `0px`;
} else if (l + styL > document.body.clientWidth - dragDom.clientWidth - 10) {
dragDom.style.left = `${document.body.clientWidth - dragDom.clientWidth - 10}px`;
} else {
dragDom.style.left = `${l + styL}px`;
}
if (t + styT <= offset) {
dragDom.style.top = offset + `px`;
} else if (t + styT > document.body.clientHeight - dragDom.clientHeight - 10) {
dragDom.style.top = `${document.body.clientHeight - dragDom.clientHeight - 10}px`;
} else {
dragDom.style.top = `${t + styT}px`;
}
/** 将此时的位置传出去*/
// binding.value({ x: e.pageX, y: e.pageY });
};
document.onmouseup = function () {
document.onmousemove = null;
document.onmouseup = null;
};
};
}
}
};
</script>
<style lang="scss">
#faultChoose .card .queryList .el-card .el-card__body .el-table--border .el-table__body-wrapper{
height: 135px !important;
overflow-y: auto !important;
}
#faultChoose .el-button--mini {
margin-left: 5px;
}
.triggerFaultListLeft{
display: inline-block;
float: left;
width: 730px;
}
// safariqq360
//
#faultChoose .el-table__body-wrapper::-webkit-scrollbar {
width: 6px;
height: 6px;
// height: 110px;
background-color: #FFFFFF;
}
/*定义滚动条轨道 内阴影+圆角*/
#faultChoose .el-table__body-wrapper::-webkit-scrollbar-track {
// box-shadow: inset 0 0 6px rgba(0,0,0,0.3);
border-radius: 10px;
background-color: #FFFFFF;;
}
/*定义滑块 内阴影+圆角*/
#faultChoose .el-table__body-wrapper::-webkit-scrollbar-thumb {
border-radius: 10px;
// box-shadow: inset 0 0 6px rgba(0,0,0,.3);
background-color: #eaeaea;
}
/*滑块效果*/
#faultChoose .el-table__body-wrapper::-webkit-scrollbar-thumb:hover {
border-radius: 5px;
// box-shadow: inset 0 0 5px rgba(0,0,0,0.2);
background: rgba(0,0,0,0.4);
}
/*IE滚动条颜色*/
html {
scrollbar-face-color:#bfbfbf;/*滚动条颜色*/
scrollbar-highlight-color:#000;
scrollbar-3dlight-color:#000;
scrollbar-darkshadow-color:#000;
scrollbar-Shadow-color:#adadad;/*滑块边色*/
scrollbar-arrow-color:rgba(0,0,0,0.4);/*箭头颜色*/
scrollbar-track-color:#eeeeee;/*背景颜色*/
}
</style>
<style scoped rel="stylesheet/scss" lang="scss">
.triggerFaultInfo{
margin-bottom:10px;
padding: 10px 10px 10px 15px;
}
.triggerFaultList{
font-size: 14px;
margin-top: 10px;
line-height: 20px;
}
.triggerFaultTitle{
font-size: 15px;
font-weight: bold;
}
.falutChooseTitle{
padding: 15px;
cursor: all-scroll;
}
#faultChoose{
width: 1000px;
position: absolute;
left: 30%;
top: 20%;
background: #fff;
padding:0px 0px 15px 0px;
// transform: translate3d(-50%,-50%,0);
border-radius: 6px;
z-index:999;
}
.faultChooseFoot{
display: inline-block;
float: right;
margin-right: 20px;
margin-top: 20px;
}
.closeFalutChoose{
position: absolute;
right: 0px;
top: 0px;
width: 35px;
height: 35px;
cursor: pointer;
}
.closeFalutChooseIn{
font-size: 20px;
margin-left: 5px;
margin-top: 10px;
}
.targetCard{
box-shadow: 0 2px 12px 0 rgba(0,0,0,.1);
border-top: 1px #ebeef5 solid;
}
.targetFormItem{
margin-bottom: 12px;
}
.targetCardHead{
padding: 5px;
display: inline-block;
width: 100%;
margin-bottom: 10px;
border-bottom: 1px #f4f4f4 solid;
text-align: right;
padding-right: 15px;
}
</style>

View File

@ -0,0 +1,118 @@
<template>
<el-dialog v-dialogDrag :title="title" :visible.sync="show" width="550px" :before-close="doClose">
<el-form ref="form" label-width="120px" :model="formModel" :rules="rules">
<el-form-item :label="$t('display.setTime.systemTime')" prop="initTime">
<el-time-picker
v-model="formModel.initTime"
:picker-options="pickerOptions"
:placeholder="$t('display.setTime.anyTime')"
@change="handleChange"
/>
</el-form-item>
<el-form-item v-if="hasNumber" :label="$t('display.setTime.loadTrainNum')" prop="loadNum">
<el-input-number v-model="formModel.loadNum" :min="1" :max="maxNumber" :label="$t('display.setTime.selectLoadTrainNum')" />
<span> {{ ` (${$t('display.setTime.maxTrainNum')} ${maxNumber}) ` }} </span>
</el-form-item>
</el-form>
<span slot="footer" class="dialog-footer">
<el-button @click="show = false">{{ $t('global.cancel') }}</el-button>
<el-button type="primary" :loading="status" @click="handleSure">{{ $t('global.confirm') }}</el-button>
</span>
</el-dialog>
</template>
<script>
import { prefixIntrger } from '@/utils/date';
import { getLoadTrainNumber } from '@/api/simulation';
//
export default {
data() {
return {
show: false,
formModel: {
initTime: new Date(),
loadNum: 1
},
maxNumber: 1,
pickerOptions: { selectableRange: '00:00:00 - 23:59:59' },
status: false
};
},
computed: {
title() {
return this.$t('display.setTime.setSimulationSystemTime');
},
hasNumber() {
return this.$route.params.mode == 'demon' && this.$route.query.prdType == '04';
},
group() {
return this.$route.query.group;
},
rules() {
return {
initTime: [
{ required: true, message: this.$t('display.setTime.selectSystemTime'), trigger: 'change' },
{
validator(rule, value, callback) {
if (this.maxNumber == 0) {
callback(new Error(this.$t('display.setTime.selectValidStartTime')));
} else {
callback();
}
},
trigger: 'change',
maxNumber: this.maxNumber
}
],
loadNum: [
{ required: true, message: this.$t('display.setTime.selectTrainNum'), trigger: 'change' }
]
};
}
},
methods: {
doShow() {
this.formModel.initTime = new Date(this.$store.state.training.initTime || null);
this.handleChange(this.formModel.initTime);
this.show = true;
},
doClose() {
this.status = false;
this.show = false;
},
handleChange(initTime) {
this.formModel.loadNum = 0;
if (this.hasNumber) {
getLoadTrainNumber({ time: this.formatTime(initTime) }, this.group).then(resp => {
this.maxNumber = parseInt(resp.data);
});
}
},
formatTime(initTime) {
const hh = prefixIntrger(initTime.getHours(), 2);
const mm = prefixIntrger(initTime.getMinutes(), 2);
const ss = prefixIntrger(initTime.getSeconds(), 2);
return `${hh}:${mm}:${ss}`;
},
handleSure() {
this.$refs.form.validate((valid) => {
if (valid) {
this.status = true;
const model = {
initTime: this.formatTime(this.formModel.initTime)
};
if (this.hasNumber) {
model['loadNum'] = this.formModel.loadNum;
}
this.$emit('ConfirmSelectBeginTime', model);
this.doClose();
}
});
}
}
};
</script>

View File

@ -0,0 +1,190 @@
<template>
<div>
<div v-if="isAllShow&&project != 'bjd'" class="display_top_draft" :style="allStyle">
<div class="btn_hover" @click="menuClick">菜单</div>
<el-button-group ref="button_group_box" class="button_group_box" :style="`margin-left:-${btnWidth}px`">
<el-button v-if="jl3dmodelShow && !isContest && project !== 'bjd'" size="small" @click="jumpjlmap3dmodel">{{ jl3dmodel }}</el-button>
</el-button-group>
</div>
<Jl3d-Device
v-if="deviceif"
v-show="deviceShow"
ref="Jl3dDevice"
:panel-show="deviceShow"
@closedevice3dview="jumpjlmap3dmodel"
/>
</div>
</template>
<script>
import Jl3dDevice from '@/views/jlmap3d/device/jl3ddevice';
import { getToken } from '@/utils/auth';
import { getSessionStorage } from '@/utils/auth';
import { EventBus } from '@/scripts/event-bus';
import { getPostByProjectCode } from '@/api/learn';
import { ProjectCode } from '@/scripts/ProjectConfig';
export default {
name:'DemonMenu',
components:{
Jl3dDevice,
},
props:{
isAllShow:{
type:Boolean,
require:true
},
jl3dmodelShow:{
type:Boolean,
require:true
},
jl3dnameShow:{
type:Boolean,
require:true
},
cctvShow:{
type:Boolean,
require:true
},
trafficplanShow:{
type:Boolean,
require:true
},
traffictrainShow:{
type:Boolean,
require:true
},
scheduleLoadShow:{
type:Boolean,
require:true
},
driverShow:{
type:Boolean,
require:true
},
schedulePreviewShow:{
type:Boolean,
require:true
},
jlmap3dFaultShow:{
type:Boolean,
require:true
},
allStyle:{
type:String,
default() {
return '';
}
}
},
data() {
return {
hoverBtn: false,
btnWidth: 0,
group:'',
mapId:'',
lineCode:'',
practiceDisabled:false,
deviceif:false,
deviceShow: true,
drivingShow: false,
messageBoard: false,
jl3dtrafficplan:this.$t('display.demon.trafficplantext'),
jl3dtraffictrain:this.$t('display.demon.traffictraintext'),
jl3dpassflow:this.$t('display.demon.passengerflow'),
jl3dname: this.$t('display.demon.threeDimensionalView'),
jl3dmodel: this.$t('display.demon.deviceView')
};
},
computed:{
isDrive() {
return this.$route.query.prdType == '04';
},
project() {
return getSessionStorage('project');
},
isContest() {
return this.$route.params.mode === 'demon' && this.project == 'drts';
},
running() {
return this.$store.state.training.started;
},
isLocal() { //
return process.env.VUE_APP_PRO === 'local';
}
},
mounted() {
this.group = this.$route.query.group;
this.mapId = this.$route.query.mapId;
this.lineCode = this.$route.query.lineCode;
EventBus.$on('loadScene', () => {
this.practiceDisabled = true;
});
EventBus.$on('quitScene', () => {
this.practiceDisabled = false;
});
if (this.project) {
getPostByProjectCode(ProjectCode[this.project]).then(resp => {
if (resp.data) {
this.messageBoard = true;
}
}).catch(() => {
console.log('获取留言板信息失败');
});
}
},
methods:{
menuClick() {
this.hoverBtn = !this.hoverBtn;
if (this.hoverBtn) {
this.btnWidth = 600;
} else {
this.btnWidth = 0;
}
},
jumpjlmap3dmodel() {
if (this.deviceif == false) {
this.deviceif = true;
} else {
if (this.deviceShow == false) {
this.deviceShow = true;
} else {
this.deviceShow = false;
}
}
}
}
};
</script>
<style lang="scss" scoped>
.display_top_draft{
position: absolute;
left: 5px;
top: 15px;
height: 32px;
overflow: hidden;
padding-left: 44px;
z-index: 35;
.btn_hover{
position: absolute;
left: 0;
top: 0;
z-index: 2;
color: #4e4d4d;
font-size: 14px;
background: #fff;
padding: 8px;
border-radius: 5px;
display: block;
cursor: pointer;
float: left;
height: 32px;
}
.button_group_box{
float: left;
transition: all 0.5s;
overflow: hidden;
margin-left: -700px;
// transform: translateX(0px);
}
}
</style>

View File

@ -0,0 +1,307 @@
<template>
<div class="main" :style="{width: canvasWidth+'px',height:'100%',position:'absolute',overflow:'hidden'}">
<template>
<transition name="el-zoom-in-bottom">
<map-system-draft ref="mapCanvas" @back="back" />
</transition>
<menu-demon v-if="isDemon" ref="menuDemon" :offset="offset" :offset-bottom="offsetBottom" :data-error="dataError" :text-status-height="textStatusHeight" @start="start" @end="end" />
</template>
<menu-system-time ref="menuSystemTime" :offset="offset" :group="group" />
</div>
</template>
<script>
import { getSessionStorage } from '@/utils/auth';
import { mapGetters } from 'vuex';
import { OperateMode } from '@/scripts/ConstDic';
import { timeFormat } from '@/utils/date';
import MapSystemDraft from '@/views/newMap/mapsystemNew/index';
import MenuDemon from './menuDemon';
import MenuSystemTime from '@/views/newMap/displayCity/menuSystemTime';
import { clearSimulation, getSimulationInfoNew, ranAsPlan, exitRunPlan } from '@/api/simulation';
import { loadMapDataById } from '@/utils/loaddata';
import { EventBus } from '@/scripts/event-bus';
export default {
name: 'DisplayDraft',
components: {
MapSystemDraft,
MenuDemon,
MenuSystemTime
},
data() {
return {
offset: 15,
offsetBottom: 15,
tipBottom: 0,
textStatusHeight: 0,
planRunning:false,
dataError: false,
group:''
};
},
computed:{
...mapGetters([
'canvasWidth'
]),
mode() {
return this.$route.params.mode;
},
project() {
return getSessionStorage('project');
},
isDemon() {
return this.mode === 'demon' && this.project != 'drts';
},
isContest() {
return this.mode === 'demon' && this.project == 'drts';
},
isExam() {
return this.mode === 'exam';
},
isLesson() {
return (this.mode === 'teach' || this.mode === 'manage');
},
isScript() {
return this.mode === 'script';
},
mapId() {
return this.$route.query.mapId;
},
width() {
return this.$store.state.app.width;
},
height() {
return this.$store.state.app.height;
}
},
watch:{
'$store.state.socket.permissionOver': function () {
this.$alert('用户权限已被收回', '提示', {
confirmButtonText: '确定',
callback: action => {
this.back();
}
});
},
// menuSchema
'$store.state.training.prdType':function(val) {
if (val == '01') { this.switchModeInner('01'); } else { this.switchModeInner('02'); }
},
'$store.state.config.menuBarLoadedCount': function (val) { // menuBar
this.setPosition();
},
'$store.state.app.windowSizeCount': function() { //
this.setWindowSize();
},
'$store.state.training.prdType': function (val) { //
this.setPosition();
},
'$store.state.map.mapViewLoadedCount': function (val) { //
if (this.planRunning) {
this.$store.dispatch('training/simulationStart');
}
},
$route() {
if (!this.isLesson && !this.isExam) {
this.initLoadData();
}
}
},
beforeDestroy() {
clearSimulation(this.group);
this.$store.dispatch('training/reset');
// this.$store.dispatch('map/mapClear');
},
async mounted() {
this.setWindowSize();
this.initLoadData();
},
methods:{
//
endViewLoading(isSuccess) {
if (!isSuccess) {
this.$store.dispatch('map/mapClear');
}
this.$nextTick(() => {
EventBus.$emit('viewLoading', false);
});
},
// 仿退
async back() {
if (this.isExam) {
await this.$refs.menuExam.back();
} else if (this.isLesson) {
await this.$refs.lessonMenu.back();
} else if (this.isDemon) {
await this.$refs.menuDemon.back();
} else if (this.isScript) {
await this.$refs.menuScript.back();
} else if (this.isContest) {
await this.$refs.menuDispatherContest.back();
}
},
//
setPosition() {
this.$nextTick(() => {
this.offset = 10;
this.offsetBottom = 15;
const menuBar = document.getElementById('menuBar');
const menuTool = document.getElementById('menuTool');
const menuBottom = document.getElementById('menuButton');
const menuButtonsBox = document.getElementById('menuButtons_box');
const textStatus = document.getElementById('textStatus');
if (menuBar) {
this.offset = (menuBar.offsetHeight || 0) + 15;
}
if (menuTool) {
this.offset = (menuTool.offsetHeight || 0) + 15;
}
const buttonWidth = this.width - 1200; // B box widht
if (menuBottom && buttonWidth < 780) {
this.offsetBottom = (menuBottom.offsetHeight || 0) + 15;
}
if (menuButtonsBox) {
this.tipBottom = (menuButtonsBox.offsetHeight || 0) + 15;
}
if (textStatus) {
this.textStatusHeight = textStatus.offsetHeight || 0;
textStatus.style.top = this.offset - 15 + 'px';
}
});
},
//
setWindowSize() {
const width = this.width;
const height = this.height;
this.$store.dispatch('config/resize', { width, height });
// this.$store.dispatch('training/updateOffsetStationCode', { offsetStationCode: this.offsetStationCode });
},
//
initLoadData() {
this.group = this.$route.query.group;
this.$store.dispatch('training/reset');
this.loadSimulationInfo();
this.loadMapData();
},
// 仿group仿
async loadSimulationInfo() {
const resp = await getSimulationInfoNew(this.group);
if (resp && resp.code == 200 && resp.data) {
if (!resp.data.dataError) {
this.$store.dispatch('scriptRecord/updateSimulationPause', resp.data.pause); //
this.$store.dispatch('training/setInitTime', +new Date(`${new Date().toLocaleDateString()} ${timeFormat(resp.data.systemTime)}`));
this.$store.dispatch('training/countTime');
this.$store.dispatch('runPlan/setRunPlanInfo', resp.data.runPlan);
this.planRunning = resp.data.planRunning;
if (resp.data.planRunning) {
this.$store.commit('training/start');
}
} else {
this.$messageBox('此地图数据正在维护中,无法运行!');
}
this.dataError = resp.data.dataError;
}
},
// showMode
switchModeInner(swch) {
let showMode = '03';
if (swch == '01') {
showMode = '03';
} else if (swch == '02') {
showMode = '02';
}
const nameList = Object.keys(this.$store.state.map.map || {});
let list = [];
nameList.forEach(item => {
if (item !== 'skinVO') {
const data = this.$store.state.map.map[item];
if (data && data.constructor === Array) {
list = [...list, ...data];
}
}
});
if (swch == '01') {
this.$jlmap.updateShowStation(list, this.$store.state.training.centerStationCode); //
} else {
this.$jlmap.updateShowStation(list, ''); //
}
this.$jlmap.updateShowMode(list, showMode); //
},
//
loadMapData() {
if (parseInt(this.mapId)) {
this.$store.dispatch('training/changeOperateMode', { mode: OperateMode.NORMAL }); //
loadMapDataById(this.mapId, 'simulation');
} else {
this.endViewLoading();
}
},
start(model) { // 仿
const data = {
time: model.initTime
};
if (this.$route.query.prdType === '04') {
data.loadNumber = model.loadNum;
}
ranAsPlan(data, this.group).then(res => {
this.$store.dispatch('training/setInitTime', +new Date(`${new Date().toLocaleDateString()} ${model.initTime}`));
}).catch(error => {
let message = '';
switch (error.code) {
case '5001':
message = this.$t('error.mapDataError');
break;
case '5002':
message = this.$t('error.runningChartDataError');
break;
case '5003':
message = this.$t('error.runningChartIsNotLoaded');
break;
case '5004':
message = this.$t('error.runningDataError');
break;
case '5000':
message = this.$t('error.systemError');
break;
case '4000':
message = this.$t('error.simulationDoesNotExist');
break;
case '4001':
message = this.$t('error.simulationOperationIsNotDefined');
break;
case '4002':
message = this.$t('error.simulationOperationProcessingMethodNotFound');
break;
case '4003':
message = this.$t('error.simulationOperationFailed');
break;
case '4004':
message = this.$t('error.operationConflict');
break;
default:
message = '按计划行车异常,请退出重试!';
// this.$messageBox(',退!');
break;
}
this.$messageBox(message + '' + this.$t('error.startSimulationFailed'));
});
},
end() {
exitRunPlan(this.group).then(() => {
this.$store.dispatch('training/over').then(() => {
this.$store.dispatch('training/setMapDefaultState').then(() => {
this.$store.dispatch('map/clearJlmapTrainView');
this.$store.dispatch('map/resetActiveTrainList', false);
this.$store.dispatch('map/setTrainWindowShow', false);
});
});
}).catch(() => {
this.$messageBox(this.$t('display.demon.endSimulationFail'));
});
}
}
};
</script>

View File

@ -0,0 +1,306 @@
<template>
<div>
<demon-menu
ref="demonMenu"
:is-all-show="!dataError"
:jl3dmodel-show="isShow3dmodel && !isShowScheduling && !isDrive"
:jl3dname-show="!isShowScheduling&&!isDrive"
:cctv-show="!isShowScheduling"
:trafficplan-show="!isShowScheduling"
:schedule-load-show="isShowScheduling && !runing"
:schedule-preview-show="isShowScheduling && runing"
:jlmap3d-fault-show="false"
:driver-show="isDrive"
:all-style="'top:'+(offset+textStatusHeight)+'px'"
/>
<menu-schema
ref="menuSchema"
:offset="offset"
:data-error="dataError"
:offset-bottom="offsetBottom"
/>
<div class="display-draft" :class="{'haerbin_btn_box': $route.query.lineCode == '07'||$route.query.lineCode == '14'}" :style="{bottom: offsetBottom + 'px'}">
<el-button-group class="button-group-box">
<template v-if="!dataError">
<template v-if="!projectDevice">
<el-button type="danger" size="small" @click="end">{{ $t('display.demon.initialize') }}</el-button>
</template>
</template>
<el-button v-if="project !='bjd'" type="primary" size="small" @click="back">{{ projectDevice?'退出':$t('display.demon.back') }}</el-button>
</el-button-group>
</div>
</div>
</template>
<!-- 单人仿真 -->
<script>
import DemonMenu from './demonMenu';
import { Notification } from 'element-ui';
import MenuSchema from '@/views/newMap/displayCity/menuSchema';
import { getGoodsTryUse } from '@/api/management/goods';
import {getSimulationInfoNew, clearSimulation } from '@/api/simulation';
import { PermissionType } from '@/scripts/ConstDic';
import { getCountTime } from '@/utils/index';
import { TrainingMode } from '@/scripts/ConstDic';
import { quitScriptNew } from '@/api/simulation';
import { setGoodsTryUse } from '@/api/management/goods';
import {loadScriptNew } from '@/api/simulation';
import StatusIcon from '@/views/components/StatusIcon/statusIcon';
import Vue from 'vue';
import { getSessionStorage } from '@/utils/auth';
import { EventBus } from '@/scripts/event-bus';
export default {
name: 'MenuDemon',
components: {
MenuSchema,
DemonMenu,
StatusIcon
},
props: {
offset: {
type: Number,
required: true
},
offsetBottom: {
type: Number,
required: true
},
dataError: {
type: Boolean,
default() {
return false;
}
},
textStatusHeight: {
type: Number,
default() {
return 0;
}
}
},
data() {
return {
tryTime: 0, //
timeNow: 0, //
time: null, //
countTime: 0, //
remainingTime: 0,
userRole:'AUDIENCE',
goodsId: this.$route.query.goodsId,
try: this.$route.query.try, //
training: {
id: '',
name: '',
remarks: ''
},
isScriptRun:false,
jl3dname: this.$t('display.demon.threeDimensionalView'),
jl3dmodel: this.$t('display.demon.deviceView'),
isShow3dmodel :true,
isGoback: false,
runing:false,
prdTypeMap: {
'01': '01', // =>
'02': '02', // =>
'04': '02', // =>
'05': '' // => null
}
};
},
computed: {
isShowScheduling() {
return this.$route.query.prdType == '05';
},
isDrive() {
return this.$route.query.prdType == '04';
},
group() {
return this.$route.query.group;
},
projectDevice() {
return this.$route.query.projectDevice;
},
project() {
return getSessionStorage('project');
}
},
watch: {
'$store.state.training.subscribeCount': function () {
this.group && this.initLoadPage();
},
'$store.state.socket.simulationRoleList':function(val) {
(val || []).forEach(item => {
if (item.messageType === 'KICK_OUT' && item.userId == this.$store.state.user.id) {
!this.isGoback && this.back();
}
});
},
'$store.state.socket.simulationOver':function(val) {
!this.isGoback && this.back();
},
'$store.state.socket.simulationStart':function(val) {
if (val) {
this.setRuning(true);
this.$store.dispatch('training/simulationStart').then(() => {
this.$store.dispatch('map/setShowCentralizedStationNum');
});
}
},
'$store.state.socket.simulationReset':function(val) {
this.setRuning(false);
}
},
created() {
this.$store.dispatch('training/setPrdType', this.$route.query.prdType);
switch (this.$store.state.training.prdType) {
case '02': { this.userRole = 'DISPATCHER'; break; }
case '01': { this.userRole = 'STATION_SUPERVISOR'; break; }
case '04': { this.userRole = 'DRIVER'; break; }
case '05': { this.userRole = 'DEPOT_DISPATCHER'; break; }
default: { this.userRole = 'AUDIENCE'; break; }
}
},
beforeDestroy() {
if (this.time) {
this.setTryTime();
clearTimeout(this.time);
}
this.$store.dispatch('scriptRecord/updateSimulationPause', false);
this.$store.dispatch('map/resetActiveTrainList', true);
},
mounted() {
this.$store.dispatch('training/end', TrainingMode.NORMAL);
this.$nextTick(() => {
this.$refs.demonMenu.menuClick();
});
},
methods: {
async initLoadPage() {
try {
if (this.try != '0') {
this.loadInitData();
}
} catch (error) {
console.log(error);
}
},
loadInitData() {
const data = {
mapId: this.$route.query.mapId,
prdType: this.$route.query.prdType,
permissionType: PermissionType.SIMULATION
};
getGoodsTryUse(data).then(res => {
this.remainingTime = res.data.tryTime;
this.timeNow = Date.parse(new Date()) / 1000 + this.remainingTime;
if (this.try) {
this.time = setInterval(() => {
this.tryTime += 1;
this.countTime = getCountTime(this.timeNow);
if (this.countTime == -1) {
this.back();
}
}, 1000);
}
}).catch(() => {
this.$messageBox(this.$t('display.demon.getTimeFail'));
});
},
selectBeginTime() {
this.$refs.setTime.doShow();
},
start(model) {
this.$emit('start', model);
},
setRuning(run) {
this.runing = run;
},
end() {
this.$emit('end');
},
clearAllData() {
this.$refs.chatbox.clearAllData();
},
async back() {
this.isGoback = true;
if (this.projectDevice) {
clearSimulation(this.group).then(res=>{
this.$store.dispatch('training/over').then(() => {
this.$store.dispatch('LogOut').then(() => {
location.reload();
});
});
});
} else {
if (this.project === 'bjd') {
window.close();
} else {
this.$store.dispatch('map/setShowCentralizedStationCode', '');
history.go(-1);
Notification.closeAll();
}
}
},
setTryTime() {
if (this.try) {
const data = { time: this.tryTime, goodsId: this.goodsId };
if (data.goodsId) {
setGoodsTryUse(data);
}
}
},
quit() {
window.close();
}
}
};
</script>
<style rel="stylesheet/scss" lang="scss" scoped>
@import "src/styles/mixin.scss";
.display-card {
z-index: 9;
display: inline-block;
position: absolute;
top: 17px;
left: 160px;
height: 32px;
}
.display-card .el-row {
line-height: 32px !important;
}
.display-score {
background-color: black;
display: -moz-inline-box;
display: inline-block;
text-align: left;
height: 32px;
line-height: 24px;
border-radius: 4px;
padding-left: 2px;
margin-left: 10px;
font-family: "Microsoft" !important;
font-size: 18px !important;
color: #fff;
}
.haerbin_btn_box{
width: 450px;
bottom: 15px!important;
}
.display-draft {
position: absolute;
right: 10px;
bottom: 15px;
.button-group-box{
float: right;
}
}
</style>

View File

@ -0,0 +1,111 @@
<template>
<div class="schema" :style="{top: offset+'px'}">
<el-button v-if="!isScheduling" size="small" :type="faultMode ? '':'primary' " @click="changeOperateMode()">{{ faultMode?'切换到普通模式[Tab]':'切换到故障模式[Tab]' }}</el-button>
<fault-choose ref="faultChoose" :group="group" :offset="offset" />
</div>
</template>
<script>
import FaultChoose from './demon/faultChoose';
import { OperateMode } from '@/scripts/ConstDic';
import { getByGroupStationList } from '@/api/jmap/map';
import { getSessionStorage } from '@/utils/auth';
import { loadRunPlanData } from '@/utils/loaddata';
import { EventBus } from '@/scripts/event-bus';
//
export default {
name: 'MenuSchema',
components: {
FaultChoose
},
props: {
offset: {
type: Number,
required: true
},
dataError: {
type: Boolean,
default() {
return false;
}
}
},
data() {
return {
mode: OperateMode.NORMAL,
OperateMode: OperateMode,
viewDisabled: true,
faultMode: false
};
},
computed: {
group() {
return this.$route.query.group;
},
isScript() {
return this.$route.params.mode === 'script';
},
isDemon() {
return this.$route.params.mode === 'demon';
},
isScheduling() {
return this.$route.query.prdType === '05';
}
},
watch: {
'$store.state.training.triggerFaultCount': function () {
this.setFault();
},
'$store.state.runPlan.loadRunPlanCount': function () {
this.viewDisabled = false;
}
},
mounted() {
EventBus.$on('CheckFaultModeEvent', () => {
if (!this.isScheduling) {
this.changeOperateMode();
}
});
},
methods: {
loadRunData() {
this.$store.dispatch('runPlan/clear').then(() => {
if (this.group) {
this.viewDisabled = true;
//
getByGroupStationList(this.group).then(response => {
this.$store.dispatch('runPlan/setStations', response.data).then(() => {
loadRunPlanData(this.group, this.dataError);
});
}).catch(() => {
this.$messageBox(this.$t('display.schema.getStationListFail'));
});
}
});
},
changeOperateMode() {
this.faultMode = !this.faultMode;
let mode = OperateMode.NORMAL;
if (this.faultMode) {
mode = OperateMode.FAULT;
}
this.$store.dispatch('training/changeOperateMode', { mode: mode });
},
setFault() {
this.$refs.faultChoose.doShow();
}
}
};
</script>
<style rel="stylesheet/scss" lang="scss" scoped>
.schema {
z-index: 36;
display: inline;
position: absolute;
right: 5px;
}
/deep/ .el-button+.el-button {
margin-left: 0px;
}
</style>

View File

@ -0,0 +1,154 @@
<template>
<div v-if="isShowSystemTime" class="display-card" :style="{top: top+'px', right: newRight+'px'}">
<template v-if="pause">
<span class="display-pause">{{ $t('display.systemTime.timePause') }}</span>
</template>
<template v-else>
<system-time
class="display-time"
:time="time"
/>
<div v-if="isShowDate" style="width: 80px;height: 58px;float: right;box-shadow: 0 0 5px #eee;">
<div class="display-date-box">{{ dateString }}</div>
<div class="display-date-box">{{ dayString }}</div>
</div>
</template>
</div>
</template>
<script>
import { prefixIntrger } from '@/utils/date';
import SystemTime from '@/views/components/systemTime/index';
import { timeFormat } from '@/utils/date';
//
export default {
name: 'MenuSystemTime',
components: {
SystemTime
},
props: {
offset: {
type: Number,
required: true
}
},
data() {
return {
time: '00:0000',
dateString: '00/00/00',
dayString: ''
};
},
computed: {
isShowSystemTime() {
return this.$route.params.mode == 'demon' ||
this.$route.params.mode == 'dp' ||
this.$route.params.mode == 'plan' ||
this.$route.params.mode == 'script' ||
this.$route.params.mode == 'practice' ||
!this.$route.params.mode;
},
pause() {
return this.$store.state.scriptRecord.simulationPause;
},
isDisplay() {
return this.$route.path.includes('displayCity') || this.$route.path.includes('scriptdisplayCity');
},
isShowDate() { // 西 线
return (this.$route.query.lineCode == 10 || this.$route.query.lineCode == 11) && this.isDisplay;
},
top() {
return this.isShowDate ? (this.$route.query.prdType == '05' ? 5 : this.offset - 10) : this.offset;
},
newRight() {
return this.isShowDate ? this.$store.state.config.width - 420 : this.$store.state.config.width / 2 - 55;
}
},
watch: {
'$store.state.training.initTime': function (initTime) {
const date = new Date(initTime);
this.initDate(date);
},
'$store.state.socket.simulationTimeSync': function (time) { // 仿
this.$store.dispatch('training/setInitTime', +new Date(`${new Date().toLocaleDateString()} ${timeFormat(time)}`));
const date = new Date(+new Date(`${new Date().toLocaleDateString()} ${timeFormat(time)}`));
this.initDate(date);
}
},
mounted() {
const initTime = this.$store.state.training.initTime;
if (initTime > 0) {
const date = new Date(initTime);
this.initDate(date);
}
},
methods: {
initDate(date) {
this.time = `${prefixIntrger(date.getHours(), 2)}:${prefixIntrger(date.getMinutes(), 2)}${prefixIntrger(date.getSeconds(), 2)}`;
const years = date.getFullYear() + '';
let months = date.getMonth() + 1 + '';
let dates = date.getDate() + '';
if (months.length < 2) { months = '0' + months; }
if (dates.length < 2) { dates = '0' + dates; }
this.dateString = dates + '/' + months + '/' + years.slice(2);
const day = date.getDay();
switch (day) {
case 0:
this.dayString = '星 期 日';
break;
case 1:
this.dayString = '星 期 一';
break;
case 2:
this.dayString = '星 期 二';
break;
case 3:
this.dayString = '星 期 三';
break;
case 4:
this.dayString = '星 期 四';
break;
case 5:
this.dayString = '星 期 五';
break;
case 6:
this.dayString = '星 期 六';
}
}
}
};
</script>
<style rel="stylesheet/scss" lang="scss" scoped>
@import "src/styles/mixin.scss";
.display-card {
z-index: 9;
display: inline;
position: absolute;
}
.display-pause {
font-size: 21px;
font-weight: bold;
color: yellow;
}
.display-time{
padding: 3px 5px;
border: 1px solid rgba(255,255,255,.2);
box-shadow: 0 2px 12px 0 rgba(255,255,255,.3);
border-radius: 3px;
}
.display-card .el-row {
line-height: 32px !important;
}
.display-date-box{
height: 29px;
line-height: 29px;
background: #404040;
color: #1DEA1E;
text-align: center;
}
</style>

View File

@ -0,0 +1,58 @@
import ConstConfig from '@/scripts/ConstConfig';
import Cookies from 'js-cookie';
import store from '@/store/index';
export function covertMemberData (activeTrainList, resp) {
let lastData = JSON.stringify(resp);
const roleTypeList = ConstConfig.ConstSelect.roleTypeNew;
roleTypeList.forEach(function(element) {
const rolename = element.value;
if (Cookies.get('user_lang') == 'en') {
lastData = lastData.replace(new RegExp(rolename, 'g'), element.enLabel);
} else {
lastData = lastData.replace(new RegExp(rolename, 'g'), element.label);
}
});
lastData = JSON.parse(lastData);
const lastMemberList = [];
// const electricDispatcherList = [];
const deviceListData = [[], [], [], [], [], [], [], []];
const driverList = [];
lastData.forEach((member, index)=>{
if (member.userId && member.userId == store.state.user.id) {
member.disabled = true;
member.userName = store.state.user.nickname;
store.dispatch('training/setOrignalUserRoleId', member.id);
} else {
member.disabled = false;
}
const userName = member.userName ? '-' + member.userName : '';
const name = member.name ? '-' + member.name : '';
if (member.deviceCode) {
const device = store.getters['map/getDeviceByCode'](member.deviceCode);
member.deviceName = device.name || device.groupNumber;
member.label = member.type + member.deviceName + name + userName;
member.normalName = member.type + member.deviceName + name;
} else {
member.deviceName = '';
member.label = member.type + name + userName;
member.normalName = member.type + name;
}
const deviceType = ['行调', '通号', '行值', '司机', '车辆段信号楼', '上级部门', '电力调度', '停车场信号楼'];
const deviceTypeIndex = deviceType.indexOf(member.type);
if (deviceTypeIndex >= 0) {
if (deviceTypeIndex == 3) {
if (activeTrainList.length > 0) {
if (activeTrainList.includes(member.deviceCode)) {
deviceListData[deviceTypeIndex].push(member);
}
}
lastMemberList.push(member);
driverList.push(member);
} else {
deviceListData[deviceTypeIndex].push(member);
lastMemberList.push(member);
}
}
});
return {lastMemberList:lastMemberList, deviceListData:deviceListData, driverList:driverList};
}

View File

@ -0,0 +1,373 @@
<template>
<!-- v-dialogDrag -->
<el-dialog
v-dialogLoading="dialogLoading"
:title="title"
:visible.sync="dialogShow"
width="100%"
:before-close="doClose"
:close-on-click-modal="false"
:modal="false"
fullscreen
>
<div :id="runPlanId" v-loading="loading" />
</el-dialog>
</template>
<script>
import { mapGetters } from 'vuex';
import { getPublishMapInfo } from '@/api/jmap/map';
import { timeFormat } from '@/utils/date';
import echarts from 'echarts';
import {toTimeStamp, formatDuring} from '@/utils/date';
//
export default {
name: 'RunPlanView',
props: {
group: {
type: String,
required: true
}
},
data() {
return {
dialogShow: false,
loading: true,
runPlanId: 'run-plan-view',
myChart: null,
PlanConvert: {},
series: [],
option: {
title: {
text: '',
left: 'center'
},
grid: {
top: '30px',
left: '120px',
right: '40px',
bottom: '80px',
containLabel: true,
backgroundColor: 'floralwhite'
},
toolbox: {
// right: '20px',
// feature: {
// dataZoom: {
// yAxisIndex: 'none'
// },
// restore: {},
// saveAsImage: {}
// }
},
tooltip: {
axisPointer: {
trigger: 'item',
type: 'cross'
},
formatter: this.axisTooltip,
borderWidth: 1
},
xAxis: [
{
type: 'category',
boundaryGap: false,
data: [],
axisLine: {
onZero: false,
lineStyle: {
width: 2,
color: '#d14a61'
}
},
axisLabel: {
formatter: this.xAxisLableFormat,
textStyle: {
color: '#333'
}
},
axisPointer: {
snap: true,
label: {
formatter: this.xAxisPointFormat,
backgroundColor: 'rgb(255,0,0,0.5)',
color: 'white'
}
}
}
],
yAxis: {
type: 'value',
splitLine: {
show: false
},
axisTick: {
show: false
},
axisLine: {
onZero: false,
lineStyle: {
width: 2,
color: '#d14a61'
}
},
axisLabel: {
interval: 'auto',
formatter: this.yAxisLableFormat
},
axisPointer: {
xAxisIndex: 'all',
label: {
formatter: this.yAxisPointFormat,
backgroundColor: 'rgb(0,100,0,0.5)',
color: 'white'
}
},
min: 0,
max: 0
},
// graphic: {
// type: 'line',
// progressive: true
// },
series: [],
dataZoom: [
{
type: 'inside'
},
{
fiterMode: 'filter',
handleIcon: 'M10.7,11.9v-1.3H9.3v1.3c-4.9,0.3-8.8,4.4-8.8,9.4c0,5,3.9,9.1,8.8,9.4v1.3h1.3v-1.3c4.9-0.3,8.8-4.4,8.8-9.4C19.5,16.3,15.6,12.2,10.7,11.9z M13.3,24.4H6.7V23h6.6V24.4z M13.3,19.6H6.7v-1.4h6.6V19.6z',
handleSize: '80%',
handleStyle: {
color: '#fff',
shadowBlur: 3,
shadowColor: 'rgba(0, 0, 0, 0.6)',
shadowOffsetX: 2,
shadowOffsetY: 2
},
bottom: '25px'
}
]
},
absoluteTime: 2 * 3600,
indexKmRangeMap: {},
runPlanData: {},
dialogLoading: false,
initialPlanData: []
};
},
computed: {
...mapGetters('runPlan', [
'stations'
]),
title() {
return this.$t('display.runPlan.previewRunDiagram');
}
},
watch: {
'$store.state.runPlan.planLoadedCount': async function () {
try {
await this.loadChartPage();
if (this.dialogShow) {
await this.loadInitData(this.series);
}
} catch (e) {
console.error(e);
} finally {
this.loading = false;
}
},
'$store.state.runPlan.planUpdateCount': function () {
this.updateRunPlanData(this.$store.state.runPlan.updateData);
},
'$store.state.app.windowSizeCount': function() {
this.reSize({ width: this.$store.state.app.width, height: this.$store.state.app.height - 55 });
}
},
mounted() {
getPublishMapInfo(this.$route.query.mapId).then(res=>{
this.PlanConvert = this.$theme.loadPlanConvert(res.data.lineCode);
this.initialPlanData = this.$store.state.runPlan.planData;
this.loadChartPage();
});
},
beforeDestroy() {
if (this.myChart && this.myChart.isDisposed) {
this.myChart.dispose();
this.myChart = null;
}
},
methods: {
async doShow() {
try {
this.dialogLoading = true;
this.dialogShow = true;
this.loadInitData(this.series);
} catch (e) {
console.error(e);
} finally {
this.dialogLoading = false;
}
},
async doClose() {
this.dialogShow = false;
},
async loadChartPage() {
const stations = this.$store.state.runPlan.stations;
const planData = this.$store.state.runPlan.planData;
this.series = [];
this.kmRangeCoordMap = this.PlanConvert.convertStationsToMap(stations);
this.pushModels(this.series, [this.PlanConvert.initializeYaxis(stations)]);
this.pushModels(this.series, this.PlanConvert.convertDataToModels(planData, stations, this.kmRangeCoordMap, { width: 1, color: '#000' }));
},
async loadInitData(series) {
this.myChart && this.myChart.showLoading();
await this.xAxisInit();
await this.yAxisInit();
await this.loadInitChart(series);
this.myChart && this.myChart.hideLoading();
},
loadInitChart(series) {
return new Promise((resolve, reject) => {
try {
if (this.myChart && this.myChart.isDisposed) {
this.myChart.clear();
}
let startValue = 3600 + this.PlanConvert.TranslationTime;
const offsetTime = 3600;
const initTime = toTimeStamp(formatDuring(this.$store.state.training.initTime));
startValue = initTime - this.PlanConvert.TranslationTime;
this.option.dataZoom[0].startValue = this.option.dataZoom[1].startValue = startValue - offsetTime;
this.option.dataZoom[0].endValue = this.option.dataZoom[1].endValue = startValue + offsetTime;
this.option.series = series;
this.myChart = echarts.init(document.getElementById(this.runPlanId));
if (this.myChart) {
this.myChart.setOption(this.option);
this.reSize({ width: document.documentElement.clientWidth - 10, height: document.documentElement.clientHeight - 55 });
this.myChart.on('click', this.mouseClick);
}
resolve(true);
} catch (error) {
reject(error);
}
});
},
updateRunPlanData(data) {
const stations = this.$store.state.runPlan.stations;
const planData = this.$store.state.runPlan.planData;
const initialPlanData = this.$store.state.runPlan.initialPlanData;
if (data[0] && initialPlanData[data[0].serviceNumber]) {
Object.keys(initialPlanData[data[0].serviceNumber].trainMap).forEach(item => {
if (initialPlanData[data[0].serviceNumber].trainMap[item + ''].tripNumber == data[0].tripNumber) {
data[0].directionCode = initialPlanData[data[0].serviceNumber].trainMap[item + ''].directionCode;
}
});
}
if (data[0]) {
data[0].secondTime = data[0].second;
}
this.series = this.PlanConvert.updateDataToModels(data, stations, this.kmRangeCoordMap,
planData, this.series, { color: '#FF00DE', width: 2 }
);
this.myChart && this.myChart.setOption({ series: this.series });
},
pushModels(series, models) {
if (models && models.length) {
models.forEach(elem => {
if (elem) {
series.push(elem);
}
});
}
return series;
},
popModels(series, models) {
if (models && models.length) {
models.forEach(elem => {
const index = series.indexOf(elem);
if (index >= 0) {
series.split(index, 1);
}
});
}
return series;
},
xAxisPointFormat(params) {
return timeFormat(params.value);
},
yAxisPointFormat(params) {
return this.PlanConvert.computedFormatYAxis(this.stations, params);
},
xAxisLableFormat(value, index) {
if (value % 60 === 0) {
return timeFormat(value);
}
},
yAxisLableFormat(value, index) {
return '';
},
xAxisInit() {
const list = [];
for (var time = this.PlanConvert.TranslationTime; time < 3600 * 24 + this.PlanConvert.TranslationTime; time++) {
list.push(time);
}
this.option.xAxis[0].data = list;
},
yAxisInit() {
if (Object.keys(this.PlanConvert).length) {
this.option.yAxis.min = this.PlanConvert.computedYaxisMinValue(this.stations);
this.option.yAxis.max = this.PlanConvert.computedYaxisMaxValue(this.stations);
}
},
axisTooltip(param) {
const station = this.stations[Math.floor((param.data[1] - this.PlanConvert.EdgeHeight) / this.PlanConvert.CoordMultiple)] || { name: '', kmRange: '' };
return [
`Point Data <hr size=1 style="margin: 3px 0">`,
`${this.$t('display.runPlan.stationName')}: ${station.name}<br>`,
`${this.$t('display.runPlan.stationMark')}: ${station.kmRange} km <br>`,
`${this.$t('display.runPlan.arrivalTime')}: ${timeFormat(param.data[0] + this.PlanConvert.TranslationTime)} (${param.data[0]})<br>`
].join('');
},
settingExac(data) {
this.absoluteTime = Math.abs(parseInt(data.endValue) - parseInt(data.startValue)) / 1000;
this.myChart && this.myChart.setOption({
xAxis: this.option.xAxis,
yAxis: this.option.yAxis
});
this.myChart && this.myChart.dispatchAction({
type: 'dataZoom',
dataZoomIndex: [0, 1],
startValue: parseInt(data.startValue / 1000),
endValue: parseInt(data.endValue / 1000)
});
},
run(start) {
this.myChart && this.myChart.dispatchAction({
type: 'dataZoom',
dataZoomIndex: [0, 1],
startValue: parseInt(start - this.absoluteTime / 2),
endValue: parseInt(start + this.absoluteTime / 2)
});
this.loadInitData(this.series);
},
reSize(opt) {
if (this.myChart) {
this.myChart.resize({ width: opt.width, height: opt.height, silent: false });
}
}
}
};
</script>
<style scoped rel="stylesheet/scss" lang="scss">
/deep/ {
.el-dialog__body {
padding: 0px !important;
background-color: floralwhite !important;
}
}
</style>

View File

@ -157,19 +157,19 @@ export default {
</script> </script>
<style lang="scss" scope> <style lang="scss" scope>
.content-route /deep/ .el-dialog__body {
width: 100%;
height:100%;
flex:1;
padding: 0 !important;
overflow: hidden !important;
}
.content-route{ .content-route{
display: flex; display: flex;
flex-direction: column; flex-direction: column;
/deep/ {
.el-dialog__body{
width: 100%;
height:100%;
flex:1;
padding: 0;
overflow: hidden;
}
}
} }
.content-box{ .content-box{
display:flex; display:flex;
height:100%; height:100%;

View File

@ -15,17 +15,17 @@
border border
> >
<el-table-column prop="name" :label="this.$t('planMonitor.runGraphName')" /> <el-table-column prop="name" :label="this.$t('planMonitor.runGraphName')" />
<el-table-column v-if="!(/^\/design\/userlist/.test(this.$route.fullPath))" :label="this.$t('global.status')"> <!-- <el-table-column v-if="!(/^\/design\/userlist/.test(this.$route.fullPath))" :label="this.$t('global.status')">
<template slot-scope="scope"> <template slot-scope="scope">
<el-tag>{{ handlerStatus(scope.row) }}</el-tag> <el-tag>{{ handlerStatus(scope.row) }}</el-tag>
</template> </template>
</el-table-column> </el-table-column> -->
<el-table-column <!-- <el-table-column
v-if="!(/^\/design\/userlist/.test(this.$route.fullPath))" v-if="!(/^\/design\/userlist/.test(this.$route.fullPath))"
prop="explanation" prop="explanation"
show-overflow-tooltip show-overflow-tooltip
:label="this.$t('planMonitor.explanation')" :label="this.$t('planMonitor.explanation')"
/> /> -->
<el-table-column :label="this.$t('planMonitor.creationDate')"> <el-table-column :label="this.$t('planMonitor.creationDate')">
<template slot-scope="scope"> <template slot-scope="scope">
<el-tag type="success">{{ handleTime(scope.row.createTime) }}</el-tag> <el-tag type="success">{{ handleTime(scope.row.createTime) }}</el-tag>
@ -33,7 +33,8 @@
</el-table-column> </el-table-column>
<el-table-column :label="this.$t('global.operate')" width="400"> <el-table-column :label="this.$t('global.operate')" width="400">
<template slot-scope="scope"> <template slot-scope="scope">
<el-button v-if="scope.row.status !=='1'" size="mini" class="button_box" type="success" :loading="scope.row.runplanLoading" @click="handleConfirm(scope.row)">{{ $t('planMonitor.load') }}</el-button> <!-- {{ $t('planMonitor.load') }} -->
<el-button v-if="scope.row.status !=='1'" size="mini" class="button_box" type="success" :loading="scope.row.runplanLoading" @click="handleConfirm(scope.row)">编制</el-button>
<el-button v-if="isCreate && scope.row.status !=='1'" size="mini" class="button_box" type="primary" @click="handleEdit(scope.row)">{{ $t('planMonitor.modifyName') }}</el-button> <el-button v-if="isCreate && scope.row.status !=='1'" size="mini" class="button_box" type="primary" @click="handleEdit(scope.row)">{{ $t('planMonitor.modifyName') }}</el-button>
<el-button v-if="isCreate && scope.row.status !=='1'" size="mini" class="button_box" type="danger" @click="handleDelete(scope.row)">{{ $t('global.delete') }}</el-button> <el-button v-if="isCreate && scope.row.status !=='1'" size="mini" class="button_box" type="danger" @click="handleDelete(scope.row)">{{ $t('global.delete') }}</el-button>
<el-button v-if="isCreate && scope.row.status ==='0' && hasRelease" size="mini" class="button_box" type="primary" @click="handlePublish(scope.row)">{{ hasRelease?$t('global.release'):$t('planMonitor.applyRelease') }}</el-button> <el-button v-if="isCreate && scope.row.status ==='0' && hasRelease" size="mini" class="button_box" type="primary" @click="handlePublish(scope.row)">{{ hasRelease?$t('global.release'):$t('planMonitor.applyRelease') }}</el-button>

View File

@ -40,7 +40,8 @@
</el-form> </el-form>
</el-row> </el-row>
</el-tab-pane> </el-tab-pane>
<el-tab-pane label="导入运行图" name="three"> <!-- v-if="hasRelease" -->
<!-- <el-tab-pane label="导入运行图" name="three">
<el-row> <el-row>
<el-button type="text" class="uploadDemo"> <el-button type="text" class="uploadDemo">
<input <input
@ -53,7 +54,7 @@
<i class="el-icon-plus" /> <i class="el-icon-plus" />
</el-button> </el-button>
</el-row> </el-row>
</el-tab-pane> </el-tab-pane> -->
</el-tabs> </el-tabs>
</div> </div>
<span slot="footer" class="dialog-footer"> <span slot="footer" class="dialog-footer">
@ -117,6 +118,10 @@ export default {
{ required: true, message: this.$t('rules.enterTheNameOfTheRunGraph'), trigger: 'blur' } { required: true, message: this.$t('rules.enterTheNameOfTheRunGraph'), trigger: 'blur' }
] ]
}; };
},
hasRelease() {
return this.$store.state.user.roles.includes('04') ||
this.$store.state.user.roles.includes('05');
} }
}, },
mounted() { mounted() {

458
src/views/test/index.vue Normal file
View File

@ -0,0 +1,458 @@
<template>
<div>
<div :id="iscsId" v-loading="loading" :style="{ width: width +'px', height: height +'px',background:'#425a74' }" class="iscs-canvas" />
<el-button-group>
<el-button @click="doRemove"> 删除 </el-button>
<el-button @click="doBinding">绑定</el-button>
<el-button @click="doUnbinding">解绑</el-button>
<el-button @click="doSource"> 源数据 </el-button>
</el-button-group>
<el-input type="textarea" :rows="6" v-model="json" />
</div>
</template>
<script>
import Vue from 'vue';
import Iscs from '@/iscs_new/map';
import orders from '@/iscs_new/utils/orders';
import shapeType from '@/iscs_new/constant/shapeType';
import ShapeBuilder from '@/iscs_new/plugins/shapeBuilder';
import ShapeProperty from '@/iscs_new/plugins/shapeProperty';
import ShapeContextMenu from '@/iscs_new/plugins/shapeContextMenu';
import { mapGetters } from 'vuex';
import { exitFullscreen } from '@/utils/screen';
import * as utils from '@/iscs_new/utils/utils';
export default {
data() {
return {
json: "{}",
dataZoom: {
offsetX: '0',
offsetY: '0',
scaleRate: '1'
},
config: {
scaleRate: '1',
origin: {
x: 0,
y: 0
}
},
selected: null, //
loading: false,
};
},
computed: {
...mapGetters('iscs', [
'iscs'
]),
iscsId() {
return ['iscs', (Math.random().toFixed(5)) * 100000].join('_');
},
width() {
return document.documentElement.clientWidth;
},
height() {
return document.documentElement.clientHeight - 200;
}
},
watch: {
'$store.state.config.canvasSizeCount': function (val) {
this.resize();
},
'$store.state.socket.equipmentStatus': function (val) {
if (val.length) {
this.stateMessage(val);
}
}
},
mounted() {
this.init();
},
beforeDestroy() {
this.destroy();
},
methods: {
init() {
document.getElementById(this.iscsId).oncontextmenu = function (e) {
return false;
};
this.$iscs = new Iscs({
dom: document.getElementById(this.iscsId),
draw: true,
config: {
renderer: 'canvas',
width: this.width,
height: this.height
},
options: {
scaleRate: 1,
offsetX: 0,
offsetY: 0
},
plugins: [
ShapeBuilder,
ShapeProperty,
ShapeContextMenu
]
});
Vue.prototype.$iscs = this.$iscs;
this.$iscs.on('viewLoaded', this.onUpdate, this);
this.$iscs.on('contextmenu', this.onContextMenu, this);
this.$iscs.on('click', this.onClick, this);
this.$iscs.on('keyboard', this.onKeyboard, this);
const opts = { panEnable: true, zoomEnable: true, keyEnable: true, draggle: true, selecting: true, selectable: true, reflect: true }
this.$iscs.setMap([
{
type: 'Device',
name: 'test',
isActive: false,
isFocus: false,
shapeList: [
{
name: 'a',
type: 'Rect',
shape: {},
style: {
fill: 'red',
stroke: 'black'
},
stateList: [
{ status: 'st1', hide: false, shape: {}, style:{ fill: 'yellow', stroke: 'black'} },
{ status: 'st2', hide: false, shape: {}, style:{ fill: 'blue', stroke: 'black'} }
]
},
{
name: 'b',
type: 'Circle',
shape: {},
style: {
fill: 'red',
stroke: 'black'
},
stateList: [
{ status: 'st1', hide: false, shape: {}, style:{ fill: 'yellow', stroke: 'black'} },
{ status: 'st2', hide: false, shape: {}, style:{ fill: 'blue', stroke: 'black'} }
]
},
],
stateList: [
{ status: 's1', frameList: [[{name: 'a', status: 'st1'}, {name: 'b', status: 'st1'}], [{name: 'a', status: 'st2'}]], weight: 2, loop: true, delay: 2000, time: 200, needDefault: true },
{ status: 's2', frameList: [[{name: 'a', status: 'st2'}, {name: 'b', status: 'st1'}], [{name: 'a', status: 'st1'}, {name: 'b', status: 'st2'}]], weight: 1, loop: true, delay: 5000, time: 500, needDefault: false }
]
}
], {
elementList: [
{
code: '1',
name: 'a',
type: 'Rect',
base: {
scale: [1, 1],
position: [0, 0],
rotation: 0,
hide: false,
},
shape: {
x: 100,
y: 100,
width: 30,
height: 30
},
style: {
fill: 'red',
stroke: 'black'
},
composeCode: '100'
},
{
code: '2',
name: 'b',
type: 'Circle',
base: {
scale: [1, 1],
position: [0, 0],
rotation: 0,
hide: false,
},
shape: {
cx: 100,
cy: 100,
r: 10,
},
style: {
fill: 'red',
stroke: 'black'
},
composeCode: '100'
},
{
code: '3',
name: 'a',
type: 'Rect',
base: {
scale: [1, 1],
position: [0, 0],
rotation: Math.PI/4,
hide: false,
},
shape: {
x: 200,
y: 100,
width: 30,
height: 30
},
style: {
fill: 'red',
stroke: 'black'
},
composeCode: '101'
},
{
code: '4',
name: 'b',
type: 'Circle',
base: {
scale: [1, 1],
position: [0, 0],
rotation: 0,
scale: [0.5, 0.5],
hide: false,
},
shape: {
cx: 200,
cy: 100,
r: 10,
},
style: {
fill: 'red',
stroke: 'black'
},
composeCode: '101'
},
{
code: '5',
name: 'c',
type: 'Droplet',
base: {
scale: [0.5, 0.5],
position: [0, 0],
rotation: 0,
hide: false,
},
shape: {
cx: 300,
cy: 200,
width: 30,
height: 30,
},
style: {
fill: 'red',
stroke: 'black'
},
composeCode: ''
},
{
code: '6',
name: 'd',
type: 'Droplet',
base: {
scale: [1, 1],
position: [0, 0],
rotation: Math.PI/2,
hide: false,
},
shape: {
cx: 400,
cy: 200,
width: 20,
height: 20,
},
style: {
fill: 'red',
stroke: 'black'
},
composeCode: ''
},
{
code: '7',
name: 'a',
type: 'Rect',
base: {
scale: [1, 1],
position: [100, 0],
rotation: Math.PI/2,
hide: false,
},
shape: {
x: 100,
y: 100,
width: 30,
height: 60
},
style: {
fill: 'red',
stroke: 'black'
},
composeCode: ''
},
],
composeList: [
{
code: '100',
type: 'Device',
elementCodes: ['1', '2'],
base: {
scale: [0.5, 0.5],
position: [100, 100],
rotation: Math.PI/2,
hide: false,
},
composeCode: '1000',
},
{
code: '101',
type: 'Device',
elementCodes: ['3', '4'],
base: {
scale: [1, 1],
position: [200, 0],
rotation: 0,
hide: false,
},
composeCode: '1000'
},
{
code: '1000',
type: 'Device',
base: {
scale: [1, 1],
position: [0, 0],
rotation: 0,
hide: false
},
elementCodes: ['100', '101'],
composeCode: ''
}
]
}, opts);
window.document.oncontextmenu = function () {
return false;
};
},
onUpdate(e) {
this.$iscs.update([
{ status: 's1', code: '100', type: 'Device' },
{ status: 's2', code: '101', type: 'Device' }
]);
setTimeout(e => {
// this.$iscs.update([
// { status: 's0', code: '100', type: 'Device' },
// { status: 's0', code: '101', type: 'Device' },
// ])
}, 15000)
},
//
onKeyboard(hook) {
console.log(hook);
},
//
onClick(em) {
this.selected = em;
},
//
onContextMenu(em) {
this.selected = em;
},
doRemove() {
if (this.selected) {
this.$iscs.render([
{
model: this.selected.model,
action: { order: orders.Delete, shapeType: shapeType.Compose }
}
]);
}
},
doBinding() {
const controller = this.$iscs.getController();
const storage = controller.getStorage()
const values = storage.values();
if (values.length > 1) {
const elem = {
model: {
code: utils.getUID('compose'),
type: 'Device',
elementCodes: values.map(el => el.model.code),
base: {
scale: [1, 1],
position: [0, 0],
rotation: 0,
hide: false,
},
composeCode: '',
},
action: { order: orders.Binding, shapeType: shapeType.Compose }
}
this.$iscs.render([elem]);
} else {
this.$message.info('请选择两个及其以上数目元素');
}
},
doUnbinding() {
if (this.selected) {
this.$iscs.render([
{
model: this.selected.model,
action: { order: orders.Unbinding, shapeType: shapeType.Compose }
}
]);
}
},
doSource() {
console.log(this.$iscs.getSource());
this.json = JSON.stringify(this.$iscs.getSource())
},
resize() {
this.$nextTick(() => {
this.$iscs && this.$iscs.resize({ width: this.width, height: this.height });
});
},
back() {
this.group = this.$route.query.group;
this.$store.dispatch('training/over').then(() => {
putJointTrainingSimulationUserNew(this.group).then(() => {
this.$router.replace({ path: `/trainroom`, query: { group: this.group, lineCode:this.$route.query.lineCode } });
exitFullscreen();
});
});
},
destroy() {
if (this.$iscs) {
this.$iscs.destroy();
this.$iscs = null;
Vue.prototype.$iscs = null;
}
},
stateMessage(val) {
this.$iscs && this.$iscs.setDeviceStatus(val);
}
}
};
</script>
<style rel="stylesheet/scss" lang="scss" scoped>
.iscs-button{
position: absolute;
float: right;
right: 20px;
bottom: 15px;
}
.iscs-canvas{
}
</style>