Parcourir la source

feat: dxf导出支持

bill il y a 3 mois
Parent
commit
b8d6b123a4
48 fichiers modifiés avec 1056 ajouts et 543 suppressions
  1. 2 9
      .env
  2. 2 12
      .env.development
  3. 2 11
      .env.test
  4. 4 0
      package.json
  5. 90 0
      pnpm-lock.yaml
  6. 1 0
      public/icons/Chair.svg
  7. 1 0
      public/icons/ThreeSofa.svg
  8. 1 0
      src/core/components/circle/temp-circle.vue
  9. 1 0
      src/core/components/icon/icon.vue
  10. 1 1
      src/core/components/icon/index.ts
  11. 19 16
      src/core/components/icon/temp-icon.vue
  12. 1 0
      src/core/components/polygon/index.ts
  13. 315 0
      src/core/hook/use-dxf.ts
  14. 6 27
      src/core/hook/use-expose.ts
  15. 8 8
      src/core/hook/use-status.ts
  16. 46 11
      src/core/hook/use-viewer.ts
  17. 21 0
      src/core/html-mount/propertys/components/text.vue
  18. 5 0
      src/core/html-mount/propertys/describes.json
  19. 4 1
      src/core/html-mount/propertys/index.ts
  20. 4 6
      src/example/components/container/container.vue
  21. 5 2
      src/example/components/header/actions.ts
  22. 1 4
      src/example/components/header/index.vue
  23. 3 2
      src/example/components/show-vr.vue
  24. 10 3
      src/example/components/slide/actions.ts
  25. 3 7
      src/example/components/slide/slide.vue
  26. 114 0
      src/example/constant.ts
  27. 1 1
      src/example/dialog/ai/index.ts
  28. 0 65
      src/example/dialog/vr/test.ts
  29. 1 28
      src/example/dialog/vr/vr.vue
  30. 1 0
      src/example/env.ts
  31. 3 4
      src/example/styles/global.scss
  32. 1 7
      src/example/fuse/main.ts
  33. 0 68
      src/example/fuse/req.ts
  34. 12 3
      src/example/fuse/store.ts
  35. 15 12
      src/example/fuse/views/overview/header.vue
  36. 1 1
      src/example/fuse/views/overview/index.vue
  37. 3 5
      src/example/fuse/views/overview/query.vue
  38. 4 105
      src/example/fuse/views/overview/slide-icons.vue
  39. 1 2
      src/example/fuse/views/tabulation/gen.ts
  40. 1 2
      src/example/fuse/views/tabulation/header.vue
  41. 1 1
      src/example/fuse/views/tabulation/index.vue
  42. 3 5
      src/example/fuse/views/tabulation/query.vue
  43. 61 29
      src/example/platform/platform-draw.ts
  44. 125 68
      src/example/platform/platform-resource.ts
  45. 2 1
      src/global.d.ts
  46. 144 0
      src/mock.ts
  47. 1 9
      src/vite-env.d.ts
  48. 5 7
      vite.config.ts

+ 2 - 9
.env

@@ -1,12 +1,5 @@
 VITE_PRIMARY='#D8000A'
 VITE_TITLE='绘图'
-VITE_ENTRY='src/example/fuse/main.ts'
+VITE_ENTRY='./mock.ts'
+VITE_ENTRY_EXAMPLE='./example/fuse/main.ts'
 
-VITE_MESH_TEMP_URL='https://test.4dkankan.com/spg.html?m={m}&lang=zh'
-VITE_CLOUD_TEMP_URL='https://uat-laser.4dkankan.com/uat/index.html?m={m}&lang=zh'
-VITE_FUSE_TEMP_URL='https://test-mix3d.4dkankan.com/code/index.html?caseId={m}&app=1&share=1#/show/summary'
-
-VITE_MESH_OSS_URL='https://4dkk.4dage.com/'
-VITE_MESH_API_URL='https://test.4dkankan.com/'
-VITE_CLOUD_API_URL='https://uat-laser.4dkankan.com/'
-VITE_FUSE_API_URL='https://test-mix3d.4dkankan.com/'

+ 2 - 12
.env.development

@@ -1,14 +1,4 @@
 VITE_PRIMARY='#109BE0'
 VITE_TITLE='绘图'
-VITE_ENTRY='src/example/fuse/main.ts'
-
-VITE_MESH_TEMP_URL='https://test.4dkankan.com/spg.html?m={m}&lang=zh'
-VITE_CLOUD_TEMP_URL='https://uat-laser.4dkankan.com/uat/index.html?m={m}&lang=zh'
-VITE_FUSE_TEMP_URL='https://test-mix3d.4dkankan.com/code/index.html?caseId={m}&app=1&share=1#/show/summary'
-
-VITE_MESH_OSS_URL='/meshOSS'
-VITE_MESH_API_URL='/meshAPI'
-VITE_CLOUD_API_URL='/cloudAPI'
-VITE_FUSE_API_URL='/fuseAPI'
-
-VITE_TARGET='test'
+VITE_ENTRY='./mock.ts'
+VITE_ENTRY_EXAMPLE='./example/fuse/main.ts'

+ 2 - 11
.env.test

@@ -1,13 +1,4 @@
 VITE_PRIMARY='#D8000A'
 VITE_TITLE='绘图'
-VITE_ENTRY='src/example/fuse/main.ts'
-
-VITE_MESH_TEMP_URL='https://test.4dkankan.com/spg.html?m={m}&lang=zh'
-VITE_CLOUD_TEMP_URL='https://uat-laser.4dkankan.com/uat/index.html?m={m}&lang=zh'
-VITE_FUSE_TEMP_URL='https://test-mix3d.4dkankan.com/code/index.html?caseId={m}&app=1&share=1#/show/summary'
-
-
-VITE_MESH_OSS_URL='https://4dkk.4dage.com/'
-VITE_MESH_API_URL='https://test.4dkankan.com/'
-VITE_CLOUD_API_URL='https://uat-laser.4dkankan.com/'
-VITE_FUSE_API_URL='https://test-mix3d.4dkankan.com/'
+VITE_ENTRY='./mock.ts'
+VITE_ENTRY_EXAMPLE='./example/fuse/main.ts'

+ 4 - 0
package.json

@@ -10,12 +10,16 @@
   },
   "dependencies": {
     "@amap/amap-jsapi-loader": "^1.0.1",
+    "@tarikjabiri/dxf": "^2.8.9",
     "@tweenjs/tween.js": "^25.0.0",
     "@types/node": "^22.9.0",
+    "@types/svg-path-parser": "^1.1.6",
     "@types/three": "^0.169.0",
+    "dxf-writer": "^1.18.4",
     "element-plus": "^2.8.6",
     "html2canvas": "^1.4.1",
     "jspdf": "^3.0.1",
+    "jszip": "^3.10.1",
     "konva": "^9.3.18",
     "localforage": "^1.10.0",
     "mitt": "^3.0.1",

+ 90 - 0
pnpm-lock.yaml

@@ -11,15 +11,24 @@ importers:
       '@amap/amap-jsapi-loader':
         specifier: ^1.0.1
         version: 1.0.1
+      '@tarikjabiri/dxf':
+        specifier: ^2.8.9
+        version: 2.8.9
       '@tweenjs/tween.js':
         specifier: ^25.0.0
         version: 25.0.0
       '@types/node':
         specifier: ^22.9.0
         version: 22.14.1
+      '@types/svg-path-parser':
+        specifier: ^1.1.6
+        version: 1.1.6
       '@types/three':
         specifier: ^0.169.0
         version: 0.169.0
+      dxf-writer:
+        specifier: ^1.18.4
+        version: 1.18.4
       element-plus:
         specifier: ^2.8.6
         version: 2.9.8(vue@3.5.13(typescript@5.6.3))
@@ -29,6 +38,9 @@ importers:
       jspdf:
         specifier: ^3.0.1
         version: 3.0.1
+      jszip:
+        specifier: ^3.10.1
+        version: 3.10.1
       konva:
         specifier: ^9.3.18
         version: 9.3.20
@@ -492,6 +504,10 @@ packages:
   '@sxzz/popperjs-es@2.11.7':
     resolution: {integrity: sha512-Ccy0NlLkzr0Ex2FKvh2X+OyERHXJ88XJ1MXtsI9y9fGexlaXaVTPzBCRBwIxFkORuOb+uBqeu+RqnpgYTEZRUQ==}
 
+  '@tarikjabiri/dxf@2.8.9':
+    resolution: {integrity: sha512-ckBkEsWdlFTvqm2ARm23xX3EVouYsnNuZWS7P/oec1WCGTjzdd9jhmtQqyhh7TQSvQo4HunbDv4Vxiv07/8zsQ==}
+    engines: {node: '>=16', pnpm: '>=8'}
+
   '@trysound/sax@0.2.0':
     resolution: {integrity: sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==}
     engines: {node: '>=10.13.0'}
@@ -520,6 +536,9 @@ packages:
   '@types/stats.js@0.17.3':
     resolution: {integrity: sha512-pXNfAD3KHOdif9EQXZ9deK82HVNaXP5ZIF5RP2QG6OQFNTaY2YIetfrE9t528vEreGQvEPRDDc8muaoYeK0SxQ==}
 
+  '@types/svg-path-parser@1.1.6':
+    resolution: {integrity: sha512-3sw6pk91pEtW6W7hRrJ9ZkAgPiJSaNdh7iY8rVOy7buajpQuy2J9A0ZUaiOVcbFvl0p7J+Ne4012muCE/MB+hQ==}
+
   '@types/svgo@2.6.4':
     resolution: {integrity: sha512-l4cmyPEckf8moNYHdJ+4wkHvFxjyW6ulm9l4YGaOxeyBWPhBOT0gvni1InpFPdzx1dKf/2s62qGITwxNWnPQng==}
 
@@ -813,6 +832,9 @@ packages:
   core-js@3.41.0:
     resolution: {integrity: sha512-SJ4/EHwS36QMJd6h/Rg+GyR4A5xE0FSI3eZ+iBVpfqf1x0eTSg1smWLHrA+2jQThZSh97fmSgFSU8B61nxosxA==}
 
+  core-util-is@1.0.3:
+    resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==}
+
   cors@2.8.5:
     resolution: {integrity: sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==}
     engines: {node: '>= 0.10'}
@@ -945,6 +967,9 @@ packages:
     resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==}
     engines: {node: '>= 0.4'}
 
+  dxf-writer@1.18.4:
+    resolution: {integrity: sha512-JdLOyP+1UyeB30yPowJLJKK0bPROq/dX+QTWcLSplQoepcyo/YMlU0Z27T7mIPxgwiPb+CQWwUIlbcRRfns+ng==}
+
   ejs@3.1.10:
     resolution: {integrity: sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==}
     engines: {node: '>=0.10.0'}
@@ -1363,6 +1388,9 @@ packages:
   jspdf@3.0.1:
     resolution: {integrity: sha512-qaGIxqxetdoNnFQQXxTKUD9/Z7AloLaw94fFsOiJMxbfYdBbrBuhWmbzI8TVjrw7s3jBY1PFHofBKMV/wZPapg==}
 
+  jszip@3.10.1:
+    resolution: {integrity: sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==}
+
   kind-of@3.2.2:
     resolution: {integrity: sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==}
     engines: {node: '>=0.10.0'}
@@ -1385,6 +1413,9 @@ packages:
   lie@3.1.1:
     resolution: {integrity: sha512-RiNhHysUjhrDQntfYSfY4MU24coXXdEOgw9WGcKHNeEwffDYbF//u87M1EWaMGzuFoSbqW0C9C6lEEhDOAswfw==}
 
+  lie@3.3.0:
+    resolution: {integrity: sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==}
+
   loader-utils@1.4.2:
     resolution: {integrity: sha512-I5d00Pd/jwMD2QCduo657+YM/6L3KZu++pmX9VFncxaxvHcru9jx1lBaFft+r4Mt2jK0Yhp41XlRAihzPxHNCg==}
     engines: {node: '>=4.0.0'}
@@ -1534,6 +1565,9 @@ packages:
     resolution: {integrity: sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==}
     engines: {node: '>= 0.4'}
 
+  pako@1.0.11:
+    resolution: {integrity: sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==}
+
   param-case@3.0.4:
     resolution: {integrity: sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A==}
 
@@ -1607,6 +1641,9 @@ packages:
     resolution: {integrity: sha512-spBB5sgC4cv2YcW03f/IAUN1pgDJWNWD8FzkyY4mArLUMJW+KlQhlmUdKAHQuPfb00Jl5xIfImeOsf6YL8QK7Q==}
     engines: {node: '>=0.10.0'}
 
+  process-nextick-args@2.0.1:
+    resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==}
+
   query-string@4.3.4:
     resolution: {integrity: sha512-O2XLNDBIg1DnTOa+2XrIwSiXEV8h2KImXUnjhhn2+UsvZ+Es2uyd5CCRTNQlDGbzUQOW3aYCBx9rVA6dzsiY7Q==}
     engines: {node: '>=0.10.0'}
