Alsmile пре 2 година
комит
349695957c
59 измењених фајлова са 3991 додато и 0 уклоњено
  1. 2 0
      .env.base
  2. 24 0
      .gitignore
  3. 1 0
      README.md
  4. 31 0
      index.html
  5. 33 0
      package.json
  6. 1516 0
      pnpm-lock.yaml
  7. 7 0
      postcss.config.js
  8. BIN
      public/favicon.ico
  9. BIN
      public/img/2d.png
  10. BIN
      public/img/3d.png
  11. BIN
      public/img/404.png
  12. BIN
      public/img/WePayLogo.png
  13. BIN
      public/img/WePayLogoText.png
  14. BIN
      public/img/aftersales.png
  15. BIN
      public/img/avatar.png
  16. BIN
      public/img/baner.png
  17. BIN
      public/img/blank.png
  18. BIN
      public/img/icon-1.png
  19. BIN
      public/img/icon-2.png
  20. BIN
      public/img/icon-3.png
  21. BIN
      public/img/icon-4.png
  22. BIN
      public/img/le5le2d.png
  23. BIN
      public/img/login.png
  24. BIN
      public/img/presales.png
  25. BIN
      public/img/web.png
  26. BIN
      public/img/wechatmini.jpg
  27. BIN
      public/img/公众号.png
  28. BIN
      public/img/商务咨询.png
  29. BIN
      public/logo.png
  30. 16 0
      src/App.vue
  31. 8 0
      src/env.d.ts
  32. 12 0
      src/global.d.ts
  33. 125 0
      src/http.ts
  34. 16 0
      src/main.ts
  35. 10 0
      src/router/index.ts
  36. 49 0
      src/services/cookie.ts
  37. 49 0
      src/services/file.ts
  38. 8 0
      src/services/html.ts
  39. 31 0
      src/services/object.ts
  40. 15 0
      src/services/random.ts
  41. 36 0
      src/services/selections.ts
  42. 20 0
      src/services/url.ts
  43. 162 0
      src/services/user.ts
  44. 241 0
      src/styles/app.css
  45. 6 0
      src/styles/index.css
  46. 119 0
      src/styles/props.css
  47. 393 0
      src/styles/tdesign.css
  48. 62 0
      src/styles/var.css
  49. 48 0
      src/views/Index.vue
  50. 87 0
      src/views/components/ElementTree.vue
  51. 236 0
      src/views/components/FileProps.vue
  52. 96 0
      src/views/components/Graphics.vue
  53. 325 0
      src/views/components/Header.vue
  54. 9 0
      src/views/components/PenProps.vue
  55. 9 0
      src/views/components/PensProps.vue
  56. 141 0
      src/views/components/View.vue
  57. 21 0
      tsconfig.json
  58. 8 0
      tsconfig.node.json
  59. 19 0
      vite.config.ts

+ 2 - 0
.env.base

@@ -0,0 +1,2 @@
+BASE_URL=/developer/
+VITE_MARKET=1

+ 24 - 0
.gitignore

