فهرست منبع

fix: 画板制作

bill 1 سال پیش
والد
کامیت
4658a13e62

+ 1 - 0
package.json

@@ -15,6 +15,7 @@
     "gl-matrix": "^3.4.3",
     "js-base64": "^3.7.7",
     "jszip": "^3.10.1",
+    "konva": "9.3.6",
     "mitt": "^3.0.1",
     "ol": "^9.1.0",
     "proj4": "^2.11.0",

+ 225 - 231
pnpm-lock.yaml

@@ -10,6 +10,7 @@ specifiers:
   gl-matrix: ^3.4.3
   js-base64: ^3.7.7
   jszip: ^3.10.1
+  konva: 9.3.6
   mitt: ^3.0.1
   ol: ^9.1.0
   proj4: ^2.11.0
@@ -23,52 +24,53 @@ specifiers:
   xlsx: ^0.18.5
 
 dependencies:
-  '@element-plus/icons-vue': 2.3.1_vue@3.4.21
-  '@types/node': 20.12.2
-  element-plus: 2.6.3_vue@3.4.21
+  '@element-plus/icons-vue': 2.3.1_vue@3.4.27
+  '@types/node': 20.12.12
+  element-plus: 2.7.3_vue@3.4.27
   gl-matrix: 3.4.3
   js-base64: 3.7.7
   jszip: 3.10.1
+  konva: 9.3.6
   mitt: 3.0.1
-  ol: 9.1.0
+  ol: 9.2.4
   proj4: 2.11.0
   qrcode: 1.5.3
-  vue: 3.4.21_typescript@5.4.3
-  vue-router: 4.3.0_vue@3.4.21
+  vue: 3.4.27_typescript@5.4.5
+  vue-router: 4.3.2_vue@3.4.27
   xlsx: 0.18.5
 
 devDependencies:
   '@types/proj4': 2.5.5
   '@types/qrcode': 1.5.5
-  '@vitejs/plugin-vue': 5.0.4_vite@5.2.7+vue@3.4.21
-  sass: 1.72.0
-  typescript: 5.4.3
-  vite: 5.2.7_2viu4ejjkxxzbuz7bxvuj6jcbe
-  vue-tsc: 2.0.7_typescript@5.4.3
+  '@vitejs/plugin-vue': 5.0.4_vite@5.2.12+vue@3.4.27
+  sass: 1.77.2
+  typescript: 5.4.5
+  vite: 5.2.12_q232fjyrpkfhcrmg5coltkhjqi
+  vue-tsc: 2.0.19_typescript@5.4.5
 
 packages:
 
-  /@babel/helper-string-parser/7.24.1:
-    resolution: {integrity: sha512-2ofRCjnnA9y+wk8b9IAREroeUP02KHp431N2mhKniy2yKIDKpbrHv9eXwm8cBeWQYcJmzv5qKCu65P47eCF7CQ==}
+  /@babel/helper-string-parser/7.24.6:
+    resolution: {integrity: sha512-WdJjwMEkmBicq5T9fm/cHND3+UlFa2Yj8ALLgmoSQAJZysYbBjw+azChSGPN4DSPLXOcooGRvDwZWMcF/mLO2Q==}
     engines: {node: '>=6.9.0'}
 