@@ -1617,6 +1654,9 @@ packages:
   raf@3.4.1:
     resolution: {integrity: sha512-Sq4CW4QhwOHE8ucn6J34MqtZCeWFP2aQSmrlroYgqAV1PjStIhJXxYuTgUIfkEk7zTLjmIjLmU5q+fbD1NnOJA==}
 
+  readable-stream@2.3.8:
+    resolution: {integrity: sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==}
+
   readable-stream@3.6.2:
     resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==}
     engines: {node: '>= 6'}
@@ -1686,6 +1726,9 @@ packages:
     resolution: {integrity: sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q==}
     engines: {node: '>=0.4'}
 
+  safe-buffer@5.1.2:
+    resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==}
+
   safe-buffer@5.2.1:
     resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==}
 
@@ -1846,6 +1889,9 @@ packages:
     resolution: {integrity: sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==}
     engines: {node: '>=0.10.0'}
 
+  setimmediate@1.0.5:
+    resolution: {integrity: sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==}
+
   side-channel-list@1.0.0:
     resolution: {integrity: sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==}
     engines: {node: '>= 0.4'}
@@ -1932,6 +1978,9 @@ packages:
     resolution: {integrity: sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==}
     engines: {node: '>= 0.4'}
 
+  string_decoder@1.1.1:
+    resolution: {integrity: sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==}
+
   string_decoder@1.3.0:
     resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==}
 
@@ -2446,6 +2495,8 @@ snapshots:
 
   '@sxzz/popperjs-es@2.11.7': {}
 
+  '@tarikjabiri/dxf@2.8.9': {}
+
   '@trysound/sax@0.2.0': {}
 
   '@tweenjs/tween.js@23.1.3': {}
@@ -2469,6 +2520,8 @@ snapshots:
 
   '@types/stats.js@0.17.3': {}
 
+  '@types/svg-path-parser@1.1.6': {}
+
   '@types/svgo@2.6.4':
     dependencies:
       '@types/node': 22.14.1
@@ -2814,6 +2867,8 @@ snapshots:
   core-js@3.41.0:
     optional: true
 
+  core-util-is@1.0.3: {}
+
   cors@2.8.5:
     dependencies:
       object-assign: 4.1.1
@@ -2958,6 +3013,8 @@ snapshots:
       es-errors: 1.3.0
       gopd: 1.2.0
 
+  dxf-writer@1.18.4: {}
+
   ejs@3.1.10:
     dependencies:
       jake: 10.9.2
@@ -3501,6 +3558,13 @@ snapshots:
       dompurify: 3.2.5
       html2canvas: 1.4.1
 
+  jszip@3.10.1:
+    dependencies:
+      lie: 3.3.0
+      pako: 1.0.11
+      readable-stream: 2.3.8
+      setimmediate: 1.0.5
+
   kind-of@3.2.2:
     dependencies:
       is-buffer: 1.1.6
@@ -3519,6 +3583,10 @@ snapshots:
     dependencies:
       immediate: 3.0.6
 
+  lie@3.3.0:
+    dependencies:
+      immediate: 3.0.6
+
   loader-utils@1.4.2:
     dependencies:
       big.js: 5.2.2
@@ -3689,6 +3757,8 @@ snapshots:
       object-keys: 1.1.1
       safe-push-apply: 1.0.0
 
+  pako@1.0.11: {}
+
   param-case@3.0.4:
     dependencies:
       dot-case: 3.0.4
@@ -3766,6 +3836,8 @@ snapshots:
       posthtml-parser: 0.2.1
       posthtml-render: 1.4.0
 
+  process-nextick-args@2.0.1: {}
+
   query-string@4.3.4:
     dependencies:
       object-assign: 4.1.1
@@ -3778,6 +3850,16 @@ snapshots:
       performance-now: 2.1.0
     optional: true
 
+  readable-stream@2.3.8:
+    dependencies:
+      core-util-is: 1.0.3
+      inherits: 2.0.4
+      isarray: 1.0.0
+      process-nextick-args: 2.0.1
+      safe-buffer: 5.1.2
+      string_decoder: 1.1.1
+      util-deprecate: 1.0.2
+
   readable-stream@3.6.2:
     dependencies:
       inherits: 2.0.4
@@ -3873,6 +3955,8 @@ snapshots:
       has-symbols: 1.1.0
       isarray: 2.0.5
 
+  safe-buffer@5.1.2: {}
+
   safe-buffer@5.2.1: {}
 
   safe-push-apply@1.0.0:
@@ -4019,6 +4103,8 @@ snapshots:
       is-plain-object: 2.0.4
       split-string: 3.1.0
 
+  setimmediate@1.0.5: {}
+
   side-channel-list@1.0.0:
     dependencies:
       es-errors: 1.3.0
@@ -4132,6 +4218,10 @@ snapshots:
       define-properties: 1.2.1
       es-object-atoms: 1.1.1
 
+  string_decoder@1.1.1:
+    dependencies:
+      safe-buffer: 5.1.2
+
   string_decoder@1.3.0:
     dependencies:
       safe-buffer: 5.2.1

Fichier diff supprimé car celui-ci est trop grand
+ 1 - 0
public/icons/Chair.svg


Fichier diff supprimé car celui-ci est trop grand
+ 1 - 0
public/icons/ThreeSofa.svg


+ 1 - 0
src/core/components/circle/temp-circle.vue

