Explorar el Código

fix: 制作需求

bill hace 1 año
padre
commit
4c2d89ade5
Se han modificado 36 ficheros con 1326 adiciones y 73 borrados
  1. 3 1
      package.json
  2. 238 0
      pnpm-lock.yaml
  3. BIN
      public/android-download/Android@2x.png
  4. 114 0
      public/android-download/app-download.html
  5. BIN
      public/android-download/app-link/app-css/fonts/d_icomoon.eot
  6. 29 0
      public/android-download/app-link/app-css/fonts/d_icomoon.svg
  7. BIN
      public/android-download/app-link/app-css/fonts/d_icomoon.ttf
  8. BIN
      public/android-download/app-link/app-css/fonts/d_icomoon.woff
  9. 210 0
      public/android-download/app-link/app-css/main.css
  10. 147 0
      public/android-download/app-link/app-css/reset.css
  11. BIN
      public/android-download/app-link/app-images/android.png
  12. BIN
      public/android-download/app-link/app-images/app-logo.png
  13. BIN
      public/android-download/app-link/app-images/apple.png
  14. BIN
      public/android-download/app-link/app-images/download_pattern_left.png
  15. BIN
      public/android-download/app-link/app-images/download_pattern_right.png
  16. BIN
      public/android-download/app-link/app-images/qrcode.png
  17. BIN
      public/image/Android@2x.png
  18. BIN
      public/image/Frame 208@2x.png
  19. BIN
      public/image/e-code.png
  20. BIN
      public/image/logo.png
  21. BIN
      public/image/logo@2x.png
  22. BIN
      public/image/pic_camera@2x.png
  23. 4 0
      src/c.d.ts
  24. 303 0
      src/components/drag-verify.vue
  25. 1 1
      src/helper/loading.ts
  26. 1 3
      src/helper/message.ts
  27. 3 0
      src/request/type.ts
  28. 1 1
      src/view/down-vision.vue
  29. 2 2
      src/view/layout/slide/index.vue
  30. 0 2
      src/view/layout/slide/submenu.vue
  31. 200 33
      src/view/login.vue
  32. 8 3
      src/view/map/map-right.vue
  33. 21 21
      src/view/pano/env.ts
  34. 2 2
      src/view/pano/pano.vue
  35. 38 3
      src/view/scene-select.vue
  36. 1 1
      vite.config.ts

+ 3 - 1
package.json

@@ -18,16 +18,18 @@
     "mitt": "^3.0.1",
     "ol": "^9.1.0",
     "proj4": "^2.11.0",
+    "qrcode": "^1.5.3",
     "vue": "^3.4.21",
     "vue-router": "^4.3.0",
     "xlsx": "^0.18.5"
   },
   "devDependencies": {
     "@types/proj4": "^2.5.5",
+    "@types/qrcode": "^1.5.5",
     "@vitejs/plugin-vue": "^5.0.4",
     "sass": "^1.72.0",
     "typescript": "^5.2.2",
     "vite": "^5.2.0",
     "vue-tsc": "^2.0.6"
   }
-}
+}

+ 238 - 0
pnpm-lock.yaml

@@ -4,6 +4,7 @@ specifiers:
   '@element-plus/icons-vue': ^2.3.1
   '@types/node': ^20.12.2
   '@types/proj4': ^2.5.5
+  '@types/qrcode': ^1.5.5
   '@vitejs/plugin-vue': ^5.0.4
   element-plus: ^2.6.3
   gl-matrix: ^3.4.3
@@ -12,10 +13,12 @@ specifiers:
   mitt: ^3.0.1
   ol: ^9.1.0
   proj4: ^2.11.0
+  qrcode: ^1.5.3
   sass: ^1.72.0
   typescript: ^5.2.2
   vite: ^5.2.0
   vue: ^3.4.21
+  vue-drag-verify2: ^1.2.0
   vue-router: ^4.3.0
   vue-tsc: ^2.0.6
   xlsx: ^0.18.5
@@ -30,12 +33,15 @@ dependencies:
   mitt: 3.0.1
   ol: 9.1.0
   proj4: 2.11.0
+  qrcode: 1.5.3
   vue: 3.4.21_typescript@5.4.3
+  vue-drag-verify2: 1.2.0
   vue-router: 4.3.0_vue@3.4.21
   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
@@ -465,6 +471,12 @@ packages:
     resolution: {integrity: sha512-y4tHUVVoMEOm2nxRLQ2/ET8upj/pBmoutGxFw2LZJTQWPgWXI+cbxVEUFFmIzr/bpFR83hGDOTSXX6HBeObvZA==}
     dev: true
 
+  /@types/qrcode/1.5.5:
+    resolution: {integrity: sha512-CdfBi/e3Qk+3Z/fXYShipBT13OJ2fDO2Q2w5CIP5anLTLIndQG9z6P1cnm+8zCWSpm5dnxMFd/uREtb0EXuQzg==}
+    dependencies:
+      '@types/node': 20.12.2
+    dev: true
+
   /@types/web-bluetooth/0.0.16:
     resolution: {integrity: sha512-oh8q2Zc32S6gd/j50GowEjKLoOVOwHP/bWVjKJInBwQqdOYMdPrf1oVlelTlyfFK3CKxL1uahMDAr+vy8T7yMQ==}
     dev: false
@@ -514,6 +526,16 @@ packages:
       '@vue/compiler-core': 3.4.21
       '@vue/shared': 3.4.21
 
+  /@vue/compiler-sfc/2.7.16:
+    resolution: {integrity: sha512-KWhJ9k5nXuNtygPU7+t1rX6baZeqOYLEforUPjgNDBnLicfHCoi48H87Q8XyLZOrNNsmhuwKqtpDQWjEFe6Ekg==}
+    dependencies:
+      '@babel/parser': 7.24.1
+      postcss: 8.4.38
+      source-map: 0.6.1
+    optionalDependencies:
+      prettier: 2.8.8
+    dev: false
+
   /@vue/compiler-sfc/3.4.21:
     resolution: {integrity: sha512-me7epoTxYlY+2CUM7hy9PCDdpMPfIwrOvAXud2Upk10g4YLv9UBW7kL798TvMeDhPthkZ0CONNrK2GoeI1ODiQ==}
     dependencies:
@@ -615,6 +637,18 @@ packages:
     engines: {node: '>=0.8'}
     dev: false
 
+  /ansi-regex/5.0.1:
+    resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==}
+    engines: {node: '>=8'}
+    dev: false
+
+  /ansi-styles/4.3.0:
+    resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==}
+    engines: {node: '>=8'}
+    dependencies:
+      color-convert: 2.0.1
+    dev: false
+
   /anymatch/3.1.3:
     resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==}
     engines: {node: '>= 8'}
@@ -649,6 +683,11 @@ packages:
       fill-range: 7.0.1
     dev: true
 
+  /camelcase/5.3.1:
+    resolution: {integrity: sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==}
+    engines: {node: '>=6'}
+    dev: false
+
   /cfb/1.2.2:
     resolution: {integrity: sha512-KfdUZsSOw19/ObEWasvBP/Ac4reZvAGauZhs6S/gqNhXhI7cKwvlH7ulj+dOEYnca4bm4SGo8C1bTAQvnTjgQA==}
     engines: {node: '>=0.8'}
@@ -672,11 +711,30 @@ packages:
       fsevents: 2.3.3
     dev: true
 
+  /cliui/6.0.0:
+    resolution: {integrity: sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==}
+    dependencies:
+      string-width: 4.2.3
+      strip-ansi: 6.0.1
+      wrap-ansi: 6.2.0
+    dev: false
+
   /codepage/1.15.0:
     resolution: {integrity: sha512-3g6NUTPd/YtuuGrhMnOMRjFc+LJw/bnMp3+0r/Wcz3IXUuCosKRJvMphm5+Q+bvTVGcJJuRvVLuYba+WojaFaA==}
     engines: {node: '>=0.8'}
     dev: false
 