@@ -0,0 +1,24 @@
+# Logs
+logs
+*.log
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+pnpm-debug.log*
+lerna-debug.log*
+
+node_modules
+dist
+dist-ssr
+*.local
+
+# Editor directories and files
+.vscode/*
+!.vscode/extensions.json
+.idea
+.DS_Store
+*.suo
+*.ntvs*
+*.njsproj
+*.sln
+*.sw?

+ 1 - 0
README.md

@@ -0,0 +1 @@
+# 乐吾乐大屏可视化设计器

+ 31 - 0
index.html

@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <meta charset="UTF-8" />
+    <link rel="icon" href="/favicon.ico" />
+    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+    <title>大屏可视化设计器 - 乐吾乐Le5le</title>
+    <meta
+      name="keywords"
+      content="乐吾乐,le5le,大屏,可视化,2D,3D,无代码,低代码,零代码,云设计,网页制作,web app,PaaS,企业信息系统"
+    />
+    <meta
+      name="description"
+      content="乐吾乐Le5le - 帮助企业快速搭建可视化或企业信息系统,提高开发效率,降低开发成本和运营成本。"
+    />
+
+    <link
+      href="https://editor.yuque.com/ne-editor/lake-content-v1.css"
+      rel="stylesheet"
+    />
+  </head>
+  <body>
+    <div id="app"></div>
+    <script type="module" src="/src/main.ts"></script>
+    <script>
+      String.prototype.replaceAll = function (s1, s2) {
+        return this.replace(new RegExp(s1, 'gm'), s2);
+      };
+    </script>
+  </body>
+</html>

+ 33 - 0
package.json

@@ -0,0 +1,33 @@
+{
+  "name": "app-studio",
+  "private": true,
+  "version": "0.0.1",
+  "scripts": {
+    "start": "vite --open --port 7000",
+    "build": "vue-tsc --noEmit && vite build",
+    "app": "vue-tsc --noEmit && vite build --mode base --base=/developer/",
+    "preview": "vite preview"
+  },
+  "dependencies": {
+    "axios": "^0.26.0",
+    "dayjs": "^1.11.5",
+    "file-saver": "^2.0.5",
+    "tdesign-vue-next": "^1.2.3",
+    "vue": "^3.2.37",
+    "vue-router": "^4.1.3"
+  },
+  "devDependencies": {
+    "@types/file-saver": "^2.0.5",
+    "@types/node": "^18.6.4",
+    "@types/qrcode": "^1.5.0",
+    "@vitejs/plugin-vue": "^4.0.0",
+    "@vitejs/plugin-vue-jsx": "^3.0.0",
+    "autoprefixer": "^10.4.13",
+    "postcss": "^8.4.6",
+    "postcss-import": "^14.1.0",
+    "postcss-nested": "^6.0.1",
+    "typescript": "^4.7.4",
+    "vite": "^4.0.3",
+    "vue-tsc": "^1.0.5"
+  }
+}

+ 1516 - 0
pnpm-lock.yaml

@@ -0,0 +1,1516 @@
+lockfileVersion: 5.4
+
+specifiers:
+  '@types/file-saver': ^2.0.5
+  '@types/node': ^18.6.4
+  '@types/qrcode': ^1.5.0
+  '@vitejs/plugin-vue': ^4.0.0
+  '@vitejs/plugin-vue-jsx': ^3.0.0
+  autoprefixer: ^10.4.13
+  axios: ^0.26.0
+  dayjs: ^1.11.5
+  file-saver: ^2.0.5
+  postcss: ^8.4.6
+  postcss-import: ^14.1.0
+  postcss-nested: ^6.0.1
+  tdesign-vue-next: ^1.2.3
+  typescript: ^4.7.4
+  vite: ^4.0.3
+  vue: ^3.2.37
+  vue-router: ^4.1.3
+  vue-tsc: ^1.0.5
+
+dependencies:
+  axios: registry.npmmirror.com/axios/0.26.1
+  dayjs: registry.npmmirror.com/dayjs/1.11.6
+  file-saver: 2.0.5
+  tdesign-vue-next: 1.2.3_vue@3.2.45
+  vue: registry.npmmirror.com/vue/3.2.45
+  vue-router: registry.npmmirror.com/vue-router/4.1.6_vue@3.2.45
+
+devDependencies:
+  '@types/file-saver': 2.0.5
+  '@types/node': registry.npmmirror.com/@types/node/18.11.9
+  '@types/qrcode': registry.npmmirror.com/@types/qrcode/1.5.0
+  '@vitejs/plugin-vue': 4.0.0_vite@4.0.3+vue@3.2.45
+  '@vitejs/plugin-vue-jsx': 3.0.0_vite@4.0.3+vue@3.2.45
+  autoprefixer: registry.npmmirror.com/autoprefixer/10.4.13_postcss@8.4.19
+  postcss: registry.npmmirror.com/postcss/8.4.19
+  postcss-import: registry.npmmirror.com/postcss-import/14.1.0_postcss@8.4.19
+  postcss-nested: 6.0.1_postcss@8.4.19
+  typescript: registry.npmmirror.com/typescript/4.9.3
+  vite: 4.0.3_@types+node@18.11.9
+  vue-tsc: registry.npmmirror.com/vue-tsc/1.0.9_typescript@4.9.3
+
+packages:
+
+  /@ampproject/remapping/2.2.0:
+    resolution: {integrity: sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w==}
+    engines: {node: '>=6.0.0'}
+    dependencies:
+      '@jridgewell/gen-mapping': 0.1.1
+      '@jridgewell/trace-mapping': 0.3.17
+    dev: true
+
+  /@babel/code-frame/7.18.6:
+    resolution: {integrity: sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==}
+    engines: {node: '>=6.9.0'}
+    dependencies:
+      '@babel/highlight': 7.18.6
+    dev: true
+
+  /@babel/compat-data/7.20.5:
+    resolution: {integrity: sha512-KZXo2t10+/jxmkhNXc7pZTqRvSOIvVv/+lJwHS+B2rErwOyjuVRh60yVpb7liQ1U5t7lLJ1bz+t8tSypUZdm0g==}
+    engines: {node: '>=6.9.0'}
+    dev: true
+
+  /@babel/core/7.20.5:
+    resolution: {integrity: sha512-UdOWmk4pNWTm/4DlPUl/Pt4Gz4rcEMb7CY0Y3eJl5Yz1vI8ZJGmHWaVE55LoxRjdpx0z259GE9U5STA9atUinQ==}
+    engines: {node: '>=6.9.0'}
+    dependencies:
+      '@ampproject/remapping': 2.2.0
+      '@babel/code-frame': 7.18.6
+      '@babel/generator': 7.20.5
+      '@babel/helper-compilation-targets': 7.20.0_@babel+core@7.20.5
+      '@babel/helper-module-transforms': 7.20.2
+      '@babel/helpers': 7.20.6
+      '@babel/parser': 7.20.5
+      '@babel/template': 7.18.10
+      '@babel/traverse': 7.20.5
+      '@babel/types': 7.20.5
+      convert-source-map: 1.9.0
+      debug: 4.3.4
+      gensync: 1.0.0-beta.2
+      json5: 2.2.1
+      semver: 6.3.0
+    transitivePeerDependencies:
+      - supports-color
+    dev: true
+
+  /@babel/generator/7.20.5:
+    resolution: {integrity: sha512-jl7JY2Ykn9S0yj4DQP82sYvPU+T3g0HFcWTqDLqiuA9tGRNIj9VfbtXGAYTTkyNEnQk1jkMGOdYka8aG/lulCA==}
+    engines: {node: '>=6.9.0'}
+    dependencies:
+      '@babel/types': 7.20.5
+      '@jridgewell/gen-mapping': 0.3.2
+      jsesc: 2.5.2
+    dev: true
+
+  /@babel/helper-annotate-as-pure/7.18.6:
+    resolution: {integrity: sha512-duORpUiYrEpzKIop6iNbjnwKLAKnJ47csTyRACyEmWj0QdUrm5aqNJGHSSEQSUAvNW0ojX0dOmK9dZduvkfeXA==}
+    engines: {node: '>=6.9.0'}
+    dependencies:
+      '@babel/types': 7.20.2
+    dev: true
+
+  /@babel/helper-compilation-targets/7.20.0_@babel+core@7.20.5:
+    resolution: {integrity: sha512-0jp//vDGp9e8hZzBc6N/KwA5ZK3Wsm/pfm4CrY7vzegkVxc65SgSn6wYOnwHe9Js9HRQ1YTCKLGPzDtaS3RoLQ==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0
+    dependencies:
+      '@babel/compat-data': 7.20.5
+      '@babel/core': 7.20.5
+      '@babel/helper-validator-option': 7.18.6
+      browserslist: 4.21.4
+      semver: 6.3.0
+    dev: true
+
+  /@babel/helper-create-class-features-plugin/7.20.5_@babel+core@7.20.5:
+    resolution: {integrity: sha512-3RCdA/EmEaikrhayahwToF0fpweU/8o2p8vhc1c/1kftHOdTKuC65kik/TLc+qfbS8JKw4qqJbne4ovICDhmww==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0
+    dependencies:
+      '@babel/core': 7.20.5
+      '@babel/helper-annotate-as-pure': 7.18.6
+      '@babel/helper-environment-visitor': 7.18.9
+      '@babel/helper-function-name': 7.19.0
+      '@babel/helper-member-expression-to-functions': 7.18.9
+      '@babel/helper-optimise-call-expression': 7.18.6
+      '@babel/helper-replace-supers': 7.19.1
+      '@babel/helper-split-export-declaration': 7.18.6
+    transitivePeerDependencies:
+      - supports-color
+    dev: true
+
+  /@babel/helper-environment-visitor/7.18.9:
+    resolution: {integrity: sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg==}
+    engines: {node: '>=6.9.0'}
+    dev: true
+
+  /@babel/helper-function-name/7.19.0:
+    resolution: {integrity: sha512-WAwHBINyrpqywkUH0nTnNgI5ina5TFn85HKS0pbPDfxFfhyR/aNQEn4hGi1P1JyT//I0t4OgXUlofzWILRvS5w==}
+    engines: {node: '>=6.9.0'}
+    dependencies:
+      '@babel/template': 7.18.10
+      '@babel/types': 7.20.2
+    dev: true
+
+  /@babel/helper-hoist-variables/7.18.6:
+    resolution: {integrity: sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==}
+    engines: {node: '>=6.9.0'}
+    dependencies:
+      '@babel/types': 7.20.5
+    dev: true
+
+  /@babel/helper-member-expression-to-functions/7.18.9:
+    resolution: {integrity: sha512-RxifAh2ZoVU67PyKIO4AMi1wTenGfMR/O/ae0CCRqwgBAt5v7xjdtRw7UoSbsreKrQn5t7r89eruK/9JjYHuDg==}
+    engines: {node: '>=6.9.0'}
+    dependencies:
+      '@babel/types': 7.20.2
+    dev: true
+
+  /@babel/helper-module-imports/7.18.6:
+    resolution: {integrity: sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA==}
+    engines: {node: '>=6.9.0'}
+    dependencies:
+      '@babel/types': 7.20.2
+    dev: true
+
+  /@babel/helper-module-transforms/7.20.2:
+    resolution: {integrity: sha512-zvBKyJXRbmK07XhMuujYoJ48B5yvvmM6+wcpv6Ivj4Yg6qO7NOZOSnvZN9CRl1zz1Z4cKf8YejmCMh8clOoOeA==}
+    engines: {node: '>=6.9.0'}
+    dependencies:
+      '@babel/helper-environment-visitor': 7.18.9
+      '@babel/helper-module-imports': 7.18.6
+      '@babel/helper-simple-access': 7.20.2
+      '@babel/helper-split-export-declaration': 7.18.6
+      '@babel/helper-validator-identifier': 7.19.1
+      '@babel/template': 7.18.10
+      '@babel/traverse': 7.20.5
+      '@babel/types': 7.20.5
+    transitivePeerDependencies:
+      - supports-color
+    dev: true
+
+  /@babel/helper-optimise-call-expression/7.18.6:
+    resolution: {integrity: sha512-HP59oD9/fEHQkdcbgFCnbmgH5vIQTJbxh2yf+CdM89/glUNnuzr87Q8GIjGEnOktTROemO0Pe0iPAYbqZuOUiA==}
+    engines: {node: '>=6.9.0'}
+    dependencies:
+      '@babel/types': 7.20.2
+    dev: true
+
+  /@babel/helper-plugin-utils/7.20.2:
+    resolution: {integrity: sha512-8RvlJG2mj4huQ4pZ+rU9lqKi9ZKiRmuvGuM2HlWmkmgOhbs6zEAw6IEiJ5cQqGbDzGZOhwuOQNtZMi/ENLjZoQ==}
+    engines: {node: '>=6.9.0'}
+    dev: true
+
+  /@babel/helper-replace-supers/7.19.1:
+    resolution: {integrity: sha512-T7ahH7wV0Hfs46SFh5Jz3s0B6+o8g3c+7TMxu7xKfmHikg7EAZ3I2Qk9LFhjxXq8sL7UkP5JflezNwoZa8WvWw==}
+    engines: {node: '>=6.9.0'}
+    dependencies:
+      '@babel/helper-environment-visitor': 7.18.9
+      '@babel/helper-member-expression-to-functions': 7.18.9
+      '@babel/helper-optimise-call-expression': 7.18.6
+      '@babel/traverse': 7.20.5
+      '@babel/types': 7.20.2
+    transitivePeerDependencies:
+      - supports-color
+    dev: true
+
+  /@babel/helper-simple-access/7.20.2:
+    resolution: {integrity: sha512-+0woI/WPq59IrqDYbVGfshjT5Dmk/nnbdpcF8SnMhhXObpTq2KNBdLFRFrkVdbDOyUmHBCxzm5FHV1rACIkIbA==}
+    engines: {node: '>=6.9.0'}
+    dependencies:
+      '@babel/types': 7.20.5
+    dev: true
+
+  /@babel/helper-split-export-declaration/7.18.6:
+    resolution: {integrity: sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==}
+    engines: {node: '>=6.9.0'}
+    dependencies:
+      '@babel/types': 7.20.2
+    dev: true
+
+  /@babel/helper-string-parser/7.19.4:
+    resolution: {integrity: sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw==}
+    engines: {node: '>=6.9.0'}
+
+  /@babel/helper-validator-identifier/7.19.1:
+    resolution: {integrity: sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==}
+    engines: {node: '>=6.9.0'}
+
+  /@babel/helper-validator-option/7.18.6:
+    resolution: {integrity: sha512-XO7gESt5ouv/LRJdrVjkShckw6STTaB7l9BrpBaAHDeF5YZT+01PCwmR0SJHnkW6i8OwW/EVWRShfi4j2x+KQw==}
+    engines: {node: '>=6.9.0'}
+    dev: true
+
+  /@babel/helpers/7.20.6:
+    resolution: {integrity: sha512-Pf/OjgfgFRW5bApskEz5pvidpim7tEDPlFtKcNRXWmfHGn9IEI2W2flqRQXTFb7gIPTyK++N6rVHuwKut4XK6w==}
+    engines: {node: '>=6.9.0'}
+    dependencies:
+      '@babel/template': 7.18.10
+      '@babel/traverse': 7.20.5
+      '@babel/types': 7.20.5
+    transitivePeerDependencies:
+      - supports-color
+    dev: true
+
+  /@babel/highlight/7.18.6:
+    resolution: {integrity: sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==}
+    engines: {node: '>=6.9.0'}
+    dependencies:
+      '@babel/helper-validator-identifier': 7.19.1
+      chalk: 2.4.2
+      js-tokens: 4.0.0
+    dev: true
+
+  /@babel/parser/7.20.5:
+    resolution: {integrity: sha512-r27t/cy/m9uKLXQNWWebeCUHgnAZq0CpG1OwKRxzJMP1vpSU4bSIK2hq+/cp0bQxetkXx38n09rNu8jVkcK/zA==}
+    engines: {node: '>=6.0.0'}
+    hasBin: true
+    dependencies:
+      '@babel/types': 7.20.5
+
+  /@babel/plugin-syntax-jsx/7.18.6_@babel+core@7.20.5:
+    resolution: {integrity: sha512-6mmljtAedFGTWu2p/8WIORGwy+61PLgOMPOdazc7YoJ9ZCWUyFy3A6CpPkRKLKD1ToAesxX8KGEViAiLo9N+7Q==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+    dependencies:
+      '@babel/core': 7.20.5
+      '@babel/helper-plugin-utils': 7.20.2
+    dev: true
+
+  /@babel/plugin-syntax-typescript/7.20.0_@babel+core@7.20.5:
+    resolution: {integrity: sha512-rd9TkG+u1CExzS4SM1BlMEhMXwFLKVjOAFFCDx9PbX5ycJWDoWMcwdJH9RhkPu1dOgn5TrxLot/Gx6lWFuAUNQ==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+    dependencies:
+      '@babel/core': 7.20.5
+      '@babel/helper-plugin-utils': 7.20.2
+    dev: true
+
+  /@babel/plugin-transform-typescript/7.20.2_@babel+core@7.20.5:
+    resolution: {integrity: sha512-jvS+ngBfrnTUBfOQq8NfGnSbF9BrqlR6hjJ2yVxMkmO5nL/cdifNbI30EfjRlN4g5wYWNnMPyj5Sa6R1pbLeag==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+    dependencies:
+      '@babel/core': 7.20.5
+      '@babel/helper-create-class-features-plugin': 7.20.5_@babel+core@7.20.5
+      '@babel/helper-plugin-utils': 7.20.2
+      '@babel/plugin-syntax-typescript': 7.20.0_@babel+core@7.20.5
+    transitivePeerDependencies:
+      - supports-color
+    dev: true
+
+  /@babel/runtime/7.20.1:
+    resolution: {integrity: sha512-mrzLkl6U9YLF8qpqI7TB82PESyEGjm/0Ly91jG575eVxMMlb8fYfOXFZIJ8XfLrJZQbm7dlKry2bJmXBUEkdFg==}
+    engines: {node: '>=6.9.0'}
+    dependencies:
+      regenerator-runtime: 0.13.10
+    dev: false
+
+  /@babel/template/7.18.10:
+    resolution: {integrity: sha512-TI+rCtooWHr3QJ27kJxfjutghu44DLnasDMwpDqCXVTal9RLp3RSYNh4NdBrRP2cQAoG9A8juOQl6P6oZG4JxA==}
+    engines: {node: '>=6.9.0'}
+    dependencies:
+      '@babel/code-frame': 7.18.6
+      '@babel/parser': 7.20.5
+      '@babel/types': 7.20.5
+    dev: true
+
+  /@babel/traverse/7.20.5:
+    resolution: {integrity: sha512-WM5ZNN3JITQIq9tFZaw1ojLU3WgWdtkxnhM1AegMS+PvHjkM5IXjmYEGY7yukz5XS4sJyEf2VzWjI8uAavhxBQ==}
+    engines: {node: '>=6.9.0'}
+    dependencies:
+      '@babel/code-frame': 7.18.6
+      '@babel/generator': 7.20.5
+      '@babel/helper-environment-visitor': 7.18.9
+      '@babel/helper-function-name': 7.19.0
+      '@babel/helper-hoist-variables': 7.18.6
+      '@babel/helper-split-export-declaration': 7.18.6
+      '@babel/parser': 7.20.5
+      '@babel/types': 7.20.5
+      debug: 4.3.4
+      globals: 11.12.0
+    transitivePeerDependencies:
+      - supports-color
+    dev: true
+
+  /@babel/types/7.20.2:
+    resolution: {integrity: sha512-FnnvsNWgZCr232sqtXggapvlkk/tuwR/qhGzcmxI0GXLCjmPYQPzio2FbdlWuY6y1sHFfQKk+rRbUZ9VStQMog==}
+    engines: {node: '>=6.9.0'}
+    dependencies:
+      '@babel/helper-string-parser': 7.19.4
+      '@babel/helper-validator-identifier': 7.19.1
+      to-fast-properties: 2.0.0
+    dev: true
+
+  /@babel/types/7.20.5:
+    resolution: {integrity: sha512-c9fst/h2/dcF7H+MJKZ2T0KjEQ8hY/BNnDk/H3XY8C4Aw/eWQXWn/lWntHF9ooUBnGmEvbfGrTgLWc+um0YDUg==}
+    engines: {node: '>=6.9.0'}
+    dependencies:
+      '@babel/helper-string-parser': 7.19.4
+      '@babel/helper-validator-identifier': 7.19.1
+      to-fast-properties: 2.0.0
+
+  /@esbuild/android-arm/0.16.13:
+    resolution: {integrity: sha512-JmtqThupn9Yf+FzANE+GG73ASUkssnPwOsndUElhp23685QzRK+MO1UompOlBaXV9D5FTuYcPnw7p4mCq2YbZQ==}
+    engines: {node: '>=12'}
+    cpu: [arm]
+    os: [android]
+    requiresBuild: true
+    dev: true
+    optional: true
+
+  /@esbuild/android-arm64/0.16.13:
+    resolution: {integrity: sha512-r4xetsd1ez1NF9/9R2f9Q6AlxqiZLwUqo7ICOcvEVwopVkXUcspIjEbJk0EVTgT6Cp5+ymzGPT6YNV0ievx4yA==}
+    engines: {node: '>=12'}
+    cpu: [arm64]
+    os: [android]
+    requiresBuild: true
+    dev: true
+    optional: true
+
+  /@esbuild/android-x64/0.16.13:
+    resolution: {integrity: sha512-hKt1bFht/Vtp0xJ0ZVzFMnPy1y1ycmM3KNnp3zsyZfQmw7nhs2WLO4vxdR5YG+6RsHKCb2zbZ3VwlC0Tij0qyA==}
+    engines: {node: '>=12'}
+    cpu: [x64]
+    os: [android]
+    requiresBuild: true
+    dev: true
+    optional: true
+
+  /@esbuild/darwin-arm64/0.16.13:
+    resolution: {integrity: sha512-ogrVuNi2URocrr3Ps20f075EMm9V7IeenOi9FRj4qdbT6mQlwLuP4l90PW2iBrKERx0oRkcZprEUNsz/3xd7ww==}
+    engines: {node: '>=12'}
+    cpu: [arm64]
+    os: [darwin]
+    requiresBuild: true
+    dev: true
+    optional: true
+
+  /@esbuild/darwin-x64/0.16.13:
+    resolution: {integrity: sha512-Agajik9SBGiKD7FPXE+ExW6x3MgA/dUdpZnXa9y1tyfE4lKQx+eQiknSdrBnWPeqa9wL0AOvkhghmYhpVkyqkA==}
+    engines: {node: '>=12'}
+    cpu: [x64]
+    os: [darwin]
+    requiresBuild: true
+    dev: true
+    optional: true
+
+  /@esbuild/freebsd-arm64/0.16.13:
+    resolution: {integrity: sha512-KxMO3/XihBcHM+xQUM6nQZO1SgQuOsd1DCnKF1a4SIf/i5VD45vrqN3k8ePgFrEbMi7m5JeGmvNqwJXinF0a4Q==}
+    engines: {node: '>=12'}
+    cpu: [arm64]
+    os: [freebsd]
+    requiresBuild: true
+    dev: true
+    optional: true
+
+  /@esbuild/freebsd-x64/0.16.13:
+    resolution: {integrity: sha512-Ez15oqV1vwvZ30cVLeBW14BsWq/fdWNQGMOxxqaSJVQVLqHhvgfQ7gxGDiN9tpJdeQhqJO+Q0r02/Tce5+USNg==}
+    engines: {node: '>=12'}
+    cpu: [x64]
+    os: [freebsd]
+    requiresBuild: true
+    dev: true
+    optional: true
+
+  /@esbuild/linux-arm/0.16.13:
+    resolution: {integrity: sha512-18dLd2L3mda+iFj6sswyBMSh2UwniamD9M4DwPv8VM+9apRFlQ5IGKxBdumnTuOI4NvwwAernmUseWhYQ9k+rg==}
+    engines: {node: '>=12'}
+    cpu: [arm]
+    os: [linux]
+    requiresBuild: true
+    dev: true
+    optional: true
+
+  /@esbuild/linux-arm64/0.16.13:
+    resolution: {integrity: sha512-qi5n7KwcGViyJeZeQnu8fB6dC3Mlm5PGaqSv2HhQDDx/MPvVfQGNMcv7zcBL4qk3FkuWhGVwXkjQ76x7R0PWlA==}
+    engines: {node: '>=12'}
+    cpu: [arm64]
+    os: [linux]
+    requiresBuild: true
+    dev: true
+    optional: true
+
+  /@esbuild/linux-ia32/0.16.13:
+    resolution: {integrity: sha512-2489Xad9sr+6GD7nB913fUqpCsSwVwgskkQTq4Or2mZntSPYPebyJm8l1YruHo7oqYMTGV6RiwGE4gRo3H+EPQ==}
+    engines: {node: '>=12'}
+    cpu: [ia32]
+    os: [linux]
+    requiresBuild: true
+    dev: true
+    optional: true
+
+  /@esbuild/linux-loong64/0.16.13:
+    resolution: {integrity: sha512-x8KplRu9Y43Px8I9YS+sPBwQ+fw44Mvp2BPVADopKDWz+h3fcj1BvRU58kxb89WObmwKX9sWdtYzepL4Fmx03A==}
+    engines: {node: '>=12'}
+    cpu: [loong64]
+    os: [linux]
+    requiresBuild: true
+    dev: true
+    optional: true
+
+  /@esbuild/linux-mips64el/0.16.13:
+    resolution: {integrity: sha512-qhhdWph9FLwD9rVVC/nUf7k2U4NZIA6/mGx0B7+O6PFV0GjmPA2E3zDQ4NUjq9P26E0DeAZy9akH9dYcUBRU7A==}
+    engines: {node: '>=12'}
+    cpu: [mips64el]
+    os: [linux]
+    requiresBuild: true
+    dev: true
+    optional: true
+
+  /@esbuild/linux-ppc64/0.16.13:
+    resolution: {integrity: sha512-cVWAPKsrRVxI1jCeJHnYSbE3BrEU+pZTZK2gfao9HRxuc+3m4+RLfs3EVEpGLmMKEcWfVCB9wZ3yNxnknutGKQ==}
+    engines: {node: '>=12'}
+    cpu: [ppc64]
+    os: [linux]
+    requiresBuild: true
+    dev: true
+    optional: true
+
+  /@esbuild/linux-riscv64/0.16.13:
+    resolution: {integrity: sha512-Agb7dbRyZWnmPn5Vvf0eyqaEUqSsaIUwwyInu2EoFTaIDRp093QU2M5alUyOooMLkRbD1WvqQNwx08Z/g+SAcQ==}
+    engines: {node: '>=12'}
+    cpu: [riscv64]
+    os: [linux]
+    requiresBuild: true
+    dev: true
+    optional: true
+
+  /@esbuild/linux-s390x/0.16.13:
+    resolution: {integrity: sha512-AqRBIrc/+kl08ahliNG+EyU+j41wIzQfwBTKpi80cCDiYvYFPuXjvzZsD9muiu58Isj0RVni9VgC4xK/AnSW4g==}
+    engines: {node: '>=12'}
+    cpu: [s390x]
+    os: [linux]
+    requiresBuild: true
+    dev: true
+    optional: true
+
+  /@esbuild/linux-x64/0.16.13:
+    resolution: {integrity: sha512-S4wn2BimuhPcoArRtVrdHUKIymCCZcYAXQE47kUiX4yrUrEX2/ifn5eKNbZ5c1jJKUlh1gC2ESIN+iw3wQax3g==}
+    engines: {node: '>=12'}
+    cpu: [x64]
+    os: [linux]
+    requiresBuild: true
+    dev: true
+    optional: true
+
+  /@esbuild/netbsd-x64/0.16.13:
+    resolution: {integrity: sha512-2c8JWgfUMlQHTdaR5X3xNMwqOyad8kgeCupuVkdm3QkUOzGREjlTETQsK6oHifocYzDCo9FeKcUwsK356SdR+g==}
+    engines: {node: '>=12'}
+    cpu: [x64]
+    os: [netbsd]
+    requiresBuild: true
+    dev: true
+    optional: true
+
+  /@esbuild/openbsd-x64/0.16.13:
+    resolution: {integrity: sha512-Bwh+PmKD/LK+xBjqIpnYnKYj0fIyQJ0YpRxsn0F+WfzvQ2OA+GKDlf8AHosiCns26Q4Dje388jQVwfOBZ1GaFw==}
+    engines: {node: '>=12'}
+    cpu: [x64]
+    os: [openbsd]
+    requiresBuild: true
+    dev: true
+    optional: true
+
+  /@esbuild/sunos-x64/0.16.13:
+    resolution: {integrity: sha512-8wwk6f9XGnhrF94/DBdFM4Xm1JeCyGTCj67r516VS9yvBVQf3Rar54L+XPVDs/oZOokwH+XsktrgkuTMAmjntg==}
+    engines: {node: '>=12'}
+    cpu: [x64]
+    os: [sunos]
+    requiresBuild: true
+    dev: true
+    optional: true
+
+  /@esbuild/win32-arm64/0.16.13:
+    resolution: {integrity: sha512-Jmwbp/5ArLCiRAHC33ODfcrlIcbP/exXkOEUVkADNJC4e/so2jm+i8IQFvVX/lA2GWvK3GdgcN0VFfp9YITAbg==}
+    engines: {node: '>=12'}
+    cpu: [arm64]
+    os: [win32]
+    requiresBuild: true
+    dev: true
+    optional: true
+
+  /@esbuild/win32-ia32/0.16.13:
+    resolution: {integrity: sha512-AX6WjntGjhJHzrPSVvjMD7grxt41koHfAOx6lxLorrpDwwIKKPaGDASPZgvFIZHTbwhOtILW6vAXxYPDsKpDJA==}
+    engines: {node: '>=12'}
+    cpu: [ia32]
+    os: [win32]
+    requiresBuild: true
+    dev: true
+    optional: true
+
+  /@esbuild/win32-x64/0.16.13:
+    resolution: {integrity: sha512-A+U4gM6OOkPS03UgVU08GTpAAAxPsP/8Z4FmneGo4TaVSD99bK9gVJXlqUEPMO/htFXEAht2O6pX4ErtLY5tVg==}
+    engines: {node: '>=12'}
+    cpu: [x64]
+    os: [win32]
+    requiresBuild: true
+    dev: true
+    optional: true
+
+  /@jridgewell/gen-mapping/0.1.1:
+    resolution: {integrity: sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w==}
+    engines: {node: '>=6.0.0'}
+    dependencies:
+      '@jridgewell/set-array': 1.1.2
+      '@jridgewell/sourcemap-codec': 1.4.14
+    dev: true
+
+  /@jridgewell/gen-mapping/0.3.2:
+    resolution: {integrity: sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==}
+    engines: {node: '>=6.0.0'}
+    dependencies:
+      '@jridgewell/set-array': 1.1.2
+      '@jridgewell/sourcemap-codec': 1.4.14
+      '@jridgewell/trace-mapping': 0.3.17
+    dev: true
+
+  /@jridgewell/resolve-uri/3.1.0:
+    resolution: {integrity: sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==}
+    engines: {node: '>=6.0.0'}
+    dev: true
+
+  /@jridgewell/set-array/1.1.2:
+    resolution: {integrity: sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==}
+    engines: {node: '>=6.0.0'}
+    dev: true
+
+  /@jridgewell/sourcemap-codec/1.4.14:
+    resolution: {integrity: sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==}
+    dev: true
+
+  /@jridgewell/trace-mapping/0.3.17:
+    resolution: {integrity: sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g==}
+    dependencies:
+      '@jridgewell/resolve-uri': 3.1.0
+      '@jridgewell/sourcemap-codec': 1.4.14
+    dev: true
+
+  /@popperjs/core/2.11.6:
+    resolution: {integrity: sha512-50/17A98tWUfQ176raKiOGXuYpLyyVMkxxG6oylzL3BPOlA6ADGdK7EYunSa4I064xerltq9TGXs8HmOk5E+vw==}
+    dev: false
+
+  /@types/file-saver/2.0.5:
+    resolution: {integrity: sha512-zv9kNf3keYegP5oThGLaPk8E081DFDuwfqjtiTzm6PoxChdJ1raSuADf2YGCVIyrSynLrgc8JWv296s7Q7pQSQ==}
+    dev: true
+
+  /@types/lodash/4.14.182:
+    resolution: {integrity: sha512-/THyiqyQAP9AfARo4pF+aCGcyiQ94tX/Is2I7HofNRqoYLgN1PBoOWu2/zTA5zMxzP5EFutMtWtGAFRKUe961Q==}
+    dev: false
+
+  /@types/sortablejs/1.15.0:
+    resolution: {integrity: sha512-qrhtM7M41EhH4tZQTNw2/RJkxllBx3reiJpTbgWCM2Dx0U1sZ6LwKp9lfNln9uqE26ZMKUaPEYaD4rzvOWYtZw==}
+    dev: false
+
+  /@types/tinycolor2/1.4.3:
+    resolution: {integrity: sha512-Kf1w9NE5HEgGxCRyIcRXR/ZYtDv0V8FVPtYHwLxl0O+maGX0erE77pQlD0gpP+/KByMZ87mOA79SjifhSB3PjQ==}
+    dev: false
+
+  /@types/validator/13.7.10:
+    resolution: {integrity: sha512-t1yxFAR2n0+VO6hd/FJ9F2uezAZVWHLmpmlJzm1eX03+H7+HsuTAp7L8QJs+2pQCfWkP1+EXsGK9Z9v7o/qPVQ==}
+    dev: false
+
+  /@vitejs/plugin-vue-jsx/3.0.0_vite@4.0.3+vue@3.2.45:
+    resolution: {integrity: sha512-vurkuzgac5SYuxd2HUZqAFAWGTF10diKBwJNbCvnWijNZfXd+7jMtqjPFbGt7idOJUn584fP1Ar9j/GN2jQ3Ew==}
+    engines: {node: ^14.18.0 || >=16.0.0}
+    peerDependencies:
+      vite: ^4.0.0
+      vue: ^3.0.0
+    dependencies:
+      '@babel/core': 7.20.5
+      '@babel/plugin-transform-typescript': 7.20.2_@babel+core@7.20.5
+      '@vue/babel-plugin-jsx': 1.1.1_@babel+core@7.20.5
+      vite: 4.0.3_@types+node@18.11.9
+      vue: registry.npmmirror.com/vue/3.2.45
+    transitivePeerDependencies:
+      - supports-color
+    dev: true
+
+  /@vitejs/plugin-vue/4.0.0_vite@4.0.3+vue@3.2.45:
+    resolution: {integrity: sha512-e0X4jErIxAB5oLtDqbHvHpJe/uWNkdpYV83AOG2xo2tEVSzCzewgJMtREZM30wXnM5ls90hxiOtAuVU6H5JgbA==}
+    engines: {node: ^14.18.0 || >=16.0.0}
+    peerDependencies:
+      vite: ^4.0.0
+      vue: ^3.2.25
+    dependencies:
+      vite: 4.0.3_@types+node@18.11.9
+      vue: registry.npmmirror.com/vue/3.2.45
+    dev: true
+
+  /@vue/babel-helper-vue-transform-on/1.0.2:
+    resolution: {integrity: sha512-hz4R8tS5jMn8lDq6iD+yWL6XNB699pGIVLk7WSJnn1dbpjaazsjZQkieJoRX6gW5zpYSCFqQ7jUquPNY65tQYA==}
+    dev: true
+
+  /@vue/babel-plugin-jsx/1.1.1_@babel+core@7.20.5:
+    resolution: {integrity: sha512-j2uVfZjnB5+zkcbc/zsOc0fSNGCMMjaEXP52wdwdIfn0qjFfEYpYZBFKFg+HHnQeJCVrjOeO0YxgaL7DMrym9w==}
+    dependencies:
+      '@babel/helper-module-imports': 7.18.6
+      '@babel/plugin-syntax-jsx': 7.18.6_@babel+core@7.20.5
+      '@babel/template': 7.18.10
+      '@babel/traverse': 7.20.5
+      '@babel/types': 7.20.2
+      '@vue/babel-helper-vue-transform-on': 1.0.2
+      camelcase: 6.3.0
+      html-tags: 3.2.0
+      svg-tags: 1.0.0
+    transitivePeerDependencies:
+      - '@babel/core'
+      - supports-color
+    dev: true
+
+  /ansi-styles/3.2.1:
+    resolution: {integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==}
+    engines: {node: '>=4'}
+    dependencies:
+      color-convert: 1.9.3
+    dev: true
+
+  /browserslist/4.21.4:
+    resolution: {integrity: sha512-CBHJJdDmgjl3daYjN5Cp5kbTf1mUhZoS+beLklHIvkOWscs83YAhLlF3Wsh/lciQYAcbBJgTOD44VtG31ZM4Hw==}
+    engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7}
+    hasBin: true
+    dependencies:
+      caniuse-lite: 1.0.30001431
+      electron-to-chromium: 1.4.284
+      node-releases: 2.0.6
+      update-browserslist-db: 1.0.10_browserslist@4.21.4
+    dev: true
+
+  /camelcase/6.3.0:
+    resolution: {integrity: sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==}
+    engines: {node: '>=10'}
+    dev: true
+
+  /caniuse-lite/1.0.30001431:
+    resolution: {integrity: sha512-zBUoFU0ZcxpvSt9IU66dXVT/3ctO1cy4y9cscs1szkPlcWb6pasYM144GqrUygUbT+k7cmUCW61cvskjcv0enQ==}
+    dev: true
+
+  /chalk/2.4.2:
+    resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==}
+    engines: {node: '>=4'}
+    dependencies:
+      ansi-styles: 3.2.1
+      escape-string-regexp: 1.0.5
+      supports-color: 5.5.0
+    dev: true
+
+  /color-convert/1.9.3:
+    resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==}
+    dependencies:
+      color-name: 1.1.3
+    dev: true
+
+  /color-name/1.1.3:
+    resolution: {integrity: sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==}
+    dev: true
+
+  /convert-source-map/1.9.0:
+    resolution: {integrity: sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==}
+    dev: true
+
+  /cssesc/3.0.0:
+    resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==}
+    engines: {node: '>=4'}
+    hasBin: true
+    dev: true
+
+  /dayjs/1.11.6:
+    resolution: {integrity: sha512-zZbY5giJAinCG+7AGaw0wIhNZ6J8AhWuSXKvuc1KAyMiRsvGQWqh4L+MomvhdAYjN+lqvVCMq1I41e3YHvXkyQ==}
+    dev: false
+
+  /debug/4.3.4:
+    resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==}
+    engines: {node: '>=6.0'}
+    peerDependencies:
+      supports-color: '*'
+    peerDependenciesMeta:
+      supports-color:
+        optional: true
+    dependencies:
+      ms: 2.1.2
+    dev: true
+
+  /electron-to-chromium/1.4.284:
+    resolution: {integrity: sha512-M8WEXFuKXMYMVr45fo8mq0wUrrJHheiKZf6BArTKk9ZBYCKJEOU5H8cdWgDT+qCVZf7Na4lVUaZsA+h6uA9+PA==}
+    dev: true
+
+  /esbuild/0.16.13:
+    resolution: {integrity: sha512-oYwFdSEIoKM1oYzyem1osgKJAvg5447XF+05ava21fOtilyb2HeQQh26/74K4WeAk5dZmj/Mx10zUqUnI14jhA==}
+    engines: {node: '>=12'}
+    hasBin: true
+    requiresBuild: true
+    optionalDependencies:
+      '@esbuild/android-arm': 0.16.13
+      '@esbuild/android-arm64': 0.16.13
+      '@esbuild/android-x64': 0.16.13
+      '@esbuild/darwin-arm64': 0.16.13
+      '@esbuild/darwin-x64': 0.16.13
+      '@esbuild/freebsd-arm64': 0.16.13
+      '@esbuild/freebsd-x64': 0.16.13
+      '@esbuild/linux-arm': 0.16.13
+      '@esbuild/linux-arm64': 0.16.13
+      '@esbuild/linux-ia32': 0.16.13
+      '@esbuild/linux-loong64': 0.16.13
+      '@esbuild/linux-mips64el': 0.16.13
+      '@esbuild/linux-ppc64': 0.16.13
+      '@esbuild/linux-riscv64': 0.16.13
+      '@esbuild/linux-s390x': 0.16.13
+      '@esbuild/linux-x64': 0.16.13
+      '@esbuild/netbsd-x64': 0.16.13
+      '@esbuild/openbsd-x64': 0.16.13
+      '@esbuild/sunos-x64': 0.16.13
+      '@esbuild/win32-arm64': 0.16.13
+      '@esbuild/win32-ia32': 0.16.13
+      '@esbuild/win32-x64': 0.16.13
+    dev: true
+
+  /escalade/3.1.1:
+    resolution: {integrity: sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==}
+    engines: {node: '>=6'}
+    dev: true
+
+  /escape-string-regexp/1.0.5:
+    resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==}
+    engines: {node: '>=0.8.0'}
+    dev: true
+
+  /file-saver/2.0.5:
+    resolution: {integrity: sha512-P9bmyZ3h/PRG+Nzga+rbdI4OEpNDzAVyy74uVO9ATgzLK6VtAsYybF/+TOCvrc0MO793d6+42lLyZTw7/ArVzA==}
+    dev: false
+
+  /fsevents/2.3.2:
+    resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==}
+    engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
+    os: [darwin]
+    requiresBuild: true
+    dev: true
+    optional: true
+
+  /function-bind/1.1.1:
+    resolution: {integrity: sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==}
+    dev: true
+
+  /gensync/1.0.0-beta.2:
+    resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==}
+    engines: {node: '>=6.9.0'}
+    dev: true
+
+  /globals/11.12.0:
+    resolution: {integrity: sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==}
+    engines: {node: '>=4'}
+    dev: true
+
+  /has-flag/3.0.0:
+    resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==}
+    engines: {node: '>=4'}
+    dev: true
+
+  /has/1.0.3:
+    resolution: {integrity: sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==}
+    engines: {node: '>= 0.4.0'}
+    dependencies:
+      function-bind: 1.1.1
+    dev: true
+
+  /html-tags/3.2.0:
+    resolution: {integrity: sha512-vy7ClnArOZwCnqZgvv+ddgHgJiAFXe3Ge9ML5/mBctVJoUoYPCdxVucOywjDARn6CVoh3dRSFdPHy2sX80L0Wg==}
+    engines: {node: '>=8'}
+    dev: true
+
+  /is-core-module/2.11.0:
+    resolution: {integrity: sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==}
+    dependencies:
+      has: 1.0.3
+    dev: true
+
+  /js-tokens/4.0.0:
+    resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==}
+    dev: true
+
+  /jsesc/2.5.2:
+    resolution: {integrity: sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==}
+    engines: {node: '>=4'}
+    hasBin: true
+    dev: true
+
+  /json5/2.2.1:
+    resolution: {integrity: sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA==}
+    engines: {node: '>=6'}
+    hasBin: true
+    dev: true
+
+  /lodash/4.17.21:
+    resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==}
+    dev: false
+
+  /mitt/3.0.0:
+    resolution: {integrity: sha512-7dX2/10ITVyqh4aOSVI9gdape+t9l2/8QxHrFmUXu4EEUpdlxl6RudZUPZoc+zuY2hk1j7XxVroIVIan/pD/SQ==}
+    dev: false
+
+  /ms/2.1.2:
+    resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==}
+    dev: true
+
+  /nanoid/3.3.4:
+    resolution: {integrity: sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==}
+    engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
+    hasBin: true
+    dev: true
+
+  /node-releases/2.0.6:
+    resolution: {integrity: sha512-PiVXnNuFm5+iYkLBNeq5211hvO38y63T0i2KKh2KnUs3RpzJ+JtODFjkD8yjLwnDkTYF1eKXheUwdssR+NRZdg==}
+    dev: true
+
+  /path-parse/1.0.7:
+    resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==}
+    dev: true
+
+  /picocolors/1.0.0:
+    resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==}
+    dev: true
+
+  /postcss-nested/6.0.1_postcss@8.4.19:
+    resolution: {integrity: sha512-mEp4xPMi5bSWiMbsgoPfcP74lsWLHkQbZc3sY+jWYd65CUwXrUaTp0fmNpa01ZcETKlIgUdFN/MpS2xZtqL9dQ==}
+    engines: {node: '>=12.0'}
+    peerDependencies:
+      postcss: ^8.2.14
+    dependencies:
+      postcss: registry.npmmirror.com/postcss/8.4.19
+      postcss-selector-parser: 6.0.11
+    dev: true
+
+  /postcss-selector-parser/6.0.11:
+    resolution: {integrity: sha512-zbARubNdogI9j7WY4nQJBiNqQf3sLS3wCP4WfOidu+p28LofJqDH1tcXypGrcmMHhDk2t9wGhCsYe/+szLTy1g==}
+    engines: {node: '>=4'}
+    dependencies:
+      cssesc: 3.0.0
+      util-deprecate: 1.0.2
+    dev: true
+
+  /postcss/8.4.20:
+    resolution: {integrity: sha512-6Q04AXR1212bXr5fh03u8aAwbLxAQNGQ/Q1LNa0VfOI06ZAlhPHtQvE4OIdpj4kLThXilalPnmDSOD65DcHt+g==}
+    engines: {node: ^10 || ^12 || >=14}
+    dependencies:
+      nanoid: 3.3.4
+      picocolors: 1.0.0
+      source-map-js: 1.0.2
+    dev: true
+
+  /regenerator-runtime/0.13.10:
+    resolution: {integrity: sha512-KepLsg4dU12hryUO7bp/axHAKvwGOCV0sGloQtpagJ12ai+ojVDqkeGSiRX1zlq+kjIMZ1t7gpze+26QqtdGqw==}
+    dev: false
+
+  /resolve/1.22.1:
+    resolution: {integrity: sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==}
+    hasBin: true
+    dependencies:
+      is-core-module: 2.11.0
+      path-parse: 1.0.7
+      supports-preserve-symlinks-flag: 1.0.0
+    dev: true
+
+  /rollup/3.9.1:
+    resolution: {integrity: sha512-GswCYHXftN8ZKGVgQhTFUJB/NBXxrRGgO2NCy6E8s1rwEJ4Q9/VttNqcYfEvx4dTo4j58YqdC3OVztPzlKSX8w==}
+    engines: {node: '>=14.18.0', npm: '>=8.0.0'}
+    hasBin: true
+    optionalDependencies:
+      fsevents: 2.3.2
+    dev: true
+
+  /semver/6.3.0:
+    resolution: {integrity: sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==}
+    hasBin: true
+    dev: true
+
+  /sortablejs/1.15.0:
+    resolution: {integrity: sha512-bv9qgVMjUMf89wAvM6AxVvS/4MX3sPeN0+agqShejLU5z5GX4C75ow1O2e5k4L6XItUyAK3gH6AxSbXrOM5e8w==}
+    dev: false
+
+  /source-map-js/1.0.2:
+    resolution: {integrity: sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==}
+    engines: {node: '>=0.10.0'}
+    dev: true
+
+  /supports-color/5.5.0:
+    resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==}
+    engines: {node: '>=4'}
+    dependencies:
+      has-flag: 3.0.0
+    dev: true
+
+  /supports-preserve-symlinks-flag/1.0.0:
+    resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==}
+    engines: {node: '>= 0.4'}
+    dev: true
+
+  /svg-tags/1.0.0:
+    resolution: {integrity: sha512-ovssysQTa+luh7A5Weu3Rta6FJlFBBbInjOh722LIt6klpU2/HtdUbszju/G4devcvk8PGt7FCLv5wftu3THUA==}
+    dev: true
+
+  /tdesign-icons-vue-next/0.1.7_vue@3.2.45:
+    resolution: {integrity: sha512-4BwPIo0K1EHPhy0YToc1I2GUnr6SLmiP6vxyNuC993+x0xv2C2jMXzOcDiJGX5QsgicJ7UEHMIfs+fvtDLgbng==}
+    peerDependencies:
+      vue: ^3.0.0
+    dependencies:
+      '@babel/runtime': 7.20.1
+      vue: registry.npmmirror.com/vue/3.2.45
+    dev: false
+
+  /tdesign-vue-next/1.2.3_vue@3.2.45:
+    resolution: {integrity: sha512-S5PTEN6olfnK0mo4p7DduhuDK14/jpHR3Y7+/qO6BTk/iRjSN5nLkvaqk2DGTN/zqmtBLuPwlWcr1qj/hpprEQ==}
+    peerDependencies:
+      vue: '>=3.1.0'
+    dependencies:
+      '@babel/runtime': 7.20.1
+      '@popperjs/core': 2.11.6
+      '@types/lodash': 4.14.182
+      '@types/sortablejs': 1.15.0
+      '@types/tinycolor2': 1.4.3
+      '@types/validator': 13.7.10
+      dayjs: 1.11.6
+      lodash: 4.17.21
+      mitt: 3.0.0
+      sortablejs: 1.15.0
+      tdesign-icons-vue-next: 0.1.7_vue@3.2.45
+      tinycolor2: 1.4.2
+      validator: 13.7.0
+      vue: registry.npmmirror.com/vue/3.2.45
+    dev: false
+
+  /tinycolor2/1.4.2:
+    resolution: {integrity: sha512-vJhccZPs965sV/L2sU4oRQVAos0pQXwsvTLkWYdqJ+a8Q5kPFzJTuOFwy7UniPli44NKQGAglksjvOcpo95aZA==}
+    dev: false
+
+  /to-fast-properties/2.0.0:
+    resolution: {integrity: sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==}
+    engines: {node: '>=4'}
+
+  /update-browserslist-db/1.0.10_browserslist@4.21.4:
+    resolution: {integrity: sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ==}
+    hasBin: true
+    peerDependencies:
+      browserslist: '>= 4.21.0'
+    dependencies:
+      browserslist: registry.npmmirror.com/browserslist/4.21.4
+      escalade: 3.1.1
+      picocolors: 1.0.0
+    dev: true
+
+  /util-deprecate/1.0.2:
+    resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==}
+    dev: true
+
+  /validator/13.7.0:
+    resolution: {integrity: sha512-nYXQLCBkpJ8X6ltALua9dRrZDHVYxjJ1wgskNt1lH9fzGjs3tgojGSCBjmEPwkWS1y29+DrizMTW19Pr9uB2nw==}
+    engines: {node: '>= 0.10'}
+    dev: false
+
+  /vite/4.0.3_@types+node@18.11.9:
+    resolution: {integrity: sha512-HvuNv1RdE7deIfQb8mPk51UKjqptO/4RXZ5yXSAvurd5xOckwS/gg8h9Tky3uSbnjYTgUm0hVCet1cyhKd73ZA==}
+    engines: {node: ^14.18.0 || >=16.0.0}
+    hasBin: true
+    peerDependencies:
+      '@types/node': '>= 14'
+      less: '*'
+      sass: '*'
+      stylus: '*'
+      sugarss: '*'
+      terser: ^5.4.0
+    peerDependenciesMeta:
+      '@types/node':
+        optional: true
+      less:
+        optional: true
+      sass:
+        optional: true
+      stylus:
+        optional: true
+      sugarss:
+        optional: true
+      terser:
+        optional: true
+    dependencies:
+      '@types/node': registry.npmmirror.com/@types/node/18.11.9
+      esbuild: 0.16.13
+      postcss: 8.4.20
+      resolve: 1.22.1
+      rollup: 3.9.1
+    optionalDependencies:
+      fsevents: 2.3.2
+    dev: true
+
+  registry.npmmirror.com/@types/node/18.11.9:
+    resolution: {integrity: sha512-CRpX21/kGdzjOpFsZSkcrXMGIBWMGNIHXXBVFSH+ggkftxg+XYP20TESbh+zFvFj3EQOl5byk0HTRn1IL6hbqg==, registry: http://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/@types/node/-/node-18.11.9.tgz}
+    name: '@types/node'
+    version: 18.11.9
+    dev: true
+
+  registry.npmmirror.com/@types/qrcode/1.5.0:
+    resolution: {integrity: sha512-x5ilHXRxUPIMfjtM+1vf/GPTRWZ81nqscursm5gMznJeK9M0YnZ1c3bEvRLQ0zSSgedLx1J6MGL231ObQGGhaA==, registry: http://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/@types/qrcode/-/qrcode-1.5.0.tgz}
+    name: '@types/qrcode'
+    version: 1.5.0
+    dependencies:
+      '@types/node': registry.npmmirror.com/@types/node/18.11.9
+    dev: true
+
+  registry.npmmirror.com/@volar/language-core/1.0.9:
+    resolution: {integrity: sha512-5Fty3slLet6svXiJw2YxhYeo6c7wFdtILrql5bZymYLM+HbiZtJbryW1YnUEKAP7MO9Mbeh+TNH4Z0HFxHgIqw==, registry: http://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/@volar/language-core/-/language-core-1.0.9.tgz}
+    name: '@volar/language-core'
+    version: 1.0.9
+    dependencies:
+      '@volar/source-map': registry.npmmirror.com/@volar/source-map/1.0.9
+      '@vue/reactivity': registry.npmmirror.com/@vue/reactivity/3.2.45
+      muggle-string: registry.npmmirror.com/muggle-string/0.1.0
+    dev: true
+
+  registry.npmmirror.com/@volar/source-map/1.0.9:
+    resolution: {integrity: sha512-fazB/vy5ZEJ3yKx4fabJyGNI3CBkdLkfEIRVu6+1P3VixK0Mn+eqyUIkLBrzGYaeFM3GybhCLCvsVdNz0Fu/CQ==, registry: http://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/@volar/source-map/-/source-map-1.0.9.tgz}
+    name: '@volar/source-map'
+    version: 1.0.9
+    dependencies:
+      muggle-string: registry.npmmirror.com/muggle-string/0.1.0
+    dev: true
+
+  registry.npmmirror.com/@volar/typescript/1.0.9:
+    resolution: {integrity: sha512-dVziu+ShQUWuMukM6bvK2v2O446/gG6l1XkTh2vfkccw1IzjfbiP1TWQoNo1ipTfZOtu5YJGYAx+o5HNrGXWfQ==, registry: http://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/@volar/typescript/-/typescript-1.0.9.tgz}
+    name: '@volar/typescript'
+    version: 1.0.9
+    dependencies:
+      '@volar/language-core': registry.npmmirror.com/@volar/language-core/1.0.9
+    dev: true
+
+  registry.npmmirror.com/@volar/vue-language-core/1.0.9:
+    resolution: {integrity: sha512-tofNoR8ShPFenHT1YVMuvoXtXWwoQE+fiXVqSmW0dSKZqEDjWQ3YeXSd0a6aqyKaIbvR7kWWGp34WbpQlwf9Ww==, registry: http://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/@volar/vue-language-core/-/vue-language-core-1.0.9.tgz}
+    name: '@volar/vue-language-core'
+    version: 1.0.9
+    dependencies:
+      '@volar/language-core': registry.npmmirror.com/@volar/language-core/1.0.9
+      '@volar/source-map': registry.npmmirror.com/@volar/source-map/1.0.9
+      '@vue/compiler-dom': registry.npmmirror.com/@vue/compiler-dom/3.2.45
+      '@vue/compiler-sfc': registry.npmmirror.com/@vue/compiler-sfc/3.2.45
+      '@vue/reactivity': registry.npmmirror.com/@vue/reactivity/3.2.45
+      '@vue/shared': registry.npmmirror.com/@vue/shared/3.2.45
+      minimatch: registry.npmmirror.com/minimatch/5.1.0
+      vue-template-compiler: registry.npmmirror.com/vue-template-compiler/2.7.14
+    dev: true
+
+  registry.npmmirror.com/@volar/vue-typescript/1.0.9:
+    resolution: {integrity: sha512-ZLe4y9YNbviACa7uAMCilzxA76gbbSlKfjspXBzk6fCobd8QCIig+VyDYcjANIlm2HhgSCX8jYTzhCKlegh4mw==, registry: http://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/@volar/vue-typescript/-/vue-typescript-1.0.9.tgz}
+    name: '@volar/vue-typescript'
+    version: 1.0.9
+    dependencies:
+      '@volar/typescript': registry.npmmirror.com/@volar/typescript/1.0.9
+      '@volar/vue-language-core': registry.npmmirror.com/@volar/vue-language-core/1.0.9
+    dev: true
+
+  registry.npmmirror.com/@vue/compiler-core/3.2.45:
+    resolution: {integrity: sha512-rcMj7H+PYe5wBV3iYeUgbCglC+pbpN8hBLTJvRiK2eKQiWqu+fG9F+8sW99JdL4LQi7Re178UOxn09puSXvn4A==, registry: http://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/@vue/compiler-core/-/compiler-core-3.2.45.tgz}
+    name: '@vue/compiler-core'
+    version: 3.2.45
+    dependencies:
+      '@babel/parser': 7.20.5
+      '@vue/shared': registry.npmmirror.com/@vue/shared/3.2.45
+      estree-walker: registry.npmmirror.com/estree-walker/2.0.2
+      source-map: registry.npmmirror.com/source-map/0.6.1
+
+  registry.npmmirror.com/@vue/compiler-dom/3.2.45:
+    resolution: {integrity: sha512-tyYeUEuKqqZO137WrZkpwfPCdiiIeXYCcJ8L4gWz9vqaxzIQRccTSwSWZ/Axx5YR2z+LvpUbmPNXxuBU45lyRw==, registry: http://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/@vue/compiler-dom/-/compiler-dom-3.2.45.tgz}
+    name: '@vue/compiler-dom'
+    version: 3.2.45
+    dependencies:
+      '@vue/compiler-core': registry.npmmirror.com/@vue/compiler-core/3.2.45
+      '@vue/shared': registry.npmmirror.com/@vue/shared/3.2.45
+
+  registry.npmmirror.com/@vue/compiler-sfc/3.2.45:
+    resolution: {integrity: sha512-1jXDuWah1ggsnSAOGsec8cFjT/K6TMZ0sPL3o3d84Ft2AYZi2jWJgRMjw4iaK0rBfA89L5gw427H4n1RZQBu6Q==, registry: http://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/@vue/compiler-sfc/-/compiler-sfc-3.2.45.tgz}
+    name: '@vue/compiler-sfc'
+    version: 3.2.45
+    dependencies:
+      '@babel/parser': 7.20.5
+      '@vue/compiler-core': registry.npmmirror.com/@vue/compiler-core/3.2.45
+      '@vue/compiler-dom': registry.npmmirror.com/@vue/compiler-dom/3.2.45
+      '@vue/compiler-ssr': registry.npmmirror.com/@vue/compiler-ssr/3.2.45
+      '@vue/reactivity-transform': registry.npmmirror.com/@vue/reactivity-transform/3.2.45
+      '@vue/shared': registry.npmmirror.com/@vue/shared/3.2.45
+      estree-walker: registry.npmmirror.com/estree-walker/2.0.2
+      magic-string: registry.npmmirror.com/magic-string/0.25.9
+      postcss: registry.npmmirror.com/postcss/8.4.19
+      source-map: registry.npmmirror.com/source-map/0.6.1
+
+  registry.npmmirror.com/@vue/compiler-ssr/3.2.45:
+    resolution: {integrity: sha512-6BRaggEGqhWht3lt24CrIbQSRD5O07MTmd+LjAn5fJj568+R9eUD2F7wMQJjX859seSlrYog7sUtrZSd7feqrQ==, registry: http://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/@vue/compiler-ssr/-/compiler-ssr-3.2.45.tgz}
+    name: '@vue/compiler-ssr'
+    version: 3.2.45
+    dependencies:
+      '@vue/compiler-dom': registry.npmmirror.com/@vue/compiler-dom/3.2.45
+      '@vue/shared': registry.npmmirror.com/@vue/shared/3.2.45
+
+  registry.npmmirror.com/@vue/devtools-api/6.4.5:
+    resolution: {integrity: sha512-JD5fcdIuFxU4fQyXUu3w2KpAJHzTVdN+p4iOX2lMWSHMOoQdMAcpFLZzm9Z/2nmsoZ1a96QEhZ26e50xLBsgOQ==, registry: http://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/@vue/devtools-api/-/devtools-api-6.4.5.tgz}
+    name: '@vue/devtools-api'
+    version: 6.4.5
+    dev: false
+
+  registry.npmmirror.com/@vue/reactivity-transform/3.2.45:
+    resolution: {integrity: sha512-BHVmzYAvM7vcU5WmuYqXpwaBHjsS8T63jlKGWVtHxAHIoMIlmaMyurUSEs1Zcg46M4AYT5MtB1U274/2aNzjJQ==, registry: http://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/@vue/reactivity-transform/-/reactivity-transform-3.2.45.tgz}
+    name: '@vue/reactivity-transform'
+    version: 3.2.45
+    dependencies:
+      '@babel/parser': 7.20.5
+      '@vue/compiler-core': registry.npmmirror.com/@vue/compiler-core/3.2.45
+      '@vue/shared': registry.npmmirror.com/@vue/shared/3.2.45
+      estree-walker: registry.npmmirror.com/estree-walker/2.0.2
+      magic-string: registry.npmmirror.com/magic-string/0.25.9
+
+  registry.npmmirror.com/@vue/reactivity/3.2.45:
+    resolution: {integrity: sha512-PRvhCcQcyEVohW0P8iQ7HDcIOXRjZfAsOds3N99X/Dzewy8TVhTCT4uXpAHfoKjVTJRA0O0K+6QNkDIZAxNi3A==, registry: http://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/@vue/reactivity/-/reactivity-3.2.45.tgz}
+    name: '@vue/reactivity'
+    version: 3.2.45
+    dependencies:
+      '@vue/shared': registry.npmmirror.com/@vue/shared/3.2.45
+
+  registry.npmmirror.com/@vue/runtime-core/3.2.45:
+    resolution: {integrity: sha512-gzJiTA3f74cgARptqzYswmoQx0fIA+gGYBfokYVhF8YSXjWTUA2SngRzZRku2HbGbjzB6LBYSbKGIaK8IW+s0A==, registry: http://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/@vue/runtime-core/-/runtime-core-3.2.45.tgz}
+    name: '@vue/runtime-core'
+    version: 3.2.45
+    dependencies:
+      '@vue/reactivity': registry.npmmirror.com/@vue/reactivity/3.2.45
+      '@vue/shared': registry.npmmirror.com/@vue/shared/3.2.45
+
+  registry.npmmirror.com/@vue/runtime-dom/3.2.45:
+    resolution: {integrity: sha512-cy88YpfP5Ue2bDBbj75Cb4bIEZUMM/mAkDMfqDTpUYVgTf/kuQ2VQ8LebuZ8k6EudgH8pYhsGWHlY0lcxlvTwA==, registry: http://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/@vue/runtime-dom/-/runtime-dom-3.2.45.tgz}
+    name: '@vue/runtime-dom'
+    version: 3.2.45
+    dependencies:
+      '@vue/runtime-core': registry.npmmirror.com/@vue/runtime-core/3.2.45
+      '@vue/shared': registry.npmmirror.com/@vue/shared/3.2.45
+      csstype: registry.npmmirror.com/csstype/2.6.21
+
+  registry.npmmirror.com/@vue/server-renderer/3.2.45_vue@3.2.45:
+    resolution: {integrity: sha512-ebiMq7q24WBU1D6uhPK//2OTR1iRIyxjF5iVq/1a5I1SDMDyDu4Ts6fJaMnjrvD3MqnaiFkKQj+LKAgz5WIK3g==, registry: http://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/@vue/server-renderer/-/server-renderer-3.2.45.tgz}
+    id: registry.npmmirror.com/@vue/server-renderer/3.2.45
+    name: '@vue/server-renderer'
+    version: 3.2.45
+    peerDependencies:
+      vue: 3.2.45
+    dependencies:
+      '@vue/compiler-ssr': registry.npmmirror.com/@vue/compiler-ssr/3.2.45
+      '@vue/shared': registry.npmmirror.com/@vue/shared/3.2.45
+      vue: registry.npmmirror.com/vue/3.2.45
+
+  registry.npmmirror.com/@vue/shared/3.2.45:
+    resolution: {integrity: sha512-Ewzq5Yhimg7pSztDV+RH1UDKBzmtqieXQlpTVm2AwraoRL/Rks96mvd8Vgi7Lj+h+TH8dv7mXD3FRZR3TUvbSg==, registry: http://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/@vue/shared/-/shared-3.2.45.tgz}
+    name: '@vue/shared'
+    version: 3.2.45
+
+  registry.npmmirror.com/autoprefixer/10.4.13_postcss@8.4.19:
+    resolution: {integrity: sha512-49vKpMqcZYsJjwotvt4+h/BCjJVnhGwcLpDt5xkcaOG3eLrG/HUYLagrihYsQ+qrIBgIzX1Rw7a6L8I/ZA1Atg==, registry: http://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/autoprefixer/-/autoprefixer-10.4.13.tgz}
+    id: registry.npmmirror.com/autoprefixer/10.4.13
+    name: autoprefixer
+    version: 10.4.13
+    engines: {node: ^10 || ^12 || >=14}
+    hasBin: true
+    peerDependencies:
+      postcss: ^8.1.0
+    dependencies:
+      browserslist: registry.npmmirror.com/browserslist/4.21.4
+      caniuse-lite: registry.npmmirror.com/caniuse-lite/1.0.30001431
+      fraction.js: registry.npmmirror.com/fraction.js/4.2.0
+      normalize-range: registry.npmmirror.com/normalize-range/0.1.2
+      picocolors: registry.npmmirror.com/picocolors/1.0.0
+      postcss: registry.npmmirror.com/postcss/8.4.19
+      postcss-value-parser: registry.npmmirror.com/postcss-value-parser/4.2.0
+    dev: true
+
+  registry.npmmirror.com/axios/0.26.1:
+    resolution: {integrity: sha512-fPwcX4EvnSHuInCMItEhAGnaSEXRBjtzh9fOtsE6E1G6p7vl7edEeZe11QHf18+6+9gR5PbKV/sGKNaD8YaMeA==, registry: http://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/axios/-/axios-0.26.1.tgz}
+    name: axios
+    version: 0.26.1
+    dependencies:
+      follow-redirects: registry.npmmirror.com/follow-redirects/1.15.2
+    transitivePeerDependencies:
+      - debug
+    dev: false
+
+  registry.npmmirror.com/balanced-match/1.0.2:
+    resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==, registry: http://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/balanced-match/-/balanced-match-1.0.2.tgz}
+    name: balanced-match
+    version: 1.0.2
+    dev: true
+
+  registry.npmmirror.com/brace-expansion/2.0.1:
+    resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==, registry: http://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/brace-expansion/-/brace-expansion-2.0.1.tgz}
+    name: brace-expansion
+    version: 2.0.1
+    dependencies:
+      balanced-match: registry.npmmirror.com/balanced-match/1.0.2
+    dev: true
+
+  registry.npmmirror.com/browserslist/4.21.4:
+    resolution: {integrity: sha512-CBHJJdDmgjl3daYjN5Cp5kbTf1mUhZoS+beLklHIvkOWscs83YAhLlF3Wsh/lciQYAcbBJgTOD44VtG31ZM4Hw==, registry: http://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/browserslist/-/browserslist-4.21.4.tgz}
+    name: browserslist
+    version: 4.21.4
+    engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7}
+    hasBin: true
+    dependencies:
+      caniuse-lite: 1.0.30001431
+      electron-to-chromium: 1.4.284
+      node-releases: 2.0.6
+      update-browserslist-db: 1.0.10_browserslist@4.21.4
+    dev: true
+
+  registry.npmmirror.com/caniuse-lite/1.0.30001431:
+    resolution: {integrity: sha512-zBUoFU0ZcxpvSt9IU66dXVT/3ctO1cy4y9cscs1szkPlcWb6pasYM144GqrUygUbT+k7cmUCW61cvskjcv0enQ==, registry: http://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/caniuse-lite/-/caniuse-lite-1.0.30001431.tgz}
+    name: caniuse-lite
+    version: 1.0.30001431
+    dev: true
+
+  registry.npmmirror.com/csstype/2.6.21:
+    resolution: {integrity: sha512-Z1PhmomIfypOpoMjRQB70jfvy/wxT50qW08YXO5lMIJkrdq4yOTR+AW7FqutScmB9NkLwxo+jU+kZLbofZZq/w==, registry: http://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/csstype/-/csstype-2.6.21.tgz}
+    name: csstype
+    version: 2.6.21
+
+  registry.npmmirror.com/dayjs/1.11.6:
+    resolution: {integrity: sha512-zZbY5giJAinCG+7AGaw0wIhNZ6J8AhWuSXKvuc1KAyMiRsvGQWqh4L+MomvhdAYjN+lqvVCMq1I41e3YHvXkyQ==, registry: http://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/dayjs/-/dayjs-1.11.6.tgz}
+    name: dayjs
+    version: 1.11.6
+    dev: false
+
+  registry.npmmirror.com/de-indent/1.0.2:
+    resolution: {integrity: sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg==, registry: http://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/de-indent/-/de-indent-1.0.2.tgz}
+    name: de-indent
+    version: 1.0.2
+    dev: true
+
+  registry.npmmirror.com/estree-walker/2.0.2:
+    resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==, registry: http://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/estree-walker/-/estree-walker-2.0.2.tgz}
+    name: estree-walker
+    version: 2.0.2
+
+  registry.npmmirror.com/follow-redirects/1.15.2:
+    resolution: {integrity: sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==, registry: http://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/follow-redirects/-/follow-redirects-1.15.2.tgz}
+    name: follow-redirects
+    version: 1.15.2
+    engines: {node: '>=4.0'}
+    peerDependencies:
+      debug: '*'
+    peerDependenciesMeta:
+      debug:
+        optional: true
+    dev: false
+
+  registry.npmmirror.com/fraction.js/4.2.0:
+    resolution: {integrity: sha512-MhLuK+2gUcnZe8ZHlaaINnQLl0xRIGRfcGk2yl8xoQAfHrSsL3rYu6FCmBdkdbhc9EPlwyGHewaRsvwRMJtAlA==, registry: http://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/fraction.js/-/fraction.js-4.2.0.tgz}
+    name: fraction.js
+    version: 4.2.0
+    dev: true
+
+  registry.npmmirror.com/function-bind/1.1.1:
+    resolution: {integrity: sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==, registry: http://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/function-bind/-/function-bind-1.1.1.tgz}
+    name: function-bind
+    version: 1.1.1
+    dev: true
+
+  registry.npmmirror.com/has/1.0.3:
+    resolution: {integrity: sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==, registry: http://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/has/-/has-1.0.3.tgz}
+    name: has
+    version: 1.0.3
+    engines: {node: '>= 0.4.0'}
+    dependencies:
+      function-bind: registry.npmmirror.com/function-bind/1.1.1
+    dev: true
+
+  registry.npmmirror.com/he/1.2.0:
+    resolution: {integrity: sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==, registry: http://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/he/-/he-1.2.0.tgz}
+    name: he
+    version: 1.2.0
+    hasBin: true
+    dev: true
+
+  registry.npmmirror.com/is-core-module/2.11.0:
+    resolution: {integrity: sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==, registry: http://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/is-core-module/-/is-core-module-2.11.0.tgz}
+    name: is-core-module
+    version: 2.11.0
+    dependencies:
+      has: registry.npmmirror.com/has/1.0.3
+    dev: true
+
+  registry.npmmirror.com/magic-string/0.25.9:
+    resolution: {integrity: sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ==, registry: http://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/magic-string/-/magic-string-0.25.9.tgz}
+    name: magic-string
+    version: 0.25.9
+    dependencies:
+      sourcemap-codec: registry.npmmirror.com/sourcemap-codec/1.4.8
+
+  registry.npmmirror.com/minimatch/5.1.0:
+    resolution: {integrity: sha512-9TPBGGak4nHfGZsPBohm9AWg6NoT7QTCehS3BIJABslyZbzxfV78QM2Y6+i741OPZIafFAaiiEMh5OyIrJPgtg==, registry: http://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/minimatch/-/minimatch-5.1.0.tgz}
+    name: minimatch
+    version: 5.1.0
+    engines: {node: '>=10'}
+    dependencies:
+      brace-expansion: registry.npmmirror.com/brace-expansion/2.0.1
+    dev: true
+
+  registry.npmmirror.com/muggle-string/0.1.0:
+    resolution: {integrity: sha512-Tr1knR3d2mKvvWthlk7202rywKbiOm4rVFLsfAaSIhJ6dt9o47W4S+JMtWhd/PW9Wrdew2/S2fSvhz3E2gkfEg==, registry: http://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/muggle-string/-/muggle-string-0.1.0.tgz}
+    name: muggle-string
+    version: 0.1.0
+    dev: true
+
+  registry.npmmirror.com/nanoid/3.3.4:
+    resolution: {integrity: sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==, registry: http://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/nanoid/-/nanoid-3.3.4.tgz}
+    name: nanoid
+    version: 3.3.4
+    engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
+    hasBin: true
+
+  registry.npmmirror.com/normalize-range/0.1.2:
+    resolution: {integrity: sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==, registry: http://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/normalize-range/-/normalize-range-0.1.2.tgz}
+    name: normalize-range
+    version: 0.1.2
+    engines: {node: '>=0.10.0'}
+    dev: true
+
+  registry.npmmirror.com/path-parse/1.0.7:
+    resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==, registry: http://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/path-parse/-/path-parse-1.0.7.tgz}
+    name: path-parse
+    version: 1.0.7
+    dev: true
+
+  registry.npmmirror.com/picocolors/1.0.0:
+    resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==, registry: http://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/picocolors/-/picocolors-1.0.0.tgz}
+    name: picocolors
+    version: 1.0.0
+
+  registry.npmmirror.com/pify/2.3.0:
+    resolution: {integrity: sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==, registry: http://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/pify/-/pify-2.3.0.tgz}
+    name: pify
+    version: 2.3.0
+    engines: {node: '>=0.10.0'}
+    dev: true
+
+  registry.npmmirror.com/postcss-import/14.1.0_postcss@8.4.19:
+    resolution: {integrity: sha512-flwI+Vgm4SElObFVPpTIT7SU7R3qk2L7PyduMcokiaVKuWv9d/U+Gm/QAd8NDLuykTWTkcrjOeD2Pp1rMeBTGw==, registry: http://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/postcss-import/-/postcss-import-14.1.0.tgz}
+    id: registry.npmmirror.com/postcss-import/14.1.0
+    name: postcss-import
+    version: 14.1.0
+    engines: {node: '>=10.0.0'}
+    peerDependencies:
+      postcss: ^8.0.0
+    dependencies:
+      postcss: registry.npmmirror.com/postcss/8.4.19
+      postcss-value-parser: registry.npmmirror.com/postcss-value-parser/4.2.0
+      read-cache: registry.npmmirror.com/read-cache/1.0.0
+      resolve: registry.npmmirror.com/resolve/1.22.1
+    dev: true
+
+  registry.npmmirror.com/postcss-value-parser/4.2.0:
+    resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==, registry: http://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz}
+    name: postcss-value-parser
+    version: 4.2.0
+    dev: true
+
+  registry.npmmirror.com/postcss/8.4.19:
+    resolution: {integrity: sha512-h+pbPsyhlYj6N2ozBmHhHrs9DzGmbaarbLvWipMRO7RLS+v4onj26MPFXA5OBYFxyqYhUJK456SwDcY9H2/zsA==, registry: http://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/postcss/-/postcss-8.4.19.tgz}
+    name: postcss
+    version: 8.4.19
+    engines: {node: ^10 || ^12 || >=14}
+    dependencies:
+      nanoid: registry.npmmirror.com/nanoid/3.3.4
+      picocolors: registry.npmmirror.com/picocolors/1.0.0
+      source-map-js: registry.npmmirror.com/source-map-js/1.0.2
+
+  registry.npmmirror.com/read-cache/1.0.0:
+    resolution: {integrity: sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==, registry: http://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/read-cache/-/read-cache-1.0.0.tgz}
+    name: read-cache
+    version: 1.0.0
+    dependencies:
+      pify: registry.npmmirror.com/pify/2.3.0
+    dev: true
+
+  registry.npmmirror.com/resolve/1.22.1:
+    resolution: {integrity: sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==, registry: http://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/resolve/-/resolve-1.22.1.tgz}
+    name: resolve
+    version: 1.22.1
+    hasBin: true
+    dependencies:
+      is-core-module: registry.npmmirror.com/is-core-module/2.11.0
+      path-parse: registry.npmmirror.com/path-parse/1.0.7
+      supports-preserve-symlinks-flag: registry.npmmirror.com/supports-preserve-symlinks-flag/1.0.0
+    dev: true
+
+  registry.npmmirror.com/source-map-js/1.0.2:
+    resolution: {integrity: sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==, registry: http://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/source-map-js/-/source-map-js-1.0.2.tgz}
+    name: source-map-js
+    version: 1.0.2
+    engines: {node: '>=0.10.0'}
+
+  registry.npmmirror.com/source-map/0.6.1:
+    resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==, registry: http://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/source-map/-/source-map-0.6.1.tgz}
+    name: source-map
+    version: 0.6.1
+    engines: {node: '>=0.10.0'}
+
+  registry.npmmirror.com/sourcemap-codec/1.4.8:
+    resolution: {integrity: sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==, registry: http://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz}
+    name: sourcemap-codec
+    version: 1.4.8
+
+  registry.npmmirror.com/supports-preserve-symlinks-flag/1.0.0:
+    resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==, registry: http://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz}
+    name: supports-preserve-symlinks-flag
+    version: 1.0.0
+    engines: {node: '>= 0.4'}
+    dev: true
+
+  registry.npmmirror.com/typescript/4.9.3:
+    resolution: {integrity: sha512-CIfGzTelbKNEnLpLdGFgdyKhG23CKdKgQPOBc+OUNrkJ2vr+KSzsSV5kq5iWhEQbok+quxgGzrAtGWCyU7tHnA==, registry: http://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/typescript/-/typescript-4.9.3.tgz}
+    name: typescript
+    version: 4.9.3
+    engines: {node: '>=4.2.0'}
+    hasBin: true
+    dev: true
+
+  registry.npmmirror.com/vue-router/4.1.6_vue@3.2.45:
+    resolution: {integrity: sha512-DYWYwsG6xNPmLq/FmZn8Ip+qrhFEzA14EI12MsMgVxvHFDYvlr4NXpVF5hrRH1wVcDP8fGi5F4rxuJSl8/r+EQ==, registry: http://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/vue-router/-/vue-router-4.1.6.tgz}
+    id: registry.npmmirror.com/vue-router/4.1.6
+    name: vue-router
+    version: 4.1.6
+    peerDependencies:
+      vue: ^3.2.0
+    dependencies:
+      '@vue/devtools-api': registry.npmmirror.com/@vue/devtools-api/6.4.5
+      vue: registry.npmmirror.com/vue/3.2.45
+    dev: false
+
+  registry.npmmirror.com/vue-template-compiler/2.7.14:
+    resolution: {integrity: sha512-zyA5Y3ArvVG0NacJDkkzJuPQDF8RFeRlzV2vLeSnhSpieO6LK2OVbdLPi5MPPs09Ii+gMO8nY4S3iKQxBxDmWQ==, registry: http://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/vue-template-compiler/-/vue-template-compiler-2.7.14.tgz}
+    name: vue-template-compiler
+    version: 2.7.14
+    dependencies:
+      de-indent: registry.npmmirror.com/de-indent/1.0.2
+      he: registry.npmmirror.com/he/1.2.0
+    dev: true
+
+  registry.npmmirror.com/vue-tsc/1.0.9_typescript@4.9.3:
+    resolution: {integrity: sha512-vRmHD1K6DmBymNhoHjQy/aYKTRQNLGOu2/ESasChG9Vy113K6CdP0NlhR0bzgFJfv2eFB9Ez/9L5kIciUajBxQ==, registry: http://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/vue-tsc/-/vue-tsc-1.0.9.tgz}
+    id: registry.npmmirror.com/vue-tsc/1.0.9
+    name: vue-tsc
+    version: 1.0.9
+    hasBin: true
+    peerDependencies:
+      typescript: '*'
+    dependencies:
+      '@volar/vue-language-core': registry.npmmirror.com/@volar/vue-language-core/1.0.9
+      '@volar/vue-typescript': registry.npmmirror.com/@volar/vue-typescript/1.0.9
+      typescript: registry.npmmirror.com/typescript/4.9.3
+    dev: true
+
+  registry.npmmirror.com/vue/3.2.45:
+    resolution: {integrity: sha512-9Nx/Mg2b2xWlXykmCwiTUCWHbWIj53bnkizBxKai1g61f2Xit700A1ljowpTIM11e3uipOeiPcSqnmBg6gyiaA==, registry: http://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/vue/-/vue-3.2.45.tgz}
+    name: vue
+    version: 3.2.45
+    dependencies:
+      '@vue/compiler-dom': registry.npmmirror.com/@vue/compiler-dom/3.2.45
+      '@vue/compiler-sfc': registry.npmmirror.com/@vue/compiler-sfc/3.2.45
+      '@vue/runtime-dom': registry.npmmirror.com/@vue/runtime-dom/3.2.45
+      '@vue/server-renderer': registry.npmmirror.com/@vue/server-renderer/3.2.45_vue@3.2.45
+      '@vue/shared': registry.npmmirror.com/@vue/shared/3.2.45

+ 7 - 0
postcss.config.js

@@ -0,0 +1,7 @@
+module.exports = {
+  plugins: {
+    'postcss-import': {},
+    'postcss-nested': {},
+    autoprefixer: {},
+  },
+};

BIN
public/favicon.ico


BIN
public/img/2d.png


BIN
public/img/3d.png


BIN
public/img/404.png


BIN
public/img/WePayLogo.png


BIN
public/img/WePayLogoText.png


BIN
public/img/aftersales.png


BIN
public/img/avatar.png


BIN
public/img/baner.png


BIN
public/img/blank.png


BIN
public/img/icon-1.png


BIN
public/img/icon-2.png


BIN
public/img/icon-3.png


BIN
public/img/icon-4.png


BIN
public/img/le5le2d.png


BIN
public/img/login.png


BIN
public/img/presales.png


BIN
public/img/web.png


BIN
public/img/wechatmini.jpg


BIN
public/img/公众号.png


BIN
public/img/商务咨询.png



+ 16 - 0
src/App.vue

@@ -0,0 +1,16 @@
+<template>
+  <router-view />
+</template>
+
+<script lang="ts" setup>
+import { onBeforeMount } from 'vue';
+
+import { useUser } from './services/user';
+
+const { getUser } = useUser();
+
+onBeforeMount(() => {
+  getUser();
+});
+</script>
+<style lang="postcss" scoped></style>

+ 8 - 0
src/env.d.ts

@@ -0,0 +1,8 @@
+/// <reference types="vite/client" />
+
+declare module '*.vue' {
+  import type { DefineComponent } from 'vue';
+  // eslint-disable5le-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/ban-types
+  const component: DefineComponent<{}, {}, any>;
+  export default component;
+}

+ 12 - 0
src/global.d.ts

@@ -0,0 +1,12 @@
+declare interface Window {
+  company: string;
+  accountHome: string;
+  wechatClientId: string;
+  githubClientId: string;
+  sinaClientId: string;
+  passwordRegex: string;
+}
+
+declare interface localStorage {
+  company: string;
+}

+ 125 - 0
src/http.ts

@@ -0,0 +1,125 @@
+import { MessagePlugin } from 'tdesign-vue-next';
+import axios from 'axios';
+import { getCookie } from '@/services/cookie';
+import router from './router';
+
+// axios 配置
+axios.defaults.timeout = 60000;
+axios.defaults.withCredentials = false;
+
+const requestDebounceMap = new Map();
+const requestThrottleSet = new Set();
+
+// http request 拦截器
+axios.interceptors.request.use(
+  (config: any) => {
+    config.baseURL = '/api';
+    config.headers.Authorization =
+      'Bearer ' + (localStorage.token || getCookie('token') || '');
+
+    if (config.params) {
+      // 防抖, 比如输入搜索
+      if (config.params.debounce > 0) {
+        const url = config.method + config.url;
+
+        const cache: any = requestDebounceMap.get(url);
+        if (cache) {
+          clearTimeout(cache.timer);
+          cache.reject();
+        }
+
+        delete config.params.debounce;
+        delete config.params.throttle;
+
+        return new Promise((resolve, reject) => {
+          const newCache: any = { reject };
+          newCache.timer = setTimeout(() => {
+            requestDebounceMap.delete(url);
+            resolve(config);
+          }, config.params.debounce);
+          requestDebounceMap.set(url, newCache);
+        });
+      }
+      // 节流,避免短时间重复请求,比如点击确定
+      else if (config.params.throttle > 0) {
+        const url = config.method + config.url;
+        // 已经存在,取消重复请求
+        if (requestThrottleSet.has(url)) {
+          return Promise.reject('Repeated request.');
+        }
+        requestThrottleSet.add(url);
+        setTimeout(() => {
+          requestThrottleSet.delete(url);
+        }, config.params.throttle);
+      }
+
+      delete config.params.debounce;
+      delete config.params.throttle;
+    }
+
+    return config;
+  },
+  (err: any) => Promise.reject(err)
+);
+
+// http response 拦截器
+axios.interceptors.response.use(
+  (response: any) => {
+    if (response && response.data && response.data.error) {
+      MessagePlugin.error(response.data.error);
+      return;
+    }
+    if (response) {
+      return response.data;
+    }
+    return;
+  },
+  (error: any) => {
+    if (error && !error.response) {
+      return;
+    }
+    if (error && error.response) {
+      if (error.response.config.url === '/account/profile') {
+        return;
+      }
+
+      if (
+        error.response.status !== 401 &&
+        error.response &&
+        error.response.data &&
+        error.response.data.error
+      ) {
+        MessagePlugin.error(error.response.data.error);
+        return;
+      }
+
+      switch (error.response.status) {
+        case 401:
+          sessionStorage.setItem('cb', encodeURIComponent(location.href));
+          router.replace({ path: '/login' });
+          break;
+        case 403:
+          MessagePlugin.error('请求错误,不合法的请求!');
+
+          break;
+        case 404:
+          if (error.response.config.url.indexOf('/data/') !== 0) {
+            MessagePlugin.error('访问数据不存在,请检查后重试!');
+          }
+          break;
+        case 500:
+          MessagePlugin.error('请求服务错误,请稍后重试!');
+          break;
+        case 504:
+          MessagePlugin.error('网络超时,请检测你的网络!');
+          break;
+        default:
+          MessagePlugin.error('未知网络错误!');
+          break;
+      }
+    }
+    // return error.response ? error.response.data : error;
+  }
+);
+
+export default axios;

+ 16 - 0
src/main.ts

@@ -0,0 +1,16 @@
+import { createApp } from 'vue';
+import TDesign from 'tdesign-vue-next';
+import App from './App.vue';
+import router from './router';
+import '@/http';
+import dayjs from 'dayjs';
+import relativeTime from 'dayjs/plugin/relativeTime';
+import zh from 'dayjs/locale/zh-cn';
+
+import './styles/index.css';
+
+dayjs.extend(relativeTime);
+dayjs.locale(zh.name);
+
+const app = createApp(App).use(router).use(TDesign);
+app.mount('#app');

+ 10 - 0
src/router/index.ts

@@ -0,0 +1,10 @@
+import { createRouter, createWebHistory } from 'vue-router';
+
+const routes = [{ path: '/', component: () => import('@/views/Index.vue') }];
+
+const router = createRouter({
+  history: createWebHistory(import.meta.env.BASE_URL),
+  routes,
+});
+
+export default router;

+ 49 - 0
src/services/cookie.ts

@@ -0,0 +1,49 @@
+export function getCookie(name: string): string {
+  let arr: RegExpMatchArray | null;
+  const reg = new RegExp('(^| )' + name + '=([^;]*)(;|$)');
+  if ((arr = document.cookie.match(reg))) {
+    return decodeURIComponent(arr[2]);
+  } else {
+    return '';
+  }
+}
+
+// options: {
+//   expires?:number,
+//   path?:string,
+//   domain?:string
+// }
+export function setCookie(name: string, value: string, options?: any) {
+  const myWindow: any = window;
+  let cookieStr = myWindow.escape(name) + '=' + myWindow.escape(value) + ';';
+
+  if (!options) {
+    options = {};
+  }
+  if (options.expires) {
+    const dtExpires = new Date(new Date().getTime() + options.expires * 1000 * 60 * 60 * 24);
+    cookieStr += 'expires=' + dtExpires.toUTCString() + ';';
+  }
+  if (options.path) {
+    cookieStr += 'path=' + options.path + ';';
+  }
+  if (options.domain) {
+    cookieStr += 'domain=' + options.domain + ';';
+  }
+
+  document.cookie = cookieStr;
+}
+
+// options: {
+//   path?:string,
+//   domain?:string
+// }
+export function deleteCookie(name: string, options?: any) {
+  if (getCookie(name)) {
+    if (!options) {
+      options = {};
+    }
+    options.expires = -1;
+    setCookie(name, '', options);
+  }
+}

+ 49 - 0
src/services/file.ts

@@ -0,0 +1,49 @@
+import axios from 'axios';
+
+export async function upload(blob: Blob, shared = false, filename = '') {
+  const form = new FormData();
+  form.append('directory', filename);
+  form.append('public', shared + '');
+  form.append('file', blob);
+  const ret: any = await axios.post('/api/image', form);
+  if (ret.error) {
+    return null;
+  }
+
+  return ret;
+}
+
+export async function delImage(image: string) {
+  const ret: any = await axios.delete('/api' + image);
+  if (ret.error) {
+    return false;
+  }
+
+  return true;
+}
+
+export async function addImage(image: string) {
+  const ret: any = await axios.post('/api/user/image', { image });
+  if (ret.error) {
+    return '';
+  }
+
+  return ret.id;
+}
+
+export function filename(str: string) {
+  const i = str.lastIndexOf('.');
+  return str.substring(0, i);
+}
+
+const units = ['KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
+export function formatBytes(size: number) {
+  let l = 0;
+  let n = size / 1024;
+
+  while (n >= 1024 && ++l) {
+    n = n / 1024;
+  }
+
+  return Math.round(n * 100) / 100 + ' ' + units[l];
+}

+ 8 - 0
src/services/html.ts

@@ -0,0 +1,8 @@
+export function getHtmlText(html: string, trimSpace?: boolean) {
+  const text = html.replace(/<[^<>]+>/g, '');
+  if (trimSpace) {
+    return text.replace(/\s/g, '');
+  }
+
+  return text;
+}

+ 31 - 0
src/services/object.ts

@@ -0,0 +1,31 @@
+export function setAttr(obj: any, key: string, value: any) {
+  key.split('.').reduce((obj, key, i, keys) => {
+    if (i === keys.length - 1) {
+      obj[key] = value;
+    }
+    return obj[key];
+  }, obj);
+}
+
+export function getAttr(obj: any, key: string): any {
+  return key.split('.').reduce((obj, key) => obj[key], obj);
+}
+
+export function clearObject(obj: any) {
+  if (!obj || typeof obj !== 'object') {
+    return;
+  }
+
+  if (Array.isArray(obj)) {
+    obj.length = 0;
+  }
+
+  for (const k in obj) {
+    obj[k] = undefined;
+  }
+}
+
+export function updateObject(obj: any, v: any) {
+  clearObject(obj);
+  Object.assign(obj, v);
+}

+ 15 - 0
src/services/random.ts

@@ -0,0 +1,15 @@
+export function s4() {
+  return (((1 + Math.random()) * 0x10000) | 0).toString(16).substring(1);
+}
+
+export function s8() {
+  return (((1 + Math.random()) * 0x100000000) | 0).toString(16).substring(1);
+}
+
+export function s12() {
+  return s4() + s8();
+}
+
+export function s16() {
+  return s8() + s8();
+}

+ 36 - 0
src/services/selections.ts

@@ -0,0 +1,36 @@
+import { reactive } from 'vue';
+
+export enum SelectionMode {
+  File,
+  Pen,
+  Pens,
+}
+
+const selections = reactive<any>({
+  // 选中对象类型:0 - 画布;1 - 单个图元;2 - 多选
+  mode: SelectionMode.File,
+  pen: undefined,
+  pens: undefined,
+});
+
+export const useSelection = () => {
+  const select = (data: any) => {
+    if (!data) {
+      selections.mode = SelectionMode.File;
+      selections.pen = undefined;
+      selections.pens = undefined;
+    } else if (Array.isArray(data)) {
+      selections.mode = SelectionMode.Pens;
+      selections.pen = undefined;
+      selections.pens = data;
+    } else {
+      selections.mode = SelectionMode.Pen;
+      selections.pen = data;
+      selections.pens = undefined;
+    }
+  };
+  return {
+    selections,
+    select,
+  };
+};

+ 20 - 0
src/services/url.ts

@@ -0,0 +1,20 @@
+
+export const objectToQueryString = (obj: any) => {
+  return '?' + Object.keys(obj)
+    .map(function (key) {
+      if (obj[key] === undefined || obj[key] === null || obj[key] === '') {
+        return '';
+      }
+
+      if (obj[key] instanceof Array || Object.prototype.toString.call(obj[key]) === '[object Array]') {
+        return obj[key]
+          .map(function (item: string) {
+            return encodeURIComponent(key) + '=' + encodeURIComponent(item);
+          })
+          .join('&');
+      } else {
+        return encodeURIComponent(key) + '=' + encodeURIComponent(obj[key]);
+      }
+    })
+    .join('&');
+};

+ 162 - 0
src/services/user.ts

@@ -0,0 +1,162 @@
+import { reactive } from 'vue';
+
+import axios from 'axios';
+import dayjs from 'dayjs';
+
+import { updateObject } from '@/services/object';
+import router from '@/router';
+import { deleteCookie, setCookie } from '@/services/cookie';
+
+export interface IUser {
+  id?: string;
+  account?: string;
+  phone?: string;
+  email?: string;
+  password?: string;
+  rePassword?: string;
+  username?: string;
+  avatarUrl?: string;
+  role?: string;
+  token?: string;
+  inviteCode?: string;
+  code?: string;
+  remember?: boolean;
+  captcha?: string;
+  vip?: string;
+  vipExpired?: boolean;
+  roles?: string[];
+  isOperation?: boolean;
+  company?: any;
+  department?: any;
+  corporation?: any;
+  teams?: { id: string; name: string }[];
+  wechat?: any;
+  sina?: any;
+  github?: any;
+  created?: any;
+  createdAt?: any;
+}
+
+const user = reactive<IUser>({
+  id: '',
+});
+
+const message = reactive<{ unread: number }>({
+  unread: 0,
+});
+
+export const useUser = () => {
+  const getUser = async (token?: boolean) => {
+    const params: any = {};
+    if (token) {
+      if (localStorage.getItem('remember')) {
+        params.token = 'r';
+      } else {
+        params.token = '1';
+      }
+    }
+    const ret: IUser = await axios.get('/account/profile', { params });
+    if (!ret) {
+      return;
+    }
+    setUser(ret);
+
+    return user;
+  };
+
+  const getMessage = async () => {
+    const ret: { unread: number } = await axios.post('/message/unread/count');
+    ret && (message.unread = ret.unread);
+  };
+
+  const setUser = async (data: IUser) => {
+    if (data.department && !data.department.id) {
+      delete data.department;
+    }
+
+    if (data.vip) {
+      const vip = new Date(data.vip);
+      if (vip > new Date()) {
+        data.vipExpired = false;
+      } else if (vip > new Date('2023-01-17T08:00:00+08:00')) {
+        data.vipExpired = true;
+      } else {
+        data.vip = undefined;
+      }
+      if (data.vip) {
+        data.vip = dayjs(data.vip).format('YYYY-MM-DD HH:mm:ss');
+      }
+    }
+    if (data.roles) {
+      for (const item of data.roles) {
+        if (item.indexOf('运营') > -1) {
+          data.isOperation = true;
+          break;
+        }
+      }
+    }
+    data.created = dayjs(data.createdAt).format('YYYY-MM-DD HH:mm:ss');
+
+    if (data.token) {
+      let baseUrl = import.meta.env.BASE_URL;
+      if (baseUrl && baseUrl !== '/') {
+        localStorage.setItem('token', data.token);
+      } else {
+        setCookie('token', data.token, {
+          path: '/',
+          domain: getRootDomain(),
+        });
+        localStorage.removeItem('token');
+      }
+      delete data.token;
+    }
+    updateObject(user, data);
+  };
+
+  const signout = () => {
+    updateObject(user, {});
+    localStorage.removeItem('token');
+    const domain = getRootDomain();
+    if (domain) {
+      deleteCookie('token', {
+        path: '/',
+        domain: 'le5le.com',
+      });
+    }
+
+    router.replace({ path: '/login', query: router.currentRoute.value.query });
+  };
+
+  const getRootDomain = () => {
+    let domain = '';
+    const domainItems = document.domain.split('.');
+    if (
+      domainItems.length < 3 ||
+      (domainItems.length === 4 &&
+        +domainItems[0] > 0 &&
+        +domainItems[1] > 0 &&
+        +domainItems[2] > 0 &&
+        +domainItems[3] > 0)
+    ) {
+      domain = '';
+    } else if (
+      document.domain.endsWith('.com.cn') ||
+      document.domain.endsWith('.org.cn')
+    ) {
+      domain = domainItems.slice(-3).join('.');
+    } else {
+      domain = domainItems.slice(-2).join('.');
+    }
+
+    return domain;
+  };
+
+  return {
+    user,
+    message,
+    getUser,
+    getMessage,
+    setUser,
+    signout,
+  };
+};

+ 241 - 0
src/styles/app.css

@@ -0,0 +1,241 @@
+* {
+  box-sizing: border-box;
+  margin: 0;
+  padding: 0;
+  outline: none;
+}
+
+body {
+  height: 100vh;
+  margin: 0;
+  padding: 0;
+  font-size: var(--font-size);
+  font-family: var(--font-family);
+  overflow: overlay;
+  background: var(--color-background);
+  color: var(--color);
+}
+
+input {
+  font-size: var(--font-size);
+}
+
+img {
+  max-width: 100%;
+  max-height: 100%;
+}
+
+#app {
+  min-height: 100%;
+}
+
+.title,
+h1,
+h2,
+h3,
+h4 {
+  color: var(--color-title);
+  font-weight: 700;
+  line-height: 2;
+}
+
+h1 {
+  font-size: 32px;
+}
+
+h2 {
+  font-size: 28px;
+}
+
+h3 {
+  font-size: 24px;
+}
+
+h4 {
+  font-size: 20px;
+}
+
+h5 {
+  font-size: 16px;
+}
+
+.bland {
+  color: var(--color-bland);
+}
+
+.color {
+  color: var(--color);
+}
+
+.primary {
+  color: var(--color-primary);
+}
+
+.desc {
+  color: var(--color-desc);
+}
+
+.gray {
+  color: var(--color-gray);
+}
+
+.success {
+  color: var(--color-success);
+}
+
+.error {
+  color: var(--color-error);
+}
+
+.hover:hover {
+  color: var(--color-primary) !important;
+  cursor: pointer !important;
+}
+
+a {
+  color: var(--color-primary);
+  cursor: pointer;
+  text-decoration: none;
+}
+
+a:hover {
+  text-decoration: underline;
+}
+
+a.hover {
+  text-decoration: none;
+}
+
+a.hover:hover {
+  color: var(--color-primary-hover);
+}
+
+.button {
+  display: inline-block !important;
+  padding: 0 16px;
+  line-height: 2;
+  color: var(--color);
+  border: 1px solid currentColor;
+  border-radius: 2px;
+  font-size: var(--font-size);
+  text-align: center;
+}
+
+.button.solid {
+  background-color: var(--color-background);
+  border-color: var(--color-background);
+
+  &.bland {
+    color: #ffffff !important;
+    background-color: var(--color-bland);
+    border-color: var(--color-bland);
+
+    &:hover {
+      color: #ffffff !important;
+      text-decoration: none;
+      background-color: var(--color-bland-hover);
+      border-color: var(--color-bland-hover);
+    }
+  }
+}
+
+.button.solid:hover {
+  background-color: var(--color-border);
+  border-color: var(--color-border);
+}
+
+.button.solid.primary {
+  color: #ffffff !important;
+  background-color: var(--color-primary);
+  border-color: var(--color-primary);
+}
+
+.button.solid.primary:hover {
+  color: #ffffff !important;
+  text-decoration: none;
+  background-color: var(--color-primary-hover);
+  border-color: var(--color-primary-hover);
+}
+
+.button.primary {
+  color: var(--color-primary) !important;
+}
+
+.button.primary:hover {
+  color: var(--color-primary-hover) !important;
+}
+
+.button.bland {
+  color: var(--color-bland) !important;
+}
+
+.button.bland:hover {
+  color: var(--color-bland-hover) !important;
+}
+
+.flex {
+  display: flex;
+  &.middle {
+    align-items: center;
+  }
+  &.center {
+    justify-content: center;
+  }
+
+  &.between {
+    justify-content: space-between;
+  }
+}
+
+.flex-grow {
+  flex: 1;
+}
+
+.w-full {
+  width: 100%;
+}
+
+.ml-4 {
+  margin-left: 4px;
+}
+
+.ml-8 {
+  margin-left: 8px;
+}
+
+.mt-8 {
+  margin-top: 8px;
+}
+
+.mt-12 {
+  margin-top: 12px;
+}
+
+.mt-16 {
+  margin-top: 16px;
+}
+
+/*定义滚动条轨道 内阴影+圆角*/
+::-webkit-scrollbar {
+  width: 5px;
+  height: 7px;
+  background: transparent;
+}
+
+::-webkit-scrollbar-corner {
+  background-color: transparent;
+}
+
+/*滚动条里面小方块*/
+::-webkit-scrollbar-thumb {
+  background-color: var(--color-scrollbar);
+  border-radius: 4px;
+}
+
+::-webkit-scrollbar-thumb:hover {
+  background-color: var(--color-scrollbar-hover);
+}
+
+::-webkit-scrollbar-track {
+  background-color: transparent !important;
+}