@@ -51,6 +51,7 @@ const emit = defineEmits<{
 const config = useConfig();
 const data = computed(() => ({ ...defaultStyle, ...props.data }));
 
+console.log(data.value);
 const matConfig = computed(() => {
   const mat = new Transform(data.value.mat);
   return mat.decompose();

+ 1 - 0
src/core/components/icon/icon.vue

@@ -79,6 +79,7 @@ const { shape, tData, data, operateMenus, describes } = useComponentStatus({
     return matResponse({ data, mat, increment: true });
   },
   propertys: [
+    "name",
     "fill",
     "stroke",
     "strokeWidth",

+ 1 - 1
src/core/components/icon/index.ts

@@ -1,4 +1,3 @@
-import { themeColor } from "@/constant/help-style.ts";
 import { Transform } from "konva/lib/Util";
 import {
   BaseItem,
@@ -57,6 +56,7 @@ export type IconData = Partial<typeof defaultStyle> &
   BaseItem & Size & {
     fill?: string;
     stroke?: string;
+    name?: string
     strokeWidth?: number;
     coverFill?: string;
     coverStroke?: string;

+ 19 - 16
src/core/components/icon/temp-icon.vue

@@ -2,14 +2,14 @@
   <v-group :config="groupConfig" v-if="groupConfig" ref="shape">
     <v-group :config="initDecMat" name="rep-position">
       <v-rect :config="rectConfig" name="repShape" />
-      <v-path v-for="config in pathConfigs" :config="config" />
+      <v-path v-for="config in pathConfigs" :config="config" name="icon-path" />
     </v-group>
   </v-group>
 </template>
 
 <script lang="ts" setup>
 import { defaultStyle, IconData } from "./index.ts";
-import { computed, ref, watch } from "vue";
+import { computed, ref, watch, watchEffect } from "vue";
 import { getSvgContent, parseSvgContent, SVGParseResult } from "@/utils/resource.ts";
 import { Group } from "konva/lib/Group";
 import { DC } from "@/deconstruction.js";
@@ -34,23 +34,11 @@ watch(
     svg.value = null;
     const svgContent = await getSvgContent(url);
     svg.value = parseSvgContent(svgContent);
-    console.log(svg.value);
   },
   { immediate: true }
 );
 
-const pathConfigs = computed(() => {
-  if (!svg.value) return [];
-  return svg.value.paths.map((path) => ({
-    ...path,
-    ...data.value,
-    id: undefined,
-    zIndex: undefined,
-    offset: { x: svg.value!.x, y: svg.value!.y },
-  }));
-});
-
-const initDecMat = computed(() => {
+const scale = computed(() => {
   if (!svg.value) return null;
   let w = data.value.width;
   let h = data.value.height;
@@ -61,9 +49,24 @@ const initDecMat = computed(() => {
     x: w / svg.value.width,
     y: h / svg.value.height,
   };
+  return scale;
+});
 
+const pathConfigs = computed(() => {
+  if (!svg.value) return [];
+  return svg.value.paths.map((path) => ({
+    ...path,
+    ...data.value,
+    id: undefined,
+    lineWidth: 1000,
+    zIndex: undefined,
+    offset: { x: svg.value!.x, y: svg.value!.y },
+  }));
+});
+const initDecMat = computed(() => {
+  if (!svg.value || !scale.value) return;
   return new Transform()
-    .scale(scale.x, scale.y)
+    .scale(scale.value.x, scale.value.y)
     .multiply(new Transform().translate(-svg.value.width / 2, -svg.value.height / 2))
     .decompose();
 });

+ 1 - 0
src/core/components/polygon/index.ts

@@ -65,6 +65,7 @@ export const interactiveToData: InteractiveTo<"polygon"> = ({
       data: {
         ...getBaseItem(),
         ...preset,
+        ...defaultStyle,
         points: [],
         attitude: [1, 0, 0, 1, 0, 0],
       },

+ 315 - 0
src/core/hook/use-dxf.ts

@@ -0,0 +1,315 @@
+import {
+  DxfWriter,
+  point2d,
+  LWPolylineFlags,
+  point3d,
+  TrueColor,
+  pattern,
+  HatchPredefinedPatterns,
+  HatchBoundaryPaths,
+  HatchPolylineBoundary,
+  vertex,
+} from "@tarikjabiri/dxf";
+import { useStore } from "../store";
+import Zip from "jszip";
+import { LineData } from "../components/line";
+import { CircleData } from "../components/circle";
+import { Transform } from "konva/lib/Util";
+import { lineVector, Pos, Size, verticalVectorLine } from "@/utils/math";
+import { RectangleData } from "../components/rectangle";
+import { useStage } from "./use-global-vars";
+import { Group } from "konva/lib/Group";
+import { Text } from "konva/lib/shapes/Text";
+import { TextData } from "../components/text";
+import { ImageData } from "../components/image";
+import { onlyId } from "@/utils/shared";
+import { ArrowData, PointerPosition } from "../components/arrow";
+import { IconData } from "../components/icon";
+import { useSetViewport, useViewer, useViewerInvertTransform } from "./use-viewer";
+import { nextTick } from "vue";
+
+export const useGetDXF = () => {
+  const store = useStore();
+  const stage = useStage();
+  const invMat = useViewerInvertTransform()
+  const { setViewport } = useSetViewport()
+  const {viewer} = useViewer()
+
+  return async () => {
+    const writer = new DxfWriter();
+    const $stage = stage.value!.getNode();
+    const zip = new Zip();
+    const genPromises: Promise<any>[] = [];
+
+    const fontStyle = writer.tables.styleTable.records[0]
+    fontStyle.fontFileName = 'SimSun'
+
+    type PL = {
+      id?: string;
+      points: Pos[];
+      fill?: string;
+      content?: string;
+      strokeWidth?: number;
+      stroke?: string;
+    };
+    const writerPolyline = (pl: PL) => {
+      if (pl.fill) {
+        const polyline = new HatchPolylineBoundary();
+        const boundary = new HatchBoundaryPaths();
+        pl.points.forEach((p) => polyline.add(vertex(p.x, -p.y)));
+        boundary.addPolylineBoundary(polyline);
+        writer.addHatch(
+          boundary,
+          pattern({ name: HatchPredefinedPatterns.SOLID }),
+          { trueColor: TrueColor.fromHex(pl.fill).toString() }
+        );
+      }
+
+      writer.addLWPolyline(
+        pl.points.map((p) => ({ point: point2d(p.x, -p.y) })),
+        {
+          flags: LWPolylineFlags.Closed,
+          constantWidth: pl.strokeWidth,
+          trueColor: TrueColor.fromHex(pl.stroke || "#FFFFFF").toString(),
+        }
+      );
+
+      if (!pl.content) return;
+      const $text = $stage.findOne<Group>(`#${pl.id}`)?.findOne<Text>(".text");
+      if ($text) {
+        writeText($text);
+      }
+    };
+
+    const writeText = ($text: Text, sp = true) => {
+      const mat = $text.getTransform();
+      const fontSize = $text.fontSize() * 0.8;
+      const text = $text.text();
+      const pad = $text.padding();
+      const align = $text.align();
+      const textArr: {content: string, charCount: number}[] = [];
+      let lineNum = 1;
+      const width = $text.width();
+      const charWidth = fontSize * 0.9;
+
+      const lineCharCount = sp ? Math.max(Math.floor(width / charWidth), 2) : Number.MAX_VALUE;
+      let ndx = 0;
+      let prevNdx = 0;
+      let charCount = 0
+
+      while (ndx < text.length) {
+        ndx++
+        const c = /[\u4e00-\u9fff]/.test(text.charAt(ndx)) ? 2.4 : 1
+        
+        if (charCount === lineCharCount || ndx >= text.length) {
+          charCount += c
+          textArr.push({
+            content: text.substring(prevNdx, ndx + 1),
+            charCount
+          });
+          charCount = 0
+          prevNdx = ndx
+        } else if (charCount > lineCharCount) {
+          textArr.push({
+            content: text.substring(prevNdx, ndx),
+            charCount
+          });
+          charCount = 0
+          ndx--
+          prevNdx = ndx
+        } else {
+          charCount += c
+        }
+      }
+
+      textArr.forEach((item) => {
+        const lineWidth = charWidth * item.charCount;
+        let p = { x: pad, y: pad + lineNum * fontSize * 1.2 };
+        if (align === "center") {
+          p.x = (width - lineWidth) / 2;
+        } else if (align === "right") {
+          p.x = width - lineWidth;
+        }
+        const start = mat.point(p);
+        const text = writer.addText(point3d(start.x, -start.y), fontSize, item.content, {
+          rotation: $text.rotation(),
+          // horizontalAlignment:
+          //   align === "center"
+          //     ? TextHorizontalAlignment.Center
+          //     : align === "right"
+          //     ? TextHorizontalAlignment.Right
+          //     : TextHorizontalAlignment.Left,
+        });
+        text.trueColor = TrueColor.fromHex($text.fill() as string).toString()
+        text.height = fontSize
+        lineNum++;
+      });
+    };
+
+    const writeImage = async (imgGroup: Group, minSize: Size) => {
+      let rect = imgGroup.getClientRect()
+      const oldViewMat = viewer.viewMat;
+      setViewport(rect)
+      await nextTick()
+      rect = imgGroup.getClientRect()
+      const img = await imgGroup!.toImage({
+        pixelRatio: 1,
+        quality: 1,
+        mimeType: 'image/png'
+      }) as HTMLImageElement
+      const start = invMat.value.point({x: rect.x, y: rect.y + rect.height})
+      const end = invMat.value.point({x: rect.x + rect.width, y: rect.y})
+      const name = onlyId().replace(/\-/g, '')
+      const path = name + '.png'
+
+      const image = writer.addImage(
+        path,
+        name,
+        point3d(start.x, -start.y),
+        img.width,
+        img.height,
+        1,
+        0
+      );
+      image.ratio = Math.abs(end.x - start.x) / img.width
+      genPromises.push(
+        fetch(img.src)
+          .then((res) => res.blob())
+          .then((blob) => zip.file(path, blob, {  }))
+      );
+      viewer.setViewMat(oldViewMat);
+    };
+
+    for (const _item of store.sortItems) {
+      if (_item.hide) continue;
+      
+      const type = store.getType(_item.id);
+      let item;
+      let mat;
+      switch (type) {
+        case "line":
+          item = _item as LineData;
+          writer.addLWPolyline(
+            item.points.map((p) => ({ point: point2d(p.x, -p.y) })),
+            {
+              flags: LWPolylineFlags.None,
+              constantWidth: item.strokeWidth,
+              trueColor: TrueColor.fromHex(item.stroke || "#FFFFFF").toString(),
+            }
+          );
+          break;
+
+        case "triangle":
+        case "rectangle":
+        case "polygon":
+          item = _item as RectangleData;
+          writerPolyline(item);
+          break;
+
+        case "circle":
+          item = _item as CircleData;
+          mat = new Transform(item.mat);
+
+          const points = [];
+          for (let angle = 0; angle <= 360; angle += 1) {
+            const rad = angle * (Math.PI / 180);
+            const x = item.radiusX * Math.cos(rad);
+            const y = item.radiusY * Math.sin(rad);
+            points.push(mat.point({ x, y }));
+          }
+          writerPolyline({ ...item, points });
+          break;
+
+        case "text":
+          item = _item as TextData;
+          writeText($stage.findOne<Text>(`#${item.id}`)!, !!item.width);
+          break;
+
+        case "arrow":
+          item = _item as ArrowData;
+          writer.addLWPolyline(
+            item.points.map((p) => ({ point: point2d(p.x, -p.y) })),
+            {
+              flags: LWPolylineFlags.None,
+              constantWidth: item.strokeWidth,
+              trueColor: TrueColor.fromHex(item.fill || "#FFFFFF").toString(),
+            }
+          );
+          const isEnd = [PointerPosition.end, PointerPosition.all].includes(
+            item.pointerPosition || PointerPosition.start
+          );
+          const isStart = [PointerPosition.start, PointerPosition.all].includes(
+            item.pointerPosition || PointerPosition.start
+          );
+
+          for (let i = 0; i < item.points.length - 1; i++) {
+            const line = [item.points[i], item.points[i + 1]];
+            const vector = lineVector(line);
+
+            if (isStart) {
+              const start = vector
+                .clone()
+                .multiplyScalar(item.pointerLength!)
+                .add(line[0]);
+              const l1 = verticalVectorLine(
+                vector,
+                start,
+                item.pointerLength! / 2
+              );
+              const l2 = verticalVectorLine(
+                vector,
+                start,
+                -item.pointerLength! / 2
+              );
+              writerPolyline({
+                points: [line[0], l1[1], l2[1]],
+                fill: item.fill,
+                stroke: item.fill,
+              });
+            }
+            if (isEnd) {
+              const start = vector
+                .clone()
+                .multiplyScalar(-item.pointerLength!)
+                .add(line[1]);
+              const l1 = verticalVectorLine(
+                vector,
+                start,
+                item.pointerLength! / 2
+              );
+              const l2 = verticalVectorLine(
+                vector,
+                start,
+                -item.pointerLength! / 2
+              );
+              writerPolyline({
+                points: [line[1], l1[1], l2[1]],
+                fill: item.fill,
+                stroke: item.fill,
+              });
+            }
+          }
+          break;
+          
+        case "image":
+          item = _item as ImageData;
+          await writeImage($stage.findOne<Group>(`#${item.id}`)!, item);
+          break;
+
+        case "icon":
+          item = _item as IconData;
+          const pathGroup = $stage
+            .findOne<Group>(`#${item.id}`)!
+            .findOne<Group>(".rep-position")!;
+          await writeImage(pathGroup, item);
+          break;
+      }
+    }
+
+    let dxfString = writer.stringify();
+    zip.file( onlyId() + ".dxf", dxfString);
+    return Promise.all(genPromises).then(() =>
+      zip.generateAsync({ type: "blob" })
+    );
+  };
+};

+ 6 - 27
src/core/hook/use-expose.ts

@@ -11,7 +11,7 @@ import { useMode, useOperMode } from "./use-status";
 import { Stage } from "konva/lib/Stage";
 import { useInteractiveProps } from "./use-interactive.ts";
 import { useStore } from "../store/index.ts";
-import { useGetViewBoxPositionPixel, useViewer } from "./use-viewer.ts";
+import { useGetViewBoxPositionPixel, useSetViewport, useViewer } from "./use-viewer.ts";
 import { useGlobalResize, useListener } from "./use-event.ts";
 import { useInteractiveDrawShapeAPI } from "./use-draw.ts";
 import { useHistory } from "./use-history.ts";
@@ -27,11 +27,10 @@ import { useResourceHandler } from "./use-fetch.ts";
 import { useConfig } from "./use-config.ts";
 import { useSelectionRevise } from "./use-selection.ts";
 import { useFormalLayer, useGetFormalChildren } from "./use-layer.ts";
-import { DataGroupId } from "@/constant/index.ts";
-import { Group } from "konva/lib/Group";
 import { components } from "../components/index.ts";
 import { useProportion } from "./use-proportion.ts";
 import { ShapeType } from "@/index.ts";
+import { useGetDXF } from "./use-dxf.ts";
 
 // 自动粘贴服务
 export const useAutoPaste = () => {
@@ -251,28 +250,7 @@ export const useExpose = installGlobalVar(() => {
     });
   };
 
-  const formalLayer = useFormalLayer();
-  const initViewport = () => {
-    const rect = formalLayer
-      .value!.findOne<Group>("#" + DataGroupId)!
-      .getClientRect();
-    const invMat = viewer.transform.invert();
-    const lt = invMat.point(rect);
-    const rb = invMat.point({
-      x: rect.x + rect.width,
-      y: rect.y + rect.height,
-    });
-    console.log();
-    viewer.setBound({
-      targetBound: {
-        ...lt,
-        width: rb.x - lt.x,
-        height: rb.y - lt.y,
-      },
-      padding: 20,
-    });
-  };
-
+  
   return {
     ...useInteractiveDrawShapeAPI(),
     get stage() {
@@ -286,7 +264,7 @@ export const useExpose = installGlobalVar(() => {
     updateSize,
     history,
     store,
-    initViewport,
+    ...useSetViewport(),
     mode,
     getData() {
       return store.data;
@@ -296,6 +274,7 @@ export const useExpose = installGlobalVar(() => {
     presetAdd: interactiveProps,
     config: useConfig(),
     mountMenus: useMountMenusAttachs(),
-    proportion: useProportion()
+    proportion: useProportion(),
+    getDXF: useGetDXF()
   };
 });

+ 8 - 8
src/core/hook/use-status.ts

@@ -26,14 +26,14 @@ export const useMode = installGlobalVar(() => {
       modes.forEach((mode) => modeStack.value.delete(mode));
     },
   };
-  if (import.meta.env.DEV) {
-    watchEffect(
-      () => {
-        console.error([...modeStack.value.values()].join(","));
-      },
-      { flush: "sync" }
-    );
-  }
+  // if (import.meta.env.DEV) {
+  //   watchEffect(
+  //     () => {
+  //       console.error([...modeStack.value.values()].join(","));
+  //     },
+  //     { flush: "sync" }
+  //   );
+  // }
   return modeStack;
 }, Symbol("mode"));
 

+ 46 - 11
src/core/hook/use-viewer.ts

@@ -8,6 +8,10 @@ import { Transform } from "konva/lib/Util";
 import { lineLen } from "@/utils/math.ts";
 import { useResize } from "./use-event.ts";
 import { FixScreen, getFixPosition } from "@/utils/bound.ts";
+import { useFormalLayer } from "./use-layer.ts";
+import { Group } from "konva/lib/Group";
+import { DataGroupId } from "@/constant/index.ts";
+import { IRect } from "konva/lib/types";
 
 export const useViewer = installGlobalVar(() => {
   const stage = useStage();
@@ -16,14 +20,13 @@ export const useViewer = installGlobalVar(() => {
   const size = useResize();
   const transform = ref(new Transform());
   const sizeMat = ref<Transform | null>(null);
-  
 
   const init = (dom: HTMLDivElement) => {
-    let downEv: PointerEvent
+    let downEv: PointerEvent;
     const onDestroy = mergeFuns(
       dragListener(dom, {
         down(_, ev) {
-          downEv = ev
+          downEv = ev;
         },
         move: ({ end, prev, ev }) => {
           if (downEv.button === 2) return;
@@ -187,16 +190,11 @@ export const useGetViewBoxPositionPixel = () => {
   const size = useResize();
   const { sizeMat, viewer, transform } = useViewer();
 
-
   return (fixPosition: FixScreen, selfSize = { width: 1, height: 1 }) => {
     if (sizeMat.value) {
       const size = viewer.viewSize!;
-      const pos = getFixPosition(
-        fixPosition,
-        selfSize,
-        size
-      );
-      return transform.value.point(pos)
+      const pos = getFixPosition(fixPosition, selfSize, size);
+      return transform.value.point(pos);
     } else {
       return getFixPosition(
         fixPosition,
@@ -204,5 +202,42 @@ export const useGetViewBoxPositionPixel = () => {
         size.value || { width: 100, height: 100 }
       );
     }
-  }
+  };
+};
+
+export const useSetViewport = () => {
+  const formalLayer = useFormalLayer();
+  const { viewer } = useViewer();
+
+  const initViewport = () => {
+    const rect = formalLayer
+      .value!.findOne<Group>("#" + DataGroupId)!
+      .getClientRect();
+    rect.width > 0 && rect.height > 0 && setViewport(rect);
+  };
+  const setViewport = (rect: IRect, padding = 20, isPixel = true) => {
+    const invMat = viewer.transform.invert();
+    const lt = isPixel ? invMat.point(rect) : rect;
+    const rb = isPixel
+      ? invMat.point({
+          x: rect.x + rect.width,
+          y: rect.y + rect.height,
+        })
+      : {
+          x: rect.x + rect.width,
+          y: rect.y + rect.height,
+        };
+    viewer.setBound({
+      targetBound: {
+        ...lt,
+        width: rb.x - lt.x,
+        height: rb.y - lt.y,
+      },
+      padding,
+    });
+  };
+  return {
+    initViewport,
+    setViewport,
+  };
 };

+ 21 - 0
src/core/html-mount/propertys/components/text.vue

@@ -0,0 +1,21 @@
+<template>
+  <el-input
+    :modelValue="value"
+    style="width: 140px"
+    @update:model-value="(value: string) => $emit('update:value', value)"
+    placeholder="Please input"
+    @change="$emit('change')"
+  />
+</template>
+
+<script lang="ts" setup>
+import { ElInput } from "element-plus";
+
+defineProps<{
+  value?: string;
+}>();
+defineEmits<{
+  (e: "update:value", val: string): void;
+  (e: "change"): void;
+}>();
+</script>

+ 5 - 0
src/core/html-mount/propertys/describes.json

@@ -4,6 +4,11 @@
     "label": "边框色",
     "layout-type": "column"
   },
+  "name": {
+    "type": "text",
+    "label": "名称",
+    "layout-type": "row"
+  },
   "coverStroke": {
     "type": "color",
     "label": "背景边框颜色",

+ 4 - 1
src/core/html-mount/propertys/index.ts

@@ -3,6 +3,7 @@ import Select from "./components/select.vue";
 import Num from "./components/num.vue";
 import Checkbox from "./components/checkbox.vue";
 import Proportion from "./components/proportion.vue";
+import Text from "./components/text.vue";
 import FixProportion from "./components/fix-proportion.vue";
 import originDescribes from "./describes.json";
 import { Ref } from "vue";
@@ -13,6 +14,7 @@ export const numType = "num";
 export const checkType = "check";
 export const proportionType = "proportion";
 export const fixProportionType = "fixProportion"
+export const textType = 'text'
 
 export const propertyComponents = {
   [colorType]: Color,
@@ -20,7 +22,8 @@ export const propertyComponents = {
   [selectType]: Select,
   [checkType]: Checkbox,
   [proportionType]: Proportion,
-  [fixProportionType]: FixProportion
+  [fixProportionType]: FixProportion,
+  [textType]: Text
 };
 
 export type PropertyType = keyof typeof propertyComponents;

+ 4 - 6
src/example/components/container/container.vue

@@ -76,8 +76,6 @@ defineExpose({
 </script>
 
 <style lang="scss" scoped>
-@use '../../styles/global';
-
 .layout {
   display: flex;
   flex-direction: column;
@@ -89,7 +87,7 @@ defineExpose({
   overflow: hidden;
 
   .container {
-    height: calc(100vh - #{global.$headerSize} - var(--top));
+    height: calc(100vh - var(--headerSize) - var(--top));
     display: flex;
     align-items: stretch;
   }
@@ -99,14 +97,14 @@ defineExpose({
     width: calc(100% - 70px - var(--left));
   }
   &.full {
-    --top: calc(-1 * #{global.$headerSize});
-    --left: calc(-1 * #{global.$slideSize});
+    --top: calc(-1 * var(--$headerSize));
+    --left: calc(-1 * var(--$slideSize));
   }
 }
 
 .confirm-items {
   position: absolute;
-  bottom: #{global.$bottomSize};
+  bottom: var(--bottomSize);
   left: 50%;
   transform: translateX(-50%);
 }

+ 5 - 2
src/example/components/header/actions.ts

@@ -123,9 +123,12 @@ export const getHeaderActions = (draw: Draw) => {
         },
         {
           handler: async () => {
-            // saveAs(await getImage(draw, 'image/jpeg'), "canvas.png");
+            draw.enterTemp(async () => {
+              const dxf = await draw.getDXF()
+              saveAs(dxf, "canvas.zip");
+            })
           },
-          text: "DXF",
+          text: "dxf",
           icon: "a-visible",
         },
       ],

+ 1 - 4
src/example/components/header/index.vue

@@ -60,9 +60,6 @@ defineProps<{
 </script>
 
 <style lang="scss" scoped>
-@use 'element-plus/theme-chalk/src/common/var';
-@use '../../styles/global';
-
 .header {
   background-color: #fff;
   display: flex;
@@ -73,7 +70,7 @@ defineProps<{
   border-bottom: 1px solid #e6e6e6;
   margin-top: var(--top);
   transition: margin-top 0.3s ease;
-  height: global.$headerSize;
+  height: var(--headerSize);
   flex: 0 0 auto;
 }
 

+ 3 - 2
src/example/components/show-vr.vue

@@ -5,7 +5,7 @@
       <Icon class="close" name="close" @click="$emit('close')" />
     </div>
     <div class="content" :style="{ pointerEvents: downPos ? 'none' : 'all' }">
-      <iframe :src="tempStrFill(SceneTypeTempUrls[scene.type], scene)" />
+      <iframe :src="tempStrFill(viewURLS[scene.type], scene)" />
       <Icon class="full" :name="isFull ? 'zoom_s' : 'zoom_b'" @click="fullHandler" />
     </div>
   </div>
@@ -16,11 +16,12 @@ import { dragListener, listener } from "@/utils/event";
 import { onUnmounted, ref, watch, watchEffect } from "vue";
 import { Pos } from "@/utils/math";
 import { mergeFuns, tempStrFill } from "@/utils/shared";
-import { Scene, SceneTypeTempUrls } from "../platform/platform-resource";
+import { Scene } from "../platform/platform-resource";
 
 defineProps<{ scene: Scene }>();
 defineEmits<{ (e: "close"): void }>();
 
+const viewURLS = window.platform.viewURLS;
 const headerRef = ref<HTMLDivElement>();
 const vrRef = ref<HTMLDivElement>();
 const width = ref(320);

+ 10 - 3
src/example/components/slide/actions.ts

@@ -8,7 +8,6 @@ import { selectFile } from "@/utils/dom";
 import { getImage } from "@/utils/resource";
 import { Draw } from "../container/use-draw";
 import { MenuItem } from "./menu";
-import { uploadResourse } from "@/example/fuse/req";
 import { copy } from "@/utils/shared";
 import { ElMessage } from "element-plus";
 
@@ -77,7 +76,7 @@ export const imp: MenuItem = {
       name: "本地",
       handler: async (draw: Draw) => {
         const files = await selectFile(false, 'image/jpeg,image/png')
-        const url = await uploadResourse(files[0])
+        const url = await window.platform.uploadResourse(files[0])
         const image = await getImage(url)
         ElMessage.warning('请在画图面板中选择放置位置,鼠标右键取消')
 
@@ -171,7 +170,7 @@ export const dbImage: MenuItem = {
         } else if (info.ratio > 1) {
           proportion = { scale: info.ratio, unit: "m" };
         }
-        const url = await uploadResourse(new File([info.blob], 'map.png'))
+        const url = await window.platform.uploadResourse(new File([info.blob], 'map.png'))
 
         draw.history.onceTrack(() => {
           draw.addShape(
@@ -253,5 +252,13 @@ export const test: MenuItem = {
         console.log(copy(draw.store.$state))
       },
     },
+    {
+      value: uuid(),
+      icon: "",
+      name: "获取dxf",
+      handler: (draw: Draw) => {
+        draw.getDXFString()
+      },
+    },
   ],
 };

+ 3 - 7
src/example/components/slide/slide.vue

@@ -66,11 +66,9 @@ const openHandler = (value: string) => {
 </script>
 
 <style lang="scss" scoped>
-@use '../../styles/global';
-
 .slide {
   flex: 0 0 auto;
-  width: global.$slideSize;
+  width: var(--slideSize);
   margin-left: var(--left);
   background: #fff;
   transition: margin-left 0.3s ease;
@@ -97,15 +95,13 @@ const openHandler = (value: string) => {
 </style>
 
 <style lang="scss">
-@use '../../styles/global';
-
 .slide-popper .el-menu--popup {
-  width: global.$slideSize;
+  width: var(--slideSize);
 }
 
 .slide-popper .el-menu--popup,
 .slide-menu-poper .el-menu--popup {
-  width: global.$slideSize;
+  width: var(--slideSize);
 }
 
 .slide-menu,

+ 114 - 0
src/example/constant.ts

@@ -0,0 +1,114 @@
+
+export const iconGroups = [
+  {
+    name: "常用名称",
+    children: [
+      {
+        name: "门",
+        children: [
+          { icon: "cad-men", name: "门" },
+          { icon: "cad-shuangkaimen", name: "双开门" },
+          { icon: "cad-yimen", name: "移门" },
+          { icon: "cad-yakou", name: "哑口" },
+        ],
+      },
+      {
+        name: "窗",
+        children: [
+          { icon: "cad-chuang", name: "窗" },
+          { icon: "cad-piaochuang", name: "飘窗" },
+          { icon: "cad-luodichuang", name: "落地窗" },
+        ],
+      },
+      {
+        name: "构件",
+        children: [
+          { icon: "cad-zhuzi", name: "柱子" },
+          { icon: "cad-yandao", name: "烟道" },
+          { icon: "cad-loudao", name: "楼道" },
+        ],
+      },
+    ],
+  },
+  {
+    name: "家具",
+    children: [
+      {
+        name: "客餐厅",
+        children: [
+          { icon: "TV", name: "电视柜" },
+          { icon: "CombinationSofa", name: "组合沙发" },
+          { icon: "ThreeSofa", name: "三人沙发" },
+          { icon: "SingleSofa", name: "单人沙发" },
+          { icon: "TeaTable", name: "茶几" },
+          { icon: "Carpet", name: "地毯" },
+          { icon: "Carpet", name: "地毯" },
+          { icon: "Plant", name: "植物" },
+          { icon: "DiningTable", name: "餐桌" },
+        ],
+      },
+      {
+        name: "卧室",
+        children: [
+          { icon: "DoubleBed", name: "双人床" },
+          { icon: "SingleBed", name: "单人床" },
+          { icon: "Wardrobe", name: "衣柜" },
+          { icon: "Dresser", name: "梳妆台" },
+          { icon: "BedsideCupboard", name: "床头柜" },
+          { icon: "Pillow", name: "抱枕" },
+        ],
+      },
+      {
+        name: "厨卫",
+        children: [
+          { icon: "GasStove", name: "燃气灶" },
+          { icon: "Cupboard", name: "橱柜" },
+          { icon: "Bathtub", name: "浴缸" },
+          { icon: "Closestool", name: "马桶" },
+          { icon: "Washstand", name: "洗漱台" },
+        ],
+      },
+      {
+        name: "其他",
+        children: [
+          { icon: "Desk", name: "书桌" },
+          { icon: "BalconyChair", name: "阳台椅" },
+          { icon: "Elevator", name: "电梯" },
+        ],
+      },
+    ],
+  },
+  {
+    name: "痕迹物证",
+    children: [
+      {
+        name: "",
+        children: [
+          { icon: "zhiwen_o", name: "手印" },
+          { icon: "zuozuji_o", name: "脚印" },
+          { icon: "youzuji_o", name: "脚印" },
+          { icon: "xieyin_o", name: "鞋印" },
+          { icon: "chelunhenji_o", name: "车轮印" },
+          { icon: "dantou_o", name: "弹头" },
+          { icon: "danke_o", name: "弹壳" },
+          { icon: "shouqiang_o", name: "手枪" },
+          { icon: "buqiang_o", name: "步枪" },
+          { icon: "xuepo_o", name: "血泊" },
+          { icon: "xueji_o", name: "血迹" },
+          { icon: "shitiz_o", name: "尸体正面" },
+          { icon: "shitib_o", name: "尸体背面" },
+          { icon: "shitifuhao_o", name: "尸体" },
+        ],
+      },
+    ],
+  },
+];
+export const aiIconMap = {
+  SingleDoor: 'cad-men',
+  DoubleDoor: 'cad-shuangkaimen',
+  SlideDoor: 'cad-yimen',
+  Pass: 'cad-yakou',
+  SingleWindow: 'cad-chuang',
+  BayWindow: 'cad-piaochuang',
+  FrenchWindow: 'cad-luodichuang',
+}

+ 1 - 1
src/example/dialog/ai/index.ts

@@ -12,7 +12,7 @@ type Props = {
   cancel: () => void;
 };
 
-export type AIExposeData = {taggings: Tagings, floors: SceneFloors, compass?: number }
+export type AIExposeData = { taggings: Tagings, floors: SceneFloors, compass?: number }
 
 export const props = reactive({
   title: "全景VR",

+ 0 - 65
src/example/dialog/vr/test.ts

@@ -1,65 +0,0 @@
-import { SCENE_TYPE } from "../../platform/platform-resource";
-
-let testToken =
-  "eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxMzYzMTI2MjkyNiIsImxvZ2luVHlwZSI6InVzZXIiLCJ1c2VyTmFtZSI6IjEzNjMxMjYyOTI2IiwiaWF0IjoxNzQ0MDc2NDg5LCJqdGkiOiI5MTczYTYzOC00ZjcyLTRlMDgtOWNmMy1lMjA5OWQ4YWU0ZjUifQ.A6wuCF7XO-RK1d-cpePt9j6z_R96_JELZUbkvMnBJ_Y";
-
-const body = {
-  pageNum: 1,
-  pageSize: 50,
-  cameraId: null,
-  searchKey: "",
-  cameraType: null,
-  isSetData: true,
-  folderId: "",
-  isOldMu: false,
-  keywordType: "sceneName",
-  folderType: "0",
-  sceneType: null,
-  sceneSource: "1",
-  endTime: "",
-  startTime: "",
-  sceneName: "1",
-};
-export const getMeshSceneList = async (key = "", token: string = testToken): Promise<any[]> => {
-  try {
-    const list = await fetch(
-      import.meta.env.VITE_MESH_API_URL + "ucenter/user/scene/getOnlySceneList",
-      {
-        method: "POST",
-        headers: { token, "content-type": "application/json;charset=UTF-8" },
-        body: JSON.stringify({ ...body, searchKey: key, sceneName: key }),
-      }
-    )
-      .then((res) => res.json())
-      .then((res) => res.data.list)
-      .then((list) =>
-        list.map((item: any) => ({
-          ...item,
-          id: item.id,
-          type: SCENE_TYPE.mesh,
-          m: item.num,
-          title: item.sceneName,
-        }))
-      );
-    return list;
-  } catch {
-    testToken = await login();
-    return await getMeshSceneList(key, testToken);
-  }
-};
-
-const login = async () => {
-  const body = {
-    password: "RtAax6waVucGVuZzEyMw==dGl1VWqhSnT78LntHR",
-    phoneNum: "13631262926",
-    randomcode: "1234",
-    rememberMe: false,
-  };
-  return fetch(import.meta.env.VITE_MESH_API_URL + "ucenter/sso/user/login", {
-    method: "POST",
-    headers: { "content-type": "application/json;charset=UTF-8" },
-    body: JSON.stringify(body),
-  })
-    .then((res) => res.json())
-    .then((res) => res.data.token);
-};

+ 1 - 28
src/example/dialog/vr/vr.vue

@@ -30,7 +30,6 @@ import { ElSelect, ElOption } from "element-plus";
 import { computed, ref } from "vue";
 import { asyncTimeout } from "@/utils/shared";
 import { Scene, SCENE_TYPE, SceneTypeNames } from "../../platform/platform-resource";
-import { getMeshSceneList } from "./test";
 
 const props = defineProps<{ value?: Scene; label: string }>();
 const emit = defineEmits<{ (e: "update:value", val: Scene): void }>();
@@ -59,33 +58,7 @@ const searchSenects = async (keyword: string) => {
 
   loading.value = true;
   await asyncTimeout(1000);
-  scenes.value = await getMeshSceneList(keyword);
-  scenes.value.unshift(
-    {
-      m: "SG-t-N6no657Kuze",
-      title: "mesh250个点位",
-      id: "-11",
-      type: SCENE_TYPE.mesh,
-    },
-    {
-      m: "KK-t-ThCAj9SjuKB",
-      title: "mesh250个点位",
-      id: "-11",
-      type: SCENE_TYPE.mesh,
-    },
-    {
-      m: "SG-t-1YWXq7Tb27z",
-      title: "mesh500个点位",
-      id: "-12",
-      type: SCENE_TYPE.mesh,
-    },
-    {
-      m: "SS-t-hCh1HyrQkr",
-      title: "mesh500个点位-1",
-      id: "-13",
-      type: SCENE_TYPE.mesh,
-    }
-  );
+  scenes.value = await window.platform.getSceneList(keyword);;
   loading.value = false;
 };
 

+ 1 - 0
src/example/env.ts

@@ -5,3 +5,4 @@ export const params = {} as { id: string };
 for (const [name, value] of urlParams.entries()) {
   (params as any)[name] = value
 }
+

+ 3 - 4
src/example/styles/global.scss

@@ -1,9 +1,5 @@
 @use 'element-plus/theme-chalk/src/common/var';
 
-$headerSize: 60px;
-$slideSize: 64px;
-$bottomSize: 50px;
-
 * {
 	box-sizing: border-box;
 	padding: 0;
@@ -14,6 +10,9 @@ body {
 	// font-family: Microsoft YaHei, Microsoft YaHei;
 	--el-font-family: PingFang SC, PingFang SC, Microsoft YaHei, Microsoft YaHei;
 	font-family: var(--el-font-family);
+	--headerSize: 60px;
+	--slideSize: 64px;
+	--bottomSize: 50px;
 }
 
 .operate {

+ 1 - 7
src/example/fuse/main.ts

@@ -1,17 +1,11 @@
 import { createApp } from 'vue'
-import '../styles/global.scss'
+import './global.scss'
 import 'element-plus/theme-chalk/src/index.scss'
 import App from './App.vue'
 import 'virtual:svg-icons-register'
 import Icon from '@/components/icon/index.vue'
 import {router} from "./router";
-import { params } from '../env'
 
-if (!params.id) {
-  alert('图纸不存在!')
-  router.back()
-  throw '图纸不存在!'
-}
 
 const app = createApp(App)
 app.component('Icon', Icon)

+ 0 - 68
src/example/fuse/req.ts

@@ -1,68 +0,0 @@
-import { StoreData } from "@/core/store/store";
-
-export const getOverviewData = async () => {
-  const storeStr = localStorage.getItem("draw-data");
-  const store = (storeStr ? JSON.parse(storeStr) : {}) as StoreData;
-
-  const vportStr = localStorage.getItem("view-port");
-  const vport = (vportStr ? JSON.parse(vportStr) : null) as number[] | null;
-
-  return {
-    store,
-    viewport: vport,
-  };
-};
-
-export const saveOverviewData = async (data: {
-  store: StoreData;
-  viewport: number[] | null;
-}) => {
-  localStorage.setItem("draw-data", JSON.stringify(data.store));
-  localStorage.setItem("view-port", JSON.stringify(data.viewport));
-};
-
-export const getTabulationData = async () => {
-  const storeStr = localStorage.getItem("tab-draw-data");
-  const store = (storeStr ? JSON.parse(storeStr) : {}) as StoreData;
-
-  const vportStr = localStorage.getItem("tab-view-port");
-  const vport = (vportStr ? JSON.parse(vportStr) : null) as number[] | null;
-
-  const paperKeyStr = localStorage.getItem("tab-paper-key");
-  const paperKey = paperKeyStr ? JSON.parse(paperKeyStr) : "a4";
-
-  return {
-    store,
-    cover: tabCover,
-    paperKey,
-    viewport: vport,
-  };
-};
-
-export const saveTabulationData = async (data: {
-  store: StoreData;
-  viewport: number[] | null;
-  paperKey?: string;
-}) => {
-  localStorage.setItem("tab-draw-data", JSON.stringify(data.store));
-  localStorage.setItem("tab-view-port", JSON.stringify(data.viewport));
-  localStorage.setItem("tab-paper-key", JSON.stringify(data.paperKey));
-};
-
-export type TabCover = {
-  url: string;
-  width: number;
-  height: number;
-  proportion: {
-    scale: number;
-    unit: string;
-  };
-};
-let tabCover: TabCover | null = null;
-export const saveTabulationCover = async (data: TabCover) => {
-  tabCover = data;
-};
-
-export const uploadResourse = async (file: File) => {
-  return URL.createObjectURL(file);
-};

+ 12 - 3
src/example/fuse/store.ts

@@ -1,5 +1,4 @@
 import { Ref, ref } from "vue";
-import { getOverviewData, getTabulationData, TabCover } from "./req";
 import { StoreData } from "@/core/store/store";
 
 export const tableCoverKey = '__tableCoverKey'
@@ -9,12 +8,22 @@ export const tableTitleKey = '__tableTitleKey'
 export const tableCoverWidth = 514
 export const tableCoverHeight = 425
 
+export type TabCover = {
+  url: string;
+  width: number;
+  height: number;
+  proportion: {
+    scale: number;
+    unit: string;
+  };
+};
+
 export const overviewData = ref() as Ref<{
   store: StoreData;
   viewport: number[] | null;
 }>
 export const refreshOverviewData = () => {
-  return getOverviewData().then(data => overviewData.value = data)
+  return window.platform.getOverviewData().then((data: any) => overviewData.value = data)
 }
 
 export const tabulationData = ref() as Ref<{
@@ -24,5 +33,5 @@ export const tabulationData = ref() as Ref<{
   viewport: number[] | null;
 }>
 export const refreshTabulationData = () => {
-  return getTabulationData().then(data => tabulationData.value = data)
+  return window.platform.getTabulationData().then((data: any) => tabulationData.value = data)
 }

+ 15 - 12
src/example/fuse/views/overview/header.vue

@@ -18,12 +18,6 @@ import { useDraw } from "../../../components/container/use-draw.ts";
 import { selectScene } from "../../../dialog/vr/index.ts";
 import { Scene } from "../../../platform/platform-resource.ts";
 import { getHeaderActions, getImage } from "../../../components/header/actions.ts";
-import {
-  saveOverviewData,
-  saveTabulationCover,
-  saveTabulationData,
-  uploadResourse,
-} from "../../req.ts";
 import { router } from "../../router.ts";
 import { params } from "@/example/env.ts";
 import {
@@ -78,7 +72,7 @@ const setViewToTableCover = async () => {
 
   const pop = draw.mode.push(Mode.readonly);
   const rect = draw.stage!.findOne<Group>(`#${DataGroupId}`)!.getClientRect();
-  const rectScale = rect.width / rect.height;
+  const rectScale = (rect.width || 1080) / (rect.height || 850);
   const tableCoverScale = tableCoverWidth / tableCoverHeight;
 
   let width: number, height: number;
@@ -91,7 +85,7 @@ const setViewToTableCover = async () => {
   }
 
   // const height = width / tableCoverScale;
-
+  console.log(width, rectScale);
   draw.config.size = { width, height };
   draw.config.showGrid = false;
   draw.config.back = undefined;
@@ -134,8 +128,13 @@ const saveHandler = async () => {
     return [blob, scale, rect] as const;
   });
 
-  await saveOverviewData({ store: storeData, viewport: draw!.viewer.transform.m });
-  const url = await uploadResourse(new File([blob], `tabulation-cover-${params.id}.png`));
+  await window.platform.saveOverviewData({
+    store: storeData,
+    viewport: draw!.viewer.transform.m,
+  });
+  const url = await window.platform.uploadResourse(
+    new File([blob], `tabulation-cover-${params.id}.png`)
+  );
   const cover = {
     url,
     width: rect.width,
@@ -143,7 +142,7 @@ const saveHandler = async () => {
     proportion: { ...draw.store.config.proportion, scale },
   };
 
-  await saveTabulationCover(cover);
+  await window.platform.saveTabulationCover(cover);
   await refreshTabulationData();
 
   const paperKey = tabulationData.value.paperKey as "a4";
@@ -159,7 +158,11 @@ const saveHandler = async () => {
     cover
   );
   tabStore.config.compass = storeData.config.compass;
-  await saveTabulationData({ ...tabulationData.value, paperKey, store: tabStore });
+  await window.platform.saveTabulationData({
+    ...tabulationData.value,
+    paperKey,
+    store: tabStore,
+  });
 };
 
 const gotoTabulation = async () => {

+ 1 - 1
src/example/fuse/views/overview/index.vue

@@ -23,13 +23,13 @@ import Header from "./header.vue";
 import Slide from "./slide.vue";
 import Container from "../../../components/container/container.vue";
 import ShowVR from "../../../components/show-vr.vue";
-import { uploadResourse } from "../../req";
 import { ref, watch } from "vue";
 import { Draw } from "../../../components/container/use-draw";
 import { Scene } from "../../../platform/platform-resource";
 import Dialog from "../../../dialog/dialog.vue";
 import { refreshOverviewData, overviewData } from "../../store";
 
+const uploadResourse = window.platform.uploadResourse;
 const full = ref(false);
 const draw = ref<Draw>();
 const vrScene = ref<Scene>();

+ 3 - 5
src/example/fuse/views/overview/query.vue

@@ -9,12 +9,12 @@
 
 <script lang="ts" setup>
 import Container from "../../../components/container/container.vue";
-import { uploadResourse } from "../../req";
 import { ref, watch } from "vue";
 import { Draw } from "../../../components/container/use-draw";
 import { refreshOverviewData, overviewData } from "../../store";
 import { Mode } from "@/constant/mode";
 
+const uploadResourse = window.platform.uploadResourse;
 const draw = ref<Draw>();
 
 const init = async (draw: Draw) => {
@@ -32,10 +32,8 @@ watch(draw, (draw) => draw && init(draw));
 </script>
 
 <style lang="scss" scoped>
-@use '../../../styles/global';
-
 .query-container {
-  --top: calc(-1 * #{global.$headerSize});
-  --left: calc(-1 * #{global.$slideSize});
+  --top: calc(-1 * var(--$headerSize));
+  --left: calc(-1 * var(--$slideSize));
 }
 </style>

+ 4 - 105
src/example/fuse/views/overview/slide-icons.vue

@@ -14,7 +14,7 @@
         <div class="icon-items">
           <div
             v-for="item in typeChildren.children"
-            @click="drawIcon(`/icons/${item.icon}.svg`)"
+            @click="drawIcon(`/icons/${item.icon}.svg`, item.name)"
           >
             <Icon :name="item.icon" size="32px" />
             <span>{{ item.name }}</span>
@@ -31,11 +31,12 @@ import { computed, ref } from "vue";
 import { ElCollapse, ElCollapseItem, ElEmpty } from "element-plus";
 import { getSvgContent, parseSvgContent } from "@/utils/resource";
 import { Draw } from "../../../components/container/use-draw.ts";
+import { iconGroups as groups } from "../../../constant";
 
 const props = defineProps<{ draw: Draw }>();
 const emit = defineEmits<{ (e: "exit"): void }>();
 
-const drawIcon = async (url: string) => {
+const drawIcon = async (url: string, name: string) => {
   const svgContent = parseSvgContent(await getSvgContent(url));
 
   const maxSize = 100;
@@ -52,114 +53,12 @@ const drawIcon = async (url: string) => {
   props.draw.enterDrawShape("icon", {
     url,
     ...size,
+    name,
     fill: "#000000",
   });
   emit("exit");
 };
 
-const groups = [
-  {
-    name: "常用名称",
-    children: [
-      {
-        name: "门",
-        children: [
-          { icon: "cad-men", name: "门" },
-          { icon: "cad-shuangkaimen", name: "双开门" },
-          { icon: "cad-yimen", name: "移门" },
-          { icon: "cad-yakou", name: "哑口" },
-        ],
-      },
-      {
-        name: "窗",
-        children: [
-          { icon: "cad-chuang", name: "窗" },
-          { icon: "cad-piaochuang", name: "飘窗" },
-          { icon: "cad-luodichuang", name: "落地窗" },
-        ],
-      },
-      {
-        name: "构件",
-        children: [
-          { icon: "cad-zhuzi", name: "柱子" },
-          { icon: "cad-yandao", name: "烟道" },
-          { icon: "cad-loudao", name: "楼道" },
-        ],
-      },
-    ],
-  },
-  {
-    name: "家具",
-    children: [
-      {
-        name: "客餐厅",
-        children: [
-          { icon: "TV", name: "电视柜" },
-          { icon: "CombinationSofa", name: "组合沙发" },
-          { icon: "SingleSofa", name: "单人沙发" },
-          { icon: "TeaTable", name: "茶几" },
-          { icon: "Carpet", name: "地毯" },
-          { icon: "Plant", name: "植物" },
-          { icon: "DiningTable", name: "餐桌" },
-        ],
-      },
-      {
-        name: "卧室",
-        children: [
-          { icon: "DoubleBed", name: "双人床" },
-          { icon: "SingleBed", name: "单人床" },
-          { icon: "Wardrobe", name: "衣柜" },
-          { icon: "Dresser", name: "梳妆台" },
-          { icon: "BedsideCupboard", name: "床头柜" },
-          { icon: "Pillow", name: "抱枕" },
-        ],
-      },
-      {
-        name: "厨卫",
-        children: [
-          { icon: "GasStove", name: "燃气灶" },
-          { icon: "Cupboard", name: "橱柜" },
-          { icon: "Bathtub", name: "浴缸" },
-          { icon: "Closestool", name: "马桶" },
-          { icon: "Washstand", name: "洗漱台" },
-        ],
-      },
-      {
-        name: "其他",
-        children: [
-          { icon: "Desk", name: "书桌" },
-          { icon: "BalconyChair", name: "阳台椅" },
-          { icon: "Elevator", name: "电梯" },
-        ],
-      },
-    ],
-  },
-  {
-    name: "痕迹物证",
-    children: [
-      {
-        name: "",
-        children: [
-          { icon: "zhiwen_o", name: "手印" },
-          { icon: "zuozuji_o", name: "脚印" },
-          { icon: "youzuji_o", name: "脚印" },
-          { icon: "xieyin_o", name: "鞋印" },
-          { icon: "chelunhenji_o", name: "车轮印" },
-          { icon: "dantou_o", name: "弹头" },
-          { icon: "danke_o", name: "弹壳" },
-          { icon: "shouqiang_o", name: "手枪" },
-          { icon: "buqiang_o", name: "步枪" },
-          { icon: "xuepo_o", name: "血泊" },
-          { icon: "xueji_o", name: "血迹" },
-          { icon: "shitiz_o", name: "尸体正面" },
-          { icon: "shitib_o", name: "尸体背面" },
-          { icon: "shitifuhao_o", name: "尸体" },
-        ],
-      },
-    ],
-  },
-];
-
 const activeGroups = ref(groups.map((item) => item.name));
 const keyword = ref("");
 const searchGroups = computed(() => {

+ 1 - 2
src/example/fuse/views/tabulation/gen.ts

@@ -4,12 +4,11 @@ import { getEmptyStoreData, StoreData } from "@/core/store/store";
 import { getFixPosition } from "@/utils/bound";
 import { Size } from "@/utils/math";
 import { getImage } from "@/utils/resource";
-import { tableCoverHeight, tableCoverKey, tableCoverScaleKey, tableCoverWidth, tableTableKey, tableTitleKey } from "../../store";
+import { TabCover, tableCoverHeight, tableCoverKey, tableCoverScaleKey, tableCoverWidth, tableTableKey, tableTitleKey } from "../../store";
 import { matResponse, TableData } from "@/core/components/table";
 import { TextData } from "@/core/components/text";
 import { ImageData } from "@/core/components/image";
 import { Transform } from "konva/lib/Util";
-import { TabCover } from "../../req";
 import { paperConfigs } from "@/example/components/slide/actions";
 
 const setCoverPosition = (

+ 1 - 2
src/example/fuse/views/tabulation/header.vue

@@ -13,7 +13,6 @@ import { ElButton, ElMessage } from "element-plus";
 import { useDraw } from "../../../components/container/use-draw.ts";
 import Header from "../../../components/header/index.vue";
 import { getHeaderActions } from "../../../components/header/actions.ts";
-import { saveTabulationData } from "../../req.ts";
 import { Transform } from "konva/lib/Util";
 import { selectExposeFormat } from "@/example/dialog/expose/index.ts";
 import { grayscaleImage } from "@/utils/dom.ts";
@@ -103,7 +102,7 @@ const actions = [
 ];
 
 const saveHandler = () => {
-  saveTabulationData({
+  window.platform.saveTabulationData({
     store: draw!.getData(),
     viewport: draw!.viewer.transform.m,
     paperKey: tabulationData.value.paperKey,

+ 1 - 1
src/example/fuse/views/tabulation/index.vue

@@ -19,7 +19,6 @@
 import Header from "./header.vue";
 import Slide from "./slide.vue";
 import Container from "../../../components/container/container.vue";
-import { uploadResourse } from "../../req";
 import { computed, ref, watch } from "vue";
 import { Draw } from "../../../components/container/use-draw";
 import Dialog from "../../../dialog/dialog.vue";
@@ -33,6 +32,7 @@ import { ImageData } from "@/core/components/image";
 import { TextData } from "@/core/components/text";
 import { getCoverPaperScale, setCoverPaperScale } from "./gen";
 
+const uploadResourse = window.platform.uploadResourse
 const full = ref(false);
 const draw = ref<Draw>();
 

+ 3 - 5
src/example/fuse/views/tabulation/query.vue

@@ -9,13 +9,13 @@
 
 <script lang="ts" setup>
 import Container from "../../../components/container/container.vue";
-import { uploadResourse } from "../../req";
 import { ref, watch } from "vue";
 import { Draw } from "../../../components/container/use-draw";
 import { tabulationData, refreshTabulationData } from "../../store";
 import { Mode } from "@/constant/mode";
 import { paper } from "@/example/components/slide/actions";
 
+const uploadResourse = window.platform.uploadResourse;
 const draw = ref<Draw>();
 const init = async (draw: Draw) => {
   await refreshTabulationData();
@@ -34,10 +34,8 @@ watch(draw, (draw) => draw && init(draw));
 </script>
 
 <style lang="scss" scoped>
-@use '../../../styles/global';
-
 .query-container {
-  --top: calc(-1 * #{global.$headerSize});
-  --left: calc(-1 * #{global.$slideSize});
+  --top: calc(-1 * var(--headerSize));
+  --left: calc(-1 * var(--slideSize));
 }
 </style>

+ 61 - 29
src/example/platform/platform-draw.ts

@@ -3,19 +3,12 @@ import { AIExposeData } from "../dialog/ai";
 import { Draw } from "../components/container/use-draw";
 import { SceneFloor } from "./platform-resource";
 import { getBaseItem } from "@/core/components/util";
-import { LineData } from "@/core/components/line";
+import { LineData, defaultStyle } from "@/core/components/line";
+import { defaultStyle as iconDefaultStyle } from "@/core/components/icon";
 import { Transform } from "konva/lib/Util";
+import { MathUtils } from "three";
 
 const scaleResource = (info: AIExposeData, scale: number) => {
-  const taggings = info.taggings.map((item) => ({
-    ...item,
-    position: {
-      x: item.position.x * scale,
-      y: item.position.y * scale,
-      z: item.position.z * scale,
-    },
-  }));
-
   const floors = info.floors.map((item) => ({
     ...item,
     geos: item.geos.map((geo) =>
@@ -34,6 +27,41 @@ const scaleResource = (info: AIExposeData, scale: number) => {
     },
   }));
 
+  const taggings = info.taggings
+    .map((item) => {
+      if (!item.pixel) {
+        return {
+          ...item,
+          position: {
+            x: item.position.x * scale,
+            y: item.position.y * scale,
+            z: item.position.z * scale,
+          },
+        };
+      } else if (item.subgroup !== undefined) {
+        const floor = floors.find(
+          (floor) => floor.subgroup === item.subgroup
+        );
+        if (!floor || !floor.box) return;
+        const w = floor.box.bound.x_max - floor.box.bound.x_min;
+        const h = floor.box.bound.y_max - floor.box.bound.y_min;
+        
+        return {
+          ...item,
+          position: {
+            x: (floor.box.bound.x_min + w * item.position.x) ,
+            y: (floor.box.bound.y_min + h * item.position.y) ,
+            z: (floor.box.bound.z_min + 0.001) ,
+          },
+          size: {
+            width: item.size!.width * w ,
+            height: item.size!.height * h ,
+          },
+        };
+      }
+    })
+    .filter((item) => !!item);
+
   return {
     ...info,
     taggings,
@@ -69,11 +97,12 @@ const getResourceLayers = (data: AIExposeData) => {
         ...floor,
         box,
         taggings: data.taggings
-          // .filter(
-          //   (item) => {
-          //     return item.position.z > box.bound.z_min &&
-          //     item.position.z <= box.bound.z_max
-          //   })
+          .filter((item) => {
+            return (
+              item.position.z > box.bound.z_min &&
+              item.position.z <= box.bound.z_max
+            );
+          })
           .map((item) => ({
             ...item,
             position: { x: item.position.x, y: item.position.y },
@@ -90,21 +119,21 @@ const drawLayerResource = (
 ) => {
   const bound = genBound();
   const images: any[] = [];
-  const createTime = Date.now()
+  const createTime = Date.now();
 
   const geos: LineData[] = [];
   layerResource.geos.forEach((item, ndx) => {
     bound.update(item);
     geos.push({
       ...getBaseItem(),
+      ...defaultStyle,
       createTime: createTime + ndx,
       lock: import.meta.env.DEV ? false : true,
       attitude: [1, 0, 0, 1, 0, 0],
       points: item,
     });
   });
-  draw.store.addItems('line', geos);
-  console.log(geos)
+  draw.store.addItems("line", geos);
 
   if (layerResource.thumb) {
     const box = layerResource.box;
@@ -133,12 +162,17 @@ const drawLayerResource = (
       bound.update(item.position);
       return {
         ...getBaseItem(),
-        createTime: createTime + ndx,
+        ...iconDefaultStyle,
+        name: item.name,
+        fill: '#000000',
+        createTime: createTime + layerResource.geos.length + ndx,
         url: item.url,
         lock: import.meta.env.DEV ? false : true,
         mat: [1, 0, 0, 1, item.position.x, item.position.y],
-        width: 30,
-        height: 30,
+        width: item.size ? item.size.width : 30,
+        height: item.size ? item.size.height : 30,
+        // width: 30,
+        // height: 30,
         cornerRadius: 0,
       };
     })
@@ -153,7 +187,6 @@ const drawLayerResource = (
     images.filter((item) => !item.url.includes(".svg"))
   );
 
-
   draw.store.addItem("group", {
     ...getBaseItem(),
     ids: [...images, ...geos].map((item) => item.id),
@@ -169,7 +202,7 @@ export const drawPlatformResource = (data: AIExposeData, draw: Draw) => {
 
   draw.history.onceTrack(() => {
     // draw.store.setConfig({ proportion: { scale: 10, unit: 'mm' } });
-    draw.store.setConfig({ proportion: { scale: 10, unit: '' } });
+    draw.store.setConfig({ proportion: { scale: 10, unit: "" } });
     for (const layer of layers) {
       // if (!draw.store.layers.includes(layer.name)) {
       //   draw.store.addLayer(layer.name);
@@ -177,15 +210,14 @@ export const drawPlatformResource = (data: AIExposeData, draw: Draw) => {
       // draw.store.setCurrentLayer(layer.name);
       layerBounds.push(drawLayerResource(layer, draw)!);
     }
-    if (typeof data.compass === 'number') {
+    if (typeof data.compass === "number") {
       draw.store.setConfig({
         compass: {
-          rotation: data.compass,
-          url: draw.store.config.compass.url
-        }
-      })
+          rotation: MathUtils.radToDeg(data.compass),
+          url: draw.store.config.compass.url,
+        },
+      });
     }
-    
   });
 
   if (layerBounds.length === 0 || !draw.viewer.size) return;

+ 125 - 68
src/example/platform/platform-resource.ts

@@ -1,11 +1,12 @@
-import { Pos } from "@/utils/math";
+import { Pos, Size } from "@/utils/math";
 import { extractConnectedSegments } from "@/utils/polygon";
 import { validNum } from "@/utils/shared";
+import { aiIconMap, iconGroups } from "../constant";
 
 export enum SCENE_TYPE {
-  fuse,
-  mesh,
-  cloud,
+  fuse = "fuse",
+  mesh = "mesh",
+  cloud = "cloud",
 }
 export type Scene = {
   type: SCENE_TYPE;
@@ -14,30 +15,49 @@ export type Scene = {
   id: string;
 };
 
-export const SceneTypeNames = {
-  [SCENE_TYPE.fuse]: "融合场景",
-  [SCENE_TYPE.mesh]: "Mesh场景",
-  [SCENE_TYPE.cloud]: "点云场景",
+export type SceneFloor = {
+  name: string;
+  subgroup?: number,
+  geos: (Pos & { z: number })[][];
+  thumb?: string;
+  box?: {
+    bound: {
+      x_min: number;
+      x_max: number;
+      y_min: number;
+      y_max: number;
+      z_min: number;
+      z_max: number;
+    };
+    rotate: number;
+    scale: number;
+  };
+  compass?: number;
 };
 
-export const SceneTypeTempUrls = {
-  [SCENE_TYPE.fuse]: import.meta.env.VITE_FUSE_TEMP_URL,
-  [SCENE_TYPE.mesh]: import.meta.env.VITE_MESH_TEMP_URL,
-  [SCENE_TYPE.cloud]: import.meta.env.VITE_CLOUD_TEMP_URL,
+export type SceneFloors = SceneFloor[];
+
+export type Taging = {
+  url: string;
+  position: Pos & { z: number };
+  size?: Size;
+  name?: string
+  pixel?: boolean;
+  subgroup?: string;
 };
 
-export const SceneTypeApiUrls = {
-  [SCENE_TYPE.fuse]: import.meta.env.VITE_FUSE_API_URL,
-  [SCENE_TYPE.mesh]: import.meta.env.VITE_MESH_OSS_URL,
-  [SCENE_TYPE.cloud]: import.meta.env.VITE_CLOUD_API_URL,
+export const SceneTypeNames = {
+  [SCENE_TYPE.fuse]: "融合场景",
+  [SCENE_TYPE.mesh]: "Mesh场景",
+  [SCENE_TYPE.cloud]: "点云场景",
 };
 
-export const getSceneApi = async (sceneType: SCENE_TYPE, url: string) => {
+export const getSceneApi = async (type: string, url: string) => {
   let uri: string;
   try {
-    uri = new URL(url, SceneTypeApiUrls[sceneType]).toString();
+    uri = new URL(url, window.platform.resourceURLS[type]).toString();
   } catch {
-    uri = SceneTypeApiUrls[sceneType] + url;
+    uri = window.platform.resourceURLS[type] + url;
   }
   const res = await fetch(uri, { method: "HEAD" });
   if (res.status !== 200) {
@@ -46,11 +66,6 @@ export const getSceneApi = async (sceneType: SCENE_TYPE, url: string) => {
   return uri;
 };
 
-export type Taging = {
-  url: string;
-  position: Pos & { z: number };
-};
-
 export type Tagings = Taging[];
 
 export const compassGets = {
@@ -58,19 +73,24 @@ export const compassGets = {
   [SCENE_TYPE.cloud]: () => void 0,
   [SCENE_TYPE.mesh]: async (scene: Scene) => {
     const prev = `/scene_view_data/${scene.m}`;
-    const config = await getSceneApi(scene.type, `${prev}/data/scene.json`)
+    const config = await getSceneApi("oss", `${prev}/data/scene.json`)
       .then((url) => fetch(url))
       .then((res) => res.json())
       .catch(() => ({ version: 0, billboards: 0, tags: 0, orientation: 0 }));
 
-    const floorpanCompass = await getSceneApi(scene.type, `${prev}/user/floorplan.json?_=${config.version}`)
-      .then(url => fetch(url))
-      .then(res => res.json())
-      .then(data => data.compass)
-      .catch(() => null)
-    return (typeof floorpanCompass === 'number' ? floorpanCompass : config.orientation) 
-  }
-}
+    const floorpanCompass = await getSceneApi(
+      "oss",
+      `${prev}/user/floorplan.json?_=${config.version}`
+    )
+      .then((url) => fetch(url))
+      .then((res) => res.json())
+      .then((data) => data.compass)
+      .catch(() => null);
+    return typeof floorpanCompass === "number"
+      ? floorpanCompass
+      : Number(config.orientation);
+  },
+};
 
 export const taggingGets = {
   [SCENE_TYPE.fuse]: async (scene: Scene, options: string[]) => {
@@ -141,14 +161,14 @@ export const taggingGets = {
   [SCENE_TYPE.mesh]: async (scene: Scene, options: string[]) => {
     const tags: Tagings = [];
     const prev = `/scene_view_data/${scene.m}`;
-    const config = await getSceneApi(scene.type, `${prev}/data/scene.json`)
+    const config = await getSceneApi("oss", `${prev}/data/scene.json`)
       .then((url) => fetch(url))
       .then((res) => res.json())
       .catch(() => ({ version: 0, billboards: 0, tags: 0, orientation: 0 }));
 
     if (options.includes("hot") && config.tags) {
       const medias = await getSceneApi(
-        scene.type,
+        "oss",
         `${prev}/user/hot.json?_=${config.version}`
       )
         .then((url) => fetch(url))
@@ -157,7 +177,7 @@ export const taggingGets = {
 
       const reqs = medias.map((media: any) => {
         if (!validNum(media.position.x) || !validNum(media.position.y)) return;
-        return getSceneApi(scene.type, `${prev}/user/${media.icon}`)
+        return getSceneApi("oss", `${prev}/user/${media.icon}`)
           .then((url) => {
             tags.push({ url, position: media.position });
           })
@@ -169,7 +189,7 @@ export const taggingGets = {
 
     if (options.includes("signage") && config.billboards) {
       const signages = await getSceneApi(
-        scene.type,
+        "oss",
         `${prev}/user/billboards.json?_=${config.version}`
       )
         .then((url) => fetch(url))
@@ -178,46 +198,75 @@ export const taggingGets = {
 
       signages.forEach((signage: any) => {
         if (!validNum(signage.pos.x) || !validNum(signage.pos.y)) return;
-        return getSceneApi(scene.type, `${prev}/user/${signage.icon}.png`)
+        return getSceneApi("oss", `${prev}/user/${signage.icon}.png`)
           .then((url) => {
             tags.push({ url, position: signage.pos });
           })
           .catch(() => {});
       });
     }
-    return tags
-  },
-};
 
-export type SceneFloor = {
-  name: string;
-  geos: (Pos & { z: number })[][];
-  thumb?: string;
-  box?: {
-    bound: {
-      x_min: number;
-      x_max: number;
-      y_min: number;
-      y_max: number;
-      z_min: number;
-      z_max: number;
-    };
-    rotate: number;
-    scale: number;
-  };
-  compass?: number
-};
+    await getSceneApi(
+      "oss",
+      `${prev}/data/floorplan/ai.json?_=${config.version}`
+    )
+      .then((url) => fetch(url))
+      .then((res) => res.json())
+      .then((datas) => {
+        for (const data of datas) {
+          const reg = data.imagePath.match(/floor_(\d)\.png/);
+          const subgroup = reg ? Number(reg[1]) : undefined;
+          for (const shape of data.shapes) {
+            const pos = {
+              x: ((shape.bbox[0] + shape.bbox[2]) / 2) / data.imageWidth,
+              y: ((shape.bbox[1] + shape.bbox[3]) / 2) / data.imageHeight,
+              z: 0,
+            };
+            const size = {
+              width: (shape.bbox[2] - shape.bbox[0]) / data.imageWidth,
+              height: (shape.bbox[3] - shape.bbox[1]) / data.imageHeight,
+            };
+            const icon = shape.category in aiIconMap ? (aiIconMap as any)[shape.category] : shape.category
+            let name = ''
+            for (const group of iconGroups) {
+              for (const itemGroup of group.children) {
+                for (const item of itemGroup.children) {
+                  if (item.icon === icon) {
+                    name = item.name
+                  }
+                }
+              }
+            }
 
-export type SceneFloors = SceneFloor[];
+            tags.push({
+              position: pos,
+              url: `/icons/${icon ? icon : 'circle'}.svg`,
+              name,
+              pixel: true,
+              size,
+              subgroup,
+            } as any);
+          }
+        }
+      })
+      .catch((e) => {
+        console.error(e);
+      });
+    return tags;
+  },
+};
 
 export const getFloors = {
   [SCENE_TYPE.mesh]: async (scene: Scene) => {
-    return getSceneApi(scene.type, `/scene_view_data/${scene.m}/data/floorplan_cad.json?_=${Date.now()}`)
+    return getSceneApi(
+      "oss",
+      `/scene_view_data/${scene.m}/data/floorplan_cad.json?_=${Date.now()}`
+    )
       .then((url) => fetch(url))
       .then((res) => res.json())
-      .catch(() => ({ floors: [] }))
-  }
-}
+      .catch(() => ({ floors: [] }));
+  },
+};
 
 export const lineGets = {
   [SCENE_TYPE.fuse]: async (scene: Scene) => {
@@ -228,7 +277,7 @@ export const lineGets = {
     const prev = `/scene_view_data/${scene.m}/data/`;
     const [{ floors }, bounds] = await Promise.all([
       getFloors[SCENE_TYPE.mesh](scene),
-      getSceneApi(scene.type, `${prev}floorplan/info.json`)
+      getSceneApi("oss", `${prev}floorplan/info.json`)
         .then((url) => fetch(url))
         .then((res) => res.json())
         .then((data) => data.floors)
@@ -239,19 +288,27 @@ export const lineGets = {
 
     const reqs = floors
       .filter((item: any) => !floorName || item.name === floorName)
-      .map((floor: any, ndx: number) => {
+      .map((floor: any) => {
+        console.log(
+          floor,
+          bounds.find((i: any) => i.subgroup === floor.subgroup)
+        );
         const bound = {
           ...(floor.cadInfo.cadBoundingBox || {}),
-          ...(bounds[ndx]?.bound || {}),
+          ...(bounds.find((i: any) => i.subgroup === floor.subgroup)?.bound ||
+            {}),
         };
         const item: any = {
           name: floor.name,
+          subgroup: floor.subgroup,
           thumb: "",
           box: {
             bound: {
               ...bound,
               y_min: -bound.y_max,
               y_max: -bound.y_min,
+              z_max: bound.z_max ? Number(bound.z_max) : bound.z_max,
+              z_min: bound.z_min ? Number(bound.z_min) : bound.z_min,
             },
             rotate: floor.cadInfo.res,
             scale: floor.cadInfo.currentScale,
@@ -265,7 +322,7 @@ export const lineGets = {
         };
         data.push(item);
         return getSceneApi(
-          scene.type,
+          "oss",
           `${prev}floorplan/floor_${floor.subgroup}.png`
         )
           .then((url) => (item.thumb = url))

+ 2 - 1
src/global.d.ts

@@ -1,6 +1,7 @@
 declare global {  
   interface Window {  
-    __VERSION__: string  
+    __VERSION__: string
+    platform: any
   }  
 }  
 

+ 144 - 0
src/mock.ts

@@ -0,0 +1,144 @@
+import type { TabCover } from "./example/fuse/store";
+import type { Scene } from "./example/platform/platform-resource";
+
+const SCENE_TYPE = {
+  fuse: "fuse",
+  mesh: "mesh",
+  cloud: "cloud",
+} as const;
+
+const getSceneList = async (): Promise<Scene[]> => [
+  {
+    m: 'SG-t-KclpWad2dW5',
+    title: "有AI",
+    id: onlyId(),
+    type: SCENE_TYPE.mesh,
+
+  },
+  {
+    m: "SG-t-jvab1SlVA1r",
+    title: "多楼层",
+    id: onlyId(),
+    type: SCENE_TYPE.mesh,
+  },
+  {
+    m: "SG-t-N6no657Kuze",
+    title: "单楼层",
+    id: onlyId(),
+    type: SCENE_TYPE.mesh,
+  },
+  {
+    m: "SS-t-hiNBZjf5EK8",
+    title: "庙堂火灾",
+    id: onlyId(),
+    type: SCENE_TYPE.mesh,
+  },
+  {
+    m: "SS-t-2tWYj9q5whZ",
+    title: "车行",
+    id: onlyId(),
+    type: SCENE_TYPE.mesh,
+  },
+  {
+    m: "SS-t-qnGLxHvngli",
+    title: "商品房",
+    id: onlyId(),
+    type: SCENE_TYPE.mesh,
+  },
+  {
+    m: "SG-t-lDhbbylq4sf",
+    title: "多楼层17.30",
+    id: onlyId(),
+    type: SCENE_TYPE.mesh,
+  },
+] as Scene[]
+
+const viewURLS = {
+  [SCENE_TYPE.mesh]: "https://test.4dkankan.com/spg.html?m={m}&lang=zh",
+  [SCENE_TYPE.cloud]:
+    "https://uat-laser.4dkankan.com/uat/index.html?m={m}&lang=zh",
+  [SCENE_TYPE.fuse]:
+    "https://test-mix3d.4dkankan.com/code/index.html?caseId={m}&app=1&share=1#/show/summary",
+};
+
+const resourceURLS = {
+  oss: "/meshOSS",
+  [SCENE_TYPE.mesh]: "/meshAPI",
+  [SCENE_TYPE.cloud]: "/cloudAPI",
+  [SCENE_TYPE.fuse]: "/fuseAPI",
+};
+
+import type { StoreData } from "@/core/store/store";
+import { onlyId } from "./utils/shared";
+
+const getOverviewData = async () => {
+  const storeStr = localStorage.getItem("draw-data");
+  const store = (storeStr ? JSON.parse(storeStr) : {}) as StoreData;
+
+  const vportStr = localStorage.getItem("view-port");
+  const vport = (vportStr ? JSON.parse(vportStr) : null) as number[] | null;
+
+  return {
+    store,
+    viewport: vport,
+  };
+};
+
+const saveOverviewData = async (data: {
+  store: StoreData;
+  viewport: number[] | null;
+}) => {
+  localStorage.setItem("draw-data", JSON.stringify(data.store));
+  localStorage.setItem("view-port", JSON.stringify(data.viewport));
+};
+
+const getTabulationData = async () => {
+  const storeStr = localStorage.getItem("tab-draw-data");
+  const store = (storeStr ? JSON.parse(storeStr) : {}) as StoreData;
+
+  const vportStr = localStorage.getItem("tab-view-port");
+  const vport = (vportStr ? JSON.parse(vportStr) : null) as number[] | null;
+
+  const paperKeyStr = localStorage.getItem("tab-paper-key");
+  const paperKey = paperKeyStr ? JSON.parse(paperKeyStr) : "a4";
+
+  return {
+    store,
+    cover: tabCover,
+    paperKey,
+    viewport: vport,
+  };
+};
+
+const saveTabulationData = async (data: {
+  store: StoreData;
+  viewport: number[] | null;
+  paperKey?: string;
+}) => {
+  localStorage.setItem("tab-draw-data", JSON.stringify(data.store));
+  localStorage.setItem("tab-view-port", JSON.stringify(data.viewport));
+  localStorage.setItem("tab-paper-key", JSON.stringify(data.paperKey));
+};
+
+let tabCover: TabCover | null = null;
+const saveTabulationCover = async (data: TabCover) => {
+  tabCover = data;
+};
+
+const uploadResourse = async (file: File) => {
+  return URL.createObjectURL(file);
+};
+
+window.platform = {
+  resourceURLS,
+  viewURLS,
+  getOverviewData,
+  getSceneList,
+  saveOverviewData,
+  getTabulationData,
+  saveTabulationData,
+  saveTabulationCover,
+  uploadResourse,
+};
+/* @vite-ignore */
+import(import.meta.env.VITE_ENTRY_EXAMPLE);

+ 1 - 9
src/vite-env.d.ts

@@ -2,14 +2,6 @@
 
 interface ImportMetaEnv {
   readonly VITE_PRIMARY: string
-  readonly VITE_MESH_TEMP_URL: string
-  readonly VITE_CLOUD_TEMP_URL: string
-  readonly VITE_FUSE_TEMP_URL: string
-
-  readonly VITE_MESH_OSS_URL: string
-  readonly VITE_MESH_API_URL: string
-  readonly VITE_CLOUD_API_URL: string
-  readonly VITE_FUSE_API_URL: string
 }
 
 interface ImportMeta {
@@ -20,4 +12,4 @@ interface ImportMeta {
  module 'virtual:svg-icons-register' {
   const content: any;
   export default content;
-}
+}

+ 5 - 7
vite.config.ts

@@ -11,7 +11,6 @@ export default ({ mode }: any) => {
 
   let proxy
   if (mode === 'development') {
-    const apiEnv = loadEnv(env.VITE_TARGET, process.cwd())
     const getProxy = (prev: string, api: string) => ({
       target: api,
       changeOrigin: true,
@@ -19,12 +18,11 @@ export default ({ mode }: any) => {
     })
 
     proxy = {
-      [env.VITE_MESH_OSS_URL]: getProxy(env.VITE_MESH_OSS_URL, apiEnv.VITE_MESH_OSS_URL),
-      [env.VITE_MESH_API_URL]: getProxy(env.VITE_MESH_API_URL, apiEnv.VITE_MESH_API_URL),
-      [env.VITE_CLOUD_API_URL]: getProxy(env.VITE_CLOUD_API_URL, apiEnv.VITE_CLOUD_API_URL),
-      [env.VITE_FUSE_API_URL]: getProxy(env.VITE_FUSE_API_URL, apiEnv.VITE_FUSE_API_URL),
+      ['/meshOSS']: getProxy('/meshOSS', 'https://4dkk.4dage.com/'),
+      ['/meshAPI']: getProxy('/meshAPI', 'https://test.4dkankan.com/'),
+      ['/cloudAPI']: getProxy('/cloudAPI', 'https://uat-laser.4dkankan.com/'),
+      ['/fuseAPI']: getProxy('/fuseAPI', 'https://test-mix3d.4dkankan.com/'),
     }
-    console.log(proxy)
   }
 
   return defineConfig({
@@ -62,7 +60,7 @@ export default ({ mode }: any) => {
       }),
       createHtmlPlugin({  
         template: 'index.html',
-        entry: env.VITE_ENTRY,
+        entry: path.resolve('src', env.VITE_ENTRY),
         inject: {
           data: {
             title: env.VITE_TITLE,