+  /color-convert/2.0.1:
+    resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==}
+    engines: {node: '>=7.0.0'}
+    dependencies:
+      color-name: 1.1.4
+    dev: false
+
+  /color-name/1.1.4:
+    resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==}
+    dev: false
+
   /color-name/2.0.0:
     resolution: {integrity: sha512-SbtvAMWvASO5TE2QP07jHBMXKafgdZz8Vrsrn96fiL+O92/FN/PLARzUW5sKt013fjAprK2d2iCn2hk2Xb5oow==}
     engines: {node: '>=12.20'}
@@ -724,6 +782,15 @@ packages:
     resolution: {integrity: sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg==}
     dev: true
 
+  /decamelize/1.2.0:
+    resolution: {integrity: sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==}
+    engines: {node: '>=0.10.0'}
+    dev: false
+
+  /dijkstrajs/1.0.3:
+    resolution: {integrity: sha512-qiSlmBq9+BCdCA/L46dw8Uy93mloxsPSbwnm5yrKn2vMPiy8KyAskTF6zuV/j5BMsmOGZDPs7KjU+mjb670kfA==}
+    dev: false
+
   /earcut/2.2.4:
     resolution: {integrity: sha512-/pjZsA1b4RPHbeWZQn66SWS8nZZWLQQ23oE3Eam7aroEFGEvwKAsJfZ9ytiEMycfzXWpca4FA9QIOehf7PocBQ==}
     dev: false
@@ -753,6 +820,14 @@ packages:
       - '@vue/composition-api'
     dev: false
 
+  /emoji-regex/8.0.0:
+    resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==}
+    dev: false
+
+  /encode-utf8/1.0.3:
+    resolution: {integrity: sha512-ucAnuBEhUK4boH2HjVYG5Q2mQyPorvv0u/ocS+zhdw0S8AlHYY+GOFhP1Gio5z4icpP2ivFSvhtFjQi8+T9ppw==}
+    dev: false
+
   /entities/4.5.0:
     resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==}
     engines: {node: '>=0.12'}
@@ -802,6 +877,14 @@ packages:
       to-regex-range: 5.0.1
     dev: true
 
+  /find-up/4.1.0:
+    resolution: {integrity: sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==}
+    engines: {node: '>=8'}
+    dependencies:
+      locate-path: 5.0.0
+      path-exists: 4.0.0
+    dev: false
+
   /frac/1.1.2:
     resolution: {integrity: sha512-w/XBfkibaTl3YDqASwfDUqkna4Z2p9cFSr1aHDt0WoMTECnRfBOv2WArlZILlqgWlmdIlALXGpM2AOhEk5W3IA==}
     engines: {node: '>=0.8'}
@@ -829,6 +912,11 @@ packages:
       zstddec: 0.1.0
     dev: false
 
+  /get-caller-file/2.0.5:
+    resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==}
+    engines: {node: 6.* || 8.* || >= 10.*}
+    dev: false
+
   /gl-matrix/3.4.3:
     resolution: {integrity: sha512-wcCp8vu8FT22BnvKVPjXa/ICBWRq/zjFfdofZy1WSpQZpphblv12/bOQLBC1rMM7SGOFS9ltVmKOHil5+Ml7gA==}
     dev: false
@@ -873,6 +961,11 @@ packages:
     engines: {node: '>=0.10.0'}
     dev: true
 
+  /is-fullwidth-code-point/3.0.0:
+    resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==}
+    engines: {node: '>=8'}
+    dev: false
+
   /is-glob/4.0.3:
     resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==}
     engines: {node: '>=0.10.0'}
@@ -912,6 +1005,13 @@ packages:
       immediate: 3.0.6
     dev: false
 
+  /locate-path/5.0.0:
+    resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==}
+    engines: {node: '>=8'}
+    dependencies:
+      p-locate: 4.1.0
+    dev: false
+
   /lodash-es/4.17.21:
     resolution: {integrity: sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==}
     dev: false
@@ -993,6 +1093,25 @@ packages:
       rbush: 3.0.1
     dev: false
 
+  /p-limit/2.3.0:
+    resolution: {integrity: sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==}
+    engines: {node: '>=6'}
+    dependencies:
+      p-try: 2.2.0
+    dev: false
+
+  /p-locate/4.1.0:
+    resolution: {integrity: sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==}
+    engines: {node: '>=8'}
+    dependencies:
+      p-limit: 2.3.0
+    dev: false
+
+  /p-try/2.2.0:
+    resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==}
+    engines: {node: '>=6'}
+    dev: false
+
   /pako/1.0.11:
     resolution: {integrity: sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==}
     dev: false
@@ -1009,6 +1128,11 @@ packages:
     resolution: {integrity: sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==}
     dev: true
 
+  /path-exists/4.0.0:
+    resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==}
+    engines: {node: '>=8'}
+    dev: false
+
   /pbf/3.2.1:
     resolution: {integrity: sha512-ClrV7pNOn7rtmoQVF4TS1vyU0WhYRnP92fzbfF75jAIwpnzdJXf8iTd4CMEqO4yUenH6NDqLiwjqlh6QgZzgLQ==}
     hasBin: true
@@ -1025,6 +1149,11 @@ packages:
     engines: {node: '>=8.6'}
     dev: true
 
+  /pngjs/5.0.0:
+    resolution: {integrity: sha512-40QW5YalBNfQo5yRYmiw7Yz6TKKVr3h6970B2YE+3fQpsWcrbj1PzJgxeJ19DRQjhMbKPIuMY8rFaXc8moolVw==}
+    engines: {node: '>=10.13.0'}
+    dev: false
+
   /postcss/8.4.38:
     resolution: {integrity: sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==}
     engines: {node: ^10 || ^12 || >=14}
@@ -1033,6 +1162,14 @@ packages:
       picocolors: 1.0.0
       source-map-js: 1.2.0
 
+  /prettier/2.8.8:
+    resolution: {integrity: sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==}
+    engines: {node: '>=10.13.0'}
+    hasBin: true
+    requiresBuild: true
+    dev: false
+    optional: true
+
   /process-nextick-args/2.0.1:
     resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==}
     dev: false
@@ -1048,6 +1185,17 @@ packages:
     resolution: {integrity: sha512-TdDRD+/QNdrCGCE7v8340QyuXd4kIWIgapsE2+n/SaGiSSbomYl4TjHlvIoCWRpE7wFt02EpB35VVA2ImcBVqw==}
     dev: false
 
+  /qrcode/1.5.3:
+    resolution: {integrity: sha512-puyri6ApkEHYiVl4CFzo1tDkAZ+ATcnbJrJ6RiBM1Fhctdn/ix9MTE3hRph33omisEbC/2fcfemsseiKgBPKZg==}
+    engines: {node: '>=10.13.0'}
+    hasBin: true
+    dependencies:
+      dijkstrajs: 1.0.3
+      encode-utf8: 1.0.3
+      pngjs: 5.0.0
+      yargs: 15.4.1
+    dev: false
+
   /quick-lru/6.1.2:
     resolution: {integrity: sha512-AAFUA5O1d83pIHEhJwWCq/RQcRukCkn/NSm2QsTEMle5f2hP0ChI2+3Xb051PZCkLryI/Ir1MVKviT2FIloaTQ==}
     engines: {node: '>=12'}
@@ -1082,6 +1230,15 @@ packages:
       picomatch: 2.3.1
     dev: true
 
+  /require-directory/2.1.1:
+    resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==}
+    engines: {node: '>=0.10.0'}
+    dev: false
+
+  /require-main-filename/2.0.0:
+    resolution: {integrity: sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==}
+    dev: false
+
   /resolve-protobuf-schema/2.1.0:
     resolution: {integrity: sha512-kI5ffTiZWmJaS/huM8wZfEMer1eRd7oJQhDuxeCLe3t7N7mX3z94CN0xPxBQxFYQTSNz9T0i+v6inKqSdK8xrQ==}
     dependencies:
@@ -1135,6 +1292,10 @@ packages:
       lru-cache: 6.0.0
     dev: true
 