+ 6 - 0
src/styles/index.css

@@ -0,0 +1,6 @@
+@import 'tdesign-vue-next/es/style/index.css';
+
+@import './var.css';
+@import './tdesign.css';
+@import './app.css';
+@import './props.css';

+ 119 - 0
src/styles/props.css

@@ -0,0 +1,119 @@
+.props {
+  .panel {
+    padding: 16px;
+    width: 268px;
+  }
+
+  .title {
+    font-size: 12px;
+    color: var(--color);
+  }
+
+  .form-item {
+    display: flex;
+
+    label {
+      width: 80px;
+      font-size: 12px;
+      line-height: 30px;
+      flex-shrink: 0;
+      color: var(--color);
+    }
+
+    .t-input-number {
+      width: 100px;
+      line-height: 30px;
+      height: 30px;
+
+      .t-input__wrap {
+        height: 100%;
+
+        .t-input {
+          height: 100%;
+          background: none;
+          border-color: transparent;
+          color: var(--color-title);
+          padding-right: 20px;
+
+          &:hover,
+          &.t-is-focused {
+            border-color: var(--color-border-input);
+          }
+        }
+      }
+
+      .t-input-number__increase,
+      .t-input-number__decrease {
+        width: 20px;
+      }
+    }
+
+    .t-input {
+      border-color: transparent;
+      &:hover,
+      &.t-is-focused {
+        border-color: var(--color-border-input);
+      }
+    }
+
+    .t-icon {
+      font-size: 16px;
+      height: 30px;
+    }
+  }
+
+  .t-collapse.t--border-less {
+    .t-collapse-panel__header {
+      font-size: 14px;
+      font-weight: 400;
+      border-top: 1px solid var(--color-border-input);
+      color: var(--color-title);
+    }
+
+    .t-collapse-panel__wrapper .t-collapse-panel__content {
+      padding: 8px 16px 16px 16px;
+    }
+  }
+
+  .t-radio-group .t-radio {
+    line-height: 2;
+  }
+  .t-radio__input {
+    width: 12px;
+    height: 12px;
+    border-color: var(--color-desc);
+
+    &::after {
+      width: 6px;
+      height: 6px;
+      left: 2px;
+      top: 2px;
+      margin: 0;
+      transform: none;
+    }
+  }
+  .t-radio__label {
+    font-size: 12px;
+    color: var(--color-desc);
+  }
+
+  .t-checkbox__input {
+    width: 14px;
+    height: 14px;
+    border-color: var(--color-desc);
+
+    &::after {
+      left: 2.5px !important;
+      top: 5.5px !important;
+    }
+  }
+
+  .t-button {
+    height: 24px;
+    border-color: var(--color-desc);
+    &:hover {
+      border-color: var(--color-primary);
+      color: var(--color-primary);
+    }
+  }
+}

