李志伟 пре 1 месец
комит
db03afe701
58 измењених фајлова са 14640 додато и 0 уклоњено
  1. 51 0
      .eslintrc.js
  2. 24 0
      .gitignore
  3. 2 0
      .npmrc
  4. 23 0
      index.html
  5. 42 0
      package.json
  6. 3912 0
      pnpm-lock.yaml
  7. 38 0
      prettier.js
  8. 1 0
      public/vite.svg
  9. 12 0
      src/ libs/api.request.ts
  10. 62 0
      src/ libs/axios.ts
  11. 160 0
      src/ libs/file.ts
  12. 376 0
      src/ libs/tools.js
  13. 1093 0
      src/ libs/util.js
  14. 48 0
      src/App.vue
  15. 82 0
      src/api/module/file.js
  16. 120 0
      src/comm_js/index.js
  17. 48 0
      src/components/file_detail/index.less
  18. 318 0
      src/components/file_detail/index.vue
  19. 35 0
      src/components/view_file/assets/excel_icon.svg
  20. 34 0
      src/components/view_file/assets/exe_icon.svg
  21. 54 0
      src/components/view_file/assets/folder_icon.svg
  22. 13 0
      src/components/view_file/assets/image_icon.svg
  23. 29 0
      src/components/view_file/assets/other_icon.svg
  24. 46 0
      src/components/view_file/assets/pdf_icon.svg
  25. 35 0
      src/components/view_file/assets/ppt_icon.svg
  26. 11 0
      src/components/view_file/assets/video_icon.svg
  27. 1 0
      src/components/view_file/assets/view_off.svg
  28. 35 0
      src/components/view_file/assets/word_icon.svg
  29. 24 0
      src/components/view_file/assets/zip_icon.svg
  30. 31 0
      src/components/view_file/components/index.less
  31. 402 0
      src/components/view_file/components/index.vue
  32. 102 0
      src/components/view_file/components/renders.js
  33. 41 0
      src/components/view_file/components/util.js
  34. 470 0
      src/components/view_file/components/vendors/colz/index.js
  35. 39 0
      src/components/view_file/components/vendors/image/ImageViewer.vue
  36. 13 0
      src/components/view_file/components/vendors/image/index.js
  37. 61 0
      src/components/view_file/components/vendors/other/index.vue
  38. 165 0
      src/components/view_file/components/vendors/pdf/PdfView.vue
  39. 8 0
      src/components/view_file/components/vendors/pdf/index.js
  40. 5213 0
      src/components/view_file/components/vendors/pptx/PPT.vue
  41. 25 0
      src/components/view_file/components/vendors/pptx/index.js
  42. 31 0
      src/components/view_file/components/vendors/text/CodeViewer.vue
  43. 15 0
      src/components/view_file/components/vendors/text/index.js
  44. 823 0
      src/components/view_file/components/vendors/xlsx/Table.vue
  45. 69 0
      src/components/view_file/components/vendors/xlsx/color.js
  46. 28 0
      src/components/view_file/components/vendors/xlsx/index.js
  47. 49 0
      src/components/view_file/components/vendors/xlsx/util.js
  48. 65 0
      src/components/view_file/index.vue
  49. BIN
      src/components/wc-page-tip/images/empty.png
  50. BIN
      src/components/wc-page-tip/images/error_data.png
  51. BIN
      src/components/wc-page-tip/images/no_auth.png
  52. 9 0
      src/components/wc-page-tip/index.js
  53. 93 0
      src/components/wc-page-tip/wc-page-tip.vue
  54. 61 0
      src/config/index.ts
  55. 25 0
      src/main.ts
  56. 25 0
      src/router/index.ts
  57. 24 0
      tsconfig.json
  58. 24 0
      vite.config.ts

+ 51 - 0
.eslintrc.js

@@ -0,0 +1,51 @@
+/*
+ * @Author: mzr
+ * @Date: 2023-04-07 14:59:16
+ * @LastEditors: wzh
+ * @LastEditTime: 2023-09-18 16:43:47
+ * @Description:
+ */
+module.exports = {
+  root: true,
+  parser: 'vue-eslint-parser',
+  parserOptions: {
+    // 指定ESlint的解析器
+    parser: '@typescript-eslint/parser',
+    // 允许使用ES语法
+    ecmaVersion: 2020,
+    // 允许使用import
+    sourceType: 'module',
+    // 允许解析JSX
+    ecmaFeatures: {
+      jsx: true,
+    },
+  },
+  env: {
+    browser: true,
+  },
+  extends: [
+    'plugin:vue/essential',
+    '@vue/standard',
+    'plugin:@typescript-eslint/recommended',
+    // '@vue/prettier',
+    // '@vue/prettier/@typescript-eslint'
+  ],
+  plugins: [
+    // 'vue'
+    '@typescript-eslint/eslint-plugin'
+  ],
+  rules: {
+    // allow debugger during development
+    'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off',
+    'vue/no-parsing-error': [2, {
+      "x-invalid-end-tag": false
+    }],
+    '@typescript-eslint/no-explicit-any': 'off',
+    '@typescript-eslint/ban-ts-comment': 'off',
+    '@typescript-eslint/no-unused-vars': 0,
+    '@typescript-eslint/no-empty-function': 0,
+    '@typescript-eslint/no-this-alias': 0,
+    'vue/no-mutating-props': 'off',
+    'vue/script-setup-uses-vars': 'off'
+  }
+}

+ 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?

+ 2 - 0
.npmrc

@@ -0,0 +1,2 @@
+registry=http://nexus.wisdomcity.com.cn/repository/wisdomcity-npm-group/
+_auth=YWRtaW46V2lzZG9tY2l0eUBAMjAyMg==

+ 23 - 0
index.html

@@ -0,0 +1,23 @@
+<!--
+ * @Author: LiZhiWei
+ * @Date: 2025-04-09 09:04:17
+ * @LastEditors: LiZhiWei
+ * @LastEditTime: 2025-04-09 10:17:33
+ * @Description: 
+-->
+<!doctype html>
+<html lang="en">
+
+<head>
+    <meta charset="UTF-8" />
+    <link rel="icon" type="image/svg+xml" href="/vite.svg" />
+    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+    <title>Vite App</title>
+</head>
+
+<body>
+    <div id="app"></div>
+    <script type="module" src="/src/main.ts"></script>
+</body>
+
+</html>

+ 42 - 0
package.json

@@ -0,0 +1,42 @@
+{
+    "name": "preview-office",
+    "private": true,
+    "version": "0.0.0",
+    "type": "module",
+    "scripts": {
+        "dev": "vite",
+        "build": "tsc && vite build",
+        "preview": "vite preview"
+    },
+    "devDependencies": {
+        "less": "^4.3.0",
+        "typescript": "~5.7.2",
+        "vite": "^2.8.6",
+        "vite-plugin-vue2": "^2.0.3",
+        "vue-template-compiler": "^2.7.16",
+        "@originjs/vite-plugin-require-context": "^1.0.9"
+    },
+    "dependencies": {
+        "@handsontable/vue": "^15.2.0",
+        "@lifetech-inc/x-data-spreadsheet": "^1.0.17",
+        "axios": "^1.8.4",
+        "docx-preview": "^0.3.5",
+        "element-ui": "^2.15.13",
+        "exceljs": "^4.4.0",
+        "file-saver": "^2.0.5",
+        "html2canvas": "^1.4.1",
+        "js-cookie": "^3.0.5",
+        "lodash": "^4.17.21",
+        "lodash-es": "^4.17.21",
+        "pdfjs-dist": "2.4.456",
+        "pptxtojson": "^1.3.1",
+        "handsontable": "^11.1.0",
+        "tinycolor2": "^1.6.0",
+        "vue": "2.7.16",
+        "vue-router": "^3.5.1",
+        "vuex": "^3.0.1",
+        "wisdom-ui": "^1.0.4-beta.5",
+        "x-data-spreadsheet": "^1.1.9",
+        "xlsx": "^0.18.5"
+    }
+}

+ 3912 - 0
pnpm-lock.yaml

@@ -0,0 +1,3912 @@
+lockfileVersion: '9.0'
+
+settings:
+  autoInstallPeers: true
+  excludeLinksFromLockfile: false
+
+importers:
+
+  .:
+    dependencies:
+      '@handsontable/vue':
+        specifier: ^15.2.0
+        version: 15.2.0(handsontable@11.1.0)(vue@2.7.16)
+      '@lifetech-inc/x-data-spreadsheet':
+        specifier: ^1.0.17
+        version: 1.0.17
+      axios:
+        specifier: ^1.8.4
+        version: 1.8.4
+      docx-preview:
+        specifier: ^0.3.5
+        version: 0.3.5
+      element-ui:
+        specifier: ^2.15.13
+        version: 2.15.14(vue@2.7.16)
+      exceljs:
+        specifier: ^4.4.0
+        version: 4.4.0
+      file-saver:
+        specifier: ^2.0.5
+        version: 2.0.5
+      handsontable:
+        specifier: ^11.1.0
+        version: 11.1.0
+      html2canvas:
+        specifier: ^1.4.1
+        version: 1.4.1
+      js-cookie:
+        specifier: ^3.0.5
+        version: 3.0.5
+      lodash:
+        specifier: ^4.17.21
+        version: 4.17.21
+      lodash-es:
+        specifier: ^4.17.21
+        version: 4.17.21
+      pdfjs-dist:
+        specifier: 2.4.456
+        version: 2.4.456
+      pptxtojson:
+        specifier: ^1.3.1
+        version: 1.3.1
+      tinycolor2:
+        specifier: ^1.6.0
+        version: 1.6.0
+      vue:
+        specifier: 2.7.16
+        version: 2.7.16
+      vue-router:
+        specifier: ^3.5.1
+        version: 3.6.5(vue@2.7.16)
+      vuex:
+        specifier: ^3.0.1
+        version: 3.6.2(vue@2.7.16)
+      wisdom-ui:
+        specifier: ^1.0.4-beta.5
+        version: 1.0.4-beta.6
+      x-data-spreadsheet:
+        specifier: ^1.1.9
+        version: 1.1.9
+      xlsx:
+        specifier: ^0.18.5
+        version: 0.18.5
+    devDependencies:
+      '@originjs/vite-plugin-require-context':
+        specifier: ^1.0.9
+        version: 1.0.9
+      less:
+        specifier: ^4.3.0
+        version: 4.3.0
+      typescript:
+        specifier: ~5.7.2
+        version: 5.7.3
+      vite:
+        specifier: ^2.8.6
+        version: 2.9.18(less@4.3.0)
+      vite-plugin-vue2:
+        specifier: ^2.0.3
+        version: 2.0.3(lodash@4.17.21)(vite@2.9.18(less@4.3.0))(vue-template-compiler@2.7.16)(vue@2.7.16)
+      vue-template-compiler:
+        specifier: ^2.7.16
+        version: 2.7.16
+
+packages:
+
+  '@ampproject/remapping@2.3.0':
+    resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==}
+    engines: {node: '>=6.0.0'}
+
+  '@babel/code-frame@7.26.2':
+    resolution: {integrity: sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==}
+    engines: {node: '>=6.9.0'}
+
+  '@babel/compat-data@7.26.8':
+    resolution: {integrity: sha512-oH5UPLMWR3L2wEFLnFJ1TZXqHufiTKAiLfqw5zkhS4dKXLJ10yVztfil/twG8EDTA4F/tvVNw9nOl4ZMslB8rQ==}
+    engines: {node: '>=6.9.0'}
+
+  '@babel/core@7.26.10':
+    resolution: {integrity: sha512-vMqyb7XCDMPvJFFOaT9kxtiRh42GwlZEg1/uIgtZshS5a/8OaduUfCi7kynKgc3Tw/6Uo2D+db9qBttghhmxwQ==}
+    engines: {node: '>=6.9.0'}
+
+  '@babel/generator@7.27.0':
+    resolution: {integrity: sha512-VybsKvpiN1gU1sdMZIp7FcqphVVKEwcuj02x73uvcHE0PTihx1nlBcowYWhDwjpoAXRv43+gDzyggGnn1XZhVw==}
+    engines: {node: '>=6.9.0'}
+
+  '@babel/helper-annotate-as-pure@7.25.9':
+    resolution: {integrity: sha512-gv7320KBUFJz1RnylIg5WWYPRXKZ884AGkYpgpWW02TH66Dl+HaC1t1CKd0z3R4b6hdYEcmrNZHUmfCP+1u3/g==}
+    engines: {node: '>=6.9.0'}
+
+  '@babel/helper-compilation-targets@7.27.0':
+    resolution: {integrity: sha512-LVk7fbXml0H2xH34dFzKQ7TDZ2G4/rVTOrq9V+icbbadjbVxxeFeDsNHv2SrZeWoA+6ZiTyWYWtScEIW07EAcA==}
+    engines: {node: '>=6.9.0'}
+
+  '@babel/helper-create-class-features-plugin@7.27.0':
+    resolution: {integrity: sha512-vSGCvMecvFCd/BdpGlhpXYNhhC4ccxyvQWpbGL4CWbvfEoLFWUZuSuf7s9Aw70flgQF+6vptvgK2IfOnKlRmBg==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0
+
+  '@babel/helper-member-expression-to-functions@7.25.9':
+    resolution: {integrity: sha512-wbfdZ9w5vk0C0oyHqAJbc62+vet5prjj01jjJ8sKn3j9h3MQQlflEdXYvuqRWjHnM12coDEqiC1IRCi0U/EKwQ==}
+    engines: {node: '>=6.9.0'}
+
+  '@babel/helper-module-imports@7.25.9':
+    resolution: {integrity: sha512-tnUA4RsrmflIM6W6RFTLFSXITtl0wKjgpnLgXyowocVPrbYrLUXSBXDgTs8BlbmIzIdlBySRQjINYs2BAkiLtw==}
+    engines: {node: '>=6.9.0'}
+
+  '@babel/helper-module-transforms@7.26.0':
+    resolution: {integrity: sha512-xO+xu6B5K2czEnQye6BHA7DolFFmS3LB7stHZFaOLb1pAwO1HWLS8fXA+eh0A2yIvltPVmx3eNNDBJA2SLHXFw==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0
+
+  '@babel/helper-optimise-call-expression@7.25.9':
+    resolution: {integrity: sha512-FIpuNaz5ow8VyrYcnXQTDRGvV6tTjkNtCK/RYNDXGSLlUD6cBuQTSw43CShGxjvfBTfcUA/r6UhUCbtYqkhcuQ==}
+    engines: {node: '>=6.9.0'}
+
+  '@babel/helper-plugin-utils@7.26.5':
+    resolution: {integrity: sha512-RS+jZcRdZdRFzMyr+wcsaqOmld1/EqTghfaBGQQd/WnRdzdlvSZ//kF7U8VQTxf1ynZ4cjUcYgjVGx13ewNPMg==}
+    engines: {node: '>=6.9.0'}
+
+  '@babel/helper-replace-supers@7.26.5':
+    resolution: {integrity: sha512-bJ6iIVdYX1YooY2X7w1q6VITt+LnUILtNk7zT78ykuwStx8BauCzxvFqFaHjOpW1bVnSUM1PN1f0p5P21wHxvg==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0
+
+  '@babel/helper-skip-transparent-expression-wrappers@7.25.9':
+    resolution: {integrity: sha512-K4Du3BFa3gvyhzgPcntrkDgZzQaq6uozzcpGbOO1OEJaI+EJdqWIMTLgFgQf6lrfiDFo5FU+BxKepI9RmZqahA==}
+    engines: {node: '>=6.9.0'}
+
+  '@babel/helper-string-parser@7.25.9':
+    resolution: {integrity: sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==}
+    engines: {node: '>=6.9.0'}
+
+  '@babel/helper-validator-identifier@7.25.9':
+    resolution: {integrity: sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==}
+    engines: {node: '>=6.9.0'}
+
+  '@babel/helper-validator-option@7.25.9':
+    resolution: {integrity: sha512-e/zv1co8pp55dNdEcCynfj9X7nyUKUXoUEwfXqaZt0omVOmDe9oOTdKStH4GmAw6zxMFs50ZayuMfHDKlO7Tfw==}
+    engines: {node: '>=6.9.0'}
+
+  '@babel/helpers@7.27.0':
+    resolution: {integrity: sha512-U5eyP/CTFPuNE3qk+WZMxFkp/4zUzdceQlfzf7DdGdhp+Fezd7HD+i8Y24ZuTMKX3wQBld449jijbGq6OdGNQg==}
+    engines: {node: '>=6.9.0'}
+
+  '@babel/parser@7.27.0':
+    resolution: {integrity: sha512-iaepho73/2Pz7w2eMS0Q5f83+0RKI7i4xmiYeBmDzfRVbQtTOG7Ts0S4HzJVsTMGI9keU8rNfuZr8DKfSt7Yyg==}
+    engines: {node: '>=6.0.0'}
+    hasBin: true
+
+  '@babel/plugin-proposal-class-properties@7.18.6':
+    resolution: {integrity: sha512-cumfXOF0+nzZrrN8Rf0t7M+tF6sZc7vhQwYQck9q1/5w2OExlD+b4v4RpMJFaV1Z7WcDRgO6FqvxqxGlwo+RHQ==}
+    engines: {node: '>=6.9.0'}
+    deprecated: This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-class-properties instead.
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+
+  '@babel/plugin-proposal-decorators@7.25.9':
+    resolution: {integrity: sha512-smkNLL/O1ezy9Nhy4CNosc4Va+1wo5w4gzSZeLe6y6dM4mmHfYOCPolXQPHQxonZCF+ZyebxN9vqOolkYrSn5g==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+
+  '@babel/plugin-proposal-nullish-coalescing-operator@7.18.6':
+    resolution: {integrity: sha512-wQxQzxYeJqHcfppzBDnm1yAY0jSRkUXR2z8RePZYrKwMKgMlE8+Z6LUno+bd6LvbGh8Gltvy74+9pIYkr+XkKA==}
+    engines: {node: '>=6.9.0'}
+    deprecated: This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-nullish-coalescing-operator instead.
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+
+  '@babel/plugin-proposal-object-rest-spread@7.20.7':
+    resolution: {integrity: sha512-d2S98yCiLxDVmBmE8UjGcfPvNEUbA1U5q5WxaWFUGRzJSVAZqm5W6MbPct0jxnegUZ0niLeNX+IOzEs7wYg9Dg==}
+    engines: {node: '>=6.9.0'}
+    deprecated: This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-object-rest-spread instead.
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+
+  '@babel/plugin-proposal-optional-chaining@7.21.0':
+    resolution: {integrity: sha512-p4zeefM72gpmEe2fkUr/OnOXpWEf8nAgk7ZYVqqfFiyIG7oFfVZcCrU64hWn5xp4tQ9LkV4bTIa5rD0KANpKNA==}
+    engines: {node: '>=6.9.0'}
+    deprecated: This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-optional-chaining instead.
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+
+  '@babel/plugin-syntax-decorators@7.25.9':
+    resolution: {integrity: sha512-ryzI0McXUPJnRCvMo4lumIKZUzhYUO/ScI+Mz4YVaTLt04DHNSjEUjKVvbzQjZFLuod/cYEc07mJWhzl6v4DPg==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+
+  '@babel/plugin-syntax-jsx@7.25.9':
+    resolution: {integrity: sha512-ld6oezHQMZsZfp6pWtbjaNDF2tiiCYYDqQszHt5VV437lewP9aSi2Of99CK0D0XB21k7FLgnLcmQKyKzynfeAA==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+
+  '@babel/plugin-syntax-nullish-coalescing-operator@7.8.3':
+    resolution: {integrity: sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+
+  '@babel/plugin-syntax-object-rest-spread@7.8.3':
+    resolution: {integrity: sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+
+  '@babel/plugin-syntax-optional-chaining@7.8.3':
+    resolution: {integrity: sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+
+  '@babel/plugin-syntax-typescript@7.25.9':
+    resolution: {integrity: sha512-hjMgRy5hb8uJJjUcdWunWVcoi9bGpJp8p5Ol1229PoN6aytsLwNMgmdftO23wnCLMfVmTwZDWMPNq/D1SY60JQ==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+
+  '@babel/plugin-transform-arrow-functions@7.25.9':
+    resolution: {integrity: sha512-6jmooXYIwn9ca5/RylZADJ+EnSxVUS5sjeJ9UPk6RWRzXCmOJCy6dqItPJFpw2cuCangPK4OYr5uhGKcmrm5Qg==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+
+  '@babel/plugin-transform-block-scoping@7.27.0':
+    resolution: {integrity: sha512-u1jGphZ8uDI2Pj/HJj6YQ6XQLZCNjOlprjxB5SVz6rq2T6SwAR+CdrWK0CP7F+9rDVMXdB0+r6Am5G5aobOjAQ==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+
+  '@babel/plugin-transform-computed-properties@7.25.9':
+    resolution: {integrity: sha512-HnBegGqXZR12xbcTHlJ9HGxw1OniltT26J5YpfruGqtUHlz/xKf/G2ak9e+t0rVqrjXa9WOhvYPz1ERfMj23AA==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+
+  '@babel/plugin-transform-destructuring@7.25.9':
+    resolution: {integrity: sha512-WkCGb/3ZxXepmMiX101nnGiU+1CAdut8oHyEOHxkKuS1qKpU2SMXE2uSvfz8PBuLd49V6LEsbtyPhWC7fnkgvQ==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+
+  '@babel/plugin-transform-parameters@7.25.9':
+    resolution: {integrity: sha512-wzz6MKwpnshBAiRmn4jR8LYz/g8Ksg0o80XmwZDlordjwEk9SxBzTWC7F5ef1jhbrbOW2DJ5J6ayRukrJmnr0g==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+
+  '@babel/plugin-transform-spread@7.25.9':
+    resolution: {integrity: sha512-oNknIB0TbURU5pqJFVbOOFspVlrpVwo2H1+HUIsVDvp5VauGGDP1ZEvO8Nn5xyMEs3dakajOxlmkNW7kNgSm6A==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+
+  '@babel/plugin-transform-typescript@7.27.0':
+    resolution: {integrity: sha512-fRGGjO2UEGPjvEcyAZXRXAS8AfdaQoq7HnxAbJoAoW10B9xOKesmmndJv+Sym2a+9FHWZ9KbyyLCe9s0Sn5jtg==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+
+  '@babel/runtime@7.27.0':
+    resolution: {integrity: sha512-VtPOkrdPHZsKc/clNqyi9WUA8TINkZ4cGk63UUE3u4pmB2k+ZMQRDuIOagv8UVd6j7k0T3+RRIb7beKTebNbcw==}
+    engines: {node: '>=6.9.0'}
+
+  '@babel/template@7.27.0':
+    resolution: {integrity: sha512-2ncevenBqXI6qRMukPlXwHKHchC7RyMuu4xv5JBXRfOGVcTy1mXCD12qrp7Jsoxll1EV3+9sE4GugBVRjT2jFA==}
+    engines: {node: '>=6.9.0'}
+
+  '@babel/traverse@7.27.0':
+    resolution: {integrity: sha512-19lYZFzYVQkkHkl4Cy4WrAVcqBkgvV2YM2TU3xG6DIwO7O3ecbDPfW3yM3bjAGcqcQHi+CCtjMR3dIEHxsd6bA==}
+    engines: {node: '>=6.9.0'}
+
+  '@babel/types@7.27.0':
+    resolution: {integrity: sha512-H45s8fVLYjbhFH62dIJ3WtmJ6RSPt/3DRO0ZcT2SUiYiQyz3BLVb9ADEnLl91m74aQPS3AzzeajZHYOalWe3bg==}
+    engines: {node: '>=6.9.0'}
+
+  '@esbuild/linux-loong64@0.14.54':
+    resolution: {integrity: sha512-bZBrLAIX1kpWelV0XemxBZllyRmM6vgFQQG2GdNb+r3Fkp0FOh1NJSvekXDs7jq70k4euu1cryLMfU+mTXlEpw==}
+    engines: {node: '>=12'}
+    cpu: [loong64]
+    os: [linux]
+
+  '@fast-csv/format@4.3.5':
+    resolution: {integrity: sha512-8iRn6QF3I8Ak78lNAa+Gdl5MJJBM5vRHivFtMRUWINdevNo00K7OXxS2PshawLKTejVwieIlPmK5YlLu6w4u8A==}
+
+  '@fast-csv/parse@4.3.6':
+    resolution: {integrity: sha512-uRsLYksqpbDmWaSmzvJcuApSEe38+6NQZBUsuAyMZKqHxH0g1wcJgsKUvN3WC8tewaqFjBMMGrkHmC+T7k8LvA==}
+
+  '@handsontable/vue@15.2.0':
+    resolution: {integrity: sha512-N7OrfGNMwm+uhbU0Kg7zrrSJVrkWFdrT9xR2Wha6XWTXfJoduFKmkHgw6PIBb4nCb7+tM3jAr8ZAR/oFaGH0KA==}
+    peerDependencies:
+      handsontable: '>=15.0.0'
+      vue: ^2.5.0
+
+  '@jest/types@26.6.2':
+    resolution: {integrity: sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==}
+    engines: {node: '>= 10.14.2'}
+
+  '@jridgewell/gen-mapping@0.3.8':
+    resolution: {integrity: sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==}
+    engines: {node: '>=6.0.0'}
+
+  '@jridgewell/resolve-uri@3.1.2':
+    resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==}
+    engines: {node: '>=6.0.0'}
+
+  '@jridgewell/set-array@1.2.1':
+    resolution: {integrity: sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==}
+    engines: {node: '>=6.0.0'}
+
+  '@jridgewell/sourcemap-codec@1.5.0':
+    resolution: {integrity: sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==}
+
+  '@jridgewell/trace-mapping@0.3.25':
+    resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==}
+
+  '@lifetech-inc/x-data-spreadsheet@1.0.17':
+    resolution: {integrity: sha512-pgWAUia9gOH0xJ3GYRatufZ7tQ4kbGBHIQo6nGDppgisrqbDQaWl9NLN6WecTv18vV4g9DgdLMK27u4+NZR2OQ==}
+
+  '@originjs/vite-plugin-require-context@1.0.9':
+    resolution: {integrity: sha512-xBC7XGm6HkniQ9x+P0yTS4uUua5bWKjnN0/FfHreCV22QrQ+zXIZVdCFDr0PPXuawXfPzqSzdljqffcpSCFNcA==}
+
+  '@riophae/vue-treeselect@0.4.0':
+    resolution: {integrity: sha512-J4atYmBqXQmiPFK/0B5sXKjtnGc21mBJEiyKIDZwk0Q9XuynVFX6IJ4EpaLmUgL5Tve7HAS7wkiGGSti6Uaxcg==}
+    peerDependencies:
+      vue: ^2.2.0
+
+  '@rollup/pluginutils@4.2.1':
+    resolution: {integrity: sha512-iKnFXr7NkdZAIHiIWE+BX5ULi/ucVFYWD6TbAV+rZctiRTY2PL6tsIKhoIOaoskiWAkgu+VsbXgUVDNLHf+InQ==}
+    engines: {node: '>= 8.0.0'}
+
+  '@types/istanbul-lib-coverage@2.0.6':
+    resolution: {integrity: sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==}
+
+  '@types/istanbul-lib-report@3.0.3':
+    resolution: {integrity: sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==}
+
+  '@types/istanbul-reports@3.0.4':
+    resolution: {integrity: sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==}
+
+  '@types/jest@26.0.24':
+    resolution: {integrity: sha512-E/X5Vib8BWqZNRlDxj9vYXhsDwPYbPINqKF9BsnSoon4RQ0D9moEuLD8txgyypFLH7J4+Lho9Nr/c8H0Fi+17w==}
+
+  '@types/node@14.18.63':
+    resolution: {integrity: sha512-fAtCfv4jJg+ExtXhvCkCqUKZ+4ok/JQk01qDKhL5BDDoS3AxKXhV5/MAVUZyQnSEd2GT92fkgZl0pz0Q0AzcIQ==}
+
+  '@types/pikaday@1.7.4':
+    resolution: {integrity: sha512-0KsHVyw5pTG829nqG4IRu7m+BFQlFEBdbE/1i3S5182HeKUKv1uEW0gyEmkJVp5i4IV+9pyh23O83+KpRkSQbw==}
+
+  '@types/yargs-parser@21.0.3':
+    resolution: {integrity: sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==}
+
+  '@types/yargs@15.0.19':
+    resolution: {integrity: sha512-2XUaGVmyQjgyAZldf0D0c14vvo/yv0MhQBSTJcejMMaitsn3nxCB6TmH4G0ZQf+uxROOa9mpanoSm8h6SG/1ZA==}
+
+  '@vue/babel-helper-vue-jsx-merge-props@1.4.0':
+    resolution: {integrity: sha512-JkqXfCkUDp4PIlFdDQ0TdXoIejMtTHP67/pvxlgeY+u5k3LEdKuWZ3LK6xkxo52uDoABIVyRwqVkfLQJhk7VBA==}
+
+  '@vue/babel-plugin-transform-vue-jsx@1.4.0':
+    resolution: {integrity: sha512-Fmastxw4MMx0vlgLS4XBX0XiBbUFzoMGeVXuMV08wyOfXdikAFqBTuYPR0tlk+XskL19EzHc39SgjrPGY23JnA==}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+
+  '@vue/babel-preset-jsx@1.4.0':
+    resolution: {integrity: sha512-QmfRpssBOPZWL5xw7fOuHNifCQcNQC1PrOo/4fu6xlhlKJJKSA3HqX92Nvgyx8fqHZTUGMPHmFA+IDqwXlqkSA==}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+      vue: '*'
+    peerDependenciesMeta:
+      vue:
+        optional: true
+
+  '@vue/babel-sugar-composition-api-inject-h@1.4.0':
+    resolution: {integrity: sha512-VQq6zEddJHctnG4w3TfmlVp5FzDavUSut/DwR0xVoe/mJKXyMcsIibL42wPntozITEoY90aBV0/1d2KjxHU52g==}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+
+  '@vue/babel-sugar-composition-api-render-instance@1.4.0':
+    resolution: {integrity: sha512-6ZDAzcxvy7VcnCjNdHJ59mwK02ZFuP5CnucloidqlZwVQv5CQLijc3lGpR7MD3TWFi78J7+a8J56YxbCtHgT9Q==}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+
+  '@vue/babel-sugar-functional-vue@1.4.0':
+    resolution: {integrity: sha512-lTEB4WUFNzYt2In6JsoF9sAYVTo84wC4e+PoZWSgM6FUtqRJz7wMylaEhSRgG71YF+wfLD6cc9nqVeXN2rwBvw==}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+
+  '@vue/babel-sugar-inject-h@1.4.0':
+    resolution: {integrity: sha512-muwWrPKli77uO2fFM7eA3G1lAGnERuSz2NgAxuOLzrsTlQl8W4G+wwbM4nB6iewlKbwKRae3nL03UaF5ffAPMA==}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+
+  '@vue/babel-sugar-v-model@1.4.0':
+    resolution: {integrity: sha512-0t4HGgXb7WHYLBciZzN5s0Hzqan4Ue+p/3FdQdcaHAb7s5D9WZFGoSxEZHrR1TFVZlAPu1bejTKGeAzaaG3NCQ==}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+
+  '@vue/babel-sugar-v-on@1.4.0':
+    resolution: {integrity: sha512-m+zud4wKLzSKgQrWwhqRObWzmTuyzl6vOP7024lrpeJM4x2UhQtRDLgYjXAw9xBXjCwS0pP9kXjg91F9ZNo9JA==}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+
+  '@vue/compiler-sfc@2.7.16':
+    resolution: {integrity: sha512-KWhJ9k5nXuNtygPU7+t1rX6baZeqOYLEforUPjgNDBnLicfHCoi48H87Q8XyLZOrNNsmhuwKqtpDQWjEFe6Ekg==}
+
+  '@vue/component-compiler-utils@3.3.0':
+    resolution: {integrity: sha512-97sfH2mYNU+2PzGrmK2haqffDpVASuib9/w2/noxiFi31Z54hW+q3izKQXXQZSNhtiUpAI36uSuYepeBe4wpHQ==}
+
+  adler-32@1.3.1:
+    resolution: {integrity: sha512-ynZ4w/nUUv5rrsR8UUGoe1VC9hZj6V5hU9Qw1HlMDJGEJw5S7TfTErWTjMys6M7vr0YWcPqs3qAr4ss0nDfP+A==}
+    engines: {node: '>=0.8'}
+
+  ansi-escapes@1.4.0:
+    resolution: {integrity: sha512-wiXutNjDUlNEDWHcYH3jtZUhd3c4/VojassD8zHdHCY13xbZy2XbW+NKQwA0tWGBVzDA9qEzYwfoSsWmviidhw==}
+    engines: {node: '>=0.10.0'}
+
+  ansi-regex@2.1.1:
+    resolution: {integrity: sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==}
+    engines: {node: '>=0.10.0'}
+
+  ansi-regex@3.0.1:
+    resolution: {integrity: sha512-+O9Jct8wf++lXxxFc4hc8LsjaSq0HFzzL7cVsw8pRDIPdjKD2mT4ytDZlLuSBZ4cLKZFXIrMGO7DbQCtMJJMKw==}
+    engines: {node: '>=4'}
+
+  ansi-regex@5.0.1:
+    resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==}
+    engines: {node: '>=8'}
+
+  ansi-styles@2.2.1:
+    resolution: {integrity: sha512-kmCevFghRiWM7HB5zTPULl4r9bVFSWjz62MhqizDGUrq2NWuNMQyuv4tHHoKJHs69M/MF64lEcHdYIocrdWQYA==}
+    engines: {node: '>=0.10.0'}
+
+  ansi-styles@4.3.0:
+    resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==}
+    engines: {node: '>=8'}
+
+  archiver-utils@2.1.0:
+    resolution: {integrity: sha512-bEL/yUb/fNNiNTuUz979Z0Yg5L+LzLxGJz8x79lYmR54fmTIb6ob/hNQgkQnIUDWIFjZVQwl9Xs356I6BAMHfw==}
+    engines: {node: '>= 6'}
+
+  archiver-utils@3.0.4:
+    resolution: {integrity: sha512-KVgf4XQVrTjhyWmx6cte4RxonPLR9onExufI1jhvw/MQ4BB6IsZD5gT8Lq+u/+pRkWna/6JoHpiQioaqFP5Rzw==}
+    engines: {node: '>= 10'}
+
+  archiver@5.3.2:
+    resolution: {integrity: sha512-+25nxyyznAXF7Nef3y0EbBeqmGZgeN/BxHX29Rs39djAfaFalmQ89SE6CWyDCHzGL0yt/ycBtNOmGTW0FyGWNw==}
+    engines: {node: '>= 10'}
+
+  argparse@1.0.10:
+    resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==}
+
+  async-validator@1.8.5:
+    resolution: {integrity: sha512-tXBM+1m056MAX0E8TL2iCjg8WvSyXu0Zc8LNtYqrVeyoL3+esHRZ4SieE9fKQyyU09uONjnMEjrNBMqT0mbvmA==}
+
+  async@3.2.6:
+    resolution: {integrity: sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==}
+
+  asynckit@0.4.0:
+    resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==}
+
+  axios@1.8.4:
+    resolution: {integrity: sha512-eBSYY4Y68NNlHbHBMdeDmKNtDgXWhQsJcGqzO3iLUM0GraQFSS9cVgPX5I9b3lbdFKyYoAEGAZF1DwhTaljNAw==}
+
+  babel-helper-vue-jsx-merge-props@2.0.3:
+    resolution: {integrity: sha512-gsLiKK7Qrb7zYJNgiXKpXblxbV5ffSwR0f5whkPAaBAR4fhi6bwRZxX9wBlIc5M/v8CCkXUbXZL4N/nSE97cqg==}
+
+  babel-polyfill@6.23.0:
+    resolution: {integrity: sha512-0l7mVU+LrQ2X/ZTUq63T5i3VyR2aTgcRTFmBcD6djQ/Fek6q1A9t5u0F4jZVYHzp78jwWAzGfLpAY1b4/I3lfg==}
+
+  babel-runtime@6.26.0:
+    resolution: {integrity: sha512-ITKNuq2wKlW1fJg9sSW52eepoYgZBggvOAHC0u/CYu/qxQ9EVzThCgR69BnSXLHjy2f7SY5zaQ4yt7H9ZVxY2g==}
+
+  balanced-match@1.0.2:
+    resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==}
+
+  base64-arraybuffer@1.0.2:
+    resolution: {integrity: sha512-I3yl4r9QB5ZRY3XuJVEPfc2XhZO6YweFPI+UovAzn+8/hb3oJ6lnysaFcjVpkCPfVWFUDvoZ8kmVDP7WyRtYtQ==}
+    engines: {node: '>= 0.6.0'}
+
+  base64-js@1.5.1:
+    resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==}
+
+  big-integer@1.6.52:
+    resolution: {integrity: sha512-QxD8cf2eVqJOOz63z6JIN9BzvVs/dlySa5HGSBH5xtR8dPteIRQnBxxKqkNTiT6jbDTF6jAfrd4oMcND9RGbQg==}
+    engines: {node: '>=0.6'}
+
+  bignumber.js@8.1.1:
+    resolution: {integrity: sha512-QD46ppGintwPGuL1KqmwhR0O+N2cZUg8JG/VzwI2e28sM9TqHjQB10lI4QAaMHVbLzwVLLAwEglpKPViWX+5NQ==}
+
+  binary@0.3.0:
+    resolution: {integrity: sha512-D4H1y5KYwpJgK8wk1Cue5LLPgmwHKYSChkbspQg5JtVuR5ulGckxfR62H3AE9UDkdMC8yyXlqYihuz3Aqg2XZg==}
+
+  bl@4.1.0:
+    resolution: {integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==}
+
+  bluebird@3.4.7:
+    resolution: {integrity: sha512-iD3898SR7sWVRHbiQv+sHUtHnMvC1o3nW5rAcqnq3uOn07DSAppZYUkIGslDz6gXC7HfunPe7YVBgoEJASPcHA==}
+
+  bluebird@3.7.2:
+    resolution: {integrity: sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==}
+
+  bmaplib.curveline@1.0.0:
+    resolution: {integrity: sha512-9wcFMVhiYxNPqpvsLDAADn3qDhNzXp2mA6VyHSHg2XOAgSooC7ZiujdFhy0sp+0QYjTfJ/MjmLuNoUg2HHxH4Q==}
+
+  bmaplib.heatmap@1.0.4:
+    resolution: {integrity: sha512-rmhqUARBpUSJ9jXzUI2j7dIOqnc38bqubkx/8a349U2qtw/ulLUwyzRD535OrA8G7w5cz4aPKm6/rNvUAarg/Q==}
+
+  bmaplib.lushu@1.0.7:
+    resolution: {integrity: sha512-LVvgpESPii6xGxyjnQjq8u+ic4NjvhdCPV/RiSS/PGTUdZKeTDS7prSpleJLZH3ES0+oc0gYn8bw0LtPYUSz2w==}
+
+  bmaplib.markerclusterer@1.0.13:
+    resolution: {integrity: sha512-VrLyWSiuDEVNi0yUfwOhFQ6z1oEEHS4w36GNu3iASu6p52QIx9uAXMUkuSCHReNR0bj2Cp9SA1dSx5RpojXajQ==}
+
+  bmaplib.texticonoverlay@1.0.2:
+    resolution: {integrity: sha512-4ZTWr4ZP3B6qEWput5Tut16CfZgII38YwM3bpyb4gFTQyORlKYryFp9WHWrwZZaHlOyYDAXG9SX0hka43jTADg==}
+
+  brace-expansion@1.1.11:
+    resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==}
+
+  brace-expansion@2.0.1:
+    resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==}
+
+  browserslist@4.24.4:
+    resolution: {integrity: sha512-KDi1Ny1gSePi1vm0q4oxSF8b4DR44GF4BbmS2YdhPLOEqd8pDviZOGH/GsmRwoWJ2+5Lr085X7naowMwKHDG1A==}
+    engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7}
+    hasBin: true
+
+  buffer-crc32@0.2.13:
+    resolution: {integrity: sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==}
+
+  buffer-indexof-polyfill@1.0.2:
+    resolution: {integrity: sha512-I7wzHwA3t1/lwXQh+A5PbNvJxgfo5r3xulgpYDB5zckTu/Z9oUK9biouBKQUjEqzaz3HnAT6TYoovmE+GqSf7A==}
+    engines: {node: '>=0.10'}
+
+  buffer@5.7.1:
+    resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==}
+
+  buffers@0.1.1:
+    resolution: {integrity: sha512-9q/rDEGSb/Qsvv2qvzIzdluL5k7AaJOTrw23z9reQthrbF7is4CtlT0DXyO1oei2DCp4uojjzQ7igaSHp1kAEQ==}
+    engines: {node: '>=0.2.0'}
+
+  call-bind-apply-helpers@1.0.2:
+    resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==}
+    engines: {node: '>= 0.4'}
+
+  camelcase@5.3.1:
+    resolution: {integrity: sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==}
+    engines: {node: '>=6'}
+
+  caniuse-lite@1.0.30001712:
+    resolution: {integrity: sha512-MBqPpGYYdQ7/hfKiet9SCI+nmN5/hp4ZzveOJubl5DTAMa5oggjAuoi0Z4onBpKPFI2ePGnQuQIzF3VxDjDJig==}
+
+  cfb@1.2.2:
+    resolution: {integrity: sha512-KfdUZsSOw19/ObEWasvBP/Ac4reZvAGauZhs6S/gqNhXhI7cKwvlH7ulj+dOEYnca4bm4SGo8C1bTAQvnTjgQA==}
+    engines: {node: '>=0.8'}
+
+  chainsaw@0.1.0:
+    resolution: {integrity: sha512-75kWfWt6MEKNC8xYXIdRpDehRYY/tNSgwKaJq+dbbDcxORuVrrQ+SEHoWsniVn9XPYfP4gmdWIeDk/4YNp1rNQ==}
+
+  chalk@1.1.3:
+    resolution: {integrity: sha512-U3lRVLMSlsCfjqYPbLyVv11M9CPW4I728d6TCKMAOJueEeB9/8o+eSsMnxPJD+Q+K909sdESg7C+tIkoH6on1A==}
+    engines: {node: '>=0.10.0'}
+
+  chalk@4.1.2:
+    resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==}
+    engines: {node: '>=10'}
+
+  chardet@0.4.2:
+    resolution: {integrity: sha512-j/Toj7f1z98Hh2cYo2BVr85EpIRWqUi7rtRSGxh/cqUjqrnJe9l9UE7IUGd2vQ2p+kSHLkSzObQPZPLUC6TQwg==}
+
+  chevrotain@6.5.0:
+    resolution: {integrity: sha512-BwqQ/AgmKJ8jcMEjaSnfMybnKMgGTrtDKowfTP3pX4jwVy0kNjRsT/AP6h+wC3+3NC+X8X15VWBnTCQlX+wQFg==}
+
+  cli-cursor@2.1.0:
+    resolution: {integrity: sha512-8lgKz8LmCRYZZQDpRyT2m5rKJ08TnU4tR9FFFW2rxpxR1FzWi4PQ/NfyODchAatHaUgnSPVcx/R5w6NuTBzFiw==}
+    engines: {node: '>=4'}
+
+  cli-width@2.2.1:
+    resolution: {integrity: sha512-GRMWDxpOB6Dgk2E5Uo+3eEBvtOOlimMmpbFiKuLFnQzYDavtLFY3K5ona41jgN/WdRZtG7utuVSVTL4HbZHGkw==}
+
+  codepage@1.15.0:
+    resolution: {integrity: sha512-3g6NUTPd/YtuuGrhMnOMRjFc+LJw/bnMp3+0r/Wcz3IXUuCosKRJvMphm5+Q+bvTVGcJJuRvVLuYba+WojaFaA==}
+    engines: {node: '>=0.8'}
+
+  color-convert@2.0.1:
+    resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==}
+    engines: {node: '>=7.0.0'}
+
+  color-name@1.1.4:
+    resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==}
+
+  combined-stream@1.0.8:
+    resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==}
+    engines: {node: '>= 0.8'}
+
+  compress-commons@4.1.2:
+    resolution: {integrity: sha512-D3uMHtGc/fcO1Gt1/L7i1e33VOvD4A9hfQLP+6ewd+BvG/gQ84Yh4oftEhAdjSMgBgwGL+jsppT7JYNpo6MHHg==}
+    engines: {node: '>= 10'}
+
+  concat-map@0.0.1:
+    resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==}
+
+  consolidate@0.15.1:
+    resolution: {integrity: sha512-DW46nrsMJgy9kqAbPt5rKaCr7uFtpo4mSUvLHIUbJEjm0vo+aY5QLwBUq3FK4tRnJr/X0Psc0C4jf/h+HtXSMw==}
+    engines: {node: '>= 0.10.0'}
+    deprecated: Please upgrade to consolidate v1.0.0+ as it has been modernized with several long-awaited fixes implemented. Maintenance is supported by Forward Email at https://forwardemail.net ; follow/watch https://github.com/ladjs/consolidate for updates and release changelog
+    peerDependencies:
+      arc-templates: ^0.5.3
+      atpl: '>=0.7.6'
+      babel-core: ^6.26.3
+      bracket-template: ^1.1.5
+      coffee-script: ^1.12.7
+      dot: ^1.1.3
+      dust: ^0.3.0
+      dustjs-helpers: ^1.7.4
+      dustjs-linkedin: ^2.7.5
+      eco: ^1.1.0-rc-3
+      ect: ^0.5.9
+      ejs: ^3.1.5
+      haml-coffee: ^1.14.1
+      hamlet: ^0.3.3
+      hamljs: ^0.6.2
+      handlebars: ^4.7.6
+      hogan.js: ^3.0.2
+      htmling: ^0.0.8
+      jade: ^1.11.0
+      jazz: ^0.0.18
+      jqtpl: ~1.1.0
+      just: ^0.1.8
+      liquid-node: ^3.0.1
+      liquor: ^0.0.5
+      lodash: ^4.17.20
+      marko: ^3.14.4
+      mote: ^0.2.0
+      mustache: ^3.0.0
+      nunjucks: ^3.2.2
+      plates: ~0.4.11
+      pug: ^3.0.0
+      qejs: ^3.0.5
+      ractive: ^1.3.12
+      razor-tmpl: ^1.3.1
+      react: ^16.13.1
+      react-dom: ^16.13.1
+      slm: ^2.0.0
+      squirrelly: ^5.1.0
+      swig: ^1.4.2
+      swig-templates: ^2.0.3
+      teacup: ^2.0.0
+      templayed: '>=0.2.3'
+      then-jade: '*'
+      then-pug: '*'
+      tinyliquid: ^0.2.34
+      toffee: ^0.3.6
+      twig: ^1.15.2
+      twing: ^5.0.2
+      underscore: ^1.11.0
+      vash: ^0.13.0
+      velocityjs: ^2.0.1
+      walrus: ^0.10.1
+      whiskers: ^0.4.0
+    peerDependenciesMeta:
+      arc-templates:
+        optional: true
+      atpl:
+        optional: true
+      babel-core:
+        optional: true
+      bracket-template:
+        optional: true
+      coffee-script:
+        optional: true
+      dot:
+        optional: true
+      dust:
+        optional: true
+      dustjs-helpers:
+        optional: true
+      dustjs-linkedin:
+        optional: true
+      eco:
+        optional: true
+      ect:
+        optional: true
+      ejs:
+        optional: true
+      haml-coffee:
+        optional: true
+      hamlet:
+        optional: true
+      hamljs:
+        optional: true
+      handlebars:
+        optional: true
+      hogan.js:
+        optional: true
+      htmling:
+        optional: true
+      jade:
+        optional: true
+      jazz:
+        optional: true
+      jqtpl:
+        optional: true
+      just:
+        optional: true
+      liquid-node:
+        optional: true
+      liquor:
+        optional: true
+      lodash:
+        optional: true
+      marko:
+        optional: true
+      mote:
+        optional: true
+      mustache:
+        optional: true
+      nunjucks:
+        optional: true
+      plates:
+        optional: true
+      pug:
+        optional: true
+      qejs:
+        optional: true
+      ractive:
+        optional: true
+      razor-tmpl:
+        optional: true
+      react:
+        optional: true
+      react-dom:
+        optional: true
+      slm:
+        optional: true
+      squirrelly:
+        optional: true
+      swig:
+        optional: true
+      swig-templates:
+        optional: true
+      teacup:
+        optional: true
+      templayed:
+        optional: true
+      then-jade:
+        optional: true
+      then-pug:
+        optional: true
+      tinyliquid:
+        optional: true
+      toffee:
+        optional: true
+      twig:
+        optional: true
+      twing:
+        optional: true
+      underscore:
+        optional: true
+      vash:
+        optional: true
+      velocityjs:
+        optional: true
+      walrus:
+        optional: true
+      whiskers:
+        optional: true
+
+  consolidate@0.16.0:
+    resolution: {integrity: sha512-Nhl1wzCslqXYTJVDyJCu3ODohy9OfBMB5uD2BiBTzd7w+QY0lBzafkR8y8755yMYHAaMD4NuzbAw03/xzfw+eQ==}
+    engines: {node: '>= 0.10.0'}
+    deprecated: Please upgrade to consolidate v1.0.0+ as it has been modernized with several long-awaited fixes implemented. Maintenance is supported by Forward Email at https://forwardemail.net ; follow/watch https://github.com/ladjs/consolidate for updates and release changelog
+    peerDependencies:
+      arc-templates: ^0.5.3
+      atpl: '>=0.7.6'
+      babel-core: ^6.26.3
+      bracket-template: ^1.1.5
+      coffee-script: ^1.12.7
+      dot: ^1.1.3
+      dust: ^0.3.0
+      dustjs-helpers: ^1.7.4
+      dustjs-linkedin: ^2.7.5
+      eco: ^1.1.0-rc-3
+      ect: ^0.5.9
+      ejs: ^3.1.5
+      haml-coffee: ^1.14.1
+      hamlet: ^0.3.3
+      hamljs: ^0.6.2
+      handlebars: ^4.7.6
+      hogan.js: ^3.0.2
+      htmling: ^0.0.8
+      jade: ^1.11.0
+      jazz: ^0.0.18
+      jqtpl: ~1.1.0
+      just: ^0.1.8
+      liquid-node: ^3.0.1
+      liquor: ^0.0.5
+      lodash: ^4.17.20
+      marko: ^3.14.4
+      mote: ^0.2.0
+      mustache: ^4.0.1
+      nunjucks: ^3.2.2
+      plates: ~0.4.11
+      pug: ^3.0.0
+      qejs: ^3.0.5
+      ractive: ^1.3.12
+      razor-tmpl: ^1.3.1
+      react: ^16.13.1
+      react-dom: ^16.13.1
+      slm: ^2.0.0
+      squirrelly: ^5.1.0
+      swig: ^1.4.2
+      swig-templates: ^2.0.3
+      teacup: ^2.0.0
+      templayed: '>=0.2.3'
+      then-jade: '*'
+      then-pug: '*'
+      tinyliquid: ^0.2.34
+      toffee: ^0.3.6
+      twig: ^1.15.2
+      twing: ^5.0.2
+      underscore: ^1.11.0
+      vash: ^0.13.0
+      velocityjs: ^2.0.1
+      walrus: ^0.10.1
+      whiskers: ^0.4.0
+    peerDependenciesMeta:
+      arc-templates:
+        optional: true
+      atpl:
+        optional: true
+      babel-core:
+        optional: true
+      bracket-template:
+        optional: true
+      coffee-script:
+        optional: true
+      dot:
+        optional: true
+      dust:
+        optional: true
+      dustjs-helpers:
+        optional: true
+      dustjs-linkedin:
+        optional: true
+      eco:
+        optional: true
+      ect:
+        optional: true
+      ejs:
+        optional: true
+      haml-coffee:
+        optional: true
+      hamlet:
+        optional: true
+      hamljs:
+        optional: true
+      handlebars:
+        optional: true
+      hogan.js:
+        optional: true
+      htmling:
+        optional: true
+      jade:
+        optional: true
+      jazz:
+        optional: true
+      jqtpl:
+        optional: true
+      just:
+        optional: true
+      liquid-node:
+        optional: true
+      liquor:
+        optional: true
+      lodash:
+        optional: true
+      marko:
+        optional: true
+      mote:
+        optional: true
+      mustache:
+        optional: true
+      nunjucks:
+        optional: true
+      plates:
+        optional: true
+      pug:
+        optional: true
+      qejs:
+        optional: true
+      ractive:
+        optional: true
+      razor-tmpl:
+        optional: true
+      react:
+        optional: true
+      react-dom:
+        optional: true
+      slm:
+        optional: true
+      squirrelly:
+        optional: true
+      swig:
+        optional: true
+      swig-templates:
+        optional: true
+      teacup:
+        optional: true
+      templayed:
+        optional: true
+      then-jade:
+        optional: true
+      then-pug:
+        optional: true
+      tinyliquid:
+        optional: true
+      toffee:
+        optional: true
+      twig:
+        optional: true
+      twing:
+        optional: true
+      underscore:
+        optional: true
+      vash:
+        optional: true
+      velocityjs:
+        optional: true
+      walrus:
+        optional: true
+      whiskers:
+        optional: true
+
+  convert-source-map@2.0.0:
+    resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==}
+
+  copy-anything@2.0.6:
+    resolution: {integrity: sha512-1j20GZTsvKNkc4BY3NpMOM8tt///wY3FpIzozTOFO2ffuZcV61nojHXVKIy3WM+7ADCy5FVhdZYHYDdgTU0yJw==}
+
+  core-js@2.6.12:
+    resolution: {integrity: sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ==}
+    deprecated: core-js@<3.23.3 is no longer maintained and not recommended for usage due to the number of issues. Because of the V8 engine whims, feature detection in old core-js versions could cause a slowdown up to 100x even if nothing is polyfilled. Some versions have web compatibility issues. Please, upgrade your dependencies to the actual version of core-js.
+
+  core-js@3.41.0:
+    resolution: {integrity: sha512-SJ4/EHwS36QMJd6h/Rg+GyR4A5xE0FSI3eZ+iBVpfqf1x0eTSg1smWLHrA+2jQThZSh97fmSgFSU8B61nxosxA==}
+
+  core-util-is@1.0.3:
+    resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==}
+
+  crc-32@1.2.2:
+    resolution: {integrity: sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==}
+    engines: {node: '>=0.8'}
+    hasBin: true
+
+  crc32-stream@4.0.3:
+    resolution: {integrity: sha512-NT7w2JVU7DFroFdYkeq8cywxrgjPHWkdX1wjpRQXPX5Asews3tA+Ght6lddQO5Mkumffp3X7GEqku3epj2toIw==}
+    engines: {node: '>= 10'}
+
+  css-line-break@2.1.0:
+    resolution: {integrity: sha512-FHcKFCZcAha3LwfVBhCQbW2nCNbkZXn7KVUJcsT5/P8YmfsVja0FMPJr0B903j/E69HUphKiV9iQArX8SDYA4w==}
+
+  cssesc@3.0.0:
+    resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==}
+    engines: {node: '>=4'}
+    hasBin: true
+
+  csstype@3.1.3:
+    resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==}
+
+  dayjs@1.11.13:
+    resolution: {integrity: sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg==}
+
+  de-indent@1.0.2:
+    resolution: {integrity: sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg==}
+
+  debug@4.4.0:
+    resolution: {integrity: sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==}
+    engines: {node: '>=6.0'}
+    peerDependencies:
+      supports-color: '*'
+    peerDependenciesMeta:
+      supports-color:
+        optional: true
+
+  deepmerge@1.5.2:
+    resolution: {integrity: sha512-95k0GDqvBjZavkuvzx/YqVLv/6YYa17fz6ILMSf7neqQITCPbnfEnQvEgMPNjH4kgobe7+WIL0yJEHku+H3qtQ==}
+    engines: {node: '>=0.10.0'}
+
+  deepmerge@4.3.1:
+    resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==}
+    engines: {node: '>=0.10.0'}
+
+  delayed-stream@1.0.0:
+    resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==}
+    engines: {node: '>=0.4.0'}
+
+  diff-sequences@26.6.2:
+    resolution: {integrity: sha512-Mv/TDa3nZ9sbc5soK+OoA74BsS3mL37yixCvUAQkiuA4Wz6YtwP/K47n2rv2ovzHZvoiQeA5FTQOschKkEwB0Q==}
+    engines: {node: '>= 10.14.2'}
+
+  docx-preview@0.3.5:
+    resolution: {integrity: sha512-nod1jG5PkvzDIiZAcgAY4gSFQzgmAAChcuZH4Hj9dj7oCzscY3Hn8NfbUv7X7Jk4xL1lfKO113JLDhWKOt6fYw==}
+
+  dompurify@2.5.8:
+    resolution: {integrity: sha512-o1vSNgrmYMQObbSSvF/1brBYEQPHhV1+gsmrusO7/GXtp1T9rCS8cXFqVxK/9crT1jA6Ccv+5MTSjBNqr7Sovw==}
+
+  dunder-proto@1.0.1:
+    resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==}
+    engines: {node: '>= 0.4'}
+
+  duplexer2@0.1.4:
+    resolution: {integrity: sha512-asLFVfWWtJ90ZyOUHMqk7/S2w2guQKxUI2itj3d92ADHhxUSbCMGi1f1cBcJ7xM1To+pE/Khbwo1yuNbMEPKeA==}
+
+  easings-css@1.0.0:
+    resolution: {integrity: sha512-7Uq7NdazNfVtr0RNmPAys8it0zKCuaqxJStYKEl72D3j4gbvXhhaM7iWNbqhA4C94ygCye6VuyhzBRQC4szeBg==}
+
+  electron-to-chromium@1.5.134:
+    resolution: {integrity: sha512-zSwzrLg3jNP3bwsLqWHmS5z2nIOQ5ngMnfMZOWWtXnqqQkPVyOipxK98w+1beLw1TB+EImPNcG8wVP/cLVs2Og==}
+
+  element-ui@2.15.14:
+    resolution: {integrity: sha512-2v9fHL0ZGINotOlRIAJD5YuVB8V7WKxrE9Qy7dXhRipa035+kF7WuU/z+tEmLVPBcJ0zt8mOu1DKpWcVzBK8IA==}
+    peerDependencies:
+      vue: ^2.5.17
+
+  encoding@0.1.13:
+    resolution: {integrity: sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==}
+
+  end-of-stream@1.4.4:
+    resolution: {integrity: sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==}
+
+  entities@1.1.2:
+    resolution: {integrity: sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==}
+
+  errno@0.1.8:
+    resolution: {integrity: sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A==}
+    hasBin: true
+
+  es-define-property@1.0.1:
+    resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==}
+    engines: {node: '>= 0.4'}
+
+  es-errors@1.3.0:
+    resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==}
+    engines: {node: '>= 0.4'}
+
+  es-object-atoms@1.1.1:
+    resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==}
+    engines: {node: '>= 0.4'}
+
+  es-set-tostringtag@2.1.0:
+    resolution: {integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==}
+    engines: {node: '>= 0.4'}
+
+  esbuild-android-64@0.14.54:
+    resolution: {integrity: sha512-Tz2++Aqqz0rJ7kYBfz+iqyE3QMycD4vk7LBRyWaAVFgFtQ/O8EJOnVmTOiDWYZ/uYzB4kvP+bqejYdVKzE5lAQ==}
+    engines: {node: '>=12'}
+    cpu: [x64]
+    os: [android]
+
+  esbuild-android-arm64@0.14.54:
+    resolution: {integrity: sha512-F9E+/QDi9sSkLaClO8SOV6etqPd+5DgJje1F9lOWoNncDdOBL2YF59IhsWATSt0TLZbYCf3pNlTHvVV5VfHdvg==}
+    engines: {node: '>=12'}
+    cpu: [arm64]
+    os: [android]
+
+  esbuild-darwin-64@0.14.54:
+    resolution: {integrity: sha512-jtdKWV3nBviOd5v4hOpkVmpxsBy90CGzebpbO9beiqUYVMBtSc0AL9zGftFuBon7PNDcdvNCEuQqw2x0wP9yug==}
+    engines: {node: '>=12'}
+    cpu: [x64]
+    os: [darwin]
+
+  esbuild-darwin-arm64@0.14.54:
+    resolution: {integrity: sha512-OPafJHD2oUPyvJMrsCvDGkRrVCar5aVyHfWGQzY1dWnzErjrDuSETxwA2HSsyg2jORLY8yBfzc1MIpUkXlctmw==}
+    engines: {node: '>=12'}
+    cpu: [arm64]
+    os: [darwin]
+
+  esbuild-freebsd-64@0.14.54:
+    resolution: {integrity: sha512-OKwd4gmwHqOTp4mOGZKe/XUlbDJ4Q9TjX0hMPIDBUWWu/kwhBAudJdBoxnjNf9ocIB6GN6CPowYpR/hRCbSYAg==}
+    engines: {node: '>=12'}
+    cpu: [x64]
+    os: [freebsd]
+
+  esbuild-freebsd-arm64@0.14.54:
+    resolution: {integrity: sha512-sFwueGr7OvIFiQT6WeG0jRLjkjdqWWSrfbVwZp8iMP+8UHEHRBvlaxL6IuKNDwAozNUmbb8nIMXa7oAOARGs1Q==}
+    engines: {node: '>=12'}
+    cpu: [arm64]
+    os: [freebsd]
+
+  esbuild-linux-32@0.14.54:
+    resolution: {integrity: sha512-1ZuY+JDI//WmklKlBgJnglpUL1owm2OX+8E1syCD6UAxcMM/XoWd76OHSjl/0MR0LisSAXDqgjT3uJqT67O3qw==}
+    engines: {node: '>=12'}
+    cpu: [ia32]
+    os: [linux]
+
+  esbuild-linux-64@0.14.54:
+    resolution: {integrity: sha512-EgjAgH5HwTbtNsTqQOXWApBaPVdDn7XcK+/PtJwZLT1UmpLoznPd8c5CxqsH2dQK3j05YsB3L17T8vE7cp4cCg==}
+    engines: {node: '>=12'}
+    cpu: [x64]
+    os: [linux]
+
+  esbuild-linux-arm64@0.14.54:
+    resolution: {integrity: sha512-WL71L+0Rwv+Gv/HTmxTEmpv0UgmxYa5ftZILVi2QmZBgX3q7+tDeOQNqGtdXSdsL8TQi1vIaVFHUPDe0O0kdig==}
+    engines: {node: '>=12'}
+    cpu: [arm64]
+    os: [linux]
+
+  esbuild-linux-arm@0.14.54:
+    resolution: {integrity: sha512-qqz/SjemQhVMTnvcLGoLOdFpCYbz4v4fUo+TfsWG+1aOu70/80RV6bgNpR2JCrppV2moUQkww+6bWxXRL9YMGw==}
+    engines: {node: '>=12'}
+    cpu: [arm]
+    os: [linux]
+
+  esbuild-linux-mips64le@0.14.54:
+    resolution: {integrity: sha512-qTHGQB8D1etd0u1+sB6p0ikLKRVuCWhYQhAHRPkO+OF3I/iSlTKNNS0Lh2Oc0g0UFGguaFZZiPJdJey3AGpAlw==}
+    engines: {node: '>=12'}
+    cpu: [mips64el]
+    os: [linux]
+
+  esbuild-linux-ppc64le@0.14.54:
+    resolution: {integrity: sha512-j3OMlzHiqwZBDPRCDFKcx595XVfOfOnv68Ax3U4UKZ3MTYQB5Yz3X1mn5GnodEVYzhtZgxEBidLWeIs8FDSfrQ==}
+    engines: {node: '>=12'}
+    cpu: [ppc64]
+    os: [linux]
+
+  esbuild-linux-riscv64@0.14.54:
+    resolution: {integrity: sha512-y7Vt7Wl9dkOGZjxQZnDAqqn+XOqFD7IMWiewY5SPlNlzMX39ocPQlOaoxvT4FllA5viyV26/QzHtvTjVNOxHZg==}
+    engines: {node: '>=12'}
+    cpu: [riscv64]
+    os: [linux]
+
+  esbuild-linux-s390x@0.14.54:
+    resolution: {integrity: sha512-zaHpW9dziAsi7lRcyV4r8dhfG1qBidQWUXweUjnw+lliChJqQr+6XD71K41oEIC3Mx1KStovEmlzm+MkGZHnHA==}
+    engines: {node: '>=12'}
+    cpu: [s390x]
+    os: [linux]
+
+  esbuild-netbsd-64@0.14.54:
+    resolution: {integrity: sha512-PR01lmIMnfJTgeU9VJTDY9ZerDWVFIUzAtJuDHwwceppW7cQWjBBqP48NdeRtoP04/AtO9a7w3viI+PIDr6d+w==}
+    engines: {node: '>=12'}
+    cpu: [x64]
+    os: [netbsd]
+
+  esbuild-openbsd-64@0.14.54:
+    resolution: {integrity: sha512-Qyk7ikT2o7Wu76UsvvDS5q0amJvmRzDyVlL0qf5VLsLchjCa1+IAvd8kTBgUxD7VBUUVgItLkk609ZHUc1oCaw==}
+    engines: {node: '>=12'}
+    cpu: [x64]
+    os: [openbsd]
+
+  esbuild-sunos-64@0.14.54:
+    resolution: {integrity: sha512-28GZ24KmMSeKi5ueWzMcco6EBHStL3B6ubM7M51RmPwXQGLe0teBGJocmWhgwccA1GeFXqxzILIxXpHbl9Q/Kw==}
+    engines: {node: '>=12'}
+    cpu: [x64]
+    os: [sunos]
+
+  esbuild-windows-32@0.14.54:
+    resolution: {integrity: sha512-T+rdZW19ql9MjS7pixmZYVObd9G7kcaZo+sETqNH4RCkuuYSuv9AGHUVnPoP9hhuE1WM1ZimHz1CIBHBboLU7w==}
+    engines: {node: '>=12'}
+    cpu: [ia32]
+    os: [win32]
+
+  esbuild-windows-64@0.14.54:
+    resolution: {integrity: sha512-AoHTRBUuYwXtZhjXZbA1pGfTo8cJo3vZIcWGLiUcTNgHpJJMC1rVA44ZereBHMJtotyN71S8Qw0npiCIkW96cQ==}
+    engines: {node: '>=12'}
+    cpu: [x64]
+    os: [win32]
+
+  esbuild-windows-arm64@0.14.54:
+    resolution: {integrity: sha512-M0kuUvXhot1zOISQGXwWn6YtS+Y/1RT9WrVIOywZnJHo3jCDyewAc79aKNQWFCQm+xNHVTq9h8dZKvygoXQQRg==}
+    engines: {node: '>=12'}
+    cpu: [arm64]
+    os: [win32]
+
+  esbuild@0.14.54:
+    resolution: {integrity: sha512-Cy9llcy8DvET5uznocPyqL3BFRrFXSVqbgpMJ9Wz8oVjZlh/zUSNbPRbov0VX7VxN2JH1Oa0uNxZ7eLRb62pJA==}
+    engines: {node: '>=12'}
+    hasBin: true
+
+  escalade@3.2.0:
+    resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==}
+    engines: {node: '>=6'}
+
+  escape-string-regexp@1.0.5:
+    resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==}
+    engines: {node: '>=0.8.0'}
+
+  estree-walker@2.0.2:
+    resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==}
+
+  exceljs@4.4.0:
+    resolution: {integrity: sha512-XctvKaEMaj1Ii9oDOqbW/6e1gXknSY4g/aLCDicOXqBE4M0nRWkUu0PTp++UPNzoFY12BNHMfs/VadKIS6llvg==}
+    engines: {node: '>=8.3.0'}
+
+  external-editor@2.2.0:
+    resolution: {integrity: sha512-bSn6gvGxKt+b7+6TKEv1ZycHleA7aHhRHyAqJyp5pbUFuYYNIzpZnQDk7AsYckyWdEnTeAnay0aCy2aV6iTk9A==}
+    engines: {node: '>=0.12'}
+
+  fast-csv@4.3.6:
+    resolution: {integrity: sha512-2RNSpuwwsJGP0frGsOmTb9oUF+VkFSM4SyLTDgwf2ciHWTarN0lQTC+F2f/t5J9QjW+c65VFIAAu85GsvMIusw==}
+    engines: {node: '>=10.0.0'}
+
+  figures@2.0.0:
+    resolution: {integrity: sha512-Oa2M9atig69ZkfwiApY8F2Yy+tzMbazyvqv21R0NsSC8floSOC09BbT1ITWAdoMGQvJ/aZnR1KMwdx9tvHnTNA==}
+    engines: {node: '>=4'}
+
+  file-saver@2.0.5:
+    resolution: {integrity: sha512-P9bmyZ3h/PRG+Nzga+rbdI4OEpNDzAVyy74uVO9ATgzLK6VtAsYybF/+TOCvrc0MO793d6+42lLyZTw7/ArVzA==}
+
+  follow-redirects@1.15.9:
+    resolution: {integrity: sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==}
+    engines: {node: '>=4.0'}
+    peerDependencies:
+      debug: '*'
+    peerDependenciesMeta:
+      debug:
+        optional: true
+
+  form-data@4.0.2:
+    resolution: {integrity: sha512-hGfm/slu0ZabnNt4oaRZ6uREyfCj6P4fT/n6A1rGV+Z0VdGXjfOhVUpkn6qVQONHGIFwmveGXyDs75+nr6FM8w==}
+    engines: {node: '>= 6'}
+
+  frac@1.1.2:
+    resolution: {integrity: sha512-w/XBfkibaTl3YDqASwfDUqkna4Z2p9cFSr1aHDt0WoMTECnRfBOv2WArlZILlqgWlmdIlALXGpM2AOhEk5W3IA==}
+    engines: {node: '>=0.8'}
+
+  fs-constants@1.0.0:
+    resolution: {integrity: sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==}
+
+  fs-extra@10.1.0:
+    resolution: {integrity: sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==}
+    engines: {node: '>=12'}
+
+  fs.realpath@1.0.0:
+    resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==}
+
+  fsevents@2.3.3:
+    resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==}
+    engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
+    os: [darwin]
+
+  fstream@1.0.12:
+    resolution: {integrity: sha512-WvJ193OHa0GHPEL+AycEJgxvBEwyfRkN1vhjca23OaPVMCaLCXTd5qAu82AjTcgP1UJmytkOKb63Ypde7raDIg==}
+    engines: {node: '>=0.6'}
+    deprecated: This package is no longer supported.
+
+  function-bind@1.1.2:
+    resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==}
+
+  fuzzysearch@1.0.3:
+    resolution: {integrity: sha512-s+kNWQuI3mo9OALw0HJ6YGmMbLqEufCh2nX/zzV5CrICQ/y4AwPxM+6TIiF9ItFCHXFCyM/BfCCmN57NTIJuPg==}
+
+  gensync@1.0.0-beta.2:
+    resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==}
+    engines: {node: '>=6.9.0'}
+
+  get-intrinsic@1.3.0:
+    resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==}
+    engines: {node: '>= 0.4'}
+
+  get-proto@1.0.1:
+    resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==}
+    engines: {node: '>= 0.4'}
+
+  glob@7.2.3:
+    resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==}
+    deprecated: Glob versions prior to v9 are no longer supported
+
+  globals@11.12.0:
+    resolution: {integrity: sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==}
+    engines: {node: '>=4'}
+
+  gopd@1.2.0:
+    resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==}
+    engines: {node: '>= 0.4'}
+
+  graceful-fs@4.2.11:
+    resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==}
+
+  handsontable@11.1.0:
+    resolution: {integrity: sha512-Ph0avovpqLy+1U0+U+ptvfPDMLYWkxfBXwJU5mT+ql475tAM1CuwhFlY8MH6iRAxrqWJdCDOzytIgIrbGrfjMQ==}
+
+  has-ansi@2.0.0:
+    resolution: {integrity: sha512-C8vBJ8DwUCx19vhm7urhTuUsr4/IyP6l4VzNQDv+ryHQObW3TTTp9yB68WpYgRe2bbaGuZ/se74IqFeVnMnLZg==}
+    engines: {node: '>=0.10.0'}
+
+  has-flag@4.0.0:
+    resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==}
+    engines: {node: '>=8'}
+
+  has-symbols@1.1.0:
+    resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==}
+    engines: {node: '>= 0.4'}
+
+  has-tostringtag@1.0.2:
+    resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==}
+    engines: {node: '>= 0.4'}
+
+  hash-sum@1.0.2:
+    resolution: {integrity: sha512-fUs4B4L+mlt8/XAtSOGMUO1TXmAelItBPtJG7CyHJfYTdDjwisntGO2JQz7oUsatOY9o68+57eziUVNw/mRHmA==}
+
+  hash-sum@2.0.0:
+    resolution: {integrity: sha512-WdZTbAByD+pHfl/g9QSsBIIwy8IT+EsPiKDs0KNX+zSHhdDLFKdZu0BQHljvO+0QI/BasbMSUa8wYNCZTvhslg==}
+
+  hasown@2.0.2:
+    resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==}
+    engines: {node: '>= 0.4'}
+
+  he@1.2.0:
+    resolution: {integrity: sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==}
+    hasBin: true
+
+  html-tags@2.0.0:
+    resolution: {integrity: sha512-+Il6N8cCo2wB/Vd3gqy/8TZhTD3QvcVeQLCnZiGkGCH3JP28IgGAY41giccp2W4R3jfyJPAP318FQTa1yU7K7g==}
+    engines: {node: '>=4'}
+
+  html2canvas@1.4.1:
+    resolution: {integrity: sha512-fPU6BHNpsyIhr8yyMpTLLxAbkaK8ArIBcmZIRiBLiDhjeqvXolaEmDGmELFuX9I4xDcaKKcJl+TKZLqruBbmWA==}
+    engines: {node: '>=8.0.0'}
+
+  hyperformula@1.3.1:
+    resolution: {integrity: sha512-9CdvkJSAy74PLaLQZf6mnvJGncE+ur2tmuZ6wB86E8M+A1hM9HeAJbl+6X3WjeZyvHle6UMV8b0uSNkjGxecKQ==}
+
+  iconv-lite@0.4.24:
+    resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==}
+    engines: {node: '>=0.10.0'}
+
+  iconv-lite@0.6.3:
+    resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==}
+    engines: {node: '>=0.10.0'}
+
+  ieee754@1.2.1:
+    resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==}
+
+  image-size@0.5.5:
+    resolution: {integrity: sha512-6TDAlDPZxUFCv+fuOkIoXT/V/f3Qbq8e37p+YOiYrUv3v9cc3/6x78VdfPgFVaB9dZYeLUfKgHRebpkm/oP2VQ==}
+    engines: {node: '>=0.10.0'}
+    hasBin: true
+
+  immediate@3.0.6:
+    resolution: {integrity: sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==}
+
+  inflight@1.0.6:
+    resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==}
+    deprecated: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.
+
+  inherits@2.0.4:
+    resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==}
+
+  inquirer@3.0.6:
+    resolution: {integrity: sha512-thluxTGBXUGb8DuQcvH9/CM/CrcGyB5xUpWc9x6Slqcq1z/hRr2a6KxUpX4ddRfmbe0hg3E4jTvo5833aWz3BA==}
+
+  is-core-module@2.16.1:
+    resolution: {integrity: sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==}
+    engines: {node: '>= 0.4'}
+
+  is-fullwidth-code-point@2.0.0:
+    resolution: {integrity: sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==}
+    engines: {node: '>=4'}
+
+  is-promise@2.2.2:
+    resolution: {integrity: sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ==}
+
+  is-stream@1.1.0:
+    resolution: {integrity: sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ==}
+    engines: {node: '>=0.10.0'}
+
+  is-what@3.14.1:
+    resolution: {integrity: sha512-sNxgpk9793nzSs7bA6JQJGeIuRBQhAaNGG77kzYQgMkrID+lS6SlK07K5LaptscDlSaIgH+GPFzf+d75FVxozA==}
+
+  isarray@1.0.0:
+    resolution: {integrity: sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==}
+
+  jest-diff@26.6.2:
+    resolution: {integrity: sha512-6m+9Z3Gv9wN0WFVasqjCL/06+EFCMTqDEUl/b87HYK2rAPTyfz4ZIuSlPhY51PIQRWx5TaxeF1qmXKe9gfN3sA==}
+    engines: {node: '>= 10.14.2'}
+
+  jest-get-type@26.3.0:
+    resolution: {integrity: sha512-TpfaviN1R2pQWkIihlfEanwOXK0zcxrKEE4MlU6Tn7keoXdN6/3gK/xl0yEh8DOunn5pOVGKf8hB4R9gVh04ig==}
+    engines: {node: '>= 10.14.2'}
+
+  js-cookie@3.0.5:
+    resolution: {integrity: sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw==}
+    engines: {node: '>=14'}
+
+  js-tokens@4.0.0:
+    resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==}
+
+  jsesc@3.1.0:
+    resolution: {integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==}
+    engines: {node: '>=6'}
+    hasBin: true
+
+  json5@2.2.3:
+    resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==}
+    engines: {node: '>=6'}
+    hasBin: true
+
+  jsonfile@6.1.0:
+    resolution: {integrity: sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==}
+
+  jszip@3.10.1:
+    resolution: {integrity: sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==}
+
+  lazystream@1.0.1:
+    resolution: {integrity: sha512-b94GiNHQNy6JNTrt5w6zNyffMrNkXZb3KTkCZJb2V1xaEGCk093vkZ2jk3tpaeP33/OiXC+WvK9AxUebnf5nbw==}
+    engines: {node: '>= 0.6.3'}
+
+  less@4.3.0:
+    resolution: {integrity: sha512-X9RyH9fvemArzfdP8Pi3irr7lor2Ok4rOttDXBhlwDg+wKQsXOXgHWduAJE1EsF7JJx0w0bcO6BC6tCKKYnXKA==}
+    engines: {node: '>=14'}
+    hasBin: true
+
+  lie@3.3.0:
+    resolution: {integrity: sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==}
+
+  linkify-it@2.2.0:
+    resolution: {integrity: sha512-GnAl/knGn+i1U/wjBz3akz2stz+HrHLsxMwHQGofCDfPvlf+gDKN58UtfmUquTY4/MXeE2x7k19KQmeoZi94Iw==}
+
+  listenercount@1.0.1:
+    resolution: {integrity: sha512-3mk/Zag0+IJxeDrxSgaDPy4zZ3w05PRZeJNnlWhzFz5OkX49J4krc+A8X2d2M69vGMBEX0uyl8M+W+8gH+kBqQ==}
+
+  lodash-es@4.17.21:
+    resolution: {integrity: sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==}
+
+  lodash.defaults@4.2.0:
+    resolution: {integrity: sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==}
+
+  lodash.difference@4.5.0:
+    resolution: {integrity: sha512-dS2j+W26TQ7taQBGN8Lbbq04ssV3emRw4NY58WErlTO29pIqS0HmoT5aJ9+TUQ1N3G+JOZSji4eugsWwGp9yPA==}
+
+  lodash.escaperegexp@4.1.2:
+    resolution: {integrity: sha512-TM9YBvyC84ZxE3rgfefxUWiQKLilstD6k7PTGt6wfbtXF8ixIJLOL3VYyV/z+ZiPLsVxAsKAFVwWlWeb2Y8Yyw==}
+
+  lodash.flatten@4.4.0:
+    resolution: {integrity: sha512-C5N2Z3DgnnKr0LOpv/hKCgKdb7ZZwafIrsesve6lmzvZIRZRGaZ/l6Q8+2W7NaT+ZwO3fFlSCzCzrDCFdJfZ4g==}
+
+  lodash.groupby@4.6.0:
+    resolution: {integrity: sha512-5dcWxm23+VAoz+awKmBaiBvzox8+RqMgFhi7UvX9DHZr2HdxHXM/Wrf8cfKpsW37RNrvtPn6hSwNqurSILbmJw==}
+
+  lodash.isboolean@3.0.3:
+    resolution: {integrity: sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==}
+
+  lodash.isequal@4.5.0:
+    resolution: {integrity: sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==}
+    deprecated: This package is deprecated. Use require('node:util').isDeepStrictEqual instead.
+
+  lodash.isfunction@3.0.9:
+    resolution: {integrity: sha512-AirXNj15uRIMMPihnkInB4i3NHeb4iBtNg9WRWuK2o31S+ePwwNmDPaTL3o7dTJ+VXNZim7rFs4rxN4YU1oUJw==}
+
+  lodash.isnil@4.0.0:
+    resolution: {integrity: sha512-up2Mzq3545mwVnMhTDMdfoG1OurpA/s5t88JmQX809eH3C8491iu2sfKhTfhQtKY78oPNhiaHJUpT/dUDAAtng==}
+
+  lodash.isplainobject@4.0.6:
+    resolution: {integrity: sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==}
+
+  lodash.isundefined@3.0.1:
+    resolution: {integrity: sha512-MXB1is3s899/cD8jheYYE2V9qTHwKvt+npCwpD+1Sxm3Q3cECXCiYHjeHWXNwr6Q0SOBPrYUDxendrO6goVTEA==}
+
+  lodash.kebabcase@4.1.1:
+    resolution: {integrity: sha512-N8XRTIMMqqDgSy4VLKPnJ/+hpGZN+PHQiJnSenYqPaVV/NCqEogTnAdZLQiGKhxX+JCs8waWq2t1XHWKOmlY8g==}
+
+  lodash.union@4.6.0:
+    resolution: {integrity: sha512-c4pB2CdGrGdjMKYLA+XiRDO7Y0PRQbm/Gzg8qMj+QH+pFVAoTp5sBpO0odL3FjoPCGjK96p6qsP+yQoiLoOBcw==}
+
+  lodash.uniq@4.5.0:
+    resolution: {integrity: sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==}
+
+  lodash@4.17.21:
+    resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==}
+
+  lru-cache@4.1.5:
+    resolution: {integrity: sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==}
+
+  lru-cache@5.1.1:
+    resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==}
+
+  magic-string@0.26.7:
+    resolution: {integrity: sha512-hX9XH3ziStPoPhJxLq1syWuZMxbDvGNbVchfrdCtanC7D13888bMFow61x8axrx+GfHLtVeAx2kxL7tTGRl+Ow==}
+    engines: {node: '>=12'}
+
+  make-dir@2.1.0:
+    resolution: {integrity: sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==}
+    engines: {node: '>=6'}
+
+  markdown-it@8.4.2:
+    resolution: {integrity: sha512-GcRz3AWTqSUphY3vsUqQSFMbgR38a4Lh3GWlHRh/7MRwz8mcu9n2IO7HOh+bXHrR9kOPDl5RNCaEsrneb+xhHQ==}
+    hasBin: true
+
+  material-colors@1.2.6:
+    resolution: {integrity: sha512-6qE4B9deFBIa9YSpOc9O0Sgc43zTeVYbgDT5veRKSlB2+ZuHNoVVxA1L/ckMUayV9Ay9y7Z/SZCLcGteW9i7bg==}
+
+  math-intrinsics@1.1.0:
+    resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==}
+    engines: {node: '>= 0.4'}
+
+  mdurl@1.0.1:
+    resolution: {integrity: sha512-/sKlQJCBYVY9Ers9hqzKou4H6V5UWc/M59TH2dvkt+84itfnq7uFOMLpOiOS4ujvHP4etln18fmIxA5R5fll0g==}
+
+  merge-source-map@1.1.0:
+    resolution: {integrity: sha512-Qkcp7P2ygktpMPh2mCQZaf3jhN6D3Z/qVZHSdWvQ+2Ef5HgRAPBO57A77+ENm0CPx2+1Ce/MYKi3ymqdfuqibw==}
+
+  mime-db@1.52.0:
+    resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==}
+    engines: {node: '>= 0.6'}
+
+  mime-types@2.1.35:
+    resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==}
+    engines: {node: '>= 0.6'}
+
+  mime@1.6.0:
+    resolution: {integrity: sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==}
+    engines: {node: '>=4'}
+    hasBin: true
+
+  mimic-fn@1.2.0:
+    resolution: {integrity: sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==}
+    engines: {node: '>=4'}
+
+  minimatch@3.1.2:
+    resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==}
+
+  minimatch@5.1.6:
+    resolution: {integrity: sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==}
+    engines: {node: '>=10'}
+
+  minimist@1.2.0:
+    resolution: {integrity: sha512-7Wl+Jz+IGWuSdgsQEJ4JunV0si/iMhg42MnQQG6h1R6TNeVenp4U9x5CC5v/gYqz/fENLQITAWXidNtVL0NNbw==}
+
+  minimist@1.2.8:
+    resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==}
+
+  mkdirp@0.5.6:
+    resolution: {integrity: sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==}
+    hasBin: true
+
+  moment@2.24.0:
+    resolution: {integrity: sha512-bV7f+6l2QigeBBZSM/6yTNq4P2fNpSWj/0e7jQcy87A8e7o2nAfP/34/2ky5Vw4B9S446EtIhodAzkFCcR4dQg==}
+
+  moment@2.30.1:
+    resolution: {integrity: sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==}
+
+  ms@2.1.3:
+    resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==}
+
+  mute-stream@0.0.7:
+    resolution: {integrity: sha512-r65nCZhrbXXb6dXOACihYApHw2Q6pV0M3V0PSxd74N0+D8nzAdEAITq2oAjA1jVnKI+tGvEBUpqiMh0+rW6zDQ==}
+
+  nanoid@3.3.11:
+    resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==}
+    engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
+    hasBin: true
+
+  needle@3.3.1:
+    resolution: {integrity: sha512-6k0YULvhpw+RoLNiQCRKOl09Rv1dPLr8hHnVjHqdolKwDrdNyk+Hmrthi4lIGPPz3r39dLx0hsF5s40sZ3Us4Q==}
+    engines: {node: '>= 4.4.x'}
+    hasBin: true
+
+  node-dir@0.1.17:
+    resolution: {integrity: sha512-tmPX422rYgofd4epzrNoOXiE8XFZYOcCq1vD7MAXCDO+O+zndlA2ztdKKMa+EeuBG5tHETpr4ml4RGgpqDCCAg==}
+    engines: {node: '>= 0.10.5'}
+
+  node-fetch@1.6.3:
+    resolution: {integrity: sha512-BDxbhLHXFFFvilHjh9xihcDyPkXQ+kjblxnl82zAX41xUYSNvuRpFRznmldR9+OKu+p+ULZ7hNoyunlLB5ecUA==}
+
+  node-releases@2.0.19:
+    resolution: {integrity: sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==}
+
+  normalize-path@3.0.0:
+    resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==}
+    engines: {node: '>=0.10.0'}
+
+  normalize-wheel@1.0.1:
+    resolution: {integrity: sha512-1OnlAPZ3zgrk8B91HyRj+eVv+kS5u+Z0SCsak6Xil/kmgEia50ga7zfkumayonZrImffAxPU/5WcyGhzetHNPA==}
+
+  numbro@2.1.2:
+    resolution: {integrity: sha512-7w833BxZmKGLE9HI0aREtNVRVH6WTYUUlWf4qgA5gKNhPQ4F/MRZ14sc0v8eoLORprk9ZTVwYaLwj8N3Zgxwiw==}
+
+  object-assign@4.1.1:
+    resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==}
+    engines: {node: '>=0.10.0'}
+
+  once@1.4.0:
+    resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==}
+
+  onetime@2.0.1:
+    resolution: {integrity: sha512-oyyPpiMaKARvvcgip+JV+7zci5L8D1W9RZIz2l1o08AM3pfspitVWnPt3mzHcBPp12oYMTy0pqrFs/C+m3EwsQ==}
+    engines: {node: '>=4'}
+
+  opencollective-postinstall@2.0.3:
+    resolution: {integrity: sha512-8AV/sCtuzUeTo8gQK5qDZzARrulB3egtLzFgteqB2tcT4Mw7B8Kt7JcDHmltjz6FOAHsvTevk70gZEbhM4ZS9Q==}
+    hasBin: true
+
+  opencollective@1.0.3:
+    resolution: {integrity: sha512-YBRI0Qa8+Ui0/STV1qYuPrJm889PT3oCPHMVoL+8Y3nwCffj7PSrB2NlGgrhgBKDujxTjxknHWJ/FiqOsYcIDw==}
+    hasBin: true
+
+  opn@4.0.2:
+    resolution: {integrity: sha512-iPBWbPP4OEOzR1xfhpGLDh+ypKBOygunZhM9jBtA7FS5sKjEiMZw0EFb82hnDOmTZX90ZWLoZKUza4cVt8MexA==}
+    engines: {node: '>=0.10.0'}
+
+  os-tmpdir@1.0.2:
+    resolution: {integrity: sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==}
+    engines: {node: '>=0.10.0'}
+
+  pako@1.0.11:
+    resolution: {integrity: sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==}
+
+  parse-node-version@1.0.1:
+    resolution: {integrity: sha512-3YHlOa/JgH6Mnpr05jP9eDG254US9ek25LyIxZlDItp2iJtwyaXQb57lBYLdT3MowkUFYEV2XXNAYIPlESvJlA==}
+    engines: {node: '>= 0.10'}
+
+  path-is-absolute@1.0.1:
+    resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==}
+    engines: {node: '>=0.10.0'}
+
+  path-parse@1.0.7:
+    resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==}
+
+  pdfjs-dist@2.4.456:
+    resolution: {integrity: sha512-yckJEHq3F48hcp6wStEpbN9McOj328Ib09UrBlGAKxvN2k+qYPN5iq6TH6jD1C0pso7zTep+g/CKsYgdrQd5QA==}
+
+  picocolors@0.2.1:
+    resolution: {integrity: sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==}
+
+  picocolors@1.1.1:
+    resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==}
+
+  picomatch@2.3.1:
+    resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==}
+    engines: {node: '>=8.6'}
+
+  pify@4.0.1:
+    resolution: {integrity: sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==}
+    engines: {node: '>=6'}
+
+  pikaday@1.8.0:
+    resolution: {integrity: sha512-SgGxMYX0NHj9oQnMaSyAipr2gOrbB4Lfs/TJTb6H6hRHs39/5c5VZi73Q8hr53+vWjdn6HzkWcj8Vtl3c9ziaA==}
+
+  pinkie-promise@2.0.1:
+    resolution: {integrity: sha512-0Gni6D4UcLTbv9c57DfxDGdr41XfgUjqWZu492f0cIGr16zDU06BWP/RAEvOuo7CQ0CNjHaLlM59YJJFm3NWlw==}
+    engines: {node: '>=0.10.0'}
+
+  pinkie@2.0.4:
+    resolution: {integrity: sha512-MnUuEycAemtSaeFSjXKW/aroV7akBbY+Sv+RkyqFjgAe73F+MR0TBWKBRDkmfWq/HiFmdavfZ1G7h4SPZXaCSg==}
+    engines: {node: '>=0.10.0'}
+
+  postcss-selector-parser@6.1.2:
+    resolution: {integrity: sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==}
+    engines: {node: '>=4'}
+
+  postcss@7.0.39:
+    resolution: {integrity: sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==}
+    engines: {node: '>=6.0.0'}
+
+  postcss@8.5.3:
+    resolution: {integrity: sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A==}
+    engines: {node: ^10 || ^12 || >=14}
+
+  pptxtojson@1.3.1:
+    resolution: {integrity: sha512-+FcSS70PTkgdlExXCEUoNL2DZcySueonyYpnQANeD6BW1AX8313o6zF71t5wN7tnO1cxh1p0/J90fBV5yMzTCA==}
+
+  prettier@2.8.8:
+    resolution: {integrity: sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==}
+    engines: {node: '>=10.13.0'}
+    hasBin: true
+
+  pretty-format@26.6.2:
+    resolution: {integrity: sha512-7AeGuCYNGmycyQbCqd/3PWH4eOoX/OiCa0uphp57NVTeAGdJGaAliecxwBDHYQCIvrW7aDBZCYeNTP/WX69mkg==}
+    engines: {node: '>= 10'}
+
+  process-nextick-args@2.0.1:
+    resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==}
+
+  proxy-from-env@1.1.0:
+    resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==}
+
+  prr@1.0.1:
+    resolution: {integrity: sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw==}
+
+  pseudomap@1.0.2:
+    resolution: {integrity: sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ==}
+
+  querystring@0.2.1:
+    resolution: {integrity: sha512-wkvS7mL/JMugcup3/rMitHmd9ecIGd2lhFhK9N3UUQ450h66d1r3Y9nvXzQAW1Lq+wyx61k/1pfKS5KuKiyEbg==}
+    engines: {node: '>=0.4.x'}
+    deprecated: The querystring API is considered Legacy. new code should use the URLSearchParams API instead.
+
+  react-is@17.0.2:
+    resolution: {integrity: sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==}
+
+  readable-stream@2.3.8:
+    resolution: {integrity: sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==}
+
+  readable-stream@3.6.2:
+    resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==}
+    engines: {node: '>= 6'}
+
+  readdir-glob@1.1.3:
+    resolution: {integrity: sha512-v05I2k7xN8zXvPD9N+z/uhXPaj0sUFCe2rcWZIpBsqxfP7xXFQ0tipAd/wjj1YxWyWtUS5IDJpOG82JKt2EAVA==}
+
+  regenerator-runtime@0.10.5:
+    resolution: {integrity: sha512-02YopEIhAgiBHWeoTiA8aitHDt8z6w+rQqNuIftlM+ZtvSl/brTouaU7DW6GO/cHtvxJvS4Hwv2ibKdxIRi24w==}
+
+  regenerator-runtime@0.11.1:
+    resolution: {integrity: sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==}
+
+  regenerator-runtime@0.13.11:
+    resolution: {integrity: sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==}
+
+  regenerator-runtime@0.14.1:
+    resolution: {integrity: sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==}
+
+  regexp-to-ast@0.4.0:
+    resolution: {integrity: sha512-4qf/7IsIKfSNHQXSwial1IFmfM1Cc/whNBQqRwe0V2stPe7KmN1U0tWQiIx6JiirgSrisjE0eECdNf7Tav1Ntw==}
+
+  resize-observer-polyfill@1.5.1:
+    resolution: {integrity: sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg==}
+
+  resolve@1.22.10:
+    resolution: {integrity: sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==}
+    engines: {node: '>= 0.4'}
+    hasBin: true
+
+  restore-cursor@2.0.0:
+    resolution: {integrity: sha512-6IzJLuGi4+R14vwagDHX+JrXmPVtPpn4mffDJ1UdR7/Edm87fl6yi8mMBIVvFtJaNTUvjughmW4hwLhRG7gC1Q==}
+    engines: {node: '>=4'}
+
+  rimraf@2.7.1:
+    resolution: {integrity: sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==}
+    deprecated: Rimraf versions prior to v4 are no longer supported
+    hasBin: true
+
+  rollup@2.77.3:
+    resolution: {integrity: sha512-/qxNTG7FbmefJWoeeYJFbHehJ2HNWnjkAFRKzWN/45eNBBF/r8lo992CwcJXEzyVxs5FmfId+vTSTQDb+bxA+g==}
+    engines: {node: '>=10.0.0'}
+    hasBin: true
+
+  rollup@2.79.2:
+    resolution: {integrity: sha512-fS6iqSPZDs3dr/y7Od6y5nha8dW1YnbgtsyotCVvoFGKbERG++CVRFv1meyGDE1SNItQA8BrnCw7ScdAhRJ3XQ==}
+    engines: {node: '>=10.0.0'}
+    hasBin: true
+
+  run-async@2.4.1:
+    resolution: {integrity: sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==}
+    engines: {node: '>=0.12.0'}
+
+  rx@4.1.0:
+    resolution: {integrity: sha512-CiaiuN6gapkdl+cZUr67W6I8jquN4lkak3vtIsIWCl4XIPP8ffsoyN6/+PuGXnQy8Cu8W2y9Xxh31Rq4M6wUug==}
+
+  safe-buffer@5.1.2:
+    resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==}
+
+  safer-buffer@2.1.2:
+    resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==}
+
+  sax@1.4.1:
+    resolution: {integrity: sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg==}
+
+  saxes@5.0.1:
+    resolution: {integrity: sha512-5LBh1Tls8c9xgGjw3QrMwETmTMVk0oFgvrFSvWx62llR2hcEInrKNZ2GZCCuuy2lvWrdl5jhbpeqc5hRYKFOcw==}
+    engines: {node: '>=10'}
+
+  semver@5.7.2:
+    resolution: {integrity: sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==}
+    hasBin: true
+
+  semver@6.3.1:
+    resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==}
+    hasBin: true
+
+  setimmediate@1.0.5:
+    resolution: {integrity: sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==}
+
+  signal-exit@3.0.7:
+    resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==}
+
+  slash@3.0.0:
+    resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==}
+    engines: {node: '>=8'}
+
+  source-map-js@1.2.1:
+    resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==}
+    engines: {node: '>=0.10.0'}
+
+  source-map@0.6.1:
+    resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==}
+    engines: {node: '>=0.10.0'}
+
+  source-map@0.7.4:
+    resolution: {integrity: sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==}
+    engines: {node: '>= 8'}
+
+  sourcemap-codec@1.4.8:
+    resolution: {integrity: sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==}
+    deprecated: Please use @jridgewell/sourcemap-codec instead
+
+  sprintf-js@1.0.3:
+    resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==}
+
+  ssf@0.11.2:
+    resolution: {integrity: sha512-+idbmIXoYET47hH+d7dfm2epdOMUDjqcB4648sTZ+t2JwoyBFL/insLfB/racrDmsKB3diwsDA696pZMieAC5g==}
+    engines: {node: '>=0.8'}
+
+  string-width@2.1.1:
+    resolution: {integrity: sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==}
+    engines: {node: '>=4'}
+
+  string_decoder@1.1.1:
+    resolution: {integrity: sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==}
+
+  strip-ansi@3.0.1:
+    resolution: {integrity: sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==}
+    engines: {node: '>=0.10.0'}
+
+  strip-ansi@4.0.0:
+    resolution: {integrity: sha512-4XaJ2zQdCzROZDivEVIDPkcQn8LMFSa8kj8Gxb/Lnwzv9A8VctNZ+lfivC/sV3ivW8ElJTERXZoPBRrZKkNKow==}
+    engines: {node: '>=4'}
+
+  supports-color@2.0.0:
+    resolution: {integrity: sha512-KKNVtd6pCYgPIKU4cp2733HWYCpplQhddZLBUryaAHou723x+FRzQ5Df824Fj+IyyuiQTRoub4SnIFfIcrp70g==}
+    engines: {node: '>=0.8.0'}
+
+  supports-color@7.2.0:
+    resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==}
+    engines: {node: '>=8'}
+
+  supports-preserve-symlinks-flag@1.0.0:
+    resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==}
+    engines: {node: '>= 0.4'}
+
+  svg-tags@1.0.0:
+    resolution: {integrity: sha512-ovssysQTa+luh7A5Weu3Rta6FJlFBBbInjOh722LIt6klpU2/HtdUbszju/G4devcvk8PGt7FCLv5wftu3THUA==}
+
+  tar-stream@2.2.0:
+    resolution: {integrity: sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==}
+    engines: {node: '>=6'}
+
+  text-segmentation@1.0.3:
+    resolution: {integrity: sha512-iOiPUo/BGnZ6+54OsWxZidGCsdU8YbE4PSpdPinp7DeMtUJNJBoJ/ouUSTJjHkh1KntHaltHl/gDs2FC4i5+Nw==}
+
+  throttle-debounce@1.1.0:
+    resolution: {integrity: sha512-XH8UiPCQcWNuk2LYePibW/4qL97+ZQ1AN3FNXwZRBNPPowo/NRU5fAlDCSNBJIYCKbioZfuYtMhG4quqoJhVzg==}
+    engines: {node: '>=4'}
+
+  throttle-debounce@3.0.1:
+    resolution: {integrity: sha512-dTEWWNu6JmeVXY0ZYoPuH5cRIwc0MeGbJwah9KUNYSJwommQpCzTySTpEe8Gs1J23aeWEuAobe4Ag7EHVt/LOg==}
+    engines: {node: '>=10'}
+
+  through2@3.0.2:
+    resolution: {integrity: sha512-enaDQ4MUyP2W6ZyT6EsMzqBPZaM/avg8iuo+l2d3QCs0J+6RaqkHV/2/lOwDTueBHeJ/2LG9lrLW3d5rWPucuQ==}
+
+  through@2.3.8:
+    resolution: {integrity: sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==}
+
+  tiny-emitter@2.1.0:
+    resolution: {integrity: sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q==}
+
+  tinycolor2@1.6.0:
+    resolution: {integrity: sha512-XPaBkWQJdsf3pLKJV9p4qN/S+fm2Oj8AIPo1BTUhg5oxkvm9+SVEGFdhyOz7tTdUTfvxMiAs4sp6/eZO2Ew+pw==}
+
+  tmp@0.0.33:
+    resolution: {integrity: sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==}
+    engines: {node: '>=0.6.0'}
+
+  tmp@0.2.3:
+    resolution: {integrity: sha512-nZD7m9iCPC5g0pYmcaxogYKggSfLsdxl8of3Q/oIbqCqLLIO9IAF0GWjX1z9NZRHPiXv8Wex4yDCaZsgEw0Y8w==}
+    engines: {node: '>=14.14'}
+
+  traverse@0.3.9:
+    resolution: {integrity: sha512-iawgk0hLP3SxGKDfnDJf8wTz4p2qImnyihM5Hh/sGvQ3K37dPi/w8sRhdNIxYA1TwFwc5mDhIJq+O0RsvXBKdQ==}
+
+  tslib@2.8.1:
+    resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==}
+
+  txml@5.1.1:
+    resolution: {integrity: sha512-TwMDLnXQ09enNaxybLVvKZU7rqog8LgnuAs4ZYXM0nV0eu10iLsSFwlX3AEknAXXtH1wT3CYfoiXAjyBexcmuw==}
+
+  typescript@5.7.3:
+    resolution: {integrity: sha512-84MVSjMEHP+FQRPy3pX9sTVV/INIex71s9TL2Gm5FG/WG1SqXeKyZ0k7/blY/4FdOzI12CBy1vGc4og/eus0fw==}
+    engines: {node: '>=14.17'}
+    hasBin: true
+
+  uc.micro@1.0.6:
+    resolution: {integrity: sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA==}
+
+  universalify@2.0.1:
+    resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==}
+    engines: {node: '>= 10.0.0'}
+
+  unorm@1.6.0:
+    resolution: {integrity: sha512-b2/KCUlYZUeA7JFUuRJZPUtr4gZvBh7tavtv4fvk4+KV9pfGiR6CQAQAWl49ZpR3ts2dk4FYkP7EIgDJoiOLDA==}
+    engines: {node: '>= 0.4.0'}
+
+  unzipper@0.10.14:
+    resolution: {integrity: sha512-ti4wZj+0bQTiX2KmKWuwj7lhV+2n//uXEotUmGuQqrbVZSEGFMbI68+c6JCQ8aAmUWYvtHEz2A8K6wXvueR/6g==}
+
+  update-browserslist-db@1.1.3:
+    resolution: {integrity: sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==}
+    hasBin: true
+    peerDependencies:
+      browserslist: '>= 4.21.0'
+
+  util-deprecate@1.0.2:
+    resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==}
+
+  utrie@1.0.2:
+    resolution: {integrity: sha512-1MLa5ouZiOmQzUbjbu9VmjLzn1QLXBhwpUa7kdLUQK+KQ5KA9I1vk5U4YHe/X2Ch7PYnJfWuWT+VbuxbGwljhw==}
+
+  uuid@8.3.2:
+    resolution: {integrity: sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==}
+    hasBin: true
+
+  vite-plugin-vue2@2.0.3:
+    resolution: {integrity: sha512-t3Tu93GWsMHbpeIv66MTO5e/rRAo8/+/eWoUtFYuAdKDMyEnn1dqsrXh+CfG+SJAlxJvcTP8U0eXkzhLjKNyMg==}
+    peerDependencies:
+      vite: ^2.0.0 || ^3.0.0 || ^4.0.0
+      vue-template-compiler: ^2.2.0
+
+  vite@2.9.18:
+    resolution: {integrity: sha512-sAOqI5wNM9QvSEE70W3UGMdT8cyEn0+PmJMTFvTB8wB0YbYUWw3gUbY62AOyrXosGieF2htmeLATvNxpv/zNyQ==}
+    engines: {node: '>=12.2.0'}
+    hasBin: true
+    peerDependencies:
+      less: '*'
+      sass: '*'
+      stylus: '*'
+    peerDependenciesMeta:
+      less:
+        optional: true
+      sass:
+        optional: true
+      stylus:
+        optional: true
+
+  vue-baidu-map@0.21.22:
+    resolution: {integrity: sha512-WQMPCih4UTh0AZCKKH/OVOYnyAWjfRNeK6BIeoLmscyY5aF8zzlJhz/NOHLb3mdztIpB0Z6aohn4Jd9mfCSjQw==}
+    peerDependencies:
+      vue: ^2.1.8
+
+  vue-class-component@7.2.6:
+    resolution: {integrity: sha512-+eaQXVrAm/LldalI272PpDe3+i4mPis0ORiMYxF6Ae4hyuCh15W8Idet7wPUEs4N4YptgFHGys4UrgNQOMyO6w==}
+    peerDependencies:
+      vue: ^2.0.0
+
+  vue-cropper@0.5.11:
+    resolution: {integrity: sha512-UeA3qL2BLCTGkOEAxEsxSNFO+qLYAn6YRHv4oS32cP9lMhF1vFmnAf/z+ZamtR0/Fh3sbZeZUCLVR2Ol2/dpTQ==}
+
+  vue-property-decorator@8.5.1:
+    resolution: {integrity: sha512-O6OUN2OMsYTGPvgFtXeBU3jPnX5ffQ9V4I1WfxFQ6dqz6cOUbR3Usou7kgFpfiXDvV7dJQSFcJ5yUPgOtPPm1Q==}
+    peerDependencies:
+      vue: '*'
+
+  vue-router@3.6.5:
+    resolution: {integrity: sha512-VYXZQLtjuvKxxcshuRAwjHnciqZVoXAjTjcqBTz4rKc8qih9g9pI3hbDjmqXaHdgL3v8pV6P8Z335XvHzESxLQ==}
+    peerDependencies:
+      vue: ^2
+
+  vue-template-babel-compiler@1.2.0:
+    resolution: {integrity: sha512-CScBSX1/wCdmmZ/Lvj/63p2CCVTS0FMj0F69VRBo73CuJrjvPAPGmeNJ7D/cwt/VS2PduowRWbO8N4Zh4Z3b0g==}
+    engines: {node: '>=12.0.0'}
+    peerDependencies:
+      vue-template-compiler: ^2.6.0
+
+  vue-template-compiler@2.7.16:
+    resolution: {integrity: sha512-AYbUWAJHLGGQM7+cNTELw+KsOG9nl2CnSv467WobS5Cv9uk3wFcnr1Etsz2sEIHEZvw1U+o9mRlEO6QbZvUPGQ==}
+
+  vue-template-es2015-compiler@1.9.1:
+    resolution: {integrity: sha512-4gDntzrifFnCEvyoO8PqyJDmguXgVPxKiIxrBKjIowvL9l+N66196+72XVYR8BBf1Uv1Fgt3bGevJ+sEmxfZzw==}
+
+  vue-virtual-scroll-list@2.3.5:
+    resolution: {integrity: sha512-YFK6u5yltqtAOfTBcij/KGAS2SoZvzbNIAf9qTULauPObEp53xj22tDuohrrM2vNkgoD5kejXICIUBt2Q4ZDqQ==}
+
+  vue@2.6.11:
+    resolution: {integrity: sha512-VfPwgcGABbGAue9+sfrD4PuwFar7gPb1yl1UK1MwXoQPAw0BKSqWfoYCT/ThFrdEVWoI51dBuyCoiNU9bZDZxQ==}
+    deprecated: Vue 2 has reached EOL and is no longer actively maintained. See https://v2.vuejs.org/eol/ for more details.
+
+  vue@2.7.16:
+    resolution: {integrity: sha512-4gCtFXaAA3zYZdTp5s4Hl2sozuySsgz4jy1EnpBHNfpMa9dK1ZCG7viqBPCwXtmgc8nHqUsAu3G4gtmXkkY3Sw==}
+    deprecated: Vue 2 has reached EOL and is no longer actively maintained. See https://v2.vuejs.org/eol/ for more details.
+
+  vuex@3.6.2:
+    resolution: {integrity: sha512-ETW44IqCgBpVomy520DT5jf8n0zoCac+sxWnn+hMe/CzaSejb/eVw2YToiXYX+Ex/AuHHia28vWTq4goAexFbw==}
+    peerDependencies:
+      vue: ^2.0.0
+
+  watch-size@2.0.0:
+    resolution: {integrity: sha512-M92R89dNoTPWyCD+HuUEDdhaDnh9jxPGOwlDc0u51jAgmjUvzqaEMynXSr3BaWs+QdHYk4KzibPy1TFtjLmOZQ==}
+
+  wisdom-ui@1.0.4-beta.6:
+    resolution: {integrity: sha512-7E9Q44CjpQdaxFapo0+u6gJqje7+gtTyahxamj5JgYUbuNSwdEOqPKwRNc7acWIQDRrwwsj3yrL0CCD5y2E6JQ==}
+
+  wmf@1.0.2:
+    resolution: {integrity: sha512-/p9K7bEh0Dj6WbXg4JG0xvLQmIadrner1bi45VMJTfnbVHsc7yIajZyoSoK60/dtVBs12Fm6WkUI5/3WAVsNMw==}
+    engines: {node: '>=0.8'}
+
+  word@0.3.0:
+    resolution: {integrity: sha512-OELeY0Q61OXpdUfTp+oweA/vtLVg5VDOXh+3he3PNzLGG/y0oylSOC1xRVj0+l4vQ3tj/bB1HVHv1ocXkQceFA==}
+    engines: {node: '>=0.8'}
+
+  wrappy@1.0.2:
+    resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==}
+
+  x-data-spreadsheet@1.1.9:
+    resolution: {integrity: sha512-wk7knDBYdHjtWiHUVQryZMy00dsGNCF+6wMb5ykwEFcAtBYkYZakJCOCHpEo8onC0Lb/q2gIynWpbQxA4qakyg==}
+
+  xlsx@0.18.5:
+    resolution: {integrity: sha512-dmg3LCjBPHZnQp5/F/+nnTa+miPJxUXB6vtk42YjBBKayDNagxGEeIdWApkYPOf3Z3pm3k62Knjzp7lMeTEtFQ==}
+    engines: {node: '>=0.8'}
+    hasBin: true
+
+  xmlchars@2.2.0:
+    resolution: {integrity: sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==}
+
+  yallist@2.1.2:
+    resolution: {integrity: sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A==}
+
+  yallist@3.1.1:
+    resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==}
+
+  zip-stream@4.1.1:
+    resolution: {integrity: sha512-9qv4rlDiopXg4E69k+vMHjNN63YFMe9sZMrdlvKnCjlCRWeCBswPPMPUfx+ipsAWq1LXHe70RcbaHdJJpS6hyQ==}
+    engines: {node: '>= 10'}
+
+snapshots:
+
+  '@ampproject/remapping@2.3.0':
+    dependencies:
+      '@jridgewell/gen-mapping': 0.3.8
+      '@jridgewell/trace-mapping': 0.3.25
+
+  '@babel/code-frame@7.26.2':
+    dependencies:
+      '@babel/helper-validator-identifier': 7.25.9
+      js-tokens: 4.0.0
+      picocolors: 1.1.1
+
+  '@babel/compat-data@7.26.8': {}
+
+  '@babel/core@7.26.10':
+    dependencies:
+      '@ampproject/remapping': 2.3.0
+      '@babel/code-frame': 7.26.2
+      '@babel/generator': 7.27.0
+      '@babel/helper-compilation-targets': 7.27.0
+      '@babel/helper-module-transforms': 7.26.0(@babel/core@7.26.10)
+      '@babel/helpers': 7.27.0
+      '@babel/parser': 7.27.0
+      '@babel/template': 7.27.0
+      '@babel/traverse': 7.27.0
+      '@babel/types': 7.27.0
+      convert-source-map: 2.0.0
+      debug: 4.4.0
+      gensync: 1.0.0-beta.2
+      json5: 2.2.3
+      semver: 6.3.1
+    transitivePeerDependencies:
+      - supports-color
+
+  '@babel/generator@7.27.0':
+    dependencies:
+      '@babel/parser': 7.27.0
+      '@babel/types': 7.27.0
+      '@jridgewell/gen-mapping': 0.3.8
+      '@jridgewell/trace-mapping': 0.3.25
+      jsesc: 3.1.0
+
+  '@babel/helper-annotate-as-pure@7.25.9':
+    dependencies:
+      '@babel/types': 7.27.0
+
+  '@babel/helper-compilation-targets@7.27.0':
+    dependencies:
+      '@babel/compat-data': 7.26.8
+      '@babel/helper-validator-option': 7.25.9
+      browserslist: 4.24.4
+      lru-cache: 5.1.1
+      semver: 6.3.1
+
+  '@babel/helper-create-class-features-plugin@7.27.0(@babel/core@7.26.10)':
+    dependencies:
+      '@babel/core': 7.26.10
+      '@babel/helper-annotate-as-pure': 7.25.9
+      '@babel/helper-member-expression-to-functions': 7.25.9
+      '@babel/helper-optimise-call-expression': 7.25.9
+      '@babel/helper-replace-supers': 7.26.5(@babel/core@7.26.10)
+      '@babel/helper-skip-transparent-expression-wrappers': 7.25.9
+      '@babel/traverse': 7.27.0
+      semver: 6.3.1
+    transitivePeerDependencies:
+      - supports-color
+
+  '@babel/helper-member-expression-to-functions@7.25.9':
+    dependencies:
+      '@babel/traverse': 7.27.0
+      '@babel/types': 7.27.0
+    transitivePeerDependencies:
+      - supports-color
+
+  '@babel/helper-module-imports@7.25.9':
+    dependencies:
+      '@babel/traverse': 7.27.0
+      '@babel/types': 7.27.0
+    transitivePeerDependencies:
+      - supports-color
+
+  '@babel/helper-module-transforms@7.26.0(@babel/core@7.26.10)':
+    dependencies:
+      '@babel/core': 7.26.10
+      '@babel/helper-module-imports': 7.25.9
+      '@babel/helper-validator-identifier': 7.25.9
+      '@babel/traverse': 7.27.0
+    transitivePeerDependencies:
+      - supports-color
+
+  '@babel/helper-optimise-call-expression@7.25.9':
+    dependencies:
+      '@babel/types': 7.27.0
+
+  '@babel/helper-plugin-utils@7.26.5': {}
+
+  '@babel/helper-replace-supers@7.26.5(@babel/core@7.26.10)':
+    dependencies:
+      '@babel/core': 7.26.10
+      '@babel/helper-member-expression-to-functions': 7.25.9
+      '@babel/helper-optimise-call-expression': 7.25.9
+      '@babel/traverse': 7.27.0
+    transitivePeerDependencies:
+      - supports-color
+
+  '@babel/helper-skip-transparent-expression-wrappers@7.25.9':
+    dependencies:
+      '@babel/traverse': 7.27.0
+      '@babel/types': 7.27.0
+    transitivePeerDependencies:
+      - supports-color
+
+  '@babel/helper-string-parser@7.25.9': {}
+
+  '@babel/helper-validator-identifier@7.25.9': {}
+
+  '@babel/helper-validator-option@7.25.9': {}
+
+  '@babel/helpers@7.27.0':
+    dependencies:
+      '@babel/template': 7.27.0
+      '@babel/types': 7.27.0
+
+  '@babel/parser@7.27.0':
+    dependencies:
+      '@babel/types': 7.27.0
+
+  '@babel/plugin-proposal-class-properties@7.18.6(@babel/core@7.26.10)':
+    dependencies:
+      '@babel/core': 7.26.10
+      '@babel/helper-create-class-features-plugin': 7.27.0(@babel/core@7.26.10)
+      '@babel/helper-plugin-utils': 7.26.5
+    transitivePeerDependencies:
+      - supports-color
+
+  '@babel/plugin-proposal-decorators@7.25.9(@babel/core@7.26.10)':
+    dependencies:
+      '@babel/core': 7.26.10
+      '@babel/helper-create-class-features-plugin': 7.27.0(@babel/core@7.26.10)
+      '@babel/helper-plugin-utils': 7.26.5
+      '@babel/plugin-syntax-decorators': 7.25.9(@babel/core@7.26.10)
+    transitivePeerDependencies:
+      - supports-color
+
+  '@babel/plugin-proposal-nullish-coalescing-operator@7.18.6(@babel/core@7.26.10)':
+    dependencies:
+      '@babel/core': 7.26.10
+      '@babel/helper-plugin-utils': 7.26.5
+      '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.26.10)
+
+  '@babel/plugin-proposal-object-rest-spread@7.20.7(@babel/core@7.26.10)':
+    dependencies:
+      '@babel/compat-data': 7.26.8
+      '@babel/core': 7.26.10
+      '@babel/helper-compilation-targets': 7.27.0
+      '@babel/helper-plugin-utils': 7.26.5
+      '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.26.10)
+      '@babel/plugin-transform-parameters': 7.25.9(@babel/core@7.26.10)
+
+  '@babel/plugin-proposal-optional-chaining@7.21.0(@babel/core@7.26.10)':
+    dependencies:
+      '@babel/core': 7.26.10
+      '@babel/helper-plugin-utils': 7.26.5
+      '@babel/helper-skip-transparent-expression-wrappers': 7.25.9
+      '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.26.10)
+    transitivePeerDependencies:
+      - supports-color
+
+  '@babel/plugin-syntax-decorators@7.25.9(@babel/core@7.26.10)':
+    dependencies:
+      '@babel/core': 7.26.10
+      '@babel/helper-plugin-utils': 7.26.5
+
+  '@babel/plugin-syntax-jsx@7.25.9(@babel/core@7.26.10)':
+    dependencies:
+      '@babel/core': 7.26.10
+      '@babel/helper-plugin-utils': 7.26.5
+
+  '@babel/plugin-syntax-nullish-coalescing-operator@7.8.3(@babel/core@7.26.10)':
+    dependencies:
+      '@babel/core': 7.26.10
+      '@babel/helper-plugin-utils': 7.26.5
+
+  '@babel/plugin-syntax-object-rest-spread@7.8.3(@babel/core@7.26.10)':
+    dependencies:
+      '@babel/core': 7.26.10
+      '@babel/helper-plugin-utils': 7.26.5
+
+  '@babel/plugin-syntax-optional-chaining@7.8.3(@babel/core@7.26.10)':
+    dependencies:
+      '@babel/core': 7.26.10
+      '@babel/helper-plugin-utils': 7.26.5
+
+  '@babel/plugin-syntax-typescript@7.25.9(@babel/core@7.26.10)':
+    dependencies:
+      '@babel/core': 7.26.10
+      '@babel/helper-plugin-utils': 7.26.5
+
+  '@babel/plugin-transform-arrow-functions@7.25.9(@babel/core@7.26.10)':
+    dependencies:
+      '@babel/core': 7.26.10
+      '@babel/helper-plugin-utils': 7.26.5
+
+  '@babel/plugin-transform-block-scoping@7.27.0(@babel/core@7.26.10)':
+    dependencies:
+      '@babel/core': 7.26.10
+      '@babel/helper-plugin-utils': 7.26.5
+
+  '@babel/plugin-transform-computed-properties@7.25.9(@babel/core@7.26.10)':
+    dependencies:
+      '@babel/core': 7.26.10
+      '@babel/helper-plugin-utils': 7.26.5
+      '@babel/template': 7.27.0
+
+  '@babel/plugin-transform-destructuring@7.25.9(@babel/core@7.26.10)':
+    dependencies:
+      '@babel/core': 7.26.10
+      '@babel/helper-plugin-utils': 7.26.5
+
+  '@babel/plugin-transform-parameters@7.25.9(@babel/core@7.26.10)':
+    dependencies:
+      '@babel/core': 7.26.10
+      '@babel/helper-plugin-utils': 7.26.5
+
+  '@babel/plugin-transform-spread@7.25.9(@babel/core@7.26.10)':
+    dependencies:
+      '@babel/core': 7.26.10
+      '@babel/helper-plugin-utils': 7.26.5
+      '@babel/helper-skip-transparent-expression-wrappers': 7.25.9
+    transitivePeerDependencies:
+      - supports-color
+
+  '@babel/plugin-transform-typescript@7.27.0(@babel/core@7.26.10)':
+    dependencies:
+      '@babel/core': 7.26.10
+      '@babel/helper-annotate-as-pure': 7.25.9
+      '@babel/helper-create-class-features-plugin': 7.27.0(@babel/core@7.26.10)
+      '@babel/helper-plugin-utils': 7.26.5
+      '@babel/helper-skip-transparent-expression-wrappers': 7.25.9
+      '@babel/plugin-syntax-typescript': 7.25.9(@babel/core@7.26.10)
+    transitivePeerDependencies:
+      - supports-color
+
+  '@babel/runtime@7.27.0':
+    dependencies:
+      regenerator-runtime: 0.14.1
+
+  '@babel/template@7.27.0':
+    dependencies:
+      '@babel/code-frame': 7.26.2
+      '@babel/parser': 7.27.0
+      '@babel/types': 7.27.0
+
+  '@babel/traverse@7.27.0':
+    dependencies:
+      '@babel/code-frame': 7.26.2
+      '@babel/generator': 7.27.0
+      '@babel/parser': 7.27.0
+      '@babel/template': 7.27.0
+      '@babel/types': 7.27.0
+      debug: 4.4.0
+      globals: 11.12.0
+    transitivePeerDependencies:
+      - supports-color
+
+  '@babel/types@7.27.0':
+    dependencies:
+      '@babel/helper-string-parser': 7.25.9
+      '@babel/helper-validator-identifier': 7.25.9
+
+  '@esbuild/linux-loong64@0.14.54':
+    optional: true
+
+  '@fast-csv/format@4.3.5':
+    dependencies:
+      '@types/node': 14.18.63
+      lodash.escaperegexp: 4.1.2
+      lodash.isboolean: 3.0.3
+      lodash.isequal: 4.5.0
+      lodash.isfunction: 3.0.9
+      lodash.isnil: 4.0.0
+
+  '@fast-csv/parse@4.3.6':
+    dependencies:
+      '@types/node': 14.18.63
+      lodash.escaperegexp: 4.1.2
+      lodash.groupby: 4.6.0
+      lodash.isfunction: 3.0.9
+      lodash.isnil: 4.0.0
+      lodash.isundefined: 3.0.1
+      lodash.uniq: 4.5.0
+
+  '@handsontable/vue@15.2.0(handsontable@11.1.0)(vue@2.7.16)':
+    dependencies:
+      handsontable: 11.1.0
+      vue: 2.7.16
+
+  '@jest/types@26.6.2':
+    dependencies:
+      '@types/istanbul-lib-coverage': 2.0.6
+      '@types/istanbul-reports': 3.0.4
+      '@types/node': 14.18.63
+      '@types/yargs': 15.0.19
+      chalk: 4.1.2
+
+  '@jridgewell/gen-mapping@0.3.8':
+    dependencies:
+      '@jridgewell/set-array': 1.2.1
+      '@jridgewell/sourcemap-codec': 1.5.0
+      '@jridgewell/trace-mapping': 0.3.25
+
+  '@jridgewell/resolve-uri@3.1.2': {}
+
+  '@jridgewell/set-array@1.2.1': {}
+
+  '@jridgewell/sourcemap-codec@1.5.0': {}
+
+  '@jridgewell/trace-mapping@0.3.25':
+    dependencies:
+      '@jridgewell/resolve-uri': 3.1.2
+      '@jridgewell/sourcemap-codec': 1.5.0
+
+  '@lifetech-inc/x-data-spreadsheet@1.0.17':
+    dependencies:
+      opencollective: 1.0.3
+      opencollective-postinstall: 2.0.3
+
+  '@originjs/vite-plugin-require-context@1.0.9':
+    dependencies:
+      '@types/jest': 26.0.24
+      node-dir: 0.1.17
+
+  '@riophae/vue-treeselect@0.4.0(vue@2.6.11)':
+    dependencies:
+      '@babel/runtime': 7.27.0
+      babel-helper-vue-jsx-merge-props: 2.0.3
+      easings-css: 1.0.0
+      fuzzysearch: 1.0.3
+      is-promise: 2.2.2
+      lodash: 4.17.21
+      material-colors: 1.2.6
+      vue: 2.6.11
+      watch-size: 2.0.0
+
+  '@rollup/pluginutils@4.2.1':
+    dependencies:
+      estree-walker: 2.0.2
+      picomatch: 2.3.1
+
+  '@types/istanbul-lib-coverage@2.0.6': {}
+
+  '@types/istanbul-lib-report@3.0.3':
+    dependencies:
+      '@types/istanbul-lib-coverage': 2.0.6
+
+  '@types/istanbul-reports@3.0.4':
+    dependencies:
+      '@types/istanbul-lib-report': 3.0.3
+
+  '@types/jest@26.0.24':
+    dependencies:
+      jest-diff: 26.6.2
+      pretty-format: 26.6.2
+
+  '@types/node@14.18.63': {}
+
+  '@types/pikaday@1.7.4':
+    dependencies:
+      moment: 2.30.1
+
+  '@types/yargs-parser@21.0.3': {}
+
+  '@types/yargs@15.0.19':
+    dependencies:
+      '@types/yargs-parser': 21.0.3
+
+  '@vue/babel-helper-vue-jsx-merge-props@1.4.0': {}
+
+  '@vue/babel-plugin-transform-vue-jsx@1.4.0(@babel/core@7.26.10)':
+    dependencies:
+      '@babel/core': 7.26.10
+      '@babel/helper-module-imports': 7.25.9
+      '@babel/plugin-syntax-jsx': 7.25.9(@babel/core@7.26.10)
+      '@vue/babel-helper-vue-jsx-merge-props': 1.4.0
+      html-tags: 2.0.0
+      lodash.kebabcase: 4.1.1
+      svg-tags: 1.0.0
+    transitivePeerDependencies:
+      - supports-color
+
+  '@vue/babel-preset-jsx@1.4.0(@babel/core@7.26.10)(vue@2.7.16)':
+    dependencies:
+      '@babel/core': 7.26.10
+      '@vue/babel-helper-vue-jsx-merge-props': 1.4.0
+      '@vue/babel-plugin-transform-vue-jsx': 1.4.0(@babel/core@7.26.10)
+      '@vue/babel-sugar-composition-api-inject-h': 1.4.0(@babel/core@7.26.10)
+      '@vue/babel-sugar-composition-api-render-instance': 1.4.0(@babel/core@7.26.10)
+      '@vue/babel-sugar-functional-vue': 1.4.0(@babel/core@7.26.10)
+      '@vue/babel-sugar-inject-h': 1.4.0(@babel/core@7.26.10)
+      '@vue/babel-sugar-v-model': 1.4.0(@babel/core@7.26.10)
+      '@vue/babel-sugar-v-on': 1.4.0(@babel/core@7.26.10)
+    optionalDependencies:
+      vue: 2.7.16
+    transitivePeerDependencies:
+      - supports-color
+
+  '@vue/babel-sugar-composition-api-inject-h@1.4.0(@babel/core@7.26.10)':
+    dependencies:
+      '@babel/core': 7.26.10
+      '@babel/plugin-syntax-jsx': 7.25.9(@babel/core@7.26.10)
+
+  '@vue/babel-sugar-composition-api-render-instance@1.4.0(@babel/core@7.26.10)':
+    dependencies:
+      '@babel/core': 7.26.10
+      '@babel/plugin-syntax-jsx': 7.25.9(@babel/core@7.26.10)
+
+  '@vue/babel-sugar-functional-vue@1.4.0(@babel/core@7.26.10)':
+    dependencies:
+      '@babel/core': 7.26.10
+      '@babel/plugin-syntax-jsx': 7.25.9(@babel/core@7.26.10)
+
+  '@vue/babel-sugar-inject-h@1.4.0(@babel/core@7.26.10)':
+    dependencies:
+      '@babel/core': 7.26.10
+      '@babel/plugin-syntax-jsx': 7.25.9(@babel/core@7.26.10)
+
+  '@vue/babel-sugar-v-model@1.4.0(@babel/core@7.26.10)':
+    dependencies:
+      '@babel/core': 7.26.10
+      '@babel/plugin-syntax-jsx': 7.25.9(@babel/core@7.26.10)
+      '@vue/babel-helper-vue-jsx-merge-props': 1.4.0
+      '@vue/babel-plugin-transform-vue-jsx': 1.4.0(@babel/core@7.26.10)
+      camelcase: 5.3.1
+      html-tags: 2.0.0
+      svg-tags: 1.0.0
+    transitivePeerDependencies:
+      - supports-color
+
+  '@vue/babel-sugar-v-on@1.4.0(@babel/core@7.26.10)':
+    dependencies:
+      '@babel/core': 7.26.10
+      '@babel/plugin-syntax-jsx': 7.25.9(@babel/core@7.26.10)
+      '@vue/babel-plugin-transform-vue-jsx': 1.4.0(@babel/core@7.26.10)
+      camelcase: 5.3.1
+    transitivePeerDependencies:
+      - supports-color
+
+  '@vue/compiler-sfc@2.7.16':
+    dependencies:
+      '@babel/parser': 7.27.0
+      postcss: 8.5.3
+      source-map: 0.6.1
+    optionalDependencies:
+      prettier: 2.8.8
+
+  '@vue/component-compiler-utils@3.3.0(lodash@4.17.21)':
+    dependencies:
+      consolidate: 0.15.1(lodash@4.17.21)
+      hash-sum: 1.0.2
+      lru-cache: 4.1.5
+      merge-source-map: 1.1.0
+      postcss: 7.0.39
+      postcss-selector-parser: 6.1.2
+      source-map: 0.6.1
+      vue-template-es2015-compiler: 1.9.1
+    optionalDependencies:
+      prettier: 2.8.8
+    transitivePeerDependencies:
+      - arc-templates
+      - atpl
+      - babel-core
+      - bracket-template
+      - coffee-script
+      - dot
+      - dust
+      - dustjs-helpers
+      - dustjs-linkedin
+      - eco
+      - ect
+      - ejs
+      - haml-coffee
+      - hamlet
+      - hamljs
+      - handlebars
+      - hogan.js
+      - htmling
+      - jade
+      - jazz
+      - jqtpl
+      - just
+      - liquid-node
+      - liquor
+      - lodash
+      - marko
+      - mote
+      - mustache
+      - nunjucks
+      - plates
+      - pug
+      - qejs
+      - ractive
+      - razor-tmpl
+      - react
+      - react-dom
+      - slm
+      - squirrelly
+      - swig
+      - swig-templates
+      - teacup
+      - templayed
+      - then-jade
+      - then-pug
+      - tinyliquid
+      - toffee
+      - twig
+      - twing
+      - underscore
+      - vash
+      - velocityjs
+      - walrus
+      - whiskers
+
+  adler-32@1.3.1: {}
+
+  ansi-escapes@1.4.0: {}
+
+  ansi-regex@2.1.1: {}
+
+  ansi-regex@3.0.1: {}
+
+  ansi-regex@5.0.1: {}
+
+  ansi-styles@2.2.1: {}
+
+  ansi-styles@4.3.0:
+    dependencies:
+      color-convert: 2.0.1
+
+  archiver-utils@2.1.0:
+    dependencies:
+      glob: 7.2.3
+      graceful-fs: 4.2.11
+      lazystream: 1.0.1
+      lodash.defaults: 4.2.0
+      lodash.difference: 4.5.0
+      lodash.flatten: 4.4.0
+      lodash.isplainobject: 4.0.6
+      lodash.union: 4.6.0
+      normalize-path: 3.0.0
+      readable-stream: 2.3.8
+
+  archiver-utils@3.0.4:
+    dependencies:
+      glob: 7.2.3
+      graceful-fs: 4.2.11
+      lazystream: 1.0.1
+      lodash.defaults: 4.2.0
+      lodash.difference: 4.5.0
+      lodash.flatten: 4.4.0
+      lodash.isplainobject: 4.0.6
+      lodash.union: 4.6.0
+      normalize-path: 3.0.0
+      readable-stream: 3.6.2
+
+  archiver@5.3.2:
+    dependencies:
+      archiver-utils: 2.1.0
+      async: 3.2.6
+      buffer-crc32: 0.2.13
+      readable-stream: 3.6.2
+      readdir-glob: 1.1.3
+      tar-stream: 2.2.0
+      zip-stream: 4.1.1
+
+  argparse@1.0.10:
+    dependencies:
+      sprintf-js: 1.0.3
+
+  async-validator@1.8.5:
+    dependencies:
+      babel-runtime: 6.26.0
+
+  async@3.2.6: {}
+
+  asynckit@0.4.0: {}
+
+  axios@1.8.4:
+    dependencies:
+      follow-redirects: 1.15.9
+      form-data: 4.0.2
+      proxy-from-env: 1.1.0
+    transitivePeerDependencies:
+      - debug
+
+  babel-helper-vue-jsx-merge-props@2.0.3: {}
+
+  babel-polyfill@6.23.0:
+    dependencies:
+      babel-runtime: 6.26.0
+      core-js: 2.6.12
+      regenerator-runtime: 0.10.5
+
+  babel-runtime@6.26.0:
+    dependencies:
+      core-js: 2.6.12
+      regenerator-runtime: 0.11.1
+
+  balanced-match@1.0.2: {}
+
+  base64-arraybuffer@1.0.2: {}
+
+  base64-js@1.5.1: {}
+
+  big-integer@1.6.52: {}
+
+  bignumber.js@8.1.1: {}
+
+  binary@0.3.0:
+    dependencies:
+      buffers: 0.1.1
+      chainsaw: 0.1.0
+
+  bl@4.1.0:
+    dependencies:
+      buffer: 5.7.1
+      inherits: 2.0.4
+      readable-stream: 3.6.2
+
+  bluebird@3.4.7: {}
+
+  bluebird@3.7.2: {}
+
+  bmaplib.curveline@1.0.0: {}
+
+  bmaplib.heatmap@1.0.4: {}
+
+  bmaplib.lushu@1.0.7: {}
+
+  bmaplib.markerclusterer@1.0.13:
+    dependencies:
+      bmaplib.texticonoverlay: 1.0.2
+
+  bmaplib.texticonoverlay@1.0.2: {}
+
+  brace-expansion@1.1.11:
+    dependencies:
+      balanced-match: 1.0.2
+      concat-map: 0.0.1
+
+  brace-expansion@2.0.1:
+    dependencies:
+      balanced-match: 1.0.2
+
+  browserslist@4.24.4:
+    dependencies:
+      caniuse-lite: 1.0.30001712
+      electron-to-chromium: 1.5.134
+      node-releases: 2.0.19
+      update-browserslist-db: 1.1.3(browserslist@4.24.4)
+
+  buffer-crc32@0.2.13: {}
+
+  buffer-indexof-polyfill@1.0.2: {}
+
+  buffer@5.7.1:
+    dependencies:
+      base64-js: 1.5.1
+      ieee754: 1.2.1
+
+  buffers@0.1.1: {}
+
+  call-bind-apply-helpers@1.0.2:
+    dependencies:
+      es-errors: 1.3.0
+      function-bind: 1.1.2
+
+  camelcase@5.3.1: {}
+
+  caniuse-lite@1.0.30001712: {}
+
+  cfb@1.2.2:
+    dependencies:
+      adler-32: 1.3.1
+      crc-32: 1.2.2
+
+  chainsaw@0.1.0:
+    dependencies:
+      traverse: 0.3.9
+
+  chalk@1.1.3:
+    dependencies:
+      ansi-styles: 2.2.1
+      escape-string-regexp: 1.0.5
+      has-ansi: 2.0.0
+      strip-ansi: 3.0.1
+      supports-color: 2.0.0
+
+  chalk@4.1.2:
+    dependencies:
+      ansi-styles: 4.3.0
+      supports-color: 7.2.0
+
+  chardet@0.4.2: {}
+
+  chevrotain@6.5.0:
+    dependencies:
+      regexp-to-ast: 0.4.0
+    optional: true
+
+  cli-cursor@2.1.0:
+    dependencies:
+      restore-cursor: 2.0.0
+
+  cli-width@2.2.1: {}
+
+  codepage@1.15.0: {}
+
+  color-convert@2.0.1:
+    dependencies:
+      color-name: 1.1.4
+
+  color-name@1.1.4: {}
+
+  combined-stream@1.0.8:
+    dependencies:
+      delayed-stream: 1.0.0
+
+  compress-commons@4.1.2:
+    dependencies:
+      buffer-crc32: 0.2.13
+      crc32-stream: 4.0.3
+      normalize-path: 3.0.0
+      readable-stream: 3.6.2
+
+  concat-map@0.0.1: {}
+
+  consolidate@0.15.1(lodash@4.17.21):
+    dependencies:
+      bluebird: 3.7.2
+    optionalDependencies:
+      lodash: 4.17.21
+
+  consolidate@0.16.0(lodash@4.17.21):
+    dependencies:
+      bluebird: 3.7.2
+    optionalDependencies:
+      lodash: 4.17.21
+
+  convert-source-map@2.0.0: {}
+
+  copy-anything@2.0.6:
+    dependencies:
+      is-what: 3.14.1
+
+  core-js@2.6.12: {}
+
+  core-js@3.41.0: {}
+
+  core-util-is@1.0.3: {}
+
+  crc-32@1.2.2: {}
+
+  crc32-stream@4.0.3:
+    dependencies:
+      crc-32: 1.2.2
+      readable-stream: 3.6.2
+
+  css-line-break@2.1.0:
+    dependencies:
+      utrie: 1.0.2
+
+  cssesc@3.0.0: {}
+
+  csstype@3.1.3: {}
+
+  dayjs@1.11.13: {}
+
+  de-indent@1.0.2: {}
+
+  debug@4.4.0:
+    dependencies:
+      ms: 2.1.3
+
+  deepmerge@1.5.2: {}
+
+  deepmerge@4.3.1: {}
+
+  delayed-stream@1.0.0: {}
+
+  diff-sequences@26.6.2: {}
+
+  docx-preview@0.3.5:
+    dependencies:
+      jszip: 3.10.1
+
+  dompurify@2.5.8: {}
+
+  dunder-proto@1.0.1:
+    dependencies:
+      call-bind-apply-helpers: 1.0.2
+      es-errors: 1.3.0
+      gopd: 1.2.0
+
+  duplexer2@0.1.4:
+    dependencies:
+      readable-stream: 2.3.8
+
+  easings-css@1.0.0: {}
+
+  electron-to-chromium@1.5.134: {}
+
+  element-ui@2.15.14(vue@2.6.11):
+    dependencies:
+      async-validator: 1.8.5
+      babel-helper-vue-jsx-merge-props: 2.0.3
+      deepmerge: 1.5.2
+      normalize-wheel: 1.0.1
+      resize-observer-polyfill: 1.5.1
+      throttle-debounce: 1.1.0
+      vue: 2.6.11
+
+  element-ui@2.15.14(vue@2.7.16):
+    dependencies:
+      async-validator: 1.8.5
+      babel-helper-vue-jsx-merge-props: 2.0.3
+      deepmerge: 1.5.2
+      normalize-wheel: 1.0.1
+      resize-observer-polyfill: 1.5.1
+      throttle-debounce: 1.1.0
+      vue: 2.7.16
+
+  encoding@0.1.13:
+    dependencies:
+      iconv-lite: 0.6.3
+
+  end-of-stream@1.4.4:
+    dependencies:
+      once: 1.4.0
+
+  entities@1.1.2: {}
+
+  errno@0.1.8:
+    dependencies:
+      prr: 1.0.1
+    optional: true
+
+  es-define-property@1.0.1: {}
+
+  es-errors@1.3.0: {}
+
+  es-object-atoms@1.1.1:
+    dependencies:
+      es-errors: 1.3.0
+
+  es-set-tostringtag@2.1.0:
+    dependencies:
+      es-errors: 1.3.0
+      get-intrinsic: 1.3.0
+      has-tostringtag: 1.0.2
+      hasown: 2.0.2
+
+  esbuild-android-64@0.14.54:
+    optional: true
+
+  esbuild-android-arm64@0.14.54:
+    optional: true
+
+  esbuild-darwin-64@0.14.54:
+    optional: true
+
+  esbuild-darwin-arm64@0.14.54:
+    optional: true
+
+  esbuild-freebsd-64@0.14.54:
+    optional: true
+
+  esbuild-freebsd-arm64@0.14.54:
+    optional: true
+
+  esbuild-linux-32@0.14.54:
+    optional: true
+
+  esbuild-linux-64@0.14.54:
+    optional: true
+
+  esbuild-linux-arm64@0.14.54:
+    optional: true
+
+  esbuild-linux-arm@0.14.54:
+    optional: true
+
+  esbuild-linux-mips64le@0.14.54:
+    optional: true
+
+  esbuild-linux-ppc64le@0.14.54:
+    optional: true
+
+  esbuild-linux-riscv64@0.14.54:
+    optional: true
+
+  esbuild-linux-s390x@0.14.54:
+    optional: true
+
+  esbuild-netbsd-64@0.14.54:
+    optional: true
+
+  esbuild-openbsd-64@0.14.54:
+    optional: true
+
+  esbuild-sunos-64@0.14.54:
+    optional: true
+
+  esbuild-windows-32@0.14.54:
+    optional: true
+
+  esbuild-windows-64@0.14.54:
+    optional: true
+
+  esbuild-windows-arm64@0.14.54:
+    optional: true
+
+  esbuild@0.14.54:
+    optionalDependencies:
+      '@esbuild/linux-loong64': 0.14.54
+      esbuild-android-64: 0.14.54
+      esbuild-android-arm64: 0.14.54
+      esbuild-darwin-64: 0.14.54
+      esbuild-darwin-arm64: 0.14.54
+      esbuild-freebsd-64: 0.14.54
+      esbuild-freebsd-arm64: 0.14.54
+      esbuild-linux-32: 0.14.54
+      esbuild-linux-64: 0.14.54
+      esbuild-linux-arm: 0.14.54
+      esbuild-linux-arm64: 0.14.54
+      esbuild-linux-mips64le: 0.14.54
+      esbuild-linux-ppc64le: 0.14.54
+      esbuild-linux-riscv64: 0.14.54
+      esbuild-linux-s390x: 0.14.54
+      esbuild-netbsd-64: 0.14.54
+      esbuild-openbsd-64: 0.14.54
+      esbuild-sunos-64: 0.14.54
+      esbuild-windows-32: 0.14.54
+      esbuild-windows-64: 0.14.54
+      esbuild-windows-arm64: 0.14.54
+
+  escalade@3.2.0: {}
+
+  escape-string-regexp@1.0.5: {}
+
+  estree-walker@2.0.2: {}
+
+  exceljs@4.4.0:
+    dependencies:
+      archiver: 5.3.2
+      dayjs: 1.11.13
+      fast-csv: 4.3.6
+      jszip: 3.10.1
+      readable-stream: 3.6.2
+      saxes: 5.0.1
+      tmp: 0.2.3
+      unzipper: 0.10.14
+      uuid: 8.3.2
+
+  external-editor@2.2.0:
+    dependencies:
+      chardet: 0.4.2
+      iconv-lite: 0.4.24
+      tmp: 0.0.33
+
+  fast-csv@4.3.6:
+    dependencies:
+      '@fast-csv/format': 4.3.5
+      '@fast-csv/parse': 4.3.6
+
+  figures@2.0.0:
+    dependencies:
+      escape-string-regexp: 1.0.5
+
+  file-saver@2.0.5: {}
+
+  follow-redirects@1.15.9: {}
+
+  form-data@4.0.2:
+    dependencies:
+      asynckit: 0.4.0
+      combined-stream: 1.0.8
+      es-set-tostringtag: 2.1.0
+      mime-types: 2.1.35
+
+  frac@1.1.2: {}
+
+  fs-constants@1.0.0: {}
+
+  fs-extra@10.1.0:
+    dependencies:
+      graceful-fs: 4.2.11
+      jsonfile: 6.1.0
+      universalify: 2.0.1
+
+  fs.realpath@1.0.0: {}
+
+  fsevents@2.3.3:
+    optional: true
+
+  fstream@1.0.12:
+    dependencies:
+      graceful-fs: 4.2.11
+      inherits: 2.0.4
+      mkdirp: 0.5.6
+      rimraf: 2.7.1
+
+  function-bind@1.1.2: {}
+
+  fuzzysearch@1.0.3: {}
+
+  gensync@1.0.0-beta.2: {}
+
+  get-intrinsic@1.3.0:
+    dependencies:
+      call-bind-apply-helpers: 1.0.2
+      es-define-property: 1.0.1
+      es-errors: 1.3.0
+      es-object-atoms: 1.1.1
+      function-bind: 1.1.2
+      get-proto: 1.0.1
+      gopd: 1.2.0
+      has-symbols: 1.1.0
+      hasown: 2.0.2
+      math-intrinsics: 1.1.0
+
+  get-proto@1.0.1:
+    dependencies:
+      dunder-proto: 1.0.1
+      es-object-atoms: 1.1.1
+
+  glob@7.2.3:
+    dependencies:
+      fs.realpath: 1.0.0
+      inflight: 1.0.6
+      inherits: 2.0.4
+      minimatch: 3.1.2
+      once: 1.4.0
+      path-is-absolute: 1.0.1
+
+  globals@11.12.0: {}
+
+  gopd@1.2.0: {}
+
+  graceful-fs@4.2.11: {}
+
+  handsontable@11.1.0:
+    dependencies:
+      '@types/pikaday': 1.7.4
+      core-js: 3.41.0
+      dompurify: 2.5.8
+      moment: 2.24.0
+      numbro: 2.1.2
+      pikaday: 1.8.0
+    optionalDependencies:
+      hyperformula: 1.3.1
+
+  has-ansi@2.0.0:
+    dependencies:
+      ansi-regex: 2.1.1
+
+  has-flag@4.0.0: {}
+
+  has-symbols@1.1.0: {}
+
+  has-tostringtag@1.0.2:
+    dependencies:
+      has-symbols: 1.1.0
+
+  hash-sum@1.0.2: {}
+
+  hash-sum@2.0.0: {}
+
+  hasown@2.0.2:
+    dependencies:
+      function-bind: 1.1.2
+
+  he@1.2.0: {}
+
+  html-tags@2.0.0: {}
+
+  html2canvas@1.4.1:
+    dependencies:
+      css-line-break: 2.1.0
+      text-segmentation: 1.0.3
+
+  hyperformula@1.3.1:
+    dependencies:
+      chevrotain: 6.5.0
+      core-js: 3.41.0
+      regenerator-runtime: 0.13.11
+      tiny-emitter: 2.1.0
+      unorm: 1.6.0
+    optional: true
+
+  iconv-lite@0.4.24:
+    dependencies:
+      safer-buffer: 2.1.2
+
+  iconv-lite@0.6.3:
+    dependencies:
+      safer-buffer: 2.1.2
+
+  ieee754@1.2.1: {}
+
+  image-size@0.5.5:
+    optional: true
+
+  immediate@3.0.6: {}
+
+  inflight@1.0.6:
+    dependencies:
+      once: 1.4.0
+      wrappy: 1.0.2
+
+  inherits@2.0.4: {}
+
+  inquirer@3.0.6:
+    dependencies:
+      ansi-escapes: 1.4.0
+      chalk: 1.1.3
+      cli-cursor: 2.1.0
+      cli-width: 2.2.1
+      external-editor: 2.2.0
+      figures: 2.0.0
+      lodash: 4.17.21
+      mute-stream: 0.0.7
+      run-async: 2.4.1
+      rx: 4.1.0
+      string-width: 2.1.1
+      strip-ansi: 3.0.1
+      through: 2.3.8
+
+  is-core-module@2.16.1:
+    dependencies:
+      hasown: 2.0.2
+
+  is-fullwidth-code-point@2.0.0: {}
+
+  is-promise@2.2.2: {}
+
+  is-stream@1.1.0: {}
+
+  is-what@3.14.1: {}
+
+  isarray@1.0.0: {}
+
+  jest-diff@26.6.2:
+    dependencies:
+      chalk: 4.1.2
+      diff-sequences: 26.6.2
+      jest-get-type: 26.3.0
+      pretty-format: 26.6.2
+
+  jest-get-type@26.3.0: {}
+
+  js-cookie@3.0.5: {}
+
+  js-tokens@4.0.0: {}
+
+  jsesc@3.1.0: {}
+
+  json5@2.2.3: {}
+
+  jsonfile@6.1.0:
+    dependencies:
+      universalify: 2.0.1
+    optionalDependencies:
+      graceful-fs: 4.2.11
+
+  jszip@3.10.1:
+    dependencies:
+      lie: 3.3.0
+      pako: 1.0.11
+      readable-stream: 2.3.8
+      setimmediate: 1.0.5
+
+  lazystream@1.0.1:
+    dependencies:
+      readable-stream: 2.3.8
+
+  less@4.3.0:
+    dependencies:
+      copy-anything: 2.0.6
+      parse-node-version: 1.0.1
+      tslib: 2.8.1
+    optionalDependencies:
+      errno: 0.1.8
+      graceful-fs: 4.2.11
+      image-size: 0.5.5
+      make-dir: 2.1.0
+      mime: 1.6.0
+      needle: 3.3.1
+      source-map: 0.6.1
+
+  lie@3.3.0:
+    dependencies:
+      immediate: 3.0.6
+
+  linkify-it@2.2.0:
+    dependencies:
+      uc.micro: 1.0.6
+
+  listenercount@1.0.1: {}
+
+  lodash-es@4.17.21: {}
+
+  lodash.defaults@4.2.0: {}
+
+  lodash.difference@4.5.0: {}
+
+  lodash.escaperegexp@4.1.2: {}
+
+  lodash.flatten@4.4.0: {}
+
+  lodash.groupby@4.6.0: {}
+
+  lodash.isboolean@3.0.3: {}
+
+  lodash.isequal@4.5.0: {}
+
+  lodash.isfunction@3.0.9: {}
+
+  lodash.isnil@4.0.0: {}
+
+  lodash.isplainobject@4.0.6: {}
+
+  lodash.isundefined@3.0.1: {}
+
+  lodash.kebabcase@4.1.1: {}
+
+  lodash.union@4.6.0: {}
+
+  lodash.uniq@4.5.0: {}
+
+  lodash@4.17.21: {}
+
+  lru-cache@4.1.5:
+    dependencies:
+      pseudomap: 1.0.2
+      yallist: 2.1.2
+
+  lru-cache@5.1.1:
+    dependencies:
+      yallist: 3.1.1
+
+  magic-string@0.26.7:
+    dependencies:
+      sourcemap-codec: 1.4.8
+
+  make-dir@2.1.0:
+    dependencies:
+      pify: 4.0.1
+      semver: 5.7.2
+    optional: true
+
+  markdown-it@8.4.2:
+    dependencies:
+      argparse: 1.0.10
+      entities: 1.1.2
+      linkify-it: 2.2.0
+      mdurl: 1.0.1
+      uc.micro: 1.0.6
+
+  material-colors@1.2.6: {}
+
+  math-intrinsics@1.1.0: {}
+
+  mdurl@1.0.1: {}
+
+  merge-source-map@1.1.0:
+    dependencies:
+      source-map: 0.6.1
+
+  mime-db@1.52.0: {}
+
+  mime-types@2.1.35:
+    dependencies:
+      mime-db: 1.52.0
+
+  mime@1.6.0:
+    optional: true
+
+  mimic-fn@1.2.0: {}
+
+  minimatch@3.1.2:
+    dependencies:
+      brace-expansion: 1.1.11
+
+  minimatch@5.1.6:
+    dependencies:
+      brace-expansion: 2.0.1
+
+  minimist@1.2.0: {}
+
+  minimist@1.2.8: {}
+
+  mkdirp@0.5.6:
+    dependencies:
+      minimist: 1.2.8
+
+  moment@2.24.0: {}
+
+  moment@2.30.1: {}
+
+  ms@2.1.3: {}
+
+  mute-stream@0.0.7: {}
+
+  nanoid@3.3.11: {}
+
+  needle@3.3.1:
+    dependencies:
+      iconv-lite: 0.6.3
+      sax: 1.4.1
+    optional: true
+
+  node-dir@0.1.17:
+    dependencies:
+      minimatch: 3.1.2
+
+  node-fetch@1.6.3:
+    dependencies:
+      encoding: 0.1.13
+      is-stream: 1.1.0
+
+  node-releases@2.0.19: {}
+
+  normalize-path@3.0.0: {}
+
+  normalize-wheel@1.0.1: {}
+
+  numbro@2.1.2:
+    dependencies:
+      bignumber.js: 8.1.1
+
+  object-assign@4.1.1: {}
+
+  once@1.4.0:
+    dependencies:
+      wrappy: 1.0.2
+
+  onetime@2.0.1:
+    dependencies:
+      mimic-fn: 1.2.0
+
+  opencollective-postinstall@2.0.3: {}
+
+  opencollective@1.0.3:
+    dependencies:
+      babel-polyfill: 6.23.0
+      chalk: 1.1.3
+      inquirer: 3.0.6
+      minimist: 1.2.0
+      node-fetch: 1.6.3
+      opn: 4.0.2
+
+  opn@4.0.2:
+    dependencies:
+      object-assign: 4.1.1
+      pinkie-promise: 2.0.1
+
+  os-tmpdir@1.0.2: {}
+
+  pako@1.0.11: {}
+
+  parse-node-version@1.0.1: {}
+
+  path-is-absolute@1.0.1: {}
+
+  path-parse@1.0.7: {}
+
+  pdfjs-dist@2.4.456: {}
+
+  picocolors@0.2.1: {}
+
+  picocolors@1.1.1: {}
+
+  picomatch@2.3.1: {}
+
+  pify@4.0.1:
+    optional: true
+
+  pikaday@1.8.0: {}
+
+  pinkie-promise@2.0.1:
+    dependencies:
+      pinkie: 2.0.4
+
+  pinkie@2.0.4: {}
+
+  postcss-selector-parser@6.1.2:
+    dependencies:
+      cssesc: 3.0.0
+      util-deprecate: 1.0.2
+
+  postcss@7.0.39:
+    dependencies:
+      picocolors: 0.2.1
+      source-map: 0.6.1
+
+  postcss@8.5.3:
+    dependencies:
+      nanoid: 3.3.11
+      picocolors: 1.1.1
+      source-map-js: 1.2.1
+
+  pptxtojson@1.3.1:
+    dependencies:
+      jszip: 3.10.1
+      tinycolor2: 1.6.0
+      txml: 5.1.1
+
+  prettier@2.8.8: {}
+
+  pretty-format@26.6.2:
+    dependencies:
+      '@jest/types': 26.6.2
+      ansi-regex: 5.0.1
+      ansi-styles: 4.3.0
+      react-is: 17.0.2
+
+  process-nextick-args@2.0.1: {}
+
+  proxy-from-env@1.1.0: {}
+
+  prr@1.0.1:
+    optional: true
+
+  pseudomap@1.0.2: {}
+
+  querystring@0.2.1: {}
+
+  react-is@17.0.2: {}
+
+  readable-stream@2.3.8:
+    dependencies:
+      core-util-is: 1.0.3
+      inherits: 2.0.4
+      isarray: 1.0.0
+      process-nextick-args: 2.0.1
+      safe-buffer: 5.1.2
+      string_decoder: 1.1.1
+      util-deprecate: 1.0.2
+
+  readable-stream@3.6.2:
+    dependencies:
+      inherits: 2.0.4
+      string_decoder: 1.1.1
+      util-deprecate: 1.0.2
+
+  readdir-glob@1.1.3:
+    dependencies:
+      minimatch: 5.1.6
+
+  regenerator-runtime@0.10.5: {}
+
+  regenerator-runtime@0.11.1: {}
+
+  regenerator-runtime@0.13.11:
+    optional: true
+
+  regenerator-runtime@0.14.1: {}
+
+  regexp-to-ast@0.4.0:
+    optional: true
+
+  resize-observer-polyfill@1.5.1: {}
+
+  resolve@1.22.10:
+    dependencies:
+      is-core-module: 2.16.1
+      path-parse: 1.0.7
+      supports-preserve-symlinks-flag: 1.0.0
+
+  restore-cursor@2.0.0:
+    dependencies:
+      onetime: 2.0.1
+      signal-exit: 3.0.7
+
+  rimraf@2.7.1:
+    dependencies:
+      glob: 7.2.3
+
+  rollup@2.77.3:
+    optionalDependencies:
+      fsevents: 2.3.3
+
+  rollup@2.79.2:
+    optionalDependencies:
+      fsevents: 2.3.3
+
+  run-async@2.4.1: {}
+
+  rx@4.1.0: {}
+
+  safe-buffer@5.1.2: {}
+
+  safer-buffer@2.1.2: {}
+
+  sax@1.4.1:
+    optional: true
+
+  saxes@5.0.1:
+    dependencies:
+      xmlchars: 2.2.0
+
+  semver@5.7.2:
+    optional: true
+
+  semver@6.3.1: {}
+
+  setimmediate@1.0.5: {}
+
+  signal-exit@3.0.7: {}
+
+  slash@3.0.0: {}
+
+  source-map-js@1.2.1: {}
+
+  source-map@0.6.1: {}
+
+  source-map@0.7.4: {}
+
+  sourcemap-codec@1.4.8: {}
+
+  sprintf-js@1.0.3: {}
+
+  ssf@0.11.2:
+    dependencies:
+      frac: 1.1.2
+
+  string-width@2.1.1:
+    dependencies:
+      is-fullwidth-code-point: 2.0.0
+      strip-ansi: 4.0.0
+
+  string_decoder@1.1.1:
+    dependencies:
+      safe-buffer: 5.1.2
+
+  strip-ansi@3.0.1:
+    dependencies:
+      ansi-regex: 2.1.1
+
+  strip-ansi@4.0.0:
+    dependencies:
+      ansi-regex: 3.0.1
+
+  supports-color@2.0.0: {}
+
+  supports-color@7.2.0:
+    dependencies:
+      has-flag: 4.0.0
+
+  supports-preserve-symlinks-flag@1.0.0: {}
+
+  svg-tags@1.0.0: {}
+
+  tar-stream@2.2.0:
+    dependencies:
+      bl: 4.1.0
+      end-of-stream: 1.4.4
+      fs-constants: 1.0.0
+      inherits: 2.0.4
+      readable-stream: 3.6.2
+
+  text-segmentation@1.0.3:
+    dependencies:
+      utrie: 1.0.2
+
+  throttle-debounce@1.1.0: {}
+
+  throttle-debounce@3.0.1: {}
+
+  through2@3.0.2:
+    dependencies:
+      inherits: 2.0.4
+      readable-stream: 3.6.2
+
+  through@2.3.8: {}
+
+  tiny-emitter@2.1.0:
+    optional: true
+
+  tinycolor2@1.6.0: {}
+
+  tmp@0.0.33:
+    dependencies:
+      os-tmpdir: 1.0.2
+
+  tmp@0.2.3: {}
+
+  traverse@0.3.9: {}
+
+  tslib@2.8.1: {}
+
+  txml@5.1.1:
+    dependencies:
+      through2: 3.0.2
+
+  typescript@5.7.3: {}
+
+  uc.micro@1.0.6: {}
+
+  universalify@2.0.1: {}
+
+  unorm@1.6.0:
+    optional: true
+
+  unzipper@0.10.14:
+    dependencies:
+      big-integer: 1.6.52
+      binary: 0.3.0
+      bluebird: 3.4.7
+      buffer-indexof-polyfill: 1.0.2
+      duplexer2: 0.1.4
+      fstream: 1.0.12
+      graceful-fs: 4.2.11
+      listenercount: 1.0.1
+      readable-stream: 2.3.8
+      setimmediate: 1.0.5
+
+  update-browserslist-db@1.1.3(browserslist@4.24.4):
+    dependencies:
+      browserslist: 4.24.4
+      escalade: 3.2.0
+      picocolors: 1.1.1
+
+  util-deprecate@1.0.2: {}
+
+  utrie@1.0.2:
+    dependencies:
+      base64-arraybuffer: 1.0.2
+
+  uuid@8.3.2: {}
+
+  vite-plugin-vue2@2.0.3(lodash@4.17.21)(vite@2.9.18(less@4.3.0))(vue-template-compiler@2.7.16)(vue@2.7.16):
+    dependencies:
+      '@babel/core': 7.26.10
+      '@babel/parser': 7.27.0
+      '@babel/plugin-proposal-class-properties': 7.18.6(@babel/core@7.26.10)
+      '@babel/plugin-proposal-decorators': 7.25.9(@babel/core@7.26.10)
+      '@babel/plugin-proposal-nullish-coalescing-operator': 7.18.6(@babel/core@7.26.10)
+      '@babel/plugin-proposal-object-rest-spread': 7.20.7(@babel/core@7.26.10)
+      '@babel/plugin-proposal-optional-chaining': 7.21.0(@babel/core@7.26.10)
+      '@babel/plugin-transform-arrow-functions': 7.25.9(@babel/core@7.26.10)
+      '@babel/plugin-transform-block-scoping': 7.27.0(@babel/core@7.26.10)
+      '@babel/plugin-transform-computed-properties': 7.25.9(@babel/core@7.26.10)
+      '@babel/plugin-transform-destructuring': 7.25.9(@babel/core@7.26.10)
+      '@babel/plugin-transform-parameters': 7.25.9(@babel/core@7.26.10)
+      '@babel/plugin-transform-spread': 7.25.9(@babel/core@7.26.10)
+      '@babel/plugin-transform-typescript': 7.27.0(@babel/core@7.26.10)
+      '@rollup/pluginutils': 4.2.1
+      '@vue/babel-helper-vue-jsx-merge-props': 1.4.0
+      '@vue/babel-preset-jsx': 1.4.0(@babel/core@7.26.10)(vue@2.7.16)
+      '@vue/component-compiler-utils': 3.3.0(lodash@4.17.21)
+      consolidate: 0.16.0(lodash@4.17.21)
+      debug: 4.4.0
+      fs-extra: 10.1.0
+      hash-sum: 2.0.0
+      magic-string: 0.26.7
+      prettier: 2.8.8
+      querystring: 0.2.1
+      rollup: 2.79.2
+      slash: 3.0.0
+      source-map: 0.7.4
+      vite: 2.9.18(less@4.3.0)
+      vue-template-babel-compiler: 1.2.0(vue-template-compiler@2.7.16)
+      vue-template-compiler: 2.7.16
+    transitivePeerDependencies:
+      - arc-templates
+      - atpl
+      - babel-core
+      - bracket-template
+      - coffee-script
+      - dot
+      - dust
+      - dustjs-helpers
+      - dustjs-linkedin
+      - eco
+      - ect
+      - ejs
+      - haml-coffee
+      - hamlet
+      - hamljs
+      - handlebars
+      - hogan.js
+      - htmling
+      - jade
+      - jazz
+      - jqtpl
+      - just
+      - liquid-node
+      - liquor
+      - lodash
+      - marko
+      - mote
+      - mustache
+      - nunjucks
+      - plates
+      - pug
+      - qejs
+      - ractive
+      - razor-tmpl
+      - react
+      - react-dom
+      - slm
+      - squirrelly
+      - supports-color
+      - swig
+      - swig-templates
+      - teacup
+      - templayed
+      - then-jade
+      - then-pug
+      - tinyliquid
+      - toffee
+      - twig
+      - twing
+      - underscore
+      - vash
+      - velocityjs
+      - vue
+      - walrus
+      - whiskers
+
+  vite@2.9.18(less@4.3.0):
+    dependencies:
+      esbuild: 0.14.54
+      postcss: 8.5.3
+      resolve: 1.22.10
+      rollup: 2.77.3
+    optionalDependencies:
+      fsevents: 2.3.3
+      less: 4.3.0
+
+  vue-baidu-map@0.21.22(vue@2.6.11):
+    dependencies:
+      bmaplib.curveline: 1.0.0
+      bmaplib.heatmap: 1.0.4
+      bmaplib.lushu: 1.0.7
+      bmaplib.markerclusterer: 1.0.13
+      markdown-it: 8.4.2
+      vue: 2.6.11
+
+  vue-class-component@7.2.6(vue@2.6.11):
+    dependencies:
+      vue: 2.6.11
+
+  vue-cropper@0.5.11: {}
+
+  vue-property-decorator@8.5.1(vue@2.6.11):
+    dependencies:
+      vue: 2.6.11
+      vue-class-component: 7.2.6(vue@2.6.11)
+
+  vue-router@3.6.5(vue@2.6.11):
+    dependencies:
+      vue: 2.6.11
+
+  vue-router@3.6.5(vue@2.7.16):
+    dependencies:
+      vue: 2.7.16
+
+  vue-template-babel-compiler@1.2.0(vue-template-compiler@2.7.16):
+    dependencies:
+      '@babel/core': 7.26.10
+      '@babel/plugin-proposal-nullish-coalescing-operator': 7.18.6(@babel/core@7.26.10)
+      '@babel/plugin-proposal-object-rest-spread': 7.20.7(@babel/core@7.26.10)
+      '@babel/plugin-proposal-optional-chaining': 7.21.0(@babel/core@7.26.10)
+      '@babel/plugin-transform-arrow-functions': 7.25.9(@babel/core@7.26.10)
+      '@babel/plugin-transform-block-scoping': 7.27.0(@babel/core@7.26.10)
+      '@babel/plugin-transform-computed-properties': 7.25.9(@babel/core@7.26.10)
+      '@babel/plugin-transform-destructuring': 7.25.9(@babel/core@7.26.10)
+      '@babel/plugin-transform-parameters': 7.25.9(@babel/core@7.26.10)
+      '@babel/plugin-transform-spread': 7.25.9(@babel/core@7.26.10)
+      '@babel/types': 7.27.0
+      deepmerge: 4.3.1
+      vue-template-compiler: 2.7.16
+    transitivePeerDependencies:
+      - supports-color
+
+  vue-template-compiler@2.7.16:
+    dependencies:
+      de-indent: 1.0.2
+      he: 1.2.0
+
+  vue-template-es2015-compiler@1.9.1: {}
+
+  vue-virtual-scroll-list@2.3.5: {}
+
+  vue@2.6.11: {}
+
+  vue@2.7.16:
+    dependencies:
+      '@vue/compiler-sfc': 2.7.16
+      csstype: 3.1.3
+
+  vuex@3.6.2(vue@2.7.16):
+    dependencies:
+      vue: 2.7.16
+
+  watch-size@2.0.0: {}
+
+  wisdom-ui@1.0.4-beta.6:
+    dependencies:
+      '@riophae/vue-treeselect': 0.4.0(vue@2.6.11)
+      core-js: 3.41.0
+      element-ui: 2.15.14(vue@2.6.11)
+      resize-observer-polyfill: 1.5.1
+      throttle-debounce: 3.0.1
+      vue: 2.6.11
+      vue-baidu-map: 0.21.22(vue@2.6.11)
+      vue-class-component: 7.2.6(vue@2.6.11)
+      vue-cropper: 0.5.11
+      vue-property-decorator: 8.5.1(vue@2.6.11)
+      vue-router: 3.6.5(vue@2.6.11)
+      vue-virtual-scroll-list: 2.3.5
+
+  wmf@1.0.2: {}
+
+  word@0.3.0: {}
+
+  wrappy@1.0.2: {}
+
+  x-data-spreadsheet@1.1.9:
+    dependencies:
+      opencollective: 1.0.3
+      opencollective-postinstall: 2.0.3
+
+  xlsx@0.18.5:
+    dependencies:
+      adler-32: 1.3.1
+      cfb: 1.2.2
+      codepage: 1.15.0
+      crc-32: 1.2.2
+      ssf: 0.11.2
+      wmf: 1.0.2
+      word: 0.3.0
+
+  xmlchars@2.2.0: {}
+
+  yallist@2.1.2: {}
+
+  yallist@3.1.1: {}
+
+  zip-stream@4.1.1:
+    dependencies:
+      archiver-utils: 3.0.4
+      compress-commons: 4.1.2
+      readable-stream: 3.6.2

+ 38 - 0
prettier.js

@@ -0,0 +1,38 @@
+
+module.exports = {
+  // 一行最多 100 字符
+  printWidth: 100,
+  // 使用 4 个空格缩进
+  tabWidth: 2,
+  // 不使用缩进符,而使用空格
+  useTabs: false,
+  // 行尾需要有分号
+  semi: false,
+  // 使用单引号
+  singleQuote: true,
+  // 对象的 key 仅在必要时用引号
+  quoteProps: 'as-needed',
+  // jsx 不使用单引号,而使用双引号
+  jsxSingleQuote: false,
+  // 末尾不需要逗号
+  trailingComma: 'es5',
+  // 大括号内的首尾需要空格
+  bracketSpacing: true,
+  // jsx 标签的反尖括号需要换行
+  jsxBracketSameLine: false,
+  // 箭头函数,只有一个参数的时候,也需要括号
+  arrowParens: 'always',
+  // 每个文件格式化的范围是文件的全部内容
+  rangeStart: 0,
+  rangeEnd: Infinity,
+  // 不需要写文件开头的 @prettier
+  requirePragma: false,
+  // 不需要自动在文件开头插入 @prettier
+  insertPragma: false,
+  // 使用默认的折行标准
+  proseWrap: 'never',
+  // 根据显示样式决定 html 要不要折行
+  htmlWhitespaceSensitivity: 'strict',
+  // 换行符使用 lf
+  endOfLine: 'lf'
+}

+ 1 - 0
public/vite.svg

@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="31.88" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 257"><defs><linearGradient id="IconifyId1813088fe1fbc01fb466" x1="-.828%" x2="57.636%" y1="7.652%" y2="78.411%"><stop offset="0%" stop-color="#41D1FF"></stop><stop offset="100%" stop-color="#BD34FE"></stop></linearGradient><linearGradient id="IconifyId1813088fe1fbc01fb467" x1="43.376%" x2="50.316%" y1="2.242%" y2="89.03%"><stop offset="0%" stop-color="#FFEA83"></stop><stop offset="8.333%" stop-color="#FFDD35"></stop><stop offset="100%" stop-color="#FFA800"></stop></linearGradient></defs><path fill="url(#IconifyId1813088fe1fbc01fb466)" d="M255.153 37.938L134.897 252.976c-2.483 4.44-8.862 4.466-11.382.048L.875 37.958c-2.746-4.814 1.371-10.646 6.827-9.67l120.385 21.517a6.537 6.537 0 0 0 2.322-.004l117.867-21.483c5.438-.991 9.574 4.796 6.877 9.62Z"></path><path fill="url(#IconifyId1813088fe1fbc01fb467)" d="M185.432.063L96.44 17.501a3.268 3.268 0 0 0-2.634 3.014l-5.474 92.456a3.268 3.268 0 0 0 3.997 3.378l24.777-5.718c2.318-.535 4.413 1.507 3.936 3.838l-7.361 36.047c-.495 2.426 1.782 4.5 4.151 3.78l15.304-4.649c2.372-.72 4.652 1.36 4.15 3.788l-11.698 56.621c-.732 3.542 3.979 5.473 5.943 2.437l1.313-2.028l72.516-144.72c1.215-2.423-.88-5.186-3.54-4.672l-25.505 4.922c-2.396.462-4.435-1.77-3.759-4.114l16.646-57.705c.677-2.35-1.37-4.583-3.769-4.113Z"></path></svg>

+ 12 - 0
src/ libs/api.request.ts

@@ -0,0 +1,12 @@
+/*
+ * @Author: WangQiBiao
+ * @LastEditors: LiZhiWei
+ * @Description:
+ * @Date: 2019-03-02 14:21:27
+ * @LastEditTime: 2025-04-15 15:22:11
+ */
+import HttpRequest from './axios'
+
+let baseUrl = '/api/'
+const axios = new HttpRequest(baseUrl)
+export default axios

+ 62 - 0
src/ libs/axios.ts

@@ -0,0 +1,62 @@
+/*
+ * @Author: LiZhiWei
+ * @Date: 2025-04-15 15:24:14
+ * @LastEditors: LiZhiWei
+ * @LastEditTime: 2025-04-15 15:28:58
+ * @Description: 
+ */
+import axios from 'axios'
+import type { AxiosRequestConfig, AxiosInstance, AxiosResponse } from 'axios'
+import { Message } from 'element-ui'
+
+class HttpRequest {
+  private baseUrl: string
+
+  constructor (baseUrl: string) {
+    this.baseUrl = baseUrl
+  }
+
+  private getInsideConfig () {
+    const config = {
+      baseURL: this.baseUrl,
+      timeout: 60000,
+      withCredentials: true,
+      headers: {
+        Accept: 'application/json;charset=utf-8'
+      }
+    }
+    return config
+  }
+
+  private interceptors (instance: AxiosInstance) {
+    // 请求拦截
+    instance.interceptors.request.use(
+      (config: AxiosRequestConfig) => {
+        return config
+      },
+      error => {
+        return Promise.reject(error)
+      }
+    )
+
+    // 响应拦截
+    instance.interceptors.response.use(
+      (response: AxiosResponse) => {
+        return response
+      },
+      error => {
+        Message.error(error.response?.data?.message || '请求失败')
+        return Promise.reject(error)
+      }
+    )
+  }
+
+  request<T = any> (options: any): Promise<T> {
+    const instance: AxiosInstance = axios.create()
+    options = Object.assign(this.getInsideConfig(), options)
+    this.interceptors(instance)
+    return instance(options) as unknown as Promise<T>
+  }
+}
+
+export default HttpRequest

+ 160 - 0
src/ libs/file.ts

@@ -0,0 +1,160 @@
+/*
+ * @Author: WangJiaCheng
+ * @Date: 2021-06-22 17:10:11
+ * @LastEditors: LiZhiWei
+ * @LastEditTime: 2025-04-16 09:53:14
+ * @Description: 文件相关工具函数
+ */
+import { Notification, Message } from 'element-ui'
+import { saveAs } from 'file-saver'
+
+import axios from './api.request'
+
+type FileInfo = {
+  name: string
+  size: string
+}
+
+/**
+ * 文件下载
+ * @author WangJiaCheng
+ * @param {string} obj  下载地址 | Blob
+ * @param {string} url  文件名,可选的
+ */
+export function saveDownload (obj: string, fileName?: string) {
+  try {
+    saveAs(obj, fileName)
+  } catch (e) {
+    console.log(e)
+  }
+}
+
+/**
+ * 进度条下载,使用Blob类型下载
+ * @param {string} url 文件下载地址
+ * @param {vnode} $vm 组件实例,使用中即是this
+ * @param {name, size} fileInfo 文件信息,包括文件名、文件大小
+ */
+export function downloadWithProgress (url: string, _vm, loading: string, fileInfo: FileInfo = { name: '', size: '' }, callBack) {
+  let p = 1
+  let progressTimer
+  const { name, size } = fileInfo
+  const notificationInstance = Notification({
+    title: '文件下载中,请稍候',
+    offset: 50,
+    customClass: 'notification-down-progress',
+    message: _vm.$createElement('el-progress', {
+      style: {
+        width: '280px'
+      },
+      props: {
+        percentage: p,
+        textInside: true,
+        strokeWidth: 18
+      },
+      ref: 'downloadProgressBar'
+    }),
+    duration: 0
+  })
+  const settingProgressBar = (p) => {
+    const progressBar = document.querySelector('.notification-down-progress .el-progress-bar__inner') as HTMLElement
+    const progressBarText = document.querySelector('.notification-down-progress .el-progress-bar__innerText') as HTMLElement
+    if (progressBarText && p) {
+      progressBarText.innerHTML = `${p}%`
+      progressBar.style.width = `${p}%`
+      progressBar.style.backgroundColor = '#409eff'
+    }
+  }
+  // 在等待后端响应blob数据前,创建虚拟的下载百分比,给用户反馈下载进度
+  if (notificationInstance) {
+    progressTimer = setInterval(() => {
+      settingProgressBar(p + 1)
+    }, 800)
+  }
+  axios.request({
+    url,
+    method: 'get',
+    params: { wc: new Date().getTime() }, // 后端默认需要字段
+    responseType: 'blob',
+    headers: {
+      Accept: 'application/json, text/plain, */*'
+    },
+    withCredentials: false,
+    timeout: 1000 * 60 * 10, // 10分钟
+    onDownloadProgress: (event) => {
+      const total = event.total || size
+      if (event) {
+        clearInterval(progressTimer)
+      }
+      if (event.target.status >= 200 && event.target.status < 300) {
+        p = Math.round((event.loaded * 100) / total)
+        if (total) {
+          settingProgressBar(p)
+        }
+        if (p >= 100) {
+          setTimeout(() => {
+            notificationInstance.close()
+          }, 500)
+        }
+      } else {
+        notificationInstance.close()
+      }
+    }
+  }).then((res) => {
+    settingProgressBar(100)
+    if (res.data) {
+      const downFileName = name || res.fileName
+      if (downFileName) {
+        saveDownload(res.data, downFileName)
+        _vm[loading] = false
+      } else {
+        saveDownload(url)
+      }
+      callBack()
+    }
+  }).catch((e) => {
+    notificationInstance.close()
+    _vm[loading] = false
+    const reader = new FileReader()
+    reader.onload = () => {
+      const { result } = reader
+      const errorInfos = JSON.parse(result as string)
+      const message = errorInfos.responseJSON.message || '下载异常,请稍后尝试'
+      Message({
+        type: 'error',
+        message: message
+      })
+    }
+    // reader.readAsText(e)
+  })
+}
+
+/**
+ * iframe 下载
+ * @param {string} url 下载链接
+ */
+export function downloadFile (url: string) {
+  const iframe = document.createElement('iframe')
+  iframe.style.display = 'none'
+  function iframeLoad () {
+    const win = iframe.contentWindow as Window
+    const doc = win.document
+    if (win.location.href === url) {
+      if (doc.body.childNodes.length > 0) {
+        // response is error
+      }
+      if (iframe.parentNode) {
+        iframe.parentNode.removeChild(iframe)
+      }
+    }
+  }
+  if ('onload' in iframe) {
+    iframe.onload = iframeLoad
+  }
+  iframe.src = ''
+  document.body.appendChild(iframe)
+
+  if (iframe.contentWindow) {
+    iframe.contentWindow.location.href = url
+  }
+}

+ 376 - 0
src/ libs/tools.js

@@ -0,0 +1,376 @@
+/*
+ * @Author: WangQiBiao
+ * @LastEditors: wzh
+ * @Description:
+ * @Date: 2019-03-12 09:40:46
+ * @LastEditTime: 2023-10-25 17:47:28
+ */
+export const arrDupliction = (arr) => {
+  return Array.from(new Set(arr))
+}
+export const forEach = (arr, fn) => {
+  if (!arr.length || !fn) return
+  let i = -1
+  let len = arr.length
+  while (++i < len) {
+    let item = arr[i]
+    fn(item, i, arr)
+  }
+}
+
+/**
+ * @param {Array} arr1
+ * @param {Array} arr2
+ * @description 得到两个数组的交集, 两个数组的元素为数值或字符串
+ */
+export const getIntersection = (arr1, arr2) => {
+  let len = Math.min(arr1.length, arr2.length)
+  let i = -1
+  let res = []
+  while (++i < len) {
+    const item = arr2[i]
+    if (arr1.indexOf(item) > -1) res.push(item)
+  }
+  return res
+}
+
+/**
+ * @param {Array} arr1
+ * @param {Array} arr2
+ * @description 得到两个数组的并集, 两个数组的元素为数值或字符串
+ */
+export const getUnion = (arr1, arr2) => {
+  return Array.from(new Set([...arr1, ...arr2]))
+}
+
+/**
+ * @param {Array} target 目标数组
+ * @param {Array} arr 需要查询的数组
+ * @description 判断要查询的数组是否至少有一个元素包含在目标数组中
+ */
+export const hasOneOf = (targetarr, arr) => {
+  return targetarr.some(_ => arr.indexOf(_) > -1)
+}
+
+/**
+ * @param {Array} arr1
+ * @param {Array} arr2
+ * @description 判断两个数组是否相同 (含有相同元素)
+ */
+export const getArrayEqual = (arr1, arr2) => {
+  if (!Array.isArray(arr1) || !Array.isArray(arr2) || arr1.length !== arr2.length) {
+    return false
+  }
+  let len = arr1.length
+  let i = -1
+  while (++i < len) {
+    if (arr1[i] !== arr2[i]) { return false }
+  }
+  return true
+}
+
+/**
+ * @param {String|Number} value 要验证的字符串或数值
+ * @param {*} validList 用来验证的列表
+ */
+export function oneOf (value, validList) {
+  for (let i = 0; i < validList.length; i++) {
+    if (value === validList[i]) {
+      return true
+    }
+  }
+  return false
+}
+
+/**
+ * @param {Number} timeStamp 判断时间戳格式是否是毫秒
+ * @returns {Boolean}
+ */
+const isMillisecond = timeStamp => {
+  const timeStr = String(timeStamp)
+  return timeStr.length > 10
+}
+
+/**
+ * @param {Number} timeStamp 传入的时间戳
+ * @param {Number} currentTime 当前时间时间戳
+ * @returns {Boolean} 传入的时间戳是否早于当前时间戳
+ */
+const isEarly = (timeStamp, currentTime) => {
+  return timeStamp < currentTime
+}
+
+/**
+ * @param {Number} num 数值
+ * @returns {String} 处理后的字符串
+ * @description 如果传入的数值小于10,即位数只有1位,则在前面补充0
+ */
+const getHandledValue = num => {
+  return num < 10 ? '0' + num : num
+}
+
+/**
+ * @param {Number} timeStamp 传入的时间戳
+ * @param {Number} startType 要返回的时间字符串的格式类型,传入'year'则返回年开头的完整时间
+ */
+const getDate = (timeStamp, startType) => {
+  const d = new Date(timeStamp * 1000)
+  const year = d.getFullYear()
+  const month = getHandledValue(d.getMonth() + 1)
+  const date = getHandledValue(d.getDate())
+  const hours = getHandledValue(d.getHours())
+  const minutes = getHandledValue(d.getMinutes())
+  const second = getHandledValue(d.getSeconds())
+  let resStr = ''
+  if (startType === 'year') resStr = year + '-' + month + '-' + date + ' ' + hours + ':' + minutes + ':' + second
+  else resStr = month + '-' + date + ' ' + hours + ':' + minutes
+  return resStr
+}
+
+/**
+ * @param {String|Number} timeStamp 时间戳
+ * @returns {String} 相对时间字符串
+ */
+export const getRelativeTime = timeStamp => {
+  // 判断当前传入的时间戳是秒格式还是毫秒
+  const IS_MILLISECOND = isMillisecond(timeStamp)
+  // 如果是毫秒格式则转为秒格式
+  if (IS_MILLISECOND) Math.floor(timeStamp /= 1000)
+  // 传入的时间戳可以是数值或字符串类型,这里统一转为数值类型
+  timeStamp = Number(timeStamp)
+  // 获取当前时间时间戳
+  const currentTime = Math.floor(Date.parse(new Date()) / 1000)
+  // 判断传入时间戳是否早于当前时间戳
+  const IS_EARLY = isEarly(timeStamp, currentTime)
+  // 获取两个时间戳差值
+  let diff = currentTime - timeStamp
+  // 如果IS_EARLY为false则差值取反
+  if (!IS_EARLY) diff = -diff
+  let resStr = ''
+  const dirStr = IS_EARLY ? '前' : '后'
+  // 少于等于59秒
+  if (diff <= 59) resStr = diff + '秒' + dirStr
+  // 多于59秒,少于等于59分钟59秒
+  else if (diff > 59 && diff <= 3599) resStr = Math.floor(diff / 60) + '分钟' + dirStr
+  // 多于59分钟59秒,少于等于23小时59分钟59秒
+  else if (diff > 3599 && diff <= 86399) resStr = Math.floor(diff / 3600) + '小时' + dirStr
+  // 多于23小时59分钟59秒,少于等于29天59分钟59秒
+  else if (diff > 86399 && diff <= 2623859) resStr = Math.floor(diff / 86400) + '天' + dirStr
+  // 多于29天59分钟59秒,少于364天23小时59分钟59秒,且传入的时间戳早于当前
+  else if (diff > 2623859 && diff <= 31567859 && IS_EARLY) resStr = getDate(timeStamp)
+  else resStr = getDate(timeStamp, 'year')
+  return resStr
+}
+
+/**
+ * @returns {String} 当前浏览器名称
+ */
+export const getExplorer = () => {
+  const ua = window.navigator.userAgent
+  const isExplorer = (exp) => {
+    return ua.indexOf(exp) > -1
+  }
+  if (isExplorer('MSIE')) return 'IE'
+  else if (isExplorer('Firefox')) return 'Firefox'
+  else if (isExplorer('Chrome')) return 'Chrome'
+  else if (isExplorer('Opera')) return 'Opera'
+  else if (isExplorer('Safari')) return 'Safari'
+}
+
+/**
+ * @description 绑定事件 on(element, event, handler)
+ */
+export const on = (function () {
+  if (document.addEventListener) {
+    return function (element, event, handler) {
+      if (element && event && handler) {
+        element.addEventListener(event, handler, false)
+      }
+    }
+  } else {
+    return function (element, event, handler) {
+      if (element && event && handler) {
+        element.attachEvent('on' + event, handler)
+      }
+    }
+  }
+})()
+
+/**
+ * @description 解绑事件 off(element, event, handler)
+ */
+export const off = (function () {
+  if (document.removeEventListener) {
+    return function (element, event, handler) {
+      if (element && event) {
+        element.removeEventListener(event, handler, false)
+      }
+    }
+  } else {
+    return function (element, event, handler) {
+      if (element && event) {
+        element.detachEvent('on' + event, handler)
+      }
+    }
+  }
+})()
+
+/**
+ * 判断一个对象是否存在key,如果传入第二个参数key,则是判断这个obj对象是否存在key这个属性
+ * 如果没有传入key这个参数,则判断obj对象是否有键值对
+ */
+export const hasKey = (obj, key) => {
+  if (key) return key in obj
+  else {
+    let keysArr = Object.keys(obj)
+    return keysArr.length
+  }
+}
+
+/**
+ * @param {*} obj1 对象
+ * @param {*} obj2 对象
+ * @description 判断两个对象是否相等,这两个对象的值只能是数字或字符串
+ */
+export const objEqual = (obj1, obj2) => {
+  const keysArr1 = Object.keys(obj1)
+  const keysArr2 = Object.keys(obj2)
+  if (keysArr1.length !== keysArr2.length) return false
+  else if (keysArr1.length === 0 && keysArr2.length === 0) return true
+  /* eslint-disable-next-line */
+  else return !keysArr1.some(key => obj1[key] != obj2[key])
+}
+/**
+ * 输入的值是否合法
+ * @只能输入1~9数字
+ * @小数点不能超过两个
+ * @小数点位数不能超过两个
+ */
+export const validateInputLegal = (value) => {
+  const slen = String(value).split('.')
+  if (!(/[\d.]/.test(value))) { // 非数字或者'.'
+    return false
+  } else if ((slen && slen.length > 2) || (slen && slen[1] && slen[1].length > 2)) { // 是否只有一个小数点并且小数点只有后两位
+    return false
+  } else {
+    return true
+  }
+}
+/**
+ * 函数 名为节流 实为防抖
+ * @param method {function} 防抖函数
+ * @param delay {number} 防抖时间
+ */
+export const throttle = (method, delay) => {
+  let timer = null
+  return function () {
+    let context = this
+    let args = arguments
+    clearTimeout(timer)
+    timer = setTimeout(function () {
+      method.apply(context, args)
+    }, delay)
+  }
+}
+/**
+ * 获取数组中的重复元素
+ * @param arr 数组
+ */
+export const refrainArr = arr => {
+  let tmp = []
+  if (Array.isArray(arr)) {
+    arr.concat().sort().sort(function (a, b) {
+      if (a === b && tmp.indexOf(a) === -1) tmp.push(a)
+    })
+  }
+  return tmp
+}
+// 导出excle
+export const exportExcle = (url, data) => {
+
+}
+
+/**
+ * @param {Number} money 大额数值
+ * @param {Number} fiexed 保留几位数
+ * @returns {Number} 无单位的数值
+ */
+export const getBigNumber = (money, fiexed = 2) => {
+  if (money >= 10000) {
+    return Number((Number(money) / 10000).toFixed(fiexed))
+  } else {
+    return Number(money)
+  }
+}
+
+/**
+ * @param {Number} money 大额数值
+ * @param {Number} fiexed 保留几位数
+ * @returns {String} 单位是万
+ */
+export const getBigNumberWithUint = (money, fiexed = 2) => {
+  if (money >= 10000) {
+    return getBigNumber(money, fiexed) + '万'
+  } else {
+    return money
+  }
+}
+/**
+ * 深度冻结对象
+ * @param obj 数组或对象
+ */
+export const deepFreeze = (obj) => {
+  var prop, propKey
+  Object.freeze(obj)
+  for (propKey in obj) {
+    prop = obj[propKey]
+    if (!obj.hasOwnProperty(propKey) || !(typeof prop === 'object') || Object.isFrozen(prop)) {
+      continue
+    }
+    deepFreeze(prop)
+  }
+}
+
+/**
+ * 字符串 在指定位置插入新字符串
+ * @param str 原字符串
+ * @param char 要插入的字符串
+ * @param index 要插入的位置,从0开始
+ */
+export const insertString = (str, char, index) => {
+  return str.slice(0, index) + char + str.slice(index)
+}
+
+/**
+ * 用子树的节点来替换主树的节点,节省内存
+ * @param {Array} tree 子树
+ * @param {Array} treeMain 主树
+ * @param {string} valueKey 节点唯一key,用来比较是否为同一个节点
+ */
+export const replaceTreeNode = (tree, treeMain, valueKey = 'id') => {
+  if (!tree && !treeMain) return []
+  if (!tree || (Array.isArray(tree) && tree.length === 0)) return treeMain
+  if (!treeMain || (Array.isArray(treeMain) && treeMain.length === 0)) return tree
+
+  if (!Array.isArray(tree)) tree = [tree]
+  if (!Array.isArray(treeMain)) treeMain = [treeMain]
+
+  const childMap = new Map()
+  treeMain.forEach(child => childMap.set(child[valueKey], child))
+
+  for (let i = 0; i < tree.length; i++) {
+    const el = tree[i]
+    const matchingChild = childMap.get(el[valueKey])
+    if (matchingChild) {
+      if (el.children && matchingChild.children && matchingChild.children.length > 0) {
+        matchingChild.children = replaceTreeNode(el.children, matchingChild.children)
+      } else {
+        treeMain[i] = matchingChild
+      }
+    } else {
+      treeMain.push(el)
+    }
+  }
+
+  return treeMain
+}

+ 1093 - 0
src/ libs/util.js

@@ -0,0 +1,1093 @@
+/*
+ * @Author: WangQiBiao
+ * @LastEditors: LiZhiWei
+ * @Description:
+ * @Date: 2019-04-16 11:44:25
+ * @LastEditTime: 2025-04-15 15:44:32
+ */
+import html2canvas from 'html2canvas'
+import { saveAs } from 'file-saver'
+import Cookies from 'js-cookie'
+// cookie保存的天数
+import config from '@/config'
+import { hasOneOf, objEqual } from './tools'
+
+// import beforeClose from '@/router/before-close'
+
+export * from './file'
+
+export const TOKEN_KEY = 'token'
+
+// 缓存表格列宽
+export const setTableHeaderDragend = (name, newWidth, column) => {
+  const tableHeaderData = JSON.parse(window.localStorage.getItem(name)) || []
+  if (tableHeaderData.length) {
+    if (tableHeaderData.some(h => h.property === column.property)) {
+      tableHeaderData[tableHeaderData.findIndex(i => i.property === column.property)].width = newWidth
+    } else {
+      tableHeaderData.push({ property: column.property, width: newWidth })
+    }
+  } else {
+    tableHeaderData.push({ property: column.property, width: newWidth })
+  }
+  window.localStorage.setItem(name, JSON.stringify(tableHeaderData))
+}
+// 获取表格列宽
+export const getTableHeaderDragend = (name) => {
+  return JSON.parse(window.localStorage.getItem(name)) || []
+}
+
+export const getTableColumnWidth = (name, property) => {
+  const tableHeaderData = JSON.parse(window.localStorage.getItem(name)) || []
+  if (tableHeaderData.some(h => h.property === property)) {
+    return tableHeaderData[tableHeaderData.findIndex(i => i.property === property)].width
+  } else {
+    return 150
+  }
+}
+
+export const setToken = token => {
+  Cookies.set(TOKEN_KEY, token, { expires: config.cookieExpires || 1 })
+}
+
+export const removeToken = () => {
+  Cookies.remove(TOKEN_KEY)
+}
+
+export const getToken = () => {
+  const token = Cookies.get(TOKEN_KEY)
+  if (token) return token
+  else return false
+}
+
+export const getCookie = (key) => {
+  return Cookies.get(key)
+}
+
+export const removeCookie = (key, options = {}) => {
+  Cookies.remove(key, options)
+}
+
+export const removeAllCookie = () => {
+  Object.keys(Cookies.get()).forEach(key => Cookies.remove(key))
+}
+
+export const hasChild = item => {
+  return item.children && item.children.length !== 0
+}
+
+const showThisMenuEle = (item, access) => {
+  if (item.meta && item.meta.access && item.meta.access.length) {
+    if (hasOneOf(item.meta.access, access)) return true
+    else return false
+  } else return true
+}
+
+/**
+ * 根据接口返回的路由配置权限路由
+ * @param {Array} asyncRouter 本地路由
+ * @param {Array} menus 接口返回的经过格式化的路由
+ * @returns newMenu 新的权限路由
+ */
+export const asyncRouterMap = (asyncRouter = [], menus = []) => {
+  const newMenu = []
+  asyncRouter.forEach(route => {
+    if (route.name && (route && route.meta && (route.meta.hideInMenu || route.meta.authorized))) {
+      // 对于一级路由设置隐藏且没有对应菜单的路由
+      newMenu.push(route)
+    } else {
+      menus.forEach(menu => {
+        if (route.name === menu.name && menu.meta.type !== 'button') {
+          if (route.children && (menu.children && menu.children.length > 0)) {
+            // 经过和菜单比较后得到的菜单路由
+            const compareChild = route.children.filter(item => {
+              const mChild = menu.children.find(mChild => mChild.name === item.name)
+              if (mChild) {
+                return item
+              } else if (item && item.meta && (item.meta.hideInMenu || item.meta.authorized)) {
+                return item
+              }
+            })
+            let newRoute = {}
+            const { children, ...reset } = route
+            newRoute = reset
+            newRoute.children = compareChild
+            newMenu.push(newRoute)
+          } else {
+            // 只有一级路由,没有二级路由
+            const { redirect } = route
+            newMenu.push({ ...menu, redirect })
+          }
+        }
+      })
+    }
+  })
+  return newMenu
+}
+
+/**
+ * @param {Array} list 通过路由列表得到菜单列表
+ * @returns {Array}
+ */
+export const getMenuByRouter = (list = [], access) => {
+  let res = []
+  const getMenus = (l, a) => {
+    l.forEach(item => {
+      if (!item.meta || (item.meta && !item.meta.hideInMenu)) {
+        let obj = {
+          icon: (item.meta && item.meta.icon) || '',
+          name: item.path.replace(/\//, '') || '_', // 过滤 '/'
+          meta: item.meta,
+          path: item.path
+        }
+        if ((hasChild(item) || (item.meta && item.meta.showAlways)) && showThisMenuEle(item, a)) {
+          obj.children = getMenuByRouter(item.children, a)
+        }
+        if (item.meta && item.meta.href) obj.href = item.meta.href
+        if (showThisMenuEle(item, a)) res.push(obj)
+      }
+    })
+  }
+  getMenus(list, access)
+  return res
+}
+/**
+ * 根据权限匹配路由
+ * @param {Array} permission 权限路由
+ * @param {Array} asyncRouter 异步路由
+ */
+export const routerByMatch = (permission, asyncRouter) => {
+  permission = JSON.parse(permission)
+  // 菜单路由表
+  const router = []
+  function createRouter (permission) {
+    // 根据路由名称匹配到router对象添加到router中
+    permission && permission.forEach(item => {
+      if (item.children && item.children.length) {
+        createRouter(item.children)
+      }
+      let routerName = item.route
+      // 过滤路由名称为null的情况
+      if (routerName) {
+        // 循环异步路由,将符合权限列表路由加入路由表中
+        asyncRouter.find(s => {
+          if (s.name === routerName) {
+            router.push(s)
+          }
+        })
+      }
+    })
+  }
+  createRouter(permission)
+  return router
+}
+
+/**
+ * @param {Array} routeMetched 当前路由metched
+ * @returns {Array}
+ */
+export const getBreadCrumbList = (route) => {
+  let routeMetched = route.matched
+  let res = routeMetched
+    .filter(item => {
+      return item.meta === undefined || !item.meta.hide
+    })
+    .map(item => {
+      let meta = { ...item.meta }
+      if (meta.title && typeof meta.title === 'function') meta.title = meta.title(route)
+      let obj = {
+        icon: (item.meta && item.meta.icon) || '',
+        name: item.name,
+        meta: meta
+      }
+      return obj
+    })
+  res = res.filter(item => {
+    return !item.meta.hideInMenu
+  })
+  // return [Object.assign(homeRoute, { to: homeRoute.path }), ...res]
+  return res
+}
+
+export const getRouteTitleHandled = route => {
+  let router = { ...route }
+  let meta = { ...route.meta }
+  if (meta.title && typeof meta.title === 'function') meta.title = meta.title(router)
+  router.meta = meta
+  return router
+}
+
+export const showTitle = (item, vm) => {
+  return vm.$config.useI18n ? vm.$t(item.name) : (item.meta && item.meta.title) || item.name
+}
+
+/**
+ * @description 本地存储和获取标签导航列表
+ */
+export const setTagNavListInLocalstorage = list => {
+  localStorage.tagNavList = JSON.stringify(list)
+}
+/**
+ * @returns {Array} 其中的每个元素只包含路由原信息中的name, path, meta三项
+ */
+export const getTagNavListFromLocalstorage = () => {
+  const list = localStorage.tagNavList
+  return list ? JSON.parse(list) : []
+}
+
+/**
+ * @param {Array} routers 路由列表数组
+ * @description 用于找到路由列表中name为home的对象
+ */
+export const getHomeRoute = routers => {
+  if (!routers) {
+    return
+  }
+  let i = -1
+  let len = routers.length
+  let homeRoute = {}
+  while (++i < len) {
+    let item = routers[i]
+    if (item.children && item.children.length) {
+      let res = getHomeRoute(item.children)
+      if (res.name) return res
+    } else {
+      // if (item.name === 'home') homeRoute = item
+      // 目前没有主页、判断为第一个路由、后期加上主页  把这行去掉  取消上行注释
+      if (item.name === routers[0].name) homeRoute = item
+    }
+  }
+  return homeRoute
+}
+
+/**
+ * @param {*} list 现有标签导航列表
+ * @param {*} newRoute 新添加的路由原信息对象
+ * @description 如果该newRoute已经存在则不再添加
+ */
+export const getNewTagList = (list, newRoute) => {
+  const { name, path, meta } = newRoute
+  let newList = [...list]
+  if (newList.findIndex(item => item.name === name) >= 0) return newList
+  else newList.push({ name, path, meta })
+  return newList
+}
+
+/**
+ * @param {*} access 用户权限数组,如 ['super_admin', 'admin']
+ * @param {*} route 路由列表
+ */
+const hasAccess = (access, route) => {
+  if (route.meta && route.meta.access) return hasOneOf(access, route.meta.access)
+  else return true
+}
+
+/**
+ * 权鉴
+ * @param {*} name 即将跳转的路由name
+ * @param {*} access 用户权限数组
+ * @param {*} routes 路由列表
+ * @description 用户是否可跳转到该页
+ */
+export const canTurnTo = (name, access, routes) => {
+  const routePermissionJudge = list => {
+    return list.some(item => {
+      if (item.children && item.children.length) {
+        return routePermissionJudge(item.children)
+      } else if (item.name === name) {
+        return hasAccess(access, item)
+      }
+    })
+  }
+
+  return routePermissionJudge(routes)
+}
+
+/**
+ * @param {String} url
+ * @description 从URL中解析参数
+ */
+export const getParams = url => {
+  const keyValueArr = url.split('?')[1].split('&')
+  let paramObj = {}
+  keyValueArr.forEach(item => {
+    const keyValue = item.split('=')
+    paramObj[keyValue[0]] = keyValue[1]
+  })
+  return paramObj
+}
+
+/**
+ * @param {Array} list 标签列表
+ * @param {String} name 当前关闭的标签的name
+ */
+export const getNextRoute = (list, route) => {
+  let res = {}
+  if (list.length === 2) {
+    res = getHomeRoute(list)
+  } else {
+    const index = list.findIndex(item => routeEqual(item, route))
+    if (index === list.length - 1) res = list[list.length - 2]
+    else res = list[index + 1]
+  }
+  return res
+}
+
+/**
+ * @param {Number} times 回调函数需要执行的次数
+ * @param {Function} callback 回调函数
+ */
+export const doCustomTimes = (times, callback) => {
+  let i = -1
+  while (++i < times) {
+    callback(i)
+  }
+}
+
+/**
+ * @param {Object} file 从上传组件得到的文件对象
+ * @returns {Promise} resolve参数是解析后的二维数组
+ * @description 从Csv文件中解析出表格,解析成二维数组
+ */
+export const getArrayFromFile = file => {
+  let nameSplit = file.name.split('.')
+  let format = nameSplit[nameSplit.length - 1]
+  return new Promise((resolve, reject) => {
+    let reader = new FileReader()
+    reader.readAsText(file) // 以文本格式读取
+    let arr = []
+    reader.onload = function (evt) {
+      let data = evt.target.result // 读到的数据
+      let pasteData = data.trim()
+      arr = pasteData
+        .split(/[\n\u0085\u2028\u2029]|\r\n?/g)
+        .map(row => {
+          return row.split('\t')
+        })
+        .map(item => {
+          return item[0].split(',')
+        })
+      if (format === 'csv') resolve(arr)
+      else reject(new Error('[Format Error]:你上传的不是Csv文件'))
+    }
+  })
+}
+
+/**
+ * @param {Array} array 表格数据二维数组
+ * @returns {Object} { columns, tableData }
+ * @description 从二维数组中获取表头和表格数据,将第一行作为表头,用于在iView的表格中展示数据
+ */
+export const getTableDataFromArray = array => {
+  let columns = []
+  let tableData = []
+  if (array.length > 1) {
+    let titles = array.shift()
+    columns = titles.map(item => {
+      return {
+        title: item,
+        key: item
+      }
+    })
+    tableData = array.map(item => {
+      let res = {}
+      item.forEach((col, i) => {
+        res[titles[i]] = col
+      })
+      return res
+    })
+  }
+  return {
+    columns,
+    tableData
+  }
+}
+
+export const findNodeUpper = (ele, tag) => {
+  if (ele.parentNode) {
+    if (ele.parentNode.tagName === tag.toUpperCase()) {
+      return ele.parentNode
+    } else {
+      return findNodeUpper(ele.parentNode, tag)
+    }
+  }
+}
+
+export const findNodeUpperByClasses = (ele, classes) => {
+  let parentNode = ele.parentNode
+  if (parentNode) {
+    let classList = parentNode.classList
+    if (classList && classes.every(className => classList.contains(className))) {
+      return parentNode
+    } else {
+      return findNodeUpperByClasses(parentNode, classes)
+    }
+  }
+}
+
+export const findNodeDownward = (ele, tag) => {
+  const tagName = tag.toUpperCase()
+  if (ele.childNodes.length) {
+    let i = -1
+    let len = ele.childNodes.length
+    while (++i < len) {
+      let child = ele.childNodes[i]
+      if (child.tagName === tagName) return child
+      else return findNodeDownward(child, tag)
+    }
+  }
+}
+
+export const showByAccess = (access, canViewAccess) => {
+  return hasOneOf(canViewAccess, access)
+}
+
+/**
+ * @description 根据name/params/query判断两个路由对象是否相等
+ * @param {*} route1 路由对象
+ * @param {*} route2 路由对象
+ */
+export const routeEqual = (route1, route2) => {
+  const query1 = route1.query || {}
+  const query2 = route2.query || {}
+  return route1.name === route2.name
+}
+
+/**
+ * 判断打开的标签列表里是否已存在这个新添加的路由对象
+ */
+export const routeHasExist = (tagNavList, routeItem) => {
+  let len = tagNavList.length
+  let res = false
+  doCustomTimes(len, index => {
+    if (routeEqual(tagNavList[index], routeItem)) res = true
+  })
+  return res
+}
+
+export const showLoadingFunc = (obj, flag) => {
+  let arr = obj.$root.$children
+  if (arr.length > 0) {
+    arr[0].isShowLoading = flag
+  }
+}
+
+export const dateFormat = (obj, fmt) => {
+  if (typeof obj === 'string') {
+    return obj
+  }
+  if (obj && obj.getFullYear) {
+    var o = {
+      'M+': obj.getMonth() + 1, // 月份
+      'd+': obj.getDate(), // 日
+      'H+': obj.getHours(), // 小时
+      'h+': obj.getHours(), // 小时
+      'm+': obj.getMinutes(), // 分
+      's+': obj.getSeconds(), // 秒
+      'q+': Math.floor((obj.getMonth() + 3) / 3), // 季度
+      S: obj.getMilliseconds() // 毫秒
+    }
+    if (/(y+)/.test(fmt)) {
+      fmt = fmt.replace(/(y+)/, (match, p1) => (obj.getFullYear() + '').substring(4 - p1.length))
+    }
+    for (var k in o) {
+      let reg = new RegExp('(' + k + ')')
+      if (reg.test(fmt)) {
+        fmt = fmt.replace(reg, (match, p1) => p1.length === 1 ? o[k] : ('00' + o[k]).substring(('' + o[k]).length))
+      }
+    }
+    return fmt
+  } else {
+    return ''
+  }
+}
+
+export const getYearWeek = (date) => {
+  /*
+      dateNow是当前日期
+      dateFirst是当年第一天
+      dataNumber是当前日期是今年第多少天
+      用dataNumber + 当前年的第一天的周差距的和在除以7就是本年第几周
+  */
+  let dateNow = date instanceof Date ? date : convertDateFromString(date, 'yyyy-MM-dd')
+  let dateFirst = new Date(dateNow.getFullYear(), 0, 1)
+  let dataNumber = Math.round((dateNow.valueOf() - dateFirst.valueOf()) / 86400000)
+  return Math.ceil((dataNumber + ((dateFirst.getDay() + 1) - 1)) / 7)
+}
+
+// 天-小时-分钟
+export const dateRule = (value) => {
+  // 需要转换的时间-秒单位
+  /**
+   * var theTime = parseInt(value);
+   */
+  var theTime1 = 0 // 分
+  var theTime2 = 0 // 小时
+  var theTime3 = 0 // 天
+  if (value > 60) {
+    theTime1 = parseInt(value / 60)
+    /**
+       * theTime = parseInt(theTime % 60);
+       */
+    if (theTime1 > 60) {
+      theTime2 = parseInt(theTime1 / 60)
+      theTime1 = parseInt(theTime1 % 60)
+      if (theTime2 > 24) {
+        // 大于24小时
+        theTime3 = parseInt(theTime2 / 24)
+        theTime2 = parseInt(theTime2 % 24)
+      }
+    }
+  }
+  var result = ''
+  /**
+   *  if (theTime > 0) {
+      result = "" + parseInt(theTime) + "秒";
+      }
+   */
+
+  if (theTime1 > 0) {
+    if (theTime2 > 0 || theTime3 > 0) {
+      result = '' + parseInt(theTime1) + '分' + result
+    } else {
+      result = '' + parseInt(theTime1) + '分钟' + result
+    }
+  }
+  if (theTime2 > 0) {
+    result = '' + parseInt(theTime2) + '小时' + result
+  }
+  if (theTime3 > 0) {
+    result = '' + parseInt(theTime3) + '天' + result
+  }
+  return result
+}
+export const convertDateFromString = (dateString, fmt) => {
+  if (dateString) {
+    if (fmt === 'yyyy-MM-dd' || fmt === 'yyyy-MM-dd hh:mm:ss') {
+      return new Date(dateString.replace(/-/g, '/'))
+    } else {
+      return null
+    }
+  } else {
+    return null
+  }
+}
+
+/**
+ * @description 日期去掉时分秒
+ * @param string value 日期
+ */
+export const dateRemoveTime = dateString => {
+  return dateString && dateString.split(' ')[0]
+}
+
+export const ObjectNone = object => {
+  if (JSON.stringify(object) === '{}') return true
+  else return false
+}
+
+/**
+ * @description 校验邮编号码 长度6位数字
+ * @param string value 值
+ */
+export const testPostal = value => {
+  return !/^\d{6}$/.test(value)
+}
+
+/**
+ * @description 校验手机号码
+ * @param string value 值
+ */
+export const testMobile = value => {
+  return !/^1[3|4|5|6|7|8|9][0-9]\d{8}$/.test(value)
+}
+
+/**
+ * @description 校验邮箱
+ * @param string value 值
+ */
+export const checkEmail = value => {
+  return !/^[a-zA-Z0-9]+([a-zA-Z0-9-_.]*)@([a-zA-Z0-9]+[-|_|.]?)*[a-zA-Z0-9]+\.[a-zA-Z]{2,4}$/g.test(value)
+}
+
+/**
+ * @description 校验固定电话
+ * @param string value 值
+ */
+export const testPhone = value => {
+  return !/^(\(\d{3,4}\)|\d{3,4}-|\s)?\d{7,14}$/.test(value)
+}
+
+/**
+ * @description 校验银行卡
+ * @param string value 值
+ */
+export const testBankNo = value => {
+  // 陈曦说8到30 2020年7月10日
+  return !/^(\d{8,30})$/.test(value)
+}
+/**
+ * @description 校验5位数短号 10086、95119
+ * @param string value 值
+ */
+export const test5ShortPhone = value => {
+  return !/^(1|9){1}\d{4}$/.test(value)
+}
+
+/**
+ * @description 校验3位数短号, 110、120
+ * @param string value 值
+ */
+export const test3ShortPhone = value => {
+  return !/^1\d{2}$/.test(value)
+}
+
+/**
+ * @description 校验400虚拟号, 400-600-7709
+ * @param string value 值
+ */
+export const test400Phone = value => {
+  return !/^(400)-(\d{3})-(\d{4}$)/.test(value)
+}
+
+/**
+ * @description 校验800虚拟号, 800-600-7709
+ * @param string value 值
+ */
+export const test800Phone = value => {
+  return !/^(800)-(\d{3})-(\d{4}$)/.test(value)
+}
+
+/**
+ * @description 校验身份证18位
+ * @param string value 值
+ */
+export const testIdCard = value => {
+  return !/^[1-9]\d{5}(18|19|([23]\d))\d{2}((0[1-9])|(10|11|12))(([0-2][1-9])|10|20|30|31)\d{3}[0-9Xx]$/.test(value)
+}
+
+/**
+ * @description 纳税人识别号 统一社会信用代码
+ * @param string value 值
+ */
+export const testUniformSocialCreditCode = value => {
+  return /^[^_IOZSVa-z\W]{2}\d{6}[^_IOZSVa-z\W]{10}$|^[1-9][0-9A-Z]{14}$|^[A-Z0-9]{20}$/.test(value)
+}
+
+/**
+ * @description 自定义导航标签名
+ * @param vm 组件实例
+ * @param title 导航名称
+ */
+export const customTagNavList = (vm, title) => {
+  vm.$store.state.app.tagNavList.forEach(item => {
+    if (routeEqual(item, vm.$route)) {
+      let { meta } = item
+      vm.$set(
+        meta,
+        'title',
+        title
+      )
+    }
+  })
+  if (vm.$store.state.app.tagNavList.length) {
+    setTagNavListInLocalstorage(vm.$store.state.app.tagNavList)
+  }
+}
+
+/**
+ * @description 替换同路由导航标签
+ * @param vm 组件实例
+ * @param route 当前路由
+ */
+export const replaceTagNavItem = (vm, { name, query, params }) => {
+  query = query || {}
+  params = params || {}
+  vm.$store.state.app.tagNavList.forEach(item => {
+    if (item.name === name && (!objEqual(item.query, query) || !objEqual(item.params, params))) {
+      vm.$set(
+        item,
+        'query',
+        query
+      )
+      vm.$set(
+        item,
+        'params',
+        params
+      )
+    }
+  })
+
+  if (vm.$store.state.app.tagNavList.length) {
+    setTagNavListInLocalstorage(vm.$store.state.app.tagNavList)
+  }
+}
+
+/**
+ * @description 手动关闭导航标签
+ * @param vm 组件实例
+ * @param route 当前路由
+ * @param callback 回调函数
+ */
+// export const handleTagNavClose = (vm, route, callback) => {
+//   let list = JSON.parse(JSON.stringify(vm.$store.state.app.tagNavList))
+//   if (route.meta && route.meta.beforeCloseName && route.meta.beforeCloseName in beforeClose) {
+//     new Promise(beforeClose[route.meta.beforeCloseName]).then(close => {
+//       if (close) {
+//         vm.$store.state.app.tagNavList = list.filter(item => !routeEqual(route, item))
+//       }
+//     })
+//   } else {
+//     vm.$store.state.app.tagNavList = list.filter(item => !routeEqual(route, item))
+//   }
+
+//   if (vm.$store.state.app.tagNavList.length) {
+//     setTagNavListInLocalstorage(vm.$store.state.app.tagNavList)
+//   }
+
+//   callback && callback()
+// }
+
+/**
+ * 递归格式化tree
+ * 返回满足要的tree结构
+ * @param dataArr {array} tree数据
+ * @param options.value {string} 值
+ * @param options.children {array} 子孙节点
+ * @param options.label {string} 显示内容
+ * @param expand {boolean} 是否展开
+ * @param selected {string} 要选中的元素
+ */
+export const recursionFormatTreeJson = (data, opt = { value: 'id', valueKey: 'value', label: 'label', labelKey: 'label', children: 'children', special: true }, selected) => {
+  const res = []
+  let obj = {}
+  data.forEach(row => {
+    const tmp = { ...row }
+    if (tmp.children) {
+      tmp.children = recursionFormatTreeJson(tmp.children, opt, selected)
+      obj = {
+        [opt.valueKey]: opt.special ? tmp[opt.value] : `${tmp[opt.value]},${tmp[opt.label]}`,
+        [opt.labelKey]: tmp[opt.label],
+        label: tmp[opt.label],
+        expand: true,
+        id: tmp[opt.value],
+        type: tmp['type'] || null,
+        selected: (tmp[opt.value] === selected),
+        [opt.children]: tmp[opt.children] && tmp[opt.children].length > 0 ? tmp[opt.children] : null
+      }
+    }
+    res.push(obj)
+  })
+  return res
+}
+
+/**
+ * 递归初始化tree
+ * 返回初始化tree,默认展开选择selected
+ * @param data {array} tree数据
+ * @param selected {string} 要选中的元素
+ */
+export const recursionInitTreeJson = (data, selected) => {
+  let res = false
+  for (let i = 0, len = data.length; i < len; i++) {
+    if (data[i].value === selected) {
+      data[i].selected = true
+      data[i].expand = true
+      return true
+    }
+
+    if (data[i].children) {
+      if ((i === 0) || (data[i - 1] && !data[i - 1].expand)) {
+        res = recursionInitTreeJson(data[i].children, selected)
+        data[i].expand = res
+      } else {
+        return true
+      }
+    }
+  }
+  return res
+}
+/**
+ * 获取当前选中地址单元描述信息
+ * @param data {array} 地址单元信息
+ * @param row {object} 当前地址信息
+ */
+export const getCommunityTitleInfo = (data, row) => {
+  let resInfo = []
+  data.filter(item => {
+    // 小区名称
+    if (item.communityId === row.communityId) {
+      resInfo.push(item.title)
+      // 栋名称
+      item.children.filter(itemBuild => {
+        if (itemBuild.buildId === row.buildId) {
+          resInfo.push(itemBuild.title)
+          // 单元名称
+          itemBuild.children.filter(itemUnit => {
+            if (itemUnit.unitId === row.unitId) {
+              resInfo.push(itemUnit.title)
+              // 房屋号
+              itemUnit.children.filter(itemHouse => {
+                if (itemHouse.houseId === row.houseId) {
+                  resInfo.push(itemHouse.title)
+                }
+              })
+            }
+          })
+        }
+      })
+    }
+  })
+  return resInfo.join(' / ')
+}
+/**
+ * 函数节流
+ */
+export const throttle = (callback, delay) => {
+  let timer = null
+  let previous = new Date()
+
+  return function () {
+    let now = new Date()
+    let remaining = now - previous
+    let args = arguments
+    let context = this
+
+    if (remaining >= delay) {
+      if (timer) {
+        clearTimeout(timer)
+      }
+
+      callback.apply(context, args)
+      previous = now
+    } else {
+      if (!timer) {
+        timer = setTimeout(function () {
+          callback.apply(context, args)
+          previous = new Date()
+        }, delay - remaining)
+      }
+    }
+  }
+}
+/**
+ * 防抖
+ */
+export const debounce = (callback, delay) => {
+  let timer = null
+
+  return function () {
+    let args = arguments
+    let context = this
+    if (timer) {
+      clearTimeout(timer)
+    }
+    timer = setTimeout(function () {
+      callback.apply(context, args)
+    }, delay)
+  }
+}
+/**
+ * js导出xlsx文件
+ * @param data {array} 表头描述;列标题,逗号隔开,每一个逗号(英文逗号)就是隔开一个单元格
+ * @param columns {array} 表格内容;数组包含对象,每个对象单元为一个表格单元格
+ * @param fileName {string} 表个名称
+ */
+export const jsExportXlsxFile = (columns = [], data = [], fileName = '数据表') => {
+  let str = columns.join(',')
+  str += `\n`
+  // 增加\t为了不让表格显示科学计数法或者其他格式、正则是为了去掉回车
+  for (let i = 0; i < data.length; i++) {
+    for (let item in data[i]) {
+      str += `${data[i][item].replace(/[\n\r]/g, '') + '\t'},`
+    }
+    str += '\n'
+  }
+  // encodeURIComponent解决中文乱码
+  let uri = 'data:text/csv;charset=utf-8,\ufeff' + encodeURIComponent(str)
+  // 通过创建a标签实现
+  let link = document.createElement('a')
+  link.href = uri
+  // 对下载的文件命名
+  link.download = `${fileName}.csv`
+  document.body.appendChild(link)
+  link.click()
+  document.body.removeChild(link)
+}
+/**
+ * 文件名称路径拼接
+ */
+export function fileToPath () {
+  let baseUrl = '/api/'
+  if (process.env.NODE_ENV === 'production') {
+    baseUrl = config.baseUrl.files
+  }
+  return baseUrl
+}
+// 资产树选择楼栋,单元,房屋
+export const assetTreeFilter = (data, type) => {
+  let result = []
+  let item = {}
+  if (type === 'build') {
+    data.forEach(build => {
+      item = Object.assign({}, build)
+      item.children = []
+      result.push(item)
+    })
+    return result
+  }
+  if (type === 'unit') {
+    let unitItem = {}
+    data.forEach(build => {
+      item = Object.assign({}, build)
+      item.children = []
+      build.children.forEach(unit => {
+        unitItem = Object.assign({}, unit)
+        unitItem.children = []
+        item.children.push(unitItem)
+      })
+      result.push(item)
+    })
+    return result
+  }
+  if (type === 'house') {
+    return data
+  }
+}
+
+/**
+ * 将dom转为图片dataUrl 和 blob
+ * @param {dom} dom 对象
+ * @param {string} type default: 获取dataUrl,blob; image: 获取dataUrl; download 自动下载并获取blob
+ * @param {Number} quality 图片质量 0~1
+ * @param {string} fileName 保存的图片名称
+ * @returns Promise
+ */
+export const dom2Image = (dom, type = 'default', quality = 0.9, fileName = '', imagetype = 'image/jpeg', options = {}) => {
+  return new Promise((resolve, reject) => {
+    html2canvas(dom, {
+      backgroundColor: null,
+      dpi: window.devicePixelRatio * 2,
+      scale: 10,
+      useCORS: true,
+      ...options
+    })
+      .then((canvas) => {
+        let dataUrl = canvas.toDataURL(imagetype, quality)
+        if (type === 'image') {
+          resolve(dataUrl)
+        } else {
+          canvas.toBlob((blob) => {
+            if (type === 'download') {
+              saveAs(blob, fileName)
+            }
+            resolve({
+              dataUrl,
+              blob
+            })
+          }, imagetype, quality)
+        }
+      })
+      .catch(err => {
+        reject(err)
+      })
+  })
+}
+
+/* 图片压缩方法-canvas压缩 */
+export const compressPhoto = (image, maxSize = 0, quality = 0.7) => {
+  let canvas = document.createElement('canvas')
+  let ctx = canvas.getContext('2d')
+  // let initSize = image.src.length
+  let { width, height } = image
+  let per = 0
+  if (width > height) {
+    per = maxSize / width
+  } else {
+    per = maxSize / height
+  }
+  canvas.width = width * per || width
+  canvas.height = height * per || height
+  ctx.fillStyle = '#fff'
+  ctx.fillRect(0, 0, canvas.width, canvas.height)
+  ctx.drawImage(image, 0, 0, canvas.width, canvas.height)
+
+  // 进行压缩0.7
+  let compressData = canvas.toDataURL('image/jpeg', quality)
+
+  // 压缩后调用方法进行base64转Blob
+  // let blobImg = dataURItoBlob(compressData)
+  return compressData
+}
+
+/* base64转Blob对象 */
+export const dataURItoBlob = (data) => {
+  let byteString
+  if (data.split(',')[0].indexOf('base64') >= 0) {
+    byteString = atob(data.split(',')[1])
+  } else {
+    byteString = unescape(data.split(',')[1])
+  }
+  let mimeString = data
+    .split(',')[0]
+    .split(':')[1]
+    .split(';')[0]
+  let ia = new Uint8Array(byteString.length)
+  for (let i = 0; i < byteString.length; i += 1) {
+    ia[i] = byteString.charCodeAt(i)
+  }
+  return new Blob([ia], { type: mimeString })
+}
+// 枚举转换
+export const enumForMat = (data) => {
+  let enumArr = []
+  if (data && data.length > 0) {
+    enumArr = data.map(item => {
+      return {
+        text: item.name,
+        value: item.code
+      }
+    })
+  }
+  return enumArr
+}
+
+export function toHump (str = '') {
+  return str.replace(/^\w|_\w/g, (match) => match.toUpperCase()).replace(/_/g, '')
+}
+
+// 表头统计模板数据转换
+export function statisitsChange (data, res) {
+  for (let item of data) {
+    item.value = res[item.key]
+  }
+  return data
+}
+
+// 金额转换大写
+export function priceToUpper (n) {
+  if (Number(n) === 0) {
+    return '零元整'
+  }
+  if (!/^(\+|-)?(0|[1-9]\d*)(\.\d+)?$/.test(n)) { return '数据非法' }
+  let unit = '仟佰拾亿仟佰拾万仟佰拾元角分'
+  let str = ''
+  n += '00'
+  let a = parseFloat(n)
+  if (a < 0) {
+    n = n.substr(1)
+  }
+  let p = n.indexOf('.')
+  if (p >= 0) {
+    n = n.substring(0, p) + n.substr(p + 1, 2)
+  }
+
+  unit = unit.substr(unit.length - n.length)
+
+  for (let i = 0; i < n.length; i++) { str += '零壹贰叁肆伍陆柒捌玖'.charAt(n.charAt(i)) + unit.charAt(i) }
+  if (a > 0) {
+    return str.replace(/零(仟|佰|拾|角)/g, '零').replace(/(零)+/g, '零').replace(/零(万|亿|元)/g, '$1').replace(/(亿)万/g, '$1').replace(/^元零?|零分/g, '').replace(/元$/g, '元整')
+  } else {
+    return '负' + str.replace(/零(仟|佰|拾|角)/g, '零').replace(/(零)+/g, '零').replace(/零(万|亿|元)/g, '$1').replace(/(亿)万|壹(拾)/g, '$1$2').replace(/^元零?|零分/g, '').replace(/元$/g, '元整')
+  }
+}

+ 48 - 0
src/App.vue

@@ -0,0 +1,48 @@
+<!--
+ * @Author: LiZhiWei
+ * @Date: 2025-04-09 09:06:21
+ * @LastEditors: LiZhiWei
+ * @LastEditTime: 2025-04-16 08:37:57
+ * @Description: 
+-->
+<template>
+  <div id="app">
+    <router-view/>
+  </div>
+</template>
+
+<script>
+export default {
+  name: "App",
+  data() {
+    return {
+      excelInstance: null,
+    }
+  },
+  methods: {
+   
+  },
+}
+</script>
+
+<style>
+#app {
+  font-family: "Avenir", Helvetica, Arial, sans-serif;
+  -webkit-font-smoothing: antialiased;
+  -moz-osx-font-smoothing: grayscale;
+  color: #2c3e50;
+  margin: 20px;
+}
+
+.header {
+  margin-bottom: 20px;
+}
+
+#excel-container {
+  width: 100%;
+  min-height: 500px;
+  border: 1px solid #ddd;
+  padding: 10px;
+  box-sizing: border-box;
+}
+</style>

+ 82 - 0
src/api/module/file.js

@@ -0,0 +1,82 @@
+/*
+ * @Author: WangQiBiao
+ * @Date: 2019-01-31 09:36:48
+ * @Last Modified by: WangQiBiao
+ * @Last Modified time: 2019-02-20 16:18:36
+ */
+import axios from '../../ libs/api.request'
+export default {
+  /**
+   * 图片查看
+   * @param id {string} 图片id
+   */
+  getFileImages (data) {
+    return axios.request({
+      url: `/file/images/${data.id}`,
+      params: data,
+      method: 'get'
+    })
+  },
+  /**
+   * 文件预览
+   * @param {String} fileId 文件id
+   * @param {Function} progressFunc 下载进度
+   */
+  previewCabinetFile (fileId, onDownloadProgress) {
+    return this.downloadFileByConfig(
+      `/serve/cabinetFile/preview/${fileId}`,
+      {
+        onDownloadProgress: onDownloadProgress,
+        timeout: 1000 * 60 * 10 // 10分钟
+      }
+    )
+  },
+  previewCabinetFileByUrl (url, config) {
+    return this.downloadFileByConfig(
+      url,
+      {
+        ...config,
+        responseType: 'blob'
+      }
+    )
+  },
+  /**
+   * 文件下载
+   * @param {String} fileId 文件id
+   * @param {Function} progressFunc 下载进度
+   */
+  downloadFile (fileId, onDownloadProgress) {
+    return this.downloadFileByConfig(
+      `/file/download/${fileId}`,
+      {
+        onDownloadProgress: onDownloadProgress
+      }
+    )
+  },
+  /**
+   * 文件下载
+   * @param {String} url 文件下载地址
+   * @param {Function} config 配置项
+   */
+  downloadFileByConfig (url, config, params = {}) {
+    let defaultConfig = {
+      url,
+      method: 'get',
+      params: params,
+      responseType: 'blob',
+      headers: {
+        Accept: 'application/force-download;application/octet-stream;'
+      }
+    }
+    return axios.request(Object.assign(defaultConfig, config))
+  },
+  postImageByManual (data) {
+    return axios.request({
+      url: '/file/images/upload',
+      data: data,
+      method: 'post',
+      timeout: 1000 * 60 * 10 // 10分钟
+    })
+  }
+
+}

+ 120 - 0
src/comm_js/index.js

@@ -0,0 +1,120 @@
+/*
+ * @Author: ChenYaJin
+ * @Date: 2021-06-08 16:11:26
+ * @LastEditors: ChenYaJin
+ * @LastEditTime: 2023-08-03 16:17:46
+ * @Description: 文件柜-公用方法
+ */
+
+const inventoryCodeName = new Map([
+  ['qualityInspection', '巡查'],
+  ['securityPatrol', '巡逻'],
+  ['deviceInspection', '巡检'],
+  ['deviceCuring', '保养'],
+  ['deviceMaintain', '维修'],
+  ['cleaningWork', '保洁'],
+  ['greeningWork', '绿化']
+])
+
+export { inventoryCodeName }
+
+export default {
+  // 根据子节点id,找出父节点列表
+  getParentList (data, id) {
+    let childList = []
+    data.forEach((item, index) => {
+      if (item.id === id) {
+        childList = [item.label]
+        return false
+      } else {
+        if (item.children) {
+          let newCidList = [item.label]
+          let list = this.nodeFun(item.children, id, newCidList)
+          if (list) {
+            childList = list
+          }
+        }
+      }
+    })
+    return childList
+  },
+  nodeFun (newVal, newId, newCidList) {
+    let flag = false
+    newVal.forEach(j => {
+      if (j.id === newId) {
+        newCidList.push(j.label)
+        flag = true
+      } else {
+        if (j.children) {
+          let childList = JSON.parse(JSON.stringify(newCidList))
+          childList.push(j.label)
+          let list = this.nodeFun(j.children, newId, childList)
+          if (list) {
+            newCidList = list
+            flag = true
+          }
+        }
+      }
+    })
+    if (flag) {
+      return newCidList
+    }
+  },
+  /**
+   * 渲染文字水印背景
+   * @{Element} targetElement 目标元素
+   * @{params} text 水印字符串
+   */
+  addWaterMarker (targetElement, text = '') {
+    var cpyName = text
+    if (text && text.length > 16) {
+      cpyName = text.substring(0, 16)
+    }
+    var can = document.createElement('canvas')
+    can.width = 180
+    can.height = 110
+    can.style.display = 'none'
+    can.style.zIndex = '999999'
+
+    var cans = can.getContext('2d')
+    cans.rotate(-25 * Math.PI / 180)
+    cans.font = '800 16px Microsoft JhengHei'
+    cans.fillStyle = '#dcdee2'
+    cans.textAlign = 'center'
+    cans.textBaseline = 'Middle'
+    if (cans.measureText(cpyName).width > 180) {
+      var size = 180 / cpyName.length
+      cans.font = '600 ' + size + 'px ' + ' Microsoft JhengHei'
+    }
+    cans.fillText(cpyName, 60, 60)
+    targetElement.style.backgroundImage = 'url(' + can.toDataURL('image/png') + ')'
+  },
+  /**
+   * 时间格式转换
+   * @param {string | Date} dateString 时间
+   * @returns {string} yyyy年mm月ddr日
+   */
+  dateFormat (dateString) {
+    const year = new Date(dateString).getFullYear()
+    const month = new Date(dateString).getMonth() + 1
+    const day = new Date(dateString).getDate()
+    return `${year}年${month}月${day}日`
+  },
+  /**
+   * 文件大小【KB】转换
+   * @param {number} size KB大小
+   * @param {string} defaultUnit size=0时,默认的单位
+   * @returns {string}
+   */
+  fileSizeFormat (size, defaultUnit = ' KB') {
+    const kbToGb = 1048576
+    const kbToMb = 1024
+    const isNegative = Number(size) < 0
+    const positiveSize = Math.abs(size)
+    const positiveFormat = Number(positiveSize) >= kbToGb ? Math.floor((Number(positiveSize) / kbToGb * 100)) / 100 + ' GB'
+      : Number(positiveSize) >= kbToMb ? Math.floor((Number(positiveSize) / kbToMb * 100)) / 100 + ' MB'
+        : positiveSize + ' KB'
+    const format = isNegative ? '-' + positiveFormat : positiveFormat
+    return positiveSize ? format : 0 + defaultUnit
+  }
+}

+ 48 - 0
src/components/file_detail/index.less

@@ -0,0 +1,48 @@
+
+.detail-box {
+  width: 350px;
+  height: 100%;
+  background: #f2f2f2;
+  border: 1px solid #ddd;
+  margin: 15px;
+  padding-bottom: 60px;
+  position: relative;
+  .header-box {
+    color: rgba(0, 0, 0, 0.85);
+    padding: 13px 24px;
+    font-size: 16px;
+    font-weight: 500;
+    text-align: center;
+    margin-bottom: 20px;
+    border-bottom: 1px solid #ddd;
+  }
+  .row-item {
+    display: flex;
+    line-height: 30px;
+    font-size: 14px;
+    .row-label {
+      width: 100px;
+      display: inline-block;
+      text-align: right;
+      margin-right: 20px;
+      font-weight: 500;
+    }
+    span {
+      &:nth-child(2) {
+        flex: 1;
+      }
+    }
+  }
+  .content-box {
+    height: ~'calc(100vh - 410px)';
+    // overflow: scroll;
+  }
+  .operation-footer {
+    text-align: center;
+    position: absolute;
+    bottom: 0;
+    padding: 20px 0;
+    width: 100%;
+  }
+}
+

+ 318 - 0
src/components/file_detail/index.vue

@@ -0,0 +1,318 @@
+<!--
+ * @Author: ChenYaJin
+ * @Date: 2021-06-07 17:46:34
+ * @LastEditors: LiZhiWei
+ * @LastEditTime: 2025-04-16 10:20:31
+ * @Description: 文件柜-文件详情
+-->
+<template>
+  <div>
+    <div class="detail-box">
+      <header class="header-box">
+        <span class="title">文件信息</span>
+      </header>
+      <div class="content-box" v-loading="loading || loadingFile">
+        <div class="info-item">
+          <i class="el-icon-document"></i>
+          <div class="info-content">
+            <div class="label">文件名</div>
+            <div class="value">{{fileDetail.name || '-'}}</div>
+          </div>
+        </div>
+        <div class="info-item">
+          <i class="el-icon-tickets"></i>
+          <div class="info-content">
+            <div class="label">文件类型</div>
+            <div class="value">{{fileDetail.type || '-'}}</div>
+          </div>
+        </div>
+        <div class="info-item">
+          <i class="el-icon-files"></i>
+          <div class="info-content">
+            <div class="label">文件大小</div>
+            <div class="value">{{getFileSize || '-'}}</div>
+          </div>
+        </div>
+      </div>
+      <div class="operation-footer">
+        <el-button 
+          type="success" 
+          @click="onDownload" 
+          :loading="downloadLoading" 
+          v-hasAuth="'FileDownload'"
+          icon="el-icon-download">
+          下载
+        </el-button>
+      </div>
+    </div>
+  </div>
+</template>
+
+<style lang="less" scoped>
+
+</style>
+<script>
+import { mapActions, mapState } from 'vuex'
+import { debounce } from 'lodash-es'
+
+import { downloadWithProgress } from '../../ libs/util'
+import commJs from '../../comm_js/index'
+import { downloadFile } from '../../ libs/file'
+
+// import EditFileModal from '../../components/file_list/modal/edit_file'
+// import PasswordConfirmModal from '../password_confirm'
+
+export default {
+  name: 'FileDetailModal',
+  components: {
+    // PasswordConfirmModal,
+    // EditFileModal
+  },
+  props: {
+    id: {
+      type: String,
+      default: null
+    },
+    showClose: {
+      type: Boolean,
+      default: false
+    },
+    file: {
+      type: Object,
+      default: () => {
+        return {}
+      }
+    },
+    loadingFile: {
+      type: Boolean,
+      default: true
+    }
+  },
+  data () {
+    return {
+      fileDetail: {},
+      fileInfo: {
+        isShow: false,
+        row: this.file
+      },
+      passwordConfirmInfo: {
+        isShow: false,
+        id: '',
+        okType: 'danger',
+        tips: '',
+        row: {}
+      },
+      loading: false,
+      downloadLoading: false,
+      deleteLoading: false
+    }
+  },
+  computed: {
+    ...mapState({
+      directoryTree: state => state.fileManage.directoryTree
+    }),
+    getSecretLevel () {
+      return this.file.secretLevel === 'secret' ? '机密' : '普通'
+    },
+    getFileSize () {
+      return commJs.fileSizeFormat(this.file.size, 'KB')
+    }
+  },
+  watch: {
+    'file': {
+      handler (value) {
+        if (value) {
+          this.fileDetail = JSON.parse(JSON.stringify(value))
+        }
+      },
+      immediate: true,
+      deep: true
+    }
+  },
+  methods: {
+    ...mapActions('fileManage', ['getFileDetail', 'deleteFile']),
+    updateSuccess () {
+      this.$emit('updateSuccess')
+      this.getFileDetailFun()
+    },
+    getFileDetailFun () {
+      if (!this.id) {
+        return
+      }
+      this.loading = true
+      this.getFileDetail(this.id)
+        .then(res => {
+          this.fileDetail = res
+          const stringArr = commJs.getParentList(this.directoryTree, res.cabinetDirectoryId)
+          this.fileDetail.directoryName = stringArr.join('/')
+        })
+        .finally(_ => {
+          this.loading = false
+        })
+    },
+    handleClose () {
+      this.$emit('close')
+    },
+    toEdit () {
+      this.fileInfo.isShow = true
+      this.fileInfo.row = JSON.parse(JSON.stringify(this.fileDetail))
+    },
+    onClose () {
+      this.fileInfo.isShow = false
+      this.passwordConfirmInfo.isShow = false
+    },
+    onDownload () {
+      if (this.fileDetail.secretLevel === 'secret') {
+        this.passwordConfirmInfo = {
+          isShow: true,
+          type: 'download',
+          okType: 'warning',
+          tips: '为防止他人恶意下载导致文件泄露,请输入登录密码确认后再批量下载。下载后,请勿随意传播,如因个人传播泄密导致公司利益受损,将追求个人的法律责任。',
+          row: this.fileDetail
+        }
+      } else {
+        this.downloadFileFun2(this.fileDetail.url)
+      }
+    },
+    downloadFileFun: debounce(function (fileId) {
+      this.downloadLoading = true
+      const { name, type, size } = this.fileDetail
+      const url = `serve/cabinetFile/download/${fileId}`
+      const fileInfo = { name: name + '.' + type, size: size }
+      downloadWithProgress(url, this, 'downloadLoading', fileInfo)
+    }, 500),
+    downloadFileFun2 :debounce(function (url) {
+      this.downloadLoading = true
+      const { name, type, size } = this.fileDetail
+      const fileInfo = { name: name + '.' + type, size: size }
+      downloadWithProgress(url, this, 'downloadLoading', fileInfo)
+    }, 500),
+    onDeleteFile () {
+      if (this.fileDetail.secretLevel === 'secret') {
+        this.passwordConfirmInfo = {
+          isShow: true,
+          type: 'delete',
+          okType: 'danger',
+          tips: '删除后不可查看或下载,为防止他人恶意操作,请输入账登录密码确认删除。',
+          row: this.fileDetail
+        }
+      } else {
+        this.$confirm(
+          '删除后不可查看或下载,请谨慎操作!',
+          '确认删除文件?',
+          {
+            confirmButtonText: '确定',
+            cancelButtonText: '取消',
+            type: 'error'
+          })
+          .then(_ => {
+            this.deleteFileFun()
+          })
+      }
+    },
+    deleteFileFun (id) {
+      this.deleteLoading = true
+      this.deleteFile(this.fileDetail.id)
+        .then(res => {
+          this.$message.success('删除文件成功')
+          this.passwordConfirmInfo.isShow = false
+          this.back()
+        })
+        .finally(_ => {
+          this.deleteLoading = false
+        })
+    },
+    back () {
+      this.$emit('back', true) // 返回需要刷新数据
+    }
+  }
+}
+</script>
+<style lang="less">
+@import './index.less';
+.detail-box {
+  background: #fff;
+  border-radius: 8px;
+  box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.05);
+  
+  .header-box {
+    padding: 20px;
+    border-bottom: 1px solid #ebeef5;
+    
+    .title {
+      font-size: 16px;
+      font-weight: 500;
+      color: #303133;
+      position: relative;
+      padding-left: 12px;
+      
+    //   &::before {
+    //     content: '';
+    //     position: absolute;
+    //     left: 0;
+    //     top: 50%;
+    //     transform: translateY(-50%);
+    //     width: 4px;
+    //     height: 16px;
+    //     background: #409eff;
+    //     border-radius: 2px;
+    //   }
+    }
+  }
+  
+  .content-box {
+    padding: 20px;
+    min-height: 200px;
+    
+    .info-item {
+      display: flex;
+      align-items: flex-start;
+      margin-bottom: 24px;
+      
+      &:last-child {
+        margin-bottom: 0;
+      }
+      
+      i {
+        font-size: 20px;
+        color: #409eff;
+        margin-right: 12px;
+        margin-top: 2px;
+      }
+      
+      .info-content {
+        flex: 1;
+        
+        .label {
+          font-size: 14px;
+          color: #909399;
+          margin-bottom: 8px;
+        }
+        
+        .value {
+          font-size: 14px;
+          color: #303133;
+          word-break: break-all;
+          line-height: 1.4;
+        }
+      }
+    }
+  }
+  
+  .operation-footer {
+    border-top: 1px solid #ebeef5;
+     .el-button {
+      padding: 9px 20px;
+      
+      [class^="el-icon-"] {
+        margin-right: 4px;
+      }
+    }
+  }
+}
+
+.el-loading-mask {
+  background-color: rgba(255, 255, 255, 0.9);
+  border-radius: 8px;
+}
+</style>

+ 35 - 0
src/components/view_file/assets/excel_icon.svg

@@ -0,0 +1,35 @@
+<svg 
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ width="56px" height="56px">
+<defs>
+<filter filterUnits="userSpaceOnUse" id="Filter_0" x="0px" y="8px" width="35px" height="35px"  >
+                <feOffset in="SourceAlpha" dx="0.5" dy="0.866" />
+                <feGaussianBlur result="blurOut" stdDeviation="2" />
+                <feFlood flood-color="rgb(15, 140, 18)" result="floodOut" />
+                <feComposite operator="atop" in="floodOut" in2="blurOut" />
+                <feComponentTransfer><feFuncA type="linear" slope="0.75"/></feComponentTransfer>
+                <feMerge>
+    <feMergeNode/>
+    <feMergeNode in="SourceGraphic"/>
+  </feMerge>
+            </filter>
+
+</defs>
+<path fill-rule="evenodd"  fill="rgb(52, 201, 55)"
+ d="M52.000,56.000 L12.000,56.000 C9.791,56.000 8.000,54.209 8.000,52.000 L8.000,4.000 C8.000,1.791 9.791,-0.000 12.000,-0.000 L41.000,-0.000 L56.000,14.000 L56.000,52.000 C56.000,54.209 54.209,56.000 52.000,56.000 Z"/>
+<path fill-rule="evenodd"  fill="rgb(40, 182, 42)"
+ d="M8.000,14.000 L56.000,14.000 L56.000,28.000 L8.000,28.000 L8.000,14.000 Z"/>
+<path fill-rule="evenodd"  fill="rgb(22, 159, 25)"
+ d="M8.000,28.000 L56.000,28.000 L56.000,42.000 L8.000,42.000 L8.000,28.000 Z"/>
+<path fill-rule="evenodd"  fill="rgb(15, 132, 16)"
+ d="M8.000,42.000 L56.000,42.000 L56.000,52.000 C56.000,54.209 54.209,56.000 52.000,56.000 L12.000,56.000 C9.791,56.000 8.000,54.209 8.000,52.000 L8.000,42.000 Z"/>
+<path fill-rule="evenodd"  fill="rgb(102, 241, 104)"
+ d="M41.000,-0.000 L41.000,10.000 C41.000,12.209 42.791,14.000 45.000,14.000 L56.000,14.000 L41.000,-0.000 Z"/>
+<g filter="url(#Filter_0)">
+<path fill-rule="evenodd"  fill="rgb(15, 132, 16)"
+ d="M6.000,10.000 L26.000,10.000 C28.209,10.000 30.000,11.791 30.000,14.000 L30.000,34.000 C30.000,36.209 28.209,38.000 26.000,38.000 L6.000,38.000 C3.791,38.000 2.000,36.209 2.000,34.000 L2.000,14.000 C2.000,11.791 3.791,10.000 6.000,10.000 Z"/>
+</g>
+<path fill-rule="evenodd"  fill="rgb(255, 255, 255)"
+ d="M8.571,31.900 L10.528,31.900 L12.474,31.900 L15.583,26.463 L18.681,31.900 L20.657,31.900 L22.622,31.900 L17.466,23.661 L22.278,16.109 L20.374,16.109 L18.470,16.109 L15.594,21.202 L12.819,16.109 L10.861,16.109 L8.915,16.109 L13.685,23.908 L8.571,31.900 L8.571,31.900 L8.571,31.900 L8.571,31.900 L8.571,31.900 L8.571,31.900 L8.571,31.900 L8.571,31.900 L8.571,31.900 L8.571,31.900 L8.571,31.900 L8.571,31.900 L8.571,31.900 L8.571,31.900 L8.571,31.900 Z"/>
+</svg>

+ 34 - 0
src/components/view_file/assets/exe_icon.svg

@@ -0,0 +1,34 @@
+<svg 
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ width="56px" height="56px">
+<defs>
+<filter filterUnits="userSpaceOnUse" id="Filter_0" x="0px" y="30px" width="47px" height="23px"  >
+                <feOffset in="SourceAlpha" dx="0.5" dy="0.866" />
+                <feGaussianBlur result="blurOut" stdDeviation="2" />
+                <feFlood flood-color="rgb(17, 148, 160)" result="floodOut" />
+                <feComposite operator="atop" in="floodOut" in2="blurOut" />
+                <feComponentTransfer><feFuncA type="linear" slope="0.75"/></feComponentTransfer>
+                <feMerge>
+    <feMergeNode/>
+    <feMergeNode in="SourceGraphic"/>
+  </feMerge>
+            </filter>
+
+</defs>
+<path fill-rule="evenodd"  fill="rgb(0, 210, 230)"
+ d="M52.000,56.000 L12.000,56.000 C9.791,56.000 8.000,54.209 8.000,52.000 L8.000,4.000 C8.000,1.791 9.791,-0.000 12.000,-0.000 L41.000,-0.000 L56.000,14.000 L56.000,3.999 C56.000,3.999 56.000,4.000 56.000,4.000 L56.000,52.000 C56.000,54.209 54.209,56.000 52.000,56.000 Z"/>
+<path fill-rule="evenodd"  fill="rgb(141, 245, 255)"
+ d="M41.000,-0.000 L41.000,10.000 C41.000,12.209 42.791,14.000 45.000,14.000 L56.000,14.000 L41.000,-0.000 Z"/>
+<path fill-rule="evenodd"  fill="rgb(140, 245, 255)"
+ d="M31.928,15.193 C31.857,15.568 31.476,15.944 31.088,16.027 L30.813,16.087 C30.122,16.282 29.501,16.721 29.113,17.361 C28.725,17.997 28.637,18.721 28.803,19.384 L28.887,19.652 C29.004,20.018 28.852,20.517 28.545,20.761 C28.545,20.761 28.274,20.983 27.498,21.408 C26.722,21.830 26.383,21.947 26.383,21.947 C26.006,22.078 25.473,21.954 25.202,21.676 L25.008,21.479 C24.486,21.006 23.777,20.714 22.997,20.714 C22.218,20.714 21.512,21.003 20.987,21.475 L20.793,21.673 C20.518,21.951 19.989,22.075 19.611,21.944 C19.611,21.944 19.276,21.830 18.500,21.405 C17.724,20.979 17.449,20.758 17.449,20.758 C17.142,20.510 16.991,20.011 17.107,19.649 L17.188,19.391 C17.357,18.724 17.266,17.997 16.878,17.357 C16.490,16.714 15.869,16.278 15.178,16.084 L14.903,16.024 C14.511,15.940 14.134,15.565 14.064,15.190 C14.064,15.190 14.000,14.854 14.000,14.004 C14.000,13.152 14.064,12.817 14.064,12.817 C14.134,12.442 14.515,12.067 14.903,11.983 L15.185,11.923 C15.876,11.729 16.493,11.293 16.881,10.653 C17.269,10.013 17.358,9.289 17.192,8.623 L17.107,8.358 C16.991,7.993 17.143,7.497 17.449,7.252 C17.449,7.252 17.721,7.031 18.497,6.602 C19.273,6.180 19.611,6.063 19.611,6.063 C19.989,5.932 20.521,6.056 20.793,6.334 L20.990,6.535 C21.512,7.007 22.221,7.296 22.997,7.296 C23.773,7.296 24.475,7.011 24.997,6.542 L25.202,6.334 C25.477,6.056 26.006,5.932 26.383,6.063 C26.383,6.063 26.718,6.180 27.494,6.602 C28.270,7.028 28.545,7.249 28.545,7.249 C28.852,7.497 29.003,7.996 28.887,8.358 L28.795,8.619 C28.626,9.286 28.718,10.013 29.106,10.653 C29.494,11.290 30.104,11.722 30.792,11.919 L31.084,11.983 C31.476,12.067 31.853,12.445 31.924,12.817 C31.924,12.817 31.987,13.152 31.991,14.007 C31.991,14.858 31.928,15.193 31.928,15.193 ZM22.891,10.375 C20.949,10.375 19.375,11.942 19.375,13.875 C19.375,15.808 20.949,17.375 22.891,17.375 C24.832,17.375 26.406,15.808 26.406,13.875 C26.406,11.942 24.832,10.375 22.891,10.375 Z"/>
+<path fill-rule="evenodd"  fill="rgb(140, 245, 255)"
+ d="M49.915,32.642 C49.821,33.158 49.313,33.674 48.796,33.789 L48.429,33.872 C47.507,34.139 46.679,34.743 46.162,35.622 C45.644,36.498 45.527,37.492 45.748,38.404 L45.861,38.773 C46.016,39.275 45.814,39.961 45.404,40.297 C45.404,40.297 45.042,40.602 44.008,41.186 C42.973,41.767 42.521,41.928 42.521,41.928 C42.018,42.108 41.308,41.937 40.946,41.555 L40.687,41.283 C39.991,40.633 39.046,40.233 38.006,40.233 C36.967,40.233 36.026,40.629 35.325,41.278 L35.067,41.550 C34.700,41.932 33.994,42.103 33.491,41.923 C33.491,41.923 33.044,41.767 32.009,41.182 C30.975,40.597 30.608,40.293 30.608,40.293 C30.199,39.952 29.996,39.266 30.152,38.768 L30.260,38.413 C30.485,37.497 30.363,36.498 29.846,35.618 C29.329,34.733 28.501,34.135 27.579,33.868 L27.212,33.785 C26.690,33.670 26.187,33.154 26.093,32.638 C26.093,32.638 26.008,32.177 26.008,31.007 C26.008,29.837 26.093,29.377 26.093,29.377 C26.187,28.861 26.695,28.345 27.212,28.230 L27.588,28.147 C28.510,27.880 29.333,27.281 29.851,26.401 C30.368,25.522 30.486,24.527 30.264,23.610 L30.152,23.246 C29.997,22.744 30.199,22.063 30.608,21.727 C30.608,21.727 30.970,21.423 32.005,20.833 C33.040,20.253 33.491,20.091 33.491,20.091 C33.994,19.912 34.704,20.082 35.067,20.465 L35.330,20.741 C36.026,21.390 36.971,21.786 38.006,21.786 C39.041,21.786 39.977,21.395 40.673,20.750 L40.946,20.465 C41.313,20.082 42.018,19.912 42.521,20.091 C42.521,20.091 42.968,20.253 44.003,20.833 C45.038,21.418 45.404,21.722 45.404,21.722 C45.813,22.063 46.016,22.749 45.861,23.246 L45.738,23.606 C45.512,24.522 45.635,25.522 46.152,26.401 C46.669,27.277 47.483,27.871 48.400,28.142 L48.791,28.230 C49.313,28.345 49.816,28.866 49.910,29.377 C49.910,29.377 49.995,29.837 50.000,31.012 C50.000,32.182 49.915,32.642 49.915,32.642 ZM37.719,26.562 C34.940,26.562 32.688,28.752 32.688,31.453 C32.688,34.154 34.940,36.344 37.719,36.344 C40.497,36.344 42.750,34.154 42.750,31.453 C42.750,28.752 40.497,26.562 37.719,26.562 Z"/>
+<path fill-rule="evenodd"  fill="rgb(0, 165, 189)"
+ d="M8.000,38.000 L56.000,38.000 L56.000,52.000 C56.000,54.209 54.209,56.000 52.000,56.000 L12.000,56.000 C9.791,56.000 8.000,54.209 8.000,52.000 L8.000,38.000 Z"/>
+<g filter="url(#Filter_0)">
+<path fill-rule="evenodd"  fill="rgb(0, 175, 201)"
+ d="M5.000,32.000 L39.000,32.000 C40.657,32.000 42.000,33.343 42.000,35.000 L42.000,45.000 C42.000,46.657 40.657,48.000 39.000,48.000 L5.000,48.000 C3.343,48.000 2.000,46.657 2.000,45.000 L2.000,35.000 C2.000,33.343 3.343,32.000 5.000,32.000 Z"/>
+</g>
+<text kerning="auto" font-family="Adobe Heiti Std" fill="rgb(0, 0, 0)" font-size="12px" x="9px" y="45.0619999999999px"><tspan font-size="12px" font-family="Hiragino Sans GB" fill="#FFFFFF">EXE</tspan></text>
+</svg>

+ 54 - 0
src/components/view_file/assets/folder_icon.svg

@@ -0,0 +1,54 @@
+<svg 
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ width="52px" height="54px">
+<defs>
+<filter filterUnits="userSpaceOnUse" id="Filter_0" x="7px" y="3px" width="45px" height="45px"  >
+                <feOffset in="SourceAlpha" dx="1" dy="1.732" />
+                <feGaussianBlur result="blurOut" stdDeviation="1.732" />
+                <feFlood flood-color="rgb(255, 189, 32)" result="floodOut" />
+                <feComposite operator="atop" in="floodOut" in2="blurOut" />
+                <feComponentTransfer><feFuncA type="linear" slope="0.75"/></feComponentTransfer>
+                <feMerge>
+    <feMergeNode/>
+    <feMergeNode in="SourceGraphic"/>
+  </feMerge>
+            </filter>
+<filter filterUnits="userSpaceOnUse" id="Filter_1" x="5px" y="12px" width="45px" height="37px"  >
+                <feOffset in="SourceAlpha" dx="1" dy="1.732" />
+                <feGaussianBlur result="blurOut" stdDeviation="1.732" />
+                <feFlood flood-color="rgb(255, 189, 32)" result="floodOut" />
+                <feComposite operator="atop" in="floodOut" in2="blurOut" />
+                <feComponentTransfer><feFuncA type="linear" slope="0.75"/></feComponentTransfer>
+                <feMerge>
+    <feMergeNode/>
+    <feMergeNode in="SourceGraphic"/>
+  </feMerge>
+            </filter>
+<linearGradient id="PSgrad_0" x1="0%" x2="0%" y1="100%" y2="0%">
+  <stop offset="38%" stop-color="rgb(39,145,255)" stop-opacity="1" />
+  <stop offset="100%" stop-color="rgb(82,167,255)" stop-opacity="1" />
+</linearGradient>
+<linearGradient id="PSgrad_1" x1="0%" x2="0%" y1="100%" y2="0%">
+  <stop offset="0%" stop-color="rgb(255,189,32)" stop-opacity="1" />
+  <stop offset="100%" stop-color="rgb(254,230,149)" stop-opacity="1" />
+</linearGradient>
+
+</defs>
+<path fill-rule="evenodd"  fill="rgb(255, 189, 32)"
+ d="M5.000,-0.000 L41.000,-0.000 C46.523,-0.000 51.000,4.477 51.000,10.000 L51.000,50.000 C51.000,52.209 49.209,54.000 47.000,54.000 L17.000,54.000 C8.163,54.000 1.000,46.836 1.000,38.000 L1.000,4.000 C1.000,1.791 2.791,-0.000 5.000,-0.000 Z"/>
+<g filter="url(#Filter_0)">
+<path fill-rule="evenodd"  fill="rgb(36, 240, 255)"
+ d="M18.427,3.922 L39.677,9.616 C45.012,11.045 48.178,16.529 46.748,21.863 L42.089,39.250 C41.518,41.384 39.324,42.650 37.190,42.078 L15.940,36.384 C10.605,34.955 7.440,29.471 8.869,24.137 L13.528,6.750 C14.099,4.616 16.293,3.350 18.427,3.922 Z"/>
+</g>
+<g filter="url(#Filter_1)">
+<path fill-rule="evenodd"  fill="url(#PSgrad_0)"
+ d="M10.000,12.000 L36.000,12.000 C41.523,12.000 46.000,16.477 46.000,22.000 L46.000,40.000 C46.000,42.209 44.209,44.000 42.000,44.000 L16.000,44.000 C10.477,44.000 6.000,39.523 6.000,34.000 L6.000,16.000 C6.000,13.791 7.791,12.000 10.000,12.000 Z"/>
+</g>
+<path fill-rule="evenodd"  fill="rgb(255, 255, 255)"
+ d="M38.500,29.000 L18.500,29.000 C17.672,29.000 17.000,28.328 17.000,27.500 C17.000,26.671 17.672,26.000 18.500,26.000 L38.500,26.000 C39.328,26.000 40.000,26.671 40.000,27.500 C40.000,28.328 39.328,29.000 38.500,29.000 ZM28.500,23.000 L18.500,23.000 C17.672,23.000 17.000,22.328 17.000,21.500 C17.000,20.671 17.672,20.000 18.500,20.000 L28.500,20.000 C29.328,20.000 30.000,20.671 30.000,21.500 C30.000,22.328 29.328,23.000 28.500,23.000 ZM12.500,29.000 C11.672,29.000 11.000,28.328 11.000,27.500 C11.000,26.671 11.672,26.000 12.500,26.000 C13.328,26.000 14.000,26.671 14.000,27.500 C14.000,28.328 13.328,29.000 12.500,29.000 ZM12.500,23.000 C11.672,23.000 11.000,22.328 11.000,21.500 C11.000,20.671 11.672,20.000 12.500,20.000 C13.328,20.000 14.000,20.671 14.000,21.500 C14.000,22.328 13.328,23.000 12.500,23.000 Z"/>
+<path fill-rule="evenodd"  opacity="0.522" fill="rgb(66, 124, 190)"
+ d="M4.174,43.000 C3.423,41.146 3.000,39.123 3.000,37.000 L3.000,33.000 L39.000,33.000 C44.523,33.000 49.000,37.477 49.000,43.000 L49.000,43.000 L4.174,43.000 Z"/>
+<path fill-rule="evenodd"  fill="url(#PSgrad_1)"
+ d="M1.000,34.000 L41.000,34.000 C46.523,34.000 51.000,38.477 51.000,44.000 L51.000,50.000 C51.000,52.209 49.209,54.000 47.000,54.000 L17.000,54.000 C8.163,54.000 1.000,46.836 1.000,38.000 L1.000,34.000 Z"/>
+</svg>

+ 13 - 0
src/components/view_file/assets/image_icon.svg

@@ -0,0 +1,13 @@
+<svg 
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ width="48px" height="50px">
+<path fill-rule="evenodd"  fill="rgb(0, 132, 228)"
+ d="M7.990,-0.010 L39.990,-0.010 C44.408,-0.010 47.990,3.572 47.990,7.990 L47.990,39.990 C47.990,45.513 43.513,49.990 37.990,49.990 L9.990,49.990 C4.467,49.990 -0.010,45.513 -0.010,39.990 L-0.010,7.990 C-0.010,3.572 3.572,-0.010 7.990,-0.010 Z"/>
+<path fill-rule="evenodd"  fill="rgb(253, 204, 15)"
+ d="M30.994,8.990 C34.857,8.990 37.988,12.124 37.988,15.990 C37.988,19.856 34.857,22.990 30.994,22.990 C27.131,22.990 24.000,19.856 24.000,15.990 C24.000,12.124 27.131,8.990 30.994,8.990 Z"/>
+<path fill-rule="evenodd"  fill="rgb(235, 120, 38)"
+ d="M47.990,27.493 L47.990,41.990 C47.990,46.408 44.408,49.990 39.990,49.990 L9.635,49.990 C16.426,44.704 21.485,39.558 21.485,39.558 C36.963,26.952 42.758,27.029 42.758,27.029 C44.861,27.029 46.584,27.222 47.990,27.493 Z"/>
+<path fill-rule="evenodd"  fill="rgb(255, 160, 76)"
+ d="M39.990,49.990 L7.990,49.990 C3.572,49.990 -0.010,46.408 -0.010,41.990 L-0.010,23.374 C4.394,22.284 8.236,23.719 11.000,25.029 C15.711,27.263 22.434,34.813 28.000,37.020 C37.289,40.701 47.882,38.048 47.990,38.021 L47.990,41.990 C47.990,46.408 44.408,49.990 39.990,49.990 Z"/>
+</svg>

+ 29 - 0
src/components/view_file/assets/other_icon.svg

@@ -0,0 +1,29 @@
+<svg 
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ width="46px" height="56px">
+<defs>
+<linearGradient id="PSgrad_0" x1="57.358%" x2="0%" y1="0%" y2="81.915%">
+  <stop offset="0%" stop-color="rgb(255,255,255)" stop-opacity="1" />
+  <stop offset="100%" stop-color="rgb(237,145,0)" stop-opacity="1" />
+</linearGradient>
+<filter id="Filter_0">
+                <feFlood flood-color="rgb(255, 255, 255)" flood-opacity="1" result="floodOut" />
+                <feComposite operator="atop" in="floodOut" in2="SourceGraphic" result="compOut" />
+                <feBlend mode="normal" in="compOut" in2="SourceGraphic" />
+            </filter>
+
+</defs>
+<path fill-rule="evenodd"  fill="rgb(255, 225, 179)"
+ d="M10.000,0.000 L42.000,0.000 C44.209,0.000 46.000,1.791 46.000,4.000 L46.000,46.000 C46.000,48.209 44.209,50.000 42.000,50.000 L10.000,50.000 C7.791,50.000 6.000,48.209 6.000,46.000 L6.000,4.000 C6.000,1.791 7.791,0.000 10.000,0.000 Z"/>
+<path fill-rule="evenodd"  fill="rgb(255, 180, 50)"
+ d="M38.000,56.000 L4.000,56.000 C1.791,56.000 -0.000,54.209 -0.000,52.000 L-0.000,9.000 C-0.000,6.791 1.791,5.000 4.000,5.000 L25.000,5.000 L42.000,19.000 L42.000,8.999 C42.000,8.999 42.000,9.000 42.000,9.000 L42.000,52.000 C42.000,54.209 40.209,56.000 38.000,56.000 Z"/>
+<path fill-rule="evenodd"  opacity="0.831" fill="rgb(237, 153, 33)"
+ d="M27.000,9.000 L27.000,19.000 C27.000,21.209 28.791,23.000 31.000,23.000 L42.000,23.000 L42.000,19.000 L27.000,9.000 Z"/>
+<path fill-rule="evenodd"  fill="url(#PSgrad_0)"
+ d="M25.000,5.000 L25.000,15.000 C25.000,17.209 26.791,19.000 29.000,19.000 L42.000,19.000 L25.000,5.000 Z"/>
+<g filter="url(#Filter_0)">
+<path fill-rule="evenodd"  fill="rgb(215, 154, 47)"
+ d="M24.000,32.513 C23.275,33.154 23.000,34.561 23.000,36.000 C23.000,36.762 23.000,36.477 23.000,37.000 L19.000,37.000 C19.031,36.706 19.000,35.000 19.000,35.000 C19.000,33.587 19.284,32.546 19.979,31.630 C20.499,30.950 20.698,30.766 22.310,29.641 C23.897,28.542 24.492,27.705 24.492,26.528 C24.492,24.958 22.959,24.000 21.000,24.000 C18.005,24.000 17.477,26.519 17.306,27.105 C17.056,27.971 16.566,28.850 15.653,28.850 C14.740,28.850 14.000,28.069 14.000,27.105 C14.000,24.216 16.289,21.000 21.439,21.000 C25.509,21.000 28.013,23.720 28.013,26.501 C28.013,29.587 25.449,31.251 24.000,32.513 ZM23.000,43.000 L19.000,43.000 L19.000,39.000 L23.000,39.000 L23.000,43.000 Z"/>
+</g>
+</svg>

+ 46 - 0
src/components/view_file/assets/pdf_icon.svg

@@ -0,0 +1,46 @@
+<svg 
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ width="56px" height="56px">
+<defs>
+<filter filterUnits="userSpaceOnUse" id="Filter_0" x="19px" y="4px" width="31px" height="31px"  >
+                <feOffset in="SourceAlpha" dx="0.5" dy="0.866" />
+                <feGaussianBlur result="blurOut" stdDeviation="1.414" />
+                <feFlood flood-color="rgb(195, 0, 0)" result="floodOut" />
+                <feComposite operator="atop" in="floodOut" in2="blurOut" />
+                <feComponentTransfer><feFuncA type="linear" slope="0.75"/></feComponentTransfer>
+                <feMerge>
+    <feMergeNode/>
+    <feMergeNode in="SourceGraphic"/>
+  </feMerge>
+            </filter>
+<filter filterUnits="userSpaceOnUse" id="Filter_1" x="0px" y="33px" width="47px" height="23px"  >
+                <feOffset in="SourceAlpha" dx="0.5" dy="0.866" />
+                <feGaussianBlur result="blurOut" stdDeviation="2" />
+                <feFlood flood-color="rgb(160, 17, 17)" result="floodOut" />
+                <feComposite operator="atop" in="floodOut" in2="blurOut" />
+                <feComponentTransfer><feFuncA type="linear" slope="0.75"/></feComponentTransfer>
+                <feMerge>
+    <feMergeNode/>
+    <feMergeNode in="SourceGraphic"/>
+  </feMerge>
+            </filter>
+
+</defs>
+<path fill-rule="evenodd"  fill="rgb(244, 64, 64)"
+ d="M52.000,56.000 L12.000,56.000 C9.791,56.000 8.000,54.209 8.000,52.000 L8.000,4.000 C8.000,1.791 9.791,-0.000 12.000,-0.000 L41.000,-0.000 L56.000,14.000 L56.000,52.000 C56.000,54.209 54.209,56.000 52.000,56.000 Z"/>
+<path fill-rule="evenodd"  fill="rgb(255, 125, 125)"
+ d="M41.000,-0.000 L41.000,10.000 C41.000,12.209 42.791,14.000 45.000,14.000 L56.000,14.000 L41.000,-0.000 Z"/>
+<path fill-rule="evenodd"  fill="rgb(160, 7, 7)"
+ d="M8.000,42.000 L56.000,42.000 L56.000,52.000 C56.000,54.209 54.209,56.000 52.000,56.000 L12.000,56.000 C9.791,56.000 8.000,54.209 8.000,52.000 L8.000,42.000 Z"/>
+<g filter="url(#Filter_0)">
+<path fill-rule="evenodd"  fill="rgb(255, 255, 255)"
+ d="M46.513,22.024 C45.672,21.119 43.991,20.607 41.588,20.607 C40.297,20.607 38.825,20.728 37.203,21.029 C36.303,20.125 35.372,19.070 34.501,17.834 C33.870,16.960 33.329,16.056 32.849,15.152 C33.810,12.168 34.260,9.757 34.260,8.008 C34.260,6.049 33.570,4.000 31.527,4.000 C30.897,4.000 30.296,4.392 29.966,4.934 C29.065,6.592 29.455,10.209 31.047,13.765 C30.446,15.544 29.846,17.261 29.065,19.160 C28.404,20.758 27.623,22.446 26.812,23.953 C22.248,25.821 19.304,27.991 19.034,29.679 C18.914,30.312 19.124,30.915 19.575,31.367 C19.725,31.488 20.325,32.000 21.317,32.000 C24.350,32.000 27.533,26.997 29.155,23.953 C30.386,23.531 31.648,23.139 32.879,22.777 C34.230,22.385 35.612,22.084 36.843,21.873 C40.026,24.796 42.849,25.279 44.261,25.279 C46.003,25.279 46.633,24.555 46.844,23.953 C47.174,23.199 46.934,22.355 46.543,21.903 L46.513,22.024 ZM44.892,23.259 C44.772,23.892 44.141,24.314 43.270,24.314 C43.030,24.314 42.819,24.284 42.579,24.224 C40.988,23.832 39.516,23.018 38.014,21.752 C39.486,21.511 40.717,21.451 41.498,21.451 C42.369,21.451 43.120,21.481 43.600,21.632 C44.171,21.752 45.072,22.144 44.892,23.259 L44.892,23.259 ZM36.092,21.270 C35.011,21.481 33.870,21.752 32.669,22.084 C31.708,22.355 30.717,22.626 29.725,22.988 C30.266,21.933 30.717,20.908 31.137,19.944 C31.648,18.738 32.038,17.533 32.458,16.387 C32.879,17.111 33.329,17.834 33.780,18.467 C34.531,19.462 35.311,20.426 36.092,21.270 L36.092,21.270 ZM30.656,5.447 C30.837,5.115 31.167,4.934 31.437,4.934 C32.308,4.934 32.458,5.959 32.458,6.773 C32.458,8.129 32.038,10.209 31.347,12.590 C30.146,9.274 30.056,6.532 30.656,5.447 L30.656,5.447 ZM26.122,25.249 C24.019,28.805 21.977,31.035 20.746,31.035 C20.506,31.035 20.296,30.945 20.115,30.824 C19.875,30.583 19.755,30.282 19.815,29.920 C20.055,28.654 22.428,26.876 26.122,25.249 L26.122,25.249 Z"/>
+</g>
+<g filter="url(#Filter_1)">
+<path fill-rule="evenodd"  fill="rgb(194, 27, 27)"
+ d="M5.000,35.000 L39.000,35.000 C40.657,35.000 42.000,36.343 42.000,38.000 L42.000,48.000 C42.000,49.657 40.657,51.000 39.000,51.000 L5.000,51.000 C3.343,51.000 2.000,49.657 2.000,48.000 L2.000,38.000 C2.000,36.343 3.343,35.000 5.000,35.000 Z"/>
+</g>
+<path fill-rule="evenodd"  fill="rgb(255, 255, 255)"
+ d="M30.873,39.636 L30.873,41.919 L35.276,41.919 L35.276,42.780 L35.276,43.648 L30.873,43.648 L30.873,47.900 L29.827,47.900 L28.775,47.900 L28.775,37.852 L35.925,37.852 L35.925,38.740 L35.925,39.636 L30.873,39.636 ZM22.793,47.900 L19.710,47.900 L18.664,47.900 L18.664,37.852 L19.710,37.852 L22.964,37.852 C24.345,37.852 25.394,38.257 26.112,39.068 C26.830,39.880 27.189,41.067 27.189,42.630 C27.189,44.352 26.816,45.662 26.071,46.557 C25.326,47.453 24.233,47.900 22.793,47.900 ZM24.495,40.364 C24.099,39.842 23.502,39.581 22.704,39.581 L20.763,39.581 L20.763,46.171 L22.533,46.171 C23.385,46.171 24.025,45.883 24.451,45.306 C24.877,44.730 25.090,43.865 25.090,42.712 C25.090,41.668 24.892,40.886 24.495,40.364 ZM13.530,44.311 C12.719,44.307 12.017,44.311 11.425,44.325 L11.425,47.900 L10.379,47.900 L9.326,47.900 L9.326,37.852 L10.372,37.852 L13.530,37.852 C14.674,37.852 15.545,38.119 16.142,38.655 C16.739,39.190 17.037,39.971 17.037,40.996 C17.037,42.081 16.739,42.904 16.142,43.467 C15.545,44.030 14.674,44.311 13.530,44.311 ZM14.498,39.940 C14.204,39.701 13.763,39.581 13.175,39.581 L11.425,39.581 L11.425,42.582 L13.175,42.582 C13.763,42.582 14.204,42.452 14.498,42.192 C14.792,41.933 14.939,41.541 14.939,41.017 C14.939,40.538 14.792,40.179 14.498,39.940 Z"/>
+</svg>

+ 35 - 0
src/components/view_file/assets/ppt_icon.svg

@@ -0,0 +1,35 @@
+<svg 
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ width="56px" height="56px">
+<defs>
+<filter filterUnits="userSpaceOnUse" id="Filter_0" x="0px" y="22px" width="49px" height="29px"  >
+                <feOffset in="SourceAlpha" dx="0.5" dy="0.866" />
+                <feGaussianBlur result="blurOut" stdDeviation="2" />
+                <feFlood flood-color="rgb(160, 68, 17)" result="floodOut" />
+                <feComposite operator="atop" in="floodOut" in2="blurOut" />
+                <feComponentTransfer><feFuncA type="linear" slope="0.75"/></feComponentTransfer>
+                <feMerge>
+    <feMergeNode/>
+    <feMergeNode in="SourceGraphic"/>
+  </feMerge>
+            </filter>
+
+</defs>
+<path fill-rule="evenodd"  fill="rgb(245, 118, 64)"
+ d="M52.000,56.000 L12.000,56.000 C9.791,56.000 8.000,54.209 8.000,52.000 L8.000,4.000 C8.000,1.791 9.791,-0.000 12.000,-0.000 L41.000,-0.000 L56.000,14.000 L56.000,52.000 C56.000,54.209 54.209,56.000 52.000,56.000 Z"/>
+<path fill-rule="evenodd"  fill="rgb(255, 164, 125)"
+ d="M41.000,-0.000 L41.000,10.000 C41.000,12.209 42.791,14.000 45.000,14.000 L56.000,14.000 L41.000,-0.000 Z"/>
+<path fill-rule="evenodd"  fill="rgb(222, 101, 49)"
+ d="M8.000,14.000 L56.000,14.000 L56.000,28.000 L8.000,28.000 L8.000,14.000 Z"/>
+<path fill-rule="evenodd"  fill="rgb(194, 77, 27)"
+ d="M8.000,28.000 L56.000,28.000 L56.000,42.000 L8.000,42.000 L8.000,28.000 Z"/>
+<path fill-rule="evenodd"  fill="rgb(161, 61, 18)"
+ d="M8.000,42.000 L56.000,42.000 L56.000,52.000 C56.000,54.209 54.209,56.000 52.000,56.000 L12.000,56.000 C9.791,56.000 8.000,54.209 8.000,52.000 L8.000,42.000 Z"/>
+<g filter="url(#Filter_0)">
+<path fill-rule="evenodd"  fill="rgb(161, 61, 18)"
+ d="M6.000,24.000 L40.000,24.000 C42.209,24.000 44.000,25.791 44.000,28.000 L44.000,42.000 C44.000,44.209 42.209,46.000 40.000,46.000 L6.000,46.000 C3.791,46.000 2.000,44.209 2.000,42.000 L2.000,28.000 C2.000,25.791 3.791,24.000 6.000,24.000 Z"/>
+</g>
+<path fill-rule="evenodd"  fill="rgb(255, 255, 255)"
+ d="M34.697,31.455 L34.697,40.900 L33.502,40.900 L32.299,40.900 L32.299,31.455 L28.830,31.455 L28.830,30.439 L28.830,29.416 L38.166,29.416 L38.166,30.432 L38.166,31.455 L34.697,31.455 ZM23.955,36.799 C23.028,36.794 22.226,36.799 21.549,36.814 L21.549,40.900 L20.354,40.900 L19.151,40.900 L19.151,29.416 L20.346,29.416 L23.955,29.416 C25.262,29.416 26.257,29.722 26.940,30.334 C27.622,30.946 27.963,31.838 27.963,33.010 C27.963,34.249 27.622,35.191 26.940,35.834 C26.257,36.477 25.262,36.799 23.955,36.799 ZM25.061,31.803 C24.725,31.529 24.221,31.393 23.549,31.393 L21.549,31.393 L21.549,34.822 L23.549,34.822 C24.221,34.822 24.725,34.674 25.061,34.377 C25.397,34.080 25.565,33.632 25.565,33.033 C25.565,32.486 25.397,32.076 25.061,31.803 ZM13.283,36.799 C12.356,36.794 11.554,36.799 10.877,36.814 L10.877,40.900 L9.682,40.900 L8.479,40.900 L8.479,29.416 L9.674,29.416 L13.283,29.416 C14.591,29.416 15.585,29.722 16.268,30.334 C16.950,30.946 17.291,31.838 17.291,33.010 C17.291,34.249 16.950,35.191 16.268,35.834 C15.585,36.477 14.591,36.799 13.283,36.799 ZM14.389,31.803 C14.053,31.529 13.549,31.393 12.877,31.393 L10.877,31.393 L10.877,34.822 L12.877,34.822 C13.549,34.822 14.053,34.674 14.389,34.377 C14.725,34.080 14.893,33.632 14.893,33.033 C14.893,32.486 14.725,32.076 14.389,31.803 Z"/>
+</svg>

+ 11 - 0
src/components/view_file/assets/video_icon.svg

@@ -0,0 +1,11 @@
+<svg 
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ width="50px" height="40px">
+<path fill-rule="evenodd"  fill="rgb(45, 140, 240)"
+ d="M8.000,-0.000 L32.000,-0.000 C36.418,-0.000 40.000,3.582 40.000,8.000 L40.000,32.000 C40.000,36.418 36.418,40.000 32.000,40.000 L8.000,40.000 C3.582,40.000 -0.000,36.418 -0.000,32.000 L-0.000,8.000 C-0.000,3.582 3.582,-0.000 8.000,-0.000 Z"/>
+<path fill-rule="evenodd"  fill="rgb(241, 208, 80)"
+ d="M16.193,9.993 C15.052,9.993 14.000,10.919 14.000,12.225 L14.000,27.774 C14.000,29.080 15.053,30.006 16.193,30.006 C16.486,30.006 16.785,29.945 17.073,29.814 L28.866,22.387 C29.620,21.782 29.997,20.891 29.997,20.000 C29.997,19.108 29.620,18.217 28.866,17.612 L17.073,10.186 C16.785,10.054 16.486,9.993 16.193,9.993 "/>
+<path fill-rule="evenodd"  fill="rgb(126, 189, 255)"
+ d="M44.920,33.767 L40.000,31.127 L40.000,9.872 L44.920,7.232 C47.218,5.999 50.000,7.663 50.000,10.269 L50.000,30.730 C50.000,33.337 47.218,35.000 44.920,33.767 Z"/>
+</svg>

+ 1 - 0
src/components/view_file/assets/view_off.svg

@@ -0,0 +1 @@
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1623827680993" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="6860" width="128" height="128" xmlns:xlink="http://www.w3.org/1999/xlink"><defs><style type="text/css"></style></defs><path d="M971.232 41.216l0.768 0.768a32 32 0 0 1 0 44.96L88.512 982.496a31.808 31.808 0 0 1-41.952 2.944l-3.808-3.424a32 32 0 0 1 0-44.96l169.12-171.36c-58.08-49.28-112.256-114.4-162.592-195.36l-7.84-12.8a65.664 65.664 0 0 1 0-67.84c130.176-215.392 286.656-323.104 469.44-323.104 81.312 0 157.6 21.312 228.8 64l186.56-189.088a31.808 31.808 0 0 1 44.992-0.32zM828.576 297.28c51.584 47.04 100.16 106.72 145.728 179.104l8.096 13.024c12.8 20.96 12.8 47.488 0 68.448-131.68 215.2-288.864 322.784-471.52 322.784-73.152 0-142.08-17.248-206.784-51.712l47.84-48.48a369.888 369.888 0 0 0 158.944 35.296c158.144 0 296.192-94.528 417.088-292.096l-7.68-12.352c-43.424-69.056-89.088-124.96-137.024-168.128l45.312-45.888z m-317.696-65.792c-158.112 0-295.392 94.496-414.848 292.16l7.424 12.064c48.32 77.76 99.552 138.88 153.856 183.904l91.008-92.256a195.776 195.776 0 0 1-29.504-103.744c0-107.552 86.016-194.752 192.128-194.752 37.6 0 72.704 10.976 102.336 29.92l79.424-80.544a373.76 373.76 0 0 0-181.824-46.72z m174.656 210.784c11.264 24.768 17.504 52.32 17.504 81.344 0 107.552-85.984 194.752-192.096 194.752a189.248 189.248 0 0 1-80.256-17.76l50.08-50.784c9.696 2.368 19.776 3.616 30.176 3.616 70.72 0 128.064-58.112 128.064-129.824a131.84 131.84 0 0 0-3.552-30.56l50.08-50.784z m-174.592-48.48c-70.72 0-128.096 58.112-128.096 129.824 0 20.064 4.48 39.04 12.48 56l170.88-173.152a126.144 126.144 0 0 0-55.264-12.672z" fill="#bfbfbf" p-id="6861"></path></svg>

+ 35 - 0
src/components/view_file/assets/word_icon.svg

@@ -0,0 +1,35 @@
+<svg 
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ width="56px" height="56px">
+<defs>
+<filter filterUnits="userSpaceOnUse" id="Filter_0" x="0px" y="8px" width="35px" height="35px"  >
+                <feOffset in="SourceAlpha" dx="0.5" dy="0.866" />
+                <feGaussianBlur result="blurOut" stdDeviation="2" />
+                <feFlood flood-color="rgb(15, 66, 140)" result="floodOut" />
+                <feComposite operator="atop" in="floodOut" in2="blurOut" />
+                <feComponentTransfer><feFuncA type="linear" slope="0.75"/></feComponentTransfer>
+                <feMerge>
+    <feMergeNode/>
+    <feMergeNode in="SourceGraphic"/>
+  </feMerge>
+            </filter>
+
+</defs>
+<path fill-rule="evenodd"  fill="rgb(63, 167, 240)"
+ d="M52.000,56.000 L12.000,56.000 C9.791,56.000 8.000,54.209 8.000,52.000 L8.000,4.000 C8.000,1.791 9.791,-0.000 12.000,-0.000 L41.000,-0.000 L56.000,14.000 L56.000,52.000 C56.000,54.209 54.209,56.000 52.000,56.000 Z"/>
+<path fill-rule="evenodd"  fill="rgb(125, 201, 255)"
+ d="M41.000,-0.000 L41.000,10.000 C41.000,12.209 42.791,14.000 45.000,14.000 L56.000,14.000 L41.000,-0.000 Z"/>
+<path fill-rule="evenodd"  fill="rgb(45, 121, 207)"
+ d="M8.000,14.000 L56.000,14.000 L56.000,28.000 L8.000,28.000 L8.000,14.000 Z"/>
+<path fill-rule="evenodd"  fill="rgb(26, 90, 187)"
+ d="M8.000,28.000 L56.000,28.000 L56.000,42.000 L8.000,42.000 L8.000,28.000 Z"/>
+<path fill-rule="evenodd"  fill="rgb(15, 66, 140)"
+ d="M8.000,42.000 L56.000,42.000 L56.000,52.000 C56.000,54.209 54.209,56.000 52.000,56.000 L12.000,56.000 C9.791,56.000 8.000,54.209 8.000,52.000 L8.000,42.000 Z"/>
+<g filter="url(#Filter_0)">
+<path fill-rule="evenodd"  fill="rgb(22, 76, 178)"
+ d="M6.000,10.000 L26.000,10.000 C28.209,10.000 30.000,11.791 30.000,14.000 L30.000,34.000 C30.000,36.209 28.209,38.000 26.000,38.000 L6.000,38.000 C3.791,38.000 2.000,36.209 2.000,34.000 L2.000,14.000 C2.000,11.791 3.791,10.000 6.000,10.000 Z"/>
+</g>
+<path fill-rule="evenodd"  fill="rgb(255, 255, 255)"
+ d="M12.920,30.900 L14.631,30.900 L16.641,21.408 L16.633,21.408 L18.592,30.900 L20.315,30.900 L22.026,30.900 L25.717,18.150 L24.015,18.150 L22.325,18.150 L20.255,27.537 L20.255,27.537 L18.369,18.150 L16.682,18.150 L15.006,18.150 L13.108,27.514 L13.094,27.514 L11.058,18.150 L9.276,18.150 L7.494,18.150 L11.197,30.900 L11.197,30.900 L11.197,30.900 L11.197,30.900 L11.197,30.900 L11.197,30.900 L11.197,30.900 L11.197,30.900 L12.920,30.900 Z"/>
+</svg>

+ 24 - 0
src/components/view_file/assets/zip_icon.svg

@@ -0,0 +1,24 @@
+<svg 
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ width="52px" height="48px">
+<defs>
+<linearGradient id="PSgrad_0" x1="0%" x2="0%" y1="100%" y2="0%">
+  <stop offset="0%" stop-color="rgb(0,72,185)" stop-opacity="1" />
+  <stop offset="100%" stop-color="rgb(3,92,232)" stop-opacity="1" />
+</linearGradient>
+
+</defs>
+<path fill-rule="evenodd"  fill="url(#PSgrad_0)"
+ d="M44.000,48.000 L8.000,48.000 C3.582,48.000 -0.000,44.418 -0.000,40.000 L-0.000,8.000 C-0.000,3.582 3.582,0.000 8.000,0.000 L21.254,-0.000 C26.007,-0.000 27.360,4.000 27.360,4.000 L44.000,4.000 C48.418,4.000 52.000,7.582 52.000,12.000 L52.000,40.000 C52.000,44.418 48.418,48.000 44.000,48.000 Z"/>
+<path fill-rule="evenodd"  fill="rgb(246, 98, 71)"
+ d="M10.990,8.990 L40.990,8.990 C42.095,8.990 42.990,9.885 42.990,10.990 L42.990,40.990 L8.990,40.990 L8.990,10.990 C8.990,9.885 9.885,8.990 10.990,8.990 Z"/>
+<path fill-rule="evenodd"  fill="rgb(244, 209, 74)"
+ d="M7.990,14.990 L43.990,14.990 C45.095,14.990 45.990,15.885 45.990,16.990 L45.990,37.990 L5.990,37.990 L5.990,16.990 C5.990,15.885 6.885,14.990 7.990,14.990 Z"/>
+<path fill-rule="evenodd"  fill="rgb(82, 221, 234)"
+ d="M5.000,21.000 L47.000,21.000 C48.105,21.000 49.000,21.895 49.000,23.000 L49.000,44.000 L3.000,44.000 L3.000,23.000 C3.000,21.895 3.895,21.000 5.000,21.000 Z"/>
+<path fill-rule="evenodd"  fill="rgb(255, 255, 255)"
+ d="M33.000,45.000 L27.000,45.000 L27.000,39.000 L33.000,39.000 L33.000,45.000 ZM21.000,39.000 L21.000,33.000 L27.000,33.000 L27.000,39.000 L21.000,39.000 ZM21.000,21.000 L27.000,21.000 L27.000,27.000 L21.000,27.000 L21.000,21.000 ZM21.000,9.000 L27.000,9.000 L27.000,15.000 L21.000,15.000 L21.000,9.000 ZM33.000,15.000 L33.000,21.000 L27.000,21.000 L27.000,15.000 L33.000,15.000 ZM33.000,27.000 L33.000,33.000 L27.000,33.000 L27.000,27.000 L33.000,27.000 Z"/>
+<path fill-rule="evenodd"  fill="rgb(22, 136, 255)"
+ d="M51.878,26.000 L44.582,26.000 C41.168,26.000 38.103,28.090 36.857,31.268 L36.174,33.006 C35.466,34.812 33.724,36.000 31.784,36.000 L20.216,36.000 C18.276,36.000 16.534,34.812 15.825,33.006 L15.143,31.268 C13.897,28.090 10.831,26.000 7.418,26.000 L-0.000,26.000 L-0.000,40.000 C-0.000,44.400 3.600,48.000 8.000,48.000 L44.000,48.000 C48.400,48.000 52.000,44.400 52.000,40.000 L52.000,26.122 C52.000,26.055 51.945,26.000 51.878,26.000 Z"/>
+</svg>

+ 31 - 0
src/components/view_file/components/index.less

@@ -0,0 +1,31 @@
+.preview-wrapper {
+  padding: 20px 40px;
+  height: ~'calc(100vh - 320px)';
+  width: 100%;
+  overflow: scroll;
+  .loading-process {
+    width: 50%;
+    position: absolute;
+    top: 50%;
+    transform: translate(-50%, -50%);
+    left: 50%;
+    .load-text {
+      line-height: 28px;
+    }
+  }
+  .preview-inner {
+    width: 100%;
+    height: fit-content;
+    position: relative;
+    .preview-mark {
+      position: absolute;
+      width: 100%;
+      height: 100%;
+      z-index: 99;
+      opacity: .7;
+    }
+  }
+  .error-wrapper {
+    height: 100%;
+  }
+}

+ 402 - 0
src/components/view_file/components/index.vue

@@ -0,0 +1,402 @@
+<!--
+ * @Author: ChenYaJin
+ * @Date: 2021-06-16 14:21:39
+ * @LastEditors: LiZhiWei
+ * @LastEditTime: 2025-04-16 10:21:48
+ * @Description: 文件柜-预览文件
+-->
+<template>
+  <div class="">
+    <w-header desc="预览文件详情。" class="header-rewrite">
+      <template>
+        <el-select
+          v-model="downloadUrl"
+          filterable
+          allow-create
+          default-first-option
+          clearable
+          placeholder="请选择或输入文件预览地址"
+          class="input-with-button"
+        >
+          <el-option
+            v-for="item in urlOptions"
+            :key="item.value"
+            :label="item.label"
+            :value="item.value"
+          >
+            <span class="url-label">{{ item.label }}</span>
+            <span class="url-value">{{ item.value }}</span>
+          </el-option>
+        </el-select>
+        <el-button
+          type="primary"
+          :loading="downloading"
+          @click="handleDownload"
+          style="margin-left: 10px;"
+        >
+          预览文件
+        </el-button>
+      </template>
+    </w-header>
+    <div class="modal-height">
+      <div class="file-box">
+        <p class="file-name">{{file.name}}</p>
+        <div class="view-wrapper">
+          <div class="preview-wrapper" ref="wrapper" v-if="isAbleView">
+            <div v-if="renderLoading && !showError" class="loading-process">
+              <el-progress class="file-progress" :text-inside="true" :stroke-width="15" :percentage="percentage"></el-progress>
+              <span class="load-text">
+                <template v-if="renderLoading && loadLoading">文件加载中....</template>
+                <template v-else>加载完成,文件显示中....</template>
+              </span>
+            </div>
+            <template  v-if="!showError">
+              <div class="preview-inner" ref="preview-inner">
+                <div class="preview-mark" ref="bg-mark" id="bg-mark"></div>
+                <div class="output" ref="output"></div>
+              </div>
+            </template>
+            <div class="error-wrapper" v-show="showError && !loadLoading">
+              <wc-page-tip tip="未找到相应文件信息" type="errorData" />
+            </div>
+          </div>
+          <!-- <view-other-component v-else  :file="currentFile" /> -->
+        </div>
+      </div>
+      <div>
+        <file-detail-modal :id="fileId"
+          :loadingFile="loadingFile"
+          :file="currentFile"
+          @updateSuccess="updateSuccess"
+          @back="turnBack"/>
+      </div>
+    </div>
+  </div>
+</template>
+<script>
+import { mapActions, mapState } from 'vuex'
+
+import commJs from '../../../comm_js/index'
+import file from '@/api/module/file.js'
+
+import { readBuffer, render } from './util'
+import FileDetailModal from '../../file_detail'
+import ViewOtherComponent from './vendors/other/index'
+import WcPageTip from '@/components/wc-page-tip'
+export default {
+  name: 'ViewFileComponent',
+  components: {
+    FileDetailModal,
+    WcPageTip,
+    ViewOtherComponent
+  },
+  props: {
+    fileId: {
+      type: String,
+      default: null
+    },
+    file: {
+      type: Object,
+      default: () => {
+        return {}
+      }
+    },
+    onTurnBack: Function
+  },
+  data () {
+    return {
+      currentFile: {},
+      reFreshFlag: false,
+      loadingFile: false,
+      loadLoading: false, // 文件加载
+      renderLoading: false, // 文件渲染
+      showError: false, // 文件出错
+      percentage: 0,
+      downloadUrl: '', // 添加输入框绑定值
+      downloading: false, // 添加加载状态
+      urlOptions: [
+        {
+          value: 'https://501351981.github.io/vue-office/examples/dist/static/test-files/test.pptx',
+          label: 'PPT示例文件'
+        },
+        {
+          value: 'https://501351981.github.io/vue-office/examples/dist/static/test-files/test.docx',
+          label: 'Word示例文件'
+        },
+        {   
+          value: 'https://501351981.github.io/vue-office/examples/dist/static/test-files/test.pdf',
+          label: 'PDF示例文件'
+        },
+        {
+          value: 'https://501351981.github.io/vue-office/examples/dist/static/test-files/test.xlsx',
+          label: 'Excel示例文件'
+        }
+      ]
+    }
+  },
+  computed: {
+    ...mapState({
+      directoryTree: state => state.fileManage.directoryTree
+    }),
+    // 可在在线预览文件类型
+    isAbleView () {
+      const lowType = (this.file.type || '').toLowerCase()
+      const typeObject = {
+        doc: ['docx'],
+        xlsx: ['xlsx'],
+        pdf: ['pdf'],
+        ppt: ['pptx'],
+        image: ['gif', 'jpg', 'jpeg', 'bmp', 'tiff', 'tif', 'png', 'svg'],
+        text: [ 'txt', 'json', 'js', 'css', 'java', 'py', 'html', 'jsx', 'ts', 'tsx', 'xml', 'md', 'log' ]
+      }
+      const ablePreviewTypes = Object.values(typeObject).flat()
+      return ablePreviewTypes.includes(lowType)
+    },
+    markMessage () {
+      const mobile = this.currentFile.mobile
+      const mobileSub = mobile ? mobile.substr(mobile.length - 4, 4) : ''
+      return this.currentFile.loginName + mobileSub
+    }
+  },
+  watch: {
+    'fileId': {
+      handler (value) {
+        if (value) {
+          this.getFileDetailFun()
+          if (this.isAbleView) {
+            this.loadFile()
+          }
+        }
+      },
+      immediate: true
+    },
+  },
+  methods: {
+    ...mapActions('fileManage', ['getFileDetail']),
+    updateSuccess () {
+      this.reFreshFlag = true
+    },
+    turnBack (reFresh) {
+      this.reFreshFlag = this.reFreshFlag || reFresh
+      this.onTurnBack(this.reFreshFlag)
+    },
+    getFileDetailFun () {
+      this.currentFile = { ...this.file }
+      this.reFreshFlag = false
+      this.loadingFile = true
+      try {
+        this.getFileDetail(this.fileId)
+          .then(res => {
+            this.currentFile = { ...this.file, ...res }
+            const stringArr = commJs.getParentList(this.directoryTree, res.cabinetDirectoryId)
+            this.currentFile.directoryName = stringArr.join('/')
+            this.currentFile.markMessage = this.markMessage
+          })
+          .finally(_ => {
+            this.loadingFile = false
+          })
+      } catch (error) {
+        this.loadingFile = false
+        this.$message.error('未查询到文件!')
+      }
+    },
+    async loadFile () {
+      this.loadLoading = true
+      this.renderLoading = true
+      this.showError = false
+      const progressFunc = progress => {
+        const percentage = Number(Number(progress.loaded / progress.total) * 100).toFixed(2)
+        this.percentage = Number(percentage)
+        if (progress.loaded >= progress.total) {
+          this.percentage = 100
+          this.loadLoading = false
+          const progressBar = document.querySelector('.file-progress .el-progress-bar__outer')
+          progressBar.style.backgroundColor = '#409eff'
+        }
+      }
+      file.previewCabinetFile(this.fileId, progressFunc).then(res => {
+        this.percentage = 100
+        const _this = this
+        readBuffer(res.data).then(arrayBuffer => {
+          this.renderResult(arrayBuffer, this.file.type)
+            .then(_ => {
+              const output = this.$refs['output']
+              const mark = this.$refs['bg-mark']
+              const isXlsx = ['xlsx'].includes(this.file.type)
+              if (isXlsx) {
+                // TODO 解决xlsx类型时,水印渲染限制table的滚动
+                _this.$refs['preview-inner'].removeChild(mark)
+                commJs.addWaterMarker(output, this.currentFile.markMessage)
+              }
+              if (mark && output && !isXlsx) {
+                mark.style.height = output.height + 'px'
+                commJs.addWaterMarker(mark, this.currentFile.markMessage)
+              }
+            })
+        })
+          .finally(_ => {
+            this.loadLoading = false
+            this.renderLoading = false
+          })
+      }, _ => {
+        this.loadLoading = false
+        this.showError = true
+      }).finally(_ => {
+        this.loadLoading = false
+        this.renderLoading = false
+      })
+    },
+    /**
+     * @{params} buffer 文件字节流
+     * @{params} extend 文件扩展名
+     */
+    renderResult (buffer, extend) {
+      const { output } = this.$refs
+      output.innerHTML = ''
+      const node = document.createElement('div')
+      const child = output.appendChild(node)
+      return new Promise((resolve, reject) =>
+        render(buffer, extend, child).then(resolve).catch(reject)
+      )
+    },
+    handleDownload() {
+      if (!this.downloadUrl) {
+        this.$message.warning('请输入文件预览地址')
+        return
+      }
+      
+      this.downloading = true
+      this.loadLoading = true
+      this.renderLoading = true
+      this.showError = false
+      
+      const progressFunc = progress => {
+        const percentage = Number(Number(progress.loaded / progress.total) * 100).toFixed(2)
+        this.percentage = Number(percentage)
+        if (progress.loaded >= progress.total) {
+          this.percentage = 100
+          this.loadLoading = false
+          const progressBar = document.querySelector('.file-progress .el-progress-bar__outer')
+          progressBar.style.backgroundColor = '#409eff'
+        }
+      }
+      
+      const config = {
+        onDownloadProgress: progressFunc,
+        timeout: 1000 * 60 * 10,
+        withCredentials: false, // Disable credentials for cross-origin requests
+        headers: {
+          'Accept': 'application/json;charset=utf-8'
+        }
+      }
+      
+      file.previewCabinetFileByUrl(this.downloadUrl, config)
+        .then(res => {
+          this.percentage = 100
+          // 从URL中获取文件名和类型
+          const urlParts = this.downloadUrl.split('/')
+          const fileName = urlParts[urlParts.length - 1]
+          const fileType = fileName.split('.').pop().toLowerCase()
+          //文件大小
+          const fileSize = res.headers['content-length']/1024
+          // 更新文件信息
+          this.file = {
+            name: fileName.split('.')[0],
+            type: fileType,
+            url: this.downloadUrl,
+            size: fileSize
+          } 
+          this.currentFile = {...this.file }
+          
+          return readBuffer(res.data).then(arrayBuffer => {
+            return this.renderResult(arrayBuffer, fileType)
+          })
+        })
+        .then(() => {
+          const output = this.$refs['output']
+          const mark = this.$refs['bg-mark']
+          const isXlsx = ['xlsx'].includes(this.file.type)
+          
+        //   if (isXlsx) {
+        //     this.$refs['preview-inner'].removeChild(mark)
+        //     commJs.addWaterMarker(output, this.currentFile.markMessage)
+        //   }
+          if (mark && output) {
+            mark.style.height = output.height + 'px'
+            commJs.addWaterMarker(mark, this.currentFile.markMessage)
+          }
+          
+          this.$message.success('文件加载成功')
+        })
+        .catch(error => {
+          this.$message.error('文件加载失败:' + error.message)
+          this.showError = true
+        })
+        .finally(() => {
+          this.downloading = false
+          this.loadLoading = false
+          this.renderLoading = false
+        })
+    },
+  }
+}
+</script>
+<style lang="less">
+.docx-wrapper {
+  background: unset !important;
+  padding: 0 !important;
+  &>section.docx {
+    background: unset !important;
+  }
+}
+</style>
+<style lang="less" scoped>
+@import './index.less';
+@deep:~'>>>';
+@{deep} .header-rewrite {
+  margin-bottom: 0 !important;
+}
+.modal-height {
+  display: flex;
+  justify-content: space-between;
+  .file-box {
+    flex: 1;
+    padding: 15px 40px;
+    overflow: scroll;
+    text-align: center;
+    .file-name {
+      line-height: 36px;
+      text-align: center;
+      font-size: 16px;
+      font-weight: 500;
+    }
+    .view-wrapper {
+      position: relative;
+      width: 100%;
+      height: ~'calc(100% - 36px)';
+      overflow: hidden;
+    }
+    .output {
+      // border: 1px solid #ccc;
+    }
+  }
+}
+.input-with-button {
+  width: 400px;
+  .url-label {
+    float: left;
+    color: #606266;
+  }
+  .url-value {
+    float: right;
+    color: #909399;
+    font-size: 13px;
+  }
+}
+
+@{deep} .el-select-dropdown__item {
+  padding: 0 20px;
+  height: 40px;
+  line-height: 40px;
+}
+</style>

+ 102 - 0
src/components/view_file/components/renders.js

@@ -0,0 +1,102 @@
+/*
+ * @Author: LiZhiWei
+ * @Date: 2025-04-09 08:10:23
+ * @LastEditors: LiZhiWei
+ * @LastEditTime: 2025-04-15 14:47:12
+ * @Description:
+ */
+import { defaultOptions, renderAsync } from "docx-preview"
+import renderPdf from "./vendors/pdf"
+import renderImage from "./vendors/image"
+import renderText from "./vendors/text"
+import renderPptx from "./vendors/pptx"
+import renderSheet from "./vendors/xlsx"
+// 假装构造一个vue的包装,让上层统一处理销毁和替换节点
+const VueWrapper = (el) => ({
+  $el: el,
+  $destroy() {
+    // 什么也不需要 nothing to do
+  },
+})
+
+const handlers = [
+  // 使用docxjs支持,目前效果最好的渲染器
+  {
+    accepts: ["docx"],
+    handler: async (buffer, target) => {
+      const docxOptions = {
+        ...defaultOptions,
+        ...{
+          debug: true,
+          experimental: true,
+        },
+      }
+      await renderAsync(buffer, target, null, docxOptions)
+      return VueWrapper(target)
+    },
+  },
+  // 使用pptx2html,已通过默认值更替
+  {
+    accepts: ["pptx"],
+    handler: async (buffer, target) => {
+      return renderPptx(buffer, target)
+    },
+  },
+  // 使用sheetjs + handsontable,无样式
+  {
+    accepts: ["xlsx"],
+    handler: async (buffer, target) => {
+      return renderSheet(buffer, target)
+    },
+  },
+  // 使用pdfjs,渲染pdf,效果最好
+  {
+    accepts: ["pdf"],
+    handler: async (buffer, target) => {
+      return renderPdf(buffer, target)
+    },
+  },
+  // 图片过滤器
+  {
+    accepts: ["gif", "jpg", "jpeg", "bmp", "tiff", "tif", "png", "svg"],
+    handler: async (buffer, target) => {
+      return renderImage(buffer, target)
+    },
+  },
+  // 纯文本预览
+  {
+    accepts: [
+      "txt",
+      "json",
+      "js",
+      "css",
+      "java",
+      "py",
+      "html",
+      "jsx",
+      "ts",
+      "tsx",
+      "xml",
+      "md",
+      "log",
+    ],
+    handler: async (buffer, target) => {
+      return renderText(buffer, target)
+    },
+  },
+  // 错误处理
+  {
+    accepts: ["error"],
+    handler: async (buffer, target, type) => {
+      target.innerHTML = `<div style="text-align: center margin-top: 80px">不支持.${type}格式的在线预览,请下载后预览或转换为支持的格式</div>
+<div style="text-align: center">支持docx, xlsx, pptx, pdf, 以及纯文本格式和各种图片格式的在线预览</div>`
+      return VueWrapper(target)
+    },
+  },
+]
+
+// 匹配
+export default handlers.reduce((result, { accepts, handler }) => {
+  accepts.forEach((type) => (result[type] = handler))
+  return result
+}, {})

+ 41 - 0
src/components/view_file/components/util.js

@@ -0,0 +1,41 @@
+import renders from './renders'
+
+export async function readBuffer (file) {
+  return new Promise((resolve, reject) => {
+    const reader = new FileReader()
+    reader.onload = loadEvent => resolve(loadEvent.target.result)
+    reader.onerror = e => reject(e)
+    reader.readAsArrayBuffer(file)
+  })
+}
+
+export async function readDataURL (buffer) {
+  return new Promise((resolve, reject) => {
+    const reader = new FileReader()
+    reader.onload = loadEvent => resolve(loadEvent.target.result)
+    reader.onerror = e => reject(e)
+    reader.readAsDataURL(new Blob([buffer]))
+  })
+}
+
+export async function readText (buffer) {
+  return new Promise((resolve, reject) => {
+    const reader = new FileReader()
+    reader.onload = loadEvent => resolve(loadEvent.target.result)
+    reader.onerror = e => reject(e)
+    reader.readAsText(new Blob([buffer]), 'utf-8')
+  })
+}
+
+export function getExtend (name) {
+  const dot = name.lastIndexOf('.')
+  return name.substr(dot + 1)
+}
+
+export async function render (buffer, type, target) {
+  const handler = renders[type]
+  if (handler) {
+    return handler(buffer, target)
+  }
+  return renders.error(buffer, target, type)
+}

+ 470 - 0
src/components/view_file/components/vendors/colz/index.js

@@ -0,0 +1,470 @@
+/**
+ * Colorz (or Colz) is a Javascript "library" to help
+ * in color conversion between the usual color-spaces
+ * Hex - Rgb - Hsl / Hsv - Hsb
+ *
+ * It provides some helpers to output Canvas / CSS
+ * color strings.
+ *
+ * by Carlos Cabo 2013
+ * http://carloscabo.com
+ *
+ * Some formulas borrowed from Wikipedia or other authors.
+ */
+
+const round = Math.round
+
+/*
+ ==================================
+ Color constructors
+ ==================================
+*/
+
+export class Rgb {
+  constructor (col) {
+    this.r = col[0]
+    this.g = col[1]
+    this.b = col[2]
+  }
+
+  toString () {
+    return `rgb(${this.r},${this.g},${this.b})`
+  }
+}
+
+export class Rgba extends Rgb {
+  constructor (col) {
+    super(col)
+    this.a = col[3]
+  }
+
+  toString () {
+    return `rgba(${this.r},${this.g},${this.b},${this.a})`
+  }
+}
+
+export class Hsl {
+  constructor (col) {
+    this.h = col[0]
+    this.s = col[1]
+    this.l = col[2]
+  }
+
+  toString () {
+    return `hsl(${this.h},${this.s}%,${this.l}%)`
+  }
+}
+
+export class Hsla extends Hsl {
+  constructor (col) {
+    super(col)
+    this.a = col[3]
+  }
+
+  toString () {
+    return `hsla(${this.h},${this.s}%,${this.l}%,${this.a})`
+  }
+}
+
+/*
+ ==================================
+ Main Colz color object
+ ==================================
+*/
+export class Color {
+  constructor (r, g, b, a = 1.0) {
+    // If args are not given in (r, g, b, [a]) format, convert
+    if (typeof r === 'string') {
+      let str = r
+      // Add initial '#' if missing
+      if (str.charAt(0) !== '#') { str = '#' + str }
+      // If Hex in #fff format convert to #ffffff
+      if (str.length < 7) {
+        str = '#' + str[1] + str[1] + str[2] + str[2] + str[3] + str[3]
+      }
+      ([r, g, b] = hexToRgb(str))
+    } else if (r instanceof Array) {
+      a = r[3] || a
+      b = r[2]
+      g = r[1]
+      r = r[0]
+    }
+
+    this.r = r
+    this.g = g
+    this.b = b
+    this.a = a
+
+    this.rgb = new Rgb([this.r, this.g, this.b])
+    this.rgba = new Rgba([this.r, this.g, this.b, this.a])
+    this.hex = rgbToHex(this.r, this.g, this.b)
+
+    this.hsl = new Hsl(rgbToHsl(this.r, this.g, this.b))
+    this.h = this.hsl.h
+    this.s = this.hsl.s
+    this.l = this.hsl.l
+    this.hsla = new Hsla([this.h, this.s, this.l, this.a])
+  }
+
+  setHue (newHue) {
+    this.h = newHue
+    this.hsl.h = newHue
+    this.hsla.h = newHue
+    this.updateFromHsl()
+  }
+
+  setSat (newSat) {
+    this.s = newSat
+    this.hsl.s = newSat
+    this.hsla.s = newSat
+    this.updateFromHsl()
+  }
+
+  setLum (newLum) {
+    this.l = newLum
+    this.hsl.l = newLum
+    this.hsla.l = newLum
+    this.updateFromHsl()
+  }
+
+  setAlpha (newAlpha) {
+    this.a = newAlpha
+    this.hsla.a = newAlpha
+    this.rgba.a = newAlpha
+  }
+
+  updateFromHsl () {
+    // Updates Rgb
+    this.rgb = null
+    this.rgb = new Rgb(hslToRgb(this.h, this.s, this.l))
+
+    this.r = this.rgb.r
+    this.g = this.rgb.g
+    this.b = this.rgb.b
+    this.rgba.r = this.rgb.r
+    this.rgba.g = this.rgb.g
+    this.rgba.b = this.rgb.b
+
+    // Updates Hex
+    this.hex = null
+    this.hex = rgbToHex([this.r, this.g, this.b])
+  }
+}
+
+/*
+ ==================================
+ Public Methods
+ ==================================
+*/
+
+export const randomColor = function () {
+  const r = '#' + Math.random().toString(16).slice(2, 8)
+  return new Color(r)
+}
+
+export const hexToRgb = function (hex) {
+  const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex)
+  return result ? [
+    parseInt(result[1], 16),
+    parseInt(result[2], 16),
+    parseInt(result[3], 16)
+  ] : null
+}
+
+export const componentToHex = function (c) {
+  const hex = c.toString(16)
+  return hex.length === 1 ? '0' + hex : hex
+}
+
+// You can pass 3 numeric values or 1 Array
+export const rgbToHex = function (r, g, b) {
+  if (r instanceof Array) {
+    b = r[2]
+    g = r[1]
+    r = r[0]
+  }
+  return '#' + componentToHex(r) + componentToHex(g) + componentToHex(b)
+}
+
+/**
+ * Converts an RGB color value to HSL. Conversion formula
+ * adapted from http://en.wikipedia.org/wiki/HSL_color_space.
+ *
+ * @param {Number} r The red color value
+ * @param {Number} g The green color value
+ * @param {Number} b The blue color value
+ * @return {Array} The HSL representation
+ */
+export const rgbToHsl = function (r, g, b) {
+  if (r instanceof Array) {
+    b = r[2]
+    g = r[1]
+    r = r[0]
+  }
+
+  let h, s, l, d, max, min
+
+  r /= 255
+  g /= 255
+  b /= 255
+
+  max = Math.max(r, g, b)
+  min = Math.min(r, g, b)
+  l = (max + min) / 2
+
+  if (max === min) {
+    h = s = 0 // achromatic
+  } else {
+    d = max - min
+    s = l > 0.5 ? d / (2 - max - min) : d / (max + min)
+
+    switch (max) {
+      case r:
+        h = (g - b) / d + (g < b ? 6 : 0)
+        break
+      case g:
+        h = (b - r) / d + 2
+        break
+      case b:
+        h = (r - g) / d + 4
+        break
+    }
+
+    h /= 6
+  }
+
+  // CARLOS
+  h = round(h * 360)
+  s = round(s * 100)
+  l = round(l * 100)
+
+  return [h, s, l]
+}
+
+export const hue2rgb = function (p, q, t) {
+  if (t < 0) { t += 1 }
+  if (t > 1) { t -= 1 }
+  if (t < 1 / 6) { return p + (q - p) * 6 * t }
+  if (t < 1 / 2) { return q }
+  if (t < 2 / 3) { return p + (q - p) * (2 / 3 - t) * 6 }
+  return p
+}
+
+/**
+ * Converts an HSL color value to RGB. Conversion formula
+ * adapted from http://en.wikipedia.org/wiki/HSL_color_space.
+ *
+ * @param {Number} h The hue
+ * @param {Number} s The saturation
+ * @param {Number} l The lightness
+ * @return {Array} The RGB representation
+ */
+
+export const hslToRgb = function (h, s, l) {
+  if (h instanceof Array) {
+    l = h[2]
+    s = h[1]
+    h = h[0]
+  }
+  h = h / 360
+  s = s / 100
+  l = l / 100
+
+  let r, g, b, q, p
+
+  if (s === 0) {
+    r = g = b = l // achromatic
+  } else {
+    q = l < 0.5 ? l * (1 + s) : l + s - l * s
+    p = 2 * l - q
+    r = hue2rgb(p, q, h + 1 / 3)
+    g = hue2rgb(p, q, h)
+    b = hue2rgb(p, q, h - 1 / 3)
+  }
+  return [round(r * 255), round(g * 255), round(b * 255)]
+}
+
+/**
+ * Converts an RGB color value to HSB / HSV. Conversion formula
+ * adapted from http://en.wikipedia.org/wiki/HSV_color_space.
+ *
+ * @param {Number} r The red color value
+ * @param {Number} g The green color value
+ * @param {Number} b The blue color value
+ * @return {Array} The HSB representation
+ */
+export const rgbToHsb = function (r, g, b) {
+  let max, min, h, s, v, d
+
+  r = r / 255
+  g = g / 255
+  b = b / 255
+
+  max = Math.max(r, g, b)
+  min = Math.min(r, g, b)
+  v = max
+
+  d = max - min
+  s = max === 0 ? 0 : d / max
+
+  if (max === min) {
+    h = 0 // achromatic
+  } else {
+    switch (max) {
+      case r:
+        h = (g - b) / d + (g < b ? 6 : 0)
+        break
+      case g:
+        h = (b - r) / d + 2
+        break
+      case b:
+        h = (r - g) / d + 4
+        break
+    }
+    h /= 6
+  }
+
+  // map top 360,100,100
+  h = round(h * 360)
+  s = round(s * 100)
+  v = round(v * 100)
+
+  return [h, s, v]
+}
+
+/**
+ * Converts an HSB / HSV color value to RGB. Conversion formula
+ * adapted from http://en.wikipedia.org/wiki/HSV_color_space.
+ *
+ * @param {Number} h The hue
+ * @param {Number} s The saturation
+ * @param {Number} v The value
+ * @return {Array} The RGB representation
+ */
+export const hsbToRgb = function (h, s, v) {
+  let r, g, b, i, f, p, q, t
+
+  // h = h / 360;
+  if (v === 0) { return [0, 0, 0] }
+
+  s = s / 100
+  v = v / 100
+  h = h / 60
+
+  i = Math.floor(h)
+  f = h - i
+  p = v * (1 - s)
+  q = v * (1 - (s * f))
+  t = v * (1 - (s * (1 - f)))
+
+  if (i === 0) {
+    r = v
+    g = t
+    b = p
+  } else if (i === 1) {
+    r = q
+    g = v
+    b = p
+  } else if (i === 2) {
+    r = p
+    g = v
+    b = t
+  } else if (i === 3) {
+    r = p
+    g = q
+    b = v
+  } else if (i === 4) {
+    r = t
+    g = p
+    b = v
+  } else if (i === 5) {
+    r = v
+    g = p
+    b = q
+  }
+
+  r = Math.floor(r * 255)
+  g = Math.floor(g * 255)
+  b = Math.floor(b * 255)
+
+  return [r, g, b]
+}
+
+export const hsvToRgb = hsbToRgb // alias
+
+/* Convert from Hsv */
+export const hsbToHsl = function (h, s, b) {
+  return rgbToHsl(hsbToRgb(h, s, b))
+}
+
+export const hsvToHsl = hsbToHsl // alias
+
+/*
+ ==================================
+ Color Scheme Builder
+ ==================================
+*/
+export class ColorScheme {
+  constructor (colorVal, angleArray) {
+    this.palette = []
+
+    if (angleArray === undefined && colorVal instanceof Array) {
+      // Asume you passing a color array ['#f00','#0f0'...]
+      this.createFromColors(colorVal)
+    } else {
+      // Create scheme from color + hue angles
+      this.createFromAngles(colorVal, angleArray)
+    }
+  }
+
+  createFromColors (colorVal) {
+    for (let i in colorVal) {
+      if (colorVal.hasOwn(i)) {
+        this.palette.push(new Color(colorVal[i]))
+      }
+    }
+    return this.palette
+  }
+
+  createFromAngles (colorVal, angleArray) {
+    this.palette.push(new Color(colorVal))
+
+    for (let i in angleArray) {
+      if (angleArray.hasOwn(i)) {
+        const tempHue = (this.palette[0].h + angleArray[i]) % 360
+        this.palette.push(new Color(hslToRgb(tempHue, this.palette[0].s, this.palette[0].l)))
+      }
+    }
+    return this.palette
+  }
+
+  /* Complementary colors constructors */
+  static Compl (colorVal) {
+    return new this(colorVal, [180])
+  }
+
+  /* Triad */
+  static Triad (colorVal) {
+    return new this(colorVal, [120, 240])
+  }
+
+  /* Tetrad */
+  static Tetrad (colorVal) {
+    return new this(colorVal, [60, 180, 240])
+  }
+
+  /* Analogous */
+  static Analog (colorVal) {
+    return new this(colorVal, [-45, 45])
+  }
+
+  /* Split complementary */
+  static Split (colorVal) {
+    return new this(colorVal, [150, 210])
+  }
+
+  /* Accented Analogous */
+  static Accent (colorVal) {
+    return new this(colorVal, [-45, 45, 180])
+  }
+}

+ 39 - 0
src/components/view_file/components/vendors/image/ImageViewer.vue

@@ -0,0 +1,39 @@
+<!--
+ * @Author: LiZhiWei
+ * @Date: 2025-04-09 08:10:23
+ * @LastEditors: LiZhiWei
+ * @LastEditTime: 2025-04-15 10:22:25
+ * @Description:
+-->
+<template>
+<div>
+   <!-- <img v-for="item in images" alt="图片" :src="item.src" :key="item.index" class="image" /> -->
+   <w-image
+    :src="images"
+    :previewList="images"
+  />
+</div>
+</template>
+
+<script>
+export default {
+  name: 'ImageViewer',
+  props: {
+    image: String
+  },
+  computed: {
+    images () {
+      return this.image ? [{ src: this.image }] : []
+    }
+  }
+}
+</script>
+
+<style scoped>
+.image {
+  display: block;
+  width: auto;
+  height: 100%;
+  margin: 0 auto;
+}
+</style>

+ 13 - 0
src/components/view_file/components/vendors/image/index.js

@@ -0,0 +1,13 @@
+import Vue from 'vue'
+import ImageViewer from './ImageViewer'
+import { readDataURL } from '../../util'
+
+/**
+ * 图片渲染
+ */
+export default async function renderImage (buffer, target) {
+  const url = await readDataURL(buffer)
+  return new Vue({
+    render: h => h(ImageViewer, { props: { image: url } })
+  }).$mount(target)
+}

+ 61 - 0
src/components/view_file/components/vendors/other/index.vue

@@ -0,0 +1,61 @@
+<!--
+ * @Author: ChenYaJin
+ * @Date: 2021-06-16 14:46:07
+ * @LastEditors: LiZhiWei
+ * @LastEditTime: 2025-04-15 15:37:16
+ * @Description: 文件柜-预览其他
+-->
+<template>
+  <div class="view-other">
+    <el-image :src="viewOffImage" class="view-off-icon">
+        <div slot="error" class="image-slot">
+          <i class="el-icon-picture-outline"></i>
+        </div>
+      </el-image>
+      <p>{{file.name}}.{{file.type}}<span class="size-item">{{getFileSize}}</span></p>
+      <p><big><strong>该类型文件不支持在线预览,请下载后查看。</strong></big></p>
+  </div>
+</template>
+<script>
+import commJs from '@/comm_js/index'
+import viewOffImage from '../../../assets/view_off.svg'
+
+export default {
+  name: 'ViewOtherComponent',
+  props: {
+    file: {
+      type: Object,
+      default: () => {
+        return {}
+      }
+    }
+  },
+  data () {
+    return {
+      viewOffImage
+    }
+  },
+  computed: {
+    getFileSize () {
+      return commJs.fileSizeFormat(this.file.size, 'KB')
+    }
+  },
+  methods: {
+  },
+  mounted () {
+  }
+}
+</script>
+<style lang="less" scoped>
+.view-other {
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  justify-content: center;
+  height: 100%;
+  .size-item {
+    margin-left: 10px;
+  }
+}
+
+</style>

+ 165 - 0
src/components/view_file/components/vendors/pdf/PdfView.vue

@@ -0,0 +1,165 @@
+<template>
+  <div class="home_wrap">
+    <div class="pdf_down">
+      <div class="pdf_set_left" @click="scaleD()">➕</div>
+      <div class="pdf_set_middle" @click="scaleX()">➖</div>
+      <!-- <div class="pdf-pre" @click="prePage">上一页</div> -->
+      <!-- <div class="pdf-next" @click="nextPage">下一页</div> -->
+    </div>
+    <div :style="{ width: pdf_div_width, margin: '0 auto' }">
+      <!-- <canvas id="the_canvas"></canvas> -->
+      <canvas
+        v-for="page in pdf_pages"
+        :id="'the_canvas' + page"
+        :key="page"
+      ></canvas>
+    </div>
+  </div>
+</template>
+
+<script>
+import PDFJS from 'pdfjs-dist'
+import workerSrc from 'pdfjs-dist/build/pdf.worker.entry'
+// const PDFJS = { GlobalWorkerOptions: {} }
+
+PDFJS.GlobalWorkerOptions.workerSrc = workerSrc
+
+export default {
+  name: 'PdfView',
+  props: {
+    data: ArrayBuffer
+  },
+  data () {
+    return {
+      pdf_scale: 1.0,
+      pdf_pages: [],
+      pdf_div_width: '',
+      currentPage: 1
+    }
+  },
+  mounted () {
+    this.loadFile()
+  },
+  methods: {
+    scaleD () {
+      let max = 0
+      if (window.screen.width > 1440) {
+        max = 1.4
+      } else {
+        max = 1.2
+      }
+      if (this.pdf_scale >= max) {
+        return
+      }
+      this.pdf_scale = this.pdf_scale + 0.1
+      this.loadFile()
+    },
+    scaleX () {
+      let min = 1.0
+      if (this.pdf_scale <= min) {
+        return
+      }
+      this.pdf_scale = this.pdf_scale - 0.1
+      this.loadFile()
+    },
+    async loadFile () {
+      this.pdfDoc = await PDFJS.getDocument(this.data).promise
+      this.pdf_pages = this.pdfDoc.numPages
+      this.$nextTick(() => this.renderPage())
+    },
+    async renderPage (num = 1) {
+      this.currentPage = num
+      const page = await this.pdfDoc.getPage(num)
+      const canvas = document.getElementById('the_canvas' + num)
+      // const canvas = document.getElementById("the_canvas")
+      const ctx = canvas.getContext('2d')
+      const dpr = window.devicePixelRatio || 1
+      const bsr =
+        ctx.webkitBackingStorePixelRatio ||
+        ctx.mozBackingStorePixelRatio ||
+        ctx.msBackingStorePixelRatio ||
+        ctx.oBackingStorePixelRatio ||
+        ctx.backingStorePixelRatio ||
+        1
+      const ratio = dpr / bsr
+      const viewport = page.getViewport({ scale: this.pdf_scale })
+      canvas.width = viewport.width * ratio
+      canvas.height = viewport.height * ratio
+      canvas.style.width = viewport.width + 'px'
+      this.pdf_div_width = viewport.width + 'px'
+      canvas.style.height = viewport.height + 'px'
+      ctx.setTransform(ratio, 0, 0, ratio, 0, 0)
+      const renderContext = {
+        canvasContext: ctx,
+        viewport: viewport
+      }
+      page.render(renderContext)
+      if (this.pdf_pages > num) {
+        setTimeout(() => {
+          return this.renderPage(num + 1)
+        })
+      }
+    },
+    nextPage () {
+      if (this.pdf_pages > this.currentPage) {
+        this.renderPage(this.currentPage + 1)
+      }
+    },
+    prePage () {
+      if (this.currentPage > 1) {
+        this.renderPage(this.currentPage - 1)
+      }
+    }
+  }
+}
+</script>
+
+<style scoped>
+.home_wrap {
+  width: 100%;
+  height: 100%;
+}
+.home_wrap .pdf_down {
+  position: fixed;
+  display: flex;
+  z-index: 20;
+  right: 26px;
+  bottom: 7%;
+  cursor: pointer;
+}
+.home_wrap .pdf-pre {
+  position: fixed;
+  display: flex;
+  z-index: 20;
+  right: 160px;
+  bottom: 9%;
+  cursor: pointer;
+}
+.home_wrap .pdf-next {
+  position: fixed;
+  display: flex;
+  z-index: 20;
+  right: 100px;
+  bottom: 9%;
+}
+.home_wrap .pdf_down .pdf_set_left {
+  width: 30px;
+  height: 40px;
+  color: #408fff;
+  font-size: 15px;
+  padding-top: 25px;
+  text-align: center;
+  margin-right: 5px;
+  cursor: pointer;
+}
+.home_wrap .pdf_down .pdf_set_middle {
+  width: 30px;
+  height: 40px;
+  color: #408fff;
+  font-size: 15px;
+  padding-top: 25px;
+  text-align: center;
+  margin-right: 5px;
+  cursor: pointer;
+}
+</style>

+ 8 - 0
src/components/view_file/components/vendors/pdf/index.js

@@ -0,0 +1,8 @@
+import Vue from 'vue'
+import PdfView from './PdfView'
+
+export default async function renderPdf (buffer, target) {
+  return new Vue({
+    render: h => h(PdfView, { props: { data: buffer } })
+  }).$mount(target)
+}

+ 5213 - 0
src/components/view_file/components/vendors/pptx/PPT.vue

@@ -0,0 +1,5213 @@
+<!--
+ * @Author: LiZhiWei
+ * @Date: 2025-04-10 14:38:27
+ * @LastEditors: LiZhiWei
+ * @LastEditTime: 2025-04-16 10:42:01
+ * @Description:
+-->
+<template>
+    <div id="pptx-container" ref="pptxContainer" class="pptx-container"></div>
+</template>
+<script>
+export default {
+  name: 'PPT',
+  props: {
+    pptxJson: Object
+  },
+  data () {
+    return {}
+  },
+  mounted () {
+    this.renderSlides()
+  },
+  computed: {},
+  watch: {
+    pptxJson: {
+      handler () {
+        this.renderSlides()
+      },
+      deep: true
+    }
+  },
+  methods: {
+    renderSlides () {
+      console.log('this.pptxJson', this.pptxJson)
+      if (!this.$refs.pptxContainer || !this.pptxJson) return
+
+      this.$refs.pptxContainer.innerHTML = ''
+      const { size, slides } = this.pptxJson
+
+      slides.forEach((slide, index) => {
+        const slideElement = this.createSlideElement(size.width, size.height)
+        this.applySlideBackground(slideElement, slide.fill)
+        if (slide.layoutElements && Array.isArray(slide.layoutElements)) {
+          this.processElements(slide.layoutElements, index + 1)
+          slide.layoutElements.forEach((element) => {
+            const el = this.createElementByType(element)
+            if (!el) return
+            slideElement.appendChild(el)
+          })
+        }
+
+        // 渲染幻灯片元素
+        if (slide.elements && Array.isArray(slide.elements)) {
+          this.processElements(slide.elements, index + 1)
+          slide.elements.forEach((element) => {
+            const el = this.createElementByType(element)
+            if (!el) return
+            slideElement.appendChild(el)
+          })
+        }
+
+        // 添加幻灯片到容器
+        this.$refs.pptxContainer.appendChild(slideElement)
+      })
+      // 为所有p标签应用样式
+      const pTags = this.$refs.pptxContainer.querySelectorAll('p')
+      pTags.forEach((p) => {
+        p.style.margin = '0px'
+        p.style.padding = '0px'
+        p.style.wordBreak = 'break-word'
+        p.style.lineHeight = '1'
+      })
+      const ulTags = this.$refs.pptxContainer.querySelectorAll('ul')
+      ulTags.forEach((ul) => {
+        ul.style.wordBreak = 'break-word'
+        ul.style.lineHeight = '1'
+      })
+      const olTags = this.$refs.pptxContainer.querySelectorAll('ol')
+      olTags.forEach((ol) => {
+        ol.style.margin = '0px'
+        ol.style.padding = 'auto 0px'
+        ol.style.wordBreak = 'break-word'
+        ol.style.lineHeight = '1'
+      })
+    },
+    // 创建幻灯片元素
+    createSlideElement (width, height) {
+      const slideElement = document.createElement('div')
+      slideElement.className = 'slide'
+      slideElement.style.position = 'relative'
+      slideElement.style.width = width + 'px'
+      slideElement.style.height = height + 'px'
+      slideElement.style.border = '1px solid #ccc'
+      slideElement.style.margin = '0px auto'
+      slideElement.style.marginBottom = '20px'
+      slideElement.style.overflow = 'hidden'
+      return slideElement
+    },
+
+    // 应用幻灯片背景
+    applySlideBackground (slideElement, fill) {
+      if (!fill) return
+
+      if (fill.type === 'gradient') {
+        const { colors, path, rot } = fill.value
+
+        if (colors && colors.length >= 2) {
+          const gradientType = path === 'rect' ? 'linear' : 'radial'
+          const gradientAngle =
+            gradientType === 'linear' ? (90 - (rot || 0)) % 360 : rot || 0
+
+          let gradientString = `${gradientType}-gradient(`
+          if (gradientType === 'linear') {
+            gradientString += `${gradientAngle}deg, `
+          }
+
+          colors.forEach((color, i) => {
+            gradientString += `${color.color} ${color.pos}${
+              i < colors.length - 1 ? ', ' : ''
+            }`
+          })
+
+          gradientString += ')'
+          slideElement.style.background = gradientString
+        }
+      } else if (fill.type === 'color') {
+        slideElement.style.backgroundColor = fill.value || '#FFFFFF'
+      }
+    },
+
+    // 根据元素类型创建DOM元素
+    createElementByType (element) {
+      switch (element.type) {
+        case 'image':
+          return this.createImageElement(element)
+        case 'video':
+          return this.createVideoElement(element)
+        case 'audio':
+          return this.createAudioElement(element)
+        case 'shape':
+          return this.createShapeElement(element)
+        case 'table':
+          return this.createTableElement(element)
+        case 'chart':
+          return this.createChartElement(element)
+        case 'diagram':
+          return this.createDiagramElement(element)
+        case 'math':
+          return this.createMathElement(element)
+        case 'group':
+          return this.createGroupElement(element)
+        default:
+          return this.createTextElement(element)
+      }
+    },
+    createGroupElement (element) {
+      // 创建组合容器
+      const groupContainer = document.createElement('div')
+      groupContainer.className = 'group-element'
+      groupContainer.style.position = 'absolute'
+      groupContainer.style.top = element.top + 'px'
+      groupContainer.style.left = element.left + 'px'
+      groupContainer.style.width = element.width + 'px'
+      groupContainer.style.height = element.height + 'px'
+      // 移除 overflow 限制,允许子元素超出容器
+      groupContainer.style.zIndex = element.order
+      groupContainer.style.transformOrigin = 'center center'
+
+      // 创建内部容器来处理子元素
+      const innerContainer = document.createElement('div')
+      innerContainer.style.position = 'relative'
+      innerContainer.style.width = '100%'
+      innerContainer.style.height = '100%'
+
+      // 应用旋转变换
+      if (element.rotate) {
+        groupContainer.style.transform = `rotate(${element.rotate}deg)`
+      }
+
+      // 应用其他样式属性
+      if (element.opacity !== undefined) {
+        groupContainer.style.opacity = element.opacity
+      }
+
+      // 递归渲染组内的所有子元素
+      if (element.elements && Array.isArray(element.elements)) {
+        element.elements.forEach((childElement) => {
+          const childCopy = JSON.parse(JSON.stringify(childElement))
+          const childEl = this.createElementByType(childCopy)
+          if (childEl) {
+            innerContainer.appendChild(childEl)
+          }
+        })
+      }
+
+      groupContainer.appendChild(innerContainer)
+      return groupContainer
+    },
+    // 递归调整元素位置
+    adjustElementPosition (element, parentElement) {
+      // 创建深拷贝,但保留特殊属性的引用
+      const adjustedElement = JSON.parse(JSON.stringify(element))
+
+      // 调整位置为相对于父组合元素的位置
+      adjustedElement.top = element.top - parentElement.top
+      adjustedElement.left = element.left - parentElement.left
+
+      // 恢复特殊属性的引用
+      const specialProps = [
+        'src',
+        'blob',
+        'fill',
+        'text',
+        'path',
+        'elements',
+        'borderColor',
+        'borderWidth',
+        'borderType'
+      ]
+      specialProps.forEach((prop) => {
+        if (element[prop] !== undefined) {
+          adjustedElement[prop] = element[prop]
+        }
+      })
+
+      // 如果是组合元素,递归处理其子元素
+      if (element.type === 'group' && element.elements) {
+        adjustedElement.elements = element.elements.map((childElement) =>
+          this.adjustElementPosition(childElement, {
+            top: element.top,
+            left: element.left
+          })
+        )
+      }
+
+      return adjustedElement
+    },
+    // 创建图像元素
+    createImageElement (element) {
+      const img = document.createElement('img')
+      img.src = element.src
+      img.style.width = element.width + 'px'
+      img.style.height = element.height + 'px'
+      img.style.objectFit = 'cover' // 保持图像的宽高比并填充容器
+      img.style.position = 'absolute'
+      img.style.top = `${element.top}px`
+      img.style.left = `${element.left}px`
+      img.style.zIndex = element.order
+
+      //   el.appendChild(img)
+      return img
+    },
+
+    // 创建视频元素
+    createVideoElement (element) {
+      const el = document.createElement('video')
+      el.src = element.blob
+      el.controls = true
+      el.style.width = element.width + 'px'
+      el.style.height = element.height + 'px'
+      el.style.position = 'absolute'
+      el.style.top = element.top + 'px'
+      el.style.left = element.left + 'px'
+      el.style.zIndex = element.order
+      return el
+    },
+
+    // 创建音频元素
+    createAudioElement (element) {
+      const el = document.createElement('audio')
+      el.src = element.blob
+      el.controls = true
+      el.style.height = element.height + 'px'
+      el.style.position = 'absolute'
+      el.style.top = element.top + 'px'
+      el.style.left = element.left + 'px'
+      el.style.zIndex = element.order
+      return el
+    },
+
+    // 创建形状元素
+    createShapeElement (element) {
+      const el = document.createElement('div')
+      el.style.position = 'absolute'
+      el.style.top = element.top + 'px'
+      el.style.left = element.left + 'px'
+      el.style.width = element.width + 'px'
+      el.style.height = element.height + 'px'
+      el.style.zIndex = element.order
+      el.style.overflow = 'visible'
+      if (element.height === 0) {
+        // 如果高度为0,直接在div上绘制边框
+        el.style.borderTop = `${element.borderWidth || 1}px ${
+          element.borderType || 'solid'
+        } ${element.borderColor || '#000'}`
+      }
+      // 使用SVG绘制形状
+      const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg')
+      svg.setAttribute('width', '100%')
+      svg.setAttribute('height', '100%')
+      svg.setAttribute('viewBox', `0 0 ${element.width} ${element.height}`)
+      svg.style.overflow = 'visible'
+
+      // 根据形状类型创建不同的SVG元素
+      switch (element.shapType) {
+        case 'rect':
+          const rect = document.createElementNS(
+            'http://www.w3.org/2000/svg',
+            'rect'
+          )
+          rect.setAttribute('x', 0)
+          rect.setAttribute('y', 0)
+          rect.setAttribute('width', element.width)
+          rect.setAttribute('height', element.height)
+          if (element.fill && element.fill.type) {
+            // 设置填充色
+            if (element.fill.type === 'color') {
+              rect.setAttribute('fill', element.fill.value || 'transparent')
+            } else if (element.fill.type === 'gradient') {
+              // 渐变填充
+              const { colors, path, rot } = element.fill.value
+
+              if (colors && colors.length >= 2) {
+                const gradientType = path === 'rect' ? 'linear' : 'radial'
+                const gradientAngle =
+                  gradientType === 'linear'
+                    ? (90 - (rot || 0)) % 360
+                    : rot || 0
+
+                let gradientString = `${gradientType}-gradient(`
+                if (gradientType === 'linear') {
+                  gradientString += `${gradientAngle}deg, `
+                }
+
+                colors.forEach((color, i) => {
+                  gradientString += `${color.color} ${color.pos}${
+                    i < colors.length - 1 ? ', ' : ''
+                  }`
+                })
+                gradientString += ')'
+
+                // 创建渐变定义
+                const gradientDef = document.createElementNS(
+                  'http://www.w3.org/2000/svg',
+                  'defs'
+                )
+                const gradientEl = document.createElementNS(
+                  'http://www.w3.org/2000/svg',
+                  gradientType === 'linear'
+                    ? 'linearGradient'
+                    : 'radialGradient'
+                )
+                const gradientId = `gradient-${Date.now()}-${Math.random()
+                  .toString(36)
+                  .substr(2, 9)}`
+                gradientEl.setAttribute('id', gradientId)
+
+                // 设置渐变属性
+                if (gradientType === 'linear') {
+                  gradientEl.setAttribute(
+                    'gradientTransform',
+                    `rotate(${gradientAngle})`
+                  )
+                }
+
+                // 添加渐变色标
+                colors.forEach((color) => {
+                  const stop = document.createElementNS(
+                    'http://www.w3.org/2000/svg',
+                    'stop'
+                  )
+                  stop.setAttribute('offset', color.pos)
+                  stop.setAttribute('stop-color', color.color)
+                  gradientEl.appendChild(stop)
+                })
+
+                gradientDef.appendChild(gradientEl)
+                svg.appendChild(gradientDef)
+
+                // 应用渐变
+                rect.setAttribute('fill', `url(#${gradientId})`)
+              }
+            } else if (element.fill.type === 'image') {
+              // 创建图案填充
+              const pattern = document.createElementNS(
+                'http://www.w3.org/2000/svg',
+                'pattern'
+              )
+              const patternId = `pattern-${Date.now()}-${Math.random()
+                .toString(36)
+                .substr(2, 9)}`
+              pattern.setAttribute('id', patternId)
+              pattern.setAttribute('patternUnits', 'userSpaceOnUse')
+              pattern.setAttribute('width', '100%')
+              pattern.setAttribute('height', '100%')
+
+              // 创建图片元素
+              const image = document.createElementNS(
+                'http://www.w3.org/2000/svg',
+                'image'
+              )
+              image.setAttribute('width', '100%')
+              image.setAttribute('height', '100%')
+              image.setAttribute('preserveAspectRatio', 'xMidYMid slice')
+              image.setAttributeNS(
+                'http://www.w3.org/1999/xlink',
+                'href',
+                element.fill.value.picBase64
+              )
+
+              pattern.appendChild(image)
+
+              // 添加pattern到defs
+              const defs = document.createElementNS(
+                'http://www.w3.org/2000/svg',
+                'defs'
+              )
+              defs.appendChild(pattern)
+              svg.appendChild(defs)
+
+              // 应用图案填充
+              rect.setAttribute('fill', `url(#${patternId})`)
+            } else {
+              console.log('element.fill', element.fill)
+              rect.setAttribute('fill', 'transparent')
+            }
+          }
+
+          // 设置边框
+          if (element.borderWidth > 0) {
+            rect.setAttribute('stroke', element.borderColor || '#000')
+            rect.setAttribute('stroke-width', element.borderWidth || 1)
+
+            // 处理虚线边框
+            if (
+              element.borderType === 'dotted' ||
+              element.borderType === 'dashed'
+            ) {
+              if (element.borderStrokeDasharray) {
+                rect.setAttribute(
+                  'stroke-dasharray',
+                  element.borderStrokeDasharray
+                )
+              } else if (element.borderType === 'dotted') {
+                rect.setAttribute('stroke-dasharray', '1, 3')
+              } else if (element.borderType === 'dashed') {
+                rect.setAttribute('stroke-dasharray', '5, 5')
+              }
+            }
+          }
+
+          svg.appendChild(rect)
+          break
+
+        case 'roundRect':
+          const radius = Math.min(element.width, element.height) * 0.1
+          const roundRect = document.createElementNS(
+            'http://www.w3.org/2000/svg',
+            'rect'
+          )
+          roundRect.setAttribute('x', 0)
+          roundRect.setAttribute('y', 0)
+          roundRect.setAttribute('width', element.width)
+          roundRect.setAttribute('height', element.height)
+          roundRect.setAttribute('rx', radius)
+          roundRect.setAttribute('ry', radius)
+
+          // 设置填充色
+          if (element.fill && element.fill.type === 'color') {
+            roundRect.setAttribute('fill', element.fill.value || 'transparent')
+          } else {
+            roundRect.setAttribute('fill', 'transparent')
+          }
+
+          // 设置边框
+          if (element.borderWidth > 0) {
+            roundRect.setAttribute('stroke', element.borderColor || '#000')
+            roundRect.setAttribute('stroke-width', element.borderWidth || 1)
+            roundRect.setAttribute('stroke-linejoin', 'round') // 设置连接处为圆角
+            roundRect.setAttribute('vector-effect', 'non-scaling-stroke') // 防止边框宽度变形
+            roundRect.setAttribute('shape-rendering', 'geometricPrecision') // 提高渲染精度
+            // 处理虚线边框
+            if (
+              element.borderType === 'dotted' ||
+              element.borderType === 'dashed'
+            ) {
+              if (element.borderStrokeDasharray) {
+                roundRect.setAttribute(
+                  'stroke-dasharray',
+                  element.borderStrokeDasharray
+                )
+              } else if (element.borderType === 'dotted') {
+                roundRect.setAttribute('stroke-dasharray', '1, 3')
+              } else if (element.borderType === 'dashed') {
+                roundRect.setAttribute('stroke-dasharray', '5, 5')
+              }
+            }
+          }
+
+          svg.appendChild(roundRect)
+          break
+        case 'snip1Rect':
+          // 使用path元素绘制剪去单角的矩形
+          const snip1Rect = document.createElementNS(
+            'http://www.w3.org/2000/svg',
+            'path'
+          )
+
+          // 计算剪角的大小,通常为矩形较短边的20%
+          const snipSize = Math.min(element.width, element.height) * 0.35
+
+          // 绘制路径:从左上角开始,顺时针方向,右上角被剪去
+          snip1Rect.setAttribute(
+            'd',
+            `M0,0 ` +
+              `L${element.width - snipSize},0 ` +
+              `L${element.width},${snipSize} ` +
+              `L${element.width},${element.height} ` +
+              `L0,${element.height} ` +
+              `Z`
+          )
+
+          // 设置填充色
+          if (element.fill && element.fill.type === 'color') {
+            snip1Rect.setAttribute('fill', element.fill.value || 'transparent')
+          } else {
+            snip1Rect.setAttribute('fill', 'transparent')
+          }
+
+          // 设置边框
+          if (element.borderWidth > 0) {
+            snip1Rect.setAttribute('stroke', element.borderColor || '#000')
+            snip1Rect.setAttribute('stroke-width', element.borderWidth || 1)
+
+            // 处理虚线边框
+            if (
+              element.borderType === 'dotted' ||
+              element.borderType === 'dashed'
+            ) {
+              if (element.borderStrokeDasharray) {
+                snip1Rect.setAttribute(
+                  'stroke-dasharray',
+                  element.borderStrokeDasharray
+                )
+              } else if (element.borderType === 'dotted') {
+                snip1Rect.setAttribute('stroke-dasharray', '1, 3')
+              } else if (element.borderType === 'dashed') {
+                snip1Rect.setAttribute('stroke-dasharray', '5, 5')
+              }
+            }
+          }
+          svg.appendChild(snip1Rect)
+          break
+        case 'snip2SameRect':
+          // 使用path元素绘制剪去四个角的矩形
+          const snip2SameRect = document.createElementNS(
+            'http://www.w3.org/2000/svg',
+            'path'
+          )
+
+          // 计算剪角的大小,通常为矩形较短边的20%
+          const snip2Size = Math.min(element.width, element.height) * 0.35
+
+          // 绘制路径:从左上角开始,顺时针方向,剪去四个角
+          snip2SameRect.setAttribute(
+            'd',
+            `M${snip2Size},0 ` +
+              `L${element.width - snip2Size},0 ` +
+              `L${element.width},${snip2Size} ` +
+              `L${element.width},${element.height - snip2Size} ` +
+              `L${element.width - snip2Size},${element.height} ` +
+              `L${snip2Size},${element.height} ` +
+              `L0,${element.height - snip2Size} ` +
+              `L0,${snip2Size} ` +
+              `Z`
+          )
+
+          // 设置填充色
+          if (element.fill && element.fill.type === 'color') {
+            snip2SameRect.setAttribute(
+              'fill',
+              element.fill.value || 'transparent'
+            )
+          } else {
+            snip2SameRect.setAttribute('fill', 'transparent')
+          }
+
+          // 设置边框
+          if (element.borderWidth > 0) {
+            snip2SameRect.setAttribute('stroke', element.borderColor || '#000')
+            snip2SameRect.setAttribute(
+              'stroke-width',
+              element.borderWidth || 1
+            )
+
+            // 处理虚线边框
+            if (
+              element.borderType === 'dotted' ||
+              element.borderType === 'dashed'
+            ) {
+              if (element.borderStrokeDasharray) {
+                snip2SameRect.setAttribute(
+                  'stroke-dasharray',
+                  element.borderStrokeDasharray
+                )
+              } else if (element.borderType === 'dotted') {
+                snip2SameRect.setAttribute('stroke-dasharray', '2, 2')
+                snip2SameRect.setAttribute('stroke-linecap', 'round')
+              } else if (element.borderType === 'dashed') {
+                snip2SameRect.setAttribute('stroke-dasharray', '6, 3')
+              }
+            }
+
+            // 添加这些属性以确保边框正确渲染
+            snip2SameRect.setAttribute('vector-effect', 'non-scaling-stroke')
+            snip2SameRect.setAttribute('shape-rendering', 'geometricPrecision')
+          }
+
+          svg.appendChild(snip2SameRect)
+          break
+        case 'line':
+          // 创建直线连接符
+          const line = document.createElementNS(
+            'http://www.w3.org/2000/svg',
+            'line'
+          )
+          // 设置SVG的最小高度为1px
+          const svgHeight = element.height > 0 ? element.height : 1
+          svg.setAttribute('height', svgHeight + 'px')
+
+          // 设置线条的起点和终点
+          line.setAttribute('x1', 0)
+          line.setAttribute('y1', 0)
+          line.setAttribute('x2', element.width)
+          line.setAttribute('y2', svgHeight)
+
+          // 确保边框颜色和宽度被正确设置
+          line.setAttribute('stroke', element.borderColor || '#000')
+          line.setAttribute('stroke-width', element.borderWidth || 1)
+
+          // 处理虚线样式
+          if (
+            element.borderType === 'dotted' ||
+            element.borderType === 'dashed'
+          ) {
+            if (element.borderStrokeDasharray) {
+              line.setAttribute(
+                'stroke-dasharray',
+                element.borderStrokeDasharray
+              )
+            } else if (element.borderType === 'dotted') {
+              line.setAttribute('stroke-dasharray', '2, 2')
+              line.setAttribute('stroke-linecap', 'round') // 添加圆角端点使点线更明显
+            } else if (element.borderType === 'dashed') {
+              line.setAttribute('stroke-dasharray', '6, 3')
+            }
+          }
+
+          // 确保线条正确渲染
+          line.setAttribute('vector-effect', 'non-scaling-stroke') // 防止缩放影响线条宽度
+          svg.appendChild(line)
+          break
+        case 'snip2DiagRect':
+          // 使用path元素绘制剪去右上角和左下角的矩形
+          const snip2DiagRect = document.createElementNS(
+            'http://www.w3.org/2000/svg',
+            'path'
+          )
+
+          // 计算剪角的大小,通常为矩形较短边的35%
+          const snipDiagSize = Math.min(element.width, element.height) * 0.35
+
+          // 绘制路径:从左上角开始,顺时针方向,右上角和左下角被剪去
+          snip2DiagRect.setAttribute(
+            'd',
+            `M0,0 ` +
+              `L${element.width - snipDiagSize},0 ` +
+              `L${element.width},${snipDiagSize} ` +
+              `L${element.width},${element.height} ` +
+              `L${snipDiagSize},${element.height} ` +
+              `L0,${element.height - snipDiagSize} ` +
+              `Z`
+          )
+
+          // 设置填充色
+          if (element.fill && element.fill.type === 'color') {
+            snip2DiagRect.setAttribute(
+              'fill',
+              element.fill.value || 'transparent'
+            )
+          } else {
+            snip2DiagRect.setAttribute('fill', 'transparent')
+          }
+
+          // 设置边框
+          if (element.borderWidth > 0) {
+            snip2DiagRect.setAttribute('stroke', element.borderColor || '#000')
+            snip2DiagRect.setAttribute(
+              'stroke-width',
+              element.borderWidth || 1
+            )
+
+            // 处理虚线边框
+            if (
+              element.borderType === 'dotted' ||
+              element.borderType === 'dashed'
+            ) {
+              if (element.borderStrokeDasharray) {
+                snip2DiagRect.setAttribute(
+                  'stroke-dasharray',
+                  element.borderStrokeDasharray
+                )
+              } else if (element.borderType === 'dotted') {
+                snip2DiagRect.setAttribute('stroke-dasharray', '2, 2')
+                snip2DiagRect.setAttribute('stroke-linecap', 'round')
+              } else if (element.borderType === 'dashed') {
+                snip2DiagRect.setAttribute('stroke-dasharray', '6, 3')
+              }
+            }
+
+            // 添加这些属性以确保边框正确渲染
+            snip2DiagRect.setAttribute('vector-effect', 'non-scaling-stroke')
+            snip2DiagRect.setAttribute('shape-rendering', 'geometricPrecision')
+          }
+
+          svg.appendChild(snip2DiagRect)
+          break
+        case 'snipRoundRect':
+          const snipRoundRect = document.createElementNS(
+            'http://www.w3.org/2000/svg',
+            'path'
+          )
+          // 计算剪切大小和圆角大小
+          const snipSize2 = Math.min(element.width, element.height) * 0.4
+          const roundSize = Math.min(element.width, element.height) * 0.5
+
+          // 绘制路径:从左上角开始,顺时针方向,左上为圆角,右上为剪切角
+          snipRoundRect.setAttribute(
+            'd',
+            `M${roundSize},0 ` +
+              `L${element.width - snipSize2},0 ` +
+              `L${element.width},${snipSize2} ` +
+              `L${element.width},${element.height} ` +
+              `L0,${element.height} ` +
+              `L0,${roundSize} ` +
+              `A${roundSize},${roundSize} 0 0,1 ${roundSize},0 ` +
+              `Z`
+          )
+
+          // 设置填充色和边框属性
+          if (element.fill && element.fill.type === 'color') {
+            snipRoundRect.setAttribute(
+              'fill',
+              element.fill.value || 'transparent'
+            )
+          } else {
+            snipRoundRect.setAttribute('fill', 'transparent')
+          }
+
+          // 设置边框
+          if (element.borderWidth > 0) {
+            snipRoundRect.setAttribute('stroke', element.borderColor || '#000')
+            snipRoundRect.setAttribute(
+              'stroke-width',
+              element.borderWidth || 1
+            )
+
+            // 处理虚线边框
+            if (
+              element.borderType === 'dotted' ||
+              element.borderType === 'dashed'
+            ) {
+              if (element.borderStrokeDasharray) {
+                snipRoundRect.setAttribute(
+                  'stroke-dasharray',
+                  element.borderStrokeDasharray
+                )
+              } else if (element.borderType === 'dotted') {
+                snipRoundRect.setAttribute('stroke-dasharray', '2, 2')
+                snipRoundRect.setAttribute('stroke-linecap', 'round')
+              } else if (element.borderType === 'dashed') {
+                snipRoundRect.setAttribute('stroke-dasharray', '6, 3')
+              }
+            }
+
+            snipRoundRect.setAttribute('vector-effect', 'non-scaling-stroke')
+            snipRoundRect.setAttribute('shape-rendering', 'geometricPrecision')
+          }
+
+          svg.appendChild(snipRoundRect)
+          break
+        case 'round1Rect':
+          const round1Rect = document.createElementNS(
+            'http://www.w3.org/2000/svg',
+            'path'
+          )
+          // 计算圆角大小
+          const round1Size = Math.min(element.width, element.height) * 0.5
+
+          // 绘制路径:从左上角开始,顺时针方向,右上角为圆角
+          round1Rect.setAttribute(
+            'd',
+            `M0,0 ` +
+              `L${element.width - round1Size},0 ` +
+              `Q${element.width},0 ${element.width},${round1Size} ` +
+              `L${element.width},${element.height} ` +
+              `L0,${element.height} ` +
+              `Z`
+          )
+
+          // 设置填充色
+          if (element.fill && element.fill.type === 'color') {
+            round1Rect.setAttribute(
+              'fill',
+              element.fill.value || 'transparent'
+            )
+          } else {
+            round1Rect.setAttribute('fill', 'transparent')
+          }
+
+          // 设置边框
+          if (element.borderWidth > 0) {
+            round1Rect.setAttribute('stroke', element.borderColor || '#000')
+            round1Rect.setAttribute('stroke-width', element.borderWidth || 1)
+            // 处理虚线边框
+            if (
+              element.borderType === 'dotted' ||
+              element.borderType === 'dashed'
+            ) {
+              if (element.borderStrokeDasharray) {
+                round1Rect.setAttribute('stroke-dasharray', '40,15,5,15')
+              } else if (element.borderType === 'dotted') {
+                round1Rect.setAttribute('stroke-dasharray', '2, 2')
+                round1Rect.setAttribute('stroke-linecap', 'round')
+              } else if (element.borderType === 'dashed') {
+                round1Rect.setAttribute('stroke-dasharray', '6, 3')
+              }
+            }
+
+            round1Rect.setAttribute('vector-effect', 'non-scaling-stroke')
+            round1Rect.setAttribute('shape-rendering', 'geometricPrecision')
+          }
+
+          svg.appendChild(round1Rect)
+          break
+        case 'ellipse':
+        case 'circle':
+          const ellipse = document.createElementNS(
+            'http://www.w3.org/2000/svg',
+            'ellipse'
+          )
+          ellipse.setAttribute('cx', element.width / 2)
+          ellipse.setAttribute('cy', element.height / 2)
+          ellipse.setAttribute('rx', element.width / 2)
+          ellipse.setAttribute('ry', element.height / 2)
+
+          // 设置填充色
+          if (element.fill && element.fill.type === 'color') {
+            ellipse.setAttribute('fill', element.fill.value || 'transparent')
+          } else {
+            ellipse.setAttribute('fill', 'transparent')
+          }
+
+          // 设置边框
+          if (element.borderWidth > 0) {
+            ellipse.setAttribute('stroke', element.borderColor || '#000')
+            ellipse.setAttribute('stroke-width', element.borderWidth || 1)
+
+            // 处理虚线边框
+            if (
+              element.borderType === 'dotted' ||
+              element.borderType === 'dashed'
+            ) {
+              if (element.borderStrokeDasharray) {
+                ellipse.setAttribute(
+                  'stroke-dasharray',
+                  element.borderStrokeDasharray
+                )
+              } else if (element.borderType === 'dotted') {
+                ellipse.setAttribute('stroke-dasharray', '1, 3')
+              } else if (element.borderType === 'dashed') {
+                ellipse.setAttribute('stroke-dasharray', '5, 5')
+              }
+            }
+          }
+
+          svg.appendChild(ellipse)
+          break
+        case 'round2SameRect':
+          const round2SameRect = document.createElementNS(
+            'http://www.w3.org/2000/svg',
+            'path'
+          )
+          // 计算圆角大小
+          const round2Size = Math.min(element.width, element.height) * 0.5
+
+          // 绘制路径:从左上角开始,顺时针方向,左上和右上为圆角
+          round2SameRect.setAttribute(
+            'd',
+            `M${round2Size},0 ` +
+              `L${element.width - round2Size},0 ` +
+              `Q${element.width},0 ${element.width},${round2Size} ` +
+              `L${element.width},${element.height} ` +
+              `L0,${element.height} ` +
+              `L0,${round2Size} ` +
+              `Q0,0 ${round2Size},0 ` +
+              `Z`
+          )
+
+          // 设置填充色
+          if (element.fill && element.fill.type === 'color') {
+            round2SameRect.setAttribute(
+              'fill',
+              element.fill.value || 'transparent'
+            )
+          } else {
+            round2SameRect.setAttribute('fill', 'transparent')
+          }
+
+          // 设置边框
+          if (element.borderWidth > 0) {
+            round2SameRect.setAttribute(
+              'stroke',
+              element.borderColor || '#000'
+            )
+            round2SameRect.setAttribute(
+              'stroke-width',
+              element.borderWidth || 1
+            )
+
+            // 处理虚线边框
+            if (
+              element.borderType === 'dotted' ||
+              element.borderType === 'dashed'
+            ) {
+              if (element.borderStrokeDasharray) {
+                round2SameRect.setAttribute(
+                  'stroke-dasharray',
+                  element.borderStrokeDasharray
+                )
+              } else if (element.borderType === 'dotted') {
+                round2SameRect.setAttribute('stroke-dasharray', '2, 2')
+                round2SameRect.setAttribute('stroke-linecap', 'round')
+              } else if (element.borderType === 'dashed') {
+                round2SameRect.setAttribute('stroke-dasharray', '6, 3')
+              }
+            }
+
+            round2SameRect.setAttribute('vector-effect', 'non-scaling-stroke')
+            round2SameRect.setAttribute(
+              'shape-rendering',
+              'geometricPrecision'
+            )
+          }
+
+          svg.appendChild(round2SameRect)
+          break
+        case 'round2DiagRect':
+          const round2DiagRect = document.createElementNS(
+            'http://www.w3.org/2000/svg',
+            'path'
+          )
+          // 计算圆角大小
+          const round2DiagSize = Math.min(element.width, element.height) * 0.5
+
+          // 绘制路径:从左上角开始,顺时针方向,左上和右下为圆角
+          round2DiagRect.setAttribute(
+            'd',
+            `M${round2DiagSize},0 ` +
+              `L${element.width},0 ` +
+              `L${element.width},${element.height - round2DiagSize} ` +
+              `Q${element.width},${element.height} ${
+                element.width - round2DiagSize
+              },${element.height} ` +
+              `L0,${element.height} ` +
+              `L0,${round2DiagSize} ` +
+              `Q0,0 ${round2DiagSize},0 ` +
+              `Z`
+          )
+
+          // 设置填充色
+          if (element.fill && element.fill.type === 'color') {
+            round2DiagRect.setAttribute(
+              'fill',
+              element.fill.value || 'transparent'
+            )
+          } else {
+            round2DiagRect.setAttribute('fill', 'transparent')
+          }
+
+          // 设置边框
+          if (element.borderWidth > 0) {
+            round2DiagRect.setAttribute(
+              'stroke',
+              element.borderColor || '#000'
+            )
+            round2DiagRect.setAttribute(
+              'stroke-width',
+              element.borderWidth || 1
+            )
+
+            // 处理虚线边框
+            if (
+              element.borderType === 'dotted' ||
+              element.borderType === 'dashed'
+            ) {
+              if (element.borderStrokeDasharray) {
+                round2DiagRect.setAttribute(
+                  'stroke-dasharray',
+                  element.borderStrokeDasharray
+                )
+              } else if (element.borderType === 'dotted') {
+                round2DiagRect.setAttribute('stroke-dasharray', '2, 2')
+                round2DiagRect.setAttribute('stroke-linecap', 'round')
+              } else if (element.borderType === 'dashed') {
+                round2DiagRect.setAttribute('stroke-dasharray', '6, 3')
+              }
+            }
+
+            round2DiagRect.setAttribute('vector-effect', 'non-scaling-stroke')
+            round2DiagRect.setAttribute(
+              'shape-rendering',
+              'geometricPrecision'
+            )
+          }
+
+          svg.appendChild(round2DiagRect)
+          break
+        case 'triangle':
+          const triangle = document.createElementNS(
+            'http://www.w3.org/2000/svg',
+            'polygon'
+          )
+          triangle.setAttribute(
+            'points',
+            `${element.width / 2},0 ${element.width},${element.height} 0,${
+              element.height
+            }`
+          )
+
+          // 设置填充色
+          if (element.fill && element.fill.type === 'color') {
+            triangle.setAttribute('fill', element.fill.value || 'transparent')
+          } else {
+            triangle.setAttribute('fill', 'transparent')
+          }
+
+          // 设置边框
+          if (element.borderWidth > 0) {
+            triangle.setAttribute('stroke', element.borderColor || '#000')
+            triangle.setAttribute('stroke-width', element.borderWidth || 1)
+
+            // 处理虚线边框
+            if (
+              element.borderType === 'dotted' ||
+              element.borderType === 'dashed'
+            ) {
+              if (element.borderStrokeDasharray) {
+                triangle.setAttribute(
+                  'stroke-dasharray',
+                  element.borderStrokeDasharray
+                )
+              } else if (element.borderType === 'dotted') {
+                triangle.setAttribute('stroke-dasharray', '1, 3')
+              } else if (element.borderType === 'dashed') {
+                triangle.setAttribute('stroke-dasharray', '5, 5')
+              }
+            }
+          }
+
+          svg.appendChild(triangle)
+          break
+        case 'custom':
+          const customPath = document.createElementNS(
+            'http://www.w3.org/2000/svg',
+            'path'
+          )
+
+          // 直接使用传入的 path 数据
+          customPath.setAttribute('d', element.path || '')
+
+          // 设置填充色
+          if (element.fill && element.fill.type === 'color') {
+            customPath.setAttribute(
+              'fill',
+              element.fill.value || 'transparent'
+            )
+          } else {
+            customPath.setAttribute('fill', 'transparent')
+          }
+
+          // 设置边框
+          if (element.borderWidth > 0) {
+            customPath.setAttribute('stroke', element.borderColor || '#000')
+            customPath.setAttribute('stroke-width', element.borderWidth || 1)
+
+            // 处理虚线边框
+            if (
+              element.borderType === 'dotted' ||
+              element.borderType === 'dashed'
+            ) {
+              if (element.borderStrokeDasharray) {
+                customPath.setAttribute(
+                  'stroke-dasharray',
+                  element.borderStrokeDasharray
+                )
+              } else if (element.borderType === 'dotted') {
+                customPath.setAttribute('stroke-dasharray', '2, 2')
+                customPath.setAttribute('stroke-linecap', 'round')
+              } else if (element.borderType === 'dashed') {
+                customPath.setAttribute('stroke-dasharray', '6, 3')
+              }
+            }
+
+            customPath.setAttribute('vector-effect', 'non-scaling-stroke')
+            customPath.setAttribute('shape-rendering', 'geometricPrecision')
+          }
+
+          svg.appendChild(customPath)
+          break
+        case 'teardrop':
+          const teardrop = document.createElementNS(
+            'http://www.w3.org/2000/svg',
+            'path'
+          )
+
+          // 计算泪珠形状的参数 - 调整为正确的泪滴形状
+          const tdCenterX = element.width / 2
+          const tdCenterY = element.height / 2
+          const tdRadiusX = element.width * 0.5
+          const tdRadiusY = element.height * 0.5
+
+          // 绘制泪珠形状:根据第二张图的正确形状
+          teardrop.setAttribute(
+            'd',
+            `M${tdCenterX},${element.height} ` +
+              `C${tdCenterX - tdRadiusX * 0.8},${element.height * 0.7} ` +
+              `${tdCenterX - tdRadiusX},${element.height * 0.4} ` +
+              `${tdCenterX},${element.height * 0.1} ` +
+              `C${tdCenterX + tdRadiusX},${element.height * 0.4} ` +
+              `${tdCenterX + tdRadiusX * 0.8},${element.height * 0.7} ` +
+              `${tdCenterX},${element.height} ` +
+              `Z`
+          )
+
+          // 设置填充色
+          if (element.fill && element.fill.type === 'color') {
+            teardrop.setAttribute('fill', element.fill.value || 'transparent')
+          } else {
+            teardrop.setAttribute('fill', 'transparent')
+          }
+
+          // 设置边框
+          if (element.borderWidth > 0) {
+            teardrop.setAttribute('stroke', element.borderColor || '#000')
+            teardrop.setAttribute('stroke-width', element.borderWidth || 1)
+
+            // 处理虚线边框
+            if (
+              element.borderType === 'dotted' ||
+              element.borderType === 'dashed'
+            ) {
+              if (element.borderStrokeDasharray) {
+                teardrop.setAttribute(
+                  'stroke-dasharray',
+                  element.borderStrokeDasharray
+                )
+              } else if (element.borderType === 'dotted') {
+                teardrop.setAttribute('stroke-dasharray', '2, 2')
+                teardrop.setAttribute('stroke-linecap', 'round')
+              } else if (element.borderType === 'dashed') {
+                teardrop.setAttribute('stroke-dasharray', '6, 3')
+              }
+            }
+
+            teardrop.setAttribute('vector-effect', 'non-scaling-stroke')
+            teardrop.setAttribute('shape-rendering', 'geometricPrecision')
+          }
+
+          svg.appendChild(teardrop)
+          break
+        case 'decagon':
+          const decagon = document.createElementNS(
+            'http://www.w3.org/2000/svg',
+            'polygon'
+          )
+
+          // 计算十边形的顶点
+          let decagonPoints = ''
+          for (let i = 0; i < 10; i++) {
+            const angle = (i * 2 * Math.PI) / 10 - Math.PI / 2 // 从顶部开始
+            const x = element.width / 2 + (element.width / 2) * Math.cos(angle)
+            const y =
+              element.height / 2 + (element.height / 2) * Math.sin(angle)
+            decagonPoints += `${x},${y} `
+          }
+          decagon.setAttribute('points', decagonPoints.trim())
+
+          // 设置填充色
+          if (element.fill && element.fill.type === 'color') {
+            decagon.setAttribute('fill', element.fill.value || 'transparent')
+          } else {
+            decagon.setAttribute('fill', 'transparent')
+          }
+
+          // 设置边框
+          if (element.borderWidth > 0) {
+            decagon.setAttribute('stroke', element.borderColor || '#000')
+            decagon.setAttribute('stroke-width', element.borderWidth || 1)
+
+            // 处理虚线边框
+            if (
+              element.borderType === 'dotted' ||
+              element.borderType === 'dashed'
+            ) {
+              if (element.borderStrokeDasharray) {
+                decagon.setAttribute(
+                  'stroke-dasharray',
+                  element.borderStrokeDasharray
+                )
+              } else if (element.borderType === 'dotted') {
+                decagon.setAttribute('stroke-dasharray', '1, 3')
+              } else if (element.borderType === 'dashed') {
+                decagon.setAttribute('stroke-dasharray', '5, 5')
+              }
+            }
+          }
+
+          svg.appendChild(decagon)
+          break
+        case 'pentagon':
+          const pentagon = document.createElementNS(
+            'http://www.w3.org/2000/svg',
+            'polygon'
+          )
+          pentagon.setAttribute(
+            'points',
+            `${element.width / 2},0 ${element.width},${
+              element.height * 0.38
+            } ` +
+              `${element.width * 0.82},${element.height} ${
+                element.width * 0.18
+              },${element.height} ` +
+              `0,${element.height * 0.38}`
+          )
+
+          // 设置填充色
+          if (element.fill && element.fill.type === 'color') {
+            pentagon.setAttribute('fill', element.fill.value || 'transparent')
+          } else {
+            pentagon.setAttribute('fill', 'transparent')
+          }
+
+          // 设置边框
+          if (element.borderWidth > 0) {
+            pentagon.setAttribute('stroke', element.borderColor || '#000')
+            pentagon.setAttribute('stroke-width', element.borderWidth || 1)
+
+            // 处理虚线边框
+            if (
+              element.borderType === 'dotted' ||
+              element.borderType === 'dashed'
+            ) {
+              if (element.borderStrokeDasharray) {
+                pentagon.setAttribute(
+                  'stroke-dasharray',
+                  element.borderStrokeDasharray
+                )
+              } else if (element.borderType === 'dotted') {
+                pentagon.setAttribute('stroke-dasharray', '1, 3')
+              } else if (element.borderType === 'dashed') {
+                pentagon.setAttribute('stroke-dasharray', '5, 5')
+              }
+            }
+          }
+
+          svg.appendChild(pentagon)
+          break
+        case 'pie':
+          const pie = document.createElementNS(
+            'http://www.w3.org/2000/svg',
+            'path'
+          )
+
+          // 使用提供的路径数据,但保留元素的宽高比例
+          const centerX = element.width / 2
+          const centerY = element.height / 2
+          const radius2 = Math.min(element.width, element.height) / 2
+
+          // 绘制饼图,使用类似提供的路径数据
+          pie.setAttribute(
+            'd',
+            `M${centerX},${centerY} ` +
+              `L${centerX * 1.85},${centerY * 1.52} ` +
+              `A${radius2},${radius2} 0 1,1 ${centerX * 1.56},${
+                centerY * 0.17
+              } ` +
+              `Z`
+          )
+
+          // 设置填充色
+          if (element.fill && element.fill.type === 'color') {
+            pie.setAttribute('fill', element.fill.value || 'transparent')
+          } else {
+            pie.setAttribute('fill', 'transparent')
+          }
+
+          // 设置边框
+          if (element.borderWidth > 0) {
+            pie.setAttribute('stroke', element.borderColor || '#000')
+            pie.setAttribute('stroke-width', element.borderWidth || 1)
+
+            // 处理虚线边框
+            if (
+              element.borderType === 'dotted' ||
+              element.borderType === 'dashed'
+            ) {
+              if (element.borderStrokeDasharray) {
+                pie.setAttribute(
+                  'stroke-dasharray',
+                  element.borderStrokeDasharray
+                )
+              } else if (element.borderType === 'dotted') {
+                pie.setAttribute('stroke-dasharray', '2, 2')
+                pie.setAttribute('stroke-linecap', 'round')
+              } else if (element.borderType === 'dashed') {
+                pie.setAttribute('stroke-dasharray', '6, 3')
+              }
+            }
+
+            pie.setAttribute('vector-effect', 'non-scaling-stroke')
+            pie.setAttribute('shape-rendering', 'geometricPrecision')
+          }
+
+          svg.appendChild(pie)
+          break
+        case 'chord':
+          const chord = document.createElementNS(
+            'http://www.w3.org/2000/svg',
+            'path'
+          )
+
+          // 计算弦形的参数
+          const chordRadiusX = element.width * 0.4
+          const chordRadiusY = element.height * 0.4
+          // 绘制弦形 - 使用更大的圆弧和不同的起始/结束点
+          chord.setAttribute(
+            'd',
+            `M${element.width * 0.4},${element.height * 0.9} ` +
+              `A ${chordRadiusX} ${chordRadiusY} 0 1 1 ${element.width * 0.8},${
+                element.height * 0.5
+              } ` +
+              `Z`
+          )
+
+          // 设置填充色
+          if (element.fill && element.fill.type === 'color') {
+            chord.setAttribute('fill', element.fill.value || 'transparent')
+          } else {
+            chord.setAttribute('fill', 'transparent')
+          }
+
+          // 设置边框
+          if (element.borderWidth > 0) {
+            chord.setAttribute('stroke', element.borderColor || '#000')
+            chord.setAttribute('stroke-width', element.borderWidth || 1)
+
+            // 处理虚线边框
+            if (
+              element.borderType === 'dotted' ||
+              element.borderType === 'dashed'
+            ) {
+              if (element.borderStrokeDasharray) {
+                chord.setAttribute(
+                  'stroke-dasharray',
+                  element.borderStrokeDasharray
+                )
+              } else if (element.borderType === 'dotted') {
+                chord.setAttribute('stroke-dasharray', '2, 2')
+                chord.setAttribute('stroke-linecap', 'round')
+              } else if (element.borderType === 'dashed') {
+                chord.setAttribute('stroke-dasharray', '6, 3')
+              }
+            }
+
+            chord.setAttribute('vector-effect', 'non-scaling-stroke')
+            chord.setAttribute('shape-rendering', 'geometricPrecision')
+          }
+
+          svg.appendChild(chord)
+          break
+        case 'heptagon':
+          const heptagon = document.createElementNS(
+            'http://www.w3.org/2000/svg',
+            'polygon'
+          )
+
+          // 计算七边形的顶点
+          let heptagonPoints = ''
+          for (let i = 0; i < 7; i++) {
+            const angle = (i * 2 * Math.PI) / 7 - Math.PI / 2 // 从顶部开始
+            const x = element.width / 2 + (element.width / 2) * Math.cos(angle)
+            const y =
+              element.height / 2 + (element.height / 2) * Math.sin(angle)
+            heptagonPoints += `${x},${y} `
+          }
+          heptagon.setAttribute('points', heptagonPoints.trim())
+
+          // 设置填充色
+          if (element.fill && element.fill.type === 'color') {
+            heptagon.setAttribute('fill', element.fill.value || 'transparent')
+          } else {
+            heptagon.setAttribute('fill', 'transparent')
+          }
+
+          // 设置边框
+          if (element.borderWidth > 0) {
+            heptagon.setAttribute('stroke', element.borderColor || '#000')
+            heptagon.setAttribute('stroke-width', element.borderWidth || 1)
+
+            // 处理虚线边框
+            if (
+              element.borderType === 'dotted' ||
+              element.borderType === 'dashed'
+            ) {
+              if (element.borderStrokeDasharray) {
+                heptagon.setAttribute(
+                  'stroke-dasharray',
+                  element.borderStrokeDasharray
+                )
+              } else if (element.borderType === 'dotted') {
+                heptagon.setAttribute('stroke-dasharray', '1, 3')
+              } else if (element.borderType === 'dashed') {
+                heptagon.setAttribute('stroke-dasharray', '5, 5')
+              }
+            }
+          }
+
+          svg.appendChild(heptagon)
+          break
+        case 'hexagon':
+          const hexagon = document.createElementNS(
+            'http://www.w3.org/2000/svg',
+            'polygon'
+          )
+          hexagon.setAttribute(
+            'points',
+            `${element.width * 0.25},0 ${element.width * 0.75},0 ${
+              element.width
+            },${element.height * 0.5} ` +
+              `${element.width * 0.75},${element.height} ${
+                element.width * 0.25
+              },${element.height} 0,${element.height * 0.5}`
+          )
+
+          // 设置填充色
+          if (element.fill && element.fill.type === 'color') {
+            hexagon.setAttribute('fill', element.fill.value || 'transparent')
+          } else {
+            hexagon.setAttribute('fill', 'transparent')
+          }
+
+          // 设置边框
+          if (element.borderWidth > 0) {
+            hexagon.setAttribute('stroke', element.borderColor || '#000')
+            hexagon.setAttribute('stroke-width', element.borderWidth || 1)
+
+            // 处理虚线边框
+            if (
+              element.borderType === 'dotted' ||
+              element.borderType === 'dashed'
+            ) {
+              if (element.borderStrokeDasharray) {
+                hexagon.setAttribute(
+                  'stroke-dasharray',
+                  element.borderStrokeDasharray
+                )
+              } else if (element.borderType === 'dotted') {
+                hexagon.setAttribute('stroke-dasharray', '1, 3')
+              } else if (element.borderType === 'dashed') {
+                hexagon.setAttribute('stroke-dasharray', '5, 5')
+              }
+            }
+          }
+
+          svg.appendChild(hexagon)
+          break
+
+        case 'octagon':
+          const octagon = document.createElementNS(
+            'http://www.w3.org/2000/svg',
+            'polygon'
+          )
+          octagon.setAttribute(
+            'points',
+            `${element.width * 0.3},0 ${element.width * 0.7},0 ${
+              element.width
+            },${element.height * 0.3} ` +
+              `${element.width},${element.height * 0.7} ${
+                element.width * 0.7
+              },${element.height} ` +
+              `${element.width * 0.3},${element.height} 0,${
+                element.height * 0.7
+              } 0,${element.height * 0.3}`
+          )
+
+          // 设置填充色
+          if (element.fill && element.fill.type === 'color') {
+            octagon.setAttribute('fill', element.fill.value || 'transparent')
+          } else {
+            octagon.setAttribute('fill', 'transparent')
+          }
+
+          // 设置边框
+          if (element.borderWidth > 0) {
+            octagon.setAttribute('stroke', element.borderColor || '#000')
+            octagon.setAttribute('stroke-width', element.borderWidth || 1)
+
+            // 处理虚线边框
+            if (
+              element.borderType === 'dotted' ||
+              element.borderType === 'dashed'
+            ) {
+              if (element.borderStrokeDasharray) {
+                octagon.setAttribute(
+                  'stroke-dasharray',
+                  element.borderStrokeDasharray
+                )
+              } else if (element.borderType === 'dotted') {
+                octagon.setAttribute('stroke-dasharray', '1, 3')
+              } else if (element.borderType === 'dashed') {
+                octagon.setAttribute('stroke-dasharray', '5, 5')
+              }
+            }
+          }
+
+          svg.appendChild(octagon)
+          break
+
+        case 'trapezoid':
+          const trapezoid = document.createElementNS(
+            'http://www.w3.org/2000/svg',
+            'polygon'
+          )
+          trapezoid.setAttribute(
+            'points',
+            `${element.width * 0.2},0 ${element.width * 0.8},0 ${
+              element.width
+            },${element.height} 0,${element.height}`
+          )
+
+          // 设置填充色
+          if (element.fill && element.fill.type === 'color') {
+            trapezoid.setAttribute('fill', element.fill.value || 'transparent')
+          } else {
+            trapezoid.setAttribute('fill', 'transparent')
+          }
+
+          // 设置边框
+          if (element.borderWidth > 0) {
+            trapezoid.setAttribute('stroke', element.borderColor || '#000')
+            trapezoid.setAttribute('stroke-width', element.borderWidth || 1)
+
+            // 处理虚线边框
+            if (
+              element.borderType === 'dotted' ||
+              element.borderType === 'dashed'
+            ) {
+              if (element.borderStrokeDasharray) {
+                trapezoid.setAttribute(
+                  'stroke-dasharray',
+                  element.borderStrokeDasharray
+                )
+              } else if (element.borderType === 'dotted') {
+                trapezoid.setAttribute('stroke-dasharray', '1, 3')
+              } else if (element.borderType === 'dashed') {
+                trapezoid.setAttribute('stroke-dasharray', '5, 5')
+              }
+            }
+          }
+
+          svg.appendChild(trapezoid)
+          break
+
+        case 'diamond':
+          const diamond = document.createElementNS(
+            'http://www.w3.org/2000/svg',
+            'polygon'
+          )
+          diamond.setAttribute(
+            'points',
+            `${element.width / 2},0 ${element.width},${element.height / 2} ${
+              element.width / 2
+            },${element.height} 0,${element.height / 2}`
+          )
+
+          // 设置填充色
+          if (element.fill && element.fill.type === 'color') {
+            diamond.setAttribute('fill', element.fill.value || 'transparent')
+          } else {
+            diamond.setAttribute('fill', 'transparent')
+          }
+
+          // 设置边框
+          if (element.borderWidth > 0) {
+            diamond.setAttribute('stroke', element.borderColor || '#000')
+            diamond.setAttribute('stroke-width', element.borderWidth || 1)
+
+            // 处理虚线边框
+            if (
+              element.borderType === 'dotted' ||
+              element.borderType === 'dashed'
+            ) {
+              if (element.borderStrokeDasharray) {
+                diamond.setAttribute(
+                  'stroke-dasharray',
+                  element.borderStrokeDasharray
+                )
+              } else if (element.borderType === 'dotted') {
+                diamond.setAttribute('stroke-dasharray', '1, 3')
+              } else if (element.borderType === 'dashed') {
+                diamond.setAttribute('stroke-dasharray', '5, 5')
+              }
+            }
+          }
+
+          svg.appendChild(diamond)
+          break
+        case 'dodecagon':
+          const dodecagon = document.createElementNS(
+            'http://www.w3.org/2000/svg',
+            'polygon'
+          )
+
+          // 计算十二边形的顶点
+          let dodecagonPoints = ''
+          for (let i = 0; i < 12; i++) {
+            const angle = (i * 2 * Math.PI) / 12 - Math.PI / 2 // 从顶部开始
+            const x = element.width / 2 + (element.width / 2) * Math.cos(angle)
+            const y =
+              element.height / 2 + (element.height / 2) * Math.sin(angle)
+            dodecagonPoints += `${x},${y} `
+          }
+          dodecagon.setAttribute('points', dodecagonPoints.trim())
+
+          // 设置填充色
+          if (element.fill && element.fill.type === 'color') {
+            dodecagon.setAttribute('fill', element.fill.value || 'transparent')
+          } else {
+            dodecagon.setAttribute('fill', 'transparent')
+          }
+
+          // 设置边框
+          if (element.borderWidth > 0) {
+            dodecagon.setAttribute('stroke', element.borderColor || '#000')
+            dodecagon.setAttribute('stroke-width', element.borderWidth || 1)
+
+            // 处理虚线边框
+            if (
+              element.borderType === 'dotted' ||
+              element.borderType === 'dashed'
+            ) {
+              if (element.borderStrokeDasharray) {
+                dodecagon.setAttribute(
+                  'stroke-dasharray',
+                  element.borderStrokeDasharray
+                )
+              } else if (element.borderType === 'dotted') {
+                dodecagon.setAttribute('stroke-dasharray', '1, 3')
+              } else if (element.borderType === 'dashed') {
+                dodecagon.setAttribute('stroke-dasharray', '5, 5')
+              }
+            }
+          }
+
+          svg.appendChild(dodecagon)
+          break
+        case 'halfFrame':
+          // 创建外框和内框
+          const outerRect2 = document.createElementNS(
+            'http://www.w3.org/2000/svg',
+            'path'
+          )
+          const innerRect2 = document.createElementNS(
+            'http://www.w3.org/2000/svg',
+            'path'
+          )
+
+          // 计算内框的边距
+          const frameWidth2 = element.width / 9
+
+          // 绘制外框的左边和上边
+          outerRect2.setAttribute(
+            'd',
+            `M0,${element.height} L0,0 L${element.width},0`
+          )
+
+          // 绘制内框的左边和上边,注意起点位置调整
+          innerRect2.setAttribute(
+            'd',
+            `M${frameWidth2},${element.height - frameWidth2} ` +
+              `L${frameWidth2},${frameWidth2} ` +
+              `L${element.width - frameWidth2},${frameWidth2}`
+          )
+
+          // 设置填充色
+          if (element.fill && element.fill.type === 'color') {
+            // 创建一个填充用的路径
+            const fillPath = document.createElementNS(
+              'http://www.w3.org/2000/svg',
+              'path'
+            )
+            fillPath.setAttribute(
+              'd',
+              `M${frameWidth2},${frameWidth2} ` +
+                `L${element.width - frameWidth2},${frameWidth2} ` +
+                `L${element.width},0 ` +
+                `L0,0 ` +
+                `L0,${element.height} ` +
+                `L${frameWidth2},${element.height - frameWidth2} Z`
+            )
+            fillPath.setAttribute('fill', element.fill.value || 'transparent')
+            svg.appendChild(fillPath)
+
+            outerRect2.setAttribute('fill', 'none')
+            innerRect2.setAttribute('fill', 'none')
+          } else {
+            outerRect2.setAttribute('fill', 'none')
+            innerRect2.setAttribute('fill', 'none')
+          }
+
+          // 设置边框
+          if (element.borderWidth > 0) {
+            const borderColor = element.borderColor || '#000'
+            const borderWidth = element.borderWidth || 1
+
+            outerRect2.setAttribute('stroke', borderColor)
+            outerRect2.setAttribute('stroke-width', borderWidth)
+            innerRect2.setAttribute('stroke', borderColor)
+            innerRect2.setAttribute('stroke-width', borderWidth)
+
+            // 处理虚线边框
+            if (
+              element.borderType === 'dotted' ||
+              element.borderType === 'dashed'
+            ) {
+              if (element.borderStrokeDasharray) {
+                const dashArray = element.borderStrokeDasharray
+                outerRect2.setAttribute('stroke-dasharray', dashArray)
+                innerRect2.setAttribute('stroke-dasharray', dashArray)
+              } else if (element.borderType === 'dotted') {
+                outerRect2.setAttribute('stroke-dasharray', '1, 3')
+                innerRect2.setAttribute('stroke-dasharray', '1, 3')
+              } else if (element.borderType === 'dashed') {
+                outerRect2.setAttribute('stroke-dasharray', '5, 5')
+                innerRect2.setAttribute('stroke-dasharray', '5, 5')
+              }
+            }
+          }
+
+          svg.appendChild(outerRect2)
+          svg.appendChild(innerRect2)
+          break
+        case 'corner':
+          const corner = document.createElementNS(
+            'http://www.w3.org/2000/svg',
+            'path'
+          )
+
+          corner.setAttribute(
+            'd',
+            `M0,0 ` +
+              `L0,${element.height} ` +
+              `L${element.width},${element.height} ` +
+              `L${element.width},${element.height * 0.6} ` +
+              `L${element.width * 0.4},${element.height * 0.6} ` +
+              `L${element.width * 0.4},0 Z`
+          )
+
+          // 设置填充色
+          if (element.fill && element.fill.type === 'color') {
+            corner.setAttribute('fill', element.fill.value || 'transparent')
+          } else {
+            corner.setAttribute('fill', 'transparent')
+          }
+
+          // 设置边框
+          if (element.borderWidth > 0) {
+            corner.setAttribute('stroke', element.borderColor || '#000')
+            corner.setAttribute('stroke-width', element.borderWidth || 1)
+
+            // 处理虚线边框
+            if (
+              element.borderType === 'dotted' ||
+              element.borderType === 'dashed'
+            ) {
+              if (element.borderStrokeDasharray) {
+                corner.setAttribute(
+                  'stroke-dasharray',
+                  element.borderStrokeDasharray
+                )
+              } else if (element.borderType === 'dotted') {
+                corner.setAttribute('stroke-dasharray', '1, 3')
+              } else if (element.borderType === 'dashed') {
+                corner.setAttribute('stroke-dasharray', '5, 5')
+              }
+            }
+          }
+
+          svg.appendChild(corner)
+          break
+        case 'diagStripe':
+          const diagStripe = document.createElementNS(
+            'http://www.w3.org/2000/svg',
+            'path'
+          )
+
+          // 绘制斜纹路径
+          diagStripe.setAttribute(
+            'd',
+            `M${element.width * 0.4},0 ` +
+              `L${element.width},0 ` +
+              `L0,${element.height} ` +
+              `L0,${element.height * 0.4} Z`
+          )
+
+          // 设置填充色
+          if (element.fill && element.fill.type === 'color') {
+            diagStripe.setAttribute(
+              'fill',
+              element.fill.value || 'transparent'
+            )
+          } else {
+            diagStripe.setAttribute('fill', 'transparent')
+          }
+
+          // 设置边框
+          if (element.borderWidth > 0) {
+            diagStripe.setAttribute('stroke', element.borderColor || '#000')
+            diagStripe.setAttribute('stroke-width', element.borderWidth || 1)
+            diagStripe.setAttribute('stroke-linejoin', 'round')
+
+            // 处理虚线边框
+            if (
+              element.borderType === 'dotted' ||
+              element.borderType === 'dashed'
+            ) {
+              if (element.borderStrokeDasharray) {
+                diagStripe.setAttribute(
+                  'stroke-dasharray',
+                  element.borderStrokeDasharray
+                )
+              } else if (element.borderType === 'dotted') {
+                diagStripe.setAttribute('stroke-dasharray', '1, 3')
+              } else if (element.borderType === 'dashed') {
+                diagStripe.setAttribute('stroke-dasharray', '5, 5')
+              }
+            }
+          }
+
+          svg.appendChild(diagStripe)
+          break
+        case 'plus':
+          const plus = document.createElementNS(
+            'http://www.w3.org/2000/svg',
+            'polygon'
+          )
+          plus.setAttribute(
+            'points',
+            `${element.width * 0.4},0 ` +
+              `${element.width * 0.6},0 ` +
+              `${element.width * 0.6},${element.height * 0.4} ` +
+              `${element.width},${element.height * 0.4} ` +
+              `${element.width},${element.height * 0.6} ` +
+              `${element.width * 0.6},${element.height * 0.6} ` +
+              `${element.width * 0.6},${element.height} ` +
+              `${element.width * 0.4},${element.height} ` +
+              `${element.width * 0.4},${element.height * 0.6} ` +
+              `0,${element.height * 0.6} ` +
+              `0,${element.height * 0.4} ` +
+              `${element.width * 0.4},${element.height * 0.4}`
+          )
+
+          // 设置填充色
+          if (element.fill && element.fill.type === 'color') {
+            plus.setAttribute('fill', element.fill.value || 'transparent')
+          } else {
+            plus.setAttribute('fill', 'transparent')
+          }
+
+          // 设置边框
+          if (element.borderWidth > 0) {
+            plus.setAttribute('stroke', element.borderColor || '#000')
+            plus.setAttribute('stroke-width', element.borderWidth || 1)
+
+            // 处理虚线边框
+            if (
+              element.borderType === 'dotted' ||
+              element.borderType === 'dashed'
+            ) {
+              if (element.borderStrokeDasharray) {
+                plus.setAttribute(
+                  'stroke-dasharray',
+                  element.borderStrokeDasharray
+                )
+              } else if (element.borderType === 'dotted') {
+                plus.setAttribute('stroke-dasharray', '1, 3')
+              } else if (element.borderType === 'dashed') {
+                plus.setAttribute('stroke-dasharray', '5, 5')
+              }
+            }
+          }
+
+          svg.appendChild(plus)
+          break
+        case 'can':
+          const can = document.createElementNS(
+            'http://www.w3.org/2000/svg',
+            'g'
+          )
+
+          // 创建圆柱体的主体部分
+          const cylinderBody = document.createElementNS(
+            'http://www.w3.org/2000/svg',
+            'path'
+          )
+          cylinderBody.setAttribute(
+            'd',
+            `M0,${element.height * 0.15} ` +
+              `A${element.width / 2},${element.height * 0.15} 0 0 1 ${
+                element.width
+              },${element.height * 0.15} ` +
+              `V${element.height * 0.85} ` +
+              `A${element.width / 2},${element.height * 0.15} 0 0 1 0,${
+                element.height * 0.85
+              } Z`
+          )
+
+          // 创建顶部椭圆
+          const topEllipse = document.createElementNS(
+            'http://www.w3.org/2000/svg',
+            'ellipse'
+          )
+          topEllipse.setAttribute('cx', element.width / 2)
+          topEllipse.setAttribute('cy', element.height * 0.15)
+          topEllipse.setAttribute('rx', element.width / 2)
+          topEllipse.setAttribute('ry', element.height * 0.15)
+
+          // 设置填充色
+          if (element.fill && element.fill.type === 'color') {
+            cylinderBody.setAttribute(
+              'fill',
+              element.fill.value || 'transparent'
+            )
+            topEllipse.setAttribute(
+              'fill',
+              element.fill.value || 'transparent'
+            )
+          } else {
+            cylinderBody.setAttribute('fill', 'transparent')
+            topEllipse.setAttribute('fill', 'transparent')
+          }
+
+          // 设置边框
+          if (element.borderWidth > 0) {
+            const borderColor = element.borderColor || '#000'
+            const borderWidth = element.borderWidth || 1
+
+            cylinderBody.setAttribute('stroke', borderColor)
+            cylinderBody.setAttribute('stroke-width', borderWidth)
+            topEllipse.setAttribute('stroke', borderColor)
+            topEllipse.setAttribute('stroke-width', borderWidth)
+
+            // 处理虚线边框
+            if (
+              element.borderType === 'dotted' ||
+              element.borderType === 'dashed'
+            ) {
+              if (element.borderStrokeDasharray) {
+                const dashArray = element.borderStrokeDasharray
+                cylinderBody.setAttribute('stroke-dasharray', dashArray)
+                topEllipse.setAttribute('stroke-dasharray', dashArray)
+              } else if (element.borderType === 'dotted') {
+                cylinderBody.setAttribute('stroke-dasharray', '1, 3')
+                topEllipse.setAttribute('stroke-dasharray', '1, 3')
+              } else if (element.borderType === 'dashed') {
+                cylinderBody.setAttribute('stroke-dasharray', '5, 5')
+                topEllipse.setAttribute('stroke-dasharray', '5, 5')
+              }
+            }
+          }
+
+          can.appendChild(cylinderBody)
+          can.appendChild(topEllipse)
+          svg.appendChild(can)
+          break
+        case 'cube':
+          const cube = document.createElementNS(
+            'http://www.w3.org/2000/svg',
+            'g'
+          )
+
+          // 计算关键点坐标
+          const offset = element.width * 0.2
+          const frontX = offset
+          const frontY = offset
+          const frontW = element.width - offset
+          const frontH = element.height
+
+          // 设置立方体的三个面的路径
+          const frontFace = document.createElementNS(
+            'http://www.w3.org/2000/svg',
+            'path'
+          )
+          frontFace.setAttribute(
+            'd',
+            `M${frontX},${frontY} ` +
+              `L${frontW},${frontY} ` +
+              `L${frontW},${frontH} ` +
+              `L${frontX},${frontH} Z`
+          )
+
+          const rightFace = document.createElementNS(
+            'http://www.w3.org/2000/svg',
+            'path'
+          )
+          rightFace.setAttribute(
+            'd',
+            `M${frontW},${frontY} ` +
+              `L${element.width},0 ` +
+              `L${element.width},${element.height - offset} ` +
+              `L${frontW},${frontH} Z`
+          )
+
+          const topFace = document.createElementNS(
+            'http://www.w3.org/2000/svg',
+            'path'
+          )
+          topFace.setAttribute(
+            'd',
+            `M${frontX},${frontY} ` +
+              `L${frontW - offset - frontX},0 ` + // 修改这里:改为从(0,0)开始
+              `L${element.width - offset},0 ` + // 修改这里:使用 offset 来计算右上角的位置
+              `L${element.width},0 ` +
+              `L${frontW},${frontY} Z`
+          )
+
+          // 设置填充色
+          if (element.fill && element.fill.type === 'color') {
+            const fillColor = element.fill.value || 'transparent'
+            frontFace.setAttribute('fill', fillColor)
+            rightFace.setAttribute(
+              'fill',
+              this.adjustBrightness(fillColor, 0.8)
+            )
+            topFace.setAttribute('fill', this.adjustBrightness(fillColor, 1.2))
+          } else {
+            frontFace.setAttribute('fill', 'transparent')
+            rightFace.setAttribute('fill', 'transparent')
+            topFace.setAttribute('fill', 'transparent')
+          }
+
+          // 设置边框
+          if (element.borderWidth > 0) {
+            const borderColor = element.borderColor || '#000'
+            const borderWidth = element.borderWidth || 1
+
+            frontFace.setAttribute('stroke', borderColor)
+            frontFace.setAttribute('stroke-width', borderWidth)
+            rightFace.setAttribute('stroke', borderColor)
+            rightFace.setAttribute('stroke-width', borderWidth)
+            topFace.setAttribute('stroke', borderColor)
+            topFace.setAttribute('stroke-width', borderWidth)
+
+            if (
+              element.borderType === 'dotted' ||
+              element.borderType === 'dashed'
+            ) {
+              const dashArray =
+                element.borderStrokeDasharray ||
+                (element.borderType === 'dotted' ? '1, 3' : '5, 5')
+              frontFace.setAttribute('stroke-dasharray', dashArray)
+              rightFace.setAttribute('stroke-dasharray', dashArray)
+              topFace.setAttribute('stroke-dasharray', dashArray)
+            }
+          }
+
+          // 按正确的顺序添加面(从后到前)
+          cube.appendChild(rightFace)
+          cube.appendChild(topFace)
+          cube.appendChild(frontFace)
+          svg.appendChild(cube)
+          break
+        case 'bevel':
+          const bevel = document.createElementNS(
+            'http://www.w3.org/2000/svg',
+            'g'
+          )
+
+          // 计算关键点坐标
+          const bevelOffset = element.width * 0.2
+          const smallRectX = bevelOffset
+          const smallRectY = bevelOffset
+          const smallRectW = element.width - 2 * bevelOffset
+          const smallRectH = element.height - 2 * bevelOffset
+
+          // 绘制顶部小矩形
+          const topRect = document.createElementNS(
+            'http://www.w3.org/2000/svg',
+            'rect'
+          )
+          topRect.setAttribute('x', smallRectX)
+          topRect.setAttribute('y', smallRectY)
+          topRect.setAttribute('width', smallRectW)
+          topRect.setAttribute('height', smallRectH)
+
+          // 绘制四个梯形
+          const topTrapezoid = document.createElementNS(
+            'http://www.w3.org/2000/svg',
+            'path'
+          )
+          topTrapezoid.setAttribute(
+            'd',
+            `M0,0 L${element.width},0 L${
+              element.width - bevelOffset
+            },${bevelOffset} L${bevelOffset},${bevelOffset} Z`
+          )
+
+          const bottomTrapezoid = document.createElementNS(
+            'http://www.w3.org/2000/svg',
+            'path'
+          )
+          bottomTrapezoid.setAttribute(
+            'd',
+            `M${bevelOffset},${element.height - bevelOffset} L${
+              element.width - bevelOffset
+            },${element.height - bevelOffset} L${element.width},${
+              element.height
+            } L0,${element.height} Z`
+          )
+
+          const leftTrapezoid = document.createElementNS(
+            'http://www.w3.org/2000/svg',
+            'path'
+          )
+          leftTrapezoid.setAttribute(
+            'd',
+            `M0,0 L${bevelOffset},${bevelOffset} L${bevelOffset},${
+              element.height - bevelOffset
+            } L0,${element.height} Z`
+          )
+
+          const rightTrapezoid = document.createElementNS(
+            'http://www.w3.org/2000/svg',
+            'path'
+          )
+          rightTrapezoid.setAttribute(
+            'd',
+            `M${element.width},0 L${element.width},${element.height} L${
+              element.width - bevelOffset
+            },${element.height - bevelOffset} L${
+              element.width - bevelOffset
+            },${bevelOffset} Z`
+          )
+
+          // 设置填充色
+          if (element.fill && element.fill.type === 'color') {
+            const fillColor = element.fill.value || 'transparent'
+            topRect.setAttribute('fill', fillColor)
+            topTrapezoid.setAttribute(
+              'fill',
+              this.adjustBrightness(fillColor, 1.2)
+            )
+            bottomTrapezoid.setAttribute(
+              'fill',
+              this.adjustBrightness(fillColor, 0.8)
+            )
+            leftTrapezoid.setAttribute(
+              'fill',
+              this.adjustBrightness(fillColor, 0.9)
+            )
+            rightTrapezoid.setAttribute(
+              'fill',
+              this.adjustBrightness(fillColor, 0.7)
+            )
+          } else {
+            topRect.setAttribute('fill', 'transparent')
+            topTrapezoid.setAttribute('fill', 'transparent')
+            bottomTrapezoid.setAttribute('fill', 'transparent')
+            leftTrapezoid.setAttribute('fill', 'transparent')
+            rightTrapezoid.setAttribute('fill', 'transparent')
+          }
+
+          // 设置边框
+          if (element.borderWidth > 0) {
+            const borderColor = element.borderColor || '#000'
+            const borderWidth = element.borderWidth || 1
+            const parts = [
+              topRect,
+              topTrapezoid,
+              bottomTrapezoid,
+              leftTrapezoid,
+              rightTrapezoid
+            ]
+
+            parts.forEach((part) => {
+              part.setAttribute('stroke', borderColor)
+              part.setAttribute('stroke-width', borderWidth)
+
+              if (
+                element.borderType === 'dotted' ||
+                element.borderType === 'dashed'
+              ) {
+                const dashArray =
+                  element.borderStrokeDasharray ||
+                  (element.borderType === 'dotted' ? '1, 3' : '5, 5')
+                part.setAttribute('stroke-dasharray', dashArray)
+              }
+            })
+          }
+
+          // 按正确的顺序添加面(从后到前)
+          bevel.appendChild(bottomTrapezoid)
+          bevel.appendChild(leftTrapezoid)
+          bevel.appendChild(rightTrapezoid)
+          bevel.appendChild(topTrapezoid)
+          bevel.appendChild(topRect)
+          svg.appendChild(bevel)
+          break
+        case 'donut':
+          const donut = document.createElementNS(
+            'http://www.w3.org/2000/svg',
+            'g'
+          )
+
+          // 外圆
+          const outerCircle = document.createElementNS(
+            'http://www.w3.org/2000/svg',
+            'circle'
+          )
+          outerCircle.setAttribute('cx', element.width / 2)
+          outerCircle.setAttribute('cy', element.height / 2)
+          outerCircle.setAttribute(
+            'r',
+            Math.min(element.width, element.height) / 2
+          )
+
+          // 内圆
+          const innerCircle = document.createElementNS(
+            'http://www.w3.org/2000/svg',
+            'circle'
+          )
+          innerCircle.setAttribute('cx', element.width / 2)
+          innerCircle.setAttribute('cy', element.height / 2)
+          innerCircle.setAttribute(
+            'r',
+            Math.min(element.width, element.height) / 4
+          )
+
+          // 设置填充色
+          if (element.fill && element.fill.type === 'color') {
+            outerCircle.setAttribute(
+              'fill',
+              element.fill.value || 'transparent'
+            )
+            innerCircle.setAttribute('fill', 'white') // 内圆为白色,形成镂空效果
+          } else {
+            outerCircle.setAttribute('fill', 'transparent')
+            innerCircle.setAttribute('fill', 'white')
+          }
+
+          // 设置边框
+          if (element.borderWidth > 0) {
+            const borderColor = element.borderColor || '#000'
+            const borderWidth = element.borderWidth || 1
+
+            outerCircle.setAttribute('stroke', borderColor)
+            outerCircle.setAttribute('stroke-width', borderWidth)
+            innerCircle.setAttribute('stroke', borderColor)
+            innerCircle.setAttribute('stroke-width', borderWidth)
+
+            if (
+              element.borderType === 'dotted' ||
+              element.borderType === 'dashed'
+            ) {
+              const dashArray =
+                element.borderStrokeDasharray ||
+                (element.borderType === 'dotted' ? '1, 3' : '5, 5')
+              outerCircle.setAttribute('stroke-dasharray', dashArray)
+              innerCircle.setAttribute('stroke-dasharray', dashArray)
+            }
+          }
+
+          donut.appendChild(outerCircle)
+          donut.appendChild(innerCircle)
+          svg.appendChild(donut)
+          break
+        case 'noSmoking':
+          const noSmoking = document.createElementNS(
+            'http://www.w3.org/2000/svg',
+            'g'
+          )
+
+          // 创建路径
+          const path = document.createElementNS(
+            'http://www.w3.org/2000/svg',
+            'path'
+          )
+          path.setAttribute(
+            'd',
+            'M0,70 A77,70 0 1,1 0,71 Z M123.80284467265982,99.39738105155146 A57.5386,50.5386 0 0 0 45.160427644134444,27.90427466198293 Z M30.197155327340184,40.602618948448544 A57.5386,50.5386 0 0 0 108.83957235586556,112.09572533801708 Z'
+          )
+          path.setAttribute('fill', 'rgba(255,217,102,1)')
+          path.setAttribute('stroke', 'rgba(23,44,81,1)')
+          path.setAttribute('stroke-width', '1px')
+          path.setAttribute('stroke-dasharray', '')
+          path.setAttribute('stroke-linecap', 'butt')
+          path.setAttribute('stroke-linejoin', 'round')
+
+          noSmoking.appendChild(path)
+          svg.appendChild(noSmoking)
+          break
+        case 'rightArrow':
+          const rightArrow = document.createElementNS(
+            'http://www.w3.org/2000/svg',
+            'polygon'
+          )
+          rightArrow.setAttribute(
+            'points',
+            `0,${element.height * 0.3} ${element.width * 0.7},${
+              element.height * 0.3
+            } ` +
+              `${element.width * 0.7},0 ${element.width},${
+                element.height * 0.5
+              } ` +
+              `${element.width * 0.7},${element.height} ${
+                element.width * 0.7
+              },${element.height * 0.7} ` +
+              `0,${element.height * 0.7}`
+          )
+
+          // 设置填充色
+          if (element.fill && element.fill.type === 'color') {
+            rightArrow.setAttribute(
+              'fill',
+              element.fill.value || 'transparent'
+            )
+          } else {
+            rightArrow.setAttribute('fill', 'transparent')
+          }
+
+          // 设置边框
+          if (element.borderWidth > 0) {
+            rightArrow.setAttribute('stroke', element.borderColor || '#000')
+            rightArrow.setAttribute('stroke-width', element.borderWidth || 1)
+
+            // 处理虚线边框
+            if (
+              element.borderType === 'dotted' ||
+              element.borderType === 'dashed'
+            ) {
+              if (element.borderStrokeDasharray) {
+                rightArrow.setAttribute(
+                  'stroke-dasharray',
+                  element.borderStrokeDasharray
+                )
+              } else if (element.borderType === 'dotted') {
+                rightArrow.setAttribute('stroke-dasharray', '1, 3')
+              } else if (element.borderType === 'dashed') {
+                rightArrow.setAttribute('stroke-dasharray', '5, 5')
+              }
+            }
+          }
+
+          svg.appendChild(rightArrow)
+          break
+        case 'leftArrow':
+          const leftArrow = document.createElementNS(
+            'http://www.w3.org/2000/svg',
+            'polygon'
+          )
+          leftArrow.setAttribute(
+            'points',
+            `${element.width},${element.height * 0.3} ${element.width * 0.3},${
+              element.height * 0.3
+            } ` +
+              `${element.width * 0.3},0 0,${element.height * 0.5} ` +
+              `${element.width * 0.3},${element.height} ${
+                element.width * 0.3
+              },${element.height * 0.7} ` +
+              `${element.width},${element.height * 0.7}`
+          )
+
+          // 设置填充色
+          if (element.fill && element.fill.type === 'color') {
+            leftArrow.setAttribute('fill', element.fill.value || 'transparent')
+          } else {
+            leftArrow.setAttribute('fill', 'transparent')
+          }
+
+          // 设置边框
+          if (element.borderWidth > 0) {
+            leftArrow.setAttribute('stroke', element.borderColor || '#000')
+            leftArrow.setAttribute('stroke-width', element.borderWidth || 1)
+
+            // 处理虚线边框
+            if (
+              element.borderType === 'dotted' ||
+              element.borderType === 'dashed'
+            ) {
+              if (element.borderStrokeDasharray) {
+                leftArrow.setAttribute(
+                  'stroke-dasharray',
+                  element.borderStrokeDasharray
+                )
+              } else if (element.borderType === 'dotted') {
+                leftArrow.setAttribute('stroke-dasharray', '1, 3')
+              } else if (element.borderType === 'dashed') {
+                leftArrow.setAttribute('stroke-dasharray', '5, 5')
+              }
+            }
+          }
+
+          svg.appendChild(leftArrow)
+          break
+        case 'upArrow':
+          const upArrow = document.createElementNS(
+            'http://www.w3.org/2000/svg',
+            'polygon'
+          )
+          upArrow.setAttribute(
+            'points',
+            `${element.width * 0.3},${element.height} ${element.width * 0.3},${
+              element.height * 0.3
+            } ` +
+              `0,${element.height * 0.3} ${element.width * 0.5},0 ` +
+              `${element.width},${element.height * 0.3} ${
+                element.width * 0.7
+              },${element.height * 0.3} ` +
+              `${element.width * 0.7},${element.height}`
+          )
+
+          // 设置填充色
+          if (element.fill && element.fill.type === 'color') {
+            upArrow.setAttribute('fill', element.fill.value || 'transparent')
+          } else {
+            upArrow.setAttribute('fill', 'transparent')
+          }
+
+          // 设置边框
+          if (element.borderWidth > 0) {
+            upArrow.setAttribute('stroke', element.borderColor || '#000')
+            upArrow.setAttribute('stroke-width', element.borderWidth || 1)
+
+            // 处理虚线边框
+            if (
+              element.borderType === 'dotted' ||
+              element.borderType === 'dashed'
+            ) {
+              if (element.borderStrokeDasharray) {
+                upArrow.setAttribute(
+                  'stroke-dasharray',
+                  element.borderStrokeDasharray
+                )
+              } else if (element.borderType === 'dotted') {
+                upArrow.setAttribute('stroke-dasharray', '1, 3')
+              } else if (element.borderType === 'dashed') {
+                upArrow.setAttribute('stroke-dasharray', '5, 5')
+              }
+            }
+          }
+
+          svg.appendChild(upArrow)
+          break
+        case 'downArrow':
+          const downArrow = document.createElementNS(
+            'http://www.w3.org/2000/svg',
+            'polygon'
+          )
+          downArrow.setAttribute(
+            'points',
+            `${element.width * 0.3},0 ${element.width * 0.3},${
+              element.height * 0.7
+            } ` +
+              `0,${element.height * 0.7} ${element.width * 0.5},${
+                element.height
+              } ` +
+              `${element.width},${element.height * 0.7} ${
+                element.width * 0.7
+              },${element.height * 0.7} ` +
+              `${element.width * 0.7},0`
+          )
+
+          // 设置填充色
+          if (element.fill && element.fill.type === 'color') {
+            downArrow.setAttribute('fill', element.fill.value || 'transparent')
+          } else {
+            downArrow.setAttribute('fill', 'transparent')
+          }
+
+          // 设置边框
+          if (element.borderWidth > 0) {
+            downArrow.setAttribute('stroke', element.borderColor || '#000')
+            downArrow.setAttribute('stroke-width', element.borderWidth || 1)
+
+            // 处理虚线边框
+            if (
+              element.borderType === 'dotted' ||
+              element.borderType === 'dashed'
+            ) {
+              if (element.borderStrokeDasharray) {
+                downArrow.setAttribute(
+                  'stroke-dasharray',
+                  element.borderStrokeDasharray
+                )
+              } else if (element.borderType === 'dotted') {
+                downArrow.setAttribute('stroke-dasharray', '1, 3')
+              } else if (element.borderType === 'dashed') {
+                downArrow.setAttribute('stroke-dasharray', '5, 5')
+              }
+            }
+          }
+
+          svg.appendChild(downArrow)
+          break
+        case 'leftRightArrow':
+          const leftRightArrow = document.createElementNS(
+            'http://www.w3.org/2000/svg',
+            'polygon'
+          )
+          leftRightArrow.setAttribute(
+            'points',
+            `0,${element.height * 0.5} ${element.width * 0.2},${
+              element.height * 0.2
+            } ` +
+              `${element.width * 0.2},${element.height * 0.4} ${
+                element.width * 0.8
+              },${element.height * 0.4} ` +
+              `${element.width * 0.8},${element.height * 0.2} ${
+                element.width
+              },${element.height * 0.5} ` +
+              `${element.width * 0.8},${element.height * 0.8} ${
+                element.width * 0.8
+              },${element.height * 0.6} ` +
+              `${element.width * 0.2},${element.height * 0.6} ${
+                element.width * 0.2
+              },${element.height * 0.8}`
+          )
+
+          // 设置填充色
+          if (element.fill && element.fill.type === 'color') {
+            leftRightArrow.setAttribute(
+              'fill',
+              element.fill.value || 'transparent'
+            )
+          } else {
+            leftRightArrow.setAttribute('fill', 'transparent')
+          }
+
+          // 设置边框
+          if (element.borderWidth > 0) {
+            leftRightArrow.setAttribute(
+              'stroke',
+              element.borderColor || '#000'
+            )
+            leftRightArrow.setAttribute(
+              'stroke-width',
+              element.borderWidth || 1
+            )
+
+            // 处理虚线边框
+            if (
+              element.borderType === 'dotted' ||
+              element.borderType === 'dashed'
+            ) {
+              if (element.borderStrokeDasharray) {
+                leftRightArrow.setAttribute(
+                  'stroke-dasharray',
+                  element.borderStrokeDasharray
+                )
+              } else if (element.borderType === 'dotted') {
+                leftRightArrow.setAttribute('stroke-dasharray', '1, 3')
+              } else if (element.borderType === 'dashed') {
+                leftRightArrow.setAttribute('stroke-dasharray', '5, 5')
+              }
+            }
+          }
+
+          svg.appendChild(leftRightArrow)
+          break
+        case 'upDownArrow':
+          const upDownArrow = document.createElementNS(
+            'http://www.w3.org/2000/svg',
+            'polygon'
+          )
+          upDownArrow.setAttribute(
+            'points',
+            `${element.width * 0.5},0 ${element.width * 0.3},${
+              element.height * 0.2
+            } ` +
+              `${element.width * 0.4},${element.height * 0.2} ${
+                element.width * 0.4
+              },${element.height * 0.8} ` +
+              `${element.width * 0.3},${element.height * 0.8} ${
+                element.width * 0.5
+              },${element.height} ` +
+              `${element.width * 0.7},${element.height * 0.8} ${
+                element.width * 0.6
+              },${element.height * 0.8} ` +
+              `${element.width * 0.6},${element.height * 0.2} ${
+                element.width * 0.7
+              },${element.height * 0.2}`
+          )
+
+          // 设置填充色
+          if (element.fill && element.fill.type === 'color') {
+            upDownArrow.setAttribute(
+              'fill',
+              element.fill.value || 'transparent'
+            )
+          } else {
+            upDownArrow.setAttribute('fill', 'transparent')
+          }
+
+          // 设置边框
+          if (element.borderWidth > 0) {
+            upDownArrow.setAttribute('stroke', element.borderColor || '#000')
+            upDownArrow.setAttribute('stroke-width', element.borderWidth || 1)
+
+            // 处理虚线边框
+            if (
+              element.borderType === 'dotted' ||
+              element.borderType === 'dashed'
+            ) {
+              if (element.borderStrokeDasharray) {
+                upDownArrow.setAttribute(
+                  'stroke-dasharray',
+                  element.borderStrokeDasharray
+                )
+              } else if (element.borderType === 'dotted') {
+                upDownArrow.setAttribute('stroke-dasharray', '1, 3')
+              } else if (element.borderType === 'dashed') {
+                upDownArrow.setAttribute('stroke-dasharray', '5, 5')
+              }
+            }
+          }
+
+          svg.appendChild(upDownArrow)
+          break
+        case 'quadArrow':
+          const quadArrow = document.createElementNS(
+            'http://www.w3.org/2000/svg',
+            'path'
+          )
+
+          // 计算缩放比例
+          const scale = Math.min(element.width / 167, element.height / 121)
+          const offsetX = (element.width - 167 * scale) / 2
+          const offsetY = (element.height - 121 * scale) / 2
+
+          // 构建路径数据
+          const pathData = `
+            M${offsetX},${60.5 * scale + offsetY}
+            L${27.225 * scale + offsetX},${33.275 * scale + offsetY}
+            L${27.225 * scale + offsetX},${46.8875 * scale + offsetY}
+            L${69.8875 * scale + offsetX},${46.8875 * scale + offsetY}
+            L${69.8875 * scale + offsetX},${27.225 * scale + offsetY}
+            L${56.275 * scale + offsetX},${27.225 * scale + offsetY}
+            L${83.5 * scale + offsetX},${offsetY}
+            L${110.725 * scale + offsetX},${27.225 * scale + offsetY}
+            L${97.1125 * scale + offsetX},${27.225 * scale + offsetY}
+            L${97.1125 * scale + offsetX},${46.8875 * scale + offsetY}
+            L${139.775 * scale + offsetX},${46.8875 * scale + offsetY}
+            L${139.775 * scale + offsetX},${33.275 * scale + offsetY}
+            L${167 * scale + offsetX},${60.5 * scale + offsetY}
+            L${139.775 * scale + offsetX},${87.725 * scale + offsetY}
+            L${139.775 * scale + offsetX},${74.1125 * scale + offsetY}
+            L${97.1125 * scale + offsetX},${74.1125 * scale + offsetY}
+            L${97.1125 * scale + offsetX},${93.775 * scale + offsetY}
+            L${110.725 * scale + offsetX},${93.775 * scale + offsetY}
+            L${83.5 * scale + offsetX},${121 * scale + offsetY}
+            L${56.275 * scale + offsetX},${93.775 * scale + offsetY}
+            L${69.8875 * scale + offsetX},${93.775 * scale + offsetY}
+            L${69.8875 * scale + offsetX},${74.1125 * scale + offsetY}
+            L${27.225 * scale + offsetX},${74.1125 * scale + offsetY}
+            L${27.225 * scale + offsetX},${87.725 * scale + offsetY}
+            Z`
+
+          quadArrow.setAttribute('d', pathData)
+
+          // 设置填充色
+          if (element.fill && element.fill.type === 'color') {
+            quadArrow.setAttribute('fill', element.fill.value || 'transparent')
+          } else {
+            quadArrow.setAttribute('fill', 'transparent')
+          }
+
+          // 设置边框
+          if (element.borderWidth > 0) {
+            quadArrow.setAttribute('stroke', element.borderColor || '#000')
+            quadArrow.setAttribute('stroke-width', element.borderWidth || 1)
+            quadArrow.setAttribute('stroke-linecap', 'butt')
+            quadArrow.setAttribute('stroke-linejoin', 'round')
+
+            // 处理虚线边框
+            if (
+              element.borderType === 'dotted' ||
+              element.borderType === 'dashed'
+            ) {
+              if (element.borderStrokeDasharray) {
+                quadArrow.setAttribute(
+                  'stroke-dasharray',
+                  element.borderStrokeDasharray
+                )
+              } else if (element.borderType === 'dotted') {
+                quadArrow.setAttribute('stroke-dasharray', '1, 3')
+              } else if (element.borderType === 'dashed') {
+                quadArrow.setAttribute('stroke-dasharray', '5, 5')
+              }
+            }
+          }
+
+          svg.appendChild(quadArrow)
+          break
+        case 'leftRightUpArrow':
+          const leftRightUpArrow = document.createElementNS(
+            'http://www.w3.org/2000/svg',
+            'path'
+          )
+
+          // 计算缩放比例
+          const scale2 = Math.min(element.width / 167, element.height / 121)
+          const offsetX2 = (element.width - 167 * scale2) / 2
+          const offsetY2 = (element.height - 121 * scale2) / 2
+
+          // 构建路径数据
+          const pathData2 = `
+            M${offsetX2},${60.5 * scale2 + offsetY2}
+            L${27.225 * scale2 + offsetX2},${33.275 * scale2 + offsetY2}
+            L${27.225 * scale2 + offsetX2},${46.8875 * scale2 + offsetY2}
+            L${69.8875 * scale2 + offsetX2},${46.8875 * scale2 + offsetY2}
+            L${69.8875 * scale2 + offsetX2},${27.225 * scale2 + offsetY2}
+            L${56.275 * scale2 + offsetX2},${27.225 * scale2 + offsetY2}
+            L${83.5 * scale2 + offsetX2},${offsetY2}
+            L${110.725 * scale2 + offsetX2},${27.225 * scale2 + offsetY2}
+            L${97.1125 * scale2 + offsetX2},${27.225 * scale2 + offsetY2}
+            L${97.1125 * scale2 + offsetX2},${46.8875 * scale2 + offsetY2}
+            L${139.775 * scale2 + offsetX2},${46.8875 * scale2 + offsetY2}
+            L${139.775 * scale2 + offsetX2},${33.275 * scale2 + offsetY2}
+            L${167 * scale2 + offsetX2},${60.5 * scale2 + offsetY2}
+            L${139.775 * scale2 + offsetX2},${87.725 * scale2 + offsetY2}
+            L${139.775 * scale2 + offsetX2},${74.1125 * scale2 + offsetY2}
+            L${97.1125 * scale2 + offsetX2},${74.1125 * scale2 + offsetY2}
+            L${69.8875 * scale2 + offsetX2},${74.1125 * scale2 + offsetY2}
+            L${27.225 * scale2 + offsetX2},${74.1125 * scale2 + offsetY2}
+            L${27.225 * scale2 + offsetX2},${87.725 * scale2 + offsetY2}
+            Z`
+
+          leftRightUpArrow.setAttribute('d', pathData2)
+
+          // 设置填充色
+          if (element.fill && element.fill.type === 'color') {
+            leftRightUpArrow.setAttribute(
+              'fill',
+              element.fill.value || 'transparent'
+            )
+          } else {
+            leftRightUpArrow.setAttribute('fill', 'transparent')
+          }
+
+          // 设置边框
+          if (element.borderWidth > 0) {
+            leftRightUpArrow.setAttribute(
+              'stroke',
+              element.borderColor || '#000'
+            )
+            leftRightUpArrow.setAttribute(
+              'stroke-width',
+              element.borderWidth || 1
+            )
+            leftRightUpArrow.setAttribute('stroke-linecap', 'butt')
+            leftRightUpArrow.setAttribute('stroke-linejoin', 'round')
+
+            // 处理虚线边框
+            if (
+              element.borderType === 'dotted' ||
+              element.borderType === 'dashed'
+            ) {
+              if (element.borderStrokeDasharray) {
+                leftRightUpArrow.setAttribute(
+                  'stroke-dasharray',
+                  element.borderStrokeDasharray
+                )
+              } else if (element.borderType === 'dotted') {
+                leftRightUpArrow.setAttribute('stroke-dasharray', '1, 3')
+              } else if (element.borderType === 'dashed') {
+                leftRightUpArrow.setAttribute('stroke-dasharray', '5, 5')
+              }
+            }
+          }
+
+          svg.appendChild(leftRightUpArrow)
+          break
+        case 'bentArrow':
+          const bentArrow = document.createElementNS(
+            'http://www.w3.org/2000/svg',
+            'path'
+          )
+
+          // 构建路径数据
+          const pathData3 = `
+            M0,${element.height * 0.88}
+            L0,${element.height * 0.495}
+            A${element.width * 0.385} ${element.height * 0.385} 0 0 1 ${
+  element.width * 0.385
+} ${element.height * 0.11}
+            L${element.width * 0.67},${element.height * 0.11}
+            L${element.width * 0.67},0
+            L${element.width * 0.89},${element.height * 0.22}
+            L${element.width * 0.67},${element.height * 0.44}
+            L${element.width * 0.67},${element.height * 0.33}
+            L${element.width * 0.385},${element.height * 0.33}
+            A${element.width * 0.165} ${element.height * 0.165} 0 0 0 ${
+  element.width * 0.22
+} ${element.height * 0.495}
+            L${element.width * 0.22},${element.height * 0.88}
+            Z`
+
+          bentArrow.setAttribute('d', pathData3)
+
+          // 设置填充色
+          if (element.fill && element.fill.type === 'color') {
+            bentArrow.setAttribute('fill', element.fill.value || 'transparent')
+          } else {
+            bentArrow.setAttribute('fill', 'transparent')
+          }
+
+          // 设置边框
+          if (element.borderWidth > 0) {
+            bentArrow.setAttribute('stroke', element.borderColor || '#000')
+            bentArrow.setAttribute('stroke-width', element.borderWidth || 1)
+            bentArrow.setAttribute('stroke-linecap', 'butt')
+            bentArrow.setAttribute('stroke-linejoin', 'round')
+
+            // 处理虚线边框
+            if (
+              element.borderType === 'dotted' ||
+              element.borderType === 'dashed'
+            ) {
+              if (element.borderStrokeDasharray) {
+                bentArrow.setAttribute(
+                  'stroke-dasharray',
+                  element.borderStrokeDasharray
+                )
+              } else if (element.borderType === 'dotted') {
+                bentArrow.setAttribute('stroke-dasharray', '1, 3')
+              } else if (element.borderType === 'dashed') {
+                bentArrow.setAttribute('stroke-dasharray', '5, 5')
+              }
+            }
+          }
+
+          svg.appendChild(bentArrow)
+          break
+        case 'parallelogram':
+          const parallelogram = document.createElementNS(
+            'http://www.w3.org/2000/svg',
+            'polygon'
+          )
+          parallelogram.setAttribute(
+            'points',
+            `${element.width * 0.25},0 ${element.width},0 ${
+              element.width * 0.75
+            },${element.height} 0,${element.height}`
+          )
+
+          // 设置填充色
+          if (element.fill && element.fill.type === 'color') {
+            parallelogram.setAttribute(
+              'fill',
+              element.fill.value || 'transparent'
+            )
+          } else {
+            parallelogram.setAttribute('fill', 'transparent')
+          }
+
+          // 设置边框
+          if (element.borderWidth > 0) {
+            parallelogram.setAttribute('stroke', element.borderColor || '#000')
+            parallelogram.setAttribute(
+              'stroke-width',
+              element.borderWidth || 1
+            )
+
+            // 处理虚线边框
+            if (
+              element.borderType === 'dotted' ||
+              element.borderType === 'dashed'
+            ) {
+              if (element.borderStrokeDasharray) {
+                parallelogram.setAttribute(
+                  'stroke-dasharray',
+                  element.borderStrokeDasharray
+                )
+              } else if (element.borderType === 'dotted') {
+                parallelogram.setAttribute('stroke-dasharray', '1, 3')
+              } else if (element.borderType === 'dashed') {
+                parallelogram.setAttribute('stroke-dasharray', '5, 5')
+              }
+            }
+          }
+
+          svg.appendChild(parallelogram)
+          break
+        case 'uturnArrow':
+          const uturnArrow = document.createElementNS(
+            'http://www.w3.org/2000/svg',
+            'path'
+          )
+
+          // 构建路径数据
+          const pathDataUturn = `
+            M0,${element.height * 0.745}
+            L0,${element.height * 0.179375}
+            A${element.width * 0.179375} ${element.height * 0.179375} 0 0 1 ${
+  element.width * 0.179375
+},0
+            L${element.width * 0.179375},0
+            A${element.width * 0.179375} ${element.height * 0.179375} 0 0 1 ${
+  element.width * 0.359375
+},${element.height * 0.179375}
+            L${element.width * 0.359375},${element.height * 0.205}
+            L${element.width * 0.41},${element.height * 0.205}
+            L${element.width * 0.3075},${element.height * 0.3075}
+            L${element.width * 0.205},${element.height * 0.205}
+            L${element.width * 0.25625},${element.height * 0.205}
+            L${element.width * 0.25625},${element.height * 0.179375}
+            A${element.width * 0.076875} ${element.height * 0.076875} 0 0 0 ${
+  element.width * 0.179375
+},${element.height * 0.1025}
+            L${element.width * 0.179375},${element.height * 0.1025}
+            A${element.width * 0.076875} ${element.height * 0.076875} 0 0 0 ${
+  element.width * 0.1025
+},${element.height * 0.179375}
+            L${element.width * 0.1025},${element.height * 0.745}
+            Z`
+
+          uturnArrow.setAttribute('d', pathDataUturn)
+
+          // 设置填充色
+          if (element.fill && element.fill.type === 'color') {
+            uturnArrow.setAttribute(
+              'fill',
+              element.fill.value || 'transparent'
+            )
+          } else {
+            uturnArrow.setAttribute('fill', 'transparent')
+          }
+
+          // 设置边框
+          if (element.borderWidth > 0) {
+            uturnArrow.setAttribute('stroke', element.borderColor || '#000')
+            uturnArrow.setAttribute('stroke-width', element.borderWidth || 1)
+            uturnArrow.setAttribute('stroke-linecap', 'butt')
+            uturnArrow.setAttribute('stroke-linejoin', 'round')
+
+            // 处理虚线边框
+            if (
+              element.borderType === 'dotted' ||
+              element.borderType === 'dashed'
+            ) {
+              if (element.borderStrokeDasharray) {
+                uturnArrow.setAttribute(
+                  'stroke-dasharray',
+                  element.borderStrokeDasharray
+                )
+              } else if (element.borderType === 'dotted') {
+                uturnArrow.setAttribute('stroke-dasharray', '1, 3')
+              } else if (element.borderType === 'dashed') {
+                uturnArrow.setAttribute('stroke-dasharray', '5, 5')
+              }
+            }
+          }
+
+          svg.appendChild(uturnArrow)
+          break
+        case 'leftUpArrow':
+          const leftUpArrow = document.createElementNS(
+            'http://www.w3.org/2000/svg',
+            'path'
+          )
+
+          // 使用提供的路径数据
+          const pathDataLeftUp = `
+            M0,${element.height * 0.65}
+            L${element.width * 0.15},${element.height * 0.4}
+            L${element.width * 0.15},${element.height * 0.55}
+            L${element.width * 0.5},${element.height * 0.55}
+            L${element.width * 0.5},${element.height * 0.1}
+            L${element.width * 0.35},${element.height * 0.1}
+            L${element.width * 0.65},0
+            L${element.width * 0.85},${element.height * 0.1}
+            L${element.width * 0.75},${element.height * 0.1}
+            L${element.width * 0.75},${element.height * 0.85}
+            L${element.width * 0.15},${element.height * 0.85}
+            L${element.width * 0.15},${element.height * 1}
+            Z`
+
+          leftUpArrow.setAttribute('d', pathDataLeftUp)
+
+          // 设置填充色
+          if (element.fill && element.fill.type === 'color') {
+            leftUpArrow.setAttribute(
+              'fill',
+              element.fill.value || 'transparent'
+            )
+          } else {
+            leftUpArrow.setAttribute('fill', 'transparent')
+          }
+
+          // 设置边框
+          if (element.borderWidth > 0) {
+            leftUpArrow.setAttribute('stroke', element.borderColor || '#000')
+            leftUpArrow.setAttribute('stroke-width', element.borderWidth || 1)
+            leftUpArrow.setAttribute('stroke-linecap', 'butt')
+            leftUpArrow.setAttribute('stroke-linejoin', 'round')
+
+            // 处理虚线边框
+            if (
+              element.borderType === 'dotted' ||
+              element.borderType === 'dashed'
+            ) {
+              if (element.borderStrokeDasharray) {
+                leftUpArrow.setAttribute(
+                  'stroke-dasharray',
+                  element.borderStrokeDasharray
+                )
+              } else if (element.borderType === 'dotted') {
+                leftUpArrow.setAttribute('stroke-dasharray', '1, 3')
+              } else if (element.borderType === 'dashed') {
+                leftUpArrow.setAttribute('stroke-dasharray', '5, 5')
+              }
+            }
+          }
+
+          svg.appendChild(leftUpArrow)
+          break
+        case 'bentUpArrow':
+          const bentUpArrow = document.createElementNS(
+            'http://www.w3.org/2000/svg',
+            'path'
+          )
+
+          // 构建路径数据,去掉左侧箭头
+          // 使用提供的路径数据
+          const pathDataBentUp = `
+            M0,${element.height * 0.89}
+            L0,${element.height * 0.7}
+            L${element.width * 0.575},${element.height * 0.7}
+            L${element.width * 0.575},${element.height * 0.2}
+            L${element.width * 0.46},${element.height * 0.2}
+            L${element.width * 0.69},0
+            L${element.width * 0.92},${element.height * 0.2}
+            L${element.width * 0.805},${element.height * 0.2}
+            L${element.width * 0.805},${element.height * 0.89}
+            Z`
+
+          bentUpArrow.setAttribute('d', pathDataBentUp)
+
+          // 设置填充色
+          if (element.fill && element.fill.type === 'color') {
+            bentUpArrow.setAttribute(
+              'fill',
+              element.fill.value || 'transparent'
+            )
+          } else {
+            bentUpArrow.setAttribute('fill', 'transparent')
+          }
+
+          // 设置边框
+          if (element.borderWidth > 0) {
+            bentUpArrow.setAttribute('stroke', element.borderColor || '#000')
+            bentUpArrow.setAttribute('stroke-width', element.borderWidth || 1)
+            bentUpArrow.setAttribute('stroke-linecap', 'butt')
+            bentUpArrow.setAttribute('stroke-linejoin', 'round')
+
+            // 处理虚线边框
+            if (
+              element.borderType === 'dotted' ||
+              element.borderType === 'dashed'
+            ) {
+              if (element.borderStrokeDasharray) {
+                bentUpArrow.setAttribute(
+                  'stroke-dasharray',
+                  element.borderStrokeDasharray
+                )
+              } else if (element.borderType === 'dotted') {
+                bentUpArrow.setAttribute('stroke-dasharray', '1, 3')
+              } else if (element.borderType === 'dashed') {
+                bentUpArrow.setAttribute('stroke-dasharray', '5, 5')
+              }
+            }
+          }
+
+          svg.appendChild(bentUpArrow)
+          break
+        case 'curvedRightArrow':
+          const curvedRightArrow = document.createElementNS(
+            'http://www.w3.org/2000/svg',
+            'path'
+          )
+
+          // 使用提供的路径数据
+          const pathDataCurvedRight = `
+            M${element.width},0
+            A${element.width} ${element.height * 0.4} 0 0 0 0 ${
+  element.height * 0.4
+}
+            L0,${element.height * 0.5}
+            A${element.width} ${element.height * 0.4} 0 0 1 ${element.width} ${
+  element.height * 0.15
+}
+            Z
+            M0,${element.height * 0.4}
+            A${element.width} ${element.height * 0.4} 0 0 0 ${
+  element.width * 0.8
+} ${element.height * 0.8}
+            L${element.width * 0.8},${element.height * 0.75}
+            L${element.width},${element.height * 0.875}
+            L${element.width * 0.8},${element.height * 1}
+            L${element.width * 0.8},${element.height * 0.95}
+            A${element.width} ${element.height * 0.4} 0 0 1 0 ${
+  element.height * 0.5
+}
+            Z`
+
+          curvedRightArrow.setAttribute('d', pathDataCurvedRight)
+
+          // 设置填充色
+          if (element.fill && element.fill.type === 'color') {
+            curvedRightArrow.setAttribute(
+              'fill',
+              element.fill.value || 'transparent'
+            )
+          } else {
+            curvedRightArrow.setAttribute('fill', 'transparent')
+          }
+
+          // 设置边框
+          if (element.borderWidth > 0) {
+            curvedRightArrow.setAttribute(
+              'stroke',
+              element.borderColor || '#000'
+            )
+            curvedRightArrow.setAttribute(
+              'stroke-width',
+              element.borderWidth || 1
+            )
+            curvedRightArrow.setAttribute('stroke-linecap', 'butt')
+            curvedRightArrow.setAttribute('stroke-linejoin', 'round')
+
+            // 处理虚线边框
+            if (
+              element.borderType === 'dotted' ||
+              element.borderType === 'dashed'
+            ) {
+              if (element.borderStrokeDasharray) {
+                curvedRightArrow.setAttribute(
+                  'stroke-dasharray',
+                  element.borderStrokeDasharray
+                )
+              } else if (element.borderType === 'dotted') {
+                curvedRightArrow.setAttribute('stroke-dasharray', '1, 3')
+              } else if (element.borderType === 'dashed') {
+                curvedRightArrow.setAttribute('stroke-dasharray', '5, 5')
+              }
+            }
+          }
+
+          svg.appendChild(curvedRightArrow)
+          break
+        case 'curvedLeftArrow':
+          const curvedLeftArrow = document.createElementNS(
+            'http://www.w3.org/2000/svg',
+            'path'
+          )
+
+          // 定义左弧形箭头的路径数据
+          const pathDataCurvedLeft = `
+            M0,0
+            A${element.width} ${element.height * 0.4} 0 0 1 ${element.width} ${
+  element.height * 0.4
+}
+            L${element.width},${element.height * 0.55}
+            A${element.width} ${element.height * 0.4} 0 0 0 0 ${
+  element.height * 0.15
+}
+            Z
+            M0,${element.height * 0.85}
+            L${element.width * 0.25},${element.height * 0.65}
+            L${element.width * 0.25},${element.height * 0.75}
+            A${element.width} ${element.height * 0.4} 0 0 0 ${element.width} ${
+  element.height * 0.4
+}
+            L${element.width},${element.height * 0.5}
+            L${element.width},${element.height * 0.5}
+            A${element.width} ${element.height * 0.4} 0 0 1 ${
+  element.width * 0.25
+} ${element.height * 0.9}
+            L${element.width * 0.25},${element.height}
+            Z`
+
+          curvedLeftArrow.setAttribute('d', pathDataCurvedLeft)
+
+          // 设置填充色
+          if (element.fill && element.fill.type === 'color') {
+            curvedLeftArrow.setAttribute(
+              'fill',
+              element.fill.value || 'transparent'
+            )
+          } else {
+            curvedLeftArrow.setAttribute('fill', 'transparent')
+          }
+
+          // 设置边框
+          if (element.borderWidth > 0) {
+            curvedLeftArrow.setAttribute(
+              'stroke',
+              element.borderColor || '#000'
+            )
+            curvedLeftArrow.setAttribute(
+              'stroke-width',
+              element.borderWidth || 1
+            )
+            curvedLeftArrow.setAttribute('stroke-linecap', 'butt')
+            curvedLeftArrow.setAttribute('stroke-linejoin', 'round')
+
+            // 处理虚线边框
+            if (
+              element.borderType === 'dotted' ||
+              element.borderType === 'dashed'
+            ) {
+              if (element.borderStrokeDasharray) {
+                curvedLeftArrow.setAttribute(
+                  'stroke-dasharray',
+                  element.borderStrokeDasharray
+                )
+              } else if (element.borderType === 'dotted') {
+                curvedLeftArrow.setAttribute('stroke-dasharray', '1, 3')
+              } else if (element.borderType === 'dashed') {
+                curvedLeftArrow.setAttribute('stroke-dasharray', '5, 5')
+              }
+            }
+          }
+
+          svg.appendChild(curvedLeftArrow)
+          break
+        case 'curvedUpArrow':
+          const curvedUpArrow = document.createElementNS(
+            'http://www.w3.org/2000/svg',
+            'path'
+          )
+
+          // 定义上弧形箭头的路径数据
+          const pathDataCurvedUp = `
+            M${element.width * 0.905},0
+            L${element.width * 0.81},${element.height * 0.25}
+            L${element.width * 0.857},${element.height * 0.25}
+            A${element.width * 0.428} ${element.height} 0 0 1 ${
+  element.width * 0.428
+},${element.height}
+            L${element.width * 0.522},${element.height}
+            A${element.width * 0.428} ${element.height} 0 0 0 ${
+  element.width * 0.952
+},${element.height * 0.25}
+            L${element.width},${element.height * 0.25}
+            Z
+            M${element.width * 0.094},0
+            L0,0
+            A${element.width * 0.428} ${element.height} 0 0 0 ${
+  element.width * 0.428
+},${element.height}
+            L${element.width * 0.522},${element.height}
+            A${element.width * 0.428} ${element.height} 0 0 1 ${
+  element.width * 0.094
+},0
+            Z`
+
+          curvedUpArrow.setAttribute('d', pathDataCurvedUp)
+
+          // 设置填充色
+          if (element.fill && element.fill.type === 'color') {
+            curvedUpArrow.setAttribute(
+              'fill',
+              element.fill.value || 'transparent'
+            )
+          } else {
+            curvedUpArrow.setAttribute('fill', 'transparent')
+          }
+
+          // 设置边框
+          if (element.borderWidth > 0) {
+            curvedUpArrow.setAttribute('stroke', element.borderColor || '#000')
+            curvedUpArrow.setAttribute(
+              'stroke-width',
+              element.borderWidth || 1
+            )
+            curvedUpArrow.setAttribute('stroke-linecap', 'butt')
+            curvedUpArrow.setAttribute('stroke-linejoin', 'round')
+
+            // 处理虚线边框
+            if (
+              element.borderType === 'dotted' ||
+              element.borderType === 'dashed'
+            ) {
+              if (element.borderStrokeDasharray) {
+                curvedUpArrow.setAttribute(
+                  'stroke-dasharray',
+                  element.borderStrokeDasharray
+                )
+              } else if (element.borderType === 'dotted') {
+                curvedUpArrow.setAttribute('stroke-dasharray', '1, 3')
+              } else if (element.borderType === 'dashed') {
+                curvedUpArrow.setAttribute('stroke-dasharray', '5, 5')
+              }
+            }
+          }
+
+          svg.appendChild(curvedUpArrow)
+          break
+        case 'curvedDownArrow':
+          const curvedDownArrow = document.createElementNS(
+            'http://www.w3.org/2000/svg',
+            'path'
+          )
+
+          // 定义下弧形箭头的路径数据
+          const pathDataCurvedDown = `
+            M0,${element.height}
+            L${element.width * 0.16},${element.height}
+            A${element.width * 0.46} ${element.height} 0 0 1 ${
+  element.width * 0.62
+},0
+            L${element.width * 0.46},0
+            A${element.width * 0.46} ${element.height} 0 0 0 0,${element.height}
+            Z
+            M${element.width},${element.height}
+            L${element.width * 0.84},${element.height * 0.75}
+            L${element.width * 0.92},${element.height * 0.75}
+            A${element.width * 0.46} ${element.height} 0 0 0 ${
+  element.width * 0.46
+},0
+            L${element.width * 0.62},0
+            A${element.width * 0.46} ${element.height} 0 0 1 ${
+  element.width * 1.08
+},${element.height * 0.75}
+            L${element.width * 1.16},${element.height * 0.75}
+            Z`
+
+          curvedDownArrow.setAttribute('d', pathDataCurvedDown)
+
+          // 设置填充色
+          if (element.fill && element.fill.type === 'color') {
+            curvedDownArrow.setAttribute(
+              'fill',
+              element.fill.value || 'transparent'
+            )
+          } else {
+            curvedDownArrow.setAttribute('fill', 'transparent')
+          }
+
+          // 设置边框
+          if (element.borderWidth > 0) {
+            curvedDownArrow.setAttribute(
+              'stroke',
+              element.borderColor || '#000'
+            )
+            curvedDownArrow.setAttribute(
+              'stroke-width',
+              element.borderWidth || 1
+            )
+            curvedDownArrow.setAttribute('stroke-linecap', 'butt')
+            curvedDownArrow.setAttribute('stroke-linejoin', 'round')
+
+            // 处理虚线边框
+            if (
+              element.borderType === 'dotted' ||
+              element.borderType === 'dashed'
+            ) {
+              if (element.borderStrokeDasharray) {
+                curvedDownArrow.setAttribute(
+                  'stroke-dasharray',
+                  element.borderStrokeDasharray
+                )
+              } else if (element.borderType === 'dotted') {
+                curvedDownArrow.setAttribute('stroke-dasharray', '1, 3')
+              } else if (element.borderType === 'dashed') {
+                curvedDownArrow.setAttribute('stroke-dasharray', '5, 5')
+              }
+            }
+          }
+
+          svg.appendChild(curvedDownArrow)
+          break
+        case 'stripedRightArrow':
+          const stripedRightArrow = document.createElementNS(
+            'http://www.w3.org/2000/svg',
+            'g'
+          )
+
+          // 主箭头部分
+          const mainArrow = document.createElementNS(
+            'http://www.w3.org/2000/svg',
+            'path'
+          )
+          mainArrow.setAttribute(
+            'd',
+            `M${element.width * 0.062},${element.height * 0.25}
+            L${element.width * 0.8},${element.height * 0.25}
+            L${element.width * 0.8},0
+            L${element.width},${element.height * 0.5}
+            L${element.width * 0.8},${element.height}
+            L${element.width * 0.8},${element.height * 0.75}
+            L${element.width * 0.062},${element.height * 0.75}
+            Z`
+          )
+
+          // 第一条尾部条纹
+          const stripe1 = document.createElementNS(
+            'http://www.w3.org/2000/svg',
+            'path'
+          )
+          stripe1.setAttribute(
+            'd',
+            `M0,${element.height * 0.25}
+            L${element.width * 0.012},${element.height * 0.25}
+            L${element.width * 0.012},${element.height * 0.75}
+            L0,${element.height * 0.75}
+            Z`
+          )
+
+          // 第二条尾部条纹
+          const stripe2 = document.createElementNS(
+            'http://www.w3.org/2000/svg',
+            'path'
+          )
+          stripe2.setAttribute(
+            'd',
+            `M${element.width * 0.025},${element.height * 0.25}
+            L${element.width * 0.049},${element.height * 0.25}
+            L${element.width * 0.049},${element.height * 0.75}
+            L${element.width * 0.025},${element.height * 0.75}
+            Z`
+          )
+
+          // 设置填充色
+          if (element.fill && element.fill.type === 'color') {
+            mainArrow.setAttribute('fill', element.fill.value || 'transparent')
+            stripe1.setAttribute('fill', element.fill.value || 'transparent')
+            stripe2.setAttribute('fill', element.fill.value || 'transparent')
+          } else {
+            mainArrow.setAttribute('fill', 'transparent')
+            stripe1.setAttribute('fill', 'transparent')
+            stripe2.setAttribute('fill', 'transparent')
+          }
+
+          // 设置边框
+          if (element.borderWidth > 0) {
+            [mainArrow, stripe1, stripe2].forEach((path) => {
+              path.setAttribute('stroke', element.borderColor || '#000')
+              path.setAttribute('stroke-width', element.borderWidth || 1)
+              path.setAttribute('stroke-linecap', 'butt')
+              path.setAttribute('stroke-linejoin', 'round')
+
+              // 处理虚线边框
+              if (
+                element.borderType === 'dotted' ||
+                element.borderType === 'dashed'
+              ) {
+                if (element.borderStrokeDasharray) {
+                  path.setAttribute(
+                    'stroke-dasharray',
+                    element.borderStrokeDasharray
+                  )
+                } else if (element.borderType === 'dotted') {
+                  path.setAttribute('stroke-dasharray', '1, 3')
+                } else if (element.borderType === 'dashed') {
+                  path.setAttribute('stroke-dasharray', '5, 5')
+                }
+              }
+            })
+          }
+
+          stripedRightArrow.appendChild(mainArrow)
+          stripedRightArrow.appendChild(stripe1)
+          stripedRightArrow.appendChild(stripe2)
+          svg.appendChild(stripedRightArrow)
+          break
+        case 'rightArrowCallout':
+          const rightArrowCallout = document.createElementNS(
+            'http://www.w3.org/2000/svg',
+            'path'
+          )
+
+          rightArrowCallout.setAttribute(
+            'd',
+            `M0,0
+            L${element.width * 0.44},0
+            L${element.width * 0.44},${element.height * 0.375}
+            L${element.width * 0.79},${element.height * 0.375}
+            L${element.width * 0.79},${element.height * 0.25}
+            L${element.width},${element.height * 0.5}
+            L${element.width * 0.79},${element.height * 0.75}
+            L${element.width * 0.79},${element.height * 0.625}
+            L${element.width * 0.44},${element.height * 0.625}
+            L${element.width * 0.44},${element.height}
+            L0,${element.height}
+            Z`
+          )
+
+          // 设置填充色
+          if (element.fill && element.fill.type === 'color') {
+            rightArrowCallout.setAttribute(
+              'fill',
+              element.fill.value || 'transparent'
+            )
+          } else {
+            rightArrowCallout.setAttribute('fill', 'transparent')
+          }
+
+          // 设置边框
+          if (element.borderWidth > 0) {
+            rightArrowCallout.setAttribute(
+              'stroke',
+              element.borderColor || '#000'
+            )
+            rightArrowCallout.setAttribute(
+              'stroke-width',
+              element.borderWidth || 1
+            )
+            rightArrowCallout.setAttribute('stroke-linecap', 'butt')
+            rightArrowCallout.setAttribute('stroke-linejoin', 'round')
+
+            // 处理虚线边框
+            if (
+              element.borderType === 'dotted' ||
+              element.borderType === 'dashed'
+            ) {
+              if (element.borderStrokeDasharray) {
+                rightArrowCallout.setAttribute(
+                  'stroke-dasharray',
+                  element.borderStrokeDasharray
+                )
+              } else if (element.borderType === 'dotted') {
+                rightArrowCallout.setAttribute('stroke-dasharray', '1, 3')
+              } else if (element.borderType === 'dashed') {
+                rightArrowCallout.setAttribute('stroke-dasharray', '5, 5')
+              }
+            }
+          }
+
+          svg.appendChild(rightArrowCallout)
+          break
+        case 'leftRightArrowCallout':
+          const leftRightArrowCallout = document.createElementNS(
+            'http://www.w3.org/2000/svg',
+            'path'
+          )
+
+          leftRightArrowCallout.setAttribute(
+            'd',
+            `M0,${element.height * 0.5}
+            L${element.width * 0.139},${element.height * 0.25}
+            L${element.width * 0.139},${element.height * 0.375}
+            L${element.width * 0.364},${element.height * 0.375}
+            L${element.width * 0.364},0
+            L${element.width * 0.636},0
+            L${element.width * 0.636},${element.height * 0.375}
+            L${element.width * 0.861},${element.height * 0.375}
+            L${element.width * 0.861},${element.height * 0.25}
+            L${element.width},${element.height * 0.5}
+            L${element.width * 0.861},${element.height * 0.75}
+            L${element.width * 0.861},${element.height * 0.625}
+            L${element.width * 0.636},${element.height * 0.625}
+            L${element.width * 0.636},${element.height}
+            L${element.width * 0.364},${element.height}
+            L${element.width * 0.364},${element.height * 0.625}
+            L${element.width * 0.139},${element.height * 0.625}
+            L${element.width * 0.139},${element.height * 0.75}
+            Z`
+          )
+
+          // 设置填充色
+          if (element.fill && element.fill.type === 'color') {
+            leftRightArrowCallout.setAttribute(
+              'fill',
+              element.fill.value || 'transparent'
+            )
+          } else {
+            leftRightArrowCallout.setAttribute('fill', 'transparent')
+          }
+
+          // 设置边框
+          if (element.borderWidth > 0) {
+            leftRightArrowCallout.setAttribute(
+              'stroke',
+              element.borderColor || '#000'
+            )
+            leftRightArrowCallout.setAttribute(
+              'stroke-width',
+              element.borderWidth || 1
+            )
+            leftRightArrowCallout.setAttribute('stroke-linecap', 'butt')
+            leftRightArrowCallout.setAttribute('stroke-linejoin', 'round')
+
+            // 处理虚线边框
+            if (
+              element.borderType === 'dotted' ||
+              element.borderType === 'dashed'
+            ) {
+              if (element.borderStrokeDasharray) {
+                leftRightArrowCallout.setAttribute(
+                  'stroke-dasharray',
+                  element.borderStrokeDasharray
+                )
+              } else if (element.borderType === 'dotted') {
+                leftRightArrowCallout.setAttribute('stroke-dasharray', '1, 3')
+              } else if (element.borderType === 'dashed') {
+                leftRightArrowCallout.setAttribute('stroke-dasharray', '5, 5')
+              }
+            }
+          }
+
+          svg.appendChild(leftRightArrowCallout)
+          break
+        case 'quadArrowCallout':
+          const quadArrowCallout = document.createElementNS(
+            'http://www.w3.org/2000/svg',
+            'path'
+          )
+
+          quadArrowCallout.setAttribute(
+            'd',
+            `M0,${element.height * 0.5}
+            L${element.width * 0.096},${element.height * 0.315}
+            L${element.width * 0.096},${element.height * 0.407}
+            L${element.width * 0.26},${element.height * 0.407}
+            L${element.width * 0.26},${element.height * 0.259}
+            L${element.width * 0.453},${element.height * 0.259}
+            L${element.width * 0.453},${element.height * 0.185}
+            L${element.width * 0.405},${element.height * 0.185}
+            L${element.width * 0.5},0
+            L${element.width * 0.595},${element.height * 0.185}
+            L${element.width * 0.547},${element.height * 0.185}
+            L${element.width * 0.547},${element.height * 0.259}
+            L${element.width * 0.74},${element.height * 0.259}
+            L${element.width * 0.74},${element.height * 0.407}
+            L${element.width * 0.904},${element.height * 0.407}
+            L${element.width * 0.904},${element.height * 0.315}
+            L${element.width},${element.height * 0.5}
+            L${element.width * 0.904},${element.height * 0.685}
+            L${element.width * 0.904},${element.height * 0.593}
+            L${element.width * 0.74},${element.height * 0.593}
+            L${element.width * 0.74},${element.height * 0.741}
+            L${element.width * 0.547},${element.height * 0.741}
+            L${element.width * 0.547},${element.height * 0.815}
+            L${element.width * 0.595},${element.height * 0.815}
+            L${element.width * 0.5},${element.height}
+            L${element.width * 0.405},${element.height * 0.815}
+            L${element.width * 0.453},${element.height * 0.815}
+            L${element.width * 0.453},${element.height * 0.741}
+            L${element.width * 0.26},${element.height * 0.741}
+            L${element.width * 0.26},${element.height * 0.593}
+            L${element.width * 0.096},${element.height * 0.593}
+            L${element.width * 0.096},${element.height * 0.685}
+            Z`
+          )
+
+          // 设置填充色
+          if (element.fill && element.fill.type === 'color') {
+            quadArrowCallout.setAttribute(
+              'fill',
+              element.fill.value || 'transparent'
+            )
+          } else {
+            quadArrowCallout.setAttribute('fill', 'transparent')
+          }
+
+          // 设置边框
+          if (element.borderWidth > 0) {
+            quadArrowCallout.setAttribute(
+              'stroke',
+              element.borderColor || '#000'
+            )
+            quadArrowCallout.setAttribute(
+              'stroke-width',
+              element.borderWidth || 1
+            )
+            quadArrowCallout.setAttribute('stroke-linecap', 'butt')
+            quadArrowCallout.setAttribute('stroke-linejoin', 'round')
+
+            // 处理虚线边框
+            if (
+              element.borderType === 'dotted' ||
+              element.borderType === 'dashed'
+            ) {
+              if (element.borderStrokeDasharray) {
+                quadArrowCallout.setAttribute(
+                  'stroke-dasharray',
+                  element.borderStrokeDasharray
+                )
+              } else if (element.borderType === 'dotted') {
+                quadArrowCallout.setAttribute('stroke-dasharray', '1, 3')
+              } else if (element.borderType === 'dashed') {
+                quadArrowCallout.setAttribute('stroke-dasharray', '5, 5')
+              }
+            }
+          }
+
+          svg.appendChild(quadArrowCallout)
+          break
+        case 'leftArrowCallout':
+          const leftArrowCallout = document.createElementNS(
+            'http://www.w3.org/2000/svg',
+            'path'
+          )
+
+          leftArrowCallout.setAttribute(
+            'd',
+            `M0,${element.height * 0.5}
+            L${element.width * 0.183},${element.height * 0.25}
+            L${element.width * 0.183},${element.height * 0.375}
+            L${element.width * 0.35},${element.height * 0.375}
+            L${element.width * 0.35},0
+            L${element.width},0
+            L${element.width},${element.height}
+            L${element.width * 0.35},${element.height}
+            L${element.width * 0.35},${element.height * 0.625}
+            L${element.width * 0.183},${element.height * 0.625}
+            L${element.width * 0.183},${element.height * 0.75}
+            Z`
+          )
+
+          // 设置填充色
+          if (element.fill && element.fill.type === 'color') {
+            leftArrowCallout.setAttribute(
+              'fill',
+              element.fill.value || 'transparent'
+            )
+          } else {
+            leftArrowCallout.setAttribute('fill', 'transparent')
+          }
+
+          // 设置边框
+          if (element.borderWidth > 0) {
+            leftArrowCallout.setAttribute(
+              'stroke',
+              element.borderColor || '#000'
+            )
+            leftArrowCallout.setAttribute(
+              'stroke-width',
+              element.borderWidth || 1
+            )
+            leftArrowCallout.setAttribute('stroke-linecap', 'butt')
+            leftArrowCallout.setAttribute('stroke-linejoin', 'round')
+
+            // 处理虚线边框
+            if (
+              element.borderType === 'dotted' ||
+              element.borderType === 'dashed'
+            ) {
+              if (element.borderStrokeDasharray) {
+                leftArrowCallout.setAttribute(
+                  'stroke-dasharray',
+                  element.borderStrokeDasharray
+                )
+              } else if (element.borderType === 'dotted') {
+                leftArrowCallout.setAttribute('stroke-dasharray', '1, 3')
+              } else if (element.borderType === 'dashed') {
+                leftArrowCallout.setAttribute('stroke-dasharray', '5, 5')
+              }
+            }
+          }
+
+          svg.appendChild(leftArrowCallout)
+          break
+        case 'upArrowCallout':
+          const upArrowCallout = document.createElementNS(
+            'http://www.w3.org/2000/svg',
+            'path'
+          )
+
+          upArrowCallout.setAttribute(
+            'd',
+            `M0,${element.height * 0.35}
+            L${element.width * 0.41},${element.height * 0.35}
+            L${element.width * 0.41},${element.height * 0.25}
+            L${element.width * 0.32},${element.height * 0.25}
+            L${element.width * 0.5},0
+            L${element.width * 0.68},${element.height * 0.25}
+            L${element.width * 0.59},${element.height * 0.25}
+            L${element.width * 0.59},${element.height * 0.35}
+            L${element.width},${element.height * 0.35}
+            L${element.width},${element.height}
+            L0,${element.height}
+            Z`
+          )
+
+          // 设置填充色
+          if (element.fill && element.fill.type === 'color') {
+            upArrowCallout.setAttribute(
+              'fill',
+              element.fill.value || 'transparent'
+            )
+          } else {
+            upArrowCallout.setAttribute('fill', 'transparent')
+          }
+
+          // 设置边框
+          if (element.borderWidth > 0) {
+            upArrowCallout.setAttribute(
+              'stroke',
+              element.borderColor || '#000'
+            )
+            upArrowCallout.setAttribute(
+              'stroke-width',
+              element.borderWidth || 1
+            )
+            upArrowCallout.setAttribute('stroke-linecap', 'butt')
+            upArrowCallout.setAttribute('stroke-linejoin', 'round')
+
+            // 处理虚线边框
+            if (
+              element.borderType === 'dotted' ||
+              element.borderType === 'dashed'
+            ) {
+              if (element.borderStrokeDasharray) {
+                upArrowCallout.setAttribute(
+                  'stroke-dasharray',
+                  element.borderStrokeDasharray
+                )
+              } else if (element.borderType === 'dotted') {
+                upArrowCallout.setAttribute('stroke-dasharray', '1, 3')
+              } else if (element.borderType === 'dashed') {
+                upArrowCallout.setAttribute('stroke-dasharray', '5, 5')
+              }
+            }
+          }
+
+          svg.appendChild(upArrowCallout)
+          break
+        case 'notchedRightArrow':
+          const notchedRightArrow = document.createElementNS(
+            'http://www.w3.org/2000/svg',
+            'path'
+          )
+
+          notchedRightArrow.setAttribute(
+            'd',
+            `M0,${element.height * 0.25}
+            L${element.width * 0.83},${element.height * 0.25}
+            L${element.width * 0.83},0
+            L${element.width},${element.height * 0.5}
+            L${element.width * 0.83},${element.height}
+            L${element.width * 0.83},${element.height * 0.75}
+            L0,${element.height * 0.75}
+            L${element.width * 0.086},${element.height * 0.5}
+            Z`
+          )
+
+          // 设置填充色
+          if (element.fill && element.fill.type === 'color') {
+            notchedRightArrow.setAttribute(
+              'fill',
+              element.fill.value || 'transparent'
+            )
+          } else {
+            notchedRightArrow.setAttribute('fill', 'transparent')
+          }
+
+          // 设置边框
+          if (element.borderWidth > 0) {
+            notchedRightArrow.setAttribute(
+              'stroke',
+              element.borderColor || '#000'
+            )
+            notchedRightArrow.setAttribute(
+              'stroke-width',
+              element.borderWidth || 1
+            )
+            notchedRightArrow.setAttribute('stroke-linecap', 'butt')
+            notchedRightArrow.setAttribute('stroke-linejoin', 'round')
+
+            // 处理虚线边框
+            if (
+              element.borderType === 'dotted' ||
+              element.borderType === 'dashed'
+            ) {
+              if (element.borderStrokeDasharray) {
+                notchedRightArrow.setAttribute(
+                  'stroke-dasharray',
+                  element.borderStrokeDasharray
+                )
+              } else if (element.borderType === 'dotted') {
+                notchedRightArrow.setAttribute('stroke-dasharray', '1, 3')
+              } else if (element.borderType === 'dashed') {
+                notchedRightArrow.setAttribute('stroke-dasharray', '5, 5')
+              }
+            }
+          }
+
+          svg.appendChild(notchedRightArrow)
+          break
+        case 'homePlate':
+          const homePlate = document.createElementNS(
+            'http://www.w3.org/2000/svg',
+            'path'
+          )
+
+          homePlate.setAttribute(
+            'd',
+            `M0,0
+            L${element.width * 0.925},0
+            L${element.width},${element.height * 0.5}
+            L${element.width * 0.925},${element.height}
+            L0,${element.height}
+            Z`
+          )
+
+          // 设置填充色
+          if (element.fill && element.fill.type === 'color') {
+            homePlate.setAttribute('fill', element.fill.value || 'transparent')
+          } else {
+            homePlate.setAttribute('fill', 'transparent')
+          }
+
+          // 设置边框
+          if (element.borderWidth > 0) {
+            homePlate.setAttribute('stroke', element.borderColor || '#000')
+            homePlate.setAttribute('stroke-width', element.borderWidth || 1)
+            homePlate.setAttribute('stroke-linecap', 'butt')
+            homePlate.setAttribute('stroke-linejoin', 'round')
+
+            // 处理虚线边框
+            if (
+              element.borderType === 'dotted' ||
+              element.borderType === 'dashed'
+            ) {
+              if (element.borderStrokeDasharray) {
+                homePlate.setAttribute(
+                  'stroke-dasharray',
+                  element.borderStrokeDasharray
+                )
+              } else if (element.borderType === 'dotted') {
+                homePlate.setAttribute('stroke-dasharray', '1, 3')
+              } else if (element.borderType === 'dashed') {
+                homePlate.setAttribute('stroke-dasharray', '5, 5')
+              }
+            }
+          }
+
+          svg.appendChild(homePlate)
+          break
+        case 'rightTriangle':
+          const rightTriangle = document.createElementNS(
+            'http://www.w3.org/2000/svg',
+            'polygon'
+          )
+          rightTriangle.setAttribute(
+            'points',
+            `0,0 ${element.width},${element.height} 0,${element.height}`
+          )
+
+          // 设置填充色
+          if (element.fill && element.fill.type === 'color') {
+            rightTriangle.setAttribute(
+              'fill',
+              element.fill.value || 'transparent'
+            )
+          } else {
+            rightTriangle.setAttribute('fill', 'transparent')
+          }
+
+          // 设置边框
+          if (element.borderWidth > 0) {
+            rightTriangle.setAttribute('stroke', element.borderColor || '#000')
+            rightTriangle.setAttribute(
+              'stroke-width',
+              element.borderWidth || 1
+            )
+
+            // 处理虚线边框
+            if (
+              element.borderType === 'dotted' ||
+              element.borderType === 'dashed'
+            ) {
+              if (element.borderStrokeDasharray) {
+                rightTriangle.setAttribute(
+                  'stroke-dasharray',
+                  element.borderStrokeDasharray
+                )
+              } else if (element.borderType === 'dotted') {
+                rightTriangle.setAttribute('stroke-dasharray', '1, 3')
+              } else if (element.borderType === 'dashed') {
+                rightTriangle.setAttribute('stroke-dasharray', '5, 5')
+              }
+            }
+          }
+
+          svg.appendChild(rightTriangle)
+          break
+        case 'semiCircle':
+          const semiCircle = document.createElementNS(
+            'http://www.w3.org/2000/svg',
+            'path'
+          )
+          semiCircle.setAttribute(
+            'd',
+            `M0,${element.height} A${element.width / 2},${
+              element.height
+            } 0 0,1 ${element.width},${element.height} Z`
+          )
+
+          // 设置填充色
+          if (element.fill && element.fill.type === 'color') {
+            semiCircle.setAttribute(
+              'fill',
+              element.fill.value || 'transparent'
+            )
+          } else {
+            semiCircle.setAttribute('fill', 'transparent')
+          }
+
+          // 设置边框
+          if (element.borderWidth > 0) {
+            semiCircle.setAttribute('stroke', element.borderColor || '#000')
+            semiCircle.setAttribute('stroke-width', element.borderWidth || 1)
+
+            // 处理虚线边框
+            if (
+              element.borderType === 'dotted' ||
+              element.borderType === 'dashed'
+            ) {
+              if (element.borderStrokeDasharray) {
+                semiCircle.setAttribute(
+                  'stroke-dasharray',
+                  element.borderStrokeDasharray
+                )
+              } else if (element.borderType === 'dotted') {
+                semiCircle.setAttribute('stroke-dasharray', '1, 3')
+              } else if (element.borderType === 'dashed') {
+                semiCircle.setAttribute('stroke-dasharray', '5, 5')
+              }
+            }
+          }
+
+          svg.appendChild(semiCircle)
+          break
+
+        case 'star':
+          const star = document.createElementNS(
+            'http://www.w3.org/2000/svg',
+            'polygon'
+          )
+          const cx = element.width / 2
+          const cy = element.height / 2
+          const outerRadius = Math.min(element.width, element.height) / 2
+          const innerRadius = outerRadius * 0.4
+          let starPoints = ''
+
+          for (let i = 0; i < 10; i++) {
+            const radius = i % 2 === 0 ? outerRadius : innerRadius
+            const angle = (Math.PI * i) / 5
+            const x = cx + radius * Math.sin(angle)
+            const y = cy - radius * Math.cos(angle)
+            starPoints += `${x},${y} `
+          }
+
+          star.setAttribute('points', starPoints.trim())
+
+          // 设置填充色
+          if (element.fill && element.fill.type === 'color') {
+            star.setAttribute('fill', element.fill.value || 'transparent')
+          } else {
+            star.setAttribute('fill', 'transparent')
+          }
+
+          // 设置边框
+          if (element.borderWidth > 0) {
+            star.setAttribute('stroke', element.borderColor || '#000')
+            star.setAttribute('stroke-width', element.borderWidth || 1)
+
+            // 处理虚线边框
+            if (
+              element.borderType === 'dotted' ||
+              element.borderType === 'dashed'
+            ) {
+              if (element.borderStrokeDasharray) {
+                star.setAttribute(
+                  'stroke-dasharray',
+                  element.borderStrokeDasharray
+                )
+              } else if (element.borderType === 'dotted') {
+                star.setAttribute('stroke-dasharray', '1, 3')
+              } else if (element.borderType === 'dashed') {
+                star.setAttribute('stroke-dasharray', '5, 5')
+              }
+            }
+          }
+
+          svg.appendChild(star)
+          break
+
+        case 'cross':
+          const cross = document.createElementNS(
+            'http://www.w3.org/2000/svg',
+            'polygon'
+          )
+          cross.setAttribute(
+            'points',
+            `${element.width * 0.35},0 ${element.width * 0.65},0 ${
+              element.width * 0.65
+            },${element.height * 0.35} ` +
+              `${element.width},${element.height * 0.35} ${element.width},${
+                element.height * 0.65
+              } ` +
+              `${element.width * 0.65},${element.height * 0.65} ${
+                element.width * 0.65
+              },${element.height} ` +
+              `${element.width * 0.35},${element.height} ${
+                element.width * 0.35
+              },${element.height * 0.65} ` +
+              `0,${element.height * 0.65} 0,${element.height * 0.35} ${
+                element.width * 0.35
+              },${element.height * 0.35}`
+          )
+
+          // 设置填充色
+          if (element.fill && element.fill.type === 'color') {
+            cross.setAttribute('fill', element.fill.value || 'transparent')
+          } else {
+            cross.setAttribute('fill', 'transparent')
+          }
+
+          // 设置边框
+          if (element.borderWidth > 0) {
+            cross.setAttribute('stroke', element.borderColor || '#000')
+            cross.setAttribute('stroke-width', element.borderWidth || 1)
+
+            // 处理虚线边框
+            if (
+              element.borderType === 'dotted' ||
+              element.borderType === 'dashed'
+            ) {
+              if (element.borderStrokeDasharray) {
+                cross.setAttribute(
+                  'stroke-dasharray',
+                  element.borderStrokeDasharray
+                )
+              } else if (element.borderType === 'dotted') {
+                cross.setAttribute('stroke-dasharray', '1, 3')
+              } else if (element.borderType === 'dashed') {
+                cross.setAttribute('stroke-dasharray', '5, 5')
+              }
+            }
+          }
+
+          svg.appendChild(cross)
+          break
+
+        case 'chevron':
+          const chevron = document.createElementNS(
+            'http://www.w3.org/2000/svg',
+            'polygon'
+          )
+          chevron.setAttribute(
+            'points',
+            `${element.width * 0.75},0 ${element.width},${
+              element.height * 0.5
+            } ${element.width * 0.75},${element.height} ` +
+              `0,${element.height} ${element.width * 0.25},${
+                element.height * 0.5
+              } 0,0`
+          )
+
+          // 设置填充色
+          if (element.fill && element.fill.type === 'color') {
+            chevron.setAttribute('fill', element.fill.value || 'transparent')
+          } else {
+            chevron.setAttribute('fill', 'transparent')
+          }
+
+          // 设置边框
+          if (element.borderWidth > 0) {
+            chevron.setAttribute('stroke', element.borderColor || '#000')
+            chevron.setAttribute('stroke-width', element.borderWidth || 1)
+
+            // 处理虚线边框
+            if (
+              element.borderType === 'dotted' ||
+              element.borderType === 'dashed'
+            ) {
+              if (element.borderStrokeDasharray) {
+                chevron.setAttribute(
+                  'stroke-dasharray',
+                  element.borderStrokeDasharray
+                )
+              } else if (element.borderType === 'dotted') {
+                chevron.setAttribute('stroke-dasharray', '1, 3')
+              } else if (element.borderType === 'dashed') {
+                chevron.setAttribute('stroke-dasharray', '5, 5')
+              }
+            }
+          }
+
+          svg.appendChild(chevron)
+          break
+
+        case 'frame':
+          // 创建外框和内框
+          const outerRect = document.createElementNS(
+            'http://www.w3.org/2000/svg',
+            'rect'
+          )
+          outerRect.setAttribute('x', 0)
+          outerRect.setAttribute('y', 0)
+          outerRect.setAttribute('width', element.width)
+          outerRect.setAttribute('height', element.height)
+
+          const innerRect = document.createElementNS(
+            'http://www.w3.org/2000/svg',
+            'rect'
+          )
+          const frameWidth = element.width / 10
+          innerRect.setAttribute('x', frameWidth)
+          innerRect.setAttribute('y', frameWidth)
+          innerRect.setAttribute('width', element.width - frameWidth * 2)
+          innerRect.setAttribute('height', element.height - frameWidth * 2)
+
+          // 设置填充色
+          if (element.fill && element.fill.type === 'color') {
+            outerRect.setAttribute('fill', element.fill.value || 'transparent')
+            innerRect.setAttribute('fill', 'white') // 内框为白色
+          } else {
+            outerRect.setAttribute('fill', 'transparent')
+            innerRect.setAttribute('fill', 'white')
+          }
+
+          // 设置边框
+          if (element.borderWidth > 0) {
+            outerRect.setAttribute('stroke', element.borderColor || '#000')
+            outerRect.setAttribute('stroke-width', element.borderWidth || 1)
+
+            // 处理虚线边框
+            if (
+              element.borderType === 'dotted' ||
+              element.borderType === 'dashed'
+            ) {
+              if (element.borderStrokeDasharray) {
+                outerRect.setAttribute(
+                  'stroke-dasharray',
+                  element.borderStrokeDasharray
+                )
+              } else if (element.borderType === 'dotted') {
+                outerRect.setAttribute('stroke-dasharray', '1, 3')
+              } else if (element.borderType === 'dashed') {
+                outerRect.setAttribute('stroke-dasharray', '5, 5')
+              }
+            }
+          }
+
+          svg.appendChild(outerRect)
+          svg.appendChild(innerRect)
+          break
+
+        case 'cloud':
+          // 使用路径绘制云形
+          const cloud = document.createElementNS(
+            'http://www.w3.org/2000/svg',
+            'path'
+          )
+          const w = element.width
+          const h = element.height
+
+          cloud.setAttribute(
+            'd',
+            `M${w * 0.2},${h * 0.6} ` +
+              `C${w * 0.05},${h * 0.6} ${w * 0.05},${h * 0.3} ${w * 0.2},${
+                h * 0.3
+              } ` +
+              `C${w * 0.2},${h * 0.1} ${w * 0.45},${h * 0.1} ${w * 0.5},${
+                h * 0.3
+              } ` +
+              `C${w * 0.55},${h * 0.1} ${w * 0.8},${h * 0.1} ${w * 0.8},${
+                h * 0.3
+              } ` +
+              `C${w * 0.95},${h * 0.3} ${w * 0.95},${h * 0.6} ${w * 0.8},${
+                h * 0.6
+              } ` +
+              `L${w * 0.2},${h * 0.6} Z`
+          )
+
+          // 设置填充色
+          if (element.fill && element.fill.type === 'color') {
+            cloud.setAttribute('fill', element.fill.value || 'transparent')
+          } else {
+            cloud.setAttribute('fill', 'transparent')
+          }
+
+          // 设置边框
+          if (element.borderWidth > 0) {
+            cloud.setAttribute('stroke', element.borderColor || '#000')
+            cloud.setAttribute('stroke-width', element.borderWidth || 1)
+
+            // 处理虚线边框
+            if (
+              element.borderType === 'dotted' ||
+              element.borderType === 'dashed'
+            ) {
+              if (element.borderStrokeDasharray) {
+                cloud.setAttribute(
+                  'stroke-dasharray',
+                  element.borderStrokeDasharray
+                )
+              } else if (element.borderType === 'dotted') {
+                cloud.setAttribute('stroke-dasharray', '1, 3')
+              } else if (element.borderType === 'dashed') {
+                cloud.setAttribute('stroke-dasharray', '5, 5')
+              }
+            }
+          }
+
+          svg.appendChild(cloud)
+          break
+
+        default:
+          // 默认形状处理 - 使用矩形
+          const defaultRect = document.createElementNS(
+            'http://www.w3.org/2000/svg',
+            'rect'
+          )
+          defaultRect.setAttribute('x', 0)
+          defaultRect.setAttribute('y', 0)
+          defaultRect.setAttribute('width', element.width)
+          defaultRect.setAttribute('height', element.height)
+
+          // 设置填充色
+          if (element.fill && element.fill.type === 'color') {
+            defaultRect.setAttribute(
+              'fill',
+              element.fill.value || 'transparent'
+            )
+          } else {
+            defaultRect.setAttribute('fill', 'transparent')
+          }
+
+          // 设置边框
+          if (element.borderWidth > 0) {
+            defaultRect.setAttribute('stroke', element.borderColor || '#000')
+            defaultRect.setAttribute('stroke-width', element.borderWidth || 1)
+          }
+
+          svg.appendChild(defaultRect)
+          break
+      }
+      // 应用翻转和旋转变换
+      const transformList = []
+      if (element.isFlipV) {
+        transformList.push(`scale(1, -1) translate(0, -${element.height})`)
+      }
+      if (element.isFlipH) {
+        transformList.push(`scale(-1, 1) translate(-${element.width}, 0)`)
+      }
+      if (element.rotate) {
+        transformList.push(
+          `rotate(${element.rotate}, ${element.width / 2}, ${
+            element.height / 2
+          })`
+        )
+      }
+      if (transformList.length > 0) {
+        svg.setAttribute('transform', transformList.join(' '))
+      }
+
+      el.appendChild(svg)
+
+      // 设置形状内容
+      if (element.content) {
+        const contentContainer = document.createElement('div')
+        contentContainer.innerHTML = this.convertPtToPxInContent(
+          element.content
+        )
+        contentContainer.style.position = 'absolute'
+        contentContainer.style.width = element.width + 'px'
+        contentContainer.style.height = element.height + 'px'
+        contentContainer.style.top = '0px'
+        contentContainer.style.left = '0px'
+        contentContainer.style.display = 'flex'
+        contentContainer.style.alignItems = 'center'
+        contentContainer.style.justifyContent = 'center'
+        contentContainer.style.zIndex = element.order
+        contentContainer.style.pointerEvents = 'none'
+
+        el.appendChild(contentContainer)
+      }
+
+      return el
+    },
+
+    // 创建表格元素
+    createTableElement (element) {
+      const el = document.createElement('div')
+      el.style.position = 'absolute'
+      el.style.top = element.top + 'px'
+      el.style.left = element.left + 'px'
+      el.style.width = element.width + 'px'
+      el.style.height = element.height + 'px'
+      el.style.zIndex = element.order
+
+      // 创建表格元素
+      const table = document.createElement('table')
+      table.style.width = element.width + 'px'
+      table.style.height = element.height + 'px'
+      table.style.borderCollapse = 'collapse'
+      table.style.tableLayout = 'fixed'
+      // 设置表格边框
+      if (element.borders) {
+        if (element.borders.all) {
+          const border = element.borders.all
+          table.style.border = `${border.borderWidth || 1}px ${
+            border.borderType || 'solid'
+          } ${border.borderColor || '#000'}`
+        } else {
+          // 分别设置四边边框
+          if (element.borders.top) {
+            table.style.borderTop = `${
+              element.borders.top.borderWidth || 1
+            }px ${element.borders.top.borderType || 'solid'} ${
+              element.borders.top.borderColor || '#000'
+            }`
+          }
+          if (element.borders.bottom) {
+            table.style.borderBottom = `${
+              element.borders.bottom.borderWidth || 1
+            }px ${element.borders.bottom.borderType || 'solid'} ${
+              element.borders.bottom.borderColor || '#000'
+            }`
+          }
+          if (element.borders.left) {
+            table.style.borderLeft = `${
+              element.borders.left.borderWidth || 1
+            }px ${element.borders.left.borderType || 'solid'} ${
+              element.borders.left.borderColor || '#000'
+            }`
+          }
+          if (element.borders.right) {
+            table.style.borderRight = `${
+              element.borders.right.borderWidth || 1
+            }px ${element.borders.right.borderType || 'solid'} ${
+              element.borders.right.borderColor || '#000'
+            }`
+          }
+        }
+      }
+
+      // 创建表格内容
+      const tbody = document.createElement('tbody')
+
+      // 处理表格数据
+      if (element.data && element.data.length > 0) {
+        element.data.forEach((rowData, rowIndex) => {
+          const tr = document.createElement('tr')
+
+          rowData.forEach((cell, colIndex) => {
+            // 跳过被合并的单元格
+            if (cell.hMerge) return
+
+            const td = document.createElement('td')
+
+            // 设置单元格内容
+            if (cell.text) {
+              td.innerHTML = this.convertPtToPxInContent(cell.text)
+            }
+
+            // 设置单元格样式
+            td.style.padding = '0px'
+            td.style.verticalAlign = 'middle'
+
+            // 设置文本样式
+            if (cell.fontColor) td.style.color = cell.fontColor
+            if (cell.fontSize) td.style.fontSize = cell.fontSize
+            if (cell.fontFamily) td.style.fontFamily = cell.fontFamily
+            if (cell.bold) td.style.fontWeight = 'bold'
+            if (cell.italic) td.style.fontStyle = 'italic'
+            if (cell.underline) td.style.textDecoration = 'underline'
+            if (cell.align) td.style.textAlign = cell.align
+
+            // 设置背景色
+            if (cell.fillColor) td.style.backgroundColor = cell.fillColor
+
+            // 设置单元格边框
+            if (cell.borders) {
+              if (cell.borders.all) {
+                const border = cell.borders.all
+                td.style.border = `${border.borderWidth || 1}px ${
+                  border.borderType || 'solid'
+                } ${border.borderColor || '#000'}`
+              } else {
+                // 分别设置四边边框
+                if (cell.borders.top) {
+                  td.style.borderTop = `${
+                    cell.borders.top.borderWidth || 1
+                  }px ${cell.borders.top.borderType || 'solid'} ${
+                    cell.borders.top.borderColor || '#000'
+                  }`
+                }
+                if (cell.borders.bottom) {
+                  td.style.borderBottom = `${
+                    cell.borders.bottom.borderWidth || 1
+                  }px ${cell.borders.bottom.borderType || 'solid'} ${
+                    cell.borders.bottom.borderColor || '#000'
+                  }`
+                }
+                if (cell.borders.left) {
+                  td.style.borderLeft = `${
+                    cell.borders.left.borderWidth || 1
+                  }px ${cell.borders.left.borderType || 'solid'} ${
+                    cell.borders.left.borderColor || '#000'
+                  }`
+                }
+                if (cell.borders.right) {
+                  td.style.borderRight = `${
+                    cell.borders.right.borderWidth || 1
+                  }px ${cell.borders.right.borderType || 'solid'} ${
+                    cell.borders.right.borderColor || '#000'
+                  }`
+                }
+              }
+            }
+            td.style.width = element.colWidths[colIndex] + 'px'
+            // 设置单元格合并
+            if (cell.colSpan && cell.colSpan > 1) {
+              td.colSpan = cell.colSpan
+              td.style.width =
+                element.colWidths[colIndex] * cell.colSpan + 'px'
+            }
+            if (cell.rowSpan && cell.rowSpan > 1) {
+              td.rowSpan = cell.rowSpan
+            }
+
+            tr.appendChild(td)
+          })
+          tr.style.height = element.rowHeights[rowIndex] + 'px'
+          tbody.appendChild(tr)
+        })
+      }
+
+      table.appendChild(tbody)
+      el.appendChild(table)
+
+      return el
+    },
+    // 创建图表元素
+    createChartElement (element) {
+      // 1. 创建基础容器
+      const el = document.createElement('div')
+      el.style.position = 'absolute'
+      el.style.top = element.top + 'px'
+      el.style.left = element.left + 'px'
+      el.style.width = element.width + 'px'
+      el.style.height = element.height + 'px'
+      el.style.zIndex = element.order
+      // 2. 创建SVG画布
+      const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg')
+      svg.setAttribute('width', element.width)
+      svg.setAttribute('height', element.height)
+      svg.setAttribute('viewBox', `0 0 ${element.width} ${element.height}`)
+
+      // 3. 设置图表内边距
+      const padding = {
+        top: 60, // 为图例留出空间
+        right: 40, // 右侧边距
+        bottom: 60, // X轴标签空间
+        left: 60 // Y轴标签空间
+      }
+
+      // 4. 计算实际绘图区域
+      const chartWidth = element.width - padding.left - padding.right
+      const chartHeight = element.height - padding.top - padding.bottom
+      // 处理不同图表类型
+      switch (element.chartType) {
+        case 'barChart':
+          // 绘制柱状图
+          this.drawBarChart(svg, element, {
+            padding,
+            chartWidth,
+            chartHeight,
+            barDir: element.barDir || 'col',
+            grouping: element.grouping || 'clustered'
+          })
+          break
+        case 'pieChart':
+        case 'doughnutChart':
+          this.drawDonutChart(svg, element, chartWidth, chartHeight)
+          break
+        default:
+          console.warn('Unsupported chart type:', element.chartType)
+      }
+
+      el.appendChild(svg)
+      return el
+    },
+    drawDonutChart (svg, element, chartWidth, chartHeight) {
+      // 计算饼图中心点和半径
+      const cx = element.width / 2
+      const cy = element.height / 2
+      const radius = Math.min(chartWidth, chartHeight) / 2
+
+      // 计算所有数据总和
+      const total = element.data[0].values.reduce(
+        (sum, item) => sum + item.y,
+        0
+      )
+
+      // 创建颜色数组
+      const colors = element.colors || [
+        '#5B9BD5',
+        '#ED7D31',
+        '#A5A5A5',
+        '#FFC000'
+      ]
+
+      let startAngle = 0
+      element.data[0].values.forEach((item, index) => {
+        // 计算扇形角度
+        const angle = (item.y / total) * Math.PI * 2
+
+        // 计算扇形路径
+        const endAngle = startAngle + angle
+        const x1 = cx + radius * Math.cos(startAngle)
+        const y1 = cy + radius * Math.sin(startAngle)
+        const x2 = cx + radius * Math.cos(endAngle)
+        const y2 = cy + radius * Math.sin(endAngle)
+
+        // 创建扇形路径
+        const path = document.createElementNS(
+          'http://www.w3.org/2000/svg',
+          'path'
+        )
+        const largeArcFlag = angle > Math.PI ? 1 : 0
+
+        // 根据是否为环形图决定内圆半径
+        const innerRadius =
+          element.chartType === 'doughnutChart' ? radius * 0.6 : 0
+        if (element.chartType === 'doughnutChart') {
+          // 环形图路径
+          const ix1 = cx + innerRadius * Math.cos(startAngle)
+          const iy1 = cy + innerRadius * Math.sin(startAngle)
+          const ix2 = cx + innerRadius * Math.cos(endAngle)
+          const iy2 = cy + innerRadius * Math.sin(endAngle)
+
+          path.setAttribute(
+            'd',
+            `
+            M ${x1} ${y1}
+            A ${radius} ${radius} 0 ${largeArcFlag} 1 ${x2} ${y2}
+            L ${ix2} ${iy2}
+            A ${innerRadius} ${innerRadius} 0 ${largeArcFlag} 0 ${ix1} ${iy1}
+            Z
+          `
+          )
+        } else {
+          // 饼图路径
+          path.setAttribute(
+            'd',
+            `
+            M ${cx} ${cy}
+            L ${x1} ${y1}
+            A ${radius} ${radius} 0 ${largeArcFlag} 1 ${x2} ${y2}
+            Z
+          `
+          )
+        }
+
+        // 设置填充色
+        path.setAttribute('fill', colors[index % colors.length])
+        path.setAttribute('stroke', '#fff')
+        path.setAttribute('stroke-width', '1')
+        svg.appendChild(path)
+
+        // 添加数值标签
+        const labelAngle = startAngle + angle / 2
+        const labelRadius = radius * 1.1
+        const labelX = cx + labelRadius * Math.cos(labelAngle)
+        const labelY = cy + labelRadius * Math.sin(labelAngle)
+
+        const label = document.createElementNS(
+          'http://www.w3.org/2000/svg',
+          'text'
+        )
+        label.setAttribute('x', labelX)
+        label.setAttribute('y', labelY)
+        label.setAttribute('text-anchor', 'middle')
+        label.setAttribute('alignment-baseline', 'middle')
+        label.setAttribute('font-size', '12px')
+        label.textContent = `${((item.y / total) * 100).toFixed(1)}%`
+        svg.appendChild(label)
+
+        startAngle = endAngle
+      })
+    },
+    // 绘制柱状图
+    drawBarChart (svg, element, options) {
+      const { padding, chartWidth, chartHeight, barDir, grouping } = options
+      const series = element.data
+      const categories = series[0].xlabels
+      const categoryCount = Object.keys(categories).length
+
+      // 1. 计算最大值
+      let maxValue = 0
+      series.forEach((serie) => {
+        const seriesMax = Math.max(...serie.values.map((v) => v.y))
+        maxValue = Math.max(maxValue, seriesMax)
+      })
+      maxValue = maxValue * 1.2 // 增加20%空间
+
+      // 2. 计算柱子布局
+      const groupWidth = chartWidth / categoryCount
+      const barWidth =
+        grouping === 'clustered'
+          ? (groupWidth * 0.6) / series.length // 分组模式
+          : groupWidth * 0.6 // 堆叠模式
+      const barSpacing =
+        (groupWidth * 0.4) / (grouping === 'clustered' ? series.length + 1 : 2)
+
+      // 3. 绘制坐标轴
+      // X轴
+      const xAxisPath = document.createElementNS(
+        'http://www.w3.org/2000/svg',
+        'path'
+      )
+      xAxisPath.setAttribute(
+        'd',
+        `M${padding.left},${element.height - padding.bottom} L${
+          element.width - padding.right
+        },${element.height - padding.bottom}`
+      )
+      xAxisPath.setAttribute('stroke', '#000')
+      xAxisPath.setAttribute('stroke-width', '1')
+      svg.appendChild(xAxisPath)
+
+      // Y轴
+      const yAxisPath = document.createElementNS(
+        'http://www.w3.org/2000/svg',
+        'path'
+      )
+      yAxisPath.setAttribute(
+        'd',
+        `M${padding.left},${padding.top} L${padding.left},${
+          element.height - padding.bottom
+        }`
+      )
+      yAxisPath.setAttribute('stroke', '#000')
+      yAxisPath.setAttribute('stroke-width', '1')
+      svg.appendChild(yAxisPath)
+
+      // 4. 绘制Y轴刻度和网格线
+      const yTickCount = 5
+      for (let i = 0; i <= yTickCount; i++) {
+        const y = padding.top + (chartHeight * i) / yTickCount
+        const value = maxValue - (maxValue * i) / yTickCount
+
+        // 水平网格线
+        const gridLine = document.createElementNS(
+          'http://www.w3.org/2000/svg',
+          'line'
+        )
+        gridLine.setAttribute('x1', padding.left)
+        gridLine.setAttribute('y1', y)
+        gridLine.setAttribute('x2', padding.left + chartWidth)
+        gridLine.setAttribute('y2', y)
+        gridLine.setAttribute('stroke', '#eee')
+        gridLine.setAttribute('stroke-width', '1')
+        svg.appendChild(gridLine)
+
+        // 刻度线
+        const tick = document.createElementNS(
+          'http://www.w3.org/2000/svg',
+          'line'
+        )
+        tick.setAttribute('x1', padding.left - 6)
+        tick.setAttribute('y1', y)
+        tick.setAttribute('x2', padding.left)
+        tick.setAttribute('y2', y)
+        tick.setAttribute('stroke', '#000')
+        tick.setAttribute('stroke-width', '1')
+        svg.appendChild(tick)
+
+        // 刻度值
+        const label = document.createElementNS(
+          'http://www.w3.org/2000/svg',
+          'text'
+        )
+        label.setAttribute('x', padding.left - 10)
+        label.setAttribute('y', y + 4)
+        label.setAttribute('text-anchor', 'end')
+        label.setAttribute('font-size', '12px')
+        label.textContent = value.toFixed(1)
+        svg.appendChild(label)
+      }
+
+      // 5. 绘制数据条
+      series.forEach((serie, serieIndex) => {
+        serie.values.forEach((value, index) => {
+          const barHeight = (value.y / maxValue) * chartHeight
+          const x =
+            padding.left +
+            groupWidth * index +
+            (grouping === 'clustered'
+              ? barSpacing * (serieIndex + 1) + barWidth * serieIndex
+              : barSpacing)
+          const y = element.height - padding.bottom - barHeight
+
+          // 绘制柱子
+          const bar = document.createElementNS(
+            'http://www.w3.org/2000/svg',
+            'rect'
+          )
+          bar.setAttribute('x', x)
+          bar.setAttribute('y', y)
+          bar.setAttribute('width', barWidth)
+          bar.setAttribute('height', barHeight)
+          bar.setAttribute(
+            'fill',
+            element.colors[serieIndex] || `hsl(${serieIndex * 60}, 70%, 50%)`
+          )
+          svg.appendChild(bar)
+
+          // 数值标签
+          if (element.marker) {
+            const label = document.createElementNS(
+              'http://www.w3.org/2000/svg',
+              'text'
+            )
+            label.setAttribute('x', x + barWidth / 2)
+            label.setAttribute('y', y - 5)
+            label.setAttribute('text-anchor', 'middle')
+            label.setAttribute('font-size', '12px')
+            label.textContent = value.y.toFixed(1)
+            svg.appendChild(label)
+          }
+        })
+      })
+
+      // 6. 绘制X轴类别标签
+      Object.values(categories).forEach((label, index) => {
+        const x = padding.left + groupWidth * (index + 0.5)
+        const text = document.createElementNS(
+          'http://www.w3.org/2000/svg',
+          'text'
+        )
+        text.setAttribute('x', x)
+        text.setAttribute('y', element.height - padding.bottom + 20)
+        text.setAttribute('text-anchor', 'middle')
+        text.setAttribute('font-size', '12px')
+        text.textContent = label
+        svg.appendChild(text)
+      })
+
+      // 7. 绘制图例
+      series.forEach((serie, index) => {
+        const legendX = padding.left + index * 120
+        const legendY = 20
+
+        const rect = document.createElementNS(
+          'http://www.w3.org/2000/svg',
+          'rect'
+        )
+        rect.setAttribute('x', legendX)
+        rect.setAttribute('y', legendY)
+        rect.setAttribute('width', 15)
+        rect.setAttribute('height', 15)
+        rect.setAttribute(
+          'fill',
+          element.colors[index] || `hsl(${index * 60}, 70%, 50%)`
+        )
+        svg.appendChild(rect)
+
+        const text = document.createElementNS(
+          'http://www.w3.org/2000/svg',
+          'text'
+        )
+        text.setAttribute('x', legendX + 25)
+        text.setAttribute('y', legendY + 12)
+        text.setAttribute('font-size', '12px')
+        text.textContent = serie.key
+        svg.appendChild(text)
+      })
+    },
+    // 绘制网格和刻度
+    drawGrid (svg, padding, width, height, maxValue) {
+      const yTickCount = 5
+      for (let i = 0; i <= yTickCount; i++) {
+        const y = padding.top + (height * i) / yTickCount
+        const value = maxValue - (maxValue * i) / yTickCount
+
+        // 刻度线
+        const tick = document.createElementNS(
+          'http://www.w3.org/2000/svg',
+          'line'
+        )
+        tick.setAttribute('x1', padding.left - 5)
+        tick.setAttribute('x2', padding.left)
+        tick.setAttribute('y1', y)
+        tick.setAttribute('y2', y)
+        tick.setAttribute('stroke', '#000')
+        svg.appendChild(tick)
+
+        // 刻度值
+        const label = document.createElementNS(
+          'http://www.w3.org/2000/svg',
+          'text'
+        )
+        label.setAttribute('x', padding.left - 8)
+        label.setAttribute('y', y + 4)
+        label.setAttribute('text-anchor', 'end')
+        label.setAttribute('font-size', '12px')
+        label.textContent = value.toFixed(1)
+        svg.appendChild(label)
+      }
+    },
+    // 绘制类别标签
+    drawCategoryLabels (svg, categories, padding, groupWidth, barDir) {
+      Object.values(categories).forEach((label, index) => {
+        const text = document.createElementNS(
+          'http://www.w3.org/2000/svg',
+          'text'
+        )
+        if (barDir === 'col') {
+          text.setAttribute('x', padding.left + groupWidth * (index + 0.5))
+          text.setAttribute(
+            'y',
+            svg.height.baseVal.value - padding.bottom + 20
+          )
+          text.setAttribute('text-anchor', 'middle')
+        } else {
+          text.setAttribute('x', padding.left - 10)
+          text.setAttribute('y', padding.top + groupWidth * (index + 0.5))
+          text.setAttribute('text-anchor', 'end')
+          text.setAttribute('dominant-baseline', 'middle')
+        }
+        text.setAttribute('font-size', '12px')
+        text.textContent = label
+        svg.appendChild(text)
+      })
+    },
+    // 绘制图例
+    drawLegend (svg, series, colors, padding) {
+      series.forEach((serie, index) => {
+        const legendX = padding.left + index * 100
+        const legendY = 20
+
+        const rect = document.createElementNS(
+          'http://www.w3.org/2000/svg',
+          'rect'
+        )
+        rect.setAttribute('x', legendX)
+        rect.setAttribute('y', legendY)
+        rect.setAttribute('width', 15)
+        rect.setAttribute('height', 15)
+        rect.setAttribute(
+          'fill',
+          colors[index] || `hsl(${index * 60}, 70%, 50%)`
+        )
+        svg.appendChild(rect)
+
+        const text = document.createElementNS(
+          'http://www.w3.org/2000/svg',
+          'text'
+        )
+        text.setAttribute('x', legendX + 20)
+        text.setAttribute('y', legendY + 12)
+        text.setAttribute('font-size', '12px')
+        text.textContent = serie.key
+        svg.appendChild(text)
+      })
+    },
+    // 创建SmartArt图表元素
+    createDiagramElement (element) {
+      const el = document.createElement('div')
+      el.style.position = 'absolute'
+      el.style.top = element.top + 'px'
+      el.style.left = element.left + 'px'
+      el.style.width = element.width + 'px'
+      el.style.height = element.height + 'px'
+      el.style.zIndex = element.order
+      // 递归渲染SmartArt节点
+      const renderDiagramNode = (node) => {
+        const nodeEl = document.createElement('div')
+        nodeEl.style.position = 'absolute'
+        nodeEl.style.left = node.left + 'px'
+        nodeEl.style.top = node.top + 'px'
+        nodeEl.style.width = node.width + 'px'
+        nodeEl.style.height = node.height + 'px'
+
+        // 设置边框
+        if (node.borderWidth > 0) {
+          nodeEl.style.border = `${node.borderWidth}px ${node.borderType} ${node.borderColor}`
+          if (
+            node.borderStrokeDasharray &&
+            node.borderStrokeDasharray !== '0'
+          ) {
+            nodeEl.style.borderStyle = 'dashed'
+          }
+        }
+
+        // 设置背景填充
+        if (node.fill && node.fill.type === 'color') {
+          nodeEl.style.backgroundColor = node.fill.value
+        }
+
+        // 设置内容
+        if (node.content) {
+          nodeEl.innerHTML = this.convertPtToPxInContent(node.content)
+        }
+
+        // 设置垂直对齐
+        if (node.vAlign === 'mid') {
+          nodeEl.style.display = 'flex'
+          nodeEl.style.alignItems = 'center'
+          nodeEl.style.justifyContent = 'center'
+        }
+
+        // 设置翻转
+        const transforms = []
+        if (node.isFlipV) {
+          transforms.push('scaleY(-1)')
+        }
+        if (node.isFlipH) {
+          transforms.push('scaleX(-1)')
+        }
+        if (transforms.length > 0) {
+          nodeEl.style.transform = transforms.join(' ')
+        }
+
+        return nodeEl
+      }
+
+      // 按照 order 排序元素
+      const sortedElements = [...element.elements].sort(
+        (a, b) => a.order - b.order
+      )
+
+      // 渲染所有节点
+      sortedElements.forEach((node) => {
+        const nodeEl = renderDiagramNode(node)
+        el.appendChild(nodeEl)
+      })
+
+      return el
+    },
+    // 创建数学公式元素
+    createMathElement (element) {
+      const el = document.createElement('div')
+      el.style.position = 'absolute'
+      el.style.top = element.top + 'px'
+      el.style.left = element.left + 'px'
+      el.style.width = element.width + 'px'
+      el.style.height = element.height + 'px'
+
+      // 如果有公式图片,优先使用图片显示
+      if (element.picBase64) {
+        const img = document.createElement('img')
+        img.src = element.picBase64
+        img.style.width = '100%'
+        img.style.height = '100%'
+        img.style.objectFit = 'contain'
+        el.appendChild(img)
+        return el
+      }
+
+      // 如果有 LaTeX 表达式,使用 MathJax 渲染
+      if (element.latex) {
+        // 创建公式容器
+        const mathContainer = document.createElement('div')
+        mathContainer.style.width = '100%'
+        mathContainer.style.height = '100%'
+        mathContainer.style.display = 'flex'
+        mathContainer.style.alignItems = 'center'
+        mathContainer.style.justifyContent = 'center'
+
+        // 添加 LaTeX 公式
+        mathContainer.innerHTML = `\\[${element.latex}\\]`
+
+        // 如果 MathJax 不可用,显示原始 LaTeX
+        mathContainer.style.fontFamily = 'monospace'
+        mathContainer.style.whiteSpace = 'pre-wrap'
+        mathContainer.style.padding = '10px'
+        mathContainer.textContent = element.latex
+
+        el.appendChild(mathContainer)
+      }
+
+      return el
+    },
+    // 创建文本元素
+    createTextElement (element) {
+      const el = document.createElement('div')
+      if (element.content) {
+        // 转换内容中的pt单位为px单位
+        const convertedContent = this.convertPtToPxInContent(element.content)
+        el.innerHTML = convertedContent
+
+        // 设置文本样式
+        el.style.width = element.width + 'px'
+        el.style.height = element.height + 'px'
+        el.style.color = element.fontColor || '#000'
+        el.style.fontSize = element.fontSize
+        el.style.fontFamily = element.fontFamily || 'Arial'
+        el.style.textAlign = element.align || 'left'
+        el.style.fontWeight = element.bold ? 'bold' : 'normal'
+        el.style.fontStyle = element.italic ? 'italic' : 'normal'
+        el.style.textDecoration = element.underline ? 'underline' : 'none'
+        el.style.position = 'absolute'
+        el.style.top = element.top + 'px' || '0'
+        el.style.left = element.left + 'px' || '0'
+        el.style.whiteSpace = element.wrapText ? 'normal' : 'nowrap'
+        el.style.overflow = element.wrapText ? 'visible' : 'hidden'
+        el.style.textOverflow = element.wrapText ? 'ellipsis' : 'clip'
+        el.style.zIndex = element.order
+        el.style.whiteSpace = 'pre-wrap'
+        // 设置段落间距
+        el.style.lineHeight = element.lineHeight + 'px' || '1.2'
+        el.style.letterSpacing = element.charSpacing
+          ? `${element.charSpacing}px`
+          : 'normal'
+      }
+      return el
+    },
+    // 调整颜色明暗度
+    adjustBrightness (color, factor) {
+      if (color === 'transparent') return color
+      const hex = color.replace('#', '')
+      const r = Math.min(
+        255,
+        Math.round(parseInt(hex.substr(0, 2), 16) * factor)
+      )
+      const g = Math.min(
+        255,
+        Math.round(parseInt(hex.substr(2, 2), 16) * factor)
+      )
+      const b = Math.min(
+        255,
+        Math.round(parseInt(hex.substr(4, 2), 16) * factor)
+      )
+      return `#${r.toString(16).padStart(2, '0')}${g
+        .toString(16)
+        .padStart(2, '0')}${b.toString(16).padStart(2, '0')}`
+    },
+    // 新增函数:转换内容中的pt单位为px单位
+    convertPtToPxInContent (content) {
+      if (!content) return content
+      // 使用正则表达式查找并替换
+      return content.replace(
+        /font-size:\s*(\d+)(pt|PT)/g,
+        (match, size, unit) => {
+          // 将pt转换为px
+          const pxSize = size
+          return `font-size: ${pxSize}px`
+        }
+      )
+    },
+    processElements (elements, slideIndex) {
+      if (!elements || !Array.isArray(elements)) return
+
+      // 使用 Map 存储原始顺序,避免多次排序
+      const orderMap = new Map(
+        elements.map((el, index) => [el, el.order || index])
+      )
+
+      // 一次性排序
+      elements.sort((a, b) => orderMap.get(a) - orderMap.get(b))
+
+      const processElement = (element, baseOrder) => {
+        // 设置当前元素的 order
+        element.order = baseOrder
+
+        // 如果有子元素,递归处理
+        if (element.elements?.length > 0) {
+          element.elements
+            .sort((a, b) => (a.order || 0) - (b.order || 0))
+            .forEach((child, idx) => {
+              processElement(child, baseOrder * 10 + idx + 1)
+            })
+        }
+      }
+
+      // 处理每个顶层元素
+      elements.forEach((element, index) => {
+        processElement(element, (slideIndex + 1) * 10 + index + 1)
+      })
+    }
+  }
+}
+</script>
+<style scoped lang="less">
+//滚动条
+.pptx-container {
+    width: 100%;
+    height: 100%;
+    z-index: 99;
+}
+</style>

+ 25 - 0
src/components/view_file/components/vendors/pptx/index.js

@@ -0,0 +1,25 @@
+/*
+ * @Author: LiZhiWei
+ * @Date: 2025-04-09 10:49:05
+ * @LastEditors: LiZhiWei
+ * @LastEditTime: 2025-04-15 10:28:33
+ * @Description:
+ */
+import Vue from 'vue'
+import PPT from './PPT.vue'
+import { parse } from 'pptxtojson'
+/**
+ * 渲染ppt
+ */
+export default async function render (buffer, target) {
+  const pptxJson = await parse(buffer)
+  const el = new Vue({
+    render: (h) =>
+      h(PPT, {
+        props: {
+          pptxJson
+        }
+      })
+  }).$mount(target)
+  return el
+}

+ 31 - 0
src/components/view_file/components/vendors/text/CodeViewer.vue

@@ -0,0 +1,31 @@
+<template>
+  <pre class="code-area">
+    {{ value }}
+  </pre>
+</template>
+
+<script>
+export default {
+  name: 'CodeEditor',
+  props: {
+    value: {
+      type: String,
+      description: '值'
+    }
+  }
+}
+</script>
+
+<style scoped>
+.code-area {
+  display: block;
+  margin: 0 auto;
+  font-size: 12px;
+  width: 1000px;
+  min-height: 500px;
+  background: #1f1f1f;
+  word-break: break-word;
+  white-space: break-spaces;
+  color: #5af117
+}
+</style>

+ 15 - 0
src/components/view_file/components/vendors/text/index.js

@@ -0,0 +1,15 @@
+import { readText } from '../../util'
+import Vue from 'vue'
+import CodeViewer from './CodeViewer'
+
+/**
+ * 渲染文本
+ * @param buffer 文本二进制内容
+ * @param target 目标
+ */
+export default async function renderText (buffer, target) {
+  const text = await readText(buffer)
+  return new Vue({
+    render: h => h(CodeViewer, { props: { value: text } })
+  }).$mount(target)
+}

+ 823 - 0
src/components/view_file/components/vendors/xlsx/Table.vue

@@ -0,0 +1,823 @@
+<template>
+  <div>
+    <div ref="spreadsheet" class="spreadsheet-container" id="spreadsheet"></div>
+  </div>
+</template>
+
+<script>
+import Spreadsheet from 'x-data-spreadsheet'
+import { indexedColors } from './color'
+import { zhCN } from 'handsontable/i18n'
+import _ from 'lodash'
+import tinycolor from 'tinycolor2'
+Spreadsheet.locale('zh-cn', zhCN)
+
+export default {
+  name: 'ExcelViewer',
+  props: {
+    workbook: Object
+  },
+  data () {
+    return {
+      spreadsheet: null,
+      themeColors: []
+    }
+  },
+  mounted () {
+    this.initSpreadsheet()
+  },
+  watch: {
+    workbook () {
+      this.parseTheme()
+      this.updateTable()
+    }
+  },
+  computed: {
+    sheets () {
+      if (this.workbook.worksheets) {
+        return this.workbook.worksheets.filter((sheet) => sheet._rows.length)
+      }
+      return []
+    }
+  },
+  methods: {
+    initSpreadsheet () {
+      // 优化性能配置
+      this.spreadsheet = new Spreadsheet(this.$refs.spreadsheet, {
+        view: {
+          height: () => document.documentElement.clientHeight - 120,
+          width: () => document.documentElement.clientWidth - 40
+        },
+        mode: 'read', // 只读模式
+        showToolbar: false,
+        showGrid: true,
+        showContextmenu: false, // 禁用右键菜单提高性能
+        multipleSheets: true,
+        rpx: 1, // 减少渲染计算
+        row: {
+          len: 100, // 限制初始行数
+          height: 25, // 固定行高
+          autoHeight: true
+        },
+        col: {
+          len: 26, // 限制初始列数
+          width: 50, // 固定列宽
+          indexWidth: 60, // 行索引宽度
+          minWidth: 30, // 最小列宽
+          autoWidth: true // 自动调整列宽
+        },
+        style: {
+          // 确保默认样式正确
+          bgcolor: '#ffffff',
+          color: '#333333',
+          align: 'left',
+          valign: 'middle',
+          textwrap: false,
+          strike: false,
+          underline: false,
+          italic: false,
+          bold: false,
+          fontSize: 12
+        }
+      }).loadData({})
+
+      // 延迟加载数据,避免初始化时卡死
+      this.$nextTick(() => {
+        this.parseTheme()
+        this.updateSpreadsheet()
+      })
+    },
+    updateSpreadsheet () {
+      if (!this.spreadsheet) return
+      // 显示加载状态
+      this.$refs.spreadsheet.classList.add('loading')
+      try {
+        const data = this.convertWorksheetToData()
+        if (this.spreadsheet) {
+          this.$refs.spreadsheet.innerHTML = ''
+        }
+        // 重新创建实例
+        this.spreadsheet = new Spreadsheet(this.$refs.spreadsheet, {
+          view: {
+            height: () => document.documentElement.clientHeight - 120,
+            width: () => document.documentElement.clientWidth - 40
+          },
+          mode: 'read',
+          showToolbar: false,
+          showGrid: true,
+          showContextmenu: false,
+          multipleSheets: true,
+          rpx: 1,
+          row: {
+            len: 100,
+            height: 25,
+            autoHeight: true
+          },
+          col: {
+            len: 26,
+            width: 50,
+            indexWidth: 60,
+            minWidth: 30,
+            autoWidth: true
+          }
+        })
+          .loadData(data)
+          .change((data) => {
+            console.log('数据已更改:', data)
+          })
+      } catch (error) {
+        console.error('加载Excel数据失败:', error)
+      } finally {
+        this.$refs.spreadsheet.classList.remove('loading')
+      }
+    },
+
+    convertWorksheetToData () {
+      let data = []
+      this.sheets.forEach((sheet) => {
+        const sheetIndex = sheet.id
+        const sheetData = {
+          name: sheet._name,
+          freeze: 'A1',
+          styles: [
+            {
+              bgcolor: '#ffffff',
+              color: '#000000',
+              align: 'left',
+              valign: 'middle',
+              fontSize: 12
+            }
+          ],
+          merges: [],
+          rows: {},
+          cols: {}
+        }
+        try {
+          // 预处理合并单元格
+          this.processMerges(sheet, sheetData)
+          // 预处理样式缓存,避免重复创建相同样式
+          const styleCache = new Map()
+          // 处理单元格数据
+          this.processRows(sheet, sheetData, styleCache)
+          // 处理列宽
+          this.processColumns(sheet, sheetData)
+          data.push(sheetData)
+        } catch (error) {
+          console.error('转换工作表数据失败:', error)
+          data[sheetIndex] = {
+            name: sheet._name || 'Sheet1',
+            rows: {},
+            cols: {}
+          }
+        }
+      })
+      return data
+    },
+
+    // 处理合并单元格
+    processMerges (sheet, sheetData) {
+      if (!sheet._merges) return
+
+      Object.values(sheet._merges).forEach((merge) => {
+        try {
+          const { top, left, bottom, right } = merge
+          sheetData.merges.push(
+            `${this.columnIndexToLetter(
+              left - 1
+            )}${top}:${this.columnIndexToLetter(right - 1)}${bottom}`
+          )
+        } catch (e) {
+          console.warn('处理合并单元格出错:', e)
+        }
+      })
+    },
+
+    // 处理行和单元格数据
+    processRows (sheet, sheetData, styleCache) {
+      // 增加最大行数限制,确保所有行都能显示
+      const maxRows = Math.min(sheet.rowCount || 50, 100)
+
+      // 预处理所有行,确保即使空行也能被处理
+      for (let rowIndex = 0; rowIndex < maxRows; rowIndex++) {
+        const row = sheet.getRow(rowIndex + 1)
+
+        // 初始化行数据和行高
+        let maxHeight = 25 // 最小行高
+        sheetData.rows[rowIndex] = {
+          cells: {},
+          height: maxHeight
+        }
+
+        // 如果行不存在,创建一个空行
+        if (!row || !row.cellCount) {
+          continue
+        }
+
+        // 处理行中的单元格
+        // 使用列数而不是cellCount,确保处理所有可能的单元格
+        const maxCols = Math.min(sheet.columnCount || 26, 100)
+        for (let colIndex = 0; colIndex < maxCols; colIndex++) {
+          try {
+            const cell = row.getCell(colIndex + 1)
+            if (!cell || (cell.type === 'null' && !cell.style)) {
+              // 对于空单元格,添加一个默认样式的空单元格
+              sheetData.rows[rowIndex].cells[colIndex] = {
+                text: '',
+                style: 0
+              }
+              continue
+            }
+
+            // 获取单元格文本和样式
+            const { text: cellText, height: cellHeight } =
+              this.getCellTextAndHeight(cell)
+            const styleIndex = this.getCellStyle(
+              cell,
+              styleCache,
+              sheetData.styles
+            )
+
+            // 更新行高
+            maxHeight = Math.max(maxHeight, cellHeight)
+
+            // 检查是否是合并单元格的起始单元格
+            const mergeInfo = Object.values(sheet._merges || {}).find(
+              (m) => m.top === row.number && m.left === colIndex + 1
+            )
+
+            // 设置单元格数据
+            if (mergeInfo) {
+              sheetData.rows[rowIndex].cells[colIndex] = {
+                text: cellText,
+                style: styleIndex,
+                merge: [
+                  mergeInfo.bottom - mergeInfo.top,
+                  mergeInfo.right - mergeInfo.left
+                ]
+              }
+            } else {
+              sheetData.rows[rowIndex].cells[colIndex] = {
+                text: cellText,
+                style: styleIndex
+              }
+            }
+          } catch (error) {
+            console.warn(
+              '处理单元格时出错:',
+              error,
+              '位置:',
+              rowIndex + 1,
+              colIndex + 1
+            )
+            sheetData.rows[rowIndex].cells[colIndex] = {
+              text: '',
+              style: 0
+            }
+          }
+        }
+
+        // 更新行高
+        sheetData.rows[rowIndex].height = maxHeight
+      }
+    },
+
+    // 获取单元格文本和计算高度
+    getCellTextAndHeight (cell) {
+      let cellText = ''
+      try {
+        // 处理不同类型的单元格内容
+        if (!cell || cell.value === null || cell.value === undefined) {
+          cellText = ''
+        } else if (
+          cell.type === 'date' ||
+          (cell.numFmt &&
+            (cell.numFmt.includes('yy') ||
+              cell.numFmt.includes('mm') ||
+              cell.numFmt.includes('dd') ||
+              cell.numFmt.includes('h') ||
+              cell.numFmt.includes('m:s')))
+        ) {
+          // 处理日期类型或带有日期格式的数字
+          try {
+            // 检查是否为日期值
+            let date
+            if (cell.value instanceof Date) {
+              date = cell.value
+            } else if (typeof cell.value === 'number') {
+              // Excel日期是从1900年1月1日开始的天数
+              // 需要转换为JavaScript日期
+              const excelEpoch = new Date(1899, 11, 30) // Excel的起始日期
+              date = new Date(
+                excelEpoch.getTime() + cell.value * 24 * 60 * 60 * 1000
+              )
+            } else {
+              date = new Date(cell.value)
+            }
+
+            if (!isNaN(date.getTime())) {
+              // 格式化日期
+              const year = date.getFullYear()
+              const month = (date.getMonth() + 1).toString().padStart(2, '0')
+              const day = date.getDate().toString().padStart(2, '0')
+
+              // 检查是否需要显示时间
+              if (
+                cell.numFmt &&
+                (cell.numFmt.includes('h') || cell.numFmt.includes('s'))
+              ) {
+                const hour = date.getHours().toString().padStart(2, '0')
+                const minute = date.getMinutes().toString().padStart(2, '0')
+                const second = date.getSeconds().toString().padStart(2, '0')
+                cellText = `${year}/${month}/${day} ${hour}:${minute}:${second}`
+              } else {
+                cellText = `${year}/${month}/${day}`
+              }
+            } else {
+              // 如果不是有效日期,使用原始文本
+              cellText = String(cell.text || cell.value || '')
+            }
+          } catch (e) {
+            console.warn('日期转换错误:', e)
+            cellText = String(cell.text || cell.value || '')
+          }
+        } else if (typeof cell.value === 'object' && cell.value !== null) {
+          if (cell.value.hyperlink) {
+            cellText = '[链接]'
+          } else if (cell.value.image) {
+            cellText = '[图片]'
+          } else if (cell.value.richText) {
+            cellText = cell.value.richText
+              .map((t) => String(t?.text || ''))
+              .join('')
+          } else {
+            cellText = String(cell.text || '')
+          }
+        } else if (cell.formula) {
+          // 检查公式结果是否可能是日期
+          if (
+            cell.numFmt &&
+            (cell.numFmt.includes('yy') ||
+              cell.numFmt.includes('mm') ||
+              cell.numFmt.includes('dd'))
+          ) {
+            try {
+              // 尝试将结果转换为日期
+              const excelEpoch = new Date(1899, 11, 30)
+              const date = new Date(
+                excelEpoch.getTime() + cell.result * 24 * 60 * 60 * 1000
+              )
+
+              if (!isNaN(date.getTime())) {
+                const year = date.getFullYear()
+                const month = (date.getMonth() + 1).toString().padStart(2, '0')
+                const day = date.getDate().toString().padStart(2, '0')
+                cellText = `${year}-${month}-${day}`
+              } else {
+                cellText = String(cell.result || cell.value || '')
+              }
+            } catch (e) {
+              cellText = String(cell.result || cell.value || '')
+            }
+          } else {
+            cellText =
+              cell.result !== undefined && cell.result !== null
+                ? String(cell.result)
+                : String(cell.value || '')
+          }
+        } else {
+          cellText =
+            cell.text !== undefined && cell.text !== null
+              ? String(cell.text)
+              : cell.value !== undefined && cell.value !== null
+                ? String(cell.value)
+                : ''
+        }
+      } catch (e) {
+        console.warn('获取单元格文本出错:', e)
+        cellText = '[格式错误]'
+      }
+
+      // 计算行高
+      const lineBreaks = ((cellText || '').match(/\n/g) || []).length + 1
+      const chars = [...(cellText || '')]
+      const lines = Math.ceil(chars.length / 40)
+      const neededHeight = Math.max(lineBreaks, lines) * 20
+
+      return { text: cellText, height: neededHeight }
+    },
+
+    // 获取单元格样式
+    getCellStyle (cell, styleCache, styles) {
+      // 创建样式对象
+      const cellStyle = {
+        bgcolor: '#ffffff',
+        color: '#000000',
+        align: cell.alignment?.horizontal || 'left',
+        valign: cell.alignment?.vertical || 'middle',
+        fontSize: cell.font?.size || 12,
+        textwrap: true,
+        bold: cell.font?.bold,
+        italic: cell.font?.italic,
+        underline: false,
+        strike: false
+      }
+
+      // 处理背景色
+      if (cell.fill && cell.fill.type === 'pattern') {
+        const fgColor = cell.fill.fgColor || {}
+
+        // 处理索引颜色
+        if (typeof fgColor.indexed === 'number') {
+          const indexedColor = indexedColors[fgColor.indexed]
+          if (indexedColor) {
+            cellStyle.bgcolor = `#${indexedColor}`
+          }
+        }
+        // 处理主题颜色
+        else if (typeof fgColor.theme === 'number') {
+          // Excel主题颜色索引映射
+          const themeColorMap = {
+            0: 1, // 浅色1
+            1: 0, // 深色1
+            2: 3, // 浅色2
+            3: 2, // 深色2
+            4: 4, // 强调色1
+            5: 5, // 强调色2
+            6: 6, // 强调色3
+            7: 7, // 强调色4
+            8: 8, // 强调色5
+            9: 9 // 强调色6
+          }
+
+          const mappedIndex =
+            themeColorMap[fgColor.theme] !== undefined
+              ? themeColorMap[fgColor.theme]
+              : fgColor.theme
+
+          if (this.themeColors[mappedIndex]) {
+            let color = `#${this.themeColors[mappedIndex]}`
+
+            // 应用色调调整
+            if (typeof fgColor.tint === 'number' && fgColor.tint !== 0) {
+              color = this.applyTint(color, fgColor.tint)
+            }
+
+            cellStyle.bgcolor = color
+          }
+        }
+        // 处理RGB颜色
+        else if (fgColor.rgb) {
+          cellStyle.bgcolor = `#${fgColor.rgb.substring(
+            fgColor.rgb.length - 6
+          )}`
+        }
+        // 处理ARGB颜色
+        else if (fgColor.argb) {
+          const color = this.convertArgbToHex(fgColor.argb)
+          if (color) cellStyle.bgcolor = color
+        }
+      }
+
+      // 处理字体颜色
+      if (cell.font && cell.font.color) {
+        const fontColor = cell.font.color
+
+        // 处理索引颜色
+        if (typeof fontColor.indexed === 'number') {
+          const indexedColor = indexedColors[fontColor.indexed]
+          if (indexedColor) {
+            cellStyle.color = `#${indexedColor}`
+          }
+        }
+        // 处理主题颜色
+        else if (typeof fontColor.theme === 'number') {
+          // Excel主题颜色索引映射
+          const themeColorMap = {
+            0: 1, // 浅色1
+            1: 0, // 深色1
+            2: 3, // 浅色2
+            3: 2, // 深色2
+            4: 4, // 强调色1
+            5: 5, // 强调色2
+            6: 6, // 强调色3
+            7: 7, // 强调色4
+            8: 8, // 强调色5
+            9: 9 // 强调色6
+          }
+
+          const mappedIndex =
+            themeColorMap[fontColor.theme] !== undefined
+              ? themeColorMap[fontColor.theme]
+              : fontColor.theme
+
+          if (this.themeColors[mappedIndex]) {
+            let color = `#${this.themeColors[mappedIndex]}`
+
+            // 应用色调调整
+            if (typeof fontColor.tint === 'number' && fontColor.tint !== 0) {
+              color = this.applyTint(color, fontColor.tint)
+            }
+
+            cellStyle.color = color
+          }
+        }
+        // 处理RGB颜色
+        else if (fontColor.rgb) {
+          cellStyle.color = `#${fontColor.rgb.substring(
+            fontColor.rgb.length - 6
+          )}`
+        }
+        // 处理ARGB颜色
+        else if (fontColor.argb) {
+          const color = this.convertArgbToHex(fontColor.argb)
+          if (color) cellStyle.color = color
+        }
+      }
+
+      // 使用样式缓存避免重复
+      const styleKey = JSON.stringify(cellStyle)
+      if (styleCache.has(styleKey)) {
+        return styleCache.get(styleKey)
+      }
+
+      // 添加新样式
+      styles.push(cellStyle)
+      const styleIndex = styles.length - 1
+      styleCache.set(styleKey, styleIndex)
+
+      return styleIndex
+    },
+    applyTint (hexColor, tint) {
+      try {
+        // 将十六进制颜色转换为RGB
+        const color = tinycolor(hexColor)
+        const rgb = color.toRgb()
+
+        // 根据Excel的色调算法调整颜色
+        const adjustColor = (value, tint) => {
+          let result
+          if (tint < 0) {
+            // 负色调值,使颜色变暗
+            result = value * (1 + tint)
+          } else {
+            // 正色调值,使颜色变亮
+            result = value + (255 - value) * tint
+          }
+          return Math.max(0, Math.min(255, Math.round(result)))
+        }
+
+        // 应用色调到每个RGB通道
+        const r = adjustColor(rgb.r, tint)
+        const g = adjustColor(rgb.g, tint)
+        const b = adjustColor(rgb.b, tint)
+
+        // 返回调整后的颜色
+        return tinycolor({ r, g, b }).toHexString()
+      } catch (e) {
+        console.warn('应用色调调整出错:', e)
+        return hexColor
+      }
+    },
+
+    // 处理列宽
+    processColumns (sheet, sheetData) {
+      const maxCols = Math.min(sheet.columnCount || 26, 50)
+      const colWidthCache = new Map()
+      const mergedCols = new Set()
+      // 收集合并单元格信息
+      if (sheet._merges) {
+        Object.values(sheet._merges).forEach((merge) => {
+          // 记录被合并的列
+          for (let col = merge.left; col <= merge.right; col++) {
+            mergedCols.add(col - 1)
+          }
+        })
+      }
+      // 先收集所有单元格的文本宽度
+      sheet.eachRow((row) => {
+        for (let colIndex = 0; colIndex < maxCols; colIndex++) {
+          try {
+            const cell = row.getCell(colIndex + 1)
+            if (!cell) continue
+            // 安全获取单元格文本
+            let cellText = ''
+            try {
+              if (cell.text !== undefined && cell.text !== null) {
+                cellText = String(cell.text)
+              } else if (cell.value !== undefined && cell.value !== null) {
+                if (typeof cell.value === 'object') {
+                  cellText = cell.value?.richText
+                    ? cell.value.richText
+                      .map((t) => String(t?.text || ''))
+                      .join('')
+                    : cell.value?.hyperlink
+                      ? '[链接]'
+                      : cell.value?.image
+                        ? '[图片]'
+                        : ''
+                } else {
+                  cellText = String(cell.value)
+                }
+              }
+            } catch (e) {
+              cellText = ''
+            }
+            // 计算文本宽度
+            if (cellText) {
+              // 检查是否是合并单元格的一部分
+              const isMerged = Object.values(sheet._merges || {}).some(
+                (merge) =>
+                  row.number >= merge.top &&
+                  row.number <= merge.bottom &&
+                  colIndex + 1 >= merge.left &&
+                  colIndex + 1 <= merge.right
+              )
+              // 如果是合并单元格,根据合并的列数调整宽度计算
+              if (isMerged) {
+                const merge = Object.values(sheet._merges || {}).find(
+                  (m) =>
+                    row.number >= m.top &&
+                    row.number <= m.bottom &&
+                    colIndex + 1 >= m.left &&
+                    colIndex + 1 <= m.right
+                )
+                if (merge && colIndex + 1 === merge.left) {
+                  // 只为合并单元格的第一列计算宽度
+                  const mergeWidth = merge.right - merge.left + 1
+                  const textWidth = [...cellText].reduce((width, char) => {
+                    return width + (/[\u4e00-\u9fa5]/.test(char) ? 2 : 1)
+                  }, 0)
+                  // 平均分配到每一列
+                  const avgWidth = Math.ceil(textWidth / mergeWidth)
+                  colWidthCache.set(
+                    colIndex,
+                    Math.max(colWidthCache.get(colIndex) || 0, avgWidth)
+                  )
+                }
+              } else {
+                // 普通单元格
+                const textWidth = [...cellText].reduce((width, char) => {
+                  return width + (/[\u4e00-\u9fa5]/.test(char) ? 2 : 1)
+                }, 0)
+
+                colWidthCache.set(
+                  colIndex,
+                  Math.max(colWidthCache.get(colIndex) || 0, textWidth)
+                )
+              }
+            }
+          } catch (e) {
+            // 忽略错误
+          }
+        }
+      })
+
+      // 设置列宽
+      for (let colIndex = 0; colIndex < maxCols; colIndex++) {
+        let maxWidth = colWidthCache.get(colIndex) || 0
+        // 对于合并单元格的列,限制最大宽度
+        if (mergedCols.has(colIndex)) {
+          maxWidth = Math.min(maxWidth, 30) // 限制合并列的宽度
+        }
+        // 设置合理的列宽范围
+        const width = Math.max(40, Math.min(300, maxWidth * 9))
+        sheetData.cols[colIndex] = {
+          width: width
+        }
+      }
+    },
+    // ARGB 转 Hex 颜色方法
+    convertArgbToHex (argb) {
+      if (!argb) return null
+      try {
+        const matches =
+          /^([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})$/i.exec(argb)
+        if (!matches) return null
+        return `#${matches[2]}${matches[3]}${matches[4]}`
+      } catch (e) {
+        console.error('Color conversion error:', e)
+        return null
+      }
+    },
+    columnIndexToLetter (index) {
+      let temp
+      let letter = ''
+      // 修复列索引转字母的算法
+      if (index < 0) return ''
+      do {
+        temp = index % 26
+        letter = String.fromCharCode(temp + 65) + letter
+        index = Math.floor(index / 26) - 1
+      } while (index >= 0)
+      return letter
+    },
+    parseTheme () {
+      const theme = this.workbook._themes?.theme1
+      if (!theme) {
+        // Office默认主题颜色
+        this.themeColors = [
+          'FFFFFF', // 白色 - 浅色1
+          '000000', // 黑色 - 深色1
+          'EEECE1', // 浅灰 - 浅色2
+          '1F497D', // 深灰 - 深色2
+          '4F81BD', // 蓝色 - 强调色1
+          'C0504D', // 红色 - 强调色2
+          '9BBB59', // 绿色 - 强调色3
+          '8064A2', // 紫色 - 强调色4
+          '4BACC6', // 青色 - 强调色5
+          'F79646' // 橙色 - 强调色6
+        ]
+        return
+      }
+      try {
+        const parser = new DOMParser()
+        const doc = parser.parseFromString(theme, 'text/xml')
+        // 获取颜色方案元素
+        const clrScheme = doc.getElementsByTagName('a:clrScheme')[0]
+        if (!clrScheme) {
+          throw new Error('找不到颜色方案元素')
+        }
+        // 初始化主题颜色数组
+        this.themeColors = []
+        const colorElements = Array.from(clrScheme.children)
+        // 处理每个颜色元素
+        for (const element of colorElements) {
+          let colorValue = null
+          // 查找颜色定义元素
+          const srgbClr = element.getElementsByTagName('a:srgbClr')[0]
+          const sysClr = element.getElementsByTagName('a:sysClr')[0]
+          if (srgbClr) {
+            colorValue = srgbClr.getAttribute('val')
+          } else if (sysClr) {
+            colorValue =
+              sysClr.getAttribute('lastClr') || sysClr.getAttribute('val')
+          }
+          this.themeColors.push(colorValue || 'FFFFFF')
+        }
+      } catch (error) {
+        console.error('解析主题颜色出错:', error)
+        this.themeColors = [
+          'FFFFFF', // 白色 - 浅色1
+          '000000', // 黑色 - 深色1
+          'EEECE1', // 浅灰 - 浅色2
+          '1F497D', // 深灰 - 深色2
+          '4F81BD', // 蓝色 - 强调色1
+          'C0504D', // 红色 - 强调色2
+          '9BBB59', // 绿色 - 强调色3
+          '8064A2', // 紫色 - 强调色4
+          '4BACC6', // 青色 - 强调色5
+          'F79646' // 橙色 - 强调色6
+        ]
+      }
+    }
+  }
+}
+</script>
+
+<style scoped>
+.spreadsheet-container {
+  width: 100%;
+  height: calc(100vh - 120px);
+  position: relative;
+}
+
+.spreadsheet-container.loading::after {
+  content: "加载中...";
+  position: absolute;
+  top: 0;
+  left: 0;
+  right: 0;
+  bottom: 0;
+  background: rgba(255, 255, 255, 0.7);
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  font-size: 18px;
+  z-index: 1000;
+}
+
+.sheet-btn {
+  border: 1px solid #ccc;
+  background-color: #f0f0f0;
+  cursor: pointer;
+  margin-right: 5px;
+  border-radius: 4px;
+  padding: 5px 15px;
+}
+
+.sheet-btn.active {
+  background-color: #4caf50;
+  color: white;
+  border-color: #388e3c;
+}
+
+.btn-group {
+  margin-bottom: 10px;
+  display: flex;
+  flex-wrap: wrap;
+  gap: 5px;
+  padding: 8px;
+  background-color: #f8f8f8;
+  border-radius: 4px;
+}
+</style>

+ 69 - 0
src/components/view_file/components/vendors/xlsx/color.js

@@ -0,0 +1,69 @@
+
+export const indexedColors = [
+  '000000',
+  'FFFFFF',
+  'FF0000',
+  '00FF00',
+  '0000FF',
+  'FFFF00',
+  'FF00FF',
+  '00FFFF',
+  '000000',
+  'FFFFFF',
+  'FF0000',
+  '00FF00',
+  '0000FF',
+  'FFFF00',
+  'FF00FF',
+  '00FFFF',
+  '800000',
+  '008000',
+  '000080',
+  '808000',
+  '800080',
+  '008080',
+  'C0C0C0',
+  '808080',
+  '9999FF',
+  '993366',
+  'FFFFCC',
+  'CCFFFF',
+  '660066',
+  'FF8080',
+  '0066CC',
+  'CCCCFF',
+  '000080',
+  'FF00FF',
+  'FFFF00',
+  '00FFFF',
+  '800080',
+  '800000',
+  '008080',
+  '0000FF',
+  '00CCFF',
+  'CCFFFF',
+  'CCFFCC',
+  'FFFF99',
+  '99CCFF',
+  'FF99CC',
+  'CC99FF',
+  'FFCC99',
+  '3366FF',
+  '33CCCC',
+  '99CC00',
+  'FFCC00',
+  'FF9900',
+  'FF6600',
+  '666699',
+  '969696',
+  '003366',
+  '339966',
+  '003300',
+  '333300',
+  '993300',
+  '993366',
+  '333399',
+  '333333',
+  'b7e0ff',
+  '00CCFF'
+]

+ 28 - 0
src/components/view_file/components/vendors/xlsx/index.js

@@ -0,0 +1,28 @@
+/*
+ * @Author: LiZhiWei
+ * @Date: 2025-04-09 10:49:05
+ * @LastEditors: LiZhiWei
+ * @LastEditTime: 2025-04-14 18:00:04
+ * @Description:
+ */
+import ExcelJS from 'exceljs'
+import Vue from 'vue'
+import Table from './Table.vue'
+import 'x-data-spreadsheet/dist/xspreadsheet.css'
+
+/**
+ * 渲染excel
+ */
+export default async function render (buffer, target) {
+  const workbook = await new ExcelJS.Workbook().xlsx.load(buffer)
+  const el = new Vue({
+    render: (h) =>
+      h(Table, {
+        props: {
+          workbook
+        }
+      })
+  }).$mount(target)
+  console.log('el', el)
+  return el
+}

+ 49 - 0
src/components/view_file/components/vendors/xlsx/util.js

@@ -0,0 +1,49 @@
+/*
+ * @Author: LiZhiWei
+ * @Date: 2025-04-09 10:49:05
+ * @LastEditors: LiZhiWei
+ * @LastEditTime: 2025-04-14 17:30:04
+ * @Description:
+ */
+// 深度扁平化routes
+export function flatten (routes) {
+  return routes.flatMap((route) =>
+    route.children ? [route, ...flatten(route.children)] : [route]
+  )
+}
+
+// 转化style对象为style字符串
+export function toStyleString (style) {
+  return [...style].map((key) => `${key}: ${style[key]}`).join(';')
+}
+
+// 修复矩阵的宽度
+export function fixMatrix (data, colLen) {
+  for (const row of data) {
+    for (let j = 0; j < colLen; j++) {
+      if (!row[j]) {
+        row[j] = ''
+      }
+    }
+  }
+  return data
+}
+
+// 首字母大写
+export function captain (str) {
+  return `${str.charAt(0).toUpperCase()}${str.slice(1)}`
+}
+
+// 连字符转驼峰
+export function camelCase (str) {
+  return str
+    .split('-')
+    .map((part, index) => {
+      if (index !== 0) {
+        return captain(part)
+      } else {
+        return part
+      }
+    })
+    .join('')
+}

+ 65 - 0
src/components/view_file/index.vue

@@ -0,0 +1,65 @@
+<!--
+ * @Author: ChenYaJin
+ * @Date: 2021-05-28 11:21:37
+ * @LastEditors: ChenYaJin
+ * @LastEditTime: 2021-06-21 10:33:48
+ * @Description: 文件柜-预览文件主页
+-->
+<template>
+  <w-layout>
+    <w-content :tabList="tabList" @change="onTabChange"></w-content>
+  </w-layout>
+</template>
+<script>
+import ViewFileComponent from './components/index'
+
+export default {
+  name: 'ViewFileIndex',
+  props: {
+    fileId: {
+      type: String,
+      default: null
+    },
+    file: {
+      type: Object,
+      default: () => {
+        return {}
+      }
+    }
+  },
+  data () {
+    const _this = this
+    return {
+      activeTab: 'fileDetail',
+      currentFile: {},
+      tabList: [
+        {
+          name: '文件预览',
+          key: 'fileDetail',
+          component: ViewFileComponent,
+          props: {
+            get activeTab () {
+              return _this.activeTab
+            },
+            get fileId () {
+              return _this.fileId
+            },
+            get file () {
+              return _this.file
+            },
+            onTurnBack: _this.handleTurnBack
+          }
+        }
+      ]
+    }
+  },
+  methods: {
+    onTabChange (tab) {
+      this.activeTab = tab
+    },
+    handleTurnBack (reFresh) {
+      this.$emit('turnBack', reFresh)
+    }
+  }
+}
+</script>

BIN
src/components/wc-page-tip/images/empty.png


BIN
src/components/wc-page-tip/images/error_data.png


BIN
src/components/wc-page-tip/images/no_auth.png


+ 9 - 0
src/components/wc-page-tip/index.js

@@ -0,0 +1,9 @@
+/*
+ * @Author: ChenYaJin
+ * @Date: 2021-07-02 16:56:40
+ * @LastEditors: ChenYaJin
+ * @LastEditTime: 2021-07-02 16:59:03
+ * @Description:
+ */
+import WcPageTip from './wc-page-tip.vue'
+export default WcPageTip

+ 93 - 0
src/components/wc-page-tip/wc-page-tip.vue

@@ -0,0 +1,93 @@
+<!--
+ * @Author: ChenYaJin
+ * @Date: 2021-07-02 16:02:41
+ * @LastEditors: mzr
+ * @LastEditTime: 2022-11-22 09:59:57
+ * @Description: 页面提示组件
+-->
+<template>
+  <div class="wc-tip">
+    <el-image :src="matchSrc" alt="" class="tip-icon" :class="size">
+      <div slot="error" class="image-slot">
+        <i class="el-icon-picture-outline"></i>
+      </div>
+    </el-image>
+    <div class="desc">
+      <slot>
+        <div v-html="tip"></div>
+      </slot>
+    </div>
+  </div>
+</template>
+<script>
+import empty from './images/empty.png'
+import noAuth from './images/no_auth.png'
+import errorData from './images/error_data.png'
+
+export default {
+  name: 'WcPageTip',
+  props: {
+    desc: {
+      type: String,
+      default: '暂无数据'
+    },
+    tip: {
+      type: String,
+      default: '暂无数据'
+    },
+    type: {
+      type: String,
+      default: 'empty'
+    },
+    size: {
+      type: String,
+      default: 'small' // medium / small / mini
+    }
+  },
+  data () {
+    return {
+      typeMenu: {
+        empty: empty,
+        noAuth: noAuth,
+        errorData: errorData
+      }
+    }
+  },
+  computed: {
+    matchSrc () {
+      return this.typeMenu[this.type]
+    }
+  }
+}
+</script>
+<style lang="less" scoped>
+.wc-tip {
+  font-size: 14px;
+  width: 100%;
+  height: 100%;
+  color: #c5c8ce;
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  justify-content: center;
+  .desc{
+    margin: 20px 0;
+    text-align: center;
+  }
+  .medium{
+    width: 320px;
+    height: 260px;
+  }
+  .small{
+    width: 160px;
+    height: 130px;
+  }
+  .mini{
+    width: 80px;
+    height: 65px;
+  }
+  p {
+    margin: 20px 0;
+  }
+}
+</style>

+ 61 - 0
src/config/index.ts

@@ -0,0 +1,61 @@
+/*
+ * @Author: WangQiBiao
+ * @LastEditors: ChenYaJin
+ * @Description: 全局参数设置
+ * @Date: 2019-03-12 09:40:46
+ * @LastEditTime: 2023-07-03 17:11:00
+ */
+import { Config } from '#/config'
+
+const REQUEST_MATCHING_HASH = `/9ad134a361f8d778` // 【9ad134a361f8d778 = 前端服务,用于后端接口匹配】
+
+const config: Config = {
+/**
+     * @description token在Cookie中存储的天数,默认1天
+     */
+  cookieExpires: 1,
+  /**
+     * @description 是否使用国际化,默认为false
+     *              如果不使用,则需要在路由中给需要在菜单中展示的路由设置meta: {title: 'xxx'}
+     *              用来在菜单中显示文字
+     */
+  useI18n: false,
+  /**
+     * @description api请求基础路径
+     */
+  baseUrl: {
+    pro: `${REQUEST_MATCHING_HASH}`, // 生产环境
+    test: `${REQUEST_MATCHING_HASH}` // 测试环境
+  },
+  // 图片路径
+  fileUrl: process.env.NODE_ENV === 'production' ? `${REQUEST_MATCHING_HASH}` : 'https://wy-test.huiguanjia.cn/9ad134a361f8d778',
+  // 上传图片路径
+  uploadFileUrl: process.env.NODE_ENV === 'production' ? `https://wy.huiguanjia.cn${REQUEST_MATCHING_HASH}` : 'https://wy-test.huiguanjia.cn/9ad134a361f8d778',
+  // 导出文件路径
+  exportUrl: process.env.NODE_ENV === 'production' ? `${REQUEST_MATCHING_HASH}` : '/api',
+  // 分享绘服务路径
+  shareUrl: process.env.NODE_ENV === 'production' ? 'https://m.huifuwu.cn' : 'http://pm-test.huiguanjia.cn', // https://m.huifuwu.cn
+  huiFuWuQrCodeUrl: 'https://m.huifuwu.cn',
+  /**
+     * 输入框最长值
+     */
+  maxTextNum: 20,
+  /**
+   * 版本tag
+   */
+  version: 'v2.5.9',
+  /**
+   * 钉钉消息通知
+   */
+  dingDingNotice: process.env.NODE_ENV === 'production' ? `${REQUEST_MATCHING_HASH}/risk/ding/front/chat/send` : '/api/risk/ding/front/chat/send',
+  /**
+   * 百度地图 ak
+   */
+  mapKey: process.env.NODE_ENV === 'production' ? '11hnWXWWGgQnCHVzL2RrILKjNbd4iSR9' : 'Grz9EBWKpYvTU4YesHQH5vcQ6f6gxpC2',
+  /**
+   * PDFjs 字体文件 cdn
+   */
+  PDFCmapURL: '/bcmaps/'
+}
+
+export default config

+ 25 - 0
src/main.ts

@@ -0,0 +1,25 @@
+/*
+ * @Author: LiZhiWei
+ * @Date: 2025-04-09 09:06:37
+ * @LastEditors: LiZhiWei
+ * @LastEditTime: 2025-04-16 08:38:26
+ * @Description:
+ */
+import Vue from "vue"
+import router from './router'
+
+import ElementUI from 'element-ui'
+import 'element-ui/lib/theme-chalk/index.css'
+import WisdomUI from 'wisdom-ui'
+import 'wisdom-ui/lib/theme/index.css'
+import App from "./App.vue"
+
+
+Vue.use(ElementUI, { size: 'small' })
+Vue.use(WisdomUI)
+
+new Vue({
+  el: "#app",
+  router,
+  render: (h) => h(App),
+})

+ 25 - 0
src/router/index.ts

@@ -0,0 +1,25 @@
+import Vue from 'vue'
+import VueRouter from 'vue-router'
+import ViewFile from '@/components/view_file'
+
+Vue.use(VueRouter)
+
+const routes = [
+  {
+    path: '/',
+    name: 'home',
+    component: ViewFile
+  },
+  {
+    path: '/preview/:id',
+    name: 'preview',
+    component: ViewFile,
+    props: true
+  }
+]
+
+const router = new VueRouter({
+  routes
+})
+
+export default router

+ 24 - 0
tsconfig.json

@@ -0,0 +1,24 @@
+{
+  "compilerOptions": {
+    "target": "ES2020",
+    "useDefineForClassFields": true,
+    "module": "ESNext",
+    "lib": ["ES2020", "DOM", "DOM.Iterable"],
+    "skipLibCheck": true,
+
+    /* Bundler mode */
+    "moduleResolution": "bundler",
+    "allowImportingTsExtensions": true,
+    "isolatedModules": true,
+    "moduleDetection": "force",
+    "noEmit": true,
+
+    /* Linting */
+    "strict": true,
+    "noUnusedLocals": true,
+    "noUnusedParameters": true,
+    "noFallthroughCasesInSwitch": true,
+    "noUncheckedSideEffectImports": true
+  },
+  "include": ["src"]
+}

+ 24 - 0
vite.config.ts

@@ -0,0 +1,24 @@
+/*
+ * @Author: LiZhiWei
+ * @Date: 2025-04-09 09:05:25
+ * @LastEditors: LiZhiWei
+ * @LastEditTime: 2025-04-15 15:39:42
+ * @Description:
+ */
+import { defineConfig } from "vite"
+import path from 'path'
+import { createVuePlugin } from "vite-plugin-vue2"
+export default defineConfig({
+    resolve: {
+        alias: {
+          '@': path.resolve(__dirname, 'src'),
+          '_c': path.resolve(__dirname, 'src/components'),
+          '_wc': path.resolve(__dirname, 'src/components/wisdom-city'),
+          '_vc': path.resolve(__dirname, 'src/view/components'),
+          '_conf': path.resolve(__dirname, 'src/config'),
+          '#': path.resolve(__dirname, 'types'),
+        },
+        extensions: ['.mjs', '.js', '.ts', '.jsx', '.tsx', '.json', '.vue']
+      },
+    plugins: [createVuePlugin()],
+})