+  /set-blocking/2.0.0:
+    resolution: {integrity: sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==}
+    dev: false
+
   /setimmediate/1.0.5:
     resolution: {integrity: sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==}
     dev: false
@@ -1143,6 +1304,11 @@ packages:
     resolution: {integrity: sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==}
     engines: {node: '>=0.10.0'}
 
+  /source-map/0.6.1:
+    resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==}
+    engines: {node: '>=0.10.0'}
+    dev: false
+
   /ssf/0.11.2:
     resolution: {integrity: sha512-+idbmIXoYET47hH+d7dfm2epdOMUDjqcB4648sTZ+t2JwoyBFL/insLfB/racrDmsKB3diwsDA696pZMieAC5g==}
     engines: {node: '>=0.8'}
@@ -1150,12 +1316,28 @@ packages:
       frac: 1.1.2
     dev: false
 
+  /string-width/4.2.3:
+    resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==}
+    engines: {node: '>=8'}
+    dependencies:
+      emoji-regex: 8.0.0
+      is-fullwidth-code-point: 3.0.0
+      strip-ansi: 6.0.1
+    dev: false
+
   /string_decoder/1.1.1:
     resolution: {integrity: sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==}
     dependencies:
       safe-buffer: 5.1.2
     dev: false
 
+  /strip-ansi/6.0.1:
+    resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==}
+    engines: {node: '>=8'}
+    dependencies:
+      ansi-regex: 5.0.1
+    dev: false
+
   /to-fast-properties/2.0.0:
     resolution: {integrity: sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==}
     engines: {node: '>=4'}
@@ -1231,6 +1413,12 @@ packages:
       vue: 3.4.21_typescript@5.4.3
     dev: false
 
+  /vue-drag-verify2/1.2.0:
+    resolution: {integrity: sha512-Ie6cxw2K1fpDZDYwFaSxfNhJ9NDKWJyXQppDCPakN/HK92DHukdIFfZqaCS9hTgtSF3ZCuaVYYZ8UOYQHiOS7Q==}
+    dependencies:
+      vue: 2.7.16
+    dev: false
+
   /vue-router/4.3.0_vue@3.4.21:
     resolution: {integrity: sha512-dqUcs8tUeG+ssgWhcPbjHvazML16Oga5w34uCUmsk7i0BcnskoLGwjpa15fqMr2Fa5JgVBrdL2MEgqz6XZ/6IQ==}
     peerDependencies:
@@ -1259,6 +1447,14 @@ packages:
       typescript: 5.4.3
     dev: true
 
+  /vue/2.7.16:
+    resolution: {integrity: sha512-4gCtFXaAA3zYZdTp5s4Hl2sozuySsgz4jy1EnpBHNfpMa9dK1ZCG7viqBPCwXtmgc8nHqUsAu3G4gtmXkkY3Sw==}
+    deprecated: Vue 2 has reached EOL and is no longer actively maintained. See https://v2.vuejs.org/eol/ for more details.
+    dependencies:
+      '@vue/compiler-sfc': 2.7.16
+      csstype: 3.1.3
+    dev: false
+
   /vue/3.4.21_typescript@5.4.3:
     resolution: {integrity: sha512-5hjyV/jLEIKD/jYl4cavMcnzKwjMKohureP8ejn3hhEjwhWIhWeuzL2kJAjzl/WyVsgPY56Sy4Z40C3lVshxXA==}
     peerDependencies:
@@ -1278,6 +1474,10 @@ packages:
     resolution: {integrity: sha512-BSR9wyRsy/KOValMgd5kMyr3JzpdeoR9KVId8u5GVlTTAtNChlsE4yTxeY7zMdNSyOmoKBv8NH2qeRY9Tg+IaA==}
     dev: false
 
+  /which-module/2.0.1:
+    resolution: {integrity: sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==}
+    dev: false
+
   /wkt-parser/1.3.3:
     resolution: {integrity: sha512-ZnV3yH8/k58ZPACOXeiHaMuXIiaTk1t0hSUVisbO0t4RjA5wPpUytcxeyiN2h+LZRrmuHIh/1UlrR9e7DHDvTw==}
     dev: false
@@ -1292,6 +1492,15 @@ packages:
     engines: {node: '>=0.8'}
     dev: false
 
+  /wrap-ansi/6.2.0:
+    resolution: {integrity: sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==}
+    engines: {node: '>=8'}
+    dependencies:
+      ansi-styles: 4.3.0
+      string-width: 4.2.3
+      strip-ansi: 6.0.1
+    dev: false
+
   /xlsx/0.18.5:
     resolution: {integrity: sha512-dmg3LCjBPHZnQp5/F/+nnTa+miPJxUXB6vtk42YjBBKayDNagxGEeIdWApkYPOf3Z3pm3k62Knjzp7lMeTEtFQ==}
     engines: {node: '>=0.8'}
@@ -1310,10 +1519,39 @@ packages:
     resolution: {integrity: sha512-1TY5yLw8DApowZAUsWCniNr8HH6Ebt6O7UQvmIwziGKwUNsQx6e+4NkfOvCfnqmYIcPjCeoI6dh1JenPJ9a1hQ==}
     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'}
+    dependencies:
+      camelcase: 5.3.1
+      decamelize: 1.2.0
+    dev: false
+
+  /yargs/15.4.1:
+    resolution: {integrity: sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==}
+    engines: {node: '>=8'}
+    dependencies:
+      cliui: 6.0.0
+      decamelize: 1.2.0
+      find-up: 4.1.0
+      get-caller-file: 2.0.5
+      require-directory: 2.1.1
+      require-main-filename: 2.0.0
+      set-blocking: 2.0.0
+      string-width: 4.2.3
+      which-module: 2.0.1
+      y18n: 4.0.3
+      yargs-parser: 18.1.3
+    dev: false
+
   /zstddec/0.1.0:
     resolution: {integrity: sha512-w2NTI8+3l3eeltKAdK8QpiLo/flRAr2p8AGeakfMZOXBxOg9HIu4LVDxBi81sYgVhFhdJjv1OrB5ssI8uFPoLg==}
     dev: false

BIN
public/android-download/Android@2x.png


+ 114 - 0
public/android-download/app-download.html