+ 393 - 0
src/styles/tdesign.css

@@ -0,0 +1,393 @@
+/* 字体图标和TDesign 命名冲突 */
+.t-message__list div.t-message:before {
+  content: none;
+}
+
+.t-input {
+  font-size: 13px;
+
+  &:hover,
+  &.t-is-focused {
+    border-color: var(--color-border-input);
+  }
+
+  &.t-is-error {
+    box-shadow: none;
+    border-color: var(--color-error);
+  }
+
+  &.t-input--prefix > .t-input__prefix {
+    font-size: 12px;
+    color: var(--color-desc);
+  }
+
+  .t-input__inner::-webkit-input-placeholder {
+    color: transparent;
+  }
+}
+
+.t-select > .t-tag:before {
+  content: none;
+}
+
+.t-input--focused,
+.t-textarea__inner:focus {
+  box-shadow: none;
+}
+
+.t-select {
+  &.t-is-active:not(.t-is-disabled):not(.t-no-border) {
+    box-shadow: none;
+  }
+}
+
+.t-select-option {
+  &.button {
+    background: none;
+    color: var(--color);
+  }
+
+  & > * {
+    width: 100%;
+  }
+
+  &.t-is-selected {
+    background-color: var(--color-background-popup-hover);
+    color: var(--color);
+  }
+}
+
+.t-tree {
+  min-width: 100%;
+  width: max-content;
+
+  .t-tree__item {
+    line-height: 30px;
+    height: 30px;
+    padding-left: calc(var(--td-comp-margin-xxl) * (var(--level) + 0.4));
+
+    &:hover {
+      background-color: var(--color-background);
+    }
+
+    .t-tree__label {
+      margin-left: 0;
+    }
+
+    &.t-is-active {
+      background-color: var(--color-background);
+      .t-tree__label {
+        background: none;
+        color: var(--color-primary);
+      }
+    }
+  }
+
+  .t-tree__icon:not(:empty):hover {
+    i {
+      color: var(--color-primary-hover);
+    }
+
+    &::after {
+      background: none;
+    }
+  }
+}
+
+.t-tree__empty {
+  font-size: 12px;
+  margin-left: 16px;
+}
+
+.t-tree__item--open .t-icon {
+  color: var(--color);
+}
+
+.t-color-picker__format-mode-select .t-select,
+.t-color-picker__format-mode-select .t-input {
+  background-color: var(--color-background-popup);
+  border-color: var(--color-border-input);
+
+  .t-fake-arrow {
+    color: var(--color);
+  }
+}
+
+.t-color-picker__format--item .input-group__item .t-input {
+  background-color: var(--color-background-popup);
+  border-color: var(--color-border-input);
+  &:hover {
+    border-color: var(--color-primary);
+  }
+}
+
+.t-color-picker__select-options > .t-popup__content {
+  box-shadow: none;
+  border: 1px solid var(--color-border-input);
+}
+
+.t-dropdown {
+  border-color: transparent;
+}
+
+.t-dropdown__menu-column {
+  padding: 4px 0;
+}
+
+.t-dropdown__menu {
+  padding: 10px 4px;
+  background-color: var(--color-background-popup);
+  border: none;
+
+  .t-dropdown__item {
+    padding: 0;
+    background-color: transparent;
+
+    .t-dropdown__item-text {
+      display: block;
+      padding: 0 16px;
+      line-height: 32px;
+      border-radius: 4px;
+
+      &:hover {
+        background-color: var(--color-background-popup-hover);
+      }
+
+      a {
+        display: block;
+        text-decoration: none;
+        color: var(--color);
+
+        label {
+          font-size: 10px;
+          background-color: #ff400030;
+          color: var(--color-bland);
+          padding: 0 6px;
+          margin-left: 4px;
+          border-radius: 2px;
+        }
+      }
+    }
+  }
+
+  .t-divider {
+    margin: 4px 0;
+    width: 100%;
+  }
+}
+
+.t-popup__content {
+  padding: 0 !important;
+  margin-top: 0 !important;
+  border-radius: var(--border-radius);
+  box-shadow: none;
+  background-color: var(--color-background-popup);
+
+  .t-color-picker__format {
+    align-items: center;
+  }
+}
+
+.t-tooltip {
+  .t-popup__content {
+    font-size: 12px;
+    padding: 2px 8px !important;
+  }
+}
+
+.t-date-range-picker {
+  width: 240px;
+}
+
+.t-table {
+  font-size: 13px;
+
+  th {
+    background: #f7f8fa;
+    color: #7f838c;
+    border-color: #edeef0;
+  }
+
+  td {
+    color: #474e59;
+    border-color: #edeef0;
+  }
+
+  &.border-none {
+    th {
+      background: none !important;
+      border: none;
+    }
+    td {
+      border: none;
+    }
+
+    .t-table__row--selected {
+      background-color: var(--color-primary-background);
+    }
+  }
+
+  &.hide-checkbox {
+    .t-table__cell-check {
+      * {
+        display: none;
+      }
+    }
+  }
+}
+
+.t-alert__description {
+  color: #002da0;
+}
+
+.t-button--variant-outline {
+  background-color: transparent !important;
+}
+
+.input-search {
+  position: relative;
+  display: flex;
+  align-items: center;
+  padding: 4px 16px;
+
+  .t-input {
+    padding-left: 32px;
+    outline: none;
+    border: none;
+    background: var(--color-background-input);
+    z-index: 0;
+    height: 28px;
+    input {
+      color: var(--color);
+      &::placeholder {
+        color: var(--color-gray);
+      }
+    }
+  }
+
+  .btn {
+    position: absolute;
+    left: 28px;
+    height: 32px;
+    line-height: 30px;
+    z-index: 1;
+  }
+}
+
+.t-tag {
+  display: inline-block;
+}
+
+.t-tab-panel {
+  height: calc(100vh - 81px);
+  overflow-y: auto;
+}
+
+.t-tabs__nav-item-wrapper > div {
+  display: none;
+}
+
+.t-tabs__nav-item {
+  color: var(--color);
+
+  &:not(.t-is-disabled):not(.t-is-active):hover .t-tabs__nav-item-wrapper {
+    background: none;
+    color: var(--color-primary);
+  }
+}
+
+.t-color-picker__trigger {
+  height: 30px;
+
+  .t-input {
+    height: 30px;
+    border: 1px solid var(--color-border-input);
+    padding: 0;
+    background: none;
+
+    & > .t-input__prefix {
+      margin: 8px;
+      width: 16px;
+      height: 16px;
+      border: 1px solid var(--color-border-input);
+      border-radius: 2px;
+
+      span {
+        width: 16px;
+        height: 16px;
+        border: 1px solid var(--color-border-input);
+      }
+    }
+  }
+
+  &.w-full {
+    .t-input--auto-width {
+      min-width: 100%;
+    }
+  }
+
+  &.simple {
+    width: 30px;
+
+    .t-input--auto-width {
+      min-width: 20px;
+      width: 20px;
+    }
+
+    .t-input {
+      height: 30px;
+      border: none;
+      padding: 0;
+      background: none;
+
+      & > .t-input__prefix {
+        width: 20px;
+        height: 20px;
+        border: 1px solid var(--color-border-input);
+        border-radius: 2px;
+
+        span {
+          height: 18px;
+        }
+      }
+
+      input {
+        display: none;
+      }
+    }
+  }
+}
+
+.rectangle.t-radio-group.t-radio-group--filled {
+  background: none;
+
+  .t-radio-group__bg-block {
+    display: none;
+  }
+
+  label {
+    width: 30px !important;
+    margin-right: 12px;
+    padding: 0;
+    background-color: var(--color-background-popup);
+    border-radius: 4px;
+    &::before {
+      content: none;
+    }
+
+    .t-radio-button__label {
+      width: 100%;
+      height: 100%;
+
+      svg {
+        width: 100%;
+        height: 100%;
+        padding: 4px;
+      }
+    }
+
+    &.t-is-checked {
+      background-color: var(--color-primary);
+    }
+  }
+}

