Bladeren bron

init commit

sy 4 maanden geleden
commit
9792195e0b
100 gewijzigde bestanden met toevoegingen van 26526 en 0 verwijderingen
  1. 7 0
      .eslintignore
  2. 103 0
      .eslintrc-auto-import.json
  3. 58 0
      .eslintrc.js
  4. 10 0
      .gitignore
  5. 15 0
      .prettierignore
  6. 9 0
      .prettierrc
  7. 20 0
      index.html
  8. 63 0
      package.json
  9. 9306 0
      pnpm-lock.yaml
  10. 51 0
      src/App.vue
  11. 54 0
      src/api/acs/domain.ts
  12. 66 0
      src/api/base/base.ts
  13. 214 0
      src/api/base/client.ts
  14. 64 0
      src/api/base/region.ts
  15. 77 0
      src/api/biz/customer.ts
  16. 82 0
      src/api/biz/staff.ts
  17. 59 0
      src/api/dashboard/project.ts
  18. 114 0
      src/api/project-center/contract.ts
  19. 99 0
      src/api/project-center/document.ts
  20. 125 0
      src/api/project-center/info.ts
  21. 98 0
      src/api/project-center/member.ts
  22. 112 0
      src/api/project-center/outcome.ts
  23. 183 0
      src/api/project-center/plan-task.ts
  24. 139 0
      src/api/project-center/week-report.ts
  25. 127 0
      src/api/project-center/work.ts
  26. 102 0
      src/auto-imports.d.ts
  27. 557 0
      src/components/cu-circular-progress/cu-circular-progress.vue
  28. 70 0
      src/components/cu-custom/index.vue
  29. 164 0
      src/components/cu-dropdown/changelog.md
  30. 171 0
      src/components/cu-dropdown/components/cell.vue
  31. 200 0
      src/components/cu-dropdown/components/daterange.vue
  32. 220 0
      src/components/cu-dropdown/components/filter.vue
  33. 81 0
      src/components/cu-dropdown/components/part-dropdown-footer.vue
  34. 220 0
      src/components/cu-dropdown/components/picker.vue
  35. 654 0
      src/components/cu-dropdown/index.vue
  36. 443 0
      src/components/cu-dropdown/readme.md
  37. 151 0
      src/components/cu-dropdown/typing.ts
  38. 207 0
      src/components/cu-dropdown/utils.ts
  39. 63 0
      src/components/cu-progress/index.scss
  40. 82 0
      src/components/cu-progress/index.vue
  41. 52 0
      src/components/cu-progress/tools.ts
  42. 144 0
      src/components/cu-timeline/index.vue
  43. 471 0
      src/components/custom-button/custom-button.vue
  44. 18 0
      src/components/dynamic-form/boolean-component.vue
  45. 134 0
      src/components/dynamic-form/download-component.vue
  46. 185 0
      src/components/dynamic-form/index.vue
  47. 31 0
      src/components/dynamic-form/number-component.vue
  48. 176 0
      src/components/dynamic-form/select-component.vue
  49. 56 0
      src/components/dynamic-form/string-component.vue
  50. 169 0
      src/components/dynamic-form/upload-component.vue
  51. 357 0
      src/components/editor-list-item/editor-list-item.vue
  52. 42 0
      src/components/index.ts
  53. 390 0
      src/components/l-echart/canvas.js
  54. 252 0
      src/components/l-echart/l-echart.uvue
  55. 508 0
      src/components/l-echart/l-echart.vue
  56. 51 0
      src/components/l-echart/nvue.js
  57. 145 0
      src/components/l-echart/utils.js
  58. 133 0
      src/components/l-echart/uvue.uts
  59. 237 0
      src/components/next-search-more/next-search-more.vue
  60. 202 0
      src/components/next-tree/next-tree.vue
  61. 270 0
      src/components/next-tree/style.css
  62. 20 0
      src/components/page/index.ts
  63. 79 0
      src/components/part-dropdown-footer/part-dropdown-footer.vue
  64. 202 0
      src/components/process-work/index.vue
  65. 154 0
      src/components/selected-list-item/index.vue
  66. 280 0
      src/components/task-tree/circle-progress.vue
  67. 207 0
      src/components/task-tree/index.vue
  68. 380 0
      src/components/task-tree/style.css
  69. 187 0
      src/components/tree-select/index.vue
  70. 269 0
      src/components/tree-select/style.css
  71. 8 0
      src/config/iconfont.config.ts
  72. 720 0
      src/enums/api.ts
  73. 9 0
      src/env.d.ts
  74. 23 0
      src/hooks/chart-option.ts
  75. 26 0
      src/logics/mitt/useEmitt.ts
  76. 21 0
      src/main.ts
  77. 77 0
      src/manifest.json
  78. 376 0
      src/pages.json
  79. 68 0
      src/pages/authorize/index.vue
  80. 9 0
      src/pages/echart/index.vue
  81. 168 0
      src/pages/echart/my-work.vue
  82. 283 0
      src/pages/echart/plan-task.vue
  83. 228 0
      src/pages/echart/project-category.vue
  84. 192 0
      src/pages/echart/project-count.vue
  85. 49 0
      src/pages/echart/static/echarts.min.js
  86. 223 0
      src/pages/home/index.vue
  87. 106 0
      src/pages/profile/archive/index.vue
  88. 135 0
      src/pages/profile/contract/List.vue
  89. 658 0
      src/pages/profile/contract/add-contract.vue
  90. 307 0
      src/pages/profile/contract/add-payment.vue
  91. 45 0
      src/pages/profile/contract/detail.vue
  92. 316 0
      src/pages/profile/contract/edit-payment.vue
  93. 335 0
      src/pages/profile/contract/index.vue
  94. 129 0
      src/pages/profile/customer/List.vue
  95. 367 0
      src/pages/profile/customer/add-customer.vue
  96. 380 0
      src/pages/profile/customer/detail.vue
  97. 207 0
      src/pages/profile/customer/index.vue
  98. 301 0
      src/pages/profile/index.vue
  99. 297 0
      src/pages/profile/info/index.vue
  100. 22 0
      src/pages/profile/version/index.vue

+ 7 - 0
.eslintignore

@@ -0,0 +1,7 @@
+node_modules
+dist
+*.md
+*.woff
+*.ttf
+.vscode
+.idea

+ 103 - 0
.eslintrc-auto-import.json

@@ -0,0 +1,103 @@
+{
+  "globals": {
+    "Component": true,
+    "ComponentPublicInstance": true,
+    "ComputedRef": true,
+    "EffectScope": true,
+    "ExtractDefaultPropTypes": true,
+    "ExtractPropTypes": true,
+    "ExtractPublicPropTypes": true,
+    "InjectionKey": true,
+    "PropType": true,
+    "Ref": true,
+    "VNode": true,
+    "WritableComputedRef": true,
+    "acceptHMRUpdate": true,
+    "computed": true,
+    "createApp": true,
+    "createPinia": true,
+    "customRef": true,
+    "defineAsyncComponent": true,
+    "defineComponent": true,
+    "defineStore": true,
+    "effectScope": true,
+    "getActivePinia": true,
+    "getCurrentInstance": true,
+    "getCurrentScope": true,
+    "h": true,
+    "inject": true,
+    "isProxy": true,
+    "isReactive": true,
+    "isReadonly": true,
+    "isRef": true,
+    "mapActions": true,
+    "mapGetters": true,
+    "mapState": true,
+    "mapStores": true,
+    "mapWritableState": true,
+    "markRaw": true,
+    "nextTick": true,
+    "onActivated": true,
+    "onAddToFavorites": true,
+    "onBackPress": true,
+    "onBeforeMount": true,
+    "onBeforeUnmount": true,
+    "onBeforeUpdate": true,
+    "onDeactivated": true,
+    "onError": true,
+    "onErrorCaptured": true,
+    "onHide": true,
+    "onLaunch": true,
+    "onLoad": true,
+    "onMounted": true,
+    "onNavigationBarButtonTap": true,
+    "onNavigationBarSearchInputChanged": true,
+    "onNavigationBarSearchInputClicked": true,
+    "onNavigationBarSearchInputConfirmed": true,
+    "onNavigationBarSearchInputFocusChanged": true,
+    "onPageNotFound": true,
+    "onPageScroll": true,
+    "onPullDownRefresh": true,
+    "onReachBottom": true,
+    "onReady": true,
+    "onRenderTracked": true,
+    "onRenderTriggered": true,
+    "onResize": true,
+    "onScopeDispose": true,
+    "onServerPrefetch": true,
+    "onShareAppMessage": true,
+    "onShareTimeline": true,
+    "onShow": true,
+    "onTabItemTap": true,
+    "onThemeChange": true,
+    "onUnhandledRejection": true,
+    "onUnload": true,
+    "onUnmounted": true,
+    "onUpdated": true,
+    "provide": true,
+    "reactive": true,
+    "readonly": true,
+    "ref": true,
+    "resolveComponent": true,
+    "setActivePinia": true,
+    "setMapStoreSuffix": true,
+    "shallowReactive": true,
+    "shallowReadonly": true,
+    "shallowRef": true,
+    "storeToRefs": true,
+    "toRaw": true,
+    "toRef": true,
+    "toRefs": true,
+    "toValue": true,
+    "triggerRef": true,
+    "unref": true,
+    "useAttrs": true,
+    "useCssModule": true,
+    "useCssVars": true,
+    "useSlots": true,
+    "watch": true,
+    "watchEffect": true,
+    "watchPostEffect": true,
+    "watchSyncEffect": true
+  }
+}

+ 58 - 0
.eslintrc.js

@@ -0,0 +1,58 @@
+// .eslintrc.js
+module.exports = {
+    root: true,
+    env: {
+      browser: true,
+      node: true,
+      es6: true
+    },
+    parser: 'vue-eslint-parser',
+    parserOptions: {
+      parser: '@typescript-eslint/parser',
+      ecmaVersion: 2020,
+      sourceType: 'module',
+      jsxPragma: 'React',
+      ecmaFeatures: {
+        jsx: true,
+        tsx: true
+      }
+    },
+    plugins: ['@typescript-eslint', 'prettier', 'import'],
+    extends: [
+      'eslint:recommended',
+      'plugin:@typescript-eslint/recommended',
+      'plugin:vue/vue3-recommended',
+      'prettier'
+    ],
+    overrides: [
+      {
+        files: ['*.ts', '*.tsx', '*.vue'],
+        rules: {
+          'no-undef': 'off'
+        }
+      }
+    ],
+    rules: {
+      'no-unused-vars': ['error', { varsIgnorePattern: '.*', args: 'none' }],
+      '@typescript-eslint/no-unused-vars': 'off',
+      'vue/component-name-in-template-casing': [
+        'error',
+        'kebab-case',
+        {
+          registeredComponentsOnly: false,
+          ignores: []
+        }
+      ],
+      'vue/prop-name-casing': ['error', 'camelCase'],
+      'vue/require-v-for-key': 'error',
+      'vue/no-use-v-if-with-v-for': [
+        'error',
+        {
+          allowUsingIterationVar: false
+        }
+      ],
+      'vue/v-bind-style': ['error', 'shorthand'],
+      'vue/v-on-style': ['error', 'shorthand'],
+      'no-useless-escape': 0
+    }
+  }

+ 10 - 0
.gitignore

@@ -0,0 +1,10 @@
+node_modules
+.DS_Store
+dist
+dist-ssr
+*.local
+node_modules
+.DS_Store
+dist
+dist-ssr
+*.local

+ 15 - 0
.prettierignore

@@ -0,0 +1,15 @@
+**/*.svg
+**/*.ico
+package.json
+package-lock.json
+/dist
+.DS_Store
+.eslintignore
+*.png
+.editorconfig
+.gitignore
+.prettierignore
+.eslintcache
+*.lock
+yarn-error.log
+**/node_modules/**

+ 9 - 0
.prettierrc

@@ -0,0 +1,9 @@
+{
+  "useTabs": false,
+  "tabWidth": 2,
+  "printWidth": 80,
+  "singleQuote": true,
+  "trailingComma": "none",
+  "semi": false,
+  "endOfLine": "auto"
+}

+ 20 - 0
index.html

@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<html lang="en">
+  <head>
+    <meta charset="UTF-8" />
+    <script>
+      var coverSupport = 'CSS' in window && typeof CSS.supports === 'function' && (CSS.supports('top: env(a)') ||
+        CSS.supports('top: constant(a)'))
+      document.write(
+        '<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0' +
+        (coverSupport ? ', viewport-fit=cover' : '') + '" />')
+    </script>
+    <title></title>
+    <!--preload-links-->
+    <!--app-context-->
+  </head>
+  <body>
+    <div id="app"><!--app-html--></div>
+    <script type="module" src="/src/main.ts"></script>
+  </body>
+</html>

+ 63 - 0
package.json

@@ -0,0 +1,63 @@
+{
+  "name": "my-mini-project",
+  "version": "0.0.0",
+  "scripts": {
+    "dev:mp-alipay": "uni -p mp-alipay",
+    "dev:mp-weixin": "uni -p mp-weixin",
+    "update:iconfont": "node node_modules/iconfont-tools-cli/lib/cli.js --config src/config/iconfont.config.ts --to src/static/style/",
+    "build:mp-alipay": "uni build -p mp-alipay",
+    "build:mp-weixin": "uni build -p mp-weixin"
+  },
+  "dependencies": {
+    "@dcloudio/uni-app": "3.0.0-4000720240327002",
+    "@dcloudio/uni-app-plus": "3.0.0-4000720240327002",
+    "@dcloudio/uni-components": "3.0.0-4000720240327002",
+    "@dcloudio/uni-mp-alipay": "3.0.0-4000720240327002",
+    "@dcloudio/uni-mp-weixin": "3.0.0-4000720240327002",
+    "@dcloudio/uni-ui": "^1.5.5",
+    "@typescript-eslint/eslint-plugin": "^7.10.0",
+    "@typescript-eslint/parser": "^7.10.0",
+    "date-fns": "^3.6.0",
+    "echarts": "^5.5.0",
+    "eslint": "^9.3.0",
+    "eslint-config-prettier": "^9.1.0",
+    "eslint-plugin-import": "^2.29.1",
+    "eslint-plugin-prettier": "^5.1.3",
+    "eslint-plugin-vue": "^9.26.0",
+    "iconfont-tools-cli": "^1.3.1",
+    "mini-html-parser2": "^0.3.0",
+    "pinia": "2.0.36",
+    "pinia-plugin-unistorage": "^0.0.19",
+    "ts-md5": "^1.2.11",
+    "vue": "^3.2.37"
+  },
+  "devDependencies": {
+    "@dcloudio/types": "^3.0.7",
+    "@dcloudio/uni-automator": "3.0.0-4000720240327002",
+    "@dcloudio/uni-cli-shared": "3.0.0-4000720240327002",
+    "@dcloudio/uni-stacktracey": "3.0.0-4000720240327002",
+    "@dcloudio/vite-plugin-uni": "3.0.0-4000720240327002",
+    "@types/node": "^20.12.12",
+    "@vue/runtime-core": "^3.3.11",
+    "sass": "^1.77.2",
+    "sass-loader": "^14.2.1",
+    "typescript": "^4.7.4",
+    "unplugin-auto-import": "^0.16.4",
+    "vite": "4.3.5",
+    "vue-eslint-parser": "^9.4.2",
+    "vue-tsc": "^1.0.24"
+  },
+  "uni-app": {
+    "scripts": {
+      "mp-dingtalk": {
+        "title": "钉钉小程序",
+        "env": {
+          "UNI_PLATFORM": "mp-alipay"
+        },
+        "define": {
+          "MP-DINGTALK": true
+        }
+      }
+    }
+  }
+}

+ 9306 - 0
pnpm-lock.yaml

@@ -0,0 +1,9306 @@
+lockfileVersion: '9.0'
+
+settings:
+  autoInstallPeers: true
+  excludeLinksFromLockfile: false
+
+importers:
+
+  .:
+    dependencies:
+      '@dcloudio/uni-app':
+        specifier: 3.0.0-4000720240327002
+        version: 3.0.0-4000720240327002(@dcloudio/types@3.4.8)(postcss@8.4.38)(rollup@3.29.4)(vue@3.4.30(typescript@4.9.5))
+      '@dcloudio/uni-app-plus':
+        specifier: 3.0.0-4000720240327002
+        version: 3.0.0-4000720240327002(postcss@8.4.38)(rollup@3.29.4)(vite@4.3.5(@types/node@20.14.9)(sass@1.77.6)(terser@5.31.1))(vue@3.4.30(typescript@4.9.5))
+      '@dcloudio/uni-components':
+        specifier: 3.0.0-4000720240327002
+        version: 3.0.0-4000720240327002(postcss@8.4.38)(rollup@3.29.4)(vue@3.4.30(typescript@4.9.5))
+      '@dcloudio/uni-mp-alipay':
+        specifier: 3.0.0-4000720240327002
+        version: 3.0.0-4000720240327002(postcss@8.4.38)(rollup@3.29.4)(vue@3.4.30(typescript@4.9.5))
+      '@dcloudio/uni-mp-weixin':
+        specifier: 3.0.0-4000720240327002
+        version: 3.0.0-4000720240327002(postcss@8.4.38)(rollup@3.29.4)(vue@3.4.30(typescript@4.9.5))
+      '@dcloudio/uni-ui':
+        specifier: ^1.5.5
+        version: 1.5.5
+      '@typescript-eslint/eslint-plugin':
+        specifier: ^7.10.0
+        version: 7.14.1(@typescript-eslint/parser@7.14.1(eslint@9.5.0)(typescript@4.9.5))(eslint@9.5.0)(typescript@4.9.5)
+      '@typescript-eslint/parser':
+        specifier: ^7.10.0
+        version: 7.14.1(eslint@9.5.0)(typescript@4.9.5)
+      date-fns:
+        specifier: ^3.6.0
+        version: 3.6.0
+      echarts:
+        specifier: ^5.5.0
+        version: 5.5.0
+      eslint:
+        specifier: ^9.3.0
+        version: 9.5.0
+      eslint-config-prettier:
+        specifier: ^9.1.0
+        version: 9.1.0(eslint@9.5.0)
+      eslint-plugin-import:
+        specifier: ^2.29.1
+        version: 2.29.1(@typescript-eslint/parser@7.14.1(eslint@9.5.0)(typescript@4.9.5))(eslint@9.5.0)
+      eslint-plugin-prettier:
+        specifier: ^5.1.3
+        version: 5.1.3(eslint-config-prettier@9.1.0(eslint@9.5.0))(eslint@9.5.0)(prettier@3.3.2)
+      eslint-plugin-vue:
+        specifier: ^9.26.0
+        version: 9.26.0(eslint@9.5.0)
+      iconfont-tools-cli:
+        specifier: ^1.3.1
+        version: 1.3.1
+      mini-html-parser2:
+        specifier: ^0.3.0
+        version: 0.3.0
+      pinia:
+        specifier: 2.0.36
+        version: 2.0.36(typescript@4.9.5)(vue@3.4.30(typescript@4.9.5))
+      pinia-plugin-unistorage:
+        specifier: ^0.0.19
+        version: 0.0.19(typescript@4.9.5)(vue@3.4.30(typescript@4.9.5))
+      ts-md5:
+        specifier: ^1.2.11
+        version: 1.3.1
+      vue:
+        specifier: ^3.2.37
+        version: 3.4.30(typescript@4.9.5)
+    devDependencies:
+      '@dcloudio/types':
+        specifier: ^3.0.7
+        version: 3.4.8
+      '@dcloudio/uni-automator':
+        specifier: 3.0.0-4000720240327002
+        version: 3.0.0-4000720240327002(jest-environment-node@27.5.1)(jest@27.0.4)(postcss@8.4.38)(rollup@3.29.4)(vue@3.4.30(typescript@4.9.5))
+      '@dcloudio/uni-cli-shared':
+        specifier: 3.0.0-4000720240327002
+        version: 3.0.0-4000720240327002(postcss@8.4.38)(rollup@3.29.4)(vue@3.4.30(typescript@4.9.5))
+      '@dcloudio/uni-stacktracey':
+        specifier: 3.0.0-4000720240327002
+        version: 3.0.0-4000720240327002
+      '@dcloudio/vite-plugin-uni':
+        specifier: 3.0.0-4000720240327002
+        version: 3.0.0-4000720240327002(postcss@8.4.38)(rollup@3.29.4)(vite@4.3.5(@types/node@20.14.9)(sass@1.77.6)(terser@5.31.1))(vue@3.4.30(typescript@4.9.5))
+      '@types/node':
+        specifier: ^20.12.12
+        version: 20.14.9
+      '@vue/runtime-core':
+        specifier: ^3.3.11
+        version: 3.4.30
+      sass:
+        specifier: ^1.77.2
+        version: 1.77.6
+      sass-loader:
+        specifier: ^14.2.1
+        version: 14.2.1(sass@1.77.6)
+      typescript:
+        specifier: ^4.7.4
+        version: 4.9.5
+      unplugin-auto-import:
+        specifier: ^0.16.4
+        version: 0.16.7(rollup@3.29.4)
+      vite:
+        specifier: 4.3.5
+        version: 4.3.5(@types/node@20.14.9)(sass@1.77.6)(terser@5.31.1)
+      vue-eslint-parser:
+        specifier: ^9.4.2
+        version: 9.4.3(eslint@9.5.0)
+      vue-tsc:
+        specifier: ^1.0.24
+        version: 1.8.27(typescript@4.9.5)
+
+packages:
+
+  '@ampproject/remapping@2.3.0':
+    resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==}
+    engines: {node: '>=6.0.0'}
+
+  '@antfu/utils@0.7.10':
+    resolution: {integrity: sha512-+562v9k4aI80m1+VuMHehNJWLOFjBnXn3tdOitzD0il5b7smkSBal4+a3oKiQTbrwMmN/TBUMDvbdoWDehgOww==}
+
+  '@babel/code-frame@7.24.7':
+    resolution: {integrity: sha512-BcYH1CVJBO9tvyIZ2jVeXgSIMvGZ2FDRvDdOIVQyuklNKSsx+eppDEBq/g47Ayw+RqNFE+URvOShmf+f/qwAlA==}
+    engines: {node: '>=6.9.0'}
+
+  '@babel/compat-data@7.24.7':
+    resolution: {integrity: sha512-qJzAIcv03PyaWqxRgO4mSU3lihncDT296vnyuE2O8uA4w3UHWI4S3hgeZd1L8W1Bft40w9JxJ2b412iDUFFRhw==}
+    engines: {node: '>=6.9.0'}
+
+  '@babel/core@7.24.7':
+    resolution: {integrity: sha512-nykK+LEK86ahTkX/3TgauT0ikKoNCfKHEaZYTUVupJdTLzGNvrblu4u6fa7DhZONAltdf8e662t/abY8idrd/g==}
+    engines: {node: '>=6.9.0'}
+
+  '@babel/generator@7.24.7':
+    resolution: {integrity: sha512-oipXieGC3i45Y1A41t4tAqpnEZWgB/lC6Ehh6+rOviR5XWpTtMmLN+fGjz9vOiNRt0p6RtO6DtD0pdU3vpqdSA==}
+    engines: {node: '>=6.9.0'}
+
+  '@babel/helper-annotate-as-pure@7.24.7':
+    resolution: {integrity: sha512-BaDeOonYvhdKw+JoMVkAixAAJzG2jVPIwWoKBPdYuY9b452e2rPuI9QPYh3KpofZ3pW2akOmwZLOiOsHMiqRAg==}
+    engines: {node: '>=6.9.0'}
+
+  '@babel/helper-builder-binary-assignment-operator-visitor@7.24.7':
+    resolution: {integrity: sha512-xZeCVVdwb4MsDBkkyZ64tReWYrLRHlMN72vP7Bdm3OUOuyFZExhsHUUnuWnm2/XOlAJzR0LfPpB56WXZn0X/lA==}
+    engines: {node: '>=6.9.0'}
+
+  '@babel/helper-compilation-targets@7.24.7':
+    resolution: {integrity: sha512-ctSdRHBi20qWOfy27RUb4Fhp07KSJ3sXcuSvTrXrc4aG8NSYDo1ici3Vhg9bg69y5bj0Mr1lh0aeEgTvc12rMg==}
+    engines: {node: '>=6.9.0'}
+
+  '@babel/helper-create-class-features-plugin@7.24.7':
+    resolution: {integrity: sha512-kTkaDl7c9vO80zeX1rJxnuRpEsD5tA81yh11X1gQo+PhSti3JS+7qeZo9U4RHobKRiFPKaGK3svUAeb8D0Q7eg==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0
+
+  '@babel/helper-create-regexp-features-plugin@7.24.7':
+    resolution: {integrity: sha512-03TCmXy2FtXJEZfbXDTSqq1fRJArk7lX9DOFC/47VthYcxyIOx+eXQmdo6DOQvrbpIix+KfXwvuXdFDZHxt+rA==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0
+
+  '@babel/helper-define-polyfill-provider@0.6.2':
+    resolution: {integrity: sha512-LV76g+C502biUK6AyZ3LK10vDpDyCzZnhZFXkH1L75zHPj68+qc8Zfpx2th+gzwA2MzyK+1g/3EPl62yFnVttQ==}
+    peerDependencies:
+      '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0
+
+  '@babel/helper-environment-visitor@7.24.7':
+    resolution: {integrity: sha512-DoiN84+4Gnd0ncbBOM9AZENV4a5ZiL39HYMyZJGZ/AZEykHYdJw0wW3kdcsh9/Kn+BRXHLkkklZ51ecPKmI1CQ==}
+    engines: {node: '>=6.9.0'}
+
+  '@babel/helper-function-name@7.24.7':
+    resolution: {integrity: sha512-FyoJTsj/PEUWu1/TYRiXTIHc8lbw+TDYkZuoE43opPS5TrI7MyONBE1oNvfguEXAD9yhQRrVBnXdXzSLQl9XnA==}
+    engines: {node: '>=6.9.0'}
+
+  '@babel/helper-hoist-variables@7.24.7':
+    resolution: {integrity: sha512-MJJwhkoGy5c4ehfoRyrJ/owKeMl19U54h27YYftT0o2teQ3FJ3nQUf/I3LlJsX4l3qlw7WRXUmiyajvHXoTubQ==}
+    engines: {node: '>=6.9.0'}
+
+  '@babel/helper-member-expression-to-functions@7.24.7':
+    resolution: {integrity: sha512-LGeMaf5JN4hAT471eJdBs/GK1DoYIJ5GCtZN/EsL6KUiiDZOvO/eKE11AMZJa2zP4zk4qe9V2O/hxAmkRc8p6w==}
+    engines: {node: '>=6.9.0'}
+
+  '@babel/helper-module-imports@7.22.15':
+    resolution: {integrity: sha512-0pYVBnDKZO2fnSPCrgM/6WMc7eS20Fbok+0r88fp+YtWVLZrp4CkafFGIp+W0VKw4a22sgebPT99y+FDNMdP4w==}
+    engines: {node: '>=6.9.0'}
+
+  '@babel/helper-module-imports@7.24.7':
+    resolution: {integrity: sha512-8AyH3C+74cgCVVXow/myrynrAGv+nTVg5vKu2nZph9x7RcRwzmh0VFallJuFTZ9mx6u4eSdXZfcOzSqTUm0HCA==}
+    engines: {node: '>=6.9.0'}
+
+  '@babel/helper-module-transforms@7.24.7':
+    resolution: {integrity: sha512-1fuJEwIrp+97rM4RWdO+qrRsZlAeL1lQJoPqtCYWv0NL115XM93hIH4CSRln2w52SqvmY5hqdtauB6QFCDiZNQ==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0
+
+  '@babel/helper-optimise-call-expression@7.24.7':
+    resolution: {integrity: sha512-jKiTsW2xmWwxT1ixIdfXUZp+P5yURx2suzLZr5Hi64rURpDYdMW0pv+Uf17EYk2Rd428Lx4tLsnjGJzYKDM/6A==}
+    engines: {node: '>=6.9.0'}
+
+  '@babel/helper-plugin-utils@7.24.7':
+    resolution: {integrity: sha512-Rq76wjt7yz9AAc1KnlRKNAi/dMSVWgDRx43FHoJEbcYU6xOWaE2dVPwcdTukJrjxS65GITyfbvEYHvkirZ6uEg==}
+    engines: {node: '>=6.9.0'}
+
+  '@babel/helper-remap-async-to-generator@7.24.7':
+    resolution: {integrity: sha512-9pKLcTlZ92hNZMQfGCHImUpDOlAgkkpqalWEeftW5FBya75k8Li2ilerxkM/uBEj01iBZXcCIB/bwvDYgWyibA==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0
+
+  '@babel/helper-replace-supers@7.24.7':
+    resolution: {integrity: sha512-qTAxxBM81VEyoAY0TtLrx1oAEJc09ZK67Q9ljQToqCnA+55eNwCORaxlKyu+rNfX86o8OXRUSNUnrtsAZXM9sg==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0
+
+  '@babel/helper-simple-access@7.24.7':
+    resolution: {integrity: sha512-zBAIvbCMh5Ts+b86r/CjU+4XGYIs+R1j951gxI3KmmxBMhCg4oQMsv6ZXQ64XOm/cvzfU1FmoCyt6+owc5QMYg==}
+    engines: {node: '>=6.9.0'}
+
+  '@babel/helper-skip-transparent-expression-wrappers@7.24.7':
+    resolution: {integrity: sha512-IO+DLT3LQUElMbpzlatRASEyQtfhSE0+m465v++3jyyXeBTBUjtVZg28/gHeV5mrTJqvEKhKroBGAvhW+qPHiQ==}
+    engines: {node: '>=6.9.0'}
+
+  '@babel/helper-split-export-declaration@7.24.7':
+    resolution: {integrity: sha512-oy5V7pD+UvfkEATUKvIjvIAH/xCzfsFVw7ygW2SI6NClZzquT+mwdTfgfdbUiceh6iQO0CHtCPsyze/MZ2YbAA==}
+    engines: {node: '>=6.9.0'}
+
+  '@babel/helper-string-parser@7.24.7':
+    resolution: {integrity: sha512-7MbVt6xrwFQbunH2DNQsAP5sTGxfqQtErvBIvIMi6EQnbgUOuVYanvREcmFrOPhoXBrTtjhhP+lW+o5UfK+tDg==}
+    engines: {node: '>=6.9.0'}
+
+  '@babel/helper-validator-identifier@7.24.7':
+    resolution: {integrity: sha512-rR+PBcQ1SMQDDyF6X0wxtG8QyLCgUB0eRAGguqRLfkCA87l7yAP7ehq8SNj96OOGTO8OBV70KhuFYcIkHXOg0w==}
+    engines: {node: '>=6.9.0'}
+
+  '@babel/helper-validator-option@7.24.7':
+    resolution: {integrity: sha512-yy1/KvjhV/ZCL+SM7hBrvnZJ3ZuT9OuZgIJAGpPEToANvc3iM6iDvBnRjtElWibHU6n8/LPR/EjX9EtIEYO3pw==}
+    engines: {node: '>=6.9.0'}
+
+  '@babel/helper-wrap-function@7.24.7':
+    resolution: {integrity: sha512-N9JIYk3TD+1vq/wn77YnJOqMtfWhNewNE+DJV4puD2X7Ew9J4JvrzrFDfTfyv5EgEXVy9/Wt8QiOErzEmv5Ifw==}
+    engines: {node: '>=6.9.0'}
+
+  '@babel/helpers@7.24.7':
+    resolution: {integrity: sha512-NlmJJtvcw72yRJRcnCmGvSi+3jDEg8qFu3z0AFoymmzLx5ERVWyzd9kVXr7Th9/8yIJi2Zc6av4Tqz3wFs8QWg==}
+    engines: {node: '>=6.9.0'}
+
+  '@babel/highlight@7.24.7':
+    resolution: {integrity: sha512-EStJpq4OuY8xYfhGVXngigBJRWxftKX9ksiGDnmlY3o7B/V7KIAc9X4oiK87uPJSc/vs5L869bem5fhZa8caZw==}
+    engines: {node: '>=6.9.0'}
+
+  '@babel/parser@7.24.7':
+    resolution: {integrity: sha512-9uUYRm6OqQrCqQdG1iCBwBPZgN8ciDBro2nIOFaiRz1/BCxaI7CNvQbDHvsArAC7Tw9Hda/B3U+6ui9u4HWXPw==}
+    engines: {node: '>=6.0.0'}
+    hasBin: true
+
+  '@babel/plugin-bugfix-firefox-class-in-computed-class-key@7.24.7':
+    resolution: {integrity: sha512-TiT1ss81W80eQsN+722OaeQMY/G4yTb4G9JrqeiDADs3N8lbPMGldWi9x8tyqCW5NLx1Jh2AvkE6r6QvEltMMQ==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0
+
+  '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@7.24.7':
+    resolution: {integrity: sha512-unaQgZ/iRu/By6tsjMZzpeBZjChYfLYry6HrEXPoz3KmfF0sVBQ1l8zKMQ4xRGLWVsjuvB8nQfjNP/DcfEOCsg==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0
+
+  '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@7.24.7':
+    resolution: {integrity: sha512-+izXIbke1T33mY4MSNnrqhPXDz01WYhEf3yF5NbnUtkiNnm+XBZJl3kNfoK6NKmYlz/D07+l2GWVK/QfDkNCuQ==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.13.0
+
+  '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@7.24.7':
+    resolution: {integrity: sha512-utA4HuR6F4Vvcr+o4DnjL8fCOlgRFGbeeBEGNg3ZTrLFw6VWG5XmUrvcQ0FjIYMU2ST4XcR2Wsp7t9qOAPnxMg==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0
+
+  '@babel/plugin-proposal-private-property-in-object@7.21.0-placeholder-for-preset-env.2':
+    resolution: {integrity: sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+
+  '@babel/plugin-syntax-async-generators@7.8.4':
+    resolution: {integrity: sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+
+  '@babel/plugin-syntax-bigint@7.8.3':
+    resolution: {integrity: sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+
+  '@babel/plugin-syntax-class-properties@7.12.13':
+    resolution: {integrity: sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+
+  '@babel/plugin-syntax-class-static-block@7.14.5':
+    resolution: {integrity: sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+
+  '@babel/plugin-syntax-dynamic-import@7.8.3':
+    resolution: {integrity: sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+
+  '@babel/plugin-syntax-export-namespace-from@7.8.3':
+    resolution: {integrity: sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q==}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+
+  '@babel/plugin-syntax-import-assertions@7.24.7':
+    resolution: {integrity: sha512-Ec3NRUMoi8gskrkBe3fNmEQfxDvY8bgfQpz6jlk/41kX9eUjvpyqWU7PBP/pLAvMaSQjbMNKJmvX57jP+M6bPg==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+
+  '@babel/plugin-syntax-import-attributes@7.24.7':
+    resolution: {integrity: sha512-hbX+lKKeUMGihnK8nvKqmXBInriT3GVjzXKFriV3YC6APGxMbP8RZNFwy91+hocLXq90Mta+HshoB31802bb8A==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+
+  '@babel/plugin-syntax-import-meta@7.10.4':
+    resolution: {integrity: sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+
+  '@babel/plugin-syntax-json-strings@7.8.3':
+    resolution: {integrity: sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+
+  '@babel/plugin-syntax-jsx@7.24.7':
+    resolution: {integrity: sha512-6ddciUPe/mpMnOKv/U+RSd2vvVy+Yw/JfBB0ZHYjEZt9NLHmCUylNYlsbqCCS1Bffjlb0fCwC9Vqz+sBz6PsiQ==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+
+  '@babel/plugin-syntax-logical-assignment-operators@7.10.4':
+    resolution: {integrity: sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==}
+    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-numeric-separator@7.10.4':
+    resolution: {integrity: sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==}
+    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-catch-binding@7.8.3':
+    resolution: {integrity: sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==}
+    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-private-property-in-object@7.14.5':
+    resolution: {integrity: sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+
+  '@babel/plugin-syntax-top-level-await@7.14.5':
+    resolution: {integrity: sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+
+  '@babel/plugin-syntax-typescript@7.24.7':
+    resolution: {integrity: sha512-c/+fVeJBB0FeKsFvwytYiUD+LBvhHjGSI0g446PRGdSVGZLRNArBUno2PETbAly3tpiNAQR5XaZ+JslxkotsbA==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+
+  '@babel/plugin-syntax-unicode-sets-regex@7.18.6':
+    resolution: {integrity: sha512-727YkEAPwSIQTv5im8QHz3upqp92JTWhidIC81Tdx4VJYIte/VndKf1qKrfnnhPLiPghStWfvC/iFaMCQu7Nqg==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0
+
+  '@babel/plugin-transform-arrow-functions@7.24.7':
+    resolution: {integrity: sha512-Dt9LQs6iEY++gXUwY03DNFat5C2NbO48jj+j/bSAz6b3HgPs39qcPiYt77fDObIcFwj3/C2ICX9YMwGflUoSHQ==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+
+  '@babel/plugin-transform-async-generator-functions@7.24.7':
+    resolution: {integrity: sha512-o+iF77e3u7ZS4AoAuJvapz9Fm001PuD2V3Lp6OSE4FYQke+cSewYtnek+THqGRWyQloRCyvWL1OkyfNEl9vr/g==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+
+  '@babel/plugin-transform-async-to-generator@7.24.7':
+    resolution: {integrity: sha512-SQY01PcJfmQ+4Ash7NE+rpbLFbmqA2GPIgqzxfFTL4t1FKRq4zTms/7htKpoCUI9OcFYgzqfmCdH53s6/jn5fA==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+
+  '@babel/plugin-transform-block-scoped-functions@7.24.7':
+    resolution: {integrity: sha512-yO7RAz6EsVQDaBH18IDJcMB1HnrUn2FJ/Jslc/WtPPWcjhpUJXU/rjbwmluzp7v/ZzWcEhTMXELnnsz8djWDwQ==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+
+  '@babel/plugin-transform-block-scoping@7.24.7':
+    resolution: {integrity: sha512-Nd5CvgMbWc+oWzBsuaMcbwjJWAcp5qzrbg69SZdHSP7AMY0AbWFqFO0WTFCA1jxhMCwodRwvRec8k0QUbZk7RQ==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+
+  '@babel/plugin-transform-class-properties@7.24.7':
+    resolution: {integrity: sha512-vKbfawVYayKcSeSR5YYzzyXvsDFWU2mD8U5TFeXtbCPLFUqe7GyCgvO6XDHzje862ODrOwy6WCPmKeWHbCFJ4w==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+
+  '@babel/plugin-transform-class-static-block@7.24.7':
+    resolution: {integrity: sha512-HMXK3WbBPpZQufbMG4B46A90PkuuhN9vBCb5T8+VAHqvAqvcLi+2cKoukcpmUYkszLhScU3l1iudhrks3DggRQ==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.12.0
+
+  '@babel/plugin-transform-classes@7.24.7':
+    resolution: {integrity: sha512-CFbbBigp8ln4FU6Bpy6g7sE8B/WmCmzvivzUC6xDAdWVsjYTXijpuuGJmYkAaoWAzcItGKT3IOAbxRItZ5HTjw==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+
+  '@babel/plugin-transform-computed-properties@7.24.7':
+    resolution: {integrity: sha512-25cS7v+707Gu6Ds2oY6tCkUwsJ9YIDbggd9+cu9jzzDgiNq7hR/8dkzxWfKWnTic26vsI3EsCXNd4iEB6e8esQ==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+
+  '@babel/plugin-transform-destructuring@7.24.7':
+    resolution: {integrity: sha512-19eJO/8kdCQ9zISOf+SEUJM/bAUIsvY3YDnXZTupUCQ8LgrWnsG/gFB9dvXqdXnRXMAM8fvt7b0CBKQHNGy1mw==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+
+  '@babel/plugin-transform-dotall-regex@7.24.7':
+    resolution: {integrity: sha512-ZOA3W+1RRTSWvyqcMJDLqbchh7U4NRGqwRfFSVbOLS/ePIP4vHB5e8T8eXcuqyN1QkgKyj5wuW0lcS85v4CrSw==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+
+  '@babel/plugin-transform-duplicate-keys@7.24.7':
+    resolution: {integrity: sha512-JdYfXyCRihAe46jUIliuL2/s0x0wObgwwiGxw/UbgJBr20gQBThrokO4nYKgWkD7uBaqM7+9x5TU7NkExZJyzw==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+
+  '@babel/plugin-transform-dynamic-import@7.24.7':
+    resolution: {integrity: sha512-sc3X26PhZQDb3JhORmakcbvkeInvxz+A8oda99lj7J60QRuPZvNAk9wQlTBS1ZynelDrDmTU4pw1tyc5d5ZMUg==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+
+  '@babel/plugin-transform-exponentiation-operator@7.24.7':
+    resolution: {integrity: sha512-Rqe/vSc9OYgDajNIK35u7ot+KeCoetqQYFXM4Epf7M7ez3lWlOjrDjrwMei6caCVhfdw+mIKD4cgdGNy5JQotQ==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+
+  '@babel/plugin-transform-export-namespace-from@7.24.7':
+    resolution: {integrity: sha512-v0K9uNYsPL3oXZ/7F9NNIbAj2jv1whUEtyA6aujhekLs56R++JDQuzRcP2/z4WX5Vg/c5lE9uWZA0/iUoFhLTA==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+
+  '@babel/plugin-transform-for-of@7.24.7':
+    resolution: {integrity: sha512-wo9ogrDG1ITTTBsy46oGiN1dS9A7MROBTcYsfS8DtsImMkHk9JXJ3EWQM6X2SUw4x80uGPlwj0o00Uoc6nEE3g==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+
+  '@babel/plugin-transform-function-name@7.24.7':
+    resolution: {integrity: sha512-U9FcnA821YoILngSmYkW6FjyQe2TyZD5pHt4EVIhmcTkrJw/3KqcrRSxuOo5tFZJi7TE19iDyI1u+weTI7bn2w==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+
+  '@babel/plugin-transform-json-strings@7.24.7':
+    resolution: {integrity: sha512-2yFnBGDvRuxAaE/f0vfBKvtnvvqU8tGpMHqMNpTN2oWMKIR3NqFkjaAgGwawhqK/pIN2T3XdjGPdaG0vDhOBGw==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+
+  '@babel/plugin-transform-literals@7.24.7':
+    resolution: {integrity: sha512-vcwCbb4HDH+hWi8Pqenwnjy+UiklO4Kt1vfspcQYFhJdpthSnW8XvWGyDZWKNVrVbVViI/S7K9PDJZiUmP2fYQ==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+
+  '@babel/plugin-transform-logical-assignment-operators@7.24.7':
+    resolution: {integrity: sha512-4D2tpwlQ1odXmTEIFWy9ELJcZHqrStlzK/dAOWYyxX3zT0iXQB6banjgeOJQXzEc4S0E0a5A+hahxPaEFYftsw==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+
+  '@babel/plugin-transform-member-expression-literals@7.24.7':
+    resolution: {integrity: sha512-T/hRC1uqrzXMKLQ6UCwMT85S3EvqaBXDGf0FaMf4446Qx9vKwlghvee0+uuZcDUCZU5RuNi4781UQ7R308zzBw==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+
+  '@babel/plugin-transform-modules-amd@7.24.7':
+    resolution: {integrity: sha512-9+pB1qxV3vs/8Hdmz/CulFB8w2tuu6EB94JZFsjdqxQokwGa9Unap7Bo2gGBGIvPmDIVvQrom7r5m/TCDMURhg==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+
+  '@babel/plugin-transform-modules-commonjs@7.24.7':
+    resolution: {integrity: sha512-iFI8GDxtevHJ/Z22J5xQpVqFLlMNstcLXh994xifFwxxGslr2ZXXLWgtBeLctOD63UFDArdvN6Tg8RFw+aEmjQ==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+
+  '@babel/plugin-transform-modules-systemjs@7.24.7':
+    resolution: {integrity: sha512-GYQE0tW7YoaN13qFh3O1NCY4MPkUiAH3fiF7UcV/I3ajmDKEdG3l+UOcbAm4zUE3gnvUU+Eni7XrVKo9eO9auw==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+
+  '@babel/plugin-transform-modules-umd@7.24.7':
+    resolution: {integrity: sha512-3aytQvqJ/h9z4g8AsKPLvD4Zqi2qT+L3j7XoFFu1XBlZWEl2/1kWnhmAbxpLgPrHSY0M6UA02jyTiwUVtiKR6A==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+
+  '@babel/plugin-transform-named-capturing-groups-regex@7.24.7':
+    resolution: {integrity: sha512-/jr7h/EWeJtk1U/uz2jlsCioHkZk1JJZVcc8oQsJ1dUlaJD83f4/6Zeh2aHt9BIFokHIsSeDfhUmju0+1GPd6g==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0
+
+  '@babel/plugin-transform-new-target@7.24.7':
+    resolution: {integrity: sha512-RNKwfRIXg4Ls/8mMTza5oPF5RkOW8Wy/WgMAp1/F1yZ8mMbtwXW+HDoJiOsagWrAhI5f57Vncrmr9XeT4CVapA==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+
+  '@babel/plugin-transform-nullish-coalescing-operator@7.24.7':
+    resolution: {integrity: sha512-Ts7xQVk1OEocqzm8rHMXHlxvsfZ0cEF2yomUqpKENHWMF4zKk175Y4q8H5knJes6PgYad50uuRmt3UJuhBw8pQ==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+
+  '@babel/plugin-transform-numeric-separator@7.24.7':
+    resolution: {integrity: sha512-e6q1TiVUzvH9KRvicuxdBTUj4AdKSRwzIyFFnfnezpCfP2/7Qmbb8qbU2j7GODbl4JMkblitCQjKYUaX/qkkwA==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+
+  '@babel/plugin-transform-object-rest-spread@7.24.7':
+    resolution: {integrity: sha512-4QrHAr0aXQCEFni2q4DqKLD31n2DL+RxcwnNjDFkSG0eNQ/xCavnRkfCUjsyqGC2OviNJvZOF/mQqZBw7i2C5Q==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+
+  '@babel/plugin-transform-object-super@7.24.7':
+    resolution: {integrity: sha512-A/vVLwN6lBrMFmMDmPPz0jnE6ZGx7Jq7d6sT/Ev4H65RER6pZ+kczlf1DthF5N0qaPHBsI7UXiE8Zy66nmAovg==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+
+  '@babel/plugin-transform-optional-catch-binding@7.24.7':
+    resolution: {integrity: sha512-uLEndKqP5BfBbC/5jTwPxLh9kqPWWgzN/f8w6UwAIirAEqiIVJWWY312X72Eub09g5KF9+Zn7+hT7sDxmhRuKA==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+
+  '@babel/plugin-transform-optional-chaining@7.24.7':
+    resolution: {integrity: sha512-tK+0N9yd4j+x/4hxF3F0e0fu/VdcxU18y5SevtyM/PCFlQvXbR0Zmlo2eBrKtVipGNFzpq56o8WsIIKcJFUCRQ==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+
+  '@babel/plugin-transform-parameters@7.24.7':
+    resolution: {integrity: sha512-yGWW5Rr+sQOhK0Ot8hjDJuxU3XLRQGflvT4lhlSY0DFvdb3TwKaY26CJzHtYllU0vT9j58hc37ndFPsqT1SrzA==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+
+  '@babel/plugin-transform-private-methods@7.24.7':
+    resolution: {integrity: sha512-COTCOkG2hn4JKGEKBADkA8WNb35TGkkRbI5iT845dB+NyqgO8Hn+ajPbSnIQznneJTa3d30scb6iz/DhH8GsJQ==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+
+  '@babel/plugin-transform-private-property-in-object@7.24.7':
+    resolution: {integrity: sha512-9z76mxwnwFxMyxZWEgdgECQglF2Q7cFLm0kMf8pGwt+GSJsY0cONKj/UuO4bOH0w/uAel3ekS4ra5CEAyJRmDA==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+
+  '@babel/plugin-transform-property-literals@7.24.7':
+    resolution: {integrity: sha512-EMi4MLQSHfd2nrCqQEWxFdha2gBCqU4ZcCng4WBGZ5CJL4bBRW0ptdqqDdeirGZcpALazVVNJqRmsO8/+oNCBA==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+
+  '@babel/plugin-transform-regenerator@7.24.7':
+    resolution: {integrity: sha512-lq3fvXPdimDrlg6LWBoqj+r/DEWgONuwjuOuQCSYgRroXDH/IdM1C0IZf59fL5cHLpjEH/O6opIRBbqv7ELnuA==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+
+  '@babel/plugin-transform-reserved-words@7.24.7':
+    resolution: {integrity: sha512-0DUq0pHcPKbjFZCfTss/pGkYMfy3vFWydkUBd9r0GHpIyfs2eCDENvqadMycRS9wZCXR41wucAfJHJmwA0UmoQ==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+
+  '@babel/plugin-transform-shorthand-properties@7.24.7':
+    resolution: {integrity: sha512-KsDsevZMDsigzbA09+vacnLpmPH4aWjcZjXdyFKGzpplxhbeB4wYtury3vglQkg6KM/xEPKt73eCjPPf1PgXBA==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+
+  '@babel/plugin-transform-spread@7.24.7':
+    resolution: {integrity: sha512-x96oO0I09dgMDxJaANcRyD4ellXFLLiWhuwDxKZX5g2rWP1bTPkBSwCYv96VDXVT1bD9aPj8tppr5ITIh8hBng==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+
+  '@babel/plugin-transform-sticky-regex@7.24.7':
+    resolution: {integrity: sha512-kHPSIJc9v24zEml5geKg9Mjx5ULpfncj0wRpYtxbvKyTtHCYDkVE3aHQ03FrpEo4gEe2vrJJS1Y9CJTaThA52g==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+
+  '@babel/plugin-transform-template-literals@7.24.7':
+    resolution: {integrity: sha512-AfDTQmClklHCOLxtGoP7HkeMw56k1/bTQjwsfhL6pppo/M4TOBSq+jjBUBLmV/4oeFg4GWMavIl44ZeCtmmZTw==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+
+  '@babel/plugin-transform-typeof-symbol@7.24.7':
+    resolution: {integrity: sha512-VtR8hDy7YLB7+Pet9IarXjg/zgCMSF+1mNS/EQEiEaUPoFXCVsHG64SIxcaaI2zJgRiv+YmgaQESUfWAdbjzgg==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+
+  '@babel/plugin-transform-typescript@7.24.7':
+    resolution: {integrity: sha512-iLD3UNkgx2n/HrjBesVbYX6j0yqn/sJktvbtKKgcaLIQ4bTTQ8obAypc1VpyHPD2y4Phh9zHOaAt8e/L14wCpw==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+
+  '@babel/plugin-transform-unicode-escapes@7.24.7':
+    resolution: {integrity: sha512-U3ap1gm5+4edc2Q/P+9VrBNhGkfnf+8ZqppY71Bo/pzZmXhhLdqgaUl6cuB07O1+AQJtCLfaOmswiNbSQ9ivhw==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+
+  '@babel/plugin-transform-unicode-property-regex@7.24.7':
+    resolution: {integrity: sha512-uH2O4OV5M9FZYQrwc7NdVmMxQJOCCzFeYudlZSzUAHRFeOujQefa92E74TQDVskNHCzOXoigEuoyzHDhaEaK5w==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+
+  '@babel/plugin-transform-unicode-regex@7.24.7':
+    resolution: {integrity: sha512-hlQ96MBZSAXUq7ltkjtu3FJCCSMx/j629ns3hA3pXnBXjanNP0LHi+JpPeA81zaWgVK1VGH95Xuy7u0RyQ8kMg==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+
+  '@babel/plugin-transform-unicode-sets-regex@7.24.7':
+    resolution: {integrity: sha512-2G8aAvF4wy1w/AGZkemprdGMRg5o6zPNhbHVImRz3lss55TYCBd6xStN19rt8XJHq20sqV0JbyWjOWwQRwV/wg==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0
+
+  '@babel/preset-env@7.24.7':
+    resolution: {integrity: sha512-1YZNsc+y6cTvWlDHidMBsQZrZfEFjRIo/BZCT906PMdzOyXtSLTgqGdrpcuTDCXyd11Am5uQULtDIcCfnTc8fQ==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+
+  '@babel/preset-modules@0.1.6-no-external-plugins':
+    resolution: {integrity: sha512-HrcgcIESLm9aIR842yhJ5RWan/gebQUJ6E/E5+rf0y9o6oj7w0Br+sWuL6kEQ/o/AdfvR1Je9jG18/gnpwjEyA==}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0 || ^8.0.0-0 <8.0.0
+
+  '@babel/regjsgen@0.8.0':
+    resolution: {integrity: sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA==}
+
+  '@babel/runtime@7.24.7':
+    resolution: {integrity: sha512-UwgBRMjJP+xv857DCngvqXI3Iq6J4v0wXmwc6sapg+zyhbwmQX67LUEFrkK5tbyJ30jGuG3ZvWpBiB9LCy1kWw==}
+    engines: {node: '>=6.9.0'}
+
+  '@babel/template@7.24.7':
+    resolution: {integrity: sha512-jYqfPrU9JTF0PmPy1tLYHW4Mp4KlgxJD9l2nP9fD6yT/ICi554DmrWBAEYpIelzjHf1msDP3PxJIRt/nFNfBig==}
+    engines: {node: '>=6.9.0'}
+
+  '@babel/traverse@7.24.7':
+    resolution: {integrity: sha512-yb65Ed5S/QAcewNPh0nZczy9JdYXkkAbIsEo+P7BE7yO3txAY30Y/oPa3QkQ5It3xVG2kpKMg9MsdxZaO31uKA==}
+    engines: {node: '>=6.9.0'}
+
+  '@babel/types@7.24.7':
+    resolution: {integrity: sha512-XEFXSlxiG5td2EJRe8vOmRbaXVgfcBlszKujvVmWIK/UpywWljQCfzAv3RQCGujWQ1RD4YYWEAqDXfuJiy8f5Q==}
+    engines: {node: '>=6.9.0'}
+
+  '@bcoe/v8-coverage@0.2.3':
+    resolution: {integrity: sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==}
+
+  '@dcloudio/types@3.4.8':
+    resolution: {integrity: sha512-IPXuoghLv7qNPOnRuP7vC5++MdRHhE0U7EMw9ia//uOh69fFXZiRTfoHd51+nzciD6R50gqYhbrCCZIxnxhM9Q==}
+
+  '@dcloudio/uni-app-plus@3.0.0-4000720240327002':
+    resolution: {integrity: sha512-roh+iEt2B9Mnwu6JYaO8gs7OaeNVESQ6fmhNdLvaNlcWvT2qdD48LpfAXrR2YQN1AYS+gkeeJeM1EoTC+ymgqQ==}
+
+  '@dcloudio/uni-app-uts@3.0.0-4000720240327002':
+    resolution: {integrity: sha512-cRcwSF5P6K0r/4YRtqyXMaNmR/t3HvGWGXxp5IKrfR2HPDTRWeUBpYxH9l+dFfctBKNNKvyUcKuYNlk7EMEyDA==}
+
+  '@dcloudio/uni-app-vite@3.0.0-4000720240327002':
+    resolution: {integrity: sha512-sZbfTma17HYEt3gagxaK0lRntT2m8/X/bJc2Q8+Oc8P/yXFQF7GTwW/EFX2SVK1gheIe9K9uO981c4Sa6hrX5Q==}
+
+  '@dcloudio/uni-app-vue@3.0.0-4000720240327002':
+    resolution: {integrity: sha512-dO7fmFQOtjvqk18K0TvcyUfeE9K9gU6vvwO8TiTmdyijuzqOZC+P+T9zNkf9SyazTzFr4hC23xC+1AMpMFO38g==}
+
+  '@dcloudio/uni-app@3.0.0-4000720240327002':
+    resolution: {integrity: sha512-ybMgIuQZ0cb1WP0Nfo1SpVWutd2vavv5c2f8RKTpjY9TW4lMgwZ2SPiHGuqndOZiP0Jjj4Bt/7HsHf8h9F5FGg==}
+    peerDependencies:
+      '@dcloudio/types': ^3.4.8
+
+  '@dcloudio/uni-automator@3.0.0-4000720240327002':
+    resolution: {integrity: sha512-gcGr6bygbvHBGjJPFmYUdTkuHOOBtTanNj+eawOsC2snd3I9AmamaLgIy/VCShjSo+CBgUok33x9pRMD6kuGJQ==}
+    peerDependencies:
+      jest: 27.0.4
+      jest-environment-node: 27.5.1
+
+  '@dcloudio/uni-cli-shared@3.0.0-4000720240327002':
+    resolution: {integrity: sha512-y53LZBk6lBdil3ykzyVn5hNS1lx+y1J9l8AW7jY7RXcGL5ku87fnKFjpl+DAmvXHdaBJhQMH446cjq2q3KIuOA==}
+    engines: {node: ^14.18.0 || >=16.0.0}
+
+  '@dcloudio/uni-cloud@3.0.0-4000720240327002':
+    resolution: {integrity: sha512-OF0ykfF7D6fb1a18yjQgpOynnoQI/X2XvqsmXEBu4v5+V77RWR5V9cBq6+m7Haqfkd5anJl09tBMY3SFrLNMWw==}
+
+  '@dcloudio/uni-components@3.0.0-4000720240327002':
+    resolution: {integrity: sha512-xDKU8lMZbc9Aa6PrqN+49mt5vHhzrpIEDWDlRekTUjOTq20pbR1Zi/P9r+B4TzhmeIu74Q5j0TPPn65iuTHbeQ==}
+
+  '@dcloudio/uni-h5-vite@3.0.0-4000720240327002':
+    resolution: {integrity: sha512-wLFMl1k1DnL0/RmvW0piWn7KZtAt70XpDDQrqfGzt0WbMrgcqKMqTj48bWtimTlC3kn4GWi6wToKmH6WQkSEQQ==}
+
+  '@dcloudio/uni-h5-vue@3.0.0-4000720240327002':
+    resolution: {integrity: sha512-lE2XO9056SrraMkZtAhNzfXojdPXgLPUiUR+sJkIM4r7aGNomh6mHKVLEoB/mHBghTuWJREQvsH/J/sCPb7/zA==}
+
+  '@dcloudio/uni-h5@3.0.0-4000720240327002':
+    resolution: {integrity: sha512-Tax9+4Fg067/CxA/b8bCEQAA7sePQXini+Eb5q+UMVc60CaXFcNWEe/xsWmK66OuseZL6rxcmZ8E59OU4dtIHg==}
+
+  '@dcloudio/uni-i18n@3.0.0-4000720240327002':
+    resolution: {integrity: sha512-gSFUM0LlkFD9dV+BRN7nV84w5P5lWFHXfckBeL5vxrROjJLbucwymBV2MQedVuACw92PhzfbQ5UxcNoxqhodCQ==}
+
+  '@dcloudio/uni-mp-alipay@3.0.0-4000720240327002':
+    resolution: {integrity: sha512-Wn7rXEyHwUF5J9dVG1LD2YuBokh6E1KXlbwi1BSN38i82fudadDa1xjeWJFVQTb6PxcQNUDN2uvt7wMRI45OsQ==}
+
+  '@dcloudio/uni-mp-compiler@3.0.0-4000720240327002':
+    resolution: {integrity: sha512-hYJ7LPo7zGKg+AuCRxzATnmIAiyA/TrVJ4ahDc40Ait6Amnyf7L215jOaG1nF1CuvYoN6h22nRN43aBTsquNOw==}
+
+  '@dcloudio/uni-mp-vite@3.0.0-4000720240327002':
+    resolution: {integrity: sha512-9gpVWP0crINKZTjEFuReCeK/0FmeKMoUbTrgkF2hnHHMqGTa7J1N6DDztO96b3mqZ0AUWqerxBFETXjKcSyybQ==}
+
+  '@dcloudio/uni-mp-vue@3.0.0-4000720240327002':
+    resolution: {integrity: sha512-beTYQ+Z9GRDBqcMvORC9AlOnnbaq1DzBRNRg9qE7wg19BIM1ame9Q0eetqC+J55BvoGooE1uCe7Eu8mADtiN3A==}
+
+  '@dcloudio/uni-mp-weixin@3.0.0-4000720240327002':
+    resolution: {integrity: sha512-quiNKsHM3pkirJM3w/puBuE6L3B4ULfCaqry9BEIvvarSXmAoQtxVnPl+PkT3+1vKbR3e0WSc1E1dt90A3m+kw==}
+
+  '@dcloudio/uni-nvue-styler@3.0.0-4000720240327002':
+    resolution: {integrity: sha512-OZbtOY388LPT+9Gkg3Qkow670C8E7ZOhl7N7nLFEOm2rQSr8aHTtgMioTtHQgjDktCmFu5gROq6Tp0O70TLmBw==}
+
+  '@dcloudio/uni-push@3.0.0-4000720240327002':
+    resolution: {integrity: sha512-v+D8SwTb3yPSZ+VuntdQqZxNjzHdmVw3EP6h5w/05j9Wj/mp0cjcBcvDWh24ggnlg9GfUgvLVMgOjVSsKJ8DvQ==}
+
+  '@dcloudio/uni-shared@3.0.0-4000720240327002':
+    resolution: {integrity: sha512-G7fMdQb97VbsJ3X1jvThbToCIOMBQ57X9yk9Ydo4mVBXtUpj2/S1S6koIiwCIYRJaWtdtNpyyAscg92UObTQqg==}
+
+  '@dcloudio/uni-stacktracey@3.0.0-4000720240327002':
+    resolution: {integrity: sha512-n2KjkzA7T+kgQXAYmrECuUZckadm96HL88B9ASNTkdGDhLJHlXqDAiR4nz+NE6iuLVN0fdx5iRFNkI2QfFsRYQ==}
+
+  '@dcloudio/uni-stat@3.0.0-4000720240327002':
+    resolution: {integrity: sha512-F9+9lqlet5urQa0r3PGj5a4iNB/vkPZjdWugElO0mI3XqEzeeWq+iXQSDYGOov8nqBxBHbR8Tf2B3SdJItUI1g==}
+
+  '@dcloudio/uni-ui@1.5.5':
+    resolution: {integrity: sha512-DTr8o4lcRgu4JJclA2eT7UFVKtglK63OUtcDGysrSeZhgAJAC2gBYm/pYbv0BFxKqxfbdoAFGAFCigCVY9+NAQ==}
+
+  '@dcloudio/vite-plugin-uni@3.0.0-4000720240327002':
+    resolution: {integrity: sha512-9VsAOHZfEFU7e2jlIxjdbgqbCBecaPBKH0/y/fFndleq9+pHh55BQsqe5+VtjxNy68Ttcg9iocSxtM5SGOPFJA==}
+    engines: {node: ^14.18.0 || >=16.0.0}
+    hasBin: true
+    peerDependencies:
+      vite: ^4.0.0
+
+  '@esbuild/android-arm64@0.17.19':
+    resolution: {integrity: sha512-KBMWvEZooR7+kzY0BtbTQn0OAYY7CsiydT63pVEaPtVYF0hXbUaOyZog37DKxK7NF3XacBJOpYT4adIJh+avxA==}
+    engines: {node: '>=12'}
+    cpu: [arm64]
+    os: [android]
+
+  '@esbuild/android-arm@0.17.19':
+    resolution: {integrity: sha512-rIKddzqhmav7MSmoFCmDIb6e2W57geRsM94gV2l38fzhXMwq7hZoClug9USI2pFRGL06f4IOPHHpFNOkWieR8A==}
+    engines: {node: '>=12'}
+    cpu: [arm]
+    os: [android]
+
+  '@esbuild/android-x64@0.17.19':
+    resolution: {integrity: sha512-uUTTc4xGNDT7YSArp/zbtmbhO0uEEK9/ETW29Wk1thYUJBz3IVnvgEiEwEa9IeLyvnpKrWK64Utw2bgUmDveww==}
+    engines: {node: '>=12'}
+    cpu: [x64]
+    os: [android]
+
+  '@esbuild/darwin-arm64@0.17.19':
+    resolution: {integrity: sha512-80wEoCfF/hFKM6WE1FyBHc9SfUblloAWx6FJkFWTWiCoht9Mc0ARGEM47e67W9rI09YoUxJL68WHfDRYEAvOhg==}
+    engines: {node: '>=12'}
+    cpu: [arm64]
+    os: [darwin]
+
+  '@esbuild/darwin-x64@0.17.19':
+    resolution: {integrity: sha512-IJM4JJsLhRYr9xdtLytPLSH9k/oxR3boaUIYiHkAawtwNOXKE8KoU8tMvryogdcT8AU+Bflmh81Xn6Q0vTZbQw==}
+    engines: {node: '>=12'}
+    cpu: [x64]
+    os: [darwin]
+
+  '@esbuild/freebsd-arm64@0.17.19':
+    resolution: {integrity: sha512-pBwbc7DufluUeGdjSU5Si+P3SoMF5DQ/F/UmTSb8HXO80ZEAJmrykPyzo1IfNbAoaqw48YRpv8shwd1NoI0jcQ==}
+    engines: {node: '>=12'}
+    cpu: [arm64]
+    os: [freebsd]
+
+  '@esbuild/freebsd-x64@0.17.19':
+    resolution: {integrity: sha512-4lu+n8Wk0XlajEhbEffdy2xy53dpR06SlzvhGByyg36qJw6Kpfk7cp45DR/62aPH9mtJRmIyrXAS5UWBrJT6TQ==}
+    engines: {node: '>=12'}
+    cpu: [x64]
+    os: [freebsd]
+
+  '@esbuild/linux-arm64@0.17.19':
+    resolution: {integrity: sha512-ct1Tg3WGwd3P+oZYqic+YZF4snNl2bsnMKRkb3ozHmnM0dGWuxcPTTntAF6bOP0Sp4x0PjSF+4uHQ1xvxfRKqg==}
+    engines: {node: '>=12'}
+    cpu: [arm64]
+    os: [linux]
+
+  '@esbuild/linux-arm@0.17.19':
+    resolution: {integrity: sha512-cdmT3KxjlOQ/gZ2cjfrQOtmhG4HJs6hhvm3mWSRDPtZ/lP5oe8FWceS10JaSJC13GBd4eH/haHnqf7hhGNLerA==}
+    engines: {node: '>=12'}
+    cpu: [arm]
+    os: [linux]
+
+  '@esbuild/linux-ia32@0.17.19':
+    resolution: {integrity: sha512-w4IRhSy1VbsNxHRQpeGCHEmibqdTUx61Vc38APcsRbuVgK0OPEnQ0YD39Brymn96mOx48Y2laBQGqgZ0j9w6SQ==}
+    engines: {node: '>=12'}
+    cpu: [ia32]
+    os: [linux]
+
+  '@esbuild/linux-loong64@0.17.19':
+    resolution: {integrity: sha512-2iAngUbBPMq439a+z//gE+9WBldoMp1s5GWsUSgqHLzLJ9WoZLZhpwWuym0u0u/4XmZ3gpHmzV84PonE+9IIdQ==}
+    engines: {node: '>=12'}
+    cpu: [loong64]
+    os: [linux]
+
+  '@esbuild/linux-mips64el@0.17.19':
+    resolution: {integrity: sha512-LKJltc4LVdMKHsrFe4MGNPp0hqDFA1Wpt3jE1gEyM3nKUvOiO//9PheZZHfYRfYl6AwdTH4aTcXSqBerX0ml4A==}
+    engines: {node: '>=12'}
+    cpu: [mips64el]
+    os: [linux]
+
+  '@esbuild/linux-ppc64@0.17.19':
+    resolution: {integrity: sha512-/c/DGybs95WXNS8y3Ti/ytqETiW7EU44MEKuCAcpPto3YjQbyK3IQVKfF6nbghD7EcLUGl0NbiL5Rt5DMhn5tg==}
+    engines: {node: '>=12'}
+    cpu: [ppc64]
+    os: [linux]
+
+  '@esbuild/linux-riscv64@0.17.19':
+    resolution: {integrity: sha512-FC3nUAWhvFoutlhAkgHf8f5HwFWUL6bYdvLc/TTuxKlvLi3+pPzdZiFKSWz/PF30TB1K19SuCxDTI5KcqASJqA==}
+    engines: {node: '>=12'}
+    cpu: [riscv64]
+    os: [linux]
+
+  '@esbuild/linux-s390x@0.17.19':
+    resolution: {integrity: sha512-IbFsFbxMWLuKEbH+7sTkKzL6NJmG2vRyy6K7JJo55w+8xDk7RElYn6xvXtDW8HCfoKBFK69f3pgBJSUSQPr+4Q==}
+    engines: {node: '>=12'}
+    cpu: [s390x]
+    os: [linux]
+
+  '@esbuild/linux-x64@0.17.19':
+    resolution: {integrity: sha512-68ngA9lg2H6zkZcyp22tsVt38mlhWde8l3eJLWkyLrp4HwMUr3c1s/M2t7+kHIhvMjglIBrFpncX1SzMckomGw==}
+    engines: {node: '>=12'}
+    cpu: [x64]
+    os: [linux]
+
+  '@esbuild/netbsd-x64@0.17.19':
+    resolution: {integrity: sha512-CwFq42rXCR8TYIjIfpXCbRX0rp1jo6cPIUPSaWwzbVI4aOfX96OXY8M6KNmtPcg7QjYeDmN+DD0Wp3LaBOLf4Q==}
+    engines: {node: '>=12'}
+    cpu: [x64]
+    os: [netbsd]
+
+  '@esbuild/openbsd-x64@0.17.19':
+    resolution: {integrity: sha512-cnq5brJYrSZ2CF6c35eCmviIN3k3RczmHz8eYaVlNasVqsNY+JKohZU5MKmaOI+KkllCdzOKKdPs762VCPC20g==}
+    engines: {node: '>=12'}
+    cpu: [x64]
+    os: [openbsd]
+
+  '@esbuild/sunos-x64@0.17.19':
+    resolution: {integrity: sha512-vCRT7yP3zX+bKWFeP/zdS6SqdWB8OIpaRq/mbXQxTGHnIxspRtigpkUcDMlSCOejlHowLqII7K2JKevwyRP2rg==}
+    engines: {node: '>=12'}
+    cpu: [x64]
+    os: [sunos]
+
+  '@esbuild/win32-arm64@0.17.19':
+    resolution: {integrity: sha512-yYx+8jwowUstVdorcMdNlzklLYhPxjniHWFKgRqH7IFlUEa0Umu3KuYplf1HUZZ422e3NU9F4LGb+4O0Kdcaag==}
+    engines: {node: '>=12'}
+    cpu: [arm64]
+    os: [win32]
+
+  '@esbuild/win32-ia32@0.17.19':
+    resolution: {integrity: sha512-eggDKanJszUtCdlVs0RB+h35wNlb5v4TWEkq4vZcmVt5u/HiDZrTXe2bWFQUez3RgNHwx/x4sk5++4NSSicKkw==}
+    engines: {node: '>=12'}
+    cpu: [ia32]
+    os: [win32]
+
+  '@esbuild/win32-x64@0.17.19':
+    resolution: {integrity: sha512-lAhycmKnVOuRYNtRtatQR1LPQf2oYCkRGkSFnseDAKPl8lu5SOsK/e1sXe5a0Pc5kHIHe6P2I/ilntNv2xf3cA==}
+    engines: {node: '>=12'}
+    cpu: [x64]
+    os: [win32]
+
+  '@eslint-community/eslint-utils@4.4.0':
+    resolution: {integrity: sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==}
+    engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
+    peerDependencies:
+      eslint: ^6.0.0 || ^7.0.0 || >=8.0.0
+
+  '@eslint-community/regexpp@4.10.1':
+    resolution: {integrity: sha512-Zm2NGpWELsQAD1xsJzGQpYfvICSsFkEpU0jxBjfdC6uNEWXcHnfs9hScFWtXVDVl+rBQJGrl4g1vcKIejpH9dA==}
+    engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0}
+
+  '@eslint/config-array@0.16.0':
+    resolution: {integrity: sha512-/jmuSd74i4Czf1XXn7wGRWZCuyaUZ330NH1Bek0Pplatt4Sy1S5haN21SCLLdbeKslQ+S0wEJ+++v5YibSi+Lg==}
+    engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+  '@eslint/eslintrc@3.1.0':
+    resolution: {integrity: sha512-4Bfj15dVJdoy3RfZmmo86RK1Fwzn6SstsvK9JS+BaVKqC6QQQQyXekNaC+g+LKNgkQ+2VhGAzm6hO40AhMR3zQ==}
+    engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+  '@eslint/js@9.5.0':
+    resolution: {integrity: sha512-A7+AOT2ICkodvtsWnxZP4Xxk3NbZ3VMHd8oihydLRGrJgqqdEz1qSeEgXYyT/Cu8h1TWWsQRejIx48mtjZ5y1w==}
+    engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+  '@eslint/object-schema@2.1.4':
+    resolution: {integrity: sha512-BsWiH1yFGjXXS2yvrf5LyuoSIIbPrGUWob917o+BTKuZ7qJdxX8aJLRxs1fS9n6r7vESrq1OUqb68dANcFXuQQ==}
+    engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+  '@humanwhocodes/module-importer@1.0.1':
+    resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==}
+    engines: {node: '>=12.22'}
+
+  '@humanwhocodes/retry@0.3.0':
+    resolution: {integrity: sha512-d2CGZR2o7fS6sWB7DG/3a95bGKQyHMACZ5aW8qGkkqQpUoZV6C0X7Pc7l4ZNMZkfNBf4VWNe9E1jRsf0G146Ew==}
+    engines: {node: '>=18.18'}
+
+  '@intlify/core-base@9.1.9':
+    resolution: {integrity: sha512-x5T0p/Ja0S8hs5xs+ImKyYckVkL4CzcEXykVYYV6rcbXxJTe2o58IquSqX9bdncVKbRZP7GlBU1EcRaQEEJ+vw==}
+    engines: {node: '>= 10'}
+
+  '@intlify/devtools-if@9.1.9':
+    resolution: {integrity: sha512-oKSMKjttG3Ut/1UGEZjSdghuP3fwA15zpDPcjkf/1FjlOIm6uIBGMNS5jXzsZy593u+P/YcnrZD6cD3IVFz9vQ==}
+    engines: {node: '>= 10'}
+
+  '@intlify/message-compiler@9.1.9':
+    resolution: {integrity: sha512-6YgCMF46Xd0IH2hMRLCssZI3gFG4aywidoWQ3QP4RGYQXQYYfFC54DxhSgfIPpVoPLQ+4AD29eoYmhiHZ+qLFQ==}
+    engines: {node: '>= 10'}
+
+  '@intlify/message-resolver@9.1.9':
+    resolution: {integrity: sha512-Lx/DBpigeK0sz2BBbzv5mu9/dAlt98HxwbG7xLawC3O2xMF9MNWU5FtOziwYG6TDIjNq0O/3ZbOJAxwITIWXEA==}
+    engines: {node: '>= 10'}
+
+  '@intlify/runtime@9.1.9':
+    resolution: {integrity: sha512-XgPw8+UlHCiie3fI41HPVa/VDJb3/aSH7bLhY1hJvlvNV713PFtb4p4Jo+rlE0gAoMsMCGcsiT982fImolSltg==}
+    engines: {node: '>= 10'}
+
+  '@intlify/shared@9.1.9':
+    resolution: {integrity: sha512-xKGM1d0EAxdDFCWedcYXOm6V5Pfw/TMudd6/qCdEb4tv0hk9EKeg7lwQF1azE0dP2phvx0yXxrt7UQK+IZjNdw==}
+    engines: {node: '>= 10'}
+
+  '@intlify/vue-devtools@9.1.9':
+    resolution: {integrity: sha512-YPehH9uL4vZcGXky4Ev5qQIITnHKIvsD2GKGXgqf+05osMUI6WSEQHaN9USRa318Rs8RyyPCiDfmA0hRu3k7og==}
+    engines: {node: '>= 10'}
+
+  '@istanbuljs/load-nyc-config@1.1.0':
+    resolution: {integrity: sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==}
+    engines: {node: '>=8'}
+
+  '@istanbuljs/schema@0.1.3':
+    resolution: {integrity: sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==}
+    engines: {node: '>=8'}
+
+  '@jest/console@27.5.1':
+    resolution: {integrity: sha512-kZ/tNpS3NXn0mlXXXPNuDZnb4c0oZ20r4K5eemM2k30ZC3G0T02nXUvyhf5YdbXWHPEJLc9qGLxEZ216MdL+Zg==}
+    engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
+
+  '@jest/core@27.5.1':
+    resolution: {integrity: sha512-AK6/UTrvQD0Cd24NSqmIA6rKsu0tKIxfiCducZvqxYdmMisOYAsdItspT+fQDQYARPf8XgjAFZi0ogW2agH5nQ==}
+    engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
+    peerDependencies:
+      node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0
+    peerDependenciesMeta:
+      node-notifier:
+        optional: true
+
+  '@jest/environment@27.5.1':
+    resolution: {integrity: sha512-/WQjhPJe3/ghaol/4Bq480JKXV/Rfw8nQdN7f41fM8VDHLcxKXou6QyXAh3EFr9/bVG3x74z1NWDkP87EiY8gA==}
+    engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
+
+  '@jest/fake-timers@27.5.1':
+    resolution: {integrity: sha512-/aPowoolwa07k7/oM3aASneNeBGCmGQsc3ugN4u6s4C/+s5M64MFo/+djTdiwcbQlRfFElGuDXWzaWj6QgKObQ==}
+    engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
+
+  '@jest/globals@27.5.1':
+    resolution: {integrity: sha512-ZEJNB41OBQQgGzgyInAv0UUfDDj3upmHydjieSxFvTRuZElrx7tXg/uVQ5hYVEwiXs3+aMsAeEc9X7xiSKCm4Q==}
+    engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
+
+  '@jest/reporters@27.5.1':
+    resolution: {integrity: sha512-cPXh9hWIlVJMQkVk84aIvXuBB4uQQmFqZiacloFuGiP3ah1sbCxCosidXFDfqG8+6fO1oR2dTJTlsOy4VFmUfw==}
+    engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
+    peerDependencies:
+      node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0
+    peerDependenciesMeta:
+      node-notifier:
+        optional: true
+
+  '@jest/source-map@27.5.1':
+    resolution: {integrity: sha512-y9NIHUYF3PJRlHk98NdC/N1gl88BL08aQQgu4k4ZopQkCw9t9cV8mtl3TV8b/YCB8XaVTFrmUTAJvjsntDireg==}
+    engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
+
+  '@jest/test-result@27.5.1':
+    resolution: {integrity: sha512-EW35l2RYFUcUQxFJz5Cv5MTOxlJIQs4I7gxzi2zVU7PJhOwfYq1MdC5nhSmYjX1gmMmLPvB3sIaC+BkcHRBfag==}
+    engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
+
+  '@jest/test-sequencer@27.5.1':
+    resolution: {integrity: sha512-LCheJF7WB2+9JuCS7VB/EmGIdQuhtqjRNI9A43idHv3E4KltCTsPsLxvdaubFHSYwY/fNjMWjl6vNRhDiN7vpQ==}
+    engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
+
+  '@jest/transform@27.5.1':
+    resolution: {integrity: sha512-ipON6WtYgl/1329g5AIJVbUuEh0wZVbdpGwC99Jw4LwuoBNS95MVphU6zOeD9pDkon+LLbFL7lOQRapbB8SCHw==}
+    engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
+
+  '@jest/types@27.5.1':
+    resolution: {integrity: sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==}
+    engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
+
+  '@jimp/bmp@0.10.3':
+    resolution: {integrity: sha512-keMOc5woiDmONXsB/6aXLR4Z5Q+v8lFq3EY2rcj2FmstbDMhRuGbmcBxlEgOqfRjwvtf/wOtJ3Of37oAWtVfLg==}
+    peerDependencies:
+      '@jimp/custom': '>=0.3.5'
+
+  '@jimp/core@0.10.3':
+    resolution: {integrity: sha512-Gd5IpL3U2bFIO57Fh/OA3HCpWm4uW/pU01E75rI03BXfTdz3T+J7TwvyG1XaqsQ7/DSlS99GXtLQPlfFIe28UA==}
+
+  '@jimp/custom@0.10.3':
+    resolution: {integrity: sha512-nZmSI+jwTi5IRyNLbKSXQovoeqsw+D0Jn0SxW08wYQvdkiWA8bTlDQFgQ7HVwCAKBm8oKkDB/ZEo9qvHJ+1gAQ==}
+
+  '@jimp/gif@0.10.3':
+    resolution: {integrity: sha512-vjlRodSfz1CrUvvrnUuD/DsLK1GHB/yDZXHthVdZu23zYJIW7/WrIiD1IgQ5wOMV7NocfrvPn2iqUfBP81/WWA==}
+    peerDependencies:
+      '@jimp/custom': '>=0.3.5'
+
+  '@jimp/jpeg@0.10.3':
+    resolution: {integrity: sha512-AAANwgUZOt6f6P7LZxY9lyJ9xclqutYJlsxt3JbriXUGJgrrFAIkcKcqv1nObgmQASSAQKYaMV9KdHjMlWFKlQ==}
+    peerDependencies:
+      '@jimp/custom': '>=0.3.5'
+
+  '@jimp/plugin-blit@0.10.3':
+    resolution: {integrity: sha512-5zlKlCfx4JWw9qUVC7GI4DzXyxDWyFvgZLaoGFoT00mlXlN75SarlDwc9iZ/2e2kp4bJWxz3cGgG4G/WXrbg3Q==}
+    peerDependencies:
+      '@jimp/custom': '>=0.3.5'
+
+  '@jimp/plugin-blur@0.10.3':
+    resolution: {integrity: sha512-cTOK3rjh1Yjh23jSfA6EHCHjsPJDEGLC8K2y9gM7dnTUK1y9NNmkFS23uHpyjgsWFIoH9oRh2SpEs3INjCpZhQ==}
+    peerDependencies:
+      '@jimp/custom': '>=0.3.5'
+
+  '@jimp/plugin-circle@0.10.3':
+    resolution: {integrity: sha512-51GAPIVelqAcfuUpaM5JWJ0iWl4vEjNXB7p4P7SX5udugK5bxXUjO6KA2qgWmdpHuCKtoNgkzWU9fNSuYp7tCA==}
+    peerDependencies:
+      '@jimp/custom': '>=0.3.5'
+
+  '@jimp/plugin-color@0.10.3':
+    resolution: {integrity: sha512-RgeHUElmlTH7vpI4WyQrz6u59spiKfVQbsG/XUzfWGamFSixa24ZDwX/yV/Ts+eNaz7pZeIuv533qmKPvw2ujg==}
+    peerDependencies:
+      '@jimp/custom': '>=0.3.5'
+
+  '@jimp/plugin-contain@0.10.3':
+    resolution: {integrity: sha512-bYJKW9dqzcB0Ihc6u7jSyKa3juStzbLs2LFr6fu8TzA2WkMS/R8h+ddkiO36+F9ILTWHP0CIA3HFe5OdOGcigw==}
+    peerDependencies:
+      '@jimp/custom': '>=0.3.5'
+      '@jimp/plugin-blit': '>=0.3.5'
+      '@jimp/plugin-resize': '>=0.3.5'
+      '@jimp/plugin-scale': '>=0.3.5'
+
+  '@jimp/plugin-cover@0.10.3':
+    resolution: {integrity: sha512-pOxu0cM0BRPzdV468n4dMocJXoMbTnARDY/EpC3ZW15SpMuc/dr1KhWQHgoQX5kVW1Wt8zgqREAJJCQ5KuPKDA==}
+    peerDependencies:
+      '@jimp/custom': '>=0.3.5'
+      '@jimp/plugin-crop': '>=0.3.5'
+      '@jimp/plugin-resize': '>=0.3.5'
+      '@jimp/plugin-scale': '>=0.3.5'
+
+  '@jimp/plugin-crop@0.10.3':
+    resolution: {integrity: sha512-nB7HgOjjl9PgdHr076xZ3Sr6qHYzeBYBs9qvs3tfEEUeYMNnvzgCCGtUl6eMakazZFCMk3mhKmcB9zQuHFOvkg==}
+    peerDependencies:
+      '@jimp/custom': '>=0.3.5'
+
+  '@jimp/plugin-displace@0.10.3':
+    resolution: {integrity: sha512-8t3fVKCH5IVqI4lewe4lFFjpxxr69SQCz5/tlpDLQZsrNScNJivHdQ09zljTrVTCSgeCqQJIKgH2Q7Sk/pAZ0w==}
+    peerDependencies:
+      '@jimp/custom': '>=0.3.5'
+
+  '@jimp/plugin-dither@0.10.3':
+    resolution: {integrity: sha512-JCX/oNSnEg1kGQ8ffZ66bEgQOLCY3Rn+lrd6v1jjLy/mn9YVZTMsxLtGCXpiCDC2wG/KTmi4862ysmP9do9dAQ==}
+    peerDependencies:
+      '@jimp/custom': '>=0.3.5'
+
+  '@jimp/plugin-fisheye@0.10.3':
+    resolution: {integrity: sha512-RRZb1wqe+xdocGcFtj2xHU7sF7xmEZmIa6BmrfSchjyA2b32TGPWKnP3qyj7p6LWEsXn+19hRYbjfyzyebPElQ==}
+    peerDependencies:
+      '@jimp/custom': '>=0.3.5'
+
+  '@jimp/plugin-flip@0.10.3':
+    resolution: {integrity: sha512-0epbi8XEzp0wmSjoW9IB0iMu0yNF17aZOxLdURCN3Zr+8nWPs5VNIMqSVa1Y62GSyiMDpVpKF/ITiXre+EqrPg==}
+    peerDependencies:
+      '@jimp/custom': '>=0.3.5'
+      '@jimp/plugin-rotate': '>=0.3.5'
+
+  '@jimp/plugin-gaussian@0.10.3':
+    resolution: {integrity: sha512-25eHlFbHUDnMMGpgRBBeQ2AMI4wsqCg46sue0KklI+c2BaZ+dGXmJA5uT8RTOrt64/K9Wz5E+2n7eBnny4dfpQ==}
+    peerDependencies:
+      '@jimp/custom': '>=0.3.5'
+
+  '@jimp/plugin-invert@0.10.3':
+    resolution: {integrity: sha512-effYSApWY/FbtlzqsKXlTLkgloKUiHBKjkQnqh5RL4oQxh/33j6aX+HFdDyQKtsXb8CMd4xd7wyiD2YYabTa0g==}
+    peerDependencies:
+      '@jimp/custom': '>=0.3.5'
+
+  '@jimp/plugin-mask@0.10.3':
+    resolution: {integrity: sha512-twrg8q8TIhM9Z6Jcu9/5f+OCAPaECb0eKrrbbIajJqJ3bCUlj5zbfgIhiQIzjPJ6KjpnFPSqHQfHkU1Vvk/nVw==}
+    peerDependencies:
+      '@jimp/custom': '>=0.3.5'
+
+  '@jimp/plugin-normalize@0.10.3':
+    resolution: {integrity: sha512-xkb5eZI/mMlbwKkDN79+1/t/+DBo8bBXZUMsT4gkFgMRKNRZ6NQPxlv1d3QpRzlocsl6UMxrHnhgnXdLAcgrXw==}
+    peerDependencies:
+      '@jimp/custom': '>=0.3.5'
+
+  '@jimp/plugin-print@0.10.3':
+    resolution: {integrity: sha512-wjRiI6yjXsAgMe6kVjizP+RgleUCLkH256dskjoNvJzmzbEfO7xQw9g6M02VET+emnbY0CO83IkrGm2q43VRyg==}
+    peerDependencies:
+      '@jimp/custom': '>=0.3.5'
+      '@jimp/plugin-blit': '>=0.3.5'
+
+  '@jimp/plugin-resize@0.10.3':
+    resolution: {integrity: sha512-rf8YmEB1d7Sg+g4LpqF0Mp+dfXfb6JFJkwlAIWPUOR7lGsPWALavEwTW91c0etEdnp0+JB9AFpy6zqq7Lwkq6w==}
+    peerDependencies:
+      '@jimp/custom': '>=0.3.5'
+
+  '@jimp/plugin-rotate@0.10.3':
+    resolution: {integrity: sha512-YXLlRjm18fkW9MOHUaVAxWjvgZM851ofOipytz5FyKp4KZWDLk+dZK1JNmVmK7MyVmAzZ5jsgSLhIgj+GgN0Eg==}
+    peerDependencies:
+      '@jimp/custom': '>=0.3.5'
+      '@jimp/plugin-blit': '>=0.3.5'
+      '@jimp/plugin-crop': '>=0.3.5'
+      '@jimp/plugin-resize': '>=0.3.5'
+
+  '@jimp/plugin-scale@0.10.3':
+    resolution: {integrity: sha512-5DXD7x7WVcX1gUgnlFXQa8F+Q3ThRYwJm+aesgrYvDOY+xzRoRSdQvhmdd4JEEue3lyX44DvBSgCIHPtGcEPaw==}
+    peerDependencies:
+      '@jimp/custom': '>=0.3.5'
+      '@jimp/plugin-resize': '>=0.3.5'
+
+  '@jimp/plugin-shadow@0.10.3':
+    resolution: {integrity: sha512-/nkFXpt2zVcdP4ETdkAUL0fSzyrC5ZFxdcphbYBodqD7fXNqChS/Un1eD4xCXWEpW8cnG9dixZgQgStjywH0Mg==}
+    peerDependencies:
+      '@jimp/custom': '>=0.3.5'
+      '@jimp/plugin-blur': '>=0.3.5'
+      '@jimp/plugin-resize': '>=0.3.5'
+
+  '@jimp/plugin-threshold@0.10.3':
+    resolution: {integrity: sha512-Dzh0Yq2wXP2SOnxcbbiyA4LJ2luwrdf1MghNIt9H+NX7B+IWw/N8qA2GuSm9n4BPGSLluuhdAWJqHcTiREriVA==}
+    peerDependencies:
+      '@jimp/custom': '>=0.3.5'
+      '@jimp/plugin-color': '>=0.8.0'
+      '@jimp/plugin-resize': '>=0.8.0'
+
+  '@jimp/plugins@0.10.3':
+    resolution: {integrity: sha512-jTT3/7hOScf0EIKiAXmxwayHhryhc1wWuIe3FrchjDjr9wgIGNN2a7XwCgPl3fML17DXK1x8EzDneCdh261bkw==}
+    peerDependencies:
+      '@jimp/custom': '>=0.3.5'
+
+  '@jimp/png@0.10.3':
+    resolution: {integrity: sha512-YKqk/dkl+nGZxSYIDQrqhmaP8tC3IK8H7dFPnnzFVvbhDnyYunqBZZO3SaZUKTichClRw8k/CjBhbc+hifSGWg==}
+    peerDependencies:
+      '@jimp/custom': '>=0.3.5'
+
+  '@jimp/tiff@0.10.3':
+    resolution: {integrity: sha512-7EsJzZ5Y/EtinkBGuwX3Bi4S+zgbKouxjt9c82VJTRJOQgLWsE/RHqcyRCOQBhHAZ9QexYmDz34medfLKdoX0g==}
+    peerDependencies:
+      '@jimp/custom': '>=0.3.5'
+
+  '@jimp/types@0.10.3':
+    resolution: {integrity: sha512-XGmBakiHZqseSWr/puGN+CHzx0IKBSpsKlmEmsNV96HKDiP6eu8NSnwdGCEq2mmIHe0JNcg1hqg59hpwtQ7Tiw==}
+    peerDependencies:
+      '@jimp/custom': '>=0.3.5'
+
+  '@jimp/utils@0.10.3':
+    resolution: {integrity: sha512-VcSlQhkil4ReYmg1KkN+WqHyYfZ2XfZxDsKAHSfST1GEz/RQHxKZbX+KhFKtKflnL0F4e6DlNQj3vznMNXCR2w==}
+
+  '@jridgewell/gen-mapping@0.3.5':
+    resolution: {integrity: sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==}
+    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/source-map@0.3.6':
+    resolution: {integrity: sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ==}
+
+  '@jridgewell/sourcemap-codec@1.4.15':
+    resolution: {integrity: sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==}
+
+  '@jridgewell/trace-mapping@0.3.25':
+    resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==}
+
+  '@nodelib/fs.scandir@2.1.5':
+    resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==}
+    engines: {node: '>= 8'}
+
+  '@nodelib/fs.stat@2.0.5':
+    resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==}
+    engines: {node: '>= 8'}
+
+  '@nodelib/fs.walk@1.2.8':
+    resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==}
+    engines: {node: '>= 8'}
+
+  '@pkgr/core@0.1.1':
+    resolution: {integrity: sha512-cq8o4cWH0ibXh9VGi5P20Tu9XF/0fFXl9EUinr9QfTM7a7p0oTA4iJRCQWppXR1Pg8dSM0UCItCkPwsk9qWWYA==}
+    engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0}
+
+  '@rollup/pluginutils@4.2.1':
+    resolution: {integrity: sha512-iKnFXr7NkdZAIHiIWE+BX5ULi/ucVFYWD6TbAV+rZctiRTY2PL6tsIKhoIOaoskiWAkgu+VsbXgUVDNLHf+InQ==}
+    engines: {node: '>= 8.0.0'}
+
+  '@rollup/pluginutils@5.1.0':
+    resolution: {integrity: sha512-XTIWOPPcpvyKI6L1NHo0lFlCyznUEyPmPY1mc3KpPVDYulHSTvyeLNVW00QTLIAFNhR3kYnJTQHeGqU4M3n09g==}
+    engines: {node: '>=14.0.0'}
+    peerDependencies:
+      rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0
+    peerDependenciesMeta:
+      rollup:
+        optional: true
+
+  '@sinonjs/commons@1.8.6':
+    resolution: {integrity: sha512-Ky+XkAkqPZSm3NLBeUng77EBQl3cmeJhITaGHdYH8kjVB+aun3S4XBRti2zt17mtt0mIUDiNxYeoJm6drVvBJQ==}
+
+  '@sinonjs/fake-timers@8.1.0':
+    resolution: {integrity: sha512-OAPJUAtgeINhh/TAlUID4QTs53Njm7xzddaVlEs/SXwgtiD1tW22zAB/W1wdqfrpmikgaWQ9Fw6Ws+hsiRm5Vg==}
+
+  '@tootallnate/once@1.1.2':
+    resolution: {integrity: sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==}
+    engines: {node: '>= 6'}
+
+  '@types/babel__core@7.20.5':
+    resolution: {integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==}
+
+  '@types/babel__generator@7.6.8':
+    resolution: {integrity: sha512-ASsj+tpEDsEiFr1arWrlN6V3mdfjRMZt6LtK/Vp/kreFLnr5QH5+DhvD5nINYZXzwJvXeGq+05iUXcAzVrqWtw==}
+
+  '@types/babel__template@7.4.4':
+    resolution: {integrity: sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==}
+
+  '@types/babel__traverse@7.20.6':
+    resolution: {integrity: sha512-r1bzfrm0tomOI8g1SzvCaQHo6Lcv6zu0EA+W2kHrt8dyrHQxGzBBL4kdkzIS+jBMV+EYcMAEAqXqYaLJq5rOZg==}
+
+  '@types/estree@1.0.5':
+    resolution: {integrity: sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==}
+
+  '@types/graceful-fs@4.1.9':
+    resolution: {integrity: sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ==}
+
+  '@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/json5@0.0.29':
+    resolution: {integrity: sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==}
+
+  '@types/node@20.14.9':
+    resolution: {integrity: sha512-06OCtnTXtWOZBJlRApleWndH4JsRVs1pDCc8dLSQp+7PpUpX3ePdHyeNSFTeSe7FtKyQkrlPvHwJOW3SLd8Oyg==}
+
+  '@types/prettier@2.7.3':
+    resolution: {integrity: sha512-+68kP9yzs4LMp7VNh8gdzMSPZFL44MLGqiHWvttYJe+6qnuVr4Ek9wSBQoveqY/r+LwjCcU29kNVkidwim+kYA==}
+
+  '@types/stack-utils@2.0.3':
+    resolution: {integrity: sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==}
+
+  '@types/yargs-parser@21.0.3':
+    resolution: {integrity: sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==}
+
+  '@types/yargs@16.0.9':
+    resolution: {integrity: sha512-tHhzvkFXZQeTECenFoRljLBYPZJ7jAVxqqtEI0qTLOmuultnFp4I9yKE17vTuhf7BkhCu7I4XuemPgikDVuYqA==}
+
+  '@typescript-eslint/eslint-plugin@7.14.1':
+    resolution: {integrity: sha512-aAJd6bIf2vvQRjUG3ZkNXkmBpN+J7Wd0mfQiiVCJMu9Z5GcZZdcc0j8XwN/BM97Fl7e3SkTXODSk4VehUv7CGw==}
+    engines: {node: ^18.18.0 || >=20.0.0}
+    peerDependencies:
+      '@typescript-eslint/parser': ^7.0.0
+      eslint: ^8.56.0
+      typescript: '*'
+    peerDependenciesMeta:
+      typescript:
+        optional: true
+
+  '@typescript-eslint/parser@7.14.1':
+    resolution: {integrity: sha512-8lKUOebNLcR0D7RvlcloOacTOWzOqemWEWkKSVpMZVF/XVcwjPR+3MD08QzbW9TCGJ+DwIc6zUSGZ9vd8cO1IA==}
+    engines: {node: ^18.18.0 || >=20.0.0}
+    peerDependencies:
+      eslint: ^8.56.0
+      typescript: '*'
+    peerDependenciesMeta:
+      typescript:
+        optional: true
+
+  '@typescript-eslint/scope-manager@7.14.1':
+    resolution: {integrity: sha512-gPrFSsoYcsffYXTOZ+hT7fyJr95rdVe4kGVX1ps/dJ+DfmlnjFN/GcMxXcVkeHDKqsq6uAcVaQaIi3cFffmAbA==}
+    engines: {node: ^18.18.0 || >=20.0.0}
+
+  '@typescript-eslint/type-utils@7.14.1':
+    resolution: {integrity: sha512-/MzmgNd3nnbDbOi3LfasXWWe292+iuo+umJ0bCCMCPc1jLO/z2BQmWUUUXvXLbrQey/JgzdF/OV+I5bzEGwJkQ==}
+    engines: {node: ^18.18.0 || >=20.0.0}
+    peerDependencies:
+      eslint: ^8.56.0
+      typescript: '*'
+    peerDependenciesMeta:
+      typescript:
+        optional: true
+
+  '@typescript-eslint/types@7.14.1':
+    resolution: {integrity: sha512-mL7zNEOQybo5R3AavY+Am7KLv8BorIv7HCYS5rKoNZKQD9tsfGUpO4KdAn3sSUvTiS4PQkr2+K0KJbxj8H9NDg==}
+    engines: {node: ^18.18.0 || >=20.0.0}
+
+  '@typescript-eslint/typescript-estree@7.14.1':
+    resolution: {integrity: sha512-k5d0VuxViE2ulIO6FbxxSZaxqDVUyMbXcidC8rHvii0I56XZPv8cq+EhMns+d/EVIL41sMXqRbK3D10Oza1bbA==}
+    engines: {node: ^18.18.0 || >=20.0.0}
+    peerDependencies:
+      typescript: '*'
+    peerDependenciesMeta:
+      typescript:
+        optional: true
+
+  '@typescript-eslint/utils@7.14.1':
+    resolution: {integrity: sha512-CMmVVELns3nak3cpJhZosDkm63n+DwBlDX8g0k4QUa9BMnF+lH2lr3d130M1Zt1xxmB3LLk3NV7KQCq86ZBBhQ==}
+    engines: {node: ^18.18.0 || >=20.0.0}
+    peerDependencies:
+      eslint: ^8.56.0
+
+  '@typescript-eslint/visitor-keys@7.14.1':
+    resolution: {integrity: sha512-Crb+F75U1JAEtBeQGxSKwI60hZmmzaqA3z9sYsVm8X7W5cwLEm5bRe0/uXS6+MR/y8CVpKSR/ontIAIEPFcEkA==}
+    engines: {node: ^18.18.0 || >=20.0.0}
+
+  '@vitejs/plugin-legacy@4.1.1':
+    resolution: {integrity: sha512-um3gbVouD2Q/g19C0qpDfHwveXDCAHzs8OC3e9g6aXpKoD1H14himgs7wkMnhAynBJy7QqUoZNAXDuqN8zLR2g==}
+    engines: {node: ^14.18.0 || >=16.0.0}
+    peerDependencies:
+      terser: ^5.4.0
+      vite: ^4.0.0
+
+  '@vitejs/plugin-vue-jsx@3.1.0':
+    resolution: {integrity: sha512-w9M6F3LSEU5kszVb9An2/MmXNxocAnUb3WhRr8bHlimhDrXNt6n6D2nJQR3UXpGlZHh/EsgouOHCsM8V3Ln+WA==}
+    engines: {node: ^14.18.0 || >=16.0.0}
+    peerDependencies:
+      vite: ^4.0.0 || ^5.0.0
+      vue: ^3.0.0
+
+  '@vitejs/plugin-vue@4.6.2':
+    resolution: {integrity: sha512-kqf7SGFoG+80aZG6Pf+gsZIVvGSCKE98JbiWqcCV9cThtg91Jav0yvYFC9Zb+jKetNGF6ZKeoaxgZfND21fWKw==}
+    engines: {node: ^14.18.0 || >=16.0.0}
+    peerDependencies:
+      vite: ^4.0.0 || ^5.0.0
+      vue: ^3.2.25
+
+  '@volar/language-core@1.11.1':
+    resolution: {integrity: sha512-dOcNn3i9GgZAcJt43wuaEykSluAuOkQgzni1cuxLxTV0nJKanQztp7FxyswdRILaKH+P2XZMPRp2S4MV/pElCw==}
+
+  '@volar/source-map@1.11.1':
+    resolution: {integrity: sha512-hJnOnwZ4+WT5iupLRnuzbULZ42L7BWWPMmruzwtLhJfpDVoZLjNBxHDi2sY2bgZXCKlpU5XcsMFoYrsQmPhfZg==}
+
+  '@volar/typescript@1.11.1':
+    resolution: {integrity: sha512-iU+t2mas/4lYierSnoFOeRFQUhAEMgsFuQxoxvwn5EdQopw43j+J27a4lt9LMInx1gLJBC6qL14WYGlgymaSMQ==}
+
+  '@vue/babel-helper-vue-transform-on@1.2.2':
+    resolution: {integrity: sha512-nOttamHUR3YzdEqdM/XXDyCSdxMA9VizUKoroLX6yTyRtggzQMHXcmwh8a7ZErcJttIBIc9s68a1B8GZ+Dmvsw==}
+
+  '@vue/babel-plugin-jsx@1.2.2':
+    resolution: {integrity: sha512-nYTkZUVTu4nhP199UoORePsql0l+wj7v/oyQjtThUVhJl1U+6qHuoVhIvR3bf7eVKjbCK+Cs2AWd7mi9Mpz9rA==}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+    peerDependenciesMeta:
+      '@babel/core':
+        optional: true
+
+  '@vue/babel-plugin-resolve-type@1.2.2':
+    resolution: {integrity: sha512-EntyroPwNg5IPVdUJupqs0CFzuf6lUrVvCspmv2J1FITLeGnUCuoGNNk78dgCusxEiYj6RMkTJflGSxk5aIC4A==}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+
+  '@vue/compiler-core@3.3.11':
+    resolution: {integrity: sha512-h97/TGWBilnLuRaj58sxNrsUU66fwdRKLOLQ9N/5iNDfp+DZhYH9Obhe0bXxhedl8fjAgpRANpiZfbgWyruQ0w==}
+
+  '@vue/compiler-core@3.4.30':
+    resolution: {integrity: sha512-ZL8y4Xxdh8O6PSwfdZ1IpQ24PjTAieOz3jXb/MDTfDtANcKBMxg1KLm6OX2jofsaQGYfIVzd3BAG22i56/cF1w==}
+
+  '@vue/compiler-dom@3.3.11':
+    resolution: {integrity: sha512-zoAiUIqSKqAJ81WhfPXYmFGwDRuO+loqLxvXmfUdR5fOitPoUiIeFI9cTTyv9MU5O1+ZZglJVTusWzy+wfk5hw==}
+
+  '@vue/compiler-dom@3.4.30':
+    resolution: {integrity: sha512-+16Sd8lYr5j/owCbr9dowcNfrHd+pz+w2/b5Lt26Oz/kB90C9yNbxQ3bYOvt7rI2bxk0nqda39hVcwDFw85c2Q==}
+
+  '@vue/compiler-sfc@3.3.11':
+    resolution: {integrity: sha512-U4iqPlHO0KQeK1mrsxCN0vZzw43/lL8POxgpzcJweopmqtoYy9nljJzWDIQS3EfjiYhfdtdk9Gtgz7MRXnz3GA==}
+
+  '@vue/compiler-sfc@3.4.30':
+    resolution: {integrity: sha512-8vElKklHn/UY8+FgUFlQrYAPbtiSB2zcgeRKW7HkpSRn/JjMRmZvuOtwDx036D1aqKNSTtXkWRfqx53Qb+HmMg==}
+
+  '@vue/compiler-ssr@3.3.11':
+    resolution: {integrity: sha512-Zd66ZwMvndxRTgVPdo+muV4Rv9n9DwQ4SSgWWKWkPFebHQfVYRrVjeygmmDmPewsHyznCNvJ2P2d6iOOhdv8Qg==}
+
+  '@vue/compiler-ssr@3.4.30':
+    resolution: {integrity: sha512-ZJ56YZGXJDd6jky4mmM0rNaNP6kIbQu9LTKZDhcpddGe/3QIalB1WHHmZ6iZfFNyj5mSypTa4+qDJa5VIuxMSg==}
+
+  '@vue/devtools-api@6.6.3':
+    resolution: {integrity: sha512-0MiMsFma/HqA6g3KLKn+AGpL1kgKhFWszC9U29NfpWK5LE7bjeXxySWJrOJ77hBz+TBrBQ7o4QJqbPbqbs8rJw==}
+
+  '@vue/language-core@1.8.27':
+    resolution: {integrity: sha512-L8Kc27VdQserNaCUNiSFdDl9LWT24ly8Hpwf1ECy3aFb9m6bDhBGQYOujDm21N7EW3moKIOKEanQwe1q5BK+mA==}
+    peerDependencies:
+      typescript: '*'
+    peerDependenciesMeta:
+      typescript:
+        optional: true
+
+  '@vue/reactivity-transform@3.3.11':
+    resolution: {integrity: sha512-fPGjH0wqJo68A0wQ1k158utDq/cRyZNlFoxGwNScE28aUFOKFEnCBsvyD8jHn+0kd0UKVpuGuaZEQ6r9FJRqCg==}
+
+  '@vue/reactivity@3.4.30':
+    resolution: {integrity: sha512-bVJurnCe3LS0JII8PPoAA63Zd2MBzcKrEzwdQl92eHCcxtIbxD2fhNwJpa+KkM3Y/A4T5FUnmdhgKwOf6BfbcA==}
+
+  '@vue/runtime-core@3.4.30':
+    resolution: {integrity: sha512-qaFEbnNpGz+tlnkaualomogzN8vBLkgzK55uuWjYXbYn039eOBZrWxyXWq/7qh9Bz2FPifZqGjVDl/FXiq9L2g==}
+
+  '@vue/runtime-dom@3.4.30':
+    resolution: {integrity: sha512-tV6B4YiZRj5QsaJgw2THCy5C1H+2UeywO9tqgWEc21tn85qHEERndHN/CxlyXvSBFrpmlexCIdnqPuR9RM9thw==}
+
+  '@vue/server-renderer@3.3.11':
+    resolution: {integrity: sha512-AIWk0VwwxCAm4wqtJyxBylRTXSy1wCLOKbWxHaHiu14wjsNYtiRCSgVuqEPVuDpErOlRdNnuRgipQfXRLjLN5A==}
+    peerDependencies:
+      vue: 3.3.11
+
+  '@vue/server-renderer@3.4.30':
+    resolution: {integrity: sha512-TBD3eqR1DeDc0cMrXS/vEs/PWzq1uXxnvjoqQuDGFIEHFIwuDTX/KWAQKIBjyMWLFHEeTDGYVsYci85z2UbTDg==}
+    peerDependencies:
+      vue: 3.4.30
+
+  '@vue/shared@3.3.11':
+    resolution: {integrity: sha512-u2G8ZQ9IhMWTMXaWqZycnK4UthG1fA238CD+DP4Dm4WJi5hdUKKLg0RMRaRpDPNMdkTwIDkp7WtD0Rd9BH9fLw==}
+
+  '@vue/shared@3.4.30':
+    resolution: {integrity: sha512-CLg+f8RQCHQnKvuHY9adMsMaQOcqclh6Z5V9TaoMgy0ut0tz848joZ7/CYFFyF/yZ5i2yaw7Fn498C+CNZVHIg==}
+
+  abab@2.0.6:
+    resolution: {integrity: sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==}
+    deprecated: Use your platform's native atob() and btoa() methods instead
+
+  accepts@1.3.8:
+    resolution: {integrity: sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==}
+    engines: {node: '>= 0.6'}
+
+  acorn-globals@6.0.0:
+    resolution: {integrity: sha512-ZQl7LOWaF5ePqqcX4hLuv/bLXYQNfNWw2c0/yX/TsPRKamzHcTGQnlCjHT3TsmkOUVEPS3crCxiPfdzE/Trlhg==}
+
+  acorn-jsx@5.3.2:
+    resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==}
+    peerDependencies:
+      acorn: ^6.0.0 || ^7.0.0 || ^8.0.0
+
+  acorn-walk@7.2.0:
+    resolution: {integrity: sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==}
+    engines: {node: '>=0.4.0'}
+
+  acorn@7.4.1:
+    resolution: {integrity: sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==}
+    engines: {node: '>=0.4.0'}
+    hasBin: true
+
+  acorn@8.12.0:
+    resolution: {integrity: sha512-RTvkC4w+KNXrM39/lWCUaG0IbRkWdCv7W/IOW9oU6SawyxulvkQy5HQPVTKxEjczcUvapcrw3cFx/60VN/NRNw==}
+    engines: {node: '>=0.4.0'}
+    hasBin: true
+
+  address@1.2.2:
+    resolution: {integrity: sha512-4B/qKCfeE/ODUaAUpSwfzazo5x29WD4r3vXiWsB7I2mSDAihwEqKO+g8GELZUQSSAo5e1XTYh3ZVfLyxBc12nA==}
+    engines: {node: '>= 10.0.0'}
+
+  agent-base@6.0.2:
+    resolution: {integrity: sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==}
+    engines: {node: '>= 6.0.0'}
+
+  ajv@6.12.6:
+    resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==}
+
+  ansi-escapes@4.3.2:
+    resolution: {integrity: sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==}
+    engines: {node: '>=8'}
+
+  ansi-regex@5.0.1:
+    resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==}
+    engines: {node: '>=8'}
+
+  ansi-styles@3.2.1:
+    resolution: {integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==}
+    engines: {node: '>=4'}
+
+  ansi-styles@4.3.0:
+    resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==}
+    engines: {node: '>=8'}
+
+  ansi-styles@5.2.0:
+    resolution: {integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==}
+    engines: {node: '>=10'}
+
+  any-base@1.1.0:
+    resolution: {integrity: sha512-uMgjozySS8adZZYePpaWs8cxB9/kdzmpX6SgJZ+wbz1K5eYk5QMYDVJaZKhxyIHUdnnJkfR7SVgStgH7LkGUyg==}
+
+  anymatch@3.1.3:
+    resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==}
+    engines: {node: '>= 8'}
+
+  argparse@1.0.10:
+    resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==}
+
+  argparse@2.0.1:
+    resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==}
+
+  array-buffer-byte-length@1.0.1:
+    resolution: {integrity: sha512-ahC5W1xgou+KTXix4sAO8Ki12Q+jf4i0+tmk3sC+zgcynshkHxzpXdImBehiUYKKKDwvfFiJl1tZt6ewscS1Mg==}
+    engines: {node: '>= 0.4'}
+
+  array-flatten@1.1.1:
+    resolution: {integrity: sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==}
+
+  array-includes@3.1.8:
+    resolution: {integrity: sha512-itaWrbYbqpGXkGhZPGUulwnhVf5Hpy1xiCFsGqyIGglbBxmG5vSjxQen3/WGOjPpNEv1RtBLKxbmVXm8HpJStQ==}
+    engines: {node: '>= 0.4'}
+
+  array-union@2.1.0:
+    resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==}
+    engines: {node: '>=8'}
+
+  array.prototype.findlastindex@1.2.5:
+    resolution: {integrity: sha512-zfETvRFA8o7EiNn++N5f/kaCw221hrpGsDmcpndVupkPzEc1Wuf3VgC0qby1BbHs7f5DVYjgtEU2LLh5bqeGfQ==}
+    engines: {node: '>= 0.4'}
+
+  array.prototype.flat@1.3.2:
+    resolution: {integrity: sha512-djYB+Zx2vLewY8RWlNCUdHjDXs2XOgm602S9E7P/UpHgfeHL00cRiIF+IN/G/aUJ7kGPb6yO/ErDI5V2s8iycA==}
+    engines: {node: '>= 0.4'}
+
+  array.prototype.flatmap@1.3.2:
+    resolution: {integrity: sha512-Ewyx0c9PmpcsByhSW4r+9zDU7sGjFc86qf/kKtuSCRdhfbk0SNLLkaT5qvcHnRGgc5NP/ly/y+qkXkqONX54CQ==}
+    engines: {node: '>= 0.4'}
+
+  arraybuffer.prototype.slice@1.0.3:
+    resolution: {integrity: sha512-bMxMKAjg13EBSVscxTaYA4mRc5t1UAXa2kXiGTNfZ079HIWXEkKmkgFrh/nJqamaLSrXO5H4WFFkPEaLJWbs3A==}
+    engines: {node: '>= 0.4'}
+
+  asynckit@0.4.0:
+    resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==}
+
+  autoprefixer@10.4.19:
+    resolution: {integrity: sha512-BaENR2+zBZ8xXhM4pUaKUxlVdxZ0EZhjvbopwnXmxRUfqDmwSpC2lAi/QXvx7NRdPCo1WKEcEF6mV64si1z4Ew==}
+    engines: {node: ^10 || ^12 || >=14}
+    hasBin: true
+    peerDependencies:
+      postcss: ^8.1.0
+
+  available-typed-arrays@1.0.7:
+    resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==}
+    engines: {node: '>= 0.4'}
+
+  axios@0.19.2:
+    resolution: {integrity: sha512-fjgm5MvRHLhx+osE2xoekY70AhARk3a6hkN+3Io1jc00jtquGvxYlKlsFUhmUET0V5te6CcZI7lcv2Ym61mjHA==}
+    deprecated: Critical security vulnerability fixed in v0.21.1. For more information, see https://github.com/axios/axios/pull/3410
+
+  axios@0.21.4:
+    resolution: {integrity: sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==}
+
+  babel-jest@27.5.1:
+    resolution: {integrity: sha512-cdQ5dXjGRd0IBRATiQ4mZGlGlRE8kJpjPOixdNRdT+m3UcNqmYWN6rK6nvtXYfY3D76cb8s/O1Ss8ea24PIwcg==}
+    engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
+    peerDependencies:
+      '@babel/core': ^7.8.0
+
+  babel-plugin-istanbul@6.1.1:
+    resolution: {integrity: sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==}
+    engines: {node: '>=8'}
+
+  babel-plugin-jest-hoist@27.5.1:
+    resolution: {integrity: sha512-50wCwD5EMNW4aRpOwtqzyZHIewTYNxLA4nhB+09d8BIssfNfzBRhkBIHiaPv1Si226TQSvp8gxAJm2iY2qs2hQ==}
+    engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
+
+  babel-plugin-polyfill-corejs2@0.4.11:
+    resolution: {integrity: sha512-sMEJ27L0gRHShOh5G54uAAPaiCOygY/5ratXuiyb2G46FmlSpc9eFCzYVyDiPxfNbwzA7mYahmjQc5q+CZQ09Q==}
+    peerDependencies:
+      '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0
+
+  babel-plugin-polyfill-corejs3@0.10.4:
+    resolution: {integrity: sha512-25J6I8NGfa5YkCDogHRID3fVCadIR8/pGl1/spvCkzb6lVn6SR3ojpx9nOn9iEBcUsjY24AmdKm5khcfKdylcg==}
+    peerDependencies:
+      '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0
+
+  babel-plugin-polyfill-regenerator@0.6.2:
+    resolution: {integrity: sha512-2R25rQZWP63nGwaAswvDazbPXfrM3HwVoBXK6HcqeKrSrL/JqcC/rDcf95l4r7LXLyxDXc8uQDa064GubtCABg==}
+    peerDependencies:
+      '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0
+
+  babel-preset-current-node-syntax@1.0.1:
+    resolution: {integrity: sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ==}
+    peerDependencies:
+      '@babel/core': ^7.0.0
+
+  babel-preset-jest@27.5.1:
+    resolution: {integrity: sha512-Nptf2FzlPCWYuJg41HBqXVT8ym6bXOevuCTbhxlUpjwtysGaIWFvDEjp4y+G7fl13FgOdjs7P/DmErqH7da0Ag==}
+    engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
+    peerDependencies:
+      '@babel/core': ^7.0.0
+
+  balanced-match@1.0.2:
+    resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==}
+
+  base64-js@1.5.1:
+    resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==}
+
+  base64url@3.0.1:
+    resolution: {integrity: sha512-ir1UPr3dkwexU7FdV8qBBbNDRUhMmIekYMFZfi+C/sLNnRESKPl23nB9b2pltqfOQNnGzsDdId90AEtG5tCx4A==}
+    engines: {node: '>=6.0.0'}
+
+  binary-extensions@2.3.0:
+    resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==}
+    engines: {node: '>=8'}
+
+  bmp-js@0.1.0:
+    resolution: {integrity: sha512-vHdS19CnY3hwiNdkaqk93DvjVLfbEcI8mys4UjuWrlX1haDmroo8o4xCzh4wD6DGV6HxRCyauwhHRqMTfERtjw==}
+
+  body-parser@1.20.2:
+    resolution: {integrity: sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==}
+    engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16}
+
+  boolbase@1.0.0:
+    resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==}
+
+  brace-expansion@1.1.11:
+    resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==}
+
+  brace-expansion@2.0.1:
+    resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==}
+
+  braces@3.0.3:
+    resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==}
+    engines: {node: '>=8'}
+
+  browser-process-hrtime@1.0.0:
+    resolution: {integrity: sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow==}
+
+  browserslist@4.23.1:
+    resolution: {integrity: sha512-TUfofFo/KsK/bWZ9TWQ5O26tsWW4Uhmt8IYklbnUa70udB6P2wA7w7o4PY4muaEPBQaAX+CEnmmIA41NVHtPVw==}
+    engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7}
+    hasBin: true
+
+  bser@2.1.1:
+    resolution: {integrity: sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==}
+
+  buffer-equal@0.0.1:
+    resolution: {integrity: sha512-RgSV6InVQ9ODPdLWJ5UAqBqJBOg370Nz6ZQtRzpt6nUjc8v0St97uJ4PYC6NztqIScrAXafKM3mZPMygSe1ggA==}
+    engines: {node: '>=0.4.0'}
+
+  buffer-from@1.1.2:
+    resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==}
+
+  buffer@5.7.1:
+    resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==}
+
+  bytes@3.1.2:
+    resolution: {integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==}
+    engines: {node: '>= 0.8'}
+
+  cac@6.7.9:
+    resolution: {integrity: sha512-XN5qEpfNQCJ8jRaZgitSkkukjMRCGio+X3Ks5KUbGGlPbV+pSem1l9VuzooCBXOiMFshUZgyYqg6rgN8rjkb/w==}
+    engines: {node: '>=8'}
+
+  call-bind@1.0.7:
+    resolution: {integrity: sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==}
+    engines: {node: '>= 0.4'}
+
+  callsites@3.1.0:
+    resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==}
+    engines: {node: '>=6'}
+
+  camelcase@5.3.1:
+    resolution: {integrity: sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==}
+    engines: {node: '>=6'}
+
+  camelcase@6.3.0:
+    resolution: {integrity: sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==}
+    engines: {node: '>=10'}
+
+  caniuse-lite@1.0.30001637:
+    resolution: {integrity: sha512-1x0qRI1mD1o9e+7mBI7XtzFAP4XszbHaVWsMiGbSPLYekKTJF7K+FNk6AsXH4sUpc+qrsI3pVgf1Jdl/uGkuSQ==}
+
+  chalk@2.4.2:
+    resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==}
+    engines: {node: '>=4'}
+
+  chalk@4.1.2:
+    resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==}
+    engines: {node: '>=10'}
+
+  char-regex@1.0.2:
+    resolution: {integrity: sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==}
+    engines: {node: '>=10'}
+
+  chardet@0.7.0:
+    resolution: {integrity: sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==}
+
+  chokidar@3.6.0:
+    resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==}
+    engines: {node: '>= 8.10.0'}
+
+  ci-info@3.9.0:
+    resolution: {integrity: sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==}
+    engines: {node: '>=8'}
+
+  cjs-module-lexer@1.3.1:
+    resolution: {integrity: sha512-a3KdPAANPbNE4ZUv9h6LckSl9zLsYOP4MBmhIPkRaeyybt+r4UghLvq+xw/YwUcC1gqylCkL4rdVs3Lwupjm4Q==}
+
+  cli-cursor@3.1.0:
+    resolution: {integrity: sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==}
+    engines: {node: '>=8'}
+
+  cli-width@3.0.0:
+    resolution: {integrity: sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==}
+    engines: {node: '>= 10'}
+
+  cliui@7.0.4:
+    resolution: {integrity: sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==}
+
+  co@4.6.0:
+    resolution: {integrity: sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==}
+    engines: {iojs: '>= 1.0.0', node: '>= 0.12.0'}
+
+  collect-v8-coverage@1.0.2:
+    resolution: {integrity: sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q==}
+
+  color-convert@1.9.3:
+    resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==}
+
+  color-convert@2.0.1:
+    resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==}
+    engines: {node: '>=7.0.0'}
+
+  color-name@1.1.3:
+    resolution: {integrity: sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==}
+
+  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'}
+
+  commander@2.20.3:
+    resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==}
+
+  compare-versions@3.6.0:
+    resolution: {integrity: sha512-W6Af2Iw1z4CB7q4uU4hv646dW9GQuBM+YpC0UvUCWSD8w90SJjp+ujJuXaEMtAXBtSqGfMPuFOVn4/+FlaqfBA==}
+
+  computeds@0.0.1:
+    resolution: {integrity: sha512-7CEBgcMjVmitjYo5q8JTJVra6X5mQ20uTThdK+0kR7UEaDrAWEQcRiBtWJzga4eRpP6afNwwLsX2SET2JhVB1Q==}
+
+  concat-map@0.0.1:
+    resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==}
+
+  confbox@0.1.7:
+    resolution: {integrity: sha512-uJcB/FKZtBMCJpK8MQji6bJHgu1tixKPxRLeGkNzBoOZzpnZUJm0jm2/sBDWcuBx1dYgxV4JU+g5hmNxCyAmdA==}
+
+  content-disposition@0.5.4:
+    resolution: {integrity: sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==}
+    engines: {node: '>= 0.6'}
+
+  content-type@1.0.5:
+    resolution: {integrity: sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==}
+    engines: {node: '>= 0.6'}
+
+  convert-source-map@1.9.0:
+    resolution: {integrity: sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==}
+
+  convert-source-map@2.0.0:
+    resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==}
+
+  cookie-signature@1.0.6:
+    resolution: {integrity: sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==}
+
+  cookie@0.6.0:
+    resolution: {integrity: sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==}
+    engines: {node: '>= 0.6'}
+
+  core-js-compat@3.37.1:
+    resolution: {integrity: sha512-9TNiImhKvQqSUkOvk/mMRZzOANTiEVC7WaBNhHcKM7x+/5E1l5NvsysR19zuDQScE8k+kfQXWRN3AtS/eOSHpg==}
+
+  core-js@3.37.1:
+    resolution: {integrity: sha512-Xn6qmxrQZyB0FFY8E3bgRXei3lWDJHhvI+u0q9TKIYM49G8pAr0FgnnrFRAmsbptZL1yxRADVXn+x5AGsbBfyw==}
+
+  cross-env@7.0.3:
+    resolution: {integrity: sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw==}
+    engines: {node: '>=10.14', npm: '>=6', yarn: '>=1'}
+    hasBin: true
+
+  cross-spawn@7.0.3:
+    resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==}
+    engines: {node: '>= 8'}
+
+  css-font-size-keywords@1.0.0:
+    resolution: {integrity: sha512-Q+svMDbMlelgCfH/RVDKtTDaf5021O486ZThQPIpahnIjUkMUslC+WuOQSWTgGSrNCH08Y7tYNEmmy0hkfMI8Q==}
+
+  css-font-stretch-keywords@1.0.1:
+    resolution: {integrity: sha512-KmugPO2BNqoyp9zmBIUGwt58UQSfyk1X5DbOlkb2pckDXFSAfjsD5wenb88fNrD6fvS+vu90a/tsPpb9vb0SLg==}
+
+  css-font-style-keywords@1.0.1:
+    resolution: {integrity: sha512-0Fn0aTpcDktnR1RzaBYorIxQily85M2KXRpzmxQPgh8pxUN9Fcn00I8u9I3grNr1QXVgCl9T5Imx0ZwKU973Vg==}
+
+  css-font-weight-keywords@1.0.0:
+    resolution: {integrity: sha512-5So8/NH+oDD+EzsnF4iaG4ZFHQ3vaViePkL1ZbZ5iC/KrsCY+WHq/lvOgrtmuOQ9pBBZ1ADGpaf+A4lj1Z9eYA==}
+
+  css-list-helpers@2.0.0:
+    resolution: {integrity: sha512-9Bj8tZ0jWbAM3u/U6m/boAzAwLPwtjzFvwivr2piSvyVa3K3rChJzQy4RIHkNkKiZCHrEMWDJWtTR8UyVhdDnQ==}
+
+  css-system-font-keywords@1.0.0:
+    resolution: {integrity: sha512-1umTtVd/fXS25ftfjB71eASCrYhilmEsvDEI6wG/QplnmlfmVM5HkZ/ZX46DT5K3eblFPgLUHt5BRCb0YXkSFA==}
+
+  cssesc@3.0.0:
+    resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==}
+    engines: {node: '>=4'}
+    hasBin: true
+
+  cssom@0.3.8:
+    resolution: {integrity: sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==}
+
+  cssom@0.4.4:
+    resolution: {integrity: sha512-p3pvU7r1MyyqbTk+WbNJIgJjG2VmTIaB10rI93LzVPrmDJKkzKYMtxxyAvQXR/NS6otuzveI7+7BBq3SjBS2mw==}
+
+  cssstyle@2.3.0:
+    resolution: {integrity: sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A==}
+    engines: {node: '>=8'}
+
+  csstype@3.1.3:
+    resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==}
+
+  data-urls@2.0.0:
+    resolution: {integrity: sha512-X5eWTSXO/BJmpdIKCRuKUgSCgAN0OwliVK3yPKbwIWU1Tdw5BRajxlzMidvh+gwko9AfQ9zIj52pzF91Q3YAvQ==}
+    engines: {node: '>=10'}
+
+  data-view-buffer@1.0.1:
+    resolution: {integrity: sha512-0lht7OugA5x3iJLOWFhWK/5ehONdprk0ISXqVFn/NFrDu+cuc8iADFrGQz5BnRK7LLU3JmkbXSxaqX+/mXYtUA==}
+    engines: {node: '>= 0.4'}
+
+  data-view-byte-length@1.0.1:
+    resolution: {integrity: sha512-4J7wRJD3ABAzr8wP+OcIcqq2dlUKp4DVflx++hs5h5ZKydWMI6/D/fAot+yh6g2tHh8fLFTvNOaVN357NvSrOQ==}
+    engines: {node: '>= 0.4'}
+
+  data-view-byte-offset@1.0.0:
+    resolution: {integrity: sha512-t/Ygsytq+R995EJ5PZlD4Cu56sWa8InXySaViRzw9apusqsOO2bQP+SbYzAhR0pFKoB+43lYy8rWban9JSuXnA==}
+    engines: {node: '>= 0.4'}
+
+  date-fns@3.6.0:
+    resolution: {integrity: sha512-fRHTG8g/Gif+kSh50gaGEdToemgfj74aRX3swtiouboip5JDLAyDE9F11nHMIcvOaXeOC6D7SpNhi7uFyB7Uww==}
+
+  de-indent@1.0.2:
+    resolution: {integrity: sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg==}
+
+  debug@2.6.9:
+    resolution: {integrity: sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==}
+    peerDependencies:
+      supports-color: '*'
+    peerDependenciesMeta:
+      supports-color:
+        optional: true
+
+  debug@3.1.0:
+    resolution: {integrity: sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==}
+    peerDependencies:
+      supports-color: '*'
+    peerDependenciesMeta:
+      supports-color:
+        optional: true
+
+  debug@3.2.7:
+    resolution: {integrity: sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==}
+    peerDependencies:
+      supports-color: '*'
+    peerDependenciesMeta:
+      supports-color:
+        optional: true
+
+  debug@4.3.5:
+    resolution: {integrity: sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==}
+    engines: {node: '>=6.0'}
+    peerDependencies:
+      supports-color: '*'
+    peerDependenciesMeta:
+      supports-color:
+        optional: true
+
+  decimal.js@10.4.3:
+    resolution: {integrity: sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==}
+
+  dedent@0.7.0:
+    resolution: {integrity: sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA==}
+
+  deep-is@0.1.4:
+    resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==}
+
+  deepmerge@4.3.1:
+    resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==}
+    engines: {node: '>=0.10.0'}
+
+  default-gateway@6.0.3:
+    resolution: {integrity: sha512-fwSOJsbbNzZ/CUFpqFBqYfYNLj1NbMPm8MMCIzHjC83iSJRBEGmDUxU+WP661BaBQImeC2yHwXtz+P/O9o+XEg==}
+    engines: {node: '>= 10'}
+
+  define-data-property@1.1.4:
+    resolution: {integrity: sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==}
+    engines: {node: '>= 0.4'}
+
+  define-properties@1.2.1:
+    resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==}
+    engines: {node: '>= 0.4'}
+
+  delayed-stream@1.0.0:
+    resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==}
+    engines: {node: '>=0.4.0'}
+
+  depd@2.0.0:
+    resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==}
+    engines: {node: '>= 0.8'}
+
+  destroy@1.2.0:
+    resolution: {integrity: sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==}
+    engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16}
+
+  detect-newline@3.1.0:
+    resolution: {integrity: sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==}
+    engines: {node: '>=8'}
+
+  diff-sequences@27.5.1:
+    resolution: {integrity: sha512-k1gCAXAsNgLwEL+Y8Wvl+M6oEFj5bgazfZULpS5CneoPPXRaCCW7dm+q21Ky2VEE5X+VeRDBVg1Pcvvsr4TtNQ==}
+    engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
+
+  dir-glob@3.0.1:
+    resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==}
+    engines: {node: '>=8'}
+
+  doctrine@2.1.0:
+    resolution: {integrity: sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==}
+    engines: {node: '>=0.10.0'}
+
+  dom-walk@0.1.2:
+    resolution: {integrity: sha512-6QvTW9mrGeIegrFXdtQi9pk7O/nSK6lSdXW2eqUspN5LWD7UTji2Fqw5V2YLjBpHEoU9Xl/eUWNpDeZvoyOv2w==}
+
+  domelementtype@1.3.1:
+    resolution: {integrity: sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==}
+
+  domexception@2.0.1:
+    resolution: {integrity: sha512-yxJ2mFy/sibVQlu5qHjOkf9J3K6zgmCxgJ94u2EdvDOV09H+32LtRswEcUsmUWN72pVLOEnTSRaIVVzVQgS0dg==}
+    engines: {node: '>=8'}
+    deprecated: Use your platform's native DOMException instead
+
+  domhandler@2.4.2:
+    resolution: {integrity: sha512-JiK04h0Ht5u/80fdLMCEmV4zkNh2BcoMFBmZ/91WtYZ8qVXSKjiw7fXMgFPnHcSZgOo3XdinHvmnDUeMf5R4wA==}
+
+  echarts@5.5.0:
+    resolution: {integrity: sha512-rNYnNCzqDAPCr4m/fqyUFv7fD9qIsd50S6GDFgO1DxZhncCsNsG7IfUlAlvZe5oSEQxtsjnHiUuppzccry93Xw==}
+
+  ee-first@1.1.1:
+    resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==}
+
+  electron-to-chromium@1.4.812:
+    resolution: {integrity: sha512-7L8fC2Ey/b6SePDFKR2zHAy4mbdp1/38Yk5TsARO66W3hC5KEaeKMMHoxwtuH+jcu2AYLSn9QX04i95t6Fl1Hg==}
+
+  emittery@0.8.1:
+    resolution: {integrity: sha512-uDfvUjVrfGJJhymx/kz6prltenw1u7WrCg1oa94zYY8xxVpLLUu045LAT0dhDZdXG58/EpPL/5kA180fQ/qudg==}
+    engines: {node: '>=10'}
+
+  emoji-regex@8.0.0:
+    resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==}
+
+  encodeurl@1.0.2:
+    resolution: {integrity: sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==}
+    engines: {node: '>= 0.8'}
+
+  entities@1.1.2:
+    resolution: {integrity: sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==}
+
+  entities@4.5.0:
+    resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==}
+    engines: {node: '>=0.12'}
+
+  error-ex@1.3.2:
+    resolution: {integrity: sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==}
+
+  es-abstract@1.23.3:
+    resolution: {integrity: sha512-e+HfNH61Bj1X9/jLc5v1owaLYuHdeHHSQlkhCBiTK8rBvKaULl/beGMxwrMXjpYrv4pz22BlY570vVePA2ho4A==}
+    engines: {node: '>= 0.4'}
+
+  es-define-property@1.0.0:
+    resolution: {integrity: sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==}
+    engines: {node: '>= 0.4'}
+
+  es-errors@1.3.0:
+    resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==}
+    engines: {node: '>= 0.4'}
+
+  es-module-lexer@1.5.4:
+    resolution: {integrity: sha512-MVNK56NiMrOwitFB7cqDwq0CQutbw+0BvLshJSse0MUNU+y1FC3bUS/AQg7oUng+/wKrrki7JfmwtVHkVfPLlw==}
+
+  es-object-atoms@1.0.0:
+    resolution: {integrity: sha512-MZ4iQ6JwHOBQjahnjwaC1ZtIBH+2ohjamzAO3oaHcXYup7qxjF2fixyH+Q71voWHeOkI2q/TnJao/KfXYIZWbw==}
+    engines: {node: '>= 0.4'}
+
+  es-set-tostringtag@2.0.3:
+    resolution: {integrity: sha512-3T8uNMC3OQTHkFUsFq8r/BwAXLHvU/9O9mE0fBc/MY5iq/8H7ncvO947LmYA6ldWw9Uh8Yhf25zu6n7nML5QWQ==}
+    engines: {node: '>= 0.4'}
+
+  es-shim-unscopables@1.0.2:
+    resolution: {integrity: sha512-J3yBRXCzDu4ULnQwxyToo/OjdMx6akgVC7K6few0a7F/0wLtmKKN7I73AH5T2836UuXRqN7Qg+IIUw/+YJksRw==}
+
+  es-to-primitive@1.2.1:
+    resolution: {integrity: sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==}
+    engines: {node: '>= 0.4'}
+
+  esbuild@0.17.19:
+    resolution: {integrity: sha512-XQ0jAPFkK/u3LcVRcvVHQcTIqD6E2H1fvZMA5dQPSOWb3suUbWbfbRf94pjc0bNzRYLfIrDRQXr7X+LHIm5oHw==}
+    engines: {node: '>=12'}
+    hasBin: true
+
+  escalade@3.1.2:
+    resolution: {integrity: sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==}
+    engines: {node: '>=6'}
+
+  escape-html@1.0.3:
+    resolution: {integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==}
+
+  escape-string-regexp@1.0.5:
+    resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==}
+    engines: {node: '>=0.8.0'}
+
+  escape-string-regexp@2.0.0:
+    resolution: {integrity: sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==}
+    engines: {node: '>=8'}
+
+  escape-string-regexp@4.0.0:
+    resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==}
+    engines: {node: '>=10'}
+
+  escape-string-regexp@5.0.0:
+    resolution: {integrity: sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==}
+    engines: {node: '>=12'}
+
+  escodegen@2.1.0:
+    resolution: {integrity: sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==}
+    engines: {node: '>=6.0'}
+    hasBin: true
+
+  eslint-config-prettier@9.1.0:
+    resolution: {integrity: sha512-NSWl5BFQWEPi1j4TjVNItzYV7dZXZ+wP6I6ZhrBGpChQhZRUaElihE9uRRkcbRnNb76UMKDF3r+WTmNcGPKsqw==}
+    hasBin: true
+    peerDependencies:
+      eslint: '>=7.0.0'
+
+  eslint-import-resolver-node@0.3.9:
+    resolution: {integrity: sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==}
+
+  eslint-module-utils@2.8.1:
+    resolution: {integrity: sha512-rXDXR3h7cs7dy9RNpUlQf80nX31XWJEyGq1tRMo+6GsO5VmTe4UTwtmonAD4ZkAsrfMVDA2wlGJ3790Ys+D49Q==}
+    engines: {node: '>=4'}
+    peerDependencies:
+      '@typescript-eslint/parser': '*'
+      eslint: '*'
+      eslint-import-resolver-node: '*'
+      eslint-import-resolver-typescript: '*'
+      eslint-import-resolver-webpack: '*'
+    peerDependenciesMeta:
+      '@typescript-eslint/parser':
+        optional: true
+      eslint:
+        optional: true
+      eslint-import-resolver-node:
+        optional: true
+      eslint-import-resolver-typescript:
+        optional: true
+      eslint-import-resolver-webpack:
+        optional: true
+
+  eslint-plugin-import@2.29.1:
+    resolution: {integrity: sha512-BbPC0cuExzhiMo4Ff1BTVwHpjjv28C5R+btTOGaCRC7UEz801up0JadwkeSk5Ued6TG34uaczuVuH6qyy5YUxw==}
+    engines: {node: '>=4'}
+    peerDependencies:
+      '@typescript-eslint/parser': '*'
+      eslint: ^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8
+    peerDependenciesMeta:
+      '@typescript-eslint/parser':
+        optional: true
+
+  eslint-plugin-prettier@5.1.3:
+    resolution: {integrity: sha512-C9GCVAs4Eq7ZC/XFQHITLiHJxQngdtraXaM+LoUFoFp/lHNl2Zn8f3WQbe9HvTBBQ9YnKFB0/2Ajdqwo5D1EAw==}
+    engines: {node: ^14.18.0 || >=16.0.0}
+    peerDependencies:
+      '@types/eslint': '>=8.0.0'
+      eslint: '>=8.0.0'
+      eslint-config-prettier: '*'
+      prettier: '>=3.0.0'
+    peerDependenciesMeta:
+      '@types/eslint':
+        optional: true
+      eslint-config-prettier:
+        optional: true
+
+  eslint-plugin-vue@9.26.0:
+    resolution: {integrity: sha512-eTvlxXgd4ijE1cdur850G6KalZqk65k1JKoOI2d1kT3hr8sPD07j1q98FRFdNnpxBELGPWxZmInxeHGF/GxtqQ==}
+    engines: {node: ^14.17.0 || >=16.0.0}
+    peerDependencies:
+      eslint: ^6.2.0 || ^7.0.0 || ^8.0.0 || ^9.0.0
+
+  eslint-scope@7.2.2:
+    resolution: {integrity: sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==}
+    engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
+
+  eslint-scope@8.0.1:
+    resolution: {integrity: sha512-pL8XjgP4ZOmmwfFE8mEhSxA7ZY4C+LWyqjQ3o4yWkkmD0qcMT9kkW3zWHOczhWcjTSgqycYAgwSlXvZltv65og==}
+    engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+  eslint-visitor-keys@3.4.3:
+    resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==}
+    engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
+
+  eslint-visitor-keys@4.0.0:
+    resolution: {integrity: sha512-OtIRv/2GyiF6o/d8K7MYKKbXrOUBIK6SfkIRM4Z0dY3w+LiQ0vy3F57m0Z71bjbyeiWFiHJ8brqnmE6H6/jEuw==}
+    engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+  eslint@9.5.0:
+    resolution: {integrity: sha512-+NAOZFrW/jFTS3dASCGBxX1pkFD0/fsO+hfAkJ4TyYKwgsXZbqzrw+seCYFCcPCYXvnD67tAnglU7GQTz6kcVw==}
+    engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+    hasBin: true
+
+  espree@10.1.0:
+    resolution: {integrity: sha512-M1M6CpiE6ffoigIOWYO9UDP8TMUw9kqb21tf+08IgDYjCsOvCuDt4jQcZmoYxx+w7zlKw9/N0KXfto+I8/FrXA==}
+    engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+  espree@9.6.1:
+    resolution: {integrity: sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==}
+    engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
+
+  esprima@4.0.1:
+    resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==}
+    engines: {node: '>=4'}
+    hasBin: true
+
+  esquery@1.5.0:
+    resolution: {integrity: sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==}
+    engines: {node: '>=0.10'}
+
+  esrecurse@4.3.0:
+    resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==}
+    engines: {node: '>=4.0'}
+
+  estraverse@5.3.0:
+    resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==}
+    engines: {node: '>=4.0'}
+
+  estree-walker@2.0.2:
+    resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==}
+
+  estree-walker@3.0.3:
+    resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==}
+
+  esutils@2.0.3:
+    resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==}
+    engines: {node: '>=0.10.0'}
+
+  etag@1.8.1:
+    resolution: {integrity: sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==}
+    engines: {node: '>= 0.6'}
+
+  events@3.3.0:
+    resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==}
+    engines: {node: '>=0.8.x'}
+
+  execa@5.1.1:
+    resolution: {integrity: sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==}
+    engines: {node: '>=10'}
+
+  exif-parser@0.1.12:
+    resolution: {integrity: sha512-c2bQfLNbMzLPmzQuOr8fy0csy84WmwnER81W88DzTp9CYNPJ6yzOj2EZAh9pywYpqHnshVLHQJ8WzldAyfY+Iw==}
+
+  exit@0.1.2:
+    resolution: {integrity: sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==}
+    engines: {node: '>= 0.8.0'}
+
+  expect@27.5.1:
+    resolution: {integrity: sha512-E1q5hSUG2AmYQwQJ041nvgpkODHQvB+RKlB4IYdru6uJsyFTRyZAP463M+1lINorwbqAmUggi6+WwkD8lCS/Dw==}
+    engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
+
+  express@4.19.2:
+    resolution: {integrity: sha512-5T6nhjsT+EOMzuck8JjBHARTHfMht0POzlA60WV2pMD3gyXw2LZnZ+ueGdNxG+0calOJcWKbpFcuzLZ91YWq9Q==}
+    engines: {node: '>= 0.10.0'}
+
+  external-editor@3.1.0:
+    resolution: {integrity: sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==}
+    engines: {node: '>=4'}
+
+  fast-deep-equal@3.1.3:
+    resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==}
+
+  fast-diff@1.3.0:
+    resolution: {integrity: sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==}
+
+  fast-glob@3.3.2:
+    resolution: {integrity: sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==}
+    engines: {node: '>=8.6.0'}
+
+  fast-json-stable-stringify@2.1.0:
+    resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==}
+
+  fast-levenshtein@2.0.6:
+    resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==}
+
+  fastq@1.17.1:
+    resolution: {integrity: sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==}
+
+  fb-watchman@2.0.2:
+    resolution: {integrity: sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==}
+
+  figures@3.2.0:
+    resolution: {integrity: sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==}
+    engines: {node: '>=8'}
+
+  file-entry-cache@8.0.0:
+    resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==}
+    engines: {node: '>=16.0.0'}
+
+  file-type@9.0.0:
+    resolution: {integrity: sha512-Qe/5NJrgIOlwijpq3B7BEpzPFcgzggOTagZmkXQY4LA6bsXKTUstK7Wp12lEJ/mLKTpvIZxmIuRcLYWT6ov9lw==}
+    engines: {node: '>=6'}
+
+  fill-range@7.1.1:
+    resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==}
+    engines: {node: '>=8'}
+
+  finalhandler@1.2.0:
+    resolution: {integrity: sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==}
+    engines: {node: '>= 0.8'}
+
+  find-up@4.1.0:
+    resolution: {integrity: sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==}
+    engines: {node: '>=8'}
+
+  find-up@5.0.0:
+    resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==}
+    engines: {node: '>=10'}
+
+  flat-cache@4.0.1:
+    resolution: {integrity: sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==}
+    engines: {node: '>=16'}
+
+  flatted@3.3.1:
+    resolution: {integrity: sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==}
+
+  follow-redirects@1.15.6:
+    resolution: {integrity: sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==}
+    engines: {node: '>=4.0'}
+    peerDependencies:
+      debug: '*'
+    peerDependenciesMeta:
+      debug:
+        optional: true
+
+  follow-redirects@1.5.10:
+    resolution: {integrity: sha512-0V5l4Cizzvqt5D44aTXbFZz+FtyXV1vrDN6qrelxtfYQKW0KO0W2T/hkE8xvGa/540LkZlkaUjO4ailYTFtHVQ==}
+    engines: {node: '>=4.0'}
+
+  for-each@0.3.3:
+    resolution: {integrity: sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==}
+
+  form-data@3.0.1:
+    resolution: {integrity: sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==}
+    engines: {node: '>= 6'}
+
+  forwarded@0.2.0:
+    resolution: {integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==}
+    engines: {node: '>= 0.6'}
+
+  fraction.js@4.3.7:
+    resolution: {integrity: sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==}
+
+  fresh@0.5.2:
+    resolution: {integrity: sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==}
+    engines: {node: '>= 0.6'}
+
+  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]
+
+  function-bind@1.1.2:
+    resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==}
+
+  function.prototype.name@1.1.6:
+    resolution: {integrity: sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg==}
+    engines: {node: '>= 0.4'}
+
+  functions-have-names@1.2.3:
+    resolution: {integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==}
+
+  generic-names@4.0.0:
+    resolution: {integrity: sha512-ySFolZQfw9FoDb3ed9d80Cm9f0+r7qj+HJkWjeD9RBfpxEVTlVhol+gvaQB/78WbwYfbnNh8nWHHBSlg072y6A==}
+
+  gensync@1.0.0-beta.2:
+    resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==}
+    engines: {node: '>=6.9.0'}
+
+  get-caller-file@2.0.5:
+    resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==}
+    engines: {node: 6.* || 8.* || >= 10.*}
+
+  get-intrinsic@1.2.4:
+    resolution: {integrity: sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==}
+    engines: {node: '>= 0.4'}
+
+  get-package-type@0.1.0:
+    resolution: {integrity: sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==}
+    engines: {node: '>=8.0.0'}
+
+  get-stream@6.0.1:
+    resolution: {integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==}
+    engines: {node: '>=10'}
+
+  get-symbol-description@1.0.2:
+    resolution: {integrity: sha512-g0QYk1dZBxGwk+Ngc+ltRH2IBp2f7zBkBMBJZCDerh6EhlhSR6+9irMCuT/09zD6qkarHUSn529sK/yL4S27mg==}
+    engines: {node: '>= 0.4'}
+
+  glob-parent@5.1.2:
+    resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==}
+    engines: {node: '>= 6'}
+
+  glob-parent@6.0.2:
+    resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==}
+    engines: {node: '>=10.13.0'}
+
+  glob@7.2.3:
+    resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==}
+    deprecated: Glob versions prior to v9 are no longer supported
+
+  global@4.4.0:
+    resolution: {integrity: sha512-wv/LAoHdRE3BeTGz53FAamhGlPLhlssK45usmGFThIi4XqnBmjKQ16u+RNbP7WvigRZDxUsM0J3gcQ5yicaL0w==}
+
+  globals@11.12.0:
+    resolution: {integrity: sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==}
+    engines: {node: '>=4'}
+
+  globals@13.24.0:
+    resolution: {integrity: sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==}
+    engines: {node: '>=8'}
+
+  globals@14.0.0:
+    resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==}
+    engines: {node: '>=18'}
+
+  globalthis@1.0.4:
+    resolution: {integrity: sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==}
+    engines: {node: '>= 0.4'}
+
+  globby@11.1.0:
+    resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==}
+    engines: {node: '>=10'}
+
+  gopd@1.0.1:
+    resolution: {integrity: sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==}
+
+  graceful-fs@4.2.11:
+    resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==}
+
+  graphemer@1.4.0:
+    resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==}
+
+  has-bigints@1.0.2:
+    resolution: {integrity: sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==}
+
+  has-flag@3.0.0:
+    resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==}
+    engines: {node: '>=4'}
+
+  has-flag@4.0.0:
+    resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==}
+    engines: {node: '>=8'}
+
+  has-property-descriptors@1.0.2:
+    resolution: {integrity: sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==}
+
+  has-proto@1.0.3:
+    resolution: {integrity: sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==}
+    engines: {node: '>= 0.4'}
+
+  has-symbols@1.0.3:
+    resolution: {integrity: sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==}
+    engines: {node: '>= 0.4'}
+
+  has-tostringtag@1.0.2:
+    resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==}
+    engines: {node: '>= 0.4'}
+
+  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-encoding-sniffer@2.0.1:
+    resolution: {integrity: sha512-D5JbOMBIR/TVZkubHT+OyT2705QvogUW4IBn6nHd756OwieSF9aDYFj4dv6HHEVGYbHaLETa3WggZYWWMyy3ZQ==}
+    engines: {node: '>=10'}
+
+  html-escaper@2.0.2:
+    resolution: {integrity: sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==}
+
+  html-tags@3.3.1:
+    resolution: {integrity: sha512-ztqyC3kLto0e9WbNp0aeP+M3kTt+nbaIveGmUxAtZa+8iFgKLUOD4YKM5j+f3QD89bra7UeumolZHKuOXnTmeQ==}
+    engines: {node: '>=8'}
+
+  http-errors@2.0.0:
+    resolution: {integrity: sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==}
+    engines: {node: '>= 0.8'}
+
+  http-proxy-agent@4.0.1:
+    resolution: {integrity: sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==}
+    engines: {node: '>= 6'}
+
+  https-proxy-agent@5.0.1:
+    resolution: {integrity: sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==}
+    engines: {node: '>= 6'}
+
+  human-signals@2.1.0:
+    resolution: {integrity: sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==}
+    engines: {node: '>=10.17.0'}
+
+  iconfont-tools-cli@1.3.1:
+    resolution: {integrity: sha512-RVGm5BGWr51KPfUM07CARAdt5jobwTfcodsiQ7wktUoJGYjcKXeVWvjm4zPxKSuhnPlK8XQBTw0UVG5Yy9MKdg==}
+    hasBin: true
+
+  iconfont-tools@1.7.13:
+    resolution: {integrity: sha512-6pJRcKvB6HGwYjk497c7HNZGo9va1mFk6KFIE0lYvQqwuFOpkEoxOLxqzM8+AM5y3x2AD/A/LSzhxZGE6K9Cvg==}
+    hasBin: true
+
+  iconv-lite@0.4.24:
+    resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==}
+    engines: {node: '>=0.10.0'}
+
+  icss-replace-symbols@1.1.0:
+    resolution: {integrity: sha512-chIaY3Vh2mh2Q3RGXttaDIzeiPvaVXJ+C4DAh/w3c37SKZ/U6PGMmuicR2EQQp9bKG8zLMCl7I+PtIoOOPp8Gg==}
+
+  icss-utils@5.1.0:
+    resolution: {integrity: sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==}
+    engines: {node: ^10 || ^12 || >= 14}
+    peerDependencies:
+      postcss: ^8.1.0
+
+  ieee754@1.2.1:
+    resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==}
+
+  ignore@5.3.1:
+    resolution: {integrity: sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==}
+    engines: {node: '>= 4'}
+
+  immutable@4.3.6:
+    resolution: {integrity: sha512-Ju0+lEMyzMVZarkTn/gqRpdqd5dOPaz1mCZ0SH3JV6iFw81PldE/PEB1hWVEA288HPt4WXW8O7AWxB10M+03QQ==}
+
+  import-fresh@3.3.0:
+    resolution: {integrity: sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==}
+    engines: {node: '>=6'}
+
+  import-local@3.1.0:
+    resolution: {integrity: sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==}
+    engines: {node: '>=8'}
+    hasBin: true
+
+  imurmurhash@0.1.4:
+    resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==}
+    engines: {node: '>=0.8.19'}
+
+  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@7.3.3:
+    resolution: {integrity: sha512-JG3eIAj5V9CwcGvuOmoo6LB9kbAYT8HXffUl6memuszlwDC/qvFAJw49XJ5NROSFNPxp3iQg1GqkFhaY/CR0IA==}
+    engines: {node: '>=8.0.0'}
+
+  internal-slot@1.0.7:
+    resolution: {integrity: sha512-NGnrKwXzSms2qUUih/ILZ5JBqNTSa1+ZmP6flaIp6KmSElgE9qdndzS3cqjrDovwFdmwsGsLdeFgB6suw+1e9g==}
+    engines: {node: '>= 0.4'}
+
+  invert-kv@3.0.1:
+    resolution: {integrity: sha512-CYdFeFexxhv/Bcny+Q0BfOV+ltRlJcd4BBZBYFX/O0u4npJrgZtIcjokegtiSMAvlMTJ+Koq0GBCc//3bueQxw==}
+    engines: {node: '>=8'}
+
+  ipaddr.js@1.9.1:
+    resolution: {integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==}
+    engines: {node: '>= 0.10'}
+
+  is-array-buffer@3.0.4:
+    resolution: {integrity: sha512-wcjaerHw0ydZwfhiKbXJWLDY8A7yV7KhjQOpb83hGgGfId/aQa4TOvwyzn2PuswW2gPCYEL/nEAiSVpdOj1lXw==}
+    engines: {node: '>= 0.4'}
+
+  is-arrayish@0.2.1:
+    resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==}
+
+  is-bigint@1.0.4:
+    resolution: {integrity: sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==}
+
+  is-binary-path@2.1.0:
+    resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==}
+    engines: {node: '>=8'}
+
+  is-boolean-object@1.1.2:
+    resolution: {integrity: sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==}
+    engines: {node: '>= 0.4'}
+
+  is-callable@1.2.7:
+    resolution: {integrity: sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==}
+    engines: {node: '>= 0.4'}
+
+  is-core-module@2.14.0:
+    resolution: {integrity: sha512-a5dFJih5ZLYlRtDc0dZWP7RiKr6xIKzmn/oAYCDvdLThadVgyJwlaoQPmRtMSpz+rk0OGAgIu+TcM9HUF0fk1A==}
+    engines: {node: '>= 0.4'}
+
+  is-data-view@1.0.1:
+    resolution: {integrity: sha512-AHkaJrsUVW6wq6JS8y3JnM/GJF/9cf+k20+iDzlSaJrinEo5+7vRiteOSwBhHRiAyQATN1AmY4hwzxJKPmYf+w==}
+    engines: {node: '>= 0.4'}
+
+  is-date-object@1.0.5:
+    resolution: {integrity: sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==}
+    engines: {node: '>= 0.4'}
+
+  is-extglob@2.1.1:
+    resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==}
+    engines: {node: '>=0.10.0'}
+
+  is-fullwidth-code-point@3.0.0:
+    resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==}
+    engines: {node: '>=8'}
+
+  is-function@1.0.2:
+    resolution: {integrity: sha512-lw7DUp0aWXYg+CBCN+JKkcE0Q2RayZnSvnZBlwgxHBQhqt5pZNVy4Ri7H9GmmXkdu7LUthszM+Tor1u/2iBcpQ==}
+
+  is-generator-fn@2.1.0:
+    resolution: {integrity: sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==}
+    engines: {node: '>=6'}
+
+  is-glob@4.0.3:
+    resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==}
+    engines: {node: '>=0.10.0'}
+
+  is-negative-zero@2.0.3:
+    resolution: {integrity: sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==}
+    engines: {node: '>= 0.4'}
+
+  is-number-object@1.0.7:
+    resolution: {integrity: sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==}
+    engines: {node: '>= 0.4'}
+
+  is-number@7.0.0:
+    resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==}
+    engines: {node: '>=0.12.0'}
+
+  is-path-inside@3.0.3:
+    resolution: {integrity: sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==}
+    engines: {node: '>=8'}
+
+  is-potential-custom-element-name@1.0.1:
+    resolution: {integrity: sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==}
+
+  is-regex@1.1.4:
+    resolution: {integrity: sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==}
+    engines: {node: '>= 0.4'}
+
+  is-shared-array-buffer@1.0.3:
+    resolution: {integrity: sha512-nA2hv5XIhLR3uVzDDfCIknerhx8XUKnstuOERPNNIinXG7v9u+ohXF67vxm4TPTEPU6lm61ZkwP3c9PCB97rhg==}
+    engines: {node: '>= 0.4'}
+
+  is-stream@2.0.1:
+    resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==}
+    engines: {node: '>=8'}
+
+  is-string@1.0.7:
+    resolution: {integrity: sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==}
+    engines: {node: '>= 0.4'}
+
+  is-symbol@1.0.4:
+    resolution: {integrity: sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==}
+    engines: {node: '>= 0.4'}
+
+  is-typed-array@1.1.13:
+    resolution: {integrity: sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw==}
+    engines: {node: '>= 0.4'}
+
+  is-typedarray@1.0.0:
+    resolution: {integrity: sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==}
+
+  is-weakref@1.0.2:
+    resolution: {integrity: sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==}
+
+  isarray@2.0.5:
+    resolution: {integrity: sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==}
+
+  isexe@2.0.0:
+    resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==}
+
+  istanbul-lib-coverage@3.2.2:
+    resolution: {integrity: sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==}
+    engines: {node: '>=8'}
+
+  istanbul-lib-instrument@5.2.1:
+    resolution: {integrity: sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==}
+    engines: {node: '>=8'}
+
+  istanbul-lib-report@3.0.1:
+    resolution: {integrity: sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==}
+    engines: {node: '>=10'}
+
+  istanbul-lib-source-maps@4.0.1:
+    resolution: {integrity: sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==}
+    engines: {node: '>=10'}
+
+  istanbul-reports@3.1.7:
+    resolution: {integrity: sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==}
+    engines: {node: '>=8'}
+
+  jest-changed-files@27.5.1:
+    resolution: {integrity: sha512-buBLMiByfWGCoMsLLzGUUSpAmIAGnbR2KJoMN10ziLhOLvP4e0SlypHnAel8iqQXTrcbmfEY9sSqae5sgUsTvw==}
+    engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
+
+  jest-circus@27.5.1:
+    resolution: {integrity: sha512-D95R7x5UtlMA5iBYsOHFFbMD/GVA4R/Kdq15f7xYWUfWHBto9NYRsOvnSauTgdF+ogCpJ4tyKOXhUifxS65gdw==}
+    engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
+
+  jest-cli@27.5.1:
+    resolution: {integrity: sha512-Hc6HOOwYq4/74/c62dEE3r5elx8wjYqxY0r0G/nFrLDPMFRu6RA/u8qINOIkvhxG7mMQ5EJsOGfRpI8L6eFUVw==}
+    engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
+    hasBin: true
+    peerDependencies:
+      node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0
+    peerDependenciesMeta:
+      node-notifier:
+        optional: true
+
+  jest-config@27.5.1:
+    resolution: {integrity: sha512-5sAsjm6tGdsVbW9ahcChPAFCk4IlkQUknH5AvKjuLTSlcO/wCZKyFdn7Rg0EkC+OGgWODEy2hDpWB1PgzH0JNA==}
+    engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
+    peerDependencies:
+      ts-node: '>=9.0.0'
+    peerDependenciesMeta:
+      ts-node:
+        optional: true
+
+  jest-diff@27.5.1:
+    resolution: {integrity: sha512-m0NvkX55LDt9T4mctTEgnZk3fmEg3NRYutvMPWM/0iPnkFj2wIeF45O1718cMSOFO1vINkqmxqD8vE37uTEbqw==}
+    engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
+
+  jest-docblock@27.5.1:
+    resolution: {integrity: sha512-rl7hlABeTsRYxKiUfpHrQrG4e2obOiTQWfMEH3PxPjOtdsfLQO4ReWSZaQ7DETm4xu07rl4q/h4zcKXyU0/OzQ==}
+    engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
+
+  jest-each@27.5.1:
+    resolution: {integrity: sha512-1Ff6p+FbhT/bXQnEouYy00bkNSY7OUpfIcmdl8vZ31A1UUaurOLPA8a8BbJOF2RDUElwJhmeaV7LnagI+5UwNQ==}
+    engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
+
+  jest-environment-jsdom@27.5.1:
+    resolution: {integrity: sha512-TFBvkTC1Hnnnrka/fUb56atfDtJ9VMZ94JkjTbggl1PEpwrYtUBKMezB3inLmWqQsXYLcMwNoDQwoBTAvFfsfw==}
+    engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
+
+  jest-environment-node@27.5.1:
+    resolution: {integrity: sha512-Jt4ZUnxdOsTGwSRAfKEnE6BcwsSPNOijjwifq5sDFSA2kesnXTvNqKHYgM0hDq3549Uf/KzdXNYn4wMZJPlFLw==}
+    engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
+
+  jest-get-type@27.5.1:
+    resolution: {integrity: sha512-2KY95ksYSaK7DMBWQn6dQz3kqAf3BB64y2udeG+hv4KfSOb9qwcYQstTJc1KCbsix+wLZWZYN8t7nwX3GOBLRw==}
+    engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
+
+  jest-haste-map@27.5.1:
+    resolution: {integrity: sha512-7GgkZ4Fw4NFbMSDSpZwXeBiIbx+t/46nJ2QitkOjvwPYyZmqttu2TDSimMHP1EkPOi4xUZAN1doE5Vd25H4Jng==}
+    engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
+
+  jest-jasmine2@27.5.1:
+    resolution: {integrity: sha512-jtq7VVyG8SqAorDpApwiJJImd0V2wv1xzdheGHRGyuT7gZm6gG47QEskOlzsN1PG/6WNaCo5pmwMHDf3AkG2pQ==}
+    engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
+
+  jest-leak-detector@27.5.1:
+    resolution: {integrity: sha512-POXfWAMvfU6WMUXftV4HolnJfnPOGEu10fscNCA76KBpRRhcMN2c8d3iT2pxQS3HLbA+5X4sOUPzYO2NUyIlHQ==}
+    engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
+
+  jest-matcher-utils@27.5.1:
+    resolution: {integrity: sha512-z2uTx/T6LBaCoNWNFWwChLBKYxTMcGBRjAt+2SbP929/Fflb9aa5LGma654Rz8z9HLxsrUaYzxE9T/EFIL/PAw==}
+    engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
+
+  jest-message-util@27.5.1:
+    resolution: {integrity: sha512-rMyFe1+jnyAAf+NHwTclDz0eAaLkVDdKVHHBFWsBWHnnh5YeJMNWWsv7AbFYXfK3oTqvL7VTWkhNLu1jX24D+g==}
+    engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
+
+  jest-mock@27.5.1:
+    resolution: {integrity: sha512-K4jKbY1d4ENhbrG2zuPWaQBvDly+iZ2yAW+T1fATN78hc0sInwn7wZB8XtlNnvHug5RMwV897Xm4LqmPM4e2Og==}
+    engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
+
+  jest-pnp-resolver@1.2.3:
+    resolution: {integrity: sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==}
+    engines: {node: '>=6'}
+    peerDependencies:
+      jest-resolve: '*'
+    peerDependenciesMeta:
+      jest-resolve:
+        optional: true
+
+  jest-regex-util@27.5.1:
+    resolution: {integrity: sha512-4bfKq2zie+x16okqDXjXn9ql2B0dScQu+vcwe4TvFVhkVyuWLqpZrZtXxLLWoXYgn0E87I6r6GRYHF7wFZBUvg==}
+    engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
+
+  jest-resolve-dependencies@27.5.1:
+    resolution: {integrity: sha512-QQOOdY4PE39iawDn5rzbIePNigfe5B9Z91GDD1ae/xNDlu9kaat8QQ5EKnNmVWPV54hUdxCVwwj6YMgR2O7IOg==}
+    engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
+
+  jest-resolve@27.5.1:
+    resolution: {integrity: sha512-FFDy8/9E6CV83IMbDpcjOhumAQPDyETnU2KZ1O98DwTnz8AOBsW/Xv3GySr1mOZdItLR+zDZ7I/UdTFbgSOVCw==}
+    engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
+
+  jest-runner@27.5.1:
+    resolution: {integrity: sha512-g4NPsM4mFCOwFKXO4p/H/kWGdJp9V8kURY2lX8Me2drgXqG7rrZAx5kv+5H7wtt/cdFIjhqYx1HrlqWHaOvDaQ==}
+    engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
+
+  jest-runtime@27.5.1:
+    resolution: {integrity: sha512-o7gxw3Gf+H2IGt8fv0RiyE1+r83FJBRruoA+FXrlHw6xEyBsU8ugA6IPfTdVyA0w8HClpbK+DGJxH59UrNMx8A==}
+    engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
+
+  jest-serializer@27.5.1:
+    resolution: {integrity: sha512-jZCyo6iIxO1aqUxpuBlwTDMkzOAJS4a3eYz3YzgxxVQFwLeSA7Jfq5cbqCY+JLvTDrWirgusI/0KwxKMgrdf7w==}
+    engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
+
+  jest-snapshot@27.5.1:
+    resolution: {integrity: sha512-yYykXI5a0I31xX67mgeLw1DZ0bJB+gpq5IpSuCAoyDi0+BhgU/RIrL+RTzDmkNTchvDFWKP8lp+w/42Z3us5sA==}
+    engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
+
+  jest-util@27.5.1:
+    resolution: {integrity: sha512-Kv2o/8jNvX1MQ0KGtw480E/w4fBCDOnH6+6DmeKi6LZUIlKA5kwY0YNdlzaWTiVgxqAqik11QyxDOKk543aKXw==}
+    engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
+
+  jest-validate@27.5.1:
+    resolution: {integrity: sha512-thkNli0LYTmOI1tDB3FI1S1RTp/Bqyd9pTarJwL87OIBFuqEb5Apv5EaApEudYg4g86e3CT6kM0RowkhtEnCBQ==}
+    engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
+
+  jest-watcher@27.5.1:
+    resolution: {integrity: sha512-z676SuD6Z8o8qbmEGhoEUFOM1+jfEiL3DXHK/xgEiG2EyNYfFG60jluWcupY6dATjfEsKQuibReS1djInQnoVw==}
+    engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
+
+  jest-worker@27.5.1:
+    resolution: {integrity: sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==}
+    engines: {node: '>= 10.13.0'}
+
+  jest@27.0.4:
+    resolution: {integrity: sha512-Px1iKFooXgGSkk1H8dJxxBIrM3tsc5SIuI4kfKYK2J+4rvCvPGr/cXktxh0e9zIPQ5g09kOMNfHQEmusBUf/ZA==}
+    engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
+    hasBin: true
+    peerDependencies:
+      node-notifier: ^8.0.1 || ^9.0.0
+    peerDependenciesMeta:
+      node-notifier:
+        optional: true
+
+  jimp@0.10.3:
+    resolution: {integrity: sha512-meVWmDMtyUG5uYjFkmzu0zBgnCvvxwWNi27c4cg55vWNVC9ES4Lcwb+ogx+uBBQE3Q+dLKjXaLl0JVW+nUNwbQ==}
+
+  jpeg-js@0.3.7:
+    resolution: {integrity: sha512-9IXdWudL61npZjvLuVe/ktHiA41iE8qFyLB+4VDTblEsWBzeg8WQTlktdUK4CdncUqtUgUg0bbOmTE2bKBKaBQ==}
+
+  js-tokens@4.0.0:
+    resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==}
+
+  js-tokens@9.0.0:
+    resolution: {integrity: sha512-WriZw1luRMlmV3LGJaR6QOJjWwgLUTf89OwT2lUOyjX2dJGBwgmIkbcz+7WFZjrZM635JOIR517++e/67CP9dQ==}
+
+  js-yaml@3.14.1:
+    resolution: {integrity: sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==}
+    hasBin: true
+
+  js-yaml@4.1.0:
+    resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==}
+    hasBin: true
+
+  jsdom@16.7.0:
+    resolution: {integrity: sha512-u9Smc2G1USStM+s/x1ru5Sxrl6mPYCbByG1U/hUmqaVsm4tbNyS7CicOSRyuGQYZhTu0h84qkZZQ/I+dzizSVw==}
+    engines: {node: '>=10'}
+    peerDependencies:
+      canvas: ^2.5.0
+    peerDependenciesMeta:
+      canvas:
+        optional: true
+
+  jsesc@0.5.0:
+    resolution: {integrity: sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA==}
+    hasBin: true
+
+  jsesc@2.5.2:
+    resolution: {integrity: sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==}
+    engines: {node: '>=4'}
+    hasBin: true
+
+  json-buffer@3.0.1:
+    resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==}
+
+  json-parse-even-better-errors@2.3.1:
+    resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==}
+
+  json-schema-traverse@0.4.1:
+    resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==}
+
+  json-stable-stringify-without-jsonify@1.0.1:
+    resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==}
+
+  json5@1.0.2:
+    resolution: {integrity: sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==}
+    hasBin: true
+
+  json5@2.2.3:
+    resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==}
+    engines: {node: '>=6'}
+    hasBin: true
+
+  jsonc-parser@3.3.1:
+    resolution: {integrity: sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==}
+
+  jsonfile@6.1.0:
+    resolution: {integrity: sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==}
+
+  keyv@4.5.4:
+    resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==}
+
+  kleur@3.0.3:
+    resolution: {integrity: sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==}
+    engines: {node: '>=6'}
+
+  lcid@3.1.1:
+    resolution: {integrity: sha512-M6T051+5QCGLBQb8id3hdvIW8+zeFV2FyBGFS9IEK5H9Wt4MueD4bW1eWikpHgZp+5xR3l5c8pZUkQsIA0BFZg==}
+    engines: {node: '>=8'}
+
+  leven@3.1.0:
+    resolution: {integrity: sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==}
+    engines: {node: '>=6'}
+
+  levn@0.4.1:
+    resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==}
+    engines: {node: '>= 0.8.0'}
+
+  licia@1.41.0:
+    resolution: {integrity: sha512-ByZNnAcROO2FJyL2UU0uERC/boE14UCPNYixs7B5PI8kcPOPCgsI0iLjkfhQgiI5M5fNp6TOvbr87EPBR7lw7Q==}
+
+  lilconfig@2.1.0:
+    resolution: {integrity: sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==}
+    engines: {node: '>=10'}
+
+  lines-and-columns@1.2.4:
+    resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==}
+
+  lines-and-columns@2.0.4:
+    resolution: {integrity: sha512-wM1+Z03eypVAVUCE7QdSqpVIvelbOakn1M0bPDoA4SGWPx3sNDVUiMo3L6To6WWGClB7VyXnhQ4Sn7gxiJbE6A==}
+    engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
+
+  load-bmfont@1.4.1:
+    resolution: {integrity: sha512-8UyQoYmdRDy81Brz6aLAUhfZLwr5zV0L3taTQ4hju7m6biuwiWiJXjPhBJxbUQJA8PrkvJ/7Enqmwk2sM14soA==}
+
+  loader-utils@3.3.1:
+    resolution: {integrity: sha512-FMJTLMXfCLMLfJxcX9PFqX5qD88Z5MRGaZCVzfuqeZSPsyiBzs+pahDQjbIWz2QIzPZz0NX9Zy4FX3lmK6YHIg==}
+    engines: {node: '>= 12.13.0'}
+
+  local-pkg@0.5.0:
+    resolution: {integrity: sha512-ok6z3qlYyCDS4ZEU27HaU6x/xZa9Whf8jD4ptH5UZTQYZVYeb9bnZ3ojVhiJNLiXK1Hfc0GNbLXcmZ5plLDDBg==}
+    engines: {node: '>=14'}
+
+  localstorage-polyfill@1.0.1:
+    resolution: {integrity: sha512-m4iHVZxFH5734oQcPKU08025gIz2+4bjWR9lulP8ZYxEJR0BpA0w32oJmkzh8y3UI9ci7xCBehQDc3oA1X+VHw==}
+    engines: {node: '>=6'}
+
+  locate-path@5.0.0:
+    resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==}
+    engines: {node: '>=8'}
+
+  locate-path@6.0.0:
+    resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==}
+    engines: {node: '>=10'}
+
+  lodash.camelcase@4.3.0:
+    resolution: {integrity: sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==}
+
+  lodash.debounce@4.0.8:
+    resolution: {integrity: sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==}
+
+  lodash.merge@4.6.2:
+    resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==}
+
+  lodash@4.17.21:
+    resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==}
+
+  lru-cache@5.1.1:
+    resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==}
+
+  magic-string@0.30.10:
+    resolution: {integrity: sha512-iIRwTIf0QKV3UAnYK4PU8uiEc4SRh5jX0mwpIwETPpHdhVM4f53RSwS/vXvN1JhGX+Cs7B8qIq3d6AH49O5fAQ==}
+
+  make-dir@4.0.0:
+    resolution: {integrity: sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==}
+    engines: {node: '>=10'}
+
+  makeerror@1.0.12:
+    resolution: {integrity: sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==}
+
+  media-typer@0.3.0:
+    resolution: {integrity: sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==}
+    engines: {node: '>= 0.6'}
+
+  merge-descriptors@1.0.1:
+    resolution: {integrity: sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==}
+
+  merge-stream@2.0.0:
+    resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==}
+
+  merge2@1.4.1:
+    resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==}
+    engines: {node: '>= 8'}
+
+  merge@2.1.1:
+    resolution: {integrity: sha512-jz+Cfrg9GWOZbQAnDQ4hlVnQky+341Yk5ru8bZSe6sIDTCIg8n9i/u7hSQGSVOF3C7lH6mGtqjkiT9G4wFLL0w==}
+
+  methods@1.1.2:
+    resolution: {integrity: sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==}
+    engines: {node: '>= 0.6'}
+
+  micromatch@4.0.7:
+    resolution: {integrity: sha512-LPP/3KorzCwBxfeUuZmaR6bG2kdeHSbe0P2tY3FLRU4vYrjYz5hI4QZwV0njUx3jeuKe67YukQ1LSPZBKDqO/Q==}
+    engines: {node: '>=8.6'}
+
+  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
+
+  mime@3.0.0:
+    resolution: {integrity: sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A==}
+    engines: {node: '>=10.0.0'}
+    hasBin: true
+
+  mimic-fn@2.1.0:
+    resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==}
+    engines: {node: '>=6'}
+
+  min-document@2.19.0:
+    resolution: {integrity: sha512-9Wy1B3m3f66bPPmU5hdA4DR4PB2OfDU/+GS3yAB7IQozE3tqXaVv2zOjgla7MEGSRv95+ILmOuvhLkOK6wJtCQ==}
+
+  mini-html-parser2@0.3.0:
+    resolution: {integrity: sha512-W4x1MCmtlnAH5M9qQ1WbRn+hTvv7bdrJx4VI+6SD0MUZatW/6K7v213Aidx7VDQmSKoRv+iAn5TswJnesOs71Q==}
+
+  minimatch@3.1.2:
+    resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==}
+
+  minimatch@9.0.5:
+    resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==}
+    engines: {node: '>=16 || 14 >=14.17'}
+
+  minimist@1.2.8:
+    resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==}
+
+  mkdirp@0.5.6:
+    resolution: {integrity: sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==}
+    hasBin: true
+
+  mlly@1.7.1:
+    resolution: {integrity: sha512-rrVRZRELyQzrIUAVMHxP97kv+G786pHmOKzuFII8zDYahFBS7qnHh2AlYSl1GAHhaMPCz6/oHjVMcfFYgFYHgA==}
+
+  module-alias@2.2.3:
+    resolution: {integrity: sha512-23g5BFj4zdQL/b6tor7Ji+QY4pEfNH784BMslY9Qb0UnJWRAt+lQGLYmRaM0KDBwIG23ffEBELhZDP2rhi9f/Q==}
+
+  ms@2.0.0:
+    resolution: {integrity: sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==}
+
+  ms@2.1.2:
+    resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==}
+
+  ms@2.1.3:
+    resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==}
+
+  muggle-string@0.3.1:
+    resolution: {integrity: sha512-ckmWDJjphvd/FvZawgygcUeQCxzvohjFO5RxTjj4eq8kw359gFF3E1brjfI+viLMxss5JrHTDRHZvu2/tuy0Qg==}
+
+  mute-stream@0.0.8:
+    resolution: {integrity: sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==}
+
+  nanoid@3.3.7:
+    resolution: {integrity: sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==}
+    engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
+    hasBin: true
+
+  natural-compare@1.4.0:
+    resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==}
+
+  negotiator@0.6.3:
+    resolution: {integrity: sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==}
+    engines: {node: '>= 0.6'}
+
+  neo-async@2.6.2:
+    resolution: {integrity: sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==}
+
+  node-int64@0.4.0:
+    resolution: {integrity: sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==}
+
+  node-releases@2.0.14:
+    resolution: {integrity: sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==}
+
+  normalize-path@3.0.0:
+    resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==}
+    engines: {node: '>=0.10.0'}
+
+  normalize-range@0.1.2:
+    resolution: {integrity: sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==}
+    engines: {node: '>=0.10.0'}
+
+  npm-run-path@4.0.1:
+    resolution: {integrity: sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==}
+    engines: {node: '>=8'}
+
+  nth-check@2.1.1:
+    resolution: {integrity: sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==}
+
+  nwsapi@2.2.10:
+    resolution: {integrity: sha512-QK0sRs7MKv0tKe1+5uZIQk/C8XGza4DAnztJG8iD+TpJIORARrCxczA738awHrZoHeTjSSoHqao2teO0dC/gFQ==}
+
+  object-inspect@1.13.2:
+    resolution: {integrity: sha512-IRZSRuzJiynemAXPYtPe5BoI/RESNYR7TYm50MC5Mqbd3Jmw5y790sErYw3V6SryFJD64b74qQQs9wn5Bg/k3g==}
+    engines: {node: '>= 0.4'}
+
+  object-keys@1.1.1:
+    resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==}
+    engines: {node: '>= 0.4'}
+
+  object.assign@4.1.5:
+    resolution: {integrity: sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ==}
+    engines: {node: '>= 0.4'}
+
+  object.fromentries@2.0.8:
+    resolution: {integrity: sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==}
+    engines: {node: '>= 0.4'}
+
+  object.groupby@1.0.3:
+    resolution: {integrity: sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ==}
+    engines: {node: '>= 0.4'}
+
+  object.values@1.2.0:
+    resolution: {integrity: sha512-yBYjY9QX2hnRmZHAjG/f13MzmBzxzYgQhFrke06TTyKY5zSTEqkOeukBzIdVA3j3ulu8Qa3MbVFShV7T2RmGtQ==}
+    engines: {node: '>= 0.4'}
+
+  omggif@1.0.10:
+    resolution: {integrity: sha512-LMJTtvgc/nugXj0Vcrrs68Mn2D1r0zf630VNtqtpI1FEO7e+O9FP4gqs9AcnBaSEeoHIPm28u6qgPR0oyEpGSw==}
+
+  on-finished@2.4.1:
+    resolution: {integrity: sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==}
+    engines: {node: '>= 0.8'}
+
+  once@1.4.0:
+    resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==}
+
+  onetime@5.1.2:
+    resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==}
+    engines: {node: '>=6'}
+
+  optionator@0.9.4:
+    resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==}
+    engines: {node: '>= 0.8.0'}
+
+  os-locale-s-fix@1.0.8-fix-1:
+    resolution: {integrity: sha512-Sv0OvhPiMutICiwORAUefv02DCPb62IelBmo8ZsSrRHyI3FStqIWZvjqDkvtjU+lcujo7UNir+dCwKSqlEQ/5w==}
+    engines: {node: '>=10', yarn: ^1.22.4}
+
+  os-tmpdir@1.0.2:
+    resolution: {integrity: sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==}
+    engines: {node: '>=0.10.0'}
+
+  p-limit@2.3.0:
+    resolution: {integrity: sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==}
+    engines: {node: '>=6'}
+
+  p-limit@3.1.0:
+    resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==}
+    engines: {node: '>=10'}
+
+  p-locate@4.1.0:
+    resolution: {integrity: sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==}
+    engines: {node: '>=8'}
+
+  p-locate@5.0.0:
+    resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==}
+    engines: {node: '>=10'}
+
+  p-try@2.2.0:
+    resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==}
+    engines: {node: '>=6'}
+
+  pako@1.0.11:
+    resolution: {integrity: sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==}
+
+  parent-module@1.0.1:
+    resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==}
+    engines: {node: '>=6'}
+
+  parse-bmfont-ascii@1.0.6:
+    resolution: {integrity: sha512-U4RrVsUFCleIOBsIGYOMKjn9PavsGOXxbvYGtMOEfnId0SVNsgehXh1DxUdVPLoxd5mvcEtvmKs2Mmf0Mpa1ZA==}
+
+  parse-bmfont-binary@1.0.6:
+    resolution: {integrity: sha512-GxmsRea0wdGdYthjuUeWTMWPqm2+FAd4GI8vCvhgJsFnoGhTrLhXDDupwTo7rXVAgaLIGoVHDZS9p/5XbSqeWA==}
+
+  parse-bmfont-xml@1.1.6:
+    resolution: {integrity: sha512-0cEliVMZEhrFDwMh4SxIyVJpqYoOWDJ9P895tFuS+XuNzI5UBmBk5U5O4KuJdTnZpSBI4LFA2+ZiJaiwfSwlMA==}
+
+  parse-css-font@4.0.0:
+    resolution: {integrity: sha512-lnY7dTUfjRXsSo5G5C639L8RaBBaVSgL+5hacIFKsNHzeCJQ5SFSZv1DZmc7+wZv/22PFGOq2YbaEHLdaCS/mQ==}
+
+  parse-headers@2.0.5:
+    resolution: {integrity: sha512-ft3iAoLOB/MlwbNXgzy43SWGP6sQki2jQvAyBg/zDFAgr9bfNWZIUj42Kw2eJIl8kEi4PbgE6U1Zau/HwI75HA==}
+
+  parse-json@5.2.0:
+    resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==}
+    engines: {node: '>=8'}
+
+  parse5@6.0.1:
+    resolution: {integrity: sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==}
+
+  parseurl@1.3.3:
+    resolution: {integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==}
+    engines: {node: '>= 0.8'}
+
+  path-browserify@1.0.1:
+    resolution: {integrity: sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==}
+
+  path-exists@4.0.0:
+    resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==}
+    engines: {node: '>=8'}
+
+  path-is-absolute@1.0.1:
+    resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==}
+    engines: {node: '>=0.10.0'}
+
+  path-key@3.1.1:
+    resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==}
+    engines: {node: '>=8'}
+
+  path-parse@1.0.7:
+    resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==}
+
+  path-to-regexp@0.1.7:
+    resolution: {integrity: sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==}
+
+  path-type@4.0.0:
+    resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==}
+    engines: {node: '>=8'}
+
+  pathe@1.1.2:
+    resolution: {integrity: sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==}
+
+  phin@2.9.3:
+    resolution: {integrity: sha512-CzFr90qM24ju5f88quFC/6qohjC144rehe5n6DH900lgXmUe86+xCKc10ev56gRKC4/BkHUoG4uSiQgBiIXwDA==}
+    deprecated: Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.
+
+  picocolors@1.0.1:
+    resolution: {integrity: sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==}
+
+  picomatch@2.3.1:
+    resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==}
+    engines: {node: '>=8.6'}
+
+  pify@2.3.0:
+    resolution: {integrity: sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==}
+    engines: {node: '>=0.10.0'}
+
+  pinia-plugin-unistorage@0.0.19:
+    resolution: {integrity: sha512-kau6KlqRkXzMQUTT6CW3QRpqzVCypewpCLKcBYShKVpoqJY/0I8lqCwRSiRsIVaM8hI8/AO86RzFgq2wQZ7ZzA==}
+
+  pinia@2.0.36:
+    resolution: {integrity: sha512-4UKApwjlmJH+VuHKgA+zQMddcCb3ezYnyewQ9NVrsDqZ/j9dMv5+rh+1r48whKNdpFkZAWVxhBp5ewYaYX9JcQ==}
+    peerDependencies:
+      '@vue/composition-api': ^1.4.0
+      typescript: '>=4.4.4'
+      vue: ^2.6.14 || ^3.2.0
+    peerDependenciesMeta:
+      '@vue/composition-api':
+        optional: true
+      typescript:
+        optional: true
+
+  pinia@2.1.7:
+    resolution: {integrity: sha512-+C2AHFtcFqjPih0zpYuvof37SFxMQ7OEG2zV9jRI12i9BOy3YQVAHwdKtyyc8pDcDyIc33WCIsZaCFWU7WWxGQ==}
+    peerDependencies:
+      '@vue/composition-api': ^1.4.0
+      typescript: '>=4.4.4'
+      vue: ^2.6.14 || ^3.3.0
+    peerDependenciesMeta:
+      '@vue/composition-api':
+        optional: true
+      typescript:
+        optional: true
+
+  pirates@4.0.6:
+    resolution: {integrity: sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==}
+    engines: {node: '>= 6'}
+
+  pixelmatch@4.0.2:
+    resolution: {integrity: sha512-J8B6xqiO37sU/gkcMglv6h5Jbd9xNER7aHzpfRdNmV4IbQBzBpe4l9XmbG+xPF/znacgu2jfEw+wHffaq/YkXA==}
+    hasBin: true
+
+  pkg-dir@4.2.0:
+    resolution: {integrity: sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==}
+    engines: {node: '>=8'}
+
+  pkg-types@1.1.1:
+    resolution: {integrity: sha512-ko14TjmDuQJ14zsotODv7dBlwxKhUKQEhuhmbqo1uCi9BB0Z2alo/wAXg6q1dTR5TyuqYyWhjtfe/Tsh+X28jQ==}
+
+  pngjs@3.4.0:
+    resolution: {integrity: sha512-NCrCHhWmnQklfH4MtJMRjZ2a8c80qXeMlQMv2uVp9ISJMTt562SbGd6n2oq0PaPgKm7Z6pL9E2UlLIhC+SHL3w==}
+    engines: {node: '>=4.0.0'}
+
+  possible-typed-array-names@1.0.0:
+    resolution: {integrity: sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==}
+    engines: {node: '>= 0.4'}
+
+  postcss-import@14.1.0:
+    resolution: {integrity: sha512-flwI+Vgm4SElObFVPpTIT7SU7R3qk2L7PyduMcokiaVKuWv9d/U+Gm/QAd8NDLuykTWTkcrjOeD2Pp1rMeBTGw==}
+    engines: {node: '>=10.0.0'}
+    peerDependencies:
+      postcss: ^8.0.0
+
+  postcss-load-config@3.1.4:
+    resolution: {integrity: sha512-6DiM4E7v4coTE4uzA8U//WhtPwyhiim3eyjEMFCnUpzbrkK9wJHgKDT2mR+HbtSrd/NubVaYTOpSpjUl8NQeRg==}
+    engines: {node: '>= 10'}
+    peerDependencies:
+      postcss: '>=8.0.9'
+      ts-node: '>=9.0.0'
+    peerDependenciesMeta:
+      postcss:
+        optional: true
+      ts-node:
+        optional: true
+
+  postcss-modules-extract-imports@3.1.0:
+    resolution: {integrity: sha512-k3kNe0aNFQDAZGbin48pL2VNidTF0w4/eASDsxlyspobzU3wZQLOGj7L9gfRe0Jo9/4uud09DsjFNH7winGv8Q==}
+    engines: {node: ^10 || ^12 || >= 14}
+    peerDependencies:
+      postcss: ^8.1.0
+
+  postcss-modules-local-by-default@4.0.5:
+    resolution: {integrity: sha512-6MieY7sIfTK0hYfafw1OMEG+2bg8Q1ocHCpoWLqOKj3JXlKu4G7btkmM/B7lFubYkYWmRSPLZi5chid63ZaZYw==}
+    engines: {node: ^10 || ^12 || >= 14}
+    peerDependencies:
+      postcss: ^8.1.0
+
+  postcss-modules-scope@3.2.0:
+    resolution: {integrity: sha512-oq+g1ssrsZOsx9M96c5w8laRmvEu9C3adDSjI8oTcbfkrTE8hx/zfyobUoWIxaKPO8bt6S62kxpw5GqypEw1QQ==}
+    engines: {node: ^10 || ^12 || >= 14}
+    peerDependencies:
+      postcss: ^8.1.0
+
+  postcss-modules-values@4.0.0:
+    resolution: {integrity: sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ==}
+    engines: {node: ^10 || ^12 || >= 14}
+    peerDependencies:
+      postcss: ^8.1.0
+
+  postcss-modules@4.3.1:
+    resolution: {integrity: sha512-ItUhSUxBBdNamkT3KzIZwYNNRFKmkJrofvC2nWab3CPKhYBQ1f27XXh1PAPE27Psx58jeelPsxWB/+og+KEH0Q==}
+    peerDependencies:
+      postcss: ^8.0.0
+
+  postcss-selector-parser@6.1.0:
+    resolution: {integrity: sha512-UMz42UD0UY0EApS0ZL9o1XnLhSTtvvvLe5Dc2H2O56fvRZi+KulDyf5ctDhhtYJBGKStV2FL1fy6253cmLgqVQ==}
+    engines: {node: '>=4'}
+
+  postcss-value-parser@4.2.0:
+    resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==}
+
+  postcss@8.4.38:
+    resolution: {integrity: sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==}
+    engines: {node: ^10 || ^12 || >=14}
+
+  prelude-ls@1.2.1:
+    resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==}
+    engines: {node: '>= 0.8.0'}
+
+  prettier-linter-helpers@1.0.0:
+    resolution: {integrity: sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==}
+    engines: {node: '>=6.0.0'}
+
+  prettier@3.3.2:
+    resolution: {integrity: sha512-rAVeHYMcv8ATV5d508CFdn+8/pHPpXeIid1DdrPwXnaAdH7cqjVbpJaT5eq4yRAFU/lsbwYwSF/n5iNrdJHPQA==}
+    engines: {node: '>=14'}
+    hasBin: true
+
+  pretty-format@27.5.1:
+    resolution: {integrity: sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==}
+    engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
+
+  process@0.11.10:
+    resolution: {integrity: sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==}
+    engines: {node: '>= 0.6.0'}
+
+  prompts@2.4.2:
+    resolution: {integrity: sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==}
+    engines: {node: '>= 6'}
+
+  proxy-addr@2.0.7:
+    resolution: {integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==}
+    engines: {node: '>= 0.10'}
+
+  psl@1.9.0:
+    resolution: {integrity: sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==}
+
+  punycode@2.3.1:
+    resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==}
+    engines: {node: '>=6'}
+
+  qrcode-reader@1.0.4:
+    resolution: {integrity: sha512-rRjALGNh9zVqvweg1j5OKIQKNsw3bLC+7qwlnead5K/9cb1cEIAGkwikt/09U0K+2IDWGD9CC6SP7tHAjUeqvQ==}
+
+  qrcode-terminal@0.12.0:
+    resolution: {integrity: sha512-EXtzRZmC+YGmGlDFbXKxQiMZNwCLEO6BANKXG4iCtSIM0yqc/pappSx3RIKr4r0uh5JsBckOXeKrB3Iz7mdQpQ==}
+    hasBin: true
+
+  qs@6.11.0:
+    resolution: {integrity: sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==}
+    engines: {node: '>=0.6'}
+
+  querystringify@2.2.0:
+    resolution: {integrity: sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==}
+
+  queue-microtask@1.2.3:
+    resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==}
+
+  range-parser@1.2.1:
+    resolution: {integrity: sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==}
+    engines: {node: '>= 0.6'}
+
+  raw-body@2.5.2:
+    resolution: {integrity: sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==}
+    engines: {node: '>= 0.8'}
+
+  react-is@17.0.2:
+    resolution: {integrity: sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==}
+
+  read-cache@1.0.0:
+    resolution: {integrity: sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==}
+
+  readdirp@3.6.0:
+    resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==}
+    engines: {node: '>=8.10.0'}
+
+  regenerate-unicode-properties@10.1.1:
+    resolution: {integrity: sha512-X007RyZLsCJVVrjgEFVpLUTZwyOZk3oiL75ZcuYjlIWd6rNJtOjkBwQc5AsRrpbKVkxN6sklw/k/9m2jJYOf8Q==}
+    engines: {node: '>=4'}
+
+  regenerate@1.4.2:
+    resolution: {integrity: sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==}
+
+  regenerator-runtime@0.13.11:
+    resolution: {integrity: sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==}
+
+  regenerator-runtime@0.14.1:
+    resolution: {integrity: sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==}
+
+  regenerator-transform@0.15.2:
+    resolution: {integrity: sha512-hfMp2BoF0qOk3uc5V20ALGDS2ddjQaLrdl7xrGXvAIow7qeWRM2VA2HuCHkUKk9slq3VwEwLNK3DFBqDfPGYtg==}
+
+  regexp.prototype.flags@1.5.2:
+    resolution: {integrity: sha512-NcDiDkTLuPR+++OCKB0nWafEmhg/Da8aUPLPMQbK+bxKKCm1/S5he+AqYa4PlMCVBalb4/yxIRub6qkEx5yJbw==}
+    engines: {node: '>= 0.4'}
+
+  regexpu-core@5.3.2:
+    resolution: {integrity: sha512-RAM5FlZz+Lhmo7db9L298p2vHP5ZywrVXmVXpmAD9GuL5MPH6t9ROw1iA/wfHkQ76Qe7AaPF0nGuim96/IrQMQ==}
+    engines: {node: '>=4'}
+
+  regjsparser@0.9.1:
+    resolution: {integrity: sha512-dQUtn90WanSNl+7mQKcXAgZxvUe7Z0SqXlgzv0za4LwiUhyzBC58yQO3liFoUgu8GiJVInAhJjkj1N0EtQ5nkQ==}
+    hasBin: true
+
+  require-directory@2.1.1:
+    resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==}
+    engines: {node: '>=0.10.0'}
+
+  requires-port@1.0.0:
+    resolution: {integrity: sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==}
+
+  resolve-cwd@3.0.0:
+    resolution: {integrity: sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==}
+    engines: {node: '>=8'}
+
+  resolve-from@4.0.0:
+    resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==}
+    engines: {node: '>=4'}
+
+  resolve-from@5.0.0:
+    resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==}
+    engines: {node: '>=8'}
+
+  resolve.exports@1.1.1:
+    resolution: {integrity: sha512-/NtpHNDN7jWhAaQ9BvBUYZ6YTXsRBgfqWFWP7BZBaoMJO/I3G5OFzvTuWNlZC3aPjins1F+TNrLKsGbH4rfsRQ==}
+    engines: {node: '>=10'}
+
+  resolve@1.22.8:
+    resolution: {integrity: sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==}
+    hasBin: true
+
+  restore-cursor@3.1.0:
+    resolution: {integrity: sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==}
+    engines: {node: '>=8'}
+
+  reusify@1.0.4:
+    resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==}
+    engines: {iojs: '>=1.0.0', node: '>=0.10.0'}
+
+  rimraf@3.0.2:
+    resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==}
+    deprecated: Rimraf versions prior to v4 are no longer supported
+    hasBin: true
+
+  rollup@3.29.4:
+    resolution: {integrity: sha512-oWzmBZwvYrU0iJHtDmhsm662rC15FRXmcjCk1xD771dFDx5jJ02ufAQQTn0etB2emNk4J9EZg/yWKpsn9BWGRw==}
+    engines: {node: '>=14.18.0', npm: '>=8.0.0'}
+    hasBin: true
+
+  run-async@2.4.1:
+    resolution: {integrity: sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==}
+    engines: {node: '>=0.12.0'}
+
+  run-parallel@1.2.0:
+    resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==}
+
+  rxjs@6.6.7:
+    resolution: {integrity: sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==}
+    engines: {npm: '>=2.0.0'}
+
+  safe-area-insets@1.4.1:
+    resolution: {integrity: sha512-r/nRWTjFGhhm3w1Z6Kd/jY11srN+lHt2mNl1E/emQGW8ic7n3Avu4noibklfSM+Y34peNphHD/BSZecav0sXYQ==}
+
+  safe-array-concat@1.1.2:
+    resolution: {integrity: sha512-vj6RsCsWBCf19jIeHEfkRMw8DPiBb+DMXklQ/1SGDHOMlHdPUkZXFQ2YdplS23zESTijAcurb1aSgJA3AgMu1Q==}
+    engines: {node: '>=0.4'}
+
+  safe-buffer@5.2.1:
+    resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==}
+
+  safe-regex-test@1.0.3:
+    resolution: {integrity: sha512-CdASjNJPvRa7roO6Ra/gLYBTzYzzPyyBXxIMdGW3USQLyjWEls2RgW5UBTXaQVp+OrpeCK3bLem8smtmheoRuw==}
+    engines: {node: '>= 0.4'}
+
+  safer-buffer@2.1.2:
+    resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==}
+
+  sass-loader@14.2.1:
+    resolution: {integrity: sha512-G0VcnMYU18a4N7VoNDegg2OuMjYtxnqzQWARVWCIVSZwJeiL9kg8QMsuIZOplsJgTzZLF6jGxI3AClj8I9nRdQ==}
+    engines: {node: '>= 18.12.0'}
+    peerDependencies:
+      '@rspack/core': 0.x || 1.x
+      node-sass: ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0 || ^9.0.0
+      sass: ^1.3.0
+      sass-embedded: '*'
+      webpack: ^5.0.0
+    peerDependenciesMeta:
+      '@rspack/core':
+        optional: true
+      node-sass:
+        optional: true
+      sass:
+        optional: true
+      sass-embedded:
+        optional: true
+      webpack:
+        optional: true
+
+  sass@1.77.6:
+    resolution: {integrity: sha512-ByXE1oLD79GVq9Ht1PeHWCPMPB8XHpBuz1r85oByKHjZY6qV6rWnQovQzXJXuQ/XyE1Oj3iPk3lo28uzaRA2/Q==}
+    engines: {node: '>=14.0.0'}
+    hasBin: true
+
+  sax@1.4.1:
+    resolution: {integrity: sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg==}
+
+  saxes@5.0.1:
+    resolution: {integrity: sha512-5LBh1Tls8c9xgGjw3QrMwETmTMVk0oFgvrFSvWx62llR2hcEInrKNZ2GZCCuuy2lvWrdl5jhbpeqc5hRYKFOcw==}
+    engines: {node: '>=10'}
+
+  scule@1.3.0:
+    resolution: {integrity: sha512-6FtHJEvt+pVMIB9IBY+IcCJ6Z5f1iQnytgyfKMhDKgmzYG+TeH/wx1y3l27rshSbLiSanrR9ffZDrEsmjlQF2g==}
+
+  semver@6.3.1:
+    resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==}
+    hasBin: true
+
+  semver@7.6.2:
+    resolution: {integrity: sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==}
+    engines: {node: '>=10'}
+    hasBin: true
+
+  send@0.18.0:
+    resolution: {integrity: sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==}
+    engines: {node: '>= 0.8.0'}
+
+  serve-static@1.15.0:
+    resolution: {integrity: sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==}
+    engines: {node: '>= 0.8.0'}
+
+  set-function-length@1.2.2:
+    resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==}
+    engines: {node: '>= 0.4'}
+
+  set-function-name@2.0.2:
+    resolution: {integrity: sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==}
+    engines: {node: '>= 0.4'}
+
+  setprototypeof@1.2.0:
+    resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==}
+
+  shebang-command@2.0.0:
+    resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==}
+    engines: {node: '>=8'}
+
+  shebang-regex@3.0.0:
+    resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==}
+    engines: {node: '>=8'}
+
+  side-channel@1.0.6:
+    resolution: {integrity: sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==}
+    engines: {node: '>= 0.4'}
+
+  signal-exit@3.0.7:
+    resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==}
+
+  sisteransi@1.0.5:
+    resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==}
+
+  slash@3.0.0:
+    resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==}
+    engines: {node: '>=8'}
+
+  source-map-js@1.2.0:
+    resolution: {integrity: sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==}
+    engines: {node: '>=0.10.0'}
+
+  source-map-support@0.5.21:
+    resolution: {integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==}
+
+  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'}
+
+  sprintf-js@1.0.3:
+    resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==}
+
+  stack-utils@2.0.6:
+    resolution: {integrity: sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==}
+    engines: {node: '>=10'}
+
+  statuses@2.0.1:
+    resolution: {integrity: sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==}
+    engines: {node: '>= 0.8'}
+
+  string-hash@1.1.3:
+    resolution: {integrity: sha512-kJUvRUFK49aub+a7T1nNE66EJbZBMnBgoC1UbCZ5n6bsZKBRga4KgBRTMn/pFkeCZSYtNeSyMxPDM0AXWELk2A==}
+
+  string-length@4.0.2:
+    resolution: {integrity: sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==}
+    engines: {node: '>=10'}
+
+  string-width@4.2.3:
+    resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==}
+    engines: {node: '>=8'}
+
+  string.prototype.trim@1.2.9:
+    resolution: {integrity: sha512-klHuCNxiMZ8MlsOihJhJEBJAiMVqU3Z2nEXWfWnIqjN0gEFS9J9+IxKozWWtQGcgoa1WUZzLjKPTr4ZHNFTFxw==}
+    engines: {node: '>= 0.4'}
+
+  string.prototype.trimend@1.0.8:
+    resolution: {integrity: sha512-p73uL5VCHCO2BZZ6krwwQE3kCzM7NKmis8S//xEC6fQonchbum4eP6kR4DLEjQFO3Wnj3Fuo8NM0kOSjVdHjZQ==}
+
+  string.prototype.trimstart@1.0.8:
+    resolution: {integrity: sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==}
+    engines: {node: '>= 0.4'}
+
+  strip-ansi@6.0.1:
+    resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==}
+    engines: {node: '>=8'}
+
+  strip-bom@3.0.0:
+    resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==}
+    engines: {node: '>=4'}
+
+  strip-bom@4.0.0:
+    resolution: {integrity: sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==}
+    engines: {node: '>=8'}
+
+  strip-final-newline@2.0.0:
+    resolution: {integrity: sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==}
+    engines: {node: '>=6'}
+
+  strip-json-comments@3.1.1:
+    resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==}
+    engines: {node: '>=8'}
+
+  strip-literal@2.1.0:
+    resolution: {integrity: sha512-Op+UycaUt/8FbN/Z2TWPBLge3jWrP3xj10f3fnYxf052bKuS3EKs1ZQcVGjnEMdsNVAM+plXRdmjrZ/KgG3Skw==}
+
+  supports-color@5.5.0:
+    resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==}
+    engines: {node: '>=4'}
+
+  supports-color@7.2.0:
+    resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==}
+    engines: {node: '>=8'}
+
+  supports-color@8.1.1:
+    resolution: {integrity: sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==}
+    engines: {node: '>=10'}
+
+  supports-hyperlinks@2.3.0:
+    resolution: {integrity: sha512-RpsAZlpWcDwOPQA22aCH4J0t7L8JmAvsCxfOSEwm7cQs3LshN36QaTkwd70DnBOXDWGssw2eUoc8CaRWT0XunA==}
+    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==}
+
+  symbol-tree@3.2.4:
+    resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==}
+
+  synckit@0.8.8:
+    resolution: {integrity: sha512-HwOKAP7Wc5aRGYdKH+dw0PRRpbO841v2DENBtjnR5HFWoiNByAl7vrx3p0G/rCyYXQsrxqtX48TImFtPcIHSpQ==}
+    engines: {node: ^14.18.0 || >=16.0.0}
+
+  systemjs@6.15.1:
+    resolution: {integrity: sha512-Nk8c4lXvMB98MtbmjX7JwJRgJOL8fluecYCfCeYBznwmpOs8Bf15hLM6z4z71EDAhQVrQrI+wt1aLWSXZq+hXA==}
+
+  tapable@2.2.1:
+    resolution: {integrity: sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==}
+    engines: {node: '>=6'}
+
+  terminal-link@2.1.1:
+    resolution: {integrity: sha512-un0FmiRUQNr5PJqy9kP7c40F5BOfpGlYTrxonDChEZB7pzZxRNp/bt+ymiy9/npwXya9KH99nJ/GXFIiUkYGFQ==}
+    engines: {node: '>=8'}
+
+  terser@5.31.1:
+    resolution: {integrity: sha512-37upzU1+viGvuFtBo9NPufCb9dwM0+l9hMxYyWfBA+fbwrPqNJAhbZ6W47bBFnZHKHTUBnMvi87434qq+qnxOg==}
+    engines: {node: '>=10'}
+    hasBin: true
+
+  test-exclude@6.0.0:
+    resolution: {integrity: sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==}
+    engines: {node: '>=8'}
+
+  text-table@0.2.0:
+    resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==}
+
+  throat@6.0.2:
+    resolution: {integrity: sha512-WKexMoJj3vEuK0yFEapj8y64V0A6xcuPuK9Gt1d0R+dzCSJc0lHqQytAbSB4cDAK0dWh4T0E2ETkoLE2WZ41OQ==}
+
+  through@2.3.8:
+    resolution: {integrity: sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==}
+
+  timm@1.7.1:
+    resolution: {integrity: sha512-IjZc9KIotudix8bMaBW6QvMuq64BrJWFs1+4V0lXwWGQZwH+LnX87doAYhem4caOEusRP9/g6jVDQmZ8XOk1nw==}
+
+  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'}
+
+  tmpl@1.0.5:
+    resolution: {integrity: sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==}
+
+  to-fast-properties@2.0.0:
+    resolution: {integrity: sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==}
+    engines: {node: '>=4'}
+
+  to-regex-range@5.0.1:
+    resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==}
+    engines: {node: '>=8.0'}
+
+  toidentifier@1.0.1:
+    resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==}
+    engines: {node: '>=0.6'}
+
+  tough-cookie@4.1.4:
+    resolution: {integrity: sha512-Loo5UUvLD9ScZ6jh8beX1T6sO1w2/MpCRpEP7V280GKMVUQ0Jzar2U3UJPsrdbziLEMMhu3Ujnq//rhiFuIeag==}
+    engines: {node: '>=6'}
+
+  tr46@2.1.0:
+    resolution: {integrity: sha512-15Ih7phfcdP5YxqiB+iDtLoaTz4Nd35+IiAv0kQ5FNKHzXgdWqPoTIqEDDJmXceQt4JZk6lVPT8lnDlPpGDppw==}
+    engines: {node: '>=8'}
+
+  ts-api-utils@1.3.0:
+    resolution: {integrity: sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ==}
+    engines: {node: '>=16'}
+    peerDependencies:
+      typescript: '>=4.2.0'
+
+  ts-md5@1.3.1:
+    resolution: {integrity: sha512-DiwiXfwvcTeZ5wCE0z+2A9EseZsztaiZtGrtSaY5JOD7ekPnR/GoIVD5gXZAlK9Na9Kvpo9Waz5rW64WKAWApg==}
+    engines: {node: '>=12'}
+
+  tsconfig-paths@3.15.0:
+    resolution: {integrity: sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==}
+
+  tslib@1.14.1:
+    resolution: {integrity: sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==}
+
+  tslib@2.3.0:
+    resolution: {integrity: sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==}
+
+  tslib@2.6.3:
+    resolution: {integrity: sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==}
+
+  type-check@0.4.0:
+    resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==}
+    engines: {node: '>= 0.8.0'}
+
+  type-detect@4.0.8:
+    resolution: {integrity: sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==}
+    engines: {node: '>=4'}
+
+  type-fest@0.20.2:
+    resolution: {integrity: sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==}
+    engines: {node: '>=10'}
+
+  type-fest@0.21.3:
+    resolution: {integrity: sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==}
+    engines: {node: '>=10'}
+
+  type-is@1.6.18:
+    resolution: {integrity: sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==}
+    engines: {node: '>= 0.6'}
+
+  typed-array-buffer@1.0.2:
+    resolution: {integrity: sha512-gEymJYKZtKXzzBzM4jqa9w6Q1Jjm7x2d+sh19AdsD4wqnMPDYyvwpsIc2Q/835kHuo3BEQ7CjelGhfTsoBb2MQ==}
+    engines: {node: '>= 0.4'}
+
+  typed-array-byte-length@1.0.1:
+    resolution: {integrity: sha512-3iMJ9q0ao7WE9tWcaYKIptkNBuOIcZCCT0d4MRvuuH88fEoEH62IuQe0OtraD3ebQEoTRk8XCBoknUNc1Y67pw==}
+    engines: {node: '>= 0.4'}
+
+  typed-array-byte-offset@1.0.2:
+    resolution: {integrity: sha512-Ous0vodHa56FviZucS2E63zkgtgrACj7omjwd/8lTEMEPFFyjfixMZ1ZXenpgCFBBt4EC1J2XsyVS2gkG0eTFA==}
+    engines: {node: '>= 0.4'}
+
+  typed-array-length@1.0.6:
+    resolution: {integrity: sha512-/OxDN6OtAk5KBpGb28T+HZc2M+ADtvRxXrKKbUwtsLgdoxgX13hyy7ek6bFRl5+aBs2yZzB0c4CnQfAtVypW/g==}
+    engines: {node: '>= 0.4'}
+
+  typedarray-to-buffer@3.1.5:
+    resolution: {integrity: sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==}
+
+  typescript@4.9.5:
+    resolution: {integrity: sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==}
+    engines: {node: '>=4.2.0'}
+    hasBin: true
+
+  ufo@1.5.3:
+    resolution: {integrity: sha512-Y7HYmWaFwPUmkoQCUIAYpKqkOf+SbVj/2fJJZ4RJMCfZp0rTGwRbzQD+HghfnhKOjL9E01okqz+ncJskGYfBNw==}
+
+  unbox-primitive@1.0.2:
+    resolution: {integrity: sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==}
+
+  undici-types@5.26.5:
+    resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==}
+
+  unicode-canonical-property-names-ecmascript@2.0.0:
+    resolution: {integrity: sha512-yY5PpDlfVIU5+y/BSCxAJRBIS1Zc2dDG3Ujq+sR0U+JjUevW2JhocOF+soROYDSaAezOzOKuyyixhD6mBknSmQ==}
+    engines: {node: '>=4'}
+
+  unicode-match-property-ecmascript@2.0.0:
+    resolution: {integrity: sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==}
+    engines: {node: '>=4'}
+
+  unicode-match-property-value-ecmascript@2.1.0:
+    resolution: {integrity: sha512-qxkjQt6qjg/mYscYMC0XKRn3Rh0wFPlfxB0xkt9CfyTvpX1Ra0+rAmdX2QyAobptSEvuy4RtpPRui6XkV+8wjA==}
+    engines: {node: '>=4'}
+
+  unicode-property-aliases-ecmascript@2.1.0:
+    resolution: {integrity: sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w==}
+    engines: {node: '>=4'}
+
+  unimport@3.7.2:
+    resolution: {integrity: sha512-91mxcZTadgXyj3lFWmrGT8GyoRHWuE5fqPOjg5RVtF6vj+OfM5G6WCzXjuYtSgELE5ggB34RY4oiCSEP8I3AHw==}
+
+  universalify@0.2.0:
+    resolution: {integrity: sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==}
+    engines: {node: '>= 4.0.0'}
+
+  universalify@2.0.1:
+    resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==}
+    engines: {node: '>= 10.0.0'}
+
+  unpipe@1.0.0:
+    resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==}
+    engines: {node: '>= 0.8'}
+
+  unplugin-auto-import@0.16.7:
+    resolution: {integrity: sha512-w7XmnRlchq6YUFJVFGSvG1T/6j8GrdYN6Em9Wf0Ye+HXgD/22kont+WnuCAA0UaUoxtuvRR1u/mXKy63g/hfqQ==}
+    engines: {node: '>=14'}
+    peerDependencies:
+      '@nuxt/kit': ^3.2.2
+      '@vueuse/core': '*'
+    peerDependenciesMeta:
+      '@nuxt/kit':
+        optional: true
+      '@vueuse/core':
+        optional: true
+
+  unplugin@1.10.1:
+    resolution: {integrity: sha512-d6Mhq8RJeGA8UfKCu54Um4lFA0eSaRa3XxdAJg8tIdxbu1ubW0hBCZUL7yI2uGyYCRndvbK8FLHzqy2XKfeMsg==}
+    engines: {node: '>=14.0.0'}
+
+  unquote@1.1.1:
+    resolution: {integrity: sha512-vRCqFv6UhXpWxZPyGDh/F3ZpNv8/qo7w6iufLpQg9aKnQ71qM4B5KiI7Mia9COcjEhrO9LueHpMYjYzsWH3OIg==}
+
+  update-browserslist-db@1.0.16:
+    resolution: {integrity: sha512-KVbTxlBYlckhF5wgfyZXTWnMn7MMZjMu9XG8bPlliUOP9ThaF4QnhP8qrjrH7DRzHfSk0oQv1wToW+iA5GajEQ==}
+    hasBin: true
+    peerDependencies:
+      browserslist: '>= 4.21.0'
+
+  uri-js@4.4.1:
+    resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==}
+
+  url-parse@1.5.10:
+    resolution: {integrity: sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==}
+
+  utif@2.0.1:
+    resolution: {integrity: sha512-Z/S1fNKCicQTf375lIP9G8Sa1H/phcysstNrrSdZKj1f9g58J4NMgb5IgiEZN9/nLMPDwF0W7hdOe9Qq2IYoLg==}
+
+  util-deprecate@1.0.2:
+    resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==}
+
+  utils-merge@1.0.1:
+    resolution: {integrity: sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==}
+    engines: {node: '>= 0.4.0'}
+
+  v8-to-istanbul@8.1.1:
+    resolution: {integrity: sha512-FGtKtv3xIpR6BYhvgH8MI/y78oT7d8Au3ww4QIxymrCtZEh5b8gCw2siywE+puhEmuWKDtmfrvF5UlB298ut3w==}
+    engines: {node: '>=10.12.0'}
+
+  vary@1.1.2:
+    resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==}
+    engines: {node: '>= 0.8'}
+
+  vite@4.3.5:
+    resolution: {integrity: sha512-0gEnL9wiRFxgz40o/i/eTBwm+NEbpUeTWhzKrZDSdKm6nplj+z4lKz8ANDgildxHm47Vg8EUia0aicKbawUVVA==}
+    engines: {node: ^14.18.0 || >=16.0.0}
+    hasBin: true
+    peerDependencies:
+      '@types/node': '>= 14'
+      less: '*'
+      sass: '*'
+      stylus: '*'
+      sugarss: '*'
+      terser: ^5.4.0
+    peerDependenciesMeta:
+      '@types/node':
+        optional: true
+      less:
+        optional: true
+      sass:
+        optional: true
+      stylus:
+        optional: true
+      sugarss:
+        optional: true
+      terser:
+        optional: true
+
+  vue-demi@0.14.8:
+    resolution: {integrity: sha512-Uuqnk9YE9SsWeReYqK2alDI5YzciATE0r2SkA6iMAtuXvNTMNACJLJEXNXaEy94ECuBe4Sk6RzRU80kjdbIo1Q==}
+    engines: {node: '>=12'}
+    hasBin: true
+    peerDependencies:
+      '@vue/composition-api': ^1.0.0-rc.1
+      vue: ^3.0.0-0 || ^2.6.0
+    peerDependenciesMeta:
+      '@vue/composition-api':
+        optional: true
+
+  vue-eslint-parser@9.4.3:
+    resolution: {integrity: sha512-2rYRLWlIpaiN8xbPiDyXZXRgLGOtWxERV7ND5fFAv5qo1D2N9Fu9MNajBNc6o13lZ+24DAWCkQCvj4klgmcITg==}
+    engines: {node: ^14.17.0 || >=16.0.0}
+    peerDependencies:
+      eslint: '>=6.0.0'
+
+  vue-router@4.4.0:
+    resolution: {integrity: sha512-HB+t2p611aIZraV2aPSRNXf0Z/oLZFrlygJm+sZbdJaW6lcFqEDQwnzUBXn+DApw+/QzDU/I9TeWx9izEjTmsA==}
+    peerDependencies:
+      vue: ^3.2.0
+
+  vue-template-compiler@2.7.16:
+    resolution: {integrity: sha512-AYbUWAJHLGGQM7+cNTELw+KsOG9nl2CnSv467WobS5Cv9uk3wFcnr1Etsz2sEIHEZvw1U+o9mRlEO6QbZvUPGQ==}
+
+  vue-tsc@1.8.27:
+    resolution: {integrity: sha512-WesKCAZCRAbmmhuGl3+VrdWItEvfoFIPXOvUJkjULi+x+6G/Dy69yO3TBRJDr9eUlmsNAwVmxsNZxvHKzbkKdg==}
+    hasBin: true
+    peerDependencies:
+      typescript: '*'
+
+  vue@3.4.30:
+    resolution: {integrity: sha512-NcxtKCwkdf1zPsr7Y8+QlDBCGqxvjLXF2EX+yi76rV5rrz90Y6gK1cq0olIhdWGgrlhs9ElHuhi9t3+W5sG5Xw==}
+    peerDependencies:
+      typescript: '*'
+    peerDependenciesMeta:
+      typescript:
+        optional: true
+
+  w3c-hr-time@1.0.2:
+    resolution: {integrity: sha512-z8P5DvDNjKDoFIHK7q8r8lackT6l+jo/Ye3HOle7l9nICP9lf1Ci25fy9vHd0JOWewkIFzXIEig3TdKT7JQ5fQ==}
+    deprecated: Use your platform's native performance.now() and performance.timeOrigin.
+
+  w3c-xmlserializer@2.0.0:
+    resolution: {integrity: sha512-4tzD0mF8iSiMiNs30BiLO3EpfGLZUT2MSX/G+o7ZywDzliWQ3OPtTZ0PTC3B3ca1UAf4cJMHB+2Bf56EriJuRA==}
+    engines: {node: '>=10'}
+
+  walker@1.0.8:
+    resolution: {integrity: sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==}
+
+  webidl-conversions@5.0.0:
+    resolution: {integrity: sha512-VlZwKPCkYKxQgeSbH5EyngOmRp7Ww7I9rQLERETtf5ofd9pGeswWiOtogpEO850jziPRarreGxn5QIiTqpb2wA==}
+    engines: {node: '>=8'}
+
+  webidl-conversions@6.1.0:
+    resolution: {integrity: sha512-qBIvFLGiBpLjfwmYAaHPXsn+ho5xZnGvyGvsarywGNc8VyQJUMHJ8OBKGGrPER0okBeMDaan4mNBlgBROxuI8w==}
+    engines: {node: '>=10.4'}
+
+  webpack-sources@3.2.3:
+    resolution: {integrity: sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==}
+    engines: {node: '>=10.13.0'}
+
+  webpack-virtual-modules@0.6.2:
+    resolution: {integrity: sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ==}
+
+  whatwg-encoding@1.0.5:
+    resolution: {integrity: sha512-b5lim54JOPN9HtzvK9HFXvBma/rnfFeqsic0hSpjtDbVxR3dJKLc+KB4V6GgiGOvl7CY/KNh8rxSo9DKQrnUEw==}
+
+  whatwg-mimetype@2.3.0:
+    resolution: {integrity: sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g==}
+
+  whatwg-url@8.7.0:
+    resolution: {integrity: sha512-gAojqb/m9Q8a5IV96E3fHJM70AzCkgt4uXYX2O7EmuyOnLrViCQlsEBmF9UQIu3/aeAIp2U17rtbpZWNntQqdg==}
+    engines: {node: '>=10'}
+
+  which-boxed-primitive@1.0.2:
+    resolution: {integrity: sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==}
+
+  which-typed-array@1.1.15:
+    resolution: {integrity: sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA==}
+    engines: {node: '>= 0.4'}
+
+  which@2.0.2:
+    resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==}
+    engines: {node: '>= 8'}
+    hasBin: true
+
+  word-wrap@1.2.5:
+    resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==}
+    engines: {node: '>=0.10.0'}
+
+  wrap-ansi@7.0.0:
+    resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==}
+    engines: {node: '>=10'}
+
+  wrappy@1.0.2:
+    resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==}
+
+  write-file-atomic@3.0.3:
+    resolution: {integrity: sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==}
+
+  ws@7.5.10:
+    resolution: {integrity: sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==}
+    engines: {node: '>=8.3.0'}
+    peerDependencies:
+      bufferutil: ^4.0.1
+      utf-8-validate: ^5.0.2
+    peerDependenciesMeta:
+      bufferutil:
+        optional: true
+      utf-8-validate:
+        optional: true
+
+  ws@8.17.1:
+    resolution: {integrity: sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==}
+    engines: {node: '>=10.0.0'}
+    peerDependencies:
+      bufferutil: ^4.0.1
+      utf-8-validate: '>=5.0.2'
+    peerDependenciesMeta:
+      bufferutil:
+        optional: true
+      utf-8-validate:
+        optional: true
+
+  xhr@2.6.0:
+    resolution: {integrity: sha512-/eCGLb5rxjx5e3mF1A7s+pLlR6CGyqWN91fv1JgER5mVWg1MZmlhBvy9kjcsOdRk8RrIujotWyJamfyrp+WIcA==}
+
+  xml-name-validator@3.0.0:
+    resolution: {integrity: sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw==}
+
+  xml-name-validator@4.0.0:
+    resolution: {integrity: sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==}
+    engines: {node: '>=12'}
+
+  xml-parse-from-string@1.0.1:
+    resolution: {integrity: sha512-ErcKwJTF54uRzzNMXq2X5sMIy88zJvfN2DmdoQvy7PAFJ+tPRU6ydWuOKNMyfmOjdyBQTFREi60s0Y0SyI0G0g==}
+
+  xml2js@0.5.0:
+    resolution: {integrity: sha512-drPFnkQJik/O+uPKpqSgr22mpuFHqKdbS835iAQrUC73L2F5WkboIRd63ai/2Yg6I1jzifPFKH2NTK+cfglkIA==}
+    engines: {node: '>=4.0.0'}
+
+  xmlbuilder@11.0.1:
+    resolution: {integrity: sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==}
+    engines: {node: '>=4.0'}
+
+  xmlchars@2.2.0:
+    resolution: {integrity: sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==}
+
+  xmlhttprequest@1.8.0:
+    resolution: {integrity: sha512-58Im/U0mlVBLM38NdZjHyhuMtCqa61469k2YP/AaPbvCoV9aQGUpbJBj1QRm2ytRiVQBD/fsw7L2bJGDVQswBA==}
+    engines: {node: '>=0.4.0'}
+
+  xregexp@3.1.0:
+    resolution: {integrity: sha512-4Y1x6DyB8xRoxosooa6PlGWqmmSKatbzhrftZ7Purmm4B8R4qIEJG1A2hZsdz5DhmIqS0msC0I7KEq93GphEVg==}
+
+  xtend@4.0.2:
+    resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==}
+    engines: {node: '>=0.4'}
+
+  y18n@5.0.8:
+    resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==}
+    engines: {node: '>=10'}
+
+  yallist@3.1.1:
+    resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==}
+
+  yaml@1.10.2:
+    resolution: {integrity: sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==}
+    engines: {node: '>= 6'}
+
+  yargs-parser@20.2.9:
+    resolution: {integrity: sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==}
+    engines: {node: '>=10'}
+
+  yargs@16.2.0:
+    resolution: {integrity: sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==}
+    engines: {node: '>=10'}
+
+  yocto-queue@0.1.0:
+    resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==}
+    engines: {node: '>=10'}
+
+  zrender@5.5.0:
+    resolution: {integrity: sha512-O3MilSi/9mwoovx77m6ROZM7sXShR/O/JIanvzTwjN3FORfLSr81PsUGd7jlaYOeds9d8tw82oP44+3YucVo+w==}
+
+snapshots:
+
+  '@ampproject/remapping@2.3.0':
+    dependencies:
+      '@jridgewell/gen-mapping': 0.3.5
+      '@jridgewell/trace-mapping': 0.3.25
+
+  '@antfu/utils@0.7.10': {}
+
+  '@babel/code-frame@7.24.7':
+    dependencies:
+      '@babel/highlight': 7.24.7
+      picocolors: 1.0.1
+
+  '@babel/compat-data@7.24.7': {}
+
+  '@babel/core@7.24.7':
+    dependencies:
+      '@ampproject/remapping': 2.3.0
+      '@babel/code-frame': 7.24.7
+      '@babel/generator': 7.24.7
+      '@babel/helper-compilation-targets': 7.24.7
+      '@babel/helper-module-transforms': 7.24.7(@babel/core@7.24.7)
+      '@babel/helpers': 7.24.7
+      '@babel/parser': 7.24.7
+      '@babel/template': 7.24.7
+      '@babel/traverse': 7.24.7
+      '@babel/types': 7.24.7
+      convert-source-map: 2.0.0
+      debug: 4.3.5
+      gensync: 1.0.0-beta.2
+      json5: 2.2.3
+      semver: 6.3.1
+    transitivePeerDependencies:
+      - supports-color
+
+  '@babel/generator@7.24.7':
+    dependencies:
+      '@babel/types': 7.24.7
+      '@jridgewell/gen-mapping': 0.3.5
+      '@jridgewell/trace-mapping': 0.3.25
+      jsesc: 2.5.2
+
+  '@babel/helper-annotate-as-pure@7.24.7':
+    dependencies:
+      '@babel/types': 7.24.7
+
+  '@babel/helper-builder-binary-assignment-operator-visitor@7.24.7':
+    dependencies:
+      '@babel/traverse': 7.24.7
+      '@babel/types': 7.24.7
+    transitivePeerDependencies:
+      - supports-color
+
+  '@babel/helper-compilation-targets@7.24.7':
+    dependencies:
+      '@babel/compat-data': 7.24.7
+      '@babel/helper-validator-option': 7.24.7
+      browserslist: 4.23.1
+      lru-cache: 5.1.1
+      semver: 6.3.1
+
+  '@babel/helper-create-class-features-plugin@7.24.7(@babel/core@7.24.7)':
+    dependencies:
+      '@babel/core': 7.24.7
+      '@babel/helper-annotate-as-pure': 7.24.7
+      '@babel/helper-environment-visitor': 7.24.7
+      '@babel/helper-function-name': 7.24.7
+      '@babel/helper-member-expression-to-functions': 7.24.7
+      '@babel/helper-optimise-call-expression': 7.24.7
+      '@babel/helper-replace-supers': 7.24.7(@babel/core@7.24.7)
+      '@babel/helper-skip-transparent-expression-wrappers': 7.24.7
+      '@babel/helper-split-export-declaration': 7.24.7
+      semver: 6.3.1
+    transitivePeerDependencies:
+      - supports-color
+
+  '@babel/helper-create-regexp-features-plugin@7.24.7(@babel/core@7.24.7)':
+    dependencies:
+      '@babel/core': 7.24.7
+      '@babel/helper-annotate-as-pure': 7.24.7
+      regexpu-core: 5.3.2
+      semver: 6.3.1
+
+  '@babel/helper-define-polyfill-provider@0.6.2(@babel/core@7.24.7)':
+    dependencies:
+      '@babel/core': 7.24.7
+      '@babel/helper-compilation-targets': 7.24.7
+      '@babel/helper-plugin-utils': 7.24.7
+      debug: 4.3.5
+      lodash.debounce: 4.0.8
+      resolve: 1.22.8
+    transitivePeerDependencies:
+      - supports-color
+
+  '@babel/helper-environment-visitor@7.24.7':
+    dependencies:
+      '@babel/types': 7.24.7
+
+  '@babel/helper-function-name@7.24.7':
+    dependencies:
+      '@babel/template': 7.24.7
+      '@babel/types': 7.24.7
+
+  '@babel/helper-hoist-variables@7.24.7':
+    dependencies:
+      '@babel/types': 7.24.7
+
+  '@babel/helper-member-expression-to-functions@7.24.7':
+    dependencies:
+      '@babel/traverse': 7.24.7
+      '@babel/types': 7.24.7
+    transitivePeerDependencies:
+      - supports-color
+
+  '@babel/helper-module-imports@7.22.15':
+    dependencies:
+      '@babel/types': 7.24.7
+
+  '@babel/helper-module-imports@7.24.7':
+    dependencies:
+      '@babel/traverse': 7.24.7
+      '@babel/types': 7.24.7
+    transitivePeerDependencies:
+      - supports-color
+
+  '@babel/helper-module-transforms@7.24.7(@babel/core@7.24.7)':
+    dependencies:
+      '@babel/core': 7.24.7
+      '@babel/helper-environment-visitor': 7.24.7
+      '@babel/helper-module-imports': 7.24.7
+      '@babel/helper-simple-access': 7.24.7
+      '@babel/helper-split-export-declaration': 7.24.7
+      '@babel/helper-validator-identifier': 7.24.7
+    transitivePeerDependencies:
+      - supports-color
+
+  '@babel/helper-optimise-call-expression@7.24.7':
+    dependencies:
+      '@babel/types': 7.24.7
+
+  '@babel/helper-plugin-utils@7.24.7': {}
+
+  '@babel/helper-remap-async-to-generator@7.24.7(@babel/core@7.24.7)':
+    dependencies:
+      '@babel/core': 7.24.7
+      '@babel/helper-annotate-as-pure': 7.24.7
+      '@babel/helper-environment-visitor': 7.24.7
+      '@babel/helper-wrap-function': 7.24.7
+    transitivePeerDependencies:
+      - supports-color
+
+  '@babel/helper-replace-supers@7.24.7(@babel/core@7.24.7)':
+    dependencies:
+      '@babel/core': 7.24.7
+      '@babel/helper-environment-visitor': 7.24.7
+      '@babel/helper-member-expression-to-functions': 7.24.7
+      '@babel/helper-optimise-call-expression': 7.24.7
+    transitivePeerDependencies:
+      - supports-color
+
+  '@babel/helper-simple-access@7.24.7':
+    dependencies:
+      '@babel/traverse': 7.24.7
+      '@babel/types': 7.24.7
+    transitivePeerDependencies:
+      - supports-color
+
+  '@babel/helper-skip-transparent-expression-wrappers@7.24.7':
+    dependencies:
+      '@babel/traverse': 7.24.7
+      '@babel/types': 7.24.7
+    transitivePeerDependencies:
+      - supports-color
+
+  '@babel/helper-split-export-declaration@7.24.7':
+    dependencies:
+      '@babel/types': 7.24.7
+
+  '@babel/helper-string-parser@7.24.7': {}
+
+  '@babel/helper-validator-identifier@7.24.7': {}
+
+  '@babel/helper-validator-option@7.24.7': {}
+
+  '@babel/helper-wrap-function@7.24.7':
+    dependencies:
+      '@babel/helper-function-name': 7.24.7
+      '@babel/template': 7.24.7
+      '@babel/traverse': 7.24.7
+      '@babel/types': 7.24.7
+    transitivePeerDependencies:
+      - supports-color
+
+  '@babel/helpers@7.24.7':
+    dependencies:
+      '@babel/template': 7.24.7
+      '@babel/types': 7.24.7
+
+  '@babel/highlight@7.24.7':
+    dependencies:
+      '@babel/helper-validator-identifier': 7.24.7
+      chalk: 2.4.2
+      js-tokens: 4.0.0
+      picocolors: 1.0.1
+
+  '@babel/parser@7.24.7':
+    dependencies:
+      '@babel/types': 7.24.7
+
+  '@babel/plugin-bugfix-firefox-class-in-computed-class-key@7.24.7(@babel/core@7.24.7)':
+    dependencies:
+      '@babel/core': 7.24.7
+      '@babel/helper-environment-visitor': 7.24.7
+      '@babel/helper-plugin-utils': 7.24.7
+
+  '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@7.24.7(@babel/core@7.24.7)':
+    dependencies:
+      '@babel/core': 7.24.7
+      '@babel/helper-plugin-utils': 7.24.7
+
+  '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@7.24.7(@babel/core@7.24.7)':
+    dependencies:
+      '@babel/core': 7.24.7
+      '@babel/helper-plugin-utils': 7.24.7
+      '@babel/helper-skip-transparent-expression-wrappers': 7.24.7
+      '@babel/plugin-transform-optional-chaining': 7.24.7(@babel/core@7.24.7)
+    transitivePeerDependencies:
+      - supports-color
+
+  '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@7.24.7(@babel/core@7.24.7)':
+    dependencies:
+      '@babel/core': 7.24.7
+      '@babel/helper-environment-visitor': 7.24.7
+      '@babel/helper-plugin-utils': 7.24.7
+
+  '@babel/plugin-proposal-private-property-in-object@7.21.0-placeholder-for-preset-env.2(@babel/core@7.24.7)':
+    dependencies:
+      '@babel/core': 7.24.7
+
+  '@babel/plugin-syntax-async-generators@7.8.4(@babel/core@7.24.7)':
+    dependencies:
+      '@babel/core': 7.24.7
+      '@babel/helper-plugin-utils': 7.24.7
+
+  '@babel/plugin-syntax-bigint@7.8.3(@babel/core@7.24.7)':
+    dependencies:
+      '@babel/core': 7.24.7
+      '@babel/helper-plugin-utils': 7.24.7
+
+  '@babel/plugin-syntax-class-properties@7.12.13(@babel/core@7.24.7)':
+    dependencies:
+      '@babel/core': 7.24.7
+      '@babel/helper-plugin-utils': 7.24.7
+
+  '@babel/plugin-syntax-class-static-block@7.14.5(@babel/core@7.24.7)':
+    dependencies:
+      '@babel/core': 7.24.7
+      '@babel/helper-plugin-utils': 7.24.7
+
+  '@babel/plugin-syntax-dynamic-import@7.8.3(@babel/core@7.24.7)':
+    dependencies:
+      '@babel/core': 7.24.7
+      '@babel/helper-plugin-utils': 7.24.7
+
+  '@babel/plugin-syntax-export-namespace-from@7.8.3(@babel/core@7.24.7)':
+    dependencies:
+      '@babel/core': 7.24.7
+      '@babel/helper-plugin-utils': 7.24.7
+
+  '@babel/plugin-syntax-import-assertions@7.24.7(@babel/core@7.24.7)':
+    dependencies:
+      '@babel/core': 7.24.7
+      '@babel/helper-plugin-utils': 7.24.7
+
+  '@babel/plugin-syntax-import-attributes@7.24.7(@babel/core@7.24.7)':
+    dependencies:
+      '@babel/core': 7.24.7
+      '@babel/helper-plugin-utils': 7.24.7
+
+  '@babel/plugin-syntax-import-meta@7.10.4(@babel/core@7.24.7)':
+    dependencies:
+      '@babel/core': 7.24.7
+      '@babel/helper-plugin-utils': 7.24.7
+
+  '@babel/plugin-syntax-json-strings@7.8.3(@babel/core@7.24.7)':
+    dependencies:
+      '@babel/core': 7.24.7
+      '@babel/helper-plugin-utils': 7.24.7
+
+  '@babel/plugin-syntax-jsx@7.24.7(@babel/core@7.24.7)':
+    dependencies:
+      '@babel/core': 7.24.7
+      '@babel/helper-plugin-utils': 7.24.7
+
+  '@babel/plugin-syntax-logical-assignment-operators@7.10.4(@babel/core@7.24.7)':
+    dependencies:
+      '@babel/core': 7.24.7
+      '@babel/helper-plugin-utils': 7.24.7
+
+  '@babel/plugin-syntax-nullish-coalescing-operator@7.8.3(@babel/core@7.24.7)':
+    dependencies:
+      '@babel/core': 7.24.7
+      '@babel/helper-plugin-utils': 7.24.7
+
+  '@babel/plugin-syntax-numeric-separator@7.10.4(@babel/core@7.24.7)':
+    dependencies:
+      '@babel/core': 7.24.7
+      '@babel/helper-plugin-utils': 7.24.7
+
+  '@babel/plugin-syntax-object-rest-spread@7.8.3(@babel/core@7.24.7)':
+    dependencies:
+      '@babel/core': 7.24.7
+      '@babel/helper-plugin-utils': 7.24.7
+
+  '@babel/plugin-syntax-optional-catch-binding@7.8.3(@babel/core@7.24.7)':
+    dependencies:
+      '@babel/core': 7.24.7
+      '@babel/helper-plugin-utils': 7.24.7
+
+  '@babel/plugin-syntax-optional-chaining@7.8.3(@babel/core@7.24.7)':
+    dependencies:
+      '@babel/core': 7.24.7
+      '@babel/helper-plugin-utils': 7.24.7
+
+  '@babel/plugin-syntax-private-property-in-object@7.14.5(@babel/core@7.24.7)':
+    dependencies:
+      '@babel/core': 7.24.7
+      '@babel/helper-plugin-utils': 7.24.7
+
+  '@babel/plugin-syntax-top-level-await@7.14.5(@babel/core@7.24.7)':
+    dependencies:
+      '@babel/core': 7.24.7
+      '@babel/helper-plugin-utils': 7.24.7
+
+  '@babel/plugin-syntax-typescript@7.24.7(@babel/core@7.24.7)':
+    dependencies:
+      '@babel/core': 7.24.7
+      '@babel/helper-plugin-utils': 7.24.7
+
+  '@babel/plugin-syntax-unicode-sets-regex@7.18.6(@babel/core@7.24.7)':
+    dependencies:
+      '@babel/core': 7.24.7
+      '@babel/helper-create-regexp-features-plugin': 7.24.7(@babel/core@7.24.7)
+      '@babel/helper-plugin-utils': 7.24.7
+
+  '@babel/plugin-transform-arrow-functions@7.24.7(@babel/core@7.24.7)':
+    dependencies:
+      '@babel/core': 7.24.7
+      '@babel/helper-plugin-utils': 7.24.7
+
+  '@babel/plugin-transform-async-generator-functions@7.24.7(@babel/core@7.24.7)':
+    dependencies:
+      '@babel/core': 7.24.7
+      '@babel/helper-environment-visitor': 7.24.7
+      '@babel/helper-plugin-utils': 7.24.7
+      '@babel/helper-remap-async-to-generator': 7.24.7(@babel/core@7.24.7)
+      '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.24.7)
+    transitivePeerDependencies:
+      - supports-color
+
+  '@babel/plugin-transform-async-to-generator@7.24.7(@babel/core@7.24.7)':
+    dependencies:
+      '@babel/core': 7.24.7
+      '@babel/helper-module-imports': 7.24.7
+      '@babel/helper-plugin-utils': 7.24.7
+      '@babel/helper-remap-async-to-generator': 7.24.7(@babel/core@7.24.7)
+    transitivePeerDependencies:
+      - supports-color
+
+  '@babel/plugin-transform-block-scoped-functions@7.24.7(@babel/core@7.24.7)':
+    dependencies:
+      '@babel/core': 7.24.7
+      '@babel/helper-plugin-utils': 7.24.7
+
+  '@babel/plugin-transform-block-scoping@7.24.7(@babel/core@7.24.7)':
+    dependencies:
+      '@babel/core': 7.24.7
+      '@babel/helper-plugin-utils': 7.24.7
+
+  '@babel/plugin-transform-class-properties@7.24.7(@babel/core@7.24.7)':
+    dependencies:
+      '@babel/core': 7.24.7
+      '@babel/helper-create-class-features-plugin': 7.24.7(@babel/core@7.24.7)
+      '@babel/helper-plugin-utils': 7.24.7
+    transitivePeerDependencies:
+      - supports-color
+
+  '@babel/plugin-transform-class-static-block@7.24.7(@babel/core@7.24.7)':
+    dependencies:
+      '@babel/core': 7.24.7
+      '@babel/helper-create-class-features-plugin': 7.24.7(@babel/core@7.24.7)
+      '@babel/helper-plugin-utils': 7.24.7
+      '@babel/plugin-syntax-class-static-block': 7.14.5(@babel/core@7.24.7)
+    transitivePeerDependencies:
+      - supports-color
+
+  '@babel/plugin-transform-classes@7.24.7(@babel/core@7.24.7)':
+    dependencies:
+      '@babel/core': 7.24.7
+      '@babel/helper-annotate-as-pure': 7.24.7
+      '@babel/helper-compilation-targets': 7.24.7
+      '@babel/helper-environment-visitor': 7.24.7
+      '@babel/helper-function-name': 7.24.7
+      '@babel/helper-plugin-utils': 7.24.7
+      '@babel/helper-replace-supers': 7.24.7(@babel/core@7.24.7)
+      '@babel/helper-split-export-declaration': 7.24.7
+      globals: 11.12.0
+    transitivePeerDependencies:
+      - supports-color
+
+  '@babel/plugin-transform-computed-properties@7.24.7(@babel/core@7.24.7)':
+    dependencies:
+      '@babel/core': 7.24.7
+      '@babel/helper-plugin-utils': 7.24.7
+      '@babel/template': 7.24.7
+
+  '@babel/plugin-transform-destructuring@7.24.7(@babel/core@7.24.7)':
+    dependencies:
+      '@babel/core': 7.24.7
+      '@babel/helper-plugin-utils': 7.24.7
+
+  '@babel/plugin-transform-dotall-regex@7.24.7(@babel/core@7.24.7)':
+    dependencies:
+      '@babel/core': 7.24.7
+      '@babel/helper-create-regexp-features-plugin': 7.24.7(@babel/core@7.24.7)
+      '@babel/helper-plugin-utils': 7.24.7
+
+  '@babel/plugin-transform-duplicate-keys@7.24.7(@babel/core@7.24.7)':
+    dependencies:
+      '@babel/core': 7.24.7
+      '@babel/helper-plugin-utils': 7.24.7
+
+  '@babel/plugin-transform-dynamic-import@7.24.7(@babel/core@7.24.7)':
+    dependencies:
+      '@babel/core': 7.24.7
+      '@babel/helper-plugin-utils': 7.24.7
+      '@babel/plugin-syntax-dynamic-import': 7.8.3(@babel/core@7.24.7)
+
+  '@babel/plugin-transform-exponentiation-operator@7.24.7(@babel/core@7.24.7)':
+    dependencies:
+      '@babel/core': 7.24.7
+      '@babel/helper-builder-binary-assignment-operator-visitor': 7.24.7
+      '@babel/helper-plugin-utils': 7.24.7
+    transitivePeerDependencies:
+      - supports-color
+
+  '@babel/plugin-transform-export-namespace-from@7.24.7(@babel/core@7.24.7)':
+    dependencies:
+      '@babel/core': 7.24.7
+      '@babel/helper-plugin-utils': 7.24.7
+      '@babel/plugin-syntax-export-namespace-from': 7.8.3(@babel/core@7.24.7)
+
+  '@babel/plugin-transform-for-of@7.24.7(@babel/core@7.24.7)':
+    dependencies:
+      '@babel/core': 7.24.7
+      '@babel/helper-plugin-utils': 7.24.7
+      '@babel/helper-skip-transparent-expression-wrappers': 7.24.7
+    transitivePeerDependencies:
+      - supports-color
+
+  '@babel/plugin-transform-function-name@7.24.7(@babel/core@7.24.7)':
+    dependencies:
+      '@babel/core': 7.24.7
+      '@babel/helper-compilation-targets': 7.24.7
+      '@babel/helper-function-name': 7.24.7
+      '@babel/helper-plugin-utils': 7.24.7
+
+  '@babel/plugin-transform-json-strings@7.24.7(@babel/core@7.24.7)':
+    dependencies:
+      '@babel/core': 7.24.7
+      '@babel/helper-plugin-utils': 7.24.7
+      '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.24.7)
+
+  '@babel/plugin-transform-literals@7.24.7(@babel/core@7.24.7)':
+    dependencies:
+      '@babel/core': 7.24.7
+      '@babel/helper-plugin-utils': 7.24.7
+
+  '@babel/plugin-transform-logical-assignment-operators@7.24.7(@babel/core@7.24.7)':
+    dependencies:
+      '@babel/core': 7.24.7
+      '@babel/helper-plugin-utils': 7.24.7
+      '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.24.7)
+
+  '@babel/plugin-transform-member-expression-literals@7.24.7(@babel/core@7.24.7)':
+    dependencies:
+      '@babel/core': 7.24.7
+      '@babel/helper-plugin-utils': 7.24.7
+
+  '@babel/plugin-transform-modules-amd@7.24.7(@babel/core@7.24.7)':
+    dependencies:
+      '@babel/core': 7.24.7
+      '@babel/helper-module-transforms': 7.24.7(@babel/core@7.24.7)
+      '@babel/helper-plugin-utils': 7.24.7
+    transitivePeerDependencies:
+      - supports-color
+
+  '@babel/plugin-transform-modules-commonjs@7.24.7(@babel/core@7.24.7)':
+    dependencies:
+      '@babel/core': 7.24.7
+      '@babel/helper-module-transforms': 7.24.7(@babel/core@7.24.7)
+      '@babel/helper-plugin-utils': 7.24.7
+      '@babel/helper-simple-access': 7.24.7
+    transitivePeerDependencies:
+      - supports-color
+
+  '@babel/plugin-transform-modules-systemjs@7.24.7(@babel/core@7.24.7)':
+    dependencies:
+      '@babel/core': 7.24.7
+      '@babel/helper-hoist-variables': 7.24.7
+      '@babel/helper-module-transforms': 7.24.7(@babel/core@7.24.7)
+      '@babel/helper-plugin-utils': 7.24.7
+      '@babel/helper-validator-identifier': 7.24.7
+    transitivePeerDependencies:
+      - supports-color
+
+  '@babel/plugin-transform-modules-umd@7.24.7(@babel/core@7.24.7)':
+    dependencies:
+      '@babel/core': 7.24.7
+      '@babel/helper-module-transforms': 7.24.7(@babel/core@7.24.7)
+      '@babel/helper-plugin-utils': 7.24.7
+    transitivePeerDependencies:
+      - supports-color
+
+  '@babel/plugin-transform-named-capturing-groups-regex@7.24.7(@babel/core@7.24.7)':
+    dependencies:
+      '@babel/core': 7.24.7
+      '@babel/helper-create-regexp-features-plugin': 7.24.7(@babel/core@7.24.7)
+      '@babel/helper-plugin-utils': 7.24.7
+
+  '@babel/plugin-transform-new-target@7.24.7(@babel/core@7.24.7)':
+    dependencies:
+      '@babel/core': 7.24.7
+      '@babel/helper-plugin-utils': 7.24.7
+
+  '@babel/plugin-transform-nullish-coalescing-operator@7.24.7(@babel/core@7.24.7)':
+    dependencies:
+      '@babel/core': 7.24.7
+      '@babel/helper-plugin-utils': 7.24.7
+      '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.24.7)
+
+  '@babel/plugin-transform-numeric-separator@7.24.7(@babel/core@7.24.7)':
+    dependencies:
+      '@babel/core': 7.24.7
+      '@babel/helper-plugin-utils': 7.24.7
+      '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.24.7)
+
+  '@babel/plugin-transform-object-rest-spread@7.24.7(@babel/core@7.24.7)':
+    dependencies:
+      '@babel/core': 7.24.7
+      '@babel/helper-compilation-targets': 7.24.7
+      '@babel/helper-plugin-utils': 7.24.7
+      '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.24.7)
+      '@babel/plugin-transform-parameters': 7.24.7(@babel/core@7.24.7)
+
+  '@babel/plugin-transform-object-super@7.24.7(@babel/core@7.24.7)':
+    dependencies:
+      '@babel/core': 7.24.7
+      '@babel/helper-plugin-utils': 7.24.7
+      '@babel/helper-replace-supers': 7.24.7(@babel/core@7.24.7)
+    transitivePeerDependencies:
+      - supports-color
+
+  '@babel/plugin-transform-optional-catch-binding@7.24.7(@babel/core@7.24.7)':
+    dependencies:
+      '@babel/core': 7.24.7
+      '@babel/helper-plugin-utils': 7.24.7
+      '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.24.7)
+
+  '@babel/plugin-transform-optional-chaining@7.24.7(@babel/core@7.24.7)':
+    dependencies:
+      '@babel/core': 7.24.7
+      '@babel/helper-plugin-utils': 7.24.7
+      '@babel/helper-skip-transparent-expression-wrappers': 7.24.7
+      '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.24.7)
+    transitivePeerDependencies:
+      - supports-color
+
+  '@babel/plugin-transform-parameters@7.24.7(@babel/core@7.24.7)':
+    dependencies:
+      '@babel/core': 7.24.7
+      '@babel/helper-plugin-utils': 7.24.7
+
+  '@babel/plugin-transform-private-methods@7.24.7(@babel/core@7.24.7)':
+    dependencies:
+      '@babel/core': 7.24.7
+      '@babel/helper-create-class-features-plugin': 7.24.7(@babel/core@7.24.7)
+      '@babel/helper-plugin-utils': 7.24.7
+    transitivePeerDependencies:
+      - supports-color
+
+  '@babel/plugin-transform-private-property-in-object@7.24.7(@babel/core@7.24.7)':
+    dependencies:
+      '@babel/core': 7.24.7
+      '@babel/helper-annotate-as-pure': 7.24.7
+      '@babel/helper-create-class-features-plugin': 7.24.7(@babel/core@7.24.7)
+      '@babel/helper-plugin-utils': 7.24.7
+      '@babel/plugin-syntax-private-property-in-object': 7.14.5(@babel/core@7.24.7)
+    transitivePeerDependencies:
+      - supports-color
+
+  '@babel/plugin-transform-property-literals@7.24.7(@babel/core@7.24.7)':
+    dependencies:
+      '@babel/core': 7.24.7
+      '@babel/helper-plugin-utils': 7.24.7
+
+  '@babel/plugin-transform-regenerator@7.24.7(@babel/core@7.24.7)':
+    dependencies:
+      '@babel/core': 7.24.7
+      '@babel/helper-plugin-utils': 7.24.7
+      regenerator-transform: 0.15.2
+
+  '@babel/plugin-transform-reserved-words@7.24.7(@babel/core@7.24.7)':
+    dependencies:
+      '@babel/core': 7.24.7
+      '@babel/helper-plugin-utils': 7.24.7
+
+  '@babel/plugin-transform-shorthand-properties@7.24.7(@babel/core@7.24.7)':
+    dependencies:
+      '@babel/core': 7.24.7
+      '@babel/helper-plugin-utils': 7.24.7
+
+  '@babel/plugin-transform-spread@7.24.7(@babel/core@7.24.7)':
+    dependencies:
+      '@babel/core': 7.24.7
+      '@babel/helper-plugin-utils': 7.24.7
+      '@babel/helper-skip-transparent-expression-wrappers': 7.24.7
+    transitivePeerDependencies:
+      - supports-color
+
+  '@babel/plugin-transform-sticky-regex@7.24.7(@babel/core@7.24.7)':
+    dependencies:
+      '@babel/core': 7.24.7
+      '@babel/helper-plugin-utils': 7.24.7
+
+  '@babel/plugin-transform-template-literals@7.24.7(@babel/core@7.24.7)':
+    dependencies:
+      '@babel/core': 7.24.7
+      '@babel/helper-plugin-utils': 7.24.7
+
+  '@babel/plugin-transform-typeof-symbol@7.24.7(@babel/core@7.24.7)':
+    dependencies:
+      '@babel/core': 7.24.7
+      '@babel/helper-plugin-utils': 7.24.7
+
+  '@babel/plugin-transform-typescript@7.24.7(@babel/core@7.24.7)':
+    dependencies:
+      '@babel/core': 7.24.7
+      '@babel/helper-annotate-as-pure': 7.24.7
+      '@babel/helper-create-class-features-plugin': 7.24.7(@babel/core@7.24.7)
+      '@babel/helper-plugin-utils': 7.24.7
+      '@babel/plugin-syntax-typescript': 7.24.7(@babel/core@7.24.7)
+    transitivePeerDependencies:
+      - supports-color
+
+  '@babel/plugin-transform-unicode-escapes@7.24.7(@babel/core@7.24.7)':
+    dependencies:
+      '@babel/core': 7.24.7
+      '@babel/helper-plugin-utils': 7.24.7
+
+  '@babel/plugin-transform-unicode-property-regex@7.24.7(@babel/core@7.24.7)':
+    dependencies:
+      '@babel/core': 7.24.7
+      '@babel/helper-create-regexp-features-plugin': 7.24.7(@babel/core@7.24.7)
+      '@babel/helper-plugin-utils': 7.24.7
+
+  '@babel/plugin-transform-unicode-regex@7.24.7(@babel/core@7.24.7)':
+    dependencies:
+      '@babel/core': 7.24.7
+      '@babel/helper-create-regexp-features-plugin': 7.24.7(@babel/core@7.24.7)
+      '@babel/helper-plugin-utils': 7.24.7
+
+  '@babel/plugin-transform-unicode-sets-regex@7.24.7(@babel/core@7.24.7)':
+    dependencies:
+      '@babel/core': 7.24.7
+      '@babel/helper-create-regexp-features-plugin': 7.24.7(@babel/core@7.24.7)
+      '@babel/helper-plugin-utils': 7.24.7
+
+  '@babel/preset-env@7.24.7(@babel/core@7.24.7)':
+    dependencies:
+      '@babel/compat-data': 7.24.7
+      '@babel/core': 7.24.7
+      '@babel/helper-compilation-targets': 7.24.7
+      '@babel/helper-plugin-utils': 7.24.7
+      '@babel/helper-validator-option': 7.24.7
+      '@babel/plugin-bugfix-firefox-class-in-computed-class-key': 7.24.7(@babel/core@7.24.7)
+      '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression': 7.24.7(@babel/core@7.24.7)
+      '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining': 7.24.7(@babel/core@7.24.7)
+      '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly': 7.24.7(@babel/core@7.24.7)
+      '@babel/plugin-proposal-private-property-in-object': 7.21.0-placeholder-for-preset-env.2(@babel/core@7.24.7)
+      '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.24.7)
+      '@babel/plugin-syntax-class-properties': 7.12.13(@babel/core@7.24.7)
+      '@babel/plugin-syntax-class-static-block': 7.14.5(@babel/core@7.24.7)
+      '@babel/plugin-syntax-dynamic-import': 7.8.3(@babel/core@7.24.7)
+      '@babel/plugin-syntax-export-namespace-from': 7.8.3(@babel/core@7.24.7)
+      '@babel/plugin-syntax-import-assertions': 7.24.7(@babel/core@7.24.7)
+      '@babel/plugin-syntax-import-attributes': 7.24.7(@babel/core@7.24.7)
+      '@babel/plugin-syntax-import-meta': 7.10.4(@babel/core@7.24.7)
+      '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.24.7)
+      '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.24.7)
+      '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.24.7)
+      '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.24.7)
+      '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.24.7)
+      '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.24.7)
+      '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.24.7)
+      '@babel/plugin-syntax-private-property-in-object': 7.14.5(@babel/core@7.24.7)
+      '@babel/plugin-syntax-top-level-await': 7.14.5(@babel/core@7.24.7)
+      '@babel/plugin-syntax-unicode-sets-regex': 7.18.6(@babel/core@7.24.7)
+      '@babel/plugin-transform-arrow-functions': 7.24.7(@babel/core@7.24.7)
+      '@babel/plugin-transform-async-generator-functions': 7.24.7(@babel/core@7.24.7)
+      '@babel/plugin-transform-async-to-generator': 7.24.7(@babel/core@7.24.7)
+      '@babel/plugin-transform-block-scoped-functions': 7.24.7(@babel/core@7.24.7)
+      '@babel/plugin-transform-block-scoping': 7.24.7(@babel/core@7.24.7)
+      '@babel/plugin-transform-class-properties': 7.24.7(@babel/core@7.24.7)
+      '@babel/plugin-transform-class-static-block': 7.24.7(@babel/core@7.24.7)
+      '@babel/plugin-transform-classes': 7.24.7(@babel/core@7.24.7)
+      '@babel/plugin-transform-computed-properties': 7.24.7(@babel/core@7.24.7)
+      '@babel/plugin-transform-destructuring': 7.24.7(@babel/core@7.24.7)
+      '@babel/plugin-transform-dotall-regex': 7.24.7(@babel/core@7.24.7)
+      '@babel/plugin-transform-duplicate-keys': 7.24.7(@babel/core@7.24.7)
+      '@babel/plugin-transform-dynamic-import': 7.24.7(@babel/core@7.24.7)
+      '@babel/plugin-transform-exponentiation-operator': 7.24.7(@babel/core@7.24.7)
+      '@babel/plugin-transform-export-namespace-from': 7.24.7(@babel/core@7.24.7)
+      '@babel/plugin-transform-for-of': 7.24.7(@babel/core@7.24.7)
+      '@babel/plugin-transform-function-name': 7.24.7(@babel/core@7.24.7)
+      '@babel/plugin-transform-json-strings': 7.24.7(@babel/core@7.24.7)
+      '@babel/plugin-transform-literals': 7.24.7(@babel/core@7.24.7)
+      '@babel/plugin-transform-logical-assignment-operators': 7.24.7(@babel/core@7.24.7)
+      '@babel/plugin-transform-member-expression-literals': 7.24.7(@babel/core@7.24.7)
+      '@babel/plugin-transform-modules-amd': 7.24.7(@babel/core@7.24.7)
+      '@babel/plugin-transform-modules-commonjs': 7.24.7(@babel/core@7.24.7)
+      '@babel/plugin-transform-modules-systemjs': 7.24.7(@babel/core@7.24.7)
+      '@babel/plugin-transform-modules-umd': 7.24.7(@babel/core@7.24.7)
+      '@babel/plugin-transform-named-capturing-groups-regex': 7.24.7(@babel/core@7.24.7)
+      '@babel/plugin-transform-new-target': 7.24.7(@babel/core@7.24.7)
+      '@babel/plugin-transform-nullish-coalescing-operator': 7.24.7(@babel/core@7.24.7)
+      '@babel/plugin-transform-numeric-separator': 7.24.7(@babel/core@7.24.7)
+      '@babel/plugin-transform-object-rest-spread': 7.24.7(@babel/core@7.24.7)
+      '@babel/plugin-transform-object-super': 7.24.7(@babel/core@7.24.7)
+      '@babel/plugin-transform-optional-catch-binding': 7.24.7(@babel/core@7.24.7)
+      '@babel/plugin-transform-optional-chaining': 7.24.7(@babel/core@7.24.7)
+      '@babel/plugin-transform-parameters': 7.24.7(@babel/core@7.24.7)
+      '@babel/plugin-transform-private-methods': 7.24.7(@babel/core@7.24.7)
+      '@babel/plugin-transform-private-property-in-object': 7.24.7(@babel/core@7.24.7)
+      '@babel/plugin-transform-property-literals': 7.24.7(@babel/core@7.24.7)
+      '@babel/plugin-transform-regenerator': 7.24.7(@babel/core@7.24.7)
+      '@babel/plugin-transform-reserved-words': 7.24.7(@babel/core@7.24.7)
+      '@babel/plugin-transform-shorthand-properties': 7.24.7(@babel/core@7.24.7)
+      '@babel/plugin-transform-spread': 7.24.7(@babel/core@7.24.7)
+      '@babel/plugin-transform-sticky-regex': 7.24.7(@babel/core@7.24.7)
+      '@babel/plugin-transform-template-literals': 7.24.7(@babel/core@7.24.7)
+      '@babel/plugin-transform-typeof-symbol': 7.24.7(@babel/core@7.24.7)
+      '@babel/plugin-transform-unicode-escapes': 7.24.7(@babel/core@7.24.7)
+      '@babel/plugin-transform-unicode-property-regex': 7.24.7(@babel/core@7.24.7)
+      '@babel/plugin-transform-unicode-regex': 7.24.7(@babel/core@7.24.7)
+      '@babel/plugin-transform-unicode-sets-regex': 7.24.7(@babel/core@7.24.7)
+      '@babel/preset-modules': 0.1.6-no-external-plugins(@babel/core@7.24.7)
+      babel-plugin-polyfill-corejs2: 0.4.11(@babel/core@7.24.7)
+      babel-plugin-polyfill-corejs3: 0.10.4(@babel/core@7.24.7)
+      babel-plugin-polyfill-regenerator: 0.6.2(@babel/core@7.24.7)
+      core-js-compat: 3.37.1
+      semver: 6.3.1
+    transitivePeerDependencies:
+      - supports-color
+
+  '@babel/preset-modules@0.1.6-no-external-plugins(@babel/core@7.24.7)':
+    dependencies:
+      '@babel/core': 7.24.7
+      '@babel/helper-plugin-utils': 7.24.7
+      '@babel/types': 7.24.7
+      esutils: 2.0.3
+
+  '@babel/regjsgen@0.8.0': {}
+
+  '@babel/runtime@7.24.7':
+    dependencies:
+      regenerator-runtime: 0.14.1
+
+  '@babel/template@7.24.7':
+    dependencies:
+      '@babel/code-frame': 7.24.7
+      '@babel/parser': 7.24.7
+      '@babel/types': 7.24.7
+
+  '@babel/traverse@7.24.7':
+    dependencies:
+      '@babel/code-frame': 7.24.7
+      '@babel/generator': 7.24.7
+      '@babel/helper-environment-visitor': 7.24.7
+      '@babel/helper-function-name': 7.24.7
+      '@babel/helper-hoist-variables': 7.24.7
+      '@babel/helper-split-export-declaration': 7.24.7
+      '@babel/parser': 7.24.7
+      '@babel/types': 7.24.7
+      debug: 4.3.5
+      globals: 11.12.0
+    transitivePeerDependencies:
+      - supports-color
+
+  '@babel/types@7.24.7':
+    dependencies:
+      '@babel/helper-string-parser': 7.24.7
+      '@babel/helper-validator-identifier': 7.24.7
+      to-fast-properties: 2.0.0
+
+  '@bcoe/v8-coverage@0.2.3': {}
+
+  '@dcloudio/types@3.4.8': {}
+
+  '@dcloudio/uni-app-plus@3.0.0-4000720240327002(postcss@8.4.38)(rollup@3.29.4)(vite@4.3.5(@types/node@20.14.9)(sass@1.77.6)(terser@5.31.1))(vue@3.4.30(typescript@4.9.5))':
+    dependencies:
+      '@dcloudio/uni-app-uts': 3.0.0-4000720240327002(postcss@8.4.38)(rollup@3.29.4)(vue@3.4.30(typescript@4.9.5))
+      '@dcloudio/uni-app-vite': 3.0.0-4000720240327002(postcss@8.4.38)(rollup@3.29.4)(vite@4.3.5(@types/node@20.14.9)(sass@1.77.6)(terser@5.31.1))(vue@3.4.30(typescript@4.9.5))
+      '@dcloudio/uni-app-vue': 3.0.0-4000720240327002
+      debug: 4.3.5
+      fs-extra: 10.1.0
+      licia: 1.41.0
+      postcss-selector-parser: 6.1.0
+    transitivePeerDependencies:
+      - '@nuxt/kit'
+      - '@vueuse/core'
+      - postcss
+      - rollup
+      - supports-color
+      - ts-node
+      - vite
+      - vue
+
+  '@dcloudio/uni-app-uts@3.0.0-4000720240327002(postcss@8.4.38)(rollup@3.29.4)(vue@3.4.30(typescript@4.9.5))':
+    dependencies:
+      '@babel/parser': 7.24.7
+      '@babel/types': 7.24.7
+      '@dcloudio/uni-cli-shared': 3.0.0-4000720240327002(postcss@8.4.38)(rollup@3.29.4)(vue@3.4.30(typescript@4.9.5))
+      '@dcloudio/uni-i18n': 3.0.0-4000720240327002
+      '@dcloudio/uni-nvue-styler': 3.0.0-4000720240327002
+      '@dcloudio/uni-shared': 3.0.0-4000720240327002
+      '@jridgewell/gen-mapping': 0.3.5
+      '@jridgewell/trace-mapping': 0.3.25
+      '@rollup/pluginutils': 4.2.1
+      '@vue/compiler-core': 3.3.11
+      '@vue/compiler-dom': 3.3.11
+      '@vue/compiler-sfc': 3.3.11
+      '@vue/shared': 3.3.11
+      debug: 4.3.5
+      es-module-lexer: 1.5.4
+      estree-walker: 2.0.2
+      fs-extra: 10.1.0
+      magic-string: 0.30.10
+      picocolors: 1.0.1
+      source-map-js: 1.2.0
+      unplugin-auto-import: 0.16.7(rollup@3.29.4)
+    transitivePeerDependencies:
+      - '@nuxt/kit'
+      - '@vueuse/core'
+      - postcss
+      - rollup
+      - supports-color
+      - ts-node
+      - vue
+
+  '@dcloudio/uni-app-vite@3.0.0-4000720240327002(postcss@8.4.38)(rollup@3.29.4)(vite@4.3.5(@types/node@20.14.9)(sass@1.77.6)(terser@5.31.1))(vue@3.4.30(typescript@4.9.5))':
+    dependencies:
+      '@dcloudio/uni-cli-shared': 3.0.0-4000720240327002(postcss@8.4.38)(rollup@3.29.4)(vue@3.4.30(typescript@4.9.5))
+      '@dcloudio/uni-i18n': 3.0.0-4000720240327002
+      '@dcloudio/uni-nvue-styler': 3.0.0-4000720240327002
+      '@dcloudio/uni-shared': 3.0.0-4000720240327002
+      '@rollup/pluginutils': 4.2.1
+      '@vitejs/plugin-vue': 4.6.2(vite@4.3.5(@types/node@20.14.9)(sass@1.77.6)(terser@5.31.1))(vue@3.4.30(typescript@4.9.5))
+      '@vue/compiler-dom': 3.3.11
+      '@vue/compiler-sfc': 3.3.11
+      debug: 4.3.5
+      fs-extra: 10.1.0
+      picocolors: 1.0.1
+    transitivePeerDependencies:
+      - '@nuxt/kit'
+      - '@vueuse/core'
+      - postcss
+      - rollup
+      - supports-color
+      - ts-node
+      - vite
+      - vue
+
+  '@dcloudio/uni-app-vue@3.0.0-4000720240327002': {}
+
+  '@dcloudio/uni-app@3.0.0-4000720240327002(@dcloudio/types@3.4.8)(postcss@8.4.38)(rollup@3.29.4)(vue@3.4.30(typescript@4.9.5))':
+    dependencies:
+      '@dcloudio/types': 3.4.8
+      '@dcloudio/uni-cloud': 3.0.0-4000720240327002(postcss@8.4.38)(rollup@3.29.4)(vue@3.4.30(typescript@4.9.5))
+      '@dcloudio/uni-components': 3.0.0-4000720240327002(postcss@8.4.38)(rollup@3.29.4)(vue@3.4.30(typescript@4.9.5))
+      '@dcloudio/uni-i18n': 3.0.0-4000720240327002
+      '@dcloudio/uni-push': 3.0.0-4000720240327002(postcss@8.4.38)(rollup@3.29.4)(vue@3.4.30(typescript@4.9.5))
+      '@dcloudio/uni-shared': 3.0.0-4000720240327002
+      '@dcloudio/uni-stat': 3.0.0-4000720240327002(postcss@8.4.38)(rollup@3.29.4)(vue@3.4.30(typescript@4.9.5))
+      '@vue/shared': 3.3.11
+    transitivePeerDependencies:
+      - '@nuxt/kit'
+      - '@vueuse/core'
+      - postcss
+      - rollup
+      - supports-color
+      - ts-node
+      - vue
+
+  '@dcloudio/uni-automator@3.0.0-4000720240327002(jest-environment-node@27.5.1)(jest@27.0.4)(postcss@8.4.38)(rollup@3.29.4)(vue@3.4.30(typescript@4.9.5))':
+    dependencies:
+      '@dcloudio/uni-cli-shared': 3.0.0-4000720240327002(postcss@8.4.38)(rollup@3.29.4)(vue@3.4.30(typescript@4.9.5))
+      address: 1.2.2
+      cross-env: 7.0.3
+      debug: 4.3.5
+      default-gateway: 6.0.3
+      fs-extra: 10.1.0
+      jest: 27.0.4
+      jest-environment-node: 27.5.1
+      jsonc-parser: 3.3.1
+      licia: 1.41.0
+      merge: 2.1.1
+      qrcode-reader: 1.0.4
+      qrcode-terminal: 0.12.0
+      ws: 8.17.1
+    transitivePeerDependencies:
+      - '@nuxt/kit'
+      - '@vueuse/core'
+      - bufferutil
+      - postcss
+      - rollup
+      - supports-color
+      - ts-node
+      - utf-8-validate
+      - vue
+
+  '@dcloudio/uni-cli-shared@3.0.0-4000720240327002(postcss@8.4.38)(rollup@3.29.4)(vue@3.4.30(typescript@4.9.5))':
+    dependencies:
+      '@ampproject/remapping': 2.3.0
+      '@babel/code-frame': 7.24.7
+      '@babel/core': 7.24.7
+      '@babel/parser': 7.24.7
+      '@babel/types': 7.24.7
+      '@dcloudio/uni-i18n': 3.0.0-4000720240327002
+      '@dcloudio/uni-shared': 3.0.0-4000720240327002
+      '@intlify/core-base': 9.1.9
+      '@intlify/shared': 9.1.9
+      '@intlify/vue-devtools': 9.1.9
+      '@rollup/pluginutils': 4.2.1
+      '@vue/compiler-core': 3.3.11
+      '@vue/compiler-dom': 3.3.11
+      '@vue/compiler-sfc': 3.3.11
+      '@vue/server-renderer': 3.3.11(vue@3.4.30(typescript@4.9.5))
+      '@vue/shared': 3.3.11
+      autoprefixer: 10.4.19(postcss@8.4.38)
+      base64url: 3.0.1
+      chokidar: 3.6.0
+      compare-versions: 3.6.0
+      debug: 4.3.5
+      es-module-lexer: 1.5.4
+      esbuild: 0.17.19
+      estree-walker: 2.0.2
+      fast-glob: 3.3.2
+      fs-extra: 10.1.0
+      hash-sum: 2.0.0
+      jsonc-parser: 3.3.1
+      lines-and-columns: 2.0.4
+      magic-string: 0.30.10
+      merge: 2.1.1
+      mime: 3.0.0
+      module-alias: 2.2.3
+      os-locale-s-fix: 1.0.8-fix-1
+      picocolors: 1.0.1
+      postcss-import: 14.1.0(postcss@8.4.38)
+      postcss-load-config: 3.1.4(postcss@8.4.38)
+      postcss-modules: 4.3.1(postcss@8.4.38)
+      postcss-selector-parser: 6.1.0
+      resolve: 1.22.8
+      tapable: 2.2.1
+      unplugin-auto-import: 0.16.7(rollup@3.29.4)
+      xregexp: 3.1.0
+    transitivePeerDependencies:
+      - '@nuxt/kit'
+      - '@vueuse/core'
+      - postcss
+      - rollup
+      - supports-color
+      - ts-node
+      - vue
+
+  '@dcloudio/uni-cloud@3.0.0-4000720240327002(postcss@8.4.38)(rollup@3.29.4)(vue@3.4.30(typescript@4.9.5))':
+    dependencies:
+      '@dcloudio/uni-cli-shared': 3.0.0-4000720240327002(postcss@8.4.38)(rollup@3.29.4)(vue@3.4.30(typescript@4.9.5))
+      '@dcloudio/uni-i18n': 3.0.0-4000720240327002
+      '@dcloudio/uni-shared': 3.0.0-4000720240327002
+      '@vue/shared': 3.3.11
+      fast-glob: 3.3.2
+    transitivePeerDependencies:
+      - '@nuxt/kit'
+      - '@vueuse/core'
+      - postcss
+      - rollup
+      - supports-color
+      - ts-node
+      - vue
+
+  '@dcloudio/uni-components@3.0.0-4000720240327002(postcss@8.4.38)(rollup@3.29.4)(vue@3.4.30(typescript@4.9.5))':
+    dependencies:
+      '@dcloudio/uni-cloud': 3.0.0-4000720240327002(postcss@8.4.38)(rollup@3.29.4)(vue@3.4.30(typescript@4.9.5))
+      '@dcloudio/uni-h5': 3.0.0-4000720240327002(postcss@8.4.38)(rollup@3.29.4)(vue@3.4.30(typescript@4.9.5))
+      '@dcloudio/uni-i18n': 3.0.0-4000720240327002
+    transitivePeerDependencies:
+      - '@nuxt/kit'
+      - '@vueuse/core'
+      - postcss
+      - rollup
+      - supports-color
+      - ts-node
+      - vue
+
+  '@dcloudio/uni-h5-vite@3.0.0-4000720240327002(postcss@8.4.38)(rollup@3.29.4)(vue@3.4.30(typescript@4.9.5))':
+    dependencies:
+      '@dcloudio/uni-cli-shared': 3.0.0-4000720240327002(postcss@8.4.38)(rollup@3.29.4)(vue@3.4.30(typescript@4.9.5))
+      '@dcloudio/uni-shared': 3.0.0-4000720240327002
+      '@rollup/pluginutils': 4.2.1
+      '@vue/compiler-dom': 3.3.11
+      '@vue/compiler-sfc': 3.3.11
+      '@vue/server-renderer': 3.3.11(vue@3.4.30(typescript@4.9.5))
+      '@vue/shared': 3.3.11
+      debug: 4.3.5
+      fs-extra: 10.1.0
+      mime: 3.0.0
+      module-alias: 2.2.3
+    transitivePeerDependencies:
+      - '@nuxt/kit'
+      - '@vueuse/core'
+      - postcss
+      - rollup
+      - supports-color
+      - ts-node
+      - vue
+
+  '@dcloudio/uni-h5-vue@3.0.0-4000720240327002(vue@3.4.30(typescript@4.9.5))':
+    dependencies:
+      '@dcloudio/uni-shared': 3.0.0-4000720240327002
+      '@vue/server-renderer': 3.3.11(vue@3.4.30(typescript@4.9.5))
+    transitivePeerDependencies:
+      - vue
+
+  '@dcloudio/uni-h5@3.0.0-4000720240327002(postcss@8.4.38)(rollup@3.29.4)(vue@3.4.30(typescript@4.9.5))':
+    dependencies:
+      '@dcloudio/uni-h5-vite': 3.0.0-4000720240327002(postcss@8.4.38)(rollup@3.29.4)(vue@3.4.30(typescript@4.9.5))
+      '@dcloudio/uni-h5-vue': 3.0.0-4000720240327002(vue@3.4.30(typescript@4.9.5))
+      '@dcloudio/uni-i18n': 3.0.0-4000720240327002
+      '@dcloudio/uni-shared': 3.0.0-4000720240327002
+      '@vue/server-renderer': 3.3.11(vue@3.4.30(typescript@4.9.5))
+      '@vue/shared': 3.3.11
+      debug: 4.3.5
+      localstorage-polyfill: 1.0.1
+      postcss-selector-parser: 6.1.0
+      safe-area-insets: 1.4.1
+      vue-router: 4.4.0(vue@3.4.30(typescript@4.9.5))
+      xmlhttprequest: 1.8.0
+    transitivePeerDependencies:
+      - '@nuxt/kit'
+      - '@vueuse/core'
+      - postcss
+      - rollup
+      - supports-color
+      - ts-node
+      - vue
+
+  '@dcloudio/uni-i18n@3.0.0-4000720240327002': {}
+
+  '@dcloudio/uni-mp-alipay@3.0.0-4000720240327002(postcss@8.4.38)(rollup@3.29.4)(vue@3.4.30(typescript@4.9.5))':
+    dependencies:
+      '@dcloudio/uni-cli-shared': 3.0.0-4000720240327002(postcss@8.4.38)(rollup@3.29.4)(vue@3.4.30(typescript@4.9.5))
+      '@dcloudio/uni-mp-vite': 3.0.0-4000720240327002(postcss@8.4.38)(rollup@3.29.4)(vue@3.4.30(typescript@4.9.5))
+      '@dcloudio/uni-mp-vue': 3.0.0-4000720240327002
+      '@dcloudio/uni-shared': 3.0.0-4000720240327002
+      '@vue/compiler-core': 3.3.11
+      '@vue/shared': 3.3.11
+    transitivePeerDependencies:
+      - '@nuxt/kit'
+      - '@vueuse/core'
+      - postcss
+      - rollup
+      - supports-color
+      - ts-node
+      - vue
+
+  '@dcloudio/uni-mp-compiler@3.0.0-4000720240327002(postcss@8.4.38)(rollup@3.29.4)(vue@3.4.30(typescript@4.9.5))':
+    dependencies:
+      '@babel/generator': 7.24.7
+      '@babel/parser': 7.24.7
+      '@babel/types': 7.24.7
+      '@dcloudio/uni-cli-shared': 3.0.0-4000720240327002(postcss@8.4.38)(rollup@3.29.4)(vue@3.4.30(typescript@4.9.5))
+      '@dcloudio/uni-shared': 3.0.0-4000720240327002
+      '@vue/compiler-core': 3.3.11
+      '@vue/compiler-dom': 3.3.11
+      '@vue/shared': 3.3.11
+      estree-walker: 2.0.2
+    transitivePeerDependencies:
+      - '@nuxt/kit'
+      - '@vueuse/core'
+      - postcss
+      - rollup
+      - supports-color
+      - ts-node
+      - vue
+
+  '@dcloudio/uni-mp-vite@3.0.0-4000720240327002(postcss@8.4.38)(rollup@3.29.4)(vue@3.4.30(typescript@4.9.5))':
+    dependencies:
+      '@dcloudio/uni-cli-shared': 3.0.0-4000720240327002(postcss@8.4.38)(rollup@3.29.4)(vue@3.4.30(typescript@4.9.5))
+      '@dcloudio/uni-i18n': 3.0.0-4000720240327002
+      '@dcloudio/uni-mp-compiler': 3.0.0-4000720240327002(postcss@8.4.38)(rollup@3.29.4)(vue@3.4.30(typescript@4.9.5))
+      '@dcloudio/uni-mp-vue': 3.0.0-4000720240327002
+      '@dcloudio/uni-shared': 3.0.0-4000720240327002
+      '@vue/compiler-sfc': 3.3.11
+      '@vue/shared': 3.3.11
+      debug: 4.3.5
+    transitivePeerDependencies:
+      - '@nuxt/kit'
+      - '@vueuse/core'
+      - postcss
+      - rollup
+      - supports-color
+      - ts-node
+      - vue
+
+  '@dcloudio/uni-mp-vue@3.0.0-4000720240327002':
+    dependencies:
+      '@dcloudio/uni-shared': 3.0.0-4000720240327002
+      '@vue/shared': 3.3.11
+
+  '@dcloudio/uni-mp-weixin@3.0.0-4000720240327002(postcss@8.4.38)(rollup@3.29.4)(vue@3.4.30(typescript@4.9.5))':
+    dependencies:
+      '@dcloudio/uni-cli-shared': 3.0.0-4000720240327002(postcss@8.4.38)(rollup@3.29.4)(vue@3.4.30(typescript@4.9.5))
+      '@dcloudio/uni-mp-vite': 3.0.0-4000720240327002(postcss@8.4.38)(rollup@3.29.4)(vue@3.4.30(typescript@4.9.5))
+      '@dcloudio/uni-mp-vue': 3.0.0-4000720240327002
+      '@dcloudio/uni-shared': 3.0.0-4000720240327002
+      '@vue/shared': 3.3.11
+      jimp: 0.10.3
+      licia: 1.41.0
+      qrcode-reader: 1.0.4
+      qrcode-terminal: 0.12.0
+      ws: 8.17.1
+    transitivePeerDependencies:
+      - '@nuxt/kit'
+      - '@vueuse/core'
+      - bufferutil
+      - postcss
+      - rollup
+      - supports-color
+      - ts-node
+      - utf-8-validate
+      - vue
+
+  '@dcloudio/uni-nvue-styler@3.0.0-4000720240327002':
+    dependencies:
+      parse-css-font: 4.0.0
+      postcss: 8.4.38
+
+  '@dcloudio/uni-push@3.0.0-4000720240327002(postcss@8.4.38)(rollup@3.29.4)(vue@3.4.30(typescript@4.9.5))':
+    dependencies:
+      '@dcloudio/uni-cli-shared': 3.0.0-4000720240327002(postcss@8.4.38)(rollup@3.29.4)(vue@3.4.30(typescript@4.9.5))
+    transitivePeerDependencies:
+      - '@nuxt/kit'
+      - '@vueuse/core'
+      - postcss
+      - rollup
+      - supports-color
+      - ts-node
+      - vue
+
+  '@dcloudio/uni-shared@3.0.0-4000720240327002':
+    dependencies:
+      '@vue/shared': 3.3.11
+
+  '@dcloudio/uni-stacktracey@3.0.0-4000720240327002': {}
+
+  '@dcloudio/uni-stat@3.0.0-4000720240327002(postcss@8.4.38)(rollup@3.29.4)(vue@3.4.30(typescript@4.9.5))':
+    dependencies:
+      '@dcloudio/uni-cli-shared': 3.0.0-4000720240327002(postcss@8.4.38)(rollup@3.29.4)(vue@3.4.30(typescript@4.9.5))
+      '@dcloudio/uni-shared': 3.0.0-4000720240327002
+      debug: 4.3.5
+    transitivePeerDependencies:
+      - '@nuxt/kit'
+      - '@vueuse/core'
+      - postcss
+      - rollup
+      - supports-color
+      - ts-node
+      - vue
+
+  '@dcloudio/uni-ui@1.5.5': {}
+
+  '@dcloudio/vite-plugin-uni@3.0.0-4000720240327002(postcss@8.4.38)(rollup@3.29.4)(vite@4.3.5(@types/node@20.14.9)(sass@1.77.6)(terser@5.31.1))(vue@3.4.30(typescript@4.9.5))':
+    dependencies:
+      '@babel/core': 7.24.7
+      '@babel/plugin-syntax-import-meta': 7.10.4(@babel/core@7.24.7)
+      '@babel/plugin-transform-typescript': 7.24.7(@babel/core@7.24.7)
+      '@dcloudio/uni-cli-shared': 3.0.0-4000720240327002(postcss@8.4.38)(rollup@3.29.4)(vue@3.4.30(typescript@4.9.5))
+      '@dcloudio/uni-shared': 3.0.0-4000720240327002
+      '@rollup/pluginutils': 4.2.1
+      '@vitejs/plugin-legacy': 4.1.1(terser@5.31.1)(vite@4.3.5(@types/node@20.14.9)(sass@1.77.6)(terser@5.31.1))
+      '@vitejs/plugin-vue': 4.6.2(vite@4.3.5(@types/node@20.14.9)(sass@1.77.6)(terser@5.31.1))(vue@3.4.30(typescript@4.9.5))
+      '@vitejs/plugin-vue-jsx': 3.1.0(vite@4.3.5(@types/node@20.14.9)(sass@1.77.6)(terser@5.31.1))(vue@3.4.30(typescript@4.9.5))
+      '@vue/compiler-core': 3.3.11
+      '@vue/compiler-dom': 3.3.11
+      '@vue/compiler-sfc': 3.3.11
+      '@vue/shared': 3.3.11
+      cac: 6.7.9
+      debug: 4.3.5
+      estree-walker: 2.0.2
+      express: 4.19.2
+      fast-glob: 3.3.2
+      fs-extra: 10.1.0
+      hash-sum: 2.0.0
+      jsonc-parser: 3.3.1
+      magic-string: 0.30.10
+      picocolors: 1.0.1
+      terser: 5.31.1
+      unplugin-auto-import: 0.16.7(rollup@3.29.4)
+      vite: 4.3.5(@types/node@20.14.9)(sass@1.77.6)(terser@5.31.1)
+    transitivePeerDependencies:
+      - '@nuxt/kit'
+      - '@vueuse/core'
+      - postcss
+      - rollup
+      - supports-color
+      - ts-node
+      - vue
+
+  '@esbuild/android-arm64@0.17.19':
+    optional: true
+
+  '@esbuild/android-arm@0.17.19':
+    optional: true
+
+  '@esbuild/android-x64@0.17.19':
+    optional: true
+
+  '@esbuild/darwin-arm64@0.17.19':
+    optional: true
+
+  '@esbuild/darwin-x64@0.17.19':
+    optional: true
+
+  '@esbuild/freebsd-arm64@0.17.19':
+    optional: true
+
+  '@esbuild/freebsd-x64@0.17.19':
+    optional: true
+
+  '@esbuild/linux-arm64@0.17.19':
+    optional: true
+
+  '@esbuild/linux-arm@0.17.19':
+    optional: true
+
+  '@esbuild/linux-ia32@0.17.19':
+    optional: true
+
+  '@esbuild/linux-loong64@0.17.19':
+    optional: true
+
+  '@esbuild/linux-mips64el@0.17.19':
+    optional: true
+
+  '@esbuild/linux-ppc64@0.17.19':
+    optional: true
+
+  '@esbuild/linux-riscv64@0.17.19':
+    optional: true
+
+  '@esbuild/linux-s390x@0.17.19':
+    optional: true
+
+  '@esbuild/linux-x64@0.17.19':
+    optional: true
+
+  '@esbuild/netbsd-x64@0.17.19':
+    optional: true
+
+  '@esbuild/openbsd-x64@0.17.19':
+    optional: true
+
+  '@esbuild/sunos-x64@0.17.19':
+    optional: true
+
+  '@esbuild/win32-arm64@0.17.19':
+    optional: true
+
+  '@esbuild/win32-ia32@0.17.19':
+    optional: true
+
+  '@esbuild/win32-x64@0.17.19':
+    optional: true
+
+  '@eslint-community/eslint-utils@4.4.0(eslint@9.5.0)':
+    dependencies:
+      eslint: 9.5.0
+      eslint-visitor-keys: 3.4.3
+
+  '@eslint-community/regexpp@4.10.1': {}
+
+  '@eslint/config-array@0.16.0':
+    dependencies:
+      '@eslint/object-schema': 2.1.4
+      debug: 4.3.5
+      minimatch: 3.1.2
+    transitivePeerDependencies:
+      - supports-color
+
+  '@eslint/eslintrc@3.1.0':
+    dependencies:
+      ajv: 6.12.6
+      debug: 4.3.5
+      espree: 10.1.0
+      globals: 14.0.0
+      ignore: 5.3.1
+      import-fresh: 3.3.0
+      js-yaml: 4.1.0
+      minimatch: 3.1.2
+      strip-json-comments: 3.1.1
+    transitivePeerDependencies:
+      - supports-color
+
+  '@eslint/js@9.5.0': {}
+
+  '@eslint/object-schema@2.1.4': {}
+
+  '@humanwhocodes/module-importer@1.0.1': {}
+
+  '@humanwhocodes/retry@0.3.0': {}
+
+  '@intlify/core-base@9.1.9':
+    dependencies:
+      '@intlify/devtools-if': 9.1.9
+      '@intlify/message-compiler': 9.1.9
+      '@intlify/message-resolver': 9.1.9
+      '@intlify/runtime': 9.1.9
+      '@intlify/shared': 9.1.9
+      '@intlify/vue-devtools': 9.1.9
+
+  '@intlify/devtools-if@9.1.9':
+    dependencies:
+      '@intlify/shared': 9.1.9
+
+  '@intlify/message-compiler@9.1.9':
+    dependencies:
+      '@intlify/message-resolver': 9.1.9
+      '@intlify/shared': 9.1.9
+      source-map: 0.6.1
+
+  '@intlify/message-resolver@9.1.9': {}
+
+  '@intlify/runtime@9.1.9':
+    dependencies:
+      '@intlify/message-compiler': 9.1.9
+      '@intlify/message-resolver': 9.1.9
+      '@intlify/shared': 9.1.9
+
+  '@intlify/shared@9.1.9': {}
+
+  '@intlify/vue-devtools@9.1.9':
+    dependencies:
+      '@intlify/message-resolver': 9.1.9
+      '@intlify/runtime': 9.1.9
+      '@intlify/shared': 9.1.9
+
+  '@istanbuljs/load-nyc-config@1.1.0':
+    dependencies:
+      camelcase: 5.3.1
+      find-up: 4.1.0
+      get-package-type: 0.1.0
+      js-yaml: 3.14.1
+      resolve-from: 5.0.0
+
+  '@istanbuljs/schema@0.1.3': {}
+
+  '@jest/console@27.5.1':
+    dependencies:
+      '@jest/types': 27.5.1
+      '@types/node': 20.14.9
+      chalk: 4.1.2
+      jest-message-util: 27.5.1
+      jest-util: 27.5.1
+      slash: 3.0.0
+
+  '@jest/core@27.5.1':
+    dependencies:
+      '@jest/console': 27.5.1
+      '@jest/reporters': 27.5.1
+      '@jest/test-result': 27.5.1
+      '@jest/transform': 27.5.1
+      '@jest/types': 27.5.1
+      '@types/node': 20.14.9
+      ansi-escapes: 4.3.2
+      chalk: 4.1.2
+      emittery: 0.8.1
+      exit: 0.1.2
+      graceful-fs: 4.2.11
+      jest-changed-files: 27.5.1
+      jest-config: 27.5.1
+      jest-haste-map: 27.5.1
+      jest-message-util: 27.5.1
+      jest-regex-util: 27.5.1
+      jest-resolve: 27.5.1
+      jest-resolve-dependencies: 27.5.1
+      jest-runner: 27.5.1
+      jest-runtime: 27.5.1
+      jest-snapshot: 27.5.1
+      jest-util: 27.5.1
+      jest-validate: 27.5.1
+      jest-watcher: 27.5.1
+      micromatch: 4.0.7
+      rimraf: 3.0.2
+      slash: 3.0.0
+      strip-ansi: 6.0.1
+    transitivePeerDependencies:
+      - bufferutil
+      - canvas
+      - supports-color
+      - ts-node
+      - utf-8-validate
+
+  '@jest/environment@27.5.1':
+    dependencies:
+      '@jest/fake-timers': 27.5.1
+      '@jest/types': 27.5.1
+      '@types/node': 20.14.9
+      jest-mock: 27.5.1
+
+  '@jest/fake-timers@27.5.1':
+    dependencies:
+      '@jest/types': 27.5.1
+      '@sinonjs/fake-timers': 8.1.0
+      '@types/node': 20.14.9
+      jest-message-util: 27.5.1
+      jest-mock: 27.5.1
+      jest-util: 27.5.1
+
+  '@jest/globals@27.5.1':
+    dependencies:
+      '@jest/environment': 27.5.1
+      '@jest/types': 27.5.1
+      expect: 27.5.1
+
+  '@jest/reporters@27.5.1':
+    dependencies:
+      '@bcoe/v8-coverage': 0.2.3
+      '@jest/console': 27.5.1
+      '@jest/test-result': 27.5.1
+      '@jest/transform': 27.5.1
+      '@jest/types': 27.5.1
+      '@types/node': 20.14.9
+      chalk: 4.1.2
+      collect-v8-coverage: 1.0.2
+      exit: 0.1.2
+      glob: 7.2.3
+      graceful-fs: 4.2.11
+      istanbul-lib-coverage: 3.2.2
+      istanbul-lib-instrument: 5.2.1
+      istanbul-lib-report: 3.0.1
+      istanbul-lib-source-maps: 4.0.1
+      istanbul-reports: 3.1.7
+      jest-haste-map: 27.5.1
+      jest-resolve: 27.5.1
+      jest-util: 27.5.1
+      jest-worker: 27.5.1
+      slash: 3.0.0
+      source-map: 0.6.1
+      string-length: 4.0.2
+      terminal-link: 2.1.1
+      v8-to-istanbul: 8.1.1
+    transitivePeerDependencies:
+      - supports-color
+
+  '@jest/source-map@27.5.1':
+    dependencies:
+      callsites: 3.1.0
+      graceful-fs: 4.2.11
+      source-map: 0.6.1
+
+  '@jest/test-result@27.5.1':
+    dependencies:
+      '@jest/console': 27.5.1
+      '@jest/types': 27.5.1
+      '@types/istanbul-lib-coverage': 2.0.6
+      collect-v8-coverage: 1.0.2
+
+  '@jest/test-sequencer@27.5.1':
+    dependencies:
+      '@jest/test-result': 27.5.1
+      graceful-fs: 4.2.11
+      jest-haste-map: 27.5.1
+      jest-runtime: 27.5.1
+    transitivePeerDependencies:
+      - supports-color
+
+  '@jest/transform@27.5.1':
+    dependencies:
+      '@babel/core': 7.24.7
+      '@jest/types': 27.5.1
+      babel-plugin-istanbul: 6.1.1
+      chalk: 4.1.2
+      convert-source-map: 1.9.0
+      fast-json-stable-stringify: 2.1.0
+      graceful-fs: 4.2.11
+      jest-haste-map: 27.5.1
+      jest-regex-util: 27.5.1
+      jest-util: 27.5.1
+      micromatch: 4.0.7
+      pirates: 4.0.6
+      slash: 3.0.0
+      source-map: 0.6.1
+      write-file-atomic: 3.0.3
+    transitivePeerDependencies:
+      - supports-color
+
+  '@jest/types@27.5.1':
+    dependencies:
+      '@types/istanbul-lib-coverage': 2.0.6
+      '@types/istanbul-reports': 3.0.4
+      '@types/node': 20.14.9
+      '@types/yargs': 16.0.9
+      chalk: 4.1.2
+
+  '@jimp/bmp@0.10.3(@jimp/custom@0.10.3)':
+    dependencies:
+      '@babel/runtime': 7.24.7
+      '@jimp/custom': 0.10.3
+      '@jimp/utils': 0.10.3
+      bmp-js: 0.1.0
+      core-js: 3.37.1
+
+  '@jimp/core@0.10.3':
+    dependencies:
+      '@babel/runtime': 7.24.7
+      '@jimp/utils': 0.10.3
+      any-base: 1.1.0
+      buffer: 5.7.1
+      core-js: 3.37.1
+      exif-parser: 0.1.12
+      file-type: 9.0.0
+      load-bmfont: 1.4.1
+      mkdirp: 0.5.6
+      phin: 2.9.3
+      pixelmatch: 4.0.2
+      tinycolor2: 1.6.0
+
+  '@jimp/custom@0.10.3':
+    dependencies:
+      '@babel/runtime': 7.24.7
+      '@jimp/core': 0.10.3
+      core-js: 3.37.1
+
+  '@jimp/gif@0.10.3(@jimp/custom@0.10.3)':
+    dependencies:
+      '@babel/runtime': 7.24.7
+      '@jimp/custom': 0.10.3
+      '@jimp/utils': 0.10.3
+      core-js: 3.37.1
+      omggif: 1.0.10
+
+  '@jimp/jpeg@0.10.3(@jimp/custom@0.10.3)':
+    dependencies:
+      '@babel/runtime': 7.24.7
+      '@jimp/custom': 0.10.3
+      '@jimp/utils': 0.10.3
+      core-js: 3.37.1
+      jpeg-js: 0.3.7
+
+  '@jimp/plugin-blit@0.10.3(@jimp/custom@0.10.3)':
+    dependencies:
+      '@babel/runtime': 7.24.7
+      '@jimp/custom': 0.10.3
+      '@jimp/utils': 0.10.3
+      core-js: 3.37.1
+
+  '@jimp/plugin-blur@0.10.3(@jimp/custom@0.10.3)':
+    dependencies:
+      '@babel/runtime': 7.24.7
+      '@jimp/custom': 0.10.3
+      '@jimp/utils': 0.10.3
+      core-js: 3.37.1
+
+  '@jimp/plugin-circle@0.10.3(@jimp/custom@0.10.3)':
+    dependencies:
+      '@babel/runtime': 7.24.7
+      '@jimp/custom': 0.10.3
+      '@jimp/utils': 0.10.3
+      core-js: 3.37.1
+
+  '@jimp/plugin-color@0.10.3(@jimp/custom@0.10.3)':
+    dependencies:
+      '@babel/runtime': 7.24.7
+      '@jimp/custom': 0.10.3
+      '@jimp/utils': 0.10.3
+      core-js: 3.37.1
+      tinycolor2: 1.6.0
+
+  '@jimp/plugin-contain@0.10.3(@jimp/custom@0.10.3)(@jimp/plugin-blit@0.10.3(@jimp/custom@0.10.3))(@jimp/plugin-resize@0.10.3(@jimp/custom@0.10.3))(@jimp/plugin-scale@0.10.3(@jimp/custom@0.10.3)(@jimp/plugin-resize@0.10.3(@jimp/custom@0.10.3)))':
+    dependencies:
+      '@babel/runtime': 7.24.7
+      '@jimp/custom': 0.10.3
+      '@jimp/plugin-blit': 0.10.3(@jimp/custom@0.10.3)
+      '@jimp/plugin-resize': 0.10.3(@jimp/custom@0.10.3)
+      '@jimp/plugin-scale': 0.10.3(@jimp/custom@0.10.3)(@jimp/plugin-resize@0.10.3(@jimp/custom@0.10.3))
+      '@jimp/utils': 0.10.3
+      core-js: 3.37.1
+
+  '@jimp/plugin-cover@0.10.3(@jimp/custom@0.10.3)(@jimp/plugin-crop@0.10.3(@jimp/custom@0.10.3))(@jimp/plugin-resize@0.10.3(@jimp/custom@0.10.3))(@jimp/plugin-scale@0.10.3(@jimp/custom@0.10.3)(@jimp/plugin-resize@0.10.3(@jimp/custom@0.10.3)))':
+    dependencies:
+      '@babel/runtime': 7.24.7
+      '@jimp/custom': 0.10.3
+      '@jimp/plugin-crop': 0.10.3(@jimp/custom@0.10.3)
+      '@jimp/plugin-resize': 0.10.3(@jimp/custom@0.10.3)
+      '@jimp/plugin-scale': 0.10.3(@jimp/custom@0.10.3)(@jimp/plugin-resize@0.10.3(@jimp/custom@0.10.3))
+      '@jimp/utils': 0.10.3
+      core-js: 3.37.1
+
+  '@jimp/plugin-crop@0.10.3(@jimp/custom@0.10.3)':
+    dependencies:
+      '@babel/runtime': 7.24.7
+      '@jimp/custom': 0.10.3
+      '@jimp/utils': 0.10.3
+      core-js: 3.37.1
+
+  '@jimp/plugin-displace@0.10.3(@jimp/custom@0.10.3)':
+    dependencies:
+      '@babel/runtime': 7.24.7
+      '@jimp/custom': 0.10.3
+      '@jimp/utils': 0.10.3
+      core-js: 3.37.1
+
+  '@jimp/plugin-dither@0.10.3(@jimp/custom@0.10.3)':
+    dependencies:
+      '@babel/runtime': 7.24.7
+      '@jimp/custom': 0.10.3
+      '@jimp/utils': 0.10.3
+      core-js: 3.37.1
+
+  '@jimp/plugin-fisheye@0.10.3(@jimp/custom@0.10.3)':
+    dependencies:
+      '@babel/runtime': 7.24.7
+      '@jimp/custom': 0.10.3
+      '@jimp/utils': 0.10.3
+      core-js: 3.37.1
+
+  '@jimp/plugin-flip@0.10.3(@jimp/custom@0.10.3)(@jimp/plugin-rotate@0.10.3(@jimp/custom@0.10.3)(@jimp/plugin-blit@0.10.3(@jimp/custom@0.10.3))(@jimp/plugin-crop@0.10.3(@jimp/custom@0.10.3))(@jimp/plugin-resize@0.10.3(@jimp/custom@0.10.3)))':
+    dependencies:
+      '@babel/runtime': 7.24.7
+      '@jimp/custom': 0.10.3
+      '@jimp/plugin-rotate': 0.10.3(@jimp/custom@0.10.3)(@jimp/plugin-blit@0.10.3(@jimp/custom@0.10.3))(@jimp/plugin-crop@0.10.3(@jimp/custom@0.10.3))(@jimp/plugin-resize@0.10.3(@jimp/custom@0.10.3))
+      '@jimp/utils': 0.10.3
+      core-js: 3.37.1
+
+  '@jimp/plugin-gaussian@0.10.3(@jimp/custom@0.10.3)':
+    dependencies:
+      '@babel/runtime': 7.24.7
+      '@jimp/custom': 0.10.3
+      '@jimp/utils': 0.10.3
+      core-js: 3.37.1
+
+  '@jimp/plugin-invert@0.10.3(@jimp/custom@0.10.3)':
+    dependencies:
+      '@babel/runtime': 7.24.7
+      '@jimp/custom': 0.10.3
+      '@jimp/utils': 0.10.3
+      core-js: 3.37.1
+
+  '@jimp/plugin-mask@0.10.3(@jimp/custom@0.10.3)':
+    dependencies:
+      '@babel/runtime': 7.24.7
+      '@jimp/custom': 0.10.3
+      '@jimp/utils': 0.10.3
+      core-js: 3.37.1
+
+  '@jimp/plugin-normalize@0.10.3(@jimp/custom@0.10.3)':
+    dependencies:
+      '@babel/runtime': 7.24.7
+      '@jimp/custom': 0.10.3
+      '@jimp/utils': 0.10.3
+      core-js: 3.37.1
+
+  '@jimp/plugin-print@0.10.3(@jimp/custom@0.10.3)(@jimp/plugin-blit@0.10.3(@jimp/custom@0.10.3))':
+    dependencies:
+      '@babel/runtime': 7.24.7
+      '@jimp/custom': 0.10.3
+      '@jimp/plugin-blit': 0.10.3(@jimp/custom@0.10.3)
+      '@jimp/utils': 0.10.3
+      core-js: 3.37.1
+      load-bmfont: 1.4.1
+
+  '@jimp/plugin-resize@0.10.3(@jimp/custom@0.10.3)':
+    dependencies:
+      '@babel/runtime': 7.24.7
+      '@jimp/custom': 0.10.3
+      '@jimp/utils': 0.10.3
+      core-js: 3.37.1
+
+  '@jimp/plugin-rotate@0.10.3(@jimp/custom@0.10.3)(@jimp/plugin-blit@0.10.3(@jimp/custom@0.10.3))(@jimp/plugin-crop@0.10.3(@jimp/custom@0.10.3))(@jimp/plugin-resize@0.10.3(@jimp/custom@0.10.3))':
+    dependencies:
+      '@babel/runtime': 7.24.7
+      '@jimp/custom': 0.10.3
+      '@jimp/plugin-blit': 0.10.3(@jimp/custom@0.10.3)
+      '@jimp/plugin-crop': 0.10.3(@jimp/custom@0.10.3)
+      '@jimp/plugin-resize': 0.10.3(@jimp/custom@0.10.3)
+      '@jimp/utils': 0.10.3
+      core-js: 3.37.1
+
+  '@jimp/plugin-scale@0.10.3(@jimp/custom@0.10.3)(@jimp/plugin-resize@0.10.3(@jimp/custom@0.10.3))':
+    dependencies:
+      '@babel/runtime': 7.24.7
+      '@jimp/custom': 0.10.3
+      '@jimp/plugin-resize': 0.10.3(@jimp/custom@0.10.3)
+      '@jimp/utils': 0.10.3
+      core-js: 3.37.1
+
+  '@jimp/plugin-shadow@0.10.3(@jimp/custom@0.10.3)(@jimp/plugin-blur@0.10.3(@jimp/custom@0.10.3))(@jimp/plugin-resize@0.10.3(@jimp/custom@0.10.3))':
+    dependencies:
+      '@babel/runtime': 7.24.7
+      '@jimp/custom': 0.10.3
+      '@jimp/plugin-blur': 0.10.3(@jimp/custom@0.10.3)
+      '@jimp/plugin-resize': 0.10.3(@jimp/custom@0.10.3)
+      '@jimp/utils': 0.10.3
+      core-js: 3.37.1
+
+  '@jimp/plugin-threshold@0.10.3(@jimp/custom@0.10.3)(@jimp/plugin-color@0.10.3(@jimp/custom@0.10.3))(@jimp/plugin-resize@0.10.3(@jimp/custom@0.10.3))':
+    dependencies:
+      '@babel/runtime': 7.24.7
+      '@jimp/custom': 0.10.3
+      '@jimp/plugin-color': 0.10.3(@jimp/custom@0.10.3)
+      '@jimp/plugin-resize': 0.10.3(@jimp/custom@0.10.3)
+      '@jimp/utils': 0.10.3
+      core-js: 3.37.1
+
+  '@jimp/plugins@0.10.3(@jimp/custom@0.10.3)':
+    dependencies:
+      '@babel/runtime': 7.24.7
+      '@jimp/custom': 0.10.3
+      '@jimp/plugin-blit': 0.10.3(@jimp/custom@0.10.3)
+      '@jimp/plugin-blur': 0.10.3(@jimp/custom@0.10.3)
+      '@jimp/plugin-circle': 0.10.3(@jimp/custom@0.10.3)
+      '@jimp/plugin-color': 0.10.3(@jimp/custom@0.10.3)
+      '@jimp/plugin-contain': 0.10.3(@jimp/custom@0.10.3)(@jimp/plugin-blit@0.10.3(@jimp/custom@0.10.3))(@jimp/plugin-resize@0.10.3(@jimp/custom@0.10.3))(@jimp/plugin-scale@0.10.3(@jimp/custom@0.10.3)(@jimp/plugin-resize@0.10.3(@jimp/custom@0.10.3)))
+      '@jimp/plugin-cover': 0.10.3(@jimp/custom@0.10.3)(@jimp/plugin-crop@0.10.3(@jimp/custom@0.10.3))(@jimp/plugin-resize@0.10.3(@jimp/custom@0.10.3))(@jimp/plugin-scale@0.10.3(@jimp/custom@0.10.3)(@jimp/plugin-resize@0.10.3(@jimp/custom@0.10.3)))
+      '@jimp/plugin-crop': 0.10.3(@jimp/custom@0.10.3)
+      '@jimp/plugin-displace': 0.10.3(@jimp/custom@0.10.3)
+      '@jimp/plugin-dither': 0.10.3(@jimp/custom@0.10.3)
+      '@jimp/plugin-fisheye': 0.10.3(@jimp/custom@0.10.3)
+      '@jimp/plugin-flip': 0.10.3(@jimp/custom@0.10.3)(@jimp/plugin-rotate@0.10.3(@jimp/custom@0.10.3)(@jimp/plugin-blit@0.10.3(@jimp/custom@0.10.3))(@jimp/plugin-crop@0.10.3(@jimp/custom@0.10.3))(@jimp/plugin-resize@0.10.3(@jimp/custom@0.10.3)))
+      '@jimp/plugin-gaussian': 0.10.3(@jimp/custom@0.10.3)
+      '@jimp/plugin-invert': 0.10.3(@jimp/custom@0.10.3)
+      '@jimp/plugin-mask': 0.10.3(@jimp/custom@0.10.3)
+      '@jimp/plugin-normalize': 0.10.3(@jimp/custom@0.10.3)
+      '@jimp/plugin-print': 0.10.3(@jimp/custom@0.10.3)(@jimp/plugin-blit@0.10.3(@jimp/custom@0.10.3))
+      '@jimp/plugin-resize': 0.10.3(@jimp/custom@0.10.3)
+      '@jimp/plugin-rotate': 0.10.3(@jimp/custom@0.10.3)(@jimp/plugin-blit@0.10.3(@jimp/custom@0.10.3))(@jimp/plugin-crop@0.10.3(@jimp/custom@0.10.3))(@jimp/plugin-resize@0.10.3(@jimp/custom@0.10.3))
+      '@jimp/plugin-scale': 0.10.3(@jimp/custom@0.10.3)(@jimp/plugin-resize@0.10.3(@jimp/custom@0.10.3))
+      '@jimp/plugin-shadow': 0.10.3(@jimp/custom@0.10.3)(@jimp/plugin-blur@0.10.3(@jimp/custom@0.10.3))(@jimp/plugin-resize@0.10.3(@jimp/custom@0.10.3))
+      '@jimp/plugin-threshold': 0.10.3(@jimp/custom@0.10.3)(@jimp/plugin-color@0.10.3(@jimp/custom@0.10.3))(@jimp/plugin-resize@0.10.3(@jimp/custom@0.10.3))
+      core-js: 3.37.1
+      timm: 1.7.1
+
+  '@jimp/png@0.10.3(@jimp/custom@0.10.3)':
+    dependencies:
+      '@babel/runtime': 7.24.7
+      '@jimp/custom': 0.10.3
+      '@jimp/utils': 0.10.3
+      core-js: 3.37.1
+      pngjs: 3.4.0
+
+  '@jimp/tiff@0.10.3(@jimp/custom@0.10.3)':
+    dependencies:
+      '@babel/runtime': 7.24.7
+      '@jimp/custom': 0.10.3
+      core-js: 3.37.1
+      utif: 2.0.1
+
+  '@jimp/types@0.10.3(@jimp/custom@0.10.3)':
+    dependencies:
+      '@babel/runtime': 7.24.7
+      '@jimp/bmp': 0.10.3(@jimp/custom@0.10.3)
+      '@jimp/custom': 0.10.3
+      '@jimp/gif': 0.10.3(@jimp/custom@0.10.3)
+      '@jimp/jpeg': 0.10.3(@jimp/custom@0.10.3)
+      '@jimp/png': 0.10.3(@jimp/custom@0.10.3)
+      '@jimp/tiff': 0.10.3(@jimp/custom@0.10.3)
+      core-js: 3.37.1
+      timm: 1.7.1
+
+  '@jimp/utils@0.10.3':
+    dependencies:
+      '@babel/runtime': 7.24.7
+      core-js: 3.37.1
+      regenerator-runtime: 0.13.11
+
+  '@jridgewell/gen-mapping@0.3.5':
+    dependencies:
+      '@jridgewell/set-array': 1.2.1
+      '@jridgewell/sourcemap-codec': 1.4.15
+      '@jridgewell/trace-mapping': 0.3.25
+
+  '@jridgewell/resolve-uri@3.1.2': {}
+
+  '@jridgewell/set-array@1.2.1': {}
+
+  '@jridgewell/source-map@0.3.6':
+    dependencies:
+      '@jridgewell/gen-mapping': 0.3.5
+      '@jridgewell/trace-mapping': 0.3.25
+
+  '@jridgewell/sourcemap-codec@1.4.15': {}
+
+  '@jridgewell/trace-mapping@0.3.25':
+    dependencies:
+      '@jridgewell/resolve-uri': 3.1.2
+      '@jridgewell/sourcemap-codec': 1.4.15
+
+  '@nodelib/fs.scandir@2.1.5':
+    dependencies:
+      '@nodelib/fs.stat': 2.0.5
+      run-parallel: 1.2.0
+
+  '@nodelib/fs.stat@2.0.5': {}
+
+  '@nodelib/fs.walk@1.2.8':
+    dependencies:
+      '@nodelib/fs.scandir': 2.1.5
+      fastq: 1.17.1
+
+  '@pkgr/core@0.1.1': {}
+
+  '@rollup/pluginutils@4.2.1':
+    dependencies:
+      estree-walker: 2.0.2
+      picomatch: 2.3.1
+
+  '@rollup/pluginutils@5.1.0(rollup@3.29.4)':
+    dependencies:
+      '@types/estree': 1.0.5
+      estree-walker: 2.0.2
+      picomatch: 2.3.1
+    optionalDependencies:
+      rollup: 3.29.4
+
+  '@sinonjs/commons@1.8.6':
+    dependencies:
+      type-detect: 4.0.8
+
+  '@sinonjs/fake-timers@8.1.0':
+    dependencies:
+      '@sinonjs/commons': 1.8.6
+
+  '@tootallnate/once@1.1.2': {}
+
+  '@types/babel__core@7.20.5':
+    dependencies:
+      '@babel/parser': 7.24.7
+      '@babel/types': 7.24.7
+      '@types/babel__generator': 7.6.8
+      '@types/babel__template': 7.4.4
+      '@types/babel__traverse': 7.20.6
+
+  '@types/babel__generator@7.6.8':
+    dependencies:
+      '@babel/types': 7.24.7
+
+  '@types/babel__template@7.4.4':
+    dependencies:
+      '@babel/parser': 7.24.7
+      '@babel/types': 7.24.7
+
+  '@types/babel__traverse@7.20.6':
+    dependencies:
+      '@babel/types': 7.24.7
+
+  '@types/estree@1.0.5': {}
+
+  '@types/graceful-fs@4.1.9':
+    dependencies:
+      '@types/node': 20.14.9
+
+  '@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/json5@0.0.29': {}
+
+  '@types/node@20.14.9':
+    dependencies:
+      undici-types: 5.26.5
+
+  '@types/prettier@2.7.3': {}
+
+  '@types/stack-utils@2.0.3': {}
+
+  '@types/yargs-parser@21.0.3': {}
+
+  '@types/yargs@16.0.9':
+    dependencies:
+      '@types/yargs-parser': 21.0.3
+
+  '@typescript-eslint/eslint-plugin@7.14.1(@typescript-eslint/parser@7.14.1(eslint@9.5.0)(typescript@4.9.5))(eslint@9.5.0)(typescript@4.9.5)':
+    dependencies:
+      '@eslint-community/regexpp': 4.10.1
+      '@typescript-eslint/parser': 7.14.1(eslint@9.5.0)(typescript@4.9.5)
+      '@typescript-eslint/scope-manager': 7.14.1
+      '@typescript-eslint/type-utils': 7.14.1(eslint@9.5.0)(typescript@4.9.5)
+      '@typescript-eslint/utils': 7.14.1(eslint@9.5.0)(typescript@4.9.5)
+      '@typescript-eslint/visitor-keys': 7.14.1
+      eslint: 9.5.0
+      graphemer: 1.4.0
+      ignore: 5.3.1
+      natural-compare: 1.4.0
+      ts-api-utils: 1.3.0(typescript@4.9.5)
+    optionalDependencies:
+      typescript: 4.9.5
+    transitivePeerDependencies:
+      - supports-color
+
+  '@typescript-eslint/parser@7.14.1(eslint@9.5.0)(typescript@4.9.5)':
+    dependencies:
+      '@typescript-eslint/scope-manager': 7.14.1
+      '@typescript-eslint/types': 7.14.1
+      '@typescript-eslint/typescript-estree': 7.14.1(typescript@4.9.5)
+      '@typescript-eslint/visitor-keys': 7.14.1
+      debug: 4.3.5
+      eslint: 9.5.0
+    optionalDependencies:
+      typescript: 4.9.5
+    transitivePeerDependencies:
+      - supports-color
+
+  '@typescript-eslint/scope-manager@7.14.1':
+    dependencies:
+      '@typescript-eslint/types': 7.14.1
+      '@typescript-eslint/visitor-keys': 7.14.1
+
+  '@typescript-eslint/type-utils@7.14.1(eslint@9.5.0)(typescript@4.9.5)':
+    dependencies:
+      '@typescript-eslint/typescript-estree': 7.14.1(typescript@4.9.5)
+      '@typescript-eslint/utils': 7.14.1(eslint@9.5.0)(typescript@4.9.5)
+      debug: 4.3.5
+      eslint: 9.5.0
+      ts-api-utils: 1.3.0(typescript@4.9.5)
+    optionalDependencies:
+      typescript: 4.9.5
+    transitivePeerDependencies:
+      - supports-color
+
+  '@typescript-eslint/types@7.14.1': {}
+
+  '@typescript-eslint/typescript-estree@7.14.1(typescript@4.9.5)':
+    dependencies:
+      '@typescript-eslint/types': 7.14.1
+      '@typescript-eslint/visitor-keys': 7.14.1
+      debug: 4.3.5
+      globby: 11.1.0
+      is-glob: 4.0.3
+      minimatch: 9.0.5
+      semver: 7.6.2
+      ts-api-utils: 1.3.0(typescript@4.9.5)
+    optionalDependencies:
+      typescript: 4.9.5
+    transitivePeerDependencies:
+      - supports-color
+
+  '@typescript-eslint/utils@7.14.1(eslint@9.5.0)(typescript@4.9.5)':
+    dependencies:
+      '@eslint-community/eslint-utils': 4.4.0(eslint@9.5.0)
+      '@typescript-eslint/scope-manager': 7.14.1
+      '@typescript-eslint/types': 7.14.1
+      '@typescript-eslint/typescript-estree': 7.14.1(typescript@4.9.5)
+      eslint: 9.5.0
+    transitivePeerDependencies:
+      - supports-color
+      - typescript
+
+  '@typescript-eslint/visitor-keys@7.14.1':
+    dependencies:
+      '@typescript-eslint/types': 7.14.1
+      eslint-visitor-keys: 3.4.3
+
+  '@vitejs/plugin-legacy@4.1.1(terser@5.31.1)(vite@4.3.5(@types/node@20.14.9)(sass@1.77.6)(terser@5.31.1))':
+    dependencies:
+      '@babel/core': 7.24.7
+      '@babel/preset-env': 7.24.7(@babel/core@7.24.7)
+      browserslist: 4.23.1
+      core-js: 3.37.1
+      magic-string: 0.30.10
+      regenerator-runtime: 0.13.11
+      systemjs: 6.15.1
+      terser: 5.31.1
+      vite: 4.3.5(@types/node@20.14.9)(sass@1.77.6)(terser@5.31.1)
+    transitivePeerDependencies:
+      - supports-color
+
+  '@vitejs/plugin-vue-jsx@3.1.0(vite@4.3.5(@types/node@20.14.9)(sass@1.77.6)(terser@5.31.1))(vue@3.4.30(typescript@4.9.5))':
+    dependencies:
+      '@babel/core': 7.24.7
+      '@babel/plugin-transform-typescript': 7.24.7(@babel/core@7.24.7)
+      '@vue/babel-plugin-jsx': 1.2.2(@babel/core@7.24.7)
+      vite: 4.3.5(@types/node@20.14.9)(sass@1.77.6)(terser@5.31.1)
+      vue: 3.4.30(typescript@4.9.5)
+    transitivePeerDependencies:
+      - supports-color
+
+  '@vitejs/plugin-vue@4.6.2(vite@4.3.5(@types/node@20.14.9)(sass@1.77.6)(terser@5.31.1))(vue@3.4.30(typescript@4.9.5))':
+    dependencies:
+      vite: 4.3.5(@types/node@20.14.9)(sass@1.77.6)(terser@5.31.1)
+      vue: 3.4.30(typescript@4.9.5)
+
+  '@volar/language-core@1.11.1':
+    dependencies:
+      '@volar/source-map': 1.11.1
+
+  '@volar/source-map@1.11.1':
+    dependencies:
+      muggle-string: 0.3.1
+
+  '@volar/typescript@1.11.1':
+    dependencies:
+      '@volar/language-core': 1.11.1
+      path-browserify: 1.0.1
+
+  '@vue/babel-helper-vue-transform-on@1.2.2': {}
+
+  '@vue/babel-plugin-jsx@1.2.2(@babel/core@7.24.7)':
+    dependencies:
+      '@babel/helper-module-imports': 7.22.15
+      '@babel/helper-plugin-utils': 7.24.7
+      '@babel/plugin-syntax-jsx': 7.24.7(@babel/core@7.24.7)
+      '@babel/template': 7.24.7
+      '@babel/traverse': 7.24.7
+      '@babel/types': 7.24.7
+      '@vue/babel-helper-vue-transform-on': 1.2.2
+      '@vue/babel-plugin-resolve-type': 1.2.2(@babel/core@7.24.7)
+      camelcase: 6.3.0
+      html-tags: 3.3.1
+      svg-tags: 1.0.0
+    optionalDependencies:
+      '@babel/core': 7.24.7
+    transitivePeerDependencies:
+      - supports-color
+
+  '@vue/babel-plugin-resolve-type@1.2.2(@babel/core@7.24.7)':
+    dependencies:
+      '@babel/code-frame': 7.24.7
+      '@babel/core': 7.24.7
+      '@babel/helper-module-imports': 7.22.15
+      '@babel/helper-plugin-utils': 7.24.7
+      '@babel/parser': 7.24.7
+      '@vue/compiler-sfc': 3.4.30
+
+  '@vue/compiler-core@3.3.11':
+    dependencies:
+      '@babel/parser': 7.24.7
+      '@vue/shared': 3.3.11
+      estree-walker: 2.0.2
+      source-map-js: 1.2.0
+
+  '@vue/compiler-core@3.4.30':
+    dependencies:
+      '@babel/parser': 7.24.7
+      '@vue/shared': 3.4.30
+      entities: 4.5.0
+      estree-walker: 2.0.2
+      source-map-js: 1.2.0
+
+  '@vue/compiler-dom@3.3.11':
+    dependencies:
+      '@vue/compiler-core': 3.3.11
+      '@vue/shared': 3.3.11
+
+  '@vue/compiler-dom@3.4.30':
+    dependencies:
+      '@vue/compiler-core': 3.4.30
+      '@vue/shared': 3.4.30
+
+  '@vue/compiler-sfc@3.3.11':
+    dependencies:
+      '@babel/parser': 7.24.7
+      '@vue/compiler-core': 3.3.11
+      '@vue/compiler-dom': 3.3.11
+      '@vue/compiler-ssr': 3.3.11
+      '@vue/reactivity-transform': 3.3.11
+      '@vue/shared': 3.3.11
+      estree-walker: 2.0.2
+      magic-string: 0.30.10
+      postcss: 8.4.38
+      source-map-js: 1.2.0
+
+  '@vue/compiler-sfc@3.4.30':
+    dependencies:
+      '@babel/parser': 7.24.7
+      '@vue/compiler-core': 3.4.30
+      '@vue/compiler-dom': 3.4.30
+      '@vue/compiler-ssr': 3.4.30
+      '@vue/shared': 3.4.30
+      estree-walker: 2.0.2
+      magic-string: 0.30.10
+      postcss: 8.4.38
+      source-map-js: 1.2.0
+
+  '@vue/compiler-ssr@3.3.11':
+    dependencies:
+      '@vue/compiler-dom': 3.3.11
+      '@vue/shared': 3.3.11
+
+  '@vue/compiler-ssr@3.4.30':
+    dependencies:
+      '@vue/compiler-dom': 3.4.30
+      '@vue/shared': 3.4.30
+
+  '@vue/devtools-api@6.6.3': {}
+
+  '@vue/language-core@1.8.27(typescript@4.9.5)':
+    dependencies:
+      '@volar/language-core': 1.11.1
+      '@volar/source-map': 1.11.1
+      '@vue/compiler-dom': 3.4.30
+      '@vue/shared': 3.4.30
+      computeds: 0.0.1
+      minimatch: 9.0.5
+      muggle-string: 0.3.1
+      path-browserify: 1.0.1
+      vue-template-compiler: 2.7.16
+    optionalDependencies:
+      typescript: 4.9.5
+
+  '@vue/reactivity-transform@3.3.11':
+    dependencies:
+      '@babel/parser': 7.24.7
+      '@vue/compiler-core': 3.3.11
+      '@vue/shared': 3.3.11
+      estree-walker: 2.0.2
+      magic-string: 0.30.10
+
+  '@vue/reactivity@3.4.30':
+    dependencies:
+      '@vue/shared': 3.4.30
+
+  '@vue/runtime-core@3.4.30':
+    dependencies:
+      '@vue/reactivity': 3.4.30
+      '@vue/shared': 3.4.30
+
+  '@vue/runtime-dom@3.4.30':
+    dependencies:
+      '@vue/reactivity': 3.4.30
+      '@vue/runtime-core': 3.4.30
+      '@vue/shared': 3.4.30
+      csstype: 3.1.3
+
+  '@vue/server-renderer@3.3.11(vue@3.4.30(typescript@4.9.5))':
+    dependencies:
+      '@vue/compiler-ssr': 3.3.11
+      '@vue/shared': 3.3.11
+      vue: 3.4.30(typescript@4.9.5)
+
+  '@vue/server-renderer@3.4.30(vue@3.4.30(typescript@4.9.5))':
+    dependencies:
+      '@vue/compiler-ssr': 3.4.30
+      '@vue/shared': 3.4.30
+      vue: 3.4.30(typescript@4.9.5)
+
+  '@vue/shared@3.3.11': {}
+
+  '@vue/shared@3.4.30': {}
+
+  abab@2.0.6: {}
+
+  accepts@1.3.8:
+    dependencies:
+      mime-types: 2.1.35
+      negotiator: 0.6.3
+
+  acorn-globals@6.0.0:
+    dependencies:
+      acorn: 7.4.1
+      acorn-walk: 7.2.0
+
+  acorn-jsx@5.3.2(acorn@8.12.0):
+    dependencies:
+      acorn: 8.12.0
+
+  acorn-walk@7.2.0: {}
+
+  acorn@7.4.1: {}
+
+  acorn@8.12.0: {}
+
+  address@1.2.2: {}
+
+  agent-base@6.0.2:
+    dependencies:
+      debug: 4.3.5
+    transitivePeerDependencies:
+      - supports-color
+
+  ajv@6.12.6:
+    dependencies:
+      fast-deep-equal: 3.1.3
+      fast-json-stable-stringify: 2.1.0
+      json-schema-traverse: 0.4.1
+      uri-js: 4.4.1
+
+  ansi-escapes@4.3.2:
+    dependencies:
+      type-fest: 0.21.3
+
+  ansi-regex@5.0.1: {}
+
+  ansi-styles@3.2.1:
+    dependencies:
+      color-convert: 1.9.3
+
+  ansi-styles@4.3.0:
+    dependencies:
+      color-convert: 2.0.1
+
+  ansi-styles@5.2.0: {}
+
+  any-base@1.1.0: {}
+
+  anymatch@3.1.3:
+    dependencies:
+      normalize-path: 3.0.0
+      picomatch: 2.3.1
+
+  argparse@1.0.10:
+    dependencies:
+      sprintf-js: 1.0.3
+
+  argparse@2.0.1: {}
+
+  array-buffer-byte-length@1.0.1:
+    dependencies:
+      call-bind: 1.0.7
+      is-array-buffer: 3.0.4
+
+  array-flatten@1.1.1: {}
+
+  array-includes@3.1.8:
+    dependencies:
+      call-bind: 1.0.7
+      define-properties: 1.2.1
+      es-abstract: 1.23.3
+      es-object-atoms: 1.0.0
+      get-intrinsic: 1.2.4
+      is-string: 1.0.7
+
+  array-union@2.1.0: {}
+
+  array.prototype.findlastindex@1.2.5:
+    dependencies:
+      call-bind: 1.0.7
+      define-properties: 1.2.1
+      es-abstract: 1.23.3
+      es-errors: 1.3.0
+      es-object-atoms: 1.0.0
+      es-shim-unscopables: 1.0.2
+
+  array.prototype.flat@1.3.2:
+    dependencies:
+      call-bind: 1.0.7
+      define-properties: 1.2.1
+      es-abstract: 1.23.3
+      es-shim-unscopables: 1.0.2
+
+  array.prototype.flatmap@1.3.2:
+    dependencies:
+      call-bind: 1.0.7
+      define-properties: 1.2.1
+      es-abstract: 1.23.3
+      es-shim-unscopables: 1.0.2
+
+  arraybuffer.prototype.slice@1.0.3:
+    dependencies:
+      array-buffer-byte-length: 1.0.1
+      call-bind: 1.0.7
+      define-properties: 1.2.1
+      es-abstract: 1.23.3
+      es-errors: 1.3.0
+      get-intrinsic: 1.2.4
+      is-array-buffer: 3.0.4
+      is-shared-array-buffer: 1.0.3
+
+  asynckit@0.4.0: {}
+
+  autoprefixer@10.4.19(postcss@8.4.38):
+    dependencies:
+      browserslist: 4.23.1
+      caniuse-lite: 1.0.30001637
+      fraction.js: 4.3.7
+      normalize-range: 0.1.2
+      picocolors: 1.0.1
+      postcss: 8.4.38
+      postcss-value-parser: 4.2.0
+
+  available-typed-arrays@1.0.7:
+    dependencies:
+      possible-typed-array-names: 1.0.0
+
+  axios@0.19.2:
+    dependencies:
+      follow-redirects: 1.5.10
+    transitivePeerDependencies:
+      - supports-color
+
+  axios@0.21.4:
+    dependencies:
+      follow-redirects: 1.15.6
+    transitivePeerDependencies:
+      - debug
+
+  babel-jest@27.5.1(@babel/core@7.24.7):
+    dependencies:
+      '@babel/core': 7.24.7
+      '@jest/transform': 27.5.1
+      '@jest/types': 27.5.1
+      '@types/babel__core': 7.20.5
+      babel-plugin-istanbul: 6.1.1
+      babel-preset-jest: 27.5.1(@babel/core@7.24.7)
+      chalk: 4.1.2
+      graceful-fs: 4.2.11
+      slash: 3.0.0
+    transitivePeerDependencies:
+      - supports-color
+
+  babel-plugin-istanbul@6.1.1:
+    dependencies:
+      '@babel/helper-plugin-utils': 7.24.7
+      '@istanbuljs/load-nyc-config': 1.1.0
+      '@istanbuljs/schema': 0.1.3
+      istanbul-lib-instrument: 5.2.1
+      test-exclude: 6.0.0
+    transitivePeerDependencies:
+      - supports-color
+
+  babel-plugin-jest-hoist@27.5.1:
+    dependencies:
+      '@babel/template': 7.24.7
+      '@babel/types': 7.24.7
+      '@types/babel__core': 7.20.5
+      '@types/babel__traverse': 7.20.6
+
+  babel-plugin-polyfill-corejs2@0.4.11(@babel/core@7.24.7):
+    dependencies:
+      '@babel/compat-data': 7.24.7
+      '@babel/core': 7.24.7
+      '@babel/helper-define-polyfill-provider': 0.6.2(@babel/core@7.24.7)
+      semver: 6.3.1
+    transitivePeerDependencies:
+      - supports-color
+
+  babel-plugin-polyfill-corejs3@0.10.4(@babel/core@7.24.7):
+    dependencies:
+      '@babel/core': 7.24.7
+      '@babel/helper-define-polyfill-provider': 0.6.2(@babel/core@7.24.7)
+      core-js-compat: 3.37.1
+    transitivePeerDependencies:
+      - supports-color
+
+  babel-plugin-polyfill-regenerator@0.6.2(@babel/core@7.24.7):
+    dependencies:
+      '@babel/core': 7.24.7
+      '@babel/helper-define-polyfill-provider': 0.6.2(@babel/core@7.24.7)
+    transitivePeerDependencies:
+      - supports-color
+
+  babel-preset-current-node-syntax@1.0.1(@babel/core@7.24.7):
+    dependencies:
+      '@babel/core': 7.24.7
+      '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.24.7)
+      '@babel/plugin-syntax-bigint': 7.8.3(@babel/core@7.24.7)
+      '@babel/plugin-syntax-class-properties': 7.12.13(@babel/core@7.24.7)
+      '@babel/plugin-syntax-import-meta': 7.10.4(@babel/core@7.24.7)
+      '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.24.7)
+      '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.24.7)
+      '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.24.7)
+      '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.24.7)
+      '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.24.7)
+      '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.24.7)
+      '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.24.7)
+      '@babel/plugin-syntax-top-level-await': 7.14.5(@babel/core@7.24.7)
+
+  babel-preset-jest@27.5.1(@babel/core@7.24.7):
+    dependencies:
+      '@babel/core': 7.24.7
+      babel-plugin-jest-hoist: 27.5.1
+      babel-preset-current-node-syntax: 1.0.1(@babel/core@7.24.7)
+
+  balanced-match@1.0.2: {}
+
+  base64-js@1.5.1: {}
+
+  base64url@3.0.1: {}
+
+  binary-extensions@2.3.0: {}
+
+  bmp-js@0.1.0: {}
+
+  body-parser@1.20.2:
+    dependencies:
+      bytes: 3.1.2
+      content-type: 1.0.5
+      debug: 2.6.9
+      depd: 2.0.0
+      destroy: 1.2.0
+      http-errors: 2.0.0
+      iconv-lite: 0.4.24
+      on-finished: 2.4.1
+      qs: 6.11.0
+      raw-body: 2.5.2
+      type-is: 1.6.18
+      unpipe: 1.0.0
+    transitivePeerDependencies:
+      - supports-color
+
+  boolbase@1.0.0: {}
+
+  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
+
+  braces@3.0.3:
+    dependencies:
+      fill-range: 7.1.1
+
+  browser-process-hrtime@1.0.0: {}
+
+  browserslist@4.23.1:
+    dependencies:
+      caniuse-lite: 1.0.30001637
+      electron-to-chromium: 1.4.812
+      node-releases: 2.0.14
+      update-browserslist-db: 1.0.16(browserslist@4.23.1)
+
+  bser@2.1.1:
+    dependencies:
+      node-int64: 0.4.0
+
+  buffer-equal@0.0.1: {}
+
+  buffer-from@1.1.2: {}
+
+  buffer@5.7.1:
+    dependencies:
+      base64-js: 1.5.1
+      ieee754: 1.2.1
+
+  bytes@3.1.2: {}
+
+  cac@6.7.9: {}
+
+  call-bind@1.0.7:
+    dependencies:
+      es-define-property: 1.0.0
+      es-errors: 1.3.0
+      function-bind: 1.1.2
+      get-intrinsic: 1.2.4
+      set-function-length: 1.2.2
+
+  callsites@3.1.0: {}
+
+  camelcase@5.3.1: {}
+
+  camelcase@6.3.0: {}
+
+  caniuse-lite@1.0.30001637: {}
+
+  chalk@2.4.2:
+    dependencies:
+      ansi-styles: 3.2.1
+      escape-string-regexp: 1.0.5
+      supports-color: 5.5.0
+
+  chalk@4.1.2:
+    dependencies:
+      ansi-styles: 4.3.0
+      supports-color: 7.2.0
+
+  char-regex@1.0.2: {}
+
+  chardet@0.7.0: {}
+
+  chokidar@3.6.0:
+    dependencies:
+      anymatch: 3.1.3
+      braces: 3.0.3
+      glob-parent: 5.1.2
+      is-binary-path: 2.1.0
+      is-glob: 4.0.3
+      normalize-path: 3.0.0
+      readdirp: 3.6.0
+    optionalDependencies:
+      fsevents: 2.3.3
+
+  ci-info@3.9.0: {}
+
+  cjs-module-lexer@1.3.1: {}
+
+  cli-cursor@3.1.0:
+    dependencies:
+      restore-cursor: 3.1.0
+
+  cli-width@3.0.0: {}
+
+  cliui@7.0.4:
+    dependencies:
+      string-width: 4.2.3
+      strip-ansi: 6.0.1
+      wrap-ansi: 7.0.0
+
+  co@4.6.0: {}
+
+  collect-v8-coverage@1.0.2: {}
+
+  color-convert@1.9.3:
+    dependencies:
+      color-name: 1.1.3
+
+  color-convert@2.0.1:
+    dependencies:
+      color-name: 1.1.4
+
+  color-name@1.1.3: {}
+
+  color-name@1.1.4: {}
+
+  combined-stream@1.0.8:
+    dependencies:
+      delayed-stream: 1.0.0
+
+  commander@2.20.3: {}
+
+  compare-versions@3.6.0: {}
+
+  computeds@0.0.1: {}
+
+  concat-map@0.0.1: {}
+
+  confbox@0.1.7: {}
+
+  content-disposition@0.5.4:
+    dependencies:
+      safe-buffer: 5.2.1
+
+  content-type@1.0.5: {}
+
+  convert-source-map@1.9.0: {}
+
+  convert-source-map@2.0.0: {}
+
+  cookie-signature@1.0.6: {}
+
+  cookie@0.6.0: {}
+
+  core-js-compat@3.37.1:
+    dependencies:
+      browserslist: 4.23.1
+
+  core-js@3.37.1: {}
+
+  cross-env@7.0.3:
+    dependencies:
+      cross-spawn: 7.0.3
+
+  cross-spawn@7.0.3:
+    dependencies:
+      path-key: 3.1.1
+      shebang-command: 2.0.0
+      which: 2.0.2
+
+  css-font-size-keywords@1.0.0: {}
+
+  css-font-stretch-keywords@1.0.1: {}
+
+  css-font-style-keywords@1.0.1: {}
+
+  css-font-weight-keywords@1.0.0: {}
+
+  css-list-helpers@2.0.0: {}
+
+  css-system-font-keywords@1.0.0: {}
+
+  cssesc@3.0.0: {}
+
+  cssom@0.3.8: {}
+
+  cssom@0.4.4: {}
+
+  cssstyle@2.3.0:
+    dependencies:
+      cssom: 0.3.8
+
+  csstype@3.1.3: {}
+
+  data-urls@2.0.0:
+    dependencies:
+      abab: 2.0.6
+      whatwg-mimetype: 2.3.0
+      whatwg-url: 8.7.0
+
+  data-view-buffer@1.0.1:
+    dependencies:
+      call-bind: 1.0.7
+      es-errors: 1.3.0
+      is-data-view: 1.0.1
+
+  data-view-byte-length@1.0.1:
+    dependencies:
+      call-bind: 1.0.7
+      es-errors: 1.3.0
+      is-data-view: 1.0.1
+
+  data-view-byte-offset@1.0.0:
+    dependencies:
+      call-bind: 1.0.7
+      es-errors: 1.3.0
+      is-data-view: 1.0.1
+
+  date-fns@3.6.0: {}
+
+  de-indent@1.0.2: {}
+
+  debug@2.6.9:
+    dependencies:
+      ms: 2.0.0
+
+  debug@3.1.0:
+    dependencies:
+      ms: 2.0.0
+
+  debug@3.2.7:
+    dependencies:
+      ms: 2.1.3
+
+  debug@4.3.5:
+    dependencies:
+      ms: 2.1.2
+
+  decimal.js@10.4.3: {}
+
+  dedent@0.7.0: {}
+
+  deep-is@0.1.4: {}
+
+  deepmerge@4.3.1: {}
+
+  default-gateway@6.0.3:
+    dependencies:
+      execa: 5.1.1
+
+  define-data-property@1.1.4:
+    dependencies:
+      es-define-property: 1.0.0
+      es-errors: 1.3.0
+      gopd: 1.0.1
+
+  define-properties@1.2.1:
+    dependencies:
+      define-data-property: 1.1.4
+      has-property-descriptors: 1.0.2
+      object-keys: 1.1.1
+
+  delayed-stream@1.0.0: {}
+
+  depd@2.0.0: {}
+
+  destroy@1.2.0: {}
+
+  detect-newline@3.1.0: {}
+
+  diff-sequences@27.5.1: {}
+
+  dir-glob@3.0.1:
+    dependencies:
+      path-type: 4.0.0
+
+  doctrine@2.1.0:
+    dependencies:
+      esutils: 2.0.3
+
+  dom-walk@0.1.2: {}
+
+  domelementtype@1.3.1: {}
+
+  domexception@2.0.1:
+    dependencies:
+      webidl-conversions: 5.0.0
+
+  domhandler@2.4.2:
+    dependencies:
+      domelementtype: 1.3.1
+
+  echarts@5.5.0:
+    dependencies:
+      tslib: 2.3.0
+      zrender: 5.5.0
+
+  ee-first@1.1.1: {}
+
+  electron-to-chromium@1.4.812: {}
+
+  emittery@0.8.1: {}
+
+  emoji-regex@8.0.0: {}
+
+  encodeurl@1.0.2: {}
+
+  entities@1.1.2: {}
+
+  entities@4.5.0: {}
+
+  error-ex@1.3.2:
+    dependencies:
+      is-arrayish: 0.2.1
+
+  es-abstract@1.23.3:
+    dependencies:
+      array-buffer-byte-length: 1.0.1
+      arraybuffer.prototype.slice: 1.0.3
+      available-typed-arrays: 1.0.7
+      call-bind: 1.0.7
+      data-view-buffer: 1.0.1
+      data-view-byte-length: 1.0.1
+      data-view-byte-offset: 1.0.0
+      es-define-property: 1.0.0
+      es-errors: 1.3.0
+      es-object-atoms: 1.0.0
+      es-set-tostringtag: 2.0.3
+      es-to-primitive: 1.2.1
+      function.prototype.name: 1.1.6
+      get-intrinsic: 1.2.4
+      get-symbol-description: 1.0.2
+      globalthis: 1.0.4
+      gopd: 1.0.1
+      has-property-descriptors: 1.0.2
+      has-proto: 1.0.3
+      has-symbols: 1.0.3
+      hasown: 2.0.2
+      internal-slot: 1.0.7
+      is-array-buffer: 3.0.4
+      is-callable: 1.2.7
+      is-data-view: 1.0.1
+      is-negative-zero: 2.0.3
+      is-regex: 1.1.4
+      is-shared-array-buffer: 1.0.3
+      is-string: 1.0.7
+      is-typed-array: 1.1.13
+      is-weakref: 1.0.2
+      object-inspect: 1.13.2
+      object-keys: 1.1.1
+      object.assign: 4.1.5
+      regexp.prototype.flags: 1.5.2
+      safe-array-concat: 1.1.2
+      safe-regex-test: 1.0.3
+      string.prototype.trim: 1.2.9
+      string.prototype.trimend: 1.0.8
+      string.prototype.trimstart: 1.0.8
+      typed-array-buffer: 1.0.2
+      typed-array-byte-length: 1.0.1
+      typed-array-byte-offset: 1.0.2
+      typed-array-length: 1.0.6
+      unbox-primitive: 1.0.2
+      which-typed-array: 1.1.15
+
+  es-define-property@1.0.0:
+    dependencies:
+      get-intrinsic: 1.2.4
+
+  es-errors@1.3.0: {}
+
+  es-module-lexer@1.5.4: {}
+
+  es-object-atoms@1.0.0:
+    dependencies:
+      es-errors: 1.3.0
+
+  es-set-tostringtag@2.0.3:
+    dependencies:
+      get-intrinsic: 1.2.4
+      has-tostringtag: 1.0.2
+      hasown: 2.0.2
+
+  es-shim-unscopables@1.0.2:
+    dependencies:
+      hasown: 2.0.2
+
+  es-to-primitive@1.2.1:
+    dependencies:
+      is-callable: 1.2.7
+      is-date-object: 1.0.5
+      is-symbol: 1.0.4
+
+  esbuild@0.17.19:
+    optionalDependencies:
+      '@esbuild/android-arm': 0.17.19
+      '@esbuild/android-arm64': 0.17.19
+      '@esbuild/android-x64': 0.17.19
+      '@esbuild/darwin-arm64': 0.17.19
+      '@esbuild/darwin-x64': 0.17.19
+      '@esbuild/freebsd-arm64': 0.17.19
+      '@esbuild/freebsd-x64': 0.17.19
+      '@esbuild/linux-arm': 0.17.19
+      '@esbuild/linux-arm64': 0.17.19
+      '@esbuild/linux-ia32': 0.17.19
+      '@esbuild/linux-loong64': 0.17.19
+      '@esbuild/linux-mips64el': 0.17.19
+      '@esbuild/linux-ppc64': 0.17.19
+      '@esbuild/linux-riscv64': 0.17.19
+      '@esbuild/linux-s390x': 0.17.19
+      '@esbuild/linux-x64': 0.17.19
+      '@esbuild/netbsd-x64': 0.17.19
+      '@esbuild/openbsd-x64': 0.17.19
+      '@esbuild/sunos-x64': 0.17.19
+      '@esbuild/win32-arm64': 0.17.19
+      '@esbuild/win32-ia32': 0.17.19
+      '@esbuild/win32-x64': 0.17.19
+
+  escalade@3.1.2: {}
+
+  escape-html@1.0.3: {}
+
+  escape-string-regexp@1.0.5: {}
+
+  escape-string-regexp@2.0.0: {}
+
+  escape-string-regexp@4.0.0: {}
+
+  escape-string-regexp@5.0.0: {}
+
+  escodegen@2.1.0:
+    dependencies:
+      esprima: 4.0.1
+      estraverse: 5.3.0
+      esutils: 2.0.3
+    optionalDependencies:
+      source-map: 0.6.1
+
+  eslint-config-prettier@9.1.0(eslint@9.5.0):
+    dependencies:
+      eslint: 9.5.0
+
+  eslint-import-resolver-node@0.3.9:
+    dependencies:
+      debug: 3.2.7
+      is-core-module: 2.14.0
+      resolve: 1.22.8
+    transitivePeerDependencies:
+      - supports-color
+
+  eslint-module-utils@2.8.1(@typescript-eslint/parser@7.14.1(eslint@9.5.0)(typescript@4.9.5))(eslint-import-resolver-node@0.3.9)(eslint@9.5.0):
+    dependencies:
+      debug: 3.2.7
+    optionalDependencies:
+      '@typescript-eslint/parser': 7.14.1(eslint@9.5.0)(typescript@4.9.5)
+      eslint: 9.5.0
+      eslint-import-resolver-node: 0.3.9
+    transitivePeerDependencies:
+      - supports-color
+
+  eslint-plugin-import@2.29.1(@typescript-eslint/parser@7.14.1(eslint@9.5.0)(typescript@4.9.5))(eslint@9.5.0):
+    dependencies:
+      array-includes: 3.1.8
+      array.prototype.findlastindex: 1.2.5
+      array.prototype.flat: 1.3.2
+      array.prototype.flatmap: 1.3.2
+      debug: 3.2.7
+      doctrine: 2.1.0
+      eslint: 9.5.0
+      eslint-import-resolver-node: 0.3.9
+      eslint-module-utils: 2.8.1(@typescript-eslint/parser@7.14.1(eslint@9.5.0)(typescript@4.9.5))(eslint-import-resolver-node@0.3.9)(eslint@9.5.0)
+      hasown: 2.0.2
+      is-core-module: 2.14.0
+      is-glob: 4.0.3
+      minimatch: 3.1.2
+      object.fromentries: 2.0.8
+      object.groupby: 1.0.3
+      object.values: 1.2.0
+      semver: 6.3.1
+      tsconfig-paths: 3.15.0
+    optionalDependencies:
+      '@typescript-eslint/parser': 7.14.1(eslint@9.5.0)(typescript@4.9.5)
+    transitivePeerDependencies:
+      - eslint-import-resolver-typescript
+      - eslint-import-resolver-webpack
+      - supports-color
+
+  eslint-plugin-prettier@5.1.3(eslint-config-prettier@9.1.0(eslint@9.5.0))(eslint@9.5.0)(prettier@3.3.2):
+    dependencies:
+      eslint: 9.5.0
+      prettier: 3.3.2
+      prettier-linter-helpers: 1.0.0
+      synckit: 0.8.8
+    optionalDependencies:
+      eslint-config-prettier: 9.1.0(eslint@9.5.0)
+
+  eslint-plugin-vue@9.26.0(eslint@9.5.0):
+    dependencies:
+      '@eslint-community/eslint-utils': 4.4.0(eslint@9.5.0)
+      eslint: 9.5.0
+      globals: 13.24.0
+      natural-compare: 1.4.0
+      nth-check: 2.1.1
+      postcss-selector-parser: 6.1.0
+      semver: 7.6.2
+      vue-eslint-parser: 9.4.3(eslint@9.5.0)
+      xml-name-validator: 4.0.0
+    transitivePeerDependencies:
+      - supports-color
+
+  eslint-scope@7.2.2:
+    dependencies:
+      esrecurse: 4.3.0
+      estraverse: 5.3.0
+
+  eslint-scope@8.0.1:
+    dependencies:
+      esrecurse: 4.3.0
+      estraverse: 5.3.0
+
+  eslint-visitor-keys@3.4.3: {}
+
+  eslint-visitor-keys@4.0.0: {}
+
+  eslint@9.5.0:
+    dependencies:
+      '@eslint-community/eslint-utils': 4.4.0(eslint@9.5.0)
+      '@eslint-community/regexpp': 4.10.1
+      '@eslint/config-array': 0.16.0
+      '@eslint/eslintrc': 3.1.0
+      '@eslint/js': 9.5.0
+      '@humanwhocodes/module-importer': 1.0.1
+      '@humanwhocodes/retry': 0.3.0
+      '@nodelib/fs.walk': 1.2.8
+      ajv: 6.12.6
+      chalk: 4.1.2
+      cross-spawn: 7.0.3
+      debug: 4.3.5
+      escape-string-regexp: 4.0.0
+      eslint-scope: 8.0.1
+      eslint-visitor-keys: 4.0.0
+      espree: 10.1.0
+      esquery: 1.5.0
+      esutils: 2.0.3
+      fast-deep-equal: 3.1.3
+      file-entry-cache: 8.0.0
+      find-up: 5.0.0
+      glob-parent: 6.0.2
+      ignore: 5.3.1
+      imurmurhash: 0.1.4
+      is-glob: 4.0.3
+      is-path-inside: 3.0.3
+      json-stable-stringify-without-jsonify: 1.0.1
+      levn: 0.4.1
+      lodash.merge: 4.6.2
+      minimatch: 3.1.2
+      natural-compare: 1.4.0
+      optionator: 0.9.4
+      strip-ansi: 6.0.1
+      text-table: 0.2.0
+    transitivePeerDependencies:
+      - supports-color
+
+  espree@10.1.0:
+    dependencies:
+      acorn: 8.12.0
+      acorn-jsx: 5.3.2(acorn@8.12.0)
+      eslint-visitor-keys: 4.0.0
+
+  espree@9.6.1:
+    dependencies:
+      acorn: 8.12.0
+      acorn-jsx: 5.3.2(acorn@8.12.0)
+      eslint-visitor-keys: 3.4.3
+
+  esprima@4.0.1: {}
+
+  esquery@1.5.0:
+    dependencies:
+      estraverse: 5.3.0
+
+  esrecurse@4.3.0:
+    dependencies:
+      estraverse: 5.3.0
+
+  estraverse@5.3.0: {}
+
+  estree-walker@2.0.2: {}
+
+  estree-walker@3.0.3:
+    dependencies:
+      '@types/estree': 1.0.5
+
+  esutils@2.0.3: {}
+
+  etag@1.8.1: {}
+
+  events@3.3.0: {}
+
+  execa@5.1.1:
+    dependencies:
+      cross-spawn: 7.0.3
+      get-stream: 6.0.1
+      human-signals: 2.1.0
+      is-stream: 2.0.1
+      merge-stream: 2.0.0
+      npm-run-path: 4.0.1
+      onetime: 5.1.2
+      signal-exit: 3.0.7
+      strip-final-newline: 2.0.0
+
+  exif-parser@0.1.12: {}
+
+  exit@0.1.2: {}
+
+  expect@27.5.1:
+    dependencies:
+      '@jest/types': 27.5.1
+      jest-get-type: 27.5.1
+      jest-matcher-utils: 27.5.1
+      jest-message-util: 27.5.1
+
+  express@4.19.2:
+    dependencies:
+      accepts: 1.3.8
+      array-flatten: 1.1.1
+      body-parser: 1.20.2
+      content-disposition: 0.5.4
+      content-type: 1.0.5
+      cookie: 0.6.0
+      cookie-signature: 1.0.6
+      debug: 2.6.9
+      depd: 2.0.0
+      encodeurl: 1.0.2
+      escape-html: 1.0.3
+      etag: 1.8.1
+      finalhandler: 1.2.0
+      fresh: 0.5.2
+      http-errors: 2.0.0
+      merge-descriptors: 1.0.1
+      methods: 1.1.2
+      on-finished: 2.4.1
+      parseurl: 1.3.3
+      path-to-regexp: 0.1.7
+      proxy-addr: 2.0.7
+      qs: 6.11.0
+      range-parser: 1.2.1
+      safe-buffer: 5.2.1
+      send: 0.18.0
+      serve-static: 1.15.0
+      setprototypeof: 1.2.0
+      statuses: 2.0.1
+      type-is: 1.6.18
+      utils-merge: 1.0.1
+      vary: 1.1.2
+    transitivePeerDependencies:
+      - supports-color
+
+  external-editor@3.1.0:
+    dependencies:
+      chardet: 0.7.0
+      iconv-lite: 0.4.24
+      tmp: 0.0.33
+
+  fast-deep-equal@3.1.3: {}
+
+  fast-diff@1.3.0: {}
+
+  fast-glob@3.3.2:
+    dependencies:
+      '@nodelib/fs.stat': 2.0.5
+      '@nodelib/fs.walk': 1.2.8
+      glob-parent: 5.1.2
+      merge2: 1.4.1
+      micromatch: 4.0.7
+
+  fast-json-stable-stringify@2.1.0: {}
+
+  fast-levenshtein@2.0.6: {}
+
+  fastq@1.17.1:
+    dependencies:
+      reusify: 1.0.4
+
+  fb-watchman@2.0.2:
+    dependencies:
+      bser: 2.1.1
+
+  figures@3.2.0:
+    dependencies:
+      escape-string-regexp: 1.0.5
+
+  file-entry-cache@8.0.0:
+    dependencies:
+      flat-cache: 4.0.1
+
+  file-type@9.0.0: {}
+
+  fill-range@7.1.1:
+    dependencies:
+      to-regex-range: 5.0.1
+
+  finalhandler@1.2.0:
+    dependencies:
+      debug: 2.6.9
+      encodeurl: 1.0.2
+      escape-html: 1.0.3
+      on-finished: 2.4.1
+      parseurl: 1.3.3
+      statuses: 2.0.1
+      unpipe: 1.0.0
+    transitivePeerDependencies:
+      - supports-color
+
+  find-up@4.1.0:
+    dependencies:
+      locate-path: 5.0.0
+      path-exists: 4.0.0
+
+  find-up@5.0.0:
+    dependencies:
+      locate-path: 6.0.0
+      path-exists: 4.0.0
+
+  flat-cache@4.0.1:
+    dependencies:
+      flatted: 3.3.1
+      keyv: 4.5.4
+
+  flatted@3.3.1: {}
+
+  follow-redirects@1.15.6: {}
+
+  follow-redirects@1.5.10:
+    dependencies:
+      debug: 3.1.0
+    transitivePeerDependencies:
+      - supports-color
+
+  for-each@0.3.3:
+    dependencies:
+      is-callable: 1.2.7
+
+  form-data@3.0.1:
+    dependencies:
+      asynckit: 0.4.0
+      combined-stream: 1.0.8
+      mime-types: 2.1.35
+
+  forwarded@0.2.0: {}
+
+  fraction.js@4.3.7: {}
+
+  fresh@0.5.2: {}
+
+  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
+
+  function-bind@1.1.2: {}
+
+  function.prototype.name@1.1.6:
+    dependencies:
+      call-bind: 1.0.7
+      define-properties: 1.2.1
+      es-abstract: 1.23.3
+      functions-have-names: 1.2.3
+
+  functions-have-names@1.2.3: {}
+
+  generic-names@4.0.0:
+    dependencies:
+      loader-utils: 3.3.1
+
+  gensync@1.0.0-beta.2: {}
+
+  get-caller-file@2.0.5: {}
+
+  get-intrinsic@1.2.4:
+    dependencies:
+      es-errors: 1.3.0
+      function-bind: 1.1.2
+      has-proto: 1.0.3
+      has-symbols: 1.0.3
+      hasown: 2.0.2
+
+  get-package-type@0.1.0: {}
+
+  get-stream@6.0.1: {}
+
+  get-symbol-description@1.0.2:
+    dependencies:
+      call-bind: 1.0.7
+      es-errors: 1.3.0
+      get-intrinsic: 1.2.4
+
+  glob-parent@5.1.2:
+    dependencies:
+      is-glob: 4.0.3
+
+  glob-parent@6.0.2:
+    dependencies:
+      is-glob: 4.0.3
+
+  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
+
+  global@4.4.0:
+    dependencies:
+      min-document: 2.19.0
+      process: 0.11.10
+
+  globals@11.12.0: {}
+
+  globals@13.24.0:
+    dependencies:
+      type-fest: 0.20.2
+
+  globals@14.0.0: {}
+
+  globalthis@1.0.4:
+    dependencies:
+      define-properties: 1.2.1
+      gopd: 1.0.1
+
+  globby@11.1.0:
+    dependencies:
+      array-union: 2.1.0
+      dir-glob: 3.0.1
+      fast-glob: 3.3.2
+      ignore: 5.3.1
+      merge2: 1.4.1
+      slash: 3.0.0
+
+  gopd@1.0.1:
+    dependencies:
+      get-intrinsic: 1.2.4
+
+  graceful-fs@4.2.11: {}
+
+  graphemer@1.4.0: {}
+
+  has-bigints@1.0.2: {}
+
+  has-flag@3.0.0: {}
+
+  has-flag@4.0.0: {}
+
+  has-property-descriptors@1.0.2:
+    dependencies:
+      es-define-property: 1.0.0
+
+  has-proto@1.0.3: {}
+
+  has-symbols@1.0.3: {}
+
+  has-tostringtag@1.0.2:
+    dependencies:
+      has-symbols: 1.0.3
+
+  hash-sum@2.0.0: {}
+
+  hasown@2.0.2:
+    dependencies:
+      function-bind: 1.1.2
+
+  he@1.2.0: {}
+
+  html-encoding-sniffer@2.0.1:
+    dependencies:
+      whatwg-encoding: 1.0.5
+
+  html-escaper@2.0.2: {}
+
+  html-tags@3.3.1: {}
+
+  http-errors@2.0.0:
+    dependencies:
+      depd: 2.0.0
+      inherits: 2.0.4
+      setprototypeof: 1.2.0
+      statuses: 2.0.1
+      toidentifier: 1.0.1
+
+  http-proxy-agent@4.0.1:
+    dependencies:
+      '@tootallnate/once': 1.1.2
+      agent-base: 6.0.2
+      debug: 4.3.5
+    transitivePeerDependencies:
+      - supports-color
+
+  https-proxy-agent@5.0.1:
+    dependencies:
+      agent-base: 6.0.2
+      debug: 4.3.5
+    transitivePeerDependencies:
+      - supports-color
+
+  human-signals@2.1.0: {}
+
+  iconfont-tools-cli@1.3.1:
+    dependencies:
+      axios: 0.21.4
+      chalk: 2.4.2
+      iconfont-tools: 1.7.13
+      inquirer: 7.3.3
+      rimraf: 3.0.2
+      semver: 6.3.1
+    transitivePeerDependencies:
+      - debug
+      - supports-color
+
+  iconfont-tools@1.7.13:
+    dependencies:
+      axios: 0.19.2
+      chalk: 2.4.2
+      inquirer: 7.3.3
+      rimraf: 3.0.2
+      semver: 6.3.1
+    transitivePeerDependencies:
+      - supports-color
+
+  iconv-lite@0.4.24:
+    dependencies:
+      safer-buffer: 2.1.2
+
+  icss-replace-symbols@1.1.0: {}
+
+  icss-utils@5.1.0(postcss@8.4.38):
+    dependencies:
+      postcss: 8.4.38
+
+  ieee754@1.2.1: {}
+
+  ignore@5.3.1: {}
+
+  immutable@4.3.6: {}
+
+  import-fresh@3.3.0:
+    dependencies:
+      parent-module: 1.0.1
+      resolve-from: 4.0.0
+
+  import-local@3.1.0:
+    dependencies:
+      pkg-dir: 4.2.0
+      resolve-cwd: 3.0.0
+
+  imurmurhash@0.1.4: {}
+
+  inflight@1.0.6:
+    dependencies:
+      once: 1.4.0
+      wrappy: 1.0.2
+
+  inherits@2.0.4: {}
+
+  inquirer@7.3.3:
+    dependencies:
+      ansi-escapes: 4.3.2
+      chalk: 4.1.2
+      cli-cursor: 3.1.0
+      cli-width: 3.0.0
+      external-editor: 3.1.0
+      figures: 3.2.0
+      lodash: 4.17.21
+      mute-stream: 0.0.8
+      run-async: 2.4.1
+      rxjs: 6.6.7
+      string-width: 4.2.3
+      strip-ansi: 6.0.1
+      through: 2.3.8
+
+  internal-slot@1.0.7:
+    dependencies:
+      es-errors: 1.3.0
+      hasown: 2.0.2
+      side-channel: 1.0.6
+
+  invert-kv@3.0.1: {}
+
+  ipaddr.js@1.9.1: {}
+
+  is-array-buffer@3.0.4:
+    dependencies:
+      call-bind: 1.0.7
+      get-intrinsic: 1.2.4
+
+  is-arrayish@0.2.1: {}
+
+  is-bigint@1.0.4:
+    dependencies:
+      has-bigints: 1.0.2
+
+  is-binary-path@2.1.0:
+    dependencies:
+      binary-extensions: 2.3.0
+
+  is-boolean-object@1.1.2:
+    dependencies:
+      call-bind: 1.0.7
+      has-tostringtag: 1.0.2
+
+  is-callable@1.2.7: {}
+
+  is-core-module@2.14.0:
+    dependencies:
+      hasown: 2.0.2
+
+  is-data-view@1.0.1:
+    dependencies:
+      is-typed-array: 1.1.13
+
+  is-date-object@1.0.5:
+    dependencies:
+      has-tostringtag: 1.0.2
+
+  is-extglob@2.1.1: {}
+
+  is-fullwidth-code-point@3.0.0: {}
+
+  is-function@1.0.2: {}
+
+  is-generator-fn@2.1.0: {}
+
+  is-glob@4.0.3:
+    dependencies:
+      is-extglob: 2.1.1
+
+  is-negative-zero@2.0.3: {}
+
+  is-number-object@1.0.7:
+    dependencies:
+      has-tostringtag: 1.0.2
+
+  is-number@7.0.0: {}
+
+  is-path-inside@3.0.3: {}
+
+  is-potential-custom-element-name@1.0.1: {}
+
+  is-regex@1.1.4:
+    dependencies:
+      call-bind: 1.0.7
+      has-tostringtag: 1.0.2
+
+  is-shared-array-buffer@1.0.3:
+    dependencies:
+      call-bind: 1.0.7
+
+  is-stream@2.0.1: {}
+
+  is-string@1.0.7:
+    dependencies:
+      has-tostringtag: 1.0.2
+
+  is-symbol@1.0.4:
+    dependencies:
+      has-symbols: 1.0.3
+
+  is-typed-array@1.1.13:
+    dependencies:
+      which-typed-array: 1.1.15
+
+  is-typedarray@1.0.0: {}
+
+  is-weakref@1.0.2:
+    dependencies:
+      call-bind: 1.0.7
+
+  isarray@2.0.5: {}
+
+  isexe@2.0.0: {}
+
+  istanbul-lib-coverage@3.2.2: {}
+
+  istanbul-lib-instrument@5.2.1:
+    dependencies:
+      '@babel/core': 7.24.7
+      '@babel/parser': 7.24.7
+      '@istanbuljs/schema': 0.1.3
+      istanbul-lib-coverage: 3.2.2
+      semver: 6.3.1
+    transitivePeerDependencies:
+      - supports-color
+
+  istanbul-lib-report@3.0.1:
+    dependencies:
+      istanbul-lib-coverage: 3.2.2
+      make-dir: 4.0.0
+      supports-color: 7.2.0
+
+  istanbul-lib-source-maps@4.0.1:
+    dependencies:
+      debug: 4.3.5
+      istanbul-lib-coverage: 3.2.2
+      source-map: 0.6.1
+    transitivePeerDependencies:
+      - supports-color
+
+  istanbul-reports@3.1.7:
+    dependencies:
+      html-escaper: 2.0.2
+      istanbul-lib-report: 3.0.1
+
+  jest-changed-files@27.5.1:
+    dependencies:
+      '@jest/types': 27.5.1
+      execa: 5.1.1
+      throat: 6.0.2
+
+  jest-circus@27.5.1:
+    dependencies:
+      '@jest/environment': 27.5.1
+      '@jest/test-result': 27.5.1
+      '@jest/types': 27.5.1
+      '@types/node': 20.14.9
+      chalk: 4.1.2
+      co: 4.6.0
+      dedent: 0.7.0
+      expect: 27.5.1
+      is-generator-fn: 2.1.0
+      jest-each: 27.5.1
+      jest-matcher-utils: 27.5.1
+      jest-message-util: 27.5.1
+      jest-runtime: 27.5.1
+      jest-snapshot: 27.5.1
+      jest-util: 27.5.1
+      pretty-format: 27.5.1
+      slash: 3.0.0
+      stack-utils: 2.0.6
+      throat: 6.0.2
+    transitivePeerDependencies:
+      - supports-color
+
+  jest-cli@27.5.1:
+    dependencies:
+      '@jest/core': 27.5.1
+      '@jest/test-result': 27.5.1
+      '@jest/types': 27.5.1
+      chalk: 4.1.2
+      exit: 0.1.2
+      graceful-fs: 4.2.11
+      import-local: 3.1.0
+      jest-config: 27.5.1
+      jest-util: 27.5.1
+      jest-validate: 27.5.1
+      prompts: 2.4.2
+      yargs: 16.2.0
+    transitivePeerDependencies:
+      - bufferutil
+      - canvas
+      - supports-color
+      - ts-node
+      - utf-8-validate
+
+  jest-config@27.5.1:
+    dependencies:
+      '@babel/core': 7.24.7
+      '@jest/test-sequencer': 27.5.1
+      '@jest/types': 27.5.1
+      babel-jest: 27.5.1(@babel/core@7.24.7)
+      chalk: 4.1.2
+      ci-info: 3.9.0
+      deepmerge: 4.3.1
+      glob: 7.2.3
+      graceful-fs: 4.2.11
+      jest-circus: 27.5.1
+      jest-environment-jsdom: 27.5.1
+      jest-environment-node: 27.5.1
+      jest-get-type: 27.5.1
+      jest-jasmine2: 27.5.1
+      jest-regex-util: 27.5.1
+      jest-resolve: 27.5.1
+      jest-runner: 27.5.1
+      jest-util: 27.5.1
+      jest-validate: 27.5.1
+      micromatch: 4.0.7
+      parse-json: 5.2.0
+      pretty-format: 27.5.1
+      slash: 3.0.0
+      strip-json-comments: 3.1.1
+    transitivePeerDependencies:
+      - bufferutil
+      - canvas
+      - supports-color
+      - utf-8-validate
+
+  jest-diff@27.5.1:
+    dependencies:
+      chalk: 4.1.2
+      diff-sequences: 27.5.1
+      jest-get-type: 27.5.1
+      pretty-format: 27.5.1
+
+  jest-docblock@27.5.1:
+    dependencies:
+      detect-newline: 3.1.0
+
+  jest-each@27.5.1:
+    dependencies:
+      '@jest/types': 27.5.1
+      chalk: 4.1.2
+      jest-get-type: 27.5.1
+      jest-util: 27.5.1
+      pretty-format: 27.5.1
+
+  jest-environment-jsdom@27.5.1:
+    dependencies:
+      '@jest/environment': 27.5.1
+      '@jest/fake-timers': 27.5.1
+      '@jest/types': 27.5.1
+      '@types/node': 20.14.9
+      jest-mock: 27.5.1
+      jest-util: 27.5.1
+      jsdom: 16.7.0
+    transitivePeerDependencies:
+      - bufferutil
+      - canvas
+      - supports-color
+      - utf-8-validate
+
+  jest-environment-node@27.5.1:
+    dependencies:
+      '@jest/environment': 27.5.1
+      '@jest/fake-timers': 27.5.1
+      '@jest/types': 27.5.1
+      '@types/node': 20.14.9
+      jest-mock: 27.5.1
+      jest-util: 27.5.1
+
+  jest-get-type@27.5.1: {}
+
+  jest-haste-map@27.5.1:
+    dependencies:
+      '@jest/types': 27.5.1
+      '@types/graceful-fs': 4.1.9
+      '@types/node': 20.14.9
+      anymatch: 3.1.3
+      fb-watchman: 2.0.2
+      graceful-fs: 4.2.11
+      jest-regex-util: 27.5.1
+      jest-serializer: 27.5.1
+      jest-util: 27.5.1
+      jest-worker: 27.5.1
+      micromatch: 4.0.7
+      walker: 1.0.8
+    optionalDependencies:
+      fsevents: 2.3.3
+
+  jest-jasmine2@27.5.1:
+    dependencies:
+      '@jest/environment': 27.5.1
+      '@jest/source-map': 27.5.1
+      '@jest/test-result': 27.5.1
+      '@jest/types': 27.5.1
+      '@types/node': 20.14.9
+      chalk: 4.1.2
+      co: 4.6.0
+      expect: 27.5.1
+      is-generator-fn: 2.1.0
+      jest-each: 27.5.1
+      jest-matcher-utils: 27.5.1
+      jest-message-util: 27.5.1
+      jest-runtime: 27.5.1
+      jest-snapshot: 27.5.1
+      jest-util: 27.5.1
+      pretty-format: 27.5.1
+      throat: 6.0.2
+    transitivePeerDependencies:
+      - supports-color
+
+  jest-leak-detector@27.5.1:
+    dependencies:
+      jest-get-type: 27.5.1
+      pretty-format: 27.5.1
+
+  jest-matcher-utils@27.5.1:
+    dependencies:
+      chalk: 4.1.2
+      jest-diff: 27.5.1
+      jest-get-type: 27.5.1
+      pretty-format: 27.5.1
+
+  jest-message-util@27.5.1:
+    dependencies:
+      '@babel/code-frame': 7.24.7
+      '@jest/types': 27.5.1
+      '@types/stack-utils': 2.0.3
+      chalk: 4.1.2
+      graceful-fs: 4.2.11
+      micromatch: 4.0.7
+      pretty-format: 27.5.1
+      slash: 3.0.0
+      stack-utils: 2.0.6
+
+  jest-mock@27.5.1:
+    dependencies:
+      '@jest/types': 27.5.1
+      '@types/node': 20.14.9
+
+  jest-pnp-resolver@1.2.3(jest-resolve@27.5.1):
+    optionalDependencies:
+      jest-resolve: 27.5.1
+
+  jest-regex-util@27.5.1: {}
+
+  jest-resolve-dependencies@27.5.1:
+    dependencies:
+      '@jest/types': 27.5.1
+      jest-regex-util: 27.5.1
+      jest-snapshot: 27.5.1
+    transitivePeerDependencies:
+      - supports-color
+
+  jest-resolve@27.5.1:
+    dependencies:
+      '@jest/types': 27.5.1
+      chalk: 4.1.2
+      graceful-fs: 4.2.11
+      jest-haste-map: 27.5.1
+      jest-pnp-resolver: 1.2.3(jest-resolve@27.5.1)
+      jest-util: 27.5.1
+      jest-validate: 27.5.1
+      resolve: 1.22.8
+      resolve.exports: 1.1.1
+      slash: 3.0.0
+
+  jest-runner@27.5.1:
+    dependencies:
+      '@jest/console': 27.5.1
+      '@jest/environment': 27.5.1
+      '@jest/test-result': 27.5.1
+      '@jest/transform': 27.5.1
+      '@jest/types': 27.5.1
+      '@types/node': 20.14.9
+      chalk: 4.1.2
+      emittery: 0.8.1
+      graceful-fs: 4.2.11
+      jest-docblock: 27.5.1
+      jest-environment-jsdom: 27.5.1
+      jest-environment-node: 27.5.1
+      jest-haste-map: 27.5.1
+      jest-leak-detector: 27.5.1
+      jest-message-util: 27.5.1
+      jest-resolve: 27.5.1
+      jest-runtime: 27.5.1
+      jest-util: 27.5.1
+      jest-worker: 27.5.1
+      source-map-support: 0.5.21
+      throat: 6.0.2
+    transitivePeerDependencies:
+      - bufferutil
+      - canvas
+      - supports-color
+      - utf-8-validate
+
+  jest-runtime@27.5.1:
+    dependencies:
+      '@jest/environment': 27.5.1
+      '@jest/fake-timers': 27.5.1
+      '@jest/globals': 27.5.1
+      '@jest/source-map': 27.5.1
+      '@jest/test-result': 27.5.1
+      '@jest/transform': 27.5.1
+      '@jest/types': 27.5.1
+      chalk: 4.1.2
+      cjs-module-lexer: 1.3.1
+      collect-v8-coverage: 1.0.2
+      execa: 5.1.1
+      glob: 7.2.3
+      graceful-fs: 4.2.11
+      jest-haste-map: 27.5.1
+      jest-message-util: 27.5.1
+      jest-mock: 27.5.1
+      jest-regex-util: 27.5.1
+      jest-resolve: 27.5.1
+      jest-snapshot: 27.5.1
+      jest-util: 27.5.1
+      slash: 3.0.0
+      strip-bom: 4.0.0
+    transitivePeerDependencies:
+      - supports-color
+
+  jest-serializer@27.5.1:
+    dependencies:
+      '@types/node': 20.14.9
+      graceful-fs: 4.2.11
+
+  jest-snapshot@27.5.1:
+    dependencies:
+      '@babel/core': 7.24.7
+      '@babel/generator': 7.24.7
+      '@babel/plugin-syntax-typescript': 7.24.7(@babel/core@7.24.7)
+      '@babel/traverse': 7.24.7
+      '@babel/types': 7.24.7
+      '@jest/transform': 27.5.1
+      '@jest/types': 27.5.1
+      '@types/babel__traverse': 7.20.6
+      '@types/prettier': 2.7.3
+      babel-preset-current-node-syntax: 1.0.1(@babel/core@7.24.7)
+      chalk: 4.1.2
+      expect: 27.5.1
+      graceful-fs: 4.2.11
+      jest-diff: 27.5.1
+      jest-get-type: 27.5.1
+      jest-haste-map: 27.5.1
+      jest-matcher-utils: 27.5.1
+      jest-message-util: 27.5.1
+      jest-util: 27.5.1
+      natural-compare: 1.4.0
+      pretty-format: 27.5.1
+      semver: 7.6.2
+    transitivePeerDependencies:
+      - supports-color
+
+  jest-util@27.5.1:
+    dependencies:
+      '@jest/types': 27.5.1
+      '@types/node': 20.14.9
+      chalk: 4.1.2
+      ci-info: 3.9.0
+      graceful-fs: 4.2.11
+      picomatch: 2.3.1
+
+  jest-validate@27.5.1:
+    dependencies:
+      '@jest/types': 27.5.1
+      camelcase: 6.3.0
+      chalk: 4.1.2
+      jest-get-type: 27.5.1
+      leven: 3.1.0
+      pretty-format: 27.5.1
+
+  jest-watcher@27.5.1:
+    dependencies:
+      '@jest/test-result': 27.5.1
+      '@jest/types': 27.5.1
+      '@types/node': 20.14.9
+      ansi-escapes: 4.3.2
+      chalk: 4.1.2
+      jest-util: 27.5.1
+      string-length: 4.0.2
+
+  jest-worker@27.5.1:
+    dependencies:
+      '@types/node': 20.14.9
+      merge-stream: 2.0.0
+      supports-color: 8.1.1
+
+  jest@27.0.4:
+    dependencies:
+      '@jest/core': 27.5.1
+      import-local: 3.1.0
+      jest-cli: 27.5.1
+    transitivePeerDependencies:
+      - bufferutil
+      - canvas
+      - supports-color
+      - ts-node
+      - utf-8-validate
+
+  jimp@0.10.3:
+    dependencies:
+      '@babel/runtime': 7.24.7
+      '@jimp/custom': 0.10.3
+      '@jimp/plugins': 0.10.3(@jimp/custom@0.10.3)
+      '@jimp/types': 0.10.3(@jimp/custom@0.10.3)
+      core-js: 3.37.1
+      regenerator-runtime: 0.13.11
+
+  jpeg-js@0.3.7: {}
+
+  js-tokens@4.0.0: {}
+
+  js-tokens@9.0.0: {}
+
+  js-yaml@3.14.1:
+    dependencies:
+      argparse: 1.0.10
+      esprima: 4.0.1
+
+  js-yaml@4.1.0:
+    dependencies:
+      argparse: 2.0.1
+
+  jsdom@16.7.0:
+    dependencies:
+      abab: 2.0.6
+      acorn: 8.12.0
+      acorn-globals: 6.0.0
+      cssom: 0.4.4
+      cssstyle: 2.3.0
+      data-urls: 2.0.0
+      decimal.js: 10.4.3
+      domexception: 2.0.1
+      escodegen: 2.1.0
+      form-data: 3.0.1
+      html-encoding-sniffer: 2.0.1
+      http-proxy-agent: 4.0.1
+      https-proxy-agent: 5.0.1
+      is-potential-custom-element-name: 1.0.1
+      nwsapi: 2.2.10
+      parse5: 6.0.1
+      saxes: 5.0.1
+      symbol-tree: 3.2.4
+      tough-cookie: 4.1.4
+      w3c-hr-time: 1.0.2
+      w3c-xmlserializer: 2.0.0
+      webidl-conversions: 6.1.0
+      whatwg-encoding: 1.0.5
+      whatwg-mimetype: 2.3.0
+      whatwg-url: 8.7.0
+      ws: 7.5.10
+      xml-name-validator: 3.0.0
+    transitivePeerDependencies:
+      - bufferutil
+      - supports-color
+      - utf-8-validate
+
+  jsesc@0.5.0: {}
+
+  jsesc@2.5.2: {}
+
+  json-buffer@3.0.1: {}
+
+  json-parse-even-better-errors@2.3.1: {}
+
+  json-schema-traverse@0.4.1: {}
+
+  json-stable-stringify-without-jsonify@1.0.1: {}
+
+  json5@1.0.2:
+    dependencies:
+      minimist: 1.2.8
+
+  json5@2.2.3: {}
+
+  jsonc-parser@3.3.1: {}
+
+  jsonfile@6.1.0:
+    dependencies:
+      universalify: 2.0.1
+    optionalDependencies:
+      graceful-fs: 4.2.11
+
+  keyv@4.5.4:
+    dependencies:
+      json-buffer: 3.0.1
+
+  kleur@3.0.3: {}
+
+  lcid@3.1.1:
+    dependencies:
+      invert-kv: 3.0.1
+
+  leven@3.1.0: {}
+
+  levn@0.4.1:
+    dependencies:
+      prelude-ls: 1.2.1
+      type-check: 0.4.0
+
+  licia@1.41.0: {}
+
+  lilconfig@2.1.0: {}
+
+  lines-and-columns@1.2.4: {}
+
+  lines-and-columns@2.0.4: {}
+
+  load-bmfont@1.4.1:
+    dependencies:
+      buffer-equal: 0.0.1
+      mime: 1.6.0
+      parse-bmfont-ascii: 1.0.6
+      parse-bmfont-binary: 1.0.6
+      parse-bmfont-xml: 1.1.6
+      phin: 2.9.3
+      xhr: 2.6.0
+      xtend: 4.0.2
+
+  loader-utils@3.3.1: {}
+
+  local-pkg@0.5.0:
+    dependencies:
+      mlly: 1.7.1
+      pkg-types: 1.1.1
+
+  localstorage-polyfill@1.0.1: {}
+
+  locate-path@5.0.0:
+    dependencies:
+      p-locate: 4.1.0
+
+  locate-path@6.0.0:
+    dependencies:
+      p-locate: 5.0.0
+
+  lodash.camelcase@4.3.0: {}
+
+  lodash.debounce@4.0.8: {}
+
+  lodash.merge@4.6.2: {}
+
+  lodash@4.17.21: {}
+
+  lru-cache@5.1.1:
+    dependencies:
+      yallist: 3.1.1
+
+  magic-string@0.30.10:
+    dependencies:
+      '@jridgewell/sourcemap-codec': 1.4.15
+
+  make-dir@4.0.0:
+    dependencies:
+      semver: 7.6.2
+
+  makeerror@1.0.12:
+    dependencies:
+      tmpl: 1.0.5
+
+  media-typer@0.3.0: {}
+
+  merge-descriptors@1.0.1: {}
+
+  merge-stream@2.0.0: {}
+
+  merge2@1.4.1: {}
+
+  merge@2.1.1: {}
+
+  methods@1.1.2: {}
+
+  micromatch@4.0.7:
+    dependencies:
+      braces: 3.0.3
+      picomatch: 2.3.1
+
+  mime-db@1.52.0: {}
+
+  mime-types@2.1.35:
+    dependencies:
+      mime-db: 1.52.0
+
+  mime@1.6.0: {}
+
+  mime@3.0.0: {}
+
+  mimic-fn@2.1.0: {}
+
+  min-document@2.19.0:
+    dependencies:
+      dom-walk: 0.1.2
+
+  mini-html-parser2@0.3.0:
+    dependencies:
+      domhandler: 2.4.2
+      entities: 1.1.2
+      events: 3.3.0
+
+  minimatch@3.1.2:
+    dependencies:
+      brace-expansion: 1.1.11
+
+  minimatch@9.0.5:
+    dependencies:
+      brace-expansion: 2.0.1
+
+  minimist@1.2.8: {}
+
+  mkdirp@0.5.6:
+    dependencies:
+      minimist: 1.2.8
+
+  mlly@1.7.1:
+    dependencies:
+      acorn: 8.12.0
+      pathe: 1.1.2
+      pkg-types: 1.1.1
+      ufo: 1.5.3
+
+  module-alias@2.2.3: {}
+
+  ms@2.0.0: {}
+
+  ms@2.1.2: {}
+
+  ms@2.1.3: {}
+
+  muggle-string@0.3.1: {}
+
+  mute-stream@0.0.8: {}
+
+  nanoid@3.3.7: {}
+
+  natural-compare@1.4.0: {}
+
+  negotiator@0.6.3: {}
+
+  neo-async@2.6.2: {}
+
+  node-int64@0.4.0: {}
+
+  node-releases@2.0.14: {}
+
+  normalize-path@3.0.0: {}
+
+  normalize-range@0.1.2: {}
+
+  npm-run-path@4.0.1:
+    dependencies:
+      path-key: 3.1.1
+
+  nth-check@2.1.1:
+    dependencies:
+      boolbase: 1.0.0
+
+  nwsapi@2.2.10: {}
+
+  object-inspect@1.13.2: {}
+
+  object-keys@1.1.1: {}
+
+  object.assign@4.1.5:
+    dependencies:
+      call-bind: 1.0.7
+      define-properties: 1.2.1
+      has-symbols: 1.0.3
+      object-keys: 1.1.1
+
+  object.fromentries@2.0.8:
+    dependencies:
+      call-bind: 1.0.7
+      define-properties: 1.2.1
+      es-abstract: 1.23.3
+      es-object-atoms: 1.0.0
+
+  object.groupby@1.0.3:
+    dependencies:
+      call-bind: 1.0.7
+      define-properties: 1.2.1
+      es-abstract: 1.23.3
+
+  object.values@1.2.0:
+    dependencies:
+      call-bind: 1.0.7
+      define-properties: 1.2.1
+      es-object-atoms: 1.0.0
+
+  omggif@1.0.10: {}
+
+  on-finished@2.4.1:
+    dependencies:
+      ee-first: 1.1.1
+
+  once@1.4.0:
+    dependencies:
+      wrappy: 1.0.2
+
+  onetime@5.1.2:
+    dependencies:
+      mimic-fn: 2.1.0
+
+  optionator@0.9.4:
+    dependencies:
+      deep-is: 0.1.4
+      fast-levenshtein: 2.0.6
+      levn: 0.4.1
+      prelude-ls: 1.2.1
+      type-check: 0.4.0
+      word-wrap: 1.2.5
+
+  os-locale-s-fix@1.0.8-fix-1:
+    dependencies:
+      lcid: 3.1.1
+
+  os-tmpdir@1.0.2: {}
+
+  p-limit@2.3.0:
+    dependencies:
+      p-try: 2.2.0
+
+  p-limit@3.1.0:
+    dependencies:
+      yocto-queue: 0.1.0
+
+  p-locate@4.1.0:
+    dependencies:
+      p-limit: 2.3.0
+
+  p-locate@5.0.0:
+    dependencies:
+      p-limit: 3.1.0
+
+  p-try@2.2.0: {}
+
+  pako@1.0.11: {}
+
+  parent-module@1.0.1:
+    dependencies:
+      callsites: 3.1.0
+
+  parse-bmfont-ascii@1.0.6: {}
+
+  parse-bmfont-binary@1.0.6: {}
+
+  parse-bmfont-xml@1.1.6:
+    dependencies:
+      xml-parse-from-string: 1.0.1
+      xml2js: 0.5.0
+
+  parse-css-font@4.0.0:
+    dependencies:
+      css-font-size-keywords: 1.0.0
+      css-font-stretch-keywords: 1.0.1
+      css-font-style-keywords: 1.0.1
+      css-font-weight-keywords: 1.0.0
+      css-list-helpers: 2.0.0
+      css-system-font-keywords: 1.0.0
+      unquote: 1.1.1
+
+  parse-headers@2.0.5: {}
+
+  parse-json@5.2.0:
+    dependencies:
+      '@babel/code-frame': 7.24.7
+      error-ex: 1.3.2
+      json-parse-even-better-errors: 2.3.1
+      lines-and-columns: 1.2.4
+
+  parse5@6.0.1: {}
+
+  parseurl@1.3.3: {}
+
+  path-browserify@1.0.1: {}
+
+  path-exists@4.0.0: {}
+
+  path-is-absolute@1.0.1: {}
+
+  path-key@3.1.1: {}
+
+  path-parse@1.0.7: {}
+
+  path-to-regexp@0.1.7: {}
+
+  path-type@4.0.0: {}
+
+  pathe@1.1.2: {}
+
+  phin@2.9.3: {}
+
+  picocolors@1.0.1: {}
+
+  picomatch@2.3.1: {}
+
+  pify@2.3.0: {}
+
+  pinia-plugin-unistorage@0.0.19(typescript@4.9.5)(vue@3.4.30(typescript@4.9.5)):
+    dependencies:
+      pinia: 2.1.7(typescript@4.9.5)(vue@3.4.30(typescript@4.9.5))
+    transitivePeerDependencies:
+      - '@vue/composition-api'
+      - typescript
+      - vue
+
+  pinia@2.0.36(typescript@4.9.5)(vue@3.4.30(typescript@4.9.5)):
+    dependencies:
+      '@vue/devtools-api': 6.6.3
+      vue: 3.4.30(typescript@4.9.5)
+      vue-demi: 0.14.8(vue@3.4.30(typescript@4.9.5))
+    optionalDependencies:
+      typescript: 4.9.5
+
+  pinia@2.1.7(typescript@4.9.5)(vue@3.4.30(typescript@4.9.5)):
+    dependencies:
+      '@vue/devtools-api': 6.6.3
+      vue: 3.4.30(typescript@4.9.5)
+      vue-demi: 0.14.8(vue@3.4.30(typescript@4.9.5))
+    optionalDependencies:
+      typescript: 4.9.5
+
+  pirates@4.0.6: {}
+
+  pixelmatch@4.0.2:
+    dependencies:
+      pngjs: 3.4.0
+
+  pkg-dir@4.2.0:
+    dependencies:
+      find-up: 4.1.0
+
+  pkg-types@1.1.1:
+    dependencies:
+      confbox: 0.1.7
+      mlly: 1.7.1
+      pathe: 1.1.2
+
+  pngjs@3.4.0: {}
+
+  possible-typed-array-names@1.0.0: {}
+
+  postcss-import@14.1.0(postcss@8.4.38):
+    dependencies:
+      postcss: 8.4.38
+      postcss-value-parser: 4.2.0
+      read-cache: 1.0.0
+      resolve: 1.22.8
+
+  postcss-load-config@3.1.4(postcss@8.4.38):
+    dependencies:
+      lilconfig: 2.1.0
+      yaml: 1.10.2
+    optionalDependencies:
+      postcss: 8.4.38
+
+  postcss-modules-extract-imports@3.1.0(postcss@8.4.38):
+    dependencies:
+      postcss: 8.4.38
+
+  postcss-modules-local-by-default@4.0.5(postcss@8.4.38):
+    dependencies:
+      icss-utils: 5.1.0(postcss@8.4.38)
+      postcss: 8.4.38
+      postcss-selector-parser: 6.1.0
+      postcss-value-parser: 4.2.0
+
+  postcss-modules-scope@3.2.0(postcss@8.4.38):
+    dependencies:
+      postcss: 8.4.38
+      postcss-selector-parser: 6.1.0
+
+  postcss-modules-values@4.0.0(postcss@8.4.38):
+    dependencies:
+      icss-utils: 5.1.0(postcss@8.4.38)
+      postcss: 8.4.38
+
+  postcss-modules@4.3.1(postcss@8.4.38):
+    dependencies:
+      generic-names: 4.0.0
+      icss-replace-symbols: 1.1.0
+      lodash.camelcase: 4.3.0
+      postcss: 8.4.38
+      postcss-modules-extract-imports: 3.1.0(postcss@8.4.38)
+      postcss-modules-local-by-default: 4.0.5(postcss@8.4.38)
+      postcss-modules-scope: 3.2.0(postcss@8.4.38)
+      postcss-modules-values: 4.0.0(postcss@8.4.38)
+      string-hash: 1.1.3
+
+  postcss-selector-parser@6.1.0:
+    dependencies:
+      cssesc: 3.0.0
+      util-deprecate: 1.0.2
+
+  postcss-value-parser@4.2.0: {}
+
+  postcss@8.4.38:
+    dependencies:
+      nanoid: 3.3.7
+      picocolors: 1.0.1
+      source-map-js: 1.2.0
+
+  prelude-ls@1.2.1: {}
+
+  prettier-linter-helpers@1.0.0:
+    dependencies:
+      fast-diff: 1.3.0
+
+  prettier@3.3.2: {}
+
+  pretty-format@27.5.1:
+    dependencies:
+      ansi-regex: 5.0.1
+      ansi-styles: 5.2.0
+      react-is: 17.0.2
+
+  process@0.11.10: {}
+
+  prompts@2.4.2:
+    dependencies:
+      kleur: 3.0.3
+      sisteransi: 1.0.5
+
+  proxy-addr@2.0.7:
+    dependencies:
+      forwarded: 0.2.0
+      ipaddr.js: 1.9.1
+
+  psl@1.9.0: {}
+
+  punycode@2.3.1: {}
+
+  qrcode-reader@1.0.4: {}
+
+  qrcode-terminal@0.12.0: {}
+
+  qs@6.11.0:
+    dependencies:
+      side-channel: 1.0.6
+
+  querystringify@2.2.0: {}
+
+  queue-microtask@1.2.3: {}
+
+  range-parser@1.2.1: {}
+
+  raw-body@2.5.2:
+    dependencies:
+      bytes: 3.1.2
+      http-errors: 2.0.0
+      iconv-lite: 0.4.24
+      unpipe: 1.0.0
+
+  react-is@17.0.2: {}
+
+  read-cache@1.0.0:
+    dependencies:
+      pify: 2.3.0
+
+  readdirp@3.6.0:
+    dependencies:
+      picomatch: 2.3.1
+
+  regenerate-unicode-properties@10.1.1:
+    dependencies:
+      regenerate: 1.4.2
+
+  regenerate@1.4.2: {}
+
+  regenerator-runtime@0.13.11: {}
+
+  regenerator-runtime@0.14.1: {}
+
+  regenerator-transform@0.15.2:
+    dependencies:
+      '@babel/runtime': 7.24.7
+
+  regexp.prototype.flags@1.5.2:
+    dependencies:
+      call-bind: 1.0.7
+      define-properties: 1.2.1
+      es-errors: 1.3.0
+      set-function-name: 2.0.2
+
+  regexpu-core@5.3.2:
+    dependencies:
+      '@babel/regjsgen': 0.8.0
+      regenerate: 1.4.2
+      regenerate-unicode-properties: 10.1.1
+      regjsparser: 0.9.1
+      unicode-match-property-ecmascript: 2.0.0
+      unicode-match-property-value-ecmascript: 2.1.0
+
+  regjsparser@0.9.1:
+    dependencies:
+      jsesc: 0.5.0
+
+  require-directory@2.1.1: {}
+
+  requires-port@1.0.0: {}
+
+  resolve-cwd@3.0.0:
+    dependencies:
+      resolve-from: 5.0.0
+
+  resolve-from@4.0.0: {}
+
+  resolve-from@5.0.0: {}
+
+  resolve.exports@1.1.1: {}
+
+  resolve@1.22.8:
+    dependencies:
+      is-core-module: 2.14.0
+      path-parse: 1.0.7
+      supports-preserve-symlinks-flag: 1.0.0
+
+  restore-cursor@3.1.0:
+    dependencies:
+      onetime: 5.1.2
+      signal-exit: 3.0.7
+
+  reusify@1.0.4: {}
+
+  rimraf@3.0.2:
+    dependencies:
+      glob: 7.2.3
+
+  rollup@3.29.4:
+    optionalDependencies:
+      fsevents: 2.3.3
+
+  run-async@2.4.1: {}
+
+  run-parallel@1.2.0:
+    dependencies:
+      queue-microtask: 1.2.3
+
+  rxjs@6.6.7:
+    dependencies:
+      tslib: 1.14.1
+
+  safe-area-insets@1.4.1: {}
+
+  safe-array-concat@1.1.2:
+    dependencies:
+      call-bind: 1.0.7
+      get-intrinsic: 1.2.4
+      has-symbols: 1.0.3
+      isarray: 2.0.5
+
+  safe-buffer@5.2.1: {}
+
+  safe-regex-test@1.0.3:
+    dependencies:
+      call-bind: 1.0.7
+      es-errors: 1.3.0
+      is-regex: 1.1.4
+
+  safer-buffer@2.1.2: {}
+
+  sass-loader@14.2.1(sass@1.77.6):
+    dependencies:
+      neo-async: 2.6.2
+    optionalDependencies:
+      sass: 1.77.6
+
+  sass@1.77.6:
+    dependencies:
+      chokidar: 3.6.0
+      immutable: 4.3.6
+      source-map-js: 1.2.0
+
+  sax@1.4.1: {}
+
+  saxes@5.0.1:
+    dependencies:
+      xmlchars: 2.2.0
+
+  scule@1.3.0: {}
+
+  semver@6.3.1: {}
+
+  semver@7.6.2: {}
+
+  send@0.18.0:
+    dependencies:
+      debug: 2.6.9
+      depd: 2.0.0
+      destroy: 1.2.0
+      encodeurl: 1.0.2
+      escape-html: 1.0.3
+      etag: 1.8.1
+      fresh: 0.5.2
+      http-errors: 2.0.0
+      mime: 1.6.0
+      ms: 2.1.3
+      on-finished: 2.4.1
+      range-parser: 1.2.1
+      statuses: 2.0.1
+    transitivePeerDependencies:
+      - supports-color
+
+  serve-static@1.15.0:
+    dependencies:
+      encodeurl: 1.0.2
+      escape-html: 1.0.3
+      parseurl: 1.3.3
+      send: 0.18.0
+    transitivePeerDependencies:
+      - supports-color
+
+  set-function-length@1.2.2:
+    dependencies:
+      define-data-property: 1.1.4
+      es-errors: 1.3.0
+      function-bind: 1.1.2
+      get-intrinsic: 1.2.4
+      gopd: 1.0.1
+      has-property-descriptors: 1.0.2
+
+  set-function-name@2.0.2:
+    dependencies:
+      define-data-property: 1.1.4
+      es-errors: 1.3.0
+      functions-have-names: 1.2.3
+      has-property-descriptors: 1.0.2
+
+  setprototypeof@1.2.0: {}
+
+  shebang-command@2.0.0:
+    dependencies:
+      shebang-regex: 3.0.0
+
+  shebang-regex@3.0.0: {}
+
+  side-channel@1.0.6:
+    dependencies:
+      call-bind: 1.0.7
+      es-errors: 1.3.0
+      get-intrinsic: 1.2.4
+      object-inspect: 1.13.2
+
+  signal-exit@3.0.7: {}
+
+  sisteransi@1.0.5: {}
+
+  slash@3.0.0: {}
+
+  source-map-js@1.2.0: {}
+
+  source-map-support@0.5.21:
+    dependencies:
+      buffer-from: 1.1.2
+      source-map: 0.6.1
+
+  source-map@0.6.1: {}
+
+  source-map@0.7.4: {}
+
+  sprintf-js@1.0.3: {}
+
+  stack-utils@2.0.6:
+    dependencies:
+      escape-string-regexp: 2.0.0
+
+  statuses@2.0.1: {}
+
+  string-hash@1.1.3: {}
+
+  string-length@4.0.2:
+    dependencies:
+      char-regex: 1.0.2
+      strip-ansi: 6.0.1
+
+  string-width@4.2.3:
+    dependencies:
+      emoji-regex: 8.0.0
+      is-fullwidth-code-point: 3.0.0
+      strip-ansi: 6.0.1
+
+  string.prototype.trim@1.2.9:
+    dependencies:
+      call-bind: 1.0.7
+      define-properties: 1.2.1
+      es-abstract: 1.23.3
+      es-object-atoms: 1.0.0
+
+  string.prototype.trimend@1.0.8:
+    dependencies:
+      call-bind: 1.0.7
+      define-properties: 1.2.1
+      es-object-atoms: 1.0.0
+
+  string.prototype.trimstart@1.0.8:
+    dependencies:
+      call-bind: 1.0.7
+      define-properties: 1.2.1
+      es-object-atoms: 1.0.0
+
+  strip-ansi@6.0.1:
+    dependencies:
+      ansi-regex: 5.0.1
+
+  strip-bom@3.0.0: {}
+
+  strip-bom@4.0.0: {}
+
+  strip-final-newline@2.0.0: {}
+
+  strip-json-comments@3.1.1: {}
+
+  strip-literal@2.1.0:
+    dependencies:
+      js-tokens: 9.0.0
+
+  supports-color@5.5.0:
+    dependencies:
+      has-flag: 3.0.0
+
+  supports-color@7.2.0:
+    dependencies:
+      has-flag: 4.0.0
+
+  supports-color@8.1.1:
+    dependencies:
+      has-flag: 4.0.0
+
+  supports-hyperlinks@2.3.0:
+    dependencies:
+      has-flag: 4.0.0
+      supports-color: 7.2.0
+
+  supports-preserve-symlinks-flag@1.0.0: {}
+
+  svg-tags@1.0.0: {}
+
+  symbol-tree@3.2.4: {}
+
+  synckit@0.8.8:
+    dependencies:
+      '@pkgr/core': 0.1.1
+      tslib: 2.6.3
+
+  systemjs@6.15.1: {}
+
+  tapable@2.2.1: {}
+
+  terminal-link@2.1.1:
+    dependencies:
+      ansi-escapes: 4.3.2
+      supports-hyperlinks: 2.3.0
+
+  terser@5.31.1:
+    dependencies:
+      '@jridgewell/source-map': 0.3.6
+      acorn: 8.12.0
+      commander: 2.20.3
+      source-map-support: 0.5.21
+
+  test-exclude@6.0.0:
+    dependencies:
+      '@istanbuljs/schema': 0.1.3
+      glob: 7.2.3
+      minimatch: 3.1.2
+
+  text-table@0.2.0: {}
+
+  throat@6.0.2: {}
+
+  through@2.3.8: {}
+
+  timm@1.7.1: {}
+
+  tinycolor2@1.6.0: {}
+
+  tmp@0.0.33:
+    dependencies:
+      os-tmpdir: 1.0.2
+
+  tmpl@1.0.5: {}
+
+  to-fast-properties@2.0.0: {}
+
+  to-regex-range@5.0.1:
+    dependencies:
+      is-number: 7.0.0
+
+  toidentifier@1.0.1: {}
+
+  tough-cookie@4.1.4:
+    dependencies:
+      psl: 1.9.0
+      punycode: 2.3.1
+      universalify: 0.2.0
+      url-parse: 1.5.10
+
+  tr46@2.1.0:
+    dependencies:
+      punycode: 2.3.1
+
+  ts-api-utils@1.3.0(typescript@4.9.5):
+    dependencies:
+      typescript: 4.9.5
+
+  ts-md5@1.3.1: {}
+
+  tsconfig-paths@3.15.0:
+    dependencies:
+      '@types/json5': 0.0.29
+      json5: 1.0.2
+      minimist: 1.2.8
+      strip-bom: 3.0.0
+
+  tslib@1.14.1: {}
+
+  tslib@2.3.0: {}
+
+  tslib@2.6.3: {}
+
+  type-check@0.4.0:
+    dependencies:
+      prelude-ls: 1.2.1
+
+  type-detect@4.0.8: {}
+
+  type-fest@0.20.2: {}
+
+  type-fest@0.21.3: {}
+
+  type-is@1.6.18:
+    dependencies:
+      media-typer: 0.3.0
+      mime-types: 2.1.35
+
+  typed-array-buffer@1.0.2:
+    dependencies:
+      call-bind: 1.0.7
+      es-errors: 1.3.0
+      is-typed-array: 1.1.13
+
+  typed-array-byte-length@1.0.1:
+    dependencies:
+      call-bind: 1.0.7
+      for-each: 0.3.3
+      gopd: 1.0.1
+      has-proto: 1.0.3
+      is-typed-array: 1.1.13
+
+  typed-array-byte-offset@1.0.2:
+    dependencies:
+      available-typed-arrays: 1.0.7
+      call-bind: 1.0.7
+      for-each: 0.3.3
+      gopd: 1.0.1
+      has-proto: 1.0.3
+      is-typed-array: 1.1.13
+
+  typed-array-length@1.0.6:
+    dependencies:
+      call-bind: 1.0.7
+      for-each: 0.3.3
+      gopd: 1.0.1
+      has-proto: 1.0.3
+      is-typed-array: 1.1.13
+      possible-typed-array-names: 1.0.0
+
+  typedarray-to-buffer@3.1.5:
+    dependencies:
+      is-typedarray: 1.0.0
+
+  typescript@4.9.5: {}
+
+  ufo@1.5.3: {}
+
+  unbox-primitive@1.0.2:
+    dependencies:
+      call-bind: 1.0.7
+      has-bigints: 1.0.2
+      has-symbols: 1.0.3
+      which-boxed-primitive: 1.0.2
+
+  undici-types@5.26.5: {}
+
+  unicode-canonical-property-names-ecmascript@2.0.0: {}
+
+  unicode-match-property-ecmascript@2.0.0:
+    dependencies:
+      unicode-canonical-property-names-ecmascript: 2.0.0
+      unicode-property-aliases-ecmascript: 2.1.0
+
+  unicode-match-property-value-ecmascript@2.1.0: {}
+
+  unicode-property-aliases-ecmascript@2.1.0: {}
+
+  unimport@3.7.2(rollup@3.29.4):
+    dependencies:
+      '@rollup/pluginutils': 5.1.0(rollup@3.29.4)
+      acorn: 8.12.0
+      escape-string-regexp: 5.0.0
+      estree-walker: 3.0.3
+      fast-glob: 3.3.2
+      local-pkg: 0.5.0
+      magic-string: 0.30.10
+      mlly: 1.7.1
+      pathe: 1.1.2
+      pkg-types: 1.1.1
+      scule: 1.3.0
+      strip-literal: 2.1.0
+      unplugin: 1.10.1
+    transitivePeerDependencies:
+      - rollup
+
+  universalify@0.2.0: {}
+
+  universalify@2.0.1: {}
+
+  unpipe@1.0.0: {}
+
+  unplugin-auto-import@0.16.7(rollup@3.29.4):
+    dependencies:
+      '@antfu/utils': 0.7.10
+      '@rollup/pluginutils': 5.1.0(rollup@3.29.4)
+      fast-glob: 3.3.2
+      local-pkg: 0.5.0
+      magic-string: 0.30.10
+      minimatch: 9.0.5
+      unimport: 3.7.2(rollup@3.29.4)
+      unplugin: 1.10.1
+    transitivePeerDependencies:
+      - rollup
+
+  unplugin@1.10.1:
+    dependencies:
+      acorn: 8.12.0
+      chokidar: 3.6.0
+      webpack-sources: 3.2.3
+      webpack-virtual-modules: 0.6.2
+
+  unquote@1.1.1: {}
+
+  update-browserslist-db@1.0.16(browserslist@4.23.1):
+    dependencies:
+      browserslist: 4.23.1
+      escalade: 3.1.2
+      picocolors: 1.0.1
+
+  uri-js@4.4.1:
+    dependencies:
+      punycode: 2.3.1
+
+  url-parse@1.5.10:
+    dependencies:
+      querystringify: 2.2.0
+      requires-port: 1.0.0
+
+  utif@2.0.1:
+    dependencies:
+      pako: 1.0.11
+
+  util-deprecate@1.0.2: {}
+
+  utils-merge@1.0.1: {}
+
+  v8-to-istanbul@8.1.1:
+    dependencies:
+      '@types/istanbul-lib-coverage': 2.0.6
+      convert-source-map: 1.9.0
+      source-map: 0.7.4
+
+  vary@1.1.2: {}
+
+  vite@4.3.5(@types/node@20.14.9)(sass@1.77.6)(terser@5.31.1):
+    dependencies:
+      esbuild: 0.17.19
+      postcss: 8.4.38
+      rollup: 3.29.4
+    optionalDependencies:
+      '@types/node': 20.14.9
+      fsevents: 2.3.3
+      sass: 1.77.6
+      terser: 5.31.1
+
+  vue-demi@0.14.8(vue@3.4.30(typescript@4.9.5)):
+    dependencies:
+      vue: 3.4.30(typescript@4.9.5)
+
+  vue-eslint-parser@9.4.3(eslint@9.5.0):
+    dependencies:
+      debug: 4.3.5
+      eslint: 9.5.0
+      eslint-scope: 7.2.2
+      eslint-visitor-keys: 3.4.3
+      espree: 9.6.1
+      esquery: 1.5.0
+      lodash: 4.17.21
+      semver: 7.6.2
+    transitivePeerDependencies:
+      - supports-color
+
+  vue-router@4.4.0(vue@3.4.30(typescript@4.9.5)):
+    dependencies:
+      '@vue/devtools-api': 6.6.3
+      vue: 3.4.30(typescript@4.9.5)
+
+  vue-template-compiler@2.7.16:
+    dependencies:
+      de-indent: 1.0.2
+      he: 1.2.0
+
+  vue-tsc@1.8.27(typescript@4.9.5):
+    dependencies:
+      '@volar/typescript': 1.11.1
+      '@vue/language-core': 1.8.27(typescript@4.9.5)
+      semver: 7.6.2
+      typescript: 4.9.5
+
+  vue@3.4.30(typescript@4.9.5):
+    dependencies:
+      '@vue/compiler-dom': 3.4.30
+      '@vue/compiler-sfc': 3.4.30
+      '@vue/runtime-dom': 3.4.30
+      '@vue/server-renderer': 3.4.30(vue@3.4.30(typescript@4.9.5))
+      '@vue/shared': 3.4.30
+    optionalDependencies:
+      typescript: 4.9.5
+
+  w3c-hr-time@1.0.2:
+    dependencies:
+      browser-process-hrtime: 1.0.0
+
+  w3c-xmlserializer@2.0.0:
+    dependencies:
+      xml-name-validator: 3.0.0
+
+  walker@1.0.8:
+    dependencies:
+      makeerror: 1.0.12
+
+  webidl-conversions@5.0.0: {}
+
+  webidl-conversions@6.1.0: {}
+
+  webpack-sources@3.2.3: {}
+
+  webpack-virtual-modules@0.6.2: {}
+
+  whatwg-encoding@1.0.5:
+    dependencies:
+      iconv-lite: 0.4.24
+
+  whatwg-mimetype@2.3.0: {}
+
+  whatwg-url@8.7.0:
+    dependencies:
+      lodash: 4.17.21
+      tr46: 2.1.0
+      webidl-conversions: 6.1.0
+
+  which-boxed-primitive@1.0.2:
+    dependencies:
+      is-bigint: 1.0.4
+      is-boolean-object: 1.1.2
+      is-number-object: 1.0.7
+      is-string: 1.0.7
+      is-symbol: 1.0.4
+
+  which-typed-array@1.1.15:
+    dependencies:
+      available-typed-arrays: 1.0.7
+      call-bind: 1.0.7
+      for-each: 0.3.3
+      gopd: 1.0.1
+      has-tostringtag: 1.0.2
+
+  which@2.0.2:
+    dependencies:
+      isexe: 2.0.0
+
+  word-wrap@1.2.5: {}
+
+  wrap-ansi@7.0.0:
+    dependencies:
+      ansi-styles: 4.3.0
+      string-width: 4.2.3
+      strip-ansi: 6.0.1
+
+  wrappy@1.0.2: {}
+
+  write-file-atomic@3.0.3:
+    dependencies:
+      imurmurhash: 0.1.4
+      is-typedarray: 1.0.0
+      signal-exit: 3.0.7
+      typedarray-to-buffer: 3.1.5
+
+  ws@7.5.10: {}
+
+  ws@8.17.1: {}
+
+  xhr@2.6.0:
+    dependencies:
+      global: 4.4.0
+      is-function: 1.0.2
+      parse-headers: 2.0.5
+      xtend: 4.0.2
+
+  xml-name-validator@3.0.0: {}
+
+  xml-name-validator@4.0.0: {}
+
+  xml-parse-from-string@1.0.1: {}
+
+  xml2js@0.5.0:
+    dependencies:
+      sax: 1.4.1
+      xmlbuilder: 11.0.1
+
+  xmlbuilder@11.0.1: {}
+
+  xmlchars@2.2.0: {}
+
+  xmlhttprequest@1.8.0: {}
+
+  xregexp@3.1.0: {}
+
+  xtend@4.0.2: {}
+
+  y18n@5.0.8: {}
+
+  yallist@3.1.1: {}
+
+  yaml@1.10.2: {}
+
+  yargs-parser@20.2.9: {}
+
+  yargs@16.2.0:
+    dependencies:
+      cliui: 7.0.4
+      escalade: 3.1.2
+      get-caller-file: 2.0.5
+      require-directory: 2.1.1
+      string-width: 4.2.3
+      y18n: 5.0.8
+      yargs-parser: 20.2.9
+
+  yocto-queue@0.1.0: {}
+
+  zrender@5.5.0:
+    dependencies:
+      tslib: 2.3.0

+ 51 - 0
src/App.vue

@@ -0,0 +1,51 @@
+<script setup lang="ts">
+import { onLaunch, onShow, onHide } from "@dcloudio/uni-app";
+import { useUserStore } from "@/stores";
+import { getPermissions, login } from "@/api/base/client";
+const { proxy } = getCurrentInstance() as any;
+const {$isResolve} = proxy
+onLaunch(async () => {
+  const userStore = useUserStore()
+  const code = await getLoginCode()
+  const { token, info_status } = await login({ code: code as string, platform: 1 })
+  userStore.setLoginInfo(info_status, token)
+  const { list } = await getPermissions();
+  userStore.setPermissionsList(list)
+  await $isResolve()
+});
+
+const getLoginCode = () => {
+  return new Promise((resolve, reject) => {
+    uni.login({
+      success: async (res: { code: string, errMsg?: string }) => {
+        if (res.code) {
+          return resolve(res.code)
+        } else {
+          uni.showModal({
+            title: '登录失败!',
+            showCancel: false,
+            content: `${res.errMsg}`
+          })
+          return reject()
+        }
+      }
+    })
+  })
+}
+onShow(() => {
+  console.log("App Show");
+});
+onHide(() => {
+  console.log("App Hide");
+});
+</script>
+<style>
+@import "@/static/style/iconfont/my.css";
+
+page {
+  background-color: #edf0f9;
+}
+
+
+
+</style>

+ 54 - 0
src/api/acs/domain.ts

@@ -0,0 +1,54 @@
+import request from '@/utils/http';
+import { Domain } from '@/enums/api'
+import { DomainTreeResParams, AddDomainResParams } from '@/types/domain'
+
+/**
+ * 获取功能项列表
+ * @param pid 有此参数,只取此父域下的域树,否则获取自己权限下的域树。
+ */
+export const getDomainTree = (pid?: number) => {
+    return request.post<DomainTreeResParams>(
+        Domain.SYSTEM_DOMAIN_TREE,
+        { pid }
+    )
+}
+
+/**
+ * 添加域
+ * @param pid 父级域
+ * @param name 域名
+ * @param desc 域描述
+ */
+export const addDomain = (pid: number, name: string, desc?: string) => {
+    return request.post<AddDomainResParams>(
+        Domain.SYSTEM_DOMAIN_ADD,
+        { pid, name, desc }
+    )
+}
+/**
+ * 修改域
+ * @param domainId 当前选中域。id不可修改
+ * @param name 域名 可修改
+ * @param memo 域描述
+ */
+export const modifyDomain = (
+    domainId: number,
+    name?: string,
+    memo?: string
+) => {
+    return request.post<BaseResParams>(
+        Domain.SYSTEM_DOMAIN_MODIFY,
+        { id: domainId, name, memo }
+    )
+}
+
+/**
+ * 删除域
+ * @param domainId 当前选中域
+ */
+export const removeDomain = (domainId: number) => {
+    return request.post<BaseResParams>(
+        Domain.SYSTEM_DOMAIN_REMOVE,
+        { id: domainId }
+    )
+}

+ 66 - 0
src/api/base/base.ts

@@ -0,0 +1,66 @@
+import request from '@/utils/http';
+import { Base } from '@/enums/api'
+import { DomainTreeResParams } from '@/types/domain'
+
+export interface BaseApiType {
+  list: { id: string; name: string }[]
+}
+
+export interface PrjPhaseList {
+  list: { id: string; name: string; color: string }[]
+}
+export interface DocTypeList {
+  list: { id: string; name: string; color: string; allow_upload: boolean }[]
+}
+
+/**
+ * 获取客户级别信息列表
+ * @returns
+ */
+export const getCustomerLevelList = () => {
+  return request.post<BaseApiType>(
+     Base.CFG_CUSTOMER_LEVEL_LIST,
+     {}
+  )
+}
+
+/**
+ * 获取行业分类信息列表
+ * @returns
+ */
+export const getIndustryTypeList = () => {
+  return request.post<BaseApiType>(
+    Base.CFG_INDUSTRY_TYPE_LIST,
+     {}
+  )
+}
+
+// 获取项目类型列表
+export const getPrjTypeList = () => {
+  return request.post<DomainTreeResParams>(
+    Base.CFG_PRJ_TYPE_TREE,
+     {}
+  )
+}
+
+// 获取项目阶段列表
+export const getPrjPhaseList = () => {
+  return request.post<PrjPhaseList>(
+     Base.CFG_PRJ_PHASE_GET_LIST,
+     {}
+  )
+}
+
+export const getMemberRoleList = () => {
+  return request.post<BaseApiType>(
+ Base.CFG_PRJ_ROLE_GET_LIST,
+     {}
+  )
+}
+
+export const getDocTypeList = () => {
+  return request.post<DocTypeList>(
+    Base.CFG_DOC_TYPE_LIST,
+     {}
+  )
+}

+ 214 - 0
src/api/base/client.ts

@@ -0,0 +1,214 @@
+import http, { checkStatus } from '@/utils/http';
+import { Client, FileDownloadSheetAction } from '@/enums/api'
+import { LoginReqParams, LoginResParams, PermissonListResParams, SelfInfoResParams } from '@/types/client';
+
+
+/**
+ * 小程序注册到系统
+ * @param code 小程序提供的code,用于换取openid
+ */
+export const login = (data: LoginReqParams) => {
+    const url = Client.LOGIN;
+    return http.post<LoginResParams>(url, data)
+}
+
+/**
+ * 获取当前登录用户权限集
+ */
+export const getPermissions = async () => {
+    const url = Client.GET_PERMISSIONS
+    return http.post<PermissonListResParams>(url, {})
+}
+
+export const postUserInfo = async (data: { code: string, mobile: { iv: string, encryptedData: string } }) => {
+    const url = Client.AUTHORIZE_MOBILE
+    return http.post<PermissonListResParams>(url, data)
+}
+
+/**
+ * 获取当前登录用户个人信息
+ */
+export const getSelfInfo = () => {
+    return http.post<SelfInfoResParams>(
+        Client.GET_SELF_INFO,
+        {}
+    )
+}
+
+/**
+ * 设置当前登录用户个人信息
+ * @param params
+ */
+export const setSelfInfo = (params: {
+    name?: string
+    mobile?: string
+    email?: string
+    old_pass?: string
+    new_pass?: string
+}) => {
+    return http.post<BaseResParams>(
+        Client.SET_SELF_INFO,
+        params
+    )
+}
+
+
+
+/**
+ * 上传
+ * @param url
+ * @param params
+ * @returns
+ */
+export const uploadFile =  (params: {
+    url: string,
+    object_name: string,
+    headers?: { [key: string]: any },
+    formData?: { [key: string]: any },
+    filePath: string,
+    uploadProgress?: (progressEvent: UniApp.OnProgressUpdateResult, otherParams: any) => void,
+    timeout?: number | 36000
+    name?: string
+    otherParams?: any
+}) => {
+    return new Promise((resolve, reject) => {
+        try {
+            const { 
+                headers,
+                object_name,
+                url, //仅为示例,并非真实接口地址。
+                formData,
+                uploadProgress,
+                filePath,
+                name,
+                timeout,
+                otherParams
+            } = params;
+            const customFilename = name || 'file'
+            var uploadTask = uni.uploadFile({
+                url, //仅为示例,并非真实接口地址。
+                formData: {
+                    key: object_name,
+                    ...formData
+                },
+                headers,
+                filePath,
+                timeout,
+                name: customFilename,
+                fail: (e) => {
+                    uni.showModal({
+                        title: '上传失败',
+                        content: `${e.errMsg}`
+                    })
+                },
+                success: (uploadFileRes) => {
+                    checkStatus(uploadFileRes.statusCode)
+                    return resolve(uploadFileRes.data)
+                }
+            });
+            uploadProgress ? uploadTask.onProgressUpdate(res => uploadProgress(res, otherParams)) : null
+        } catch (e) {
+            reject(e)
+        }
+    })
+}
+
+
+/**
+ * 下载
+ * @param url
+ * @param params
+ * @returns
+ */
+export const downloadFile = (params: {
+    url: string,
+    downloadProgress?: (progressEvent: UniApp.OnProgressDownloadResult, otherParams: any) => void,
+    timeout?: number | 36000
+    otherParams?: any,
+    filename?: string
+}) => {
+    return new Promise<{ savedFilePath: string }>((resolve, reject) => {
+        try {
+            const { url, downloadProgress, timeout, otherParams, filename
+            } = params;
+            var downloadTask = uni.downloadFile({
+                url, //仅为示例,并非真实接口地址。
+                timeout,
+                fail: (e) => {
+                    uni.showModal({
+                        title: '下载失败',
+                        content: `${e.errMsg}`
+                    })
+                },
+                success: (downloadFileRes: UniApp.DownloadSuccessData) => {
+                    checkStatus(downloadFileRes.statusCode)
+                    return resolve({
+                        savedFilePath: downloadFileRes.tempFilePath
+                    })
+
+                }
+            });
+            downloadProgress ? downloadTask.onProgressUpdate(res => downloadProgress(res, otherParams)) : null
+        } catch (e) {
+            return reject(e)
+        }
+    })
+}
+
+
+export const saveFile = async (savedFilePath: string, filename?: string, sheetIndex?: FileDownloadSheetAction) => {
+    if (sheetIndex === FileDownloadSheetAction.SHARE) {
+        uni.shareFileMessage({
+            filePath: savedFilePath,
+            fileName: filename,
+            success: () => {
+                uni.showModal({
+                    title: `分享成功`
+                })
+                console.log('成功')
+            },
+            fail: (err: UniApp.GeneralCallbackResult) => {
+                uni.showModal({
+                    title: `分享失败`,
+                    content: err.errMsg
+                })
+                throw new Error(`分享失败:${err.errMsg}`)
+            }
+        })
+
+    } else {
+        uni.addFileToFavorites({
+            filePath: savedFilePath,
+            fileName: filename,
+            success: () => {
+                uni.showModal({
+                    title: `收藏成功`
+                })
+            },
+            fail: (err: UniApp.GeneralCallbackResult) => {
+                uni.showModal({
+                    title: `收藏失败`,
+                    content: err.errMsg
+                })
+                throw new Error(`收藏失败:${err.errMsg}`)
+            }
+        })
+    }
+}
+
+// export const collectFile = async (savedFilePath: string, filename?: string) => {
+//     uni.addFileToFavorites({
+//         filePath: savedFilePath,
+//         fileName: filename,
+//         success: () => {
+//             return uni.showModal({
+//                 title: `收藏成功`
+//             })
+//         },
+//         fail: (err: UniApp.GeneralCallbackResult) => {
+//             uni.showModal({
+//                 title: `收藏失败:${err.errMsg}`
+//             })
+//         }
+//     });
+// }

+ 64 - 0
src/api/base/region.ts

@@ -0,0 +1,64 @@
+import request from '@/utils/http'
+import { Base } from '@/enums/api'
+import { DomainTreeResParams, AddDomainResParams } from '@/types/domain'
+
+/**
+ * 获取区域树
+ */
+export const getRegionTree = () => {
+  return request.post<DomainTreeResParams>(
+     Base.CFG_REGION_TREE,
+     {}
+  )
+}
+
+/**
+ * 添加区域
+ * @param pid 上一级区域id,如果当前添加的是一级区域,此字段为空或为200
+ * @param name 新添加的区域名称
+ * @param memo 备注
+ */
+export const addRegion = (name: string, pid?: number, memo?: string) => {
+  return request.post<AddDomainResParams>(
+     Base.CFG_REGION_ADD,
+     {
+      pid,
+      name,
+      memo
+    }
+  )
+}
+
+/**
+ * 修改区域
+ * @param orgId 区域id
+ * @param name 修改后的区域名称
+ * @param memo 修改后的区域备注
+ */
+export const modifyRegion = (
+  regionId: number,
+  name?: string,
+  memo?: string
+) => {
+  return request.post<BaseResParams>(
+     Base.CFG_REGION_MODIFY,
+     {
+      id: regionId,
+      name,
+      memo
+    }
+  )
+}
+
+/**
+ * 删除区域
+ * @param regionId 区域id
+ */
+export const removeRegion = (regionId: number) => {
+  return request.post<BaseResParams>(
+     Base.CFG_REGION_REMOVE,
+     {
+      id: regionId
+    }
+  )
+}

+ 77 - 0
src/api/biz/customer.ts

@@ -0,0 +1,77 @@
+import http from '@/utils/http';
+import { Customer } from '@/enums/api'
+import {
+    AddCustomerReqParams,
+    CustomerListReqParams,
+    ModifyCustomerReqParams,
+    CustomerListResParams,
+    AddCustomerResParams,
+    CustomerDetailResParams
+} from '@/types/customer'
+
+/**
+ * 获取客户信息列表
+ * @param params RequestCustomerListParams
+ * @returns
+ */
+export const getCustomerList = (data: CustomerListReqParams) => {
+    const url = Customer.BIZ_CUSTOMER_LIST
+    return http.post<CustomerListResParams>(
+        url,
+        data
+    )
+}
+
+/**
+ * 添加客户
+ * @param params RequestAddCustomerParams
+ * @returns
+ */
+export const addCustomer = (data: AddCustomerReqParams) => {
+    const url = Customer.BIZ_CUSTOMER_ADD;
+    return http.post<AddCustomerResParams>(
+        url,
+        data
+    )
+}
+
+/**
+ * 修改客户信息
+ * @param params RequestModifyCustomerParams
+ * @returns
+ */
+export const modifyCustomer = (data: ModifyCustomerReqParams) => {
+    const url = Customer.BIZ_CUSTOMER_MODIFY;
+    return http.post<BaseResParams>(
+        url,
+        data
+    )
+}
+
+/**
+ * 删除客户
+ * @param id 客户编号
+ * @returns
+ */
+export const removeCustomer = (id: string) => {
+    const url = Customer.BIZ_CUSTOMER_REMOVE;
+    const data = {id}
+    return http.post<BaseResParams>(
+        url,
+        data
+    )
+}
+
+/**
+ * 获取客户详情
+ * @param id 客户编号
+ * @returns
+ */
+export const getCustomerDetail = (id: string) => {
+    const url = Customer.BIZ_CUSTOMER_DETAIL;
+    const data = {id}
+    return http.post<CustomerDetailResParams>(
+        url,
+        data
+    )
+}

+ 82 - 0
src/api/biz/staff.ts

@@ -0,0 +1,82 @@
+import request from '@/utils/http';
+import { Base } from '@/enums/api'
+import {
+    StaffListResParams,
+    StaffListReqParams,
+    ModifyStaffReqParams,
+    AddStaffReqParams
+} from '@/types/staff'
+
+/**
+ * 获取员工列表
+ * @param params
+ * @returns
+ */
+export const getStaffList = (params: StaffListReqParams) => {
+
+//     return {list: [
+//         {
+//         id: '13217000816',
+//         name:'公司管理员'
+//       },
+//       {
+//         id: '15669068030',
+//         name:'dsdsd'
+//       },
+//       {
+//         id: '13100078512',
+//         name:'项目组成员'
+//       },
+//       {
+//         id: '13217000876',
+//         name:'商务负责人'
+//       },
+//       {
+//         id: '13217000875',
+//         name:'项目负责人'
+//       }
+// ]}
+const url = Base.CFG_STAFF_LIST
+    return request.post<StaffListResParams>(
+        url,
+        params
+    )
+}
+
+/**
+ * 修改员工信息
+ * @param params
+ * @returns
+ */
+export const modifyStaff = (params: ModifyStaffReqParams) => {
+    const url = Base.CFG_STAFF_MODIFY
+    return request.post<BaseResParams>(
+        url,
+        params
+    )
+}
+
+/**
+ * 删除员工
+ * @param id
+ * @returns
+ */
+export const removeStaff = (id: string) => {
+    const url = Base.CFG_STAFF_MODIFY
+    return request.post<BaseResParams>(
+        url,
+        { id }
+    )
+}
+
+/**
+ * 添加员工
+ * @param params
+ * @returns
+ */
+export const addStaff = (params: AddStaffReqParams) => {
+    return request.post<BaseResParams>(
+        Base.CFG_STAFF_ADD,
+        params
+    )
+}

+ 59 - 0
src/api/dashboard/project.ts

@@ -0,0 +1,59 @@
+import request from '@/utils/http'
+import { Dashboard } from '@/enums/api'
+import {
+  ProjectCategoryStatResParams,
+  ProjectMonthCountStatReqParams,
+  ProjectMonthCountStatResParams,
+  ProjectMultiStatResParams,
+  ProjectPlanStatReqParams,
+  ProjectPlanStatResParams
+} from '@/types/dashboard'
+
+/**
+ * 获取项目分类统计
+ * @returns
+ */
+export const getProjectCategoryStat = () => {
+  return request.post<ProjectCategoryStatResParams>(
+     Dashboard.REPORT_PRJOJECT_CAT_STAT,
+     {}
+  )
+}
+
+/**
+ * 获取项目计划统计
+ * @param ProjectPlanStatReqParams
+ * @returns
+ */
+export const getProjectPlanStat = (params: ProjectPlanStatReqParams) => {
+  return request.post<ProjectPlanStatResParams>(
+    Dashboard.REPORT_PRJOJECT_PLAN_STAT,
+     params
+  )
+}
+
+/**
+ * 获取项目数量累计,每月底计算一次累计值,每月一条数据
+ * @param ProjectMonthCountStatReqParams
+ * @returns
+ */
+export const getProjectMonthCountStat = (
+  params: ProjectMonthCountStatReqParams
+) => {
+  return request.post<ProjectMonthCountStatResParams>(
+     Dashboard.REPORT_PRJOJECT_COUNT_STAT,
+     params
+  )
+}
+
+/**
+ * 获取项目数量累计,每月底计算一次累计值,每月一条数据
+ * @param ProjectMonthCountStatReqParams
+ * @returns
+ */
+export const getProjectMultiStat = () => {
+  return request.post<ProjectMultiStatResParams>(
+    Dashboard.REPORT_PRJOJECT_MULTI_STAT,
+     {}
+  )
+}

+ 114 - 0
src/api/project-center/contract.ts

@@ -0,0 +1,114 @@
+import request from '@/utils/http'
+import { Project } from '@/enums/api'
+import {
+  ContractListResParams,
+  ContractListReqParams,
+  AddContractReqParams,
+  AddContractResParams,
+  ModifyContractReqParams,
+  ContractDetailResParams,
+  ContractDownloadDocUrlReqParams,
+  ContractDownloadDocUrlResParams,
+  ContractUploadDocUrlReqParams,
+  ContractUploadDocUrlResParams,
+  RemoveContractDocUrlReqParams
+} from '@/types/contract'
+
+/**
+ * 获取合同列表
+ * @param params ContractListReqParams
+ */
+export const getContractList = (params: ContractListReqParams) => {
+  return request.post<ContractListResParams>(
+    Project.PROJECT_CONTRACT_LIST,
+    params
+  )
+}
+
+/**
+ * 添加合同信息
+ * @param params AddContractReqParams
+ * @returns
+ */
+export const addContract = (params: AddContractReqParams) => {
+  return request.post<AddContractResParams>(
+    Project.PROJECT_CONTRACT_ADD,
+    params
+  )
+}
+
+/**
+ * 修改合同信息
+ * @param params ModifyContractReqParams
+ * @returns
+ */
+export const modifyContract = (params: ModifyContractReqParams) => {
+  return request.post<BaseResParams>(
+    Project.PROJECT_CONTRACT_MODIFY,
+    params
+  )
+}
+
+/**
+ * 删除合同信息
+ * @param id 合同编号
+ * @returns
+ */
+export const removeContract = (id: string) => {
+  return request.post<BaseResParams>(
+    Project.PROJECT_CONTRACT_REMOVE,
+    { id }
+  )
+}
+
+/**
+ * 获取合同概览
+ * @param id 合同编号
+ * @returns
+ */
+export const getContractDetail = (id: string) => {
+  return request.post<ContractDetailResParams>(
+    Project.PROJECT_CONTRACT_DETAIL,
+    { id }
+  )
+}
+
+/**
+ * 获取上传合同附件的url。
+ * @param params ContractUploadDocUrlResParams
+ * @returns
+ */
+export const getUploadContractFileUrl = (
+  params: ContractUploadDocUrlReqParams
+) => {
+  return request.post<ContractUploadDocUrlResParams>(
+    Project.PROJECT_CONTRACT_UPLOAD_DOC,
+    params
+  )
+}
+
+/**
+ * 获取下载合同附件的url。
+ * @param params ContractDownloadDocUrlReqParams
+ * @returns
+ */
+export const getDownloadContractFileUrl = (
+  params: ContractDownloadDocUrlReqParams
+) => {
+  return request.post<ContractDownloadDocUrlResParams>(
+    Project.PROJECT_CONTRACT_DOWNLOAD_DOC,
+    params
+  )
+}
+
+/**
+ * 用于删除已上传的合同附件。已归档的附件不允许修改。
+ * @param params
+ * @returns
+ */
+export const removeContractFile = (params: RemoveContractDocUrlReqParams) => {
+  return request.post<BaseResParams>(
+    Project.PROJECT_CONTRACT_REMOVE_DOC,
+    params
+  )
+}

+ 99 - 0
src/api/project-center/document.ts

@@ -0,0 +1,99 @@
+import request from '@/utils/http';
+import { Project } from '@/enums/api'
+import {
+  DocumentDetailResParams,
+  DocumentListReqParams,
+  DocumentListResParams,
+  DownloadDocumentReqParams,
+  DownloadDocumentResParams,
+  UploadDocumentReqParams,
+  UploadDocumentResParams
+} from '@/types/document'
+
+/**
+ * 获取项目文档列表
+ * @param params DocumentListReqParams
+ * @returns DocumentListResParams
+ */
+export const getDocumentList = (params: DocumentListReqParams) => {
+  return request.post<DocumentListResParams>(
+    Project.PROJECT_DOC_LIST,
+     params
+  )
+}
+
+/**
+ * 获取上传项目文档Url
+ * @param params UploadDocumentReqParams
+ * @returns UploadDocumentResParams
+ */
+export const getUploadDocumentUrl = (params: UploadDocumentReqParams) => {
+  return request.post<UploadDocumentResParams>(
+    Project.PROJECT_DOC_UPLOAD,
+    params
+  )
+}
+
+/**
+ * 获取下载项目文档Url
+ * @param params DownloadDocumentReqParams
+ * @returns DownloadDocumentResParams
+ */
+export const getDownloadDocumentUrl = (params: DownloadDocumentReqParams) => {
+  return request.post<DownloadDocumentResParams>(
+     Project.PROJECT_DOC_DOWNLOAD,
+     params
+  )
+}
+
+/**
+ * 删除项目文档
+ * @param params
+ * @returns
+ */
+export const removeDocument = (params: { id: string }) => {
+  return request.post<BaseResParams>(
+    Project.PROJECT_DOC_REOMOVE,
+    params
+  )
+}
+
+
+
+/**
+ * 删除项目文档
+ * @param params
+ * @returns
+ */
+export const getDocumentDetail = (params: { id: string }) => {
+  return request.post<DocumentDetailResParams>(
+    Project.PROJECT_DOC_DETAIL,
+    params
+  )
+}
+
+
+
+// 归档文件
+export const archiveDocument = (params: { id: string; archive: boolean }) => {
+  return request.post<BaseResParams>(
+     Project.PROJECT_DOC_ARCHIVE,
+    params
+  )
+}
+
+// 复制文件
+export const copyDocument = (params: { id: string; target: string }) => {
+  return request.post<BaseResParams>(
+     Project.PROJECT_DOC_COPY,
+     params
+  )
+}
+
+// 重命名
+export const renameDocument = (params: { id: string; filename: string }) => {
+  return request.post<BaseResParams>(
+    Project.PROJECT_DOC_RENAME,
+    params
+  )
+}

+ 125 - 0
src/api/project-center/info.ts

@@ -0,0 +1,125 @@
+import request from '@/utils/http';
+import { Project } from '@/enums/api'
+import {
+    ProjectListResParams,
+    ProjectListReqParams,
+    AddProjectReqParams,
+    AddProjectResParams,
+    ModifyProjectReqParams,
+    OverviewProjectResParams,
+    ProjectLogsReqParams,
+    ProjectLogsResParams,
+    ProjectMilestoneResParams,
+    ProjectCheckersResParams,
+    Checker
+} from '@/types/project'
+
+/**
+ * 获取项目列表
+ * @param params RequestProjectListParams
+ */
+export const getProjectList = (params: ProjectListReqParams) => {
+    return request.post<ProjectListResParams>(
+        Project.PROJECT_INFO_LIST,
+        params
+    )
+}
+
+/**
+ * 添加项目信息
+ * @param params
+ * @returns
+ */
+export const addProject = (params: AddProjectReqParams) => {
+    return request.post<AddProjectResParams>(
+        Project.PROJECT_INFO_ADD,
+        params
+    )
+}
+
+/**
+ * 修改项目信息
+ * @param params
+ * @returns
+ */
+export const modifyProject = (params: ModifyProjectReqParams) => {
+    return request.post<BaseResParams>(
+        Project.PROJECT_INFO_MODIFY,
+        params
+    )
+}
+
+/**
+ * 删除项目信息
+ * @param id 项目编号
+ * @returns
+ */
+export const removeProject = (id: string) => {
+    return request.post<BaseResParams>(
+        Project.PROJECT_INFO_REMOVE,
+        { id }
+    )
+}
+
+/**
+ * 获取项目概览
+ * @param id 项目编号
+ * @returns
+ */
+export const overviewProject = (id: string) => {
+    return request.post<OverviewProjectResParams>(
+        Project.PROJECT_INFO_OVERVIEW,
+        { id }
+    )
+}
+
+/**
+ * 获取项目动态列表
+ * @param params ProjectLogsReqParams
+ * @returns
+ */
+export const getProjectLogs = (params: ProjectLogsReqParams) => {
+    return request.post<ProjectLogsResParams>(
+        Project.PROJECT_INFO_LOGS,
+        params
+    )
+}
+
+/**
+ *
+ * @param params
+ * @returns
+ */
+export const getProjectMilestone = (params: { prj_id: string }) => {
+    return request.post<ProjectMilestoneResParams>(
+        Project.PROJECT_KANBAN_MILESTONE,
+        params
+    )
+}
+
+/**
+ * 获取项目审核人列表
+ * @param params
+ * @returns
+ */
+export const getProjectCheckers = (params: { id: string }) => {
+    return request.post<ProjectCheckersResParams>(
+        Project.PROJECT_INFO_GET_CHECKERS,
+        params
+    )
+}
+
+/**
+ * 获取项目审核人列表
+ * @param params
+ * @returns
+ */
+export const setProjectCheckers = (params: {
+    id: string
+    checkers: { id: string; name: string }[]
+}) => {
+    return request.post<BaseResParams>(
+        Project.PROJECT_INFO_SET_CHECKERS,
+        params
+    )
+}

+ 98 - 0
src/api/project-center/member.ts

@@ -0,0 +1,98 @@
+import request from '@/utils/http'
+import { Project } from '@/enums/api'
+import {
+  MemberListReqParams,
+  MemberListResParams,
+  AddMemberReqParams,
+  ApplyRemoveMemberReqParams,
+  ApplyAddMemberResParams,
+  ApplyAddMemberReqParams,
+  ApplyRemoveMemberResParams,
+  ModifyMemberReqParams,
+  RemoveMemberReqParams,
+  CheckMemberReqParams
+} from '@/types/member'
+
+/**
+ * 获取项目组成员列表
+ * @param params MemberListReqParams
+ * @returns MemberListResParams
+ */
+export const getMemberList = (params: MemberListReqParams) => {
+  return request.post<MemberListResParams>(
+    Project.PROJECT_MEMBER_LIST,
+    params
+  )
+}
+
+/**
+ * 添加项目组成员
+ * @param params MemberListReqParams
+ * @returns BaseResParams
+ */
+export const addMember = (params: AddMemberReqParams) => {
+  return request.post<BaseResParams>(
+    Project.PROJECT_MEMBER_ADD,
+    params
+  )
+}
+
+/**
+ * 修改项目组成员
+ * @param params ModifyMemberReqParams
+ * @returns BaseResParams
+ */
+export const modifyMember = (params: ModifyMemberReqParams) => {
+  return request.post<BaseResParams>(
+    Project.PROJECT_MEMBER_MODIFY,
+    params
+  )
+}
+
+/**
+ * 删除项目组成员
+ * @param params ModifyMemberReqParams
+ * @returns BaseResParams
+ */
+export const removeMember = (params: RemoveMemberReqParams) => {
+  return request.post<BaseResParams>(
+    Project.PROJECT_MEMBER_REMOVE,
+    params
+  )
+}
+
+/**
+ * 申请添加项目组成员
+ * @param params ApplyAddMemberReqParams
+ * @returns ApplyAddMemberResParams
+ */
+export const applyAddMember = (params: ApplyAddMemberReqParams) => {
+  return request.post<ApplyAddMemberResParams>(
+    Project.PROJECT_MEMBER_APPLY_ADD,
+    params
+  )
+}
+
+/**
+ * 申请删除项目组成员
+ * @param params ApplyRemoveMemberReqParams
+ * @returns ApplyAddMemberResParams
+ */
+export const applyRemoveMember = (params: ApplyRemoveMemberReqParams) => {
+  return request.post<ApplyRemoveMemberResParams>(
+    Project.PROJECT_MEMBER_APPLY_REMOVE,
+    params
+  )
+}
+
+/**
+ * 申请添加或移除项目组成员后,审核是否通过。
+ * @param params CheckMemberReqParams
+ * @returns BaseResParams
+ */
+export const checkMember = (params: CheckMemberReqParams) => {
+  return request.post<BaseResParams>(
+    Project.PROJECT_MEMBER_CHECK,
+    params
+  )
+}

+ 112 - 0
src/api/project-center/outcome.ts

@@ -0,0 +1,112 @@
+import request from '@/utils/http'
+import { Project } from '@/enums/api'
+import {
+  OutcomeListResParams,
+  OutcomeListReqParams,
+  AddOutcomeReqParams,
+  AddOutcomeResParams,
+  RemoveOutcomeReqParams,
+  ModifyOutcomeReqParams,
+  OutcomeDetailReqParams,
+  OutcomeDetailResParams,
+  OutcomeUploadUrlReqParams,
+  OutcomeUploadUrlResParams,
+  RemoveOutcomeFileReqParams,
+  OutcomeDownloadUrlReqParams,
+  OutcomeDownloadUrlResParams
+} from '@/types/outcome'
+
+/**
+ * 获取任务交付物列表
+ * @param params OUTCOMEListReqParams
+ */
+export const getOutcomeList = (params: OutcomeListReqParams) => {
+  return request.post<OutcomeListResParams>(
+    Project.PROJECT_OUTCOME_LIST,
+    params
+  )
+}
+
+/**
+ * 添加任务交付物信息
+ * @param params AddOUTCOMEReqParams
+ * @returns
+ */
+export const addOutcome = (params: AddOutcomeReqParams) => {
+  return request.post<AddOutcomeResParams>(
+    Project.PROJECT_OUTCOME_ADD,
+    params
+  )
+}
+
+/**
+ * 修改任务交付物信息
+ * @param params ModifyOUTCOMEReqParams
+ * @returns
+ */
+export const modifyOutcome = (params: ModifyOutcomeReqParams) => {
+  return request.post<BaseResParams>(
+    Project.PROJECT_OUTCOME_MODIFY,
+    params
+  )
+}
+
+/**
+ * 删除任务交付物信息
+ * @param id 任务交付物编号
+ * @returns
+ */
+export const removeOutcome = (params: RemoveOutcomeReqParams) => {
+  return request.post<BaseResParams>(
+    Project.PROJECT_OUTCOME_REMOVE,
+    params
+  )
+}
+
+/**
+ * 获取任务交付物详情
+ * @param params OUTCOMEDetailReqParams
+ * @returns
+ */
+export const getOutcomeDetail = (params: OutcomeDetailReqParams) => {
+  return request.post<OutcomeDetailResParams>(
+    Project.PROJECT_OUTCOME_DETAIL,
+    params
+  )
+}
+
+/**
+ * 获取上传任务交付物附件的url。
+ * @param params OUTCOMEUploadDocUrlResParams
+ * @returns
+ */
+export const getUploadOutcomeUrl = (params: OutcomeUploadUrlReqParams) => {
+  return request.post<OutcomeUploadUrlResParams>(
+    Project.PROJECT_OUTCOME_UPLOAD,
+    params
+  )
+}
+
+/**
+ * 删除交付物文件
+ * @param params
+ * @returns
+ */
+export const removeOutcomeFile = (params: RemoveOutcomeFileReqParams) => {
+  return request.post<BaseResParams>(
+    Project.PROJECT_OUTCOME_REMOVE_FILE,
+    params
+  )
+}
+
+/**
+ * 获取上传任务交付物附件的url。
+ * @param params OUTCOMEUploadDocUrlResParams
+ * @returns
+ */
+export const getDownloadOutcomeUrl = (params: OutcomeDownloadUrlReqParams) => {
+  return request.post<OutcomeDownloadUrlResParams>(
+    Project.PROJECT_OUTCOME_DOWNLOAD,
+    params
+  )
+}

+ 183 - 0
src/api/project-center/plan-task.ts

@@ -0,0 +1,183 @@
+import request from '@/utils/http'
+import { Project } from '@/enums/api'
+import {
+  TaskListResParams,
+  TaskListReqParams,
+  AddTaskReqParams,
+  AddTaskResParams,
+  ModifyTaskReqParams,
+  TaskDetailResParams,
+  PlanDetailReqParams,
+  PlanDetailResParams,
+  RemoveTaskReqParams,
+  SortTaskReqParams,
+  CancelTaskReqParams,
+  CancelTaskResParams,
+  TaskDetailReqParams,
+  TaskLogsReqParams,
+  GetTaskFlowsResParams,
+  SetTaskFlowsReqParams,
+  GetPlanLogsReqParams,
+  GetPlanLogsResParams
+} from '@/types/plan-task'
+
+/**
+ * 获取计划任务列表
+ * @param params TaskListReqParams
+ */
+export const getPlanTaskList = (params: TaskListReqParams) => {
+  return request.post<TaskListResParams>(
+    Project.PROJECT_PLAN_TASK_LIST,
+    params
+  )
+}
+
+/**
+ * 添加计划任务信息
+ * @param params AddTaskReqParams
+ * @returns
+ */
+export const addPlanTask = (params: AddTaskReqParams) => {
+  return request.post<AddTaskResParams>(
+    Project.PROJECT_PLAN_TASK_ADD,
+    params
+  )
+}
+
+/**
+ * 获取计划详情
+ * @param params PlanDetailReqParams
+ * @returns
+ */
+export const getPlanDetail = (params: PlanDetailReqParams) => {
+  return request.post<PlanDetailResParams>(
+    Project.PROJECT_PLAN_DETAIL,
+    params
+  )
+}
+
+/**
+ * 修改计划任务信息
+ * @param params ModifyTaskReqParams
+ * @returns
+ */
+export const modifyPlanTask = (params: ModifyTaskReqParams) => {
+  return request.post<BaseResParams>(
+    Project.PROJECT_PLAN_TASK_MODIFY,
+    params
+  )
+}
+
+/**
+ * 删除计划任务信息
+ * @param id 计划任务编号
+ * @returns
+ */
+export const removePlanTask = (params: RemoveTaskReqParams) => {
+  return request.post<BaseResParams>(
+    Project.PROJECT_PLAN_TASK_REMOVE,
+    params
+  )
+}
+
+/**
+ * 调整任务项顺序
+ * @param params SortTaskReqParams
+ * @returns
+ */
+export const sortPlanTask = (params: SortTaskReqParams) => {
+  return request.post<BaseResParams>(
+    Project.PROJECT_PLAN_TASK_SORT,
+    params
+  )
+}
+
+/**
+ * 中止任务项
+ * @param params CancelTaskReqParams
+ * @returns
+ */
+export const cancelPlanTask = (params: CancelTaskReqParams) => {
+  return request.post<CancelTaskResParams>(
+    Project.PROJECT_PLAN_TASK_CANCEL,
+    params
+  )
+}
+
+/**
+ * 获取计划任务详情
+ * @param params TaskDetailReqParams
+ * @returns
+ */
+export const getPlanTaskDetail = (params: TaskDetailReqParams) => {
+  return request.post<TaskDetailResParams>(
+    Project.PROJECT_PLAN_TASK_DETAIL,
+    params
+  )
+}
+
+/**
+ * 获取项目计划中某个任务的执行动态,即日志。
+ * @param params TaskLogsReqParams
+ * @returns
+ */
+export const getPlanTaskLogs = (params: TaskLogsReqParams) => {
+  return request.post<TaskLogsReqParams>(
+    Project.PROJECT_PLAN_TASK_LOGS,
+    params
+  )
+}
+
+/**
+ * 获取项目计划中某个任务的执行动态,即日志。
+ * @param params TaskLogsReqParams
+ * @returns
+ */
+export const setPlanProgress = (params: {
+  task_id: string
+  progress: number
+}) => {
+  return request.post<BaseResParams>(
+    Project.PROJECT_PLAN_SET_PROGRESS,
+    params
+  )
+}
+
+/**
+ * 获取计划审核流程列表
+ * @param params
+ * @returns
+ */
+export const getPlanCheckFlow = (params: {
+  draft: boolean
+  task_id: string
+}) => {
+  return request.post<GetTaskFlowsResParams>(
+    Project.PROJECT_PLAN_GET_CHECK_FLOW,
+    params
+  )
+}
+
+/**
+ * 设置计划审核流程列表
+ * @param params
+ * @returns
+ */
+export const setPlanCheckFlow = (params: SetTaskFlowsReqParams) => {
+  return request.post<BaseResParams>(
+    Project.PROJECT_PLAN_SET_CHECK_FLOW,
+    params
+  )
+}
+
+/**
+ * 获取任务动态
+ * @param params
+ * @returns
+ */
+export const getPlanLogs = (params: GetPlanLogsReqParams) => {
+  return request.post<GetPlanLogsResParams>(
+    Project.PROJECT_PLAN_LOGS,
+    params
+  )
+}

+ 139 - 0
src/api/project-center/week-report.ts

@@ -0,0 +1,139 @@
+import request from '@/utils/http'
+import { Project } from '@/enums/api'
+import {
+  WeekReportListResParams,
+  WeekReportListReqParams,
+  AddWeekReportReqParams,
+  AddWeekReportResParams,
+  ModifyWeekReportReqParams,
+  WeekReportDetailResParams,
+  WeekReportDownloadFileUrlReqParams,
+  WeekReportDownloadFileUrlResParams,
+  WeekReportUploadFileUrlReqParams,
+  WeekReportUploadFileUrlResParams,
+  CommitWeekReportReqParams,
+  WithdrawWeekReportReqParams
+} from '@/types/week-report'
+
+/**
+ * 获取周报列表
+ * @param params WeekReportListReqParams
+ */
+export const getWeekReportList = (params: WeekReportListReqParams) => {
+  return request.post<WeekReportListResParams>(
+    Project.PROJECT_WEEK_REPORT_LIST,
+    params
+  )
+}
+
+/**
+ * 添加周报信息
+ * @param params AddWeekReportReqParams
+ * @returns
+ */
+export const addWeekReport = (params: AddWeekReportReqParams) => {
+  return request.post<AddWeekReportResParams>(
+    Project.PROJECT_WEEK_REPORT_ADD,
+    params
+  )
+}
+
+/**
+ * 修改周报信息
+ * @param params ModifyWeekReportReqParams
+ * @returns
+ */
+export const modifyWeekReport = (params: ModifyWeekReportReqParams) => {
+  return request.post<BaseResParams>(
+    Project.PROJECT_WEEK_REPORT_MODIFY,
+    params
+  )
+}
+
+/**
+ * 删除周报信息
+ * @param id 周报编号
+ * @returns
+ */
+export const removeWeekReport = (id: string) => {
+  return request.post<BaseResParams>(
+    Project.PROJECT_WEEK_REPORT_REMOVE,
+    { id }
+  )
+}
+
+/**
+ * 获取周报概览
+ * @param id 周报编号
+ * @returns
+ */
+export const getWeekReportDetail = (id: string) => {
+  return request.post<WeekReportDetailResParams>(
+    Project.PROJECT_WEEK_REPORT_DETAIL,
+    { id }
+  )
+}
+
+/**
+ * 获取上传周报附件的url。
+ * @param params WeekReportUploadFileUrlResParams
+ * @returns
+ */
+export const getUploadWeekReportFileUrl = (
+  params: WeekReportUploadFileUrlReqParams
+) => {
+  return request.post<WeekReportUploadFileUrlResParams>(
+    Project.PROJECT_WEEK_REPORT_UPLOAD_FILE,
+    params
+  )
+}
+
+/**
+ * 获取下载周报附件的url。
+ * @param params WeekReportDownloadFileUrlReqParams
+ * @returns
+ */
+export const getDownloadWeekReportFileUrl = (
+  params: WeekReportDownloadFileUrlReqParams
+) => {
+  return request.post<WeekReportDownloadFileUrlResParams>(
+    Project.PROJECT_WEEK_REPORT_DOWNLOAD_FILE,
+    params
+  )
+}
+
+/**
+ * 删除周报附件。
+ * @param params 周报附件id
+ * @returns
+ */
+export const removeReportFile = (id: string) => {
+  return request.post<BaseResParams>(
+    Project.PROJECT_WEEK_REPORT_REMOVE_FILE,
+    { id }
+  )
+}
+
+/**
+ * 创建后的项目周报,需要提交,才进入后续流程。
+ * @param params CommitWeekReportReqParams
+ * @returns
+ */
+export const commitWeekReportFile = (params: CommitWeekReportReqParams) => {
+  return request.post<BaseResParams>(
+    Project.PROJECT_WEEK_REPORT_COMMIT,
+    params
+  )
+}
+
+/**
+ * 提交后的项目周报,如还没有被审阅,可以撤回后重新修改。
+ * @param params WithdrawWeekReportReqParams
+ * @returns
+ */
+export const withdrawWeekReportFile = (params: WithdrawWeekReportReqParams) => {
+  return request.post<BaseResParams>(
+    Project.PROJECT_WEEK_REPORT_WITHDRAW,
+    params
+  )
+}

+ 127 - 0
src/api/project-center/work.ts

@@ -0,0 +1,127 @@
+import request from '@/utils/http'
+import { Project } from '@/enums/api'
+import {
+    WorkListReqParams,
+    WorkListResParams,
+    WorkDetailResParams,
+    WorkSubmitReqParams,
+    UploadWorkFileReqParams,
+    UploadWorkFileResParams,
+    WorkStatResParams,
+    WorkChainReqParams,
+    WorkChain,
+    WorkChainResParams
+} from '@/types/work'
+
+/**
+ * 获取我的任务列表
+ * @param params WorkListReqParams
+ * @returns
+ */
+export const getWorkList = (params: WorkListReqParams) => {
+    return request.post<WorkListResParams>(
+        Project.PROJECT_WORK_LIST,
+        params
+    )
+}
+
+/**
+ * 获取任务详情
+ * @param id id
+ * @returns
+ */
+export const getWorkDetail = (id: string) => {
+    return request.post<WorkDetailResParams>(
+        Project.PROJECT_WORK_DETAIL,
+        { id }
+    )
+}
+
+/**
+ * 提交我的待办任务信息
+ * @param parasm WorkSubmitReqParams
+ * @returns
+ */
+export const submitWork = (params: WorkSubmitReqParams) => {
+    return request.post<WorkDetailResParams>(
+        Project.PROJECT_WORK_SUBMIT,
+        params
+    )
+}
+
+/**
+ * 获取work表单提交上传文件url
+ * @param params
+ * @returns
+ */
+export const getUploadWorkFileUrl = (params: UploadWorkFileReqParams) => {
+    return request.post<UploadWorkFileResParams>(
+        Project.PROJECT_WORK_UPLOAD_FILE,
+        params
+    )
+}
+
+/**
+ * 获取工作统计信息
+ * @returns
+ */
+export const getWorkStat = () => {
+    return request.post<WorkStatResParams>(
+        Project.PROJECT_WORK_STAT,
+        {}
+    )
+}
+
+/**
+ * 获取工作统计信息
+ * @returns
+ */
+export const getWorkDoneStat = () => {
+    return request.post<WorkStatResParams>(
+        Project.PROJECT_WORK_DONE_STAT,
+        {}
+    )
+}
+
+/**
+ * 获取工作统计信息
+ * @returns
+ */
+export const getWorkUndoneStat = () => {
+    return request.post<WorkStatResParams>(
+        Project.PROJECT_WORK_UNDONE_STAT,
+        {}
+    )
+}
+
+/**
+ * 获取work表单提交上传文件url
+ * @param params
+ * @returns
+ */
+export const getDownloadWorkFileUrl = (params: { id: string }) => {
+    return request.post<UploadWorkFileResParams>(
+        Project.PROJECT_WORK_DOWNLOAD_FILE,
+        params
+    )
+}
+
+// 获取我的未完成工作数量
+export const getWorkUndoneCount = () => {
+    return request.post<{ undone_count: number }>(
+        Project.PROJECT_WORK_UNDONE_COUNT,
+        {}
+    )
+}
+
+/**
+ * 获取工作项链
+ * @param params
+ * @returns
+ */
+export const getWorkChain = (params: WorkChainReqParams) => {
+    return request.post<WorkChainResParams>(
+        Project.PROJECT_WORK_GET_CHAIN,
+        params
+    )
+}

+ 102 - 0
src/auto-imports.d.ts

@@ -0,0 +1,102 @@
+/* eslint-disable */
+/* prettier-ignore */
+// @ts-nocheck
+// noinspection JSUnusedGlobalSymbols
+// Generated by unplugin-auto-import
+export {}
+declare global {
+  const EffectScope: typeof import('vue')['EffectScope']
+  const acceptHMRUpdate: typeof import('pinia')['acceptHMRUpdate']
+  const computed: typeof import('vue')['computed']
+  const createApp: typeof import('vue')['createApp']
+  const createPinia: typeof import('pinia')['createPinia']
+  const customRef: typeof import('vue')['customRef']
+  const defineAsyncComponent: typeof import('vue')['defineAsyncComponent']
+  const defineComponent: typeof import('vue')['defineComponent']
+  const defineStore: typeof import('pinia')['defineStore']
+  const effectScope: typeof import('vue')['effectScope']
+  const getActivePinia: typeof import('pinia')['getActivePinia']
+  const getCurrentInstance: typeof import('vue')['getCurrentInstance']
+  const getCurrentScope: typeof import('vue')['getCurrentScope']
+  const h: typeof import('vue')['h']
+  const inject: typeof import('vue')['inject']
+  const isProxy: typeof import('vue')['isProxy']
+  const isReactive: typeof import('vue')['isReactive']
+  const isReadonly: typeof import('vue')['isReadonly']
+  const isRef: typeof import('vue')['isRef']
+  const mapActions: typeof import('pinia')['mapActions']
+  const mapGetters: typeof import('pinia')['mapGetters']
+  const mapState: typeof import('pinia')['mapState']
+  const mapStores: typeof import('pinia')['mapStores']
+  const mapWritableState: typeof import('pinia')['mapWritableState']
+  const markRaw: typeof import('vue')['markRaw']
+  const nextTick: typeof import('vue')['nextTick']
+  const onActivated: typeof import('vue')['onActivated']
+  const onAddToFavorites: typeof import('@dcloudio/uni-app')['onAddToFavorites']
+  const onBackPress: typeof import('@dcloudio/uni-app')['onBackPress']
+  const onBeforeMount: typeof import('vue')['onBeforeMount']
+  const onBeforeUnmount: typeof import('vue')['onBeforeUnmount']
+  const onBeforeUpdate: typeof import('vue')['onBeforeUpdate']
+  const onDeactivated: typeof import('vue')['onDeactivated']
+  const onError: typeof import('@dcloudio/uni-app')['onError']
+  const onErrorCaptured: typeof import('vue')['onErrorCaptured']
+  const onHide: typeof import('@dcloudio/uni-app')['onHide']
+  const onLaunch: typeof import('@dcloudio/uni-app')['onLaunch']
+  const onLoad: typeof import('@dcloudio/uni-app')['onLoad']
+  const onMounted: typeof import('vue')['onMounted']
+  const onNavigationBarButtonTap: typeof import('@dcloudio/uni-app')['onNavigationBarButtonTap']
+  const onNavigationBarSearchInputChanged: typeof import('@dcloudio/uni-app')['onNavigationBarSearchInputChanged']
+  const onNavigationBarSearchInputClicked: typeof import('@dcloudio/uni-app')['onNavigationBarSearchInputClicked']
+  const onNavigationBarSearchInputConfirmed: typeof import('@dcloudio/uni-app')['onNavigationBarSearchInputConfirmed']
+  const onNavigationBarSearchInputFocusChanged: typeof import('@dcloudio/uni-app')['onNavigationBarSearchInputFocusChanged']
+  const onPageNotFound: typeof import('@dcloudio/uni-app')['onPageNotFound']
+  const onPageScroll: typeof import('@dcloudio/uni-app')['onPageScroll']
+  const onPullDownRefresh: typeof import('@dcloudio/uni-app')['onPullDownRefresh']
+  const onReachBottom: typeof import('@dcloudio/uni-app')['onReachBottom']
+  const onReady: typeof import('@dcloudio/uni-app')['onReady']
+  const onRenderTracked: typeof import('vue')['onRenderTracked']
+  const onRenderTriggered: typeof import('vue')['onRenderTriggered']
+  const onResize: typeof import('@dcloudio/uni-app')['onResize']
+  const onScopeDispose: typeof import('vue')['onScopeDispose']
+  const onServerPrefetch: typeof import('vue')['onServerPrefetch']
+  const onShareAppMessage: typeof import('@dcloudio/uni-app')['onShareAppMessage']
+  const onShareTimeline: typeof import('@dcloudio/uni-app')['onShareTimeline']
+  const onShow: typeof import('@dcloudio/uni-app')['onShow']
+  const onTabItemTap: typeof import('@dcloudio/uni-app')['onTabItemTap']
+  const onThemeChange: typeof import('@dcloudio/uni-app')['onThemeChange']
+  const onUnhandledRejection: typeof import('@dcloudio/uni-app')['onUnhandledRejection']
+  const onUnload: typeof import('@dcloudio/uni-app')['onUnload']
+  const onUnmounted: typeof import('vue')['onUnmounted']
+  const onUpdated: typeof import('vue')['onUpdated']
+  const provide: typeof import('vue')['provide']
+  const reactive: typeof import('vue')['reactive']
+  const readonly: typeof import('vue')['readonly']
+  const ref: typeof import('vue')['ref']
+  const resolveComponent: typeof import('vue')['resolveComponent']
+  const setActivePinia: typeof import('pinia')['setActivePinia']
+  const setMapStoreSuffix: typeof import('pinia')['setMapStoreSuffix']
+  const shallowReactive: typeof import('vue')['shallowReactive']
+  const shallowReadonly: typeof import('vue')['shallowReadonly']
+  const shallowRef: typeof import('vue')['shallowRef']
+  const storeToRefs: typeof import('pinia')['storeToRefs']
+  const toRaw: typeof import('vue')['toRaw']
+  const toRef: typeof import('vue')['toRef']
+  const toRefs: typeof import('vue')['toRefs']
+  const toValue: typeof import('vue')['toValue']
+  const triggerRef: typeof import('vue')['triggerRef']
+  const unref: typeof import('vue')['unref']
+  const useAttrs: typeof import('vue')['useAttrs']
+  const useCssModule: typeof import('vue')['useCssModule']
+  const useCssVars: typeof import('vue')['useCssVars']
+  const useSlots: typeof import('vue')['useSlots']
+  const watch: typeof import('vue')['watch']
+  const watchEffect: typeof import('vue')['watchEffect']
+  const watchPostEffect: typeof import('vue')['watchPostEffect']
+  const watchSyncEffect: typeof import('vue')['watchSyncEffect']
+}
+// for type re-export
+declare global {
+  // @ts-ignore
+  export type { Component, ComponentPublicInstance, ComputedRef, ExtractDefaultPropTypes, ExtractPropTypes, ExtractPublicPropTypes, InjectionKey, PropType, Ref, VNode, WritableComputedRef } from 'vue'
+  import('vue')
+}

+ 557 - 0
src/components/cu-circular-progress/cu-circular-progress.vue

@@ -0,0 +1,557 @@
+<template>
+	<view class="cmd-progress cmd-progress-default" :class="setStatusClass">
+	  <block v-if="type == 'circle' || type == 'dashboard'">
+		<view class="cmd-progress cmd-progress-default" :class="setStatusClass">
+		  <view class="cmd-progress-inner" :style="setCircleStyle">
+			<!-- 绘制圈 start -->
+			<!-- #ifdef H5 -->
+			<svg viewBox="0 0 100 100" class="cmd-progress-circle">
+			  <path :d="setCirclePath" stroke="#f3f3f3" :stroke-linecap="strokeShape" :stroke-width="strokeWidth"
+				fill-opacity="0" class="cmd-progress-circle-trail" :style="setCircleTrailStyle"></path>
+			  <path :d="setCirclePath" :stroke-linecap="strokeShape" :stroke-width="strokeWidth" fill-opacity="0" class="cmd-progress-circle-path"
+				:style="setCirclePathStyle"></path>
+			</svg>
+			<!-- #endif -->
+			<!-- #ifndef H5 -->
+			<text :style="setCircle"></text>
+			<!-- #endif -->
+			<!-- 绘制圈 end -->
+			<!-- 状态文本 start -->
+			<block v-if="showInfo">
+			  <text class="cmd-progress-text" :title="setFormat">
+				<block v-if="status != 'success' && status != 'exception' && setProgress < 100">{{setFormat}}</block>
+				<!-- #ifdef H5 -->
+				<svg v-if="status == 'exception'" viewBox="64 64 896 896" data-icon="close" width="1em" height="1em" fill="currentColor"
+				  aria-hidden="true">
+				  <path d="M563.8 512l262.5-312.9c4.4-5.2.7-13.1-6.1-13.1h-79.8c-4.7 0-9.2 2.1-12.3 5.7L511.6 449.8 295.1 191.7c-3-3.6-7.5-5.7-12.3-5.7H203c-6.8 0-10.5 7.9-6.1 13.1L459.4 512 196.9 824.9A7.95 7.95 0 0 0 203 838h79.8c4.7 0 9.2-2.1 12.3-5.7l216.5-258.1 216.5 258.1c3 3.6 7.5 5.7 12.3 5.7h79.8c6.8 0 10.5-7.9 6.1-13.1L563.8 512z"></path>
+				</svg>
+				<svg v-if="status == 'success' || setProgress == 100" viewBox="64 64 896 896" data-icon="check" width="1em"
+				  height="1em" fill="currentColor" aria-hidden="true" :style="{'color': strokeColor ? strokeColor : ''}">
+				  <path d="M912 190h-69.9c-9.8 0-19.1 4.5-25.1 12.2L404.7 724.5 207 474a32 32 0 0 0-25.1-12.2H112c-6.7 0-10.4 7.7-6.3 12.9l273.9 347c12.8 16.2 37.4 16.2 50.3 0l488.4-618.9c4.1-5.1.4-12.8-6.3-12.8z"></path>
+				</svg>
+				<!-- #endif -->
+				<!-- #ifndef H5 -->
+				<text v-if="status == 'exception' || status == 'success' || setProgress == 100" :style="setCircleIcon"></text>
+				<!-- #endif -->
+			  </text>
+			</block>
+			<!-- 状态文本 end -->
+		  </view>
+		</view>
+	  </block>
+  
+	  <block v-if="type == 'line'">
+		<!-- 进度条 start -->
+		<view class="cmd-progress-outer">
+		  <view class="cmd-progress-inner" :style="{'border-radius': strokeShape == 'square' ? 0 : '100px'}">
+			<view class="cmd-progress-bg" :style="setLineStyle"></view>
+			<view v-if="successPercent" class="cmd-progress-success-bg" :style="setLineSuccessStyle"></view>
+		  </view>
+		</view>
+		<!-- 进度条 end -->
+		<!-- 进度条是否显示信息 start -->
+		<block v-if="showInfo">
+		  <text class="cmd-progress-text" :title="setFormat">
+			<block v-if="status != 'success' && status != 'exception' && setProgress < 100">{{setFormat}}</block>
+			<!-- #ifdef H5 -->
+			<svg v-if="status == 'exception'" viewBox="64 64 896 896" data-icon="close-circle" width="1em" height="1em"
+			  fill="currentColor" aria-hidden="true">
+			  <path d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm165.4 618.2l-66-.3L512 563.4l-99.3 118.4-66.1.3c-4.4 0-8-3.5-8-8 0-1.9.7-3.7 1.9-5.2l130.1-155L340.5 359a8.32 8.32 0 0 1-1.9-5.2c0-4.4 3.6-8 8-8l66.1.3L512 464.6l99.3-118.4 66-.3c4.4 0 8 3.5 8 8 0 1.9-.7 3.7-1.9 5.2L553.5 514l130 155c1.2 1.5 1.9 3.3 1.9 5.2 0 4.4-3.6 8-8 8z"></path>
+			</svg>
+			<svg v-if="status == 'success' || setProgress == 100" viewBox="64 64 896 896" data-icon="check-circle" width="1em"
+			  height="1em" fill="currentColor" aria-hidden="true" :style="{'color': strokeColor ? strokeColor : ''}">
+			  <path d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm193.5 301.7l-210.6 292a31.8 31.8 0 0 1-51.7 0L318.5 484.9c-3.8-5.3 0-12.7 6.5-12.7h46.9c10.2 0 19.9 4.9 25.9 13.3l71.2 98.8 157.2-218c6-8.3 15.6-13.3 25.9-13.3H699c6.5 0 10.3 7.4 6.5 12.7z"></path>
+			</svg>
+			<!-- #endif -->
+			<!-- #ifndef H5 -->
+			<text v-if="status == 'exception' || status == 'success' || setProgress == 100" :style="setLineStatusIcon"></text>
+			<!-- #endif -->
+		  </text>
+		</block>
+		<!-- 进度条是否显示信息 end -->
+	  </block>
+	</view>
+  </template>
+  
+  <script>
+	/**  
+	 * 进度条组件  
+	 * @description 显示一个操作完成的百分比时,为用户显示该操作的当前进度和状态。  
+	 * @property {String} type 进度类型 - 线型:line、圆圈形:circle、仪表盘:dashboard,默认线型:line  
+	 * @property {Number} percent 进度百分比值 - 显示范围0-100 ,可能数比较大就需要自己转成百分比的值  
+	 * @property {Number} success-percent 进度已完成的百分几 - 仅支持进度线型:line  
+	 * @property {String} status 进度状态 - 涌动:active(仅支持线型:line)、正常:normal、完成:success、失败:exception,默认正常:normal  
+	 * @property {Boolean} show-info 进度状态信息 - 是否显示进度数值或状态图标,默认true  
+	 * @property {Number} stroke-width 进度线条的宽度 - 建议在条线的宽度范围:1-50,与进度条显示宽度有关,默认8  
+	 * @property {String} stroke-color 进度线条的颜色 - 渐变色仅支持线型:line  
+	 * @property {String} stroke-shape 进度线条两端的形状 - 圆:round、方块直角:square,默认圆:round  
+	 * @property {Number} width 进度画布宽度 - 仅支持圆圈形:circle、仪表盘:dashboard,默认80  
+	 * @property {String} gap-degree 进度圆形缺口角度 - 可取值 0 ~ 360,仅支持圆圈形:circle、仪表盘:dashboard  
+	 * @property {String} gap-position 进度圆形缺口位置 - 可取值'top', 'bottom', 'left', 'right',仅支持圆圈形:circle、仪表盘:dashboard  
+	 * @example <cmd-progress :percent="30"></cmd-progress>  
+	 */
+	export default {  
+	  props: {
+		/**
+		 * 类型默认:line,可选 line circle dashboard
+		 */
+		type: {
+		  validator: val => {
+			return ['line', 'circle', 'dashboard'].includes(val);
+		  },
+		  default: 'line'
+		},
+		/**
+		 * 百分比
+		 */
+		percent: {
+		  type: Number,
+		  default: 0
+		},
+		/**
+		 * 已完成的分段百分,仅支持类型line
+		 */
+		successPercent: {
+		  type: Number,
+		  default: 0
+		},
+		/**
+		 * 是否显示进度数值或状态图标
+		 */
+		showInfo: {
+		  type: Boolean,
+		  default: true
+		},
+		/**
+		 * 进度状态,可选:normal success exception (active仅支持类型line
+		 */
+		status: {
+		  validator: val => {
+			return ['normal', 'success', 'exception', 'active'].includes(val);
+		  },
+		  default: 'normal'
+		},
+		/**
+		 * 条线的宽度1-50,与width有关
+		 */
+		strokeWidth: {
+		  type: Number,
+		  default: 6
+		},
+		/**
+		 * 条线的颜色,渐变色仅支持类型line
+		 */
+		strokeColor: {
+		  type: String,
+		  default: ''
+		},
+		/**
+		 * 条线两端的形状 可选:'round', 'square'
+		 */
+		strokeShape: {
+		  validator: val => {
+			return ['round', 'square'].includes(val);
+		  },
+		  default: 'round'
+		},
+		/**
+		 * 圆形进度条画布宽度,支持类型circle dashboard
+		 */
+		width: {
+		  type: Number,
+		  default: 80
+		},
+		/**
+		 * 圆形进度条缺口角度,可取值 0 ~ 360,支持类型circle dashboard
+		 */
+		gapDegree: {
+		  type: Number,
+		  default: 0
+		},
+		/**
+		 * 圆形进度条缺口位置,可取值'top', 'bottom', 'left', 'right' ,支持类型circle dashboard
+		 */
+		gapPosition: {
+		  validator: val => {
+			return ['top', 'bottom', 'left', 'right'].includes(val);
+		  },
+		  default: 'top'
+		}
+	  },
+  
+	  computed: {
+		/**
+		 * 如果需要自定义格式就在这改
+		 */
+		setFormat() {
+		  return `${this.setProgress}%`;
+		},
+		/**
+		 * 设置显示进度值,禁止小于0和超过100
+		 */
+		setProgress() {
+		  let percent = this.percent;
+		  if (!this.percent || this.percent < 0) {
+			percent = 0;
+		  } else if (this.percent >= 100) {
+			percent = 100;
+		  }
+		  return percent;
+		},
+		/**
+		 * 进度圈svg大小
+		 */
+		setCircleStyle() {
+		  return `width: ${this.width}px;
+				  height: ${this.width}px;
+				  fontSize: ${this.width * 0.15 + 6}px;`
+		},
+		/**
+		 * 圈底色
+		 */
+		setCircleTrailStyle() {
+		  const radius = 50 - this.strokeWidth / 2;
+		  const len = Math.PI * 2 * radius;
+		  const gapDeg = this.gapDegree || (this.type === 'dashboard' && 75);
+		  return `stroke-dasharray: ${len - (gapDeg||0)}px, ${len}px;
+				  stroke-dashoffset: -${(gapDeg||0) / 2}px;
+				  transition: stroke-dashoffset 0.3s ease 0s, stroke-dasharray 0.3s ease 0s, stroke 0.3s;`
+		},
+		/**
+		 * 圈进度
+		 */
+		setCirclePathStyle() {
+		  const radius = 50 - this.strokeWidth / 2;
+		  const len = Math.PI * 2 * radius;
+		  const gapDeg = this.gapDegree || (this.type === 'dashboard' && 75);
+		  return `stroke: ${this.strokeColor};
+				  stroke-dasharray: ${(this.setProgress / 100) * (len - (gapDeg||0))}px, ${len}px;
+				  stroke-dashoffset: -${(gapDeg||0) / 2}px;
+				  transition: stroke-dashoffset 0.3s ease 0s, stroke-dasharray 0.3s ease 0s, stroke 0.3s, stroke-width 0.06s ease 0.3s;`
+		},
+		/**
+		 * 绘制圈
+		 */
+		setCirclePath() {
+		  const radius = 50 - this.strokeWidth / 2;
+		  let beginPositionX = 0;
+		  let beginPositionY = -radius;
+		  let endPositionX = 0;
+		  let endPositionY = -2 * radius;
+		  const gapPos = (this.type === 'dashboard' && 'bottom') || this.gapPosition || 'top';
+		  switch (gapPos) {
+			case 'left':
+			  beginPositionX = -radius;
+			  beginPositionY = 0;
+			  endPositionX = 2 * radius;
+			  endPositionY = 0;
+			  break;
+			case 'right':
+			  beginPositionX = radius;
+			  beginPositionY = 0;
+			  endPositionX = -2 * radius;
+			  endPositionY = 0;
+			  break;
+			case 'bottom':
+			  beginPositionY = radius;
+			  endPositionY = 2 * radius;
+			  break;
+			default:
+			  break;
+		  }
+		  return `M 50,50 m ${beginPositionX},${beginPositionY} a ${radius},${radius} 0 1 1 ${endPositionX},${-endPositionY} a ${radius},${radius} 0 1 1 ${-endPositionX},${endPositionY}`;
+		},
+		// #ifndef H5
+		/**
+		 * 非H5端,绘制进度圈svg转base URL
+		 */
+		setCircle() {
+		  const radius = 50 - this.strokeWidth / 2;
+		  const len = Math.PI * 2 * radius;
+		  const gapDeg = this.gapDegree || (this.type === 'dashboard' && 75);
+		  let currentColor = '#108ee9'
+		  // 异常进度
+		  if (this.status == 'exception') {
+			currentColor = '#dd524d'
+		  }
+		  // 完成进度
+		  if (this.status == 'success' || this.setProgress >= 100 || this.strokeColor) {
+			currentColor = this.strokeColor || '#4cd964'
+		  }
+		  let svgToBase =
+			`data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100' class='cmd-progress-circle'%3E%3Cpath d='${this.setCirclePath}' stroke='%23f3f3f3' stroke-linecap='${this.strokeShape}' stroke-width='${this.strokeWidth}' fill-opacity='0' class='cmd-progress-circle-trail' style='stroke-dasharray: ${len - (gapDeg||0)}px, ${len}px;stroke-dashoffset: -${(gapDeg||0) / 2}px;transition: stroke-dashoffset 0.3s ease 0s, stroke-dasharray 0.3s ease 0s, stroke 0.3s;'%3E%3C/path%3E%3Cpath  d='${this.setCirclePath}' stroke-linecap='${this.strokeShape}' stroke-width='${this.strokeWidth}' fill-opacity='0' class='cmd-progress-circle-path' style='stroke: ${escape(currentColor)};stroke-dasharray: ${(this.setProgress / 100) * (len - (gapDeg||0))}px, ${len}px;stroke-dashoffset: -${(gapDeg||0) / 2}px;transition: stroke-dashoffset 0.3s ease 0s, stroke-dasharray 0.3s ease 0s, stroke 0.3s, stroke-width 0.06s ease 0.3s;'%3E%3C/path%3E%3C/svg%3E`
+		  return `background-image: url("${svgToBase}");
+				  background-size: cover;
+				  display: inline-block;
+				  ${this.setCircleStyle}`;
+		},
+		/**
+		 * 设置进度圈状态图标
+		 */
+		setCircleIcon() {
+		  let currentColor = '#108ee9'
+		  let svgToBase = ''
+		  // 异常进度
+		  if (this.status == 'exception') {
+			currentColor = '#dd524d'
+			svgToBase =
+			  `data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='64 64 896 896' data-icon='close' width='1em' height='1em' fill='${escape(currentColor)}' aria-hidden='true'%3E %3Cpath d='M563.8 512l262.5-312.9c4.4-5.2.7-13.1-6.1-13.1h-79.8c-4.7 0-9.2 2.1-12.3 5.7L511.6 449.8 295.1 191.7c-3-3.6-7.5-5.7-12.3-5.7H203c-6.8 0-10.5 7.9-6.1 13.1L459.4 512 196.9 824.9A7.95 7.95 0 0 0 203 838h79.8c4.7 0 9.2-2.1 12.3-5.7l216.5-258.1 216.5 258.1c3 3.6 7.5 5.7 12.3 5.7h79.8c6.8 0 10.5-7.9 6.1-13.1L563.8 512z'%3E%3C/path%3E %3C/svg%3E`;
+		  }
+		  // 完成进度
+		  if (this.status == 'success' || this.setProgress >= 100) {
+			currentColor = this.strokeColor || '#4cd964'
+			svgToBase =
+			  `data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='64 64 896 896' data-icon='check' width='1em' height='1em' fill='${escape(currentColor)}' aria-hidden='true'%3E %3Cpath d='M912 190h-69.9c-9.8 0-19.1 4.5-25.1 12.2L404.7 724.5 207 474a32 32 0 0 0-25.1-12.2H112c-6.7 0-10.4 7.7-6.3 12.9l273.9 347c12.8 16.2 37.4 16.2 50.3 0l488.4-618.9c4.1-5.1.4-12.8-6.3-12.8z'%3E%3C/path%3E %3C/svg%3E`;
+		  }
+		  return `background-image: url("${svgToBase}");
+				  background-size: cover;
+				  display: inline-block;
+				  width: 1em;
+				  height: 1em;`;
+		},
+		// #endif
+		/**
+		 * 设置进度条样式
+		 */
+		setLineStyle() {
+		  return `width: ${this.setProgress}%;
+				  height: ${this.strokeWidth}px;
+				  background: ${this.strokeColor};
+				  border-radius: ${this.strokeShape === 'square' ? 0 : '100px'};`;
+		},
+		/**
+		 * 设置已完成分段进度
+		 */
+		setLineSuccessStyle() {
+		  let successPercent = this.successPercent;
+		  if (!this.successPercent || this.successPercent < 0 || this.setProgress < this.successPercent) {
+			successPercent = 0;
+		  } else if (this.successPercent >= 100) {
+			successPercent = 100;
+		  }
+		  return `width: ${successPercent}%;
+				  height: ${this.strokeWidth}px;
+				  border-radius: ${this.strokeShape === 'square' ? 0 : '100px'};`;
+		},
+		// #ifndef H5
+		/**
+		 * 设置进度条状态图标
+		 */
+		setLineStatusIcon() {
+		  let currentColor = '#108ee9'
+		  let svgToBase = ''
+		  // 异常进度
+		  if (this.status == 'exception') {
+			currentColor = '#dd524d'
+			svgToBase =
+			  `data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='64 64 896 896' data-icon='close-circle' width='1em' height='1em' fill='${escape(currentColor)}' aria-hidden='true'%3E %3Cpath d='M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm165.4 618.2l-66-.3L512 563.4l-99.3 118.4-66.1.3c-4.4 0-8-3.5-8-8 0-1.9.7-3.7 1.9-5.2l130.1-155L340.5 359a8.32 8.32 0 0 1-1.9-5.2c0-4.4 3.6-8 8-8l66.1.3L512 464.6l99.3-118.4 66-.3c4.4 0 8 3.5 8 8 0 1.9-.7 3.7-1.9 5.2L553.5 514l130 155c1.2 1.5 1.9 3.3 1.9 5.2 0 4.4-3.6 8-8 8z'%3E%3C/path%3E %3C/svg%3E`;
+		  }
+		  // 完成进度
+		  if (this.status == 'success' || this.setProgress >= 100) {
+			currentColor = this.strokeColor || '#4cd964'
+			svgToBase =
+			  `data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='64 64 896 896' data-icon='check-circle' width='1em' height='1em' fill='${escape(currentColor)}' aria-hidden='true'%3E %3Cpath d='M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm193.5 301.7l-210.6 292a31.8 31.8 0 0 1-51.7 0L318.5 484.9c-3.8-5.3 0-12.7 6.5-12.7h46.9c10.2 0 19.9 4.9 25.9 13.3l71.2 98.8 157.2-218c6-8.3 15.6-13.3 25.9-13.3H699c6.5 0 10.3 7.4 6.5 12.7z'%3E%3C/path%3E %3C/svg%3E`;
+		  }
+		  return `background-image: url("${svgToBase}");
+				  background-size: cover;
+				  display: inline-block;
+				  width: 1em;
+				  height: 1em;`;
+		},
+		// #endif
+		/**
+		 * 状态样式
+		 */
+		setStatusClass() {
+		  let statusClass = [];
+		  // 异常进度
+		  if (this.status == 'exception') {
+			statusClass.push('cmd-progress-status-exception')
+		  }
+		  // 完成进度
+		  if (this.status == 'success' || this.setProgress >= 100) {
+			statusClass.push('cmd-progress-status-success')
+		  }
+		  // 活动进度条
+		  if (this.status == 'active') {
+			statusClass.push('cmd-progress-status-active')
+		  }
+		  // 是否显示信息
+		  if (this.showInfo) {
+			statusClass.push('cmd-progress-show-info')
+		  }
+		  // 进度条类型
+		  if (this.type === 'line') {
+			statusClass.push('cmd-progress-line')
+		  }
+		  // 进度圈、仪表盘类型
+		  if (this.type === 'circle' || this.type === 'dashboard') {
+			statusClass.push('cmd-progress-circle')
+		  }
+		  statusClass.push('cmd-progress-status-normal')
+		  return statusClass;
+		}
+	  }
+	}
+  </script>
+  
+  <style>
+	.cmd-progress { 
+	  box-sizing: border-box;
+	  margin: 0;
+	  padding: 0;
+	  list-style: none;
+	  display: inline-block;
+	}
+  
+	.cmd-progress-line {
+	  width: 100%;
+	  font-size: 28upx;
+	  position: relative;
+	  display: flex;
+	  flex-direction: row;
+	  justify-content: center;
+	  align-items: center;
+	}
+  
+	.cmd-progress-outer {
+	  display: inline-block;
+	  width: 100%;
+	  margin-right: 0;
+	  padding-right: 0;
+	}
+  
+	.cmd-progress-show-info .cmd-progress-outer {
+	  flex: 1;
+	}
+  
+	.cmd-progress-inner {
+	  display: inline-block;
+	  width: 100%;
+	  background-color: #f5f5f5;
+	  border-radius: 200upx;
+	  vertical-align: middle;
+	  position: relative;
+	}
+  
+	.cmd-progress-circle-trail {
+	  stroke: #f5f5f5;
+	}
+  
+	.cmd-progress-circle-path {
+	  stroke: #007aff;
+	  animation: appear 0.3s;
+	}
+  
+	.cmd-progress-success-bg,
+	.cmd-progress-bg {
+	  background-color: #007aff;
+	  transition: all 0.4s cubic-bezier(0.08, 0.82, 0.17, 1) 0s;
+	  position: relative;
+	}
+  
+	.cmd-progress-success-bg {
+	  background-color: #4cd964;
+	  position: absolute;
+	  top: 0;
+	  left: 0;
+	}
+  
+	.cmd-progress-text {
+	  word-break: normal;
+	  width: 60upx;
+	  text-align: left;
+	  font-size: 10px;
+	  margin-left: 16upx;
+	  vertical-align: middle;
+	  display: inline-block;
+	  white-space: nowrap;
+	  color: #323233;
+	  line-height: 1;
+	}
+  
+	.cmd-progress-status-active .cmd-progress-bg:before {
+	  content: "";
+	  opacity: 0;
+	  position: absolute;
+	  top: 0;
+	  left: 0;
+	  right: 0;
+	  bottom: 0;
+	  background: #fff;
+	  border-radius: 20upx;
+	  -webkit-animation: cmd-progress-active 2.4s cubic-bezier(0.23, 1, 0.32, 1) infinite;
+	  animation: cmd-progress-active 2.4s cubic-bezier(0.23, 1, 0.32, 1) infinite;
+	}
+  
+	.cmd-progress-status-exception .cmd-progress-bg {
+	  background-color: #dd524d;
+	}
+  
+	.cmd-progress-status-exception .cmd-progress-text {
+	  color: #dd524d;
+	}
+  
+	.cmd-progress-status-exception .cmd-progress-circle-path {
+	  stroke: #dd524d;
+	}
+  
+	.cmd-progress-status-success .cmd-progress-bg {
+	  background-color: #4cd964;
+	}
+  
+	.cmd-progress-status-success .cmd-progress-text {
+	  color: #4cd964;
+	}
+  
+	.cmd-progress-status-success .cmd-progress-circle-path {
+	  stroke: #4cd964;
+	}
+  
+	.cmd-progress-circle .cmd-progress-inner {
+	  position: relative;
+	  line-height: 1;
+	  background-color: transparent;
+	}
+  
+	.cmd-progress-circle .cmd-progress-text {
+	  display: block;
+	  position: absolute;
+	  width: 100%;
+	  text-align: center;
+	  line-height: 1;
+	  top: 50%;
+	  -webkit-transform: translateY(-50%);
+	  transform: translateY(-50%);
+	  left: 0;
+	  margin: 0;
+	  color: rgba(0, 0, 0, 0.65);
+	  white-space: normal;
+	}
+  
+	.cmd-progress-circle .cmd-progress-status-exception .cmd-progress-text {
+	  color: #dd524d;
+	}
+  
+	.cmd-progress-circle .cmd-progress-status-success .cmd-progress-text {
+	  color: #4cd964;
+	}
+  
+	@keyframes cmd-progress-active {
+	  0% {
+		opacity: 0.1;
+		width: 0;
+	  }
+  
+	  20% {
+		opacity: 0.5;
+		width: 0;
+	  }
+  
+	  100% {
+		opacity: 0;
+		width: 100%;
+	  }
+	}
+  </style>
+  

+ 70 - 0
src/components/cu-custom/index.vue

@@ -0,0 +1,70 @@
+<template>
+	<view>
+		<view class="cu-custom" :style="[{height:CustomBar + 'px'}]">
+			<view class="cu-bar fixed" :style="style" :class="[bgImage!=''?'none-bg text-white bg-img':'',bgColor]">
+				<view class="action" @tap="BackPage" v-if="isBack">
+					<text class="cuIcon-back"></text>
+					<slot name="backText"></slot>
+				</view>
+				<view class="content" :style="[{top:StatusBar + 'px'}]">
+					<slot name="content"></slot>
+				</view>
+				<slot name="right"></slot>
+			</view>
+		</view>
+	</view>
+</template>
+
+<script>
+	/**
+	 * oa-custom 自定义头部
+	 * @description 可定义背景色和背景图片及返回按钮
+	 */
+	export default {
+
+		data() {
+			return {
+				StatusBar: this.StatusBar,
+				CustomBar: this.CustomBar
+			};
+		},
+		name: 'cu-custom',
+		computed: {
+			style() {
+				const StatusBar = this.StatusBar;
+				const CustomBar = this.CustomBar;
+				const bgImage = this.bgImage;
+				let style = `height:${CustomBar}px;padding-top:${StatusBar}px;`;
+				if (this.bgImage) {
+					style = `${style}background-image:url(${bgImage});`;
+				}
+				return style;
+			}
+		},
+		props: {
+			bgColor: {
+				type: String,
+				default: ''
+			},
+			isBack: {
+				type: [Boolean, String],
+				default: false
+			},
+			bgImage: {
+				type: String,
+				default: ''
+			}
+		},
+		methods: {
+			BackPage() {
+				uni.navigateBack({
+					delta: 1
+				});
+			}
+		}
+	};
+</script>
+
+<style>
+
+</style>

+ 164 - 0
src/components/cu-dropdown/changelog.md

@@ -0,0 +1,164 @@
+# 2.2.2
+
+修复
+
+1. 修复`picker`值选定残留问题
+
+# 2.2.1
+
+优化
+
+1. 新增`getMenuList`方法,获取菜单数据【示例6】
+2. 新增`getMenuIndex`方法,获取指定`prop`的菜单索引位置
+3. 优化示例使用方法
+4. 修复互斥、联动的值处理问题
+5. 更新了说明文档
+
+# 2.2.0
+
+推荐更新
+
+1. 新增更多的示例,示例更全面丰富
+2. 新增`openMenuItemPopup`方法,打开指定菜单弹窗【示例8】
+3. 新增`closeMenuPopup`方法,关闭菜单弹窗【示例5】
+4. 新增`getMenuValue`方法,获取菜单的值【示例8】
+5. 新增`updateMenu`方法,更新指定菜单项【示例7】
+6. 新增`setMenuLoading`方法,让某个菜单项处于加载中状态,异步数据有用【示例7】
+7. 修复`fixedTopValue`及顶部样式错误【示例2】
+8. 新增`syncDataKey`支持异步数据嵌套回调【示例7】
+9. 新增异步数据加载状态【示例7】
+10. 修复日期快捷获取上个月时间的错误
+11. 修复手动关闭时存在的点击残留
+12. 优化菜单项联动、互斥选择
+13. 优化异步数据阻塞菜单回显问题
+14. 修复数据回调时,会重新渲染菜单问题
+15. `@close`参数2支持返回当前菜单列表(Array)
+16. `@confirm`参数2支持返回当前菜单已选内容(Object)
+17. 菜单项`daterange`支持`showQuick`控制是否显示日期快选
+18. 移除`menuActiveText`,用处不大
+19. 更新了说明文档
+
+# 2.1.1
+
+优化
+
+1. 优化图标字体命名
+
+# 2.1.0
+
+推荐更新
+
+1. 现阶段由于兼容性问题,移除动态插槽
+2. 新增五个拓展插槽,`data`类型为 `slot1`/`slot2`/`slot3`/`slot4`/`slot5`
+3. 修复倒三角点击蒙层没有复原
+4. 新增更多演示示例
+5. 优化非固定页面顶部的效果
+6. 类型`cell`(下拉列表)数据项新增 `disabled` 用来禁用部分不可用选项
+7. 修复小程序对`v-show`、主题色的兼容问题
+8. 优化对 APP 的兼容
+
+# 2.0.9
+
+修复
+
+1. 修复初始化数据时可能存在的选项高亮问题
+
+# 2.0.8
+
+优化
+
+1. 优化拷贝函数引起的 App 兼容问题
+
+# 2.0.7
+
+优化
+
+1. 优化模块图标支持主题换色
+2. 新增`fixedTopValue`,用于优化异形屏导致搜索框被挡住问题,具体请参考示例项目
+
+# 2.0.6
+
+优化
+
+1. 优化三角图标支持主题换色
+
+# 2.0.5
+
+修复
+
+1. 修复 `picker` 大量数据时未能滚动问题
+
+# 2.0.4
+
+修复
+
+1. 修复弹窗后点击 `click`、`sort` 类型未能收起弹窗
+
+# 2.0.3
+
+优化
+
+1. 移除 scss 的 @use 用法,以修复可能会导致部分用户的 lint 错误
+
+# 2.0.2
+
+优化
+
+1. 优化菜单样式
+
+# 2.0.1
+
+新增
+
+1. 下拉列表`cell`、级联`picker`新增选中图标`showIcon`
+
+# 2.0.0
+
+移除 TS
+
+1. 移除了 TS 写法,现在是纯粹 JS 版本的 Vue3 版本
+2. 进一步完善使用文档及示例
+
+# 1.2.1
+
+新增功能
+
+1. 新增顶部搜索,当 type 为 `slot` 时,可在弹窗内容自定义插槽
+2. 新增自定插槽,当 type 为 `search` 时,头部显示搜索框
+3. 优化界面样式
+
+# 1.1.2
+
+优化异步菜单项数据
+
+1. 菜单项新增 `syncDataFn` 函数字段,返回异步菜单项数据内容
+
+# 1.1.1
+
+优化固定弹窗问题
+
+1. `da-dropdown`新增 `bgColor` 字段,当固定在顶部时,需要填写背景颜色,默认`#fff`
+2. 优化弹窗时底部滑动问题
+
+# 1.1.0
+
+优化数据问题
+
+### 优化
+
+1. `da-dropdown`新增 `prop` 字段,通过 prop 的唯一性来区分已选的数据
+2. `da-dropdown`新增 `fixedTop` 字段,为 true 时将会固定在头部
+3. 优化说明文档
+
+# 1.0.0
+
+初始版本 1.0.0,基于 Typescript+Scss 进行开发,基本完善相关各大平台的小程序兼容问题
+
+### 新增
+
+1. 下拉列表(单选)
+2. 点击高亮
+3. 点击排序
+4. 下拉筛选(单选按钮、多选按钮、滑动选择器)
+5. 级联筛选(单选)
+6. 日期筛选(日期快选、日期区间选择)

+ 171 - 0
src/components/cu-dropdown/components/cell.vue

@@ -0,0 +1,171 @@
+<template>
+  <view class="da-dropdown-cell">
+    <view
+      class="da-dropdown-cell-item"
+      :class="[item.checked ? 'is-actived' : '',item.disabled ? 'is-disabled' : '']"
+      v-for="(item, index) in cellOptions"
+      :key="index"
+      @click="handleSelect(item)">
+      <text class="da-dropdown-cell-item--label">{{ item.label }}</text>
+      <text class="da-dropdown-cell-item--suffix">{{ item.suffix }}</text>
+      <text class="da-dropdown-cell-item--check" v-if="item.checked && showIcon" />
+    </view>
+  </view>
+</template>
+
+<script>
+import { defineComponent, watch, ref } from 'vue'
+import { deepClone } from '../utils'
+
+export default defineComponent({
+  props: {
+    dropdownItem: {
+      type: Object,
+      default: null,
+    },
+    dropdownIndex: {
+      type: Number,
+    },
+  },
+  emits: ['success'],
+  setup(props, { emit }) {
+    const cellOptions = ref([])
+    const showIcon = ref(false)
+
+    function initData(options, value) {
+      const list = deepClone(options)
+      for (let i = 0; i < list.length; i++) {
+        const item = list[i]
+        if (item.value === value) {
+          item.checked = true
+          break
+        }
+      }
+      cellOptions.value = list
+    }
+
+    function handleSelect(item) {
+      if (item.disabled) {
+        return
+      }
+      if (props.dropdownItem?.prop) {
+        const res = { [props.dropdownItem.prop]: item.value }
+        emit('success', res, item, props.dropdownIndex)
+      } else {
+        console.error(`菜单项${props.dropdownItem.title}未定义prop,返回内容失败`)
+      }
+    }
+
+    watch(() => props.dropdownItem,
+      (v) => {
+        if (v?.options?.length) {
+          initData(v.options, v.value)
+        } else {
+          cellOptions.value = []
+        }
+        showIcon.value = v?.showIcon || false
+      }, { immediate: true, deep: true })
+
+    return {
+      cellOptions,
+      showIcon,
+      handleSelect,
+    }
+  },
+})
+</script>
+
+<style lang="scss" scoped>
+// 下拉列表
+.da-dropdown-cell {
+  --cell-height: 80rpx;
+
+  width: 100%;
+  max-height: 60vh;
+  overflow: hidden auto;
+
+  &-item {
+    box-sizing: border-box;
+    display: flex;
+    align-items: center;
+    justify-content: space-between;
+    width: 100%;
+    height: var(--cell-height);
+    padding: 0 24rpx;
+    overflow: hidden;
+    font-size: 28rpx;
+    color: var(--dropdown-text-color);
+    white-space: nowrap;
+    border-bottom: 1rpx solid #dedede;
+
+    &:last-child {
+      border-bottom: none;
+    }
+
+    &--label {
+      flex-grow: 1;
+      max-width: 80%;
+      overflow: hidden;
+      text-overflow: ellipsis;
+      white-space: nowrap;
+
+      // #ifdef H5
+      :deep(> span) {
+        overflow: hidden;
+        text-overflow: ellipsis;
+        white-space: nowrap;
+      }
+
+      // #endif
+    }
+
+    &--suffix {
+      flex-grow: 1;
+      margin-left: 10px;
+      overflow: hidden;
+      font-size: 24rpx;
+      color: #999;
+      text-align: right;
+      text-overflow: ellipsis;
+      white-space: nowrap;
+
+      // #ifdef H5
+      :deep(> span) {
+        overflow: hidden;
+        text-overflow: ellipsis;
+        white-space: nowrap;
+      }
+
+      // #endif
+    }
+
+    &--check {
+      display: flex;
+      flex-shrink: 0;
+      align-items: center;
+      justify-content: center;
+      width: var(--cell-height);
+      height: var(--cell-height);
+
+      &::after {
+        /* stylelint-disable-next-line font-family-no-missing-generic-family-keyword */
+        font-family: 'da-dropdown-iconfont' !important;
+        font-size: calc(var(--cell-height) / 3 * 2);
+        font-style: normal;
+        content: '\e736';
+        -webkit-font-smoothing: antialiased;
+        -moz-osx-font-smoothing: grayscale;
+      }
+    }
+
+    &.is-actived {
+      color: var(--dropdown-theme-color);
+    }
+
+    &.is-disabled {
+      color: #aaa;
+      background: #efefef;
+    }
+  }
+}
+</style>

+ 200 - 0
src/components/cu-dropdown/components/daterange.vue

@@ -0,0 +1,200 @@
+<template>
+  <view class="da-dropdown-daterange-box">
+    <view class="da-dropdown-daterange">
+      <view class="da-dropdown-daterange--date" :class="daterange.start ? 'is-actived' : ''">
+        <picker mode="date" :value="daterange.start" @change="handleStartDate">
+          <text>{{ daterange.start || '请选择日期' }}</text>
+        </picker>
+      </view>
+      <view class="da-dropdown-daterange--separate">至</view>
+      <view class="da-dropdown-daterange--date" :class="daterange.end ? 'is-actived' : ''">
+        <picker mode="date" :value="daterange.end" :disabled="!daterange.start" :start="daterange.start"
+          @change="handleEndDate">
+          <text> {{ daterange.end || '请选择日期' }}</text>
+        </picker>
+      </view>
+    </view>
+    <view class="da-dropdown-daterange-tags" v-if="dropdownItem.showQuick">
+      <block v-for="(tag, tagi) in dateTagList" :key="tagi">
+        <view class="da-dropdown-tag" :class="datetag === tag.value ? 'is-actived' : ''"
+          @click="handleTagDate(tag.value)">
+          <text class="da-dropdown-tag--text">{{ tag.label }}</text>
+        </view>
+      </block>
+    </view>
+    <PartDropdownFooter :resetText="dropdownItem.resetText" :confirmText="dropdownItem.confirmText"
+      @reset="handleReset()" @confirm="handleConfirm()"></PartDropdownFooter>
+  </view>
+</template>
+
+<script>
+import { defineComponent, ref, watch } from 'vue'
+import { deepClone, getRangeDate } from '../utils'
+import PartDropdownFooter from './part-dropdown-footer.vue'
+
+export default defineComponent({
+  components: { PartDropdownFooter },
+  props: {
+    dropdownItem: {
+      type: Object,
+      default: null,
+    },
+    dropdownIndex: {
+      type: Number,
+    },
+  },
+  emits: ['success'],
+  setup(props, { emit }) {
+    const daterange = ref(null)
+    const datetag = ref('')
+    const dateTagList = ref([
+      { value: '-7', label: '本周' },
+      { value: '-14', label: '上周' },
+      // { value: '-30', label: '本月' },
+      { value: '-60', label: '上月' },
+      // { value: '-1', label: '昨日' },
+      { value: '7', label: '近一周' },
+      { value: '30', label: '近一月' },
+      { value: '90', label: '近三个月' },
+    ])
+
+    function initData(dropdownItem, clearValue = false) {
+      const item = deepClone(dropdownItem || null)
+      if (clearValue === true) {
+        daterange.value = {
+          start: '',
+          end: '',
+        }
+        datetag.value = ''
+      } else {
+        daterange.value = {
+          start: item.value?.start || '',
+          end: item.value?.end || '',
+        }
+      }
+    }
+
+    function handleStartDate(item) {
+      daterange.value.start = item.detail.value
+      daterange.value.end = ''
+      datetag.value = ''
+    }
+    function handleEndDate(item) {
+      if (!daterange.value?.start) {
+        return
+      }
+      daterange.value.end = item.detail.value
+      datetag.value = ''
+    }
+    function handleTagDate(code) {
+      daterange.value = getRangeDate(code)
+      datetag.value = code
+    }
+    function handleReset() {
+      initData(props.dropdownItem, true)
+    }
+    function handleConfirm() {
+      if (props.dropdownItem?.prop) {
+        const res = { [props.dropdownItem.prop]: deepClone(daterange.value) }
+        emit('success', res, daterange.value, props.dropdownIndex)
+      } else {
+        console.error(`菜单项${props.dropdownItem.title}未定义prop,返回内容失败`)
+      }
+    }
+
+    watch(
+      () => props.dropdownItem,
+      (v) => {
+        initData(v)
+      },
+      { immediate: true },
+    )
+
+    return {
+      daterange,
+      datetag,
+      dateTagList,
+      handleStartDate,
+      handleEndDate,
+      handleTagDate,
+      handleReset,
+      handleConfirm,
+    }
+  },
+})
+</script>
+
+<style lang="scss" scoped>
+// 日期范围
+.da-dropdown-daterange {
+  display: flex;
+  align-items: center;
+  margin: 24rpx;
+  background-color: #f5f5f5;
+  border-radius: 999rpx;
+
+  &--date {
+    flex-grow: 1;
+    height: 66rpx;
+    padding: 0 24rpx;
+    font-size: 26rpx;
+    line-height: 66rpx;
+    color: var(--dropdown-text-color);
+    text-align: center;
+    border-radius: 4rpx;
+
+    &.is-actived {
+      color: var(--dropdown-theme-color);
+    }
+  }
+
+  &--separate {
+    flex-shrink: 0;
+    padding: 0 20rpx;
+  }
+
+  &-tags {
+    display: flex;
+    flex-wrap: wrap;
+    justify-content: flex-start;
+    padding: 0 24rpx;
+  }
+}
+
+.da-dropdown-tag {
+  position: relative;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  padding: 20rpx 40rpx;
+  margin-right: 20rpx;
+  margin-bottom: 20rpx;
+  overflow: hidden;
+  font-size: 28rpx;
+  color: var(--dropdown-text-color);
+  background-color: #f5f5f5;
+  border-radius: 999rpx;
+
+  &--text {
+    position: relative;
+    z-index: 1;
+  }
+
+  &.is-actived {
+    color: var(--dropdown-theme-color);
+    background-color: #fff;
+
+    &::after {
+      position: absolute;
+      top: 0;
+      right: 0;
+      bottom: 0;
+      left: 0;
+      z-index: 0;
+      content: '';
+      background-color: var(--dropdown-theme-color);
+      opacity: 0.05;
+    }
+  }
+}
+</style>

+ 220 - 0
src/components/cu-dropdown/components/filter.vue

@@ -0,0 +1,220 @@
+<template>
+  <view class="da-dropdown-filter">
+    <view class="da-dropdown-filter-box" v-for="(item, index) in filterList" :key="index">
+      <view class="da-dropdown-filter--title">{{ item.title }}</view>
+      <view class="da-dropdown-filter-content">
+        <!-- 单选类型 -->
+        <block v-if="item.type === 'radio'">
+          <view
+            v-for="(opt, optIdx) in item.options"
+            class="da-dropdown-filter-item da-dropdown-tag"
+            :class="item.value === opt.value ? 'is-actived' : ''"
+            :key="optIdx"
+            @click="handleRadioChange(item, opt, optIdx, index)">
+            <text class="da-dropdown-tag--text">{{ opt.label }}</text>
+          </view>
+        </block>
+        <!-- 多选类型 -->
+        <block v-else-if="item.type === 'checkbox'">
+          <view
+            v-for="(opt, optIdx) in item.options"
+            class="da-dropdown-filter-item da-dropdown-tag"
+            :class="opt.isActived ? 'is-actived' : ''"
+            :key="optIdx"
+            @click="handleCheckboxChange(item, opt, optIdx, index)">
+            <text class="da-dropdown-tag--text">{{ opt.label }}</text>
+          </view>
+        </block>
+        <!-- 滑块类型 -->
+        <block v-else-if="item.type === 'slider'">
+          <slider
+            style="width: 100%"
+            :value="item.value"
+            :min="item.componentProps.min || 0"
+            :max="item.componentProps.max || 100"
+            :step="item.componentProps.step || 1"
+            :activeColor="item.componentProps.activeColor"
+            :show-value="item.componentProps.showValue"
+            @change="(e) => handleSliderChange(e, item, index)" />
+        </block>
+      </view>
+    </view>
+    <PartDropdownFooter
+      :resetText="dropdownItem.resetText"
+      :confirmText="dropdownItem.confirmText"
+      @reset="handleReset()"
+      @confirm="handleConfirm()"></PartDropdownFooter>
+  </view>
+</template>
+
+<script>
+import { defineComponent, ref, watch } from 'vue'
+import { deepClone } from '../utils'
+import PartDropdownFooter from './part-dropdown-footer.vue'
+
+export default defineComponent({
+  components: { PartDropdownFooter },
+  props: {
+    dropdownItem: {
+      type: Object,
+      default: null,
+    },
+    dropdownIndex: {
+      type: Number,
+    },
+  },
+  emits: ['success', 'change'],
+  setup(props, { emit }) {
+    const filterList = ref(null)
+
+    function initData(dropdownItem, clearValue = false) {
+      const { options = [], value = {} } = dropdownItem
+      if (options?.length) {
+        const list = deepClone(options)
+        for (let i = 0; i < list.length; i++) {
+          const k = list[i]
+          if (clearValue !== true && (value[k.prop] || value[k.prop] === 0)) {
+            k.value = value[k.prop]
+          }
+
+          // 多选
+          if (k.type === 'checkbox' && k.value?.length) {
+            if (k.options?.length) {
+              k.options.forEach((x) => {
+                x.isActived = k.value?.includes(x.value)
+              })
+            }
+          }
+        }
+        filterList.value = list
+      } else {
+        filterList.value = []
+      }
+    }
+
+    function handleRadioChange(item, opt, _optIdx, _index) {
+      item.value = opt.value
+    }
+    function handleCheckboxChange(item, opt, _optIdx, _index) {
+      if (opt.isActived) {
+        opt.isActived = false
+        if (item.value?.length) {
+          const idx = item.value.findIndex((k) => k === opt.value)
+          item.value.splice(idx, 1)
+        } else {
+          item.value = []
+        }
+      } else {
+        if (item.value?.length) {
+          item.value.push(opt.value)
+        } else {
+          item.value = [opt.value]
+        }
+        opt.isActived = true
+      }
+    }
+    function handleSliderChange(event, item, _index) {
+      const v = event.detail.value
+      item.value = v
+    }
+    function handleReset() {
+      initData(props.dropdownItem || [], true)
+    }
+    function handleConfirm() {
+      const list = deepClone(filterList.value)
+
+      if (props.dropdownItem?.prop) {
+        const obj = {}
+        for (let i = 0; i < list.length; i++) {
+          const k = list[i]
+          if (k.value || k.value === 0) {
+            obj[k.prop] = k.value
+          }
+        }
+        const res = { [props.dropdownItem.prop]: obj }
+        emit('success', res, obj, props.dropdownIndex)
+      } else {
+        console.error(`菜单项${props.dropdownItem.title}未定义prop,返回内容失败`)
+      }
+    }
+
+    watch(
+      () => props.dropdownItem,
+      (v) => {
+        initData(v || null)
+      },
+      { immediate: true },
+    )
+
+    return {
+      filterList,
+      handleRadioChange,
+      handleCheckboxChange,
+      handleSliderChange,
+      handleReset,
+      handleConfirm,
+    }
+  },
+})
+</script>
+
+<style lang="scss" scoped>
+// 多条件筛选
+.da-dropdown-filter {
+  &-box {
+    padding: 0 24rpx;
+    line-height: 1;
+  }
+
+  &--title {
+    flex-shrink: 0;
+    padding: 20rpx 0;
+    font-size: 26rpx;
+    color: var(--dropdown-text-color);
+    white-space: nowrap;
+  }
+
+  &-content {
+    display: flex;
+    flex-wrap: wrap;
+    justify-content: flex-start;
+  }
+}
+
+.da-dropdown-tag {
+  position: relative;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  padding: 20rpx 40rpx;
+  margin-right: 20rpx;
+  margin-bottom: 20rpx;
+  overflow: hidden;
+  font-size: 26rpx;
+  color: var(--dropdown-text-color);
+  background-color: #f5f5f5;
+  border-radius: 999rpx;
+
+  &--text {
+    position: relative;
+    z-index: 1;
+  }
+
+  &.is-actived {
+    color: var(--dropdown-theme-color);
+    background-color: #fff;
+
+    &::after {
+      position: absolute;
+      top: 0;
+      right: 0;
+      bottom: 0;
+      left: 0;
+      z-index: 0;
+      content: '';
+      background-color: var(--dropdown-theme-color);
+      opacity: 0.05;
+    }
+  }
+}
+</style>

+ 81 - 0
src/components/cu-dropdown/components/part-dropdown-footer.vue

@@ -0,0 +1,81 @@
+<template>
+  <view class="da-dropdown-footer" :style="{
+    padding: isDropdown ? '24rpx' : '',
+    marginTop: isDropdown ? '20rpx' : ''
+  }">
+    <view class="da-dropdown-footer--reset" @click="handleReset()">{{ resetText || '重置' }}</view>
+    <view class="da-dropdown-footer--confirm" @click="handleConfirm()">
+      {{ confirmText || '确定' }}
+    </view>
+  </view>
+</template>
+
+<script>
+import { defineComponent } from 'vue'
+
+export default defineComponent({
+  name: 'PartDropdownFooter',
+  props: {
+    resetText: {
+      type: String,
+      default: '重置',
+    },
+    confirmText: {
+      type: String,
+      default: '确定',
+    },
+    isDropdown: {
+      type: Boolean,
+      default: true,
+    }
+  },
+  emits: ['confirm', 'reset'],
+  setup(_, { emit }) {
+    function handleReset() {
+      emit('reset')
+    }
+    function handleConfirm() {
+      emit('confirm')
+    }
+
+    return {
+      handleReset,
+      handleConfirm,
+    }
+  },
+})
+</script>
+
+<style lang="scss" scoped>
+.da-dropdown-footer {
+  display: flex;
+  align-items: center;
+
+
+  &--reset,
+  &--confirm {
+    display: flex;
+    flex: 1;
+    align-items: center;
+    justify-content: center;
+    height: 66rpx;
+    font-size: 28rpx;
+    color: #555;
+    background-color: #fff;
+    border: 2rpx solid #ccc;
+    border-radius: 66rpx;
+  }
+
+  &--confirm {
+    margin-left: 24rpx;
+    color: #fff;
+    background-color: var(--dropdown-theme-color);
+    border-color: var(--dropdown-theme-color);
+  }
+
+  &--reset:hover,
+  &--confirm:hover {
+    opacity: 0.8;
+  }
+}
+</style>

+ 220 - 0
src/components/cu-dropdown/components/picker.vue

@@ -0,0 +1,220 @@
+<template>
+  <view class="da-dropdown-picker" v-if="viewCol && viewCol.length">
+    <view
+      class="da-dropdown-picker-inner"
+      v-for="(vc, vci) in viewCol"
+      :key="vci">
+      <scroll-view
+        class="da-dropdown-picker-view"
+        scroll-y>
+        <view
+          class="da-dropdown-picker-item"
+          :class="vr.checked ? 'is-actived' : ''"
+          v-for="(vr, vri) in viewRow[vci]"
+          :key="vri"
+          @click="handleSelect(vr, vci, vri)">
+          <text class="da-dropdown-picker-item--name">{{ vr.label }}</text>
+          <text class="da-dropdown-picker-item--icon" v-if="vr.children && vr.children.length"></text>
+          <text class="da-dropdown-picker-item--check" v-if="vr.checked && (!vr.children || vr.children.length === 0)" />
+        </view>
+      </scroll-view>
+    </view>
+  </view>
+</template>
+
+<script>
+import { defineComponent, ref, watch } from 'vue'
+import { deepClone } from '../utils'
+
+export default defineComponent({
+  props: {
+    dropdownItem: {
+      type: Object,
+      default: null,
+    },
+    dropdownIndex: {
+      type: Number,
+    },
+  },
+  emits: ['success'],
+  setup(props, { emit }) {
+    const viewCol = ref([])
+    const viewRow = ref([])
+
+    function checkData(selected, list) {
+      for (let i = 0; i < list.length; i++) {
+        const k = list[i]
+        for (let j = 0; j < selected.length; j++) {
+          const x = selected[j]
+          if (k.value === x) {
+            k.checked = true
+            viewCol.value.push(k.value)
+            viewRow.value.push(list)
+            if (k.children?.length) {
+              checkData(selected, k.children)
+            }
+            break
+          }
+        }
+      }
+    }
+
+    function initData(item) {
+      const list = deepClone(item?.options || [])
+      if (list?.length) {
+        if (item.value?.length) {
+          viewCol.value = []
+          viewRow.value = []
+
+          checkData(item.value, list)
+        } else {
+          viewCol.value.push('tmpValue')
+          viewRow.value.push(list)
+        }
+      } else {
+        viewCol.value = []
+        viewRow.value = []
+      }
+    }
+
+    function handleSelect(item, colIndex, _rowIndex) {
+      let lastItem = false
+      viewCol.value.splice(colIndex)
+      viewCol.value[colIndex] = item.value
+
+      if (viewRow.value[colIndex]?.length) {
+        viewRow.value[colIndex].forEach(k => {
+          k.checked = false
+        })
+      }
+
+      item.checked = true
+      const list = item?.children || null
+
+      if (list?.length) {
+        viewCol.value[colIndex + 1] = 'tmpValue'
+        viewRow.value[colIndex + 1] = list
+        lastItem = false
+      } else {
+        console.warn('最后一项', item)
+        lastItem = true
+      }
+
+      try {
+        if (viewRow.value[colIndex + 1]?.length) {
+          viewRow.value[colIndex + 1].forEach(k => {
+            k.checked = false
+          })
+        }
+      } catch (e) {
+        console.warn('try clean row data', e)
+        // --
+      }
+
+      if (lastItem) {
+        if (props.dropdownItem?.prop) {
+          const res = { [props.dropdownItem.prop]: deepClone(viewCol.value) }
+          emit('success', res, viewCol.value, props.dropdownIndex)
+        } else {
+          console.error(`菜单项${props.dropdownItem.title}未定义prop,返回内容失败`)
+        }
+      }
+    }
+
+    watch(
+      () => props.dropdownItem,
+      (v) => {
+        initData(v)
+      },
+      { immediate: true },
+    )
+
+    return {
+      viewCol,
+      viewRow,
+
+      handleSelect,
+    }
+  },
+})
+</script>
+
+<style lang="scss" scoped>
+.da-dropdown-picker {
+  display: flex;
+  width: 100%;
+  max-height: 60vh;
+  overflow: hidden;
+  line-height: 1;
+
+  &-inner {
+    flex-grow: 1;
+  }
+
+  &-view {
+    display: flex;
+
+    /* #ifdef MP-ALIPAY */
+    flex-direction: column;
+    flex-wrap: wrap;
+
+    /* #endif */
+
+    width: 100%;
+    height: 100%;
+
+    + .da-dropdown-picker-view {
+      border-left: 1px solid #eee;
+    }
+  }
+
+  &-item {
+    display: flex;
+    align-items: center;
+    justify-content: space-between;
+    padding: 24rpx;
+    font-size: 26rpx;
+    color: var(--dropdown-text-color);
+    text-align: left;
+
+    &--icon {
+      width: 24rpx;
+      height: 24rpx;
+
+      &::after {
+        /* stylelint-disable-next-line font-family-no-missing-generic-family-keyword */
+        font-family: 'da-dropdown-iconfont' !important;
+        font-size: 26rpx;
+        font-style: normal;
+        content: '\e643';
+        -webkit-font-smoothing: antialiased;
+        -moz-osx-font-smoothing: grayscale;
+      }
+    }
+
+    &--check {
+      flex-shrink: 0;
+      width: 24rpx;
+      height: 24rpx;
+
+      &::after {
+        /* stylelint-disable-next-line font-family-no-missing-generic-family-keyword */
+        font-family: 'da-dropdown-iconfont' !important;
+        font-size: 26rpx;
+        font-style: normal;
+        content: '\e696';
+        -webkit-font-smoothing: antialiased;
+        -moz-osx-font-smoothing: grayscale;
+      }
+    }
+
+    &:hover {
+      background: #eee;
+    }
+
+    &.is-actived {
+      color: var(--dropdown-theme-color);
+    }
+  }
+}
+</style>

File diff suppressed because it is too large
+ 654 - 0
src/components/cu-dropdown/index.vue


+ 443 - 0
src/components/cu-dropdown/readme.md

@@ -0,0 +1,443 @@
+# da-dropdown
+
+一个基于 Vue3 的头部导航栏下拉弹窗组件,多平台兼容。
+
+组件一直在更新,遇到问题可在下方讨论。
+
+`同时更新 Vue2 版本,在此查看 ===>` **[Vue2 版](https://ext.dcloud.net.cn/plugin?id=13062)**
+
+### 关于使用
+
+可在右侧的`使用 HBuilderX 导入插件`或`下载示例项目ZIP`,示例项目已添加多个示例,方便快速上手。
+
+可通过下方的示例及文档说明,进一步了解使用组件相关细节参数。
+
+插件地址:https://ext.dcloud.net.cn/plugin?id=11840
+
+### 功能一览
+
+1. 下拉列表(单选)
+2. 点击常亮
+3. 点击排序
+4. 下拉筛选(单选按钮、多选按钮、滑动选择器)
+5. 级联筛选(单选)
+6. 日期筛选(日期快选、日期区间选择)
+7. 顶部搜索
+8. 自定插槽
+
+### 组件示例
+
+```jsx
+<template>
+  <DaDropdown
+    :dropdownMenu="dropdownMenuList"
+    themeColor="#007aff"
+    textColor="#333333"
+    :duration="300"
+    fixedTop
+    @confirm="handleConfirm"
+    @close="handleClose"
+    @open="handleOpen">
+    <template #slot1="{item,index}">
+      <view style="padding: 40px">自定义插槽内容</view>
+    </template>
+  </DaDropdown>
+</template>
+```
+
+```js
+import { defineComponent, ref } from 'vue'
+
+import DaDropdown from '@/components/da-dropdown/index.vue'
+
+export default defineComponent({
+  components: { DaDropdown },
+  setup() {
+    const dropdownMenuList = ref([
+      // 演示数据请看下方各模块说明或下载示例项目查看
+      // ...
+    ])
+    function handleConfirm(v) {
+      console.log('handleConfirm ==>', v)
+    }
+    function handleClose(v) {
+      console.log('handleClose ==>', v)
+    }
+    function handleOpen(v) {
+      console.log('handleOpen ==>', v)
+    }
+    return {
+      dropdownMenuList,
+      handleConfirm,
+      handleClose,
+      handleOpen,
+    }
+  },
+})
+```
+
+### 组件参数
+
+| 属性                 | 类型      | 默认值    | 必填 | 说明                               |
+| :------------------- | :-------- | :-------- | :--- | :--------------------------------- |
+| v-model:dropdownMenu | `Array`   | `[]`      | 是   | 导航菜单数据                       |
+| themeColor           | `String`  | `#007aff` | 否   | 主题颜色                           |
+| textColor            | `String`  | `#333333` | 否   | 导航文字颜色                       |
+| bgColor              | `String`  | `#ffffff` | 否   | 背景颜色,当固定在顶部时,此为必填 |
+| fixedTop             | `Boolean` | `false`   | 否   | 是否固定在顶部                     |
+| fixedTopValue        | `Number`  | `0`       | 否   | 固定在头部时的位置,单位 px        |
+| duration             | `Number`  | `300`     | 否   | 弹窗动画的过渡时间                 |
+
+> 温馨提示:如果页面定义了 "navigationStyle": "custom" ,因此固定头部时需要额外获取状态栏高度,以免被异形屏头部覆盖,此时的 fixedTopValue 的作用就出来了,通过 fixedTopValue 自定义加减固定头部所处的位置。
+
+
+### 组件事件
+
+| 事件名称 | 回调参数                   | 说明                                                               |
+| :------- | :------------------------- | :----------------------------------------------------------------- |
+| open     | `(index) => void`          | 打开弹窗时回调                                                     |
+| close    | `(index,menuList) => void` | 关闭弹窗时回调                                                     |
+| confirm  | `(value,data) => void`     | 确定选择内容时回调,返回选择的数据,格式`{'菜单项prop值': '内容'}` |
+
+
+### 组件方法
+
+| 事件名称          | 回调参数                   | 说明                                    |
+| :---------------- | :------------------------- | :-------------------------------------- |
+| openMenuItemPopup | `(index) => void`          | 打开指定位置的菜单项弹窗                |
+| closeMenuPopup    | `() => void`               | 关闭菜单项弹窗                          |
+| getMenuValue      | `() => object`             | 获取菜单存在的值                        |
+| updateMenu        | `(prop,value,key) => void` | 更新菜单项内容【参考示例7】             |
+| setMenuLoading    | `(prop,state) => void`     | 操作指定菜单项为加载中状态【参考示例7】 |
+| getMenuIndex      | `(prop) => number`         | 获取菜单项所在索引位置                  |
+| getMenuList       | `() => array`              | 获取当前菜单列表数据【参考示例6】       |
+
+
+### 组件菜单项
+
+#### dropdownMenu 基础参数
+
+| 属性        | 类型       | 默认值 | 必填 | 说明                                                             |
+| :---------- | :--------- | :----- | :--- | :--------------------------------------------------------------- |
+| title       | `String`   | -      | 是   | 菜单名称                                                         |
+| prop        | `String`   | -      | 是   | 菜单 prop 值,**菜单项的 prop 是唯一的**                         |
+| type        | `String`   | -      | 是   | 菜单类型,参考下方类型说明                                       |
+| syncDataFn  | `Function` | -      | 否   | 异步函数返回子项数据,优先级大于 options                         |
+| syncDataKey | `String`   | -      | 否   | 异步数据不是根数据时需要。支持嵌套,如:`data.list`【参考示例7】 |
+
+除上方基础参数以外,不同的菜单项(type)会有额外的配置参数
+
+**type 说明**  
+**cell** 下拉列表  
+**click** 点击  
+**sort** 排序  
+**filter** 复杂筛选  
+**picker** 级联  
+**daterange** 日期范围  
+**search** 搜索框(菜单项 type 唯一)  
+**slot** 弹窗插槽  
+
+#### 菜单项 - 下拉列表(cell)
+
+| 属性     | 类型               | 默认值                                                 | 必填 | 说明                                       |
+| :------- | :----------------- | :----------------------------------------------------- | :--- | :----------------------------------------- |
+| value    | `Number`\|`String` | -                                                      | 否   | 默认值,和`options`的 value 必须保持同类型 |
+| showAll  | `Boolean`          | `false`                                                | 否   | 是否显示 “不限” 项                         |
+| showIcon | `Boolean`          | `false`                                                | 否   | 是否在选中时显示勾选图标                   |
+| field    | `Object`           | `{ label: 'label', value: 'value', suffix: 'suffix' }` | 否   | 列表子项数据对应内容字段                   |
+| options  | `Array`            | `[]`                                                   | 否   | 下拉列表子项数据                           |
+
+
+```js
+// 简单示例
+const dropdownMenuList = [
+  {
+    title: '下拉',
+    type: 'cell',
+    prop: 'god1',
+    showAll: true,
+    showIcon: true,
+    // value: '2', // 默认内容2
+    options: [
+      { label: '下拉列表项1', value: '1', suffix: '副标题' },
+      { label: '下拉列表项2', value: '2' },
+      { label: '下拉列表项3', value: '3' },
+    ],
+  },
+]
+```
+
+#### 菜单项 - 高亮(click)
+
+| 属性  | 类型      | 默认值 | 必填 | 说明                              |
+| :---- | :-------- | :----- | :--- | :-------------------------------- |
+| value | `Boolean` | -      | 否   | 默认值,true 选中、false 取消选中 |
+
+```js
+// 简单示例
+const dropdownMenuList = [
+  {
+    title: '点击',
+    type: 'click',
+    prop: 'god2',
+    // value: true, // 默认选中
+  },
+]
+```
+
+#### 菜单项 - 排序(sort)
+
+| 属性  | 类型          | 默认值 | 必填 | 说明                        |
+| :---- | :------------ | :----- | :--- | :-------------------------- |
+| value | `asc`\|`desc` | -      | 否   | 默认值,asc 升序、desc 倒序 |
+
+```js
+// 简单示例
+const dropdownMenuList = [
+  {
+    title: '排序',
+    type: 'sort',
+    prop: 'god3',
+    // value: 'asc', // 默认升序
+  },
+]
+```
+
+#### 菜单项 - 筛选(filter)
+
+| 属性    | 类型     | 默认值 | 必填 | 说明                                         |
+| :------ | :------- | :----- | :--- | :------------------------------------------- |
+| value   | `Object` | -      | 否   | 默认值,格式`{ prop1: '值1', prop2: '值2' }` |
+| options | `Array`  | `[]`   | 否   | 筛选子项数据,**说明见下**                   |
+
+##### filter -> options 参数说明
+
+| 属性           | 类型                          | 必填 | 说明                                                                                          |
+| :------------- | :---------------------------- | :--- | :-------------------------------------------------------------------------------------------- |
+| title          | `String`                      | 是   | 筛选项的子项标题                                                                              |
+| type           | `radio`\|`checkbox`\|`slider` | 是   | 筛选项的子项类型,可选 radio 单选按钮、checkbox 多选按钮、slider 滑动选择器                   |
+| prop           | `String`                      | 是   | 筛选项的子项 prop,**注意保持子项 prop 唯一**                                                 |
+| componentProps | `Object`                      | 否   | 筛选项的对应的组件配置,[slider 组件配置](https://uniapp.dcloud.net.cn/component/slider.html) |
+| options        | `Array`                       | 否   | 筛选子项的类型对应的数据                                                                      |
+
+```js
+// 简单示例
+const dropdownMenuList = [
+  {
+    title: '筛选',
+    type: 'filter',
+    prop: 'god4',
+    // 默认选中单选2、多选2、3、滑动30
+    // value: { ft1: '2', ft2: ['2', '3'], ft3: 30 },
+    options: [
+      {
+        title: '单选',
+        type: 'radio',
+        prop: 'ft1',
+        options: [
+          { label: '单选1', value: '1' },
+          { label: '单选2', value: '2' }
+        ],
+      },
+      {
+        title: '多选',
+        type: 'checkbox',
+        prop: 'ft2',
+        options: [
+          { label: '多选1', value: '1' },
+          { label: '多选2', value: '2' }
+        ],
+      },
+      {
+        title: '滑块',
+        type: 'slider',
+        prop: 'ft3',
+        componentProps: {
+          min: 0,
+          max: 100,
+          step: 1,
+          showValue: true,
+        },
+      },
+    ],
+  },
+]
+```
+
+#### 菜单项 - 级联(picker)
+
+| 属性        | 类型       | 默认值                                                     | 必填 | 说明                                                             |
+| :---------- | :--------- | :--------------------------------------------------------- | :--- | :--------------------------------------------------------------- |
+| value       | `Array`    | -                                                          | 否   | 默认值,格式`['一级value', '二级value']`                         |
+| showAll     | `Boolean`  | `false`                                                    | 否   | 是否显示 “不限” 项                                               |
+| showIcon    | `Boolean`  | `false`                                                    | 否   | 是否在选中末级时显示勾选图标                                     |
+| field       | `Object`   | `{ label: 'label', value: 'value', children: 'children' }` | 否   | 级联子项数据对应内容字段                                         |
+| options     | `Array`    | `[]`                                                       | 否   | 级联子项数据                                                     |
+| syncDataFn  | `Function` | -                                                          | 否   | 异步函数返回级联子项数据,优先级大于 options                     |
+| syncDataKey | `String`   | -                                                          | 否   | 异步数据不是根数据时需要。支持嵌套,如:`data.list`【参考示例7】 |
+
+```js
+// 简单示例
+const dropdownMenuList = [
+  {
+    title: '级联选择',
+    type: 'picker',
+    prop: 'god5',
+    showAll: true,
+    showIcon: true,
+    // showAll 为true时相当于在options第一的位置插入“不限”项
+    // { label: '不限', value: '-9999' },
+    field: {
+      label: 'label',
+      value: 'value',
+      children: 'children',
+    },
+    // value: ['2', '22'], // 默认选中 级联X22
+    options: [
+      {
+        label: '级联X1',
+        value: '1',
+        children: [
+          { label: '级联X11', value: '11' },
+          { label: '级联X12', value: '12' },
+        ],
+      },
+      {
+        label: '级联X2',
+        value: '2',
+        children: [
+          { label: '级联X21', value: '21' },
+          { label: '级联X22', value: '22' },
+        ],
+      },
+    ],
+  },
+]
+```
+
+#### 菜单项 - 日期(daterange)
+
+| 属性      | 类型      | 默认值 | 必填 | 说明                                                 |
+| :-------- | :-------- | :----- | :--- | :--------------------------------------------------- |
+| value     | `Object`  | -      | 否   | 默认值,格式`{ start: '开始日期', end: '结束日期' }` |
+| showQuick | `Boolean` | `true` | 否   | 是否显示日期快选                                     |
+
+```js
+// 简单示例
+const dropdownMenuList = [
+  {
+    title: '日期范围',
+    type: 'daterange',
+    prop: 'god6',
+    // 默认选中 2022-01-01到2022-02-01
+    // value: { start: '2022-01-01', end: '2022-02-01' },
+  },
+]
+```
+
+#### 菜单项 - 顶部搜索框(search)
+
+
+当存在此类型时,头部将会展示搜索框,**注意:此类型唯一**
+
+| 属性  | 类型     | 默认值 | 必填 | 说明   |
+| :---- | :------- | :----- | :--- | :----- |
+| value | `String` | -      | 否   | 默认值 |
+
+```js
+// 简单示例
+const dropdownMenuList = [
+  {
+    title: '搜索',
+    type: 'search',
+    prop: 'god0',
+  },
+]
+```
+
+#### 菜单项 - 拓展插槽(slot1、slot2、slot3、slot4、slot5)
+
+拓展插槽有 5 个,足以应付业务需求了,类型名称为`slot1`、`slot2`、`slot3`、`slot4`、`slot5`,这是固定的类型值
+
+| 属性  | 类型     | 默认值 | 必填 | 说明   |
+| :---- | :------- | :----- | :--- | :----- |
+| value | `String` | -      | 否   | 默认值 |
+
+```jsx
+// 简单示例
+<template>
+  <DaDropdown>
+    <template #slot1="{item,index}">
+      <view>自定义插槽2内容 {{item.value}} {{index}}</view>
+    </template>
+    <template #slot2="{item,index}">
+      <view>自定义插槽2内容 {{item.value}} {{index}}</view>
+    </template>
+    <template #slot3="{item,index}">
+      <view>自定义插槽3内容 {{item.value}} {{index}}</view>
+    </template>
+    <template #slot4="{item,index}">
+      <view>自定义插槽4内容 {{item.value}} {{index}}</view>
+    </template>
+    <template #slot5="{item,index}">
+      <view>自定义插槽5内容 {{item.value}} {{index}}</view>
+    </template>
+  </DaDropdown>
+</template>
+```
+
+```js
+const dropdownMenuList = [
+  {
+    title: '插槽1',
+    type: 'slot1',
+    prop: 'god1',
+  },
+  {
+    title: '插槽2',
+    type: 'slot2',
+    prop: 'god2',
+  },
+  {
+    title: '插槽3',
+    type: 'slot3',
+    prop: 'god3',
+  },
+  {
+    title: '插槽4',
+    type: 'slot4',
+    prop: 'god4',
+  },
+  {
+    title: '插槽5',
+    type: 'slot5',
+    prop: 'god5',
+  },
+]
+```
+
+### 组件版本
+
+v2.2.2
+
+### 差异化
+
+已通过测试
+
+> - H5 页面
+> - 微信小程序
+> - 支付宝、钉钉小程序
+> - 字节跳动、抖音、今日头条小程序
+> - 百度小程序
+> - 飞书小程序
+> - QQ 小程序
+> - 京东小程序
+
+未测试
+
+> - 快手小程序由于非企业用户暂无演示
+> - 快应用、360 小程序因 Vue3 支持的原因暂无演示
+
+### 开发组
+
+[@CRLANG](https://crlang.com)

+ 151 - 0
src/components/cu-dropdown/typing.ts

@@ -0,0 +1,151 @@
+/**
+ * 菜单项-下拉配置
+ */
+export interface DaCellOption {
+  /**
+   * 是否显示“不限”选项
+   */
+  showAll?: boolean
+  /**
+   * 是否显示勾选图标
+   */
+  showIcon?: boolean
+}
+/**
+ * 菜单项-点击配置
+ */
+export interface DaClickOption {}
+/**
+ * 菜单项-排序配置
+ */
+export interface DaSortOption {}
+/**
+ * 菜单项-筛选配置
+ */
+export interface DaFilterOption {}
+/**
+ * 菜单项-级联配置
+ */
+export interface DaPickerOption {
+  /**
+   * 是否显示“不限”选项
+   */
+  showAll?: boolean
+  /**
+   * 是否显示勾选图标
+   */
+  showIcon?: boolean
+  field?: {
+    label: string
+    value: string
+    children: string
+  }
+}
+/**
+ * 菜单项-日期范围配置
+ */
+export interface DaDaterangeOption {
+  value?: {
+    start: string
+    end: string
+  }
+}
+/**
+ * 下拉列表-项内容
+ */
+export interface DaCellItemOption extends DaDropdownMenuListOption {
+  /**
+   * 右侧子标题
+   */
+  suffix?: string
+}
+/**
+ * 筛选-项内容
+ */
+export interface DaFilterItemOption {
+  /**
+   * 筛选标题
+   */
+  title: string
+  /**
+   * 筛选类型,可选 radio 单选按钮、checkbox 多选按钮、slider 滑动选择器
+   */
+  type: 'radio' | 'checkbox' | 'slider'
+  /**
+   * 筛选项prop
+   */
+  prop: string
+  /**
+   * 已选内容
+   */
+  value?: string | number | string[] | number[]
+  /**
+   * 筛选项-slider子项组件prop,具体参考 https://uniapp.dcloud.net.cn/component/slider.html
+   */
+  componentProp?: object
+  /**
+   * 筛选项-子项
+   */
+  options?: DaDropdownMenuListOption[]
+}
+
+/**
+ * 级联-项内容
+ */
+export interface DaPickerItem extends DaDropdownMenuListOption {
+  isActived: boolean
+  /**
+   * 子项
+   */
+  children?: DaPickerItem[]
+}
+
+/**
+ * 菜单列表选择项
+ */
+export interface DaDropdownMenuListOption {
+  /**
+   * 选择项标题
+   */
+  label: string
+  /**
+   * 选择项内容
+   */
+  value: string
+}
+
+/**
+ * 菜单项
+ */
+export interface DaDropdownMenuListItem extends DaCellOption, DaClickOption, DaSortOption, DaFilterOption, DaPickerOption {
+  /**
+   * 菜单标题
+   */
+  title: string
+  /**
+   * 菜单类型
+   * 可选:cell 下拉选择、click 点击、sort 排序、filter 复杂筛选、picker 级联、daterange 日期范围
+   */
+  type: 'cell' |'click' | 'sort' | 'filter' | 'picker'| 'daterange'
+  /**
+   * 菜单项prop
+   */
+  prop: string
+  /**
+   * 菜单值
+   */
+  value?: string
+  /**
+   * 菜单选项函数,优先级大于 options
+   */
+  syncDataFn?: Function
+  /**
+   * 菜单选项
+   */
+  options?: DaDropdownMenuListOption[] | DaFilterItemOption[]
+}
+
+/**
+ * 菜单列表
+ */
+export type DaDropdownMenuList = DaDropdownMenuListItem[]

+ 207 - 0
src/components/cu-dropdown/utils.ts

@@ -0,0 +1,207 @@
+/**
+ * 深拷贝内容
+ * @param originData 拷贝对象
+ * @author crlang(https://crlang.com)
+ */
+export function deepClone(originData) {
+  const type = Object.prototype.toString.call(originData)
+  let data
+  if (type === '[object Array]') {
+    data = []
+    for (let i = 0; i < originData.length; i++) {
+      data.push(deepClone(originData[i]))
+    }
+  } else if (type === '[object Object]') {
+    data = {}
+    for (const prop in originData) {
+      // eslint-disable-next-line no-prototype-builtins
+      if (originData.hasOwnProperty(prop)) { // 非继承属性
+        data[prop] = deepClone(originData[prop])
+      }
+    }
+  } else {
+    data = originData
+  }
+  return data
+}
+
+export function getValueByKey(object, path, defaultVal = undefined) {
+  console.log('object, path', object, path)
+  // 先将path处理成统一格式
+  let newPath = []
+  if (Array.isArray(path)) {
+    newPath = path
+  } else {
+  // 先将字符串中的'['、']'去除替换为'.',split分割成数组形式
+    newPath = path.replace(/\[/g, '.').replace(/\]/g, '').split('.')
+  }
+
+  // 递归处理,返回最后结果
+  return newPath.reduce((o, k) => {
+    console.log(o, k) // 此处o初始值为下边传入的 object,后续值为每次取的内部值
+    return (o || {})[k]
+  }, object) || defaultVal
+}
+
+/**
+ * 处理部分初始数据
+ * @param data
+ */
+export function checkDataField(options, fields) {
+  if (!fields || !options || options.length === 0) {
+    return options
+  }
+
+  for (let i = 0; i < options.length; i++) {
+    const k = options[i]
+    k.label = k[fields.label || 'label'] || null
+    k.value = k[fields.value || 'value'] || null
+    k.suffix = k[fields.suffix || 'suffix'] || null
+    k.children = k[fields.children || 'children'] || null
+    if (k.children?.length) {
+      k.options = checkDataField(k.children, fields) 
+    }
+  }
+  return options
+}
+
+/**
+ * 格式化数值-个位数补零
+ * @param n 数值
+ * @author crlang(https://crlang.com)
+ */
+export function formatNumber(n) {
+  let s = parseInt(n)
+  if (isNaN(s)) {
+    s = '0'
+  } else {
+    s = s.toString()
+  }
+  return s[1] ? s : `0${s}`
+}
+
+/**
+ * 格式化时间
+ * @param date 时间对象
+ * @param format 格式
+ * @author crlang(https://crlang.com)
+ */
+export function formatTime(date, format) {
+  const daDate = new Date(date.toString().length < 11 ? date * 1000 : date)
+  const fromatsRule = ['y', 'm', 'd', 'h', 'i', 's']
+  let tmp = []
+  const year = daDate.getFullYear()
+  const month = daDate.getMonth() + 1
+  const day = daDate.getDate()
+  const hour = daDate.getHours()
+  const minute = daDate.getMinutes()
+  const second = daDate.getSeconds()
+
+  if (format) {
+    tmp.push(year, month, day, hour, minute, second)
+    tmp = tmp.map(formatNumber)
+    for (let i = 0; i < tmp.length; i++) {
+      format = format.toLowerCase().replace(fromatsRule[i], tmp[i])
+    }
+    return format
+  }
+
+  return `${[year, month, day].map(formatNumber).join('/')} ${[hour, minute, second].map(formatNumber).join(':')}`
+}
+
+/**
+ * 获取某个时间范围
+ *
+ * @param v -1、-7、-14、-30、-60
+ * @returns object {start: y-m-d,end: y-m-d}
+ * @author crlang(https://crlang.com)
+ */
+export function getRangeDate(v) {
+  const now = new Date()
+  const nowTime = now.getTime()
+  const oneDay = 24 * 60 * 60 * 1000
+  const dateRange = { start: '', end: '' }
+  const nowWeekDay = now.getDay() // 今天本周的第几天
+  const nowDay = now.getDate() // 当前日
+  const nowMonth = now.getMonth() // 当前月
+  const nowYear = now.getFullYear() // 当前年
+
+  /**
+   * 获得某个月的天数
+   * @param month 当前月份
+   */
+  const getMonthDays = function(month) {
+    const monthStartDate = new Date(nowYear, month, 1)
+    const monthEndDate = new Date(nowYear, month + 1, 1)
+    const days = (monthEndDate - monthStartDate) / oneDay
+    return days
+  }
+
+  // 昨日
+  if (v === '-1') {
+    dateRange.start = formatTime(new Date(nowTime - oneDay), 'y-m-d')
+    dateRange.end = dateRange.start
+    // 本周
+  } else if (v === '-7') {
+    const weekStart = new Date(nowYear, nowMonth, nowDay - nowWeekDay + 1)
+    const weekEnd = new Date(nowTime + oneDay) // 今日
+    dateRange.start = formatTime(weekStart, 'y-m-d')
+    dateRange.end = formatTime(weekEnd, 'y-m-d')
+    // 上周
+  } else if (v === '-14') {
+    const weekStart = new Date(nowYear, nowMonth, nowDay - nowWeekDay - 6)
+    const weekEnd = new Date(nowYear, nowMonth, nowDay - nowWeekDay)
+    dateRange.start = formatTime(weekStart, 'y-m-d')
+    dateRange.end = formatTime(weekEnd, 'y-m-d')
+    // 本月
+  } else if (v === '-30') {
+    const monthStart = new Date(nowYear, nowMonth, 1)
+    const monthEnd = new Date(nowTime + oneDay)
+    dateRange.start = formatTime(monthStart, 'y-m-d')
+    dateRange.end = formatTime(monthEnd, 'y-m-d')
+    // 上月
+  } else if (v === '-60') {
+    const lastMonthDate = new Date() // 上月日期
+    lastMonthDate.setDate(1)
+    lastMonthDate.setMonth(lastMonthDate.getMonth() - 1)
+    const lastMonth = lastMonthDate.getMonth()
+    const lastMonthStart = new Date(nowMonth === 0 ? nowYear - 1 : nowYear, lastMonth, 1)
+    const lastMonthEnd = new Date(nowMonth === 0 ? nowYear - 1 : nowYear, lastMonth, getMonthDays(lastMonth))
+    dateRange.start = formatTime(lastMonthStart, 'y-m-d')
+    dateRange.end = formatTime(lastMonthEnd, 'y-m-d')
+  } else {
+    // 传入 v 为整数是即为近 xx 天
+    if (v > 0) {
+      dateRange.start = formatTime(new Date(nowTime - oneDay * parseInt(v)), 'y-m-d')
+      dateRange.end = formatTime(new Date(nowTime - oneDay), 'y-m-d') // 不含今天
+    }
+  }
+  return dateRange
+}
+
+export const menuInitOpts = {
+  cell: {
+    showArrow: true,
+  },
+  click: {
+  },
+  sort: {
+    showSort: true,
+  },
+  filter: {
+    showArrow: true,
+  },
+  picker: {
+    showArrow: true,
+  },
+  daterange: {
+    showQuick: true,
+    showArrow: true,
+  },
+  slot: {
+    showArrow: true,
+  },
+  search: {
+    showSearch: true,
+  },
+}

+ 63 - 0
src/components/cu-progress/index.scss

@@ -0,0 +1,63 @@
+.ayi-progress {
+	display: flex;
+	align-items: center;
+	min-height: 34rpx;
+	&-bar {
+		box-sizing: border-box;
+		flex: 1;
+		margin-right: 10rpx;
+		&__outer {
+			border-radius: 50rpx;
+			overflow: hidden;
+			position: relative;
+		}
+		&__inner {
+			position: absolute;
+			left: 0;
+			top: 0;
+			height: 100%;
+			background-color: #007aff;
+			text-align: right;
+			border-radius: 50rpx;
+			line-height: 1;
+			white-space: nowrap;
+			transition: width 0.6s ease;
+		}
+	}
+	&__text {
+		display: inline-block;
+		height: 34rpx;
+		width: 100rpx;
+		line-height: 34rpx;
+		color: #606266;
+		font-size: 24rpx;
+		text-align: left;
+	}
+	&__icon {
+		font-size: 30rpx;
+	}
+	&.is-success {
+		.ayi-progress-bar__inner {
+			background-color: #4cd964;
+		}
+		.ayi-progress__text {
+			color: 4cd964;
+		}
+	}
+	&.is-warning {
+		.ayi-progress-bar__inner {
+			background-color: #f0ad4e;
+		}
+		.ayi-progress__text {
+			color: #f0ad4e;
+		}
+	}
+	&.is-error {
+		.ayi-progress-bar__inner {
+			background-color: #dd524d;
+		}
+		.ayi-progress__text {
+			color: #dd524d;
+		}
+	}
+}

+ 82 - 0
src/components/cu-progress/index.vue

@@ -0,0 +1,82 @@
+<template>
+	<view class="ayi-progress">
+    <view class="ayi-progress-bar" v-if="type === 'line'">
+      <view class="ayi-progress-bar__outer" :style="{ height: setRpx(strokeWidth), backgroundColor: unColor }">
+        <view class="ayi-progress-bar__inner" :style="{ backgroundColor, width }"></view>
+      </view>
+    </view>
+    <slot name="text">
+      <view class="ayi-progress__text" v-if="showText">
+        <template v-if="!status">{{ value }}%</template>
+        <text class="ayi-progress__icon" v-else :class="icon"></text>
+      </view>
+    </slot>
+  </view>
+</template>
+<script lang="ts" setup>
+/**
+ * @description 进度条,支持多种颜色显示
+ * @property {Number} value 绑定值
+ * @property {String} type 类型
+ * @property {String} strokeWidth 线条宽度
+ * @property {String} showText 是否显示文本
+ * @property {String} color 线条颜色, 支持多色
+ * @property {String} status 状态
+ * @property {Boolean} icon 尾部图标
+ * @example <ayi-progress :value="40"></ayi-progress>
+ */
+import { computed } from "vue"
+import { setRpx, getCurrentColor } from "./tools"
+const props = defineProps({
+	value: {
+		type: Number,
+		default: 0,
+		required: true
+	},
+	type: {
+		type: String,
+		default: "line"
+	},
+	strokeWidth: {
+		type: Number,
+		default: 12
+	},
+	showText: {
+		type: Boolean,
+		default: true
+	},
+	color: {
+		type: [String, Array],
+		default: ""
+	},
+	unColor: {
+		type: String,
+		default: "#ebeef5"
+	},
+	status: {
+		type: Boolean
+	},
+	icon: String
+})
+// 宽度
+const width = computed(() => {
+	if (props.value > 100) {
+		return "100%"
+	} else if (props.value < 0) {
+		return 0
+	} else {
+		return `${props.value}%`
+	}
+})
+// 背景颜色
+const backgroundColor = computed(() => {
+	return getCurrentColor({
+		value: props.value,
+		color: props.color,
+		max: 100
+	})
+})
+</script>
+<style lang="scss" scoped>
+@import "./index.scss";
+</style>

+ 52 - 0
src/components/cu-progress/tools.ts

@@ -0,0 +1,52 @@
+function getTag(value) {
+	if (value == null) {
+		return value === undefined ? "[object Undefined]" : "[object Null]"
+	}
+	return toString.call(value)
+}
+
+function isObjectLike(value) {
+	return typeof value === "object" && value !== null
+}
+
+export function isNumber(value) {
+	return typeof value === "number" || (isObjectLike(value) && getTag(value) == "[object Number]")
+}
+
+export function isBoolean(value) {
+	return typeof value === "boolean"
+}
+
+export function isArray(value) {
+	return Array.isArray(value)
+}
+export function setRpx(val : any) : string {
+	return isArray(val) ? val.map(setRpx).join(" ") : isNumber(val) ? `${val}rpx` : val
+}
+export function isString(value) {
+	return typeof value === "string"
+}
+export function getCurrentColor({ color, max, value } : any) {
+	if (isString(color)) {
+		return color
+	} else {
+		const colorArray = color
+			.map((item : any, index : number) => {
+				if (isString(item)) {
+					return {
+						color: item,
+						value: (index + 1) * (max / color.length)
+					}
+				}
+				return item
+			})
+			.sort((a : any, b : any) => a.value - b.value)
+
+		for (let i = 0; i < colorArray.length; i++) {
+			if (colorArray[i].value >= value) {
+				return colorArray[i].color
+			}
+		}
+		return colorArray[colorArray.length - 1].color
+	}
+}

+ 144 - 0
src/components/cu-timeline/index.vue

@@ -0,0 +1,144 @@
+<template>
+    <view class="cu-timeline">
+        <view class="item"
+            :style="{ '--color': '#0396FF', '--gap': gap, '--left': leftWidth }" v-if="showInit">
+            <view class="left" v-if="showLeft">
+                <slot name="left"></slot>
+            </view>
+            <view class="line">
+                <view class="svg">
+                    <i class="my my-start" style="width: 16px;height: 16px;"></i>
+                </view>
+            </view>
+            <view class="right">
+                <slot name="right"></slot>
+            </view>
+        </view>
+        <view v-for="(item, index) in dataList" :key="index" class="item"
+            :style="{ '--color': item.color || '#0396FF', '--bgcolor': item.color ? item.color + '1a' : '#0396FF1a', '--gap': gap, '--left': leftWidth }">
+            <view class="left" v-if="showLeft">
+                <slot name="left" :item="item"></slot>
+            </view>
+            <view class="line">
+                <view class="svg">
+                    <slot name="dot" :item="item"></slot>
+                </view>
+            </view>
+            <view class="right">
+                <slot name="right" :item="item"></slot>
+            </view>
+        </view>
+    </view>
+</template>
+
+<script>
+export default {
+    props: {
+        dataList: {
+            type: Array,
+            default: () => []
+        },
+        showInit: {
+            type: Boolean,
+            default: true
+        },
+        showLeft: {
+            type: Boolean,
+            default: true
+        },
+        leftWidth: {
+            type: String,
+            default: '150rpx'
+        },
+        gap: {
+            type: String,
+            default: '20rpx'
+        }
+    },
+    data() {
+        return {
+
+        }
+    }
+};
+</script>
+
+<style lang="scss" scoped>
+.cu-timeline {
+    position: relative;
+    padding: 10rpx;
+    display: flex;
+    flex-direction: column;
+
+    .item {
+        display: flex;
+        justify-content: center;
+    }
+
+    .left {
+        font-size: 13px;
+        width: 150px;
+        overflow: hidden;
+        margin-bottom: var(--gap);
+        text-align: right;
+        white-space: pre-wrap;
+        word-wrap: break-word;
+        word-break: break-all;
+        flex-shrink: 0;
+        margin-right: 10px;
+    }
+
+    .line {
+        margin: 2px 10px 0px 10px;
+        width: 2px;
+        background: #eeeeee;
+        position: relative;
+        flex-shrink: 0;
+        flex-grow: 0;
+
+        .svg {
+            position: absolute;
+            top: 0px;
+            left: 50%;
+            transform: translateX(-50%);
+            display: flex;
+            justify-content: center;
+            align-items: center;
+            width: 16px;
+            height: 16px;
+            // background: var(--color);
+            // box-shadow: 0 0 3px 1px var(--color);
+            // border-radius: 50%;
+        }
+        .dot {
+            position: absolute;
+            top: 0px;
+            left: 50%;
+            transform: translateX(-50%);
+            display: flex;
+            justify-content: center;
+            align-items: center;
+            width: 8px;
+            height: 8px;
+            background: var(--color);
+            box-shadow: 0 0 3px 1px var(--color);
+            border-radius: 50%;
+        }
+    }
+
+    .right {
+        flex: 1;
+        margin-bottom: var(--gap);
+        margin-left: 10px;
+        font-size: 13px;
+
+
+    }
+
+    .item:last-child {
+        .line {
+            background: transparent;
+        }
+    }
+}
+</style>

+ 471 - 0
src/components/custom-button/custom-button.vue

@@ -0,0 +1,471 @@
+<template>
+	<view :style="{
+		width: width,
+		height: height,
+		backgroundColor: disabled && type !== 'hollow' ? '#ffffff' : 'rgba(0, 0, 0, 0)',
+		borderTopLeftRadius: radius[0],
+		borderTopRightRadius: radius.length === 1 ? radius[0] : radius[1],
+		borderBottomRightRadius: radius.length === 1 ? radius[0] : radius[2],
+		borderBottomLeftRadius: radius.length === 1 ? radius[0] : radius[3],
+		transform: 'scale(0.99)'
+	}">
+		<button class="wyb-button" :open-type="openType" :disabled="disabled" :data-param="dataParam"
+			:hover-stop-propagation="true" :lang="lang" :session-from="sessionFrom"
+			:send-message-title="sendMessageTitle" :send-message-path="sendMessagePath"
+			:send-message-img="sendMessageImg" :show-message-card="showMessageCard" @touchstart="touch"
+			@tap="onClick" :form-type="formType" @longtap.stop="onLongClick" @getphonenumber="getphonenumber"
+			@getuserinfo="getuserinfo" @error="error" @opensetting="opensetting" @launchapp="launchapp"
+			:hover-class="disabled || ripple || type === 'none' ? ''
+				: (type === 'filled' ? 'btnHoverClass-filled' : (type === 'plain' ? 'btnHoverClass-plain' : 'btnHoverClass-hollow'))" :style="{
+				display: autoWidth === 'auto' ? 'inline-flex' : 'flex',
+				width: autoWidth,
+				height: height,
+				color: type === 'filled' ? '#ffffff' : color,
+				border: type === 'plain' || type === 'hollow' ? borderSize + 'px ' + color + ' ' + borderType : 'none',
+				backgroundColor: type === 'filled' ? color : (type === 'plain' ? RGBChange(color, 0.88, 'light') : 'rgba(0, 0, 0, 0)'),
+				borderTopLeftRadius: radius[0],
+				borderTopRightRadius: radius.length === 1 ? radius[0] : radius[1],
+				borderBottomRightRadius: radius.length === 1 ? radius[0] : radius[2],
+				borderBottomLeftRadius: radius.length === 1 ? radius[0] : radius[3],
+				transform: 'scale(1.01)',
+				opacity: disabled ? (type !== 'filled' ? 0.4 : 0.5) : 1.0,
+				fontSize: ftSize,
+				boxShadow: boxShadow
+			}">
+			<view v-if="isShowLoading" class="load-container loading">
+				<view class="loader" :style="{
+					width: loaderSize + 'rpx',
+					height: loaderSize + 'rpx',
+					borderTop: '1px solid ' + loadingColor.top,
+					borderRight: '1px solid ' + loadingColor.right,
+					borderBottom: '1px solid ' + loadingColor.bottom,
+					borderLeft: '1px solid ' + loadingColor.left,
+					transform: 'scale(0.5)'
+				}" />
+			</view>
+			<image v-if="iconPath" :src="iconPath" :style="{
+				width: icSize,
+				height: icSize,
+				marginRight: iconMarginRight + 'rpx'
+			}" />
+			<view v-if="!startCountDown">
+				<slot></slot>
+			</view>
+			<view v-if="countDown && startCountDown" class="count-down">
+				<text>{{ count }}秒{{ countDownText }}</text>
+			</view>
+			<view v-if="ripple" class="waveRipple" :class="[isWaving ? 'waveActive' : '']" :style="{
+				top: rippleToTop + 'px',
+				left: rippleToLeft + 'px',
+				width: domInfo.targetWidth + 'px',
+				height: domInfo.targetWidth + 'px',
+				backgroundColor: rippleBgColor
+			}">
+			</view>
+		</button>
+	</view>
+</template>
+
+<script>
+export default {
+	computed: {
+		loadingColor() {
+			let color = this.color
+			if (this.type === 'filled') color = '#ffffff'
+			let rgbList = this.hexToRgb(color)
+			let top = 'rgba(' + rgbList[0] + ',' + rgbList[1] + ',' + rgbList[2] + ', 0.3)'
+			let bottom = 'rgba(' + rgbList[0] + ',' + rgbList[1] + ',' + rgbList[2] + ', 0.3)'
+			let right = 'rgba(' + rgbList[0] + ',' + rgbList[1] + ',' + rgbList[2] + ', 0.3)'
+			let left = 'rgb(' + rgbList[0] + ',' + rgbList[1] + ',' + rgbList[2] + ')'
+			return {
+				top,
+				bottom,
+				right,
+				left
+			}
+		},
+		loaderSize() {
+			return parseFloat(this.height.replace(/[^0-9]/ig, "")) * 0.3
+		},
+		ftSize() {
+			return (this.fontSize || (parseFloat(this.height.replace(/[^0-9]/ig, "")) * 0.388)) + 'rpx'
+		},
+		icSize() {
+			return (this.iconSize || (parseFloat(this.height.replace(/[^0-9]/ig, "")) * 0.388)) + 'rpx'
+		}
+	},
+	data() {
+		return {
+			rippleToTop: 0,
+			rippleToLeft: 0,
+			domInfo: {},
+			isWaving: false,
+			btnStyle: {},
+			basePlateStyle: {},
+			iconStyle: {},
+			timer: {},
+			flag: false,
+			startCountDown: false,
+			count: this.countDownNum,
+			timer: {}
+		}
+	},
+	props: {
+		type: {
+			type: String,
+			default: 'filled'
+		},
+		width: {
+			type: String,
+			default: uni.getSystemInfoSync().screenWidth * 0.9 + 'px'
+		},
+		autoWidth: {
+			type: String,
+			default: '100%'
+		},
+		height: {
+			type: String,
+			default: '80rpx'
+		},
+		color: {
+			type: String,
+			default: '#007aff'
+		},
+		fontSize: {
+			type: [String, Number],
+			default: ''
+		},
+		radius: {
+			type: Array,
+			default() {
+				return ['5px']
+			}
+		},
+		borderSize: {
+			type: [String, Number],
+			default: 1
+		},
+		borderType: {
+			type: String,
+			default: 'solid'
+		},
+		rippleBgColor: {
+			type: String,
+			default: 'rgba(0, 0, 0, 0.15)'
+		},
+		ripple: {
+			type: Boolean,
+			default: false
+		},
+		isShowLoading: {
+			type: Boolean,
+			default: false
+		},
+		disabled: {
+			type: Boolean,
+			default: false
+		},
+		iconPath: {
+			type: String,
+			default: ''
+		},
+		iconSize: {
+			type: [String, Number],
+			default: ''
+		},
+		iconMarginRight: {
+			type: [String, Number],
+			default: '15'
+		},
+		boxShadow: {
+			type: String,
+			default: ''
+		},
+		countDown: {
+			type: Boolean,
+			default: false
+		},
+		countDownNum: {
+			type: Number,
+			default: 60
+		},
+		countDownText: {
+			type: String,
+			default: '后重新获取'
+		},
+		formType: {
+			type: String,
+			default: ''
+		},
+		dataParam: {
+			type: String,
+			default: ''
+		},
+		openType: {
+			type: String,
+			default: ''
+		},
+		lang: {
+			type: String,
+			default: 'zh_CN'
+		},
+		sessionFrom: {
+			type: String,
+			default: ''
+		},
+		sendMessageTitle: {
+			type: String,
+			default: ''
+		},
+		sendMessagePath: {
+			type: String,
+			default: ''
+		},
+		sendMessageImg: {
+			type: String,
+			default: ''
+		},
+		showMessageCard: {
+			type: Boolean,
+			default: false
+		}
+	},
+	watch: {
+		count(val) {
+			if (val === 0) {
+				clearInterval(this.timer)
+				this.startCountDown = false
+				this.count = this.countDownNum
+			}
+		}
+	},
+	methods: {
+		touch(e) {
+			this.throttle(() => {
+				if (!this.disabled) {
+					if (this.ripple) {
+						this.isWaving = false
+						this.$nextTick(function () {
+							this.getWaveQuery(e)
+						})
+					}
+				}
+			})
+		},
+		onClick(e) {
+			if (!this.disabled && !this.startCountDown) {
+				this.$emit('click', e)
+			}
+			if (this.countDown && !this.startCountDown) {
+				this.startCountDown = true
+				this.timer = setInterval(() => {
+					this.count--
+				}, 1000)
+			}
+		},
+		onLongClick(e) {
+			if (!this.disabled && !this.startCountDown) {
+				this.$emit('longclick', e)
+			}
+		},
+		getWaveQuery(e) {
+			this.getElQuery().then(res => {
+				let data = res[0]
+				if (!data.width || !data.width) return
+				data.targetWidth = data.height > data.width ? data.height : data.width
+				if (!data.targetWidth) return
+				this.domInfo = data;
+				let touchesX = ''
+				let touchesY = ''
+				// #ifndef MP-BAIDU || MP-ALIPAY
+				touchesX = e.touches[0].clientX
+				touchesY = e.touches[0].clientY
+				// #endif
+				// #ifdef MP-BAIDU
+				touchesX = e.changedTouches[0].clientX
+				touchesY = e.changedTouches[0].clientY
+				// #endif
+				// #ifdef MP-ALIPAY
+				touchesX = e.detail.clientX
+				touchesY = e.detail.clientY
+				// #endif
+				this.rippleToTop = touchesY - data.top - data.targetWidth / 2
+				this.rippleToLeft = touchesX - data.left - data.targetWidth / 2
+				this.$nextTick(() => {
+					this.isWaving = true
+				})
+			})
+		},
+		getElQuery() {
+			return new Promise(resolve => {
+				let queryInfo = ''
+				queryInfo = uni.createSelectorQuery().in(this)
+				//#ifdef MP-ALIPAY
+				queryInfo = uni.createSelectorQuery()
+				//#endif
+				queryInfo.select('.wyb-button').boundingClientRect()
+				queryInfo.exec(data => {
+					resolve(data)
+				})
+			})
+		},
+		RGBChange(color, level, type) {
+			// hex转rgb
+			if (color.length === 4) {
+				let arr = color.split('')
+				color = '#' + arr[1] + arr[1] + arr[2] + arr[2] + arr[3] + arr[3]
+			}
+			let color16List = [color.substring(1, 3), color.substring(3, 5), color.substring(5, 7)]
+			let r = parseInt(color16List[0], 16)
+			let g = parseInt(color16List[1], 16)
+			let b = parseInt(color16List[2], 16)
+			let rgbc = [r, g, b]
+			// 减淡或加深
+			for (var i = 0; i < 3; i++)
+				type === 'light' ? rgbc[i] = Math.floor((255 - rgbc[i]) * level + rgbc[i]) : rgbc[i] = Math.floor(rgbc[i] * (1 -
+					level))
+			// rgb转hex
+			let R = rgbc[0].toString(16)
+			let G = rgbc[1].toString(16)
+			let B = rgbc[2].toString(16)
+			if (R.length === 1) R = '0' + R
+			if (G.length === 1) G = '0' + G
+			if (B.length === 1) B = '0' + B
+			return '#' + R + G + B
+		},
+		hexToRgb(color) {
+			if (color.length === 4) {
+				let arr = color.split('')
+				color = '#' + arr[1] + arr[1] + arr[2] + arr[2] + arr[3] + arr[3]
+			}
+			let color16List = [color.substring(1, 3), color.substring(3, 5), color.substring(5, 7)]
+			let r = parseInt(color16List[0], 16)
+			let g = parseInt(color16List[1], 16)
+			let b = parseInt(color16List[2], 16)
+			return [r, g, b]
+		},
+		throttle(fc, waitTime = 500, imme = true) {
+			if (imme) {
+				if (!this.flag) {
+					this.flag = true
+					typeof fc === 'function' && fc()
+					this.timer = setTimeout(() => {
+						this.flag = false
+					}, waitTime)
+				}
+			} else {
+				if (!this.flag) {
+					this.flag = true
+					this.timer = setTimeout(() => {
+						this.flag = false
+						typeof fc === 'function' && fc()
+					}, waitTime)
+				}
+			}
+		},
+		getphonenumber(res) {
+			this.$emit('getphonenumber', res)
+		},
+		getuserinfo(res) {
+			this.$emit('getuserinfo', res)
+		},
+		error(res) {
+			this.$emit('error', res)
+		},
+		opensetting(res) {
+			this.$emit('opensetting', res)
+		},
+		launchapp(res) {
+			this.$emit('launchapp', res)
+		}
+	}
+}
+</script>
+
+<style>
+.wyb-button {
+
+	align-items: center;
+	justify-content: center;
+	position: relative;
+	overflow: hidden;
+	line-height: 1;
+	z-index: 1;
+	box-sizing: border-box;
+	transition: all 0.12s;
+	white-space: nowrap;
+}
+
+.wyb-button::after {
+	border: none;
+	transform-origin: center;
+	transform: scale(1.5);
+}
+
+.btnHoverClass-plain::after {
+	background-color: rgba(0, 0, 0, 0.08) !important;
+}
+
+.btnHoverClass-hollow::after {
+	background-color: rgba(0, 0, 0, 0.08) !important;
+}
+
+.btnHoverClass-filled::after {
+	background-color: rgba(0, 0, 0, 0.12) !important;
+}
+
+.waveRipple {
+	z-index: 0;
+	position: absolute;
+	border-radius: 100%;
+	background-clip: padding-box;
+	transform-origin: center;
+	pointer-events: none;
+	transform: scale(0);
+	user-select: none;
+	opacity: 1;
+}
+
+.waveRipple.waveActive {
+	opacity: 0;
+	transform: scale(2);
+	transition: opacity 0.6s linear, transform 0.6s linear;
+}
+
+.loader {
+	font-size: 10px;
+	position: relative;
+	-webkit-animation: loading .75s infinite linear;
+	animation: loading .75s infinite linear;
+	margin-right: 20rpx;
+}
+
+.loader,
+.loader:after {
+	border-radius: 50%;
+}
+
+@-webkit-keyframes loading {
+	0% {
+		-webkit-transform: rotate(0deg);
+		transform: rotate(0deg);
+	}
+
+	100% {
+		-webkit-transform: rotate(360deg);
+		transform: rotate(360deg);
+	}
+}
+
+@keyframes loading {
+	0% {
+		-webkit-transform: rotate(0deg);
+		transform: rotate(0deg);
+	}
+
+	100% {
+		-webkit-transform: rotate(360deg);
+		transform: rotate(360deg);
+	}
+}
+</style>

+ 18 - 0
src/components/dynamic-form/boolean-component.vue

@@ -0,0 +1,18 @@
+<template>
+    <switch :checked="checked" @change="change" />
+</template>
+<script lang="ts" setup>
+
+const emits = defineEmits(['update:modelValue'])
+const props = defineProps<{
+    modelValue?: boolean
+}>()
+
+const checked = ref(props.modelValue)
+const change = (e: any)=>{
+    emits('update:modelValue', e.detail.value)
+}
+
+
+</script>
+<style scoped lang="scss"></style>

+ 134 - 0
src/components/dynamic-form/download-component.vue

@@ -0,0 +1,134 @@
+<template>
+    <view style="display: flex;flex-direction: column;">
+        <template v-for="(item,index) in fileList" :key="item.id">
+            <div class="download-container">
+                <div class="file-container">
+                    <div class="filename">
+                        {{ item.name }}
+                    </div>
+                </div>
+                <div style="margin-left: 10px">
+                    <uni-icons :size="25" color="#007aff" type="download" style=" padding: 0 5px;" @click="startDownloadContractFile(item)"></uni-icons>
+                </div>
+            </div>
+        </template>
+    </view>
+    <custom-progress v-if="showUploadProgress" :value="currentProgress" style="min-width: 250px"
+        :stroke-width="16"></custom-progress>
+</template>
+
+<script lang="ts" setup>
+import {  ref, PropType } from 'vue'
+import { getDownloadWorkFileUrl, getUploadWorkFileUrl } from '@/api/project-center/work'
+import customProgress from '@/components/cu-progress/index.vue'
+import { downloadFile, saveFile } from '@/api/base/client';
+
+const currentProgress = ref(0) // 控制进度条
+const showUploadProgress = ref(false)
+
+const emits = defineEmits(['update:modelValue'])
+
+
+const props = defineProps({
+    modelValue: {
+        type: Array as PropType<{id: string, name: string}[]>,
+        required: true,
+        default: ()=> []
+    },
+    workId: {
+        type: String
+    },
+    field: {
+        type: Object,
+        required: true
+    }
+})
+
+const fileList = ref<{id:string, name: string}[]>(props.modelValue)
+
+// 获取进度信息
+const onProgressResult = (e: UniApp.OnProgressDownloadResult) => {
+    showUploadProgress.value = true
+    currentProgress.value = e.progress
+
+}
+
+const startDownloadContractFile = async (item: {id: string, name: string}) => {
+    uni.showActionSheet({
+        itemList: ['分享到好友', '添加到收藏'],
+        success: async (res: UniApp.ShowActionSheetRes) => {
+            await handlerDownloadFile(item, res.tapIndex)
+        }
+    });
+
+}
+
+
+// 下载文件
+const handlerDownloadFile = async (item: {id: string, name: string}, sheetIndex: number) => {
+    try {
+        const { url } = await getDownloadWorkFileUrl({
+            id: item.id,
+        })
+        const { savedFilePath } = await downloadFile({ url, downloadProgress: onProgressResult })
+        await saveFile(savedFilePath, item.name, sheetIndex)
+        showUploadProgress.value = false
+        currentProgress.value = 0
+    } catch (_e) {
+        showUploadProgress.value = false
+        currentProgress.value = 0
+    }
+}
+
+</script>
+<script lang="ts">
+export default {
+    options: {
+        styleIsolation: 'apply-shared', //解除样式隔离
+    }
+
+}
+</script>
+<style scoped>
+.download-container {
+    position: relative;
+    display: flex;
+    align-items: center;
+    box-sizing: border-box;
+    margin-top: 10rpx;
+
+}
+
+.file-container {
+    display: flex;
+    align-items: center;
+    overflow: hidden;
+    font-size: 14px;
+    border-radius: 3px;
+    transition: background-color 0.1s cubic-bezier(0, 0, 1, 1);
+}
+
+.filename {
+    padding: 8px 0px 8px 10px;
+    flex: 1;
+    width: 300px;
+    background-color: #f7f8fa;
+    align-items: center;
+    font-size: 13px;
+    overflow: hidden;
+    color: #606266;
+    font-size: 14px;
+    line-height: 1.4286;
+    white-space: nowrap;
+    text-overflow: ellipsis;
+}
+
+.file-extra {
+    margin-left: 20px;
+    color: #606266;
+}
+
+:deep(.is-text-box) {
+    display: none;
+}
+</style>

+ 185 - 0
src/components/dynamic-form/index.vue

@@ -0,0 +1,185 @@
+<template>
+    <template v-for="(field, index) in keys" :key="index">
+      <uni-forms-item
+        :label="props.schema!.properties[field].title"
+        :name="`${field}`"
+        :required="getRequired(props.schema?.required, field)"
+        :rules="[{ required: getRequired(props.schema?.required, field), errorMessage: `${props.schema!.properties[field].title}不能为空`}]"
+      >
+
+      <SelectComponent v-model="props.formData![field]" 
+       v-if="column(props.schema!.properties[field]).enum" 
+       :placeholder="column(props.schema!.properties[field]).placeholder"
+       :field="column(props.schema!.properties[field])"
+       />
+       <StringComponent v-model="props.formData![field]" 
+       v-else-if="['string'].includes(column(props.schema!.properties[field]).type)" 
+       :placeholder="column(props.schema!.properties[field]).placeholder"
+       :field="column(props.schema!.properties[field])"
+       />
+       <BooleanComponent v-model="props.formData![field]" v-else-if="['boolean'].includes(column(props.schema!.properties[field]).type)" :placeholder="column(props.schema!.properties[field]).placeholder"
+       :field="column(props.schema!.properties[field])"
+       />
+       <NumberComponent v-model="props.formData![field]" v-else-if="['number', 'integer'].includes(column(props.schema!.properties[field]).type)" :placeholder="column(props.schema!.properties[field]).placeholder"
+       :field="column(props.schema!.properties[field])"
+       />
+       <UploadComponent v-model="props.formData![field]" 
+       :workId="workId"
+       v-else-if="['upload'].includes(column(props.schema!.properties[field]).type)" 
+       :placeholder="column(props.schema!.properties[field]).placeholder"
+       :field="column(props.schema!.properties[field])"
+       />
+       <DownloadComponent v-model="props.formData![field]" 
+       :workId="workId"
+       v-else-if="['download'].includes(column(props.schema!.properties[field]).type)" 
+       :placeholder="column(props.schema!.properties[field]).placeholder"
+       :field="column(props.schema!.properties[field])"
+       />
+      </uni-forms-item>
+    </template>
+  </template>
+  
+  <script setup lang="ts">
+  import { h, computed, PropType } from 'vue'
+  import { orderProperties } from '@/utils'
+  import NumberComponent from './number-component.vue'
+  import StringComponent from './string-component.vue'
+  import SelectComponent from './select-component.vue'
+  import BooleanComponent from './boolean-component.vue'
+  import UploadComponent from './upload-component.vue'
+  import DownloadComponent from './download-component.vue'
+
+//   import {
+//     Input,
+//     InputNumber,
+//     Switch,
+//     TimePicker,
+//     Textarea,
+//     DatePicker,
+//     MonthPicker,
+//     YearPicker
+//   } from '@arco-design/web-vue'
+//   import CustomSelect from './Select.vue'
+//   import Upload from './Upload.vue'
+//   import Download from './Download.vue'
+  // 状态下拉框列表枚举值
+  
+  const props = defineProps({
+    workId: {
+      type: String
+    },
+    formData: {
+      type: Object as PropType<{ [key: string]: any }>
+    },
+    schema: {
+      type: Object as PropType<{ [key: string]: any }>,
+      default: () => {
+        return {}
+      }
+    }
+  })
+  
+  const keys = computed(() => {
+    if(!props.schema['ui:order']){
+        return  []
+    }
+    return orderProperties(
+      Object.keys(props.schema?.properties),
+      props.schema['ui:order']
+    )
+  })
+  
+  const getRequired = (fields: string[], field: string) => {
+    if (!fields) {
+      return false
+    }
+    return fields.includes(field)
+  }
+  
+  const column = (field: { [key: string]: any }): any => {
+    return field
+  }
+//   const getFieldComponent = (field: { [key: string]: any }): any => {
+//     if (field.enum) {
+//       return h(CustomSelect, {
+//         options: field.enum.map((item: any, index: number) => {
+//           return {
+//             key: item.value || item,
+//             value: item.value || item,
+//             label: field.enumNames[index] || item
+//           }
+//         }),
+//         multiple: field.multiple,
+//         readonly: field.readonly,
+//         placeholder: `请输入${field.title}`
+//       })
+//     }
+//     switch (field.type) {
+//       case 'string':
+//         switch (field.format) {
+//           case 'time':
+//             return h(TimePicker, {
+//               placeholder: `请选择${field.title}`,
+//               readonly: field.readonly
+//             })
+//           case 'date':
+//             return h(DatePicker, {
+//               placeholder: `请选择${field.title}`,
+//               readonly: field.readonly
+//             })
+//           case 'datetime':
+//             return h(DatePicker, { showTime: true })
+//           case 'month':
+//             return h(MonthPicker, {
+//               placeholder: `请选择${field.title}`,
+//               readonly: field.readonly
+//             })
+//           case 'year':
+//             return h(YearPicker, {
+//               placeholder: `请选择${field.title}`,
+//               readonly: field.readonly
+//             })
+//           case 'textarea':
+//             return h(Textarea, {
+//               autoSize: {
+//                 minRows: field.minRows || 10
+//               },
+//               placeholder: `请输入${field.title}`,
+//               readonly: field.readonly
+//             })
+  
+//           default:
+//             return h(Input, {
+//               placeholder: `请输入${field.title}`,
+//               allowClear: true,
+//               readonly: field.readonly
+//             })
+//         }
+//       case 'upload':
+//         return h(Upload, {
+//           accept: field.accept,
+//           workId: props.workId,
+//           limit: field.limitCount || 0
+//         })
+//       case 'download':
+//         return h(Download, {
+//           default: field.default || []
+//         })
+//       case 'integer':
+//       case 'number':
+//         return h(InputNumber, {
+//           precision: field.type === 'integer' ? 0 : 2,
+//           min: field.minimum,
+//           max: field.maximum,
+//           allowClear: true,
+//           readonly: field.readonly,
+//           placeholder: `请输入${field.title}`
+//         })
+//       case 'boolean':
+//         return Switch
+//       default:
+//         return null
+//     }
+//   }
+  </script>
+  

+ 31 - 0
src/components/dynamic-form/number-component.vue

@@ -0,0 +1,31 @@
+<template>
+    <uni-easyinput @change="change" v-model="inputValue" 
+        :placeholder="field.description || placeholder || `请输入${field.title}`" type="number" />
+</template>
+<script lang="ts" setup>
+
+const emits = defineEmits(['update:modelValue'])
+const props = defineProps({
+    modelValue: {
+        type: [Number]
+    },
+    placeholder: {
+        type: String
+    },
+    readyonly: {
+        type: Boolean,
+        default: () => false
+    },
+    field: {
+        type: Object,
+        required: true
+    },
+})
+
+const inputValue = ref(props.modelValue)
+
+const change = (e: any) => {
+    emits('update:modelValue', inputValue.value)
+}
+</script>
+<style scoped lang="scss"></style>

+ 176 - 0
src/components/dynamic-form/select-component.vue

@@ -0,0 +1,176 @@
+<template>
+    <view class="custom-select-class" @click="showTreeSelectModal()">
+        <view class="custom-select-content">
+            <text v-if="selectValue">{{ visibleName }}</text>
+            <text v-else style="color:#909399;font-size: 12px;">{{ field.description || placeholder ||
+                `请输入${field.title}` }}</text>
+        </view>
+        <uni-icons :size="14" color="#909399" type="down" style=" padding: 0 5px;"></uni-icons>
+    </view>
+    <next-tree :ifSearch="true" :border="true" labelKey="name" ref="treeSelectRef" :checkStrictly="true"
+        :funcMode="field.multiple ? 'checkbox' : 'radio'" :treeData="options" @confirm="confirmTreeSelect"
+        @cancel="cancelTreeSelect"></next-tree>
+</template>
+
+
+<script lang="ts" setup>
+import nextTree from '@/components/next-tree/next-tree.vue'
+import { computed, useAttrs, PropType } from 'vue'
+
+
+interface DataItem {
+    id: string | number
+    name: string
+    children?: DataItem[]
+}
+
+/**
+ * 根据id查找name值,递归则获取路径拼接name
+ * @param nodes
+ * @param id
+ * @param path
+ */
+function findPathById(
+    nodes: DataItem[],
+    id: string,
+    path: string[] = []
+): string | null {
+    const result = nodes
+        .map(node => {
+            if (node.id === id) {
+                return [...path, node.name].join(' / ')
+            }
+            if (node.children) {
+                return findPathById(node.children, id, [...path, node.name])
+            }
+            return null
+        })
+        .find(p => p !== null)
+
+    return result || null
+}
+
+
+const visibleName = computed(() => {
+    const id = selectValue.value
+    if (props.field.multiple) {
+        const arrName: string[] = []
+        options?.map((item: { id: string | number, name: string }) => {
+            if (id.includes(item.id)) {
+                arrName.push(item.name)
+            }
+            return item
+        })
+        return arrName.join(',')
+    }
+    return findPathById(options, id)
+
+})
+const emits = defineEmits(['update:modelValue'])
+const treeSelectRef = ref()
+
+// const props = defineProps({
+//     modelValue: {
+//         type: [Array]
+//     },
+//     placeholder: {
+//         type: String
+//     },
+//     readyonly: {
+//         type: Boolean,
+//         default: () => false
+//     },
+//     field: {
+//         type: Object,
+//         required: true
+//     },
+// })
+
+const props = defineProps<{
+    modelValue?: any
+    placeholder?: String
+    readyonly?: Boolean,
+    field?: any
+}>()
+
+const selectValue = ref(props.modelValue)
+const options = props.field.enum.map((item: any, index: number) => {
+    return {
+        id: item.value || item,
+        name: props.field.enumNames[index] || item
+    }
+})
+
+function checkedTreeData(treeData: any, selectIds?: any) {
+    treeData.map((item: any) => {
+        if (selectIds.indexOf(item.id) !== -1) {
+            item.checked = true
+        } else {
+            item.checked = false
+        }
+        if (item.children && item.children.length) {
+            checkedTreeData(item.children, selectIds)
+        }
+    })
+}
+
+const showTreeSelectModal = async () => {
+    checkedTreeData(options, props.field.multiple ? selectValue.value : [selectValue.value])
+    unref(treeSelectRef).showTree = true
+}
+
+const cancelTreeSelect = () => {
+
+}
+
+const confirmTreeSelect = (list: { id: string | number, name: string }[]) => {
+    if (props.field.multiple) {
+        selectValue.value = list.map(item => item.id)
+    } else {
+        selectValue.value = list[0].id
+    }
+    emits('update:modelValue', selectValue.value)
+}
+</script>
+
+<style scoped lang="scss">
+.example {
+  padding: 15px;
+  background-color: #fff;
+}
+
+:deep(.is-disabled) {
+  color: #333;
+
+}
+
+.custom-select-class {
+  border-color: #e5e5e5;
+  background-color: transparent;
+  display: flex;
+  box-sizing: border-box;
+  flex-direction: row;
+  align-items: center;
+  border: 1px solid #dcdfe6;
+  border-radius: 4px;
+}
+
+.custom-select-content {
+  width: auto;
+  position: relative;
+  overflow: hidden;
+  flex: 1;
+  line-height: 1;
+  font-size: 14px;
+  line-height: 35px;
+  padding-left: 10px;
+  height: 35px;
+
+}
+
+.tag-container {
+  &__item {
+    padding: 0 6px 0 0;
+  }
+}
+</style>

+ 56 - 0
src/components/dynamic-form/string-component.vue

@@ -0,0 +1,56 @@
+<template>
+    <uni-easyinput @input="change"  v-model="inputValue" v-if="['textarea'].includes(field.format)" @blur="change" 
+        :placeholder="field.description || placeholder || `请输入${field.title}`" type="textarea" :disabled="props.field.readonly"/>
+    <uni-easyinput v-if="!field.format" v-bind="getBindValue" :placeholder="placeholder" type="text" :disabled="props.field.readonly"/>
+</template>
+<script lang="ts" setup>
+
+const emits = defineEmits(['update:modelValue'])
+const props = defineProps({
+    modelValue: {
+        type: [String, Number]
+    },
+    placeholder: {
+        type: String
+    },
+    readyonly: {
+        type: Boolean,
+        default: () => false
+    },
+    field: {
+        type: Object,
+        required: true
+    },
+})
+
+const inputValue = ref(props.modelValue)
+const getBindValue = computed(() => {
+    const attrs = useAttrs()
+    const obj = { ...attrs, ...props }
+    return obj
+})
+
+// const format = computed(() => {
+//     return props.field.format
+// })
+
+const change = (e: any) => {
+    emits('update:modelValue', inputValue.value)
+}
+</script>
+<script lang="ts">
+export default {
+    options: {
+        styleIsolation: 'apply-shared', //解除样式隔离
+    }
+
+}
+</script>
+<style scoped >
+
+:deep(.is-disabled){
+    border-color: #e5e5e5 !important;
+    background-color: #fff !important;
+    color:#333 !important;
+}
+</style>

+ 169 - 0
src/components/dynamic-form/upload-component.vue

@@ -0,0 +1,169 @@
+<template>
+    <uni-file-picker v-if="!showUploadProgress" file-mediatype="all" :auto-upload="false" :limit="field.limitCount"
+        mode="list" @select="(e: any) => handlerSelectFileUpload(e)">
+        <button size="mini" style="color:#ffffff;background-color:#007aff;border-color:#007aff"
+            hover-class="is-hover">上传文件</button>
+    </uni-file-picker>
+    <view style="display: flex;flex-direction: column;">
+        <template v-for="(item,index) in fileList" :key="item.id">
+            <div class="upload-container">
+                <div class="file-container">
+                    <div class="filename">
+                        {{ item.name }}
+                    </div>
+                </div>
+                <div style="margin-left: 10px">
+                    <uni-icons :size="25" color="#dd524d" type="trash" style=" padding: 0 5px;" @click="removeFileItem(index)"></uni-icons>
+                </div>
+            </div>
+        </template>
+    </view>
+    <custom-progress v-if="showUploadProgress" :value="currentProgress" style="min-width: 250px"
+        :stroke-width="16"></custom-progress>
+</template>
+
+<script lang="ts" setup>
+import { computed, useAttrs, h, ref, PropType } from 'vue'
+import { getUploadWorkFileUrl } from '@/api/project-center/work'
+import customProgress from '@/components/cu-progress/index.vue'
+import { uploadFile } from '@/api/base/client';
+const fileList = ref<{id:string, name: string}[]>([])
+const objectNameList = ref<{ id: string; name: string }[]>([])
+const currentProgress = ref(0) // 控制进度条
+const showUploadProgress = ref(false)
+
+const emits = defineEmits(['update:modelValue'])
+
+const listStyles = {
+    // 是否显示边框
+    border: true,
+    // 是否显示分隔线
+    dividline: true,
+    // 线条样式
+    borderStyle: {
+        width: 1,
+        color: 'blue',
+        style: 'dashed',
+        radius: 2
+    }
+}
+const props = defineProps({
+    modelValue: {
+        type: Array as PropType<string[]>
+    },
+    workId: {
+        type: String
+    },
+    field: {
+        type: Object,
+        required: true
+    }
+})
+
+// 获取上传进度信息
+const onProgressResult = (e: UniApp.OnProgressUpdateResult) => {
+    showUploadProgress.value = true
+    currentProgress.value = e.progress
+
+}
+
+// 选择合同上传文件,并上传
+const handlerSelectFileUpload = async (e: {
+    tempFilePaths: string[], tempFiles: {
+        name: string
+        extname: string
+        url: string
+    }[]
+}) => {
+    try {
+        const { tempFiles, tempFilePaths } = e
+        if (tempFilePaths.length > 0) {
+            const {
+                url,
+                form_data,
+                object_name,
+                headers
+            } = await getUploadWorkFileUrl({
+                work_id: props.workId!,
+                filename: tempFiles[0].name
+            })
+         
+            await uploadFile({ url, object_name, headers, formData: form_data, filePath: tempFilePaths[0], uploadProgress: onProgressResult })
+            fileList.value.push({
+                id: object_name,
+                name: tempFiles[0].name
+            })
+            emits('update:modelValue', fileList.value)
+            showUploadProgress.value = false
+            currentProgress.value = 0
+        }
+
+    } catch (_e) {
+        showUploadProgress.value = false
+        currentProgress.value = 0
+
+    }
+
+}
+
+/**
+ * 删除
+ * @param index 
+ */
+const removeFileItem = (index: number)=>{
+    fileList.value.splice(index, 1)
+    emits('update:modelValue', fileList.value)
+}
+
+</script>
+<script lang="ts">
+export default {
+    options: {
+        styleIsolation: 'apply-shared', //解除样式隔离
+    }
+
+}
+</script>
+<style scoped>
+.upload-container {
+    position: relative;
+    display: flex;
+    align-items: center;
+    box-sizing: border-box;
+    margin-top: 10rpx;
+
+}
+
+.file-container {
+    display: flex;
+    align-items: center;
+    overflow: hidden;
+    font-size: 14px;
+    border-radius: 3px;
+    transition: background-color 0.1s cubic-bezier(0, 0, 1, 1);
+}
+
+.filename {
+    padding: 8px 0px 8px 10px;
+    flex: 1;
+    width: 300px;
+    background-color: #f7f8fa;
+    align-items: center;
+    font-size: 13px;
+    overflow: hidden;
+    color: #606266;
+    font-size: 14px;
+    line-height: 1.4286;
+    white-space: nowrap;
+    text-overflow: ellipsis;
+}
+
+.file-extra {
+    margin-left: 20px;
+    color: #606266;
+}
+
+:deep(.is-text-box) {
+    display: none;
+}
+</style>

+ 357 - 0
src/components/editor-list-item/editor-list-item.vue

@@ -0,0 +1,357 @@
+<template>
+    <view v-if="!isModify" class="edited-text" @click="showEditor">
+        <view v-if="props.componentsType === ComponentsType.TEXTAREA" style="display: inline-flex;">
+            <view v-html="visibleName"></view><uni-icons :size="16" color="#bbb" type="right"
+                style=" margin-right: -5px;color:#bbb;align-items: center;display: flex;"></uni-icons>
+        </view>
+        <view v-else style="display: inline-flex;">
+            <text>{{ visibleName }} {{ props.inputSuffix }}</text>
+            <uni-icons :size="16" color="#bbb" type="right"
+                style=" margin-right: -5px;color:#bbb;align-items: center;display: flex;"></uni-icons></view>
+    </view>
+    <view v-else>
+        <view v-if="[ComponentsType.STRINGINPUT, ComponentsType.TEXTAREA].includes(componentsType)">
+            <uni-easyinput v-model="editingValue" :placeholder="placeholder" :type="componentsType"
+                suffixIcon="checkbox" :focus="true" :autoHeight="true" @iconClick="confirmData" @blur="confirmData" />
+        </view>
+        <view v-if="[ComponentsType.NUMBERINPUT, ComponentsType.DIGIT].includes(componentsType)">
+            <view class="uni-easyinput">
+                <view class="uni-easyinput__content is-input-border" :style="{
+                    borderColor: is_foucs ? '#2979ff' : '',
+                    background: is_foucs ? '#fff' : ''
+                }">
+                    <input class="uni-easyinput__content-input" v-model="editingValue"
+                        placeholder-class="uni-easyinput__placeholder-class" :focus="is_foucs" :type="componentsType"
+                        :placeholder="placeholder" @focus="is_foucs = true" @blur="confirmData">
+                    <uni-icons class="content-clear-icon" type="checkbox" color="#c0c4cc" @click="confirmData"
+                        size="22"></uni-icons>
+                </view>
+            </view>
+
+        </view>
+        <view v-if="[ComponentsType.DATEPICKER].includes(componentsType)">
+            <uni-datetime-picker type="date" v-model="editingValue" :focus="true" @maskClick="confirmData"
+                @change="confirmData" ref="datepicker" :clear-icon="!notAllowNull" />
+        </view>
+        <!-- <view v-if="[ComponentsType.CASCADER, ComponentsType.SELECT].includes(componentsType)"> -->
+
+        <!-- <next-tree :ifSearch="true" :showAuxiliaryLine="showAuxiliaryLine" :border="true" labelKey="name"
+                ref="treeSelectRef" :checkStrictly="true" :selectParent="selectParent"
+                :funcMode="multiple ? 'checkbox' : 'radio'" :treeData="options" @confirm="confirmTreeSelect"
+                @cancel="cancelTreeSelect"></next-tree> -->
+        <!-- </view> -->
+
+    </view>
+    <view v-if="options">
+        <next-tree :ifSearch="true" :showAuxiliaryLine="showAuxiliaryLine" :border="true" labelKey="name"
+            :allowClear='!notAllowNull' ref="treeSelectRef" :checkStrictly="true" :selectParent="selectParent"
+            :funcMode="multiple ? 'checkbox' : 'radio'" :treeData="options" @confirm="confirmTreeSelect"
+            @cancel="cancelTreeSelect"></next-tree>
+    </view>
+</template>
+<script setup lang="ts">
+import { ComponentsType } from '@/enums/api';
+
+import nextTree from '@/components/next-tree/next-tree.vue'
+// import { invoke, until, useCounter } from '@vueuse/core'
+
+interface DataItem {
+    id: string | number
+    name: string
+    children?: DataItem[]
+}
+
+const props = defineProps<{
+    modelValue?: any
+    oldValue?: any
+    disabled?: boolean
+    placeholder?: string
+    componentsType: ComponentsType
+    isInputNumber?: boolean
+    inputSuffix?: string
+    options?: DataItem[]
+    multiple?: boolean
+    showNumberButton?: boolean
+    unCheckStrictly?: boolean
+    showAuxiliaryLine?: boolean
+    dateType?: string
+    notAllowNull?: boolean
+    selectParent?: boolean
+}>()
+
+const test = ref(0)
+const datepicker = ref()
+const treeSelectRef = ref()
+const emits = defineEmits(['update:modelValue', 'modify'])
+const isModify = ref(false)
+const is_foucs = ref(true)
+
+
+
+const editingValue = ref(
+    props.componentsType === ComponentsType.TEXTAREA && props.modelValue
+        ? props.modelValue.replace(/<br\/>/g, '\n')
+        : props.modelValue
+)
+
+
+function checkedTreeData(treeData: any, selectIds?: any) {
+    treeData.map((item: any) => {
+        if (selectIds.indexOf(item.id) !== -1) {
+            item.checked = true
+        } else {
+            item.checked = false
+        }
+        if (item.children && item.children.length) {
+            checkedTreeData(item.children, selectIds)
+        }
+    })
+}
+
+
+const showEditor = async () => {
+    isModify.value = true
+    if ([ComponentsType.CASCADER, ComponentsType.SELECT].includes(props.componentsType)) {
+        nextTick(() => {
+            checkedTreeData(props.options, props.multiple ? props.modelValue : [props.modelValue])
+            unref(treeSelectRef).showTree = true
+        })
+    }
+}
+/**
+ * 根据id查找name值,递归则获取路径拼接name
+ * @param nodes
+ * @param id
+ * @param path
+ */
+function findPathById(
+    nodes: DataItem[],
+    id: string,
+    path: string[] = []
+): string | null {
+    const result = nodes
+        .map(node => {
+            if (node.id === id) {
+                return [...path, node.name].join(' / ')
+            }
+            if (node.children) {
+                return findPathById(node.children, id, [...path, node.name])
+            }
+            return null
+        })
+        .find(p => p !== null)
+
+    return result || null
+}
+
+const visibleName = computed(() => {
+    if (
+        props.componentsType === ComponentsType.STRINGINPUT ||
+        props.componentsType === ComponentsType.NUMBERINPUT
+    ) {
+        return props.modelValue
+    }
+
+    if (
+        props.componentsType === ComponentsType.SELECT ||
+        props.componentsType === ComponentsType.CASCADER
+    ) {
+        const ids = editingValue.value
+        if (props.multiple) {
+            const arrName: string[] = []
+            ids.map((item: string | number) => {
+                const isFind = props.options?.find(p => p.id === item)
+                if (isFind) {
+                    arrName.push(isFind.name)
+                }
+                return item
+            })
+            return arrName.join(',')
+        }
+        return findPathById(props.options || [], ids)
+    }
+    return props.modelValue
+})
+
+const confirmTreeSelect = (list: any) => {
+    if (list.length > 0) {
+        if (props.multiple) {
+            let ids: any = []
+            list.map((item: { id: string | number, name: string }) => {
+                ids.push(item.id)
+                return item
+
+            })
+            editingValue.value = ids
+        } else {
+            editingValue.value = list[0].id
+        }
+        const v = editingValue.value
+        emits('update:modelValue', v)
+        emits('modify', { oldValue: props.oldValue })
+    } else {
+        if (props.notAllowNull) {
+            uni.showToast(({
+                icon: 'error',
+                title: '不允许为空'
+            }))
+            return
+        }
+        const v = editingValue.value
+        emits('update:modelValue', null)
+        emits('modify', { oldValue: props.oldValue })
+    }
+
+    isModify.value = false
+}
+
+watch(
+    () => props.modelValue,
+    () => {
+        const v =
+            props.componentsType === ComponentsType.TEXTAREA
+                ? props.modelValue.replace(/<br\/>/g, '\n')
+                : props.modelValue
+        editingValue.value = v
+    }
+)
+
+watch(
+    () => datepicker.value,
+    () => {
+        datepicker.value?.show()
+    }
+)
+
+/**
+ * 确认修改
+ */
+const confirmData = () => {
+    is_foucs.value = false
+    const v = editingValue.value
+    if (props.notAllowNull && v === '') {
+        uni.showToast(({
+            icon: 'error',
+            title: '不允许为空'
+        }))
+        return;
+    }
+    if (props.modelValue === v) {
+        isModify.value = false
+        return
+    }
+    const f_value =
+        (props.componentsType === ComponentsType.TEXTAREA
+            ? v.replace(/\n/g, '<br/>')
+            : v)
+    emits('update:modelValue', f_value)
+    emits('modify', { oldValue: props.oldValue })
+    isModify.value = false
+}
+
+
+const cancelTreeSelect = () => {
+    // const v = editingValue.value
+    // emits('update:modelValue', v)
+    // emits('modify', { oldValue: props.oldValue })
+    checkedTreeData(unref(props.options), [])
+    isModify.value = false
+}
+</script>
+<script lang="ts">
+export default {
+  options: {
+    styleIsolation: 'isolated', //解除样式隔离
+  }
+
+}
+</script>
+<style scoped lang="scss">
+.edited-text {
+    color: #5d5959;
+    font-size: 13px;
+    width: 100%;
+    height: 100%;
+    min-width: 250px;
+    text-overflow: ellipsis;
+    text-align: right;
+    text-wrap: wrap;
+    overflow-wrap: anywhere;
+    white-space-collapse: collapse;
+}
+
+.uni-easyinput {
+    /* #ifndef APP-NVUE */
+    width: 100%;
+    /* #endif */
+    flex: 1;
+    position: relative;
+    text-align: left;
+    color: #333;
+    font-size: 14px;
+}
+
+.uni-easyinput__content {
+    flex: 1;
+    /* #ifndef APP-NVUE */
+    width: 100%;
+    display: flex;
+    box-sizing: border-box;
+    // min-height: 36px;
+    /* #endif */
+    flex-direction: row;
+    align-items: center;
+    // 处理border动画刚开始显示黑色的问题
+    border-color: #fff;
+    transition-property: border-color;
+    transition-duration: 0.3s;
+}
+
+.is-input-border {
+    display: flex;
+    box-sizing: border-box;
+    flex-direction: row;
+    align-items: center;
+    border: 1px solid #dcdfe6;
+    border-radius: 4px;
+}
+
+.uni-easyinput__content-input {
+    /* #ifndef APP-NVUE */
+    width: auto;
+    /* #endif */
+    position: relative;
+    overflow: hidden;
+    flex: 1;
+    line-height: 1;
+    font-size: 14px;
+    padding-left: 10px;
+    height: 35px;
+    // min-height: 36px;
+
+    /*ifdef H5*/
+    & ::-ms-reveal {
+        display: none;
+    }
+
+    & ::-ms-clear {
+        display: none;
+    }
+
+    & ::-o-clear {
+        display: none;
+    }
+
+    /*endif*/
+}
+
+.content-clear-icon {
+    padding: 0 5px;
+}
+
+:deep(.uni-easyinput__placeholder-class) {
+    color: #999;
+    font-size: 12px;
+    // font-weight: 200;
+}
+</style>

+ 42 - 0
src/components/index.ts

@@ -0,0 +1,42 @@
+import { App } from 'vue'
+import CustomButton from './custom-button/custom-button.vue'
+// Manually introduce ECharts modules to reduce packing size
+// import { use } from 'echarts/core'
+// import { CanvasRenderer } from 'echarts/renderers'
+// import { BarChart, LineChart, PieChart, RadarChart } from 'echarts/charts'
+// import {
+//   GridComponent,
+//   TooltipComponent,
+//   LegendComponent,
+//   DataZoomComponent,
+//   GraphicComponent
+// } from 'echarts/components'
+// import Chart from './chart/index.vue'
+
+// use([
+//   CanvasRenderer,
+//   BarChart,
+//   LineChart,
+//   PieChart,
+//   RadarChart,
+//   GridComponent,
+//   TooltipComponent,
+//   LegendComponent,
+//   DataZoomComponent,
+//   GraphicComponent
+// ])
+
+export default {
+  install(Vue: App) {
+    // Vue.component('Chart', Chart)
+    Vue.component('CustomButton', CustomButton)
+  }
+}
+
+// import type { App } from 'vue';
+// import { Button } from './Button';
+// import { Input, Layout } from 'ant-design-vue';
+
+// export function registerGlobComp(app: App) {
+//   app.use(CustomButton).use(Button).use(Layout);
+// }

+ 390 - 0
src/components/l-echart/canvas.js

@@ -0,0 +1,390 @@
+const cacheChart = {}
+const fontSizeReg = /([\d\.]+)px/;
+class EventEmit {
+	constructor() {
+		this.__events = {};
+	}
+	on(type, listener) {
+		if (!type || !listener) {
+			return;
+		}
+		const events = this.__events[type] || [];
+		events.push(listener);
+		this.__events[type] = events;
+	}
+	emit(type, e) {
+		if (type.constructor === Object) {
+			e = type;
+			type = e && e.type;
+		}
+		if (!type) {
+			return;
+		}
+		const events = this.__events[type];
+		if (!events || !events.length) {
+			return;
+		}
+		events.forEach((listener) => {
+			listener.call(this, e);
+		});
+	}
+	off(type, listener) {
+		const __events = this.__events;
+		const events = __events[type];
+		if (!events || !events.length) {
+			return;
+		}
+		if (!listener) {
+			delete __events[type];
+			return;
+		}
+		for (let i = 0, len = events.length; i < len; i++) {
+			if (events[i] === listener) {
+				events.splice(i, 1);
+				i--;
+			}
+		}
+	}
+}
+class Image {
+	constructor() {
+		this.currentSrc = null
+		this.naturalHeight = 0
+		this.naturalWidth = 0
+		this.width = 0
+		this.height = 0
+		this.tagName = 'IMG'
+	}
+	set src(src) {
+		this.currentSrc = src
+		uni.getImageInfo({
+			src,
+			success: (res) => {
+				this.naturalWidth = this.width = res.width
+				this.naturalHeight = this.height = res.height
+				this.onload()
+			},
+			fail: () => {
+				this.onerror()
+			}
+		})
+	}
+	get src() {
+		return this.currentSrc
+	}
+}
+class OffscreenCanvas {
+	constructor(ctx, com, canvasId) {
+		this.tagName = 'canvas'
+		this.com = com
+		this.canvasId = canvasId
+		this.ctx = ctx
+	}
+	set width(w) {
+		this.com.offscreenWidth = w
+	}
+	set height(h) {
+		this.com.offscreenHeight = h
+	}
+	get width() {
+		return this.com.offscreenWidth || 0
+	}
+	get height() {
+		return this.com.offscreenHeight || 0
+	}
+	getContext(type) {
+		return this.ctx
+	}
+	getImageData() {
+		return new Promise((resolve, reject) => {
+			this.com.$nextTick(() => {
+				uni.canvasGetImageData({
+					x:0,
+					y:0,
+					width: this.com.offscreenWidth,
+					height: this.com.offscreenHeight,
+					canvasId: this.canvasId,
+					success: (res) => {
+						resolve(res)
+					},
+					fail: (err) => {
+						reject(err)
+					},
+				}, this.com)
+			})
+		})
+	}
+}
+export class Canvas {
+	constructor(ctx, com, isNew, canvasNode={}) {
+		cacheChart[com.canvasId] = {ctx}
+		this.canvasId = com.canvasId;
+		this.chart = null;
+		this.isNew = isNew
+		this.tagName = 'canvas'
+		this.canvasNode = canvasNode;
+		this.com = com;
+		if (!isNew) {
+			this._initStyle(ctx)
+		}
+		this._initEvent();
+		this._ee = new EventEmit()
+	}
+	getContext(type) {
+		if (type === '2d') {
+			return this.ctx;
+		}
+	}
+	setAttribute(key, value) {
+		if(key === 'aria-label') {
+			this.com['ariaLabel'] = value
+		}
+	}
+	setChart(chart) {
+		this.chart = chart;
+	}
+	createOffscreenCanvas(param){
+		if(!this.children) {
+			this.com.isOffscreenCanvas = true
+			this.com.offscreenWidth = param.width||300
+			this.com.offscreenHeight = param.height||300
+			const com = this.com
+			const canvasId = this.com.offscreenCanvasId
+			const context = uni.createCanvasContext(canvasId, this.com)
+			this._initStyle(context)
+			this.children = new OffscreenCanvas(context, com, canvasId)
+		} 
+		return this.children
+	}
+	appendChild(child) {
+		console.log('child', child)
+	}
+	dispatchEvent(type, e) {
+		if(typeof type == 'object') {
+			this._ee.emit(type.type, type);
+		} else {
+			this._ee.emit(type, e);
+		}
+		return true
+	}
+	attachEvent() {
+	}
+	detachEvent() {
+	}
+	addEventListener(type, listener) {
+		this._ee.on(type, listener)
+	}
+	removeEventListener(type, listener) {
+		this._ee.off(type, listener)
+	}
+	_initCanvas(zrender, ctx) {
+		// zrender.util.getContext = function() {
+		// 	return ctx;
+		// };
+		// zrender.util.$override('measureText', function(text, font) {
+		// 	ctx.font = font || '12px sans-serif';
+		// 	return ctx.measureText(text, font);
+		// });
+	}
+	_initStyle(ctx, child) {
+		const styles = [
+			'fillStyle',
+			'strokeStyle',
+			'fontSize',
+			'globalAlpha',
+			'opacity',
+			'textAlign',
+			'textBaseline',
+			'shadow',
+			'lineWidth',
+			'lineCap',
+			'lineJoin',
+			'lineDash',
+			'miterLimit',
+			// #ifdef H5
+			'font',
+			// #endif
+		];
+		const colorReg = /#([0-9a-fA-F])([0-9a-fA-F])([0-9a-fA-F])\b/g;
+		styles.forEach(style => {
+			Object.defineProperty(ctx, style, {
+				set: value => {
+					// #ifdef H5
+					if (style === 'font' && fontSizeReg.test(value)) {
+						const match = fontSizeReg.exec(value);
+						ctx.setFontSize(match[1]);
+						return;
+					}
+					// #endif
+					
+					if (style === 'opacity') {
+						ctx.setGlobalAlpha(value)
+						return;
+					}
+					if (style !== 'fillStyle' && style !== 'strokeStyle' || value !== 'none' && value !== null) {
+						// #ifdef H5 || APP-PLUS || MP-BAIDU
+						if(typeof value == 'object') {
+							if (value.hasOwnProperty('colorStop') || value.hasOwnProperty('colors')) {
+								ctx['set' + style.charAt(0).toUpperCase() + style.slice(1)](value);
+							}
+							return
+						} 
+						// #endif
+						// #ifdef MP-TOUTIAO
+						if(colorReg.test(value)) {
+							value = value.replace(colorReg, '#$1$1$2$2$3$3')
+						}
+						// #endif
+						ctx['set' + style.charAt(0).toUpperCase() + style.slice(1)](value);
+					}
+				}
+			});
+		});
+		if(!this.isNew && !child) {
+			ctx.uniDrawImage = ctx.drawImage
+			ctx.drawImage = (...a) => {
+				a[0] = a[0].src
+				ctx.uniDrawImage(...a)
+			}
+		}
+		if(!ctx.createRadialGradient) {
+			ctx.createRadialGradient = function() {
+				return ctx.createCircularGradient(...[...arguments].slice(-3))
+			};
+		}
+		// 字节不支持
+		if (!ctx.strokeText) {
+			ctx.strokeText = (...a) => {
+				ctx.fillText(...a)
+			}
+		}
+		// 钉钉不支持 
+		if (!ctx.measureText) {
+			const strLen = (str) => {
+				let len = 0;
+				for (let i = 0; i < str.length; i++) {
+					if (str.charCodeAt(i) > 0 && str.charCodeAt(i) < 128) {
+						len++;
+					} else {
+						len += 2;
+					}
+				}
+				return len;
+			}
+			ctx.measureText = (text, font) => {
+				let fontSize = ctx?.state?.fontSize || 12;
+				if (font) {
+					fontSize = parseInt(font.match(/([\d\.]+)px/)[1])
+				}
+				fontSize /= 2;
+				let isBold = fontSize >= 16;
+				const widthFactor = isBold ? 1.3 : 1;
+				return {
+					width: strLen(text) * fontSize * widthFactor
+				};
+			}
+		}
+	}
+
+	_initEvent(e) {
+		this.event = {};
+		const eventNames = [{
+			wxName: 'touchStart',
+			ecName: 'mousedown'
+		}, {
+			wxName: 'touchMove',
+			ecName: 'mousemove'
+		}, {
+			wxName: 'touchEnd',
+			ecName: 'mouseup'
+		}, {
+			wxName: 'touchEnd',
+			ecName: 'click'
+		}];
+
+		eventNames.forEach(name => {
+			this.event[name.wxName] = e => {
+				const touch = e.touches[0];
+				this.chart.getZr().handler.dispatch(name.ecName, {
+					zrX: name.wxName === 'tap' ? touch.clientX : touch.x,
+					zrY: name.wxName === 'tap' ? touch.clientY : touch.y
+				});
+			};
+		});
+	}
+
+	set width(w) {
+		this.canvasNode.width = w
+	}
+	set height(h) {
+		this.canvasNode.height = h
+	}
+
+	get width() {
+		return this.canvasNode.width || 0
+	}
+	get height() {
+		return this.canvasNode.height || 0
+	}
+	get ctx() {
+		return cacheChart[this.canvasId]['ctx'] || null
+	}
+	set chart(chart) {
+		cacheChart[this.canvasId]['chart'] = chart
+	}
+	get chart() {
+		return cacheChart[this.canvasId]['chart'] || null
+	}
+}
+
+export function dispatch(name, {x,y, wheelDelta}) {
+	this.dispatch(name, {
+		zrX: x,
+		zrY: y,
+		zrDelta: wheelDelta,
+		preventDefault: () => {},
+		stopPropagation: () =>{}
+	});
+}
+export function setCanvasCreator(echarts, {canvas, node}) {
+	// echarts.setCanvasCreator(() => canvas);
+	if(echarts && !echarts.registerPreprocessor) {
+		return console.warn('echarts 版本不对或未传入echarts,vue3请使用esm格式')
+	}
+	echarts.registerPreprocessor(option => {
+		if (option && option.series) {
+			if (option.series.length > 0) {
+				option.series.forEach(series => {
+					series.progressive = 0;
+				});
+			} else if (typeof option.series === 'object') {
+				option.series.progressive = 0;
+			}
+		}
+	});
+	function loadImage(src, onload, onerror) {
+		let img = null
+		if(node && node.createImage) {
+			img = node.createImage()
+			img.onload = onload.bind(img);
+			img.onerror = onerror.bind(img);
+			img.src = src;
+			return img
+		} else {
+			img = new Image()
+			img.onload = onload.bind(img)
+			img.onerror = onerror.bind(img);
+			img.src = src
+			return img
+		}
+	}
+	if(echarts.setPlatformAPI) {
+		echarts.setPlatformAPI({
+			loadImage: canvas.setChart ? loadImage : null,
+			createCanvas(){
+				const key = 'createOffscreenCanvas'
+				return uni.canIUse(key) && uni[key] ? uni[key]({type: '2d'}) : canvas
+			}
+		})
+	}
+}

+ 252 - 0
src/components/l-echart/l-echart.uvue

@@ -0,0 +1,252 @@
+<template>
+	<!-- #ifdef APP -->
+	<web-view class="lime-echart" ref="chartRef" @load="loaded" :style="[customStyle]" 
+		:webview-styles="[webviewStyles]" src="/uni_modules/lime-echart/static/uvue.html?v=1011">
+	</web-view>
+	<!-- #endif -->
+	<!-- #ifdef H5 -->
+	<div class="lime-echart" ref="chartRef"></div>
+	<!-- #endif -->
+</template>
+
+<script lang="uts" setup>
+	// @ts-nocheck
+	import { Echarts } from './uvue';
+	type EchartsResolve = (value : Echarts) => void
+	defineOptions({
+		name: 'l-echart'
+	})
+	const emits = defineEmits(['finished'])
+	const props = defineProps({
+		// #ifdef APP
+		webviewStyles: {
+			type: Object
+		},
+		customStyle: {
+			type: Object
+		},
+		// #endif
+		// #ifndef APP
+		webviewStyles: {
+			type: Object
+		},
+		customStyle: {
+			type: [String, Object]
+		},
+		// #endif
+		isDisableScroll: {
+			type: Boolean,
+			default: false
+		},
+		isClickable: {
+			type: Boolean,
+			default: true
+		},
+		enableHover: {
+			type: Boolean,
+			default: false
+		},
+		beforeDelay: {
+			type: Number,
+			default: 30
+		}
+	})
+
+	const finished = ref(false)
+	const map = [] as EchartsResolve[]
+	const callbackMap = [] as EchartsResolve[]
+	// let context = null as UniWebViewElement | null
+	let chart = null as Echarts | null
+	let chartRef = ref<UniWebViewElement | null>(null)
+	
+	const trigger = () => {
+		// #ifdef APP
+		if (finished.value) {
+			if (chart == null) {
+				chart = new Echarts(chartRef.value!)
+			}
+			while (map.length > 0) {
+				const resolve = map.pop() as EchartsResolve
+				resolve(chart!)
+			}
+		}
+		// #endif
+		// #ifdef H5
+		while (map.length > 0) {
+			if(chart != null){
+				const resolve = map.pop() as EchartsResolve
+				resolve(chart!)
+			}
+		}
+		// #endif
+		
+		if(chart != null){
+			while(callbackMap.length > 0){
+				const callback = callbackMap.pop() as EchartsResolve
+				callback(chart!)
+			}
+		}
+	}
+	
+	// #ifdef APP
+	const loaded = (event : UniWebViewLoadEvent) => {
+		event.stopPropagation()
+		event.preventDefault()
+		finished.value = true
+		trigger()
+		emits('finished')
+	}
+	// #endif
+	
+	
+	const _next = () : boolean => {
+		if (chart == null) {
+			console.warn(`组件还未初始化,请先使用 init`)
+			return true
+		}
+		return false
+	}
+	const setOption = (option : UTSJSONObject) => {
+		if (_next()) return
+		chart!.setOption(option);
+	}
+	const showLoading = () => {
+		if (_next()) return
+		chart!.showLoading();
+	}
+	const hideLoading = () => {
+		if (_next()) return
+		chart!.hideLoading();
+	}
+	const clear = () => {
+		if (_next()) return
+		chart!.clear();
+	}
+	const dispose = () => {
+		if (_next()) return
+		chart!.dispose();
+	}
+	const resize = (size : UTSJSONObject) => {
+		if (_next()) return
+		chart!.resize(size);
+	}
+	const canvasToTempFilePath = (opt : UTSJSONObject) => {
+		if (_next()) return
+		chart!.canvasToTempFilePath(opt);
+	}
+	// function init() : Promise<Echarts> {
+	// 	return new Promise((resolve) => {
+	// 		map.push(resolve)
+	// 		trigger()
+	// 	})
+	// }
+	// #ifdef APP
+	function init(callback : ((chart : Echarts) => void) | null) : Promise<Echarts> {
+		// if (chart !== null && callback != null) {
+		// 	callback(chart!)
+		// } else {
+		// 	console.warn('echarts 未加载完成,您可以延时一下')
+		// }
+		if(callback!=null){
+			callbackMap.push(callback)
+		}
+		return new Promise<Echarts>((resolve) => {
+			map.push(resolve)
+			trigger()
+		})
+	}
+	// #endif
+	// #ifdef H5
+	const touchstart = (e) => {
+		if(chart == null) return
+		const handler = chart.getZr().handler;
+		const rect = chart.getZr().dom.getBoundingClientRect()
+		handler.dispatch('mousedown', {
+			zrX: e.touches[0].clientX - rect.left,
+			zrY: e.touches[0].clientY - rect.top
+		})
+		handler.dispatch('click', {
+			zrX: e.touches[0].clientX - rect.left,
+			zrY: e.touches[0].clientY - rect.top
+		})
+	}
+	const touchmove = (e) => {
+		if(chart == null) return
+		const handler = chart.getZr().handler;
+		const rect = chart.getZr().dom.getBoundingClientRect()
+		handler.dispatch('mousemove', {
+			zrX: e.touches[0].clientX - rect.left,
+			zrY: e.touches[0].clientY - rect.top
+		})
+	}
+	const mouseup = (e) => {
+		if(chart == null) return
+		const handler = chart.getZr().handler;
+		handler.dispatch('mousemove', {
+			zrX: 999999999,
+			zrY: 999999999
+		})
+		handler.dispatch('mouseup', {
+			zrX: 999999999,
+			zrY: 999999999
+		})
+	}
+	function init(echarts: any, ...args: any[]): Promise<Echarts>{
+		if(echarts == null){
+			console.error('请确保已经引入了 ECharts 库');
+			return Promise.reject('请确保已经引入了 ECharts 库');
+		}
+		let theme:string|null=null
+		let opts={}
+		let callback:Function|null=null;
+		
+		args.forEach(item =>{
+			if(typeof item === 'function') {
+				callback = item
+			} else if(['string'].includes(typeof item)){
+				theme = item
+			} else if(typeof item === 'object'){
+				opts = item
+			}
+		})
+		chart = echarts.init(chartRef.value, theme, opts)
+		window.addEventListener('touchstart', touchstart)
+		window.addEventListener('touchmove', touchmove)
+		window.addEventListener('touchend', mouseup)
+		
+		if(callback!=null && typeof callback == 'function'){
+			callbackMap.push(callback)
+		}
+		return new Promise<Echarts>((resolve) => {
+			map.push(resolve)
+			trigger()
+		})
+	}
+	onMounted(()=>{
+		finished.value = true
+		trigger()
+		emits('finished')
+	})
+	onUnmounted(()=>{
+		window.removeEventListener('touchstart', touchstart)
+		window.removeEventListener('touchmove', touchmove)
+		window.removeEventListener('touchend', mouseup)
+	})
+	// #endif
+	
+	defineExpose({
+		init,
+		setOption,
+		showLoading,
+		hideLoading,
+		clear,
+		dispose,
+		resize,
+		canvasToTempFilePath
+	})
+</script>
+<style lang="scss">
+	.lime-echart {
+		flex: 1;
+	}
+</style>

+ 508 - 0
src/components/l-echart/l-echart.vue

@@ -0,0 +1,508 @@
+<template>
+	<view class="lime-echart" :style="customStyle" v-if="canvasId" ref="limeEchart" :aria-label="ariaLabel">
+		<!-- #ifndef APP-NVUE -->
+		<canvas
+			class="lime-echart__canvas"
+			v-if="use2dCanvas"
+			type="2d"
+			:id="canvasId"
+			:style="canvasStyle"
+			:disable-scroll="isDisableScroll"
+	
+		/>
+		<canvas
+			class="lime-echart__canvas"
+			v-else
+			:width="nodeWidth"
+			:height="nodeHeight"
+			:style="canvasStyle"
+			:canvas-id="canvasId"
+			:id="canvasId"
+			:disable-scroll="isDisableScroll"
+			@touchstart="touchStart"
+			@touchmove="touchMove"
+			@touchend="touchEnd"
+		/>
+		<view class="lime-echart__mask"
+			v-if="isPC"
+			@mousedown="touchStart"
+			@mousemove="touchMove"
+			@mouseup="touchEnd"
+			@touchstart="touchStart"
+			@touchmove="touchMove"
+			@touchend="touchEnd">
+		</view>
+		<canvas v-if="isOffscreenCanvas" :style="offscreenStyle" :canvas-id="offscreenCanvasId"></canvas>
+		<!-- #endif -->
+		<!-- #ifdef APP-NVUE -->
+		<web-view
+			class="lime-echart__canvas"
+			:id="canvasId"
+			:style="canvasStyle"
+			:webview-styles="webviewStyles"
+			ref="webview"
+			src="/uni_modules/lime-echart/static/uvue.html?v=1"
+			@pagefinish="finished = true"
+			@onPostMessage="onMessage"
+		></web-view>
+		<!-- #endif -->
+	</view>
+</template>
+
+<script>
+// #ifdef VUE3
+// #ifdef APP-PLUS
+global = {}
+// #endif
+// #endif
+// #ifndef APP-NVUE
+import {Canvas, setCanvasCreator, dispatch} from './canvas';
+import {wrapTouch, convertTouchesToArray, devicePixelRatio ,sleep, canIUseCanvas2d, getRect} from './utils';
+// #endif
+// #ifdef APP-NVUE
+import { base64ToPath, sleep } from './utils';
+import {Echarts} from './nvue'
+// #endif
+const charts = {}
+const echartsObj = {}
+
+
+/**
+	 * LimeChart 图表
+	 * @description 全端兼容的eCharts
+	 * @tutorial https://ext.dcloud.net.cn/plugin?id=4899
+
+	 * @property {String} customStyle 自定义样式
+	 * @property {String} type 指定 canvas 类型
+	 * @value 2d 使用canvas 2d,部分小程序支持
+	 * @value '' 使用原生canvas,会有层级问题
+	 * @value bottom right	不缩放图片,只显示图片的右下边区域
+	 * @property {Boolean} isDisableScroll	 
+	 * @property {number} beforeDelay = [30]  延迟初始化 (毫秒)
+	 * @property {Boolean} enableHover PC端使用鼠标悬浮
+	
+	 * @event {Function} finished 加载完成触发
+	 */
+export default {
+	name: 'lime-echart',
+	props: {
+		// #ifdef MP-WEIXIN || MP-TOUTIAO
+		type: {
+			type: String,
+			default: '2d'
+		},
+		// #endif
+		// #ifdef APP-NVUE
+		webviewStyles: Object,
+		// hybrid: Boolean,
+		// #endif
+		customStyle: String,
+		isDisableScroll: Boolean,
+		isClickable: {
+			type: Boolean,
+			default: true
+		},
+		enableHover: Boolean,
+		beforeDelay: {
+			type: Number,
+			default: 30
+		}
+	},
+	data() {
+		return {
+			// #ifdef MP-WEIXIN || MP-TOUTIAO || MP-ALIPAY
+			use2dCanvas: true,
+			// #endif
+			// #ifndef MP-WEIXIN || MP-TOUTIAO || MP-ALIPAY
+			use2dCanvas: false,
+			// #endif
+			ariaLabel: '图表',
+			width: null,
+			height: null,
+			nodeWidth: null,
+			nodeHeight: null,
+			// canvasNode: null,
+			config: {},
+			inited: false,
+			finished: false,
+			file: '',
+			platform: '',
+			isPC: false,
+			isDown: false,
+			isOffscreenCanvas: false,
+			offscreenWidth: 0,
+			offscreenHeight: 0
+		};
+	},
+	computed: {
+		canvasId() {
+			return `lime-echart${this._ && this._.uid || this._uid}`
+		},
+		offscreenCanvasId() {
+			return `${this.canvasId}_offscreen`
+		},
+		offscreenStyle() {
+			return `width:${this.offscreenWidth}px;height: ${this.offscreenHeight}px; position: fixed; left: 99999px; background: red`
+		},
+		canvasStyle() {
+			return  this.width && this.height ? ('width:' + this.width + 'px;height:' + this.height + 'px') : ''
+		}
+	},
+	// #ifndef VUE3
+	beforeDestroy() {
+		this.clear()
+		this.dispose()
+		// #ifdef H5
+		if(this.isPC) {
+			document.removeEventListener('mousewheel', this.mousewheel)
+		}
+		// #endif
+	},
+	// #endif
+	// #ifdef VUE3
+	beforeUnmount() {
+		this.clear()
+		this.dispose()
+		// #ifdef H5
+		if(this.isPC) {
+			document.removeEventListener('mousewheel', this.mousewheel)
+		}
+		// #endif
+	},
+	// #endif
+	created() {
+		// #ifdef H5
+		if(!('ontouchstart' in window)) {
+			this.isPC = true
+			document.addEventListener('mousewheel', this.mousewheel)
+		}
+		// #endif
+		// #ifdef MP-WEIXIN || MP-TOUTIAO || MP-ALIPAY
+		const { platform } = uni.getSystemInfoSync();
+		this.isPC = /windows/i.test(platform)
+		// #endif
+		this.use2dCanvas = this.type === '2d' && canIUseCanvas2d()
+	},
+	mounted() {
+		this.$nextTick(() => {
+			this.$emit('finished')
+		})
+	},
+	methods: {
+		// #ifdef APP-NVUE
+		onMessage(e) {
+			const detail = e?.detail?.data[0] || null;
+			const data = detail?.data
+			const key = detail?.event
+			const options = data?.options
+			const event = data?.event
+			const file = detail?.file
+			if (key == 'log' && data) {
+			}
+			if(event) {
+				this.chart.dispatchAction(event.replace(/"/g,''), options)
+			}
+			if(file) {
+				thie.file = file
+			}
+		},
+		// #endif
+		setChart(callback) {
+			if(!this.chart) {
+				console.warn(`组件还未初始化,请先使用 init`)
+				return
+			}
+			if(typeof callback === 'function' && this.chart) {
+				callback(this.chart);
+			}
+			// #ifdef APP-NVUE
+			if(typeof callback === 'function') {
+				this.$refs.webview.evalJs(`setChart(${JSON.stringify(callback.toString())}, ${JSON.stringify(this.chart.options)})`);
+			}
+			// #endif
+		},
+		setOption() {
+			if (!this.chart || !this.chart.setOption) {
+				console.warn(`组件还未初始化,请先使用 init`)
+				return
+			}
+			this.chart.setOption(...arguments);
+		},
+		showLoading() {
+			if(this.chart) {
+				this.chart.showLoading(...arguments)
+			}
+		},
+		hideLoading() {
+			if(this.chart) {
+				this.chart.hideLoading()
+			}
+		},
+		clear() {
+			if(this.chart) {
+				this.chart.clear()
+			}
+		},
+		dispose() {
+			if(this.chart) {
+				this.chart.dispose()
+			}
+		},
+		resize(size) {
+			if(size && size.width && size.height) {
+				this.height = size.height
+				this.width = size.width
+				if(this.chart) {this.chart.resize(size)}
+			} else {
+				this.$nextTick(() => {
+					uni.createSelectorQuery()
+						.in(this)
+						.select(`.lime-echart`)
+						.boundingClientRect()
+						.exec(res => {
+							if (res) {
+								let { width, height } = res[0];
+								this.width = width = width || 300;
+								this.height = height = height || 300;
+								this.chart.resize({width, height})
+							}
+						});
+				})
+				
+			}
+			
+		},
+		canvasToTempFilePath(args = {}) {
+			// #ifndef APP-NVUE
+			const { use2dCanvas, canvasId } = this;
+			return new Promise((resolve, reject) => {
+				const copyArgs = Object.assign({
+					canvasId,
+					success: resolve,
+					fail: reject
+				}, args);
+				if (use2dCanvas) {
+					delete copyArgs.canvasId;
+					copyArgs.canvas = this.canvasNode;
+				}
+				uni.canvasToTempFilePath(copyArgs, this);
+			});
+			// #endif
+			// #ifdef APP-NVUE
+			this.file = ''
+			this.$refs.webview.evalJs(`canvasToTempFilePath()`);
+			return new Promise((resolve, reject) => {
+				this.$watch('file', async (file) => {
+					if(file) {
+						const tempFilePath = await base64ToPath(file)
+						resolve(args.success({tempFilePath}))
+					} else {
+						reject(args.fail({error: ``}))
+					}
+				})
+			})
+			// #endif
+		},
+		async init(echarts, ...args) {
+			// #ifndef APP-NVUE
+			if(args && args.length == 0 && !echarts) {
+				console.error('缺少参数:init(echarts, theme?:string, opts?: object, callback?: function)')
+				return
+			}
+			// #endif
+			let theme=null,opts={},callback;
+			
+			Array.from(arguments).forEach(item => {
+				if(typeof item === 'function') {
+					callback = item
+				}
+				if(['string'].includes(typeof item)) {
+					theme = item
+				}
+				if(typeof item === 'object') {
+					opts = item
+				}
+			})
+			
+			if(this.beforeDelay) {
+				await sleep(this.beforeDelay)
+			}
+			let config = await this.getContext();
+			// #ifndef APP-NVUE
+			setCanvasCreator(echarts, config)
+			this.chart = echarts.init(config.canvas, theme, Object.assign({}, config, opts))
+			if(typeof callback === 'function') {
+				callback(this.chart)
+			} else {
+				return this.chart
+			}
+			// #endif
+			// #ifdef APP-NVUE
+			this.chart = new Echarts(this.$refs.webview)
+			this.$refs.webview.evalJs(`init(null, null, ${JSON.stringify(opts)}, ${theme})`)
+			if(callback) {
+				callback(this.chart)
+			} else {
+				return this.chart
+			}
+			// #endif
+		},
+		getContext() {
+			// #ifdef APP-NVUE
+			if(this.finished) {
+				return Promise.resolve(this.finished)
+			}
+			return new Promise(resolve => {
+				this.$watch('finished', (val) => {
+					if(val) {
+						resolve(this.finished)
+					}
+				})
+			})
+			// #endif
+			// #ifndef APP-NVUE
+			return getRect(`#${this.canvasId}`, {context: this, type: this.use2dCanvas ? 'fields': 'boundingClientRect'}).then(res => {
+				if(res) {
+					let dpr = devicePixelRatio
+					let {width, height, node} = res
+					let canvas;
+					this.width = width = width || 300;
+					this.height = height = height || 300;
+					if(node) {
+						const ctx = node.getContext('2d');
+						canvas = new Canvas(ctx, this, true, node);
+						this.canvasNode = node
+					} else {
+						// #ifdef MP-TOUTIAO
+						dpr = !this.isPC ? devicePixelRatio : 1// 1.25
+						// #endif
+						// #ifndef MP-ALIPAY || MP-TOUTIAO
+						dpr = this.isPC ? devicePixelRatio : 1
+						// #endif
+						// #ifdef MP-ALIPAY || MP-LARK
+						dpr = devicePixelRatio
+						// #endif
+						this.rect = res
+						this.nodeWidth = width * dpr;
+						this.nodeHeight = height * dpr;
+						const ctx = uni.createCanvasContext(this.canvasId, this);
+						canvas =  new Canvas(ctx, this, false);
+					}
+					return { canvas, width, height, devicePixelRatio: dpr, node };
+				} else {
+					return {}
+				}
+			})
+			// #endif
+		},
+		// #ifndef APP-NVUE
+		getRelative(e, touches) {
+			let { clientX, clientY } = e
+			if(!(clientX && clientY) && touches && touches[0]) {
+				clientX = touches[0].clientX
+				clientY = touches[0].clientY
+			}
+			return {x: clientX - this.rect.left, y: clientY - this.rect.top, wheelDelta: e.wheelDelta || 0}
+		},
+		getTouch(e, touches) {
+			const {x} = touches && touches[0] || {}
+			return x ? touches[0] : this.getRelative(e, touches);
+		},
+		touchStart(e) {
+			this.isDown = true
+			const next = () => {
+				const touches = convertTouchesToArray(e.touches)
+				if(this.chart) {
+					const touch = this.getTouch(e, touches)
+					this.startX = touch.x
+					this.startY = touch.y
+					this.startT = new Date()
+					const handler = this.chart.getZr().handler;
+					dispatch.call(handler, 'mousedown', touch)
+					dispatch.call(handler, 'mousemove', touch)
+					handler.processGesture(wrapTouch(e), 'start');
+					clearTimeout(this.endTimer);
+				}
+				
+			}
+			if(this.isPC) {
+				getRect(`#${this.canvasId}`, {context: this}).then(res => {
+					this.rect = res
+					next()
+				})
+				return
+			}
+			next()
+		},
+		touchMove(e) {
+			if(this.isPC && this.enableHover && !this.isDown) {this.isDown = true}
+			const touches = convertTouchesToArray(e.touches)
+			if (this.chart && this.isDown) {
+				const handler = this.chart.getZr().handler;
+				dispatch.call(handler, 'mousemove', this.getTouch(e, touches))
+				handler.processGesture(wrapTouch(e), 'change');
+			}
+			
+		},
+		touchEnd(e) {
+			this.isDown = false
+			if (this.chart) {
+				const touches = convertTouchesToArray(e.changedTouches)
+				const {x} = touches && touches[0] || {}
+				const touch = (x ? touches[0] : this.getRelative(e, touches)) || {};
+				const handler = this.chart.getZr().handler;
+				const isClick = Math.abs(touch.x - this.startX) < 10 && new Date() - this.startT < 200;
+				dispatch.call(handler, 'mouseup', touch)
+				handler.processGesture(wrapTouch(e), 'end');
+				if(isClick) {
+					dispatch.call(handler, 'click', touch)
+				} else {
+					this.endTimer = setTimeout(() => {
+						dispatch.call(handler, 'mousemove', {x: 999999999,y: 999999999});
+						dispatch.call(handler, 'mouseup', {x: 999999999,y: 999999999});
+					},50)
+				}
+			}
+		},
+		// #endif
+		// #ifdef H5
+		mousewheel(e){
+			if(this.chart) {
+				dispatch.call(this.chart.getZr().handler, 'mousewheel', this.getTouch(e))
+			}
+		}
+		// #endif
+	}
+};
+</script>
+<style>	
+.lime-echart {
+	position: relative;
+	/* #ifndef APP-NVUE */
+	width: 100%;
+	height: 100%;
+	/* #endif */
+	/* #ifdef APP-NVUE */
+	flex: 1;
+	/* #endif */
+}
+.lime-echart__canvas {
+	/* #ifndef APP-NVUE */
+	width: 100%;
+	height: 100%;
+	/* #endif */
+	/* #ifdef APP-NVUE */
+	flex: 1;
+	/* #endif */
+}
+/* #ifndef APP-NVUE */
+.lime-echart__mask {
+	position: absolute;
+	width: 100%;
+	height: 100%;
+	left: 0;
+	top: 0;
+	z-index: 1;
+}
+/* #endif */
+</style>

+ 51 - 0
src/components/l-echart/nvue.js

@@ -0,0 +1,51 @@
+export class Echarts {
+	eventMap = new Map()
+	constructor(webview) {
+		this.webview = webview
+		this.options = null
+	}
+	setOption() {
+		this.options = arguments
+		this.webview.evalJs(`setOption(${JSON.stringify(arguments)})`);
+	}
+	getOption() {
+		return this.options
+	}
+	showLoading() {
+		this.webview.evalJs(`showLoading(${JSON.stringify(arguments)})`);
+	}
+	hideLoading() {
+		this.webview.evalJs(`hideLoading()`);
+	}
+	clear() {
+		this.webview.evalJs(`clear()`);
+	}
+	dispose() {
+		this.webview.evalJs(`dispose()`);
+	}
+	resize(size) {
+		if(size) {
+			this.webview.evalJs(`resize(${JSON.stringify(size)})`);
+		} else {
+			this.webview.evalJs(`resize()`);
+		}
+	}
+	on(type, ...args) {
+		const query = args[0]
+		const useQuery = query && typeof query != 'function'
+		const param = useQuery ? [type, query] : [type]
+		const key = `${type}${useQuery ? JSON.stringify(query): '' }`
+		const callback = useQuery ? args[1]: args[0]
+		if(typeof callback == 'function'){
+			this.eventMap.set(key, callback)
+		}
+		this.webview.evalJs(`on(${JSON.stringify(param)})`);
+		console.warn('nvue 暂不支持事件')
+	}
+	dispatchAction(type, options){
+		const handler = this.eventMap.get(type)
+		if(handler){
+			handler(options)
+		}
+	}
+}

+ 145 - 0
src/components/l-echart/utils.js

@@ -0,0 +1,145 @@
+// #ifndef APP-NVUE
+// 计算版本
+export function compareVersion(v1, v2) {
+	v1 = v1.split('.')
+	v2 = v2.split('.')
+	const len = Math.max(v1.length, v2.length)
+	while (v1.length < len) {
+		v1.push('0')
+	}
+	while (v2.length < len) {
+		v2.push('0')
+	}
+	for (let i = 0; i < len; i++) {
+		const num1 = parseInt(v1[i], 10)
+		const num2 = parseInt(v2[i], 10)
+
+		if (num1 > num2) {
+			return 1
+		} else if (num1 < num2) {
+			return -1
+		}
+	}
+	return 0
+}
+const systemInfo = uni.getSystemInfoSync();
+
+function gte(version) {
+	// 截止 2023-03-22 mac pc小程序不支持 canvas 2d
+	let {
+		SDKVersion,
+		platform
+	} = systemInfo;
+	// #ifdef MP-ALIPAY
+	SDKVersion = my.SDKVersion
+	// #endif
+	// #ifdef MP-WEIXIN
+	return platform !== 'mac' && compareVersion(SDKVersion, version) >= 0;
+	// #endif
+	return compareVersion(SDKVersion, version) >= 0;
+}
+
+
+export function canIUseCanvas2d() {
+	// #ifdef MP-WEIXIN
+	return gte('2.9.0');
+	// #endif
+	// #ifdef MP-ALIPAY
+	return gte('2.7.0');
+	// #endif
+	// #ifdef MP-TOUTIAO
+	return gte('1.78.0');
+	// #endif
+	return false
+}
+
+export function convertTouchesToArray(touches) {
+	// 如果 touches 是一个数组,则直接返回它
+	if (Array.isArray(touches)) {
+		return touches;
+	}
+	// 如果touches是一个对象,则转换为数组
+	if (typeof touches === 'object' && touches !== null) {
+		return Object.values(touches);
+	}
+	// 对于其他类型,直接返回它
+	return touches;
+}
+
+export function wrapTouch(event) {
+	for (let i = 0; i < event.touches.length; ++i) {
+		const touch = event.touches[i];
+		touch.offsetX = touch.x;
+		touch.offsetY = touch.y;
+	}
+	return event;
+}
+export const devicePixelRatio = uni.getSystemInfoSync().pixelRatio
+// #endif
+// #ifdef APP-NVUE
+export function base64ToPath(base64) {
+	return new Promise((resolve, reject) => {
+		const [, format, bodyData] = /data:image\/(\w+);base64,(.*)/.exec(base64) || [];
+		const bitmap = new plus.nativeObj.Bitmap('bitmap' + Date.now())
+		bitmap.loadBase64Data(base64, () => {
+			if (!format) {
+				reject(new Error('ERROR_BASE64SRC_PARSE'))
+			}
+			const time = new Date().getTime();
+			const filePath = `_doc/uniapp_temp/${time}.${format}`
+
+			bitmap.save(filePath, {},
+				() => {
+					bitmap.clear()
+					resolve(filePath)
+				},
+				(error) => {
+					bitmap.clear()
+					console.error(`${JSON.stringify(error)}`)
+					reject(error)
+				})
+		}, (error) => {
+			bitmap.clear()
+			console.error(`${JSON.stringify(error)}`)
+			reject(error)
+		})
+	})
+}
+// #endif
+
+
+export function sleep(time) {
+	return new Promise((resolve) => {
+		setTimeout(() => {
+			resolve(true)
+		}, time)
+	})
+}
+
+
+export function getRect(selector, options = {}) {
+	const typeDefault = 'boundingClientRect'
+	const {
+		context,
+		type = typeDefault
+	} = options
+	return new Promise((resolve, reject) => {
+		const dom = uni.createSelectorQuery().in(context).select(selector);
+		const result = (rect) => {
+			if (rect) {
+				resolve(rect)
+			} else {
+				reject()
+			}
+		}
+		if (type == typeDefault) {
+			dom[type](result).exec()
+		} else {
+			dom[type]({
+				node: true,
+				size: true,
+				rect: true
+			}, result).exec()
+		}
+	});
+};

+ 133 - 0
src/components/l-echart/uvue.uts

@@ -0,0 +1,133 @@
+// @ts-nocheck
+// #ifdef APP
+type EchartsEventHandler = (event: UTSJSONObject)=>void
+// type EchartsTempResolve = (obj : UTSJSONObject) => void
+// type EchartsTempOptions = UTSJSONObject
+export class Echarts {
+	options: UTSJSONObject = {} as UTSJSONObject
+	context: UniWebViewElement
+	eventMap: Map<string, EchartsEventHandler> = new Map()
+	private temp: UTSJSONObject[] = []
+	constructor(context: UniWebViewElement){
+		this.context = context
+		this.init()
+	}
+	init(){
+		this.context.evalJS(`init(null, null, ${JSON.stringify({})})`)
+		
+		this.context.addEventListener('message', (e : UniWebViewMessageEvent) => {
+			// event.stopPropagation()
+			// event.preventDefault()
+			
+			const detail = e.detail.data[0]
+			const file = detail.getString('file')
+			const data = detail.get('data')
+			const key = detail.getString('event')
+			const options = typeof data == 'object' ? (data as UTSJSONObject).getJSON('options'): null
+			const event = typeof data == 'object' ? (data as UTSJSONObject).getString('event'): null
+			if (key == 'log' && data != null) {
+				console.log(data)
+			}
+			if (event != null && options != null) {
+				this.dispatchAction(event.replace(/"/g,''), options)
+			}
+			if(file != null){
+				while (this.temp.length > 0) {
+					const opt = this.temp.pop()
+					const success = opt?.get('success')
+					if(typeof success == 'function'){
+						success as (res: UTSJSONObject) => void
+						success({tempFilePath: file})
+					}
+				}
+			}
+			
+		})
+	}
+	setOption(option: UTSJSONObject){
+		this.options = option;
+		this.context.evalJS(`setOption(${JSON.stringify([option])})`)
+	}
+	setOption(option: UTSJSONObject, notMerge: boolean = false, lazyUpdate: boolean = false){
+		this.options = option;
+		this.context.evalJS(`setOption(${JSON.stringify([option, notMerge, lazyUpdate])})`)
+	}
+	setOption(option: UTSJSONObject, notMerge: UTSJSONObject){
+		this.options = option;
+		this.context.evalJS(`setOption(${JSON.stringify([option, notMerge])})`)
+	}
+	getOption(): UTSJSONObject {
+		return this.options
+	}
+	showLoading(){
+		this.context.evalJS(`showLoading(${JSON.stringify([] as any[])})`);
+	}
+	showLoading(type: string, opts: UTSJSONObject){
+		this.context.evalJS(`showLoading(${JSON.stringify([type, opts])})`);
+	}
+	hideLoading(){
+		this.context.evalJS(`hideLoading()`);
+	}
+	clear(){
+		this.context.evalJS(`clear()`);
+	}
+	dispose(){
+		this.context.evalJS(`dispose()`);
+	}
+	resize(size:UTSJSONObject){
+		setTimeout(()=>{
+			this.context.evalJS(`resize(${JSON.stringify(size)})`);
+		},0)
+	}
+	resize(){
+		setTimeout(()=>{
+			this.context.evalJS(`resize()`);
+		},10)
+		
+	}
+	on(type:string, query: any, callback: EchartsEventHandler) {
+		const key = `${type}${JSON.stringify(query)}`
+		if(typeof callback == 'function'){
+			this.eventMap.set(key, callback)
+		}
+		this.context.evalJS(`on(${JSON.stringify([type, query])})`);
+		console.warn('uvue 暂不支持事件')
+	}
+	on(type:string, callback: EchartsEventHandler) {
+		const key = `${type}`
+		if(typeof callback == 'function'){
+			this.eventMap.set(key, callback)
+		}
+		this.context.evalJS(`on(${JSON.stringify([type])})`);
+		console.warn('uvue 暂不支持事件')
+	}
+	dispatchAction(type:string, options: UTSJSONObject){
+		const handler = this.eventMap.get(type)
+		if(handler!=null){
+			handler(options)
+		}
+	}
+	canvasToTempFilePath(opt: UTSJSONObject){
+		// this.context.evalJS(`on(${JSON.stringify(opt)})`);
+		this.context.evalJS(`canvasToTempFilePath(${JSON.stringify(opt)})`);
+		this.temp.push(opt)
+	}
+}
+
+// #endif
+// #ifndef APP
+export class Echarts {
+	constructor() {}
+	setOption(option: UTSJSONObject): void
+	isDisposed(): boolean;
+	clear(): void;
+	resize(size:UTSJSONObject): void;
+	resize(): void;
+	canvasToTempFilePath(opt : UTSJSONObject): void;
+	dispose(): void;
+	showLoading(cfg?: UTSJSONObject): void;
+	showLoading(name?: string, cfg?: UTSJSONObject): void;
+	hideLoading(): void;
+	getZr(): any
+}
+// #endif

+ 237 - 0
src/components/next-search-more/next-search-more.vue

@@ -0,0 +1,237 @@
+<template>
+	<view class="next-search-more">
+		<view class="search" :style="{ backgroundColor: backgroundColor }">
+			<view class="content" :style="{ 'border-radius': radius + 'px', border: border }">
+				<view class="content-box" :class="{ center: mode === 'center' }">
+					<!-- <text class="icon icon-search">&#xe66f;</text> -->
+					<uni-icons class="icon" color="#999" type="search" style=" padding: 0 5px;"></uni-icons>
+					<input class="input" :class="{ center: !active && mode === 'center' }" :focus="isFocus" :placeholder="placeholder" v-model="inputVal" @input="input" @focus="focus" @blur="blur" />
+					<text v-if="isDelShow" class="icon icon-del" @click="clear">&#xe61c;</text>
+				</view>
+				<view v-if="(active && isFixedSearchBtn && button === 'inside') || (isDelShow && button === 'inside')" class="searchBtn" @click="search">搜索</view>
+			</view>
+			<template v-if="mode === 'common' || mode ==='center'">
+				<view v-if="button === 'outside'" class="button" :class="{ active: isFixedSearchBtn || active }" @click="search">
+					<view class="button-item">{{ !isFixedSearchBtn ? searchName : '搜索' }}</view>
+				</view>
+			</template>
+			<template v-else-if="mode === 'more'">
+				<view class="button active" @click="selectMore">
+					<view class="button-item"><text class="icon icon-more">&#xe61a;</text></view>
+				</view>
+			</template>
+		</view>
+		<view class="more-container-parent">
+			<view v-if="mode === 'more' && showMore" class="more-container">
+				<slot name="more"></slot>
+			</view>
+		</view>
+	</view>
+</template>
+
+<script>
+export default {
+	props: {
+		mode: {
+			type: String,
+			default: 'common'
+		},
+		button: {
+			type: String,
+			default: 'outside'
+		},
+		isFixedSearchBtn: {
+			type: Boolean,
+			default: true
+		},
+		radius: {
+			type: String,
+			default: '60'
+		},
+		placeholder: {
+			type: String,
+			default: '请输入搜索内容'
+		},
+		backgroundColor: {
+			type: String,
+			default: '#fff'
+		},
+		showMore: {
+			type: Boolean,
+			default: false
+		},
+		border: { type: String, default: '1px #f5f5f5 solid' }
+		
+	},
+	data() {
+		return {
+			active: false,
+			inputVal: '',
+			searchName: '取消',
+			isDelShow: false,
+			isFocus: false,
+			timer: 0
+		};
+	},
+	methods: {
+		focus() {
+			this.active = true;
+		},
+		blur() {
+			this.isFocus = false;
+			if (!this.inputVal) {
+				this.active = false;
+			}
+		},
+		input() {
+			clearInterval(this.timer)
+			this.timer = setTimeout(() => {
+				this.$emit('input', this.inputVal);
+			}, 500)
+		},
+		clear() {
+			this.inputVal = '';
+			this.active = false;
+			this.$emit('input', this.inputVal);
+			this.$emit('search', '');
+		},
+		getFocus() {
+			this.isFocus = true;
+		},
+		search() {
+			if (!this.inputVal) return;
+			this.$emit('search', this.inputVal);
+		},
+		selectMore() {
+			this.$emit('moreClick')
+		}
+	},
+	created() {
+		this.$watch(() => this.inputVal, (newVal) => {
+			if (newVal) {
+				this.searchName = '搜索';
+				this.isDelShow = true;
+			} else {
+				this.searchName = '取消';
+				this.isDelShow = false;
+			}
+		})
+	}
+};
+</script>
+
+<style lang="scss" scoped>
+.next-search-more {
+	.search {
+		display: flex;
+		width: 100%;
+		border-bottom: 1px #f5f5f5 solid;
+		box-sizing: border-box;
+		padding: 15upx;
+		font-size: $uni-font-size-base;
+		background: #fff;
+		.content {
+			display: flex;
+			align-items: center;
+			width: 100%;
+			height: 60upx;
+			border: 1px #ccc solid;
+			background: #fff;
+			overflow: hidden;
+			transition: all 0.2s linear;
+			border-radius: 30px;
+	
+			.content-box {
+				width: 100%;
+				display: flex;
+				align-items: center;
+				&.center {
+					justify-content: center;
+				}
+				.icon {
+					padding: 0 15upx;
+					&.icon-del {
+						font-size: 38upx;
+					}
+				}
+				.input {
+					width: 100%;
+					max-width: 100%;
+					line-height: 60upx;
+					height: 60upx;
+					transition: all 0.2s linear;
+					&.center {
+						width: 200upx;
+					}
+					&.sub {
+						// position: absolute;
+						width: auto;
+						color: grey;
+					}
+				}
+			}
+			.searchBtn {
+				height: 100%;
+				flex-shrink: 0;
+				padding: 0 30upx;
+				background: $uni-color-success;
+				line-height: 60upx;
+				color: #fff;
+				border-left: 1px #ccc solid;
+				transition: all 0.3s;
+			}
+		}
+	
+		.button {
+			display: flex;
+			align-items: center;
+			justify-content: center;
+			position: relative;
+			flex-shrink: 0;
+			width: 0;
+			transition: all 0.2s linear;
+			white-space: nowrap;
+			overflow: hidden;
+			&.active {
+				padding-left: 15upx;
+				width: 100upx;
+			}
+			.icon-more {
+				font-size: 48upx;
+			}
+		}
+	}
+	.more-container-parent {
+		flex-shrink: 0;
+		width: 100%;
+		// position: fixed;
+		// position: sticky;
+		z-index: 997;
+		flex-wrap: nowrap;
+		display: flex;
+		flex-direction: row;
+		position: relative;
+		flex-direction: column;
+		.more-container {
+			position: absolute;
+			top: 0;
+			left: 0;
+			width: 100%;
+			height: auto;
+			background-color: #ffffff;
+			padding: 20rpx;
+			border-radius: 0 0 30rpx 30rpx;
+			box-sizing: border-box;
+			overflow: hidden;
+		}
+	}
+}
+
+
+.icon {
+	font-family: iconfont;
+	font-size: 32upx;
+	font-style: normal;
+	color: #999;
+}
+</style>

File diff suppressed because it is too large
+ 202 - 0
src/components/next-tree/next-tree.vue


+ 270 - 0
src/components/next-tree/style.css

@@ -0,0 +1,270 @@
+/* @font-face {
+	font-family: 'iconfont';
+	src: url('//at.alicdn.com/t/c/font_4110624_qs48wckazsh.ttf?t=1712479573821') format('truetype');
+} */
+@keyframes spin {
+	0% { transform: rotate(0deg); }
+	100% { transform: rotate(360deg); }
+}
+
+.iconfont {
+	font-family: iconfont;
+	font-style: normal;
+}
+.iconfont-loading {
+	font-family: iconfont;
+	display: inline-block;
+	font-style: normal;
+	animation: spin 1s linear infinite;
+}
+.next-tree-mask {
+  position: fixed;
+  top: 0rpx;
+  right: 0rpx;
+  bottom: 0rpx;
+  left: 0rpx;
+  z-index: 997;
+  background-color: rgba(0, 0, 0, 0.6);
+  opacity: 0;
+  transition: all 0.3s ease;
+  visibility: hidden;
+}
+.next-tree-mask.show {
+  visibility: visible;
+  opacity: 1;
+}
+.next-tree-cnt {
+  position: fixed;
+  top: 0rpx;
+  right: 0rpx;
+  bottom: 0rpx;
+  left: 0rpx;
+  z-index: 997;
+  top: 360rpx;
+  transition: all 0.3s ease;
+  transform: translateY(100%);
+}
+.next-tree-cnt.next-tree-cnt-page {
+	transition: none;
+}
+.next-tree-cnt.show {
+  transform: translateY(0);
+}
+.next-tree-bar {
+  background-color: #fff;
+  height: 72rpx;
+  padding-left: 20rpx;
+  padding-right: 20rpx;
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  box-sizing: border-box;
+  border-bottom-width: 1rpx !important;
+  border-bottom-style: solid;
+  border-bottom-color: #f5f5f5;
+  font-size: 32rpx;
+  color: #757575;
+  line-height: 1;
+}
+.next-tree-bar-btns {
+	display: inline-block;
+	display: flex;
+	flex-direction: row;
+}
+.btn-divid {
+	display: inline-block;
+	width: 1px;
+	margin: 0 10px;
+	background-color: #ccc;
+}
+.next-tree-bar-confirm {
+  color: #007aff;
+}
+.next-tree-view {
+  position: absolute;
+  top: 0rpx;
+  right: 0rpx;
+  bottom: 0rpx;
+  left: 0rpx;
+  top: 72rpx;
+  background-color: #fff;
+  padding-top: 20rpx;
+  padding-right: 20rpx;
+  padding-bottom: 20rpx;
+  padding-left: 20rpx;
+}
+.next-tree-view-sc {
+  height: 100%;
+  overflow: hidden;
+}
+.next-tree-view-sc .empty {
+	text-align: center;
+	color: #757575;
+	padding: 30rpx;
+}
+.next-tree-item-block {
+	
+}
+.next-tree-item {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  font-size: 26rpx;
+  color: #757575;
+  line-height: 1;
+  height: 0;
+  opacity: 0;
+  transition: 0.2s;
+  position: relative;
+  overflow: hidden;
+}
+.next-tree-item .icon-btn {
+	font-size: 30rpx;
+	margin-right: 20rpx;
+}
+.next-tree-item .left-line {
+	position: relative;
+	width: 1rpx;
+	height: 100%;
+	box-sizing: border-box;
+}
+.next-tree-item .left-line::before {
+	position: absolute;
+	content: "";
+	width: 1rpx;
+	height: 100%;
+	background-color: rgba(204,204,204,0.9);
+	box-sizing: border-box;
+	
+	left: -18rpx;
+}
+.next-tree-item .parent-horizontal-line {
+	width: 1rpx;
+	height: 100%;
+	position: absolute;
+	top: 0;
+	left: 0rpx;
+	box-sizing: border-box;
+	background-color: rgba(204,204,204,0.9);
+}
+.next-tree-item .left-line .horizontal-line {
+	width: 20rpx;
+	height: 1rpx;
+	position: absolute;
+	top: 40rpx;
+	left: 0rpx;
+	background-color: rgba(204,204,204,0.9);
+	box-sizing: border-box;
+}
+
+.next-tree-item.show {
+  height: 80rpx;
+  opacity: 1;
+}
+.next-tree-item.showchild:before {
+  transform: rotate(90deg);
+}
+.next-tree-item.border {
+  border-bottom: 1rpx solid rgba(204,204,204,0.2);
+}
+.next-tree-item.last:before {
+  opacity: 0;
+}
+.next-tree-item.disabled {
+  color: #ccc!important;
+}
+
+.next-tree-icon {
+  width: 26rpx;
+  height: 26rpx;
+  margin-right: 8rpx;
+}
+.next-tree-label {
+  flex: 1;
+  display: flex;
+  align-items: center;
+  height: 100%;
+  line-height: 1.2;
+}
+.next-tree-label .label-input {
+	border: 1rpx solid #f0f0f0;
+	border-radius: 10rpx;
+	width: 100%;
+	padding: 12rpx 18rpx;
+	margin-right: 30rpx;
+}
+.next-tree-check {
+  width: 40px;
+  height: 40px;
+  display: flex;
+  justify-content: center;
+  align-items: center;
+}
+.next-tree-check-yes,
+.next-tree-check-no {
+  width: 20px;
+  height: 20px;
+  border-top-left-radius: 20%;
+  border-top-right-radius: 20%;
+  border-bottom-right-radius: 20%;
+  border-bottom-left-radius: 20%;
+  border-top-width: 1rpx;
+  border-left-width: 1rpx;
+  border-bottom-width: 1rpx;
+  border-right-width: 1rpx;
+  border-style: solid;
+  border-color: #007aff;
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  box-sizing: border-box;
+}
+.next-tree-check-yes-b {
+  border-top-left-radius: 20%;
+  border-top-right-radius: 20%;
+  border-bottom-right-radius: 20%;
+  border-bottom-left-radius: 20%;
+  background-color: #007aff;
+	color: #fff;
+}
+.next-tree-check-yes-b .icon-text {
+	font-size: 14px;
+	font-weight: normal;
+	font-family: uicon-iconfont;
+	display: flex;
+	flex-direction: row;
+	align-items: center;
+}
+.next-tree-check .radio {
+  border-top-left-radius: 50%;
+  border-top-right-radius: 50%;
+  border-bottom-right-radius: 50%;
+  border-bottom-left-radius: 50%;
+}
+.next-tree-check .radio .next-tree-check-yes-b {
+  border-top-left-radius: 50%;
+  border-top-right-radius: 50%;
+  border-bottom-right-radius: 50%;
+  border-bottom-left-radius: 50%;
+}
+
+.next-tree-item.disabled .next-tree-check-no {
+	color: #ccc!important;
+}
+.next-tree-item.disabled .next-tree-check-yes-b {
+	background-color: #ccc!important;
+}
+.hover-c {
+  opacity: 0.6;
+}
+
+.fixed-bottom-bar {
+	position: fixed;
+	bottom: 0rpx;
+	left: 0rpx;
+	right: 0rpx;
+  background: #fff;
+	z-index: 998;
+}
+
+

+ 20 - 0
src/components/page/index.ts

@@ -0,0 +1,20 @@
+
+export interface IPage {
+  pageIndex: number
+  pageSize: number
+  total: number
+  sizes: number[]
+}
+
+export default () => {
+  const page: IPage = reactive({
+    pageIndex: 1,
+    pageSize: 10,
+    total: 0,
+    sizes: [10, 20, 50, 100, 200]
+  })
+
+  return {
+    page
+  }
+}

+ 79 - 0
src/components/part-dropdown-footer/part-dropdown-footer.vue

@@ -0,0 +1,79 @@
+<template>
+  <view class="da-dropdown-footer" :style="{
+    padding: isDropdown ? '24rpx' : '',
+    marginTop: isDropdown ? '20rpx' : ''
+  }">
+    <view class="da-dropdown-footer--reset" @click="handleReset()">{{ resetText || '重置' }}</view>
+    <view class="da-dropdown-footer--confirm" @click="handleConfirm()">
+      {{ confirmText || '确定' }}
+    </view>
+  </view>
+</template>
+
+<script>
+
+export default{
+  props: {
+    resetText: {
+      type: String,
+      default: '重置',
+    },
+    confirmText: {
+      type: String,
+      default: '确定',
+    },
+    isDropdown: {
+      type: Boolean,
+      default: true,
+    }
+  },
+  emits: ['confirm', 'reset'],
+  setup(_, { emit }) {
+    function handleReset() {
+      emit('reset')
+    }
+    function handleConfirm() {
+      emit('confirm')
+    }
+
+    return {
+      handleReset,
+      handleConfirm,
+    }
+  },
+}
+</script>
+
+<style lang="scss" scoped>
+.da-dropdown-footer {
+  display: flex;
+  align-items: center;
+
+
+  &--reset,
+  &--confirm {
+    display: flex;
+    flex: 1;
+    align-items: center;
+    justify-content: center;
+    height: 66rpx;
+    font-size: 28rpx;
+    color: #555;
+    background-color: #fff;
+    border: 2rpx solid #ccc;
+    border-radius: 66rpx;
+  }
+
+  &--confirm {
+    margin-left: 24rpx;
+    color: #fff;
+    background-color: var(--dropdown-theme-color);
+    border-color: var(--dropdown-theme-color);
+  }
+
+  &--reset:hover,
+  &--confirm:hover {
+    opacity: 0.8;
+  }
+}
+</style>

+ 202 - 0
src/components/process-work/index.vue

@@ -0,0 +1,202 @@
+<template>
+    <view>
+        <uni-popup ref="work_popup" background-color="#fff" type="center" borderRadius="10px">
+            <view
+                style="height: 30px;line-height: 30px; text-align: center; padding: 10px; font-size: 15px; font-weight: 600;">
+                <text>{{ detail.work?.name }}</text>
+            </view>
+            <view class="popup-content" style="padding: 10px;min-height: 150px;width: 80vw;">
+                <view style="padding:15px;font-size: 13px;">
+                    <rich-text :nodes="parseHTML(detail.work?.desc)"></rich-text>
+                </view>
+            </view>
+            <view>
+                <button size="default"
+                    style="color:#ffffff;background-color:#007aff;border-color:#007aff;font-size: 24rpx;width:120rpx; margin:20rpx; float: right;"
+                    hover-class="is-hover" @click="closePopup()">确认</button>
+            </view>
+        </uni-popup>
+    </view>
+</template>
+
+<script lang="ts" setup>
+import {
+    getWorkDetail,
+    getWorkUndoneCount,
+    submitWork
+} from '@/api/project-center/work'
+import { useEmitt } from '@/logics/mitt/useEmitt';
+import { WorkDetailResParams } from '@/types/work';
+import { parseHTML } from '@/utils'
+
+const workId = ref()
+const work_popup = ref()
+const { emitter } = useEmitt()
+
+const detail = reactive<{ work?: WorkDetailResParams }>({
+    work: undefined
+}) // 详情
+
+/**
+ * 显示级别modal
+ * @param type modal 类型
+ * @param item
+ */
+const showWorkModal = async (id: string) => {
+    const workDetail = await getWorkDetail(id)
+    detail.work = workDetail
+    if (workDetail.status === 2 && workDetail.process_type === 1) {
+        return
+    }
+    let type = 0 // 弹出确认框
+    if (workDetail.process_type === 1 && workDetail.form) {
+        type = 1
+    }
+    if (workDetail.process_type === 2) {
+        type = 2
+    }
+    workId.value = workDetail.id
+    if (type === 0) {
+        showConfirmModal(workDetail)
+    }
+    if (type === 1) {
+        uni.navigateTo({ url: `/pages/project/components/form-work?id=${workDetail.id}` })
+        // modalTitle.value = workDetail.form?.title
+        // schema.value = workDetail.form?.schema || {}
+        // formData.value = getDefaultFormState(schema.value, {}, schema.value, true)
+        // visibleWorkModal.value = true
+    }
+    if (type === 2) {
+        showToBeReadModal(workDetail)
+    }
+}
+
+
+/**
+ * 当不需要提交表单,且为待办时,生成确认框。
+ * @param detail
+ */
+const showConfirmModal = async (detail: WorkDetailResParams) => {
+    uni.showModal({
+        title: detail.name,
+        content: `确认${detail.name}吗?`,
+        success: async (res: { confirm: boolean }) => {
+            const { confirm } = res;
+            if (confirm) {
+                await submitWork({ id: detail.id! })
+                emitter.emit('get_project_list')
+                emitter.emit('get_project_log')
+                emitter.emit('get_task_log')
+                uni.showToast({
+                    icon: 'success',
+                    title: '操作成功'
+                })
+                setTimeout(() => {
+                    emitter.emit('get_task_list')
+                    emitter.emit('get_task_detail')
+                    emitter.emit('get_project_detail', true)
+                    // resetWorkUndoneCount()
+                }, 1500)
+            }
+        }
+    })
+    //   const title = {
+    //     setup() {
+    //       return () => h('strong', detail.name)
+    //     }
+    //   }
+    //   const ModalContent = {
+    //     setup() {
+    //       return () =>
+    //         h('span', { style: 'margin: 10px;' }, [
+    //           h('span', `确认`),
+    //           h('strong', `${detail.name}`),
+    //           h('span', ` 吗?`)
+    //         ])
+    //     }
+    //   }
+    //   Modal.warning({
+    //     title: () => h(title),
+    //     content: () => h(ModalContent),
+    //     alignCenter: true,
+    //     titleAlign: 'start',
+    //     mask: true,
+    //     maskClosable: false,
+    //     draggable: true,
+    //     escToClose: true,
+    //     width: 380,
+    //     simple: true,
+    //     hideCancel: false,
+    //     onOk: async () => {
+    //       try {
+    //         await submitWork({ id: detail.id! })
+    //         emitter.emit('get_project_list')
+    //         emitter.emit('get_project_log')
+    //         emitter.emit('get_task_log')
+    //         Message.success('操作成功')
+    //         setTimeout(() => {
+    //           emitter.emit('get_task_list')
+    //           emitter.emit('get_task_detail')
+    //           emitter.emit('get_project_detail', true)
+    //           resetWorkUndoneCount()
+    //         }, 1500)
+    //       } catch (_e) {
+    //         loading.value = false
+    //       }
+    //     }
+    //   })
+}
+
+// 显示阅读对话框
+const showToBeReadModal = async (detail: WorkDetailResParams) => {
+    work_popup.value?.open()
+}
+
+const closePopup = async () => {
+    work_popup.value?.close()
+    if (detail.work?.status === 2) {
+        return
+    }
+    await submitWork({ id: detail.work?.id! })
+    uni.showToast({
+        icon: 'success',
+        title: '操作成功'
+    })
+    setTimeout(() => {
+        emitter.emit('get_project_list')
+        emitter.emit('get_project_log')
+        emitter.emit('get_task_log')
+        emitter.emit('get_work_list')
+        emitter.emit('get_project_detail', true)
+    }, 1500)
+}
+
+defineExpose({ showWorkModal }) // 暴露子组件的方法或者数据
+
+
+// const finalyDescText = ((htmlString: string) => {
+//     let charsArray: any = [];
+
+//     const handler = {
+//          start :function (tag: string) {
+//         const element = {
+//             name: tag,
+//             children: [],
+//             attrs: {}
+//         }
+//         charsArray.push(element);
+//     }
+//         chars: function (text: string) {
+//             charsArray.push(text);
+//         }
+//     };
+//     if (detail.work?.desc) {
+//         htmlParser(detail.work?.desc, handler);
+//     }
+//     return charsArray
+// })
+
+onMounted(() => {
+    // console.log(htmlParser('<div>test</div>', {}))
+})
+</script>

+ 154 - 0
src/components/selected-list-item/index.vue

@@ -0,0 +1,154 @@
+<template>
+    <!-- <button class="button" @click="toggle()"><text class="button-text">右侧</text></button> -->
+    <uni-popup ref="popup" background-color="#fff" @change="change">
+        <view class="popup-content" :class="{ 'popup-height': 'right' }">
+            <div class="search">
+                <uni-search-bar class="uni-mt-10" radius="5" placeholder="自动显示隐藏" clearButton="auto" cancelButton="none"
+                    @confirm="search" style="width: 200px;" />
+            </div>
+            <div class="checkbox">
+                <uni-data-checkbox v-model="selectedId" :localdata="list" model="button" :map="customColumn"
+                    style="width: 100%;"></uni-data-checkbox>
+            </div>
+            
+            <!-- <div class="pagination"><uni-pagination :total="50" title="标题文字" style="width:100%" /></div> -->
+            <div class="footer">
+                <button size="default" style="color:#ffffff;background-color:#007aff;border-color:#007aff;margin-top: 10px;width:100%"
+        hover-class="is-hover" @click="close()">确认</button>
+            </div>
+        </view>
+    </uni-popup>
+</template>
+
+<script lang="ts" setup>
+const emit = defineEmits(['closeDrawer'])
+
+const props = defineProps({
+    modelValue: {
+        type: [String],
+        required: false
+    },
+    showCheckboxDrawer: {
+        type: Boolean,
+        default: () => {
+            return false;
+        }
+    },
+    list: {
+        type: Array as PropType<{ id: string, name: string }[]>
+    }
+})
+
+const selectedId = ref()
+const popup = ref()
+const customColumn = {
+    text: 'name',
+    value: 'id'
+}
+
+const toggle = () => {
+    popup.value?.open('right')
+}
+const search = () => {
+
+}
+const change = (e:any) =>{
+    if(!e.show){
+        emit('closeDrawer', selectedId.value)
+        selectedId.value = undefined
+    }
+   
+}
+
+const close = ()=>{
+    popup.value?.close()
+}
+
+watch(
+    () => props.showCheckboxDrawer,
+    _value => {
+        if (props.showCheckboxDrawer) {
+            toggle()
+        } 
+    }
+)
+
+watch(
+  () => props.modelValue,
+  () => {
+    selectedId.value = props.modelValue
+  }
+)
+
+</script>
+<script lang="ts">
+export default {
+    options: {
+        styleIsolation: 'apply-shared', //解除样式隔离
+    }
+
+}
+</script>
+<style lang="scss" scoped>
+@mixin flex {
+    /* #ifndef APP-NVUE */
+    display: flex;
+    /* #endif */
+    flex-direction: row;
+}
+
+@mixin height {
+    /* #ifndef APP-NVUE */
+    height: 80%;
+    /* #endif */
+    /* #ifdef APP-NVUE */
+    flex: 1;
+    /* #endif */
+}
+
+.popup-content {
+    @include flex;
+    padding: 10px;
+    flex-direction: column;
+    background-color: #fff;
+    height: 100%;
+    justify-content: space-around;
+}
+
+.popup-height {
+    @include height;
+    width: auto;
+}
+
+.search {
+    @include flex;
+    justify-content: center;
+}
+
+.checkbox {
+    @include flex;
+    height: 80%;
+    overflow: auto;
+}
+
+.pagination {
+    @include flex;
+    width: 100%;
+    margin: 0 5px;
+    justify-content: center;
+}
+.footer{
+    @include flex;
+    width: 100%;
+    margin: 0 5px;
+    justify-content: center;
+}
+
+:deep(.uni-data-checklist .checklist-group) {
+    flex-direction: column !important;
+}
+
+:deep(.uni-data-checklist .checklist-group .checklist-box) {
+    margin: 10px 10px;
+}
+</style>

+ 280 - 0
src/components/task-tree/circle-progress.vue

@@ -0,0 +1,280 @@
+<template>
+	<view>
+		2222222222
+	</view>
+</template>
+
+<script setup lang="ts">
+const emits = defineEmits(['change', 'end'])
+const props = defineProps({
+	/*
+				  传值需使用rpx进行转换保证各终端兼容
+				  px = rpx / 750 * wx.getSystemInfoSync().windowWidth
+				  圆形进度条(画布)宽度,直径 [px]
+				*/
+	diam: {
+		type: Number,
+		default: 60
+	},
+	height: { // //圆形进度条(画布)高度,默认取diam值[当画半弧时传值,height有值时则取height]
+		type: Number,
+		default: 0
+	},
+	lineWidth: { // 进度条线条宽度[px]
+		type: Number,
+		default: 4
+	},
+
+	lineCap: {
+		type: String,
+		default: 'round'
+	},
+	//圆环进度字体大小 [px]
+	fontSize: {
+		type: Number,
+		default: 12
+	},
+	//是否显示进度文字
+	fontShow: {
+		type: Boolean,
+		default: true
+	},
+	/*
+		 自定义显示文字[默认为空,显示百分比,fontShow=true时生效]
+		 可以使用 slot自定义显示内容
+		*/
+	percentText: {
+		type: String,
+		default: ''
+	},
+	//是否显示默认(背景)进度条
+	defaultShow: {
+		type: Boolean,
+		default: true
+	},
+	//默认进度条颜色
+	defaultColor: {
+		type: String,
+		default: '#CCCCCC'
+	},
+	//进度条颜色
+	progressColor: {
+		type: String,
+		default: ''
+	},
+	//进度条渐变颜色[结合progressColor使用,默认为空]
+	gradualColor: {
+		type: String,
+		default: ''
+	},
+	//起始弧度,单位弧度
+	sAngle: {
+		type: Number,
+		default: -Math.PI / 2
+	},
+	//指定弧度的方向是逆时针还是顺时针。默认是false,即顺时针
+	counterclockwise: {
+		type: Boolean,
+		default: false
+	},
+	//进度百分比 [10% 传值 10]
+	percentage: {
+		type: Number,
+		default: 0
+	},
+	//进度百分比缩放倍数[使用半弧为100%时,则可传2]
+	multiple: {
+		type: Number,
+		default: 1
+	},
+	//动画执行时间[单位毫秒,低于50无动画]
+	duration: {
+		type: Number,
+		default: 800
+	},
+	//backwards: 动画从头播;forwards:动画从上次结束点接着播
+	activeMode: {
+		type: String,
+		default: 'backwards'
+	}
+})
+
+
+// #ifndef MP-WEIXIN || MP-QQ
+let cid = `id01_${Math.ceil(Math.random() * 10e5).toString(36)}`
+let did = `id02_${Math.ceil(Math.random() * 10e5).toString(36)}`
+// #endif
+
+// #ifdef MP-WEIXIN || MP-QQ
+const progressCanvasId = 'progressCanvasId';
+	defaultCanvasId: 'defaultCanvasId';
+// #endif
+// #ifndef MP-WEIXIN || MP-QQ
+const progressCanvasId = cid;
+const defaultCanvasId = did;
+// #endif
+const progressContext = null;
+const linearGradient = null;
+//起始百分比
+const startPercentage = 0
+
+const initDraw = (init) => {
+	let start = this.activeMode === 'backwards' ? 0 : this.startPercentage;
+	start = start > this.percentage ? 0 : start;
+	if (this.defaultShow && init) {
+		this.drawDefaultCircular();
+	}
+	this.drawProgressCircular(start);
+}
+//默认(背景)圆环
+const drawDefaultCircular = () => {
+	let ctx = uni.createCanvasContext(this.defaultCanvasId, this);
+	let lineWidth = Number(this.lineWidth)
+	// #ifdef MP-ALIPAY
+	lineWidth = lineWidth * 4
+	// #endif
+	ctx.setLineWidth(lineWidth);
+	ctx.setStrokeStyle(this.defaultColor);
+	//终止弧度
+	let eAngle = Math.PI * (this.height ? 1 : 2) + this.sAngle;
+	this.drawArc(ctx, eAngle);
+}
+
+//进度圆环
+const drawProgressCircular = (startPercentage) => {
+	let ctx = this.progressContext;
+	let gradient = this.linearGradient;
+	if (!ctx) {
+		ctx = uni.createCanvasContext(this.progressCanvasId, this);
+		//创建一个线性的渐变颜色 CanvasGradient对象
+		let diam = Number(this.diam)
+		// #ifdef MP-ALIPAY
+		diam = diam * 4
+		// #endif
+		const progressColor = this.progressColor || (uni && uni.$tui && uni.$tui.color.primary) || '#5677fc';
+		gradient = ctx.createLinearGradient(0, 0, diam, 0);
+		gradient.addColorStop('0', progressColor);
+		if (this.gradualColor) {
+			gradient.addColorStop('1', this.gradualColor);
+		}
+		// #ifdef APP-PLUS || MP
+		const res = uni.getSystemInfoSync();
+		if (!this.gradualColor && res.platform.toLocaleLowerCase() == 'android') {
+			gradient.addColorStop('1', progressColor);
+		}
+		// #endif
+		this.progressContext = ctx;
+		this.linearGradient = gradient;
+	}
+	let lineWidth = Number(this.lineWidth)
+	// #ifdef MP-ALIPAY
+	lineWidth = lineWidth * 4
+	// #endif
+	ctx.setLineWidth(lineWidth);
+	ctx.setStrokeStyle(gradient);
+	let time = this.percentage == 0 || this.duration < 50 ? 0 : this.duration / this.percentage;
+	if (this.percentage > 0) {
+		startPercentage = this.duration < 50 ? this.percentage - 1 : startPercentage;
+		startPercentage++;
+	}
+	if (this.fontShow) {
+		let fontSize = Number(this.fontSize)
+		// #ifdef MP-ALIPAY
+		fontSize = fontSize * 4
+		// #endif
+		ctx.setFontSize(fontSize);
+		const fontColor = this.fontColor || (uni && uni.$tui && uni.$tui.color.primary) || '#5677fc';
+		ctx.setFillStyle(fontColor);
+		ctx.setTextAlign('center');
+		ctx.setTextBaseline('middle');
+		let percentage = this.percentText;
+		if (!percentage) {
+			percentage = this.counterclockwise ? 100 - startPercentage * this.multiple : startPercentage * this
+				.multiple;
+			percentage = `${percentage}%`;
+		}
+		let radius = this.diam / 2;
+		// #ifdef MP-ALIPAY
+		radius = radius * 4
+		// #endif
+		ctx.fillText(percentage, radius, radius);
+	}
+	if (this.percentage == 0 || (this.counterclockwise && startPercentage == 100)) {
+		ctx.draw();
+	} else {
+		let eAngle = ((2 * Math.PI) / 100) * startPercentage + this.sAngle;
+		this.drawArc(ctx, eAngle);
+	}
+	setTimeout(() => {
+		this.startPercentage = startPercentage;
+		if (startPercentage >= this.percentage) {
+			this.$emit('end', {
+				canvasId: this.progressCanvasId,
+				percentage: startPercentage
+			});
+		} else {
+			this.drawProgressCircular(startPercentage);
+		}
+		this.$emit('change', {
+			percentage: startPercentage
+		});
+	}, time);
+	// #ifdef H5
+	// requestAnimationFrame(()=>{})
+	// #endif
+}
+//创建弧线
+const drawArc = (ctx, eAngle) => {
+	ctx.setLineCap(this.lineCap);
+	ctx.beginPath();
+	let radius = this.diam / 2; //x=y
+	let lineWidth = Number(this.lineWidth)
+	// #ifdef MP-ALIPAY
+	radius = radius * 4
+	lineWidth = lineWidth * 4
+	// #endif
+	ctx.arc(radius, radius, radius - lineWidth, this.sAngle, eAngle, this.counterclockwise);
+	ctx.stroke();
+	ctx.draw();
+}
+
+
+watch(
+	() => percentage.value,
+	() => {
+		this.initDraw();
+	}
+)
+
+onMounted(() => {
+	nextTick(() => {
+		setTimeout(() => {
+			this.initDraw(true);
+		}, 50)
+	})
+})
+</script>
+
+<style scoped>
+.tui-circular-container,
+.tui-circular-default {
+	position: relative;
+
+}
+
+/* #ifdef MP-ALIPAY */
+.tui-circular-default,
+.tui-circular-progress {
+	zoom: 0.25;
+}
+
+/* #endif */
+
+
+.tui-circular-progress {
+	position: absolute;
+	left: 0;
+	top: 0;
+	z-index: 10;
+}
+</style>

File diff suppressed because it is too large
+ 207 - 0
src/components/task-tree/index.vue


+ 380 - 0
src/components/task-tree/style.css

@@ -0,0 +1,380 @@
+/* @font-face {
+	font-family: 'iconfont';
+	src: url('//at.alicdn.com/t/c/font_4110624_qs48wckazsh.ttf?t=1712479573821') format('truetype');
+} */
+@keyframes spin {
+	0% { transform: rotate(0deg); }
+	100% { transform: rotate(360deg); }
+}
+
+.iconfont {
+	font-family: iconfont;
+	font-style: normal;
+}
+.iconfont-loading {
+	font-family: iconfont;
+	display: inline-block;
+	font-style: normal;
+	animation: spin 1s linear infinite;
+}
+.next-tree-mask {
+  position: fixed;
+  top: 0rpx;
+  right: 0rpx;
+  bottom: 0rpx;
+  left: 0rpx;
+  z-index: 997;
+  background-color: rgba(0, 0, 0, 0.6);
+  opacity: 0;
+  transition: all 0.3s ease;
+  visibility: hidden;
+}
+.next-tree-mask.show {
+  visibility: visible;
+  opacity: 1;
+}
+.next-tree-cnt {
+  position: fixed;
+  /* top: 0rpx; */
+  right: 10rpx;
+  bottom: 0rpx;
+  left: 10rpx;
+  z-index: 997;
+  /* top: 360rpx; */
+  transition: all 0.3s ease;
+  transform: translateY(100%);
+  
+}
+.next-tree-cnt.next-tree-cnt-page {
+	transition: none;
+}
+.next-tree-cnt.show {
+  transform: translateY(0);
+}
+.next-tree-bar {
+  background-color: #fff;
+  height: 72rpx;
+  padding-left: 20rpx;
+  padding-right: 20rpx;
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  box-sizing: border-box;
+  border-bottom-width: 1rpx !important;
+  border-bottom-style: solid;
+  border-bottom-color: #f5f5f5;
+  font-size: 32rpx;
+  color: #757575;
+  line-height: 1;
+}
+.next-tree-bar-btns {
+	display: inline-block;
+	display: flex;
+	flex-direction: row;
+}
+.btn-divid {
+	display: inline-block;
+	width: 1px;
+	margin: 0 10px;
+	background-color: #ccc;
+}
+.next-tree-bar-confirm {
+  color: #007aff;
+}
+.next-tree-view {
+  position: absolute;
+  top: 0rpx;
+  right: 0rpx;
+  bottom: 0rpx;
+  left: 0rpx;
+  top: 72rpx;
+  background-color: #fff;
+  padding-top: 10rpx;
+  /* padding-right: 20rpx; */
+  padding-bottom:  45px;
+  /* padding-left: 20rpx; */
+}
+.next-tree-view-sc {
+  /* height: calc(100% - 45px); */
+  overflow: hidden;
+  border-top: 1px #EBEEF5 solid;
+}
+.next-tree-view-sc .empty {
+	text-align: center;
+	color: #757575;
+	padding: 30rpx;
+}
+
+.next-tree-item {
+  display: flex;
+  flex: 1;
+  justify-content: space-between;
+  align-items: center;
+  font-size: 28rpx;
+  color: #757575;
+  line-height: 1;
+  height: 0;
+  opacity: 0;
+  transition: 0.2s;
+  position: relative;
+  overflow: hidden;
+}
+.next-tree-item-block{
+  padding: 0 20rpx;
+  display: flex;
+}
+.next-tree-item .icon-btn {
+	font-size: 30rpx;
+	margin-right: 20rpx;
+}
+.next-tree-item .left-line {
+	position: relative;
+	width: 1rpx;
+	height: 100%;
+	box-sizing: border-box;
+}
+.next-tree-item .left-line::before {
+	position: absolute;
+	content: "";
+	width: 1rpx;
+	height: 100%;
+	background-color: rgba(204,204,204,0.9);
+	box-sizing: border-box;
+	
+	left: -18rpx;
+}
+.next-tree-item .parent-horizontal-line {
+	width: 1rpx;
+	height: 100%;
+	position: absolute;
+	top: 0;
+	left: 0rpx;
+	box-sizing: border-box;
+	background-color: rgba(204,204,204,0.9);
+}
+.next-tree-item .left-line .horizontal-line {
+	width: 20rpx;
+	height: 1rpx;
+	position: absolute;
+	top: 40rpx;
+	left: 0rpx;
+	background-color: rgba(204,204,204,0.9);
+	box-sizing: border-box;
+}
+
+.next-tree-item.show {
+  height: 90rpx;
+  opacity: 1;
+}
+.next-tree-item.showchild:before {
+  transform: rotate(90deg);
+}
+.next-tree-item.border {
+  border-bottom: 1rpx solid rgba(204,204,204,0.2);
+}
+.next-tree-item.last:before {
+  opacity: 0;
+}
+.next-tree-item.disabled {
+  color: #ccc!important;
+}
+
+.next-tree-icon {
+  width: 26rpx;
+  height: 26rpx;
+  margin-right: 8rpx;
+  vertical-align: middle;
+}
+.next-tree-label {
+  align-items: center;
+  height: 100%;
+  line-height: 45px;
+  color: #323233;
+  flex-grow: 1;
+  white-space: nowrap;
+  overflow: hidden;
+  text-overflow: ellipsis;
+}
+.next-tree-label .label-input {
+	border: 1rpx solid #f0f0f0;
+	border-radius: 10rpx;
+	width: 100%;
+	padding: 12rpx 18rpx;
+	margin-right: 30rpx;
+}
+.next-tree-check {
+  width: 40px;
+  height: 40px;
+  display: flex;
+  justify-content: center;
+  align-items: center;
+}
+.next-tree-check-yes,
+.next-tree-check-no {
+  width: 20px;
+  height: 20px;
+  border-top-left-radius: 20%;
+  border-top-right-radius: 20%;
+  border-bottom-right-radius: 20%;
+  border-bottom-left-radius: 20%;
+  border-top-width: 1rpx;
+  border-left-width: 1rpx;
+  border-bottom-width: 1rpx;
+  border-right-width: 1rpx;
+  border-style: solid;
+  border-color: #007aff;
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  box-sizing: border-box;
+}
+.next-tree-check-yes-b {
+  border-top-left-radius: 20%;
+  border-top-right-radius: 20%;
+  border-bottom-right-radius: 20%;
+  border-bottom-left-radius: 20%;
+  background-color: #007aff;
+	color: #fff;
+}
+.next-tree-check-yes-b .icon-text {
+	font-size: 14px;
+	font-weight: normal;
+	font-family: uicon-iconfont;
+	display: flex;
+	flex-direction: row;
+	align-items: center;
+}
+.next-tree-check .radio {
+  border-top-left-radius: 50%;
+  border-top-right-radius: 50%;
+  border-bottom-right-radius: 50%;
+  border-bottom-left-radius: 50%;
+}
+.next-tree-check .radio .next-tree-check-yes-b {
+  border-top-left-radius: 50%;
+  border-top-right-radius: 50%;
+  border-bottom-right-radius: 50%;
+  border-bottom-left-radius: 50%;
+}
+
+.next-tree-item.disabled .next-tree-check-no {
+	color: #ccc!important;
+}
+.next-tree-item.disabled .next-tree-check-yes-b {
+	background-color: #ccc!important;
+}
+.hover-c {
+  opacity: 0.6;
+}
+
+.fixed-bottom-bar {
+	position: fixed;
+	bottom: 0rpx;
+	left: 10px;
+	right: 10px;
+	z-index: 998;
+}
+
+
+
+@keyframes in {
+  0% {
+    transform: translateX(-120%);
+  }
+  100% {
+    transform: translateX(0%);
+  }
+}
+
+@keyframes out {
+  0% {
+    transform: translateX(120%);
+  }
+
+  100% {
+    transform: translateX(0%);
+  }
+}
+.slide-in {
+  animation: in 0.5s;
+}
+.slide-out {
+  animation: out 0.5s;
+}
+
+.ribbon-wrapper {
+  width: 71px;
+  height: 51px;
+  overflow: hidden;
+  position: absolute;
+  /* top: -9px; */
+  right: -26px;
+}
+
+.ribbon {
+  font: bold 15px Sans-Serif;
+  line-height: 10px;
+  font-size: 10px;
+  text-align: center;
+  text-transform: uppercase;
+  -webkit-transform: rotate(45deg);
+  -moz-transform: rotate(45deg);
+  -ms-transform: rotate(45deg);
+  -o-transform: rotate(45deg);
+  position: relative;
+  padding: 3px 0;
+  left: 10px;
+  top: 6px;
+  color: #fff;
+  box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
+  letter-spacing: 0.5px;
+  box-shadow: -3px 5px 6px -5px rgba(0, 0, 0, 0.5);
+}
+
+.ribbon:before,
+.ribbon:after {
+  content: '';
+  border-left: 4px solid transparent;
+  border-right: 4px solid transparent;
+  position: absolute;
+  bottom: -4px;
+}
+
+.ribbon:before {
+  content: '';
+  position: absolute;
+  left: 0px;
+  top: 100%;
+}
+
+.ribbon:after {
+  content: '';
+  position: absolute;
+  right: 0px;
+  top: 100%;
+  z-index: -1;
+}
+
+.ribbon-add {
+  background-color: #4cd964;
+}
+
+.ribbon-modify {
+  background-color: #f0ad4e;
+}
+
+.ribbon-remove {
+  background-color: #dd524d;
+}
+
+.ribbon-sub-add {
+  background-color: #aff0b5;
+}
+
+.ribbon-sub-modify {
+  background-color: #fff7e8;
+}
+
+.ribbon-sub-remove {
+  background-color: #ffece8;
+}

File diff suppressed because it is too large
+ 187 - 0
src/components/tree-select/index.vue


+ 269 - 0
src/components/tree-select/style.css

@@ -0,0 +1,269 @@
+/* @font-face {
+	font-family: 'iconfont';
+	src: url('//at.alicdn.com/t/c/font_4110624_qs48wckazsh.ttf?t=1712479573821') format('truetype');
+} */
+@keyframes spin {
+	0% { transform: rotate(0deg); }
+	100% { transform: rotate(360deg); }
+}
+
+.iconfont {
+	font-family: iconfont;
+	font-style: normal;
+}
+.iconfont-loading {
+	font-family: iconfont;
+	display: inline-block;
+	font-style: normal;
+	animation: spin 1s linear infinite;
+}
+.next-tree-mask {
+  position: fixed;
+  top: 0rpx;
+  right: 0rpx;
+  bottom: 0rpx;
+  left: 0rpx;
+  z-index: 997;
+  background-color: rgba(0, 0, 0, 0.6);
+  opacity: 0;
+  transition: all 0.3s ease;
+  visibility: hidden;
+}
+.next-tree-mask.show {
+  visibility: visible;
+  opacity: 1;
+}
+.next-tree-cnt {
+  position: fixed;
+  top: 0rpx;
+  right: 0rpx;
+  bottom: 0rpx;
+  left: 0rpx;
+  z-index: 997;
+  top: 360rpx;
+  transition: all 0.3s ease;
+  transform: translateY(100%);
+}
+.next-tree-cnt.next-tree-cnt-page {
+	transition: none;
+}
+.next-tree-cnt.show {
+  transform: translateY(0);
+}
+.next-tree-bar {
+  background-color: #fff;
+  height: 72rpx;
+  padding-left: 20rpx;
+  padding-right: 20rpx;
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  box-sizing: border-box;
+  border-bottom-width: 1rpx !important;
+  border-bottom-style: solid;
+  border-bottom-color: #f5f5f5;
+  font-size: 32rpx;
+  color: #757575;
+  line-height: 1;
+}
+.next-tree-bar-btns {
+	display: inline-block;
+	display: flex;
+	flex-direction: row;
+}
+.btn-divid {
+	display: inline-block;
+	width: 1px;
+	margin: 0 10px;
+	background-color: #ccc;
+}
+.next-tree-bar-confirm {
+  color: #007aff;
+}
+.next-tree-view {
+  position: absolute;
+  top: 0rpx;
+  right: 0rpx;
+  bottom: 0rpx;
+  left: 0rpx;
+  top: 72rpx;
+  background-color: #fff;
+  padding-top: 20rpx;
+  padding-right: 20rpx;
+  padding-bottom: 20rpx;
+  padding-left: 20rpx;
+}
+.next-tree-view-sc {
+  height: 100%;
+  overflow: hidden;
+}
+.next-tree-view-sc .empty {
+	text-align: center;
+	color: #757575;
+	padding: 30rpx;
+}
+.next-tree-item-block {
+	
+}
+.next-tree-item {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  font-size: 26rpx;
+  color: #757575;
+  line-height: 1;
+  height: 0;
+  opacity: 0;
+  transition: 0.2s;
+  position: relative;
+  overflow: hidden;
+}
+.next-tree-item .icon-btn {
+	font-size: 30rpx;
+	margin-right: 20rpx;
+}
+.next-tree-item .left-line {
+	position: relative;
+	width: 1rpx;
+	height: 100%;
+	box-sizing: border-box;
+}
+.next-tree-item .left-line::before {
+	position: absolute;
+	content: "";
+	width: 1rpx;
+	height: 100%;
+	background-color: rgba(204,204,204,0.9);
+	box-sizing: border-box;
+	
+	left: -18rpx;
+}
+.next-tree-item .parent-horizontal-line {
+	width: 1rpx;
+	height: 100%;
+	position: absolute;
+	top: 0;
+	left: 0rpx;
+	box-sizing: border-box;
+	background-color: rgba(204,204,204,0.9);
+}
+.next-tree-item .left-line .horizontal-line {
+	width: 20rpx;
+	height: 1rpx;
+	position: absolute;
+	top: 40rpx;
+	left: 0rpx;
+	background-color: rgba(204,204,204,0.9);
+	box-sizing: border-box;
+}
+
+.next-tree-item.show {
+  height: 80rpx;
+  opacity: 1;
+}
+.next-tree-item.showchild:before {
+  transform: rotate(90deg);
+}
+.next-tree-item.border {
+  border-bottom: 1rpx solid rgba(204,204,204,0.2);
+}
+.next-tree-item.last:before {
+  opacity: 0;
+}
+.next-tree-item.disabled {
+  color: #ccc!important;
+}
+
+.next-tree-icon {
+  width: 26rpx;
+  height: 26rpx;
+  margin-right: 8rpx;
+}
+.next-tree-label {
+  flex: 1;
+  display: flex;
+  align-items: center;
+  height: 100%;
+  line-height: 1.2;
+}
+.next-tree-label .label-input {
+	border: 1rpx solid #f0f0f0;
+	border-radius: 10rpx;
+	width: 100%;
+	padding: 12rpx 18rpx;
+	margin-right: 30rpx;
+}
+.next-tree-check {
+  width: 40px;
+  height: 40px;
+  display: flex;
+  justify-content: center;
+  align-items: center;
+}
+.next-tree-check-yes,
+.next-tree-check-no {
+  width: 20px;
+  height: 20px;
+  border-top-left-radius: 20%;
+  border-top-right-radius: 20%;
+  border-bottom-right-radius: 20%;
+  border-bottom-left-radius: 20%;
+  border-top-width: 1rpx;
+  border-left-width: 1rpx;
+  border-bottom-width: 1rpx;
+  border-right-width: 1rpx;
+  border-style: solid;
+  border-color: #007aff;
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  box-sizing: border-box;
+}
+.next-tree-check-yes-b {
+  border-top-left-radius: 20%;
+  border-top-right-radius: 20%;
+  border-bottom-right-radius: 20%;
+  border-bottom-left-radius: 20%;
+  background-color: #007aff;
+	color: #fff;
+}
+.next-tree-check-yes-b .icon-text {
+	font-size: 14px;
+	font-weight: normal;
+	font-family: uicon-iconfont;
+	display: flex;
+	flex-direction: row;
+	align-items: center;
+}
+.next-tree-check .radio {
+  border-top-left-radius: 50%;
+  border-top-right-radius: 50%;
+  border-bottom-right-radius: 50%;
+  border-bottom-left-radius: 50%;
+}
+.next-tree-check .radio .next-tree-check-yes-b {
+  border-top-left-radius: 50%;
+  border-top-right-radius: 50%;
+  border-bottom-right-radius: 50%;
+  border-bottom-left-radius: 50%;
+}
+
+.next-tree-item.disabled .next-tree-check-no {
+	color: #ccc!important;
+}
+.next-tree-item.disabled .next-tree-check-yes-b {
+	background-color: #ccc!important;
+}
+.hover-c {
+  opacity: 0.6;
+}
+
+.fixed-bottom-bar {
+	position: fixed;
+	bottom: 0rpx;
+	left: 0rpx;
+	right: 0rpx;
+	z-index: 998;
+}
+
+

+ 8 - 0
src/config/iconfont.config.ts

@@ -0,0 +1,8 @@
+
+module.exports = {
+    iconfontUrl: '//at.alicdn.com/t/c/font_4466254_26phpwtoaxj.js', // 矢量图标库Symbol地址
+    dirName: 'iconfont', // 需要生成的css对应文件夹
+    fileName: 'my', // css文件名称
+    icon: 'my', // Font Family
+    fontSize: '14px', // 默认大小
+}

+ 720 - 0
src/enums/api.ts

@@ -0,0 +1,720 @@
+
+export enum Client {
+  LOGIN = 'api/mp/login', // 登录
+  AUTHORIZE_MOBILE = 'api/mp/post_userinfo', // 登录
+  GET_PERMISSIONS = 'api/client/get_permissions', // 获取所有用户有权限调用的api集合
+  GET_SELF_INFO = 'api/client/get_self_info', // 获取当前登录个人信息
+  SET_SELF_INFO = 'api/client/set_self_info', // 设置当前登录个人信息
+  GET_IMAGE_VODE = 'api/client/get_vcode', // 获取图片验证码
+  INFO_DOMAIN_TREE = 'api/client/get_self_domains'
+}
+
+export enum Application {
+  SYSTEM_APPLICATION_ADD = 'api/acs.application.add', // 添加应用
+  SYSTEM_APPLICATION_MODIFY = 'api/acs.application.modify', // 修改应用
+  SYSTEM_APPLICATION_LIST = 'api/acs.application.get_list', // 获取应用列表
+  SYSTEM_APPLICATION_REMOVE = 'api/acs.application.remove', // 删除应用
+  SYSTEM_APPLICATION_DETAIL = 'api/acs.application.detail' // 获取应用详情
+}
+
+export enum Domain {
+  SYSTEM_DOMAIN_ADD = 'api/acs.domain.add', // 添加功能模块
+  SYSTEM_DOMAIN_MODIFY = 'api/acs.domain.modify', // 修改功能模块
+  SYSTEM_DOMAIN_REMOVE = 'api/acs.domain.remove', // 删除功能模块
+  SYSTEM_DOMAIN_TREE = 'api/acs.domain.get_tree' // 获取功能模块树
+}
+
+export enum Function {
+  SYSTEM_FUNC_ADD = 'api/acs.function.add', // 添加功能项
+  SYSTEM_FUNC_MODIFY = 'api/acs.function.modify', // 修改功能项
+  SYSTEM_FUNC_REMOVE = 'api/acs.function.remove', // 删除功能项
+  SYSTEM_FUNC_LIST = 'api/acs.function.get_list' // 获取功能项列表
+}
+
+export enum Module {
+  SYSTEM_MODULE_ADD = 'api/acs.module.add', // 添加功能模块
+  SYSTEM_MODULE_MODIFY = 'api/acs.module.modify', // 修改功能模块
+  SYSTEM_MODULE_REMOVE = 'api/acs.module.remove', // 删除功能模块
+  SYSTEM_MODULE_TREE = 'api/acs.module.get_tree' // 获取功能模块树
+}
+
+export enum Role {
+  SYSTEM_ROLE_ADD = 'api/acs.role.add', // 添加角色
+  SYSTEM_ROLE_MODIFY = 'api/acs.role.modify', // 修改角色
+  SYSTEM_ROLE_LIST = 'api/acs.role.get_list', // 获取角色列表
+  SYSTEM_ROLE_REMOVE = 'api/acs.role.remove', // 删除角色
+  SYSTEM_ROLE_SET_F = 'api/acs.role.set_funcs', // 未角色设置功能项
+  SYSTEM_ROLE_GET_F = 'api/acs.role.get_funcs', // 获取角色已有功能项
+  SYSTEM_ROLE_GET_GROUP_TREE = 'api/acs.role.get_func_group_tree' // 获取功能项分组树
+}
+
+export enum User {
+  SYSTEM_USER_ADD = 'api/acs.user.add', // 添加用户
+  SYSTEM_USER_MODIFY = 'api/acs.user.modify', // 修改用户
+  SYSTEM_USER_REMOVE = 'api/acs.user.remove', // 删除用户
+  SYSTEM_USER_LIST = 'api/acs.user.get_list', // 获取用户列表
+  SYSTEM_USER_GET_ROLES = 'api/acs.user.get_roles', // 获取用户可设置角色
+  SYSTEM_USER_SET_ROLES = 'api/acs.user.set_roles', // 设置用户角色
+  SYSTEM_USER_GET_DOMAINS = 'api/acs.user.get_domains', // 获取账号的分组列表
+  SYSTEM_USER_SET_DOMAINS = 'api/acs.user.set_domains' // 设置账号所能访问的分组
+}
+
+
+// 项目接口
+export enum Project {
+  PROJECT_INFO_LIST = 'api/prj.info.get_list', // 获取项目列表
+  PROJECT_INFO_ADD = 'api/prj.info.add', // 创建项目
+  PROJECT_INFO_REMOVE = 'api/prj.info.remove', // 删除项目
+  PROJECT_INFO_MODIFY = 'api/prj.info.modify', // 修改项目
+  PROJECT_INFO_OVERVIEW = 'api/prj.info.overview', // 项目预览
+  PROJECT_INFO_LOGS = 'api/prj.info.get_logs', // 获取项目动态列表
+  PROJECT_FLOW_APPLY = 'api/prj.flow.apply', // 申请立项
+  PROJECT_FLOW_CHECK_START = 'api/prj.flow.check_start', // 审核立项
+  PROJECT_MEMBER_LIST = 'api/prj.member.get_list', // 获取项目组成员列表
+  PROJECT_MEMBER_ADD = 'api/prj.member.add', // 创建项目组成员
+  PROJECT_MEMBER_REMOVE = 'api/prj.member.remove', // 删除项目组成员
+  PROJECT_MEMBER_MODIFY = 'api/prj.member.modify', // 修改项目组成员
+  PROJECT_MEMBER_APPLY_ADD = 'api/prj.member.apply_add', // 删除项目中的项目组成员。立项申请后,项目组成员不允许删除,如需要删除,需撤回申请后删除,或在审核通过后再使用申请删除项目组成员接口。
+  PROJECT_MEMBER_APPLY_REMOVE = 'api/prj.member.apply_remove', // 立项后,需要移除项目组成员时,使用此接口申请,在审核通过后生效。
+  PROJECT_MEMBER_CHECK = 'api/prj.member.check', // 申请添加或移除项目组成员后,审核是否通过。
+  PROJECT_PLAN_TASK_LIST = 'api/prj.plan.get_tasks', // 获取项目计划,计划中的任务为两级树状结构。计划有正本和副本之分,新建和正在执行的任务计划均为正本,对于审核通过后正在执行的计划,如需要变更,则在副本中修改,副本中的任何修改不影响正本的内容。当副本提高变更申请,并审核通过后,副本的内容将同步到正本中去。
+  PROJECT_PLAN_TASK_ADD = 'api/prj.plan.add_task', // 添加项目计划任务项,包括一级任务和二级子任务。仅在任务计划处于新建状态时才允许直接添加,否则需要在副本中添加后,提交审核并通过后才生效
+  PROJECT_PLAN_TASK_REMOVE = 'api/prj.plan.remove_task', // 删除任务项,仅在任务计划处于新建状态时才允许直接删除。否则需要在草稿中删除,并且仅允许删除状态为未开始的任务,提交审核并通过后才正式生效。
+  PROJECT_PLAN_TASK_MODIFY = 'api/prj.plan.modify_task', // 修改任务项,仅在任务计划处于新建状态时才允许直接修改。否则需要在副本中修改,提交审核并通过后才正式生效,如果任务项已执行完成,不允许修改。
+  PROJECT_PLAN_DETAIL = 'api/prj.plan.detail', // 获取项目任务计划完整信息。与任务计划清单不同的是,清单中只包含任务项的信息,而完整信息包括了任务项的交付物以及交付物的审核流程。
+  PROJECT_PLAN_TASK_DETAIL = 'api/prj.plan.task_detail', // 获取指定任务项详情
+  PROJECT_PLAN_TASK_SORT = 'api/prj.plan.sort_task', // 调整任务项的顺序,仅在任务计划处于新建状态时才允许直接调整。否则需要在副本中修改,提交审核并通过后才正式生效。
+  PROJECT_PLAN_TASK_CANCEL = 'api/prj.plan.cancel_task', // 项目在新建状态时,可直接删除不需要的任务。已完成的任务项不允许删除。计划通过审核开始执行后,不管任务是否已开始执行,都不允许删除,不再执行的任务,需要申请中止任务,经审核通过后,可中止。中止任务项的申请直接在正本中进行。
+  PROJECT_PLAN_TASK_LOGS = 'api/prj.plan.task_logs', // 获取项目计划中某个任务的执行动态,即日志。
+  PROJECT_PLAN_SET_PROGRESS = 'api/prj.plan.set_progress',
+  PROJECT_CONTRACT_LIST = 'api/prj.contract.get_list', // 获取所有项目的合同列表,可条件筛选。
+  PROJECT_CONTRACT_ADD = 'api/prj.contract.add', // 新录入合同记录,新建合同可以在合同集中管理界面中新建,也可以在项目工作台的合同管理中新建。
+  PROJECT_CONTRACT_REMOVE = 'api/prj.contract.remove', // 删除合同
+  PROJECT_CONTRACT_MODIFY = 'api/prj.contract.modify', // 修改合同内容
+  PROJECT_CONTRACT_DETAIL = 'api/prj.contract.detail', // 获取合同详情。
+  PROJECT_CONTRACT_UPLOAD_DOC = 'api/prj.contract.upload_doc', // 获取上传合同附件的url。
+  PROJECT_CONTRACT_DOWNLOAD_DOC = 'api/prj.contract.download_doc', // 获取下载合同附件的url。
+  PROJECT_CONTRACT_REMOVE_DOC = 'api/prj.contract.remove_doc', // 用于删除已上传的合同附件。已归档的附件不允许修改
+  PROJECT_WEEK_REPORT_LIST = 'api/prj.week_report.get_list', // 获取项目周报列表
+  PROJECT_WEEK_REPORT_ADD = 'api/prj.week_report.add', // 创建项目周报
+  PROJECT_WEEK_REPORT_REMOVE = 'api/prj.week_report.remove', // 删除项目周报
+  PROJECT_WEEK_REPORT_MODIFY = 'api/prj.week_report.modify', // 修改项目周报
+  PROJECT_WEEK_REPORT_DETAIL = 'api/prj.week_report.detail', // 查看项目周报详情
+  PROJECT_WEEK_REPORT_COMMIT = 'api/prj.week_report.commit', // 提交项目周报
+  PROJECT_WEEK_REPORT_REMOVE_FILE = 'api/prj.week_report.remove_file', // 提交项目周报
+
+  PROJECT_WEEK_REPORT_WITHDRAW = 'api/prj.week_report.withdraw', // 撤回提交项目周报
+  PROJECT_WEEK_REPORT_UPLOAD_FILE = 'api/prj.week_report.upload_file', // 获取上传周报附件的url。
+  PROJECT_WEEK_REPORT_DOWNLOAD_FILE = 'api/prj.week_report.download_file', // 获取下载周报附件的url。
+  PROJECT_OUTCOME_LIST = 'api/prj.outcome.get_list', // 获取项目计划中某一任务的交付物清单。与任务一样,也有正副本之分。交付物清单以预期交付时间作正向排序。
+  PROJECT_OUTCOME_DETAIL = 'api/prj.outcome.detail', // 获取指定任务项中指定交付的详情。
+  PROJECT_OUTCOME_ADD = 'api/prj.outcome.add', //  添加任务项中的交付物。仅在任务计划处于新建状态时才允许直接添加,否则需要在草稿中添加后,提交审核并通过后才生效。
+  PROJECT_OUTCOME_MODIFY = 'api/prj.outcome.modify', // 修改任务项交付物。仅在任务计划处于新建状态时才允许直接修改。否则需要在草稿中修改,提交审核并通过后才正式生效。
+  PROJECT_OUTCOME_REMOVE = 'api/prj.outcome.remove', // 删除任务项交付物,仅在任务计划处于新建状态时才允许直接删除。否则需要在草稿中删除,并且仅允许删除状态为未交付的交付物,提交审核并通过后才正式生效。
+  PROJECT_OUTCOME_UPLOAD = 'api/prj.outcome.upload', // 获取上传交付物的url。上传成功后,如果该交付物需要审核,后台会将该交付物提交给相关审核人,否则上传成功后状态将置为已交付。
+  PROJECT_OUTCOME_DOWNLOAD = 'api/prj.outcome.download', // 获取上传交付物的url。上传成功后,如果该交付物需要审核,后台会将该交付物提交给相关审核人,否则上传成功后状态将置为已交付。
+  PROJECT_OUTCOME_REMOVE_FILE = 'api/prj.outcome.remove_file',
+  PROJECT_APPLY_START = 'api/prj.apply.start', // 用户在创建项目后,申请立项
+  PROJECT_APPLY_NEW_PLAN = 'api/prj.apply.new_plan', // 立项后,项目负责人编写任务计划,由审核人审核。 
+  PROJECT_APPLY_MODIFY_PLAN = 'api/prj.apply.modify_plan', // 审核通过的任务计划,需要在副本中变更,并提交审核通过后方能同步到正本中执行。 
+  PROJECT_APPLY_CANCEL = 'api/prj.apply.cancel', // 任务计划的正本或副本在提交审核后,在未审核时,可撤回申请继续修改
+  PROJECT_DOC_LIST = 'api/prj.doc.get_list', // 获取项目中的文档列表
+  PROJECT_DOC_UPLOAD = 'api/prj.doc.upload', // 上传项目文档
+  PROJECT_DOC_DOWNLOAD = 'api/prj.doc.download', // 下载项目文档
+  PROJECT_DOC_REOMOVE = 'api/prj.doc.remove', // 删除项目文档
+  PROJECT_DOC_ARCHIVE = 'api/prj.doc.archive',
+  PROJECT_DOC_COPY = 'api/prj.doc.copy',
+  PROJECT_DOC_RENAME = 'api/prj.doc.rename',
+  PROJECT_DOC_DETAIL = 'api/prj.doc.detail',
+  PROJECT_KANBAN_MILESTONE = 'api/prj.kanban.milestone', // 获取项目里程碑及进度
+  PROJECT_INFO_GET_CHECKERS = 'api/prj.info.get_checkers', // 获取项目审核人列表
+  PROJECT_INFO_SET_CHECKERS = 'api/prj.info.set_checkers', // 设置项目审核人列表
+  PROJECT_WORK_LIST = 'api/prj.work.get_list', // 获取我的任务列表
+  PROJECT_WORK_DETAIL = 'api/prj.work.detail', // 获取任务详情
+  PROJECT_WORK_SUBMIT = 'api/prj.work.submit', // 提交任务
+  PROJECT_WORK_UPLOAD_FILE = 'api/prj.work.upload_file', // 上传工作项附件
+  PROJECT_WORK_DOWNLOAD_FILE = 'api/prj.work.download_file', // 上传工作项附件
+  PROJECT_WORK_UNDONE_COUNT = 'api/prj.work.undone_count',
+  PROJECT_WORK_GET_CHAIN = 'api/prj.work.get_chain', // 获取工作项链
+
+  PROJECT_PLAN_GET_CHECK_FLOW = 'api/prj.plan.get_check_flow', // 获取审核流程
+  PROJECT_PLAN_SET_CHECK_FLOW = 'api/prj.plan.set_check_flow', // 设置审核流程
+  PROJECT_INFO_STAT = "api/prj.info.stat", // 获取项目统计信息
+  PROJECT_WORK_STAT = 'api/prj.work.stat', // 获取工作统计信息
+  PROJECT_WORK_DONE_STAT = 'api/prj.work.done_stat', // 获取已工作统计信息
+  PROJECT_WORK_UNDONE_STAT = 'api/prj.work.undone_stat', // 获取未工作统计信息
+  PROJECT_CONTRACT_STAT = 'api/prj.contract.stat', // 获取合同统计信息
+  PROJECT_PLAN_LOGS = 'api/prj.plan.get_logs' // 获取项目计划任务动态
+}
+
+
+// 审核
+export enum Review {
+  REVIEW_TICKET_LIST = 'api/review.ticket.get_list', // 审核人获取自己针对某个审核申请的整改工单列表。
+  REVIEW_TICKET_ADD = 'api/review.ticket.add', // 针对某个审核申请内容,创建一条新的整改工单。
+  REVIEW_TICKET_MODIFY = 'api/review.ticket.modify', // 修改整改工单内容。审核完成后,工单内容不允许修改,但可作废。
+  REVIEW_TICKET_REMOVE = 'api/review.ticket.remove', // 未完成审核前,可删除整改工单。
+  REVIEW_TICKET_TRANSFER = 'api/review.ticket.transfer', // 1. 某些工单在审核人分配的时候,会分配给主责人,需要由主责人再分配给具体负责人。 2. 在分配具体负责人时不准确,此时可以由当前负责人再次将工作单移交给其它项目组成员。 3. 审核人提交的整改意见本身有问题,负责人打回给审核人,审核人可重新编写说明后流转给负责人,也可以作废工单。
+  REVIEW_TICKET_CANCEL = 'api/review.ticket.cancel', // 审核完成后,审核人可废除整改工单,但不能删除。
+  REVIEW_TICKET_CONFIRM = 'api/prj.flow_start.review' // 最终确定审核是否通过。
+}
+
+
+
+//客户
+export enum Customer {
+  BIZ_CUSTOMER_LIST = 'api/biz.customer.get_list',
+  BIZ_CUSTOMER_ADD = 'api/biz.customer.add',
+  BIZ_CUSTOMER_MODIFY = 'api/biz.customer.modify',
+  BIZ_CUSTOMER_REMOVE = 'api/biz.customer.remove',
+  BIZ_CUSTOMER_DETAIL = 'api/biz.customer.detail'
+}
+
+
+export enum Base {
+  CFG_PRJ_TYPE_TREE = 'api/cfg.prj_type.get_tree',
+  CFG_INDUSTRY_TYPE_LIST = 'api/cfg.industry_type.get_list',
+  CFG_CUSTOMER_LEVEL_LIST = 'api/cfg.customer_level.get_list',
+  CFG_PRJ_PHASE_GET_LIST = 'api/cfg.prj_phase.get_list',
+  CFG_PRJ_ROLE_GET_LIST = 'api/cfg.prj_role.get_list',
+  CFG_STAFF_LIST = 'api/cfg.staff.get_list',
+  CFG_STAFF_ADD = 'api/cfg.staff.add',
+  CFG_STAFF_MODIFY = 'api/cfg.staff.modify',
+  CFG_STAFF_REMOVE = 'api/cfg.staff.remove',
+  CFG_DOC_TYPE_LIST = 'api/cfg.doc_type.get_list',
+  CFG_ORG_TREE = 'api/cfg.org.get_tree',
+  CFG_ORG_ADD = 'api/cfg.org.add',
+  CFG_ORG_MODIFY = 'api/cfg.org.modify',
+  CFG_ORG_REMOVE = 'api/cfg.org.remove',
+  CFG_REGION_TREE = 'api/cfg.region.get_tree',
+  CFG_REGION_ADD = 'api/cfg.region.add',
+  CFG_REGION_MODIFY = 'api/cfg.region.modify',
+  CFG_REGION_REMOVE = 'api/cfg.region.remove',
+}
+
+// 审核
+export enum Dashboard {
+  REPORT_PRJOJECT_CAT_STAT = 'api/report.prj.cat_stat', // 获取项目分类统计
+  REPORT_PRJOJECT_PLAN_STAT = 'api/report.prj.task_stat', // 获取项目计划统计
+  REPORT_PRJOJECT_COUNT_STAT = 'api/report.prj.count', // 获取项目计划统计
+  REPORT_PRJOJECT_MULTI_STAT = 'api/report.prj.multi_stat'
+}
+
+
+// 登录/注册page
+export enum PageEnum {
+  LOGIN_PATH = '/passport/login', // 登录路径
+  REGISTER_PATH = '/passport/register' // 注册路径
+}
+
+// 项目阶段
+export enum PhaseItem {
+  NEW = 'new',
+  APPLY_CREATE = 'apply_create',
+  CREATED = 'created',
+  REJECT_CREATE = 'reject_create',
+  OVERVIEW_CREATE = 'overview_create',
+  APPLY_PLAN = 'apply_plan',
+  OVERVIEW_PLAN = 'overview_plan',
+  APPLY_PLAN_ALT = 'apply_plan_alt',
+  REJECT_PLAN = 'reject_plan',
+  REJECT_PLAN_ALT = 'reject_plan_alt',
+  DOING = 'doing',
+  CHECKING = 'checking',
+  DONE = 'done',
+  DEPRECATED = 'deprecated'
+}
+
+// 项目阶段
+export enum PhaseTitle {
+  NEW = '新建',
+  APPLY_CREATE = '申请立项中',
+  OVERVIEW_CREATE = '立项审核中',
+  CREATED = '已立项',
+  REJECT_CREATE = '立项被驳回',
+  APPLY_PLAN = '计划已提交',
+  OVERVIEW_PLAN = '计划审核中',
+  APPLY_PLAN_ALT = '计划变更已提交',
+  REJECT_PLAN = '计划被驳回',
+  REJECT_PLAN_ALT = '计划变更被驳回',
+  DOING = '执行中',
+  CHECKING = '验收中',
+  DONE = '已完成',
+  DEPRECATED = '已作废'
+}
+
+
+export const PhaseList: { id: PhaseItem, name: PhaseTitle, color: string }[] = [
+  {
+    id: PhaseItem.NEW,
+    name: PhaseTitle.NEW,
+    color: 'pinkpurple'
+  },
+  {
+    id: PhaseItem.APPLY_CREATE,
+    name: PhaseTitle.APPLY_CREATE,
+    color: 'gold'
+  },
+  {
+    id: PhaseItem.CREATED,
+    name: PhaseTitle.CREATED,
+    color: 'cyan'
+  },
+  {
+    id: PhaseItem.REJECT_CREATE,
+    name: PhaseTitle.REJECT_CREATE,
+    color: 'red'
+  },
+  {
+    id: PhaseItem.APPLY_PLAN,
+    name: PhaseTitle.APPLY_PLAN,
+    color: 'green'
+  },
+  {
+    id: PhaseItem.OVERVIEW_PLAN,
+    name: PhaseTitle.OVERVIEW_PLAN,
+    color: 'arcoblue'
+  },
+  {
+    id: PhaseItem.APPLY_PLAN_ALT,
+    name: PhaseTitle.APPLY_PLAN_ALT,
+    color: 'green'
+  },
+  {
+    id: PhaseItem.REJECT_PLAN,
+    name: PhaseTitle.REJECT_PLAN,
+    color: 'orange'
+  },
+  {
+    id: PhaseItem.REJECT_PLAN_ALT,
+    name: PhaseTitle.REJECT_PLAN_ALT,
+    color: 'red'
+  },
+  {
+    id: PhaseItem.DOING,
+    name: PhaseTitle.DOING,
+    color: 'arcoblue'
+  },
+  {
+    id: PhaseItem.DEPRECATED,
+    name: PhaseTitle.DEPRECATED,
+    color: 'gray'
+  }
+]
+
+
+
+// 项目工作台tab数组
+export enum ProjectDetailTabTitle {
+  OVERVIEW = '项目概览',
+  TASK = '项目计划',
+  PROGRESSING = '进度看板',
+  CONTRACT = '项目合同',
+  REPORT = '项目周报',
+  DOCUMENT = '项目文档'
+}
+
+export enum ProjectDetailTabPath {
+  OVERVIEW = 'overview',
+  TASK = 'plan-task',
+  PROGRESSING = 'progressing',
+  CONTRACT = 'contract',
+  REPORT = 'report',
+  DOCUMENT = 'document'
+}
+
+export const ProjectDetailTabTitleList: { title: ProjectDetailTabTitle, path: ProjectDetailTabPath, api: string[], redirectId?: string[], routerName: string }[] = [
+  {
+    title: ProjectDetailTabTitle.OVERVIEW,
+    path: ProjectDetailTabPath.OVERVIEW,
+    api: [Project.PROJECT_INFO_OVERVIEW],
+    redirectId: ['project-detail'],
+    routerName: 'ProjectDetail'
+  },
+  {
+    title: ProjectDetailTabTitle.TASK,
+    path: ProjectDetailTabPath.TASK,
+    api: [Project.PROJECT_PLAN_TASK_LIST],
+    redirectId: ['project-plan', 'project-plan-draft', 'project-outcome'],
+    routerName: 'ProjectPlan'
+  },
+  {
+    title: ProjectDetailTabTitle.PROGRESSING,
+    path: ProjectDetailTabPath.PROGRESSING,
+    api: [Project.PROJECT_PLAN_TASK_LIST],
+    routerName: 'ProjectProgressing'
+  },
+  {
+    title: ProjectDetailTabTitle.CONTRACT,
+    path: ProjectDetailTabPath.CONTRACT,
+    api: [Project.PROJECT_CONTRACT_LIST],
+    routerName: 'ProjectContract'
+  },
+  {
+    title: ProjectDetailTabTitle.REPORT,
+    path: ProjectDetailTabPath.REPORT,
+    api: [Project.PROJECT_WEEK_REPORT_LIST],
+    redirectId: ['project-week-report'],
+    routerName: 'ProjectWeekReport'
+  },
+  {
+    title: ProjectDetailTabTitle.DOCUMENT,
+    path: ProjectDetailTabPath.DOCUMENT,
+    api: [Project.PROJECT_DOC_LIST],
+    routerName: 'ProjectDocument'
+  }
+]
+
+export enum OperateTypeItem {
+  UNDEFINED = 'undefined',
+  R = 'r',
+  W = 'w',
+  RW = 'rw'
+}
+
+
+// 客户级别项
+export enum CustomerLevel {
+  VIP = 'VIP',
+  A = 'A',
+  B = 'B',
+  C = 'C'
+}
+
+// 客户级别列表
+export const CustomerLevelList = [
+  {
+    name: 'VIP',
+    id: CustomerLevel.VIP,
+  },
+  {
+    name: 'A级',
+    id: CustomerLevel.A,
+  },
+  {
+    name: 'B级',
+    id: CustomerLevel.B,
+  }, {
+    name: 'C级',
+    id: CustomerLevel.C
+  }
+]
+
+// 周报状态项
+export enum ReportStatusItem {
+  DRAFTING = 10,
+  SUMITTING = 20,
+  WITHDRAWING = 30,
+  REJECTING = 40,
+  REVIEWED = 100
+}
+// 合同文档类型
+export enum ContractTypeItem {
+  DOC = 10, // 打印件
+  PDF = 20 // 扫描件
+}
+
+// 周报状态列表
+export const ReportStatusList: { label: string, value: number, tagProps: any }[] = [
+  {
+    value: ReportStatusItem.DRAFTING,
+    label: '起草',
+    tagProps: {
+      color: undefined
+    }
+  },
+  {
+    value: ReportStatusItem.SUMITTING,
+    label: '提交',
+    tagProps: {
+      color: 'arcoblue'
+    }
+  },
+  {
+    value: ReportStatusItem.WITHDRAWING,
+    label: '撤回',
+    tagProps: {
+      color: 'orange'
+    }
+  },
+  {
+    value: ReportStatusItem.REJECTING,
+    label: '驳回',
+    tagProps: {
+      color: 'red'
+    }
+  },
+  {
+    value: ReportStatusItem.REVIEWED,
+    label: '已审阅',
+    tagProps: {
+      color: 'green'
+    }
+  }
+]
+
+
+export const ChangeMarkerList: { label: string, value: number, class?: string, subClass?: string }[] = [
+  {
+    value: 1,
+    label: '已修改',
+    class: 'ribbon-modify',
+    subClass: 'ribbon-sub-modify',
+  },
+  {
+    value: 2,
+    label: '新增',
+    class: 'ribbon-add',
+    subClass: 'ribbon-sub-add',
+  },
+  {
+    value: 3,
+    label: '已删除',
+    class: 'ribbon-remove',
+    subClass: 'ribbon-sub-remove',
+  }
+]
+
+
+// 操作项
+export enum OperateItem {
+  ADD = 0, // 添加
+  MODIFY = 1, // 修改
+  DETAIL = 2, //  详情
+  REMOVE = 3, // 删除
+  LIST = 4 // 列表
+}
+
+export enum FlowNode {
+  DRAFTING = '起草',
+  A = '抄送',
+  B = '审批',
+  C = '结束',
+}
+
+export enum ComponentsType {
+  STRINGINPUT = 'text', // 默认string input 组件
+  NUMBERINPUT = 'number', // 默认number input 组件
+  DIGIT = 'digit', // 默认number input 组件
+  SELECT = 'select', // 下拉列表
+  CASCADER = 'cascader', // 级联
+  DATEPICKER = 'datepicker', // 日期
+  TEXTAREA = 'textarea' // 文本域
+}
+
+export enum ApiCustomeCode {
+  SUCCESS = 10000
+}
+
+
+export const filterDateList: { title: string, value: number, key: string }[] = [
+  {
+    title: '不限',
+    value: 0,
+    key: 'no-limit'
+  },
+  {
+    title: '近一周',
+    value: 1,
+    key: 'one-week'
+  },
+  {
+    title: '近一个月',
+    value: 2,
+    key: 'one-month'
+  },
+  {
+    title: '近三个月',
+    value: 3,
+    key: 'three-month'
+  },
+  {
+    title: '近半年',
+    value: 4,
+    key: 'six-month'
+  },
+  {
+    title: '近一年',
+    value: 5,
+    key: 'one-year'
+  },
+]
+
+// 计划任务状态
+export enum TaskStatus {
+  DEFAULT = 0,
+  DOING = 10,
+  TIMEOUT = 20,
+  REJECTING = 30,
+  APPLY_CREATE = 40,
+  APPLYING = 50,
+  DELAY_DONE = 80,
+  CANCEL = 90,
+  COMPLETE = 100
+}
+
+// 计划任务状态列表
+export const TaskStatusList: { label: string, value: number, color?: string }[] = [
+  {
+    label: '未开始',
+    value: TaskStatus.DEFAULT,
+    color: 'gray'
+
+  },
+  {
+    label: '进行中',
+    value: TaskStatus.DOING,
+    color: 'arcoblue'
+
+  }, {
+    label: '已超时',
+    value: TaskStatus.TIMEOUT,
+    color: 'orange'
+
+  }, 
+  {
+    label: '审核未通过',
+    value: TaskStatus.REJECTING,
+    color: 'red'
+
+  }, 
+  {
+    label: '申请审核',
+    value: TaskStatus.APPLY_CREATE,
+    color: 'blue'
+
+  },
+  {
+    label: '审核中',
+    value: TaskStatus.APPLYING,
+    color: 'blue'
+
+  },
+  {
+    label: '延期完成',
+    value: TaskStatus.DELAY_DONE,
+    color: 'cyan'
+
+  }, {
+    label: '已完成',
+    value: TaskStatus.COMPLETE,
+    color: 'green'
+
+  },
+]
+
+export const MilestoneStatusList: { label: string, theme?: "normal" | "success" | "warning" | "danger" | undefined, color: string, status: TaskStatus, icon: string, iconSize?: number }[] = [
+  {
+    label: '未开始',
+    theme: undefined,
+    color: '#c0c0c0',
+    status: TaskStatus.DEFAULT,
+    icon: 'my-clock',
+    iconSize: 26
+  },
+  {
+    label: '进行中',
+    color: '#007aff',
+    theme: 'normal',
+    status: TaskStatus.DOING,
+    icon: 'my-rocket'
+  },
+  {
+    label: '审核未通过',
+    status: TaskStatus.REJECTING,
+    color: '#dd524d',
+    icon: 'close'
+  }, 
+  {
+    label: '申请审核',
+    status: TaskStatus.APPLY_CREATE,
+    color: 'rgb(52, 145, 250)',
+    icon: 'navigate'
+
+  },
+  {
+    label: '审核中',
+    status: TaskStatus.APPLYING,
+    color: 'rgb(52, 145, 250)',
+    icon: 'auth-filled'
+
+  },
+  {
+    label: '已超时',
+    color: '#f0ad4e',
+    theme: 'warning',
+    status: TaskStatus.TIMEOUT,
+    icon: 'my-timeout'
+  },
+  {
+    label: '延期完成',
+    color: '#4cd964',
+    theme: 'success',
+    status: TaskStatus.DELAY_DONE,
+    icon: 'my-delay',
+    iconSize: 26
+  },
+  {
+    label: '按时完成',
+    color: '#4cd964',
+    theme: 'success',
+    status: TaskStatus.COMPLETE,
+    icon: 'my-check'
+  }
+]
+
+
+export const OutcomeStatusList: { label: string, color: string, status: number }[] = [
+  {
+    label: '未交付',
+    color: 'gray',
+    status: 0
+  },
+  {
+    label: '已上传',
+    color: 'cyan',
+    status: 5
+  },
+  {
+    label: '申请审核中',
+    color: 'orange',
+    status: 10
+  },
+  {
+    label: '审核中',
+    color: 'arcoblue',
+    status: 20
+  },
+  {
+    label: '审核未通过',
+    color: 'red',
+    status: 30
+  },
+  {
+    label: '已完成交付',
+    color: 'green',
+    status: 100
+  }
+]
+
+// 字体图标库
+export enum IconFontUrl {
+  URL = '//at.alicdn.com/t/c/font_4466254_cd3ym5xqpem.js'
+}
+
+// minio
+export enum MinioUrl {
+  URL = 'https://pmr.surkw.com:19000/', // minio 地址
+  CustomMeta = 'custom-meta' // 自定义图元路径
+}
+
+
+// minio
+export enum Provider {
+  WX = 'weixin',
+}
+
+export enum FileDownloadSheetAction {
+  SHARE = 0,
+  Collect = 1
+}

+ 9 - 0
src/env.d.ts

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

+ 23 - 0
src/hooks/chart-option.ts

@@ -0,0 +1,23 @@
+import { computed } from 'vue'
+import { EChartsOption } from 'echarts'
+
+// for code hints
+// import { SeriesOption } from 'echarts';
+// Because there are so many configuration items, this provides a relatively convenient code hint.
+// When using vue, pay attention to the reactive issues. It is necessary to ensure that corresponding functions can be triggered, TypeScript does not report errors, and code writing is convenient.
+interface optionsFn {
+  (): EChartsOption
+}
+
+export default function useChartOption(sourceOption: optionsFn) {
+
+  // echarts support https://echarts.apache.org/zh/theme-builder.html
+  // It's not used here
+  // TODO echarts themes
+  const chartOption = computed<EChartsOption>(() => {
+    return sourceOption()
+  })
+  return {
+    chartOption
+  }
+}

+ 26 - 0
src/logics/mitt/useEmitt.ts

@@ -0,0 +1,26 @@
+import mitt from '@/utils/mitt'
+import { onBeforeUnmount } from 'vue'
+
+declare interface Fn<T = any> {
+  (...arg: T[]): T
+}
+interface Option {
+  name: string // 事件名称
+  callback: Fn // 回调
+}
+
+const emitter = mitt()
+
+export const useEmitt = (option?: Option) => {
+  if (option) {
+    emitter.on(option.name, option.callback)
+
+    onBeforeUnmount(() => {
+      emitter.off(option.name, option.callback)
+    })
+  }
+
+  return {
+    emitter
+  }
+}

+ 21 - 0
src/main.ts

@@ -0,0 +1,21 @@
+import {
+	createSSRApp
+} from "vue";
+import * as Pinia from 'pinia';
+import { createUnistorage } from 'pinia-plugin-unistorage'
+import App from "./App.vue";
+import globalComponents from '@/components'
+export function createApp() {
+    const app = createSSRApp(App);
+    const store = Pinia.createPinia();
+    app.config.globalProperties.$onLaunched = new Promise(resolve => {
+        app.config.globalProperties.$isResolve = resolve
+      })
+    store.use(createUnistorage());
+    app.use(store);
+    app.use(globalComponents)
+    return {
+        app,
+        Pinia
+    };
+}

+ 77 - 0
src/manifest.json

@@ -0,0 +1,77 @@
+{
+    "name" : "",
+    "appid" : "",
+    "description" : "",
+    "versionName" : "1.0.0",
+    "versionCode" : "100",
+    "transformPx" : false,
+    /* 5+App特有相关 */
+    "app-plus" : {
+        "usingComponents" : true,
+        "nvueStyleCompiler" : "uni-app",
+        "compilerVersion" : 3,
+        "splashscreen" : {
+            "alwaysShowBeforeRender" : true,
+            "waiting" : true,
+            "autoclose" : true,
+            "delay" : 0
+        },
+        /* 模块配置 */
+        "modules" : {},
+        /* 应用发布信息 */
+        "distribute" : {
+            /* android打包配置 */
+            "android" : {
+                "permissions" : [
+                    "<uses-permission android:name=\"android.permission.CHANGE_NETWORK_STATE\"/>",
+                    "<uses-permission android:name=\"android.permission.MOUNT_UNMOUNT_FILESYSTEMS\"/>",
+                    "<uses-permission android:name=\"android.permission.VIBRATE\"/>",
+                    "<uses-permission android:name=\"android.permission.READ_LOGS\"/>",
+                    "<uses-permission android:name=\"android.permission.ACCESS_WIFI_STATE\"/>",
+                    "<uses-feature android:name=\"android.hardware.camera.autofocus\"/>",
+                    "<uses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\"/>",
+                    "<uses-permission android:name=\"android.permission.CAMERA\"/>",
+                    "<uses-permission android:name=\"android.permission.GET_ACCOUNTS\"/>",
+                    "<uses-permission android:name=\"android.permission.READ_PHONE_STATE\"/>",
+                    "<uses-permission android:name=\"android.permission.CHANGE_WIFI_STATE\"/>",
+                    "<uses-permission android:name=\"android.permission.WAKE_LOCK\"/>",
+                    "<uses-permission android:name=\"android.permission.FLASHLIGHT\"/>",
+                    "<uses-feature android:name=\"android.hardware.camera\"/>",
+                    "<uses-permission android:name=\"android.permission.WRITE_SETTINGS\"/>"
+                ]
+            },
+            /* ios打包配置 */
+            "ios" : {},
+            /* SDK配置 */
+            "sdkConfigs" : {}
+        }
+    },
+    /* 快应用特有相关 */
+    "quickapp" : {},
+    /* 小程序特有相关 */
+    "mp-weixin" : {
+        "appid" : "wx78e79f8a976d3952",
+        "setting" : {
+            "urlCheck" : true
+        },
+        "usingComponents" : true,
+        "optimization" : {
+            "subPackages" : true
+        }
+    },
+    "mp-alipay" : {
+        "usingComponents" : true,
+        "styleIsolation" : "apply-shared",
+        "mergeVirtualHostAttributes" : true
+    },
+    "mp-baidu" : {
+        "usingComponents" : true
+    },
+    "mp-toutiao" : {
+        "usingComponents" : true
+    },
+    "uniStatistics" : {
+        "enable" : false
+    },
+    "vueVersion" : "3"
+}

+ 376 - 0
src/pages.json

@@ -0,0 +1,376 @@
+{
+	"easycom": {
+		"autoscan": true,
+		"custom": {
+			"^uni-(.*)": "@dcloudio/uni-ui/lib/uni-$1/uni-$1.vue",
+			"^(?!z-paging-refresh|z-paging-load-more)z-paging(.*)": "@/uni_modules/z-paging/components/z-paging$1/z-paging$1.vue",
+			"^y-(.*)": "@/uni_modules/y-$1/components/y-$1.vue"
+		}
+	},
+	"pages": [
+		{
+			"path": "pages/home/index",
+			"style": {
+				"navigationBarTitleText": "牧云环保项目管理",
+				"componentPlaceholder": {
+					"plan-task": "view",
+					"my-work": "view",
+					"project-count": "view",
+					"project-category": "view"
+				}
+			}
+		},
+		{
+			"path": "pages/work/index",
+			"style": {
+				"navigationBarTitleText": "我的工作",
+				"app-plus": {
+					"bounce": "none"
+				}
+			}
+		},
+		{
+			"path": "pages/project/index",
+			"style": {
+				"navigationBarTitleText": "项目管理"
+			}
+		},
+		{
+			"path": "pages/profile/index",
+			"style": {
+				"navigationBarTitleText": "我的",
+				"navigationStyle": "custom",
+				"mp-alipay": {
+					"transparentTitle": "always",
+					"titlePenetrate": "YES"
+				}
+			}
+		},
+		{
+			"path": "pages/authorize/index",
+			"style": {
+				"navigationBarTitleText": "授权"
+			}
+		}
+	],
+	"globalStyle": {
+		"navigationBarTextStyle": "black",
+		"navigationBarTitleText": "uni-app",
+		"navigationBarBackgroundColor": "#F8F8F8",
+		"backgroundColor": "#edf0f9"
+	},
+	"subPackages": [
+		{
+			"root": "pages/project/components",
+			"pages": [
+				{
+					"path": "add-project",
+					"style": {
+						"navigationBarTitleText": "创建项目"
+					}
+				},
+				{
+					"path": "detail",
+					"style": {
+						"navigationBarTitleText": "项目详情"
+					}
+				},
+				{
+					"path": "doc-detail",
+					"style": {
+						"navigationBarTitleText": "文档详情"
+					}
+				},
+				{
+					"path": "form-work",
+					"style": {
+						"navigationBarTitleText": "工作表单"
+					}
+				},
+				{
+					"path": "logs",
+					"style": {
+						"navigationBarTitleText": "动态"
+					}
+				},
+				{
+					"path": "work-chain",
+					"style": {
+						"navigationBarTitleText": "工作链"
+					}
+				}
+			]
+		},
+		{
+			"root": "pages/project/overview",
+			"pages": [
+				{
+					"path": "add-checker",
+					"style": {
+						"navigationBarTitleText": "添加审核组成员"
+					}
+				},
+				{
+					"path": "add-member",
+					"style": {
+						"navigationBarTitleText": "添加项目组成员"
+					}
+				},
+				{
+					"path": "edit-checker",
+					"style": {
+						"navigationBarTitleText": "修改审核组成员"
+					}
+				},
+				{
+					"path": "edit-member",
+					"style": {
+						"navigationBarTitleText": "修改项目组成员"
+					}
+				}
+			]
+		},
+		{
+			"root": "pages/project/plan-task-list",
+			"pages": [
+				{
+					"path": "add-plan-task",
+					"style": {
+						"navigationBarTitleText": "添加任务"
+					}
+				},
+				{
+					"path": "edit-plan-task",
+					"style": {
+						"navigationBarTitleText": "修改任务"
+					}
+				},
+				{
+					"path": "set-plan-progress",
+					"style": {
+						"navigationBarTitleText": "设置任务进度"
+					}
+				}
+			]
+		},
+		{
+			"root": "pages/project/plan-task-detail",
+			"pages": [
+				{
+					"path": "add-task-check-flow",
+					"style": {
+						"navigationBarTitleText": "添加任务审核"
+					}
+				},
+				{
+					"path": "edit-task-check-flow",
+					"style": {
+						"navigationBarTitleText": "修改任务审核流程"
+					}
+				},
+				{
+					"path": "add-outcome",
+					"style": {
+						"navigationBarTitleText": "添加交付物"
+					}
+				},
+				{
+					"path": "edit-outcome",
+					"style": {
+						"navigationBarTitleText": "edit-outcome"
+					}
+				}
+			]
+		},
+		{
+			"root": "pages/echart",
+			"pages": [
+				{
+					"path": "index",
+					"style": {
+						"navigationBarTitleText": "index"
+					}
+				}
+			]
+		},
+		{
+			"root": "pages/profile/week-report",
+			"pages": [
+				{
+					"path": "index",
+					"style": {
+						"navigationBarTitleText": "我的周报",
+						"componentPlaceholder": {
+							"week-report": "view"
+						}
+					}
+				}
+			]
+		},
+		{
+			"root": "pages/project/week-report",
+			"pages": [
+				{
+					"path": "add-week-report",
+					"style": {
+						"navigationBarTitleText": "新建周报"
+					}
+				},
+				{
+					"path": "week-report-detail",
+					"style": {
+						"navigationBarTitleText": "week-report-detail"
+					}
+				}
+			]
+		},
+		{
+			"root": "pages/profile/contract",
+			"pages": [
+				{
+					"path": "index",
+					"style": {
+						"navigationBarTitleText": "我的合同"
+					}
+				},
+				{
+					"path": "detail",
+					"style": {
+						"navigationBarTitleText": "合同详情",
+						"componentPlaceholder": {
+							"contract-detail": "view"
+						}
+					}
+				},
+				{
+					"path": "add-contract",
+					"style": {
+						"navigationBarTitleText": "录入合同"
+					}
+				},
+				{
+					"path": "add-payment",
+					"style": {
+						"navigationBarTitleText": "添加收款约定"
+					}
+				},
+				{
+					"path": "edit-payment",
+					"style": {
+						"navigationBarTitleText": "修改收款约定"
+					}
+				}
+			]
+		},
+		{
+			"root": "pages/profile/customer",
+			"pages": [
+				{
+					"path": "index",
+					"style": {
+						"navigationBarTitleText": "客户管理"
+					}
+				},
+				{
+					"path": "detail",
+					"style": {
+						"navigationBarTitleText": "客户详情"
+					}
+				},
+				{
+					"path": "add-customer",
+					"style": {
+						"navigationBarTitleText": "新增客户"
+					}
+				}
+			]
+		},
+		{
+			"root": "pages/project/progressing",
+			"pages": [
+				{
+					"path": "outcome-list",
+					"style": {
+						"navigationBarTitleText": "交付物列表"
+					}
+				}
+			]
+		},
+		{
+			"root": "pages/profile/version",
+			"pages": [
+				{
+					"path": "index",
+					"style": {
+						"navigationBarTitleText": "版本记录"
+					}
+				}
+			]
+		},
+		{
+			"root": "pages/profile/info",
+			"pages": [
+				{
+					"path": "index",
+					"style": {
+						"navigationBarTitleText": "个人资料"
+					}
+				}
+			]
+		},
+		{
+			"root": "pages/profile/archive",
+			"pages": [
+				{
+					"path": "index",
+					"style": {
+						"navigationBarTitleText": "已归档文件",
+						"componentPlaceholder": {
+							"document": "view"
+						}
+					}
+				}
+			]
+		}
+	],
+	"preloadRule": {
+		"pages/home/index": {
+			"network": "all",
+			"packages": [
+				"pages/echart"
+			]
+		}
+	},
+	"tabBar": {
+		"color": "#8a8a8a",
+		"selectedColor": "#3d7eff",
+		"backgroundColor": "#edf0f9",
+		"borderStyle": "black",
+		"list": [
+			{
+				"text": "首页",
+				"pagePath": "pages/home/index",
+				"iconPath": "./static/images/tabBar/home.png",
+				"selectedIconPath": "./static/images/tabBar/home-selected.png"
+			},
+			{
+				"text": "工作",
+				"pagePath": "pages/work/index",
+				"iconPath": "./static/images/tabBar/notice.png",
+				"selectedIconPath": "./static/images/tabBar/notice-selected.png"
+			},
+			{
+				"text": "项目",
+				"pagePath": "pages/project/index",
+				"iconPath": "./static/images/tabBar/project.png",
+				"selectedIconPath": "./static/images/tabBar/project-selected.png"
+			},
+			{
+				"text": "我的",
+				"pagePath": "pages/profile/index",
+				"iconPath": "./static/images/tabBar/mine.png",
+				"selectedIconPath": "./static/images/tabBar/mine-selected.png"
+			}
+		]
+	}
+}

+ 68 - 0
src/pages/authorize/index.vue

@@ -0,0 +1,68 @@
+<template>
+    <view class="page">
+        <view class="phone_view">
+            <view class="title">牧云项目管理系统</view>
+            <view class="profile">第一次使用此程序功能,需要授权手机号</view>
+            <custom-button  class="mini-btn" open-type="getPhoneNumber" width="600rpx" type="filled" color="#007aff" @getphonenumber="getPhoneNumber" ripple>授权手机号</custom-button>
+        </view>
+    </view>
+</template>
+<script lang="ts" setup>
+import { getPermissions, postUserInfo } from '@/api/base/client';
+import { useUserStore } from '@/stores'
+const userStore = useUserStore()
+const getPhoneNumber = (e: any) => {
+    uni.login({
+        async success(loginRes: UniApp.LoginRes) {
+            const { code } = loginRes
+            const { encryptedData, iv } = e.detail;
+            await postUserInfo({ code, mobile: { iv, encryptedData } })
+            uni.showToast({
+                icon: 'success',
+                title: '授权成功'
+            })
+            const { list } = await getPermissions();
+            userStore.setAuthorize({ mobile: true, user_info: true })
+            userStore.setPermissionsList(list)
+            uni.reLaunch({ url: '/pages/home/index' })
+        }
+    })
+}
+</script>
+<style lang="less" scoped>
+.page {
+    background-color: rgba(255, 255, 255, 1.000000);
+    position: relative;
+    width: 750rpx;
+    height: 1624rpx;
+    overflow: hidden;
+    display: flex;
+    flex-direction: column;
+
+    .phone_view {
+        height: 70%;
+        display: flex;
+        flex-direction: column;
+        align-items: center;
+        justify-content: center;
+        box-sizing: border-box;
+
+        .title {
+            text-align: center;
+        }
+
+        .profile {
+            text-align: center;
+            margin-top: 30rpx;
+            font-size: 14px;
+            color: #888;
+        }
+
+        .mini-btn {
+            margin-top: 50rpx;
+            margin-left: 50px;
+            margin-right: 50px;
+        }
+    }
+}
+</style>

+ 9 - 0
src/pages/echart/index.vue

@@ -0,0 +1,9 @@
+<template>
+  <div class="index">index</div>
+</template>
+
+<script lang="ts">
+
+</script>
+
+<style scoped></style>

+ 168 - 0
src/pages/echart/my-work.vue

@@ -0,0 +1,168 @@
+<template>
+    <uni-card title="我的工作" margin="0px" padding="0px">
+        <view style="height: 300px;">
+        <LEchart ref="chartRef" style="width: 100%;height: 100%;" @touchstart="touchStart" @touchmove="touchMove"
+            @touchend="touchEnd" />
+        </view>
+    </uni-card>
+</template>
+
+<script setup lang="ts">
+import LEchart from '@/components/l-echart/l-echart.vue';
+import { ref, watch, nextTick, onMounted } from 'vue'
+
+import useChartOption from '@/hooks/chart-option'
+import { getWorkStat } from '@/api/project-center/work';
+// #ifdef MP
+const echarts = require('./static/echarts.min')
+// #endif
+
+
+const { mobile } = uni.getStorageSync('info_status')
+const proxy = getCurrentInstance()?.proxy
+const { $onLaunched } = proxy as any
+const chartRef = ref()
+const workStat = ref<{
+    units: string[]
+    name: string[]
+    values: number[]
+}>({
+    units: [],
+    name: [],
+    values: []
+})
+const initData = async (data: any) => {
+    await nextTick()
+    if (chartRef.value) {
+        const myChart = await chartRef.value?.init(echarts)
+        myChart.setOption(data)
+    }
+}
+
+
+
+
+const { chartOption } = useChartOption(() => {
+    if (workStat.value.name.length === 0) {
+        return {}
+    }
+    return {
+        animation: false,
+        grid: {
+            left: 80,
+            right: 20,
+            top: 0,
+            bottom: 30
+        },
+        xAxis: {
+            type: 'value',
+            axisLabel: {
+                show: true,
+                formatter(value: number, idx: number) {
+                    if (idx === 0) return String(value)
+                    return `${Number(value)}`
+                }
+            },
+            splitLine: {
+                lineStyle: {
+                    color: '#E5E8EF'
+                }
+            }
+        },
+        yAxis: {
+            type: 'category',
+            data: workStat.value.name,
+            axisLabel: {
+                show: true,
+                color: '#4E5969'
+            },
+            axisTick: {
+                show: true,
+                length: 2,
+                lineStyle: {
+                    color: '#A9AEB8'
+                },
+                alignWithLabel: true
+            },
+            axisLine: {
+                lineStyle: {
+                    color: '#A9AEB8'
+                }
+            }
+        },
+        tooltip: {
+            trigger: 'axis',
+            axisPointer: {
+                type: 'shadow'
+            },
+            confine: true
+        },
+        // tooltip: {
+        //   trigger: 'axis',
+        //   formatter(params) {
+        //     const [firstElement] = params as ToolTipFormatterParams[]
+        //     return `<div>
+        //           <p class="tooltip-title">${firstElement.axisValueLabel}</p>
+        //           ${tooltipItemsHtmlString(params as ToolTipFormatterParams[])}
+        //         </div>`
+        //   },
+        //   className: 'echarts-tooltip-diy'
+        // },
+        series: [
+            {
+                data: workStat.value.values,
+                type: 'bar',
+                barWidth: 7,
+                itemStyle: {
+                    color: '#4086FF',
+                    borderRadius: 4
+                }
+            }
+        ]
+    }
+})
+
+const getDataStat = async () => {
+    await $onLaunched;
+    if(!mobile){
+        return
+    }
+    const { list } = await getWorkStat()
+    list.map(item => {
+        workStat.value.values.push(item.value)
+        workStat.value.name.push(item.name)
+        workStat.value.units.push(item.unit)
+        return item
+    })
+}
+
+onMounted(() => {
+    getDataStat()
+})
+
+watch(
+    () => chartOption.value,
+    () => {
+        initData(chartOption.value)
+    },
+    {
+        flush: 'post',
+        deep: true
+    }
+)
+
+const touchStart = (e: any) => {
+    chartRef.value.touchStart(e)
+}
+
+const touchMove = (e: any) => {
+    chartRef.value.touchMove(e)
+}
+
+
+const touchEnd = (e: any) => {
+    chartRef.value.touchEnd(e)
+}
+</script>
+
+<style lang="scss" scoped></style>

+ 283 - 0
src/pages/echart/plan-task.vue

@@ -0,0 +1,283 @@
+<template>
+    <uni-card title="项目计划统计" extra="更多" margin="0px" padding="0px">
+        <view style="height: 300px;">
+            <LEchart ref="chartRef" style="width: 100%;height: 100%;" @touchstart="touchStart" @touchmove="touchMove"
+                @touchend="touchEnd" />
+        </view>
+    </uni-card>
+</template>
+
+<script setup lang="ts">
+import LEchart from '@/components/l-echart/l-echart.vue';
+import { ref, watch, nextTick, onMounted, getCurrentInstance } from 'vue'
+import {
+    endOfYear,
+    format,
+    startOfYear,
+    eachDayOfInterval,
+    startOfDay,
+    subMonths,
+    subWeeks
+} from 'date-fns'
+import useChartOption from '@/hooks/chart-option'
+import { LineSeriesOption } from 'echarts/types/dist/echarts';
+import { ProjectPlanStatReqParams } from '@/types/dashboard';
+import { getProjectPlanStat } from '@/api/dashboard/project';
+
+// #ifdef MP
+const echarts = require('./static/echarts.min')
+// #endif
+
+// #ifndef MP
+// import * as echarts from 'echarts'
+// #endif
+
+const { mobile } = uni.getStorageSync('info_status')
+const units = ['个', '个', '个', '%']
+const chartRef = ref()
+const dateType = ref(2)
+const totalCount = ref<any[]>([])
+const inCompleteCount = ref<any[]>([])
+const delayedCount = ref<any[]>([])
+const delayRate = ref<any[]>([])
+const xAxisStat = ref<string[]>([])
+const proxy = getCurrentInstance()?.proxy
+const { $onLaunched } = proxy as any
+const preloadData = [{
+    "id": "",
+    "dataset": {},
+    "left": 0,
+    "right": 375,
+    "top": 0,
+    "bottom": 0,
+    "width": "100%",
+    "height": "100%",
+    "skeletonType": "rect"
+}]
+
+const requestData = reactive<{
+    beginAt: Date
+    endAt: Date
+    dateType: number
+}>({
+    dateType: 0,
+    beginAt: subWeeks(new Date(), 1),
+    endAt: startOfDay(new Date())
+})
+const initData = async (data: any) => {
+    await nextTick()
+    if (chartRef.value) {
+        const myChart = await chartRef.value?.init(echarts)
+        myChart.setOption(data)
+    }
+}
+
+function graphicFactory(side: AnyObject) {
+    return {
+        type: 'text',
+        bottom: '8',
+        ...side,
+        style: {
+            text: '',
+            textAlign: 'center',
+            fill: '#4E5969',
+            fontSize: 12
+        }
+    }
+}
+const graphicElements = ref([
+    graphicFactory({ left: '2.6%' }),
+    graphicFactory({ right: 0 })
+])
+
+const generateSeries = (
+    name: string,
+    lineColor: string,
+    itemBorderColor: string,
+    data: number[]
+): LineSeriesOption => {
+    return {
+        name,
+        data,
+        type: 'line',
+        smooth: true,
+        connectNulls: true,
+        symbol: 'circle',
+        symbolSize: 8,
+        itemStyle: {
+            color: lineColor
+        },
+        emphasis: {
+            focus: 'series',
+            itemStyle: {
+                color: lineColor,
+                borderWidth: 2,
+                borderColor: itemBorderColor
+            }
+        },
+        lineStyle: {
+            width: 2,
+            color: lineColor
+        },
+        showSymbol: false,
+        areaStyle: {
+            opacity: 0.1,
+            color: lineColor
+        }
+        // label: {
+        //   show: true,
+        //   formatter: params => {
+        //     // 当数据值为 0 时,不显示标签
+        //     return params.value === 0 ? '' : params.value
+        //   }
+        // }
+    }
+}
+
+const { chartOption } = useChartOption(() => {
+    return {
+        animation: false,
+        legend: {
+            show: true,
+            width: '80%',
+            textStyle: {
+                color: '#333'
+            }
+        },
+        grid: {
+            right: '0',
+            bottom: '30'
+        },
+        xAxis: {
+            type: 'category',
+            data: xAxisStat.value,
+            axisLine: {
+                show: true
+            },
+            axisTick: {
+                show: true
+            },
+            splitLine: {
+                show: true
+            },
+            axisPointer: {
+                show: true,
+                lineStyle: {
+                    color: '#23ADFF',
+                    width: 2
+                }
+            }
+        },
+        yAxis: {
+            type: 'value',
+            axisLine: {
+                show: true
+            },
+            splitLine: {
+                lineStyle: {
+                    color: '#F2F3F5'
+                }
+            }
+        },
+        tooltip: {
+            trigger: 'axis',
+            axisPointer: {
+                type: 'shadow'
+            },
+            confine: true
+        },
+        graphic: {
+            elements: graphicElements.value
+        },
+        series: [
+            generateSeries(
+                '总任务数',
+                'rgb(22, 93, 255)',
+                'rgb(232, 243, 255)',
+                totalCount.value
+            ),
+            generateSeries(
+                '未完成任务数',
+                '#F77234',
+                '#FFE4BA',
+                inCompleteCount.value
+            ),
+            generateSeries('任务延期数', '#33D1C9', '#E8FFFB', delayedCount.value),
+            generateSeries('任务延期率', '#722ED1', '#F5E8FF', delayRate.value)
+        ]
+    }
+})
+
+const getDataStat = async () => {
+    try {
+        xAxisStat.value = []
+        totalCount.value = []
+        inCompleteCount.value = []
+        delayedCount.value = []
+        delayRate.value = []
+        const { beginAt, endAt } = requestData
+        const params: ProjectPlanStatReqParams = {
+            begin_at: format(beginAt, 'yyyy-MM-dd'),
+            end_at: format(endAt, 'yyyy-MM-dd')
+        }
+        const xValue = eachDayOfInterval({
+            start: beginAt,
+            end: endAt
+        }).map(item => {
+            return format(item, 'yyyy-MM-dd')
+        })
+        xAxisStat.value = xValue
+        await $onLaunched;
+        if(!mobile){
+            return
+        }
+        const { list } = await getProjectPlanStat(params)
+        xValue.map(item => {
+            const data = list.find(l => l.ts === item)
+            if (data) {
+                totalCount.value.push(data.total_count)
+                inCompleteCount.value.push(data.incomplete_count)
+                delayedCount.value.push(data.delayed_count)
+                delayRate.value.push(data.delay_rate)
+            } else {
+                totalCount.value.push(null)
+                inCompleteCount.value.push(null)
+                delayedCount.value.push(null)
+                delayRate.value.push(null)
+            }
+            return item
+        })
+    } catch (e) {
+        console.log(e)
+    }
+}
+
+onMounted(() => {
+    getDataStat()
+})
+
+watch(
+    () => chartOption.value,
+    () => {
+        initData(chartOption.value)
+    },
+    {
+        flush: 'post',
+        deep: true
+    }
+)
+
+const touchStart = (e: any) => {
+    chartRef.value.touchStart(e)
+}
+
+const touchMove = (e: any) => {
+    chartRef.value.touchMove(e)
+}
+
+
+const touchEnd = (e: any) => {
+    chartRef.value.touchEnd(e)
+}
+
+</script>

+ 228 - 0
src/pages/echart/project-category.vue

@@ -0,0 +1,228 @@
+<template>
+    <uni-card title="项目分类统计" margin="0px" padding="0px">
+        <view style="height: 500px;">
+            <LEchart ref="chartRef" style="width: 100%;height: 100%;" 
+            @touchstart="touchStart"
+			@touchmove="touchMove"
+			@touchend="touchEnd"
+            />
+        </view>
+    </uni-card>
+</template>
+
+<script setup lang="ts">
+import LEchart from '@/components/l-echart/l-echart.vue';
+import { ref, watch, nextTick, onMounted } from 'vue'
+import useChartOption from '@/hooks/chart-option'
+import { getProjectCategoryStat } from '@/api/dashboard/project';
+// #ifdef MP
+const echarts = require('./static/echarts.min')
+// #endif
+
+// #ifndef MP
+// import * as echarts from 'echarts'
+// #endif
+
+
+
+const proxy = getCurrentInstance()?.proxy
+const { $onLaunched } = proxy as any
+const chartRef = ref()
+const { mobile } = uni.getStorageSync('info_status')
+
+const categoryStat = ref<{
+    totalCount: number
+    workCount: number
+    name: string[]
+    count: number[]
+    works: number[]
+}>({
+    totalCount: 0,
+    workCount: 0,
+    name: [],
+    count: [],
+    works: []
+})
+
+const initData = async (data: any) => {
+    await nextTick()
+    if (chartRef.value) {
+        const myChart = await chartRef.value?.init(echarts)
+        myChart.setOption(data)
+    }
+}
+
+
+
+
+const { chartOption } = useChartOption(() => {
+    if (categoryStat.value.name.length === 0) {
+        return {}
+    }
+    return {
+        animation: false,
+        legend: {
+            left: 'center',
+            data: categoryStat.value.name,
+            bottom: 10,
+            icon: 'circle',
+            itemWidth: 8,
+            textStyle: {
+                color: '#4E5969'
+            },
+            itemStyle: {
+                borderWidth: 0
+            }
+        },
+        tooltip: {
+            show: true,
+            trigger: 'item'
+        },
+        graphic: {
+            elements: [
+                {
+                    type: 'text',
+                    left: '45%',
+                    top: '22%',
+                    style: {
+                        text: '项目数',
+                        textAlign: 'center',
+                        fill: '#4E5969',
+                        fontSize: 14
+                    }
+                },
+                {
+                    type: 'text',
+                    left: '45%',
+                    top: '27%',
+                    style: {
+                        text: `${categoryStat.value.totalCount}个`,
+                        textAlign: 'center',
+                        fill: '#1D2129',
+                        fontSize: 16,
+                        fontWeight: 500
+                    }
+                },
+                {
+                    type: 'text',
+                    left: '45%',
+                    top: '62%',
+                    style: {
+                        text: '工时',
+                        textAlign: 'center',
+                        fill: '#4E5969',
+                        fontSize: 14
+                    }
+                },
+                {
+                    type: 'text',
+                    left: '45%',
+                    top: '67%',
+                    style: {
+                        text: `${categoryStat.value.workCount}天`,
+                        textAlign: 'center',
+                        fill: '#1D2129',
+                        fontSize: 16,
+                        fontWeight: 500
+                    }
+                }
+            ]
+        },
+        series: [
+            {
+                type: 'pie',
+                radius: ['50%', '40%'],
+                center: ['50%', '25%'],
+                // radius: ['50%', '70%'],
+                // center: ['25%', '50%'],
+                // center: ['100%', '75%'],
+                label: {
+                    formatter: '{d}%',
+                    fontSize: 14,
+                    color: '#4E5969'
+                },
+                itemStyle: {
+                    borderColor: '#fff',
+                    borderWidth: 1
+                },
+                data: categoryStat.value.name.map((item, index) => {
+                    console.log(item)
+                    return {
+                        name: item,
+                        value: categoryStat.value.count[index]
+                    }
+                })
+            },
+            {
+                type: 'pie',
+                radius: ['50%', '40%'],
+                center: ['50%', '65%'],
+                // radius: ['50%', '70%'],
+                // center: ['75%', '50%'],
+                label: {
+                    formatter: '{d}%',
+                    fontSize: 14,
+                    color: '#4E5969'
+                },
+                itemStyle: {
+                    borderColor: '#fff',
+                    borderWidth: 1
+                },
+                data: categoryStat.value.name.map((item, index) => {
+                    return {
+                        name: item,
+                        value: categoryStat.value.works[index]
+                    }
+                })
+            }
+        ]
+    }
+})
+
+
+const getDataStat = async () => {
+    await $onLaunched
+    console.log('mobile')
+    if(!mobile){
+        return
+    }
+    const { list } = await getProjectCategoryStat()
+    list.map(item => {
+        categoryStat.value.totalCount += item.project_count
+        categoryStat.value.workCount += Number(item.total_work_days)
+        categoryStat.value.count.push(item.project_count)
+        categoryStat.value.works.push(Number(item.total_work_days))
+        categoryStat.value.name.push(item.type_name)
+        return item
+    })
+}
+
+onMounted(() => {
+    getDataStat()
+})
+
+watch(
+    () => chartOption.value,
+    () => {
+        initData(chartOption.value)
+    },
+    {
+        flush: 'post',
+        deep: true
+    }
+)
+
+const touchStart = (e: any) => {
+    chartRef.value.touchStart(e)
+}
+
+const touchMove = (e: any) => {
+    chartRef.value.touchMove(e)
+}
+
+
+const touchEnd = (e: any) => {
+    chartRef.value.touchEnd(e)
+}
+
+</script>

+ 192 - 0
src/pages/echart/project-count.vue

@@ -0,0 +1,192 @@
+<template>
+  <uni-card title="近一年每月项目数量统计" margin="0px" padding="0px">
+    <view style="height: 300px;">
+      <LEchart ref="chartRef" style="width: 100%;height: 100%;" @touchstart="touchStart" @touchmove="touchMove"
+        @touchend="touchEnd" />
+    </view>
+  </uni-card>
+</template>
+
+<script setup lang="ts">
+import LEchart from '@/components/l-echart/l-echart.vue';
+import { ref, watch, nextTick, onMounted } from 'vue'
+import {
+  endOfYear,
+  format,
+  startOfYear,
+  eachMonthOfInterval
+} from 'date-fns'
+import useChartOption from '@/hooks/chart-option'
+
+import { getProjectMonthCountStat } from '@/api/dashboard/project';
+// #ifdef MP
+const echarts = require('./static/echarts.min')
+// #endif
+
+// #ifndef MP
+// import * as echarts from 'echarts'
+// #endif
+
+
+
+const { mobile } = uni.getStorageSync('info_status')
+const proxy = getCurrentInstance()?.proxy
+const { $onLaunched } = proxy as any
+const chartRef = ref()
+const xAxisStat = ref<string[]>([])
+const totalCount = ref<number[]>([])
+const undoneCount = ref<number[]>([])
+const initData = async (data: any) => {
+  await nextTick()
+  if (chartRef.value) {
+    const myChart = await chartRef.value?.init(echarts)
+    myChart.setOption(data)
+  }
+}
+
+
+
+
+const { chartOption } = useChartOption(() => {
+  return {
+    animation: false,
+    grid: {
+      right: 0,
+      top: '20',
+      bottom: '60'
+    },
+    legend: {
+      bottom: 0,
+      icon: 'circle',
+      textStyle: {
+        color: '#4E5969'
+      }
+    },
+    xAxis: {
+      type: 'category',
+      data: xAxisStat.value,
+      axisLine: {
+        lineStyle: {
+          color: '#A9AEB8'
+        }
+      },
+      axisTick: {
+        show: true,
+        alignWithLabel: true,
+        lineStyle: {
+          color: '#86909C'
+        }
+      },
+      axisLabel: {
+        color: '#86909C'
+      }
+    },
+    yAxis: {
+      type: 'value',
+      axisLabel: {
+        color: '#86909C',
+        formatter(value: number, idx: number) {
+          if (idx === 0) return `${value}`
+          return `${value}个`
+        }
+      },
+      splitLine: {
+        lineStyle: {
+          color: '#E5E6EB'
+        }
+      }
+    },
+    tooltip: {
+      trigger: 'axis',
+      axisPointer: {
+        type: 'shadow'
+      },
+      confine: true
+    },
+    series: [
+      {
+        name: '总数',
+        data: totalCount.value,
+        type: 'bar',
+        barWidth: 10,
+        color: '#246EFF'
+      },
+      {
+        name: '未完成',
+        data: undoneCount.value,
+        barWidth: 10,
+        type: 'bar',
+        color: '#ff7d00'
+      }
+    ]
+  }
+})
+
+const getDataStat = async () => {
+  const beginAt = startOfYear(new Date())
+  const endAt = endOfYear(new Date())
+  const xValue = eachMonthOfInterval({
+    start: beginAt,
+    end: endAt
+  }).map(item => {
+    return format(item, 'yyyy-MM')
+  })
+  await $onLaunched
+  if(!mobile){
+            return
+        }
+  const { list } = await getProjectMonthCountStat({
+    begin_at: format(beginAt, 'yyyy-MM'),
+    end_at: format(endAt, 'yyyy-MM')
+  })
+  xAxisStat.value = xValue
+  xValue.map(item => {
+    const data = list.find(l => l.ts === item)
+    if (data) {
+      totalCount.value.push(data.amount)
+      undoneCount.value.push(data.undone)
+    } else {
+      totalCount.value.push(0)
+      undoneCount.value.push(0)
+    }
+    return item
+  })
+}
+
+onMounted(() => {
+  getDataStat()
+})
+
+watch(
+  () => chartOption.value,
+  () => {
+    initData(chartOption.value)
+  },
+  {
+    flush: 'post',
+    deep: true
+  }
+)
+
+const touchStart = (e: any) => {
+  chartRef.value?.touchStart(e)
+}
+
+const touchMove = (e: any) => {
+  chartRef.value?.touchMove(e)
+}
+
+
+const touchEnd = (e: any) => {
+  chartRef.value?.touchEnd(e)
+}
+// watch(() => props.options, (newValue) => {
+//   console.log(props.options)
+//   initData(newValue)
+// }, {
+//   deep: true
+// })
+
+</script>
+
+<style lang="scss" scoped></style>

File diff suppressed because it is too large
+ 49 - 0
src/pages/echart/static/echarts.min.js


+ 223 - 0
src/pages/home/index.vue

@@ -0,0 +1,223 @@
+<template>
+	<view class="page">
+		<view class="card-panel">
+			<uni-card margin="0px" padding="0px">
+				<view class="panel">
+					<view class="item" @click="redirectProjectPage()">
+						<text class="title">总项目数</text>
+						<view class="value p-color">
+							<text>{{ multi.prjCount.toLocaleString('en-US') }}</text>
+						</view>
+						<text class="unit">个</text>
+					</view>
+					<view class="item" @click="redirectCustomerPage()">
+						<text class="title">总客户数</text>
+						<view class="value c-color">
+							<text>{{ multi.customerCount.toLocaleString('en-US') }}</text>
+						</view>
+						<text class="unit">位</text>
+					</view>
+					<view class="item" @click="redirectWorkPage()">
+						<text class="title">总工作数</text>
+						<view class="value w-color">
+							<text>{{ multi.workCount.toLocaleString('en-US') }}</text>
+						</view>
+						<text class="unit">个</text>
+					</view>
+					<view class="item" @click="redirectContractPage()">
+						<text class="title">总合同数</text>
+						<view class="value ct-color">
+							<text>{{ multi.contractCount.toLocaleString('en-US') }}</text>
+						</view>
+						<text class="unit">份</text>
+					</view>
+				</view>
+			</uni-card>
+		</view>
+		<view class="card-plan-task">
+			<PlanTask />
+		</view>
+		<view class="card-plan-task">
+			<MyWork />
+		</view>
+		<view class="card-plan-task">
+			<ProjectCount />
+		</view>
+		<view class="card-plan-task">
+			<ProjectCategory />
+		</view>
+	</view>
+</template>
+
+
+<script setup lang="ts">
+import { getProjectMultiStat } from '@/api/dashboard/project';
+import PlanTask from '@/pages/echart/plan-task.vue'
+import MyWork from '@/pages/echart/my-work.vue'
+import ProjectCount from '@/pages/echart/project-count.vue'
+import ProjectCategory from '@/pages/echart/project-category.vue'
+const { mobile } = uni.getStorageSync('info_status')
+const multi = reactive<{
+	contractCount: number
+	customerCount: number
+	prjCount: number
+	workCount: number
+}>({
+	contractCount: 0,
+	customerCount: 0,
+	prjCount: 0,
+	workCount: 0
+})
+
+const proxy = getCurrentInstance()?.proxy
+const { $onLaunched } = proxy as any
+
+const getStat = async () => {
+	await $onLaunched
+	if (!mobile) {
+		uni.showModal({
+			title: '提示',
+			content: `暂未需登录`,
+			confirmText: '去登录',
+			cancelText: '取消',
+			success: (res: { confirm: boolean }) => {
+				const { confirm } = res;
+				if (confirm) {
+					return   uni.redirectTo({
+                        url: '/pages/authorize/index'
+                    })
+				}
+			}
+		})
+		return
+	}
+	console.log(mobile)
+	const data = await getProjectMultiStat()
+	multi.contractCount = data.contract_count
+	multi.customerCount = data.customer_count
+	multi.prjCount = data.prj_count
+	multi.workCount = data.work_count
+}
+
+const redirectWorkPage = async () => {
+	uni.switchTab({ url: '/pages/work/index' })
+}
+
+const redirectCustomerPage = async () => {
+	uni.navigateTo({ url: '/pages/profile/customer/index' })
+}
+
+const redirectProjectPage = async () => {
+	uni.switchTab({ url: '/pages/project/index' })
+}
+
+const redirectContractPage = async () => {
+	uni.navigateTo({ url: '/pages/profile/contract/index' })
+}
+
+onMounted(() => {
+	getStat()
+})
+</script>
+<script lang="ts">
+export default {
+	options: {
+		styleIsolation: 'apply-shared', //解除样式隔离
+	}
+
+}
+</script>
+<style lang="less" scoped>
+.page {
+	background: linear-gradient(180deg, #7fb4ec, #edf0f9);
+	padding: 5px 0 10px;
+
+	.card-panel {
+		margin: 10rpx;
+		border-radius: 12rpx;
+
+		.panel {
+			display: flex;
+			justify-content: center;
+
+			.item {
+				width: 225rpx;
+				padding: 15rpx 0;
+				display: flex;
+				flex-direction: column;
+				align-items: center;
+				border-radius: 12rpx;
+
+				.value {
+					width: 120rpx;
+					height: 120rpx;
+					margin: 10rpx 0;
+					display: flex;
+					justify-content: center;
+					align-items: center;
+					border-radius: 5px;
+
+					text {
+						color: #fff;
+						font-weight: bold;
+					}
+				}
+
+				.p-color {
+					background: #0fc3ff;
+				}
+
+				.c-color {
+					background: #ffcb1d;
+				}
+
+				.w-color {
+					background: #3bdcaa;
+				}
+
+				.ct-color {
+					background: #63c2de;
+				}
+
+				image {
+					width: 110rpx;
+					height: 110rpx;
+					margin: 10rpx 0;
+				}
+
+				.title {
+					font-weight: 500;
+					font-size: 13px;
+					color: #6e6b6bbf
+				}
+
+				.unit {
+					font-size: 10px;
+					color: #b2a9a6
+				}
+			}
+		}
+	}
+
+	.card-plan-task {
+		margin: auto;
+		margin: 10rpx;
+	}
+
+}
+
+:deep(.uni-nav-bar-text) {
+	font-size: 16px !important;
+	font-weight: bolder;
+	font-family: fangsong;
+}
+
+:deep(.uni-card .uni-card__content) {
+	font-size: inherit !important;
+}
+
+:deep(.uni-card__header-content-title) {
+	font-size: 13px !important;
+	color: #6e6b6bbf !important;
+}
+</style>

+ 106 - 0
src/pages/profile/archive/index.vue

@@ -0,0 +1,106 @@
+<template>
+	<Document :prj-list="prjList" :archived="true" :hiddenCategoryFilterItem="true"/>
+</template>
+
+<script lang="ts" setup>
+import Document from '@/pages/project/components/detail/document/document-layout.vue'
+import { getProjectList } from '@/api/project-center/info';
+
+const prjList = ref<{ id: string, name: string }[]>([])
+
+/**
+ * 获取项目列表
+ */
+const getProjects = async () => {
+	try {
+		const { list } = await getProjectList({ page_no: 1, page_size: 9999 })
+		prjList.value = list
+	} catch (e) {
+		console.error(e)
+	}
+}
+
+onLoad((options) => {
+	console.log(options)
+})
+
+onMounted(() => {
+	getProjects()
+})
+
+</script>
+<script lang="ts">
+export default {
+	options: {
+		styleIsolation: 'apply-shared', //解除样式隔离
+	}
+
+}
+</script>
+<style scoped lang="scss">
+.c-nav-bar {
+	&__content {
+		color: red;
+	}
+}
+
+
+.box-bg {
+	background-color: #F5F5F5;
+}
+
+
+.input-view {
+	/* #ifndef APP-PLUS-NVUE */
+	display: flex;
+	/* #endif */
+	flex-direction: row;
+	flex: 1;
+	flex-wrap: nowrap;
+	margin: 7px 3px 7px 0px;
+	line-height: 30px;
+}
+
+.input-uni-icon {
+	line-height: 30px;
+}
+
+.nav-bar-input {
+	height: 30px;
+	line-height: 30px;
+	/* #ifdef APP-PLUS-NVUE */
+	/* #endif */
+	padding: 0 5px;
+	font-size: 12px;
+	background-color: #f8f8f8;
+}
+
+:deep(.uni-searchbar__box-icon-clear) {
+	padding-top: 5px;
+}
+
+.uni-navbar {
+	&__header {
+		padding: 0 !important
+	}
+}
+
+:deep(.uni-searchbar) {
+	padding: 0;
+	width: 100%
+}
+
+:deep(.uni-searchbar__box) {
+	height: 30px !important;
+}
+
+:deep(.uni-navbar__header-container) {
+	padding: 0px !important;
+}
+
+
+:deep(.uni-searchbar__cancel) {
+	height: 30px;
+	line-height: 30px;
+}
+</style>

+ 135 - 0
src/pages/profile/contract/List.vue

@@ -0,0 +1,135 @@
+<template>
+    <view>
+        <uni-swipe-action ref="swiper_action">
+            <uni-swipe-action-item :right-options="options" v-for="item in contractList" :key="item.id"
+                @click="handlerRemoveContract(item)" class="swipe-action-item" :threshold="20">
+                <uni-card margin="5px" padding="0px" :title="item.name" :sub-title="item.prj_name || '-'" :extra="item.id"
+                    spacing="5px" thumbnail="../../../../../static/images/contract.png"
+                    @click="redirectContractDetailPage(item)">
+                    <view>
+                        <view class="card-actions">
+                            <text class="subtitle font-size-base">合同生效:<text class="paragraph font-size-sm">{{
+                                item.begin_at }}~{{
+                                        item.end_at }}</text></text>
+                            <text class="subtitle font-size-base">合同金额:<text class="font-size-base"
+                                    style="color: #007aff;">{{ item.amount }}万元</text></text>
+                        </view>
+                    </view>
+                </uni-card>
+            </uni-swipe-action-item>
+        </uni-swipe-action>
+    </view>
+</template>
+
+
+
+<script setup lang="ts">
+import { removeContract } from '@/api/project-center/contract';
+import { useEmitt } from '@/logics/mitt/useEmitt';
+import { Contract } from '@/types/contract';
+const swiper_action = ref()
+
+defineProps({
+    contractList: {
+        type: Array as PropType<Contract[]>,
+        required: true
+    }
+})
+const { emitter } = useEmitt()
+const options = [
+    {
+        text: '删除',
+        style: {
+            backgroundColor: '#dd524d',
+            margin: "5px 0"
+        }
+    }
+]
+
+// 跳转到详情页面
+const redirectContractDetailPage = (contract: Contract) => {
+    uni.navigateTo({ url: `/pages/profile/contract/detail?id=${contract.id}` })
+}
+
+// 删除合同
+const handlerRemoveContract = (contract: Contract) => {
+    uni.showModal({
+        title: '警告',
+        content: `是否删除合同:${contract.name}`,
+        success: async (res: { confirm: boolean }) => {
+            const { confirm } = res;
+            if (confirm) {
+                await removeContract(contract.id)
+                emitter.emit('get_contract_list')
+            }
+        }
+    })
+    swiper_action.value?.closeAll()
+}
+
+</script>
+
+
+
+<script lang="ts">
+export default {
+    options: {
+        styleIsolation: 'apply-shared', //解除样式隔离
+    }
+
+}
+</script>
+<style scoped lang="scss">
+:deep(.uni-swipe_button-group) {
+    top: 5px;
+    bottom: 5px;
+}
+
+.uni-mt-5 {
+    margin-top: 5px;
+}
+
+.border-top {
+    border-top: 1px #eee solid;
+}
+
+.card-actions {
+    display: flex;
+    flex-direction: row;
+    justify-content: space-between;
+    align-items: center;
+    height: 25px;
+    line-height: 25px;
+    font-size: $uni-font-size-sm;
+    padding: 5px 10px 0 10px;
+}
+
+.progress-actions {
+    display: flex;
+    flex-direction: row;
+    justify-content: flex-start;
+    align-items: center;
+    width: 100%;
+    height: 25px;
+    line-height: 25px;
+    font-size: $uni-font-size-sm;
+    padding: 5px 10px 0 10px;
+}
+
+.font-size-sm {
+    font-size: $uni-font-size-sm;
+}
+
+.font-size-base {
+    font-size: 26rpx;
+}
+
+.paragraph {
+    color: #b2a9a6;
+    font-weight: 600;
+}
+
+.subtitle {
+    color: #b2a9a6;
+}
+</style>

+ 658 - 0
src/pages/profile/contract/add-contract.vue

@@ -0,0 +1,658 @@
+<template>
+
+  <view class="example">
+    <!-- 展示不同的排列方式 -->
+    <uni-forms ref="contractFormRef" :modelValue="contractFormData" :rules="rules" validateTrigger="submit"
+      label-position="top">
+      <uni-forms-item label="合同名称" required label-width="90px" name="name">
+        <uni-easyinput v-model="contractFormData.name" placeholder="请输入合同名称" />
+      </uni-forms-item>
+      <uni-forms-item label="合同客户" required label-width="90px" name="customer_id">
+        <view class="custom-select-class" @click="showTreeSelectModal(customerList, 0, [contractFormData.customer_id])">
+          <view class="custom-select-content">
+            <text v-if="contractFormData.customer_id">{{ contractFormData.customer_name }}</text>
+            <text v-else style="color:#909399;font-size: 12px;">请选择合同客户</text>
+          </view>
+          <uni-icons :size="14" color="#909399" type="down" style=" padding: 0 5px;"></uni-icons>
+        </view>
+      </uni-forms-item>
+      <uni-forms-item label="合同项目" label-width="90px" name="prj_id">
+        <view class="custom-select-class" @click="showTreeSelectModal(projectList, 1, [contractFormData.prj_id])">
+          <view class="custom-select-content">
+            <text v-if="contractFormData.prj_id">{{ contractFormData.prj_name }}</text>
+            <text v-else style="color:#909399;font-size: 12px;">请选择合同项目</text>
+          </view>
+          <uni-icons :size="14" color="#909399" type="down" style=" padding: 0 5px;"></uni-icons>
+        </view>
+      </uni-forms-item>
+      <uni-forms-item label="合同金额(万元)" required label-width="130px" name="amount">
+        <view class="uni-easyinput">
+          <view class="uni-easyinput__content is-input-border" :style="{
+            borderColor: is_foucs ? '#2979ff' : '',
+            background: is_foucs ? '#fff' : ''
+          }">
+            <input class="uni-easyinput__content-input" v-model="contractFormData.amount"
+              placeholder-class="uni-easyinput__placeholder-class" :focus="is_foucs" type="digit" placeholder="输入合同金额"
+              @focus="is_foucs = true" @blur="handlerAmountBlur()">
+          </view>
+        </view>
+        <!-- <uni-datetime-picker type="date" v-model="contractFormData." /> -->
+      </uni-forms-item>
+      <uni-forms-item label="合同签订人" required label-width="90px" name="handler_id">
+        <view class="custom-select-class" @click="showTreeSelectModal(accountList, 2, [contractFormData.handler_id])">
+          <view class="custom-select-content">
+            <text v-if="contractFormData.handler_id">{{ contractFormData.handler_name }}</text>
+            <text v-else style="color:#909399;font-size: 12px;">请选择合同签订人</text>
+          </view>
+          <uni-icons :size="14" color="#909399" type="down" style=" padding: 0 5px;"></uni-icons>
+        </view>
+      </uni-forms-item>
+      <uni-forms-item label="签订日期" required label-width="90px" name="signed_at">
+        <uni-datetime-picker type="date" v-model="contractFormData.signed_at" />
+      </uni-forms-item>
+      <uni-forms-item label="合同起始日" required label-width="90px" name="begin_at">
+        <uni-datetime-picker type="date" v-model="contractFormData.begin_at" />
+      </uni-forms-item>
+      <uni-forms-item label="合同结束日" label-width="90px" name="end_at">
+        <uni-datetime-picker type="date" v-model="contractFormData.end_at" />
+      </uni-forms-item>
+      <uni-forms-item label="合同摘要" label-width="90px" name="intro">
+        <uni-easyinput type="textarea" v-model="contractFormData.comment" placeholder="请输入合同摘要" />
+      </uni-forms-item>
+      <uni-section title="收款约定" type="line">
+        <template v-slot:right>
+          <view style="color: #007aff;display: flex;align-items: center;" @click="redirectAddContractPaymentPage()">
+            <uni-icons :size="14" color="#007aff" type="plusempty"></uni-icons>
+            <text>添加</text>
+          </view>
+        </template>
+      </uni-section>
+      <uni-swipe-action ref="swiper_action">
+        <uni-swipe-action-item :right-options="options" class="swipe-action-item" :threshold="20"
+          v-for="(item, index) in contractFormData.payments" :key="index"
+          @click="(e: any) => handlerSwipeAction(e, item, index)">
+          <uni-card margin="5px" padding="0px" :title="item.condition" :extra="`${item.percent}%`" spacing="5px"
+            :sub-title="item.memo" thumbnail="./../../../../../static/images/payment.png">
+            <view>
+              <view class="card-actions">
+                <text class="subtitle font-size-base">收款金额:<text class="font-size-base" style="color: #007aff;">{{
+                  Math.round(
+                    (contractFormData.amount || 0) *
+                    item.percent *
+                    100
+                  ) / 10000
+                    }} 万元</text></text>
+                <text class="subtitle font-size-base">收款日期:<text class="paragraph font-size-sm">{{ item.expect_pay_date
+                    }}</text></text>
+
+              </view>
+            </view>
+          </uni-card>
+        </uni-swipe-action-item>
+      </uni-swipe-action>
+    </uni-forms>
+    <view style="margin: 10px 0;display: flex;">
+      <custom-button width="100%" type="filled" color="#dd524d" @click="restForm()"
+        style="margin-right: 5px;flex:1">重置</custom-button>
+      <custom-button width="100%" type="filled" color="#007aff" style="flex:1;"
+        @click="handlerAddContract()">提交</custom-button>
+    </view>
+    <next-tree :ifSearch="true" :border="true" labelKey="name" ref="treeSelectRef" :funcMode="'radio'"
+      :treeData="treeSelectData" @confirm="confirmTreeSelect" @cancel="cancelTreeSelect"></next-tree>
+  </view>
+
+</template>
+
+<script lang="ts" setup>
+import { getPrjTypeList } from '@/api/base/base';
+import { getStaffList } from '@/api/biz/staff';
+import nextTree from '@/components/next-tree/next-tree.vue'
+import { DomainTree } from '@/types/domain';
+import { useEmitt } from '@/logics/mitt/useEmitt';
+import { getProjectList } from '@/api/project-center/info';
+import { getCustomerList } from '@/api/biz/customer';
+import { AddContractReqParams, Payment } from '@/types/contract';
+import { addContract } from '@/api/project-center/contract';
+
+const swiper_action = ref()
+const treeDataType = ref<number>(0)
+const accountList = ref<{ id: string, name: string }[]>([])
+const customerList = ref<{ id: string, name: string }[]>([])
+const projectList = ref<{ id: string, name: string }[]>([])
+const treeSelectData = ref<DomainTree[]>([]) // 区域树
+const contractFormRef = ref()
+const is_foucs = ref(false)
+const treeSelectRef = ref()
+const { emitter } = useEmitt()
+
+
+const options = [
+  {
+    text: '修改',
+    style: {
+      backgroundColor: '#4cd964',
+      margin: "5px 0"
+    }
+  },
+  {
+    text: '删除',
+    style: {
+      backgroundColor: '#dd524d',
+      margin: "5px 0"
+    }
+  }
+]
+
+const rules = {
+  // 对name字段进行必填验证
+  name: {
+    // name 字段的校验规则
+    rules: [
+      // 校验 name 不能为空
+      {
+        required: true,
+        errorMessage: '名称不能为空',
+      }
+    ]
+  },
+  customer_id: {
+    // name 字段的校验规则
+    rules: [
+      // 校验 name 不能为空
+      {
+        required: true,
+        errorMessage: '合同客户不能为空',
+      }
+    ]
+  },
+  amount: {
+    // name 字段的校验规则
+    rules: [
+      // 校验 name 不能为空
+      {
+        required: true,
+        errorMessage: '合同金额不能为空',
+      }
+    ]
+  },
+  handler_id: {
+    // name 字段的校验规则
+    rules: [
+      // 校验 name 不能为空
+      {
+        required: true,
+        errorMessage: '合同签订人不能为空',
+      }
+    ]
+  },
+  signed_at: {
+    // name 字段的校验规则
+    rules: [
+      // 校验 name 不能为空
+      {
+        required: true,
+        errorMessage: '签订日期不能为空',
+      }
+    ]
+  },
+  begin_at: {
+    // name 字段的校验规则
+    rules: [
+      // 校验 name 不能为空
+      {
+        required: true,
+        errorMessage: '合同起始日不能为空',
+      }
+    ]
+  }
+}
+
+const contractFormData = ref<{
+  name?: string
+  customer_id?: string // 
+  customer_name?: string // 
+  prj_id?: string // 
+  prj_name?: string // 
+  handler_id?: string // 
+  handler_name?: string //
+  amount?: number // 
+  signed_at?: string
+  begin_at?: string
+  end_at?: string
+  comment?: string
+  payments?: Payment[]
+}>({})
+
+
+
+function checkedTreeData(treeData: any, selectIds?: any) {
+  treeData.map((item: any) => {
+    if (selectIds.indexOf(item.id) !== -1) {
+      item.checked = true
+    } else {
+      item.checked = false
+    }
+    if (item.children && item.children.length) {
+      checkedTreeData(item.children, selectIds)
+    }
+  })
+}
+
+const showTreeSelectModal = async (treeData: any, type: number, selectId?: any) => {
+  treeDataType.value = type;
+  treeSelectData.value = treeData;
+  checkedTreeData(treeSelectData.value, selectId)
+  unref(treeSelectRef).showTree = true
+}
+
+// 只保留到6位
+const handlerAmountBlur = () => {
+  is_foucs.value = false
+  if (![undefined, null, '', 0].includes(contractFormData.value.amount)) {
+    contractFormData.value.amount = Math.round(Number(contractFormData.value.amount) * 10000) / 10000
+  }
+}
+
+/**
+* 获取用户列表
+*/
+const getAccounts = async () => {
+  try {
+    const { list } = await getStaffList({ page_no: 1, page_size: 9999 })
+    accountList.value = list
+  } catch (e) {
+    console.error(e)
+  }
+}
+
+
+
+
+// 获取客户列表
+const getCustomers = async () => {
+  try {
+    const params = {
+      page_no: 1,
+      page_size: 9999,
+    }
+    const { list } = await getCustomerList(params)
+    customerList.value = list
+  } catch (e) {
+    console.error(e)
+  }
+}
+
+const getProjects = async (name?: string) => {
+  try {
+    const { list } = await getProjectList({ page_no: 1, page_size: 9999 })
+    projectList.value = list
+  } catch (e) {
+    console.error(e)
+  }
+}
+
+
+
+function confirmTreeSelect(list: any) {
+  switch (treeDataType.value) {
+    case 0:
+      if (list.length > 0) {
+        contractFormData.value.customer_id = list[0].id
+        contractFormData.value.customer_name = list[0].name
+      } else {
+        contractFormData.value.customer_id = undefined
+        contractFormData.value.customer_name = undefined
+      }
+      break;
+    case 1:
+      if (list.length > 0) {
+        contractFormData.value.prj_id = list[0].id
+        contractFormData.value.prj_name = list[0].name
+      } else {
+        contractFormData.value.prj_id = undefined
+        contractFormData.value.prj_name = undefined
+      }
+      break;
+    case 2:
+      if (list.length > 0) {
+        contractFormData.value.handler_id = list[0].id
+        contractFormData.value.handler_name = list[0].name
+      } else {
+        contractFormData.value.handler_id = undefined
+        contractFormData.value.handler_name = undefined
+      }
+      break;
+    default:
+      break;
+  }
+  treeSelectData.value = [];
+}
+
+
+const cancelTreeSelect = () => {
+  treeSelectData.value = []
+  // checkedTreeData(unref(tr), []) 
+}
+
+function calculateRemainingAmount(payments: Payment[], excludeIndex?: number): number {
+  const totalPaidPercentage = payments.reduce((total, payment, index) => {
+    // 如果当前的下标等于要排除的下标,则不累加其percent值
+    if (index === excludeIndex) {
+      return total;
+    }
+    return total + payment.percent;
+  }, 0);
+  const percent = 100 - totalPaidPercentage
+  return Math.round(percent * 100) / 100
+}
+
+//跳转添加收款约定页面
+const redirectAddContractPaymentPage = () => {
+  if ([undefined, null, '', 0].includes(contractFormData.value.amount)) {
+    uni.showToast({
+      icon: 'error',
+      title: '合同金额不足'
+    })
+    return
+  }
+  const amount = Number(contractFormData.value.amount)
+  const remainingPercentage = calculateRemainingAmount(contractFormData.value.payments || [])
+  if (remainingPercentage === 0) {
+    uni.showToast({
+      icon: 'none',
+      title: '收款比例已用完'
+    })
+    return
+  }
+  uni.navigateTo({
+    url: `/pages/profile/contract/add-payment?total_amount=${amount}&remaining_percentage=${remainingPercentage}`
+  })
+}
+
+// swipe action操作。修改约定或删除收款约定
+const handlerSwipeAction = (e: { index: number }, payment: Payment, pIndex: number) => {
+  swiper_action.value?.closeAll()
+  const { index } = e;
+  const isEdit = index === 0;
+  const isRemove = index === 1;
+  if (isRemove) {
+    contractFormData.value.payments?.splice(pIndex, 1)
+    return
+  }
+  if (isEdit) {  // 跳转修改收款约定页面
+    const amount = Number(contractFormData.value.amount)
+    const remainingPercentage = calculateRemainingAmount(contractFormData.value.payments || [], pIndex)
+    uni.navigateTo({
+      url: `/pages/profile/contract/edit-payment?total_amount=${amount}&remaining_percentage=${remainingPercentage}`,
+      success: () => {
+        emitter.emit('editor_payment_item', {
+          index: pIndex,
+          payment: payment
+        })
+      }
+    })
+  }
+}
+
+const handlerAddContract = async () => {
+  const isValid = await contractFormRef.value?.validate()
+  if (!isValid) {
+    return
+  }
+  const { name, customer_id, prj_id, handler_id, signed_at, begin_at, end_at, comment, amount, payments } = contractFormData.value
+  const params: AddContractReqParams = {
+    name: name!,
+    customer_id: customer_id!,
+    prj_id,
+    handler_id: handler_id!,
+    signed_at: signed_at!,
+    begin_at: begin_at!,
+    end_at,
+    comment,
+    amount: Number(amount)!,
+    payments: payments
+  }
+
+  await addContract(params)
+  emitter.emit('get_contract_list')
+  uni.showModal({
+    title: '添加成功',
+    content: `是否返回合同管理页面`,
+    success: (res: { confirm: boolean }) => {
+      const { confirm } = res;
+      if (confirm) {
+        return uni.navigateBack()
+      }
+    }
+  })
+}
+
+
+const restForm = async () => {
+  contractFormData.value = {}
+  contractFormRef.value?.clearValidate()
+}
+
+// 监听子组件增加,修改,删除事件,更新列表
+useEmitt({
+  name: 'add_payment',
+  callback: (payment: Payment) => {
+    const payments = contractFormData.value.payments || []
+    payments.push(payment)
+    contractFormData.value.payments = payments
+  }
+})
+
+useEmitt({
+  name: 'modify_payment',
+  callback: (e: { index: number, payment: Payment }) => {
+    const { index, payment } = e;
+    const payments = contractFormData.value.payments || []
+    payments.splice(index, 1, payment)
+  }
+})
+
+
+
+onMounted(() => {
+  getAccounts()
+  getProjects()
+  getCustomers()
+})
+
+</script>
+<script lang="ts">
+export default {
+  options: {
+    styleIsolation: 'apply-shared', //解除样式隔离
+  }
+
+}
+</script>
+<style scoped lang="scss">
+.example {
+  padding: 15px;
+  background-color: #fff;
+}
+
+:deep(.is-disabled) {
+  color: #333;
+
+}
+
+.custom-select-class {
+  border-color: #e5e5e5;
+  background-color: transparent;
+  display: flex;
+  box-sizing: border-box;
+  flex-direction: row;
+  align-items: center;
+  border: 1px solid #dcdfe6;
+  border-radius: 4px;
+}
+
+.custom-select-content {
+  width: auto;
+  position: relative;
+  overflow: hidden;
+  flex: 1;
+  line-height: 1;
+  font-size: 14px;
+  line-height: 35px;
+  padding-left: 10px;
+  height: 35px;
+
+}
+
+.tag-container {
+  &__item {
+    padding: 0 6px 0 0;
+  }
+}
+
+.uni-easyinput {
+  /* #ifndef APP-NVUE */
+  width: 100%;
+  /* #endif */
+  flex: 1;
+  position: relative;
+  text-align: left;
+  color: #333;
+  font-size: 14px;
+}
+
+.uni-easyinput__content {
+  flex: 1;
+  /* #ifndef APP-NVUE */
+  width: 100%;
+  display: flex;
+  box-sizing: border-box;
+  // min-height: 36px;
+  /* #endif */
+  flex-direction: row;
+  align-items: center;
+  // 处理border动画刚开始显示黑色的问题
+  border-color: #fff;
+  transition-property: border-color;
+  transition-duration: 0.3s;
+}
+
+.is-input-border {
+  display: flex;
+  box-sizing: border-box;
+  flex-direction: row;
+  align-items: center;
+  border: 1px solid #dcdfe6;
+  border-radius: 4px;
+}
+
+.uni-easyinput__content-input {
+  /* #ifndef APP-NVUE */
+  width: auto;
+  /* #endif */
+  position: relative;
+  overflow: hidden;
+  flex: 1;
+  line-height: 1;
+  font-size: 14px;
+  padding-left: 10px;
+  height: 35px;
+  // min-height: 36px;
+
+  /*ifdef H5*/
+  & ::-ms-reveal {
+    display: none;
+  }
+
+  & ::-ms-clear {
+    display: none;
+  }
+
+  & ::-o-clear {
+    display: none;
+  }
+
+  /*endif*/
+}
+
+.content-clear-icon {
+  padding: 0 5px;
+}
+
+:deep(.uni-easyinput__placeholder-class) {
+  color: #999;
+  font-size: 12px;
+  // font-weight: 200;
+}
+
+:deep(.uni-section .uni-section-header) {
+  padding: 12px 0
+}
+
+:deep(.uni-swipe_button-group) {
+  top: 5px;
+  bottom: 5px;
+}
+
+.uni-mt-5 {
+  margin-top: 5px;
+}
+
+.border-top {
+  border-top: 1px #eee solid;
+}
+
+.card-actions {
+  display: flex;
+  flex-direction: row;
+  justify-content: space-between;
+  align-items: center;
+  height: 25px;
+  line-height: 25px;
+  font-size: $uni-font-size-sm;
+  padding: 5px 10px 0 10px;
+}
+
+.progress-actions {
+  display: flex;
+  flex-direction: row;
+  justify-content: flex-start;
+  align-items: center;
+  width: 100%;
+  height: 25px;
+  line-height: 25px;
+  font-size: $uni-font-size-sm;
+  padding: 5px 10px 0 10px;
+}
+
+.font-size-sm {
+  font-size: $uni-font-size-sm;
+}
+
+.font-size-base {
+  font-size: 26rpx;
+}
+
+.paragraph {
+  color: #b2a9a6;
+  font-weight: 600;
+}
+
+.subtitle {
+  color: #b2a9a6;
+}
+
+:deep(.uni-ellipsis) {
+  overflow: hidden;
+  text-overflow: ellipsis;
+  display: -webkit-box;
+  -webkit-line-clamp: 3;
+  -webkit-box-orient: vertical;
+}
+
+:deep(.uni-card__header-extra-text) {
+  font-size: 16px !important;
+  color: #3a3a3a !important;
+  font-weight: bold !important;
+}
+</style>

+ 307 - 0
src/pages/profile/contract/add-payment.vue

@@ -0,0 +1,307 @@
+<template>
+
+  <view class="example">
+    <!-- 展示不同的排列方式 -->
+    <uni-forms ref="paymentFormRef" :modelValue="paymentFormData" :rules="rules" validateTrigger="submit"
+      label-position="top">
+      <uni-forms-item label="合同总金额(万元)" required label-width="140px" name="total_amount">
+        <view class="custom-select-class form-item-disabled">
+          <view class="custom-select-content">
+            <text style="color:#909399;font-size: 12px;">{{ paymentFormData.total_amount || 0 }}</text>
+          </view>
+          <!-- <uni-icons :size="14" color="#909399" type="down" style=" padding: 0 5px;"></uni-icons> -->
+        </view>
+      </uni-forms-item>
+      <uni-forms-item label="合同剩余收款比例(%)" required label-width="160px" name="remaining_percentage">
+        <view class="custom-select-class form-item-disabled">
+          <view class="custom-select-content">
+            <text style="color:#909399;font-size: 12px;">{{ paymentFormData.remaining_percentage || 0 }}</text>
+          </view>
+          <!-- <uni-icons :size="14" color="#909399" type="down" style=" padding: 0 5px;"></uni-icons> -->
+        </view>
+      </uni-forms-item>
+      <uni-forms-item label="收款条件" required label-width="90px" name="condition">
+        <uni-easyinput v-model="paymentFormData.condition" placeholder="请输入收款条件" />
+      </uni-forms-item>
+      <uni-forms-item label="收款比例(%)" required label-width="140px" name="percent">
+        <view class="uni-easyinput">
+          <view class="uni-easyinput__content is-input-border" :style="{
+            borderColor: is_foucs ? '#2979ff' : '',
+            background: is_foucs ? '#fff' : ''
+          }">
+            <input class="uni-easyinput__content-input" v-model="paymentFormData.percent"
+              placeholder-class="uni-easyinput__placeholder-class" :focus="is_foucs" type="digit" placeholder="输入收款比例"
+              @focus="is_foucs = true" @blur="handlerPercentBlur()">
+          </view>
+        </view>
+      </uni-forms-item>
+      <uni-forms-item label="收款金额(万元)" required label-width="130px" name="amount">
+        <view class="custom-select-class form-item-disabled">
+          <view class="custom-select-content">
+            <text style="color:#909399;font-size: 12px;">{{ paymentFormData.amount || 0 }}</text>
+          </view>
+        </view>
+      </uni-forms-item>
+      <uni-forms-item label="收款日期" required label-width="90px" name="expect_pay_date">
+        <uni-datetime-picker type="date" v-model="paymentFormData.expect_pay_date" />
+      </uni-forms-item>
+      <uni-forms-item label="收款备注" label-width="90px" name="memo">
+        <uni-easyinput type="textarea" v-model="paymentFormData.memo" placeholder="收款备注" />
+      </uni-forms-item>
+    </uni-forms>
+    <view style="margin: 10px 0;display: flex;">
+      <custom-button width="100%" type="filled" color="#dd524d" @click="restForm()"
+        style="margin-right: 5px;flex:1">重置</custom-button>
+      <custom-button width="100%" type="filled" color="#007aff" style="flex:1;"
+        @click="handlerAddPayment()">提交</custom-button>
+    </view>
+  </view>
+
+</template>
+
+<script lang="ts" setup>
+import { useEmitt } from '@/logics/mitt/useEmitt';
+import { Payment } from '@/types/contract';
+const paymentFormRef = ref()
+const is_foucs = ref(false)
+const { emitter } = useEmitt()
+
+
+
+const rules = {
+  // 对name字段进行必填验证
+  condition: {
+    // name 字段的校验规则
+    rules: [
+      // 校验 name 不能为空
+      {
+        required: true,
+        errorMessage: '不能为空',
+      }
+    ]
+  },
+  percent: {
+    // name 字段的校验规则
+    rules: [
+      // 校验 name 不能为空
+      {
+        required: true,
+        errorMessage: '收款比例不能为空',
+      }, {
+        validateFunction: function (rule: any, value: any, data: any, callback: any) {
+          if (value > data.remaining_percentage) {
+            callback('收款比例不能大于合同剩余收款比例')
+          }
+          return true
+        }
+      }]
+  },
+  expect_pay_date: {
+    // name 字段的校验规则
+    rules: [
+      // 校验 name 不能为空
+      {
+        required: true,
+        errorMessage: '不能为空',
+      }
+    ]
+  }
+}
+
+const paymentFormData = ref<{
+  total_amount?: number
+  remaining_percentage?: number
+  condition?: string
+  percent?: number // 收款比例
+  amount?: number
+  expect_pay_date?: string // 预计收款日
+  memo?: string // 备注
+}>({})
+
+// 收款比例只保存
+const handlerPercentBlur = () => {
+  is_foucs.value = false
+  if (![undefined, null, '', 0].includes(paymentFormData.value.percent)) {
+    paymentFormData.value.percent = Math.round(Number(paymentFormData.value.percent) * 100) / 100
+    paymentFormData.value.amount =  Math.round( (paymentFormData.value.total_amount || 0) * paymentFormData.value.percent * 100  ) / 10000
+  }
+}
+
+const handlerAddPayment = async () => {
+  const isValid = await paymentFormRef.value?.validate()
+  if (!isValid) {
+    return
+  }
+
+  emitter.emit('add_payment', {
+    condition: paymentFormData.value.condition,
+    percent: Number(paymentFormData.value.percent),
+    expect_pay_date: paymentFormData.value.expect_pay_date,
+    memo: paymentFormData.value.memo,
+  })
+  return uni.navigateBack()
+}
+
+
+const restForm = async () => {
+  paymentFormData.value = {}
+  paymentFormRef.value?.clearValidate()
+}
+
+// 监听子组件增加,修改,删除事件,更新列表
+useEmitt({
+  name: 'editor_payment_item',
+  callback: (payment: Payment[]) => {
+    console.log(payment)
+  }
+})
+
+onLoad((options) => {
+  console.log(options)
+  paymentFormData.value.total_amount = Number(options?.total_amount)
+  paymentFormData.value.remaining_percentage = Number(options?.remaining_percentage)
+})
+
+</script>
+<script lang="ts">
+export default {
+  options: {
+    styleIsolation: 'apply-shared', //解除样式隔离
+  }
+
+}
+</script>
+<style scoped lang="scss">
+.example {
+  padding: 15px;
+  background-color: #fff;
+}
+
+:deep(.is-disabled) {
+  color: #333;
+
+}
+
+.custom-select-class {
+  border-color: #e5e5e5;
+  background-color: transparent;
+  display: flex;
+  box-sizing: border-box;
+  flex-direction: row;
+  align-items: center;
+  border: 1px solid #dcdfe6;
+  border-radius: 4px;
+}
+
+.custom-select-content {
+  width: auto;
+  position: relative;
+  overflow: hidden;
+  flex: 1;
+  line-height: 1;
+  font-size: 14px;
+  line-height: 35px;
+  padding-left: 10px;
+  height: 35px;
+
+}
+
+.tag-container {
+  &__item {
+    padding: 0 6px 0 0;
+  }
+}
+
+.uni-easyinput {
+  /* #ifndef APP-NVUE */
+  width: 100%;
+  /* #endif */
+  flex: 1;
+  position: relative;
+  text-align: left;
+  color: #333;
+  font-size: 14px;
+}
+
+.uni-easyinput__content {
+  flex: 1;
+  /* #ifndef APP-NVUE */
+  width: 100%;
+  display: flex;
+  box-sizing: border-box;
+  /* #endif */
+  flex-direction: row;
+  align-items: center;
+  border-color: #fff;
+  transition-property: border-color;
+  transition-duration: 0.3s;
+}
+
+.is-input-border {
+  display: flex;
+  box-sizing: border-box;
+  flex-direction: row;
+  align-items: center;
+  border: 1px solid #dcdfe6;
+  border-radius: 4px;
+}
+
+.uni-easyinput__content-input {
+  /* #ifndef APP-NVUE */
+  width: auto;
+  /* #endif */
+  position: relative;
+  overflow: hidden;
+  flex: 1;
+  line-height: 1;
+  font-size: 14px;
+  padding-left: 10px;
+  height: 35px;
+
+  /*ifdef H5*/
+  & ::-ms-reveal {
+    display: none;
+  }
+
+  & ::-ms-clear {
+    display: none;
+  }
+
+  & ::-o-clear {
+    display: none;
+  }
+
+}
+
+.content-clear-icon {
+  padding: 0 5px;
+}
+
+:deep(.uni-easyinput__placeholder-class) {
+  color: #999;
+  font-size: 12px;
+}
+
+:deep(.uni-section .uni-section-header) {
+  padding: 12px 0
+}
+
+:deep(.uni-swipe_button-group) {
+  top: 5px;
+  bottom: 5px;
+}
+
+.uni-mt-5 {
+  margin-top: 5px;
+}
+
+.border-top {
+  border-top: 1px #eee solid;
+}
+
+.form-item-disabled {
+  background: #f7f6f6;
+  cursor: not-allowed;
+  color: #d5d5d5
+}
+</style>

+ 45 - 0
src/pages/profile/contract/detail.vue

@@ -0,0 +1,45 @@
+<template>
+  <ContractDetail v-if="contract_id" :contract_id="contract_id" :account-list="accountList"
+    :customer-list="customerList" />
+</template>
+
+<script lang="ts" setup>
+
+import { getCustomerList } from '@/api/biz/customer';
+import { getStaffList } from '@/api/biz/staff';
+import ContractDetail from '@/pages/project/components/detail/contract-detail.vue'
+import { Customer } from '@/types/customer';
+
+const contract_id = ref();
+const accountList = ref<{ id: string, name: string }[]>()
+const customerList = ref<Customer[]>()
+
+// 获取客户列表
+const getCustomers = async (name?: string) => {
+  try {
+    const params = {
+      page_no: 1,
+      page_size: 9999,
+      name
+    }
+    const { list } = await getCustomerList(params)
+    customerList.value = list
+  } catch (e) {
+    console.error(e)
+  }
+}
+
+// 获取主责人账号信息
+const getAccounts = async () => {
+  const { list } = await getStaffList({ page_no: 1, page_size: 9999 })
+  accountList.value = list
+}
+
+onLoad(async (option) => {
+  contract_id.value = option?.id
+  getAccounts()
+  getCustomers()
+});
+</script>
+
+<style scoped></style>

+ 316 - 0
src/pages/profile/contract/edit-payment.vue

@@ -0,0 +1,316 @@
+<template>
+
+  <view class="example">
+    <!-- 展示不同的排列方式 -->
+    <uni-forms ref="paymentFormRef" :modelValue="paymentFormData" :rules="rules" validateTrigger="submit"
+      label-position="top">
+      <uni-forms-item label="合同总金额(万元)" required label-width="140px" name="total_amount">
+        <view class="custom-select-class form-item-disabled">
+          <view class="custom-select-content">
+            <text style="color:#909399;font-size: 12px;">{{ paymentFormData.total_amount || 0 }}</text>
+          </view>
+          <!-- <uni-icons :size="14" color="#909399" type="down" style=" padding: 0 5px;"></uni-icons> -->
+        </view>
+      </uni-forms-item>
+      <uni-forms-item label="合同剩余收款比例(%)" required label-width="160px" name="remaining_percentage">
+        <view class="custom-select-class form-item-disabled">
+          <view class="custom-select-content">
+            <text style="color:#909399;font-size: 12px;">{{ paymentFormData.remaining_percentage || 0 }}</text>
+          </view>
+          <!-- <uni-icons :size="14" color="#909399" type="down" style=" padding: 0 5px;"></uni-icons> -->
+        </view>
+      </uni-forms-item>
+      <uni-forms-item label="收款条件" required label-width="90px" name="condition">
+        <uni-easyinput v-model="paymentFormData.condition" placeholder="请输入收款条件" />
+      </uni-forms-item>
+      <uni-forms-item label="收款比例(%)" required label-width="140px" name="percent">
+        <view class="uni-easyinput">
+          <view class="uni-easyinput__content is-input-border" :style="{
+            borderColor: is_foucs ? '#2979ff' : '',
+            background: is_foucs ? '#fff' : ''
+          }">
+            <input class="uni-easyinput__content-input" v-model="paymentFormData.percent"
+              placeholder-class="uni-easyinput__placeholder-class" :focus="is_foucs" type="digit" placeholder="输入收款比例"
+              @focus="is_foucs = true" @blur="handlerPercentBlur()">
+          </view>
+        </view>
+      </uni-forms-item>
+      <uni-forms-item label="收款金额(万元)" required label-width="130px" name="amount">
+        <view class="custom-select-class form-item-disabled">
+          <view class="custom-select-content">
+            <text style="color:#909399;font-size: 12px;">{{ paymentFormData.amount || 0 }}</text>
+          </view>
+        </view>
+      </uni-forms-item>
+      <uni-forms-item label="收款日期" required label-width="90px" name="expect_pay_date">
+        <uni-datetime-picker type="date" v-model="paymentFormData.expect_pay_date" />
+      </uni-forms-item>
+      <uni-forms-item label="收款备注" label-width="90px" name="memo">
+        <uni-easyinput type="textarea" v-model="paymentFormData.memo" placeholder="收款备注" />
+      </uni-forms-item>
+    </uni-forms>
+    <view style="margin: 10px 0;display: flex;">
+      <custom-button width="100%" type="filled" color="#dd524d" @click="restForm()"
+        style="margin-right: 5px;flex:1">重置</custom-button>
+      <custom-button width="100%" type="filled" color="#4cd964" style="flex:1;"
+        @click="handlerModifyPayment()">修改</custom-button>
+    </view>
+  </view>
+
+</template>
+
+<script lang="ts" setup>
+import { useEmitt } from '@/logics/mitt/useEmitt';
+import { Payment } from '@/types/contract';
+const paymentFormRef = ref()
+const is_foucs = ref(false)
+const { emitter } = useEmitt()
+
+
+
+const rules = {
+  // 对name字段进行必填验证
+  condition: {
+    // name 字段的校验规则
+    rules: [
+      // 校验 name 不能为空
+      {
+        required: true,
+        errorMessage: '不能为空',
+      }
+    ]
+  },
+  percent: {
+    // name 字段的校验规则
+    rules: [
+      // 校验 name 不能为空
+      {
+        required: true,
+        errorMessage: '收款比例不能为空',
+      }, {
+        validateFunction: function (rule: any, value: any, data: any, callback: any) {
+          if (value > data.remaining_percentage) {
+            callback('收款比例不能大于合同剩余收款比例')
+          }
+          return true
+        }
+      }]
+  },
+  expect_pay_date: {
+    // name 字段的校验规则
+    rules: [
+      // 校验 name 不能为空
+      {
+        required: true,
+        errorMessage: '不能为空',
+      }
+    ]
+  }
+}
+
+const paymentFormData = ref<{
+  eIndex?: number
+  total_amount?: number
+  remaining_percentage?: number
+  condition?: string
+  percent?: number // 收款比例
+  amount?: number
+  expect_pay_date?: string // 预计收款日
+  memo?: string // 备注
+}>({})
+
+// 收款比例只保存
+const handlerPercentBlur = () => {
+  is_foucs.value = false
+  if (![undefined, null, '', 0].includes(paymentFormData.value.percent)) {
+    paymentFormData.value.percent = Math.round(Number(paymentFormData.value.percent) * 100) / 100
+    paymentFormData.value.amount = Math.round((paymentFormData.value.total_amount || 0) * paymentFormData.value.percent * 100) / 10000
+  }
+}
+
+const handlerModifyPayment = async () => {
+  const isValid = await paymentFormRef.value?.validate()
+  if (!isValid) {
+    return
+  }
+  // 发送修改payment信息
+  emitter.emit('modify_payment', {
+    index: paymentFormData.value.eIndex,
+    payment: {
+      condition: paymentFormData.value.condition,
+      percent: Number(paymentFormData.value.percent),
+      expect_pay_date: paymentFormData.value.expect_pay_date,
+      memo: paymentFormData.value.memo,
+    }
+  })
+  return uni.navigateBack()
+}
+
+
+const restForm = async () => {
+  paymentFormData.value = {}
+  paymentFormRef.value?.clearValidate()
+}
+
+// 监听子组件增加,修改,删除事件,更新列表
+useEmitt({
+  name: 'editor_payment_item',
+  callback: (e: { payment: Payment, index: number }) => {
+    const { index, payment } = e;
+    paymentFormData.value.eIndex = index;
+    paymentFormData.value.percent = payment.percent;
+    paymentFormData.value.condition = payment.condition;
+    paymentFormData.value.expect_pay_date = payment.expect_pay_date;
+    paymentFormData.value.memo = payment.memo
+    paymentFormData.value.amount = Math.round((paymentFormData.value.total_amount || 0) * payment.percent * 100) / 10000
+  }
+})
+
+onLoad((options) => {
+  paymentFormData.value.total_amount = Number(options?.total_amount)
+  paymentFormData.value.remaining_percentage = Number(options?.remaining_percentage)
+})
+
+</script>
+<script lang="ts">
+export default {
+  options: {
+    styleIsolation: 'apply-shared', //解除样式隔离
+  }
+
+}
+</script>
+<style scoped lang="scss">
+.example {
+  padding: 15px;
+  background-color: #fff;
+}
+
+:deep(.is-disabled) {
+  color: #333;
+
+}
+
+.custom-select-class {
+  border-color: #e5e5e5;
+  background-color: transparent;
+  display: flex;
+  box-sizing: border-box;
+  flex-direction: row;
+  align-items: center;
+  border: 1px solid #dcdfe6;
+  border-radius: 4px;
+}
+
+.custom-select-content {
+  width: auto;
+  position: relative;
+  overflow: hidden;
+  flex: 1;
+  line-height: 1;
+  font-size: 14px;
+  line-height: 35px;
+  padding-left: 10px;
+  height: 35px;
+
+}
+
+.tag-container {
+  &__item {
+    padding: 0 6px 0 0;
+  }
+}
+
+.uni-easyinput {
+  /* #ifndef APP-NVUE */
+  width: 100%;
+  /* #endif */
+  flex: 1;
+  position: relative;
+  text-align: left;
+  color: #333;
+  font-size: 14px;
+}
+
+.uni-easyinput__content {
+  flex: 1;
+  /* #ifndef APP-NVUE */
+  width: 100%;
+  display: flex;
+  box-sizing: border-box;
+  /* #endif */
+  flex-direction: row;
+  align-items: center;
+  border-color: #fff;
+  transition-property: border-color;
+  transition-duration: 0.3s;
+}
+
+.is-input-border {
+  display: flex;
+  box-sizing: border-box;
+  flex-direction: row;
+  align-items: center;
+  border: 1px solid #dcdfe6;
+  border-radius: 4px;
+}
+
+.uni-easyinput__content-input {
+  /* #ifndef APP-NVUE */
+  width: auto;
+  /* #endif */
+  position: relative;
+  overflow: hidden;
+  flex: 1;
+  line-height: 1;
+  font-size: 14px;
+  padding-left: 10px;
+  height: 35px;
+
+  /*ifdef H5*/
+  & ::-ms-reveal {
+    display: none;
+  }
+
+  & ::-ms-clear {
+    display: none;
+  }
+
+  & ::-o-clear {
+    display: none;
+  }
+
+}
+
+.content-clear-icon {
+  padding: 0 5px;
+}
+
+:deep(.uni-easyinput__placeholder-class) {
+  color: #999;
+  font-size: 12px;
+}
+
+:deep(.uni-section .uni-section-header) {
+  padding: 12px 0
+}
+
+:deep(.uni-swipe_button-group) {
+  top: 5px;
+  bottom: 5px;
+}
+
+.uni-mt-5 {
+  margin-top: 5px;
+}
+
+.border-top {
+  border-top: 1px #eee solid;
+}
+
+.form-item-disabled {
+  background: #f7f6f6;
+  cursor: not-allowed;
+  color: #d5d5d5
+}
+</style>

+ 335 - 0
src/pages/profile/contract/index.vue

@@ -0,0 +1,335 @@
+<template>
+  <z-paging ref="paging" v-model="contractList" @query="queryList" :default-page-size="detaultPageSize" empty-view-text="暂无合同数据"
+    :safe-area-inset-bottom="true">
+    <template #top>
+      <custom-dropdown ref="dropdownRef" :dropdownMenu="dropdownMenuList" themeColor="#007aff"
+        style="width:100%;border-top: 1px solid #edf0f9;" textColor="#333333" :duration="300" @confirm="handleConfirm"
+        @close="handleClose" bgColor="#f8f8f8" @open="handleOpen">
+        <template #slot1="{ item, index }">
+          <view style="width: 100%;height: 300px;">
+            <next-tree ref="regionTreeRef" :ifSearch="true" :showDropdownFooter="true" :border="true" labelKey="name"
+              :showBottomBar="true" funcMode="radio" :treeData="prjList" uiMode="page" @confirm="confirmProject">
+            </next-tree>
+          </view>
+        </template>
+        <template #slot2="{ item, index }">
+          <view style="margin: 40rpx 40rpx 0">
+            <uni-easyinput prefixIcon="search" v-model="customer_name" placeholder="按客户名称模糊查询" @clear="resetCustomer">
+            </uni-easyinput>
+            <PartDropdownFooter @reset="resetCustomer" @confirm="confirmCustomer" />
+          </view>
+        </template>
+      </custom-dropdown>
+    </template>
+    <List :contract-list="contractList" />
+    <template #bottom>
+      <view style="padding: 0 10px;position: relative;bottom: 1px;">
+        <custom-button width="100%"  color="#007aff" :radius="['5px']" ripple
+          @click="redirectAddContractPage()">录入合同</custom-button>
+      </view>
+    </template>
+  </z-paging>
+</template>
+
+<script lang="ts" setup>
+import List from './List.vue'
+import { useEmitt } from '@/logics/mitt/useEmitt';
+import customDropdown from '@/components/cu-dropdown/index.vue'
+import PartDropdownFooter from '@/components/cu-dropdown/components/part-dropdown-footer.vue'
+import nextTree from '@/components/next-tree/next-tree.vue'
+import { getProjectList } from '@/api/project-center/info';
+import { getContractList } from '@/api/project-center/contract';
+import { getStaffList } from '@/api/biz/staff';
+import { Contract } from '@/types/contract';
+
+const detaultPageSize = 10;
+const paging = ref()
+const customer_name = ref()
+const contractList = ref<Contract[]>([])
+const dropdownRef = ref()
+const prjList = ref<{ id: string, name: string }[]>([])
+
+/**
+ * 条件筛选表单
+ */
+const filterForm = reactive<{
+  name?: string
+  prj_id?: string
+  date?: string[]
+  customer_name?: string
+  handler_id?: number
+}>({
+  name: undefined,
+  prj_id: undefined,
+  customer_name: undefined,
+  handler_id: undefined,
+  date: undefined
+})
+
+const dropdownMenuList = ref([
+  {
+    title: '合同名称',
+    type: 'search',
+    prop: 'name',
+    placeholder: '按合同名称搜索'
+  },
+  {
+    title: '所属项目',
+    type: 'slot1',
+    prop: 'prj_id',
+    showArrow: true,
+    showIcon: true,
+  },
+  {
+    title: '签订客户',
+    type: 'slot2',
+    prop: 'customer_name',
+    showArrow: true,
+    showIcon: true,
+  },
+  {
+    title: '签订人员',
+    type: 'cell',
+    prop: 'handler_id',
+    showArrow: true,
+    showIcon: true,
+    field: {
+      label: 'name',
+      value: 'id'
+    },
+    options: []
+  },
+  {
+
+    title: '时间范围',
+    showAll: true,
+    showIcon: true,
+    type: 'daterange',
+    prop: 'date'
+  },
+])
+
+/**
+ * 获取筛选框列表
+ */
+const getFilterForm = () => {
+  const {
+    name,
+    prj_id,
+    handler_id,
+    customer_name,
+    date
+  } = filterForm
+  const begin_from = date ? date[0] : date
+  const end_to = date ? date[1] : date
+  return {
+    name,
+    prj_id,
+    handler_id,
+    customer_name,
+    begin_from,
+    end_to
+  }
+}
+
+// @query所绑定的方法不要自己调用!!需要刷新列表数据时,只需要调用paging.value.reload()即可
+const queryList = async (pageNo: number, pageSize: number) => {
+  try {
+    const { name, prj_id, handler_id, begin_from, end_to, customer_name } = getFilterForm()
+    const { list } = await getContractList({ page_no: pageNo, page_size: pageSize, name, prj_id, customer_name, begin_from, end_to, handler_id })
+    paging.value?.complete(list);
+  } catch (e) {
+    paging.value.complete(false);
+  }
+}
+
+const resetCustomer = () => {
+  customer_name.value = undefined
+}
+
+const confirmCustomer = () => {
+  if (![undefined, null, ''].includes(customer_name.value)) {
+    dropdownRef.value?.updateMenu('customer_name', customer_name.value, 'value')
+    dropdownRef.value?.updateMenu('customer_name', true, 'isActived')
+  } else {
+    dropdownRef.value?.updateMenu('customer_name', undefined, 'value')
+    dropdownRef.value?.updateMenu('customer_name', false, 'isActived')
+  }
+  dropdownRef.value?.closeMenuPopup()
+  handleConfirm(2)
+}
+
+
+const confirmProject = (v: { id: string }[]) => {
+  if (v.length > 0) {
+    dropdownRef.value?.updateMenu('prj_id', v[0].id, 'value')
+    dropdownRef.value?.updateMenu('prj_id', true, 'isActived')
+  } else {
+    dropdownRef.value?.updateMenu('prj_id', undefined, 'value')
+    dropdownRef.value?.updateMenu('prj_id', false, 'isActived')
+  }
+  dropdownRef.value?.closeMenuPopup()
+  handleConfirm(1)
+}
+
+// 跳转录入合同页面
+const redirectAddContractPage = () => {
+  uni.navigateTo({ url: `/pages/profile/contract/add-contract` })
+}
+
+async function handleOpen(index: number) {
+  if (index === 2) {
+    customer_name.value = filterForm.customer_name
+  }
+
+}
+
+function handleClose(index: number) {
+  console.log('handleClose ==>', index)
+
+}
+
+function handleConfirm(v: any) {
+  const dropdownValues = dropdownRef.value?.getMenuValue()
+  filterForm.prj_id = dropdownValues.prj_id
+  filterForm.handler_id = dropdownValues.handler_id
+  filterForm.customer_name = dropdownValues.customer_name
+  filterForm.name = dropdownValues.name
+  filterForm.date = dropdownValues.date ? [dropdownValues.date.start, dropdownValues.date.end] : dropdownValues.date
+  paging.value?.reload()
+}
+
+
+
+// 监听子组件增加,修改,删除事件,更新列表
+useEmitt({
+  name: 'get_contract_list',
+  callback: () => {
+    console.log('刷新')
+    paging.value?.reload()
+  }
+})
+
+
+/**
+ * 获取项目列表
+ */
+const getProjects = async () => {
+  try {
+    const { list } = await getProjectList({ page_no: 1, page_size: 9999 })
+    prjList.value = list
+  } catch (e) {
+    console.error(e)
+  }
+}
+
+/**
+ * 获取项目列表
+ */
+const getAccounts = async () => {
+  try {
+    const { list } = await getStaffList({ page_no: 1, page_size: 9999 })
+    nextTick(() => {
+      dropdownRef.value?.updateMenu('handler_id', [{ name: '不限', id: '-9999' }, ...list], 'options')
+    })
+  } catch (e) {
+    console.error(e)
+  }
+}
+
+onMounted(() => {
+  getProjects()
+  getAccounts()
+})
+
+</script>
+<script lang="ts">
+export default {
+  options: {
+    styleIsolation: 'apply-shared', //解除样式隔离
+  }
+
+}
+</script>
+<style scoped lang="scss">
+.c-nav-bar {
+  &__content {
+    color: red;
+  }
+}
+
+.is-hover {
+  color: rgba(255, 255, 255, 0.6);
+  background-color: #179b16;
+  border-color: #179b16;
+}
+
+.city {
+  /* #ifndef APP-PLUS-NVUE */
+  display: flex;
+  /* #endif */
+  flex-direction: row;
+  align-items: center;
+  justify-content: flex-start;
+  margin-left: 4px;
+}
+
+.box-bg {
+  background-color: #F5F5F5;
+}
+
+
+.input-view {
+  /* #ifndef APP-PLUS-NVUE */
+  display: flex;
+  /* #endif */
+  flex-direction: row;
+  flex: 1;
+  flex-wrap: nowrap;
+  margin: 7px 3px 7px 0px;
+  line-height: 30px;
+}
+
+.input-uni-icon {
+  line-height: 30px;
+}
+
+.nav-bar-input {
+  height: 30px;
+  line-height: 30px;
+  /* #ifdef APP-PLUS-NVUE */
+  /* #endif */
+  padding: 0 5px;
+  font-size: 12px;
+  background-color: #f8f8f8;
+}
+
+:deep(.uni-searchbar__box-icon-clear) {
+  padding-top: 5px;
+}
+
+.uni-navbar {
+  &__header {
+    padding: 0 !important
+  }
+}
+
+:deep(.uni-searchbar) {
+  padding: 0;
+  width: 100%;
+}
+
+:deep(.uni-searchbar__box) {
+  height: 30px !important;
+}
+
+:deep(.uni-navbar__header-container) {
+  padding: 0px !important;
+}
+
+
+:deep(.uni-searchbar__cancel) {
+  height: 30px;
+  line-height: 30px;
+}
+</style>

+ 129 - 0
src/pages/profile/customer/List.vue

@@ -0,0 +1,129 @@
+<template>
+    <div>
+        <uni-swipe-action ref="swiper_action">
+            <uni-swipe-action-item :right-options="options" v-for="item in customerList" :key="item.id"
+                @click="handlerRemoveCustomer(item)" class="swipe-action-item" :threshold="5">
+                <uni-card :title="item.name" :extra="item.created_at" margin="5px" padding="0px"
+                    @click="redirectCustomerDetailPage(item)">
+                    <!-- <uni-group> -->
+                    <view class=" card-actions">
+                        <text class="subtitle font-size-base">所在地区:<text class="paragraph font-size-sm">{{
+                                item.region_name }}</text></text>
+                        <text class="subtitle font-size-base">所属行业:<text class="paragraph font-size-sm">{{
+                                item.industry_name }}</text></text>
+                    </view>
+                    <view class="font-size-sm card-actions ">
+                        <text class="subtitle font-size-base">客户级别:<text class="paragraph font-size-sm">{{
+                                item.level_name }}</text></text>
+                    </view>
+                    <!-- </uni-group> -->
+                    <view slot="actions" class="card-actions border-top">
+                        <text class="subtitle font-size-base">联系人:<text class="paragraph font-size-sm">{{ item.link_man
+                                || '暂无' }}</text></text>
+                        <text class="subtitle font-size-base">联系电话:<text class="paragraph font-size-sm">{{
+                                item.link_phone || '暂无' }}</text></text>
+                    </view>
+                </uni-card>
+            </uni-swipe-action-item>
+        </uni-swipe-action>
+    </div>
+</template>
+
+
+<script lang="ts">
+export default {
+    options: {
+        styleIsolation: 'apply-shared', //解除样式隔离
+    }
+
+}
+</script>
+<script setup lang="ts">
+import { removeCustomer } from '@/api/biz/customer';
+import { useEmitt } from '@/logics/mitt/useEmitt';
+import { Customer } from '@/types/customer';
+const swiper_action = ref()
+const options = [
+    {
+        text: '删除',
+        style: {
+            backgroundColor: '#dd524d',
+            margin: "5px 0"
+        }
+    }
+]
+defineProps({
+    customerList: {
+        type: Array as PropType<Customer[]>,
+        required: true
+    }
+})
+
+const { emitter } = useEmitt()
+
+const handlerRemoveCustomer = async (item: Customer) => {
+    uni.showModal({
+        title: '警告',
+        content: `是否删除客户:${item.name}`,
+        success: async (res: { confirm: boolean }) => {
+            const { confirm } = res;
+            if (confirm) {
+                await removeCustomer(item.id)
+                // props.customerList = p
+                emitter.emit('remove_customer', item.id)
+                // return uni.navigateBack()
+            }
+        }
+    })
+    swiper_action.value?.closeAll()
+}
+// 跳转客户详情
+const redirectCustomerDetailPage = async (item: Customer) => {
+    uni.navigateTo({ url: `/pages/profile/customer/detail?id=${item.id}` })
+} 
+</script>
+<style scoped lang="scss">
+:deep(.uni-swipe_button-group) {
+    top: 5px;
+    bottom: 5px;
+}
+
+.uni-mt-5 {
+    margin-top: 5px;
+}
+
+.border-top {
+    border-top: 1px #eee solid;
+}
+
+.card-actions {
+    display: flex;
+    flex-direction: row;
+    justify-content: space-between;
+    // align-items: center;
+    height: 25px;
+    line-height: 25px;
+    font-size: $uni-font-size-sm;
+    padding: 5px 10px;
+
+}
+
+.font-size-sm {
+    font-size: $uni-font-size-sm;
+}
+
+.font-size-base {
+    font-size: 26rpx;
+}
+
+.paragraph {
+    /* color: $uni-color-paragraph;*/
+    color: #b2a9a6;
+    font-weight:600;
+}
+
+.subtitle {
+   /* color: $uni-color-subtitle*/
+   color: #b2a9a6;
+}
+</style>

+ 367 - 0
src/pages/profile/customer/add-customer.vue

@@ -0,0 +1,367 @@
+<template>
+  <SelectItemModal :show-checkbox-drawer="showCheckboxDrawer" @closeDrawer="closeCheckBoxDrawer" :list="accountList"
+    v-model="customerFormData.bizman_id" />
+  <view class="example">
+    <!-- 展示不同的排列方式 -->
+    <uni-forms ref="customerFormRef" :modelValue="customerFormData" :rules="rules" validateTrigger="submit" label-position="top">
+      <uni-forms-item label="客户名称" required label-width="90px" name="name">
+        <uni-easyinput v-model="customerFormData.name" placeholder="请输入客户名称" />
+      </uni-forms-item>
+      <uni-forms-item label="客户行业" required label-width="90px" name="industry_id">
+        <uni-data-select v-model="customerFormData.industry_id" :localdata="industryList" 
+          placeholder="请选择客户行业"></uni-data-select>
+      </uni-forms-item>
+      <uni-forms-item label="客户级别" required label-width="90px" name="level_id">
+        <uni-data-select v-model="customerFormData.level_id" :localdata="customerLevelList" 
+          placeholder="请选择客户级别"></uni-data-select>
+      </uni-forms-item>
+      <uni-forms-item label="所在地区" required label-width="90px" name="region_id">
+        <view class="custom-select-class" @click="showRegionModal">
+          <view class="custom-select-content">
+            <text v-if="customerFormData.region_id">{{ customerFormData.region_name }}</text>
+            <text v-else style="color:#909399;font-size: 12px;">请选择客户所在地区</text>
+          </view>
+          <uni-icons :size="14" color="#909399" type="down" style=" padding: 0 5px;color:#909399;"></uni-icons>
+        </view>
+      </uni-forms-item>
+      <uni-forms-item label="客户联系人" label-width="90px" name="link_man">
+        <uni-easyinput v-model="customerFormData.link_man" placeholder="请输入客户联系人" />
+      </uni-forms-item>
+      <uni-forms-item label="联系电话" label-width="90px" name="link_phone">
+        <uni-easyinput v-model="customerFormData.link_phone" placeholder="请输入联系电话" />
+      </uni-forms-item>
+      <uni-forms-item label="经营地址" label-width="90px" name="address">
+        <uni-easyinput v-model="customerFormData.address" placeholder="请输入客户经营地址" />
+      </uni-forms-item>
+      <uni-forms-item label="市场负责人" label-width="90px" name="bizman_id">
+        <view class="custom-select-class" @click="showCheckBoxDrawer">
+          <view class="custom-select-content">
+            <text v-if="customerFormData.bizman_id">{{ customerFormData.bizman_name }}</text>
+            <text v-else style="color:#909399;font-size: 12px;">请选择市场负责人</text>
+          </view>
+
+          <uni-icons :size="14" color="#909399" type="down" style=" padding: 0 5px;"></uni-icons>
+        </view>
+      </uni-forms-item>
+      <uni-forms-item label="发票抬头" label-width="90px" name="invoice_title">
+        <uni-easyinput v-model="customerFormData.invoice_title" placeholder="请输入发票抬头" />
+      </uni-forms-item>
+      <uni-forms-item label="客户税号" label-width="90px" name="tax_no">
+        <uni-easyinput v-model="customerFormData.tax_no" placeholder="请输入客户税号" />
+      </uni-forms-item>
+      <uni-forms-item label="客户开户行" label-width="90px" name="bank_name">
+        <uni-easyinput v-model="customerFormData.bank_name" placeholder="请输入客户开户行" />
+      </uni-forms-item>
+      <uni-forms-item label="开户行账号" label-width="90px" name="bank_account">
+        <uni-easyinput v-model="customerFormData.bank_account" placeholder="请输入开户行账号" />
+      </uni-forms-item>
+      <uni-forms-item label="开户行地址" label-width="90px" name="invoice_addr">
+        <uni-easyinput v-model="customerFormData.invoice_addr" placeholder="请输入开户行地址" />
+      </uni-forms-item>
+      <uni-forms-item label="开户行电话" label-width="90px" name="invoice_phone">
+        <uni-easyinput v-model="customerFormData.invoice_phone" placeholder="请输入开户行电话" />
+      </uni-forms-item>
+      <uni-forms-item label="备注信息" label-width="90px" name="memo">
+        <uni-easyinput type="textarea" v-model="customerFormData.memo" placeholder="请输入客户备注信息" />
+      </uni-forms-item>
+    </uni-forms>
+    <view style="margin-bottom: 10px;display: flex;">
+      <custom-button width="100%" type="filled" color="#dd524d" @click="restForm()"
+        style="margin-right: 5px;flex:1">重置</custom-button>
+      <custom-button width="100%" type="filled" color="#007aff" style="flex:1;"
+        @click="handlerAddCustomer()">提交</custom-button>
+    </view>
+    <next-tree :ifSearch="true" :showAuxiliaryLine="true" :border="true" labelKey="name" ref="regionTreeRef"
+      :checkStrictly="true" :selectParent="true" funcMode="radio" :treeData="regionTreeData"
+      @confirm="confirmRegion"></next-tree>
+    <next-tree :ifSearch="true" :showAuxiliaryLine="false" :border="true" labelKey="name" ref="accountTreeRef"
+      :checkStrictly="true" :selectParent="true" funcMode="radio" :treeData="accountList"
+      @confirm="confirmAccount"></next-tree>
+  </view>
+
+</template>
+
+<script lang="ts" setup>
+import { getCustomerLevelList, getIndustryTypeList } from '@/api/base/base';
+import { getStaffList } from '@/api/biz/staff';
+import SelectItemModal from '@/components/selected-list-item/index.vue'
+import nextTree from '@/components/next-tree/next-tree.vue'
+import { DomainTree } from '@/types/domain';
+import { AddCustomerReqParams } from '@/types/customer';
+import { addCustomer } from '@/api/biz/customer';
+import { useEmitt } from '@/logics/mitt/useEmitt';
+import { getRegionTree } from '@/api/base/region';
+const showCheckboxDrawer = ref(false)
+const accountList = ref<{ id: string, name: string }[]>([])
+const customerLevelList = ref<{ value: string, text: string }[]>([])
+const regionTreeData = ref<DomainTree[]>([]) // 区域树
+const customerFormRef = ref()
+const regionTreeRef = ref()
+const accountTreeRef = ref()
+const industryList = ref<{ value: string; text: string }[]>([])
+const { emitter } = useEmitt()
+
+const rules = {
+  // 对name字段进行必填验证
+  name: {
+    // name 字段的校验规则
+    rules: [
+      // 校验 name 不能为空
+      {
+        required: true,
+        errorMessage: '名称不能为空',
+      }
+    ]
+  },
+  industry_id: {
+    rules: [
+      {
+        required: true,
+        errorMessage: '行业信息不能为空',
+      }
+    ]
+  },
+  region_id: {
+    rules: [
+      {
+        required: true,
+        errorMessage: '所属区域不能为空',
+      }
+    ]
+  },
+  level_id: {
+    rules: [
+      {
+        required: true,
+        errorMessage: '客户级别不能为空',
+      }
+    ]
+  }
+}
+const customerFormData = ref<{
+  name?: string
+  industry_id?: string
+  industry_name?: string
+  region_name?: string
+  region_id?: number
+  level_id?: string
+  link_man?: string
+  link_phone?: string
+  address?: string
+  invoice_title?: string
+  tax_no?: string
+  bank_name?: string
+  bank_account?: string
+  invoice_addr?: string
+  invoice_phone?: string
+  bizman_id?: string
+  bizman_name?: string
+  memo?: string
+}>({
+
+})
+const range = [
+  { value: 0, text: "篮球" },
+  { value: 1, text: "足球" },
+  { value: 2, text: "游泳" },
+]
+
+const showCheckBoxDrawer = async () => {
+  // await get
+  checkedTreeData(unref(accountList), customerFormData.value.bizman_id)
+  unref(accountTreeRef).showTree = true
+  // showCheckboxDrawer.value = true
+}
+const closeCheckBoxDrawer = (id?: string) => {
+  customerFormData.value.bizman_id = id;
+  customerFormData.value.bizman_name = accountList.value.find(item => item.id === id)?.name
+  showCheckboxDrawer.value = false
+}
+
+function checkedTreeData(treeData: any, selectIds?: number | string) {
+  treeData.map((item: any) => {
+    if (selectIds === item.id) {
+      item.checked = true
+    } else {
+      item.checked = false
+    }
+    if (item.children && item.children.length) {
+      checkedTreeData(item.children, selectIds)
+    }
+  })
+}
+
+const showRegionModal = async () => {
+  checkedTreeData(unref(regionTreeData), customerFormData.value.region_id)
+  unref(regionTreeRef).showTree = true
+}
+
+/**
+ * 递归获取ng-tree需要的结构体
+ * @param domains 接口返回的树状数组
+ */
+const getNodesTree = (domains: DomainTree): DomainTree => {
+  domains.key = domains.id.toString()
+  if (domains.children && domains.children.length > 0) {
+    domains.children.map(res => getNodesTree(res))
+  }
+  return domains
+}
+
+/**
+ * 获取区域树
+ */
+const getTree = async () => {
+  const { tree } = await getRegionTree()
+  regionTreeData.value = tree
+}
+
+/**
+* 获取用户列表
+*/
+const getAccountList = async () => {
+  try {
+    const { list } = await getStaffList({ page_no: 1, page_size: 9999 })
+    accountList.value = list
+  } catch (e) {
+    console.error(e)
+  }
+}
+
+const getIndustryType = async () => {
+  const { list } = await getIndustryTypeList()
+  industryList.value = list.map(item => {
+    return {
+      value: item.id,
+      text: item.name
+    }
+  })
+
+}
+
+const getLevels = async () => {
+  const { list } = await getCustomerLevelList()
+  customerLevelList.value = list.map(item => {
+    return {
+      value: item.id,
+      text: item.name
+    }
+  })
+}
+
+function confirmRegion(list: DomainTree[]) {
+  if (list.length > 0) {
+    customerFormData.value.region_id = list[0].id
+    customerFormData.value.region_name = list[0].name
+  }else{
+    customerFormData.value.region_id = undefined
+    customerFormData.value.region_name = undefined
+  }
+}
+
+function confirmAccount(list: { id: string, name: string }[]) {
+  if (list.length > 0) {
+    customerFormData.value.bizman_id = list[0].id
+    customerFormData.value.bizman_name = list[0].name
+  }else{
+    customerFormData.value.bizman_id = undefined
+    customerFormData.value.bizman_name = undefined
+  }
+}
+
+
+const handlerAddCustomer = async () => {
+  const isValid = await customerFormRef.value?.validate()
+  if (!isValid) {
+    return
+  }
+  const { name, region_id, address, industry_id, level_id, link_man, link_phone, bank_account, bank_name, bizman_id, invoice_addr, invoice_phone, invoice_title, memo, tax_no } = unref(customerFormData)
+  const params: AddCustomerReqParams = {
+    name: name!,
+    region_id: region_id!,
+    industry_id: industry_id!,
+    level_id: level_id!,
+    link_man,
+    link_phone,
+    bank_account,
+    bank_name,
+    bizman_id,
+    invoice_addr,
+    invoice_phone,
+    invoice_title,
+    tax_no,
+    address,
+    memo
+  }
+  await addCustomer(params)
+  emitter.emit('get_customer_list')
+  uni.showModal({
+    title: '添加成功',
+    content: `是否返回客户管理页面`,
+    success: (res: { confirm: boolean }) => {
+      const { confirm } = res;
+      if(confirm){
+        return uni.navigateBack()
+      }
+    }
+  })
+  // const {}
+}
+
+
+const restForm = async()=>{
+  customerFormData.value = {}
+  customerFormRef.value?.clearValidate()
+}
+
+
+onMounted(() => {
+  getAccountList()
+  getIndustryType()
+  getLevels()
+  getTree()
+})
+
+</script>
+<script lang="ts">
+export default {
+  options: {
+    styleIsolation: 'apply-shared', //解除样式隔离
+  }
+
+}
+</script>
+<style scoped>
+.example {
+  padding: 15px;
+  background-color: #fff;
+}
+
+:deep(.is-disabled) {
+  color: #333;
+
+}
+
+.custom-select-class {
+  border-color: #e5e5e5;
+  background-color: transparent;
+  display: flex;
+  box-sizing: border-box;
+  flex-direction: row;
+  align-items: center;
+  border: 1px solid #dcdfe6;
+  border-radius: 4px;
+}
+
+.custom-select-content {
+  width: auto;
+  position: relative;
+  overflow: hidden;
+  flex: 1;
+  line-height: 1;
+  font-size: 14px;
+  line-height: 35px;
+  padding-left: 10px;
+  height: 35px;
+}
+</style>

+ 380 - 0
src/pages/profile/customer/detail.vue

@@ -0,0 +1,380 @@
+<template>
+  <view class="container">
+    <uni-section class="mb-10" title="基本信息" type="line"></uni-section>
+    <uni-list :border="true">
+      <uni-list-item title="创建日期" :rightText="detail.customer?.created_at" />
+      <uni-list-item title="客户编号" :rightText="detail.customer?.id" />
+      <uni-list-item title="客户名称" :rightText="detail.customer?.name">
+        <template #footer>
+          <view style="width:100%">
+            <EditorListItem style="width:100%" v-model="modifyParams.name" placeholder="客户名称"
+              :old-value="detail.customer?.name" :not-allow-null="true" :components-type="ComponentsType.STRINGINPUT"
+              @modify="modifyCustomerInfo" />
+          </view>
+        </template>
+      </uni-list-item>
+      <uni-list-item title="所属地区" :rightText="detail.customer?.region_name">
+        <template #footer>
+          <view style="width:100%">
+            <EditorListItem style="width:100%" v-model="modifyParams.region_id" placeholder="所属地区"
+              :options="regionTreeData" :old-value="detail.customer?.region_id"
+              :components-type="ComponentsType.CASCADER" @modify="modifyCustomerInfo" :showAuxiliaryLine="true"
+              :selectParent="true" :not-allow-null="true" />
+          </view>
+        </template>
+      </uni-list-item>
+      <uni-list-item title="所属行业" :rightText="detail.customer?.industry_name">
+        <template #footer>
+          <view style="width:100%">
+            <EditorListItem style="width:100%" v-model="modifyParams.industry_id" placeholder="所属行业"
+              :options="industryList" :old-value="detail.customer?.industry_id" :components-type="ComponentsType.SELECT"
+              @modify="modifyCustomerInfo" :not-allow-null="true" />
+          </view>
+        </template>
+      </uni-list-item>
+      <uni-list-item title="客户级别" :rightText="detail.customer?.level_name">
+        <template #footer>
+          <view style="width:100%">
+            <EditorListItem style="width:100%" v-model="modifyParams.level_id" placeholder="客户级别"
+              :options="customerLevelList" :old-value="detail.customer?.level_id"
+              :components-type="ComponentsType.SELECT" @modify="modifyCustomerInfo" 
+             :not-allow-null="true" />
+          </view>
+        </template>
+      </uni-list-item>
+      <uni-list-item title="市场负责人" :rightText="detail.customer?.bizman_name">
+        <template #footer>
+          <view style="width:100%">
+            <EditorListItem style="width:100%" v-model="modifyParams.bizman_id" placeholder="市场负责人"
+              :options="accountList" :old-value="detail.customer?.bizman_id" :components-type="ComponentsType.SELECT"
+              @modify="modifyCustomerInfo" :not-allow-null="true" />
+          </view>
+        </template>
+      </uni-list-item>
+      <uni-list-item title="客户联系人" :rightText="detail.customer?.link_man">
+        <template #footer>
+          <view style="width:100%">
+            <EditorListItem style="width:100%" v-model="modifyParams.link_man" placeholder="客户联系人"
+              :old-value="detail.customer?.link_man" :not-allow-null="true"
+              :components-type="ComponentsType.STRINGINPUT" @modify="modifyCustomerInfo" field="name" />
+          </view>
+        </template>
+      </uni-list-item>
+      <uni-list-item title="联系人电话" :rightText="detail.customer?.link_phone">
+        <template #footer>
+          <view style="width:100%">
+            <EditorListItem style="width:100%" v-model="modifyParams.link_phone" placeholder="联系人电话"
+              :old-value="detail.customer?.link_phone" :not-allow-null="true"
+              :components-type="ComponentsType.STRINGINPUT" @modify="modifyCustomerInfo" field="name" />
+          </view>
+        </template>
+      </uni-list-item>
+      <uni-list-item title="经营地址" :rightText="detail.customer?.address">
+        <template #footer>
+          <view style="width:100%">
+            <EditorListItem style="width:100%" v-model="modifyParams.address" placeholder="经营地址"
+              :old-value="detail.customer?.name" :not-allow-null="true" :components-type="ComponentsType.STRINGINPUT"
+              @modify="modifyCustomerInfo" field="name" />
+          </view>
+        </template>
+      </uni-list-item>
+
+      <uni-list-item title="备注信息">
+        <template #footer>
+          <view style="width:100%">
+            <EditorListItem style="width:100%" v-model="modifyParams.memo" placeholder="备注信息"
+              :old-value="detail.customer?.name" :components-type="ComponentsType.TEXTAREA"
+              @modify="modifyCustomerInfo" />
+          </view>
+        </template>
+      </uni-list-item>
+    </uni-list>
+    <uni-section class="mb-10" title="开票资料" type="line"></uni-section>
+    <uni-list :border="true">
+      <uni-list-item title="发票抬头" :rightText="detail.customer?.invoice_title">
+        <template #footer>
+          <view style="width:100%">
+            <EditorListItem style="width:100%" v-model="modifyParams.invoice_title" placeholder="发票抬头"
+              :old-value="detail.customer?.name" :not-allow-null="true" :components-type="ComponentsType.STRINGINPUT"
+              @modify="modifyCustomerInfo" field="name" />
+          </view>
+        </template>
+      </uni-list-item>
+      <uni-list-item title="客户税号" :rightText="detail.customer?.tax_no">
+        <template #footer>
+          <view style="width:100%">
+            <EditorListItem style="width:100%" v-model="modifyParams.tax_no" placeholder="客户税号"
+              :old-value="detail.customer?.tax_no" :not-allow-null="true" :components-type="ComponentsType.STRINGINPUT"
+              @modify="modifyCustomerInfo" field="name" />
+          </view>
+        </template>
+      </uni-list-item>
+      <uni-list-item title="客户开户行" :rightText="detail.customer?.bank_name">
+        <template #footer>
+          <view style="width:100%">
+            <EditorListItem style="width:100%" v-model="modifyParams.bank_name" placeholder="客户开户行"
+              :old-value="detail.customer?.bank_name" :not-allow-null="true" :components-type="ComponentsType.STRINGINPUT"
+              @modify="modifyCustomerInfo" field="name" />
+          </view>
+        </template>
+      </uni-list-item>
+      <uni-list-item title="开户行账号" :rightText="detail.customer?.bank_account">
+        <template #footer>
+          <view style="width:100%">
+            <EditorListItem style="width:100%" v-model="modifyParams.bank_account" placeholder="开户行账号"
+              :old-value="detail.customer?.bank_account" :not-allow-null="true" :components-type="ComponentsType.STRINGINPUT"
+              @modify="modifyCustomerInfo" field="name" />
+          </view>
+        </template>
+      </uni-list-item>
+      <uni-list-item title="开户行地址" :rightText="detail.customer?.invoice_addr">
+        <template #footer>
+          <view style="width:100%">
+            <EditorListItem style="width:100%" v-model="modifyParams.invoice_addr" placeholder="开户行地址"
+              :old-value="detail.customer?.invoice_addr" :not-allow-null="true" :components-type="ComponentsType.STRINGINPUT"
+              @modify="modifyCustomerInfo" field="name" />
+          </view>
+        </template>
+      </uni-list-item>
+      <uni-list-item title="开户行电话" :rightText="detail.customer?.invoice_phone">
+        <template #footer>
+          <view style="width:100%">
+            <EditorListItem style="width:100%" v-model="modifyParams.invoice_phone" placeholder="开户行电话"
+              :old-value="detail.customer?.invoice_phone" :not-allow-null="true" :components-type="ComponentsType.STRINGINPUT"
+              @modify="modifyCustomerInfo" field="name" />
+          </view>
+        </template>
+      </uni-list-item>
+    </uni-list>
+  </view>
+</template>
+<script setup lang="ts">
+import { getCustomerDetail, modifyCustomer } from '@/api/biz/customer';
+import { useEmitt } from '@/logics/mitt/useEmitt';
+import { CustomerDetailResParams, ModifyCustomerReqParams } from '@/types/customer';
+import EditorListItem from '@/components/editor-list-item/editor-list-item.vue'
+import { ComponentsType } from '@/enums/api'
+import { getStaffList } from '@/api/biz/staff';
+import { getRegionTree } from '@/api/base/region';
+import { DomainTree } from '@/types/domain';
+import { getCustomerLevelList, getIndustryTypeList } from '@/api/base/base';
+
+
+const customerLevelList = ref<{ id: string, name: string }[]>([])
+const accountList = ref<{ id: string, name: string }[]>([])
+const regionTreeData = ref<DomainTree[]>([]) // 区域树
+const customer_id = ref()
+const detail = reactive<{ customer?: CustomerDetailResParams }>({
+  customer: undefined
+}) // 客户详情
+
+const industryList = ref<{ id: string; name: string }[]>([])
+const modifyParams = ref<Omit<ModifyCustomerReqParams, 'id'>>({
+})
+
+
+/**
+ * 获取客户详情
+ * @param customerId
+ */
+const getDetail = async () => {
+  const customerDetail = await getCustomerDetail(customer_id.value)
+  nextTick(() => {
+    detail.customer = customerDetail
+    uni.setNavigationBarTitle({ title: `详情-${customerDetail.name}` })
+    modifyParams.value.name = customerDetail.name
+    modifyParams.value.region_id = customerDetail.region_id
+    modifyParams.value.level_id = customerDetail.level_id
+    modifyParams.value.industry_id = customerDetail.industry_id
+    modifyParams.value.link_man = customerDetail.link_man
+    modifyParams.value.link_phone = customerDetail.link_phone
+    modifyParams.value.address = customerDetail.address
+    modifyParams.value.memo = customerDetail.memo
+    modifyParams.value.tax_no = customerDetail.tax_no
+    modifyParams.value.bank_name = customerDetail.bank_name
+    modifyParams.value.bank_account = customerDetail.bank_account
+    modifyParams.value.invoice_addr = customerDetail.invoice_addr
+    modifyParams.value.invoice_phone = customerDetail.invoice_phone
+    modifyParams.value.invoice_title = customerDetail.invoice_title
+    modifyParams.value.bizman_id = customerDetail.bizman_id
+  })
+}
+
+
+// { id: customer_id.value, name: modifyParams.value.name }
+const modifyCustomerInfo = async () => {
+  try {
+    const params: ModifyCustomerReqParams = {
+      id: customer_id.value,
+      ...toRaw(modifyParams.value)
+    }
+    await modifyCustomer(params)
+    uni.showToast(({
+      icon: 'success',
+      title: '修改成功'
+    }))
+  } catch (e) {
+    getDetail()
+  }
+}
+
+/**
+ * 获取区域树
+ */
+const getTree = async () => {
+  const { tree } = await getRegionTree()
+  regionTreeData.value = tree
+}
+
+/**
+* 获取用户列表
+*/
+const getAccountList = async () => {
+  try {
+    const { list } = await getStaffList({ page_no: 1, page_size: 9999 })
+    accountList.value = list
+  } catch (e) {
+    console.error(e)
+  }
+}
+
+
+
+const getIndustryType = async () => {
+  const { list } = await getIndustryTypeList()
+  industryList.value = list
+}
+
+const getLevels = async () => {
+  const { list } = await getCustomerLevelList()
+  customerLevelList.value = list
+}
+
+
+
+onLoad((option) => {
+  customer_id.value = option?.id
+});
+
+
+onMounted(() => {
+  getAccountList()
+  getTree()
+  getIndustryType()
+  getLevels()
+  getDetail()
+})
+
+// 监听子组件增加,修改,删除事件,更新列表
+useEmitt({
+  name: 'get_customer_detail',
+  callback: () => {
+    getDetail()
+  }
+})
+
+const redirectModifyCustomerPage = (id?: string) => {
+  uni.navigateTo({ url: `/pages/customer/components/edit-customer?id=${id}` })
+}
+
+</script>
+<script lang="ts">
+export default {
+  options: {
+    styleIsolation: 'apply-shared', //解除样式隔离
+  }
+
+}
+</script>
+<style scoped>
+.container {
+  padding: 0 5px 40px 5px;
+}
+
+:deep(.uni-list-item__content-note) {
+  overflow-wrap: break-word;
+}
+
+:deep(.uni-list-item__content) {
+  flex-direction: row;
+  align-items: center;
+  flex: 0 0 80px;
+}
+
+:deep(.uni-list-item__content-title) {
+  font-size: 13px;
+  color: #3b4144a8;
+}
+
+:deep(.uni-list-item__extra) {
+  flex: 1 1 auto;
+}
+
+:deep(.uni-list-item__extra-text) {
+  color: #5d5959;
+  font-size: 13px;
+  width: 200px;
+  text-overflow: ellipsis;
+  text-align: right;
+  text-wrap: wrap;
+  overflow-wrap: anywhere;
+  white-space-collapse: collapse;
+}
+
+.slot-box {
+  /* #ifndef APP-NVUE */
+  display: flex;
+  /* #endif */
+  flex-direction: row;
+  align-items: center;
+  width: 45px;
+  background-color: rgb(201, 205, 212);
+  border-radius: 4px;
+  margin-right: 10px;
+  height: 45px;
+}
+
+
+.slot-content {
+  display: flex;
+  justify-content: space-between;
+  flex-direction: row;
+  width: 100%;
+  height: 45px;
+}
+
+.slot-panel {
+  display: flex;
+  flex-direction: column;
+  justify-content: space-around;
+}
+
+.slot-title {
+  display: flex;
+  font-size: 14px;
+  color: #3b4144;
+}
+
+.slot-subtitle {
+  display: flex;
+  font-size: 12px;
+  color: #999
+}
+
+.slot-desc {
+  font-size: 12px;
+  color: #999;
+  max-width: 200px;
+  max-height: 50px;
+  overflow: hidden;
+  text-overflow: ellipsis;
+  display: -webkit-box;
+  -webkit-box-orient: vertical;
+  -webkit-line-clamp: 3;
+
+}
+
+:deep(.uni-section) {
+  margin-top: 5px;
+}
+</style>

+ 207 - 0
src/pages/profile/customer/index.vue

@@ -0,0 +1,207 @@
+<template>
+  <z-paging ref="paging" v-model="customerList" @query="queryList" :default-page-size="detaultPageSize" empty-view-text="暂无客户数据">
+    <template #top>
+      <uni-nav-bar title="标题" backgroundColor="#edf0f9" :fixed="true" :shadow="true" :border="false" :leftWidth="0"
+        class="c-nav-bar" :rightWidth="58">
+        <view class="input-view">
+          <uni-search-bar placeholder="搜索客户名称" bgColor="#f8f8f8" clearButton="auto" cancelButton="none"
+            style="width:100%; line-height: 30px;height: 30px;" @confirm="filterCustomer" v-model="name"
+            @blur="filterCustomer" @clear="clear">
+          </uni-search-bar>
+        </view>
+        <template #right>
+          <button size="default" class="mini-btn"
+            hover-class="is-hover" @click="redierectAddCustomerPage()">新增</button>
+        </template>
+      </uni-nav-bar>
+    </template>
+    <List :customer-list="customerList" />
+  </z-paging>
+</template>
+
+<script lang="ts" setup>
+import { getCustomerList } from '@/api/biz/customer';
+import List from './List.vue'
+import { Customer } from '@/types/customer';
+import { useEmitt } from '@/logics/mitt/useEmitt';
+const detaultPageSize = 10;
+const paging = ref()
+const name = ref()
+const customerList = ref<Customer[]>([])
+const showClearIcon = ref(false)
+
+const { emitter } = useEmitt()
+
+const nameInput = (event: any) => {
+  if (event.detail.value.length > 0) {
+    showClearIcon.value = true;
+  } else {
+    showClearIcon.value = false;
+  }
+}
+
+// @query所绑定的方法不要自己调用!!需要刷新列表数据时,只需要调用paging.value.reload()即可
+const queryList = async (pageNo: number, pageSize: number) => {
+  try {
+    const { list } = await getCustomerList({ page_no: pageNo, page_size: pageSize })
+    paging.value?.complete(list);
+  } catch (e) {
+    paging.value.complete(false);
+  }
+}
+
+const filterCustomer = async () => {
+  const { list } = await getCustomerList({ page_no: 1, page_size: detaultPageSize, name: name.value })
+  customerList.value = list;
+}
+
+const clear = () => {
+  name.value = undefined;
+  filterCustomer()
+}
+
+const redierectAddCustomerPage = () => {
+  uni.navigateTo({ url: '/pages/profile/customer/add-customer' })
+}
+
+// 监听子组件增加,修改,删除事件,更新列表
+useEmitt({
+  name: 'get_customer_list',
+  callback: () => {
+    console.log('刷新')
+    filterCustomer()
+  }
+})
+
+// 监听子组件增加,修改,删除事件,更新列表
+useEmitt({
+  name: 'remove_customer',
+  callback: (id: string) => {
+    customerList.value = customerList.value.filter(item => item.id !== id)
+    uni.showToast({
+      title: '删除成功',
+      icon: 'success',
+      duration: 1500
+    })
+  }
+})
+
+
+</script>
+<script lang="ts">
+export default {
+  options: {
+    styleIsolation: 'apply-shared', //解除样式隔离
+  }
+
+}
+</script>
+<style scoped lang="scss">
+.c-nav-bar {
+  &__content {
+    color: red;
+  }
+}
+
+.is-hover {
+  color: rgba(255, 255, 255, 0.6);
+  background-color: #179b16;
+  border-color: #179b16;
+}
+
+.city {
+  /* #ifndef APP-PLUS-NVUE */
+  display: flex;
+  /* #endif */
+  flex-direction: row;
+  align-items: center;
+  justify-content: flex-start;
+  margin-left: 4px;
+}
+
+.box-bg {
+  background-color: #F5F5F5;
+}
+
+
+.input-view {
+  /* #ifndef APP-PLUS-NVUE */
+  display: flex;
+  /* #endif */
+  flex-direction: row;
+  flex: 1;
+  // background-color: #f8f8f8;
+  // height: 30px;
+  // border-radius: 15px;
+  flex-wrap: nowrap;
+  margin: 7px 3px 7px 0px;
+  // padding-left: 4px;
+  line-height: 30px;
+}
+
+.input-uni-icon {
+  line-height: 30px;
+}
+
+.nav-bar-input {
+  height: 30px;
+  line-height: 30px;
+  /* #ifdef APP-PLUS-NVUE */
+  /* #endif */
+  padding: 0 5px;
+  font-size: 12px;
+  background-color: #f8f8f8;
+}
+
+:deep(.uni-searchbar__box-icon-clear) {
+  padding-top: 5px;
+}
+
+.uni-navbar {
+  &__header {
+    padding: 0 !important
+  }
+}
+
+:deep(.uni-searchbar) {
+  padding: 0;
+  width: 100%
+}
+
+:deep(.uni-searchbar__box) {
+  height: 30px !important;
+}
+
+:deep(.uni-navbar__header-container) {
+  padding: 0px !important;
+}
+
+
+:deep(.uni-searchbar__cancel) {
+  height: 30px;
+  line-height: 30px;
+}
+
+.mini-btn {
+  color: #ffffff !important;
+  background-color: #007aff;
+  border-color: #007aff !important;
+  font-size: 24rpx !important;
+  -webkit-tap-highlight-color: transparent;
+  border-radius: 5px;
+  box-sizing: border-box;
+  cursor: pointer;
+  display: block;
+  line-height: 2.55555556;
+  margin-left: auto;
+  margin-right: auto;
+  overflow: hidden;
+  padding-left: 28rpx;
+  padding-right: 28rpx;
+  position: relative;
+  text-align: center;
+  text-decoration: none;
+  height: auto;
+}
+
+</style>

+ 301 - 0
src/pages/profile/index.vue

@@ -0,0 +1,301 @@
+<template>
+	<view class="page">
+		<uni-nav-bar background-color="linear-gradient(180deg, #007aff, #fff)" status-bar :border="false"
+			title="个人中心" />
+		<view class="user" :style="{ paddingTop: navHeight + 'px' }">
+			<image class="avatar" src="@img/profile.png"></image>
+			<view class="name">
+				<text>{{ name }}</text>
+				<text>某部门</text>
+			</view>
+		</view>
+
+		<view class="card">
+			<view class="important">
+				<view class="item" @click="redirectWorkPage()">
+					<uni-badge style="width: 80rpx;height: 80rpx; margin-bottom: 10rpx;" class="uni-badge-left-margin"
+						:is-dot="false" :text="undoneCount" :customStyle="{
+							fontSize: '14px',
+							padding: '2px 6px',
+							height: '24px'
+						}" absolute="rightTop" size="small">
+						<image src="@img/work.png"></image>
+					</uni-badge>
+					<text>未完成工作</text>
+				</view>
+				<view class="item" @click="redirectReportWeekPage()">
+					<image src="@img/report.png"></image>
+					<text>我的周报</text>
+				</view>
+				<view class="item" @click="redirectContractPage()">
+					<image src="@img/contract.png"></image>
+					<text>我的合同</text>
+				</view>
+			</view>
+		</view>
+
+		<view class="card">
+			<view class="menu">
+				<view class="item" @click="redirectInfoPage()">
+					<image src="@img/idcard.png"></image>
+					<text>个人资料</text>
+					<image class="arrow" src="@img/right.png"></image>
+				</view>
+				<view class="item" @click="redirectCustomerPage()">
+					<image src="@img/customer.png"></image>
+					<text>客户管理</text>
+					<image class="arrow" src="@img/right.png"></image>
+				</view>
+				<view class="item" @click="redirecArchivePage()">
+					<image src="@img/archiver.png"></image>
+					<text>归档文件</text>
+					<image class="arrow" src="@img/right.png"></image>
+				</view>
+			</view>
+		</view>
+
+		<view class="card">
+			<view class="menu">
+				<button open-type="feedback" style="background: transparent; border: none;text-align: left;padding: 0;"
+					class="contract-btn">
+					<view class="item">
+						<image src="@img/bug.png"></image>
+						<text>BUG反馈</text>
+						<image class="arrow" src="@img/right.png"></image>
+					</view>
+				</button>
+				<button open-type="contact" style="background: transparent; border: none;text-align: left;padding: 0;"
+					class="contract-btn">
+					<view class="item">
+						<image src="@img/kefu.png"></image>
+						<text>联系客服</text>
+						<image class="arrow" src="@img/right.png"></image>
+					</view>
+				</button>
+				<view class="item" @click="redirectVersionPage()">
+					<image src="@img/version.png"></image>
+					<text>版本记录
+						<text class="ver-text">v1.1.6</text>
+					</text>
+					<image class="arrow" src="@img/right.png"></image>
+				</view>
+			</view>
+		</view>
+	</view>
+</template>
+
+
+<script setup lang="ts">
+import { getSelfInfo } from '@/api/base/client';
+import { getWorkUndoneCount } from '@/api/project-center/work';
+import { useEmitt } from '@/logics/mitt/useEmitt';
+import { SelfInfoResParams } from '@/types/client';
+import { getHeaderHeight } from '@/utils';
+
+const navHeight = ref(0) //顶部导航区域高度
+const undoneCount = ref(0)
+const name = ref()
+const { emitter } = useEmitt()
+
+
+/**
+* 获取个人账号信息
+*/
+const getUserInfo = async () => {
+	const result: SelfInfoResParams = await getSelfInfo()
+	name.value = result.name
+}
+
+const redirectWorkPage = async () => {
+	uni.switchTab({
+		url: `/pages/work/index`,
+		success: () => {
+			emitter.emit('get_incomplete_work_list', 0)
+		},
+	})
+}
+
+const redirectReportWeekPage = async () => {
+	uni.navigateTo({ url: '/pages/profile/week-report/index' })
+}
+
+const redirectContractPage = async () => {
+	uni.navigateTo({ url: '/pages/profile/contract/index' })
+}
+
+const redirectCustomerPage = async () => {
+	uni.navigateTo({ url: '/pages/profile/customer/index' })
+}
+
+const redirectVersionPage = async () => {
+	uni.navigateTo({ url: '/pages/profile/version/index' })
+}
+
+const redirectInfoPage = async () => {
+	uni.navigateTo({ url: '/pages/profile/info/index' })
+}
+
+const redirecArchivePage = async () => {
+	uni.navigateTo({ url: '/pages/profile/archive/index' })
+}
+
+const getUndoneCount = async () => {
+	const { undone_count } = await getWorkUndoneCount()
+	undoneCount.value = undone_count
+}
+
+// 监听子组件增加,修改,删除事件,更新列表
+useEmitt({
+	name: 'set_user_name',
+	callback: (new_name: string) => {
+		name.value = new_name
+	}
+})
+
+
+
+onShow(() => {
+	getUndoneCount()
+})
+
+onMounted(() => {
+	navHeight.value = getHeaderHeight().navHeight
+	getUserInfo()
+	getUndoneCount()
+})
+</script>
+
+<style lang="scss">
+.page {
+	background: linear-gradient(180deg, #007aff, #fff);
+	min-height: 100vh;
+
+	.user {
+		display: flex;
+		align-items: center;
+		padding: 25rpx;
+
+		&:active {
+			background: #3c8a3e;
+		}
+
+		.avatar {
+			width: 90rpx;
+			height: 90rpx;
+			border-radius: 50%;
+		}
+
+		.right {
+			width: 30rpx;
+			height: 30rpx;
+		}
+
+		.name {
+			height: 80rpx;
+			flex-grow: 1;
+			display: flex;
+			flex-direction: column;
+			padding-left: 20rpx;
+			color: #fff;
+			justify-content: space-between;
+
+			text {
+				&:nth-child(1) {
+					font-size: 16px;
+					font-weight: bold;
+				}
+
+				&:nth-child(2) {
+					font-size: 12px;
+					color: #f0f0f0;
+				}
+			}
+		}
+	}
+
+	.card {
+		background: #fff;
+		box-sizing: border-box;
+		width: 700rpx;
+		margin: auto;
+		padding: 15rpx;
+		margin-top: 20rpx;
+		border-radius: 12rpx;
+
+		.important {
+			display: flex;
+			justify-content: center;
+
+			.item {
+				width: 225rpx;
+				padding: 15rpx 0;
+				display: flex;
+				flex-direction: column;
+				align-items: center;
+				border-radius: 12rpx;
+
+				image {
+					width: 80rpx;
+					height: 80rpx;
+					margin-bottom: 10rpx;
+				}
+
+				text {
+					font-size: 12px;
+
+				}
+			}
+		}
+
+		.menu {
+			button::after {
+				border: none;
+			}
+
+			.item {
+				display: flex;
+				height: 100rpx;
+				align-items: center;
+				padding-left: 20rpx;
+				box-sizing: border-box;
+				width: 100%;
+
+				&:active {
+					background: #f0f0f0;
+				}
+
+				image {
+					height: 45rpx;
+					width: 45rpx;
+				}
+
+				text {
+					font-size: 28rpx;
+					color: #444;
+					padding-left: 15rpx;
+					box-sizing: border-box;
+					width: calc(100% - 100rpx);
+
+					.ver-text {
+						font-size: 26rpx;
+						position: relative;
+						top: 1rpx;
+						color: #007aff;
+						width: auto;
+						float: right;
+						margin-right: 10rpx;
+					}
+				}
+
+
+
+				.arrow {
+					width: 26rpx;
+					height: 26rpx;
+				}
+			}
+		}
+	}
+
+}
+</style>

+ 297 - 0
src/pages/profile/info/index.vue

@@ -0,0 +1,297 @@
+<template>
+  <view class="container">
+    <uni-section class="mb-10" title="个人资料" type="line"></uni-section>
+    <uni-list :border="true">
+      <uni-list-item title="用户名称" :rightText="detail.self?.name">
+        <template #footer>
+          <view style="width:100%">
+            <EditorListItem style="width:100%" v-model="modifyParams.name" placeholder="用户名称"
+              :old-value="detail.self?.name" :not-allow-null="true" :components-type="ComponentsType.STRINGINPUT"
+              @modify="modifyInfo" />
+          </view>
+        </template>
+      </uni-list-item>
+      <uni-list-item title="手机号码" :rightText="detail.self?.mobile" />
+      <uni-list-item title="电子邮箱" :rightText="detail.self?.email">
+        <template #footer>
+          <view style="width:100%">
+            <EditorListItem style="width:100%" v-model="modifyParams.email" placeholder="电子邮箱"
+              :old-value="detail.self?.email" :not-allow-null="false" :components-type="ComponentsType.STRINGINPUT"
+              @modify="modifyInfo" />
+          </view>
+        </template>
+      </uni-list-item>
+      <uni-list-item title="注册时间" :rightText="detail.self?.reg_time">
+      </uni-list-item>
+      <uni-list-item title="最近活动" :rightText="detail.self?.act_time">
+      </uni-list-item>
+    </uni-list>
+    <view style="margin-top: 20rpx;" v-if="!showResetPwdForm">
+      <custom-button width="100%" type="filled" color="#007aff" @click="handlerResetPwd()">重置密码</custom-button>
+    </view>
+    <view v-else>
+      <uni-section class="mb-10" title="重置密码" type="line"></uni-section>
+      <view style="padding:20rpx;background: #fff;">
+        <uni-forms ref="formRef" :modelValue="resetPwdFormData" :rules="rules" validateTrigger="submit"
+          label-position="top">
+          <uni-forms-item label="原密码" required label-width="90px" name="old_pass">
+            <uni-easyinput v-model="resetPwdFormData.old_pass" placeholder="请输入原登录密码" type="password" />
+          </uni-forms-item>
+          <uni-forms-item label="新密码" required label-width="90px" name="new_pass">
+            <uni-easyinput v-model="resetPwdFormData.new_pass" placeholder="请输入新的登录密码" type="password" />
+          </uni-forms-item>
+          <uni-forms-item label="确认新密码" required label-width="90px" name="confirm_new_pass">
+            <uni-easyinput v-model="resetPwdFormData.confirm_new_pass" placeholder="请再次输入新登录密码" type="password" />
+          </uni-forms-item>
+        </uni-forms>
+        <view style="margin-bottom: 10px;display: flex;">
+          <custom-button width="100%" type="filled" color="#f2f3f5" style="flex:1;margin-right: 5px;"
+            @click="handlerCancelResetPwd()"><text style="color: #606266;">取消</text></custom-button>
+          <custom-button width="100%" type="filled" color="#007aff" style="flex:1"
+            @click="handlerConfirmResetPwd()">重置</custom-button>
+        </view>
+      </view>
+    </view>
+  </view>
+</template>
+
+<script setup lang="ts">
+import { getSelfInfo, setSelfInfo } from '@/api/base/client';
+import { SelfInfoResParams } from '@/types/client';
+import EditorListItem from '@/components/editor-list-item/editor-list-item.vue'
+import { ComponentsType } from '@/enums/api'
+import { useEmitt } from '@/logics/mitt/useEmitt';
+import { Md5 } from 'ts-md5'
+
+const detail = reactive<{ self?: SelfInfoResParams }>({
+  self: undefined
+})
+
+const showResetPwdForm = ref(false)
+const formRef = ref()
+const { emitter } = useEmitt()
+
+const resetPwdFormData = ref<{
+  old_pass?: string
+  new_pass?: string
+  confirm_new_pass?: string
+}>({
+
+})
+
+const modifyParams = ref<{
+  name?: string,
+  email?: string
+}>({
+
+})
+
+const rules = {
+  // 对name字段进行必填验证
+  old_pass: {
+    // name 字段的校验规则
+    rules: [
+      // 校验 name 不能为空
+      {
+        required: true,
+        errorMessage: '原密码不能为空',
+      }
+    ]
+  },
+  new_pass: {
+    // name 字段的校验规则
+    rules: [
+      // 校验 name 不能为空
+      {
+        required: true,
+        errorMessage: '新密码不能为空',
+      }
+    ]
+  },
+  confirm_new_pass: {
+    // name 字段的校验规则
+    rules: [
+      // 校验 name 不能为空
+      {
+        required: true,
+        errorMessage: '再次确认密码不能为空',
+      }, {
+        validateFunction: function (rule: any, value: any, data: any, callback: any) {
+          if (value !== data.new_pass) {
+            callback('两次密码不一致')
+          }
+          return true
+        }
+      }
+    ]
+  }
+
+
+}
+
+const getUserInfo = async () => {
+  const result: SelfInfoResParams = await getSelfInfo();
+  detail.self = result
+  modifyParams.value.name = result.name
+  modifyParams.value.email = result.email
+  uni.setNavigationBarTitle({
+    title: result.name,
+  })
+}
+
+// 修改用户名或邮箱
+const modifyInfo = async () => {
+  try {
+    const { name, email } = modifyParams.value
+    await setSelfInfo({
+      name,
+      email
+    })
+    uni.showToast(({
+      icon: 'success',
+      title: '修改成功'
+    }))
+    emitter.emit('set_user_name', name)
+    uni.setNavigationBarTitle({
+      title: name!,
+    })
+  } catch (e) {
+    getUserInfo()
+  }
+
+}
+
+// 显示重置密码表单
+const handlerResetPwd = async () => {
+  showResetPwdForm.value = true
+}
+
+// 取消重置密码
+const handlerCancelResetPwd =  () => {
+  resetPwdFormData.value = {}
+  formRef.value?.clearValidate()
+  showResetPwdForm.value = false
+}
+
+// 确认重置密码
+const handlerConfirmResetPwd = async () => {
+  const isValid = await formRef.value?.validate()
+  if (!isValid) {
+    return
+  }
+  const { old_pass, new_pass } = resetPwdFormData.value;
+  await setSelfInfo({
+    old_pass: Md5.hashStr(old_pass!) ,
+    new_pass: Md5.hashStr(new_pass!) 
+  })
+  uni.showToast({
+    icon: 'success',
+    title: '重置成功'
+  })
+  handlerCancelResetPwd()
+}
+onMounted(() => {
+  getUserInfo()
+})
+
+</script>
+<script lang="ts">
+export default {
+  options: {
+    styleIsolation: 'apply-shared', //解除样式隔离
+  }
+
+}
+</script>
+<style scoped>
+.container {
+  padding: 0 5px 40px 5px;
+}
+
+:deep(.uni-list-item__content-note) {
+  overflow-wrap: break-word;
+}
+
+:deep(.uni-list-item__content) {
+  flex-direction: row;
+  align-items: center;
+  flex: 0 0 80px;
+}
+
+:deep(.uni-list-item__content-title) {
+  font-size: 13px;
+  color: #3b4144a8;
+}
+
+:deep(.uni-list-item__extra) {
+  flex: 1 1 auto;
+}
+
+:deep(.uni-list-item__extra-text) {
+  color: #5d5959;
+  font-size: 13px;
+  width: 200px;
+  text-overflow: ellipsis;
+  text-align: right;
+  text-wrap: wrap;
+  overflow-wrap: anywhere;
+  white-space-collapse: collapse;
+}
+
+.slot-box {
+  /* #ifndef APP-NVUE */
+  display: flex;
+  /* #endif */
+  flex-direction: row;
+  align-items: center;
+  width: 45px;
+  background-color: rgb(201, 205, 212);
+  border-radius: 4px;
+  margin-right: 10px;
+  height: 45px;
+}
+
+
+.slot-content {
+  display: flex;
+  justify-content: space-between;
+  flex-direction: row;
+  width: 100%;
+  height: 45px;
+}
+
+.slot-panel {
+  display: flex;
+  flex-direction: column;
+  justify-content: space-around;
+}
+
+.slot-title {
+  display: flex;
+  font-size: 14px;
+  color: #3b4144;
+}
+
+.slot-subtitle {
+  display: flex;
+  font-size: 12px;
+  color: #999
+}
+
+.slot-desc {
+  font-size: 12px;
+  color: #999;
+  max-width: 200px;
+  max-height: 50px;
+  overflow: hidden;
+  text-overflow: ellipsis;
+  display: -webkit-box;
+  -webkit-box-orient: vertical;
+  -webkit-line-clamp: 3;
+
+}
+
+:deep(.uni-section) {
+  margin-top: 5px;
+}
+</style>

+ 22 - 0
src/pages/profile/version/index.vue

@@ -0,0 +1,22 @@
+<template>
+  <view class="container">
+    <uni-steps :options="VersionList.list" active-color="#007AFF"  direction="column" />
+  </view>
+</template>
+
+<script lang="ts" setup>
+import VersionList from './ver.json'
+</script>
+<style scoped>
+.container {
+ margin: 10rpx;
+ padding: 20rpx; 
+ background: #fff;
+}
+:deep(.uni-steps__column-title){
+  font-size: 32rpx;
+}
+:deep(.uni-steps__column-desc){
+  font-size: 28rpx;
+}
+</style>

Some files were not shown because too many files changed in this diff