-  /@babel/helper-validator-identifier/7.22.20:
-    resolution: {integrity: sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==}
+  /@babel/helper-validator-identifier/7.24.6:
+    resolution: {integrity: sha512-4yA7s865JHaqUdRbnaxarZREuPTHrjpDT+pXoAZ1yhyo6uFnIEpS8VMu16siFOHDpZNKYv5BObhsB//ycbICyw==}
     engines: {node: '>=6.9.0'}
 
-  /@babel/parser/7.24.1:
-    resolution: {integrity: sha512-Zo9c7N3xdOIQrNip7Lc9wvRPzlRtovHVE4lkz8WEDr7uYh/GMQhSiIgFxGIArRHYdJE5kxtZjAf8rT0xhdLCzg==}
+  /@babel/parser/7.24.6:
+    resolution: {integrity: sha512-eNZXdfU35nJC2h24RznROuOpO94h6x8sg9ju0tT9biNtLZ2vuP8SduLqqV+/8+cebSLV9SJEAN5Z3zQbJG/M+Q==}
     engines: {node: '>=6.0.0'}
     hasBin: true
     dependencies:
-      '@babel/types': 7.24.0
+      '@babel/types': 7.24.6
 
-  /@babel/types/7.24.0:
-    resolution: {integrity: sha512-+j7a5c253RfKh8iABBhywc8NSfP5LURe7Uh4qpsh6jc+aLJguvmIUBdjSdEMQv2bENrCR5MfRdjGo7vzS/ob7w==}
+  /@babel/types/7.24.6:
+    resolution: {integrity: sha512-WaMsgi6Q8zMgMth93GvWPXkhAIEobfsIkLTacoVZoK1J0CevIPGYY2Vo5YvJGqyHqXM6P4ppOYGsIRU8MM9pFQ==}
     engines: {node: '>=6.9.0'}
     dependencies:
-      '@babel/helper-string-parser': 7.24.1
-      '@babel/helper-validator-identifier': 7.22.20
+      '@babel/helper-string-parser': 7.24.6
+      '@babel/helper-validator-identifier': 7.24.6
       to-fast-properties: 2.0.0
 
   /@ctrl/tinycolor/3.6.1:
@@ -76,12 +78,12 @@ packages:
     engines: {node: '>=10'}
     dev: false
 
-  /@element-plus/icons-vue/2.3.1_vue@3.4.21:
+  /@element-plus/icons-vue/2.3.1_vue@3.4.27:
     resolution: {integrity: sha512-XxVUZv48RZAd87ucGS48jPf6pKu0yV5UCg9f4FFwtrYxXOwWuVJo6wOvSLKEoMQKjv8GsX/mhP6UsC1lRwbUWg==}
     peerDependencies:
       vue: ^3.2.0
     dependencies:
-      vue: 3.4.21_typescript@5.4.3
+      vue: 3.4.27_typescript@5.4.5
     dev: false
 
   /@esbuild/aix-ppc64/0.20.2:
@@ -291,151 +293,152 @@ packages:
     dev: true
     optional: true
 
-  /@floating-ui/core/1.6.0:
-    resolution: {integrity: sha512-PcF++MykgmTj3CIyOQbKA/hDzOAiqI3mhuoN44WRCopIs1sgoDoU4oty4Jtqaj/y3oDU6fnVSm4QG0a3t5i0+g==}
+  /@floating-ui/core/1.6.2:
+    resolution: {integrity: sha512-+2XpQV9LLZeanU4ZevzRnGFg2neDeKHgFLjP6YLW+tly0IvrhqT4u8enLGjLH3qeh85g19xY5rsAusfwTdn5lg==}
     dependencies:
-      '@floating-ui/utils': 0.2.1
+      '@floating-ui/utils': 0.2.2
     dev: false
 
-  /@floating-ui/dom/1.6.3:
-    resolution: {integrity: sha512-RnDthu3mzPlQ31Ss/BTwQ1zjzIhr3lk1gZB1OC56h/1vEtaXkESrOqL5fQVMfXpwGtRwX+YsZBdyHtJMQnkArw==}
+  /@floating-ui/dom/1.6.5:
+    resolution: {integrity: sha512-Nsdud2X65Dz+1RHjAIP0t8z5e2ff/IRbei6BqFrl1urT8sDVzM1HMQ+R0XcU5ceRfyO3I6ayeqIfh+6Wb8LGTw==}
     dependencies:
-      '@floating-ui/core': 1.6.0
-      '@floating-ui/utils': 0.2.1
+      '@floating-ui/core': 1.6.2
+      '@floating-ui/utils': 0.2.2
     dev: false
 
-  /@floating-ui/utils/0.2.1:
-    resolution: {integrity: sha512-9TANp6GPoMtYzQdt54kfAyMmz1+osLlXdg2ENroU7zzrtflTLrrC/lgrIfaSe+Wu0b89GKccT7vxXA0MoAIO+Q==}
+  /@floating-ui/utils/0.2.2:
+    resolution: {integrity: sha512-J4yDIIthosAsRZ5CPYP/jQvUAQtlZTTD/4suA08/FEnlxqW3sKS9iAhgsa9VYLZ6vDHn/ixJgIqRQPotoBjxIw==}
     dev: false
 
   /@jridgewell/sourcemap-codec/1.4.15:
     resolution: {integrity: sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==}
 
-  /@petamoriken/float16/3.8.6:
-    resolution: {integrity: sha512-GNJhABTtcmt9al/nqdJPycwFD46ww2+q2zwZzTjY0dFFwUAFRw9zszvEr9osyJRd9krRGy6hUDopWUg9fX7VVw==}
+  /@petamoriken/float16/3.8.7:
+    resolution: {integrity: sha512-/Ri4xDDpe12NT6Ex/DRgHzLlobiQXEW/hmG08w1wj/YU7hLemk97c+zHQFp0iZQ9r7YqgLEXZR2sls4HxBf9NA==}
     dev: false
 
-  /@rollup/rollup-android-arm-eabi/4.13.2:
-    resolution: {integrity: sha512-3XFIDKWMFZrMnao1mJhnOT1h2g0169Os848NhhmGweEcfJ4rCi+3yMCOLG4zA61rbJdkcrM/DjVZm9Hg5p5w7g==}
+  /@rollup/rollup-android-arm-eabi/4.18.0:
+    resolution: {integrity: sha512-Tya6xypR10giZV1XzxmH5wr25VcZSncG0pZIjfePT0OVBvqNEurzValetGNarVrGiq66EBVAFn15iYX4w6FKgQ==}
     cpu: [arm]
     os: [android]
     requiresBuild: true
     dev: true
     optional: true
 
-  /@rollup/rollup-android-arm64/4.13.2:
-    resolution: {integrity: sha512-GdxxXbAuM7Y/YQM9/TwwP+L0omeE/lJAR1J+olu36c3LqqZEBdsIWeQ91KBe6nxwOnb06Xh7JS2U5ooWU5/LgQ==}
+  /@rollup/rollup-android-arm64/4.18.0:
+    resolution: {integrity: sha512-avCea0RAP03lTsDhEyfy+hpfr85KfyTctMADqHVhLAF3MlIkq83CP8UfAHUssgXTYd+6er6PaAhx/QGv4L1EiA==}
     cpu: [arm64]
     os: [android]
     requiresBuild: true
     dev: true
     optional: true
 
-  /@rollup/rollup-darwin-arm64/4.13.2:
-    resolution: {integrity: sha512-mCMlpzlBgOTdaFs83I4XRr8wNPveJiJX1RLfv4hggyIVhfB5mJfN4P8Z6yKh+oE4Luz+qq1P3kVdWrCKcMYrrA==}
+  /@rollup/rollup-darwin-arm64/4.18.0:
+    resolution: {integrity: sha512-IWfdwU7KDSm07Ty0PuA/W2JYoZ4iTj3TUQjkVsO/6U+4I1jN5lcR71ZEvRh52sDOERdnNhhHU57UITXz5jC1/w==}
     cpu: [arm64]
     os: [darwin]
     requiresBuild: true
     dev: true
     optional: true
 
-  /@rollup/rollup-darwin-x64/4.13.2:
-    resolution: {integrity: sha512-yUoEvnH0FBef/NbB1u6d3HNGyruAKnN74LrPAfDQL3O32e3k3OSfLrPgSJmgb3PJrBZWfPyt6m4ZhAFa2nZp2A==}
+  /@rollup/rollup-darwin-x64/4.18.0:
+    resolution: {integrity: sha512-n2LMsUz7Ynu7DoQrSQkBf8iNrjOGyPLrdSg802vk6XT3FtsgX6JbE8IHRvposskFm9SNxzkLYGSq9QdpLYpRNA==}
     cpu: [x64]
     os: [darwin]
     requiresBuild: true
     dev: true
     optional: true
 
-  /@rollup/rollup-linux-arm-gnueabihf/4.13.2:
-    resolution: {integrity: sha512-GYbLs5ErswU/Xs7aGXqzc3RrdEjKdmoCrgzhJWyFL0r5fL3qd1NPcDKDowDnmcoSiGJeU68/Vy+OMUluRxPiLQ==}
+  /@rollup/rollup-linux-arm-gnueabihf/4.18.0:
+    resolution: {integrity: sha512-C/zbRYRXFjWvz9Z4haRxcTdnkPt1BtCkz+7RtBSuNmKzMzp3ZxdM28Mpccn6pt28/UWUCTXa+b0Mx1k3g6NOMA==}
     cpu: [arm]
     os: [linux]
     requiresBuild: true
     dev: true
     optional: true
 
-  /@rollup/rollup-linux-arm64-gnu/4.13.2:
-    resolution: {integrity: sha512-L1+D8/wqGnKQIlh4Zre9i4R4b4noxzH5DDciyahX4oOz62CphY7WDWqJoQ66zNR4oScLNOqQJfNSIAe/6TPUmQ==}
+  /@rollup/rollup-linux-arm-musleabihf/4.18.0:
+    resolution: {integrity: sha512-l3m9ewPgjQSXrUMHg93vt0hYCGnrMOcUpTz6FLtbwljo2HluS4zTXFy2571YQbisTnfTKPZ01u/ukJdQTLGh9A==}
+    cpu: [arm]
+    os: [linux]
+    requiresBuild: true
+    dev: true
+    optional: true
+
+  /@rollup/rollup-linux-arm64-gnu/4.18.0:
+    resolution: {integrity: sha512-rJ5D47d8WD7J+7STKdCUAgmQk49xuFrRi9pZkWoRD1UeSMakbcepWXPF8ycChBoAqs1pb2wzvbY6Q33WmN2ftw==}
     cpu: [arm64]
     os: [linux]
-    libc: [glibc]
     requiresBuild: true
     dev: true
     optional: true
 
-  /@rollup/rollup-linux-arm64-musl/4.13.2:
-    resolution: {integrity: sha512-tK5eoKFkXdz6vjfkSTCupUzCo40xueTOiOO6PeEIadlNBkadH1wNOH8ILCPIl8by/Gmb5AGAeQOFeLev7iZDOA==}
+  /@rollup/rollup-linux-arm64-musl/4.18.0:
+    resolution: {integrity: sha512-be6Yx37b24ZwxQ+wOQXXLZqpq4jTckJhtGlWGZs68TgdKXJgw54lUUoFYrg6Zs/kjzAQwEwYbp8JxZVzZLRepQ==}
     cpu: [arm64]
     os: [linux]
-    libc: [musl]
     requiresBuild: true
     dev: true
     optional: true
 
-  /@rollup/rollup-linux-powerpc64le-gnu/4.13.2:
-    resolution: {integrity: sha512-zvXvAUGGEYi6tYhcDmb9wlOckVbuD+7z3mzInCSTACJ4DQrdSLPNUeDIcAQW39M3q6PDquqLWu7pnO39uSMRzQ==}
-    cpu: [ppc64le]
+  /@rollup/rollup-linux-powerpc64le-gnu/4.18.0:
+    resolution: {integrity: sha512-hNVMQK+qrA9Todu9+wqrXOHxFiD5YmdEi3paj6vP02Kx1hjd2LLYR2eaN7DsEshg09+9uzWi2W18MJDlG0cxJA==}
+    cpu: [ppc64]
     os: [linux]
-    libc: [glibc]
     requiresBuild: true
     dev: true
     optional: true
 
-  /@rollup/rollup-linux-riscv64-gnu/4.13.2:
-    resolution: {integrity: sha512-C3GSKvMtdudHCN5HdmAMSRYR2kkhgdOfye4w0xzyii7lebVr4riCgmM6lRiSCnJn2w1Xz7ZZzHKuLrjx5620kw==}
+  /@rollup/rollup-linux-riscv64-gnu/4.18.0:
+    resolution: {integrity: sha512-ROCM7i+m1NfdrsmvwSzoxp9HFtmKGHEqu5NNDiZWQtXLA8S5HBCkVvKAxJ8U+CVctHwV2Gb5VUaK7UAkzhDjlg==}
     cpu: [riscv64]
     os: [linux]
-    libc: [glibc]
     requiresBuild: true
     dev: true
     optional: true
 
-  /@rollup/rollup-linux-s390x-gnu/4.13.2:
-    resolution: {integrity: sha512-l4U0KDFwzD36j7HdfJ5/TveEQ1fUTjFFQP5qIt9gBqBgu1G8/kCaq5Ok05kd5TG9F8Lltf3MoYsUMw3rNlJ0Yg==}
+  /@rollup/rollup-linux-s390x-gnu/4.18.0:
+    resolution: {integrity: sha512-0UyyRHyDN42QL+NbqevXIIUnKA47A+45WyasO+y2bGJ1mhQrfrtXUpTxCOrfxCR4esV3/RLYyucGVPiUsO8xjg==}
     cpu: [s390x]
     os: [linux]
-    libc: [glibc]
     requiresBuild: true
     dev: true
     optional: true
 
-  /@rollup/rollup-linux-x64-gnu/4.13.2:
-    resolution: {integrity: sha512-xXMLUAMzrtsvh3cZ448vbXqlUa7ZL8z0MwHp63K2IIID2+DeP5iWIT6g1SN7hg1VxPzqx0xZdiDM9l4n9LRU1A==}
+  /@rollup/rollup-linux-x64-gnu/4.18.0:
+    resolution: {integrity: sha512-xuglR2rBVHA5UsI8h8UbX4VJ470PtGCf5Vpswh7p2ukaqBGFTnsfzxUBetoWBWymHMxbIG0Cmx7Y9qDZzr648w==}
     cpu: [x64]
     os: [linux]
-    libc: [glibc]
     requiresBuild: true
     dev: true
     optional: true
 
-  /@rollup/rollup-linux-x64-musl/4.13.2:
-    resolution: {integrity: sha512-M/JYAWickafUijWPai4ehrjzVPKRCyDb1SLuO+ZyPfoXgeCEAlgPkNXewFZx0zcnoIe3ay4UjXIMdXQXOZXWqA==}
+  /@rollup/rollup-linux-x64-musl/4.18.0:
+    resolution: {integrity: sha512-LKaqQL9osY/ir2geuLVvRRs+utWUNilzdE90TpyoX0eNqPzWjRm14oMEE+YLve4k/NAqCdPkGYDaDF5Sw+xBfg==}
     cpu: [x64]
     os: [linux]
-    libc: [musl]
     requiresBuild: true
     dev: true
     optional: true
 
-  /@rollup/rollup-win32-arm64-msvc/4.13.2:
-    resolution: {integrity: sha512-2YWwoVg9KRkIKaXSh0mz3NmfurpmYoBBTAXA9qt7VXk0Xy12PoOP40EFuau+ajgALbbhi4uTj3tSG3tVseCjuA==}
+  /@rollup/rollup-win32-arm64-msvc/4.18.0:
+    resolution: {integrity: sha512-7J6TkZQFGo9qBKH0pk2cEVSRhJbL6MtfWxth7Y5YmZs57Pi+4x6c2dStAUvaQkHQLnEQv1jzBUW43GvZW8OFqA==}
     cpu: [arm64]
     os: [win32]
     requiresBuild: true
     dev: true
     optional: true
 
-  /@rollup/rollup-win32-ia32-msvc/4.13.2:
-    resolution: {integrity: sha512-2FSsE9aQ6OWD20E498NYKEQLneShWes0NGMPQwxWOdws35qQXH+FplabOSP5zEe1pVjurSDOGEVCE2agFwSEsw==}
+  /@rollup/rollup-win32-ia32-msvc/4.18.0:
+    resolution: {integrity: sha512-Txjh+IxBPbkUB9+SXZMpv+b/vnTEtFyfWZgJ6iyCmt2tdx0OF5WhFowLmnh8ENGNpfUlUZkdI//4IEmhwPieNg==}
     cpu: [ia32]
     os: [win32]
     requiresBuild: true
     dev: true
     optional: true
 
-  /@rollup/rollup-win32-x64-msvc/4.13.2:
-    resolution: {integrity: sha512-7h7J2nokcdPePdKykd8wtc8QqqkqxIrUz7MHj6aNr8waBRU//NLDVnNjQnqQO6fqtjrtCdftpbTuOKAyrAQETQ==}
+  /@rollup/rollup-win32-x64-msvc/4.18.0:
+    resolution: {integrity: sha512-UOo5FdvOL0+eIVTgS4tIdbW+TtnBLWg1YBCcU2KWM7nuNwRz9bksDX1bekJJCpu25N1DVWaCwnT39dVQxzqS8g==}
     cpu: [x64]
     os: [win32]
     requiresBuild: true
@@ -453,15 +456,15 @@ packages:
   /@types/lodash-es/4.17.12:
     resolution: {integrity: sha512-0NgftHUcV4v34VhXm8QBSftKVXtbkBG3ViCjs6+eJ5a6y6Mi/jiFGPc1sC7QK+9BFhWrURE3EOggmWaSxL9OzQ==}
     dependencies:
-      '@types/lodash': 4.17.0
+      '@types/lodash': 4.17.4
     dev: false
 
-  /@types/lodash/4.17.0:
-    resolution: {integrity: sha512-t7dhREVv6dbNj0q17X12j7yDG4bD/DHYX7o5/DbDxobP0HnGPgpRz2Ej77aL7TZT3DSw13fqUTj8J4mMnqa7WA==}
+  /@types/lodash/4.17.4:
+    resolution: {integrity: sha512-wYCP26ZLxaT3R39kiN2+HcJ4kTd3U1waI/cY7ivWYqFP6pW3ZNpvi6Wd6PHZx7T/t8z0vlkXMg3QYLa7DZ/IJQ==}
     dev: false
 
-  /@types/node/20.12.2:
-    resolution: {integrity: sha512-zQ0NYO87hyN6Xrclcqp7f8ZbXNbRfoGWNcMvHTPQp9UUrwI0mI7XBz+cu7/W6/VClYo2g63B0cjull/srU7LgQ==}
+  /@types/node/20.12.12:
+    resolution: {integrity: sha512-eWLDGF/FOSPtAvEqeRAQ4C8LSA7M1I7i0ky1I8U7kD1J5ITyW3AsRhQrKVoWf5pFKZ2kILsEGJhsI9r93PYnOw==}
     dependencies:
       undici-types: 5.26.5
 
@@ -472,136 +475,136 @@ packages:
   /@types/qrcode/1.5.5:
     resolution: {integrity: sha512-CdfBi/e3Qk+3Z/fXYShipBT13OJ2fDO2Q2w5CIP5anLTLIndQG9z6P1cnm+8zCWSpm5dnxMFd/uREtb0EXuQzg==}
     dependencies:
-      '@types/node': 20.12.2
+      '@types/node': 20.12.12
     dev: true
 
   /@types/web-bluetooth/0.0.16:
     resolution: {integrity: sha512-oh8q2Zc32S6gd/j50GowEjKLoOVOwHP/bWVjKJInBwQqdOYMdPrf1oVlelTlyfFK3CKxL1uahMDAr+vy8T7yMQ==}
     dev: false
 
-  /@vitejs/plugin-vue/5.0.4_vite@5.2.7+vue@3.4.21:
+  /@vitejs/plugin-vue/5.0.4_vite@5.2.12+vue@3.4.27:
     resolution: {integrity: sha512-WS3hevEszI6CEVEx28F8RjTX97k3KsrcY6kvTg7+Whm5y3oYvcqzVeGCU3hxSAn4uY2CLCkeokkGKpoctccilQ==}
     engines: {node: ^18.0.0 || >=20.0.0}
     peerDependencies:
       vite: ^5.0.0
       vue: ^3.2.25
     dependencies:
-      vite: 5.2.7_2viu4ejjkxxzbuz7bxvuj6jcbe
-      vue: 3.4.21_typescript@5.4.3
+      vite: 5.2.12_q232fjyrpkfhcrmg5coltkhjqi
+      vue: 3.4.27_typescript@5.4.5
     dev: true
 
-  /@volar/language-core/2.1.6:
-    resolution: {integrity: sha512-pAlMCGX/HatBSiDFMdMyqUshkbwWbLxpN/RL7HCQDOo2gYBE+uS+nanosLc1qR6pTQ/U8q00xt8bdrrAFPSC0A==}
+  /@volar/language-core/2.2.5:
+    resolution: {integrity: sha512-2htyAuxRrAgETmFeUhT4XLELk3LiEcqoW/B8YUXMF6BrGWLMwIR09MFaZYvrA2UhbdAeSyeQ726HaWSWkexUcQ==}
     dependencies:
-      '@volar/source-map': 2.1.6
+      '@volar/source-map': 2.2.5
     dev: true
 
-  /@volar/source-map/2.1.6:
-    resolution: {integrity: sha512-TeyH8pHHonRCHYI91J7fWUoxi0zWV8whZTVRlsWHSYfjm58Blalkf9LrZ+pj6OiverPTmrHRkBsG17ScQyWECw==}
+  /@volar/source-map/2.2.5:
+    resolution: {integrity: sha512-wrOEIiZNf4E+PWB0AxyM4tfhkfldPsb3bxg8N6FHrxJH2ohar7aGu48e98bp3pR9HUA7P/pR9VrLmkTrgCCnWQ==}
     dependencies:
       muggle-string: 0.4.1
     dev: true
 
-  /@volar/typescript/2.1.6:
-    resolution: {integrity: sha512-JgPGhORHqXuyC3r6skPmPHIZj4LoMmGlYErFTuPNBq9Nhc9VTv7ctHY7A3jMN3ngKEfRrfnUcwXHztvdSQqNfw==}
+  /@volar/typescript/2.2.5:
+    resolution: {integrity: sha512-eSV/n75+ppfEVugMC/salZsI44nXDPAyL6+iTYCNLtiLHGJsnMv9GwiDMujrvAUj/aLQyqRJgYtXRoxop2clCw==}
     dependencies:
-      '@volar/language-core': 2.1.6
+      '@volar/language-core': 2.2.5
       path-browserify: 1.0.1
     dev: true
 
-  /@vue/compiler-core/3.4.21:
-    resolution: {integrity: sha512-MjXawxZf2SbZszLPYxaFCjxfibYrzr3eYbKxwpLR9EQN+oaziSu3qKVbwBERj1IFIB8OLUewxB5m/BFzi613og==}
+  /@vue/compiler-core/3.4.27:
+    resolution: {integrity: sha512-E+RyqY24KnyDXsCuQrI+mlcdW3ALND6U7Gqa/+bVwbcpcR3BRRIckFoz7Qyd4TTlnugtwuI7YgjbvsLmxb+yvg==}
     dependencies:
-      '@babel/parser': 7.24.1
-      '@vue/shared': 3.4.21
+      '@babel/parser': 7.24.6
+      '@vue/shared': 3.4.27
       entities: 4.5.0
       estree-walker: 2.0.2
       source-map-js: 1.2.0
 
-  /@vue/compiler-dom/3.4.21:
-    resolution: {integrity: sha512-IZC6FKowtT1sl0CR5DpXSiEB5ayw75oT2bma1BEhV7RRR1+cfwLrxc2Z8Zq/RGFzJ8w5r9QtCOvTjQgdn0IKmA==}
+  /@vue/compiler-dom/3.4.27:
+    resolution: {integrity: sha512-kUTvochG/oVgE1w5ViSr3KUBh9X7CWirebA3bezTbB5ZKBQZwR2Mwj9uoSKRMFcz4gSMzzLXBPD6KpCLb9nvWw==}
     dependencies:
-      '@vue/compiler-core': 3.4.21
-      '@vue/shared': 3.4.21
+      '@vue/compiler-core': 3.4.27
+      '@vue/shared': 3.4.27
 
-  /@vue/compiler-sfc/3.4.21:
-    resolution: {integrity: sha512-me7epoTxYlY+2CUM7hy9PCDdpMPfIwrOvAXud2Upk10g4YLv9UBW7kL798TvMeDhPthkZ0CONNrK2GoeI1ODiQ==}
+  /@vue/compiler-sfc/3.4.27:
+    resolution: {integrity: sha512-nDwntUEADssW8e0rrmE0+OrONwmRlegDA1pD6QhVeXxjIytV03yDqTey9SBDiALsvAd5U4ZrEKbMyVXhX6mCGA==}
     dependencies:
-      '@babel/parser': 7.24.1
-      '@vue/compiler-core': 3.4.21
-      '@vue/compiler-dom': 3.4.21
-      '@vue/compiler-ssr': 3.4.21
-      '@vue/shared': 3.4.21
+      '@babel/parser': 7.24.6
+      '@vue/compiler-core': 3.4.27
+      '@vue/compiler-dom': 3.4.27
+      '@vue/compiler-ssr': 3.4.27
+      '@vue/shared': 3.4.27
       estree-walker: 2.0.2
-      magic-string: 0.30.8
+      magic-string: 0.30.10
       postcss: 8.4.38
       source-map-js: 1.2.0
 
-  /@vue/compiler-ssr/3.4.21:
-    resolution: {integrity: sha512-M5+9nI2lPpAsgXOGQobnIueVqc9sisBFexh5yMIMRAPYLa7+5wEJs8iqOZc1WAa9WQbx9GR2twgznU8LTIiZ4Q==}
+  /@vue/compiler-ssr/3.4.27:
+    resolution: {integrity: sha512-CVRzSJIltzMG5FcidsW0jKNQnNRYC8bT21VegyMMtHmhW3UOI7knmUehzswXLrExDLE6lQCZdrhD4ogI7c+vuw==}
     dependencies:
-      '@vue/compiler-dom': 3.4.21
-      '@vue/shared': 3.4.21
+      '@vue/compiler-dom': 3.4.27
+      '@vue/shared': 3.4.27
 
   /@vue/devtools-api/6.6.1:
     resolution: {integrity: sha512-LgPscpE3Vs0x96PzSSB4IGVSZXZBZHpfxs+ZA1d+VEPwHdOXowy/Y2CsvCAIFrf+ssVU1pD1jidj505EpUnfbA==}
     dev: false
 
-  /@vue/language-core/2.0.7_typescript@5.4.3:
-    resolution: {integrity: sha512-Vh1yZX3XmYjn9yYLkjU8DN6L0ceBtEcapqiyclHne8guG84IaTzqtvizZB1Yfxm3h6m7EIvjerLO5fvOZO6IIQ==}
+  /@vue/language-core/2.0.19_typescript@5.4.5:
+    resolution: {integrity: sha512-A9EGOnvb51jOvnCYoRLnMP+CcoPlbZVxI9gZXE/y2GksRWM6j/PrLEIC++pnosWTN08tFpJgxhSS//E9v/Sg+Q==}
     peerDependencies:
       typescript: '*'
     peerDependenciesMeta:
       typescript:
         optional: true
     dependencies:
-      '@volar/language-core': 2.1.6
-      '@vue/compiler-dom': 3.4.21
-      '@vue/shared': 3.4.21
+      '@volar/language-core': 2.2.5
+      '@vue/compiler-dom': 3.4.27
+      '@vue/shared': 3.4.27
       computeds: 0.0.1
       minimatch: 9.0.4
       path-browserify: 1.0.1
-      typescript: 5.4.3
+      typescript: 5.4.5
       vue-template-compiler: 2.7.16
     dev: true
 
-  /@vue/reactivity/3.4.21:
-    resolution: {integrity: sha512-UhenImdc0L0/4ahGCyEzc/pZNwVgcglGy9HVzJ1Bq2Mm9qXOpP8RyNTjookw/gOCUlXSEtuZ2fUg5nrHcoqJcw==}
+  /@vue/reactivity/3.4.27:
+    resolution: {integrity: sha512-kK0g4NknW6JX2yySLpsm2jlunZJl2/RJGZ0H9ddHdfBVHcNzxmQ0sS0b09ipmBoQpY8JM2KmUw+a6sO8Zo+zIA==}
     dependencies:
-      '@vue/shared': 3.4.21
+      '@vue/shared': 3.4.27
 
-  /@vue/runtime-core/3.4.21:
-    resolution: {integrity: sha512-pQthsuYzE1XcGZznTKn73G0s14eCJcjaLvp3/DKeYWoFacD9glJoqlNBxt3W2c5S40t6CCcpPf+jG01N3ULyrA==}
+  /@vue/runtime-core/3.4.27:
+    resolution: {integrity: sha512-7aYA9GEbOOdviqVvcuweTLe5Za4qBZkUY7SvET6vE8kyypxVgaT1ixHLg4urtOlrApdgcdgHoTZCUuTGap/5WA==}
     dependencies:
-      '@vue/reactivity': 3.4.21
-      '@vue/shared': 3.4.21
+      '@vue/reactivity': 3.4.27
+      '@vue/shared': 3.4.27
 
-  /@vue/runtime-dom/3.4.21:
-    resolution: {integrity: sha512-gvf+C9cFpevsQxbkRBS1NpU8CqxKw0ebqMvLwcGQrNpx6gqRDodqKqA+A2VZZpQ9RpK2f9yfg8VbW/EpdFUOJw==}
+  /@vue/runtime-dom/3.4.27:
+    resolution: {integrity: sha512-ScOmP70/3NPM+TW9hvVAz6VWWtZJqkbdf7w6ySsws+EsqtHvkhxaWLecrTorFxsawelM5Ys9FnDEMt6BPBDS0Q==}
     dependencies:
-      '@vue/runtime-core': 3.4.21
-      '@vue/shared': 3.4.21
+      '@vue/runtime-core': 3.4.27
+      '@vue/shared': 3.4.27
       csstype: 3.1.3
 
-  /@vue/server-renderer/3.4.21_vue@3.4.21:
-    resolution: {integrity: sha512-aV1gXyKSN6Rz+6kZ6kr5+Ll14YzmIbeuWe7ryJl5muJ4uwSwY/aStXTixx76TwkZFJLm1aAlA/HSWEJ4EyiMkg==}
+  /@vue/server-renderer/3.4.27_vue@3.4.27:
+    resolution: {integrity: sha512-dlAMEuvmeA3rJsOMJ2J1kXU7o7pOxgsNHVr9K8hB3ImIkSuBrIdy0vF66h8gf8Tuinf1TK3mPAz2+2sqyf3KzA==}
     peerDependencies:
-      vue: 3.4.21
+      vue: 3.4.27
     dependencies:
-      '@vue/compiler-ssr': 3.4.21
-      '@vue/shared': 3.4.21
-      vue: 3.4.21_typescript@5.4.3
+      '@vue/compiler-ssr': 3.4.27
+      '@vue/shared': 3.4.27
+      vue: 3.4.27_typescript@5.4.5
 
-  /@vue/shared/3.4.21:
-    resolution: {integrity: sha512-PuJe7vDIi6VYSinuEbUIQgMIRZGgM8e4R+G+/dQTk0X1NEdvgvvgv7m+rfmDH1gZzyA1OjjoWskvHlfRNfQf3g==}
+  /@vue/shared/3.4.27:
+    resolution: {integrity: sha512-DL3NmY2OFlqmYYrzp39yi3LDkKxa5vZVwxWdQ3rG0ekuWscHraeIbnI8t+aZK7qhYqEqWKTUdijadunb9pnrgA==}
 
-  /@vueuse/core/9.13.0_vue@3.4.21:
+  /@vueuse/core/9.13.0_vue@3.4.27:
     resolution: {integrity: sha512-pujnclbeHWxxPRqXWmdkKV5OX4Wk4YeK7wusHqRwU0Q7EFusHoqNA/aPhB6KCh9hEqJkLAJo7bb0Lh9b+OIVzw==}
     dependencies:
       '@types/web-bluetooth': 0.0.16
       '@vueuse/metadata': 9.13.0
-      '@vueuse/shared': 9.13.0_vue@3.4.21
-      vue-demi: 0.14.7_vue@3.4.21
+      '@vueuse/shared': 9.13.0_vue@3.4.27
+      vue-demi: 0.14.7_vue@3.4.27
     transitivePeerDependencies:
       - '@vue/composition-api'
       - vue
@@ -611,10 +614,10 @@ packages:
     resolution: {integrity: sha512-gdU7TKNAUVlXXLbaF+ZCfte8BjRJQWPCa2J55+7/h+yDtzw3vOoGQDRXzI6pyKyo6bXFT5/QoPE4hAknExjRLQ==}
     dev: false
 
-  /@vueuse/shared/9.13.0_vue@3.4.21:
+  /@vueuse/shared/9.13.0_vue@3.4.27:
     resolution: {integrity: sha512-UrnhU+Cnufu4S6JLCPZnkWh0WwZGUp72ktOF2DFptMlOs3TOdVv8xJN53zhHGARmVOsz5KqOls09+J1NR6sBKw==}
     dependencies:
-      vue-demi: 0.14.7_vue@3.4.21
+      vue-demi: 0.14.7_vue@3.4.27
     transitivePeerDependencies:
       - '@vue/composition-api'
       - vue
@@ -664,11 +667,11 @@ packages:
       balanced-match: 1.0.2
     dev: true
 
-  /braces/3.0.2:
-    resolution: {integrity: sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==}
+  /braces/3.0.3:
+    resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==}
     engines: {node: '>=8'}
     dependencies:
-      fill-range: 7.0.1
+      fill-range: 7.1.1
     dev: true
 
   /camelcase/5.3.1:
@@ -689,7 +692,7 @@ packages:
     engines: {node: '>= 8.10.0'}
     dependencies:
       anymatch: 3.1.3
-      braces: 3.0.2
+      braces: 3.0.3
       glob-parent: 5.1.2
       is-binary-path: 2.1.0
       is-glob: 4.0.3
@@ -762,8 +765,8 @@ packages:
   /csstype/3.1.3:
     resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==}
 
-  /dayjs/1.11.10:
-    resolution: {integrity: sha512-vjAczensTgRcqDERK0SR2XMwsF/tSvnvlv6VcF2GIhg6Sx4yOIt/irsr1RDJsKiIyBzJDpCoXiWWq28MqH2cnQ==}
+  /dayjs/1.11.11:
+    resolution: {integrity: sha512-okzr3f11N6WuqYtZSvm+F776mB41wRZMhKP+hc34YdW+KmtYYK9iqvHSwo2k9FEH3fhGXvOPV6yz2IcSrfRUDg==}
     dev: false
 
   /de-indent/1.0.2:
@@ -783,27 +786,27 @@ packages:
     resolution: {integrity: sha512-/pjZsA1b4RPHbeWZQn66SWS8nZZWLQQ23oE3Eam7aroEFGEvwKAsJfZ9ytiEMycfzXWpca4FA9QIOehf7PocBQ==}
     dev: false
 
-  /element-plus/2.6.3_vue@3.4.21:
-    resolution: {integrity: sha512-U4L/mr+1r+EmAUYUHrs0V/8hHMdBGP07rPymSC72LZCN4jK1UwygQYICegTQ5us4mxeqBvW6wfoEfo003fwCqw==}
+  /element-plus/2.7.3_vue@3.4.27:
+    resolution: {integrity: sha512-OaqY1kQ2xzNyRFyge3fzM7jqMwux+464RBEqd+ybRV9xPiGxtgnj/sVK4iEbnKnzQIa9XK03DOIFzoToUhu1DA==}
     peerDependencies:
       vue: ^3.2.0
     dependencies:
       '@ctrl/tinycolor': 3.6.1
-      '@element-plus/icons-vue': 2.3.1_vue@3.4.21
-      '@floating-ui/dom': 1.6.3
+      '@element-plus/icons-vue': 2.3.1_vue@3.4.27
+      '@floating-ui/dom': 1.6.5
       '@popperjs/core': /@sxzz/popperjs-es/2.11.7
-      '@types/lodash': 4.17.0
+      '@types/lodash': 4.17.4
       '@types/lodash-es': 4.17.12
-      '@vueuse/core': 9.13.0_vue@3.4.21
+      '@vueuse/core': 9.13.0_vue@3.4.27
       async-validator: 4.2.5
-      dayjs: 1.11.10
+      dayjs: 1.11.11
       escape-html: 1.0.3
       lodash: 4.17.21
       lodash-es: 4.17.21
       lodash-unified: 1.0.3_vpgwo5v3ie2bia5ss74pgoa5ly
       memoize-one: 6.0.0
       normalize-wheel-es: 1.2.0
-      vue: 3.4.21_typescript@5.4.3
+      vue: 3.4.27_typescript@5.4.5
     transitivePeerDependencies:
       - '@vue/composition-api'
     dev: false
@@ -858,8 +861,8 @@ packages:
   /estree-walker/2.0.2:
     resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==}
 
-  /fill-range/7.0.1:
-    resolution: {integrity: sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==}
+  /fill-range/7.1.1:
+    resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==}
     engines: {node: '>=8'}
     dependencies:
       to-regex-range: 5.0.1