+ 62 - 0
src/styles/var.css

@@ -0,0 +1,62 @@
+:root {
+  --font-family: '-apple-system', 'BlinkMacSystemFont', 'PingFang SC',
+    'Hiragino Sans GB', 'Microsoft YaHei UI', ' Microsoft YaHei',
+    'Source Han Sans CN', 'sans-serif', 'Apple Color Emoji', 'Segoe UI Emoji',
+    'Segoe UI Symbol';
+  --font-size: 13px;
+  --color: #bdc7db;
+  --color-title: #e3e8f4;
+  --color-desc: #617b91;
+  --color-gray: #4f5b75;
+
+  --color-bland: #ff4000;
+  --color-bland-hover: #fa541c;
+
+  --color-primary: #4583ff;
+  --color-primary-hover: #1677ff;
+  --color-primary-disabled: #bae7ff;
+  --color-success: #52c41a;
+  --color-success-hover: #3fad09;
+  --color-success-disabled: #c6ebb4;
+  --color-warning: #faad14;
+  --color-warning-hover: #d9a116;
+  --color-warning-disabled: #fff6dd;
+  --color-error: #f50000;
+  --color-error-hover: #f00;
+  --color-error-disabled: #ffc2c5;
+
+  --color-background: #1e2430;
+  --color-background-active: #161f2c;
+  --color-background-hover: #181f29;
+  --color-background-input: #303746;
+  --color-background-editor: #0f151f;
+  --color-background-popup: #303746;
+  --color-background-popup-hover: #454f64;
+
+  --color-border: #000000;
+  --color-sub-border: #f7f7f7;
+  --color-border-input: #535f79;
+
+  --color-scrollbar: #dad7d7;
+  --color-scrollbar-hover: #e5e5e5;
+
+  --border-radius: 4px;
+
+  --shadow: 0px 2px 6px 0px rgba(0, 10, 38, 0.1);
+  --shadow-hover: 0px 4px 8px 0px rgba(0, 10, 38, 0.3);
+  --shadow-panel: 0px 2px 6px 0px rgb(0 10 38 / 4%);
+
+  --td-radius-medium: 4px;
+  --td-bg-color-container: #1e2430;
+  --td-component-border: #42516c;
+  --td-bg-color-secondarycontainer: #42516c;
+  --td-comp-size-xxl: 40px;
+  --td-text-color-primary: var(--color);
+  --td-text-color-secondary: var(--color-desc);
+  --td-bg-color-container-hover: var(--color-background-hover);
+  --td-font-family: var(--font-family);
+  --td-font-body-medium: var(--font-size) / var(--td-line-height-body-medium)
+    var(--td-font-family);
+  --td-border-level-1-color: var(--color-background-popup-hover);
+  --td-text-color-placeholder: var(--color-desc);
+}

