From c5d7868e9e5fb8013edb088a70e75fc83d575690 Mon Sep 17 00:00:00 2001
From: Junjie <fallin.jie@qq.com>
Date: 星期六, 07 三月 2026 10:36:05 +0800
Subject: [PATCH] #
---
src/main/webapp/views/locMap/locMap.html | 1836 ++++++++++++++++++++++++++++++++++++++-----------------
src/main/webapp/components/MapCanvas.js | 14
src/main/java/com/zy/asrs/controller/ConsoleController.java | 117 +++
3 files changed, 1,388 insertions(+), 579 deletions(-)
diff --git a/src/main/java/com/zy/asrs/controller/ConsoleController.java b/src/main/java/com/zy/asrs/controller/ConsoleController.java
index 51b7777..54a1ef5 100644
--- a/src/main/java/com/zy/asrs/controller/ConsoleController.java
+++ b/src/main/java/com/zy/asrs/controller/ConsoleController.java
@@ -350,21 +350,126 @@
*/
@GetMapping("/map/{lev}/auth")
public R getLocMap(@PathVariable Integer lev) {
- Object object = redisUtil.get(RedisKeyType.LOC_MAP_BASE.key);
- List<List<HashMap<String, Object>>> mapNodeList = null;
- if (object != null) {
- mapNodeList = (List<List<HashMap<String, Object>>>) object;
+ List<List<HashMap<String, Object>>> mapNodeList = getLocMapBaseSnapshot();
+ if (mapNodeList == null || mapNodeList.isEmpty()) {
+ return R.error("璇峰厛鍒濆鍖栧湴鍥�");
}
List<LocMast> locMastList = locMastService.selectLocByLev(lev);
+ boolean needRefreshBase = false;
for (LocMast locMast : locMastList) {
- String[] locType = locMast.getLocType().split("-");
- HashMap<String, Object> mapNode = mapNodeList.get(Integer.parseInt(locType[0])).get(Integer.parseInt(locType[1]));
+ Integer[] pos = parseLocTypePos(locMast.getLocType());
+ if (pos == null || !isValidMapNodeIndex(mapNodeList, pos[0], pos[1])) {
+ needRefreshBase = true;
+ break;
+ }
+ }
+
+ if (needRefreshBase) {
+ refreshLocMapBaseCache();
+ mapNodeList = getLocMapBaseSnapshot();
+ }
+
+ for (LocMast locMast : locMastList) {
+ Integer[] pos = parseLocTypePos(locMast.getLocType());
+ if (pos == null || !isValidMapNodeIndex(mapNodeList, pos[0], pos[1])) {
+ log.warn("locMap skip invalid locType, locNo={}, locType={}", locMast.getLocNo(), locMast.getLocType());
+ continue;
+ }
+ HashMap<String, Object> mapNode = mapNodeList.get(pos[0]).get(pos[1]);
mapNode.put("locSts", locMast.getLocSts());
mapNode.put("locNo", locMast.getLocNo());
}
return R.ok().add(mapNodeList);
}
+ private List<List<HashMap<String, Object>>> getLocMapBaseSnapshot() {
+ Object object = redisUtil.get(RedisKeyType.LOC_MAP_BASE.key);
+ if (!(object instanceof List)) {
+ return buildLocMapBase();
+ }
+ return cloneMapNodeList((List<List<HashMap<String, Object>>>) object);
+ }
+
+ private void refreshLocMapBaseCache() {
+ List<List<HashMap<String, Object>>> base = buildLocMapBase();
+ if (base != null && !base.isEmpty()) {
+ redisUtil.set(RedisKeyType.LOC_MAP_BASE.key, base);
+ }
+ }
+
+ private List<List<HashMap<String, Object>>> buildLocMapBase() {
+ BasMap basMap = basMapService.selectOne(new EntityWrapper<BasMap>().eq("lev", 1));
+ if (Cools.isEmpty(basMap) || Cools.isEmpty(basMap.getData())) {
+ return null;
+ }
+
+ List<List<JSONObject>> dataList = JSON.parseObject(basMap.getData(), List.class);
+ List<List<HashMap<String, Object>>> mapNodeList = new ArrayList<>();
+ for (int i = 0; i < dataList.size(); i++) {
+ List<JSONObject> row = dataList.get(i);
+ List<HashMap<String, Object>> mapNodeRow = new ArrayList<>();
+ for (int j = 0; j < row.size(); j++) {
+ JSONObject map = row.get(j);
+ HashMap<String, Object> mapNode = new HashMap<>();
+ mapNode.put("id", i + "-" + j);
+
+ String nodeType = map.getString("type");
+ mapNode.put("type", nodeType);
+ if ("shelf".equals(nodeType)) {
+ mapNode.put("value", MapNodeType.NORMAL_PATH.id);
+ } else if ("devp".equals(nodeType)) {
+ mapNode.put("value", MapNodeType.DISABLE.id);
+ } else if ("crn".equals(nodeType) || "dualCrn".equals(nodeType) || "rgv".equals(nodeType)) {
+ mapNode.put("value", MapNodeType.MAIN_PATH.id);
+ } else {
+ mapNode.put("value", MapNodeType.DISABLE.id);
+ }
+
+ mapNodeRow.add(mapNode);
+ }
+ mapNodeList.add(mapNodeRow);
+ }
+ return mapNodeList;
+ }
+
+ private List<List<HashMap<String, Object>>> cloneMapNodeList(List<List<HashMap<String, Object>>> source) {
+ List<List<HashMap<String, Object>>> copy = new ArrayList<>();
+ for (List<HashMap<String, Object>> row : source) {
+ List<HashMap<String, Object>> rowCopy = new ArrayList<>();
+ if (row != null) {
+ for (HashMap<String, Object> item : row) {
+ rowCopy.add(item == null ? new HashMap<>() : new HashMap<>(item));
+ }
+ }
+ copy.add(rowCopy);
+ }
+ return copy;
+ }
+
+ private Integer[] parseLocTypePos(String locType) {
+ if (Cools.isEmpty(locType)) {
+ return null;
+ }
+ String[] parts = locType.split("-");
+ if (parts.length < 2) {
+ return null;
+ }
+ try {
+ return new Integer[]{Integer.parseInt(parts[0]), Integer.parseInt(parts[1])};
+ } catch (NumberFormatException e) {
+ log.warn("parse locType fail, locType={}", locType, e);
+ return null;
+ }
+ }
+
+ private boolean isValidMapNodeIndex(List<List<HashMap<String, Object>>> mapNodeList, Integer rowIdx, Integer colIdx) {
+ if (mapNodeList == null || rowIdx == null || colIdx == null || rowIdx < 0 || colIdx < 0 || rowIdx >= mapNodeList.size()) {
+ return false;
+ }
+ List<HashMap<String, Object>> row = mapNodeList.get(rowIdx);
+ return row != null && colIdx < row.size();
+ }
+
@RequestMapping(value = "/map/locList")
public R mapLocList() {
Object object = redisUtil.get(RedisKeyType.LOC_MAST_MAP_LIST.key);
diff --git a/src/main/webapp/components/MapCanvas.js b/src/main/webapp/components/MapCanvas.js
index be2b7d8..b5ced58 100644
--- a/src/main/webapp/components/MapCanvas.js
+++ b/src/main/webapp/components/MapCanvas.js
@@ -26,6 +26,7 @@
<div v-show="showMapToolPanel" :style="mapToolBarStyle()">
<div :style="mapToolRowStyle()">
<button type="button" @click="toggleStationDirection" :style="mapToolButtonStyle(showStationDirection)">{{ showStationDirection ? '闅愯棌绔欑偣鏂瑰悜' : '鏄剧ず绔欑偣鏂瑰悜' }}</button>
+ <button type="button" @click="resetMapView" :style="mapToolButtonStyle(false)">閲嶇疆瑙嗗浘</button>
<button type="button" @click="rotateMap" :style="mapToolButtonStyle(false)">鏃嬭浆</button>
<button type="button" @click="toggleMirror" :style="mapToolButtonStyle(mapMirrorX)">{{ mapMirrorX ? '鍙栨秷闀滃儚' : '闀滃儚' }}</button>
</div>
@@ -154,7 +155,7 @@
this.loadStationColorConfig();
this.loadLocList();
this.connectWs();
-
+
setTimeout(() => {
this.getMap(this.currentLev);
}, 1000);
@@ -2408,6 +2409,11 @@
this.applyMapTransform(true);
this.saveMapTransformConfig();
},
+ resetMapView() {
+ this.fitStageToContent();
+ this.scheduleAdjustLabels();
+ this.scheduleShelfChunkCulling();
+ },
toggleStationDirection() {
this.showStationDirection = !this.showStationDirection;
this.applyStationDirectionVisibility();
@@ -2640,12 +2646,6 @@
}
}
});
-
-
-
-
-
-
diff --git a/src/main/webapp/views/locMap/locMap.html b/src/main/webapp/views/locMap/locMap.html
index 36fdee8..fdd8960 100644
--- a/src/main/webapp/views/locMap/locMap.html
+++ b/src/main/webapp/views/locMap/locMap.html
@@ -3,486 +3,1218 @@
<head>
<meta charset="UTF-8">
<title>搴撲綅鍦板浘</title>
- <link rel="stylesheet" href="../../static/css/animate.min.css">
<link rel="stylesheet" href="../../static/vue/element/element.css">
- <link rel="stylesheet" href="../../static/css/console_vue.css">
- <link rel="stylesheet" href="../../static/css/toggle-switch.css">
<script type="text/javascript" src="../../static/js/jquery/jquery-3.3.1.min.js"></script>
- <script type="text/javascript" src="../../static/layui/layui.js"></script>
- <script type="text/javascript" src="../../static/js/handlebars/handlebars-v4.5.3.js"></script>
<script type="text/javascript" src="../../static/js/common.js"></script>
<script type="text/javascript" src="../../static/vue/js/vue.min.js"></script>
<script type="text/javascript" src="../../static/vue/element/element.js"></script>
<script src="../../static/js/gsap.min.js"></script>
<script src="../../static/js/pixi-legacy.min.js"></script>
<style>
- *{
+ html, body, #app {
+ width: 100%;
+ height: 100%;
margin: 0;
- padding: 0;
+ overflow: hidden;
+ }
+
+ body {
+ background: linear-gradient(180deg, #eef4f8 0%, #e7edf4 100%);
+ font-family: "Helvetica Neue", Arial, sans-serif;
+ }
+
+ * {
+ box-sizing: border-box;
+ }
+
+ .locmap-shell {
+ position: relative;
+ width: 100%;
+ height: 100%;
+ overflow: hidden;
+ }
+
+ .locmap-canvas {
+ position: absolute;
+ inset: 0;
+ }
+
+ .locmap-title-card {
+ position: absolute;
+ top: 18px;
+ left: 18px;
+ z-index: 20;
+ padding: 14px 16px;
+ min-width: 220px;
+ border-radius: 18px;
+ border: 1px solid rgba(255, 255, 255, 0.42);
+ background: rgba(248, 251, 253, 0.92);
+ box-shadow: 0 10px 24px rgba(88, 110, 136, 0.08);
+ backdrop-filter: blur(6px);
+ pointer-events: none;
+ }
+
+ .locmap-title {
+ color: #243447;
+ font-size: 18px;
+ font-weight: 700;
+ line-height: 1.2;
+ }
+
+ .locmap-title-desc {
+ margin-top: 6px;
+ color: #6b7b8d;
+ font-size: 12px;
+ line-height: 1.5;
+ }
+
+ .locmap-tools {
+ position: absolute;
+ top: 18px;
+ right: 28px;
+ z-index: 30;
+ display: flex;
+ flex-direction: column;
+ align-items: flex-end;
+ gap: 8px;
+ }
+
+ .locmap-fps {
+ padding: 4px 10px;
+ border-radius: 999px;
+ background: rgba(255, 255, 255, 0.7);
+ border: 1px solid rgba(160, 180, 205, 0.28);
+ color: #48617c;
+ font-size: 12px;
+ line-height: 18px;
+ letter-spacing: 0.04em;
+ box-shadow: 0 6px 16px rgba(37, 64, 97, 0.06);
+ user-select: none;
+ }
+
+ .locmap-tool-toggle,
+ .locmap-tool-btn,
+ .locmap-floor-btn,
+ .locmap-detail-close {
+ appearance: none;
+ cursor: pointer;
+ transition: all .18s ease;
+ outline: none;
+ }
+
+ .locmap-tool-toggle {
+ height: 30px;
+ padding: 0 12px;
+ border-radius: 999px;
+ border: 1px solid rgba(160, 180, 205, 0.3);
+ background: rgba(255, 255, 255, 0.82);
+ color: #46617b;
+ font-size: 12px;
+ line-height: 30px;
+ box-shadow: 0 6px 16px rgba(37, 64, 97, 0.06);
+ }
+
+ .locmap-tool-toggle.is-active {
+ border-color: rgba(96, 132, 170, 0.36);
+ background: rgba(235, 243, 251, 0.96);
+ }
+
+ .locmap-tool-panel {
+ display: flex;
+ flex-direction: column;
+ gap: 8px;
+ min-width: 168px;
+ padding: 8px;
+ border-radius: 14px;
+ background: rgba(255, 255, 255, 0.72);
+ border: 1px solid rgba(160, 180, 205, 0.3);
+ box-shadow: 0 8px 20px rgba(37, 64, 97, 0.08);
+ backdrop-filter: blur(4px);
+ }
+
+ .locmap-tool-row {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 8px;
+ justify-content: flex-end;
+ }
+
+ .locmap-tool-btn,
+ .locmap-floor-btn {
+ min-width: 64px;
+ height: 30px;
+ padding: 0 12px;
+ border-radius: 10px;
+ border: 1px solid rgba(160, 180, 205, 0.3);
+ background: rgba(255, 255, 255, 0.88);
+ color: #4d647d;
+ font-size: 12px;
+ line-height: 30px;
+ white-space: nowrap;
+ }
+
+ .locmap-tool-btn:hover,
+ .locmap-floor-btn:hover,
+ .locmap-detail-close:hover,
+ .locmap-tool-toggle:hover {
+ transform: translateY(-1px);
+ }
+
+ .locmap-tool-btn.is-active,
+ .locmap-floor-btn.is-active {
+ border-color: rgba(255, 136, 93, 0.38);
+ background: rgba(255, 119, 77, 0.16);
+ color: #d85a31;
+ }
+
+ .locmap-tool-section {
+ display: flex;
+ flex-direction: column;
+ gap: 4px;
+ padding-top: 6px;
+ border-top: 1px solid rgba(160, 180, 205, 0.22);
+ }
+
+ .locmap-tool-label {
+ color: #6a7f95;
+ font-size: 10px;
+ line-height: 14px;
+ text-align: right;
+ }
+
+ .locmap-detail-panel {
+ position: absolute;
+ top: 92px;
+ right: 18px;
+ bottom: 18px;
+ z-index: 25;
+ width: min(max(320px, 25vw), calc(100vw - 92px));
+ border-radius: 20px;
+ border: 1px solid rgba(255, 255, 255, 0.42);
+ background: rgba(248, 251, 253, 0.94);
+ box-shadow: 0 10px 24px rgba(88, 110, 136, 0.08);
+ overflow: hidden;
+ display: flex;
+ flex-direction: column;
+ backdrop-filter: blur(6px);
+ }
+
+ .locmap-detail-header {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ gap: 12px;
+ padding: 16px 16px 12px;
+ border-bottom: 1px solid rgba(226, 232, 240, 0.72);
+ background: rgba(255, 255, 255, 0.24);
+ }
+
+ .locmap-detail-title-wrap {
+ min-width: 0;
+ }
+
+ .locmap-detail-type {
+ display: inline-flex;
+ align-items: center;
+ height: 22px;
+ padding: 0 10px;
+ border-radius: 999px;
+ background: rgba(103, 149, 193, 0.12);
+ color: #4b6782;
+ font-size: 11px;
+ font-weight: 700;
+ }
+
+ .locmap-detail-title {
+ margin-top: 8px;
+ color: #243447;
+ font-size: 18px;
+ font-weight: 700;
+ line-height: 1.2;
+ }
+
+ .locmap-detail-subtitle {
+ margin-top: 6px;
+ color: #6b7b8d;
+ font-size: 12px;
+ line-height: 1.4;
+ }
+
+ .locmap-detail-close {
+ flex-shrink: 0;
+ width: 32px;
+ height: 32px;
+ border-radius: 10px;
+ border: 1px solid rgba(160, 180, 205, 0.28);
+ background: rgba(255, 255, 255, 0.84);
+ color: #4d647d;
+ font-size: 18px;
+ line-height: 30px;
+ text-align: center;
+ }
+
+ .locmap-detail-body {
+ flex: 1;
+ min-height: 0;
+ padding: 16px;
+ overflow: auto;
+ }
+
+ .locmap-kv-grid {
+ display: grid;
+ grid-template-columns: repeat(2, minmax(0, 1fr));
+ gap: 12px;
+ }
+
+ .locmap-kv-card {
+ padding: 14px;
+ border-radius: 16px;
+ border: 1px solid rgba(224, 232, 239, 0.92);
+ background: rgba(255, 255, 255, 0.62);
+ box-shadow: 0 8px 18px rgba(148, 163, 184, 0.06);
+ }
+
+ .locmap-kv-label {
+ color: #7d8fa2;
+ font-size: 12px;
+ line-height: 1.4;
+ }
+
+ .locmap-kv-value {
+ margin-top: 8px;
+ color: #334155;
+ font-size: 18px;
+ font-weight: 600;
+ line-height: 1.35;
+ word-break: break-all;
+ }
+
+ .locmap-empty {
+ position: absolute;
+ right: 18px;
+ bottom: 18px;
+ z-index: 20;
+ width: min(max(260px, 20vw), calc(100vw - 92px));
+ padding: 18px;
+ border-radius: 18px;
+ border: 1px solid rgba(255, 255, 255, 0.4);
+ background: rgba(248, 251, 253, 0.92);
+ box-shadow: 0 10px 24px rgba(88, 110, 136, 0.08);
+ color: #6b7b8d;
+ font-size: 12px;
+ line-height: 1.7;
+ backdrop-filter: blur(6px);
+ }
+
+ @media (max-width: 960px) {
+ .locmap-title-card {
+ right: 18px;
+ min-width: 0;
+ }
+
+ .locmap-detail-panel {
+ top: auto;
+ height: min(54vh, 460px);
+ width: calc(100vw - 36px);
+ }
+
+ .locmap-empty {
+ width: calc(100vw - 36px);
+ }
+
+ .locmap-kv-grid {
+ grid-template-columns: 1fr;
+ }
}
</style>
</head>
<body>
<div id="app">
- <div id="pixiView">
-
- </div>
-
- <!--杈撳嚭鎿嶄綔鍜孎PS-->
- <div style="position: absolute;top: 20px;right: 50px;">
- <div>FPS:{{mapFps}}</div>
- <el-button @click="drawer = true">鎿嶄綔</el-button>
- </div>
-
- <el-drawer
- title="鎿嶄綔鍖哄煙"
- :visible.sync="drawer"
- :with-header="true"
- :modal="false"
- >
- <div class="floorBtnBox" v-for="(lev,idx) in floorList">
- <el-button :style="{background:currentLev === lev ? '#7DCDFF':''}" @click="changeFloor(lev)">{{lev}}F</el-button>
- </div>
-<!-- <div>-->
-<!-- <el-button @click="testMove()">娴嬭瘯绉诲姩杞�</el-button>-->
-<!-- </div>-->
-<!-- <div style="margin-top: 10px;">-->
-<!-- <el-button @click="resetMap()">閲嶇疆鍦板浘</el-button>-->
-<!-- </div>-->
- </el-drawer>
-
- <el-drawer
- title="搴撲綅璇︽儏"
- :visible.sync="drawerLocNo"
- :with-header="true"
- :modal="false"
- >
- <div v-if="drawerLocNoData!=null">
- <div style="margin: 10px;">
-<!-- <div style="margin-top: 5px;">鎺掞細{{drawerLocNoData.row}}</div>-->
-<!-- <div style="margin-top: 5px;">鍒楋細{{drawerLocNoData.bay}}</div>-->
-<!-- <div style="margin-top: 5px;">灞傦細{{drawerLocNoData.lev}}</div>-->
- <div style="margin-top: 5px;">搴撲綅鍙凤細{{drawerLocNoData.locNo}}</div>
- <div style="margin-top: 5px;">搴撲綅鐘舵�侊細{{drawerLocNoData.locSts}}</div>
- </div>
- </div>
- </el-drawer>
-
- <el-drawer
- title="绔欑偣淇℃伅"
- :visible.sync="drawerSta"
- :with-header="true"
- :modal="false"
- >
- <div v-if="drawerStaData!=null">
- <div style="margin: 10px;">
- <div style="margin-top: 5px;">绔欑偣锛歿{drawerStaData.siteId}}</div>
- <div style="margin-top: 5px;">宸ヤ綔鍙凤細{{drawerStaData.workNo}}</div>
- <div style="margin-top: 5px;">宸ヤ綔鐘舵�侊細{{drawerStaData.wrkSts}}</div>
- <div style="margin-top: 5px;">宸ヤ綔绫诲瀷锛歿{drawerStaData.ioType}}</div>
- <div style="margin-top: 5px;">婧愮珯锛歿{drawerStaData.sourceStaNo}}</div>
- <div style="margin-top: 5px;">鐩爣绔欙細{{drawerStaData.staNo}}</div>
- <div style="margin-top: 5px;">婧愬簱浣嶏細{{drawerStaData.sourceLocNo}}</div>
- <div style="margin-top: 5px;">鐩爣搴撲綅锛歿{drawerStaData.locNo}}</div>
- <div style="margin-top: 5px;">鑷姩锛歿{drawerStaData.autoing}}</div>
- <div style="margin-top: 5px;">鏈夌墿锛歿{drawerStaData.loading}}</div>
- <div style="margin-top: 5px;">鑳藉叆锛歿{drawerStaData.canining}}</div>
- <div style="margin-top: 5px;">鑳藉嚭锛歿{drawerStaData.canouting}}</div>
- <div style="margin-top: 5px;">鑳藉嚭锛歿{drawerStaData.canouting}}</div>
- </div>
- </div>
- </el-drawer>
-
+ <loc-map-canvas></loc-map-canvas>
</div>
-<script>
- let width = 25;
- let height = 25;
- let pixiApp;
- let pixiStageList = [];
- let pixiStaMap = new Map();
- let objectsContainer;
- let objectsContainer3;
- let tracksGraphics;
- let mapRoot;
- let mapContentSize = { width: 0, height: 0 };
- let graphics0;
- let graphicsF;
- let graphics3;
- let graphics4;
- let graphics5;
- let graphics9;
- let graphics67;
- let graphicsLock;
- let ws;
- var app = new Vue({
- el: '#app',
- data: {
- map: [],
- currentLev: 1,
- floorList: [], //褰撳墠椤圭洰妤煎眰
- drawer: false,
- drawerLocNo: false,
- drawerLocNoData: null,
- drawerLocDetls: [],
- reloadMap: true,
- mapFps: 0,
- currentLevStaList: [],//褰撳墠妤煎眰绔欑偣list
- drawerSta: false,
- drawerStaData: null,
- mapRotation: 0,
- mapMirrorX: false,
- mapConfigCodes: {
- rotate: 'map_canvas_rotation',
- mirror: 'map_canvas_mirror_x'
+<script>
+ Vue.component('loc-map-canvas', {
+ template: `
+ <div class="locmap-shell" ref="shell">
+ <div ref="pixiView" class="locmap-canvas"></div>
+
+ <div class="locmap-title-card">
+ <div class="locmap-title">搴撲綅鍦板浘</div>
+ <div class="locmap-title-desc">鐐瑰嚮搴撲綅鍚庡湪鍙充晶鏌ョ湅璇︽儏銆�</div>
+ </div>
+
+ <div class="locmap-tools">
+ <div class="locmap-fps">FPS {{ mapFps }}</div>
+ <button type="button"
+ class="locmap-tool-toggle"
+ :class="{ 'is-active': showMapToolPanel }"
+ @click="toggleMapToolPanel">{{ showMapToolPanel ? '鏀惰捣鎿嶄綔' : '鍦板浘鎿嶄綔' }}</button>
+ <div v-show="showMapToolPanel" class="locmap-tool-panel">
+ <div class="locmap-tool-row">
+ <button type="button" class="locmap-tool-btn" @click="fitStageToContent">閲嶇疆瑙嗗浘</button>
+ <button type="button" class="locmap-tool-btn" @click="rotateMap">鏃嬭浆</button>
+ <button type="button"
+ class="locmap-tool-btn"
+ :class="{ 'is-active': mapMirrorX }"
+ @click="toggleMirror">{{ mapMirrorX ? '鍙栨秷闀滃儚' : '闀滃儚' }}</button>
+ </div>
+ <div v-if="floorList && floorList.length > 0" class="locmap-tool-section">
+ <div class="locmap-tool-label">妤煎眰</div>
+ <div class="locmap-tool-row">
+ <button v-for="lev in floorList"
+ :key="'loc-floor-' + lev"
+ type="button"
+ class="locmap-floor-btn"
+ :class="{ 'is-active': currentLev === lev }"
+ @click="changeFloor(lev)">{{ lev }}F</button>
+ </div>
+ </div>
+ </div>
+ </div>
+
+ <div v-if="detailPanelOpen" class="locmap-detail-panel" ref="detailPanel">
+ <div class="locmap-detail-header">
+ <div class="locmap-detail-title-wrap">
+ <div class="locmap-detail-type">{{ detailTypeLabel }}</div>
+ <div class="locmap-detail-title">{{ detailTitle }}</div>
+ <div class="locmap-detail-subtitle">{{ detailSubtitle }}</div>
+ </div>
+ <button type="button" class="locmap-detail-close" @click="closeDetailPanel">脳</button>
+ </div>
+ <div class="locmap-detail-body">
+ <div class="locmap-kv-grid">
+ <div v-for="item in detailFields" :key="item.label" class="locmap-kv-card">
+ <div class="locmap-kv-label">{{ item.label }}</div>
+ <div class="locmap-kv-value">{{ formatDetailValue(item.value) }}</div>
+ </div>
+ </div>
+ </div>
+ </div>
+
+ <div v-else class="locmap-empty">
+ 鐐瑰嚮搴撲綅鍙煡鐪嬪簱浣嶇姸鎬侊紝鐐瑰嚮绔欑偣鍙煡鐪嬬珯鐐逛綔涓氳鎯呫�傚湴鍥炬搷浣滃湪鍙充笂瑙掑伐鍏烽潰鏉夸腑銆�
+ </div>
+ </div>
+ `,
+ data() {
+ return {
+ cellWidth: 25,
+ cellHeight: 25,
+ pixiApp: null,
+ pixiStageList: [],
+ pixiStaMap: new Map(),
+ pixiTrackMap: new Map(),
+ pixiLabelList: [],
+ mapRoot: null,
+ shelvesContainer: null,
+ objectsContainer: null,
+ objectsOverlayContainer: null,
+ tracksGraphics: null,
+ mapContentSize: { width: 0, height: 0 },
+ textureMap: {},
+ locChunkList: [],
+ locChunkSize: 1024,
+ locCullPadding: 160,
+ locCullRaf: null,
+ ws: null,
+ wsReconnectTimer: null,
+ wsReconnectAttempts: 0,
+ wsReconnectBaseDelay: 1000,
+ wsReconnectMaxDelay: 15000,
+ containerResizeObserver: null,
+ adjustLabelTimer: null,
+ map: [],
+ floorList: [],
+ currentLev: 1,
+ reloadMap: true,
+ mapFps: 0,
+ showMapToolPanel: false,
+ detailPanelOpen: false,
+ detailType: '',
+ detailPayload: null,
+ highlightedSprite: null,
+ highlightedLocCell: null,
+ highlightedLocGraphic: null,
+ mapRotation: 0,
+ mapMirrorX: false,
+ mapConfigCodes: {
+ rotate: 'map_canvas_rotation',
+ mirror: 'map_canvas_mirror_x'
+ }
+ };
+ },
+ computed: {
+ detailTypeLabel() {
+ return this.detailType === 'site' ? '绔欑偣淇℃伅' : '搴撲綅璇︽儏';
+ },
+ detailTitle() {
+ if (this.detailType === 'site' && this.detailPayload) {
+ return '绔欑偣 ' + this.formatDetailValue(this.detailPayload.siteId);
+ }
+ if (this.detailType === 'loc' && this.detailPayload) {
+ return this.formatDetailValue(this.detailPayload.locNo);
+ }
+ return '璇︽儏';
+ },
+ detailSubtitle() {
+ if (this.detailType === 'site') {
+ return '绔欑偣浣滀笟鐘舵�併�佹潵婧愮洰鏍囧拰浣滀笟鍙傛暟';
+ }
+ return '搴撲綅褰撳墠鐘舵�佸拰鎵�鍦ㄦ帓鍒楀眰淇℃伅';
+ },
+ detailFields() {
+ if (!this.detailPayload) { return []; }
+ if (this.detailType === 'site') {
+ return [
+ { label: '绔欑偣', value: this.detailPayload.siteId },
+ { label: '宸ヤ綔鍙�', value: this.detailPayload.workNo },
+ { label: '宸ヤ綔鐘舵��', value: this.detailPayload.wrkSts },
+ { label: '宸ヤ綔绫诲瀷', value: this.detailPayload.ioType },
+ { label: '婧愮珯', value: this.detailPayload.sourceStaNo },
+ { label: '鐩爣绔�', value: this.detailPayload.staNo },
+ { label: '婧愬簱浣�', value: this.detailPayload.sourceLocNo },
+ { label: '鐩爣搴撲綅', value: this.detailPayload.locNo },
+ { label: '鑷姩', value: this.detailPayload.autoing },
+ { label: '鏈夌墿', value: this.detailPayload.loading },
+ { label: '鑳藉叆', value: this.detailPayload.canining },
+ { label: '鑳藉嚭', value: this.detailPayload.canouting }
+ ];
+ }
+ return [
+ { label: '搴撲綅鍙�', value: this.detailPayload.locNo },
+ { label: '搴撲綅鐘舵��', value: this.detailPayload.locSts },
+ { label: '鎺�', value: this.detailPayload.row },
+ { label: '鍒�', value: this.detailPayload.bay },
+ { label: '灞�', value: this.detailPayload.lev }
+ ];
}
},
mounted() {
- this.init()
- this.createMap()
+ this.createMap();
+ this.startContainerResizeObserve();
+ this.loadMapTransformConfig();
+ this.initLev();
+ this.connectWs();
+ setTimeout(() => {
+ this.getMap(this.currentLev);
+ }, 300);
},
- watch: {
- map: {
- deep: true,
- handler(val) {
-
- }
- },
- drawerLocNo: {
- deep: true,
- handler(val) {
- if (!val) {
- var sprite = pixiStageList[this.drawerLocNoData.x][this.drawerLocNoData.y];
- updateColor(sprite, 0xFFFFFF);//鎭㈠棰滆壊
- }
- }
+ beforeDestroy() {
+ if (this.adjustLabelTimer) {
+ clearTimeout(this.adjustLabelTimer);
+ this.adjustLabelTimer = null;
+ }
+ if (this.locCullRaf) {
+ cancelAnimationFrame(this.locCullRaf);
+ this.locCullRaf = null;
+ }
+ if (this.containerResizeObserver) {
+ this.containerResizeObserver.disconnect();
+ this.containerResizeObserver = null;
+ }
+ window.removeEventListener('resize', this.resizeToContainer);
+ if (this.wsReconnectTimer) {
+ clearTimeout(this.wsReconnectTimer);
+ this.wsReconnectTimer = null;
+ }
+ if (this.ws && (this.ws.readyState === WebSocket.OPEN || this.ws.readyState === WebSocket.CONNECTING)) {
+ try { this.ws.close(); } catch (e) {}
+ }
+ if (window.gsap && this.pixiApp && this.pixiApp.stage) {
+ window.gsap.killTweensOf(this.pixiApp.stage.position);
+ }
+ this.clearLocChunks();
+ if (this.pixiApp) {
+ this.pixiApp.destroy(true, { children: true });
+ this.pixiApp = null;
}
},
methods: {
- init(){
- let that = this
- ws = new WebSocket("ws://" + window.location.host + baseUrl + "/console/websocket");
- ws.onopen = this.webSocketOnOpen
- ws.onerror = this.webSocketOnError
- ws.onmessage = this.webSocketOnMessage
- ws.onclose = this.webSocketClose
-
-
- this.loadMapTransformConfig()
- this.initLev()//鍒濆鍖栨ゼ灞備俊鎭�
- setTimeout(() => {
- that.getMap(this.currentLev)
- }, 1000);
-
+ toggleMapToolPanel() {
+ this.showMapToolPanel = !this.showMapToolPanel;
},
- initLev(){
- let that = this
+ formatDetailValue(value) {
+ return value == null || value === '' ? '-' : value;
+ },
+ initLev() {
$.ajax({
url: baseUrl + "/console/map/lev/list",
- headers: {
- 'token': localStorage.getItem('token')
- },
- data: {},
+ headers: { token: localStorage.getItem('token') },
method: 'get',
- success: function(res) {
+ success: (res) => {
if (res.code === 200) {
- that.floorList = res.data;
+ this.floorList = Array.isArray(res.data) ? res.data : [];
+ if (this.floorList.length > 0 && this.floorList.indexOf(this.currentLev) === -1) {
+ this.currentLev = this.floorList[0];
+ }
} else if (res.code === 403) {
parent.location.href = baseUrl + "/login";
} else {
- that.$message({
- message: res.msg,
- type: 'error'
- });
+ this.showMessage('error', res.msg || '妤煎眰淇℃伅鍔犺浇澶辫触');
}
}
});
},
- //鑾峰彇鍦板浘鏁版嵁
getMap(lev) {
- let that = this;
- $.ajax({
- url: baseUrl + "/console/map/" + lev + "/auth",
- headers: {
- 'token': localStorage.getItem('token')
- },
- data: {},
- method: 'get',
- success: function(res) {
- //鑾峰彇鍦板浘鏁版嵁
- let data = res.data
- that.createMapData(data)
- }
- })
+ $.ajax({
+ url: baseUrl + "/console/map/" + lev + "/auth",
+ headers: { token: localStorage.getItem('token') },
+ method: 'get',
+ success: (res) => {
+ if (res.code === 200) {
+ this.reloadMap = true;
+ this.createMapData(res.data || []);
+ } else if (res.code === 403) {
+ parent.location.href = baseUrl + "/login";
+ } else {
+ this.showMessage('error', res.msg || '鍦板浘鍔犺浇澶辫触');
+ }
+ }
+ });
},
changeFloor(lev) {
- this.currentLev = lev
- this.loadMapTransformConfig()
- this.reloadMap = true
- this.getMap(lev)
+ if (this.currentLev === lev) { return; }
+ this.currentLev = lev;
+ this.reloadMap = true;
+ this.closeDetailPanel();
+ this.getMap(lev);
},
- createMap(){
- //Create a Pixi Application
- pixiApp = new PIXI.Application({
- // width: 1500,
- // height: 800,
- backgroundColor: 0xF5F7F9FF,
- resizeTo: window
+ connectWs() {
+ if (this.ws && (this.ws.readyState === WebSocket.OPEN || this.ws.readyState === WebSocket.CONNECTING)) {
+ return;
+ }
+ this.ws = new WebSocket("ws://" + window.location.host + baseUrl + "/console/websocket");
+ this.ws.onopen = this.webSocketOnOpen;
+ this.ws.onerror = this.webSocketOnError;
+ this.ws.onmessage = this.webSocketOnMessage;
+ this.ws.onclose = this.webSocketClose;
+ },
+ scheduleReconnectWs() {
+ if (this.wsReconnectTimer) { return; }
+ const delay = Math.min(this.wsReconnectMaxDelay, this.wsReconnectBaseDelay * Math.pow(2, this.wsReconnectAttempts));
+ this.wsReconnectAttempts += 1;
+ this.wsReconnectTimer = setTimeout(() => {
+ this.wsReconnectTimer = null;
+ this.connectWs();
+ }, delay);
+ },
+ webSocketOnOpen() {
+ this.wsReconnectAttempts = 0;
+ },
+ webSocketOnError() {},
+ webSocketOnMessage(event) {
+ let result = null;
+ try {
+ result = JSON.parse(event.data);
+ } catch (e) {
+ return;
+ }
+ if (!result || !result.url) { return; }
+ if (result.url === "/console/map/auth" || result.url === "/console/locMap/auth") {
+ let data = [];
+ try {
+ data = JSON.parse(result.data || '[]');
+ } catch (e) {
+ data = [];
+ }
+ this.setMap(data);
+ }
+ },
+ webSocketClose() {
+ this.scheduleReconnectWs();
+ },
+ setMap(data) {
+ if (!Array.isArray(data) || this.currentLev == null) { return; }
+ this.reloadMap = true;
+ this.createMapData(data);
+ },
+ showMessage(type, message) {
+ if (this.$message) {
+ this.$message({ type: type, message: message });
+ }
+ },
+ createMap() {
+ this.pixiApp = new PIXI.Application({
+ backgroundColor: 0xEEF4F8,
+ resizeTo: this.$refs.shell,
+ antialias: true,
+ autoDensity: true
});
- //Add the canvas that Pixi automatically created for you to the HTML document
- $("#pixiView").append(pixiApp.view)
+ this.$refs.pixiView.appendChild(this.pixiApp.view);
+ this.createBaseTextures();
- // 浠嶨raphics瀵硅薄鍒涘缓涓�涓汗鐞�
- graphicsF = pixiApp.renderer.generateTexture(getContainer(1000));
- graphics0 = pixiApp.renderer.generateTexture(getContainer(0));
- graphics3 = pixiApp.renderer.generateTexture(getContainer(3));
- graphics4 = pixiApp.renderer.generateTexture(getContainer(4));
- graphics5 = pixiApp.renderer.generateTexture(getContainer(5));
- graphics9 = pixiApp.renderer.generateTexture(getContainer(9));
- graphics67 = pixiApp.renderer.generateTexture(getContainer(67));
- graphicsLock = pixiApp.renderer.generateTexture(getContainer(-999));
+ this.mapRoot = new PIXI.Container();
+ this.pixiApp.stage.addChild(this.mapRoot);
- mapRoot = new PIXI.Container();
- pixiApp.stage.addChild(mapRoot);
- // 鍒涘缓涓�涓鍣ㄦ潵绠$悊澶ф壒閲忕殑鏄剧ず瀵硅薄
- objectsContainer = new PIXI.Container();
- mapRoot.addChild(objectsContainer);
+ this.shelvesContainer = new PIXI.Container();
+ this.mapRoot.addChild(this.shelvesContainer);
- tracksGraphics = new PIXI.Graphics();
- mapRoot.addChild(tracksGraphics);
+ this.objectsContainer = new PIXI.Container();
+ this.mapRoot.addChild(this.objectsContainer);
- // 鍒涘缓涓�涓鍣ㄦ潵绠$悊澶ф壒閲忕殑鏄剧ず瀵硅薄
- objectsContainer3 = new PIXI.Container();
- mapRoot.addChild(objectsContainer3);
+ this.tracksGraphics = new PIXI.Graphics();
+ this.mapRoot.addChild(this.tracksGraphics);
- //*******************鎷栧姩鐢诲竷*******************
- let stageOriginalPos;
- let mouseDownPoint;
+ this.objectsOverlayContainer = new PIXI.Container();
+ this.mapRoot.addChild(this.objectsOverlayContainer);
+
+ this.initStageInteractions();
+ this.initFpsTicker();
+ },
+ createBaseTextures() {
+ this.textureMap = {
+ locEmpty: this.pixiApp.renderer.generateTexture(this.buildCellGraphic(0x5faeff)),
+ locFull: this.pixiApp.renderer.generateTexture(this.buildCellGraphic(0xf05d5d)),
+ site: this.pixiApp.renderer.generateTexture(this.buildCellGraphic(0xf6ca4b)),
+ charge: this.pixiApp.renderer.generateTexture(this.buildCellGraphic(0xffa66b)),
+ elevator: this.pixiApp.renderer.generateTexture(this.buildCellGraphic(0x7dd9ff)),
+ lock: this.pixiApp.renderer.generateTexture(this.buildCellGraphic(0xf83333))
+ };
+ },
+ buildCellGraphic(fillColor) {
+ const graphics = new PIXI.Graphics();
+ graphics.beginFill(fillColor);
+ graphics.lineStyle(1, 0xffffff, 1);
+ graphics.drawRect(0, 0, this.cellWidth, this.cellHeight);
+ graphics.endFill();
+ return graphics;
+ },
+ initStageInteractions() {
+ let stageOriginalPos = null;
+ let mouseDownPoint = null;
let touchBlank = false;
- pixiApp.renderer.plugins.interaction.on(
- 'pointerdown',
- (event) => {
- const globalPos = event.data.global;
- // 璁板綍涓媠tage鍘熸潵鐨勪綅缃�
- stageOriginalPos = [pixiApp.stage.position._x, pixiApp.stage.position._y];
- // 璁板綍涓媘ouse down鐨勪綅缃�
- mouseDownPoint = [globalPos.x, globalPos.y];
- if (!event.target) {
- // 鐐瑰埌浜嗙敾甯冪殑绌虹櫧浣嶇疆
- touchBlank = true;
- }
- }
- );
+ let pointerDownMoved = false;
+ const interaction = this.pixiApp.renderer.plugins.interaction;
- pixiApp.renderer.plugins.interaction.on(
- 'pointermove',
- (event) => {
- const globalPos = event.data.global;
+ interaction.on('pointerdown', (event) => {
+ const globalPos = event.data.global;
+ stageOriginalPos = [this.pixiApp.stage.position.x, this.pixiApp.stage.position.y];
+ mouseDownPoint = [globalPos.x, globalPos.y];
+ pointerDownMoved = false;
+ touchBlank = !event.target;
+ });
- if (touchBlank) {
- // 鎷栨嫿鐢诲竷
- const dx = globalPos.x - mouseDownPoint[0];
- const dy = globalPos.y - mouseDownPoint[1];
- pixiApp.stage.position.set(
- stageOriginalPos[0] + dx,
- stageOriginalPos[1] + dy
- );
- }
- }
- );
+ interaction.on('pointermove', (event) => {
+ const globalPos = event.data.global;
+ if (mouseDownPoint) {
+ const dragDx = globalPos.x - mouseDownPoint[0];
+ const dragDy = globalPos.y - mouseDownPoint[1];
+ if (Math.abs(dragDx) > 4 || Math.abs(dragDy) > 4) {
+ pointerDownMoved = true;
+ }
+ }
+ if (!touchBlank || !stageOriginalPos || !mouseDownPoint) { return; }
+ const dx = globalPos.x - mouseDownPoint[0];
+ const dy = globalPos.y - mouseDownPoint[1];
+ this.pixiApp.stage.position.set(stageOriginalPos[0] + dx, stageOriginalPos[1] + dy);
+ this.scheduleAdjustLabels();
+ this.scheduleLocChunkCulling();
+ });
- pixiApp.renderer.plugins.interaction.on(
- 'pointerup',
- (event) => {
- touchBlank = false;
- }
- );
- //*******************鎷栧姩鐢诲竷*******************
+ interaction.on('pointerup', (event) => {
+ if (touchBlank && !pointerDownMoved && event && event.data && event.data.global) {
+ this.handleBlankPointerTap(event.data.global);
+ }
+ touchBlank = false;
+ mouseDownPoint = null;
+ stageOriginalPos = null;
+ });
- //*******************缂╂斁鐢诲竷*******************
- pixiApp.view.addEventListener('wheel', (event) => {
+ interaction.on('pointerupoutside', () => {
+ touchBlank = false;
+ mouseDownPoint = null;
+ stageOriginalPos = null;
+ });
+
+ this.pixiApp.view.addEventListener('wheel', (event) => {
event.stopPropagation();
event.preventDefault();
- const rect = pixiApp.view.getBoundingClientRect();
+ const rect = this.pixiApp.view.getBoundingClientRect();
const sx = event.clientX - rect.left;
const sy = event.clientY - rect.top;
- const oldZoomX = pixiApp.stage.scale.x || 1;
- const oldZoomY = pixiApp.stage.scale.y || 1;
+ const oldZoomX = this.pixiApp.stage.scale.x || 1;
+ const oldZoomY = this.pixiApp.stage.scale.y || 1;
const oldZoomAbs = Math.abs(oldZoomX) || 1;
- const delta = event.deltaY;
- let newZoomAbs = oldZoomAbs * 0.999 ** delta;
+ let newZoomAbs = oldZoomAbs * Math.pow(0.999, event.deltaY);
+ newZoomAbs = Math.max(0.08, Math.min(newZoomAbs, 8));
const mirrorX = this.mapMirrorX ? -1 : 1;
const newZoomX = mirrorX * newZoomAbs;
const newZoomY = newZoomAbs;
- const worldX = (sx - pixiApp.stage.position.x) / oldZoomX;
- const worldY = (sy - pixiApp.stage.position.y) / oldZoomY;
+ const worldX = (sx - this.pixiApp.stage.position.x) / oldZoomX;
+ const worldY = (sy - this.pixiApp.stage.position.y) / oldZoomY;
const newPosX = sx - worldX * newZoomX;
const newPosY = sy - worldY * newZoomY;
- pixiApp.stage.setTransform(newPosX, newPosY, newZoomX, newZoomY, 0, 0, 0, 0, 0);
-
- });
- //*******************缂╂斁鐢诲竷*******************
-
- //*******************FPS*******************
- let g_Time = 0;
- let fpsLastUpdateTs = 0;
- let fpsDeltaSumMs = 0;
- let fpsFrameCount = 0;
- const fpsUpdateInterval = 200;
- pixiApp.ticker.add((delta) => {
- const timeNow = (new Date()).getTime();
- const timeDiff = timeNow - g_Time;
- g_Time = timeNow;
- fpsDeltaSumMs += timeDiff;
- fpsFrameCount += 1;
- if (timeNow - fpsLastUpdateTs >= fpsUpdateInterval) {
- const avgFps = fpsDeltaSumMs > 0 ? (fpsFrameCount * 1000 / fpsDeltaSumMs) : 0;
- this.mapFps = Math.round(avgFps);
- fpsDeltaSumMs = 0;
- fpsFrameCount = 0;
- fpsLastUpdateTs = timeNow;
+ this.pixiApp.stage.setTransform(newPosX, newPosY, newZoomX, newZoomY, 0, 0, 0, 0, 0);
+ this.scheduleAdjustLabels();
+ this.scheduleLocChunkCulling();
+ }, { passive: false });
+ },
+ initFpsTicker() {
+ let lastTs = 0;
+ let deltaSum = 0;
+ let frameCount = 0;
+ const updateInterval = 200;
+ this.pixiApp.ticker.add(() => {
+ const now = Date.now();
+ if (!lastTs) {
+ lastTs = now;
+ return;
+ }
+ deltaSum += now - lastTs;
+ frameCount += 1;
+ lastTs = now;
+ if (deltaSum >= updateInterval) {
+ this.mapFps = deltaSum > 0 ? Math.round(frameCount * 1000 / deltaSum) : 0;
+ deltaSum = 0;
+ frameCount = 0;
}
});
- //*******************FPS*******************
-
},
createMapData(map) {
- if (this.reloadMap) {
- this.reloadMap = false
- pixiStageList = [map.length]//鍒濆鍖栧垪琛�
- pixiStaMap = new Map();//閲嶇疆
- objectsContainer.removeChildren()
- if (tracksGraphics) { tracksGraphics.clear(); }
- map.forEach((item,index) => {
- pixiStageList[index] = [item.length]
- for (let idx = 0; idx < item.length; idx++) {
- let val = item[idx]
- if (val.value < 0 && (val.value != -999)) {
- continue;
- }
+ if (!this.reloadMap) {
+ this.map = map;
+ return;
+ }
+ this.reloadMap = false;
+ this.map = map;
+ this.pixiStageList = [];
+ this.pixiStaMap = new Map();
+ this.pixiLabelList = [];
+ this.restoreHighlightedSprite();
+ this.clearLocHighlight();
+ this.clearLocChunks();
+ this.objectsContainer.removeChildren();
+ this.objectsOverlayContainer.removeChildren();
+ if (this.tracksGraphics) { this.tracksGraphics.clear(); }
- let sprite = getSprite(val.value, idx * width, index * height, val, (e) => {
- if (val.value == 4) {
- //绔欑偣
- this.openDrawerSta(val)
- }else {
- //搴撲綅
- this.rightEvent(index, idx, e);
- updateColor(sprite, 0x9900ff);
- }
- });
+ let rows = Array.isArray(map) ? map.length : 0;
+ let maxCols = 0;
+ map.forEach((row, rowIndex) => {
+ if (!Array.isArray(row)) { return; }
+ this.pixiStageList[rowIndex] = [];
+ maxCols = Math.max(maxCols, row.length);
+ row.forEach((cell, colIndex) => {
+ if (cell.value < 0 && cell.value !== -999) { return; }
+ if (parseInt(cell.value, 10) === 0) {
+ this.pixiStageList[rowIndex][colIndex] = null;
+ return;
+ }
+ if (this.isTrackCell(cell)) {
+ cell.trackMask = this.resolveTrackMask(map, rowIndex, colIndex);
+ }
+ const sprite = this.createSprite(cell, rowIndex, colIndex);
+ if (cell.value === -999) {
+ this.objectsOverlayContainer.addChild(sprite);
+ } else {
+ this.objectsContainer.addChild(sprite);
+ }
+ this.pixiStageList[rowIndex][colIndex] = sprite;
+ });
+ });
- if (val.value == 4) {
- // 鍒涘缓鏂囨湰瀵硅薄
- const style = new PIXI.TextStyle({
- fontFamily: 'Arial',
- fontSize: 10,
- fill: '#000000',
- });
- const text = new PIXI.Text(val.data, style);
- text.anchor.set(0.5); // 璁剧疆鏂囨湰閿氱偣涓轰腑蹇冪偣
- text.position.set(sprite.width / 2, sprite.height / 2); // 灏嗘枃鏈綅缃缃负Graphics瀵硅薄鐨勪腑蹇冪偣
- // 灏嗘枃鏈璞℃坊鍔犲埌Graphics瀵硅薄涓�
- sprite.addChild(text);
- sprite.textObj = text;
- pixiStaMap.set(parseInt(val.data), sprite);//绔欑偣鏁版嵁娣诲姞鍒癿ap涓�
- }else if (val.value == 67) {
- // 鍒涘缓鎻愬崌鏈烘枃鏈璞�
- const style = new PIXI.TextStyle({
- fontFamily: 'Arial',
- fontSize: 10,
- fill: '#000000',
- });
- const text = new PIXI.Text(val.data, style);
- text.anchor.set(0.5); // 璁剧疆鏂囨湰閿氱偣涓轰腑蹇冪偣
- text.position.set(sprite.width / 2, sprite.height / 2); // 灏嗘枃鏈綅缃缃负Graphics瀵硅薄鐨勪腑蹇冪偣
- // 灏嗘枃鏈璞℃坊鍔犲埌Graphics瀵硅薄涓�
- sprite.addChild(text);
- sprite.textObj = text;
- pixiStaMap.set(parseInt(val.data), sprite);//绔欑偣鏁版嵁娣诲姞鍒癿ap涓�
- }
+ this.mapContentSize = {
+ width: maxCols * this.cellWidth,
+ height: rows * this.cellHeight
+ };
+ this.buildLocChunks(map, this.mapContentSize.width, this.mapContentSize.height);
+ this.drawTracks(map);
+ this.applyMapTransform(true);
+ },
+ createSprite(cell, rowIndex, colIndex) {
+ const sprite = this.isTrackCell(cell)
+ ? this.createTrackSprite(this.cellWidth, this.cellHeight, cell.trackMask)
+ : new PIXI.Sprite(this.getTextureForCell(cell));
+ sprite.position.set(colIndex * this.cellWidth, rowIndex * this.cellHeight);
+ sprite.baseTint = 0xFFFFFF;
+ sprite.cellData = cell;
- if (val.value == -999) {
- pixiShuttleLockPathMap.set(val.locNo, sprite);
- objectsContainer3.addChild(sprite);
- }else {
- objectsContainer.addChild(sprite);
- }
- pixiStageList[index][idx] = sprite
+ if (cell.value === 4 || cell.value === 67) {
+ this.attachCellLabel(sprite, cell.data);
+ if (cell.value === 4 && cell.data != null) {
+ this.pixiStaMap.set(parseInt(cell.data, 10), sprite);
+ }
+ }
+
+ if (!this.isTrackCell(cell)) {
+ sprite.interactive = true;
+ sprite.buttonMode = true;
+ sprite.on('pointerdown', () => {
+ if (cell.value === 4) {
+ this.openSiteDetail(cell, sprite);
+ } else {
+ this.openLocDetail(rowIndex, colIndex, sprite);
}
});
-
- const b1 = objectsContainer.getLocalBounds();
- const minX = b1.x;
- const minY = b1.y;
- const maxX = b1.x + b1.width;
- const maxY = b1.y + b1.height;
- const contentW = Math.max(0, maxX - minX);
- const contentH = Math.max(0, maxY - minY);
- mapContentSize = { width: contentW, height: contentH };
- this.drawTracks(map);
- this.applyMapTransform(true);
}
- this.map = map;
+ return sprite;
+ },
+ createTrackSprite(width, height, mask) {
+ const trackMask = mask != null ? mask : 10;
+ const key = width + '-' + height + '-' + trackMask;
+ let texture = this.pixiTrackMap.get(key);
+ if (!texture) {
+ texture = this.createTrackTexture(width, height, trackMask);
+ this.pixiTrackMap.set(key, texture);
+ }
+ return new PIXI.Sprite(texture);
+ },
+ createTrackTexture(width, height, mask) {
+ const TRACK_N = 1;
+ const TRACK_E = 2;
+ const TRACK_S = 4;
+ const TRACK_W = 8;
+ const trackMask = mask != null ? mask : (TRACK_E | TRACK_W);
+ const g = new PIXI.Graphics();
+ const size = Math.max(1, Math.min(width, height));
+ const rail = Math.max(2, Math.round(size * 0.12));
+ const gap = Math.max(4, Math.round(size * 0.38));
+ const midX = Math.round(width / 2);
+ const midY = Math.round(height / 2);
+
+ const hasN = (trackMask & TRACK_N) !== 0;
+ const hasE = (trackMask & TRACK_E) !== 0;
+ const hasS = (trackMask & TRACK_S) !== 0;
+ const hasW = (trackMask & TRACK_W) !== 0;
+
+ const hasH = hasW || hasE;
+ const hasV = hasN || hasS;
+ const isCorner = hasH && hasV && !(hasW && hasE) && !(hasN && hasS);
+ const railColor = 0x555555;
+
+ if (hasH && !isCorner) {
+ const hStart = hasW ? 0 : midX;
+ const hEnd = hasE ? width : midX;
+ const hWidth = Math.max(1, hEnd - hStart);
+ g.beginFill(railColor);
+ g.drawRect(hStart, midY - Math.round(rail / 2), hWidth, rail);
+ g.endFill();
+ }
+ if (hasV && !isCorner) {
+ const vStart = hasN ? 0 : midY;
+ const vEnd = hasS ? height : midY;
+ const vHeight = Math.max(1, vEnd - vStart);
+ g.beginFill(railColor);
+ g.drawRect(midX - Math.round(rail / 2), vStart, rail, vHeight);
+ g.endFill();
+ }
+ if (isCorner) {
+ const cornerEast = hasE;
+ const cornerSouth = hasS;
+ const centerX = cornerEast ? (width - 1) : 0;
+ const centerY = cornerSouth ? (height - 1) : 0;
+ const angleStart = (cornerEast && cornerSouth) ? Math.PI : (cornerEast ? Math.PI / 2 : (cornerSouth ? -Math.PI / 2 : 0));
+ const angleEnd = (cornerEast && cornerSouth) ? Math.PI * 1.5 : (cornerEast ? Math.PI : (cornerSouth ? 0 : Math.PI / 2));
+ const radius = Math.min(Math.abs(centerX - midX), Math.abs(centerY - midY));
+ g.lineStyle(rail, railColor, 1);
+ g.arc(centerX, centerY, radius, angleStart, angleEnd);
+ g.lineStyle(0, 0, 0);
+ }
+
+ const rt = PIXI.RenderTexture.create({ width: width, height: height });
+ this.pixiApp.renderer.render(g, rt);
+ return rt;
+ },
+ attachCellLabel(sprite, textValue) {
+ const text = new PIXI.Text(String(textValue == null ? '' : textValue), {
+ fontFamily: 'Arial',
+ fontSize: 10,
+ fill: '#1f2937',
+ align: 'center'
+ });
+ text.anchor.set(0.5);
+ text.position.set(sprite.width / 2, sprite.height / 2);
+ sprite.addChild(text);
+ sprite.textObj = text;
+ this.pixiLabelList.push(sprite);
+ },
+ getTextureForCell(cell) {
+ const value = parseInt(cell.value, 10);
+ if (value === 4) { return this.textureMap.site; }
+ if (value === 5) { return this.textureMap.charge; }
+ if (value === 67) { return this.textureMap.elevator; }
+ if (value === -999) { return this.textureMap.lock; }
+ return this.textureMap.locEmpty;
},
isTrackCell(cell) {
if (!cell) { return false; }
const type = cell.type ? String(cell.type).toLowerCase() : '';
if (type === 'track' || type === 'crn' || type === 'dualcrn' || type === 'rgv') { return true; }
if (cell.trackSiteNo != null) { return true; }
- const v = parseInt(cell.value, 10);
- if (v === 3 || v === 9) { return true; }
- if (cell.value != null) {
- try {
- const obj = (typeof cell.value === 'string') ? JSON.parse(cell.value) : cell.value;
- if (obj && (obj.trackSiteNo != null || (obj.deviceNo != null && (type === 'crn' || type === 'dualcrn' || type === 'rgv')))) {
- return true;
- }
- } catch (e) {}
+ const value = parseInt(cell.value, 10);
+ return value === 3 || value === 9;
+ },
+ resolveTrackMask(map, rowIndex, colIndex) {
+ const TRACK_N = 1;
+ const TRACK_E = 2;
+ const TRACK_S = 4;
+ const TRACK_W = 8;
+ const row = Array.isArray(map) ? map[rowIndex] : null;
+ const cell = row && row[colIndex] ? row[colIndex] : null;
+ if (!this.isTrackCell(cell)) {
+ return TRACK_E | TRACK_W;
}
- return false;
+ let mask = 0;
+ const north = rowIndex > 0 && Array.isArray(map[rowIndex - 1]) ? map[rowIndex - 1][colIndex] : null;
+ const east = row && colIndex + 1 < row.length ? row[colIndex + 1] : null;
+ const south = rowIndex + 1 < map.length && Array.isArray(map[rowIndex + 1]) ? map[rowIndex + 1][colIndex] : null;
+ const west = row && colIndex > 0 ? row[colIndex - 1] : null;
+ if (north && this.isTrackCell(north)) { mask |= TRACK_N; }
+ if (east && this.isTrackCell(east)) { mask |= TRACK_E; }
+ if (south && this.isTrackCell(south)) { mask |= TRACK_S; }
+ if (west && this.isTrackCell(west)) { mask |= TRACK_W; }
+ return mask || (TRACK_E | TRACK_W);
},
drawTracks(map) {
- if (!tracksGraphics || !Array.isArray(map)) { return; }
- tracksGraphics.clear();
- const railColor = 0x6c727a;
- const railWidth = Math.max(1, Math.round(Math.min(width, height) * 0.08));
- tracksGraphics.lineStyle(railWidth, railColor, 1);
-
- for (let r = 0; r < map.length; r++) {
- const row = map[r];
+ if (this.tracksGraphics) { this.tracksGraphics.clear(); }
+ },
+ buildLocChunks(map, contentW, contentH) {
+ this.clearLocChunks();
+ if (!this.pixiApp || !this.pixiApp.renderer || !this.shelvesContainer || !Array.isArray(map)) { return; }
+ const chunkSize = Math.max(256, parseInt(this.locChunkSize, 10) || 1024);
+ const chunkMap = new Map();
+ for (let rowIndex = 0; rowIndex < map.length; rowIndex += 1) {
+ const row = map[rowIndex];
if (!Array.isArray(row)) { continue; }
- for (let c = 0; c < row.length; c++) {
- const cell = row[c];
- if (!this.isTrackCell(cell)) { continue; }
- const cx = c * width + width / 2;
- const cy = r * height + height / 2;
- const up = (r - 1 >= 0 && Array.isArray(map[r - 1])) ? map[r - 1][c] : null;
- const right = (c + 1 < row.length) ? row[c + 1] : null;
- const down = (r + 1 < map.length && Array.isArray(map[r + 1])) ? map[r + 1][c] : null;
- const left = (c - 1 >= 0) ? row[c - 1] : null;
- const hasN = this.isTrackCell(up);
- const hasE = this.isTrackCell(right);
- const hasS = this.isTrackCell(down);
- const hasW = this.isTrackCell(left);
- const seg = Math.min(width, height) * 0.5;
- let drew = false;
- if (hasN) { tracksGraphics.moveTo(cx, cy); tracksGraphics.lineTo(cx, cy - seg); drew = true; }
- if (hasE) { tracksGraphics.moveTo(cx, cy); tracksGraphics.lineTo(cx + seg, cy); drew = true; }
- if (hasS) { tracksGraphics.moveTo(cx, cy); tracksGraphics.lineTo(cx, cy + seg); drew = true; }
- if (hasW) { tracksGraphics.moveTo(cx, cy); tracksGraphics.lineTo(cx - seg, cy); drew = true; }
- if (!drew) {
- tracksGraphics.moveTo(cx - seg * 0.4, cy);
- tracksGraphics.lineTo(cx + seg * 0.4, cy);
+ for (let colIndex = 0; colIndex < row.length; colIndex += 1) {
+ const cell = row[colIndex];
+ if (!cell || parseInt(cell.value, 10) !== 0) { continue; }
+ const posX = colIndex * this.cellWidth;
+ const posY = rowIndex * this.cellHeight;
+ const chunkX = Math.floor(posX / chunkSize);
+ const chunkY = Math.floor(posY / chunkSize);
+ const key = chunkX + ',' + chunkY;
+ let list = chunkMap.get(key);
+ if (!list) {
+ list = [];
+ chunkMap.set(key, list);
}
+ list.push({
+ x: posX,
+ y: posY,
+ width: this.cellWidth,
+ height: this.cellHeight,
+ color: cell.locSts === 'F' ? 0xf05d5d : 0x5faeff
+ });
}
}
+
+ const chunkList = [];
+ chunkMap.forEach((cells, key) => {
+ const keyParts = key.split(',');
+ const chunkX = parseInt(keyParts[0], 10) || 0;
+ const chunkY = parseInt(keyParts[1], 10) || 0;
+ const chunkLeft = chunkX * chunkSize;
+ const chunkTop = chunkY * chunkSize;
+ const chunkWidth = Math.max(1, Math.min(chunkSize, contentW - chunkLeft));
+ const chunkHeight = Math.max(1, Math.min(chunkSize, contentH - chunkTop));
+ const graphics = new PIXI.Graphics();
+ for (let i = 0; i < cells.length; i += 1) {
+ const cell = cells[i];
+ graphics.beginFill(cell.color);
+ graphics.lineStyle(1, 0xffffff, 1);
+ graphics.drawRect(cell.x - chunkLeft, cell.y - chunkTop, cell.width, cell.height);
+ graphics.endFill();
+ }
+ const texture = this.pixiApp.renderer.generateTexture(
+ graphics,
+ PIXI.SCALE_MODES.LINEAR,
+ 1,
+ new PIXI.Rectangle(0, 0, chunkWidth, chunkHeight)
+ );
+ graphics.destroy(true);
+ const sprite = new PIXI.Sprite(texture);
+ sprite.position.set(chunkLeft, chunkTop);
+ sprite._chunkBounds = {
+ x: chunkLeft,
+ y: chunkTop,
+ width: chunkWidth,
+ height: chunkHeight
+ };
+ this.shelvesContainer.addChild(sprite);
+ chunkList.push(sprite);
+ });
+ this.locChunkList = chunkList;
+ this.updateVisibleLocChunks();
+ },
+ clearLocChunks() {
+ if (this.locCullRaf) {
+ cancelAnimationFrame(this.locCullRaf);
+ this.locCullRaf = null;
+ }
+ this.locChunkList = [];
+ if (!this.shelvesContainer) { return; }
+ const children = this.shelvesContainer.removeChildren();
+ children.forEach((child) => {
+ if (child && typeof child.destroy === 'function') {
+ child.destroy({ children: true, texture: true, baseTexture: true });
+ }
+ });
+ },
+ getViewportLocalBounds(padding) {
+ if (!this.mapRoot || !this.pixiApp) { return null; }
+ const viewport = this.getViewportSize();
+ const pad = Math.max(0, Number(padding) || 0);
+ const points = [
+ new PIXI.Point(-pad, -pad),
+ new PIXI.Point(viewport.width + pad, -pad),
+ new PIXI.Point(-pad, viewport.height + pad),
+ new PIXI.Point(viewport.width + pad, viewport.height + pad)
+ ];
+ let minX = Infinity;
+ let minY = Infinity;
+ let maxX = -Infinity;
+ let maxY = -Infinity;
+ points.forEach((point) => {
+ const local = this.mapRoot.toLocal(point);
+ if (local.x < minX) { minX = local.x; }
+ if (local.y < minY) { minY = local.y; }
+ if (local.x > maxX) { maxX = local.x; }
+ if (local.y > maxY) { maxY = local.y; }
+ });
+ if (!isFinite(minX) || !isFinite(minY) || !isFinite(maxX) || !isFinite(maxY)) { return null; }
+ return { minX: minX, minY: minY, maxX: maxX, maxY: maxY };
+ },
+ updateVisibleLocChunks() {
+ if (!this.locChunkList || this.locChunkList.length === 0) { return; }
+ const localBounds = this.getViewportLocalBounds(this.locCullPadding);
+ if (!localBounds) { return; }
+ for (let i = 0; i < this.locChunkList.length; i += 1) {
+ const sprite = this.locChunkList[i];
+ const bounds = sprite && sprite._chunkBounds;
+ if (!bounds) { continue; }
+ const visible = bounds.x < localBounds.maxX &&
+ bounds.x + bounds.width > localBounds.minX &&
+ bounds.y < localBounds.maxY &&
+ bounds.y + bounds.height > localBounds.minY;
+ if (sprite.visible !== visible) {
+ sprite.visible = visible;
+ }
+ }
+ },
+ scheduleLocChunkCulling() {
+ if (this.locCullRaf) { return; }
+ this.locCullRaf = requestAnimationFrame(() => {
+ this.locCullRaf = null;
+ this.updateVisibleLocChunks();
+ });
+ },
+ handleBlankPointerTap(globalPos) {
+ if (!globalPos || !this.mapRoot || !Array.isArray(this.map) || this.map.length === 0) { return; }
+ const local = this.mapRoot.toLocal(new PIXI.Point(globalPos.x, globalPos.y));
+ const rowIndex = Math.floor(local.y / this.cellHeight);
+ const colIndex = Math.floor(local.x / this.cellWidth);
+ if (rowIndex < 0 || colIndex < 0 || rowIndex >= this.map.length) {
+ this.closeDetailPanel();
+ return;
+ }
+ const row = this.map[rowIndex];
+ if (!Array.isArray(row) || colIndex >= row.length) {
+ this.closeDetailPanel();
+ return;
+ }
+ const cell = row[colIndex];
+ if (!cell || parseInt(cell.value, 10) !== 0) {
+ this.closeDetailPanel();
+ return;
+ }
+ this.openLocDetail(rowIndex, colIndex);
+ },
+ parseLocNoMeta(locNo) {
+ if (locNo == null || locNo === '') { return { row: null, bay: null, lev: null }; }
+ const parts = String(locNo).split('-');
+ return {
+ row: parts.length > 0 ? parts[0] : null,
+ bay: parts.length > 1 ? parts[1] : null,
+ lev: parts.length > 2 ? parts[2] : null
+ };
+ },
+ openLocDetail(rowIndex, colIndex, sprite) {
+ const cell = this.map[rowIndex] && this.map[rowIndex][colIndex] ? this.map[rowIndex][colIndex] : null;
+ if (!cell) { return; }
+ const locMeta = this.parseLocNoMeta(cell.locNo);
+ if (sprite) {
+ this.highlightSprite(sprite, 0x915eff);
+ } else {
+ this.restoreHighlightedSprite();
+ this.highlightLocCell(rowIndex, colIndex);
+ }
+ this.detailType = 'loc';
+ this.detailPayload = {
+ locNo: cell.locNo,
+ locSts: cell.locSts,
+ row: cell.row != null ? cell.row : locMeta.row,
+ bay: cell.bay != null ? cell.bay : locMeta.bay,
+ lev: cell.lev != null ? cell.lev : (locMeta.lev != null ? locMeta.lev : this.currentLev)
+ };
+ this.detailPanelOpen = true;
+ },
+ openSiteDetail(cell, sprite) {
+ this.clearLocHighlight();
+ this.highlightSprite(sprite, 0xff7e47);
+ $.ajax({
+ url: baseUrl + "/console/site/detail",
+ headers: { token: localStorage.getItem('token') },
+ data: { siteId: cell.data },
+ method: 'post',
+ success: (res) => {
+ if (res.code === 200) {
+ this.detailType = 'site';
+ this.detailPayload = res.data || {};
+ this.detailPanelOpen = true;
+ } else if (res.code === 403) {
+ parent.location.href = baseUrl + "/login";
+ } else {
+ this.showMessage('error', res.msg || '绔欑偣璇︽儏鍔犺浇澶辫触');
+ }
+ }
+ });
+ },
+ highlightSprite(sprite, tint) {
+ this.restoreHighlightedSprite();
+ if (!sprite) { return; }
+ sprite.tint = tint;
+ this.highlightedSprite = sprite;
+ },
+ restoreHighlightedSprite() {
+ if (this.highlightedSprite) {
+ this.highlightedSprite.tint = this.highlightedSprite.baseTint || 0xFFFFFF;
+ this.highlightedSprite = null;
+ }
+ },
+ highlightLocCell(rowIndex, colIndex) {
+ this.clearLocHighlight();
+ this.highlightedLocCell = { rowIndex: rowIndex, colIndex: colIndex };
+ const graphic = new PIXI.Graphics();
+ graphic.lineStyle(2, 0x915eff, 0.95);
+ graphic.beginFill(0x915eff, 0.14);
+ graphic.drawRect(colIndex * this.cellWidth, rowIndex * this.cellHeight, this.cellWidth, this.cellHeight);
+ graphic.endFill();
+ this.objectsOverlayContainer.addChild(graphic);
+ this.highlightedLocGraphic = graphic;
+ },
+ clearLocHighlight() {
+ this.highlightedLocCell = null;
+ if (this.highlightedLocGraphic) {
+ if (this.highlightedLocGraphic.parent) {
+ this.highlightedLocGraphic.parent.removeChild(this.highlightedLocGraphic);
+ }
+ this.highlightedLocGraphic.destroy(true);
+ this.highlightedLocGraphic = null;
+ }
+ },
+ closeDetailPanel() {
+ this.detailPanelOpen = false;
+ this.detailType = '';
+ this.detailPayload = null;
+ this.restoreHighlightedSprite();
+ this.clearLocHighlight();
},
parseRotation(value) {
const num = parseInt(value, 10);
if (!isFinite(num)) { return 0; }
- const rot = ((num % 360) + 360) % 360;
- return (rot === 90 || rot === 180 || rot === 270) ? rot : 0;
+ const rotation = ((num % 360) + 360) % 360;
+ return rotation === 90 || rotation === 180 || rotation === 270 ? rotation : 0;
},
parseMirror(value) {
if (value === true || value === false) { return value; }
@@ -490,11 +1222,45 @@
const str = String(value).toLowerCase();
return str === '1' || str === 'true' || str === 'y';
},
+ buildMissingMapConfigList(byCode) {
+ const list = [];
+ if (!byCode[this.mapConfigCodes.rotate]) {
+ list.push({
+ name: '鍦板浘鏃嬭浆',
+ code: this.mapConfigCodes.rotate,
+ value: String(this.mapRotation || 0),
+ type: 1,
+ status: 1,
+ selectType: 'map'
+ });
+ }
+ if (!byCode[this.mapConfigCodes.mirror]) {
+ list.push({
+ name: '鍦板浘闀滃儚',
+ code: this.mapConfigCodes.mirror,
+ value: this.mapMirrorX ? '1' : '0',
+ type: 1,
+ status: 1,
+ selectType: 'map'
+ });
+ }
+ return list;
+ },
+ createMapConfigs(list) {
+ if (!Array.isArray(list) || list.length === 0) { return; }
+ list.forEach((cfg) => {
+ $.ajax({
+ url: baseUrl + "/config/add/auth",
+ headers: { token: localStorage.getItem('token') },
+ method: 'POST',
+ data: cfg
+ });
+ });
+ },
loadMapTransformConfig() {
- if (!window.$ || typeof baseUrl === 'undefined') { return; }
$.ajax({
url: baseUrl + "/config/listAll/auth",
- headers: { 'token': localStorage.getItem('token') },
+ headers: { token: localStorage.getItem('token') },
dataType: 'json',
method: 'GET',
success: (res) => {
@@ -504,210 +1270,148 @@
}
const byCode = {};
res.data.forEach((item) => {
- if (item && item.code) { byCode[item.code] = item; }
+ if (item && item.code) {
+ byCode[item.code] = item;
+ }
});
- const rotateCfg = byCode[this.mapConfigCodes.rotate];
- const mirrorCfg = byCode[this.mapConfigCodes.mirror];
- if (rotateCfg && rotateCfg.value != null) {
- this.mapRotation = this.parseRotation(rotateCfg.value);
+ if (byCode[this.mapConfigCodes.rotate] && byCode[this.mapConfigCodes.rotate].value != null) {
+ this.mapRotation = this.parseRotation(byCode[this.mapConfigCodes.rotate].value);
}
- if (mirrorCfg && mirrorCfg.value != null) {
- this.mapMirrorX = this.parseMirror(mirrorCfg.value);
+ if (byCode[this.mapConfigCodes.mirror] && byCode[this.mapConfigCodes.mirror].value != null) {
+ this.mapMirrorX = this.parseMirror(byCode[this.mapConfigCodes.mirror].value);
}
- if (mapContentSize && mapContentSize.width > 0 && mapContentSize.height > 0) {
+ this.createMapConfigs(this.buildMissingMapConfigList(byCode));
+ if (this.mapContentSize.width > 0 && this.mapContentSize.height > 0) {
this.applyMapTransform(true);
}
}
});
},
+ saveMapTransformConfig() {
+ $.ajax({
+ url: baseUrl + "/config/updateBatch",
+ headers: { token: localStorage.getItem('token') },
+ data: JSON.stringify([
+ { code: this.mapConfigCodes.rotate, value: String(this.mapRotation || 0) },
+ { code: this.mapConfigCodes.mirror, value: this.mapMirrorX ? '1' : '0' }
+ ]),
+ dataType: 'json',
+ contentType: 'application/json;charset=UTF-8',
+ method: 'POST'
+ });
+ },
+ rotateMap() {
+ this.mapRotation = (this.mapRotation + 90) % 360;
+ this.applyMapTransform(true);
+ this.saveMapTransformConfig();
+ },
+ toggleMirror() {
+ this.mapMirrorX = !this.mapMirrorX;
+ this.applyMapTransform(true);
+ this.saveMapTransformConfig();
+ },
getViewportSize() {
- if (!pixiApp || !pixiApp.renderer) { return { width: 0, height: 0 }; }
- const screen = pixiApp.renderer.screen;
+ if (!this.pixiApp || !this.pixiApp.renderer) { return { width: 0, height: 0 }; }
+ const screen = this.pixiApp.renderer.screen;
if (screen && screen.width > 0 && screen.height > 0) {
return { width: screen.width, height: screen.height };
}
- const rect = pixiApp.view ? pixiApp.view.getBoundingClientRect() : null;
+ const rect = this.pixiApp.view ? this.pixiApp.view.getBoundingClientRect() : null;
return { width: rect ? rect.width : 0, height: rect ? rect.height : 0 };
},
+ getViewportPadding() {
+ return { top: 24, right: 24, bottom: 24, left: 24 };
+ },
getTransformedContentSize() {
- const size = mapContentSize || { width: 0, height: 0 };
- const w = size.width || 0;
- const h = size.height || 0;
- const rot = ((this.mapRotation % 360) + 360) % 360;
- const swap = rot === 90 || rot === 270;
- return { width: swap ? h : w, height: swap ? w : h };
+ const width = this.mapContentSize.width || 0;
+ const height = this.mapContentSize.height || 0;
+ const rotation = ((this.mapRotation % 360) + 360) % 360;
+ const swap = rotation === 90 || rotation === 270;
+ return { width: swap ? height : width, height: swap ? width : height };
},
fitStageToContent() {
- if (!pixiApp || !mapContentSize) { return; }
+ if (!this.pixiApp || !this.mapContentSize.width || !this.mapContentSize.height) { return; }
const size = this.getTransformedContentSize();
- const contentW = size.width || 0;
- const contentH = size.height || 0;
- if (contentW <= 0 || contentH <= 0) { return; }
const viewport = this.getViewportSize();
- const vw = viewport.width;
- const vh = viewport.height;
- let scale = Math.min(vw / contentW, vh / contentH) * 0.95;
+ const padding = this.getViewportPadding();
+ const availableW = Math.max(1, viewport.width - padding.left - padding.right);
+ const availableH = Math.max(1, viewport.height - padding.top - padding.bottom);
+ let scale = Math.min(availableW / size.width, availableH / size.height) * 0.95;
if (!isFinite(scale) || scale <= 0) { scale = 1; }
- const baseW = mapContentSize.width || contentW;
- const baseH = mapContentSize.height || contentH;
const mirrorX = this.mapMirrorX ? -1 : 1;
const scaleX = scale * mirrorX;
const scaleY = scale;
- const posX = (vw / 2) - (baseW / 2) * scaleX;
- const posY = (vh / 2) - (baseH / 2) * scaleY;
- pixiApp.stage.setTransform(posX, posY, scaleX, scaleY, 0, 0, 0, 0, 0);
+ const centerX = padding.left + availableW / 2;
+ const centerY = padding.top + availableH / 2;
+ const posX = centerX - (this.mapContentSize.width / 2) * scaleX;
+ const posY = centerY - (this.mapContentSize.height / 2) * scaleY;
+ this.pixiApp.stage.setTransform(posX, posY, scaleX, scaleY, 0, 0, 0, 0, 0);
+ this.scheduleAdjustLabels();
+ this.scheduleLocChunkCulling();
},
applyMapTransform(fitToView) {
- if (!mapRoot || !mapContentSize) { return; }
- const contentW = mapContentSize.width || 0;
- const contentH = mapContentSize.height || 0;
- if (contentW <= 0 || contentH <= 0) { return; }
- mapRoot.pivot.set(contentW / 2, contentH / 2);
- mapRoot.position.set(contentW / 2, contentH / 2);
- mapRoot.rotation = (this.mapRotation % 360) * Math.PI / 180;
- mapRoot.scale.set(1, 1);
- if (fitToView) { this.fitStageToContent(); }
- },
- rightEvent(x, y, e) {
- this.drawerLocNo = true
- this.drawerLocNoData = {x:x, y: y, z: this.currentLev, locNo: this.map[x][y].locNo,
- locSts: this.map[x][y].locSts,row:this.map[x][y].row, bay: this.map[x][y].bay, lev: this.currentLev};
- },
- webSocketOnOpen(e) {
- console.log("open");
- },
- webSocketOnError(e) {
- console.log(e);
- },
- webSocketOnMessage(e) {
- const result = JSON.parse(e.data);
- if (result.url == "/console/map/auth") {
- this.setMap(JSON.parse(result.data))
- }else if (result.url == "/console/locMap/auth") {
- this.setMap(JSON.parse(result.data))
+ if (!this.mapRoot || !this.mapContentSize.width || !this.mapContentSize.height) { return; }
+ const contentW = this.mapContentSize.width;
+ const contentH = this.mapContentSize.height;
+ this.mapRoot.pivot.set(contentW / 2, contentH / 2);
+ this.mapRoot.position.set(contentW / 2, contentH / 2);
+ this.mapRoot.rotation = (this.mapRotation % 360) * Math.PI / 180;
+ this.mapRoot.scale.set(1, 1);
+ if (fitToView) {
+ this.fitStageToContent();
+ } else {
+ this.scheduleAdjustLabels();
+ this.scheduleLocChunkCulling();
}
},
- webSocketClose(e) {
- console.log("close");
- },
- sendWs(message) {
- if (ws.readyState == WebSocket.OPEN) {
- ws.send(message)
+ scheduleAdjustLabels() {
+ if (this.adjustLabelTimer) {
+ clearTimeout(this.adjustLabelTimer);
}
+ this.adjustLabelTimer = setTimeout(() => {
+ this.adjustLabelScale();
+ this.adjustLabelTimer = null;
+ }, 20);
},
- openDrawerSta(data) {
- let that = this
- this.drawerSta = true;
- $.ajax({
- url: baseUrl + "/console/site/detail",
- headers: {
- 'token': localStorage.getItem('token')
- },
- data: {
- siteId: data.data
- },
- method: 'post',
- success: function(res) {
- //瑙f瀽鏁版嵁
- let siteInfo = res.data;
- if (res.code == 200) {
- that.drawerStaData = siteInfo;
- }
- }
- })
+ adjustLabelScale() {
+ if (!this.pixiApp || !this.pixiLabelList.length) { return; }
+ const scaleX = this.pixiApp.stage.scale.x || 1;
+ const scaleY = this.pixiApp.stage.scale.y || 1;
+ const scale = Math.max(Math.abs(scaleX), Math.abs(scaleY), 0.0001);
+ const mirrorSign = scaleX < 0 ? -1 : 1;
+ const inverseRotation = -this.mapRoot.rotation;
+ const visible = scale >= 0.25;
+ this.pixiLabelList.forEach((sprite) => {
+ if (!sprite || !sprite.textObj) { return; }
+ const textObj = sprite.textObj;
+ let textScale = 1 / scale;
+ textScale = Math.max(0.9, Math.min(textScale, 3));
+ textObj.scale.set(textScale * mirrorSign, textScale);
+ textObj.rotation = inverseRotation;
+ textObj.visible = visible;
+ textObj.position.set(sprite.width / 2, sprite.height / 2);
+ });
},
- }
- })
-
- function getContainer(value) {
- let graphics = new PIXI.Graphics();
- if (value === 0) {
- graphics.beginFill(0x55aaff);
- } else if (value === 3) {//姣嶈建閬�
- graphics.beginFill(0x00ff7f);
- graphics.visible = true;
- } else if (value === 4) {//绔欑偣
- graphics.beginFill(0xffff00);
- graphics.visible = true;
- } else if (value === 5) {//鍏呯數妗�
- graphics.beginFill(0xffaa7f);
- graphics.visible = true;
- } else if (value === 9) {//杞ㄨ抗
- graphics.beginFill(0xff0000);
- }else if (value === 67) {//鎻愬崌鏈�
- graphics.beginFill(0xaaffff);
- }else if (value === -999) {//璺緞閿佸畾
- graphics.beginFill(0xf83333);
- }else if (value === 1000) {//婊″簱浣�
- graphics.beginFill(0xf83333);
- }
- graphics.lineStyle(1, 0xffffff, 1);
- graphics.drawRect(0, 0, width, height);
- graphics.endFill();
-
- return graphics;
- }
-
- function getGraphics(color, width, height, x, y) {
- let graphics = new PIXI.Graphics();
- graphics.beginFill(color);
- graphics.lineStyle(1, 0xffffff, 1);
- graphics.drawRect(0, 0, width, height);
- graphics.position.set(x, y);
- graphics.endFill();
- return graphics;
- }
-
- function getSprite(value, x, y, item, pointerDownEvent) {
- let sprite;
- if (value == 0) {
- if(item.locSts == 'O') {
- sprite = new PIXI.Sprite(graphics0);
- }else if(item.locSts == 'F') {
- sprite = new PIXI.Sprite(graphicsF);
- }else {
- sprite = new PIXI.Sprite(graphics0);
+ startContainerResizeObserve() {
+ this.resizeToContainer = () => {
+ if (!this.pixiApp || !this.mapContentSize.width || !this.mapContentSize.height) { return; }
+ this.fitStageToContent();
+ };
+ if (window.ResizeObserver) {
+ this.containerResizeObserver = new ResizeObserver(() => {
+ this.resizeToContainer();
+ });
+ this.containerResizeObserver.observe(this.$refs.shell);
+ } else {
+ window.addEventListener('resize', this.resizeToContainer);
+ }
}
- } else if (value == 3) {
- sprite = new PIXI.Sprite(graphics3);
- } else if (value == 4) {
- sprite = new PIXI.Sprite(graphics4);
- } else if (value == 5) {
- sprite = new PIXI.Sprite(graphics5);
- } else if (value == 9) {
- sprite = new PIXI.Sprite(graphics9);
- } else if (value == 67) {
- sprite = new PIXI.Sprite(graphics67);
- } else if (value == -999) {
- sprite = new PIXI.Sprite(graphicsLock);
- } else {
- sprite = new PIXI.Sprite(graphics0);
}
- sprite.position.set(x, y);
- const type = item && item.type ? String(item.type).toLowerCase() : '';
- const numVal = parseInt(value, 10);
- const isTrackCell = numVal === 3 || numVal === 9 || type === 'track' || type === 'crn' || type === 'dualcrn' || type === 'rgv';
- if (!isTrackCell) {
- sprite.interactive = true; // 蹇呴』瑕佽缃墠鑳芥帴鏀朵簨浠�
- sprite.buttonMode = true; // 璁╁厜鏍囧湪hover鏃跺彉涓烘墜鍨嬫寚閽�
- sprite.on('pointerdown', (e) => {
- pointerDownEvent(e)
- })
- }
+ });
- return sprite;
- }
-
- /**
- * 鏇存柊棰滆壊
- */
- function updateColor(sprite, color) {
- // graphics.clear()
- // graphics.beginFill(color);
- // graphics.drawRect(0, 0, width, height);
- sprite.tint = color;
- }
-
+ new Vue({
+ el: '#app'
+ });
</script>
</body>
</html>
--
Gitblit v1.9.1