Pārlūkot izejas kodu

feat: 迁移项目;

liujia 2 mēneši atpakaļ
revīzija
7d418fcd64
100 mainītis faili ar 10175 papildinājumiem un 0 dzēšanām
  1. 9 0
      .editorconfig
  2. 8 0
      .env.development
  3. 8 0
      .env.production
  4. 37 0
      .eslintrc.cjs
  5. 1 0
      .gitattributes
  6. 37 0
      .gitignore
  7. 4 0
      .prettierignore
  8. 20 0
      .prettierrc.json
  9. 8 0
      .vscode/extensions.json
  10. 25 0
      Dockerfile
  11. 125 0
      Jenkinsfile
  12. 57 0
      components.d.ts
  13. 14 0
      env.d.ts
  14. 22 0
      eslint.config.ts
  15. 13 0
      index.html
  16. 36 0
      nginx.conf
  17. 59 0
      package.json
  18. 5405 0
      pnpm-lock.yaml
  19. BIN
      public/favicon.ico
  20. BIN
      public/设备清单.xlsx
  21. 53 0
      src/App.vue
  22. 18 0
      src/api/admin/index.ts
  23. 41 0
      src/api/admin/types.ts
  24. 71 0
      src/api/device/index.ts
  25. 258 0
      src/api/device/types.ts
  26. 6 0
      src/api/pub/index.ts
  27. 0 0
      src/api/pub/types.ts
  28. 18 0
      src/api/room/index.ts
  29. 59 0
      src/api/room/types.ts
  30. 23 0
      src/api/stats/index.ts
  31. 154 0
      src/api/stats/types.ts
  32. 15 0
      src/api/system/index.ts
  33. 0 0
      src/api/system/types.ts
  34. 92 0
      src/api/tenant/index.ts
  35. 67 0
      src/api/tenant/types.ts
  36. 18 0
      src/api/user/index.ts
  37. 41 0
      src/api/user/types.ts
  38. BIN
      src/assets/furnitures/bath_basin.png
  39. BIN
      src/assets/furnitures/bath_door.png
  40. BIN
      src/assets/furnitures/bath_floor.png
  41. BIN
      src/assets/furnitures/bath_shower.png
  42. BIN
      src/assets/furnitures/bath_toilet.png
  43. BIN
      src/assets/furnitures/bed.png
  44. BIN
      src/assets/furnitures/bed_cabinet.png
  45. BIN
      src/assets/furnitures/bed_dressing_chair.png
  46. BIN
      src/assets/furnitures/bed_dressing_mirror.png
  47. BIN
      src/assets/furnitures/bed_floor.png
  48. BIN
      src/assets/furnitures/bed_table.png
  49. BIN
      src/assets/furnitures/dining_chair.png
  50. BIN
      src/assets/furnitures/dining_floor.png
  51. BIN
      src/assets/furnitures/dining_fridge.png
  52. BIN
      src/assets/furnitures/dining_table.png
  53. BIN
      src/assets/furnitures/dining_table_rect.png
  54. BIN
      src/assets/furnitures/living_bookcase.png
  55. BIN
      src/assets/furnitures/living_floor.png
  56. BIN
      src/assets/furnitures/living_sofa.png
  57. BIN
      src/assets/furnitures/living_sofa_single.jpg
  58. BIN
      src/assets/furnitures/living_tea_table.png
  59. BIN
      src/assets/furnitures/living_tv_stand.png
  60. BIN
      src/assets/furnitures/radar.png
  61. BIN
      src/assets/logo.png
  62. 80 0
      src/components/baseCard/components/e-charts/index.vue
  63. 63 0
      src/components/baseCard/index.vue
  64. 63 0
      src/components/basePagination/index.vue
  65. 102 0
      src/components/baseWeather/index.vue
  66. 45 0
      src/components/furnitureIcon/index.vue
  67. 79 0
      src/components/furnitureItem/index.vue
  68. 52 0
      src/components/furnitureList/index.vue
  69. 82 0
      src/components/rangePicker/index.vue
  70. 23 0
      src/const/device.ts
  71. 179 0
      src/const/furniture.ts
  72. 61 0
      src/const/menus.ts
  73. 40 0
      src/hooks/useDict.ts
  74. 18 0
      src/hooks/useDictName.ts
  75. 32 0
      src/hooks/useSearch.ts
  76. 368 0
      src/layout/components/alertModal/index.vue
  77. 62 0
      src/layout/components/timeNow/index.vue
  78. 58 0
      src/layout/components/userDropdown/index.vue
  79. 373 0
      src/layout/index.vue
  80. 30 0
      src/main.ts
  81. 106 0
      src/request/index.ts
  82. 15 0
      src/router/guard.ts
  83. 28 0
      src/router/index.ts
  84. 8 0
      src/router/modules/alarm.ts
  85. 22 0
      src/router/modules/community.ts
  86. 22 0
      src/router/modules/device.ts
  87. 8 0
      src/router/modules/login.ts
  88. 8 0
      src/router/modules/system.ts
  89. 8 0
      src/router/modules/user.ts
  90. 16 0
      src/router/typed-router.d.ts
  91. 7 0
      src/stores/index.ts
  92. 122 0
      src/stores/user.ts
  93. 6 0
      src/styles/index.css
  94. BIN
      src/styles/qweather/fonts/qweather-icons.ttf
  95. BIN
      src/styles/qweather/fonts/qweather-icons.woff
  96. BIN
      src/styles/qweather/fonts/qweather-icons.woff2
  97. 1018 0
      src/styles/qweather/qweather-icons.css
  98. 55 0
      src/types/furniture.ts
  99. 50 0
      src/types/global.d.ts
  100. 44 0
      src/utils/index.ts

+ 9 - 0
.editorconfig

@@ -0,0 +1,9 @@
+[*.{js,jsx,mjs,cjs,ts,tsx,mts,cts,vue,css,scss,sass,less,styl}]
+charset = utf-8
+indent_size = 2
+indent_style = space
+insert_final_newline = true
+trim_trailing_whitespace = true
+
+end_of_line = lf
+max_line_length = 100

+ 8 - 0
.env.development

@@ -0,0 +1,8 @@
+
+NODE_ENV=development
+VITE_ENV=development
+MODE=development
+VITE_APP_TITLE="雷能信息技术"
+# VITE_API_BASE_URL=http://jkld.radar-power.com/
+VITE_API_BASE_URL=/portal-service-server/web
+VITE_STOTRE_NAME=LEINENG_WEB

+ 8 - 0
.env.production

@@ -0,0 +1,8 @@
+
+NODE_ENV=production
+VITE_ENV=production
+MODE=production
+VITE_APP_TITLE="雷能信息技术"
+# VITE_API_BASE_URL=http://jkld.radar-power.com/
+VITE_API_BASE_URL=/portal-service-server/web
+VITE_STOTRE_NAME=LEINENG_WEB

+ 37 - 0
.eslintrc.cjs

@@ -0,0 +1,37 @@
+module.exports = {
+  root: true,
+  env: {
+    browser: true,
+    es2024: true,
+    node: true,
+  },
+  extends: [
+    'eslint:recommended',
+    'plugin:vue/vue3-recommended',
+    '@vue/eslint-config-typescript',
+    '@vue/eslint-config-prettier',
+  ],
+  parserOptions: {
+    ecmaVersion: 'latest',
+    parser: '@typescript-eslint/parser',
+    sourceType: 'module',
+  },
+  plugins: ['vue', '@typescript-eslint'],
+  rules: {
+    // 自定义规则
+    'vue/multi-word-component-names': 'off', // 允许单文件组件使用单个单词
+    '@typescript-eslint/no-explicit-any': 'off', // 允许使用 any 类型
+    'vue/html-self-closing': [
+      'error',
+      {
+        // 自闭合标签配置
+        html: {
+          void: 'always',
+          normal: 'always',
+          component: 'always',
+        },
+      },
+    ],
+    'prettier/prettier': ['error', { endOfLine: 'auto' }],
+  },
+}

+ 1 - 0
.gitattributes

@@ -0,0 +1 @@
+* text=auto eol=lf

+ 37 - 0
.gitignore

@@ -0,0 +1,37 @@
+# Logs
+logs
+*.log
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+pnpm-debug.log*
+lerna-debug.log*
+
+node_modules
+.DS_Store
+dist
+dist-ssr
+coverage
+*.local
+
+/cypress/videos/
+/cypress/screenshots/
+
+# Editor directories and files
+.vscode/*
+!.vscode/extensions.json
+.idea
+*.suo
+*.ntvs*
+*.njsproj
+*.sln
+*.sw?
+
+*.tsbuildinfo
+
+**/dist/*
+**/node_modules/*
+*.md
+.lh
+# *.json
+# !tsconfig.app.json

+ 4 - 0
.prettierignore

@@ -0,0 +1,4 @@
+/dist
+/node_modules
+*.md
+*.json

+ 20 - 0
.prettierrc.json

@@ -0,0 +1,20 @@
+{
+  "printWidth": 100,
+  "tabWidth": 2,
+  "useTabs": false,
+  "semi": false,
+  "singleQuote": true,
+  "quoteProps": "as-needed",
+  "jsxSingleQuote": true,
+  "trailingComma": "es5",
+  "bracketSpacing": true,
+  "jsxBracketSameLine": false,
+  "arrowParens": "always",
+  "requirePragma": false,
+  "insertPragma": false,
+  "proseWrap": "never",
+  "htmlWhitespaceSensitivity": "strict",
+  "vueIndentScriptAndStyle": false,
+  "endOfLine": "auto",
+  "embeddedLanguageFormatting": "auto"
+}

+ 8 - 0
.vscode/extensions.json

@@ -0,0 +1,8 @@
+{
+  "recommendations": [
+    "Vue.volar",
+    "dbaeumer.vscode-eslint",
+    "EditorConfig.EditorConfig",
+    "esbenp.prettier-vscode"
+  ]
+}

+ 25 - 0
Dockerfile

@@ -0,0 +1,25 @@
+FROM node:20 AS builder
+
+WORKDIR /app
+
+COPY package*.json ./
+RUN npm config set registry https://registry.npmmirror.com
+RUN npm install
+
+COPY . .
+
+ARG ENV=production
+ENV NODE_ENV=$ENV
+ENV VITE_APP_ENV=$ENV
+
+RUN npm run build
+
+FROM nginx:alpine
+
+COPY --from=builder /app/dist /usr/share/nginx/html
+
+COPY nginx.conf /etc/nginx/conf.d/default.conf
+
+
+# 启动 nginx
+CMD ["nginx", "-g", "daemon off;"]

+ 125 - 0
Jenkinsfile

@@ -0,0 +1,125 @@
+pipeline {
+    agent any
+
+    parameters {
+        choice(name: 'env', choices: ['dev', 'test', 'prod'], description: '部署环境')
+        string(name: 'NAMESPACE', defaultValue: 'hfln-dev', description: 'K8s 命名空间')
+    }
+
+    environment {
+        PROJECT_NAME    = 'new-portal-frontend'
+        BUILD_DIR       = 'dist'
+        NODE_ENV        = 'production'
+        HARBOR_HOST     = '8.130.28.21:81'
+        KUBECONFIG_PATH = '/root/.kube/config'
+        HARBOR_USER     = 'admin'
+        HARBOR_PASS     = 'Hfln@1024'
+        HARBOR_RETENTION_ID = '1'
+    }
+
+    stages {
+        stage('🧬 初始化环境') {
+            steps {
+                script {
+                    env.HARBOR_PROJECT = params.env
+                    env.IMAGE_TAG = "${HARBOR_HOST}/${env.HARBOR_PROJECT}/${PROJECT_NAME}:${BUILD_NUMBER}"
+                    echo ">>> 环境:${params.env}, Harbor项目:${env.HARBOR_PROJECT}, K8s命名空间:${params.NAMESPACE}"
+                }
+            }
+        }
+
+        stage('📥 拉取代码') {
+            steps {
+                checkout scm
+                echo "✅ 代码拉取成功"
+            }
+        }
+
+        stage('🔧 构建 Docker 镜像') {
+            steps {
+                script {
+                    sh """
+                        docker login -u ${HARBOR_USER} -p ${HARBOR_PASS} ${HARBOR_HOST}
+                        docker build --build-arg ENV=${params.env} -t ${IMAGE_TAG} .
+                    """
+                    echo "✅ 镜像构建成功:${IMAGE_TAG}"
+                }
+            }
+        }
+
+        stage('🚀 推送镜像到 Harbor') {
+            steps {
+                script {
+                    sh """
+                        docker push ${IMAGE_TAG}
+                        docker rmi ${IMAGE_TAG}
+                    """
+                    echo "✅ 镜像推送并本地清理完成"
+                }
+            }
+        }
+
+        stage('📦 Kubernetes 部署') {
+            steps {
+                script {
+                    sh """
+                        export KUBECONFIG=${KUBECONFIG_PATH}
+                        kubectl -n ${params.NAMESPACE} set image deployment/${PROJECT_NAME} ${PROJECT_NAME}=${IMAGE_TAG}
+                    """
+                    echo "✅ 已更新部署镜像至 K8s"
+                }
+            }
+        }
+
+        stage('🧹 清理本地旧镜像(保留最新3个)') {
+            steps {
+                script {
+                    def baseImage = "${HARBOR_HOST}/${env.HARBOR_PROJECT}/${PROJECT_NAME}"
+                    sh """
+                        docker images ${baseImage} --format "{{.Repository}}:{{.Tag}}" \\
+                        | grep -v latest \\
+                        | sort -r -t ':' -k2 \\
+                        | tail -n +4 \\
+                        | xargs -r docker rmi || true
+                    """
+                    echo "✅ 本地旧镜像清理完成"
+                }
+            }
+        }
+
+        stage('🧹 清理悬空镜像 <none>') {
+            steps {
+                script {
+                    sh """
+                        docker images -f "dangling=true" -q | xargs -r docker rmi || true
+                    """
+                    echo "✅ 悬空镜像(<none>)清理完成"
+                }
+            }
+        }
+
+        stage('🧹 触发 Harbor 镜像保留策略') {
+            steps {
+                script {
+                    sh """
+                        curl -u ${HARBOR_USER}:${HARBOR_PASS} -X POST \\
+                        "http://${HARBOR_HOST}/api/v2.0/retentions/${HARBOR_RETENTION_ID}/executions"
+                    """
+                    echo "✅ Harbor 镜像保留策略已触发"
+                }
+            }
+        }
+    }
+
+    post {
+        success {
+            echo "✅ 构建 & 部署成功 🎉"
+        }
+        failure {
+            echo "❌ 构建或部署失败,请检查日志"
+        }
+        always {
+            cleanWs()
+        }
+    }
+}

+ 57 - 0
components.d.ts

@@ -0,0 +1,57 @@
+/* eslint-disable */
+// @ts-nocheck
+// Generated by unplugin-vue-components
+// Read more: https://github.com/vuejs/core/pull/3399
+// biome-ignore lint: disable
+export {}
+
+/* prettier-ignore */
+declare module 'vue' {
+  export interface GlobalComponents {
+    AAvatar: typeof import('ant-design-vue/es')['Avatar']
+    AButton: typeof import('ant-design-vue/es')['Button']
+    ACascader: typeof import('ant-design-vue/es')['Cascader']
+    AConfigProvider: typeof import('ant-design-vue/es')['ConfigProvider']
+    ADrawer: typeof import('ant-design-vue/es')['Drawer']
+    ADropdown: typeof import('ant-design-vue/es')['Dropdown']
+    AForm: typeof import('ant-design-vue/es')['Form']
+    AFormItem: typeof import('ant-design-vue/es')['FormItem']
+    AInput: typeof import('ant-design-vue/es')['Input']
+    AInputNumber: typeof import('ant-design-vue/es')['InputNumber']
+    AInputPassword: typeof import('ant-design-vue/es')['InputPassword']
+    ALayout: typeof import('ant-design-vue/es')['Layout']
+    ALayoutContent: typeof import('ant-design-vue/es')['LayoutContent']
+    ALayoutFooter: typeof import('ant-design-vue/es')['LayoutFooter']
+    ALayoutHeader: typeof import('ant-design-vue/es')['LayoutHeader']
+    ALayoutSider: typeof import('ant-design-vue/es')['LayoutSider']
+    AMenu: typeof import('ant-design-vue/es')['Menu']
+    AMenuItem: typeof import('ant-design-vue/es')['MenuItem']
+    AModal: typeof import('ant-design-vue/es')['Modal']
+    APageHeader: typeof import('ant-design-vue/es')['PageHeader']
+    APagination: typeof import('ant-design-vue/es')['Pagination']
+    ARadioButton: typeof import('ant-design-vue/es')['RadioButton']
+    ARadioGroup: typeof import('ant-design-vue/es')['RadioGroup']
+    ARangePicker: typeof import('ant-design-vue/es')['RangePicker']
+    ASelect: typeof import('ant-design-vue/es')['Select']
+    ASelectOption: typeof import('ant-design-vue/es')['SelectOption']
+    ASkeleton: typeof import('ant-design-vue/es')['Skeleton']
+    ASpace: typeof import('ant-design-vue/es')['Space']
+    ASpin: typeof import('ant-design-vue/es')['Spin']
+    ASwitch: typeof import('ant-design-vue/es')['Switch']
+    ATable: typeof import('ant-design-vue/es')['Table']
+    ATag: typeof import('ant-design-vue/es')['Tag']
+    ATooltip: typeof import('ant-design-vue/es')['Tooltip']
+    ATree: typeof import('ant-design-vue/es')['Tree']
+    AUploadDragger: typeof import('ant-design-vue/es')['UploadDragger']
+    BaseCard: typeof import('./src/components/baseCard/index.vue')['default']
+    BasePagination: typeof import('./src/components/basePagination/index.vue')['default']
+    BaseWeather: typeof import('./src/components/baseWeather/index.vue')['default']
+    ECharts: typeof import('./src/components/baseCard/components/e-charts/index.vue')['default']
+    FurnitureIcon: typeof import('./src/components/furnitureIcon/index.vue')['default']
+    FurnitureItem: typeof import('./src/components/furnitureItem/index.vue')['default']
+    FurnitureList: typeof import('./src/components/furnitureList/index.vue')['default']
+    RangePicker: typeof import('./src/components/rangePicker/index.vue')['default']
+    RouterLink: typeof import('vue-router')['RouterLink']
+    RouterView: typeof import('vue-router')['RouterView']
+  }
+}

+ 14 - 0
env.d.ts

@@ -0,0 +1,14 @@
+/// <reference types="vite/client" />
+
+interface ImportMetaEnv {
+  // 基础配置
+  readonly NODE_ENV: 'development' | 'production' | 'test'
+  readonly VITE_ENV: 'dev' | 'test' | 'pre' | 'prod'
+  readonly MODE: string
+  readonly VITE_APP_TITLE: string
+  readonly VITE_API_BASE_URL: string
+}
+
+interface ImportMeta {
+  readonly env: ImportMetaEnv
+}

+ 22 - 0
eslint.config.ts

@@ -0,0 +1,22 @@
+import { globalIgnores } from 'eslint/config'
+import { defineConfigWithVueTs, vueTsConfigs } from '@vue/eslint-config-typescript'
+import pluginVue from 'eslint-plugin-vue'
+import skipFormatting from '@vue/eslint-config-prettier/skip-formatting'
+
+// To allow more languages other than `ts` in `.vue` files, uncomment the following lines:
+// import { configureVueProject } from '@vue/eslint-config-typescript'
+// configureVueProject({ scriptLangs: ['ts', 'tsx'] })
+// More info at https://github.com/vuejs/eslint-config-typescript/#advanced-setup
+
+export default defineConfigWithVueTs(
+  {
+    name: 'app/files-to-lint',
+    files: ['**/*.{ts,mts,tsx,vue}'],
+  },
+
+  globalIgnores(['**/dist/**', '**/dist-ssr/**', '**/coverage/**']),
+
+  pluginVue.configs['flat/essential'],
+  vueTsConfigs.recommended,
+  skipFormatting,
+)

+ 13 - 0
index.html

@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<html lang="">
+  <head>
+    <meta charset="UTF-8">
+    <link rel="icon" href="/favicon.ico">
+    <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
+    <title>%VITE_APP_TITLE%</title>
+  </head>
+  <body>
+    <div id="app"></div>
+    <script type="module" src="/src/main.ts"></script>
+  </body>
+</html>

+ 36 - 0
nginx.conf

@@ -0,0 +1,36 @@
+server {
+    listen 80;
+    server_name localhost;
+
+    root /usr/share/nginx/html;
+    index index.html;
+
+    # 代理 /wap 接口
+    location /wap/ {
+        proxy_pass http://8.130.28.21:31090/wap/;
+        proxy_set_header Host $host;
+        proxy_set_header X-Real-IP $remote_addr;
+        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+        proxy_set_header X-Forwarded-Proto $scheme;
+    }
+
+    # 代理 /portal-service-server 接口
+    location /portal-service-server/ {
+        proxy_pass http://8.130.28.21:31090/portal-service-server/;
+        proxy_set_header Host $host;
+        proxy_set_header X-Real-IP $remote_addr;
+        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+        proxy_set_header X-Forwarded-Proto $scheme;
+    }
+
+    # 静态资源和前端路由
+    location / {
+        try_files $uri $uri/ /index.html;
+    }
+
+    location ~* \.(?:ico|css|js|gif|jpe?g|png|woff2?)$ {
+        expires 30d;
+        access_log off;
+        add_header Cache-Control "public";
+    }
+}

+ 59 - 0
package.json

@@ -0,0 +1,59 @@
+{
+  "name": "vue-project",
+  "version": "0.0.2",
+  "private": true,
+  "type": "module",
+  "scripts": {
+    "dev": "vite --host --mode development --port 3000",
+    "build": "vite build",
+    "build:test": "node version-script.js test",
+    "build:publish": "node version-script.js publish",
+    "build:tag": "node version-script.js build-by-tag",
+    "preview": "vite preview",
+    "type-check": "vue-tsc --build",
+    "lint": "eslint . --fix",
+    "format": "prettier --write src/"
+  },
+  "dependencies": {
+    "@ant-design/icons-vue": "^7.0.1",
+    "@types/lodash-es": "^4.17.12",
+    "@vueuse/components": "^13.5.0",
+    "@vueuse/core": "^13.5.0",
+    "ant-design-vue": "~4.2.6",
+    "axios": "^1.10.0",
+    "dayjs": "^1.11.13",
+    "echarts": "^5.6.0",
+    "lodash-es": "^4.17.21",
+    "mqtt": "^5.13.1",
+    "nanoid": "^5.1.5",
+    "pinia": "^3.0.3",
+    "pinia-plugin-persistedstate": "^4.4.1",
+    "pinyin-pro": "^3.26.0",
+    "vue": "^3.5.17",
+    "vue-router": "^4.5.1"
+  },
+  "devDependencies": {
+    "@tsconfig/node22": "^22.0.2",
+    "@types/echarts": "^5.0.0",
+    "@types/mqtt": "^2.5.0",
+    "@types/node": "^22.15.32",
+    "@vitejs/plugin-vue": "^6.0.0",
+    "@vue/eslint-config-prettier": "^10.2.0",
+    "@vue/eslint-config-typescript": "^14.5.1",
+    "@vue/tsconfig": "^0.7.0",
+    "eslint": "^9.29.0",
+    "eslint-import-resolver-alias": "^1.1.2",
+    "eslint-plugin-import": "^2.32.0",
+    "eslint-plugin-prettier": "^5.5.1",
+    "eslint-plugin-vue": "~10.2.0",
+    "jiti": "^2.4.2",
+    "less": "^4.3.0",
+    "npm-run-all2": "^8.0.4",
+    "prettier": "3.5.3",
+    "typescript": "~5.8.0",
+    "unplugin-vue-components": "^28.8.0",
+    "vite": "^7.0.0",
+    "vite-plugin-vue-devtools": "^7.7.7",
+    "vue-tsc": "^2.2.10"
+  }
+}

+ 5405 - 0
pnpm-lock.yaml

@@ -0,0 +1,5405 @@
+lockfileVersion: '9.0'
+
+settings:
+  autoInstallPeers: true
+  excludeLinksFromLockfile: false
+
+importers:
+
+  .:
+    dependencies:
+      '@ant-design/icons-vue':
+        specifier: ^7.0.1
+        version: 7.0.1(vue@3.5.17(typescript@5.8.3))
+      '@types/lodash-es':
+        specifier: ^4.17.12
+        version: 4.17.12
+      '@vueuse/components':
+        specifier: ^13.5.0
+        version: 13.5.0(vue@3.5.17(typescript@5.8.3))
+      '@vueuse/core':
+        specifier: ^13.5.0
+        version: 13.5.0(vue@3.5.17(typescript@5.8.3))
+      ant-design-vue:
+        specifier: ~4.2.6
+        version: 4.2.6(vue@3.5.17(typescript@5.8.3))
+      axios:
+        specifier: ^1.10.0
+        version: 1.10.0
+      dayjs:
+        specifier: ^1.11.13
+        version: 1.11.13
+      echarts:
+        specifier: ^5.6.0
+        version: 5.6.0
+      lodash-es:
+        specifier: ^4.17.21
+        version: 4.17.21
+      mqtt:
+        specifier: ^5.13.1
+        version: 5.13.1
+      nanoid:
+        specifier: ^5.1.5
+        version: 5.1.5
+      pinia:
+        specifier: ^3.0.3
+        version: 3.0.3(typescript@5.8.3)(vue@3.5.17(typescript@5.8.3))
+      pinia-plugin-persistedstate:
+        specifier: ^4.4.1
+        version: 4.4.1(pinia@3.0.3(typescript@5.8.3)(vue@3.5.17(typescript@5.8.3)))
+      pinyin-pro:
+        specifier: ^3.26.0
+        version: 3.26.0
+      vue:
+        specifier: ^3.5.17
+        version: 3.5.17(typescript@5.8.3)
+      vue-router:
+        specifier: ^4.5.1
+        version: 4.5.1(vue@3.5.17(typescript@5.8.3))
+    devDependencies:
+      '@tsconfig/node22':
+        specifier: ^22.0.2
+        version: 22.0.2
+      '@types/echarts':
+        specifier: ^5.0.0
+        version: 5.0.0
+      '@types/mqtt':
+        specifier: ^2.5.0
+        version: 2.5.0
+      '@types/node':
+        specifier: ^22.15.32
+        version: 22.16.0
+      '@vitejs/plugin-vue':
+        specifier: ^6.0.0
+        version: 6.0.0(vite@7.0.2(@types/node@22.16.0)(jiti@2.4.2)(less@4.3.0))(vue@3.5.17(typescript@5.8.3))
+      '@vue/eslint-config-prettier':
+        specifier: ^10.2.0
+        version: 10.2.0(eslint@9.30.1(jiti@2.4.2))(prettier@3.5.3)
+      '@vue/eslint-config-typescript':
+        specifier: ^14.5.1
+        version: 14.6.0(eslint-plugin-vue@10.2.0(eslint@9.30.1(jiti@2.4.2))(vue-eslint-parser@10.2.0(eslint@9.30.1(jiti@2.4.2))))(eslint@9.30.1(jiti@2.4.2))(typescript@5.8.3)
+      '@vue/tsconfig':
+        specifier: ^0.7.0
+        version: 0.7.0(typescript@5.8.3)(vue@3.5.17(typescript@5.8.3))
+      eslint:
+        specifier: ^9.29.0
+        version: 9.30.1(jiti@2.4.2)
+      eslint-import-resolver-alias:
+        specifier: ^1.1.2
+        version: 1.1.2(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.35.1(eslint@9.30.1(jiti@2.4.2))(typescript@5.8.3))(eslint@9.30.1(jiti@2.4.2)))
+      eslint-plugin-import:
+        specifier: ^2.32.0
+        version: 2.32.0(@typescript-eslint/parser@8.35.1(eslint@9.30.1(jiti@2.4.2))(typescript@5.8.3))(eslint@9.30.1(jiti@2.4.2))
+      eslint-plugin-prettier:
+        specifier: ^5.5.1
+        version: 5.5.1(eslint-config-prettier@10.1.5(eslint@9.30.1(jiti@2.4.2)))(eslint@9.30.1(jiti@2.4.2))(prettier@3.5.3)
+      eslint-plugin-vue:
+        specifier: ~10.2.0
+        version: 10.2.0(eslint@9.30.1(jiti@2.4.2))(vue-eslint-parser@10.2.0(eslint@9.30.1(jiti@2.4.2)))
+      jiti:
+        specifier: ^2.4.2
+        version: 2.4.2
+      less:
+        specifier: ^4.3.0
+        version: 4.3.0
+      npm-run-all2:
+        specifier: ^8.0.4
+        version: 8.0.4
+      prettier:
+        specifier: 3.5.3
+        version: 3.5.3
+      typescript:
+        specifier: ~5.8.0
+        version: 5.8.3
+      unplugin-vue-components:
+        specifier: ^28.8.0
+        version: 28.8.0(@babel/parser@7.28.0)(vue@3.5.17(typescript@5.8.3))
+      vite:
+        specifier: ^7.0.0
+        version: 7.0.2(@types/node@22.16.0)(jiti@2.4.2)(less@4.3.0)
+      vite-plugin-vue-devtools:
+        specifier: ^7.7.7
+        version: 7.7.7(rollup@4.44.2)(vite@7.0.2(@types/node@22.16.0)(jiti@2.4.2)(less@4.3.0))(vue@3.5.17(typescript@5.8.3))
+      vue-tsc:
+        specifier: ^2.2.10
+        version: 2.2.12(typescript@5.8.3)
+
+packages:
+
+  '@ampproject/remapping@2.3.0':
+    resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==}
+    engines: {node: '>=6.0.0'}
+
+  '@ant-design/colors@6.0.0':
+    resolution: {integrity: sha512-qAZRvPzfdWHtfameEGP2Qvuf838NhergR35o+EuVyB5XvSA98xod5r4utvi4TJ3ywmevm290g9nsCG5MryrdWQ==}
+
+  '@ant-design/icons-svg@4.4.2':
+    resolution: {integrity: sha512-vHbT+zJEVzllwP+CM+ul7reTEfBR0vgxFe7+lREAsAA7YGsYpboiq2sQNeQeRvh09GfQgs/GyFEvZpJ9cLXpXA==}
+
+  '@ant-design/icons-vue@7.0.1':
+    resolution: {integrity: sha512-eCqY2unfZK6Fe02AwFlDHLfoyEFreP6rBwAZMIJ1LugmfMiVgwWDYlp1YsRugaPtICYOabV1iWxXdP12u9U43Q==}
+    peerDependencies:
+      vue: '>=3.0.3'
+
+  '@antfu/utils@0.7.10':
+    resolution: {integrity: sha512-+562v9k4aI80m1+VuMHehNJWLOFjBnXn3tdOitzD0il5b7smkSBal4+a3oKiQTbrwMmN/TBUMDvbdoWDehgOww==}
+
+  '@babel/code-frame@7.27.1':
+    resolution: {integrity: sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==}
+    engines: {node: '>=6.9.0'}
+
+  '@babel/compat-data@7.28.0':
+    resolution: {integrity: sha512-60X7qkglvrap8mn1lh2ebxXdZYtUcpd7gsmy9kLaBJ4i/WdY8PqTSdxyA8qraikqKQK5C1KRBKXqznrVapyNaw==}
+    engines: {node: '>=6.9.0'}
+
+  '@babel/core@7.28.0':
+    resolution: {integrity: sha512-UlLAnTPrFdNGoFtbSXwcGFQBtQZJCNjaN6hQNP3UPvuNXT1i82N26KL3dZeIpNalWywr9IuQuncaAfUaS1g6sQ==}
+    engines: {node: '>=6.9.0'}
+
+  '@babel/generator@7.28.0':
+    resolution: {integrity: sha512-lJjzvrbEeWrhB4P3QBsH7tey117PjLZnDbLiQEKjQ/fNJTjuq4HSqgFA+UNSwZT8D7dxxbnuSBMsa1lrWzKlQg==}
+    engines: {node: '>=6.9.0'}
+
+  '@babel/helper-annotate-as-pure@7.27.3':
+    resolution: {integrity: sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg==}
+    engines: {node: '>=6.9.0'}
+
+  '@babel/helper-compilation-targets@7.27.2':
+    resolution: {integrity: sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==}
+    engines: {node: '>=6.9.0'}
+
+  '@babel/helper-create-class-features-plugin@7.27.1':
+    resolution: {integrity: sha512-QwGAmuvM17btKU5VqXfb+Giw4JcN0hjuufz3DYnpeVDvZLAObloM77bhMXiqry3Iio+Ai4phVRDwl6WU10+r5A==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0
+
+  '@babel/helper-globals@7.28.0':
+    resolution: {integrity: sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==}
+    engines: {node: '>=6.9.0'}
+
+  '@babel/helper-member-expression-to-functions@7.27.1':
+    resolution: {integrity: sha512-E5chM8eWjTp/aNoVpcbfM7mLxu9XGLWYise2eBKGQomAk/Mb4XoxyqXTZbuTohbsl8EKqdlMhnDI2CCLfcs9wA==}
+    engines: {node: '>=6.9.0'}
+
+  '@babel/helper-module-imports@7.27.1':
+    resolution: {integrity: sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==}
+    engines: {node: '>=6.9.0'}
+
+  '@babel/helper-module-transforms@7.27.3':
+    resolution: {integrity: sha512-dSOvYwvyLsWBeIRyOeHXp5vPj5l1I011r52FM1+r1jCERv+aFXYk4whgQccYEGYxK2H3ZAIA8nuPkQ0HaUo3qg==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0
+
+  '@babel/helper-optimise-call-expression@7.27.1':
+    resolution: {integrity: sha512-URMGH08NzYFhubNSGJrpUEphGKQwMQYBySzat5cAByY1/YgIRkULnIy3tAMeszlL/so2HbeilYloUmSpd7GdVw==}
+    engines: {node: '>=6.9.0'}
+
+  '@babel/helper-plugin-utils@7.27.1':
+    resolution: {integrity: sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==}
+    engines: {node: '>=6.9.0'}
+
+  '@babel/helper-replace-supers@7.27.1':
+    resolution: {integrity: sha512-7EHz6qDZc8RYS5ElPoShMheWvEgERonFCs7IAonWLLUTXW59DP14bCZt89/GKyreYn8g3S83m21FelHKbeDCKA==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0
+
+  '@babel/helper-skip-transparent-expression-wrappers@7.27.1':
+    resolution: {integrity: sha512-Tub4ZKEXqbPjXgWLl2+3JpQAYBJ8+ikpQ2Ocj/q/r0LwE3UhENh7EUabyHjz2kCEsrRY83ew2DQdHluuiDQFzg==}
+    engines: {node: '>=6.9.0'}
+
+  '@babel/helper-string-parser@7.27.1':
+    resolution: {integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==}
+    engines: {node: '>=6.9.0'}
+
+  '@babel/helper-validator-identifier@7.27.1':
+    resolution: {integrity: sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==}
+    engines: {node: '>=6.9.0'}
+
+  '@babel/helper-validator-option@7.27.1':
+    resolution: {integrity: sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==}
+    engines: {node: '>=6.9.0'}
+
+  '@babel/helpers@7.27.6':
+    resolution: {integrity: sha512-muE8Tt8M22638HU31A3CgfSUciwz1fhATfoVai05aPXGor//CdWDCbnlY1yvBPo07njuVOCNGCSp/GTt12lIug==}
+    engines: {node: '>=6.9.0'}
+
+  '@babel/parser@7.28.0':
+    resolution: {integrity: sha512-jVZGvOxOuNSsuQuLRTh13nU0AogFlw32w/MT+LV6D3sP5WdbW61E77RnkbaO2dUvmPAYrBDJXGn5gGS6tH4j8g==}
+    engines: {node: '>=6.0.0'}
+    hasBin: true
+
+  '@babel/plugin-proposal-decorators@7.28.0':
+    resolution: {integrity: sha512-zOiZqvANjWDUaUS9xMxbMcK/Zccztbe/6ikvUXaG9nsPH3w6qh5UaPGAnirI/WhIbZ8m3OHU0ReyPrknG+ZKeg==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+
+  '@babel/plugin-syntax-decorators@7.27.1':
+    resolution: {integrity: sha512-YMq8Z87Lhl8EGkmb0MwYkt36QnxC+fzCgrl66ereamPlYToRpIk5nUjKUY3QKLWq8mwUB1BgbeXcTJhZOCDg5A==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+
+  '@babel/plugin-syntax-import-attributes@7.27.1':
+    resolution: {integrity: sha512-oFT0FrKHgF53f4vOsZGi2Hh3I35PfSmVs4IBFLFj4dnafP+hIWDLg3VyKmUHfLoLHlyxY4C7DGtmHuJgn+IGww==}
+    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-jsx@7.27.1':
+    resolution: {integrity: sha512-y8YTNIeKoyhGd9O0Jiyzyyqk8gdjnumGTQPsz0xOZOQ2RmkVJeZ1vmmfIvFEKqucBG6axJGBZDE/7iI5suUI/w==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+
+  '@babel/plugin-syntax-typescript@7.27.1':
+    resolution: {integrity: sha512-xfYCBMxveHrRMnAWl1ZlPXOZjzkN82THFvLhQhFXFt81Z5HnN+EtUkZhv/zcKpmT3fzmWZB0ywiBrbC3vogbwQ==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+
+  '@babel/plugin-transform-typescript@7.28.0':
+    resolution: {integrity: sha512-4AEiDEBPIZvLQaWlc9liCavE0xRM0dNca41WtBeM3jgFptfUOSG9z0uteLhq6+3rq+WB6jIvUwKDTpXEHPJ2Vg==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+
+  '@babel/runtime@7.27.6':
+    resolution: {integrity: sha512-vbavdySgbTTrmFE+EsiqUTzlOr5bzlnJtUv9PynGCAKvfQqjIXbvFdumPM/GxMDfyuGMJaJAU6TO4zc1Jf1i8Q==}
+    engines: {node: '>=6.9.0'}
+
+  '@babel/template@7.27.2':
+    resolution: {integrity: sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==}
+    engines: {node: '>=6.9.0'}
+
+  '@babel/traverse@7.28.0':
+    resolution: {integrity: sha512-mGe7UK5wWyh0bKRfupsUchrQGqvDbZDbKJw+kcRGSmdHVYrv+ltd0pnpDTVpiTqnaBru9iEvA8pz8W46v0Amwg==}
+    engines: {node: '>=6.9.0'}
+
+  '@babel/types@7.28.0':
+    resolution: {integrity: sha512-jYnje+JyZG5YThjHiF28oT4SIZLnYOcSBb6+SDaFIyzDVSkXQmQQYclJ2R+YxcdmK0AX6x1E5OQNtuh3jHDrUg==}
+    engines: {node: '>=6.9.0'}
+
+  '@ctrl/tinycolor@3.6.1':
+    resolution: {integrity: sha512-SITSV6aIXsuVNV3f3O0f2n/cgyEDWoSqtZMYiAmcsYHydcKrOz3gUxB/iXd/Qf08+IZX4KpgNbvUdMBmWz+kcA==}
+    engines: {node: '>=10'}
+
+  '@emotion/hash@0.9.2':
+    resolution: {integrity: sha512-MyqliTZGuOm3+5ZRSaaBGP3USLw6+EGykkwZns2EPC5g8jJ4z9OrdZY9apkl3+UP9+sdz76YYkwCKP5gh8iY3g==}
+
+  '@emotion/unitless@0.8.1':
+    resolution: {integrity: sha512-KOEGMu6dmJZtpadb476IsZBclKvILjopjUii3V+7MnXIQCYh8W3NgNcgwo21n9LXZX6EDIKvqfjYxXebDwxKmQ==}
+
+  '@esbuild/aix-ppc64@0.25.5':
+    resolution: {integrity: sha512-9o3TMmpmftaCMepOdA5k/yDw8SfInyzWWTjYTFCX3kPSDJMROQTb8jg+h9Cnwnmm1vOzvxN7gIfB5V2ewpjtGA==}
+    engines: {node: '>=18'}
+    cpu: [ppc64]
+    os: [aix]
+
+  '@esbuild/android-arm64@0.25.5':
+    resolution: {integrity: sha512-VGzGhj4lJO+TVGV1v8ntCZWJktV7SGCs3Pn1GRWI1SBFtRALoomm8k5E9Pmwg3HOAal2VDc2F9+PM/rEY6oIDg==}
+    engines: {node: '>=18'}
+    cpu: [arm64]
+    os: [android]
+
+  '@esbuild/android-arm@0.25.5':
+    resolution: {integrity: sha512-AdJKSPeEHgi7/ZhuIPtcQKr5RQdo6OO2IL87JkianiMYMPbCtot9fxPbrMiBADOWWm3T2si9stAiVsGbTQFkbA==}
+    engines: {node: '>=18'}
+    cpu: [arm]
+    os: [android]
+
+  '@esbuild/android-x64@0.25.5':
+    resolution: {integrity: sha512-D2GyJT1kjvO//drbRT3Hib9XPwQeWd9vZoBJn+bu/lVsOZ13cqNdDeqIF/xQ5/VmWvMduP6AmXvylO/PIc2isw==}
+    engines: {node: '>=18'}
+    cpu: [x64]
+    os: [android]
+
+  '@esbuild/darwin-arm64@0.25.5':
+    resolution: {integrity: sha512-GtaBgammVvdF7aPIgH2jxMDdivezgFu6iKpmT+48+F8Hhg5J/sfnDieg0aeG/jfSvkYQU2/pceFPDKlqZzwnfQ==}
+    engines: {node: '>=18'}
+    cpu: [arm64]
+    os: [darwin]
+
+  '@esbuild/darwin-x64@0.25.5':
+    resolution: {integrity: sha512-1iT4FVL0dJ76/q1wd7XDsXrSW+oLoquptvh4CLR4kITDtqi2e/xwXwdCVH8hVHU43wgJdsq7Gxuzcs6Iq/7bxQ==}
+    engines: {node: '>=18'}
+    cpu: [x64]
+    os: [darwin]
+
+  '@esbuild/freebsd-arm64@0.25.5':
+    resolution: {integrity: sha512-nk4tGP3JThz4La38Uy/gzyXtpkPW8zSAmoUhK9xKKXdBCzKODMc2adkB2+8om9BDYugz+uGV7sLmpTYzvmz6Sw==}
+    engines: {node: '>=18'}
+    cpu: [arm64]
+    os: [freebsd]
+
+  '@esbuild/freebsd-x64@0.25.5':
+    resolution: {integrity: sha512-PrikaNjiXdR2laW6OIjlbeuCPrPaAl0IwPIaRv+SMV8CiM8i2LqVUHFC1+8eORgWyY7yhQY+2U2fA55mBzReaw==}
+    engines: {node: '>=18'}
+    cpu: [x64]
+    os: [freebsd]
+
+  '@esbuild/linux-arm64@0.25.5':
+    resolution: {integrity: sha512-Z9kfb1v6ZlGbWj8EJk9T6czVEjjq2ntSYLY2cw6pAZl4oKtfgQuS4HOq41M/BcoLPzrUbNd+R4BXFyH//nHxVg==}
+    engines: {node: '>=18'}
+    cpu: [arm64]
+    os: [linux]
+
+  '@esbuild/linux-arm@0.25.5':
+    resolution: {integrity: sha512-cPzojwW2okgh7ZlRpcBEtsX7WBuqbLrNXqLU89GxWbNt6uIg78ET82qifUy3W6OVww6ZWobWub5oqZOVtwolfw==}
+    engines: {node: '>=18'}
+    cpu: [arm]
+    os: [linux]
+
+  '@esbuild/linux-ia32@0.25.5':
+    resolution: {integrity: sha512-sQ7l00M8bSv36GLV95BVAdhJ2QsIbCuCjh/uYrWiMQSUuV+LpXwIqhgJDcvMTj+VsQmqAHL2yYaasENvJ7CDKA==}
+    engines: {node: '>=18'}
+    cpu: [ia32]
+    os: [linux]
+
+  '@esbuild/linux-loong64@0.25.5':
+    resolution: {integrity: sha512-0ur7ae16hDUC4OL5iEnDb0tZHDxYmuQyhKhsPBV8f99f6Z9KQM02g33f93rNH5A30agMS46u2HP6qTdEt6Q1kg==}
+    engines: {node: '>=18'}
+    cpu: [loong64]
+    os: [linux]
+
+  '@esbuild/linux-mips64el@0.25.5':
+    resolution: {integrity: sha512-kB/66P1OsHO5zLz0i6X0RxlQ+3cu0mkxS3TKFvkb5lin6uwZ/ttOkP3Z8lfR9mJOBk14ZwZ9182SIIWFGNmqmg==}
+    engines: {node: '>=18'}
+    cpu: [mips64el]
+    os: [linux]
+
+  '@esbuild/linux-ppc64@0.25.5':
+    resolution: {integrity: sha512-UZCmJ7r9X2fe2D6jBmkLBMQetXPXIsZjQJCjgwpVDz+YMcS6oFR27alkgGv3Oqkv07bxdvw7fyB71/olceJhkQ==}
+    engines: {node: '>=18'}
+    cpu: [ppc64]
+    os: [linux]
+
+  '@esbuild/linux-riscv64@0.25.5':
+    resolution: {integrity: sha512-kTxwu4mLyeOlsVIFPfQo+fQJAV9mh24xL+y+Bm6ej067sYANjyEw1dNHmvoqxJUCMnkBdKpvOn0Ahql6+4VyeA==}
+    engines: {node: '>=18'}
+    cpu: [riscv64]
+    os: [linux]
+
+  '@esbuild/linux-s390x@0.25.5':
+    resolution: {integrity: sha512-K2dSKTKfmdh78uJ3NcWFiqyRrimfdinS5ErLSn3vluHNeHVnBAFWC8a4X5N+7FgVE1EjXS1QDZbpqZBjfrqMTQ==}
+    engines: {node: '>=18'}
+    cpu: [s390x]
+    os: [linux]
+
+  '@esbuild/linux-x64@0.25.5':
+    resolution: {integrity: sha512-uhj8N2obKTE6pSZ+aMUbqq+1nXxNjZIIjCjGLfsWvVpy7gKCOL6rsY1MhRh9zLtUtAI7vpgLMK6DxjO8Qm9lJw==}
+    engines: {node: '>=18'}
+    cpu: [x64]
+    os: [linux]
+
+  '@esbuild/netbsd-arm64@0.25.5':
+    resolution: {integrity: sha512-pwHtMP9viAy1oHPvgxtOv+OkduK5ugofNTVDilIzBLpoWAM16r7b/mxBvfpuQDpRQFMfuVr5aLcn4yveGvBZvw==}
+    engines: {node: '>=18'}
+    cpu: [arm64]
+    os: [netbsd]
+
+  '@esbuild/netbsd-x64@0.25.5':
+    resolution: {integrity: sha512-WOb5fKrvVTRMfWFNCroYWWklbnXH0Q5rZppjq0vQIdlsQKuw6mdSihwSo4RV/YdQ5UCKKvBy7/0ZZYLBZKIbwQ==}
+    engines: {node: '>=18'}
+    cpu: [x64]
+    os: [netbsd]
+
+  '@esbuild/openbsd-arm64@0.25.5':
+    resolution: {integrity: sha512-7A208+uQKgTxHd0G0uqZO8UjK2R0DDb4fDmERtARjSHWxqMTye4Erz4zZafx7Di9Cv+lNHYuncAkiGFySoD+Mw==}
+    engines: {node: '>=18'}
+    cpu: [arm64]
+    os: [openbsd]
+
+  '@esbuild/openbsd-x64@0.25.5':
+    resolution: {integrity: sha512-G4hE405ErTWraiZ8UiSoesH8DaCsMm0Cay4fsFWOOUcz8b8rC6uCvnagr+gnioEjWn0wC+o1/TAHt+It+MpIMg==}
+    engines: {node: '>=18'}
+    cpu: [x64]
+    os: [openbsd]
+
+  '@esbuild/sunos-x64@0.25.5':
+    resolution: {integrity: sha512-l+azKShMy7FxzY0Rj4RCt5VD/q8mG/e+mDivgspo+yL8zW7qEwctQ6YqKX34DTEleFAvCIUviCFX1SDZRSyMQA==}
+    engines: {node: '>=18'}
+    cpu: [x64]
+    os: [sunos]
+
+  '@esbuild/win32-arm64@0.25.5':
+    resolution: {integrity: sha512-O2S7SNZzdcFG7eFKgvwUEZ2VG9D/sn/eIiz8XRZ1Q/DO5a3s76Xv0mdBzVM5j5R639lXQmPmSo0iRpHqUUrsxw==}
+    engines: {node: '>=18'}
+    cpu: [arm64]
+    os: [win32]
+
+  '@esbuild/win32-ia32@0.25.5':
+    resolution: {integrity: sha512-onOJ02pqs9h1iMJ1PQphR+VZv8qBMQ77Klcsqv9CNW2w6yLqoURLcgERAIurY6QE63bbLuqgP9ATqajFLK5AMQ==}
+    engines: {node: '>=18'}
+    cpu: [ia32]
+    os: [win32]
+
+  '@esbuild/win32-x64@0.25.5':
+    resolution: {integrity: sha512-TXv6YnJ8ZMVdX+SXWVBo/0p8LTcrUYngpWjvm91TMjjBQii7Oz11Lw5lbDV5Y0TzuhSJHwiH4hEtC1I42mMS0g==}
+    engines: {node: '>=18'}
+    cpu: [x64]
+    os: [win32]
+
+  '@eslint-community/eslint-utils@4.7.0':
+    resolution: {integrity: sha512-dyybb3AcajC7uha6CvhdVRJqaKyn7w2YKqKyAN37NKYgZT36w+iRb0Dymmc5qEJ549c/S31cMMSFd75bteCpCw==}
+    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.12.1':
+    resolution: {integrity: sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==}
+    engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0}
+
+  '@eslint/config-array@0.21.0':
+    resolution: {integrity: sha512-ENIdc4iLu0d93HeYirvKmrzshzofPw6VkZRKQGe9Nv46ZnWUzcF1xV01dcvEg/1wXUR61OmmlSfyeyO7EvjLxQ==}
+    engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+  '@eslint/config-helpers@0.3.0':
+    resolution: {integrity: sha512-ViuymvFmcJi04qdZeDc2whTHryouGcDlaxPqarTD0ZE10ISpxGUVZGZDx4w01upyIynL3iu6IXH2bS1NhclQMw==}
+    engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+  '@eslint/core@0.14.0':
+    resolution: {integrity: sha512-qIbV0/JZr7iSDjqAc60IqbLdsj9GDt16xQtWD+B78d/HAlvysGdZZ6rpJHGAc2T0FQx1X6thsSPdnoiGKdNtdg==}
+    engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+  '@eslint/core@0.15.1':
+    resolution: {integrity: sha512-bkOp+iumZCCbt1K1CmWf0R9pM5yKpDv+ZXtvSyQpudrI9kuFLp+bM2WOPXImuD/ceQuaa8f5pj93Y7zyECIGNA==}
+    engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+  '@eslint/eslintrc@3.3.1':
+    resolution: {integrity: sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==}
+    engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+  '@eslint/js@9.30.1':
+    resolution: {integrity: sha512-zXhuECFlyep42KZUhWjfvsmXGX39W8K8LFb8AWXM9gSV9dQB+MrJGLKvW6Zw0Ggnbpw0VHTtrhFXYe3Gym18jg==}
+    engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+  '@eslint/object-schema@2.1.6':
+    resolution: {integrity: sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==}
+    engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+  '@eslint/plugin-kit@0.3.3':
+    resolution: {integrity: sha512-1+WqvgNMhmlAambTvT3KPtCl/Ibr68VldY2XY40SL1CE0ZXiakFR/cbTspaF5HsnpDMvcYYoJHfl4980NBjGag==}
+    engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+  '@humanfs/core@0.19.1':
+    resolution: {integrity: sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==}
+    engines: {node: '>=18.18.0'}
+
+  '@humanfs/node@0.16.6':
+    resolution: {integrity: sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw==}
+    engines: {node: '>=18.18.0'}
+
+  '@humanwhocodes/module-importer@1.0.1':
+    resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==}
+    engines: {node: '>=12.22'}
+
+  '@humanwhocodes/retry@0.3.1':
+    resolution: {integrity: sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==}
+    engines: {node: '>=18.18'}
+
+  '@humanwhocodes/retry@0.4.3':
+    resolution: {integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==}
+    engines: {node: '>=18.18'}
+
+  '@jridgewell/gen-mapping@0.3.12':
+    resolution: {integrity: sha512-OuLGC46TjB5BbN1dH8JULVVZY4WTdkF7tV9Ys6wLL1rubZnCMstOhNHueU5bLCrnRuDhKPDM4g6sw4Bel5Gzqg==}
+
+  '@jridgewell/resolve-uri@3.1.2':
+    resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==}
+    engines: {node: '>=6.0.0'}
+
+  '@jridgewell/sourcemap-codec@1.5.4':
+    resolution: {integrity: sha512-VT2+G1VQs/9oz078bLrYbecdZKs912zQlkelYpuf+SXF+QvZDYJlbx/LSx+meSAwdDFnF8FVXW92AVjjkVmgFw==}
+
+  '@jridgewell/trace-mapping@0.3.29':
+    resolution: {integrity: sha512-uw6guiW/gcAGPDhLmd77/6lW8QLeiV5RUTsAX46Db6oLhGaVj4lhnPwb184s1bkc8kdVg/+h988dro8GRDpmYQ==}
+
+  '@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.2.7':
+    resolution: {integrity: sha512-YLT9Zo3oNPJoBjBc4q8G2mjU4tqIbf5CEOORbUUr48dCD9q3umJ3IPlVqOqDakPfd2HuwccBaqlGhN4Gmr5OWg==}
+    engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0}
+
+  '@polka/url@1.0.0-next.29':
+    resolution: {integrity: sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==}
+
+  '@rolldown/pluginutils@1.0.0-beta.19':
+    resolution: {integrity: sha512-3FL3mnMbPu0muGOCaKAhhFEYmqv9eTfPSJRJmANrCwtgK8VuxpsZDGK+m0LYAGoyO8+0j5uRe4PeyPDK1yA/hA==}
+
+  '@rollup/pluginutils@5.2.0':
+    resolution: {integrity: sha512-qWJ2ZTbmumwiLFomfzTyt5Kng4hwPi9rwCYN4SHb6eaRU1KNO4ccxINHr/VhH4GgPlt1XfSTLX2LBTme8ne4Zw==}
+    engines: {node: '>=14.0.0'}
+    peerDependencies:
+      rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0
+    peerDependenciesMeta:
+      rollup:
+        optional: true
+
+  '@rollup/rollup-android-arm-eabi@4.44.2':
+    resolution: {integrity: sha512-g0dF8P1e2QYPOj1gu7s/3LVP6kze9A7m6x0BZ9iTdXK8N5c2V7cpBKHV3/9A4Zd8xxavdhK0t4PnqjkqVmUc9Q==}
+    cpu: [arm]
+    os: [android]
+
+  '@rollup/rollup-android-arm64@4.44.2':
+    resolution: {integrity: sha512-Yt5MKrOosSbSaAK5Y4J+vSiID57sOvpBNBR6K7xAaQvk3MkcNVV0f9fE20T+41WYN8hDn6SGFlFrKudtx4EoxA==}
+    cpu: [arm64]
+    os: [android]
+
+  '@rollup/rollup-darwin-arm64@4.44.2':
+    resolution: {integrity: sha512-EsnFot9ZieM35YNA26nhbLTJBHD0jTwWpPwmRVDzjylQT6gkar+zenfb8mHxWpRrbn+WytRRjE0WKsfaxBkVUA==}
+    cpu: [arm64]
+    os: [darwin]
+
+  '@rollup/rollup-darwin-x64@4.44.2':
+    resolution: {integrity: sha512-dv/t1t1RkCvJdWWxQ2lWOO+b7cMsVw5YFaS04oHpZRWehI1h0fV1gF4wgGCTyQHHjJDfbNpwOi6PXEafRBBezw==}
+    cpu: [x64]
+    os: [darwin]
+
+  '@rollup/rollup-freebsd-arm64@4.44.2':
+    resolution: {integrity: sha512-W4tt4BLorKND4qeHElxDoim0+BsprFTwb+vriVQnFFtT/P6v/xO5I99xvYnVzKWrK6j7Hb0yp3x7V5LUbaeOMg==}
+    cpu: [arm64]
+    os: [freebsd]
+
+  '@rollup/rollup-freebsd-x64@4.44.2':
+    resolution: {integrity: sha512-tdT1PHopokkuBVyHjvYehnIe20fxibxFCEhQP/96MDSOcyjM/shlTkZZLOufV3qO6/FQOSiJTBebhVc12JyPTA==}
+    cpu: [x64]
+    os: [freebsd]
+
+  '@rollup/rollup-linux-arm-gnueabihf@4.44.2':
+    resolution: {integrity: sha512-+xmiDGGaSfIIOXMzkhJ++Oa0Gwvl9oXUeIiwarsdRXSe27HUIvjbSIpPxvnNsRebsNdUo7uAiQVgBD1hVriwSQ==}
+    cpu: [arm]
+    os: [linux]
+    libc: [glibc]
+
+  '@rollup/rollup-linux-arm-musleabihf@4.44.2':
+    resolution: {integrity: sha512-bDHvhzOfORk3wt8yxIra8N4k/N0MnKInCW5OGZaeDYa/hMrdPaJzo7CSkjKZqX4JFUWjUGm88lI6QJLCM7lDrA==}
+    cpu: [arm]
+    os: [linux]
+    libc: [musl]
+
+  '@rollup/rollup-linux-arm64-gnu@4.44.2':
+    resolution: {integrity: sha512-NMsDEsDiYghTbeZWEGnNi4F0hSbGnsuOG+VnNvxkKg0IGDvFh7UVpM/14mnMwxRxUf9AdAVJgHPvKXf6FpMB7A==}
+    cpu: [arm64]
+    os: [linux]
+    libc: [glibc]
+
+  '@rollup/rollup-linux-arm64-musl@4.44.2':
+    resolution: {integrity: sha512-lb5bxXnxXglVq+7imxykIp5xMq+idehfl+wOgiiix0191av84OqbjUED+PRC5OA8eFJYj5xAGcpAZ0pF2MnW+A==}
+    cpu: [arm64]
+    os: [linux]
+    libc: [musl]
+
+  '@rollup/rollup-linux-loongarch64-gnu@4.44.2':
+    resolution: {integrity: sha512-Yl5Rdpf9pIc4GW1PmkUGHdMtbx0fBLE1//SxDmuf3X0dUC57+zMepow2LK0V21661cjXdTn8hO2tXDdAWAqE5g==}
+    cpu: [loong64]
+    os: [linux]
+    libc: [glibc]
+
+  '@rollup/rollup-linux-powerpc64le-gnu@4.44.2':
+    resolution: {integrity: sha512-03vUDH+w55s680YYryyr78jsO1RWU9ocRMaeV2vMniJJW/6HhoTBwyyiiTPVHNWLnhsnwcQ0oH3S9JSBEKuyqw==}
+    cpu: [ppc64]
+    os: [linux]
+    libc: [glibc]
+
+  '@rollup/rollup-linux-riscv64-gnu@4.44.2':
+    resolution: {integrity: sha512-iYtAqBg5eEMG4dEfVlkqo05xMOk6y/JXIToRca2bAWuqjrJYJlx/I7+Z+4hSrsWU8GdJDFPL4ktV3dy4yBSrzg==}
+    cpu: [riscv64]
+    os: [linux]
+    libc: [glibc]
+
+  '@rollup/rollup-linux-riscv64-musl@4.44.2':
+    resolution: {integrity: sha512-e6vEbgaaqz2yEHqtkPXa28fFuBGmUJ0N2dOJK8YUfijejInt9gfCSA7YDdJ4nYlv67JfP3+PSWFX4IVw/xRIPg==}
+    cpu: [riscv64]
+    os: [linux]
+    libc: [musl]
+
+  '@rollup/rollup-linux-s390x-gnu@4.44.2':
+    resolution: {integrity: sha512-evFOtkmVdY3udE+0QKrV5wBx7bKI0iHz5yEVx5WqDJkxp9YQefy4Mpx3RajIVcM6o7jxTvVd/qpC1IXUhGc1Mw==}
+    cpu: [s390x]
+    os: [linux]
+    libc: [glibc]
+
+  '@rollup/rollup-linux-x64-gnu@4.44.2':
+    resolution: {integrity: sha512-/bXb0bEsWMyEkIsUL2Yt5nFB5naLAwyOWMEviQfQY1x3l5WsLKgvZf66TM7UTfED6erckUVUJQ/jJ1FSpm3pRQ==}
+    cpu: [x64]
+    os: [linux]
+    libc: [glibc]
+
+  '@rollup/rollup-linux-x64-musl@4.44.2':
+    resolution: {integrity: sha512-3D3OB1vSSBXmkGEZR27uiMRNiwN08/RVAcBKwhUYPaiZ8bcvdeEwWPvbnXvvXHY+A/7xluzcN+kaiOFNiOZwWg==}
+    cpu: [x64]
+    os: [linux]
+    libc: [musl]
+
+  '@rollup/rollup-win32-arm64-msvc@4.44.2':
+    resolution: {integrity: sha512-VfU0fsMK+rwdK8mwODqYeM2hDrF2WiHaSmCBrS7gColkQft95/8tphyzv2EupVxn3iE0FI78wzffoULH1G+dkw==}
+    cpu: [arm64]
+    os: [win32]
+
+  '@rollup/rollup-win32-ia32-msvc@4.44.2':
+    resolution: {integrity: sha512-+qMUrkbUurpE6DVRjiJCNGZBGo9xM4Y0FXU5cjgudWqIBWbcLkjE3XprJUsOFgC6xjBClwVa9k6O3A7K3vxb5Q==}
+    cpu: [ia32]
+    os: [win32]
+
+  '@rollup/rollup-win32-x64-msvc@4.44.2':
+    resolution: {integrity: sha512-3+QZROYfJ25PDcxFF66UEk8jGWigHJeecZILvkPkyQN7oc5BvFo4YEXFkOs154j3FTMp9mn9Ky8RCOwastduEA==}
+    cpu: [x64]
+    os: [win32]
+
+  '@rtsao/scc@1.1.0':
+    resolution: {integrity: sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==}
+
+  '@sec-ant/readable-stream@0.4.1':
+    resolution: {integrity: sha512-831qok9r2t8AlxLko40y2ebgSDhenenCatLVeW/uBtnHPyhHOvG0C7TvfgecV+wHzIm5KUICgzmVpWS+IMEAeg==}
+
+  '@simonwep/pickr@1.8.2':
+    resolution: {integrity: sha512-/l5w8BIkrpP6n1xsetx9MWPWlU6OblN5YgZZphxan0Tq4BByTCETL6lyIeY8lagalS2Nbt4F2W034KHLIiunKA==}
+
+  '@sindresorhus/merge-streams@4.0.0':
+    resolution: {integrity: sha512-tlqY9xq5ukxTUZBmoOp+m61cqwQD5pHJtFY3Mn8CA8ps6yghLH/Hw8UPdqg4OLmFW3IFlcXnQNmo/dh8HzXYIQ==}
+    engines: {node: '>=18'}
+
+  '@tsconfig/node22@22.0.2':
+    resolution: {integrity: sha512-Kmwj4u8sDRDrMYRoN9FDEcXD8UpBSaPQQ24Gz+Gamqfm7xxn+GBR7ge/Z7pK8OXNGyUzbSwJj+TH6B+DS/epyA==}
+
+  '@types/echarts@5.0.0':
+    resolution: {integrity: sha512-5uc/16BlYpzH8kU/u8aeRRgY2FV6yRY7RjPnYfUFPowl0F3kvNgfaz09PmeVdLkqdAtMft3XkCfqiJPJjG2DNQ==}
+    deprecated: This is a stub types definition. echarts provides its own type definitions, so you do not need this installed.
+
+  '@types/estree@1.0.8':
+    resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==}
+
+  '@types/json-schema@7.0.15':
+    resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==}
+
+  '@types/json5@0.0.29':
+    resolution: {integrity: sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==}
+
+  '@types/lodash-es@4.17.12':
+    resolution: {integrity: sha512-0NgftHUcV4v34VhXm8QBSftKVXtbkBG3ViCjs6+eJ5a6y6Mi/jiFGPc1sC7QK+9BFhWrURE3EOggmWaSxL9OzQ==}
+
+  '@types/lodash@4.17.20':
+    resolution: {integrity: sha512-H3MHACvFUEiujabxhaI/ImO6gUrd8oOurg7LQtS7mbwIXA/cUqWrvBsaeJ23aZEPk1TAYkurjfMbSELfoCXlGA==}
+
+  '@types/mqtt@2.5.0':
+    resolution: {integrity: sha512-n+0/ErBin30j+UbhcHGK/STjHjh65k85WNR6NlUjRG0g9yctpF12pS+SOkwz0wmp+7momAo9Cyi4Wmvy8UsCQg==}
+    deprecated: This is a stub types definition for MQTT (https://github.com/mqttjs/MQTT.js). MQTT provides its own type definitions, so you don't need @types/mqtt installed!
+
+  '@types/node@22.16.0':
+    resolution: {integrity: sha512-B2egV9wALML1JCpv3VQoQ+yesQKAmNMBIAY7OteVrikcOcAkWm+dGL6qpeCktPjAv6N1JLnhbNiqS35UpFyBsQ==}
+
+  '@types/readable-stream@4.0.21':
+    resolution: {integrity: sha512-19eKVv9tugr03IgfXlA9UVUVRbW6IuqRO5B92Dl4a6pT7K8uaGrNS0GkxiZD0BOk6PLuXl5FhWl//eX/pzYdTQ==}
+
+  '@types/web-bluetooth@0.0.21':
+    resolution: {integrity: sha512-oIQLCGWtcFZy2JW77j9k8nHzAOpqMHLQejDA48XXMWH6tjCQHz5RCFz1bzsmROyL6PUm+LLnUiI4BCn221inxA==}
+
+  '@typescript-eslint/eslint-plugin@8.35.1':
+    resolution: {integrity: sha512-9XNTlo7P7RJxbVeICaIIIEipqxLKguyh+3UbXuT2XQuFp6d8VOeDEGuz5IiX0dgZo8CiI6aOFLg4e8cF71SFVg==}
+    engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+    peerDependencies:
+      '@typescript-eslint/parser': ^8.35.1
+      eslint: ^8.57.0 || ^9.0.0
+      typescript: '>=4.8.4 <5.9.0'
+
+  '@typescript-eslint/parser@8.35.1':
+    resolution: {integrity: sha512-3MyiDfrfLeK06bi/g9DqJxP5pV74LNv4rFTyvGDmT3x2p1yp1lOd+qYZfiRPIOf/oON+WRZR5wxxuF85qOar+w==}
+    engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+    peerDependencies:
+      eslint: ^8.57.0 || ^9.0.0
+      typescript: '>=4.8.4 <5.9.0'
+
+  '@typescript-eslint/project-service@8.35.1':
+    resolution: {integrity: sha512-VYxn/5LOpVxADAuP3NrnxxHYfzVtQzLKeldIhDhzC8UHaiQvYlXvKuVho1qLduFbJjjy5U5bkGwa3rUGUb1Q6Q==}
+    engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+    peerDependencies:
+      typescript: '>=4.8.4 <5.9.0'
+
+  '@typescript-eslint/scope-manager@8.35.1':
+    resolution: {integrity: sha512-s/Bpd4i7ht2934nG+UoSPlYXd08KYz3bmjLEb7Ye1UVob0d1ENiT3lY8bsCmik4RqfSbPw9xJJHbugpPpP5JUg==}
+    engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+  '@typescript-eslint/tsconfig-utils@8.35.1':
+    resolution: {integrity: sha512-K5/U9VmT9dTHoNowWZpz+/TObS3xqC5h0xAIjXPw+MNcKV9qg6eSatEnmeAwkjHijhACH0/N7bkhKvbt1+DXWQ==}
+    engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+    peerDependencies:
+      typescript: '>=4.8.4 <5.9.0'
+
+  '@typescript-eslint/type-utils@8.35.1':
+    resolution: {integrity: sha512-HOrUBlfVRz5W2LIKpXzZoy6VTZzMu2n8q9C2V/cFngIC5U1nStJgv0tMV4sZPzdf4wQm9/ToWUFPMN9Vq9VJQQ==}
+    engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+    peerDependencies:
+      eslint: ^8.57.0 || ^9.0.0
+      typescript: '>=4.8.4 <5.9.0'
+
+  '@typescript-eslint/types@8.35.1':
+    resolution: {integrity: sha512-q/O04vVnKHfrrhNAscndAn1tuQhIkwqnaW+eu5waD5IPts2eX1dgJxgqcPx5BX109/qAz7IG6VrEPTOYKCNfRQ==}
+    engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+  '@typescript-eslint/typescript-estree@8.35.1':
+    resolution: {integrity: sha512-Vvpuvj4tBxIka7cPs6Y1uvM7gJgdF5Uu9F+mBJBPY4MhvjrjWGK4H0lVgLJd/8PWZ23FTqsaJaLEkBCFUk8Y9g==}
+    engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+    peerDependencies:
+      typescript: '>=4.8.4 <5.9.0'
+
+  '@typescript-eslint/utils@8.35.1':
+    resolution: {integrity: sha512-lhnwatFmOFcazAsUm3ZnZFpXSxiwoa1Lj50HphnDe1Et01NF4+hrdXONSUHIcbVu2eFb1bAf+5yjXkGVkXBKAQ==}
+    engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+    peerDependencies:
+      eslint: ^8.57.0 || ^9.0.0
+      typescript: '>=4.8.4 <5.9.0'
+
+  '@typescript-eslint/visitor-keys@8.35.1':
+    resolution: {integrity: sha512-VRwixir4zBWCSTP/ljEo091lbpypz57PoeAQ9imjG+vbeof9LplljsL1mos4ccG6H9IjfrVGM359RozUnuFhpw==}
+    engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+  '@vitejs/plugin-vue@6.0.0':
+    resolution: {integrity: sha512-iAliE72WsdhjzTOp2DtvKThq1VBC4REhwRcaA+zPAAph6I+OQhUXv+Xu2KS7ElxYtb7Zc/3R30Hwv1DxEo7NXQ==}
+    engines: {node: ^20.19.0 || >=22.12.0}
+    peerDependencies:
+      vite: ^5.0.0 || ^6.0.0 || ^7.0.0
+      vue: ^3.2.25
+
+  '@volar/language-core@2.4.15':
+    resolution: {integrity: sha512-3VHw+QZU0ZG9IuQmzT68IyN4hZNd9GchGPhbD9+pa8CVv7rnoOZwo7T8weIbrRmihqy3ATpdfXFnqRrfPVK6CA==}
+
+  '@volar/source-map@2.4.15':
+    resolution: {integrity: sha512-CPbMWlUN6hVZJYGcU/GSoHu4EnCHiLaXI9n8c9la6RaI9W5JHX+NqG+GSQcB0JdC2FIBLdZJwGsfKyBB71VlTg==}
+
+  '@volar/typescript@2.4.15':
+    resolution: {integrity: sha512-2aZ8i0cqPGjXb4BhkMsPYDkkuc2ZQ6yOpqwAuNwUoncELqoy5fRgOQtLR9gB0g902iS0NAkvpIzs27geVyVdPg==}
+
+  '@vue/babel-helper-vue-transform-on@1.4.0':
+    resolution: {integrity: sha512-mCokbouEQ/ocRce/FpKCRItGo+013tHg7tixg3DUNS+6bmIchPt66012kBMm476vyEIJPafrvOf4E5OYj3shSw==}
+
+  '@vue/babel-plugin-jsx@1.4.0':
+    resolution: {integrity: sha512-9zAHmwgMWlaN6qRKdrg1uKsBKHvnUU+Py+MOCTuYZBoZsopa90Di10QRjB+YPnVss0BZbG/H5XFwJY1fTxJWhA==}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+    peerDependenciesMeta:
+      '@babel/core':
+        optional: true
+
+  '@vue/babel-plugin-resolve-type@1.4.0':
+    resolution: {integrity: sha512-4xqDRRbQQEWHQyjlYSgZsWj44KfiF6D+ktCuXyZ8EnVDYV3pztmXJDf1HveAjUAXxAnR8daCQT51RneWWxtTyQ==}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+
+  '@vue/compiler-core@3.5.17':
+    resolution: {integrity: sha512-Xe+AittLbAyV0pabcN7cP7/BenRBNcteM4aSDCtRvGw0d9OL+HG1u/XHLY/kt1q4fyMeZYXyIYrsHuPSiDPosA==}
+
+  '@vue/compiler-dom@3.5.17':
+    resolution: {integrity: sha512-+2UgfLKoaNLhgfhV5Ihnk6wB4ljyW1/7wUIog2puUqajiC29Lp5R/IKDdkebh9jTbTogTbsgB+OY9cEWzG95JQ==}
+
+  '@vue/compiler-sfc@3.5.17':
+    resolution: {integrity: sha512-rQQxbRJMgTqwRugtjw0cnyQv9cP4/4BxWfTdRBkqsTfLOHWykLzbOc3C4GGzAmdMDxhzU/1Ija5bTjMVrddqww==}
+
+  '@vue/compiler-ssr@3.5.17':
+    resolution: {integrity: sha512-hkDbA0Q20ZzGgpj5uZjb9rBzQtIHLS78mMilwrlpWk2Ep37DYntUz0PonQ6kr113vfOEdM+zTBuJDaceNIW0tQ==}
+
+  '@vue/compiler-vue2@2.7.16':
+    resolution: {integrity: sha512-qYC3Psj9S/mfu9uVi5WvNZIzq+xnXMhOwbTFKKDD7b1lhpnn71jXSFdTQ+WsIEk0ONCd7VV2IMm7ONl6tbQ86A==}
+
+  '@vue/devtools-api@6.6.4':
+    resolution: {integrity: sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g==}
+
+  '@vue/devtools-api@7.7.7':
+    resolution: {integrity: sha512-lwOnNBH2e7x1fIIbVT7yF5D+YWhqELm55/4ZKf45R9T8r9dE2AIOy8HKjfqzGsoTHFbWbr337O4E0A0QADnjBg==}
+
+  '@vue/devtools-core@7.7.7':
+    resolution: {integrity: sha512-9z9TLbfC+AjAi1PQyWX+OErjIaJmdFlbDHcD+cAMYKY6Bh5VlsAtCeGyRMrXwIlMEQPukvnWt3gZBLwTAIMKzQ==}
+    peerDependencies:
+      vue: ^3.0.0
+
+  '@vue/devtools-kit@7.7.7':
+    resolution: {integrity: sha512-wgoZtxcTta65cnZ1Q6MbAfePVFxfM+gq0saaeytoph7nEa7yMXoi6sCPy4ufO111B9msnw0VOWjPEFCXuAKRHA==}
+
+  '@vue/devtools-shared@7.7.7':
+    resolution: {integrity: sha512-+udSj47aRl5aKb0memBvcUG9koarqnxNM5yjuREvqwK6T3ap4mn3Zqqc17QrBFTqSMjr3HK1cvStEZpMDpfdyw==}
+
+  '@vue/eslint-config-prettier@10.2.0':
+    resolution: {integrity: sha512-GL3YBLwv/+b86yHcNNfPJxOTtVFJ4Mbc9UU3zR+KVoG7SwGTjPT+32fXamscNumElhcpXW3mT0DgzS9w32S7Bw==}
+    peerDependencies:
+      eslint: '>= 8.21.0'
+      prettier: '>= 3.0.0'
+
+  '@vue/eslint-config-typescript@14.6.0':
+    resolution: {integrity: sha512-UpiRY/7go4Yps4mYCjkvlIbVWmn9YvPGQDxTAlcKLphyaD77LjIu3plH4Y9zNT0GB4f3K5tMmhhtRhPOgrQ/bQ==}
+    engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+    peerDependencies:
+      eslint: ^9.10.0
+      eslint-plugin-vue: ^9.28.0 || ^10.0.0
+      typescript: '>=4.8.4'
+    peerDependenciesMeta:
+      typescript:
+        optional: true
+
+  '@vue/language-core@2.2.12':
+    resolution: {integrity: sha512-IsGljWbKGU1MZpBPN+BvPAdr55YPkj2nB/TBNGNC32Vy2qLG25DYu/NBN2vNtZqdRbTRjaoYrahLrToim2NanA==}
+    peerDependencies:
+      typescript: '*'
+    peerDependenciesMeta:
+      typescript:
+        optional: true
+
+  '@vue/reactivity@3.5.17':
+    resolution: {integrity: sha512-l/rmw2STIscWi7SNJp708FK4Kofs97zc/5aEPQh4bOsReD/8ICuBcEmS7KGwDj5ODQLYWVN2lNibKJL1z5b+Lw==}
+
+  '@vue/runtime-core@3.5.17':
+    resolution: {integrity: sha512-QQLXa20dHg1R0ri4bjKeGFKEkJA7MMBxrKo2G+gJikmumRS7PTD4BOU9FKrDQWMKowz7frJJGqBffYMgQYS96Q==}
+
+  '@vue/runtime-dom@3.5.17':
+    resolution: {integrity: sha512-8El0M60TcwZ1QMz4/os2MdlQECgGoVHPuLnQBU3m9h3gdNRW9xRmI8iLS4t/22OQlOE6aJvNNlBiCzPHur4H9g==}
+
+  '@vue/server-renderer@3.5.17':
+    resolution: {integrity: sha512-BOHhm8HalujY6lmC3DbqF6uXN/K00uWiEeF22LfEsm9Q93XeJ/plHTepGwf6tqFcF7GA5oGSSAAUock3VvzaCA==}
+    peerDependencies:
+      vue: 3.5.17
+
+  '@vue/shared@3.5.17':
+    resolution: {integrity: sha512-CabR+UN630VnsJO/jHWYBC1YVXyMq94KKp6iF5MQgZJs5I8cmjw6oVMO1oDbtBkENSHSSn/UadWlW/OAgdmKrg==}
+
+  '@vue/tsconfig@0.7.0':
+    resolution: {integrity: sha512-ku2uNz5MaZ9IerPPUyOHzyjhXoX2kVJaVf7hL315DC17vS6IiZRmmCPfggNbU16QTvM80+uYYy3eYJB59WCtvg==}
+    peerDependencies:
+      typescript: 5.x
+      vue: ^3.4.0
+    peerDependenciesMeta:
+      typescript:
+        optional: true
+      vue:
+        optional: true
+
+  '@vueuse/components@13.5.0':
+    resolution: {integrity: sha512-bU/FJNQMCxzDFp67YuxFQSV9CptHxZIgtExjFgJU8AE/gRRJMzGfPjNm+HvaVnMRl8IFFv4E+6JQV0kDu4zIpw==}
+    peerDependencies:
+      vue: ^3.5.0
+
+  '@vueuse/core@13.5.0':
+    resolution: {integrity: sha512-wV7z0eUpifKmvmN78UBZX8T7lMW53Nrk6JP5+6hbzrB9+cJ3jr//hUlhl9TZO/03bUkMK6gGkQpqOPWoabr72g==}
+    peerDependencies:
+      vue: ^3.5.0
+
+  '@vueuse/metadata@13.5.0':
+    resolution: {integrity: sha512-euhItU3b0SqXxSy8u1XHxUCdQ8M++bsRs+TYhOLDU/OykS7KvJnyIFfep0XM5WjIFry9uAPlVSjmVHiqeshmkw==}
+
+  '@vueuse/shared@13.5.0':
+    resolution: {integrity: sha512-K7GrQIxJ/ANtucxIXbQlUHdB0TPA8c+q5i+zbrjxuhJCnJ9GtBg75sBSnvmLSxHKPg2Yo8w62PWksl9kwH0Q8g==}
+    peerDependencies:
+      vue: ^3.5.0
+
+  abort-controller@3.0.0:
+    resolution: {integrity: sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==}
+    engines: {node: '>=6.5'}
+
+  acorn-jsx@5.3.2:
+    resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==}
+    peerDependencies:
+      acorn: ^6.0.0 || ^7.0.0 || ^8.0.0
+
+  acorn@8.15.0:
+    resolution: {integrity: sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==}
+    engines: {node: '>=0.4.0'}
+    hasBin: true
+
+  ajv@6.12.6:
+    resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==}
+
+  alien-signals@1.0.13:
+    resolution: {integrity: sha512-OGj9yyTnJEttvzhTUWuscOvtqxq5vrhF7vL9oS0xJ2mK0ItPYP1/y+vCFebfxoEyAz0++1AIwJ5CMr+Fk3nDmg==}
+
+  ansi-styles@4.3.0:
+    resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==}
+    engines: {node: '>=8'}
+
+  ansi-styles@6.2.1:
+    resolution: {integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==}
+    engines: {node: '>=12'}
+
+  ant-design-vue@4.2.6:
+    resolution: {integrity: sha512-t7eX13Yj3i9+i5g9lqFyYneoIb3OzTvQjq9Tts1i+eiOd3Eva/6GagxBSXM1fOCjqemIu0FYVE1ByZ/38epR3Q==}
+    engines: {node: '>=12.22.0'}
+    peerDependencies:
+      vue: '>=3.2.0'
+
+  anymatch@3.1.3:
+    resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==}
+    engines: {node: '>= 8'}
+
+  argparse@2.0.1:
+    resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==}
+
+  array-buffer-byte-length@1.0.2:
+    resolution: {integrity: sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==}
+    engines: {node: '>= 0.4'}
+
+  array-includes@3.1.9:
+    resolution: {integrity: sha512-FmeCCAenzH0KH381SPT5FZmiA/TmpndpcaShhfgEN9eCVjnFBqq3l1xrI42y8+PPLI6hypzou4GXw00WHmPBLQ==}
+    engines: {node: '>= 0.4'}
+
+  array-tree-filter@2.1.0:
+    resolution: {integrity: sha512-4ROwICNlNw/Hqa9v+rk5h22KjmzB1JGTMVKP2AKJBOCgb0yL0ASf0+YvCcLNNwquOHNX48jkeZIJ3a+oOQqKcw==}
+
+  array.prototype.findlastindex@1.2.6:
+    resolution: {integrity: sha512-F/TKATkzseUExPlfvmwQKGITM3DGTK+vkAsCZoDc5daVygbJBnjEUCbgkAvVFsgfXfX4YIqZ/27G3k3tdXrTxQ==}
+    engines: {node: '>= 0.4'}
+
+  array.prototype.flat@1.3.3:
+    resolution: {integrity: sha512-rwG/ja1neyLqCuGZ5YYrznA62D4mZXg0i1cIskIUKSiqF3Cje9/wXAls9B9s1Wa2fomMsIv8czB8jZcPmxCXFg==}
+    engines: {node: '>= 0.4'}
+
+  array.prototype.flatmap@1.3.3:
+    resolution: {integrity: sha512-Y7Wt51eKJSyi80hFrJCePGGNo5ktJCslFuboqJsbf57CCPcm5zztluPlc4/aD8sWsKvlwatezpV4U1efk8kpjg==}
+    engines: {node: '>= 0.4'}
+
+  arraybuffer.prototype.slice@1.0.4:
+    resolution: {integrity: sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==}
+    engines: {node: '>= 0.4'}
+
+  async-function@1.0.0:
+    resolution: {integrity: sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==}
+    engines: {node: '>= 0.4'}
+
+  async-validator@4.2.5:
+    resolution: {integrity: sha512-7HhHjtERjqlNbZtqNqy2rckN/SpOOlmDliet+lP7k+eKZEjPk3DgyeU9lIXLdeLz0uBbbVp+9Qdow9wJWgwwfg==}
+
+  asynckit@0.4.0:
+    resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==}
+
+  available-typed-arrays@1.0.7:
+    resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==}
+    engines: {node: '>= 0.4'}
+
+  axios@1.10.0:
+    resolution: {integrity: sha512-/1xYAC4MP/HEG+3duIhFr4ZQXR4sQXOIe+o6sdqzeykGLx6Upp/1p8MHqhINOvGeP7xyNHe7tsiJByc4SSVUxw==}
+
+  balanced-match@1.0.2:
+    resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==}
+
+  base64-js@1.5.1:
+    resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==}
+
+  binary-extensions@2.3.0:
+    resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==}
+    engines: {node: '>=8'}
+
+  birpc@2.4.0:
+    resolution: {integrity: sha512-5IdNxTyhXHv2UlgnPHQ0h+5ypVmkrYHzL8QT+DwFZ//2N/oNV8Ch+BCRmTJ3x6/z9Axo/cXYBc9eprsUVK/Jsg==}
+
+  bl@6.1.0:
+    resolution: {integrity: sha512-ClDyJGQkc8ZtzdAAbAwBmhMSpwN/sC9HA8jxdYm6nVUbCfZbe2mgza4qh7AuEYyEPB/c4Kznf9s66bnsKMQDjw==}
+
+  boolbase@1.0.0:
+    resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==}
+
+  brace-expansion@1.1.12:
+    resolution: {integrity: sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==}
+
+  brace-expansion@2.0.2:
+    resolution: {integrity: sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==}
+
+  braces@3.0.3:
+    resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==}
+    engines: {node: '>=8'}
+
+  browserslist@4.25.1:
+    resolution: {integrity: sha512-KGj0KoOMXLpSNkkEI6Z6mShmQy0bc1I+T7K9N81k4WWMrfz+6fQ6es80B/YLAeRoKvjYE1YSHHOW1qe9xIVzHw==}
+    engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7}
+    hasBin: true
+
+  buffer-from@1.1.2:
+    resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==}
+
+  buffer@6.0.3:
+    resolution: {integrity: sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==}
+
+  bundle-name@4.1.0:
+    resolution: {integrity: sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q==}
+    engines: {node: '>=18'}
+
+  call-bind-apply-helpers@1.0.2:
+    resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==}
+    engines: {node: '>= 0.4'}
+
+  call-bind@1.0.8:
+    resolution: {integrity: sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==}
+    engines: {node: '>= 0.4'}
+
+  call-bound@1.0.4:
+    resolution: {integrity: sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==}
+    engines: {node: '>= 0.4'}
+
+  callsites@3.1.0:
+    resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==}
+    engines: {node: '>=6'}
+
+  caniuse-lite@1.0.30001727:
+    resolution: {integrity: sha512-pB68nIHmbN6L/4C6MH1DokyR3bYqFwjaSs/sWDHGj4CTcFtQUQMuJftVwWkXq7mNWOybD3KhUv3oWHoGxgP14Q==}
+
+  chalk@4.1.2:
+    resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==}
+    engines: {node: '>=10'}
+
+  chokidar@3.6.0:
+    resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==}
+    engines: {node: '>= 8.10.0'}
+
+  color-convert@2.0.1:
+    resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==}
+    engines: {node: '>=7.0.0'}
+
+  color-name@1.1.4:
+    resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==}
+
+  combined-stream@1.0.8:
+    resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==}
+    engines: {node: '>= 0.8'}
+
+  commist@3.2.0:
+    resolution: {integrity: sha512-4PIMoPniho+LqXmpS5d3NuGYncG6XWlkBSVGiWycL22dd42OYdUGil2CWuzklaJoNxyxUSpO4MKIBU94viWNAw==}
+
+  compute-scroll-into-view@1.0.20:
+    resolution: {integrity: sha512-UCB0ioiyj8CRjtrvaceBLqqhZCVP+1B8+NWQhmdsm0VXOJtobBCf1dBQmebCCo34qZmUwZfIH2MZLqNHazrfjg==}
+
+  concat-map@0.0.1:
+    resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==}
+
+  concat-stream@2.0.0:
+    resolution: {integrity: sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A==}
+    engines: {'0': node >= 6.0}
+
+  confbox@0.1.8:
+    resolution: {integrity: sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==}
+
+  confbox@0.2.2:
+    resolution: {integrity: sha512-1NB+BKqhtNipMsov4xI/NnhCKp9XG9NamYp5PVm9klAT0fsrNPjaFICsCFhNhwZJKNh7zB/3q8qXz0E9oaMNtQ==}
+
+  convert-source-map@2.0.0:
+    resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==}
+
+  copy-anything@2.0.6:
+    resolution: {integrity: sha512-1j20GZTsvKNkc4BY3NpMOM8tt///wY3FpIzozTOFO2ffuZcV61nojHXVKIy3WM+7ADCy5FVhdZYHYDdgTU0yJw==}
+
+  copy-anything@3.0.5:
+    resolution: {integrity: sha512-yCEafptTtb4bk7GLEQoM8KVJpxAfdBJYaXyzQEgQQQgYrZiDp8SJmGKlYza6CYjEDNstAdNdKA3UuoULlEbS6w==}
+    engines: {node: '>=12.13'}
+
+  core-js@3.43.0:
+    resolution: {integrity: sha512-N6wEbTTZSYOY2rYAn85CuvWWkCK6QweMn7/4Nr3w+gDBeBhk/x4EJeY6FPo4QzDoJZxVTv8U7CMvgWk6pOHHqA==}
+
+  cross-spawn@7.0.6:
+    resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==}
+    engines: {node: '>= 8'}
+
+  cssesc@3.0.0:
+    resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==}
+    engines: {node: '>=4'}
+    hasBin: true
+
+  csstype@3.1.3:
+    resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==}
+
+  data-view-buffer@1.0.2:
+    resolution: {integrity: sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==}
+    engines: {node: '>= 0.4'}
+
+  data-view-byte-length@1.0.2:
+    resolution: {integrity: sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ==}
+    engines: {node: '>= 0.4'}
+
+  data-view-byte-offset@1.0.1:
+    resolution: {integrity: sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==}
+    engines: {node: '>= 0.4'}
+
+  dayjs@1.11.13:
+    resolution: {integrity: sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg==}
+
+  de-indent@1.0.2:
+    resolution: {integrity: sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg==}
+
+  debug@3.2.7:
+    resolution: {integrity: sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==}
+    peerDependencies:
+      supports-color: '*'
+    peerDependenciesMeta:
+      supports-color:
+        optional: true
+
+  debug@4.4.1:
+    resolution: {integrity: sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==}
+    engines: {node: '>=6.0'}
+    peerDependencies:
+      supports-color: '*'
+    peerDependenciesMeta:
+      supports-color:
+        optional: true
+
+  deep-is@0.1.4:
+    resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==}
+
+  deep-pick-omit@1.2.1:
+    resolution: {integrity: sha512-2J6Kc/m3irCeqVG42T+SaUMesaK7oGWaedGnQQK/+O0gYc+2SP5bKh/KKTE7d7SJ+GCA9UUE1GRzh6oDe0EnGw==}
+
+  default-browser-id@5.0.0:
+    resolution: {integrity: sha512-A6p/pu/6fyBcA1TRz/GqWYPViplrftcW2gZC9q79ngNCKAeR/X3gcEdXQHl4KNXV+3wgIJ1CPkJQ3IHM6lcsyA==}
+    engines: {node: '>=18'}
+
+  default-browser@5.2.1:
+    resolution: {integrity: sha512-WY/3TUME0x3KPYdRRxEJJvXRHV4PyPoUsxtZa78lwItwRQRHhd2U9xOscaT/YTf8uCXIAjeJOFBVEh/7FtD8Xg==}
+    engines: {node: '>=18'}
+
+  define-data-property@1.1.4:
+    resolution: {integrity: sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==}
+    engines: {node: '>= 0.4'}
+
+  define-lazy-prop@3.0.0:
+    resolution: {integrity: sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==}
+    engines: {node: '>=12'}
+
+  define-properties@1.2.1:
+    resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==}
+    engines: {node: '>= 0.4'}
+
+  defu@6.1.4:
+    resolution: {integrity: sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg==}
+
+  delayed-stream@1.0.0:
+    resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==}
+    engines: {node: '>=0.4.0'}
+
+  destr@2.0.5:
+    resolution: {integrity: sha512-ugFTXCtDZunbzasqBxrK93Ik/DRYsO6S/fedkWEMKqt04xZ4csmnmwGDBAb07QWNaGMAmnTIemsYZCksjATwsA==}
+
+  doctrine@2.1.0:
+    resolution: {integrity: sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==}
+    engines: {node: '>=0.10.0'}
+
+  dom-align@1.12.4:
+    resolution: {integrity: sha512-R8LUSEay/68zE5c8/3BDxiTEvgb4xZTF0RKmAHfiEVN3klfIpXfi2/QCoiWPccVQ0J/ZGdz9OjzL4uJEP/MRAw==}
+
+  dom-scroll-into-view@2.0.1:
+    resolution: {integrity: sha512-bvVTQe1lfaUr1oFzZX80ce9KLDlZ3iU+XGNE/bz9HnGdklTieqsbmsLHe+rT2XWqopvL0PckkYqN7ksmm5pe3w==}
+
+  dunder-proto@1.0.1:
+    resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==}
+    engines: {node: '>= 0.4'}
+
+  echarts@5.6.0:
+    resolution: {integrity: sha512-oTbVTsXfKuEhxftHqL5xprgLoc0k7uScAwtryCgWF6hPYFLRwOUHiFmHGCBKP5NPFNkDVopOieyUqYGH8Fa3kA==}
+
+  electron-to-chromium@1.5.179:
+    resolution: {integrity: sha512-UWKi/EbBopgfFsc5k61wFpV7WrnnSlSzW/e2XcBmS6qKYTivZlLtoll5/rdqRTxGglGHkmkW0j0pFNJG10EUIQ==}
+
+  entities@4.5.0:
+    resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==}
+    engines: {node: '>=0.12'}
+
+  errno@0.1.8:
+    resolution: {integrity: sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A==}
+    hasBin: true
+
+  error-stack-parser-es@0.1.5:
+    resolution: {integrity: sha512-xHku1X40RO+fO8yJ8Wh2f2rZWVjqyhb1zgq1yZ8aZRQkv6OOKhKWRUaht3eSCUbAOBaKIgM+ykwFLE+QUxgGeg==}
+
+  es-abstract@1.24.0:
+    resolution: {integrity: sha512-WSzPgsdLtTcQwm4CROfS5ju2Wa1QQcVeT37jFjYzdFz1r9ahadC8B8/a4qxJxM+09F18iumCdRmlr96ZYkQvEg==}
+    engines: {node: '>= 0.4'}
+
+  es-define-property@1.0.1:
+    resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==}
+    engines: {node: '>= 0.4'}
+
+  es-errors@1.3.0:
+    resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==}
+    engines: {node: '>= 0.4'}
+
+  es-object-atoms@1.1.1:
+    resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==}
+    engines: {node: '>= 0.4'}
+
+  es-set-tostringtag@2.1.0:
+    resolution: {integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==}
+    engines: {node: '>= 0.4'}
+
+  es-shim-unscopables@1.1.0:
+    resolution: {integrity: sha512-d9T8ucsEhh8Bi1woXCf+TIKDIROLG5WCkxg8geBCbvk22kzwC5G2OnXVMO6FUsvQlgUUXQ2itephWDLqDzbeCw==}
+    engines: {node: '>= 0.4'}
+
+  es-to-primitive@1.3.0:
+    resolution: {integrity: sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==}
+    engines: {node: '>= 0.4'}
+
+  esbuild@0.25.5:
+    resolution: {integrity: sha512-P8OtKZRv/5J5hhz0cUAdu/cLuPIKXpQl1R9pZtvmHWQvrAUVd0UNIPT4IB4W3rNOqVO0rlqHmCIbSwxh/c9yUQ==}
+    engines: {node: '>=18'}
+    hasBin: true
+
+  escalade@3.2.0:
+    resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==}
+    engines: {node: '>=6'}
+
+  escape-string-regexp@4.0.0:
+    resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==}
+    engines: {node: '>=10'}
+
+  eslint-config-prettier@10.1.5:
+    resolution: {integrity: sha512-zc1UmCpNltmVY34vuLRV61r1K27sWuX39E+uyUnY8xS2Bex88VV9cugG+UZbRSRGtGyFboj+D8JODyme1plMpw==}
+    hasBin: true
+    peerDependencies:
+      eslint: '>=7.0.0'
+
+  eslint-import-resolver-alias@1.1.2:
+    resolution: {integrity: sha512-WdviM1Eu834zsfjHtcGHtGfcu+F30Od3V7I9Fi57uhBEwPkjDcii7/yW8jAT+gOhn4P/vOxxNAXbFAKsrrc15w==}
+    engines: {node: '>= 4'}
+    peerDependencies:
+      eslint-plugin-import: '>=1.4.0'
+
+  eslint-import-resolver-node@0.3.9:
+    resolution: {integrity: sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==}
+
+  eslint-module-utils@2.12.1:
+    resolution: {integrity: sha512-L8jSWTze7K2mTg0vos/RuLRS5soomksDPoJLXIslC7c8Wmut3bx7CPpJijDcBZtxQ5lrbUdM+s0OlNbz0DCDNw==}
+    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.32.0:
+    resolution: {integrity: sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA==}
+    engines: {node: '>=4'}
+    peerDependencies:
+      '@typescript-eslint/parser': '*'
+      eslint: ^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 || ^9
+    peerDependenciesMeta:
+      '@typescript-eslint/parser':
+        optional: true
+
+  eslint-plugin-prettier@5.5.1:
+    resolution: {integrity: sha512-dobTkHT6XaEVOo8IO90Q4DOSxnm3Y151QxPJlM/vKC0bVy+d6cVWQZLlFiuZPP0wS6vZwSKeJgKkcS+KfMBlRw==}
+    engines: {node: ^14.18.0 || >=16.0.0}
+    peerDependencies:
+      '@types/eslint': '>=8.0.0'
+      eslint: '>=8.0.0'
+      eslint-config-prettier: '>= 7.0.0 <10.0.0 || >=10.1.0'
+      prettier: '>=3.0.0'
+    peerDependenciesMeta:
+      '@types/eslint':
+        optional: true
+      eslint-config-prettier:
+        optional: true
+
+  eslint-plugin-vue@10.2.0:
+    resolution: {integrity: sha512-tl9s+KN3z0hN2b8fV2xSs5ytGl7Esk1oSCxULLwFcdaElhZ8btYYZFrWxvh4En+czrSDtuLCeCOGa8HhEZuBdQ==}
+    engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+    peerDependencies:
+      eslint: ^8.57.0 || ^9.0.0
+      vue-eslint-parser: ^10.0.0
+
+  eslint-scope@8.4.0:
+    resolution: {integrity: sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==}
+    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.2.1:
+    resolution: {integrity: sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==}
+    engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+  eslint@9.30.1:
+    resolution: {integrity: sha512-zmxXPNMOXmwm9E0yQLi5uqXHs7uq2UIiqEKo3Gq+3fwo1XrJ+hijAZImyF7hclW3E6oHz43Yk3RP8at6OTKflQ==}
+    engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+    hasBin: true
+    peerDependencies:
+      jiti: '*'
+    peerDependenciesMeta:
+      jiti:
+        optional: true
+
+  espree@10.4.0:
+    resolution: {integrity: sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==}
+    engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+  esquery@1.6.0:
+    resolution: {integrity: sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==}
+    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==}
+
+  esutils@2.0.3:
+    resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==}
+    engines: {node: '>=0.10.0'}
+
+  event-target-shim@5.0.1:
+    resolution: {integrity: sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==}
+    engines: {node: '>=6'}
+
+  events@3.3.0:
+    resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==}
+    engines: {node: '>=0.8.x'}
+
+  execa@9.6.0:
+    resolution: {integrity: sha512-jpWzZ1ZhwUmeWRhS7Qv3mhpOhLfwI+uAX4e5fOcXqwMR7EcJ0pj2kV1CVzHVMX/LphnKWD3LObjZCoJ71lKpHw==}
+    engines: {node: ^18.19.0 || >=20.5.0}
+
+  exsolve@1.0.7:
+    resolution: {integrity: sha512-VO5fQUzZtI6C+vx4w/4BWJpg3s/5l+6pRQEHzFRM8WFi4XffSP1Z+4qi7GbjWbvRQEbdIco5mIMq+zX4rPuLrw==}
+
+  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.3:
+    resolution: {integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==}
+    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==}
+
+  fast-unique-numbers@8.0.13:
+    resolution: {integrity: sha512-7OnTFAVPefgw2eBJ1xj2PGGR9FwYzSUso9decayHgCDX4sJkHLdcsYTytTg+tYv+wKF3U8gJuSBz2jJpQV4u/g==}
+    engines: {node: '>=16.1.0'}
+
+  fastq@1.19.1:
+    resolution: {integrity: sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==}
+
+  fdir@6.4.6:
+    resolution: {integrity: sha512-hiFoqpyZcfNm1yc4u8oWCf9A2c4D3QjCrks3zmoVKVxpQRzmPNar1hUJcBG2RQHvEVGDN+Jm81ZheVLAQMK6+w==}
+    peerDependencies:
+      picomatch: ^3 || ^4
+    peerDependenciesMeta:
+      picomatch:
+        optional: true
+
+  figures@6.1.0:
+    resolution: {integrity: sha512-d+l3qxjSesT4V7v2fh+QnmFnUWv9lSpjarhShNTgBOfA0ttejbQUAlHLitbjkoRiDulW0OPoQPYIGhIC8ohejg==}
+    engines: {node: '>=18'}
+
+  file-entry-cache@8.0.0:
+    resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==}
+    engines: {node: '>=16.0.0'}
+
+  fill-range@7.1.1:
+    resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==}
+    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.3:
+    resolution: {integrity: sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==}
+
+  follow-redirects@1.15.9:
+    resolution: {integrity: sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==}
+    engines: {node: '>=4.0'}
+    peerDependencies:
+      debug: '*'
+    peerDependenciesMeta:
+      debug:
+        optional: true
+
+  for-each@0.3.5:
+    resolution: {integrity: sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==}
+    engines: {node: '>= 0.4'}
+
+  form-data@4.0.3:
+    resolution: {integrity: sha512-qsITQPfmvMOSAdeyZ+12I1c+CKSstAFAwu+97zrnWAbIr5u8wfsExUzCesVLC8NgHuRUqNN4Zy6UPWUTRGslcA==}
+    engines: {node: '>= 6'}
+
+  fs-extra@11.3.0:
+    resolution: {integrity: sha512-Z4XaCL6dUDHfP/jT25jJKMmtxvuwbkrD1vNSMFlo9lNLY2c5FHYSQgHPRZUjAB26TpDEoW9HCOgplrdbaPV/ew==}
+    engines: {node: '>=14.14'}
+
+  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.8:
+    resolution: {integrity: sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q==}
+    engines: {node: '>= 0.4'}
+
+  functions-have-names@1.2.3:
+    resolution: {integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==}
+
+  gensync@1.0.0-beta.2:
+    resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==}
+    engines: {node: '>=6.9.0'}
+
+  get-intrinsic@1.3.0:
+    resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==}
+    engines: {node: '>= 0.4'}
+
+  get-proto@1.0.1:
+    resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==}
+    engines: {node: '>= 0.4'}
+
+  get-stream@9.0.1:
+    resolution: {integrity: sha512-kVCxPF3vQM/N0B1PmoqVUqgHP+EeVjmZSQn+1oCRPxd2P21P2F19lIgbR3HBosbB1PUhOAoctJnfEn2GbN2eZA==}
+    engines: {node: '>=18'}
+
+  get-symbol-description@1.1.0:
+    resolution: {integrity: sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==}
+    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'}
+
+  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'}
+
+  gopd@1.2.0:
+    resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==}
+    engines: {node: '>= 0.4'}
+
+  graceful-fs@4.2.11:
+    resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==}
+
+  graphemer@1.4.0:
+    resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==}
+
+  has-bigints@1.1.0:
+    resolution: {integrity: sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==}
+    engines: {node: '>= 0.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.2.0:
+    resolution: {integrity: sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ==}
+    engines: {node: '>= 0.4'}
+
+  has-symbols@1.1.0:
+    resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==}
+    engines: {node: '>= 0.4'}
+
+  has-tostringtag@1.0.2:
+    resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==}
+    engines: {node: '>= 0.4'}
+
+  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
+
+  help-me@5.0.0:
+    resolution: {integrity: sha512-7xgomUX6ADmcYzFik0HzAxh/73YlKR9bmFzf51CZwR+b6YtzU2m0u49hQCqV6SvlqIqsaxovfwdvbnsw3b/zpg==}
+
+  hookable@5.5.3:
+    resolution: {integrity: sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ==}
+
+  human-signals@8.0.1:
+    resolution: {integrity: sha512-eKCa6bwnJhvxj14kZk5NCPc6Hb6BdsU9DZcOnmQKSnO1VKrfV0zCvtttPZUsBvjmNDn8rpcJfpwSYnHBjc95MQ==}
+    engines: {node: '>=18.18.0'}
+
+  iconv-lite@0.6.3:
+    resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==}
+    engines: {node: '>=0.10.0'}
+
+  ieee754@1.2.1:
+    resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==}
+
+  ignore@5.3.2:
+    resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==}
+    engines: {node: '>= 4'}
+
+  ignore@7.0.5:
+    resolution: {integrity: sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==}
+    engines: {node: '>= 4'}
+
+  image-size@0.5.5:
+    resolution: {integrity: sha512-6TDAlDPZxUFCv+fuOkIoXT/V/f3Qbq8e37p+YOiYrUv3v9cc3/6x78VdfPgFVaB9dZYeLUfKgHRebpkm/oP2VQ==}
+    engines: {node: '>=0.10.0'}
+    hasBin: true
+
+  import-fresh@3.3.1:
+    resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==}
+    engines: {node: '>=6'}
+
+  imurmurhash@0.1.4:
+    resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==}
+    engines: {node: '>=0.8.19'}
+
+  inherits@2.0.4:
+    resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==}
+
+  internal-slot@1.1.0:
+    resolution: {integrity: sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==}
+    engines: {node: '>= 0.4'}
+
+  ip-address@9.0.5:
+    resolution: {integrity: sha512-zHtQzGojZXTwZTHQqra+ETKd4Sn3vgi7uBmlPoXVWZqYvuKmtI0l/VZTjqGmJY9x88GGOaZ9+G9ES8hC4T4X8g==}
+    engines: {node: '>= 12'}
+
+  is-array-buffer@3.0.5:
+    resolution: {integrity: sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==}
+    engines: {node: '>= 0.4'}
+
+  is-async-function@2.1.1:
+    resolution: {integrity: sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==}
+    engines: {node: '>= 0.4'}
+
+  is-bigint@1.1.0:
+    resolution: {integrity: sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==}
+    engines: {node: '>= 0.4'}
+
+  is-binary-path@2.1.0:
+    resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==}
+    engines: {node: '>=8'}
+
+  is-boolean-object@1.2.2:
+    resolution: {integrity: sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==}
+    engines: {node: '>= 0.4'}
+
+  is-callable@1.2.7:
+    resolution: {integrity: sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==}
+    engines: {node: '>= 0.4'}
+
+  is-core-module@2.16.1:
+    resolution: {integrity: sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==}
+    engines: {node: '>= 0.4'}
+
+  is-data-view@1.0.2:
+    resolution: {integrity: sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw==}
+    engines: {node: '>= 0.4'}
+
+  is-date-object@1.1.0:
+    resolution: {integrity: sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==}
+    engines: {node: '>= 0.4'}
+
+  is-docker@3.0.0:
+    resolution: {integrity: sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==}
+    engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
+    hasBin: true
+
+  is-extglob@2.1.1:
+    resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==}
+    engines: {node: '>=0.10.0'}
+
+  is-finalizationregistry@1.1.1:
+    resolution: {integrity: sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==}
+    engines: {node: '>= 0.4'}
+
+  is-generator-function@1.1.0:
+    resolution: {integrity: sha512-nPUB5km40q9e8UfN/Zc24eLlzdSf9OfKByBw9CIdw4H1giPMeA0OIJvbchsCu4npfI2QcMVBsGEBHKZ7wLTWmQ==}
+    engines: {node: '>= 0.4'}
+
+  is-glob@4.0.3:
+    resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==}
+    engines: {node: '>=0.10.0'}
+
+  is-inside-container@1.0.0:
+    resolution: {integrity: sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==}
+    engines: {node: '>=14.16'}
+    hasBin: true
+
+  is-map@2.0.3:
+    resolution: {integrity: sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==}
+    engines: {node: '>= 0.4'}
+
+  is-negative-zero@2.0.3:
+    resolution: {integrity: sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==}
+    engines: {node: '>= 0.4'}
+
+  is-number-object@1.1.1:
+    resolution: {integrity: sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==}
+    engines: {node: '>= 0.4'}
+
+  is-number@7.0.0:
+    resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==}
+    engines: {node: '>=0.12.0'}
+
+  is-plain-obj@4.1.0:
+    resolution: {integrity: sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==}
+    engines: {node: '>=12'}
+
+  is-plain-object@3.0.1:
+    resolution: {integrity: sha512-Xnpx182SBMrr/aBik8y+GuR4U1L9FqMSojwDQwPMmxyC6bvEqly9UBCxhauBF5vNh2gwWJNX6oDV7O+OM4z34g==}
+    engines: {node: '>=0.10.0'}
+
+  is-regex@1.2.1:
+    resolution: {integrity: sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==}
+    engines: {node: '>= 0.4'}
+
+  is-set@2.0.3:
+    resolution: {integrity: sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==}
+    engines: {node: '>= 0.4'}
+
+  is-shared-array-buffer@1.0.4:
+    resolution: {integrity: sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==}
+    engines: {node: '>= 0.4'}
+
+  is-stream@4.0.1:
+    resolution: {integrity: sha512-Dnz92NInDqYckGEUJv689RbRiTSEHCQ7wOVeALbkOz999YpqT46yMRIGtSNl2iCL1waAZSx40+h59NV/EwzV/A==}
+    engines: {node: '>=18'}
+
+  is-string@1.1.1:
+    resolution: {integrity: sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==}
+    engines: {node: '>= 0.4'}
+
+  is-symbol@1.1.1:
+    resolution: {integrity: sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==}
+    engines: {node: '>= 0.4'}
+
+  is-typed-array@1.1.15:
+    resolution: {integrity: sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==}
+    engines: {node: '>= 0.4'}
+
+  is-unicode-supported@2.1.0:
+    resolution: {integrity: sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ==}
+    engines: {node: '>=18'}
+
+  is-weakmap@2.0.2:
+    resolution: {integrity: sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==}
+    engines: {node: '>= 0.4'}
+
+  is-weakref@1.1.1:
+    resolution: {integrity: sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew==}
+    engines: {node: '>= 0.4'}
+
+  is-weakset@2.0.4:
+    resolution: {integrity: sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==}
+    engines: {node: '>= 0.4'}
+
+  is-what@3.14.1:
+    resolution: {integrity: sha512-sNxgpk9793nzSs7bA6JQJGeIuRBQhAaNGG77kzYQgMkrID+lS6SlK07K5LaptscDlSaIgH+GPFzf+d75FVxozA==}
+
+  is-what@4.1.16:
+    resolution: {integrity: sha512-ZhMwEosbFJkA0YhFnNDgTM4ZxDRsS6HqTo7qsZM08fehyRYIYa0yHu5R6mgo1n/8MgaPBXiPimPD77baVFYg+A==}
+    engines: {node: '>=12.13'}
+
+  is-wsl@3.1.0:
+    resolution: {integrity: sha512-UcVfVfaK4Sc4m7X3dUSoHoozQGBEFeDC+zVo06t98xe8CzHSZZBekNXH+tu0NalHolcJ/QAGqS46Hef7QXBIMw==}
+    engines: {node: '>=16'}
+
+  isarray@2.0.5:
+    resolution: {integrity: sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==}
+
+  isexe@2.0.0:
+    resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==}
+
+  isexe@3.1.1:
+    resolution: {integrity: sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ==}
+    engines: {node: '>=16'}
+
+  jiti@2.4.2:
+    resolution: {integrity: sha512-rg9zJN+G4n2nfJl5MW3BMygZX56zKPNVEYYqq7adpmMh4Jn2QNEwhvQlFy6jPVdcod7txZtKHWnyZiA3a0zP7A==}
+    hasBin: true
+
+  js-sdsl@4.3.0:
+    resolution: {integrity: sha512-mifzlm2+5nZ+lEcLJMoBK0/IH/bDg8XnJfd/Wq6IP+xoCjLZsTOnV2QpxlVbX9bMnkl5PdEjNtBJ9Cj1NjifhQ==}
+
+  js-tokens@4.0.0:
+    resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==}
+
+  js-yaml@4.1.0:
+    resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==}
+    hasBin: true
+
+  jsbn@1.1.0:
+    resolution: {integrity: sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A==}
+
+  jsesc@3.1.0:
+    resolution: {integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==}
+    engines: {node: '>=6'}
+    hasBin: true
+
+  json-buffer@3.0.1:
+    resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==}
+
+  json-parse-even-better-errors@4.0.0:
+    resolution: {integrity: sha512-lR4MXjGNgkJc7tkQ97kb2nuEMnNCyU//XYVH0MKTGcXEiSudQ5MKGKen3C5QubYy0vmq+JGitUg92uuywGEwIA==}
+    engines: {node: ^18.17.0 || >=20.5.0}
+
+  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
+
+  jsonfile@6.1.0:
+    resolution: {integrity: sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==}
+
+  keyv@4.5.4:
+    resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==}
+
+  kolorist@1.8.0:
+    resolution: {integrity: sha512-Y+60/zizpJ3HRH8DCss+q95yr6145JXZo46OTpFvDZWLfRCE4qChOyk1b26nMaNpfHHgxagk9dXT5OP0Tfe+dQ==}
+
+  less@4.3.0:
+    resolution: {integrity: sha512-X9RyH9fvemArzfdP8Pi3irr7lor2Ok4rOttDXBhlwDg+wKQsXOXgHWduAJE1EsF7JJx0w0bcO6BC6tCKKYnXKA==}
+    engines: {node: '>=14'}
+    hasBin: true
+
+  levn@0.4.1:
+    resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==}
+    engines: {node: '>= 0.8.0'}
+
+  local-pkg@1.1.1:
+    resolution: {integrity: sha512-WunYko2W1NcdfAFpuLUoucsgULmgDBRkdxHxWQ7mK0cQqwPiy8E1enjuRBrhLtZkB5iScJ1XIPdhVEFK8aOLSg==}
+    engines: {node: '>=14'}
+
+  locate-path@6.0.0:
+    resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==}
+    engines: {node: '>=10'}
+
+  lodash-es@4.17.21:
+    resolution: {integrity: sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==}
+
+  lodash.merge@4.6.2:
+    resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==}
+
+  lodash@4.17.21:
+    resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==}
+
+  loose-envify@1.4.0:
+    resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==}
+    hasBin: true
+
+  lru-cache@10.4.3:
+    resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==}
+
+  lru-cache@5.1.1:
+    resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==}
+
+  magic-string@0.30.17:
+    resolution: {integrity: sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==}
+
+  make-dir@2.1.0:
+    resolution: {integrity: sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==}
+    engines: {node: '>=6'}
+
+  math-intrinsics@1.1.0:
+    resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==}
+    engines: {node: '>= 0.4'}
+
+  memorystream@0.3.1:
+    resolution: {integrity: sha512-S3UwM3yj5mtUSEfP41UZmt/0SCoVYUcU1rkXv+BQ5Ig8ndL4sPoJNBUJERafdPb5jjHJGuMgytgKvKIf58XNBw==}
+    engines: {node: '>= 0.10.0'}
+
+  merge2@1.4.1:
+    resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==}
+    engines: {node: '>= 8'}
+
+  micromatch@4.0.8:
+    resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==}
+    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
+
+  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==}
+
+  mitt@3.0.1:
+    resolution: {integrity: sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==}
+
+  mlly@1.7.4:
+    resolution: {integrity: sha512-qmdSIPC4bDJXgZTCR7XosJiNKySV7O215tsPtDN9iEO/7q/76b/ijtgRu/+epFXSJhijtTCCGp3DWS549P3xKw==}
+
+  mqtt-packet@9.0.2:
+    resolution: {integrity: sha512-MvIY0B8/qjq7bKxdN1eD+nrljoeaai+qjLJgfRn3TiMuz0pamsIWY2bFODPZMSNmabsLANXsLl4EMoWvlaTZWA==}
+
+  mqtt@5.13.1:
+    resolution: {integrity: sha512-g+4G+ma0UeL3Pgu1y1si2NHb4VLIEUCtF789WrG99lLG0XZyoab2EJoy58YgGSg/1yFdthDBH0+4llsZZD/vug==}
+    engines: {node: '>=16.0.0'}
+    hasBin: true
+
+  mrmime@2.0.1:
+    resolution: {integrity: sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ==}
+    engines: {node: '>=10'}
+
+  ms@2.1.3:
+    resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==}
+
+  muggle-string@0.4.1:
+    resolution: {integrity: sha512-VNTrAak/KhO2i8dqqnqnAHOa3cYBwXEZe9h+D5h/1ZqFSTEFHdM65lR7RoIqq3tBBYavsOXV84NoHXZ0AkPyqQ==}
+
+  nanoid@3.3.11:
+    resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==}
+    engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
+    hasBin: true
+
+  nanoid@5.1.5:
+    resolution: {integrity: sha512-Ir/+ZpE9fDsNH0hQ3C68uyThDXzYcim2EqcZ8zn8Chtt1iylPT9xXJB0kPCnqzgcEGikO9RxSrh63MsmVCU7Fw==}
+    engines: {node: ^18 || >=20}
+    hasBin: true
+
+  nanopop@2.4.2:
+    resolution: {integrity: sha512-NzOgmMQ+elxxHeIha+OG/Pv3Oc3p4RU2aBhwWwAqDpXrdTbtRylbRLQztLy8dMMwfl6pclznBdfUhccEn9ZIzw==}
+
+  natural-compare@1.4.0:
+    resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==}
+
+  needle@3.3.1:
+    resolution: {integrity: sha512-6k0YULvhpw+RoLNiQCRKOl09Rv1dPLr8hHnVjHqdolKwDrdNyk+Hmrthi4lIGPPz3r39dLx0hsF5s40sZ3Us4Q==}
+    engines: {node: '>= 4.4.x'}
+    hasBin: true
+
+  node-releases@2.0.19:
+    resolution: {integrity: sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==}
+
+  normalize-path@3.0.0:
+    resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==}
+    engines: {node: '>=0.10.0'}
+
+  npm-normalize-package-bin@4.0.0:
+    resolution: {integrity: sha512-TZKxPvItzai9kN9H/TkmCtx/ZN/hvr3vUycjlfmH0ootY9yFBzNOpiXAdIn1Iteqsvk4lQn6B5PTrt+n6h8k/w==}
+    engines: {node: ^18.17.0 || >=20.5.0}
+
+  npm-run-all2@8.0.4:
+    resolution: {integrity: sha512-wdbB5My48XKp2ZfJUlhnLVihzeuA1hgBnqB2J9ahV77wLS+/YAJAlN8I+X3DIFIPZ3m5L7nplmlbhNiFDmXRDA==}
+    engines: {node: ^20.5.0 || >=22.0.0, npm: '>= 10'}
+    hasBin: true
+
+  npm-run-path@6.0.0:
+    resolution: {integrity: sha512-9qny7Z9DsQU8Ou39ERsPU4OZQlSTP47ShQzuKZ6PRXpYLtIFgl/DEBYEXKlvcEa+9tHVcK8CF81Y2V72qaZhWA==}
+    engines: {node: '>=18'}
+
+  nth-check@2.1.1:
+    resolution: {integrity: sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==}
+
+  number-allocator@1.0.14:
+    resolution: {integrity: sha512-OrL44UTVAvkKdOdRQZIJpLkAdjXGTRda052sN4sO77bKEzYYqWKMBjQvrJFzqygI99gL6Z4u2xctPW1tB8ErvA==}
+
+  object-inspect@1.13.4:
+    resolution: {integrity: sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==}
+    engines: {node: '>= 0.4'}
+
+  object-keys@1.1.1:
+    resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==}
+    engines: {node: '>= 0.4'}
+
+  object.assign@4.1.7:
+    resolution: {integrity: sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==}
+    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.1:
+    resolution: {integrity: sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA==}
+    engines: {node: '>= 0.4'}
+
+  open@10.1.2:
+    resolution: {integrity: sha512-cxN6aIDPz6rm8hbebcP7vrQNhvRcveZoJU72Y7vskh4oIm+BZwBECnx5nTmrlres1Qapvx27Qo1Auukpf8PKXw==}
+    engines: {node: '>=18'}
+
+  optionator@0.9.4:
+    resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==}
+    engines: {node: '>= 0.8.0'}
+
+  own-keys@1.0.1:
+    resolution: {integrity: sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==}
+    engines: {node: '>= 0.4'}
+
+  p-limit@3.1.0:
+    resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==}
+    engines: {node: '>=10'}
+
+  p-locate@5.0.0:
+    resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==}
+    engines: {node: '>=10'}
+
+  parent-module@1.0.1:
+    resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==}
+    engines: {node: '>=6'}
+
+  parse-ms@4.0.0:
+    resolution: {integrity: sha512-TXfryirbmq34y8QBwgqCVLi+8oA3oWx2eAnSn62ITyEhEYaWRlVZ2DvMM9eZbMs/RfxPu/PK/aBLyGj4IrqMHw==}
+    engines: {node: '>=18'}
+
+  parse-node-version@1.0.1:
+    resolution: {integrity: sha512-3YHlOa/JgH6Mnpr05jP9eDG254US9ek25LyIxZlDItp2iJtwyaXQb57lBYLdT3MowkUFYEV2XXNAYIPlESvJlA==}
+    engines: {node: '>= 0.10'}
+
+  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-key@3.1.1:
+    resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==}
+    engines: {node: '>=8'}
+
+  path-key@4.0.0:
+    resolution: {integrity: sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==}
+    engines: {node: '>=12'}
+
+  path-parse@1.0.7:
+    resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==}
+
+  pathe@2.0.3:
+    resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==}
+
+  perfect-debounce@1.0.0:
+    resolution: {integrity: sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA==}
+
+  picocolors@1.1.1:
+    resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==}
+
+  picomatch@2.3.1:
+    resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==}
+    engines: {node: '>=8.6'}
+
+  picomatch@4.0.2:
+    resolution: {integrity: sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==}
+    engines: {node: '>=12'}
+
+  pidtree@0.6.0:
+    resolution: {integrity: sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g==}
+    engines: {node: '>=0.10'}
+    hasBin: true
+
+  pify@4.0.1:
+    resolution: {integrity: sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==}
+    engines: {node: '>=6'}
+
+  pinia-plugin-persistedstate@4.4.1:
+    resolution: {integrity: sha512-lmuMPpXla2zJKjxEq34e1E9P9jxkWEhcVwwioCCE0izG45kkTOvQfCzvwhW3i38cvnaWC7T1eRdkd15Re59ldw==}
+    peerDependencies:
+      '@nuxt/kit': '>=3.0.0'
+      '@pinia/nuxt': '>=0.10.0'
+      pinia: '>=3.0.0'
+    peerDependenciesMeta:
+      '@nuxt/kit':
+        optional: true
+      '@pinia/nuxt':
+        optional: true
+      pinia:
+        optional: true
+
+  pinia@3.0.3:
+    resolution: {integrity: sha512-ttXO/InUULUXkMHpTdp9Fj4hLpD/2AoJdmAbAeW2yu1iy1k+pkFekQXw5VpC0/5p51IOR/jDaDRfRWRnMMsGOA==}
+    peerDependencies:
+      typescript: '>=4.4.4'
+      vue: ^2.7.0 || ^3.5.11
+    peerDependenciesMeta:
+      typescript:
+        optional: true
+
+  pinyin-pro@3.26.0:
+    resolution: {integrity: sha512-HcBZZb0pvm0/JkPhZHWA5Hqp2cWHXrrW/WrV+OtaYYM+kf35ffvZppIUuGmyuQ7gDr1JDJKMkbEE+GN0wfMoGg==}
+
+  pkg-types@1.3.1:
+    resolution: {integrity: sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==}
+
+  pkg-types@2.2.0:
+    resolution: {integrity: sha512-2SM/GZGAEkPp3KWORxQZns4M+WSeXbC2HEvmOIJe3Cmiv6ieAJvdVhDldtHqM5J1Y7MrR1XhkBT/rMlhh9FdqQ==}
+
+  possible-typed-array-names@1.1.0:
+    resolution: {integrity: sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==}
+    engines: {node: '>= 0.4'}
+
+  postcss-selector-parser@6.1.2:
+    resolution: {integrity: sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==}
+    engines: {node: '>=4'}
+
+  postcss@8.5.6:
+    resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==}
+    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.5.3:
+    resolution: {integrity: sha512-QQtaxnoDJeAkDvDKWCLiwIXkTgRhwYDEQCghU9Z6q03iyek/rxRh/2lC3HB7P8sWT2xC/y5JDctPLBIGzHKbhw==}
+    engines: {node: '>=14'}
+    hasBin: true
+
+  pretty-ms@9.2.0:
+    resolution: {integrity: sha512-4yf0QO/sllf/1zbZWYnvWw3NxCQwLXKzIj0G849LSufP15BXKM0rbD2Z3wVnkMfjdn/CB0Dpp444gYAACdsplg==}
+    engines: {node: '>=18'}
+
+  process-nextick-args@2.0.1:
+    resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==}
+
+  process@0.11.10:
+    resolution: {integrity: sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==}
+    engines: {node: '>= 0.6.0'}
+
+  proxy-from-env@1.1.0:
+    resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==}
+
+  prr@1.0.1:
+    resolution: {integrity: sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw==}
+
+  punycode@2.3.1:
+    resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==}
+    engines: {node: '>=6'}
+
+  quansync@0.2.10:
+    resolution: {integrity: sha512-t41VRkMYbkHyCYmOvx/6URnN80H7k4X0lLdBMGsz+maAwrJQYB1djpV6vHrQIBE0WBSGqhtEHrK9U3DWWH8v7A==}
+
+  queue-microtask@1.2.3:
+    resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==}
+
+  read-package-json-fast@4.0.0:
+    resolution: {integrity: sha512-qpt8EwugBWDw2cgE2W+/3oxC+KTez2uSVR8JU9Q36TXPAGCaozfQUs59v4j4GFpWTaw0i6hAZSvOmu1J0uOEUg==}
+    engines: {node: ^18.17.0 || >=20.5.0}
+
+  readable-stream@3.6.2:
+    resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==}
+    engines: {node: '>= 6'}
+
+  readable-stream@4.7.0:
+    resolution: {integrity: sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==}
+    engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
+
+  readdirp@3.6.0:
+    resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==}
+    engines: {node: '>=8.10.0'}
+
+  reflect.getprototypeof@1.0.10:
+    resolution: {integrity: sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==}
+    engines: {node: '>= 0.4'}
+
+  regexp.prototype.flags@1.5.4:
+    resolution: {integrity: sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==}
+    engines: {node: '>= 0.4'}
+
+  resize-observer-polyfill@1.5.1:
+    resolution: {integrity: sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg==}
+
+  resolve-from@4.0.0:
+    resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==}
+    engines: {node: '>=4'}
+
+  resolve@1.22.10:
+    resolution: {integrity: sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==}
+    engines: {node: '>= 0.4'}
+    hasBin: true
+
+  reusify@1.1.0:
+    resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==}
+    engines: {iojs: '>=1.0.0', node: '>=0.10.0'}
+
+  rfdc@1.4.1:
+    resolution: {integrity: sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==}
+
+  rollup@4.44.2:
+    resolution: {integrity: sha512-PVoapzTwSEcelaWGth3uR66u7ZRo6qhPHc0f2uRO9fX6XDVNrIiGYS0Pj9+R8yIIYSD/mCx2b16Ws9itljKSPg==}
+    engines: {node: '>=18.0.0', npm: '>=8.0.0'}
+    hasBin: true
+
+  run-applescript@7.0.0:
+    resolution: {integrity: sha512-9by4Ij99JUr/MCFBUkDKLWK3G9HVXmabKz9U5MlIAIuvuzkiOicRYs8XJLxX+xahD+mLiiCYDqF9dKAgtzKP1A==}
+    engines: {node: '>=18'}
+
+  run-parallel@1.2.0:
+    resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==}
+
+  safe-array-concat@1.1.3:
+    resolution: {integrity: sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q==}
+    engines: {node: '>=0.4'}
+
+  safe-buffer@5.2.1:
+    resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==}
+
+  safe-push-apply@1.0.0:
+    resolution: {integrity: sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==}
+    engines: {node: '>= 0.4'}
+
+  safe-regex-test@1.1.0:
+    resolution: {integrity: sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==}
+    engines: {node: '>= 0.4'}
+
+  safer-buffer@2.1.2:
+    resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==}
+
+  sax@1.4.1:
+    resolution: {integrity: sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg==}
+
+  scroll-into-view-if-needed@2.2.31:
+    resolution: {integrity: sha512-dGCXy99wZQivjmjIqihaBQNjryrz5rueJY7eHfTdyWEiR4ttYpsajb14rn9s5d4DY4EcY6+4+U/maARBXJedkA==}
+
+  semver@5.7.2:
+    resolution: {integrity: sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==}
+    hasBin: true
+
+  semver@6.3.1:
+    resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==}
+    hasBin: true
+
+  semver@7.7.2:
+    resolution: {integrity: sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==}
+    engines: {node: '>=10'}
+    hasBin: true
+
+  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'}
+
+  set-proto@1.0.0:
+    resolution: {integrity: sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==}
+    engines: {node: '>= 0.4'}
+
+  shallow-equal@1.2.1:
+    resolution: {integrity: sha512-S4vJDjHHMBaiZuT9NPb616CSmLf618jawtv3sufLl6ivK8WocjAo58cXwbRV1cgqxH0Qbv+iUt6m05eqEa2IRA==}
+
+  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'}
+
+  shell-quote@1.8.3:
+    resolution: {integrity: sha512-ObmnIF4hXNg1BqhnHmgbDETF8dLPCggZWBjkQfhZpbszZnYur5DUljTcCHii5LC3J5E0yeO/1LIMyH+UvHQgyw==}
+    engines: {node: '>= 0.4'}
+
+  side-channel-list@1.0.0:
+    resolution: {integrity: sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==}
+    engines: {node: '>= 0.4'}
+
+  side-channel-map@1.0.1:
+    resolution: {integrity: sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==}
+    engines: {node: '>= 0.4'}
+
+  side-channel-weakmap@1.0.2:
+    resolution: {integrity: sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==}
+    engines: {node: '>= 0.4'}
+
+  side-channel@1.1.0:
+    resolution: {integrity: sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==}
+    engines: {node: '>= 0.4'}
+
+  signal-exit@4.1.0:
+    resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==}
+    engines: {node: '>=14'}
+
+  sirv@3.0.1:
+    resolution: {integrity: sha512-FoqMu0NCGBLCcAkS1qA+XJIQTR6/JHfQXl+uGteNCQ76T91DMUjPa9xfmeqMY3z80nLSg9yQmNjK0Px6RWsH/A==}
+    engines: {node: '>=18'}
+
+  smart-buffer@4.2.0:
+    resolution: {integrity: sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==}
+    engines: {node: '>= 6.0.0', npm: '>= 3.0.0'}
+
+  socks@2.8.5:
+    resolution: {integrity: sha512-iF+tNDQla22geJdTyJB1wM/qrX9DMRwWrciEPwWLPRWAUEM8sQiyxgckLxWT1f7+9VabJS0jTGGr4QgBuvi6Ww==}
+    engines: {node: '>= 10.0.0', npm: '>= 3.0.0'}
+
+  source-map-js@1.2.1:
+    resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==}
+    engines: {node: '>=0.10.0'}
+
+  source-map@0.6.1:
+    resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==}
+    engines: {node: '>=0.10.0'}
+
+  speakingurl@14.0.1:
+    resolution: {integrity: sha512-1POYv7uv2gXoyGFpBCmpDVSNV74IfsWlDW216UPjbWufNf+bSU6GdbDsxdcxtfwb4xlI3yxzOTKClUosxARYrQ==}
+    engines: {node: '>=0.10.0'}
+
+  split2@4.2.0:
+    resolution: {integrity: sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==}
+    engines: {node: '>= 10.x'}
+
+  sprintf-js@1.1.3:
+    resolution: {integrity: sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==}
+
+  stop-iteration-iterator@1.1.0:
+    resolution: {integrity: sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==}
+    engines: {node: '>= 0.4'}
+
+  string.prototype.trim@1.2.10:
+    resolution: {integrity: sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA==}
+    engines: {node: '>= 0.4'}
+
+  string.prototype.trimend@1.0.9:
+    resolution: {integrity: sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ==}
+    engines: {node: '>= 0.4'}
+
+  string.prototype.trimstart@1.0.8:
+    resolution: {integrity: sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==}
+    engines: {node: '>= 0.4'}
+
+  string_decoder@1.3.0:
+    resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==}
+
+  strip-bom@3.0.0:
+    resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==}
+    engines: {node: '>=4'}
+
+  strip-final-newline@4.0.0:
+    resolution: {integrity: sha512-aulFJcD6YK8V1G7iRB5tigAP4TsHBZZrOV8pjV++zdUwmeV8uzbY7yn6h9MswN62adStNZFuCIx4haBnRuMDaw==}
+    engines: {node: '>=18'}
+
+  strip-json-comments@3.1.1:
+    resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==}
+    engines: {node: '>=8'}
+
+  stylis@4.3.6:
+    resolution: {integrity: sha512-yQ3rwFWRfwNUY7H5vpU0wfdkNSnvnJinhF9830Swlaxl03zsOjCfmX0ugac+3LtK0lYSgwL/KXc8oYL3mG4YFQ==}
+
+  superjson@2.2.2:
+    resolution: {integrity: sha512-5JRxVqC8I8NuOUjzBbvVJAKNM8qoVuH0O77h4WInc/qC2q5IreqKxYwgkga3PfA22OayK2ikceb/B26dztPl+Q==}
+    engines: {node: '>=16'}
+
+  supports-color@7.2.0:
+    resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==}
+    engines: {node: '>=8'}
+
+  supports-preserve-symlinks-flag@1.0.0:
+    resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==}
+    engines: {node: '>= 0.4'}
+
+  synckit@0.11.8:
+    resolution: {integrity: sha512-+XZ+r1XGIJGeQk3VvXhT6xx/VpbHsRzsTkGgF6E5RX9TTXD0118l87puaEBZ566FhqblC6U0d4XnubznJDm30A==}
+    engines: {node: ^14.18.0 || >=16.0.0}
+
+  throttle-debounce@5.0.2:
+    resolution: {integrity: sha512-B71/4oyj61iNH0KeCamLuE2rmKuTO5byTOSVwECM5FA7TiAiAW+UqTKZ9ERueC4qvgSttUhdmq1mXC3kJqGX7A==}
+    engines: {node: '>=12.22'}
+
+  tinyglobby@0.2.14:
+    resolution: {integrity: sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ==}
+    engines: {node: '>=12.0.0'}
+
+  to-regex-range@5.0.1:
+    resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==}
+    engines: {node: '>=8.0'}
+
+  totalist@3.0.1:
+    resolution: {integrity: sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==}
+    engines: {node: '>=6'}
+
+  ts-api-utils@2.1.0:
+    resolution: {integrity: sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==}
+    engines: {node: '>=18.12'}
+    peerDependencies:
+      typescript: '>=4.8.4'
+
+  tsconfig-paths@3.15.0:
+    resolution: {integrity: sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==}
+
+  tslib@2.3.0:
+    resolution: {integrity: sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==}
+
+  tslib@2.8.1:
+    resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==}
+
+  type-check@0.4.0:
+    resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==}
+    engines: {node: '>= 0.8.0'}
+
+  typed-array-buffer@1.0.3:
+    resolution: {integrity: sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==}
+    engines: {node: '>= 0.4'}
+
+  typed-array-byte-length@1.0.3:
+    resolution: {integrity: sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg==}
+    engines: {node: '>= 0.4'}
+
+  typed-array-byte-offset@1.0.4:
+    resolution: {integrity: sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ==}
+    engines: {node: '>= 0.4'}
+
+  typed-array-length@1.0.7:
+    resolution: {integrity: sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==}
+    engines: {node: '>= 0.4'}
+
+  typedarray@0.0.6:
+    resolution: {integrity: sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==}
+
+  typescript-eslint@8.35.1:
+    resolution: {integrity: sha512-xslJjFzhOmHYQzSB/QTeASAHbjmxOGEP6Coh93TXmUBFQoJ1VU35UHIDmG06Jd6taf3wqqC1ntBnCMeymy5Ovw==}
+    engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+    peerDependencies:
+      eslint: ^8.57.0 || ^9.0.0
+      typescript: '>=4.8.4 <5.9.0'
+
+  typescript@5.8.3:
+    resolution: {integrity: sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==}
+    engines: {node: '>=14.17'}
+    hasBin: true
+
+  ufo@1.6.1:
+    resolution: {integrity: sha512-9a4/uxlTWJ4+a5i0ooc1rU7C7YOw3wT+UGqdeNNHWnOF9qcMBgLRS+4IYUqbczewFx4mLEig6gawh7X6mFlEkA==}
+
+  unbox-primitive@1.1.0:
+    resolution: {integrity: sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==}
+    engines: {node: '>= 0.4'}
+
+  undici-types@6.21.0:
+    resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==}
+
+  unicorn-magic@0.3.0:
+    resolution: {integrity: sha512-+QBBXBCvifc56fsbuxZQ6Sic3wqqc3WWaqxs58gvJrcOuN83HGTCwz3oS5phzU9LthRNE9VrJCFCLUgHeeFnfA==}
+    engines: {node: '>=18'}
+
+  universalify@2.0.1:
+    resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==}
+    engines: {node: '>= 10.0.0'}
+
+  unplugin-utils@0.2.4:
+    resolution: {integrity: sha512-8U/MtpkPkkk3Atewj1+RcKIjb5WBimZ/WSLhhR3w6SsIj8XJuKTacSP8g+2JhfSGw0Cb125Y+2zA/IzJZDVbhA==}
+    engines: {node: '>=18.12.0'}
+
+  unplugin-vue-components@28.8.0:
+    resolution: {integrity: sha512-2Q6ZongpoQzuXDK0ZsVzMoshH0MWZQ1pzVL538G7oIDKRTVzHjppBDS8aB99SADGHN3lpGU7frraCG6yWNoL5Q==}
+    engines: {node: '>=14'}
+    peerDependencies:
+      '@babel/parser': ^7.15.8
+      '@nuxt/kit': ^3.2.2 || ^4.0.0
+      vue: 2 || 3
+    peerDependenciesMeta:
+      '@babel/parser':
+        optional: true
+      '@nuxt/kit':
+        optional: true
+
+  unplugin@2.3.5:
+    resolution: {integrity: sha512-RyWSb5AHmGtjjNQ6gIlA67sHOsWpsbWpwDokLwTcejVdOjEkJZh7QKu14J00gDDVSh8kGH4KYC/TNBceXFZhtw==}
+    engines: {node: '>=18.12.0'}
+
+  update-browserslist-db@1.1.3:
+    resolution: {integrity: sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==}
+    hasBin: true
+    peerDependencies:
+      browserslist: '>= 4.21.0'
+
+  uri-js@4.4.1:
+    resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==}
+
+  util-deprecate@1.0.2:
+    resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==}
+
+  vite-hot-client@2.1.0:
+    resolution: {integrity: sha512-7SpgZmU7R+dDnSmvXE1mfDtnHLHQSisdySVR7lO8ceAXvM0otZeuQQ6C8LrS5d/aYyP/QZ0hI0L+dIPrm4YlFQ==}
+    peerDependencies:
+      vite: ^2.6.0 || ^3.0.0 || ^4.0.0 || ^5.0.0-0 || ^6.0.0-0 || ^7.0.0-0
+
+  vite-plugin-inspect@0.8.9:
+    resolution: {integrity: sha512-22/8qn+LYonzibb1VeFZmISdVao5kC22jmEKm24vfFE8siEn47EpVcCLYMv6iKOYMJfjSvSJfueOwcFCkUnV3A==}
+    engines: {node: '>=14'}
+    peerDependencies:
+      '@nuxt/kit': '*'
+      vite: ^3.1.0 || ^4.0.0 || ^5.0.0-0 || ^6.0.1
+    peerDependenciesMeta:
+      '@nuxt/kit':
+        optional: true
+
+  vite-plugin-vue-devtools@7.7.7:
+    resolution: {integrity: sha512-d0fIh3wRcgSlr4Vz7bAk4va1MkdqhQgj9ANE/rBhsAjOnRfTLs2ocjFMvSUOsv6SRRXU9G+VM7yMgqDb6yI4iQ==}
+    engines: {node: '>=v14.21.3'}
+    peerDependencies:
+      vite: ^3.1.0 || ^4.0.0-0 || ^5.0.0-0 || ^6.0.0-0 || ^7.0.0-0
+
+  vite-plugin-vue-inspector@5.3.2:
+    resolution: {integrity: sha512-YvEKooQcSiBTAs0DoYLfefNja9bLgkFM7NI2b07bE2SruuvX0MEa9cMaxjKVMkeCp5Nz9FRIdcN1rOdFVBeL6Q==}
+    peerDependencies:
+      vite: ^3.0.0-0 || ^4.0.0-0 || ^5.0.0-0 || ^6.0.0-0 || ^7.0.0-0
+
+  vite@7.0.2:
+    resolution: {integrity: sha512-hxdyZDY1CM6SNpKI4w4lcUc3Mtkd9ej4ECWVHSMrOdSinVc2zYOAppHeGc/hzmRo3pxM5blMzkuWHOJA/3NiFw==}
+    engines: {node: ^20.19.0 || >=22.12.0}
+    hasBin: true
+    peerDependencies:
+      '@types/node': ^20.19.0 || >=22.12.0
+      jiti: '>=1.21.0'
+      less: ^4.0.0
+      lightningcss: ^1.21.0
+      sass: ^1.70.0
+      sass-embedded: ^1.70.0
+      stylus: '>=0.54.8'
+      sugarss: ^5.0.0
+      terser: ^5.16.0
+      tsx: ^4.8.1
+      yaml: ^2.4.2
+    peerDependenciesMeta:
+      '@types/node':
+        optional: true
+      jiti:
+        optional: true
+      less:
+        optional: true
+      lightningcss:
+        optional: true
+      sass:
+        optional: true
+      sass-embedded:
+        optional: true
+      stylus:
+        optional: true
+      sugarss:
+        optional: true
+      terser:
+        optional: true
+      tsx:
+        optional: true
+      yaml:
+        optional: true
+
+  vscode-uri@3.1.0:
+    resolution: {integrity: sha512-/BpdSx+yCQGnCvecbyXdxHDkuk55/G3xwnC0GqY4gmQ3j+A+g8kzzgB4Nk/SINjqn6+waqw3EgbVF2QKExkRxQ==}
+
+  vue-eslint-parser@10.2.0:
+    resolution: {integrity: sha512-CydUvFOQKD928UzZhTp4pr2vWz1L+H99t7Pkln2QSPdvmURT0MoC4wUccfCnuEaihNsu9aYYyk+bep8rlfkUXw==}
+    engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+    peerDependencies:
+      eslint: ^8.57.0 || ^9.0.0
+
+  vue-router@4.5.1:
+    resolution: {integrity: sha512-ogAF3P97NPm8fJsE4by9dwSYtDwXIY1nFY9T6DyQnGHd1E2Da94w9JIolpe42LJGIl0DwOHBi8TcRPlPGwbTtw==}
+    peerDependencies:
+      vue: ^3.2.0
+
+  vue-tsc@2.2.12:
+    resolution: {integrity: sha512-P7OP77b2h/Pmk+lZdJ0YWs+5tJ6J2+uOQPo7tlBnY44QqQSPYvS0qVT4wqDJgwrZaLe47etJLLQRFia71GYITw==}
+    hasBin: true
+    peerDependencies:
+      typescript: '>=5.0.0'
+
+  vue-types@3.0.2:
+    resolution: {integrity: sha512-IwUC0Aq2zwaXqy74h4WCvFCUtoV0iSWr0snWnE9TnU18S66GAQyqQbRf2qfJtUuiFsBf6qp0MEwdonlwznlcrw==}
+    engines: {node: '>=10.15.0'}
+    peerDependencies:
+      vue: ^3.0.0
+
+  vue@3.5.17:
+    resolution: {integrity: sha512-LbHV3xPN9BeljML+Xctq4lbz2lVHCR6DtbpTf5XIO6gugpXUN49j2QQPcMj086r9+AkJ0FfUT8xjulKKBkkr9g==}
+    peerDependencies:
+      typescript: '*'
+    peerDependenciesMeta:
+      typescript:
+        optional: true
+
+  warning@4.0.3:
+    resolution: {integrity: sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==}
+
+  webpack-virtual-modules@0.6.2:
+    resolution: {integrity: sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ==}
+
+  which-boxed-primitive@1.1.1:
+    resolution: {integrity: sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==}
+    engines: {node: '>= 0.4'}
+
+  which-builtin-type@1.2.1:
+    resolution: {integrity: sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q==}
+    engines: {node: '>= 0.4'}
+
+  which-collection@1.0.2:
+    resolution: {integrity: sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==}
+    engines: {node: '>= 0.4'}
+
+  which-typed-array@1.1.19:
+    resolution: {integrity: sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw==}
+    engines: {node: '>= 0.4'}
+
+  which@2.0.2:
+    resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==}
+    engines: {node: '>= 8'}
+    hasBin: true
+
+  which@5.0.0:
+    resolution: {integrity: sha512-JEdGzHwwkrbWoGOlIHqQ5gtprKGOenpDHpxE9zVR1bWbOtYRyPPHMe9FaP6x61CmNaTThSkb0DAJte5jD+DmzQ==}
+    engines: {node: ^18.17.0 || >=20.5.0}
+    hasBin: true
+
+  word-wrap@1.2.5:
+    resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==}
+    engines: {node: '>=0.10.0'}
+
+  worker-timers-broker@6.1.8:
+    resolution: {integrity: sha512-FUCJu9jlK3A8WqLTKXM9E6kAmI/dR1vAJ8dHYLMisLNB/n3GuaFIjJ7pn16ZcD1zCOf7P6H62lWIEBi+yz/zQQ==}
+
+  worker-timers-worker@7.0.71:
+    resolution: {integrity: sha512-ks/5YKwZsto1c2vmljroppOKCivB/ma97g9y77MAAz2TBBjPPgpoOiS1qYQKIgvGTr2QYPT3XhJWIB6Rj2MVPQ==}
+
+  worker-timers@7.1.8:
+    resolution: {integrity: sha512-R54psRKYVLuzff7c1OTFcq/4Hue5Vlz4bFtNEIarpSiCYhpifHU3aIQI29S84o1j87ePCYqbmEJPqwBTf+3sfw==}
+
+  ws@8.18.3:
+    resolution: {integrity: sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==}
+    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
+
+  xml-name-validator@4.0.0:
+    resolution: {integrity: sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==}
+    engines: {node: '>=12'}
+
+  yallist@3.1.1:
+    resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==}
+
+  yocto-queue@0.1.0:
+    resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==}
+    engines: {node: '>=10'}
+
+  yoctocolors@2.1.1:
+    resolution: {integrity: sha512-GQHQqAopRhwU8Kt1DDM8NjibDXHC8eoh1erhGAJPEyveY9qqVeXvVikNKrDz69sHowPMorbPUrH/mx8c50eiBQ==}
+    engines: {node: '>=18'}
+
+  zrender@5.6.1:
+    resolution: {integrity: sha512-OFXkDJKcrlx5su2XbzJvj/34Q3m6PvyCZkVPHGYpcCJ52ek4U/ymZyfuV1nKE23AyBJ51E/6Yr0mhZ7xGTO4ag==}
+
+snapshots:
+
+  '@ampproject/remapping@2.3.0':
+    dependencies:
+      '@jridgewell/gen-mapping': 0.3.12
+      '@jridgewell/trace-mapping': 0.3.29
+
+  '@ant-design/colors@6.0.0':
+    dependencies:
+      '@ctrl/tinycolor': 3.6.1
+
+  '@ant-design/icons-svg@4.4.2': {}
+
+  '@ant-design/icons-vue@7.0.1(vue@3.5.17(typescript@5.8.3))':
+    dependencies:
+      '@ant-design/colors': 6.0.0
+      '@ant-design/icons-svg': 4.4.2
+      vue: 3.5.17(typescript@5.8.3)
+
+  '@antfu/utils@0.7.10': {}
+
+  '@babel/code-frame@7.27.1':
+    dependencies:
+      '@babel/helper-validator-identifier': 7.27.1
+      js-tokens: 4.0.0
+      picocolors: 1.1.1
+
+  '@babel/compat-data@7.28.0': {}
+
+  '@babel/core@7.28.0':
+    dependencies:
+      '@ampproject/remapping': 2.3.0
+      '@babel/code-frame': 7.27.1
+      '@babel/generator': 7.28.0
+      '@babel/helper-compilation-targets': 7.27.2
+      '@babel/helper-module-transforms': 7.27.3(@babel/core@7.28.0)
+      '@babel/helpers': 7.27.6
+      '@babel/parser': 7.28.0
+      '@babel/template': 7.27.2
+      '@babel/traverse': 7.28.0
+      '@babel/types': 7.28.0
+      convert-source-map: 2.0.0
+      debug: 4.4.1
+      gensync: 1.0.0-beta.2
+      json5: 2.2.3
+      semver: 6.3.1
+    transitivePeerDependencies:
+      - supports-color
+
+  '@babel/generator@7.28.0':
+    dependencies:
+      '@babel/parser': 7.28.0
+      '@babel/types': 7.28.0
+      '@jridgewell/gen-mapping': 0.3.12
+      '@jridgewell/trace-mapping': 0.3.29
+      jsesc: 3.1.0
+
+  '@babel/helper-annotate-as-pure@7.27.3':
+    dependencies:
+      '@babel/types': 7.28.0
+
+  '@babel/helper-compilation-targets@7.27.2':
+    dependencies:
+      '@babel/compat-data': 7.28.0
+      '@babel/helper-validator-option': 7.27.1
+      browserslist: 4.25.1
+      lru-cache: 5.1.1
+      semver: 6.3.1
+
+  '@babel/helper-create-class-features-plugin@7.27.1(@babel/core@7.28.0)':
+    dependencies:
+      '@babel/core': 7.28.0
+      '@babel/helper-annotate-as-pure': 7.27.3
+      '@babel/helper-member-expression-to-functions': 7.27.1
+      '@babel/helper-optimise-call-expression': 7.27.1
+      '@babel/helper-replace-supers': 7.27.1(@babel/core@7.28.0)
+      '@babel/helper-skip-transparent-expression-wrappers': 7.27.1
+      '@babel/traverse': 7.28.0
+      semver: 6.3.1
+    transitivePeerDependencies:
+      - supports-color
+
+  '@babel/helper-globals@7.28.0': {}
+
+  '@babel/helper-member-expression-to-functions@7.27.1':
+    dependencies:
+      '@babel/traverse': 7.28.0
+      '@babel/types': 7.28.0
+    transitivePeerDependencies:
+      - supports-color
+
+  '@babel/helper-module-imports@7.27.1':
+    dependencies:
+      '@babel/traverse': 7.28.0
+      '@babel/types': 7.28.0
+    transitivePeerDependencies:
+      - supports-color
+
+  '@babel/helper-module-transforms@7.27.3(@babel/core@7.28.0)':
+    dependencies:
+      '@babel/core': 7.28.0
+      '@babel/helper-module-imports': 7.27.1
+      '@babel/helper-validator-identifier': 7.27.1
+      '@babel/traverse': 7.28.0
+    transitivePeerDependencies:
+      - supports-color
+
+  '@babel/helper-optimise-call-expression@7.27.1':
+    dependencies:
+      '@babel/types': 7.28.0
+
+  '@babel/helper-plugin-utils@7.27.1': {}
+
+  '@babel/helper-replace-supers@7.27.1(@babel/core@7.28.0)':
+    dependencies:
+      '@babel/core': 7.28.0
+      '@babel/helper-member-expression-to-functions': 7.27.1
+      '@babel/helper-optimise-call-expression': 7.27.1
+      '@babel/traverse': 7.28.0
+    transitivePeerDependencies:
+      - supports-color
+
+  '@babel/helper-skip-transparent-expression-wrappers@7.27.1':
+    dependencies:
+      '@babel/traverse': 7.28.0
+      '@babel/types': 7.28.0
+    transitivePeerDependencies:
+      - supports-color
+
+  '@babel/helper-string-parser@7.27.1': {}
+
+  '@babel/helper-validator-identifier@7.27.1': {}
+
+  '@babel/helper-validator-option@7.27.1': {}
+
+  '@babel/helpers@7.27.6':
+    dependencies:
+      '@babel/template': 7.27.2
+      '@babel/types': 7.28.0
+
+  '@babel/parser@7.28.0':
+    dependencies:
+      '@babel/types': 7.28.0
+
+  '@babel/plugin-proposal-decorators@7.28.0(@babel/core@7.28.0)':
+    dependencies:
+      '@babel/core': 7.28.0
+      '@babel/helper-create-class-features-plugin': 7.27.1(@babel/core@7.28.0)
+      '@babel/helper-plugin-utils': 7.27.1
+      '@babel/plugin-syntax-decorators': 7.27.1(@babel/core@7.28.0)
+    transitivePeerDependencies:
+      - supports-color
+
+  '@babel/plugin-syntax-decorators@7.27.1(@babel/core@7.28.0)':
+    dependencies:
+      '@babel/core': 7.28.0
+      '@babel/helper-plugin-utils': 7.27.1
+
+  '@babel/plugin-syntax-import-attributes@7.27.1(@babel/core@7.28.0)':
+    dependencies:
+      '@babel/core': 7.28.0
+      '@babel/helper-plugin-utils': 7.27.1
+
+  '@babel/plugin-syntax-import-meta@7.10.4(@babel/core@7.28.0)':
+    dependencies:
+      '@babel/core': 7.28.0
+      '@babel/helper-plugin-utils': 7.27.1
+
+  '@babel/plugin-syntax-jsx@7.27.1(@babel/core@7.28.0)':
+    dependencies:
+      '@babel/core': 7.28.0
+      '@babel/helper-plugin-utils': 7.27.1
+
+  '@babel/plugin-syntax-typescript@7.27.1(@babel/core@7.28.0)':
+    dependencies:
+      '@babel/core': 7.28.0
+      '@babel/helper-plugin-utils': 7.27.1
+
+  '@babel/plugin-transform-typescript@7.28.0(@babel/core@7.28.0)':
+    dependencies:
+      '@babel/core': 7.28.0
+      '@babel/helper-annotate-as-pure': 7.27.3
+      '@babel/helper-create-class-features-plugin': 7.27.1(@babel/core@7.28.0)
+      '@babel/helper-plugin-utils': 7.27.1
+      '@babel/helper-skip-transparent-expression-wrappers': 7.27.1
+      '@babel/plugin-syntax-typescript': 7.27.1(@babel/core@7.28.0)
+    transitivePeerDependencies:
+      - supports-color
+
+  '@babel/runtime@7.27.6': {}
+
+  '@babel/template@7.27.2':
+    dependencies:
+      '@babel/code-frame': 7.27.1
+      '@babel/parser': 7.28.0
+      '@babel/types': 7.28.0
+
+  '@babel/traverse@7.28.0':
+    dependencies:
+      '@babel/code-frame': 7.27.1
+      '@babel/generator': 7.28.0
+      '@babel/helper-globals': 7.28.0
+      '@babel/parser': 7.28.0
+      '@babel/template': 7.27.2
+      '@babel/types': 7.28.0
+      debug: 4.4.1
+    transitivePeerDependencies:
+      - supports-color
+
+  '@babel/types@7.28.0':
+    dependencies:
+      '@babel/helper-string-parser': 7.27.1
+      '@babel/helper-validator-identifier': 7.27.1
+
+  '@ctrl/tinycolor@3.6.1': {}
+
+  '@emotion/hash@0.9.2': {}
+
+  '@emotion/unitless@0.8.1': {}
+
+  '@esbuild/aix-ppc64@0.25.5':
+    optional: true
+
+  '@esbuild/android-arm64@0.25.5':
+    optional: true
+
+  '@esbuild/android-arm@0.25.5':
+    optional: true
+
+  '@esbuild/android-x64@0.25.5':
+    optional: true
+
+  '@esbuild/darwin-arm64@0.25.5':
+    optional: true
+
+  '@esbuild/darwin-x64@0.25.5':
+    optional: true
+
+  '@esbuild/freebsd-arm64@0.25.5':
+    optional: true
+
+  '@esbuild/freebsd-x64@0.25.5':
+    optional: true
+
+  '@esbuild/linux-arm64@0.25.5':
+    optional: true
+
+  '@esbuild/linux-arm@0.25.5':
+    optional: true
+
+  '@esbuild/linux-ia32@0.25.5':
+    optional: true
+
+  '@esbuild/linux-loong64@0.25.5':
+    optional: true
+
+  '@esbuild/linux-mips64el@0.25.5':
+    optional: true
+
+  '@esbuild/linux-ppc64@0.25.5':
+    optional: true
+
+  '@esbuild/linux-riscv64@0.25.5':
+    optional: true
+
+  '@esbuild/linux-s390x@0.25.5':
+    optional: true
+
+  '@esbuild/linux-x64@0.25.5':
+    optional: true
+
+  '@esbuild/netbsd-arm64@0.25.5':
+    optional: true
+
+  '@esbuild/netbsd-x64@0.25.5':
+    optional: true
+
+  '@esbuild/openbsd-arm64@0.25.5':
+    optional: true
+
+  '@esbuild/openbsd-x64@0.25.5':
+    optional: true
+
+  '@esbuild/sunos-x64@0.25.5':
+    optional: true
+
+  '@esbuild/win32-arm64@0.25.5':
+    optional: true
+
+  '@esbuild/win32-ia32@0.25.5':
+    optional: true
+
+  '@esbuild/win32-x64@0.25.5':
+    optional: true
+
+  '@eslint-community/eslint-utils@4.7.0(eslint@9.30.1(jiti@2.4.2))':
+    dependencies:
+      eslint: 9.30.1(jiti@2.4.2)
+      eslint-visitor-keys: 3.4.3
+
+  '@eslint-community/regexpp@4.12.1': {}
+
+  '@eslint/config-array@0.21.0':
+    dependencies:
+      '@eslint/object-schema': 2.1.6
+      debug: 4.4.1
+      minimatch: 3.1.2
+    transitivePeerDependencies:
+      - supports-color
+
+  '@eslint/config-helpers@0.3.0': {}
+
+  '@eslint/core@0.14.0':
+    dependencies:
+      '@types/json-schema': 7.0.15
+
+  '@eslint/core@0.15.1':
+    dependencies:
+      '@types/json-schema': 7.0.15
+
+  '@eslint/eslintrc@3.3.1':
+    dependencies:
+      ajv: 6.12.6
+      debug: 4.4.1
+      espree: 10.4.0
+      globals: 14.0.0
+      ignore: 5.3.2
+      import-fresh: 3.3.1
+      js-yaml: 4.1.0
+      minimatch: 3.1.2
+      strip-json-comments: 3.1.1
+    transitivePeerDependencies:
+      - supports-color
+
+  '@eslint/js@9.30.1': {}
+
+  '@eslint/object-schema@2.1.6': {}
+
+  '@eslint/plugin-kit@0.3.3':
+    dependencies:
+      '@eslint/core': 0.15.1
+      levn: 0.4.1
+
+  '@humanfs/core@0.19.1': {}
+
+  '@humanfs/node@0.16.6':
+    dependencies:
+      '@humanfs/core': 0.19.1
+      '@humanwhocodes/retry': 0.3.1
+
+  '@humanwhocodes/module-importer@1.0.1': {}
+
+  '@humanwhocodes/retry@0.3.1': {}
+
+  '@humanwhocodes/retry@0.4.3': {}
+
+  '@jridgewell/gen-mapping@0.3.12':
+    dependencies:
+      '@jridgewell/sourcemap-codec': 1.5.4
+      '@jridgewell/trace-mapping': 0.3.29
+
+  '@jridgewell/resolve-uri@3.1.2': {}
+
+  '@jridgewell/sourcemap-codec@1.5.4': {}
+
+  '@jridgewell/trace-mapping@0.3.29':
+    dependencies:
+      '@jridgewell/resolve-uri': 3.1.2
+      '@jridgewell/sourcemap-codec': 1.5.4
+
+  '@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.19.1
+
+  '@pkgr/core@0.2.7': {}
+
+  '@polka/url@1.0.0-next.29': {}
+
+  '@rolldown/pluginutils@1.0.0-beta.19': {}
+
+  '@rollup/pluginutils@5.2.0(rollup@4.44.2)':
+    dependencies:
+      '@types/estree': 1.0.8
+      estree-walker: 2.0.2
+      picomatch: 4.0.2
+    optionalDependencies:
+      rollup: 4.44.2
+
+  '@rollup/rollup-android-arm-eabi@4.44.2':
+    optional: true
+
+  '@rollup/rollup-android-arm64@4.44.2':
+    optional: true
+
+  '@rollup/rollup-darwin-arm64@4.44.2':
+    optional: true
+
+  '@rollup/rollup-darwin-x64@4.44.2':
+    optional: true
+
+  '@rollup/rollup-freebsd-arm64@4.44.2':
+    optional: true
+
+  '@rollup/rollup-freebsd-x64@4.44.2':
+    optional: true
+
+  '@rollup/rollup-linux-arm-gnueabihf@4.44.2':
+    optional: true
+
+  '@rollup/rollup-linux-arm-musleabihf@4.44.2':
+    optional: true
+
+  '@rollup/rollup-linux-arm64-gnu@4.44.2':
+    optional: true
+
+  '@rollup/rollup-linux-arm64-musl@4.44.2':
+    optional: true
+
+  '@rollup/rollup-linux-loongarch64-gnu@4.44.2':
+    optional: true
+
+  '@rollup/rollup-linux-powerpc64le-gnu@4.44.2':
+    optional: true
+
+  '@rollup/rollup-linux-riscv64-gnu@4.44.2':
+    optional: true
+
+  '@rollup/rollup-linux-riscv64-musl@4.44.2':
+    optional: true
+
+  '@rollup/rollup-linux-s390x-gnu@4.44.2':
+    optional: true
+
+  '@rollup/rollup-linux-x64-gnu@4.44.2':
+    optional: true
+
+  '@rollup/rollup-linux-x64-musl@4.44.2':
+    optional: true
+
+  '@rollup/rollup-win32-arm64-msvc@4.44.2':
+    optional: true
+
+  '@rollup/rollup-win32-ia32-msvc@4.44.2':
+    optional: true
+
+  '@rollup/rollup-win32-x64-msvc@4.44.2':
+    optional: true
+
+  '@rtsao/scc@1.1.0': {}
+
+  '@sec-ant/readable-stream@0.4.1': {}
+
+  '@simonwep/pickr@1.8.2':
+    dependencies:
+      core-js: 3.43.0
+      nanopop: 2.4.2
+
+  '@sindresorhus/merge-streams@4.0.0': {}
+
+  '@tsconfig/node22@22.0.2': {}
+
+  '@types/echarts@5.0.0':
+    dependencies:
+      echarts: 5.6.0
+
+  '@types/estree@1.0.8': {}
+
+  '@types/json-schema@7.0.15': {}
+
+  '@types/json5@0.0.29': {}
+
+  '@types/lodash-es@4.17.12':
+    dependencies:
+      '@types/lodash': 4.17.20
+
+  '@types/lodash@4.17.20': {}
+
+  '@types/mqtt@2.5.0':
+    dependencies:
+      mqtt: 5.13.1
+    transitivePeerDependencies:
+      - bufferutil
+      - supports-color
+      - utf-8-validate
+
+  '@types/node@22.16.0':
+    dependencies:
+      undici-types: 6.21.0
+
+  '@types/readable-stream@4.0.21':
+    dependencies:
+      '@types/node': 22.16.0
+
+  '@types/web-bluetooth@0.0.21': {}
+
+  '@typescript-eslint/eslint-plugin@8.35.1(@typescript-eslint/parser@8.35.1(eslint@9.30.1(jiti@2.4.2))(typescript@5.8.3))(eslint@9.30.1(jiti@2.4.2))(typescript@5.8.3)':
+    dependencies:
+      '@eslint-community/regexpp': 4.12.1
+      '@typescript-eslint/parser': 8.35.1(eslint@9.30.1(jiti@2.4.2))(typescript@5.8.3)
+      '@typescript-eslint/scope-manager': 8.35.1
+      '@typescript-eslint/type-utils': 8.35.1(eslint@9.30.1(jiti@2.4.2))(typescript@5.8.3)
+      '@typescript-eslint/utils': 8.35.1(eslint@9.30.1(jiti@2.4.2))(typescript@5.8.3)
+      '@typescript-eslint/visitor-keys': 8.35.1
+      eslint: 9.30.1(jiti@2.4.2)
+      graphemer: 1.4.0
+      ignore: 7.0.5
+      natural-compare: 1.4.0
+      ts-api-utils: 2.1.0(typescript@5.8.3)
+      typescript: 5.8.3
+    transitivePeerDependencies:
+      - supports-color
+
+  '@typescript-eslint/parser@8.35.1(eslint@9.30.1(jiti@2.4.2))(typescript@5.8.3)':
+    dependencies:
+      '@typescript-eslint/scope-manager': 8.35.1
+      '@typescript-eslint/types': 8.35.1
+      '@typescript-eslint/typescript-estree': 8.35.1(typescript@5.8.3)
+      '@typescript-eslint/visitor-keys': 8.35.1
+      debug: 4.4.1
+      eslint: 9.30.1(jiti@2.4.2)
+      typescript: 5.8.3
+    transitivePeerDependencies:
+      - supports-color
+
+  '@typescript-eslint/project-service@8.35.1(typescript@5.8.3)':
+    dependencies:
+      '@typescript-eslint/tsconfig-utils': 8.35.1(typescript@5.8.3)
+      '@typescript-eslint/types': 8.35.1
+      debug: 4.4.1
+      typescript: 5.8.3
+    transitivePeerDependencies:
+      - supports-color
+
+  '@typescript-eslint/scope-manager@8.35.1':
+    dependencies:
+      '@typescript-eslint/types': 8.35.1
+      '@typescript-eslint/visitor-keys': 8.35.1
+
+  '@typescript-eslint/tsconfig-utils@8.35.1(typescript@5.8.3)':
+    dependencies:
+      typescript: 5.8.3
+
+  '@typescript-eslint/type-utils@8.35.1(eslint@9.30.1(jiti@2.4.2))(typescript@5.8.3)':
+    dependencies:
+      '@typescript-eslint/typescript-estree': 8.35.1(typescript@5.8.3)
+      '@typescript-eslint/utils': 8.35.1(eslint@9.30.1(jiti@2.4.2))(typescript@5.8.3)
+      debug: 4.4.1
+      eslint: 9.30.1(jiti@2.4.2)
+      ts-api-utils: 2.1.0(typescript@5.8.3)
+      typescript: 5.8.3
+    transitivePeerDependencies:
+      - supports-color
+
+  '@typescript-eslint/types@8.35.1': {}
+
+  '@typescript-eslint/typescript-estree@8.35.1(typescript@5.8.3)':
+    dependencies:
+      '@typescript-eslint/project-service': 8.35.1(typescript@5.8.3)
+      '@typescript-eslint/tsconfig-utils': 8.35.1(typescript@5.8.3)
+      '@typescript-eslint/types': 8.35.1
+      '@typescript-eslint/visitor-keys': 8.35.1
+      debug: 4.4.1
+      fast-glob: 3.3.3
+      is-glob: 4.0.3
+      minimatch: 9.0.5
+      semver: 7.7.2
+      ts-api-utils: 2.1.0(typescript@5.8.3)
+      typescript: 5.8.3
+    transitivePeerDependencies:
+      - supports-color
+
+  '@typescript-eslint/utils@8.35.1(eslint@9.30.1(jiti@2.4.2))(typescript@5.8.3)':
+    dependencies:
+      '@eslint-community/eslint-utils': 4.7.0(eslint@9.30.1(jiti@2.4.2))
+      '@typescript-eslint/scope-manager': 8.35.1
+      '@typescript-eslint/types': 8.35.1
+      '@typescript-eslint/typescript-estree': 8.35.1(typescript@5.8.3)
+      eslint: 9.30.1(jiti@2.4.2)
+      typescript: 5.8.3
+    transitivePeerDependencies:
+      - supports-color
+
+  '@typescript-eslint/visitor-keys@8.35.1':
+    dependencies:
+      '@typescript-eslint/types': 8.35.1
+      eslint-visitor-keys: 4.2.1
+
+  '@vitejs/plugin-vue@6.0.0(vite@7.0.2(@types/node@22.16.0)(jiti@2.4.2)(less@4.3.0))(vue@3.5.17(typescript@5.8.3))':
+    dependencies:
+      '@rolldown/pluginutils': 1.0.0-beta.19
+      vite: 7.0.2(@types/node@22.16.0)(jiti@2.4.2)(less@4.3.0)
+      vue: 3.5.17(typescript@5.8.3)
+
+  '@volar/language-core@2.4.15':
+    dependencies:
+      '@volar/source-map': 2.4.15
+
+  '@volar/source-map@2.4.15': {}
+
+  '@volar/typescript@2.4.15':
+    dependencies:
+      '@volar/language-core': 2.4.15
+      path-browserify: 1.0.1
+      vscode-uri: 3.1.0
+
+  '@vue/babel-helper-vue-transform-on@1.4.0': {}
+
+  '@vue/babel-plugin-jsx@1.4.0(@babel/core@7.28.0)':
+    dependencies:
+      '@babel/helper-module-imports': 7.27.1
+      '@babel/helper-plugin-utils': 7.27.1
+      '@babel/plugin-syntax-jsx': 7.27.1(@babel/core@7.28.0)
+      '@babel/template': 7.27.2
+      '@babel/traverse': 7.28.0
+      '@babel/types': 7.28.0
+      '@vue/babel-helper-vue-transform-on': 1.4.0
+      '@vue/babel-plugin-resolve-type': 1.4.0(@babel/core@7.28.0)
+      '@vue/shared': 3.5.17
+    optionalDependencies:
+      '@babel/core': 7.28.0
+    transitivePeerDependencies:
+      - supports-color
+
+  '@vue/babel-plugin-resolve-type@1.4.0(@babel/core@7.28.0)':
+    dependencies:
+      '@babel/code-frame': 7.27.1
+      '@babel/core': 7.28.0
+      '@babel/helper-module-imports': 7.27.1
+      '@babel/helper-plugin-utils': 7.27.1
+      '@babel/parser': 7.28.0
+      '@vue/compiler-sfc': 3.5.17
+    transitivePeerDependencies:
+      - supports-color
+
+  '@vue/compiler-core@3.5.17':
+    dependencies:
+      '@babel/parser': 7.28.0
+      '@vue/shared': 3.5.17
+      entities: 4.5.0
+      estree-walker: 2.0.2
+      source-map-js: 1.2.1
+
+  '@vue/compiler-dom@3.5.17':
+    dependencies:
+      '@vue/compiler-core': 3.5.17
+      '@vue/shared': 3.5.17
+
+  '@vue/compiler-sfc@3.5.17':
+    dependencies:
+      '@babel/parser': 7.28.0
+      '@vue/compiler-core': 3.5.17
+      '@vue/compiler-dom': 3.5.17
+      '@vue/compiler-ssr': 3.5.17
+      '@vue/shared': 3.5.17
+      estree-walker: 2.0.2
+      magic-string: 0.30.17
+      postcss: 8.5.6
+      source-map-js: 1.2.1
+
+  '@vue/compiler-ssr@3.5.17':
+    dependencies:
+      '@vue/compiler-dom': 3.5.17
+      '@vue/shared': 3.5.17
+
+  '@vue/compiler-vue2@2.7.16':
+    dependencies:
+      de-indent: 1.0.2
+      he: 1.2.0
+
+  '@vue/devtools-api@6.6.4': {}
+
+  '@vue/devtools-api@7.7.7':
+    dependencies:
+      '@vue/devtools-kit': 7.7.7
+
+  '@vue/devtools-core@7.7.7(vite@7.0.2(@types/node@22.16.0)(jiti@2.4.2)(less@4.3.0))(vue@3.5.17(typescript@5.8.3))':
+    dependencies:
+      '@vue/devtools-kit': 7.7.7
+      '@vue/devtools-shared': 7.7.7
+      mitt: 3.0.1
+      nanoid: 5.1.5
+      pathe: 2.0.3
+      vite-hot-client: 2.1.0(vite@7.0.2(@types/node@22.16.0)(jiti@2.4.2)(less@4.3.0))
+      vue: 3.5.17(typescript@5.8.3)
+    transitivePeerDependencies:
+      - vite
+
+  '@vue/devtools-kit@7.7.7':
+    dependencies:
+      '@vue/devtools-shared': 7.7.7
+      birpc: 2.4.0
+      hookable: 5.5.3
+      mitt: 3.0.1
+      perfect-debounce: 1.0.0
+      speakingurl: 14.0.1
+      superjson: 2.2.2
+
+  '@vue/devtools-shared@7.7.7':
+    dependencies:
+      rfdc: 1.4.1
+
+  '@vue/eslint-config-prettier@10.2.0(eslint@9.30.1(jiti@2.4.2))(prettier@3.5.3)':
+    dependencies:
+      eslint: 9.30.1(jiti@2.4.2)
+      eslint-config-prettier: 10.1.5(eslint@9.30.1(jiti@2.4.2))
+      eslint-plugin-prettier: 5.5.1(eslint-config-prettier@10.1.5(eslint@9.30.1(jiti@2.4.2)))(eslint@9.30.1(jiti@2.4.2))(prettier@3.5.3)
+      prettier: 3.5.3
+    transitivePeerDependencies:
+      - '@types/eslint'
+
+  '@vue/eslint-config-typescript@14.6.0(eslint-plugin-vue@10.2.0(eslint@9.30.1(jiti@2.4.2))(vue-eslint-parser@10.2.0(eslint@9.30.1(jiti@2.4.2))))(eslint@9.30.1(jiti@2.4.2))(typescript@5.8.3)':
+    dependencies:
+      '@typescript-eslint/utils': 8.35.1(eslint@9.30.1(jiti@2.4.2))(typescript@5.8.3)
+      eslint: 9.30.1(jiti@2.4.2)
+      eslint-plugin-vue: 10.2.0(eslint@9.30.1(jiti@2.4.2))(vue-eslint-parser@10.2.0(eslint@9.30.1(jiti@2.4.2)))
+      fast-glob: 3.3.3
+      typescript-eslint: 8.35.1(eslint@9.30.1(jiti@2.4.2))(typescript@5.8.3)
+      vue-eslint-parser: 10.2.0(eslint@9.30.1(jiti@2.4.2))
+    optionalDependencies:
+      typescript: 5.8.3
+    transitivePeerDependencies:
+      - supports-color
+
+  '@vue/language-core@2.2.12(typescript@5.8.3)':
+    dependencies:
+      '@volar/language-core': 2.4.15
+      '@vue/compiler-dom': 3.5.17
+      '@vue/compiler-vue2': 2.7.16
+      '@vue/shared': 3.5.17
+      alien-signals: 1.0.13
+      minimatch: 9.0.5
+      muggle-string: 0.4.1
+      path-browserify: 1.0.1
+    optionalDependencies:
+      typescript: 5.8.3
+
+  '@vue/reactivity@3.5.17':
+    dependencies:
+      '@vue/shared': 3.5.17
+
+  '@vue/runtime-core@3.5.17':
+    dependencies:
+      '@vue/reactivity': 3.5.17
+      '@vue/shared': 3.5.17
+
+  '@vue/runtime-dom@3.5.17':
+    dependencies:
+      '@vue/reactivity': 3.5.17
+      '@vue/runtime-core': 3.5.17
+      '@vue/shared': 3.5.17
+      csstype: 3.1.3
+
+  '@vue/server-renderer@3.5.17(vue@3.5.17(typescript@5.8.3))':
+    dependencies:
+      '@vue/compiler-ssr': 3.5.17
+      '@vue/shared': 3.5.17
+      vue: 3.5.17(typescript@5.8.3)
+
+  '@vue/shared@3.5.17': {}
+
+  '@vue/tsconfig@0.7.0(typescript@5.8.3)(vue@3.5.17(typescript@5.8.3))':
+    optionalDependencies:
+      typescript: 5.8.3
+      vue: 3.5.17(typescript@5.8.3)
+
+  '@vueuse/components@13.5.0(vue@3.5.17(typescript@5.8.3))':
+    dependencies:
+      '@vueuse/core': 13.5.0(vue@3.5.17(typescript@5.8.3))
+      '@vueuse/shared': 13.5.0(vue@3.5.17(typescript@5.8.3))
+      vue: 3.5.17(typescript@5.8.3)
+
+  '@vueuse/core@13.5.0(vue@3.5.17(typescript@5.8.3))':
+    dependencies:
+      '@types/web-bluetooth': 0.0.21
+      '@vueuse/metadata': 13.5.0
+      '@vueuse/shared': 13.5.0(vue@3.5.17(typescript@5.8.3))
+      vue: 3.5.17(typescript@5.8.3)
+
+  '@vueuse/metadata@13.5.0': {}
+
+  '@vueuse/shared@13.5.0(vue@3.5.17(typescript@5.8.3))':
+    dependencies:
+      vue: 3.5.17(typescript@5.8.3)
+
+  abort-controller@3.0.0:
+    dependencies:
+      event-target-shim: 5.0.1
+
+  acorn-jsx@5.3.2(acorn@8.15.0):
+    dependencies:
+      acorn: 8.15.0
+
+  acorn@8.15.0: {}
+
+  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
+
+  alien-signals@1.0.13: {}
+
+  ansi-styles@4.3.0:
+    dependencies:
+      color-convert: 2.0.1
+
+  ansi-styles@6.2.1: {}
+
+  ant-design-vue@4.2.6(vue@3.5.17(typescript@5.8.3)):
+    dependencies:
+      '@ant-design/colors': 6.0.0
+      '@ant-design/icons-vue': 7.0.1(vue@3.5.17(typescript@5.8.3))
+      '@babel/runtime': 7.27.6
+      '@ctrl/tinycolor': 3.6.1
+      '@emotion/hash': 0.9.2
+      '@emotion/unitless': 0.8.1
+      '@simonwep/pickr': 1.8.2
+      array-tree-filter: 2.1.0
+      async-validator: 4.2.5
+      csstype: 3.1.3
+      dayjs: 1.11.13
+      dom-align: 1.12.4
+      dom-scroll-into-view: 2.0.1
+      lodash: 4.17.21
+      lodash-es: 4.17.21
+      resize-observer-polyfill: 1.5.1
+      scroll-into-view-if-needed: 2.2.31
+      shallow-equal: 1.2.1
+      stylis: 4.3.6
+      throttle-debounce: 5.0.2
+      vue: 3.5.17(typescript@5.8.3)
+      vue-types: 3.0.2(vue@3.5.17(typescript@5.8.3))
+      warning: 4.0.3
+
+  anymatch@3.1.3:
+    dependencies:
+      normalize-path: 3.0.0
+      picomatch: 2.3.1
+
+  argparse@2.0.1: {}
+
+  array-buffer-byte-length@1.0.2:
+    dependencies:
+      call-bound: 1.0.4
+      is-array-buffer: 3.0.5
+
+  array-includes@3.1.9:
+    dependencies:
+      call-bind: 1.0.8
+      call-bound: 1.0.4
+      define-properties: 1.2.1
+      es-abstract: 1.24.0
+      es-object-atoms: 1.1.1
+      get-intrinsic: 1.3.0
+      is-string: 1.1.1
+      math-intrinsics: 1.1.0
+
+  array-tree-filter@2.1.0: {}
+
+  array.prototype.findlastindex@1.2.6:
+    dependencies:
+      call-bind: 1.0.8
+      call-bound: 1.0.4
+      define-properties: 1.2.1
+      es-abstract: 1.24.0
+      es-errors: 1.3.0
+      es-object-atoms: 1.1.1
+      es-shim-unscopables: 1.1.0
+
+  array.prototype.flat@1.3.3:
+    dependencies:
+      call-bind: 1.0.8
+      define-properties: 1.2.1
+      es-abstract: 1.24.0
+      es-shim-unscopables: 1.1.0
+
+  array.prototype.flatmap@1.3.3:
+    dependencies:
+      call-bind: 1.0.8
+      define-properties: 1.2.1
+      es-abstract: 1.24.0
+      es-shim-unscopables: 1.1.0
+
+  arraybuffer.prototype.slice@1.0.4:
+    dependencies:
+      array-buffer-byte-length: 1.0.2
+      call-bind: 1.0.8
+      define-properties: 1.2.1
+      es-abstract: 1.24.0
+      es-errors: 1.3.0
+      get-intrinsic: 1.3.0
+      is-array-buffer: 3.0.5
+
+  async-function@1.0.0: {}
+
+  async-validator@4.2.5: {}
+
+  asynckit@0.4.0: {}
+
+  available-typed-arrays@1.0.7:
+    dependencies:
+      possible-typed-array-names: 1.1.0
+
+  axios@1.10.0:
+    dependencies:
+      follow-redirects: 1.15.9
+      form-data: 4.0.3
+      proxy-from-env: 1.1.0
+    transitivePeerDependencies:
+      - debug
+
+  balanced-match@1.0.2: {}
+
+  base64-js@1.5.1: {}
+
+  binary-extensions@2.3.0: {}
+
+  birpc@2.4.0: {}
+
+  bl@6.1.0:
+    dependencies:
+      '@types/readable-stream': 4.0.21
+      buffer: 6.0.3
+      inherits: 2.0.4
+      readable-stream: 4.7.0
+
+  boolbase@1.0.0: {}
+
+  brace-expansion@1.1.12:
+    dependencies:
+      balanced-match: 1.0.2
+      concat-map: 0.0.1
+
+  brace-expansion@2.0.2:
+    dependencies:
+      balanced-match: 1.0.2
+
+  braces@3.0.3:
+    dependencies:
+      fill-range: 7.1.1
+
+  browserslist@4.25.1:
+    dependencies:
+      caniuse-lite: 1.0.30001727
+      electron-to-chromium: 1.5.179
+      node-releases: 2.0.19
+      update-browserslist-db: 1.1.3(browserslist@4.25.1)
+
+  buffer-from@1.1.2: {}
+
+  buffer@6.0.3:
+    dependencies:
+      base64-js: 1.5.1
+      ieee754: 1.2.1
+
+  bundle-name@4.1.0:
+    dependencies:
+      run-applescript: 7.0.0
+
+  call-bind-apply-helpers@1.0.2:
+    dependencies:
+      es-errors: 1.3.0
+      function-bind: 1.1.2
+
+  call-bind@1.0.8:
+    dependencies:
+      call-bind-apply-helpers: 1.0.2
+      es-define-property: 1.0.1
+      get-intrinsic: 1.3.0
+      set-function-length: 1.2.2
+
+  call-bound@1.0.4:
+    dependencies:
+      call-bind-apply-helpers: 1.0.2
+      get-intrinsic: 1.3.0
+
+  callsites@3.1.0: {}
+
+  caniuse-lite@1.0.30001727: {}
+
+  chalk@4.1.2:
+    dependencies:
+      ansi-styles: 4.3.0
+      supports-color: 7.2.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
+
+  color-convert@2.0.1:
+    dependencies:
+      color-name: 1.1.4
+
+  color-name@1.1.4: {}
+
+  combined-stream@1.0.8:
+    dependencies:
+      delayed-stream: 1.0.0
+
+  commist@3.2.0: {}
+
+  compute-scroll-into-view@1.0.20: {}
+
+  concat-map@0.0.1: {}
+
+  concat-stream@2.0.0:
+    dependencies:
+      buffer-from: 1.1.2
+      inherits: 2.0.4
+      readable-stream: 3.6.2
+      typedarray: 0.0.6
+
+  confbox@0.1.8: {}
+
+  confbox@0.2.2: {}
+
+  convert-source-map@2.0.0: {}
+
+  copy-anything@2.0.6:
+    dependencies:
+      is-what: 3.14.1
+
+  copy-anything@3.0.5:
+    dependencies:
+      is-what: 4.1.16
+
+  core-js@3.43.0: {}
+
+  cross-spawn@7.0.6:
+    dependencies:
+      path-key: 3.1.1
+      shebang-command: 2.0.0
+      which: 2.0.2
+
+  cssesc@3.0.0: {}
+
+  csstype@3.1.3: {}
+
+  data-view-buffer@1.0.2:
+    dependencies:
+      call-bound: 1.0.4
+      es-errors: 1.3.0
+      is-data-view: 1.0.2
+
+  data-view-byte-length@1.0.2:
+    dependencies:
+      call-bound: 1.0.4
+      es-errors: 1.3.0
+      is-data-view: 1.0.2
+
+  data-view-byte-offset@1.0.1:
+    dependencies:
+      call-bound: 1.0.4
+      es-errors: 1.3.0
+      is-data-view: 1.0.2
+
+  dayjs@1.11.13: {}
+
+  de-indent@1.0.2: {}
+
+  debug@3.2.7:
+    dependencies:
+      ms: 2.1.3
+
+  debug@4.4.1:
+    dependencies:
+      ms: 2.1.3
+
+  deep-is@0.1.4: {}
+
+  deep-pick-omit@1.2.1: {}
+
+  default-browser-id@5.0.0: {}
+
+  default-browser@5.2.1:
+    dependencies:
+      bundle-name: 4.1.0
+      default-browser-id: 5.0.0
+
+  define-data-property@1.1.4:
+    dependencies:
+      es-define-property: 1.0.1
+      es-errors: 1.3.0
+      gopd: 1.2.0
+
+  define-lazy-prop@3.0.0: {}
+
+  define-properties@1.2.1:
+    dependencies:
+      define-data-property: 1.1.4
+      has-property-descriptors: 1.0.2
+      object-keys: 1.1.1
+
+  defu@6.1.4: {}
+
+  delayed-stream@1.0.0: {}
+
+  destr@2.0.5: {}
+
+  doctrine@2.1.0:
+    dependencies:
+      esutils: 2.0.3
+
+  dom-align@1.12.4: {}
+
+  dom-scroll-into-view@2.0.1: {}
+
+  dunder-proto@1.0.1:
+    dependencies:
+      call-bind-apply-helpers: 1.0.2
+      es-errors: 1.3.0
+      gopd: 1.2.0
+
+  echarts@5.6.0:
+    dependencies:
+      tslib: 2.3.0
+      zrender: 5.6.1
+
+  electron-to-chromium@1.5.179: {}
+
+  entities@4.5.0: {}
+
+  errno@0.1.8:
+    dependencies:
+      prr: 1.0.1
+    optional: true
+
+  error-stack-parser-es@0.1.5: {}
+
+  es-abstract@1.24.0:
+    dependencies:
+      array-buffer-byte-length: 1.0.2
+      arraybuffer.prototype.slice: 1.0.4
+      available-typed-arrays: 1.0.7
+      call-bind: 1.0.8
+      call-bound: 1.0.4
+      data-view-buffer: 1.0.2
+      data-view-byte-length: 1.0.2
+      data-view-byte-offset: 1.0.1
+      es-define-property: 1.0.1
+      es-errors: 1.3.0
+      es-object-atoms: 1.1.1
+      es-set-tostringtag: 2.1.0
+      es-to-primitive: 1.3.0
+      function.prototype.name: 1.1.8
+      get-intrinsic: 1.3.0
+      get-proto: 1.0.1
+      get-symbol-description: 1.1.0
+      globalthis: 1.0.4
+      gopd: 1.2.0
+      has-property-descriptors: 1.0.2
+      has-proto: 1.2.0
+      has-symbols: 1.1.0
+      hasown: 2.0.2
+      internal-slot: 1.1.0
+      is-array-buffer: 3.0.5
+      is-callable: 1.2.7
+      is-data-view: 1.0.2
+      is-negative-zero: 2.0.3
+      is-regex: 1.2.1
+      is-set: 2.0.3
+      is-shared-array-buffer: 1.0.4
+      is-string: 1.1.1
+      is-typed-array: 1.1.15
+      is-weakref: 1.1.1
+      math-intrinsics: 1.1.0
+      object-inspect: 1.13.4
+      object-keys: 1.1.1
+      object.assign: 4.1.7
+      own-keys: 1.0.1
+      regexp.prototype.flags: 1.5.4
+      safe-array-concat: 1.1.3
+      safe-push-apply: 1.0.0
+      safe-regex-test: 1.1.0
+      set-proto: 1.0.0
+      stop-iteration-iterator: 1.1.0
+      string.prototype.trim: 1.2.10
+      string.prototype.trimend: 1.0.9
+      string.prototype.trimstart: 1.0.8
+      typed-array-buffer: 1.0.3
+      typed-array-byte-length: 1.0.3
+      typed-array-byte-offset: 1.0.4
+      typed-array-length: 1.0.7
+      unbox-primitive: 1.1.0
+      which-typed-array: 1.1.19
+
+  es-define-property@1.0.1: {}
+
+  es-errors@1.3.0: {}
+
+  es-object-atoms@1.1.1:
+    dependencies:
+      es-errors: 1.3.0
+
+  es-set-tostringtag@2.1.0:
+    dependencies:
+      es-errors: 1.3.0
+      get-intrinsic: 1.3.0
+      has-tostringtag: 1.0.2
+      hasown: 2.0.2
+
+  es-shim-unscopables@1.1.0:
+    dependencies:
+      hasown: 2.0.2
+
+  es-to-primitive@1.3.0:
+    dependencies:
+      is-callable: 1.2.7
+      is-date-object: 1.1.0
+      is-symbol: 1.1.1
+
+  esbuild@0.25.5:
+    optionalDependencies:
+      '@esbuild/aix-ppc64': 0.25.5
+      '@esbuild/android-arm': 0.25.5
+      '@esbuild/android-arm64': 0.25.5
+      '@esbuild/android-x64': 0.25.5
+      '@esbuild/darwin-arm64': 0.25.5
+      '@esbuild/darwin-x64': 0.25.5
+      '@esbuild/freebsd-arm64': 0.25.5
+      '@esbuild/freebsd-x64': 0.25.5
+      '@esbuild/linux-arm': 0.25.5
+      '@esbuild/linux-arm64': 0.25.5
+      '@esbuild/linux-ia32': 0.25.5
+      '@esbuild/linux-loong64': 0.25.5
+      '@esbuild/linux-mips64el': 0.25.5
+      '@esbuild/linux-ppc64': 0.25.5
+      '@esbuild/linux-riscv64': 0.25.5
+      '@esbuild/linux-s390x': 0.25.5
+      '@esbuild/linux-x64': 0.25.5
+      '@esbuild/netbsd-arm64': 0.25.5
+      '@esbuild/netbsd-x64': 0.25.5
+      '@esbuild/openbsd-arm64': 0.25.5
+      '@esbuild/openbsd-x64': 0.25.5
+      '@esbuild/sunos-x64': 0.25.5
+      '@esbuild/win32-arm64': 0.25.5
+      '@esbuild/win32-ia32': 0.25.5
+      '@esbuild/win32-x64': 0.25.5
+
+  escalade@3.2.0: {}
+
+  escape-string-regexp@4.0.0: {}
+
+  eslint-config-prettier@10.1.5(eslint@9.30.1(jiti@2.4.2)):
+    dependencies:
+      eslint: 9.30.1(jiti@2.4.2)
+
+  eslint-import-resolver-alias@1.1.2(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.35.1(eslint@9.30.1(jiti@2.4.2))(typescript@5.8.3))(eslint@9.30.1(jiti@2.4.2))):
+    dependencies:
+      eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.35.1(eslint@9.30.1(jiti@2.4.2))(typescript@5.8.3))(eslint@9.30.1(jiti@2.4.2))
+
+  eslint-import-resolver-node@0.3.9:
+    dependencies:
+      debug: 3.2.7
+      is-core-module: 2.16.1
+      resolve: 1.22.10
+    transitivePeerDependencies:
+      - supports-color
+
+  eslint-module-utils@2.12.1(@typescript-eslint/parser@8.35.1(eslint@9.30.1(jiti@2.4.2))(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint@9.30.1(jiti@2.4.2)):
+    dependencies:
+      debug: 3.2.7
+    optionalDependencies:
+      '@typescript-eslint/parser': 8.35.1(eslint@9.30.1(jiti@2.4.2))(typescript@5.8.3)
+      eslint: 9.30.1(jiti@2.4.2)
+      eslint-import-resolver-node: 0.3.9
+    transitivePeerDependencies:
+      - supports-color
+
+  eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.35.1(eslint@9.30.1(jiti@2.4.2))(typescript@5.8.3))(eslint@9.30.1(jiti@2.4.2)):
+    dependencies:
+      '@rtsao/scc': 1.1.0
+      array-includes: 3.1.9
+      array.prototype.findlastindex: 1.2.6
+      array.prototype.flat: 1.3.3
+      array.prototype.flatmap: 1.3.3
+      debug: 3.2.7
+      doctrine: 2.1.0
+      eslint: 9.30.1(jiti@2.4.2)
+      eslint-import-resolver-node: 0.3.9
+      eslint-module-utils: 2.12.1(@typescript-eslint/parser@8.35.1(eslint@9.30.1(jiti@2.4.2))(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint@9.30.1(jiti@2.4.2))
+      hasown: 2.0.2
+      is-core-module: 2.16.1
+      is-glob: 4.0.3
+      minimatch: 3.1.2
+      object.fromentries: 2.0.8
+      object.groupby: 1.0.3
+      object.values: 1.2.1
+      semver: 6.3.1
+      string.prototype.trimend: 1.0.9
+      tsconfig-paths: 3.15.0
+    optionalDependencies:
+      '@typescript-eslint/parser': 8.35.1(eslint@9.30.1(jiti@2.4.2))(typescript@5.8.3)
+    transitivePeerDependencies:
+      - eslint-import-resolver-typescript
+      - eslint-import-resolver-webpack
+      - supports-color
+
+  eslint-plugin-prettier@5.5.1(eslint-config-prettier@10.1.5(eslint@9.30.1(jiti@2.4.2)))(eslint@9.30.1(jiti@2.4.2))(prettier@3.5.3):
+    dependencies:
+      eslint: 9.30.1(jiti@2.4.2)
+      prettier: 3.5.3
+      prettier-linter-helpers: 1.0.0
+      synckit: 0.11.8
+    optionalDependencies:
+      eslint-config-prettier: 10.1.5(eslint@9.30.1(jiti@2.4.2))
+
+  eslint-plugin-vue@10.2.0(eslint@9.30.1(jiti@2.4.2))(vue-eslint-parser@10.2.0(eslint@9.30.1(jiti@2.4.2))):
+    dependencies:
+      '@eslint-community/eslint-utils': 4.7.0(eslint@9.30.1(jiti@2.4.2))
+      eslint: 9.30.1(jiti@2.4.2)
+      natural-compare: 1.4.0
+      nth-check: 2.1.1
+      postcss-selector-parser: 6.1.2
+      semver: 7.7.2
+      vue-eslint-parser: 10.2.0(eslint@9.30.1(jiti@2.4.2))
+      xml-name-validator: 4.0.0
+
+  eslint-scope@8.4.0:
+    dependencies:
+      esrecurse: 4.3.0
+      estraverse: 5.3.0
+
+  eslint-visitor-keys@3.4.3: {}
+
+  eslint-visitor-keys@4.2.1: {}
+
+  eslint@9.30.1(jiti@2.4.2):
+    dependencies:
+      '@eslint-community/eslint-utils': 4.7.0(eslint@9.30.1(jiti@2.4.2))
+      '@eslint-community/regexpp': 4.12.1
+      '@eslint/config-array': 0.21.0
+      '@eslint/config-helpers': 0.3.0
+      '@eslint/core': 0.14.0
+      '@eslint/eslintrc': 3.3.1
+      '@eslint/js': 9.30.1
+      '@eslint/plugin-kit': 0.3.3
+      '@humanfs/node': 0.16.6
+      '@humanwhocodes/module-importer': 1.0.1
+      '@humanwhocodes/retry': 0.4.3
+      '@types/estree': 1.0.8
+      '@types/json-schema': 7.0.15
+      ajv: 6.12.6
+      chalk: 4.1.2
+      cross-spawn: 7.0.6
+      debug: 4.4.1
+      escape-string-regexp: 4.0.0
+      eslint-scope: 8.4.0
+      eslint-visitor-keys: 4.2.1
+      espree: 10.4.0
+      esquery: 1.6.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.2
+      imurmurhash: 0.1.4
+      is-glob: 4.0.3
+      json-stable-stringify-without-jsonify: 1.0.1
+      lodash.merge: 4.6.2
+      minimatch: 3.1.2
+      natural-compare: 1.4.0
+      optionator: 0.9.4
+    optionalDependencies:
+      jiti: 2.4.2
+    transitivePeerDependencies:
+      - supports-color
+
+  espree@10.4.0:
+    dependencies:
+      acorn: 8.15.0
+      acorn-jsx: 5.3.2(acorn@8.15.0)
+      eslint-visitor-keys: 4.2.1
+
+  esquery@1.6.0:
+    dependencies:
+      estraverse: 5.3.0
+
+  esrecurse@4.3.0:
+    dependencies:
+      estraverse: 5.3.0
+
+  estraverse@5.3.0: {}
+
+  estree-walker@2.0.2: {}
+
+  esutils@2.0.3: {}
+
+  event-target-shim@5.0.1: {}
+
+  events@3.3.0: {}
+
+  execa@9.6.0:
+    dependencies:
+      '@sindresorhus/merge-streams': 4.0.0
+      cross-spawn: 7.0.6
+      figures: 6.1.0
+      get-stream: 9.0.1
+      human-signals: 8.0.1
+      is-plain-obj: 4.1.0
+      is-stream: 4.0.1
+      npm-run-path: 6.0.0
+      pretty-ms: 9.2.0
+      signal-exit: 4.1.0
+      strip-final-newline: 4.0.0
+      yoctocolors: 2.1.1
+
+  exsolve@1.0.7: {}
+
+  fast-deep-equal@3.1.3: {}
+
+  fast-diff@1.3.0: {}
+
+  fast-glob@3.3.3:
+    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.8
+
+  fast-json-stable-stringify@2.1.0: {}
+
+  fast-levenshtein@2.0.6: {}
+
+  fast-unique-numbers@8.0.13:
+    dependencies:
+      '@babel/runtime': 7.27.6
+      tslib: 2.8.1
+
+  fastq@1.19.1:
+    dependencies:
+      reusify: 1.1.0
+
+  fdir@6.4.6(picomatch@4.0.2):
+    optionalDependencies:
+      picomatch: 4.0.2
+
+  figures@6.1.0:
+    dependencies:
+      is-unicode-supported: 2.1.0
+
+  file-entry-cache@8.0.0:
+    dependencies:
+      flat-cache: 4.0.1
+
+  fill-range@7.1.1:
+    dependencies:
+      to-regex-range: 5.0.1
+
+  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.3
+      keyv: 4.5.4
+
+  flatted@3.3.3: {}
+
+  follow-redirects@1.15.9: {}
+
+  for-each@0.3.5:
+    dependencies:
+      is-callable: 1.2.7
+
+  form-data@4.0.3:
+    dependencies:
+      asynckit: 0.4.0
+      combined-stream: 1.0.8
+      es-set-tostringtag: 2.1.0
+      hasown: 2.0.2
+      mime-types: 2.1.35
+
+  fs-extra@11.3.0:
+    dependencies:
+      graceful-fs: 4.2.11
+      jsonfile: 6.1.0
+      universalify: 2.0.1
+
+  fsevents@2.3.3:
+    optional: true
+
+  function-bind@1.1.2: {}
+
+  function.prototype.name@1.1.8:
+    dependencies:
+      call-bind: 1.0.8
+      call-bound: 1.0.4
+      define-properties: 1.2.1
+      functions-have-names: 1.2.3
+      hasown: 2.0.2
+      is-callable: 1.2.7
+
+  functions-have-names@1.2.3: {}
+
+  gensync@1.0.0-beta.2: {}
+
+  get-intrinsic@1.3.0:
+    dependencies:
+      call-bind-apply-helpers: 1.0.2
+      es-define-property: 1.0.1
+      es-errors: 1.3.0
+      es-object-atoms: 1.1.1
+      function-bind: 1.1.2
+      get-proto: 1.0.1
+      gopd: 1.2.0
+      has-symbols: 1.1.0
+      hasown: 2.0.2
+      math-intrinsics: 1.1.0
+
+  get-proto@1.0.1:
+    dependencies:
+      dunder-proto: 1.0.1
+      es-object-atoms: 1.1.1
+
+  get-stream@9.0.1:
+    dependencies:
+      '@sec-ant/readable-stream': 0.4.1
+      is-stream: 4.0.1
+
+  get-symbol-description@1.1.0:
+    dependencies:
+      call-bound: 1.0.4
+      es-errors: 1.3.0
+      get-intrinsic: 1.3.0
+
+  glob-parent@5.1.2:
+    dependencies:
+      is-glob: 4.0.3
+
+  glob-parent@6.0.2:
+    dependencies:
+      is-glob: 4.0.3
+
+  globals@14.0.0: {}
+
+  globalthis@1.0.4:
+    dependencies:
+      define-properties: 1.2.1
+      gopd: 1.2.0
+
+  gopd@1.2.0: {}
+
+  graceful-fs@4.2.11: {}
+
+  graphemer@1.4.0: {}
+
+  has-bigints@1.1.0: {}
+
+  has-flag@4.0.0: {}
+
+  has-property-descriptors@1.0.2:
+    dependencies:
+      es-define-property: 1.0.1
+
+  has-proto@1.2.0:
+    dependencies:
+      dunder-proto: 1.0.1
+
+  has-symbols@1.1.0: {}
+
+  has-tostringtag@1.0.2:
+    dependencies:
+      has-symbols: 1.1.0
+
+  hasown@2.0.2:
+    dependencies:
+      function-bind: 1.1.2
+
+  he@1.2.0: {}
+
+  help-me@5.0.0: {}
+
+  hookable@5.5.3: {}
+
+  human-signals@8.0.1: {}
+
+  iconv-lite@0.6.3:
+    dependencies:
+      safer-buffer: 2.1.2
+    optional: true
+
+  ieee754@1.2.1: {}
+
+  ignore@5.3.2: {}
+
+  ignore@7.0.5: {}
+
+  image-size@0.5.5:
+    optional: true
+
+  import-fresh@3.3.1:
+    dependencies:
+      parent-module: 1.0.1
+      resolve-from: 4.0.0
+
+  imurmurhash@0.1.4: {}
+
+  inherits@2.0.4: {}
+
+  internal-slot@1.1.0:
+    dependencies:
+      es-errors: 1.3.0
+      hasown: 2.0.2
+      side-channel: 1.1.0
+
+  ip-address@9.0.5:
+    dependencies:
+      jsbn: 1.1.0
+      sprintf-js: 1.1.3
+
+  is-array-buffer@3.0.5:
+    dependencies:
+      call-bind: 1.0.8
+      call-bound: 1.0.4
+      get-intrinsic: 1.3.0
+
+  is-async-function@2.1.1:
+    dependencies:
+      async-function: 1.0.0
+      call-bound: 1.0.4
+      get-proto: 1.0.1
+      has-tostringtag: 1.0.2
+      safe-regex-test: 1.1.0
+
+  is-bigint@1.1.0:
+    dependencies:
+      has-bigints: 1.1.0
+
+  is-binary-path@2.1.0:
+    dependencies:
+      binary-extensions: 2.3.0
+
+  is-boolean-object@1.2.2:
+    dependencies:
+      call-bound: 1.0.4
+      has-tostringtag: 1.0.2
+
+  is-callable@1.2.7: {}
+
+  is-core-module@2.16.1:
+    dependencies:
+      hasown: 2.0.2
+
+  is-data-view@1.0.2:
+    dependencies:
+      call-bound: 1.0.4
+      get-intrinsic: 1.3.0
+      is-typed-array: 1.1.15
+
+  is-date-object@1.1.0:
+    dependencies:
+      call-bound: 1.0.4
+      has-tostringtag: 1.0.2
+
+  is-docker@3.0.0: {}
+
+  is-extglob@2.1.1: {}
+
+  is-finalizationregistry@1.1.1:
+    dependencies:
+      call-bound: 1.0.4
+
+  is-generator-function@1.1.0:
+    dependencies:
+      call-bound: 1.0.4
+      get-proto: 1.0.1
+      has-tostringtag: 1.0.2
+      safe-regex-test: 1.1.0
+
+  is-glob@4.0.3:
+    dependencies:
+      is-extglob: 2.1.1
+
+  is-inside-container@1.0.0:
+    dependencies:
+      is-docker: 3.0.0
+
+  is-map@2.0.3: {}
+
+  is-negative-zero@2.0.3: {}
+
+  is-number-object@1.1.1:
+    dependencies:
+      call-bound: 1.0.4
+      has-tostringtag: 1.0.2
+
+  is-number@7.0.0: {}
+
+  is-plain-obj@4.1.0: {}
+
+  is-plain-object@3.0.1: {}
+
+  is-regex@1.2.1:
+    dependencies:
+      call-bound: 1.0.4
+      gopd: 1.2.0
+      has-tostringtag: 1.0.2
+      hasown: 2.0.2
+
+  is-set@2.0.3: {}
+
+  is-shared-array-buffer@1.0.4:
+    dependencies:
+      call-bound: 1.0.4
+
+  is-stream@4.0.1: {}
+
+  is-string@1.1.1:
+    dependencies:
+      call-bound: 1.0.4
+      has-tostringtag: 1.0.2
+
+  is-symbol@1.1.1:
+    dependencies:
+      call-bound: 1.0.4
+      has-symbols: 1.1.0
+      safe-regex-test: 1.1.0
+
+  is-typed-array@1.1.15:
+    dependencies:
+      which-typed-array: 1.1.19
+
+  is-unicode-supported@2.1.0: {}
+
+  is-weakmap@2.0.2: {}
+
+  is-weakref@1.1.1:
+    dependencies:
+      call-bound: 1.0.4
+
+  is-weakset@2.0.4:
+    dependencies:
+      call-bound: 1.0.4
+      get-intrinsic: 1.3.0
+
+  is-what@3.14.1: {}
+
+  is-what@4.1.16: {}
+
+  is-wsl@3.1.0:
+    dependencies:
+      is-inside-container: 1.0.0
+
+  isarray@2.0.5: {}
+
+  isexe@2.0.0: {}
+
+  isexe@3.1.1: {}
+
+  jiti@2.4.2: {}
+
+  js-sdsl@4.3.0: {}
+
+  js-tokens@4.0.0: {}
+
+  js-yaml@4.1.0:
+    dependencies:
+      argparse: 2.0.1
+
+  jsbn@1.1.0: {}
+
+  jsesc@3.1.0: {}
+
+  json-buffer@3.0.1: {}
+
+  json-parse-even-better-errors@4.0.0: {}
+
+  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: {}
+
+  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
+
+  kolorist@1.8.0: {}
+
+  less@4.3.0:
+    dependencies:
+      copy-anything: 2.0.6
+      parse-node-version: 1.0.1
+      tslib: 2.8.1
+    optionalDependencies:
+      errno: 0.1.8
+      graceful-fs: 4.2.11
+      image-size: 0.5.5
+      make-dir: 2.1.0
+      mime: 1.6.0
+      needle: 3.3.1
+      source-map: 0.6.1
+
+  levn@0.4.1:
+    dependencies:
+      prelude-ls: 1.2.1
+      type-check: 0.4.0
+
+  local-pkg@1.1.1:
+    dependencies:
+      mlly: 1.7.4
+      pkg-types: 2.2.0
+      quansync: 0.2.10
+
+  locate-path@6.0.0:
+    dependencies:
+      p-locate: 5.0.0
+
+  lodash-es@4.17.21: {}
+
+  lodash.merge@4.6.2: {}
+
+  lodash@4.17.21: {}
+
+  loose-envify@1.4.0:
+    dependencies:
+      js-tokens: 4.0.0
+
+  lru-cache@10.4.3: {}
+
+  lru-cache@5.1.1:
+    dependencies:
+      yallist: 3.1.1
+
+  magic-string@0.30.17:
+    dependencies:
+      '@jridgewell/sourcemap-codec': 1.5.4
+
+  make-dir@2.1.0:
+    dependencies:
+      pify: 4.0.1
+      semver: 5.7.2
+    optional: true
+
+  math-intrinsics@1.1.0: {}
+
+  memorystream@0.3.1: {}
+
+  merge2@1.4.1: {}
+
+  micromatch@4.0.8:
+    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:
+    optional: true
+
+  minimatch@3.1.2:
+    dependencies:
+      brace-expansion: 1.1.12
+
+  minimatch@9.0.5:
+    dependencies:
+      brace-expansion: 2.0.2
+
+  minimist@1.2.8: {}
+
+  mitt@3.0.1: {}
+
+  mlly@1.7.4:
+    dependencies:
+      acorn: 8.15.0
+      pathe: 2.0.3
+      pkg-types: 1.3.1
+      ufo: 1.6.1
+
+  mqtt-packet@9.0.2:
+    dependencies:
+      bl: 6.1.0
+      debug: 4.4.1
+      process-nextick-args: 2.0.1
+    transitivePeerDependencies:
+      - supports-color
+
+  mqtt@5.13.1:
+    dependencies:
+      commist: 3.2.0
+      concat-stream: 2.0.0
+      debug: 4.4.1
+      help-me: 5.0.0
+      lru-cache: 10.4.3
+      minimist: 1.2.8
+      mqtt-packet: 9.0.2
+      number-allocator: 1.0.14
+      readable-stream: 4.7.0
+      rfdc: 1.4.1
+      socks: 2.8.5
+      split2: 4.2.0
+      worker-timers: 7.1.8
+      ws: 8.18.3
+    transitivePeerDependencies:
+      - bufferutil
+      - supports-color
+      - utf-8-validate
+
+  mrmime@2.0.1: {}
+
+  ms@2.1.3: {}
+
+  muggle-string@0.4.1: {}
+
+  nanoid@3.3.11: {}
+
+  nanoid@5.1.5: {}
+
+  nanopop@2.4.2: {}
+
+  natural-compare@1.4.0: {}
+
+  needle@3.3.1:
+    dependencies:
+      iconv-lite: 0.6.3
+      sax: 1.4.1
+    optional: true
+
+  node-releases@2.0.19: {}
+
+  normalize-path@3.0.0: {}
+
+  npm-normalize-package-bin@4.0.0: {}
+
+  npm-run-all2@8.0.4:
+    dependencies:
+      ansi-styles: 6.2.1
+      cross-spawn: 7.0.6
+      memorystream: 0.3.1
+      picomatch: 4.0.2
+      pidtree: 0.6.0
+      read-package-json-fast: 4.0.0
+      shell-quote: 1.8.3
+      which: 5.0.0
+
+  npm-run-path@6.0.0:
+    dependencies:
+      path-key: 4.0.0
+      unicorn-magic: 0.3.0
+
+  nth-check@2.1.1:
+    dependencies:
+      boolbase: 1.0.0
+
+  number-allocator@1.0.14:
+    dependencies:
+      debug: 4.4.1
+      js-sdsl: 4.3.0
+    transitivePeerDependencies:
+      - supports-color
+
+  object-inspect@1.13.4: {}
+
+  object-keys@1.1.1: {}
+
+  object.assign@4.1.7:
+    dependencies:
+      call-bind: 1.0.8
+      call-bound: 1.0.4
+      define-properties: 1.2.1
+      es-object-atoms: 1.1.1
+      has-symbols: 1.1.0
+      object-keys: 1.1.1
+
+  object.fromentries@2.0.8:
+    dependencies:
+      call-bind: 1.0.8
+      define-properties: 1.2.1
+      es-abstract: 1.24.0
+      es-object-atoms: 1.1.1
+
+  object.groupby@1.0.3:
+    dependencies:
+      call-bind: 1.0.8
+      define-properties: 1.2.1
+      es-abstract: 1.24.0
+
+  object.values@1.2.1:
+    dependencies:
+      call-bind: 1.0.8
+      call-bound: 1.0.4
+      define-properties: 1.2.1
+      es-object-atoms: 1.1.1
+
+  open@10.1.2:
+    dependencies:
+      default-browser: 5.2.1
+      define-lazy-prop: 3.0.0
+      is-inside-container: 1.0.0
+      is-wsl: 3.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
+
+  own-keys@1.0.1:
+    dependencies:
+      get-intrinsic: 1.3.0
+      object-keys: 1.1.1
+      safe-push-apply: 1.0.0
+
+  p-limit@3.1.0:
+    dependencies:
+      yocto-queue: 0.1.0
+
+  p-locate@5.0.0:
+    dependencies:
+      p-limit: 3.1.0
+
+  parent-module@1.0.1:
+    dependencies:
+      callsites: 3.1.0
+
+  parse-ms@4.0.0: {}
+
+  parse-node-version@1.0.1: {}
+
+  path-browserify@1.0.1: {}
+
+  path-exists@4.0.0: {}
+
+  path-key@3.1.1: {}
+
+  path-key@4.0.0: {}
+
+  path-parse@1.0.7: {}
+
+  pathe@2.0.3: {}
+
+  perfect-debounce@1.0.0: {}
+
+  picocolors@1.1.1: {}
+
+  picomatch@2.3.1: {}
+
+  picomatch@4.0.2: {}
+
+  pidtree@0.6.0: {}
+
+  pify@4.0.1:
+    optional: true
+
+  pinia-plugin-persistedstate@4.4.1(pinia@3.0.3(typescript@5.8.3)(vue@3.5.17(typescript@5.8.3))):
+    dependencies:
+      deep-pick-omit: 1.2.1
+      defu: 6.1.4
+      destr: 2.0.5
+    optionalDependencies:
+      pinia: 3.0.3(typescript@5.8.3)(vue@3.5.17(typescript@5.8.3))
+
+  pinia@3.0.3(typescript@5.8.3)(vue@3.5.17(typescript@5.8.3)):
+    dependencies:
+      '@vue/devtools-api': 7.7.7
+      vue: 3.5.17(typescript@5.8.3)
+    optionalDependencies:
+      typescript: 5.8.3
+
+  pinyin-pro@3.26.0: {}
+
+  pkg-types@1.3.1:
+    dependencies:
+      confbox: 0.1.8
+      mlly: 1.7.4
+      pathe: 2.0.3
+
+  pkg-types@2.2.0:
+    dependencies:
+      confbox: 0.2.2
+      exsolve: 1.0.7
+      pathe: 2.0.3
+
+  possible-typed-array-names@1.1.0: {}
+
+  postcss-selector-parser@6.1.2:
+    dependencies:
+      cssesc: 3.0.0
+      util-deprecate: 1.0.2
+
+  postcss@8.5.6:
+    dependencies:
+      nanoid: 3.3.11
+      picocolors: 1.1.1
+      source-map-js: 1.2.1
+
+  prelude-ls@1.2.1: {}
+
+  prettier-linter-helpers@1.0.0:
+    dependencies:
+      fast-diff: 1.3.0
+
+  prettier@3.5.3: {}
+
+  pretty-ms@9.2.0:
+    dependencies:
+      parse-ms: 4.0.0
+
+  process-nextick-args@2.0.1: {}
+
+  process@0.11.10: {}
+
+  proxy-from-env@1.1.0: {}
+
+  prr@1.0.1:
+    optional: true
+
+  punycode@2.3.1: {}
+
+  quansync@0.2.10: {}
+
+  queue-microtask@1.2.3: {}
+
+  read-package-json-fast@4.0.0:
+    dependencies:
+      json-parse-even-better-errors: 4.0.0
+      npm-normalize-package-bin: 4.0.0
+
+  readable-stream@3.6.2:
+    dependencies:
+      inherits: 2.0.4
+      string_decoder: 1.3.0
+      util-deprecate: 1.0.2
+
+  readable-stream@4.7.0:
+    dependencies:
+      abort-controller: 3.0.0
+      buffer: 6.0.3
+      events: 3.3.0
+      process: 0.11.10
+      string_decoder: 1.3.0
+
+  readdirp@3.6.0:
+    dependencies:
+      picomatch: 2.3.1
+
+  reflect.getprototypeof@1.0.10:
+    dependencies:
+      call-bind: 1.0.8
+      define-properties: 1.2.1
+      es-abstract: 1.24.0
+      es-errors: 1.3.0
+      es-object-atoms: 1.1.1
+      get-intrinsic: 1.3.0
+      get-proto: 1.0.1
+      which-builtin-type: 1.2.1
+
+  regexp.prototype.flags@1.5.4:
+    dependencies:
+      call-bind: 1.0.8
+      define-properties: 1.2.1
+      es-errors: 1.3.0
+      get-proto: 1.0.1
+      gopd: 1.2.0
+      set-function-name: 2.0.2
+
+  resize-observer-polyfill@1.5.1: {}
+
+  resolve-from@4.0.0: {}
+
+  resolve@1.22.10:
+    dependencies:
+      is-core-module: 2.16.1
+      path-parse: 1.0.7
+      supports-preserve-symlinks-flag: 1.0.0
+
+  reusify@1.1.0: {}
+
+  rfdc@1.4.1: {}
+
+  rollup@4.44.2:
+    dependencies:
+      '@types/estree': 1.0.8
+    optionalDependencies:
+      '@rollup/rollup-android-arm-eabi': 4.44.2
+      '@rollup/rollup-android-arm64': 4.44.2
+      '@rollup/rollup-darwin-arm64': 4.44.2
+      '@rollup/rollup-darwin-x64': 4.44.2
+      '@rollup/rollup-freebsd-arm64': 4.44.2
+      '@rollup/rollup-freebsd-x64': 4.44.2
+      '@rollup/rollup-linux-arm-gnueabihf': 4.44.2
+      '@rollup/rollup-linux-arm-musleabihf': 4.44.2
+      '@rollup/rollup-linux-arm64-gnu': 4.44.2
+      '@rollup/rollup-linux-arm64-musl': 4.44.2
+      '@rollup/rollup-linux-loongarch64-gnu': 4.44.2
+      '@rollup/rollup-linux-powerpc64le-gnu': 4.44.2
+      '@rollup/rollup-linux-riscv64-gnu': 4.44.2
+      '@rollup/rollup-linux-riscv64-musl': 4.44.2
+      '@rollup/rollup-linux-s390x-gnu': 4.44.2
+      '@rollup/rollup-linux-x64-gnu': 4.44.2
+      '@rollup/rollup-linux-x64-musl': 4.44.2
+      '@rollup/rollup-win32-arm64-msvc': 4.44.2
+      '@rollup/rollup-win32-ia32-msvc': 4.44.2
+      '@rollup/rollup-win32-x64-msvc': 4.44.2
+      fsevents: 2.3.3
+
+  run-applescript@7.0.0: {}
+
+  run-parallel@1.2.0:
+    dependencies:
+      queue-microtask: 1.2.3
+
+  safe-array-concat@1.1.3:
+    dependencies:
+      call-bind: 1.0.8
+      call-bound: 1.0.4
+      get-intrinsic: 1.3.0
+      has-symbols: 1.1.0
+      isarray: 2.0.5
+
+  safe-buffer@5.2.1: {}
+
+  safe-push-apply@1.0.0:
+    dependencies:
+      es-errors: 1.3.0
+      isarray: 2.0.5
+
+  safe-regex-test@1.1.0:
+    dependencies:
+      call-bound: 1.0.4
+      es-errors: 1.3.0
+      is-regex: 1.2.1
+
+  safer-buffer@2.1.2:
+    optional: true
+
+  sax@1.4.1:
+    optional: true
+
+  scroll-into-view-if-needed@2.2.31:
+    dependencies:
+      compute-scroll-into-view: 1.0.20
+
+  semver@5.7.2:
+    optional: true
+
+  semver@6.3.1: {}
+
+  semver@7.7.2: {}
+
+  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.3.0
+      gopd: 1.2.0
+      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
+
+  set-proto@1.0.0:
+    dependencies:
+      dunder-proto: 1.0.1
+      es-errors: 1.3.0
+      es-object-atoms: 1.1.1
+
+  shallow-equal@1.2.1: {}
+
+  shebang-command@2.0.0:
+    dependencies:
+      shebang-regex: 3.0.0
+
+  shebang-regex@3.0.0: {}
+
+  shell-quote@1.8.3: {}
+
+  side-channel-list@1.0.0:
+    dependencies:
+      es-errors: 1.3.0
+      object-inspect: 1.13.4
+
+  side-channel-map@1.0.1:
+    dependencies:
+      call-bound: 1.0.4
+      es-errors: 1.3.0
+      get-intrinsic: 1.3.0
+      object-inspect: 1.13.4
+
+  side-channel-weakmap@1.0.2:
+    dependencies:
+      call-bound: 1.0.4
+      es-errors: 1.3.0
+      get-intrinsic: 1.3.0
+      object-inspect: 1.13.4
+      side-channel-map: 1.0.1
+
+  side-channel@1.1.0:
+    dependencies:
+      es-errors: 1.3.0
+      object-inspect: 1.13.4
+      side-channel-list: 1.0.0
+      side-channel-map: 1.0.1
+      side-channel-weakmap: 1.0.2
+
+  signal-exit@4.1.0: {}
+
+  sirv@3.0.1:
+    dependencies:
+      '@polka/url': 1.0.0-next.29
+      mrmime: 2.0.1
+      totalist: 3.0.1
+
+  smart-buffer@4.2.0: {}
+
+  socks@2.8.5:
+    dependencies:
+      ip-address: 9.0.5
+      smart-buffer: 4.2.0
+
+  source-map-js@1.2.1: {}
+
+  source-map@0.6.1:
+    optional: true
+
+  speakingurl@14.0.1: {}
+
+  split2@4.2.0: {}
+
+  sprintf-js@1.1.3: {}
+
+  stop-iteration-iterator@1.1.0:
+    dependencies:
+      es-errors: 1.3.0
+      internal-slot: 1.1.0
+
+  string.prototype.trim@1.2.10:
+    dependencies:
+      call-bind: 1.0.8
+      call-bound: 1.0.4
+      define-data-property: 1.1.4
+      define-properties: 1.2.1
+      es-abstract: 1.24.0
+      es-object-atoms: 1.1.1
+      has-property-descriptors: 1.0.2
+
+  string.prototype.trimend@1.0.9:
+    dependencies:
+      call-bind: 1.0.8
+      call-bound: 1.0.4
+      define-properties: 1.2.1
+      es-object-atoms: 1.1.1
+
+  string.prototype.trimstart@1.0.8:
+    dependencies:
+      call-bind: 1.0.8
+      define-properties: 1.2.1
+      es-object-atoms: 1.1.1
+
+  string_decoder@1.3.0:
+    dependencies:
+      safe-buffer: 5.2.1
+
+  strip-bom@3.0.0: {}
+
+  strip-final-newline@4.0.0: {}
+
+  strip-json-comments@3.1.1: {}
+
+  stylis@4.3.6: {}
+
+  superjson@2.2.2:
+    dependencies:
+      copy-anything: 3.0.5
+
+  supports-color@7.2.0:
+    dependencies:
+      has-flag: 4.0.0
+
+  supports-preserve-symlinks-flag@1.0.0: {}
+
+  synckit@0.11.8:
+    dependencies:
+      '@pkgr/core': 0.2.7
+
+  throttle-debounce@5.0.2: {}
+
+  tinyglobby@0.2.14:
+    dependencies:
+      fdir: 6.4.6(picomatch@4.0.2)
+      picomatch: 4.0.2
+
+  to-regex-range@5.0.1:
+    dependencies:
+      is-number: 7.0.0
+
+  totalist@3.0.1: {}
+
+  ts-api-utils@2.1.0(typescript@5.8.3):
+    dependencies:
+      typescript: 5.8.3
+
+  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@2.3.0: {}
+
+  tslib@2.8.1: {}
+
+  type-check@0.4.0:
+    dependencies:
+      prelude-ls: 1.2.1
+
+  typed-array-buffer@1.0.3:
+    dependencies:
+      call-bound: 1.0.4
+      es-errors: 1.3.0
+      is-typed-array: 1.1.15
+
+  typed-array-byte-length@1.0.3:
+    dependencies:
+      call-bind: 1.0.8
+      for-each: 0.3.5
+      gopd: 1.2.0
+      has-proto: 1.2.0
+      is-typed-array: 1.1.15
+
+  typed-array-byte-offset@1.0.4:
+    dependencies:
+      available-typed-arrays: 1.0.7
+      call-bind: 1.0.8
+      for-each: 0.3.5
+      gopd: 1.2.0
+      has-proto: 1.2.0
+      is-typed-array: 1.1.15
+      reflect.getprototypeof: 1.0.10
+
+  typed-array-length@1.0.7:
+    dependencies:
+      call-bind: 1.0.8
+      for-each: 0.3.5
+      gopd: 1.2.0
+      is-typed-array: 1.1.15
+      possible-typed-array-names: 1.1.0
+      reflect.getprototypeof: 1.0.10
+
+  typedarray@0.0.6: {}
+
+  typescript-eslint@8.35.1(eslint@9.30.1(jiti@2.4.2))(typescript@5.8.3):
+    dependencies:
+      '@typescript-eslint/eslint-plugin': 8.35.1(@typescript-eslint/parser@8.35.1(eslint@9.30.1(jiti@2.4.2))(typescript@5.8.3))(eslint@9.30.1(jiti@2.4.2))(typescript@5.8.3)
+      '@typescript-eslint/parser': 8.35.1(eslint@9.30.1(jiti@2.4.2))(typescript@5.8.3)
+      '@typescript-eslint/utils': 8.35.1(eslint@9.30.1(jiti@2.4.2))(typescript@5.8.3)
+      eslint: 9.30.1(jiti@2.4.2)
+      typescript: 5.8.3
+    transitivePeerDependencies:
+      - supports-color
+
+  typescript@5.8.3: {}
+
+  ufo@1.6.1: {}
+
+  unbox-primitive@1.1.0:
+    dependencies:
+      call-bound: 1.0.4
+      has-bigints: 1.1.0
+      has-symbols: 1.1.0
+      which-boxed-primitive: 1.1.1
+
+  undici-types@6.21.0: {}
+
+  unicorn-magic@0.3.0: {}
+
+  universalify@2.0.1: {}
+
+  unplugin-utils@0.2.4:
+    dependencies:
+      pathe: 2.0.3
+      picomatch: 4.0.2
+
+  unplugin-vue-components@28.8.0(@babel/parser@7.28.0)(vue@3.5.17(typescript@5.8.3)):
+    dependencies:
+      chokidar: 3.6.0
+      debug: 4.4.1
+      local-pkg: 1.1.1
+      magic-string: 0.30.17
+      mlly: 1.7.4
+      tinyglobby: 0.2.14
+      unplugin: 2.3.5
+      unplugin-utils: 0.2.4
+      vue: 3.5.17(typescript@5.8.3)
+    optionalDependencies:
+      '@babel/parser': 7.28.0
+    transitivePeerDependencies:
+      - supports-color
+
+  unplugin@2.3.5:
+    dependencies:
+      acorn: 8.15.0
+      picomatch: 4.0.2
+      webpack-virtual-modules: 0.6.2
+
+  update-browserslist-db@1.1.3(browserslist@4.25.1):
+    dependencies:
+      browserslist: 4.25.1
+      escalade: 3.2.0
+      picocolors: 1.1.1
+
+  uri-js@4.4.1:
+    dependencies:
+      punycode: 2.3.1
+
+  util-deprecate@1.0.2: {}
+
+  vite-hot-client@2.1.0(vite@7.0.2(@types/node@22.16.0)(jiti@2.4.2)(less@4.3.0)):
+    dependencies:
+      vite: 7.0.2(@types/node@22.16.0)(jiti@2.4.2)(less@4.3.0)
+
+  vite-plugin-inspect@0.8.9(rollup@4.44.2)(vite@7.0.2(@types/node@22.16.0)(jiti@2.4.2)(less@4.3.0)):
+    dependencies:
+      '@antfu/utils': 0.7.10
+      '@rollup/pluginutils': 5.2.0(rollup@4.44.2)
+      debug: 4.4.1
+      error-stack-parser-es: 0.1.5
+      fs-extra: 11.3.0
+      open: 10.1.2
+      perfect-debounce: 1.0.0
+      picocolors: 1.1.1
+      sirv: 3.0.1
+      vite: 7.0.2(@types/node@22.16.0)(jiti@2.4.2)(less@4.3.0)
+    transitivePeerDependencies:
+      - rollup
+      - supports-color
+
+  vite-plugin-vue-devtools@7.7.7(rollup@4.44.2)(vite@7.0.2(@types/node@22.16.0)(jiti@2.4.2)(less@4.3.0))(vue@3.5.17(typescript@5.8.3)):
+    dependencies:
+      '@vue/devtools-core': 7.7.7(vite@7.0.2(@types/node@22.16.0)(jiti@2.4.2)(less@4.3.0))(vue@3.5.17(typescript@5.8.3))
+      '@vue/devtools-kit': 7.7.7
+      '@vue/devtools-shared': 7.7.7
+      execa: 9.6.0
+      sirv: 3.0.1
+      vite: 7.0.2(@types/node@22.16.0)(jiti@2.4.2)(less@4.3.0)
+      vite-plugin-inspect: 0.8.9(rollup@4.44.2)(vite@7.0.2(@types/node@22.16.0)(jiti@2.4.2)(less@4.3.0))
+      vite-plugin-vue-inspector: 5.3.2(vite@7.0.2(@types/node@22.16.0)(jiti@2.4.2)(less@4.3.0))
+    transitivePeerDependencies:
+      - '@nuxt/kit'
+      - rollup
+      - supports-color
+      - vue
+
+  vite-plugin-vue-inspector@5.3.2(vite@7.0.2(@types/node@22.16.0)(jiti@2.4.2)(less@4.3.0)):
+    dependencies:
+      '@babel/core': 7.28.0
+      '@babel/plugin-proposal-decorators': 7.28.0(@babel/core@7.28.0)
+      '@babel/plugin-syntax-import-attributes': 7.27.1(@babel/core@7.28.0)
+      '@babel/plugin-syntax-import-meta': 7.10.4(@babel/core@7.28.0)
+      '@babel/plugin-transform-typescript': 7.28.0(@babel/core@7.28.0)
+      '@vue/babel-plugin-jsx': 1.4.0(@babel/core@7.28.0)
+      '@vue/compiler-dom': 3.5.17
+      kolorist: 1.8.0
+      magic-string: 0.30.17
+      vite: 7.0.2(@types/node@22.16.0)(jiti@2.4.2)(less@4.3.0)
+    transitivePeerDependencies:
+      - supports-color
+
+  vite@7.0.2(@types/node@22.16.0)(jiti@2.4.2)(less@4.3.0):
+    dependencies:
+      esbuild: 0.25.5
+      fdir: 6.4.6(picomatch@4.0.2)
+      picomatch: 4.0.2
+      postcss: 8.5.6
+      rollup: 4.44.2
+      tinyglobby: 0.2.14
+    optionalDependencies:
+      '@types/node': 22.16.0
+      fsevents: 2.3.3
+      jiti: 2.4.2
+      less: 4.3.0
+
+  vscode-uri@3.1.0: {}
+
+  vue-eslint-parser@10.2.0(eslint@9.30.1(jiti@2.4.2)):
+    dependencies:
+      debug: 4.4.1
+      eslint: 9.30.1(jiti@2.4.2)
+      eslint-scope: 8.4.0
+      eslint-visitor-keys: 4.2.1
+      espree: 10.4.0
+      esquery: 1.6.0
+      semver: 7.7.2
+    transitivePeerDependencies:
+      - supports-color
+
+  vue-router@4.5.1(vue@3.5.17(typescript@5.8.3)):
+    dependencies:
+      '@vue/devtools-api': 6.6.4
+      vue: 3.5.17(typescript@5.8.3)
+
+  vue-tsc@2.2.12(typescript@5.8.3):
+    dependencies:
+      '@volar/typescript': 2.4.15
+      '@vue/language-core': 2.2.12(typescript@5.8.3)
+      typescript: 5.8.3
+
+  vue-types@3.0.2(vue@3.5.17(typescript@5.8.3)):
+    dependencies:
+      is-plain-object: 3.0.1
+      vue: 3.5.17(typescript@5.8.3)
+
+  vue@3.5.17(typescript@5.8.3):
+    dependencies:
+      '@vue/compiler-dom': 3.5.17
+      '@vue/compiler-sfc': 3.5.17
+      '@vue/runtime-dom': 3.5.17
+      '@vue/server-renderer': 3.5.17(vue@3.5.17(typescript@5.8.3))
+      '@vue/shared': 3.5.17
+    optionalDependencies:
+      typescript: 5.8.3
+
+  warning@4.0.3:
+    dependencies:
+      loose-envify: 1.4.0
+
+  webpack-virtual-modules@0.6.2: {}
+
+  which-boxed-primitive@1.1.1:
+    dependencies:
+      is-bigint: 1.1.0
+      is-boolean-object: 1.2.2
+      is-number-object: 1.1.1
+      is-string: 1.1.1
+      is-symbol: 1.1.1
+
+  which-builtin-type@1.2.1:
+    dependencies:
+      call-bound: 1.0.4
+      function.prototype.name: 1.1.8
+      has-tostringtag: 1.0.2
+      is-async-function: 2.1.1
+      is-date-object: 1.1.0
+      is-finalizationregistry: 1.1.1
+      is-generator-function: 1.1.0
+      is-regex: 1.2.1
+      is-weakref: 1.1.1
+      isarray: 2.0.5
+      which-boxed-primitive: 1.1.1
+      which-collection: 1.0.2
+      which-typed-array: 1.1.19
+
+  which-collection@1.0.2:
+    dependencies:
+      is-map: 2.0.3
+      is-set: 2.0.3
+      is-weakmap: 2.0.2
+      is-weakset: 2.0.4
+
+  which-typed-array@1.1.19:
+    dependencies:
+      available-typed-arrays: 1.0.7
+      call-bind: 1.0.8
+      call-bound: 1.0.4
+      for-each: 0.3.5
+      get-proto: 1.0.1
+      gopd: 1.2.0
+      has-tostringtag: 1.0.2
+
+  which@2.0.2:
+    dependencies:
+      isexe: 2.0.0
+
+  which@5.0.0:
+    dependencies:
+      isexe: 3.1.1
+
+  word-wrap@1.2.5: {}
+
+  worker-timers-broker@6.1.8:
+    dependencies:
+      '@babel/runtime': 7.27.6
+      fast-unique-numbers: 8.0.13
+      tslib: 2.8.1
+      worker-timers-worker: 7.0.71
+
+  worker-timers-worker@7.0.71:
+    dependencies:
+      '@babel/runtime': 7.27.6
+      tslib: 2.8.1
+
+  worker-timers@7.1.8:
+    dependencies:
+      '@babel/runtime': 7.27.6
+      tslib: 2.8.1
+      worker-timers-broker: 6.1.8
+      worker-timers-worker: 7.0.71
+
+  ws@8.18.3: {}
+
+  xml-name-validator@4.0.0: {}
+
+  yallist@3.1.1: {}
+
+  yocto-queue@0.1.0: {}
+
+  yoctocolors@2.1.1: {}
+
+  zrender@5.6.1:
+    dependencies:
+      tslib: 2.3.0

BIN
public/favicon.ico


BIN
public/设备清单.xlsx


+ 53 - 0
src/App.vue

@@ -0,0 +1,53 @@
+<template>
+  <a-config-provider :locale="locale" :auto-insert-space-in-button="false">
+    <RouterView v-if="isRouterReady">
+      <template #default="{ Component, route }">
+        <component
+          :is="isFullScreen ? Component : BaseLayout"
+          v-bind="isFullScreen ? {} : { content: Component }"
+        >
+          <keep-alive>
+            <component
+              :is="Component"
+              v-if="route.meta.keepAlive"
+              :key="route.meta.usePathKey ? route.path : route.name"
+            />
+          </keep-alive>
+
+          <component
+            :is="Component"
+            v-if="!route.meta.keepAlive"
+            :key="route.meta.usePathKey ? route.path : route.name"
+          />
+        </component>
+      </template>
+    </RouterView>
+  </a-config-provider>
+</template>
+
+<script setup lang="ts">
+import { computed, ref, onMounted } from 'vue'
+import BaseLayout from '@/layout/index.vue'
+import { useRoute, useRouter } from 'vue-router'
+
+import zhCN from 'ant-design-vue/es/locale/zh_CN'
+import dayjs from 'dayjs'
+import 'dayjs/locale/zh-cn'
+dayjs.locale('zh-cn')
+const locale = zhCN
+
+const route = useRoute()
+const router = useRouter()
+const isRouterReady = ref(false)
+
+onMounted(() => {
+  router.isReady().then(() => {
+    isRouterReady.value = true
+  })
+})
+
+const isFullScreen = computed(() => Boolean(route.meta?.isFullScreen))
+console.log('🚀App route🚀', route)
+</script>
+
+<style scoped lang="less"></style>

+ 18 - 0
src/api/admin/index.ts

@@ -0,0 +1,18 @@
+import request from '@/request'
+import type * as TYPE from './types'
+
+/**
+ * 用户登录
+ * @param account 账号
+ * @param password 密码
+ */
+export const login = (params: TYPE.LoginParams): Promise<ResponseData<TYPE.LoginResponseData>> => {
+  return request.post('/manage/login', params)
+}
+
+/**
+ * 退出登录
+ */
+export const logout = (): Promise<ResponseData<object>> => {
+  return request.get('/manage/logout')
+}

+ 41 - 0
src/api/admin/types.ts

@@ -0,0 +1,41 @@
+/**
+ * 登录请求参数
+ * @param account 账号
+ * @param password 密码
+ */
+export interface LoginParams {
+  account: string
+  password: string
+}
+
+/**
+ * 登录响应数据
+ * @param tokenName token名称
+ * @param tokenValue token值
+ * @param isLogin 是否已登录
+ * @param loginId 此token对应的LoginId,未登录时为null
+ * @param loginType 多账号体系下的账号类型
+ * @param tokenTimeout token 剩余有效期(单位: 秒)
+ * @param sessionTimeout Account-Session 剩余有效时间(单位: 秒)
+ * @param tokenSessionTimeout Token-Session 剩余有效时间(单位: 秒)
+ * @param loginDeviceType 登录设备类型
+ * @param tags 自定义数据(暂无意义,留作扩展)
+ * @param phone 手机号
+ * @param userId userId
+ * @param account 账户
+ */
+export interface LoginResponseData {
+  tokenName: string // token名称
+  tokenValue: string // token值
+  isLogin: string // 是否已登录
+  loginId: string // 此token对应的LoginId,未登录时为null
+  loginType: string // 多账号体系下的账号类型
+  tokenTimeout: string // token 剩余有效期(单位: 秒)
+  sessionTimeout: string // Account-Session 剩余有效时间(单位: 秒)
+  tokenSessionTimeout: string // Token-Session 剩余有效时间(单位: 秒)
+  loginDeviceType: string // 登录设备类型
+  tags: string // 自定义数据(暂无意义,留作扩展)
+  phone: string // 手机号
+  userId: number // userId
+  account: string // 账户
+}

+ 71 - 0
src/api/device/index.ts

@@ -0,0 +1,71 @@
+import request from '@/request'
+import type * as TYPE from './types'
+
+/**
+ * 获取设备列表
+  @param pageNo 当前页码
+  @param pageSize 页面大小
+  @param clientId 设备ID
+  @param devName 设备名称
+  @param online 设备状态:0-离线,1-在线
+  @param createTimeStart 起始时间 格式yyyy-MM-dd
+  @param createTimeEnd 结束时间 格式yyyy-MM-dd
+ */
+export const getDeviceList = (
+  params: TYPE.DeviceListParams
+): Promise<ResponseData<TYPE.DeviceListData>> => {
+  return request.post('/device/queryDevList', params)
+}
+
+/**
+ * 获取设备详情
+ */
+export const getDeviceDetail = (
+  params: TYPE.DeviceDetailParams
+): Promise<ResponseData<TYPE.Device[]>> => {
+  return request.post('/s/wxxcx/index/queryList', params)
+}
+
+/**
+ * 添加设备
+ */
+export const addDevice = (params: TYPE.AddDeviceParams): Promise<ResponseData<TYPE.Device[]>> => {
+  return request.post(`/device/addOneDevice`, params)
+}
+
+/**
+ * 删除设备
+ */
+export const deleteDevice = (
+  params: TYPE.DeleteDeviceParams
+): Promise<ResponseData<TYPE.Device[]>> => {
+  return request.post('/s/wxxcx/index/deleteDevice', params)
+}
+
+/**
+ * 批量导入设备
+ */
+export const uploadDevInfo = (
+  params: TYPE.UploadDevInfoParams
+): Promise<ResponseData<TYPE.Device[]>> => {
+  return request.post('/s/wxxcx/index/uploadDevInfo', params)
+}
+
+/**
+ * 获取设备详情
+ * @param devId 设备ID
+ */
+export const getDeviceDetailByDevId = (params: {
+  devId: string
+}): Promise<ResponseData<TYPE.DeviceDetailData>> => {
+  return request.get('/device/query', { params })
+}
+
+/**
+ * 更新设备信息
+ */
+export const updateDevice = (
+  params: TYPE.UpdateDeviceParams
+): Promise<ResponseData<TYPE.Device[]>> => {
+  return request.post('device/updateDevice', params)
+}

+ 258 - 0
src/api/device/types.ts

@@ -0,0 +1,258 @@
+/**
+ * 设备类型
+ * @param devId 主键ID
+ * @param clientId 设备ID
+ * @param userId 用户ID
+ * @param devName 设备名称
+ * @param devType 设备类型
+ * @param online 设备状态:0-离线,1-在线
+ * @param devWarn 设备报警:0-正常,1-报警
+ * @param software 软件版本
+ * @param hardware 硬件版本
+ * @param wifiName 无线网络名称
+ * @param wifiPassword 无线网络密码
+ * @param ip IP地址
+ * @param mountPlain 安装方式:Wall-墙装,Ceiling-顶装
+ * @param startX 跟踪区域起始X坐标
+ * @param startY 跟踪区域起始Y坐标
+ * @param startZ 跟踪区域起始Z坐标
+ * @param stopX 跟踪区域结束X坐标
+ * @param stopY 跟踪区域结束Y坐标
+ * @param stopZ 跟踪区域结束Z坐标
+ * @param height 安装高度
+ * @param length 房间长度
+ * @param width 房间宽度
+ * @param targetPoints target数组 雷达的mqtt目标值
+ * @param signalTime 信号时间
+ * @param northAngle 北向角度
+ * @param x 安装坐标x
+ * @param y 安装坐标y
+ * @param statusLight 状态灯:0-关闭,1-开启
+ * @param createId 创建人
+ * @param updateId 修改人
+ * @param createTime 创建时间
+ * @param updateTime 修改时间
+ */
+export interface Device {
+  devId: number // 主键ID
+  clientId: string // 设备ID
+  userId: number // 用户ID
+  devName: string // 设备名称
+  devType: string // 设备类型
+  online: 0 | 1 // 设备状态:0-离线,1-在线
+  devWarn: 0 | 1 // 设备报警:0-正常,1-报警
+  software: string // 软件版本
+  hardware: string // 硬件版本
+  wifiName: string // 无线网络名称
+  wifiPassword: string // 无线网络密码
+  ip: string // IP地址
+  mountPlain: 'Wall' | 'Ceiling' // 安装方式:Wall-墙装,Ceiling-顶装
+  startX: number // 跟踪区域起始X坐标
+  startY: number // 跟踪区域起始Y坐标
+  startZ: number // 跟踪区域起始Z坐标
+  stopX: number // 跟踪区域结束X坐标
+  stopY: number // 跟踪区域结束Y坐标
+  stopZ: number // 跟踪区域结束Z坐标
+  height: number // 安装高度
+  length: number // 房间长度
+  width: number // 房间宽度
+  targetPoints: object // target数组 雷达的mqtt目标值
+  signalTime: string // 信号时间
+  northAngle: number // 北向角度
+  x: number // 安装坐标x
+  y: number // 安装坐标y
+  statusLight: 0 | 1 // 状态灯:0-关闭,1-开启
+  createId: string // 创建人
+  updateId: string // 修改人
+  createTime: string // 创建时间
+  updateTime: string // 修改时间
+}
+
+/**
+ * 设备列表请求参数
+ * @param userId 用户ID
+ * @param pageNum 页码
+ * @param pageSize 每页数量
+ * @param deviceId 设备ID(可选)
+ * @param createTimeStart 创建时间开始(可选)
+ * @param createTimeEnd 创建时间结束(可选)
+ */
+export interface DeviceListParams {
+  pageNo: number // 当前页码
+  pageSize: number // 页面大小
+  clientId?: string // 设备ID
+  devName?: string // 设备名称
+  online?: number | null // 设备状态:0-离线,1-在线
+  createTimeStart?: string // 起始时间 格式yyyy-MM-dd
+  createTimeEnd?: string // 结束时间 格式yyyy-MM-dd
+  tenantId?: string | null // 租户ID
+}
+
+/**
+ * 设备列表响应数据
+ */
+export interface DeviceListData {
+  rows: Device[]
+  total: number
+  pageNum: number
+  pageSize: number
+  outTotalPageNum: number
+  totalPageNum: number
+}
+
+/**
+ * 设备详情请求参数
+ * @param userId 用户ID
+ * @param deviceId 设备ID
+ */
+export interface DeviceDetailParams {
+  userId: string
+  deviceId: string
+}
+
+/**
+ * 添加设备请求参数
+ * @param dev_id 设备ID
+ * @param dev_type 设备类型
+ * @param software 软件版本
+ */
+export interface AddDeviceParams {
+  clientId: string // 设备ID
+  devType: string // 设备类型
+  software?: string // 软件版本
+  hardware: string // 硬件版本
+  tenantId: string | null // 租户ID
+}
+
+/**
+ * 删除设备请求参数
+ * @param userId 用户ID
+ * @param deviceId 设备ID
+ */
+export interface DeleteDeviceParams {
+  userId: string
+  deviceId: string
+}
+
+/**
+ * 批量导入设备请求参数
+ * @param userId 用户ID
+ * @param file 文件
+ */
+export interface UploadDevInfoParams {
+  userId: string
+  file: File
+}
+
+/**
+ * 设备详情,返回数据
+ * @param devId 主键ID
+ * @param clientId 设备ID
+ * @param userId 用户ID
+ * @param devName 设备名称
+ * @param devType 设备类型
+ * @param online 设备状态:0-离线,1-在线
+ * @param devWarn 设备报警:0-正常,1-报警
+ * @param software 软件版本
+ * @param hardware 硬件版本
+ * @param wifiName 无线网络名称
+ * @param wifiPassword 无线网络密码
+ * @param ip IP地址
+ * @param mountPlain 安装方式:Wall-墙装,Ceiling-顶装
+ * @param installPosition 安装位置:Toilet-卫生间,Bedroom-卧室,LivingRoom-客厅,Restaurant-餐厅
+ * @param xxStart 雷达监测范围x开始,范围:-200-200 cm之内
+ * @param xxEnd 雷达监测范围x结束,范围:-200-200 cm之内,且xxEnd > xxStart
+ * @param yyStart 雷达监测范围y开始,范围:-250-250 cm之内
+ * @param yyEnd 雷达监测范围y结束,范围:-250-250 cm之内,且yyEnd > yyStart
+ * @param zzStart 雷达监测范围z开始,范围:0-5 cm之内
+ * @param zzEnd 雷达监测范围z结束,范围:200-300 cm之内
+ * @param height 安装高度: 250 - 370 cm 之内
+ * @param length 雷达监测长度(X) xxEnd-xxStart
+ * @param width 	雷达监测宽度(Y) yyEnd-yyStart
+ * @param targetPoints target数组 雷达的mqtt目标值
+ * @param signalTime 接收target时间
+ * @param northAngle 北向夹角 允许传值: 0,90,180,270
+ * @param activeTime 激活时间
+ * @param statusLight 指示灯开关:0-关闭,1-开启
+ * @param createId 创建人
+ * @param updateId 修改人
+ * @param createTime 创建时间
+ * @param updateTime 修改时间
+ */
+export interface DeviceDetailData {
+  devId: ID // 主键ID
+  clientId: ID // 设备ID
+  tenantId: ID // 租户ID
+  tenantName: string // 租户名称
+  userId: ID // 用户ID
+  devName: string //设备名称
+  devType: string // 设备类型
+  online: Switch // 设备状态:0-离线,1-在线
+  devWarn: Switch // 设备报警:0-正常,1-报警
+  software: string // 软件版本
+  hardware: string // 硬件版本
+  wifiName: string // 无线网络名称
+  wifiPassword: string // 无线网络密码
+  ip: string // IP地址
+  fallingConfirm: ID // 设备跌倒确认时间 通常180s
+  mountPlain: InstallWay // 安装方式:Wall-墙装,Ceiling-顶装
+  installPosition: InstallPosition // 安装位置:Toilet-卫生间,Bedroom-卧室,LivingRoom-客厅,Restaurant-餐厅
+  xxStart: ID // 雷达监测范围x开始,范围:-200-200 cm之内
+  xxEnd: ID // 雷达监测范围x结束,范围:-200-200 cm之内,且xxEnd > xxStart
+  yyStart: ID // 雷达监测范围y开始,范围:-250-250 cm之内
+  yyEnd: ID // 雷达监测范围y结束,范围:-250-250 cm之内,且yyEnd > yyStart
+  zzStart: ID // 雷达监测范围z开始,范围:0-5 cm之内
+  zzEnd: ID // 雷达监测范围z结束,范围:200-300 cm之内
+  height: ID // 安装高度: 250 - 370 cm 之内
+  length: ID // 雷达监测长度(X) xxEnd-xxStart
+  width: ID // 	雷达监测宽度(Y) yyEnd-yyStart
+  targetPoints: number[] // target数组 雷达的mqtt目标值
+  signalTime: ID // 接收target时间
+  northAngle: NorthAngle // 北向夹角 允许传值: 0,90,180,270
+  activeTime: string // 激活时间
+  statusLight: Switch // 指示灯开关:0-关闭,1-开启
+  createId: ID // 创建人
+  updateId: ID // 修改人
+  createTime: string // 创建时间
+  updateTime: string // 修改时间
+}
+
+/**
+ * 更新设备信息,请求参数
+ * @param clientId 设备ID
+ * @param userId 用户ID
+ * @param tenantId 租户ID
+ * @param devName 设备名称:最多10个字符
+ * @param height 安装高度: 250 - 370 cm 之内
+ * @param wifiName WIFI名称
+ * @param wifiPassword WIFI密码
+ * @param northAngle 北向夹角 允许传值: 0,90,180,270
+ * @param mountPlain 安装方式:Wall-墙装,Ceiling-顶装
+ * @param installPosition 安装位置:Toilet-卫生间,Bedroom-卧室,LivingRoom-客厅,Restaurant-餐厅
+ * @param xxStart 雷达监测范围x开始,范围:-200-200 cm之内
+ * @param xxEnd 雷达监测范围x结束,范围:-200-200 cm之内,且xxEnd > xxStart
+ * @param yyStart 雷达监测范围y开始,范围:-250-250 cm之内
+ * @param yyEnd 雷达监测范围y结束,范围:-250-250 cm之内,且yyEnd > yyStart
+ * @param zzStart 雷达监测范围z开始,范围:0-5 cm之内
+ * @param zzEnd 雷达监测范围z结束,范围:200-300 cm之内
+ * @param fallingConfirm 设备跌倒确认时间 通常180s
+ */
+export interface UpdateDeviceParams {
+  clientId: string // 设备ID
+  userId: ID // 用户ID
+  tenantId?: ID // 租户ID
+  devName?: string // 设备名称:最多10个字符
+  height?: ID // 安装高度: 250 - 370 cm 之内
+  wifiName?: string // WIFI名称
+  wifiPassword?: string // WIFI密码
+  northAngle: NorthAngle // 北向夹角 允许传值: 0,90,180,270
+  mountPlain?: InstallWay // 安装方式:Wall-墙装,Ceiling-顶装
+  installPosition?: InstallPosition // 安装位置:Toilet-卫生间,Bedroom-卧室,LivingRoom-客厅,Restaurant-餐厅
+  xxStart?: ID // 雷达监测范围x开始,范围:-200-200 cm之内
+  xxEnd?: ID // 	雷达监测范围x结束,范围:-200-200 cm之内,且xxEnd > xxStart
+  yyStart?: ID // 	雷达监测范围y开始,范围:-250-250 cm之内
+  yyEnd?: ID // 雷达监测范围y结束,范围:-250-250 cm之内,且yyEnd > yyStart
+  zzStart?: ID // 雷达监测范围z开始,范围:0-5 cm之内
+  zzEnd?: ID // 雷达监测范围z结束,范围:200-300 cm之内
+  fallingConfirm?: ID // 设备跌倒确认时间 通常180s
+}

+ 6 - 0
src/api/pub/index.ts

@@ -0,0 +1,6 @@
+import request from '@/request'
+
+// 获取字典列表
+export const queryDictList = (params: { dicType: string }): Promise<ResponseData<object>> => {
+  return request.get('/pub/dic/query', { params, baseURL: '/portal-service-server' })
+}

+ 0 - 0
src/api/pub/types.ts


+ 18 - 0
src/api/room/index.ts

@@ -0,0 +1,18 @@
+import request from '@/request'
+import type * as TYPE from './types'
+
+/**
+ * 读取房间信息
+ */
+export const queryRoomInfo = (
+  params: TYPE.RoomInfoParams
+): Promise<ResponseData<TYPE.RoomData>> => {
+  return request.get('/room/readRoom', { params })
+}
+
+/**
+ * 房间信息保存
+ */
+export const saveRoomInfo = (params: TYPE.RoomData): Promise<ResponseData<TYPE.RoomData>> => {
+  return request.post('/room/saveRoom', params)
+}

+ 59 - 0
src/api/room/types.ts

@@ -0,0 +1,59 @@
+/**
+ * 房间信息请求参数
+ * @param devId 设备ID
+ */
+export interface RoomInfoParams {
+  devId: string // 设备ID
+}
+
+/**
+ * 家具类型
+ * @description: 床、沙发、凳子、桌子、水槽、马桶、橱柜、雷达等
+ */
+export type FurnitureType =
+  | 'bed'
+  | 'sofa'
+  | 'stool'
+  | 'table'
+  | 'sink'
+  | 'closestool'
+  | 'cabinet'
+  | 'radar'
+
+/**
+ * 家具信息
+ */
+export type Furniture = {
+  name: string // 名称
+  type: FurnitureType // 类型
+  width: number // 宽度
+  length: number // 长度
+  top: number // 距离顶点相对距离
+  left: number // 距离左侧边相对距离
+  rotate: 0 | 90 | 180 | 270 // 旋转角度: 0°,90°,180°,270°
+  x: number // 	距离雷达的X距离
+  y: number // 距离雷达的Y距离
+}
+
+// 类型继承自Furniture
+export interface SubRegions {
+  startXx: number
+  stopXx: number
+  startYy: number
+  stopYy: number
+  startZz: number
+  stopZz: number
+  isLowSnr: number
+  isDoor: number
+  presenceEnterDuration: number
+  presenceExitDuration: number
+  trackPresence: number // 是否开启区域跟踪存在 0-否,1-是
+  excludeFalling: number // 是否屏蔽区域跌倒检测 0-否,1-是
+}
+
+export interface RoomData {
+  roomId?: string // 房间ID
+  devId?: number | string // 设备ID
+  subRegions?: SubRegions[] | null // 屏蔽子区域信息
+  furnitures?: Furniture[] // 家具信息
+}

+ 23 - 0
src/api/stats/index.ts

@@ -0,0 +1,23 @@
+import request from '@/request'
+import type * as TYPE from './types'
+
+// 跌倒统计
+export const statsFallQuery = (
+  params: TYPE.StatsFallQueryParams
+): Promise<ResponseData<TYPE.StatsFallQueryData>> => {
+  return request.post('/stats/fallQuery', params)
+}
+
+// 异常滞留统计
+export const statsAlarmRetentionQuery = (
+  params: TYPE.StatsAlarmRetentionQueryParams
+): Promise<ResponseData<TYPE.StatsAlarmRetentionQueryData>> => {
+  return request.post('/stats/alarmRetentionQuery', params)
+}
+
+// 一般滞留统计
+export const statsGeneralRetentionQuery = (
+  params: TYPE.StatsGeneralRetentionQueryParams
+): Promise<ResponseData<TYPE.StatsGeneralRetentionQueryData>> => {
+  return request.post('/stats/generalRetentionQuery', params)
+}

+ 154 - 0
src/api/stats/types.ts

@@ -0,0 +1,154 @@
+/**
+ * 跌倒统计入参
+ * @description: 分页查询设备的跌倒统计信息
+ * @param devId 设备ID
+ * @param tenantId 租户id
+ * @param createTimeStart 起始时间 格式yyyy-MM-dd
+ * @param createTimeEnd 结束时间 格式yyyy-MM-dd
+ * @param pageNo 页码
+ * @param pageSize 每页条数
+ */
+export interface StatsFallQueryParams {
+  pageNo: number // 页码
+  pageSize: number // 每页条数
+  devId?: ID // 设备ID
+  tenantId?: ID // 租户id
+  createTimeStart: string // 起始时间 格式yyyy-MM-dd
+  createTimeEnd: string // 结束时间 格式yyyy-MM-dd
+}
+
+/**
+ * 跌倒统计出参 rows
+ * @description: 分页查询设备的跌倒统计信息
+ * @param createId 创建人
+ * @param updateId 修改人
+ * @param createTime 创建时间
+ * @param updateTime 更新时间
+ * @param isDeleted 删除标记:0-未删除,1-已删除
+ * @param remark 备注
+ * @param eventListId 事件主键
+ * @param devId 设备Id
+ * @param pose 姿态
+ * @param targetPoints target数组
+ * @param eventType 事件类型
+ * @param isHandle 是否处理:0-未处理,1-已处理
+ */
+export interface StatsFallQueryDataRow {
+  createId: ID // 创建人
+  updateId: ID // 修改人
+  createTime: string // 创建时间
+  updateTime: string // 更新时间
+  isDeleted: Switch // 删除标记:0-未删除,1-已删除
+  remark: ID // 备注
+  eventListId: ID // 事件主键
+  devId: ID // 设备Id
+  pose: ID // 姿态
+  poseName: string // 姿态名称
+  targetPoints: ID // target数组
+  eventType: ID // 事件类型
+  eventTypeName: string // 事件类型名称
+  isHandle: Switch // 是否处理:0-未处理,1-已处理
+}
+
+/**
+ * 跌倒统计出参
+ * @description: 分页查询设备的跌倒统计信息
+ */
+export interface StatsFallQueryData {
+  rows: StatsFallQueryDataRow[]
+  total: number
+  pageNum: number
+  pageSize: number
+  outTotalPageNum: boolean
+  totalPageNum: number
+}
+
+/**
+ * 异常滞留入参
+ * @description: 分页查询设备的异常滞留统计信息
+ * @param devId 设备ID
+ * @param tenantId 租户id
+ * @param createTimeStart 起始时间 格式yyyy-MM-dd
+ * @param createTimeEnd 结束时间 格式yyyy-MM-dd
+ * @param pageNo 页码
+ * @param pageSize 每页条数
+ * @param eventType 事件类型 0:异常滞留 1:异常消失 2:异常起夜 3:异常入厕
+ */
+export interface StatsAlarmRetentionQueryParams extends StatsFallQueryParams {
+  eventType: ID // 事件类型 0:异常滞留 1:异常消失 2:异常起夜 3:异常入厕
+}
+
+/**
+ * 异常滞留出参 rows
+ */
+export interface StatsAlarmRetentionQueryDataRow {
+  alarmEventId: ID // 告警表主键ID
+  devId: ID // 设备表主键ID
+  pose: string // 姿势
+  poseName: string // 姿势名称
+  stayTimeId: string // 事件表主键id
+  eventType: string // 告警类型
+  eventTypeName: string // 告警类型名称
+  isHandle: Switch // 是否处理:0-未处理,1-已处理
+}
+
+/**
+ * 异常滞留出参
+ */
+export interface StatsAlarmRetentionQueryData {
+  rows: StatsAlarmRetentionQueryDataRow[]
+  total: number
+  pageNum: number
+  pageSize: number
+  outTotalPageNum: boolean
+  totalPageNum: number
+}
+
+/**
+ * 一般滞留入参
+ * @description: 分页查询设备的一般滞留统计信息
+ * @param devId 设备ID
+ * @param tenantId 租户id
+ * @param createTimeStart 起始时间 格式yyyy-MM-dd
+ * @param createTimeEnd 结束时间 格式yyyy-MM-dd
+ * @param pageNo 页码
+ * @param pageSize 每页条数
+ * @param type 类型 0:一般滞留事件 2:马桶滞留事件
+ */
+export interface StatsGeneralRetentionQueryParams extends StatsFallQueryParams {
+  type: ID // 类型 0:一般滞留事件 2:马桶滞留事件
+}
+
+/**
+ * 一般滞留出参 rows
+ */
+export interface StatsGeneralRetentionQueryDataRows {
+  createId: ID // 创建人
+  updateId: ID // 修改人
+  createTime: string // 创建时间
+  updateTime: string // 更新时间
+  isDeleted: Switch // 	删除标记:0-未删除,1-已删除
+  remark: string // 备注
+  stayTimeId: ID // stay_time表主键id
+  devId: ID // 设备表主键id
+  clientId: ID // 设备clientId
+  devName: string // 设备名称
+  type: ID // 停留类型
+  typeName: string // 停留类型名称
+  enterTime: string // 进入时间
+  leaveTime: string // 离开时间
+  stayTime: number // 停留时长(秒)
+  childStayTime: StatsGeneralRetentionQueryDataRows[] // 子停留信息
+}
+
+/**
+ * 一般滞留出参
+ */
+export interface StatsGeneralRetentionQueryData {
+  rows: StatsGeneralRetentionQueryDataRows[]
+  total: number
+  pageNum: number
+  pageSize: number
+  outTotalPageNum: boolean
+  totalPageNum: number
+}

+ 15 - 0
src/api/system/index.ts

@@ -0,0 +1,15 @@
+import request from '@/request'
+
+/**
+ * 获取角色列表
+ */
+export const queryRoleList = (): Promise<
+  ResponseData<
+    {
+      roleName: string
+      roleId: string
+    }[]
+  >
+> => {
+  return request.get('/system/roleList')
+}

+ 0 - 0
src/api/system/types.ts


+ 92 - 0
src/api/tenant/index.ts

@@ -0,0 +1,92 @@
+import request from '@/request'
+import type * as TYPE from './types'
+
+/**
+ * 查询租户(小区列表)
+ */
+export const queryTenant = (params: {
+  pageNo: number
+  pageSize: number
+  tenantName?: string
+}): Promise<ResponseData<TYPE.queryTenantData>> => {
+  return request.post('/tenant/query', params)
+}
+
+/**
+ * 添加小区
+ */
+export const queryTenantAdd = (params: TYPE.TenantInfoAddParams): Promise<ResponseData<object>> => {
+  return request.post('/tenant/add', params)
+}
+
+/**
+ * 添加小区超管
+ */
+export const queryTenantAddAdmin = (params: {
+  account: string
+  password: string
+  phone?: string
+  roleId: string
+  tenantId: string | number
+  userName?: string
+  userId?: string | number | null
+}): Promise<ResponseData<object>> => {
+  return request.post('/tenant/addAdmin', params)
+}
+
+/**
+ * 查询小区超管
+ */
+export const queryTenantAdminInfo = (params: {
+  tenantId: string | number
+}): Promise<
+  ResponseData<{
+    account: string
+    password: string
+    userType: string
+    phone: string
+    userRole: {
+      roleId: string
+      roleName: string
+    }
+    userId: string
+  }>
+> => {
+  return request.post('/tenant/queryAdmin', params)
+}
+
+/**
+ * 查询小区详情信息
+ */
+export const queryTenantInfo = (params: {
+  tenantId: string
+}): Promise<ResponseData<TYPE.TenantInfoData[]>> => {
+  return request.get('/tenant/queryTenantInfo', { params })
+}
+
+/**
+ * 新增小区楼栋
+ */
+export const queryAddBuilding = (params: {
+  tenantId: string
+  buildingInfos: {
+    tinfoName: string
+    sort?: number
+  }[]
+}): Promise<ResponseData<object>> => {
+  return request.post('/tenant/addBuilding', params)
+}
+
+/**
+ * 新增小区楼层
+ */
+export const queryAddFloor = (params: {
+  tenantId: string
+  parentId: string
+  floorInfos: {
+    tinfoName: string
+    sort?: number
+  }[]
+}): Promise<ResponseData<object>> => {
+  return request.post('/tenant/addFloor', params)
+}

+ 67 - 0
src/api/tenant/types.ts

@@ -0,0 +1,67 @@
+/**
+ * 租户信息类型
+ */
+export interface TenantItem {
+  createId?: number // 创建人
+  updateId?: number // 修改人
+  createTime?: string // 创建时间
+  updateTime?: string // 更新时间
+  isDeleted?: 0 | 1 //  删除标记:0-未删除,1-已删除
+  remark?: string // 备注
+  tenantId?: number | string // 主键id
+  tenantCode?: string // 租户代码
+  tenantName: string // 租户名称
+  parentId?: number // 父租户 ID
+  tenantType?: string // 租户类型
+  sort?: number // 排序号
+  province: string // 租户地址--省
+  city: string // 租户地址--市
+  district: string // 租户地址--区
+  address: string // 租户地址--详细地址
+}
+
+/**
+ * 查询租户信息返回类型
+ */
+export type queryTenantData = {
+  rows: TenantItem[]
+  total: number // 总条数
+  pageNo: number // 当前页码
+  pageSize: number // 页面大小
+  outTotalPageNum: boolean
+  totalPageNum: number // 总页数
+}
+
+/**/
+export interface TenantInfoAddParams {
+  tenantCode: string // 租户代码
+  tenantName: string // 租户名称
+  parentId?: string // 父租户 ID
+  tenantType: string // 租户类型
+  sort?: string // 排序号
+  province?: string // 租户地址--省
+  city?: string // 租户地址--市
+  district?: string // 租户地址--区
+  address?: string // 租户地址--详细地址
+  tenantId?: string | number // 主键id
+  remark?: string // 备注
+}
+
+/**
+ * 查询租户详情返回类型
+ */
+export interface TenantInfoData {
+  createId: number // 创建人
+  updateId: number // 修改人
+  createTime: string // 创建时间
+  updateTime: string // 更新时间
+  isDeleted: 0 | 1 //  删除标记:0-未删除,1-已删除
+  remark: string // 备注
+  tinfoId: number // 主键id
+  tenantId: number // 主键id
+  tinfoType: string // 租户类型
+  tinfoName: string // 租户名称
+  parentId: number // 父租户 ID
+  sort: number // 排序号
+  children: TenantInfoData[] // 子租户信息
+}

+ 18 - 0
src/api/user/index.ts

@@ -0,0 +1,18 @@
+import request from '@/request'
+import type * as TYPE from './types'
+
+/**
+ * 用户登录
+ * @param account 账号
+ * @param password 密码
+ */
+export const login = (params: TYPE.LoginParams): Promise<ResponseData<TYPE.LoginResponseData>> => {
+  return request.post('/user/login', params)
+}
+
+/**
+ * 退出登录
+ */
+export const logout = (): Promise<ResponseData<object>> => {
+  return request.get('/user/logout')
+}

+ 41 - 0
src/api/user/types.ts

@@ -0,0 +1,41 @@
+/**
+ * 登录请求参数
+ * @param account 账号
+ * @param password 密码
+ */
+export interface LoginParams {
+  account: string
+  password: string
+}
+
+/**
+ * 登录响应数据
+ * @param tokenName token名称
+ * @param tokenValue token值
+ * @param isLogin 是否已登录
+ * @param loginId 此token对应的LoginId,未登录时为null
+ * @param loginType 多账号体系下的账号类型
+ * @param tokenTimeout token 剩余有效期(单位: 秒)
+ * @param sessionTimeout Account-Session 剩余有效时间(单位: 秒)
+ * @param tokenSessionTimeout Token-Session 剩余有效时间(单位: 秒)
+ * @param loginDeviceType 登录设备类型
+ * @param tags 自定义数据(暂无意义,留作扩展)
+ * @param phone 手机号
+ * @param userId userId
+ * @param account 账户
+ */
+export interface LoginResponseData {
+  tokenName: string // token名称
+  tokenValue: string // token值
+  isLogin: string // 是否已登录
+  loginId: string // 此token对应的LoginId,未登录时为null
+  loginType: string // 多账号体系下的账号类型
+  tokenTimeout: string // token 剩余有效期(单位: 秒)
+  sessionTimeout: string // Account-Session 剩余有效时间(单位: 秒)
+  tokenSessionTimeout: string // Token-Session 剩余有效时间(单位: 秒)
+  loginDeviceType: string // 登录设备类型
+  tags: string // 自定义数据(暂无意义,留作扩展)
+  phone: string // 手机号
+  userId: number // userId
+  account: string // 账户
+}

BIN
src/assets/furnitures/bath_basin.png


BIN
src/assets/furnitures/bath_door.png


BIN
src/assets/furnitures/bath_floor.png


BIN
src/assets/furnitures/bath_shower.png


BIN
src/assets/furnitures/bath_toilet.png


BIN
src/assets/furnitures/bed.png


BIN
src/assets/furnitures/bed_cabinet.png


BIN
src/assets/furnitures/bed_dressing_chair.png


BIN
src/assets/furnitures/bed_dressing_mirror.png


BIN
src/assets/furnitures/bed_floor.png


BIN
src/assets/furnitures/bed_table.png


BIN
src/assets/furnitures/dining_chair.png


BIN
src/assets/furnitures/dining_floor.png


BIN
src/assets/furnitures/dining_fridge.png


BIN
src/assets/furnitures/dining_table.png


BIN
src/assets/furnitures/dining_table_rect.png


BIN
src/assets/furnitures/living_bookcase.png


BIN
src/assets/furnitures/living_floor.png


BIN
src/assets/furnitures/living_sofa.png


BIN
src/assets/furnitures/living_sofa_single.jpg


BIN
src/assets/furnitures/living_tea_table.png


BIN
src/assets/furnitures/living_tv_stand.png


BIN
src/assets/furnitures/radar.png


BIN
src/assets/logo.png


+ 80 - 0
src/components/baseCard/components/e-charts/index.vue

@@ -0,0 +1,80 @@
+<template>
+  <div ref="echartsRef" class="echarts"> </div>
+</template>
+
+<script setup lang="ts">
+import * as echarts from 'echarts/core'
+import { TooltipComponent, LegendComponent } from 'echarts/components'
+import { PieChart } from 'echarts/charts'
+import { LabelLayout } from 'echarts/features'
+import { CanvasRenderer } from 'echarts/renderers'
+import { ref, onMounted } from 'vue'
+
+defineOptions({
+  name: 'ECharts',
+})
+
+type Props = {
+  data: {
+    value: string
+    name: string
+    itemStyle: {
+      color: string
+    }
+  }[]
+}
+
+const props = withDefaults(defineProps<Props>(), {
+  data: () => [],
+})
+
+const echartsRef = ref()
+echarts.use([TooltipComponent, LegendComponent, PieChart, CanvasRenderer, LabelLayout])
+
+const option = ref({
+  tooltip: {
+    trigger: 'item',
+  },
+  legend: {
+    orient: 'vertical',
+    right: 0,
+    top: 'center',
+  },
+  series: [
+    {
+      type: 'pie',
+      center: ['30%', '50%'],
+      radius: ['40%', '70%'],
+      avoidLabelOverlap: false,
+      label: {
+        show: false,
+      },
+      emphasis: {
+        label: {
+          show: false,
+        },
+      },
+      labelLine: {
+        show: false,
+      },
+      data: props.data,
+    },
+  ],
+})
+
+const initEcharts = () => {
+  const myChart = echarts.init(echartsRef.value)
+  myChart.setOption(option.value)
+}
+
+onMounted(() => {
+  initEcharts()
+})
+</script>
+
+<style scoped lang="less">
+.echarts {
+  width: 200px;
+  height: 150px;
+}
+</style>

+ 63 - 0
src/components/baseCard/index.vue

@@ -0,0 +1,63 @@
+<template>
+  <div class="baseCard">
+    <div class="text">
+      <div class="title">{{ props.count }}</div>
+      <div class="subTitle">{{ props.title }}</div>
+    </div>
+    <e-charts :data="props.data"></e-charts>
+  </div>
+</template>
+
+<script setup lang="ts">
+defineOptions({
+  name: 'BaseCard',
+})
+
+type Props = {
+  count: number
+  title: string
+  data: {
+    value: string
+    name: string
+    itemStyle: {
+      color: string
+    }
+  }[]
+}
+
+const props = withDefaults(defineProps<Props>(), {
+  count: 123,
+  title: '设备数量',
+  data: () => [],
+})
+</script>
+
+<style scoped lang="less">
+.baseCard {
+  width: 250px;
+  font-size: 14px;
+  border-radius: 8px;
+  border: 1px solid #d9d9d9;
+  background-color: #fff;
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  padding: 12px;
+
+  .text {
+    flex-shrink: 0;
+    flex-grow: 1;
+    text-align: center;
+
+    .title {
+      font-size: 32px;
+      margin-bottom: 8px;
+    }
+
+    .subTitle {
+      font-size: 16px;
+      color: #666;
+    }
+  }
+}
+</style>

+ 63 - 0
src/components/basePagination/index.vue

@@ -0,0 +1,63 @@
+<template>
+  <div class="basePagination">
+    <a-pagination
+      :current="current"
+      :page-size="pageSize"
+      :defaultPageSize="defaultPageSize"
+      :hideOnSinglePage="hideOnSinglePage"
+      :total="total"
+      :show-total="(total: number) => `共 ${total} 条`"
+      @change="change"
+      @showSizeChange="showSizeChange"
+    />
+  </div>
+</template>
+
+<script setup lang="ts">
+defineOptions({
+  name: 'BasePagination',
+})
+
+type Props = {
+  current: number
+  pageSize: number
+  defaultPageSize?: number
+  hideOnSinglePage?: boolean
+  total?: number
+}
+const emit = defineEmits<{
+  (e: 'change', current: number, pageSize: number): void
+  (e: 'showSizeChange', current: number, pageSize: number): void
+  (e: 'update:current', value: number): void
+  (e: 'update:pageSize', value: number): void
+}>()
+
+// eslint-disable-next-line @typescript-eslint/no-unused-vars
+const props = withDefaults(defineProps<Props>(), {
+  current: 1,
+  pageSize: 10,
+  defaultPageSize: 10,
+  hideOnSinglePage: true,
+  total: 20,
+})
+
+const change = (current: number, pageSize: number) => {
+  emit('update:current', current)
+  emit('update:pageSize', pageSize)
+  emit('change', current, pageSize)
+}
+
+const showSizeChange = (current: number, pageSize: number) => {
+  emit('update:current', current)
+  emit('update:pageSize', pageSize)
+  emit('showSizeChange', current, pageSize)
+}
+</script>
+
+<style scoped lang="less">
+.basePagination {
+  width: 100%;
+  padding: 20px;
+  text-align: right;
+}
+</style>

+ 102 - 0
src/components/baseWeather/index.vue

@@ -0,0 +1,102 @@
+<template>
+  <div class="baseWeather" @click="clickHandler">
+    <div v-if="props.mode === 'text'" class="mode-text">
+      <i :class="`qi-${weatherIcon}`"> </i>
+      <span>{{ weatherText }}</span>
+      <span>{{ weatherTemp }}℃</span>
+    </div>
+    <div v-if="props.mode === 'card'" class="mode-card">
+      <i :class="`qi-${weatherIcon}`" class="icon"> </i>
+      <div class="desc">
+        <span>{{ weatherText }}</span>
+        <span>{{ weatherTemp }}℃</span>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script setup lang="ts">
+import { ref } from 'vue'
+
+defineOptions({
+  name: 'BaseWeather',
+})
+
+type Props = {
+  mode: 'text' | 'card'
+}
+const props = withDefaults(defineProps<Props>(), {
+  mode: 'text',
+})
+
+const weatherIcon = ref('') // 天气图标
+const weatherTemp = ref('') // 天气温度
+const weatherFeelsLike = ref('') // 体感温度
+const weatherText = ref('') // 天气描述
+const weatherwindDir = ref('') // 天气风向
+const weatherWindSpeed = ref('') // 风速
+const fxLink = ref('') // 天气链接
+
+const getWeather = async () => {
+  try {
+    const response = await fetch(
+      'https://kp36x7gr79.re.qweatherapi.com/v7/weather/now?location=101220101',
+      {
+        headers: {
+          'X-QW-Api-Key': '72afe228cd7f436ebea7caa1345956f2',
+        },
+      }
+    )
+
+    if (!response.ok) {
+      throw new Error(`HTTP error! status: ${response.status}`)
+    }
+
+    const data = await response.json()
+    console.log('天气数据:', data.now)
+    fxLink.value = data.fxLink
+    weatherIcon.value = data.now.icon
+    weatherTemp.value = data.now.temp
+    weatherFeelsLike.value = data.now.feelsLike
+    weatherText.value = data.now.text
+    weatherwindDir.value = data.now.windDir
+    weatherWindSpeed.value = data.now.windSpeed
+  } catch (error) {
+    console.error('获取天气失败:', error)
+  }
+}
+getWeather()
+
+const clickHandler = () => {
+  window.open(fxLink.value, '_blank')
+}
+</script>
+
+<style scoped lang="less">
+.baseWeather {
+  padding: 0 10px;
+  .mode-text {
+    display: flex;
+    align-items: center;
+    gap: 5px;
+    cursor: pointer;
+  }
+
+  .mode-card {
+    display: flex;
+    flex-direction: column;
+    align-items: center;
+    cursor: pointer;
+
+    .icon {
+      font-size: 32px;
+      line-height: 50px;
+      margin-right: 8px;
+    }
+    .desc {
+      font-size: 18px;
+      margin-bottom: 8px;
+    }
+  }
+}
+</style>

+ 45 - 0
src/components/furnitureIcon/index.vue

@@ -0,0 +1,45 @@
+<template>
+  <div
+    class="furnitureIcon"
+    :style="{ width: `${props.width}px`, height: `${props.height}px` }"
+    tabindex="0"
+    :draggable="props.draggable"
+  >
+    <img :src="furnitureIconUrlMap[props.icon]" alt="" class="img" :draggable="false" />
+  </div>
+</template>
+
+<script setup lang="ts">
+import type { FurnitureIconType } from '@/types/furniture'
+import { furnitureIconUrlMap } from '@/const/furniture'
+
+defineOptions({
+  name: 'FurnitureIcon',
+})
+
+type Props = {
+  icon: FurnitureIconType
+  width?: number
+  height?: number
+  draggable?: boolean
+}
+
+const props = withDefaults(defineProps<Props>(), {
+  icon: 'bed',
+  width: 50,
+  height: 50,
+  draggable: true,
+})
+</script>
+
+<style scoped lang="less">
+.furnitureIcon {
+  flex-shrink: 0;
+  user-select: none;
+  pointer-events: none;
+  .img {
+    width: 100%;
+    height: 100%;
+  }
+}
+</style>

+ 79 - 0
src/components/furnitureItem/index.vue

@@ -0,0 +1,79 @@
+<template>
+  <div class="furnitureItem">
+    <furniture-icon :icon="props.icon" :width="width" :height="height" />
+    <div class="contant">
+      <span class="text">{{ furnitureIconNameMap[props.icon] }}</span>
+      <div class="action" @click="addHandler">
+        <slot name="action">
+          <PlusCircleOutlined />
+        </slot>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script setup lang="ts">
+import type { FurnitureIconType } from '@/types/furniture'
+import { furnitureIconNameMap } from '@/const/furniture'
+import { PlusCircleOutlined } from '@ant-design/icons-vue'
+
+defineOptions({
+  name: 'FurnitureItem',
+})
+
+type Props = {
+  icon: FurnitureIconType
+  width?: number
+  height?: number
+}
+
+const emit = defineEmits<{
+  (e: 'add', icon: FurnitureIconType): void
+}>()
+const props = withDefaults(defineProps<Props>(), {
+  icon: 'bed',
+  width: 30,
+  height: 30,
+})
+
+const addHandler = () => {
+  emit('add', props.icon)
+}
+</script>
+
+<style scoped lang="less">
+.furnitureItem {
+  display: flex;
+  align-items: center;
+  padding: 5px;
+  .text {
+    font-size: 14px;
+    user-select: none;
+  }
+
+  .contant {
+    display: flex;
+    align-items: center;
+    justify-content: space-between;
+    width: 100%;
+    padding: 0 8px;
+
+    .action {
+      cursor: pointer;
+      font-size: 16px;
+      transition: color 0.2s;
+    }
+  }
+  .contant:hover {
+    color: #1890ff;
+    .action:hover {
+      color: green;
+    }
+  }
+  &:hover {
+    box-shadow: 0 4px 12px rgba(220, 220, 220, 0.7);
+    box-sizing: border-box;
+    border-radius: 5px;
+  }
+}
+</style>

+ 52 - 0
src/components/furnitureList/index.vue

@@ -0,0 +1,52 @@
+<template>
+  <div class="furnitureList">
+    <furniture-item
+      class="furniture-item"
+      v-for="(icon, index) in props.icons"
+      :key="index"
+      :icon="icon"
+      :width="width"
+      :height="height"
+      @add="addHandler(icon)"
+    >
+    </furniture-item>
+  </div>
+</template>
+
+<script setup lang="ts">
+import type { FurnitureIconType } from '@/types/furniture'
+
+defineOptions({
+  name: 'FurnitureList',
+})
+
+type Props = {
+  icons: FurnitureIconType[]
+  width?: number
+  height?: number
+}
+
+const emit = defineEmits<{
+  (e: 'add', icon: FurnitureIconType): void
+}>()
+const props = withDefaults(defineProps<Props>(), {
+  icons: () => [],
+  width: 30,
+  height: 30,
+})
+
+const addHandler = (icon: FurnitureIconType) => {
+  emit('add', icon)
+}
+</script>
+
+<style scoped lang="less">
+.furnitureList {
+  .furniture-item {
+    margin-bottom: 8px;
+  }
+  .furniture-item:last-child {
+    margin-bottom: 0;
+  }
+}
+</style>

+ 82 - 0
src/components/rangePicker/index.vue

@@ -0,0 +1,82 @@
+<template>
+  <a-range-picker
+    v-model:value="rangeTimes"
+    :show-time="props.showTime"
+    :valueFormat="props.valueFormat"
+    :presets="props.rangePresets"
+  />
+</template>
+
+<script setup lang="ts">
+import { computed } from 'vue'
+import dayjs, { type Dayjs } from 'dayjs'
+
+defineOptions({
+  name: 'rangePicker',
+})
+
+type RangeValue = [Dayjs, Dayjs]
+interface Props {
+  start: string | Dayjs // 开始时间
+  end: string | Dayjs // 结束时间
+  showTime?: boolean // 是否显示时间选择器
+  valueFormat?: string // 时间格式
+  rangePresets?: {
+    label: string
+    value: RangeValue
+  }[]
+}
+
+const emit = defineEmits<{
+  (e: 'update:start', value: string): void
+  (e: 'update:end', value: string): void
+}>()
+const props = withDefaults(defineProps<Props>(), {
+  start: '',
+  end: '',
+  showTime: false,
+  valueFormat: 'YYYY-MM-DD', // 时间格式 YYYY-MM-DD HH:mm:ss
+  rangePresets: () => [
+    { label: '今天', value: [dayjs().startOf('day'), dayjs().endOf('day')] },
+    { label: '最近7天', value: [dayjs().add(-6, 'd').startOf('day'), dayjs().endOf('day')] },
+    { label: '最近14天', value: [dayjs().add(-13, 'd').startOf('day'), dayjs().endOf('day')] },
+    { label: '最近30天', value: [dayjs().add(-29, 'd').startOf('day'), dayjs().endOf('day')] },
+    { label: '最近90天', value: [dayjs().add(-89, 'd').startOf('day'), dayjs().endOf('day')] },
+  ],
+})
+
+const rangeTimes = computed({
+  get: () => {
+    if (props.start && props.end) {
+      const isValidRange = [props.start, props.end].every((v) => dayjs(v).isValid())
+
+      return isValidRange
+        ? [
+            dayjs.isDayjs(props.start) ? props.start : dayjs(props.start),
+            dayjs.isDayjs(props.end) ? props.end : dayjs(props.end),
+          ]
+        : undefined
+    }
+    return undefined
+  },
+  set: (val: RangeValue | undefined) => {
+    if (Array.isArray(val) && val.length === 2) {
+      const start = dayjs(val[0])
+      const end = dayjs(val[1])
+
+      const format = (d: Dayjs) => {
+        const date = dayjs(d)
+        return date.isValid() ? date.format(props.valueFormat) : ''
+      }
+
+      emit('update:start', start.isValid() ? format(val[0]) : '')
+      emit('update:end', end.isValid() ? format(val[1]) : '')
+    } else {
+      emit('update:start', '')
+      emit('update:end', '')
+    }
+  },
+})
+</script>
+
+<style scoped lang="less"></style>

+ 23 - 0
src/const/device.ts

@@ -0,0 +1,23 @@
+// 设备在离线状态映射
+export const deviceOnlineStateMap = {
+  0: {
+    name: '离线',
+    color: 'red',
+  },
+  1: {
+    name: '在线',
+    color: 'green',
+  },
+  2: {
+    name: '未激活',
+    color: 'gray',
+  },
+}
+
+// 设备安装位置映射
+export const deviceInstallPositionNameMap = {
+  Toilet: '卫生间',
+  Bedroom: '卧室',
+  LivingRoom: '客厅',
+  Restaurant: '餐厅',
+}

+ 179 - 0
src/const/furniture.ts

@@ -0,0 +1,179 @@
+// 卫生间家具
+import bathBasinUrl from '@/assets/furnitures/bath_basin.png'
+import bathDoorUrl from '@/assets/furnitures/bath_door.png'
+import bathFloorUrl from '@/assets/furnitures/bath_floor.png'
+import bathShowerUrl from '@/assets/furnitures/bath_shower.png'
+import bathToiletUrl from '@/assets/furnitures/bath_toilet.png'
+
+// 卧室家具
+import bedCabinetUrl from '@/assets/furnitures/bed_cabinet.png'
+import bedDressingChairUrl from '@/assets/furnitures/bed_dressing_chair.png'
+import bedDressingMirrorUrl from '@/assets/furnitures/bed_dressing_mirror.png'
+import bedFloorUrl from '@/assets/furnitures/bed_floor.png'
+import bedTableUrl from '@/assets/furnitures/bed_table.png'
+import bedUrl from '@/assets/furnitures/bed.png'
+
+// 餐厅家具
+import diningChairUrl from '@/assets/furnitures/dining_chair.png'
+import diningFloorUrl from '@/assets/furnitures/dining_floor.png'
+import diningFridgeUrl from '@/assets/furnitures/dining_fridge.png'
+import diningTableRectUrl from '@/assets/furnitures/dining_table_rect.png'
+import diningTableUrl from '@/assets/furnitures/dining_table.png'
+
+// 客厅家具
+import livingFloorBookcaseUrl from '@/assets/furnitures/living_bookcase.png'
+import livingFloorUrl from '@/assets/furnitures/living_floor.png'
+import livingSofaUrl from '@/assets/furnitures/living_sofa.png'
+import livingSofaSingleUrl from '@/assets/furnitures/living_sofa_single.jpg'
+import livingTeaTableUrl from '@/assets/furnitures/living_tea_table.png'
+import livingTvStandUrl from '@/assets/furnitures/living_tv_stand.png'
+
+// 其他
+import radarUrl from '@/assets/furnitures/radar.png'
+
+export const furnitureIconUrlMap = {
+  bed: bedUrl,
+  bed_cabinet: bedCabinetUrl,
+  bed_dressing_chair: bedDressingChairUrl,
+  bed_dressing_mirror: bedDressingMirrorUrl,
+  bed_floor: bedFloorUrl,
+  bed_table: bedTableUrl,
+  bath_basin: bathBasinUrl,
+  bath_door: bathDoorUrl,
+  bath_floor: bathFloorUrl,
+  bath_shower: bathShowerUrl,
+  bath_toilet: bathToiletUrl,
+  dining_chair: diningChairUrl,
+  dining_floor: diningFloorUrl,
+  dining_fridge: diningFridgeUrl,
+  dining_table_rect: diningTableRectUrl,
+  dining_table: diningTableUrl,
+  living_bookcase: livingFloorBookcaseUrl,
+  living_floor: livingFloorUrl,
+  living_sofa: livingSofaUrl,
+  living_sofa_single: livingSofaSingleUrl,
+  living_tea_table: livingTeaTableUrl,
+  living_tv_stand: livingTvStandUrl,
+  radar: radarUrl,
+}
+
+export const furnitureIconNameMap = {
+  bed: '床',
+  bed_cabinet: '柜子',
+  bed_dressing_chair: '化妆椅',
+  bed_dressing_mirror: '化妆镜',
+  bed_floor: '地板',
+  bed_table: '床头柜',
+  bath_basin: '脸盆',
+  bath_door: '门',
+  bath_floor: '地板',
+  bath_shower: '淋浴',
+  bath_toilet: '马桶',
+  dining_table_rect: '方形餐桌',
+  dining_table: '餐桌',
+  dining_chair: '餐椅',
+  dining_floor: '地板',
+  dining_fridge: '冰箱',
+  living_bookcase: '书柜',
+  living_floor: '地板',
+  living_sofa: '沙发',
+  living_sofa_single: '单人沙发',
+  living_tea_table: '茶几',
+  living_tv_stand: '电视柜',
+  radar: '雷达',
+}
+
+export const furnitureIconSizeMap = {
+  bed: {
+    width: 80,
+    height: 70,
+  },
+  bed_cabinet: {
+    width: 40,
+    height: 80,
+  },
+  bed_dressing_chair: {
+    width: 40,
+    height: 40,
+  },
+  bed_dressing_mirror: {
+    width: 40,
+    height: 80,
+  },
+  bed_floor: {
+    width: 40,
+    height: 40,
+  },
+  bed_table: {
+    width: 40,
+    height: 40,
+  },
+  bath_basin: {
+    width: 40,
+    height: 80,
+  },
+  bath_door: {
+    width: 40,
+    height: 40,
+  },
+  bath_floor: {
+    width: 40,
+    height: 40,
+  },
+  bath_shower: {
+    width: 40,
+    height: 60,
+  },
+  bath_toilet: {
+    width: 40,
+    height: 40,
+  },
+  dining_table_rect: {
+    width: 40,
+    height: 40,
+  },
+  dining_table: {
+    width: 40,
+    height: 80,
+  },
+  dining_chair: {
+    width: 40,
+    height: 80,
+  },
+  dining_floor: {
+    width: 40,
+    height: 40,
+  },
+  dining_fridge: {
+    width: 40,
+    height: 80,
+  },
+  living_bookcase: {
+    width: 40,
+    height: 80,
+  },
+  living_floor: {
+    width: 40,
+    height: 40,
+  },
+  living_sofa: {
+    width: 40,
+    height: 80,
+  },
+  living_sofa_single: {
+    width: 40,
+    height: 40,
+  },
+  living_tea_table: {
+    width: 40,
+    height: 80,
+  },
+  living_tv_stand: {
+    width: 40,
+    height: 80,
+  },
+  radar: {
+    width: 40,
+    height: 40,
+  },
+}

+ 61 - 0
src/const/menus.ts

@@ -0,0 +1,61 @@
+import {
+  // PieChartOutlined,
+  DatabaseOutlined,
+  InsertRowLeftOutlined,
+  // AlertOutlined,
+  // UserOutlined,
+  // SettingOutlined,
+} from '@ant-design/icons-vue'
+import { h, ref } from 'vue'
+
+export const menus = ref([
+  // {
+  //   key: 'home',
+  //   icon: () => h(PieChartOutlined),
+  //   label: '首页看板',
+  //   title: '首页看板',
+  // },
+  // {
+  //   key: 'device',
+  //   icon: () => h(DatabaseOutlined),
+  //   label: '设备管理',
+  //   title: '设备管理',
+  // },
+  {
+    key: 'community',
+    icon: () => h(InsertRowLeftOutlined),
+    label: '小区管理',
+    title: '小区管理',
+  },
+  {
+    key: 'device',
+    icon: () => h(DatabaseOutlined),
+    label: '设备管理',
+    title: '设备管理',
+  },
+  // {
+  //   key: 'alarm',
+  //   icon: () => h(AlertOutlined),
+  //   label: '告警管理',
+  //   title: '告警管理',
+  // },
+  // {
+  //   key: 'user',
+  //   icon: () => h(UserOutlined),
+  //   label: '用户管理',
+  //   title: '用户管理',
+  // },
+  // {
+  //   key: 'system',
+  //   icon: () => h(SettingOutlined),
+  //   label: '系统管理',
+  //   title: '系统管理',
+  //   children: [
+  //     {
+  //       key: 'community',
+  //       label: '开通小区',
+  //       title: '开通小区',
+  //     },
+  //   ],
+  // },
+])

+ 40 - 0
src/hooks/useDict.ts

@@ -0,0 +1,40 @@
+import { ref } from 'vue'
+import * as pubApi from '@/api/pub'
+
+export interface DictItem {
+  label: string
+  value: ID
+}
+
+/**
+ * 获取字典值列表
+ * @param dicType 字典类型
+ * @example 使用示例
+ * const { dictList, fetchDict } = useDict('DIC_TYPE')
+ * @enum { string } institution_type 机构类型
+ */
+export const useDict = (dicType: string) => {
+  const dictList = ref<DictItem[]>([])
+
+  const fetchDict = async () => {
+    console.log('👏👏👏useDict fetchDict', dicType)
+    try {
+      const res = await pubApi.queryDictList({ dicType })
+      const data = res.data || []
+      dictList.value = Array.isArray(data)
+        ? data.map((item) => ({
+            label: item.itemName,
+            value: item.itemCode,
+          }))
+        : []
+    } catch (error) {
+      console.error('获取字典数据失败:', error)
+      dictList.value = []
+    }
+  }
+
+  return {
+    dictList,
+    fetchDict,
+  }
+}

+ 18 - 0
src/hooks/useDictName.ts

@@ -0,0 +1,18 @@
+import type { Ref } from 'vue'
+
+/**
+ * 字典映射
+ * @param dictList 字典列表
+ * @example 使用示例
+ * const { dictNameMap } = useDictName(dictList)
+ * const label = dictNameMap('value')
+ */
+export const useDictName = (dictList: Ref<Array<{ label: string; value: ID }>>) => {
+  const dictNameMap = (value: string) => {
+    return dictList.value.find((item) => item.value === value)?.label || value
+  }
+
+  return {
+    dictNameMap,
+  }
+}

+ 32 - 0
src/hooks/useSearch.ts

@@ -0,0 +1,32 @@
+/* eslint-disable @typescript-eslint/no-unused-vars */
+import { ref, reactive, watch, watchEffect } from 'vue'
+import type { Ref } from 'vue'
+import { cloneDeep } from 'lodash-es'
+
+type SearchOptions = {
+  deepClone?: boolean // 是否使用深拷贝 默认 true
+  beforeReset?: () => void // 重置前钩子
+  afterReset?: () => void // 重置后钩子
+}
+
+export function useSearch<T extends object>(initialData: T, options: SearchOptions = {}) {
+  const { deepClone = true, beforeReset, afterReset } = options
+
+  const clone = deepClone ? cloneDeep : (obj: T) => ({ ...obj })
+
+  const initialState = clone(initialData)
+  const state = reactive(clone(initialData)) as T
+
+  // 监听外部初始数据变化
+  watchEffect(() => {
+    Object.assign(initialState, clone(initialData))
+  })
+
+  const reset = () => {
+    beforeReset?.()
+    Object.assign(state, clone(initialState))
+    afterReset?.()
+  }
+
+  return [state, reset] as const
+}

+ 368 - 0
src/layout/components/alertModal/index.vue

@@ -0,0 +1,368 @@
+<template>
+  <div v-if="props.open" class="alertModal">
+    <div class="alertModal-contant">
+      <div class="scan-line"></div>
+      <div class="grid-overlay"></div>
+      <div class="corner-decoration corner-tl"></div>
+      <div class="corner-decoration corner-tr"></div>
+      <div class="corner-decoration corner-bl"></div>
+      <div class="corner-decoration corner-br"></div>
+      <div class="status-led"></div>
+
+      <div class="alert-header">
+        <div class="alert-icon">
+          <WarningOutlined />
+        </div>
+        <div>
+          <h1 class="alert-title">跌倒警报</h1>
+          <p class="alert-subtitle">系统检测到跌倒事件,请及时处理!</p>
+        </div>
+      </div>
+
+      <div class="alert-body">
+        <div class="alert-details">
+          <div class="detail-row">
+            <div class="detail-label">设备名称</div>
+            <div class="detail-value">{{ props.data.devName }}</div>
+          </div>
+          <div class="detail-row">
+            <div class="detail-label">小区名称</div>
+            <div class="detail-value">{{ props.data.tenantName }}</div>
+          </div>
+          <div class="detail-row">
+            <div class="detail-label">设备ID</div>
+            <div class="detail-value">{{ props.data.clientId }}</div>
+          </div>
+        </div>
+      </div>
+
+      <div class="alert-footer">
+        <button class="btn btn-acknowledge" @click="close"> 立即处理 </button>
+        <button class="btn btn-view-details" @click="close"> 稍后处理 </button>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script setup lang="ts">
+import { defineProps, defineEmits, withDefaults } from 'vue'
+import { WarningOutlined } from '@ant-design/icons-vue'
+
+defineOptions({
+  name: 'AlertModal',
+})
+
+type Props = {
+  open: boolean
+  data: {
+    devName: string
+    tenantName: string
+    clientId: string
+  }
+}
+const emit = defineEmits<{
+  (e: 'update:open', value: boolean): void
+}>()
+
+const props = withDefaults(defineProps<Props>(), {
+  open: false,
+  data: () => ({
+    devName: '',
+    tenantName: '',
+    clientId: '',
+  }),
+})
+
+const close = () => {
+  emit('update:open', false)
+}
+</script>
+
+<style scoped lang="less">
+.alertModal {
+  position: fixed;
+  top: 0;
+  left: 0;
+  inset: 0;
+  background-color: rgba(0, 0, 0, 0.7);
+  z-index: 10000;
+
+  &-contant {
+    top: 50%;
+    left: 50%;
+    transform: translate(-50%, -50%);
+    padding: 20px;
+    border-radius: 10px;
+    z-index: 10001;
+    width: 500px;
+
+    background: rgba(10, 15, 35, 0.9);
+    border: 1px solid rgba(0, 195, 255, 0.3);
+    box-shadow:
+      0 0 20px rgba(0, 195, 255, 0.5),
+      0 0 40px rgba(0, 89, 255, 0.3),
+      inset 0 0 15px rgba(0, 195, 255, 0.2);
+    overflow: hidden;
+    position: relative;
+    backdrop-filter: blur(10px);
+    transition: all 0.4s ease;
+
+    .alert-header {
+      background: linear-gradient(90deg, rgba(0, 80, 120, 0.7) 0%, rgba(0, 150, 200, 0.5) 100%);
+      padding: 20px;
+      position: relative;
+      display: flex;
+      align-items: center;
+      border-bottom: 1px solid rgba(0, 195, 255, 0.3);
+    }
+
+    .alert-icon {
+      width: 60px;
+      height: 60px;
+      background: rgba(255, 40, 40, 0.2);
+      border-radius: 50%;
+      display: flex;
+      justify-content: center;
+      align-items: center;
+      margin-right: 20px;
+      animation: pulse 2s infinite;
+    }
+
+    .alert-icon {
+      font-size: 30px;
+      color: #ff2a2a;
+      animation: shake 0.8s infinite;
+    }
+
+    @keyframes pulse {
+      0% {
+        box-shadow: 0 0 0 0 rgba(255, 40, 40, 0.4);
+      }
+      70% {
+        box-shadow: 0 0 0 15px rgba(255, 40, 40, 0);
+      }
+      100% {
+        box-shadow: 0 0 0 0 rgba(255, 40, 40, 0);
+      }
+    }
+
+    @keyframes shake {
+      0% {
+        transform: translateX(0);
+      }
+      25% {
+        transform: translateX(-3px);
+      }
+      50% {
+        transform: translateX(3px);
+      }
+      75% {
+        transform: translateX(-3px);
+      }
+      100% {
+        transform: translateX(0);
+      }
+    }
+
+    @keyframes rotate {
+      0% {
+        transform: rotate(0deg);
+      }
+      100% {
+        transform: rotate(360deg);
+      }
+    }
+
+    .alert-title {
+      font-size: 24px;
+      font-weight: 700;
+      color: #ff4d4d;
+      text-shadow: 0 0 10px rgba(255, 77, 77, 0.7);
+      letter-spacing: 1px;
+    }
+
+    .alert-subtitle {
+      font-size: 16px;
+      color: #8fbcff;
+      margin-top: 5px;
+    }
+
+    .alert-body {
+      padding: 25px;
+    }
+
+    .alert-content {
+      margin-bottom: 25px;
+      line-height: 1.6;
+    }
+
+    .alert-details {
+      background: rgba(0, 30, 60, 0.4);
+      border-radius: 10px;
+      padding: 15px;
+      margin: 20px 0;
+      border-left: 3px solid #00c3ff;
+    }
+
+    .detail-row {
+      display: flex;
+      padding: 8px 0;
+      border-bottom: 1px solid rgba(0, 195, 255, 0.1);
+    }
+
+    .detail-row:last-child {
+      border-bottom: none;
+    }
+
+    .detail-label {
+      width: 40%;
+      color: #8fbcff;
+      font-weight: 500;
+    }
+
+    .detail-value {
+      width: 60%;
+      color: #ffffff;
+      font-weight: 600;
+    }
+
+    .critical {
+      color: #ff4d4d;
+      text-shadow: 0 0 5px rgba(255, 77, 77, 0.5);
+    }
+
+    .alert-message {
+      background: rgba(255, 40, 40, 0.1);
+      border: 1px solid rgba(255, 77, 77, 0.3);
+      border-radius: 8px;
+      padding: 15px;
+      margin: 20px 0;
+      color: #ff9999;
+      line-height: 1.6;
+    }
+
+    .alert-footer {
+      display: flex;
+      justify-content: flex-end;
+      padding: 20px;
+      background: rgba(0, 20, 40, 0.5);
+      border-top: 1px solid rgba(0, 195, 255, 0.2);
+    }
+
+    .btn {
+      padding: 12px 30px;
+      border-radius: 8px;
+      font-weight: 600;
+      font-size: 16px;
+      cursor: pointer;
+      transition: all 0.3s ease;
+      border: none;
+      outline: none;
+      letter-spacing: 1px;
+      margin-left: 15px;
+    }
+
+    .btn-acknowledge {
+      background: linear-gradient(90deg, #00c3ff 0%, #0077ff 100%);
+      color: white;
+      box-shadow: 0 0 15px rgba(0, 195, 255, 0.4);
+    }
+
+    .btn-acknowledge:hover {
+      background: linear-gradient(90deg, #00d9ff 0%, #0088ff 100%);
+      box-shadow: 0 0 20px rgba(0, 195, 255, 0.7);
+      // transform: translateY(-2px);
+    }
+
+    .btn-view-details {
+      background: transparent;
+      color: #00c3ff;
+      border: 1px solid #00c3ff;
+    }
+
+    .btn-view-details:hover {
+      background: rgba(0, 195, 255, 0.1);
+      box-shadow: 0 0 15px rgba(0, 195, 255, 0.3);
+    }
+
+    .grid-overlay {
+      position: absolute;
+      top: 0;
+      left: 0;
+      width: 100%;
+      height: 100%;
+      background:
+        linear-gradient(rgba(0, 195, 255, 0.05) 1px, transparent 1px),
+        linear-gradient(90deg, rgba(0, 195, 255, 0.05) 1px, transparent 1px);
+      background-size: 20px 20px;
+      pointer-events: none;
+      z-index: -1;
+    }
+
+    .corner-decoration {
+      position: absolute;
+      width: 15px;
+      height: 15px;
+    }
+
+    .corner-tl {
+      top: 0;
+      left: 0;
+      border-top: 2px solid #00c3ff;
+      border-left: 2px solid #00c3ff;
+    }
+
+    .corner-tr {
+      top: 0;
+      right: 0;
+      border-top: 2px solid #00c3ff;
+      border-right: 2px solid #00c3ff;
+    }
+
+    .corner-bl {
+      bottom: 0;
+      left: 0;
+      border-bottom: 2px solid #00c3ff;
+      border-left: 2px solid #00c3ff;
+    }
+
+    .corner-br {
+      bottom: 0;
+      right: 0;
+      border-bottom: 2px solid #00c3ff;
+      border-right: 2px solid #00c3ff;
+    }
+
+    .scan-line {
+      position: absolute;
+      top: 0;
+      left: 0;
+      width: 100%;
+      height: 3px;
+      background: linear-gradient(90deg, transparent, rgba(0, 195, 255, 0.7), transparent);
+      animation: scan 4s linear infinite;
+    }
+
+    @keyframes scan {
+      0% {
+        top: 0;
+      }
+      100% {
+        top: 100%;
+      }
+    }
+
+    .status-led {
+      position: absolute;
+      top: 25px;
+      right: 25px;
+      width: 12px;
+      height: 12px;
+      background-color: #ff2a2a;
+      border-radius: 50%;
+      box-shadow: 0 0 10px #ff2a2a;
+      animation: blink 1s infinite;
+    }
+  }
+}
+</style>

+ 62 - 0
src/layout/components/timeNow/index.vue

@@ -0,0 +1,62 @@
+<template>
+  <div class="timeNow">
+    <span class="item date">{{ timeNow.date }}</span>
+    <span class="item week">{{ timeNow.week }}</span>
+    <span class="item time">{{ timeNow.time }}</span>
+  </div>
+</template>
+
+<script setup lang="ts">
+import { ref, onUnmounted } from 'vue'
+
+defineOptions({
+  name: 'timeNow',
+})
+
+// 获取现在的时间
+const getNowTime = () => {
+  const now = new Date()
+  // const year = now.getFullYear()
+  const month = now.getMonth() + 1
+  const day = now.getDate()
+  const week = now.getDay()
+  const hour = now.getHours().toString().padStart(2, '0')
+  const minute = now.getMinutes().toString().padStart(2, '0')
+  const second = now.getSeconds().toString().padStart(2, '0')
+  const weekMap = ['日', '一', '二', '三', '四', '五', '六']
+  return {
+    date: `${month}月${day}日`,
+    week: `星期${weekMap[week]}`,
+    time: `${hour}:${minute}:${second}`,
+  }
+}
+const timeNow = ref(getNowTime())
+
+// 每秒更新一次时间
+const timer = setInterval(() => {
+  timeNow.value = getNowTime()
+}, 1000)
+
+// 组件卸载时清除定时器
+onUnmounted(() => {
+  clearInterval(timer)
+})
+</script>
+
+<style scoped lang="less">
+.timeNow {
+  font-size: 14px;
+  font-weight: 600;
+  padding: 0 10px;
+  user-select: none;
+  .item {
+    display: inline-block;
+    padding: 0 3px;
+  }
+  .time {
+    width: 75px;
+    display: inline-block;
+    letter-spacing: 2px;
+  }
+}
+</style>

+ 58 - 0
src/layout/components/userDropdown/index.vue

@@ -0,0 +1,58 @@
+<template>
+  <div class="userDropdown">
+    <a-dropdown>
+      <span class="avatar">
+        <a-avatar>
+          <template #icon>
+            <UserOutlined />
+          </template>
+        </a-avatar>
+        <span class="username"> {{ account }} </span>
+      </span>
+      <template #overlay>
+        <a-menu>
+          <a-menu-item>
+            <a href="javascript:;" @click="logout">退出登录</a>
+          </a-menu-item>
+        </a-menu>
+      </template>
+    </a-dropdown>
+  </div>
+</template>
+
+<script setup lang="ts">
+import { UserOutlined } from '@ant-design/icons-vue'
+import { useUserStore } from '@/stores/user'
+import { message } from 'ant-design-vue'
+
+defineOptions({
+  name: 'userDropdown',
+})
+
+const userStore = useUserStore()
+const { account } = userStore.getUserInfo()
+
+const logout = async () => {
+  console.log('退出登录')
+  await userStore.logout(true) // 主动退出登录,需调用接口
+  message.success('退出登录成功')
+}
+</script>
+
+<style scoped lang="less">
+.avatar {
+  display: inline-block;
+}
+.username {
+  display: inline-block;
+  margin-left: 10px;
+  font-size: 16px;
+  font-weight: 600;
+}
+.userDropdown {
+  cursor: pointer;
+  .ant-dropdown-trigger {
+    display: block;
+  }
+}
+</style>

+ 373 - 0
src/layout/index.vue

@@ -0,0 +1,373 @@
+<template>
+  <a-layout style="min-height: 100vh">
+    <a-layout-sider
+      v-model:collapsed="state.collapsed"
+      collapsible
+      :theme="theme"
+      class="layout-sider"
+    >
+      <div :class="['logoWrap', theme === 'light' ? 'light' : 'dark']">
+        <div class="logo">
+          <slot name="logo"><img src="@/assets/logo.png" alt="" /></slot>
+        </div>
+        <div class="text">雷能信息后台管理</div>
+      </div>
+      <a-menu
+        v-model:selectedKeys="state.selectedKeys"
+        mode="inline"
+        :theme="theme"
+        :inline-collapsed="state.collapsed"
+        :items="menus"
+        @click="clickMenuItemHandler"
+      ></a-menu>
+    </a-layout-sider>
+
+    <a-layout class="layout-content">
+      <a-layout-header>
+        <slot name="header">
+          <base-weather class="weather" mode="text"></base-weather>
+          <time-now class="timeNow"></time-now>
+          <user-dropdown class="userDropdown"></user-dropdown>
+        </slot>
+      </a-layout-header>
+      <a-layout-content>
+        <slot name="breadcrumb">
+          <a-page-header
+            v-if="routes && routes.length > 1"
+            :title="routes[routes.length - 1]?.breadcrumbName"
+            :breadcrumb="breadcrumbConfig"
+            @back="() => $router.go(-1)"
+          />
+        </slot>
+        <slot></slot>
+      </a-layout-content>
+      <a-layout-footer>
+        <slot name="footer">合肥雷能信息技术有限公司 © 2025 All Rights Reserved.</slot>
+      </a-layout-footer>
+    </a-layout>
+
+    <alert-modal v-model:open="openAlertModal" :data="alertModalState"></alert-modal>
+  </a-layout>
+</template>
+
+<script setup lang="ts">
+import { ref, defineOptions, reactive, watchEffect, computed, h, onUnmounted } from 'vue'
+import userDropdown from './components/userDropdown/index.vue'
+import { menus } from '@/const/menus'
+import { useRoute, useRouter } from 'vue-router'
+import timeNow from './components/timeNow/index.vue'
+import type { Route } from 'ant-design-vue/es/breadcrumb/Breadcrumb'
+import mqtt, { MqttClient } from 'mqtt'
+import { useUserStore } from '@/stores/user'
+import AlertModal from './components/alertModal/index.vue'
+
+const userStore = useUserStore()
+const userId = ref(userStore?.userInfo?.userId || '')
+
+let mqttClient: MqttClient | null = null
+let mqttTimeout: number | null = null
+const MQTT_TIMEOUT_MS = 10000 // 10秒
+
+const resetMqttTimeout = () => {
+  if (mqttTimeout) clearTimeout(mqttTimeout)
+  mqttTimeout = window.setTimeout(() => {
+    console.log('MQTT超时未收到新消息')
+    closeMqtt()
+  }, MQTT_TIMEOUT_MS)
+}
+
+const MqttData = ref()
+
+const initMqttManager = async () => {
+  try {
+    mqttClient = mqtt.connect('ws://8.130.28.21:8083/mqtt', {
+      clientId: 'mqtt_vue_test' + Math.random().toString(16).substring(2, 8),
+      username: 'admin',
+      password: 'public',
+      will: {
+        // ✅ 添加遗嘱消息配置
+        topic: '/mps/client/connect',
+        payload: JSON.stringify({
+          userId: userId.value,
+          deviceType: 'wb',
+          msgType: 'disconnect',
+        }),
+        qos: 2,
+        retain: false,
+      },
+    })
+    mqttClient.on('connect', () => {
+      console.log('MQTT已连接')
+      const messageData = {
+        userId: userId.value, // 用户ID
+        deviceType: 'wb', // 设备类型
+        msgType: 'connect', // 消息类型
+      }
+      // 发布消息
+      mqttClient?.publish('/mps/client/connect', JSON.stringify(messageData), { qos: 2 }, (err) => {
+        if (err) console.error('发布失败', err)
+        else console.log('✅ 已发送参数:', messageData)
+      })
+      // 订阅所有主题
+      mqttClient?.subscribe(`/mps/client/connect/`, (err) => {
+        if (err) {
+          console.error('MQTT订阅失败', err)
+        } else {
+          console.log(`🔥已订阅主题 /mps/client/connect`)
+        }
+      })
+      mqttClient?.subscribe(`/mps/wb_${userStore.userInfo?.userId}/notice`, (err) => {
+        if (err) {
+          console.error('MQTT订阅失败', err)
+        } else {
+          console.log(`🔥已订阅主题 /mps/wb_${userStore.userInfo?.userId}/notice`)
+        }
+      })
+    })
+    mqttClient.on('error', (err) => {
+      console.error('MQTT连接错误', err)
+    })
+    mqttClient.on('message', (topic: string, message: Uint8Array) => {
+      resetMqttTimeout()
+      try {
+        const data = JSON.parse(message.toString())
+        console.log('👏👏收到MQTT消息', topic, data)
+        MqttData.value = data
+      } catch (e) {
+        console.error('👏👏MQTT消息解析失败', e)
+      }
+    })
+  } catch (error) {
+    console.error('MQTT连接失败:', error)
+  }
+}
+
+initMqttManager()
+
+// 报警弹窗
+const openAlertModal = ref(false)
+const alertModalState = reactive({
+  devName: '',
+  tenantName: '',
+  clientId: '',
+})
+watchEffect(() => {
+  const { msgType, event, devName, tenantName, clientId } = MqttData.value || {}
+  if (msgType === 'fall' && event === 'fall_confirmed') {
+    alertModalState.devName = devName
+    alertModalState.tenantName = tenantName
+    alertModalState.clientId = clientId
+    openAlertModal.value = true
+  }
+
+  // 退出连接,取消mqtt连接
+  if (!userStore.userInfo.tokenValue) {
+    console.log('🚀🚀🚀退出连接,取消mqtt连接')
+    closeMqtt()
+  }
+})
+
+const closeMqtt = () => {
+  mqttClient?.end(true)
+}
+
+onUnmounted(() => {
+  resetMqttTimeout()
+})
+
+defineOptions({
+  name: 'baseLayout',
+})
+
+const route = useRoute()
+const router = useRouter()
+const theme = ref('dark') // 主题 dark / light
+
+const state = reactive({
+  collapsed: false,
+  selectedKeys: [route.name],
+})
+
+const routes = computed(() => {
+  const currentRoute = router.currentRoute.value
+  const items = []
+
+  if (currentRoute.matched) {
+    currentRoute.matched.forEach((matchedRoute) => {
+      if (matchedRoute.meta?.title) {
+        items.push({
+          breadcrumbName: matchedRoute.meta.title,
+          path: matchedRoute.path,
+        })
+      }
+    })
+  }
+
+  if (items.length === 0 && currentRoute.meta?.title) {
+    items.push({
+      breadcrumbName: currentRoute.meta.title,
+      path: currentRoute.path,
+    })
+  }
+
+  return items
+})
+
+const breadcrumbConfig = computed(() => {
+  return {
+    routes: routes.value,
+    itemRender,
+  }
+})
+
+// eslint-disable-next-line @typescript-eslint/no-explicit-any
+const itemRender = (options: { route: Route; params: any; routes: Route[]; paths: string[] }) => {
+  console.log('itemRender', options)
+  if (!Array.isArray(options.routes)) {
+    return h('span', options.route.breadcrumbName || '')
+  }
+
+  const currentIndex = routes.value.indexOf(options.route)
+
+  if (currentIndex === -1 || currentIndex === routes.value.length - 1) {
+    return h(
+      'span',
+      {
+        class: 'current-breadcrumb',
+      },
+      options.route.breadcrumbName
+    )
+  }
+
+  return h(
+    'a',
+    {
+      class: 'breadcrumb-link',
+      onClick: (e) => {
+        console.log('🚀customRouterLinkRende onClickr🚀', e)
+        e.preventDefault()
+        if (options.route.path) {
+          router.push(options.route.path)
+        }
+      },
+    },
+    options.route.breadcrumbName
+  )
+}
+
+watchEffect(() => {
+  if (routes.value.length > 0 && routes.value[0].path) {
+    state.selectedKeys = [routes.value[0].path.split('/')[1] || 'home']
+  }
+})
+
+const clickMenuItemHandler = ({ key, keyPath }: { key: string; keyPath: string[] }) => {
+  console.log('🚀clickMenuItemHandler🚀', key, keyPath)
+  if (route.name !== key) {
+    router.push({ name: key })
+  }
+}
+</script>
+
+<style scoped lang="less">
+.layout-sider {
+  position: relative;
+  background-color: #1a3a5f;
+
+  :deep(.ant-layout-sider-children) {
+    position: sticky;
+    top: 0;
+    height: auto;
+    background-color: #1a3a5f;
+    .ant-menu {
+      background-color: #1a3a5f;
+    }
+  }
+  :deep(.ant-layout-sider-trigger) {
+    background-color: #1a3a5f;
+  }
+}
+
+.logoWrap {
+  height: 32px;
+  margin: 16px;
+  display: flex;
+  align-items: center;
+  overflow: hidden;
+  transition: all 0.3s cubic-bezier(0.645, 0.045, 0.355, 1);
+  color: #fff;
+
+  .logo {
+    width: 45px;
+    height: 32px;
+    flex-shrink: 0;
+    font-size: 20px;
+    display: flex;
+    justify-content: center;
+    align-items: center;
+    img {
+      width: 100%;
+      height: 100%;
+    }
+  }
+
+  .text {
+    margin-left: 10px;
+    flex-grow: 1;
+    opacity: 1;
+    transition:
+      opacity 0.3s cubic-bezier(0.645, 0.045, 0.355, 1),
+      width 0.3s cubic-bezier(0.645, 0.045, 0.355, 1);
+
+    overflow: hidden;
+    text-overflow: ellipsis;
+    display: -webkit-box;
+    -webkit-box-orient: vertical;
+    -webkit-line-clamp: 2;
+  }
+}
+
+.light {
+  color: #141414;
+}
+.dark {
+  color: #fff;
+}
+
+.layout-content {
+  .ant-layout-header {
+    background-color: #1a3a5f;
+    color: #fff;
+    padding: 0 32px;
+    text-align: right;
+    display: flex;
+    justify-content: flex-end;
+    position: sticky;
+    top: 0;
+    z-index: 1000;
+  }
+  .ant-layout-content {
+    margin: 16px;
+  }
+  .ant-layout-footer {
+    text-align: center;
+    background-color: transparent;
+    color: #4774a7;
+  }
+}
+
+.ant-layout-sider-collapsed & {
+  .text {
+    opacity: 0;
+    width: 0;
+    margin-left: 0;
+  }
+}
+
+.site-layout .site-layout-background {
+  background: #101830b3;
+}
+[data-theme='dark'] .site-layout .site-layout-background {
+  background: #101830b3;
+}
+</style>

+ 30 - 0
src/main.ts

@@ -0,0 +1,30 @@
+import { createApp, type DefineComponent } from 'vue'
+import request from '@/request'
+
+import 'ant-design-vue/dist/reset.css'
+import '@/styles/index.css'
+
+import App from './App.vue'
+import router from './router'
+import pinia from '@/stores/index'
+
+const app = createApp(App)
+
+app.config.globalProperties.$http = request
+app.use(pinia)
+app.use(router)
+
+const components = import.meta.glob('./components/**/*.vue', { eager: true })
+Object.entries(components).forEach(([path, module]) => {
+  const componentName =
+    (module as { default: DefineComponent }).default?.name ||
+    path
+      .split('/')
+      .pop()
+      ?.replace(/\.\w+$/, '')
+  if (componentName) {
+    app.component(componentName, (module as { default: DefineComponent }).default)
+  }
+})
+
+app.mount('#app')

+ 106 - 0
src/request/index.ts

@@ -0,0 +1,106 @@
+import axios from 'axios'
+import { message } from 'ant-design-vue'
+import { useUserStore } from '@/stores/user'
+
+const service = axios.create({
+  baseURL: import.meta.env.VITE_API_BASE_URL, // 从环境变量读取接口地址
+  timeout: 1000 * 30,
+  headers: {
+    'Content-Type': 'application/json',
+    Accept: 'application/json',
+  },
+  withCredentials: true, // 允许跨域携带cookie
+})
+
+// 请求拦截器
+service.interceptors.request.use(
+  (config) => {
+    const userStore = useUserStore()
+    if (config.url === '/user/login') {
+      return config
+    }
+    if (userStore.userInfo.tokenValue) {
+      // token存在就携带token
+      config.headers[userStore.userInfo.tokenName] = `${userStore.userInfo.tokenValue}`
+    } else {
+      userStore.logout()
+      return Promise.reject(new Error('未授权,请先登录'))
+    }
+
+    // 添加时间戳防止缓存
+    if (config.method === 'get') {
+      config.params = {
+        ...config.params,
+        _t: Date.now(),
+      }
+    }
+
+    return config
+  },
+  (error) => {
+    console.error('请求错误:', error)
+    return Promise.reject(error)
+  }
+)
+
+// 响应拦截器
+service.interceptors.response.use(
+  (response) => {
+    const userStore = useUserStore()
+    // HTTP状态码为2xx
+    console.log('✅ 请求成功 🚀', response)
+    const { data } = response
+
+    // 业务状态码处理
+    if ([200, '200'].includes(data.code)) {
+      return data
+    } else {
+      // token过期
+      if (['11011', '11012', '11016'].includes(data.code)) {
+        if (!userStore.userInfo.tokenValue) return
+        message.error('登录失效,请重新登录')
+        userStore.logout()
+        return
+      }
+      if (['60001'].includes(data.code)) {
+        // 房间信息不存在,不用报错提示
+        return
+      }
+      if ([500, '500'].includes(data.code)) {
+        message.error(data.message || data.msg || '服务异常,请联系管理员')
+        return
+      }
+      message.error(data.message || data.msg) // 全局错误信息
+      return Promise.reject(new Error(data.message))
+    }
+  },
+  (error) => {
+    const userStore = useUserStore()
+    // HTTP状态码非2xx
+    if (error.response) {
+      // 服务器有响应的情况
+      const { status, data } = error.response
+      console.error('❌ 服务器错误 🚀', status, data, error.config.url)
+      const errorMessage = data?.message || data?.msg || `服务异常,请联系管理员`
+      if (status === 400 && data?.code === '11016') {
+        message.error('登录失效,请重新登录')
+        userStore.logout()
+        return
+      }
+      message.error(errorMessage)
+      return Promise.reject(new Error(errorMessage))
+    } else if (error.request) {
+      // 请求已发出但没有收到响应
+      console.error('❌ 网络错误 🚀', error.message)
+      message.error('网络异常,请检查网络')
+      return Promise.reject(new Error('网络异常,请检查网络'))
+    } else {
+      // 请求配置错误
+      console.error('❌ 请求错误 🚀', error.message)
+      message.error(`${error.message || '请求异常,请联系管理员'}`)
+      return Promise.reject(error)
+    }
+  }
+)
+
+export default service

+ 15 - 0
src/router/guard.ts

@@ -0,0 +1,15 @@
+import type { RouteLocationNormalized } from 'vue-router'
+import { useUserStore } from '@/stores/user'
+import { message } from 'ant-design-vue'
+
+export const authGuard = (to: RouteLocationNormalized) => {
+  const userStore = useUserStore()
+
+  if (!userStore.userInfo.tokenValue && to.name !== 'login') {
+    message.error('登录失效,请重新登录')
+    return {
+      name: 'login',
+      query: { redirect: to.fullPath },
+    }
+  }
+}

+ 28 - 0
src/router/index.ts

@@ -0,0 +1,28 @@
+import { createRouter, createWebHistory } from 'vue-router'
+// import HomeView from '@/views/home/index.vue'
+import { authGuard } from './guard'
+
+// 自动加载modules目录下的所有路由文件
+const moduleRoutes = Object.values(import.meta.glob('./modules/*.ts', { eager: true })).flatMap(
+  // eslint-disable-next-line @typescript-eslint/no-explicit-any
+  (module) => (module as { default: any }).default
+)
+
+const router = createRouter({
+  history: createWebHistory(import.meta.env.BASE_URL),
+  routes: [
+    {
+      path: '/',
+      name: 'home',
+      // component: HomeView,
+      // meta: { title: '首页看板', isFullScreen: false, keepAlive: false },
+      redirect: '/community/list',
+      meta: { title: '小区管理', isFullScreen: false },
+    },
+    // 自动合并所有模块路由
+    ...moduleRoutes,
+  ],
+})
+router.beforeEach(authGuard)
+
+export default router

+ 8 - 0
src/router/modules/alarm.ts

@@ -0,0 +1,8 @@
+export default [
+  {
+    path: '/alarm',
+    name: 'alarm',
+    component: () => import('@/views/alarm/index.vue'),
+    meta: { title: '告警管理', isFullScreen: false, keepAlive: false },
+  },
+]

+ 22 - 0
src/router/modules/community.ts

@@ -0,0 +1,22 @@
+export default [
+  {
+    path: '/community',
+    name: 'community',
+    redirect: '/community/list',
+    meta: { title: '小区管理', isFullScreen: false },
+    children: [
+      {
+        path: 'list',
+        name: 'communityList',
+        component: () => import('@/views/community/list/index.vue'),
+        meta: { title: '小区列表', isFullScreen: false, keepAlive: true },
+      },
+      {
+        path: 'detail',
+        name: 'communityDetail',
+        component: () => import('@/views/community/detail/index.vue'),
+        meta: { title: '小区详情', isFullScreen: false, keepAlive: false },
+      },
+    ],
+  },
+]

+ 22 - 0
src/router/modules/device.ts

@@ -0,0 +1,22 @@
+export default [
+  {
+    path: '/device',
+    name: 'device',
+    redirect: '/device/list',
+    meta: { title: '设备管理', isFullScreen: false },
+    children: [
+      {
+        path: 'list',
+        name: 'deviceList',
+        component: () => import('@/views/device/list/index.vue'),
+        meta: { title: '设备列表', isFullScreen: false, keepAlive: true },
+      },
+      {
+        path: 'detail',
+        name: 'deviceDetail',
+        component: () => import('@/views/device/detail/index.vue'),
+        meta: { title: '设备详情', isFullScreen: false, keepAlive: false },
+      },
+    ],
+  },
+]

+ 8 - 0
src/router/modules/login.ts

@@ -0,0 +1,8 @@
+export default [
+  {
+    path: '/login',
+    name: 'login',
+    component: () => import('@/views/login/index.vue'),
+    meta: { title: '登录', isFullScreen: true, keepAlive: false },
+  },
+]

+ 8 - 0
src/router/modules/system.ts

@@ -0,0 +1,8 @@
+export default [
+  {
+    path: '/system',
+    name: 'system',
+    component: () => import('@/views/system/index.vue'),
+    meta: { title: '系统管理', isFullScreen: false, keepAlive: false },
+  },
+]

+ 8 - 0
src/router/modules/user.ts

@@ -0,0 +1,8 @@
+export default [
+  {
+    path: '/user',
+    name: 'user',
+    component: () => import('@/views/user/index.vue'),
+    meta: { title: '用户管理', isFullScreen: false, keepAlive: false },
+  },
+]

+ 16 - 0
src/router/typed-router.d.ts

@@ -0,0 +1,16 @@
+import type { RouteRecordRaw } from 'vue-router'
+
+declare module 'vue-router' {
+  interface RouteMeta {
+    title: string
+    isFullScreen: boolean
+    keepAlive: boolean
+  }
+}
+
+// 为自动导入的路由模块添加类型支持
+declare global {
+  declare interface RouteModule {
+    default: RouteRecordRaw[] | RouteRecordRaw
+  }
+}

+ 7 - 0
src/stores/index.ts

@@ -0,0 +1,7 @@
+import { createPinia } from 'pinia'
+import piniaPluginPersistedstate from 'pinia-plugin-persistedstate'
+
+const pinia = createPinia()
+pinia.use(piniaPluginPersistedstate)
+
+export default pinia

+ 122 - 0
src/stores/user.ts

@@ -0,0 +1,122 @@
+import { ref, toRefs } from 'vue'
+import { defineStore } from 'pinia'
+import * as userApi from '@/api/user'
+import { useRoute, useRouter } from 'vue-router'
+
+type LoginParams = {
+  account: string
+  password: string
+}
+
+type UserInfo = {
+  tokenName: string // token名称
+  tokenValue: string // token值
+  isLogin: string // 是否已登录
+  loginId: string // 此token对应的LoginId,未登录时为null
+  loginType: string // 多账号体系下的账号类型
+  tokenTimeout: string // token 剩余有效期(单位: 秒)
+  sessionTimeout: string // Account-Session 剩余有效时间(单位: 秒)
+  tokenSessionTimeout: string // Token-Session 剩余有效时间(单位: 秒)
+  loginDeviceType: string // 登录设备类型
+  tags: string // 自定义数据(暂无意义,留作扩展)
+  phone: string // 手机号
+  userId: number // userId
+  account: string // 账户
+}
+
+export const useUserStore = defineStore(
+  'user',
+  () => {
+    const route = useRoute()
+    const router = useRouter()
+
+    const userInfo = ref<UserInfo>({
+      tokenName: '',
+      tokenValue: '',
+      isLogin: '',
+      loginId: '',
+      loginType: '',
+      tokenTimeout: '',
+      sessionTimeout: '',
+      tokenSessionTimeout: '',
+      loginDeviceType: '',
+      tags: '',
+      phone: '',
+      userId: 0,
+      account: '',
+    })
+
+    // 登录
+    function login(params: LoginParams) {
+      const redirectPath = route.query.redirect as string
+      console.log('🚀🚀🚀userStore Login', params, redirectPath)
+      return new Promise((resolve, reject) => {
+        userApi
+          .login(params)
+          .then((res) => {
+            console.log('🚀🚀🚀userStore Login res', res)
+            userInfo.value = res.data
+            resolve(res)
+            if (redirectPath) {
+              router.replace(redirectPath)
+            } else {
+              router.replace({ path: '/' })
+            }
+          })
+          .catch((err) => {
+            console.log('🚀🚀🚀userStore Login err', err)
+            reject(err)
+          })
+      })
+    }
+
+    // 退出登录
+    async function logout(active = false) {
+      if (userInfo.value.tokenValue && active) {
+        try {
+          const res = await userApi.logout()
+          console.log('✅ userStore logout success', res)
+        } catch (error) {
+          console.log('❌ userStore logout error', error)
+        }
+      }
+      userInfo.value = {
+        tokenName: '',
+        tokenValue: '',
+        isLogin: '',
+        loginId: '',
+        loginType: '',
+        tokenTimeout: '',
+        sessionTimeout: '',
+        tokenSessionTimeout: '',
+        loginDeviceType: '',
+        tags: '',
+        phone: '',
+        userId: 0,
+        account: '',
+      }
+      const redirectPath = router.currentRoute.value.fullPath || ''
+      console.log('✅ userStore Logout', redirectPath)
+      if (router.currentRoute.value.name === 'login') return
+      router.push({
+        name: 'login',
+        query: {
+          redirect: redirectPath,
+        },
+      })
+    }
+
+    // 获取用户信息
+    function getUserInfo() {
+      return toRefs(userInfo.value)
+    }
+
+    return { login, logout, getUserInfo, userInfo }
+  },
+  {
+    persist: {
+      key: `${import.meta.env.VITE_STOTRE_NAME}_USER_INFO`,
+      storage: localStorage,
+    },
+  }
+)

+ 6 - 0
src/styles/index.css

@@ -0,0 +1,6 @@
+@import './qweather/qweather-icons.css';
+
+html,
+body {
+  touch-action: manipulation;
+}

BIN
src/styles/qweather/fonts/qweather-icons.ttf


BIN
src/styles/qweather/fonts/qweather-icons.woff


BIN
src/styles/qweather/fonts/qweather-icons.woff2


+ 1018 - 0
src/styles/qweather/qweather-icons.css

@@ -0,0 +1,1018 @@
+/*!
+* QWeather Icons (https://icons.qweather.com)
+* Copyright QWeather 和风天气 (https://www.qweather.com)
+* License:  Code for MIT, Icons for CC BY 4.0
+*/
+
+@font-face {
+  font-family: "qweather-icons";
+  src: url("./fonts/qweather-icons.woff2?683382eedec19edbc7e167b16847eb27") format("woff2"),
+url("./fonts/qweather-icons.woff?683382eedec19edbc7e167b16847eb27") format("woff"),
+url("./fonts/qweather-icons.ttf?683382eedec19edbc7e167b16847eb27") format("truetype");
+}
+
+[class^="qi-"]::before,
+[class*=" qi-"]::before {
+  display: inline-block;
+  font-family: "qweather-icons" !important;
+  font-style: normal;
+  font-weight: normal !important;
+  font-variant: normal;
+  text-transform: none;
+  line-height: 1;
+  vertical-align: -.125em;
+  -webkit-font-smoothing: antialiased;
+  -moz-osx-font-smoothing: grayscale;
+}
+
+.qi-100::before { content: "\f101"; }
+.qi-101::before { content: "\f102"; }
+.qi-102::before { content: "\f103"; }
+.qi-103::before { content: "\f104"; }
+.qi-104::before { content: "\f105"; }
+.qi-150::before { content: "\f106"; }
+.qi-151::before { content: "\f107"; }
+.qi-152::before { content: "\f108"; }
+.qi-153::before { content: "\f109"; }
+.qi-300::before { content: "\f10a"; }
+.qi-301::before { content: "\f10b"; }
+.qi-302::before { content: "\f10c"; }
+.qi-303::before { content: "\f10d"; }
+.qi-304::before { content: "\f10e"; }
+.qi-305::before { content: "\f10f"; }
+.qi-306::before { content: "\f110"; }
+.qi-307::before { content: "\f111"; }
+.qi-308::before { content: "\f112"; }
+.qi-309::before { content: "\f113"; }
+.qi-310::before { content: "\f114"; }
+.qi-311::before { content: "\f115"; }
+.qi-312::before { content: "\f116"; }
+.qi-313::before { content: "\f117"; }
+.qi-314::before { content: "\f118"; }
+.qi-315::before { content: "\f119"; }
+.qi-316::before { content: "\f11a"; }
+.qi-317::before { content: "\f11b"; }
+.qi-318::before { content: "\f11c"; }
+.qi-350::before { content: "\f11d"; }
+.qi-351::before { content: "\f11e"; }
+.qi-399::before { content: "\f11f"; }
+.qi-400::before { content: "\f120"; }
+.qi-401::before { content: "\f121"; }
+.qi-402::before { content: "\f122"; }
+.qi-403::before { content: "\f123"; }
+.qi-404::before { content: "\f124"; }
+.qi-405::before { content: "\f125"; }
+.qi-406::before { content: "\f126"; }
+.qi-407::before { content: "\f127"; }
+.qi-408::before { content: "\f128"; }
+.qi-409::before { content: "\f129"; }
+.qi-410::before { content: "\f12a"; }
+.qi-456::before { content: "\f12b"; }
+.qi-457::before { content: "\f12c"; }
+.qi-499::before { content: "\f12d"; }
+.qi-500::before { content: "\f12e"; }
+.qi-501::before { content: "\f12f"; }
+.qi-502::before { content: "\f130"; }
+.qi-503::before { content: "\f131"; }
+.qi-504::before { content: "\f132"; }
+.qi-507::before { content: "\f133"; }
+.qi-508::before { content: "\f134"; }
+.qi-509::before { content: "\f135"; }
+.qi-510::before { content: "\f136"; }
+.qi-511::before { content: "\f137"; }
+.qi-512::before { content: "\f138"; }
+.qi-513::before { content: "\f139"; }
+.qi-514::before { content: "\f13a"; }
+.qi-515::before { content: "\f13b"; }
+.qi-800::before { content: "\f13c"; }
+.qi-801::before { content: "\f13d"; }
+.qi-802::before { content: "\f13e"; }
+.qi-803::before { content: "\f13f"; }
+.qi-804::before { content: "\f140"; }
+.qi-805::before { content: "\f141"; }
+.qi-806::before { content: "\f142"; }
+.qi-807::before { content: "\f143"; }
+.qi-900::before { content: "\f144"; }
+.qi-901::before { content: "\f145"; }
+.qi-999::before { content: "\f146"; }
+.qi-1001::before { content: "\f147"; }
+.qi-1002::before { content: "\f148"; }
+.qi-1003::before { content: "\f149"; }
+.qi-1004::before { content: "\f14a"; }
+.qi-1005::before { content: "\f14b"; }
+.qi-1006::before { content: "\f14c"; }
+.qi-1007::before { content: "\f14d"; }
+.qi-1008::before { content: "\f14e"; }
+.qi-1009::before { content: "\f14f"; }
+.qi-1010::before { content: "\f150"; }
+.qi-1011::before { content: "\f151"; }
+.qi-1012::before { content: "\f152"; }
+.qi-1013::before { content: "\f153"; }
+.qi-1014::before { content: "\f154"; }
+.qi-1015::before { content: "\f155"; }
+.qi-1016::before { content: "\f156"; }
+.qi-1017::before { content: "\f157"; }
+.qi-1018::before { content: "\f158"; }
+.qi-1019::before { content: "\f159"; }
+.qi-1020::before { content: "\f15a"; }
+.qi-1021::before { content: "\f15b"; }
+.qi-1022::before { content: "\f15c"; }
+.qi-1023::before { content: "\f15d"; }
+.qi-1024::before { content: "\f15e"; }
+.qi-1025::before { content: "\f15f"; }
+.qi-1026::before { content: "\f160"; }
+.qi-1027::before { content: "\f161"; }
+.qi-1028::before { content: "\f162"; }
+.qi-1029::before { content: "\f163"; }
+.qi-1030::before { content: "\f164"; }
+.qi-1031::before { content: "\f165"; }
+.qi-1032::before { content: "\f166"; }
+.qi-1033::before { content: "\f167"; }
+.qi-1034::before { content: "\f168"; }
+.qi-1035::before { content: "\f169"; }
+.qi-1036::before { content: "\f16a"; }
+.qi-1037::before { content: "\f16b"; }
+.qi-1038::before { content: "\f16c"; }
+.qi-1039::before { content: "\f16d"; }
+.qi-1040::before { content: "\f16e"; }
+.qi-1041::before { content: "\f16f"; }
+.qi-1042::before { content: "\f170"; }
+.qi-1043::before { content: "\f171"; }
+.qi-1044::before { content: "\f172"; }
+.qi-1045::before { content: "\f173"; }
+.qi-1046::before { content: "\f174"; }
+.qi-1047::before { content: "\f175"; }
+.qi-1048::before { content: "\f176"; }
+.qi-1049::before { content: "\f177"; }
+.qi-1050::before { content: "\f178"; }
+.qi-1051::before { content: "\f179"; }
+.qi-1052::before { content: "\f17a"; }
+.qi-1053::before { content: "\f17b"; }
+.qi-1054::before { content: "\f17c"; }
+.qi-1055::before { content: "\f17d"; }
+.qi-1056::before { content: "\f17e"; }
+.qi-1057::before { content: "\f17f"; }
+.qi-1058::before { content: "\f180"; }
+.qi-1059::before { content: "\f181"; }
+.qi-1060::before { content: "\f182"; }
+.qi-1061::before { content: "\f183"; }
+.qi-1062::before { content: "\f184"; }
+.qi-1063::before { content: "\f185"; }
+.qi-1064::before { content: "\f186"; }
+.qi-1065::before { content: "\f187"; }
+.qi-1066::before { content: "\f188"; }
+.qi-1067::before { content: "\f189"; }
+.qi-1068::before { content: "\f18a"; }
+.qi-1069::before { content: "\f18b"; }
+.qi-1071::before { content: "\f18c"; }
+.qi-1072::before { content: "\f18d"; }
+.qi-1073::before { content: "\f18e"; }
+.qi-1074::before { content: "\f18f"; }
+.qi-1075::before { content: "\f190"; }
+.qi-1076::before { content: "\f191"; }
+.qi-1077::before { content: "\f192"; }
+.qi-1078::before { content: "\f193"; }
+.qi-1079::before { content: "\f194"; }
+.qi-1080::before { content: "\f195"; }
+.qi-1081::before { content: "\f196"; }
+.qi-1082::before { content: "\f197"; }
+.qi-1084::before { content: "\f198"; }
+.qi-1085::before { content: "\f199"; }
+.qi-1086::before { content: "\f19a"; }
+.qi-1087::before { content: "\f19b"; }
+.qi-1088::before { content: "\f19c"; }
+.qi-1089::before { content: "\f19d"; }
+.qi-1201::before { content: "\f2c5"; }
+.qi-1202::before { content: "\f2c6"; }
+.qi-1203::before { content: "\f2c7"; }
+.qi-1204::before { content: "\f2c8"; }
+.qi-1205::before { content: "\f2c9"; }
+.qi-1206::before { content: "\f2ca"; }
+.qi-1207::before { content: "\f2cb"; }
+.qi-1208::before { content: "\f2cc"; }
+.qi-1209::before { content: "\f2cd"; }
+.qi-1210::before { content: "\f2ce"; }
+.qi-1211::before { content: "\f2cf"; }
+.qi-1212::before { content: "\f2d0"; }
+.qi-1213::before { content: "\f2d1"; }
+.qi-1214::before { content: "\f2d2"; }
+.qi-1215::before { content: "\f2d3"; }
+.qi-1216::before { content: "\f2d4"; }
+.qi-1217::before { content: "\f2d5"; }
+.qi-1218::before { content: "\f2d6"; }
+.qi-1219::before { content: "\f2d7"; }
+.qi-1221::before { content: "\f2d8"; }
+.qi-1241::before { content: "\f2d9"; }
+.qi-1242::before { content: "\f2da"; }
+.qi-1243::before { content: "\f2db"; }
+.qi-1244::before { content: "\f2dc"; }
+.qi-1245::before { content: "\f2dd"; }
+.qi-1246::before { content: "\f2de"; }
+.qi-1247::before { content: "\f2df"; }
+.qi-1248::before { content: "\f2e0"; }
+.qi-1249::before { content: "\f2e1"; }
+.qi-1250::before { content: "\f2e2"; }
+.qi-1251::before { content: "\f2e3"; }
+.qi-1271::before { content: "\f2f6"; }
+.qi-1272::before { content: "\f2f7"; }
+.qi-1273::before { content: "\f2f8"; }
+.qi-1274::before { content: "\f2f9"; }
+.qi-1601::before { content: "\f1a1"; }
+.qi-1602::before { content: "\f1a2"; }
+.qi-1603::before { content: "\f1a3"; }
+.qi-1604::before { content: "\f1a4"; }
+.qi-1605::before { content: "\f1a5"; }
+.qi-1606::before { content: "\f1a6"; }
+.qi-1607::before { content: "\f1a7"; }
+.qi-1608::before { content: "\f20c"; }
+.qi-1609::before { content: "\f20d"; }
+.qi-1610::before { content: "\f20e"; }
+.qi-1701::before { content: "\f1a8"; }
+.qi-1702::before { content: "\f1a9"; }
+.qi-1703::before { content: "\f1aa"; }
+.qi-1801::before { content: "\f20f"; }
+.qi-1802::before { content: "\f210"; }
+.qi-1803::before { content: "\f211"; }
+.qi-1804::before { content: "\f212"; }
+.qi-1805::before { content: "\f213"; }
+.qi-2001::before { content: "\f1ab"; }
+.qi-2002::before { content: "\f1ac"; }
+.qi-2003::before { content: "\f1ad"; }
+.qi-2004::before { content: "\f1ae"; }
+.qi-2005::before { content: "\f1af"; }
+.qi-2006::before { content: "\f1b0"; }
+.qi-2007::before { content: "\f1b1"; }
+.qi-2029::before { content: "\f214"; }
+.qi-2030::before { content: "\f215"; }
+.qi-2031::before { content: "\f216"; }
+.qi-2032::before { content: "\f217"; }
+.qi-2033::before { content: "\f218"; }
+.qi-2050::before { content: "\f219"; }
+.qi-2051::before { content: "\f21a"; }
+.qi-2052::before { content: "\f1c7"; }
+.qi-2053::before { content: "\f1c8"; }
+.qi-2054::before { content: "\f1c9"; }
+.qi-2070::before { content: "\f21b"; }
+.qi-2071::before { content: "\f21c"; }
+.qi-2072::before { content: "\f21d"; }
+.qi-2073::before { content: "\f21e"; }
+.qi-2074::before { content: "\f21f"; }
+.qi-2075::before { content: "\f220"; }
+.qi-2076::before { content: "\f221"; }
+.qi-2077::before { content: "\f222"; }
+.qi-2078::before { content: "\f223"; }
+.qi-2079::before { content: "\f224"; }
+.qi-2080::before { content: "\f225"; }
+.qi-2081::before { content: "\f226"; }
+.qi-2082::before { content: "\f227"; }
+.qi-2083::before { content: "\f228"; }
+.qi-2084::before { content: "\f229"; }
+.qi-2085::before { content: "\f22a"; }
+.qi-2100::before { content: "\f22b"; }
+.qi-2101::before { content: "\f22c"; }
+.qi-2102::before { content: "\f22d"; }
+.qi-2103::before { content: "\f22e"; }
+.qi-2104::before { content: "\f22f"; }
+.qi-2105::before { content: "\f230"; }
+.qi-2106::before { content: "\f231"; }
+.qi-2107::before { content: "\f232"; }
+.qi-2108::before { content: "\f233"; }
+.qi-2109::before { content: "\f234"; }
+.qi-2111::before { content: "\f235"; }
+.qi-2120::before { content: "\f236"; }
+.qi-2121::before { content: "\f237"; }
+.qi-2122::before { content: "\f238"; }
+.qi-2123::before { content: "\f239"; }
+.qi-2124::before { content: "\f23a"; }
+.qi-2125::before { content: "\f23b"; }
+.qi-2126::before { content: "\f23c"; }
+.qi-2127::before { content: "\f23d"; }
+.qi-2128::before { content: "\f23e"; }
+.qi-2129::before { content: "\f23f"; }
+.qi-2130::before { content: "\f240"; }
+.qi-2131::before { content: "\f241"; }
+.qi-2132::before { content: "\f242"; }
+.qi-2133::before { content: "\f243"; }
+.qi-2134::before { content: "\f244"; }
+.qi-2135::before { content: "\f245"; }
+.qi-2150::before { content: "\f246"; }
+.qi-2151::before { content: "\f247"; }
+.qi-2152::before { content: "\f248"; }
+.qi-2153::before { content: "\f249"; }
+.qi-2154::before { content: "\f24a"; }
+.qi-2155::before { content: "\f24b"; }
+.qi-2156::before { content: "\f24c"; }
+.qi-2157::before { content: "\f24d"; }
+.qi-2158::before { content: "\f24e"; }
+.qi-2159::before { content: "\f24f"; }
+.qi-2160::before { content: "\f250"; }
+.qi-2161::before { content: "\f251"; }
+.qi-2162::before { content: "\f252"; }
+.qi-2163::before { content: "\f253"; }
+.qi-2164::before { content: "\f254"; }
+.qi-2165::before { content: "\f255"; }
+.qi-2166::before { content: "\f256"; }
+.qi-2190::before { content: "\f257"; }
+.qi-2191::before { content: "\f258"; }
+.qi-2192::before { content: "\f259"; }
+.qi-2193::before { content: "\f25a"; }
+.qi-2200::before { content: "\f2e4"; }
+.qi-2201::before { content: "\f2e5"; }
+.qi-2202::before { content: "\f2e6"; }
+.qi-2203::before { content: "\f2e7"; }
+.qi-2204::before { content: "\f2e8"; }
+.qi-2205::before { content: "\f2e9"; }
+.qi-2207::before { content: "\f2ea"; }
+.qi-2208::before { content: "\f2eb"; }
+.qi-2209::before { content: "\f2ec"; }
+.qi-2210::before { content: "\f2ed"; }
+.qi-2211::before { content: "\f2ee"; }
+.qi-2212::before { content: "\f2ef"; }
+.qi-2213::before { content: "\f2f0"; }
+.qi-2214::before { content: "\f2f1"; }
+.qi-2215::before { content: "\f2f2"; }
+.qi-2216::before { content: "\f2f3"; }
+.qi-2217::before { content: "\f2f4"; }
+.qi-2218::before { content: "\f2f5"; }
+.qi-2300::before { content: "\f25b"; }
+.qi-2301::before { content: "\f25c"; }
+.qi-2302::before { content: "\f25d"; }
+.qi-2303::before { content: "\f25e"; }
+.qi-2304::before { content: "\f25f"; }
+.qi-2305::before { content: "\f260"; }
+.qi-2306::before { content: "\f261"; }
+.qi-2307::before { content: "\f262"; }
+.qi-2308::before { content: "\f263"; }
+.qi-2309::before { content: "\f264"; }
+.qi-2311::before { content: "\f265"; }
+.qi-2312::before { content: "\f266"; }
+.qi-2313::before { content: "\f267"; }
+.qi-2314::before { content: "\f268"; }
+.qi-2315::before { content: "\f269"; }
+.qi-2316::before { content: "\f26a"; }
+.qi-2317::before { content: "\f26b"; }
+.qi-2318::before { content: "\f26c"; }
+.qi-2319::before { content: "\f26d"; }
+.qi-2320::before { content: "\f26e"; }
+.qi-2321::before { content: "\f26f"; }
+.qi-2322::before { content: "\f270"; }
+.qi-2323::before { content: "\f271"; }
+.qi-2324::before { content: "\f272"; }
+.qi-2325::before { content: "\f273"; }
+.qi-2326::before { content: "\f274"; }
+.qi-2327::before { content: "\f275"; }
+.qi-2328::before { content: "\f276"; }
+.qi-2330::before { content: "\f277"; }
+.qi-2331::before { content: "\f278"; }
+.qi-2332::before { content: "\f279"; }
+.qi-2333::before { content: "\f27a"; }
+.qi-2341::before { content: "\f27b"; }
+.qi-2343::before { content: "\f27c"; }
+.qi-2345::before { content: "\f27d"; }
+.qi-2346::before { content: "\f27e"; }
+.qi-2348::before { content: "\f27f"; }
+.qi-2349::before { content: "\f280"; }
+.qi-2350::before { content: "\f281"; }
+.qi-2351::before { content: "\f282"; }
+.qi-2352::before { content: "\f283"; }
+.qi-2353::before { content: "\f284"; }
+.qi-2354::before { content: "\f285"; }
+.qi-2355::before { content: "\f286"; }
+.qi-2356::before { content: "\f287"; }
+.qi-2357::before { content: "\f288"; }
+.qi-2358::before { content: "\f289"; }
+.qi-2359::before { content: "\f28a"; }
+.qi-2360::before { content: "\f28b"; }
+.qi-2361::before { content: "\f28c"; }
+.qi-2362::before { content: "\f28d"; }
+.qi-2363::before { content: "\f28e"; }
+.qi-2364::before { content: "\f28f"; }
+.qi-2365::before { content: "\f290"; }
+.qi-2366::before { content: "\f291"; }
+.qi-2367::before { content: "\f292"; }
+.qi-2368::before { content: "\f293"; }
+.qi-2369::before { content: "\f294"; }
+.qi-2370::before { content: "\f295"; }
+.qi-2371::before { content: "\f296"; }
+.qi-2372::before { content: "\f297"; }
+.qi-2373::before { content: "\f298"; }
+.qi-2374::before { content: "\f299"; }
+.qi-2375::before { content: "\f29a"; }
+.qi-2376::before { content: "\f29b"; }
+.qi-2377::before { content: "\f29c"; }
+.qi-2378::before { content: "\f29d"; }
+.qi-2379::before { content: "\f29e"; }
+.qi-2380::before { content: "\f29f"; }
+.qi-2381::before { content: "\f2a0"; }
+.qi-2382::before { content: "\f2a1"; }
+.qi-2383::before { content: "\f2a2"; }
+.qi-2384::before { content: "\f2a3"; }
+.qi-2385::before { content: "\f2a4"; }
+.qi-2386::before { content: "\f2a5"; }
+.qi-2387::before { content: "\f2a6"; }
+.qi-2388::before { content: "\f2a7"; }
+.qi-2389::before { content: "\f2a8"; }
+.qi-2390::before { content: "\f2a9"; }
+.qi-2391::before { content: "\f2aa"; }
+.qi-2392::before { content: "\f2ab"; }
+.qi-2393::before { content: "\f2ac"; }
+.qi-2394::before { content: "\f2ad"; }
+.qi-2395::before { content: "\f2ae"; }
+.qi-2396::before { content: "\f2af"; }
+.qi-2397::before { content: "\f2b0"; }
+.qi-2398::before { content: "\f2b1"; }
+.qi-2399::before { content: "\f2b2"; }
+.qi-2400::before { content: "\f2b3"; }
+.qi-2409::before { content: "\f2b4"; }
+.qi-2411::before { content: "\f2b5"; }
+.qi-2412::before { content: "\f2b6"; }
+.qi-2413::before { content: "\f2b7"; }
+.qi-2414::before { content: "\f2b8"; }
+.qi-2415::before { content: "\f2b9"; }
+.qi-2416::before { content: "\f2ba"; }
+.qi-2417::before { content: "\f2bb"; }
+.qi-2418::before { content: "\f2bc"; }
+.qi-2419::before { content: "\f2bd"; }
+.qi-2420::before { content: "\f2be"; }
+.qi-2421::before { content: "\f2bf"; }
+.qi-2422::before { content: "\f2c0"; }
+.qi-2423::before { content: "\f2c1"; }
+.qi-2424::before { content: "\f2c2"; }
+.qi-2425::before { content: "\f2c3"; }
+.qi-2426::before { content: "\f2c4"; }
+.qi-2501::before { content: "\f2fa"; }
+.qi-2502::before { content: "\f2fb"; }
+.qi-2521::before { content: "\f2fc"; }
+.qi-2522::before { content: "\f2fd"; }
+.qi-2523::before { content: "\f2fe"; }
+.qi-2524::before { content: "\f2ff"; }
+.qi-2525::before { content: "\f300"; }
+.qi-2526::before { content: "\f301"; }
+.qi-2527::before { content: "\f302"; }
+.qi-2528::before { content: "\f303"; }
+.qi-2529::before { content: "\f304"; }
+.qi-2530::before { content: "\f305"; }
+.qi-2531::before { content: "\f306"; }
+.qi-2532::before { content: "\f307"; }
+.qi-2550::before { content: "\f308"; }
+.qi-2551::before { content: "\f309"; }
+.qi-2552::before { content: "\f30a"; }
+.qi-2553::before { content: "\f30b"; }
+.qi-2554::before { content: "\f30c"; }
+.qi-9999::before { content: "\f1cb"; }
+.qi-100-fill::before { content: "\f1cc"; }
+.qi-101-fill::before { content: "\f1cd"; }
+.qi-102-fill::before { content: "\f1ce"; }
+.qi-103-fill::before { content: "\f1cf"; }
+.qi-104-fill::before { content: "\f1d0"; }
+.qi-150-fill::before { content: "\f1d1"; }
+.qi-151-fill::before { content: "\f1d2"; }
+.qi-152-fill::before { content: "\f1d3"; }
+.qi-153-fill::before { content: "\f1d4"; }
+.qi-300-fill::before { content: "\f1d5"; }
+.qi-301-fill::before { content: "\f1d6"; }
+.qi-302-fill::before { content: "\f1d7"; }
+.qi-303-fill::before { content: "\f1d8"; }
+.qi-304-fill::before { content: "\f1d9"; }
+.qi-305-fill::before { content: "\f1da"; }
+.qi-306-fill::before { content: "\f1db"; }
+.qi-307-fill::before { content: "\f1dc"; }
+.qi-308-fill::before { content: "\f1dd"; }
+.qi-309-fill::before { content: "\f1de"; }
+.qi-310-fill::before { content: "\f1df"; }
+.qi-311-fill::before { content: "\f1e0"; }
+.qi-312-fill::before { content: "\f1e1"; }
+.qi-313-fill::before { content: "\f1e2"; }
+.qi-314-fill::before { content: "\f1e3"; }
+.qi-315-fill::before { content: "\f1e4"; }
+.qi-316-fill::before { content: "\f1e5"; }
+.qi-317-fill::before { content: "\f1e6"; }
+.qi-318-fill::before { content: "\f1e7"; }
+.qi-350-fill::before { content: "\f1e8"; }
+.qi-351-fill::before { content: "\f1e9"; }
+.qi-399-fill::before { content: "\f1ea"; }
+.qi-400-fill::before { content: "\f1eb"; }
+.qi-401-fill::before { content: "\f1ec"; }
+.qi-402-fill::before { content: "\f1ed"; }
+.qi-403-fill::before { content: "\f1ee"; }
+.qi-404-fill::before { content: "\f1ef"; }
+.qi-405-fill::before { content: "\f1f0"; }
+.qi-406-fill::before { content: "\f1f1"; }
+.qi-407-fill::before { content: "\f1f2"; }
+.qi-408-fill::before { content: "\f1f3"; }
+.qi-409-fill::before { content: "\f1f4"; }
+.qi-410-fill::before { content: "\f1f5"; }
+.qi-456-fill::before { content: "\f1f6"; }
+.qi-457-fill::before { content: "\f1f7"; }
+.qi-499-fill::before { content: "\f1f8"; }
+.qi-500-fill::before { content: "\f1f9"; }
+.qi-501-fill::before { content: "\f1fa"; }
+.qi-502-fill::before { content: "\f1fb"; }
+.qi-503-fill::before { content: "\f1fc"; }
+.qi-504-fill::before { content: "\f1fd"; }
+.qi-507-fill::before { content: "\f1fe"; }
+.qi-508-fill::before { content: "\f1ff"; }
+.qi-509-fill::before { content: "\f200"; }
+.qi-510-fill::before { content: "\f201"; }
+.qi-511-fill::before { content: "\f202"; }
+.qi-512-fill::before { content: "\f203"; }
+.qi-513-fill::before { content: "\f204"; }
+.qi-514-fill::before { content: "\f205"; }
+.qi-515-fill::before { content: "\f206"; }
+.qi-900-fill::before { content: "\f207"; }
+.qi-901-fill::before { content: "\f208"; }
+.qi-999-fill::before { content: "\f209"; }
+.qi-qweather-fill::before { content: "\f20a"; }
+.qi-qweather::before { content: "\f20b"; }
+.qi-sunny::before { content: "\f101"; }
+.qi-cloudy::before { content: "\f102"; }
+.qi-few-clouds::before { content: "\f103"; }
+.qi-partly-cloudy::before { content: "\f104"; }
+.qi-overcast::before { content: "\f105"; }
+.qi-clear-night::before { content: "\f106"; }
+.qi-cloudy-night::before { content: "\f107"; }
+.qi-few-clouds-night::before { content: "\f108"; }
+.qi-partly-cloudy-night::before { content: "\f109"; }
+.qi-shower-rain::before { content: "\f10a"; }
+.qi-heavy-shower-rain::before { content: "\f10b"; }
+.qi-thundershower::before { content: "\f10c"; }
+.qi-heavy-thunderstorm::before { content: "\f10d"; }
+.qi-thundershower-with-hail::before { content: "\f10e"; }
+.qi-light-rain::before { content: "\f10f"; }
+.qi-moderate-rain::before { content: "\f110"; }
+.qi-heavy-rain::before { content: "\f111"; }
+.qi-extreme-rain::before { content: "\f112"; }
+.qi-drizzle-rain::before { content: "\f113"; }
+.qi-storm::before { content: "\f114"; }
+.qi-heavy-storm::before { content: "\f115"; }
+.qi-severe-storm::before { content: "\f116"; }
+.qi-freezing-rain::before { content: "\f117"; }
+.qi-light-to-moderate-rain::before { content: "\f118"; }
+.qi-moderate-to-heavy-rain::before { content: "\f119"; }
+.qi-heavy-rain-to-storm::before { content: "\f11a"; }
+.qi-storm-to-heavy-storm::before { content: "\f11b"; }
+.qi-heavy-to-severe-storm::before { content: "\f11c"; }
+.qi-shower-rain-night::before { content: "\f11d"; }
+.qi-heavy-shower-rain-night::before { content: "\f11e"; }
+.qi-rain::before { content: "\f11f"; }
+.qi-light-snow::before { content: "\f120"; }
+.qi-moderate-snow::before { content: "\f121"; }
+.qi-heavy-snow::before { content: "\f122"; }
+.qi-snowstorm::before { content: "\f123"; }
+.qi-sleet::before { content: "\f124"; }
+.qi-rain-and-snow::before { content: "\f125"; }
+.qi-shower-snow::before { content: "\f126"; }
+.qi-snow-flurry::before { content: "\f127"; }
+.qi-light-to-moderate-snow::before { content: "\f128"; }
+.qi-moderate-to-heavy-snow::before { content: "\f129"; }
+.qi-heavy-snow-to-snowstorm::before { content: "\f12a"; }
+.qi-shower-snow-night::before { content: "\f12b"; }
+.qi-snow-flurry-night::before { content: "\f12c"; }
+.qi-snow::before { content: "\f12d"; }
+.qi-mist::before { content: "\f12e"; }
+.qi-foggy::before { content: "\f12f"; }
+.qi-haze::before { content: "\f130"; }
+.qi-sand::before { content: "\f131"; }
+.qi-dust::before { content: "\f132"; }
+.qi-duststorm::before { content: "\f133"; }
+.qi-sandstorm::before { content: "\f134"; }
+.qi-dense-fog::before { content: "\f135"; }
+.qi-strong-fog::before { content: "\f136"; }
+.qi-moderate-haze::before { content: "\f137"; }
+.qi-heavy-haze::before { content: "\f138"; }
+.qi-severe-haze::before { content: "\f139"; }
+.qi-heavy-fog::before { content: "\f13a"; }
+.qi-extra-heavy-fog::before { content: "\f13b"; }
+.qi-new-moon::before { content: "\f13c"; }
+.qi-waxing-crescent::before { content: "\f13d"; }
+.qi-first-quarter::before { content: "\f13e"; }
+.qi-waxing-gibbous::before { content: "\f13f"; }
+.qi-full-moon::before { content: "\f140"; }
+.qi-waning-gibbous::before { content: "\f141"; }
+.qi-last-quarter::before { content: "\f142"; }
+.qi-waning-crescent::before { content: "\f143"; }
+.qi-hot::before { content: "\f144"; }
+.qi-cold::before { content: "\f145"; }
+.qi-unknown::before { content: "\f146"; }
+.qi-typhoon::before { content: "\f147"; }
+.qi-tornado::before { content: "\f148"; }
+.qi-rainstorm::before { content: "\f149"; }
+.qi-snow-storm::before { content: "\f14a"; }
+.qi-cold-wave::before { content: "\f14b"; }
+.qi-gale::before { content: "\f14c"; }
+.qi-sandstorm-warning::before { content: "\f14d"; }
+.qi-low-temperature-freeze::before { content: "\f14e"; }
+.qi-high-temperature::before { content: "\f14f"; }
+.qi-heat-wave::before { content: "\f150"; }
+.qi-dry-hot-wind::before { content: "\f151"; }
+.qi-downburst::before { content: "\f152"; }
+.qi-avalanche::before { content: "\f153"; }
+.qi-lightning::before { content: "\f154"; }
+.qi-hail::before { content: "\f155"; }
+.qi-frost::before { content: "\f156"; }
+.qi-heavy-fog-warning::before { content: "\f157"; }
+.qi-low-level-wind-shearl::before { content: "\f158"; }
+.qi-haze-warning::before { content: "\f159"; }
+.qi-thunder-gust::before { content: "\f15a"; }
+.qi-road-icing::before { content: "\f15b"; }
+.qi-drought::before { content: "\f15c"; }
+.qi-gale-at-sea::before { content: "\f15d"; }
+.qi-heat-stroke::before { content: "\f15e"; }
+.qi-wildfire::before { content: "\f15f"; }
+.qi-grassland-fire::before { content: "\f160"; }
+.qi-freeze::before { content: "\f161"; }
+.qi-space-weather::before { content: "\f162"; }
+.qi-heavy-air-pollution::before { content: "\f163"; }
+.qi-low-temperature-rain-and-snow::before { content: "\f164"; }
+.qi-strong-convection::before { content: "\f165"; }
+.qi-ozone::before { content: "\f166"; }
+.qi-heavy-snow-warning::before { content: "\f167"; }
+.qi-cold-warning::before { content: "\f168"; }
+.qi-continuous-rain::before { content: "\f169"; }
+.qi-waterlogging::before { content: "\f16a"; }
+.qi-geological-hazard::before { content: "\f16b"; }
+.qi-heavy-rainfall::before { content: "\f16c"; }
+.qi-severely-falling-temperature::before { content: "\f16d"; }
+.qi-snow-disaster::before { content: "\f16e"; }
+.qi-wildfire-grassland::before { content: "\f16f"; }
+.qi-medical-meteorology::before { content: "\f170"; }
+.qi-thunderstorm::before { content: "\f171"; }
+.qi-school-closure::before { content: "\f172"; }
+.qi-factory-closure::before { content: "\f173"; }
+.qi-maritime-risk::before { content: "\f174"; }
+.qi-spring-dust::before { content: "\f175"; }
+.qi-falling-temperature::before { content: "\f176"; }
+.qi-typhoon-and-rainstorm::before { content: "\f177"; }
+.qi-severe-cold::before { content: "\f178"; }
+.qi-sand-dust::before { content: "\f179"; }
+.qi-sea-thunderstorms::before { content: "\f17a"; }
+.qi-sea-fog::before { content: "\f17b"; }
+.qi-sea-thunder::before { content: "\f17c"; }
+.qi-sea-typhoon::before { content: "\f17d"; }
+.qi-low-temperature::before { content: "\f17e"; }
+.qi-road-ice-and-snow::before { content: "\f17f"; }
+.qi-thunderstorm-and-gale::before { content: "\f180"; }
+.qi-continuous-low-temperature::before { content: "\f181"; }
+.qi-low-visibility::before { content: "\f182"; }
+.qi-strong-dust::before { content: "\f183"; }
+.qi-gale-in-sea-area::before { content: "\f184"; }
+.qi-short-duration-heavy-shower-rain::before { content: "\f185"; }
+.qi-short-lived-heavy-shower-rain::before { content: "\f186"; }
+.qi-sea-area-fog::before { content: "\f187"; }
+.qi-heat-stroke-conditions::before { content: "\f188"; }
+.qi-heavy-pollution-weather::before { content: "\f189"; }
+.qi-co-poisoning-weather-conditions::before { content: "\f18a"; }
+.qi-respiratory-disease-weather-wonditions::before { content: "\f18b"; }
+.qi-intestinal-disease-weather-wonditions::before { content: "\f18c"; }
+.qi-cardiovascular-disease-weather-wonditions::before { content: "\f18d"; }
+.qi-flooding-weather-risk::before { content: "\f18e"; }
+.qi-heavy-pollution-weather-conditions::before { content: "\f18f"; }
+.qi-urban-flooding-weather-risk::before { content: "\f190"; }
+.qi-flooding-weather-risk-2::before { content: "\f191"; }
+.qi-wildfire-weather-risk::before { content: "\f192"; }
+.qi-meteorological-drought::before { content: "\f193"; }
+.qi-agricultural-weather-risk::before { content: "\f194"; }
+.qi-strong-monsoon::before { content: "\f195"; }
+.qi-ice-accretion-on-wire::before { content: "\f196"; }
+.qi-stroke-weather-risk::before { content: "\f197"; }
+.qi-wildfire-grassland-risk::before { content: "\f198"; }
+.qi-thunderstorm-and-strong-winds::before { content: "\f199"; }
+.qi-low-temperature-freeze2::before { content: "\f19a"; }
+.qi-low-temperature-damage::before { content: "\f19b"; }
+.qi-national-agricultural-meteorological-risk::before { content: "\f19c"; }
+.qi-dry-hot-wind-risk-for-winter-wheat::before { content: "\f19d"; }
+.qi-flood::before { content: "\f2c5"; }
+.qi-urban-flooding::before { content: "\f2c6"; }
+.qi-reservoir-danger::before { content: "\f2c7"; }
+.qi-dike-danger::before { content: "\f2c8"; }
+.qi-ice-flood::before { content: "\f2c9"; }
+.qi-waterlogging2::before { content: "\f2ca"; }
+.qi-flood-and-waterlogging::before { content: "\f2cb"; }
+.qi-dry-water::before { content: "\f2cc"; }
+.qi-flood-and-flash-flood-in-small-and-medium-rivers::before { content: "\f2cd"; }
+.qi-difficulty-drinking-water-for-rural-people-and-animals::before { content: "\f2ce"; }
+.qi-flood-in-small-and-medium-rivers::before { content: "\f2cf"; }
+.qi-flood-and-drought-advisory::before { content: "\f2d0"; }
+.qi-urban-flood-risk::before { content: "\f2d1"; }
+.qi-flash-flood::before { content: "\f2d2"; }
+.qi-agricultural-drought::before { content: "\f2d3"; }
+.qi-urban-water-shortage::before { content: "\f2d4"; }
+.qi-ecological-drought::before { content: "\f2d5"; }
+.qi-disaster-risk-early-warning::before { content: "\f2d6"; }
+.qi-flash-flood-weather-risk::before { content: "\f2d7"; }
+.qi-water-conservancy-and-drought::before { content: "\f2d8"; }
+.qi-landslide::before { content: "\f2d9"; }
+.qi-debris-flows::before { content: "\f2da"; }
+.qi-landslide-event::before { content: "\f2db"; }
+.qi-ground-collapses::before { content: "\f2dc"; }
+.qi-ground-fissure::before { content: "\f2dd"; }
+.qi-land-subsidence::before { content: "\f2de"; }
+.qi-volcanic-eruption::before { content: "\f2df"; }
+.qi-geological-hazard-weather-risk::before { content: "\f2e0"; }
+.qi-geological-hazard-weather::before { content: "\f2e1"; }
+.qi-geological-hazard2::before { content: "\f2e2"; }
+.qi-geological-hazard-risk::before { content: "\f2e3"; }
+.qi-air-pollution-incident::before { content: "\f2f6"; }
+.qi-heavy-air-pollution-2::before { content: "\f2f7"; }
+.qi-air-pollution::before { content: "\f2f8"; }
+.qi-heavy-pollution-weather-2::before { content: "\f2f9"; }
+.qi-very-hot-weather::before { content: "\f1a1"; }
+.qi-strong-monsoon-signal::before { content: "\f1a2"; }
+.qi-landslip::before { content: "\f1a3"; }
+.qi-tropical-cyclone::before { content: "\f1a4"; }
+.qi-fire-danger::before { content: "\f1a5"; }
+.qi-flooding-in-the-northern-new-territories::before { content: "\f1a6"; }
+.qi-cold-weather::before { content: "\f1a7"; }
+.qi-thunderstorm2::before { content: "\f20c"; }
+.qi-rainstorm2::before { content: "\f20d"; }
+.qi-frost2::before { content: "\f20e"; }
+.qi-cold-surge-advisory::before { content: "\f1a8"; }
+.qi-strong-wind-advisory::before { content: "\f1a9"; }
+.qi-rainfall-advisory::before { content: "\f1aa"; }
+.qi-strong-monsoon-signal2::before { content: "\f20f"; }
+.qi-storm-surge2::before { content: "\f210"; }
+.qi-tropical-cyclone2::before { content: "\f211"; }
+.qi-rainstorm3::before { content: "\f212"; }
+.qi-thunderstorm3::before { content: "\f213"; }
+.qi-wind-warning::before { content: "\f1ab"; }
+.qi-snow-ice::before { content: "\f1ac"; }
+.qi-fog::before { content: "\f1ad"; }
+.qi-coastal-event::before { content: "\f1ae"; }
+.qi-forest-fire::before { content: "\f1af"; }
+.qi-rain-warning::before { content: "\f1b0"; }
+.qi-rain-flood::before { content: "\f1b1"; }
+.qi-thunderstorm4::before { content: "\f214"; }
+.qi-high-temperature2::before { content: "\f215"; }
+.qi-low-temperature2::before { content: "\f216"; }
+.qi-avalanches::before { content: "\f217"; }
+.qi-flooding::before { content: "\f218"; }
+.qi-rain-warning2::before { content: "\f250"; }
+.qi-wind::before { content: "\f21a"; }
+.qi-snow-warning::before { content: "\f1c7"; }
+.qi-zonda-wind::before { content: "\f1c8"; }
+.qi-storm-warning::before { content: "\f1c9"; }
+.qi-dust-raising-winds2::before { content: "\f21b"; }
+.qi-strong-surface-winds2::before { content: "\f21c"; }
+.qi-hot-day2::before { content: "\f21d"; }
+.qi-warm-night2::before { content: "\f21e"; }
+.qi-cold-day2::before { content: "\f21f"; }
+.qi-thunderstorm-and-lightning2::before { content: "\f220"; }
+.qi-hailstorm2::before { content: "\f221"; }
+.qi-sea-area-warning2::before { content: "\f222"; }
+.qi-fishermen-warning2::before { content: "\f223"; }
+.qi-heavy-snow-warning2::before { content: "\f243"; }
+.qi-dust-storm::before { content: "\f225"; }
+.qi-heat-wave2::before { content: "\f304"; }
+.qi-cold-wave2::before { content: "\f305"; }
+.qi-fog2::before { content: "\f228"; }
+.qi-heavy-rain-warning::before { content: "\f229"; }
+.qi-ground-frost2::before { content: "\f22a"; }
+.qi-fog3::before { content: "\f22b"; }
+.qi-thunder-rain2::before { content: "\f22c"; }
+.qi-thunder-storm::before { content: "\f22d"; }
+.qi-light-rain-warning::before { content: "\f22e"; }
+.qi-heavy-rain-warning2::before { content: "\f22f"; }
+.qi-fresh-wind::before { content: "\f230"; }
+.qi-thunderstorm-and-dust::before { content: "\f231"; }
+.qi-dust-warning::before { content: "\f232"; }
+.qi-high-wave::before { content: "\f233"; }
+.qi-frost3::before { content: "\f234"; }
+.qi-drop-in-visibility::before { content: "\f235"; }
+.qi-low-humidity2::before { content: "\f236"; }
+.qi-accumulated-rain2::before { content: "\f237"; }
+.qi-cold-wave3::before { content: "\f238"; }
+.qi-tornado2::before { content: "\f239"; }
+.qi-thunderstorm5::before { content: "\f23a"; }
+.qi-hail2::before { content: "\f23b"; }
+.qi-heavy-rainfall2::before { content: "\f23c"; }
+.qi-gale2::before { content: "\f23d"; }
+.qi-heat-wave3::before { content: "\f23e"; }
+.qi-cold-warning2::before { content: "\f23f"; }
+.qi-frost4::before { content: "\f240"; }
+.qi-drought2::before { content: "\f241"; }
+.qi-forest-fire2::before { content: "\f242"; }
+.qi-severely-falling-temperature2::before { content: "\f244"; }
+.qi-rainstorm4::before { content: "\f245"; }
+.qi-wind2::before { content: "\f246"; }
+.qi-snow-ice2::before { content: "\f247"; }
+.qi-freeze2::before { content: "\f248"; }
+.qi-thunderstorms::before { content: "\f30b"; }
+.qi-fog4::before { content: "\f24a"; }
+.qi-high-temperature3::before { content: "\f24b"; }
+.qi-low-temperature3::before { content: "\f24c"; }
+.qi-coastal-event2::before { content: "\f24d"; }
+.qi-forest-fire3::before { content: "\f24e"; }
+.qi-avalanches2::before { content: "\f24f"; }
+.qi-flood2::before { content: "\f251"; }
+.qi-rain-flood2::before { content: "\f252"; }
+.qi-mudflow2::before { content: "\f253"; }
+.qi-duststorm-warning::before { content: "\f254"; }
+.qi-freezing-rain-and-icing::before { content: "\f255"; }
+.qi-other-dangers::before { content: "\f256"; }
+.qi-severe-thunderstorms::before { content: "\f257"; }
+.qi-damaging-winds2::before { content: "\f258"; }
+.qi-veld-fire-conditions2::before { content: "\f259"; }
+.qi-weather-advisory2::before { content: "\f25a"; }
+.qi-thunderstorm6::before { content: "\f2e4"; }
+.qi-squall::before { content: "\f2e5"; }
+.qi-air-quality::before { content: "\f2e6"; }
+.qi-rainfall::before { content: "\f2e7"; }
+.qi-fog5::before { content: "\f2e8"; }
+.qi-heat::before { content: "\f2e9"; }
+.qi-wildfire2::before { content: "\f2ea"; }
+.qi-wind3::before { content: "\f2eb"; }
+.qi-freezing-rain-warning::before { content: "\f2ec"; }
+.qi-tornado3::before { content: "\f2ed"; }
+.qi-blizzard::before { content: "\f2ee"; }
+.qi-weather-warning::before { content: "\f2ef"; }
+.qi-winter-storm::before { content: "\f2f0"; }
+.qi-freezing-drizzle::before { content: "\f2f1"; }
+.qi-snowfall::before { content: "\f2f2"; }
+.qi-blowing-snow::before { content: "\f2f3"; }
+.qi-extreme-cold::before { content: "\f2f4"; }
+.qi-frost5::before { content: "\f2f5"; }
+.qi-hazardous-seas-warning::before { content: "\f25b"; }
+.qi-heavy-freezing-spray-warning::before { content: "\f25c"; }
+.qi-red-flag-warning::before { content: "\f25d"; }
+.qi-freeze-warning::before { content: "\f25e"; }
+.qi-hard-freeze-warning::before { content: "\f25f"; }
+.qi-extreme-cold-warning::before { content: "\f260"; }
+.qi-wind-chill-warning::before { content: "\f261"; }
+.qi-gale-warning::before { content: "\f262"; }
+.qi-excessive-heat-warning::before { content: "\f263"; }
+.qi-lake-effect-snow-warning::before { content: "\f264"; }
+.qi-blowing-dust-warning::before { content: "\f265"; }
+.qi-dust-storm-warning::before { content: "\f266"; }
+.qi-storm-warning2::before { content: "\f267"; }
+.qi-tropical-storm-warning::before { content: "\f268"; }
+.qi-high-wind-warning::before { content: "\f269"; }
+.qi-high-surf-warning::before { content: "\f26a"; }
+.qi-flood-warning::before { content: "\f26b"; }
+.qi-lakeshore-flood-warning::before { content: "\f26c"; }
+.qi-coastal-flood-warning::before { content: "\f26d"; }
+.qi-ashfall-warning::before { content: "\f26e"; }
+.qi-volcano-warning::before { content: "\f26f"; }
+.qi-earthquake-warning::before { content: "\f270"; }
+.qi-avalanche-warning::before { content: "\f271"; }
+.qi-winter-storm-warning::before { content: "\f272"; }
+.qi-ice-storm-warning::before { content: "\f273"; }
+.qi-snow-squall-warning::before { content: "\f274"; }
+.qi-blizzard-warning::before { content: "\f275"; }
+.qi-special-marine-warning::before { content: "\f276"; }
+.qi-typhoon-warning::before { content: "\f277"; }
+.qi-hurricane-warning::before { content: "\f278"; }
+.qi-hurricane-force-wind-warning::before { content: "\f279"; }
+.qi-storm-surge-warning::before { content: "\f27a"; }
+.qi-flash-flood-warning::before { content: "\f27b"; }
+.qi-severe-thunderstorm-warning::before { content: "\f27c"; }
+.qi-extreme-wind-warning::before { content: "\f27d"; }
+.qi-tornado-warning::before { content: "\f27e"; }
+.qi-tsunami-warning::before { content: "\f27f"; }
+.qi-fire-weather-watch::before { content: "\f280"; }
+.qi-freeze-watch::before { content: "\f281"; }
+.qi-hard-freeze-watch::before { content: "\f282"; }
+.qi-wind-chill-watch::before { content: "\f283"; }
+.qi-extreme-cold-watch::before { content: "\f284"; }
+.qi-excessive-heat-watch::before { content: "\f285"; }
+.qi-high-wind-watch::before { content: "\f286"; }
+.qi-flood-watch::before { content: "\f287"; }
+.qi-lakeshore-flood-watch::before { content: "\f288"; }
+.qi-coastal-flood-watch::before { content: "\f289"; }
+.qi-heavy-freezing-spray-watch::before { content: "\f28a"; }
+.qi-hazardous-seas-watch::before { content: "\f28b"; }
+.qi-winter-storm-watch::before { content: "\f28c"; }
+.qi-gale-watch::before { content: "\f28d"; }
+.qi-avalanche-watch::before { content: "\f28e"; }
+.qi-storm-watch::before { content: "\f28f"; }
+.qi-tropical-storm-watch::before { content: "\f290"; }
+.qi-typhoon-watch::before { content: "\f291"; }
+.qi-hurricane-force-wind-watch::before { content: "\f292"; }
+.qi-hurricane-watch::before { content: "\f293"; }
+.qi-storm-surge-watch::before { content: "\f294"; }
+.qi-flash-flood-watch::before { content: "\f295"; }
+.qi-severe-thunderstorm-watch::before { content: "\f296"; }
+.qi-tornado-watch::before { content: "\f297"; }
+.qi-tsunami-watch::before { content: "\f298"; }
+.qi-air-stagnation-advisory::before { content: "\f299"; }
+.qi-low-water-advisory::before { content: "\f29a"; }
+.qi-freezing-spray-advisory::before { content: "\f29b"; }
+.qi-freezing-fog-advisory::before { content: "\f29c"; }
+.qi-ashfall-advisory::before { content: "\f29d"; }
+.qi-frost-advisory::before { content: "\f29e"; }
+.qi-wind-advisory::before { content: "\f29f"; }
+.qi-lake-wind-advisory::before { content: "\f2a0"; }
+.qi-blowing-dust-advisory::before { content: "\f2a1"; }
+.qi-dust-advisory::before { content: "\f2a2"; }
+.qi-brisk-wind-advisory::before { content: "\f2a3"; }
+.qi-small-craft-advisory::before { content: "\f2a4"; }
+.qi-small-craft-advisory-for-winds::before { content: "\f2a5"; }
+.qi-small-craft-advisory-for-rough-bar::before { content: "\f2a6"; }
+.qi-small-craft-advisory-for-hazardous-seas::before { content: "\f2a7"; }
+.qi-dense-smoke-advisory::before { content: "\f2a8"; }
+.qi-dense-fog-advisory::before { content: "\f2a9"; }
+.qi-high-surf-advisory::before { content: "\f2aa"; }
+.qi-coastal-flood-advisory::before { content: "\f2ab"; }
+.qi-lakeshore-flood-advisory::before { content: "\f2ac"; }
+.qi-hydrologic-advisory::before { content: "\f2ad"; }
+.qi-flood-advisory::before { content: "\f2ae"; }
+.qi-heat-advisory::before { content: "\f2af"; }
+.qi-wind-chill-advisory::before { content: "\f2b0"; }
+.qi-winter-weather-advisory::before { content: "\f2b1"; }
+.qi-avalanche-advisory::before { content: "\f2b2"; }
+.qi-tsunami-advisory::before { content: "\f2b3"; }
+.qi-flood-statement::before { content: "\f2b4"; }
+.qi-hydrologic-outlook::before { content: "\f2b5"; }
+.qi-hazardous-weather-outlook::before { content: "\f2b6"; }
+.qi-air-quality-alert::before { content: "\f2b7"; }
+.qi-extreme-fire-danger::before { content: "\f2b8"; }
+.qi-marine-weather-statement::before { content: "\f2b9"; }
+.qi-special-weather-statement::before { content: "\f2ba"; }
+.qi-lakeshore-flood-statement::before { content: "\f2bb"; }
+.qi-coastal-flood-statement::before { content: "\f2bc"; }
+.qi-beach-hazards-statement::before { content: "\f2bd"; }
+.qi-rip-current-statement::before { content: "\f2be"; }
+.qi-tropical-depression-local-statement::before { content: "\f2bf"; }
+.qi-tropical-storm-local-statement::before { content: "\f2c0"; }
+.qi-typhoon-local-statement::before { content: "\f2c1"; }
+.qi-hurricane-local-statement::before { content: "\f2c2"; }
+.qi-severe-weather-statement::before { content: "\f2c3"; }
+.qi-flash-flood-statement::before { content: "\f2c4"; }
+.qi-heavy-rain2::before { content: "\f2fa"; }
+.qi-very-heavy-rain::before { content: "\f2fb"; }
+.qi-moderate-rain2::before { content: "\f2fc"; }
+.qi-light-rain2::before { content: "\f2fd"; }
+.qi-heavy-rain3::before { content: "\f2fe"; }
+.qi-light-mist::before { content: "\f2ff"; }
+.qi-raised-dust::before { content: "\f300"; }
+.qi-high-winds::before { content: "\f301"; }
+.qi-dusty-plankton::before { content: "\f302"; }
+.qi-active-winds::before { content: "\f303"; }
+.qi-fog6::before { content: "\f306"; }
+.qi-light-fog::before { content: "\f307"; }
+.qi-heavy-rain4::before { content: "\f308"; }
+.qi-strong-wind::before { content: "\f309"; }
+.qi-heavy-snow2::before { content: "\f30a"; }
+.qi-road-snowfall::before { content: "\f30c"; }
+.qi-warning-default::before { content: "\f1cb"; }
+.qi-sunny-fill::before { content: "\f1cc"; }
+.qi-cloudy-fill::before { content: "\f1cd"; }
+.qi-few-clouds-fill::before { content: "\f1ce"; }
+.qi-partly-cloudy-fill::before { content: "\f1cf"; }
+.qi-overcast-fill::before { content: "\f1d0"; }
+.qi-clear-night-fill::before { content: "\f1d1"; }
+.qi-cloudy-night-fill::before { content: "\f1d2"; }
+.qi-few-clouds-night-fill::before { content: "\f1d3"; }
+.qi-partly-cloudy-night-fill::before { content: "\f1d4"; }
+.qi-shower-rain-fill::before { content: "\f1d5"; }
+.qi-heavy-shower-rain-fill::before { content: "\f1d6"; }
+.qi-thundershower-fill::before { content: "\f1d7"; }
+.qi-heavy-thunderstorm-fill::before { content: "\f1d8"; }
+.qi-thundershower-with-hail-fill::before { content: "\f1d9"; }
+.qi-light-rain-fill::before { content: "\f1da"; }
+.qi-moderate-rain-fill::before { content: "\f1db"; }
+.qi-heavy-rain-fill::before { content: "\f1dc"; }
+.qi-extreme-rain-fill::before { content: "\f1dd"; }
+.qi-drizzle-rain-fill::before { content: "\f1de"; }
+.qi-storm-fill::before { content: "\f1df"; }
+.qi-heavy-storm-fill::before { content: "\f1e0"; }
+.qi-severe-storm-fill::before { content: "\f1e1"; }
+.qi-freezing-rain-fill::before { content: "\f1e2"; }
+.qi-light-to-moderate-rain-fill::before { content: "\f1e3"; }
+.qi-moderate-to-heavy-rain-fill::before { content: "\f1e4"; }
+.qi-heavy-rain-to-storm-fill::before { content: "\f1e5"; }
+.qi-storm-to-heavy-storm-fill::before { content: "\f1e6"; }
+.qi-heavy-to-severe-storm-fill::before { content: "\f1e7"; }
+.qi-shower-rain-night-fill::before { content: "\f1e8"; }
+.qi-heavy-shower-rain-night-fill::before { content: "\f1e9"; }
+.qi-rain-fill::before { content: "\f1ea"; }
+.qi-light-snow-fill::before { content: "\f1eb"; }
+.qi-moderate-snow-fill::before { content: "\f1ec"; }
+.qi-heavy-snow-fill::before { content: "\f1ed"; }
+.qi-snowstorm-fill::before { content: "\f1ee"; }
+.qi-sleet-fill::before { content: "\f1ef"; }
+.qi-rain-and-snow-fill::before { content: "\f1f0"; }
+.qi-shower-snow-fill::before { content: "\f1f1"; }
+.qi-snow-flurry-fill::before { content: "\f1f2"; }
+.qi-light-to-moderate-snow-fill::before { content: "\f1f3"; }
+.qi-moderate-to-heavy-snow-fill::before { content: "\f1f4"; }
+.qi-heavy-snow-to-snowstorm-fill::before { content: "\f1f5"; }
+.qi-shower-snow-night-fill::before { content: "\f1f6"; }
+.qi-snow-flurry-night-fill::before { content: "\f1f7"; }
+.qi-snow-fill::before { content: "\f1f8"; }
+.qi-mist-fill::before { content: "\f1f9"; }
+.qi-foggy-fill::before { content: "\f1fa"; }
+.qi-haze-fill::before { content: "\f1fb"; }
+.qi-sand-fill::before { content: "\f1fc"; }
+.qi-dust-fill::before { content: "\f1fd"; }
+.qi-duststorm-fill::before { content: "\f1fe"; }
+.qi-sandstorm-fill::before { content: "\f1ff"; }
+.qi-dense-fog-fill::before { content: "\f200"; }
+.qi-strong-fog-fill::before { content: "\f201"; }
+.qi-moderate-haze-fill::before { content: "\f202"; }
+.qi-heavy-haze-fill::before { content: "\f203"; }
+.qi-severe-haze-fill::before { content: "\f204"; }
+.qi-heavy-fog-fill::before { content: "\f205"; }
+.qi-extra-heavy-fog-fill::before { content: "\f206"; }
+.qi-hot-fill::before { content: "\f207"; }
+.qi-cold-fill::before { content: "\f208"; }
+.qi-unknown-fill::before { content: "\f209"; }

+ 55 - 0
src/types/furniture.ts

@@ -0,0 +1,55 @@
+/**
+ * 房间类型
+ * @description 用于区分不同的房间类型,如客厅、餐厅、卧室、卫生间等
+ */
+export type RoomType = 'livingroom' | 'diningroom' | 'bedroom' | 'bathroom'
+
+/**
+ * 卫生间家具类型
+ * @description 如脸盆、门、地板、淋浴、马桶等
+ */
+export type BathroomType = 'bath_basin' | 'bath_door' | 'bath_floor' | 'bath_shower' | 'bath_toilet'
+
+/**
+ * 卧室家具类型
+ * @description 如床头柜、床、柜子、化妆椅、化妆镜、地板等
+ */
+export type BedroomType =
+  | 'bed'
+  | 'bed_table'
+  | 'bed_cabinet'
+  | 'bed_dressing_chair'
+  | 'bed_dressing_mirror'
+  | 'bed_floor'
+
+/**
+ * 餐厅家具类型
+ * @description 如餐桌、餐桌(方形)、餐椅、地板等
+ */
+export type DiningRoomType =
+  | 'dining_chair'
+  | 'dining_table'
+  | 'dining_table_rect'
+  | 'dining_fridge'
+  | 'dining_floor'
+
+/**
+ * 客厅家具类型
+ * @description 如地板、沙发、茶几、电视柜、冰箱等
+ */
+export type LivingRoomType =
+  | 'living_floor'
+  | 'living_sofa'
+  | 'living_tea_table'
+  | 'living_tv_stand'
+  | 'living_bookcase'
+
+/**
+ * 家具图标类型
+ * @description 所有房间家具图标的联合类型,用于区分不同的家具图标
+ * @union BathroomType - 卫生间家具类型
+ * @union BedroomType - 卧室家具类型
+ * @union DiningRoomType - 餐厅家具类型
+ * @union LivingRoomType - 客厅家具类型
+ */
+export type FurnitureIconType = BathroomType | BedroomType | DiningRoomType | LivingRoomType

+ 50 - 0
src/types/global.d.ts

@@ -0,0 +1,50 @@
+type Recordable = Record<string, unknown>
+
+// 基础数据类型
+type ID = number | string | null
+
+/**
+ * 开关数据类型
+ */
+type Switch = 0 | 1
+
+/**
+ * 安装方式类型
+ * @param Wall 墙装
+ * @param Ceiling 顶装
+ */
+type InstallWay = 'Wall' | 'Ceiling'
+
+/**
+ * 安装位置类型
+ * @param Toilet 卫生间
+ * @param Bedroom 卧室
+ * @param LivingRoom 客厅
+ * @param Restaurant 餐厅
+ */
+type InstallPosition = 'Toilet' | 'Bedroom' | 'LivingRoom' | 'Restaurant'
+
+/**
+ * 北向角度类型
+ * @param 0 0度
+ * @param 90 90度
+ * @param 180 180度
+ * @param 270 270度
+ */
+type NorthAngle = 0 | 90 | 180 | 270
+
+/**
+ * 通用响应类型
+ * @param code 响应码
+ * @param message 响应信息
+ * @param dateTime 响应时间
+ * @param data 响应数据
+ * @param traceId 跟踪ID
+ */
+interface ResponseData<T> {
+  code: string
+  message: string
+  dateTime: string
+  data: T
+  traceId: string
+}

+ 44 - 0
src/utils/index.ts

@@ -0,0 +1,44 @@
+/**
+ * 格式化秒数为年、月、日、时、分、秒的字符串表示
+ * @param {number} seconds - 秒数
+ * @returns {string} 格式化后的字符串,例如:"1年2月3日4时5分6秒"
+ * @example
+ * formatSeconds(30); // 30秒
+ * formatSeconds(3600); // 1时1分
+ */
+export function formatSeconds(seconds: number) {
+  // 处理 NaN 和无效输入
+  if (isNaN(seconds) || seconds === null || seconds === undefined) {
+    return ''
+  }
+
+  // 处理边界情况:0秒
+  if (seconds === 0) return '0秒'
+
+  // 确保输入为整数
+  seconds = Math.floor(Number(seconds))
+
+  // 计算各时间单位
+  const totalDays = Math.floor(seconds / 86400)
+  const years = Math.floor(totalDays / 365)
+  const months = Math.floor((totalDays % 365) / 30)
+  const days = (totalDays % 365) % 30
+  const hours = Math.floor(seconds / 3600) % 24
+  const minutes = Math.floor((seconds % 3600) / 60)
+  const secs = seconds % 60
+
+  // 构建结果数组(自动过滤0值)
+  const parts = []
+  if (years > 0) parts.push(`${years}年`)
+  if (months > 0) parts.push(`${months}月`)
+  if (days > 0) {
+    parts.push(`${days}日`)
+    parts.push(`${days}天`)
+  }
+  if (hours > 0) parts.push(`${hours}时`)
+  if (minutes > 0) parts.push(`${minutes}分`)
+  if (secs > 0) parts.push(`${secs}秒`)
+
+  // 处理所有单位都是0的情况(当seconds为小数时可能发生)
+  return parts.length > 0 ? parts.join('') : '0秒'
+}

Daži faili netika attēloti, jo izmaiņu fails ir pārāk liels