@@ -890,13 +893,13 @@ packages:
     resolution: {integrity: sha512-PT6uoF5a1+kbC3tHmZSUsLHBp2QJlHasxxxxPW47QIY1VBKpFB+FcDvX+MxER6UzgLQZ0xDzJ9s48B9JbOCTqA==}
     engines: {node: '>=10.19'}
     dependencies:
-      '@petamoriken/float16': 3.8.6
+      '@petamoriken/float16': 3.8.7
       lerc: 3.0.0
       pako: 2.1.0
       parse-headers: 2.0.5
       quick-lru: 6.1.2
       web-worker: 1.3.0
-      xml-utils: 1.8.0
+      xml-utils: 1.9.0
       zstddec: 0.1.0
     dev: false
 
@@ -929,8 +932,8 @@ packages:
     resolution: {integrity: sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==}
     dev: false
 
-  /immutable/4.3.5:
-    resolution: {integrity: sha512-8eabxkth9gZatlwl5TBuJnCsoTADlL6ftEr7A4qgdaTsPyreilDSnUk57SO+jfKcNtxPa22U5KK6DSeAYhpBJw==}
+  /immutable/4.3.6:
+    resolution: {integrity: sha512-Ju0+lEMyzMVZarkTn/gqRpdqd5dOPaz1mCZ0SH3JV6iFw81PldE/PEB1hWVEA288HPt4WXW8O7AWxB10M+03QQ==}
     dev: true
 
   /inherits/2.0.4:
@@ -983,6 +986,10 @@ packages:
       setimmediate: 1.0.5
     dev: false
 
+  /konva/9.3.6:
+    resolution: {integrity: sha512-dqR8EbcM0hjuilZCBP6xauQ5V3kH3m9kBcsDkqPypQuRgsXbcXUrxqYxhNbdvKZpYNW8Amq94jAD/C0NY3qfBQ==}
+    dev: false
+
   /lerc/3.0.0:
     resolution: {integrity: sha512-Rm4J/WaHhRa93nCN2mwWDZFoRVF18G1f47C+kvQWyHGEZxFpTUi73p7lMVSAndyxGt6lJ2/CFbOcf9ra5p8aww==}
     dev: false
@@ -1020,16 +1027,8 @@ packages:
     resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==}
     dev: false
 
-  /lru-cache/6.0.0:
-    resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==}
-    engines: {node: '>=10'}
-    dependencies:
-      yallist: 4.0.0
-    dev: true
-
-  /magic-string/0.30.8:
-    resolution: {integrity: sha512-ISQTe55T2ao7XtlAStud6qwYPZjE4GK1S/BeVPus4jrq6JuOnQ00YKQC581RWhR122W7msZV263KzVeLoqidyQ==}
-    engines: {node: '>=12'}
+  /magic-string/0.30.10:
+    resolution: {integrity: sha512-iIRwTIf0QKV3UAnYK4PU8uiEc4SRh5jX0mwpIwETPpHdhVM4f53RSwS/vXvN1JhGX+Cs7B8qIq3d6AH49O5fAQ==}
     dependencies:
       '@jridgewell/sourcemap-codec': 1.4.15
 
@@ -1070,8 +1069,8 @@ packages:
     resolution: {integrity: sha512-Wj7+EJQ8mSuXr2iWfnujrimU35R2W4FAErEyTmJoJ7ucwTn2hOUSsRehMb5RSYkxXGTM7Y9QpvPmp++w5ftoJw==}
     dev: false
 
-  /ol/9.1.0:
-    resolution: {integrity: sha512-nDrkJ2tzZNpo/wzN/PpHV5zdxbnXZaFktoMaD2cFLEc6gCwlgLY21Yd8wnt/4FjaVYwLBnbN9USXSwIBGcyksQ==}
+  /ol/9.2.4:
+    resolution: {integrity: sha512-bsbu4ObaAlbELMIZWnYEvX4Z9jO+OyCBshtODhDKmqYTPEfnKOX3RieCr97tpJkqWTZvyV4tS9UQDvHoCdxS+A==}
     dependencies:
       color-rgba: 3.0.0
       color-space: 2.0.1
@@ -1129,8 +1128,8 @@ packages:
       resolve-protobuf-schema: 2.1.0
     dev: false
 
-  /picocolors/1.0.0:
-    resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==}
+  /picocolors/1.0.1:
+    resolution: {integrity: sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==}
 
   /picomatch/2.3.1:
     resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==}
@@ -1147,7 +1146,7 @@ packages:
     engines: {node: ^10 || ^12 || >=14}
     dependencies:
       nanoid: 3.3.7
-      picocolors: 1.0.0
+      picocolors: 1.0.1
       source-map-js: 1.2.0
 
   /process-nextick-args/2.0.1:
@@ -1225,28 +1224,29 @@ packages:
       protocol-buffers-schema: 3.6.0
     dev: false
 
-  /rollup/4.13.2:
-    resolution: {integrity: sha512-MIlLgsdMprDBXC+4hsPgzWUasLO9CE4zOkj/u6j+Z6j5A4zRY+CtiXAdJyPtgCsc42g658Aeh1DlrdVEJhsL2g==}
+  /rollup/4.18.0:
+    resolution: {integrity: sha512-QmJz14PX3rzbJCN1SG4Xe/bAAX2a6NpCP8ab2vfu2GiUr8AQcr2nCV/oEO3yneFarB67zk8ShlIyWb2LGTb3Sg==}
     engines: {node: '>=18.0.0', npm: '>=8.0.0'}
     hasBin: true
     dependencies:
       '@types/estree': 1.0.5
     optionalDependencies:
-      '@rollup/rollup-android-arm-eabi': 4.13.2
-      '@rollup/rollup-android-arm64': 4.13.2
-      '@rollup/rollup-darwin-arm64': 4.13.2
-      '@rollup/rollup-darwin-x64': 4.13.2
-      '@rollup/rollup-linux-arm-gnueabihf': 4.13.2
-      '@rollup/rollup-linux-arm64-gnu': 4.13.2
-      '@rollup/rollup-linux-arm64-musl': 4.13.2
-      '@rollup/rollup-linux-powerpc64le-gnu': 4.13.2
-      '@rollup/rollup-linux-riscv64-gnu': 4.13.2
-      '@rollup/rollup-linux-s390x-gnu': 4.13.2
-      '@rollup/rollup-linux-x64-gnu': 4.13.2
-      '@rollup/rollup-linux-x64-musl': 4.13.2
-      '@rollup/rollup-win32-arm64-msvc': 4.13.2
-      '@rollup/rollup-win32-ia32-msvc': 4.13.2
-      '@rollup/rollup-win32-x64-msvc': 4.13.2
+      '@rollup/rollup-android-arm-eabi': 4.18.0
+      '@rollup/rollup-android-arm64': 4.18.0
+      '@rollup/rollup-darwin-arm64': 4.18.0
+      '@rollup/rollup-darwin-x64': 4.18.0
+      '@rollup/rollup-linux-arm-gnueabihf': 4.18.0
+      '@rollup/rollup-linux-arm-musleabihf': 4.18.0
+      '@rollup/rollup-linux-arm64-gnu': 4.18.0
+      '@rollup/rollup-linux-arm64-musl': 4.18.0
+      '@rollup/rollup-linux-powerpc64le-gnu': 4.18.0
+      '@rollup/rollup-linux-riscv64-gnu': 4.18.0
+      '@rollup/rollup-linux-s390x-gnu': 4.18.0
+      '@rollup/rollup-linux-x64-gnu': 4.18.0
+      '@rollup/rollup-linux-x64-musl': 4.18.0
+      '@rollup/rollup-win32-arm64-msvc': 4.18.0
+      '@rollup/rollup-win32-ia32-msvc': 4.18.0
+      '@rollup/rollup-win32-x64-msvc': 4.18.0
       fsevents: 2.3.3
     dev: true
 
@@ -1254,22 +1254,20 @@ packages:
     resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==}
     dev: false
 
-  /sass/1.72.0:
-    resolution: {integrity: sha512-Gpczt3WA56Ly0Mn8Sl21Vj94s1axi9hDIzDFn9Ph9x3C3p4nNyvsqJoQyVXKou6cBlfFWEgRW4rT8Tb4i3XnVA==}
+  /sass/1.77.2:
+    resolution: {integrity: sha512-eb4GZt1C3avsX3heBNlrc7I09nyT00IUuo4eFhAbeXWU2fvA7oXI53SxODVAA+zgZCk9aunAZgO+losjR3fAwA==}
     engines: {node: '>=14.0.0'}
     hasBin: true
     dependencies:
       chokidar: 3.6.0
-      immutable: 4.3.5
+      immutable: 4.3.6
       source-map-js: 1.2.0
     dev: true
 
-  /semver/7.6.0:
-    resolution: {integrity: sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==}
+  /semver/7.6.2:
+    resolution: {integrity: sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==}
     engines: {node: '>=10'}
     hasBin: true
-    dependencies:
-      lru-cache: 6.0.0
     dev: true
 
   /set-blocking/2.0.0:
@@ -1324,8 +1322,8 @@ packages:
       is-number: 7.0.0
     dev: true
 
-  /typescript/5.4.3:
-    resolution: {integrity: sha512-KrPd3PKaCLr78MalgiwJnA25Nm8HAmdwN3mYUYZgG/wizIo9EainNVQI9/yDavtVFRN2h3k8uf3GLHuhDMgEHg==}
+  /typescript/5.4.5:
+    resolution: {integrity: sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==}
     engines: {node: '>=14.17'}
     hasBin: true
 
@@ -1336,8 +1334,8 @@ packages:
     resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==}
     dev: false
 
-  /vite/5.2.7_2viu4ejjkxxzbuz7bxvuj6jcbe:
-    resolution: {integrity: sha512-k14PWOKLI6pMaSzAuGtT+Cf0YmIx12z9YGon39onaJNy8DLBfBJrzg9FQEmkAM5lpHBZs9wksWAsyF/HkpEwJA==}
+  /vite/5.2.12_q232fjyrpkfhcrmg5coltkhjqi:
+    resolution: {integrity: sha512-/gC8GxzxMK5ntBwb48pR32GGhENnjtY30G4A0jemunsBkiEZFw60s8InGpN8gkhHEkjnRK1aSAxeQgwvFhUHAA==}
     engines: {node: ^18.0.0 || >=20.0.0}
     hasBin: true
     peerDependencies:
@@ -1364,16 +1362,16 @@ packages:
       terser:
         optional: true
     dependencies:
-      '@types/node': 20.12.2
+      '@types/node': 20.12.12
       esbuild: 0.20.2
       postcss: 8.4.38
-      rollup: 4.13.2
-      sass: 1.72.0
+      rollup: 4.18.0
+      sass: 1.77.2
     optionalDependencies:
       fsevents: 2.3.3
     dev: true
 