+ 48 - 0
src/views/Index.vue

@@ -0,0 +1,48 @@
+<template>
+  <div class="page-app">
+    <Header />
+
+    <div class="design-body">
+      <Graphics />
+      <View />
+      <div>
+        <FileProps v-if="selections.mode === SelectionMode.File" />
+        <PenProps v-else-if="selections.mode === SelectionMode.Pen" />
+        <PensProps v-else />
+      </div>
+    </div>
+  </div>
+</template>
+
+<script lang="ts" setup>
+import { onBeforeMount, reactive } from 'vue';
+import axios from 'axios';
+
+import Header from './components/Header.vue';
+import Graphics from './components/Graphics.vue';
+import View from './components/View.vue';
+import FileProps from './components/FileProps.vue';
+import PenProps from './components/PenProps.vue';
+import PensProps from './components/PensProps.vue';
+
+import { useSelection, SelectionMode } from '@/services/selections';
+
+const { selections } = useSelection();
+
+const data = reactive<any>({});
+
+onBeforeMount(async () => {});
+</script>
+
+<style lang="postcss" scoped>
+.page-app {
+  height: 100vh;
+
+  .design-body {
+    display: grid;
+    grid-template-columns: 300px 1fr 300px;
+    height: calc(100vh - 40px);
+    border-top: 1px solid var(--color-border);
+  }
+}
+</style>

