From 7443e8040d9a7669a8117c8a6937dbd4bd792709 Mon Sep 17 00:00:00 2001
From: lsh <lsh@163.com>
Date: 星期二, 21 四月 2026 15:49:05 +0800
Subject: [PATCH] 添加环穿轨道
---
src/main/webapp/static/js/basMap/editor.js | 8793 ++++++++++++++++++++++++++++++++---------------------------
1 files changed, 4,756 insertions(+), 4,037 deletions(-)
diff --git a/src/main/webapp/static/js/basMap/editor.js b/src/main/webapp/static/js/basMap/editor.js
index 40205cf..f164257 100644
--- a/src/main/webapp/static/js/basMap/editor.js
+++ b/src/main/webapp/static/js/basMap/editor.js
@@ -1,4059 +1,4778 @@
(function () {
- var FREE_EDITOR_MODE = 'free-v1';
- var MAP_TRANSFER_FORMAT = 'bas-map-editor-transfer-v2';
- var HISTORY_LIMIT = 60;
- var DEFAULT_CANVAS_WIDTH = 6400;
- var DEFAULT_CANVAS_HEIGHT = 3600;
- var MIN_ELEMENT_SIZE = 24;
- var HANDLE_SCREEN_SIZE = 10;
- var DRAG_START_THRESHOLD = 5;
- var EDGE_SNAP_SCREEN_TOLERANCE = 8;
- var COORD_EPSILON = 0.01;
- var DEFERRED_STATIC_REBUILD_DELAY = 120;
- var PAN_LABEL_REFRESH_DELAY = 160;
- var ZOOM_REFRESH_DELAY = 220;
- var POINTER_STATUS_UPDATE_INTERVAL = 48;
- var SPATIAL_BUCKET_SIZE = 240;
- var STATIC_VIEW_PADDING = 120;
- var MIN_LABEL_SCALE = 0.17;
- var ABS_MIN_LABEL_SCREEN_WIDTH = 26;
- var ABS_MIN_LABEL_SCREEN_HEIGHT = 14;
- var STATIC_SPRITE_SCALE_THRESHOLD = 0.85;
- var STATIC_SIMPLIFY_SCALE_THRESHOLD = 0.22;
- var DENSE_SIMPLIFY_SCALE_THRESHOLD = 0.8;
- var DENSE_SIMPLIFY_ELEMENT_THRESHOLD = 1200;
- var DENSE_LABEL_HIDE_SCALE_THRESHOLD = 1.05;
- var DENSE_LABEL_HIDE_ELEMENT_THRESHOLD = 1200;
- var STATIC_SPRITE_POOL_SLACK = 96;
- var MIN_LABEL_COUNT = 180;
- var MAX_LABEL_COUNT = 360;
- var SHOW_CANVAS_ELEMENT_LABELS = true;
- var DRAW_TYPES = ['shelf', 'devp', 'crn', 'dualCrn', 'rgv'];
- var ARRAY_TEMPLATE_TYPES = ['shelf', 'crn', 'dualCrn', 'rgv'];
- var DEVICE_CONFIG_TYPES = ['crn', 'dualCrn', 'rgv'];
- var DEVP_DIRECTION_OPTIONS = [
- { key: 'top', label: '涓�', arrow: '鈫�' },
- { key: 'right', label: '鍙�', arrow: '鈫�' },
- { key: 'bottom', label: '涓�', arrow: '鈫�' },
- { key: 'left', label: '宸�', arrow: '鈫�' }
- ];
- var idSeed = Date.now();
+ var FREE_EDITOR_MODE = 'free-v1';
+ var MAP_TRANSFER_FORMAT = 'bas-map-editor-transfer-v2';
+ var HISTORY_LIMIT = 60;
+ var DEFAULT_CANVAS_WIDTH = 6400;
+ var DEFAULT_CANVAS_HEIGHT = 3600;
+ var MIN_ELEMENT_SIZE = 24;
+ var HANDLE_SCREEN_SIZE = 10;
+ var DRAG_START_THRESHOLD = 5;
+ var EDGE_SNAP_SCREEN_TOLERANCE = 8;
+ var COORD_EPSILON = 0.01;
+ var DEFERRED_STATIC_REBUILD_DELAY = 120;
+ var PAN_LABEL_REFRESH_DELAY = 160;
+ var ZOOM_REFRESH_DELAY = 220;
+ var POINTER_STATUS_UPDATE_INTERVAL = 48;
+ var SPATIAL_BUCKET_SIZE = 240;
+ var STATIC_VIEW_PADDING = 120;
+ var MIN_LABEL_SCALE = 0.17;
+ var ABS_MIN_LABEL_SCREEN_WIDTH = 26;
+ var ABS_MIN_LABEL_SCREEN_HEIGHT = 14;
+ var STATIC_SPRITE_SCALE_THRESHOLD = 0.85;
+ var STATIC_SIMPLIFY_SCALE_THRESHOLD = 0.22;
+ var DENSE_SIMPLIFY_SCALE_THRESHOLD = 0.8;
+ var DENSE_SIMPLIFY_ELEMENT_THRESHOLD = 1200;
+ var DENSE_LABEL_HIDE_SCALE_THRESHOLD = 1.05;
+ var DENSE_LABEL_HIDE_ELEMENT_THRESHOLD = 1200;
+ var STATIC_SPRITE_POOL_SLACK = 96;
+ var MIN_LABEL_COUNT = 180;
+ var MAX_LABEL_COUNT = 360;
+ var DRAW_TYPES = ['shelf', 'repairHub', 'devp', 'crn', 'dualCrn', 'rgv', 'annulus'];
+ var ARRAY_TEMPLATE_TYPES = ['shelf', 'repairHub'];
+ var DEVICE_CONFIG_TYPES = ['crn', 'dualCrn', 'rgv', 'annulus'];
+ var DEVP_DIRECTION_OPTIONS = [
+ { key: 'top', label: '涓�', arrow: '鈫�' },
+ { key: 'right', label: '鍙�', arrow: '鈫�' },
+ { key: 'bottom', label: '涓�', arrow: '鈫�' },
+ { key: 'left', label: '宸�', arrow: '鈫�' }
+ ];
+ var idSeed = Date.now();
- var TYPE_META = {
- shelf: { label: '璐ф灦', shortLabel: 'SHELF', fill: 0x7d96bf, border: 0x4f6486 },
- devp: { label: '杈撻�佺嚎', shortLabel: 'DEVP', fill: 0xf0b06f, border: 0xa45f21 },
- crn: { label: '鍫嗗灈鏈鸿建閬�', shortLabel: 'CRN', fill: 0x68bfd0, border: 0x1d6e81 },
- dualCrn: { label: '鍙屽伐浣嶈建閬�', shortLabel: 'DCRN', fill: 0x54c1a4, border: 0x0f7b62 },
- rgv: { label: 'RGV杞ㄩ亾', shortLabel: 'RGV', fill: 0xc691e9, border: 0x744b98 }
+ var G = window.BasMapTrackGeometry;
+ if (!G) {
+ throw new Error('mapTrackGeometry.js must be loaded before editor.js');
+ }
+
+ function nextId() {
+ idSeed += 1;
+ return 'el_' + idSeed;
+ }
+
+ function deepClone(obj) {
+ return JSON.parse(JSON.stringify(obj == null ? null : obj));
+ }
+
+ function padNumber(value) {
+ return value < 10 ? '0' + value : String(value);
+ }
+
+ function authHeaders() {
+ return {
+ token: localStorage.getItem('token')
+ };
+ }
+
+ function getQueryParam(name) {
+ var search = window.location.search || '';
+ if (!search) {
+ return '';
+ }
+ var target = name.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
+ var match = search.match(new RegExp('(?:[?&])' + target + '=([^&]*)'));
+ return match ? decodeURIComponent(match[1]) : '';
+ }
+
+ function toNumber(value, defaultValue) {
+ if (value === null || value === undefined || value === '') {
+ return defaultValue;
+ }
+ var parsed = Number(value);
+ return isFinite(parsed) ? parsed : defaultValue;
+ }
+
+ function toInt(value, defaultValue) {
+ return Math.round(toNumber(value, defaultValue));
+ }
+
+ function clamp(value, min, max) {
+ return Math.max(min, Math.min(max, value));
+ }
+
+ function roundCoord(value) {
+ return Math.round(value * 1000) / 1000;
+ }
+
+ function normalizeValue(value) {
+ if (value === null || value === undefined) {
+ return '';
+ }
+ return typeof value === 'string' ? value : JSON.stringify(value);
+ }
+
+ function parseShelfLocationValue(value) {
+ var text = normalizeValue(value).trim();
+ var matched = text.match(/^(-?\d+)\s*-\s*(-?\d+)$/);
+ if (!matched) {
+ return null;
+ }
+ return {
+ row: toInt(matched[1], 0),
+ col: toInt(matched[2], 0)
+ };
+ }
+
+ function formatShelfLocationValue(row, col) {
+ return String(toInt(row, 0)) + '-' + String(toInt(col, 0));
+ }
+
+ function isShelfLikeNodeType(type) {
+ return type === 'shelf' || type === 'repairHub';
+ }
+
+ function safeParseJson(text) {
+ if (!text || typeof text !== 'string') {
+ return null;
+ }
+ try {
+ return JSON.parse(text);
+ } catch (e) {
+ return null;
+ }
+ }
+
+ function boolFlag(value) {
+ return value === true || value === 1 || value === '1';
+ }
+
+ function normalizeDirectionList(direction) {
+ var list = Array.isArray(direction) ? direction : String(direction || '').split(/[,\s|/]+/);
+ var result = [];
+ var seen = {};
+ for (var i = 0; i < list.length; i++) {
+ var item = String(list[i] || '')
+ .trim()
+ .toLowerCase();
+ if (!item || seen[item]) {
+ continue;
+ }
+ seen[item] = true;
+ result.push(item);
+ }
+ return result;
+ }
+
+ function directionTokenToArrow(token) {
+ if (token === 'top' || token === 'up' || token === 'north' || token === 'n') {
+ return '鈫�';
+ }
+ if (token === 'right' || token === 'east' || token === 'e') {
+ return '鈫�';
+ }
+ if (token === 'bottom' || token === 'down' || token === 'south' || token === 's') {
+ return '鈫�';
+ }
+ if (token === 'left' || token === 'west' || token === 'w') {
+ return '鈫�';
+ }
+ return '';
+ }
+
+ function formatDirectionArrows(direction) {
+ var list = normalizeDirectionList(direction);
+ var arrows = [];
+ for (var i = 0; i < list.length; i++) {
+ var arrow = directionTokenToArrow(list[i]);
+ if (arrow) {
+ arrows.push(arrow);
+ }
+ }
+ return arrows.join('');
+ }
+
+ function isDeviceConfigType(type) {
+ return DEVICE_CONFIG_TYPES.indexOf(type) >= 0;
+ }
+
+ function pickDeviceValueKey(type, json) {
+ if (type === 'crn' || type === 'dualCrn') {
+ return 'crnNo';
+ }
+ if (type === 'rgv') {
+ return 'rgvNo';
+ }
+ return 'deviceNo';
+ }
+
+ function isInputLike(target) {
+ if (!target || !target.tagName) {
+ return false;
+ }
+ var tag = String(target.tagName || '').toLowerCase();
+ return tag === 'input' || tag === 'textarea' || tag === 'select' || !!target.isContentEditable;
+ }
+
+ function rectsOverlap(a, b) {
+ return (
+ a.x < b.x + b.width - COORD_EPSILON &&
+ a.x + a.width > b.x + COORD_EPSILON &&
+ a.y < b.y + b.height - COORD_EPSILON &&
+ a.y + a.height > b.y + COORD_EPSILON
+ );
+ }
+
+ function rectIntersects(a, b) {
+ return (
+ a.x <= b.x + b.width && a.x + a.width >= b.x && a.y <= b.y + b.height && a.y + a.height >= b.y
+ );
+ }
+
+ function isRectWithinCanvas(rect, canvasWidth, canvasHeight) {
+ return (
+ rect.x >= -COORD_EPSILON &&
+ rect.y >= -COORD_EPSILON &&
+ rect.x + rect.width <= canvasWidth + COORD_EPSILON &&
+ rect.y + rect.height <= canvasHeight + COORD_EPSILON
+ );
+ }
+
+ function findDocOverlapId(doc) {
+ if (!doc || !doc.elements || !doc.elements.length) {
+ return '';
+ }
+ var buckets = {};
+ var elements = doc.elements;
+ for (var i = 0; i < elements.length; i++) {
+ var element = elements[i];
+ var minX = Math.floor(element.x / SPATIAL_BUCKET_SIZE);
+ var maxX = Math.floor((element.x + element.width) / SPATIAL_BUCKET_SIZE);
+ var minY = Math.floor(element.y / SPATIAL_BUCKET_SIZE);
+ var maxY = Math.floor((element.y + element.height) / SPATIAL_BUCKET_SIZE);
+ for (var bx = minX; bx <= maxX; bx++) {
+ for (var by = minY; by <= maxY; by++) {
+ var key = bucketKey(bx, by);
+ var bucket = buckets[key];
+ if (!bucket || !bucket.length) {
+ continue;
+ }
+ for (var j = 0; j < bucket.length; j++) {
+ if (rectsOverlap(element, bucket[j])) {
+ return element.id || 'el_' + i;
+ }
+ }
+ }
+ }
+ for (bx = minX; bx <= maxX; bx++) {
+ for (by = minY; by <= maxY; by++) {
+ key = bucketKey(bx, by);
+ if (!buckets[key]) {
+ buckets[key] = [];
+ }
+ buckets[key].push(element);
+ }
+ }
+ }
+ return '';
+ }
+
+ function buildRectFromPoints(a, b) {
+ var left = Math.min(a.x, b.x);
+ var top = Math.min(a.y, b.y);
+ var right = Math.max(a.x, b.x);
+ var bottom = Math.max(a.y, b.y);
+ return {
+ x: roundCoord(left),
+ y: roundCoord(top),
+ width: roundCoord(right - left),
+ height: roundCoord(bottom - top)
+ };
+ }
+
+ function getTypeMeta(type) {
+ return G.TYPE_META[type] || G.TYPE_META.shelf;
+ }
+
+ function rangesNearOrOverlap(a1, a2, b1, b2, tolerance) {
+ return a1 <= b2 + tolerance && a2 >= b1 - tolerance;
+ }
+
+ function bucketKey(x, y) {
+ return x + ':' + y;
+ }
+
+ function getPreferredResolution() {
+ return Math.min(window.devicePixelRatio || 1, 1.25);
+ }
+
+ function drawElementByType(graphics, element, type, style) {
+ const cameraScale = this.camera.scale;
+ const rect = element;
+
+ const drawDeviceList = () => {
+ const deviceForm = G.safeParseJson(rect.value);
+ if (!deviceForm || !deviceForm.deviceList || deviceForm.deviceList.length === 0) {
+ return;
+ }
+
+ const fontSize = Math.max(10 / cameraScale, 6);
+ const textStyle = new PIXI.TextStyle({
+ fontFamily: 'Arial',
+ fontSize: fontSize,
+ fill: '#000000',
+ stroke: '#ffffff',
+ strokeThickness: Math.max(1 / cameraScale, 0.5),
+ align: 'center'
+ });
+
+ const getDeviceNoText = (item) => {
+ if (item == null) return '';
+ if (item.deviceNo != null && String(item.deviceNo).trim() !== '') {
+ return String(item.deviceNo).trim();
+ }
+ // 鍏煎鍘嗗彶瀛楁
+ if (item.crnNo != null && String(item.crnNo).trim() !== '') {
+ return String(item.crnNo).trim();
+ }
+ if (item.rgvNo != null && String(item.rgvNo).trim() !== '') {
+ return String(item.rgvNo).trim();
+ }
+ return '';
+ };
+
+ const newDeviceInfo = G.getDeviceInfo(rect);
+ newDeviceInfo.deviceList.forEach((item) => {
+ // annulus 杞ㄩ亾涓婃斁缃殑鏄� rgv 璁惧
+ const deviceType = type === 'annulus' ? 'rgv' : type;
+ const deviceContainer = new PIXI.Container();
+ const isHorizontal = rect.width > rect.height;
+ const centerX = type === 'annulus' ? item.x : item.x + item.width / 2;
+ const centerY = type === 'annulus' ? item.y : item.y + item.height / 2;
+
+ // 涓� getDeviceInfo / getDevicePixelBoxForTrack 涓�鑷�
+ const drawW = Math.max(2, Math.round(item.width));
+ const drawH = Math.max(2, Math.round(item.height));
+
+ deviceContainer.pivot.set(drawW / 2, drawH / 2);
+ deviceContainer.position.set(centerX, centerY);
+ deviceContainer.rotation =
+ type === 'annulus'
+ ? G.getRotate({ x: item.x, y: item.y }, item.path)
+ : isHorizontal
+ ? 0
+ : Math.PI / 2;
+
+ const deviceGraphics = new PIXI.Graphics();
+ if (deviceType === 'rgv') {
+ G.drawRgvDeviceGraphics(deviceGraphics, drawW, drawH, 0x245a9a);
+ } else if (deviceType === 'crn' || deviceType === 'dualCrn') {
+ G.drawCrnDeviceGraphics(deviceGraphics, drawW, drawH, 0x245a9a);
+ } else {
+ // fallback: keep previous minimal shape for unknown types
+ const radius = Math.max(6 / cameraScale, 2);
+ deviceGraphics.beginFill(style.fill.color, 0.92);
+ deviceGraphics.lineStyle(style.line.width, style.line.color, style.line.alpha);
+ deviceGraphics.drawRoundedRect(0, 0, drawW, drawH, radius);
+ deviceGraphics.endFill();
+ }
+ deviceContainer.addChild(deviceGraphics);
+
+ const txt = getDeviceNoText(item);
+ if (txt) {
+ const text = new PIXI.Text(txt, textStyle);
+ text.anchor.set(0.5);
+ text.position.set(drawW / 2, drawH / 2);
+ deviceContainer.addChild(text);
+ }
+
+ graphics.addChild(deviceContainer);
+ });
};
- function nextId() {
- idSeed += 1;
- return 'el_' + idSeed;
- }
+ graphics.lineStyle(style.line.width, style.line.color, style.line.alpha);
+ graphics.beginFill(style.fill.color, style.fill.alpha);
+ if (type === 'annulus') {
+ G.startDrawSmoothedPath(graphics, element, element.shape || this.annulusShape);
+ drawDeviceList(rect);
+ } else if (isDeviceConfigType(type)) {
+ graphics.lineStyle(0);
+ graphics.beginFill(style.fill.color, style.fill.alpha);
+ graphics.drawRoundedRect(
+ rect.x,
+ rect.y,
+ rect.width,
+ rect.height,
+ Math.max(6 / cameraScale, 2)
+ );
+ graphics.endFill();
+ graphics.lineStyle(style.line.width, style.line.color, style.line.alpha);
+ const isHorizontal = rect.width > rect.height;
- function deepClone(obj) {
- return JSON.parse(JSON.stringify(obj == null ? null : obj));
+ if (isHorizontal) {
+ const center = rect.height / 2;
+ graphics.moveTo(rect.x, rect.y + center);
+ graphics.lineTo(rect.x + rect.width, rect.y + center);
+ } else {
+ const center = rect.width / 2;
+ graphics.moveTo(rect.x + center, rect.y);
+ graphics.lineTo(rect.x + center, rect.y + rect.height);
+ }
+ drawDeviceList(rect);
+ } else {
+ graphics.drawRoundedRect(
+ element.x,
+ element.y,
+ element.width,
+ element.height,
+ Math.max(6 / cameraScale, 2)
+ );
}
+ graphics.endFill();
+ }
- function padNumber(value) {
- return value < 10 ? ('0' + value) : String(value);
- }
-
- function authHeaders() {
- return {
- token: localStorage.getItem('token')
- };
- }
-
- function getQueryParam(name) {
- var search = window.location.search || '';
- if (!search) {
- return '';
+ new Vue({
+ el: '#app',
+ data: function () {
+ return {
+ remoteLevOptions: [],
+ levOptions: [],
+ currentLev: null,
+ floorPickerLev: null,
+ draftDocs: {},
+ doc: null,
+ activeTool: 'select',
+ toolPanelCollapsed: false,
+ inspectorPanelCollapsed: false,
+ interactionTools: [
+ {
+ key: 'select',
+ label: '閫夋嫨 / 绉诲姩',
+ desc: '鐐瑰嚮鍏冪礌閫夋嫨锛屾嫋鎷界Щ鍔紝绌虹櫧澶勬嫋鍔ㄧ敾甯�'
+ },
+ { key: 'marquee', label: '妗嗛��', desc: '鍦ㄧ敾甯冧腑妗嗛�変竴缁勫厓绱�' },
+ {
+ key: 'array',
+ label: '闃靛垪',
+ desc: '閫変腑璐ф灦鎴栫淮淇珯鍙版ā鏉垮悗鎷栫嚎鐢熸垚涓�鎺掞紝鎸夋帓-鍒楃画鍙�'
+ },
+ { key: 'pan', label: '骞崇Щ', desc: '涓撻棬鐢ㄤ簬鎷栧姩鐢诲竷鍜岃瀵熷叏鍥�' }
+ ],
+ drawTools: [
+ { key: 'shelf', label: '璐ф灦', desc: '鑷敱鎷夊嚭璐ф灦鐭╁舰' },
+ { key: 'repairHub', label: '缁翠慨绔欏彴', desc: '鑷敱鎷夊嚭缁翠慨绔欏彴鐭╁舰' },
+ { key: 'devp', label: '杈撻�佺嚎', desc: '鎷夊嚭绔欑偣 / 杈撻�佺嚎鐭╁舰' },
+ { key: 'crn', label: '鍫嗗灈鏈�', desc: '鎷夊嚭鍫嗗灈鏈鸿建閬撶煩褰�' },
+ { key: 'dualCrn', label: '鍙屽伐浣嶅爢鍨涙満', desc: '鎷夊嚭鍙屽伐浣嶈建閬撶煩褰�' },
+ { key: 'rgv', label: 'RGV', desc: '鎷夊嚭 RGV 杞ㄩ亾鐭╁舰' },
+ { key: 'annulus', label: '鐜┛', desc: '鎷夊嚭鐜┛杞ㄩ亾鐭╁舰' }
+ ],
+ pixiApp: null,
+ mapRoot: null,
+ gridLayer: null,
+ trackLayer: null,
+ nodeLayer: null,
+ patchObjectLayer: null,
+ activeLayer: null,
+ labelLayer: null,
+ selectionLayer: null,
+ guideLayer: null,
+ guideText: null,
+ hoverLayer: null,
+ labelPool: [],
+ renderQueued: false,
+ gridSceneDirty: true,
+ staticSceneDirty: true,
+ spatialIndexDirty: true,
+ spatialBuckets: null,
+ gridRenderRect: null,
+ gridRenderKey: '',
+ staticRenderRect: null,
+ staticRenderKey: '',
+ staticExcludedKey: '',
+ camera: {
+ x: 80,
+ y: 80,
+ scale: 1
+ },
+ viewZoom: 1,
+ selectedIds: [],
+ clipboard: [],
+ hoverElementId: '',
+ pointerStatus: '--',
+ lastPointerStatusUpdateTs: 0,
+ pixiResolution: getPreferredResolution(),
+ fpsValue: 0,
+ fpsFrameCount: 0,
+ fpsSampleStartTs: 0,
+ fpsTickerHandler: null,
+ interactionState: null,
+ isZooming: false,
+ isPanning: false,
+ zoomRefreshTimer: null,
+ panRefreshTimer: null,
+ pendingViewportRefresh: false,
+ pendingStaticCommit: null,
+ deferredStaticRebuildTimer: null,
+ currentPointerId: null,
+ boundCanvasHandlers: null,
+ boundWindowHandlers: null,
+ resizeObserver: null,
+ labelCapability: {
+ maxWidth: 0,
+ maxHeight: 0
+ },
+ labelCapabilityDirty: true,
+ undoStack: [],
+ redoStack: [],
+ savedSnapshot: '',
+ isDirty: false,
+ saving: false,
+ savingAll: false,
+ loadingFloor: false,
+ switchingFloorLev: null,
+ floorRequestSeq: 0,
+ activeFloorRequestSeq: 0,
+ blankDialogVisible: false,
+ blankForm: {
+ lev: '',
+ width: String(DEFAULT_CANVAS_WIDTH),
+ height: String(DEFAULT_CANVAS_HEIGHT)
+ },
+ canvasForm: {
+ width: String(DEFAULT_CANVAS_WIDTH),
+ height: String(DEFAULT_CANVAS_HEIGHT)
+ },
+ geometryForm: {
+ x: '',
+ y: '',
+ width: '',
+ height: ''
+ },
+ devpForm: {
+ stationId: '',
+ deviceNo: '',
+ direction: [],
+ isBarcodeStation: false,
+ barcodeIdx: '',
+ backStation: '',
+ backStationDeviceNo: '',
+ isInStation: false,
+ barcodeStation: '',
+ barcodeStationDeviceNo: '',
+ isOutStation: false,
+ runBlockReassign: false,
+ isOutOrder: false,
+ isLiftTransfer: false
+ },
+ deviceForm: {
+ trackId: '',
+ barCodeStart: 0,
+ barCodeEnd: 100000,
+ deviceList: []
+ },
+ devpDirectionOptions: DEVP_DIRECTION_OPTIONS,
+ shelfFillForm: {
+ startValue: '',
+ rowStep: 'desc',
+ colStep: 'asc'
+ },
+ valueEditorText: '',
+ spacePressed: false,
+ lastCursor: 'default',
+ annulusShape: 'rect'
+ };
+ },
+ computed: {
+ singleSelectedElement: function () {
+ if (!this.doc || this.selectedIds.length !== 1) {
+ return null;
}
- var target = name.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
- var match = search.match(new RegExp('(?:[?&])' + target + '=([^&]*)'));
- return match ? decodeURIComponent(match[1]) : '';
- }
-
- function toNumber(value, defaultValue) {
- if (value === null || value === undefined || value === '') {
- return defaultValue;
+ return this.findElementById(this.selectedIds[0]);
+ },
+ singleSelectedDeviceElement: function () {
+ if (!this.singleSelectedElement || !isDeviceConfigType(this.singleSelectedElement.type)) {
+ return null;
}
- var parsed = Number(value);
- return isFinite(parsed) ? parsed : defaultValue;
- }
-
- function toInt(value, defaultValue) {
- return Math.round(toNumber(value, defaultValue));
- }
-
- function clamp(value, min, max) {
- return Math.max(min, Math.min(max, value));
- }
-
- function roundCoord(value) {
- return Math.round(value * 1000) / 1000;
- }
-
- function normalizeValue(value) {
- if (value === null || value === undefined) {
- return '';
+ return this.singleSelectedElement;
+ },
+ selectedShelfElements: function () {
+ if (!this.doc || !this.selectedIds.length) {
+ return [];
}
- return typeof value === 'string' ? value : JSON.stringify(value);
- }
-
- function parseShelfLocationValue(value) {
- var text = normalizeValue(value).trim();
- var matched = text.match(/^(-?\d+)\s*-\s*(-?\d+)$/);
- if (!matched) {
- return null;
+ return this.getSelectedElements().filter(function (item) {
+ return item && isShelfLikeNodeType(item.type);
+ });
+ },
+ devpRequiresBarcodeLink: function () {
+ return !!(this.devpForm && this.devpForm.isInStation);
+ },
+ devpRequiresBarcodeIndex: function () {
+ return !!(this.devpForm && this.devpForm.isBarcodeStation);
+ },
+ devpRequiresBackStation: function () {
+ return !!(this.devpForm && this.devpForm.isBarcodeStation);
+ },
+ arrayPreviewCount: function () {
+ if (!this.interactionState || this.interactionState.type !== 'array') {
+ return 0;
}
- return {
- row: toInt(matched[1], 0),
- col: toInt(matched[2], 0)
- };
- }
-
- function formatShelfLocationValue(row, col) {
- return String(toInt(row, 0)) + '-' + String(toInt(col, 0));
- }
-
- function safeParseJson(text) {
- if (!text || typeof text !== 'string') {
- return null;
- }
- try {
- return JSON.parse(text);
- } catch (e) {
- return null;
- }
- }
-
- function boolFlag(value) {
- return value === true || value === 1 || value === '1';
- }
-
- function normalizeDirectionList(direction) {
- var list = Array.isArray(direction) ? direction : String(direction || '').split(/[,\s|/]+/);
+ return this.interactionState.previewItems ? this.interactionState.previewItems.length : 0;
+ },
+ viewPercent: function () {
+ return Math.round(this.viewZoom * 100);
+ },
+ fpsText: function () {
+ return this.fpsValue > 0 ? String(this.fpsValue) : '--';
+ },
+ dirtyDraftLevs: function () {
var result = [];
var seen = {};
+ if (this.doc && this.doc.lev && this.isDirty) {
+ var currentLev = toInt(this.doc.lev, 0);
+ if (currentLev > 0) {
+ seen[currentLev] = true;
+ result.push(currentLev);
+ }
+ }
+ var self = this;
+ Object.keys(this.draftDocs || {}).forEach(function (key) {
+ var lev = toInt(key, 0);
+ if (lev <= 0 || seen[lev]) {
+ return;
+ }
+ if (self.hasDirtyDraft(lev)) {
+ seen[lev] = true;
+ result.push(lev);
+ }
+ });
+ result.sort(function (a, b) {
+ return a - b;
+ });
+ return result;
+ },
+ dirtyDraftCount: function () {
+ return this.dirtyDraftLevs.length;
+ }
+ },
+ mounted: function () {
+ this.initPixi();
+ this.attachEvents();
+ this.loadLevOptions();
+ var lev = toInt(getQueryParam('lev'), 0);
+ if (lev > 0) {
+ this.floorPickerLev = lev;
+ this.fetchFloor(lev);
+ } else {
+ this.createLocalBlankDoc(1, DEFAULT_CANVAS_WIDTH, DEFAULT_CANVAS_HEIGHT, '');
+ }
+ },
+ beforeDestroy: function () {
+ this.detachEvents();
+ if (this.zoomRefreshTimer) {
+ window.clearTimeout(this.zoomRefreshTimer);
+ this.zoomRefreshTimer = null;
+ }
+ if (this.panRefreshTimer) {
+ window.clearTimeout(this.panRefreshTimer);
+ this.panRefreshTimer = null;
+ }
+ this.clearDeferredStaticCommit();
+ this.stopFpsTicker();
+ if (this.pixiApp) {
+ this.pixiApp.destroy(true, { children: true });
+ this.pixiApp = null;
+ }
+ },
+ methods: {
+ showMessage: function (type, message) {
+ if (this.$message) {
+ this.$message({ type: type, message: message });
+ }
+ },
+ formatNumber: function (value) {
+ var num = toNumber(value, 0);
+ if (Math.abs(num) >= 1000 || num === Math.round(num)) {
+ return String(Math.round(num));
+ }
+ return String(Math.round(num * 100) / 100);
+ },
+ syncFloorQueryParam: function (lev) {
+ lev = toInt(lev, 0);
+ if (lev <= 0 || !window.history || !window.history.replaceState || !window.URL) {
+ return;
+ }
+ try {
+ var url = new URL(window.location.href);
+ url.searchParams.set('lev', String(lev));
+ window.history.replaceState(null, '', url.toString());
+ } catch (e) {
+ // Ignore URL sync failures and keep editor usable.
+ }
+ },
+ toolLabel: function (tool) {
+ var list = this.interactionTools.concat(this.drawTools);
for (var i = 0; i < list.length; i++) {
- var item = String(list[i] || '').trim().toLowerCase();
- if (!item || seen[item]) {
- continue;
+ if (list[i].key === tool) {
+ return list[i].label;
+ }
+ }
+ return tool || '--';
+ },
+ toggleToolPanel: function () {
+ this.toolPanelCollapsed = !this.toolPanelCollapsed;
+ },
+ toggleInspectorPanel: function () {
+ this.inspectorPanelCollapsed = !this.inspectorPanelCollapsed;
+ },
+ startFpsTicker: function () {
+ if (!this.pixiApp || !this.pixiApp.ticker || this.fpsTickerHandler) {
+ return;
+ }
+ var self = this;
+ this.fpsValue = 0;
+ this.fpsFrameCount = 0;
+ this.fpsSampleStartTs =
+ window.performance && performance.now ? performance.now() : Date.now();
+ this.fpsTickerHandler = function () {
+ var now = window.performance && performance.now ? performance.now() : Date.now();
+ self.fpsFrameCount += 1;
+ var elapsed = now - self.fpsSampleStartTs;
+ if (elapsed < 400) {
+ return;
+ }
+ self.fpsValue = Math.max(0, Math.round((self.fpsFrameCount * 1000) / elapsed));
+ self.fpsFrameCount = 0;
+ self.fpsSampleStartTs = now;
+ };
+ this.pixiApp.ticker.add(this.fpsTickerHandler);
+ },
+ stopFpsTicker: function () {
+ if (this.pixiApp && this.pixiApp.ticker && this.fpsTickerHandler) {
+ this.pixiApp.ticker.remove(this.fpsTickerHandler);
+ }
+ this.fpsTickerHandler = null;
+ this.fpsFrameCount = 0;
+ this.fpsSampleStartTs = 0;
+ },
+ initPixi: function () {
+ var host = this.$refs.canvasHost;
+ if (!host) {
+ return;
+ }
+ var resolution = getPreferredResolution();
+ this.pixiResolution = resolution;
+ var app = new PIXI.Application({
+ width: Math.max(host.clientWidth, 320),
+ height: Math.max(host.clientHeight, 320),
+ antialias: false,
+ autoDensity: true,
+ backgroundAlpha: 1,
+ backgroundColor: 0xf6f9fc,
+ resolution: resolution,
+ powerPreference: 'high-performance'
+ });
+ host.innerHTML = '';
+ host.appendChild(app.view);
+ app.view.style.width = '100%';
+ app.view.style.height = '100%';
+ app.view.style.touchAction = 'none';
+ app.view.style.background = '#f6f9fc';
+ app.renderer.roundPixels = true;
+
+ this.pixiApp = app;
+ this.mapRoot = new PIXI.Container();
+ app.stage.addChild(this.mapRoot);
+
+ this.gridLayer = new PIXI.Graphics();
+ this.staticLayer = new PIXI.Container();
+ this.staticTrackSpriteLayer = null;
+ this.staticNodeSpriteLayer = null;
+ this.trackLayer = new PIXI.Graphics();
+ this.nodeLayer = new PIXI.Graphics();
+ this.eraseLayer = new PIXI.Graphics();
+ this.patchObjectLayer = new PIXI.Graphics();
+ this.activeLayer = new PIXI.Graphics();
+ this.labelLayer = new PIXI.Container();
+ this.selectionLayer = new PIXI.Graphics();
+ this.guideLayer = new PIXI.Graphics();
+ this.guideText = new PIXI.Text('', {
+ fontFamily: 'PingFang SC, Microsoft YaHei, sans-serif',
+ fontSize: 14,
+ fontWeight: '700',
+ fill: 0x1f4f86,
+ stroke: 0xffffff,
+ strokeThickness: 4,
+ lineJoin: 'round'
+ });
+ this.guideText.anchor.set(0.5, 1);
+ this.guideText.visible = false;
+ this.hoverLayer = new PIXI.Graphics();
+ this.staticTrackSpritePool = [];
+ this.staticNodeSpritePool = [];
+
+ this.mapRoot.addChild(this.gridLayer);
+ this.staticTrackSpriteLayer = new PIXI.ParticleContainer(
+ 12000,
+ {
+ position: true,
+ scale: true,
+ alpha: true,
+ tint: true
+ },
+ 16384,
+ true
+ );
+ this.staticNodeSpriteLayer = new PIXI.ParticleContainer(
+ 12000,
+ {
+ position: true,
+ scale: true,
+ alpha: true,
+ tint: true
+ },
+ 16384,
+ true
+ );
+ this.staticLayer.addChild(this.staticTrackSpriteLayer);
+ this.staticLayer.addChild(this.trackLayer);
+ this.staticLayer.addChild(this.staticNodeSpriteLayer);
+ this.staticLayer.addChild(this.nodeLayer);
+ this.mapRoot.addChild(this.staticLayer);
+ this.mapRoot.addChild(this.eraseLayer);
+ this.mapRoot.addChild(this.patchObjectLayer);
+ this.mapRoot.addChild(this.activeLayer);
+ this.mapRoot.addChild(this.labelLayer);
+ this.mapRoot.addChild(this.hoverLayer);
+ this.mapRoot.addChild(this.selectionLayer);
+ this.mapRoot.addChild(this.guideLayer);
+ this.mapRoot.addChild(this.guideText);
+
+ this.boundCanvasHandlers = {
+ pointerdown: this.onCanvasPointerDown.bind(this),
+ wheel: this.onCanvasWheel.bind(this)
+ };
+ app.view.addEventListener('pointerdown', this.boundCanvasHandlers.pointerdown);
+ app.view.addEventListener('wheel', this.boundCanvasHandlers.wheel, {
+ passive: false
+ });
+
+ if (window.ResizeObserver) {
+ this.resizeObserver = new ResizeObserver(this.handleResize.bind(this));
+ this.resizeObserver.observe(host);
+ }
+ this.startFpsTicker();
+ this.handleResize();
+ },
+ attachEvents: function () {
+ this.boundWindowHandlers = {
+ pointermove: this.onWindowPointerMove.bind(this),
+ pointerup: this.onWindowPointerUp.bind(this),
+ pointercancel: this.onWindowPointerUp.bind(this),
+ keydown: this.onWindowKeyDown.bind(this),
+ keyup: this.onWindowKeyUp.bind(this),
+ beforeunload: this.onBeforeUnload.bind(this),
+ resize: this.handleResize.bind(this)
+ };
+ window.addEventListener('pointermove', this.boundWindowHandlers.pointermove);
+ window.addEventListener('pointerup', this.boundWindowHandlers.pointerup);
+ window.addEventListener('pointercancel', this.boundWindowHandlers.pointercancel);
+ window.addEventListener('keydown', this.boundWindowHandlers.keydown);
+ window.addEventListener('keyup', this.boundWindowHandlers.keyup);
+ window.addEventListener('beforeunload', this.boundWindowHandlers.beforeunload);
+ window.addEventListener('resize', this.boundWindowHandlers.resize);
+ },
+ detachEvents: function () {
+ if (this.pixiApp && this.boundCanvasHandlers) {
+ this.pixiApp.view.removeEventListener(
+ 'pointerdown',
+ this.boundCanvasHandlers.pointerdown
+ );
+ this.pixiApp.view.removeEventListener('wheel', this.boundCanvasHandlers.wheel);
+ }
+ if (this.resizeObserver) {
+ this.resizeObserver.disconnect();
+ this.resizeObserver = null;
+ }
+ if (!this.boundWindowHandlers) {
+ return;
+ }
+ window.removeEventListener('pointermove', this.boundWindowHandlers.pointermove);
+ window.removeEventListener('pointerup', this.boundWindowHandlers.pointerup);
+ window.removeEventListener('pointercancel', this.boundWindowHandlers.pointercancel);
+ window.removeEventListener('keydown', this.boundWindowHandlers.keydown);
+ window.removeEventListener('keyup', this.boundWindowHandlers.keyup);
+ window.removeEventListener('beforeunload', this.boundWindowHandlers.beforeunload);
+ window.removeEventListener('resize', this.boundWindowHandlers.resize);
+ },
+ handleResize: function () {
+ if (!this.pixiApp || !this.$refs.canvasHost) {
+ return;
+ }
+ var host = this.$refs.canvasHost;
+ var width = Math.max(host.clientWidth, 320);
+ var height = Math.max(host.clientHeight, 320);
+ this.pixiApp.renderer.resize(width, height);
+ this.markGridSceneDirty();
+ this.markStaticSceneDirty();
+ this.scheduleRender();
+ },
+ loadLevOptions: function () {
+ var self = this;
+ $.ajax({
+ url: baseUrl + '/basMap/getLevList',
+ method: 'GET',
+ headers: authHeaders(),
+ success: function (res) {
+ if (res && res.code === 200 && Array.isArray(res.data)) {
+ self.remoteLevOptions = res.data
+ .map(function (item) {
+ return toInt(item, 0);
+ })
+ .filter(function (item) {
+ return item > 0;
+ });
+ self.refreshLevOptions();
}
- seen[item] = true;
- result.push(item);
+ }
+ });
+ },
+ refreshLevOptions: function () {
+ var set = {};
+ var result = [];
+ var pushLev = function (lev) {
+ lev = toInt(lev, 0);
+ if (lev <= 0 || set[lev]) {
+ return;
+ }
+ set[lev] = true;
+ result.push(lev);
+ };
+ this.remoteLevOptions.forEach(pushLev);
+ Object.keys(this.draftDocs || {}).forEach(pushLev);
+ pushLev(this.currentLev);
+ pushLev(this.floorPickerLev);
+ result.sort(function (a, b) {
+ return a - b;
+ });
+ this.levOptions = result;
+ },
+ exportDoc: function (doc) {
+ var source = doc || this.doc || {};
+ return {
+ lev: toInt(source.lev, 0),
+ editorMode: FREE_EDITOR_MODE,
+ canvasWidth: roundCoord(toNumber(source.canvasWidth, DEFAULT_CANVAS_WIDTH)),
+ canvasHeight: roundCoord(toNumber(source.canvasHeight, DEFAULT_CANVAS_HEIGHT)),
+ elements: (source.elements || []).map(function (item, index) {
+ const info = {
+ id: item && item.id ? String(item.id) : 'el_' + (index + 1),
+ type: DRAW_TYPES.indexOf(item && item.type) >= 0 ? item.type : 'shelf',
+ x: roundCoord(Math.max(0, toNumber(item && item.x, 0))),
+ y: roundCoord(Math.max(0, toNumber(item && item.y, 0))),
+ width: roundCoord(
+ Math.max(MIN_ELEMENT_SIZE, toNumber(item && item.width, MIN_ELEMENT_SIZE))
+ ),
+ height: roundCoord(
+ Math.max(MIN_ELEMENT_SIZE, toNumber(item && item.height, MIN_ELEMENT_SIZE))
+ ),
+ value: normalizeValue(item && item.value),
+ shape: item.shape,
+ pathList: item.pathList,
+ turningPoint: item.turningPoint
+ ? { x: toNumber(item.turningPoint.x, 0), y: toNumber(item.turningPoint.y, 0) }
+ : undefined,
+ annulusBandInset:
+ item.annulusBandInset != null ? toNumber(item.annulusBandInset, 0) : undefined
+ };
+ return info;
+ })
+ };
+ },
+ normalizeDoc: function (doc) {
+ var normalized = this.exportDoc(doc || {});
+ if (normalized.lev <= 0) {
+ normalized.lev = toInt(this.currentLev, 1) || 1;
+ }
+ return normalized;
+ },
+ snapshotDoc: function (doc) {
+ return JSON.stringify(this.exportDoc(doc));
+ },
+ syncDirty: function () {
+ var currentSnapshot = this.snapshotDoc(this.doc);
+ this.isDirty = currentSnapshot !== this.savedSnapshot;
+ },
+ setDraftDocEntry: function (lev, doc, savedSnapshot) {
+ lev = toInt(lev, 0);
+ if (lev <= 0 || !doc) {
+ return;
+ }
+ var entry = {
+ doc: this.exportDoc(doc),
+ savedSnapshot: savedSnapshot != null ? savedSnapshot : ''
+ };
+ if (this.$set) {
+ this.$set(this.draftDocs, lev, entry);
+ } else {
+ this.draftDocs[lev] = entry;
+ }
+ },
+ removeDraftDocEntry: function (lev) {
+ lev = toInt(lev, 0);
+ if (lev <= 0) {
+ return;
+ }
+ if (this.$delete) {
+ this.$delete(this.draftDocs, lev);
+ } else {
+ delete this.draftDocs[lev];
+ }
+ },
+ cacheCurrentDraft: function () {
+ if (!this.doc || !this.doc.lev) {
+ return;
+ }
+ this.setDraftDocEntry(this.doc.lev, this.doc, this.savedSnapshot);
+ this.refreshLevOptions();
+ },
+ clearCurrentDraftIfSaved: function () {
+ if (!this.doc || !this.doc.lev) {
+ return;
+ }
+ this.setDraftDocEntry(this.doc.lev, this.doc, this.savedSnapshot);
+ },
+ clearFloorTransientState: function () {
+ this.clearDeferredStaticCommit();
+ this.interactionState = null;
+ this.currentPointerId = null;
+ this.hoverElementId = '';
+ this.pointerStatus = '--';
+ this.lastPointerStatusUpdateTs = 0;
+ this.selectedIds = [];
+ this.isPanning = false;
+ this.isZooming = false;
+ this.pendingViewportRefresh = false;
+ if (this.zoomRefreshTimer) {
+ window.clearTimeout(this.zoomRefreshTimer);
+ this.zoomRefreshTimer = null;
+ }
+ if (this.panRefreshTimer) {
+ window.clearTimeout(this.panRefreshTimer);
+ this.panRefreshTimer = null;
+ }
+ },
+ resetRenderLayers: function () {
+ if (this.gridLayer) {
+ this.gridLayer.clear();
+ }
+ if (this.trackLayer) {
+ this.trackLayer.clear();
+ this.trackLayer.removeChildren();
+ }
+ if (this.nodeLayer) {
+ this.nodeLayer.clear();
+ this.nodeLayer.removeChildren();
+ }
+ if (this.eraseLayer) {
+ this.eraseLayer.clear();
+ }
+ if (this.patchObjectLayer) {
+ this.patchObjectLayer.clear();
+ this.patchObjectLayer.removeChildren();
+ }
+ if (this.activeLayer) {
+ this.activeLayer.clear();
+ this.activeLayer.removeChildren();
+ }
+ if (this.selectionLayer) {
+ this.selectionLayer.clear();
+ this.selectionLayer.removeChildren();
+ }
+ if (this.guideLayer) {
+ this.guideLayer.clear();
+ this.guideLayer.removeChildren();
+ }
+ if (this.hoverLayer) {
+ this.hoverLayer.clear();
+ }
+ if (this.guideText) {
+ this.guideText.visible = false;
+ this.guideText.text = '';
+ }
+ if (this.labelLayer) {
+ this.labelLayer.visible = false;
+ }
+ for (var i = 0; i < this.labelPool.length; i++) {
+ this.labelPool[i].visible = false;
+ this.labelPool[i].text = '';
+ }
+ this.hideUnusedStaticSprites(this.staticTrackSpritePool || [], 0);
+ this.hideUnusedStaticSprites(this.staticNodeSpritePool || [], 0);
+ if (this.staticTrackSpriteLayer) {
+ this.staticTrackSpriteLayer.removeChildren();
+ this.staticTrackSpritePool = [];
+ }
+ if (this.staticNodeSpriteLayer) {
+ this.staticNodeSpriteLayer.removeChildren();
+ this.staticNodeSpritePool = [];
+ }
+ if (this.staticTrackSpriteLayer) {
+ this.staticTrackSpriteLayer.visible = false;
+ }
+ if (this.staticNodeSpriteLayer) {
+ this.staticNodeSpriteLayer.visible = false;
+ }
+ },
+ hasDirtyDraft: function (lev) {
+ lev = toInt(lev, 0);
+ if (lev <= 0) {
+ return false;
+ }
+ var entry = this.draftDocs[lev];
+ if (!entry || !entry.doc) {
+ return false;
+ }
+ var snapshot = this.snapshotDoc(entry.doc);
+ return snapshot !== (entry.savedSnapshot || '');
+ },
+ markStaticSceneDirty: function () {
+ this.staticSceneDirty = true;
+ },
+ markGridSceneDirty: function () {
+ this.gridSceneDirty = true;
+ },
+ clearRenderCaches: function () {
+ this.gridRenderRect = null;
+ this.gridRenderKey = '';
+ this.staticRenderRect = null;
+ this.staticRenderKey = '';
+ this.staticExcludedKey = '';
+ },
+ scheduleZoomRefresh: function () {
+ if (this.zoomRefreshTimer) {
+ window.clearTimeout(this.zoomRefreshTimer);
+ }
+ this.isZooming = true;
+ this.zoomRefreshTimer = window.setTimeout(
+ function () {
+ this.zoomRefreshTimer = null;
+ this.isZooming = false;
+ if (this.isPanning || (this.interactionState && this.interactionState.type === 'pan')) {
+ this.pendingViewportRefresh = true;
+ return;
+ }
+ this.markGridSceneDirty();
+ this.markStaticSceneDirty();
+ this.scheduleRender();
+ }.bind(this),
+ ZOOM_REFRESH_DELAY
+ );
+ },
+ cancelPanRefresh: function () {
+ if (this.panRefreshTimer) {
+ window.clearTimeout(this.panRefreshTimer);
+ this.panRefreshTimer = null;
+ }
+ },
+ schedulePanRefresh: function () {
+ this.cancelPanRefresh();
+ this.isPanning = true;
+ this.panRefreshTimer = window.setTimeout(
+ function () {
+ this.panRefreshTimer = null;
+ this.isPanning = false;
+ if (this.pendingViewportRefresh) {
+ this.pendingViewportRefresh = false;
+ this.markGridSceneDirty();
+ this.markStaticSceneDirty();
+ }
+ this.scheduleRender();
+ }.bind(this),
+ PAN_LABEL_REFRESH_DELAY
+ );
+ },
+ rebuildLabelCapability: function () {
+ var maxWidth = 0;
+ var maxHeight = 0;
+ var elements = this.doc && this.doc.elements ? this.doc.elements : [];
+ for (var i = 0; i < elements.length; i++) {
+ var element = elements[i];
+ if (element.width > maxWidth) {
+ maxWidth = element.width;
+ }
+ if (element.height > maxHeight) {
+ maxHeight = element.height;
+ }
+ }
+ this.labelCapability = {
+ maxWidth: maxWidth,
+ maxHeight: maxHeight
+ };
+ this.labelCapabilityDirty = false;
+ },
+ ensureLabelCapability: function () {
+ if (this.labelCapabilityDirty) {
+ this.rebuildLabelCapability();
+ }
+ return this.labelCapability;
+ },
+ markSpatialIndexDirty: function () {
+ this.spatialIndexDirty = true;
+ },
+ rebuildSpatialIndex: function () {
+ var buckets = {};
+ var elements = this.doc && this.doc.elements ? this.doc.elements : [];
+ for (var i = 0; i < elements.length; i++) {
+ var element = elements[i];
+ var minX = Math.floor(element.x / SPATIAL_BUCKET_SIZE);
+ var maxX = Math.floor((element.x + element.width) / SPATIAL_BUCKET_SIZE);
+ var minY = Math.floor(element.y / SPATIAL_BUCKET_SIZE);
+ var maxY = Math.floor((element.y + element.height) / SPATIAL_BUCKET_SIZE);
+ for (var bx = minX; bx <= maxX; bx++) {
+ for (var by = minY; by <= maxY; by++) {
+ var key = bucketKey(bx, by);
+ if (!buckets[key]) {
+ buckets[key] = [];
+ }
+ buckets[key].push(element);
+ }
+ }
+ }
+ this.spatialBuckets = buckets;
+ this.spatialIndexDirty = false;
+ },
+ ensureSpatialIndex: function () {
+ if (this.spatialIndexDirty || !this.spatialBuckets) {
+ this.rebuildSpatialIndex();
+ }
+ },
+ querySpatialCandidates: function (rect, padding, excludeIds) {
+ if (!this.doc || !rect) {
+ return [];
+ }
+ this.ensureSpatialIndex();
+ var excludeMap = {};
+ excludeIds = excludeIds || [];
+ for (var i = 0; i < excludeIds.length; i++) {
+ excludeMap[excludeIds[i]] = true;
+ }
+ var seen = {};
+ var result = [];
+ var pad = Math.max(0, padding || 0);
+ var minX = Math.floor((rect.x - pad) / SPATIAL_BUCKET_SIZE);
+ var maxX = Math.floor((rect.x + rect.width + pad) / SPATIAL_BUCKET_SIZE);
+ var minY = Math.floor((rect.y - pad) / SPATIAL_BUCKET_SIZE);
+ var maxY = Math.floor((rect.y + rect.height + pad) / SPATIAL_BUCKET_SIZE);
+ for (var bx = minX; bx <= maxX; bx++) {
+ for (var by = minY; by <= maxY; by++) {
+ var key = bucketKey(bx, by);
+ var bucket = this.spatialBuckets[key];
+ if (!bucket || !bucket.length) {
+ continue;
+ }
+ for (var j = 0; j < bucket.length; j++) {
+ var element = bucket[j];
+ if (!element || seen[element.id] || excludeMap[element.id]) {
+ continue;
+ }
+ seen[element.id] = true;
+ result.push(element);
+ }
+ }
}
return result;
- }
-
- function directionTokenToArrow(token) {
- if (token === 'top' || token === 'up' || token === 'north' || token === 'n') {
- return '鈫�';
+ },
+ cancelDeferredStaticRebuild: function () {
+ if (this.deferredStaticRebuildTimer) {
+ window.clearTimeout(this.deferredStaticRebuildTimer);
+ this.deferredStaticRebuildTimer = null;
}
- if (token === 'right' || token === 'east' || token === 'e') {
- return '鈫�';
- }
- if (token === 'bottom' || token === 'down' || token === 'south' || token === 's') {
- return '鈫�';
- }
- if (token === 'left' || token === 'west' || token === 'w') {
- return '鈫�';
- }
- return '';
- }
-
- function formatDirectionArrows(direction) {
- var list = normalizeDirectionList(direction);
- var arrows = [];
- for (var i = 0; i < list.length; i++) {
- var arrow = directionTokenToArrow(list[i]);
- if (arrow) {
- arrows.push(arrow);
- }
- }
- return arrows.join('');
- }
-
- function isDeviceConfigType(type) {
- return DEVICE_CONFIG_TYPES.indexOf(type) >= 0;
- }
-
- function pickDeviceValueKey(type, json) {
- if (json && json.deviceNo != null) {
- return 'deviceNo';
- }
- if ((type === 'crn' || type === 'dualCrn') && json && json.crnNo != null) {
- return 'crnNo';
- }
- if (type === 'rgv' && json && json.rgvNo != null) {
- return 'rgvNo';
- }
- return 'deviceNo';
- }
-
- function isInputLike(target) {
- if (!target || !target.tagName) {
- return false;
- }
- var tag = String(target.tagName || '').toLowerCase();
- return tag === 'input' || tag === 'textarea' || tag === 'select' || !!target.isContentEditable;
- }
-
- function rectsOverlap(a, b) {
- return a.x < b.x + b.width - COORD_EPSILON && a.x + a.width > b.x + COORD_EPSILON
- && a.y < b.y + b.height - COORD_EPSILON && a.y + a.height > b.y + COORD_EPSILON;
- }
-
- function rectIntersects(a, b) {
- return a.x <= b.x + b.width && a.x + a.width >= b.x
- && a.y <= b.y + b.height && a.y + a.height >= b.y;
- }
-
- function isRectWithinCanvas(rect, canvasWidth, canvasHeight) {
- return rect.x >= -COORD_EPSILON && rect.y >= -COORD_EPSILON
- && rect.x + rect.width <= canvasWidth + COORD_EPSILON
- && rect.y + rect.height <= canvasHeight + COORD_EPSILON;
- }
-
- function findDocOverlapId(doc) {
- if (!doc || !doc.elements || !doc.elements.length) {
- return '';
- }
- var buckets = {};
- var elements = doc.elements;
- for (var i = 0; i < elements.length; i++) {
- var element = elements[i];
- var minX = Math.floor(element.x / SPATIAL_BUCKET_SIZE);
- var maxX = Math.floor((element.x + element.width) / SPATIAL_BUCKET_SIZE);
- var minY = Math.floor(element.y / SPATIAL_BUCKET_SIZE);
- var maxY = Math.floor((element.y + element.height) / SPATIAL_BUCKET_SIZE);
- for (var bx = minX; bx <= maxX; bx++) {
- for (var by = minY; by <= maxY; by++) {
- var key = bucketKey(bx, by);
- var bucket = buckets[key];
- if (!bucket || !bucket.length) {
- continue;
- }
- for (var j = 0; j < bucket.length; j++) {
- if (rectsOverlap(element, bucket[j])) {
- return element.id || ('el_' + i);
- }
- }
- }
- }
- for (bx = minX; bx <= maxX; bx++) {
- for (by = minY; by <= maxY; by++) {
- key = bucketKey(bx, by);
- if (!buckets[key]) {
- buckets[key] = [];
- }
- buckets[key].push(element);
- }
- }
- }
- return '';
- }
-
- function buildRectFromPoints(a, b) {
- var left = Math.min(a.x, b.x);
- var top = Math.min(a.y, b.y);
- var right = Math.max(a.x, b.x);
- var bottom = Math.max(a.y, b.y);
- return {
- x: roundCoord(left),
- y: roundCoord(top),
- width: roundCoord(right - left),
- height: roundCoord(bottom - top)
- };
- }
-
- function getTypeMeta(type) {
- return TYPE_META[type] || TYPE_META.shelf;
- }
-
- function rangesNearOrOverlap(a1, a2, b1, b2, tolerance) {
- return a1 <= b2 + tolerance && a2 >= b1 - tolerance;
- }
-
- function bucketKey(x, y) {
- return x + ':' + y;
- }
-
- function getPreferredResolution() {
- return Math.min(window.devicePixelRatio || 1, 1.25);
- }
-
- new Vue({
- el: '#app',
- data: function () {
+ },
+ stageDeferredStaticCommit: function (ids, eraseRects) {
+ this.pendingStaticCommit = {
+ ids: (ids || []).slice(),
+ eraseRects: (eraseRects || []).map(function (item) {
return {
- remoteLevOptions: [],
- levOptions: [],
- currentLev: null,
- floorPickerLev: null,
- draftDocs: {},
- doc: null,
- activeTool: 'select',
- toolPanelCollapsed: false,
- inspectorPanelCollapsed: false,
- interactionTools: [
- { key: 'select', label: '閫夋嫨 / 绉诲姩', desc: '鐐瑰嚮鍏冪礌閫夋嫨锛屾嫋鎷界Щ鍔紝绌虹櫧澶勬嫋鍔ㄧ敾甯�' },
- { key: 'marquee', label: '妗嗛��', desc: '鍦ㄧ敾甯冧腑妗嗛�変竴缁勫厓绱�' },
- { key: 'array', label: '闃靛垪', desc: '閫変腑涓�涓揣鏋� / 杞ㄩ亾鍚庢嫋涓�鏉$嚎鑷姩鐢熸垚涓�鎺�' },
- { key: 'pan', label: '骞崇Щ', desc: '涓撻棬鐢ㄤ簬鎷栧姩鐢诲竷鍜岃瀵熷叏鍥�' }
- ],
- drawTools: [
- { key: 'shelf', label: '璐ф灦', desc: '鑷敱鎷夊嚭璐ф灦鐭╁舰' },
- { key: 'devp', label: '杈撻�佺嚎', desc: '鎷夊嚭绔欑偣 / 杈撻�佺嚎鐭╁舰' },
- { key: 'crn', label: 'CRN', desc: '鎷夊嚭鍫嗗灈鏈鸿建閬撶煩褰�' },
- { key: 'dualCrn', label: '鍙屽伐浣�', desc: '鎷夊嚭鍙屽伐浣嶈建閬撶煩褰�' },
- { key: 'rgv', label: 'RGV', desc: '鎷夊嚭 RGV 杞ㄩ亾鐭╁舰' }
- ],
- pixiApp: null,
- mapRoot: null,
- gridLayer: null,
- trackLayer: null,
- nodeLayer: null,
- patchObjectLayer: null,
- activeLayer: null,
- labelLayer: null,
- selectionLayer: null,
- guideLayer: null,
- guideText: null,
- hoverLayer: null,
- labelPool: [],
- renderQueued: false,
- gridSceneDirty: true,
- staticSceneDirty: true,
- spatialIndexDirty: true,
- spatialBuckets: null,
- gridRenderRect: null,
- gridRenderKey: '',
- staticRenderRect: null,
- staticRenderKey: '',
- staticExcludedKey: '',
- camera: {
- x: 80,
- y: 80,
- scale: 1
- },
- viewZoom: 1,
- selectedIds: [],
- clipboard: [],
- hoverElementId: '',
- pointerStatus: '--',
- lastPointerStatusUpdateTs: 0,
- pixiResolution: getPreferredResolution(),
- fpsValue: 0,
- fpsFrameCount: 0,
- fpsSampleStartTs: 0,
- fpsTickerHandler: null,
- interactionState: null,
- isZooming: false,
- isPanning: false,
- zoomRefreshTimer: null,
- panRefreshTimer: null,
- pendingViewportRefresh: false,
- pendingStaticCommit: null,
- deferredStaticRebuildTimer: null,
- currentPointerId: null,
- boundCanvasHandlers: null,
- boundWindowHandlers: null,
- resizeObserver: null,
- labelCapability: {
- maxWidth: 0,
- maxHeight: 0
- },
- labelCapabilityDirty: true,
- undoStack: [],
- redoStack: [],
- savedSnapshot: '',
- isDirty: false,
- saving: false,
- savingAll: false,
- loadingFloor: false,
- switchingFloorLev: null,
- floorRequestSeq: 0,
- activeFloorRequestSeq: 0,
- blankDialogVisible: false,
- blankForm: {
- lev: '',
- width: String(DEFAULT_CANVAS_WIDTH),
- height: String(DEFAULT_CANVAS_HEIGHT)
- },
- canvasForm: {
- width: String(DEFAULT_CANVAS_WIDTH),
- height: String(DEFAULT_CANVAS_HEIGHT)
- },
- geometryForm: {
- x: '',
- y: '',
- width: '',
- height: ''
- },
- devpForm: {
- stationId: '',
- deviceNo: '',
- direction: [],
- isBarcodeStation: false,
- barcodeIdx: '',
- backStation: '',
- backStationDeviceNo: '',
- isInStation: false,
- barcodeStation: '',
- barcodeStationDeviceNo: '',
- isOutStation: false,
- runBlockReassign: false,
- isOutOrder: false,
- isLiftTransfer: false
- },
- deviceForm: {
- valueKey: '',
- deviceNo: ''
- },
- devpDirectionOptions: DEVP_DIRECTION_OPTIONS,
- shelfFillForm: {
- startValue: '',
- rowStep: 'desc',
- colStep: 'asc'
- },
- valueEditorText: '',
- spacePressed: false,
- lastCursor: 'default'
+ x: item.x,
+ y: item.y,
+ width: item.width,
+ height: item.height
};
- },
- computed: {
- singleSelectedElement: function () {
- if (!this.doc || this.selectedIds.length !== 1) {
- return null;
- }
- return this.findElementById(this.selectedIds[0]);
- },
- singleSelectedDeviceElement: function () {
- if (!this.singleSelectedElement || !isDeviceConfigType(this.singleSelectedElement.type)) {
- return null;
- }
- return this.singleSelectedElement;
- },
- selectedShelfElements: function () {
- if (!this.doc || !this.selectedIds.length) {
- return [];
- }
- return this.getSelectedElements().filter(function (item) {
- return item && item.type === 'shelf';
- });
- },
- devpRequiresBarcodeLink: function () {
- return !!(this.devpForm && this.devpForm.isInStation);
- },
- devpRequiresBarcodeIndex: function () {
- return !!(this.devpForm && this.devpForm.isBarcodeStation);
- },
- devpRequiresBackStation: function () {
- return !!(this.devpForm && this.devpForm.isBarcodeStation);
- },
- arrayPreviewCount: function () {
- if (!this.interactionState || this.interactionState.type !== 'array') {
- return 0;
- }
- return this.interactionState.previewItems ? this.interactionState.previewItems.length : 0;
- },
- viewPercent: function () {
- return Math.round(this.viewZoom * 100);
- },
- fpsText: function () {
- return this.fpsValue > 0 ? String(this.fpsValue) : '--';
- },
- dirtyDraftLevs: function () {
- var result = [];
- var seen = {};
- if (this.doc && this.doc.lev && this.isDirty) {
- var currentLev = toInt(this.doc.lev, 0);
- if (currentLev > 0) {
- seen[currentLev] = true;
- result.push(currentLev);
- }
- }
- var self = this;
- Object.keys(this.draftDocs || {}).forEach(function (key) {
- var lev = toInt(key, 0);
- if (lev <= 0 || seen[lev]) {
- return;
- }
- if (self.hasDirtyDraft(lev)) {
- seen[lev] = true;
- result.push(lev);
- }
- });
- result.sort(function (a, b) { return a - b; });
- return result;
- },
- dirtyDraftCount: function () {
- return this.dirtyDraftLevs.length;
- }
- },
- mounted: function () {
- this.initPixi();
- this.attachEvents();
- this.loadLevOptions();
- var lev = toInt(getQueryParam('lev'), 0);
- if (lev > 0) {
- this.floorPickerLev = lev;
- this.fetchFloor(lev);
- } else {
- this.createLocalBlankDoc(1, DEFAULT_CANVAS_WIDTH, DEFAULT_CANVAS_HEIGHT, '');
- }
- },
- beforeDestroy: function () {
- this.detachEvents();
- if (this.zoomRefreshTimer) {
- window.clearTimeout(this.zoomRefreshTimer);
- this.zoomRefreshTimer = null;
- }
- if (this.panRefreshTimer) {
- window.clearTimeout(this.panRefreshTimer);
- this.panRefreshTimer = null;
- }
- this.clearDeferredStaticCommit();
- this.stopFpsTicker();
- if (this.pixiApp) {
- this.pixiApp.destroy(true, { children: true });
- this.pixiApp = null;
- }
- },
- methods: {
- showMessage: function (type, message) {
- if (this.$message) {
- this.$message({ type: type, message: message });
- }
- },
- formatNumber: function (value) {
- var num = toNumber(value, 0);
- if (Math.abs(num) >= 1000 || num === Math.round(num)) {
- return String(Math.round(num));
- }
- return String(Math.round(num * 100) / 100);
- },
- syncFloorQueryParam: function (lev) {
- lev = toInt(lev, 0);
- if (lev <= 0 || !window.history || !window.history.replaceState || !window.URL) {
- return;
- }
- try {
- var url = new URL(window.location.href);
- url.searchParams.set('lev', String(lev));
- window.history.replaceState(null, '', url.toString());
- } catch (e) {
- // Ignore URL sync failures and keep editor usable.
- }
- },
- toolLabel: function (tool) {
- var list = this.interactionTools.concat(this.drawTools);
- for (var i = 0; i < list.length; i++) {
- if (list[i].key === tool) {
- return list[i].label;
- }
- }
- return tool || '--';
- },
- toggleToolPanel: function () {
- this.toolPanelCollapsed = !this.toolPanelCollapsed;
- },
- toggleInspectorPanel: function () {
- this.inspectorPanelCollapsed = !this.inspectorPanelCollapsed;
- },
- startFpsTicker: function () {
- if (!this.pixiApp || !this.pixiApp.ticker || this.fpsTickerHandler) {
- return;
- }
- var self = this;
- this.fpsValue = 0;
- this.fpsFrameCount = 0;
- this.fpsSampleStartTs = (window.performance && performance.now) ? performance.now() : Date.now();
- this.fpsTickerHandler = function () {
- var now = (window.performance && performance.now) ? performance.now() : Date.now();
- self.fpsFrameCount += 1;
- var elapsed = now - self.fpsSampleStartTs;
- if (elapsed < 400) {
- return;
- }
- self.fpsValue = Math.max(0, Math.round(self.fpsFrameCount * 1000 / elapsed));
- self.fpsFrameCount = 0;
- self.fpsSampleStartTs = now;
- };
- this.pixiApp.ticker.add(this.fpsTickerHandler);
- },
- stopFpsTicker: function () {
- if (this.pixiApp && this.pixiApp.ticker && this.fpsTickerHandler) {
- this.pixiApp.ticker.remove(this.fpsTickerHandler);
- }
- this.fpsTickerHandler = null;
- this.fpsFrameCount = 0;
- this.fpsSampleStartTs = 0;
- },
- initPixi: function () {
- var host = this.$refs.canvasHost;
- if (!host) {
- return;
- }
- var resolution = getPreferredResolution();
- this.pixiResolution = resolution;
- var app = new PIXI.Application({
- width: Math.max(host.clientWidth, 320),
- height: Math.max(host.clientHeight, 320),
- antialias: false,
- autoDensity: true,
- backgroundAlpha: 1,
- backgroundColor: 0xf6f9fc,
- resolution: resolution,
- powerPreference: 'high-performance'
- });
- host.innerHTML = '';
- host.appendChild(app.view);
- app.view.style.width = '100%';
- app.view.style.height = '100%';
- app.view.style.touchAction = 'none';
- app.view.style.background = '#f6f9fc';
- app.renderer.roundPixels = true;
-
- this.pixiApp = app;
- this.mapRoot = new PIXI.Container();
- app.stage.addChild(this.mapRoot);
-
- this.gridLayer = new PIXI.Graphics();
- this.staticLayer = new PIXI.Container();
- this.staticTrackSpriteLayer = null;
- this.staticNodeSpriteLayer = null;
- this.trackLayer = new PIXI.Graphics();
- this.nodeLayer = new PIXI.Graphics();
- this.eraseLayer = new PIXI.Graphics();
- this.patchObjectLayer = new PIXI.Graphics();
- this.activeLayer = new PIXI.Graphics();
- this.labelLayer = new PIXI.Container();
- this.selectionLayer = new PIXI.Graphics();
- this.guideLayer = new PIXI.Graphics();
- this.guideText = new PIXI.Text('', {
- fontFamily: 'PingFang SC, Microsoft YaHei, sans-serif',
- fontSize: 14,
- fontWeight: '700',
- fill: 0x1f4f86,
- stroke: 0xffffff,
- strokeThickness: 4,
- lineJoin: 'round'
- });
- this.guideText.anchor.set(0.5, 1);
- this.guideText.visible = false;
- this.hoverLayer = new PIXI.Graphics();
- this.staticTrackSpritePool = [];
- this.staticNodeSpritePool = [];
-
- this.mapRoot.addChild(this.gridLayer);
- this.staticTrackSpriteLayer = new PIXI.ParticleContainer(12000, {
- position: true,
- scale: true,
- alpha: true,
- tint: true
- }, 16384, true);
- this.staticNodeSpriteLayer = new PIXI.ParticleContainer(12000, {
- position: true,
- scale: true,
- alpha: true,
- tint: true
- }, 16384, true);
- this.staticLayer.addChild(this.staticTrackSpriteLayer);
- this.staticLayer.addChild(this.trackLayer);
- this.staticLayer.addChild(this.staticNodeSpriteLayer);
- this.staticLayer.addChild(this.nodeLayer);
- this.mapRoot.addChild(this.staticLayer);
- this.mapRoot.addChild(this.eraseLayer);
- this.mapRoot.addChild(this.patchObjectLayer);
- this.mapRoot.addChild(this.activeLayer);
- this.mapRoot.addChild(this.labelLayer);
- this.mapRoot.addChild(this.hoverLayer);
- this.mapRoot.addChild(this.selectionLayer);
- this.mapRoot.addChild(this.guideLayer);
- this.mapRoot.addChild(this.guideText);
-
- this.boundCanvasHandlers = {
- pointerdown: this.onCanvasPointerDown.bind(this),
- wheel: this.onCanvasWheel.bind(this)
- };
- app.view.addEventListener('pointerdown', this.boundCanvasHandlers.pointerdown);
- app.view.addEventListener('wheel', this.boundCanvasHandlers.wheel, { passive: false });
-
- if (window.ResizeObserver) {
- this.resizeObserver = new ResizeObserver(this.handleResize.bind(this));
- this.resizeObserver.observe(host);
- }
- this.startFpsTicker();
- this.handleResize();
- },
- attachEvents: function () {
- this.boundWindowHandlers = {
- pointermove: this.onWindowPointerMove.bind(this),
- pointerup: this.onWindowPointerUp.bind(this),
- pointercancel: this.onWindowPointerUp.bind(this),
- keydown: this.onWindowKeyDown.bind(this),
- keyup: this.onWindowKeyUp.bind(this),
- beforeunload: this.onBeforeUnload.bind(this),
- resize: this.handleResize.bind(this)
- };
- window.addEventListener('pointermove', this.boundWindowHandlers.pointermove);
- window.addEventListener('pointerup', this.boundWindowHandlers.pointerup);
- window.addEventListener('pointercancel', this.boundWindowHandlers.pointercancel);
- window.addEventListener('keydown', this.boundWindowHandlers.keydown);
- window.addEventListener('keyup', this.boundWindowHandlers.keyup);
- window.addEventListener('beforeunload', this.boundWindowHandlers.beforeunload);
- window.addEventListener('resize', this.boundWindowHandlers.resize);
- },
- detachEvents: function () {
- if (this.pixiApp && this.boundCanvasHandlers) {
- this.pixiApp.view.removeEventListener('pointerdown', this.boundCanvasHandlers.pointerdown);
- this.pixiApp.view.removeEventListener('wheel', this.boundCanvasHandlers.wheel);
- }
- if (this.resizeObserver) {
- this.resizeObserver.disconnect();
- this.resizeObserver = null;
- }
- if (!this.boundWindowHandlers) {
- return;
- }
- window.removeEventListener('pointermove', this.boundWindowHandlers.pointermove);
- window.removeEventListener('pointerup', this.boundWindowHandlers.pointerup);
- window.removeEventListener('pointercancel', this.boundWindowHandlers.pointercancel);
- window.removeEventListener('keydown', this.boundWindowHandlers.keydown);
- window.removeEventListener('keyup', this.boundWindowHandlers.keyup);
- window.removeEventListener('beforeunload', this.boundWindowHandlers.beforeunload);
- window.removeEventListener('resize', this.boundWindowHandlers.resize);
- },
- handleResize: function () {
- if (!this.pixiApp || !this.$refs.canvasHost) {
- return;
- }
- var host = this.$refs.canvasHost;
- var width = Math.max(host.clientWidth, 320);
- var height = Math.max(host.clientHeight, 320);
- this.pixiApp.renderer.resize(width, height);
- this.markGridSceneDirty();
- this.markStaticSceneDirty();
- this.scheduleRender();
- },
- loadLevOptions: function () {
- var self = this;
- $.ajax({
- url: baseUrl + '/basMap/getLevList',
- method: 'GET',
- headers: authHeaders(),
- success: function (res) {
- if (res && res.code === 200 && Array.isArray(res.data)) {
- self.remoteLevOptions = res.data.map(function (item) {
- return toInt(item, 0);
- }).filter(function (item) {
- return item > 0;
- });
- self.refreshLevOptions();
- }
- }
- });
- },
- refreshLevOptions: function () {
- var set = {};
- var result = [];
- var pushLev = function (lev) {
- lev = toInt(lev, 0);
- if (lev <= 0 || set[lev]) {
- return;
- }
- set[lev] = true;
- result.push(lev);
- };
- this.remoteLevOptions.forEach(pushLev);
- Object.keys(this.draftDocs || {}).forEach(pushLev);
- pushLev(this.currentLev);
- pushLev(this.floorPickerLev);
- result.sort(function (a, b) { return a - b; });
- this.levOptions = result;
- },
- exportDoc: function (doc) {
- var source = doc || this.doc || {};
- return {
- lev: toInt(source.lev, 0),
- editorMode: FREE_EDITOR_MODE,
- canvasWidth: roundCoord(toNumber(source.canvasWidth, DEFAULT_CANVAS_WIDTH)),
- canvasHeight: roundCoord(toNumber(source.canvasHeight, DEFAULT_CANVAS_HEIGHT)),
- elements: (source.elements || []).map(function (item, index) {
- return {
- id: item && item.id ? String(item.id) : ('el_' + (index + 1)),
- type: DRAW_TYPES.indexOf(item && item.type) >= 0 ? item.type : 'shelf',
- x: roundCoord(Math.max(0, toNumber(item && item.x, 0))),
- y: roundCoord(Math.max(0, toNumber(item && item.y, 0))),
- width: roundCoord(Math.max(MIN_ELEMENT_SIZE, toNumber(item && item.width, MIN_ELEMENT_SIZE))),
- height: roundCoord(Math.max(MIN_ELEMENT_SIZE, toNumber(item && item.height, MIN_ELEMENT_SIZE))),
- value: normalizeValue(item && item.value)
- };
- })
- };
- },
- normalizeDoc: function (doc) {
- var normalized = this.exportDoc(doc || {});
- if (normalized.lev <= 0) {
- normalized.lev = toInt(this.currentLev, 1) || 1;
- }
- return normalized;
- },
- snapshotDoc: function (doc) {
- return JSON.stringify(this.exportDoc(doc));
- },
- syncDirty: function () {
- var currentSnapshot = this.snapshotDoc(this.doc);
- this.isDirty = currentSnapshot !== this.savedSnapshot;
- },
- setDraftDocEntry: function (lev, doc, savedSnapshot) {
- lev = toInt(lev, 0);
- if (lev <= 0 || !doc) {
- return;
- }
- var entry = {
- doc: this.exportDoc(doc),
- savedSnapshot: savedSnapshot != null ? savedSnapshot : ''
- };
- if (this.$set) {
- this.$set(this.draftDocs, lev, entry);
- } else {
- this.draftDocs[lev] = entry;
- }
- },
- removeDraftDocEntry: function (lev) {
- lev = toInt(lev, 0);
- if (lev <= 0) {
- return;
- }
- if (this.$delete) {
- this.$delete(this.draftDocs, lev);
- } else {
- delete this.draftDocs[lev];
- }
- },
- cacheCurrentDraft: function () {
- if (!this.doc || !this.doc.lev) {
- return;
- }
- this.setDraftDocEntry(this.doc.lev, this.doc, this.savedSnapshot);
- this.refreshLevOptions();
- },
- clearCurrentDraftIfSaved: function () {
- if (!this.doc || !this.doc.lev) {
- return;
- }
- this.setDraftDocEntry(this.doc.lev, this.doc, this.savedSnapshot);
- },
- clearFloorTransientState: function () {
- this.clearDeferredStaticCommit();
- this.interactionState = null;
- this.currentPointerId = null;
- this.hoverElementId = '';
- this.pointerStatus = '--';
- this.lastPointerStatusUpdateTs = 0;
- this.selectedIds = [];
- this.isPanning = false;
- this.isZooming = false;
- this.pendingViewportRefresh = false;
- if (this.zoomRefreshTimer) {
- window.clearTimeout(this.zoomRefreshTimer);
- this.zoomRefreshTimer = null;
- }
- if (this.panRefreshTimer) {
- window.clearTimeout(this.panRefreshTimer);
- this.panRefreshTimer = null;
- }
- },
- resetRenderLayers: function () {
- if (this.gridLayer) {
- this.gridLayer.clear();
- }
- if (this.trackLayer) {
- this.trackLayer.clear();
- }
- if (this.nodeLayer) {
- this.nodeLayer.clear();
- }
- if (this.eraseLayer) {
- this.eraseLayer.clear();
- }
- if (this.patchObjectLayer) {
- this.patchObjectLayer.clear();
- }
- if (this.activeLayer) {
- this.activeLayer.clear();
- }
- if (this.selectionLayer) {
- this.selectionLayer.clear();
- }
- if (this.guideLayer) {
- this.guideLayer.clear();
- }
- if (this.hoverLayer) {
- this.hoverLayer.clear();
- }
- if (this.guideText) {
- this.guideText.visible = false;
- this.guideText.text = '';
- }
- if (this.labelLayer) {
- this.labelLayer.visible = false;
- }
- for (var i = 0; i < this.labelPool.length; i++) {
- this.labelPool[i].visible = false;
- this.labelPool[i].text = '';
- }
- this.hideUnusedStaticSprites(this.staticTrackSpritePool || [], 0);
- this.hideUnusedStaticSprites(this.staticNodeSpritePool || [], 0);
- if (this.staticTrackSpriteLayer) {
- this.staticTrackSpriteLayer.removeChildren();
- this.staticTrackSpritePool = [];
- }
- if (this.staticNodeSpriteLayer) {
- this.staticNodeSpriteLayer.removeChildren();
- this.staticNodeSpritePool = [];
- }
- if (this.staticTrackSpriteLayer) {
- this.staticTrackSpriteLayer.visible = false;
- }
- if (this.staticNodeSpriteLayer) {
- this.staticNodeSpriteLayer.visible = false;
- }
- },
- hasDirtyDraft: function (lev) {
- lev = toInt(lev, 0);
- if (lev <= 0) {
- return false;
- }
- var entry = this.draftDocs[lev];
- if (!entry || !entry.doc) {
- return false;
- }
- var snapshot = this.snapshotDoc(entry.doc);
- return snapshot !== (entry.savedSnapshot || '');
- },
- markStaticSceneDirty: function () {
- this.staticSceneDirty = true;
- },
- markGridSceneDirty: function () {
- this.gridSceneDirty = true;
- },
- clearRenderCaches: function () {
- this.gridRenderRect = null;
- this.gridRenderKey = '';
- this.staticRenderRect = null;
- this.staticRenderKey = '';
- this.staticExcludedKey = '';
- },
- scheduleZoomRefresh: function () {
- if (this.zoomRefreshTimer) {
- window.clearTimeout(this.zoomRefreshTimer);
- }
- this.isZooming = true;
- this.zoomRefreshTimer = window.setTimeout(function () {
- this.zoomRefreshTimer = null;
- this.isZooming = false;
- if (this.isPanning || (this.interactionState && this.interactionState.type === 'pan')) {
- this.pendingViewportRefresh = true;
- return;
- }
- this.markGridSceneDirty();
- this.markStaticSceneDirty();
- this.scheduleRender();
- }.bind(this), ZOOM_REFRESH_DELAY);
- },
- cancelPanRefresh: function () {
- if (this.panRefreshTimer) {
- window.clearTimeout(this.panRefreshTimer);
- this.panRefreshTimer = null;
- }
- },
- schedulePanRefresh: function () {
- this.cancelPanRefresh();
- this.isPanning = true;
- this.panRefreshTimer = window.setTimeout(function () {
- this.panRefreshTimer = null;
- this.isPanning = false;
- if (this.pendingViewportRefresh) {
- this.pendingViewportRefresh = false;
- this.markGridSceneDirty();
- this.markStaticSceneDirty();
- }
- this.scheduleRender();
- }.bind(this), PAN_LABEL_REFRESH_DELAY);
- },
- rebuildLabelCapability: function () {
- var maxWidth = 0;
- var maxHeight = 0;
- var elements = this.doc && this.doc.elements ? this.doc.elements : [];
- for (var i = 0; i < elements.length; i++) {
- var element = elements[i];
- if (element.width > maxWidth) {
- maxWidth = element.width;
- }
- if (element.height > maxHeight) {
- maxHeight = element.height;
- }
- }
- this.labelCapability = {
- maxWidth: maxWidth,
- maxHeight: maxHeight
- };
- this.labelCapabilityDirty = false;
- },
- ensureLabelCapability: function () {
- if (this.labelCapabilityDirty) {
- this.rebuildLabelCapability();
- }
- return this.labelCapability;
- },
- markSpatialIndexDirty: function () {
- this.spatialIndexDirty = true;
- },
- rebuildSpatialIndex: function () {
- var buckets = {};
- var elements = this.doc && this.doc.elements ? this.doc.elements : [];
- for (var i = 0; i < elements.length; i++) {
- var element = elements[i];
- var minX = Math.floor(element.x / SPATIAL_BUCKET_SIZE);
- var maxX = Math.floor((element.x + element.width) / SPATIAL_BUCKET_SIZE);
- var minY = Math.floor(element.y / SPATIAL_BUCKET_SIZE);
- var maxY = Math.floor((element.y + element.height) / SPATIAL_BUCKET_SIZE);
- for (var bx = minX; bx <= maxX; bx++) {
- for (var by = minY; by <= maxY; by++) {
- var key = bucketKey(bx, by);
- if (!buckets[key]) {
- buckets[key] = [];
- }
- buckets[key].push(element);
- }
- }
- }
- this.spatialBuckets = buckets;
- this.spatialIndexDirty = false;
- },
- ensureSpatialIndex: function () {
- if (this.spatialIndexDirty || !this.spatialBuckets) {
- this.rebuildSpatialIndex();
- }
- },
- querySpatialCandidates: function (rect, padding, excludeIds) {
- if (!this.doc || !rect) {
- return [];
- }
- this.ensureSpatialIndex();
- var excludeMap = {};
- excludeIds = excludeIds || [];
- for (var i = 0; i < excludeIds.length; i++) {
- excludeMap[excludeIds[i]] = true;
- }
- var seen = {};
- var result = [];
- var pad = Math.max(0, padding || 0);
- var minX = Math.floor((rect.x - pad) / SPATIAL_BUCKET_SIZE);
- var maxX = Math.floor((rect.x + rect.width + pad) / SPATIAL_BUCKET_SIZE);
- var minY = Math.floor((rect.y - pad) / SPATIAL_BUCKET_SIZE);
- var maxY = Math.floor((rect.y + rect.height + pad) / SPATIAL_BUCKET_SIZE);
- for (var bx = minX; bx <= maxX; bx++) {
- for (var by = minY; by <= maxY; by++) {
- var key = bucketKey(bx, by);
- var bucket = this.spatialBuckets[key];
- if (!bucket || !bucket.length) {
- continue;
- }
- for (var j = 0; j < bucket.length; j++) {
- var element = bucket[j];
- if (!element || seen[element.id] || excludeMap[element.id]) {
- continue;
- }
- seen[element.id] = true;
- result.push(element);
- }
- }
- }
- return result;
- },
- cancelDeferredStaticRebuild: function () {
- if (this.deferredStaticRebuildTimer) {
- window.clearTimeout(this.deferredStaticRebuildTimer);
- this.deferredStaticRebuildTimer = null;
- }
- },
- stageDeferredStaticCommit: function (ids, eraseRects) {
- this.pendingStaticCommit = {
- ids: (ids || []).slice(),
- eraseRects: (eraseRects || []).map(function (item) {
- return {
- x: item.x,
- y: item.y,
- width: item.width,
- height: item.height
- };
- })
- };
- },
- clearDeferredStaticCommit: function () {
- this.cancelDeferredStaticRebuild();
- this.pendingStaticCommit = null;
- },
- scheduleDeferredStaticRebuild: function () {
- this.cancelDeferredStaticRebuild();
- this.deferredStaticRebuildTimer = window.setTimeout(function () {
- this.deferredStaticRebuildTimer = null;
- this.pendingStaticCommit = null;
- this.markStaticSceneDirty();
- this.scheduleRender();
- }.bind(this), DEFERRED_STATIC_REBUILD_DELAY);
- },
- selectionKey: function (ids) {
- return (ids || []).slice().sort().join('|');
- },
- setSelectedIds: function (ids, options) {
- options = options || {};
- var nextIds = (ids || []).filter(Boolean);
- this.selectedIds = nextIds.slice();
- if (options.refreshInspector !== false) {
- this.refreshInspector();
- }
- },
- setCurrentDoc: function (doc, options) {
- options = options || {};
- var normalized = this.normalizeDoc(doc);
- this.clearFloorTransientState();
- this.resetRenderLayers();
- this.clearRenderCaches();
- this.doc = normalized;
- this.markSpatialIndexDirty();
- this.labelCapabilityDirty = true;
- this.pendingViewportRefresh = false;
- this.currentLev = normalized.lev;
- this.floorPickerLev = normalized.lev;
- this.switchingFloorLev = null;
- this.loadingFloor = false;
- this.syncFloorQueryParam(normalized.lev);
- this.markGridSceneDirty();
- this.markStaticSceneDirty();
- this.undoStack = [];
- this.redoStack = [];
- this.savedSnapshot = options.savedSnapshot != null ? options.savedSnapshot : this.snapshotDoc(normalized);
- this.syncDirty();
- this.refreshInspector();
- this.refreshLevOptions();
- this.$nextTick(function () {
- this.fitContent();
- this.scheduleRender();
- }.bind(this));
- },
- replaceDocFromSnapshot: function (snapshot) {
- if (!snapshot) {
- return;
- }
- try {
- this.clearFloorTransientState();
- this.resetRenderLayers();
- this.clearRenderCaches();
- this.doc = this.normalizeDoc(JSON.parse(snapshot));
- this.markSpatialIndexDirty();
- this.labelCapabilityDirty = true;
- this.pendingViewportRefresh = false;
- } catch (e) {
- this.showMessage('error', '鍘嗗彶璁板綍鎭㈠澶辫触');
- return;
- }
- this.markGridSceneDirty();
- this.markStaticSceneDirty();
- this.floorPickerLev = this.doc.lev;
- this.currentLev = this.doc.lev;
- this.refreshInspector();
- this.syncDirty();
- this.cacheCurrentDraft();
- this.scheduleRender();
- },
- pushUndoSnapshot: function (snapshot) {
- if (!snapshot) {
- return;
- }
- if (this.undoStack.length > 0 && this.undoStack[this.undoStack.length - 1] === snapshot) {
- return;
- }
- this.undoStack.push(snapshot);
- if (this.undoStack.length > HISTORY_LIMIT) {
- this.undoStack.shift();
- }
- },
- commitMutation: function (beforeSnapshot, options) {
- options = options || {};
- var afterSnapshot = this.snapshotDoc(this.doc);
- if (beforeSnapshot === afterSnapshot) {
- this.scheduleRender();
- this.refreshInspector();
- return false;
- }
- this.pushUndoSnapshot(beforeSnapshot);
- this.redoStack = [];
- this.markSpatialIndexDirty();
- this.labelCapabilityDirty = true;
- if (options.staticSceneDirty !== false) {
- this.clearDeferredStaticCommit();
- this.markStaticSceneDirty();
- }
- this.syncDirty();
- this.cacheCurrentDraft();
- this.refreshInspector();
- this.scheduleRender();
- return true;
- },
- runMutation: function (mutator) {
- if (!this.doc) {
- return false;
- }
- var beforeSnapshot = this.snapshotDoc(this.doc);
- mutator();
- return this.commitMutation(beforeSnapshot);
- },
- undo: function () {
- if (this.undoStack.length === 0 || !this.doc) {
- return;
- }
- var currentSnapshot = this.snapshotDoc(this.doc);
- var snapshot = this.undoStack.pop();
- this.redoStack.push(currentSnapshot);
- this.replaceDocFromSnapshot(snapshot);
- },
- redo: function () {
- if (this.redoStack.length === 0 || !this.doc) {
- return;
- }
- var currentSnapshot = this.snapshotDoc(this.doc);
- var snapshot = this.redoStack.pop();
- this.pushUndoSnapshot(currentSnapshot);
- this.replaceDocFromSnapshot(snapshot);
- },
- createLocalBlankDoc: function (lev, width, height, savedSnapshot) {
- var doc = {
- lev: toInt(lev, 1),
- editorMode: FREE_EDITOR_MODE,
- canvasWidth: Math.max(MIN_ELEMENT_SIZE * 4, toNumber(width, DEFAULT_CANVAS_WIDTH)),
- canvasHeight: Math.max(MIN_ELEMENT_SIZE * 4, toNumber(height, DEFAULT_CANVAS_HEIGHT)),
- elements: []
- };
- this.setCurrentDoc(doc, {
- savedSnapshot: savedSnapshot != null ? savedSnapshot : ''
- });
- this.cacheCurrentDraft();
- this.syncDirty();
- },
- openBlankDialog: function () {
- var lev = this.currentLev || 1;
- this.blankForm = {
- lev: String(lev),
- width: String(Math.round(this.doc ? this.doc.canvasWidth : DEFAULT_CANVAS_WIDTH)),
- height: String(Math.round(this.doc ? this.doc.canvasHeight : DEFAULT_CANVAS_HEIGHT))
- };
- this.blankDialogVisible = true;
- },
- createBlankMap: function () {
- var lev = toInt(this.blankForm.lev, 0);
- var width = toNumber(this.blankForm.width, DEFAULT_CANVAS_WIDTH);
- var height = toNumber(this.blankForm.height, DEFAULT_CANVAS_HEIGHT);
- if (lev <= 0) {
- this.showMessage('warning', '妤煎眰涓嶈兘涓虹┖');
- return;
- }
- if (width <= 0 || height <= 0) {
- this.showMessage('warning', '鐢诲竷灏哄蹇呴』澶т簬 0');
- return;
- }
- this.blankDialogVisible = false;
- this.createLocalBlankDoc(lev, width, height, '');
- },
- buildTransferPayload: function () {
- var doc = this.exportDoc(this.doc);
- return {
- format: MAP_TRANSFER_FORMAT,
- exportedAt: new Date().toISOString(),
- source: {
- lev: doc.lev,
- editorMode: doc.editorMode
- },
- docs: [doc]
- };
- },
- buildTransferFilename: function (docs) {
- var levs = (docs || []).map(function (item) {
- return toInt(item && item.lev, 0);
- }).filter(function (lev) {
- return lev > 0;
- }).sort(function (a, b) {
- return a - b;
- });
- var scope = levs.length <= 1
- ? (String(levs[0] || (this.currentLev || 1)) + 'F')
- : ('all-' + levs.length + '-floors');
- var now = new Date();
- return [
- 'bas-map',
- scope,
- now.getFullYear(),
- padNumber(now.getMonth() + 1),
- padNumber(now.getDate()),
- padNumber(now.getHours()),
- padNumber(now.getMinutes()),
- padNumber(now.getSeconds())
- ].join('-') + '.json';
- },
- requestEditorDoc: function (lev) {
- return new Promise(function (resolve, reject) {
- $.ajax({
- url: baseUrl + '/basMap/editor/' + lev + '/auth',
- method: 'GET',
- headers: authHeaders(),
- success: function (res) {
- if (!res || res.code !== 200 || !res.data) {
- reject(new Error((res && res.msg) ? res.msg : ('鍔犺浇 ' + lev + 'F 鍦板浘澶辫触')));
- return;
- }
- resolve(res.data);
- },
- error: function () {
- reject(new Error('鍔犺浇 ' + lev + 'F 鍦板浘澶辫触'));
- }
- });
- });
- },
- collectAllTransferDocs: function () {
- var self = this;
- var levMap = {};
- (this.remoteLevOptions || []).forEach(function (lev) {
- lev = toInt(lev, 0);
- if (lev > 0) {
- levMap[lev] = true;
- }
- });
- Object.keys(this.draftDocs || {}).forEach(function (key) {
- var lev = toInt(key, 0);
- if (lev > 0) {
- levMap[lev] = true;
- }
- });
- if (this.doc && this.doc.lev) {
- levMap[toInt(this.doc.lev, 0)] = true;
- }
- var levs = Object.keys(levMap).map(function (key) {
- return toInt(key, 0);
- }).filter(function (lev) {
- return lev > 0;
- }).sort(function (a, b) {
- return a - b;
- });
- if (!levs.length) {
- return Promise.resolve([]);
- }
- return Promise.all(levs.map(function (lev) {
- if (self.doc && self.doc.lev === lev) {
- return Promise.resolve(self.exportDoc(self.doc));
- }
- if (self.draftDocs[lev] && self.draftDocs[lev].doc) {
- return Promise.resolve(self.exportDoc(self.draftDocs[lev].doc));
- }
- return self.requestEditorDoc(lev).then(function (doc) {
- return self.normalizeDoc(doc);
- });
- }));
- },
- exportMapPackage: function () {
- var self = this;
- if (!this.doc && (!this.remoteLevOptions || !this.remoteLevOptions.length)) {
- this.showMessage('warning', '褰撳墠娌℃湁鍙鍑虹殑鍦板浘');
- return;
- }
- this.collectAllTransferDocs().then(function (docs) {
- if (!docs || !docs.length) {
- self.showMessage('warning', '褰撳墠娌℃湁鍙鍑虹殑鍦板浘');
- return;
- }
- var payload = {
- format: MAP_TRANSFER_FORMAT,
- exportedAt: new Date().toISOString(),
- source: {
- lev: self.currentLev || (docs[0] && docs[0].lev) || 1,
- editorMode: FREE_EDITOR_MODE
- },
- docs: docs.map(function (doc) {
- return self.exportDoc(doc);
- })
- };
- var blob = new Blob([JSON.stringify(payload, null, 2)], {
- type: 'application/json;charset=utf-8'
- });
- var href = window.URL.createObjectURL(blob);
- var link = document.createElement('a');
- link.href = href;
- link.download = self.buildTransferFilename(payload.docs);
- document.body.appendChild(link);
- link.click();
- document.body.removeChild(link);
- window.setTimeout(function () {
- window.URL.revokeObjectURL(href);
- }, 0);
- self.showMessage('success', '宸插鍑� ' + payload.docs.length + ' 涓ゼ灞傜殑鍦板浘鍖�');
- }).catch(function (error) {
- self.showMessage('error', error && error.message ? error.message : '瀵煎嚭鍦板浘澶辫触');
- });
- },
- triggerImportMap: function () {
- if (this.$refs.mapImportInput) {
- this.$refs.mapImportInput.value = '';
- this.$refs.mapImportInput.click();
- }
- },
- parseTransferPackage: function (raw) {
- if (!raw) {
- return null;
- }
- if (raw.format === MAP_TRANSFER_FORMAT && Array.isArray(raw.docs) && raw.docs.length) {
- return {
- docs: raw.docs,
- activeLev: toInt(raw.source && raw.source.lev, 0)
- };
- }
- if ((raw.format === 'bas-map-editor-transfer-v1' || raw.format === MAP_TRANSFER_FORMAT) && raw.doc) {
- return {
- docs: [raw.doc],
- activeLev: toInt(raw.source && raw.source.lev, 0)
- };
- }
- if (raw.editorMode === FREE_EDITOR_MODE && Array.isArray(raw.elements)) {
- return {
- docs: [raw],
- activeLev: toInt(raw.lev, 0)
- };
- }
- return null;
- },
- importMapPackage: function (payload, options) {
- options = options || {};
- if (!payload || !Array.isArray(payload.docs) || !payload.docs.length) {
- this.showMessage('error', '瀵煎叆鏂囦欢鏍煎紡涓嶆纭�');
- return;
- }
- if (this.isDirty && options.skipConfirm !== true) {
- if (!window.confirm('瀵煎叆鍦板浘浼氭浛鎹㈠綋鍓嶇紪杈戞�佹湭淇濆瓨鍐呭锛屾槸鍚︾户缁紵')) {
- return;
- }
- }
- if (this.doc) {
- this.cacheCurrentDraft();
- }
- var self = this;
- var normalizedDocs = payload.docs.map(function (item) {
- return self.normalizeDoc(item);
- }).sort(function (a, b) {
- return toInt(a.lev, 0) - toInt(b.lev, 0);
- });
- normalizedDocs.forEach(function (doc) {
- self.setDraftDocEntry(doc.lev, doc, '');
- });
- var activeLev = toInt(payload.activeLev, 0);
- var targetDoc = normalizedDocs[0];
- for (var i = 0; i < normalizedDocs.length; i++) {
- if (normalizedDocs[i].lev === activeLev) {
- targetDoc = normalizedDocs[i];
- break;
- }
- }
- this.refreshLevOptions();
- this.floorPickerLev = targetDoc.lev;
- this.setCurrentDoc(targetDoc, { savedSnapshot: '' });
- if (normalizedDocs.length > 1) {
- this.showMessage('success', '鍦板浘鍖呭凡瀵煎叆 ' + normalizedDocs.length + ' 涓ゼ灞傦紝鍙偣鍑烩�滀繚瀛樺叏閮ㄦゼ灞傗�濊惤搴�');
- return;
- }
- this.showMessage('success', '鍦板浘鍖呭凡瀵煎叆锛屼繚瀛樺悗鎵嶄細瑕嗙洊杩愯鍦板浘');
- },
- handleImportMap: function (event) {
- var file = event && event.target && event.target.files ? event.target.files[0] : null;
- if (!file) {
- return;
- }
- var self = this;
- var reader = new FileReader();
- reader.onload = function (loadEvent) {
- try {
- var text = loadEvent && loadEvent.target ? loadEvent.target.result : '';
- var raw = JSON.parse(text || '{}');
- var payload = self.parseTransferPackage(raw);
- self.importMapPackage(payload);
- } catch (e) {
- self.showMessage('error', '鍦板浘鏂囦欢瑙f瀽澶辫触');
- }
- };
- reader.onerror = function () {
- self.showMessage('error', '鍦板浘鏂囦欢璇诲彇澶辫触');
- };
- reader.readAsText(file, 'utf-8');
- },
- triggerImportExcel: function () {
- if (this.$refs.importInput) {
- this.$refs.importInput.value = '';
- this.$refs.importInput.click();
- }
- },
- handleImportExcel: function (event) {
- var self = this;
- var file = event && event.target && event.target.files ? event.target.files[0] : null;
- if (!file) {
- return;
- }
- var formData = new FormData();
- formData.append('file', file);
- $.ajax({
- url: baseUrl + '/basMap/editor/importExcel/auth',
- method: 'POST',
- headers: authHeaders(),
- data: formData,
- processData: false,
- contentType: false,
- success: function (res) {
- if (!res || res.code !== 200 || !Array.isArray(res.data) || res.data.length === 0) {
- self.showMessage('error', (res && res.msg) ? res.msg : 'Excel 瀵煎叆澶辫触');
- return;
- }
- res.data.forEach(function (item) {
- var doc = self.normalizeDoc(item);
- self.setDraftDocEntry(doc.lev, doc, '');
- });
- self.refreshLevOptions();
- self.floorPickerLev = toInt(res.data[0].lev, 0);
- self.setCurrentDoc(res.data[0], { savedSnapshot: '' });
- self.showMessage('success', 'Excel 宸插鍏ュ埌缂栬緫鍣紝淇濆瓨鍚庢墠浼氳鐩栬繍琛屽湴鍥�');
- },
- error: function () {
- self.showMessage('error', 'Excel 瀵煎叆澶辫触');
- }
- });
- },
- handleFloorChange: function (lev) {
- lev = toInt(lev, 0);
- if (lev <= 0) {
- return;
- }
- this.floorPickerLev = lev;
- if (this.doc && this.doc.lev === lev && !this.loadingFloor) {
- this.switchingFloorLev = null;
- return;
- }
- if (this.doc) {
- this.cacheCurrentDraft();
- }
- this.clearFloorTransientState();
- this.resetRenderLayers();
- this.switchingFloorLev = lev;
- this.markGridSceneDirty();
- this.markStaticSceneDirty();
- this.scheduleRender();
- this.fetchFloor(lev);
- },
- loadCurrentFloor: function () {
- if (!this.currentLev) {
- this.showMessage('warning', '璇峰厛閫夋嫨妤煎眰');
- return;
- }
- if (this.isDirty && !window.confirm('閲嶆柊璇诲彇浼氫涪寮冨綋鍓嶆ゼ灞傛湭淇濆瓨鐨勮嚜鐢辩敾甯冪紪杈戯紝鏄惁缁х画锛�')) {
- return;
- }
- this.removeDraftDocEntry(this.currentLev);
- this.refreshLevOptions();
- this.fetchFloor(this.currentLev);
- },
- fetchFloor: function (lev) {
- var self = this;
- lev = toInt(lev, 0);
- if (lev <= 0) {
- return;
- }
- var requestSeq = ++this.floorRequestSeq;
- this.activeFloorRequestSeq = requestSeq;
- this.loadingFloor = true;
- this.switchingFloorLev = lev;
- $.ajax({
- url: baseUrl + '/basMap/editor/' + lev + '/auth',
- method: 'GET',
- headers: authHeaders(),
- success: function (res) {
- if (requestSeq !== self.activeFloorRequestSeq) {
- return;
- }
- self.loadingFloor = false;
- if (!res || res.code !== 200 || !res.data) {
- self.switchingFloorLev = null;
- self.floorPickerLev = self.currentLev;
- self.markGridSceneDirty();
- self.markStaticSceneDirty();
- self.scheduleRender();
- self.showMessage('error', (res && res.msg) ? res.msg : '鍔犺浇鍦板浘澶辫触');
- return;
- }
- var normalized = self.normalizeDoc(res.data);
- self.setDraftDocEntry(normalized.lev, normalized, self.snapshotDoc(normalized));
- self.setCurrentDoc(normalized, {
- savedSnapshot: self.snapshotDoc(normalized)
- });
- },
- error: function () {
- if (requestSeq !== self.activeFloorRequestSeq) {
- return;
- }
- self.loadingFloor = false;
- self.switchingFloorLev = null;
- self.floorPickerLev = self.currentLev;
- self.markGridSceneDirty();
- self.markStaticSceneDirty();
- self.scheduleRender();
- self.showMessage('error', '鍔犺浇鍦板浘澶辫触');
- }
- });
- },
- validateDocBeforeSave: function (doc) {
- var source = this.normalizeDoc(doc);
- if (!source || !source.lev) {
- return '妤煎眰涓嶈兘涓虹┖';
- }
- if (toNumber(source.canvasWidth, 0) <= 0 || toNumber(source.canvasHeight, 0) <= 0) {
- return '鐢诲竷灏哄蹇呴』澶т簬 0';
- }
- var elements = source.elements || [];
- for (var i = 0; i < elements.length; i++) {
- var element = elements[i];
- if (element.width <= 0 || element.height <= 0) {
- return '瀛樺湪灏哄鏃犳晥鐨勫厓绱�';
- }
- if (element.x < 0 || element.y < 0) {
- return '鍏冪礌鍧愭爣涓嶈兘灏忎簬 0';
- }
- if (!isRectWithinCanvas(element, source.canvasWidth, source.canvasHeight)) {
- return '瀛樺湪瓒呭嚭鐢诲竷杈圭晫鐨勫厓绱�: ' + element.id;
- }
- if (element.type === 'devp') {
- var value = safeParseJson(element.value);
- if (!value || toInt(value.stationId, 0) <= 0 || toInt(value.deviceNo, 0) <= 0) {
- return '杈撻�佺嚎鍏冪礌蹇呴』閰嶇疆鏈夋晥鐨� stationId 鍜� deviceNo';
- }
- }
- }
- var overlapId = findDocOverlapId(source);
- if (overlapId) {
- return '瀛樺湪閲嶅彔鍏冪礌: ' + overlapId;
- }
- return '';
- },
- validateBeforeSave: function () {
- return this.validateDocBeforeSave(this.doc);
- },
- requestSaveDoc: function (doc) {
- return new Promise(function (resolve, reject) {
- $.ajax({
- url: baseUrl + '/basMap/editor/save/auth',
- method: 'POST',
- headers: $.extend({
- 'Content-Type': 'application/json;charset=UTF-8'
- }, authHeaders()),
- data: JSON.stringify(doc),
- success: function (res) {
- if (!res || res.code !== 200) {
- reject(new Error((res && res.msg) ? res.msg : '淇濆瓨澶辫触'));
- return;
- }
- resolve(res);
- },
- error: function () {
- reject(new Error('淇濆瓨澶辫触'));
- }
- });
- });
- },
- collectDirtyDocsForSave: function () {
- var result = [];
- var seen = {};
- if (this.doc && this.doc.lev && this.isDirty) {
- var currentDoc = this.exportDoc(this.doc);
- result.push(currentDoc);
- seen[currentDoc.lev] = true;
- }
- var self = this;
- Object.keys(this.draftDocs || {}).forEach(function (key) {
- var lev = toInt(key, 0);
- if (lev <= 0 || seen[lev]) {
- return;
- }
- var entry = self.draftDocs[lev];
- if (!entry || !entry.doc) {
- return;
- }
- var snapshot = self.snapshotDoc(entry.doc);
- if (snapshot === (entry.savedSnapshot || '')) {
- return;
- }
- var doc = self.exportDoc(entry.doc);
- result.push(doc);
- seen[doc.lev] = true;
- });
- result.sort(function (a, b) {
- return toInt(a.lev, 0) - toInt(b.lev, 0);
- });
- return result;
- },
- markDocSavedState: function (doc) {
- var normalized = this.normalizeDoc(doc);
- var savedSnapshot = this.snapshotDoc(normalized);
- this.setDraftDocEntry(normalized.lev, normalized, savedSnapshot);
- if (this.doc && this.doc.lev === normalized.lev) {
- this.savedSnapshot = savedSnapshot;
- this.syncDirty();
- }
- },
- saveDoc: function () {
- var self = this;
- if (!this.doc) {
- return;
- }
- var error = this.validateBeforeSave();
- if (error) {
- this.showMessage('warning', error);
- return;
- }
- this.saving = true;
- var payload = this.exportDoc(this.doc);
- this.requestSaveDoc(payload).then(function () {
- self.saving = false;
- self.savedSnapshot = self.snapshotDoc(self.doc);
- self.syncDirty();
- self.clearCurrentDraftIfSaved();
- self.refreshLevOptions();
- self.showMessage('success', '褰撳墠妤煎眰宸蹭繚瀛樺苟缂栬瘧鍒拌繍琛屽湴鍥�');
- }).catch(function (error) {
- self.saving = false;
- self.showMessage('error', error && error.message ? error.message : '淇濆瓨澶辫触');
- });
- },
- saveAllDocs: function () {
- var self = this;
- if (this.saving || this.savingAll) {
- return;
- }
- var docs = this.collectDirtyDocsForSave();
- if (!docs.length) {
- this.showMessage('warning', '褰撳墠娌℃湁闇�瑕佷繚瀛樼殑妤煎眰');
- return;
- }
- if (docs.length > 1 && !window.confirm('灏嗕繚瀛� ' + docs.length + ' 涓ゼ灞傚埌杩愯鍦板浘锛屾槸鍚︾户缁紵')) {
- return;
- }
- for (var i = 0; i < docs.length; i++) {
- var error = this.validateDocBeforeSave(docs[i]);
- if (error) {
- this.showMessage('warning', docs[i].lev + 'F 淇濆瓨鍓嶆牎楠屽け璐�: ' + error);
- return;
- }
- }
- this.savingAll = true;
- var index = 0;
- var total = docs.length;
- var next = function () {
- if (index >= total) {
- self.savingAll = false;
- self.refreshLevOptions();
- self.showMessage('success', '宸蹭繚瀛� ' + total + ' 涓ゼ灞傚埌杩愯鍦板浘');
- return;
- }
- var doc = docs[index++];
- self.requestSaveDoc(doc).then(function () {
- self.markDocSavedState(doc);
- next();
- }).catch(function (error) {
- self.savingAll = false;
- self.showMessage('error', doc.lev + 'F 淇濆瓨澶辫触: ' + (error && error.message ? error.message : '淇濆瓨澶辫触'));
- });
- };
- next();
- },
- setTool: function (tool) {
- this.activeTool = tool;
- this.updateCursor();
- },
- findElementById: function (id) {
- if (!this.doc || !id) {
- return null;
- }
- var elements = this.doc.elements || [];
- for (var i = 0; i < elements.length; i++) {
- if (elements[i].id === id) {
- return elements[i];
- }
- }
- return null;
- },
- getSelectedElements: function () {
- var self = this;
- return this.selectedIds.map(function (id) {
- return self.findElementById(id);
- }).filter(Boolean);
- },
- refreshInspector: function () {
- var element = this.singleSelectedElement;
- if (!this.doc) {
- this.canvasForm = {
- width: String(DEFAULT_CANVAS_WIDTH),
- height: String(DEFAULT_CANVAS_HEIGHT)
- };
- this.valueEditorText = '';
- this.resetDevpForm();
- this.resetDeviceForm();
- return;
- }
- this.canvasForm = {
- width: String(Math.round(this.doc.canvasWidth)),
- height: String(Math.round(this.doc.canvasHeight))
- };
- if (!element) {
- this.geometryForm = { x: '', y: '', width: '', height: '' };
- this.valueEditorText = '';
- this.resetDevpForm();
- this.resetDeviceForm();
- return;
- }
- this.geometryForm = {
- x: String(this.formatNumber(element.x)),
- y: String(this.formatNumber(element.y)),
- width: String(this.formatNumber(element.width)),
- height: String(this.formatNumber(element.height))
- };
- this.valueEditorText = element.value || '';
- if (element.type === 'devp') {
- this.loadDevpForm(element.value);
- } else {
- this.resetDevpForm();
- }
- if (isDeviceConfigType(element.type)) {
- this.loadDeviceForm(element.type, element.value);
- } else {
- this.resetDeviceForm();
- }
- this.ensureShelfFillStartValue();
- },
- resetDevpForm: function () {
- this.devpForm = {
- stationId: '',
- deviceNo: '',
- direction: [],
- isBarcodeStation: false,
- barcodeIdx: '',
- backStation: '',
- backStationDeviceNo: '',
- isInStation: false,
- barcodeStation: '',
- barcodeStationDeviceNo: '',
- isOutStation: false,
- runBlockReassign: false,
- isOutOrder: false,
- isLiftTransfer: false
- };
- },
- resetDeviceForm: function () {
- this.deviceForm = {
- valueKey: '',
- deviceNo: ''
- };
- },
- ensureShelfFillStartValue: function () {
- var element = this.singleSelectedElement;
- if (!element || element.type !== 'shelf') {
- return;
- }
- if (!this.shelfFillForm.startValue || !parseShelfLocationValue(this.shelfFillForm.startValue)) {
- this.shelfFillForm.startValue = normalizeValue(element.value || '');
- }
- },
- loadDevpForm: function (value) {
- this.resetDevpForm();
- var json = safeParseJson(value);
- if (!json) {
- return;
- }
- this.devpForm.stationId = json.stationId != null ? String(json.stationId) : '';
- this.devpForm.deviceNo = json.deviceNo != null ? String(json.deviceNo) : '';
- this.devpForm.direction = normalizeDirectionList(json.direction);
- this.devpForm.isBarcodeStation = boolFlag(json.isBarcodeStation);
- this.devpForm.barcodeIdx = json.barcodeIdx != null ? String(json.barcodeIdx) : '';
- this.devpForm.backStation = json.backStation != null ? String(json.backStation) : '';
- this.devpForm.backStationDeviceNo = json.backStationDeviceNo != null ? String(json.backStationDeviceNo) : '';
- this.devpForm.isInStation = boolFlag(json.isInStation);
- this.devpForm.barcodeStation = json.barcodeStation != null ? String(json.barcodeStation) : '';
- this.devpForm.barcodeStationDeviceNo = json.barcodeStationDeviceNo != null ? String(json.barcodeStationDeviceNo) : '';
- this.devpForm.isOutStation = boolFlag(json.isOutStation);
- this.devpForm.runBlockReassign = boolFlag(json.runBlockReassign);
- this.devpForm.isOutOrder = boolFlag(json.isOutOrder);
- this.devpForm.isLiftTransfer = boolFlag(json.isLiftTransfer);
- },
- getDeviceConfigLabel: function (type) {
- var meta = getTypeMeta(type);
- return meta.label + '鍙傛暟';
- },
- getDeviceConfigKeyLabel: function (type, valueKey) {
- if (valueKey === 'crnNo') {
- return 'crnNo';
- }
- if (valueKey === 'rgvNo') {
- return 'rgvNo';
- }
- return type === 'rgv' ? 'deviceNo / rgvNo' : 'deviceNo / crnNo';
- },
- loadDeviceForm: function (type, value) {
- this.resetDeviceForm();
- if (!isDeviceConfigType(type)) {
- return;
- }
- var json = safeParseJson(value);
- var valueKey = pickDeviceValueKey(type, json);
- var deviceNo = '';
- if (json && json[valueKey] != null) {
- deviceNo = String(json[valueKey]);
- }
- this.deviceForm = {
- valueKey: valueKey,
- deviceNo: deviceNo
- };
- },
- isDevpDirectionActive: function (directionKey) {
- return this.devpForm.direction.indexOf(directionKey) >= 0;
- },
- toggleDevpDirection: function (directionKey) {
- if (!directionKey) {
- return;
- }
- var next = this.devpForm.direction.slice();
- var index = next.indexOf(directionKey);
- if (index >= 0) {
- next.splice(index, 1);
- } else {
- next.push(directionKey);
- }
- this.devpForm.direction = DEVP_DIRECTION_OPTIONS.map(function (item) {
- return item.key;
- }).filter(function (item) {
- return next.indexOf(item) >= 0;
- });
- },
- applyCanvasSize: function () {
- var self = this;
- if (!this.doc) {
- return;
- }
- var width = toNumber(this.canvasForm.width, 0);
- var height = toNumber(this.canvasForm.height, 0);
- if (width <= 0 || height <= 0) {
- this.showMessage('warning', '鐢诲竷灏哄蹇呴』澶т簬 0');
- return;
- }
- var bounds = this.getElementBounds((this.doc.elements || []).map(function (item) {
- return item.id;
- }));
- if (bounds && (width < bounds.x + bounds.width || height < bounds.y + bounds.height)) {
- this.showMessage('warning', '鐢诲竷涓嶈兘灏忎簬褰撳墠鍏冪礌鍗犵敤鑼冨洿');
- return;
- }
- this.runMutation(function () {
- self.doc.canvasWidth = roundCoord(width);
- self.doc.canvasHeight = roundCoord(height);
- });
- },
- applyGeometry: function () {
- var self = this;
- var element = this.singleSelectedElement;
- if (!element) {
- return;
- }
- var next = {
- x: roundCoord(Math.max(0, toNumber(this.geometryForm.x, element.x))),
- y: roundCoord(Math.max(0, toNumber(this.geometryForm.y, element.y))),
- width: roundCoord(Math.max(MIN_ELEMENT_SIZE, toNumber(this.geometryForm.width, element.width))),
- height: roundCoord(Math.max(MIN_ELEMENT_SIZE, toNumber(this.geometryForm.height, element.height)))
- };
- if (!this.isWithinCanvas(next)) {
- this.showMessage('warning', '鍑犱綍灞炴�ц秴鍑哄綋鍓嶇敾甯冭寖鍥�');
- return;
- }
- var preview = deepClone(element);
- preview.x = next.x;
- preview.y = next.y;
- preview.width = next.width;
- preview.height = next.height;
- if (this.hasOverlap(preview, [preview.id])) {
- this.showMessage('warning', '璋冩暣鍚庝細涓庡叾浠栧厓绱犻噸鍙�');
- return;
- }
- this.runMutation(function () {
- element.x = next.x;
- element.y = next.y;
- element.width = next.width;
- element.height = next.height;
- });
- },
- applyRawValue: function () {
- var self = this;
- var element = this.singleSelectedElement;
- if (!element || element.type === 'devp') {
- return;
- }
- this.runMutation(function () {
- element.value = normalizeValue(self.valueEditorText);
- });
- },
- applyDeviceForm: function () {
- var self = this;
- var element = this.singleSelectedDeviceElement;
- if (!element) {
- return;
- }
- var deviceNo = toInt(this.deviceForm.deviceNo, 0);
- if (deviceNo <= 0) {
- this.showMessage('warning', '璁惧缂栧彿蹇呴』澶т簬 0');
- return;
- }
- var valueKey = this.deviceForm.valueKey || pickDeviceValueKey(element.type, safeParseJson(element.value));
- this.runMutation(function () {
- var payload = safeParseJson(element.value) || {};
- delete payload.deviceNo;
- delete payload.crnNo;
- delete payload.rgvNo;
- payload[valueKey] = deviceNo;
- element.value = JSON.stringify(payload);
- self.valueEditorText = element.value;
- });
- },
- applyDevpForm: function () {
- var self = this;
- var element = this.singleSelectedElement;
- if (!element || element.type !== 'devp') {
- return;
- }
- var stationId = toInt(this.devpForm.stationId, 0);
- var deviceNo = toInt(this.devpForm.deviceNo, 0);
- if (stationId <= 0 || deviceNo <= 0) {
- this.showMessage('warning', '绔欏彿鍜� PLC 缂栧彿蹇呴』澶т簬 0');
- return;
- }
- var payload = {
- stationId: stationId,
- deviceNo: deviceNo
- };
- var directionList = normalizeDirectionList(this.devpForm.direction);
- if (directionList.length > 0) {
- payload.direction = directionList;
- }
- var barcodeIdx = this.devpForm.barcodeIdx === '' ? 0 : toInt(this.devpForm.barcodeIdx, 0);
- var backStation = this.devpForm.backStation === '' ? 0 : toInt(this.devpForm.backStation, 0);
- var backStationDeviceNo = this.devpForm.backStationDeviceNo === '' ? 0 : toInt(this.devpForm.backStationDeviceNo, 0);
- var barcodeStation = this.devpForm.barcodeStation === '' ? 0 : toInt(this.devpForm.barcodeStation, 0);
- var barcodeStationDeviceNo = this.devpForm.barcodeStationDeviceNo === '' ? 0 : toInt(this.devpForm.barcodeStationDeviceNo, 0);
- if (this.devpForm.isInStation && (barcodeStation <= 0 || barcodeStationDeviceNo <= 0)) {
- this.showMessage('warning', '鍏ョ珯鐐瑰繀椤诲~鍐欐潯鐮佺珯鍜屾潯鐮佺珯 PLC 缂栧彿');
- return;
- }
- if (this.devpForm.isBarcodeStation && (backStation <= 0 || backStationDeviceNo <= 0 || barcodeIdx <= 0)) {
- this.showMessage('warning', '鏉$爜绔欏繀椤诲~鍐欐潯鐮佺储寮曘�侀��鍥炵珯鍜岄��鍥炵珯 PLC 缂栧彿');
- return;
- }
- if (this.devpForm.isBarcodeStation) {
- payload.isBarcodeStation = 1;
- }
- if (barcodeIdx > 0) {
- payload.barcodeIdx = barcodeIdx;
- }
- if (backStation > 0) {
- payload.backStation = backStation;
- }
- if (backStationDeviceNo > 0) {
- payload.backStationDeviceNo = backStationDeviceNo;
- }
- if (this.devpForm.isInStation) {
- payload.isInStation = 1;
- }
- if (barcodeStation > 0) {
- payload.barcodeStation = barcodeStation;
- }
- if (barcodeStationDeviceNo > 0) {
- payload.barcodeStationDeviceNo = barcodeStationDeviceNo;
- }
- if (this.devpForm.isOutStation) {
- payload.isOutStation = 1;
- }
- if (this.devpForm.runBlockReassign) {
- payload.runBlockReassign = 1;
- }
- if (this.devpForm.isOutOrder) {
- payload.isOutOrder = 1;
- }
- if (this.devpForm.isLiftTransfer) {
- payload.isLiftTransfer = 1;
- }
- this.runMutation(function () {
- element.value = JSON.stringify(payload);
- self.valueEditorText = element.value;
- });
- },
- deleteSelection: function () {
- var self = this;
- if (!this.doc || this.selectedIds.length === 0) {
- return;
- }
- var ids = this.selectedIds.slice();
- this.runMutation(function () {
- self.doc.elements = self.doc.elements.filter(function (item) {
- return ids.indexOf(item.id) === -1;
- });
- self.selectedIds = [];
- });
- },
- copySelection: function () {
- var elements = this.getSelectedElements();
- if (!elements.length) {
- return;
- }
- this.clipboard = deepClone(elements);
- this.showMessage('success', '宸插鍒� ' + elements.length + ' 涓厓绱�');
- },
- getElementListBounds: function (elements) {
- if (!elements || !elements.length) {
- return null;
- }
- var minX = elements[0].x;
- var minY = elements[0].y;
- var maxX = elements[0].x + elements[0].width;
- var maxY = elements[0].y + elements[0].height;
- for (var i = 1; i < elements.length; i++) {
- var element = elements[i];
- minX = Math.min(minX, element.x);
- minY = Math.min(minY, element.y);
- maxX = Math.max(maxX, element.x + element.width);
- maxY = Math.max(maxY, element.y + element.height);
- }
- return {
- x: minX,
- y: minY,
- width: maxX - minX,
- height: maxY - minY
- };
- },
- getPasteTargetWorld: function () {
- if (!this.doc) {
- return { x: 0, y: 0 };
- }
- var visible = this.getVisibleCanvasRect ? this.getVisibleCanvasRect() : this.getVisibleWorldRect();
- var fallback = {
- x: visible.x + visible.width / 2,
- y: visible.y + visible.height / 2
- };
- if (!this.lastPointerWorld) {
- return fallback;
- }
- return {
- x: clamp(this.lastPointerWorld.x, 0, this.doc.canvasWidth),
- y: clamp(this.lastPointerWorld.y, 0, this.doc.canvasHeight),
- screenX: this.lastPointerWorld.screenX,
- screenY: this.lastPointerWorld.screenY
- };
- },
- pasteClipboard: function () {
- var self = this;
- if (!this.doc || !this.clipboard.length) {
- return;
- }
- var sourceBounds = this.getElementListBounds(this.clipboard);
- if (!sourceBounds) {
- return;
- }
- var target = this.getPasteTargetWorld();
- var offsetX = target.x - (sourceBounds.x + sourceBounds.width / 2);
- var offsetY = target.y - (sourceBounds.y + sourceBounds.height / 2);
- var minOffsetX = -sourceBounds.x;
- var maxOffsetX = this.doc.canvasWidth - (sourceBounds.x + sourceBounds.width);
- var minOffsetY = -sourceBounds.y;
- var maxOffsetY = this.doc.canvasHeight - (sourceBounds.y + sourceBounds.height);
- offsetX = clamp(offsetX, minOffsetX, maxOffsetX);
- offsetY = clamp(offsetY, minOffsetY, maxOffsetY);
- var copies = deepClone(this.clipboard).map(function (item) {
- item.id = nextId();
- item.x = roundCoord(item.x + offsetX);
- item.y = roundCoord(item.y + offsetY);
- return item;
- });
- if (!this.canPlaceElements(copies, [])) {
- this.showMessage('warning', '绮樿创鍚庣殑鍏冪礌涓庣幇鏈夊厓绱犻噸鍙犳垨瓒呭嚭鐢诲竷');
- return;
- }
- this.runMutation(function () {
- self.doc.elements = self.doc.elements.concat(copies);
- self.selectedIds = copies.map(function (item) { return item.id; });
- });
- },
- canArrayFromElement: function (element) {
- return !!(element && ARRAY_TEMPLATE_TYPES.indexOf(element.type) >= 0);
- },
- getShelfFillSteps: function () {
- return {
- row: this.shelfFillForm.rowStep === 'asc' ? 1 : -1,
- col: this.shelfFillForm.colStep === 'desc' ? -1 : 1
- };
- },
- applyShelfSequenceToArrayCopies: function (template, copies) {
- if (!template || template.type !== 'shelf' || !copies || !copies.length) {
- return copies;
- }
- var base = parseShelfLocationValue(template.value) || parseShelfLocationValue(this.shelfFillForm.startValue);
- if (!base) {
- return copies;
- }
- var steps = this.getShelfFillSteps();
- var horizontal = Math.abs(copies[0].x - template.x) >= Math.abs(copies[0].y - template.y);
- var direction = 1;
- if (horizontal) {
- direction = copies[0].x >= template.x ? 1 : -1;
- } else {
- direction = copies[0].y >= template.y ? 1 : -1;
- }
- for (var i = 0; i < copies.length; i++) {
- var offset = i + 1;
- var row = base.row;
- var col = base.col;
- if (horizontal) {
- col = base.col + steps.col * direction * offset;
- } else {
- row = base.row + steps.row * direction * offset;
- }
- copies[i].value = formatShelfLocationValue(row, col);
- }
- return copies;
- },
- buildShelfGridAssignments: function (elements) {
- if (!elements || !elements.length) {
- return null;
- }
- var clusterAxis = function (list, axis, sizeKey) {
- var sorted = list.map(function (item) {
- return {
- id: item.id,
- center: item[axis] + item[sizeKey] / 2,
- size: item[sizeKey]
- };
- }).sort(function (a, b) {
- return a.center - b.center;
- });
- var avgSize = sorted.reduce(function (sum, item) {
- return sum + item.size;
- }, 0) / sorted.length;
- var tolerance = Math.max(6, avgSize * 0.45);
- var groups = [];
- for (var i = 0; i < sorted.length; i++) {
- var current = sorted[i];
- var last = groups.length ? groups[groups.length - 1] : null;
- if (!last || Math.abs(current.center - last.center) > tolerance) {
- groups.push({
- center: current.center,
- items: [current]
- });
- } else {
- last.items.push(current);
- last.center = last.items.reduce(function (sum, item) {
- return sum + item.center;
- }, 0) / last.items.length;
- }
- }
- var indexById = {};
- for (var groupIndex = 0; groupIndex < groups.length; groupIndex++) {
- for (var itemIndex = 0; itemIndex < groups[groupIndex].items.length; itemIndex++) {
- indexById[groups[groupIndex].items[itemIndex].id] = groupIndex;
- }
- }
- return indexById;
- };
- return {
- rowById: clusterAxis(elements, 'y', 'height'),
- colById: clusterAxis(elements, 'x', 'width')
- };
- },
- applyShelfAutoFill: function () {
- var self = this;
- var shelves = this.selectedShelfElements.slice();
- if (!shelves.length) {
- this.showMessage('warning', '璇峰厛閫変腑鑷冲皯涓�涓揣鏋�');
- return;
- }
- var start = parseShelfLocationValue(this.shelfFillForm.startValue);
- if (!start) {
- this.showMessage('warning', '璧峰鍊兼牸寮忓繀椤绘槸 鎺�-鍒楋紝渚嬪 12-1');
- return;
- }
- var grid = this.buildShelfGridAssignments(shelves);
- if (!grid) {
- return;
- }
- var steps = this.getShelfFillSteps();
- this.runMutation(function () {
- shelves.forEach(function (item) {
- var rowIndex = grid.rowById[item.id] || 0;
- var colIndex = grid.colById[item.id] || 0;
- item.value = formatShelfLocationValue(
- start.row + rowIndex * steps.row,
- start.col + colIndex * steps.col
- );
- });
- if (self.singleSelectedElement && self.singleSelectedElement.type === 'shelf') {
- self.valueEditorText = self.singleSelectedElement.value || '';
- }
- });
- },
- buildArrayCopies: function (template, startWorld, currentWorld) {
- if (!this.doc || !template || !startWorld || !currentWorld || !this.canArrayFromElement(template)) {
- return [];
- }
- var deltaX = currentWorld.x - startWorld.x;
- var deltaY = currentWorld.y - startWorld.y;
- if (Math.abs(deltaX) < COORD_EPSILON && Math.abs(deltaY) < COORD_EPSILON) {
- return [];
- }
- var horizontal = Math.abs(deltaX) >= Math.abs(deltaY);
- var step = horizontal ? template.width : template.height;
- if (step <= COORD_EPSILON) {
- return [];
- }
- var direction = (horizontal ? deltaX : deltaY) >= 0 ? 1 : -1;
- var distance;
- if (horizontal) {
- distance = direction > 0
- ? currentWorld.x - (template.x + template.width)
- : template.x - currentWorld.x;
- } else {
- distance = direction > 0
- ? currentWorld.y - (template.y + template.height)
- : template.y - currentWorld.y;
- }
- var count = Math.max(0, Math.floor((distance + step * 0.5) / step));
- if (count <= 0) {
- return [];
- }
- var copies = [];
- for (var i = 1; i <= count; i++) {
- copies.push({
- type: template.type,
- x: roundCoord(template.x + (horizontal ? direction * template.width * i : 0)),
- y: roundCoord(template.y + (horizontal ? 0 : direction * template.height * i)),
- width: template.width,
- height: template.height,
- value: template.value
- });
- }
- return this.applyShelfSequenceToArrayCopies(template, copies);
- },
- duplicateSelection: function () {
- this.copySelection();
- this.pasteClipboard();
- },
- getElementBounds: function (ids) {
- if (!this.doc) {
- return null;
- }
- var elements = ids && ids.length ? this.getSelectedElements() : (this.doc.elements || []);
- if (ids && ids.length) {
- elements = ids.map(function (id) {
- return this.findElementById(id);
- }, this).filter(Boolean);
- }
- if (!elements.length) {
- return null;
- }
- var minX = elements[0].x;
- var minY = elements[0].y;
- var maxX = elements[0].x + elements[0].width;
- var maxY = elements[0].y + elements[0].height;
- for (var i = 1; i < elements.length; i++) {
- var element = elements[i];
- minX = Math.min(minX, element.x);
- minY = Math.min(minY, element.y);
- maxX = Math.max(maxX, element.x + element.width);
- maxY = Math.max(maxY, element.y + element.height);
- }
- return {
- x: minX,
- y: minY,
- width: maxX - minX,
- height: maxY - minY
- };
- },
- fitContent: function () {
- if (!this.doc || !this.pixiApp) {
- return;
- }
- var contentBounds = this.getElementBounds();
- if (contentBounds && contentBounds.width > 0 && contentBounds.height > 0) {
- this.fitRect(contentBounds, this.pixiApp.renderer.width, this.pixiApp.renderer.height);
- return;
- }
- this.fitCanvas();
- },
- fitCanvas: function () {
- if (!this.doc || !this.pixiApp) {
- return;
- }
- var renderer = this.pixiApp.renderer;
- var target = {
- x: 0,
- y: 0,
- width: Math.max(1, this.doc.canvasWidth),
- height: Math.max(1, this.doc.canvasHeight)
- };
- this.fitRect(target, renderer.width, renderer.height);
- },
- fitSelection: function () {
- if (!this.selectedIds.length || !this.pixiApp) {
- return;
- }
- var bounds = this.getElementBounds(this.selectedIds);
- if (!bounds) {
- return;
- }
- this.fitRect(bounds, this.pixiApp.renderer.width, this.pixiApp.renderer.height);
- },
- fitRect: function (rect, viewportWidth, viewportHeight) {
- var padding = 80;
- var scale = Math.min(
- (viewportWidth - padding * 2) / Math.max(rect.width, 1),
- (viewportHeight - padding * 2) / Math.max(rect.height, 1)
- );
- scale = clamp(scale, 0.06, 4);
- this.camera.scale = scale;
- this.camera.x = Math.round((viewportWidth - rect.width * scale) / 2 - rect.x * scale);
- this.camera.y = Math.round((viewportHeight - rect.height * scale) / 2 - rect.y * scale);
- this.viewZoom = scale;
- this.markGridSceneDirty();
- this.markStaticSceneDirty();
- this.scheduleRender();
- },
- resetView: function () {
- this.fitCanvas();
- },
- getVisibleWorldRect: function () {
- if (!this.pixiApp) {
- return {
- x: 0,
- y: 0,
- width: 0,
- height: 0
- };
- }
- return {
- x: (-this.camera.x) / this.camera.scale,
- y: (-this.camera.y) / this.camera.scale,
- width: this.pixiApp.renderer.width / this.camera.scale,
- height: this.pixiApp.renderer.height / this.camera.scale
- };
- },
- getVisibleCanvasRect: function () {
- if (!this.doc) {
- return {
- x: 0,
- y: 0,
- width: 0,
- height: 0
- };
- }
- var visible = this.getVisibleWorldRect();
- var left = clamp(visible.x, 0, this.doc.canvasWidth);
- var top = clamp(visible.y, 0, this.doc.canvasHeight);
- var right = clamp(visible.x + visible.width, 0, this.doc.canvasWidth);
- var bottom = clamp(visible.y + visible.height, 0, this.doc.canvasHeight);
- return {
- x: left,
- y: top,
- width: Math.max(0, right - left),
- height: Math.max(0, bottom - top)
- };
- },
- getWorldRectWithPadding: function (screenPadding) {
- if (!this.doc) {
- return {
- x: 0,
- y: 0,
- width: 0,
- height: 0
- };
- }
- var visible = this.getVisibleWorldRect();
- var padding = Math.max(screenPadding / this.camera.scale, 24);
- var left = Math.max(0, visible.x - padding);
- var top = Math.max(0, visible.y - padding);
- var right = Math.min(this.doc.canvasWidth, visible.x + visible.width + padding);
- var bottom = Math.min(this.doc.canvasHeight, visible.y + visible.height + padding);
- return {
- x: left,
- y: top,
- width: Math.max(0, right - left),
- height: Math.max(0, bottom - top)
- };
- },
- worldRectContains: function (outer, inner) {
- if (!outer || !inner) {
- return false;
- }
- return inner.x >= outer.x - COORD_EPSILON
- && inner.y >= outer.y - COORD_EPSILON
- && inner.x + inner.width <= outer.x + outer.width + COORD_EPSILON
- && inner.y + inner.height <= outer.y + outer.height + COORD_EPSILON;
- },
- getGridRenderKey: function () {
- var minorStep = this.camera.scale > 1.5 ? 50 : (this.camera.scale > 0.45 ? 100 : 200);
- return minorStep + '|' + (Math.round(this.camera.scale * 8) / 8);
- },
- getStaticRenderKey: function () {
- return (this.camera.scale >= 0.85 ? 'round' : 'flat') + '|' + (Math.round(this.camera.scale * 8) / 8);
- },
- scheduleRender: function () {
- if (this.renderQueued) {
- return;
- }
- this.renderQueued = true;
- window.requestAnimationFrame(function () {
- this.renderQueued = false;
- this.renderScene();
- }.bind(this));
- },
- renderScene: function () {
- if (!this.pixiApp || !this.doc) {
- return;
- }
- this.mapRoot.position.set(this.camera.x, this.camera.y);
- this.mapRoot.scale.set(this.camera.scale, this.camera.scale);
- this.viewZoom = this.camera.scale;
- var visible = this.getVisibleCanvasRect();
- var viewportSettled = !this.isZooming && !this.isPanning && !(this.interactionState && this.interactionState.type === 'pan');
- var gridKeyChanged = this.gridRenderKey !== this.getGridRenderKey();
- if (this.gridSceneDirty || !this.gridRenderRect || (viewportSettled && gridKeyChanged) || (viewportSettled && !this.worldRectContains(this.gridRenderRect, visible))) {
- this.renderGrid(this.getWorldRectWithPadding(STATIC_VIEW_PADDING));
- this.gridSceneDirty = false;
- }
- var excludedKey = this.selectionKey(this.getStaticExcludedIds());
- var staticKeyChanged = this.staticRenderKey !== this.getStaticRenderKey();
- if (this.staticSceneDirty || !this.staticRenderRect || (viewportSettled && staticKeyChanged)
- || this.staticExcludedKey !== excludedKey || (viewportSettled && !this.worldRectContains(this.staticRenderRect, visible))) {
- this.renderStaticElements(this.getWorldRectWithPadding(STATIC_VIEW_PADDING), excludedKey);
- this.staticSceneDirty = false;
- }
- this.renderActiveElements();
- this.renderLabels();
- this.renderHover();
- this.renderSelection();
- this.renderGuide();
- this.updateCursor();
- },
- getStaticExcludedIds: function () {
- if (!this.interactionState) {
- return [];
- }
- if (this.interactionState.type === 'move' && this.selectedIds.length) {
- return this.selectedIds.slice();
- }
- if (this.interactionState.type === 'resize' && this.interactionState.elementId) {
- return [this.interactionState.elementId];
- }
- return [];
- },
- getRenderableElements: function (excludeIds, renderRect) {
- if (!this.doc) {
- return [];
- }
- var rect = renderRect || this.getWorldRectWithPadding(STATIC_VIEW_PADDING);
- var candidates = this.querySpatialCandidates(rect, 0, excludeIds);
- var result = [];
- for (var i = 0; i < candidates.length; i++) {
- if (rectIntersects(rect, candidates[i])) {
- result.push(candidates[i]);
- }
- }
- return result;
- },
- renderGrid: function (renderRect) {
- if (!this.gridLayer || !this.doc) {
- return;
- }
- var visible = renderRect || this.getVisibleWorldRect();
- var width = this.doc.canvasWidth;
- var height = this.doc.canvasHeight;
- var minorStep = this.camera.scale > 1.5 ? 50 : (this.camera.scale > 0.45 ? 100 : 200);
- var majorStep = minorStep * 5;
- var lineWidth = 1 / this.camera.scale;
- var xStart = Math.max(0, Math.floor(visible.x / minorStep) * minorStep);
- var yStart = Math.max(0, Math.floor(visible.y / minorStep) * minorStep);
- var xEnd = Math.min(width, visible.x + visible.width);
- var yEnd = Math.min(height, visible.y + visible.height);
-
- this.gridLayer.clear();
- this.gridLayer.beginFill(0xfafcff, 1);
- this.gridLayer.drawRect(0, 0, width, height);
- this.gridLayer.endFill();
-
- this.gridLayer.lineStyle(lineWidth, 0xdbe4ee, 1);
- this.gridLayer.drawRect(0, 0, width, height);
-
- for (var x = xStart; x <= xEnd; x += minorStep) {
- var colorX = (x % majorStep === 0) ? 0xc9d7e6 : 0xe4ebf3;
- this.gridLayer.lineStyle(lineWidth, colorX, x % majorStep === 0 ? 0.95 : 0.75);
- this.gridLayer.moveTo(x, 0);
- this.gridLayer.lineTo(x, height);
- }
- for (var y = yStart; y <= yEnd; y += minorStep) {
- var colorY = (y % majorStep === 0) ? 0xc9d7e6 : 0xe4ebf3;
- this.gridLayer.lineStyle(lineWidth, colorY, y % majorStep === 0 ? 0.95 : 0.75);
- this.gridLayer.moveTo(0, y);
- this.gridLayer.lineTo(width, y);
- }
- this.gridRenderRect = {
- x: visible.x,
- y: visible.y,
- width: visible.width,
- height: visible.height
- };
- this.gridRenderKey = this.getGridRenderKey();
- },
- drawGridPatch: function (rects, layer) {
- if (!this.doc || !layer || !rects || !rects.length) {
- return;
- }
- var width = this.doc.canvasWidth;
- var height = this.doc.canvasHeight;
- var minorStep = this.camera.scale > 1.5 ? 50 : (this.camera.scale > 0.45 ? 100 : 200);
- var majorStep = minorStep * 5;
- var lineWidth = 1 / this.camera.scale;
- for (var i = 0; i < rects.length; i++) {
- var rect = rects[i];
- var left = clamp(rect.x - lineWidth, 0, width);
- var top = clamp(rect.y - lineWidth, 0, height);
- var right = clamp(rect.x + rect.width + lineWidth, 0, width);
- var bottom = clamp(rect.y + rect.height + lineWidth, 0, height);
- if (right <= left || bottom <= top) {
- continue;
- }
- layer.lineStyle(0, 0, 0, 0);
- layer.beginFill(0xfafcff, 1);
- layer.drawRect(left, top, right - left, bottom - top);
- layer.endFill();
- if (right - left < minorStep || bottom - top < minorStep) {
- continue;
- }
- var xStart = Math.floor(left / minorStep) * minorStep;
- var yStart = Math.floor(top / minorStep) * minorStep;
- for (var x = xStart; x <= right; x += minorStep) {
- if (x < left || x > right) {
- continue;
- }
- var colorX = (x % majorStep === 0) ? 0xc9d7e6 : 0xe4ebf3;
- layer.lineStyle(lineWidth, colorX, x % majorStep === 0 ? 0.95 : 0.75);
- layer.moveTo(x, top);
- layer.lineTo(x, bottom);
- }
- for (var y = yStart; y <= bottom; y += minorStep) {
- if (y < top || y > bottom) {
- continue;
- }
- var colorY = (y % majorStep === 0) ? 0xc9d7e6 : 0xe4ebf3;
- layer.lineStyle(lineWidth, colorY, y % majorStep === 0 ? 0.95 : 0.75);
- layer.moveTo(left, y);
- layer.lineTo(right, y);
- }
- }
- },
- drawPatchObjects: function (rects, excludeIds) {
- if (!rects || !rects.length || !this.patchObjectLayer) {
- return;
- }
- var seen = {};
- var elements = [];
- for (var i = 0; i < rects.length; i++) {
- var candidates = this.querySpatialCandidates(rects[i], 0, excludeIds);
- for (var j = 0; j < candidates.length; j++) {
- var item = candidates[j];
- if (!seen[item.id] && rectIntersects(rects[i], item)) {
- seen[item.id] = true;
- elements.push(item);
- }
- }
- }
- if (!elements.length) {
- return;
- }
- this.drawElementsToLayers(elements, this.patchObjectLayer, this.patchObjectLayer);
- },
- drawElementsToLayers: function (elements, trackLayer, nodeLayer) {
- var lineWidth = 1 / this.camera.scale;
- var useRounded = this.camera.scale >= 0.85;
- var radius = Math.max(6 / this.camera.scale, 2);
- var buckets = {};
- for (var i = 0; i < elements.length; i++) {
- var element = elements[i];
- var bucketKey = (element.type === 'shelf' ? 'node' : 'track') + ':' + element.type;
- if (!buckets[bucketKey]) {
- buckets[bucketKey] = [];
- }
- buckets[bucketKey].push(element);
- }
- for (var bucketKey in buckets) {
- if (!buckets.hasOwnProperty(bucketKey)) {
- continue;
- }
- var parts = bucketKey.split(':');
- var type = parts[1];
- var meta = getTypeMeta(type);
- var layer = parts[0] === 'node' ? nodeLayer : trackLayer;
- layer.lineStyle(lineWidth, meta.border, 1);
- layer.beginFill(meta.fill, 0.92);
- var bucket = buckets[bucketKey];
- for (var j = 0; j < bucket.length; j++) {
- var item = bucket[j];
- if (useRounded) {
- layer.drawRoundedRect(item.x, item.y, item.width, item.height, radius);
- } else {
- layer.drawRect(item.x, item.y, item.width, item.height);
- }
- }
- layer.endFill();
- }
- },
- ensureStaticSprite: function (poolName, index) {
- var pool = poolName === 'node' ? this.staticNodeSpritePool : this.staticTrackSpritePool;
- var layer = poolName === 'node' ? this.staticNodeSpriteLayer : this.staticTrackSpriteLayer;
- if (pool[index]) {
- return pool[index];
- }
- var sprite = new PIXI.Sprite(PIXI.Texture.WHITE);
- sprite.position.set(0, 0);
- sprite.anchor.set(0, 0);
- sprite.visible = false;
- sprite.alpha = 0;
- layer.addChild(sprite);
- pool[index] = sprite;
- return sprite;
- },
- hideUnusedStaticSprites: function (pool, fromIndex) {
- for (var i = fromIndex; i < pool.length; i++) {
- pool[i].visible = false;
- pool[i].alpha = 0;
- pool[i].width = 0;
- pool[i].height = 0;
- pool[i].position.set(-99999, -99999);
- }
- },
- pruneStaticSpritePool: function (poolName, keepCount, slack) {
- var pool = poolName === 'node' ? this.staticNodeSpritePool : this.staticTrackSpritePool;
- var layer = poolName === 'node' ? this.staticNodeSpriteLayer : this.staticTrackSpriteLayer;
- var target = Math.max(0, keepCount + Math.max(0, slack || 0));
- if (!pool || !layer || pool.length <= target) {
- return;
- }
- for (var i = pool.length - 1; i >= target; i--) {
- var sprite = pool[i];
- layer.removeChild(sprite);
- if (sprite && sprite.destroy) {
- sprite.destroy();
- }
- pool.pop();
- }
- },
- drawElementsToSpriteLayers: function (elements) {
- var trackCount = 0;
- var nodeCount = 0;
- for (var i = 0; i < elements.length; i++) {
- var item = elements[i];
- var meta = getTypeMeta(item.type);
- var poolName = item.type === 'shelf' ? 'node' : 'track';
- var sprite = this.ensureStaticSprite(poolName, poolName === 'node' ? nodeCount : trackCount);
- sprite.visible = true;
- sprite.position.set(item.x, item.y);
- sprite.width = item.width;
- sprite.height = item.height;
- sprite.tint = meta.fill;
- sprite.alpha = 0.92;
- if (poolName === 'node') {
- nodeCount += 1;
- } else {
- trackCount += 1;
- }
- }
- this.hideUnusedStaticSprites(this.staticTrackSpritePool, trackCount);
- this.hideUnusedStaticSprites(this.staticNodeSpritePool, nodeCount);
- if (this.camera.scale < DENSE_SIMPLIFY_SCALE_THRESHOLD) {
- this.pruneStaticSpritePool('track', trackCount, STATIC_SPRITE_POOL_SLACK);
- this.pruneStaticSpritePool('node', nodeCount, STATIC_SPRITE_POOL_SLACK);
- }
- },
- simplifyRenderableElements: function (elements) {
- if (!elements || elements.length < 2) {
- return elements || [];
- }
- var sorted = elements.slice().sort(function (a, b) {
- if (a.type !== b.type) {
- return a.type < b.type ? -1 : 1;
- }
- if (Math.abs(a.y - b.y) > COORD_EPSILON) {
- return a.y - b.y;
- }
- if (Math.abs(a.height - b.height) > COORD_EPSILON) {
- return a.height - b.height;
- }
- return a.x - b.x;
- });
- var result = [];
- var current = null;
- for (var i = 0; i < sorted.length; i++) {
- var item = sorted[i];
- if (!current) {
- current = {
- type: item.type,
- x: item.x,
- y: item.y,
- width: item.width,
- height: item.height
- };
- continue;
- }
- var currentRight = current.x + current.width;
- var itemRight = item.x + item.width;
- var sameBand = current.type === item.type
- && Math.abs(current.y - item.y) <= 0.5
- && Math.abs(current.height - item.height) <= 0.5;
- var joinable = item.x <= currentRight + 0.5;
- if (sameBand && joinable) {
- current.width = roundCoord(Math.max(currentRight, itemRight) - current.x);
- } else {
- result.push(current);
- current = {
- type: item.type,
- x: item.x,
- y: item.y,
- width: item.width,
- height: item.height
- };
- }
- }
- if (current) {
- result.push(current);
- }
- return result;
- },
- renderStaticElements: function (renderRect, excludedKey) {
- if (!this.doc) {
- return;
- }
- this.trackLayer.clear();
- this.nodeLayer.clear();
- this.eraseLayer.clear();
- this.patchObjectLayer.clear();
- var renderableElements = this.getRenderableElements(this.getStaticExcludedIds(), renderRect);
- var useSpriteMode = this.camera.scale < STATIC_SPRITE_SCALE_THRESHOLD;
- var shouldSimplify = this.camera.scale < STATIC_SIMPLIFY_SCALE_THRESHOLD
- || (this.camera.scale < DENSE_SIMPLIFY_SCALE_THRESHOLD && renderableElements.length > DENSE_SIMPLIFY_ELEMENT_THRESHOLD);
- this.staticTrackSpriteLayer.visible = useSpriteMode;
- this.staticNodeSpriteLayer.visible = useSpriteMode;
- this.trackLayer.visible = !useSpriteMode;
- this.nodeLayer.visible = !useSpriteMode;
- if (useSpriteMode) {
- if (shouldSimplify) {
- renderableElements = this.simplifyRenderableElements(renderableElements);
- }
- this.drawElementsToSpriteLayers(renderableElements);
- } else {
- this.hideUnusedStaticSprites(this.staticTrackSpritePool, 0);
- this.hideUnusedStaticSprites(this.staticNodeSpritePool, 0);
- this.drawElementsToLayers(renderableElements, this.trackLayer, this.nodeLayer);
- }
- var rect = renderRect || this.getWorldRectWithPadding(STATIC_VIEW_PADDING);
- this.staticRenderRect = {
- x: rect.x,
- y: rect.y,
- width: rect.width,
- height: rect.height
- };
- this.staticRenderKey = this.getStaticRenderKey();
- this.staticExcludedKey = excludedKey != null ? excludedKey : this.selectionKey(this.getStaticExcludedIds());
- this.pendingStaticCommit = null;
- },
- renderActiveElements: function () {
- this.activeLayer.clear();
- this.eraseLayer.clear();
- this.patchObjectLayer.clear();
- var activeIds = this.getStaticExcludedIds();
- if (!activeIds.length) {
- return;
- }
- var activeElements = [];
- for (var idx = 0; idx < activeIds.length; idx++) {
- var element = this.findElementById(activeIds[idx]);
- if (element) {
- activeElements.push(element);
- }
- }
- if (!activeElements.length) {
- return;
- }
- this.drawElementsToLayers(activeElements, this.activeLayer, this.activeLayer);
- },
- getLabelText: function (element) {
- var meta = getTypeMeta(element.type);
- var value = safeParseJson(element.value);
- if (element.type === 'devp' && value) {
- var station = value.stationId != null ? String(value.stationId) : '';
- var arrows = formatDirectionArrows(value.direction);
- if (station && arrows) {
- return element.height > element.width * 1.15 ? (station + '\n' + arrows) : (station + ' ' + arrows);
- }
- if (station) {
- return station;
- }
- if (arrows) {
- return arrows;
- }
- return meta.shortLabel;
- }
- if ((element.type === 'crn' || element.type === 'dualCrn' || element.type === 'rgv') && value) {
- if (value.deviceNo != null) {
- return meta.shortLabel + ' ' + value.deviceNo;
- }
- if (value.crnNo != null) {
- return meta.shortLabel + ' ' + value.crnNo;
- }
- if (value.rgvNo != null) {
- return meta.shortLabel + ' ' + value.rgvNo;
- }
- }
- if (element.value && element.value.length <= 18 && element.value.indexOf('{') !== 0) {
- return element.value;
- }
- return meta.shortLabel;
- },
- ensureLabelSprite: function (index) {
- if (this.labelPool[index]) {
- return this.labelPool[index];
- }
- var label = new PIXI.Text('', {
- fontFamily: 'Avenir Next, PingFang SC, Microsoft YaHei, sans-serif',
- fontSize: 12,
- fontWeight: '600',
- fill: 0x223448,
- align: 'center'
- });
- label.anchor.set(0.5);
- this.labelLayer.addChild(label);
- this.labelPool[index] = label;
- return label;
- },
- getLabelRenderBudget: function () {
- if (!this.pixiApp || !this.pixiApp.renderer) {
- return MIN_LABEL_COUNT;
- }
- var renderer = this.pixiApp.renderer;
- var viewportArea = renderer.width * renderer.height;
- return clamp(Math.round(viewportArea / 12000), MIN_LABEL_COUNT, MAX_LABEL_COUNT);
- },
- getLabelMinScreenWidth: function (text) {
- var lines = String(text || '').split('\n');
- var length = 0;
- for (var i = 0; i < lines.length; i++) {
- length = Math.max(length, String(lines[i] || '').trim().length);
- }
- if (length <= 4) {
- return 26;
- }
- if (length <= 8) {
- return 40;
- }
- if (length <= 12) {
- return 52;
- }
- return 64;
- },
- getLabelMinScreenHeight: function (text) {
- var lines = String(text || '').split('\n');
- var length = 0;
- for (var i = 0; i < lines.length; i++) {
- length = Math.max(length, String(lines[i] || '').trim().length);
- }
- var lineHeight = length <= 4 ? 14 : 18;
- return lineHeight * Math.max(lines.length, 1);
- },
- renderLabels: function () {
- if (!this.doc) {
- return;
- }
- if (!SHOW_CANVAS_ELEMENT_LABELS) {
- this.labelLayer.visible = false;
- for (var hiddenIdx = 0; hiddenIdx < this.labelPool.length; hiddenIdx++) {
- this.labelPool[hiddenIdx].visible = false;
- }
- return;
- }
- var capability = this.ensureLabelCapability();
- if (capability.maxWidth * this.camera.scale < ABS_MIN_LABEL_SCREEN_WIDTH
- || capability.maxHeight * this.camera.scale < ABS_MIN_LABEL_SCREEN_HEIGHT) {
- this.labelLayer.visible = false;
- return;
- }
- if (this.isZooming || this.isPanning || this.camera.scale < MIN_LABEL_SCALE
- || (this.interactionState && (this.interactionState.type === 'move' || this.interactionState.type === 'resize' || this.interactionState.type === 'pan'))) {
- this.labelLayer.visible = false;
- return;
- }
- this.labelLayer.visible = true;
- var visible = this.getVisibleWorldRect();
- var elements = this.querySpatialCandidates(visible, 0, []);
- if (elements.length > DENSE_LABEL_HIDE_ELEMENT_THRESHOLD && this.camera.scale < DENSE_LABEL_HIDE_SCALE_THRESHOLD) {
- this.labelLayer.visible = false;
- return;
- }
- var hasRoomForAnyLabel = false;
- for (var roomIdx = 0; roomIdx < elements.length; roomIdx++) {
- var candidate = elements[roomIdx];
- if (candidate.width * this.camera.scale >= ABS_MIN_LABEL_SCREEN_WIDTH
- && candidate.height * this.camera.scale >= ABS_MIN_LABEL_SCREEN_HEIGHT) {
- hasRoomForAnyLabel = true;
- break;
- }
- }
- if (!hasRoomForAnyLabel) {
- this.labelLayer.visible = false;
- return;
- }
- var visibleElements = [];
- for (var i = 0; i < elements.length; i++) {
- var element = elements[i];
- var text = this.getLabelText(element);
- if (!text) {
- continue;
- }
- if (!rectIntersects(visible, element)) {
- continue;
- }
- if (element.width * this.camera.scale < this.getLabelMinScreenWidth(text) || element.height * this.camera.scale < this.getLabelMinScreenHeight(text)) {
- continue;
- }
- visibleElements.push({
- element: element,
- text: text
- });
- }
- visibleElements.sort(function (a, b) {
- return (b.element.width * b.element.height) - (a.element.width * a.element.height);
- });
- var labelBudget = this.getLabelRenderBudget();
- if (visibleElements.length > labelBudget) {
- visibleElements = visibleElements.slice(0, labelBudget);
- }
- for (var j = 0; j < visibleElements.length; j++) {
- var item = visibleElements[j].element;
- var label = this.ensureLabelSprite(j);
- label.visible = true;
- label.text = visibleElements[j].text;
- label.position.set(item.x + item.width / 2, item.y + item.height / 2);
- label.scale.set(1 / this.camera.scale, 1 / this.camera.scale);
- label.alpha = this.selectedIds.indexOf(item.id) >= 0 ? 1 : 0.88;
- }
- for (var k = visibleElements.length; k < this.labelPool.length; k++) {
- this.labelPool[k].visible = false;
- }
- },
- renderHover: function () {
- this.hoverLayer.clear();
- if (this.interactionState || !this.hoverElementId || this.selectedIds.indexOf(this.hoverElementId) >= 0) {
- return;
- }
- var element = this.findElementById(this.hoverElementId);
- if (!element) {
- return;
- }
- var lineWidth = 2 / this.camera.scale;
- this.hoverLayer.lineStyle(lineWidth, 0x2f79d6, 0.95);
- this.hoverLayer.drawRoundedRect(element.x, element.y, element.width, element.height, Math.max(6 / this.camera.scale, 2));
- },
- renderSelection: function () {
- this.selectionLayer.clear();
- if (!this.selectedIds.length || (this.interactionState && (this.interactionState.type === 'move' || this.interactionState.type === 'resize'))) {
- return;
- }
- var elements = this.getSelectedElements();
- var lineWidth = 2 / this.camera.scale;
- for (var i = 0; i < elements.length; i++) {
- var element = elements[i];
- this.selectionLayer.lineStyle(lineWidth, 0x2568b8, 1);
- this.selectionLayer.beginFill(0x2f79d6, 0.07);
- this.selectionLayer.drawRoundedRect(element.x, element.y, element.width, element.height, Math.max(6 / this.camera.scale, 2));
- this.selectionLayer.endFill();
- }
- if (elements.length !== 1) {
- return;
- }
- var handleSize = HANDLE_SCREEN_SIZE / this.camera.scale;
- var handlePositions = this.getHandlePositions(elements[0]);
- this.selectionLayer.lineStyle(1 / this.camera.scale, 0x1d5ea9, 1);
- this.selectionLayer.beginFill(0xffffff, 1);
- for (var key in handlePositions) {
- if (!handlePositions.hasOwnProperty(key)) {
- continue;
- }
- var pos = handlePositions[key];
- this.selectionLayer.drawRect(pos.x - handleSize / 2, pos.y - handleSize / 2, handleSize, handleSize);
- }
- this.selectionLayer.endFill();
- },
- renderGuide: function () {
- this.guideLayer.clear();
- if (this.guideText) {
- this.guideText.visible = false;
- }
- if (!this.interactionState) {
- return;
- }
- var state = this.interactionState;
- if (state.type === 'draw' && state.rect && state.rect.width > 0 && state.rect.height > 0) {
- var drawMeta = getTypeMeta(state.elementType);
- this.guideLayer.lineStyle(2 / this.camera.scale, drawMeta.border, 0.95);
- this.guideLayer.beginFill(drawMeta.fill, 0.18);
- this.guideLayer.drawRoundedRect(state.rect.x, state.rect.y, state.rect.width, state.rect.height, Math.max(6 / this.camera.scale, 2));
- this.guideLayer.endFill();
- return;
- }
- if (state.type === 'array' && state.template) {
- var previewItems = state.previewItems || [];
- var arrayMeta = getTypeMeta(state.template.type);
- var lineWidth = 2 / this.camera.scale;
- var templateCenterX = state.template.x + state.template.width / 2;
- var templateCenterY = state.template.y + state.template.height / 2;
- this.guideLayer.lineStyle(lineWidth, arrayMeta.border, 0.9);
- this.guideLayer.moveTo(templateCenterX, templateCenterY);
- this.guideLayer.lineTo(state.currentWorld.x, state.currentWorld.y);
- if (!previewItems.length) {
- return;
- }
- this.guideLayer.lineStyle(1 / this.camera.scale, arrayMeta.border, 0.8);
- this.guideLayer.beginFill(arrayMeta.fill, 0.2);
- for (var previewIndex = 0; previewIndex < previewItems.length; previewIndex++) {
- var preview = previewItems[previewIndex];
- this.guideLayer.drawRoundedRect(preview.x, preview.y, preview.width, preview.height, Math.max(6 / this.camera.scale, 2));
- }
- this.guideLayer.endFill();
- if (this.guideText) {
- this.guideText.text = '灏嗙敓鎴� ' + previewItems.length + ' 涓�';
- this.guideText.position.set(state.currentWorld.x, state.currentWorld.y - 10 / this.camera.scale);
- this.guideText.scale.set(1 / this.camera.scale);
- this.guideText.visible = true;
- }
- return;
- }
- if (state.type === 'marquee') {
- var rect = buildRectFromPoints(state.startWorld, state.currentWorld);
- if (rect.width <= 0 || rect.height <= 0) {
- return;
- }
- this.guideLayer.lineStyle(2 / this.camera.scale, 0x2f79d6, 0.92);
- this.guideLayer.beginFill(0x2f79d6, 0.06);
- this.guideLayer.drawRect(rect.x, rect.y, rect.width, rect.height);
- this.guideLayer.endFill();
- }
- },
- pointerToWorld: function (event) {
- var rect = this.pixiApp.view.getBoundingClientRect();
- var screenX = event.clientX - rect.left;
- var screenY = event.clientY - rect.top;
- return {
- screenX: screenX,
- screenY: screenY,
- x: roundCoord((screenX - this.camera.x) / this.camera.scale),
- y: roundCoord((screenY - this.camera.y) / this.camera.scale)
- };
- },
- isWithinCanvas: function (rect) {
- if (!this.doc) {
- return false;
- }
- return rect.x >= -COORD_EPSILON && rect.y >= -COORD_EPSILON
- && rect.x + rect.width <= this.doc.canvasWidth + COORD_EPSILON
- && rect.y + rect.height <= this.doc.canvasHeight + COORD_EPSILON;
- },
- canPlaceElements: function (elements, excludeIds) {
- excludeIds = excludeIds || [];
- for (var i = 0; i < elements.length; i++) {
- if (!this.isWithinCanvas(elements[i])) {
- return false;
- }
- if (this.hasOverlap(elements[i], excludeIds.concat([elements[i].id]))) {
- return false;
- }
- }
- return true;
- },
- hasOverlap: function (candidate, excludeIds) {
- if (!this.doc) {
- return false;
- }
- var elements = this.querySpatialCandidates(candidate, COORD_EPSILON, excludeIds);
- for (var i = 0; i < elements.length; i++) {
- var item = elements[i];
- if (rectsOverlap(candidate, item)) {
- return true;
- }
- }
- return false;
- },
- snapToleranceWorld: function () {
- return Math.max(1, EDGE_SNAP_SCREEN_TOLERANCE / this.camera.scale);
- },
- collectMoveSnap: function (baseItems, dx, dy, excludeIds) {
- if (!this.doc || !baseItems || !baseItems.length) {
- return { dx: 0, dy: 0 };
- }
- var tolerance = this.snapToleranceWorld();
- var bestDx = null;
- var bestDy = null;
- for (var i = 0; i < baseItems.length; i++) {
- var moving = baseItems[i];
- var movedLeft = moving.x + dx;
- var movedRight = movedLeft + moving.width;
- var movedTop = moving.y + dy;
- var movedBottom = movedTop + moving.height;
- var candidates = this.querySpatialCandidates({
- x: movedLeft,
- y: movedTop,
- width: moving.width,
- height: moving.height
- }, tolerance, excludeIds);
- for (var j = 0; j < candidates.length; j++) {
- var other = candidates[j];
- var otherLeft = other.x;
- var otherRight = other.x + other.width;
- var otherTop = other.y;
- var otherBottom = other.y + other.height;
- if (rangesNearOrOverlap(movedTop, movedBottom, otherTop, otherBottom, tolerance)) {
- var horizontalCandidates = [
- otherLeft - movedRight,
- otherRight - movedLeft,
- otherLeft - movedLeft,
- otherRight - movedRight
- ];
- for (var hx = 0; hx < horizontalCandidates.length; hx++) {
- var deltaX = horizontalCandidates[hx];
- if (Math.abs(deltaX) <= tolerance && (bestDx === null || Math.abs(deltaX) < Math.abs(bestDx))) {
- bestDx = deltaX;
- }
- }
- }
- if (rangesNearOrOverlap(movedLeft, movedRight, otherLeft, otherRight, tolerance)) {
- var verticalCandidates = [
- otherTop - movedBottom,
- otherBottom - movedTop,
- otherTop - movedTop,
- otherBottom - movedBottom
- ];
- for (var vy = 0; vy < verticalCandidates.length; vy++) {
- var deltaY = verticalCandidates[vy];
- if (Math.abs(deltaY) <= tolerance && (bestDy === null || Math.abs(deltaY) < Math.abs(bestDy))) {
- bestDy = deltaY;
- }
- }
- }
- }
- }
- return {
- dx: bestDx == null ? 0 : bestDx,
- dy: bestDy == null ? 0 : bestDy
- };
- },
- collectResizeSnap: function (rect, handle, excludeIds) {
- if (!this.doc || !rect) {
- return null;
- }
- var tolerance = this.snapToleranceWorld();
- var left = rect.x;
- var right = rect.x + rect.width;
- var top = rect.y;
- var bottom = rect.y + rect.height;
- var bestLeft = null;
- var bestRight = null;
- var bestTop = null;
- var bestBottom = null;
- function pickBest(current, candidate) {
- if (candidate == null) {
- return current;
- }
- if (current == null || Math.abs(candidate) < Math.abs(current)) {
- return candidate;
- }
- return current;
- }
- if (handle.indexOf('w') >= 0) {
- bestLeft = pickBest(bestLeft, -left);
- }
- if (handle.indexOf('e') >= 0) {
- bestRight = pickBest(bestRight, this.doc.canvasWidth - right);
- }
- if (handle.indexOf('n') >= 0) {
- bestTop = pickBest(bestTop, -top);
- }
- if (handle.indexOf('s') >= 0) {
- bestBottom = pickBest(bestBottom, this.doc.canvasHeight - bottom);
- }
- var elements = this.querySpatialCandidates(rect, tolerance, excludeIds);
- for (var i = 0; i < elements.length; i++) {
- var other = elements[i];
- var otherLeft = other.x;
- var otherRight = other.x + other.width;
- var otherTop = other.y;
- var otherBottom = other.y + other.height;
- if (rangesNearOrOverlap(top, bottom, otherTop, otherBottom, tolerance)) {
- if (handle.indexOf('w') >= 0) {
- bestLeft = pickBest(bestLeft, otherLeft - left);
- bestLeft = pickBest(bestLeft, otherRight - left);
- }
- if (handle.indexOf('e') >= 0) {
- bestRight = pickBest(bestRight, otherLeft - right);
- bestRight = pickBest(bestRight, otherRight - right);
- }
- }
- if (rangesNearOrOverlap(left, right, otherLeft, otherRight, tolerance)) {
- if (handle.indexOf('n') >= 0) {
- bestTop = pickBest(bestTop, otherTop - top);
- bestTop = pickBest(bestTop, otherBottom - top);
- }
- if (handle.indexOf('s') >= 0) {
- bestBottom = pickBest(bestBottom, otherTop - bottom);
- bestBottom = pickBest(bestBottom, otherBottom - bottom);
- }
- }
- }
- if (bestLeft != null && Math.abs(bestLeft) > tolerance) {
- bestLeft = null;
- }
- if (bestRight != null && Math.abs(bestRight) > tolerance) {
- bestRight = null;
- }
- if (bestTop != null && Math.abs(bestTop) > tolerance) {
- bestTop = null;
- }
- if (bestBottom != null && Math.abs(bestBottom) > tolerance) {
- bestBottom = null;
- }
- return {
- left: bestLeft,
- right: bestRight,
- top: bestTop,
- bottom: bestBottom
- };
- },
- hitTestElement: function (point) {
- if (!this.doc) {
- return null;
- }
- var candidates = this.querySpatialCandidates({
- x: point.x,
- y: point.y,
- width: 0,
- height: 0
- }, 0, []);
- if (!candidates.length) {
- return null;
- }
- var candidateMap = {};
- for (var c = 0; c < candidates.length; c++) {
- candidateMap[candidates[c].id] = true;
- }
- var elements = this.doc.elements || [];
- for (var i = elements.length - 1; i >= 0; i--) {
- var element = elements[i];
- if (!candidateMap[element.id]) {
- continue;
- }
- if (point.x >= element.x && point.x <= element.x + element.width
- && point.y >= element.y && point.y <= element.y + element.height) {
- return element;
- }
- }
- return null;
- },
- getHandlePositions: function (element) {
- var x = element.x;
- var y = element.y;
- var w = element.width;
- var h = element.height;
- var cx = x + w / 2;
- var cy = y + h / 2;
- return {
- nw: { x: x, y: y },
- n: { x: cx, y: y },
- ne: { x: x + w, y: y },
- e: { x: x + w, y: cy },
- se: { x: x + w, y: y + h },
- s: { x: cx, y: y + h },
- sw: { x: x, y: y + h },
- w: { x: x, y: cy }
- };
- },
- getResizeHandleAt: function (point, element) {
- var handlePositions = this.getHandlePositions(element);
- var baseTolerance = HANDLE_SCREEN_SIZE / this.camera.scale;
- var sizeLimitedTolerance = Math.max(Math.min(element.width, element.height) / 4, 3 / this.camera.scale);
- var tolerance = Math.min(baseTolerance, sizeLimitedTolerance);
- var bestHandle = '';
- var bestDistance = Infinity;
- for (var key in handlePositions) {
- if (!handlePositions.hasOwnProperty(key)) {
- continue;
- }
- var pos = handlePositions[key];
- var dx = Math.abs(point.x - pos.x);
- var dy = Math.abs(point.y - pos.y);
- if (dx <= tolerance && dy <= tolerance) {
- var distance = dx + dy;
- if (distance < bestDistance) {
- bestDistance = distance;
- bestHandle = key;
- }
- }
- }
- return bestHandle;
- },
- cursorForHandle: function (handle) {
- if (handle === 'nw' || handle === 'se') {
- return 'nwse-resize';
- }
- if (handle === 'ne' || handle === 'sw') {
- return 'nesw-resize';
- }
- if (handle === 'n' || handle === 's') {
- return 'ns-resize';
- }
- if (handle === 'e' || handle === 'w') {
- return 'ew-resize';
- }
- return 'default';
- },
- updateCursor: function () {
- if (!this.pixiApp) {
- return;
- }
- var cursor = 'default';
- if (this.interactionState) {
- if (this.interactionState.type === 'pan') {
- cursor = 'grabbing';
- } else if (this.interactionState.type === 'draw' || this.interactionState.type === 'marquee') {
- cursor = 'crosshair';
- } else if (this.interactionState.type === 'array') {
- cursor = 'crosshair';
- } else if (this.interactionState.type === 'move') {
- cursor = 'move';
- } else if (this.interactionState.type === 'movePending') {
- cursor = 'grab';
- } else if (this.interactionState.type === 'resize') {
- cursor = this.cursorForHandle(this.interactionState.handle);
- }
- } else if (this.spacePressed || this.activeTool === 'pan') {
- cursor = 'grab';
- } else if (DRAW_TYPES.indexOf(this.activeTool) >= 0 || this.activeTool === 'marquee' || this.activeTool === 'array') {
- cursor = 'crosshair';
- } else if (this.singleSelectedElement) {
- var point = this.lastPointerWorld || null;
- if (point) {
- var handle = this.getResizeHandleAt(point, this.singleSelectedElement);
- cursor = handle ? this.cursorForHandle(handle) : 'default';
- }
- if (cursor === 'default' && this.hoverElementId) {
- cursor = 'move';
- } else if (cursor === 'default') {
- cursor = 'grab';
- }
- } else {
- cursor = this.hoverElementId ? 'move' : 'grab';
- }
- if (cursor !== this.lastCursor) {
- this.lastCursor = cursor;
- this.pixiApp.view.style.cursor = cursor;
- }
- },
- startPan: function (point) {
- this.cancelDeferredStaticRebuild();
- this.cancelPanRefresh();
- if (this.zoomRefreshTimer) {
- window.clearTimeout(this.zoomRefreshTimer);
- this.zoomRefreshTimer = null;
- this.isZooming = false;
- this.pendingViewportRefresh = true;
- }
- this.isPanning = true;
- this.interactionState = {
- type: 'pan',
- startScreen: {
- x: point.screenX,
- y: point.screenY
- },
- startCamera: {
- x: this.camera.x,
- y: this.camera.y
- }
- };
- this.updateCursor();
- },
- startMarquee: function (point, additive) {
- this.cancelDeferredStaticRebuild();
- this.interactionState = {
- type: 'marquee',
- additive: !!additive,
- startWorld: { x: point.x, y: point.y },
- currentWorld: { x: point.x, y: point.y }
- };
- this.updateCursor();
- },
- startDraw: function (point) {
- this.cancelDeferredStaticRebuild();
- this.interactionState = {
- type: 'draw',
- beforeSnapshot: this.snapshotDoc(this.doc),
- elementType: this.activeTool,
- startWorld: { x: point.x, y: point.y },
- rect: { x: point.x, y: point.y, width: 0, height: 0 }
- };
- this.updateCursor();
- },
- startArray: function (point, element) {
- if (!this.canArrayFromElement(element)) {
- this.showMessage('warning', '闃靛垪宸ュ叿褰撳墠鍙敮鎸佽揣鏋躲�丆RN銆佸弻宸ヤ綅鍜� RGV');
- return;
- }
- this.cancelDeferredStaticRebuild();
- this.interactionState = {
- type: 'array',
- beforeSnapshot: this.snapshotDoc(this.doc),
- template: {
- id: element.id,
- type: element.type,
- x: element.x,
- y: element.y,
- width: element.width,
- height: element.height,
- value: element.value
- },
- startWorld: { x: point.x, y: point.y },
- currentWorld: { x: point.x, y: point.y },
- previewItems: []
- };
- this.updateCursor();
- },
- startMove: function (point) {
- var selected = this.getSelectedElements();
- if (!selected.length) {
- return;
- }
- this.cancelDeferredStaticRebuild();
- var baseItems = selected.map(function (item) {
- return {
- id: item.id,
- x: item.x,
- y: item.y,
- width: item.width,
- height: item.height,
- value: item.value,
- type: item.type
- };
- });
- this.interactionState = {
- type: 'movePending',
- beforeSnapshot: this.snapshotDoc(this.doc),
- startScreen: { x: point.screenX, y: point.screenY },
- startWorld: { x: point.x, y: point.y },
- baseItems: baseItems
- };
- this.updateCursor();
- },
- startResize: function (point, element, handle) {
- this.cancelDeferredStaticRebuild();
- this.interactionState = {
- type: 'resize',
- handle: handle,
- elementId: element.id,
- beforeSnapshot: this.snapshotDoc(this.doc),
- baseRect: {
- x: element.x,
- y: element.y,
- width: element.width,
- height: element.height
- }
- };
- this.markStaticSceneDirty();
- this.scheduleRender();
- this.updateCursor();
- },
- onCanvasPointerDown: function (event) {
- if (!this.doc || !this.pixiApp) {
- return;
- }
- if (event.button !== 0 && event.button !== 1) {
- return;
- }
- if (this.pixiApp.view.setPointerCapture && event.pointerId != null) {
- try {
- this.pixiApp.view.setPointerCapture(event.pointerId);
- } catch (ignore) {
- }
- }
- this.currentPointerId = event.pointerId;
- var point = this.pointerToWorld(event);
- this.lastPointerWorld = point;
- this.pointerStatus = this.formatNumber(point.x) + ', ' + this.formatNumber(point.y);
- if (this.spacePressed || this.activeTool === 'pan' || event.button === 1) {
- this.startPan(point);
- return;
- }
- if (DRAW_TYPES.indexOf(this.activeTool) >= 0) {
- this.startDraw(point);
- return;
- }
- if (this.activeTool === 'marquee') {
- this.startMarquee(point, event.shiftKey);
- return;
- }
- if (this.activeTool === 'array') {
- var arrayHit = this.hitTestElement(point);
- var arrayTemplate = arrayHit || this.singleSelectedElement;
- if (arrayHit && this.selectedIds.indexOf(arrayHit.id) < 0) {
- this.setSelectedIds([arrayHit.id]);
- arrayTemplate = arrayHit;
- }
- if (!arrayTemplate) {
- this.showMessage('warning', '璇峰厛閫変腑涓�涓揣鏋舵垨杞ㄩ亾浣滀负闃靛垪妯℃澘');
- return;
- }
- this.startArray(point, arrayTemplate);
- return;
- }
-
- var selected = this.singleSelectedElement;
- var handle = selected ? this.getResizeHandleAt(point, selected) : '';
- if (handle) {
- this.startResize(point, selected, handle);
- return;
- }
-
- var hit = this.hitTestElement(point);
- if (hit) {
- if (event.shiftKey) {
- var index = this.selectedIds.indexOf(hit.id);
- if (index >= 0) {
- var nextIds = this.selectedIds.slice();
- nextIds.splice(index, 1);
- this.setSelectedIds(nextIds);
- } else {
- this.setSelectedIds(this.selectedIds.concat([hit.id]));
- }
- this.scheduleRender();
- return;
- }
- if (this.selectedIds.indexOf(hit.id) < 0) {
- this.setSelectedIds([hit.id]);
- this.scheduleRender();
- }
- this.startMove(point);
- return;
- }
-
- if (this.selectedIds.length) {
- this.setSelectedIds([]);
- this.scheduleRender();
- }
- this.startPan(point);
- },
- onCanvasWheel: function (event) {
- if (!this.pixiApp || !this.doc) {
- return;
- }
- event.preventDefault();
- var point = this.pointerToWorld(event);
- var delta = event.deltaY < 0 ? 1.12 : 0.89;
- var nextScale = clamp(this.camera.scale * delta, 0.06, 4);
- this.camera.scale = nextScale;
- this.camera.x = Math.round(point.screenX - point.x * nextScale);
- this.camera.y = Math.round(point.screenY - point.y * nextScale);
- this.viewZoom = nextScale;
- this.scheduleZoomRefresh();
- this.scheduleRender();
- },
- onWindowPointerMove: function (event) {
- if (!this.pixiApp || !this.doc) {
- return;
- }
- var point = this.pointerToWorld(event);
- this.lastPointerWorld = point;
- var pointerText = this.formatNumber(point.x) + ', ' + this.formatNumber(point.y);
- var now = (window.performance && performance.now) ? performance.now() : Date.now();
- if (pointerText !== this.pointerStatus && (now - this.lastPointerStatusUpdateTs >= POINTER_STATUS_UPDATE_INTERVAL || this.pointerStatus === '--')) {
- this.pointerStatus = pointerText;
- this.lastPointerStatusUpdateTs = now;
- }
- if (!this.interactionState) {
- var hover = this.hitTestElement(point);
- var hoverId = hover ? hover.id : '';
- if (hoverId !== this.hoverElementId) {
- this.hoverElementId = hoverId;
- this.scheduleRender();
- }
- this.updateCursor();
- return;
- }
-
- var state = this.interactionState;
- if (state.type === 'pan') {
- this.camera.x = Math.round(state.startCamera.x + (point.screenX - state.startScreen.x));
- this.camera.y = Math.round(state.startCamera.y + (point.screenY - state.startScreen.y));
- this.scheduleRender();
- return;
- }
-
- if (state.type === 'marquee') {
- state.currentWorld = { x: point.x, y: point.y };
- this.scheduleRender();
- return;
- }
-
- if (state.type === 'draw') {
- var rawRect = buildRectFromPoints(state.startWorld, point);
- var clipped = {
- x: clamp(rawRect.x, 0, this.doc.canvasWidth),
- y: clamp(rawRect.y, 0, this.doc.canvasHeight),
- width: clamp(rawRect.width, 0, this.doc.canvasWidth),
- height: clamp(rawRect.height, 0, this.doc.canvasHeight)
- };
- if (clipped.x + clipped.width > this.doc.canvasWidth) {
- clipped.width = roundCoord(this.doc.canvasWidth - clipped.x);
- }
- if (clipped.y + clipped.height > this.doc.canvasHeight) {
- clipped.height = roundCoord(this.doc.canvasHeight - clipped.y);
- }
- state.rect = clipped;
- this.scheduleRender();
- return;
- }
- if (state.type === 'array') {
- state.currentWorld = { x: point.x, y: point.y };
- state.previewItems = this.buildArrayCopies(state.template, state.startWorld, state.currentWorld);
- this.scheduleRender();
- return;
- }
-
- if (state.type === 'movePending') {
- var dragDistance = Math.max(Math.abs(point.screenX - state.startScreen.x), Math.abs(point.screenY - state.startScreen.y));
- if (dragDistance < DRAG_START_THRESHOLD) {
- return;
- }
- state.type = 'move';
- this.markStaticSceneDirty();
- this.scheduleRender();
- this.updateCursor();
- }
-
- if (state.type === 'move') {
- var dx = point.x - state.startWorld.x;
- var dy = point.y - state.startWorld.y;
- var minDx = -Infinity;
- var maxDx = Infinity;
- var minDy = -Infinity;
- var maxDy = Infinity;
- for (var i = 0; i < state.baseItems.length; i++) {
- var base = state.baseItems[i];
- minDx = Math.max(minDx, -base.x);
- minDy = Math.max(minDy, -base.y);
- maxDx = Math.min(maxDx, this.doc.canvasWidth - (base.x + base.width));
- maxDy = Math.min(maxDy, this.doc.canvasHeight - (base.y + base.height));
- }
- dx = clamp(dx, minDx, maxDx);
- dy = clamp(dy, minDy, maxDy);
- var snapDelta = this.collectMoveSnap(state.baseItems, dx, dy, this.selectedIds.slice());
- dx = clamp(dx + snapDelta.dx, minDx, maxDx);
- dy = clamp(dy + snapDelta.dy, minDy, maxDy);
- for (var j = 0; j < state.baseItems.length; j++) {
- var baseItem = state.baseItems[j];
- var element = this.findElementById(baseItem.id);
- if (!element) {
- continue;
- }
- element.x = roundCoord(baseItem.x + dx);
- element.y = roundCoord(baseItem.y + dy);
- }
- this.scheduleRender();
- return;
- }
-
- if (state.type === 'resize') {
- var target = this.findElementById(state.elementId);
- if (!target) {
- return;
- }
- var baseRect = state.baseRect;
- var left = baseRect.x;
- var right = baseRect.x + baseRect.width;
- var top = baseRect.y;
- var bottom = baseRect.y + baseRect.height;
- if (state.handle.indexOf('w') >= 0) {
- left = clamp(point.x, 0, right - MIN_ELEMENT_SIZE);
- }
- if (state.handle.indexOf('e') >= 0) {
- right = clamp(point.x, left + MIN_ELEMENT_SIZE, this.doc.canvasWidth);
- }
- if (state.handle.indexOf('n') >= 0) {
- top = clamp(point.y, 0, bottom - MIN_ELEMENT_SIZE);
- }
- if (state.handle.indexOf('s') >= 0) {
- bottom = clamp(point.y, top + MIN_ELEMENT_SIZE, this.doc.canvasHeight);
- }
- var snapped = this.collectResizeSnap({
- x: left,
- y: top,
- width: right - left,
- height: bottom - top
- }, state.handle, [target.id]);
- if (snapped) {
- if (state.handle.indexOf('w') >= 0 && snapped.left != null) {
- left = clamp(left + snapped.left, 0, right - MIN_ELEMENT_SIZE);
- }
- if (state.handle.indexOf('e') >= 0 && snapped.right != null) {
- right = clamp(right + snapped.right, left + MIN_ELEMENT_SIZE, this.doc.canvasWidth);
- }
- if (state.handle.indexOf('n') >= 0 && snapped.top != null) {
- top = clamp(top + snapped.top, 0, bottom - MIN_ELEMENT_SIZE);
- }
- if (state.handle.indexOf('s') >= 0 && snapped.bottom != null) {
- bottom = clamp(bottom + snapped.bottom, top + MIN_ELEMENT_SIZE, this.doc.canvasHeight);
- }
- }
- target.x = roundCoord(left);
- target.y = roundCoord(top);
- target.width = roundCoord(right - left);
- target.height = roundCoord(bottom - top);
- this.scheduleRender();
- }
- },
- onWindowPointerUp: function (event) {
- if (!this.interactionState) {
- return;
- }
- if (this.currentPointerId != null && event.pointerId != null && this.currentPointerId !== event.pointerId) {
- return;
- }
- if (this.pixiApp && this.pixiApp.view.releasePointerCapture && event.pointerId != null) {
- try {
- this.pixiApp.view.releasePointerCapture(event.pointerId);
- } catch (ignore) {
- }
- }
- this.currentPointerId = null;
-
- var state = this.interactionState;
- this.interactionState = null;
-
- if (state.type === 'pan') {
- this.updateCursor();
- this.schedulePanRefresh();
- this.scheduleRender();
- return;
- }
-
- if (state.type === 'marquee') {
- var rect = buildRectFromPoints(state.startWorld, state.currentWorld);
- if (rect.width > 2 && rect.height > 2) {
- var matched = (this.doc.elements || []).filter(function (item) {
- return rectIntersects(rect, item);
- }).map(function (item) {
- return item.id;
- });
- this.setSelectedIds(state.additive ? Array.from(new Set(this.selectedIds.concat(matched))) : matched);
- }
- this.scheduleRender();
- return;
- }
-
- if (state.type === 'movePending') {
- this.updateCursor();
- return;
- }
-
- if (state.type === 'draw') {
- var drawRect = state.rect;
- if (drawRect && drawRect.width >= MIN_ELEMENT_SIZE && drawRect.height >= MIN_ELEMENT_SIZE) {
- var newElement = {
- id: nextId(),
- type: state.elementType,
- x: roundCoord(drawRect.x),
- y: roundCoord(drawRect.y),
- width: roundCoord(drawRect.width),
- height: roundCoord(drawRect.height),
- value: ''
- };
- if (this.hasOverlap(newElement, [])) {
- this.showMessage('warning', '鏂板厓绱犱笉鑳戒笌宸叉湁鍏冪礌閲嶅彔');
- } else if (!this.isWithinCanvas(newElement)) {
- this.showMessage('warning', '鏂板厓绱犺秴鍑虹敾甯冭寖鍥�');
- } else {
- this.doc.elements.push(newElement);
- this.selectedIds = [newElement.id];
- this.commitMutation(state.beforeSnapshot);
- this.refreshInspector();
- return;
- }
- }
- this.refreshInspector();
- this.scheduleRender();
- return;
- }
- if (state.type === 'array') {
- var copies = state.previewItems && state.previewItems.length
- ? state.previewItems
- : this.buildArrayCopies(state.template, state.startWorld, state.currentWorld || state.startWorld);
- if (!copies.length) {
- this.scheduleRender();
- return;
- }
- if (!this.canPlaceElements(copies, [])) {
- this.showMessage('warning', '闃靛垪鐢熸垚鍚庝細閲嶅彔鎴栬秴鍑虹敾甯冿紝宸插彇娑�');
- this.scheduleRender();
- return;
- }
- var finalizedCopies = copies.map(function (item) {
- return $.extend({}, item, { id: nextId() });
- });
- var self = this;
- this.runMutation(function () {
- self.doc.elements = self.doc.elements.concat(finalizedCopies);
- self.selectedIds = [finalizedCopies[finalizedCopies.length - 1].id];
- });
- return;
- }
-
- if (state.type === 'move') {
- var movedElements = this.getSelectedElements();
- if (!this.canPlaceElements(movedElements, this.selectedIds.slice())) {
- for (var i = 0; i < state.baseItems.length; i++) {
- var base = state.baseItems[i];
- var element = this.findElementById(base.id);
- if (!element) {
- continue;
- }
- element.x = base.x;
- element.y = base.y;
- }
- this.showMessage('warning', '绉诲姩鍚庝細閲嶅彔鎴栬秴鍑虹敾甯冿紝宸叉仮澶�');
- this.refreshInspector();
- this.scheduleRender();
- return;
- }
- if (!this.commitMutation(state.beforeSnapshot)) {
- this.markStaticSceneDirty();
- this.scheduleRender();
- }
- return;
- }
-
- if (state.type === 'resize') {
- var resized = this.findElementById(state.elementId);
- if (resized) {
- if (!this.isWithinCanvas(resized) || this.hasOverlap(resized, [resized.id])) {
- resized.x = state.baseRect.x;
- resized.y = state.baseRect.y;
- resized.width = state.baseRect.width;
- resized.height = state.baseRect.height;
- this.showMessage('warning', '缂╂斁鍚庝細閲嶅彔鎴栬秴鍑虹敾甯冿紝宸叉仮澶�');
- this.refreshInspector();
- this.scheduleRender();
- return;
- }
- }
- if (!this.commitMutation(state.beforeSnapshot)) {
- this.markStaticSceneDirty();
- this.scheduleRender();
- }
- return;
- }
-
- this.scheduleRender();
- },
- onWindowKeyDown: function (event) {
- if (event.key === ' ' && !isInputLike(event.target)) {
- this.spacePressed = true;
- this.updateCursor();
- event.preventDefault();
- }
- if (!this.doc) {
- return;
- }
- if (isInputLike(event.target)) {
- return;
- }
- var ctrl = event.ctrlKey || event.metaKey;
- if (event.key === 'Delete' || event.key === 'Backspace') {
- event.preventDefault();
- this.deleteSelection();
- return;
- }
- if (ctrl && (event.key === 'z' || event.key === 'Z')) {
- event.preventDefault();
- if (event.shiftKey) {
- this.redo();
- } else {
- this.undo();
- }
- return;
- }
- if (ctrl && (event.key === 'y' || event.key === 'Y')) {
- event.preventDefault();
- this.redo();
- return;
- }
- if (ctrl && (event.key === 'c' || event.key === 'C')) {
- event.preventDefault();
- this.copySelection();
- return;
- }
- if (ctrl && (event.key === 'v' || event.key === 'V')) {
- event.preventDefault();
- this.pasteClipboard();
- return;
- }
- if (event.key === 'Escape') {
- this.interactionState = null;
- this.setSelectedIds([]);
- this.hoverElementId = '';
- this.scheduleRender();
- }
- },
- onWindowKeyUp: function (event) {
- if (event.key === ' ') {
- this.spacePressed = false;
- this.updateCursor();
- }
- },
- onBeforeUnload: function (event) {
- if (!this.isDirty) {
- return;
- }
- event.preventDefault();
- event.returnValue = '';
- }
+ })
+ };
+ },
+ clearDeferredStaticCommit: function () {
+ this.cancelDeferredStaticRebuild();
+ this.pendingStaticCommit = null;
+ },
+ scheduleDeferredStaticRebuild: function () {
+ this.cancelDeferredStaticRebuild();
+ this.deferredStaticRebuildTimer = window.setTimeout(
+ function () {
+ this.deferredStaticRebuildTimer = null;
+ this.pendingStaticCommit = null;
+ this.markStaticSceneDirty();
+ this.scheduleRender();
+ }.bind(this),
+ DEFERRED_STATIC_REBUILD_DELAY
+ );
+ },
+ selectionKey: function (ids) {
+ return (ids || []).slice().sort().join('|');
+ },
+ setSelectedIds: function (ids, options) {
+ options = options || {};
+ var nextIds = (ids || []).filter(Boolean);
+ this.selectedIds = nextIds.slice();
+ if (options.refreshInspector !== false) {
+ this.refreshInspector();
}
- });
+ },
+ setCurrentDoc: function (doc, options) {
+ options = options || {};
+ var normalized = this.normalizeDoc(doc);
+ this.clearFloorTransientState();
+ this.resetRenderLayers();
+ this.clearRenderCaches();
+ this.doc = normalized;
+ this.markSpatialIndexDirty();
+ this.labelCapabilityDirty = true;
+ this.pendingViewportRefresh = false;
+ this.currentLev = normalized.lev;
+ this.floorPickerLev = normalized.lev;
+ this.switchingFloorLev = null;
+ this.loadingFloor = false;
+ this.syncFloorQueryParam(normalized.lev);
+ this.markGridSceneDirty();
+ this.markStaticSceneDirty();
+ this.undoStack = [];
+ this.redoStack = [];
+ this.savedSnapshot =
+ options.savedSnapshot != null ? options.savedSnapshot : this.snapshotDoc(normalized);
+ this.syncDirty();
+ this.refreshInspector();
+ this.refreshLevOptions();
+ this.$nextTick(
+ function () {
+ this.fitContent();
+ this.scheduleRender();
+ }.bind(this)
+ );
+ },
+ replaceDocFromSnapshot: function (snapshot) {
+ if (!snapshot) {
+ return;
+ }
+ try {
+ this.clearFloorTransientState();
+ this.resetRenderLayers();
+ this.clearRenderCaches();
+ this.doc = this.normalizeDoc(JSON.parse(snapshot));
+ this.markSpatialIndexDirty();
+ this.labelCapabilityDirty = true;
+ this.pendingViewportRefresh = false;
+ } catch (e) {
+ this.showMessage('error', '鍘嗗彶璁板綍鎭㈠澶辫触');
+ return;
+ }
+ this.markGridSceneDirty();
+ this.markStaticSceneDirty();
+ this.floorPickerLev = this.doc.lev;
+ this.currentLev = this.doc.lev;
+ this.refreshInspector();
+ this.syncDirty();
+ this.cacheCurrentDraft();
+ this.scheduleRender();
+ },
+ pushUndoSnapshot: function (snapshot) {
+ if (!snapshot) {
+ return;
+ }
+ if (this.undoStack.length > 0 && this.undoStack[this.undoStack.length - 1] === snapshot) {
+ return;
+ }
+ this.undoStack.push(snapshot);
+ if (this.undoStack.length > HISTORY_LIMIT) {
+ this.undoStack.shift();
+ }
+ },
+ commitMutation: function (beforeSnapshot, options) {
+ options = options || {};
+ var afterSnapshot = this.snapshotDoc(this.doc);
+ if (beforeSnapshot === afterSnapshot) {
+ this.scheduleRender();
+ this.refreshInspector();
+ return false;
+ }
+ this.pushUndoSnapshot(beforeSnapshot);
+ this.redoStack = [];
+ this.markSpatialIndexDirty();
+ this.labelCapabilityDirty = true;
+ if (options.staticSceneDirty !== false) {
+ this.clearDeferredStaticCommit();
+ this.markStaticSceneDirty();
+ }
+ this.syncDirty();
+ this.cacheCurrentDraft();
+ this.refreshInspector();
+ this.scheduleRender();
+ return true;
+ },
+ runMutation: function (mutator) {
+ if (!this.doc) {
+ return false;
+ }
+ var beforeSnapshot = this.snapshotDoc(this.doc);
+ mutator();
+ return this.commitMutation(beforeSnapshot);
+ },
+ undo: function () {
+ if (this.undoStack.length === 0 || !this.doc) {
+ return;
+ }
+ var currentSnapshot = this.snapshotDoc(this.doc);
+ var snapshot = this.undoStack.pop();
+ this.redoStack.push(currentSnapshot);
+ this.replaceDocFromSnapshot(snapshot);
+ },
+ redo: function () {
+ if (this.redoStack.length === 0 || !this.doc) {
+ return;
+ }
+ var currentSnapshot = this.snapshotDoc(this.doc);
+ var snapshot = this.redoStack.pop();
+ this.pushUndoSnapshot(currentSnapshot);
+ this.replaceDocFromSnapshot(snapshot);
+ },
+ createLocalBlankDoc: function (lev, width, height, savedSnapshot) {
+ var doc = {
+ lev: toInt(lev, 1),
+ editorMode: FREE_EDITOR_MODE,
+ canvasWidth: Math.max(MIN_ELEMENT_SIZE * 4, toNumber(width, DEFAULT_CANVAS_WIDTH)),
+ canvasHeight: Math.max(MIN_ELEMENT_SIZE * 4, toNumber(height, DEFAULT_CANVAS_HEIGHT)),
+ elements: []
+ };
+ this.setCurrentDoc(doc, {
+ savedSnapshot: savedSnapshot != null ? savedSnapshot : ''
+ });
+ this.cacheCurrentDraft();
+ this.syncDirty();
+ },
+ openBlankDialog: function () {
+ var lev = this.currentLev || 1;
+ this.blankForm = {
+ lev: String(lev),
+ width: String(Math.round(this.doc ? this.doc.canvasWidth : DEFAULT_CANVAS_WIDTH)),
+ height: String(Math.round(this.doc ? this.doc.canvasHeight : DEFAULT_CANVAS_HEIGHT))
+ };
+ this.blankDialogVisible = true;
+ },
+ createBlankMap: function () {
+ var lev = toInt(this.blankForm.lev, 0);
+ var width = toNumber(this.blankForm.width, DEFAULT_CANVAS_WIDTH);
+ var height = toNumber(this.blankForm.height, DEFAULT_CANVAS_HEIGHT);
+ if (lev <= 0) {
+ this.showMessage('warning', '妤煎眰涓嶈兘涓虹┖');
+ return;
+ }
+ if (width <= 0 || height <= 0) {
+ this.showMessage('warning', '鐢诲竷灏哄蹇呴』澶т簬 0');
+ return;
+ }
+ this.blankDialogVisible = false;
+ this.createLocalBlankDoc(lev, width, height, '');
+ },
+ buildTransferPayload: function () {
+ var doc = this.exportDoc(this.doc);
+ return {
+ format: MAP_TRANSFER_FORMAT,
+ exportedAt: new Date().toISOString(),
+ source: {
+ lev: doc.lev,
+ editorMode: doc.editorMode
+ },
+ docs: [doc]
+ };
+ },
+ buildTransferFilename: function (docs) {
+ var levs = (docs || [])
+ .map(function (item) {
+ return toInt(item && item.lev, 0);
+ })
+ .filter(function (lev) {
+ return lev > 0;
+ })
+ .sort(function (a, b) {
+ return a - b;
+ });
+ var scope =
+ levs.length <= 1
+ ? String(levs[0] || this.currentLev || 1) + 'F'
+ : 'all-' + levs.length + '-floors';
+ var now = new Date();
+ return (
+ [
+ 'bas-map',
+ scope,
+ now.getFullYear(),
+ padNumber(now.getMonth() + 1),
+ padNumber(now.getDate()),
+ padNumber(now.getHours()),
+ padNumber(now.getMinutes()),
+ padNumber(now.getSeconds())
+ ].join('-') + '.json'
+ );
+ },
+ requestEditorDoc: function (lev) {
+ return new Promise(function (resolve, reject) {
+ $.ajax({
+ url: baseUrl + '/basMap/editor/' + lev + '/auth',
+ method: 'GET',
+ headers: authHeaders(),
+ success: function (res) {
+ if (!res || res.code !== 200 || !res.data) {
+ reject(new Error(res && res.msg ? res.msg : '鍔犺浇 ' + lev + 'F 鍦板浘澶辫触'));
+ return;
+ }
+ resolve(res.data);
+ },
+ error: function () {
+ reject(new Error('鍔犺浇 ' + lev + 'F 鍦板浘澶辫触'));
+ }
+ });
+ });
+ },
+ collectAllTransferDocs: function () {
+ var self = this;
+ var levMap = {};
+ (this.remoteLevOptions || []).forEach(function (lev) {
+ lev = toInt(lev, 0);
+ if (lev > 0) {
+ levMap[lev] = true;
+ }
+ });
+ Object.keys(this.draftDocs || {}).forEach(function (key) {
+ var lev = toInt(key, 0);
+ if (lev > 0) {
+ levMap[lev] = true;
+ }
+ });
+ if (this.doc && this.doc.lev) {
+ levMap[toInt(this.doc.lev, 0)] = true;
+ }
+ var levs = Object.keys(levMap)
+ .map(function (key) {
+ return toInt(key, 0);
+ })
+ .filter(function (lev) {
+ return lev > 0;
+ })
+ .sort(function (a, b) {
+ return a - b;
+ });
+ if (!levs.length) {
+ return Promise.resolve([]);
+ }
+ return Promise.all(
+ levs.map(function (lev) {
+ if (self.doc && self.doc.lev === lev) {
+ return Promise.resolve(self.exportDoc(self.doc));
+ }
+ if (self.draftDocs[lev] && self.draftDocs[lev].doc) {
+ return Promise.resolve(self.exportDoc(self.draftDocs[lev].doc));
+ }
+ return self.requestEditorDoc(lev).then(function (doc) {
+ return self.normalizeDoc(doc);
+ });
+ })
+ );
+ },
+ exportMapPackage: function () {
+ var self = this;
+ if (!this.doc && (!this.remoteLevOptions || !this.remoteLevOptions.length)) {
+ this.showMessage('warning', '褰撳墠娌℃湁鍙鍑虹殑鍦板浘');
+ return;
+ }
+ this.collectAllTransferDocs()
+ .then(function (docs) {
+ if (!docs || !docs.length) {
+ self.showMessage('warning', '褰撳墠娌℃湁鍙鍑虹殑鍦板浘');
+ return;
+ }
+ var payload = {
+ format: MAP_TRANSFER_FORMAT,
+ exportedAt: new Date().toISOString(),
+ source: {
+ lev: self.currentLev || (docs[0] && docs[0].lev) || 1,
+ editorMode: FREE_EDITOR_MODE
+ },
+ docs: docs.map(function (doc) {
+ return self.exportDoc(doc);
+ })
+ };
+ var blob = new Blob([JSON.stringify(payload, null, 2)], {
+ type: 'application/json;charset=utf-8'
+ });
+ var href = window.URL.createObjectURL(blob);
+ var link = document.createElement('a');
+ link.href = href;
+ link.download = self.buildTransferFilename(payload.docs);
+ document.body.appendChild(link);
+ link.click();
+ document.body.removeChild(link);
+ window.setTimeout(function () {
+ window.URL.revokeObjectURL(href);
+ }, 0);
+ self.showMessage('success', '宸插鍑� ' + payload.docs.length + ' 涓ゼ灞傜殑鍦板浘鍖�');
+ })
+ .catch(function (error) {
+ self.showMessage('error', error && error.message ? error.message : '瀵煎嚭鍦板浘澶辫触');
+ });
+ },
+ triggerImportMap: function () {
+ if (this.$refs.mapImportInput) {
+ this.$refs.mapImportInput.value = '';
+ this.$refs.mapImportInput.click();
+ }
+ },
+ parseTransferPackage: function (raw) {
+ if (!raw) {
+ return null;
+ }
+ if (raw.format === MAP_TRANSFER_FORMAT && Array.isArray(raw.docs) && raw.docs.length) {
+ return {
+ docs: raw.docs,
+ activeLev: toInt(raw.source && raw.source.lev, 0)
+ };
+ }
+ if (
+ (raw.format === 'bas-map-editor-transfer-v1' || raw.format === MAP_TRANSFER_FORMAT) &&
+ raw.doc
+ ) {
+ return {
+ docs: [raw.doc],
+ activeLev: toInt(raw.source && raw.source.lev, 0)
+ };
+ }
+ if (raw.editorMode === FREE_EDITOR_MODE && Array.isArray(raw.elements)) {
+ return {
+ docs: [raw],
+ activeLev: toInt(raw.lev, 0)
+ };
+ }
+ return null;
+ },
+ importMapPackage: function (payload, options) {
+ options = options || {};
+ if (!payload || !Array.isArray(payload.docs) || !payload.docs.length) {
+ this.showMessage('error', '瀵煎叆鏂囦欢鏍煎紡涓嶆纭�');
+ return;
+ }
+ if (this.isDirty && options.skipConfirm !== true) {
+ if (!window.confirm('瀵煎叆鍦板浘浼氭浛鎹㈠綋鍓嶇紪杈戞�佹湭淇濆瓨鍐呭锛屾槸鍚︾户缁紵')) {
+ return;
+ }
+ }
+ if (this.doc) {
+ this.cacheCurrentDraft();
+ }
+ var self = this;
+ var normalizedDocs = payload.docs
+ .map(function (item) {
+ return self.normalizeDoc(item);
+ })
+ .sort(function (a, b) {
+ return toInt(a.lev, 0) - toInt(b.lev, 0);
+ });
+ normalizedDocs.forEach(function (doc) {
+ self.setDraftDocEntry(doc.lev, doc, '');
+ });
+ var activeLev = toInt(payload.activeLev, 0);
+ var targetDoc = normalizedDocs[0];
+ for (var i = 0; i < normalizedDocs.length; i++) {
+ if (normalizedDocs[i].lev === activeLev) {
+ targetDoc = normalizedDocs[i];
+ break;
+ }
+ }
+ this.refreshLevOptions();
+ this.floorPickerLev = targetDoc.lev;
+ this.setCurrentDoc(targetDoc, { savedSnapshot: '' });
+ if (normalizedDocs.length > 1) {
+ this.showMessage(
+ 'success',
+ '鍦板浘鍖呭凡瀵煎叆 ' + normalizedDocs.length + ' 涓ゼ灞傦紝鍙偣鍑烩�滀繚瀛樺叏閮ㄦゼ灞傗�濊惤搴�'
+ );
+ return;
+ }
+ this.showMessage('success', '鍦板浘鍖呭凡瀵煎叆锛屼繚瀛樺悗鎵嶄細瑕嗙洊杩愯鍦板浘');
+ },
+ handleImportMap: function (event) {
+ var file = event && event.target && event.target.files ? event.target.files[0] : null;
+ if (!file) {
+ return;
+ }
+ var self = this;
+ var reader = new FileReader();
+ reader.onload = function (loadEvent) {
+ try {
+ var text = loadEvent && loadEvent.target ? loadEvent.target.result : '';
+ var raw = JSON.parse(text || '{}');
+ var payload = self.parseTransferPackage(raw);
+ self.importMapPackage(payload);
+ } catch (e) {
+ self.showMessage('error', '鍦板浘鏂囦欢瑙f瀽澶辫触');
+ }
+ };
+ reader.onerror = function () {
+ self.showMessage('error', '鍦板浘鏂囦欢璇诲彇澶辫触');
+ };
+ reader.readAsText(file, 'utf-8');
+ },
+ triggerImportExcel: function () {
+ if (this.$refs.importInput) {
+ this.$refs.importInput.value = '';
+ this.$refs.importInput.click();
+ }
+ },
+ handleImportExcel: function (event) {
+ var self = this;
+ var file = event && event.target && event.target.files ? event.target.files[0] : null;
+ if (!file) {
+ return;
+ }
+ var formData = new FormData();
+ formData.append('file', file);
+ $.ajax({
+ url: baseUrl + '/basMap/editor/importExcel/auth',
+ method: 'POST',
+ headers: authHeaders(),
+ data: formData,
+ processData: false,
+ contentType: false,
+ success: function (res) {
+ if (!res || res.code !== 200 || !Array.isArray(res.data) || res.data.length === 0) {
+ self.showMessage('error', res && res.msg ? res.msg : 'Excel 瀵煎叆澶辫触');
+ return;
+ }
+ res.data.forEach(function (item) {
+ var doc = self.normalizeDoc(item);
+ self.setDraftDocEntry(doc.lev, doc, '');
+ });
+ self.refreshLevOptions();
+ self.floorPickerLev = toInt(res.data[0].lev, 0);
+ self.setCurrentDoc(res.data[0], { savedSnapshot: '' });
+ self.showMessage('success', 'Excel 宸插鍏ュ埌缂栬緫鍣紝淇濆瓨鍚庢墠浼氳鐩栬繍琛屽湴鍥�');
+ },
+ error: function () {
+ self.showMessage('error', 'Excel 瀵煎叆澶辫触');
+ }
+ });
+ },
+ handleFloorChange: function (lev) {
+ lev = toInt(lev, 0);
+ if (lev <= 0) {
+ return;
+ }
+ this.floorPickerLev = lev;
+ if (this.doc && this.doc.lev === lev && !this.loadingFloor) {
+ this.switchingFloorLev = null;
+ return;
+ }
+ if (this.doc) {
+ this.cacheCurrentDraft();
+ }
+ this.clearFloorTransientState();
+ this.resetRenderLayers();
+ this.switchingFloorLev = lev;
+ this.markGridSceneDirty();
+ this.markStaticSceneDirty();
+ this.scheduleRender();
+ this.fetchFloor(lev);
+ },
+ loadCurrentFloor: function () {
+ if (!this.currentLev) {
+ this.showMessage('warning', '璇峰厛閫夋嫨妤煎眰');
+ return;
+ }
+ if (
+ this.isDirty &&
+ !window.confirm('閲嶆柊璇诲彇浼氫涪寮冨綋鍓嶆ゼ灞傛湭淇濆瓨鐨勮嚜鐢辩敾甯冪紪杈戯紝鏄惁缁х画锛�')
+ ) {
+ return;
+ }
+ this.removeDraftDocEntry(this.currentLev);
+ this.refreshLevOptions();
+ this.fetchFloor(this.currentLev);
+ },
+ fetchFloor: function (lev) {
+ var self = this;
+ lev = toInt(lev, 0);
+ if (lev <= 0) {
+ return;
+ }
+ var requestSeq = ++this.floorRequestSeq;
+ this.activeFloorRequestSeq = requestSeq;
+ this.loadingFloor = true;
+ this.switchingFloorLev = lev;
+ $.ajax({
+ url: baseUrl + '/basMap/editor/' + lev + '/auth',
+ method: 'GET',
+ headers: authHeaders(),
+ success: function (res) {
+ if (requestSeq !== self.activeFloorRequestSeq) {
+ return;
+ }
+ self.loadingFloor = false;
+ if (!res || res.code !== 200 || !res.data) {
+ self.switchingFloorLev = null;
+ self.floorPickerLev = self.currentLev;
+ self.markGridSceneDirty();
+ self.markStaticSceneDirty();
+ self.scheduleRender();
+ self.showMessage('error', res && res.msg ? res.msg : '鍔犺浇鍦板浘澶辫触');
+ return;
+ }
+ var normalized = self.normalizeDoc(res.data);
+ self.setDraftDocEntry(normalized.lev, normalized, self.snapshotDoc(normalized));
+ self.setCurrentDoc(normalized, {
+ savedSnapshot: self.snapshotDoc(normalized)
+ });
+ },
+ error: function () {
+ if (requestSeq !== self.activeFloorRequestSeq) {
+ return;
+ }
+ self.loadingFloor = false;
+ self.switchingFloorLev = null;
+ self.floorPickerLev = self.currentLev;
+ self.markGridSceneDirty();
+ self.markStaticSceneDirty();
+ self.scheduleRender();
+ self.showMessage('error', '鍔犺浇鍦板浘澶辫触');
+ }
+ });
+ },
+ validateDocBeforeSave: function (doc) {
+ var source = this.normalizeDoc(doc);
+ if (!source || !source.lev) {
+ return '妤煎眰涓嶈兘涓虹┖';
+ }
+ if (toNumber(source.canvasWidth, 0) <= 0 || toNumber(source.canvasHeight, 0) <= 0) {
+ return '鐢诲竷灏哄蹇呴』澶т簬 0';
+ }
+ var elements = source.elements || [];
+ for (var i = 0; i < elements.length; i++) {
+ var element = elements[i];
+ if (element.width <= 0 || element.height <= 0) {
+ return '瀛樺湪灏哄鏃犳晥鐨勫厓绱�';
+ }
+ if (element.x < 0 || element.y < 0) {
+ return '鍏冪礌鍧愭爣涓嶈兘灏忎簬 0';
+ }
+ if (!isRectWithinCanvas(element, source.canvasWidth, source.canvasHeight)) {
+ return '瀛樺湪瓒呭嚭鐢诲竷杈圭晫鐨勫厓绱�: ' + element.id;
+ }
+ if (element.type === 'devp') {
+ var value = safeParseJson(element.value);
+ if (!value || toInt(value.stationId, 0) <= 0 || toInt(value.deviceNo, 0) <= 0) {
+ return '杈撻�佺嚎鍏冪礌蹇呴』閰嶇疆鏈夋晥鐨� stationId 鍜� deviceNo';
+ }
+ }
+ }
+ var overlapId = findDocOverlapId(source);
+ if (overlapId) {
+ return '瀛樺湪閲嶅彔鍏冪礌: ' + overlapId;
+ }
+ return '';
+ },
+ validateBeforeSave: function () {
+ return this.validateDocBeforeSave(this.doc);
+ },
+ requestSaveDoc: function (doc) {
+ return new Promise(function (resolve, reject) {
+ $.ajax({
+ url: baseUrl + '/basMap/editor/save/auth',
+ method: 'POST',
+ headers: $.extend(
+ {
+ 'Content-Type': 'application/json;charset=UTF-8'
+ },
+ authHeaders()
+ ),
+ data: JSON.stringify(doc),
+ success: function (res) {
+ if (!res || res.code !== 200) {
+ reject(new Error(res && res.msg ? res.msg : '淇濆瓨澶辫触'));
+ return;
+ }
+ resolve(res);
+ },
+ error: function () {
+ reject(new Error('淇濆瓨澶辫触'));
+ }
+ });
+ });
+ },
+ collectDirtyDocsForSave: function () {
+ var result = [];
+ var seen = {};
+ if (this.doc && this.doc.lev && this.isDirty) {
+ var currentDoc = this.exportDoc(this.doc);
+ result.push(currentDoc);
+ seen[currentDoc.lev] = true;
+ }
+ var self = this;
+ Object.keys(this.draftDocs || {}).forEach(function (key) {
+ var lev = toInt(key, 0);
+ if (lev <= 0 || seen[lev]) {
+ return;
+ }
+ var entry = self.draftDocs[lev];
+ if (!entry || !entry.doc) {
+ return;
+ }
+ var snapshot = self.snapshotDoc(entry.doc);
+ if (snapshot === (entry.savedSnapshot || '')) {
+ return;
+ }
+ var doc = self.exportDoc(entry.doc);
+ result.push(doc);
+ seen[doc.lev] = true;
+ });
+ result.sort(function (a, b) {
+ return toInt(a.lev, 0) - toInt(b.lev, 0);
+ });
+ return result;
+ },
+ markDocSavedState: function (doc) {
+ var normalized = this.normalizeDoc(doc);
+ var savedSnapshot = this.snapshotDoc(normalized);
+ this.setDraftDocEntry(normalized.lev, normalized, savedSnapshot);
+ if (this.doc && this.doc.lev === normalized.lev) {
+ this.savedSnapshot = savedSnapshot;
+ this.syncDirty();
+ }
+ },
+ saveDoc: function () {
+ var self = this;
+ if (!this.doc) {
+ return;
+ }
+ var error = this.validateBeforeSave();
+ if (error) {
+ this.showMessage('warning', error);
+ return;
+ }
+ this.saving = true;
+ var payload = this.exportDoc(this.doc);
+ this.requestSaveDoc(payload)
+ .then(function () {
+ self.saving = false;
+ self.savedSnapshot = self.snapshotDoc(self.doc);
+ self.syncDirty();
+ self.clearCurrentDraftIfSaved();
+ self.refreshLevOptions();
+ self.showMessage('success', '褰撳墠妤煎眰宸蹭繚瀛樺苟缂栬瘧鍒拌繍琛屽湴鍥�');
+ })
+ .catch(function (error) {
+ self.saving = false;
+ self.showMessage('error', error && error.message ? error.message : '淇濆瓨澶辫触');
+ });
+ },
+ saveAllDocs: function () {
+ var self = this;
+ if (this.saving || this.savingAll) {
+ return;
+ }
+ var docs = this.collectDirtyDocsForSave();
+ if (!docs.length) {
+ this.showMessage('warning', '褰撳墠娌℃湁闇�瑕佷繚瀛樼殑妤煎眰');
+ return;
+ }
+ if (
+ docs.length > 1 &&
+ !window.confirm('灏嗕繚瀛� ' + docs.length + ' 涓ゼ灞傚埌杩愯鍦板浘锛屾槸鍚︾户缁紵')
+ ) {
+ return;
+ }
+ for (var i = 0; i < docs.length; i++) {
+ var error = this.validateDocBeforeSave(docs[i]);
+ if (error) {
+ this.showMessage('warning', docs[i].lev + 'F 淇濆瓨鍓嶆牎楠屽け璐�: ' + error);
+ return;
+ }
+ }
+ this.savingAll = true;
+ var index = 0;
+ var total = docs.length;
+ var next = function () {
+ if (index >= total) {
+ self.savingAll = false;
+ self.refreshLevOptions();
+ self.showMessage('success', '宸蹭繚瀛� ' + total + ' 涓ゼ灞傚埌杩愯鍦板浘');
+ return;
+ }
+ var doc = docs[index++];
+ self
+ .requestSaveDoc(doc)
+ .then(function () {
+ self.markDocSavedState(doc);
+ next();
+ })
+ .catch(function (error) {
+ self.savingAll = false;
+ self.showMessage(
+ 'error',
+ doc.lev + 'F 淇濆瓨澶辫触: ' + (error && error.message ? error.message : '淇濆瓨澶辫触')
+ );
+ });
+ };
+ next();
+ },
+ setTool: function (tool) {
+ this.activeTool = tool;
+ this.updateCursor();
+ },
+ findElementById: function (id) {
+ if (!this.doc || !id) {
+ return null;
+ }
+ var elements = this.doc.elements || [];
+ for (var i = 0; i < elements.length; i++) {
+ if (elements[i].id === id) {
+ return elements[i];
+ }
+ }
+ return null;
+ },
+ getSelectedElements: function () {
+ var self = this;
+ return this.selectedIds
+ .map(function (id) {
+ return self.findElementById(id);
+ })
+ .filter(Boolean);
+ },
+ refreshInspector: function () {
+ var element = this.singleSelectedElement;
+ if (!this.doc) {
+ this.canvasForm = {
+ width: String(DEFAULT_CANVAS_WIDTH),
+ height: String(DEFAULT_CANVAS_HEIGHT)
+ };
+ this.valueEditorText = '';
+ this.resetDevpForm();
+ this.resetDeviceForm();
+ return;
+ }
+ this.canvasForm = {
+ width: String(Math.round(this.doc.canvasWidth)),
+ height: String(Math.round(this.doc.canvasHeight))
+ };
+ if (!element) {
+ this.geometryForm = { x: '', y: '', width: '', height: '' };
+ this.valueEditorText = '';
+ this.resetDevpForm();
+ this.resetDeviceForm();
+ return;
+ }
+ this.geometryForm = {
+ x: String(this.formatNumber(element.x)),
+ y: String(this.formatNumber(element.y)),
+ width: String(this.formatNumber(element.width)),
+ height: String(this.formatNumber(element.height))
+ };
+ this.valueEditorText = element.value || '';
+ if (element.type === 'devp') {
+ this.loadDevpForm(element.value);
+ } else {
+ this.resetDevpForm();
+ }
+ if (isDeviceConfigType(element.type)) {
+ this.loadDeviceForm(element.type, element.value);
+ } else {
+ this.resetDeviceForm();
+ }
+ this.ensureShelfFillStartValue();
+ },
+ resetDevpForm: function () {
+ this.devpForm = {
+ stationId: '',
+ deviceNo: '',
+ direction: [],
+ isBarcodeStation: false,
+ barcodeIdx: '',
+ backStation: '',
+ backStationDeviceNo: '',
+ isInStation: false,
+ barcodeStation: '',
+ barcodeStationDeviceNo: '',
+ isOutStation: false,
+ runBlockReassign: false,
+ isOutOrder: false,
+ isLiftTransfer: false
+ };
+ },
+ resetDeviceForm: function () {
+ this.deviceForm = {
+ trackId: '',
+ barCodeStart: 0,
+ barCodeEnd: 100000,
+ deviceList: [
+ {
+ valueKey: '',
+ deviceNo: '',
+ progress: 0,
+ deviceLength: '',
+ deviceWidth: ''
+ }
+ ]
+ };
+ },
+ ensureShelfFillStartValue: function () {
+ var element = this.singleSelectedElement;
+ if (!element || !isShelfLikeNodeType(element.type)) {
+ return;
+ }
+ if (
+ !this.shelfFillForm.startValue ||
+ !parseShelfLocationValue(this.shelfFillForm.startValue)
+ ) {
+ this.shelfFillForm.startValue = normalizeValue(element.value || '');
+ }
+ },
+ loadDevpForm: function (value) {
+ this.resetDevpForm();
+ var json = safeParseJson(value);
+ if (!json) {
+ return;
+ }
+ this.devpForm.stationId = json.stationId != null ? String(json.stationId) : '';
+ this.devpForm.deviceNo = json.deviceNo != null ? String(json.deviceNo) : '';
+ this.devpForm.direction = normalizeDirectionList(json.direction);
+ this.devpForm.isBarcodeStation = boolFlag(json.isBarcodeStation);
+ this.devpForm.barcodeIdx = json.barcodeIdx != null ? String(json.barcodeIdx) : '';
+ this.devpForm.backStation = json.backStation != null ? String(json.backStation) : '';
+ this.devpForm.backStationDeviceNo =
+ json.backStationDeviceNo != null ? String(json.backStationDeviceNo) : '';
+ this.devpForm.isInStation = boolFlag(json.isInStation);
+ this.devpForm.barcodeStation =
+ json.barcodeStation != null ? String(json.barcodeStation) : '';
+ this.devpForm.barcodeStationDeviceNo =
+ json.barcodeStationDeviceNo != null ? String(json.barcodeStationDeviceNo) : '';
+ this.devpForm.isOutStation = boolFlag(json.isOutStation);
+ this.devpForm.runBlockReassign = boolFlag(json.runBlockReassign);
+ this.devpForm.isOutOrder = boolFlag(json.isOutOrder);
+ this.devpForm.isLiftTransfer = boolFlag(json.isLiftTransfer);
+ },
+ getDeviceConfigLabel: function (type) {
+ var meta = getTypeMeta(type);
+ return meta.label + '鍙傛暟';
+ },
+ getDeviceConfigKeyLabel: function (type, valueKey) {
+ if (valueKey === 'crnNo') {
+ return 'crnNo';
+ }
+ if (valueKey === 'rgvNo') {
+ return 'rgvNo';
+ }
+ return type === 'rgv' ? 'deviceNo / rgvNo' : 'deviceNo / crnNo';
+ },
+ getAutoTrackDeviceBox: function () {
+ var element = this.singleSelectedDeviceElement;
+ if (!element || !G || !G.getAutoTrackDeviceBox) {
+ return null;
+ }
+ return G.getAutoTrackDeviceBox(element);
+ },
+ getDeviceLengthPlaceholder: function () {
+ var box = this.getAutoTrackDeviceBox();
+ return box && box.along ? '榛樿: ' + box.along : '';
+ },
+ getDeviceWidthPlaceholder: function () {
+ var box = this.getAutoTrackDeviceBox();
+ return box && box.across ? '榛樿: ' + box.across : '';
+ },
+ loadDeviceForm: function (type, value) {
+ this.resetDeviceForm();
+ if (!isDeviceConfigType(type)) {
+ return;
+ }
+ var json = safeParseJson(value) || {};
+ this.deviceForm = {
+ trackId: '',
+ barCodeStart: 0,
+ barCodeEnd: 100000,
+ deviceList: [
+ {
+ valueKey: '',
+ deviceNo: '',
+ progress: 0,
+ deviceLength: '',
+ deviceWidth: ''
+ }
+ ],
+ ...json
+ };
+ var trackId = toInt(this.deviceForm.trackId, 0);
+ if (trackId <= 0) {
+ this.deviceForm.trackId = String(this.getNextDeviceTrackId(this.singleSelectedElement));
+ } else {
+ this.deviceForm.trackId = String(trackId);
+ }
+ this.deviceForm.barCodeStart = toInt(this.deviceForm.barCodeStart, 0);
+ this.deviceForm.barCodeEnd = toInt(this.deviceForm.barCodeEnd, 100000);
+ },
+ getNextDeviceTrackId: function (excludeElement) {
+ if (!this.doc || !Array.isArray(this.doc.elements)) {
+ return 1;
+ }
+ var excludeId = excludeElement && excludeElement.id ? String(excludeElement.id) : '';
+ var maxValue = 0;
+ for (var i = 0; i < this.doc.elements.length; i++) {
+ var item = this.doc.elements[i];
+ if (!item || (excludeId && item.id === excludeId) || !isDeviceConfigType(item.type)) {
+ continue;
+ }
+ var json = safeParseJson(item.value);
+ if (!json) {
+ continue;
+ }
+ var trackId = toInt(json.trackId, 0);
+ if (trackId > maxValue) {
+ maxValue = trackId;
+ }
+ }
+ return Math.max(1, maxValue + 1);
+ },
+ isDevpDirectionActive: function (directionKey) {
+ return this.devpForm.direction.indexOf(directionKey) >= 0;
+ },
+ toggleDevpDirection: function (directionKey) {
+ if (!directionKey) {
+ return;
+ }
+ var next = this.devpForm.direction.slice();
+ var index = next.indexOf(directionKey);
+ if (index >= 0) {
+ next.splice(index, 1);
+ } else {
+ next.push(directionKey);
+ }
+ this.devpForm.direction = DEVP_DIRECTION_OPTIONS.map(function (item) {
+ return item.key;
+ }).filter(function (item) {
+ return next.indexOf(item) >= 0;
+ });
+ },
+ applyCanvasSize: function () {
+ var self = this;
+ if (!this.doc) {
+ return;
+ }
+ var width = toNumber(this.canvasForm.width, 0);
+ var height = toNumber(this.canvasForm.height, 0);
+ if (width <= 0 || height <= 0) {
+ this.showMessage('warning', '鐢诲竷灏哄蹇呴』澶т簬 0');
+ return;
+ }
+ var bounds = this.getElementBounds(
+ (this.doc.elements || []).map(function (item) {
+ return item.id;
+ })
+ );
+ if (bounds && (width < bounds.x + bounds.width || height < bounds.y + bounds.height)) {
+ this.showMessage('warning', '鐢诲竷涓嶈兘灏忎簬褰撳墠鍏冪礌鍗犵敤鑼冨洿');
+ return;
+ }
+ this.runMutation(function () {
+ self.doc.canvasWidth = roundCoord(width);
+ self.doc.canvasHeight = roundCoord(height);
+ });
+ },
+ applyGeometry: function () {
+ var self = this;
+ var element = this.singleSelectedElement;
+ if (!element) {
+ return;
+ }
+ var next = {
+ x: roundCoord(Math.max(0, toNumber(this.geometryForm.x, element.x))),
+ y: roundCoord(Math.max(0, toNumber(this.geometryForm.y, element.y))),
+ width: roundCoord(
+ Math.max(MIN_ELEMENT_SIZE, toNumber(this.geometryForm.width, element.width))
+ ),
+ height: roundCoord(
+ Math.max(MIN_ELEMENT_SIZE, toNumber(this.geometryForm.height, element.height))
+ )
+ };
+ if (!this.isWithinCanvas(next)) {
+ this.showMessage('warning', '鍑犱綍灞炴�ц秴鍑哄綋鍓嶇敾甯冭寖鍥�');
+ return;
+ }
+ var preview = deepClone(element);
+ preview.x = next.x;
+ preview.y = next.y;
+ preview.width = next.width;
+ preview.height = next.height;
+ if (this.hasOverlap(preview, [preview.id])) {
+ this.showMessage('warning', '璋冩暣鍚庝細涓庡叾浠栧厓绱犻噸鍙�');
+ return;
+ }
+ this.runMutation(function () {
+ element.x = next.x;
+ element.y = next.y;
+ element.width = next.width;
+ element.height = next.height;
+ });
+ },
+ applyRawValue: function () {
+ var self = this;
+ var element = this.singleSelectedElement;
+ if (!element || element.type === 'devp') {
+ return;
+ }
+ this.runMutation(function () {
+ element.value = normalizeValue(self.valueEditorText);
+ });
+ },
+ addDeviceForm: function () {
+ this.deviceForm.deviceList.push({
+ valueKey: '',
+ deviceNo: '',
+ progress: 0,
+ deviceLength: '',
+ deviceWidth: ''
+ });
+ },
+ applyDeviceForm: function () {
+ var self = this;
+ var element = this.singleSelectedDeviceElement;
+ if (!element) {
+ return;
+ }
+ var trackId = toInt(this.deviceForm.trackId, 0);
+ if (trackId <= 0) {
+ this.showMessage('warning', '杞ㄩ亾ID蹇呴』澶т簬 0');
+ return;
+ }
+ if (
+ !this.deviceForm.deviceList ||
+ this.deviceForm.deviceList.length === 0 ||
+ this.deviceForm.deviceList.some((item) => item.deviceNo === '')
+ ) {
+ this.showMessage('warning', '璁惧鍒楄〃涓嶈兘涓虹┖');
+ return;
+ }
+ var valueKey = pickDeviceValueKey(element.type);
+ this.runMutation(function () {
+ var payload = safeParseJson(element.value) || {};
+ delete payload.deviceNo;
+ delete payload.crnNo;
+ delete payload.rgvNo;
+ self.deviceForm.deviceList.forEach((item) => {
+ item.valueKey = valueKey;
+ // 鍏佽閫氳繃灞炴�ч潰鏉胯鐩栭粯璁よ澶囧儚绱犲昂瀵革紙娌胯建閬�/鍨傜洿杞ㄩ亾锛�
+ var deviceLength = toInt(item.deviceLength, 0);
+ var deviceWidth = toInt(item.deviceWidth, 0);
+ if (deviceLength > 0) {
+ item.deviceLength = String(deviceLength);
+ } else {
+ delete item.deviceLength;
+ }
+ if (deviceWidth > 0) {
+ item.deviceWidth = String(deviceWidth);
+ } else {
+ delete item.deviceWidth;
+ }
+ });
+ self.deviceForm.trackId = String(trackId);
+ self.deviceForm.barCodeStart = toInt(self.deviceForm.barCodeStart, 0);
+ self.deviceForm.barCodeEnd = toInt(self.deviceForm.barCodeEnd, 100000);
+ element.value = JSON.stringify(self.deviceForm);
+ self.valueEditorText = element.value;
+ });
+ },
+ applyDevpForm: function () {
+ var self = this;
+ var element = this.singleSelectedElement;
+ if (!element || element.type !== 'devp') {
+ return;
+ }
+ var stationId = toInt(this.devpForm.stationId, 0);
+ var deviceNo = toInt(this.devpForm.deviceNo, 0);
+ if (stationId <= 0 || deviceNo <= 0) {
+ this.showMessage('warning', '绔欏彿鍜� PLC 缂栧彿蹇呴』澶т簬 0');
+ return;
+ }
+ var payload = {
+ stationId: stationId,
+ deviceNo: deviceNo
+ };
+ var directionList = normalizeDirectionList(this.devpForm.direction);
+ if (directionList.length > 0) {
+ payload.direction = directionList;
+ }
+ var barcodeIdx = this.devpForm.barcodeIdx === '' ? 0 : toInt(this.devpForm.barcodeIdx, 0);
+ var backStation =
+ this.devpForm.backStation === '' ? 0 : toInt(this.devpForm.backStation, 0);
+ var backStationDeviceNo =
+ this.devpForm.backStationDeviceNo === ''
+ ? 0
+ : toInt(this.devpForm.backStationDeviceNo, 0);
+ var barcodeStation =
+ this.devpForm.barcodeStation === '' ? 0 : toInt(this.devpForm.barcodeStation, 0);
+ var barcodeStationDeviceNo =
+ this.devpForm.barcodeStationDeviceNo === ''
+ ? 0
+ : toInt(this.devpForm.barcodeStationDeviceNo, 0);
+ if (this.devpForm.isInStation && (barcodeStation <= 0 || barcodeStationDeviceNo <= 0)) {
+ this.showMessage('warning', '鍏ョ珯鐐瑰繀椤诲~鍐欐潯鐮佺珯鍜屾潯鐮佺珯 PLC 缂栧彿');
+ return;
+ }
+ if (
+ this.devpForm.isBarcodeStation &&
+ (backStation <= 0 || backStationDeviceNo <= 0 || barcodeIdx <= 0)
+ ) {
+ this.showMessage('warning', '鏉$爜绔欏繀椤诲~鍐欐潯鐮佺储寮曘�侀��鍥炵珯鍜岄��鍥炵珯 PLC 缂栧彿');
+ return;
+ }
+ if (this.devpForm.isBarcodeStation) {
+ payload.isBarcodeStation = 1;
+ }
+ if (barcodeIdx > 0) {
+ payload.barcodeIdx = barcodeIdx;
+ }
+ if (backStation > 0) {
+ payload.backStation = backStation;
+ }
+ if (backStationDeviceNo > 0) {
+ payload.backStationDeviceNo = backStationDeviceNo;
+ }
+ if (this.devpForm.isInStation) {
+ payload.isInStation = 1;
+ }
+ if (barcodeStation > 0) {
+ payload.barcodeStation = barcodeStation;
+ }
+ if (barcodeStationDeviceNo > 0) {
+ payload.barcodeStationDeviceNo = barcodeStationDeviceNo;
+ }
+ if (this.devpForm.isOutStation) {
+ payload.isOutStation = 1;
+ }
+ if (this.devpForm.runBlockReassign) {
+ payload.runBlockReassign = 1;
+ }
+ if (this.devpForm.isOutOrder) {
+ payload.isOutOrder = 1;
+ }
+ if (this.devpForm.isLiftTransfer) {
+ payload.isLiftTransfer = 1;
+ }
+ this.runMutation(function () {
+ element.value = JSON.stringify(payload);
+ self.valueEditorText = element.value;
+ });
+ },
+ deleteSelection: function () {
+ var self = this;
+ if (!this.doc || this.selectedIds.length === 0) {
+ return;
+ }
+ var ids = this.selectedIds.slice();
+ this.runMutation(function () {
+ self.doc.elements = self.doc.elements.filter(function (item) {
+ return ids.indexOf(item.id) === -1;
+ });
+ self.selectedIds = [];
+ });
+ },
+ copySelection: function () {
+ var elements = this.getSelectedElements();
+ if (!elements.length) {
+ return;
+ }
+ this.clipboard = deepClone(elements);
+ this.showMessage('success', '宸插鍒� ' + elements.length + ' 涓厓绱�');
+ },
+ getElementListBounds: function (elements) {
+ if (!elements || !elements.length) {
+ return null;
+ }
+ var minX = elements[0].x;
+ var minY = elements[0].y;
+ var maxX = elements[0].x + elements[0].width;
+ var maxY = elements[0].y + elements[0].height;
+ for (var i = 1; i < elements.length; i++) {
+ var element = elements[i];
+ minX = Math.min(minX, element.x);
+ minY = Math.min(minY, element.y);
+ maxX = Math.max(maxX, element.x + element.width);
+ maxY = Math.max(maxY, element.y + element.height);
+ }
+ return {
+ x: minX,
+ y: minY,
+ width: maxX - minX,
+ height: maxY - minY
+ };
+ },
+ getPasteTargetWorld: function () {
+ if (!this.doc) {
+ return { x: 0, y: 0 };
+ }
+ var visible = this.getVisibleCanvasRect
+ ? this.getVisibleCanvasRect()
+ : this.getVisibleWorldRect();
+ var fallback = {
+ x: visible.x + visible.width / 2,
+ y: visible.y + visible.height / 2
+ };
+ if (!this.lastPointerWorld) {
+ return fallback;
+ }
+ return {
+ x: clamp(this.lastPointerWorld.x, 0, this.doc.canvasWidth),
+ y: clamp(this.lastPointerWorld.y, 0, this.doc.canvasHeight),
+ screenX: this.lastPointerWorld.screenX,
+ screenY: this.lastPointerWorld.screenY
+ };
+ },
+ pasteClipboard: function () {
+ var self = this;
+ if (!this.doc || !this.clipboard.length) {
+ return;
+ }
+ var sourceBounds = this.getElementListBounds(this.clipboard);
+ if (!sourceBounds) {
+ return;
+ }
+ var target = this.getPasteTargetWorld();
+ var offsetX = target.x - (sourceBounds.x + sourceBounds.width / 2);
+ var offsetY = target.y - (sourceBounds.y + sourceBounds.height / 2);
+ var minOffsetX = -sourceBounds.x;
+ var maxOffsetX = this.doc.canvasWidth - (sourceBounds.x + sourceBounds.width);
+ var minOffsetY = -sourceBounds.y;
+ var maxOffsetY = this.doc.canvasHeight - (sourceBounds.y + sourceBounds.height);
+ offsetX = clamp(offsetX, minOffsetX, maxOffsetX);
+ offsetY = clamp(offsetY, minOffsetY, maxOffsetY);
+ var copies = deepClone(this.clipboard).map(function (item) {
+ item.id = nextId();
+ item.x = roundCoord(item.x + offsetX);
+ item.y = roundCoord(item.y + offsetY);
+ return item;
+ });
+ if (!this.canPlaceElements(copies, [])) {
+ this.showMessage('warning', '绮樿创鍚庣殑鍏冪礌涓庣幇鏈夊厓绱犻噸鍙犳垨瓒呭嚭鐢诲竷');
+ return;
+ }
+ this.runMutation(function () {
+ self.doc.elements = self.doc.elements.concat(copies);
+ self.selectedIds = copies.map(function (item) {
+ return item.id;
+ });
+ });
+ },
+ canArrayFromElement: function (element) {
+ return !!(element && ARRAY_TEMPLATE_TYPES.indexOf(element.type) >= 0);
+ },
+ getShelfFillSteps: function () {
+ return {
+ row: this.shelfFillForm.rowStep === 'asc' ? 1 : -1,
+ col: this.shelfFillForm.colStep === 'desc' ? -1 : 1
+ };
+ },
+ applyShelfSequenceToArrayCopies: function (template, copies) {
+ if (!template || !isShelfLikeNodeType(template.type) || !copies || !copies.length) {
+ return copies;
+ }
+ var base =
+ parseShelfLocationValue(template.value) ||
+ parseShelfLocationValue(this.shelfFillForm.startValue);
+ if (!base) {
+ return copies;
+ }
+ var steps = this.getShelfFillSteps();
+ var horizontal = Math.abs(copies[0].x - template.x) >= Math.abs(copies[0].y - template.y);
+ var direction = 1;
+ if (horizontal) {
+ direction = copies[0].x >= template.x ? 1 : -1;
+ } else {
+ direction = copies[0].y >= template.y ? 1 : -1;
+ }
+ for (var i = 0; i < copies.length; i++) {
+ var offset = i + 1;
+ var row = base.row;
+ var col = base.col;
+ if (horizontal) {
+ col = base.col + steps.col * direction * offset;
+ } else {
+ row = base.row + steps.row * direction * offset;
+ }
+ copies[i].value = formatShelfLocationValue(row, col);
+ }
+ return copies;
+ },
+ buildShelfGridAssignments: function (elements) {
+ if (!elements || !elements.length) {
+ return null;
+ }
+ var clusterAxis = function (list, axis, sizeKey) {
+ var sorted = list
+ .map(function (item) {
+ return {
+ id: item.id,
+ center: item[axis] + item[sizeKey] / 2,
+ size: item[sizeKey]
+ };
+ })
+ .sort(function (a, b) {
+ return a.center - b.center;
+ });
+ var avgSize =
+ sorted.reduce(function (sum, item) {
+ return sum + item.size;
+ }, 0) / sorted.length;
+ var tolerance = Math.max(6, avgSize * 0.45);
+ var groups = [];
+ for (var i = 0; i < sorted.length; i++) {
+ var current = sorted[i];
+ var last = groups.length ? groups[groups.length - 1] : null;
+ if (!last || Math.abs(current.center - last.center) > tolerance) {
+ groups.push({
+ center: current.center,
+ items: [current]
+ });
+ } else {
+ last.items.push(current);
+ last.center =
+ last.items.reduce(function (sum, item) {
+ return sum + item.center;
+ }, 0) / last.items.length;
+ }
+ }
+ var indexById = {};
+ for (var groupIndex = 0; groupIndex < groups.length; groupIndex++) {
+ for (var itemIndex = 0; itemIndex < groups[groupIndex].items.length; itemIndex++) {
+ indexById[groups[groupIndex].items[itemIndex].id] = groupIndex;
+ }
+ }
+ return indexById;
+ };
+ return {
+ rowById: clusterAxis(elements, 'y', 'height'),
+ colById: clusterAxis(elements, 'x', 'width')
+ };
+ },
+ applyShelfAutoFill: function () {
+ var self = this;
+ var shelves = this.selectedShelfElements.slice();
+ if (!shelves.length) {
+ this.showMessage('warning', '璇峰厛閫変腑鑷冲皯涓�涓揣鏋舵垨缁翠慨绔欏彴');
+ return;
+ }
+ var start = parseShelfLocationValue(this.shelfFillForm.startValue);
+ if (!start) {
+ this.showMessage('warning', '璧峰鍊兼牸寮忓繀椤绘槸 鎺�-鍒楋紝渚嬪 12-1');
+ return;
+ }
+ var grid = this.buildShelfGridAssignments(shelves);
+ if (!grid) {
+ return;
+ }
+ var steps = this.getShelfFillSteps();
+ this.runMutation(function () {
+ shelves.forEach(function (item) {
+ var rowIndex = grid.rowById[item.id] || 0;
+ var colIndex = grid.colById[item.id] || 0;
+ item.value = formatShelfLocationValue(
+ start.row + rowIndex * steps.row,
+ start.col + colIndex * steps.col
+ );
+ });
+ if (self.singleSelectedElement && isShelfLikeNodeType(self.singleSelectedElement.type)) {
+ self.valueEditorText = self.singleSelectedElement.value || '';
+ }
+ });
+ },
+ buildArrayCopies: function (template, startWorld, currentWorld) {
+ if (
+ !this.doc ||
+ !template ||
+ !startWorld ||
+ !currentWorld ||
+ !this.canArrayFromElement(template)
+ ) {
+ return [];
+ }
+ var deltaX = currentWorld.x - startWorld.x;
+ var deltaY = currentWorld.y - startWorld.y;
+ if (Math.abs(deltaX) < COORD_EPSILON && Math.abs(deltaY) < COORD_EPSILON) {
+ return [];
+ }
+ var horizontal = Math.abs(deltaX) >= Math.abs(deltaY);
+ var step = horizontal ? template.width : template.height;
+ if (step <= COORD_EPSILON) {
+ return [];
+ }
+ var direction = (horizontal ? deltaX : deltaY) >= 0 ? 1 : -1;
+ var distance;
+ if (horizontal) {
+ distance =
+ direction > 0
+ ? currentWorld.x - (template.x + template.width)
+ : template.x - currentWorld.x;
+ } else {
+ distance =
+ direction > 0
+ ? currentWorld.y - (template.y + template.height)
+ : template.y - currentWorld.y;
+ }
+ var count = Math.max(0, Math.floor((distance + step * 0.5) / step));
+ if (count <= 0) {
+ return [];
+ }
+ var copies = [];
+ for (var i = 1; i <= count; i++) {
+ copies.push({
+ type: template.type,
+ x: roundCoord(template.x + (horizontal ? direction * template.width * i : 0)),
+ y: roundCoord(template.y + (horizontal ? 0 : direction * template.height * i)),
+ width: template.width,
+ height: template.height,
+ value: template.value
+ });
+ }
+ return this.applyShelfSequenceToArrayCopies(template, copies);
+ },
+ duplicateSelection: function () {
+ this.copySelection();
+ this.pasteClipboard();
+ },
+ getElementBounds: function (ids) {
+ if (!this.doc) {
+ return null;
+ }
+ var elements = ids && ids.length ? this.getSelectedElements() : this.doc.elements || [];
+ if (ids && ids.length) {
+ elements = ids
+ .map(function (id) {
+ return this.findElementById(id);
+ }, this)
+ .filter(Boolean);
+ }
+ if (!elements.length) {
+ return null;
+ }
+ var minX = elements[0].x;
+ var minY = elements[0].y;
+ var maxX = elements[0].x + elements[0].width;
+ var maxY = elements[0].y + elements[0].height;
+ for (var i = 1; i < elements.length; i++) {
+ var element = elements[i];
+ minX = Math.min(minX, element.x);
+ minY = Math.min(minY, element.y);
+ maxX = Math.max(maxX, element.x + element.width);
+ maxY = Math.max(maxY, element.y + element.height);
+ }
+ return {
+ x: minX,
+ y: minY,
+ width: maxX - minX,
+ height: maxY - minY
+ };
+ },
+ fitContent: function () {
+ if (!this.doc || !this.pixiApp) {
+ return;
+ }
+ var contentBounds = this.getElementBounds();
+ if (contentBounds && contentBounds.width > 0 && contentBounds.height > 0) {
+ this.fitRect(contentBounds, this.pixiApp.renderer.width, this.pixiApp.renderer.height);
+ return;
+ }
+ this.fitCanvas();
+ },
+ fitCanvas: function () {
+ if (!this.doc || !this.pixiApp) {
+ return;
+ }
+ var renderer = this.pixiApp.renderer;
+ var target = {
+ x: 0,
+ y: 0,
+ width: Math.max(1, this.doc.canvasWidth),
+ height: Math.max(1, this.doc.canvasHeight)
+ };
+ this.fitRect(target, renderer.width, renderer.height);
+ },
+ fitSelection: function () {
+ if (!this.selectedIds.length || !this.pixiApp) {
+ return;
+ }
+ var bounds = this.getElementBounds(this.selectedIds);
+ if (!bounds) {
+ return;
+ }
+ this.fitRect(bounds, this.pixiApp.renderer.width, this.pixiApp.renderer.height);
+ },
+ fitRect: function (rect, viewportWidth, viewportHeight) {
+ var padding = 80;
+ var scale = Math.min(
+ (viewportWidth - padding * 2) / Math.max(rect.width, 1),
+ (viewportHeight - padding * 2) / Math.max(rect.height, 1)
+ );
+ scale = clamp(scale, 0.06, 4);
+ this.camera.scale = scale;
+ this.camera.x = Math.round((viewportWidth - rect.width * scale) / 2 - rect.x * scale);
+ this.camera.y = Math.round((viewportHeight - rect.height * scale) / 2 - rect.y * scale);
+ this.viewZoom = scale;
+ this.markGridSceneDirty();
+ this.markStaticSceneDirty();
+ this.scheduleRender();
+ },
+ resetView: function () {
+ this.fitCanvas();
+ },
+ getVisibleWorldRect: function () {
+ if (!this.pixiApp) {
+ return {
+ x: 0,
+ y: 0,
+ width: 0,
+ height: 0
+ };
+ }
+ return {
+ x: -this.camera.x / this.camera.scale,
+ y: -this.camera.y / this.camera.scale,
+ width: this.pixiApp.renderer.width / this.camera.scale,
+ height: this.pixiApp.renderer.height / this.camera.scale
+ };
+ },
+ getVisibleCanvasRect: function () {
+ if (!this.doc) {
+ return {
+ x: 0,
+ y: 0,
+ width: 0,
+ height: 0
+ };
+ }
+ var visible = this.getVisibleWorldRect();
+ var left = clamp(visible.x, 0, this.doc.canvasWidth);
+ var top = clamp(visible.y, 0, this.doc.canvasHeight);
+ var right = clamp(visible.x + visible.width, 0, this.doc.canvasWidth);
+ var bottom = clamp(visible.y + visible.height, 0, this.doc.canvasHeight);
+ return {
+ x: left,
+ y: top,
+ width: Math.max(0, right - left),
+ height: Math.max(0, bottom - top)
+ };
+ },
+ getWorldRectWithPadding: function (screenPadding) {
+ if (!this.doc) {
+ return {
+ x: 0,
+ y: 0,
+ width: 0,
+ height: 0
+ };
+ }
+ var visible = this.getVisibleWorldRect();
+ var padding = Math.max(screenPadding / this.camera.scale, 24);
+ var left = Math.max(0, visible.x - padding);
+ var top = Math.max(0, visible.y - padding);
+ var right = Math.min(this.doc.canvasWidth, visible.x + visible.width + padding);
+ var bottom = Math.min(this.doc.canvasHeight, visible.y + visible.height + padding);
+ return {
+ x: left,
+ y: top,
+ width: Math.max(0, right - left),
+ height: Math.max(0, bottom - top)
+ };
+ },
+ worldRectContains: function (outer, inner) {
+ if (!outer || !inner) {
+ return false;
+ }
+ return (
+ inner.x >= outer.x - COORD_EPSILON &&
+ inner.y >= outer.y - COORD_EPSILON &&
+ inner.x + inner.width <= outer.x + outer.width + COORD_EPSILON &&
+ inner.y + inner.height <= outer.y + outer.height + COORD_EPSILON
+ );
+ },
+ getGridRenderKey: function () {
+ var minorStep = this.camera.scale > 1.5 ? 50 : this.camera.scale > 0.45 ? 100 : 200;
+ return minorStep + '|' + Math.round(this.camera.scale * 8) / 8;
+ },
+ getStaticRenderKey: function () {
+ return (
+ (this.camera.scale >= 0.85 ? 'round' : 'flat') +
+ '|' +
+ Math.round(this.camera.scale * 8) / 8
+ );
+ },
+ scheduleRender: function () {
+ if (this.renderQueued) {
+ return;
+ }
+ this.renderQueued = true;
+ window.requestAnimationFrame(
+ function () {
+ this.renderQueued = false;
+ this.renderScene();
+ }.bind(this)
+ );
+ },
+ renderScene: function () {
+ if (!this.pixiApp || !this.doc) {
+ return;
+ }
+ this.mapRoot.position.set(this.camera.x, this.camera.y);
+ this.mapRoot.scale.set(this.camera.scale, this.camera.scale);
+ this.viewZoom = this.camera.scale;
+ var visible = this.getVisibleCanvasRect();
+ var viewportSettled =
+ !this.isZooming &&
+ !this.isPanning &&
+ !(this.interactionState && this.interactionState.type === 'pan');
+ var gridKeyChanged = this.gridRenderKey !== this.getGridRenderKey();
+ if (
+ this.gridSceneDirty ||
+ !this.gridRenderRect ||
+ (viewportSettled && gridKeyChanged) ||
+ (viewportSettled && !this.worldRectContains(this.gridRenderRect, visible))
+ ) {
+ this.renderGrid(this.getWorldRectWithPadding(STATIC_VIEW_PADDING));
+ this.gridSceneDirty = false;
+ }
+ var excludedKey = this.selectionKey(this.getStaticExcludedIds());
+ var staticKeyChanged = this.staticRenderKey !== this.getStaticRenderKey();
+ if (
+ this.staticSceneDirty ||
+ !this.staticRenderRect ||
+ (viewportSettled && staticKeyChanged) ||
+ this.staticExcludedKey !== excludedKey ||
+ (viewportSettled && !this.worldRectContains(this.staticRenderRect, visible))
+ ) {
+ this.renderStaticElements(this.getWorldRectWithPadding(STATIC_VIEW_PADDING), excludedKey);
+ this.staticSceneDirty = false;
+ }
+ this.renderActiveElements();
+ this.renderLabels();
+ this.renderHover();
+ this.renderSelection();
+ this.renderGuide();
+ this.updateCursor();
+ },
+ getStaticExcludedIds: function () {
+ if (!this.interactionState) {
+ return [];
+ }
+ if (this.interactionState.type === 'move' && this.selectedIds.length) {
+ return this.selectedIds.slice();
+ }
+ if (this.interactionState.type === 'resize' && this.interactionState.elementId) {
+ return [this.interactionState.elementId];
+ }
+ return [];
+ },
+ getRenderableElements: function (excludeIds, renderRect) {
+ if (!this.doc) {
+ return [];
+ }
+ var rect = renderRect || this.getWorldRectWithPadding(STATIC_VIEW_PADDING);
+ var candidates = this.querySpatialCandidates(rect, 0, excludeIds);
+ var result = [];
+ for (var i = 0; i < candidates.length; i++) {
+ if (rectIntersects(rect, candidates[i])) {
+ result.push(candidates[i]);
+ }
+ }
+ return result;
+ },
+ renderGrid: function (renderRect) {
+ if (!this.gridLayer || !this.doc) {
+ return;
+ }
+ var visible = renderRect || this.getVisibleWorldRect();
+ var width = this.doc.canvasWidth;
+ var height = this.doc.canvasHeight;
+ var minorStep = this.camera.scale > 1.5 ? 50 : this.camera.scale > 0.45 ? 100 : 200;
+ var majorStep = minorStep * 5;
+ var lineWidth = 1 / this.camera.scale;
+ var xStart = Math.max(0, Math.floor(visible.x / minorStep) * minorStep);
+ var yStart = Math.max(0, Math.floor(visible.y / minorStep) * minorStep);
+ var xEnd = Math.min(width, visible.x + visible.width);
+ var yEnd = Math.min(height, visible.y + visible.height);
+
+ this.gridLayer.clear();
+ this.gridLayer.beginFill(0xfafcff, 1);
+ this.gridLayer.drawRect(0, 0, width, height);
+ this.gridLayer.endFill();
+
+ this.gridLayer.lineStyle(lineWidth, 0xdbe4ee, 1);
+ this.gridLayer.drawRect(0, 0, width, height);
+
+ for (var x = xStart; x <= xEnd; x += minorStep) {
+ var colorX = x % majorStep === 0 ? 0xc9d7e6 : 0xe4ebf3;
+ this.gridLayer.lineStyle(lineWidth, colorX, x % majorStep === 0 ? 0.95 : 0.75);
+ this.gridLayer.moveTo(x, 0);
+ this.gridLayer.lineTo(x, height);
+ }
+ for (var y = yStart; y <= yEnd; y += minorStep) {
+ var colorY = y % majorStep === 0 ? 0xc9d7e6 : 0xe4ebf3;
+ this.gridLayer.lineStyle(lineWidth, colorY, y % majorStep === 0 ? 0.95 : 0.75);
+ this.gridLayer.moveTo(0, y);
+ this.gridLayer.lineTo(width, y);
+ }
+ this.gridRenderRect = {
+ x: visible.x,
+ y: visible.y,
+ width: visible.width,
+ height: visible.height
+ };
+ this.gridRenderKey = this.getGridRenderKey();
+ },
+ drawGridPatch: function (rects, layer) {
+ if (!this.doc || !layer || !rects || !rects.length) {
+ return;
+ }
+ var width = this.doc.canvasWidth;
+ var height = this.doc.canvasHeight;
+ var minorStep = this.camera.scale > 1.5 ? 50 : this.camera.scale > 0.45 ? 100 : 200;
+ var majorStep = minorStep * 5;
+ var lineWidth = 1 / this.camera.scale;
+ for (var i = 0; i < rects.length; i++) {
+ var rect = rects[i];
+ var left = clamp(rect.x - lineWidth, 0, width);
+ var top = clamp(rect.y - lineWidth, 0, height);
+ var right = clamp(rect.x + rect.width + lineWidth, 0, width);
+ var bottom = clamp(rect.y + rect.height + lineWidth, 0, height);
+ if (right <= left || bottom <= top) {
+ continue;
+ }
+ layer.lineStyle(0, 0, 0, 0);
+ layer.beginFill(0xfafcff, 1);
+ layer.drawRect(left, top, right - left, bottom - top);
+ layer.endFill();
+ if (right - left < minorStep || bottom - top < minorStep) {
+ continue;
+ }
+ var xStart = Math.floor(left / minorStep) * minorStep;
+ var yStart = Math.floor(top / minorStep) * minorStep;
+ for (var x = xStart; x <= right; x += minorStep) {
+ if (x < left || x > right) {
+ continue;
+ }
+ var colorX = x % majorStep === 0 ? 0xc9d7e6 : 0xe4ebf3;
+ layer.lineStyle(lineWidth, colorX, x % majorStep === 0 ? 0.95 : 0.75);
+ layer.moveTo(x, top);
+ layer.lineTo(x, bottom);
+ }
+ for (var y = yStart; y <= bottom; y += minorStep) {
+ if (y < top || y > bottom) {
+ continue;
+ }
+ var colorY = y % majorStep === 0 ? 0xc9d7e6 : 0xe4ebf3;
+ layer.lineStyle(lineWidth, colorY, y % majorStep === 0 ? 0.95 : 0.75);
+ layer.moveTo(left, y);
+ layer.lineTo(right, y);
+ }
+ }
+ },
+ drawPatchObjects: function (rects, excludeIds) {
+ if (!rects || !rects.length || !this.patchObjectLayer) {
+ return;
+ }
+ var seen = {};
+ var elements = [];
+ for (var i = 0; i < rects.length; i++) {
+ var candidates = this.querySpatialCandidates(rects[i], 0, excludeIds);
+ for (var j = 0; j < candidates.length; j++) {
+ var item = candidates[j];
+ if (!seen[item.id] && rectIntersects(rects[i], item)) {
+ seen[item.id] = true;
+ elements.push(item);
+ }
+ }
+ }
+ if (!elements.length) {
+ return;
+ }
+ this.drawElementsToLayers(elements, this.patchObjectLayer, this.patchObjectLayer);
+ },
+ drawElementsToLayers: function (elements, trackLayer, nodeLayer) {
+ console.log('drawElementsToLayers');
+ var lineWidth = 2 / this.camera.scale;
+ var buckets = {};
+ for (var i = 0; i < elements.length; i++) {
+ var element = elements[i];
+ var bucketKey =
+ (isShelfLikeNodeType(element.type) ? 'node' : 'track') + ':' + element.type;
+ if (!buckets[bucketKey]) {
+ buckets[bucketKey] = [];
+ }
+ buckets[bucketKey].push(element);
+ }
+ for (var bucketKey in buckets) {
+ if (!buckets.hasOwnProperty(bucketKey)) {
+ continue;
+ }
+ var parts = bucketKey.split(':');
+ var type = parts[1];
+ var meta = getTypeMeta(type);
+ var layer = parts[0] === 'node' ? nodeLayer : trackLayer;
+ var bucket = buckets[bucketKey];
+ for (var j = 0; j < bucket.length; j++) {
+ var item = bucket[j];
+ drawElementByType.call(this, layer, item, type, {
+ line: {
+ width: lineWidth,
+ color: meta.border,
+ alpha: 0.95
+ },
+ fill: {
+ color: meta.fill,
+ alpha: meta.alpha ?? 0.92
+ }
+ });
+ }
+ }
+ },
+ ensureStaticSprite: function (poolName, index) {
+ var pool = poolName === 'node' ? this.staticNodeSpritePool : this.staticTrackSpritePool;
+ var layer = poolName === 'node' ? this.staticNodeSpriteLayer : this.staticTrackSpriteLayer;
+ if (pool[index]) {
+ return pool[index];
+ }
+ var sprite = new PIXI.Sprite(PIXI.Texture.WHITE);
+ sprite.position.set(0, 0);
+ sprite.anchor.set(0, 0);
+ sprite.visible = false;
+ sprite.alpha = 0;
+ layer.addChild(sprite);
+ pool[index] = sprite;
+ return sprite;
+ },
+ hideUnusedStaticSprites: function (pool, fromIndex) {
+ for (var i = fromIndex; i < pool.length; i++) {
+ pool[i].visible = false;
+ pool[i].alpha = 0;
+ pool[i].width = 0;
+ pool[i].height = 0;
+ pool[i].position.set(-99999, -99999);
+ }
+ },
+ pruneStaticSpritePool: function (poolName, keepCount, slack) {
+ var pool = poolName === 'node' ? this.staticNodeSpritePool : this.staticTrackSpritePool;
+ var layer = poolName === 'node' ? this.staticNodeSpriteLayer : this.staticTrackSpriteLayer;
+ var target = Math.max(0, keepCount + Math.max(0, slack || 0));
+ if (!pool || !layer || pool.length <= target) {
+ return;
+ }
+ for (var i = pool.length - 1; i >= target; i--) {
+ var sprite = pool[i];
+ layer.removeChild(sprite);
+ if (sprite && sprite.destroy) {
+ sprite.destroy();
+ }
+ pool.pop();
+ }
+ },
+ drawElementsToSpriteLayers: function (elements) {
+ var trackCount = 0;
+ var nodeCount = 0;
+ for (var i = 0; i < elements.length; i++) {
+ var item = elements[i];
+ var meta = getTypeMeta(item.type);
+ var poolName = isShelfLikeNodeType(item.type) ? 'node' : 'track';
+ var sprite = this.ensureStaticSprite(
+ poolName,
+ poolName === 'node' ? nodeCount : trackCount
+ );
+ sprite.visible = true;
+ sprite.position.set(item.x, item.y);
+ sprite.width = item.width;
+ sprite.height = item.height;
+ sprite.tint = meta.fill;
+ sprite.alpha = meta.alpha ?? 1;
+ if (poolName === 'node') {
+ nodeCount += 1;
+ } else {
+ trackCount += 1;
+ }
+ }
+ this.hideUnusedStaticSprites(this.staticTrackSpritePool, trackCount);
+ this.hideUnusedStaticSprites(this.staticNodeSpritePool, nodeCount);
+ if (this.camera.scale < DENSE_SIMPLIFY_SCALE_THRESHOLD) {
+ this.pruneStaticSpritePool('track', trackCount, STATIC_SPRITE_POOL_SLACK);
+ this.pruneStaticSpritePool('node', nodeCount, STATIC_SPRITE_POOL_SLACK);
+ }
+ },
+ simplifyRenderableElements: function (elements) {
+ if (!elements || elements.length < 2) {
+ return elements || [];
+ }
+ var sorted = elements.slice().sort(function (a, b) {
+ if (a.type !== b.type) {
+ return a.type < b.type ? -1 : 1;
+ }
+ if (Math.abs(a.y - b.y) > COORD_EPSILON) {
+ return a.y - b.y;
+ }
+ if (Math.abs(a.height - b.height) > COORD_EPSILON) {
+ return a.height - b.height;
+ }
+ return a.x - b.x;
+ });
+ var result = [];
+ var current = null;
+ for (var i = 0; i < sorted.length; i++) {
+ var item = sorted[i];
+ if (!current) {
+ current = {
+ type: item.type,
+ x: item.x,
+ y: item.y,
+ width: item.width,
+ height: item.height
+ };
+ continue;
+ }
+ var currentRight = current.x + current.width;
+ var itemRight = item.x + item.width;
+ var sameBand =
+ current.type === item.type &&
+ Math.abs(current.y - item.y) <= 0.5 &&
+ Math.abs(current.height - item.height) <= 0.5;
+ var joinable = item.x <= currentRight + 0.5;
+ if (sameBand && joinable) {
+ current.width = roundCoord(Math.max(currentRight, itemRight) - current.x);
+ } else {
+ result.push(current);
+ current = {
+ type: item.type,
+ x: item.x,
+ y: item.y,
+ width: item.width,
+ height: item.height
+ };
+ }
+ }
+ if (current) {
+ result.push(current);
+ }
+ return result;
+ },
+ renderStaticElements: function (renderRect, excludedKey) {
+ if (!this.doc) {
+ return;
+ }
+ this.trackLayer.clear();
+ this.trackLayer.removeChildren();
+ this.nodeLayer.clear();
+ this.nodeLayer.removeChildren();
+ this.eraseLayer.clear();
+ this.patchObjectLayer.clear();
+ this.patchObjectLayer.removeChildren();
+ var renderableElements = this.getRenderableElements(
+ this.getStaticExcludedIds(),
+ renderRect
+ );
+ // 1. 绛涢�夊嚭 annulus 鍜岃澶囪建閬撳厓绱狅紝鍥犱负 Sprite 涓嶆敮鎸佺粯鍒朵腑蹇冪嚎
+ var annulusElements = [];
+ var deviceElements = [];
+ var normalElements = [];
+ for (var i = 0; i < renderableElements.length; i++) {
+ var el = renderableElements[i];
+ if (el.type === 'annulus') {
+ annulusElements.push(el);
+ } else if (isDeviceConfigType(el.type)) {
+ deviceElements.push(el);
+ } else {
+ normalElements.push(el);
+ }
+ }
+ var hasAnnulus = annulusElements.length > 0;
+ var hasDeviceElements = deviceElements.length > 0;
+
+ var useSpriteMode = this.camera.scale < STATIC_SPRITE_SCALE_THRESHOLD;
+ var shouldSimplify =
+ this.camera.scale < STATIC_SIMPLIFY_SCALE_THRESHOLD ||
+ (this.camera.scale < DENSE_SIMPLIFY_SCALE_THRESHOLD &&
+ renderableElements.length > DENSE_SIMPLIFY_ELEMENT_THRESHOLD);
+
+ // 2. 閲嶆柊璁惧畾灞傜殑鍙鎬�
+ // Sprite灞傦細浠呭湪闇�瑕佷笖鏈夋櫘閫氬厓绱犳椂鏄剧ず
+ this.staticTrackSpriteLayer.visible = useSpriteMode && normalElements.length > 0;
+ this.staticNodeSpriteLayer.visible = useSpriteMode && normalElements.length > 0;
+
+ // Graphics灞傦細濡傛灉涓嶆槸 Sprite 妯″紡锛屾垨鑰呭瓨鍦� annulus 鎴栬澶囪建閬撳厓绱狅紝鍒欏繀椤绘樉绀�
+ this.trackLayer.visible = !useSpriteMode || hasAnnulus || hasDeviceElements;
+ this.nodeLayer.visible = !useSpriteMode;
+
+ if (useSpriteMode) {
+ // 3. 缁樺埗鏅�氬厓绱犲埌 Sprite 灞�
+ if (shouldSimplify) {
+ normalElements = this.simplifyRenderableElements(normalElements);
+ }
+ this.drawElementsToSpriteLayers(normalElements);
+
+ // 4. 缁樺埗 annulus 鍜岃澶囪建閬撳厓绱犲埌 Graphics 灞�
+ // 鍗充娇鍦ㄧ缉鐣ュ浘妯″紡涓嬶紝杩欎簺鍏冪礌涔熷繀椤荤敤 Graphics 缁樺埗浠ヤ繚鎸佷腑蹇冪嚎
+ var graphicsElements = annulusElements.concat(deviceElements);
+ if (graphicsElements.length > 0) {
+ // 娉ㄦ剰锛歞rawElementsToLayers 浼氭牴鎹厓绱犵被鍨嬭嚜鍔ㄥ垎閰嶅埌 trackLayer 鎴� nodeLayer
+ // annulus 鍜岃澶囪建閬撳睘浜� track 绫诲埆锛屼細缁樺埗鍒� trackLayer
+ this.drawElementsToLayers(graphicsElements, this.trackLayer, this.trackLayer);
+ }
+ } else {
+ this.hideUnusedStaticSprites(this.staticTrackSpritePool, 0);
+ this.hideUnusedStaticSprites(this.staticNodeSpritePool, 0);
+ this.drawElementsToLayers(renderableElements, this.trackLayer, this.nodeLayer);
+ }
+ var rect = renderRect || this.getWorldRectWithPadding(STATIC_VIEW_PADDING);
+ this.staticRenderRect = {
+ x: rect.x,
+ y: rect.y,
+ width: rect.width,
+ height: rect.height
+ };
+ this.staticRenderKey = this.getStaticRenderKey();
+ this.staticExcludedKey =
+ excludedKey != null ? excludedKey : this.selectionKey(this.getStaticExcludedIds());
+ this.pendingStaticCommit = null;
+ },
+ renderActiveElements: function () {
+ this.activeLayer.clear();
+ this.activeLayer.removeChildren();
+ this.eraseLayer.clear();
+ this.patchObjectLayer.clear();
+ this.patchObjectLayer.removeChildren();
+ var activeIds = this.getStaticExcludedIds();
+ if (!activeIds.length) {
+ return;
+ }
+ var activeElements = [];
+ for (var idx = 0; idx < activeIds.length; idx++) {
+ var element = this.findElementById(activeIds[idx]);
+ if (element) {
+ activeElements.push(element);
+ }
+ }
+ if (!activeElements.length) {
+ return;
+ }
+ this.drawElementsToLayers(activeElements, this.activeLayer, this.activeLayer);
+ },
+ getLabelText: function (element) {
+ var meta = getTypeMeta(element.type);
+ var value = safeParseJson(element.value);
+ if (element.type === 'devp' && value) {
+ var station = value.stationId != null ? String(value.stationId) : '';
+ var arrows = formatDirectionArrows(value.direction);
+ if (station && arrows) {
+ return element.height > element.width * 1.15
+ ? station + '\n' + arrows
+ : station + ' ' + arrows;
+ }
+ if (station) {
+ return station;
+ }
+ if (arrows) {
+ return arrows;
+ }
+ return meta.shortLabel;
+ }
+ if (
+ (element.type === 'crn' || element.type === 'dualCrn' || element.type === 'rgv') &&
+ value
+ ) {
+ if (value.deviceNo != null) {
+ return meta.shortLabel + ' ' + value.deviceNo;
+ }
+ if (value.crnNo != null) {
+ return meta.shortLabel + ' ' + value.crnNo;
+ }
+ if (value.rgvNo != null) {
+ return meta.shortLabel + ' ' + value.rgvNo;
+ }
+ }
+ if (element.value && element.value.length <= 18 && element.value.indexOf('{') !== 0) {
+ return element.value;
+ }
+ return meta.shortLabel;
+ },
+ ensureLabelSprite: function (index) {
+ if (this.labelPool[index]) {
+ return this.labelPool[index];
+ }
+ var label = new PIXI.Text('', {
+ fontFamily: 'Avenir Next, PingFang SC, Microsoft YaHei, sans-serif',
+ fontSize: 12,
+ fontWeight: '600',
+ fill: 0x223448,
+ align: 'center'
+ });
+ label.anchor.set(0.5);
+ this.labelLayer.addChild(label);
+ this.labelPool[index] = label;
+ return label;
+ },
+ getLabelRenderBudget: function () {
+ if (!this.pixiApp || !this.pixiApp.renderer) {
+ return MIN_LABEL_COUNT;
+ }
+ var renderer = this.pixiApp.renderer;
+ var viewportArea = renderer.width * renderer.height;
+ return clamp(Math.round(viewportArea / 12000), MIN_LABEL_COUNT, MAX_LABEL_COUNT);
+ },
+ getLabelMinScreenWidth: function (text) {
+ var lines = String(text || '').split('\n');
+ var length = 0;
+ for (var i = 0; i < lines.length; i++) {
+ length = Math.max(length, String(lines[i] || '').trim().length);
+ }
+ if (length <= 4) {
+ return 26;
+ }
+ if (length <= 8) {
+ return 40;
+ }
+ if (length <= 12) {
+ return 52;
+ }
+ return 64;
+ },
+ getLabelMinScreenHeight: function (text) {
+ var lines = String(text || '').split('\n');
+ var length = 0;
+ for (var i = 0; i < lines.length; i++) {
+ length = Math.max(length, String(lines[i] || '').trim().length);
+ }
+ var lineHeight = length <= 4 ? 14 : 18;
+ return lineHeight * Math.max(lines.length, 1);
+ },
+ renderLabels: function () {
+ if (!this.doc) {
+ return;
+ }
+ var capability = this.ensureLabelCapability();
+ if (
+ capability.maxWidth * this.camera.scale < ABS_MIN_LABEL_SCREEN_WIDTH ||
+ capability.maxHeight * this.camera.scale < ABS_MIN_LABEL_SCREEN_HEIGHT
+ ) {
+ this.labelLayer.visible = false;
+ return;
+ }
+ if (
+ this.isZooming ||
+ this.isPanning ||
+ this.camera.scale < MIN_LABEL_SCALE ||
+ (this.interactionState &&
+ (this.interactionState.type === 'move' ||
+ this.interactionState.type === 'resize' ||
+ this.interactionState.type === 'pan'))
+ ) {
+ this.labelLayer.visible = false;
+ return;
+ }
+ this.labelLayer.visible = true;
+ var visible = this.getVisibleWorldRect();
+ var elements = this.querySpatialCandidates(visible, 0, []);
+ if (
+ elements.length > DENSE_LABEL_HIDE_ELEMENT_THRESHOLD &&
+ this.camera.scale < DENSE_LABEL_HIDE_SCALE_THRESHOLD
+ ) {
+ this.labelLayer.visible = false;
+ return;
+ }
+ var hasRoomForAnyLabel = false;
+ for (var roomIdx = 0; roomIdx < elements.length; roomIdx++) {
+ var candidate = elements[roomIdx];
+ if (
+ candidate.width * this.camera.scale >= ABS_MIN_LABEL_SCREEN_WIDTH &&
+ candidate.height * this.camera.scale >= ABS_MIN_LABEL_SCREEN_HEIGHT
+ ) {
+ hasRoomForAnyLabel = true;
+ break;
+ }
+ }
+ if (!hasRoomForAnyLabel) {
+ this.labelLayer.visible = false;
+ return;
+ }
+ var visibleElements = [];
+ for (var i = 0; i < elements.length; i++) {
+ var element = elements[i];
+ var text = this.getLabelText(element);
+ if (!text) {
+ continue;
+ }
+ if (!rectIntersects(visible, element)) {
+ continue;
+ }
+ if (
+ element.width * this.camera.scale < this.getLabelMinScreenWidth(text) ||
+ element.height * this.camera.scale < this.getLabelMinScreenHeight(text)
+ ) {
+ continue;
+ }
+ visibleElements.push({
+ element: element,
+ text: text
+ });
+ }
+ visibleElements.sort(function (a, b) {
+ return b.element.width * b.element.height - a.element.width * a.element.height;
+ });
+ var labelBudget = this.getLabelRenderBudget();
+ if (visibleElements.length > labelBudget) {
+ visibleElements = visibleElements.slice(0, labelBudget);
+ }
+ for (var j = 0; j < visibleElements.length; j++) {
+ var item = visibleElements[j].element;
+ var label = this.ensureLabelSprite(j);
+ label.visible = true;
+ label.text = visibleElements[j].text;
+ label.position.set(item.x + item.width / 2, item.y + item.height / 2);
+ label.scale.set(1 / this.camera.scale, 1 / this.camera.scale);
+ label.alpha = this.selectedIds.indexOf(item.id) >= 0 ? 1 : 0.88;
+ }
+ for (var k = visibleElements.length; k < this.labelPool.length; k++) {
+ this.labelPool[k].visible = false;
+ }
+ },
+ renderHover: function () {
+ this.hoverLayer.clear();
+ if (
+ this.interactionState ||
+ !this.hoverElementId ||
+ this.selectedIds.indexOf(this.hoverElementId) >= 0
+ ) {
+ return;
+ }
+ var element = this.findElementById(this.hoverElementId);
+ if (!element) {
+ return;
+ }
+ var lineWidth = 2 / this.camera.scale;
+ this.hoverLayer.lineStyle(lineWidth, 0x2f79d6, 0.95);
+ this.hoverLayer.drawRoundedRect(
+ element.x,
+ element.y,
+ element.width,
+ element.height,
+ Math.max(6 / this.camera.scale, 2)
+ );
+ },
+ renderSelection: function () {
+ console.log('renderSelection');
+ this.selectionLayer.clear();
+ this.selectionLayer.removeChildren();
+ if (
+ !this.selectedIds.length ||
+ (this.interactionState &&
+ (this.interactionState.type === 'move' || this.interactionState.type === 'resize'))
+ ) {
+ return;
+ }
+ var elements = this.getSelectedElements();
+ var lineWidth = 2 / this.camera.scale;
+ for (var i = 0; i < elements.length; i++) {
+ var element = elements[i];
+ drawElementByType.call(this, this.selectionLayer, element, element.type, {
+ line: {
+ width: lineWidth,
+ color: 0x2568b8,
+ alpha: 1
+ },
+ fill: {
+ color: 0x2f79d6,
+ alpha: 0.07
+ }
+ });
+ this.selectionLayer.endFill();
+ }
+ if (elements.length !== 1) {
+ return;
+ }
+ var handleSize = HANDLE_SCREEN_SIZE / this.camera.scale;
+ var handlePositions = this.getHandlePositions(elements[0]);
+ this.selectionLayer.lineStyle(1 / this.camera.scale, 0x1d5ea9, 1);
+ this.selectionLayer.beginFill(0xffffff, 1);
+ for (var key in handlePositions) {
+ if (!handlePositions.hasOwnProperty(key)) {
+ continue;
+ }
+ var pos = handlePositions[key];
+ this.selectionLayer.drawRect(
+ pos.x - handleSize / 2,
+ pos.y - handleSize / 2,
+ handleSize,
+ handleSize
+ );
+ }
+ this.selectionLayer.endFill();
+ },
+ renderGuide: function () {
+ console.log('renderGuide');
+ this.guideLayer.clear();
+ this.guideLayer.removeChildren();
+ if (this.guideText) {
+ this.guideText.visible = false;
+ }
+ if (!this.interactionState) {
+ return;
+ }
+ var state = this.interactionState;
+ if (state.type === 'draw' && state.rect && state.rect.width > 0 && state.rect.height > 0) {
+ var drawMeta = getTypeMeta(state.elementType);
+ drawElementByType.call(this, this.guideLayer, state.rect, state.elementType, {
+ line: {
+ width: 2 / this.camera.scale,
+ color: drawMeta.border,
+ alpha: 0.95
+ },
+ fill: {
+ color: drawMeta.fill,
+ alpha: drawMeta.alpha ?? 0.18
+ }
+ });
+ return;
+ }
+ if (state.type === 'array' && state.template) {
+ var previewItems = state.previewItems || [];
+ var arrayMeta = getTypeMeta(state.template.type);
+ var lineWidth = 2 / this.camera.scale;
+ var templateCenterX = state.template.x + state.template.width / 2;
+ var templateCenterY = state.template.y + state.template.height / 2;
+ this.guideLayer.lineStyle(lineWidth, arrayMeta.border, 0.9);
+ this.guideLayer.moveTo(templateCenterX, templateCenterY);
+ this.guideLayer.lineTo(state.currentWorld.x, state.currentWorld.y);
+ if (!previewItems.length) {
+ return;
+ }
+ this.guideLayer.lineStyle(1 / this.camera.scale, arrayMeta.border, 0.8);
+ this.guideLayer.beginFill(arrayMeta.fill, 0.2);
+ for (var previewIndex = 0; previewIndex < previewItems.length; previewIndex++) {
+ var preview = previewItems[previewIndex];
+ this.guideLayer.drawRoundedRect(
+ preview.x,
+ preview.y,
+ preview.width,
+ preview.height,
+ Math.max(6 / this.camera.scale, 2)
+ );
+ }
+ this.guideLayer.endFill();
+ if (this.guideText) {
+ this.guideText.text = '灏嗙敓鎴� ' + previewItems.length + ' 涓�';
+ this.guideText.position.set(
+ state.currentWorld.x,
+ state.currentWorld.y - 10 / this.camera.scale
+ );
+ this.guideText.scale.set(1 / this.camera.scale);
+ this.guideText.visible = true;
+ }
+ return;
+ }
+ if (state.type === 'marquee') {
+ var rect = buildRectFromPoints(state.startWorld, state.currentWorld);
+ if (rect.width <= 0 || rect.height <= 0) {
+ return;
+ }
+ this.guideLayer.lineStyle(2 / this.camera.scale, 0x2f79d6, 0.92);
+ this.guideLayer.beginFill(0x2f79d6, 0.06);
+ this.guideLayer.drawRect(rect.x, rect.y, rect.width, rect.height);
+ this.guideLayer.endFill();
+ }
+ },
+ pointerToWorld: function (event) {
+ var rect = this.pixiApp.view.getBoundingClientRect();
+ var screenX = event.clientX - rect.left;
+ var screenY = event.clientY - rect.top;
+ return {
+ screenX: screenX,
+ screenY: screenY,
+ x: roundCoord((screenX - this.camera.x) / this.camera.scale),
+ y: roundCoord((screenY - this.camera.y) / this.camera.scale)
+ };
+ },
+ isWithinCanvas: function (rect) {
+ if (!this.doc) {
+ return false;
+ }
+ return (
+ rect.x >= -COORD_EPSILON &&
+ rect.y >= -COORD_EPSILON &&
+ rect.x + rect.width <= this.doc.canvasWidth + COORD_EPSILON &&
+ rect.y + rect.height <= this.doc.canvasHeight + COORD_EPSILON
+ );
+ },
+ canPlaceElements: function (elements, excludeIds) {
+ excludeIds = excludeIds || [];
+ for (var i = 0; i < elements.length; i++) {
+ if (!this.isWithinCanvas(elements[i])) {
+ return false;
+ }
+ if (this.hasOverlap(elements[i], excludeIds.concat([elements[i].id]))) {
+ return false;
+ }
+ }
+ return true;
+ },
+ hasOverlap: function (candidate, excludeIds) {
+ if (!this.doc) {
+ return false;
+ }
+ var elements = this.querySpatialCandidates(candidate, COORD_EPSILON, excludeIds);
+ for (var i = 0; i < elements.length; i++) {
+ var item = elements[i];
+ if (rectsOverlap(candidate, item)) {
+ return true;
+ }
+ }
+ return false;
+ },
+ snapToleranceWorld: function () {
+ return Math.max(1, EDGE_SNAP_SCREEN_TOLERANCE / this.camera.scale);
+ },
+ collectMoveSnap: function (baseItems, dx, dy, excludeIds) {
+ if (!this.doc || !baseItems || !baseItems.length) {
+ return { dx: 0, dy: 0 };
+ }
+ var tolerance = this.snapToleranceWorld();
+ var bestDx = null;
+ var bestDy = null;
+ for (var i = 0; i < baseItems.length; i++) {
+ var moving = baseItems[i];
+ var movedLeft = moving.x + dx;
+ var movedRight = movedLeft + moving.width;
+ var movedTop = moving.y + dy;
+ var movedBottom = movedTop + moving.height;
+ var candidates = this.querySpatialCandidates(
+ {
+ x: movedLeft,
+ y: movedTop,
+ width: moving.width,
+ height: moving.height
+ },
+ tolerance,
+ excludeIds
+ );
+ for (var j = 0; j < candidates.length; j++) {
+ var other = candidates[j];
+ var otherLeft = other.x;
+ var otherRight = other.x + other.width;
+ var otherTop = other.y;
+ var otherBottom = other.y + other.height;
+ if (rangesNearOrOverlap(movedTop, movedBottom, otherTop, otherBottom, tolerance)) {
+ var horizontalCandidates = [
+ otherLeft - movedRight,
+ otherRight - movedLeft,
+ otherLeft - movedLeft,
+ otherRight - movedRight
+ ];
+ for (var hx = 0; hx < horizontalCandidates.length; hx++) {
+ var deltaX = horizontalCandidates[hx];
+ if (
+ Math.abs(deltaX) <= tolerance &&
+ (bestDx === null || Math.abs(deltaX) < Math.abs(bestDx))
+ ) {
+ bestDx = deltaX;
+ }
+ }
+ }
+ if (rangesNearOrOverlap(movedLeft, movedRight, otherLeft, otherRight, tolerance)) {
+ var verticalCandidates = [
+ otherTop - movedBottom,
+ otherBottom - movedTop,
+ otherTop - movedTop,
+ otherBottom - movedBottom
+ ];
+ for (var vy = 0; vy < verticalCandidates.length; vy++) {
+ var deltaY = verticalCandidates[vy];
+ if (
+ Math.abs(deltaY) <= tolerance &&
+ (bestDy === null || Math.abs(deltaY) < Math.abs(bestDy))
+ ) {
+ bestDy = deltaY;
+ }
+ }
+ }
+ }
+ }
+ return {
+ dx: bestDx == null ? 0 : bestDx,
+ dy: bestDy == null ? 0 : bestDy
+ };
+ },
+ collectResizeSnap: function (rect, handle, excludeIds) {
+ if (!this.doc || !rect) {
+ return null;
+ }
+ var tolerance = this.snapToleranceWorld();
+ var left = rect.x;
+ var right = rect.x + rect.width;
+ var top = rect.y;
+ var bottom = rect.y + rect.height;
+ var bestLeft = null;
+ var bestRight = null;
+ var bestTop = null;
+ var bestBottom = null;
+ function pickBest(current, candidate) {
+ if (candidate == null) {
+ return current;
+ }
+ if (current == null || Math.abs(candidate) < Math.abs(current)) {
+ return candidate;
+ }
+ return current;
+ }
+ if (handle.indexOf('w') >= 0) {
+ bestLeft = pickBest(bestLeft, -left);
+ }
+ if (handle.indexOf('e') >= 0) {
+ bestRight = pickBest(bestRight, this.doc.canvasWidth - right);
+ }
+ if (handle.indexOf('n') >= 0) {
+ bestTop = pickBest(bestTop, -top);
+ }
+ if (handle.indexOf('s') >= 0) {
+ bestBottom = pickBest(bestBottom, this.doc.canvasHeight - bottom);
+ }
+ var elements = this.querySpatialCandidates(rect, tolerance, excludeIds);
+ for (var i = 0; i < elements.length; i++) {
+ var other = elements[i];
+ var otherLeft = other.x;
+ var otherRight = other.x + other.width;
+ var otherTop = other.y;
+ var otherBottom = other.y + other.height;
+ if (rangesNearOrOverlap(top, bottom, otherTop, otherBottom, tolerance)) {
+ if (handle.indexOf('w') >= 0) {
+ bestLeft = pickBest(bestLeft, otherLeft - left);
+ bestLeft = pickBest(bestLeft, otherRight - left);
+ }
+ if (handle.indexOf('e') >= 0) {
+ bestRight = pickBest(bestRight, otherLeft - right);
+ bestRight = pickBest(bestRight, otherRight - right);
+ }
+ }
+ if (rangesNearOrOverlap(left, right, otherLeft, otherRight, tolerance)) {
+ if (handle.indexOf('n') >= 0) {
+ bestTop = pickBest(bestTop, otherTop - top);
+ bestTop = pickBest(bestTop, otherBottom - top);
+ }
+ if (handle.indexOf('s') >= 0) {
+ bestBottom = pickBest(bestBottom, otherTop - bottom);
+ bestBottom = pickBest(bestBottom, otherBottom - bottom);
+ }
+ }
+ }
+ if (bestLeft != null && Math.abs(bestLeft) > tolerance) {
+ bestLeft = null;
+ }
+ if (bestRight != null && Math.abs(bestRight) > tolerance) {
+ bestRight = null;
+ }
+ if (bestTop != null && Math.abs(bestTop) > tolerance) {
+ bestTop = null;
+ }
+ if (bestBottom != null && Math.abs(bestBottom) > tolerance) {
+ bestBottom = null;
+ }
+ return {
+ left: bestLeft,
+ right: bestRight,
+ top: bestTop,
+ bottom: bestBottom
+ };
+ },
+ hitTestElement: function (point) {
+ if (!this.doc) {
+ return null;
+ }
+ var candidates = this.querySpatialCandidates(
+ {
+ x: point.x,
+ y: point.y,
+ width: 0,
+ height: 0
+ },
+ 0,
+ []
+ );
+ if (!candidates.length) {
+ return null;
+ }
+ var candidateMap = {};
+ for (var c = 0; c < candidates.length; c++) {
+ candidateMap[candidates[c].id] = true;
+ }
+ var elements = this.doc.elements || [];
+ for (var i = elements.length - 1; i >= 0; i--) {
+ var element = elements[i];
+ if (!candidateMap[element.id]) {
+ continue;
+ }
+ if (
+ point.x >= element.x &&
+ point.x <= element.x + element.width &&
+ point.y >= element.y &&
+ point.y <= element.y + element.height
+ ) {
+ return element;
+ }
+ }
+ return null;
+ },
+ getHandlePositions: function (element) {
+ var x = element.x;
+ var y = element.y;
+ var w = element.width;
+ var h = element.height;
+ var cx = x + w / 2;
+ var cy = y + h / 2;
+ if (element.type === 'annulus' && element.shape !== 'rect') {
+ return {
+ nw: { x: x, y: y },
+ ne: { x: x + w, y: y },
+ se: { x: x + w, y: y + h },
+ sw: { x: x, y: y + h },
+ turningPoint: element.turningPoint,
+ shape: element.shape
+ };
+ }
+ return {
+ nw: { x: x, y: y },
+ n: { x: cx, y: y },
+ ne: { x: x + w, y: y },
+ e: { x: x + w, y: cy },
+ se: { x: x + w, y: y + h },
+ s: { x: cx, y: y + h },
+ sw: { x: x, y: y + h },
+ w: { x: x, y: cy }
+ };
+ },
+ getResizeHandleAt: function (point, element) {
+ var handlePositions = this.getHandlePositions(element);
+ var baseTolerance = HANDLE_SCREEN_SIZE / this.camera.scale;
+ var sizeLimitedTolerance = Math.max(
+ Math.min(element.width, element.height) / 4,
+ 3 / this.camera.scale
+ );
+ var tolerance = Math.min(baseTolerance, sizeLimitedTolerance);
+ var bestHandle = '';
+ var bestDistance = Infinity;
+ for (var key in handlePositions) {
+ if (!handlePositions.hasOwnProperty(key)) {
+ continue;
+ }
+ var pos = handlePositions[key];
+ var dx = Math.abs(point.x - pos.x);
+ var dy = Math.abs(point.y - pos.y);
+ if (dx <= tolerance && dy <= tolerance) {
+ var distance = dx + dy;
+ if (distance < bestDistance) {
+ bestDistance = distance;
+ bestHandle = key;
+ }
+ }
+ }
+ return bestHandle;
+ },
+ cursorForHandle: function (handle) {
+ if (handle === 'nw' || handle === 'se') {
+ return 'nwse-resize';
+ }
+ if (handle === 'ne' || handle === 'sw') {
+ return 'nesw-resize';
+ }
+ if (handle === 'n' || handle === 's') {
+ return 'ns-resize';
+ }
+ if (handle === 'e' || handle === 'w') {
+ return 'ew-resize';
+ }
+ if (handle === 'turningPoint') {
+ const element = this.singleSelectedElement || {};
+ return element.shape === 'L1' || element.shape === 'L3' ? 'nesw-resize' : 'nwse-resize';
+ }
+ return 'default';
+ },
+ updateCursor: function () {
+ if (!this.pixiApp) {
+ return;
+ }
+ var cursor = 'default';
+ if (this.interactionState) {
+ if (this.interactionState.type === 'pan') {
+ cursor = 'grabbing';
+ } else if (
+ this.interactionState.type === 'draw' ||
+ this.interactionState.type === 'marquee'
+ ) {
+ cursor = 'crosshair';
+ } else if (this.interactionState.type === 'array') {
+ cursor = 'crosshair';
+ } else if (this.interactionState.type === 'move') {
+ cursor = 'move';
+ } else if (this.interactionState.type === 'movePending') {
+ cursor = 'grab';
+ } else if (this.interactionState.type === 'resize') {
+ cursor = this.cursorForHandle(this.interactionState.handle);
+ }
+ } else if (this.spacePressed || this.activeTool === 'pan') {
+ cursor = 'grab';
+ } else if (
+ DRAW_TYPES.indexOf(this.activeTool) >= 0 ||
+ this.activeTool === 'marquee' ||
+ this.activeTool === 'array'
+ ) {
+ cursor = 'crosshair';
+ } else if (this.singleSelectedElement) {
+ var point = this.lastPointerWorld || null;
+ if (point) {
+ var handle = this.getResizeHandleAt(point, this.singleSelectedElement);
+ cursor = handle ? this.cursorForHandle(handle) : 'default';
+ }
+ if (cursor === 'default' && this.hoverElementId) {
+ cursor = 'move';
+ } else if (cursor === 'default') {
+ cursor = 'grab';
+ }
+ } else {
+ cursor = this.hoverElementId ? 'move' : 'grab';
+ }
+ if (cursor !== this.lastCursor) {
+ this.lastCursor = cursor;
+ this.pixiApp.view.style.cursor = cursor;
+ }
+ },
+ startPan: function (point) {
+ this.cancelDeferredStaticRebuild();
+ this.cancelPanRefresh();
+ if (this.zoomRefreshTimer) {
+ window.clearTimeout(this.zoomRefreshTimer);
+ this.zoomRefreshTimer = null;
+ this.isZooming = false;
+ this.pendingViewportRefresh = true;
+ }
+ this.isPanning = true;
+ this.interactionState = {
+ type: 'pan',
+ startScreen: {
+ x: point.screenX,
+ y: point.screenY
+ },
+ startCamera: {
+ x: this.camera.x,
+ y: this.camera.y
+ }
+ };
+ this.updateCursor();
+ },
+ startMarquee: function (point, additive) {
+ this.cancelDeferredStaticRebuild();
+ this.interactionState = {
+ type: 'marquee',
+ additive: !!additive,
+ startWorld: { x: point.x, y: point.y },
+ currentWorld: { x: point.x, y: point.y }
+ };
+ this.updateCursor();
+ },
+ startDraw: function (point) {
+ this.cancelDeferredStaticRebuild();
+ var initialRect = { x: point.x, y: point.y, width: 0, height: 0 };
+ if (this.activeTool === 'annulus') {
+ initialRect.type = 'annulus';
+ initialRect.shape = this.annulusShape;
+ }
+ this.interactionState = {
+ type: 'draw',
+ beforeSnapshot: this.snapshotDoc(this.doc),
+ elementType: this.activeTool,
+ startWorld: { x: point.x, y: point.y },
+ rect: initialRect
+ };
+ this.updateCursor();
+ },
+ startArray: function (point, element) {
+ if (!this.canArrayFromElement(element)) {
+ this.showMessage('warning', '闃靛垪宸ュ叿褰撳墠鍙敮鎸佽揣鏋朵笌缁翠慨绔欏彴');
+ return;
+ }
+ this.cancelDeferredStaticRebuild();
+ this.interactionState = {
+ type: 'array',
+ beforeSnapshot: this.snapshotDoc(this.doc),
+ template: {
+ id: element.id,
+ type: element.type,
+ x: element.x,
+ y: element.y,
+ width: element.width,
+ height: element.height,
+ value: element.value
+ },
+ startWorld: { x: point.x, y: point.y },
+ currentWorld: { x: point.x, y: point.y },
+ previewItems: []
+ };
+ this.updateCursor();
+ },
+ startMove: function (point) {
+ var selected = this.getSelectedElements();
+ if (!selected.length) {
+ return;
+ }
+ this.cancelDeferredStaticRebuild();
+ var baseItems = selected.map(function (item) {
+ return {
+ id: item.id,
+ x: item.x,
+ y: item.y,
+ width: item.width,
+ height: item.height,
+ value: item.value,
+ type: item.type,
+ turningPoint: item.turningPoint
+ ? { x: item.turningPoint.x, y: item.turningPoint.y }
+ : null
+ };
+ });
+ this.interactionState = {
+ type: 'movePending',
+ beforeSnapshot: this.snapshotDoc(this.doc),
+ startScreen: { x: point.screenX, y: point.screenY },
+ startWorld: { x: point.x, y: point.y },
+ baseItems: baseItems
+ };
+ this.updateCursor();
+ },
+ startResize: function (point, element, handle) {
+ this.cancelDeferredStaticRebuild();
+ this.interactionState = {
+ type: 'resize',
+ handle: handle,
+ elementId: element.id,
+ beforeSnapshot: this.snapshotDoc(this.doc),
+ startWorld: { x: point.x, y: point.y },
+ baseRect: {
+ x: element.x,
+ y: element.y,
+ width: element.width,
+ height: element.height,
+ baseTurningPoint: element.turningPoint
+ ? { x: element.turningPoint.x, y: element.turningPoint.y }
+ : null,
+ shape: element.shape
+ }
+ };
+ this.markStaticSceneDirty();
+ this.scheduleRender();
+ this.updateCursor();
+ },
+ onCanvasPointerDown: function (event) {
+ if (!this.doc || !this.pixiApp) {
+ return;
+ }
+ if (event.button !== 0 && event.button !== 1) {
+ return;
+ }
+ if (this.pixiApp.view.setPointerCapture && event.pointerId != null) {
+ try {
+ this.pixiApp.view.setPointerCapture(event.pointerId);
+ } catch (ignore) {}
+ }
+ this.currentPointerId = event.pointerId;
+ var point = this.pointerToWorld(event);
+ this.lastPointerWorld = point;
+ this.pointerStatus = this.formatNumber(point.x) + ', ' + this.formatNumber(point.y);
+ if (this.spacePressed || this.activeTool === 'pan' || event.button === 1) {
+ this.startPan(point);
+ return;
+ }
+ if (DRAW_TYPES.indexOf(this.activeTool) >= 0) {
+ this.startDraw(point);
+ return;
+ }
+ if (this.activeTool === 'marquee') {
+ this.startMarquee(point, event.shiftKey);
+ return;
+ }
+ if (this.activeTool === 'array') {
+ var arrayHit = this.hitTestElement(point);
+ var arrayTemplate = arrayHit || this.singleSelectedElement;
+ if (arrayHit && this.selectedIds.indexOf(arrayHit.id) < 0) {
+ this.setSelectedIds([arrayHit.id]);
+ arrayTemplate = arrayHit;
+ }
+ if (!arrayTemplate) {
+ this.showMessage('warning', '璇峰厛閫変腑涓�涓揣鏋舵垨缁翠慨绔欏彴浣滀负闃靛垪妯℃澘');
+ return;
+ }
+ this.startArray(point, arrayTemplate);
+ return;
+ }
+
+ var selected = this.singleSelectedElement;
+ var handle = selected ? this.getResizeHandleAt(point, selected) : '';
+ if (handle) {
+ this.startResize(point, selected, handle);
+ return;
+ }
+
+ var hit = this.hitTestElement(point);
+ if (hit) {
+ if (event.shiftKey) {
+ var index = this.selectedIds.indexOf(hit.id);
+ if (index >= 0) {
+ var nextIds = this.selectedIds.slice();
+ nextIds.splice(index, 1);
+ this.setSelectedIds(nextIds);
+ } else {
+ this.setSelectedIds(this.selectedIds.concat([hit.id]));
+ }
+ this.scheduleRender();
+ return;
+ }
+ if (this.selectedIds.indexOf(hit.id) < 0) {
+ this.setSelectedIds([hit.id]);
+ this.scheduleRender();
+ }
+ this.startMove(point);
+ return;
+ }
+
+ if (this.selectedIds.length) {
+ this.setSelectedIds([]);
+ this.scheduleRender();
+ }
+ this.startPan(point);
+ },
+ onCanvasWheel: function (event) {
+ if (!this.pixiApp || !this.doc) {
+ return;
+ }
+ event.preventDefault();
+ var point = this.pointerToWorld(event);
+ var delta = event.deltaY < 0 ? 1.12 : 0.89;
+ var nextScale = clamp(this.camera.scale * delta, 0.06, 4);
+ this.camera.scale = nextScale;
+ this.camera.x = Math.round(point.screenX - point.x * nextScale);
+ this.camera.y = Math.round(point.screenY - point.y * nextScale);
+ this.viewZoom = nextScale;
+ this.scheduleZoomRefresh();
+ this.scheduleRender();
+ },
+ onWindowPointerMove: function (event) {
+ if (!this.pixiApp || !this.doc) {
+ return;
+ }
+ var point = this.pointerToWorld(event);
+ this.lastPointerWorld = point;
+ var pointerText = this.formatNumber(point.x) + ', ' + this.formatNumber(point.y);
+ var now = window.performance && performance.now ? performance.now() : Date.now();
+ if (
+ pointerText !== this.pointerStatus &&
+ (now - this.lastPointerStatusUpdateTs >= POINTER_STATUS_UPDATE_INTERVAL ||
+ this.pointerStatus === '--')
+ ) {
+ this.pointerStatus = pointerText;
+ this.lastPointerStatusUpdateTs = now;
+ }
+ if (!this.interactionState) {
+ var hover = this.hitTestElement(point);
+ var hoverId = hover ? hover.id : '';
+ if (hoverId !== this.hoverElementId) {
+ this.hoverElementId = hoverId;
+ this.scheduleRender();
+ }
+ this.updateCursor();
+ return;
+ }
+
+ var state = this.interactionState;
+ if (state.type === 'pan') {
+ this.camera.x = Math.round(state.startCamera.x + (point.screenX - state.startScreen.x));
+ this.camera.y = Math.round(state.startCamera.y + (point.screenY - state.startScreen.y));
+ this.scheduleRender();
+ return;
+ }
+
+ if (state.type === 'marquee') {
+ state.currentWorld = { x: point.x, y: point.y };
+ this.scheduleRender();
+ return;
+ }
+
+ if (state.type === 'draw') {
+ var rawRect = buildRectFromPoints(state.startWorld, point);
+ var clipped = {
+ x: clamp(rawRect.x, 0, this.doc.canvasWidth),
+ y: clamp(rawRect.y, 0, this.doc.canvasHeight),
+ width: clamp(rawRect.width, 0, this.doc.canvasWidth),
+ height: clamp(rawRect.height, 0, this.doc.canvasHeight)
+ };
+ if (clipped.x + clipped.width > this.doc.canvasWidth) {
+ clipped.width = roundCoord(this.doc.canvasWidth - clipped.x);
+ }
+ if (clipped.y + clipped.height > this.doc.canvasHeight) {
+ clipped.height = roundCoord(this.doc.canvasHeight - clipped.y);
+ }
+ if (state.elementType === 'annulus') {
+ clipped.type = 'annulus';
+ clipped.shape = this.annulusShape;
+ }
+ state.rect = clipped;
+ this.scheduleRender();
+ return;
+ }
+ if (state.type === 'array') {
+ state.currentWorld = { x: point.x, y: point.y };
+ state.previewItems = this.buildArrayCopies(
+ state.template,
+ state.startWorld,
+ state.currentWorld
+ );
+ this.scheduleRender();
+ return;
+ }
+
+ if (state.type === 'movePending') {
+ var dragDistance = Math.max(
+ Math.abs(point.screenX - state.startScreen.x),
+ Math.abs(point.screenY - state.startScreen.y)
+ );
+ if (dragDistance < DRAG_START_THRESHOLD) {
+ return;
+ }
+ state.type = 'move';
+ this.markStaticSceneDirty();
+ this.scheduleRender();
+ this.updateCursor();
+ }
+
+ if (state.type === 'move') {
+ var dx = point.x - state.startWorld.x;
+ var dy = point.y - state.startWorld.y;
+ var minDx = -Infinity;
+ var maxDx = Infinity;
+ var minDy = -Infinity;
+ var maxDy = Infinity;
+ for (var i = 0; i < state.baseItems.length; i++) {
+ var base = state.baseItems[i];
+ minDx = Math.max(minDx, -base.x);
+ minDy = Math.max(minDy, -base.y);
+ maxDx = Math.min(maxDx, this.doc.canvasWidth - (base.x + base.width));
+ maxDy = Math.min(maxDy, this.doc.canvasHeight - (base.y + base.height));
+ }
+ dx = clamp(dx, minDx, maxDx);
+ dy = clamp(dy, minDy, maxDy);
+ var snapDelta = this.collectMoveSnap(state.baseItems, dx, dy, this.selectedIds.slice());
+ dx = clamp(dx + snapDelta.dx, minDx, maxDx);
+ dy = clamp(dy + snapDelta.dy, minDy, maxDy);
+ for (var j = 0; j < state.baseItems.length; j++) {
+ var baseItem = state.baseItems[j];
+ var element = this.findElementById(baseItem.id);
+ if (!element) {
+ continue;
+ }
+ element.x = roundCoord(baseItem.x + dx);
+ element.y = roundCoord(baseItem.y + dy);
+ if (element.type === 'annulus' && baseItem.turningPoint) {
+ if (!element.turningPoint) element.turningPoint = {};
+ element.turningPoint.x = baseItem.turningPoint.x + dx;
+ element.turningPoint.y = baseItem.turningPoint.y + dy;
+ }
+ }
+ this.scheduleRender();
+ return;
+ }
+
+ if (state.type === 'resize') {
+ var target = this.findElementById(state.elementId);
+ if (!target) {
+ return;
+ }
+ if (state.handle === 'turningPoint') {
+ var deltaX = point.x - state.startWorld.x;
+ var deltaY = point.y - state.startWorld.y;
+ var newTurningPoint = {
+ x: roundCoord(state.baseRect.baseTurningPoint.x + deltaX),
+ y: roundCoord(state.baseRect.baseTurningPoint.y + deltaY)
+ };
+ // 杈圭晫绾︽潫锛氭嫄鐐瑰繀椤诲湪鐭╁舰鍐呴儴锛屼笖涓嶈兘瀵艰嚧鑷傞暱灏忎簬 MIN_ELEMENT_SIZE
+ var minX = target.x + MIN_ELEMENT_SIZE;
+ var maxX = target.x + target.width - MIN_ELEMENT_SIZE;
+ var minY = target.y + MIN_ELEMENT_SIZE;
+ var maxY = target.y + target.height - MIN_ELEMENT_SIZE;
+ newTurningPoint.x = clamp(newTurningPoint.x, minX, maxX);
+ newTurningPoint.y = clamp(newTurningPoint.y, minY, maxY);
+ // 棰濆淇濊瘉涓ゆ潯鑷傝嚦灏戜负 MIN_ELEMENT_SIZE
+ var leftArm = newTurningPoint.x - target.x;
+ var rightArm = target.x + target.width - newTurningPoint.x;
+ var topArm = newTurningPoint.y - target.y;
+ var bottomArm = target.y + target.height - newTurningPoint.y;
+ if (leftArm < MIN_ELEMENT_SIZE) {
+ newTurningPoint.x = target.x + MIN_ELEMENT_SIZE;
+ }
+ if (rightArm < MIN_ELEMENT_SIZE) {
+ newTurningPoint.x = target.x + target.width - MIN_ELEMENT_SIZE;
+ }
+ if (topArm < MIN_ELEMENT_SIZE) {
+ newTurningPoint.y = target.y + MIN_ELEMENT_SIZE;
+ }
+ if (bottomArm < MIN_ELEMENT_SIZE) {
+ newTurningPoint.y = target.y + target.height - MIN_ELEMENT_SIZE;
+ }
+ const isStillHalf = G.getIsStillHalf({
+ ...target,
+ turningPoint: {
+ x: newTurningPoint.x,
+ y: newTurningPoint.y
+ }
+ });
+ if (!isStillHalf) {
+ return;
+ }
+ target.turningPoint = {
+ x: newTurningPoint.x,
+ y: newTurningPoint.y
+ };
+ this.markStaticSceneDirty();
+ this.scheduleRender();
+ return;
+ }
+
+ var baseRect = state.baseRect;
+ var left = baseRect.x;
+ var right = baseRect.x + baseRect.width;
+ var top = baseRect.y;
+ var bottom = baseRect.y + baseRect.height;
+ if (state.handle.indexOf('w') >= 0) {
+ left = clamp(point.x, 0, right - MIN_ELEMENT_SIZE);
+ }
+ if (state.handle.indexOf('e') >= 0) {
+ right = clamp(point.x, left + MIN_ELEMENT_SIZE, this.doc.canvasWidth);
+ }
+ if (state.handle.indexOf('n') >= 0) {
+ top = clamp(point.y, 0, bottom - MIN_ELEMENT_SIZE);
+ }
+ if (state.handle.indexOf('s') >= 0) {
+ bottom = clamp(point.y, top + MIN_ELEMENT_SIZE, this.doc.canvasHeight);
+ }
+ var snapped = this.collectResizeSnap(
+ {
+ x: left,
+ y: top,
+ width: right - left,
+ height: bottom - top
+ },
+ state.handle,
+ [target.id]
+ );
+ if (snapped) {
+ if (state.handle.indexOf('w') >= 0 && snapped.left != null) {
+ left = clamp(left + snapped.left, 0, right - MIN_ELEMENT_SIZE);
+ }
+ if (state.handle.indexOf('e') >= 0 && snapped.right != null) {
+ right = clamp(right + snapped.right, left + MIN_ELEMENT_SIZE, this.doc.canvasWidth);
+ }
+ if (state.handle.indexOf('n') >= 0 && snapped.top != null) {
+ top = clamp(top + snapped.top, 0, bottom - MIN_ELEMENT_SIZE);
+ }
+ if (state.handle.indexOf('s') >= 0 && snapped.bottom != null) {
+ bottom = clamp(
+ bottom + snapped.bottom,
+ top + MIN_ELEMENT_SIZE,
+ this.doc.canvasHeight
+ );
+ }
+ }
+ if (target.type === 'annulus' && target.shape !== 'rect') {
+ const isStillHalf = G.getIsStillHalf({
+ ...target,
+ x: roundCoord(left),
+ y: roundCoord(top),
+ width: roundCoord(right - left),
+ height: roundCoord(bottom - top)
+ });
+ if (!isStillHalf) {
+ return;
+ }
+ }
+ // 闃叉杈硅秺杩� turningPoint 瀵艰嚧鐮村潖 L 鍨嬬粨鏋勶紝闄愬埗杈硅窛绂� turningPoint 鑷冲皯 MIN_ELEMENT_SIZE
+ if (target.type === 'annulus' && target.shape !== 'rect' && target.turningPoint) {
+ if (state.handle.indexOf('w') >= 0) {
+ left = Math.min(left, target.turningPoint.x - MIN_ELEMENT_SIZE);
+ }
+ if (state.handle.indexOf('e') >= 0) {
+ right = Math.max(right, target.turningPoint.x + MIN_ELEMENT_SIZE);
+ }
+ if (state.handle.indexOf('n') >= 0) {
+ top = Math.min(top, target.turningPoint.y - MIN_ELEMENT_SIZE);
+ }
+ if (state.handle.indexOf('s') >= 0) {
+ bottom = Math.max(bottom, target.turningPoint.y + MIN_ELEMENT_SIZE);
+ }
+ }
+
+ target.x = roundCoord(left);
+ target.y = roundCoord(top);
+ target.width = roundCoord(right - left);
+ target.height = roundCoord(bottom - top);
+ this.scheduleRender();
+ }
+ },
+ onWindowPointerUp: function (event) {
+ if (!this.interactionState) {
+ return;
+ }
+ if (
+ this.currentPointerId != null &&
+ event.pointerId != null &&
+ this.currentPointerId !== event.pointerId
+ ) {
+ return;
+ }
+ if (this.pixiApp && this.pixiApp.view.releasePointerCapture && event.pointerId != null) {
+ try {
+ this.pixiApp.view.releasePointerCapture(event.pointerId);
+ } catch (ignore) {}
+ }
+ this.currentPointerId = null;
+
+ var state = this.interactionState;
+ this.interactionState = null;
+
+ if (state.type === 'pan') {
+ this.updateCursor();
+ this.schedulePanRefresh();
+ this.scheduleRender();
+ return;
+ }
+
+ if (state.type === 'marquee') {
+ var rect = buildRectFromPoints(state.startWorld, state.currentWorld);
+ if (rect.width > 2 && rect.height > 2) {
+ var matched = (this.doc.elements || [])
+ .filter(function (item) {
+ return rectIntersects(rect, item);
+ })
+ .map(function (item) {
+ return item.id;
+ });
+ this.setSelectedIds(
+ state.additive ? Array.from(new Set(this.selectedIds.concat(matched))) : matched
+ );
+ }
+ this.scheduleRender();
+ return;
+ }
+
+ if (state.type === 'movePending') {
+ this.updateCursor();
+ return;
+ }
+
+ if (state.type === 'draw') {
+ var drawRect = state.rect;
+ if (
+ drawRect &&
+ drawRect.width >= MIN_ELEMENT_SIZE &&
+ drawRect.height >= MIN_ELEMENT_SIZE
+ ) {
+ var newElement = {
+ id: nextId(),
+ type: state.elementType,
+ x: roundCoord(drawRect.x),
+ y: roundCoord(drawRect.y),
+ width: roundCoord(drawRect.width),
+ height: roundCoord(drawRect.height),
+ value: '',
+ shape: drawRect.shape,
+ pathList: drawRect.pathList
+ };
+ if (isDeviceConfigType(newElement.type)) {
+ newElement.value = JSON.stringify({
+ trackId: this.getNextDeviceTrackId(null),
+ deviceList: [
+ {
+ valueKey: '',
+ deviceNo: '',
+ progress: 0
+ }
+ ]
+ });
+ }
+ if (this.hasOverlap(newElement, [])) {
+ this.showMessage('warning', '鏂板厓绱犱笉鑳戒笌宸叉湁鍏冪礌閲嶅彔');
+ } else if (!this.isWithinCanvas(newElement)) {
+ this.showMessage('warning', '鏂板厓绱犺秴鍑虹敾甯冭寖鍥�');
+ } else {
+ this.doc.elements.push(newElement);
+ this.selectedIds = [newElement.id];
+ this.commitMutation(state.beforeSnapshot);
+ this.refreshInspector();
+ return;
+ }
+ }
+ this.refreshInspector();
+ this.scheduleRender();
+ return;
+ }
+ if (state.type === 'array') {
+ var copies =
+ state.previewItems && state.previewItems.length
+ ? state.previewItems
+ : this.buildArrayCopies(
+ state.template,
+ state.startWorld,
+ state.currentWorld || state.startWorld
+ );
+ if (!copies.length) {
+ this.scheduleRender();
+ return;
+ }
+ if (!this.canPlaceElements(copies, [])) {
+ this.showMessage('warning', '闃靛垪鐢熸垚鍚庝細閲嶅彔鎴栬秴鍑虹敾甯冿紝宸插彇娑�');
+ this.scheduleRender();
+ return;
+ }
+ var finalizedCopies = copies.map(function (item) {
+ return $.extend({}, item, { id: nextId() });
+ });
+ var self = this;
+ this.runMutation(function () {
+ self.doc.elements = self.doc.elements.concat(finalizedCopies);
+ self.selectedIds = [finalizedCopies[finalizedCopies.length - 1].id];
+ });
+ return;
+ }
+
+ if (state.type === 'move') {
+ var movedElements = this.getSelectedElements();
+ if (!this.canPlaceElements(movedElements, this.selectedIds.slice())) {
+ for (var i = 0; i < state.baseItems.length; i++) {
+ var base = state.baseItems[i];
+ var element = this.findElementById(base.id);
+ if (!element) {
+ continue;
+ }
+ element.x = base.x;
+ element.y = base.y;
+ if (base.turningPoint) {
+ element.turningPoint = {
+ x: base.turningPoint.x,
+ y: base.turningPoint.y
+ };
+ } else if (element.turningPoint) {
+ delete element.turningPoint;
+ }
+ }
+ this.showMessage('warning', '绉诲姩鍚庝細閲嶅彔鎴栬秴鍑虹敾甯冿紝宸叉仮澶�');
+ this.refreshInspector();
+ this.scheduleRender();
+ return;
+ }
+ if (!this.commitMutation(state.beforeSnapshot)) {
+ this.markStaticSceneDirty();
+ this.scheduleRender();
+ }
+ return;
+ }
+
+ if (state.type === 'resize') {
+ var resized = this.findElementById(state.elementId);
+ if (resized) {
+ if (!this.isWithinCanvas(resized) || this.hasOverlap(resized, [resized.id])) {
+ resized.x = state.baseRect.x;
+ resized.y = state.baseRect.y;
+ resized.width = state.baseRect.width;
+ resized.height = state.baseRect.height;
+ if (state.baseTurningPoint) {
+ resized.turningPoint = {
+ x: state.baseTurningPoint.x,
+ y: state.baseTurningPoint.y
+ };
+ } else if (resized.turningPoint) {
+ delete resized.turningPoint;
+ }
+ this.showMessage('warning', '缂╂斁鍚庝細閲嶅彔鎴栬秴鍑虹敾甯冿紝宸叉仮澶�');
+ this.refreshInspector();
+ this.scheduleRender();
+ return;
+ }
+ }
+ if (!this.commitMutation(state.beforeSnapshot)) {
+ this.markStaticSceneDirty();
+ this.scheduleRender();
+ }
+ return;
+ }
+
+ this.scheduleRender();
+ },
+ onWindowKeyDown: function (event) {
+ if (event.key === ' ' && !isInputLike(event.target)) {
+ this.spacePressed = true;
+ this.updateCursor();
+ event.preventDefault();
+ }
+ if (!this.doc) {
+ return;
+ }
+ if (isInputLike(event.target)) {
+ return;
+ }
+ var ctrl = event.ctrlKey || event.metaKey;
+ if (event.key === 'Delete' || event.key === 'Backspace') {
+ event.preventDefault();
+ this.deleteSelection();
+ return;
+ }
+ if (ctrl && (event.key === 'z' || event.key === 'Z')) {
+ event.preventDefault();
+ if (event.shiftKey) {
+ this.redo();
+ } else {
+ this.undo();
+ }
+ return;
+ }
+ if (ctrl && (event.key === 'y' || event.key === 'Y')) {
+ event.preventDefault();
+ this.redo();
+ return;
+ }
+ if (ctrl && (event.key === 'c' || event.key === 'C')) {
+ event.preventDefault();
+ this.copySelection();
+ return;
+ }
+ if (ctrl && (event.key === 'v' || event.key === 'V')) {
+ event.preventDefault();
+ this.pasteClipboard();
+ return;
+ }
+ if (event.key === 'Escape') {
+ this.interactionState = null;
+ this.setSelectedIds([]);
+ this.hoverElementId = '';
+ this.scheduleRender();
+ }
+ },
+ onWindowKeyUp: function (event) {
+ if (event.key === ' ') {
+ this.spacePressed = false;
+ this.updateCursor();
+ }
+ },
+ onBeforeUnload: function (event) {
+ if (!this.isDirty) {
+ return;
+ }
+ event.preventDefault();
+ event.returnValue = '';
+ }
+ }
+ });
})();
--
Gitblit v1.9.1