-  /vue-demi/0.14.7_vue@3.4.21:
+  /vue-demi/0.14.7_vue@3.4.27:
     resolution: {integrity: sha512-EOG8KXDQNwkJILkx/gPcoL/7vH+hORoBaKgGe+6W7VFMvCYJfmF2dGbvgDroVnI8LU7/kTu8mbjRZGBU1z9NTA==}
     engines: {node: '>=12'}
     hasBin: true
@@ -1385,16 +1383,16 @@ packages:
       '@vue/composition-api':
         optional: true
     dependencies:
-      vue: 3.4.21_typescript@5.4.3
+      vue: 3.4.27_typescript@5.4.5
     dev: false
 
-  /vue-router/4.3.0_vue@3.4.21:
-    resolution: {integrity: sha512-dqUcs8tUeG+ssgWhcPbjHvazML16Oga5w34uCUmsk7i0BcnskoLGwjpa15fqMr2Fa5JgVBrdL2MEgqz6XZ/6IQ==}
+  /vue-router/4.3.2_vue@3.4.27:
+    resolution: {integrity: sha512-hKQJ1vDAZ5LVkKEnHhmm1f9pMiWIBNGF5AwU67PdH7TyXCj/a4hTccuUuYCAMgJK6rO/NVYtQIEN3yL8CECa7Q==}
     peerDependencies:
       vue: ^3.2.0
     dependencies:
       '@vue/devtools-api': 6.6.1
-      vue: 3.4.21_typescript@5.4.3
+      vue: 3.4.27_typescript@5.4.5
     dev: false
 
   /vue-template-compiler/2.7.16:
@@ -1404,32 +1402,32 @@ packages:
       he: 1.2.0
     dev: true
 
-  /vue-tsc/2.0.7_typescript@5.4.3:
-    resolution: {integrity: sha512-LYa0nInkfcDBB7y8jQ9FQ4riJTRNTdh98zK/hzt4gEpBZQmf30dPhP+odzCa+cedGz6B/guvJEd0BavZaRptjg==}
+  /vue-tsc/2.0.19_typescript@5.4.5:
+    resolution: {integrity: sha512-JWay5Zt2/871iodGF72cELIbcAoPyhJxq56mPPh+M2K7IwI688FMrFKc/+DvB05wDWEuCPexQJ6L10zSwzzapg==}
     hasBin: true
     peerDependencies:
       typescript: '*'
     dependencies:
-      '@volar/typescript': 2.1.6
-      '@vue/language-core': 2.0.7_typescript@5.4.3
-      semver: 7.6.0
-      typescript: 5.4.3
+      '@volar/typescript': 2.2.5
+      '@vue/language-core': 2.0.19_typescript@5.4.5
+      semver: 7.6.2
+      typescript: 5.4.5
     dev: true
 
-  /vue/3.4.21_typescript@5.4.3:
-    resolution: {integrity: sha512-5hjyV/jLEIKD/jYl4cavMcnzKwjMKohureP8ejn3hhEjwhWIhWeuzL2kJAjzl/WyVsgPY56Sy4Z40C3lVshxXA==}
+  /vue/3.4.27_typescript@5.4.5:
+    resolution: {integrity: sha512-8s/56uK6r01r1icG/aEOHqyMVxd1bkYcSe9j8HcKtr/xTOFWvnzIVTehNW+5Yt89f+DLBe4A569pnZLS5HzAMA==}
     peerDependencies:
       typescript: '*'
     peerDependenciesMeta:
       typescript:
         optional: true
     dependencies:
-      '@vue/compiler-dom': 3.4.21
-      '@vue/compiler-sfc': 3.4.21
-      '@vue/runtime-dom': 3.4.21
-      '@vue/server-renderer': 3.4.21_vue@3.4.21
-      '@vue/shared': 3.4.21
-      typescript: 5.4.3
+      '@vue/compiler-dom': 3.4.27
+      '@vue/compiler-sfc': 3.4.27
+      '@vue/runtime-dom': 3.4.27
+      '@vue/server-renderer': 3.4.27_vue@3.4.27
+      '@vue/shared': 3.4.27
+      typescript: 5.4.5
 
   /web-worker/1.3.0:
     resolution: {integrity: sha512-BSR9wyRsy/KOValMgd5kMyr3JzpdeoR9KVId8u5GVlTTAtNChlsE4yTxeY7zMdNSyOmoKBv8NH2qeRY9Tg+IaA==}
@@ -1476,18 +1474,14 @@ packages:
       word: 0.3.0
     dev: false
 
-  /xml-utils/1.8.0:
-    resolution: {integrity: sha512-1TY5yLw8DApowZAUsWCniNr8HH6Ebt6O7UQvmIwziGKwUNsQx6e+4NkfOvCfnqmYIcPjCeoI6dh1JenPJ9a1hQ==}
+  /xml-utils/1.9.0:
+    resolution: {integrity: sha512-bE++owdhr9WNSar5MAL04c727SjSHyEtu2+0hTVbHPeJgzauy5bnhfJV3gdKwm4m+ZNjKmjM97WphKwxWqW1IA==}
     dev: false
 
   /y18n/4.0.3:
     resolution: {integrity: sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==}
     dev: false
 
-  /yallist/4.0.0:
-    resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==}
-    dev: true
-
   /yargs-parser/18.1.3:
     resolution: {integrity: sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==}
     engines: {node: '>=6'}

+ 4 - 0
public/map/location/location_b.svg

@@ -0,0 +1,4 @@
+<svg width="44" height="44" viewBox="0 0 44 44" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M22 44C32.6667 33.891 38 25.891 38 20C38 11.1634 30.8366 4 22 4C13.1634 4 6 11.1634 6 20C6 25.891 11.3333 33.891 22 44Z" fill="#409EFF"/>
+<path d="M22 30C27.5228 30 32 25.5228 32 20C32 14.4772 27.5228 10 22 10C16.4772 10 12 14.4772 12 20C12 25.5228 16.4772 30 22 30Z" fill="white"/>
+</svg>

+ 5 - 0
public/map/location/location_n.svg

@@ -0,0 +1,5 @@
+<svg width="44" height="44" viewBox="0 0 44 44" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M22 44C32.6667 33.891 38 25.891 38 20C38 11.1634 30.8366 4 22 4C13.1634 4 6 11.1634 6 20C6 25.891 11.3333 33.891 22 44Z" fill="#728191"/>
+<path d="M33.1679 29.8636C35.7879 25.9337 37 22.6592 37 20C37 11.7157 30.2843 5 22 5C13.7157 5 7 11.7157 7 20C7 22.6592 8.21213 25.9337 10.8321 29.8636C13.3275 33.6067 17.0414 37.8575 22 42.6181C26.9586 37.8575 30.6726 33.6067 33.1679 29.8636ZM22 44C11.3333 33.891 6 25.891 6 20C6 11.1634 13.1634 4 22 4C30.8366 4 38 11.1634 38 20C38 25.891 32.6667 33.891 22 44Z" fill="white"/>
+<path d="M22 30C27.5228 30 32 25.5228 32 20C32 14.4772 27.5228 10 22 10C16.4772 10 12 14.4772 12 20C12 25.5228 16.4772 30 22 30Z" fill="white"/>
+</svg>

+ 5 - 0
public/map/location/location_o.svg

@@ -0,0 +1,5 @@
+<svg width="44" height="44" viewBox="0 0 44 44" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M22 44C32.6667 33.891 38 25.891 38 20C38 11.1634 30.8366 4 22 4C13.1634 4 6 11.1634 6 20C6 25.891 11.3333 33.891 22 44Z" fill="#E6A23C"/>
+<path d="M33.1679 29.8636C30.6726 33.6067 26.9586 37.8575 22 42.6181C17.0414 37.8575 13.3275 33.6067 10.8321 29.8636C8.21213 25.9337 7 22.6592 7 20C7 11.7157 13.7157 5 22 5C30.2843 5 37 11.7157 37 20C37 22.6592 35.7879 25.9337 33.1679 29.8636ZM22 44C32.6667 33.891 38 25.891 38 20C38 11.1634 30.8366 4 22 4C13.1634 4 6 11.1634 6 20C6 25.891 11.3333 33.891 22 44Z" fill="white"/>
+<path d="M22 30C27.5228 30 32 25.5228 32 20C32 14.4772 27.5228 10 22 10C16.4772 10 12 14.4772 12 20C12 25.5228 16.4772 30 22 30Z" fill="white"/>
+</svg>

+ 6 - 0
public/map/location/location_y.svg

@@ -0,0 +1,6 @@
+<svg width="44" height="44" viewBox="0 0 44 44" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M22 44C32.6667 33.891 38 25.891 38 20C38 11.1634 30.8366 4 22 4C13.1634 4 6 11.1634 6 20C6 25.891 11.3333 33.891 22 44Z" fill="#409EFF"/>
+<path d="M33.1679 29.8636C30.6726 33.6067 26.9586 37.8575 22 42.6181C17.0414 37.8575 13.3275 33.6067 10.8321 29.8636C8.21213 25.9337 7 22.6592 7 20C7 11.7157 13.7157 5 22 5C30.2843 5 37 11.7157 37 20C37 22.6592 35.7879 25.9337 33.1679 29.8636ZM22 44C32.6667 33.891 38 25.891 38 20C38 11.1634 30.8366 4 22 4C13.1634 4 6 11.1634 6 20C6 25.891 11.3333 33.891 22 44Z" fill="white"/>
+<path d="M22 30C27.5228 30 32 25.5228 32 20C32 14.4772 27.5228 10 22 10C16.4772 10 12 14.4772 12 20C12 25.5228 16.4772 30 22 30Z" fill="white"/>
+<path d="M28.2071 16.7929C28.5976 17.1834 28.5976 17.8166 28.2071 18.2071L21 25.4142L16.2929 20.7071C15.9024 20.3166 15.9024 19.6834 16.2929 19.2929C16.6834 18.9024 17.3166 18.9024 17.7071 19.2929L21 22.5858L26.7929 16.7929C27.1834 16.4024 27.8166 16.4024 28.2071 16.7929Z" fill="#409EFF"/>
+</svg>

+ 59 - 1
src/request/index.ts

@@ -10,7 +10,15 @@ import {
   Param,
 } from "./state";
 import { ElMessage } from "element-plus";
-import { Relics, Scene, ScenePoint, ResPage, UserInfo, Device } from "./type";
+import {
+  Relics,
+  Scene,
+  ScenePoint,
+  ResPage,
+  UserInfo,
+  Device,
+  PolygonsAttrib,
+} from "./type";
 
 const error = throttle((msg: string) => ElMessage.error(msg), 2000);
 
@@ -180,6 +188,56 @@ export const relicsScenePosInfoFetch = (posId: number) =>
     { paths: { posId } }
   );
 