+ 87 - 0
src/views/components/ElementTree.vue

@@ -0,0 +1,87 @@
+<template>
+  <div class="elements">
+    <t-tree
+      class="flex-grow"
+      ref="tree"
+      :data="data.tree"
+      v-model:actived="data.actived"
+      activable
+      :expand-parent="true"
+      @active="onActive"
+      style="margin: 8px 0"
+    >
+      <template #icon="{ node }">
+        <template v-if="!node.data.leaf">
+          <t-icon v-if="node.expanded" name="chevron-down" />
+          <t-icon v-else name="chevron-right" />
+        </template>
+      </template>
+      <template #label="{ node }">
+        <div class="flex middle">
+          {{ node.label }}
+        </div>
+      </template>
+    </t-tree>
+    <div class="groups">
+      <div class="flex middle between">
+        <div class="title" style="margin-bottom: 8px">图层分组</div>
+        <a> +新建分组</a>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script lang="ts" setup>
+import { onBeforeMount, reactive, ref } from 'vue';
+
+const tree = ref<any>(null);
+const data = reactive<any>({
+  tree: [
+    {
+      label: '图元1',
+      children: [
+        {
+          label: '图元1.1',
+        },
+        {
+          label: '图元1.2',
+        },
+      ],
+    },
+    {
+      label: '图元2',
+      children: [
+        {
+          label: '图元2.1',
+        },
+        {
+          label: '图元2.2',
+        },
+      ],
+    },
+  ],
+  actived: [],
+});
+
+onBeforeMount(() => {});
+
+const onActive = (value: string[]) => {};
+</script>
+<style lang="postcss" scoped>
+.elements {
+  display: flex;
+  flex-direction: column;
+  height: 100%;
+
+  & > * {
+    overflow-y: auto;
+  }
+
+  .groups {
+    flex-shrink: 0;
+    height: 240px;
+    border-top: 1px solid var(--color-border);
+    padding: 8px 12px;
+  }
+}
+</style>

+ 236 - 0
src/views/components/FileProps.vue

@@ -0,0 +1,236 @@
+<template>
+  <div class="props">
+    <t-tabs v-model="data.tab">
+      <t-tab-panel :value="1" label="画布">
+        <t-space direction="vertical" class="panel">
+          <div class="form-item">
+            <label>画布尺寸</label>
+            <t-input-number label="W" placeholder="宽" theme="column" min="1" />
+            <t-input-number
+              class="ml-8"
+              label="H"
+              placeholder="高"
+              theme="column"
+              min="1"
+            />
+            <t-dropdown
+              :minColumnWidth="180"
+              :maxHeight="500"
+              :delay2="[10, 150]"
+              overlayClassName="header-dropdown"
+            >
+              <t-icon name="more" class="ml-8 hover" />
+              <t-dropdown-menu>
+                <t-dropdown-item>
+                  <div class="flex between">
+                    大屏
+                    <span class="desc">
+                      1920
+                      <span style="font-size: 10px; margin: 0 4px">x</span>
+                      1080
+                    </span>
+                  </div>
+                </t-dropdown-item>
+                <t-dropdown-item>
+                  <div class="flex between">
+                    网页
+                    <span class="desc">
+                      1440
+                      <span style="font-size: 10px; margin: 0 4px">x</span>
+                      1024
+                    </span>
+                  </div>
+                </t-dropdown-item>
+                <t-dropdown-item>
+                  <div class="flex between">
+                    平板12.9"
+                    <span class="desc">
+                      1024
+                      <span style="font-size: 10px; margin: 0 4px">x</span>
+                      1366
+                    </span>
+                  </div>
+                </t-dropdown-item>
+                <t-dropdown-item>
+                  <div class="flex between">
+                    平板11"
+                    <span class="desc">
+                      834
+                      <span style="font-size: 10px; margin: 0 4px">x</span>
+                      1194
+                    </span>
+                  </div>
+                </t-dropdown-item>
+                <t-dropdown-item>
+                  <div class="flex between">
+                    平板Mini
+                    <span class="desc">
+                      768
+                      <span style="font-size: 10px; margin: 0 4px">x</span>
+                      1024
+                    </span>
+                  </div>
+                </t-dropdown-item>
+                <t-dropdown-item>
+                  <div class="flex between">
+                    华为P8
+                    <span class="desc">
+                      360
+                      <span style="font-size: 10px; margin: 0 4px">x</span>
+                      640
+                    </span>
+                  </div>
+                </t-dropdown-item>
+                <t-dropdown-item>
+                  <div class="flex between">
+                    华为P40
+                    <span class="desc">
+                      395
+                      <span style="font-size: 10px; margin: 0 4px">x</span>
+                      856
+                    </span>
+                  </div>
+                </t-dropdown-item>
+                <t-dropdown-item>
+                  <div class="flex between">
+                    手机1
+                    <span class="desc">
+                      430
+                      <span style="font-size: 10px; margin: 0 4px">x</span>
+                      932
+                    </span>
+                  </div>
+                </t-dropdown-item>
+                <t-dropdown-item>
+                  <div class="flex between">
+                    手机2
+                    <span class="desc">
+                      375
+                      <span style="font-size: 10px; margin: 0 4px">x</span>
+                      812
+                    </span>
+                  </div>
+                </t-dropdown-item>
+              </t-dropdown-menu>
+            </t-dropdown>
+          </div>
+
+          <div class="form-item">
+            <label>背景颜色</label>
+            <t-color-picker
+              class="w-full"
+              format="CSS"
+              :color-modes="['monochrome']"
+              :show-primary-color-preview="false"
+            />
+          </div>
+          <div class="form-item">
+            <label>背景图片</label>
+            <t-upload
+              class="ml-8"
+              v-model="data.files"
+              action="/api/image/upload"
+              theme="image"
+              accept="image/*"
+              :headers="headers"
+              :data="updataData"
+              @success="fileSuccessed"
+            />
+          </div>
+        </t-space>
+        <t-space direction="vertical" class="mt-8">
+          <t-collapse
+            :defaultValue="['1']"
+            expandIconPlacement="right"
+            :borderless="true"
+          >
+            <t-collapse-panel value="1" header="预览设置">
+              <t-space direction="vertical">
+                <div class="form-item">
+                  <label>缩放方式</label>
+                  <t-radio-group class="ml-8" default-value="1">
+                    <t-radio value="1" title="宽高中较小的铺满"
+                      >自动铺满</t-radio
+                    >
+                    <t-radio value="2" title="高度等比例缩放">宽度铺满</t-radio>
+                    <t-radio value="3" title="宽度等比例缩放">高度铺满</t-radio>
+                  </t-radio-group>
+                </div>
+                <div class="form-item">
+                  <label>显示滚动条</label>
+                  <t-checkbox v-model="data.scroll" class="ml-8" />
+                </div>
+                <template v-if="data.scroll">
+                  <div class="form-item">
+                    <label>水平对齐</label>
+                    <t-radio-group class="ml-8" default-value="2">
+                      <t-radio value="1">左对齐</t-radio>
+                      <t-radio value="2">居中</t-radio>
+                      <t-radio value="3">右对齐</t-radio>
+                    </t-radio-group>
+                  </div>
+                  <div class="form-item">
+                    <label>垂直对齐</label>
+                    <t-radio-group class="ml-8" default-value="2">
+                      <t-radio value="1">顶部对齐</t-radio>
+                      <t-radio value="2">居中</t-radio>
+                      <t-radio value="3">底部对齐</t-radio>
+                    </t-radio-group>
+                  </div>
+                </template>
+              </t-space>
+            </t-collapse-panel>
+            <t-collapse-panel value="2" header="进阶设置">
+              <t-space direction="vertical">
+                <div class="form-item">
+                  <label>初始化动作</label>
+                  <t-button
+                    variant="outline"
+                    style="padding: 0 6px; margin: 2px 8px"
+                  >
+                    <t-icon name="ellipsis" />
+                  </t-button>
+                </div>
+                <div class="form-item">
+                  <label>数据格式转换</label>
+                  <t-button
+                    variant="outline"
+                    style="padding: 0 6px; margin: 2px 8px"
+                  >
+                    <t-icon name="ellipsis" />
+                  </t-button>
+                </div>
+              </t-space>
+            </t-collapse-panel>
+          </t-collapse>
+        </t-space>
+      </t-tab-panel>
+      <t-tab-panel :value="2" label="结构">
+        <ElementTree />
+      </t-tab-panel>
+    </t-tabs>
+  </div>
+</template>
+
+<script lang="ts" setup>
+import { onBeforeMount, reactive } from 'vue';
+import axios from 'axios';
+import { getCookie } from '@/services/cookie';
+
+import ElementTree from './ElementTree.vue';
+
+const headers = {
+  Authorization: 'Bearer ' + (localStorage.token || getCookie('token') || ''),
+};
+const updataData = { directory: '/项目' };
+
+const data = reactive<any>({
+  tab: 1,
+});
+
+const fileSuccessed = async () => {};
+</script>
+<style lang="postcss" scoped>
+.props {
+}
+</style>

+ 96 - 0
src/views/components/Graphics.vue

@@ -0,0 +1,96 @@
+<template>
+  <div class="graphics">
+    <div class="input-search">
+      <div class="btn">
+        <t-icon name="search" />
+      </div>
+      <t-input placeholder="搜索" />
+    </div>
+    <div class="groups">
+      <div class="sub-groups">
+        <div class="active">
+          <t-icon name="desktop" />
+          场景
+        </div>
+        <div>
+          <t-icon name="root-list" />
+          模板
+        </div>
+        <div>
+          <t-icon name="chart" />
+          图表
+        </div>
+        <div>
+          <t-icon name="control-platform" />
+          控件
+        </div>
+        <div>
+          <t-icon name="file-icon" />
+          图标
+        </div>
+        <div>
+          <t-icon name="chart-bubble" />
+          图形
+        </div>
+        <div>
+          <t-icon name="app" />
+          我的
+        </div>
+      </div>
+      <div class="list"></div>
+    </div>
+  </div>
+</template>
+
+<script lang="ts" setup></script>
+<style lang="postcss" scoped>
+.graphics {
+  display: flex;
+  flex-direction: column;
+  border-right: 1px solid var(--color-border);
+
+  .input-search {
+    flex-shrink: 0;
+    height: 40px;
+  }
+
+  .groups {
+    display: grid;
+    grid-template-columns: 50px 1fr;
+    border-top: 1px solid var(--color-border);
+    flex-grow: 1;
+    overflow-y: auto;
+    font-size: 12px;
+
+    .sub-groups {
+      & > div {
+        display: flex;
+        flex-direction: column;
+        align-items: center;
+        padding: 16px 4px;
+        line-height: 1;
+        cursor: pointer;
+
+        .t-icon {
+          font-size: 20px;
+          margin-bottom: 5px;
+        }
+
+        &:hover {
+          color: var(--color-primary);
+        }
+      }
+
+      .active {
+        background-color: var(--color-background-active);
+        color: var(--color-primary);
+        border-left: 2px solid var(--color-primary);
+      }
+    }
+
+    .list {
+      background-color: var(--color-background-active);
+    }
+  }
+}
+</style>

