diff --git a/src/assets/ibp_images/ibp_bg.png b/src/assets/ibp_images/ibp_bg.png new file mode 100644 index 000000000..ba2d03cbf Binary files /dev/null and b/src/assets/ibp_images/ibp_bg.png differ diff --git a/src/assets/ibp_images/red_button.png b/src/assets/ibp_images/red_button.png new file mode 100644 index 000000000..4dbb2bc6a Binary files /dev/null and b/src/assets/ibp_images/red_button.png differ diff --git a/src/assets/ibp_images/red_button_on.png b/src/assets/ibp_images/red_button_on.png new file mode 100644 index 000000000..bcd9f6dd2 Binary files /dev/null and b/src/assets/ibp_images/red_button_on.png differ diff --git a/src/ibp/constant/deviceRender.js b/src/ibp/constant/deviceRender.js new file mode 100644 index 000000000..f304fe19c --- /dev/null +++ b/src/ibp/constant/deviceRender.js @@ -0,0 +1,44 @@ +import deviceType from './deviceType'; + +const deviceRender = {}; + +/** Text渲染配置*/ +deviceRender[deviceType.Text] = { + zlevel: 1 +}; + +/** SquareButton渲染配置*/ +deviceRender[deviceType.SquareButton] = { + zlevel: 1 +}; + +/** WarnButton渲染配置*/ +deviceRender[deviceType.WarnButton] = { + zlevel: 1 +}; + +/** Arrow渲染配置*/ +deviceRender[deviceType.Arrow] = { + zlevel: 1 +}; + +/** RotatingButton渲染配置*/ +deviceRender[deviceType.RotatingButton] = { + zlevel: 1 +}; + +/** Tip渲染配置*/ +deviceRender[deviceType.Tip] = { + zlevel: 1 +}; + +/** BackGround渲染配置*/ +deviceRender[deviceType.Background] = { + zlevel: 0 +}; + +/** CircularLamp渲染配置 */ +deviceRender[deviceType.CircularLamp] = { + zlevel: 1 +}; +export default deviceRender; diff --git a/src/ibp/constant/deviceType.js b/src/ibp/constant/deviceType.js new file mode 100644 index 000000000..18fb9e2be --- /dev/null +++ b/src/ibp/constant/deviceType.js @@ -0,0 +1,12 @@ +const deviceType = { + Text: 'Text', + SquareButton: 'SquareButton', + WarnButton: 'WarnButton', + Arrow: 'Arrow', + RotatingButton: 'RotatingButton', + Tip: 'Tip', + Background: 'Background', + CircularLamp: 'CircularLamp' +}; + +export default deviceType; diff --git a/src/ibp/constant/ibpData.js b/src/ibp/constant/ibpData.js new file mode 100644 index 000000000..656fc75cc --- /dev/null +++ b/src/ibp/constant/ibpData.js @@ -0,0 +1,51 @@ + +const ibpData = { + background: { + type: 'Background', + code: 'bg_0000' + }, + arrowList: [ + { + type: 'Arrow', + code: '1111_arrow', + point: { + x: 510, + y: 400 + }, + length: 488, + stroke: '#0000CD', + fill: '#0000CD', + lineWidth: 1, + direction: '上行', + stationstandDirection: '三桥方向' + }, + { + type: 'Arrow', + code: '1222_arrow', + point: { + x: 510, + y: 200 + }, + length: 488, + stroke: '#0000CD', + fill: '#0000CD', + lineWidth: 1, + direction: '下行', + stationstandDirection: '车辆段方向' + } + ], + circularLampList: [ + { + type: 'CircularLamp', + code: '1333_lamp', + point: { + x: 600, + y: 600 + }, + r: 40, + fillColor: '#332C22' + } + ] +}; + +export default ibpData; diff --git a/src/ibp/ibpPan.js b/src/ibp/ibpPan.js new file mode 100644 index 000000000..a09561fee --- /dev/null +++ b/src/ibp/ibpPan.js @@ -0,0 +1,306 @@ +import zrender from 'zrender'; +import * as zrUtil from 'zrender/src/core/util'; +import localStore from 'storejs'; +import Options from './options'; +import MouseController from './mouseController'; +import Painter from './painter'; +import deviceState from '../jmap/constant/deviceState'; +import deviceType from './constant/deviceType'; +import {calculateDCenter, createBoundingRect, deviceFactory} from './utils/parser'; + +const renderer = 'canvas'; +const devicePixelRatio = 1; + +class IbpPan { + constructor(opts) { + this.methods = opts.methods; + + // 鼠标事件 + this.events = { __Pan: 'pan', __Zoom: 'zoom', Selected: 'selected', Contextmenu: 'contextmenu', DataZoom: 'dataZoom'}; + + // 皮肤参数 + // this.skinCode = ''; + + // 设备数据 + this.ibpDevice = {}; + + // 默认状态 + this.defaultStateDict = this.loadDefaultState(); + + this.initIbpPage(opts); + } + initIbpPage(opts) { + const width = opts.config.width; + const height = opts.config.height; + this.$ibpZr = zrender.init(opts.dom, Object.assign({ renderer, devicePixelRatio, width, height }, opts.config)); + this.$options = new Options(Object.assign({ scaleRate: 1, offsetX: 0, offsetY: 0 }, opts.options || {}), (dataZoom) => { this.$mouseController.trigger(this.events.DataZoom, dataZoom); }); // 缩放 + + this.$painter = new Painter(this); + this.$painter.updateZrSize({width: this.$ibpZr.getWidth(), height: this.$ibpZr.getHeight()}); + this.$painter.updateTransform(this.$options); + + this.optionsHandler = this.setOptions.bind(this); + + this.$mouseController = new MouseController(this); + this.$mouseController.enable(); + + this.$mouseController.on(this.events.__Pan, this.optionsHandler); + this.$mouseController.on(this.events.__Zoom, this.optionsHandler); + + } + loadDefaultState() { + const defaultStateDict = {}; + + zrUtil.each(Object.keys(deviceState), (type) => { + defaultStateDict[type] = {}; + zrUtil.each(Object.keys(deviceState[type] || {}), (state) => { + defaultStateDict[type][state] = deviceState[type][state].Default; + }, this); + }, this); + + return defaultStateDict; + } + setMap(config, ibpDevice) { + // 保存皮肤类型 + if (config.config) { + this.$options.scaleRate = config.scaling; + this.$options.offsetX = config.origin.x; + this.$options.offsetY = config.origin.y; + this.$painter.updateTransform({ scaleRate: config.scaling, offsetX: config.origin.x, offsetY: config.origin.y }); + } + + // 保存原始数据 + this.data = config; + + // 解析地图数据 + this.ibpDevice = ibpDevice; + + // 加载对应皮肤 + // this.style = this.loadStyle(this.skinCode); + + // 数据加载完成 回调 + if (this.methods.dataLoaded instanceof Function) { this.methods.dataLoaded(this.ibpDevice); } + + // 初次渲染视图 + this.$painter.repaint(this.ibpDevice); + + // 视图加载完成 回调 + if (this.methods.viewLoaded instanceof Function) { this.methods.viewLoaded(this.ibpDevice); } + + } + + setDefaultState() { + const list = []; + this.update(list); + if (this.methods.stateLoaded instanceof Function) { this.methods.stateLoaded(list); } + } + + setOptions(opts) { + const options = this.pullBack(opts); + this.$options.update(options); + this.$painter.updateTransform(this.$options); + + if (this.$options.disabled == true) { + this.$mouseController.disable(); + } else { + this.$mouseController.enable(opts); + } + + if (this.methods.optionsUpdate instanceof Function) { this.methods.optionsUpdate(this.$options); } + } + + setCenter(deviceCode) { + const device = this.ibpDevice[deviceCode]; + if (device && device.instance) { + var rect = createBoundingRect(device.instance); + var dcenter = calculateDCenter(rect, { width: this.$ibpZr.getWidth(), height: this.$ibpZr.getHeight() }); + this.setOptions(dcenter); + } + } + + setLevelVisible(list) { + this.$painter.setLevelVisible(list); + } + + render(list) { + (list || []).forEach(elem => { + const code = elem.code; + const type = elem._type; + const oDevice = this.ibpDevice[code] || deviceFactory(type, elem); + const nDevice = Object.assign(oDevice || {}, elem); + this.dataSync(nDevice); + this.$painter.delete(oDevice); + if (!elem._dispose) { + this.ibpDevice[code] = nDevice; + this.$painter.add(nDevice); + } + }); + + if (this.methods.viewUpdate instanceof Function) { this.methods.viewUpdate(list); } + } + + // 中间处理 + hookHandle(oDevice, elem) { + const code = elem.code; + const type = elem._type; + // 如果是延时计时,需要保存计数值到全局 + if (type === deviceType.StationCounter) { + let val = '' + elem.val; + if (val === '0' || !elem.val) { + val = elem.val = localStore.get(code) || '0'; + } + + localStore(code, val); + } + for (var prop in elem) { + if (elem[prop] != oDevice[prop]) { + Object.assign(oDevice, elem); + return true; + } + } + + return false; + } + + update(list) { + (list || []).forEach(elem => { + const code = elem.code; + const oDevice = this.ibpDevice[code]; + if (elem.dispose) { + this.$painter.delete(oDevice); + } else { + if (this.hookHandle(oDevice, elem)) { + this.$painter.update(oDevice); + } + } + }); + + if (this.methods.stateUpdate instanceof Function) { this.methods.stateUpdate(list); } + } + + pullBack(payload) { + if (payload.type === 'zoom') { + const zrWidth = this.$ibpZr.getWidth(); + const zrHeight = this.$ibpZr.getHeight(); + const originX = payload.originX || zrWidth / 2; + const originY = payload.originY || zrHeight / 2; + const x = (this.$options.offsetX + originX) / this.$options.scaleRate; + const y = (this.$options.offsetY + originY) / this.$options.scaleRate; + const newScaleRate = this.$options.getScaleRate(payload.scale); + const dx = originX - (x * newScaleRate - this.$options.offsetX); + const dy = originY - (y * newScaleRate - this.$options.offsetY); + payload.dx = dx; + payload.dy = dy; + } + + return payload || {}; + } + + dataSync(model) { + var prop = null; + var type = model._type; + var code = model.code; + + switch (type) { + case deviceType.Link: prop = 'linkList'; break; + } + + const list = this.data[prop] || []; + const idex = list.findIndex(elem => { return elem.code == code; }); + if (list) { + if (model._dispose) { + idex >= 0 && list.splice(idex, 1); + } else { + const elem = list[idex]; + if (elem) { + Object.keys(elem).forEach(key => { + elem[key] = model[key]; + }); + } else { + list.push(Object.assign({}, model)); + } + } + } + } + + getZr() { + return this.$ibpZr; + } + + getEvents() { + return this.events; + } + + getDeviceByCode(code) { + return this.ibpDevice[code]; + } + + getShapeTipPoint(opts) { + const device = this.ibpDevice[opts.code]; + if (device) { + return this.$painter.getShapeTipPoint(device.instance, opts); + } + } + + resize(opt) { + this.$ibpZr.resize(opt); + this.$painter.updateZrSize(opt); + } + + refresh() { + this.$painter.refresh(); + } + + clear() { + this.skinCode = ''; + this.style = {}; + this.ibpDevice = {}; + this.$painter.clear(); + } + + dispose() { + this.off(this.events.Pan, this.optionsHandler); + this.off(this.events.Zoom, this.optionsHandler); + + this.clear(); + + this.$mouseController.dispose(); + this.$ibpZr && zrender.dispose(this.$ibpZr); + this.$painter.dispose(); + } + + on(eventname, cb, context) { + const idx = Object.values(this.events).indexOf(eventname); + if (idx >= 0) { + switch (eventname) { + case this.events.Selected: + this.$mouseController.on(this.events.Selected, cb, context); + break; + case this.events.Contextmenu: + this.$mouseController.on(this.events.Contextmenu, cb, context); + break; + case this.events.DataZoom: + this.$mouseController.on(this.events.DataZoom, cb, context); + break; + } + } + } + + off(eventname, cb) { + const idx = Object.values(this.events).indexOf(eventname); + if (idx >= 0) { + switch (eventname) { + case this.events.Selected: + this.$mouseController.off(this.events.Selected, cb); + break; + case this.events.Contextmenu: + this.$mouseController.off(this.events.Contextmenu, cb); + break; + case this.events.DataZoom: + this.$mouseController.off(this.events.DataZoom, cb); + break; + } + } + } +} +export default IbpPan; diff --git a/src/ibp/mouseController.js b/src/ibp/mouseController.js new file mode 100644 index 000000000..f061602c2 --- /dev/null +++ b/src/ibp/mouseController.js @@ -0,0 +1,201 @@ +import deviceType from './constant/deviceType'; +import Eventful from 'zrender/src/mixin/Eventful'; +import * as eventTool from 'zrender/src/core/event'; +import store from '@/store'; + +class EventModel { + constructor(e) { + this.clientX = e.event.clientX; + this.clientY = e.event.clientY; + + let view = e.target; + while (view) { + if (Object.values(deviceType).includes(view._type)) { + this.deviceCode = view._code; + this.deviceType = view._type; + break; + } + + if (view._subType) { + this.subType = view._subType; + } + if (view._val) { + this.val = view._val; + } + + view = view.parent; + } + } +} + +class MouseController extends Eventful { + constructor(ibp) { + super(); + this.$ibp = ibp; + this.$zr = ibp.getZr(); + this.events = ibp.getEvents(); + this.initHandler(this.$zr); + } + + initHandler(zr) { + if (zr) { + zr.on('click', this.click, this); + zr.on('contextmenu', this.contextmenu, this); + zr.on('mousemove', this.moveEvent, this); + + this.enable = function (opts) { + opts = opts || {}; + this._moveOnMouseMove = opts.moveOnMouseMove || true; + this._zoomOnMouseWheel = opts.zoomOnMouseWheel || false; + this._preventDefaultMouseMove = opts.preventDefaultMouseMove || true; + + this.disable(); + + zr.on('mousedown', this.mousedown, this); + zr.on('mousemove', this.mousemove, this); + zr.on('mouseup', this.mouseup, this); + zr.on('mousewheel', this.mousewheel, this); + }; + + this.disable = function () { + zr.off('mousedown', this.mousedown); + zr.off('mousemove', this.mousemove); + zr.off('mouseup', this.mouseup); + zr.off('mousewheel', this.mousewheel); + }; + + this.dispose = function () { + zr.off('click', this.click); + zr.off('contextmenu', this.contextmenu); + zr.off('mousemove', this.moveEvent); + this.disable(); + }; + + this.isDragging = function () { return this._dragging; }; + } + } + + mousedown(e) { + if (eventTool.notLeftMouse(e)) { + return; + } + + var x = e.offsetX; + var y = e.offsetY; + + this._x = x; + this._y = y; + this._dragging = true; + } + + mousemove(e) { + if (eventTool.notLeftMouse(e) || + !this._moveOnMouseMove || + !this._dragging + ) { + return; + } + + const oldX = this._x; + const oldY = this._y; + + const dx = e.offsetX - oldX; + const dy = e.offsetY - oldY; + + this._x = e.offsetX; + this._y = e.offsetY; + + this._preventDefaultMouseMove && eventTool.stop(e.event); + + this.trigger(this.events.__Pan, { dx, dy, oldX, oldY, newX: this._x, newY: this._y }); + } + + mouseup(e) { + if (!eventTool.notLeftMouse(e)) { + this._dragging = false; + } + } + + mousewheel(e) { + const shouldZoom = this._zoomOnMouseWheel; + const wheelDelta = e.wheelDelta; + const originX = e.offsetX; + const originY = e.offsetY; + + if (wheelDelta === 0 || !shouldZoom) { + return; + } + + if (shouldZoom) { + eventTool.stop(e.event); + let scale = 1; + if (wheelDelta > 0) { + scale = 1; + } else if (wheelDelta < 0) { + scale = -1; + } + + this.trigger(this.events.__Zoom, {type: 'zoom', scale, originX, originY }); + } + } + + click(e) { + var em = this.checkEvent(e); + this.trigger(this.events.Selected, em); + } + + contextmenu(e) { + var em = this.checkEvent(e); + this.trigger(this.events.Contextmenu, em); + } + + moveEvent(e) { + const newEm = new EventModel(e); + const trainDetails = store.state.map.trainDetails; + if (trainDetails) { + if (newEm.deviceType != deviceType.Train || trainDetails.code != newEm.deviceCode) { + var instance = (this.$ibp.getDeviceByCode(trainDetails.code) || {} ).instance; + instance && instance.removeTrainDetail && instance.removeTrainDetail(); + } + } + } + + checkEvent(e) { + var oldEm = new EventModel(this.$zr.curEvent || { event: {} }); + var newEm = new EventModel(e); + if ([1, 3].includes(e.which)) { + // 查找之前和当前鼠标选中的实例 + var oldDevice = this.$ibp.getDeviceByCode(oldEm.deviceCode) || {}; + var newDevice = this.$ibp.getDeviceByCode(newEm.deviceCode) || {}; + var oldInstance = (this.$ibp.getDeviceByCode(oldEm.deviceCode) || {}).instance || {}; + var newInstance = (this.$ibp.getDeviceByCode(newEm.deviceCode) || {}).instance || {}; + + // 如果之前和当前选中的实例不一致 + if (oldInstance != newInstance) { + // 如果实例有取消选择函数并且被点击,则执行取消选中函数 + if (oldInstance.mouseEvent && oldInstance.mouseEvent.mouseout) { + // 视图数据设置点击标志,同步执行 + oldDevice['down'] = false; + oldInstance.mouseEvent['mouseout'](e); + } + + // 如果实例有选中函数并且被点击,则执行选中函数 + if (e.which == 3 && newInstance.mouseEvent && newInstance.mouseEvent.mouseover) { + newDevice['down'] = true; + newInstance.mouseEvent['mouseover'](e); + } + } + + // 保存当前实例到全局 + this.$zr.curEvent = e; + } + + return newEm; + } + + updateDatazoom(zoom) { + this.trigger(this.events.Datazoom, zoom); + } +} + +export default MouseController; diff --git a/src/ibp/options.js b/src/ibp/options.js new file mode 100644 index 000000000..170db21ef --- /dev/null +++ b/src/ibp/options.js @@ -0,0 +1,100 @@ +class Options { + constructor(opts, trigger) { + this.scaleIndex = 0; + this.scaleList = [ + 0.5, 0.6, 0.7, 0.8, 0.9, + 1, 1.2, 1.4, 1.6, 1.8, + 2, 2.2, 2.4, 2.6, 2.8, + 3, 3.2, 3.4, 3.6, 3.8, + 4, 4.2, 4.4, 4.6, 4.8, + 5, 5.2, 5.4, 5.6, 5.8, + 6, 6.2, 6.4, 6.6, 6.8, + 7, 7.2, 7.4, 7.6, 7.8, + 8, 8.2, 8.4, 8.6, 8.8 + ]; + + if (Number.isFinite(opts.scaleRate)) { + const idx = this.scaleList.indexOf(opts.scaleRate); + if (idx >= 0) { + this.scaleIndex = idx; + } + } + + this.scaleRate = opts.scaleRate || this.scaleList[this.scaleIndex]; // 缩放比例 + + this.offsetX = opts.offsetX || 0; // x偏移 + + this.offsetY = opts.offsetY || 0; // y偏移 + + this.throttle = opts.throttle || 100; // 刷新频率 + + this.disabled = false; + + this.moveOnMouseMove = true; + + this.zoomOnMouseWheel = false; + + this.preventDefaultMouseMove = true; + + this.trigger = trigger; + } + + 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)) { + if (Number.isFinite(payload.scale)) { + if ((this.scaleIndex + payload.scale) >= 0 && (this.scaleIndex + payload.scale) < this.scaleList.length) { + this.scaleIndex = this.scaleIndex + payload.scale; + } + } + this.scaleRate = this.scaleList[this.scaleIndex]; + } + + if (Number.isFinite(payload.scaleRate)) { + const idx = this.scaleList.indexOf(payload.scaleRate); + if (idx < 0) { + return; + } + this.scaleIndex = idx; + this.scaleRate = payload.scaleRate; + } + + if (payload.disabled === true || payload.disabled === false) { + this.disabled = payload.disabled; + } + + if (payload.moveOnMouseMove === true || payload.moveOnMouseMove === false) { + this.moveOnMouseMove = payload.moveOnMouseMove; + } + + if (payload.zoomOnMouseWheel === true || payload.zoomOnMouseWheel === false) { + this.zoomOnMouseWheel = payload.zoomOnMouseWheel; + } + + if (this.trigger instanceof Function) { this.trigger(this); } + } + + getScaleRate(scale) { + if (Number.isFinite(scale)) { + if ((this.scaleIndex + scale) >= 0 && (this.scaleIndex + scale) < this.scaleList.length) { + return this.scaleList[this.scaleIndex + scale]; + } + } + return this.scaleList[this.scaleIndex]; + } +} + +export default Options; diff --git a/src/ibp/painter.js b/src/ibp/painter.js new file mode 100644 index 000000000..532276e83 --- /dev/null +++ b/src/ibp/painter.js @@ -0,0 +1,194 @@ +import * as zrUtil from 'zrender/src/core/util'; +import * as vector from 'zrender/src/core/vector'; +import Group from 'zrender/src/container/Group'; +import deviceType from './constant/deviceType'; +import shapefactory from './shape/factory'; +import TransformHandle from './transformHandle'; + +class Painter { + constructor(ibp) { + // 父级实例 + this.$ibp = ibp; + this.$ibpZr = ibp.getZr(); + + // 图层数据 + this.ibpInstanceLevel = {}; + + // 初始图层 + this.initLevels(); + + // 视图控制器 + this.$transformHandle = new TransformHandle(this); + } + + /** + * 初始绘图实例 + * @param {*} dom + * @param {*} config + */ + initLevels() { + + // 添加父级图层 + this.parentLevel = new Group({ name: '__parent__' }); + this.$ibpZr.add(this.parentLevel); + + // 添加子级图层 + zrUtil.each(Object.values(deviceType), (type) => { + const level = new Group({ name: `__${type}__` }); + this.ibpInstanceLevel[type] = level; + this.parentLevel.add(level); + }); + } + + /** + * 重绘视图 + * @param {*} ibpDevice + */ + repaint(ibpDevice) { + // 清空视图 + this.clear(); + + // 创建视图 + Object.values(ibpDevice).forEach(device => { + this.add(device); + }); + } + + /** + * 添加视图 + * @param {*} device + */ + add(device) { + const instance = shapefactory(device, this.$ibp); + if (instance) { + device.instance = instance; + this.$transformHandle.transformView(instance); + this.ibpInstanceLevel[device._type].add(instance); + } + } + + /** + * 删除视图 + * @param {*} device + */ + delete(device) { + const instance = device.instance; + if (instance) { + this.ibpInstanceLevel[device._type].remove(instance); + } + } + + /** + * 更新视图 + * @param {*} device + */ + update(device) { + if (device) { + if (device._dispose) { + this.delete(device); + } else { + const instance = device.instance; + if (instance) { + instance.setState(device); + } + } + } + } + + /** + * 更新transform变化 + * @param {*} opt + */ + updateTransform(opt,) { + this.$transformHandle.updateTransform(opt); + } + + /** + * 更新zrender尺寸 + * @param {*} opt + */ + updateZrSize(opt) { + this.$transformHandle.updateZrSize(opt); + } + + /** + * 过去坐标提示位置 + * @param {*} opts + */ + getShapeTipPoint(instance, opts) { + if (instance) { + var point = instance.getShapeTipPoint(opts); + if (point) { + // 矩阵变换 + var transform = this.$transformHandle.transform; + var transPoint = vector.applyTransform([], [point.x, point.y], transform); + return { + x: transPoint[0], + y: transPoint[1] + }; + } + + } + } + + /** + * 设置图层可见 + * @param {*} code + */ + setLevelVisible(list) { + zrUtil.each(Object.values(deviceType), type => { + const level = this.ibpInstanceLevel[type]; + if (list.includes(type)) { + level.show(); + } else { + level.hide(); + } + }, this); + } + + /** + * 刷新图层 + */ + refresh() { + this.$ibpZr.refresh(); + } + + /** + * 清除图层 + */ + clearLevel(type) { + const level = this.ibpInstanceLevel[type]; + if (level) { + level.removeAll(); + } + } + + /** + * 清除canvas + */ + clear() { + zrUtil.each(Object.values(this.ibpInstanceLevel), (level) => { + level && level.removeAll(); + }, this); + + this.refresh(); + } + + /** + * 销毁图层 + */ + dispose() { + this.ibpInstanceLevel = {}; + this.parentLevel = null; + } + + /** + * 父级图层 + */ + getParentLevel() { + return this.parentLevel; + } + +} + +export default Painter; diff --git a/src/ibp/shape/arrow.js b/src/ibp/shape/arrow.js new file mode 100644 index 000000000..00d133fba --- /dev/null +++ b/src/ibp/shape/arrow.js @@ -0,0 +1,37 @@ +import Polygon from 'zrender/src/graphic/shape/Polygon'; +import Group from 'zrender/src/container/Group'; +import {arrow} from '../../jmap/shape/utils/ShapePoints'; + +class Arrow extends Group { + constructor(model) { + super(); + this.model = model; + this.create(); + } + + create() { + const model = this.model; + const point = arrow(this.model.point.x, this.model.point.y, this.model.length, 10); + this.arrow = new Polygon({ + zlevel: model.zlevel, + z: model.z, + shape: { + points: point + }, + style: { + stroke: model.stroke, + lineWidth: model.lineWidth, + fill: model.fill + } + }); + this.add(this.arrow); + } + + // 箭头颜色 + setColor(color) { + this.arrow.setStyle('fill', color); + } + +} + +export default Arrow; diff --git a/src/ibp/shape/background.js b/src/ibp/shape/background.js new file mode 100644 index 000000000..dc75fb541 --- /dev/null +++ b/src/ibp/shape/background.js @@ -0,0 +1,50 @@ +import Group from 'zrender/src/container/Group'; +import Image from 'zrender/src/graphic/Image'; +import Rect from 'zrender/src/graphic/shape/Rect'; +import ibpBg from '@/assets/ibp_images/ibp_bg.png'; + +export default class background extends Group { + constructor(model) { + super(); + this.model = model; + this.zlevel = model.zlevel; + this.z = 1; + this.create(); + } + create() { + this.imageBg = new Image({ + zlevel: this.zlevel, + z: this.z, + style: { + x: 0, + y: 0, + image: ibpBg, + width: 2048, + height: 1024 + } + }); + this.tailorRect = new Rect({ + zlevel: this.zlevel, + z: this.z, + shape: { + x: 0, + y: 0, + width: this.model.width, + height: this.model.height + } + }); + this.tailorBgImage(); + } + tailorBgImage() { + // this.imageBg.setClipPath(this.tailorRect); + this.add(this.imageBg); + } + setInitialPosition(opt) { + const x = this.imageBg.style.x; + const y = this.imageBg.style.y; + this.imageBg.setStyle('x', x-opt.offsetX); + this.imageBg.setStyle('y', y-opt.offsetY); + this.tailorRect.setShape('x', x-opt.offsetX); + this.tailorRect.setShape('y', y-opt.offsetY); + } +} diff --git a/src/ibp/shape/button.js b/src/ibp/shape/button.js new file mode 100644 index 000000000..79bd902d0 --- /dev/null +++ b/src/ibp/shape/button.js @@ -0,0 +1,163 @@ +import Group from 'zrender/src/container/Group'; +import Image from 'zrender/src/graphic/Image'; +// import Eventful from 'zrender/src/mixin/Eventful'; + +import * as eventTool from 'zrender/src/core/event'; + +import buttonPic from '@/assets/ibp_images/red_button.png'; +import buttonPicOn from '@/assets/ibp_images/red_button_on.png'; + +export default class button extends Group { + constructor(model) { + super(); + this.model = model; + this.zlevel = model.zlevel; + this.z = model.z; + this.create(); + this.createMouseEvent(); + } + create() { + const model = this.model; + this.imageBg = new Image({ + zlevel: this.zlevel, + z: this.z, + style: { + image: model.status === '01' ? buttonPic : buttonPicOn, + x: this.model.point.x, + y: this.model.point.y, + width: this.model.width, + height: this.model.height + } + }); + this.add(this.imageBg); + } + + // 设置按钮状态 + setState(model) { + switch (model.status) { + case '01': this.close(); break; // 关闭 + case '02': this.open(); break; // 开放 + } + } + + // 绑定按钮事件 + createMouseEvent() { + this.model.isDraging=true; + if (this.model.isDraging) { + // 按钮拖拽事件监听 + this.imageBg.on('mousedown', (e) => { + if (eventTool.notLeftMouse(e)) { + return; + } + eventTool.stop(e.event); + var x = e.offsetX; + var y = e.offsetY; + this._x = x; + this._y = y; + this._dragginger = true; + }); + this.imageBg.on('mousemove', (e) => { + // !this._moveOnMouseMove || + if (eventTool.notLeftMouse(e) || !this._dragginger) { + return; + } + // eventTool.stop(e.event); + const oldX = this._x; + const oldY = this._y; + const dx = e.offsetX - oldX; + const dy = e.offsetY - oldY; + this._x = e.offsetX; + this._y = e.offsetY; + debugger; + // this.imageBg.preventDefaultMouseMove && + eventTool.stop(e.event); + // debugger; + this.imageBg.setStyle({x: oldX+dx, y: oldY+dy }); + }); + + this.imageBg.on('mouseup', (e) => { + if (!eventTool.notLeftMouse(e)) { + this._dragginger = false; + } + }); + + } else { + // 按钮的点击监听 + this.imageBg.on('click', (e) => { + switch (this.model.status) { + case '01': { + this.open(); + this.model.status='02'; + break; + } + case '02': { + this.close(); + this.model.status='01'; + break; + } + } + }); + } + } + +// if (this.model.isDraging) { +// // this.mouseEvent = new EMouse(this); +// // this.add(this.mouseEvent); +// this.imageBg.on('mousedown', (e) => { +// // debugger; +// if (eventTool.notLeftMouse(e)) { +// return; +// } +// var x = e.offsetX; +// var y = e.offsetY; +// this._x = x; +// this._y = y; +// this._dragging = true; +// }); +// this.imageBg.on('mousemove', (e) => { +// if (eventTool.notLeftMouse(e) || !this._moveOnMouseMove ||!this._dragging) { +// return; +// } +// const oldX = this._x; +// const oldY = this._y; +// const dx = e.offsetX - oldX; +// const dy = e.offsetY - oldY; +// this._x = e.offsetX; +// this._y = e.offsetY; +// debugger; +// this.imageBg.preventDefaultMouseMove && eventTool.stop(e.event); +// debugger; +// this.imageBg.setStyle({x: dx, y: dy}); +// // this.trigger(this.events.__Pan, { dx, dy, oldX, oldY, newX: this._x, newY: this._y }); + +// }); +// this.on('mouseup', (e) => { }); +// } +// else { +// this.on('mousedown', (e) => { this.mouseEvent.mouseout(e); }); +// // this.on('mousemove', (e) => { this.mouseEvent.mouseover(e); }); +// this.on('mouseup', (e) => { this.mouseEvent.mouseover(e); }); +// } + + // 关闭 + close() { + this.imageBg.setStyle({image: buttonPic}); + } + // 开放 + open() { + this.imageBg.setStyle({image: buttonPicOn}); + } + + getShapeTipPoint() { + if (this.imageBg) { + var distance = 2; + var rect = this.imageBg.getBoundingRect(); + return { + x: rect.x + rect.width / 2, + y: rect.y - distance + }; + } + return null; + } + +} diff --git a/src/ibp/shape/circularLamp.js b/src/ibp/shape/circularLamp.js new file mode 100644 index 000000000..38f6bc778 --- /dev/null +++ b/src/ibp/shape/circularLamp.js @@ -0,0 +1,33 @@ +import Group from 'zrender/src/container/Group'; +import Circle from 'zrender/src/graphic/shape/Circle'; +import MouseController from '../mouseController'; + +export default class CircularLamp extends Group { + constructor(model) { + super(); + this.model = model; + this.zlevel = model.zlevel; + this.z = model.z; + this.create(); + } + + create() { + this.lamp = new Circle({ + zlevel: this.zlevel, + z: this.z, + shape: { + cx: this.model.point.x, + cy: this.model.point.y, + r: this.model.r + }, + style: { + fill: this.model.fillColor + } + }); + this.add(this.lamp); + } + + setCircularLampColor(color) { + this.lamp.setStyle('fill', color); + } +} diff --git a/src/ibp/shape/element/textName.js b/src/ibp/shape/element/textName.js new file mode 100644 index 000000000..fa743f051 --- /dev/null +++ b/src/ibp/shape/element/textName.js @@ -0,0 +1,25 @@ +// import Group from 'zrender/src/container/Group'; +import Text from 'zrender/src/graphic/Text'; + +/** 名称元素*/ +export default function ETextName(model) { + const TextName = new Text({ + zlevel: model.zlevel, + z: model.z, + silent: model.silent || false, + style: { + x: model.x, + y: model.y, + fontWeight: model.fontWeight, + fontSize: model.fontSize, + fontFamily: model.fontFamily, + text: model.text, + textStrokeWidth: model.textStrokeWidth, + textFill: model.textFill, + textAlign: model.textAlign, + textPosition: model.textPosition || 'inside', + textVerticalAlign: model.textVerticalAlign || null + } + }); + return TextName; +} diff --git a/src/ibp/shape/factory.js b/src/ibp/shape/factory.js new file mode 100644 index 000000000..3c9a1732a --- /dev/null +++ b/src/ibp/shape/factory.js @@ -0,0 +1,20 @@ +import Arrow from './arrow'; +import deviceType from '../constant/deviceType'; +import Background from './background'; +import CircularLamp from './circularLamp' + +const ibpShape = {}; +ibpShape[deviceType.Arrow] = Arrow; +ibpShape[deviceType.Background] = Background; +ibpShape[deviceType.CircularLamp] = CircularLamp; + +function shapefactory(device, ibp) { + const type = device._type; + const shape = ibpShape[type]; + if (shape instanceof Function) { + // eslint-disable-next-line + return new shape(device, ibp.style); + } +} + +export default shapefactory; diff --git a/src/ibp/transformHandle.js b/src/ibp/transformHandle.js new file mode 100644 index 000000000..055aed10c --- /dev/null +++ b/src/ibp/transformHandle.js @@ -0,0 +1,71 @@ + +import {createTransform, createBoundingRect} from './utils/parser'; + +class TransformHandle { + constructor(painter) { + this.$painter = painter; + + this.parentLevel = painter.getParentLevel(); + + this.rect = { x: 0, y: 0, width: 0, height: 0 }; + + this.transform = createTransform({ scaleRate: 1, offsetX: 0, offsetY: 0 }); + } + + checkVisible(view) { + return createBoundingRect(view).intersect(this.rect); + } + + revisibleView(view) { + if (this.checkVisible(view)) { + view.show(); + } else { + view.hide(); + } + + view.dirty(); + } + + // 视图进行缩放/平移 + transformView(view) { + if (view) { + view.transform = this.transform; + view.decomposeTransform(); + this.revisibleView(view); + } + // return view; + } + + // 处理所有视图缩放/平移 + transformAll() { + this.traverse(this.transformView, this); + } + + // 重新计算显示图形 + revisibleAll() { + this.traverse(this.revisibleView, this); + } + + // 更新偏移量 + updateTransform(opts) { + this.transform = createTransform(opts); + this.transformAll(); + } + + // 更新画布尺寸 + updateZrSize(opts) { + this.rect = { x: 0, y: 0, width: opts.width, height: opts.height }; + this.revisibleAll(); + } + + // 遍历group执行回调 + traverse(cb, context) { + this.parentLevel.eachChild(level => { + level.eachChild((view) => { + cb.call(context, view); + }, context); + }, context); + } +} + +export default TransformHandle; diff --git a/src/ibp/utils/parser.js b/src/ibp/utils/parser.js new file mode 100644 index 000000000..049c8b137 --- /dev/null +++ b/src/ibp/utils/parser.js @@ -0,0 +1,78 @@ +import * as zrUtil from 'zrender/src/core/util'; +import * as matrix from 'zrender/src/core/matrix'; +import deviceType from '../constant/deviceType'; +import deviceRender from '../constant/deviceRender'; + +export function createTransform(opts) { + let transform = matrix.create(); + transform = matrix.scale(matrix.create(), transform, [opts.scaleRate, opts.scaleRate]); + transform = matrix.translate(matrix.create(), transform, [-opts.offsetX, -opts.offsetY]); + return transform; +} + +export function createBoundingRect(view) { + const rect = view.getBoundingRect(); + const scale = view.scale[0]; + const offsetX = view.position[0]; + const offsetY = view.position[1]; + rect.x = rect.x * scale + offsetX; + rect.y = rect.y * scale + offsetY; + rect.width = rect.width * scale; + rect.height = rect.height * scale; + return rect; +} + +export function calculateDCenter(viewRect, zrbound) { + var dx = (zrbound.width - viewRect.width) / 2 - viewRect.x; + var dy = 0; + return { dx: dx, dy: dy }; +} + +export function deviceFactory(type, elem) { + return Object.assign({ _type: type }, deviceRender[type], elem); +} + +export function createDevice(type, model, propConvert) { + const device = deviceFactory(type, model); + return propConvert ? propConvert.initPrivateProps(device) : device; +} + +export function parser(data, config) { + var ibpDevice = {}; + const propConvert = null; + // var propConvert = skinCode ? Vue.prototype.$theme.loadPropConvert(skinCode): null; + if (data) { + + Object.assign(data.background, config); + ibpDevice[data.background.code] = createDevice(deviceType.Background, data.background, propConvert); + zrUtil.each(data.textList || [], elem => { + ibpDevice[elem.code] = createDevice(deviceType.Text, elem, propConvert); + }, this); + + zrUtil.each(data.squareButtonList || [], elem => { + ibpDevice[elem.code] = createDevice(deviceType.SquareButton, elem, propConvert); + }, this); + + zrUtil.each(data.circularLampList || [], elem => { + ibpDevice[elem.code] = createDevice(deviceType.CircularLamp, elem, propConvert); + }, this); + + zrUtil.each(data.warnButtonList || [], elem => { + ibpDevice[elem.code] = createDevice(deviceType.WarnButton, elem, propConvert); + }, this); + + zrUtil.each(data.arrowList || [], elem => { + ibpDevice[elem.code] = createDevice(deviceType.Arrow, elem, propConvert); + }, this); + + zrUtil.each(data.rotatingButtonList || [], elem => { + ibpDevice[elem.code] = createDevice(deviceType.RotatingButton, elem, propConvert); + }, this); + + zrUtil.each(data.tipList || [], elem => { + ibpDevice[elem.code] = createDevice(deviceType.Tip, elem, propConvert); + }, this); + } + + return ibpDevice; +} diff --git a/src/jmap/config/skinCode/chengdu_03.js b/src/jmap/config/skinCode/chengdu_03.js index f7727888d..d127f42bd 100644 --- a/src/jmap/config/skinCode/chengdu_03.js +++ b/src/jmap/config/skinCode/chengdu_03.js @@ -27,7 +27,7 @@ class SkinCode extends defaultStyle { textVerticalAlign: 'middle' // 文字垂直对齐方式 }, logicText: { - show: false, // 逻辑区段名称显示 + show: false, // 逻辑区段名称显示 position: 0, // 区段名称位置 1 上面 -1 下面 0 对称 distance: 12, // 文字离区段距离 fontSize: 11, // 字体大小 @@ -276,7 +276,7 @@ class SkinCode extends defaultStyle { emergencyControlShow: true, // 紧急站控显示 centerControlShow: true, // 中控显示 substationControlShow: true, // 站控按钮显示 - turnedAroundControlShow: false // 按图折返显示 + turnedAroundControlShow: true // 按图折返显示 }, arrow: { show: false // 控制模式箭头显隐 diff --git a/src/utils/baseUrl.js b/src/utils/baseUrl.js index 2e575c51e..b49dd28b5 100644 --- a/src/utils/baseUrl.js +++ b/src/utils/baseUrl.js @@ -4,7 +4,7 @@ export function getBaseUrl() { if (process.env.NODE_ENV === 'development') { // BASE_API = 'https://joylink.club/jlcloud'; BASE_API = 'https://test.joylink.club/jlcloud'; - // BASE_API = 'http://192.168.3.5:9000'; // 袁琪 + // BASE_API = 'http://192.168.9.255:9000'; // 袁琪 // BASE_API = 'http://192.168.3.6:9000'; // 旭强 // BASE_API = 'http://192.168.3.4:9000' // 琰培 } else { diff --git a/src/views/display/demon/addQuest.vue b/src/views/display/demon/addQuest.vue index 0c2c146af..5d362c4bb 100644 --- a/src/views/display/demon/addQuest.vue +++ b/src/views/display/demon/addQuest.vue @@ -177,7 +177,10 @@ export default { }, async confirm() { + await this.$store.dispatch('training/over'); + await this.$store.dispatch('training/setMapDefaultState'); await this.$store.dispatch('map/clearJlmapTrainView'); + await this.$store.dispatch('map/setTrainWindowShow', false); this.$emit('selectQuest', this.row, this.form.role); this.doClose(); this.roleDoClose(); diff --git a/src/views/display/index.vue b/src/views/display/index.vue index 163787bfb..bb02ae19a 100644 --- a/src/views/display/index.vue +++ b/src/views/display/index.vue @@ -18,6 +18,7 @@ @tryTime="tryTime" @hidepanel="hidepanel" @quitQuest="quitQuest" + @showIbp="showIbp" /> @@ -45,6 +46,7 @@ + @@ -85,6 +87,7 @@ import Vue from 'vue'; // 三维 import Jl3dSimulation from '@/views/jlmap3d/simulation/jl3dsimulation'; import Jl3dDrive from '@/views/jlmap3d/drive/jl3ddrive'; +import IbpPlate from '@/views/ibp/index' export default { name: 'DisplayDraft', @@ -104,7 +107,8 @@ export default { MenuSchema, MenuSystemTime, Jl3dSimulation, - Jl3dDrive + Jl3dDrive, + IbpPlate }, mixins: [WindowResizeHandler], props: { @@ -137,7 +141,8 @@ export default { '01': '01', // 现地 => 现地 '02': '02', // 行调 => 行调 '04': '02' // 司机加实 => 行调 - } + }, + ibpShow: false }; }, computed: { @@ -553,6 +558,11 @@ export default { this.$refs.Jl3dSimulation.show(this.skinCode); } }, + showIbp() { + this.panelShow = false; + this.ibpShow = true; + this.$refs.ibpPlate.show(); + }, showpanel() { this.panelShow = true; this.simulationShow = false; @@ -561,6 +571,10 @@ export default { this.panelShow = true; this.drivingShow = false; }, + hideIbp() { + this.panelShow = true; + this.ibpShow = false; + }, runPlanViewShow() { this.$refs.runPlanView.doShow(); }, diff --git a/src/views/display/menuDemon.vue b/src/views/display/menuDemon.vue index 449245924..5c1e3d1d6 100644 --- a/src/views/display/menuDemon.vue +++ b/src/views/display/menuDemon.vue @@ -7,6 +7,7 @@
+ {{ jl3dname }}