+export const relicsPolyginsFetch = () => {
+  return new Promise<PolygonsAttrib>((resolve) => {
+    setTimeout(() => {
+      resolve({
+        lines: [
+          { id: "1", pointIds: ["2666", "2667"] },
+          { id: "2", pointIds: ["2667", "2669"] },
+          { id: "3", pointIds: ["2669", "2674"] },
+          { id: "4", pointIds: ["2674", "2675"] },
+          { id: "5", pointIds: ["2675", "2676"] },
+          { id: "6", pointIds: ["2676", "2673"] },
+          { id: "7", pointIds: ["2673", "2672"] },
+          { id: "8", pointIds: ["2672", "2670"] },
+        ],
+        polygons: [
+          { id: "1", lineIds: ["1", "2", "3", "4", "5", "6", "7", "8"] },
+        ],
+        points: [
+          { rtk: false, x: 115.949835199646, y: 30.0971239995873, id: "2666" },
+          { rtk: false, x: 115.949706558269, y: 30.0975243383135, id: "2667" },
+          { rtk: false, x: 115.950002555619, y: 30.0977552558535, id: "2668" },
+          { rtk: false, x: 115.949968744193, y: 30.097862045865, id: "2669" },
+          { rtk: true, x: 115.950063977564, y: 30.0978879318173, id: "2670" },
+          { rtk: true, x: 115.949964417593, y: 30.0978650571868, id: "2671" },
+          { rtk: true, x: 115.950300839723, y: 30.0976756336231, id: "2672" },
+          { rtk: true, x: 115.950437426448, y: 30.097269657442, id: "2673" },
+          {
+            rtk: false,
+            x: 115.95025353664198,
+            y: 30.097554052476422,
+            id: "2674",
+          },
+          {
+            rtk: false,
+            x: 115.95021286828855,
+            y: 30.09733715459145,
+            id: "2675",
+          },
+          {
+            rtk: false,
+            x: 115.95031589478391,
+            y: 30.097000962869746,
+            id: "2676",
+          },
+        ],
+      });
+    }, 500);
+  });
+};
+
 export type ScenePageProps = PageProps<{
   sceneCode?: string;
   sceneName?: string;

+ 14 - 0
src/request/type.ts

@@ -5,6 +5,11 @@ import {
   creationMethodDesc,
 } from "@/store/relics";
 import { SceneStatus } from "@/store/scene";
+import {
+  WholeLineLineAttrib,
+  WholeLinePointAttrib,
+  WholeLinePolygonAttrib,
+} from "drawing-board";
 
 export type UserInfo = {
   head: string;
@@ -132,3 +137,12 @@ export type Device = {
   userId: 2;
   userName: string;
 };
+
+export type PolygonsPointAttrib = WholeLinePointAttrib & { rtk: boolean };
+export type PolygonsLineAttrib = WholeLineLineAttrib;
+
+export type PolygonsAttrib = {
+  lines: PolygonsLineAttrib[];
+  polygons: WholeLinePolygonAttrib[];
+  points: PolygonsPointAttrib[];
+};

+ 7 - 1
src/router.ts

@@ -39,7 +39,13 @@ const routes: RouteRecordRaw[] = [
             path: "",
             name: "map",
             meta: { title: "文物", navClass: "map" },
-            component: () => import("@/view/map/map.vue"),
+            component: () => import("@/view/map/map-borad.vue"),
+          },
+          {
+            path: "test",
+            name: "map-test",
+            meta: { title: "文物", navClass: "map" },
+            component: () => import("@/view/map/test-board.vue"),
           },
           {
             path: "pano/:pid",

+ 41 - 0
src/store/polygons.ts

@@ -0,0 +1,41 @@
+import {
+  WholeLineLineAttrib,
+  WholeLinePointAttrib,
+  WholeLinePolygonAttrib,
+} from "drawing-board";
+import { ref } from "vue";
+
+export type Polygons = {
+  id: string;
+  lines: WholeLineLineAttrib[];
+  polygons: WholeLinePolygonAttrib[];
+  points: (WholeLinePointAttrib & { rtk: boolean })[];
+};
+
+export const polygons = ref<Polygons>({
+  id: "0",
+  lines: [],
+  polygons: [],
+  points: [],
+});
+
+setTimeout(() => {
+  polygons.value = {
+    id: "0",
+    lines: [
+      { id: "1", pointIds: ["2666", "2667"] },
+      { id: "2", pointIds: ["2667", "2669"] },
+    ],
+    polygons: [{ id: "1", lineIds: ["1", "2"] }],
+    points: [
+      { rtk: false, x: 115.949835199646, y: 30.0971239995873, id: "2666" },
+      { rtk: false, x: 115.949706558269, y: 30.0975243383135, id: "2667" },
+      { rtk: false, x: 115.950002555619, y: 30.0977552558535, id: "2668" },
+      { rtk: false, x: 115.949968744193, y: 30.097862045865, id: "2669" },
+      { rtk: true, x: 115.950063977564, y: 30.0978879318173, id: "2670" },
+      { rtk: true, x: 115.949964417593, y: 30.0978650571868, id: "2671" },
+      { rtk: true, x: 115.950300839723, y: 30.0976756336231, id: "2672" },
+      { rtk: true, x: 115.950437426448, y: 30.097269657442, id: "2673" },
+    ],
+  };
+}, 2000);

+ 1 - 0
src/store/relics.ts

@@ -1,5 +1,6 @@
 import {
   relicsInfoFetch,
+  relicsPolyginsFetch,
   relicsSelfCheckFetch,
   updateRelicsFetch,
 } from "@/request";

+ 50 - 0
src/view/map/board/index.ts

@@ -0,0 +1,50 @@
+import { Polygons } from "./polygons";
+import { Manage } from "../openlayer";
+import { register, BoundQueryPlugin } from "drawing-board";
+import { PolygonsAttrib } from "@/request/type";
+
+const initBoard = register(
+  { polygons: Polygons },
+  { bound: new BoundQueryPlugin() }
+);
+
+export const createBoard = (container: HTMLDivElement, mapManage: Manage) => {
+  const board = initBoard(container, { polygons: undefined }, false);
+  const syncBound = () => {
+    const bound = mapManage.getBound();
+    board.bound.setSize(container.offsetWidth, container.offsetHeight);
+    board.bound.setBound([bound[0], bound[3], bound[2], bound[1]]);
+  };
+
+  const { map } = mapManage;
+  map.addEventListener("moveend", syncBound);
+
+  board.bound.enableMove((newBound) => {
+    mapManage.setBound(newBound);
+    return false;
+  });
+  board.bound.enableWheel((newBound) => {
+    mapManage.setBound(newBound);
+    return false;
+  });
+
+  return {
+    getData() {
+      return board.getData().polygons;
+    },
+    setData(polygons: PolygonsAttrib, id: string) {
+      board.setData({ polygons: { ...polygons, id } });
+    },
+    destory() {
+      board.destory();
+      map.removeEventListener("moveend", syncBound);
+    },
+    polygon() {
+      console.log(board.tree);
+      return board.tree.children[0] as Polygons;
+    },
+    getPixelFromCoordinate(point: number[]) {
+      return board.tree.getPixelFromStage(point);
+    },
+  };
+};

+ 34 - 0
src/view/map/board/line.ts

@@ -0,0 +1,34 @@
+import { PolygonsLineAttrib } from "@/request/type";
+import { WholeLineLine, WholeLineLineProps } from "drawing-board";
+import { Line } from "konva/lib/shapes/Line";
+
+const actShapeFactory = () => {
+  const line = new Line({
+    strokeScaleEnabled: false,
+    strokeWidth: 5,
+  });
+  return {
+    shape: line,
+    setData(data: number[]) {
+      line.points(data);
+    },
+    common: () => {
+      line.stroke("#FFFFFF");
+    },
+    hover: () => {
+      line.stroke("#FF0000");
+    },
+  };
+};
+
+export class PYLine extends WholeLineLine<PolygonsLineAttrib> {
+  constructor(props: WholeLineLineProps<PolygonsLineAttrib>) {
+    super(props);
+    this.actShapeFactory = actShapeFactory;
+  }
+
+  mounted() {
+    super.mounted();
+    this.enableMouseAct(this.actShape);
+  }
+}

+ 161 - 0
src/view/map/board/point.ts

@@ -0,0 +1,161 @@
+import { PolygonsPointAttrib } from "@/request/type";
+import {
+  openEntityDrag,
+  getRealAbsoluteSize,
+  WholeLinePoint,
+  getWholeLinePolygonPoints,
+  WholeLinePointProps,
+} from "drawing-board";
+import { Path } from "konva/lib/shapes/Path";
+import { Group } from "konva/lib/Group";
+import { Circle } from "konva/lib/shapes/Circle";
+import { Label, Tag } from "konva/lib/shapes/Label";
+import { Text } from "konva/lib/shapes/Text";
+import { Polygons } from "./polygons";
+
+const actShapeFactory = (attrib: PolygonsPointAttrib, tree: any) => {
+  const polygons = tree.parent as Polygons;
+  const size = { width: 40, height: 40 };
+  const out = new Path({
+    data: `M22 44C32.6667 33.891 38 25.891 38 20C38 11.1634 30.8366 4 22 4C13.1634 4 6 11.1634 6 20C6 25.891 11.3333 33.891 22 44Z`,
+  });
+  const inner = new Path({
+    fill: "#fff",
+    data: `M22 30C27.5228 30 32 25.5228 32 20C32 14.4772 27.5228 10 22 10C16.4772 10 12 14.4772 12 20C12 25.5228 16.4772 30 22 30Z`,
+  });
+  const rect = new Circle({
+    radius: Math.min(size.width, size.height) / 2,
+    fill: "rgba(0, 0, 0, 0)",
+    offset: { x: -size.width / 2, y: -size.height / 2 },
+  });
+  const index = new Text({
+    name: "text",
+    text: `1`,
+    fontFamily: "Calibri",
+    fontSize: 12,
+    padding: 5,
+    offsetY: -8,
+    fill: "#000",
+  });
+
+  const label = new Label({
+    visible: false,
+    opacity: 0.75,
+    name: "label",
+    offsetX: -size.width / 2,
+    offsetY: -6,
+  });
+
+  label.add(
+    new Tag({
+      name: "tag",
+      fill: "rgba(255, 255, 255, 0.8)",
+      pointerDirection: "down",
+      pointerWidth: 5,
+      pointerHeight: 5,
+      lineJoin: "round",
+      shadowColor: "black",
+      shadowBlur: 10,
+      shadowOffsetX: 10,
+      shadowOffsetY: 10,
+      shadowOpacity: 0.5,
+    }),
+    new Text({
+      name: "text",
+      text: `P${attrib.id}`,
+      fontFamily: "Calibri",
+      fontSize: 10,
+      padding: 5,
+      fill: "#000",
+    })
+  );
+
+  const offsetGroup = new Group();
+  offsetGroup.add(out, inner, rect, label, index);
+  offsetGroup.x(-size.width / 2);
+  offsetGroup.y(-size.height);
+
+  const group = new Group();
+  group.add(offsetGroup);
+
+  const setStyle = () => {
+    let [width, height] = getRealAbsoluteSize(group, [1, 1]);
+    group.scale({ x: width, y: height });
+
+    if (polygons.currentPolygonId) {
+      const points = getWholeLinePolygonPoints(
+        polygons.attrib,
+        polygons.currentPolygonId
+      ).map(({ id }) => id);
+      const ndx = points.indexOf(attrib.id);
+      if (~ndx) {
+        index.text((ndx + 1).toString()).visible(true);
+        index.offsetX(-rect.width() / 2 + index.width() / 2.5);
+        return;
+      }
+    }
+    index.visible(false);
+  };
+
+  const result = {
+    shape: group,
+    common: () => {
+      out.fill(attrib.rtk ? "#728190" : "#409EFF");
+      label.visible(false);
+      polygons.bus.emit("hoverPoint", null);
+    },
+    hover: () => {
+      out.fill(attrib.rtk ? "#728190" : "#E6A23C");
+      label.visible(true);
+      polygons.bus.emit("hoverPoint", tree.attrib);
+    },
+    setData(data: number[]) {
+      setStyle();
+      group.x(data[0]);
+      group.y(data[1]);
+    },
+    active() {
+      out.fill("#E6A23C");
+      if (!polygons.currentPolygonId) {
+        polygons.bus.emit("activePoint", tree.attrib);
+      }
+    },
+    draging() {
+      out.fill("#E6A23C");
+    },
+  };
+
+  if (attrib.rtk) {
+    return result;
+  } else {
+    return {
+      ...result,
+      draging() {
+        out.fill("#E6A23C");
+      },
+    };
+  }
+};
+
+export class PYPoint extends WholeLinePoint<PolygonsPointAttrib, Group> {
+  constructor(props: WholeLinePointProps<PolygonsPointAttrib>) {
+    super(props);
+    this.actShapeFactory = actShapeFactory;
+  }
+
+  mounted() {
+    super.mounted();
+    if (!this.attrib.rtk) {
+      openEntityDrag(this, {
+        readyHandler: (attrib) => {
+          return [attrib.x, attrib.y];
+        },
+        moveHandler: (pointAttrib, move) => {
+          pointAttrib.x = move[0];
+          pointAttrib.y = move[1];
+        },
+      });
+    }
+    this.enableMouseAct(this.actShape);
+  }
+}

+ 350 - 0
src/view/map/board/polygons.ts

@@ -0,0 +1,350 @@
+import {
+  PolygonsAttrib,
+  PolygonsLineAttrib,
+  PolygonsPointAttrib,
+} from "@/request/type";
+import {
+  WholeLine,
+  EntityDragHandlers,
+  openEntityDrag,
+  getRealAbsoluteSize,
+  incEntitysFactoryGenerate,
+  WholeLineLine,
+  WholeLinePoint,
+  Attrib,
+  wholeLineFixLineAddPoint,
+  wholeLinePolygonLastAddPoint,
+  wholeLinePolygonAddPoint,
+  wholeLineAddPoint,
+  shapeParentsEq,
+  getWholeLinePolygonPoints,
+  wholeLineDelPointByPointIds,
+} from "drawing-board";
+import mitt from "mitt";
+import { Path } from "konva/lib/shapes/Path";
+import { Group } from "konva/lib/Group";
+import { Circle } from "konva/lib/shapes/Circle";
+import { Line } from "konva/lib/shapes/Line";
+import { Label, Tag } from "konva/lib/shapes/Label";
+import { Text } from "konva/lib/shapes/Text";
+
+// 加点
+const getPolygonPoint = (position: number[]) => {
+  const pointAttrib = {
+    rtk: false,
+    x: position[0],
+    y: position[1],
+  };
+  return pointAttrib;
+};
+
+export class Polygons extends WholeLine<PolygonsAttrib & Attrib, Group, Line> {
+  bus = mitt<{
+    hoverPoint: PolygonsPointAttrib | null;
+    activePoint: PolygonsPointAttrib;
+  }>();
+  points: PolygonsPointAttrib[] = [];
+  pointDragHandler = {} as EntityDragHandlers<PolygonsPointAttrib, number[]>;
+
+  diffRedraw() {
+    const inc = super.diffRedraw();
+    inc.pointEntityInc.adds.forEach((point) => {
+      if (!point.attrib.rtk) {
+        openEntityDrag(point, this.pointDragHandler);
+      }
+      point.enableMouseAct(point.actShape);
+    });
+    inc.lineEntityInc.adds.forEach((line) => {
+      line.enableMouseAct(line.actShape);
+    });
+    return inc;
+  }
+
+  initIncFactory() {
+    this.incLinesFactory = incEntitysFactoryGenerate(
+      WholeLineLine<PolygonsLineAttrib, Line>,
+      this,
+      (line) => {
+        line.setConfig(this.attrib);
+        line.setActShapeFactory(lineActShapeFactory);
+      }
+    );
+    this.incPointsFactory = incEntitysFactoryGenerate(
+      WholeLinePoint<PolygonsPointAttrib, Group>,
+      this,
+      (point) => {
+        point.setActShapeFactory(pointActShapeFactory);
+      }
+    );
+  }
+
+  currentPolygonId: string | null = null;
+
+  enableAddMode(polygonId: string) {
+    this.modeDestory();
+    this.currentPolygonId = polygonId;
+
+    const polyginAttrib = this.attrib.polygons.find(
+      ({ id }) => id === polygonId
+    );
+    if (!polyginAttrib) return;
+
+    const root = this.container;
+    const stage = root.stage;
+    let prevPid: string;
+    stage.on("click.addMode", (evt) => {
+      const target = shapeParentsEq(evt.target, (shape) => {
+        const id = shape.id();
+        return (
+          id.includes(WholeLineLine.namespace) ||
+          id.includes(WholeLinePoint.namespace)
+        );
+      });
+      const child = target && this.find(target.id());
+      const pixel = [evt.evt.x, evt.evt.y];
+
+      if (child instanceof WholeLineLine) {
+        wholeLineFixLineAddPoint(
+          this.attrib,
+          child.attrib.id,
+          getPolygonPoint(root.getRealFromStage(pixel))
+        );
+      } else {
+        const pointAttrib =
+          child instanceof WholeLinePoint
+            ? child.attrib
+            : getPolygonPoint(this.container.getRealFromStage(pixel));
+
+        const exixts = getWholeLinePolygonPoints(
+          this.attrib,
+          polyginAttrib.id
+        ).some(({ id }) => id === pointAttrib.id);
+
+        if (exixts) {
+          return;
+        }
+
+        if (polyginAttrib.lineIds.length > 0) {
+          wholeLinePolygonLastAddPoint(this.attrib, polygonId, pointAttrib);
+        } else if (prevPid) {
+          wholeLinePolygonAddPoint(
+            this.attrib,
+            polygonId,
+            prevPid,
+            pointAttrib
+          );
+        } else {
+          prevPid = wholeLineAddPoint(this.attrib, pointAttrib)!.id as string;
+        }
+      }
+    });
+
+    this.redraw();
+  }
+
+  enableDelMode(polygonId: string) {
+    this.currentPolygonId = polygonId;
+    this.modeDestory();
+
+    const polyginAttrib = this.attrib.polygons.find(
+      ({ id }) => id === polygonId
+    );
+    if (!polyginAttrib) return;
+
+    const root = this.container;
+    const stage = root.stage;
+    stage.on("click.delMode", (evt) => {
+      const target = shapeParentsEq(evt.target, (shape) => {
+        const id = shape.id();
+        return id.includes(WholeLinePoint.namespace);
+      });
+      const child = target && this.find(target.id());
+      if (child) {
+        const pointAttrib = child.attrib as PolygonsPointAttrib;
+        wholeLineDelPointByPointIds(
+          this.attrib,
+          pointAttrib.id,
+          !pointAttrib.rtk
+        );
+      }
+    });
+
+    this.redraw();
+  }
+
+  private modeDestory() {
+    this.container.stage.off("click.addMode");
+    this.container.stage.off("click.delMode");
+  }
+
+  commonMode() {
+    this.currentPolygonId = null;
+    this.modeDestory();
+    this.redraw();
+  }
+
+  init() {
+    this.pointDragHandler = {
+      readyHandler: (attrib) => {
+        return [attrib.x, attrib.y];
+      },
+      moveHandler: (pointAttrib, move) => {
+        pointAttrib.x = move[0];
+        pointAttrib.y = move[1];
+      },
+    };
+    super.init();
+  }
+  mounted(): void {
+    super.mounted();
+    this.enableAddMode("1");
+  }
+
+  destory(): void {
+    super.destory();
+    this.modeDestory();
+  }
+}
+
+const pointActShapeFactory = (attrib: PolygonsPointAttrib, tree: any) => {
+  const polygons = tree.parent as Polygons;
+  const size = { width: 40, height: 40 };
+  const out = new Path({
+    data: `M22 44C32.6667 33.891 38 25.891 38 20C38 11.1634 30.8366 4 22 4C13.1634 4 6 11.1634 6 20C6 25.891 11.3333 33.891 22 44Z`,
+  });
+  const inner = new Path({
+    fill: "#fff",
+    data: `M22 30C27.5228 30 32 25.5228 32 20C32 14.4772 27.5228 10 22 10C16.4772 10 12 14.4772 12 20C12 25.5228 16.4772 30 22 30Z`,
+  });
+  const rect = new Circle({
+    radius: Math.min(size.width, size.height) / 2,
+    fill: "rgba(0, 0, 0, 0)",
+    offset: { x: -size.width / 2, y: -size.height / 2 },
+  });
+  const index = new Text({
+    name: "text",
+    text: `1`,
+    fontFamily: "Calibri",
+    fontSize: 12,
+    padding: 5,
+    offsetY: -8,
+    fill: "#000",
+  });
+
+  const label = new Label({
+    visible: false,
+    opacity: 0.75,
+    name: "label",
+    offsetX: -size.width / 2,
+    offsetY: -6,
+  });
+
+  label.add(
+    new Tag({
+      name: "tag",
+      fill: "rgba(255, 255, 255, 0.8)",
+      pointerDirection: "down",
+      pointerWidth: 5,
+      pointerHeight: 5,
+      lineJoin: "round",
+      shadowColor: "black",
+      shadowBlur: 10,
+      shadowOffsetX: 10,
+      shadowOffsetY: 10,
+      shadowOpacity: 0.5,
+    }),
+    new Text({
+      name: "text",
+      text: `P${attrib.id}`,
+      fontFamily: "Calibri",
+      fontSize: 10,
+      padding: 5,
+      fill: "#000",
+    })
+  );
+
+  const offsetGroup = new Group();
+  offsetGroup.add(out, inner, rect, label, index);
+  offsetGroup.x(-size.width / 2);
+  offsetGroup.y(-size.height);
+
+  const group = new Group();
+  group.add(offsetGroup);
+
+  const setStyle = () => {
+    let [width, height] = getRealAbsoluteSize(group, [1, 1]);
+    group.scale({ x: width, y: height });
+
+    if (polygons.currentPolygonId) {
+      const points = getWholeLinePolygonPoints(
+        polygons.attrib,
+        polygons.currentPolygonId
+      ).map(({ id }) => id);
+      const ndx = points.indexOf(attrib.id);
+      if (~ndx) {
+        index.text((ndx + 1).toString()).visible(true);
+        index.offsetX(-rect.width() / 2 + index.width() / 2.5);
+        return;
+      }
+    }
+    index.visible(false);
+  };
+
+  const result = {
+    shape: group,
+    common: () => {
+      out.fill(attrib.rtk ? "#728190" : "#409EFF");
+      label.visible(false);
+      polygons.bus.emit("hoverPoint", null);
+    },
+    hover: () => {
+      out.fill(attrib.rtk ? "#728190" : "#E6A23C");
+      label.visible(true);
+      polygons.bus.emit("hoverPoint", tree.attrib);
+    },
+    setData(data: number[]) {
+      setStyle();
+      group.x(data[0]);
+      group.y(data[1]);
+    },
+    active() {
+      out.fill("#E6A23C");
+      if (!polygons.currentPolygonId) {
+        polygons.bus.emit("activePoint", tree.attrib);
+      }
+    },
+    draging() {
+      out.fill("#E6A23C");
+    },
+  };
+
+  if (attrib.rtk) {
+    return result;
+  } else {
+    return {
+      ...result,
+      draging() {
+        out.fill("#E6A23C");
+      },
+    };
+  }
+};
+
+const lineActShapeFactory = () => {
+  const line = new Line({
+    strokeScaleEnabled: false,
+    strokeWidth: 5,
+  });
+  return {
+    shape: line,
+    setData(data: number[]) {
+      line.points(data);
+    },
+    common: () => {
+      line.stroke("#FFFFFF");
+    },
+    hover: () => {
+      line.stroke("#FF0000");
+    },
+  };
+};

+ 230 - 0
src/view/map/board/storeData.json

@@ -0,0 +1,230 @@
+{
+  "rooms": [
+    {
+      "id": "2",
+      "points": [
+        {
+          "id": "1",
+          "x": 881,
+          "y": 492
+        },
+        {
+          "id": "2",
+          "x": 136,
+          "y": 237
+        },
+        {
+          "id": "8",
+          "x": 371,
+          "y": 699
+        },
+        {
+          "id": "18",
+          "x": 480,
+          "y": 511
+        },
+        {
+          "id": "19",
+          "x": 83,
+          "y": 647
+        },
+        {
+          "id": "3",
+          "x": 178,
+          "y": 803
+        },
+        {
+          "x": 675.8655056700748,
+          "y": 268.3909090194661,
+          "id": "20"
+        },
+        {
+          "x": 163.5307763543757,
+          "y": 608.0099861089675,
+          "id": "21"
+        },
+        {
+          "x": 579.4292704980454,
+          "y": 468.065825925416,
+          "id": "22"
+        },
+        {
+          "x": 579.1720366885647,
+          "y": 467.6217710319711,
+          "id": "23"
+        },
+        {
+          "x": 445.2292582546364,
+          "y": 325.58255334590945,
+          "id": "24"
+        }
+      ],
+      "polygons": [
+        {
+          "id": "1",
+          "lineIds": [
+            "1",
+            "2",
+            "3",
+            "4",
+            "5",
+            "6",
+            "7",
+            "8",
+            "9",
+            "10",
+            "11",
+            "12",
+            "13"
+          ]
+        },
+        {
+          "id": "2",
+          "lineIds": [
+            "14",
+            "15",
+            "16",
+            "17",
+            "18",
+            "19"
+          ]
+        }
+      ],
+      "lines": [
+        {
+          "id": "1",
+          "pointIds": [
+            "1",
+            "22"
+          ]
+        },
+        {
+          "id": "2",
+          "pointIds": [
+            "22",
+            "23"
+          ]
+        },
+        {
+          "id": "3",
+          "pointIds": [
+            "23",
+            "24"
+          ]
+        },
+        {
+          "id": "4",
+          "pointIds": [
+            "24",
+            "2"
+          ]
+        },
+        {
+          "id": "5",
+          "pointIds": [
+            "2",
+            "20"
+          ]
+        },
+        {
+          "id": "6",
+          "pointIds": [
+            "20",
+            "22"
+          ]
+        },
+        {
+          "id": "7",
+          "pointIds": [
+            "22",
+            "8"
+          ]
+        },
+        {
+          "id": "8",
+          "pointIds": [
+            "8",
+            "18"
+          ]
+        },
+        {
+          "id": "9",
+          "pointIds": [
+            "18",
+            "23"
+          ]
+        },
+        {
+          "id": "10",
+          "pointIds": [
+            "23",
+            "20"
+          ]
+        },
+        {
+          "id": "11",
+          "pointIds": [
+            "20",
+            "24"
+          ]
+        },
+        {
+          "id": "12",
+          "pointIds": [
+            "24",
+            "21"
+          ]
+        },
+        {
+          "id": "13",
+          "pointIds": [
+            "21",
+            "19"
+          ]
+        },
+        {
+          "id": "14",
+          "pointIds": [
+            "3",
+            "21"
+          ]
+        },
+        {
+          "id": "15",
+          "pointIds": [
+            "21",
+            "2"
+          ]
+        },
+        {
+          "id": "16",
+          "pointIds": [
+            "2",
+            "24"
+          ]
+        },
+        {
+          "id": "17",
+          "pointIds": [
+            "24",
+            "23"
+          ]
+        },
+        {
+          "id": "18",
+          "pointIds": [
+            "23",
+            "22"
+          ]
+        },
+        {
+          "id": "19",
+          "pointIds": [
+            "22",
+            "1"
+          ]
+        }
+      ]
+    }
+  ]
+}

+ 255 - 0
src/view/map/map-borad.vue

@@ -0,0 +1,255 @@
+<template>
+  <div class="map-layout">
+    <div id="map" class="map-container" ref="mapContainer" @click.stop>
+      <div class="map-component">
+        <el-select
+          v-model="tileType"
+          placeholder="选择底图"
+          style="width: 120px"
+          class="tile-type-select"
+        >
+          <el-option
+            v-for="item in tileOptions"
+            :key="item"
+            :label="item"
+            :value="item"
+          />
+        </el-select>
+      </div>
+
+      <div class="board" ref="boardContainer"></div>
+    </div>
+    <div class="right-control">
+      <MapRight
+        @fly-point="flyScenePoint"
+        @fly-scene="flyScene"
+        @goto-point="gotoPointPage"
+      />
+    </div>
+  </div>
+</template>
+
+<script setup lang="ts">
+import MapRight from "./map-right.vue";
+import { router, setDocTitle } from "@/router";
+import { Manage } from "./openlayer";
+import { ScenePoint, Scene, scenePoints, scenes } from "@/store/scene";
+import { initRelics, initSelfRelics, relics } from "@/store/relics";
+import { onMounted, ref, watchEffect, watch, onUnmounted } from "vue";
+import { createBoard } from "./board/";
+import { relicsPolyginsFetch } from "@/request";
+import {
+  mapManageInit,
+  boardInit,
+  flyUserCenter,
+  tileOptions,
+  tileType,
+} from "./map-flow";
+
+const gotoPointPage = (point: ScenePoint) => {
+  router.push({
+    name: router.currentRoute.value.name === "map" ? "pano" : "query-pano",
+    params: { pid: point.id },
+  });
+};
+
+const autoInitPos = () => {
+  const scene = scenes.value.find(
+    (scene) => !scene.scenePos.every((pos) => !pos.pos || pos.pos.length === 0)
+  );
+  if (scene) {
+    flyScene(scene);
+    return true;
+  } else {
+    return false;
+  }
+};
+const flyPos = (pos: number[]) => mapManage.map.getView().setCenter(pos);
+const flyScene = (scene: Scene) => {
+  const totalPos = [0, 0];
+  let numCalc = 0;
+  for (let i = 0; i < scene.scenePos.length; i++) {
+    const coord = scene.scenePos[i].pos as number[];
+    if (coord && coord.length > 0) {
+      totalPos[0] += coord[0];
+      totalPos[1] += coord[1];
+      numCalc++;
+    }
+  }
+
+  totalPos[0] /= numCalc;
+  totalPos[1] /= numCalc;
+  flyPos(totalPos);
+};
+
+const flyScenePoint = (point: ScenePoint) => {
+  flyPos(point.pos);
+};
+
+watch(
+  () => [router.currentRoute.value.name, router.currentRoute.value.params?.relicsId],
+  ([name, rid], old) => {
+    if (["map", "query-map"].includes(name as string) && (!old || old[1] !== rid)) {
+      relics.value = undefined;
+      const fn = name === "map" ? initSelfRelics : initRelics;
+      fn(Number(rid)).finally(() => {
+        if (!relics.value) {
+          return router.replace({ name: "relics" });
+        }
+        if (mapManage && !autoInitPos()) {
+          flyUserCenter(mapManage);
+        }
+
+        relicsPolyginsFetch().then((data) => {
+          board.setData(data, router.currentRoute.value.params.relicsId as string);
+          window.board = board;
+          console.log(board.polygon());
+          board.polygon().bus.on("activePoint", (bpoint) => {
+            const point =
+              bpoint.id &&
+              scenePoints.value.find((point) => point.id.toString() === bpoint.id);
+            point && gotoPointPage(point);
+          });
+        });
+      });
+    }
+  },
+  { immediate: true }
+);
+
+watch(
+  () => {
+    const scene = scenes.value.find(
+      (scene) => !scene.scenePos.every((pos) => !pos.pos || pos.pos.length === 0)
+    );
+    return scene?.sceneCode;
+  },
+  (firstCode) => {
+    if (firstCode && mapManage) {
+      autoInitPos();
+    }
+  }
+);
+
+watchEffect(() => {
+  if (
+    ["map", "query-map"].includes(router.currentRoute.value.name as string) &&
+    relics.value
+  ) {
+    setDocTitle(relics.value.name);
+  }
+});
+
+// ----------流程--------
+const mapContainer = ref<HTMLDivElement>();
+const boardContainer = ref<HTMLDivElement>();
+let mapManage: Manage;
+let board: ReturnType<typeof createBoard>;
+
+onMounted(async () => {
+  mapManage = mapManageInit(mapContainer.value!);
+  board = boardInit(boardContainer.value!, mapManage);
+});
+
+onUnmounted(() => {
+  mapManage.map.dispose();
+  board.destory();
+});
+</script>
+
+<style lang="scss">
+.tooltip {
+  pointer-events: none;
+}
+.map-layout {
+  display: flex;
+  flex-direction: row;
+  height: 100%;
+}
+
+.map-container {
+  flex: 1;
+  position: relative;
+}
+
+.right-control {
+  flex: none;
+  width: 300px;
+  padding: 15px;
+
+  border-left: 1px solid var(--border-color);
+}
+
+.map-component {
+  width: 100%;
+  height: 100%;
+  position: relative;
+}
+
+.active {
+  cursor: pointer;
+}
+
+.active-point {
+  position: absolute;
+  pointer-events: none;
+}
+
+.map-component {
+  pointer-events: none;
+  position: absolute;
+  width: 100%;
+  height: 100%;
+  left: 0;
+  top: 0;
+  z-index: 9;
+}
+.env {
+  width: 100%;
+  height: 100%;
+}
+
+.tile-type-select {
+  pointer-events: all;
+  position: absolute;
+  right: 10px;
+  top: 10px;
+}
+
+.scale-view {
+  --color: #fff;
+  position: absolute;
+  left: 20px;
+  bottom: 20px;
+  height: 8px;
+  color: var(--color);
+  text-align: center;
+  border: 1px solid var(--color);
+  border-top: none;
+  z-index: 1;
+  font-size: 14px;
+  display: flex;
+  align-items: end;
+  &.light {
+    --color: #fff;
+    > div {
+      text-shadow: 0 0 2px #000;
+    }
+  }
+  &.dark {
+    --color: #000;
+    > div {
+      text-shadow: 0 0 2px #fff;
+    }
+  }
+}
+
+.board {
+  position: absolute;
+  left: 0;
+  top: 0;
+  bottom: 0;
+  right: 0;
+  z-index: 1;
+}
+</style>

+ 68 - 0
src/view/map/map-flow.ts

@@ -0,0 +1,68 @@
+import { TileType, createMap, Manage } from "./openlayer";
+import { createBoard } from "./board/";
+import { ref, watch, watchEffect } from "vue";
+import ScaleLine from "ol/control/ScaleLine";
+
+export const tileOptions: TileType[] = ["影像底图", "矢量底图"];
+export const tileType = ref<TileType>(tileOptions[0]);
+const defaultCenter = [116.412611, 39.908866];
+
+const refreshTileType = (mapManage: Manage) => {
+  mapManage.setTileType(tileType.value);
+};
+
+export const flyUserCenter = (mapManage: Manage) => {
+  mapManage.setCenter(defaultCenter);
+  navigator.geolocation.getCurrentPosition(
+    (pos) => {
+      console.log("获取中心位置成功", pos);
+      mapManage.setCenter([pos.coords.longitude, pos.coords.latitude]);
+    },
+    (e) => {
+      console.error(e);
+      console.error("获取中心位置失败");
+    },
+    {
+      enableHighAccuracy: false,
+      timeout: 50000,
+      maximumAge: 0,
+    }
+  );
+};
+
+const addScale = (mapManage: Manage) => {
+  const scaleLine = new ScaleLine({
+    className: "scale-view",
+    maxWidth: 150,
+    minWidth: 100,
+    units: "metric",
+  });
+  // 加载比例尺
+  mapManage.map.addControl(scaleLine);
+
+  watch(
+    tileType,
+    (type) => {
+      const el = (scaleLine as any).element as HTMLDivElement;
+      el.classList.add(type === "影像底图" ? "light" : "dark");
+      el.classList.remove(type === "影像底图" ? "dark" : "light");
+      console.log(el, type);
+    },
+    { flush: "post", immediate: true }
+  );
+};
+
+export const mapManageInit = (container: HTMLDivElement) => {
+  const mapManage = createMap(container!);
+  mapManage.setCenter(defaultCenter);
+
+  watchEffect(() => refreshTileType(mapManage));
+  addScale(mapManage);
+
+  return mapManage;
+};
+
+export const boardInit = (container: HTMLDivElement, mapManage: Manage) => {
+  const board = createBoard(container!, mapManage);
+  return board;
+};

+ 1 - 0
src/view/map/map-right.vue

@@ -9,6 +9,7 @@
             </template>
           </el-input>
         </el-form-item> -->
+
         <el-form-item>
           <el-button type="primary" :icon="Plus" style="width: 100%" @click="addHandler">
             添加场景

+ 2 - 2
src/view/map/map.vue

@@ -47,11 +47,10 @@
 <script setup lang="ts">
 import MapRight from "./map-right.vue";
 import { router, setDocTitle } from "@/router";
-import { TileType, createMap } from "./";
+import { TileType, createMap, Manage } from "./openlayer";
 import { ScenePoint, Scene, scenePoints, scenes } from "@/store/scene";
 import { initRelics, initSelfRelics, relics } from "@/store/relics";
 import { computed, onMounted, ref, watchEffect, watch, onUnmounted } from "vue";
-import { Manage } from "./manage";
 import ScaleLine from "ol/control/ScaleLine";
 
 const activeId = ref<ScenePoint["id"] | null>();
@@ -338,3 +337,4 @@ onUnmounted(() => mapManage.map.dispose());
   }
 }
 </style>
+./openlayer./openlayer/manage

src/view/map/hot.ts → src/view/map/openlayer/hot.ts


+ 2 - 0
src/view/map/index.ts

@@ -4,3 +4,5 @@ export type { TileType } from "./tile";
 export const createMap = (dom: HTMLDivElement) => {
   return new Manage(dom);
 };
+
+export * from "./manage";

+ 16 - 0
src/view/map/manage.ts

@@ -9,6 +9,7 @@ import {
   clearHots,
 } from "./hot";
 import { Emitter } from "mitt";
+import { boundingExtent } from "ol/extent";
 
 const createMap = (container: HTMLDivElement) => {
   const view = new View({
@@ -61,6 +62,21 @@ export class Manage {
     this.map.render();
   }
 
+  getBound() {
+    return this.map.getView().calculateExtent(this.map.getSize());
+  }
+  setBound(bound: number[]) {
+    const extent = boundingExtent([
+      [bound[0], bound[1]],
+      [bound[2], bound[3]],
+    ]);
+    this.map.getView().fit(extent, {
+      size: this.map.getSize(),
+      padding: [0, 0, 0, 0], // 根据需要调整边距
+      maxZoom: 22, // 防止过度放大
+    });
+  }
+
   render() {
     this.map.render();
   }

src/view/map/tile.ts → src/view/map/openlayer/tile.ts


+ 29 - 0
src/view/map/test-board.vue

@@ -0,0 +1,29 @@
+<template>
+  <div class="board-layout" ref="containerRef"></div>
+</template>
+
+<script setup lang="ts">
+import { ref, watch } from "vue";
+import { register, EditWholeLine } from "drawing-board";
+import storeData from "./board/storeData.json";
+
+const containerRef = ref<HTMLDivElement>();
+const initBoard = register({ rooms: EditWholeLine });
+watch(containerRef, (container, _, onClanup) => {
+  if (container) {
+    const board = initBoard(container, storeData, false);
+    console.log(board.tree);
+    onClanup(() => board.destory);
+  }
+});
+</script>
+
+<style lang="scss" scoped>
+.board-layout {
+  position: relative;
+  display: inline-block;
+
+  width: 100vw;
+  height: 100vh;
+}
+</style>

+ 3 - 1
src/view/step-tree/helper.ts

@@ -270,7 +270,9 @@ export const getTextBound = (
   ctx.font = font;
   const textMetrics = ctx.measureText(text);
   const width = textMetrics.width + (padding[1] + margin[1]) * 2;
-  const height = textMetrics.hangingBaseline + (padding[0] + margin[0]) * 2;
+  const fontHeight =
+    textMetrics.fontBoundingBoxAscent + textMetrics.fontBoundingBoxDescent;
+  const height = fontHeight + (padding[0] + margin[0]) * 2;
 
   return { width, height };
 };

+ 6 - 0
tsconfig.json

@@ -25,6 +25,12 @@
     "paths": {
       "@/*": [
         "src/*"
+      ],
+      "drawing-board": [
+        "../drawing-board/src/board/index.ts"
+      ],
+      "drawing-board/*": [
+        "../drawing-board/src/board/*"
       ]
     }
   },

+ 4 - 0
vite.config.ts

@@ -12,6 +12,10 @@ export default defineConfig({
         find: "@",
         replacement: resolve(__dirname, "./src"),
       },
+      {
+        find: "drawing-board",
+        replacement: resolve(__dirname, "../drawing-board/src/board"),
+      },
     ],
   },
   server: {