import * as zrUtil from 'zrender/src/core/util'; import zrender from 'zrender'; import localStore from 'storejs'; import Painter from './painter'; import Options from './options'; import MouseController from './mouseController'; import KeyboardController from './keyboardController'; import deviceState from './constant/deviceState'; // 初始化默认状态 import deviceType from './constant/deviceType'; import { selectLineCode } from './config/deviceStyle'; import { deviceFactory, createBoundingRect, calculateDCenter } from './utils/parser'; import { deepAssign } from '@/utils/index'; import store from '@/store/index_APP_TARGET'; import Vue from 'vue'; const renderer = 'canvas'; const devicePixelRatio = 1; class Jlmap { constructor(opts) { // 回调事件 this.methods = opts.methods; // 鼠标事件 this.events = { __Pan: 'pan', __Zoom: 'zoom', Selected: 'selected', Contextmenu: 'contextmenu', DataZoom: 'dataZoom', Keyboard: 'keyboard'}; // 线路参数 this.lineCode = ''; // 皮肤风格 this.style = {}; // 设备数据 this.mapDevice = {}; // 默认状态 this.defaultStateDict = this.loadDefaultState(); this.initMapInstance(opts); // 显示集中站 this.stationCode = ''; } // 初始化属性有鼠标事件 缩放等 initMapInstance(opts) { const width = opts.dom.clientWidth; const height = opts.dom.clientHeight; this.zoomOnMouseWheel = opts.options.zoomOnMouseWheel; this.previewOrMapDraw = opts.showConfig.previewOrMapDraw; this.$zr = zrender.init(opts.dom, deepAssign({ renderer, devicePixelRatio, width, height }, opts.config)); this.$options = new Options(deepAssign({ 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.$zr.getWidth(), height: this.$zr.getHeight()}); 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); this.$keyboardController = new KeyboardController(this); this.$keyboardController.enable(); this.showConfig = opts.showConfig; } loadStyle(lineCode) { return selectLineCode(lineCode); } loadDefaultState() { // 加载默认状态 const defaultStateDict = {}; zrUtil.each(Object.keys(deviceState), (type) => { defaultStateDict[type] = {}; zrUtil.each(Object.keys(deviceState[type] || {}), (state) => { defaultStateDict[type][state] = deviceState[type][state]; // 新版地图直接使用判断结果 }, this); }, this); return defaultStateDict; } setMap(map, mapDevice, logicData) { // 保存皮肤类型 if (map.skinVO) { this.lineCode = map.skinVO.code; this.$options.scaleRate = map.scaling || 1; this.$options.offsetX = map.origin ? map.origin.x : 0; this.$options.offsetY = map.origin ? map.origin.y : 0; } // 更新视图大小 this.$painter.updateTransform({ scaleRate: this.$options.scaleRate, offsetX: this.$options.offsetX, offsetY: this.$options.offsetY }); // 解析后的数据 this.mapDevice = mapDevice; // 进路数据 this.logicData = logicData; // 加载对应皮肤 this.style = this.loadStyle(this.lineCode); // 数据加载完成 回调 if (this.methods.dataLoaded instanceof Function) { this.methods.dataLoaded(this.mapDevice); } // 初次渲染视图 this.$painter.repaint(this.mapDevice); // 视图加载完成 回调 if (this.methods.viewLoaded instanceof Function) { this.methods.viewLoaded(this.mapDevice); } } setMapDevice(mapDevice) { this.mapDevice = mapDevice; } setDefaultState() { const list = []; Object.values(this.mapDevice).forEach(elem => { const code = elem.code; const type = elem._type; // 列车不需要设置默认状态 type != deviceType.Train && list.push(Object.assign({ code, _type: type }, this.defaultStateDict[type])); }); 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 { opts['zoomOnMouseWheel'] = this.zoomOnMouseWheel; opts['previewOrMapDraw'] = this.previewOrMapDraw; this.$mouseController.enable(opts); } if (this.methods.optionsUpdate instanceof Function) { this.methods.optionsUpdate(this.$options); } } setCenter(deviceCode) { const device = this.mapDevice[deviceCode]; if (device && device.instance) { var rect = createBoundingRect(device.instance); var dcenter = calculateDCenter(rect, { width: this.$zr.getWidth(), height: this.$zr.getHeight() }); this.setOptions(dcenter); } } setRecover(opts) { this.$painter.updateTransform({ scaleRate: opts.scaleRate, offsetX: opts.offsetX, offsetY: opts.offsetY }); } setUpdateScreen(opts) { this.setRecover({ scaleRate: 1, offsetX: 0, offsetY: 0 }); const arr = []; const rectList = []; let rect = ''; for (const i in this.mapDevice) { const element = this.mapDevice[i]; if (element.instance) { if (!rect) { rect = element.instance.getBoundingRect().clone(); } else { rect.union(element.instance.getBoundingRect().clone()); } } } const screenSplit = opts.list.length ? opts.list : Vue.prototype.$theme.loadPropConvert(store.state.map.map.skinVO.code).screenSplit; const splitList = JSON.parse(JSON.stringify(screenSplit)); const num = screenSplit.length + 1; const offsetY = (opts.height - 100) / num; // 高度差 const maxWidth = rect.width; splitList.push(maxWidth); const scaleWidth = Math.floor((((opts.width - 200) * num) / rect.width) * 100) / 100; const scaleHeight = Math.floor(((opts.height - 100) / (rect.height * num)) * 100) / 100; const scale = Math.min(scaleWidth, scaleHeight); for (let i = 0; i < splitList.length; i++) { let offsetX = ''; if (i == 0) { offsetX = -(opts.width - splitList[0] * scale) / 2; } else { const dx = (opts.width - (splitList[i] - splitList[i - 1]) * scale) / 2; // 居中计算偏移值 offsetX = splitList[i - 1] * scale - dx; } const param = { scaleRate: scale, offsetX: offsetX, offsetY: -100 - (offsetY * i) }; arr.push(param); const rect = {x: 0, y: 0, width: Number(splitList[i]) + 5, height: opts.height}; rectList.push(rect); } this.$painter.updateTransform1(arr, rectList); } setLevelVisible(list) { this.$painter.setLevelVisible(list); } render(list) { (list || []).forEach(elem => { const code = elem.code; const type = elem._type; const oDevice = this.mapDevice[code] || deviceFactory(type, elem, this.showConfig); const nDevice = deepAssign(oDevice || {}, elem); this.$painter.delete(oDevice); delete this.mapDevice[code]; if (!elem._dispose) { this.mapDevice[code] = nDevice; this.$painter.add(nDevice); } }); if (this.methods.viewUpdate instanceof Function) { this.methods.viewUpdate(list); } this.updateShowMode(list, this.showConfig.showMode); this.updateShowStation(list, this.stationCode); } // 中间处理 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 (oDevice[prop] != elem[prop]) { deepAssign(oDevice, elem); // 更新状态 return true; } } return false; } // 后处理 postHandle(list) { list.forEach(elem => { const code = elem.code; const type = elem._type; if (type == deviceType.Switch) { const item = this.mapDevice[code]; if (item) { const sectionA = this.mapDevice[item.sectionACode]; const sectionB = this.mapDevice[item.sectionBCode]; const sectionC = this.mapDevice[item.sectionCCode]; if (sectionA && sectionB && sectionC) { item['cutOff'] = sectionA.cutOff; } } this.$painter.update(item); } if (type == deviceType.Section) { const item = this.mapDevice[code]; if (item) { const swch = this.mapDevice[item.relSwitchCode]; if (swch) { const sectionA = this.mapDevice[swch.sectionACode]; const sectionB = this.mapDevice[swch.sectionBCode]; const sectionC = this.mapDevice[swch.sectionCCode]; if (sectionA && sectionB && sectionC) { swch['cutOff'] = sectionA.cutOff; } this.$painter.update(swch); } } } }); } setUpdateMapDevice(list) { store.dispatch('map/updateMapDevice', list); (list || []).forEach(elem => { const code = elem.code; const type = elem._type; if (elem.deviceType === 'TRAIN' && elem.type === 'HEAD') { elem.serviceNumber = ''; } else if (elem.deviceType === 'TRAIN' && elem.type === 'MANUAL') { elem.serviceNumber = ''; elem.tripNumber = ''; elem.destinationCode = ''; } const oDevice = this.mapDevice[code] || deviceFactory(type, elem); this.hookHandle(oDevice, elem); }); } updateShowMode(list, showMode) { this.showConfig.showMode = showMode; (list || []).forEach(elem => { const code = elem.code; const type = elem._type; const oDevice = this.mapDevice[code] || deviceFactory(type, elem, this.showConfig); oDevice.showMode = showMode; this.$painter.updateShowMode(oDevice); }); this.$painter.$transformHandle.revisibleAll(); } getShowConfig() { return this.showConfig; } updateShowStation(list, stationCode) { this.stationCode = stationCode; (list || []).forEach(elem => { const code = elem.code; const type = elem._type; const oDevice = this.mapDevice[code] || deviceFactory(type, elem, this.showConfig); if (oDevice._type === deviceType.Station) { this.showStationHandleStation(oDevice, stationCode); } else if (oDevice._type === deviceType.Section) { this.$painter.updateShowStation(oDevice, stationCode); this.showStationHandleSection(oDevice, stationCode); } else if (oDevice._type === deviceType.TrainWindow) { // 不处理车次窗 勿删 } else if (oDevice._type === deviceType.Psd) { this.showStationHandlePsd(oDevice, stationCode); } else { this.$painter.updateShowStation(oDevice, stationCode); } }); this.$painter.$transformHandle.revisibleAll(); } showStationHandlePsd(oDevice, stationCode) { const standDevice = this.mapDevice[oDevice.standCode]; if (standDevice && standDevice.deviceStationCode === stationCode || !stationCode) { this.$painter.updateSpecialShowStation(oDevice, true); } else { this.$painter.updateSpecialShowStation(oDevice, false); } } showStationHandleSection(oDevice, stationCode) { const trainWinDevice = this.mapDevice[oDevice.trainWindowCode]; if (oDevice.stationCode === stationCode || !stationCode) { this.$painter.updateSpecialShowStation(trainWinDevice, true); } else { this.$painter.updateSpecialShowStation(trainWinDevice, false); } } showStationHandleStation (oDevice, stationCode) { if ((oDevice.centralized && oDevice.code === stationCode) || !stationCode) { this.$painter.updateSpecialShowStation(oDevice, true); oDevice.chargeStationCodeList.forEach(staCode => { const staDevice = this.mapDevice[staCode]; this.$painter.updateSpecialShowStation(staDevice, true); }); } else if (oDevice.centralized && oDevice.code !== stationCode) { this.$painter.updateSpecialShowStation(oDevice, false); oDevice.chargeStationCodeList.forEach(staCode => { const staDevice = this.mapDevice[staCode]; this.$painter.updateSpecialShowStation(staDevice, false); }); } } updatePrdType(val, list) { this.showConfig.prdType = val; (list || []).forEach(item => { this.hookHandle(item, this.showConfig); this.$painter.update(item); }); } update(list) { this.setUpdateMapDevice(list || []); // 增加一个 前数据 处理 为了在区段中 获取全部的 道岔信息 const signalDeviceList = []; (list || []).forEach((elem, index) => { const code = elem.code; const type = elem._type; if (elem.deviceType === 'ROUTE') { // 处理进路数据状态 store.dispatch('map/updateRouteState', elem); const route = this.logicData.routeData[code]; if (route.automaticRouteCode) { const automaticRoute = this.mapDevice[route.automaticRouteCode]; const automaticRouteStatus = {fleetMode: elem.fleetMode}; if (this.hookHandle(automaticRoute, automaticRouteStatus)) { this.$painter.update(automaticRoute); } } const signalDevice = this.mapDevice[route.startSignalCode]; const index = signalDeviceList.indexOf(signalDevice); if (index === -1) { signalDeviceList.push(signalDevice); } } else if (elem.deviceType === 'CYCLE') { store.dispatch('map/updateAutoReentryState', elem); const autoReentryData = this.logicData.autoReentryData[code]; const cycleButton = this.mapDevice[autoReentryData.cycleButtonCode]; const cycleButtonStatus = {setUp: elem.setUp}; if (cycleButton && this.hookHandle(cycleButton, cycleButtonStatus)) { this.$painter.update(cycleButton); } } else if (elem.deviceType === 'STATION') { const oDevice = this.mapDevice[code] || deviceFactory(type, elem, this.showConfig); const guideLock = this.mapDevice[oDevice.guideLockCode]; const guideLockStatus = {totalGuideLock: elem.totalGuideLock}; if (guideLock && this.hookHandle(guideLock, guideLockStatus)) { this.$painter.update(guideLock); } if (elem.dispose) { this.$painter.delete(oDevice); } else { this.$painter.update(oDevice); } } else { if (elem.deviceType === 'TRAIN') { store.dispatch('map/updateTrainState', elem); store.dispatch('map/setActiveTrainList', elem); } else if (elem.deviceType === 'STAND') { store.dispatch('map/updateStationStand', elem); } const oDevice = this.mapDevice[code] || deviceFactory(type, elem, this.showConfig); if (elem.dispose) { this.$painter.delete(oDevice); } else { // if (this.hookHandle(oDevice, elem)) { this.$painter.update(oDevice); // } } } }); this.handleRouteSignalStatus(signalDeviceList); // 状态后处理 this.postHandle(list || []); if (this.methods.stateUpdate instanceof Function) { this.methods.stateUpdate(list); } } handleRouteSignalStatus(signalDeviceList) { const routeStartSignalData = store.state.map.routeStartSignalData; signalDeviceList.forEach((item, index)=> { const status = {}; if (routeStartSignalData[item.code] && routeStartSignalData[item.code].length) { routeStartSignalData[item.code].forEach((elem, index)=> { if (index) { status.atsControl = status.atsControl && elem.atsControl; status.fleetMode = status.fleetMode || elem.fleetMode; status.ciControl = status.ciControl && elem.ciControl; } else { status.atsControl = elem.atsControl; status.fleetMode = elem.fleetMode; status.ciControl = elem.ciControl; } }); } if (item && this.hookHandle(item, status)) { this.$painter.update(item); } }); } pullBack(payload) { if (payload.type === 'zoom') { const zrWidth = this.$zr.getWidth(); const zrHeight = this.$zr.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 || {}; } getZr() { return this.$zr; } getEvents() { return this.events; } getDeviceByCode(code) { return this.mapDevice[code]; } getShapeTipPoint(opts) { const device = this.mapDevice[opts.code]; if (device) { return this.$painter.getShapeTipPoint(device.instance, opts); } } resize(opt) { this.$zr.resize(opt); this.$painter.updateZrSize(opt); } refresh() { this.$painter.refresh(); } clearTrainView() { this.$painter.clearLevel(deviceType.Train); zrUtil.each(Object.values(this.mapDevice), device => { if (device._type == deviceType.Train) { device.instance = null; } }); } clear() { this.lineCode = ''; this.style = {}; this.mapDevice = {}; this.$painter.clear(); } dispose() { this.off(this.events.Pan, this.optionsHandler); this.off(this.events.Zoom, this.optionsHandler); this.clear(); this.$mouseController.dispose(); this.$keyboardController.dispose(); this.$zr && zrender.dispose(this.$zr); 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; case this.events.Keyboard: this.$keyboardController.on(this.events.Keyboard, cb, context); break; case this.events.__Pan: this.$mouseController.on(this.events.__Pan, this.optionsHandler); break; case this.events.__Zoom: this.$mouseController.on(this.events.__Zoom, this.optionsHandler); 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; case this.events.Keyboard: this.$keyboardController.off(this.events.keyboard, cb); break; case this.events.__Pan: this.$mouseController.off(this.events.__Pan, cb); break; case this.events.__Zoom: this.$mouseController.off(this.events.__Zoom, cb); break; } } } renderCheckBox(model) { const type = model._type; const code = model.code; const oDevice = this.mapDevice[code] || deviceFactory(type, model); const nDevice = deviceFactory(type, Object.assign(oDevice.model || {}, model)); delete this.mapDevice[code]; this.$painter.delete(oDevice); if (!model._dispose) { this.mapDevice[code] = nDevice; this.$painter.add(nDevice); } } deleteCheckBox(code) { const oDevice = this.mapDevice[code]; if (oDevice) { delete this.mapDevice[code]; this.$painter.delete(oDevice); } } } export default Jlmap;