@@ -0,0 +1,114 @@
+<!DOCTYPE html>
+<html>
+
+<head>
+  <meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1,minimum-scale=1,user-scalable=no">
+  <meta charset="UTF-8">
+  <meta http-equiv="Cache-Control" content="no-cache" />
+  <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
+  <meta http-equiv="expires" content="0">
+  <meta content="telephone=no" name="format-detection">
+  <meta name="description" content="世界上首款消费级3D相机—四维看看(4DKanKan)。技术核心三要素:易操作;自动化;高精度。主要应用领域为数字文博、数字地产、数字电商、数字餐饮、数字家居等。">
+  <link rel="shortcut icon" href="//4dkk.4dage.com/FDKKIMG/icon/kankan_icon.ico">
+  <link rel="icon" type="image/png" href="//4dkk.4dage.com/FDKKIMG/icon/kankan_icon192.png" sizes="192x192">
+  <link rel="apple-touch-icon" sizes="180x180" href="//4dkk.4dage.com/FDKKIMG/icon/kankan_icon180.png">
+  <link rel="stylesheet" href="./app-link/app-css/reset.css">
+  <link rel="stylesheet" href="./app-link/app-css/main.css">
+
+  <title>四维看看</title>
+</head>
+
+<body>
+  <div class="container">
+    <div class="bg">
+      <img src="./app-link/app-images/download_pattern_left.png" alt="">
+      <img class="bg-r" src="./app-link/app-images/download_pattern_right.png" alt="">
+    </div>
+    <div class="con">
+      <div class="c-c">
+        <div class="icon-container wrapper">
+          <i class="icon-icon_path bg-path"></i>
+          <span class="icon">
+            <img src="./app-link/app-images/app-logo.png" itemprop="image">
+          </span>
+          <span class="qrcode" title="">
+            <img alt="Scan me!" src="./app-link/app-images/qrcode.png" style="display: block;">
+          </span>
+        </div>
+        <div class="m-icon">
+          <span class="icon">
+            <img src="./app-link/app-images/app-logo.png" itemprop="image">
+          </span>
+        </div>
+        <h1 class="name">
+          <span class="icon-warp">
+            <i class="icon-ios icon-cls"></i>
+            四维看看Pro
+          </span>
+        </h1>
+        <div class="scan-tips">
+          <p>扫描二维码下载</p>
+          <p>或用手机浏览器输入这个网址: <span class="datePublished">https://www.4dkankan.com/app-download.html</span></p>
+        </div>
+        <div class="release-info">
+          <p><span itemprop="softwareVersion">1.6.3</span></p>
+          <p>更新于: <span>2023-06-16 09:00</span></p>
+          <p class='only-suport'></p>
+        </div>
+        <div id="btn" class="btn">下载</div>
+      </div>
+    </div>
+  </div>
+  <script>
+    function versions() {
+      var u = window.navigator.userAgent
+      return {
+        // IE内核
+        trident: u.indexOf('Trident') > -1,
+        // opera内核
+        presto: u.indexOf('Presto') > -1,
+        // 苹果、谷歌内核
+        webKit: u.indexOf('AppleWebKit') > -1,
+        // 火狐内核
+        gecko: u.indexOf('Gecko') > -1 && u.indexOf('KHTML') === -1,
+        // 是否为移动终端
+        mobile: /Android|webOS|iPhone|iPod|BlackBerry/i.test(navigator.userAgent),
+        // ios终端
+        ios: !!u.match(/\(i[^;]+;( U;)? CPU.+Mac OS X/),
+        // android终端或者uc浏览器
+        android: u.indexOf('Android') > -1 || u.indexOf('Linux') > -1,
+        // 是否为iPhone或者安卓QQ浏览器
+        iPhone: u.indexOf('iPhone') > -1 || u.indexOf('Mac') > -1,
+        // 是否为iPad
+        iPad: u.indexOf('iPad') > -1,
+        // 是否为web应用程序,没有头部与底部
+        webApp: u.indexOf('Safari') === -1,
+        // 是否为微信浏览器
+        weixin: ~u.indexOf('MicroMessenger')
+      }
+    }
+    var ua = versions()
+    var domicon = document.querySelector('.icon-warp i')
+
+    document.getElementById('btn').addEventListener('click', function (e) {
+      if (ua.weixin) {
+        alert('微信/QQ内无法下载应用,请点击右上角,选择"浏览器中打开"')
+      }
+      else {
+        location.href = 'https://4dkankan.oss-cn-shenzhen.aliyuncs.com/apps/4dkkpro1.6.3.apk'
+      }
+    })
+
+    if (ua.ios) {
+      domicon.classList.add('icon-ios')
+      domicon.classList.remove('icon-android')
+    }
+    else {
+      domicon.classList.add('icon-android')
+      domicon.classList.remove('icon-ios')
+      document.querySelector('.only-suport').innerHTML = '仅适用于支持<br/>双频WiFi (IEEE802.11a/ac)  的安卓手机'
+    }
+  </script>
+</body>
+
+</html>

BIN
public/android-download/app-link/app-css/fonts/d_icomoon.eot


La diferencia del archivo ha sido suprimido porque es demasiado grande
+ 29 - 0
public/android-download/app-link/app-css/fonts/d_icomoon.svg


BIN
public/android-download/app-link/app-css/fonts/d_icomoon.ttf


BIN
public/android-download/app-link/app-css/fonts/d_icomoon.woff


+ 210 - 0
public/android-download/app-link/app-css/main.css

@@ -0,0 +1,210 @@
+html,body{
+  width: 100%;
+  height: 100%;
+}
+.container{
+  position: relative;
+}
+
+.container,.bg{
+  width: 100%;
+  height: 100%;
+  overflow: hidden;
+  position: absolute;
+}
+
+.bg img{
+ height: 100%;
+}
+
+
+
+.bg .bg-r{
+  float: right;
+}
+
+.con{
+  width: 700px;
+  min-height: 700px;
+  height: 100%;
+  position: relative;
+  margin: 0 auto;
+  position: relative;
+  text-align: center;
+}
+
+.c-c{
+  position: absolute;
+  top: 50%;
+  left: 50%;
+  transform: translate(-50%,-50%);
+  width: 100%;
+}
+
+.btn{
+  display: none;
+  padding: 12px 46px;
+  min-width: 200px;
+  border: 1px solid #32B2A7;
+  border-radius: 40px;
+  font-size: 14px;
+  background: #32B2A7;
+  color: #fff;
+  text-align: center;
+  margin-top: 30px;;
+}
+
+.wrapper{
+  width: 300px;
+  height: 300px;
+  margin: 0 auto;
+  position: relative;
+}
+
+.bg-path{
+  position: absolute;
+  top: 4px;
+  left: 4px;
+  z-index: -1;
+  color: #EFF2F2;
+  font-size: 310px;
+}
+
+.icon{
+  position: absolute;
+  top: 0;
+  left: 0;
+  padding: 10px;
+  width: 140px;
+  height: 140px;
+  border-radius: 17.54%;
+  background-color: #fff;
+  display: inline-block;
+}
+.icon img{
+  max-width: 100%;
+  width: 120px;
+  height: 120px;
+  border-radius: 17.54%;
+}
+.m-icon{
+  display: none;
+}
+
+.qrcode{
+  position: absolute;
+  bottom: -10px;
+  right: -10px;
+  width: 140px;
+  height: 140px;
+  border: 20px solid transparent;
+  border-radius: 20px;
+  transition: all .25s;
+  background-color: #EFF2F2;
+}
+
+.qrcode img{
+  width: 100%;
+  height: 100%;
+
+}
+
+.qrcode:hover{
+  transform: scale(1.6);
+  box-shadow: 0 1px 5px rgba(0,0,0,.3);
+}
+
+.name{
+  position: relative;
+  margin: 40px auto 10px;
+  width: 290px;
+  color: #505556;
+  text-align: left;
+  font-weight: 400;
+  font-size: 28px;
+
+}
+
+.scan-tips{
+  margin: 0 auto;
+  width: 290px;
+  text-align: left;
+  white-space: nowrap;
+  line-height: 22px;
+  color: #A9B1B3;
+  font-size: 14px;
+  margin-bottom: 40px;
+
+}
+
+.icon-cls{
+  position: absolute;
+  right: 100%;
+  top: 2px;
+  margin-right: 10px;
+  font-size: 26px;
+}
+
+.release-info{
+  border-top: 1px solid #DAE2E3;
+  max-width: 500px;
+  position: relative;
+  margin: 0 auto;
+  padding-top: 30px;
+}
+
+.release-info p{
+  color: #A9B1B3;
+  font-size: 14px;
+}
+
+.datePublished{
+  color: #202020;
+}
+
+@media screen and (max-width: 768px) {
+  .bg img{
+    height: 40%;
+  }
+  .con{
+    width: 100%;
+  }
+  .c-c{
+    width: 90%;
+  }
+  .wrapper{
+    display: none;
+  }
+  .m-icon{
+    width: 100%;
+    display: block;
+    text-align: center;
+  }
+  .icon{
+    position: static;
+  }
+  .name{
+    width: 100%;
+    text-align: center;
+    margin-bottom: 30px;
+  }
+  .icon-cls{
+    position: static;
+    margin: 0;
+  }
+  .scan-tips{
+    display: none;
+  }
+  .release-info{
+    width: 100%;
+  }
+  .btn{
+    display: inline-block;
+  }
+}
+
+@media screen and (max-width: 330px) {
+  .c-c{
+    top: 35%;
+  }
+}

+ 147 - 0
public/android-download/app-link/app-css/reset.css

@@ -0,0 +1,147 @@
+/* reset */
+html,body,h1,h2,h3,h4,h5,h6,div,dl,dt,dd,ul,ol,li,p,blockquote,pre,hr,figure,table,caption,th,td,form,fieldset,legend,input,button,textarea,menu{margin:0;padding:0;}
+header,footer,section,article,aside,nav,hgroup,address,figure,figcaption,menu,details{display:block;}
+table{border-collapse:collapse;border-spacing:0;}
+caption,th{text-align:left;font-weight:normal;}
+html,body,fieldset,img,iframe,abbr{border:0;}
+i,cite,em,var,address,dfn{font-style:normal;}
+[hidefocus],summary{outline:0;}
+li{list-style:none;}
+h1,h2,h3,h4,h5,h6,small{font-size:100%;}
+sup,sub{font-size:83%;}
+pre,code,kbd,samp{font-family:inherit;}
+q:before,q:after{content:none;}
+textarea{overflow:auto;resize:none;}
+label,summary{cursor:default;}
+a,button{cursor:pointer;}
+h1,h2,h3,h4,h5,h6,em,strong,b{font-weight:bold;}
+del,ins,u,s,a,a:hover{text-decoration:none;}
+body,textarea,input,button,select,keygen,legend{font:14px/1.14;outline:0;}
+body{background:#fff;}
+*{box-sizing: border-box}
+a{text-decoration: none;}
+
+html,body{
+  width: 100%;
+  height: 100%;
+}
+
+::-webkit-scrollbar {
+  width: 8px;
+  height: 8px;
+}
+
+::-webkit-scrollbar-thumb {
+  height: 50px;
+  background-color: #ddd;
+  -webkit-border-radius: 4px;
+  outline: 2px solid #fff;
+  outline-offset: -2px;
+}
+
+::-webkit-scrollbar-thumb:hover {
+  height: 50px;
+  background-color: #9f9f9f;
+  -webkit-border-radius: 4px;
+}
+
+
+@font-face {
+  font-family: d_icomoon;
+  src: url(./fonts/d_icomoon.eot?33id3j);
+  src: url(./fonts/d_icomoon.eot?33id3j#iefix) format("embedded-opentype"),url(./fonts/d_icomoon.ttf?33id3j) format("truetype"),url(./fonts/d_icomoon.woff?33id3j) format("woff"),url(./fonts/d_icomoon.svg?33id3j#d_icomoon) format("svg");
+  font-weight: 400;
+  font-style: normal
+}
+
+[class*=" icon-"],[class^=icon-] {
+  font-family: d_icomoon!important;
+  speak: none;
+  font-style: normal;
+  font-weight: 400;
+  font-variant: normal;
+  text-transform: none;
+  line-height: 1;
+  -webkit-font-smoothing: antialiased;
+  -moz-osx-font-smoothing: grayscale
+}
+
+.desc-section pre,.releases-section .release-view .version-info .changelog .wrapper,body,html {
+  font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif
+}
+
+.icon-loading:before {
+  content: "\e64d"
+}
+
+.icon-android_store:before {
+  content: "\e600"
+}
+
+.icon-android:before {
+  content: "\e601"
+}
+
+.icon-icon_path:before {
+  content: "\e602"
+}
+
+.icon-ios_store:before {
+  content: "\e603"
+}
+
+.icon-ios:before {
+  content: "\e604"
+}
+
+.icon-qrcode:before {
+  content: "\e605"
+}
+
+.icon-brace-left:before {
+  content: "\e613"
+}
+
+.icon-brace-right:before {
+  content: "\e617"
+}
+
+.icon-comma-eye:before {
+  content: "\e618"
+}
+
+.icon-mouth:before {
+  content: "\e619"
+}
+
+.icon-upload:before {
+  content: "\e631"
+}
+
+.icon-close:before {
+  content: "\e63b"
+}
+
+.icon-Downloading:before {
+  content: "\e900"
+}
+
+.icon-refresh:before {
+  content: "\e901"
+}
+
+.icon-download:before {
+  content: "\e902"
+}
+
+.icon-text:before {
+  content: "\e922"
+}
+
+.icon-plus:before {
+  content: "\e92b"
+}
+
+.icon-pdf:before {
+  content: "\eadf"
+}

BIN
public/android-download/app-link/app-images/android.png


BIN
public/android-download/app-link/app-images/app-logo.png


BIN
public/android-download/app-link/app-images/apple.png


BIN
public/android-download/app-link/app-images/download_pattern_left.png


BIN
public/android-download/app-link/app-images/download_pattern_right.png


BIN
public/android-download/app-link/app-images/qrcode.png


BIN
public/image/Android@2x.png


BIN
public/image/Frame 208@2x.png


BIN
public/image/e-code.png


BIN
public/image/logo.png


BIN
public/image/logo@2x.png


BIN
public/image/pic_camera@2x.png


+ 4 - 0
src/c.d.ts

@@ -0,0 +1,4 @@
+declare module "*.vue" {
+  import { ComponentOptions } from "vue";
+  export default ComponentOptions;
+}

+ 303 - 0
src/components/drag-verify.vue

@@ -0,0 +1,303 @@
+<template>
+  <div
+    ref="dragVerify"
+    class="drag_verify"
+    :style="dragVerifyStyle"
+    @mousemove="dragMoving"
+    @mouseup="dragFinish"
+    @mouseleave="dragFinish"
+    @touchmove="dragMoving"
+    @touchend="dragFinish"
+  >
+    <div
+      class="dv_progress_bar"
+      :class="{ goFirst2: isOk }"
+      ref="progressBar"
+      :style="progressBarStyle"
+    ></div>
+    <div class="dv_text" :style="textStyle" ref="message">
+      <slot name="textBefore" v-if="$slots.textBefore"></slot>
+      {{ message }}
+      <slot name="textAfter" v-if="$slots.textAfter"></slot>
+    </div>
+
+    <div
+      class="dv_handler dv_handler_bg"
+      :class="{ goFirst: isOk }"
+      @mousedown="dragStart"
+      @touchstart="dragStart"
+      ref="handler"
+      :style="handlerStyle"
+    >
+      <slot name="handlerIcon"></slot>
+    </div>
+  </div>
+</template>
+<script>
+export default {
+  name: "dragVerify",
+  props: {
+    isPassing: {
+      type: Boolean,
+      default: false,
+    },
+    width: {
+      type: Number,
+      default: 250,
+    },
+    height: {
+      type: Number,
+      default: 40,
+    },
+    text: {
+      type: String,
+      default: "swiping to the right side",
+    },
+    successText: {
+      type: String,
+      default: "success",
+    },
+    background: {
+      type: String,
+      default: "#eee",
+    },
+    progressBarBg: {
+      type: String,
+      default: "#76c61d",
+    },
+    completedBg: {
+      type: String,
+      default: "#76c61d",
+    },
+    circle: {
+      type: Boolean,
+      default: false,
+    },
+    radius: {
+      type: String,
+      default: "4px",
+    },
+    handlerIcon: {
+      type: String,
+    },
+    successIcon: {
+      type: String,
+    },
+    handlerBg: {
+      type: String,
+      default: "#fff",
+    },
+    textSize: {
+      type: String,
+      default: "14px",
+    },
+    textColor: {
+      type: String,
+      default: "#333",
+    },
+  },
+  mounted: function () {
+    const dragEl = this.$refs.dragVerify;
+    dragEl.style.setProperty("--textColor", this.textColor);
+    dragEl.style.setProperty("--width", Math.floor(this.width / 2) + "px");
+    dragEl.style.setProperty("--pwidth", -Math.floor(this.width / 2) + "px");
+    console.log(this.$slots);
+  },
+  computed: {
+    handlerStyle: function () {
+      return {
+        width: this.height + "px",
+        height: this.height + "px",
+        background: this.handlerBg,
+      };
+    },
+    message: function () {
+      return this.isPassing ? this.successText : this.text;
+    },
+    dragVerifyStyle: function () {
+      return {
+        width: this.width + "px",
+        height: this.height + "px",
+        lineHeight: this.height + "px",
+        background: this.background,
+        borderRadius: this.circle ? this.height / 2 + "px" : this.radius,
+      };
+    },
+    progressBarStyle: function () {
+      return {
+        background: this.progressBarBg,
+        height: this.height + "px",
+        borderRadius: this.circle
+          ? this.height / 2 + "px 0 0 " + this.height / 2 + "px"
+          : this.radius,
+      };
+    },
+    textStyle: function () {
+      return {
+        height: this.height + "px",
+        width: this.width + "px",
+        fontSize: this.textSize,
+      };
+    },
+  },
+  data() {
+    return {
+      isMoving: false,
+      x: 0,
+      isOk: false,
+    };
+  },
+  methods: {
+    dragStart: function (e) {
+      if (!this.isPassing) {
+        this.isMoving = true;
+        this.x = e.pageX || e.touches[0].pageX;
+      }
+      this.$emit("handlerMove");
+    },
+    dragMoving: function (e) {
+      if (this.isMoving && !this.isPassing) {
+        var _x = (e.pageX || e.touches[0].pageX) - this.x;
+        var handler = this.$refs.handler;
+        if (_x > 0 && _x <= this.width - this.height) {
+          handler.style.left = _x + "px";
+          this.$refs.progressBar.style.width = _x + this.height / 2 + "px";
+        } else if (_x > this.width - this.height) {
+          handler.style.left = this.width - this.height + "px";
+          this.$refs.progressBar.style.width = this.width - this.height / 2 + "px";
+          this.passVerify();
+        }
+      }
+    },
+    dragFinish: function (e) {
+      if (this.isMoving && !this.isPassing) {
+        var _x = (e.pageX || e.changedTouches[0].pageX) - this.x;
+        if (_x < this.width - this.height) {
+          this.isOk = true;
+          var that = this;
+          setTimeout(function () {
+            that.$refs.handler.style.left = "0";
+            that.$refs.progressBar.style.width = "0";
+            that.isOk = false;
+          }, 500);
+          this.$emit("passfail");
+        } else {
+          var handler = this.$refs.handler;
+          handler.style.left = this.width - this.height + "px";
+          this.$refs.progressBar.style.width = this.width - this.height / 2 + "px";
+          this.passVerify();
+        }
+        this.isMoving = false;
+      }
+    },
+    passVerify: function () {
+      this.$emit("update:isPassing", true);
+      this.isMoving = false;
+      var handler = this.$refs.handler;
+      handler.children[0].className = this.successIcon;
+      this.$refs.progressBar.style.background = this.completedBg;
+      this.$refs.message.style["-webkit-text-fill-color"] = "unset";
+      this.$refs.message.style.animation = "slidetounlock2 3s infinite";
+      this.$refs.message.style.color = "#fff";
+      this.$emit("passcallback");
+    },
+    reset: function () {
+      const oriData = this.$options.data();
+      for (const key in oriData) {
+        if (Object.prototype.hasOwnProperty.call(oriData, key)) {
+          this[key] = oriData[key];
+        }
+      }
+      var handler = this.$refs.handler;
+      var message = this.$refs.message;
+      handler.style.left = "0";
+      this.$refs.progressBar.style.width = "0";
+      handler.children[0].className = this.handlerIcon;
+      message.style["-webkit-text-fill-color"] = "transparent";
+      message.style.animation = "slidetounlock 3s infinite";
+      message.style.color = this.background;
+    },
+  },
+};
+</script>
+<style scoped>
+.drag_verify {
+  position: relative;
+  background-color: #e8e8e8;
+  text-align: center;
+  overflow: hidden;
+}
+.drag_verify .dv_handler {
+  position: absolute;
+  top: 0px;
+  left: 0px;
+  cursor: move;
+}
+.drag_verify .dv_handler i {
+  color: #666;
+  padding-left: 0;
+  font-size: 16px;
+}
+.drag_verify .dv_handler .el-icon-circle-check {
+  color: #6c6;
+  margin-top: 9px;
+}
+.drag_verify .dv_progress_bar {
+  position: absolute;
+  height: 34px;
+  width: 0px;
+}
+.drag_verify .dv_text {
+  position: absolute;
+  top: 0px;
+  color: transparent;
+  -moz-user-select: none;
+  -webkit-user-select: none;
+  user-select: none;
+  -o-user-select: none;
+  -ms-user-select: none;
+  background: -webkit-gradient(
+    linear,
+    left top,
+    right top,
+    color-stop(0, var(--textColor)),
+    color-stop(0.4, var(--textColor)),
+    color-stop(0.5, #fff),
+    color-stop(0.6, var(--textColor)),
+    color-stop(1, var(--textColor))
+  );
+  -webkit-background-clip: text;
+  -webkit-text-fill-color: transparent;
+  -webkit-text-size-adjust: none;
+  animation: slidetounlock 3s infinite;
+}
+.drag_verify .dv_text * {
+  -webkit-text-fill-color: var(--textColor);
+}
+.goFirst {
+  left: 0px !important;
+  transition: left 0.5s;
+}
+.goFirst2 {
+  width: 0px !important;
+  transition: width 0.5s;
+}
+</style>
+<style>
+@-webkit-keyframes slidetounlock {
+  0% {
+    background-position: var(--pwidth) 0;
+  }
+  100% {
+    background-position: var(--width) 0;
+  }
+}
+@-webkit-keyframes slidetounlock2 {
+  0% {
+    background-position: var(--pwidth) 0;
+  }
+  100% {
+    background-position: var(--pwidth) 0;
+  }
+}
+</style>

+ 1 - 1
src/helper/loading.ts

@@ -2,7 +2,7 @@ import { ElLoading } from "element-plus";
 
 let loading: ReturnType<typeof ElLoading.service> | null;
 
-export const openLoading = (url?: string) => {
+export const openLoading = () => {
   if (loading) return;
 
   loading = ElLoading.service({

+ 1 - 3
src/helper/message.ts

@@ -1,6 +1,4 @@
-import { InfoFilled, SuccessFilled } from "@element-plus/icons-vue";
-import { ElMessage, ElMessageBox } from "element-plus";
-import { markRaw } from "vue";
+import { ElMessageBox } from "element-plus";
 
 export const confirm = (msg: string, okText = "确定") =>
   ElMessageBox.confirm(msg, "系统提示", {

+ 3 - 0
src/request/type.ts

@@ -4,6 +4,7 @@ import {
   relicsTypeDesc,
   creationMethodDesc,
 } from "@/store/relics";
+import { SceneStatus } from "@/store/scene";
 
 export type UserInfo = {
   head: string;
@@ -45,6 +46,8 @@ export type Scene = {
   id: number;
   sceneId: number;
   sceneCode: string;
+  creationMethod?: keyof typeof creationMethodDesc;
+  calcStatus?: SceneStatus;
   sceneName: string;
   title: string;
   cameraType: number;

+ 1 - 1
src/view/down-vision.vue

@@ -16,7 +16,7 @@ import { openLoading, closeLoading } from "@/helper/loading";
 import { ElMessage } from "element-plus";
 import { downloadPointsXLSL } from "@/util/pc4xlsl";
 
-const sceneCode = ref("KJ-t-3OdMlfX1z2g");
+const sceneCode = ref("");
 const onSubmit = async () => {
   if (!sceneCode.value) {
     ElMessage.error("请输入场景码");

+ 2 - 2
src/view/layout/slide/index.vue

@@ -7,7 +7,7 @@
       <sub-menu
         v-for="route in routes"
         :meta="route.meta"
-        :name="route.name"
+        :name="(route.name as string)"
         :key="route.name"
       />
     </el-menu>
@@ -19,7 +19,7 @@ import subMenu from "./submenu.vue";
 import { router, findRoute } from "@/router";
 
 const names = ["relics", "scene", "device"];
-const routes = names.map((name) => findRoute(name));
+const routes = names.map((name) => findRoute(name)!);
 </script>
 
 <style lang="scss" scoped>

+ 0 - 2
src/view/layout/slide/submenu.vue

@@ -6,7 +6,5 @@
 </template>
 
 <script lang="ts" setup>
-import { RouteMeta } from "@/router";
-
 defineProps<{ meta: any; name: string }>();
 </script>

+ 200 - 33
src/view/login.vue

@@ -1,11 +1,34 @@
 <template>
-  <div class="system-layer" :style="{ backgroundImage: `url(/criminalBanner.png)` }">
+  <div
+    class="system-layer"
+    :style="{ backgroundImage: `url('/image/Frame 208@2x.png')` }"
+  >
     <div class="l-content">
       <div class="login-layer">
         <div class="content">
           <div class="info">
-            <h1>第四次全国文物普查</h1>
-            <p>数据采集管理平台</p>
+            <div class="top">
+              <img src="/image/logo@2x.png" />
+            </div>
+            <div class="center">
+              <h1>第四次全国文物普查</h1>
+              <p>
+                <img src="/image/logo.png" />
+              </p>
+            </div>
+            <div class="bottom">
+              <div class="abr-info">
+                <p>Android版</p>
+                <div class="e-code">
+                  <span v-html="qrUrl"></span>
+                  <div class="e-logo">
+                    <img src="/image/e-code.png" />
+                  </div>
+                </div>
+                <p class="desc">扫描安装文保1号App</p>
+              </div>
+              <img class="code" src="/image/pic_camera@2x.png" />
+            </div>
           </div>
           <el-form class="panel login" :model="form" @submit.stop>
             <h2>欢迎登录</h2>
@@ -36,8 +59,36 @@
               </el-input>
             </el-form-item>
 
+            <el-form-item class="panel-form-item" style="user-select: none">
+              <dragVerify
+                :class="{ passing: isPassing2 }"
+                :isPassing="isPassing2"
+                @passcallback="isPassing2 = true"
+                handlerIcon="el-icon-d-arrow-right"
+                background="#D9D9D9"
+                textColor="#333333"
+                successIcon="el-icon-circle-check"
+                :text="isPassing2 ? '已通过验证' : '登录需要拖拽验证'"
+                successText="验证通过"
+                :width="400"
+              >
+                <template v-slot:handlerIcon>
+                  <el-icon
+                    :size="20"
+                    style="width: 20px; display: inline-block"
+                    :style="{ 'margin-top': !isPassing2 ? '8px' : '2px' }"
+                  >
+                    <DArrowRight v-if="!isPassing2" />
+                    <SuccessFilled v-else />
+                  </el-icon>
+                </template>
+              </dragVerify>
+            </el-form-item>
+
             <el-form-item class="panel-form-item">
-              <el-button type="primary" class="fill" @click="submitClick">登录</el-button>
+              <el-button type="primary" class="fill submit" @click="submitClick"
+                >登录</el-button
+              >
             </el-form-item>
           </el-form>
         </div>
@@ -48,10 +99,12 @@
 
 <script lang="ts" setup>
 import { reactive, watch, ref } from "vue";
-import { View, Hide } from "@element-plus/icons-vue";
+import { View, Hide, DArrowRight, SuccessFilled } from "@element-plus/icons-vue";
 import { login } from "@/store/user";
 import { ElMessage } from "element-plus";
 import { router } from "@/router";
+import qrCode from "qrcode";
+import dragVerify from "@/components/drag-verify.vue";
 
 const PHONE = {
   REG: /^1(3|4|5|6|7|8|9)\d{9}$/,
@@ -60,6 +113,7 @@ const PHONE = {
 };
 // 是否显示明文密码
 const flag = ref(true);
+const isPassing2 = ref(false);
 // 表单
 const form = reactive({
   phone: import.meta.env.DEV ? "15915816041" : "",
@@ -86,8 +140,20 @@ watch(
   { immediate: true }
 );
 
+const qrUrl = ref<string>();
+qrCode.toString(
+  `${location.origin}/android-download/app-download.html`,
+  { type: "svg", width: 128, margin: 0, errorCorrectionLevel: "L" },
+  (_, url) => {
+    qrUrl.value = url;
+  }
+);
+
 // 表单提交
 const submitClick = async () => {
+  if (!isPassing2.value) {
+    return ElMessage.error("登录需要拖拽验证");
+  }
   if (verification.phone && verification.phone !== "88888888888") {
     return ElMessage.error(verification.phone);
   }
@@ -104,34 +170,119 @@ const submitClick = async () => {
 
 <style lang="scss" scoped>
 .login-layer {
-  text-align: right;
   width: 100%;
   height: 100%;
 }
 .content {
   display: flex;
   justify-content: center;
-  align-items: flex-start;
+  align-items: center;
+  justify-content: space-between;
+  box-sizing: border-box;
+  max-width: 1620px;
+  height: 100vh;
+  padding: 0 50px 0 50px;
 }
 .info {
   color: #000;
-  margin-right: 143px;
-  padding-top: 80px;
-  padding-bottom: 80px;
   flex: none;
   text-align: left;
-  img {
-    width: 76px;
-    height: 76px;
+  display: flex;
+  flex-direction: column;
+  justify-content: space-between;
+  max-width: 800px;
+  height: 100vh;
+  flex: 1;
+  margin-right: 10%;
+
+  .top {
+    margin-top: 50px;
+    img {
+      width: 300px;
+    }
   }
-  h1 {
-    font-size: 2.8rem;
-    line-height: 3.7rem;
-    margin-bottom: 0.7rem;
+  .bottom {
+    height: 470px;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+
+    .code {
+      height: 100%;
+      flex: none;
+    }
+
+    .abr-info {
+      flex: 1;
+      display: flex;
+      flex-direction: column;
+      align-items: center;
+
+      p:first-child {
+        font-size: 20px;
+        line-height: 32px;
+        color: #93795d;
+        padding-left: 32px;
+        position: relative;
+
+        &::after {
+          content: "";
+          position: absolute;
+          left: 0;
+          top: 0;
+          width: 32px;
+          height: 32px;
+          background: url("/image/Android@2x.png") no-repeat center center;
+          background-size: 100% 100%;
+        }
+      }
+      .e-code {
+        width: 128px;
+        margin-top: 13px;
+        position: relative;
+        > img {
+          width: 100%;
+        }
+        .e-logo {
+          position: absolute;
+          top: 50%;
+          left: 50%;
+          width: 40px;
+          height: 40px;
+          transform: translate(-50%, -50%);
+          padding: 7px;
+          border-radius: 4px;
+          text-align: center;
+          img {
+            height: 100%;
+            width: 100%;
+            background-size: cover;
+          }
+        }
+      }
+      p:last-child {
+        font-weight: 400;
+        font-size: 14px;
+        color: rgba(0, 0, 0, 0.5);
+      }
+    }
   }
-  p {
-    font-size: 2rem;
-    line-height: 2.2rem;
+
+  .center {
+    text-align: center;
+    h1 {
+      color: #781c0b;
+      font-size: 48px;
+      line-height: 3.7rem;
+      margin-bottom: 0.7rem;
+    }
+    p {
+      width: 100%;
+      margin-top: 40px;
+      img {
+        width: 320px;
+      }
+    }
   }
 }
 
@@ -150,15 +301,16 @@ const submitClick = async () => {
 }
 .login {
   width: 400px;
-  padding: 40px 40px 30px;
+  // padding: 40px 40px 30px;
   position: relative;
   display: inline-block;
+  flex: none;
 
   h2 {
     padding-left: 0;
     padding-bottom: 0;
     border-bottom: none;
-    margin-bottom: 2.14rem;
+    margin-bottom: 24px;
 
     span {
       color: #646566;
@@ -204,7 +356,7 @@ const submitClick = async () => {
   align-items: center;
   justify-content: center;
   background: no-repeat left bottom;
-  background-size: cover;
+  background-size: auto 100%;
 }
 .l-content {
   display: flex;
@@ -242,26 +394,26 @@ input[type="password"]::-ms-reveal {
 }
 
 .panel {
-  background: rgba(255, 255, 255, 0.7);
+  /* background: rgba(255, 255, 255, 0.7);
   box-shadow: 0px 2px 20px 0px rgba(5, 38, 38, 0.15);
-  border-radius: 10px;
-  width: 600px;
-  padding: 30px 0 40px;
+  border-radius: 10px; */
+  width: 400px;
+  /* padding: 30px 0 40px; */
   text-align: initial;
 
   h2 {
-    color: #323233;
-    font-size: 1.85rem;
+    font-weight: bold;
+    font-size: 30px;
+    font-weight: bold;
+    color: rgba(0, 0, 0, 0.85);
     margin-bottom: 2.14rem;
-    font-weight: normal;
     padding-left: 60px;
     padding-bottom: 20px;
-    border-bottom: 1px solid #e9e9e9;
   }
 
   .panel-form-item {
     position: relative;
-    padding-bottom: 2.14rem;
+    padding-bottom: 24px;
     margin: 0;
     padding-left: 90px;
     padding-right: 90px;
@@ -302,7 +454,7 @@ input[type="password"]::-ms-reveal {
 
 .panel-form-item .el-button,
 .panel-form-item .el-input__inner {
-  height: 50px;
+  height: 40px;
   font-size: 1.14rem;
 }
 
@@ -315,4 +467,19 @@ input[type="password"]::-ms-reveal {
 .panel-form-item .el-form-item__label {
   line-height: 50px;
 }
+.e-code img {
+  width: 100%;
+}
+
+.submit {
+  --el-color-primary: #93795d;
+  --el-button-hover-border-color: #93795d;
+  --el-button-hover-bg-color: #93795d;
+  --el-button-active-bg-color: #93795d;
+  --el-button-active-border-color: #93795d;
+}
+
+.drag_verify {
+  border: 1px solid #dcdfe6;
+}
 </style>

+ 8 - 3
src/view/map/map-right.vue

@@ -52,9 +52,11 @@
               </span>
               <span class="oper">
                 <template v-if="router.currentRoute.value.name === 'map'">
-                  <el-icon color="#409efc" v-if="data.type === 'scene'">
-                    <Delete @click.stop="delRelicsScene(data.raw)" />
-                  </el-icon>
+                  <template v-if="data.type === 'scene'">
+                    <el-icon color="#409efc" v-if="data.raw.creationMethod !== 2">
+                      <Delete @click.stop="delRelicsScene(data.raw)" />
+                    </el-icon>
+                  </template>
                   <el-icon v-else color="#409efc">
                     <Edit @click.stop="inputPoint = data.raw" />
                   </el-icon>
@@ -178,6 +180,9 @@ const addHandler = async () => {
   const sceneCodes = scenes.value.map((scene) => scene.sceneCode);
   await selectScenes({
     sceneCodes: sceneCodes,
+    selfSceneCodes: scenes.value
+      .filter((scene) => scene.creationMethod === 2)
+      .map((scene) => scene.sceneCode),
     submit: async (nSceneCodes) => {
       const requests: Promise<any>[] = [];
       for (let i = 0; i < sceneCodes.length; i++) {

+ 21 - 21
src/view/pano/env.ts

@@ -7,27 +7,27 @@ import { setUniforms } from "./setUniform";
 
 const generatePreset = (gl: WebGL2RenderingContext) => {
   const skyCubeTex = gl.createTexture();
-  const updateSky1 = (images: HTMLImageElement[]) => {
-    gl.bindTexture(gl.TEXTURE_CUBE_MAP, skyCubeTex);
-    const mapper = [2, 4, 0, 5, 1, 3];
-    for (let i = 0; i < 6; i++) {
-      gl.texImage2D(
-        gl.TEXTURE_CUBE_MAP_POSITIVE_X + i,
-        0,
-        gl.RGB,
-        gl.RGB,
-        gl.UNSIGNED_BYTE,
-        images[mapper[i]]
-      );
-    }
-    gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
-    gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
-    gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_WRAP_R, gl.CLAMP_TO_EDGE);
-    gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
-    gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
-    gl.generateMipmap(gl.TEXTURE_CUBE_MAP);
-    gl.bindTexture(gl.TEXTURE_CUBE_MAP, null);
-  };
+  // const updateSky1 = (images: HTMLImageElement[]) => {
+  //   gl.bindTexture(gl.TEXTURE_CUBE_MAP, skyCubeTex);
+  //   const mapper = [2, 4, 0, 5, 1, 3];
+  //   for (let i = 0; i < 6; i++) {
+  //     gl.texImage2D(
+  //       gl.TEXTURE_CUBE_MAP_POSITIVE_X + i,
+  //       0,
+  //       gl.RGB,
+  //       gl.RGB,
+  //       gl.UNSIGNED_BYTE,
+  //       images[mapper[i]]
+  //     );
+  //   }
+  //   gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
+  //   gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
+  //   gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_WRAP_R, gl.CLAMP_TO_EDGE);
+  //   gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
+  //   gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
+  //   gl.generateMipmap(gl.TEXTURE_CUBE_MAP);
+  //   gl.bindTexture(gl.TEXTURE_CUBE_MAP, null);
+  // };
   const updateSky = (image: HTMLImageElement) => {
     gl.bindTexture(gl.TEXTURE_2D, skyCubeTex);
     gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image);

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

@@ -99,8 +99,8 @@ const photo = () => {
 onMounted(() => {
   if (!panoDomRef.value) throw "没有canvas DOM";
   const canvas = panoDomRef.value;
-  canvas.width = canvas.offsetWidth * 4;
-  canvas.height = canvas.offsetHeight * 4;
+  canvas.width = canvas.offsetWidth * 2;
+  canvas.height = canvas.offsetHeight * 2;
   const pano = init(canvas);
 
   destroyFns.push(

+ 38 - 3
src/view/scene-select.vue

@@ -9,13 +9,14 @@
 <script lang="ts" setup>
 import { computed, ref, watch } from "vue";
 import SceneTable from "./scene.vue";
-import { ElTable } from "element-plus";
+import { ElMessage, ElTable } from "element-plus";
 import { QuiskExpose } from "@/helper/mount";
-import { Scene } from "@/store/scene";
+import { Scene, SceneStatus } from "@/store/scene";
 
 const props = defineProps<{
   submit: (sceneCodes: string[]) => Promise<any>;
   sceneCodes: string[];
+  selfSceneCodes?: string[];
 }>();
 const sceneCodes = ref([...props.sceneCodes]);
 const originScenes = ref<Scene[]>([]);
@@ -31,7 +32,41 @@ const tableProps = {
     sceneCodes.value = sceneCodes.value.filter(
       (code) => !originSceneCodes.includes(code)
     );
-    sceneCodes.value.push(...val.map((scene) => scene.sceneCode));
+    let tip = false;
+    sceneCodes.value.push(
+      ...val
+        .filter((scene) => {
+          if (scene.calcStatus !== SceneStatus.SUCCESS) {
+            tableProps.tableRef.value!.toggleRowSelection(scene, false);
+            tip || ElMessage.error({ message: "计算中常见无法添加", repeatNum: 1 });
+            tip = true;
+            return false;
+          } else {
+            return true;
+          }
+        })
+        .map((scene) => scene.sceneCode)
+    );
+
+    sceneCodes.value.filter;
+
+    if (props.selfSceneCodes) {
+      const foreChecks = props.selfSceneCodes.filter(
+        (selfCode) => !sceneCodes.value.includes(selfCode)
+      );
+      console.log(props.selfSceneCodes, foreChecks);
+      if (foreChecks.length) {
+        tip || ElMessage.error({ message: "自动场景无法取消!", repeatNum: 1 });
+        sceneCodes.value.push(...foreChecks);
+        originScenes.value.forEach((scene) => {
+          if (foreChecks.includes(scene.sceneCode)) {
+            tableProps.tableRef.value!.toggleRowSelection(scene, true);
+          }
+        });
+      }
+    }
+
+    tip = false;
   },
   tableDataChange(val: Scene[]) {
     originScenes.value = val;

+ 1 - 1
vite.config.ts

@@ -25,7 +25,7 @@ export default defineConfig({
         rewrite: (path) => path.replace(/^\/api/, ""),
       },
       // "/api": {
-      //   target: `https://test-sp.4dkankan.com/`,
+      //   target: `sp.4dkankan.com`,
       //   changeOrigin: true,
       //   rewrite: (path) => path.replace(/^\/api/, "/api"),
       // },