+ 325 - 0
src/views/components/Header.vue

@@ -0,0 +1,325 @@
+<template>
+  <div class="app-header">
+    <a class="logo" href="https://le5le.com" target="_blank">
+      <img src="favicon.ico" />
+      <span>乐吾乐</span>
+    </a>
+    <t-dropdown
+      :minColumnWidth="200"
+      :maxHeight="560"
+      :delay2="[10, 150]"
+      overlayClassName="header-dropdown"
+      trigger="click"
+    >
+      <a> 文件 </a>
+      <t-dropdown-menu>
+        <t-dropdown-item>
+          <a>新建文件</a>
+        </t-dropdown-item>
+        <t-dropdown-item>
+          <a>打开文件</a>
+        </t-dropdown-item>
+        <t-dropdown-item divider="true">
+          <a>导入文件</a>
+        </t-dropdown-item>
+        <t-dropdown-item>
+          <a>保存</a>
+        </t-dropdown-item>
+        <t-dropdown-item>
+          <a>另保存</a>
+        </t-dropdown-item>
+        <t-dropdown-item divider="true">
+          <a>下载JSON文件</a>
+        </t-dropdown-item>
+        <t-dropdown-item>
+          <a>
+            <div class="flex">
+              导出为ZIP文件 <span class="flex-grow"></span>
+              <span><label>VIP</label></span>
+            </div>
+          </a>
+        </t-dropdown-item>
+        <t-dropdown-item>
+          <a>
+            <div class="flex">
+              导出为HTML <span class="flex-grow"></span>
+              <span><label>VIP</label></span>
+            </div>
+          </a>
+        </t-dropdown-item>
+        <t-dropdown-item>
+          <a>
+            <div class="flex">
+              导出为Vue3组件 <span class="flex-grow"></span>
+              <span><label>VIP</label></span>
+            </div>
+          </a>
+        </t-dropdown-item>
+        <t-dropdown-item>
+          <a>
+            <div class="flex">
+              导出为Vue2组件 <span class="flex-grow"></span>
+              <span><label>VIP</label></span>
+            </div>
+          </a>
+        </t-dropdown-item>
+        <t-dropdown-item divider="true">
+          <a>
+            <div class="flex">
+              导出为React组件 <span class="flex-grow"></span>
+              <span><label>VIP</label></span>
+            </div>
+          </a>
+        </t-dropdown-item>
+        <t-dropdown-item>
+          <a>下载为PNG</a>
+        </t-dropdown-item>
+        <t-dropdown-item>
+          <a>下载为SVG</a>
+        </t-dropdown-item>
+      </t-dropdown-menu>
+    </t-dropdown>
+    <t-dropdown
+      :minColumnWidth="180"
+      :maxHeight="500"
+      :delay2="[10, 150]"
+      overlayClassName="header-dropdown"
+    >
+      <a> 编辑 </a>
+      <t-dropdown-menu>
+        <t-dropdown-item>
+          <a>
+            <div class="flex">
+              撤销 <span class="flex-grow"></span> Ctrl + Z
+            </div>
+          </a>
+        </t-dropdown-item>
+        <t-dropdown-item divider="true">
+          <a>
+            <div class="flex">
+              恢复 <span class="flex-grow"></span> Ctrl + Y
+            </div>
+          </a>
+        </t-dropdown-item>
+        <t-dropdown-item>
+          <a>
+            <div class="flex">
+              剪切 <span class="flex-grow"></span> Ctrl + X
+            </div>
+          </a>
+        </t-dropdown-item>
+        <t-dropdown-item>
+          <a>
+            <div class="flex">
+              复制 <span class="flex-grow"></span> Ctrl + C
+            </div>
+          </a>
+        </t-dropdown-item>
+        <t-dropdown-item divider="true">
+          <a>
+            <div class="flex">
+              粘贴 <span class="flex-grow"></span> Ctrl + V
+            </div>
+          </a>
+        </t-dropdown-item>
+        <t-dropdown-item>
+          <a>
+            <div class="flex">
+              添加/删除锚点 <span class="flex-grow"></span> A
+            </div>
+          </a>
+        </t-dropdown-item>
+        <t-dropdown-item>
+          <a>
+            <div class="flex">添加手柄 <span class="flex-grow"></span> H</div>
+          </a>
+        </t-dropdown-item>
+        <t-dropdown-item>
+          <a>
+            <div class="flex">删除手柄 <span class="flex-grow"></span> D</div>
+          </a>
+        </t-dropdown-item>
+        <t-dropdown-item>
+          <a>
+            <div class="flex">
+              切换手柄 <span class="flex-grow"></span> Shift
+            </div>
+          </a>
+        </t-dropdown-item>
+      </t-dropdown-menu>
+    </t-dropdown>
+    <t-dropdown
+      :minColumnWidth="180"
+      :maxHeight="500"
+      :delay2="[10, 150]"
+      overlayClassName="header-dropdown"
+    >
+      <a> 工具 </a>
+      <t-dropdown-menu>
+        <t-dropdown-item>
+          <a>窗口大小</a>
+        </t-dropdown-item>
+        <t-dropdown-item>
+          <a>放大</a>
+        </t-dropdown-item>
+        <t-dropdown-item>
+          <a>缩小</a>
+        </t-dropdown-item>
+        <t-dropdown-item divider="true">
+          <a>100%视图</a>
+        </t-dropdown-item>
+        <t-dropdown-item>
+          <a>鹰眼地图</a>
+        </t-dropdown-item>
+        <t-dropdown-item divider="true">
+          <a>放大镜</a>
+        </t-dropdown-item>
+        <t-dropdown-item>
+          <a>
+            <div class="flex middle">
+              自动锚点 <span class="flex-grow"></span> <t-icon name="check" />
+            </div>
+          </a>
+        </t-dropdown-item>
+        <t-dropdown-item divider="true">
+          <a>
+            <div class="flex middle">
+              禁用锚点 <span class="flex-grow"></span> <t-icon name="check" />
+            </div>
+          </a>
+        </t-dropdown-item>
+        <t-dropdown-item>
+          <a>明亮主题</a>
+        </t-dropdown-item>
+        <t-dropdown-item>
+          <a>暗黑主题</a>
+        </t-dropdown-item>
+      </t-dropdown-menu>
+    </t-dropdown>
+    <t-dropdown
+      :minColumnWidth="180"
+      :maxHeight="500"
+      :delay2="[10, 150]"
+      overlayClassName="header-dropdown"
+    >
+      <a> 帮助 </a>
+      <t-dropdown-menu>
+        <t-dropdown-item>
+          <a>产品介绍</a>
+        </t-dropdown-item>
+        <t-dropdown-item>
+          <a>快速上手</a>
+        </t-dropdown-item>
+        <t-dropdown-item>
+          <a>使用手册</a>
+        </t-dropdown-item>
+        <t-dropdown-item divider="true">
+          <a>快捷键</a>
+        </t-dropdown-item>
+        <t-dropdown-item divider="true">
+          <a>企业服务与支持</a>
+        </t-dropdown-item>
+        <t-dropdown-item>
+          <a>关于我们</a>
+        </t-dropdown-item>
+      </t-dropdown-menu>
+    </t-dropdown>
+    <input v-model="data.name" />
+
+    <div style="width: 290px; flex-shrink: 0"></div>
+    <t-dropdown
+      v-if="user.id"
+      :minColumnWidth="200"
+      :maxHeight="500"
+      :delay2="[10, 150]"
+      overlayClassName="custom-dropdown header"
+    >
+      <a style="margin-left: 32px; margin-right: 0">
+        <t-avatar
+          size="small"
+          :image="user.avatarUrl ? user.avatarUrl : baseUrl + 'img/avatar.png'"
+        />
+      </a>
+      <t-dropdown-menu>
+        <t-dropdown-item>
+          <router-link to="/account/info">
+            {{ user.username }}
+          </router-link>
+        </t-dropdown-item>
+        <t-dropdown-item>
+          <router-link to="/account/info">我的图纸</router-link>
+        </t-dropdown-item>
+        <t-dropdown-item>
+          <router-link to="/account/teams">我的团队</router-link>
+        </t-dropdown-item>
+        <t-dropdown-item>
+          <router-link to="/account/info">账号信息</router-link>
+        </t-dropdown-item>
+        <t-dropdown-item :divider="true">
+          <router-link to="/account/security"> 安全设置 </router-link>
+        </t-dropdown-item>
+        <t-dropdown-item>
+          <a @click="signout">退出</a>
+        </t-dropdown-item>
+      </t-dropdown-menu>
+    </t-dropdown>
+    <div class="flex middle" v-else>
+      <a class="button primary solid" style="width: 80px">登录</a>
+    </div>
+  </div>
+</template>
+
+<script lang="ts" setup>
+import { reactive } from 'vue';
+import { useUser } from '@/services/user';
+
+const baseUrl = import.meta.env.BASE_URL || '/';
+
+const { user, message, getUser, getMessage, signout } = useUser();
+
+const data = reactive({
+  name: '空白文件',
+});
+</script>
+<style lang="postcss" scoped>
+.app-header {
+  display: flex;
+  height: 40px;
+
+  .logo {
+    display: flex;
+    padding: 0 16px;
+    align-items: center;
+    font-size: 14px;
+    font-weight: 500;
+
+    img {
+      height: 20px;
+      margin-right: 6px;
+    }
+  }
+
+  a {
+    display: flex;
+    padding: 0 8px;
+    margin: 0 8px;
+    align-items: center;
+    color: var(--color);
+    text-decoration: none;
+
+    &:hover {
+      color: var(--color-primary);
+    }
+  }
+
+  input {
+    font-size: var(--font-size);
+    flex-grow: 1;
+    background: none;
+    outline: none;
+    border: none;
+    text-align: center;
+    color: var(--color-title);
+  }
+}
+</style>

+ 9 - 0
src/views/components/PenProps.vue

@@ -0,0 +1,9 @@
+<template>
+  <div class="props"></div>
+</template>
+
+<script lang="ts" setup></script>
+<style lang="postcss" scoped>
+.props {
+}
+</style>

+ 9 - 0
src/views/components/PensProps.vue

@@ -0,0 +1,9 @@
+<template>
+  <div class="props"></div>
+</template>
+
+<script lang="ts" setup></script>
+<style lang="postcss" scoped>
+.props {
+}
+</style>

+ 141 - 0
src/views/components/View.vue

@@ -0,0 +1,141 @@
+<template>
+  <div class="meta2d">
+    <div class="tools">
+      <t-tooltip content="新建" placement="bottom">
+        <a><t-icon name="add" /></a>
+      </t-tooltip>
+      <t-tooltip content="保存" placement="bottom">
+        <a> <t-icon name="save" /></a>
+      </t-tooltip>
+      <t-tooltip content="保存为模板" placement="bottom">
+        <a><t-icon name="layers" /></a>
+      </t-tooltip>
+      <t-tooltip content="格式化" placement="bottom">
+        <a>
+          <svg
+            width="1em"
+            height="1em"
+            viewBox="0 0 256 256"
+            xmlns="http://www.w3.org/2000/svg"
+            xmlns:xlink="http://www.w3.org/1999/xlink"
+          >
+            <defs><path id="77456364" d="M0 0h256v256H0z"></path></defs>
+            <g fill="none" fill-rule="evenodd">
+              <mask id="3804093554b" fill="#fff">
+                <use xlink:href="#77456364"></use>
+              </mask>
+              <path
+                d="M213 77c0 14.36-11.64 26-26 26H69c-14.36 0-26-11.64-26-26V51c0-14.36 11.64-26 26-26h118c14.36 0 26 11.64 26 26v2h9.125c9.83 0 17.82 7.88 17.997 17.67l.003.33v50c0 9.83-7.88 17.82-17.67 17.997l-.33.003h-84.626v18H139c9.83 0 17.82 7.88 17.997 17.67l.003.33v35c0 16.016-12.984 29-29 29-15.856 0-28.74-12.725-28.996-28.52L99 210v-35c0-9.83 7.88-17.82 17.67-17.997L117 157h.499l.001-20c0-9.83 7.88-17.82 17.67-17.997l.33-.003h84.625V73H213Zm-76 100h-18v33a9 9 0 0 0 8.471 8.985l.264.011.265.004a9 9 0 0 0 8.996-8.735L137 210v-33Zm50-132H69a6 6 0 0 0-6 6v26a6 6 0 0 0 6 6h118a6 6 0 0 0 6-6V51a6 6 0 0 0-6-6Z"
+                fill="currentColor"
+                fill-rule="nonzero"
+                mask="url(#3804093554b)"
+              ></path>
+            </g>
+          </svg>
+        </a>
+      </t-tooltip>
+      <t-tooltip content="清除格式" placement="bottom">
+        <a>
+          <svg
+            width="1em"
+            height="1em"
+            viewBox="0 0 1024 1024"
+            xmlns="http://www.w3.org/2000/svg"
+          >
+            <path
+              d="M889.186 384.07 677.671 172.56c-53.063-53.063-139.094-53.063-192.157 0L134.617 523.457c-53.063 53.063-53.063 139.099 0 192.158l170.196 170.2a41.354 41.354 0 0 0 29.243 12.11h215.001a41.354 41.354 0 0 0 29.184-12.05L889.155 576.26c53.094-53.09 53.094-139.126.031-192.19zM830.7 442.558c20.48 20.472 20.764 53.492.855 74.319l-.961.984-298.618 297.358H351.185l-158.09-158.086c-20.76-20.763-20.76-54.43 0-75.193l350.901-350.897c20.764-20.764 54.43-20.764 75.193 0l211.515 211.511z"
+              fill="currentColor"
+            ></path>
+            <path
+              d="m685.505 678.754-58.19 58.77-317.587-314.43 58.191-58.774zm197.55 136.508c23.46 0 42.483 18.514 42.483 41.353 0 22.45-18.38 40.724-41.294 41.338l-1.19.016h-454.6c-23.462 0-42.485-18.514-42.485-41.354 0-22.449 18.381-40.723 41.295-41.338l1.19-.015h454.6z"
+              fill="currentColor"
+            ></path>
+          </svg>
+        </a>
+      </t-tooltip>
+      <div class="flex-grow"></div>
+
+      <t-tooltip content="直线" placement="bottom">
+        <a><t-icon name="slash" /></a>
+      </t-tooltip>
+      <t-tooltip content="文字" placement="bottom">
+        <a>T</a>
+      </t-tooltip>
+      <t-tooltip content="图片" placement="bottom">
+        <a><t-icon name="image" /></a>
+      </t-tooltip>
+      <t-tooltip content="视图大小" placement="bottom">
+        <div style="line-height: 40px; margin-left: 8px">100%</div>
+      </t-tooltip>
+
+      <t-tooltip content="100%视图" placement="bottom">
+        <a><t-icon name="refresh" /></a>
+      </t-tooltip>
+      <t-tooltip content="窗口大小" placement="bottom">
+        <a><t-icon name="minus-rectangle" /></a>
+      </t-tooltip>
+      <t-tooltip content="数据源" placement="bottom">
+        <a><t-icon name="server" /></a>
+      </t-tooltip>
+
+      <div class="flex-grow"></div>
+      <t-tooltip content="预览" placement="bottom">
+        <a><t-icon name="browse" /></a>
+      </t-tooltip>
+      <t-tooltip content="运行" placement="bottom">
+        <a><t-icon name="caret-right" /></a>
+      </t-tooltip>
+      <t-tooltip content="手机查看" placement="bottom">
+        <a><t-icon name="qrcode" /></a>
+      </t-tooltip>
+      <t-tooltip content="分享" placement="bottom">
+        <a><t-icon name="share" /></a>
+      </t-tooltip>
+      <t-tooltip content="发布" placement="bottom">
+        <a><t-icon name="cloud" /></a>
+      </t-tooltip>
+    </div>
+    <div id="meta2d"></div>
+  </div>
+</template>
+
+<script lang="ts" setup></script>
+<style lang="postcss" scoped>
+.meta2d {
+  display: flex;
+  flex-direction: column;
+  background-color: var(--color-background-editor);
+  border-right: 1px solid var(--color-border);
+
+  .tools {
+    display: flex;
+    font-size: 12px;
+    background-color: var(--color-background);
+    height: 40px;
+    flex-shrink: 0;
+    padding: 0 12px;
+
+    a {
+      display: flex;
+      align-items: center;
+      height: 100%;
+      padding: 0 10px;
+      color: var(--color);
+      text-decoration: none;
+
+      &:hover {
+        color: var(--color-primary);
+      }
+    }
+
+    .t-icon {
+      font-size: 16px;
+    }
+  }
+
+  #meta2d {
+    border-top: 1px solid var(--color-border);
+    height: calc(100vh - 81px);
+  }
+}
+</style>

+ 21 - 0
tsconfig.json

@@ -0,0 +1,21 @@
+{
+  "compilerOptions": {
+    "baseUrl": ".",
+    "target": "esnext",
+    "useDefineForClassFields": true,
+    "module": "esnext",
+    "moduleResolution": "node",
+    "strict": true,
+    "jsx": "preserve",
+    "sourceMap": true,
+    "resolveJsonModule": true,
+    "esModuleInterop": true,
+    "skipLibCheck": true,
+    "lib": ["esnext", "dom"],
+    "paths": {
+      "@/*": ["src/*"],
+    },
+  },
+  "include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"],
+  "references": [{ "path": "./tsconfig.node.json" }]
+}

+ 8 - 0
tsconfig.node.json

@@ -0,0 +1,8 @@
+{
+  "compilerOptions": {
+    "composite": true,
+    "module": "esnext",
+    "moduleResolution": "node"
+  },
+  "include": ["vite.config.ts"]
+}

+ 19 - 0
vite.config.ts

@@ -0,0 +1,19 @@
+import { defineConfig } from 'vite';
+import vue from '@vitejs/plugin-vue';
+import vueJsx from '@vitejs/plugin-vue-jsx';
+import * as path from 'path';
+
+// https://vitejs.dev/config/
+export default defineConfig({
+  plugins: [vue(), vueJsx()],
+  resolve: {
+    alias: { '@': path.resolve(__dirname, './src/') },
+  },
+  server: {
+    proxy: {
+      '/image': 'http://127.0.0.1:777/',
+      '/file': 'http://127.0.0.1:777/',
+      '/api': 'http://127.0.0.1:777/',
+    },
+  },
+});