Browse Source

删除pc,迁移到pc-v3

Fade 1 month ago
parent
commit
69259ebc74
73 changed files with 0 additions and 29713 deletions
  1. 0 8
      pc/.claude/settings.local.json
  2. 0 24
      pc/.gitignore
  3. 0 74
      pc/CLAUDE.md
  4. 0 33
      pc/README.md
  5. 0 5
      pc/babel.config.js
  6. 0 19
      pc/jsconfig.json
  7. 0 12583
      pc/package-lock.json
  8. 0 51
      pc/package.json
  9. BIN
      pc/public/favicon.ico
  10. 0 17
      pc/public/index.html
  11. 0 40
      pc/scripts/capture-one.js
  12. 0 120
      pc/scripts/capture-screenshots.js
  13. 0 171
      pc/scripts/capture-supplements.js
  14. 0 14
      pc/src/App.vue
  15. BIN
      pc/src/assets/logo.png
  16. 0 140
      pc/src/components/EquityLinkDialog.vue
  17. 0 58
      pc/src/components/HelloWorld.vue
  18. 0 250
      pc/src/components/Layout.vue
  19. 0 119
      pc/src/components/RongshuProductDialog.vue
  20. 0 88
      pc/src/components/ShowPagesDialog.vue
  21. 0 119
      pc/src/components/crowdNameList/AdjustMemberDialog.vue
  22. 0 199
      pc/src/components/crowdNameList/UploadAdjustDialog.vue
  23. 0 131
      pc/src/components/crowdTask/AddRuleGroupDialog.vue
  24. 0 40
      pc/src/components/crowdTask/SampleDetailDialog.vue
  25. 0 268
      pc/src/components/ruleGroup/RuleEditDialog.vue
  26. 0 549
      pc/src/components/tag/RuleBuilder.vue
  27. 0 200
      pc/src/config/metaRules.js
  28. 0 297
      pc/src/config/tagParams.js
  29. 0 18
      pc/src/main.js
  30. 0 1026
      pc/src/mock/index.js
  31. 0 273
      pc/src/router/index.js
  32. 0 129
      pc/src/store/crowdNameListStore.js
  33. 0 191
      pc/src/store/crowdTaskStore.js
  34. 0 163
      pc/src/store/ruleGroupStore.js
  35. 0 87
      pc/src/store/smsTemplateStore.js
  36. 0 184
      pc/src/store/tagStore.js
  37. 0 19
      pc/src/utils/linkOptions.js
  38. 0 20
      pc/src/utils/request.js
  39. 0 974
      pc/src/views/AddEquityProduct.vue
  40. 0 527
      pc/src/views/AuditDetail.vue
  41. 0 752
      pc/src/views/AuditEquityProduct.vue
  42. 0 167
      pc/src/views/AuditList.vue
  43. 0 144
      pc/src/views/CampaignManagement.vue
  44. 0 230
      pc/src/views/CashDiscountGrant.vue
  45. 0 313
      pc/src/views/ChannelManagement.vue
  46. 0 348
      pc/src/views/CreditCardEquity.vue
  47. 0 268
      pc/src/views/CrowdNameListEdit.vue
  48. 0 274
      pc/src/views/CrowdTaskEdit.vue
  49. 0 329
      pc/src/views/CrowdTaskList.vue
  50. 0 188
      pc/src/views/CrowdTaskView.vue
  51. 0 253
      pc/src/views/CustomerEquityGrant.vue
  52. 0 966
      pc/src/views/EditEquityProduct.vue
  53. 0 209
      pc/src/views/EquityGrantCampaign.vue
  54. 0 375
      pc/src/views/EquityGrantCampaignEdit.vue
  55. 0 175
      pc/src/views/EquityGrantCampaignView.vue
  56. 0 502
      pc/src/views/EquityProductCoupons.vue
  57. 0 455
      pc/src/views/EquityProducts.vue
  58. 0 132
      pc/src/views/Login.vue
  59. 0 726
      pc/src/views/PageAssembly.vue
  60. 0 396
      pc/src/views/PageHomeConfig.vue
  61. 0 22
      pc/src/views/PageManagement.vue
  62. 0 598
      pc/src/views/PageZoneConfig.vue
  63. 0 171
      pc/src/views/PointsDetail.vue
  64. 0 291
      pc/src/views/RuleGroupEdit.vue
  65. 0 175
      pc/src/views/RuleGroupList.vue
  66. 0 127
      pc/src/views/RuleGroupView.vue
  67. 0 235
      pc/src/views/SmsSendRecord.vue
  68. 0 262
      pc/src/views/SmsTemplateList.vue
  69. 0 260
      pc/src/views/TagEdit.vue
  70. 0 201
      pc/src/views/TagList.vue
  71. 0 104
      pc/src/views/TagView.vue
  72. 0 824
      pc/src/views/ViewEquityProduct.vue
  73. 0 13
      pc/vue.config.js

+ 0 - 8
pc/.claude/settings.local.json

@@ -1,8 +0,0 @@
-{
-  "permissions": {
-    "allow": [
-      "Bash(vue create:*)",
-      "Bash(printf \"y\\\\n\")"
-    ]
-  }
-}

+ 0 - 24
pc/.gitignore

@@ -1,24 +0,0 @@
-.DS_Store
-node_modules
-/dist
-
-
-# local env files
-.env.local
-.env.*.local
-
-# Log files
-npm-debug.log*
-yarn-debug.log*
-yarn-error.log*
-pnpm-debug.log*
-
-# Editor directories and files
-.idea
-.vscode
-*.suo
-*.ntvs*
-*.njsproj
-*.sln
-*.sw?
-.vercel

+ 0 - 74
pc/CLAUDE.md

@@ -1,74 +0,0 @@
-# [CLAUDE.md](http://CLAUDE.md)
-
-This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
-
-## 语言约束
-
-本项目默认使用简体中文输出(界面文案、注释、文档),技术名词(API、URL、ID 等)可保留英文。
-
-## 常用命令
-
-```bash
-npm install        # 安装依赖
-npm run serve      # 启动开发服务器(热更新,使用轮询监听文件)
-npm run build      # 生产构建
-npm run lint       # ESLint 校验并自动修复
-```
-
-本项目没有测试框架,未配置单测命令。
-
-## 技术栈
-
--   Vue 2.6 + Vue Router 3 + Element UI 2.15
-    
--   Axios 封装请求;Mock.js 拦截 `/api/*` 实现前端假数据
-    
--   vue-quill-editor 富文本编辑
-    
--   Vue CLI 5 构建,开发服务器开启 `usePolling` 文件监听(适配 Docker/WSL 等场景)
-    
-
-## 整体架构
-
-这是一个 PM(产品经理)用于快速搭建管理后台 Demo 的 Vue 项目,**不连接真实后端**,所有数据通过 Mock.js 在浏览器内拦截生成。
-
-### 启动流程
-
-`src/main.js` 引入 Element UI、富文本、router 和 `src/mock`(重要:导入 mock 模块即注册所有 `Mock.mock(...)` 拦截器,删除此行将导致接口全部 404)。
-
-### 路由与布局(认证骨架)
-
--   `src/router/index.js` 定义全部路由,`beforeEach` 检查 `localStorage.token`,无 token 跳 `/login`
-    
--   `/login` 是独立路由;其余页面全部作为 `Layout.vue` 的 children 渲染在 `<router-view />` 中
-    
--   `Layout.vue` 承担侧边栏菜单 + 顶部面包屑 + 用户下拉。**新增页面 = 在 router 里加一条 children + 在 Layout 的** `<el-menu>` **里加对应菜单项 + 在** `currentTitle` **映射里加面包屑标题**
-    
-
-### 请求层
-
--   `src/utils/request.js` 统一封装 axios:
-    
-    -   请求拦截自动带 `Authorization: Bearer <token>`
-        
-    -   响应拦截约定 `res.data.code === 200` 视为成功,否则 `Message.error` 并 reject
-        
-    -   401 自动清 token 并跳登录
-        
--   所有 Mock 返回体必须遵循 `{ code, data, msg }` 结构,否则会被响应拦截器判为失败
-    
-
-### Mock 数据
-
--   `src/mock/index.js` 用 `Mock.mock(url, method, handler)` 注册接口。新增后端接口时在这里加一条拦截
-    
--   正则形式 `/\/api\/foo/` 可匹配带 query 的 URL
-    
-
-### 定制化模块约定
-
-路由里已有 `customization/hsbc/equity-products` 这类命名,表示"定制客户/功能"的层级结构。新增定制页面遵循 `customization/<客户>/<模块>` 的路径前缀,便于区分通用功能与客户定制功能。
-
-## 父目录上下文
-
-本项目位于 `pm_agent/` 工作空间下的 `pc/` 目录,是整个 PM 工作流(需求分析 → PRD → PC Demo)的最后一环。父级 `CLAUDE.md` 定义了 `pc-demo` skill,当用户说"做一个后台页面/管理界面/添加菜单"等触发词时会进入此项目开发流程。

+ 0 - 33
pc/README.md

@@ -1,33 +0,0 @@
-## Project setup
-
-```
-npm install
-```
-
-### Compiles and hot-reloads for development
-
-```
-npm run serve
-```
-
-### Compiles and minifies for production
-
-```
-npm run build
-```
-
-### Lints and fixes files
-
-```
-npm run lint
-```
-
-### Customize configuration
-
-See [Configuration Reference](https://cli.vuejs.org/config/).
-
-<br>
-
-<br>
-
-<br>

+ 0 - 5
pc/babel.config.js

@@ -1,5 +0,0 @@
-module.exports = {
-  presets: [
-    '@vue/cli-plugin-babel/preset'
-  ]
-}

+ 0 - 19
pc/jsconfig.json

@@ -1,19 +0,0 @@
-{
-  "compilerOptions": {
-    "target": "es5",
-    "module": "esnext",
-    "baseUrl": "./",
-    "moduleResolution": "node",
-    "paths": {
-      "@/*": [
-        "src/*"
-      ]
-    },
-    "lib": [
-      "esnext",
-      "dom",
-      "dom.iterable",
-      "scripthost"
-    ]
-  }
-}

+ 0 - 12583
pc/package-lock.json

@@ -1,12583 +0,0 @@
-{
-  "name": "liso",
-  "version": "0.1.0",
-  "lockfileVersion": 3,
-  "requires": true,
-  "packages": {
-    "": {
-      "name": "liso",
-      "version": "0.1.0",
-      "dependencies": {
-        "axios": "^1.15.0",
-        "core-js": "^3.8.3",
-        "element-ui": "^2.15.14",
-        "mockjs": "^1.1.0",
-        "vue": "^2.6.14",
-        "vue-quill-editor": "^3.0.6",
-        "vue-router": "^3.6.5"
-      },
-      "devDependencies": {
-        "@babel/core": "^7.12.16",
-        "@babel/eslint-parser": "^7.12.16",
-        "@vue/cli-plugin-babel": "~5.0.0",
-        "@vue/cli-plugin-eslint": "~5.0.0",
-        "@vue/cli-service": "~5.0.0",
-        "eslint": "^7.32.0",
-        "eslint-plugin-vue": "^8.0.3",
-        "playwright": "^1.60.0",
-        "vue-template-compiler": "^2.6.14"
-      }
-    },
-    "node_modules/@achrinza/node-ipc": {
-      "version": "9.2.10",
-      "resolved": "https://registry.npmmirror.com/@achrinza/node-ipc/-/node-ipc-9.2.10.tgz",
-      "integrity": "sha512-rCkw57K82y1XA9KwBmuMrupFQr9VOS4Rn77vW2UD2j0+HjlP/npSON9COkUIfocd95B4wv5EpfWMr6lGD4lN3A==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@node-ipc/js-queue": "2.0.3",
-        "event-pubsub": "4.3.0",
-        "js-message": "1.0.7"
-      },
-      "engines": {
-        "node": "8 || 9 || 10 || 11 || 12 || 13 || 14 || 15 || 16 || 17 || 18 || 19 || 20 || 21 || 22 || 23 || 24 || 25"
-      }
-    },
-    "node_modules/@babel/code-frame": {
-      "version": "7.29.0",
-      "resolved": "https://registry.npmmirror.com/@babel/code-frame/-/code-frame-7.29.0.tgz",
-      "integrity": "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@babel/helper-validator-identifier": "^7.28.5",
-        "js-tokens": "^4.0.0",
-        "picocolors": "^1.1.1"
-      },
-      "engines": {
-        "node": ">=6.9.0"
-      }
-    },
-    "node_modules/@babel/compat-data": {
-      "version": "7.29.0",
-      "resolved": "https://registry.npmmirror.com/@babel/compat-data/-/compat-data-7.29.0.tgz",
-      "integrity": "sha512-T1NCJqT/j9+cn8fvkt7jtwbLBfLC/1y1c7NtCeXFRgzGTsafi68MRv8yzkYSapBnFA6L3U2VSc02ciDzoAJhJg==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=6.9.0"
-      }
-    },
-    "node_modules/@babel/core": {
-      "version": "7.29.0",
-      "resolved": "https://registry.npmmirror.com/@babel/core/-/core-7.29.0.tgz",
-      "integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@babel/code-frame": "^7.29.0",
-        "@babel/generator": "^7.29.0",
-        "@babel/helper-compilation-targets": "^7.28.6",
-        "@babel/helper-module-transforms": "^7.28.6",
-        "@babel/helpers": "^7.28.6",
-        "@babel/parser": "^7.29.0",
-        "@babel/template": "^7.28.6",
-        "@babel/traverse": "^7.29.0",
-        "@babel/types": "^7.29.0",
-        "@jridgewell/remapping": "^2.3.5",
-        "convert-source-map": "^2.0.0",
-        "debug": "^4.1.0",
-        "gensync": "^1.0.0-beta.2",
-        "json5": "^2.2.3",
-        "semver": "^6.3.1"
-      },
-      "engines": {
-        "node": ">=6.9.0"
-      },
-      "funding": {
-        "type": "opencollective",
-        "url": "https://opencollective.com/babel"
-      }
-    },
-    "node_modules/@babel/eslint-parser": {
-      "version": "7.28.6",
-      "resolved": "https://registry.npmmirror.com/@babel/eslint-parser/-/eslint-parser-7.28.6.tgz",
-      "integrity": "sha512-QGmsKi2PBO/MHSQk+AAgA9R6OHQr+VqnniFE0eMWZcVcfBZoA2dKn2hUsl3Csg/Plt9opRUWdY7//VXsrIlEiA==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@nicolo-ribaudo/eslint-scope-5-internals": "5.1.1-v1",
-        "eslint-visitor-keys": "^2.1.0",
-        "semver": "^6.3.1"
-      },
-      "engines": {
-        "node": "^10.13.0 || ^12.13.0 || >=14.0.0"
-      },
-      "peerDependencies": {
-        "@babel/core": "^7.11.0",
-        "eslint": "^7.5.0 || ^8.0.0 || ^9.0.0"
-      }
-    },
-    "node_modules/@babel/generator": {
-      "version": "7.29.1",
-      "resolved": "https://registry.npmmirror.com/@babel/generator/-/generator-7.29.1.tgz",
-      "integrity": "sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@babel/parser": "^7.29.0",
-        "@babel/types": "^7.29.0",
-        "@jridgewell/gen-mapping": "^0.3.12",
-        "@jridgewell/trace-mapping": "^0.3.28",
-        "jsesc": "^3.0.2"
-      },
-      "engines": {
-        "node": ">=6.9.0"
-      }
-    },
-    "node_modules/@babel/helper-annotate-as-pure": {
-      "version": "7.27.3",
-      "resolved": "https://registry.npmmirror.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.27.3.tgz",
-      "integrity": "sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@babel/types": "^7.27.3"
-      },
-      "engines": {
-        "node": ">=6.9.0"
-      }
-    },
-    "node_modules/@babel/helper-compilation-targets": {
-      "version": "7.28.6",
-      "resolved": "https://registry.npmmirror.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.28.6.tgz",
-      "integrity": "sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@babel/compat-data": "^7.28.6",
-        "@babel/helper-validator-option": "^7.27.1",
-        "browserslist": "^4.24.0",
-        "lru-cache": "^5.1.1",
-        "semver": "^6.3.1"
-      },
-      "engines": {
-        "node": ">=6.9.0"
-      }
-    },
-    "node_modules/@babel/helper-create-class-features-plugin": {
-      "version": "7.28.6",
-      "resolved": "https://registry.npmmirror.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.28.6.tgz",
-      "integrity": "sha512-dTOdvsjnG3xNT9Y0AUg1wAl38y+4Rl4sf9caSQZOXdNqVn+H+HbbJ4IyyHaIqNR6SW9oJpA/RuRjsjCw2IdIow==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@babel/helper-annotate-as-pure": "^7.27.3",
-        "@babel/helper-member-expression-to-functions": "^7.28.5",
-        "@babel/helper-optimise-call-expression": "^7.27.1",
-        "@babel/helper-replace-supers": "^7.28.6",
-        "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1",
-        "@babel/traverse": "^7.28.6",
-        "semver": "^6.3.1"
-      },
-      "engines": {
-        "node": ">=6.9.0"
-      },
-      "peerDependencies": {
-        "@babel/core": "^7.0.0"
-      }
-    },
-    "node_modules/@babel/helper-create-regexp-features-plugin": {
-      "version": "7.28.5",
-      "resolved": "https://registry.npmmirror.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.28.5.tgz",
-      "integrity": "sha512-N1EhvLtHzOvj7QQOUCCS3NrPJP8c5W6ZXCHDn7Yialuy1iu4r5EmIYkXlKNqT99Ciw+W0mDqWoR6HWMZlFP3hw==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@babel/helper-annotate-as-pure": "^7.27.3",
-        "regexpu-core": "^6.3.1",
-        "semver": "^6.3.1"
-      },
-      "engines": {
-        "node": ">=6.9.0"
-      },
-      "peerDependencies": {
-        "@babel/core": "^7.0.0"
-      }
-    },
-    "node_modules/@babel/helper-define-polyfill-provider": {
-      "version": "0.6.8",
-      "resolved": "https://registry.npmmirror.com/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.6.8.tgz",
-      "integrity": "sha512-47UwBLPpQi1NoWzLuHNjRoHlYXMwIJoBf7MFou6viC/sIHWYygpvr0B6IAyh5sBdA2nr2LPIRww8lfaUVQINBA==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@babel/helper-compilation-targets": "^7.28.6",
-        "@babel/helper-plugin-utils": "^7.28.6",
-        "debug": "^4.4.3",
-        "lodash.debounce": "^4.0.8",
-        "resolve": "^1.22.11"
-      },
-      "peerDependencies": {
-        "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0"
-      }
-    },
-    "node_modules/@babel/helper-globals": {
-      "version": "7.28.0",
-      "resolved": "https://registry.npmmirror.com/@babel/helper-globals/-/helper-globals-7.28.0.tgz",
-      "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=6.9.0"
-      }
-    },
-    "node_modules/@babel/helper-member-expression-to-functions": {
-      "version": "7.28.5",
-      "resolved": "https://registry.npmmirror.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.28.5.tgz",
-      "integrity": "sha512-cwM7SBRZcPCLgl8a7cY0soT1SptSzAlMH39vwiRpOQkJlh53r5hdHwLSCZpQdVLT39sZt+CRpNwYG4Y2v77atg==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@babel/traverse": "^7.28.5",
-        "@babel/types": "^7.28.5"
-      },
-      "engines": {
-        "node": ">=6.9.0"
-      }
-    },
-    "node_modules/@babel/helper-module-imports": {
-      "version": "7.28.6",
-      "resolved": "https://registry.npmmirror.com/@babel/helper-module-imports/-/helper-module-imports-7.28.6.tgz",
-      "integrity": "sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@babel/traverse": "^7.28.6",
-        "@babel/types": "^7.28.6"
-      },
-      "engines": {
-        "node": ">=6.9.0"
-      }
-    },
-    "node_modules/@babel/helper-module-transforms": {
-      "version": "7.28.6",
-      "resolved": "https://registry.npmmirror.com/@babel/helper-module-transforms/-/helper-module-transforms-7.28.6.tgz",
-      "integrity": "sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@babel/helper-module-imports": "^7.28.6",
-        "@babel/helper-validator-identifier": "^7.28.5",
-        "@babel/traverse": "^7.28.6"
-      },
-      "engines": {
-        "node": ">=6.9.0"
-      },
-      "peerDependencies": {
-        "@babel/core": "^7.0.0"
-      }
-    },
-    "node_modules/@babel/helper-optimise-call-expression": {
-      "version": "7.27.1",
-      "resolved": "https://registry.npmmirror.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.27.1.tgz",
-      "integrity": "sha512-URMGH08NzYFhubNSGJrpUEphGKQwMQYBySzat5cAByY1/YgIRkULnIy3tAMeszlL/so2HbeilYloUmSpd7GdVw==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@babel/types": "^7.27.1"
-      },
-      "engines": {
-        "node": ">=6.9.0"
-      }
-    },
-    "node_modules/@babel/helper-plugin-utils": {
-      "version": "7.28.6",
-      "resolved": "https://registry.npmmirror.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.28.6.tgz",
-      "integrity": "sha512-S9gzZ/bz83GRysI7gAD4wPT/AI3uCnY+9xn+Mx/KPs2JwHJIz1W8PZkg2cqyt3RNOBM8ejcXhV6y8Og7ly/Dug==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=6.9.0"
-      }
-    },
-    "node_modules/@babel/helper-remap-async-to-generator": {
-      "version": "7.27.1",
-      "resolved": "https://registry.npmmirror.com/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.27.1.tgz",
-      "integrity": "sha512-7fiA521aVw8lSPeI4ZOD3vRFkoqkJcS+z4hFo82bFSH/2tNd6eJ5qCVMS5OzDmZh/kaHQeBaeyxK6wljcPtveA==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@babel/helper-annotate-as-pure": "^7.27.1",
-        "@babel/helper-wrap-function": "^7.27.1",
-        "@babel/traverse": "^7.27.1"
-      },
-      "engines": {
-        "node": ">=6.9.0"
-      },
-      "peerDependencies": {
-        "@babel/core": "^7.0.0"
-      }
-    },
-    "node_modules/@babel/helper-replace-supers": {
-      "version": "7.28.6",
-      "resolved": "https://registry.npmmirror.com/@babel/helper-replace-supers/-/helper-replace-supers-7.28.6.tgz",
-      "integrity": "sha512-mq8e+laIk94/yFec3DxSjCRD2Z0TAjhVbEJY3UQrlwVo15Lmt7C2wAUbK4bjnTs4APkwsYLTahXRraQXhb1WCg==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@babel/helper-member-expression-to-functions": "^7.28.5",
-        "@babel/helper-optimise-call-expression": "^7.27.1",
-        "@babel/traverse": "^7.28.6"
-      },
-      "engines": {
-        "node": ">=6.9.0"
-      },
-      "peerDependencies": {
-        "@babel/core": "^7.0.0"
-      }
-    },
-    "node_modules/@babel/helper-skip-transparent-expression-wrappers": {
-      "version": "7.27.1",
-      "resolved": "https://registry.npmmirror.com/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.27.1.tgz",
-      "integrity": "sha512-Tub4ZKEXqbPjXgWLl2+3JpQAYBJ8+ikpQ2Ocj/q/r0LwE3UhENh7EUabyHjz2kCEsrRY83ew2DQdHluuiDQFzg==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@babel/traverse": "^7.27.1",
-        "@babel/types": "^7.27.1"
-      },
-      "engines": {
-        "node": ">=6.9.0"
-      }
-    },
-    "node_modules/@babel/helper-string-parser": {
-      "version": "7.27.1",
-      "resolved": "https://registry.npmmirror.com/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz",
-      "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==",
-      "license": "MIT",
-      "engines": {
-        "node": ">=6.9.0"
-      }
-    },
-    "node_modules/@babel/helper-validator-identifier": {
-      "version": "7.28.5",
-      "resolved": "https://registry.npmmirror.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz",
-      "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==",
-      "license": "MIT",
-      "engines": {
-        "node": ">=6.9.0"
-      }
-    },
-    "node_modules/@babel/helper-validator-option": {
-      "version": "7.27.1",
-      "resolved": "https://registry.npmmirror.com/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz",
-      "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=6.9.0"
-      }
-    },
-    "node_modules/@babel/helper-wrap-function": {
-      "version": "7.28.6",
-      "resolved": "https://registry.npmmirror.com/@babel/helper-wrap-function/-/helper-wrap-function-7.28.6.tgz",
-      "integrity": "sha512-z+PwLziMNBeSQJonizz2AGnndLsP2DeGHIxDAn+wdHOGuo4Fo1x1HBPPXeE9TAOPHNNWQKCSlA2VZyYyyibDnQ==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@babel/template": "^7.28.6",
-        "@babel/traverse": "^7.28.6",
-        "@babel/types": "^7.28.6"
-      },
-      "engines": {
-        "node": ">=6.9.0"
-      }
-    },
-    "node_modules/@babel/helpers": {
-      "version": "7.29.2",
-      "resolved": "https://registry.npmmirror.com/@babel/helpers/-/helpers-7.29.2.tgz",
-      "integrity": "sha512-HoGuUs4sCZNezVEKdVcwqmZN8GoHirLUcLaYVNBK2J0DadGtdcqgr3BCbvH8+XUo4NGjNl3VOtSjEKNzqfFgKw==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@babel/template": "^7.28.6",
-        "@babel/types": "^7.29.0"
-      },
-      "engines": {
-        "node": ">=6.9.0"
-      }
-    },
-    "node_modules/@babel/highlight": {
-      "version": "7.25.9",
-      "resolved": "https://registry.npmmirror.com/@babel/highlight/-/highlight-7.25.9.tgz",
-      "integrity": "sha512-llL88JShoCsth8fF8R4SJnIn+WLvR6ccFxu1H3FlMhDontdcmZWf2HgIZ7AIqV3Xcck1idlohrN4EUBQz6klbw==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@babel/helper-validator-identifier": "^7.25.9",
-        "chalk": "^2.4.2",
-        "js-tokens": "^4.0.0",
-        "picocolors": "^1.0.0"
-      },
-      "engines": {
-        "node": ">=6.9.0"
-      }
-    },
-    "node_modules/@babel/highlight/node_modules/ansi-styles": {
-      "version": "3.2.1",
-      "resolved": "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-3.2.1.tgz",
-      "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "color-convert": "^1.9.0"
-      },
-      "engines": {
-        "node": ">=4"
-      }
-    },
-    "node_modules/@babel/highlight/node_modules/chalk": {
-      "version": "2.4.2",
-      "resolved": "https://registry.npmmirror.com/chalk/-/chalk-2.4.2.tgz",
-      "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "ansi-styles": "^3.2.1",
-        "escape-string-regexp": "^1.0.5",
-        "supports-color": "^5.3.0"
-      },
-      "engines": {
-        "node": ">=4"
-      }
-    },
-    "node_modules/@babel/highlight/node_modules/color-convert": {
-      "version": "1.9.3",
-      "resolved": "https://registry.npmmirror.com/color-convert/-/color-convert-1.9.3.tgz",
-      "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "color-name": "1.1.3"
-      }
-    },
-    "node_modules/@babel/highlight/node_modules/color-name": {
-      "version": "1.1.3",
-      "resolved": "https://registry.npmmirror.com/color-name/-/color-name-1.1.3.tgz",
-      "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==",
-      "dev": true,
-      "license": "MIT"
-    },
-    "node_modules/@babel/highlight/node_modules/has-flag": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmmirror.com/has-flag/-/has-flag-3.0.0.tgz",
-      "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=4"
-      }
-    },
-    "node_modules/@babel/highlight/node_modules/supports-color": {
-      "version": "5.5.0",
-      "resolved": "https://registry.npmmirror.com/supports-color/-/supports-color-5.5.0.tgz",
-      "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "has-flag": "^3.0.0"
-      },
-      "engines": {
-        "node": ">=4"
-      }
-    },
-    "node_modules/@babel/parser": {
-      "version": "7.29.2",
-      "resolved": "https://registry.npmmirror.com/@babel/parser/-/parser-7.29.2.tgz",
-      "integrity": "sha512-4GgRzy/+fsBa72/RZVJmGKPmZu9Byn8o4MoLpmNe1m8ZfYnz5emHLQz3U4gLud6Zwl0RZIcgiLD7Uq7ySFuDLA==",
-      "license": "MIT",
-      "dependencies": {
-        "@babel/types": "^7.29.0"
-      },
-      "bin": {
-        "parser": "bin/babel-parser.js"
-      },
-      "engines": {
-        "node": ">=6.0.0"
-      }
-    },
-    "node_modules/@babel/plugin-bugfix-firefox-class-in-computed-class-key": {
-      "version": "7.28.5",
-      "resolved": "https://registry.npmmirror.com/@babel/plugin-bugfix-firefox-class-in-computed-class-key/-/plugin-bugfix-firefox-class-in-computed-class-key-7.28.5.tgz",
-      "integrity": "sha512-87GDMS3tsmMSi/3bWOte1UblL+YUTFMV8SZPZ2eSEL17s74Cw/l63rR6NmGVKMYW2GYi85nE+/d6Hw5N0bEk2Q==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@babel/helper-plugin-utils": "^7.27.1",
-        "@babel/traverse": "^7.28.5"
-      },
-      "engines": {
-        "node": ">=6.9.0"
-      },
-      "peerDependencies": {
-        "@babel/core": "^7.0.0"
-      }
-    },
-    "node_modules/@babel/plugin-bugfix-safari-class-field-initializer-scope": {
-      "version": "7.27.1",
-      "resolved": "https://registry.npmmirror.com/@babel/plugin-bugfix-safari-class-field-initializer-scope/-/plugin-bugfix-safari-class-field-initializer-scope-7.27.1.tgz",
-      "integrity": "sha512-qNeq3bCKnGgLkEXUuFry6dPlGfCdQNZbn7yUAPCInwAJHMU7THJfrBSozkcWq5sNM6RcF3S8XyQL2A52KNR9IA==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@babel/helper-plugin-utils": "^7.27.1"
-      },
-      "engines": {
-        "node": ">=6.9.0"
-      },
-      "peerDependencies": {
-        "@babel/core": "^7.0.0"
-      }
-    },
-    "node_modules/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": {
-      "version": "7.27.1",
-      "resolved": "https://registry.npmmirror.com/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.27.1.tgz",
-      "integrity": "sha512-g4L7OYun04N1WyqMNjldFwlfPCLVkgB54A/YCXICZYBsvJJE3kByKv9c9+R/nAfmIfjl2rKYLNyMHboYbZaWaA==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@babel/helper-plugin-utils": "^7.27.1"
-      },
-      "engines": {
-        "node": ">=6.9.0"
-      },
-      "peerDependencies": {
-        "@babel/core": "^7.0.0"
-      }
-    },
-    "node_modules/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": {
-      "version": "7.27.1",
-      "resolved": "https://registry.npmmirror.com/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.27.1.tgz",
-      "integrity": "sha512-oO02gcONcD5O1iTLi/6frMJBIwWEHceWGSGqrpCmEL8nogiS6J9PBlE48CaK20/Jx1LuRml9aDftLgdjXT8+Cw==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@babel/helper-plugin-utils": "^7.27.1",
-        "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1",
-        "@babel/plugin-transform-optional-chaining": "^7.27.1"
-      },
-      "engines": {
-        "node": ">=6.9.0"
-      },
-      "peerDependencies": {
-        "@babel/core": "^7.13.0"
-      }
-    },
-    "node_modules/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": {
-      "version": "7.28.6",
-      "resolved": "https://registry.npmmirror.com/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.28.6.tgz",
-      "integrity": "sha512-a0aBScVTlNaiUe35UtfxAN7A/tehvvG4/ByO6+46VPKTRSlfnAFsgKy0FUh+qAkQrDTmhDkT+IBOKlOoMUxQ0g==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@babel/helper-plugin-utils": "^7.28.6",
-        "@babel/traverse": "^7.28.6"
-      },
-      "engines": {
-        "node": ">=6.9.0"
-      },
-      "peerDependencies": {
-        "@babel/core": "^7.0.0"
-      }
-    },
-    "node_modules/@babel/plugin-proposal-class-properties": {
-      "version": "7.18.6",
-      "resolved": "https://registry.npmmirror.com/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.18.6.tgz",
-      "integrity": "sha512-cumfXOF0+nzZrrN8Rf0t7M+tF6sZc7vhQwYQck9q1/5w2OExlD+b4v4RpMJFaV1Z7WcDRgO6FqvxqxGlwo+RHQ==",
-      "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-class-properties instead.",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@babel/helper-create-class-features-plugin": "^7.18.6",
-        "@babel/helper-plugin-utils": "^7.18.6"
-      },
-      "engines": {
-        "node": ">=6.9.0"
-      },
-      "peerDependencies": {
-        "@babel/core": "^7.0.0-0"
-      }
-    },
-    "node_modules/@babel/plugin-proposal-decorators": {
-      "version": "7.29.0",
-      "resolved": "https://registry.npmmirror.com/@babel/plugin-proposal-decorators/-/plugin-proposal-decorators-7.29.0.tgz",
-      "integrity": "sha512-CVBVv3VY/XRMxRYq5dwr2DS7/MvqPm23cOCjbwNnVrfOqcWlnefua1uUs0sjdKOGjvPUG633o07uWzJq4oI6dA==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@babel/helper-create-class-features-plugin": "^7.28.6",
-        "@babel/helper-plugin-utils": "^7.28.6",
-        "@babel/plugin-syntax-decorators": "^7.28.6"
-      },
-      "engines": {
-        "node": ">=6.9.0"
-      },
-      "peerDependencies": {
-        "@babel/core": "^7.0.0-0"
-      }
-    },
-    "node_modules/@babel/plugin-proposal-private-property-in-object": {
-      "version": "7.21.0-placeholder-for-preset-env.2",
-      "resolved": "https://registry.npmmirror.com/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0-placeholder-for-preset-env.2.tgz",
-      "integrity": "sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=6.9.0"
-      },
-      "peerDependencies": {
-        "@babel/core": "^7.0.0-0"
-      }
-    },
-    "node_modules/@babel/plugin-syntax-decorators": {
-      "version": "7.28.6",
-      "resolved": "https://registry.npmmirror.com/@babel/plugin-syntax-decorators/-/plugin-syntax-decorators-7.28.6.tgz",
-      "integrity": "sha512-71EYI0ONURHJBL4rSFXnITXqXrrY8q4P0q006DPfN+Rk+ASM+++IBXem/ruokgBZR8YNEWZ8R6B+rCb8VcUTqA==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@babel/helper-plugin-utils": "^7.28.6"
-      },
-      "engines": {
-        "node": ">=6.9.0"
-      },
-      "peerDependencies": {
-        "@babel/core": "^7.0.0-0"
-      }
-    },
-    "node_modules/@babel/plugin-syntax-dynamic-import": {
-      "version": "7.8.3",
-      "resolved": "https://registry.npmmirror.com/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz",
-      "integrity": "sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@babel/helper-plugin-utils": "^7.8.0"
-      },
-      "peerDependencies": {
-        "@babel/core": "^7.0.0-0"
-      }
-    },
-    "node_modules/@babel/plugin-syntax-import-assertions": {
-      "version": "7.28.6",
-      "resolved": "https://registry.npmmirror.com/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.28.6.tgz",
-      "integrity": "sha512-pSJUpFHdx9z5nqTSirOCMtYVP2wFgoWhP0p3g8ONK/4IHhLIBd0B9NYqAvIUAhq+OkhO4VM1tENCt0cjlsNShw==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@babel/helper-plugin-utils": "^7.28.6"
-      },
-      "engines": {
-        "node": ">=6.9.0"
-      },
-      "peerDependencies": {
-        "@babel/core": "^7.0.0-0"
-      }
-    },
-    "node_modules/@babel/plugin-syntax-import-attributes": {
-      "version": "7.28.6",
-      "resolved": "https://registry.npmmirror.com/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.28.6.tgz",
-      "integrity": "sha512-jiLC0ma9XkQT3TKJ9uYvlakm66Pamywo+qwL+oL8HJOvc6TWdZXVfhqJr8CCzbSGUAbDOzlGHJC1U+vRfLQDvw==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@babel/helper-plugin-utils": "^7.28.6"
-      },
-      "engines": {
-        "node": ">=6.9.0"
-      },
-      "peerDependencies": {
-        "@babel/core": "^7.0.0-0"
-      }
-    },
-    "node_modules/@babel/plugin-syntax-jsx": {
-      "version": "7.28.6",
-      "resolved": "https://registry.npmmirror.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.28.6.tgz",
-      "integrity": "sha512-wgEmr06G6sIpqr8YDwA2dSRTE3bJ+V0IfpzfSY3Lfgd7YWOaAdlykvJi13ZKBt8cZHfgH1IXN+CL656W3uUa4w==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@babel/helper-plugin-utils": "^7.28.6"
-      },
-      "engines": {
-        "node": ">=6.9.0"
-      },
-      "peerDependencies": {
-        "@babel/core": "^7.0.0-0"
-      }
-    },
-    "node_modules/@babel/plugin-syntax-unicode-sets-regex": {
-      "version": "7.18.6",
-      "resolved": "https://registry.npmmirror.com/@babel/plugin-syntax-unicode-sets-regex/-/plugin-syntax-unicode-sets-regex-7.18.6.tgz",
-      "integrity": "sha512-727YkEAPwSIQTv5im8QHz3upqp92JTWhidIC81Tdx4VJYIte/VndKf1qKrfnnhPLiPghStWfvC/iFaMCQu7Nqg==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@babel/helper-create-regexp-features-plugin": "^7.18.6",
-        "@babel/helper-plugin-utils": "^7.18.6"
-      },
-      "engines": {
-        "node": ">=6.9.0"
-      },
-      "peerDependencies": {
-        "@babel/core": "^7.0.0"
-      }
-    },
-    "node_modules/@babel/plugin-transform-arrow-functions": {
-      "version": "7.27.1",
-      "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.27.1.tgz",
-      "integrity": "sha512-8Z4TGic6xW70FKThA5HYEKKyBpOOsucTOD1DjU3fZxDg+K3zBJcXMFnt/4yQiZnf5+MiOMSXQ9PaEK/Ilh1DeA==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@babel/helper-plugin-utils": "^7.27.1"
-      },
-      "engines": {
-        "node": ">=6.9.0"
-      },
-      "peerDependencies": {
-        "@babel/core": "^7.0.0-0"
-      }
-    },
-    "node_modules/@babel/plugin-transform-async-generator-functions": {
-      "version": "7.29.0",
-      "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.29.0.tgz",
-      "integrity": "sha512-va0VdWro4zlBr2JsXC+ofCPB2iG12wPtVGTWFx2WLDOM3nYQZZIGP82qku2eW/JR83sD+k2k+CsNtyEbUqhU6w==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@babel/helper-plugin-utils": "^7.28.6",
-        "@babel/helper-remap-async-to-generator": "^7.27.1",
-        "@babel/traverse": "^7.29.0"
-      },
-      "engines": {
-        "node": ">=6.9.0"
-      },
-      "peerDependencies": {
-        "@babel/core": "^7.0.0-0"
-      }
-    },
-    "node_modules/@babel/plugin-transform-async-to-generator": {
-      "version": "7.28.6",
-      "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.28.6.tgz",
-      "integrity": "sha512-ilTRcmbuXjsMmcZ3HASTe4caH5Tpo93PkTxF9oG2VZsSWsahydmcEHhix9Ik122RcTnZnUzPbmux4wh1swfv7g==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@babel/helper-module-imports": "^7.28.6",
-        "@babel/helper-plugin-utils": "^7.28.6",
-        "@babel/helper-remap-async-to-generator": "^7.27.1"
-      },
-      "engines": {
-        "node": ">=6.9.0"
-      },
-      "peerDependencies": {
-        "@babel/core": "^7.0.0-0"
-      }
-    },
-    "node_modules/@babel/plugin-transform-block-scoped-functions": {
-      "version": "7.27.1",
-      "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.27.1.tgz",
-      "integrity": "sha512-cnqkuOtZLapWYZUYM5rVIdv1nXYuFVIltZ6ZJ7nIj585QsjKM5dhL2Fu/lICXZ1OyIAFc7Qy+bvDAtTXqGrlhg==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@babel/helper-plugin-utils": "^7.27.1"
-      },
-      "engines": {
-        "node": ">=6.9.0"
-      },
-      "peerDependencies": {
-        "@babel/core": "^7.0.0-0"
-      }
-    },
-    "node_modules/@babel/plugin-transform-block-scoping": {
-      "version": "7.28.6",
-      "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.28.6.tgz",
-      "integrity": "sha512-tt/7wOtBmwHPNMPu7ax4pdPz6shjFrmHDghvNC+FG9Qvj7D6mJcoRQIF5dy4njmxR941l6rgtvfSB2zX3VlUIw==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@babel/helper-plugin-utils": "^7.28.6"
-      },
-      "engines": {
-        "node": ">=6.9.0"
-      },
-      "peerDependencies": {
-        "@babel/core": "^7.0.0-0"
-      }
-    },
-    "node_modules/@babel/plugin-transform-class-properties": {
-      "version": "7.28.6",
-      "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.28.6.tgz",
-      "integrity": "sha512-dY2wS3I2G7D697VHndN91TJr8/AAfXQNt5ynCTI/MpxMsSzHp+52uNivYT5wCPax3whc47DR8Ba7cmlQMg24bw==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@babel/helper-create-class-features-plugin": "^7.28.6",
-        "@babel/helper-plugin-utils": "^7.28.6"
-      },
-      "engines": {
-        "node": ">=6.9.0"
-      },
-      "peerDependencies": {
-        "@babel/core": "^7.0.0-0"
-      }
-    },
-    "node_modules/@babel/plugin-transform-class-static-block": {
-      "version": "7.28.6",
-      "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.28.6.tgz",
-      "integrity": "sha512-rfQ++ghVwTWTqQ7w8qyDxL1XGihjBss4CmTgGRCTAC9RIbhVpyp4fOeZtta0Lbf+dTNIVJer6ych2ibHwkZqsQ==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@babel/helper-create-class-features-plugin": "^7.28.6",
-        "@babel/helper-plugin-utils": "^7.28.6"
-      },
-      "engines": {
-        "node": ">=6.9.0"
-      },
-      "peerDependencies": {
-        "@babel/core": "^7.12.0"
-      }
-    },
-    "node_modules/@babel/plugin-transform-classes": {
-      "version": "7.28.6",
-      "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.28.6.tgz",
-      "integrity": "sha512-EF5KONAqC5zAqT783iMGuM2ZtmEBy+mJMOKl2BCvPZ2lVrwvXnB6o+OBWCS+CoeCCpVRF2sA2RBKUxvT8tQT5Q==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@babel/helper-annotate-as-pure": "^7.27.3",
-        "@babel/helper-compilation-targets": "^7.28.6",
-        "@babel/helper-globals": "^7.28.0",
-        "@babel/helper-plugin-utils": "^7.28.6",
-        "@babel/helper-replace-supers": "^7.28.6",
-        "@babel/traverse": "^7.28.6"
-      },
-      "engines": {
-        "node": ">=6.9.0"
-      },
-      "peerDependencies": {
-        "@babel/core": "^7.0.0-0"
-      }
-    },
-    "node_modules/@babel/plugin-transform-computed-properties": {
-      "version": "7.28.6",
-      "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.28.6.tgz",
-      "integrity": "sha512-bcc3k0ijhHbc2lEfpFHgx7eYw9KNXqOerKWfzbxEHUGKnS3sz9C4CNL9OiFN1297bDNfUiSO7DaLzbvHQQQ1BQ==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@babel/helper-plugin-utils": "^7.28.6",
-        "@babel/template": "^7.28.6"
-      },
-      "engines": {
-        "node": ">=6.9.0"
-      },
-      "peerDependencies": {
-        "@babel/core": "^7.0.0-0"
-      }
-    },
-    "node_modules/@babel/plugin-transform-destructuring": {
-      "version": "7.28.5",
-      "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.28.5.tgz",
-      "integrity": "sha512-Kl9Bc6D0zTUcFUvkNuQh4eGXPKKNDOJQXVyyM4ZAQPMveniJdxi8XMJwLo+xSoW3MIq81bD33lcUe9kZpl0MCw==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@babel/helper-plugin-utils": "^7.27.1",
-        "@babel/traverse": "^7.28.5"
-      },
-      "engines": {
-        "node": ">=6.9.0"
-      },
-      "peerDependencies": {
-        "@babel/core": "^7.0.0-0"
-      }
-    },
-    "node_modules/@babel/plugin-transform-dotall-regex": {
-      "version": "7.28.6",
-      "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.28.6.tgz",
-      "integrity": "sha512-SljjowuNKB7q5Oayv4FoPzeB74g3QgLt8IVJw9ADvWy3QnUb/01aw8I4AVv8wYnPvQz2GDDZ/g3GhcNyDBI4Bg==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@babel/helper-create-regexp-features-plugin": "^7.28.5",
-        "@babel/helper-plugin-utils": "^7.28.6"
-      },
-      "engines": {
-        "node": ">=6.9.0"
-      },
-      "peerDependencies": {
-        "@babel/core": "^7.0.0-0"
-      }
-    },
-    "node_modules/@babel/plugin-transform-duplicate-keys": {
-      "version": "7.27.1",
-      "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.27.1.tgz",
-      "integrity": "sha512-MTyJk98sHvSs+cvZ4nOauwTTG1JeonDjSGvGGUNHreGQns+Mpt6WX/dVzWBHgg+dYZhkC4X+zTDfkTU+Vy9y7Q==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@babel/helper-plugin-utils": "^7.27.1"
-      },
-      "engines": {
-        "node": ">=6.9.0"
-      },
-      "peerDependencies": {
-        "@babel/core": "^7.0.0-0"
-      }
-    },
-    "node_modules/@babel/plugin-transform-duplicate-named-capturing-groups-regex": {
-      "version": "7.29.0",
-      "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-duplicate-named-capturing-groups-regex/-/plugin-transform-duplicate-named-capturing-groups-regex-7.29.0.tgz",
-      "integrity": "sha512-zBPcW2lFGxdiD8PUnPwJjag2J9otbcLQzvbiOzDxpYXyCuYX9agOwMPGn1prVH0a4qzhCKu24rlH4c1f7yA8rw==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@babel/helper-create-regexp-features-plugin": "^7.28.5",
-        "@babel/helper-plugin-utils": "^7.28.6"
-      },
-      "engines": {
-        "node": ">=6.9.0"
-      },
-      "peerDependencies": {
-        "@babel/core": "^7.0.0"
-      }
-    },
-    "node_modules/@babel/plugin-transform-dynamic-import": {
-      "version": "7.27.1",
-      "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.27.1.tgz",
-      "integrity": "sha512-MHzkWQcEmjzzVW9j2q8LGjwGWpG2mjwaaB0BNQwst3FIjqsg8Ct/mIZlvSPJvfi9y2AC8mi/ktxbFVL9pZ1I4A==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@babel/helper-plugin-utils": "^7.27.1"
-      },
-      "engines": {
-        "node": ">=6.9.0"
-      },
-      "peerDependencies": {
-        "@babel/core": "^7.0.0-0"
-      }
-    },
-    "node_modules/@babel/plugin-transform-explicit-resource-management": {
-      "version": "7.28.6",
-      "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-explicit-resource-management/-/plugin-transform-explicit-resource-management-7.28.6.tgz",
-      "integrity": "sha512-Iao5Konzx2b6g7EPqTy40UZbcdXE126tTxVFr/nAIj+WItNxjKSYTEw3RC+A2/ZetmdJsgueL1KhaMCQHkLPIg==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@babel/helper-plugin-utils": "^7.28.6",
-        "@babel/plugin-transform-destructuring": "^7.28.5"
-      },
-      "engines": {
-        "node": ">=6.9.0"
-      },
-      "peerDependencies": {
-        "@babel/core": "^7.0.0-0"
-      }
-    },
-    "node_modules/@babel/plugin-transform-exponentiation-operator": {
-      "version": "7.28.6",
-      "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.28.6.tgz",
-      "integrity": "sha512-WitabqiGjV/vJ0aPOLSFfNY1u9U3R7W36B03r5I2KoNix+a3sOhJ3pKFB3R5It9/UiK78NiO0KE9P21cMhlPkw==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@babel/helper-plugin-utils": "^7.28.6"
-      },
-      "engines": {
-        "node": ">=6.9.0"
-      },
-      "peerDependencies": {
-        "@babel/core": "^7.0.0-0"
-      }
-    },
-    "node_modules/@babel/plugin-transform-export-namespace-from": {
-      "version": "7.27.1",
-      "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.27.1.tgz",
-      "integrity": "sha512-tQvHWSZ3/jH2xuq/vZDy0jNn+ZdXJeM8gHvX4lnJmsc3+50yPlWdZXIc5ay+umX+2/tJIqHqiEqcJvxlmIvRvQ==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@babel/helper-plugin-utils": "^7.27.1"
-      },
-      "engines": {
-        "node": ">=6.9.0"
-      },
-      "peerDependencies": {
-        "@babel/core": "^7.0.0-0"
-      }
-    },
-    "node_modules/@babel/plugin-transform-for-of": {
-      "version": "7.27.1",
-      "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.27.1.tgz",
-      "integrity": "sha512-BfbWFFEJFQzLCQ5N8VocnCtA8J1CLkNTe2Ms2wocj75dd6VpiqS5Z5quTYcUoo4Yq+DN0rtikODccuv7RU81sw==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@babel/helper-plugin-utils": "^7.27.1",
-        "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1"
-      },
-      "engines": {
-        "node": ">=6.9.0"
-      },
-      "peerDependencies": {
-        "@babel/core": "^7.0.0-0"
-      }
-    },
-    "node_modules/@babel/plugin-transform-function-name": {
-      "version": "7.27.1",
-      "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.27.1.tgz",
-      "integrity": "sha512-1bQeydJF9Nr1eBCMMbC+hdwmRlsv5XYOMu03YSWFwNs0HsAmtSxxF1fyuYPqemVldVyFmlCU7w8UE14LupUSZQ==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@babel/helper-compilation-targets": "^7.27.1",
-        "@babel/helper-plugin-utils": "^7.27.1",
-        "@babel/traverse": "^7.27.1"
-      },
-      "engines": {
-        "node": ">=6.9.0"
-      },
-      "peerDependencies": {
-        "@babel/core": "^7.0.0-0"
-      }
-    },
-    "node_modules/@babel/plugin-transform-json-strings": {
-      "version": "7.28.6",
-      "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.28.6.tgz",
-      "integrity": "sha512-Nr+hEN+0geQkzhbdgQVPoqr47lZbm+5fCUmO70722xJZd0Mvb59+33QLImGj6F+DkK3xgDi1YVysP8whD6FQAw==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@babel/helper-plugin-utils": "^7.28.6"
-      },
-      "engines": {
-        "node": ">=6.9.0"
-      },
-      "peerDependencies": {
-        "@babel/core": "^7.0.0-0"
-      }
-    },
-    "node_modules/@babel/plugin-transform-literals": {
-      "version": "7.27.1",
-      "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-literals/-/plugin-transform-literals-7.27.1.tgz",
-      "integrity": "sha512-0HCFSepIpLTkLcsi86GG3mTUzxV5jpmbv97hTETW3yzrAij8aqlD36toB1D0daVFJM8NK6GvKO0gslVQmm+zZA==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@babel/helper-plugin-utils": "^7.27.1"
-      },
-      "engines": {
-        "node": ">=6.9.0"
-      },
-      "peerDependencies": {
-        "@babel/core": "^7.0.0-0"
-      }
-    },
-    "node_modules/@babel/plugin-transform-logical-assignment-operators": {
-      "version": "7.28.6",
-      "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.28.6.tgz",
-      "integrity": "sha512-+anKKair6gpi8VsM/95kmomGNMD0eLz1NQ8+Pfw5sAwWH9fGYXT50E55ZpV0pHUHWf6IUTWPM+f/7AAff+wr9A==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@babel/helper-plugin-utils": "^7.28.6"
-      },
-      "engines": {
-        "node": ">=6.9.0"
-      },
-      "peerDependencies": {
-        "@babel/core": "^7.0.0-0"
-      }
-    },
-    "node_modules/@babel/plugin-transform-member-expression-literals": {
-      "version": "7.27.1",
-      "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.27.1.tgz",
-      "integrity": "sha512-hqoBX4dcZ1I33jCSWcXrP+1Ku7kdqXf1oeah7ooKOIiAdKQ+uqftgCFNOSzA5AMS2XIHEYeGFg4cKRCdpxzVOQ==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@babel/helper-plugin-utils": "^7.27.1"
-      },
-      "engines": {
-        "node": ">=6.9.0"
-      },
-      "peerDependencies": {
-        "@babel/core": "^7.0.0-0"
-      }
-    },
-    "node_modules/@babel/plugin-transform-modules-amd": {
-      "version": "7.27.1",
-      "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.27.1.tgz",
-      "integrity": "sha512-iCsytMg/N9/oFq6n+gFTvUYDZQOMK5kEdeYxmxt91fcJGycfxVP9CnrxoliM0oumFERba2i8ZtwRUCMhvP1LnA==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@babel/helper-module-transforms": "^7.27.1",
-        "@babel/helper-plugin-utils": "^7.27.1"
-      },
-      "engines": {
-        "node": ">=6.9.0"
-      },
-      "peerDependencies": {
-        "@babel/core": "^7.0.0-0"
-      }
-    },
-    "node_modules/@babel/plugin-transform-modules-commonjs": {
-      "version": "7.28.6",
-      "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.28.6.tgz",
-      "integrity": "sha512-jppVbf8IV9iWWwWTQIxJMAJCWBuuKx71475wHwYytrRGQ2CWiDvYlADQno3tcYpS/T2UUWFQp3nVtYfK/YBQrA==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@babel/helper-module-transforms": "^7.28.6",
-        "@babel/helper-plugin-utils": "^7.28.6"
-      },
-      "engines": {
-        "node": ">=6.9.0"
-      },
-      "peerDependencies": {
-        "@babel/core": "^7.0.0-0"
-      }
-    },
-    "node_modules/@babel/plugin-transform-modules-systemjs": {
-      "version": "7.29.0",
-      "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.29.0.tgz",
-      "integrity": "sha512-PrujnVFbOdUpw4UHiVwKvKRLMMic8+eC0CuNlxjsyZUiBjhFdPsewdXCkveh2KqBA9/waD0W1b4hXSOBQJezpQ==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@babel/helper-module-transforms": "^7.28.6",
-        "@babel/helper-plugin-utils": "^7.28.6",
-        "@babel/helper-validator-identifier": "^7.28.5",
-        "@babel/traverse": "^7.29.0"
-      },
-      "engines": {
-        "node": ">=6.9.0"
-      },
-      "peerDependencies": {
-        "@babel/core": "^7.0.0-0"
-      }
-    },
-    "node_modules/@babel/plugin-transform-modules-umd": {
-      "version": "7.27.1",
-      "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.27.1.tgz",
-      "integrity": "sha512-iQBE/xC5BV1OxJbp6WG7jq9IWiD+xxlZhLrdwpPkTX3ydmXdvoCpyfJN7acaIBZaOqTfr76pgzqBJflNbeRK+w==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@babel/helper-module-transforms": "^7.27.1",
-        "@babel/helper-plugin-utils": "^7.27.1"
-      },
-      "engines": {
-        "node": ">=6.9.0"
-      },
-      "peerDependencies": {
-        "@babel/core": "^7.0.0-0"
-      }
-    },
-    "node_modules/@babel/plugin-transform-named-capturing-groups-regex": {
-      "version": "7.29.0",
-      "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.29.0.tgz",
-      "integrity": "sha512-1CZQA5KNAD6ZYQLPw7oi5ewtDNxH/2vuCh+6SmvgDfhumForvs8a1o9n0UrEoBD8HU4djO2yWngTQlXl1NDVEQ==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@babel/helper-create-regexp-features-plugin": "^7.28.5",
-        "@babel/helper-plugin-utils": "^7.28.6"
-      },
-      "engines": {
-        "node": ">=6.9.0"
-      },
-      "peerDependencies": {
-        "@babel/core": "^7.0.0"
-      }
-    },
-    "node_modules/@babel/plugin-transform-new-target": {
-      "version": "7.27.1",
-      "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.27.1.tgz",
-      "integrity": "sha512-f6PiYeqXQ05lYq3TIfIDu/MtliKUbNwkGApPUvyo6+tc7uaR4cPjPe7DFPr15Uyycg2lZU6btZ575CuQoYh7MQ==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@babel/helper-plugin-utils": "^7.27.1"
-      },
-      "engines": {
-        "node": ">=6.9.0"
-      },
-      "peerDependencies": {
-        "@babel/core": "^7.0.0-0"
-      }
-    },
-    "node_modules/@babel/plugin-transform-nullish-coalescing-operator": {
-      "version": "7.28.6",
-      "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.28.6.tgz",
-      "integrity": "sha512-3wKbRgmzYbw24mDJXT7N+ADXw8BC/imU9yo9c9X9NKaLF1fW+e5H1U5QjMUBe4Qo4Ox/o++IyUkl1sVCLgevKg==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@babel/helper-plugin-utils": "^7.28.6"
-      },
-      "engines": {
-        "node": ">=6.9.0"
-      },
-      "peerDependencies": {
-        "@babel/core": "^7.0.0-0"
-      }
-    },
-    "node_modules/@babel/plugin-transform-numeric-separator": {
-      "version": "7.28.6",
-      "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.28.6.tgz",
-      "integrity": "sha512-SJR8hPynj8outz+SlStQSwvziMN4+Bq99it4tMIf5/Caq+3iOc0JtKyse8puvyXkk3eFRIA5ID/XfunGgO5i6w==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@babel/helper-plugin-utils": "^7.28.6"
-      },
-      "engines": {
-        "node": ">=6.9.0"
-      },
-      "peerDependencies": {
-        "@babel/core": "^7.0.0-0"
-      }
-    },
-    "node_modules/@babel/plugin-transform-object-rest-spread": {
-      "version": "7.28.6",
-      "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.28.6.tgz",
-      "integrity": "sha512-5rh+JR4JBC4pGkXLAcYdLHZjXudVxWMXbB6u6+E9lRL5TrGVbHt1TjxGbZ8CkmYw9zjkB7jutzOROArsqtncEA==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@babel/helper-compilation-targets": "^7.28.6",
-        "@babel/helper-plugin-utils": "^7.28.6",
-        "@babel/plugin-transform-destructuring": "^7.28.5",
-        "@babel/plugin-transform-parameters": "^7.27.7",
-        "@babel/traverse": "^7.28.6"
-      },
-      "engines": {
-        "node": ">=6.9.0"
-      },
-      "peerDependencies": {
-        "@babel/core": "^7.0.0-0"
-      }
-    },
-    "node_modules/@babel/plugin-transform-object-super": {
-      "version": "7.27.1",
-      "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.27.1.tgz",
-      "integrity": "sha512-SFy8S9plRPbIcxlJ8A6mT/CxFdJx/c04JEctz4jf8YZaVS2px34j7NXRrlGlHkN/M2gnpL37ZpGRGVFLd3l8Ng==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@babel/helper-plugin-utils": "^7.27.1",
-        "@babel/helper-replace-supers": "^7.27.1"
-      },
-      "engines": {
-        "node": ">=6.9.0"
-      },
-      "peerDependencies": {
-        "@babel/core": "^7.0.0-0"
-      }
-    },
-    "node_modules/@babel/plugin-transform-optional-catch-binding": {
-      "version": "7.28.6",
-      "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.28.6.tgz",
-      "integrity": "sha512-R8ja/Pyrv0OGAvAXQhSTmWyPJPml+0TMqXlO5w+AsMEiwb2fg3WkOvob7UxFSL3OIttFSGSRFKQsOhJ/X6HQdQ==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@babel/helper-plugin-utils": "^7.28.6"
-      },
-      "engines": {
-        "node": ">=6.9.0"
-      },
-      "peerDependencies": {
-        "@babel/core": "^7.0.0-0"
-      }
-    },
-    "node_modules/@babel/plugin-transform-optional-chaining": {
-      "version": "7.28.6",
-      "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.28.6.tgz",
-      "integrity": "sha512-A4zobikRGJTsX9uqVFdafzGkqD30t26ck2LmOzAuLL8b2x6k3TIqRiT2xVvA9fNmFeTX484VpsdgmKNA0bS23w==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@babel/helper-plugin-utils": "^7.28.6",
-        "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1"
-      },
-      "engines": {
-        "node": ">=6.9.0"
-      },
-      "peerDependencies": {
-        "@babel/core": "^7.0.0-0"
-      }
-    },
-    "node_modules/@babel/plugin-transform-parameters": {
-      "version": "7.27.7",
-      "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.27.7.tgz",
-      "integrity": "sha512-qBkYTYCb76RRxUM6CcZA5KRu8K4SM8ajzVeUgVdMVO9NN9uI/GaVmBg/WKJJGnNokV9SY8FxNOVWGXzqzUidBg==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@babel/helper-plugin-utils": "^7.27.1"
-      },
-      "engines": {
-        "node": ">=6.9.0"
-      },
-      "peerDependencies": {
-        "@babel/core": "^7.0.0-0"
-      }
-    },
-    "node_modules/@babel/plugin-transform-private-methods": {
-      "version": "7.28.6",
-      "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.28.6.tgz",
-      "integrity": "sha512-piiuapX9CRv7+0st8lmuUlRSmX6mBcVeNQ1b4AYzJxfCMuBfB0vBXDiGSmm03pKJw1v6cZ8KSeM+oUnM6yAExg==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@babel/helper-create-class-features-plugin": "^7.28.6",
-        "@babel/helper-plugin-utils": "^7.28.6"
-      },
-      "engines": {
-        "node": ">=6.9.0"
-      },
-      "peerDependencies": {
-        "@babel/core": "^7.0.0-0"
-      }
-    },
-    "node_modules/@babel/plugin-transform-private-property-in-object": {
-      "version": "7.28.6",
-      "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.28.6.tgz",
-      "integrity": "sha512-b97jvNSOb5+ehyQmBpmhOCiUC5oVK4PMnpRvO7+ymFBoqYjeDHIU9jnrNUuwHOiL9RpGDoKBpSViarV+BU+eVA==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@babel/helper-annotate-as-pure": "^7.27.3",
-        "@babel/helper-create-class-features-plugin": "^7.28.6",
-        "@babel/helper-plugin-utils": "^7.28.6"
-      },
-      "engines": {
-        "node": ">=6.9.0"
-      },
-      "peerDependencies": {
-        "@babel/core": "^7.0.0-0"
-      }
-    },
-    "node_modules/@babel/plugin-transform-property-literals": {
-      "version": "7.27.1",
-      "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.27.1.tgz",
-      "integrity": "sha512-oThy3BCuCha8kDZ8ZkgOg2exvPYUlprMukKQXI1r1pJ47NCvxfkEy8vK+r/hT9nF0Aa4H1WUPZZjHTFtAhGfmQ==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@babel/helper-plugin-utils": "^7.27.1"
-      },
-      "engines": {
-        "node": ">=6.9.0"
-      },
-      "peerDependencies": {
-        "@babel/core": "^7.0.0-0"
-      }
-    },
-    "node_modules/@babel/plugin-transform-regenerator": {
-      "version": "7.29.0",
-      "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.29.0.tgz",
-      "integrity": "sha512-FijqlqMA7DmRdg/aINBSs04y8XNTYw/lr1gJ2WsmBnnaNw1iS43EPkJW+zK7z65auG3AWRFXWj+NcTQwYptUog==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@babel/helper-plugin-utils": "^7.28.6"
-      },
-      "engines": {
-        "node": ">=6.9.0"
-      },
-      "peerDependencies": {
-        "@babel/core": "^7.0.0-0"
-      }
-    },
-    "node_modules/@babel/plugin-transform-regexp-modifiers": {
-      "version": "7.28.6",
-      "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-regexp-modifiers/-/plugin-transform-regexp-modifiers-7.28.6.tgz",
-      "integrity": "sha512-QGWAepm9qxpaIs7UM9FvUSnCGlb8Ua1RhyM4/veAxLwt3gMat/LSGrZixyuj4I6+Kn9iwvqCyPTtbdxanYoWYg==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@babel/helper-create-regexp-features-plugin": "^7.28.5",
-        "@babel/helper-plugin-utils": "^7.28.6"
-      },
-      "engines": {
-        "node": ">=6.9.0"
-      },
-      "peerDependencies": {
-        "@babel/core": "^7.0.0"
-      }
-    },
-    "node_modules/@babel/plugin-transform-reserved-words": {
-      "version": "7.27.1",
-      "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.27.1.tgz",
-      "integrity": "sha512-V2ABPHIJX4kC7HegLkYoDpfg9PVmuWy/i6vUM5eGK22bx4YVFD3M5F0QQnWQoDs6AGsUWTVOopBiMFQgHaSkVw==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@babel/helper-plugin-utils": "^7.27.1"
-      },
-      "engines": {
-        "node": ">=6.9.0"
-      },
-      "peerDependencies": {
-        "@babel/core": "^7.0.0-0"
-      }
-    },
-    "node_modules/@babel/plugin-transform-runtime": {
-      "version": "7.29.0",
-      "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.29.0.tgz",
-      "integrity": "sha512-jlaRT5dJtMaMCV6fAuLbsQMSwz/QkvaHOHOSXRitGGwSpR1blCY4KUKoyP2tYO8vJcqYe8cEj96cqSztv3uF9w==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@babel/helper-module-imports": "^7.28.6",
-        "@babel/helper-plugin-utils": "^7.28.6",
-        "babel-plugin-polyfill-corejs2": "^0.4.14",
-        "babel-plugin-polyfill-corejs3": "^0.13.0",
-        "babel-plugin-polyfill-regenerator": "^0.6.5",
-        "semver": "^6.3.1"
-      },
-      "engines": {
-        "node": ">=6.9.0"
-      },
-      "peerDependencies": {
-        "@babel/core": "^7.0.0-0"
-      }
-    },
-    "node_modules/@babel/plugin-transform-shorthand-properties": {
-      "version": "7.27.1",
-      "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.27.1.tgz",
-      "integrity": "sha512-N/wH1vcn4oYawbJ13Y/FxcQrWk63jhfNa7jef0ih7PHSIHX2LB7GWE1rkPrOnka9kwMxb6hMl19p7lidA+EHmQ==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@babel/helper-plugin-utils": "^7.27.1"
-      },
-      "engines": {
-        "node": ">=6.9.0"
-      },
-      "peerDependencies": {
-        "@babel/core": "^7.0.0-0"
-      }
-    },
-    "node_modules/@babel/plugin-transform-spread": {
-      "version": "7.28.6",
-      "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.28.6.tgz",
-      "integrity": "sha512-9U4QObUC0FtJl05AsUcodau/RWDytrU6uKgkxu09mLR9HLDAtUMoPuuskm5huQsoktmsYpI+bGmq+iapDcriKA==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@babel/helper-plugin-utils": "^7.28.6",
-        "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1"
-      },
-      "engines": {
-        "node": ">=6.9.0"
-      },
-      "peerDependencies": {
-        "@babel/core": "^7.0.0-0"
-      }
-    },
-    "node_modules/@babel/plugin-transform-sticky-regex": {
-      "version": "7.27.1",
-      "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.27.1.tgz",
-      "integrity": "sha512-lhInBO5bi/Kowe2/aLdBAawijx+q1pQzicSgnkB6dUPc1+RC8QmJHKf2OjvU+NZWitguJHEaEmbV6VWEouT58g==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@babel/helper-plugin-utils": "^7.27.1"
-      },
-      "engines": {
-        "node": ">=6.9.0"
-      },
-      "peerDependencies": {
-        "@babel/core": "^7.0.0-0"
-      }
-    },
-    "node_modules/@babel/plugin-transform-template-literals": {
-      "version": "7.27.1",
-      "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.27.1.tgz",
-      "integrity": "sha512-fBJKiV7F2DxZUkg5EtHKXQdbsbURW3DZKQUWphDum0uRP6eHGGa/He9mc0mypL680pb+e/lDIthRohlv8NCHkg==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@babel/helper-plugin-utils": "^7.27.1"
-      },
-      "engines": {
-        "node": ">=6.9.0"
-      },
-      "peerDependencies": {
-        "@babel/core": "^7.0.0-0"
-      }
-    },
-    "node_modules/@babel/plugin-transform-typeof-symbol": {
-      "version": "7.27.1",
-      "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.27.1.tgz",
-      "integrity": "sha512-RiSILC+nRJM7FY5srIyc4/fGIwUhyDuuBSdWn4y6yT6gm652DpCHZjIipgn6B7MQ1ITOUnAKWixEUjQRIBIcLw==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@babel/helper-plugin-utils": "^7.27.1"
-      },
-      "engines": {
-        "node": ">=6.9.0"
-      },
-      "peerDependencies": {
-        "@babel/core": "^7.0.0-0"
-      }
-    },
-    "node_modules/@babel/plugin-transform-unicode-escapes": {
-      "version": "7.27.1",
-      "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.27.1.tgz",
-      "integrity": "sha512-Ysg4v6AmF26k9vpfFuTZg8HRfVWzsh1kVfowA23y9j/Gu6dOuahdUVhkLqpObp3JIv27MLSii6noRnuKN8H0Mg==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@babel/helper-plugin-utils": "^7.27.1"
-      },
-      "engines": {
-        "node": ">=6.9.0"
-      },
-      "peerDependencies": {
-        "@babel/core": "^7.0.0-0"
-      }
-    },
-    "node_modules/@babel/plugin-transform-unicode-property-regex": {
-      "version": "7.28.6",
-      "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.28.6.tgz",
-      "integrity": "sha512-4Wlbdl/sIZjzi/8St0evF0gEZrgOswVO6aOzqxh1kDZOl9WmLrHq2HtGhnOJZmHZYKP8WZ1MDLCt5DAWwRo57A==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@babel/helper-create-regexp-features-plugin": "^7.28.5",
-        "@babel/helper-plugin-utils": "^7.28.6"
-      },
-      "engines": {
-        "node": ">=6.9.0"
-      },
-      "peerDependencies": {
-        "@babel/core": "^7.0.0-0"
-      }
-    },
-    "node_modules/@babel/plugin-transform-unicode-regex": {
-      "version": "7.27.1",
-      "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.27.1.tgz",
-      "integrity": "sha512-xvINq24TRojDuyt6JGtHmkVkrfVV3FPT16uytxImLeBZqW3/H52yN+kM1MGuyPkIQxrzKwPHs5U/MP3qKyzkGw==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@babel/helper-create-regexp-features-plugin": "^7.27.1",
-        "@babel/helper-plugin-utils": "^7.27.1"
-      },
-      "engines": {
-        "node": ">=6.9.0"
-      },
-      "peerDependencies": {
-        "@babel/core": "^7.0.0-0"
-      }
-    },
-    "node_modules/@babel/plugin-transform-unicode-sets-regex": {
-      "version": "7.28.6",
-      "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.28.6.tgz",
-      "integrity": "sha512-/wHc/paTUmsDYN7SZkpWxogTOBNnlx7nBQYfy6JJlCT7G3mVhltk3e++N7zV0XfgGsrqBxd4rJQt9H16I21Y1Q==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@babel/helper-create-regexp-features-plugin": "^7.28.5",
-        "@babel/helper-plugin-utils": "^7.28.6"
-      },
-      "engines": {
-        "node": ">=6.9.0"
-      },
-      "peerDependencies": {
-        "@babel/core": "^7.0.0"
-      }
-    },
-    "node_modules/@babel/preset-env": {
-      "version": "7.29.2",
-      "resolved": "https://registry.npmmirror.com/@babel/preset-env/-/preset-env-7.29.2.tgz",
-      "integrity": "sha512-DYD23veRYGvBFhcTY1iUvJnDNpuqNd/BzBwCvzOTKUnJjKg5kpUBh3/u9585Agdkgj+QuygG7jLfOPWMa2KVNw==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@babel/compat-data": "^7.29.0",
-        "@babel/helper-compilation-targets": "^7.28.6",
-        "@babel/helper-plugin-utils": "^7.28.6",
-        "@babel/helper-validator-option": "^7.27.1",
-        "@babel/plugin-bugfix-firefox-class-in-computed-class-key": "^7.28.5",
-        "@babel/plugin-bugfix-safari-class-field-initializer-scope": "^7.27.1",
-        "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.27.1",
-        "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.27.1",
-        "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "^7.28.6",
-        "@babel/plugin-proposal-private-property-in-object": "7.21.0-placeholder-for-preset-env.2",
-        "@babel/plugin-syntax-import-assertions": "^7.28.6",
-        "@babel/plugin-syntax-import-attributes": "^7.28.6",
-        "@babel/plugin-syntax-unicode-sets-regex": "^7.18.6",
-        "@babel/plugin-transform-arrow-functions": "^7.27.1",
-        "@babel/plugin-transform-async-generator-functions": "^7.29.0",
-        "@babel/plugin-transform-async-to-generator": "^7.28.6",
-        "@babel/plugin-transform-block-scoped-functions": "^7.27.1",
-        "@babel/plugin-transform-block-scoping": "^7.28.6",
-        "@babel/plugin-transform-class-properties": "^7.28.6",
-        "@babel/plugin-transform-class-static-block": "^7.28.6",
-        "@babel/plugin-transform-classes": "^7.28.6",
-        "@babel/plugin-transform-computed-properties": "^7.28.6",
-        "@babel/plugin-transform-destructuring": "^7.28.5",
-        "@babel/plugin-transform-dotall-regex": "^7.28.6",
-        "@babel/plugin-transform-duplicate-keys": "^7.27.1",
-        "@babel/plugin-transform-duplicate-named-capturing-groups-regex": "^7.29.0",
-        "@babel/plugin-transform-dynamic-import": "^7.27.1",
-        "@babel/plugin-transform-explicit-resource-management": "^7.28.6",
-        "@babel/plugin-transform-exponentiation-operator": "^7.28.6",
-        "@babel/plugin-transform-export-namespace-from": "^7.27.1",
-        "@babel/plugin-transform-for-of": "^7.27.1",
-        "@babel/plugin-transform-function-name": "^7.27.1",
-        "@babel/plugin-transform-json-strings": "^7.28.6",
-        "@babel/plugin-transform-literals": "^7.27.1",
-        "@babel/plugin-transform-logical-assignment-operators": "^7.28.6",
-        "@babel/plugin-transform-member-expression-literals": "^7.27.1",
-        "@babel/plugin-transform-modules-amd": "^7.27.1",
-        "@babel/plugin-transform-modules-commonjs": "^7.28.6",
-        "@babel/plugin-transform-modules-systemjs": "^7.29.0",
-        "@babel/plugin-transform-modules-umd": "^7.27.1",
-        "@babel/plugin-transform-named-capturing-groups-regex": "^7.29.0",
-        "@babel/plugin-transform-new-target": "^7.27.1",
-        "@babel/plugin-transform-nullish-coalescing-operator": "^7.28.6",
-        "@babel/plugin-transform-numeric-separator": "^7.28.6",
-        "@babel/plugin-transform-object-rest-spread": "^7.28.6",
-        "@babel/plugin-transform-object-super": "^7.27.1",
-        "@babel/plugin-transform-optional-catch-binding": "^7.28.6",
-        "@babel/plugin-transform-optional-chaining": "^7.28.6",
-        "@babel/plugin-transform-parameters": "^7.27.7",
-        "@babel/plugin-transform-private-methods": "^7.28.6",
-        "@babel/plugin-transform-private-property-in-object": "^7.28.6",
-        "@babel/plugin-transform-property-literals": "^7.27.1",
-        "@babel/plugin-transform-regenerator": "^7.29.0",
-        "@babel/plugin-transform-regexp-modifiers": "^7.28.6",
-        "@babel/plugin-transform-reserved-words": "^7.27.1",
-        "@babel/plugin-transform-shorthand-properties": "^7.27.1",
-        "@babel/plugin-transform-spread": "^7.28.6",
-        "@babel/plugin-transform-sticky-regex": "^7.27.1",
-        "@babel/plugin-transform-template-literals": "^7.27.1",
-        "@babel/plugin-transform-typeof-symbol": "^7.27.1",
-        "@babel/plugin-transform-unicode-escapes": "^7.27.1",
-        "@babel/plugin-transform-unicode-property-regex": "^7.28.6",
-        "@babel/plugin-transform-unicode-regex": "^7.27.1",
-        "@babel/plugin-transform-unicode-sets-regex": "^7.28.6",
-        "@babel/preset-modules": "0.1.6-no-external-plugins",
-        "babel-plugin-polyfill-corejs2": "^0.4.15",
-        "babel-plugin-polyfill-corejs3": "^0.14.0",
-        "babel-plugin-polyfill-regenerator": "^0.6.6",
-        "core-js-compat": "^3.48.0",
-        "semver": "^6.3.1"
-      },
-      "engines": {
-        "node": ">=6.9.0"
-      },
-      "peerDependencies": {
-        "@babel/core": "^7.0.0-0"
-      }
-    },
-    "node_modules/@babel/preset-env/node_modules/babel-plugin-polyfill-corejs3": {
-      "version": "0.14.2",
-      "resolved": "https://registry.npmmirror.com/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.14.2.tgz",
-      "integrity": "sha512-coWpDLJ410R781Npmn/SIBZEsAetR4xVi0SxLMXPaMO4lSf1MwnkGYMtkFxew0Dn8B3/CpbpYxN0JCgg8mn67g==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@babel/helper-define-polyfill-provider": "^0.6.8",
-        "core-js-compat": "^3.48.0"
-      },
-      "peerDependencies": {
-        "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0"
-      }
-    },
-    "node_modules/@babel/preset-modules": {
-      "version": "0.1.6-no-external-plugins",
-      "resolved": "https://registry.npmmirror.com/@babel/preset-modules/-/preset-modules-0.1.6-no-external-plugins.tgz",
-      "integrity": "sha512-HrcgcIESLm9aIR842yhJ5RWan/gebQUJ6E/E5+rf0y9o6oj7w0Br+sWuL6kEQ/o/AdfvR1Je9jG18/gnpwjEyA==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@babel/helper-plugin-utils": "^7.0.0",
-        "@babel/types": "^7.4.4",
-        "esutils": "^2.0.2"
-      },
-      "peerDependencies": {
-        "@babel/core": "^7.0.0-0 || ^8.0.0-0 <8.0.0"
-      }
-    },
-    "node_modules/@babel/runtime": {
-      "version": "7.29.2",
-      "resolved": "https://registry.npmmirror.com/@babel/runtime/-/runtime-7.29.2.tgz",
-      "integrity": "sha512-JiDShH45zKHWyGe4ZNVRrCjBz8Nh9TMmZG1kh4QTK8hCBTWBi8Da+i7s1fJw7/lYpM4ccepSNfqzZ/QvABBi5g==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=6.9.0"
-      }
-    },
-    "node_modules/@babel/template": {
-      "version": "7.28.6",
-      "resolved": "https://registry.npmmirror.com/@babel/template/-/template-7.28.6.tgz",
-      "integrity": "sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@babel/code-frame": "^7.28.6",
-        "@babel/parser": "^7.28.6",
-        "@babel/types": "^7.28.6"
-      },
-      "engines": {
-        "node": ">=6.9.0"
-      }
-    },
-    "node_modules/@babel/traverse": {
-      "version": "7.29.0",
-      "resolved": "https://registry.npmmirror.com/@babel/traverse/-/traverse-7.29.0.tgz",
-      "integrity": "sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@babel/code-frame": "^7.29.0",
-        "@babel/generator": "^7.29.0",
-        "@babel/helper-globals": "^7.28.0",
-        "@babel/parser": "^7.29.0",
-        "@babel/template": "^7.28.6",
-        "@babel/types": "^7.29.0",
-        "debug": "^4.3.1"
-      },
-      "engines": {
-        "node": ">=6.9.0"
-      }
-    },
-    "node_modules/@babel/types": {
-      "version": "7.29.0",
-      "resolved": "https://registry.npmmirror.com/@babel/types/-/types-7.29.0.tgz",
-      "integrity": "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==",
-      "license": "MIT",
-      "dependencies": {
-        "@babel/helper-string-parser": "^7.27.1",
-        "@babel/helper-validator-identifier": "^7.28.5"
-      },
-      "engines": {
-        "node": ">=6.9.0"
-      }
-    },
-    "node_modules/@discoveryjs/json-ext": {
-      "version": "0.5.7",
-      "resolved": "https://registry.npmmirror.com/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz",
-      "integrity": "sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=10.0.0"
-      }
-    },
-    "node_modules/@eslint/eslintrc": {
-      "version": "0.4.3",
-      "resolved": "https://registry.npmmirror.com/@eslint/eslintrc/-/eslintrc-0.4.3.tgz",
-      "integrity": "sha512-J6KFFz5QCYUJq3pf0mjEcCJVERbzv71PUIDczuh9JkwGEzced6CO5ADLHB1rbf/+oPBtoPfMYNOpGDzCANlbXw==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "ajv": "^6.12.4",
-        "debug": "^4.1.1",
-        "espree": "^7.3.0",
-        "globals": "^13.9.0",
-        "ignore": "^4.0.6",
-        "import-fresh": "^3.2.1",
-        "js-yaml": "^3.13.1",
-        "minimatch": "^3.0.4",
-        "strip-json-comments": "^3.1.1"
-      },
-      "engines": {
-        "node": "^10.12.0 || >=12.0.0"
-      }
-    },
-    "node_modules/@eslint/eslintrc/node_modules/ignore": {
-      "version": "4.0.6",
-      "resolved": "https://registry.npmmirror.com/ignore/-/ignore-4.0.6.tgz",
-      "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">= 4"
-      }
-    },
-    "node_modules/@hapi/hoek": {
-      "version": "9.3.0",
-      "resolved": "https://registry.npmmirror.com/@hapi/hoek/-/hoek-9.3.0.tgz",
-      "integrity": "sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ==",
-      "dev": true,
-      "license": "BSD-3-Clause"
-    },
-    "node_modules/@hapi/topo": {
-      "version": "5.1.0",
-      "resolved": "https://registry.npmmirror.com/@hapi/topo/-/topo-5.1.0.tgz",
-      "integrity": "sha512-foQZKJig7Ob0BMAYBfcJk8d77QtOe7Wo4ox7ff1lQYoNNAb6jwcY1ncdoy2e9wQZzvNy7ODZCYJkK8kzmcAnAg==",
-      "dev": true,
-      "license": "BSD-3-Clause",
-      "dependencies": {
-        "@hapi/hoek": "^9.0.0"
-      }
-    },
-    "node_modules/@humanwhocodes/config-array": {
-      "version": "0.5.0",
-      "resolved": "https://registry.npmmirror.com/@humanwhocodes/config-array/-/config-array-0.5.0.tgz",
-      "integrity": "sha512-FagtKFz74XrTl7y6HCzQpwDfXP0yhxe9lHLD1UZxjvZIcbyRz8zTFF/yYNfSfzU414eDwZ1SrO0Qvtyf+wFMQg==",
-      "deprecated": "Use @eslint/config-array instead",
-      "dev": true,
-      "license": "Apache-2.0",
-      "dependencies": {
-        "@humanwhocodes/object-schema": "^1.2.0",
-        "debug": "^4.1.1",
-        "minimatch": "^3.0.4"
-      },
-      "engines": {
-        "node": ">=10.10.0"
-      }
-    },
-    "node_modules/@humanwhocodes/object-schema": {
-      "version": "1.2.1",
-      "resolved": "https://registry.npmmirror.com/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz",
-      "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==",
-      "deprecated": "Use @eslint/object-schema instead",
-      "dev": true,
-      "license": "BSD-3-Clause"
-    },
-    "node_modules/@jridgewell/gen-mapping": {
-      "version": "0.3.13",
-      "resolved": "https://registry.npmmirror.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz",
-      "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@jridgewell/sourcemap-codec": "^1.5.0",
-        "@jridgewell/trace-mapping": "^0.3.24"
-      }
-    },
-    "node_modules/@jridgewell/remapping": {
-      "version": "2.3.5",
-      "resolved": "https://registry.npmmirror.com/@jridgewell/remapping/-/remapping-2.3.5.tgz",
-      "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@jridgewell/gen-mapping": "^0.3.5",
-        "@jridgewell/trace-mapping": "^0.3.24"
-      }
-    },
-    "node_modules/@jridgewell/resolve-uri": {
-      "version": "3.1.2",
-      "resolved": "https://registry.npmmirror.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
-      "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=6.0.0"
-      }
-    },
-    "node_modules/@jridgewell/source-map": {
-      "version": "0.3.11",
-      "resolved": "https://registry.npmmirror.com/@jridgewell/source-map/-/source-map-0.3.11.tgz",
-      "integrity": "sha512-ZMp1V8ZFcPG5dIWnQLr3NSI1MiCU7UETdS/A0G8V/XWHvJv3ZsFqutJn1Y5RPmAPX6F3BiE397OqveU/9NCuIA==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@jridgewell/gen-mapping": "^0.3.5",
-        "@jridgewell/trace-mapping": "^0.3.25"
-      }
-    },
-    "node_modules/@jridgewell/sourcemap-codec": {
-      "version": "1.5.5",
-      "resolved": "https://registry.npmmirror.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz",
-      "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==",
-      "dev": true,
-      "license": "MIT"
-    },
-    "node_modules/@jridgewell/trace-mapping": {
-      "version": "0.3.31",
-      "resolved": "https://registry.npmmirror.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz",
-      "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@jridgewell/resolve-uri": "^3.1.0",
-        "@jridgewell/sourcemap-codec": "^1.4.14"
-      }
-    },
-    "node_modules/@leichtgewicht/ip-codec": {
-      "version": "2.0.5",
-      "resolved": "https://registry.npmmirror.com/@leichtgewicht/ip-codec/-/ip-codec-2.0.5.tgz",
-      "integrity": "sha512-Vo+PSpZG2/fmgmiNzYK9qWRh8h/CHrwD0mo1h1DzL4yzHNSfWYujGTYsWGreD000gcgmZ7K4Ys6Tx9TxtsKdDw==",
-      "dev": true,
-      "license": "MIT"
-    },
-    "node_modules/@nicolo-ribaudo/eslint-scope-5-internals": {
-      "version": "5.1.1-v1",
-      "resolved": "https://registry.npmmirror.com/@nicolo-ribaudo/eslint-scope-5-internals/-/eslint-scope-5-internals-5.1.1-v1.tgz",
-      "integrity": "sha512-54/JRvkLIzzDWshCWfuhadfrfZVPiElY8Fcgmg1HroEly/EDSszzhBAsarCux+D/kOslTRquNzuyGSmUSTTHGg==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "eslint-scope": "5.1.1"
-      }
-    },
-    "node_modules/@node-ipc/js-queue": {
-      "version": "2.0.3",
-      "resolved": "https://registry.npmmirror.com/@node-ipc/js-queue/-/js-queue-2.0.3.tgz",
-      "integrity": "sha512-fL1wpr8hhD5gT2dA1qifeVaoDFlQR5es8tFuKqjHX+kdOtdNHnxkVZbtIrR2rxnMFvehkjaZRNV2H/gPXlb0hw==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "easy-stack": "1.0.1"
-      },
-      "engines": {
-        "node": ">=1.0.0"
-      }
-    },
-    "node_modules/@nodelib/fs.scandir": {
-      "version": "2.1.5",
-      "resolved": "https://registry.npmmirror.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
-      "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@nodelib/fs.stat": "2.0.5",
-        "run-parallel": "^1.1.9"
-      },
-      "engines": {
-        "node": ">= 8"
-      }
-    },
-    "node_modules/@nodelib/fs.stat": {
-      "version": "2.0.5",
-      "resolved": "https://registry.npmmirror.com/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz",
-      "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">= 8"
-      }
-    },
-    "node_modules/@nodelib/fs.walk": {
-      "version": "1.2.8",
-      "resolved": "https://registry.npmmirror.com/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz",
-      "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@nodelib/fs.scandir": "2.1.5",
-        "fastq": "^1.6.0"
-      },
-      "engines": {
-        "node": ">= 8"
-      }
-    },
-    "node_modules/@polka/url": {
-      "version": "1.0.0-next.29",
-      "resolved": "https://registry.npmmirror.com/@polka/url/-/url-1.0.0-next.29.tgz",
-      "integrity": "sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==",
-      "dev": true,
-      "license": "MIT"
-    },
-    "node_modules/@sideway/address": {
-      "version": "4.1.5",
-      "resolved": "https://registry.npmmirror.com/@sideway/address/-/address-4.1.5.tgz",
-      "integrity": "sha512-IqO/DUQHUkPeixNQ8n0JA6102hT9CmaljNTPmQ1u8MEhBo/R4Q8eKLN/vGZxuebwOroDB4cbpjheD4+/sKFK4Q==",
-      "dev": true,
-      "license": "BSD-3-Clause",
-      "dependencies": {
-        "@hapi/hoek": "^9.0.0"
-      }
-    },
-    "node_modules/@sideway/formula": {
-      "version": "3.0.1",
-      "resolved": "https://registry.npmmirror.com/@sideway/formula/-/formula-3.0.1.tgz",
-      "integrity": "sha512-/poHZJJVjx3L+zVD6g9KgHfYnb443oi7wLu/XKojDviHy6HOEOA6z1Trk5aR1dGcmPenJEgb2sK2I80LeS3MIg==",
-      "dev": true,
-      "license": "BSD-3-Clause"
-    },
-    "node_modules/@sideway/pinpoint": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmmirror.com/@sideway/pinpoint/-/pinpoint-2.0.0.tgz",
-      "integrity": "sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ==",
-      "dev": true,
-      "license": "BSD-3-Clause"
-    },
-    "node_modules/@soda/friendly-errors-webpack-plugin": {
-      "version": "1.8.1",
-      "resolved": "https://registry.npmmirror.com/@soda/friendly-errors-webpack-plugin/-/friendly-errors-webpack-plugin-1.8.1.tgz",
-      "integrity": "sha512-h2ooWqP8XuFqTXT+NyAFbrArzfQA7R6HTezADrvD9Re8fxMLTPPniLdqVTdDaO0eIoLaAwKT+d6w+5GeTk7Vbg==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "chalk": "^3.0.0",
-        "error-stack-parser": "^2.0.6",
-        "string-width": "^4.2.3",
-        "strip-ansi": "^6.0.1"
-      },
-      "engines": {
-        "node": ">=8.0.0"
-      },
-      "peerDependencies": {
-        "webpack": "^4.0.0 || ^5.0.0"
-      }
-    },
-    "node_modules/@soda/get-current-script": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmmirror.com/@soda/get-current-script/-/get-current-script-1.0.2.tgz",
-      "integrity": "sha512-T7VNNlYVM1SgQ+VsMYhnDkcGmWhQdL0bDyGm5TlQ3GBXnJscEClUUOKduWTmm2zCnvNLC1hc3JpuXjs/nFOc5w==",
-      "dev": true,
-      "license": "MIT"
-    },
-    "node_modules/@types/body-parser": {
-      "version": "1.19.6",
-      "resolved": "https://registry.npmmirror.com/@types/body-parser/-/body-parser-1.19.6.tgz",
-      "integrity": "sha512-HLFeCYgz89uk22N5Qg3dvGvsv46B8GLvKKo1zKG4NybA8U2DiEO3w9lqGg29t/tfLRJpJ6iQxnVw4OnB7MoM9g==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@types/connect": "*",
-        "@types/node": "*"
-      }
-    },
-    "node_modules/@types/bonjour": {
-      "version": "3.5.13",
-      "resolved": "https://registry.npmmirror.com/@types/bonjour/-/bonjour-3.5.13.tgz",
-      "integrity": "sha512-z9fJ5Im06zvUL548KvYNecEVlA7cVDkGUi6kZusb04mpyEFKCIZJvloCcmpmLaIahDpOQGHaHmG6imtPMmPXGQ==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@types/node": "*"
-      }
-    },
-    "node_modules/@types/connect": {
-      "version": "3.4.38",
-      "resolved": "https://registry.npmmirror.com/@types/connect/-/connect-3.4.38.tgz",
-      "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@types/node": "*"
-      }
-    },
-    "node_modules/@types/connect-history-api-fallback": {
-      "version": "1.5.4",
-      "resolved": "https://registry.npmmirror.com/@types/connect-history-api-fallback/-/connect-history-api-fallback-1.5.4.tgz",
-      "integrity": "sha512-n6Cr2xS1h4uAulPRdlw6Jl6s1oG8KrVilPN2yUITEs+K48EzMJJ3W1xy8K5eWuFvjp3R74AOIGSmp2UfBJ8HFw==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@types/express-serve-static-core": "*",
-        "@types/node": "*"
-      }
-    },
-    "node_modules/@types/eslint": {
-      "version": "8.56.12",
-      "resolved": "https://registry.npmmirror.com/@types/eslint/-/eslint-8.56.12.tgz",
-      "integrity": "sha512-03ruubjWyOHlmljCVoxSuNDdmfZDzsrrz0P2LeJsOXr+ZwFQ+0yQIwNCwt/GYhV7Z31fgtXJTAEs+FYlEL851g==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@types/estree": "*",
-        "@types/json-schema": "*"
-      }
-    },
-    "node_modules/@types/eslint-scope": {
-      "version": "3.7.7",
-      "resolved": "https://registry.npmmirror.com/@types/eslint-scope/-/eslint-scope-3.7.7.tgz",
-      "integrity": "sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@types/eslint": "*",
-        "@types/estree": "*"
-      }
-    },
-    "node_modules/@types/estree": {
-      "version": "1.0.8",
-      "resolved": "https://registry.npmmirror.com/@types/estree/-/estree-1.0.8.tgz",
-      "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==",
-      "dev": true,
-      "license": "MIT"
-    },
-    "node_modules/@types/express": {
-      "version": "4.17.25",
-      "resolved": "https://registry.npmmirror.com/@types/express/-/express-4.17.25.tgz",
-      "integrity": "sha512-dVd04UKsfpINUnK0yBoYHDF3xu7xVH4BuDotC/xGuycx4CgbP48X/KF/586bcObxT0HENHXEU8Nqtu6NR+eKhw==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@types/body-parser": "*",
-        "@types/express-serve-static-core": "^4.17.33",
-        "@types/qs": "*",
-        "@types/serve-static": "^1"
-      }
-    },
-    "node_modules/@types/express-serve-static-core": {
-      "version": "5.1.1",
-      "resolved": "https://registry.npmmirror.com/@types/express-serve-static-core/-/express-serve-static-core-5.1.1.tgz",
-      "integrity": "sha512-v4zIMr/cX7/d2BpAEX3KNKL/JrT1s43s96lLvvdTmza1oEvDudCqK9aF/djc/SWgy8Yh0h30TZx5VpzqFCxk5A==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@types/node": "*",
-        "@types/qs": "*",
-        "@types/range-parser": "*",
-        "@types/send": "*"
-      }
-    },
-    "node_modules/@types/express/node_modules/@types/express-serve-static-core": {
-      "version": "4.19.8",
-      "resolved": "https://registry.npmmirror.com/@types/express-serve-static-core/-/express-serve-static-core-4.19.8.tgz",
-      "integrity": "sha512-02S5fmqeoKzVZCHPZid4b8JH2eM5HzQLZWN2FohQEy/0eXTq8VXZfSN6Pcr3F6N9R/vNrj7cpgbhjie6m/1tCA==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@types/node": "*",
-        "@types/qs": "*",
-        "@types/range-parser": "*",
-        "@types/send": "*"
-      }
-    },
-    "node_modules/@types/html-minifier-terser": {
-      "version": "6.1.0",
-      "resolved": "https://registry.npmmirror.com/@types/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz",
-      "integrity": "sha512-oh/6byDPnL1zeNXFrDXFLyZjkr1MsBG667IM792caf1L2UPOOMf65NFzjUH/ltyfwjAGfs1rsX1eftK0jC/KIg==",
-      "dev": true,
-      "license": "MIT"
-    },
-    "node_modules/@types/http-errors": {
-      "version": "2.0.5",
-      "resolved": "https://registry.npmmirror.com/@types/http-errors/-/http-errors-2.0.5.tgz",
-      "integrity": "sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg==",
-      "dev": true,
-      "license": "MIT"
-    },
-    "node_modules/@types/http-proxy": {
-      "version": "1.17.17",
-      "resolved": "https://registry.npmmirror.com/@types/http-proxy/-/http-proxy-1.17.17.tgz",
-      "integrity": "sha512-ED6LB+Z1AVylNTu7hdzuBqOgMnvG/ld6wGCG8wFnAzKX5uyW2K3WD52v0gnLCTK/VLpXtKckgWuyScYK6cSPaw==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@types/node": "*"
-      }
-    },
-    "node_modules/@types/json-schema": {
-      "version": "7.0.15",
-      "resolved": "https://registry.npmmirror.com/@types/json-schema/-/json-schema-7.0.15.tgz",
-      "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==",
-      "dev": true,
-      "license": "MIT"
-    },
-    "node_modules/@types/mime": {
-      "version": "1.3.5",
-      "resolved": "https://registry.npmmirror.com/@types/mime/-/mime-1.3.5.tgz",
-      "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==",
-      "dev": true,
-      "license": "MIT"
-    },
-    "node_modules/@types/minimist": {
-      "version": "1.2.5",
-      "resolved": "https://registry.npmmirror.com/@types/minimist/-/minimist-1.2.5.tgz",
-      "integrity": "sha512-hov8bUuiLiyFPGyFPE1lwWhmzYbirOXQNNo40+y3zow8aFVTeyn3VWL0VFFfdNddA8S4Vf0Tc062rzyNr7Paag==",
-      "dev": true,
-      "license": "MIT"
-    },
-    "node_modules/@types/node": {
-      "version": "25.6.0",
-      "resolved": "https://registry.npmmirror.com/@types/node/-/node-25.6.0.tgz",
-      "integrity": "sha512-+qIYRKdNYJwY3vRCZMdJbPLJAtGjQBudzZzdzwQYkEPQd+PJGixUL5QfvCLDaULoLv+RhT3LDkwEfKaAkgSmNQ==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "undici-types": "~7.19.0"
-      }
-    },
-    "node_modules/@types/node-forge": {
-      "version": "1.3.14",
-      "resolved": "https://registry.npmmirror.com/@types/node-forge/-/node-forge-1.3.14.tgz",
-      "integrity": "sha512-mhVF2BnD4BO+jtOp7z1CdzaK4mbuK0LLQYAvdOLqHTavxFNq4zA1EmYkpnFjP8HOUzedfQkRnp0E2ulSAYSzAw==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@types/node": "*"
-      }
-    },
-    "node_modules/@types/normalize-package-data": {
-      "version": "2.4.4",
-      "resolved": "https://registry.npmmirror.com/@types/normalize-package-data/-/normalize-package-data-2.4.4.tgz",
-      "integrity": "sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==",
-      "dev": true,
-      "license": "MIT"
-    },
-    "node_modules/@types/parse-json": {
-      "version": "4.0.2",
-      "resolved": "https://registry.npmmirror.com/@types/parse-json/-/parse-json-4.0.2.tgz",
-      "integrity": "sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==",
-      "dev": true,
-      "license": "MIT"
-    },
-    "node_modules/@types/qs": {
-      "version": "6.15.0",
-      "resolved": "https://registry.npmmirror.com/@types/qs/-/qs-6.15.0.tgz",
-      "integrity": "sha512-JawvT8iBVWpzTrz3EGw9BTQFg3BQNmwERdKE22vlTxawwtbyUSlMppvZYKLZzB5zgACXdXxbD3m1bXaMqP/9ow==",
-      "dev": true,
-      "license": "MIT"
-    },
-    "node_modules/@types/range-parser": {
-      "version": "1.2.7",
-      "resolved": "https://registry.npmmirror.com/@types/range-parser/-/range-parser-1.2.7.tgz",
-      "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==",
-      "dev": true,
-      "license": "MIT"
-    },
-    "node_modules/@types/retry": {
-      "version": "0.12.0",
-      "resolved": "https://registry.npmmirror.com/@types/retry/-/retry-0.12.0.tgz",
-      "integrity": "sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==",
-      "dev": true,
-      "license": "MIT"
-    },
-    "node_modules/@types/send": {
-      "version": "1.2.1",
-      "resolved": "https://registry.npmmirror.com/@types/send/-/send-1.2.1.tgz",
-      "integrity": "sha512-arsCikDvlU99zl1g69TcAB3mzZPpxgw0UQnaHeC1Nwb015xp8bknZv5rIfri9xTOcMuaVgvabfIRA7PSZVuZIQ==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@types/node": "*"
-      }
-    },
-    "node_modules/@types/serve-index": {
-      "version": "1.9.4",
-      "resolved": "https://registry.npmmirror.com/@types/serve-index/-/serve-index-1.9.4.tgz",
-      "integrity": "sha512-qLpGZ/c2fhSs5gnYsQxtDEq3Oy8SXPClIXkW5ghvAvsNuVSA8k+gCONcUCS/UjLEYvYps+e8uBtfgXgvhwfNug==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@types/express": "*"
-      }
-    },
-    "node_modules/@types/serve-static": {
-      "version": "1.15.10",
-      "resolved": "https://registry.npmmirror.com/@types/serve-static/-/serve-static-1.15.10.tgz",
-      "integrity": "sha512-tRs1dB+g8Itk72rlSI2ZrW6vZg0YrLI81iQSTkMmOqnqCaNr/8Ek4VwWcN5vZgCYWbg/JJSGBlUaYGAOP73qBw==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@types/http-errors": "*",
-        "@types/node": "*",
-        "@types/send": "<1"
-      }
-    },
-    "node_modules/@types/serve-static/node_modules/@types/send": {
-      "version": "0.17.6",
-      "resolved": "https://registry.npmmirror.com/@types/send/-/send-0.17.6.tgz",
-      "integrity": "sha512-Uqt8rPBE8SY0RK8JB1EzVOIZ32uqy8HwdxCnoCOsYrvnswqmFZ/k+9Ikidlk/ImhsdvBsloHbAlewb2IEBV/Og==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@types/mime": "^1",
-        "@types/node": "*"
-      }
-    },
-    "node_modules/@types/sockjs": {
-      "version": "0.3.36",
-      "resolved": "https://registry.npmmirror.com/@types/sockjs/-/sockjs-0.3.36.tgz",
-      "integrity": "sha512-MK9V6NzAS1+Ud7JV9lJLFqW85VbC9dq3LmwZCuBe4wBDgKC0Kj/jd8Xl+nSviU+Qc3+m7umHHyHg//2KSa0a0Q==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@types/node": "*"
-      }
-    },
-    "node_modules/@types/ws": {
-      "version": "8.18.1",
-      "resolved": "https://registry.npmmirror.com/@types/ws/-/ws-8.18.1.tgz",
-      "integrity": "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@types/node": "*"
-      }
-    },
-    "node_modules/@vue/babel-helper-vue-jsx-merge-props": {
-      "version": "1.4.0",
-      "resolved": "https://registry.npmmirror.com/@vue/babel-helper-vue-jsx-merge-props/-/babel-helper-vue-jsx-merge-props-1.4.0.tgz",
-      "integrity": "sha512-JkqXfCkUDp4PIlFdDQ0TdXoIejMtTHP67/pvxlgeY+u5k3LEdKuWZ3LK6xkxo52uDoABIVyRwqVkfLQJhk7VBA==",
-      "dev": true,
-      "license": "MIT"
-    },
-    "node_modules/@vue/babel-helper-vue-transform-on": {
-      "version": "1.5.0",
-      "resolved": "https://registry.npmmirror.com/@vue/babel-helper-vue-transform-on/-/babel-helper-vue-transform-on-1.5.0.tgz",
-      "integrity": "sha512-0dAYkerNhhHutHZ34JtTl2czVQHUNWv6xEbkdF5W+Yrv5pCWsqjeORdOgbtW2I9gWlt+wBmVn+ttqN9ZxR5tzA==",
-      "dev": true,
-      "license": "MIT"
-    },
-    "node_modules/@vue/babel-plugin-jsx": {
-      "version": "1.5.0",
-      "resolved": "https://registry.npmmirror.com/@vue/babel-plugin-jsx/-/babel-plugin-jsx-1.5.0.tgz",
-      "integrity": "sha512-mneBhw1oOqCd2247O0Yw/mRwC9jIGACAJUlawkmMBiNmL4dGA2eMzuNZVNqOUfYTa6vqmND4CtOPzmEEEqLKFw==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@babel/helper-module-imports": "^7.27.1",
-        "@babel/helper-plugin-utils": "^7.27.1",
-        "@babel/plugin-syntax-jsx": "^7.27.1",
-        "@babel/template": "^7.27.2",
-        "@babel/traverse": "^7.28.0",
-        "@babel/types": "^7.28.2",
-        "@vue/babel-helper-vue-transform-on": "1.5.0",
-        "@vue/babel-plugin-resolve-type": "1.5.0",
-        "@vue/shared": "^3.5.18"
-      },
-      "peerDependencies": {
-        "@babel/core": "^7.0.0-0"
-      },
-      "peerDependenciesMeta": {
-        "@babel/core": {
-          "optional": true
-        }
-      }
-    },
-    "node_modules/@vue/babel-plugin-resolve-type": {
-      "version": "1.5.0",
-      "resolved": "https://registry.npmmirror.com/@vue/babel-plugin-resolve-type/-/babel-plugin-resolve-type-1.5.0.tgz",
-      "integrity": "sha512-Wm/60o+53JwJODm4Knz47dxJnLDJ9FnKnGZJbUUf8nQRAtt6P+undLUAVU3Ha33LxOJe6IPoifRQ6F/0RrU31w==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@babel/code-frame": "^7.27.1",
-        "@babel/helper-module-imports": "^7.27.1",
-        "@babel/helper-plugin-utils": "^7.27.1",
-        "@babel/parser": "^7.28.0",
-        "@vue/compiler-sfc": "^3.5.18"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/sxzz"
-      },
-      "peerDependencies": {
-        "@babel/core": "^7.0.0-0"
-      }
-    },
-    "node_modules/@vue/babel-plugin-transform-vue-jsx": {
-      "version": "1.4.0",
-      "resolved": "https://registry.npmmirror.com/@vue/babel-plugin-transform-vue-jsx/-/babel-plugin-transform-vue-jsx-1.4.0.tgz",
-      "integrity": "sha512-Fmastxw4MMx0vlgLS4XBX0XiBbUFzoMGeVXuMV08wyOfXdikAFqBTuYPR0tlk+XskL19EzHc39SgjrPGY23JnA==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@babel/helper-module-imports": "^7.0.0",
-        "@babel/plugin-syntax-jsx": "^7.2.0",
-        "@vue/babel-helper-vue-jsx-merge-props": "^1.4.0",
-        "html-tags": "^2.0.0",
-        "lodash.kebabcase": "^4.1.1",
-        "svg-tags": "^1.0.0"
-      },
-      "peerDependencies": {
-        "@babel/core": "^7.0.0-0"
-      }
-    },
-    "node_modules/@vue/babel-preset-app": {
-      "version": "5.0.9",
-      "resolved": "https://registry.npmmirror.com/@vue/babel-preset-app/-/babel-preset-app-5.0.9.tgz",
-      "integrity": "sha512-0rKOF4s/AhaRMJLybxOCgXfwtYhO3pwDSL/q/W8wRs1LzmHAc77FyTXWlun6VyKiSKwSdtH7CvOiWqq+DfofdA==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@babel/core": "^7.12.16",
-        "@babel/helper-compilation-targets": "^7.12.16",
-        "@babel/helper-module-imports": "^7.12.13",
-        "@babel/plugin-proposal-class-properties": "^7.12.13",
-        "@babel/plugin-proposal-decorators": "^7.12.13",
-        "@babel/plugin-syntax-dynamic-import": "^7.8.3",
-        "@babel/plugin-syntax-jsx": "^7.12.13",
-        "@babel/plugin-transform-runtime": "^7.12.15",
-        "@babel/preset-env": "^7.12.16",
-        "@babel/runtime": "^7.12.13",
-        "@vue/babel-plugin-jsx": "^1.0.3",
-        "@vue/babel-preset-jsx": "^1.1.2",
-        "babel-plugin-dynamic-import-node": "^2.3.3",
-        "core-js": "^3.8.3",
-        "core-js-compat": "^3.8.3",
-        "semver": "^7.3.4"
-      },
-      "peerDependencies": {
-        "@babel/core": "*",
-        "core-js": "^3",
-        "vue": "^2 || ^3.2.13"
-      },
-      "peerDependenciesMeta": {
-        "core-js": {
-          "optional": true
-        },
-        "vue": {
-          "optional": true
-        }
-      }
-    },
-    "node_modules/@vue/babel-preset-app/node_modules/semver": {
-      "version": "7.7.4",
-      "resolved": "https://registry.npmmirror.com/semver/-/semver-7.7.4.tgz",
-      "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==",
-      "dev": true,
-      "license": "ISC",
-      "bin": {
-        "semver": "bin/semver.js"
-      },
-      "engines": {
-        "node": ">=10"
-      }
-    },
-    "node_modules/@vue/babel-preset-jsx": {
-      "version": "1.4.0",
-      "resolved": "https://registry.npmmirror.com/@vue/babel-preset-jsx/-/babel-preset-jsx-1.4.0.tgz",
-      "integrity": "sha512-QmfRpssBOPZWL5xw7fOuHNifCQcNQC1PrOo/4fu6xlhlKJJKSA3HqX92Nvgyx8fqHZTUGMPHmFA+IDqwXlqkSA==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@vue/babel-helper-vue-jsx-merge-props": "^1.4.0",
-        "@vue/babel-plugin-transform-vue-jsx": "^1.4.0",
-        "@vue/babel-sugar-composition-api-inject-h": "^1.4.0",
-        "@vue/babel-sugar-composition-api-render-instance": "^1.4.0",
-        "@vue/babel-sugar-functional-vue": "^1.4.0",
-        "@vue/babel-sugar-inject-h": "^1.4.0",
-        "@vue/babel-sugar-v-model": "^1.4.0",
-        "@vue/babel-sugar-v-on": "^1.4.0"
-      },
-      "peerDependencies": {
-        "@babel/core": "^7.0.0-0",
-        "vue": "*"
-      },
-      "peerDependenciesMeta": {
-        "vue": {
-          "optional": true
-        }
-      }
-    },
-    "node_modules/@vue/babel-sugar-composition-api-inject-h": {
-      "version": "1.4.0",
-      "resolved": "https://registry.npmmirror.com/@vue/babel-sugar-composition-api-inject-h/-/babel-sugar-composition-api-inject-h-1.4.0.tgz",
-      "integrity": "sha512-VQq6zEddJHctnG4w3TfmlVp5FzDavUSut/DwR0xVoe/mJKXyMcsIibL42wPntozITEoY90aBV0/1d2KjxHU52g==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@babel/plugin-syntax-jsx": "^7.2.0"
-      },
-      "peerDependencies": {
-        "@babel/core": "^7.0.0-0"
-      }
-    },
-    "node_modules/@vue/babel-sugar-composition-api-render-instance": {
-      "version": "1.4.0",
-      "resolved": "https://registry.npmmirror.com/@vue/babel-sugar-composition-api-render-instance/-/babel-sugar-composition-api-render-instance-1.4.0.tgz",
-      "integrity": "sha512-6ZDAzcxvy7VcnCjNdHJ59mwK02ZFuP5CnucloidqlZwVQv5CQLijc3lGpR7MD3TWFi78J7+a8J56YxbCtHgT9Q==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@babel/plugin-syntax-jsx": "^7.2.0"
-      },
-      "peerDependencies": {
-        "@babel/core": "^7.0.0-0"
-      }
-    },
-    "node_modules/@vue/babel-sugar-functional-vue": {
-      "version": "1.4.0",
-      "resolved": "https://registry.npmmirror.com/@vue/babel-sugar-functional-vue/-/babel-sugar-functional-vue-1.4.0.tgz",
-      "integrity": "sha512-lTEB4WUFNzYt2In6JsoF9sAYVTo84wC4e+PoZWSgM6FUtqRJz7wMylaEhSRgG71YF+wfLD6cc9nqVeXN2rwBvw==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@babel/plugin-syntax-jsx": "^7.2.0"
-      },
-      "peerDependencies": {
-        "@babel/core": "^7.0.0-0"
-      }
-    },
-    "node_modules/@vue/babel-sugar-inject-h": {
-      "version": "1.4.0",
-      "resolved": "https://registry.npmmirror.com/@vue/babel-sugar-inject-h/-/babel-sugar-inject-h-1.4.0.tgz",
-      "integrity": "sha512-muwWrPKli77uO2fFM7eA3G1lAGnERuSz2NgAxuOLzrsTlQl8W4G+wwbM4nB6iewlKbwKRae3nL03UaF5ffAPMA==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@babel/plugin-syntax-jsx": "^7.2.0"
-      },
-      "peerDependencies": {
-        "@babel/core": "^7.0.0-0"
-      }
-    },
-    "node_modules/@vue/babel-sugar-v-model": {
-      "version": "1.4.0",
-      "resolved": "https://registry.npmmirror.com/@vue/babel-sugar-v-model/-/babel-sugar-v-model-1.4.0.tgz",
-      "integrity": "sha512-0t4HGgXb7WHYLBciZzN5s0Hzqan4Ue+p/3FdQdcaHAb7s5D9WZFGoSxEZHrR1TFVZlAPu1bejTKGeAzaaG3NCQ==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@babel/plugin-syntax-jsx": "^7.2.0",
-        "@vue/babel-helper-vue-jsx-merge-props": "^1.4.0",
-        "@vue/babel-plugin-transform-vue-jsx": "^1.4.0",
-        "camelcase": "^5.0.0",
-        "html-tags": "^2.0.0",
-        "svg-tags": "^1.0.0"
-      },
-      "peerDependencies": {
-        "@babel/core": "^7.0.0-0"
-      }
-    },
-    "node_modules/@vue/babel-sugar-v-on": {
-      "version": "1.4.0",
-      "resolved": "https://registry.npmmirror.com/@vue/babel-sugar-v-on/-/babel-sugar-v-on-1.4.0.tgz",
-      "integrity": "sha512-m+zud4wKLzSKgQrWwhqRObWzmTuyzl6vOP7024lrpeJM4x2UhQtRDLgYjXAw9xBXjCwS0pP9kXjg91F9ZNo9JA==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@babel/plugin-syntax-jsx": "^7.2.0",
-        "@vue/babel-plugin-transform-vue-jsx": "^1.4.0",
-        "camelcase": "^5.0.0"
-      },
-      "peerDependencies": {
-        "@babel/core": "^7.0.0-0"
-      }
-    },
-    "node_modules/@vue/cli-overlay": {
-      "version": "5.0.9",
-      "resolved": "https://registry.npmmirror.com/@vue/cli-overlay/-/cli-overlay-5.0.9.tgz",
-      "integrity": "sha512-aBdZWrYKxLuFz1FDsk/muFD7GycrsW73Gi11yRc7R2W7Bm8mDRc9HKAI790gdg4NV+chkDFmfkegjg5iMDEpAA==",
-      "dev": true,
-      "license": "MIT"
-    },
-    "node_modules/@vue/cli-plugin-babel": {
-      "version": "5.0.9",
-      "resolved": "https://registry.npmmirror.com/@vue/cli-plugin-babel/-/cli-plugin-babel-5.0.9.tgz",
-      "integrity": "sha512-oDZt1Kfe4KGNtig3/3zFo2pIeDJij2uS0M6S+tAqQno4Zpla2D8Hk/AR5PrstUd/HmhHZYJoGyF78MOfj3SbWg==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@babel/core": "^7.12.16",
-        "@vue/babel-preset-app": "^5.0.9",
-        "@vue/cli-shared-utils": "^5.0.9",
-        "babel-loader": "^8.2.2",
-        "thread-loader": "^3.0.0",
-        "webpack": "^5.54.0"
-      },
-      "peerDependencies": {
-        "@vue/cli-service": "^3.0.0 || ^4.0.0 || ^5.0.0-0"
-      }
-    },
-    "node_modules/@vue/cli-plugin-eslint": {
-      "version": "5.0.9",
-      "resolved": "https://registry.npmmirror.com/@vue/cli-plugin-eslint/-/cli-plugin-eslint-5.0.9.tgz",
-      "integrity": "sha512-OfAa85qhP0dKSprI8+9qjbXW8BzOlOvEtXwdrTrAKlD6aN8oa/u6k4vbfJGdYbpsbpkj8FXYdCRkTgNG8KZbxg==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@vue/cli-shared-utils": "^5.0.9",
-        "eslint-webpack-plugin": "^3.1.0",
-        "globby": "^11.0.2",
-        "webpack": "^5.54.0",
-        "yorkie": "^2.0.0"
-      },
-      "peerDependencies": {
-        "@vue/cli-service": "^3.0.0 || ^4.0.0 || ^5.0.0-0",
-        "eslint": ">=7.5.0"
-      }
-    },
-    "node_modules/@vue/cli-plugin-router": {
-      "version": "5.0.9",
-      "resolved": "https://registry.npmmirror.com/@vue/cli-plugin-router/-/cli-plugin-router-5.0.9.tgz",
-      "integrity": "sha512-kopbO/8kIl5CAffwgptXEwV509i+M0FfwW4sSkgQ2RzpxOYBjQZvp+096mjZfFcWKSmryNP/ri/Mnu78vmhlhw==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@vue/cli-shared-utils": "^5.0.9"
-      },
-      "peerDependencies": {
-        "@vue/cli-service": "^3.0.0 || ^4.0.0 || ^5.0.0-0"
-      }
-    },
-    "node_modules/@vue/cli-plugin-vuex": {
-      "version": "5.0.9",
-      "resolved": "https://registry.npmmirror.com/@vue/cli-plugin-vuex/-/cli-plugin-vuex-5.0.9.tgz",
-      "integrity": "sha512-AQhgGNFVd4Pu2crvS0a+hRckgrJv07gzOASdbLd3I72wkT43dd01MLRp8IBRRsu92t3MXenW86AZUCbQBz3//A==",
-      "dev": true,
-      "license": "MIT",
-      "peerDependencies": {
-        "@vue/cli-service": "^3.0.0 || ^4.0.0 || ^5.0.0-0"
-      }
-    },
-    "node_modules/@vue/cli-service": {
-      "version": "5.0.9",
-      "resolved": "https://registry.npmmirror.com/@vue/cli-service/-/cli-service-5.0.9.tgz",
-      "integrity": "sha512-yTX7GVyM19tEbd+y5/gA6MkVKA6K61nVYHYAivD61Hx6odVFmQsaC3/R3cWAHM1P5oVKCevBbumPljbT+tFG2w==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@babel/helper-compilation-targets": "^7.12.16",
-        "@soda/friendly-errors-webpack-plugin": "^1.8.0",
-        "@soda/get-current-script": "^1.0.2",
-        "@types/minimist": "^1.2.0",
-        "@vue/cli-overlay": "^5.0.9",
-        "@vue/cli-plugin-router": "^5.0.9",
-        "@vue/cli-plugin-vuex": "^5.0.9",
-        "@vue/cli-shared-utils": "^5.0.9",
-        "@vue/component-compiler-utils": "^3.3.0",
-        "@vue/vue-loader-v15": "npm:vue-loader@^15.9.7",
-        "@vue/web-component-wrapper": "^1.3.0",
-        "acorn": "^8.0.5",
-        "acorn-walk": "^8.0.2",
-        "address": "^1.1.2",
-        "autoprefixer": "^10.2.4",
-        "browserslist": "^4.16.3",
-        "case-sensitive-paths-webpack-plugin": "^2.3.0",
-        "cli-highlight": "^2.1.10",
-        "clipboardy": "^2.3.0",
-        "cliui": "^7.0.4",
-        "copy-webpack-plugin": "^9.0.1",
-        "css-loader": "^6.5.0",
-        "css-minimizer-webpack-plugin": "^3.0.2",
-        "cssnano": "^5.0.0",
-        "debug": "^4.1.1",
-        "default-gateway": "^6.0.3",
-        "dotenv": "^10.0.0",
-        "dotenv-expand": "^5.1.0",
-        "fs-extra": "^9.1.0",
-        "globby": "^11.0.2",
-        "hash-sum": "^2.0.0",
-        "html-webpack-plugin": "^5.1.0",
-        "is-file-esm": "^1.0.0",
-        "launch-editor-middleware": "^2.2.1",
-        "lodash.defaultsdeep": "^4.6.1",
-        "lodash.mapvalues": "^4.6.0",
-        "mini-css-extract-plugin": "^2.5.3",
-        "minimist": "^1.2.5",
-        "module-alias": "^2.2.2",
-        "portfinder": "^1.0.26",
-        "postcss": "^8.2.6",
-        "postcss-loader": "^6.1.1",
-        "progress-webpack-plugin": "^1.0.12",
-        "ssri": "^8.0.1",
-        "terser-webpack-plugin": "^5.1.1",
-        "thread-loader": "^3.0.0",
-        "vue-loader": "^17.0.0",
-        "vue-style-loader": "^4.1.3",
-        "webpack": "^5.54.0",
-        "webpack-bundle-analyzer": "^4.4.0",
-        "webpack-chain": "^6.5.1",
-        "webpack-dev-server": "^4.7.3",
-        "webpack-merge": "^5.7.3",
-        "webpack-virtual-modules": "^0.4.2",
-        "whatwg-fetch": "^3.6.2"
-      },
-      "bin": {
-        "vue-cli-service": "bin/vue-cli-service.js"
-      },
-      "engines": {
-        "node": "^12.0.0 || >= 14.0.0"
-      },
-      "peerDependencies": {
-        "vue-template-compiler": "^2.0.0",
-        "webpack-sources": "*"
-      },
-      "peerDependenciesMeta": {
-        "cache-loader": {
-          "optional": true
-        },
-        "less-loader": {
-          "optional": true
-        },
-        "pug-plain-loader": {
-          "optional": true
-        },
-        "raw-loader": {
-          "optional": true
-        },
-        "sass-loader": {
-          "optional": true
-        },
-        "stylus-loader": {
-          "optional": true
-        },
-        "vue-template-compiler": {
-          "optional": true
-        },
-        "webpack-sources": {
-          "optional": true
-        }
-      }
-    },
-    "node_modules/@vue/cli-shared-utils": {
-      "version": "5.0.9",
-      "resolved": "https://registry.npmmirror.com/@vue/cli-shared-utils/-/cli-shared-utils-5.0.9.tgz",
-      "integrity": "sha512-lf4KykiG8j9KwvNVi7fKtASmHuLsxCcCsflVU2b2CHMRuR4weOIV3zuuCrjWKjk0APn/MHJhgCjJGzHMbTtd5w==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@achrinza/node-ipc": "^9.2.5",
-        "chalk": "^4.1.2",
-        "execa": "^1.0.0",
-        "joi": "^17.4.0",
-        "launch-editor": "^2.2.1",
-        "lru-cache": "^6.0.0",
-        "node-fetch": "^2.6.7",
-        "open": "^8.0.2",
-        "ora": "^5.3.0",
-        "read-pkg": "^5.1.1",
-        "semver": "^7.3.4",
-        "strip-ansi": "^6.0.0"
-      }
-    },
-    "node_modules/@vue/cli-shared-utils/node_modules/chalk": {
-      "version": "4.1.2",
-      "resolved": "https://registry.npmmirror.com/chalk/-/chalk-4.1.2.tgz",
-      "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "ansi-styles": "^4.1.0",
-        "supports-color": "^7.1.0"
-      },
-      "engines": {
-        "node": ">=10"
-      },
-      "funding": {
-        "url": "https://github.com/chalk/chalk?sponsor=1"
-      }
-    },
-    "node_modules/@vue/cli-shared-utils/node_modules/lru-cache": {
-      "version": "6.0.0",
-      "resolved": "https://registry.npmmirror.com/lru-cache/-/lru-cache-6.0.0.tgz",
-      "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
-      "dev": true,
-      "license": "ISC",
-      "dependencies": {
-        "yallist": "^4.0.0"
-      },
-      "engines": {
-        "node": ">=10"
-      }
-    },
-    "node_modules/@vue/cli-shared-utils/node_modules/semver": {
-      "version": "7.7.4",
-      "resolved": "https://registry.npmmirror.com/semver/-/semver-7.7.4.tgz",
-      "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==",
-      "dev": true,
-      "license": "ISC",
-      "bin": {
-        "semver": "bin/semver.js"
-      },
-      "engines": {
-        "node": ">=10"
-      }
-    },
-    "node_modules/@vue/cli-shared-utils/node_modules/yallist": {
-      "version": "4.0.0",
-      "resolved": "https://registry.npmmirror.com/yallist/-/yallist-4.0.0.tgz",
-      "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
-      "dev": true,
-      "license": "ISC"
-    },
-    "node_modules/@vue/compiler-core": {
-      "version": "3.5.32",
-      "resolved": "https://registry.npmmirror.com/@vue/compiler-core/-/compiler-core-3.5.32.tgz",
-      "integrity": "sha512-4x74Tbtqnda8s/NSD6e1Dr5p1c8HdMU5RWSjMSUzb8RTcUQqevDCxVAitcLBKT+ie3o0Dl9crc/S/opJM7qBGQ==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@babel/parser": "^7.29.2",
-        "@vue/shared": "3.5.32",
-        "entities": "^7.0.1",
-        "estree-walker": "^2.0.2",
-        "source-map-js": "^1.2.1"
-      }
-    },
-    "node_modules/@vue/compiler-dom": {
-      "version": "3.5.32",
-      "resolved": "https://registry.npmmirror.com/@vue/compiler-dom/-/compiler-dom-3.5.32.tgz",
-      "integrity": "sha512-ybHAu70NtiEI1fvAUz3oXZqkUYEe5J98GjMDpTGl5iHb0T15wQYLR4wE3h9xfuTNA+Cm2f4czfe8B4s+CCH57Q==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@vue/compiler-core": "3.5.32",
-        "@vue/shared": "3.5.32"
-      }
-    },
-    "node_modules/@vue/compiler-sfc": {
-      "version": "3.5.32",
-      "resolved": "https://registry.npmmirror.com/@vue/compiler-sfc/-/compiler-sfc-3.5.32.tgz",
-      "integrity": "sha512-8UYUYo71cP/0YHMO814TRZlPuUUw3oifHuMR7Wp9SNoRSrxRQnhMLNlCeaODNn6kNTJsjFoQ/kqIj4qGvya4Xg==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@babel/parser": "^7.29.2",
-        "@vue/compiler-core": "3.5.32",
-        "@vue/compiler-dom": "3.5.32",
-        "@vue/compiler-ssr": "3.5.32",
-        "@vue/shared": "3.5.32",
-        "estree-walker": "^2.0.2",
-        "magic-string": "^0.30.21",
-        "postcss": "^8.5.8",
-        "source-map-js": "^1.2.1"
-      }
-    },
-    "node_modules/@vue/compiler-ssr": {
-      "version": "3.5.32",
-      "resolved": "https://registry.npmmirror.com/@vue/compiler-ssr/-/compiler-ssr-3.5.32.tgz",
-      "integrity": "sha512-Gp4gTs22T3DgRotZ8aA/6m2jMR+GMztvBXUBEUOYOcST+giyGWJ4WvFd7QLHBkzTxkfOt8IELKNdpzITLbA2rw==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@vue/compiler-dom": "3.5.32",
-        "@vue/shared": "3.5.32"
-      }
-    },
-    "node_modules/@vue/component-compiler-utils": {
-      "version": "3.3.0",
-      "resolved": "https://registry.npmmirror.com/@vue/component-compiler-utils/-/component-compiler-utils-3.3.0.tgz",
-      "integrity": "sha512-97sfH2mYNU+2PzGrmK2haqffDpVASuib9/w2/noxiFi31Z54hW+q3izKQXXQZSNhtiUpAI36uSuYepeBe4wpHQ==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "consolidate": "^0.15.1",
-        "hash-sum": "^1.0.2",
-        "lru-cache": "^4.1.2",
-        "merge-source-map": "^1.1.0",
-        "postcss": "^7.0.36",
-        "postcss-selector-parser": "^6.0.2",
-        "source-map": "~0.6.1",
-        "vue-template-es2015-compiler": "^1.9.0"
-      },
-      "optionalDependencies": {
-        "prettier": "^1.18.2 || ^2.0.0"
-      }
-    },
-    "node_modules/@vue/component-compiler-utils/node_modules/hash-sum": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmmirror.com/hash-sum/-/hash-sum-1.0.2.tgz",
-      "integrity": "sha512-fUs4B4L+mlt8/XAtSOGMUO1TXmAelItBPtJG7CyHJfYTdDjwisntGO2JQz7oUsatOY9o68+57eziUVNw/mRHmA==",
-      "dev": true,
-      "license": "MIT"
-    },
-    "node_modules/@vue/component-compiler-utils/node_modules/lru-cache": {
-      "version": "4.1.5",
-      "resolved": "https://registry.npmmirror.com/lru-cache/-/lru-cache-4.1.5.tgz",
-      "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==",
-      "dev": true,
-      "license": "ISC",
-      "dependencies": {
-        "pseudomap": "^1.0.2",
-        "yallist": "^2.1.2"
-      }
-    },
-    "node_modules/@vue/component-compiler-utils/node_modules/picocolors": {
-      "version": "0.2.1",
-      "resolved": "https://registry.npmmirror.com/picocolors/-/picocolors-0.2.1.tgz",
-      "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==",
-      "dev": true,
-      "license": "ISC"
-    },
-    "node_modules/@vue/component-compiler-utils/node_modules/postcss": {
-      "version": "7.0.39",
-      "resolved": "https://registry.npmmirror.com/postcss/-/postcss-7.0.39.tgz",
-      "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "picocolors": "^0.2.1",
-        "source-map": "^0.6.1"
-      },
-      "engines": {
-        "node": ">=6.0.0"
-      },
-      "funding": {
-        "type": "opencollective",
-        "url": "https://opencollective.com/postcss/"
-      }
-    },
-    "node_modules/@vue/component-compiler-utils/node_modules/yallist": {
-      "version": "2.1.2",
-      "resolved": "https://registry.npmmirror.com/yallist/-/yallist-2.1.2.tgz",
-      "integrity": "sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A==",
-      "dev": true,
-      "license": "ISC"
-    },
-    "node_modules/@vue/shared": {
-      "version": "3.5.32",
-      "resolved": "https://registry.npmmirror.com/@vue/shared/-/shared-3.5.32.tgz",
-      "integrity": "sha512-ksNyrmRQzWJJ8n3cRDuSF7zNNontuJg1YHnmWRJd2AMu8Ij2bqwiiri2lH5rHtYPZjj4STkNcgcmiQqlOjiYGg==",
-      "dev": true,
-      "license": "MIT"
-    },
-    "node_modules/@vue/vue-loader-v15": {
-      "name": "vue-loader",
-      "version": "15.11.1",
-      "resolved": "https://registry.npmmirror.com/vue-loader/-/vue-loader-15.11.1.tgz",
-      "integrity": "sha512-0iw4VchYLePqJfJu9s62ACWUXeSqM30SQqlIftbYWM3C+jpPcEHKSPUZBLjSF9au4HTHQ/naF6OGnO3Q/qGR3Q==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@vue/component-compiler-utils": "^3.1.0",
-        "hash-sum": "^1.0.2",
-        "loader-utils": "^1.1.0",
-        "vue-hot-reload-api": "^2.3.0",
-        "vue-style-loader": "^4.1.0"
-      },
-      "peerDependencies": {
-        "css-loader": "*",
-        "webpack": "^3.0.0 || ^4.1.0 || ^5.0.0-0"
-      },
-      "peerDependenciesMeta": {
-        "cache-loader": {
-          "optional": true
-        },
-        "prettier": {
-          "optional": true
-        },
-        "vue-template-compiler": {
-          "optional": true
-        }
-      }
-    },
-    "node_modules/@vue/vue-loader-v15/node_modules/hash-sum": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmmirror.com/hash-sum/-/hash-sum-1.0.2.tgz",
-      "integrity": "sha512-fUs4B4L+mlt8/XAtSOGMUO1TXmAelItBPtJG7CyHJfYTdDjwisntGO2JQz7oUsatOY9o68+57eziUVNw/mRHmA==",
-      "dev": true,
-      "license": "MIT"
-    },
-    "node_modules/@vue/web-component-wrapper": {
-      "version": "1.3.0",
-      "resolved": "https://registry.npmmirror.com/@vue/web-component-wrapper/-/web-component-wrapper-1.3.0.tgz",
-      "integrity": "sha512-Iu8Tbg3f+emIIMmI2ycSI8QcEuAUgPTgHwesDU1eKMLE4YC/c/sFbGc70QgMq31ijRftV0R7vCm9co6rldCeOA==",
-      "dev": true,
-      "license": "MIT"
-    },
-    "node_modules/@webassemblyjs/ast": {
-      "version": "1.14.1",
-      "resolved": "https://registry.npmmirror.com/@webassemblyjs/ast/-/ast-1.14.1.tgz",
-      "integrity": "sha512-nuBEDgQfm1ccRp/8bCQrx1frohyufl4JlbMMZ4P1wpeOfDhF6FQkxZJ1b/e+PLwr6X1Nhw6OLme5usuBWYBvuQ==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@webassemblyjs/helper-numbers": "1.13.2",
-        "@webassemblyjs/helper-wasm-bytecode": "1.13.2"
-      }
-    },
-    "node_modules/@webassemblyjs/floating-point-hex-parser": {
-      "version": "1.13.2",
-      "resolved": "https://registry.npmmirror.com/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.13.2.tgz",
-      "integrity": "sha512-6oXyTOzbKxGH4steLbLNOu71Oj+C8Lg34n6CqRvqfS2O71BxY6ByfMDRhBytzknj9yGUPVJ1qIKhRlAwO1AovA==",
-      "dev": true,
-      "license": "MIT"
-    },
-    "node_modules/@webassemblyjs/helper-api-error": {
-      "version": "1.13.2",
-      "resolved": "https://registry.npmmirror.com/@webassemblyjs/helper-api-error/-/helper-api-error-1.13.2.tgz",
-      "integrity": "sha512-U56GMYxy4ZQCbDZd6JuvvNV/WFildOjsaWD3Tzzvmw/mas3cXzRJPMjP83JqEsgSbyrmaGjBfDtV7KDXV9UzFQ==",
-      "dev": true,
-      "license": "MIT"
-    },
-    "node_modules/@webassemblyjs/helper-buffer": {
-      "version": "1.14.1",
-      "resolved": "https://registry.npmmirror.com/@webassemblyjs/helper-buffer/-/helper-buffer-1.14.1.tgz",
-      "integrity": "sha512-jyH7wtcHiKssDtFPRB+iQdxlDf96m0E39yb0k5uJVhFGleZFoNw1c4aeIcVUPPbXUVJ94wwnMOAqUHyzoEPVMA==",
-      "dev": true,
-      "license": "MIT"
-    },
-    "node_modules/@webassemblyjs/helper-numbers": {
-      "version": "1.13.2",
-      "resolved": "https://registry.npmmirror.com/@webassemblyjs/helper-numbers/-/helper-numbers-1.13.2.tgz",
-      "integrity": "sha512-FE8aCmS5Q6eQYcV3gI35O4J789wlQA+7JrqTTpJqn5emA4U2hvwJmvFRC0HODS+3Ye6WioDklgd6scJ3+PLnEA==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@webassemblyjs/floating-point-hex-parser": "1.13.2",
-        "@webassemblyjs/helper-api-error": "1.13.2",
-        "@xtuc/long": "4.2.2"
-      }
-    },
-    "node_modules/@webassemblyjs/helper-wasm-bytecode": {
-      "version": "1.13.2",
-      "resolved": "https://registry.npmmirror.com/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.13.2.tgz",
-      "integrity": "sha512-3QbLKy93F0EAIXLh0ogEVR6rOubA9AoZ+WRYhNbFyuB70j3dRdwH9g+qXhLAO0kiYGlg3TxDV+I4rQTr/YNXkA==",
-      "dev": true,
-      "license": "MIT"
-    },
-    "node_modules/@webassemblyjs/helper-wasm-section": {
-      "version": "1.14.1",
-      "resolved": "https://registry.npmmirror.com/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.14.1.tgz",
-      "integrity": "sha512-ds5mXEqTJ6oxRoqjhWDU83OgzAYjwsCV8Lo/N+oRsNDmx/ZDpqalmrtgOMkHwxsG0iI//3BwWAErYRHtgn0dZw==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@webassemblyjs/ast": "1.14.1",
-        "@webassemblyjs/helper-buffer": "1.14.1",
-        "@webassemblyjs/helper-wasm-bytecode": "1.13.2",
-        "@webassemblyjs/wasm-gen": "1.14.1"
-      }
-    },
-    "node_modules/@webassemblyjs/ieee754": {
-      "version": "1.13.2",
-      "resolved": "https://registry.npmmirror.com/@webassemblyjs/ieee754/-/ieee754-1.13.2.tgz",
-      "integrity": "sha512-4LtOzh58S/5lX4ITKxnAK2USuNEvpdVV9AlgGQb8rJDHaLeHciwG4zlGr0j/SNWlr7x3vO1lDEsuePvtcDNCkw==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@xtuc/ieee754": "^1.2.0"
-      }
-    },
-    "node_modules/@webassemblyjs/leb128": {
-      "version": "1.13.2",
-      "resolved": "https://registry.npmmirror.com/@webassemblyjs/leb128/-/leb128-1.13.2.tgz",
-      "integrity": "sha512-Lde1oNoIdzVzdkNEAWZ1dZ5orIbff80YPdHx20mrHwHrVNNTjNr8E3xz9BdpcGqRQbAEa+fkrCb+fRFTl/6sQw==",
-      "dev": true,
-      "license": "Apache-2.0",
-      "dependencies": {
-        "@xtuc/long": "4.2.2"
-      }
-    },
-    "node_modules/@webassemblyjs/utf8": {
-      "version": "1.13.2",
-      "resolved": "https://registry.npmmirror.com/@webassemblyjs/utf8/-/utf8-1.13.2.tgz",
-      "integrity": "sha512-3NQWGjKTASY1xV5m7Hr0iPeXD9+RDobLll3T9d2AO+g3my8xy5peVyjSag4I50mR1bBSN/Ct12lo+R9tJk0NZQ==",
-      "dev": true,
-      "license": "MIT"
-    },
-    "node_modules/@webassemblyjs/wasm-edit": {
-      "version": "1.14.1",
-      "resolved": "https://registry.npmmirror.com/@webassemblyjs/wasm-edit/-/wasm-edit-1.14.1.tgz",
-      "integrity": "sha512-RNJUIQH/J8iA/1NzlE4N7KtyZNHi3w7at7hDjvRNm5rcUXa00z1vRz3glZoULfJ5mpvYhLybmVcwcjGrC1pRrQ==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@webassemblyjs/ast": "1.14.1",
-        "@webassemblyjs/helper-buffer": "1.14.1",
-        "@webassemblyjs/helper-wasm-bytecode": "1.13.2",
-        "@webassemblyjs/helper-wasm-section": "1.14.1",
-        "@webassemblyjs/wasm-gen": "1.14.1",
-        "@webassemblyjs/wasm-opt": "1.14.1",
-        "@webassemblyjs/wasm-parser": "1.14.1",
-        "@webassemblyjs/wast-printer": "1.14.1"
-      }
-    },
-    "node_modules/@webassemblyjs/wasm-gen": {
-      "version": "1.14.1",
-      "resolved": "https://registry.npmmirror.com/@webassemblyjs/wasm-gen/-/wasm-gen-1.14.1.tgz",
-      "integrity": "sha512-AmomSIjP8ZbfGQhumkNvgC33AY7qtMCXnN6bL2u2Js4gVCg8fp735aEiMSBbDR7UQIj90n4wKAFUSEd0QN2Ukg==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@webassemblyjs/ast": "1.14.1",
-        "@webassemblyjs/helper-wasm-bytecode": "1.13.2",
-        "@webassemblyjs/ieee754": "1.13.2",
-        "@webassemblyjs/leb128": "1.13.2",
-        "@webassemblyjs/utf8": "1.13.2"
-      }
-    },
-    "node_modules/@webassemblyjs/wasm-opt": {
-      "version": "1.14.1",
-      "resolved": "https://registry.npmmirror.com/@webassemblyjs/wasm-opt/-/wasm-opt-1.14.1.tgz",
-      "integrity": "sha512-PTcKLUNvBqnY2U6E5bdOQcSM+oVP/PmrDY9NzowJjislEjwP/C4an2303MCVS2Mg9d3AJpIGdUFIQQWbPds0Sw==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@webassemblyjs/ast": "1.14.1",
-        "@webassemblyjs/helper-buffer": "1.14.1",
-        "@webassemblyjs/wasm-gen": "1.14.1",
-        "@webassemblyjs/wasm-parser": "1.14.1"
-      }
-    },
-    "node_modules/@webassemblyjs/wasm-parser": {
-      "version": "1.14.1",
-      "resolved": "https://registry.npmmirror.com/@webassemblyjs/wasm-parser/-/wasm-parser-1.14.1.tgz",
-      "integrity": "sha512-JLBl+KZ0R5qB7mCnud/yyX08jWFw5MsoalJ1pQ4EdFlgj9VdXKGuENGsiCIjegI1W7p91rUlcB/LB5yRJKNTcQ==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@webassemblyjs/ast": "1.14.1",
-        "@webassemblyjs/helper-api-error": "1.13.2",
-        "@webassemblyjs/helper-wasm-bytecode": "1.13.2",
-        "@webassemblyjs/ieee754": "1.13.2",
-        "@webassemblyjs/leb128": "1.13.2",
-        "@webassemblyjs/utf8": "1.13.2"
-      }
-    },
-    "node_modules/@webassemblyjs/wast-printer": {
-      "version": "1.14.1",
-      "resolved": "https://registry.npmmirror.com/@webassemblyjs/wast-printer/-/wast-printer-1.14.1.tgz",
-      "integrity": "sha512-kPSSXE6De1XOR820C90RIo2ogvZG+c3KiHzqUoO/F34Y2shGzesfqv7o57xrxovZJH/MetF5UjroJ/R/3isoiw==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@webassemblyjs/ast": "1.14.1",
-        "@xtuc/long": "4.2.2"
-      }
-    },
-    "node_modules/@xtuc/ieee754": {
-      "version": "1.2.0",
-      "resolved": "https://registry.npmmirror.com/@xtuc/ieee754/-/ieee754-1.2.0.tgz",
-      "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==",
-      "dev": true,
-      "license": "BSD-3-Clause"
-    },
-    "node_modules/@xtuc/long": {
-      "version": "4.2.2",
-      "resolved": "https://registry.npmmirror.com/@xtuc/long/-/long-4.2.2.tgz",
-      "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==",
-      "dev": true,
-      "license": "Apache-2.0"
-    },
-    "node_modules/accepts": {
-      "version": "1.3.8",
-      "resolved": "https://registry.npmmirror.com/accepts/-/accepts-1.3.8.tgz",
-      "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "mime-types": "~2.1.34",
-        "negotiator": "0.6.3"
-      },
-      "engines": {
-        "node": ">= 0.6"
-      }
-    },
-    "node_modules/accepts/node_modules/negotiator": {
-      "version": "0.6.3",
-      "resolved": "https://registry.npmmirror.com/negotiator/-/negotiator-0.6.3.tgz",
-      "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">= 0.6"
-      }
-    },
-    "node_modules/acorn": {
-      "version": "8.16.0",
-      "resolved": "https://registry.npmmirror.com/acorn/-/acorn-8.16.0.tgz",
-      "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==",
-      "dev": true,
-      "license": "MIT",
-      "bin": {
-        "acorn": "bin/acorn"
-      },
-      "engines": {
-        "node": ">=0.4.0"
-      }
-    },
-    "node_modules/acorn-import-phases": {
-      "version": "1.0.4",
-      "resolved": "https://registry.npmmirror.com/acorn-import-phases/-/acorn-import-phases-1.0.4.tgz",
-      "integrity": "sha512-wKmbr/DDiIXzEOiWrTTUcDm24kQ2vGfZQvM2fwg2vXqR5uW6aapr7ObPtj1th32b9u90/Pf4AItvdTh42fBmVQ==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=10.13.0"
-      },
-      "peerDependencies": {
-        "acorn": "^8.14.0"
-      }
-    },
-    "node_modules/acorn-jsx": {
-      "version": "5.3.2",
-      "resolved": "https://registry.npmmirror.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz",
-      "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==",
-      "dev": true,
-      "license": "MIT",
-      "peerDependencies": {
-        "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0"
-      }
-    },
-    "node_modules/acorn-walk": {
-      "version": "8.3.5",
-      "resolved": "https://registry.npmmirror.com/acorn-walk/-/acorn-walk-8.3.5.tgz",
-      "integrity": "sha512-HEHNfbars9v4pgpW6SO1KSPkfoS0xVOM/9UzkJltjlsHZmJasxg8aXkuZa7SMf8vKGIBhpUsPluQSqhJFCqebw==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "acorn": "^8.11.0"
-      },
-      "engines": {
-        "node": ">=0.4.0"
-      }
-    },
-    "node_modules/address": {
-      "version": "1.2.2",
-      "resolved": "https://registry.npmmirror.com/address/-/address-1.2.2.tgz",
-      "integrity": "sha512-4B/qKCfeE/ODUaAUpSwfzazo5x29WD4r3vXiWsB7I2mSDAihwEqKO+g8GELZUQSSAo5e1XTYh3ZVfLyxBc12nA==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">= 10.0.0"
-      }
-    },
-    "node_modules/ajv": {
-      "version": "6.14.0",
-      "resolved": "https://registry.npmmirror.com/ajv/-/ajv-6.14.0.tgz",
-      "integrity": "sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "fast-deep-equal": "^3.1.1",
-        "fast-json-stable-stringify": "^2.0.0",
-        "json-schema-traverse": "^0.4.1",
-        "uri-js": "^4.2.2"
-      },
-      "funding": {
-        "type": "github",
-        "url": "https://github.com/sponsors/epoberezkin"
-      }
-    },
-    "node_modules/ajv-formats": {
-      "version": "2.1.1",
-      "resolved": "https://registry.npmmirror.com/ajv-formats/-/ajv-formats-2.1.1.tgz",
-      "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "ajv": "^8.0.0"
-      },
-      "peerDependencies": {
-        "ajv": "^8.0.0"
-      },
-      "peerDependenciesMeta": {
-        "ajv": {
-          "optional": true
-        }
-      }
-    },
-    "node_modules/ajv-formats/node_modules/ajv": {
-      "version": "8.18.0",
-      "resolved": "https://registry.npmmirror.com/ajv/-/ajv-8.18.0.tgz",
-      "integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "fast-deep-equal": "^3.1.3",
-        "fast-uri": "^3.0.1",
-        "json-schema-traverse": "^1.0.0",
-        "require-from-string": "^2.0.2"
-      },
-      "funding": {
-        "type": "github",
-        "url": "https://github.com/sponsors/epoberezkin"
-      }
-    },
-    "node_modules/ajv-formats/node_modules/json-schema-traverse": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmmirror.com/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
-      "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
-      "dev": true,
-      "license": "MIT"
-    },
-    "node_modules/ajv-keywords": {
-      "version": "3.5.2",
-      "resolved": "https://registry.npmmirror.com/ajv-keywords/-/ajv-keywords-3.5.2.tgz",
-      "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==",
-      "dev": true,
-      "license": "MIT",
-      "peerDependencies": {
-        "ajv": "^6.9.1"
-      }
-    },
-    "node_modules/ansi-colors": {
-      "version": "4.1.3",
-      "resolved": "https://registry.npmmirror.com/ansi-colors/-/ansi-colors-4.1.3.tgz",
-      "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=6"
-      }
-    },
-    "node_modules/ansi-escapes": {
-      "version": "3.2.0",
-      "resolved": "https://registry.npmmirror.com/ansi-escapes/-/ansi-escapes-3.2.0.tgz",
-      "integrity": "sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=4"
-      }
-    },
-    "node_modules/ansi-html-community": {
-      "version": "0.0.8",
-      "resolved": "https://registry.npmmirror.com/ansi-html-community/-/ansi-html-community-0.0.8.tgz",
-      "integrity": "sha512-1APHAyr3+PCamwNw3bXCPp4HFLONZt/yIH0sZp0/469KWNTEy+qN5jQ3GVX6DMZ1UXAi34yVwtTeaG/HpBuuzw==",
-      "dev": true,
-      "engines": [
-        "node >= 0.8.0"
-      ],
-      "license": "Apache-2.0",
-      "bin": {
-        "ansi-html": "bin/ansi-html"
-      }
-    },
-    "node_modules/ansi-regex": {
-      "version": "5.0.1",
-      "resolved": "https://registry.npmmirror.com/ansi-regex/-/ansi-regex-5.0.1.tgz",
-      "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=8"
-      }
-    },
-    "node_modules/ansi-styles": {
-      "version": "4.3.0",
-      "resolved": "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-4.3.0.tgz",
-      "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "color-convert": "^2.0.1"
-      },
-      "engines": {
-        "node": ">=8"
-      },
-      "funding": {
-        "url": "https://github.com/chalk/ansi-styles?sponsor=1"
-      }
-    },
-    "node_modules/any-promise": {
-      "version": "1.3.0",
-      "resolved": "https://registry.npmmirror.com/any-promise/-/any-promise-1.3.0.tgz",
-      "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==",
-      "dev": true,
-      "license": "MIT"
-    },
-    "node_modules/anymatch": {
-      "version": "3.1.3",
-      "resolved": "https://registry.npmmirror.com/anymatch/-/anymatch-3.1.3.tgz",
-      "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==",
-      "dev": true,
-      "license": "ISC",
-      "dependencies": {
-        "normalize-path": "^3.0.0",
-        "picomatch": "^2.0.4"
-      },
-      "engines": {
-        "node": ">= 8"
-      }
-    },
-    "node_modules/arch": {
-      "version": "2.2.0",
-      "resolved": "https://registry.npmmirror.com/arch/-/arch-2.2.0.tgz",
-      "integrity": "sha512-Of/R0wqp83cgHozfIYLbBMnej79U/SVGOOyuB3VVFv1NRM/PSFMK12x9KVtiYzJqmnU5WR2qp0Z5rHb7sWGnFQ==",
-      "dev": true,
-      "funding": [
-        {
-          "type": "github",
-          "url": "https://github.com/sponsors/feross"
-        },
-        {
-          "type": "patreon",
-          "url": "https://www.patreon.com/feross"
-        },
-        {
-          "type": "consulting",
-          "url": "https://feross.org/support"
-        }
-      ],
-      "license": "MIT"
-    },
-    "node_modules/argparse": {
-      "version": "1.0.10",
-      "resolved": "https://registry.npmmirror.com/argparse/-/argparse-1.0.10.tgz",
-      "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "sprintf-js": "~1.0.2"
-      }
-    },
-    "node_modules/array-flatten": {
-      "version": "1.1.1",
-      "resolved": "https://registry.npmmirror.com/array-flatten/-/array-flatten-1.1.1.tgz",
-      "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==",
-      "dev": true,
-      "license": "MIT"
-    },
-    "node_modules/array-union": {
-      "version": "2.1.0",
-      "resolved": "https://registry.npmmirror.com/array-union/-/array-union-2.1.0.tgz",
-      "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=8"
-      }
-    },
-    "node_modules/astral-regex": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmmirror.com/astral-regex/-/astral-regex-2.0.0.tgz",
-      "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=8"
-      }
-    },
-    "node_modules/async": {
-      "version": "3.2.6",
-      "resolved": "https://registry.npmmirror.com/async/-/async-3.2.6.tgz",
-      "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==",
-      "dev": true,
-      "license": "MIT"
-    },
-    "node_modules/async-validator": {
-      "version": "1.8.5",
-      "resolved": "https://registry.npmmirror.com/async-validator/-/async-validator-1.8.5.tgz",
-      "integrity": "sha512-tXBM+1m056MAX0E8TL2iCjg8WvSyXu0Zc8LNtYqrVeyoL3+esHRZ4SieE9fKQyyU09uONjnMEjrNBMqT0mbvmA==",
-      "dependencies": {
-        "babel-runtime": "6.x"
-      }
-    },
-    "node_modules/asynckit": {
-      "version": "0.4.0",
-      "resolved": "https://registry.npmmirror.com/asynckit/-/asynckit-0.4.0.tgz",
-      "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==",
-      "license": "MIT"
-    },
-    "node_modules/at-least-node": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmmirror.com/at-least-node/-/at-least-node-1.0.0.tgz",
-      "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==",
-      "dev": true,
-      "license": "ISC",
-      "engines": {
-        "node": ">= 4.0.0"
-      }
-    },
-    "node_modules/autoprefixer": {
-      "version": "10.5.0",
-      "resolved": "https://registry.npmmirror.com/autoprefixer/-/autoprefixer-10.5.0.tgz",
-      "integrity": "sha512-FMhOoZV4+qR6aTUALKX2rEqGG+oyATvwBt9IIzVR5rMa2HRWPkxf+P+PAJLD1I/H5/II+HuZcBJYEFBpq39ong==",
-      "dev": true,
-      "funding": [
-        {
-          "type": "opencollective",
-          "url": "https://opencollective.com/postcss/"
-        },
-        {
-          "type": "tidelift",
-          "url": "https://tidelift.com/funding/github/npm/autoprefixer"
-        },
-        {
-          "type": "github",
-          "url": "https://github.com/sponsors/ai"
-        }
-      ],
-      "license": "MIT",
-      "dependencies": {
-        "browserslist": "^4.28.2",
-        "caniuse-lite": "^1.0.30001787",
-        "fraction.js": "^5.3.4",
-        "picocolors": "^1.1.1",
-        "postcss-value-parser": "^4.2.0"
-      },
-      "bin": {
-        "autoprefixer": "bin/autoprefixer"
-      },
-      "engines": {
-        "node": "^10 || ^12 || >=14"
-      },
-      "peerDependencies": {
-        "postcss": "^8.1.0"
-      }
-    },
-    "node_modules/axios": {
-      "version": "1.15.0",
-      "resolved": "https://registry.npmmirror.com/axios/-/axios-1.15.0.tgz",
-      "integrity": "sha512-wWyJDlAatxk30ZJer+GeCWS209sA42X+N5jU2jy6oHTp7ufw8uzUTVFBX9+wTfAlhiJXGS0Bq7X6efruWjuK9Q==",
-      "license": "MIT",
-      "dependencies": {
-        "follow-redirects": "^1.15.11",
-        "form-data": "^4.0.5",
-        "proxy-from-env": "^2.1.0"
-      }
-    },
-    "node_modules/babel-helper-vue-jsx-merge-props": {
-      "version": "2.0.3",
-      "resolved": "https://registry.npmmirror.com/babel-helper-vue-jsx-merge-props/-/babel-helper-vue-jsx-merge-props-2.0.3.tgz",
-      "integrity": "sha512-gsLiKK7Qrb7zYJNgiXKpXblxbV5ffSwR0f5whkPAaBAR4fhi6bwRZxX9wBlIc5M/v8CCkXUbXZL4N/nSE97cqg==",
-      "license": "MIT"
-    },
-    "node_modules/babel-loader": {
-      "version": "8.4.1",
-      "resolved": "https://registry.npmmirror.com/babel-loader/-/babel-loader-8.4.1.tgz",
-      "integrity": "sha512-nXzRChX+Z1GoE6yWavBQg6jDslyFF3SDjl2paADuoQtQW10JqShJt62R6eJQ5m/pjJFDT8xgKIWSP85OY8eXeA==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "find-cache-dir": "^3.3.1",
-        "loader-utils": "^2.0.4",
-        "make-dir": "^3.1.0",
-        "schema-utils": "^2.6.5"
-      },
-      "engines": {
-        "node": ">= 8.9"
-      },
-      "peerDependencies": {
-        "@babel/core": "^7.0.0",
-        "webpack": ">=2"
-      }
-    },
-    "node_modules/babel-loader/node_modules/loader-utils": {
-      "version": "2.0.4",
-      "resolved": "https://registry.npmmirror.com/loader-utils/-/loader-utils-2.0.4.tgz",
-      "integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "big.js": "^5.2.2",
-        "emojis-list": "^3.0.0",
-        "json5": "^2.1.2"
-      },
-      "engines": {
-        "node": ">=8.9.0"
-      }
-    },
-    "node_modules/babel-plugin-dynamic-import-node": {
-      "version": "2.3.3",
-      "resolved": "https://registry.npmmirror.com/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.3.tgz",
-      "integrity": "sha512-jZVI+s9Zg3IqA/kdi0i6UDCybUI3aSBLnglhYbSSjKlV7yF1F/5LWv8MakQmvYpnbJDS6fcBL2KzHSxNCMtWSQ==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "object.assign": "^4.1.0"
-      }
-    },
-    "node_modules/babel-plugin-polyfill-corejs2": {
-      "version": "0.4.17",
-      "resolved": "https://registry.npmmirror.com/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.17.tgz",
-      "integrity": "sha512-aTyf30K/rqAsNwN76zYrdtx8obu0E4KoUME29B1xj+B3WxgvWkp943vYQ+z8Mv3lw9xHXMHpvSPOBxzAkIa94w==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@babel/compat-data": "^7.28.6",
-        "@babel/helper-define-polyfill-provider": "^0.6.8",
-        "semver": "^6.3.1"
-      },
-      "peerDependencies": {
-        "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0"
-      }
-    },
-    "node_modules/babel-plugin-polyfill-corejs3": {
-      "version": "0.13.0",
-      "resolved": "https://registry.npmmirror.com/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.13.0.tgz",
-      "integrity": "sha512-U+GNwMdSFgzVmfhNm8GJUX88AadB3uo9KpJqS3FaqNIPKgySuvMb+bHPsOmmuWyIcuqZj/pzt1RUIUZns4y2+A==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@babel/helper-define-polyfill-provider": "^0.6.5",
-        "core-js-compat": "^3.43.0"
-      },
-      "peerDependencies": {
-        "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0"
-      }
-    },
-    "node_modules/babel-plugin-polyfill-regenerator": {
-      "version": "0.6.8",
-      "resolved": "https://registry.npmmirror.com/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.6.8.tgz",
-      "integrity": "sha512-M762rNHfSF1EV3SLtnCJXFoQbbIIz0OyRwnCmV0KPC7qosSfCO0QLTSuJX3ayAebubhE6oYBAYPrBA5ljowaZg==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@babel/helper-define-polyfill-provider": "^0.6.8"
-      },
-      "peerDependencies": {
-        "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0"
-      }
-    },
-    "node_modules/babel-runtime": {
-      "version": "6.26.0",
-      "resolved": "https://registry.npmmirror.com/babel-runtime/-/babel-runtime-6.26.0.tgz",
-      "integrity": "sha512-ITKNuq2wKlW1fJg9sSW52eepoYgZBggvOAHC0u/CYu/qxQ9EVzThCgR69BnSXLHjy2f7SY5zaQ4yt7H9ZVxY2g==",
-      "license": "MIT",
-      "dependencies": {
-        "core-js": "^2.4.0",
-        "regenerator-runtime": "^0.11.0"
-      }
-    },
-    "node_modules/babel-runtime/node_modules/core-js": {
-      "version": "2.6.12",
-      "resolved": "https://registry.npmmirror.com/core-js/-/core-js-2.6.12.tgz",
-      "integrity": "sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ==",
-      "deprecated": "core-js@<3.23.3 is no longer maintained and not recommended for usage due to the number of issues. Because of the V8 engine whims, feature detection in old core-js versions could cause a slowdown up to 100x even if nothing is polyfilled. Some versions have web compatibility issues. Please, upgrade your dependencies to the actual version of core-js.",
-      "hasInstallScript": true,
-      "license": "MIT"
-    },
-    "node_modules/balanced-match": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmmirror.com/balanced-match/-/balanced-match-1.0.2.tgz",
-      "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
-      "dev": true,
-      "license": "MIT"
-    },
-    "node_modules/base64-js": {
-      "version": "1.5.1",
-      "resolved": "https://registry.npmmirror.com/base64-js/-/base64-js-1.5.1.tgz",
-      "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==",
-      "dev": true,
-      "funding": [
-        {
-          "type": "github",
-          "url": "https://github.com/sponsors/feross"
-        },
-        {
-          "type": "patreon",
-          "url": "https://www.patreon.com/feross"
-        },
-        {
-          "type": "consulting",
-          "url": "https://feross.org/support"
-        }
-      ],
-      "license": "MIT"
-    },
-    "node_modules/baseline-browser-mapping": {
-      "version": "2.10.19",
-      "resolved": "https://registry.npmmirror.com/baseline-browser-mapping/-/baseline-browser-mapping-2.10.19.tgz",
-      "integrity": "sha512-qCkNLi2sfBOn8XhZQ0FXsT1Ki/Yo5P90hrkRamVFRS7/KV9hpfA4HkoWNU152+8w0zPjnxo5psx5NL3PSGgv5g==",
-      "dev": true,
-      "license": "Apache-2.0",
-      "bin": {
-        "baseline-browser-mapping": "dist/cli.cjs"
-      },
-      "engines": {
-        "node": ">=6.0.0"
-      }
-    },
-    "node_modules/batch": {
-      "version": "0.6.1",
-      "resolved": "https://registry.npmmirror.com/batch/-/batch-0.6.1.tgz",
-      "integrity": "sha512-x+VAiMRL6UPkx+kudNvxTl6hB2XNNCG2r+7wixVfIYwu/2HKRXimwQyaumLjMveWvT2Hkd/cAJw+QBMfJ/EKVw==",
-      "dev": true,
-      "license": "MIT"
-    },
-    "node_modules/big.js": {
-      "version": "5.2.2",
-      "resolved": "https://registry.npmmirror.com/big.js/-/big.js-5.2.2.tgz",
-      "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": "*"
-      }
-    },
-    "node_modules/binary-extensions": {
-      "version": "2.3.0",
-      "resolved": "https://registry.npmmirror.com/binary-extensions/-/binary-extensions-2.3.0.tgz",
-      "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=8"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/sindresorhus"
-      }
-    },
-    "node_modules/bl": {
-      "version": "4.1.0",
-      "resolved": "https://registry.npmmirror.com/bl/-/bl-4.1.0.tgz",
-      "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "buffer": "^5.5.0",
-        "inherits": "^2.0.4",
-        "readable-stream": "^3.4.0"
-      }
-    },
-    "node_modules/bluebird": {
-      "version": "3.7.2",
-      "resolved": "https://registry.npmmirror.com/bluebird/-/bluebird-3.7.2.tgz",
-      "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==",
-      "dev": true,
-      "license": "MIT"
-    },
-    "node_modules/body-parser": {
-      "version": "1.20.4",
-      "resolved": "https://registry.npmmirror.com/body-parser/-/body-parser-1.20.4.tgz",
-      "integrity": "sha512-ZTgYYLMOXY9qKU/57FAo8F+HA2dGX7bqGc71txDRC1rS4frdFI5R7NhluHxH6M0YItAP0sHB4uqAOcYKxO6uGA==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "bytes": "~3.1.2",
-        "content-type": "~1.0.5",
-        "debug": "2.6.9",
-        "depd": "2.0.0",
-        "destroy": "~1.2.0",
-        "http-errors": "~2.0.1",
-        "iconv-lite": "~0.4.24",
-        "on-finished": "~2.4.1",
-        "qs": "~6.14.0",
-        "raw-body": "~2.5.3",
-        "type-is": "~1.6.18",
-        "unpipe": "~1.0.0"
-      },
-      "engines": {
-        "node": ">= 0.8",
-        "npm": "1.2.8000 || >= 1.4.16"
-      }
-    },
-    "node_modules/body-parser/node_modules/debug": {
-      "version": "2.6.9",
-      "resolved": "https://registry.npmmirror.com/debug/-/debug-2.6.9.tgz",
-      "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "ms": "2.0.0"
-      }
-    },
-    "node_modules/body-parser/node_modules/ms": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmmirror.com/ms/-/ms-2.0.0.tgz",
-      "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
-      "dev": true,
-      "license": "MIT"
-    },
-    "node_modules/bonjour-service": {
-      "version": "1.3.0",
-      "resolved": "https://registry.npmmirror.com/bonjour-service/-/bonjour-service-1.3.0.tgz",
-      "integrity": "sha512-3YuAUiSkWykd+2Azjgyxei8OWf8thdn8AITIog2M4UICzoqfjlqr64WIjEXZllf/W6vK1goqleSR6brGomxQqA==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "fast-deep-equal": "^3.1.3",
-        "multicast-dns": "^7.2.5"
-      }
-    },
-    "node_modules/boolbase": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmmirror.com/boolbase/-/boolbase-1.0.0.tgz",
-      "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==",
-      "dev": true,
-      "license": "ISC"
-    },
-    "node_modules/brace-expansion": {
-      "version": "1.1.14",
-      "resolved": "https://registry.npmmirror.com/brace-expansion/-/brace-expansion-1.1.14.tgz",
-      "integrity": "sha512-MWPGfDxnyzKU7rNOW9SP/c50vi3xrmrua/+6hfPbCS2ABNWfx24vPidzvC7krjU/RTo235sV776ymlsMtGKj8g==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "balanced-match": "^1.0.0",
-        "concat-map": "0.0.1"
-      }
-    },
-    "node_modules/braces": {
-      "version": "3.0.3",
-      "resolved": "https://registry.npmmirror.com/braces/-/braces-3.0.3.tgz",
-      "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "fill-range": "^7.1.1"
-      },
-      "engines": {
-        "node": ">=8"
-      }
-    },
-    "node_modules/browserslist": {
-      "version": "4.28.2",
-      "resolved": "https://registry.npmmirror.com/browserslist/-/browserslist-4.28.2.tgz",
-      "integrity": "sha512-48xSriZYYg+8qXna9kwqjIVzuQxi+KYWp2+5nCYnYKPTr0LvD89Jqk2Or5ogxz0NUMfIjhh2lIUX/LyX9B4oIg==",
-      "dev": true,
-      "funding": [
-        {
-          "type": "opencollective",
-          "url": "https://opencollective.com/browserslist"
-        },
-        {
-          "type": "tidelift",
-          "url": "https://tidelift.com/funding/github/npm/browserslist"
-        },
-        {
-          "type": "github",
-          "url": "https://github.com/sponsors/ai"
-        }
-      ],
-      "license": "MIT",
-      "dependencies": {
-        "baseline-browser-mapping": "^2.10.12",
-        "caniuse-lite": "^1.0.30001782",
-        "electron-to-chromium": "^1.5.328",
-        "node-releases": "^2.0.36",
-        "update-browserslist-db": "^1.2.3"
-      },
-      "bin": {
-        "browserslist": "cli.js"
-      },
-      "engines": {
-        "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
-      }
-    },
-    "node_modules/buffer": {
-      "version": "5.7.1",
-      "resolved": "https://registry.npmmirror.com/buffer/-/buffer-5.7.1.tgz",
-      "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==",
-      "dev": true,
-      "funding": [
-        {
-          "type": "github",
-          "url": "https://github.com/sponsors/feross"
-        },
-        {
-          "type": "patreon",
-          "url": "https://www.patreon.com/feross"
-        },
-        {
-          "type": "consulting",
-          "url": "https://feross.org/support"
-        }
-      ],
-      "license": "MIT",
-      "dependencies": {
-        "base64-js": "^1.3.1",
-        "ieee754": "^1.1.13"
-      }
-    },
-    "node_modules/buffer-from": {
-      "version": "1.1.2",
-      "resolved": "https://registry.npmmirror.com/buffer-from/-/buffer-from-1.1.2.tgz",
-      "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==",
-      "dev": true,
-      "license": "MIT"
-    },
-    "node_modules/bytes": {
-      "version": "3.1.2",
-      "resolved": "https://registry.npmmirror.com/bytes/-/bytes-3.1.2.tgz",
-      "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">= 0.8"
-      }
-    },
-    "node_modules/call-bind": {
-      "version": "1.0.9",
-      "resolved": "https://registry.npmmirror.com/call-bind/-/call-bind-1.0.9.tgz",
-      "integrity": "sha512-a/hy+pNsFUTR+Iz8TCJvXudKVLAnz/DyeSUo10I5yvFDQJBFU2s9uqQpoSrJlroHUKoKqzg+epxyP9lqFdzfBQ==",
-      "license": "MIT",
-      "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"
-      },
-      "engines": {
-        "node": ">= 0.4"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
-    "node_modules/call-bind-apply-helpers": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmmirror.com/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz",
-      "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==",
-      "license": "MIT",
-      "dependencies": {
-        "es-errors": "^1.3.0",
-        "function-bind": "^1.1.2"
-      },
-      "engines": {
-        "node": ">= 0.4"
-      }
-    },
-    "node_modules/call-bound": {
-      "version": "1.0.4",
-      "resolved": "https://registry.npmmirror.com/call-bound/-/call-bound-1.0.4.tgz",
-      "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==",
-      "license": "MIT",
-      "dependencies": {
-        "call-bind-apply-helpers": "^1.0.2",
-        "get-intrinsic": "^1.3.0"
-      },
-      "engines": {
-        "node": ">= 0.4"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
-    "node_modules/callsites": {
-      "version": "3.1.0",
-      "resolved": "https://registry.npmmirror.com/callsites/-/callsites-3.1.0.tgz",
-      "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=6"
-      }
-    },
-    "node_modules/camel-case": {
-      "version": "4.1.2",
-      "resolved": "https://registry.npmmirror.com/camel-case/-/camel-case-4.1.2.tgz",
-      "integrity": "sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "pascal-case": "^3.1.2",
-        "tslib": "^2.0.3"
-      }
-    },
-    "node_modules/camelcase": {
-      "version": "5.3.1",
-      "resolved": "https://registry.npmmirror.com/camelcase/-/camelcase-5.3.1.tgz",
-      "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=6"
-      }
-    },
-    "node_modules/caniuse-api": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmmirror.com/caniuse-api/-/caniuse-api-3.0.0.tgz",
-      "integrity": "sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "browserslist": "^4.0.0",
-        "caniuse-lite": "^1.0.0",
-        "lodash.memoize": "^4.1.2",
-        "lodash.uniq": "^4.5.0"
-      }
-    },
-    "node_modules/caniuse-lite": {
-      "version": "1.0.30001788",
-      "resolved": "https://registry.npmmirror.com/caniuse-lite/-/caniuse-lite-1.0.30001788.tgz",
-      "integrity": "sha512-6q8HFp+lOQtcf7wBK+uEenxymVWkGKkjFpCvw5W25cmMwEDU45p1xQFBQv8JDlMMry7eNxyBaR+qxgmTUZkIRQ==",
-      "dev": true,
-      "funding": [
-        {
-          "type": "opencollective",
-          "url": "https://opencollective.com/browserslist"
-        },
-        {
-          "type": "tidelift",
-          "url": "https://tidelift.com/funding/github/npm/caniuse-lite"
-        },
-        {
-          "type": "github",
-          "url": "https://github.com/sponsors/ai"
-        }
-      ],
-      "license": "CC-BY-4.0"
-    },
-    "node_modules/case-sensitive-paths-webpack-plugin": {
-      "version": "2.4.0",
-      "resolved": "https://registry.npmmirror.com/case-sensitive-paths-webpack-plugin/-/case-sensitive-paths-webpack-plugin-2.4.0.tgz",
-      "integrity": "sha512-roIFONhcxog0JSSWbvVAh3OocukmSgpqOH6YpMkCvav/ySIV3JKg4Dc8vYtQjYi/UxpNE36r/9v+VqTQqgkYmw==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=4"
-      }
-    },
-    "node_modules/chalk": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmmirror.com/chalk/-/chalk-3.0.0.tgz",
-      "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "ansi-styles": "^4.1.0",
-        "supports-color": "^7.1.0"
-      },
-      "engines": {
-        "node": ">=8"
-      }
-    },
-    "node_modules/chokidar": {
-      "version": "3.6.0",
-      "resolved": "https://registry.npmmirror.com/chokidar/-/chokidar-3.6.0.tgz",
-      "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "anymatch": "~3.1.2",
-        "braces": "~3.0.2",
-        "glob-parent": "~5.1.2",
-        "is-binary-path": "~2.1.0",
-        "is-glob": "~4.0.1",
-        "normalize-path": "~3.0.0",
-        "readdirp": "~3.6.0"
-      },
-      "engines": {
-        "node": ">= 8.10.0"
-      },
-      "funding": {
-        "url": "https://paulmillr.com/funding/"
-      },
-      "optionalDependencies": {
-        "fsevents": "~2.3.2"
-      }
-    },
-    "node_modules/chokidar/node_modules/glob-parent": {
-      "version": "5.1.2",
-      "resolved": "https://registry.npmmirror.com/glob-parent/-/glob-parent-5.1.2.tgz",
-      "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
-      "dev": true,
-      "license": "ISC",
-      "dependencies": {
-        "is-glob": "^4.0.1"
-      },
-      "engines": {
-        "node": ">= 6"
-      }
-    },
-    "node_modules/chrome-trace-event": {
-      "version": "1.0.4",
-      "resolved": "https://registry.npmmirror.com/chrome-trace-event/-/chrome-trace-event-1.0.4.tgz",
-      "integrity": "sha512-rNjApaLzuwaOTjCiT8lSDdGN1APCiqkChLMJxJPWLunPAt5fy8xgU9/jNOchV84wfIxrA0lRQB7oCT8jrn/wrQ==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=6.0"
-      }
-    },
-    "node_modules/ci-info": {
-      "version": "1.6.0",
-      "resolved": "https://registry.npmmirror.com/ci-info/-/ci-info-1.6.0.tgz",
-      "integrity": "sha512-vsGdkwSCDpWmP80ncATX7iea5DWQemg1UgCW5J8tqjU3lYw4FBYuj89J0CTVomA7BEfvSZd84GmHko+MxFQU2A==",
-      "dev": true,
-      "license": "MIT"
-    },
-    "node_modules/clean-css": {
-      "version": "5.3.3",
-      "resolved": "https://registry.npmmirror.com/clean-css/-/clean-css-5.3.3.tgz",
-      "integrity": "sha512-D5J+kHaVb/wKSFcyyV75uCn8fiY4sV38XJoe4CUyGQ+mOU/fMVYUdH1hJC+CJQ5uY3EnW27SbJYS4X8BiLrAFg==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "source-map": "~0.6.0"
-      },
-      "engines": {
-        "node": ">= 10.0"
-      }
-    },
-    "node_modules/cli-cursor": {
-      "version": "3.1.0",
-      "resolved": "https://registry.npmmirror.com/cli-cursor/-/cli-cursor-3.1.0.tgz",
-      "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "restore-cursor": "^3.1.0"
-      },
-      "engines": {
-        "node": ">=8"
-      }
-    },
-    "node_modules/cli-highlight": {
-      "version": "2.1.11",
-      "resolved": "https://registry.npmmirror.com/cli-highlight/-/cli-highlight-2.1.11.tgz",
-      "integrity": "sha512-9KDcoEVwyUXrjcJNvHD0NFc/hiwe/WPVYIleQh2O1N2Zro5gWJZ/K+3DGn8w8P/F6FxOgzyC5bxDyHIgCSPhGg==",
-      "dev": true,
-      "license": "ISC",
-      "dependencies": {
-        "chalk": "^4.0.0",
-        "highlight.js": "^10.7.1",
-        "mz": "^2.4.0",
-        "parse5": "^5.1.1",
-        "parse5-htmlparser2-tree-adapter": "^6.0.0",
-        "yargs": "^16.0.0"
-      },
-      "bin": {
-        "highlight": "bin/highlight"
-      },
-      "engines": {
-        "node": ">=8.0.0",
-        "npm": ">=5.0.0"
-      }
-    },
-    "node_modules/cli-highlight/node_modules/chalk": {
-      "version": "4.1.2",
-      "resolved": "https://registry.npmmirror.com/chalk/-/chalk-4.1.2.tgz",
-      "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "ansi-styles": "^4.1.0",
-        "supports-color": "^7.1.0"
-      },
-      "engines": {
-        "node": ">=10"
-      },
-      "funding": {
-        "url": "https://github.com/chalk/chalk?sponsor=1"
-      }
-    },
-    "node_modules/cli-spinners": {
-      "version": "2.9.2",
-      "resolved": "https://registry.npmmirror.com/cli-spinners/-/cli-spinners-2.9.2.tgz",
-      "integrity": "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=6"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/sindresorhus"
-      }
-    },
-    "node_modules/clipboardy": {
-      "version": "2.3.0",
-      "resolved": "https://registry.npmmirror.com/clipboardy/-/clipboardy-2.3.0.tgz",
-      "integrity": "sha512-mKhiIL2DrQIsuXMgBgnfEHOZOryC7kY7YO//TN6c63wlEm3NG5tz+YgY5rVi29KCmq/QQjKYvM7a19+MDOTHOQ==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "arch": "^2.1.1",
-        "execa": "^1.0.0",
-        "is-wsl": "^2.1.1"
-      },
-      "engines": {
-        "node": ">=8"
-      }
-    },
-    "node_modules/cliui": {
-      "version": "7.0.4",
-      "resolved": "https://registry.npmmirror.com/cliui/-/cliui-7.0.4.tgz",
-      "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==",
-      "dev": true,
-      "license": "ISC",
-      "dependencies": {
-        "string-width": "^4.2.0",
-        "strip-ansi": "^6.0.0",
-        "wrap-ansi": "^7.0.0"
-      }
-    },
-    "node_modules/clone": {
-      "version": "1.0.4",
-      "resolved": "https://registry.npmmirror.com/clone/-/clone-1.0.4.tgz",
-      "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=0.8"
-      }
-    },
-    "node_modules/clone-deep": {
-      "version": "4.0.1",
-      "resolved": "https://registry.npmmirror.com/clone-deep/-/clone-deep-4.0.1.tgz",
-      "integrity": "sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "is-plain-object": "^2.0.4",
-        "kind-of": "^6.0.2",
-        "shallow-clone": "^3.0.0"
-      },
-      "engines": {
-        "node": ">=6"
-      }
-    },
-    "node_modules/color-convert": {
-      "version": "2.0.1",
-      "resolved": "https://registry.npmmirror.com/color-convert/-/color-convert-2.0.1.tgz",
-      "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "color-name": "~1.1.4"
-      },
-      "engines": {
-        "node": ">=7.0.0"
-      }
-    },
-    "node_modules/color-name": {
-      "version": "1.1.4",
-      "resolved": "https://registry.npmmirror.com/color-name/-/color-name-1.1.4.tgz",
-      "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
-      "dev": true,
-      "license": "MIT"
-    },
-    "node_modules/colord": {
-      "version": "2.9.3",
-      "resolved": "https://registry.npmmirror.com/colord/-/colord-2.9.3.tgz",
-      "integrity": "sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw==",
-      "dev": true,
-      "license": "MIT"
-    },
-    "node_modules/colorette": {
-      "version": "2.0.20",
-      "resolved": "https://registry.npmmirror.com/colorette/-/colorette-2.0.20.tgz",
-      "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==",
-      "dev": true,
-      "license": "MIT"
-    },
-    "node_modules/combined-stream": {
-      "version": "1.0.8",
-      "resolved": "https://registry.npmmirror.com/combined-stream/-/combined-stream-1.0.8.tgz",
-      "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
-      "license": "MIT",
-      "dependencies": {
-        "delayed-stream": "~1.0.0"
-      },
-      "engines": {
-        "node": ">= 0.8"
-      }
-    },
-    "node_modules/commander": {
-      "version": "8.3.0",
-      "resolved": "https://registry.npmmirror.com/commander/-/commander-8.3.0.tgz",
-      "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==",
-      "license": "MIT",
-      "engines": {
-        "node": ">= 12"
-      }
-    },
-    "node_modules/commondir": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmmirror.com/commondir/-/commondir-1.0.1.tgz",
-      "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==",
-      "dev": true,
-      "license": "MIT"
-    },
-    "node_modules/compressible": {
-      "version": "2.0.18",
-      "resolved": "https://registry.npmmirror.com/compressible/-/compressible-2.0.18.tgz",
-      "integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "mime-db": ">= 1.43.0 < 2"
-      },
-      "engines": {
-        "node": ">= 0.6"
-      }
-    },
-    "node_modules/compression": {
-      "version": "1.8.1",
-      "resolved": "https://registry.npmmirror.com/compression/-/compression-1.8.1.tgz",
-      "integrity": "sha512-9mAqGPHLakhCLeNyxPkK4xVo746zQ/czLH1Ky+vkitMnWfWZps8r0qXuwhwizagCRttsL4lfG4pIOvaWLpAP0w==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "bytes": "3.1.2",
-        "compressible": "~2.0.18",
-        "debug": "2.6.9",
-        "negotiator": "~0.6.4",
-        "on-headers": "~1.1.0",
-        "safe-buffer": "5.2.1",
-        "vary": "~1.1.2"
-      },
-      "engines": {
-        "node": ">= 0.8.0"
-      }
-    },
-    "node_modules/compression/node_modules/debug": {
-      "version": "2.6.9",
-      "resolved": "https://registry.npmmirror.com/debug/-/debug-2.6.9.tgz",
-      "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "ms": "2.0.0"
-      }
-    },
-    "node_modules/compression/node_modules/ms": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmmirror.com/ms/-/ms-2.0.0.tgz",
-      "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
-      "dev": true,
-      "license": "MIT"
-    },
-    "node_modules/concat-map": {
-      "version": "0.0.1",
-      "resolved": "https://registry.npmmirror.com/concat-map/-/concat-map-0.0.1.tgz",
-      "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
-      "dev": true,
-      "license": "MIT"
-    },
-    "node_modules/connect-history-api-fallback": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmmirror.com/connect-history-api-fallback/-/connect-history-api-fallback-2.0.0.tgz",
-      "integrity": "sha512-U73+6lQFmfiNPrYbXqr6kZ1i1wiRqXnp2nhMsINseWXO8lDau0LGEffJ8kQi4EjLZympVgRdvqjAgiZ1tgzDDA==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=0.8"
-      }
-    },
-    "node_modules/consolidate": {
-      "version": "0.15.1",
-      "resolved": "https://registry.npmmirror.com/consolidate/-/consolidate-0.15.1.tgz",
-      "integrity": "sha512-DW46nrsMJgy9kqAbPt5rKaCr7uFtpo4mSUvLHIUbJEjm0vo+aY5QLwBUq3FK4tRnJr/X0Psc0C4jf/h+HtXSMw==",
-      "deprecated": "Please upgrade to consolidate v1.0.0+ as it has been modernized with several long-awaited fixes implemented. Maintenance is supported by Forward Email at https://forwardemail.net ; follow/watch https://github.com/ladjs/consolidate for updates and release changelog",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "bluebird": "^3.1.1"
-      },
-      "engines": {
-        "node": ">= 0.10.0"
-      }
-    },
-    "node_modules/content-disposition": {
-      "version": "0.5.4",
-      "resolved": "https://registry.npmmirror.com/content-disposition/-/content-disposition-0.5.4.tgz",
-      "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "safe-buffer": "5.2.1"
-      },
-      "engines": {
-        "node": ">= 0.6"
-      }
-    },
-    "node_modules/content-type": {
-      "version": "1.0.5",
-      "resolved": "https://registry.npmmirror.com/content-type/-/content-type-1.0.5.tgz",
-      "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">= 0.6"
-      }
-    },
-    "node_modules/convert-source-map": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmmirror.com/convert-source-map/-/convert-source-map-2.0.0.tgz",
-      "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==",
-      "dev": true,
-      "license": "MIT"
-    },
-    "node_modules/cookie": {
-      "version": "0.7.2",
-      "resolved": "https://registry.npmmirror.com/cookie/-/cookie-0.7.2.tgz",
-      "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">= 0.6"
-      }
-    },
-    "node_modules/cookie-signature": {
-      "version": "1.0.7",
-      "resolved": "https://registry.npmmirror.com/cookie-signature/-/cookie-signature-1.0.7.tgz",
-      "integrity": "sha512-NXdYc3dLr47pBkpUCHtKSwIOQXLVn8dZEuywboCOJY/osA0wFSLlSawr3KN8qXJEyX66FcONTH8EIlVuK0yyFA==",
-      "dev": true,
-      "license": "MIT"
-    },
-    "node_modules/copy-webpack-plugin": {
-      "version": "9.1.0",
-      "resolved": "https://registry.npmmirror.com/copy-webpack-plugin/-/copy-webpack-plugin-9.1.0.tgz",
-      "integrity": "sha512-rxnR7PaGigJzhqETHGmAcxKnLZSR5u1Y3/bcIv/1FnqXedcL/E2ewK7ZCNrArJKCiSv8yVXhTqetJh8inDvfsA==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "fast-glob": "^3.2.7",
-        "glob-parent": "^6.0.1",
-        "globby": "^11.0.3",
-        "normalize-path": "^3.0.0",
-        "schema-utils": "^3.1.1",
-        "serialize-javascript": "^6.0.0"
-      },
-      "engines": {
-        "node": ">= 12.13.0"
-      },
-      "funding": {
-        "type": "opencollective",
-        "url": "https://opencollective.com/webpack"
-      },
-      "peerDependencies": {
-        "webpack": "^5.1.0"
-      }
-    },
-    "node_modules/copy-webpack-plugin/node_modules/schema-utils": {
-      "version": "3.3.0",
-      "resolved": "https://registry.npmmirror.com/schema-utils/-/schema-utils-3.3.0.tgz",
-      "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@types/json-schema": "^7.0.8",
-        "ajv": "^6.12.5",
-        "ajv-keywords": "^3.5.2"
-      },
-      "engines": {
-        "node": ">= 10.13.0"
-      },
-      "funding": {
-        "type": "opencollective",
-        "url": "https://opencollective.com/webpack"
-      }
-    },
-    "node_modules/core-js": {
-      "version": "3.49.0",
-      "resolved": "https://registry.npmmirror.com/core-js/-/core-js-3.49.0.tgz",
-      "integrity": "sha512-es1U2+YTtzpwkxVLwAFdSpaIMyQaq0PBgm3YD1W3Qpsn1NAmO3KSgZfu+oGSWVu6NvLHoHCV/aYcsE5wiB7ALg==",
-      "hasInstallScript": true,
-      "license": "MIT",
-      "funding": {
-        "type": "opencollective",
-        "url": "https://opencollective.com/core-js"
-      }
-    },
-    "node_modules/core-js-compat": {
-      "version": "3.49.0",
-      "resolved": "https://registry.npmmirror.com/core-js-compat/-/core-js-compat-3.49.0.tgz",
-      "integrity": "sha512-VQXt1jr9cBz03b331DFDCCP90b3fanciLkgiOoy8SBHy06gNf+vQ1A3WFLqG7I8TipYIKeYK9wxd0tUrvHcOZA==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "browserslist": "^4.28.1"
-      },
-      "funding": {
-        "type": "opencollective",
-        "url": "https://opencollective.com/core-js"
-      }
-    },
-    "node_modules/core-util-is": {
-      "version": "1.0.3",
-      "resolved": "https://registry.npmmirror.com/core-util-is/-/core-util-is-1.0.3.tgz",
-      "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==",
-      "dev": true,
-      "license": "MIT"
-    },
-    "node_modules/cosmiconfig": {
-      "version": "7.1.0",
-      "resolved": "https://registry.npmmirror.com/cosmiconfig/-/cosmiconfig-7.1.0.tgz",
-      "integrity": "sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@types/parse-json": "^4.0.0",
-        "import-fresh": "^3.2.1",
-        "parse-json": "^5.0.0",
-        "path-type": "^4.0.0",
-        "yaml": "^1.10.0"
-      },
-      "engines": {
-        "node": ">=10"
-      }
-    },
-    "node_modules/cross-spawn": {
-      "version": "6.0.6",
-      "resolved": "https://registry.npmmirror.com/cross-spawn/-/cross-spawn-6.0.6.tgz",
-      "integrity": "sha512-VqCUuhcd1iB+dsv8gxPttb5iZh/D0iubSP21g36KXdEuf6I5JiioesUVjpCdHV9MZRUfVFlvwtIUyPfxo5trtw==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "nice-try": "^1.0.4",
-        "path-key": "^2.0.1",
-        "semver": "^5.5.0",
-        "shebang-command": "^1.2.0",
-        "which": "^1.2.9"
-      },
-      "engines": {
-        "node": ">=4.8"
-      }
-    },
-    "node_modules/cross-spawn/node_modules/semver": {
-      "version": "5.7.2",
-      "resolved": "https://registry.npmmirror.com/semver/-/semver-5.7.2.tgz",
-      "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==",
-      "dev": true,
-      "license": "ISC",
-      "bin": {
-        "semver": "bin/semver"
-      }
-    },
-    "node_modules/css-declaration-sorter": {
-      "version": "6.4.1",
-      "resolved": "https://registry.npmmirror.com/css-declaration-sorter/-/css-declaration-sorter-6.4.1.tgz",
-      "integrity": "sha512-rtdthzxKuyq6IzqX6jEcIzQF/YqccluefyCYheovBOLhFT/drQA9zj/UbRAa9J7C0o6EG6u3E6g+vKkay7/k3g==",
-      "dev": true,
-      "license": "ISC",
-      "engines": {
-        "node": "^10 || ^12 || >=14"
-      },
-      "peerDependencies": {
-        "postcss": "^8.0.9"
-      }
-    },
-    "node_modules/css-loader": {
-      "version": "6.11.0",
-      "resolved": "https://registry.npmmirror.com/css-loader/-/css-loader-6.11.0.tgz",
-      "integrity": "sha512-CTJ+AEQJjq5NzLga5pE39qdiSV56F8ywCIsqNIRF0r7BDgWsN25aazToqAFg7ZrtA/U016xudB3ffgweORxX7g==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "icss-utils": "^5.1.0",
-        "postcss": "^8.4.33",
-        "postcss-modules-extract-imports": "^3.1.0",
-        "postcss-modules-local-by-default": "^4.0.5",
-        "postcss-modules-scope": "^3.2.0",
-        "postcss-modules-values": "^4.0.0",
-        "postcss-value-parser": "^4.2.0",
-        "semver": "^7.5.4"
-      },
-      "engines": {
-        "node": ">= 12.13.0"
-      },
-      "funding": {
-        "type": "opencollective",
-        "url": "https://opencollective.com/webpack"
-      },
-      "peerDependencies": {
-        "@rspack/core": "0.x || 1.x",
-        "webpack": "^5.0.0"
-      },
-      "peerDependenciesMeta": {
-        "@rspack/core": {
-          "optional": true
-        },
-        "webpack": {
-          "optional": true
-        }
-      }
-    },
-    "node_modules/css-loader/node_modules/semver": {
-      "version": "7.7.4",
-      "resolved": "https://registry.npmmirror.com/semver/-/semver-7.7.4.tgz",
-      "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==",
-      "dev": true,
-      "license": "ISC",
-      "bin": {
-        "semver": "bin/semver.js"
-      },
-      "engines": {
-        "node": ">=10"
-      }
-    },
-    "node_modules/css-minimizer-webpack-plugin": {
-      "version": "3.4.1",
-      "resolved": "https://registry.npmmirror.com/css-minimizer-webpack-plugin/-/css-minimizer-webpack-plugin-3.4.1.tgz",
-      "integrity": "sha512-1u6D71zeIfgngN2XNRJefc/hY7Ybsxd74Jm4qngIXyUEk7fss3VUzuHxLAq/R8NAba4QU9OUSaMZlbpRc7bM4Q==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "cssnano": "^5.0.6",
-        "jest-worker": "^27.0.2",
-        "postcss": "^8.3.5",
-        "schema-utils": "^4.0.0",
-        "serialize-javascript": "^6.0.0",
-        "source-map": "^0.6.1"
-      },
-      "engines": {
-        "node": ">= 12.13.0"
-      },
-      "funding": {
-        "type": "opencollective",
-        "url": "https://opencollective.com/webpack"
-      },
-      "peerDependencies": {
-        "webpack": "^5.0.0"
-      },
-      "peerDependenciesMeta": {
-        "@parcel/css": {
-          "optional": true
-        },
-        "clean-css": {
-          "optional": true
-        },
-        "csso": {
-          "optional": true
-        },
-        "esbuild": {
-          "optional": true
-        }
-      }
-    },
-    "node_modules/css-minimizer-webpack-plugin/node_modules/ajv": {
-      "version": "8.18.0",
-      "resolved": "https://registry.npmmirror.com/ajv/-/ajv-8.18.0.tgz",
-      "integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "fast-deep-equal": "^3.1.3",
-        "fast-uri": "^3.0.1",
-        "json-schema-traverse": "^1.0.0",
-        "require-from-string": "^2.0.2"
-      },
-      "funding": {
-        "type": "github",
-        "url": "https://github.com/sponsors/epoberezkin"
-      }
-    },
-    "node_modules/css-minimizer-webpack-plugin/node_modules/ajv-keywords": {
-      "version": "5.1.0",
-      "resolved": "https://registry.npmmirror.com/ajv-keywords/-/ajv-keywords-5.1.0.tgz",
-      "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "fast-deep-equal": "^3.1.3"
-      },
-      "peerDependencies": {
-        "ajv": "^8.8.2"
-      }
-    },
-    "node_modules/css-minimizer-webpack-plugin/node_modules/json-schema-traverse": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmmirror.com/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
-      "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
-      "dev": true,
-      "license": "MIT"
-    },
-    "node_modules/css-minimizer-webpack-plugin/node_modules/schema-utils": {
-      "version": "4.3.3",
-      "resolved": "https://registry.npmmirror.com/schema-utils/-/schema-utils-4.3.3.tgz",
-      "integrity": "sha512-eflK8wEtyOE6+hsaRVPxvUKYCpRgzLqDTb8krvAsRIwOGlHoSgYLgBXoubGgLd2fT41/OUYdb48v4k4WWHQurA==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@types/json-schema": "^7.0.9",
-        "ajv": "^8.9.0",
-        "ajv-formats": "^2.1.1",
-        "ajv-keywords": "^5.1.0"
-      },
-      "engines": {
-        "node": ">= 10.13.0"
-      },
-      "funding": {
-        "type": "opencollective",
-        "url": "https://opencollective.com/webpack"
-      }
-    },
-    "node_modules/css-select": {
-      "version": "4.3.0",
-      "resolved": "https://registry.npmmirror.com/css-select/-/css-select-4.3.0.tgz",
-      "integrity": "sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ==",
-      "dev": true,
-      "license": "BSD-2-Clause",
-      "dependencies": {
-        "boolbase": "^1.0.0",
-        "css-what": "^6.0.1",
-        "domhandler": "^4.3.1",
-        "domutils": "^2.8.0",
-        "nth-check": "^2.0.1"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/fb55"
-      }
-    },
-    "node_modules/css-tree": {
-      "version": "1.1.3",
-      "resolved": "https://registry.npmmirror.com/css-tree/-/css-tree-1.1.3.tgz",
-      "integrity": "sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "mdn-data": "2.0.14",
-        "source-map": "^0.6.1"
-      },
-      "engines": {
-        "node": ">=8.0.0"
-      }
-    },
-    "node_modules/css-what": {
-      "version": "6.2.2",
-      "resolved": "https://registry.npmmirror.com/css-what/-/css-what-6.2.2.tgz",
-      "integrity": "sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA==",
-      "dev": true,
-      "license": "BSD-2-Clause",
-      "engines": {
-        "node": ">= 6"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/fb55"
-      }
-    },
-    "node_modules/cssesc": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmmirror.com/cssesc/-/cssesc-3.0.0.tgz",
-      "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==",
-      "dev": true,
-      "license": "MIT",
-      "bin": {
-        "cssesc": "bin/cssesc"
-      },
-      "engines": {
-        "node": ">=4"
-      }
-    },
-    "node_modules/cssnano": {
-      "version": "5.1.15",
-      "resolved": "https://registry.npmmirror.com/cssnano/-/cssnano-5.1.15.tgz",
-      "integrity": "sha512-j+BKgDcLDQA+eDifLx0EO4XSA56b7uut3BQFH+wbSaSTuGLuiyTa/wbRYthUXX8LC9mLg+WWKe8h+qJuwTAbHw==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "cssnano-preset-default": "^5.2.14",
-        "lilconfig": "^2.0.3",
-        "yaml": "^1.10.2"
-      },
-      "engines": {
-        "node": "^10 || ^12 || >=14.0"
-      },
-      "funding": {
-        "type": "opencollective",
-        "url": "https://opencollective.com/cssnano"
-      },
-      "peerDependencies": {
-        "postcss": "^8.2.15"
-      }
-    },
-    "node_modules/cssnano-preset-default": {
-      "version": "5.2.14",
-      "resolved": "https://registry.npmmirror.com/cssnano-preset-default/-/cssnano-preset-default-5.2.14.tgz",
-      "integrity": "sha512-t0SFesj/ZV2OTylqQVOrFgEh5uanxbO6ZAdeCrNsUQ6fVuXwYTxJPNAGvGTxHbD68ldIJNec7PyYZDBrfDQ+6A==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "css-declaration-sorter": "^6.3.1",
-        "cssnano-utils": "^3.1.0",
-        "postcss-calc": "^8.2.3",
-        "postcss-colormin": "^5.3.1",
-        "postcss-convert-values": "^5.1.3",
-        "postcss-discard-comments": "^5.1.2",
-        "postcss-discard-duplicates": "^5.1.0",
-        "postcss-discard-empty": "^5.1.1",
-        "postcss-discard-overridden": "^5.1.0",
-        "postcss-merge-longhand": "^5.1.7",
-        "postcss-merge-rules": "^5.1.4",
-        "postcss-minify-font-values": "^5.1.0",
-        "postcss-minify-gradients": "^5.1.1",
-        "postcss-minify-params": "^5.1.4",
-        "postcss-minify-selectors": "^5.2.1",
-        "postcss-normalize-charset": "^5.1.0",
-        "postcss-normalize-display-values": "^5.1.0",
-        "postcss-normalize-positions": "^5.1.1",
-        "postcss-normalize-repeat-style": "^5.1.1",
-        "postcss-normalize-string": "^5.1.0",
-        "postcss-normalize-timing-functions": "^5.1.0",
-        "postcss-normalize-unicode": "^5.1.1",
-        "postcss-normalize-url": "^5.1.0",
-        "postcss-normalize-whitespace": "^5.1.1",
-        "postcss-ordered-values": "^5.1.3",
-        "postcss-reduce-initial": "^5.1.2",
-        "postcss-reduce-transforms": "^5.1.0",
-        "postcss-svgo": "^5.1.0",
-        "postcss-unique-selectors": "^5.1.1"
-      },
-      "engines": {
-        "node": "^10 || ^12 || >=14.0"
-      },
-      "peerDependencies": {
-        "postcss": "^8.2.15"
-      }
-    },
-    "node_modules/cssnano-utils": {
-      "version": "3.1.0",
-      "resolved": "https://registry.npmmirror.com/cssnano-utils/-/cssnano-utils-3.1.0.tgz",
-      "integrity": "sha512-JQNR19/YZhz4psLX/rQ9M83e3z2Wf/HdJbryzte4a3NSuafyp9w/I4U+hx5C2S9g41qlstH7DEWnZaaj83OuEA==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": "^10 || ^12 || >=14.0"
-      },
-      "peerDependencies": {
-        "postcss": "^8.2.15"
-      }
-    },
-    "node_modules/csso": {
-      "version": "4.2.0",
-      "resolved": "https://registry.npmmirror.com/csso/-/csso-4.2.0.tgz",
-      "integrity": "sha512-wvlcdIbf6pwKEk7vHj8/Bkc0B4ylXZruLvOgs9doS5eOsOpuodOV2zJChSpkp+pRpYQLQMeF04nr3Z68Sta9jA==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "css-tree": "^1.1.2"
-      },
-      "engines": {
-        "node": ">=8.0.0"
-      }
-    },
-    "node_modules/csstype": {
-      "version": "3.2.3",
-      "resolved": "https://registry.npmmirror.com/csstype/-/csstype-3.2.3.tgz",
-      "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==",
-      "license": "MIT"
-    },
-    "node_modules/de-indent": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmmirror.com/de-indent/-/de-indent-1.0.2.tgz",
-      "integrity": "sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg==",
-      "dev": true,
-      "license": "MIT"
-    },
-    "node_modules/debounce": {
-      "version": "1.2.1",
-      "resolved": "https://registry.npmmirror.com/debounce/-/debounce-1.2.1.tgz",
-      "integrity": "sha512-XRRe6Glud4rd/ZGQfiV1ruXSfbvfJedlV9Y6zOlP+2K04vBYiJEte6stfFkCP03aMnY5tsipamumUjL14fofug==",
-      "dev": true,
-      "license": "MIT"
-    },
-    "node_modules/debug": {
-      "version": "4.4.3",
-      "resolved": "https://registry.npmmirror.com/debug/-/debug-4.4.3.tgz",
-      "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "ms": "^2.1.3"
-      },
-      "engines": {
-        "node": ">=6.0"
-      },
-      "peerDependenciesMeta": {
-        "supports-color": {
-          "optional": true
-        }
-      }
-    },
-    "node_modules/deep-equal": {
-      "version": "1.1.2",
-      "resolved": "https://registry.npmmirror.com/deep-equal/-/deep-equal-1.1.2.tgz",
-      "integrity": "sha512-5tdhKF6DbU7iIzrIOa1AOUt39ZRm13cmL1cGEh//aqR8x9+tNfbywRf0n5FD/18OKMdo7DNEtrX2t22ZAkI+eg==",
-      "license": "MIT",
-      "dependencies": {
-        "is-arguments": "^1.1.1",
-        "is-date-object": "^1.0.5",
-        "is-regex": "^1.1.4",
-        "object-is": "^1.1.5",
-        "object-keys": "^1.1.1",
-        "regexp.prototype.flags": "^1.5.1"
-      },
-      "engines": {
-        "node": ">= 0.4"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
-    "node_modules/deep-is": {
-      "version": "0.1.4",
-      "resolved": "https://registry.npmmirror.com/deep-is/-/deep-is-0.1.4.tgz",
-      "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==",
-      "dev": true,
-      "license": "MIT"
-    },
-    "node_modules/deepmerge": {
-      "version": "1.5.2",
-      "resolved": "https://registry.npmmirror.com/deepmerge/-/deepmerge-1.5.2.tgz",
-      "integrity": "sha512-95k0GDqvBjZavkuvzx/YqVLv/6YYa17fz6ILMSf7neqQITCPbnfEnQvEgMPNjH4kgobe7+WIL0yJEHku+H3qtQ==",
-      "license": "MIT",
-      "engines": {
-        "node": ">=0.10.0"
-      }
-    },
-    "node_modules/default-gateway": {
-      "version": "6.0.3",
-      "resolved": "https://registry.npmmirror.com/default-gateway/-/default-gateway-6.0.3.tgz",
-      "integrity": "sha512-fwSOJsbbNzZ/CUFpqFBqYfYNLj1NbMPm8MMCIzHjC83iSJRBEGmDUxU+WP661BaBQImeC2yHwXtz+P/O9o+XEg==",
-      "dev": true,
-      "license": "BSD-2-Clause",
-      "dependencies": {
-        "execa": "^5.0.0"
-      },
-      "engines": {
-        "node": ">= 10"
-      }
-    },
-    "node_modules/default-gateway/node_modules/cross-spawn": {
-      "version": "7.0.6",
-      "resolved": "https://registry.npmmirror.com/cross-spawn/-/cross-spawn-7.0.6.tgz",
-      "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "path-key": "^3.1.0",
-        "shebang-command": "^2.0.0",
-        "which": "^2.0.1"
-      },
-      "engines": {
-        "node": ">= 8"
-      }
-    },
-    "node_modules/default-gateway/node_modules/execa": {
-      "version": "5.1.1",
-      "resolved": "https://registry.npmmirror.com/execa/-/execa-5.1.1.tgz",
-      "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "cross-spawn": "^7.0.3",
-        "get-stream": "^6.0.0",
-        "human-signals": "^2.1.0",
-        "is-stream": "^2.0.0",
-        "merge-stream": "^2.0.0",
-        "npm-run-path": "^4.0.1",
-        "onetime": "^5.1.2",
-        "signal-exit": "^3.0.3",
-        "strip-final-newline": "^2.0.0"
-      },
-      "engines": {
-        "node": ">=10"
-      },
-      "funding": {
-        "url": "https://github.com/sindresorhus/execa?sponsor=1"
-      }
-    },
-    "node_modules/default-gateway/node_modules/get-stream": {
-      "version": "6.0.1",
-      "resolved": "https://registry.npmmirror.com/get-stream/-/get-stream-6.0.1.tgz",
-      "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=10"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/sindresorhus"
-      }
-    },
-    "node_modules/default-gateway/node_modules/is-stream": {
-      "version": "2.0.1",
-      "resolved": "https://registry.npmmirror.com/is-stream/-/is-stream-2.0.1.tgz",
-      "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=8"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/sindresorhus"
-      }
-    },
-    "node_modules/default-gateway/node_modules/npm-run-path": {
-      "version": "4.0.1",
-      "resolved": "https://registry.npmmirror.com/npm-run-path/-/npm-run-path-4.0.1.tgz",
-      "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "path-key": "^3.0.0"
-      },
-      "engines": {
-        "node": ">=8"
-      }
-    },
-    "node_modules/default-gateway/node_modules/path-key": {
-      "version": "3.1.1",
-      "resolved": "https://registry.npmmirror.com/path-key/-/path-key-3.1.1.tgz",
-      "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=8"
-      }
-    },
-    "node_modules/default-gateway/node_modules/shebang-command": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmmirror.com/shebang-command/-/shebang-command-2.0.0.tgz",
-      "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "shebang-regex": "^3.0.0"
-      },
-      "engines": {
-        "node": ">=8"
-      }
-    },
-    "node_modules/default-gateway/node_modules/shebang-regex": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmmirror.com/shebang-regex/-/shebang-regex-3.0.0.tgz",
-      "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=8"
-      }
-    },
-    "node_modules/default-gateway/node_modules/which": {
-      "version": "2.0.2",
-      "resolved": "https://registry.npmmirror.com/which/-/which-2.0.2.tgz",
-      "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
-      "dev": true,
-      "license": "ISC",
-      "dependencies": {
-        "isexe": "^2.0.0"
-      },
-      "bin": {
-        "node-which": "bin/node-which"
-      },
-      "engines": {
-        "node": ">= 8"
-      }
-    },
-    "node_modules/defaults": {
-      "version": "1.0.4",
-      "resolved": "https://registry.npmmirror.com/defaults/-/defaults-1.0.4.tgz",
-      "integrity": "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "clone": "^1.0.2"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/sindresorhus"
-      }
-    },
-    "node_modules/define-data-property": {
-      "version": "1.1.4",
-      "resolved": "https://registry.npmmirror.com/define-data-property/-/define-data-property-1.1.4.tgz",
-      "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==",
-      "license": "MIT",
-      "dependencies": {
-        "es-define-property": "^1.0.0",
-        "es-errors": "^1.3.0",
-        "gopd": "^1.0.1"
-      },
-      "engines": {
-        "node": ">= 0.4"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
-    "node_modules/define-lazy-prop": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmmirror.com/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz",
-      "integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=8"
-      }
-    },
-    "node_modules/define-properties": {
-      "version": "1.2.1",
-      "resolved": "https://registry.npmmirror.com/define-properties/-/define-properties-1.2.1.tgz",
-      "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==",
-      "license": "MIT",
-      "dependencies": {
-        "define-data-property": "^1.0.1",
-        "has-property-descriptors": "^1.0.0",
-        "object-keys": "^1.1.1"
-      },
-      "engines": {
-        "node": ">= 0.4"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
-    "node_modules/delayed-stream": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmmirror.com/delayed-stream/-/delayed-stream-1.0.0.tgz",
-      "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
-      "license": "MIT",
-      "engines": {
-        "node": ">=0.4.0"
-      }
-    },
-    "node_modules/depd": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmmirror.com/depd/-/depd-2.0.0.tgz",
-      "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">= 0.8"
-      }
-    },
-    "node_modules/destroy": {
-      "version": "1.2.0",
-      "resolved": "https://registry.npmmirror.com/destroy/-/destroy-1.2.0.tgz",
-      "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">= 0.8",
-        "npm": "1.2.8000 || >= 1.4.16"
-      }
-    },
-    "node_modules/detect-node": {
-      "version": "2.1.0",
-      "resolved": "https://registry.npmmirror.com/detect-node/-/detect-node-2.1.0.tgz",
-      "integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==",
-      "dev": true,
-      "license": "MIT"
-    },
-    "node_modules/dir-glob": {
-      "version": "3.0.1",
-      "resolved": "https://registry.npmmirror.com/dir-glob/-/dir-glob-3.0.1.tgz",
-      "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "path-type": "^4.0.0"
-      },
-      "engines": {
-        "node": ">=8"
-      }
-    },
-    "node_modules/dns-packet": {
-      "version": "5.6.1",
-      "resolved": "https://registry.npmmirror.com/dns-packet/-/dns-packet-5.6.1.tgz",
-      "integrity": "sha512-l4gcSouhcgIKRvyy99RNVOgxXiicE+2jZoNmaNmZ6JXiGajBOJAesk1OBlJuM5k2c+eudGdLxDqXuPCKIj6kpw==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@leichtgewicht/ip-codec": "^2.0.1"
-      },
-      "engines": {
-        "node": ">=6"
-      }
-    },
-    "node_modules/doctrine": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmmirror.com/doctrine/-/doctrine-3.0.0.tgz",
-      "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==",
-      "dev": true,
-      "license": "Apache-2.0",
-      "dependencies": {
-        "esutils": "^2.0.2"
-      },
-      "engines": {
-        "node": ">=6.0.0"
-      }
-    },
-    "node_modules/dom-converter": {
-      "version": "0.2.0",
-      "resolved": "https://registry.npmmirror.com/dom-converter/-/dom-converter-0.2.0.tgz",
-      "integrity": "sha512-gd3ypIPfOMr9h5jIKq8E3sHOTCjeirnl0WK5ZdS1AW0Odt0b1PaWaHdJ4Qk4klv+YB9aJBS7mESXjFoDQPu6DA==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "utila": "~0.4"
-      }
-    },
-    "node_modules/dom-serializer": {
-      "version": "1.4.1",
-      "resolved": "https://registry.npmmirror.com/dom-serializer/-/dom-serializer-1.4.1.tgz",
-      "integrity": "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "domelementtype": "^2.0.1",
-        "domhandler": "^4.2.0",
-        "entities": "^2.0.0"
-      },
-      "funding": {
-        "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1"
-      }
-    },
-    "node_modules/dom-serializer/node_modules/entities": {
-      "version": "2.2.0",
-      "resolved": "https://registry.npmmirror.com/entities/-/entities-2.2.0.tgz",
-      "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==",
-      "dev": true,
-      "license": "BSD-2-Clause",
-      "funding": {
-        "url": "https://github.com/fb55/entities?sponsor=1"
-      }
-    },
-    "node_modules/domelementtype": {
-      "version": "2.3.0",
-      "resolved": "https://registry.npmmirror.com/domelementtype/-/domelementtype-2.3.0.tgz",
-      "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==",
-      "dev": true,
-      "funding": [
-        {
-          "type": "github",
-          "url": "https://github.com/sponsors/fb55"
-        }
-      ],
-      "license": "BSD-2-Clause"
-    },
-    "node_modules/domhandler": {
-      "version": "4.3.1",
-      "resolved": "https://registry.npmmirror.com/domhandler/-/domhandler-4.3.1.tgz",
-      "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==",
-      "dev": true,
-      "license": "BSD-2-Clause",
-      "dependencies": {
-        "domelementtype": "^2.2.0"
-      },
-      "engines": {
-        "node": ">= 4"
-      },
-      "funding": {
-        "url": "https://github.com/fb55/domhandler?sponsor=1"
-      }
-    },
-    "node_modules/domutils": {
-      "version": "2.8.0",
-      "resolved": "https://registry.npmmirror.com/domutils/-/domutils-2.8.0.tgz",
-      "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==",
-      "dev": true,
-      "license": "BSD-2-Clause",
-      "dependencies": {
-        "dom-serializer": "^1.0.1",
-        "domelementtype": "^2.2.0",
-        "domhandler": "^4.2.0"
-      },
-      "funding": {
-        "url": "https://github.com/fb55/domutils?sponsor=1"
-      }
-    },
-    "node_modules/dot-case": {
-      "version": "3.0.4",
-      "resolved": "https://registry.npmmirror.com/dot-case/-/dot-case-3.0.4.tgz",
-      "integrity": "sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "no-case": "^3.0.4",
-        "tslib": "^2.0.3"
-      }
-    },
-    "node_modules/dotenv": {
-      "version": "10.0.0",
-      "resolved": "https://registry.npmmirror.com/dotenv/-/dotenv-10.0.0.tgz",
-      "integrity": "sha512-rlBi9d8jpv9Sf1klPjNfFAuWDjKLwTIJJ/VxtoTwIR6hnZxcEOQCZg2oIL3MWBYw5GpUDKOEnND7LXTbIpQ03Q==",
-      "dev": true,
-      "license": "BSD-2-Clause",
-      "engines": {
-        "node": ">=10"
-      }
-    },
-    "node_modules/dotenv-expand": {
-      "version": "5.1.0",
-      "resolved": "https://registry.npmmirror.com/dotenv-expand/-/dotenv-expand-5.1.0.tgz",
-      "integrity": "sha512-YXQl1DSa4/PQyRfgrv6aoNjhasp/p4qs9FjJ4q4cQk+8m4r6k4ZSiEyytKG8f8W9gi8WsQtIObNmKd+tMzNTmA==",
-      "dev": true,
-      "license": "BSD-2-Clause"
-    },
-    "node_modules/dunder-proto": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmmirror.com/dunder-proto/-/dunder-proto-1.0.1.tgz",
-      "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==",
-      "license": "MIT",
-      "dependencies": {
-        "call-bind-apply-helpers": "^1.0.1",
-        "es-errors": "^1.3.0",
-        "gopd": "^1.2.0"
-      },
-      "engines": {
-        "node": ">= 0.4"
-      }
-    },
-    "node_modules/duplexer": {
-      "version": "0.1.2",
-      "resolved": "https://registry.npmmirror.com/duplexer/-/duplexer-0.1.2.tgz",
-      "integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==",
-      "dev": true,
-      "license": "MIT"
-    },
-    "node_modules/easy-stack": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmmirror.com/easy-stack/-/easy-stack-1.0.1.tgz",
-      "integrity": "sha512-wK2sCs4feiiJeFXn3zvY0p41mdU5VUgbgs1rNsc/y5ngFUijdWd+iIN8eoyuZHKB8xN6BL4PdWmzqFmxNg6V2w==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=6.0.0"
-      }
-    },
-    "node_modules/ee-first": {
-      "version": "1.1.1",
-      "resolved": "https://registry.npmmirror.com/ee-first/-/ee-first-1.1.1.tgz",
-      "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==",
-      "dev": true,
-      "license": "MIT"
-    },
-    "node_modules/electron-to-chromium": {
-      "version": "1.5.339",
-      "resolved": "https://registry.npmmirror.com/electron-to-chromium/-/electron-to-chromium-1.5.339.tgz",
-      "integrity": "sha512-Is+0BBHJ4NrdpAYiperrmp53pLywG/yV/6lIMTAnhxvzj/Cmn5Q/ogSHC6AKe7X+8kPLxxFk0cs5oc/3j/fxIg==",
-      "dev": true,
-      "license": "ISC"
-    },
-    "node_modules/element-ui": {
-      "version": "2.15.14",
-      "resolved": "https://registry.npmmirror.com/element-ui/-/element-ui-2.15.14.tgz",
-      "integrity": "sha512-2v9fHL0ZGINotOlRIAJD5YuVB8V7WKxrE9Qy7dXhRipa035+kF7WuU/z+tEmLVPBcJ0zt8mOu1DKpWcVzBK8IA==",
-      "license": "MIT",
-      "dependencies": {
-        "async-validator": "~1.8.1",
-        "babel-helper-vue-jsx-merge-props": "^2.0.0",
-        "deepmerge": "^1.2.0",
-        "normalize-wheel": "^1.0.1",
-        "resize-observer-polyfill": "^1.5.0",
-        "throttle-debounce": "^1.0.1"
-      },
-      "peerDependencies": {
-        "vue": "^2.5.17"
-      }
-    },
-    "node_modules/emoji-regex": {
-      "version": "8.0.0",
-      "resolved": "https://registry.npmmirror.com/emoji-regex/-/emoji-regex-8.0.0.tgz",
-      "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
-      "dev": true,
-      "license": "MIT"
-    },
-    "node_modules/emojis-list": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmmirror.com/emojis-list/-/emojis-list-3.0.0.tgz",
-      "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">= 4"
-      }
-    },
-    "node_modules/encodeurl": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmmirror.com/encodeurl/-/encodeurl-2.0.0.tgz",
-      "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">= 0.8"
-      }
-    },
-    "node_modules/end-of-stream": {
-      "version": "1.4.5",
-      "resolved": "https://registry.npmmirror.com/end-of-stream/-/end-of-stream-1.4.5.tgz",
-      "integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "once": "^1.4.0"
-      }
-    },
-    "node_modules/enhanced-resolve": {
-      "version": "5.20.1",
-      "resolved": "https://registry.npmmirror.com/enhanced-resolve/-/enhanced-resolve-5.20.1.tgz",
-      "integrity": "sha512-Qohcme7V1inbAfvjItgw0EaxVX5q2rdVEZHRBrEQdRZTssLDGsL8Lwrznl8oQ/6kuTJONLaDcGjkNP247XEhcA==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "graceful-fs": "^4.2.4",
-        "tapable": "^2.3.0"
-      },
-      "engines": {
-        "node": ">=10.13.0"
-      }
-    },
-    "node_modules/enquirer": {
-      "version": "2.4.1",
-      "resolved": "https://registry.npmmirror.com/enquirer/-/enquirer-2.4.1.tgz",
-      "integrity": "sha512-rRqJg/6gd538VHvR3PSrdRBb/1Vy2YfzHqzvbhGIQpDRKIa4FgV/54b5Q1xYSxOOwKvjXweS26E0Q+nAMwp2pQ==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "ansi-colors": "^4.1.1",
-        "strip-ansi": "^6.0.1"
-      },
-      "engines": {
-        "node": ">=8.6"
-      }
-    },
-    "node_modules/entities": {
-      "version": "7.0.1",
-      "resolved": "https://registry.npmmirror.com/entities/-/entities-7.0.1.tgz",
-      "integrity": "sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==",
-      "dev": true,
-      "license": "BSD-2-Clause",
-      "engines": {
-        "node": ">=0.12"
-      },
-      "funding": {
-        "url": "https://github.com/fb55/entities?sponsor=1"
-      }
-    },
-    "node_modules/error-ex": {
-      "version": "1.3.4",
-      "resolved": "https://registry.npmmirror.com/error-ex/-/error-ex-1.3.4.tgz",
-      "integrity": "sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "is-arrayish": "^0.2.1"
-      }
-    },
-    "node_modules/error-stack-parser": {
-      "version": "2.1.4",
-      "resolved": "https://registry.npmmirror.com/error-stack-parser/-/error-stack-parser-2.1.4.tgz",
-      "integrity": "sha512-Sk5V6wVazPhq5MhpO+AUxJn5x7XSXGl1R93Vn7i+zS15KDVxQijejNCrz8340/2bgLBjR9GtEG8ZVKONDjcqGQ==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "stackframe": "^1.3.4"
-      }
-    },
-    "node_modules/es-define-property": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmmirror.com/es-define-property/-/es-define-property-1.0.1.tgz",
-      "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==",
-      "license": "MIT",
-      "engines": {
-        "node": ">= 0.4"
-      }
-    },
-    "node_modules/es-errors": {
-      "version": "1.3.0",
-      "resolved": "https://registry.npmmirror.com/es-errors/-/es-errors-1.3.0.tgz",
-      "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
-      "license": "MIT",
-      "engines": {
-        "node": ">= 0.4"
-      }
-    },
-    "node_modules/es-module-lexer": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmmirror.com/es-module-lexer/-/es-module-lexer-2.0.0.tgz",
-      "integrity": "sha512-5POEcUuZybH7IdmGsD8wlf0AI55wMecM9rVBTI/qEAy2c1kTOm3DjFYjrBdI2K3BaJjJYfYFeRtM0t9ssnRuxw==",
-      "dev": true,
-      "license": "MIT"
-    },
-    "node_modules/es-object-atoms": {
-      "version": "1.1.1",
-      "resolved": "https://registry.npmmirror.com/es-object-atoms/-/es-object-atoms-1.1.1.tgz",
-      "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==",
-      "license": "MIT",
-      "dependencies": {
-        "es-errors": "^1.3.0"
-      },
-      "engines": {
-        "node": ">= 0.4"
-      }
-    },
-    "node_modules/es-set-tostringtag": {
-      "version": "2.1.0",
-      "resolved": "https://registry.npmmirror.com/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz",
-      "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==",
-      "license": "MIT",
-      "dependencies": {
-        "es-errors": "^1.3.0",
-        "get-intrinsic": "^1.2.6",
-        "has-tostringtag": "^1.0.2",
-        "hasown": "^2.0.2"
-      },
-      "engines": {
-        "node": ">= 0.4"
-      }
-    },
-    "node_modules/escalade": {
-      "version": "3.2.0",
-      "resolved": "https://registry.npmmirror.com/escalade/-/escalade-3.2.0.tgz",
-      "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=6"
-      }
-    },
-    "node_modules/escape-html": {
-      "version": "1.0.3",
-      "resolved": "https://registry.npmmirror.com/escape-html/-/escape-html-1.0.3.tgz",
-      "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==",
-      "dev": true,
-      "license": "MIT"
-    },
-    "node_modules/escape-string-regexp": {
-      "version": "1.0.5",
-      "resolved": "https://registry.npmmirror.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
-      "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=0.8.0"
-      }
-    },
-    "node_modules/eslint": {
-      "version": "7.32.0",
-      "resolved": "https://registry.npmmirror.com/eslint/-/eslint-7.32.0.tgz",
-      "integrity": "sha512-VHZ8gX+EDfz+97jGcgyGCyRia/dPOd6Xh9yPv8Bl1+SoaIwD+a/vlrOmGRUyOYu7MwUhc7CxqeaDZU13S4+EpA==",
-      "deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@babel/code-frame": "7.12.11",
-        "@eslint/eslintrc": "^0.4.3",
-        "@humanwhocodes/config-array": "^0.5.0",
-        "ajv": "^6.10.0",
-        "chalk": "^4.0.0",
-        "cross-spawn": "^7.0.2",
-        "debug": "^4.0.1",
-        "doctrine": "^3.0.0",
-        "enquirer": "^2.3.5",
-        "escape-string-regexp": "^4.0.0",
-        "eslint-scope": "^5.1.1",
-        "eslint-utils": "^2.1.0",
-        "eslint-visitor-keys": "^2.0.0",
-        "espree": "^7.3.1",
-        "esquery": "^1.4.0",
-        "esutils": "^2.0.2",
-        "fast-deep-equal": "^3.1.3",
-        "file-entry-cache": "^6.0.1",
-        "functional-red-black-tree": "^1.0.1",
-        "glob-parent": "^5.1.2",
-        "globals": "^13.6.0",
-        "ignore": "^4.0.6",
-        "import-fresh": "^3.0.0",
-        "imurmurhash": "^0.1.4",
-        "is-glob": "^4.0.0",
-        "js-yaml": "^3.13.1",
-        "json-stable-stringify-without-jsonify": "^1.0.1",
-        "levn": "^0.4.1",
-        "lodash.merge": "^4.6.2",
-        "minimatch": "^3.0.4",
-        "natural-compare": "^1.4.0",
-        "optionator": "^0.9.1",
-        "progress": "^2.0.0",
-        "regexpp": "^3.1.0",
-        "semver": "^7.2.1",
-        "strip-ansi": "^6.0.0",
-        "strip-json-comments": "^3.1.0",
-        "table": "^6.0.9",
-        "text-table": "^0.2.0",
-        "v8-compile-cache": "^2.0.3"
-      },
-      "bin": {
-        "eslint": "bin/eslint.js"
-      },
-      "engines": {
-        "node": "^10.12.0 || >=12.0.0"
-      },
-      "funding": {
-        "url": "https://opencollective.com/eslint"
-      }
-    },
-    "node_modules/eslint-plugin-vue": {
-      "version": "8.7.1",
-      "resolved": "https://registry.npmmirror.com/eslint-plugin-vue/-/eslint-plugin-vue-8.7.1.tgz",
-      "integrity": "sha512-28sbtm4l4cOzoO1LtzQPxfxhQABararUb1JtqusQqObJpWX2e/gmVyeYVfepizPFne0Q5cILkYGiBoV36L12Wg==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "eslint-utils": "^3.0.0",
-        "natural-compare": "^1.4.0",
-        "nth-check": "^2.0.1",
-        "postcss-selector-parser": "^6.0.9",
-        "semver": "^7.3.5",
-        "vue-eslint-parser": "^8.0.1"
-      },
-      "engines": {
-        "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
-      },
-      "peerDependencies": {
-        "eslint": "^6.2.0 || ^7.0.0 || ^8.0.0"
-      }
-    },
-    "node_modules/eslint-plugin-vue/node_modules/eslint-utils": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmmirror.com/eslint-utils/-/eslint-utils-3.0.0.tgz",
-      "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "eslint-visitor-keys": "^2.0.0"
-      },
-      "engines": {
-        "node": "^10.0.0 || ^12.0.0 || >= 14.0.0"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/mysticatea"
-      },
-      "peerDependencies": {
-        "eslint": ">=5"
-      }
-    },
-    "node_modules/eslint-plugin-vue/node_modules/semver": {
-      "version": "7.7.4",
-      "resolved": "https://registry.npmmirror.com/semver/-/semver-7.7.4.tgz",
-      "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==",
-      "dev": true,
-      "license": "ISC",
-      "bin": {
-        "semver": "bin/semver.js"
-      },
-      "engines": {
-        "node": ">=10"
-      }
-    },
-    "node_modules/eslint-scope": {
-      "version": "5.1.1",
-      "resolved": "https://registry.npmmirror.com/eslint-scope/-/eslint-scope-5.1.1.tgz",
-      "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==",
-      "dev": true,
-      "license": "BSD-2-Clause",
-      "dependencies": {
-        "esrecurse": "^4.3.0",
-        "estraverse": "^4.1.1"
-      },
-      "engines": {
-        "node": ">=8.0.0"
-      }
-    },
-    "node_modules/eslint-utils": {
-      "version": "2.1.0",
-      "resolved": "https://registry.npmmirror.com/eslint-utils/-/eslint-utils-2.1.0.tgz",
-      "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "eslint-visitor-keys": "^1.1.0"
-      },
-      "engines": {
-        "node": ">=6"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/mysticatea"
-      }
-    },
-    "node_modules/eslint-utils/node_modules/eslint-visitor-keys": {
-      "version": "1.3.0",
-      "resolved": "https://registry.npmmirror.com/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz",
-      "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==",
-      "dev": true,
-      "license": "Apache-2.0",
-      "engines": {
-        "node": ">=4"
-      }
-    },
-    "node_modules/eslint-visitor-keys": {
-      "version": "2.1.0",
-      "resolved": "https://registry.npmmirror.com/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz",
-      "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==",
-      "dev": true,
-      "license": "Apache-2.0",
-      "engines": {
-        "node": ">=10"
-      }
-    },
-    "node_modules/eslint-webpack-plugin": {
-      "version": "3.2.0",
-      "resolved": "https://registry.npmmirror.com/eslint-webpack-plugin/-/eslint-webpack-plugin-3.2.0.tgz",
-      "integrity": "sha512-avrKcGncpPbPSUHX6B3stNGzkKFto3eL+DKM4+VyMrVnhPc3vRczVlCq3uhuFOdRvDHTVXuzwk1ZKUrqDQHQ9w==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@types/eslint": "^7.29.0 || ^8.4.1",
-        "jest-worker": "^28.0.2",
-        "micromatch": "^4.0.5",
-        "normalize-path": "^3.0.0",
-        "schema-utils": "^4.0.0"
-      },
-      "engines": {
-        "node": ">= 12.13.0"
-      },
-      "funding": {
-        "type": "opencollective",
-        "url": "https://opencollective.com/webpack"
-      },
-      "peerDependencies": {
-        "eslint": "^7.0.0 || ^8.0.0",
-        "webpack": "^5.0.0"
-      }
-    },
-    "node_modules/eslint-webpack-plugin/node_modules/ajv": {
-      "version": "8.18.0",
-      "resolved": "https://registry.npmmirror.com/ajv/-/ajv-8.18.0.tgz",
-      "integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "fast-deep-equal": "^3.1.3",
-        "fast-uri": "^3.0.1",
-        "json-schema-traverse": "^1.0.0",
-        "require-from-string": "^2.0.2"
-      },
-      "funding": {
-        "type": "github",
-        "url": "https://github.com/sponsors/epoberezkin"
-      }
-    },
-    "node_modules/eslint-webpack-plugin/node_modules/ajv-keywords": {
-      "version": "5.1.0",
-      "resolved": "https://registry.npmmirror.com/ajv-keywords/-/ajv-keywords-5.1.0.tgz",
-      "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "fast-deep-equal": "^3.1.3"
-      },
-      "peerDependencies": {
-        "ajv": "^8.8.2"
-      }
-    },
-    "node_modules/eslint-webpack-plugin/node_modules/jest-worker": {
-      "version": "28.1.3",
-      "resolved": "https://registry.npmmirror.com/jest-worker/-/jest-worker-28.1.3.tgz",
-      "integrity": "sha512-CqRA220YV/6jCo8VWvAt1KKx6eek1VIHMPeLEbpcfSfkEeWyBNppynM/o6q+Wmw+sOhos2ml34wZbSX3G13//g==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@types/node": "*",
-        "merge-stream": "^2.0.0",
-        "supports-color": "^8.0.0"
-      },
-      "engines": {
-        "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0"
-      }
-    },
-    "node_modules/eslint-webpack-plugin/node_modules/json-schema-traverse": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmmirror.com/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
-      "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
-      "dev": true,
-      "license": "MIT"
-    },
-    "node_modules/eslint-webpack-plugin/node_modules/schema-utils": {
-      "version": "4.3.3",
-      "resolved": "https://registry.npmmirror.com/schema-utils/-/schema-utils-4.3.3.tgz",
-      "integrity": "sha512-eflK8wEtyOE6+hsaRVPxvUKYCpRgzLqDTb8krvAsRIwOGlHoSgYLgBXoubGgLd2fT41/OUYdb48v4k4WWHQurA==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@types/json-schema": "^7.0.9",
-        "ajv": "^8.9.0",
-        "ajv-formats": "^2.1.1",
-        "ajv-keywords": "^5.1.0"
-      },
-      "engines": {
-        "node": ">= 10.13.0"
-      },
-      "funding": {
-        "type": "opencollective",
-        "url": "https://opencollective.com/webpack"
-      }
-    },
-    "node_modules/eslint-webpack-plugin/node_modules/supports-color": {
-      "version": "8.1.1",
-      "resolved": "https://registry.npmmirror.com/supports-color/-/supports-color-8.1.1.tgz",
-      "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "has-flag": "^4.0.0"
-      },
-      "engines": {
-        "node": ">=10"
-      },
-      "funding": {
-        "url": "https://github.com/chalk/supports-color?sponsor=1"
-      }
-    },
-    "node_modules/eslint/node_modules/@babel/code-frame": {
-      "version": "7.12.11",
-      "resolved": "https://registry.npmmirror.com/@babel/code-frame/-/code-frame-7.12.11.tgz",
-      "integrity": "sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@babel/highlight": "^7.10.4"
-      }
-    },
-    "node_modules/eslint/node_modules/chalk": {
-      "version": "4.1.2",
-      "resolved": "https://registry.npmmirror.com/chalk/-/chalk-4.1.2.tgz",
-      "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "ansi-styles": "^4.1.0",
-        "supports-color": "^7.1.0"
-      },
-      "engines": {
-        "node": ">=10"
-      },
-      "funding": {
-        "url": "https://github.com/chalk/chalk?sponsor=1"
-      }
-    },
-    "node_modules/eslint/node_modules/cross-spawn": {
-      "version": "7.0.6",
-      "resolved": "https://registry.npmmirror.com/cross-spawn/-/cross-spawn-7.0.6.tgz",
-      "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "path-key": "^3.1.0",
-        "shebang-command": "^2.0.0",
-        "which": "^2.0.1"
-      },
-      "engines": {
-        "node": ">= 8"
-      }
-    },
-    "node_modules/eslint/node_modules/escape-string-regexp": {
-      "version": "4.0.0",
-      "resolved": "https://registry.npmmirror.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
-      "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=10"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/sindresorhus"
-      }
-    },
-    "node_modules/eslint/node_modules/glob-parent": {
-      "version": "5.1.2",
-      "resolved": "https://registry.npmmirror.com/glob-parent/-/glob-parent-5.1.2.tgz",
-      "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
-      "dev": true,
-      "license": "ISC",
-      "dependencies": {
-        "is-glob": "^4.0.1"
-      },
-      "engines": {
-        "node": ">= 6"
-      }
-    },
-    "node_modules/eslint/node_modules/ignore": {
-      "version": "4.0.6",
-      "resolved": "https://registry.npmmirror.com/ignore/-/ignore-4.0.6.tgz",
-      "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">= 4"
-      }
-    },
-    "node_modules/eslint/node_modules/path-key": {
-      "version": "3.1.1",
-      "resolved": "https://registry.npmmirror.com/path-key/-/path-key-3.1.1.tgz",
-      "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=8"
-      }
-    },
-    "node_modules/eslint/node_modules/semver": {
-      "version": "7.7.4",
-      "resolved": "https://registry.npmmirror.com/semver/-/semver-7.7.4.tgz",
-      "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==",
-      "dev": true,
-      "license": "ISC",
-      "bin": {
-        "semver": "bin/semver.js"
-      },
-      "engines": {
-        "node": ">=10"
-      }
-    },
-    "node_modules/eslint/node_modules/shebang-command": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmmirror.com/shebang-command/-/shebang-command-2.0.0.tgz",
-      "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "shebang-regex": "^3.0.0"
-      },
-      "engines": {
-        "node": ">=8"
-      }
-    },
-    "node_modules/eslint/node_modules/shebang-regex": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmmirror.com/shebang-regex/-/shebang-regex-3.0.0.tgz",
-      "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=8"
-      }
-    },
-    "node_modules/eslint/node_modules/which": {
-      "version": "2.0.2",
-      "resolved": "https://registry.npmmirror.com/which/-/which-2.0.2.tgz",
-      "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
-      "dev": true,
-      "license": "ISC",
-      "dependencies": {
-        "isexe": "^2.0.0"
-      },
-      "bin": {
-        "node-which": "bin/node-which"
-      },
-      "engines": {
-        "node": ">= 8"
-      }
-    },
-    "node_modules/espree": {
-      "version": "7.3.1",
-      "resolved": "https://registry.npmmirror.com/espree/-/espree-7.3.1.tgz",
-      "integrity": "sha512-v3JCNCE64umkFpmkFGqzVKsOT0tN1Zr+ueqLZfpV1Ob8e+CEgPWa+OxCoGH3tnhimMKIaBm4m/vaRpJ/krRz2g==",
-      "dev": true,
-      "license": "BSD-2-Clause",
-      "dependencies": {
-        "acorn": "^7.4.0",
-        "acorn-jsx": "^5.3.1",
-        "eslint-visitor-keys": "^1.3.0"
-      },
-      "engines": {
-        "node": "^10.12.0 || >=12.0.0"
-      }
-    },
-    "node_modules/espree/node_modules/acorn": {
-      "version": "7.4.1",
-      "resolved": "https://registry.npmmirror.com/acorn/-/acorn-7.4.1.tgz",
-      "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==",
-      "dev": true,
-      "license": "MIT",
-      "bin": {
-        "acorn": "bin/acorn"
-      },
-      "engines": {
-        "node": ">=0.4.0"
-      }
-    },
-    "node_modules/espree/node_modules/eslint-visitor-keys": {
-      "version": "1.3.0",
-      "resolved": "https://registry.npmmirror.com/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz",
-      "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==",
-      "dev": true,
-      "license": "Apache-2.0",
-      "engines": {
-        "node": ">=4"
-      }
-    },
-    "node_modules/esprima": {
-      "version": "4.0.1",
-      "resolved": "https://registry.npmmirror.com/esprima/-/esprima-4.0.1.tgz",
-      "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==",
-      "dev": true,
-      "license": "BSD-2-Clause",
-      "bin": {
-        "esparse": "bin/esparse.js",
-        "esvalidate": "bin/esvalidate.js"
-      },
-      "engines": {
-        "node": ">=4"
-      }
-    },
-    "node_modules/esquery": {
-      "version": "1.7.0",
-      "resolved": "https://registry.npmmirror.com/esquery/-/esquery-1.7.0.tgz",
-      "integrity": "sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==",
-      "dev": true,
-      "license": "BSD-3-Clause",
-      "dependencies": {
-        "estraverse": "^5.1.0"
-      },
-      "engines": {
-        "node": ">=0.10"
-      }
-    },
-    "node_modules/esquery/node_modules/estraverse": {
-      "version": "5.3.0",
-      "resolved": "https://registry.npmmirror.com/estraverse/-/estraverse-5.3.0.tgz",
-      "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
-      "dev": true,
-      "license": "BSD-2-Clause",
-      "engines": {
-        "node": ">=4.0"
-      }
-    },
-    "node_modules/esrecurse": {
-      "version": "4.3.0",
-      "resolved": "https://registry.npmmirror.com/esrecurse/-/esrecurse-4.3.0.tgz",
-      "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==",
-      "dev": true,
-      "license": "BSD-2-Clause",
-      "dependencies": {
-        "estraverse": "^5.2.0"
-      },
-      "engines": {
-        "node": ">=4.0"
-      }
-    },
-    "node_modules/esrecurse/node_modules/estraverse": {
-      "version": "5.3.0",
-      "resolved": "https://registry.npmmirror.com/estraverse/-/estraverse-5.3.0.tgz",
-      "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
-      "dev": true,
-      "license": "BSD-2-Clause",
-      "engines": {
-        "node": ">=4.0"
-      }
-    },
-    "node_modules/estraverse": {
-      "version": "4.3.0",
-      "resolved": "https://registry.npmmirror.com/estraverse/-/estraverse-4.3.0.tgz",
-      "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==",
-      "dev": true,
-      "license": "BSD-2-Clause",
-      "engines": {
-        "node": ">=4.0"
-      }
-    },
-    "node_modules/estree-walker": {
-      "version": "2.0.2",
-      "resolved": "https://registry.npmmirror.com/estree-walker/-/estree-walker-2.0.2.tgz",
-      "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==",
-      "dev": true,
-      "license": "MIT"
-    },
-    "node_modules/esutils": {
-      "version": "2.0.3",
-      "resolved": "https://registry.npmmirror.com/esutils/-/esutils-2.0.3.tgz",
-      "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==",
-      "dev": true,
-      "license": "BSD-2-Clause",
-      "engines": {
-        "node": ">=0.10.0"
-      }
-    },
-    "node_modules/etag": {
-      "version": "1.8.1",
-      "resolved": "https://registry.npmmirror.com/etag/-/etag-1.8.1.tgz",
-      "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">= 0.6"
-      }
-    },
-    "node_modules/event-pubsub": {
-      "version": "4.3.0",
-      "resolved": "https://registry.npmmirror.com/event-pubsub/-/event-pubsub-4.3.0.tgz",
-      "integrity": "sha512-z7IyloorXvKbFx9Bpie2+vMJKKx1fH1EN5yiTfp8CiLOTptSYy1g8H4yDpGlEdshL1PBiFtBHepF2cNsqeEeFQ==",
-      "dev": true,
-      "license": "Unlicense",
-      "engines": {
-        "node": ">=4.0.0"
-      }
-    },
-    "node_modules/eventemitter3": {
-      "version": "4.0.7",
-      "resolved": "https://registry.npmmirror.com/eventemitter3/-/eventemitter3-4.0.7.tgz",
-      "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==",
-      "dev": true,
-      "license": "MIT"
-    },
-    "node_modules/events": {
-      "version": "3.3.0",
-      "resolved": "https://registry.npmmirror.com/events/-/events-3.3.0.tgz",
-      "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=0.8.x"
-      }
-    },
-    "node_modules/execa": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmmirror.com/execa/-/execa-1.0.0.tgz",
-      "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "cross-spawn": "^6.0.0",
-        "get-stream": "^4.0.0",
-        "is-stream": "^1.1.0",
-        "npm-run-path": "^2.0.0",
-        "p-finally": "^1.0.0",
-        "signal-exit": "^3.0.0",
-        "strip-eof": "^1.0.0"
-      },
-      "engines": {
-        "node": ">=6"
-      }
-    },
-    "node_modules/express": {
-      "version": "4.22.1",
-      "resolved": "https://registry.npmmirror.com/express/-/express-4.22.1.tgz",
-      "integrity": "sha512-F2X8g9P1X7uCPZMA3MVf9wcTqlyNp7IhH5qPCI0izhaOIYXaW9L535tGA3qmjRzpH+bZczqq7hVKxTR4NWnu+g==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "accepts": "~1.3.8",
-        "array-flatten": "1.1.1",
-        "body-parser": "~1.20.3",
-        "content-disposition": "~0.5.4",
-        "content-type": "~1.0.4",
-        "cookie": "~0.7.1",
-        "cookie-signature": "~1.0.6",
-        "debug": "2.6.9",
-        "depd": "2.0.0",
-        "encodeurl": "~2.0.0",
-        "escape-html": "~1.0.3",
-        "etag": "~1.8.1",
-        "finalhandler": "~1.3.1",
-        "fresh": "~0.5.2",
-        "http-errors": "~2.0.0",
-        "merge-descriptors": "1.0.3",
-        "methods": "~1.1.2",
-        "on-finished": "~2.4.1",
-        "parseurl": "~1.3.3",
-        "path-to-regexp": "~0.1.12",
-        "proxy-addr": "~2.0.7",
-        "qs": "~6.14.0",
-        "range-parser": "~1.2.1",
-        "safe-buffer": "5.2.1",
-        "send": "~0.19.0",
-        "serve-static": "~1.16.2",
-        "setprototypeof": "1.2.0",
-        "statuses": "~2.0.1",
-        "type-is": "~1.6.18",
-        "utils-merge": "1.0.1",
-        "vary": "~1.1.2"
-      },
-      "engines": {
-        "node": ">= 0.10.0"
-      },
-      "funding": {
-        "type": "opencollective",
-        "url": "https://opencollective.com/express"
-      }
-    },
-    "node_modules/express/node_modules/debug": {
-      "version": "2.6.9",
-      "resolved": "https://registry.npmmirror.com/debug/-/debug-2.6.9.tgz",
-      "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "ms": "2.0.0"
-      }
-    },
-    "node_modules/express/node_modules/ms": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmmirror.com/ms/-/ms-2.0.0.tgz",
-      "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
-      "dev": true,
-      "license": "MIT"
-    },
-    "node_modules/extend": {
-      "version": "3.0.2",
-      "resolved": "https://registry.npmmirror.com/extend/-/extend-3.0.2.tgz",
-      "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==",
-      "license": "MIT"
-    },
-    "node_modules/fast-deep-equal": {
-      "version": "3.1.3",
-      "resolved": "https://registry.npmmirror.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
-      "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
-      "dev": true,
-      "license": "MIT"
-    },
-    "node_modules/fast-diff": {
-      "version": "1.1.2",
-      "resolved": "https://registry.npmmirror.com/fast-diff/-/fast-diff-1.1.2.tgz",
-      "integrity": "sha512-KaJUt+M9t1qaIteSvjc6P3RbMdXsNhK61GRftR6SNxqmhthcd9MGIi4T+o0jD8LUSpSnSKXE20nLtJ3fOHxQig==",
-      "license": "Apache-2.0"
-    },
-    "node_modules/fast-glob": {
-      "version": "3.3.3",
-      "resolved": "https://registry.npmmirror.com/fast-glob/-/fast-glob-3.3.3.tgz",
-      "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@nodelib/fs.stat": "^2.0.2",
-        "@nodelib/fs.walk": "^1.2.3",
-        "glob-parent": "^5.1.2",
-        "merge2": "^1.3.0",
-        "micromatch": "^4.0.8"
-      },
-      "engines": {
-        "node": ">=8.6.0"
-      }
-    },
-    "node_modules/fast-glob/node_modules/glob-parent": {
-      "version": "5.1.2",
-      "resolved": "https://registry.npmmirror.com/glob-parent/-/glob-parent-5.1.2.tgz",
-      "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
-      "dev": true,
-      "license": "ISC",
-      "dependencies": {
-        "is-glob": "^4.0.1"
-      },
-      "engines": {
-        "node": ">= 6"
-      }
-    },
-    "node_modules/fast-json-stable-stringify": {
-      "version": "2.1.0",
-      "resolved": "https://registry.npmmirror.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
-      "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==",
-      "dev": true,
-      "license": "MIT"
-    },
-    "node_modules/fast-levenshtein": {
-      "version": "2.0.6",
-      "resolved": "https://registry.npmmirror.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz",
-      "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==",
-      "dev": true,
-      "license": "MIT"
-    },
-    "node_modules/fast-uri": {
-      "version": "3.1.0",
-      "resolved": "https://registry.npmmirror.com/fast-uri/-/fast-uri-3.1.0.tgz",
-      "integrity": "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==",
-      "dev": true,
-      "funding": [
-        {
-          "type": "github",
-          "url": "https://github.com/sponsors/fastify"
-        },
-        {
-          "type": "opencollective",
-          "url": "https://opencollective.com/fastify"
-        }
-      ],
-      "license": "BSD-3-Clause"
-    },
-    "node_modules/fastq": {
-      "version": "1.20.1",
-      "resolved": "https://registry.npmmirror.com/fastq/-/fastq-1.20.1.tgz",
-      "integrity": "sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==",
-      "dev": true,
-      "license": "ISC",
-      "dependencies": {
-        "reusify": "^1.0.4"
-      }
-    },
-    "node_modules/faye-websocket": {
-      "version": "0.11.4",
-      "resolved": "https://registry.npmmirror.com/faye-websocket/-/faye-websocket-0.11.4.tgz",
-      "integrity": "sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g==",
-      "dev": true,
-      "license": "Apache-2.0",
-      "dependencies": {
-        "websocket-driver": ">=0.5.1"
-      },
-      "engines": {
-        "node": ">=0.8.0"
-      }
-    },
-    "node_modules/figures": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmmirror.com/figures/-/figures-2.0.0.tgz",
-      "integrity": "sha512-Oa2M9atig69ZkfwiApY8F2Yy+tzMbazyvqv21R0NsSC8floSOC09BbT1ITWAdoMGQvJ/aZnR1KMwdx9tvHnTNA==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "escape-string-regexp": "^1.0.5"
-      },
-      "engines": {
-        "node": ">=4"
-      }
-    },
-    "node_modules/file-entry-cache": {
-      "version": "6.0.1",
-      "resolved": "https://registry.npmmirror.com/file-entry-cache/-/file-entry-cache-6.0.1.tgz",
-      "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "flat-cache": "^3.0.4"
-      },
-      "engines": {
-        "node": "^10.12.0 || >=12.0.0"
-      }
-    },
-    "node_modules/fill-range": {
-      "version": "7.1.1",
-      "resolved": "https://registry.npmmirror.com/fill-range/-/fill-range-7.1.1.tgz",
-      "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "to-regex-range": "^5.0.1"
-      },
-      "engines": {
-        "node": ">=8"
-      }
-    },
-    "node_modules/finalhandler": {
-      "version": "1.3.2",
-      "resolved": "https://registry.npmmirror.com/finalhandler/-/finalhandler-1.3.2.tgz",
-      "integrity": "sha512-aA4RyPcd3badbdABGDuTXCMTtOneUCAYH/gxoYRTZlIJdF0YPWuGqiAsIrhNnnqdXGswYk6dGujem4w80UJFhg==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "debug": "2.6.9",
-        "encodeurl": "~2.0.0",
-        "escape-html": "~1.0.3",
-        "on-finished": "~2.4.1",
-        "parseurl": "~1.3.3",
-        "statuses": "~2.0.2",
-        "unpipe": "~1.0.0"
-      },
-      "engines": {
-        "node": ">= 0.8"
-      }
-    },
-    "node_modules/finalhandler/node_modules/debug": {
-      "version": "2.6.9",
-      "resolved": "https://registry.npmmirror.com/debug/-/debug-2.6.9.tgz",
-      "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "ms": "2.0.0"
-      }
-    },
-    "node_modules/finalhandler/node_modules/ms": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmmirror.com/ms/-/ms-2.0.0.tgz",
-      "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
-      "dev": true,
-      "license": "MIT"
-    },
-    "node_modules/find-cache-dir": {
-      "version": "3.3.2",
-      "resolved": "https://registry.npmmirror.com/find-cache-dir/-/find-cache-dir-3.3.2.tgz",
-      "integrity": "sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "commondir": "^1.0.1",
-        "make-dir": "^3.0.2",
-        "pkg-dir": "^4.1.0"
-      },
-      "engines": {
-        "node": ">=8"
-      },
-      "funding": {
-        "url": "https://github.com/avajs/find-cache-dir?sponsor=1"
-      }
-    },
-    "node_modules/find-up": {
-      "version": "4.1.0",
-      "resolved": "https://registry.npmmirror.com/find-up/-/find-up-4.1.0.tgz",
-      "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "locate-path": "^5.0.0",
-        "path-exists": "^4.0.0"
-      },
-      "engines": {
-        "node": ">=8"
-      }
-    },
-    "node_modules/flat": {
-      "version": "5.0.2",
-      "resolved": "https://registry.npmmirror.com/flat/-/flat-5.0.2.tgz",
-      "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==",
-      "dev": true,
-      "license": "BSD-3-Clause",
-      "bin": {
-        "flat": "cli.js"
-      }
-    },
-    "node_modules/flat-cache": {
-      "version": "3.2.0",
-      "resolved": "https://registry.npmmirror.com/flat-cache/-/flat-cache-3.2.0.tgz",
-      "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "flatted": "^3.2.9",
-        "keyv": "^4.5.3",
-        "rimraf": "^3.0.2"
-      },
-      "engines": {
-        "node": "^10.12.0 || >=12.0.0"
-      }
-    },
-    "node_modules/flatted": {
-      "version": "3.4.2",
-      "resolved": "https://registry.npmmirror.com/flatted/-/flatted-3.4.2.tgz",
-      "integrity": "sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==",
-      "dev": true,
-      "license": "ISC"
-    },
-    "node_modules/follow-redirects": {
-      "version": "1.16.0",
-      "resolved": "https://registry.npmmirror.com/follow-redirects/-/follow-redirects-1.16.0.tgz",
-      "integrity": "sha512-y5rN/uOsadFT/JfYwhxRS5R7Qce+g3zG97+JrtFZlC9klX/W5hD7iiLzScI4nZqUS7DNUdhPgw4xI8W2LuXlUw==",
-      "funding": [
-        {
-          "type": "individual",
-          "url": "https://github.com/sponsors/RubenVerborgh"
-        }
-      ],
-      "license": "MIT",
-      "engines": {
-        "node": ">=4.0"
-      },
-      "peerDependenciesMeta": {
-        "debug": {
-          "optional": true
-        }
-      }
-    },
-    "node_modules/form-data": {
-      "version": "4.0.5",
-      "resolved": "https://registry.npmmirror.com/form-data/-/form-data-4.0.5.tgz",
-      "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==",
-      "license": "MIT",
-      "dependencies": {
-        "asynckit": "^0.4.0",
-        "combined-stream": "^1.0.8",
-        "es-set-tostringtag": "^2.1.0",
-        "hasown": "^2.0.2",
-        "mime-types": "^2.1.12"
-      },
-      "engines": {
-        "node": ">= 6"
-      }
-    },
-    "node_modules/forwarded": {
-      "version": "0.2.0",
-      "resolved": "https://registry.npmmirror.com/forwarded/-/forwarded-0.2.0.tgz",
-      "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">= 0.6"
-      }
-    },
-    "node_modules/fraction.js": {
-      "version": "5.3.4",
-      "resolved": "https://registry.npmmirror.com/fraction.js/-/fraction.js-5.3.4.tgz",
-      "integrity": "sha512-1X1NTtiJphryn/uLQz3whtY6jK3fTqoE3ohKs0tT+Ujr1W59oopxmoEh7Lu5p6vBaPbgoM0bzveAW4Qi5RyWDQ==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": "*"
-      },
-      "funding": {
-        "type": "github",
-        "url": "https://github.com/sponsors/rawify"
-      }
-    },
-    "node_modules/fresh": {
-      "version": "0.5.2",
-      "resolved": "https://registry.npmmirror.com/fresh/-/fresh-0.5.2.tgz",
-      "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">= 0.6"
-      }
-    },
-    "node_modules/fs-extra": {
-      "version": "9.1.0",
-      "resolved": "https://registry.npmmirror.com/fs-extra/-/fs-extra-9.1.0.tgz",
-      "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "at-least-node": "^1.0.0",
-        "graceful-fs": "^4.2.0",
-        "jsonfile": "^6.0.1",
-        "universalify": "^2.0.0"
-      },
-      "engines": {
-        "node": ">=10"
-      }
-    },
-    "node_modules/fs-monkey": {
-      "version": "1.1.0",
-      "resolved": "https://registry.npmmirror.com/fs-monkey/-/fs-monkey-1.1.0.tgz",
-      "integrity": "sha512-QMUezzXWII9EV5aTFXW1UBVUO77wYPpjqIF8/AviUCThNeSYZykpoTixUeaNNBwmCev0AMDWMAni+f8Hxb1IFw==",
-      "dev": true,
-      "license": "Unlicense"
-    },
-    "node_modules/fs.realpath": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmmirror.com/fs.realpath/-/fs.realpath-1.0.0.tgz",
-      "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==",
-      "dev": true,
-      "license": "ISC"
-    },
-    "node_modules/fsevents": {
-      "version": "2.3.3",
-      "resolved": "https://registry.npmmirror.com/fsevents/-/fsevents-2.3.3.tgz",
-      "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
-      "dev": true,
-      "hasInstallScript": true,
-      "license": "MIT",
-      "optional": true,
-      "os": [
-        "darwin"
-      ],
-      "engines": {
-        "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
-      }
-    },
-    "node_modules/function-bind": {
-      "version": "1.1.2",
-      "resolved": "https://registry.npmmirror.com/function-bind/-/function-bind-1.1.2.tgz",
-      "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
-      "license": "MIT",
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
-    "node_modules/functional-red-black-tree": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmmirror.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz",
-      "integrity": "sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g==",
-      "dev": true,
-      "license": "MIT"
-    },
-    "node_modules/functions-have-names": {
-      "version": "1.2.3",
-      "resolved": "https://registry.npmmirror.com/functions-have-names/-/functions-have-names-1.2.3.tgz",
-      "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==",
-      "license": "MIT",
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
-    "node_modules/gensync": {
-      "version": "1.0.0-beta.2",
-      "resolved": "https://registry.npmmirror.com/gensync/-/gensync-1.0.0-beta.2.tgz",
-      "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=6.9.0"
-      }
-    },
-    "node_modules/get-caller-file": {
-      "version": "2.0.5",
-      "resolved": "https://registry.npmmirror.com/get-caller-file/-/get-caller-file-2.0.5.tgz",
-      "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
-      "dev": true,
-      "license": "ISC",
-      "engines": {
-        "node": "6.* || 8.* || >= 10.*"
-      }
-    },
-    "node_modules/get-intrinsic": {
-      "version": "1.3.0",
-      "resolved": "https://registry.npmmirror.com/get-intrinsic/-/get-intrinsic-1.3.0.tgz",
-      "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==",
-      "license": "MIT",
-      "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"
-      },
-      "engines": {
-        "node": ">= 0.4"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
-    "node_modules/get-proto": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmmirror.com/get-proto/-/get-proto-1.0.1.tgz",
-      "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==",
-      "license": "MIT",
-      "dependencies": {
-        "dunder-proto": "^1.0.1",
-        "es-object-atoms": "^1.0.0"
-      },
-      "engines": {
-        "node": ">= 0.4"
-      }
-    },
-    "node_modules/get-stream": {
-      "version": "4.1.0",
-      "resolved": "https://registry.npmmirror.com/get-stream/-/get-stream-4.1.0.tgz",
-      "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "pump": "^3.0.0"
-      },
-      "engines": {
-        "node": ">=6"
-      }
-    },
-    "node_modules/glob": {
-      "version": "7.2.3",
-      "resolved": "https://registry.npmmirror.com/glob/-/glob-7.2.3.tgz",
-      "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
-      "deprecated": "Glob versions prior to v9 are no longer supported",
-      "dev": true,
-      "license": "ISC",
-      "dependencies": {
-        "fs.realpath": "^1.0.0",
-        "inflight": "^1.0.4",
-        "inherits": "2",
-        "minimatch": "^3.1.1",
-        "once": "^1.3.0",
-        "path-is-absolute": "^1.0.0"
-      },
-      "engines": {
-        "node": "*"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/isaacs"
-      }
-    },
-    "node_modules/glob-parent": {
-      "version": "6.0.2",
-      "resolved": "https://registry.npmmirror.com/glob-parent/-/glob-parent-6.0.2.tgz",
-      "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==",
-      "dev": true,
-      "license": "ISC",
-      "dependencies": {
-        "is-glob": "^4.0.3"
-      },
-      "engines": {
-        "node": ">=10.13.0"
-      }
-    },
-    "node_modules/glob-to-regexp": {
-      "version": "0.4.1",
-      "resolved": "https://registry.npmmirror.com/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz",
-      "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==",
-      "dev": true,
-      "license": "BSD-2-Clause"
-    },
-    "node_modules/globals": {
-      "version": "13.24.0",
-      "resolved": "https://registry.npmmirror.com/globals/-/globals-13.24.0.tgz",
-      "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "type-fest": "^0.20.2"
-      },
-      "engines": {
-        "node": ">=8"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/sindresorhus"
-      }
-    },
-    "node_modules/globals/node_modules/type-fest": {
-      "version": "0.20.2",
-      "resolved": "https://registry.npmmirror.com/type-fest/-/type-fest-0.20.2.tgz",
-      "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==",
-      "dev": true,
-      "license": "(MIT OR CC0-1.0)",
-      "engines": {
-        "node": ">=10"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/sindresorhus"
-      }
-    },
-    "node_modules/globby": {
-      "version": "11.1.0",
-      "resolved": "https://registry.npmmirror.com/globby/-/globby-11.1.0.tgz",
-      "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "array-union": "^2.1.0",
-        "dir-glob": "^3.0.1",
-        "fast-glob": "^3.2.9",
-        "ignore": "^5.2.0",
-        "merge2": "^1.4.1",
-        "slash": "^3.0.0"
-      },
-      "engines": {
-        "node": ">=10"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/sindresorhus"
-      }
-    },
-    "node_modules/gopd": {
-      "version": "1.2.0",
-      "resolved": "https://registry.npmmirror.com/gopd/-/gopd-1.2.0.tgz",
-      "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==",
-      "license": "MIT",
-      "engines": {
-        "node": ">= 0.4"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
-    "node_modules/graceful-fs": {
-      "version": "4.2.11",
-      "resolved": "https://registry.npmmirror.com/graceful-fs/-/graceful-fs-4.2.11.tgz",
-      "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==",
-      "dev": true,
-      "license": "ISC"
-    },
-    "node_modules/gzip-size": {
-      "version": "6.0.0",
-      "resolved": "https://registry.npmmirror.com/gzip-size/-/gzip-size-6.0.0.tgz",
-      "integrity": "sha512-ax7ZYomf6jqPTQ4+XCpUGyXKHk5WweS+e05MBO4/y3WJ5RkmPXNKvX+bx1behVILVwr6JSQvZAku021CHPXG3Q==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "duplexer": "^0.1.2"
-      },
-      "engines": {
-        "node": ">=10"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/sindresorhus"
-      }
-    },
-    "node_modules/handle-thing": {
-      "version": "2.0.1",
-      "resolved": "https://registry.npmmirror.com/handle-thing/-/handle-thing-2.0.1.tgz",
-      "integrity": "sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg==",
-      "dev": true,
-      "license": "MIT"
-    },
-    "node_modules/has-flag": {
-      "version": "4.0.0",
-      "resolved": "https://registry.npmmirror.com/has-flag/-/has-flag-4.0.0.tgz",
-      "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=8"
-      }
-    },
-    "node_modules/has-property-descriptors": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmmirror.com/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz",
-      "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==",
-      "license": "MIT",
-      "dependencies": {
-        "es-define-property": "^1.0.0"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
-    "node_modules/has-symbols": {
-      "version": "1.1.0",
-      "resolved": "https://registry.npmmirror.com/has-symbols/-/has-symbols-1.1.0.tgz",
-      "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==",
-      "license": "MIT",
-      "engines": {
-        "node": ">= 0.4"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
-    "node_modules/has-tostringtag": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmmirror.com/has-tostringtag/-/has-tostringtag-1.0.2.tgz",
-      "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==",
-      "license": "MIT",
-      "dependencies": {
-        "has-symbols": "^1.0.3"
-      },
-      "engines": {
-        "node": ">= 0.4"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
-    "node_modules/hash-sum": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmmirror.com/hash-sum/-/hash-sum-2.0.0.tgz",
-      "integrity": "sha512-WdZTbAByD+pHfl/g9QSsBIIwy8IT+EsPiKDs0KNX+zSHhdDLFKdZu0BQHljvO+0QI/BasbMSUa8wYNCZTvhslg==",
-      "dev": true,
-      "license": "MIT"
-    },
-    "node_modules/hasown": {
-      "version": "2.0.2",
-      "resolved": "https://registry.npmmirror.com/hasown/-/hasown-2.0.2.tgz",
-      "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
-      "license": "MIT",
-      "dependencies": {
-        "function-bind": "^1.1.2"
-      },
-      "engines": {
-        "node": ">= 0.4"
-      }
-    },
-    "node_modules/he": {
-      "version": "1.2.0",
-      "resolved": "https://registry.npmmirror.com/he/-/he-1.2.0.tgz",
-      "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==",
-      "dev": true,
-      "license": "MIT",
-      "bin": {
-        "he": "bin/he"
-      }
-    },
-    "node_modules/highlight.js": {
-      "version": "10.7.3",
-      "resolved": "https://registry.npmmirror.com/highlight.js/-/highlight.js-10.7.3.tgz",
-      "integrity": "sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==",
-      "dev": true,
-      "license": "BSD-3-Clause",
-      "engines": {
-        "node": "*"
-      }
-    },
-    "node_modules/hosted-git-info": {
-      "version": "2.8.9",
-      "resolved": "https://registry.npmmirror.com/hosted-git-info/-/hosted-git-info-2.8.9.tgz",
-      "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==",
-      "dev": true,
-      "license": "ISC"
-    },
-    "node_modules/hpack.js": {
-      "version": "2.1.6",
-      "resolved": "https://registry.npmmirror.com/hpack.js/-/hpack.js-2.1.6.tgz",
-      "integrity": "sha512-zJxVehUdMGIKsRaNt7apO2Gqp0BdqW5yaiGHXXmbpvxgBYVZnAql+BJb4RO5ad2MgpbZKn5G6nMnegrH1FcNYQ==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "inherits": "^2.0.1",
-        "obuf": "^1.0.0",
-        "readable-stream": "^2.0.1",
-        "wbuf": "^1.1.0"
-      }
-    },
-    "node_modules/hpack.js/node_modules/readable-stream": {
-      "version": "2.3.8",
-      "resolved": "https://registry.npmmirror.com/readable-stream/-/readable-stream-2.3.8.tgz",
-      "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "core-util-is": "~1.0.0",
-        "inherits": "~2.0.3",
-        "isarray": "~1.0.0",
-        "process-nextick-args": "~2.0.0",
-        "safe-buffer": "~5.1.1",
-        "string_decoder": "~1.1.1",
-        "util-deprecate": "~1.0.1"
-      }
-    },
-    "node_modules/hpack.js/node_modules/safe-buffer": {
-      "version": "5.1.2",
-      "resolved": "https://registry.npmmirror.com/safe-buffer/-/safe-buffer-5.1.2.tgz",
-      "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
-      "dev": true,
-      "license": "MIT"
-    },
-    "node_modules/hpack.js/node_modules/string_decoder": {
-      "version": "1.1.1",
-      "resolved": "https://registry.npmmirror.com/string_decoder/-/string_decoder-1.1.1.tgz",
-      "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "safe-buffer": "~5.1.0"
-      }
-    },
-    "node_modules/html-entities": {
-      "version": "2.6.0",
-      "resolved": "https://registry.npmmirror.com/html-entities/-/html-entities-2.6.0.tgz",
-      "integrity": "sha512-kig+rMn/QOVRvr7c86gQ8lWXq+Hkv6CbAH1hLu+RG338StTpE8Z0b44SDVaqVu7HGKf27frdmUYEs9hTUX/cLQ==",
-      "dev": true,
-      "funding": [
-        {
-          "type": "github",
-          "url": "https://github.com/sponsors/mdevils"
-        },
-        {
-          "type": "patreon",
-          "url": "https://patreon.com/mdevils"
-        }
-      ],
-      "license": "MIT"
-    },
-    "node_modules/html-escaper": {
-      "version": "2.0.2",
-      "resolved": "https://registry.npmmirror.com/html-escaper/-/html-escaper-2.0.2.tgz",
-      "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==",
-      "dev": true,
-      "license": "MIT"
-    },
-    "node_modules/html-minifier-terser": {
-      "version": "6.1.0",
-      "resolved": "https://registry.npmmirror.com/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz",
-      "integrity": "sha512-YXxSlJBZTP7RS3tWnQw74ooKa6L9b9i9QYXY21eUEvhZ3u9XLfv6OnFsQq6RxkhHygsaUMvYsZRV5rU/OVNZxw==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "camel-case": "^4.1.2",
-        "clean-css": "^5.2.2",
-        "commander": "^8.3.0",
-        "he": "^1.2.0",
-        "param-case": "^3.0.4",
-        "relateurl": "^0.2.7",
-        "terser": "^5.10.0"
-      },
-      "bin": {
-        "html-minifier-terser": "cli.js"
-      },
-      "engines": {
-        "node": ">=12"
-      }
-    },
-    "node_modules/html-tags": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmmirror.com/html-tags/-/html-tags-2.0.0.tgz",
-      "integrity": "sha512-+Il6N8cCo2wB/Vd3gqy/8TZhTD3QvcVeQLCnZiGkGCH3JP28IgGAY41giccp2W4R3jfyJPAP318FQTa1yU7K7g==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=4"
-      }
-    },
-    "node_modules/html-webpack-plugin": {
-      "version": "5.6.6",
-      "resolved": "https://registry.npmmirror.com/html-webpack-plugin/-/html-webpack-plugin-5.6.6.tgz",
-      "integrity": "sha512-bLjW01UTrvoWTJQL5LsMRo1SypHW80FTm12OJRSnr3v6YHNhfe+1r0MYUZJMACxnCHURVnBWRwAsWs2yPU9Ezw==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@types/html-minifier-terser": "^6.0.0",
-        "html-minifier-terser": "^6.0.2",
-        "lodash": "^4.17.21",
-        "pretty-error": "^4.0.0",
-        "tapable": "^2.0.0"
-      },
-      "engines": {
-        "node": ">=10.13.0"
-      },
-      "funding": {
-        "type": "opencollective",
-        "url": "https://opencollective.com/html-webpack-plugin"
-      },
-      "peerDependencies": {
-        "@rspack/core": "0.x || 1.x",
-        "webpack": "^5.20.0"
-      },
-      "peerDependenciesMeta": {
-        "@rspack/core": {
-          "optional": true
-        },
-        "webpack": {
-          "optional": true
-        }
-      }
-    },
-    "node_modules/htmlparser2": {
-      "version": "6.1.0",
-      "resolved": "https://registry.npmmirror.com/htmlparser2/-/htmlparser2-6.1.0.tgz",
-      "integrity": "sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A==",
-      "dev": true,
-      "funding": [
-        "https://github.com/fb55/htmlparser2?sponsor=1",
-        {
-          "type": "github",
-          "url": "https://github.com/sponsors/fb55"
-        }
-      ],
-      "license": "MIT",
-      "dependencies": {
-        "domelementtype": "^2.0.1",
-        "domhandler": "^4.0.0",
-        "domutils": "^2.5.2",
-        "entities": "^2.0.0"
-      }
-    },
-    "node_modules/htmlparser2/node_modules/entities": {
-      "version": "2.2.0",
-      "resolved": "https://registry.npmmirror.com/entities/-/entities-2.2.0.tgz",
-      "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==",
-      "dev": true,
-      "license": "BSD-2-Clause",
-      "funding": {
-        "url": "https://github.com/fb55/entities?sponsor=1"
-      }
-    },
-    "node_modules/http-deceiver": {
-      "version": "1.2.7",
-      "resolved": "https://registry.npmmirror.com/http-deceiver/-/http-deceiver-1.2.7.tgz",
-      "integrity": "sha512-LmpOGxTfbpgtGVxJrj5k7asXHCgNZp5nLfp+hWc8QQRqtb7fUy6kRY3BO1h9ddF6yIPYUARgxGOwB42DnxIaNw==",
-      "dev": true,
-      "license": "MIT"
-    },
-    "node_modules/http-errors": {
-      "version": "2.0.1",
-      "resolved": "https://registry.npmmirror.com/http-errors/-/http-errors-2.0.1.tgz",
-      "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "depd": "~2.0.0",
-        "inherits": "~2.0.4",
-        "setprototypeof": "~1.2.0",
-        "statuses": "~2.0.2",
-        "toidentifier": "~1.0.1"
-      },
-      "engines": {
-        "node": ">= 0.8"
-      },
-      "funding": {
-        "type": "opencollective",
-        "url": "https://opencollective.com/express"
-      }
-    },
-    "node_modules/http-parser-js": {
-      "version": "0.5.10",
-      "resolved": "https://registry.npmmirror.com/http-parser-js/-/http-parser-js-0.5.10.tgz",
-      "integrity": "sha512-Pysuw9XpUq5dVc/2SMHpuTY01RFl8fttgcyunjL7eEMhGM3cI4eOmiCycJDVCo/7O7ClfQD3SaI6ftDzqOXYMA==",
-      "dev": true,
-      "license": "MIT"
-    },
-    "node_modules/http-proxy": {
-      "version": "1.18.1",
-      "resolved": "https://registry.npmmirror.com/http-proxy/-/http-proxy-1.18.1.tgz",
-      "integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "eventemitter3": "^4.0.0",
-        "follow-redirects": "^1.0.0",
-        "requires-port": "^1.0.0"
-      },
-      "engines": {
-        "node": ">=8.0.0"
-      }
-    },
-    "node_modules/http-proxy-middleware": {
-      "version": "2.0.9",
-      "resolved": "https://registry.npmmirror.com/http-proxy-middleware/-/http-proxy-middleware-2.0.9.tgz",
-      "integrity": "sha512-c1IyJYLYppU574+YI7R4QyX2ystMtVXZwIdzazUIPIJsHuWNd+mho2j+bKoHftndicGj9yh+xjd+l0yj7VeT1Q==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@types/http-proxy": "^1.17.8",
-        "http-proxy": "^1.18.1",
-        "is-glob": "^4.0.1",
-        "is-plain-obj": "^3.0.0",
-        "micromatch": "^4.0.2"
-      },
-      "engines": {
-        "node": ">=12.0.0"
-      },
-      "peerDependencies": {
-        "@types/express": "^4.17.13"
-      },
-      "peerDependenciesMeta": {
-        "@types/express": {
-          "optional": true
-        }
-      }
-    },
-    "node_modules/human-signals": {
-      "version": "2.1.0",
-      "resolved": "https://registry.npmmirror.com/human-signals/-/human-signals-2.1.0.tgz",
-      "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==",
-      "dev": true,
-      "license": "Apache-2.0",
-      "engines": {
-        "node": ">=10.17.0"
-      }
-    },
-    "node_modules/iconv-lite": {
-      "version": "0.4.24",
-      "resolved": "https://registry.npmmirror.com/iconv-lite/-/iconv-lite-0.4.24.tgz",
-      "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "safer-buffer": ">= 2.1.2 < 3"
-      },
-      "engines": {
-        "node": ">=0.10.0"
-      }
-    },
-    "node_modules/icss-utils": {
-      "version": "5.1.0",
-      "resolved": "https://registry.npmmirror.com/icss-utils/-/icss-utils-5.1.0.tgz",
-      "integrity": "sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==",
-      "dev": true,
-      "license": "ISC",
-      "engines": {
-        "node": "^10 || ^12 || >= 14"
-      },
-      "peerDependencies": {
-        "postcss": "^8.1.0"
-      }
-    },
-    "node_modules/ieee754": {
-      "version": "1.2.1",
-      "resolved": "https://registry.npmmirror.com/ieee754/-/ieee754-1.2.1.tgz",
-      "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==",
-      "dev": true,
-      "funding": [
-        {
-          "type": "github",
-          "url": "https://github.com/sponsors/feross"
-        },
-        {
-          "type": "patreon",
-          "url": "https://www.patreon.com/feross"
-        },
-        {
-          "type": "consulting",
-          "url": "https://feross.org/support"
-        }
-      ],
-      "license": "BSD-3-Clause"
-    },
-    "node_modules/ignore": {
-      "version": "5.3.2",
-      "resolved": "https://registry.npmmirror.com/ignore/-/ignore-5.3.2.tgz",
-      "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">= 4"
-      }
-    },
-    "node_modules/import-fresh": {
-      "version": "3.3.1",
-      "resolved": "https://registry.npmmirror.com/import-fresh/-/import-fresh-3.3.1.tgz",
-      "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "parent-module": "^1.0.0",
-        "resolve-from": "^4.0.0"
-      },
-      "engines": {
-        "node": ">=6"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/sindresorhus"
-      }
-    },
-    "node_modules/imurmurhash": {
-      "version": "0.1.4",
-      "resolved": "https://registry.npmmirror.com/imurmurhash/-/imurmurhash-0.1.4.tgz",
-      "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=0.8.19"
-      }
-    },
-    "node_modules/inflight": {
-      "version": "1.0.6",
-      "resolved": "https://registry.npmmirror.com/inflight/-/inflight-1.0.6.tgz",
-      "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==",
-      "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.",
-      "dev": true,
-      "license": "ISC",
-      "dependencies": {
-        "once": "^1.3.0",
-        "wrappy": "1"
-      }
-    },
-    "node_modules/inherits": {
-      "version": "2.0.4",
-      "resolved": "https://registry.npmmirror.com/inherits/-/inherits-2.0.4.tgz",
-      "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
-      "dev": true,
-      "license": "ISC"
-    },
-    "node_modules/ipaddr.js": {
-      "version": "2.3.0",
-      "resolved": "https://registry.npmmirror.com/ipaddr.js/-/ipaddr.js-2.3.0.tgz",
-      "integrity": "sha512-Zv/pA+ciVFbCSBBjGfaKUya/CcGmUHzTydLMaTwrUUEM2DIEO3iZvueGxmacvmN50fGpGVKeTXpb2LcYQxeVdg==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">= 10"
-      }
-    },
-    "node_modules/is-arguments": {
-      "version": "1.2.0",
-      "resolved": "https://registry.npmmirror.com/is-arguments/-/is-arguments-1.2.0.tgz",
-      "integrity": "sha512-7bVbi0huj/wrIAOzb8U1aszg9kdi3KN/CyU19CTI7tAoZYEZoL9yCDXpbXN+uPsuWnP02cyug1gleqq+TU+YCA==",
-      "license": "MIT",
-      "dependencies": {
-        "call-bound": "^1.0.2",
-        "has-tostringtag": "^1.0.2"
-      },
-      "engines": {
-        "node": ">= 0.4"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
-    "node_modules/is-arrayish": {
-      "version": "0.2.1",
-      "resolved": "https://registry.npmmirror.com/is-arrayish/-/is-arrayish-0.2.1.tgz",
-      "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==",
-      "dev": true,
-      "license": "MIT"
-    },
-    "node_modules/is-binary-path": {
-      "version": "2.1.0",
-      "resolved": "https://registry.npmmirror.com/is-binary-path/-/is-binary-path-2.1.0.tgz",
-      "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "binary-extensions": "^2.0.0"
-      },
-      "engines": {
-        "node": ">=8"
-      }
-    },
-    "node_modules/is-ci": {
-      "version": "1.2.1",
-      "resolved": "https://registry.npmmirror.com/is-ci/-/is-ci-1.2.1.tgz",
-      "integrity": "sha512-s6tfsaQaQi3JNciBH6shVqEDvhGut0SUXr31ag8Pd8BBbVVlcGfWhpPmEOoM6RJ5TFhbypvf5yyRw/VXW1IiWg==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "ci-info": "^1.5.0"
-      },
-      "bin": {
-        "is-ci": "bin.js"
-      }
-    },
-    "node_modules/is-core-module": {
-      "version": "2.16.1",
-      "resolved": "https://registry.npmmirror.com/is-core-module/-/is-core-module-2.16.1.tgz",
-      "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "hasown": "^2.0.2"
-      },
-      "engines": {
-        "node": ">= 0.4"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
-    "node_modules/is-date-object": {
-      "version": "1.1.0",
-      "resolved": "https://registry.npmmirror.com/is-date-object/-/is-date-object-1.1.0.tgz",
-      "integrity": "sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==",
-      "license": "MIT",
-      "dependencies": {
-        "call-bound": "^1.0.2",
-        "has-tostringtag": "^1.0.2"
-      },
-      "engines": {
-        "node": ">= 0.4"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
-    "node_modules/is-docker": {
-      "version": "2.2.1",
-      "resolved": "https://registry.npmmirror.com/is-docker/-/is-docker-2.2.1.tgz",
-      "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==",
-      "dev": true,
-      "license": "MIT",
-      "bin": {
-        "is-docker": "cli.js"
-      },
-      "engines": {
-        "node": ">=8"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/sindresorhus"
-      }
-    },
-    "node_modules/is-extglob": {
-      "version": "2.1.1",
-      "resolved": "https://registry.npmmirror.com/is-extglob/-/is-extglob-2.1.1.tgz",
-      "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=0.10.0"
-      }
-    },
-    "node_modules/is-file-esm": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmmirror.com/is-file-esm/-/is-file-esm-1.0.0.tgz",
-      "integrity": "sha512-rZlaNKb4Mr8WlRu2A9XdeoKgnO5aA53XdPHgCKVyCrQ/rWi89RET1+bq37Ru46obaQXeiX4vmFIm1vks41hoSA==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "read-pkg-up": "^7.0.1"
-      }
-    },
-    "node_modules/is-fullwidth-code-point": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmmirror.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
-      "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=8"
-      }
-    },
-    "node_modules/is-glob": {
-      "version": "4.0.3",
-      "resolved": "https://registry.npmmirror.com/is-glob/-/is-glob-4.0.3.tgz",
-      "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "is-extglob": "^2.1.1"
-      },
-      "engines": {
-        "node": ">=0.10.0"
-      }
-    },
-    "node_modules/is-interactive": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmmirror.com/is-interactive/-/is-interactive-1.0.0.tgz",
-      "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=8"
-      }
-    },
-    "node_modules/is-number": {
-      "version": "7.0.0",
-      "resolved": "https://registry.npmmirror.com/is-number/-/is-number-7.0.0.tgz",
-      "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=0.12.0"
-      }
-    },
-    "node_modules/is-plain-obj": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmmirror.com/is-plain-obj/-/is-plain-obj-3.0.0.tgz",
-      "integrity": "sha512-gwsOE28k+23GP1B6vFl1oVh/WOzmawBrKwo5Ev6wMKzPkaXaCDIQKzLnvsA42DRlbVTWorkgTKIviAKCWkfUwA==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=10"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/sindresorhus"
-      }
-    },
-    "node_modules/is-plain-object": {
-      "version": "2.0.4",
-      "resolved": "https://registry.npmmirror.com/is-plain-object/-/is-plain-object-2.0.4.tgz",
-      "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "isobject": "^3.0.1"
-      },
-      "engines": {
-        "node": ">=0.10.0"
-      }
-    },
-    "node_modules/is-regex": {
-      "version": "1.2.1",
-      "resolved": "https://registry.npmmirror.com/is-regex/-/is-regex-1.2.1.tgz",
-      "integrity": "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==",
-      "license": "MIT",
-      "dependencies": {
-        "call-bound": "^1.0.2",
-        "gopd": "^1.2.0",
-        "has-tostringtag": "^1.0.2",
-        "hasown": "^2.0.2"
-      },
-      "engines": {
-        "node": ">= 0.4"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
-    "node_modules/is-stream": {
-      "version": "1.1.0",
-      "resolved": "https://registry.npmmirror.com/is-stream/-/is-stream-1.1.0.tgz",
-      "integrity": "sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=0.10.0"
-      }
-    },
-    "node_modules/is-unicode-supported": {
-      "version": "0.1.0",
-      "resolved": "https://registry.npmmirror.com/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz",
-      "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=10"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/sindresorhus"
-      }
-    },
-    "node_modules/is-wsl": {
-      "version": "2.2.0",
-      "resolved": "https://registry.npmmirror.com/is-wsl/-/is-wsl-2.2.0.tgz",
-      "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "is-docker": "^2.0.0"
-      },
-      "engines": {
-        "node": ">=8"
-      }
-    },
-    "node_modules/isarray": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmmirror.com/isarray/-/isarray-1.0.0.tgz",
-      "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==",
-      "dev": true,
-      "license": "MIT"
-    },
-    "node_modules/isexe": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmmirror.com/isexe/-/isexe-2.0.0.tgz",
-      "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
-      "dev": true,
-      "license": "ISC"
-    },
-    "node_modules/isobject": {
-      "version": "3.0.1",
-      "resolved": "https://registry.npmmirror.com/isobject/-/isobject-3.0.1.tgz",
-      "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=0.10.0"
-      }
-    },
-    "node_modules/javascript-stringify": {
-      "version": "2.1.0",
-      "resolved": "https://registry.npmmirror.com/javascript-stringify/-/javascript-stringify-2.1.0.tgz",
-      "integrity": "sha512-JVAfqNPTvNq3sB/VHQJAFxN/sPgKnsKrCwyRt15zwNCdrMMJDdcEOdubuy+DuJYYdm0ox1J4uzEuYKkN+9yhVg==",
-      "dev": true,
-      "license": "MIT"
-    },
-    "node_modules/jest-worker": {
-      "version": "27.5.1",
-      "resolved": "https://registry.npmmirror.com/jest-worker/-/jest-worker-27.5.1.tgz",
-      "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@types/node": "*",
-        "merge-stream": "^2.0.0",
-        "supports-color": "^8.0.0"
-      },
-      "engines": {
-        "node": ">= 10.13.0"
-      }
-    },
-    "node_modules/jest-worker/node_modules/supports-color": {
-      "version": "8.1.1",
-      "resolved": "https://registry.npmmirror.com/supports-color/-/supports-color-8.1.1.tgz",
-      "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "has-flag": "^4.0.0"
-      },
-      "engines": {
-        "node": ">=10"
-      },
-      "funding": {
-        "url": "https://github.com/chalk/supports-color?sponsor=1"
-      }
-    },
-    "node_modules/joi": {
-      "version": "17.13.3",
-      "resolved": "https://registry.npmmirror.com/joi/-/joi-17.13.3.tgz",
-      "integrity": "sha512-otDA4ldcIx+ZXsKHWmp0YizCweVRZG96J10b0FevjfuncLO1oX59THoAmHkNubYJ+9gWsYsp5k8v4ib6oDv1fA==",
-      "dev": true,
-      "license": "BSD-3-Clause",
-      "dependencies": {
-        "@hapi/hoek": "^9.3.0",
-        "@hapi/topo": "^5.1.0",
-        "@sideway/address": "^4.1.5",
-        "@sideway/formula": "^3.0.1",
-        "@sideway/pinpoint": "^2.0.0"
-      }
-    },
-    "node_modules/js-message": {
-      "version": "1.0.7",
-      "resolved": "https://registry.npmmirror.com/js-message/-/js-message-1.0.7.tgz",
-      "integrity": "sha512-efJLHhLjIyKRewNS9EGZ4UpI8NguuL6fKkhRxVuMmrGV2xN/0APGdQYwLFky5w9naebSZ0OwAGp0G6/2Cg90rA==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=0.6.0"
-      }
-    },
-    "node_modules/js-tokens": {
-      "version": "4.0.0",
-      "resolved": "https://registry.npmmirror.com/js-tokens/-/js-tokens-4.0.0.tgz",
-      "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
-      "dev": true,
-      "license": "MIT"
-    },
-    "node_modules/js-yaml": {
-      "version": "3.14.2",
-      "resolved": "https://registry.npmmirror.com/js-yaml/-/js-yaml-3.14.2.tgz",
-      "integrity": "sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "argparse": "^1.0.7",
-        "esprima": "^4.0.0"
-      },
-      "bin": {
-        "js-yaml": "bin/js-yaml.js"
-      }
-    },
-    "node_modules/jsesc": {
-      "version": "3.1.0",
-      "resolved": "https://registry.npmmirror.com/jsesc/-/jsesc-3.1.0.tgz",
-      "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==",
-      "dev": true,
-      "license": "MIT",
-      "bin": {
-        "jsesc": "bin/jsesc"
-      },
-      "engines": {
-        "node": ">=6"
-      }
-    },
-    "node_modules/json-buffer": {
-      "version": "3.0.1",
-      "resolved": "https://registry.npmmirror.com/json-buffer/-/json-buffer-3.0.1.tgz",
-      "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==",
-      "dev": true,
-      "license": "MIT"
-    },
-    "node_modules/json-parse-better-errors": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmmirror.com/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz",
-      "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==",
-      "dev": true,
-      "license": "MIT"
-    },
-    "node_modules/json-parse-even-better-errors": {
-      "version": "2.3.1",
-      "resolved": "https://registry.npmmirror.com/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz",
-      "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==",
-      "dev": true,
-      "license": "MIT"
-    },
-    "node_modules/json-schema-traverse": {
-      "version": "0.4.1",
-      "resolved": "https://registry.npmmirror.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
-      "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
-      "dev": true,
-      "license": "MIT"
-    },
-    "node_modules/json-stable-stringify-without-jsonify": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmmirror.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz",
-      "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==",
-      "dev": true,
-      "license": "MIT"
-    },
-    "node_modules/json5": {
-      "version": "2.2.3",
-      "resolved": "https://registry.npmmirror.com/json5/-/json5-2.2.3.tgz",
-      "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==",
-      "dev": true,
-      "license": "MIT",
-      "bin": {
-        "json5": "lib/cli.js"
-      },
-      "engines": {
-        "node": ">=6"
-      }
-    },
-    "node_modules/jsonfile": {
-      "version": "6.2.0",
-      "resolved": "https://registry.npmmirror.com/jsonfile/-/jsonfile-6.2.0.tgz",
-      "integrity": "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "universalify": "^2.0.0"
-      },
-      "optionalDependencies": {
-        "graceful-fs": "^4.1.6"
-      }
-    },
-    "node_modules/keyv": {
-      "version": "4.5.4",
-      "resolved": "https://registry.npmmirror.com/keyv/-/keyv-4.5.4.tgz",
-      "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "json-buffer": "3.0.1"
-      }
-    },
-    "node_modules/kind-of": {
-      "version": "6.0.3",
-      "resolved": "https://registry.npmmirror.com/kind-of/-/kind-of-6.0.3.tgz",
-      "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=0.10.0"
-      }
-    },
-    "node_modules/klona": {
-      "version": "2.0.6",
-      "resolved": "https://registry.npmmirror.com/klona/-/klona-2.0.6.tgz",
-      "integrity": "sha512-dhG34DXATL5hSxJbIexCft8FChFXtmskoZYnoPWjXQuebWYCNkVeV3KkGegCK9CP1oswI/vQibS2GY7Em/sJJA==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">= 8"
-      }
-    },
-    "node_modules/launch-editor": {
-      "version": "2.13.2",
-      "resolved": "https://registry.npmmirror.com/launch-editor/-/launch-editor-2.13.2.tgz",
-      "integrity": "sha512-4VVDnbOpLXy/s8rdRCSXb+zfMeFR0WlJWpET1iA9CQdlZDfwyLjUuGQzXU4VeOoey6AicSAluWan7Etga6Kcmg==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "picocolors": "^1.1.1",
-        "shell-quote": "^1.8.3"
-      }
-    },
-    "node_modules/launch-editor-middleware": {
-      "version": "2.13.2",
-      "resolved": "https://registry.npmmirror.com/launch-editor-middleware/-/launch-editor-middleware-2.13.2.tgz",
-      "integrity": "sha512-kI7VqA9g6mhoeQ6YdNgd+gKLaeuWHAUR8oDM8vFtt924wG8HbI2XO0n/hSX2ML4hcJbTgUihuPHfpnPjIKMdJg==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "launch-editor": "^2.13.2"
-      }
-    },
-    "node_modules/levn": {
-      "version": "0.4.1",
-      "resolved": "https://registry.npmmirror.com/levn/-/levn-0.4.1.tgz",
-      "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "prelude-ls": "^1.2.1",
-        "type-check": "~0.4.0"
-      },
-      "engines": {
-        "node": ">= 0.8.0"
-      }
-    },
-    "node_modules/lilconfig": {
-      "version": "2.1.0",
-      "resolved": "https://registry.npmmirror.com/lilconfig/-/lilconfig-2.1.0.tgz",
-      "integrity": "sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=10"
-      }
-    },
-    "node_modules/lines-and-columns": {
-      "version": "1.2.4",
-      "resolved": "https://registry.npmmirror.com/lines-and-columns/-/lines-and-columns-1.2.4.tgz",
-      "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==",
-      "dev": true,
-      "license": "MIT"
-    },
-    "node_modules/loader-runner": {
-      "version": "4.3.1",
-      "resolved": "https://registry.npmmirror.com/loader-runner/-/loader-runner-4.3.1.tgz",
-      "integrity": "sha512-IWqP2SCPhyVFTBtRcgMHdzlf9ul25NwaFx4wCEH/KjAXuuHY4yNjvPXsBokp8jCB936PyWRaPKUNh8NvylLp2Q==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=6.11.5"
-      },
-      "funding": {
-        "type": "opencollective",
-        "url": "https://opencollective.com/webpack"
-      }
-    },
-    "node_modules/loader-utils": {
-      "version": "1.4.2",
-      "resolved": "https://registry.npmmirror.com/loader-utils/-/loader-utils-1.4.2.tgz",
-      "integrity": "sha512-I5d00Pd/jwMD2QCduo657+YM/6L3KZu++pmX9VFncxaxvHcru9jx1lBaFft+r4Mt2jK0Yhp41XlRAihzPxHNCg==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "big.js": "^5.2.2",
-        "emojis-list": "^3.0.0",
-        "json5": "^1.0.1"
-      },
-      "engines": {
-        "node": ">=4.0.0"
-      }
-    },
-    "node_modules/loader-utils/node_modules/json5": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmmirror.com/json5/-/json5-1.0.2.tgz",
-      "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "minimist": "^1.2.0"
-      },
-      "bin": {
-        "json5": "lib/cli.js"
-      }
-    },
-    "node_modules/locate-path": {
-      "version": "5.0.0",
-      "resolved": "https://registry.npmmirror.com/locate-path/-/locate-path-5.0.0.tgz",
-      "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "p-locate": "^4.1.0"
-      },
-      "engines": {
-        "node": ">=8"
-      }
-    },
-    "node_modules/lodash": {
-      "version": "4.18.1",
-      "resolved": "https://registry.npmmirror.com/lodash/-/lodash-4.18.1.tgz",
-      "integrity": "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==",
-      "dev": true,
-      "license": "MIT"
-    },
-    "node_modules/lodash.debounce": {
-      "version": "4.0.8",
-      "resolved": "https://registry.npmmirror.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz",
-      "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==",
-      "dev": true,
-      "license": "MIT"
-    },
-    "node_modules/lodash.defaultsdeep": {
-      "version": "4.6.1",
-      "resolved": "https://registry.npmmirror.com/lodash.defaultsdeep/-/lodash.defaultsdeep-4.6.1.tgz",
-      "integrity": "sha512-3j8wdDzYuWO3lM3Reg03MuQR957t287Rpcxp1njpEa8oDrikb+FwGdW3n+FELh/A6qib6yPit0j/pv9G/yeAqA==",
-      "dev": true,
-      "license": "MIT"
-    },
-    "node_modules/lodash.kebabcase": {
-      "version": "4.1.1",
-      "resolved": "https://registry.npmmirror.com/lodash.kebabcase/-/lodash.kebabcase-4.1.1.tgz",
-      "integrity": "sha512-N8XRTIMMqqDgSy4VLKPnJ/+hpGZN+PHQiJnSenYqPaVV/NCqEogTnAdZLQiGKhxX+JCs8waWq2t1XHWKOmlY8g==",
-      "dev": true,
-      "license": "MIT"
-    },
-    "node_modules/lodash.mapvalues": {
-      "version": "4.6.0",
-      "resolved": "https://registry.npmmirror.com/lodash.mapvalues/-/lodash.mapvalues-4.6.0.tgz",
-      "integrity": "sha512-JPFqXFeZQ7BfS00H58kClY7SPVeHertPE0lNuCyZ26/XlN8TvakYD7b9bGyNmXbT/D3BbtPAAmq90gPWqLkxlQ==",
-      "dev": true,
-      "license": "MIT"
-    },
-    "node_modules/lodash.memoize": {
-      "version": "4.1.2",
-      "resolved": "https://registry.npmmirror.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz",
-      "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==",
-      "dev": true,
-      "license": "MIT"
-    },
-    "node_modules/lodash.merge": {
-      "version": "4.6.2",
-      "resolved": "https://registry.npmmirror.com/lodash.merge/-/lodash.merge-4.6.2.tgz",
-      "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==",
-      "dev": true,
-      "license": "MIT"
-    },
-    "node_modules/lodash.truncate": {
-      "version": "4.4.2",
-      "resolved": "https://registry.npmmirror.com/lodash.truncate/-/lodash.truncate-4.4.2.tgz",
-      "integrity": "sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw==",
-      "dev": true,
-      "license": "MIT"
-    },
-    "node_modules/lodash.uniq": {
-      "version": "4.5.0",
-      "resolved": "https://registry.npmmirror.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz",
-      "integrity": "sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==",
-      "dev": true,
-      "license": "MIT"
-    },
-    "node_modules/log-symbols": {
-      "version": "4.1.0",
-      "resolved": "https://registry.npmmirror.com/log-symbols/-/log-symbols-4.1.0.tgz",
-      "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "chalk": "^4.1.0",
-        "is-unicode-supported": "^0.1.0"
-      },
-      "engines": {
-        "node": ">=10"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/sindresorhus"
-      }
-    },
-    "node_modules/log-symbols/node_modules/chalk": {
-      "version": "4.1.2",
-      "resolved": "https://registry.npmmirror.com/chalk/-/chalk-4.1.2.tgz",
-      "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "ansi-styles": "^4.1.0",
-        "supports-color": "^7.1.0"
-      },
-      "engines": {
-        "node": ">=10"
-      },
-      "funding": {
-        "url": "https://github.com/chalk/chalk?sponsor=1"
-      }
-    },
-    "node_modules/log-update": {
-      "version": "2.3.0",
-      "resolved": "https://registry.npmmirror.com/log-update/-/log-update-2.3.0.tgz",
-      "integrity": "sha512-vlP11XfFGyeNQlmEn9tJ66rEW1coA/79m5z6BCkudjbAGE83uhAcGYrBFwfs3AdLiLzGRusRPAbSPK9xZteCmg==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "ansi-escapes": "^3.0.0",
-        "cli-cursor": "^2.0.0",
-        "wrap-ansi": "^3.0.1"
-      },
-      "engines": {
-        "node": ">=4"
-      }
-    },
-    "node_modules/log-update/node_modules/ansi-regex": {
-      "version": "3.0.1",
-      "resolved": "https://registry.npmmirror.com/ansi-regex/-/ansi-regex-3.0.1.tgz",
-      "integrity": "sha512-+O9Jct8wf++lXxxFc4hc8LsjaSq0HFzzL7cVsw8pRDIPdjKD2mT4ytDZlLuSBZ4cLKZFXIrMGO7DbQCtMJJMKw==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=4"
-      }
-    },
-    "node_modules/log-update/node_modules/cli-cursor": {
-      "version": "2.1.0",
-      "resolved": "https://registry.npmmirror.com/cli-cursor/-/cli-cursor-2.1.0.tgz",
-      "integrity": "sha512-8lgKz8LmCRYZZQDpRyT2m5rKJ08TnU4tR9FFFW2rxpxR1FzWi4PQ/NfyODchAatHaUgnSPVcx/R5w6NuTBzFiw==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "restore-cursor": "^2.0.0"
-      },
-      "engines": {
-        "node": ">=4"
-      }
-    },
-    "node_modules/log-update/node_modules/is-fullwidth-code-point": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmmirror.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
-      "integrity": "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=4"
-      }
-    },
-    "node_modules/log-update/node_modules/mimic-fn": {
-      "version": "1.2.0",
-      "resolved": "https://registry.npmmirror.com/mimic-fn/-/mimic-fn-1.2.0.tgz",
-      "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=4"
-      }
-    },
-    "node_modules/log-update/node_modules/onetime": {
-      "version": "2.0.1",
-      "resolved": "https://registry.npmmirror.com/onetime/-/onetime-2.0.1.tgz",
-      "integrity": "sha512-oyyPpiMaKARvvcgip+JV+7zci5L8D1W9RZIz2l1o08AM3pfspitVWnPt3mzHcBPp12oYMTy0pqrFs/C+m3EwsQ==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "mimic-fn": "^1.0.0"
-      },
-      "engines": {
-        "node": ">=4"
-      }
-    },
-    "node_modules/log-update/node_modules/restore-cursor": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmmirror.com/restore-cursor/-/restore-cursor-2.0.0.tgz",
-      "integrity": "sha512-6IzJLuGi4+R14vwagDHX+JrXmPVtPpn4mffDJ1UdR7/Edm87fl6yi8mMBIVvFtJaNTUvjughmW4hwLhRG7gC1Q==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "onetime": "^2.0.0",
-        "signal-exit": "^3.0.2"
-      },
-      "engines": {
-        "node": ">=4"
-      }
-    },
-    "node_modules/log-update/node_modules/string-width": {
-      "version": "2.1.1",
-      "resolved": "https://registry.npmmirror.com/string-width/-/string-width-2.1.1.tgz",
-      "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "is-fullwidth-code-point": "^2.0.0",
-        "strip-ansi": "^4.0.0"
-      },
-      "engines": {
-        "node": ">=4"
-      }
-    },
-    "node_modules/log-update/node_modules/strip-ansi": {
-      "version": "4.0.0",
-      "resolved": "https://registry.npmmirror.com/strip-ansi/-/strip-ansi-4.0.0.tgz",
-      "integrity": "sha512-4XaJ2zQdCzROZDivEVIDPkcQn8LMFSa8kj8Gxb/Lnwzv9A8VctNZ+lfivC/sV3ivW8ElJTERXZoPBRrZKkNKow==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "ansi-regex": "^3.0.0"
-      },
-      "engines": {
-        "node": ">=4"
-      }
-    },
-    "node_modules/log-update/node_modules/wrap-ansi": {
-      "version": "3.0.1",
-      "resolved": "https://registry.npmmirror.com/wrap-ansi/-/wrap-ansi-3.0.1.tgz",
-      "integrity": "sha512-iXR3tDXpbnTpzjKSylUJRkLuOrEC7hwEB221cgn6wtF8wpmz28puFXAEfPT5zrjM3wahygB//VuWEr1vTkDcNQ==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "string-width": "^2.1.1",
-        "strip-ansi": "^4.0.0"
-      },
-      "engines": {
-        "node": ">=4"
-      }
-    },
-    "node_modules/lower-case": {
-      "version": "2.0.2",
-      "resolved": "https://registry.npmmirror.com/lower-case/-/lower-case-2.0.2.tgz",
-      "integrity": "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "tslib": "^2.0.3"
-      }
-    },
-    "node_modules/lru-cache": {
-      "version": "5.1.1",
-      "resolved": "https://registry.npmmirror.com/lru-cache/-/lru-cache-5.1.1.tgz",
-      "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==",
-      "dev": true,
-      "license": "ISC",
-      "dependencies": {
-        "yallist": "^3.0.2"
-      }
-    },
-    "node_modules/magic-string": {
-      "version": "0.30.21",
-      "resolved": "https://registry.npmmirror.com/magic-string/-/magic-string-0.30.21.tgz",
-      "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@jridgewell/sourcemap-codec": "^1.5.5"
-      }
-    },
-    "node_modules/make-dir": {
-      "version": "3.1.0",
-      "resolved": "https://registry.npmmirror.com/make-dir/-/make-dir-3.1.0.tgz",
-      "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "semver": "^6.0.0"
-      },
-      "engines": {
-        "node": ">=8"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/sindresorhus"
-      }
-    },
-    "node_modules/math-intrinsics": {
-      "version": "1.1.0",
-      "resolved": "https://registry.npmmirror.com/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
-      "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==",
-      "license": "MIT",
-      "engines": {
-        "node": ">= 0.4"
-      }
-    },
-    "node_modules/mdn-data": {
-      "version": "2.0.14",
-      "resolved": "https://registry.npmmirror.com/mdn-data/-/mdn-data-2.0.14.tgz",
-      "integrity": "sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==",
-      "dev": true,
-      "license": "CC0-1.0"
-    },
-    "node_modules/media-typer": {
-      "version": "0.3.0",
-      "resolved": "https://registry.npmmirror.com/media-typer/-/media-typer-0.3.0.tgz",
-      "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">= 0.6"
-      }
-    },
-    "node_modules/memfs": {
-      "version": "3.5.3",
-      "resolved": "https://registry.npmmirror.com/memfs/-/memfs-3.5.3.tgz",
-      "integrity": "sha512-UERzLsxzllchadvbPs5aolHh65ISpKpM+ccLbOJ8/vvpBKmAWf+la7dXFy7Mr0ySHbdHrFv5kGFCUHHe6GFEmw==",
-      "dev": true,
-      "license": "Unlicense",
-      "dependencies": {
-        "fs-monkey": "^1.0.4"
-      },
-      "engines": {
-        "node": ">= 4.0.0"
-      }
-    },
-    "node_modules/merge-descriptors": {
-      "version": "1.0.3",
-      "resolved": "https://registry.npmmirror.com/merge-descriptors/-/merge-descriptors-1.0.3.tgz",
-      "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==",
-      "dev": true,
-      "license": "MIT",
-      "funding": {
-        "url": "https://github.com/sponsors/sindresorhus"
-      }
-    },
-    "node_modules/merge-source-map": {
-      "version": "1.1.0",
-      "resolved": "https://registry.npmmirror.com/merge-source-map/-/merge-source-map-1.1.0.tgz",
-      "integrity": "sha512-Qkcp7P2ygktpMPh2mCQZaf3jhN6D3Z/qVZHSdWvQ+2Ef5HgRAPBO57A77+ENm0CPx2+1Ce/MYKi3ymqdfuqibw==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "source-map": "^0.6.1"
-      }
-    },
-    "node_modules/merge-stream": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmmirror.com/merge-stream/-/merge-stream-2.0.0.tgz",
-      "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==",
-      "dev": true,
-      "license": "MIT"
-    },
-    "node_modules/merge2": {
-      "version": "1.4.1",
-      "resolved": "https://registry.npmmirror.com/merge2/-/merge2-1.4.1.tgz",
-      "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">= 8"
-      }
-    },
-    "node_modules/methods": {
-      "version": "1.1.2",
-      "resolved": "https://registry.npmmirror.com/methods/-/methods-1.1.2.tgz",
-      "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">= 0.6"
-      }
-    },
-    "node_modules/micromatch": {
-      "version": "4.0.8",
-      "resolved": "https://registry.npmmirror.com/micromatch/-/micromatch-4.0.8.tgz",
-      "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "braces": "^3.0.3",
-        "picomatch": "^2.3.1"
-      },
-      "engines": {
-        "node": ">=8.6"
-      }
-    },
-    "node_modules/mime": {
-      "version": "1.6.0",
-      "resolved": "https://registry.npmmirror.com/mime/-/mime-1.6.0.tgz",
-      "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==",
-      "dev": true,
-      "license": "MIT",
-      "bin": {
-        "mime": "cli.js"
-      },
-      "engines": {
-        "node": ">=4"
-      }
-    },
-    "node_modules/mime-db": {
-      "version": "1.54.0",
-      "resolved": "https://registry.npmmirror.com/mime-db/-/mime-db-1.54.0.tgz",
-      "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">= 0.6"
-      }
-    },
-    "node_modules/mime-types": {
-      "version": "2.1.35",
-      "resolved": "https://registry.npmmirror.com/mime-types/-/mime-types-2.1.35.tgz",
-      "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
-      "license": "MIT",
-      "dependencies": {
-        "mime-db": "1.52.0"
-      },
-      "engines": {
-        "node": ">= 0.6"
-      }
-    },
-    "node_modules/mime-types/node_modules/mime-db": {
-      "version": "1.52.0",
-      "resolved": "https://registry.npmmirror.com/mime-db/-/mime-db-1.52.0.tgz",
-      "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
-      "license": "MIT",
-      "engines": {
-        "node": ">= 0.6"
-      }
-    },
-    "node_modules/mimic-fn": {
-      "version": "2.1.0",
-      "resolved": "https://registry.npmmirror.com/mimic-fn/-/mimic-fn-2.1.0.tgz",
-      "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=6"
-      }
-    },
-    "node_modules/mini-css-extract-plugin": {
-      "version": "2.10.2",
-      "resolved": "https://registry.npmmirror.com/mini-css-extract-plugin/-/mini-css-extract-plugin-2.10.2.tgz",
-      "integrity": "sha512-AOSS0IdEB95ayVkxn5oGzNQwqAi2J0Jb/kKm43t7H73s8+f5873g0yuj0PNvK4dO75mu5DHg4nlgp4k6Kga8eg==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "schema-utils": "^4.0.0",
-        "tapable": "^2.2.1"
-      },
-      "engines": {
-        "node": ">= 12.13.0"
-      },
-      "funding": {
-        "type": "opencollective",
-        "url": "https://opencollective.com/webpack"
-      },
-      "peerDependencies": {
-        "webpack": "^5.0.0"
-      }
-    },
-    "node_modules/mini-css-extract-plugin/node_modules/ajv": {
-      "version": "8.18.0",
-      "resolved": "https://registry.npmmirror.com/ajv/-/ajv-8.18.0.tgz",
-      "integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "fast-deep-equal": "^3.1.3",
-        "fast-uri": "^3.0.1",
-        "json-schema-traverse": "^1.0.0",
-        "require-from-string": "^2.0.2"
-      },
-      "funding": {
-        "type": "github",
-        "url": "https://github.com/sponsors/epoberezkin"
-      }
-    },
-    "node_modules/mini-css-extract-plugin/node_modules/ajv-keywords": {
-      "version": "5.1.0",
-      "resolved": "https://registry.npmmirror.com/ajv-keywords/-/ajv-keywords-5.1.0.tgz",
-      "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "fast-deep-equal": "^3.1.3"
-      },
-      "peerDependencies": {
-        "ajv": "^8.8.2"
-      }
-    },
-    "node_modules/mini-css-extract-plugin/node_modules/json-schema-traverse": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmmirror.com/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
-      "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
-      "dev": true,
-      "license": "MIT"
-    },
-    "node_modules/mini-css-extract-plugin/node_modules/schema-utils": {
-      "version": "4.3.3",
-      "resolved": "https://registry.npmmirror.com/schema-utils/-/schema-utils-4.3.3.tgz",
-      "integrity": "sha512-eflK8wEtyOE6+hsaRVPxvUKYCpRgzLqDTb8krvAsRIwOGlHoSgYLgBXoubGgLd2fT41/OUYdb48v4k4WWHQurA==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@types/json-schema": "^7.0.9",
-        "ajv": "^8.9.0",
-        "ajv-formats": "^2.1.1",
-        "ajv-keywords": "^5.1.0"
-      },
-      "engines": {
-        "node": ">= 10.13.0"
-      },
-      "funding": {
-        "type": "opencollective",
-        "url": "https://opencollective.com/webpack"
-      }
-    },
-    "node_modules/minimalistic-assert": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmmirror.com/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz",
-      "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==",
-      "dev": true,
-      "license": "ISC"
-    },
-    "node_modules/minimatch": {
-      "version": "3.1.5",
-      "resolved": "https://registry.npmmirror.com/minimatch/-/minimatch-3.1.5.tgz",
-      "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==",
-      "dev": true,
-      "license": "ISC",
-      "dependencies": {
-        "brace-expansion": "^1.1.7"
-      },
-      "engines": {
-        "node": "*"
-      }
-    },
-    "node_modules/minimist": {
-      "version": "1.2.8",
-      "resolved": "https://registry.npmmirror.com/minimist/-/minimist-1.2.8.tgz",
-      "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==",
-      "dev": true,
-      "license": "MIT",
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
-    "node_modules/minipass": {
-      "version": "3.3.6",
-      "resolved": "https://registry.npmmirror.com/minipass/-/minipass-3.3.6.tgz",
-      "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==",
-      "dev": true,
-      "license": "ISC",
-      "dependencies": {
-        "yallist": "^4.0.0"
-      },
-      "engines": {
-        "node": ">=8"
-      }
-    },
-    "node_modules/minipass/node_modules/yallist": {
-      "version": "4.0.0",
-      "resolved": "https://registry.npmmirror.com/yallist/-/yallist-4.0.0.tgz",
-      "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
-      "dev": true,
-      "license": "ISC"
-    },
-    "node_modules/mockjs": {
-      "version": "1.1.0",
-      "resolved": "https://registry.npmmirror.com/mockjs/-/mockjs-1.1.0.tgz",
-      "integrity": "sha512-eQsKcWzIaZzEZ07NuEyO4Nw65g0hdWAyurVol1IPl1gahRwY+svqzfgfey8U8dahLwG44d6/RwEzuK52rSa/JQ==",
-      "dependencies": {
-        "commander": "*"
-      },
-      "bin": {
-        "random": "bin/random"
-      }
-    },
-    "node_modules/module-alias": {
-      "version": "2.3.4",
-      "resolved": "https://registry.npmmirror.com/module-alias/-/module-alias-2.3.4.tgz",
-      "integrity": "sha512-bOclZt8hkpuGgSSoG07PKmvzTizROilUTvLNyrMqvlC9snhs7y7GzjNWAVbISIOlhCP1T14rH1PDAV9iNyBq/w==",
-      "dev": true,
-      "license": "MIT"
-    },
-    "node_modules/mrmime": {
-      "version": "2.0.1",
-      "resolved": "https://registry.npmmirror.com/mrmime/-/mrmime-2.0.1.tgz",
-      "integrity": "sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=10"
-      }
-    },
-    "node_modules/ms": {
-      "version": "2.1.3",
-      "resolved": "https://registry.npmmirror.com/ms/-/ms-2.1.3.tgz",
-      "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
-      "dev": true,
-      "license": "MIT"
-    },
-    "node_modules/multicast-dns": {
-      "version": "7.2.5",
-      "resolved": "https://registry.npmmirror.com/multicast-dns/-/multicast-dns-7.2.5.tgz",
-      "integrity": "sha512-2eznPJP8z2BFLX50tf0LuODrpINqP1RVIm/CObbTcBRITQgmC/TjcREF1NeTBzIcR5XO/ukWo+YHOjBbFwIupg==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "dns-packet": "^5.2.2",
-        "thunky": "^1.0.2"
-      },
-      "bin": {
-        "multicast-dns": "cli.js"
-      }
-    },
-    "node_modules/mz": {
-      "version": "2.7.0",
-      "resolved": "https://registry.npmmirror.com/mz/-/mz-2.7.0.tgz",
-      "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "any-promise": "^1.0.0",
-        "object-assign": "^4.0.1",
-        "thenify-all": "^1.0.0"
-      }
-    },
-    "node_modules/nanoid": {
-      "version": "3.3.11",
-      "resolved": "https://registry.npmmirror.com/nanoid/-/nanoid-3.3.11.tgz",
-      "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==",
-      "funding": [
-        {
-          "type": "github",
-          "url": "https://github.com/sponsors/ai"
-        }
-      ],
-      "license": "MIT",
-      "bin": {
-        "nanoid": "bin/nanoid.cjs"
-      },
-      "engines": {
-        "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
-      }
-    },
-    "node_modules/natural-compare": {
-      "version": "1.4.0",
-      "resolved": "https://registry.npmmirror.com/natural-compare/-/natural-compare-1.4.0.tgz",
-      "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==",
-      "dev": true,
-      "license": "MIT"
-    },
-    "node_modules/negotiator": {
-      "version": "0.6.4",
-      "resolved": "https://registry.npmmirror.com/negotiator/-/negotiator-0.6.4.tgz",
-      "integrity": "sha512-myRT3DiWPHqho5PrJaIRyaMv2kgYf0mUVgBNOYMuCH5Ki1yEiQaf/ZJuQ62nvpc44wL5WDbTX7yGJi1Neevw8w==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">= 0.6"
-      }
-    },
-    "node_modules/neo-async": {
-      "version": "2.6.2",
-      "resolved": "https://registry.npmmirror.com/neo-async/-/neo-async-2.6.2.tgz",
-      "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==",
-      "dev": true,
-      "license": "MIT"
-    },
-    "node_modules/nice-try": {
-      "version": "1.0.5",
-      "resolved": "https://registry.npmmirror.com/nice-try/-/nice-try-1.0.5.tgz",
-      "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==",
-      "dev": true,
-      "license": "MIT"
-    },
-    "node_modules/no-case": {
-      "version": "3.0.4",
-      "resolved": "https://registry.npmmirror.com/no-case/-/no-case-3.0.4.tgz",
-      "integrity": "sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "lower-case": "^2.0.2",
-        "tslib": "^2.0.3"
-      }
-    },
-    "node_modules/node-fetch": {
-      "version": "2.7.0",
-      "resolved": "https://registry.npmmirror.com/node-fetch/-/node-fetch-2.7.0.tgz",
-      "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "whatwg-url": "^5.0.0"
-      },
-      "engines": {
-        "node": "4.x || >=6.0.0"
-      },
-      "peerDependencies": {
-        "encoding": "^0.1.0"
-      },
-      "peerDependenciesMeta": {
-        "encoding": {
-          "optional": true
-        }
-      }
-    },
-    "node_modules/node-forge": {
-      "version": "1.4.0",
-      "resolved": "https://registry.npmmirror.com/node-forge/-/node-forge-1.4.0.tgz",
-      "integrity": "sha512-LarFH0+6VfriEhqMMcLX2F7SwSXeWwnEAJEsYm5QKWchiVYVvJyV9v7UDvUv+w5HO23ZpQTXDv/GxdDdMyOuoQ==",
-      "dev": true,
-      "license": "(BSD-3-Clause OR GPL-2.0)",
-      "engines": {
-        "node": ">= 6.13.0"
-      }
-    },
-    "node_modules/node-releases": {
-      "version": "2.0.37",
-      "resolved": "https://registry.npmmirror.com/node-releases/-/node-releases-2.0.37.tgz",
-      "integrity": "sha512-1h5gKZCF+pO/o3Iqt5Jp7wc9rH3eJJ0+nh/CIoiRwjRxde/hAHyLPXYN4V3CqKAbiZPSeJFSWHmJsbkicta0Eg==",
-      "dev": true,
-      "license": "MIT"
-    },
-    "node_modules/normalize-package-data": {
-      "version": "2.5.0",
-      "resolved": "https://registry.npmmirror.com/normalize-package-data/-/normalize-package-data-2.5.0.tgz",
-      "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==",
-      "dev": true,
-      "license": "BSD-2-Clause",
-      "dependencies": {
-        "hosted-git-info": "^2.1.4",
-        "resolve": "^1.10.0",
-        "semver": "2 || 3 || 4 || 5",
-        "validate-npm-package-license": "^3.0.1"
-      }
-    },
-    "node_modules/normalize-package-data/node_modules/semver": {
-      "version": "5.7.2",
-      "resolved": "https://registry.npmmirror.com/semver/-/semver-5.7.2.tgz",
-      "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==",
-      "dev": true,
-      "license": "ISC",
-      "bin": {
-        "semver": "bin/semver"
-      }
-    },
-    "node_modules/normalize-path": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmmirror.com/normalize-path/-/normalize-path-3.0.0.tgz",
-      "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=0.10.0"
-      }
-    },
-    "node_modules/normalize-url": {
-      "version": "6.1.0",
-      "resolved": "https://registry.npmmirror.com/normalize-url/-/normalize-url-6.1.0.tgz",
-      "integrity": "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=10"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/sindresorhus"
-      }
-    },
-    "node_modules/normalize-wheel": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmmirror.com/normalize-wheel/-/normalize-wheel-1.0.1.tgz",
-      "integrity": "sha512-1OnlAPZ3zgrk8B91HyRj+eVv+kS5u+Z0SCsak6Xil/kmgEia50ga7zfkumayonZrImffAxPU/5WcyGhzetHNPA==",
-      "license": "BSD-3-Clause"
-    },
-    "node_modules/npm-run-path": {
-      "version": "2.0.2",
-      "resolved": "https://registry.npmmirror.com/npm-run-path/-/npm-run-path-2.0.2.tgz",
-      "integrity": "sha512-lJxZYlT4DW/bRUtFh1MQIWqmLwQfAxnqWG4HhEdjMlkrJYnJn0Jrr2u3mgxqaWsdiBc76TYkTG/mhrnYTuzfHw==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "path-key": "^2.0.0"
-      },
-      "engines": {
-        "node": ">=4"
-      }
-    },
-    "node_modules/nth-check": {
-      "version": "2.1.1",
-      "resolved": "https://registry.npmmirror.com/nth-check/-/nth-check-2.1.1.tgz",
-      "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==",
-      "dev": true,
-      "license": "BSD-2-Clause",
-      "dependencies": {
-        "boolbase": "^1.0.0"
-      },
-      "funding": {
-        "url": "https://github.com/fb55/nth-check?sponsor=1"
-      }
-    },
-    "node_modules/object-assign": {
-      "version": "4.1.1",
-      "resolved": "https://registry.npmmirror.com/object-assign/-/object-assign-4.1.1.tgz",
-      "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
-      "license": "MIT",
-      "engines": {
-        "node": ">=0.10.0"
-      }
-    },
-    "node_modules/object-inspect": {
-      "version": "1.13.4",
-      "resolved": "https://registry.npmmirror.com/object-inspect/-/object-inspect-1.13.4.tgz",
-      "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">= 0.4"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
-    "node_modules/object-is": {
-      "version": "1.1.6",
-      "resolved": "https://registry.npmmirror.com/object-is/-/object-is-1.1.6.tgz",
-      "integrity": "sha512-F8cZ+KfGlSGi09lJT7/Nd6KJZ9ygtvYC0/UYYLI9nmQKLMnydpB9yvbv9K1uSkEu7FU9vYPmVwLg328tX+ot3Q==",
-      "license": "MIT",
-      "dependencies": {
-        "call-bind": "^1.0.7",
-        "define-properties": "^1.2.1"
-      },
-      "engines": {
-        "node": ">= 0.4"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
-    "node_modules/object-keys": {
-      "version": "1.1.1",
-      "resolved": "https://registry.npmmirror.com/object-keys/-/object-keys-1.1.1.tgz",
-      "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==",
-      "license": "MIT",
-      "engines": {
-        "node": ">= 0.4"
-      }
-    },
-    "node_modules/object.assign": {
-      "version": "4.1.7",
-      "resolved": "https://registry.npmmirror.com/object.assign/-/object.assign-4.1.7.tgz",
-      "integrity": "sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "call-bind": "^1.0.8",
-        "call-bound": "^1.0.3",
-        "define-properties": "^1.2.1",
-        "es-object-atoms": "^1.0.0",
-        "has-symbols": "^1.1.0",
-        "object-keys": "^1.1.1"
-      },
-      "engines": {
-        "node": ">= 0.4"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
-    "node_modules/obuf": {
-      "version": "1.1.2",
-      "resolved": "https://registry.npmmirror.com/obuf/-/obuf-1.1.2.tgz",
-      "integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==",
-      "dev": true,
-      "license": "MIT"
-    },
-    "node_modules/on-finished": {
-      "version": "2.4.1",
-      "resolved": "https://registry.npmmirror.com/on-finished/-/on-finished-2.4.1.tgz",
-      "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "ee-first": "1.1.1"
-      },
-      "engines": {
-        "node": ">= 0.8"
-      }
-    },
-    "node_modules/on-headers": {
-      "version": "1.1.0",
-      "resolved": "https://registry.npmmirror.com/on-headers/-/on-headers-1.1.0.tgz",
-      "integrity": "sha512-737ZY3yNnXy37FHkQxPzt4UZ2UWPWiCZWLvFZ4fu5cueciegX0zGPnrlY6bwRg4FdQOe9YU8MkmJwGhoMybl8A==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">= 0.8"
-      }
-    },
-    "node_modules/once": {
-      "version": "1.4.0",
-      "resolved": "https://registry.npmmirror.com/once/-/once-1.4.0.tgz",
-      "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
-      "dev": true,
-      "license": "ISC",
-      "dependencies": {
-        "wrappy": "1"
-      }
-    },
-    "node_modules/onetime": {
-      "version": "5.1.2",
-      "resolved": "https://registry.npmmirror.com/onetime/-/onetime-5.1.2.tgz",
-      "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "mimic-fn": "^2.1.0"
-      },
-      "engines": {
-        "node": ">=6"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/sindresorhus"
-      }
-    },
-    "node_modules/open": {
-      "version": "8.4.2",
-      "resolved": "https://registry.npmmirror.com/open/-/open-8.4.2.tgz",
-      "integrity": "sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "define-lazy-prop": "^2.0.0",
-        "is-docker": "^2.1.1",
-        "is-wsl": "^2.2.0"
-      },
-      "engines": {
-        "node": ">=12"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/sindresorhus"
-      }
-    },
-    "node_modules/opener": {
-      "version": "1.5.2",
-      "resolved": "https://registry.npmmirror.com/opener/-/opener-1.5.2.tgz",
-      "integrity": "sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A==",
-      "dev": true,
-      "license": "(WTFPL OR MIT)",
-      "bin": {
-        "opener": "bin/opener-bin.js"
-      }
-    },
-    "node_modules/optionator": {
-      "version": "0.9.4",
-      "resolved": "https://registry.npmmirror.com/optionator/-/optionator-0.9.4.tgz",
-      "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "deep-is": "^0.1.3",
-        "fast-levenshtein": "^2.0.6",
-        "levn": "^0.4.1",
-        "prelude-ls": "^1.2.1",
-        "type-check": "^0.4.0",
-        "word-wrap": "^1.2.5"
-      },
-      "engines": {
-        "node": ">= 0.8.0"
-      }
-    },
-    "node_modules/ora": {
-      "version": "5.4.1",
-      "resolved": "https://registry.npmmirror.com/ora/-/ora-5.4.1.tgz",
-      "integrity": "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "bl": "^4.1.0",
-        "chalk": "^4.1.0",
-        "cli-cursor": "^3.1.0",
-        "cli-spinners": "^2.5.0",
-        "is-interactive": "^1.0.0",
-        "is-unicode-supported": "^0.1.0",
-        "log-symbols": "^4.1.0",
-        "strip-ansi": "^6.0.0",
-        "wcwidth": "^1.0.1"
-      },
-      "engines": {
-        "node": ">=10"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/sindresorhus"
-      }
-    },
-    "node_modules/ora/node_modules/chalk": {
-      "version": "4.1.2",
-      "resolved": "https://registry.npmmirror.com/chalk/-/chalk-4.1.2.tgz",
-      "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "ansi-styles": "^4.1.0",
-        "supports-color": "^7.1.0"
-      },
-      "engines": {
-        "node": ">=10"
-      },
-      "funding": {
-        "url": "https://github.com/chalk/chalk?sponsor=1"
-      }
-    },
-    "node_modules/p-finally": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmmirror.com/p-finally/-/p-finally-1.0.0.tgz",
-      "integrity": "sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=4"
-      }
-    },
-    "node_modules/p-limit": {
-      "version": "2.3.0",
-      "resolved": "https://registry.npmmirror.com/p-limit/-/p-limit-2.3.0.tgz",
-      "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "p-try": "^2.0.0"
-      },
-      "engines": {
-        "node": ">=6"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/sindresorhus"
-      }
-    },
-    "node_modules/p-locate": {
-      "version": "4.1.0",
-      "resolved": "https://registry.npmmirror.com/p-locate/-/p-locate-4.1.0.tgz",
-      "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "p-limit": "^2.2.0"
-      },
-      "engines": {
-        "node": ">=8"
-      }
-    },
-    "node_modules/p-retry": {
-      "version": "4.6.2",
-      "resolved": "https://registry.npmmirror.com/p-retry/-/p-retry-4.6.2.tgz",
-      "integrity": "sha512-312Id396EbJdvRONlngUx0NydfrIQ5lsYu0znKVUzVvArzEIt08V1qhtyESbGVd1FGX7UKtiFp5uwKZdM8wIuQ==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@types/retry": "0.12.0",
-        "retry": "^0.13.1"
-      },
-      "engines": {
-        "node": ">=8"
-      }
-    },
-    "node_modules/p-try": {
-      "version": "2.2.0",
-      "resolved": "https://registry.npmmirror.com/p-try/-/p-try-2.2.0.tgz",
-      "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=6"
-      }
-    },
-    "node_modules/param-case": {
-      "version": "3.0.4",
-      "resolved": "https://registry.npmmirror.com/param-case/-/param-case-3.0.4.tgz",
-      "integrity": "sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "dot-case": "^3.0.4",
-        "tslib": "^2.0.3"
-      }
-    },
-    "node_modules/parchment": {
-      "version": "1.1.4",
-      "resolved": "https://registry.npmmirror.com/parchment/-/parchment-1.1.4.tgz",
-      "integrity": "sha512-J5FBQt/pM2inLzg4hEWmzQx/8h8D0CiDxaG3vyp9rKrQRSDgBlhjdP5jQGgosEajXPSQouXGHOmVdgo7QmJuOg==",
-      "license": "BSD-3-Clause"
-    },
-    "node_modules/parent-module": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmmirror.com/parent-module/-/parent-module-1.0.1.tgz",
-      "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "callsites": "^3.0.0"
-      },
-      "engines": {
-        "node": ">=6"
-      }
-    },
-    "node_modules/parse-json": {
-      "version": "5.2.0",
-      "resolved": "https://registry.npmmirror.com/parse-json/-/parse-json-5.2.0.tgz",
-      "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@babel/code-frame": "^7.0.0",
-        "error-ex": "^1.3.1",
-        "json-parse-even-better-errors": "^2.3.0",
-        "lines-and-columns": "^1.1.6"
-      },
-      "engines": {
-        "node": ">=8"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/sindresorhus"
-      }
-    },
-    "node_modules/parse5": {
-      "version": "5.1.1",
-      "resolved": "https://registry.npmmirror.com/parse5/-/parse5-5.1.1.tgz",
-      "integrity": "sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug==",
-      "dev": true,
-      "license": "MIT"
-    },
-    "node_modules/parse5-htmlparser2-tree-adapter": {
-      "version": "6.0.1",
-      "resolved": "https://registry.npmmirror.com/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-6.0.1.tgz",
-      "integrity": "sha512-qPuWvbLgvDGilKc5BoicRovlT4MtYT6JfJyBOMDsKoiT+GiuP5qyrPCnR9HcPECIJJmZh5jRndyNThnhhb/vlA==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "parse5": "^6.0.1"
-      }
-    },
-    "node_modules/parse5-htmlparser2-tree-adapter/node_modules/parse5": {
-      "version": "6.0.1",
-      "resolved": "https://registry.npmmirror.com/parse5/-/parse5-6.0.1.tgz",
-      "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==",
-      "dev": true,
-      "license": "MIT"
-    },
-    "node_modules/parseurl": {
-      "version": "1.3.3",
-      "resolved": "https://registry.npmmirror.com/parseurl/-/parseurl-1.3.3.tgz",
-      "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">= 0.8"
-      }
-    },
-    "node_modules/pascal-case": {
-      "version": "3.1.2",
-      "resolved": "https://registry.npmmirror.com/pascal-case/-/pascal-case-3.1.2.tgz",
-      "integrity": "sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "no-case": "^3.0.4",
-        "tslib": "^2.0.3"
-      }
-    },
-    "node_modules/path-exists": {
-      "version": "4.0.0",
-      "resolved": "https://registry.npmmirror.com/path-exists/-/path-exists-4.0.0.tgz",
-      "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=8"
-      }
-    },
-    "node_modules/path-is-absolute": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmmirror.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
-      "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=0.10.0"
-      }
-    },
-    "node_modules/path-key": {
-      "version": "2.0.1",
-      "resolved": "https://registry.npmmirror.com/path-key/-/path-key-2.0.1.tgz",
-      "integrity": "sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=4"
-      }
-    },
-    "node_modules/path-parse": {
-      "version": "1.0.7",
-      "resolved": "https://registry.npmmirror.com/path-parse/-/path-parse-1.0.7.tgz",
-      "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
-      "dev": true,
-      "license": "MIT"
-    },
-    "node_modules/path-to-regexp": {
-      "version": "0.1.13",
-      "resolved": "https://registry.npmmirror.com/path-to-regexp/-/path-to-regexp-0.1.13.tgz",
-      "integrity": "sha512-A/AGNMFN3c8bOlvV9RreMdrv7jsmF9XIfDeCd87+I8RNg6s78BhJxMu69NEMHBSJFxKidViTEdruRwEk/WIKqA==",
-      "dev": true,
-      "license": "MIT"
-    },
-    "node_modules/path-type": {
-      "version": "4.0.0",
-      "resolved": "https://registry.npmmirror.com/path-type/-/path-type-4.0.0.tgz",
-      "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=8"
-      }
-    },
-    "node_modules/picocolors": {
-      "version": "1.1.1",
-      "resolved": "https://registry.npmmirror.com/picocolors/-/picocolors-1.1.1.tgz",
-      "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
-      "license": "ISC"
-    },
-    "node_modules/picomatch": {
-      "version": "2.3.2",
-      "resolved": "https://registry.npmmirror.com/picomatch/-/picomatch-2.3.2.tgz",
-      "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=8.6"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/jonschlinkert"
-      }
-    },
-    "node_modules/pkg-dir": {
-      "version": "4.2.0",
-      "resolved": "https://registry.npmmirror.com/pkg-dir/-/pkg-dir-4.2.0.tgz",
-      "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "find-up": "^4.0.0"
-      },
-      "engines": {
-        "node": ">=8"
-      }
-    },
-    "node_modules/playwright": {
-      "version": "1.60.0",
-      "resolved": "https://registry.npmmirror.com/playwright/-/playwright-1.60.0.tgz",
-      "integrity": "sha512-hheHdokM8cdqCb0lcE3s+zT4t4W+vvjpGxsZlDnikarzx8tSzMebh3UiFtgqwFwnTnjYQcsyMF8ei2mCO/tpeA==",
-      "dev": true,
-      "license": "Apache-2.0",
-      "dependencies": {
-        "playwright-core": "1.60.0"
-      },
-      "bin": {
-        "playwright": "cli.js"
-      },
-      "engines": {
-        "node": ">=18"
-      },
-      "optionalDependencies": {
-        "fsevents": "2.3.2"
-      }
-    },
-    "node_modules/playwright-core": {
-      "version": "1.60.0",
-      "resolved": "https://registry.npmmirror.com/playwright-core/-/playwright-core-1.60.0.tgz",
-      "integrity": "sha512-9bW6zvX/m0lEbgTKJ6YppOKx8H3VOPBMOCFh2irXFOT4BbHgrx5hPjwJYLT40Lu+4qtD36qKc/Hn56StUW57IA==",
-      "dev": true,
-      "license": "Apache-2.0",
-      "bin": {
-        "playwright-core": "cli.js"
-      },
-      "engines": {
-        "node": ">=18"
-      }
-    },
-    "node_modules/playwright/node_modules/fsevents": {
-      "version": "2.3.2",
-      "resolved": "https://registry.npmmirror.com/fsevents/-/fsevents-2.3.2.tgz",
-      "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
-      "dev": true,
-      "hasInstallScript": true,
-      "license": "MIT",
-      "optional": true,
-      "os": [
-        "darwin"
-      ],
-      "engines": {
-        "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
-      }
-    },
-    "node_modules/portfinder": {
-      "version": "1.0.38",
-      "resolved": "https://registry.npmmirror.com/portfinder/-/portfinder-1.0.38.tgz",
-      "integrity": "sha512-rEwq/ZHlJIKw++XtLAO8PPuOQA/zaPJOZJ37BVuN97nLpMJeuDVLVGRwbFoBgLudgdTMP2hdRJP++H+8QOA3vg==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "async": "^3.2.6",
-        "debug": "^4.3.6"
-      },
-      "engines": {
-        "node": ">= 10.12"
-      }
-    },
-    "node_modules/postcss": {
-      "version": "8.5.10",
-      "resolved": "https://registry.npmmirror.com/postcss/-/postcss-8.5.10.tgz",
-      "integrity": "sha512-pMMHxBOZKFU6HgAZ4eyGnwXF/EvPGGqUr0MnZ5+99485wwW41kW91A4LOGxSHhgugZmSChL5AlElNdwlNgcnLQ==",
-      "funding": [
-        {
-          "type": "opencollective",
-          "url": "https://opencollective.com/postcss/"
-        },
-        {
-          "type": "tidelift",
-          "url": "https://tidelift.com/funding/github/npm/postcss"
-        },
-        {
-          "type": "github",
-          "url": "https://github.com/sponsors/ai"
-        }
-      ],
-      "license": "MIT",
-      "dependencies": {
-        "nanoid": "^3.3.11",
-        "picocolors": "^1.1.1",
-        "source-map-js": "^1.2.1"
-      },
-      "engines": {
-        "node": "^10 || ^12 || >=14"
-      }
-    },
-    "node_modules/postcss-calc": {
-      "version": "8.2.4",
-      "resolved": "https://registry.npmmirror.com/postcss-calc/-/postcss-calc-8.2.4.tgz",
-      "integrity": "sha512-SmWMSJmB8MRnnULldx0lQIyhSNvuDl9HfrZkaqqE/WHAhToYsAvDq+yAsA/kIyINDszOp3Rh0GFoNuH5Ypsm3Q==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "postcss-selector-parser": "^6.0.9",
-        "postcss-value-parser": "^4.2.0"
-      },
-      "peerDependencies": {
-        "postcss": "^8.2.2"
-      }
-    },
-    "node_modules/postcss-colormin": {
-      "version": "5.3.1",
-      "resolved": "https://registry.npmmirror.com/postcss-colormin/-/postcss-colormin-5.3.1.tgz",
-      "integrity": "sha512-UsWQG0AqTFQmpBegeLLc1+c3jIqBNB0zlDGRWR+dQ3pRKJL1oeMzyqmH3o2PIfn9MBdNrVPWhDbT769LxCTLJQ==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "browserslist": "^4.21.4",
-        "caniuse-api": "^3.0.0",
-        "colord": "^2.9.1",
-        "postcss-value-parser": "^4.2.0"
-      },
-      "engines": {
-        "node": "^10 || ^12 || >=14.0"
-      },
-      "peerDependencies": {
-        "postcss": "^8.2.15"
-      }
-    },
-    "node_modules/postcss-convert-values": {
-      "version": "5.1.3",
-      "resolved": "https://registry.npmmirror.com/postcss-convert-values/-/postcss-convert-values-5.1.3.tgz",
-      "integrity": "sha512-82pC1xkJZtcJEfiLw6UXnXVXScgtBrjlO5CBmuDQc+dlb88ZYheFsjTn40+zBVi3DkfF7iezO0nJUPLcJK3pvA==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "browserslist": "^4.21.4",
-        "postcss-value-parser": "^4.2.0"
-      },
-      "engines": {
-        "node": "^10 || ^12 || >=14.0"
-      },
-      "peerDependencies": {
-        "postcss": "^8.2.15"
-      }
-    },
-    "node_modules/postcss-discard-comments": {
-      "version": "5.1.2",
-      "resolved": "https://registry.npmmirror.com/postcss-discard-comments/-/postcss-discard-comments-5.1.2.tgz",
-      "integrity": "sha512-+L8208OVbHVF2UQf1iDmRcbdjJkuBF6IS29yBDSiWUIzpYaAhtNl6JYnYm12FnkeCwQqF5LeklOu6rAqgfBZqQ==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": "^10 || ^12 || >=14.0"
-      },
-      "peerDependencies": {
-        "postcss": "^8.2.15"
-      }
-    },
-    "node_modules/postcss-discard-duplicates": {
-      "version": "5.1.0",
-      "resolved": "https://registry.npmmirror.com/postcss-discard-duplicates/-/postcss-discard-duplicates-5.1.0.tgz",
-      "integrity": "sha512-zmX3IoSI2aoenxHV6C7plngHWWhUOV3sP1T8y2ifzxzbtnuhk1EdPwm0S1bIUNaJ2eNbWeGLEwzw8huPD67aQw==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": "^10 || ^12 || >=14.0"
-      },
-      "peerDependencies": {
-        "postcss": "^8.2.15"
-      }
-    },
-    "node_modules/postcss-discard-empty": {
-      "version": "5.1.1",
-      "resolved": "https://registry.npmmirror.com/postcss-discard-empty/-/postcss-discard-empty-5.1.1.tgz",
-      "integrity": "sha512-zPz4WljiSuLWsI0ir4Mcnr4qQQ5e1Ukc3i7UfE2XcrwKK2LIPIqE5jxMRxO6GbI3cv//ztXDsXwEWT3BHOGh3A==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": "^10 || ^12 || >=14.0"
-      },
-      "peerDependencies": {
-        "postcss": "^8.2.15"
-      }
-    },
-    "node_modules/postcss-discard-overridden": {
-      "version": "5.1.0",
-      "resolved": "https://registry.npmmirror.com/postcss-discard-overridden/-/postcss-discard-overridden-5.1.0.tgz",
-      "integrity": "sha512-21nOL7RqWR1kasIVdKs8HNqQJhFxLsyRfAnUDm4Fe4t4mCWL9OJiHvlHPjcd8zc5Myu89b/7wZDnOSjFgeWRtw==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": "^10 || ^12 || >=14.0"
-      },
-      "peerDependencies": {
-        "postcss": "^8.2.15"
-      }
-    },
-    "node_modules/postcss-loader": {
-      "version": "6.2.1",
-      "resolved": "https://registry.npmmirror.com/postcss-loader/-/postcss-loader-6.2.1.tgz",
-      "integrity": "sha512-WbbYpmAaKcux/P66bZ40bpWsBucjx/TTgVVzRZ9yUO8yQfVBlameJ0ZGVaPfH64hNSBh63a+ICP5nqOpBA0w+Q==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "cosmiconfig": "^7.0.0",
-        "klona": "^2.0.5",
-        "semver": "^7.3.5"
-      },
-      "engines": {
-        "node": ">= 12.13.0"
-      },
-      "funding": {
-        "type": "opencollective",
-        "url": "https://opencollective.com/webpack"
-      },
-      "peerDependencies": {
-        "postcss": "^7.0.0 || ^8.0.1",
-        "webpack": "^5.0.0"
-      }
-    },
-    "node_modules/postcss-loader/node_modules/semver": {
-      "version": "7.7.4",
-      "resolved": "https://registry.npmmirror.com/semver/-/semver-7.7.4.tgz",
-      "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==",
-      "dev": true,
-      "license": "ISC",
-      "bin": {
-        "semver": "bin/semver.js"
-      },
-      "engines": {
-        "node": ">=10"
-      }
-    },
-    "node_modules/postcss-merge-longhand": {
-      "version": "5.1.7",
-      "resolved": "https://registry.npmmirror.com/postcss-merge-longhand/-/postcss-merge-longhand-5.1.7.tgz",
-      "integrity": "sha512-YCI9gZB+PLNskrK0BB3/2OzPnGhPkBEwmwhfYk1ilBHYVAZB7/tkTHFBAnCrvBBOmeYyMYw3DMjT55SyxMBzjQ==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "postcss-value-parser": "^4.2.0",
-        "stylehacks": "^5.1.1"
-      },
-      "engines": {
-        "node": "^10 || ^12 || >=14.0"
-      },
-      "peerDependencies": {
-        "postcss": "^8.2.15"
-      }
-    },
-    "node_modules/postcss-merge-rules": {
-      "version": "5.1.4",
-      "resolved": "https://registry.npmmirror.com/postcss-merge-rules/-/postcss-merge-rules-5.1.4.tgz",
-      "integrity": "sha512-0R2IuYpgU93y9lhVbO/OylTtKMVcHb67zjWIfCiKR9rWL3GUk1677LAqD/BcHizukdZEjT8Ru3oHRoAYoJy44g==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "browserslist": "^4.21.4",
-        "caniuse-api": "^3.0.0",
-        "cssnano-utils": "^3.1.0",
-        "postcss-selector-parser": "^6.0.5"
-      },
-      "engines": {
-        "node": "^10 || ^12 || >=14.0"
-      },
-      "peerDependencies": {
-        "postcss": "^8.2.15"
-      }
-    },
-    "node_modules/postcss-minify-font-values": {
-      "version": "5.1.0",
-      "resolved": "https://registry.npmmirror.com/postcss-minify-font-values/-/postcss-minify-font-values-5.1.0.tgz",
-      "integrity": "sha512-el3mYTgx13ZAPPirSVsHqFzl+BBBDrXvbySvPGFnQcTI4iNslrPaFq4muTkLZmKlGk4gyFAYUBMH30+HurREyA==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "postcss-value-parser": "^4.2.0"
-      },
-      "engines": {
-        "node": "^10 || ^12 || >=14.0"
-      },
-      "peerDependencies": {
-        "postcss": "^8.2.15"
-      }
-    },
-    "node_modules/postcss-minify-gradients": {
-      "version": "5.1.1",
-      "resolved": "https://registry.npmmirror.com/postcss-minify-gradients/-/postcss-minify-gradients-5.1.1.tgz",
-      "integrity": "sha512-VGvXMTpCEo4qHTNSa9A0a3D+dxGFZCYwR6Jokk+/3oB6flu2/PnPXAh2x7x52EkY5xlIHLm+Le8tJxe/7TNhzw==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "colord": "^2.9.1",
-        "cssnano-utils": "^3.1.0",
-        "postcss-value-parser": "^4.2.0"
-      },
-      "engines": {
-        "node": "^10 || ^12 || >=14.0"
-      },
-      "peerDependencies": {
-        "postcss": "^8.2.15"
-      }
-    },
-    "node_modules/postcss-minify-params": {
-      "version": "5.1.4",
-      "resolved": "https://registry.npmmirror.com/postcss-minify-params/-/postcss-minify-params-5.1.4.tgz",
-      "integrity": "sha512-+mePA3MgdmVmv6g+30rn57USjOGSAyuxUmkfiWpzalZ8aiBkdPYjXWtHuwJGm1v5Ojy0Z0LaSYhHaLJQB0P8Jw==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "browserslist": "^4.21.4",
-        "cssnano-utils": "^3.1.0",
-        "postcss-value-parser": "^4.2.0"
-      },
-      "engines": {
-        "node": "^10 || ^12 || >=14.0"
-      },
-      "peerDependencies": {
-        "postcss": "^8.2.15"
-      }
-    },
-    "node_modules/postcss-minify-selectors": {
-      "version": "5.2.1",
-      "resolved": "https://registry.npmmirror.com/postcss-minify-selectors/-/postcss-minify-selectors-5.2.1.tgz",
-      "integrity": "sha512-nPJu7OjZJTsVUmPdm2TcaiohIwxP+v8ha9NehQ2ye9szv4orirRU3SDdtUmKH+10nzn0bAyOXZ0UEr7OpvLehg==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "postcss-selector-parser": "^6.0.5"
-      },
-      "engines": {
-        "node": "^10 || ^12 || >=14.0"
-      },
-      "peerDependencies": {
-        "postcss": "^8.2.15"
-      }
-    },
-    "node_modules/postcss-modules-extract-imports": {
-      "version": "3.1.0",
-      "resolved": "https://registry.npmmirror.com/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.1.0.tgz",
-      "integrity": "sha512-k3kNe0aNFQDAZGbin48pL2VNidTF0w4/eASDsxlyspobzU3wZQLOGj7L9gfRe0Jo9/4uud09DsjFNH7winGv8Q==",
-      "dev": true,
-      "license": "ISC",
-      "engines": {
-        "node": "^10 || ^12 || >= 14"
-      },
-      "peerDependencies": {
-        "postcss": "^8.1.0"
-      }
-    },
-    "node_modules/postcss-modules-local-by-default": {
-      "version": "4.2.0",
-      "resolved": "https://registry.npmmirror.com/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.2.0.tgz",
-      "integrity": "sha512-5kcJm/zk+GJDSfw+V/42fJ5fhjL5YbFDl8nVdXkJPLLW+Vf9mTD5Xe0wqIaDnLuL2U6cDNpTr+UQ+v2HWIBhzw==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "icss-utils": "^5.0.0",
-        "postcss-selector-parser": "^7.0.0",
-        "postcss-value-parser": "^4.1.0"
-      },
-      "engines": {
-        "node": "^10 || ^12 || >= 14"
-      },
-      "peerDependencies": {
-        "postcss": "^8.1.0"
-      }
-    },
-    "node_modules/postcss-modules-local-by-default/node_modules/postcss-selector-parser": {
-      "version": "7.1.1",
-      "resolved": "https://registry.npmmirror.com/postcss-selector-parser/-/postcss-selector-parser-7.1.1.tgz",
-      "integrity": "sha512-orRsuYpJVw8LdAwqqLykBj9ecS5/cRHlI5+nvTo8LcCKmzDmqVORXtOIYEEQuL9D4BxtA1lm5isAqzQZCoQ6Eg==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "cssesc": "^3.0.0",
-        "util-deprecate": "^1.0.2"
-      },
-      "engines": {
-        "node": ">=4"
-      }
-    },
-    "node_modules/postcss-modules-scope": {
-      "version": "3.2.1",
-      "resolved": "https://registry.npmmirror.com/postcss-modules-scope/-/postcss-modules-scope-3.2.1.tgz",
-      "integrity": "sha512-m9jZstCVaqGjTAuny8MdgE88scJnCiQSlSrOWcTQgM2t32UBe+MUmFSO5t7VMSfAf/FJKImAxBav8ooCHJXCJA==",
-      "dev": true,
-      "license": "ISC",
-      "dependencies": {
-        "postcss-selector-parser": "^7.0.0"
-      },
-      "engines": {
-        "node": "^10 || ^12 || >= 14"
-      },
-      "peerDependencies": {
-        "postcss": "^8.1.0"
-      }
-    },
-    "node_modules/postcss-modules-scope/node_modules/postcss-selector-parser": {
-      "version": "7.1.1",
-      "resolved": "https://registry.npmmirror.com/postcss-selector-parser/-/postcss-selector-parser-7.1.1.tgz",
-      "integrity": "sha512-orRsuYpJVw8LdAwqqLykBj9ecS5/cRHlI5+nvTo8LcCKmzDmqVORXtOIYEEQuL9D4BxtA1lm5isAqzQZCoQ6Eg==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "cssesc": "^3.0.0",
-        "util-deprecate": "^1.0.2"
-      },
-      "engines": {
-        "node": ">=4"
-      }
-    },
-    "node_modules/postcss-modules-values": {
-      "version": "4.0.0",
-      "resolved": "https://registry.npmmirror.com/postcss-modules-values/-/postcss-modules-values-4.0.0.tgz",
-      "integrity": "sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ==",
-      "dev": true,
-      "license": "ISC",
-      "dependencies": {
-        "icss-utils": "^5.0.0"
-      },
-      "engines": {
-        "node": "^10 || ^12 || >= 14"
-      },
-      "peerDependencies": {
-        "postcss": "^8.1.0"
-      }
-    },
-    "node_modules/postcss-normalize-charset": {
-      "version": "5.1.0",
-      "resolved": "https://registry.npmmirror.com/postcss-normalize-charset/-/postcss-normalize-charset-5.1.0.tgz",
-      "integrity": "sha512-mSgUJ+pd/ldRGVx26p2wz9dNZ7ji6Pn8VWBajMXFf8jk7vUoSrZ2lt/wZR7DtlZYKesmZI680qjr2CeFF2fbUg==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": "^10 || ^12 || >=14.0"
-      },
-      "peerDependencies": {
-        "postcss": "^8.2.15"
-      }
-    },
-    "node_modules/postcss-normalize-display-values": {
-      "version": "5.1.0",
-      "resolved": "https://registry.npmmirror.com/postcss-normalize-display-values/-/postcss-normalize-display-values-5.1.0.tgz",
-      "integrity": "sha512-WP4KIM4o2dazQXWmFaqMmcvsKmhdINFblgSeRgn8BJ6vxaMyaJkwAzpPpuvSIoG/rmX3M+IrRZEz2H0glrQNEA==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "postcss-value-parser": "^4.2.0"
-      },
-      "engines": {
-        "node": "^10 || ^12 || >=14.0"
-      },
-      "peerDependencies": {
-        "postcss": "^8.2.15"
-      }
-    },
-    "node_modules/postcss-normalize-positions": {
-      "version": "5.1.1",
-      "resolved": "https://registry.npmmirror.com/postcss-normalize-positions/-/postcss-normalize-positions-5.1.1.tgz",
-      "integrity": "sha512-6UpCb0G4eofTCQLFVuI3EVNZzBNPiIKcA1AKVka+31fTVySphr3VUgAIULBhxZkKgwLImhzMR2Bw1ORK+37INg==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "postcss-value-parser": "^4.2.0"
-      },
-      "engines": {
-        "node": "^10 || ^12 || >=14.0"
-      },
-      "peerDependencies": {
-        "postcss": "^8.2.15"
-      }
-    },
-    "node_modules/postcss-normalize-repeat-style": {
-      "version": "5.1.1",
-      "resolved": "https://registry.npmmirror.com/postcss-normalize-repeat-style/-/postcss-normalize-repeat-style-5.1.1.tgz",
-      "integrity": "sha512-mFpLspGWkQtBcWIRFLmewo8aC3ImN2i/J3v8YCFUwDnPu3Xz4rLohDO26lGjwNsQxB3YF0KKRwspGzE2JEuS0g==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "postcss-value-parser": "^4.2.0"
-      },
-      "engines": {
-        "node": "^10 || ^12 || >=14.0"
-      },
-      "peerDependencies": {
-        "postcss": "^8.2.15"
-      }
-    },
-    "node_modules/postcss-normalize-string": {
-      "version": "5.1.0",
-      "resolved": "https://registry.npmmirror.com/postcss-normalize-string/-/postcss-normalize-string-5.1.0.tgz",
-      "integrity": "sha512-oYiIJOf4T9T1N4i+abeIc7Vgm/xPCGih4bZz5Nm0/ARVJ7K6xrDlLwvwqOydvyL3RHNf8qZk6vo3aatiw/go3w==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "postcss-value-parser": "^4.2.0"
-      },
-      "engines": {
-        "node": "^10 || ^12 || >=14.0"
-      },
-      "peerDependencies": {
-        "postcss": "^8.2.15"
-      }
-    },
-    "node_modules/postcss-normalize-timing-functions": {
-      "version": "5.1.0",
-      "resolved": "https://registry.npmmirror.com/postcss-normalize-timing-functions/-/postcss-normalize-timing-functions-5.1.0.tgz",
-      "integrity": "sha512-DOEkzJ4SAXv5xkHl0Wa9cZLF3WCBhF3o1SKVxKQAa+0pYKlueTpCgvkFAHfk+Y64ezX9+nITGrDZeVGgITJXjg==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "postcss-value-parser": "^4.2.0"
-      },
-      "engines": {
-        "node": "^10 || ^12 || >=14.0"
-      },
-      "peerDependencies": {
-        "postcss": "^8.2.15"
-      }
-    },
-    "node_modules/postcss-normalize-unicode": {
-      "version": "5.1.1",
-      "resolved": "https://registry.npmmirror.com/postcss-normalize-unicode/-/postcss-normalize-unicode-5.1.1.tgz",
-      "integrity": "sha512-qnCL5jzkNUmKVhZoENp1mJiGNPcsJCs1aaRmURmeJGES23Z/ajaln+EPTD+rBeNkSryI+2WTdW+lwcVdOikrpA==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "browserslist": "^4.21.4",
-        "postcss-value-parser": "^4.2.0"
-      },
-      "engines": {
-        "node": "^10 || ^12 || >=14.0"
-      },
-      "peerDependencies": {
-        "postcss": "^8.2.15"
-      }
-    },
-    "node_modules/postcss-normalize-url": {
-      "version": "5.1.0",
-      "resolved": "https://registry.npmmirror.com/postcss-normalize-url/-/postcss-normalize-url-5.1.0.tgz",
-      "integrity": "sha512-5upGeDO+PVthOxSmds43ZeMeZfKH+/DKgGRD7TElkkyS46JXAUhMzIKiCa7BabPeIy3AQcTkXwVVN7DbqsiCew==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "normalize-url": "^6.0.1",
-        "postcss-value-parser": "^4.2.0"
-      },
-      "engines": {
-        "node": "^10 || ^12 || >=14.0"
-      },
-      "peerDependencies": {
-        "postcss": "^8.2.15"
-      }
-    },
-    "node_modules/postcss-normalize-whitespace": {
-      "version": "5.1.1",
-      "resolved": "https://registry.npmmirror.com/postcss-normalize-whitespace/-/postcss-normalize-whitespace-5.1.1.tgz",
-      "integrity": "sha512-83ZJ4t3NUDETIHTa3uEg6asWjSBYL5EdkVB0sDncx9ERzOKBVJIUeDO9RyA9Zwtig8El1d79HBp0JEi8wvGQnA==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "postcss-value-parser": "^4.2.0"
-      },
-      "engines": {
-        "node": "^10 || ^12 || >=14.0"
-      },
-      "peerDependencies": {
-        "postcss": "^8.2.15"
-      }
-    },
-    "node_modules/postcss-ordered-values": {
-      "version": "5.1.3",
-      "resolved": "https://registry.npmmirror.com/postcss-ordered-values/-/postcss-ordered-values-5.1.3.tgz",
-      "integrity": "sha512-9UO79VUhPwEkzbb3RNpqqghc6lcYej1aveQteWY+4POIwlqkYE21HKWaLDF6lWNuqCobEAyTovVhtI32Rbv2RQ==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "cssnano-utils": "^3.1.0",
-        "postcss-value-parser": "^4.2.0"
-      },
-      "engines": {
-        "node": "^10 || ^12 || >=14.0"
-      },
-      "peerDependencies": {
-        "postcss": "^8.2.15"
-      }
-    },
-    "node_modules/postcss-reduce-initial": {
-      "version": "5.1.2",
-      "resolved": "https://registry.npmmirror.com/postcss-reduce-initial/-/postcss-reduce-initial-5.1.2.tgz",
-      "integrity": "sha512-dE/y2XRaqAi6OvjzD22pjTUQ8eOfc6m/natGHgKFBK9DxFmIm69YmaRVQrGgFlEfc1HePIurY0TmDeROK05rIg==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "browserslist": "^4.21.4",
-        "caniuse-api": "^3.0.0"
-      },
-      "engines": {
-        "node": "^10 || ^12 || >=14.0"
-      },
-      "peerDependencies": {
-        "postcss": "^8.2.15"
-      }
-    },
-    "node_modules/postcss-reduce-transforms": {
-      "version": "5.1.0",
-      "resolved": "https://registry.npmmirror.com/postcss-reduce-transforms/-/postcss-reduce-transforms-5.1.0.tgz",
-      "integrity": "sha512-2fbdbmgir5AvpW9RLtdONx1QoYG2/EtqpNQbFASDlixBbAYuTcJ0dECwlqNqH7VbaUnEnh8SrxOe2sRIn24XyQ==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "postcss-value-parser": "^4.2.0"
-      },
-      "engines": {
-        "node": "^10 || ^12 || >=14.0"
-      },
-      "peerDependencies": {
-        "postcss": "^8.2.15"
-      }
-    },
-    "node_modules/postcss-selector-parser": {
-      "version": "6.1.2",
-      "resolved": "https://registry.npmmirror.com/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz",
-      "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "cssesc": "^3.0.0",
-        "util-deprecate": "^1.0.2"
-      },
-      "engines": {
-        "node": ">=4"
-      }
-    },
-    "node_modules/postcss-svgo": {
-      "version": "5.1.0",
-      "resolved": "https://registry.npmmirror.com/postcss-svgo/-/postcss-svgo-5.1.0.tgz",
-      "integrity": "sha512-D75KsH1zm5ZrHyxPakAxJWtkyXew5qwS70v56exwvw542d9CRtTo78K0WeFxZB4G7JXKKMbEZtZayTGdIky/eA==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "postcss-value-parser": "^4.2.0",
-        "svgo": "^2.7.0"
-      },
-      "engines": {
-        "node": "^10 || ^12 || >=14.0"
-      },
-      "peerDependencies": {
-        "postcss": "^8.2.15"
-      }
-    },
-    "node_modules/postcss-unique-selectors": {
-      "version": "5.1.1",
-      "resolved": "https://registry.npmmirror.com/postcss-unique-selectors/-/postcss-unique-selectors-5.1.1.tgz",
-      "integrity": "sha512-5JiODlELrz8L2HwxfPnhOWZYWDxVHWL83ufOv84NrcgipI7TaeRsatAhK4Tr2/ZiYldpK/wBvw5BD3qfaK96GA==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "postcss-selector-parser": "^6.0.5"
-      },
-      "engines": {
-        "node": "^10 || ^12 || >=14.0"
-      },
-      "peerDependencies": {
-        "postcss": "^8.2.15"
-      }
-    },
-    "node_modules/postcss-value-parser": {
-      "version": "4.2.0",
-      "resolved": "https://registry.npmmirror.com/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz",
-      "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==",
-      "dev": true,
-      "license": "MIT"
-    },
-    "node_modules/prelude-ls": {
-      "version": "1.2.1",
-      "resolved": "https://registry.npmmirror.com/prelude-ls/-/prelude-ls-1.2.1.tgz",
-      "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">= 0.8.0"
-      }
-    },
-    "node_modules/prettier": {
-      "version": "2.8.8",
-      "resolved": "https://registry.npmmirror.com/prettier/-/prettier-2.8.8.tgz",
-      "integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==",
-      "license": "MIT",
-      "optional": true,
-      "bin": {
-        "prettier": "bin-prettier.js"
-      },
-      "engines": {
-        "node": ">=10.13.0"
-      },
-      "funding": {
-        "url": "https://github.com/prettier/prettier?sponsor=1"
-      }
-    },
-    "node_modules/pretty-error": {
-      "version": "4.0.0",
-      "resolved": "https://registry.npmmirror.com/pretty-error/-/pretty-error-4.0.0.tgz",
-      "integrity": "sha512-AoJ5YMAcXKYxKhuJGdcvse+Voc6v1RgnsR3nWcYU7q4t6z0Q6T86sv5Zq8VIRbOWWFpvdGE83LtdSMNd+6Y0xw==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "lodash": "^4.17.20",
-        "renderkid": "^3.0.0"
-      }
-    },
-    "node_modules/process-nextick-args": {
-      "version": "2.0.1",
-      "resolved": "https://registry.npmmirror.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
-      "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==",
-      "dev": true,
-      "license": "MIT"
-    },
-    "node_modules/progress": {
-      "version": "2.0.3",
-      "resolved": "https://registry.npmmirror.com/progress/-/progress-2.0.3.tgz",
-      "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=0.4.0"
-      }
-    },
-    "node_modules/progress-webpack-plugin": {
-      "version": "1.0.16",
-      "resolved": "https://registry.npmmirror.com/progress-webpack-plugin/-/progress-webpack-plugin-1.0.16.tgz",
-      "integrity": "sha512-sdiHuuKOzELcBANHfrupYo+r99iPRyOnw15qX+rNlVUqXGfjXdH4IgxriKwG1kNJwVswKQHMdj1hYZMcb9jFaA==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "chalk": "^2.1.0",
-        "figures": "^2.0.0",
-        "log-update": "^2.3.0"
-      },
-      "engines": {
-        "node": ">= 10.13.0"
-      },
-      "peerDependencies": {
-        "webpack": "^2.0.0 || ^3.0.0 || ^4.0.0 || ^5.0.0"
-      }
-    },
-    "node_modules/progress-webpack-plugin/node_modules/ansi-styles": {
-      "version": "3.2.1",
-      "resolved": "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-3.2.1.tgz",
-      "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "color-convert": "^1.9.0"
-      },
-      "engines": {
-        "node": ">=4"
-      }
-    },
-    "node_modules/progress-webpack-plugin/node_modules/chalk": {
-      "version": "2.4.2",
-      "resolved": "https://registry.npmmirror.com/chalk/-/chalk-2.4.2.tgz",
-      "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "ansi-styles": "^3.2.1",
-        "escape-string-regexp": "^1.0.5",
-        "supports-color": "^5.3.0"
-      },
-      "engines": {
-        "node": ">=4"
-      }
-    },
-    "node_modules/progress-webpack-plugin/node_modules/color-convert": {
-      "version": "1.9.3",
-      "resolved": "https://registry.npmmirror.com/color-convert/-/color-convert-1.9.3.tgz",
-      "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "color-name": "1.1.3"
-      }
-    },
-    "node_modules/progress-webpack-plugin/node_modules/color-name": {
-      "version": "1.1.3",
-      "resolved": "https://registry.npmmirror.com/color-name/-/color-name-1.1.3.tgz",
-      "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==",
-      "dev": true,
-      "license": "MIT"
-    },
-    "node_modules/progress-webpack-plugin/node_modules/has-flag": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmmirror.com/has-flag/-/has-flag-3.0.0.tgz",
-      "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=4"
-      }
-    },
-    "node_modules/progress-webpack-plugin/node_modules/supports-color": {
-      "version": "5.5.0",
-      "resolved": "https://registry.npmmirror.com/supports-color/-/supports-color-5.5.0.tgz",
-      "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "has-flag": "^3.0.0"
-      },
-      "engines": {
-        "node": ">=4"
-      }
-    },
-    "node_modules/proxy-addr": {
-      "version": "2.0.7",
-      "resolved": "https://registry.npmmirror.com/proxy-addr/-/proxy-addr-2.0.7.tgz",
-      "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "forwarded": "0.2.0",
-        "ipaddr.js": "1.9.1"
-      },
-      "engines": {
-        "node": ">= 0.10"
-      }
-    },
-    "node_modules/proxy-addr/node_modules/ipaddr.js": {
-      "version": "1.9.1",
-      "resolved": "https://registry.npmmirror.com/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
-      "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">= 0.10"
-      }
-    },
-    "node_modules/proxy-from-env": {
-      "version": "2.1.0",
-      "resolved": "https://registry.npmmirror.com/proxy-from-env/-/proxy-from-env-2.1.0.tgz",
-      "integrity": "sha512-cJ+oHTW1VAEa8cJslgmUZrc+sjRKgAKl3Zyse6+PV38hZe/V6Z14TbCuXcan9F9ghlz4QrFr2c92TNF82UkYHA==",
-      "license": "MIT",
-      "engines": {
-        "node": ">=10"
-      }
-    },
-    "node_modules/pseudomap": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmmirror.com/pseudomap/-/pseudomap-1.0.2.tgz",
-      "integrity": "sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ==",
-      "dev": true,
-      "license": "ISC"
-    },
-    "node_modules/pump": {
-      "version": "3.0.4",
-      "resolved": "https://registry.npmmirror.com/pump/-/pump-3.0.4.tgz",
-      "integrity": "sha512-VS7sjc6KR7e1ukRFhQSY5LM2uBWAUPiOPa/A3mkKmiMwSmRFUITt0xuj+/lesgnCv+dPIEYlkzrcyXgquIHMcA==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "end-of-stream": "^1.1.0",
-        "once": "^1.3.1"
-      }
-    },
-    "node_modules/punycode": {
-      "version": "2.3.1",
-      "resolved": "https://registry.npmmirror.com/punycode/-/punycode-2.3.1.tgz",
-      "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=6"
-      }
-    },
-    "node_modules/qs": {
-      "version": "6.14.2",
-      "resolved": "https://registry.npmmirror.com/qs/-/qs-6.14.2.tgz",
-      "integrity": "sha512-V/yCWTTF7VJ9hIh18Ugr2zhJMP01MY7c5kh4J870L7imm6/DIzBsNLTXzMwUA3yZ5b/KBqLx8Kp3uRvd7xSe3Q==",
-      "dev": true,
-      "license": "BSD-3-Clause",
-      "dependencies": {
-        "side-channel": "^1.1.0"
-      },
-      "engines": {
-        "node": ">=0.6"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
-    "node_modules/queue-microtask": {
-      "version": "1.2.3",
-      "resolved": "https://registry.npmmirror.com/queue-microtask/-/queue-microtask-1.2.3.tgz",
-      "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==",
-      "dev": true,
-      "funding": [
-        {
-          "type": "github",
-          "url": "https://github.com/sponsors/feross"
-        },
-        {
-          "type": "patreon",
-          "url": "https://www.patreon.com/feross"
-        },
-        {
-          "type": "consulting",
-          "url": "https://feross.org/support"
-        }
-      ],
-      "license": "MIT"
-    },
-    "node_modules/quill": {
-      "version": "1.3.7",
-      "resolved": "https://registry.npmmirror.com/quill/-/quill-1.3.7.tgz",
-      "integrity": "sha512-hG/DVzh/TiknWtE6QmWAF/pxoZKYxfe3J/d/+ShUWkDvvkZQVTPeVmUJVu1uE6DDooC4fWTiCLh84ul89oNz5g==",
-      "license": "BSD-3-Clause",
-      "dependencies": {
-        "clone": "^2.1.1",
-        "deep-equal": "^1.0.1",
-        "eventemitter3": "^2.0.3",
-        "extend": "^3.0.2",
-        "parchment": "^1.1.4",
-        "quill-delta": "^3.6.2"
-      }
-    },
-    "node_modules/quill-delta": {
-      "version": "3.6.3",
-      "resolved": "https://registry.npmmirror.com/quill-delta/-/quill-delta-3.6.3.tgz",
-      "integrity": "sha512-wdIGBlcX13tCHOXGMVnnTVFtGRLoP0imqxM696fIPwIf5ODIYUHIvHbZcyvGlZFiFhK5XzDC2lpjbxRhnM05Tg==",
-      "license": "MIT",
-      "dependencies": {
-        "deep-equal": "^1.0.1",
-        "extend": "^3.0.2",
-        "fast-diff": "1.1.2"
-      },
-      "engines": {
-        "node": ">=0.10"
-      }
-    },
-    "node_modules/quill/node_modules/clone": {
-      "version": "2.1.2",
-      "resolved": "https://registry.npmmirror.com/clone/-/clone-2.1.2.tgz",
-      "integrity": "sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w==",
-      "license": "MIT",
-      "engines": {
-        "node": ">=0.8"
-      }
-    },
-    "node_modules/quill/node_modules/eventemitter3": {
-      "version": "2.0.3",
-      "resolved": "https://registry.npmmirror.com/eventemitter3/-/eventemitter3-2.0.3.tgz",
-      "integrity": "sha512-jLN68Dx5kyFHaePoXWPsCGW5qdyZQtLYHkxkg02/Mz6g0kYpDx4FyP6XfArhQdlOC4b8Mv+EMxPo/8La7Tzghg==",
-      "license": "MIT"
-    },
-    "node_modules/randombytes": {
-      "version": "2.1.0",
-      "resolved": "https://registry.npmmirror.com/randombytes/-/randombytes-2.1.0.tgz",
-      "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "safe-buffer": "^5.1.0"
-      }
-    },
-    "node_modules/range-parser": {
-      "version": "1.2.1",
-      "resolved": "https://registry.npmmirror.com/range-parser/-/range-parser-1.2.1.tgz",
-      "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">= 0.6"
-      }
-    },
-    "node_modules/raw-body": {
-      "version": "2.5.3",
-      "resolved": "https://registry.npmmirror.com/raw-body/-/raw-body-2.5.3.tgz",
-      "integrity": "sha512-s4VSOf6yN0rvbRZGxs8Om5CWj6seneMwK3oDb4lWDH0UPhWcxwOWw5+qk24bxq87szX1ydrwylIOp2uG1ojUpA==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "bytes": "~3.1.2",
-        "http-errors": "~2.0.1",
-        "iconv-lite": "~0.4.24",
-        "unpipe": "~1.0.0"
-      },
-      "engines": {
-        "node": ">= 0.8"
-      }
-    },
-    "node_modules/read-pkg": {
-      "version": "5.2.0",
-      "resolved": "https://registry.npmmirror.com/read-pkg/-/read-pkg-5.2.0.tgz",
-      "integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@types/normalize-package-data": "^2.4.0",
-        "normalize-package-data": "^2.5.0",
-        "parse-json": "^5.0.0",
-        "type-fest": "^0.6.0"
-      },
-      "engines": {
-        "node": ">=8"
-      }
-    },
-    "node_modules/read-pkg-up": {
-      "version": "7.0.1",
-      "resolved": "https://registry.npmmirror.com/read-pkg-up/-/read-pkg-up-7.0.1.tgz",
-      "integrity": "sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "find-up": "^4.1.0",
-        "read-pkg": "^5.2.0",
-        "type-fest": "^0.8.1"
-      },
-      "engines": {
-        "node": ">=8"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/sindresorhus"
-      }
-    },
-    "node_modules/read-pkg-up/node_modules/type-fest": {
-      "version": "0.8.1",
-      "resolved": "https://registry.npmmirror.com/type-fest/-/type-fest-0.8.1.tgz",
-      "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==",
-      "dev": true,
-      "license": "(MIT OR CC0-1.0)",
-      "engines": {
-        "node": ">=8"
-      }
-    },
-    "node_modules/readable-stream": {
-      "version": "3.6.2",
-      "resolved": "https://registry.npmmirror.com/readable-stream/-/readable-stream-3.6.2.tgz",
-      "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "inherits": "^2.0.3",
-        "string_decoder": "^1.1.1",
-        "util-deprecate": "^1.0.1"
-      },
-      "engines": {
-        "node": ">= 6"
-      }
-    },
-    "node_modules/readdirp": {
-      "version": "3.6.0",
-      "resolved": "https://registry.npmmirror.com/readdirp/-/readdirp-3.6.0.tgz",
-      "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "picomatch": "^2.2.1"
-      },
-      "engines": {
-        "node": ">=8.10.0"
-      }
-    },
-    "node_modules/regenerate": {
-      "version": "1.4.2",
-      "resolved": "https://registry.npmmirror.com/regenerate/-/regenerate-1.4.2.tgz",
-      "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==",
-      "dev": true,
-      "license": "MIT"
-    },
-    "node_modules/regenerate-unicode-properties": {
-      "version": "10.2.2",
-      "resolved": "https://registry.npmmirror.com/regenerate-unicode-properties/-/regenerate-unicode-properties-10.2.2.tgz",
-      "integrity": "sha512-m03P+zhBeQd1RGnYxrGyDAPpWX/epKirLrp8e3qevZdVkKtnCrjjWczIbYc8+xd6vcTStVlqfycTx1KR4LOr0g==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "regenerate": "^1.4.2"
-      },
-      "engines": {
-        "node": ">=4"
-      }
-    },
-    "node_modules/regenerator-runtime": {
-      "version": "0.11.1",
-      "resolved": "https://registry.npmmirror.com/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz",
-      "integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==",
-      "license": "MIT"
-    },
-    "node_modules/regexp.prototype.flags": {
-      "version": "1.5.4",
-      "resolved": "https://registry.npmmirror.com/regexp.prototype.flags/-/regexp.prototype.flags-1.5.4.tgz",
-      "integrity": "sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==",
-      "license": "MIT",
-      "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"
-      },
-      "engines": {
-        "node": ">= 0.4"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
-    "node_modules/regexpp": {
-      "version": "3.2.0",
-      "resolved": "https://registry.npmmirror.com/regexpp/-/regexpp-3.2.0.tgz",
-      "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=8"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/mysticatea"
-      }
-    },
-    "node_modules/regexpu-core": {
-      "version": "6.4.0",
-      "resolved": "https://registry.npmmirror.com/regexpu-core/-/regexpu-core-6.4.0.tgz",
-      "integrity": "sha512-0ghuzq67LI9bLXpOX/ISfve/Mq33a4aFRzoQYhnnok1JOFpmE/A2TBGkNVenOGEeSBCjIiWcc6MVOG5HEQv0sA==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "regenerate": "^1.4.2",
-        "regenerate-unicode-properties": "^10.2.2",
-        "regjsgen": "^0.8.0",
-        "regjsparser": "^0.13.0",
-        "unicode-match-property-ecmascript": "^2.0.0",
-        "unicode-match-property-value-ecmascript": "^2.2.1"
-      },
-      "engines": {
-        "node": ">=4"
-      }
-    },
-    "node_modules/regjsgen": {
-      "version": "0.8.0",
-      "resolved": "https://registry.npmmirror.com/regjsgen/-/regjsgen-0.8.0.tgz",
-      "integrity": "sha512-RvwtGe3d7LvWiDQXeQw8p5asZUmfU1G/l6WbUXeHta7Y2PEIvBTwH6E2EfmYUK8pxcxEdEmaomqyp0vZZ7C+3Q==",
-      "dev": true,
-      "license": "MIT"
-    },
-    "node_modules/regjsparser": {
-      "version": "0.13.1",
-      "resolved": "https://registry.npmmirror.com/regjsparser/-/regjsparser-0.13.1.tgz",
-      "integrity": "sha512-dLsljMd9sqwRkby8zhO1gSg3PnJIBFid8f4CQj/sXx+7cKx+E7u0PKhZ+U4wmhx7EfmtvnA318oVaIkAB1lRJw==",
-      "dev": true,
-      "license": "BSD-2-Clause",
-      "dependencies": {
-        "jsesc": "~3.1.0"
-      },
-      "bin": {
-        "regjsparser": "bin/parser"
-      }
-    },
-    "node_modules/relateurl": {
-      "version": "0.2.7",
-      "resolved": "https://registry.npmmirror.com/relateurl/-/relateurl-0.2.7.tgz",
-      "integrity": "sha512-G08Dxvm4iDN3MLM0EsP62EDV9IuhXPR6blNz6Utcp7zyV3tr4HVNINt6MpaRWbxoOHT3Q7YN2P+jaHX8vUbgog==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">= 0.10"
-      }
-    },
-    "node_modules/renderkid": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmmirror.com/renderkid/-/renderkid-3.0.0.tgz",
-      "integrity": "sha512-q/7VIQA8lmM1hF+jn+sFSPWGlMkSAeNYcPLmDQx2zzuiDfaLrOmumR8iaUKlenFgh0XRPIUeSPlH3A+AW3Z5pg==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "css-select": "^4.1.3",
-        "dom-converter": "^0.2.0",
-        "htmlparser2": "^6.1.0",
-        "lodash": "^4.17.21",
-        "strip-ansi": "^6.0.1"
-      }
-    },
-    "node_modules/require-directory": {
-      "version": "2.1.1",
-      "resolved": "https://registry.npmmirror.com/require-directory/-/require-directory-2.1.1.tgz",
-      "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=0.10.0"
-      }
-    },
-    "node_modules/require-from-string": {
-      "version": "2.0.2",
-      "resolved": "https://registry.npmmirror.com/require-from-string/-/require-from-string-2.0.2.tgz",
-      "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=0.10.0"
-      }
-    },
-    "node_modules/requires-port": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmmirror.com/requires-port/-/requires-port-1.0.0.tgz",
-      "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==",
-      "dev": true,
-      "license": "MIT"
-    },
-    "node_modules/resize-observer-polyfill": {
-      "version": "1.5.1",
-      "resolved": "https://registry.npmmirror.com/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz",
-      "integrity": "sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg==",
-      "license": "MIT"
-    },
-    "node_modules/resolve": {
-      "version": "1.22.12",
-      "resolved": "https://registry.npmmirror.com/resolve/-/resolve-1.22.12.tgz",
-      "integrity": "sha512-TyeJ1zif53BPfHootBGwPRYT1RUt6oGWsaQr8UyZW/eAm9bKoijtvruSDEmZHm92CwS9nj7/fWttqPCgzep8CA==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "es-errors": "^1.3.0",
-        "is-core-module": "^2.16.1",
-        "path-parse": "^1.0.7",
-        "supports-preserve-symlinks-flag": "^1.0.0"
-      },
-      "bin": {
-        "resolve": "bin/resolve"
-      },
-      "engines": {
-        "node": ">= 0.4"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
-    "node_modules/resolve-from": {
-      "version": "4.0.0",
-      "resolved": "https://registry.npmmirror.com/resolve-from/-/resolve-from-4.0.0.tgz",
-      "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=4"
-      }
-    },
-    "node_modules/restore-cursor": {
-      "version": "3.1.0",
-      "resolved": "https://registry.npmmirror.com/restore-cursor/-/restore-cursor-3.1.0.tgz",
-      "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "onetime": "^5.1.0",
-        "signal-exit": "^3.0.2"
-      },
-      "engines": {
-        "node": ">=8"
-      }
-    },
-    "node_modules/retry": {
-      "version": "0.13.1",
-      "resolved": "https://registry.npmmirror.com/retry/-/retry-0.13.1.tgz",
-      "integrity": "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">= 4"
-      }
-    },
-    "node_modules/reusify": {
-      "version": "1.1.0",
-      "resolved": "https://registry.npmmirror.com/reusify/-/reusify-1.1.0.tgz",
-      "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "iojs": ">=1.0.0",
-        "node": ">=0.10.0"
-      }
-    },
-    "node_modules/rimraf": {
-      "version": "3.0.2",
-      "resolved": "https://registry.npmmirror.com/rimraf/-/rimraf-3.0.2.tgz",
-      "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==",
-      "deprecated": "Rimraf versions prior to v4 are no longer supported",
-      "dev": true,
-      "license": "ISC",
-      "dependencies": {
-        "glob": "^7.1.3"
-      },
-      "bin": {
-        "rimraf": "bin.js"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/isaacs"
-      }
-    },
-    "node_modules/run-parallel": {
-      "version": "1.2.0",
-      "resolved": "https://registry.npmmirror.com/run-parallel/-/run-parallel-1.2.0.tgz",
-      "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==",
-      "dev": true,
-      "funding": [
-        {
-          "type": "github",
-          "url": "https://github.com/sponsors/feross"
-        },
-        {
-          "type": "patreon",
-          "url": "https://www.patreon.com/feross"
-        },
-        {
-          "type": "consulting",
-          "url": "https://feross.org/support"
-        }
-      ],
-      "license": "MIT",
-      "dependencies": {
-        "queue-microtask": "^1.2.2"
-      }
-    },
-    "node_modules/safe-buffer": {
-      "version": "5.2.1",
-      "resolved": "https://registry.npmmirror.com/safe-buffer/-/safe-buffer-5.2.1.tgz",
-      "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
-      "dev": true,
-      "funding": [
-        {
-          "type": "github",
-          "url": "https://github.com/sponsors/feross"
-        },
-        {
-          "type": "patreon",
-          "url": "https://www.patreon.com/feross"
-        },
-        {
-          "type": "consulting",
-          "url": "https://feross.org/support"
-        }
-      ],
-      "license": "MIT"
-    },
-    "node_modules/safer-buffer": {
-      "version": "2.1.2",
-      "resolved": "https://registry.npmmirror.com/safer-buffer/-/safer-buffer-2.1.2.tgz",
-      "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
-      "dev": true,
-      "license": "MIT"
-    },
-    "node_modules/sax": {
-      "version": "1.6.0",
-      "resolved": "https://registry.npmmirror.com/sax/-/sax-1.6.0.tgz",
-      "integrity": "sha512-6R3J5M4AcbtLUdZmRv2SygeVaM7IhrLXu9BmnOGmmACak8fiUtOsYNWUS4uK7upbmHIBbLBeFeI//477BKLBzA==",
-      "dev": true,
-      "license": "BlueOak-1.0.0",
-      "engines": {
-        "node": ">=11.0.0"
-      }
-    },
-    "node_modules/schema-utils": {
-      "version": "2.7.1",
-      "resolved": "https://registry.npmmirror.com/schema-utils/-/schema-utils-2.7.1.tgz",
-      "integrity": "sha512-SHiNtMOUGWBQJwzISiVYKu82GiV4QYGePp3odlY1tuKO7gPtphAT5R/py0fA6xtbgLL/RvtJZnU9b8s0F1q0Xg==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@types/json-schema": "^7.0.5",
-        "ajv": "^6.12.4",
-        "ajv-keywords": "^3.5.2"
-      },
-      "engines": {
-        "node": ">= 8.9.0"
-      },
-      "funding": {
-        "type": "opencollective",
-        "url": "https://opencollective.com/webpack"
-      }
-    },
-    "node_modules/select-hose": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmmirror.com/select-hose/-/select-hose-2.0.0.tgz",
-      "integrity": "sha512-mEugaLK+YfkijB4fx0e6kImuJdCIt2LxCRcbEYPqRGCs4F2ogyfZU5IAZRdjCP8JPq2AtdNoC/Dux63d9Kiryg==",
-      "dev": true,
-      "license": "MIT"
-    },
-    "node_modules/selfsigned": {
-      "version": "2.4.1",
-      "resolved": "https://registry.npmmirror.com/selfsigned/-/selfsigned-2.4.1.tgz",
-      "integrity": "sha512-th5B4L2U+eGLq1TVh7zNRGBapioSORUeymIydxgFpwww9d2qyKvtuPU2jJuHvYAwwqi2Y596QBL3eEqcPEYL8Q==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@types/node-forge": "^1.3.0",
-        "node-forge": "^1"
-      },
-      "engines": {
-        "node": ">=10"
-      }
-    },
-    "node_modules/semver": {
-      "version": "6.3.1",
-      "resolved": "https://registry.npmmirror.com/semver/-/semver-6.3.1.tgz",
-      "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
-      "dev": true,
-      "license": "ISC",
-      "bin": {
-        "semver": "bin/semver.js"
-      }
-    },
-    "node_modules/send": {
-      "version": "0.19.2",
-      "resolved": "https://registry.npmmirror.com/send/-/send-0.19.2.tgz",
-      "integrity": "sha512-VMbMxbDeehAxpOtWJXlcUS5E8iXh6QmN+BkRX1GARS3wRaXEEgzCcB10gTQazO42tpNIya8xIyNx8fll1OFPrg==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "debug": "2.6.9",
-        "depd": "2.0.0",
-        "destroy": "1.2.0",
-        "encodeurl": "~2.0.0",
-        "escape-html": "~1.0.3",
-        "etag": "~1.8.1",
-        "fresh": "~0.5.2",
-        "http-errors": "~2.0.1",
-        "mime": "1.6.0",
-        "ms": "2.1.3",
-        "on-finished": "~2.4.1",
-        "range-parser": "~1.2.1",
-        "statuses": "~2.0.2"
-      },
-      "engines": {
-        "node": ">= 0.8.0"
-      }
-    },
-    "node_modules/send/node_modules/debug": {
-      "version": "2.6.9",
-      "resolved": "https://registry.npmmirror.com/debug/-/debug-2.6.9.tgz",
-      "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "ms": "2.0.0"
-      }
-    },
-    "node_modules/send/node_modules/debug/node_modules/ms": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmmirror.com/ms/-/ms-2.0.0.tgz",
-      "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
-      "dev": true,
-      "license": "MIT"
-    },
-    "node_modules/serialize-javascript": {
-      "version": "6.0.2",
-      "resolved": "https://registry.npmmirror.com/serialize-javascript/-/serialize-javascript-6.0.2.tgz",
-      "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==",
-      "dev": true,
-      "license": "BSD-3-Clause",
-      "dependencies": {
-        "randombytes": "^2.1.0"
-      }
-    },
-    "node_modules/serve-index": {
-      "version": "1.9.2",
-      "resolved": "https://registry.npmmirror.com/serve-index/-/serve-index-1.9.2.tgz",
-      "integrity": "sha512-KDj11HScOaLmrPxl70KYNW1PksP4Nb/CLL2yvC+Qd2kHMPEEpfc4Re2e4FOay+bC/+XQl/7zAcWON3JVo5v3KQ==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "accepts": "~1.3.8",
-        "batch": "0.6.1",
-        "debug": "2.6.9",
-        "escape-html": "~1.0.3",
-        "http-errors": "~1.8.0",
-        "mime-types": "~2.1.35",
-        "parseurl": "~1.3.3"
-      },
-      "engines": {
-        "node": ">= 0.8.0"
-      },
-      "funding": {
-        "type": "opencollective",
-        "url": "https://opencollective.com/express"
-      }
-    },
-    "node_modules/serve-index/node_modules/debug": {
-      "version": "2.6.9",
-      "resolved": "https://registry.npmmirror.com/debug/-/debug-2.6.9.tgz",
-      "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "ms": "2.0.0"
-      }
-    },
-    "node_modules/serve-index/node_modules/depd": {
-      "version": "1.1.2",
-      "resolved": "https://registry.npmmirror.com/depd/-/depd-1.1.2.tgz",
-      "integrity": "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">= 0.6"
-      }
-    },
-    "node_modules/serve-index/node_modules/http-errors": {
-      "version": "1.8.1",
-      "resolved": "https://registry.npmmirror.com/http-errors/-/http-errors-1.8.1.tgz",
-      "integrity": "sha512-Kpk9Sm7NmI+RHhnj6OIWDI1d6fIoFAtFt9RLaTMRlg/8w49juAStsrBgp0Dp4OdxdVbRIeKhtCUvoi/RuAhO4g==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "depd": "~1.1.2",
-        "inherits": "2.0.4",
-        "setprototypeof": "1.2.0",
-        "statuses": ">= 1.5.0 < 2",
-        "toidentifier": "1.0.1"
-      },
-      "engines": {
-        "node": ">= 0.6"
-      }
-    },
-    "node_modules/serve-index/node_modules/ms": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmmirror.com/ms/-/ms-2.0.0.tgz",
-      "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
-      "dev": true,
-      "license": "MIT"
-    },
-    "node_modules/serve-index/node_modules/statuses": {
-      "version": "1.5.0",
-      "resolved": "https://registry.npmmirror.com/statuses/-/statuses-1.5.0.tgz",
-      "integrity": "sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">= 0.6"
-      }
-    },
-    "node_modules/serve-static": {
-      "version": "1.16.3",
-      "resolved": "https://registry.npmmirror.com/serve-static/-/serve-static-1.16.3.tgz",
-      "integrity": "sha512-x0RTqQel6g5SY7Lg6ZreMmsOzncHFU7nhnRWkKgWuMTu5NN0DR5oruckMqRvacAN9d5w6ARnRBXl9xhDCgfMeA==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "encodeurl": "~2.0.0",
-        "escape-html": "~1.0.3",
-        "parseurl": "~1.3.3",
-        "send": "~0.19.1"
-      },
-      "engines": {
-        "node": ">= 0.8.0"
-      }
-    },
-    "node_modules/set-function-length": {
-      "version": "1.2.2",
-      "resolved": "https://registry.npmmirror.com/set-function-length/-/set-function-length-1.2.2.tgz",
-      "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==",
-      "license": "MIT",
-      "dependencies": {
-        "define-data-property": "^1.1.4",
-        "es-errors": "^1.3.0",
-        "function-bind": "^1.1.2",
-        "get-intrinsic": "^1.2.4",
-        "gopd": "^1.0.1",
-        "has-property-descriptors": "^1.0.2"
-      },
-      "engines": {
-        "node": ">= 0.4"
-      }
-    },
-    "node_modules/set-function-name": {
-      "version": "2.0.2",
-      "resolved": "https://registry.npmmirror.com/set-function-name/-/set-function-name-2.0.2.tgz",
-      "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==",
-      "license": "MIT",
-      "dependencies": {
-        "define-data-property": "^1.1.4",
-        "es-errors": "^1.3.0",
-        "functions-have-names": "^1.2.3",
-        "has-property-descriptors": "^1.0.2"
-      },
-      "engines": {
-        "node": ">= 0.4"
-      }
-    },
-    "node_modules/setprototypeof": {
-      "version": "1.2.0",
-      "resolved": "https://registry.npmmirror.com/setprototypeof/-/setprototypeof-1.2.0.tgz",
-      "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==",
-      "dev": true,
-      "license": "ISC"
-    },
-    "node_modules/shallow-clone": {
-      "version": "3.0.1",
-      "resolved": "https://registry.npmmirror.com/shallow-clone/-/shallow-clone-3.0.1.tgz",
-      "integrity": "sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "kind-of": "^6.0.2"
-      },
-      "engines": {
-        "node": ">=8"
-      }
-    },
-    "node_modules/shebang-command": {
-      "version": "1.2.0",
-      "resolved": "https://registry.npmmirror.com/shebang-command/-/shebang-command-1.2.0.tgz",
-      "integrity": "sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "shebang-regex": "^1.0.0"
-      },
-      "engines": {
-        "node": ">=0.10.0"
-      }
-    },
-    "node_modules/shebang-regex": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmmirror.com/shebang-regex/-/shebang-regex-1.0.0.tgz",
-      "integrity": "sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=0.10.0"
-      }
-    },
-    "node_modules/shell-quote": {
-      "version": "1.8.3",
-      "resolved": "https://registry.npmmirror.com/shell-quote/-/shell-quote-1.8.3.tgz",
-      "integrity": "sha512-ObmnIF4hXNg1BqhnHmgbDETF8dLPCggZWBjkQfhZpbszZnYur5DUljTcCHii5LC3J5E0yeO/1LIMyH+UvHQgyw==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">= 0.4"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
-    "node_modules/side-channel": {
-      "version": "1.1.0",
-      "resolved": "https://registry.npmmirror.com/side-channel/-/side-channel-1.1.0.tgz",
-      "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "es-errors": "^1.3.0",
-        "object-inspect": "^1.13.3",
-        "side-channel-list": "^1.0.0",
-        "side-channel-map": "^1.0.1",
-        "side-channel-weakmap": "^1.0.2"
-      },
-      "engines": {
-        "node": ">= 0.4"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
-    "node_modules/side-channel-list": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmmirror.com/side-channel-list/-/side-channel-list-1.0.1.tgz",
-      "integrity": "sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "es-errors": "^1.3.0",
-        "object-inspect": "^1.13.4"
-      },
-      "engines": {
-        "node": ">= 0.4"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
-    "node_modules/side-channel-map": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmmirror.com/side-channel-map/-/side-channel-map-1.0.1.tgz",
-      "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "call-bound": "^1.0.2",
-        "es-errors": "^1.3.0",
-        "get-intrinsic": "^1.2.5",
-        "object-inspect": "^1.13.3"
-      },
-      "engines": {
-        "node": ">= 0.4"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
-    "node_modules/side-channel-weakmap": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmmirror.com/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz",
-      "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "call-bound": "^1.0.2",
-        "es-errors": "^1.3.0",
-        "get-intrinsic": "^1.2.5",
-        "object-inspect": "^1.13.3",
-        "side-channel-map": "^1.0.1"
-      },
-      "engines": {
-        "node": ">= 0.4"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
-    "node_modules/signal-exit": {
-      "version": "3.0.7",
-      "resolved": "https://registry.npmmirror.com/signal-exit/-/signal-exit-3.0.7.tgz",
-      "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==",
-      "dev": true,
-      "license": "ISC"
-    },
-    "node_modules/sirv": {
-      "version": "2.0.4",
-      "resolved": "https://registry.npmmirror.com/sirv/-/sirv-2.0.4.tgz",
-      "integrity": "sha512-94Bdh3cC2PKrbgSOUqTiGPWVZeSiXfKOVZNJniWoqrWrRkB1CJzBU3NEbiTsPcYy1lDsANA/THzS+9WBiy5nfQ==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@polka/url": "^1.0.0-next.24",
-        "mrmime": "^2.0.0",
-        "totalist": "^3.0.0"
-      },
-      "engines": {
-        "node": ">= 10"
-      }
-    },
-    "node_modules/slash": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmmirror.com/slash/-/slash-3.0.0.tgz",
-      "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=8"
-      }
-    },
-    "node_modules/slice-ansi": {
-      "version": "4.0.0",
-      "resolved": "https://registry.npmmirror.com/slice-ansi/-/slice-ansi-4.0.0.tgz",
-      "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "ansi-styles": "^4.0.0",
-        "astral-regex": "^2.0.0",
-        "is-fullwidth-code-point": "^3.0.0"
-      },
-      "engines": {
-        "node": ">=10"
-      },
-      "funding": {
-        "url": "https://github.com/chalk/slice-ansi?sponsor=1"
-      }
-    },
-    "node_modules/sockjs": {
-      "version": "0.3.24",
-      "resolved": "https://registry.npmmirror.com/sockjs/-/sockjs-0.3.24.tgz",
-      "integrity": "sha512-GJgLTZ7vYb/JtPSSZ10hsOYIvEYsjbNU+zPdIHcUaWVNUEPivzxku31865sSSud0Da0W4lEeOPlmw93zLQchuQ==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "faye-websocket": "^0.11.3",
-        "uuid": "^8.3.2",
-        "websocket-driver": "^0.7.4"
-      }
-    },
-    "node_modules/source-map": {
-      "version": "0.6.1",
-      "resolved": "https://registry.npmmirror.com/source-map/-/source-map-0.6.1.tgz",
-      "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
-      "license": "BSD-3-Clause",
-      "engines": {
-        "node": ">=0.10.0"
-      }
-    },
-    "node_modules/source-map-js": {
-      "version": "1.2.1",
-      "resolved": "https://registry.npmmirror.com/source-map-js/-/source-map-js-1.2.1.tgz",
-      "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
-      "license": "BSD-3-Clause",
-      "engines": {
-        "node": ">=0.10.0"
-      }
-    },
-    "node_modules/source-map-support": {
-      "version": "0.5.21",
-      "resolved": "https://registry.npmmirror.com/source-map-support/-/source-map-support-0.5.21.tgz",
-      "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "buffer-from": "^1.0.0",
-        "source-map": "^0.6.0"
-      }
-    },
-    "node_modules/spdx-correct": {
-      "version": "3.2.0",
-      "resolved": "https://registry.npmmirror.com/spdx-correct/-/spdx-correct-3.2.0.tgz",
-      "integrity": "sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==",
-      "dev": true,
-      "license": "Apache-2.0",
-      "dependencies": {
-        "spdx-expression-parse": "^3.0.0",
-        "spdx-license-ids": "^3.0.0"
-      }
-    },
-    "node_modules/spdx-exceptions": {
-      "version": "2.5.0",
-      "resolved": "https://registry.npmmirror.com/spdx-exceptions/-/spdx-exceptions-2.5.0.tgz",
-      "integrity": "sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==",
-      "dev": true,
-      "license": "CC-BY-3.0"
-    },
-    "node_modules/spdx-expression-parse": {
-      "version": "3.0.1",
-      "resolved": "https://registry.npmmirror.com/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz",
-      "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "spdx-exceptions": "^2.1.0",
-        "spdx-license-ids": "^3.0.0"
-      }
-    },
-    "node_modules/spdx-license-ids": {
-      "version": "3.0.23",
-      "resolved": "https://registry.npmmirror.com/spdx-license-ids/-/spdx-license-ids-3.0.23.tgz",
-      "integrity": "sha512-CWLcCCH7VLu13TgOH+r8p1O/Znwhqv/dbb6lqWy67G+pT1kHmeD/+V36AVb/vq8QMIQwVShJ6Ssl5FPh0fuSdw==",
-      "dev": true,
-      "license": "CC0-1.0"
-    },
-    "node_modules/spdy": {
-      "version": "4.0.2",
-      "resolved": "https://registry.npmmirror.com/spdy/-/spdy-4.0.2.tgz",
-      "integrity": "sha512-r46gZQZQV+Kl9oItvl1JZZqJKGr+oEkB08A6BzkiR7593/7IbtuncXHd2YoYeTsG4157ZssMu9KYvUHLcjcDoA==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "debug": "^4.1.0",
-        "handle-thing": "^2.0.0",
-        "http-deceiver": "^1.2.7",
-        "select-hose": "^2.0.0",
-        "spdy-transport": "^3.0.0"
-      },
-      "engines": {
-        "node": ">=6.0.0"
-      }
-    },
-    "node_modules/spdy-transport": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmmirror.com/spdy-transport/-/spdy-transport-3.0.0.tgz",
-      "integrity": "sha512-hsLVFE5SjA6TCisWeJXFKniGGOpBgMLmerfO2aCyCU5s7nJ/rpAepqmFifv/GCbSbueEeAJJnmSQ2rKC/g8Fcw==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "debug": "^4.1.0",
-        "detect-node": "^2.0.4",
-        "hpack.js": "^2.1.6",
-        "obuf": "^1.1.2",
-        "readable-stream": "^3.0.6",
-        "wbuf": "^1.7.3"
-      }
-    },
-    "node_modules/sprintf-js": {
-      "version": "1.0.3",
-      "resolved": "https://registry.npmmirror.com/sprintf-js/-/sprintf-js-1.0.3.tgz",
-      "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==",
-      "dev": true,
-      "license": "BSD-3-Clause"
-    },
-    "node_modules/ssri": {
-      "version": "8.0.1",
-      "resolved": "https://registry.npmmirror.com/ssri/-/ssri-8.0.1.tgz",
-      "integrity": "sha512-97qShzy1AiyxvPNIkLWoGua7xoQzzPjQ0HAH4B0rWKo7SZ6USuPcrUiAFrws0UH8RrbWmgq3LMTObhPIHbbBeQ==",
-      "dev": true,
-      "license": "ISC",
-      "dependencies": {
-        "minipass": "^3.1.1"
-      },
-      "engines": {
-        "node": ">= 8"
-      }
-    },
-    "node_modules/stable": {
-      "version": "0.1.8",
-      "resolved": "https://registry.npmmirror.com/stable/-/stable-0.1.8.tgz",
-      "integrity": "sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==",
-      "deprecated": "Modern JS already guarantees Array#sort() is a stable sort, so this library is deprecated. See the compatibility table on MDN: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort#browser_compatibility",
-      "dev": true,
-      "license": "MIT"
-    },
-    "node_modules/stackframe": {
-      "version": "1.3.4",
-      "resolved": "https://registry.npmmirror.com/stackframe/-/stackframe-1.3.4.tgz",
-      "integrity": "sha512-oeVtt7eWQS+Na6F//S4kJ2K2VbRlS9D43mAlMyVpVWovy9o+jfgH8O9agzANzaiLjclA0oYzUXEM4PurhSUChw==",
-      "dev": true,
-      "license": "MIT"
-    },
-    "node_modules/statuses": {
-      "version": "2.0.2",
-      "resolved": "https://registry.npmmirror.com/statuses/-/statuses-2.0.2.tgz",
-      "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">= 0.8"
-      }
-    },
-    "node_modules/string_decoder": {
-      "version": "1.3.0",
-      "resolved": "https://registry.npmmirror.com/string_decoder/-/string_decoder-1.3.0.tgz",
-      "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "safe-buffer": "~5.2.0"
-      }
-    },
-    "node_modules/string-width": {
-      "version": "4.2.3",
-      "resolved": "https://registry.npmmirror.com/string-width/-/string-width-4.2.3.tgz",
-      "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "emoji-regex": "^8.0.0",
-        "is-fullwidth-code-point": "^3.0.0",
-        "strip-ansi": "^6.0.1"
-      },
-      "engines": {
-        "node": ">=8"
-      }
-    },
-    "node_modules/strip-ansi": {
-      "version": "6.0.1",
-      "resolved": "https://registry.npmmirror.com/strip-ansi/-/strip-ansi-6.0.1.tgz",
-      "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "ansi-regex": "^5.0.1"
-      },
-      "engines": {
-        "node": ">=8"
-      }
-    },
-    "node_modules/strip-eof": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmmirror.com/strip-eof/-/strip-eof-1.0.0.tgz",
-      "integrity": "sha512-7FCwGGmx8mD5xQd3RPUvnSpUXHM3BWuzjtpD4TXsfcZ9EL4azvVVUscFYwD9nx8Kh+uCBC00XBtAykoMHwTh8Q==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=0.10.0"
-      }
-    },
-    "node_modules/strip-final-newline": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmmirror.com/strip-final-newline/-/strip-final-newline-2.0.0.tgz",
-      "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=6"
-      }
-    },
-    "node_modules/strip-indent": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmmirror.com/strip-indent/-/strip-indent-2.0.0.tgz",
-      "integrity": "sha512-RsSNPLpq6YUL7QYy44RnPVTn/lcVZtb48Uof3X5JLbF4zD/Gs7ZFDv2HWol+leoQN2mT86LAzSshGfkTlSOpsA==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=4"
-      }
-    },
-    "node_modules/strip-json-comments": {
-      "version": "3.1.1",
-      "resolved": "https://registry.npmmirror.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
-      "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=8"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/sindresorhus"
-      }
-    },
-    "node_modules/stylehacks": {
-      "version": "5.1.1",
-      "resolved": "https://registry.npmmirror.com/stylehacks/-/stylehacks-5.1.1.tgz",
-      "integrity": "sha512-sBpcd5Hx7G6seo7b1LkpttvTz7ikD0LlH5RmdcBNb6fFR0Fl7LQwHDFr300q4cwUqi+IYrFGmsIHieMBfnN/Bw==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "browserslist": "^4.21.4",
-        "postcss-selector-parser": "^6.0.4"
-      },
-      "engines": {
-        "node": "^10 || ^12 || >=14.0"
-      },
-      "peerDependencies": {
-        "postcss": "^8.2.15"
-      }
-    },
-    "node_modules/supports-color": {
-      "version": "7.2.0",
-      "resolved": "https://registry.npmmirror.com/supports-color/-/supports-color-7.2.0.tgz",
-      "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "has-flag": "^4.0.0"
-      },
-      "engines": {
-        "node": ">=8"
-      }
-    },
-    "node_modules/supports-preserve-symlinks-flag": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmmirror.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",
-      "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">= 0.4"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
-    "node_modules/svg-tags": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmmirror.com/svg-tags/-/svg-tags-1.0.0.tgz",
-      "integrity": "sha512-ovssysQTa+luh7A5Weu3Rta6FJlFBBbInjOh722LIt6klpU2/HtdUbszju/G4devcvk8PGt7FCLv5wftu3THUA==",
-      "dev": true
-    },
-    "node_modules/svgo": {
-      "version": "2.8.2",
-      "resolved": "https://registry.npmmirror.com/svgo/-/svgo-2.8.2.tgz",
-      "integrity": "sha512-TyzE4NVGLUFy+H/Uy4N6c3G0HEeprsVfge6Lmq+0FdQQ/zqoVYB62IsBZORsiL+o96s6ff/V6/3UQo/C0cgCAA==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "commander": "^7.2.0",
-        "css-select": "^4.1.3",
-        "css-tree": "^1.1.3",
-        "csso": "^4.2.0",
-        "picocolors": "^1.0.0",
-        "sax": "^1.5.0",
-        "stable": "^0.1.8"
-      },
-      "bin": {
-        "svgo": "bin/svgo"
-      },
-      "engines": {
-        "node": ">=10.13.0"
-      }
-    },
-    "node_modules/svgo/node_modules/commander": {
-      "version": "7.2.0",
-      "resolved": "https://registry.npmmirror.com/commander/-/commander-7.2.0.tgz",
-      "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">= 10"
-      }
-    },
-    "node_modules/table": {
-      "version": "6.9.0",
-      "resolved": "https://registry.npmmirror.com/table/-/table-6.9.0.tgz",
-      "integrity": "sha512-9kY+CygyYM6j02t5YFHbNz2FN5QmYGv9zAjVp4lCDjlCw7amdckXlEt/bjMhUIfj4ThGRE4gCUH5+yGnNuPo5A==",
-      "dev": true,
-      "license": "BSD-3-Clause",
-      "dependencies": {
-        "ajv": "^8.0.1",
-        "lodash.truncate": "^4.4.2",
-        "slice-ansi": "^4.0.0",
-        "string-width": "^4.2.3",
-        "strip-ansi": "^6.0.1"
-      },
-      "engines": {
-        "node": ">=10.0.0"
-      }
-    },
-    "node_modules/table/node_modules/ajv": {
-      "version": "8.18.0",
-      "resolved": "https://registry.npmmirror.com/ajv/-/ajv-8.18.0.tgz",
-      "integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "fast-deep-equal": "^3.1.3",
-        "fast-uri": "^3.0.1",
-        "json-schema-traverse": "^1.0.0",
-        "require-from-string": "^2.0.2"
-      },
-      "funding": {
-        "type": "github",
-        "url": "https://github.com/sponsors/epoberezkin"
-      }
-    },
-    "node_modules/table/node_modules/json-schema-traverse": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmmirror.com/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
-      "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
-      "dev": true,
-      "license": "MIT"
-    },
-    "node_modules/tapable": {
-      "version": "2.3.2",
-      "resolved": "https://registry.npmmirror.com/tapable/-/tapable-2.3.2.tgz",
-      "integrity": "sha512-1MOpMXuhGzGL5TTCZFItxCc0AARf1EZFQkGqMm7ERKj8+Hgr5oLvJOVFcC+lRmR8hCe2S3jC4T5D7Vg/d7/fhA==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=6"
-      },
-      "funding": {
-        "type": "opencollective",
-        "url": "https://opencollective.com/webpack"
-      }
-    },
-    "node_modules/terser": {
-      "version": "5.46.1",
-      "resolved": "https://registry.npmmirror.com/terser/-/terser-5.46.1.tgz",
-      "integrity": "sha512-vzCjQO/rgUuK9sf8VJZvjqiqiHFaZLnOiimmUuOKODxWL8mm/xua7viT7aqX7dgPY60otQjUotzFMmCB4VdmqQ==",
-      "dev": true,
-      "license": "BSD-2-Clause",
-      "dependencies": {
-        "@jridgewell/source-map": "^0.3.3",
-        "acorn": "^8.15.0",
-        "commander": "^2.20.0",
-        "source-map-support": "~0.5.20"
-      },
-      "bin": {
-        "terser": "bin/terser"
-      },
-      "engines": {
-        "node": ">=10"
-      }
-    },
-    "node_modules/terser-webpack-plugin": {
-      "version": "5.4.0",
-      "resolved": "https://registry.npmmirror.com/terser-webpack-plugin/-/terser-webpack-plugin-5.4.0.tgz",
-      "integrity": "sha512-Bn5vxm48flOIfkdl5CaD2+1CiUVbonWQ3KQPyP7/EuIl9Gbzq/gQFOzaMFUEgVjB1396tcK0SG8XcNJ/2kDH8g==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@jridgewell/trace-mapping": "^0.3.25",
-        "jest-worker": "^27.4.5",
-        "schema-utils": "^4.3.0",
-        "terser": "^5.31.1"
-      },
-      "engines": {
-        "node": ">= 10.13.0"
-      },
-      "funding": {
-        "type": "opencollective",
-        "url": "https://opencollective.com/webpack"
-      },
-      "peerDependencies": {
-        "webpack": "^5.1.0"
-      },
-      "peerDependenciesMeta": {
-        "@swc/core": {
-          "optional": true
-        },
-        "esbuild": {
-          "optional": true
-        },
-        "uglify-js": {
-          "optional": true
-        }
-      }
-    },
-    "node_modules/terser-webpack-plugin/node_modules/ajv": {
-      "version": "8.18.0",
-      "resolved": "https://registry.npmmirror.com/ajv/-/ajv-8.18.0.tgz",
-      "integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "fast-deep-equal": "^3.1.3",
-        "fast-uri": "^3.0.1",
-        "json-schema-traverse": "^1.0.0",
-        "require-from-string": "^2.0.2"
-      },
-      "funding": {
-        "type": "github",
-        "url": "https://github.com/sponsors/epoberezkin"
-      }
-    },
-    "node_modules/terser-webpack-plugin/node_modules/ajv-keywords": {
-      "version": "5.1.0",
-      "resolved": "https://registry.npmmirror.com/ajv-keywords/-/ajv-keywords-5.1.0.tgz",
-      "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "fast-deep-equal": "^3.1.3"
-      },
-      "peerDependencies": {
-        "ajv": "^8.8.2"
-      }
-    },
-    "node_modules/terser-webpack-plugin/node_modules/json-schema-traverse": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmmirror.com/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
-      "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
-      "dev": true,
-      "license": "MIT"
-    },
-    "node_modules/terser-webpack-plugin/node_modules/schema-utils": {
-      "version": "4.3.3",
-      "resolved": "https://registry.npmmirror.com/schema-utils/-/schema-utils-4.3.3.tgz",
-      "integrity": "sha512-eflK8wEtyOE6+hsaRVPxvUKYCpRgzLqDTb8krvAsRIwOGlHoSgYLgBXoubGgLd2fT41/OUYdb48v4k4WWHQurA==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@types/json-schema": "^7.0.9",
-        "ajv": "^8.9.0",
-        "ajv-formats": "^2.1.1",
-        "ajv-keywords": "^5.1.0"
-      },
-      "engines": {
-        "node": ">= 10.13.0"
-      },
-      "funding": {
-        "type": "opencollective",
-        "url": "https://opencollective.com/webpack"
-      }
-    },
-    "node_modules/terser/node_modules/commander": {
-      "version": "2.20.3",
-      "resolved": "https://registry.npmmirror.com/commander/-/commander-2.20.3.tgz",
-      "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
-      "dev": true,
-      "license": "MIT"
-    },
-    "node_modules/text-table": {
-      "version": "0.2.0",
-      "resolved": "https://registry.npmmirror.com/text-table/-/text-table-0.2.0.tgz",
-      "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==",
-      "dev": true,
-      "license": "MIT"
-    },
-    "node_modules/thenify": {
-      "version": "3.3.1",
-      "resolved": "https://registry.npmmirror.com/thenify/-/thenify-3.3.1.tgz",
-      "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "any-promise": "^1.0.0"
-      }
-    },
-    "node_modules/thenify-all": {
-      "version": "1.6.0",
-      "resolved": "https://registry.npmmirror.com/thenify-all/-/thenify-all-1.6.0.tgz",
-      "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "thenify": ">= 3.1.0 < 4"
-      },
-      "engines": {
-        "node": ">=0.8"
-      }
-    },
-    "node_modules/thread-loader": {
-      "version": "3.0.4",
-      "resolved": "https://registry.npmmirror.com/thread-loader/-/thread-loader-3.0.4.tgz",
-      "integrity": "sha512-ByaL2TPb+m6yArpqQUZvP+5S1mZtXsEP7nWKKlAUTm7fCml8kB5s1uI3+eHRP2bk5mVYfRSBI7FFf+tWEyLZwA==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "json-parse-better-errors": "^1.0.2",
-        "loader-runner": "^4.1.0",
-        "loader-utils": "^2.0.0",
-        "neo-async": "^2.6.2",
-        "schema-utils": "^3.0.0"
-      },
-      "engines": {
-        "node": ">= 10.13.0"
-      },
-      "funding": {
-        "type": "opencollective",
-        "url": "https://opencollective.com/webpack"
-      },
-      "peerDependencies": {
-        "webpack": "^4.27.0 || ^5.0.0"
-      }
-    },
-    "node_modules/thread-loader/node_modules/loader-utils": {
-      "version": "2.0.4",
-      "resolved": "https://registry.npmmirror.com/loader-utils/-/loader-utils-2.0.4.tgz",
-      "integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "big.js": "^5.2.2",
-        "emojis-list": "^3.0.0",
-        "json5": "^2.1.2"
-      },
-      "engines": {
-        "node": ">=8.9.0"
-      }
-    },
-    "node_modules/thread-loader/node_modules/schema-utils": {
-      "version": "3.3.0",
-      "resolved": "https://registry.npmmirror.com/schema-utils/-/schema-utils-3.3.0.tgz",
-      "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@types/json-schema": "^7.0.8",
-        "ajv": "^6.12.5",
-        "ajv-keywords": "^3.5.2"
-      },
-      "engines": {
-        "node": ">= 10.13.0"
-      },
-      "funding": {
-        "type": "opencollective",
-        "url": "https://opencollective.com/webpack"
-      }
-    },
-    "node_modules/throttle-debounce": {
-      "version": "1.1.0",
-      "resolved": "https://registry.npmmirror.com/throttle-debounce/-/throttle-debounce-1.1.0.tgz",
-      "integrity": "sha512-XH8UiPCQcWNuk2LYePibW/4qL97+ZQ1AN3FNXwZRBNPPowo/NRU5fAlDCSNBJIYCKbioZfuYtMhG4quqoJhVzg==",
-      "license": "MIT",
-      "engines": {
-        "node": ">=4"
-      }
-    },
-    "node_modules/thunky": {
-      "version": "1.1.0",
-      "resolved": "https://registry.npmmirror.com/thunky/-/thunky-1.1.0.tgz",
-      "integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==",
-      "dev": true,
-      "license": "MIT"
-    },
-    "node_modules/to-regex-range": {
-      "version": "5.0.1",
-      "resolved": "https://registry.npmmirror.com/to-regex-range/-/to-regex-range-5.0.1.tgz",
-      "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "is-number": "^7.0.0"
-      },
-      "engines": {
-        "node": ">=8.0"
-      }
-    },
-    "node_modules/toidentifier": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmmirror.com/toidentifier/-/toidentifier-1.0.1.tgz",
-      "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=0.6"
-      }
-    },
-    "node_modules/totalist": {
-      "version": "3.0.1",
-      "resolved": "https://registry.npmmirror.com/totalist/-/totalist-3.0.1.tgz",
-      "integrity": "sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=6"
-      }
-    },
-    "node_modules/tr46": {
-      "version": "0.0.3",
-      "resolved": "https://registry.npmmirror.com/tr46/-/tr46-0.0.3.tgz",
-      "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==",
-      "dev": true,
-      "license": "MIT"
-    },
-    "node_modules/tslib": {
-      "version": "2.8.1",
-      "resolved": "https://registry.npmmirror.com/tslib/-/tslib-2.8.1.tgz",
-      "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
-      "dev": true,
-      "license": "0BSD"
-    },
-    "node_modules/type-check": {
-      "version": "0.4.0",
-      "resolved": "https://registry.npmmirror.com/type-check/-/type-check-0.4.0.tgz",
-      "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "prelude-ls": "^1.2.1"
-      },
-      "engines": {
-        "node": ">= 0.8.0"
-      }
-    },
-    "node_modules/type-fest": {
-      "version": "0.6.0",
-      "resolved": "https://registry.npmmirror.com/type-fest/-/type-fest-0.6.0.tgz",
-      "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==",
-      "dev": true,
-      "license": "(MIT OR CC0-1.0)",
-      "engines": {
-        "node": ">=8"
-      }
-    },
-    "node_modules/type-is": {
-      "version": "1.6.18",
-      "resolved": "https://registry.npmmirror.com/type-is/-/type-is-1.6.18.tgz",
-      "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "media-typer": "0.3.0",
-        "mime-types": "~2.1.24"
-      },
-      "engines": {
-        "node": ">= 0.6"
-      }
-    },
-    "node_modules/undici-types": {
-      "version": "7.19.2",
-      "resolved": "https://registry.npmmirror.com/undici-types/-/undici-types-7.19.2.tgz",
-      "integrity": "sha512-qYVnV5OEm2AW8cJMCpdV20CDyaN3g0AjDlOGf1OW4iaDEx8MwdtChUp4zu4H0VP3nDRF/8RKWH+IPp9uW0YGZg==",
-      "dev": true,
-      "license": "MIT"
-    },
-    "node_modules/unicode-canonical-property-names-ecmascript": {
-      "version": "2.0.1",
-      "resolved": "https://registry.npmmirror.com/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.1.tgz",
-      "integrity": "sha512-dA8WbNeb2a6oQzAQ55YlT5vQAWGV9WXOsi3SskE3bcCdM0P4SDd+24zS/OCacdRq5BkdsRj9q3Pg6YyQoxIGqg==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=4"
-      }
-    },
-    "node_modules/unicode-match-property-ecmascript": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmmirror.com/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz",
-      "integrity": "sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "unicode-canonical-property-names-ecmascript": "^2.0.0",
-        "unicode-property-aliases-ecmascript": "^2.0.0"
-      },
-      "engines": {
-        "node": ">=4"
-      }
-    },
-    "node_modules/unicode-match-property-value-ecmascript": {
-      "version": "2.2.1",
-      "resolved": "https://registry.npmmirror.com/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.2.1.tgz",
-      "integrity": "sha512-JQ84qTuMg4nVkx8ga4A16a1epI9H6uTXAknqxkGF/aFfRLw1xC/Bp24HNLaZhHSkWd3+84t8iXnp1J0kYcZHhg==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=4"
-      }
-    },
-    "node_modules/unicode-property-aliases-ecmascript": {
-      "version": "2.2.0",
-      "resolved": "https://registry.npmmirror.com/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.2.0.tgz",
-      "integrity": "sha512-hpbDzxUY9BFwX+UeBnxv3Sh1q7HFxj48DTmXchNgRa46lO8uj3/1iEn3MiNUYTg1g9ctIqXCCERn8gYZhHC5lQ==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=4"
-      }
-    },
-    "node_modules/universalify": {
-      "version": "2.0.1",
-      "resolved": "https://registry.npmmirror.com/universalify/-/universalify-2.0.1.tgz",
-      "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">= 10.0.0"
-      }
-    },
-    "node_modules/unpipe": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmmirror.com/unpipe/-/unpipe-1.0.0.tgz",
-      "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">= 0.8"
-      }
-    },
-    "node_modules/update-browserslist-db": {
-      "version": "1.2.3",
-      "resolved": "https://registry.npmmirror.com/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz",
-      "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==",
-      "dev": true,
-      "funding": [
-        {
-          "type": "opencollective",
-          "url": "https://opencollective.com/browserslist"
-        },
-        {
-          "type": "tidelift",
-          "url": "https://tidelift.com/funding/github/npm/browserslist"
-        },
-        {
-          "type": "github",
-          "url": "https://github.com/sponsors/ai"
-        }
-      ],
-      "license": "MIT",
-      "dependencies": {
-        "escalade": "^3.2.0",
-        "picocolors": "^1.1.1"
-      },
-      "bin": {
-        "update-browserslist-db": "cli.js"
-      },
-      "peerDependencies": {
-        "browserslist": ">= 4.21.0"
-      }
-    },
-    "node_modules/uri-js": {
-      "version": "4.4.1",
-      "resolved": "https://registry.npmmirror.com/uri-js/-/uri-js-4.4.1.tgz",
-      "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==",
-      "dev": true,
-      "license": "BSD-2-Clause",
-      "dependencies": {
-        "punycode": "^2.1.0"
-      }
-    },
-    "node_modules/util-deprecate": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmmirror.com/util-deprecate/-/util-deprecate-1.0.2.tgz",
-      "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
-      "dev": true,
-      "license": "MIT"
-    },
-    "node_modules/utila": {
-      "version": "0.4.0",
-      "resolved": "https://registry.npmmirror.com/utila/-/utila-0.4.0.tgz",
-      "integrity": "sha512-Z0DbgELS9/L/75wZbro8xAnT50pBVFQZ+hUEueGDU5FN51YSCYM+jdxsfCiHjwNP/4LCDD0i/graKpeBnOXKRA==",
-      "dev": true,
-      "license": "MIT"
-    },
-    "node_modules/utils-merge": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmmirror.com/utils-merge/-/utils-merge-1.0.1.tgz",
-      "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">= 0.4.0"
-      }
-    },
-    "node_modules/uuid": {
-      "version": "8.3.2",
-      "resolved": "https://registry.npmmirror.com/uuid/-/uuid-8.3.2.tgz",
-      "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==",
-      "dev": true,
-      "license": "MIT",
-      "bin": {
-        "uuid": "dist/bin/uuid"
-      }
-    },
-    "node_modules/v8-compile-cache": {
-      "version": "2.4.0",
-      "resolved": "https://registry.npmmirror.com/v8-compile-cache/-/v8-compile-cache-2.4.0.tgz",
-      "integrity": "sha512-ocyWc3bAHBB/guyqJQVI5o4BZkPhznPYUG2ea80Gond/BgNWpap8TOmLSeeQG7bnh2KMISxskdADG59j7zruhw==",
-      "dev": true,
-      "license": "MIT"
-    },
-    "node_modules/validate-npm-package-license": {
-      "version": "3.0.4",
-      "resolved": "https://registry.npmmirror.com/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz",
-      "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==",
-      "dev": true,
-      "license": "Apache-2.0",
-      "dependencies": {
-        "spdx-correct": "^3.0.0",
-        "spdx-expression-parse": "^3.0.0"
-      }
-    },
-    "node_modules/vary": {
-      "version": "1.1.2",
-      "resolved": "https://registry.npmmirror.com/vary/-/vary-1.1.2.tgz",
-      "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">= 0.8"
-      }
-    },
-    "node_modules/vue": {
-      "version": "2.7.16",
-      "resolved": "https://registry.npmmirror.com/vue/-/vue-2.7.16.tgz",
-      "integrity": "sha512-4gCtFXaAA3zYZdTp5s4Hl2sozuySsgz4jy1EnpBHNfpMa9dK1ZCG7viqBPCwXtmgc8nHqUsAu3G4gtmXkkY3Sw==",
-      "deprecated": "Vue 2 has reached EOL and is no longer actively maintained. See https://v2.vuejs.org/eol/ for more details.",
-      "license": "MIT",
-      "dependencies": {
-        "@vue/compiler-sfc": "2.7.16",
-        "csstype": "^3.1.0"
-      }
-    },
-    "node_modules/vue-eslint-parser": {
-      "version": "8.3.0",
-      "resolved": "https://registry.npmmirror.com/vue-eslint-parser/-/vue-eslint-parser-8.3.0.tgz",
-      "integrity": "sha512-dzHGG3+sYwSf6zFBa0Gi9ZDshD7+ad14DGOdTLjruRVgZXe2J+DcZ9iUhyR48z5g1PqRa20yt3Njna/veLJL/g==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "debug": "^4.3.2",
-        "eslint-scope": "^7.0.0",
-        "eslint-visitor-keys": "^3.1.0",
-        "espree": "^9.0.0",
-        "esquery": "^1.4.0",
-        "lodash": "^4.17.21",
-        "semver": "^7.3.5"
-      },
-      "engines": {
-        "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/mysticatea"
-      },
-      "peerDependencies": {
-        "eslint": ">=6.0.0"
-      }
-    },
-    "node_modules/vue-eslint-parser/node_modules/eslint-scope": {
-      "version": "7.2.2",
-      "resolved": "https://registry.npmmirror.com/eslint-scope/-/eslint-scope-7.2.2.tgz",
-      "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==",
-      "dev": true,
-      "license": "BSD-2-Clause",
-      "dependencies": {
-        "esrecurse": "^4.3.0",
-        "estraverse": "^5.2.0"
-      },
-      "engines": {
-        "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
-      },
-      "funding": {
-        "url": "https://opencollective.com/eslint"
-      }
-    },
-    "node_modules/vue-eslint-parser/node_modules/eslint-visitor-keys": {
-      "version": "3.4.3",
-      "resolved": "https://registry.npmmirror.com/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz",
-      "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==",
-      "dev": true,
-      "license": "Apache-2.0",
-      "engines": {
-        "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
-      },
-      "funding": {
-        "url": "https://opencollective.com/eslint"
-      }
-    },
-    "node_modules/vue-eslint-parser/node_modules/espree": {
-      "version": "9.6.1",
-      "resolved": "https://registry.npmmirror.com/espree/-/espree-9.6.1.tgz",
-      "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==",
-      "dev": true,
-      "license": "BSD-2-Clause",
-      "dependencies": {
-        "acorn": "^8.9.0",
-        "acorn-jsx": "^5.3.2",
-        "eslint-visitor-keys": "^3.4.1"
-      },
-      "engines": {
-        "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
-      },
-      "funding": {
-        "url": "https://opencollective.com/eslint"
-      }
-    },
-    "node_modules/vue-eslint-parser/node_modules/estraverse": {
-      "version": "5.3.0",
-      "resolved": "https://registry.npmmirror.com/estraverse/-/estraverse-5.3.0.tgz",
-      "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
-      "dev": true,
-      "license": "BSD-2-Clause",
-      "engines": {
-        "node": ">=4.0"
-      }
-    },
-    "node_modules/vue-eslint-parser/node_modules/semver": {
-      "version": "7.7.4",
-      "resolved": "https://registry.npmmirror.com/semver/-/semver-7.7.4.tgz",
-      "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==",
-      "dev": true,
-      "license": "ISC",
-      "bin": {
-        "semver": "bin/semver.js"
-      },
-      "engines": {
-        "node": ">=10"
-      }
-    },
-    "node_modules/vue-hot-reload-api": {
-      "version": "2.3.4",
-      "resolved": "https://registry.npmmirror.com/vue-hot-reload-api/-/vue-hot-reload-api-2.3.4.tgz",
-      "integrity": "sha512-BXq3jwIagosjgNVae6tkHzzIk6a8MHFtzAdwhnV5VlvPTFxDCvIttgSiHWjdGoTJvXtmRu5HacExfdarRcFhog==",
-      "dev": true,
-      "license": "MIT"
-    },
-    "node_modules/vue-loader": {
-      "version": "17.4.2",
-      "resolved": "https://registry.npmmirror.com/vue-loader/-/vue-loader-17.4.2.tgz",
-      "integrity": "sha512-yTKOA4R/VN4jqjw4y5HrynFL8AK0Z3/Jt7eOJXEitsm0GMRHDBjCfCiuTiLP7OESvsZYo2pATCWhDqxC5ZrM6w==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "chalk": "^4.1.0",
-        "hash-sum": "^2.0.0",
-        "watchpack": "^2.4.0"
-      },
-      "peerDependencies": {
-        "webpack": "^4.1.0 || ^5.0.0-0"
-      },
-      "peerDependenciesMeta": {
-        "@vue/compiler-sfc": {
-          "optional": true
-        },
-        "vue": {
-          "optional": true
-        }
-      }
-    },
-    "node_modules/vue-loader/node_modules/chalk": {
-      "version": "4.1.2",
-      "resolved": "https://registry.npmmirror.com/chalk/-/chalk-4.1.2.tgz",
-      "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "ansi-styles": "^4.1.0",
-        "supports-color": "^7.1.0"
-      },
-      "engines": {
-        "node": ">=10"
-      },
-      "funding": {
-        "url": "https://github.com/chalk/chalk?sponsor=1"
-      }
-    },
-    "node_modules/vue-quill-editor": {
-      "version": "3.0.6",
-      "resolved": "https://registry.npmmirror.com/vue-quill-editor/-/vue-quill-editor-3.0.6.tgz",
-      "integrity": "sha512-g20oSZNWg8Hbu41Kinjd55e235qVWPLfg4NvsLW6d+DhgBTFbEuMpcWlUdrD6qT3+Noim6DRu18VLM9lVShXOQ==",
-      "license": "MIT",
-      "dependencies": {
-        "object-assign": "^4.1.1",
-        "quill": "^1.3.4"
-      },
-      "engines": {
-        "node": ">= 4.0.0",
-        "npm": ">= 3.0.0"
-      }
-    },
-    "node_modules/vue-router": {
-      "version": "3.6.5",
-      "resolved": "https://registry.npmmirror.com/vue-router/-/vue-router-3.6.5.tgz",
-      "integrity": "sha512-VYXZQLtjuvKxxcshuRAwjHnciqZVoXAjTjcqBTz4rKc8qih9g9pI3hbDjmqXaHdgL3v8pV6P8Z335XvHzESxLQ==",
-      "license": "MIT"
-    },
-    "node_modules/vue-style-loader": {
-      "version": "4.1.3",
-      "resolved": "https://registry.npmmirror.com/vue-style-loader/-/vue-style-loader-4.1.3.tgz",
-      "integrity": "sha512-sFuh0xfbtpRlKfm39ss/ikqs9AbKCoXZBpHeVZ8Tx650o0k0q/YCM7FRvigtxpACezfq6af+a7JeqVTWvncqDg==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "hash-sum": "^1.0.2",
-        "loader-utils": "^1.0.2"
-      }
-    },
-    "node_modules/vue-style-loader/node_modules/hash-sum": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmmirror.com/hash-sum/-/hash-sum-1.0.2.tgz",
-      "integrity": "sha512-fUs4B4L+mlt8/XAtSOGMUO1TXmAelItBPtJG7CyHJfYTdDjwisntGO2JQz7oUsatOY9o68+57eziUVNw/mRHmA==",
-      "dev": true,
-      "license": "MIT"
-    },
-    "node_modules/vue-template-compiler": {
-      "version": "2.7.16",
-      "resolved": "https://registry.npmmirror.com/vue-template-compiler/-/vue-template-compiler-2.7.16.tgz",
-      "integrity": "sha512-AYbUWAJHLGGQM7+cNTELw+KsOG9nl2CnSv467WobS5Cv9uk3wFcnr1Etsz2sEIHEZvw1U+o9mRlEO6QbZvUPGQ==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "de-indent": "^1.0.2",
-        "he": "^1.2.0"
-      }
-    },
-    "node_modules/vue-template-es2015-compiler": {
-      "version": "1.9.1",
-      "resolved": "https://registry.npmmirror.com/vue-template-es2015-compiler/-/vue-template-es2015-compiler-1.9.1.tgz",
-      "integrity": "sha512-4gDntzrifFnCEvyoO8PqyJDmguXgVPxKiIxrBKjIowvL9l+N66196+72XVYR8BBf1Uv1Fgt3bGevJ+sEmxfZzw==",
-      "dev": true,
-      "license": "MIT"
-    },
-    "node_modules/vue/node_modules/@vue/compiler-sfc": {
-      "version": "2.7.16",
-      "resolved": "https://registry.npmmirror.com/@vue/compiler-sfc/-/compiler-sfc-2.7.16.tgz",
-      "integrity": "sha512-KWhJ9k5nXuNtygPU7+t1rX6baZeqOYLEforUPjgNDBnLicfHCoi48H87Q8XyLZOrNNsmhuwKqtpDQWjEFe6Ekg==",
-      "dependencies": {
-        "@babel/parser": "^7.23.5",
-        "postcss": "^8.4.14",
-        "source-map": "^0.6.1"
-      },
-      "optionalDependencies": {
-        "prettier": "^1.18.2 || ^2.0.0"
-      }
-    },
-    "node_modules/watchpack": {
-      "version": "2.5.1",
-      "resolved": "https://registry.npmmirror.com/watchpack/-/watchpack-2.5.1.tgz",
-      "integrity": "sha512-Zn5uXdcFNIA1+1Ei5McRd+iRzfhENPCe7LeABkJtNulSxjma+l7ltNx55BWZkRlwRnpOgHqxnjyaDgJnNXnqzg==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "glob-to-regexp": "^0.4.1",
-        "graceful-fs": "^4.1.2"
-      },
-      "engines": {
-        "node": ">=10.13.0"
-      }
-    },
-    "node_modules/wbuf": {
-      "version": "1.7.3",
-      "resolved": "https://registry.npmmirror.com/wbuf/-/wbuf-1.7.3.tgz",
-      "integrity": "sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "minimalistic-assert": "^1.0.0"
-      }
-    },
-    "node_modules/wcwidth": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmmirror.com/wcwidth/-/wcwidth-1.0.1.tgz",
-      "integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "defaults": "^1.0.3"
-      }
-    },
-    "node_modules/webidl-conversions": {
-      "version": "3.0.1",
-      "resolved": "https://registry.npmmirror.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
-      "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==",
-      "dev": true,
-      "license": "BSD-2-Clause"
-    },
-    "node_modules/webpack": {
-      "version": "5.106.2",
-      "resolved": "https://registry.npmmirror.com/webpack/-/webpack-5.106.2.tgz",
-      "integrity": "sha512-wGN3qcrBQIFmQ/c0AiOAQBvrZ5lmY8vbbMv4Mxfgzqd/B6+9pXtLo73WuS1dSGXM5QYY3hZnIbvx+K1xxe6FyA==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@types/eslint-scope": "^3.7.7",
-        "@types/estree": "^1.0.8",
-        "@types/json-schema": "^7.0.15",
-        "@webassemblyjs/ast": "^1.14.1",
-        "@webassemblyjs/wasm-edit": "^1.14.1",
-        "@webassemblyjs/wasm-parser": "^1.14.1",
-        "acorn": "^8.16.0",
-        "acorn-import-phases": "^1.0.3",
-        "browserslist": "^4.28.1",
-        "chrome-trace-event": "^1.0.2",
-        "enhanced-resolve": "^5.20.0",
-        "es-module-lexer": "^2.0.0",
-        "eslint-scope": "5.1.1",
-        "events": "^3.2.0",
-        "glob-to-regexp": "^0.4.1",
-        "graceful-fs": "^4.2.11",
-        "loader-runner": "^4.3.1",
-        "mime-db": "^1.54.0",
-        "neo-async": "^2.6.2",
-        "schema-utils": "^4.3.3",
-        "tapable": "^2.3.0",
-        "terser-webpack-plugin": "^5.3.17",
-        "watchpack": "^2.5.1",
-        "webpack-sources": "^3.3.4"
-      },
-      "bin": {
-        "webpack": "bin/webpack.js"
-      },
-      "engines": {
-        "node": ">=10.13.0"
-      },
-      "funding": {
-        "type": "opencollective",
-        "url": "https://opencollective.com/webpack"
-      },
-      "peerDependenciesMeta": {
-        "webpack-cli": {
-          "optional": true
-        }
-      }
-    },
-    "node_modules/webpack-bundle-analyzer": {
-      "version": "4.10.2",
-      "resolved": "https://registry.npmmirror.com/webpack-bundle-analyzer/-/webpack-bundle-analyzer-4.10.2.tgz",
-      "integrity": "sha512-vJptkMm9pk5si4Bv922ZbKLV8UTT4zib4FPgXMhgzUny0bfDDkLXAVQs3ly3fS4/TN9ROFtb0NFrm04UXFE/Vw==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@discoveryjs/json-ext": "0.5.7",
-        "acorn": "^8.0.4",
-        "acorn-walk": "^8.0.0",
-        "commander": "^7.2.0",
-        "debounce": "^1.2.1",
-        "escape-string-regexp": "^4.0.0",
-        "gzip-size": "^6.0.0",
-        "html-escaper": "^2.0.2",
-        "opener": "^1.5.2",
-        "picocolors": "^1.0.0",
-        "sirv": "^2.0.3",
-        "ws": "^7.3.1"
-      },
-      "bin": {
-        "webpack-bundle-analyzer": "lib/bin/analyzer.js"
-      },
-      "engines": {
-        "node": ">= 10.13.0"
-      }
-    },
-    "node_modules/webpack-bundle-analyzer/node_modules/commander": {
-      "version": "7.2.0",
-      "resolved": "https://registry.npmmirror.com/commander/-/commander-7.2.0.tgz",
-      "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">= 10"
-      }
-    },
-    "node_modules/webpack-bundle-analyzer/node_modules/escape-string-regexp": {
-      "version": "4.0.0",
-      "resolved": "https://registry.npmmirror.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
-      "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=10"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/sindresorhus"
-      }
-    },
-    "node_modules/webpack-chain": {
-      "version": "6.5.1",
-      "resolved": "https://registry.npmmirror.com/webpack-chain/-/webpack-chain-6.5.1.tgz",
-      "integrity": "sha512-7doO/SRtLu8q5WM0s7vPKPWX580qhi0/yBHkOxNkv50f6qB76Zy9o2wRTrrPULqYTvQlVHuvbA8v+G5ayuUDsA==",
-      "deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.",
-      "dev": true,
-      "license": "MPL-2.0",
-      "dependencies": {
-        "deepmerge": "^1.5.2",
-        "javascript-stringify": "^2.0.1"
-      },
-      "engines": {
-        "node": ">=8"
-      }
-    },
-    "node_modules/webpack-dev-middleware": {
-      "version": "5.3.4",
-      "resolved": "https://registry.npmmirror.com/webpack-dev-middleware/-/webpack-dev-middleware-5.3.4.tgz",
-      "integrity": "sha512-BVdTqhhs+0IfoeAf7EoH5WE+exCmqGerHfDM0IL096Px60Tq2Mn9MAbnaGUe6HiMa41KMCYF19gyzZmBcq/o4Q==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "colorette": "^2.0.10",
-        "memfs": "^3.4.3",
-        "mime-types": "^2.1.31",
-        "range-parser": "^1.2.1",
-        "schema-utils": "^4.0.0"
-      },
-      "engines": {
-        "node": ">= 12.13.0"
-      },
-      "funding": {
-        "type": "opencollective",
-        "url": "https://opencollective.com/webpack"
-      },
-      "peerDependencies": {
-        "webpack": "^4.0.0 || ^5.0.0"
-      }
-    },
-    "node_modules/webpack-dev-middleware/node_modules/ajv": {
-      "version": "8.18.0",
-      "resolved": "https://registry.npmmirror.com/ajv/-/ajv-8.18.0.tgz",
-      "integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "fast-deep-equal": "^3.1.3",
-        "fast-uri": "^3.0.1",
-        "json-schema-traverse": "^1.0.0",
-        "require-from-string": "^2.0.2"
-      },
-      "funding": {
-        "type": "github",
-        "url": "https://github.com/sponsors/epoberezkin"
-      }
-    },
-    "node_modules/webpack-dev-middleware/node_modules/ajv-keywords": {
-      "version": "5.1.0",
-      "resolved": "https://registry.npmmirror.com/ajv-keywords/-/ajv-keywords-5.1.0.tgz",
-      "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "fast-deep-equal": "^3.1.3"
-      },
-      "peerDependencies": {
-        "ajv": "^8.8.2"
-      }
-    },
-    "node_modules/webpack-dev-middleware/node_modules/json-schema-traverse": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmmirror.com/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
-      "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
-      "dev": true,
-      "license": "MIT"
-    },
-    "node_modules/webpack-dev-middleware/node_modules/schema-utils": {
-      "version": "4.3.3",
-      "resolved": "https://registry.npmmirror.com/schema-utils/-/schema-utils-4.3.3.tgz",
-      "integrity": "sha512-eflK8wEtyOE6+hsaRVPxvUKYCpRgzLqDTb8krvAsRIwOGlHoSgYLgBXoubGgLd2fT41/OUYdb48v4k4WWHQurA==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@types/json-schema": "^7.0.9",
-        "ajv": "^8.9.0",
-        "ajv-formats": "^2.1.1",
-        "ajv-keywords": "^5.1.0"
-      },
-      "engines": {
-        "node": ">= 10.13.0"
-      },
-      "funding": {
-        "type": "opencollective",
-        "url": "https://opencollective.com/webpack"
-      }
-    },
-    "node_modules/webpack-dev-server": {
-      "version": "4.15.2",
-      "resolved": "https://registry.npmmirror.com/webpack-dev-server/-/webpack-dev-server-4.15.2.tgz",
-      "integrity": "sha512-0XavAZbNJ5sDrCbkpWL8mia0o5WPOd2YGtxrEiZkBK9FjLppIUK2TgxK6qGD2P3hUXTJNNPVibrerKcx5WkR1g==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@types/bonjour": "^3.5.9",
-        "@types/connect-history-api-fallback": "^1.3.5",
-        "@types/express": "^4.17.13",
-        "@types/serve-index": "^1.9.1",
-        "@types/serve-static": "^1.13.10",
-        "@types/sockjs": "^0.3.33",
-        "@types/ws": "^8.5.5",
-        "ansi-html-community": "^0.0.8",
-        "bonjour-service": "^1.0.11",
-        "chokidar": "^3.5.3",
-        "colorette": "^2.0.10",
-        "compression": "^1.7.4",
-        "connect-history-api-fallback": "^2.0.0",
-        "default-gateway": "^6.0.3",
-        "express": "^4.17.3",
-        "graceful-fs": "^4.2.6",
-        "html-entities": "^2.3.2",
-        "http-proxy-middleware": "^2.0.3",
-        "ipaddr.js": "^2.0.1",
-        "launch-editor": "^2.6.0",
-        "open": "^8.0.9",
-        "p-retry": "^4.5.0",
-        "rimraf": "^3.0.2",
-        "schema-utils": "^4.0.0",
-        "selfsigned": "^2.1.1",
-        "serve-index": "^1.9.1",
-        "sockjs": "^0.3.24",
-        "spdy": "^4.0.2",
-        "webpack-dev-middleware": "^5.3.4",
-        "ws": "^8.13.0"
-      },
-      "bin": {
-        "webpack-dev-server": "bin/webpack-dev-server.js"
-      },
-      "engines": {
-        "node": ">= 12.13.0"
-      },
-      "funding": {
-        "type": "opencollective",
-        "url": "https://opencollective.com/webpack"
-      },
-      "peerDependencies": {
-        "webpack": "^4.37.0 || ^5.0.0"
-      },
-      "peerDependenciesMeta": {
-        "webpack": {
-          "optional": true
-        },
-        "webpack-cli": {
-          "optional": true
-        }
-      }
-    },
-    "node_modules/webpack-dev-server/node_modules/ajv": {
-      "version": "8.18.0",
-      "resolved": "https://registry.npmmirror.com/ajv/-/ajv-8.18.0.tgz",
-      "integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "fast-deep-equal": "^3.1.3",
-        "fast-uri": "^3.0.1",
-        "json-schema-traverse": "^1.0.0",
-        "require-from-string": "^2.0.2"
-      },
-      "funding": {
-        "type": "github",
-        "url": "https://github.com/sponsors/epoberezkin"
-      }
-    },
-    "node_modules/webpack-dev-server/node_modules/ajv-keywords": {
-      "version": "5.1.0",
-      "resolved": "https://registry.npmmirror.com/ajv-keywords/-/ajv-keywords-5.1.0.tgz",
-      "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "fast-deep-equal": "^3.1.3"
-      },
-      "peerDependencies": {
-        "ajv": "^8.8.2"
-      }
-    },
-    "node_modules/webpack-dev-server/node_modules/json-schema-traverse": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmmirror.com/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
-      "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
-      "dev": true,
-      "license": "MIT"
-    },
-    "node_modules/webpack-dev-server/node_modules/schema-utils": {
-      "version": "4.3.3",
-      "resolved": "https://registry.npmmirror.com/schema-utils/-/schema-utils-4.3.3.tgz",
-      "integrity": "sha512-eflK8wEtyOE6+hsaRVPxvUKYCpRgzLqDTb8krvAsRIwOGlHoSgYLgBXoubGgLd2fT41/OUYdb48v4k4WWHQurA==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@types/json-schema": "^7.0.9",
-        "ajv": "^8.9.0",
-        "ajv-formats": "^2.1.1",
-        "ajv-keywords": "^5.1.0"
-      },
-      "engines": {
-        "node": ">= 10.13.0"
-      },
-      "funding": {
-        "type": "opencollective",
-        "url": "https://opencollective.com/webpack"
-      }
-    },
-    "node_modules/webpack-dev-server/node_modules/ws": {
-      "version": "8.20.0",
-      "resolved": "https://registry.npmmirror.com/ws/-/ws-8.20.0.tgz",
-      "integrity": "sha512-sAt8BhgNbzCtgGbt2OxmpuryO63ZoDk/sqaB/znQm94T4fCEsy/yV+7CdC1kJhOU9lboAEU7R3kquuycDoibVA==",
-      "dev": true,
-      "license": "MIT",
-      "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
-        }
-      }
-    },
-    "node_modules/webpack-merge": {
-      "version": "5.10.0",
-      "resolved": "https://registry.npmmirror.com/webpack-merge/-/webpack-merge-5.10.0.tgz",
-      "integrity": "sha512-+4zXKdx7UnO+1jaN4l2lHVD+mFvnlZQP/6ljaJVb4SZiwIKeUnrT5l0gkT8z+n4hKpC+jpOv6O9R+gLtag7pSA==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "clone-deep": "^4.0.1",
-        "flat": "^5.0.2",
-        "wildcard": "^2.0.0"
-      },
-      "engines": {
-        "node": ">=10.0.0"
-      }
-    },
-    "node_modules/webpack-sources": {
-      "version": "3.3.4",
-      "resolved": "https://registry.npmmirror.com/webpack-sources/-/webpack-sources-3.3.4.tgz",
-      "integrity": "sha512-7tP1PdV4vF+lYPnkMR0jMY5/la2ub5Fc/8VQrrU+lXkiM6C4TjVfGw7iKfyhnTQOsD+6Q/iKw0eFciziRgD58Q==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=10.13.0"
-      }
-    },
-    "node_modules/webpack-virtual-modules": {
-      "version": "0.4.6",
-      "resolved": "https://registry.npmmirror.com/webpack-virtual-modules/-/webpack-virtual-modules-0.4.6.tgz",
-      "integrity": "sha512-5tyDlKLqPfMqjT3Q9TAqf2YqjwmnUleZwzJi1A5qXnlBCdj2AtOJ6wAWdglTIDOPgOiOrXeBeFcsQ8+aGQ6QbA==",
-      "dev": true,
-      "license": "MIT"
-    },
-    "node_modules/webpack/node_modules/ajv": {
-      "version": "8.18.0",
-      "resolved": "https://registry.npmmirror.com/ajv/-/ajv-8.18.0.tgz",
-      "integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "fast-deep-equal": "^3.1.3",
-        "fast-uri": "^3.0.1",
-        "json-schema-traverse": "^1.0.0",
-        "require-from-string": "^2.0.2"
-      },
-      "funding": {
-        "type": "github",
-        "url": "https://github.com/sponsors/epoberezkin"
-      }
-    },
-    "node_modules/webpack/node_modules/ajv-keywords": {
-      "version": "5.1.0",
-      "resolved": "https://registry.npmmirror.com/ajv-keywords/-/ajv-keywords-5.1.0.tgz",
-      "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "fast-deep-equal": "^3.1.3"
-      },
-      "peerDependencies": {
-        "ajv": "^8.8.2"
-      }
-    },
-    "node_modules/webpack/node_modules/json-schema-traverse": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmmirror.com/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
-      "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
-      "dev": true,
-      "license": "MIT"
-    },
-    "node_modules/webpack/node_modules/schema-utils": {
-      "version": "4.3.3",
-      "resolved": "https://registry.npmmirror.com/schema-utils/-/schema-utils-4.3.3.tgz",
-      "integrity": "sha512-eflK8wEtyOE6+hsaRVPxvUKYCpRgzLqDTb8krvAsRIwOGlHoSgYLgBXoubGgLd2fT41/OUYdb48v4k4WWHQurA==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@types/json-schema": "^7.0.9",
-        "ajv": "^8.9.0",
-        "ajv-formats": "^2.1.1",
-        "ajv-keywords": "^5.1.0"
-      },
-      "engines": {
-        "node": ">= 10.13.0"
-      },
-      "funding": {
-        "type": "opencollective",
-        "url": "https://opencollective.com/webpack"
-      }
-    },
-    "node_modules/websocket-driver": {
-      "version": "0.7.4",
-      "resolved": "https://registry.npmmirror.com/websocket-driver/-/websocket-driver-0.7.4.tgz",
-      "integrity": "sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==",
-      "dev": true,
-      "license": "Apache-2.0",
-      "dependencies": {
-        "http-parser-js": ">=0.5.1",
-        "safe-buffer": ">=5.1.0",
-        "websocket-extensions": ">=0.1.1"
-      },
-      "engines": {
-        "node": ">=0.8.0"
-      }
-    },
-    "node_modules/websocket-extensions": {
-      "version": "0.1.4",
-      "resolved": "https://registry.npmmirror.com/websocket-extensions/-/websocket-extensions-0.1.4.tgz",
-      "integrity": "sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==",
-      "dev": true,
-      "license": "Apache-2.0",
-      "engines": {
-        "node": ">=0.8.0"
-      }
-    },
-    "node_modules/whatwg-fetch": {
-      "version": "3.6.20",
-      "resolved": "https://registry.npmmirror.com/whatwg-fetch/-/whatwg-fetch-3.6.20.tgz",
-      "integrity": "sha512-EqhiFU6daOA8kpjOWTL0olhVOF3i7OrFzSYiGsEMB8GcXS+RrzauAERX65xMeNWVqxA6HXH2m69Z9LaKKdisfg==",
-      "dev": true,
-      "license": "MIT"
-    },
-    "node_modules/whatwg-url": {
-      "version": "5.0.0",
-      "resolved": "https://registry.npmmirror.com/whatwg-url/-/whatwg-url-5.0.0.tgz",
-      "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "tr46": "~0.0.3",
-        "webidl-conversions": "^3.0.0"
-      }
-    },
-    "node_modules/which": {
-      "version": "1.3.1",
-      "resolved": "https://registry.npmmirror.com/which/-/which-1.3.1.tgz",
-      "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==",
-      "dev": true,
-      "license": "ISC",
-      "dependencies": {
-        "isexe": "^2.0.0"
-      },
-      "bin": {
-        "which": "bin/which"
-      }
-    },
-    "node_modules/wildcard": {
-      "version": "2.0.1",
-      "resolved": "https://registry.npmmirror.com/wildcard/-/wildcard-2.0.1.tgz",
-      "integrity": "sha512-CC1bOL87PIWSBhDcTrdeLo6eGT7mCFtrg0uIJtqJUFyK+eJnzl8A1niH56uu7KMa5XFrtiV+AQuHO3n7DsHnLQ==",
-      "dev": true,
-      "license": "MIT"
-    },
-    "node_modules/word-wrap": {
-      "version": "1.2.5",
-      "resolved": "https://registry.npmmirror.com/word-wrap/-/word-wrap-1.2.5.tgz",
-      "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=0.10.0"
-      }
-    },
-    "node_modules/wrap-ansi": {
-      "version": "7.0.0",
-      "resolved": "https://registry.npmmirror.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
-      "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "ansi-styles": "^4.0.0",
-        "string-width": "^4.1.0",
-        "strip-ansi": "^6.0.0"
-      },
-      "engines": {
-        "node": ">=10"
-      },
-      "funding": {
-        "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
-      }
-    },
-    "node_modules/wrappy": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmmirror.com/wrappy/-/wrappy-1.0.2.tgz",
-      "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
-      "dev": true,
-      "license": "ISC"
-    },
-    "node_modules/ws": {
-      "version": "7.5.10",
-      "resolved": "https://registry.npmmirror.com/ws/-/ws-7.5.10.tgz",
-      "integrity": "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=8.3.0"
-      },
-      "peerDependencies": {
-        "bufferutil": "^4.0.1",
-        "utf-8-validate": "^5.0.2"
-      },
-      "peerDependenciesMeta": {
-        "bufferutil": {
-          "optional": true
-        },
-        "utf-8-validate": {
-          "optional": true
-        }
-      }
-    },
-    "node_modules/y18n": {
-      "version": "5.0.8",
-      "resolved": "https://registry.npmmirror.com/y18n/-/y18n-5.0.8.tgz",
-      "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==",
-      "dev": true,
-      "license": "ISC",
-      "engines": {
-        "node": ">=10"
-      }
-    },
-    "node_modules/yallist": {
-      "version": "3.1.1",
-      "resolved": "https://registry.npmmirror.com/yallist/-/yallist-3.1.1.tgz",
-      "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==",
-      "dev": true,
-      "license": "ISC"
-    },
-    "node_modules/yaml": {
-      "version": "1.10.3",
-      "resolved": "https://registry.npmmirror.com/yaml/-/yaml-1.10.3.tgz",
-      "integrity": "sha512-vIYeF1u3CjlhAFekPPAk2h/Kv4T3mAkMox5OymRiJQB0spDP10LHvt+K7G9Ny6NuuMAb25/6n1qyUjAcGNf/AA==",
-      "dev": true,
-      "license": "ISC",
-      "engines": {
-        "node": ">= 6"
-      }
-    },
-    "node_modules/yargs": {
-      "version": "16.2.0",
-      "resolved": "https://registry.npmmirror.com/yargs/-/yargs-16.2.0.tgz",
-      "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "cliui": "^7.0.2",
-        "escalade": "^3.1.1",
-        "get-caller-file": "^2.0.5",
-        "require-directory": "^2.1.1",
-        "string-width": "^4.2.0",
-        "y18n": "^5.0.5",
-        "yargs-parser": "^20.2.2"
-      },
-      "engines": {
-        "node": ">=10"
-      }
-    },
-    "node_modules/yargs-parser": {
-      "version": "20.2.9",
-      "resolved": "https://registry.npmmirror.com/yargs-parser/-/yargs-parser-20.2.9.tgz",
-      "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==",
-      "dev": true,
-      "license": "ISC",
-      "engines": {
-        "node": ">=10"
-      }
-    },
-    "node_modules/yorkie": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmmirror.com/yorkie/-/yorkie-2.0.0.tgz",
-      "integrity": "sha512-jcKpkthap6x63MB4TxwCyuIGkV0oYP/YRyuQU5UO0Yz/E/ZAu+653/uov+phdmO54n6BcvFRyyt0RRrWdN2mpw==",
-      "dev": true,
-      "hasInstallScript": true,
-      "license": "MIT",
-      "dependencies": {
-        "execa": "^0.8.0",
-        "is-ci": "^1.0.10",
-        "normalize-path": "^1.0.0",
-        "strip-indent": "^2.0.0"
-      },
-      "engines": {
-        "node": ">=4"
-      }
-    },
-    "node_modules/yorkie/node_modules/cross-spawn": {
-      "version": "5.1.0",
-      "resolved": "https://registry.npmmirror.com/cross-spawn/-/cross-spawn-5.1.0.tgz",
-      "integrity": "sha512-pTgQJ5KC0d2hcY8eyL1IzlBPYjTkyH72XRZPnLyKus2mBfNjQs3klqbJU2VILqZryAZUt9JOb3h/mWMy23/f5A==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "lru-cache": "^4.0.1",
-        "shebang-command": "^1.2.0",
-        "which": "^1.2.9"
-      }
-    },
-    "node_modules/yorkie/node_modules/execa": {
-      "version": "0.8.0",
-      "resolved": "https://registry.npmmirror.com/execa/-/execa-0.8.0.tgz",
-      "integrity": "sha512-zDWS+Rb1E8BlqqhALSt9kUhss8Qq4nN3iof3gsOdyINksElaPyNBtKUMTR62qhvgVWR0CqCX7sdnKe4MnUbFEA==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "cross-spawn": "^5.0.1",
-        "get-stream": "^3.0.0",
-        "is-stream": "^1.1.0",
-        "npm-run-path": "^2.0.0",
-        "p-finally": "^1.0.0",
-        "signal-exit": "^3.0.0",
-        "strip-eof": "^1.0.0"
-      },
-      "engines": {
-        "node": ">=4"
-      }
-    },
-    "node_modules/yorkie/node_modules/get-stream": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmmirror.com/get-stream/-/get-stream-3.0.0.tgz",
-      "integrity": "sha512-GlhdIUuVakc8SJ6kK0zAFbiGzRFzNnY4jUuEbV9UROo4Y+0Ny4fjvcZFVTeDA4odpFyOQzaw6hXukJSq/f28sQ==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=4"
-      }
-    },
-    "node_modules/yorkie/node_modules/lru-cache": {
-      "version": "4.1.5",
-      "resolved": "https://registry.npmmirror.com/lru-cache/-/lru-cache-4.1.5.tgz",
-      "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==",
-      "dev": true,
-      "license": "ISC",
-      "dependencies": {
-        "pseudomap": "^1.0.2",
-        "yallist": "^2.1.2"
-      }
-    },
-    "node_modules/yorkie/node_modules/normalize-path": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmmirror.com/normalize-path/-/normalize-path-1.0.0.tgz",
-      "integrity": "sha512-7WyT0w8jhpDStXRq5836AMmihQwq2nrUVQrgjvUo/p/NZf9uy/MeJ246lBJVmWuYXMlJuG9BNZHF0hWjfTbQUA==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=0.10.0"
-      }
-    },
-    "node_modules/yorkie/node_modules/yallist": {
-      "version": "2.1.2",
-      "resolved": "https://registry.npmmirror.com/yallist/-/yallist-2.1.2.tgz",
-      "integrity": "sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A==",
-      "dev": true,
-      "license": "ISC"
-    }
-  }
-}

+ 0 - 51
pc/package.json

@@ -1,51 +0,0 @@
-{
-  "name": "liso",
-  "version": "0.1.0",
-  "private": true,
-  "scripts": {
-    "serve": "vue-cli-service serve",
-    "build": "vue-cli-service build",
-    "lint": "vue-cli-service lint"
-  },
-  "dependencies": {
-    "axios": "^1.15.0",
-    "core-js": "^3.8.3",
-    "element-ui": "^2.15.14",
-    "mockjs": "^1.1.0",
-    "vue": "^2.6.14",
-    "vue-quill-editor": "^3.0.6",
-    "vue-router": "^3.6.5"
-  },
-  "devDependencies": {
-    "@babel/core": "^7.12.16",
-    "@babel/eslint-parser": "^7.12.16",
-    "@vue/cli-plugin-babel": "~5.0.0",
-    "@vue/cli-plugin-eslint": "~5.0.0",
-    "@vue/cli-service": "~5.0.0",
-    "eslint": "^7.32.0",
-    "eslint-plugin-vue": "^8.0.3",
-    "playwright": "^1.60.0",
-    "vue-template-compiler": "^2.6.14"
-  },
-  "eslintConfig": {
-    "root": true,
-    "env": {
-      "node": true
-    },
-    "extends": [
-      "plugin:vue/essential",
-      "eslint:recommended"
-    ],
-    "parserOptions": {
-      "parser": "@babel/eslint-parser"
-    },
-    "rules": {
-      "vue/multi-word-component-names": "off"
-    }
-  },
-  "browserslist": [
-    "> 1%",
-    "last 2 versions",
-    "not dead"
-  ]
-}

BIN
pc/public/favicon.ico


+ 0 - 17
pc/public/index.html

@@ -1,17 +0,0 @@
-<!DOCTYPE html>
-<html lang="">
-  <head>
-    <meta charset="utf-8">
-    <meta http-equiv="X-UA-Compatible" content="IE=edge">
-    <meta name="viewport" content="width=device-width,initial-scale=1.0">
-    <link rel="icon" href="<%= BASE_URL %>favicon.ico">
-    <title><%= htmlWebpackPlugin.options.title %></title>
-  </head>
-  <body>
-    <noscript>
-      <strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
-    </noscript>
-    <div id="app"></div>
-    <!-- built files will be auto injected -->
-  </body>
-</html>

+ 0 - 40
pc/scripts/capture-one.js

@@ -1,40 +0,0 @@
-/* eslint-disable */
-// 测试脚本:只截一张图(借记卡权益列表)
-const { chromium } = require('playwright')
-const path = require('path')
-const fs = require('fs')
-
-const BASE = 'http://localhost:8080'
-const OUT_DIR = path.resolve(__dirname, '../../prd/汇丰银行/screenshots')
-
-;(async () => {
-  console.log('启动 chromium...')
-  const browser = await chromium.launch({ headless: true })
-  const context = await browser.newContext({ viewport: { width: 1440, height: 900 }, deviceScaleFactor: 2 })
-  const page = await context.newPage()
-
-  console.log('访问登录页注入 token...')
-  await page.goto(BASE + '/login', { waitUntil: 'domcontentloaded' })
-  await page.evaluate(() => {
-    localStorage.setItem('token', 'mock-token-' + Date.now())
-    localStorage.setItem('username', 'admin')
-  })
-
-  const url = BASE + '/#/customization/hsbc/equity-products'
-  console.log('访问', url)
-  try {
-    await page.goto(url, { waitUntil: 'networkidle', timeout: 15000 })
-  } catch (e) {
-    console.log('networkidle 超时,降级 domcontentloaded')
-    await page.goto(url, { waitUntil: 'domcontentloaded', timeout: 15000 })
-  }
-  await page.waitForTimeout(1500)
-
-  if (!fs.existsSync(OUT_DIR)) fs.mkdirSync(OUT_DIR, { recursive: true })
-  const file = path.join(OUT_DIR, '2-1-equity-products-list.png')
-  await page.screenshot({ path: file, fullPage: true })
-  const size = (fs.statSync(file).size / 1024).toFixed(0)
-  console.log(`✓ ${file}  (${size}KB)`)
-
-  await browser.close()
-})()

+ 0 - 120
pc/scripts/capture-screenshots.js

@@ -1,120 +0,0 @@
-/* eslint-disable */
-// 自动截图脚本:登录后逐页访问、等待加载、截图保存到 prd/汇丰银行/screenshots/
-// 用法:node pc/scripts/capture-screenshots.js
-// 前置:pc/ 下 npm run serve 已在 http://localhost:8080 运行
-
-const { chromium } = require('playwright')
-const path = require('path')
-const fs = require('fs')
-
-const BASE = 'http://localhost:8080'
-const OUT_DIR = path.resolve(__dirname, '../../prd/汇丰银行/screenshots')
-
-// 页面清单:{ name: 输出文件名(不带扩展名), path: 路由, step?: 多步骤页的某一步, wait?: 额外等待 ms, action?: 截图前执行的函数 }
-const PAGES = [
-  // §2 借记卡权益
-  { name: '2-1-equity-products-list', path: '/customization/hsbc/equity-products' },
-  { name: '2-2-equity-add-step1', path: '/customization/hsbc/equity-products/add' },
-  { name: '2-2-equity-add-step2', path: '/customization/hsbc/equity-products/add', step: 2 },
-  { name: '2-2-equity-add-step3', path: '/customization/hsbc/equity-products/add', step: 3 },
-  { name: '2-3-equity-edit', path: '/customization/hsbc/equity-products/edit/Q100006' },
-  { name: '2-4-equity-view', path: '/customization/hsbc/equity-products/view/Q100001' },
-  // §3 信用卡权益
-  { name: '3-credit-card-equity-list', path: '/customization/hsbc/credit-card-equity' },
-  // §6 权益审核
-  { name: '6-audit-list', path: '/customization/hsbc/audit' },
-  { name: '6-audit-detail', path: '/customization/hsbc/audit/AR202605150001' },
-  // §三 人群管理
-  { name: 'crowd-task-list', path: '/customization/hsbc/crowd-tasks' },
-  { name: 'crowd-task-add-step1', path: '/customization/hsbc/crowd-tasks/add' },
-  { name: 'crowd-task-add-step2', path: '/customization/hsbc/crowd-tasks/add', step: 2 },
-  { name: 'crowd-name-list-add-step1', path: '/customization/hsbc/crowd-name-lists/add' },
-  { name: 'crowd-name-list-add-step2', path: '/customization/hsbc/crowd-name-lists/add', step: 2 },
-  { name: 'rule-group-list', path: '/customization/hsbc/rule-groups' },
-  { name: 'rule-group-add-step1', path: '/customization/hsbc/rule-groups/add' },
-  { name: 'rule-group-add-step2', path: '/customization/hsbc/rule-groups/add', step: 2 },
-  // §四 发放计划
-  { name: '4-grant-campaign-list', path: '/customization/hsbc/equity-grant-campaign' },
-  { name: '4-grant-campaign-add', path: '/customization/hsbc/equity-grant-campaign/add' },
-  // §五 页面管理
-  { name: '5-page-home', path: '/customization/hsbc/pages/home' },
-  { name: '5-page-zone', path: '/customization/hsbc/pages/zone' },
-  { name: '5-page-assembly', path: '/customization/hsbc/pages/assembly' },
-  // Channel/Campaign
-  { name: 'channel-management', path: '/customization/hsbc/channel' },
-  { name: 'campaign-management', path: '/customization/hsbc/campaign' },
-  // §六 短信管理
-  { name: '6-sms-template-list', path: '/customization/hsbc/sms-templates' },
-  { name: '6-sms-send-record', path: '/customization/hsbc/sms-send-records' },
-  // §七 报表中心
-  { name: '7-points-detail', path: '/customization/hsbc/points-detail' },
-  { name: '7-customer-equity-grant', path: '/customization/hsbc/equity-grant' },
-  { name: '7-cash-discount-grant', path: '/customization/hsbc/cash-discount-grant' }
-]
-
-async function waitDevServer() {
-  const fetchUrl = BASE + '/'
-  for (let i = 0; i < 60; i++) {
-    try {
-      const res = await fetch(fetchUrl)
-      if (res.ok || res.status === 200) return true
-    } catch (_) { /* not ready */ }
-    await new Promise(r => setTimeout(r, 1000))
-  }
-  throw new Error('dev server 未在 60s 内就绪,请确认 npm run serve 已启动')
-}
-
-async function gotoStep(page, stepIdx) {
-  // 三步骤向导(添加/编辑/规则组/客群任务)通过点击「下一步」翻到目标 step
-  // stepIdx 是 1-based 业务步骤;首屏已是 step 1,则点击 stepIdx-1 次「下一步」
-  for (let i = 1; i < stepIdx; i++) {
-    const nextBtn = page.locator('button:has-text("下一步")').first()
-    if (await nextBtn.count() === 0) break
-    await nextBtn.click().catch(() => {})
-    await page.waitForTimeout(400)
-  }
-}
-
-;(async () => {
-  console.log('[1/4] 等待 dev server 就绪...')
-  await waitDevServer()
-  console.log('     dev server OK')
-
-  console.log('[2/4] 启动 chromium...')
-  const browser = await chromium.launch({ headless: true })
-  // 视口宽高按 1/0.8 放大,让一屏容纳更多内容(等价于"缩放 80%"的视觉效果)
-  const context = await browser.newContext({ viewport: { width: 1800, height: 1125 }, deviceScaleFactor: 2 })
-  const page = await context.newPage()
-
-  console.log('[3/4] 注入 token 跳过登录...')
-  await page.goto(BASE + '/#/login', { waitUntil: 'domcontentloaded' })
-  await page.evaluate(() => {
-    localStorage.setItem('token', 'mock-token-' + Date.now())
-    localStorage.setItem('username', 'admin')
-  })
-
-  console.log(`[4/4] 开始截图(共 ${PAGES.length} 张)...`)
-  const results = []
-  for (const item of PAGES) {
-    const url = BASE + '/#' + item.path
-    try {
-      await page.goto(url, { waitUntil: 'networkidle', timeout: 15000 })
-    } catch (_) {
-      // networkidle 在 dev server 热更新场景下可能 timeout,降级用 domcontentloaded
-      await page.goto(url, { waitUntil: 'domcontentloaded', timeout: 15000 })
-    }
-    await page.waitForTimeout(800)
-    if (item.step && item.step > 1) {
-      await gotoStep(page, item.step)
-      await page.waitForTimeout(400)
-    }
-    const file = path.join(OUT_DIR, item.name + '.png')
-    await page.screenshot({ path: file, fullPage: true })
-    const size = (fs.statSync(file).size / 1024).toFixed(0)
-    console.log(`     ✓ ${item.name}.png  (${size}KB)`)
-    results.push({ name: item.name, file })
-  }
-
-  await browser.close()
-  console.log(`\n完成。共 ${results.length} 张,输出目录:${OUT_DIR}`)
-})()

+ 0 - 171
pc/scripts/capture-supplements.js

@@ -1,171 +0,0 @@
-/* eslint-disable */
-// B 类补截:每张图独立 try-catch
-const { chromium } = require('playwright')
-const path = require('path')
-
-const BASE = 'http://localhost:8080'
-const OUT_DIR = path.resolve(__dirname, '../../prd/汇丰银行/screenshots')
-
-async function shot(page, name, full = false) {
-  await page.screenshot({ path: path.join(OUT_DIR, name + '.png'), fullPage: full })
-  console.log('✓', name)
-}
-
-async function safe(label, fn) {
-  try { await fn() } catch (e) { console.log('✗', label, '—', e.message.split('\n')[0]) }
-}
-
-;(async () => {
-  const browser = await chromium.launch({ headless: true })
-  const context = await browser.newContext({ viewport: { width: 1800, height: 1125 }, deviceScaleFactor: 2 })
-  const page = await context.newPage()
-
-  await page.goto(BASE + '/#/login', { waitUntil: 'domcontentloaded' })
-  await page.evaluate(() => {
-    localStorage.setItem('token', 'mock-token-' + Date.now())
-    localStorage.setItem('username', 'admin')
-  })
-
-  // ---- 详情页(3 张)----
-  for (const item of [
-    { name: 'crowd-task-view', url: '/#/customization/hsbc/crowd-tasks/view/00000120251224' },
-    { name: 'rule-group-view', url: '/#/customization/hsbc/rule-groups/view/RG20260401001' },
-    { name: '4-grant-campaign-view', url: '/#/customization/hsbc/equity-grant-campaign/view/P000001' }
-  ]) {
-    await safe(item.name, async () => {
-      await page.goto(BASE + item.url, { waitUntil: 'domcontentloaded' })
-      await page.waitForTimeout(1200)
-      await shot(page, item.name, true)
-    })
-  }
-
-  // ---- 添加权益 4 个分支 ----
-  const goAdd = async () => {
-    await page.goto(BASE + '/#/customization/hsbc/equity-products/add', { waitUntil: 'domcontentloaded' })
-    await page.waitForTimeout(800)
-  }
-
-  await safe('2-2-equity-add-source-rongshu', async () => {
-    await goAdd()
-    await page.locator('label.el-radio:has-text("荣数商品")').first().click()
-    await page.waitForTimeout(400)
-    await shot(page, '2-2-equity-add-source-rongshu', true)
-  })
-
-  await safe('2-2-equity-add-third-list', async () => {
-    await goAdd()
-    await page.locator('label.el-radio:has-text("第三方")').first().click()
-    await page.waitForTimeout(400)
-    await shot(page, '2-2-equity-add-third-list', true)
-  })
-
-  await safe('2-2-equity-add-third-detail', async () => {
-    await goAdd()
-    await page.locator('label.el-radio:has-text("第三方")').first().click()
-    await page.waitForTimeout(300)
-    await page.locator('label.el-radio-button:has-text("有")').first().click()
-    await page.waitForTimeout(400)
-    await shot(page, '2-2-equity-add-third-detail', true)
-  })
-
-  await safe('2-2-equity-add-third-miniprogram', async () => {
-    await goAdd()
-    await page.locator('label.el-radio:has-text("第三方")').first().click()
-    await page.waitForTimeout(300)
-    await page.locator('.el-form-item:has-text("外部权益链接") .el-select').click()
-    await page.waitForTimeout(300)
-    await page.locator('.el-select-dropdown__item:has-text("小程序链接")').click()
-    await page.waitForTimeout(400)
-    await shot(page, '2-2-equity-add-third-miniprogram', true)
-  })
-
-  // ---- 关键弹窗 ----
-  // 下架确认(权益A 已上架 + 审核通过)
-  await safe('dialog-offshelf-confirm', async () => {
-    await page.goto(BASE + '/#/customization/hsbc/equity-products', { waitUntil: 'domcontentloaded' })
-    await page.waitForTimeout(1000)
-    // 通过权益A 这一行的"下架"按钮
-    await page.locator('tr:has-text("权益A") button:has-text("下架")').first().click({ force: true })
-    await page.waitForTimeout(500)
-    await shot(page, 'dialog-offshelf-confirm', false)
-    await page.locator('.el-message-box__btns button:has-text("取消")').click({ force: true }).catch(() => {})
-    await page.waitForTimeout(300)
-  })
-
-  // 删除确认(权益F 草稿状态)
-  await safe('dialog-delete-confirm', async () => {
-    await page.goto(BASE + '/#/customization/hsbc/equity-products', { waitUntil: 'domcontentloaded' })
-    await page.waitForTimeout(1000)
-    await page.locator('tr:has-text("权益F(草稿)") button.el-button--text:has(i.el-icon-more)').first().click({ force: true })
-    await page.waitForTimeout(500)
-    await page.evaluate(() => {
-      const el = [...document.querySelectorAll('.more-actions-item')]
-        .find(e => e.textContent.trim() === '删除' && e.offsetParent !== null)
-      el && el.click()
-    })
-    await page.waitForTimeout(500)
-    await shot(page, 'dialog-delete-confirm', false)
-    await page.locator('.el-message-box__btns button:has-text("取消")').click({ force: true }).catch(() => {})
-    await page.waitForTimeout(300)
-  })
-
-  // 调整顺序气泡(权益A 行)
-  await safe('popover-adjust-order', async () => {
-    await page.goto(BASE + '/#/customization/hsbc/equity-products', { waitUntil: 'domcontentloaded' })
-    await page.waitForTimeout(1000)
-    await page.locator('tr:has-text("权益A") button.el-button--text:has(i.el-icon-more)').first().click({ force: true })
-    await page.waitForTimeout(500)
-    await page.evaluate(() => {
-      const el = [...document.querySelectorAll('.more-actions-item')]
-        .find(e => e.textContent.trim() === '调整显示顺序' && e.offsetParent !== null)
-      el && el.click()
-    })
-    await page.waitForTimeout(500)
-    await shot(page, 'popover-adjust-order', false)
-    // 关闭:点空白处
-    await page.mouse.click(10, 10).catch(() => {})
-    await page.waitForTimeout(300)
-  })
-
-  // 设置显示页面对话框(信用卡列表)
-  await safe('dialog-show-pages', async () => {
-    await page.goto(BASE + '/#/customization/hsbc/credit-card-equity', { waitUntil: 'domcontentloaded' })
-    await page.waitForTimeout(1000)
-    await page.locator('button:has-text("设置显示页面")').first().click({ force: true })
-    await page.waitForTimeout(700)
-    await shot(page, 'dialog-show-pages', false)
-    await page.locator('.el-dialog__footer button:has-text("取消")').click({ force: true }).catch(() => {})
-    await page.waitForTimeout(300)
-  })
-
-  // 生成链接弹窗
-  await safe('dialog-equity-link', async () => {
-    await page.goto(BASE + '/#/customization/hsbc/equity-products', { waitUntil: 'domcontentloaded' })
-    await page.waitForTimeout(1000)
-    await page.locator('tr:has-text("权益A") button.el-button--text:has(i.el-icon-more)').first().click({ force: true })
-    await page.waitForTimeout(500)
-    await page.evaluate(() => {
-      const el = [...document.querySelectorAll('.more-actions-item')]
-        .find(e => e.textContent.trim() === '生成链接' && e.offsetParent !== null)
-      el && el.click()
-    })
-    await page.waitForTimeout(700)
-    await shot(page, 'dialog-equity-link', false)
-    await page.locator('.el-dialog__headerbtn').click({ force: true }).catch(() => {})
-    await page.waitForTimeout(300)
-  })
-
-  // 荣数商品选择弹窗
-  await safe('dialog-rongshu-product', async () => {
-    await page.goto(BASE + '/#/customization/hsbc/equity-products/add', { waitUntil: 'domcontentloaded' })
-    await page.waitForTimeout(1000)
-    await page.locator('label.el-radio:has-text("荣数商品")').first().click()
-    await page.waitForTimeout(500)
-    await page.locator('button:has-text("选择")').first().click({ force: true })
-    await page.waitForTimeout(800)
-    await shot(page, 'dialog-rongshu-product', false)
-  })
-
-  await browser.close()
-  console.log('\nB 类截图完成。')
-})()

+ 0 - 14
pc/src/App.vue

@@ -1,14 +0,0 @@
-<template>
-  <router-view />
-</template>
-
-<script>
-export default {
-  name: 'App'
-}
-</script>
-
-<style>
-* { margin: 0; padding: 0; box-sizing: border-box; }
-body { font-family: -apple-system, BlinkMacSystemFont, 'PingFang SC', sans-serif; }
-</style>

BIN
pc/src/assets/logo.png


+ 0 - 140
pc/src/components/EquityLinkDialog.vue

@@ -1,140 +0,0 @@
-<template>
-  <el-dialog
-    :title="title"
-    :visible.sync="dialogVisible"
-    width="680px"
-    @open="handleOpen"
-  >
-    <el-form label-width="100px" inline>
-      <el-form-item label="Campaign ID">
-        <el-select v-model="form.campaignId" clearable filterable placeholder="请选择 (无)" style="width:240px">
-          <el-option
-            v-for="opt in campaignOptions"
-            :key="opt.value"
-            :label="opt.label"
-            :value="opt.value"
-          />
-        </el-select>
-      </el-form-item>
-      <el-form-item label="Channel ID">
-        <el-select v-model="form.channelId" clearable filterable placeholder="请选择 (无)" style="width:240px">
-          <el-option
-            v-for="opt in channelOptions"
-            :key="opt.value"
-            :label="opt.label"
-            :value="opt.value"
-          />
-        </el-select>
-      </el-form-item>
-    </el-form>
-    <div class="link-section">
-      <div class="link-label"><span class="dot"></span> 权益链接</div>
-      <div class="link-row">
-        <span class="link-type">H5链接</span>
-        <el-input :value="h5Url" readonly size="small" />
-        <el-button size="small" type="primary" plain @click="copyLink(h5Url)">复制</el-button>
-      </div>
-      <div class="link-row">
-        <span class="link-type">小程序链接</span>
-        <el-input :value="appUrl" readonly size="small" />
-        <el-button size="small" type="primary" plain @click="copyLink(appUrl)">复制</el-button>
-      </div>
-    </div>
-  </el-dialog>
-</template>
-
-<script>
-import { fetchLinkOptions } from '@/utils/linkOptions'
-
-export default {
-  name: 'EquityLinkDialog',
-  props: {
-    visible: { type: Boolean, default: false },
-    id: { type: [String, Number], default: '' },
-    pathName: { type: String, default: 'equity' },
-    title: { type: String, default: '生成链接' }
-  },
-  data() {
-    return {
-      form: { campaignId: '', channelId: '' },
-      campaignOptions: [],
-      channelOptions: []
-    }
-  },
-  computed: {
-    dialogVisible: {
-      get() { return this.visible },
-      set(v) { this.$emit('update:visible', v) }
-    },
-    queryStr() {
-      const params = []
-      if (this.form.campaignId) params.push(`CampaignId=${this.form.campaignId}`)
-      if (this.form.channelId) params.push(`Channelid=${this.form.channelId}`)
-      return params.length ? '&' + params.join('&') : ''
-    },
-    h5Url() {
-      return `https://hsbc.com/${this.pathName}?id=${this.id}${this.queryStr}`
-    },
-    appUrl() {
-      return `hsbc-app://${this.pathName}?id=${this.id}${this.queryStr}`
-    }
-  },
-  methods: {
-    async handleOpen() {
-      this.resetForm()
-      const { campaignOptions, channelOptions } = await fetchLinkOptions()
-      this.campaignOptions = campaignOptions
-      this.channelOptions = channelOptions
-    },
-    resetForm() {
-      this.form = { campaignId: '', channelId: '' }
-    },
-    copyLink(text) {
-      if (!text) return
-      navigator.clipboard.writeText(text).then(() => {
-        this.$message.success('链接已复制到剪贴板')
-      }).catch(() => {
-        this.$message.error('复制失败,请手动选取')
-      })
-    }
-  }
-}
-</script>
-
-<style scoped>
-.link-section {
-  margin-top: 8px;
-  padding: 12px;
-  background: #fafafa;
-  border-radius: 6px;
-  border: 1px solid #ebeef5;
-}
-.link-label {
-  font-weight: 600;
-  margin-bottom: 10px;
-  display: flex;
-  align-items: center;
-  gap: 6px;
-  color: #303133;
-}
-.link-row {
-  display: flex;
-  align-items: center;
-  gap: 8px;
-  margin-bottom: 8px;
-}
-.link-row:last-child { margin-bottom: 0; }
-.link-type {
-  font-size: 12px;
-  color: #909399;
-  width: 70px;
-  flex-shrink: 0;
-}
-.dot {
-  width: 8px;
-  height: 8px;
-  border-radius: 50%;
-  display: inline-block;
-  background: #67C23A;
-}
-</style>

+ 0 - 58
pc/src/components/HelloWorld.vue

@@ -1,58 +0,0 @@
-<template>
-  <div class="hello">
-    <h1>{{ msg }}</h1>
-    <p>
-      For a guide and recipes on how to configure / customize this project,<br>
-      check out the
-      <a href="https://cli.vuejs.org" target="_blank" rel="noopener">vue-cli documentation</a>.
-    </p>
-    <h3>Installed CLI Plugins</h3>
-    <ul>
-      <li><a href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-babel" target="_blank" rel="noopener">babel</a></li>
-      <li><a href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-eslint" target="_blank" rel="noopener">eslint</a></li>
-    </ul>
-    <h3>Essential Links</h3>
-    <ul>
-      <li><a href="https://vuejs.org" target="_blank" rel="noopener">Core Docs</a></li>
-      <li><a href="https://forum.vuejs.org" target="_blank" rel="noopener">Forum</a></li>
-      <li><a href="https://chat.vuejs.org" target="_blank" rel="noopener">Community Chat</a></li>
-      <li><a href="https://twitter.com/vuejs" target="_blank" rel="noopener">Twitter</a></li>
-      <li><a href="https://news.vuejs.org" target="_blank" rel="noopener">News</a></li>
-    </ul>
-    <h3>Ecosystem</h3>
-    <ul>
-      <li><a href="https://router.vuejs.org" target="_blank" rel="noopener">vue-router</a></li>
-      <li><a href="https://vuex.vuejs.org" target="_blank" rel="noopener">vuex</a></li>
-      <li><a href="https://github.com/vuejs/vue-devtools#vue-devtools" target="_blank" rel="noopener">vue-devtools</a></li>
-      <li><a href="https://vue-loader.vuejs.org" target="_blank" rel="noopener">vue-loader</a></li>
-      <li><a href="https://github.com/vuejs/awesome-vue" target="_blank" rel="noopener">awesome-vue</a></li>
-    </ul>
-  </div>
-</template>
-
-<script>
-export default {
-  name: 'HelloWorld',
-  props: {
-    msg: String
-  }
-}
-</script>
-
-<!-- Add "scoped" attribute to limit CSS to this component only -->
-<style scoped>
-h3 {
-  margin: 40px 0 0;
-}
-ul {
-  list-style-type: none;
-  padding: 0;
-}
-li {
-  display: inline-block;
-  margin: 0 10px;
-}
-a {
-  color: #42b983;
-}
-</style>

+ 0 - 250
pc/src/components/Layout.vue

@@ -1,250 +0,0 @@
-<template>
-  <div class="layout">
-    <!-- 侧边栏 -->
-    <aside :class="['sidebar', { collapsed }]">
-      <div class="logo">
-        <i class="el-icon-monitor"></i>
-        <span v-if="!collapsed">管理后台</span>
-      </div>
-      <el-menu
-        :default-active="$route.path"
-        router
-        :collapse="collapsed"
-        background-color="#304156"
-        text-color="#bfcbd9"
-        active-text-color="#409EFF"
-      >
-        <el-submenu index="customization">
-          <template slot="title">
-            <i class="el-icon-setting"></i>
-            <span>定制功能</span>
-          </template>
-          <el-submenu index="customization-hsbc">
-            <template slot="title">汇丰银行</template>
-            <el-submenu index="customization-hsbc-pages">
-              <template slot="title">页面管理</template>
-              <el-menu-item index="/customization/hsbc/pages/home">
-                权益介绍页
-              </el-menu-item>
-              <el-menu-item index="/customization/hsbc/pages/zone">
-                权益首页
-              </el-menu-item>
-              <el-menu-item index="/customization/hsbc/pages/assembly">
-                组装页
-              </el-menu-item>
-            </el-submenu>
-            <el-submenu index="customization-hsbc-equity">
-              <template slot="title">权益管理</template>
-              <el-menu-item index="/customization/hsbc/equity-products">
-                借记卡权益
-              </el-menu-item>
-              <el-menu-item index="/customization/hsbc/credit-card-equity">
-                信用卡权益
-              </el-menu-item>
-              <el-menu-item index="/customization/hsbc/audit">
-                权益审核
-              </el-menu-item>
-            </el-submenu>
-            <el-menu-item index="/customization/hsbc/equity-grant-campaign">
-              发放计划
-            </el-menu-item>
-            <el-submenu index="customization-hsbc-crowd">
-              <template slot="title">人群管理</template>
-              <el-menu-item index="/customization/hsbc/crowd-tasks">
-                客群任务管理
-              </el-menu-item>
-              <el-menu-item index="/customization/hsbc/rule-groups">
-                规则组管理
-              </el-menu-item>
-            </el-submenu>
-            <el-submenu index="customization-hsbc-sms">
-              <template slot="title">短信管理</template>
-              <el-menu-item index="/customization/hsbc/sms-templates">
-                短信模板管理
-              </el-menu-item>
-              <el-menu-item index="/customization/hsbc/sms-send-records">
-                发送记录查询
-              </el-menu-item>
-            </el-submenu>
-            <el-submenu index="customization-hsbc-channel">
-              <template slot="title">渠道管理</template>
-              <el-menu-item index="/customization/hsbc/channel">
-                Channel管理
-              </el-menu-item>
-              <el-menu-item index="/customization/hsbc/campaign">
-                Campaign管理
-              </el-menu-item>
-            </el-submenu>
-            <el-submenu index="customization-hsbc-report">
-              <template slot="title">报表中心</template>
-              <el-menu-item index="/customization/hsbc/points-detail">
-                积分明细查询
-              </el-menu-item>
-              <el-menu-item index="/customization/hsbc/equity-grant">
-                客户权益发放
-              </el-menu-item>
-              <el-menu-item index="/customization/hsbc/cash-discount-grant">
-                立减金发放明细
-              </el-menu-item>
-            </el-submenu>
-          </el-submenu>
-        </el-submenu>
-        <el-submenu index="system">
-          <template slot="title">
-            <i class="el-icon-s-tools"></i>
-            <span>系统设置</span>
-          </template>
-          <el-menu-item index="/system/users">用户管理</el-menu-item>
-          <el-menu-item index="/system/roles">角色管理</el-menu-item>
-        </el-submenu>
-      </el-menu>
-    </aside>
-
-    <!-- 主内容区 -->
-    <div class="main">
-      <!-- 顶部栏 -->
-      <header class="header">
-        <i
-          :class="collapsed ? 'el-icon-s-unfold' : 'el-icon-s-fold'"
-          class="toggle-btn"
-          @click="collapsed = !collapsed"
-        ></i>
-        <el-breadcrumb separator="/" class="breadcrumb">
-          <el-breadcrumb-item :to="{ path: '/' }">首页</el-breadcrumb-item>
-          <el-breadcrumb-item v-if="currentTitle">
-            {{ currentTitle }}
-          </el-breadcrumb-item>
-        </el-breadcrumb>
-        <el-dropdown trigger="click" @command="handleUserCommand">
-          <span class="user-info">
-            <i class="el-icon-user-solid"></i>
-            {{ username }}
-            <i class="el-icon-arrow-down"></i>
-          </span>
-          <el-dropdown-menu slot="dropdown">
-            <el-dropdown-item command="logout">退出登录</el-dropdown-item>
-          </el-dropdown-menu>
-        </el-dropdown>
-      </header>
-
-      <!-- 页面内容 -->
-      <div class="content">
-        <router-view />
-      </div>
-    </div>
-  </div>
-</template>
-
-<script>
-export default {
-  name: 'Layout',
-  data() {
-    return {
-      collapsed: false,
-      username: localStorage.getItem('username') || '用户'
-    }
-  },
-  computed: {
-    currentTitle() {
-      return this.$route.meta?.title || ''
-    }
-  },
-  methods: {
-    handleUserCommand(cmd) {
-      if (cmd === 'logout') {
-        this.$confirm('确定要退出登录吗?', '提示', {
-          confirmButtonText: '退出',
-          cancelButtonText: '取消',
-          type: 'warning'
-        }).then(() => {
-          localStorage.removeItem('token')
-          localStorage.removeItem('username')
-          this.$router.replace('/login')
-        }).catch(() => {})
-      }
-    }
-  }
-}
-</script>
-
-<style scoped>
-.layout { display: flex; height: 100vh; overflow: hidden; }
-
-.sidebar {
-  width: 210px;
-  background: #304156;
-  transition: width 0.3s;
-  display: flex;
-  flex-direction: column;
-  flex-shrink: 0;
-  overflow: hidden;
-}
-.sidebar.collapsed { width: 64px; }
-
-.logo {
-  height: 60px;
-  display: flex;
-  align-items: center;
-  justify-content: center;
-  gap: 10px;
-  color: #fff;
-  font-size: 18px;
-  font-weight: bold;
-  background: #263445;
-  overflow: hidden;
-  white-space: nowrap;
-}
-.logo i { font-size: 22px; flex-shrink: 0; }
-
-.el-menu {
-  border: none;
-  flex: 1;
-  min-height: 0;
-  overflow-x: hidden;
-  overflow-y: auto;
-}
-.el-menu--collapse { width: 64px; }
-
-.el-menu::-webkit-scrollbar { width: 6px; }
-.el-menu::-webkit-scrollbar-thumb {
-  background: rgba(191, 203, 217, .28);
-  border-radius: 6px;
-}
-.el-menu::-webkit-scrollbar-track { background: transparent; }
-
-.main { flex: 1; display: flex; flex-direction: column; overflow: hidden; }
-
-.header {
-  height: 60px;
-  background: #fff;
-  box-shadow: 0 1px 4px rgba(0,21,41,.08);
-  display: flex;
-  align-items: center;
-  padding: 0 20px;
-  gap: 16px;
-  flex-shrink: 0;
-}
-
-.toggle-btn { font-size: 20px; cursor: pointer; color: #606266; }
-.toggle-btn:hover { color: #409EFF; }
-
-.breadcrumb { flex: 1; }
-
-.user-info {
-  display: inline-flex;
-  align-items: center;
-  gap: 6px;
-  color: #606266;
-  cursor: pointer;
-  font-size: 14px;
-}
-.user-info:hover { color: #409EFF; }
-.user-info .el-icon-user-solid { font-size: 18px; }
-
-.content {
-  flex: 1;
-  padding: 20px;
-  background: #f0f2f5;
-  overflow-y: auto;
-}
-</style>

+ 0 - 119
pc/src/components/RongshuProductDialog.vue

@@ -1,119 +0,0 @@
-<template>
-  <el-dialog
-    title="选择荣数商品"
-    :visible.sync="dialogVisible"
-    width="900px"
-    @open="onOpen"
-  >
-    <el-form :model="filter" inline>
-      <el-form-item label="商品名称">
-        <el-input
-          v-model="filter.name"
-          placeholder="请输入"
-          clearable
-          style="width: 220px"
-          @keyup.enter.native="handleSearch"
-        />
-      </el-form-item>
-      <el-form-item>
-        <el-button type="primary" @click="handleSearch">查询</el-button>
-        <el-button @click="handleReset">重置</el-button>
-      </el-form-item>
-    </el-form>
-
-    <el-table :data="pagedData" border stripe size="small" style="width: 100%">
-      <el-table-column prop="name" label="商品名称" min-width="180" show-overflow-tooltip />
-      <el-table-column prop="merchant" label="商户名称" width="160" show-overflow-tooltip />
-      <el-table-column label="银行结算价" width="120" align="right">
-        <template slot-scope="{ row }">¥{{ row.settlePrice.toFixed(2) }}</template>
-      </el-table-column>
-      <el-table-column prop="stock" label="库存数" width="100" align="right" />
-      <el-table-column label="操作" width="80" align="center" fixed="right">
-        <template slot-scope="{ row }">
-          <el-button type="text" size="small" @click="handleSelect(row)">选择</el-button>
-        </template>
-      </el-table-column>
-    </el-table>
-
-    <div class="pagination-bar">
-      <el-pagination
-        background
-        layout="total, prev, pager, next"
-        :page-size="10"
-        :current-page.sync="currentPage"
-        :total="filteredData.length"
-      />
-    </div>
-  </el-dialog>
-</template>
-
-<script>
-const MOCK_PRODUCTS = [
-  { name: '微信立减金 5元(满10减5)', merchant: '微信支付', settlePrice: 5, stock: 2000, mainImage: [{ name: 'wx-5.png', url: 'https://placehold.co/200x200/07c160/ffffff?text=%C2%A55' }] },
-  { name: '微信立减金 8元(满30减8)', merchant: '微信支付', settlePrice: 8, stock: 1500, mainImage: [{ name: 'wx-8.png', url: 'https://placehold.co/200x200/07c160/ffffff?text=%C2%A58' }] },
-  { name: '微信立减金 10元(满50减10)', merchant: '微信支付', settlePrice: 10, stock: 1200, mainImage: [{ name: 'wx-10.png', url: 'https://placehold.co/200x200/07c160/ffffff?text=%C2%A510' }] },
-  { name: '微信立减金 18元(满88减18)', merchant: '微信支付', settlePrice: 18, stock: 1000, mainImage: [{ name: 'wx-18.png', url: 'https://placehold.co/200x200/07c160/ffffff?text=%C2%A518' }] },
-  { name: '微信立减金 20元(满100减20)', merchant: '微信支付', settlePrice: 20, stock: 800, mainImage: [{ name: 'wx-20.png', url: 'https://placehold.co/200x200/07c160/ffffff?text=%C2%A520' }] },
-  { name: '微信立减金 30元(满150减30)', merchant: '微信支付', settlePrice: 30, stock: 600, mainImage: [{ name: 'wx-30.png', url: 'https://placehold.co/200x200/07c160/ffffff?text=%C2%A530' }] },
-  { name: '微信立减金 50元(满200减50)', merchant: '微信支付', settlePrice: 50, stock: 400, mainImage: [{ name: 'wx-50.png', url: 'https://placehold.co/200x200/07c160/ffffff?text=%C2%A550' }] },
-  { name: '微信立减金 66元(满300减66)', merchant: '微信支付', settlePrice: 66, stock: 300, mainImage: [{ name: 'wx-66.png', url: 'https://placehold.co/200x200/07c160/ffffff?text=%C2%A566' }] },
-  { name: '微信立减金 88元(满388减88)', merchant: '微信支付', settlePrice: 88, stock: 200, mainImage: [{ name: 'wx-88.png', url: 'https://placehold.co/200x200/07c160/ffffff?text=%C2%A588' }] },
-  { name: '微信立减金 100元(满500减100)', merchant: '微信支付', settlePrice: 100, stock: 150, mainImage: [{ name: 'wx-100.png', url: 'https://placehold.co/200x200/07c160/ffffff?text=%C2%A5100' }] },
-  { name: '微信立减金 128元(满600减128)', merchant: '微信支付', settlePrice: 128, stock: 100, mainImage: [{ name: 'wx-128.png', url: 'https://placehold.co/200x200/07c160/ffffff?text=%C2%A5128' }] },
-  { name: '微信立减金 200元(满800减200)', merchant: '微信支付', settlePrice: 200, stock: 80, mainImage: [{ name: 'wx-200.png', url: 'https://placehold.co/200x200/07c160/ffffff?text=%C2%A5200' }] }
-]
-
-export default {
-  name: 'RongshuProductDialog',
-  props: {
-    visible: { type: Boolean, default: false }
-  },
-  data() {
-    return {
-      filter: { name: '' },
-      currentPage: 1,
-      products: MOCK_PRODUCTS
-    }
-  },
-  computed: {
-    dialogVisible: {
-      get() { return this.visible },
-      set(v) { this.$emit('update:visible', v) }
-    },
-    filteredData() {
-      if (!this.filter.name) return this.products
-      const kw = this.filter.name.trim()
-      return this.products.filter(p => p.name.includes(kw))
-    },
-    pagedData() {
-      const start = (this.currentPage - 1) * 10
-      return this.filteredData.slice(start, start + 10)
-    }
-  },
-  methods: {
-    onOpen() {
-      this.filter = { name: '' }
-      this.currentPage = 1
-    },
-    handleSearch() {
-      this.currentPage = 1
-    },
-    handleReset() {
-      this.filter = { name: '' }
-      this.currentPage = 1
-    },
-    handleSelect(row) {
-      this.$emit('select', { ...row })
-      this.dialogVisible = false
-    }
-  }
-}
-</script>
-
-<style scoped>
-.pagination-bar {
-  margin-top: 12px;
-  display: flex;
-  justify-content: flex-end;
-}
-</style>

+ 0 - 88
pc/src/components/ShowPagesDialog.vue

@@ -1,88 +0,0 @@
-<template>
-  <el-dialog
-    title="设置显示页面"
-    :visible.sync="dialogVisible"
-    width="560px"
-    @open="onOpen"
-  >
-    <el-form :model="form" label-width="140px" label-position="left">
-      <el-form-item label="权益首页/列表页">
-        <el-radio-group v-model="form.showOnHome">
-          <el-radio label="显示">显示</el-radio>
-          <el-radio label="不显示">不显示</el-radio>
-        </el-radio-group>
-      </el-form-item>
-      <el-form-item label="组装页">
-        <el-radio-group v-model="form.showOnAssembly">
-          <el-radio label="显示">显示</el-radio>
-          <el-radio label="不显示">不显示</el-radio>
-        </el-radio-group>
-        <div
-          v-if="form.showOnAssembly === '显示'"
-          style="background: #f1f2f5;padding: 16px;margin-top: 10px;"
-        >
-          <el-select
-            v-model="form.assemblyPages"
-            multiple
-            filterable
-            placeholder="请选择组装页"
-            style="width: 100%"
-          >
-            <el-option label="组装页A" value="组装页A"></el-option>
-            <el-option label="组装页B" value="组装页B"></el-option>
-            <el-option label="组装页C" value="组装页C"></el-option>
-            <el-option label="组装页D" value="组装页D"></el-option>
-          </el-select>
-        </div>
-      </el-form-item>
-    </el-form>
-    <span slot="footer">
-      <el-button @click="dialogVisible = false">取消</el-button>
-      <el-button type="primary" @click="handleConfirm">确定</el-button>
-    </span>
-  </el-dialog>
-</template>
-
-<script>
-export default {
-  name: 'ShowPagesDialog',
-  props: {
-    visible: { type: Boolean, default: false },
-    row: { type: Object, default: null }
-  },
-  data() {
-    return {
-      form: {
-        showOnHome: '显示',
-        showOnAssembly: '不显示',
-        assemblyPages: []
-      }
-    }
-  },
-  computed: {
-    dialogVisible: {
-      get() { return this.visible },
-      set(val) { this.$emit('update:visible', val) }
-    }
-  },
-  methods: {
-    onOpen() {
-      if (this.row) {
-        this.form.showOnHome = this.row.showOnHome || '显示'
-        this.form.showOnAssembly = this.row.showOnAssembly || '不显示'
-        this.form.assemblyPages = Array.isArray(this.row.assemblyPages)
-          ? [...this.row.assemblyPages]
-          : []
-      } else {
-        this.form.showOnHome = '显示'
-        this.form.showOnAssembly = '不显示'
-        this.form.assemblyPages = []
-      }
-    },
-    handleConfirm() {
-      this.$emit('confirm', { ...this.form, assemblyPages: [...this.form.assemblyPages] })
-      this.dialogVisible = false
-    }
-  }
-}
-</script>

+ 0 - 119
pc/src/components/crowdNameList/AdjustMemberDialog.vue

@@ -1,119 +0,0 @@
-<template>
-  <el-dialog
-    title="人员调整"
-    :visible.sync="innerVisible"
-    width="520px"
-    :close-on-click-modal="false"
-    @close="reset"
-  >
-    <el-form label-width="100px" label-position="right">
-      <el-form-item label="客群编号" required>
-        <el-select
-          v-model="form.listId"
-          placeholder="请选择"
-          filterable
-          style="width: 100%"
-        >
-          <el-option
-            v-for="item in availableLists"
-            :key="item.id"
-            :label="`${item.id}-${item.name}`"
-            :value="item.id"
-          />
-        </el-select>
-      </el-form-item>
-
-      <el-form-item label="客户号" required>
-        <el-input
-          v-model.trim="form.customerId"
-          placeholder="请输入客户号(5 位数字)"
-          maxlength="10"
-        />
-      </el-form-item>
-
-      <el-form-item label="调整方向" required>
-        <el-select v-model="form.direction" placeholder="请选择" style="width: 100%">
-          <el-option label="调增 - A" value="A" />
-          <el-option label="调减 - D" value="D" />
-        </el-select>
-      </el-form-item>
-    </el-form>
-
-    <div slot="footer" class="dialog-footer">
-      <el-button @click="handleCancel">取 消</el-button>
-      <el-button type="primary" @click="handleSave">保 存</el-button>
-    </div>
-  </el-dialog>
-</template>
-
-<script>
-import crowdNameListStore from '@/store/crowdNameListStore'
-
-export default {
-  name: 'AdjustMemberDialog',
-  props: {
-    visible: { type: Boolean, default: false },
-    defaultListId: { type: String, default: '' }
-  },
-  data() {
-    return {
-      form: { listId: '', customerId: '', direction: '' }
-    }
-  },
-  computed: {
-    innerVisible: {
-      get() { return this.visible },
-      set(v) { this.$emit('update:visible', v) }
-    },
-    availableLists() {
-      return crowdNameListStore.list.filter((l) => l.status !== 'stopped')
-    }
-  },
-  watch: {
-    visible(v) {
-      if (v) {
-        this.reset()
-        if (this.defaultListId) this.form.listId = this.defaultListId
-      }
-    }
-  },
-  methods: {
-    reset() {
-      this.form = { listId: '', customerId: '', direction: '' }
-    },
-    handleSave() {
-      if (!this.form.listId) {
-        this.$message.error('请选择客群编号')
-        return
-      }
-      if (!this.form.customerId) {
-        this.$message.error('请输入客户号')
-        return
-      }
-      if (!this.form.direction) {
-        this.$message.error('请选择调整方向')
-        return
-      }
-      const r = crowdNameListStore.adjustOne({
-        listId: this.form.listId,
-        customerId: this.form.customerId,
-        direction: this.form.direction
-      })
-      if (r.ok) {
-        this.$message.success(`调整提交成功(${r.action === 'add' ? '已新增' : '已移除'}),审核通过后生效(demo 默认通过)`)
-        this.innerVisible = false
-        this.$emit('saved')
-      } else {
-        this.$message.error(r.reason)
-      }
-    },
-    handleCancel() {
-      this.innerVisible = false
-    }
-  }
-}
-</script>
-
-<style scoped>
-.dialog-footer { text-align: center; }
-</style>

+ 0 - 199
pc/src/components/crowdNameList/UploadAdjustDialog.vue

@@ -1,199 +0,0 @@
-<template>
-  <el-dialog
-    title="上传调整文件"
-    :visible.sync="innerVisible"
-    width="640px"
-    :close-on-click-modal="false"
-    @close="reset"
-  >
-    <div class="format-block">
-      <div class="format-title">文件格式说明</div>
-      <div class="format-line">每行一条调整记录,使用 <code>|+|</code> 作为分隔符:</div>
-      <pre class="format-sample">客户号|+|调整方向
-00001|+|A
-00002|+|D</pre>
-      <div class="format-line">调整方向:<code>A</code> = 调增,<code>D</code> = 调减</div>
-      <div v-if="targetListLabel" class="format-line">本次调整目标:<code>{{ targetListLabel }}</code></div>
-    </div>
-
-    <el-upload
-      class="upload-area"
-      drag action="#"
-      :auto-upload="false"
-      :show-file-list="false"
-      :on-change="handleFile"
-      accept=".txt,.csv"
-    >
-      <i class="el-icon-upload"></i>
-      <div class="el-upload__text">将文件拖到此处,或<em>点击选择文件</em></div>
-      <div slot="tip" class="el-upload__tip">仅支持 .txt / .csv,建议 UTF-8 编码</div>
-    </el-upload>
-
-    <div v-if="rawLines.length > 0" class="preview">
-      <div class="preview-title">解析预览(共 {{ rawLines.length }} 行,有效 {{ validItems.length }} 行,无效 {{ invalidLines.length }} 行)</div>
-      <el-table :data="previewRows" border size="small" max-height="240">
-        <el-table-column prop="line" label="行号" width="60" align="center" />
-        <el-table-column prop="customerId" label="客户号" width="120" />
-        <el-table-column label="调整方向" width="110">
-          <template slot-scope="scope">
-            {{ scope.row.direction === 'A' ? '调增-A' : scope.row.direction === 'D' ? '调减-D' : scope.row.direction }}
-          </template>
-        </el-table-column>
-        <el-table-column label="状态" min-width="160">
-          <template slot-scope="scope">
-            <el-tag v-if="scope.row.error" type="danger" size="small">{{ scope.row.error }}</el-tag>
-            <el-tag v-else type="success" size="small">有效</el-tag>
-          </template>
-        </el-table-column>
-      </el-table>
-    </div>
-
-    <div v-if="result" class="result-block">
-      <el-alert
-        :title="`处理完成:成功 ${result.success} 条,失败 ${result.failed} 条`"
-        :type="result.failed === 0 ? 'success' : 'warning'"
-        :closable="false"
-        show-icon
-      />
-      <div v-if="result.failures.length > 0" class="result-failures">
-        <div>失败明细:</div>
-        <ul>
-          <li v-for="(f, i) in result.failures" :key="i">
-            第 {{ f.line }} 行({{ f.item.customerId }} / {{ f.item.direction }}):{{ f.reason }}
-          </li>
-        </ul>
-      </div>
-    </div>
-
-    <div slot="footer" class="dialog-footer">
-      <el-button @click="handleCancel">取 消</el-button>
-      <el-button
-        type="primary"
-        :disabled="validItems.length === 0"
-        @click="handleApply"
-      >确认上传({{ validItems.length }} 条)</el-button>
-    </div>
-  </el-dialog>
-</template>
-
-<script>
-import crowdNameListStore from '@/store/crowdNameListStore'
-
-export default {
-  name: 'UploadAdjustDialog',
-  props: {
-    visible: { type: Boolean, default: false },
-    defaultListId: { type: String, default: '' }
-  },
-  data() {
-    return {
-      rawLines: [],
-      previewRows: [],
-      result: null
-    }
-  },
-  computed: {
-    innerVisible: {
-      get() { return this.visible },
-      set(v) { this.$emit('update:visible', v) }
-    },
-    validItems() {
-      return this.previewRows.filter((r) => !r.error)
-    },
-    invalidLines() {
-      return this.previewRows.filter((r) => r.error)
-    },
-    targetListLabel() {
-      if (!this.defaultListId) return ''
-      const l = crowdNameListStore.getById(this.defaultListId)
-      return l ? `${l.id}-${l.name}` : this.defaultListId
-    }
-  },
-  watch: {
-    visible(v) {
-      if (v) this.reset()
-    }
-  },
-  methods: {
-    reset() {
-      this.rawLines = []
-      this.previewRows = []
-      this.result = null
-    },
-    handleFile(file) {
-      const reader = new FileReader()
-      reader.onload = (e) => {
-        const text = e.target.result || ''
-        const lines = text.split(/\r?\n/).map((l) => l.trim()).filter((l) => l)
-        this.rawLines = lines
-        this.parseLines(lines)
-      }
-      reader.readAsText(file.raw, 'utf-8')
-    },
-    parseLines(lines) {
-      const listId = this.defaultListId
-      const list = listId ? crowdNameListStore.getById(listId) : null
-      this.previewRows = lines.map((line, idx) => {
-        const parts = line.split('|+|')
-        if (parts.length !== 2) {
-          return { line: idx + 1, raw: line, error: '格式错误(应为2段,使用 |+| 分隔)' }
-        }
-        const [customerId, direction] = parts.map((s) => s.trim())
-        if (!customerId) return { line: idx + 1, customerId, direction, error: '客户号为空' }
-        if (!list) return { line: idx + 1, customerId, direction, error: '目标客群不存在' }
-        if (list.status === 'stopped') return { line: idx + 1, customerId, direction, error: '名单已停止' }
-        if (direction !== 'A' && direction !== 'D') {
-          return { line: idx + 1, customerId, direction, error: '调整方向只能为 A 或 D' }
-        }
-        return { line: idx + 1, customerId, direction }
-      })
-    },
-    handleApply() {
-      if (this.validItems.length === 0) {
-        this.$message.warning('没有有效记录可上传')
-        return
-      }
-      const items = this.validItems.map((r) => ({
-        listId: this.defaultListId,
-        customerId: r.customerId,
-        direction: r.direction
-      }))
-      this.result = crowdNameListStore.applyBatch(items)
-      this.$message.success(`已提交 ${items.length} 条调整,审核通过后生效(demo 默认通过)`)
-      this.$emit('applied')
-    },
-    handleCancel() {
-      this.innerVisible = false
-    }
-  }
-}
-</script>
-
-<style scoped>
-.format-block {
-  background: #f5f7fa; border-radius: 4px;
-  padding: 12px 16px; margin-bottom: 16px; font-size: 13px;
-}
-.format-title { font-weight: 600; color: #303133; margin-bottom: 6px; }
-.format-line { color: #606266; margin: 4px 0; }
-.format-line code { background: #fff; padding: 1px 4px; border-radius: 2px; color: #F56C6C; }
-.format-sample {
-  background: #fff; padding: 8px 12px; border: 1px solid #ebeef5; border-radius: 2px;
-  font-family: 'Menlo', 'Consolas', monospace; font-size: 12px;
-  margin: 6px 0; white-space: pre;
-}
-
-.upload-area { margin-bottom: 16px; }
-
-.preview { margin-top: 12px; }
-.preview-title { font-size: 13px; color: #606266; margin-bottom: 8px; }
-
-.result-block { margin-top: 16px; }
-.result-failures {
-  margin-top: 8px; font-size: 12px; color: #909399;
-  max-height: 120px; overflow-y: auto;
-}
-.result-failures ul { padding-left: 18px; margin: 4px 0; }
-
-.dialog-footer { text-align: center; }
-</style>

+ 0 - 131
pc/src/components/crowdTask/AddRuleGroupDialog.vue

@@ -1,131 +0,0 @@
-<template>
-  <el-dialog
-    title="新增规则组"
-    :visible.sync="innerVisible"
-    width="560px"
-    :close-on-click-modal="false"
-    @close="reset"
-  >
-    <el-form ref="form" :model="form" label-width="140px" label-position="right">
-      <el-form-item label="规则组卡种类型" required>
-        <el-select
-          v-model="form.cardType"
-          placeholder="请选择"
-          style="width: 100%"
-          @change="form.ruleGroupId = ''"
-        >
-          <el-option label="借记卡圈客规则" value="debitCard" />
-          <el-option label="信用卡圈客规则" value="creditCard" />
-        </el-select>
-      </el-form-item>
-
-      <el-form-item label="规则组" required>
-        <el-select
-          v-model="form.ruleGroupId"
-          placeholder="请选择"
-          filterable
-          :filter-method="filterRuleGroup"
-          style="width: 100%"
-          no-data-text="暂无可选规则组"
-          no-match-text="未找到匹配的规则组"
-        >
-          <el-option
-            v-for="rg in filteredGroups"
-            :key="rg.id"
-            :label="`${rg.id}(${rg.name})`"
-            :value="rg.id"
-          />
-        </el-select>
-        <div class="hint">仅展示「已上线」状态且与卡种类型匹配的规则组</div>
-      </el-form-item>
-    </el-form>
-
-    <div slot="footer" class="dialog-footer">
-      <el-button @click="handleCancel">取 消</el-button>
-      <el-button type="primary" @click="handleSave">保 存</el-button>
-    </div>
-  </el-dialog>
-</template>
-
-<script>
-import ruleGroupStore from '@/store/ruleGroupStore'
-
-export default {
-  name: 'AddRuleGroupDialog',
-  props: {
-    visible: { type: Boolean, default: false },
-    excludeIds: { type: Array, default: () => [] }
-  },
-  data() {
-    return {
-      form: { cardType: '', ruleGroupId: '' },
-      filterKeyword: ''
-    }
-  },
-  computed: {
-    innerVisible: {
-      get() { return this.visible },
-      set(v) { this.$emit('update:visible', v) }
-    },
-    filteredGroups() {
-      const all = ruleGroupStore.list.filter((rg) => rg.status === 'online')
-      return all.filter((rg) => {
-        if (this.form.cardType && rg.type !== this.form.cardType) return false
-        if (this.excludeIds.includes(rg.id)) return false
-        if (this.filterKeyword) {
-          const k = this.filterKeyword.toLowerCase()
-          if (!rg.id.toLowerCase().includes(k) && !rg.name.toLowerCase().includes(k)) {
-            return false
-          }
-        }
-        return true
-      })
-    }
-  },
-  watch: {
-    visible(v) {
-      if (v) this.reset()
-    }
-  },
-  methods: {
-    reset() {
-      this.form = { cardType: '', ruleGroupId: '' }
-      this.filterKeyword = ''
-    },
-    filterRuleGroup(k) {
-      this.filterKeyword = k
-    },
-    handleSave() {
-      if (!this.form.cardType) {
-        this.$message.error('请选择规则组卡种类型')
-        return
-      }
-      if (!this.form.ruleGroupId) {
-        this.$message.error('请选择规则组')
-        return
-      }
-      const rg = ruleGroupStore.getById(this.form.ruleGroupId)
-      if (!rg) {
-        this.$message.error('所选规则组不存在')
-        return
-      }
-      this.$emit('save', {
-        id: rg.id,
-        name: rg.name,
-        description: rg.description || '-',
-        logic: rg.logic,
-        ruleCount: (rg.rules || []).length
-      })
-      this.innerVisible = false
-    },
-    handleCancel() {
-      this.innerVisible = false
-    }
-  }
-}
-</script>
-
-<style scoped>
-.hint { color: #909399; font-size: 12px; margin-top: 4px; }
-.dialog-footer { text-align: center; }
-</style>

+ 0 - 40
pc/src/components/crowdTask/SampleDetailDialog.vue

@@ -1,40 +0,0 @@
-<template>
-  <el-dialog
-    title="sample结果详情"
-    :visible.sync="innerVisible"
-    width="520px"
-  >
-    <div class="dialog-body">
-      <span class="label"><span class="required">*</span> sample 结果</span>
-      <el-table :data="rows" border stripe size="small" style="margin-top: 8px">
-        <el-table-column prop="index" label="序号" width="100" align="center" />
-        <el-table-column prop="customerId" label="客户号" align="center" />
-      </el-table>
-    </div>
-    <div slot="footer" class="dialog-footer">
-      <el-button type="primary" @click="innerVisible = false">返 回</el-button>
-    </div>
-  </el-dialog>
-</template>
-
-<script>
-export default {
-  name: 'SampleDetailDialog',
-  props: {
-    visible: { type: Boolean, default: false },
-    rows: { type: Array, default: () => [] }
-  },
-  computed: {
-    innerVisible: {
-      get() { return this.visible },
-      set(v) { this.$emit('update:visible', v) }
-    }
-  }
-}
-</script>
-
-<style scoped>
-.dialog-body .label { font-weight: 600; color: #303133; }
-.required { color: #F56C6C; margin-right: 4px; }
-.dialog-footer { text-align: center; }
-</style>

+ 0 - 268
pc/src/components/ruleGroup/RuleEditDialog.vue

@@ -1,268 +0,0 @@
-<template>
-  <el-dialog
-    :title="dialogTitle"
-    :visible.sync="innerVisible"
-    width="640px"
-    :close-on-click-modal="false"
-    @close="handleCancel"
-  >
-    <el-form
-      ref="form"
-      :model="form"
-      label-width="140px"
-      label-position="right"
-    >
-      <el-form-item label="元标签规则" required>
-        <el-select
-          v-model="form.metaCode"
-          placeholder="请选择"
-          :disabled="readonly"
-          style="width: 100%"
-          @change="handleMetaChange"
-        >
-          <el-option
-            v-for="meta in metaRules"
-            :key="meta.code"
-            :label="`${meta.code}-${meta.name}`"
-            :value="meta.code"
-          />
-        </el-select>
-      </el-form-item>
-
-      <template v-if="currentMeta">
-        <el-divider />
-
-        <el-form-item label="规则名称">
-          <el-input
-            v-model="form.ruleName"
-            :disabled="readonly"
-            placeholder="便于识别该规则用途"
-            maxlength="40"
-            show-word-limit
-            style="width: 100%"
-          />
-        </el-form-item>
-
-        <el-form-item
-          v-for="field in currentMeta.fields"
-          :key="field.key"
-          :label="field.label"
-          :required="field.required"
-        >
-          <!-- 单选 -->
-          <el-select
-            v-if="field.type === 'select'"
-            v-model="form.config[field.key]"
-            :disabled="readonly"
-            placeholder="请选择"
-            clearable
-            style="width: 100%"
-          >
-            <el-option
-              v-for="opt in field.options"
-              :key="String(opt.value)"
-              :label="opt.label"
-              :value="opt.value"
-            />
-          </el-select>
-          <!-- 多选 -->
-          <el-select
-            v-else-if="field.type === 'multiSelect'"
-            v-model="form.config[field.key]"
-            :disabled="readonly"
-            multiple
-            collapse-tags
-            placeholder="请选择"
-            clearable
-            style="width: 100%"
-          >
-            <el-option
-              v-for="opt in field.options"
-              :key="String(opt.value)"
-              :label="opt.label"
-              :value="opt.value"
-            />
-          </el-select>
-          <!-- 日期 -->
-          <el-date-picker
-            v-else-if="field.type === 'datePicker'"
-            v-model="form.config[field.key]"
-            :disabled="readonly"
-            type="date"
-            placeholder="请选择日期"
-            value-format="yyyy-MM-dd"
-            style="width: 100%"
-          />
-          <!-- 月份 -->
-          <el-date-picker
-            v-else-if="field.type === 'monthPicker'"
-            v-model="form.config[field.key]"
-            :disabled="readonly"
-            type="month"
-            placeholder="请选择日期"
-            value-format="yyyy-MM"
-            style="width: 100%"
-          />
-          <!-- 数字 -->
-          <el-input
-            v-else-if="field.type === 'number'"
-            v-model.number="form.config[field.key]"
-            :disabled="readonly"
-            type="number"
-            :min="field.min"
-            :max="field.max"
-            placeholder="请输入"
-            style="width: 100%"
-          />
-          <!-- 连续达标月数 / 时间范围 -->
-          <div v-else-if="field.type === 'nm'" class="nm-input">
-            <el-input
-              v-model.number="form.config[field.key].n"
-              :disabled="readonly"
-              type="number"
-              :min="1"
-              style="width: 90px"
-            />
-            <span class="nm-sep">/</span>
-            <el-input
-              v-model.number="form.config[field.key].m"
-              :disabled="readonly"
-              type="number"
-              :min="1"
-              style="width: 90px"
-            />
-          </div>
-          <!-- 文本 -->
-          <el-input
-            v-else
-            v-model="form.config[field.key]"
-            :disabled="readonly"
-            placeholder="请输入"
-            style="width: 100%"
-          />
-        </el-form-item>
-      </template>
-    </el-form>
-
-    <div slot="footer" class="dialog-footer">
-      <el-button @click="handleCancel">取 消</el-button>
-      <el-button v-if="!readonly" type="primary" @click="handleSave">保 存</el-button>
-    </div>
-  </el-dialog>
-</template>
-
-<script>
-import { META_RULES, getMetaRule } from '@/config/metaRules'
-
-const emptyForm = () => ({
-  id: '',
-  metaCode: '',
-  ruleName: '',
-  config: {}
-})
-
-export default {
-  name: 'RuleEditDialog',
-  props: {
-    visible: { type: Boolean, default: false },
-    rule: { type: Object, default: null },
-    mode: { type: String, default: 'add' } // add | edit | view
-  },
-  data() {
-    return {
-      form: emptyForm(),
-      metaRules: META_RULES
-    }
-  },
-  computed: {
-    innerVisible: {
-      get() { return this.visible },
-      set(v) { this.$emit('update:visible', v) }
-    },
-    dialogTitle() {
-      if (this.mode === 'edit') return '编辑规则信息'
-      if (this.mode === 'view') return '查看规则信息'
-      return '新增规则信息'
-    },
-    readonly() {
-      return this.mode === 'view'
-    },
-    currentMeta() {
-      return this.form.metaCode ? getMetaRule(this.form.metaCode) : null
-    }
-  },
-  watch: {
-    visible(v) {
-      if (v) this.initForm()
-    }
-  },
-  methods: {
-    initForm() {
-      if (this.rule) {
-        this.form = {
-          id: this.rule.id,
-          metaCode: this.rule.metaCode,
-          ruleName: this.rule.ruleName || '',
-          config: JSON.parse(JSON.stringify(this.rule.config || {}))
-        }
-      } else {
-        this.form = emptyForm()
-      }
-    },
-    handleMetaChange(code) {
-      const meta = getMetaRule(code)
-      const config = {}
-      if (meta) {
-        meta.fields.forEach((f) => {
-          if (f.type === 'multiSelect') config[f.key] = []
-          else if (f.type === 'nm') config[f.key] = f.defaultValue ? { ...f.defaultValue } : { n: 3, m: 6 }
-          else config[f.key] = ''
-        })
-      }
-      this.form.config = config
-      if (!this.form.ruleName && meta) {
-        this.form.ruleName = meta.name
-      }
-    },
-    validateConfig() {
-      if (!this.form.metaCode) {
-        this.$message.error('请选择元标签规则')
-        return false
-      }
-      const meta = this.currentMeta
-      if (!meta) return false
-      for (const f of meta.fields) {
-        if (!f.required) continue
-        const v = this.form.config[f.key]
-        const empty = v === '' || v === null || v === undefined ||
-          (Array.isArray(v) && v.length === 0)
-        if (empty) {
-          this.$message.error(`请填写「${f.label}」`)
-          return false
-        }
-      }
-      return true
-    },
-    handleSave() {
-      if (!this.validateConfig()) return
-      this.$emit('save', {
-        id: this.form.id || `r_${Date.now()}_${Math.random().toString(36).slice(2, 6)}`,
-        metaCode: this.form.metaCode,
-        ruleName: this.form.ruleName || this.currentMeta.name,
-        config: JSON.parse(JSON.stringify(this.form.config))
-      })
-      this.innerVisible = false
-    },
-    handleCancel() {
-      this.innerVisible = false
-      this.$emit('cancel')
-    }
-  }
-}
-</script>
-
-<style scoped>
-.nm-input { display: flex; align-items: center; }
-.nm-sep { margin: 0 12px; color: #303133; font-size: 16px; }
-.dialog-footer { text-align: center; }
-</style>

+ 0 - 549
pc/src/components/tag/RuleBuilder.vue

@@ -1,549 +0,0 @@
-<template>
-  <div class="rule-builder">
-    <div class="toolbar">
-      <span class="rule-tip">规则组之间为 <b>且</b>;组内关系可切换 且/或</span>
-      <el-button
-        v-if="!readonly"
-        type="primary"
-        size="small"
-        icon="el-icon-plus"
-        plain
-        @click="addGroup"
-      >新增规则组</el-button>
-    </div>
-
-    <div
-      v-for="(group, gIndex) in groups"
-      :key="group.id"
-      class="rule-group"
-    >
-      <div class="group-header">
-        <div class="group-title">
-          <span class="group-no">规则组 {{ gIndex + 1 }}</span>
-          <span class="group-logic">
-            组内关系:
-            <el-radio-group
-              v-model="group.logic"
-              size="mini"
-              :disabled="readonly"
-            >
-              <el-radio-button label="AND">且</el-radio-button>
-              <el-radio-button label="OR">或</el-radio-button>
-            </el-radio-group>
-          </span>
-        </div>
-        <div v-if="!readonly" class="group-actions">
-          <el-button size="mini" icon="el-icon-document-copy" @click="copyGroup(gIndex)">复制</el-button>
-          <el-button
-            size="mini"
-            type="danger"
-            plain
-            icon="el-icon-delete"
-            :disabled="groups.length <= 1"
-            @click="removeGroup(gIndex)"
-          >删除</el-button>
-        </div>
-      </div>
-
-      <div class="rule-list">
-        <div
-          v-for="(rule, rIndex) in group.rules"
-          :key="rule.id"
-          class="rule-row"
-        >
-          <span class="rule-no">{{ rIndex + 1 }}</span>
-
-          <!-- 参数选择 -->
-          <el-select
-            v-model="rule.paramKey"
-            placeholder="选择参数"
-            filterable
-            size="small"
-            style="width: 240px"
-            :disabled="readonly"
-            @change="onParamChange(rule)"
-          >
-            <el-option-group
-              v-for="grp in paramGroups"
-              :key="grp.key"
-              :label="grp.label"
-            >
-              <el-option
-                v-for="p in grp.params"
-                :key="p.key"
-                :label="p.label"
-                :value="p.key"
-              />
-            </el-option-group>
-          </el-select>
-
-          <!-- 时间窗口(仅历史值参数显示) -->
-          <template v-if="showTimeWindow(rule)">
-            <el-select
-              v-model="rule.timeWindow.mode"
-              size="small"
-              style="width: 120px"
-              :disabled="readonly"
-              @change="onTimeModeChange(rule)"
-            >
-              <el-option
-                v-for="m in timeWindowModes"
-                :key="m.value"
-                :label="m.label"
-                :value="m.value"
-              />
-            </el-select>
-
-            <el-date-picker
-              v-if="rule.timeWindow.mode === 'specificMonth'"
-              v-model="rule.timeWindow.month"
-              type="month"
-              size="small"
-              style="width: 140px"
-              placeholder="选择月份"
-              value-format="yyyy-MM"
-              :disabled="readonly"
-            />
-
-            <template v-if="rule.timeWindow.mode === 'recentMonths'">
-              <el-input-number
-                v-model="rule.timeWindow.n"
-                :min="1"
-                :max="12"
-                size="small"
-                style="width: 110px"
-                :disabled="readonly"
-              />
-              <span class="inline-label">个月</span>
-              <el-select
-                v-model="rule.timeWindow.aggregator"
-                size="small"
-                style="width: 140px"
-                :disabled="readonly"
-              >
-                <el-option
-                  v-for="a in aggregatorsFor(rule)"
-                  :key="a.value"
-                  :label="a.label"
-                  :value="a.value"
-                />
-              </el-select>
-              <el-input-number
-                v-if="rule.timeWindow.aggregator === 'atLeast'"
-                v-model="rule.timeWindow.m"
-                :min="1"
-                :max="rule.timeWindow.n || 12"
-                size="small"
-                style="width: 90px"
-                :disabled="readonly"
-              />
-              <span v-if="rule.timeWindow.aggregator === 'atLeast'" class="inline-label">个月</span>
-            </template>
-          </template>
-
-          <!-- 运算符 -->
-          <el-select
-            v-model="rule.operator"
-            size="small"
-            style="width: 140px"
-            placeholder="运算符"
-            :disabled="!rule.paramKey || readonly"
-            @change="onOperatorChange(rule)"
-          >
-            <el-option
-              v-for="op in operatorsFor(rule)"
-              :key="op.value"
-              :label="op.label"
-              :value="op.value"
-            />
-          </el-select>
-
-          <!-- 比较值:根据 operator & param 动态切换 -->
-          <component
-            :is="valueComponentFor(rule).is"
-            v-if="needValue(rule)"
-            v-bind="valueComponentFor(rule).props"
-            v-model="rule.value"
-            :disabled="readonly"
-          >
-            <template v-if="valueComponentFor(rule).options">
-              <el-option
-                v-for="opt in valueComponentFor(rule).options"
-                :key="opt.value"
-                :label="opt.label"
-                :value="opt.value"
-              />
-            </template>
-          </component>
-
-          <!-- 区间 value2 -->
-          <template v-if="rule.operator === 'between' && needValue(rule)">
-            <span class="inline-label">~</span>
-            <component
-              :is="valueComponentFor(rule).is"
-              v-bind="valueComponentFor(rule).props"
-              v-model="rule.value2"
-              :disabled="readonly"
-            />
-          </template>
-
-          <!-- 相对日期:N 日/月/年内 -->
-          <template v-if="['withinLast', 'beforeLast'].includes(rule.operator)">
-            <el-select
-              v-model="rule.unit"
-              size="small"
-              style="width: 90px"
-              :disabled="readonly"
-            >
-              <el-option label="日" value="day" />
-              <el-option label="月" value="month" />
-              <el-option label="年" value="year" />
-            </el-select>
-          </template>
-
-          <!-- 删除规则按钮 -->
-          <el-button
-            v-if="!readonly"
-            class="rule-delete"
-            size="mini"
-            type="text"
-            icon="el-icon-close"
-            :disabled="group.rules.length <= 1"
-            @click="removeRule(gIndex, rIndex)"
-          />
-        </div>
-
-        <div v-if="!readonly" class="rule-add">
-          <el-button
-            size="small"
-            icon="el-icon-plus"
-            plain
-            @click="addRule(gIndex)"
-          >新增规则</el-button>
-        </div>
-      </div>
-    </div>
-  </div>
-</template>
-
-<script>
-import {
-  PARAM_GROUPS,
-  PARAM_MAP,
-  PARAM_TYPE,
-  TIME_WINDOW_MODES,
-  AGGREGATORS_ENUM,
-  AGGREGATORS_NUMBER,
-  BOOLEAN_OPTIONS,
-  getOperatorsByParam,
-  needTimeWindow,
-  needValueInput
-} from '@/config/tagParams'
-
-let uid = 0
-const nextId = () => `rb_${Date.now()}_${uid++}`
-
-function makeEmptyRule() {
-  return {
-    id: nextId(),
-    paramKey: '',
-    timeWindow: {
-      mode: 'current',
-      month: '',
-      n: 3,
-      m: 1,
-      aggregator: 'any'
-    },
-    operator: '',
-    value: '',
-    value2: '',
-    unit: 'day'
-  }
-}
-
-function makeEmptyGroup() {
-  return {
-    id: nextId(),
-    logic: 'AND',
-    rules: [makeEmptyRule()]
-  }
-}
-
-export default {
-  name: 'RuleBuilder',
-  props: {
-    value: {
-      type: Array,
-      default: () => []
-    },
-    readonly: {
-      type: Boolean,
-      default: false
-    }
-  },
-  data() {
-    return {
-      paramGroups: PARAM_GROUPS,
-      timeWindowModes: TIME_WINDOW_MODES,
-      groups: []
-    }
-  },
-  created() {
-    if (this.value && this.value.length) {
-      this.groups = this.cloneGroups(this.value)
-    } else {
-      this.groups = [makeEmptyGroup()]
-    }
-    // 初始化后把规范化后的 groups 同步回父组件一次,
-    // 避免父组件持有与子组件引用不一致的数组导致后续 v-model 回流。
-    this.$nextTick(() => {
-      this.$emit('input', this.groups)
-    })
-  },
-  watch: {
-    groups: {
-      deep: true,
-      handler(val) {
-        this.$emit('input', val)
-        this.$emit('change', val)
-      }
-    }
-  },
-  methods: {
-    cloneGroups(val) {
-      return val.map((g) => ({
-        id: g.id || nextId(),
-        logic: g.logic || 'AND',
-        rules: (g.rules || []).map((r) => ({
-          ...makeEmptyRule(),
-          ...r,
-          id: r.id || nextId(),
-          timeWindow: {
-            ...makeEmptyRule().timeWindow,
-            ...(r.timeWindow || {})
-          }
-        }))
-      }))
-    },
-    addGroup() {
-      this.groups.push(makeEmptyGroup())
-    },
-    removeGroup(index) {
-      if (this.groups.length <= 1) return
-      this.$confirm('确定删除该规则组?', '提示', { type: 'warning' })
-        .then(() => this.groups.splice(index, 1))
-        .catch(() => {})
-    },
-    copyGroup(index) {
-      const src = this.groups[index]
-      const copy = JSON.parse(JSON.stringify(src))
-      copy.id = nextId()
-      copy.rules = copy.rules.map((r) => ({ ...r, id: nextId() }))
-      this.groups.splice(index + 1, 0, copy)
-    },
-    addRule(gIndex) {
-      this.groups[gIndex].rules.push(makeEmptyRule())
-    },
-    removeRule(gIndex, rIndex) {
-      const group = this.groups[gIndex]
-      if (group.rules.length <= 1) return
-      group.rules.splice(rIndex, 1)
-    },
-    onParamChange(rule) {
-      rule.operator = ''
-      rule.value = ''
-      rule.value2 = ''
-      const param = PARAM_MAP[rule.paramKey]
-      if (!param || !param.hasHistory) {
-        rule.timeWindow = makeEmptyRule().timeWindow
-      }
-    },
-    onTimeModeChange(rule) {
-      if (rule.timeWindow.mode !== 'recentMonths') {
-        rule.timeWindow.aggregator = 'any'
-      }
-    },
-    onOperatorChange(rule) {
-      rule.value = ''
-      rule.value2 = ''
-    },
-    showTimeWindow(rule) {
-      return needTimeWindow(PARAM_MAP[rule.paramKey])
-    },
-    aggregatorsFor(rule) {
-      const param = PARAM_MAP[rule.paramKey]
-      if (!param) return AGGREGATORS_ENUM
-      if (param.type === PARAM_TYPE.NUMBER) return AGGREGATORS_NUMBER
-      if (param.type === PARAM_TYPE.LIST && param.listItemType === PARAM_TYPE.NUMBER) return AGGREGATORS_NUMBER
-      return AGGREGATORS_ENUM
-    },
-    operatorsFor(rule) {
-      return getOperatorsByParam(PARAM_MAP[rule.paramKey])
-    },
-    needValue(rule) {
-      if (!rule.operator) return false
-      return needValueInput(rule.operator)
-    },
-    valueComponentFor(rule) {
-      const param = PARAM_MAP[rule.paramKey]
-      if (!param) return { is: 'el-input', props: { size: 'small', style: 'width: 180px', placeholder: '比较值' } }
-
-      // 推荐关系
-      if (param.type === PARAM_TYPE.REFERRAL) {
-        if (['includesCustomerId', 'notIncludesCustomerId'].includes(rule.operator)) {
-          return {
-            is: 'el-input',
-            props: { size: 'small', style: 'width: 220px', placeholder: '客户号,多个英文逗号分隔' }
-          }
-        }
-        return {
-          is: 'el-input-number',
-          props: { size: 'small', style: 'width: 140px', min: 0, controlsPosition: 'right' }
-        }
-      }
-
-      // Boolean
-      if (param.type === PARAM_TYPE.BOOLEAN) {
-        return {
-          is: 'el-select',
-          props: { size: 'small', style: 'width: 120px', placeholder: '请选择' },
-          options: BOOLEAN_OPTIONS
-        }
-      }
-
-      // Date
-      if (param.type === PARAM_TYPE.DATE) {
-        if (['withinLast', 'beforeLast'].includes(rule.operator)) {
-          return {
-            is: 'el-input-number',
-            props: { size: 'small', style: 'width: 120px', min: 1, controlsPosition: 'right' }
-          }
-        }
-        const isMonth = param.dateFormat === 'YYYY-MM'
-        return {
-          is: 'el-date-picker',
-          props: {
-            type: isMonth ? 'month' : 'date',
-            size: 'small',
-            style: 'width: 160px',
-            placeholder: '选择日期',
-            valueFormat: isMonth ? 'yyyy-MM' : 'yyyy-MM-dd'
-          }
-        }
-      }
-
-      // 多选枚举
-      if (param.multiple && param.options) {
-        return {
-          is: 'el-select',
-          props: {
-            multiple: true,
-            collapseTags: true,
-            size: 'small',
-            style: 'width: 280px',
-            placeholder: '请选择'
-          },
-          options: param.options
-        }
-      }
-
-      // 枚举(单选 或 in/notIn 变多选)
-      if (param.options) {
-        if (['in', 'notIn'].includes(rule.operator)) {
-          return {
-            is: 'el-select',
-            props: {
-              multiple: true,
-              collapseTags: true,
-              size: 'small',
-              style: 'width: 260px',
-              placeholder: '请选择'
-            },
-            options: param.options
-          }
-        }
-        return {
-          is: 'el-select',
-          props: { size: 'small', style: 'width: 200px', placeholder: '请选择' },
-          options: param.options
-        }
-      }
-
-      // 数值
-      if (param.type === PARAM_TYPE.NUMBER || param.type === PARAM_TYPE.LIST) {
-        return {
-          is: 'el-input-number',
-          props: { size: 'small', style: 'width: 160px', controlsPosition: 'right' }
-        }
-      }
-
-      // 默认文本
-      return {
-        is: 'el-input',
-        props: { size: 'small', style: 'width: 200px', placeholder: '比较值' }
-      }
-    }
-  }
-}
-</script>
-
-<style scoped>
-.rule-builder { display: flex; flex-direction: column; gap: 16px; }
-
-.toolbar {
-  display: flex;
-  justify-content: space-between;
-  align-items: center;
-}
-.rule-tip { color: #909399; font-size: 13px; }
-.rule-tip b { color: #F56C6C; margin: 0 2px; }
-
-.rule-group {
-  border: 1px solid #ebeef5;
-  border-radius: 6px;
-  background: #fafbfc;
-}
-.group-header {
-  padding: 10px 16px;
-  display: flex;
-  justify-content: space-between;
-  align-items: center;
-  border-bottom: 1px dashed #ebeef5;
-  background: #f4f6f8;
-  border-radius: 6px 6px 0 0;
-}
-.group-title { display: flex; align-items: center; gap: 16px; }
-.group-no { font-weight: 600; color: #303133; }
-.group-logic { color: #606266; font-size: 13px; display: flex; align-items: center; gap: 8px; }
-.group-actions { display: flex; gap: 8px; }
-
-.rule-list { padding: 12px 16px; }
-
-.rule-row {
-  display: flex;
-  align-items: center;
-  flex-wrap: wrap;
-  gap: 8px;
-  padding: 8px 0;
-  border-bottom: 1px dashed #f0f2f5;
-}
-.rule-row:last-of-type { border-bottom: none; }
-
-.rule-no {
-  width: 24px;
-  height: 24px;
-  line-height: 24px;
-  text-align: center;
-  background: #ecf5ff;
-  color: #409EFF;
-  border-radius: 50%;
-  font-size: 12px;
-  flex-shrink: 0;
-}
-
-.inline-label { color: #606266; font-size: 13px; }
-
-.rule-delete { margin-left: auto; color: #F56C6C; }
-
-.rule-add { padding-top: 8px; }
-</style>

+ 0 - 200
pc/src/config/metaRules.js

@@ -1,200 +0,0 @@
-// 元规则(Meta Rule)字典
-// 每个元规则定义编号、名称、配置字段,并提供 buildDescription / buildParams
-// 用于在规则列表中自动渲染"规则描述"和"规则参数"
-
-// 规则组类型选项
-export const GROUP_TYPES = [
-  { value: 'debitCard', label: '借记卡圈客规则' },
-  { value: 'creditCard', label: '信用卡圈客规则' }
-]
-
-// 规则逻辑
-export const LOGIC_OPTIONS = [
-  { value: 'AND', label: '且(AND)' },
-  { value: 'OR', label: '或(OR)' }
-]
-
-// 状态
-export const STATUS_OPTIONS = [
-  { value: 'draft', label: '新建' },
-  { value: 'online', label: '已上线' }
-]
-
-// 常用比较运算符(用于 TRB / 余额 / 年龄等数值字段)
-const NUMERIC_OPS = [
-  { value: '>', label: '大于 >' },
-  { value: '<', label: '小于 <' },
-  { value: '=', label: '等于 =' },
-  { value: '>=', label: '大于等于 >=' },
-  { value: '<=', label: '小于等于 <=' },
-  { value: '!=', label: '不等于 !=' }
-]
-
-// 日期比较运算符
-const DATE_OPS = [
-  { value: '<', label: '在...前' },
-  { value: '>', label: '在...后' },
-  { value: '=', label: '在...当日' }
-]
-
-// 集合运算符
-const SET_OPS = [
-  { value: 'in', label: '属于' },
-  { value: 'not_in', label: '不属于' },
-  { value: '=', label: '等于' },
-  { value: '!=', label: '不等于' }
-]
-
-// =============================================
-// 工具函数
-// =============================================
-function formatMonthCN(yyyymm) {
-  if (!yyyymm) return ''
-  const [y, m] = yyyymm.split('-')
-  return `${y}年${parseInt(m, 10)}月`
-}
-function formatMonthCompact(yyyymm) {
-  return yyyymm ? yyyymm.replace('-', '') : ''
-}
-function formatDateSlash(yyyymmdd) {
-  if (!yyyymmdd) return ''
-  const [y, m, d] = yyyymmdd.split('-')
-  return `${y}/${parseInt(m, 10)}/${parseInt(d, 10)}`
-}
-function formatAmount(num) {
-  if (num === '' || num === null || num === undefined) return ''
-  const n = Number(num)
-  if (Number.isNaN(n)) return String(num)
-  if (n >= 10000) return `${n / 10000}万`
-  return String(n)
-}
-function dateOpSuffix(op) {
-  return { '<': '前', '>': '后', '=': '当日' }[op] || op || ''
-}
-
-// =============================================
-// 元规则列表
-// =============================================
-export const META_RULES = [
-  {
-    code: '001',
-    name: '开户时间(open_date)',
-    paramKey: 'open_date',
-    fields: [
-      {
-        key: 'accountType', label: '账户类型', type: 'select', required: true,
-        options: [
-          { value: 'I', label: 'I类账户' },
-          { value: 'II', label: 'II类账户' },
-          { value: 'III', label: 'III类账户' }
-        ]
-      },
-      { key: 'operator', label: '比较方式', type: 'select', required: true, options: DATE_OPS },
-      { key: 'date', label: '开户时间', type: 'datePicker', required: true }
-    ],
-    buildDescription(c) {
-      if (!c.accountType || !c.date) return ''
-      return `开户时间(${c.accountType}类账户)在${formatDateSlash(c.date)}${dateOpSuffix(c.operator)}`
-    },
-    buildParams(c) {
-      if (!c.operator || !c.date) return ''
-      return `{open_date,${c.operator},(${c.date})}`
-    }
-  },
-  {
-    code: '010',
-    name: 'MSC',
-    paramKey: 'MSC',
-    fields: [
-      { key: 'operator', label: 'MSC表达式', type: 'select', required: true, options: SET_OPS },
-      {
-        key: 'values', label: 'MSC值', type: 'multiSelect', required: true,
-        options: [
-          { value: 'premier', label: 'premier(卓越理财)' },
-          { value: 'advance', label: 'advance(运筹理财)' },
-          { value: 'excellence', label: 'excellence(卓越理财·尊尚)' },
-          { value: 'global', label: 'global(环球私人银行)' }
-        ]
-      }
-    ],
-    buildDescription(c) {
-      const vals = (c.values || []).join('/')
-      if (!vals) return ''
-      const opText = c.operator === 'not_in' || c.operator === '!=' ? '≠' : '='
-      return `MSC${opText}${vals}`
-    },
-    buildParams(c) {
-      const vals = (c.values || []).join('/')
-      if (!c.operator || !vals) return ''
-      return `{MSC,${c.operator},(${vals})}`
-    }
-  },
-  {
-    code: '050',
-    name: 'TRB值-Avg TRB口径(不含家庭账户余额)',
-    paramKey: 'Avg_TRB',
-    fields: [
-      { key: 'month', label: '开始月份', type: 'monthPicker', required: true },
-      { key: 'nm', label: '连续达标月数/时间范围(月)', type: 'nm', required: false, defaultValue: { n: 3, m: 6 } },
-      { key: 'operator', label: 'Avg TRB表达式', type: 'select', required: true, options: NUMERIC_OPS },
-      { key: 'amount', label: 'Avg TRB值', type: 'number', required: true, min: 0 }
-    ],
-    buildDescription(c) {
-      if (!c.month || !c.operator || c.amount === '' || c.amount == null) return ''
-      const n = c.nm && c.nm.n ? `(连续${c.nm.n}个月达标)` : ''
-      return `${formatMonthCN(c.month)}月末 TRB值-Avg TRB口径(不含家庭账户余额)${c.operator}${formatAmount(c.amount)}${n}`
-    },
-    buildParams(c) {
-      if (!c.month || !c.operator) return ''
-      const month = formatMonthCompact(c.month)
-      const nm = c.nm && c.nm.n ? `;{${c.nm.n}/${c.nm.m || ''}}` : ''
-      return `{month,=,${month}};{Avg_TRB,${c.operator},${c.amount}}${nm}`
-    }
-  },
-  {
-    code: '051',
-    name: '月末余额(End of day balance)',
-    paramKey: 'balance',
-    fields: [
-      { key: 'month', label: '开始月份', type: 'monthPicker', required: true },
-      { key: 'nm', label: '连续达标月数/时间范围(月)', type: 'nm', required: false, defaultValue: { n: 3, m: 6 } },
-      { key: 'operator', label: '余额表达式', type: 'select', required: true, options: NUMERIC_OPS },
-      { key: 'amount', label: '余额值', type: 'number', required: true, min: 0 }
-    ],
-    buildDescription(c) {
-      if (!c.month || !c.operator || c.amount === '' || c.amount == null) return ''
-      const n = c.nm && c.nm.n ? `(连续${c.nm.n}个月达标)` : ''
-      return `${formatMonthCN(c.month)}月末 End of day balance (所有资金含产品)${c.operator}${formatAmount(c.amount)}${n}`
-    },
-    buildParams(c) {
-      if (!c.month || !c.operator) return ''
-      const month = formatMonthCompact(c.month)
-      const nm = c.nm && c.nm.n ? `;{${c.nm.n}/${c.nm.m || ''}}` : ''
-      return `{month,=,${month}};{balance,${c.operator},${c.amount}}${nm}`
-    }
-  }
-]
-
-// 元规则下拉显示文本:编号-名称
-export function metaRuleLabel(meta) {
-  return `${meta.code}-${meta.name}`
-}
-
-// 根据 code 查询元规则
-export function getMetaRule(code) {
-  return META_RULES.find((m) => m.code === code)
-}
-
-// 计算一条规则的描述
-export function buildRuleDescription(metaCode, config) {
-  const meta = getMetaRule(metaCode)
-  if (!meta || !meta.buildDescription) return ''
-  return meta.buildDescription(config || {})
-}
-
-// 计算一条规则的参数
-export function buildRuleParams(metaCode, config) {
-  const meta = getMetaRule(metaCode)
-  if (!meta || !meta.buildParams) return ''
-  return meta.buildParams(config || {})
-}

+ 0 - 297
pc/src/config/tagParams.js

@@ -1,297 +0,0 @@
-// 标签参数清单 —— 与《数据标签需求》附录 A 对齐
-// 每个参数字段说明:
-//   key           参数名(英文)
-//   label         中文展示名
-//   type          String | BigDecimal | Boolean | Date | List | ReferralList
-//   hasHistory    是否存在"近 12 个月月末值"变体(List 变体)
-//   dateFormat    日期参数格式:YYYY-MM-DD | YYYY-MM(仅 Date 类型)
-//   options       枚举取值(String 枚举或多选枚举时使用)
-//   multiple      是否为多选枚举(多个枚举值以英文逗号分隔)
-//   listItemType  List 元素类型(仅 List 类型使用),用于决定聚合方式(数值可均值/极值,枚举仅计数)
-//   desc          补充说明
-
-export const PARAM_TYPE = {
-  STRING: 'String',
-  NUMBER: 'BigDecimal',
-  BOOLEAN: 'Boolean',
-  DATE: 'Date',
-  LIST: 'List',
-  REFERRAL: 'ReferralList'
-}
-
-// 运算符清单,按类型分组
-export const OPERATORS = {
-  // String 自由文本
-  STRING_TEXT: [
-    { value: 'eq', label: '等于' },
-    { value: 'neq', label: '不等于' },
-    { value: 'contains', label: '包含' },
-    { value: 'notContains', label: '不包含' },
-    { value: 'isNull', label: '为空' },
-    { value: 'isNotNull', label: '不为空' }
-  ],
-  // String 枚举
-  STRING_ENUM: [
-    { value: 'eq', label: '等于' },
-    { value: 'neq', label: '不等于' },
-    { value: 'in', label: '属于' },
-    { value: 'notIn', label: '不属于' },
-    { value: 'isNull', label: '为空' },
-    { value: 'isNotNull', label: '不为空' }
-  ],
-  // 数值
-  NUMBER: [
-    { value: 'eq', label: '=' },
-    { value: 'neq', label: '≠' },
-    { value: 'gt', label: '>' },
-    { value: 'gte', label: '≥' },
-    { value: 'lt', label: '<' },
-    { value: 'lte', label: '≤' },
-    { value: 'between', label: '区间' },
-    { value: 'isNull', label: '为空' },
-    { value: 'isNotNull', label: '不为空' }
-  ],
-  // 布尔
-  BOOLEAN: [
-    { value: 'eq', label: '=' },
-    { value: 'neq', label: '≠' }
-  ],
-  // 日期
-  DATE: [
-    { value: 'eq', label: '=' },
-    { value: 'neq', label: '≠' },
-    { value: 'gt', label: '晚于' },
-    { value: 'gte', label: '不早于' },
-    { value: 'lt', label: '早于' },
-    { value: 'lte', label: '不晚于' },
-    { value: 'between', label: '区间' },
-    { value: 'withinLast', label: '最近 N 日/月/年内' },
-    { value: 'beforeLast', label: '超过 N 日/月/年' },
-    { value: 'isNull', label: '为空' },
-    { value: 'isNotNull', label: '不为空' }
-  ]
-}
-
-// 时间窗口 - 取值方式
-export const TIME_WINDOW_MODES = [
-  { value: 'current', label: '当前值' },
-  { value: 'specificMonth', label: '指定某月' },
-  { value: 'recentMonths', label: '近 N 个月' }
-]
-
-// 时间窗口 - 聚合方式(仅取值方式=近 N 个月 时使用)
-export const AGGREGATORS_ENUM = [
-  { value: 'any', label: '任一月满足' },
-  { value: 'all', label: '所有月都满足' },
-  { value: 'atLeast', label: '至少 M 个月满足' }
-]
-
-export const AGGREGATORS_NUMBER = [
-  ...AGGREGATORS_ENUM,
-  { value: 'avg', label: '平均值' },
-  { value: 'max', label: '最大值' },
-  { value: 'min', label: '最小值' }
-]
-
-// 枚举字典
-export const ENUMS = {
-  privateBankTag: [
-    { value: '银辉', label: '银辉' },
-    { value: '金曜', label: '金曜' },
-    { value: '钻皓私人银行客户', label: '钻皓私人银行客户' }
-  ],
-  validRpq: [
-    { value: '0', label: '0 级' },
-    { value: '1', label: '1 级' },
-    { value: '2', label: '2 级' },
-    { value: '3', label: '3 级' },
-    { value: '4', label: '4 级' },
-    { value: '5', label: '5 级' },
-    { value: 'NA', label: 'NA' }
-  ],
-  assetCategoryDetails: [
-    { value: '1', label: '1 存款(定期/通知/个人大额存单)' },
-    { value: '2', label: '2 结构性存款(自有)' },
-    { value: '3', label: '3 基金相关产品' },
-    { value: '4', label: '4 理财产品(代销)' },
-    { value: '5', label: '5 代客境外理财海外债券型/结构性票据型(自有)' },
-    { value: '6', label: '6 资产管理计划(代销)' },
-    { value: '7', label: '7 信托计划(代销)' },
-    { value: '8', label: '8 保险产品(代销)' },
-    { value: '9', label: '9 预留' }
-  ],
-  fixedDepositTypes: [
-    { value: '1', label: '1 不持有定存' },
-    { value: '2', label: '2 持有普通定存(美金)' },
-    { value: '3', label: '3 持有普通定存(人民币)' },
-    { value: '4', label: '4 持有普通定存(其他币种)' },
-    { value: '5', label: '5 持有大额存单(人民币)' }
-  ]
-}
-
-// 参数分组及清单
-export const PARAM_GROUPS = [
-  {
-    key: 'basic',
-    label: '客户基础与身份',
-    params: [
-      { key: 'mscGroup', label: '客户 MSC Group', type: PARAM_TYPE.STRING, hasHistory: true },
-      { key: 'mscCode', label: '客户类型 MSC', type: PARAM_TYPE.STRING, hasHistory: true },
-      { key: 'currentMscEntryDate', label: 'Current MSC entry date', type: PARAM_TYPE.DATE, dateFormat: 'YYYY-MM-DD' },
-      { key: 'mob', label: '客户 MOB 值(含信用卡)', type: PARAM_TYPE.NUMBER, desc: '取值 0/1/2/3/4/5/6/...' },
-      { key: 'firstContactDate', label: 'First contact date', type: PARAM_TYPE.DATE, dateFormat: 'YYYY-MM-DD' },
-      { key: 'openDate', label: '开户时间', type: PARAM_TYPE.DATE, dateFormat: 'YYYY-MM-DD' },
-      { key: 'city', label: '城市', type: PARAM_TYPE.STRING },
-      { key: 'rmType', label: 'RM 类型', type: PARAM_TYPE.STRING },
-      { key: 'customerName', label: '客户姓名', type: PARAM_TYPE.STRING }
-    ]
-  },
-  {
-    key: 'asset',
-    label: '资产与持有',
-    params: [
-      { key: 'endOfBalance', label: 'End of day balance(所有资金含产品)', type: PARAM_TYPE.NUMBER, hasHistory: true },
-      { key: 'avgTrbIncludingHousehold', label: 'TRB 值-Avg TRB 口径(含家庭账户余额)', type: PARAM_TYPE.NUMBER, hasHistory: true },
-      { key: 'avgTrbExcludingHousehold', label: 'TRB 值-Avg TRB 口径(不含家庭账户余额)', type: PARAM_TYPE.NUMBER, hasHistory: true },
-      {
-        key: 'assetCategoryDetails',
-        label: '资产大类明细',
-        type: PARAM_TYPE.STRING,
-        multiple: true,
-        options: ENUMS.assetCategoryDetails
-      },
-      {
-        key: 'fixedDepositTypes',
-        label: '客户持有定存种类',
-        type: PARAM_TYPE.STRING,
-        multiple: true,
-        options: ENUMS.fixedDepositTypes
-      }
-    ]
-  },
-  {
-    key: 'premier',
-    label: '尊尚 / 私行',
-    params: [
-      { key: 'isQualifiedLast6Months', label: '近 6 个月是否符合尊尚资格', type: PARAM_TYPE.BOOLEAN },
-      { key: 'privateBankTag', label: '私行权益标签', type: PARAM_TYPE.STRING, options: ENUMS.privateBankTag },
-      { key: 'firstUpgradePremierDate', label: '首次升级尊尚的时间', type: PARAM_TYPE.DATE, dateFormat: 'YYYY-MM-DD' },
-      { key: 'privateBankId', label: '私行 ID 卓越理财尊尚编号', type: PARAM_TYPE.STRING },
-      { key: 'premierValidityDate', label: '尊尚资格到期日', type: PARAM_TYPE.DATE, dateFormat: 'YYYY-MM-DD' },
-      { key: 'firstAchievementDate', label: '首次达标日期', type: PARAM_TYPE.DATE, dateFormat: 'YYYY-MM' },
-      { key: 'firstPBUpgradeDate', label: '首次升级为 PB 的日期', type: PARAM_TYPE.DATE, dateFormat: 'YYYY-MM' },
-      { key: 'isPbActivityWhitelisted', label: 'PB 活动参与白名单', type: PARAM_TYPE.BOOLEAN }
-    ]
-  },
-  {
-    key: 'crossBorder',
-    label: '跨境资格(1+1)',
-    params: [
-      { key: 'isCrossBorderHK', label: '是否为 1+1 CN-HK 双边客户', type: PARAM_TYPE.BOOLEAN },
-      { key: 'isCrossBorderSG', label: '是否为 1+1 CN-SG 双边客户', type: PARAM_TYPE.BOOLEAN },
-      { key: 'isHKQualifier', label: '是否为 HK qualifier', type: PARAM_TYPE.BOOLEAN },
-      { key: 'cnHkQualifierDate', label: 'New to 1+1 CN-HK qualifier 时间', type: PARAM_TYPE.DATE, dateFormat: 'YYYY-MM' },
-      { key: 'isSGQualifier', label: '是否为 SG qualifier', type: PARAM_TYPE.BOOLEAN },
-      { key: 'cnSgQualifierDate', label: 'New to 1+1 CN-SG qualifier 时间', type: PARAM_TYPE.DATE, dateFormat: 'YYYY-MM' }
-    ]
-  },
-  {
-    key: 'mgm',
-    label: 'MGM / 推荐关系',
-    params: [
-      { key: 'monthlySuccessfulReferrals', label: '某自然月成功推荐人数', type: PARAM_TYPE.LIST, listItemType: PARAM_TYPE.NUMBER, hasHistory: true },
-      { key: 'yearlySuccessfulReferrals', label: '某自然年成功推荐人数', type: PARAM_TYPE.LIST, listItemType: PARAM_TYPE.NUMBER, hasHistory: true },
-      { key: 'isReferredAndQualified', label: '是否被成功推荐来开户且达标', type: PARAM_TYPE.BOOLEAN },
-      { key: 'mgmCaseId', label: 'MGM case ID', type: PARAM_TYPE.STRING },
-      { key: 'monthlyReferrals', label: '某自然月推荐人数', type: PARAM_TYPE.LIST, listItemType: PARAM_TYPE.NUMBER, hasHistory: true },
-      { key: 'yearlyReferrals', label: '某自然年推荐人数', type: PARAM_TYPE.LIST, listItemType: PARAM_TYPE.NUMBER, hasHistory: true },
-      { key: 'referralRelation', label: '推荐关系', type: PARAM_TYPE.REFERRAL, desc: '被推荐人客户号列表' }
-    ]
-  },
-  {
-    key: 'rpq',
-    label: '投资偏好',
-    params: [
-      { key: 'validRpq', label: '客户持有 valid RPQ 等级', type: PARAM_TYPE.STRING, options: ENUMS.validRpq },
-      { key: 'prospectsNeeds', label: 'Prospects Needs', type: PARAM_TYPE.BOOLEAN }
-    ]
-  },
-  {
-    key: 'behavior',
-    label: '行为与触点',
-    params: [
-      { key: 'isHsbcWechatLinked', label: '是否关注"汇丰中国客户服务"微信服务号且绑定银行账户', type: PARAM_TYPE.BOOLEAN },
-      { key: 'isPrevMonthInactiveCurMonthActive', label: '上个自然月未登录、当前自然月登录过手机银行 1 天或以上', type: PARAM_TYPE.BOOLEAN },
-      { key: 'isPrevTwoMonthsActive', label: '从当前自然月起前序连续 2 个完整月是否登录手机银行', type: PARAM_TYPE.BOOLEAN },
-      { key: 'isRmWeComBound', label: '是否绑定 RM 企业微信', type: PARAM_TYPE.BOOLEAN },
-      { key: 'firstBiometricLogin', label: '是否首次使用 Face ID 或指纹登录手机银行', type: PARAM_TYPE.BOOLEAN },
-      { key: 'isSimulatedAllocationCompleted', label: '成功完成模拟资产配置的客户', type: PARAM_TYPE.BOOLEAN }
-    ]
-  }
-]
-
-// 扁平参数字典:key -> param
-export const PARAM_MAP = (() => {
-  const map = {}
-  PARAM_GROUPS.forEach((group) => {
-    group.params.forEach((p) => {
-      map[p.key] = { ...p, groupKey: group.key, groupLabel: group.label }
-    })
-  })
-  return map
-})()
-
-// 根据参数推导可用运算符
-export function getOperatorsByParam(param) {
-  if (!param) return []
-  switch (param.type) {
-    case PARAM_TYPE.STRING:
-      return param.options ? OPERATORS.STRING_ENUM : OPERATORS.STRING_TEXT
-    case PARAM_TYPE.NUMBER:
-      return OPERATORS.NUMBER
-    case PARAM_TYPE.BOOLEAN:
-      return OPERATORS.BOOLEAN
-    case PARAM_TYPE.DATE:
-      return OPERATORS.DATE
-    case PARAM_TYPE.LIST:
-      // List 先聚合再判断,聚合后以数值运算符为主
-      return OPERATORS.NUMBER
-    case PARAM_TYPE.REFERRAL:
-      // 推荐关系:1. 列表长度(数值) 2. 是否包含指定客户号
-      return [
-        { value: 'lengthEq', label: '推荐人数 =' },
-        { value: 'lengthGte', label: '推荐人数 ≥' },
-        { value: 'lengthLte', label: '推荐人数 ≤' },
-        { value: 'includesCustomerId', label: '包含客户号' },
-        { value: 'notIncludesCustomerId', label: '不包含客户号' }
-      ]
-    default:
-      return []
-  }
-}
-
-// 是否需要展示时间窗口
-export function needTimeWindow(param) {
-  return !!(param && param.hasHistory)
-}
-
-// 是否需要填写比较值
-export function needValueInput(operator) {
-  return !['isNull', 'isNotNull'].includes(operator)
-}
-
-// 布尔值下拉选项
-export const BOOLEAN_OPTIONS = [
-  { value: true, label: '真' },
-  { value: false, label: '假' }
-]
-
-// 是否是区间运算符
-export function isBetweenOperator(operator) {
-  return operator === 'between'
-}
-
-// 是否是多值运算符
-export function isMultiValueOperator(operator) {
-  return ['in', 'notIn'].includes(operator)
-}

+ 0 - 18
pc/src/main.js

@@ -1,18 +0,0 @@
-import Vue from 'vue'
-import App from './App.vue'
-import ElementUI from 'element-ui'
-import 'element-ui/lib/theme-chalk/index.css'
-import VueQuillEditor from 'vue-quill-editor'
-import 'quill/dist/quill.core.css'
-import 'quill/dist/quill.snow.css'
-import router from './router'
-import './mock'
-
-Vue.use(ElementUI)
-Vue.use(VueQuillEditor)
-Vue.config.productionTip = false
-
-new Vue({
-  router,
-  render: h => h(App),
-}).$mount('#app')

+ 0 - 1026
pc/src/mock/index.js

@@ -1,1026 +0,0 @@
-import Mock from 'mockjs'
-
-// =============================================
-// 登录
-// =============================================
-Mock.mock(/\/api\/login/, 'post', (options) => {
-  const { username, password } = JSON.parse(options.body || '{}')
-  if (!username || !password) {
-    return { code: 400, data: null, msg: '账号或密码不能为空' }
-  }
-  if (username === 'admin' && password === '123456') {
-    return {
-      code: 200,
-      data: { token: 'mock-token-' + Date.now(), username },
-      msg: '登录成功'
-    }
-  }
-  return { code: 401, data: null, msg: '账号或密码错误' }
-})
-
-// =============================================
-// 页面管理 - 权益介绍页配置
-// =============================================
-const homeConfigData = {
-  premier: {
-    tabName: '卓越理财',
-    buttons: { unlocked: '进入权益专区', locked: '待解锁' },
-    header: {
-      imageUrl: 'https://images.unsplash.com/photo-1518893494013-481c1d8ed3fd?w=800',
-      mainTitle: '卓越理财',
-      subTitle: '为您和您的家人提供尊崇专属礼遇和权益'
-    },
-    privileges: [
-      { imageUrl: '', title: '国际教育', desc: '享专属国际化学校优惠及背景提升项目' },
-      { imageUrl: '', title: '酒店礼遇', desc: '高端酒店及商场会籍匹配' }
-    ]
-  },
-  elite: {
-    tabName: '卓越理财·尊尚',
-    buttons: { unlocked: '进入权益专区', locked: '待解锁' },
-    header: {
-      imageUrl: 'https://images.unsplash.com/photo-1518893494013-481c1d8ed3fd?w=800',
-      mainTitle: '卓越理财·尊尚',
-      subTitle: '为您和您的家人提供尊崇专属礼遇和权益'
-    },
-    privileges: [
-      { imageUrl: '', title: '国际教育', desc: '享专属国际化学校优惠及背景提升项目' },
-      { imageUrl: '', title: '酒店礼遇', desc: '高端酒店及商场会籍匹配' }
-    ]
-  },
-  global: {
-    tabName: '环球私人银行',
-    buttons: { unlocked: '进入权益专区', locked: '待解锁' },
-    header: {
-      imageUrl: 'https://images.unsplash.com/photo-1518893494013-481c1d8ed3fd?w=800',
-      mainTitle: '环球私人银行',
-      subTitle: '为您和您的家人提供尊崇专属礼遇和权益'
-    },
-    privileges: [
-      { imageUrl: '', title: '国际教育', desc: '享专属国际化学校优惠及背景提升项目' },
-      { imageUrl: '', title: '酒店礼遇', desc: '高端酒店及商场会籍匹配' }
-    ]
-  }
-}
-
-let homeStatus = 'published'
-
-Mock.mock(/\/api\/page-config\/home/, 'get', () => {
-  return { code: 200, data: { config: homeConfigData, status: homeStatus }, msg: 'ok' }
-})
-
-Mock.mock(/\/api\/page-config\/home/, 'put', (options) => {
-  const body = JSON.parse(options.body)
-  Object.assign(homeConfigData, body.config || {})
-  homeStatus = body.status || homeStatus
-  return { code: 200, data: null, msg: '保存成功' }
-})
-
-// =============================================
-// 页面管理 - 权益首页配置
-// =============================================
-const zoneConfigData = {
-  identityModule: {
-    advance: {
-      bgImage: 'https://images.unsplash.com/photo-1486406146926-c627a92ad1ab?w=800',
-      identityText: '汇丰运筹理财',
-      showQualifyStatus: false,
-      showAvgBalance: true,
-      avgBalanceRule: '月内日均余额 = 当月内每日账户余额之和 / 当月天数。'
-    },
-    premier: {
-      bgImage: 'https://images.unsplash.com/photo-1583847268964-b28dc8f51f92?w=800',
-      identityText: '汇丰卓越理财',
-      showQualifyStatus: true,
-      showAvgBalance: true,
-      avgBalanceRule: '计算规则:截止到昨日的账户月内日均余额。'
-    },
-    premierElite: {
-      bgImage: 'https://images.unsplash.com/photo-1557682250-33bd709cbe85?w=800',
-      identityText: '汇丰卓越理财·尊尚',
-      showQualifyStatus: true,
-      showAvgBalance: true,
-      avgBalanceRule: '计算规则:截止到昨日的账户月内日均余额。支持查看合格状态及复制编号。'
-    },
-    privateBank: {
-      bgImage: 'https://images.unsplash.com/photo-1492684223066-81342ee5ff30?w=800',
-      identityText: '汇丰环球私人银行',
-      showPbLevel: true
-    }
-  },
-  adCarousel: [{
-    imageUrl: 'https://images.unsplash.com/photo-1556742049-0cfed4f6a45d?w=800',
-    linkH5: '', linkApp: '', wechatAppId: '', wechatPath: '',
-    title: '汇丰卓越理财新户达标礼',
-    subtitle: '报名参加汇丰活动,体验更多精彩礼遇',
-    tags: ['t1', 't2']
-  }],
-  exchangeConfig: {
-    items: [
-      { imageUrl: 'https://images.unsplash.com/photo-1436491865332-7a61a109cc05?w=200', title: '航空里程', link: '/flight' },
-      { imageUrl: 'https://images.unsplash.com/photo-1518893494013-481c1d8ed3fd?w=200', title: '贵宾厅', link: '/lounge' }
-    ]
-  },
-  ruleModal: {
-    title: '积分规则',
-    content: '<p>本积分使用规则为汇丰银行(中国)有限公司...</p>'
-  }
-}
-
-let zoneStatus = 'published'
-
-const availableTags = Array.from({ length: 8 }, (_, i) => ({ id: `t${i + 1}`, name: `人群标签${i + 1}` }))
-
-Mock.mock(/\/api\/page-config\/zone/, 'get', () => {
-  return { code: 200, data: { config: zoneConfigData, status: zoneStatus, availableTags }, msg: 'ok' }
-})
-
-Mock.mock(/\/api\/page-config\/zone/, 'put', (options) => {
-  const body = JSON.parse(options.body)
-  Object.assign(zoneConfigData, body.config || {})
-  zoneStatus = body.status || zoneStatus
-  return { code: 200, data: null, msg: '保存成功' }
-})
-
-// =============================================
-// 页面管理 - 组装页
-// =============================================
-let assemblyIdCounter = 3000
-
-const assemblyList = [
-  {
-    id: 'A7538', name: '双十一迎新专属活动页', status: 'published',
-    createTime: '2025-10-01 10:00:00', updateTime: '2025-10-15 14:30:00',
-    blocks: [
-      { type: 'image', id: 'b_init1', data: { imageUrl: 'https://images.unsplash.com/photo-1557683316-973673baf926?w=800' } },
-      { type: 'privilege', id: 'b_init2', data: { title: '专属权益', items: [{ id: '1', title: '机场贵宾厅服务', desc: '全年免费使用6次指定机场贵宾厅' }, { id: '2', title: '境内礼宾车', desc: '免费2次接送' }] } },
-      { type: 'richtext', id: 'b_init3', data: { content: '<p>欢迎参与双十一专属福利活动...</p>' } },
-      { type: 'buttonGroup', id: 'b_init4', data: { buttons: [{ text: '立即参与', link: '/join', style: 'primary', actionType: 'link', dialog: { title: '', desc: '', confirmText: '确认', confirmLink: '' } }] } }
-    ]
-  },
-  {
-    id: 'A2091', name: '私行高净值沙龙引导页', status: 'draft',
-    createTime: '2025-11-20 09:15:00', updateTime: '2025-11-20 09:15:00',
-    blocks: [
-      { type: 'image', id: 'b_init5', data: { imageUrl: 'https://images.unsplash.com/photo-1486406146926-c627a92ad1ab?w=800' } },
-      { type: 'privilege', id: 'b_init6', data: { title: '尊享体验', items: [{ id: '1', title: '高尔夫权益', desc: 'VIP高尔夫俱乐部免会籍预约' }] } }
-    ]
-  }
-]
-
-Mock.mock(/\/api\/assembly\/list/, 'get', (options) => {
-  const url = new URL(options.url, 'http://localhost')
-  const id = url.searchParams.get('id') || ''
-  const name = url.searchParams.get('name') || ''
-  let filtered = assemblyList
-  if (id) filtered = filtered.filter(a => a.id.includes(id))
-  if (name) filtered = filtered.filter(a => a.name.includes(name))
-  return { code: 200, data: filtered, msg: 'ok' }
-})
-
-Mock.mock(/\/api\/assembly\/detail/, 'get', (options) => {
-  const url = new URL(options.url, 'http://localhost')
-  const id = url.searchParams.get('id')
-  const item = assemblyList.find(a => a.id === id)
-  return item
-    ? { code: 200, data: item, msg: 'ok' }
-    : { code: 404, data: null, msg: '未找到' }
-})
-
-Mock.mock(/\/api\/assembly\/save/, 'post', (options) => {
-  const body = JSON.parse(options.body)
-  const now = new Date().toLocaleString('zh-CN', { hour12: false })
-  if (body.id) {
-    const idx = assemblyList.findIndex(a => a.id === body.id)
-    if (idx >= 0) {
-      assemblyList[idx] = { ...assemblyList[idx], ...body, updateTime: now }
-    }
-  } else {
-    body.id = 'A' + (++assemblyIdCounter)
-    body.createTime = now
-    body.updateTime = now
-    assemblyList.unshift(body)
-  }
-  return { code: 200, data: { id: body.id }, msg: '保存成功' }
-})
-
-Mock.mock(/\/api\/assembly\/delete/, 'post', (options) => {
-  const body = JSON.parse(options.body)
-  const idx = assemblyList.findIndex(a => a.id === body.id)
-  if (idx >= 0) assemblyList.splice(idx, 1)
-  return { code: 200, data: null, msg: '删除成功' }
-})
-
-// =============================================
-// 渠道管理 - Channel
-// =============================================
-const channelDict = {
-  'Human-assisted': {
-    '汇丰中国客户服务号': ['汇丰中国客户服务号'],
-    'MGM': ['MGM - Mortgage', 'MGM - Premier'],
-    'CCSS': ['CCSS SMS'],
-    'Referral': ['GPB Referral', 'HK referral', 'Non WPB staff Referral', 'Pinnacle Referral', 'RBB Refferal'],
-    'New staff': ['New staff'],
-    'IBC': ['IBC referral staff code'],
-    'Group Acct': ['HTC', 'NIVIDA']
-  },
-  'Unassisted': {
-    '汇丰中国APP': ['Mobile X'],
-    '汇丰中国官网': ['PWS site'],
-    '汇丰中国小程序': ['汇丰中国小程序'],
-    '汇丰中国订阅号': ['汇丰中国订阅号', '汇丰中国订阅号推文', '推文尾部入口'],
-    'Chatbot': ['Chatbot'],
-    'SMS': ['SMS'],
-    'Paid Media': ['汇丰中国订阅号推文', 'others'],
-    'Others': ['others', 'River APP']
-  }
-}
-
-let channelIdCounter = 9
-const channelRows = [
-  { id: 'CH0001', type: 'Human-assisted', category: '汇丰中国客户服务号', name: '汇丰中国客户服务号', subName: '', owner: '', createTime: '2025-11-20 09:15:00', updateTime: '2025-11-20 09:15:00' },
-  { id: 'CH0002', type: 'Human-assisted', category: 'MGM', name: 'MGM - Mortgage', subName: '', owner: '', createTime: '2025-11-20 09:15:00', updateTime: '2025-11-20 09:15:00' },
-  { id: 'CH0003', type: 'Human-assisted', category: 'MGM', name: 'MGM - Premier', subName: '双十一特惠', owner: '张经理', createTime: '2025-11-20 09:15:00', updateTime: '2025-11-21 14:30:00' },
-  { id: 'CH0004', type: 'Human-assisted', category: 'CCSS', name: 'CCSS SMS', subName: '', owner: '', createTime: '2025-11-20 09:15:00', updateTime: '2025-11-20 09:15:00' },
-  { id: 'CH0005', type: 'Human-assisted', category: 'Referral', name: 'GPB Referral', subName: '', owner: '', createTime: '2025-11-20 09:15:00', updateTime: '2025-11-20 09:15:00' },
-  { id: 'CH0006', type: 'Unassisted', category: '汇丰中国APP', name: 'Mobile X', subName: '', owner: '', createTime: '2025-11-20 09:15:00', updateTime: '2025-11-20 09:15:00' },
-  { id: 'CH0007', type: 'Unassisted', category: '汇丰中国官网', name: 'PWS site', subName: '私行沙龙引导', owner: '', createTime: '2025-11-20 09:15:00', updateTime: '2025-11-20 09:15:00' },
-  { id: 'CH0008', type: 'Unassisted', category: '汇丰中国订阅号', name: '汇丰中国订阅号', subName: '', owner: '', createTime: '2025-11-20 09:15:00', updateTime: '2025-11-20 09:15:00' },
-  { id: 'CH0009', type: 'Unassisted', category: '汇丰中国订阅号', name: '推文尾部入口', subName: '', owner: '', createTime: '2025-11-20 09:15:00', updateTime: '2025-11-20 09:15:00' }
-]
-
-Mock.mock(/\/api\/channel\/dict/, 'get', () => {
-  return { code: 200, data: channelDict, msg: 'ok' }
-})
-
-Mock.mock(/\/api\/channel\/dict\/save/, 'post', (options) => {
-  const body = JSON.parse(options.body)
-  const { target, type, category, oldValue, newValue, mode } = body
-  if (target === 'type') {
-    if (mode === 'add' && channelDict[newValue]) return { code: 400, data: null, msg: 'Channel 类型已存在,不能重复新增' }
-    if (mode === 'edit' && oldValue !== newValue && channelDict[newValue]) return { code: 400, data: null, msg: 'Channel 类型已存在,不能修改为重复值' }
-    if (mode === 'add') { channelDict[newValue] = {} }
-    else { channelDict[newValue] = channelDict[oldValue]; delete channelDict[oldValue]; channelRows.forEach(r => { if (r.type === oldValue) r.type = newValue }) }
-  } else if (target === 'category') {
-    if (mode === 'add' && channelDict[type][newValue]) return { code: 400, data: null, msg: 'Channel 类别已存在,不能重复新增' }
-    if (mode === 'edit' && oldValue !== newValue && channelDict[type][newValue]) return { code: 400, data: null, msg: 'Channel 类别已存在,不能修改为重复值' }
-    if (mode === 'add') { channelDict[type][newValue] = [] }
-    else { channelDict[type][newValue] = channelDict[type][oldValue]; delete channelDict[type][oldValue]; channelRows.forEach(r => { if (r.type === type && r.category === oldValue) r.category = newValue }) }
-  } else if (target === 'name') {
-    const list = channelDict[type][category]
-    if (mode === 'add' && list.includes(newValue)) return { code: 400, data: null, msg: 'Channel 名称已存在,不能重复新增' }
-    if (mode === 'edit' && oldValue !== newValue && list.includes(newValue)) return { code: 400, data: null, msg: 'Channel 名称已存在,不能修改为重复值' }
-    if (mode === 'add') { list.push(newValue) }
-    else { const idx = list.indexOf(oldValue); if (idx >= 0) list[idx] = newValue; channelRows.forEach(r => { if (r.type === type && r.category === category && r.name === oldValue) r.name = newValue }) }
-  }
-  return { code: 200, data: channelDict, msg: '保存成功' }
-})
-
-Mock.mock(/\/api\/channel\/list/, 'get', (options) => {
-  const url = new URL(options.url, 'http://localhost')
-  const sId = (url.searchParams.get('id') || '').toLowerCase()
-  const sType = url.searchParams.get('type') || ''
-  const sCategory = (url.searchParams.get('category') || '').toLowerCase()
-  const sName = (url.searchParams.get('name') || '').toLowerCase()
-  const sSubName = (url.searchParams.get('subName') || '').toLowerCase()
-  const sOwner = (url.searchParams.get('owner') || '').toLowerCase()
-  let filtered = channelRows
-  if (sId) filtered = filtered.filter(c => c.id.toLowerCase().includes(sId))
-  if (sType) filtered = filtered.filter(c => c.type === sType)
-  if (sCategory) filtered = filtered.filter(c => c.category.toLowerCase().includes(sCategory))
-  if (sName) filtered = filtered.filter(c => c.name.toLowerCase().includes(sName))
-  if (sSubName) filtered = filtered.filter(c => c.subName.toLowerCase().includes(sSubName))
-  if (sOwner) filtered = filtered.filter(c => c.owner.toLowerCase().includes(sOwner))
-  return { code: 200, data: filtered, msg: 'ok' }
-})
-
-Mock.mock(/\/api\/channel\/save/, 'post', (options) => {
-  const body = JSON.parse(options.body)
-  const now = new Date().toLocaleString('zh-CN', { hour12: false })
-  if (body.id) {
-    const idx = channelRows.findIndex(c => c.id === body.id)
-    if (idx >= 0) channelRows[idx] = { ...channelRows[idx], ...body, updateTime: now }
-  } else {
-    body.id = 'CH' + String(++channelIdCounter).padStart(4, '0')
-    body.createTime = now
-    body.updateTime = now
-    channelRows.unshift(body)
-  }
-  return { code: 200, data: { id: body.id }, msg: '保存成功' }
-})
-
-Mock.mock(/\/api\/channel\/delete/, 'post', (options) => {
-  const body = JSON.parse(options.body)
-  const idx = channelRows.findIndex(c => c.id === body.id)
-  if (idx >= 0) channelRows.splice(idx, 1)
-  return { code: 200, data: null, msg: '删除成功' }
-})
-
-// =============================================
-// 渠道管理 - Campaign
-// =============================================
-let campaignIdCounter = 9002
-const campaignRows = [
-  { id: 'CP9001', name: 'Q3 大客户回馈', owner: 'Market Team', staff: '王专员', createTime: '2025-10-01 10:00:00', updateTime: '2025-10-15 14:30:00' },
-  { id: 'CP9002', name: '新春开户活动', owner: 'Retail Team', staff: '李专员', createTime: '2025-11-20 09:15:00', updateTime: '2025-11-20 09:15:00' }
-]
-
-Mock.mock(/\/api\/campaign\/list/, 'get', (options) => {
-  const url = new URL(options.url, 'http://localhost')
-  const sId = (url.searchParams.get('id') || '').toLowerCase()
-  const sName = (url.searchParams.get('name') || '').toLowerCase()
-  const sOwner = (url.searchParams.get('owner') || '').toLowerCase()
-  const sStaff = (url.searchParams.get('staff') || '').toLowerCase()
-  let filtered = campaignRows
-  if (sId) filtered = filtered.filter(c => c.id.toLowerCase().includes(sId))
-  if (sName) filtered = filtered.filter(c => c.name.toLowerCase().includes(sName))
-  if (sOwner) filtered = filtered.filter(c => c.owner.toLowerCase().includes(sOwner))
-  if (sStaff) filtered = filtered.filter(c => c.staff.toLowerCase().includes(sStaff))
-  return { code: 200, data: filtered, msg: 'ok' }
-})
-
-Mock.mock(/\/api\/campaign\/save/, 'post', (options) => {
-  const body = JSON.parse(options.body)
-  const now = new Date().toLocaleString('zh-CN', { hour12: false })
-  if (body.id) {
-    const idx = campaignRows.findIndex(c => c.id === body.id)
-    if (idx >= 0) campaignRows[idx] = { ...campaignRows[idx], ...body, updateTime: now }
-  } else {
-    body.id = 'CP' + (++campaignIdCounter)
-    body.createTime = now
-    body.updateTime = now
-    campaignRows.unshift(body)
-  }
-  return { code: 200, data: { id: body.id }, msg: '保存成功' }
-})
-
-Mock.mock(/\/api\/campaign\/delete/, 'post', (options) => {
-  const body = JSON.parse(options.body)
-  const idx = campaignRows.findIndex(c => c.id === body.id)
-  if (idx >= 0) campaignRows.splice(idx, 1)
-  return { code: 200, data: null, msg: '删除成功' }
-})
-
-// =============================================
-// 借记卡权益 + 审核中心
-// =============================================
-
-// 商品状态:未上架 / 已上架
-// 审核状态:草稿 / 上架待审核 / 下架待审核 / 审核通过
-// 合法组合(5 种):
-//   未上架+草稿        = 草稿
-//   未上架+上架待审核   = 新增待审核
-//   未上架+审核通过     = 已下架
-//   已上架+审核通过     = 已上架
-//   已上架+下架待审核   = 已上架·下架待审核
-const equityList = [
-  {
-    productId: 'Q100001',
-    equityName: '权益A',
-    source: '银行自有',
-    jumpMethod: '-',
-    jumpLink: '-',
-    exchangeMethod: '权益平台',
-    productType: '券码',
-    skuNo: 'SKU001',
-    merchantName: '商户A',
-    creator: '李运营',
-    createTime: '2026-04-10 09:32:15',
-    reviewer: '王五',
-    reviewTime: '2026-04-15 10:30:00',
-    shelfStatus: '已上架',
-    auditStatus: '审核通过',
-    showOnHome: '显示',
-    showOnAssembly: '不显示',
-    assemblyPages: []
-  },
-  {
-    productId: 'Q100002',
-    equityName: '权益B',
-    source: '第三方',
-    jumpMethod: '没有',
-    jumpLink: '太古地产',
-    exchangeMethod: '第三方(列表跳转)',
-    productType: '',
-    skuNo: 'SKU002',
-    merchantName: '商户B',
-    creator: '王运营',
-    createTime: '2026-04-12 14:08:42',
-    reviewer: '',
-    reviewTime: '',
-    shelfStatus: '未上架',
-    auditStatus: '上架待审核',
-    showOnHome: '显示',
-    showOnAssembly: '不显示',
-    assemblyPages: []
-  },
-  {
-    productId: 'Q100003',
-    equityName: '权益C',
-    source: '第三方',
-    jumpMethod: '有',
-    jumpLink: '龙腾',
-    exchangeMethod: '第三方(详情页跳转)',
-    productType: '',
-    skuNo: 'SKU003',
-    merchantName: '商户C',
-    creator: '李运营',
-    createTime: '2026-04-08 11:20:05',
-    reviewer: '王五',
-    reviewTime: '2026-04-14 16:45:00',
-    shelfStatus: '已上架',
-    auditStatus: '审核通过',
-    showOnHome: '显示',
-    showOnAssembly: '不显示',
-    assemblyPages: []
-  },
-  {
-    productId: 'Q100004',
-    equityName: '权益D',
-    source: '第三方',
-    jumpMethod: '有',
-    jumpLink: 'h5链接',
-    exchangeMethod: '第三方(详情页跳转)',
-    productType: '',
-    skuNo: 'SKU004',
-    merchantName: '商户D',
-    creator: '陈运营',
-    createTime: '2026-04-05 15:50:18',
-    reviewer: '赵六',
-    reviewTime: '2026-04-13 09:15:00',
-    shelfStatus: '已上架',
-    auditStatus: '下架待审核',
-    showOnHome: '显示',
-    showOnAssembly: '不显示',
-    assemblyPages: []
-  },
-  {
-    productId: 'Q100005',
-    equityName: '权益E',
-    source: '银行自有',
-    jumpMethod: '-',
-    jumpLink: '-',
-    exchangeMethod: '权益平台',
-    productType: '券码',
-    skuNo: 'SKU005',
-    merchantName: '商户E',
-    creator: '王运营',
-    createTime: '2026-03-28 09:00:11',
-    reviewer: '钱七',
-    reviewTime: '2026-04-12 11:20:00',
-    shelfStatus: '未上架',
-    auditStatus: '审核通过',
-    showOnHome: '显示',
-    showOnAssembly: '不显示',
-    assemblyPages: []
-  },
-  {
-    productId: 'Q100006',
-    equityName: '权益F(草稿)',
-    source: '银行自有',
-    jumpMethod: '-',
-    jumpLink: '-',
-    exchangeMethod: '权益平台',
-    productType: '券码',
-    skuNo: 'SKU006',
-    merchantName: '商户F',
-    creator: '李运营',
-    createTime: '2026-05-16 10:00:00',
-    reviewer: '',
-    reviewTime: '',
-    shelfStatus: '未上架',
-    auditStatus: '草稿',
-    showOnHome: '不显示',
-    showOnAssembly: '不显示',
-    assemblyPages: []
-  }
-]
-
-let equityIdCounter = 100006
-
-const auditList = [
-  {
-    id: 'AR202605150001',
-    auditType: 'new',
-    bizModule: 'equity_product',
-    bizModuleName: '借记卡权益',
-    bizId: 'Q100002',
-    bizName: '权益B',
-    snapshot: {
-      equityName: '权益B',
-      source: '第三方',
-      jumpMethod: '没有',
-      jumpLink: '太古地产',
-      productType: '',
-      skuNo: 'SKU002',
-      merchantName: '商户B',
-      subTitle: '外部列表页副标题'
-    },
-    submitReason: '新建第三方权益,链接已对齐',
-    submitterId: 'u001',
-    submitterName: '王运营',
-    submitTime: '2026-05-15 10:00:00',
-    auditorId: null,
-    auditorName: '',
-    auditTime: '',
-    auditStatus: 'pending',
-    auditComment: ''
-  },
-  {
-    id: 'AR202605170001',
-    auditType: 'offshelf',
-    bizModule: 'equity_product',
-    bizModuleName: '借记卡权益',
-    bizId: 'Q100004',
-    bizName: '权益D',
-    snapshot: {
-      equityName: '权益D',
-      source: '第三方',
-      jumpMethod: '有',
-      jumpLink: 'h5链接',
-      productType: '',
-      skuNo: 'SKU004',
-      merchantName: '商户D',
-      subTitle: '外部自定义链接副标题'
-    },
-    submitReason: '业务合作到期,申请下架',
-    submitterId: 'u003',
-    submitterName: '陈运营',
-    submitTime: '2026-05-17 09:00:00',
-    auditorId: null,
-    auditorName: '',
-    auditTime: '',
-    auditStatus: 'pending',
-    auditComment: ''
-  },
-  {
-    id: 'AR202605120001',
-    auditType: 'new',
-    bizModule: 'equity_product',
-    bizModuleName: '借记卡权益',
-    bizId: 'Q100001',
-    bizName: '权益A',
-    snapshot: {
-      equityName: '权益A',
-      source: '银行自有',
-      jumpMethod: '-',
-      jumpLink: '-',
-      productType: '券码',
-      skuNo: 'SKU001',
-      merchantName: '商户A',
-      subTitle: '副标题示例'
-    },
-    submitReason: '',
-    submitterId: 'u001',
-    submitterName: '李运营',
-    submitTime: '2026-04-10 09:32:15',
-    auditorId: 'u099',
-    auditorName: '王五',
-    auditTime: '2026-04-15 10:30:00',
-    auditStatus: 'approved',
-    auditComment: '内容合规,准予上架'
-  }
-]
-
-let auditIdCounter = 4
-
-function nowStr() {
-  return new Date().toLocaleString('zh-CN', { hour12: false }).replace(/\//g, '-')
-}
-
-function nextAuditId() {
-  auditIdCounter += 1
-  const d = new Date()
-  const ymd = d.getFullYear().toString() + String(d.getMonth() + 1).padStart(2, '0') + String(d.getDate()).padStart(2, '0')
-  return 'AR' + ymd + String(auditIdCounter).padStart(4, '0')
-}
-
-function snapshotOf(equity) {
-  return {
-    equityName: equity.equityName,
-    source: equity.source,
-    jumpMethod: equity.jumpMethod,
-    jumpLink: equity.jumpLink,
-    productType: equity.productType,
-    skuNo: equity.skuNo,
-    merchantName: equity.merchantName,
-    subTitle: equity.subTitle || ''
-  }
-}
-
-// 借记卡权益 - 列表
-Mock.mock(/\/api\/equity\/list/, 'get', (options) => {
-  const url = new URL(options.url, 'http://localhost')
-  const equityName = url.searchParams.get('equityName') || ''
-  const productId = url.searchParams.get('productId') || ''
-  const shelfStatus = url.searchParams.get('shelfStatus') || ''
-  const auditStatus = url.searchParams.get('auditStatus') || ''
-  let filtered = equityList
-  if (equityName) filtered = filtered.filter(e => e.equityName.includes(equityName))
-  if (productId) filtered = filtered.filter(e => e.productId.includes(productId))
-  if (shelfStatus) filtered = filtered.filter(e => e.shelfStatus === shelfStatus)
-  if (auditStatus) filtered = filtered.filter(e => e.auditStatus === auditStatus)
-  return { code: 200, data: filtered, msg: 'ok' }
-})
-
-// 借记卡权益 - 详情
-Mock.mock(/\/api\/equity\/detail/, 'get', (options) => {
-  const url = new URL(options.url, 'http://localhost')
-  const id = url.searchParams.get('id')
-  const item = equityList.find(e => e.productId === id)
-  return item
-    ? { code: 200, data: item, msg: 'ok' }
-    : { code: 404, data: null, msg: '权益不存在' }
-})
-
-// 保存草稿(新建或修改草稿)
-Mock.mock(/\/api\/equity\/save-draft/, 'post', (options) => {
-  const body = JSON.parse(options.body)
-  if (body.productId) {
-    // 修改草稿
-    const idx = equityList.findIndex(e => e.productId === body.productId)
-    if (idx < 0) return { code: 404, data: null, msg: '权益不存在' }
-    const cur = equityList[idx]
-    if (cur.auditStatus !== '草稿') {
-      return { code: 400, data: null, msg: '仅草稿状态可保存为草稿' }
-    }
-    equityList[idx] = { ...cur, ...body, shelfStatus: '未上架', auditStatus: '草稿' }
-    return { code: 200, data: { productId: cur.productId }, msg: '草稿已保存' }
-  }
-  // 新建草稿
-  equityIdCounter += 1
-  const newId = 'Q' + String(equityIdCounter)
-  const newRow = {
-    productId: newId,
-    equityName: body.equityName || '未命名权益',
-    source: body.source || '银行自有',
-    jumpMethod: body.jumpMethod || '-',
-    jumpLink: body.jumpLink || '-',
-    exchangeMethod: body.exchangeMethod || '权益平台',
-    productType: body.productType || '',
-    skuNo: body.skuNo || '',
-    merchantName: body.merchantName || '',
-    creator: body.creator || '李运营',
-    createTime: nowStr(),
-    reviewer: '',
-    reviewTime: '',
-    shelfStatus: '未上架',
-    auditStatus: '草稿',
-    showOnHome: body.showOnHome || '不显示',
-    showOnAssembly: body.showOnAssembly || '不显示',
-    assemblyPages: body.assemblyPages || [],
-    subTitle: body.subTitle || ''
-  }
-  equityList.unshift(newRow)
-  return { code: 200, data: { productId: newId }, msg: '草稿已保存' }
-})
-
-// 提交审核(新增上架审核 / 已上架编辑提审 / 已下架重新提审)
-Mock.mock(/\/api\/equity\/submit/, 'post', (options) => {
-  const body = JSON.parse(options.body)
-  let cur
-  let idx = -1
-  if (body.productId) {
-    idx = equityList.findIndex(e => e.productId === body.productId)
-    if (idx < 0) return { code: 404, data: null, msg: '权益不存在' }
-    cur = equityList[idx]
-  }
-
-  const submitReason = body.submitReason || ''
-  const submitter = body.submitterName || '李运营'
-
-  // 新建 + 直接提审
-  if (!cur) {
-    equityIdCounter += 1
-    const newId = 'Q' + String(equityIdCounter)
-    const newRow = {
-      productId: newId,
-      equityName: body.equityName || '未命名权益',
-      source: body.source || '银行自有',
-      jumpMethod: body.jumpMethod || '-',
-      jumpLink: body.jumpLink || '-',
-      exchangeMethod: body.exchangeMethod || '权益平台',
-      productType: body.productType || '',
-      skuNo: body.skuNo || '',
-      merchantName: body.merchantName || '',
-      creator: submitter,
-      createTime: nowStr(),
-      reviewer: '',
-      reviewTime: '',
-      shelfStatus: '未上架',
-      auditStatus: '上架待审核',
-      showOnHome: body.showOnHome || '不显示',
-      showOnAssembly: body.showOnAssembly || '不显示',
-      assemblyPages: body.assemblyPages || [],
-      subTitle: body.subTitle || ''
-    }
-    equityList.unshift(newRow)
-    auditList.unshift({
-      id: nextAuditId(),
-      auditType: 'new',
-      bizModule: 'equity_product',
-      bizModuleName: '借记卡权益',
-      bizId: newId,
-      bizName: newRow.equityName,
-      snapshot: snapshotOf(newRow),
-      submitReason,
-      submitterId: 'u001',
-      submitterName: submitter,
-      submitTime: nowStr(),
-      auditorId: null,
-      auditorName: '',
-      auditTime: '',
-      auditStatus: 'pending',
-      auditComment: ''
-    })
-    return { code: 200, data: { productId: newId }, msg: '已提交审核' }
-  }
-
-  // 草稿 → 上架待审核(new)
-  if (cur.shelfStatus === '未上架' && cur.auditStatus === '草稿') {
-    const merged = { ...cur, ...body }
-    equityList[idx] = { ...merged, shelfStatus: '未上架', auditStatus: '上架待审核' }
-    auditList.unshift({
-      id: nextAuditId(),
-      auditType: 'new',
-      bizModule: 'equity_product',
-      bizModuleName: '借记卡权益',
-      bizId: cur.productId,
-      bizName: equityList[idx].equityName,
-      snapshot: snapshotOf(equityList[idx]),
-      submitReason,
-      submitterId: 'u001',
-      submitterName: submitter,
-      submitTime: nowStr(),
-      auditorId: null,
-      auditorName: '',
-      auditTime: '',
-      auditStatus: 'pending',
-      auditComment: ''
-    })
-    return { code: 200, data: { productId: cur.productId }, msg: '已提交审核' }
-  }
-
-  // 已下架 → 重新上架(也是 new)
-  if (cur.shelfStatus === '未上架' && cur.auditStatus === '审核通过') {
-    equityList[idx] = { ...cur, ...body, shelfStatus: '未上架', auditStatus: '上架待审核' }
-    auditList.unshift({
-      id: nextAuditId(),
-      auditType: 'new',
-      bizModule: 'equity_product',
-      bizModuleName: '借记卡权益',
-      bizId: cur.productId,
-      bizName: equityList[idx].equityName,
-      snapshot: snapshotOf(equityList[idx]),
-      submitReason,
-      submitterId: 'u001',
-      submitterName: submitter,
-      submitTime: nowStr(),
-      auditorId: null,
-      auditorName: '',
-      auditTime: '',
-      auditStatus: 'pending',
-      auditComment: ''
-    })
-    return { code: 200, data: { productId: cur.productId }, msg: '已提交审核' }
-  }
-
-  // 已上架商品禁止直接编辑(需先走下架审核)
-
-  return { code: 400, data: null, msg: '当前状态不可提交审核' }
-})
-
-// 申请下架
-Mock.mock(/\/api\/equity\/request-offshelf/, 'post', (options) => {
-  const body = JSON.parse(options.body)
-  const idx = equityList.findIndex(e => e.productId === body.productId)
-  if (idx < 0) return { code: 404, data: null, msg: '权益不存在' }
-  const cur = equityList[idx]
-  if (cur.shelfStatus !== '已上架' || cur.auditStatus !== '审核通过') {
-    return { code: 400, data: null, msg: '仅已上架且无审核中的权益可申请下架' }
-  }
-  equityList[idx] = { ...cur, auditStatus: '下架待审核' }
-  auditList.unshift({
-    id: nextAuditId(),
-    auditType: 'offshelf',
-    bizModule: 'equity_product',
-    bizModuleName: '借记卡权益',
-    bizId: cur.productId,
-    bizName: cur.equityName,
-    snapshot: snapshotOf(cur),
-    submitReason: '',
-    submitterId: 'u001',
-    submitterName: body.submitterName || '李运营',
-    submitTime: nowStr(),
-    auditorId: null,
-    auditorName: '',
-    auditTime: '',
-    auditStatus: 'pending',
-    auditComment: ''
-  })
-  return { code: 200, data: null, msg: '已提交下架审核' }
-})
-
-// 删除权益(仅草稿/已下架)
-Mock.mock(/\/api\/equity\/delete/, 'post', (options) => {
-  const body = JSON.parse(options.body)
-  const idx = equityList.findIndex(e => e.productId === body.productId)
-  if (idx < 0) return { code: 404, data: null, msg: '权益不存在' }
-  const cur = equityList[idx]
-  const canDelete = (cur.shelfStatus === '未上架' && (cur.auditStatus === '草稿' || cur.auditStatus === '审核通过'))
-  if (!canDelete) return { code: 400, data: null, msg: '当前状态不可删除' }
-  equityList.splice(idx, 1)
-  return { code: 200, data: null, msg: '删除成功' }
-})
-
-// 审核中心 - 列表
-Mock.mock(/\/api\/audit\/list/, 'get', (options) => {
-  const url = new URL(options.url, 'http://localhost')
-  const auditStatus = url.searchParams.get('auditStatus') || ''
-  const bizId = url.searchParams.get('bizId') || ''
-  let filtered = auditList
-  if (auditStatus) filtered = filtered.filter(a => a.auditStatus === auditStatus)
-  if (bizId) filtered = filtered.filter(a => String(a.bizId).includes(bizId))
-  return { code: 200, data: filtered, msg: 'ok' }
-})
-
-// 审核中心 - 详情
-Mock.mock(/\/api\/audit\/detail/, 'get', (options) => {
-  const url = new URL(options.url, 'http://localhost')
-  const id = url.searchParams.get('id')
-  const item = auditList.find(a => a.id === id)
-  return item
-    ? { code: 200, data: item, msg: 'ok' }
-    : { code: 404, data: null, msg: '审核单不存在' }
-})
-
-// 审核中心 - 通过
-Mock.mock(/\/api\/audit\/approve/, 'post', (options) => {
-  const body = JSON.parse(options.body)
-  const idx = auditList.findIndex(a => a.id === body.id)
-  if (idx < 0) return { code: 404, data: null, msg: '审核单不存在' }
-  const audit = auditList[idx]
-  if (audit.auditStatus !== 'pending') return { code: 400, data: null, msg: '该审核单已完成' }
-
-  const eqIdx = equityList.findIndex(e => e.productId === audit.bizId)
-  if (eqIdx < 0) return { code: 404, data: null, msg: '关联权益不存在' }
-  const eq = equityList[eqIdx]
-  const now = nowStr()
-  const auditor = body.auditorName || '王五'
-
-  if (audit.auditType === 'new') {
-    equityList[eqIdx] = { ...eq, shelfStatus: '已上架', auditStatus: '审核通过', reviewer: auditor, reviewTime: now }
-  } else if (audit.auditType === 'offshelf') {
-    equityList[eqIdx] = { ...eq, shelfStatus: '未上架', auditStatus: '审核通过', reviewer: auditor, reviewTime: now }
-  }
-
-  auditList[idx] = { ...audit, auditStatus: 'approved', auditorId: 'u099', auditorName: auditor, auditTime: now, auditComment: body.comment || '' }
-  return { code: 200, data: null, msg: '审核通过' }
-})
-
-// 审核中心 - 驳回
-Mock.mock(/\/api\/audit\/reject/, 'post', (options) => {
-  const body = JSON.parse(options.body)
-  const idx = auditList.findIndex(a => a.id === body.id)
-  if (idx < 0) return { code: 404, data: null, msg: '审核单不存在' }
-  const audit = auditList[idx]
-  if (audit.auditStatus !== 'pending') return { code: 400, data: null, msg: '该审核单已完成' }
-
-  const eqIdx = equityList.findIndex(e => e.productId === audit.bizId)
-  if (eqIdx < 0) return { code: 404, data: null, msg: '关联权益不存在' }
-  const eq = equityList[eqIdx]
-  const now = nowStr()
-  const auditor = body.auditorName || '王五'
-
-  // 驳回后权益主表回退到提审前的状态
-  if (audit.auditType === 'new') {
-    equityList[eqIdx] = { ...eq, shelfStatus: '未上架', auditStatus: '草稿' }
-  } else if (audit.auditType === 'offshelf') {
-    equityList[eqIdx] = { ...eq, shelfStatus: '已上架', auditStatus: '审核通过' }
-  }
-
-  auditList[idx] = { ...audit, auditStatus: 'rejected', auditorId: 'u099', auditorName: auditor, auditTime: now, auditComment: body.comment || '' }
-  return { code: 200, data: null, msg: '已驳回' }
-})
-
-// =============================================
-// 发放计划
-// =============================================
-const equityGrantCampaignList = [
-  {
-    id: 'P000002',
-    name: '夏日畅享权益月',
-    startTime: '2026-05-01 00:00:00',
-    endTime: '2026-08-31 23:59:59',
-    crowdList: [],
-    grantMode: 'claim',
-    equityList: ['Q100001', 'Q100004', 'Q100005'],
-    grantCycle: 'total',
-    grantQuantity: 1,
-    validity: { type: 'relative', fixedRange: [], relativeDays: 30 },
-    createTime: '2026-04-20 14:10:00',
-    updateTime: '2026-04-20 14:10:00'
-  },
-  {
-    id: 'P000001',
-    name: '春季会员尊享礼遇',
-    startTime: '2026-04-01 00:00:00',
-    endTime: '2026-06-30 23:59:59',
-    crowdList: ['A0000120251224'],
-    grantMode: 'direct',
-    equityList: ['Q100001'],
-    grantCycle: 'monthly',
-    grantQuantity: 2,
-    validity: { type: 'fixed', fixedRange: ['2026-04-01', '2026-06-30'], relativeDays: 30 },
-    createTime: '2026-03-25 10:20:30',
-    updateTime: '2026-03-25 10:20:30'
-  }
-]
-let equityGrantCampaignCounter = equityGrantCampaignList.length
-
-function nextGrantCampaignId() {
-  equityGrantCampaignCounter += 1
-  return 'P' + String(equityGrantCampaignCounter).padStart(6, '0')
-}
-
-Mock.mock(/\/api\/equity-grant-campaign\/list/, 'get', (options) => {
-  const url = new URL(options.url, 'http://localhost')
-  const name = url.searchParams.get('name') || ''
-  const startDate = url.searchParams.get('startDate') || ''
-  const endDate = url.searchParams.get('endDate') || ''
-  let filtered = equityGrantCampaignList
-  if (name) filtered = filtered.filter(c => c.name.includes(name))
-  if (startDate) filtered = filtered.filter(c => c.endTime.slice(0, 10) >= startDate)
-  if (endDate) filtered = filtered.filter(c => c.startTime.slice(0, 10) <= endDate)
-  return { code: 200, data: filtered, msg: 'ok' }
-})
-
-Mock.mock(/\/api\/equity-grant-campaign\/save/, 'post', (options) => {
-  const body = JSON.parse(options.body)
-  const now = nowStr()
-  const defaultValidity = { type: 'fixed', fixedRange: [], relativeDays: 30 }
-  if (body.id) {
-    const idx = equityGrantCampaignList.findIndex(c => c.id === body.id)
-    if (idx < 0) return { code: 404, data: null, msg: '发放计划不存在' }
-    equityGrantCampaignList[idx] = {
-      ...equityGrantCampaignList[idx],
-      name: body.name,
-      startTime: body.startTime,
-      endTime: body.endTime,
-      crowdList: body.crowdList || [],
-      equityList: body.equityList || [],
-      grantCycle: body.grantCycle || 'total',
-      grantQuantity: body.grantQuantity || 1,
-      validity: body.validity || defaultValidity,
-      updateTime: now
-    }
-    return { code: 200, data: { id: body.id }, msg: '保存成功' }
-  }
-  const newId = nextGrantCampaignId()
-  equityGrantCampaignList.unshift({
-    id: newId,
-    name: body.name,
-    startTime: body.startTime,
-    endTime: body.endTime,
-    crowdList: body.crowdList || [],
-    equityList: body.equityList || [],
-    grantCycle: body.grantCycle || 'total',
-    grantQuantity: body.grantQuantity || 1,
-    validity: body.validity || defaultValidity,
-    createTime: now,
-    updateTime: now
-  })
-  return { code: 200, data: { id: newId }, msg: '新建成功' }
-})
-
-Mock.mock(/\/api\/equity-grant-campaign\/delete/, 'post', (options) => {
-  const body = JSON.parse(options.body)
-  const idx = equityGrantCampaignList.findIndex(c => c.id === body.id)
-  if (idx < 0) return { code: 404, data: null, msg: '发放计划不存在' }
-  equityGrantCampaignList.splice(idx, 1)
-  return { code: 200, data: null, msg: '删除成功' }
-})
-
-Mock.mock(/\/api\/equity-grant-campaign\/detail/, 'get', (options) => {
-  const url = new URL(options.url, 'http://localhost')
-  const id = url.searchParams.get('id')
-  const item = equityGrantCampaignList.find(c => c.id === id)
-  return item
-    ? { code: 200, data: item, msg: 'ok' }
-    : { code: 404, data: null, msg: '发放计划不存在' }
-})
-
-Mock.mock(/\/api\/equity-grant-campaign\/end/, 'post', (options) => {
-  const body = JSON.parse(options.body)
-  const idx = equityGrantCampaignList.findIndex(c => c.id === body.id)
-  if (idx < 0) return { code: 404, data: null, msg: '发放计划不存在' }
-  const now = nowStr()
-  equityGrantCampaignList[idx] = {
-    ...equityGrantCampaignList[idx],
-    endTime: now,
-    updateTime: now
-  }
-  return { code: 200, data: null, msg: '已结束' }
-})
-
-export default Mock

+ 0 - 273
pc/src/router/index.js

@@ -1,273 +0,0 @@
-import Vue from 'vue'
-import VueRouter from 'vue-router'
-import Layout from '@/components/Layout.vue'
-
-Vue.use(VueRouter)
-
-const routes = [
-  {
-    path: '/login',
-    name: 'Login',
-    component: () => import('@/views/Login.vue'),
-    meta: { public: true }
-  },
-  {
-    path: '/',
-    component: Layout,
-    redirect: '/customization/hsbc/equity-products',
-    children: [
-      {
-        path: 'customization/hsbc/pages',
-        redirect: 'customization/hsbc/pages/home',
-        meta: { title: '页面管理' }
-      },
-      {
-        path: 'customization/hsbc/pages/home',
-        name: 'PageHomeConfig',
-        component: () => import('@/views/PageHomeConfig.vue'),
-        meta: { title: '权益介绍页' }
-      },
-      {
-        path: 'customization/hsbc/pages/zone',
-        name: 'PageZoneConfig',
-        component: () => import('@/views/PageZoneConfig.vue'),
-        meta: { title: '权益首页' }
-      },
-      {
-        path: 'customization/hsbc/pages/assembly',
-        name: 'PageAssembly',
-        component: () => import('@/views/PageAssembly.vue'),
-        meta: { title: '组装页' }
-      },
-      {
-        path: 'customization/hsbc/equity-products',
-        name: 'EquityProducts',
-        component: () => import('@/views/EquityProducts.vue'),
-        meta: { title: '借记卡权益', icon: 'el-icon-goods' }
-      },
-      {
-        path: 'customization/hsbc/credit-card-equity',
-        name: 'CreditCardEquity',
-        component: () => import('@/views/CreditCardEquity.vue'),
-        meta: { title: '信用卡权益', icon: 'el-icon-bank-card' }
-      },
-      {
-        path: 'customization/hsbc/crowd-tasks',
-        name: 'CrowdTaskList',
-        component: () => import('@/views/CrowdTaskList.vue'),
-        meta: { title: '客群任务管理', icon: 'el-icon-user' }
-      },
-      {
-        path: 'customization/hsbc/crowd-tasks/add',
-        name: 'CrowdTaskAdd',
-        component: () => import('@/views/CrowdTaskEdit.vue'),
-        meta: { title: '新建客群任务' }
-      },
-      {
-        path: 'customization/hsbc/crowd-tasks/edit/:id',
-        name: 'CrowdTaskEdit',
-        component: () => import('@/views/CrowdTaskEdit.vue'),
-        meta: { title: '编辑客群任务' }
-      },
-      {
-        path: 'customization/hsbc/crowd-tasks/copy/:id',
-        name: 'CrowdTaskCopy',
-        component: () => import('@/views/CrowdTaskEdit.vue'),
-        meta: { title: '复制客群任务' }
-      },
-      {
-        path: 'customization/hsbc/crowd-tasks/view/:id',
-        name: 'CrowdTaskView',
-        component: () => import('@/views/CrowdTaskView.vue'),
-        meta: { title: '客群任务详情' }
-      },
-      {
-        path: 'customization/hsbc/crowd-name-lists/add',
-        name: 'CrowdNameListAdd',
-        component: () => import('@/views/CrowdNameListEdit.vue'),
-        meta: { title: '新增指定客户号客群' }
-      },
-      {
-        path: 'customization/hsbc/crowd-name-lists/edit/:id',
-        name: 'CrowdNameListEdit',
-        component: () => import('@/views/CrowdNameListEdit.vue'),
-        meta: { title: '编辑指定客户号客群' }
-      },
-      {
-        path: 'customization/hsbc/rule-groups',
-        name: 'RuleGroupList',
-        component: () => import('@/views/RuleGroupList.vue'),
-        meta: { title: '规则组管理', icon: 'el-icon-set-up' }
-      },
-      {
-        path: 'customization/hsbc/rule-groups/add',
-        name: 'RuleGroupAdd',
-        component: () => import('@/views/RuleGroupEdit.vue'),
-        meta: { title: '新增规则组' }
-      },
-      {
-        path: 'customization/hsbc/rule-groups/edit/:id',
-        name: 'RuleGroupEdit',
-        component: () => import('@/views/RuleGroupEdit.vue'),
-        meta: { title: '编辑规则组' }
-      },
-      {
-        path: 'customization/hsbc/rule-groups/view/:id',
-        name: 'RuleGroupView',
-        component: () => import('@/views/RuleGroupView.vue'),
-        meta: { title: '规则组详情' }
-      },
-      {
-        path: 'customization/hsbc/equity-products/add',
-        name: 'AddEquityProduct',
-        component: () => import('@/views/AddEquityProduct.vue'),
-        meta: { title: '添加权益', icon: 'el-icon-edit' }
-      },
-      {
-        path: 'customization/hsbc/equity-products/view/:id',
-        name: 'ViewEquityProduct',
-        component: () => import('@/views/ViewEquityProduct.vue'),
-        meta: { title: '查看权益', icon: 'el-icon-view' }
-      },
-      {
-        path: 'customization/hsbc/equity-products/edit/:id',
-        name: 'EditEquityProduct',
-        component: () => import('@/views/EditEquityProduct.vue'),
-        meta: { title: '编辑权益', icon: 'el-icon-edit' }
-      },
-      {
-        path: 'customization/hsbc/equity-products/audit/:id',
-        name: 'AuditEquityProduct',
-        component: () => import('@/views/AuditEquityProduct.vue'),
-        meta: { title: '审核权益', icon: 'el-icon-check' }
-      },
-      {
-        path: 'customization/hsbc/equity-products/coupons/:id',
-        name: 'EquityProductCoupons',
-        component: () => import('@/views/EquityProductCoupons.vue'),
-        meta: { title: '券码管理', icon: 'el-icon-files' }
-      },
-      {
-        path: 'customization/hsbc/tags',
-        name: 'TagList',
-        component: () => import('@/views/TagList.vue'),
-        meta: { title: '标签管理', icon: 'el-icon-collection-tag' }
-      },
-      {
-        path: 'customization/hsbc/tags/add',
-        name: 'TagAdd',
-        component: () => import('@/views/TagEdit.vue'),
-        meta: { title: '新建标签', icon: 'el-icon-plus' }
-      },
-      {
-        path: 'customization/hsbc/tags/edit/:id',
-        name: 'TagEdit',
-        component: () => import('@/views/TagEdit.vue'),
-        meta: { title: '编辑标签', icon: 'el-icon-edit' }
-      },
-      {
-        path: 'customization/hsbc/tags/view/:id',
-        name: 'TagView',
-        component: () => import('@/views/TagView.vue'),
-        meta: { title: '查看标签', icon: 'el-icon-view' }
-      },
-      {
-        path: 'customization/hsbc/sms-templates',
-        name: 'SmsTemplateList',
-        component: () => import('@/views/SmsTemplateList.vue'),
-        meta: { title: '短信模板管理', icon: 'el-icon-message' }
-      },
-      {
-        path: 'customization/hsbc/sms-send-records',
-        name: 'SmsSendRecord',
-        component: () => import('@/views/SmsSendRecord.vue'),
-        meta: { title: '发送记录查询', icon: 'el-icon-document-copy' }
-      },
-      {
-        path: 'customization/hsbc/equity-grant',
-        name: 'CustomerEquityGrant',
-        component: () => import('@/views/CustomerEquityGrant.vue'),
-        meta: { title: '客户权益发放', icon: 'el-icon-s-data' }
-      },
-      {
-        path: 'customization/hsbc/cash-discount-grant',
-        name: 'CashDiscountGrant',
-        component: () => import('@/views/CashDiscountGrant.vue'),
-        meta: { title: '立减金发放明细', icon: 'el-icon-s-finance' }
-      },
-      {
-        path: 'customization/hsbc/points-detail',
-        name: 'PointsDetail',
-        component: () => import('@/views/PointsDetail.vue'),
-        meta: { title: '积分明细查询', icon: 'el-icon-s-order' }
-      },
-      {
-        path: 'customization/hsbc/channel',
-        name: 'ChannelManagement',
-        component: () => import('@/views/ChannelManagement.vue'),
-        meta: { title: 'Channel管理' }
-      },
-      {
-        path: 'customization/hsbc/campaign',
-        name: 'CampaignManagement',
-        component: () => import('@/views/CampaignManagement.vue'),
-        meta: { title: 'Campaign管理' }
-      },
-      {
-        path: 'customization/hsbc/audit',
-        name: 'AuditList',
-        component: () => import('@/views/AuditList.vue'),
-        meta: { title: '权益审核', icon: 'el-icon-finished' }
-      },
-      {
-        path: 'customization/hsbc/audit/:id',
-        name: 'AuditDetail',
-        component: () => import('@/views/AuditDetail.vue'),
-        meta: { title: '审核详情' }
-      },
-      {
-        path: 'customization/hsbc/equity-grant-campaign',
-        name: 'EquityGrantCampaign',
-        component: () => import('@/views/EquityGrantCampaign.vue'),
-        meta: { title: '发放计划', icon: 'el-icon-present' }
-      },
-      {
-        path: 'customization/hsbc/equity-grant-campaign/add',
-        name: 'EquityGrantCampaignAdd',
-        component: () => import('@/views/EquityGrantCampaignEdit.vue'),
-        meta: { title: '新建发放计划' }
-      },
-      {
-        path: 'customization/hsbc/equity-grant-campaign/edit/:id',
-        name: 'EquityGrantCampaignEdit',
-        component: () => import('@/views/EquityGrantCampaignEdit.vue'),
-        meta: { title: '编辑发放计划' }
-      },
-      {
-        path: 'customization/hsbc/equity-grant-campaign/view/:id',
-        name: 'EquityGrantCampaignView',
-        component: () => import('@/views/EquityGrantCampaignView.vue'),
-        meta: { title: '查看发放计划' }
-      }
-    ]
-  },
-  { path: '*', redirect: '/' }
-]
-
-const router = new VueRouter({
-  routes
-})
-
-router.beforeEach((to, from, next) => {
-  const token = localStorage.getItem('token')
-  if (to.meta && to.meta.public) {
-    if (token && to.path === '/login') return next('/')
-    return next()
-  }
-  if (!token) {
-    return next({ path: '/login', query: { redirect: to.fullPath } })
-  }
-  next()
-})
-
-export default router

+ 0 - 129
pc/src/store/crowdNameListStore.js

@@ -1,129 +0,0 @@
-// 客群名单 store(demo 用,刷新后重置为初始种子数据)
-import Vue from 'vue'
-
-const now = () => {
-  const d = new Date()
-  const pad = (n) => String(n).padStart(2, '0')
-  return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())} ${pad(d.getHours())}:${pad(d.getMinutes())}:${pad(d.getSeconds())}`
-}
-
-// A 开头 + 6 位序列 + 8 位日期 = 15 位
-function genId(seq) {
-  const d = new Date()
-  const pad = (n) => String(n).padStart(2, '0')
-  const stamp = `${d.getFullYear()}${pad(d.getMonth() + 1)}${pad(d.getDate())}`
-  return `A${String(seq).padStart(6, '0')}${stamp}`
-}
-
-const state = Vue.observable({
-  list: [
-    {
-      id: 'A0000120251224',
-      name: '名单1',
-      description: '层层礼活动黑名单',
-      status: 'draft',
-      members: ['00001', '00002', '00003', '00004'],
-      creator: '运维A',
-      reviewer: '审核B',
-      createdAt: '2025-12-24 12:52:30',
-      updatedAt: '2025-12-31 15:31:12'
-    }
-  ],
-  seq: 2,
-  // 调整流水(demo 仅用于上传文件结果展示)
-  adjustments: []
-})
-
-function findById(id) {
-  return state.list.find((t) => t.id === id)
-}
-
-export default {
-  get list() {
-    return state.list
-  },
-  query({ id = '', name = '' } = {}) {
-    return state.list.filter((t) => {
-      if (id && !t.id.includes(id)) return false
-      if (name && !t.name.includes(name)) return false
-      return true
-    })
-  },
-  getById(id) {
-    return findById(id)
-  },
-  create(payload) {
-    const initialMembers = Array.isArray(payload.members)
-      ? Array.from(new Set(payload.members.filter((m) => m)))
-      : []
-    const record = {
-      id: genId(state.seq++),
-      name: payload.name,
-      description: payload.description || '',
-      status: 'draft',
-      members: initialMembers,
-      creator: '当前用户',
-      reviewer: '审核B',
-      createdAt: now(),
-      updatedAt: now()
-    }
-    state.list.unshift(record)
-    return record
-  },
-  update(id, payload) {
-    const target = findById(id)
-    if (!target) return null
-    target.name = payload.name
-    target.description = payload.description || ''
-    target.updatedAt = now()
-    return target
-  },
-  activate(id) {
-    const target = findById(id)
-    if (!target) return
-    target.status = 'active'
-    target.updatedAt = now()
-  },
-  stop(id) {
-    const target = findById(id)
-    if (!target) return
-    target.status = 'stopped'
-    target.updatedAt = now()
-  },
-  // 单条人员调整
-  adjustOne({ listId, customerId, direction }) {
-    const target = findById(listId)
-    if (!target) return { ok: false, reason: '名单不存在' }
-    if (target.status === 'stopped') return { ok: false, reason: '已停止名单不可调整' }
-    if (direction === 'A') {
-      if (target.members.includes(customerId)) {
-        return { ok: false, reason: '客户号已存在名单中' }
-      }
-      target.members.push(customerId)
-      target.updatedAt = now()
-      return { ok: true, action: 'add' }
-    }
-    if (direction === 'D') {
-      const idx = target.members.indexOf(customerId)
-      if (idx < 0) return { ok: false, reason: '客户号不在名单中' }
-      target.members.splice(idx, 1)
-      target.updatedAt = now()
-      return { ok: true, action: 'remove' }
-    }
-    return { ok: false, reason: '调整方向非法' }
-  },
-  // 批量调整(来自上传文件)
-  applyBatch(items) {
-    const result = { success: 0, failed: 0, failures: [] }
-    items.forEach((item, idx) => {
-      const r = this.adjustOne(item)
-      if (r.ok) {
-        result.success++
-      } else {
-        result.failed++
-        result.failures.push({ line: idx + 1, item, reason: r.reason })
-      }
-    })
-    return result
-  }
-}

+ 0 - 191
pc/src/store/crowdTaskStore.js

@@ -1,191 +0,0 @@
-// 客群任务 store(demo 用,刷新后重置为初始种子数据)
-import Vue from 'vue'
-
-const now = () => {
-  const d = new Date()
-  const pad = (n) => String(n).padStart(2, '0')
-  return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())} ${pad(d.getHours())}:${pad(d.getMinutes())}:${pad(d.getSeconds())}`
-}
-
-// 14 位编号:6 位序列 + 8 位日期
-function genTaskId(seq) {
-  const d = new Date()
-  const pad = (n) => String(n).padStart(2, '0')
-  const stamp = `${d.getFullYear()}${pad(d.getMonth() + 1)}${pad(d.getDate())}`
-  return `${String(seq).padStart(6, '0')}${stamp}`
-}
-
-// sample 数量规则
-function sampleSize(count) {
-  if (count < 30000) return 5
-  if (count < 50000) return 8
-  if (count < 150000) return 12
-  return 15
-}
-
-function generateSample(count) {
-  const size = Math.min(sampleSize(count), Math.max(count, 0))
-  const arr = []
-  const used = new Set()
-  while (arr.length < size) {
-    const cid = String(Math.floor(Math.random() * 99999) + 1).padStart(5, '0')
-    if (!used.has(cid)) {
-      used.add(cid)
-      arr.push({ index: arr.length + 1, customerId: cid })
-    }
-  }
-  return arr
-}
-
-function randomCount() {
-  // 随机分布在 1000 - 200000
-  return Math.floor(Math.random() * 199000) + 1000
-}
-
-const state = Vue.observable({
-  list: [
-    {
-      id: '00000120251224',
-      name: '层层礼圈选客群1',
-      description: '圈选层层礼报名客群',
-      startDate: '2025-12-25',
-      endDate: '2025-12-31',
-      ruleGroupLogic: 'AND',
-      ruleGroups: [
-        { id: 'RG20260401001', name: '层层礼-借记卡新客圈选', description: '2025/9/30 前开户 + MSC 属于卓越/运筹', logic: 'AND', ruleCount: 4 },
-        { id: 'RG20260410002', name: '信用卡-高净值持卡客户', description: 'MSC 属于卓越/尊尚 且 月末余额 ≥ 100 万', logic: 'AND', ruleCount: 2 }
-      ],
-      status: 'verifying',
-      creator: '运维A',
-      reviewer: '审核B',
-      createdAt: '2025-12-24 12:52:30',
-      updatedAt: '2025-12-31 15:31:12',
-      run: {
-        count: 20000,
-        sample: generateSample(20000),
-        fulllist: null
-      }
-    }
-  ],
-  seq: 2
-})
-
-function findById(id) {
-  return state.list.find((t) => t.id === id)
-}
-
-// 模拟"行内"异步返回
-function asyncRun(task, kind, delay = 2000) {
-  if (kind === 'countSample') {
-    Vue.set(task.run, 'count', 'running')
-    Vue.set(task.run, 'sample', 'running')
-    setTimeout(() => {
-      const c = randomCount()
-      Vue.set(task.run, 'count', c)
-      Vue.set(task.run, 'sample', generateSample(c))
-      task.status = 'verifying'
-      task.updatedAt = now()
-    }, delay)
-  } else if (kind === 'fulllist') {
-    Vue.set(task.run, 'fulllist', 'running')
-    setTimeout(() => {
-      Vue.set(task.run, 'fulllist', 'completed')
-      task.status = 'online'
-      task.updatedAt = now()
-    }, delay + 1000)
-  }
-}
-
-export default {
-  get list() {
-    return state.list
-  },
-  query({ id = '', name = '' } = {}) {
-    return state.list.filter((t) => {
-      if (id && !t.id.includes(id)) return false
-      if (name && !t.name.includes(name)) return false
-      return true
-    })
-  },
-  getById(id) {
-    return findById(id)
-  },
-  create(payload) {
-    const record = {
-      id: genTaskId(state.seq++),
-      name: payload.name,
-      description: payload.description || '',
-      startDate: payload.startDate,
-      endDate: payload.endDate,
-      ruleGroupLogic: payload.ruleGroupLogic || 'AND',
-      ruleGroups: payload.ruleGroups || [],
-      status: 'draft',
-      creator: '当前用户',
-      reviewer: '',
-      createdAt: now(),
-      updatedAt: now(),
-      run: { count: null, sample: null, fulllist: null }
-    }
-    state.list.unshift(record)
-    return record
-  },
-  copyFrom(sourceId) {
-    const src = findById(sourceId)
-    if (!src) return null
-    return this.create({
-      name: src.name + '-副本',
-      description: src.description,
-      startDate: src.startDate,
-      endDate: src.endDate,
-      ruleGroupLogic: src.ruleGroupLogic,
-      ruleGroups: src.ruleGroups.map((r) => ({ ...r }))
-    })
-  },
-  update(id, payload, opts = {}) {
-    const target = findById(id)
-    if (!target) return null
-    target.name = payload.name
-    target.description = payload.description || ''
-    target.startDate = payload.startDate
-    target.endDate = payload.endDate
-    target.ruleGroupLogic = payload.ruleGroupLogic || 'AND'
-    target.ruleGroups = payload.ruleGroups || []
-    target.updatedAt = now()
-    // 编辑校验状态的任务 → 回退到新建并重置运行信息
-    if (opts.resetRun) {
-      target.status = 'draft'
-      target.run = { count: null, sample: null, fulllist: null }
-    }
-    return target
-  },
-  stop(id) {
-    const target = findById(id)
-    if (!target) return
-    target.status = 'stopped'
-    target.updatedAt = now()
-  },
-  run(id) {
-    const target = findById(id)
-    if (!target || target.status !== 'draft') return
-    asyncRun(target, 'countSample')
-  },
-  rerunCountSample(id) {
-    const target = findById(id)
-    if (!target) return
-    asyncRun(target, 'countSample')
-  },
-  publish(id) {
-    const target = findById(id)
-    if (!target || target.status !== 'verifying') return
-    target.reviewer = target.reviewer || '审核B'
-    asyncRun(target, 'fulllist')
-  },
-  // 给视图用:当前任务允许的操作列表
-  allowedActions(task) {
-    if (!task) return []
-    if (task.status === 'draft') return ['edit', 'stop', 'detail', 'run']
-    if (task.status === 'verifying') return ['edit', 'stop', 'detail', 'publish']
-    if (task.status === 'online') return ['stop', 'detail']
-    return ['detail']
-  }
-}

+ 0 - 163
pc/src/store/ruleGroupStore.js

@@ -1,163 +0,0 @@
-// 规则组数据 store(demo 用,刷新后重置为初始种子数据)
-import Vue from 'vue'
-
-const now = () => {
-  const d = new Date()
-  const pad = (n) => String(n).padStart(2, '0')
-  return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())} ${pad(d.getHours())}:${pad(d.getMinutes())}:${pad(d.getSeconds())}`
-}
-
-const state = Vue.observable({
-  list: [
-    {
-      id: 'RG20260401001',
-      name: '层层礼-借记卡新客圈选',
-      type: 'debitCard',
-      logic: 'AND',
-      description: '2025/9/30 前开户 + MSC 属于卓越/运筹 + TRB < 50万',
-      status: 'online',
-      creator: '张运营',
-      createdAt: '2026-04-01 09:15:30',
-      updater: '王审核',
-      updatedAt: '2026-04-05 11:20:18',
-      rules: [
-        {
-          id: 'r1',
-          metaCode: '001',
-          ruleName: '层层礼开户时间',
-          config: { accountType: 'I', operator: '<', date: '2025-09-30' }
-        },
-        {
-          id: 'r2',
-          metaCode: '010',
-          ruleName: '层层礼,MSC',
-          config: { operator: 'in', values: ['premier', 'advance'] }
-        },
-        {
-          id: 'r3',
-          metaCode: '050',
-          ruleName: 'TRB值不含家庭账户余额(连续3个月达标)',
-          config: { month: '2025-12', nm: { n: 3, m: 6 }, operator: '<', amount: 500000 }
-        },
-        {
-          id: 'r4',
-          metaCode: '051',
-          ruleName: '2025年12月月末余额(连续3个月达标)',
-          config: { month: '2025-12', nm: { n: 3, m: 6 }, operator: '<', amount: 500000 }
-        }
-      ]
-    },
-    {
-      id: 'RG20260410002',
-      name: '信用卡-高净值持卡客户',
-      type: 'creditCard',
-      logic: 'AND',
-      description: 'MSC 属于卓越/尊尚 且 月末余额 ≥ 100 万',
-      status: 'online',
-      creator: '李产品',
-      createdAt: '2026-04-10 16:55:02',
-      updater: '李产品',
-      updatedAt: '2026-04-10 16:55:02',
-      rules: [
-        {
-          id: 'r1',
-          metaCode: '010',
-          ruleName: '信用卡MSC',
-          config: { operator: 'in', values: ['premier', 'excellence'] }
-        },
-        {
-          id: 'r2',
-          metaCode: '051',
-          ruleName: '月末余额 ≥ 100万',
-          config: { month: '2026-03', nm: { n: 3, m: 6 }, operator: '>=', amount: 1000000 }
-        }
-      ]
-    },
-    {
-      id: 'RG20260420003',
-      name: '借记卡-跨境候选客群',
-      type: 'debitCard',
-      logic: 'OR',
-      description: 'TRB ≥ 50 万 或 月末余额 ≥ 50 万',
-      status: 'draft',
-      creator: '赵市场',
-      createdAt: '2026-04-20 11:48:33',
-      updater: '赵市场',
-      updatedAt: '2026-04-25 09:30:01',
-      rules: [
-        {
-          id: 'r1',
-          metaCode: '050',
-          ruleName: 'TRB ≥ 50万',
-          config: { month: '2026-04', nm: { n: 1, m: 1 }, operator: '>=', amount: 500000 }
-        }
-      ]
-    }
-  ]
-})
-
-let seq = state.list.length + 1
-
-function genId() {
-  const d = new Date()
-  const pad = (n) => String(n).padStart(2, '0')
-  const stamp = `${d.getFullYear()}${pad(d.getMonth() + 1)}${pad(d.getDate())}`
-  return `RG${stamp}${String(seq++).padStart(3, '0')}`
-}
-
-export default {
-  get list() {
-    return state.list
-  },
-  query({ id = '', name = '', type = '' } = {}) {
-    return state.list.filter((t) => {
-      if (id && !t.id.includes(id)) return false
-      if (name && !t.name.includes(name)) return false
-      if (type && t.type !== type) return false
-      return true
-    })
-  },
-  getById(id) {
-    return state.list.find((t) => t.id === id)
-  },
-  create(payload) {
-    const record = {
-      id: genId(),
-      name: payload.name,
-      type: payload.type,
-      logic: payload.logic || 'AND',
-      description: payload.description || '',
-      status: 'draft',
-      creator: '当前用户',
-      createdAt: now(),
-      updater: '当前用户',
-      updatedAt: now(),
-      rules: payload.rules || []
-    }
-    state.list.unshift(record)
-    return record
-  },
-  update(id, payload) {
-    const target = state.list.find((t) => t.id === id)
-    if (!target) return null
-    target.name = payload.name
-    target.type = payload.type
-    target.logic = payload.logic || 'AND'
-    target.description = payload.description || ''
-    target.rules = payload.rules || []
-    target.updater = '当前用户'
-    target.updatedAt = now()
-    return target
-  },
-  remove(id) {
-    const idx = state.list.findIndex((t) => t.id === id)
-    if (idx >= 0) state.list.splice(idx, 1)
-  },
-  publish(id) {
-    const target = state.list.find((t) => t.id === id)
-    if (!target) return
-    target.status = 'online'
-    target.updater = '当前用户'
-    target.updatedAt = now()
-  }
-}

+ 0 - 87
pc/src/store/smsTemplateStore.js

@@ -1,87 +0,0 @@
-// 短信模板数据的会话内存储(demo 用,刷新后重置为初始种子数据)
-import Vue from 'vue'
-
-const now = () => {
-  const d = new Date()
-  const pad = (n) => String(n).padStart(2, '0')
-  return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())} ${pad(d.getHours())}:${pad(d.getMinutes())}:${pad(d.getSeconds())}`
-}
-
-const state = Vue.observable({
-  list: [
-    {
-      id: 'SMS_TPL_001',
-      name: '通用权益支付成功模板',
-      content: '【银行】您购买的${productName}已支付成功,卡号:${exchangeNum},卡密:${exchangePwd},有效期至${expireDate},详情:${qrCodeDownPaths}',
-      creator: '张运营',
-      createdAt: '2026-04-01 09:15:30',
-      updater: '张运营',
-      updatedAt: '2026-04-10 14:20:10'
-    },
-    {
-      id: 'SMS_TPL_002',
-      name: '券码类短信模板',
-      content: '【银行】尊敬的客户,您的${productName}购买成功,券码为${exchangeNum},请于${expireDate}前使用。',
-      creator: '李产品',
-      createdAt: '2026-04-02 10:32:18',
-      updater: '李产品',
-      updatedAt: '2026-04-18 09:02:45'
-    },
-    {
-      id: 'SMS_TPL_003',
-      name: '实物类短信模板',
-      content: '【银行】您购买的${productName}已受理,我们将尽快安排发货,感谢您的支持。',
-      creator: '王运营',
-      createdAt: '2026-04-05 11:08:00',
-      updater: '王运营',
-      updatedAt: '2026-04-05 11:08:00'
-    }
-  ]
-})
-
-let seq = state.list.length
-
-const genId = () => {
-  seq += 1
-  return `SMS_TPL_${String(seq).padStart(3, '0')}`
-}
-
-export default {
-  state,
-  query({ name = '', id = '' } = {}) {
-    return state.list.filter(item => {
-      if (name && !item.name.includes(name)) return false
-      if (id && !item.id.includes(id)) return false
-      return true
-    })
-  },
-  getById(id) {
-    return state.list.find(item => item.id === id)
-  },
-  create({ name, content }) {
-    const item = {
-      id: genId(),
-      name,
-      content,
-      creator: '当前用户',
-      createdAt: now(),
-      updater: '当前用户',
-      updatedAt: now()
-    }
-    state.list.unshift(item)
-    return item
-  },
-  update(id, { name, content }) {
-    const target = state.list.find(item => item.id === id)
-    if (!target) return null
-    target.name = name
-    target.content = content
-    target.updater = '当前用户'
-    target.updatedAt = now()
-    return target
-  },
-  remove(id) {
-    const idx = state.list.findIndex(item => item.id === id)
-    if (idx >= 0) state.list.splice(idx, 1)
-  }
-}

+ 0 - 184
pc/src/store/tagStore.js

@@ -1,184 +0,0 @@
-// 标签数据的会话内存储(demo 用,刷新后重置为初始种子数据)
-import Vue from 'vue'
-
-const now = () => {
-  const d = new Date()
-  const pad = (n) => String(n).padStart(2, '0')
-  return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())} ${pad(d.getHours())}:${pad(d.getMinutes())}:${pad(d.getSeconds())}`
-}
-
-const state = Vue.observable({
-  list: [
-    {
-      id: 'TAG20260401001',
-      name: '近 6 个月尊尚客户',
-      description: '近 6 个月符合尊尚资格的客户,用于尊尚专享权益投放',
-      status: 'enabled',
-      creator: '张运营',
-      createdAt: '2026-04-01 09:15:30',
-      updater: '张运营',
-      updatedAt: '2026-04-10 14:20:10',
-      referenceCount: 3,
-      groups: [
-        {
-          id: 'g1',
-          logic: 'AND',
-          rules: [
-            {
-              id: 'r1',
-              paramKey: 'isQualifiedLast6Months',
-              timeWindow: { mode: 'current', month: '', n: 3, m: 1, aggregator: 'any' },
-              operator: 'eq',
-              value: true,
-              value2: '',
-              unit: 'day'
-            }
-          ]
-        }
-      ]
-    },
-    {
-      id: 'TAG20260402002',
-      name: '高净值活跃客户',
-      description: 'TRB ≥ 100万 且 近 3 个月登录过手机银行',
-      status: 'enabled',
-      creator: '李产品',
-      createdAt: '2026-04-02 10:32:18',
-      updater: '王审核',
-      updatedAt: '2026-04-18 09:02:45',
-      referenceCount: 1,
-      groups: [
-        {
-          id: 'g1',
-          logic: 'AND',
-          rules: [
-            {
-              id: 'r1',
-              paramKey: 'avgTrbIncludingHousehold',
-              timeWindow: { mode: 'current', month: '', n: 3, m: 1, aggregator: 'any' },
-              operator: 'gte',
-              value: 1000000,
-              value2: '',
-              unit: 'day'
-            },
-            {
-              id: 'r2',
-              paramKey: 'isPrevTwoMonthsActive',
-              timeWindow: { mode: 'current', month: '', n: 3, m: 1, aggregator: 'any' },
-              operator: 'eq',
-              value: true,
-              value2: '',
-              unit: 'day'
-            }
-          ]
-        }
-      ]
-    },
-    {
-      id: 'TAG20260410003',
-      name: '跨境 1+1 CN-HK 新客',
-      description: '2026 年新晋 1+1 CN-HK qualifier',
-      status: 'disabled',
-      creator: '赵市场',
-      createdAt: '2026-04-10 16:55:02',
-      updater: '赵市场',
-      updatedAt: '2026-04-20 11:48:33',
-      referenceCount: 0,
-      groups: [
-        {
-          id: 'g1',
-          logic: 'AND',
-          rules: [
-            {
-              id: 'r1',
-              paramKey: 'isHKQualifier',
-              timeWindow: { mode: 'current', month: '', n: 3, m: 1, aggregator: 'any' },
-              operator: 'eq',
-              value: true,
-              value2: '',
-              unit: 'day'
-            },
-            {
-              id: 'r2',
-              paramKey: 'cnHkQualifierDate',
-              timeWindow: { mode: 'current', month: '', n: 3, m: 1, aggregator: 'any' },
-              operator: 'gte',
-              value: '2026-01',
-              value2: '',
-              unit: 'day'
-            }
-          ]
-        }
-      ]
-    }
-  ]
-})
-
-let seq = state.list.length + 1
-
-function genId() {
-  const d = new Date()
-  const pad = (n) => String(n).padStart(2, '0')
-  const stamp = `${d.getFullYear()}${pad(d.getMonth() + 1)}${pad(d.getDate())}`
-  return `TAG${stamp}${String(seq++).padStart(3, '0')}`
-}
-
-export default {
-  get list() {
-    return state.list
-  },
-  query({ name = '', id = '', status = '', creator = '', createdRange = [] } = {}) {
-    return state.list.filter((t) => {
-      if (name && !t.name.includes(name)) return false
-      if (id && t.id !== id) return false
-      if (status && t.status !== status) return false
-      if (creator && !t.creator.includes(creator)) return false
-      if (createdRange && createdRange.length === 2 && createdRange[0] && createdRange[1]) {
-        const c = t.createdAt.slice(0, 10)
-        if (c < createdRange[0] || c > createdRange[1]) return false
-      }
-      return true
-    })
-  },
-  getById(id) {
-    return state.list.find((t) => t.id === id)
-  },
-  create(payload) {
-    const record = {
-      id: genId(),
-      name: payload.name,
-      description: payload.description || '',
-      status: payload.status ? 'enabled' : 'disabled',
-      creator: '当前用户',
-      createdAt: now(),
-      updater: '当前用户',
-      updatedAt: now(),
-      referenceCount: 0,
-      groups: payload.groups || []
-    }
-    state.list.unshift(record)
-    return record
-  },
-  update(id, payload) {
-    const target = state.list.find((t) => t.id === id)
-    if (!target) return null
-    target.name = payload.name
-    target.description = payload.description || ''
-    target.status = payload.status ? 'enabled' : 'disabled'
-    target.groups = payload.groups || []
-    target.updater = '当前用户'
-    target.updatedAt = now()
-    return target
-  },
-  remove(id) {
-    const idx = state.list.findIndex((t) => t.id === id)
-    if (idx >= 0) state.list.splice(idx, 1)
-  },
-  toggleStatus(id) {
-    const target = state.list.find((t) => t.id === id)
-    if (!target) return
-    target.status = target.status === 'enabled' ? 'disabled' : 'enabled'
-    target.updater = '当前用户'
-    target.updatedAt = now()
-  }
-}

+ 0 - 19
pc/src/utils/linkOptions.js

@@ -1,19 +0,0 @@
-import request from '@/utils/request'
-
-export async function fetchLinkOptions() {
-  const [campaignRes, channelRes] = await Promise.all([
-    request.get('/api/campaign/list'),
-    request.get('/api/channel/list')
-  ])
-
-  return {
-    campaignOptions: (campaignRes.data || []).map(item => ({
-      label: `${item.id} - ${item.name}`,
-      value: item.id
-    })),
-    channelOptions: (channelRes.data || []).map(item => ({
-      label: `${item.id} - ${item.subName || item.name || '-'}`,
-      value: item.id
-    }))
-  }
-}

+ 0 - 20
pc/src/utils/request.js

@@ -1,20 +0,0 @@
-import axios from 'axios'
-import { Message } from 'element-ui'
-
-const request = axios.create({ timeout: 5000 })
-
-request.interceptors.response.use(
-  res => {
-    if (res.data.code !== 200) {
-      Message.error(res.data.msg || '请求失败')
-      return Promise.reject(res.data)
-    }
-    return res.data
-  },
-  err => {
-    Message.error('网络错误')
-    return Promise.reject(err)
-  }
-)
-
-export default request

+ 0 - 974
pc/src/views/AddEquityProduct.vue

@@ -1,974 +0,0 @@
-<template>
-  <div class="add-equity-product">
-    <el-card>
-      <div slot="header" class="header-title">
-        <span>添加权益</span>
-      </div>
-
-      <!-- 步骤条 -->
-      <el-steps :active="activeStep" simple style="margin-bottom: 20px;">
-        <el-step title="填写权益信息" icon="el-icon-info"></el-step>
-        <el-step title="设置规则" icon="el-icon-setting"></el-step>
-        <el-step title="更多设置" icon="el-icon-monitor"></el-step>
-      </el-steps>
-
-      <!-- 步骤内容 -->
-      <div v-show="activeStep === 0">
-        <!-- 填写权益信息 -->
-        <el-card shadow="never" class="section-card">
-          <div slot="header" class="section-title">基本信息</div>
-          <el-form :model="form" label-width="120px" label-position="left">
-            <el-row :gutter="20">
-              <el-col :span="8">
-                <el-form-item label="权益来源" required>
-                  <el-radio-group v-model="form.source">
-                    <el-radio label="银行自有"></el-radio>
-                    <el-radio label="荣数商品"></el-radio>
-                    <el-radio label="第三方"></el-radio>
-                  </el-radio-group>
-                </el-form-item>
-              </el-col>
-              <el-col :span="8" v-if="form.source === '荣数商品'">
-                <el-form-item label="选择权益" required>
-                  <el-input
-                    v-model="form.rongshuProduct"
-                    placeholder="请选择权益"
-                    readonly
-                  >
-                    <el-button slot="append" @click="rongshuDialogVisible = true">选择</el-button>
-                  </el-input>
-                </el-form-item>
-              </el-col>
-              <el-col :span="8" v-if="form.source === '银行自有'">
-                <el-form-item label="权益类型" required>
-                  <el-radio-group v-model="form.productType">
-                    <el-radio label="券码"></el-radio>
-                  </el-radio-group>
-                </el-form-item>
-              </el-col>
-            </el-row>
-            <el-row :gutter="20">
-              <el-col :span="8" v-if="form.source === '第三方'">
-                <el-form-item label="外部权益链接" required>
-                  <el-select v-model="form.externalLink" placeholder="请选择外部权益链接">
-                    <el-option label="太古地产" value="太古地产"></el-option>
-                    <el-option label="龙腾" value="龙腾"></el-option>
-                    <el-option label="h5链接" value="h5链接"></el-option>
-                    <el-option label="小程序链接" value="小程序链接"></el-option>
-                  </el-select>
-                </el-form-item>
-              </el-col>
-              <el-col :span="8" v-if="form.source === '第三方' && form.externalLink === 'h5链接'" :gutter="20">
-                <el-form-item label="h5链接" required>
-                  <el-input v-model="form.customLink" placeholder="请输入h5链接"></el-input>
-                </el-form-item>
-              </el-col>
-              <el-col :span="8" v-if="form.source === '第三方'">
-                <el-form-item required label-width="140px">
-                  <span slot="label" style="white-space: nowrap;">是否有详情页<el-tooltip content="没有详情页时直接从首页/列表页跳转" placement="top"><i class="el-icon-question tip-icon"></i></el-tooltip></span>
-                  <el-radio-group v-model="form.externalJumpMethod">
-                    <el-radio-button label="没有"></el-radio-button>
-                    <el-radio-button label="有"></el-radio-button>
-                  </el-radio-group>
-                </el-form-item>
-              </el-col>
-            </el-row>
-            <el-row :gutter="20" v-if="form.source === '第三方' && form.externalLink === '小程序链接'">
-              <el-col :span="8">
-                <el-form-item label="小程序appid" required>
-                  <el-input v-model="form.miniAppId" placeholder="请输入小程序appid"></el-input>
-                </el-form-item>
-              </el-col>
-              <el-col :span="8">
-                <el-form-item label="小程序页面地址" required label-width="140px">
-                  <el-input v-model="form.miniAppPath" placeholder="请输入小程序页面地址"></el-input>
-                </el-form-item>
-              </el-col>
-            </el-row>
-
-            <template v-if="form.source === '银行自有' || form.source === '荣数商品'">
-              <el-row :gutter="20">
-                <el-col :span="8">
-                  <el-form-item label="权益名称" required>
-                    <el-input v-model="form.equityName" maxlength="40" placeholder="请输入权益名称"></el-input>
-                  </el-form-item>
-                </el-col>
-                <el-col :span="8">
-                  <el-form-item label="权益副标题">
-                    <el-input v-model="form.subTitle" maxlength="40" placeholder="请输入权益副标题"></el-input>
-                  </el-form-item>
-                </el-col>
-              </el-row>
-              <el-row :gutter="20" v-if="form.source === '荣数商品'">
-                <el-col :span="8">
-                  <el-form-item label="库存数" required>
-                    <el-input-number
-                      v-model="form.stockCount"
-                      :min="0"
-                      :max="100000"
-                      style="width: 100%"
-                    ></el-input-number>
-                  </el-form-item>
-                </el-col>
-              </el-row>
-              <el-row :gutter="20">
-                <el-col :span="8">
-                  <el-form-item required>
-                    <span slot="label">权益图标
-                      <el-tooltip content="展示在权益首页和权益列表页" placement="top">
-                        <i class="el-icon-question tip-icon"></i>
-                      </el-tooltip>
-                    </span>
-                    <el-upload
-                      action=""
-                      :auto-upload="false"
-                      :file-list="form.listIcon"
-                      :on-change="file => handleUpload(file, 'listIcon')"
-                      :on-preview="handlePreviewImage"
-                      :on-remove="() => form.listIcon = []"
-                      :class="{ 'upload-hide-trigger': form.listIcon.length >= 1 }"
-                      :limit="1"
-                      list-type="picture-card"
-                    >
-                      <i class="el-icon-plus"></i>
-                    </el-upload>
-                    <div class="upload-size-tip">建议尺寸:80x80px</div>
-                  </el-form-item>
-                </el-col>
-              </el-row>
-            </template>
-
-            <template v-else-if="form.source === '第三方'">
-              <el-row :gutter="20">
-                <el-col :span="8">
-                  <el-form-item label="权益名称" required>
-                    <el-input v-model="form.equityName" maxlength="40" placeholder="请输入权益名称"></el-input>
-                  </el-form-item>
-                </el-col>
-                <el-col :span="8">
-                  <el-form-item label="权益副标题">
-                    <el-input v-model="form.subTitle" maxlength="40" placeholder="请输入权益副标题"></el-input>
-                  </el-form-item>
-                </el-col>
-              </el-row>
-              <el-row :gutter="20" v-if="form.externalJumpMethod === '没有'">
-                <el-col :span="8">
-                  <el-form-item required>
-                    <span slot="label">权益图标
-                      <el-tooltip content="展示在权益首页和权益列表页" placement="top">
-                        <i class="el-icon-question tip-icon"></i>
-                      </el-tooltip>
-                    </span>
-                    <el-upload
-                      action=""
-                      :auto-upload="false"
-                      :file-list="form.listIcon"
-                      :on-change="file => handleUpload(file, 'listIcon')"
-                      :on-preview="handlePreviewImage"
-                      :on-remove="() => form.listIcon = []"
-                      :class="{ 'upload-hide-trigger': form.listIcon.length >= 1 }"
-                      :limit="1"
-                      list-type="picture-card"
-                    >
-                      <i class="el-icon-plus"></i>
-                    </el-upload>
-                    <div class="upload-size-tip">建议尺寸:80x80px</div>
-                  </el-form-item>
-                </el-col>
-              </el-row>
-              <el-row v-if="form.externalJumpMethod === '有'" :gutter="20">
-                <el-col :span="8">
-                  <el-form-item required>
-                    <span slot="label">权益图标
-                      <el-tooltip content="展示在权益首页和权益列表页" placement="top">
-                        <i class="el-icon-question tip-icon"></i>
-                      </el-tooltip>
-                    </span>
-                    <el-upload
-                      action=""
-                      :auto-upload="false"
-                      :file-list="form.listIcon"
-                      :on-change="file => handleUpload(file, 'listIcon')"
-                      :on-preview="handlePreviewImage"
-                      :on-remove="() => form.listIcon = []"
-                      :class="{ 'upload-hide-trigger': form.listIcon.length >= 1 }"
-                      :limit="1"
-                      list-type="picture-card"
-                    >
-                      <i class="el-icon-plus"></i>
-                    </el-upload>
-                    <div class="upload-size-tip">建议尺寸:80x80px</div>
-                  </el-form-item>
-                </el-col>
-              </el-row>
-            </template>
-          </el-form>
-        </el-card>
-
-        <!-- 详情信息 -->
-        <el-card v-if="shouldShowDetailCard" shadow="never" class="section-card">
-          <div slot="header" class="section-title">详情信息</div>
-          <el-form :model="form" label-width="120px" label-position="left">
-            <el-row :gutter="20">
-              <el-col :span="8">
-                <el-form-item label="权益主图" required>
-                  <el-upload
-                    action=""
-                    :auto-upload="false"
-                    :file-list="form.mainImage"
-                    :on-change="file => handleUpload(file, 'mainImage')"
-                    :on-preview="handlePreviewImage"
-                    :on-remove="() => form.mainImage = []"
-                    :class="{ 'upload-hide-trigger': form.mainImage.length >= 1 }"
-                    :limit="1"
-                    list-type="picture-card"
-                  >
-                    <i class="el-icon-plus"></i>
-                  </el-upload>
-                  <div class="upload-size-tip">建议尺寸:716x400px</div>
-                </el-form-item>
-              </el-col>
-            </el-row>
-            <el-row :gutter="20">
-              <el-col :span="12">
-                <el-form-item label="权益介绍" required>
-                  <el-input
-                    v-model="form.introduction"
-                    type="textarea"
-                    :rows="4"
-                    maxlength="500"
-                    show-word-limit
-                    placeholder="请输入权益介绍"
-                  ></el-input>
-                </el-form-item>
-              </el-col>
-            </el-row>
-            <el-row :gutter="20">
-              <el-col :span="12">
-                <el-form-item label="权益有效期" required>
-                  <template v-if="form.source === '银行自有' || form.source === '荣数商品'">
-                    <el-radio-group v-model="form.validityType">
-                      <el-radio label="fromGrantPlan">取自发放计划</el-radio>
-                      <el-radio label="custom">自定义输入</el-radio>
-                    </el-radio-group>
-                    <el-input
-                      v-if="form.validityType === 'custom'"
-                      v-model="form.validityText"
-                      maxlength="50"
-                      show-word-limit
-                      placeholder="请输入权益有效期"
-                      style="margin-top: 8px;"
-                    ></el-input>
-                  </template>
-                  <el-input
-                    v-else
-                    v-model="form.validityText"
-                    maxlength="50"
-                    show-word-limit
-                    placeholder="请输入权益有效期"
-                  ></el-input>
-                </el-form-item>
-              </el-col>
-            </el-row>
-            <el-row :gutter="20">
-              <el-col :span="12">
-                <el-form-item label="权益内容" required>
-                  <quill-editor v-model="form.overview" :options="quillOptions"></quill-editor>
-                </el-form-item>
-              </el-col>
-              <el-col :span="12">
-                <el-form-item label="权益说明" required>
-                  <quill-editor v-model="form.details" :options="quillOptions"></quill-editor>
-                </el-form-item>
-              </el-col>
-            </el-row>
-            <template v-if="form.source === '银行自有' || form.source === '荣数商品'">
-              <el-row :gutter="20">
-                <el-col :span="24">
-                  <el-form-item>
-                    <span slot="label">下单成功短信
-                      <el-tooltip placement="top">
-                        <div slot="content">
-                          此模板为权益支付成功,发送给持卡人的短信模板;<br/>
-                          开启后需选择短信模板,关闭则不发送短信。<br/>
-                          占位符如下:<br/>
-                          权益名称:${productName}<br/>
-                          结束日期:${expireDate}<br/>
-                          卡号:${exchangeNum}<br/>
-                          卡密:${exchangePwd}<br/>
-                          券码路径:${qrCodeDownPaths}
-                        </div>
-                        <i class="el-icon-question tip-icon"></i>
-                      </el-tooltip>
-                    </span>
-                    <el-switch v-model="form.smsEnabled" @change="handleSmsEnabledChange"></el-switch>
-                    <template v-if="form.smsEnabled">
-                      <div style="width: 600px;background: #f1f2f5;padding: 20px;margin-top: 10px;">
-                        <el-select
-                          v-model="form.smsTemplateId"
-                          placeholder="请选择短信模板"
-                          style="width: 100%"
-                          @change="handleSmsTemplateChange"
-                        >
-                          <el-option
-                            v-for="item in smsTemplateOptions"
-                            :key="item.id"
-                            :label="item.name"
-                            :value="item.id"
-                          />
-                        </el-select>
-                        <div v-if="form.smsTemplate" class="sms-template-preview">{{ form.smsTemplate }}</div>
-                      </div>
-                    </template>
-                  </el-form-item>
-                </el-col>
-              </el-row>
-              <el-row :gutter="20">
-                <el-col :span="8">
-                  <el-form-item label="领取成功按钮" required>
-                    <el-input v-model="form.successButtonText" placeholder="请输入按钮文案"></el-input>
-                  </el-form-item>
-                </el-col>
-              </el-row>
-            </template>
-          </el-form>
-        </el-card>
-
-      </div>
-
-      <div v-show="activeStep === 1">
-        <!-- 设置规则 -->
-        <el-card shadow="never" class="section-card">
-          <div slot="header" class="section-title">设置规则</div>
-          
-          <!-- 展示规则 -->
-          <div class="rule-section">
-            <h4>展示规则</h4>
-            <el-form :model="form" label-width="120px" label-position="left">
-              <el-form-item label="展示时间限制">
-                <el-switch v-model="form.showTimeEnabled"></el-switch>
-                <template v-if="form.showTimeEnabled">
-                  <div style="display: flex;width: 600px;background: #f1f2f5;padding: 20px;margin-top: 10px;">
-                    <el-date-picker
-                      v-model="form.showStartTime"
-                      type="datetime"
-                      placeholder="请选择开始时间"
-                      format="yyyy-MM-dd HH:mm:ss"
-                      value-format="yyyy-MM-dd HH:mm:ss"
-                      style="width: 100%"
-                    ></el-date-picker>
-                    <div style="width: 50px;text-align: center;">-</div>
-                    <el-date-picker
-                      v-model="form.showEndTime"
-                      type="datetime"
-                      placeholder="请选择结束时间"
-                      format="yyyy-MM-dd HH:mm:ss"
-                      value-format="yyyy-MM-dd HH:mm:ss"
-                      style="width: 100%"
-                    ></el-date-picker>
-                  </div>
-                </template>
-              </el-form-item>
-              <el-form-item label="展示客群限制">
-                <el-switch v-model="form.showGroupEnabled"></el-switch>
-                <template v-if="form.showGroupEnabled">
-                  <div style="display: flex;width: 600px;background: #f1f2f5;padding: 20px;margin-top: 10px;">
-                    <el-select v-model="form.showGroupTagIds" multiple filterable placeholder="请选择客群" style="width: 100%">
-                      <el-option label="客群A" value="客群A"></el-option>
-                      <el-option label="客群B" value="客群B"></el-option>
-                      <el-option label="客群C" value="客群C"></el-option>
-                    </el-select>
-                  </div>
-                </template>
-              </el-form-item>
-            </el-form>
-          </div>
-
-          <!-- 领取规则 -->
-          <div class="rule-section">
-            <h4>领取规则</h4>
-            <el-form :model="form" label-width="120px" label-position="left">
-              <el-form-item label="领取时间限制">
-                <el-switch v-model="form.claimTimeEnabled"></el-switch>
-                <template v-if="form.claimTimeEnabled">
-                  <div style="display: flex;width: 600px;background: #f1f2f5;padding: 20px;margin-top: 10px;">
-                    <el-date-picker
-                      v-model="form.claimStartTime"
-                      type="datetime"
-                      placeholder="请选择开始时间"
-                      format="yyyy-MM-dd HH:mm:ss"
-                      value-format="yyyy-MM-dd HH:mm:ss"
-                      style="width: 100%"
-                    ></el-date-picker>
-                    <div style="width: 50px;text-align: center;">-</div>
-                    <el-date-picker
-                      v-model="form.claimEndTime"
-                      type="datetime"
-                      placeholder="请选择结束时间"
-                      format="yyyy-MM-dd HH:mm:ss"
-                      value-format="yyyy-MM-dd HH:mm:ss"
-                      style="width: 100%"
-                    ></el-date-picker>
-                  </div>
-                </template>
-              </el-form-item>
-              <el-form-item label="领取客群限制">
-                <el-switch v-model="form.claimGroupEnabled"></el-switch>
-                <template v-if="form.claimGroupEnabled">
-                  <div style="width: 600px;background: #f1f2f5;padding: 20px;margin-top: 10px;">
-                    <el-alert
-                      title="需在【发放计划】中选择权益发放,未发放的权益移动端不展示"
-                      type="info"
-                      :closable="false"
-                      show-icon
-                      style="margin-bottom: 12px;"
-                    ></el-alert>
-                    <div style="font-size: 14px;color: #606266;margin-bottom: 8px;font-weight: 500;">不满足提示</div>
-                    <el-form-item label="提示内容" label-width="80px" style="margin-bottom: 12px;">
-                      <el-input v-model="form.claimDenyMessage" maxlength="200" placeholder="请输入提示内容"></el-input>
-                    </el-form-item>
-                    <el-form-item label="跳转按钮文案" label-width="100px" style="margin-bottom: 12px;">
-                      <el-input v-model="form.claimDenyButtonText" maxlength="10" placeholder="请输入按钮文案"></el-input>
-                    </el-form-item>
-                    <el-form-item label="跳转按钮链接" label-width="100px" style="margin-bottom: 0;">
-                      <el-input v-model="form.claimDenyButtonLink" maxlength="200" placeholder="请输入按钮链接"></el-input>
-                    </el-form-item>
-                  </div>
-                </template>
-              </el-form-item>
-            </el-form>
-          </div>
-        </el-card>
-      </div>
-
-      <div v-show="activeStep === 2">
-        <!-- 显示页面 -->
-        <el-card shadow="never" class="section-card">
-          <div slot="header" class="section-title">显示页面</div>
-          <el-form :model="form" label-width="120px" label-position="left">
-            <el-form-item label="权益首页/列表页">
-              <el-radio-group v-model="form.showOnHome">
-                <el-radio label="显示">显示</el-radio>
-                <el-radio label="不显示">不显示</el-radio>
-              </el-radio-group>
-            </el-form-item>
-            <el-form-item label="组装页">
-              <el-radio-group v-model="form.showOnAssembly">
-                <el-radio label="显示">显示</el-radio>
-                <el-radio label="不显示">不显示</el-radio>
-              </el-radio-group>
-              <div
-                v-if="form.showOnAssembly === '显示'"
-                style="width: 600px;background: #f1f2f5;padding: 20px;margin-top: 10px;"
-              >
-                <el-select
-                  v-model="form.assemblyPages"
-                  multiple
-                  filterable
-                  placeholder="请选择组装页"
-                  style="width: 100%"
-                >
-                  <el-option label="组装页A" value="组装页A"></el-option>
-                  <el-option label="组装页B" value="组装页B"></el-option>
-                  <el-option label="组装页C" value="组装页C"></el-option>
-                  <el-option label="组装页D" value="组装页D"></el-option>
-                </el-select>
-              </div>
-            </el-form-item>
-          </el-form>
-        </el-card>
-
-        <!-- 分享设置 -->
-        <el-card shadow="never" class="section-card">
-          <div slot="header" class="section-title">分享设置</div>
-          <el-form :model="form" label-width="120px" label-position="left">
-            <el-form-item label="是否支持分享">
-              <el-switch v-model="form.shareEnabled" @change="handleShareEnabledChange"></el-switch>
-              <template v-if="form.shareEnabled">
-                <div style="width: 600px;background: #f1f2f5;padding: 20px;margin-top: 10px;">
-                  <el-form-item label="分享名称" label-width="100px" required style="margin-bottom: 16px;">
-                    <el-input
-                      v-model="form.shareName"
-                      maxlength="40"
-                      placeholder="请输入分享名称"
-                    ></el-input>
-                  </el-form-item>
-                  <el-form-item label="分享图片" label-width="100px" required style="margin-bottom: 0;">
-                    <el-upload
-                      action=""
-                      :auto-upload="false"
-                      :file-list="form.shareImage"
-                      :on-change="file => handleUpload(file, 'shareImage')"
-                      :limit="1"
-                      list-type="picture-card"
-                    >
-                      <i class="el-icon-plus"></i>
-                    </el-upload>
-                  </el-form-item>
-                </div>
-              </template>
-            </el-form-item>
-          </el-form>
-        </el-card>
-      </div>
-
-      <!-- 操作栏 -->
-      <div class="action-bar">
-        <div class="action-left">
-          <el-button
-            v-if="activeStep > 0"
-            @click="prevStep"
-            icon="el-icon-arrow-left"
-          >上一步</el-button>
-        </div>
-        <div class="action-center">
-          <el-button @click="goBack">取消</el-button>
-          <el-button type="primary" plain @click="handleSaveDraft">保存草稿</el-button>
-          <el-button
-            v-if="activeStep === 2"
-            type="primary"
-            @click="handleSubmitAudit"
-          >提交审核</el-button>
-        </div>
-        <div class="action-right">
-          <el-button
-            v-if="activeStep < 2"
-            type="primary"
-            @click="nextStep"
-          >下一步<i class="el-icon-arrow-right el-icon--right"></i></el-button>
-        </div>
-      </div>
-    </el-card>
-
-    <rongshu-product-dialog
-      :visible.sync="rongshuDialogVisible"
-      @select="onRongshuSelect"
-    />
-
-    <el-dialog :visible.sync="previewVisible" title="图片预览" width="640px" append-to-body>
-      <img v-if="previewImageUrl" :src="previewImageUrl" style="display:block; max-width:100%; margin:0 auto;" />
-    </el-dialog>
-  </div>
-</template>
-
-<script>
-import RongshuProductDialog from '@/components/RongshuProductDialog.vue'
-import request from '@/utils/request'
-
-export default {
-  name: 'AddEquityProduct',
-  components: { RongshuProductDialog },
-  data() {
-    return {
-      activeStep: 0,
-      rongshuDialogVisible: false,
-      previewVisible: false,
-      previewImageUrl: '',
-      quillOptions: {
-        placeholder: '请输入内容...',
-        modules: {
-          toolbar: [
-            ['bold', 'italic', 'underline'],
-            [{ list: 'ordered' }, { list: 'bullet' }],
-            [{ header: [1, 2, 3, false] }],
-            ['link', 'image'],
-            ['clean']
-          ]
-        }
-      },
-      smsTemplateOptions: [
-        { id: 'SMS_TPL_001', name: '通用权益支付成功模板', content: '【银行】您购买的${productName}已支付成功,卡号:${exchangeNum},卡密:${exchangePwd},有效期至${expireDate},详情:${qrCodeDownPaths}' },
-        { id: 'SMS_TPL_002', name: '券码类短信模板', content: '【银行】尊敬的客户,您的${productName}购买成功,券码为${exchangeNum},请于${expireDate}前使用。' },
-        { id: 'SMS_TPL_003', name: '实物类短信模板', content: '【银行】您购买的${productName}已受理,我们将尽快安排发货,感谢您的支持。' }
-      ],
-      form: {
-        source: '银行自有',
-        rongshuProduct: '',
-        productType: '券码',
-        faceValueThreshold: '',
-        faceValueDiscount: '',
-        stockCount: null,
-        shareEnabled: false,
-        shareImage: [],
-        shareName: '',
-        externalJumpMethod: '没有',
-        externalLink: '太古地产',
-        customLink: '',
-        miniAppId: '',
-        miniAppPath: '',
-        equityName: '',
-        productId: '',
-        merchantName: '',
-        subTitle: '',
-        listIcon: [],
-        mainImage: [],
-        introduction: '',
-        validityType: 'fromGrantPlan',
-        validityText: '',
-        overview: '',
-        details: '',
-        smsEnabled: false,
-        smsTemplateId: '',
-        smsTemplate: '',
-        successButtonText: '马上去使用',
-        showTimeEnabled: false,
-        showStartTime: '',
-        showEndTime: '',
-        showGroupEnabled: false,
-        showGroupTagIds: [],
-        claimTimeEnabled: false,
-        claimStartTime: '',
-        claimEndTime: '',
-        claimGroupEnabled: false,
-        claimGroupIsPotential: false,
-        claimGroupTagId: '',
-        claimDenyMessage: '',
-        claimDenyButtonText: '',
-        claimDenyButtonLink: '',
-        showOnHome: '显示',
-        showOnAssembly: '不显示',
-        assemblyPages: []
-      }
-    }
-  },
-  computed: {
-    shouldShowDetailCard() {
-      const s = this.form.source
-      if (s === '银行自有' || s === '荣数商品') return true
-      if (s === '第三方' && this.form.externalJumpMethod === '有') return true
-      return false
-    }
-  },
-  created() {
-    const copyFrom = this.$route.query.copyFrom
-    if (copyFrom) {
-      this.loadCopySource(copyFrom)
-    }
-  },
-  methods: {
-    loadCopySource(id) {
-      const mockMap = {
-        'Q100001': {
-          source: '银行自有',
-          externalJumpMethod: '',
-          externalLink: '',
-          customLink: '',
-          miniAppId: '',
-          miniAppPath: '',
-          skuNo: 'SKU001',
-          equityName: '权益A',
-          subTitle: '副标题示例',
-          listIcon: [],
-          mainImage: [],
-          overview: '<p>权益内容内容</p>',
-          details: '<p>权益说明内容</p>',
-          smsEnabled: true,
-          smsTemplateId: 'SMS_TPL_001',
-          smsTemplate: '【银行】您购买的${productName}已支付成功,卡号:${exchangeNum},卡密:${exchangePwd},有效期至${expireDate},详情:${qrCodeDownPaths}',
-          successButtonText: '马上去使用',
-          showTimeEnabled: true,
-          showStartTime: '2026-01-01 00:00:00',
-          showEndTime: '2026-12-31 23:59:59',
-          showGroupEnabled: false,
-          showGroupTagIds: [],
-          claimTimeEnabled: true,
-          claimStartTime: '2026-01-01 00:00:00',
-          claimEndTime: '2026-12-31 23:59:59',
-          claimGroupEnabled: false,
-          claimGroupTagId: '',
-          claimDenyMessage: '',
-          claimDenyButtonText: '',
-          claimDenyButtonLink: ''
-        },
-        'Q100002': {
-          source: '第三方',
-          externalJumpMethod: '没有',
-          externalLink: '太古地产',
-          customLink: '',
-          miniAppId: '',
-          miniAppPath: '',
-          skuNo: 'SKU002',
-          equityName: '权益B',
-          subTitle: '外部列表页副标题',
-          listIcon: [],
-          mainImage: [],
-          overview: '',
-          details: '',
-          smsEnabled: false,
-          smsTemplateId: '',
-          smsTemplate: '',
-          successButtonText: '',
-          showTimeEnabled: false,
-          showStartTime: '',
-          showEndTime: '',
-          showGroupEnabled: true,
-          showGroupTagIds: ['客群A'],
-          claimTimeEnabled: true,
-          claimStartTime: '2026-02-01 00:00:00',
-          claimEndTime: '2026-11-30 23:59:59',
-          claimGroupEnabled: true,
-          claimGroupIsPotential: false,
-          claimGroupTagId: '客群奖励活动A',
-          claimDenyMessage: '您暂不满足领取条件',
-          claimDenyButtonText: '去升级',
-          claimDenyButtonLink: 'https://example.com/upgrade'
-        },
-        'Q100003': {
-          source: '第三方',
-          externalJumpMethod: '有',
-          externalLink: '龙腾',
-          customLink: '',
-          miniAppId: '',
-          miniAppPath: '',
-          skuNo: 'SKU003',
-          equityName: '权益C',
-          subTitle: '外部详情页副标题',
-          listIcon: [],
-          mainImage: [],
-          overview: '<p>外部详情页权益内容</p>',
-          details: '<p>外部详情页权益说明</p>',
-          smsEnabled: false,
-          smsTemplateId: '',
-          smsTemplate: '',
-          successButtonText: '',
-          showTimeEnabled: false,
-          showStartTime: '',
-          showEndTime: '',
-          showGroupEnabled: false,
-          showGroupTagIds: [],
-          claimTimeEnabled: false,
-          claimStartTime: '',
-          claimEndTime: '',
-          claimGroupEnabled: false,
-          claimGroupTagId: '',
-          claimDenyMessage: '',
-          claimDenyButtonText: '',
-          claimDenyButtonLink: ''
-        },
-        'Q100004': {
-          source: '第三方',
-          externalJumpMethod: '有',
-          externalLink: 'h5链接',
-          customLink: 'https://example.com/custom',
-          skuNo: 'SKU004',
-          equityName: '权益D',
-          subTitle: '外部自定义链接副标题',
-          listIcon: [],
-          mainImage: [],
-          overview: '<p>自定义链接权益内容</p>',
-          details: '<p>自定义链接权益说明</p>',
-          smsEnabled: false,
-          smsTemplateId: '',
-          smsTemplate: '',
-          successButtonText: '',
-          showTimeEnabled: false,
-          showStartTime: '',
-          showEndTime: '',
-          showGroupEnabled: false,
-          showGroupTagIds: [],
-          claimTimeEnabled: false,
-          claimStartTime: '',
-          claimEndTime: '',
-          claimGroupEnabled: false,
-          claimGroupTagId: '',
-          claimDenyMessage: '',
-          claimDenyButtonText: '',
-          claimDenyButtonLink: ''
-        }
-      }
-
-      request.get(`/api/equity/detail?id=${id}`).then(res => {
-        const core = res.data || {}
-        const rich = mockMap[id] || {}
-        const merged = { ...rich }
-        const coreFields = ['equityName', 'source', 'productType', 'skuNo', 'merchantName', 'subTitle']
-        coreFields.forEach(k => {
-          if (core[k] !== undefined && core[k] !== null && core[k] !== '') {
-            merged[k] = core[k]
-          }
-        })
-        if (core.source === '第三方') {
-          if (core.jumpMethod) merged.externalJumpMethod = core.jumpMethod
-          if (core.jumpLink) merged.externalLink = core.jumpLink
-        }
-        if (core.showOnHome) merged.showOnHome = core.showOnHome
-        if (core.showOnAssembly) merged.showOnAssembly = core.showOnAssembly
-        if (Array.isArray(core.assemblyPages)) merged.assemblyPages = [...core.assemblyPages]
-        // 复制后清空标识字段,让用户重新命名并由后端生成新编号
-        delete merged.productId
-        Object.keys(merged).forEach(k => {
-          this.$set(this.form, k, merged[k])
-        })
-        this.$message.success(`已复制「${core.equityName || id}」的内容,请确认后保存`)
-      }).catch(() => {
-        this.$message.warning('未能加载源权益信息,将以空白表单创建')
-      })
-    },
-    nextStep() {
-      if (this.activeStep < 2) {
-        this.activeStep++
-      }
-    },
-    prevStep() {
-      if (this.activeStep > 0) {
-        this.activeStep--
-      }
-    },
-    buildPayload() {
-      const f = this.form
-      const isCustomValidity = f.source === '第三方' || f.validityType === 'custom'
-      return {
-        equityName: f.equityName,
-        source: f.source,
-        productType: f.productType || '',
-        skuNo: f.skuNo || '',
-        merchantName: f.merchantName || '',
-        subTitle: f.subTitle || '',
-        jumpMethod: f.source === '第三方' ? f.externalJumpMethod : '-',
-        jumpLink: f.source === '第三方' ? f.externalLink : '-',
-        exchangeMethod: f.source === '第三方'
-          ? (f.externalJumpMethod === '有' ? '第三方(详情页跳转)' : '第三方(列表跳转)')
-          : '权益平台',
-        validityType: isCustomValidity ? 'custom' : 'fromGrantPlan',
-        validityText: isCustomValidity ? (f.validityText || '') : '',
-        showOnHome: f.showOnHome,
-        showOnAssembly: f.showOnAssembly,
-        assemblyPages: [...(f.assemblyPages || [])]
-      }
-    },
-    handleSaveDraft() {
-      if (!this.form.equityName) {
-        this.$message.warning('请填写权益名称后再保存草稿')
-        return
-      }
-      request.post('/api/equity/save-draft', this.buildPayload()).then(() => {
-        this.$message.success('草稿已保存')
-        this.$router.push('/customization/hsbc/equity-products')
-      }).catch(() => {})
-    },
-    handleSubmitAudit() {
-      if (!this.form.equityName) {
-        this.$message.warning('请填写权益名称后再提交')
-        return
-      }
-      this.$prompt('请填写提交说明(可选)', '提交审核', {
-        confirmButtonText: '提交',
-        cancelButtonText: '取消',
-        inputType: 'textarea',
-        inputPlaceholder: '可填写本次提交的关键变更点',
-        inputValidator: () => true
-      }).then(({ value }) => {
-        const payload = { ...this.buildPayload(), submitReason: value || '' }
-        request.post('/api/equity/submit', payload).then(() => {
-          this.$message.success('已提交审核')
-          this.$router.push('/customization/hsbc/equity-products')
-        }).catch(() => {})
-      }).catch(() => {})
-    },
-    goBack() {
-      this.$router.push('/customization/hsbc/equity-products')
-    },
-    handleShareEnabledChange(val) {
-      if (val) {
-        if (!this.form.shareName) {
-          this.form.shareName = this.form.equityName || ''
-        }
-      }
-    },
-    handleSmsEnabledChange(val) {
-      if (!val) {
-        this.form.smsTemplateId = ''
-        this.form.smsTemplate = ''
-      }
-    },
-    handleSmsTemplateChange(id) {
-      const tpl = this.smsTemplateOptions.find(i => i.id === id)
-      this.form.smsTemplate = tpl ? tpl.content : ''
-    },
-    handleUpload(file, field) {
-      if (!file || !file.raw) return
-      const reader = new FileReader()
-      reader.onload = (e) => {
-        this.form[field] = [{ name: file.name, url: e.target.result, uid: file.uid }]
-      }
-      reader.readAsDataURL(file.raw)
-    },
-    handlePreviewImage(file) {
-      this.previewImageUrl = file.url || ''
-      this.previewVisible = !!this.previewImageUrl
-    },
-    onRongshuSelect(product) {
-      this.form.rongshuProduct = product.name
-      this.form.equityName = product.name
-      this.form.merchantName = product.merchant
-      this.form.stockCount = product.stock
-      this.form.mainImage = product.mainImage ? [...product.mainImage] : []
-    }
-  }
-}
-</script>
-
-<style scoped>
-.add-equity-product {
-  padding: 20px;
-}
-.header-title {
-  font-size: 18px;
-  font-weight: 500;
-}
-.section-card {
-  margin-bottom: 16px;
-  border: none;
-}
-.upload-hide-trigger >>> .el-upload--picture-card { display: none; }
-
-.section-card >>> .el-card__header {
-  padding: 12px 20px;
-  background: #fafafa;
-  border-bottom: 1px solid #ebeef5;
-}
-.section-title {
-  font-size: 16px;
-  font-weight: 600;
-  color: #303133;
-}
-.action-bar {
-  display: flex;
-  align-items: center;
-  justify-content: space-between;
-  margin-top: 20px;
-  padding-top: 20px;
-  border-top: 1px solid #ebeef5;
-}
-.action-left, .action-right {
-  width: 120px;
-}
-.action-center {
-  display: flex;
-  justify-content: center;
-  gap: 12px;
-}
-.tip-icon {
-  color: #909399;
-  cursor: pointer;
-  font-size: 14px;
-}
-.upload-size-tip {
-  margin-top: 4px;
-  font-size: 12px;
-  color: #909399;
-  line-height: 1.5;
-}
-.sms-template-preview {
-  margin-top: 12px;
-  padding: 12px;
-  background: #fff;
-  border: 1px dashed #dcdfe6;
-  border-radius: 4px;
-  font-size: 13px;
-  color: #606266;
-  line-height: 1.6;
-  white-space: pre-wrap;
-  word-break: break-all;
-}
-</style>

+ 0 - 527
pc/src/views/AuditDetail.vue

@@ -1,527 +0,0 @@
-<template>
-  <div class="audit-detail" v-if="audit">
-    <el-card>
-      <!-- 步骤条 -->
-      <el-steps :active="activeStep" simple style="margin-bottom: 20px;">
-        <el-step title="权益信息" icon="el-icon-info"></el-step>
-        <el-step title="设置规则" icon="el-icon-setting"></el-step>
-        <el-step title="更多设置" icon="el-icon-monitor"></el-step>
-      </el-steps>
-
-      <!-- 步骤 0:权益信息 -->
-      <div v-show="activeStep === 0">
-        <el-card shadow="never" class="section-card">
-          <div slot="header" class="section-title">基本信息</div>
-          <el-form :model="form" label-width="120px" label-position="left">
-            <el-row :gutter="20">
-              <el-col :span="8">
-                <el-form-item label="权益来源">
-                  <el-radio-group v-model="form.source" disabled>
-                    <el-radio-button label="银行自有"></el-radio-button>
-                    <el-radio-button label="荣数商品"></el-radio-button>
-                    <el-radio-button label="第三方"></el-radio-button>
-                  </el-radio-group>
-                </el-form-item>
-              </el-col>
-              <el-col :span="8" v-if="form.source === '银行自有'">
-                <el-form-item label="权益类型">
-                  <el-radio-group v-model="form.productType" disabled>
-                    <el-radio-button label="券码"></el-radio-button>
-                  </el-radio-group>
-                </el-form-item>
-              </el-col>
-              <el-col :span="8" v-if="form.source === '银行自有'">
-                <el-form-item label="商户名称">
-                  <el-input :value="form.merchantName" readonly></el-input>
-                </el-form-item>
-              </el-col>
-            </el-row>
-            <el-row :gutter="20">
-              <el-col :span="8" v-if="form.source === '第三方'">
-                <el-form-item label="外部权益链接">
-                  <el-input :value="form.externalLink" readonly></el-input>
-                </el-form-item>
-              </el-col>
-              <el-col :span="8" v-if="form.source === '第三方'">
-                <el-form-item label="是否有详情页">
-                  <el-radio-group v-model="form.externalJumpMethod" disabled>
-                    <el-radio-button label="没有"></el-radio-button>
-                    <el-radio-button label="有"></el-radio-button>
-                  </el-radio-group>
-                </el-form-item>
-              </el-col>
-            </el-row>
-            <el-row :gutter="20">
-              <el-col :span="8">
-                <el-form-item label="权益名称">
-                  <el-input :value="form.equityName" readonly></el-input>
-                </el-form-item>
-              </el-col>
-              <el-col :span="8">
-                <el-form-item label="权益副标题">
-                  <el-input :value="form.subTitle" readonly></el-input>
-                </el-form-item>
-              </el-col>
-              <el-col :span="8" v-if="form.source === '银行自有'">
-                <el-form-item label="SIP商品编号">
-                  <el-input :value="form.skuNo" readonly></el-input>
-                </el-form-item>
-              </el-col>
-            </el-row>
-            <el-row :gutter="20">
-              <el-col :span="8">
-                <el-form-item label="权益图标">
-                  <div v-if="form.listIcon && form.listIcon.length" class="image-preview">
-                    <img :src="form.listIcon[0]" class="preview-img" />
-                  </div>
-                  <span v-else class="no-data">暂无图片</span>
-                </el-form-item>
-              </el-col>
-            </el-row>
-          </el-form>
-        </el-card>
-
-        <el-card v-if="shouldShowDetailCard" shadow="never" class="section-card">
-          <div slot="header" class="section-title">详情信息</div>
-          <el-form :model="form" label-width="120px" label-position="left">
-            <el-row :gutter="20">
-              <el-col :span="8">
-                <el-form-item label="权益主图">
-                  <div v-if="form.mainImage && form.mainImage.length" class="image-preview">
-                    <img :src="form.mainImage[0]" class="preview-img" />
-                  </div>
-                  <span v-else class="no-data">暂无图片</span>
-                </el-form-item>
-              </el-col>
-            </el-row>
-            <el-row :gutter="20">
-              <el-col :span="12">
-                <el-form-item label="权益介绍">
-                  <el-input :value="form.introduction" type="textarea" :rows="4" readonly></el-input>
-                </el-form-item>
-              </el-col>
-            </el-row>
-            <el-row :gutter="20">
-              <el-col :span="12">
-                <el-form-item label="权益有效期">
-                  <el-input
-                    :value="form.validityType === 'fromGrantPlan' ? '取自发放计划' : (form.validityText || '-')"
-                    readonly
-                  ></el-input>
-                </el-form-item>
-              </el-col>
-            </el-row>
-            <el-row :gutter="20">
-              <el-col :span="12">
-                <el-form-item label="权益内容">
-                  <div class="rich-text-view" v-html="form.overview || '暂无内容'"></div>
-                </el-form-item>
-              </el-col>
-              <el-col :span="12">
-                <el-form-item label="权益说明">
-                  <div class="rich-text-view" v-html="form.details || '暂无内容'"></div>
-                </el-form-item>
-              </el-col>
-            </el-row>
-            <el-row :gutter="20" v-if="form.source === '银行自有' || form.source === '荣数商品'">
-              <el-col :span="8">
-                <el-form-item label="领取成功按钮">
-                  <el-input :value="form.successButtonText" readonly></el-input>
-                </el-form-item>
-              </el-col>
-            </el-row>
-          </el-form>
-        </el-card>
-      </div>
-
-      <!-- 步骤 1:规则 -->
-      <div v-show="activeStep === 1">
-        <el-card shadow="never" class="section-card">
-          <div slot="header" class="section-title">展示规则</div>
-          <el-form :model="form" label-width="120px" label-position="left">
-            <el-form-item label="展示时间限制">
-              <el-switch v-model="form.showTimeEnabled" disabled></el-switch>
-              <template v-if="form.showTimeEnabled">
-                <div class="rule-pane">
-                  <el-input :value="form.showStartTime" readonly style="width: 240px;"></el-input>
-                  <span style="margin: 0 8px;">-</span>
-                  <el-input :value="form.showEndTime" readonly style="width: 240px;"></el-input>
-                </div>
-              </template>
-            </el-form-item>
-            <el-form-item label="展示客群限制">
-              <el-switch v-model="form.showGroupEnabled" disabled></el-switch>
-              <template v-if="form.showGroupEnabled">
-                <div class="rule-pane">
-                  <el-input :value="(form.showGroupTagIds || []).join('、')" readonly></el-input>
-                </div>
-              </template>
-            </el-form-item>
-          </el-form>
-        </el-card>
-
-        <el-card shadow="never" class="section-card">
-          <div slot="header" class="section-title">领取规则</div>
-          <el-form :model="form" label-width="120px" label-position="left">
-            <el-form-item label="领取时间限制">
-              <el-switch v-model="form.claimTimeEnabled" disabled></el-switch>
-              <template v-if="form.claimTimeEnabled">
-                <div class="rule-pane">
-                  <el-input :value="form.claimStartTime" readonly style="width: 240px;"></el-input>
-                  <span style="margin: 0 8px;">-</span>
-                  <el-input :value="form.claimEndTime" readonly style="width: 240px;"></el-input>
-                </div>
-              </template>
-            </el-form-item>
-            <el-form-item label="领取客群限制">
-              <el-switch v-model="form.claimGroupEnabled" disabled></el-switch>
-            </el-form-item>
-          </el-form>
-        </el-card>
-      </div>
-
-      <!-- 步骤 2:更多设置 -->
-      <div v-show="activeStep === 2">
-        <el-card shadow="never" class="section-card">
-          <div slot="header" class="section-title">显示页面</div>
-          <el-form :model="form" label-width="120px" label-position="left">
-            <el-form-item label="权益首页/列表页">
-              <el-radio-group v-model="form.showOnHome" disabled>
-                <el-radio label="显示">显示</el-radio>
-                <el-radio label="不显示">不显示</el-radio>
-              </el-radio-group>
-            </el-form-item>
-            <el-form-item label="组装页">
-              <el-radio-group v-model="form.showOnAssembly" disabled>
-                <el-radio label="显示">显示</el-radio>
-                <el-radio label="不显示">不显示</el-radio>
-              </el-radio-group>
-              <div v-if="form.showOnAssembly === '显示'" class="rule-pane">
-                <el-input :value="(form.assemblyPages || []).join(',')" readonly></el-input>
-              </div>
-            </el-form-item>
-          </el-form>
-        </el-card>
-
-        <el-card shadow="never" class="section-card">
-          <div slot="header" class="section-title">分享设置</div>
-          <el-form :model="form" label-width="120px" label-position="left">
-            <el-form-item label="是否支持分享">
-              <el-switch v-model="form.shareEnabled" disabled></el-switch>
-            </el-form-item>
-          </el-form>
-        </el-card>
-      </div>
-
-      <!-- 步骤导航 -->
-      <div class="step-navigation">
-        <el-button v-if="activeStep > 0" @click="prevStep" icon="el-icon-arrow-left">上一步</el-button>
-        <el-button
-          v-if="activeStep < 2"
-          type="primary"
-          @click="nextStep"
-          style="margin-left: auto;"
-        >下一步<i class="el-icon-arrow-right el-icon--right"></i></el-button>
-      </div>
-
-      <!-- 已审记录 -->
-      <el-card v-if="audit.auditStatus !== 'pending'" shadow="never" class="section-card audit-action-card">
-        <div slot="header" class="section-title">审核记录</div>
-        <el-form label-width="120px" label-position="left">
-          <el-row :gutter="20">
-            <el-col :span="8">
-              <el-form-item label="审核人">
-                <span>{{ audit.auditorName || '-' }}</span>
-              </el-form-item>
-            </el-col>
-            <el-col :span="8">
-              <el-form-item label="审核时间">
-                <span>{{ audit.auditTime || '-' }}</span>
-              </el-form-item>
-            </el-col>
-            <el-col :span="8">
-              <el-form-item label="审核结果">
-                <el-tag :type="auditStatusTag(audit.auditStatus)" size="small">
-                  {{ auditStatusLabel(audit.auditStatus) }}
-                </el-tag>
-              </el-form-item>
-            </el-col>
-          </el-row>
-        </el-form>
-      </el-card>
-
-      <!-- 操作栏 -->
-      <div class="action-bar">
-        <el-button @click="goBack">返回</el-button>
-        <template v-if="canAudit">
-          <el-button type="danger" @click="handleReject">驳回</el-button>
-          <el-button type="primary" @click="handleApprove">通过</el-button>
-        </template>
-      </div>
-    </el-card>
-  </div>
-</template>
-
-<script>
-import request from '@/utils/request'
-
-const detailMockMap = {
-  'Q100001': {
-    listIcon: [], mainImage: [],
-    introduction: '权益A 介绍内容示例',
-    overview: '<p>权益内容内容</p>',
-    details: '<p>权益说明内容</p>',
-    successButtonText: '马上去使用',
-    showTimeEnabled: true,
-    showStartTime: '2026-01-01 00:00:00',
-    showEndTime: '2026-12-31 23:59:59',
-    showGroupEnabled: false,
-    showGroupTagIds: [],
-    claimTimeEnabled: true,
-    claimStartTime: '2026-01-01 00:00:00',
-    claimEndTime: '2026-12-31 23:59:59',
-    claimGroupEnabled: false
-  },
-  'Q100002': {
-    externalJumpMethod: '没有', externalLink: '太古地产',
-    listIcon: [], mainImage: [], introduction: '',
-    overview: '', details: '',
-    successButtonText: '',
-    showTimeEnabled: false, showStartTime: '', showEndTime: '',
-    showGroupEnabled: true, showGroupTagIds: ['客群A'],
-    claimTimeEnabled: true,
-    claimStartTime: '2026-02-01 00:00:00',
-    claimEndTime: '2026-11-30 23:59:59',
-    claimGroupEnabled: true
-  },
-  'Q100003': {
-    externalJumpMethod: '有', externalLink: '龙腾',
-    listIcon: [], mainImage: [],
-    introduction: '权益C 介绍内容示例',
-    overview: '<p>外部详情页权益内容</p>',
-    details: '<p>外部详情页权益说明</p>',
-    showTimeEnabled: false,
-    claimTimeEnabled: false,
-    claimGroupEnabled: false
-  },
-  'Q100004': {
-    externalJumpMethod: '有', externalLink: 'h5链接',
-    listIcon: [], mainImage: [],
-    introduction: '权益D 介绍内容示例',
-    overview: '<p>自定义链接权益内容</p>',
-    details: '<p>自定义链接权益说明</p>',
-    showTimeEnabled: false,
-    claimTimeEnabled: false,
-    claimGroupEnabled: false
-  }
-}
-
-export default {
-  name: 'AuditDetail',
-  data() {
-    return {
-      audit: null,
-      activeStep: 0,
-      form: {
-        source: '银行自有',
-        productType: '',
-        externalJumpMethod: '',
-        externalLink: '',
-        equityName: '',
-        subTitle: '',
-        skuNo: '',
-        merchantName: '',
-        listIcon: [],
-        mainImage: [],
-        introduction: '',
-        validityType: 'fromGrantPlan',
-        validityText: '',
-        overview: '',
-        details: '',
-        successButtonText: '',
-        showTimeEnabled: false,
-        showStartTime: '',
-        showEndTime: '',
-        showGroupEnabled: false,
-        showGroupTagIds: [],
-        claimTimeEnabled: false,
-        claimStartTime: '',
-        claimEndTime: '',
-        claimGroupEnabled: false,
-        showOnHome: '不显示',
-        showOnAssembly: '不显示',
-        assemblyPages: [],
-        shareEnabled: false
-      }
-    }
-  },
-  computed: {
-    shouldShowDetailCard() {
-      const s = this.form.source
-      if (s === '银行自有' || s === '荣数商品') return true
-      if (s === '第三方' && this.form.externalJumpMethod === '有') return true
-      return false
-    },
-    canAudit() {
-      if (!this.audit || this.audit.auditStatus !== 'pending') return false
-      const t = this.audit.auditType
-      if (t === 'new') return this.activeStep === 2
-      return true
-    }
-  },
-  created() {
-    this.loadAudit()
-  },
-  methods: {
-    loadAudit() {
-      const id = this.$route.params.id
-      request.get(`/api/audit/detail?id=${id}`).then(res => {
-        this.audit = res.data
-        if (this.audit) this.populateForm()
-      }).catch(() => {})
-    },
-    populateForm() {
-      const bizId = this.audit.bizId
-      // 1) 先用 mockMap 填充丰富的详情字段
-      if (detailMockMap[bizId]) {
-        Object.assign(this.form, detailMockMap[bizId])
-      }
-      // 2) 用 audit snapshot 覆盖(变更类型时会包含最新内容)
-      Object.assign(this.form, this.audit.snapshot || {})
-      // 3) 推导第三方相关字段
-      if (this.form.source === '第三方') {
-        if (this.form.jumpLink && !this.form.externalLink) this.form.externalLink = this.form.jumpLink
-        if (this.form.jumpMethod && !this.form.externalJumpMethod) {
-          this.form.externalJumpMethod = this.form.jumpMethod
-        }
-      }
-    },
-    auditTypeLabel(t) {
-      return { new: '新增', offshelf: '下架' }[t] || t
-    },
-    auditTypeTag(t) {
-      return { new: 'success', offshelf: 'danger' }[t] || ''
-    },
-    auditStatusLabel(s) {
-      return { pending: '待审核', approved: '已通过', rejected: '已驳回' }[s] || s
-    },
-    auditStatusTag(s) {
-      return { pending: 'warning', approved: 'success', rejected: 'danger' }[s] || ''
-    },
-    nextStep() { if (this.activeStep < 2) this.activeStep++ },
-    prevStep() { if (this.activeStep > 0) this.activeStep-- },
-    handleApprove() {
-      this.$confirm('确定通过该审核单吗?', '提示', {
-        confirmButtonText: '通过',
-        cancelButtonText: '取消',
-        type: 'success'
-      }).then(() => {
-        request.post('/api/audit/approve', { id: this.audit.id }).then(() => {
-          this.$message.success('审核通过')
-          this.$router.push('/customization/hsbc/audit')
-        }).catch(() => {})
-      }).catch(() => {})
-    },
-    handleReject() {
-      this.$confirm('确定驳回该审核单吗?', '提示', {
-        confirmButtonText: '驳回',
-        cancelButtonText: '取消',
-        type: 'warning'
-      }).then(() => {
-        request.post('/api/audit/reject', { id: this.audit.id }).then(() => {
-          this.$message.success('已驳回')
-          this.$router.push('/customization/hsbc/audit')
-        }).catch(() => {})
-      }).catch(() => {})
-    },
-    goBack() {
-      this.$router.push('/customization/hsbc/audit')
-    }
-  }
-}
-</script>
-
-<style scoped>
-.audit-detail {
-  padding: 0;
-}
-.header-bar {
-  display: flex;
-  justify-content: space-between;
-  align-items: center;
-}
-.header-left .title {
-  font-size: 16px;
-  font-weight: 600;
-  color: #303133;
-}
-.section-card {
-  margin-bottom: 16px;
-  border: none;
-}
-.section-card >>> .el-card__header {
-  padding: 12px 20px;
-  background: #fafafa;
-  border-bottom: 1px solid #ebeef5;
-}
-.section-title {
-  font-size: 14px;
-  font-weight: 600;
-  color: #303133;
-}
-.offshelf-reason {
-  color: #f56c6c;
-  font-weight: 500;
-}
-.rule-pane {
-  display: flex;
-  align-items: center;
-  width: 600px;
-  background: #f1f2f5;
-  padding: 12px 20px;
-  margin-top: 10px;
-}
-.image-preview {
-  width: 80px;
-  height: 80px;
-  border: 1px solid #d9d9d9;
-  border-radius: 4px;
-  overflow: hidden;
-}
-.preview-img {
-  width: 100%;
-  height: 100%;
-  object-fit: cover;
-}
-.no-data {
-  color: #c0c4cc;
-  font-size: 13px;
-}
-.rich-text-view {
-  border: 1px solid #dcdfe6;
-  border-radius: 4px;
-  padding: 8px 12px;
-  min-height: 80px;
-  font-size: 14px;
-  color: #606266;
-  line-height: 1.6;
-}
-.step-navigation {
-  display: flex;
-  justify-content: space-between;
-  margin-top: 20px;
-  padding-top: 20px;
-  border-top: 1px solid #ebeef5;
-}
-.audit-action-card {
-  margin-top: 8px;
-}
-.action-bar {
-  display: flex;
-  justify-content: center;
-  gap: 12px;
-  margin-top: 20px;
-  padding-top: 20px;
-  border-top: 1px solid #ebeef5;
-}
-</style>

+ 0 - 752
pc/src/views/AuditEquityProduct.vue

@@ -1,752 +0,0 @@
-<template>
-  <div class="audit-equity-product">
-    <el-card>
-      <div slot="header" class="header-title">
-        <span>审核权益</span>
-      </div>
-
-      <!-- 步骤条 -->
-      <el-steps :active="activeStep" simple style="margin-bottom: 20px;">
-        <el-step title="填写权益信息" icon="el-icon-info"></el-step>
-        <el-step title="设置规则" icon="el-icon-setting"></el-step>
-        <el-step title="更多设置" icon="el-icon-monitor"></el-step>
-      </el-steps>
-
-      <!-- 步骤内容 -->
-      <div v-show="activeStep === 0">
-        <!-- 基本信息 -->
-        <el-card shadow="never" class="section-card">
-          <div slot="header" class="section-title">基本信息</div>
-          <el-form :model="form" label-width="120px" label-position="left">
-            <el-row :gutter="20">
-              <el-col :span="8">
-                <el-form-item label="权益来源">
-                  <el-radio-group v-model="form.source" disabled>
-                    <el-radio-button label="银行自有"></el-radio-button>
-                    <el-radio-button label="荣数商品"></el-radio-button>
-                    <el-radio-button label="第三方"></el-radio-button>
-                  </el-radio-group>
-                </el-form-item>
-              </el-col>
-              <el-col :span="8" v-if="form.source === '荣数商品'">
-                <el-form-item label="选择权益">
-                  <el-input :value="form.rongshuProduct" readonly></el-input>
-                </el-form-item>
-              </el-col>
-              <el-col :span="8" v-if="form.source === '银行自有'">
-                <el-form-item label="权益类型">
-                  <el-radio-group v-model="form.productType" disabled>
-                    <el-radio-button label="券码"></el-radio-button>
-                  </el-radio-group>
-                </el-form-item>
-              </el-col>
-              <el-col :span="8" v-if="form.source === '银行自有'">
-                <el-form-item label="商户名称">
-                  <el-input :value="form.merchantName" readonly></el-input>
-                </el-form-item>
-              </el-col>
-            </el-row>
-            <el-row :gutter="20">
-              <el-col :span="8" v-if="form.source === '第三方'">
-                <el-form-item label="外部权益链接">
-                  <el-select v-model="form.externalLink" placeholder="请选择外部权益链接" disabled>
-                    <el-option label="太古地产" value="太古地产"></el-option>
-                    <el-option label="龙腾" value="龙腾"></el-option>
-                    <el-option label="h5链接" value="h5链接"></el-option>
-                    <el-option label="小程序链接" value="小程序链接"></el-option>
-                  </el-select>
-                </el-form-item>
-              </el-col>
-              <el-col :span="8" v-if="form.source === '第三方' && form.externalLink === 'h5链接'" :gutter="20">
-                <el-form-item label="h5链接">
-                  <el-input v-model="form.customLink" placeholder="请输入h5链接" readonly></el-input>
-                </el-form-item>
-              </el-col>
-              <el-col :span="8" v-if="form.source === '第三方'">
-                <el-form-item label-width="140px">
-                  <span slot="label" style="white-space: nowrap;">是否有详情页<el-tooltip content="没有详情页时直接从首页/列表页跳转" placement="top"><i class="el-icon-question tip-icon"></i></el-tooltip></span>
-                  <el-radio-group v-model="form.externalJumpMethod" disabled>
-                    <el-radio-button label="没有"></el-radio-button>
-                    <el-radio-button label="有"></el-radio-button>
-                  </el-radio-group>
-                </el-form-item>
-              </el-col>
-            </el-row>
-            <el-row :gutter="20" v-if="form.source === '第三方' && form.externalLink === '小程序链接'">
-              <el-col :span="8">
-                <el-form-item label="小程序appid">
-                  <el-input v-model="form.miniAppId" readonly></el-input>
-                </el-form-item>
-              </el-col>
-              <el-col :span="8">
-                <el-form-item label="小程序页面地址" label-width="140px">
-                  <el-input v-model="form.miniAppPath" readonly></el-input>
-                </el-form-item>
-              </el-col>
-            </el-row>
-
-            <template v-if="form.source === '银行自有' || form.source === '荣数商品'">
-              <el-row :gutter="20" v-if="form.source === '荣数商品'">
-                <el-col :span="8">
-                  <el-form-item label="库存数">
-                    <el-input :value="form.stockCount" readonly></el-input>
-                  </el-form-item>
-                </el-col>
-              </el-row>
-              <el-row :gutter="20">
-                <el-col :span="8" v-if="form.source === '银行自有'">
-                  <el-form-item label="SIP商品编号">
-                    <el-input v-model="form.skuNo" readonly></el-input>
-                  </el-form-item>
-                </el-col>
-                <el-col :span="8">
-                  <el-form-item label="权益名称">
-                    <el-input v-model="form.equityName" readonly></el-input>
-                  </el-form-item>
-                </el-col>
-                <el-col :span="8">
-                  <el-form-item label="权益副标题">
-                    <el-input v-model="form.subTitle" readonly></el-input>
-                  </el-form-item>
-                </el-col>
-              </el-row>
-              <el-row :gutter="20">
-                <el-col :span="8">
-                  <el-form-item>
-                    <span slot="label">权益图标
-                      <el-tooltip content="展示在权益首页和权益列表页" placement="top">
-                        <i class="el-icon-question tip-icon"></i>
-                      </el-tooltip>
-                    </span>
-                    <div v-if="form.listIcon && form.listIcon.length" class="image-preview">
-                      <img :src="form.listIcon[0]" class="preview-img" />
-                    </div>
-                    <span v-else class="no-data">暂无图片</span>
-                  </el-form-item>
-                </el-col>
-              </el-row>
-            </template>
-
-            <template v-else-if="form.source === '第三方'">
-              <el-row :gutter="20">
-                <el-col :span="8">
-                  <el-form-item label="权益名称">
-                    <el-input v-model="form.equityName" readonly></el-input>
-                  </el-form-item>
-                </el-col>
-                <el-col :span="8">
-                  <el-form-item label="权益副标题">
-                    <el-input v-model="form.subTitle" readonly></el-input>
-                  </el-form-item>
-                </el-col>
-              </el-row>
-              <el-row :gutter="20" v-if="form.externalJumpMethod === '没有'">
-                <el-col :span="8">
-                  <el-form-item>
-                    <span slot="label">权益图标
-                      <el-tooltip content="展示在权益首页和权益列表页" placement="top">
-                        <i class="el-icon-question tip-icon"></i>
-                      </el-tooltip>
-                    </span>
-                    <div v-if="form.listIcon && form.listIcon.length" class="image-preview">
-                      <img :src="form.listIcon[0]" class="preview-img" />
-                    </div>
-                    <span v-else class="no-data">暂无图片</span>
-                  </el-form-item>
-                </el-col>
-              </el-row>
-              <el-row v-if="form.externalJumpMethod === '有'" :gutter="20">
-                <el-col :span="8">
-                  <el-form-item>
-                    <span slot="label">权益图标
-                      <el-tooltip content="展示在权益首页和权益列表页" placement="top">
-                        <i class="el-icon-question tip-icon"></i>
-                      </el-tooltip>
-                    </span>
-                    <div v-if="form.listIcon && form.listIcon.length" class="image-preview">
-                      <img :src="form.listIcon[0]" class="preview-img" />
-                    </div>
-                    <span v-else class="no-data">暂无图片</span>
-                  </el-form-item>
-                </el-col>
-              </el-row>
-            </template>
-          </el-form>
-        </el-card>
-
-        <!-- 详情信息 -->
-        <el-card v-if="shouldShowDetailCard" shadow="never" class="section-card">
-          <div slot="header" class="section-title">详情信息</div>
-          <el-form :model="form" label-width="120px" label-position="left">
-            <el-row :gutter="20">
-              <el-col :span="8">
-                <el-form-item label="权益主图">
-                  <div v-if="form.mainImage && form.mainImage.length" class="image-preview">
-                    <img :src="form.mainImage[0]" class="preview-img" />
-                  </div>
-                  <span v-else class="no-data">暂无图片</span>
-                </el-form-item>
-              </el-col>
-            </el-row>
-            <el-row :gutter="20">
-              <el-col :span="12">
-                <el-form-item label="权益介绍">
-                  <el-input
-                    v-model="form.introduction"
-                    type="textarea"
-                    :rows="4"
-                    readonly
-                  ></el-input>
-                </el-form-item>
-              </el-col>
-            </el-row>
-            <el-row :gutter="20">
-              <el-col :span="12">
-                <el-form-item label="权益有效期">
-                  <el-input
-                    :value="form.validityType === 'fromGrantPlan' ? '取自发放计划' : (form.validityText || '-')"
-                    readonly
-                  ></el-input>
-                </el-form-item>
-              </el-col>
-            </el-row>
-            <el-row :gutter="20">
-              <el-col :span="12">
-                <el-form-item label="权益内容">
-                  <div class="rich-text-view" v-html="form.overview || '暂无内容'"></div>
-                </el-form-item>
-              </el-col>
-              <el-col :span="12">
-                <el-form-item label="权益说明">
-                  <div class="rich-text-view" v-html="form.details || '暂无内容'"></div>
-                </el-form-item>
-              </el-col>
-            </el-row>
-            <template v-if="form.source === '银行自有' || form.source === '荣数商品'">
-              <el-row :gutter="20">
-                <el-col :span="24">
-                  <el-form-item label="下单成功短信">
-                    <el-switch v-model="form.smsEnabled" disabled></el-switch>
-                    <template v-if="form.smsEnabled">
-                      <div style="width: 600px;background: #f1f2f5;padding: 20px;margin-top: 10px;">
-                        <el-select
-                          v-model="form.smsTemplateId"
-                          placeholder="请选择短信模板"
-                          style="width: 100%"
-                          disabled
-                        >
-                          <el-option
-                            v-for="item in smsTemplateOptions"
-                            :key="item.id"
-                            :label="item.name"
-                            :value="item.id"
-                          />
-                        </el-select>
-                        <div v-if="form.smsTemplate" class="sms-template-preview">{{ form.smsTemplate }}</div>
-                      </div>
-                    </template>
-                  </el-form-item>
-                </el-col>
-              </el-row>
-              <el-row :gutter="20">
-                <el-col :span="8">
-                  <el-form-item label="领取成功按钮">
-                    <el-input :value="form.successButtonText" readonly></el-input>
-                  </el-form-item>
-                </el-col>
-              </el-row>
-            </template>
-          </el-form>
-        </el-card>
-
-      </div>
-
-      <div v-show="activeStep === 1">
-        <!-- 设置规则 -->
-        <el-card shadow="never" class="section-card">
-          <div slot="header" class="section-title">设置规则</div>
-          
-          <!-- 展示规则 -->
-          <div class="rule-section">
-            <h4>展示规则</h4>
-            <el-form :model="form" label-width="120px" label-position="left">
-              <el-form-item label="展示时间限制">
-                <el-switch v-model="form.showTimeEnabled" disabled></el-switch>
-                <template v-if="form.showTimeEnabled">
-                  <div style="display: flex;width: 600px;background: #f1f2f5;padding: 20px;margin-top: 10px;">
-                    <el-input :value="form.showStartTime" readonly style="width: 100%"></el-input>
-                    <div style="width: 50px;text-align: center;">-</div>
-                    <el-input :value="form.showEndTime" readonly style="width: 100%"></el-input>
-                  </div>
-                </template>
-              </el-form-item>
-              <el-form-item label="展示客群限制">
-                <el-switch v-model="form.showGroupEnabled" disabled></el-switch>
-                <template v-if="form.showGroupEnabled">
-                  <div style="display: flex;width: 600px;background: #f1f2f5;padding: 20px;margin-top: 10px;">
-                    <el-input :value="(form.showGroupTagIds || []).join('、')" readonly></el-input>
-                  </div>
-                </template>
-              </el-form-item>
-            </el-form>
-          </div>
-
-          <!-- 领取规则 -->
-          <div class="rule-section">
-            <h4>领取规则</h4>
-            <el-form :model="form" label-width="120px" label-position="left">
-              <el-form-item label="领取时间限制">
-                <el-switch v-model="form.claimTimeEnabled" disabled></el-switch>
-                <template v-if="form.claimTimeEnabled">
-                  <div style="display: flex;width: 600px;background: #f1f2f5;padding: 20px;margin-top: 10px;">
-                    <el-input :value="form.claimStartTime" readonly style="width: 100%"></el-input>
-                    <div style="width: 50px;text-align: center;">-</div>
-                    <el-input :value="form.claimEndTime" readonly style="width: 100%"></el-input>
-                  </div>
-                </template>
-              </el-form-item>
-              <el-form-item label="领取客群限制">
-                <el-switch v-model="form.claimGroupEnabled" disabled></el-switch>
-                <template v-if="form.claimGroupEnabled">
-                  <div style="width: 600px;background: #f1f2f5;padding: 20px;margin-top: 10px;">
-                    <el-alert
-                      title="需在【发放计划】中选择权益发放,未发放的权益移动端不展示"
-                      type="info"
-                      :closable="false"
-                      show-icon
-                      style="margin-bottom: 12px;"
-                    ></el-alert>
-                    <div style="font-size: 14px;color: #606266;margin-bottom: 8px;font-weight: 500;">不满足提示</div>
-                    <el-form-item label="提示内容" label-width="80px" style="margin-bottom: 12px;">
-                      <el-input :value="form.claimDenyMessage" readonly></el-input>
-                    </el-form-item>
-                    <el-form-item label="跳转按钮文案" label-width="100px" style="margin-bottom: 12px;">
-                      <el-input :value="form.claimDenyButtonText" readonly></el-input>
-                    </el-form-item>
-                    <el-form-item label="跳转按钮链接" label-width="100px" style="margin-bottom: 0;">
-                      <el-input :value="form.claimDenyButtonLink" readonly></el-input>
-                    </el-form-item>
-                  </div>
-                </template>
-              </el-form-item>
-            </el-form>
-          </div>
-        </el-card>
-      </div>
-
-      <div v-show="activeStep === 2">
-        <!-- 显示页面 -->
-        <el-card shadow="never" class="section-card">
-          <div slot="header" class="section-title">显示页面</div>
-          <el-form :model="form" label-width="120px" label-position="left">
-            <el-form-item label="权益首页/列表页">
-              <el-radio-group v-model="form.showOnHome" disabled>
-                <el-radio label="显示">显示</el-radio>
-                <el-radio label="不显示">不显示</el-radio>
-              </el-radio-group>
-            </el-form-item>
-            <el-form-item label="组装页">
-              <el-radio-group v-model="form.showOnAssembly" disabled>
-                <el-radio label="显示">显示</el-radio>
-                <el-radio label="不显示">不显示</el-radio>
-              </el-radio-group>
-              <div
-                v-if="form.showOnAssembly === '显示'"
-                style="width: 600px;background: #f1f2f5;padding: 20px;margin-top: 10px;"
-              >
-                <el-input :value="form.assemblyPages.join(',')" readonly></el-input>
-              </div>
-            </el-form-item>
-          </el-form>
-        </el-card>
-
-        <!-- 分享设置 -->
-        <el-card shadow="never" class="section-card">
-          <div slot="header" class="section-title">分享设置</div>
-          <el-form :model="form" label-width="120px" label-position="left">
-            <el-form-item label="是否支持分享">
-              <el-switch v-model="form.shareEnabled" disabled></el-switch>
-              <template v-if="form.shareEnabled">
-                <div style="width: 600px;background: #f1f2f5;padding: 20px;margin-top: 10px;">
-                  <el-form-item label="分享名称" label-width="100px" style="margin-bottom: 16px;">
-                    <el-input :value="form.shareName" readonly></el-input>
-                  </el-form-item>
-                  <el-form-item label="分享图片" label-width="100px" style="margin-bottom: 0;">
-                    <div v-if="form.shareImage && form.shareImage.length" class="image-preview">
-                      <img :src="(form.shareImage[0] && form.shareImage[0].url) || ''" class="preview-img" />
-                    </div>
-                    <span v-else class="no-data">暂无图片</span>
-                  </el-form-item>
-                </div>
-              </template>
-            </el-form-item>
-          </el-form>
-        </el-card>
-      </div>
-
-      <!-- 步骤导航 -->
-      <div class="step-navigation">
-        <el-button
-          v-if="activeStep > 0"
-          @click="prevStep"
-          icon="el-icon-arrow-left"
-        >上一步</el-button>
-        <el-button
-          v-if="activeStep < 2"
-          type="primary"
-          @click="nextStep"
-          icon="el-icon-arrow-right"
-          style="margin-left: auto;"
-        >下一步</el-button>
-      </div>
-
-      <div class="action-footer">
-        <el-button @click="goBack">返回</el-button>
-        <template v-if="activeStep === 2">
-          <el-button type="danger" @click="handleReject">审核拒绝</el-button>
-          <el-button type="primary" @click="handleApprove">审核通过</el-button>
-        </template>
-      </div>
-    </el-card>
-  </div>
-</template>
-
-<script>
-export default {
-  name: 'AuditEquityProduct',
-  data() {
-    return {
-      activeStep: 0,
-      smsTemplateOptions: [
-        { id: 'SMS_TPL_001', name: '通用权益支付成功模板', content: '【银行】您购买的${productName}已支付成功,卡号:${exchangeNum},卡密:${exchangePwd},有效期至${expireDate},详情:${qrCodeDownPaths}' },
-        { id: 'SMS_TPL_002', name: '券码类短信模板', content: '【银行】尊敬的客户,您的${productName}购买成功,券码为${exchangeNum},请于${expireDate}前使用。' },
-        { id: 'SMS_TPL_003', name: '实物类短信模板', content: '【银行】您购买的${productName}已受理,我们将尽快安排发货,感谢您的支持。' }
-      ],
-      form: {
-        source: '银行自有',
-        rongshuProduct: '',
-        productType: '券码',
-        faceValueThreshold: null,
-        faceValueDiscount: null,
-        stockCount: null,
-        shareEnabled: false,
-        shareImage: [],
-        shareName: '',
-        externalJumpMethod: '没有',
-        externalLink: '太古地产',
-        customLink: '',
-        miniAppId: '',
-        miniAppPath: '',
-        skuNo: '',
-        equityName: '',
-        productId: '',
-        merchantName: '',
-        subTitle: '',
-        listIcon: [],
-        mainImage: [],
-        introduction: '',
-        validityType: 'fromGrantPlan',
-        validityText: '',
-        overview: '',
-        details: '',
-        smsEnabled: false,
-        smsTemplateId: '',
-        smsTemplate: '',
-        successButtonText: '',
-        showTimeEnabled: false,
-        showStartTime: '',
-        showEndTime: '',
-        showGroupEnabled: false,
-        showGroupTagIds: [],
-        claimTimeEnabled: false,
-        claimStartTime: '',
-        claimEndTime: '',
-        claimGroupEnabled: false,
-        claimGroupIsPotential: false,
-        claimGroupTagId: '',
-        claimDenyMessage: '',
-        claimDenyButtonText: '',
-        claimDenyButtonLink: '',
-        showOnHome: '显示',
-        showOnAssembly: '不显示',
-        assemblyPages: []
-      }
-    }
-  },
-  computed: {
-    shouldShowDetailCard() {
-      const s = this.form.source
-      if (s === '银行自有' || s === '荣数商品') return true
-      if (s === '第三方' && this.form.externalJumpMethod === '有') return true
-      return false
-    }
-  },
-  created() {
-    this.loadData()
-  },
-  methods: {
-    loadData() {
-      const id = this.$route.params.id
-      // TODO: 根据 id 从接口获取数据,这里用 mock 数据示例
-      const mockMap = {
-        'Q100001': {
-          source: '银行自有',
-          externalJumpMethod: '',
-          externalLink: '',
-          customLink: '',
-        miniAppId: '',
-        miniAppPath: '',
-          skuNo: 'SKU001',
-          equityName: '权益A',
-          subTitle: '副标题示例',
-          listIcon: [],
-          mainImage: [],
-          overview: '<p>权益内容内容</p>',
-          details: '<p>权益说明内容</p>',
-          smsEnabled: true,
-          smsTemplateId: 'SMS_TPL_001',
-          smsTemplate: '【银行】您购买的${productName}已支付成功,卡号:${exchangeNum},卡密:${exchangePwd},有效期至${expireDate},详情:${qrCodeDownPaths}',
-          successButtonText: '马上去使用',
-          showTimeEnabled: true,
-          showStartTime: '2026-01-01 00:00:00',
-          showEndTime: '2026-12-31 23:59:59',
-          showGroupEnabled: false,
-          showGroupTagIds: [],
-          claimTimeEnabled: true,
-          claimStartTime: '2026-01-01 00:00:00',
-          claimEndTime: '2026-12-31 23:59:59',
-          claimGroupEnabled: false,
-          claimGroupTagId: '',
-          claimDenyMessage: '',
-          claimDenyButtonText: '',
-          claimDenyButtonLink: ''
-        },
-        'Q100002': {
-          source: '第三方',
-          externalJumpMethod: '没有',
-          externalLink: '太古地产',
-          customLink: '',
-        miniAppId: '',
-        miniAppPath: '',
-          skuNo: 'SKU002',
-          equityName: '权益B',
-          subTitle: '外部列表页副标题',
-          listIcon: [],
-          mainImage: [],
-          overview: '',
-          details: '',
-          smsEnabled: false,
-          smsTemplateId: '',
-          smsTemplate: '',
-          successButtonText: '',
-          showTimeEnabled: false,
-          showStartTime: '',
-          showEndTime: '',
-          showGroupEnabled: true,
-          showGroupTagIds: ['客群A'],
-          claimTimeEnabled: true,
-          claimStartTime: '2026-02-01 00:00:00',
-          claimEndTime: '2026-11-30 23:59:59',
-          claimGroupEnabled: true,
-          claimGroupIsPotential: false,
-          claimGroupTagId: '客群奖励活动A',
-          claimDenyMessage: '您暂不满足领取条件',
-          claimDenyButtonText: '了解更多',
-          claimDenyButtonLink: 'https://example.com'
-        },
-        'Q100003': {
-          source: '第三方',
-          externalJumpMethod: '有',
-          externalLink: '龙腾',
-          customLink: '',
-        miniAppId: '',
-        miniAppPath: '',
-          skuNo: 'SKU003',
-          equityName: '权益C',
-          subTitle: '外部详情页副标题',
-          listIcon: [],
-          mainImage: [],
-          overview: '<p>详情页权益内容</p>',
-          details: '<p>详情页权益说明</p>',
-          smsEnabled: false,
-          smsTemplateId: '',
-          smsTemplate: '',
-          successButtonText: '',
-          showTimeEnabled: true,
-          showStartTime: '2026-03-01 00:00:00',
-          showEndTime: '2026-10-31 23:59:59',
-          showGroupEnabled: false,
-          showGroupTagIds: [],
-          claimTimeEnabled: false,
-          claimStartTime: '',
-          claimEndTime: '',
-          claimGroupEnabled: false,
-          claimGroupTagId: '',
-          claimDenyMessage: '',
-          claimDenyButtonText: '',
-          claimDenyButtonLink: ''
-        },
-        'Q100004': {
-          source: '第三方',
-          externalJumpMethod: '有',
-          externalLink: 'h5链接',
-          customLink: 'https://custom-link.com',
-          skuNo: 'SKU004',
-          equityName: '权益D',
-          subTitle: '自定义链接副标题',
-          listIcon: [],
-          mainImage: [],
-          overview: '<p>自定义链接权益内容</p>',
-          details: '<p>自定义链接权益说明</p>',
-          smsEnabled: false,
-          smsTemplateId: '',
-          smsTemplate: '',
-          successButtonText: '',
-          showTimeEnabled: false,
-          showStartTime: '',
-          showEndTime: '',
-          showGroupEnabled: false,
-          showGroupTagIds: [],
-          claimTimeEnabled: false,
-          claimStartTime: '',
-          claimEndTime: '',
-          claimGroupEnabled: false,
-          claimGroupTagId: '',
-          claimDenyMessage: '',
-          claimDenyButtonText: '',
-          claimDenyButtonLink: ''
-        }
-      }
-      if (mockMap[id]) {
-        this.form = { ...this.form, ...mockMap[id] }
-      }
-    },
-    goBack() {
-      this.$router.go(-1)
-    },
-    handleReject() {
-      this.$confirm('确定要审核拒绝吗?', '提示', {
-        confirmButtonText: '确定',
-        cancelButtonText: '取消',
-        type: 'warning'
-      }).then(() => {
-        this.$message.success('审核拒绝成功')
-        this.$router.push('/customization/hsbc/equity-products')
-      }).catch(() => {
-        this.$message.info('已取消审核拒绝')
-      })
-    },
-    nextStep() {
-      if (this.activeStep < 2) {
-        this.activeStep++
-      }
-    },
-    prevStep() {
-      if (this.activeStep > 0) {
-        this.activeStep--
-      }
-    },
-    handleApprove() {
-      this.$confirm('确定要审核通过吗?', '提示', {
-        confirmButtonText: '确定',
-        cancelButtonText: '取消',
-        type: 'success'
-      }).then(() => {
-        this.$message.success('审核通过成功')
-        this.$router.push('/customization/hsbc/equity-products')
-      }).catch(() => {
-        this.$message.info('已取消审核通过')
-      })
-    }
-  }
-}
-</script>
-
-<style scoped>
-.audit-equity-product {
-  padding: 20px;
-}
-.tip-icon {
-  color: #909399;
-  cursor: pointer;
-  font-size: 14px;
-}
-
-.header-title {
-  font-size: 18px;
-  font-weight: bold;
-}
-
-.section-card {
-  margin-top: 20px;
-}
-
-.section-title {
-  font-size: 16px;
-  font-weight: bold;
-}
-
-.image-preview {
-  display: inline-block;
-}
-
-.preview-img {
-  width: 100px;
-  height: 100px;
-  object-fit: cover;
-  border: 1px solid #ddd;
-}
-
-.no-data {
-  color: #999;
-  font-style: italic;
-}
-
-.rich-text-view {
-  padding: 10px;
-  border: 1px solid #ddd;
-  border-radius: 4px;
-  min-height: 100px;
-  background-color: #f9f9f9;
-}
-
-.rule-section {
-  margin-bottom: 24px;
-}
-.rule-section h4 {
-  font-size: 14px;
-  font-weight: 600;
-  color: #606266;
-  margin-bottom: 16px;
-  border-bottom: 1px solid #ebeef5;
-  padding-bottom: 8px;
-}
-
-.step-navigation {
-  display: flex;
-  justify-content: space-between;
-  margin-top: 20px;
-  padding-top: 20px;
-  border-top: 1px solid #ebeef5;
-}
-
-.action-footer {
-  display: flex;
-  justify-content: center;
-  gap: 10px;
-  margin-top: 20px;
-}
-
-.sms-template-preview {
-  margin-top: 12px;
-  padding: 12px;
-  background: #fff;
-  border: 1px dashed #dcdfe6;
-  border-radius: 4px;
-  font-size: 13px;
-  color: #606266;
-  line-height: 1.6;
-  white-space: pre-wrap;
-  word-break: break-all;
-}
-</style>

+ 0 - 167
pc/src/views/AuditList.vue

@@ -1,167 +0,0 @@
-<template>
-  <div class="audit-list">
-    <el-card>
-      <el-tabs v-model="activeTab" @tab-click="loadData">
-        <el-tab-pane label="待审核" name="pending"></el-tab-pane>
-        <el-tab-pane label="审核通过" name="approved"></el-tab-pane>
-        <el-tab-pane label="审核驳回" name="rejected"></el-tab-pane>
-      </el-tabs>
-      <div class="search-section">
-        <div class="search-row">
-          <el-form :model="searchForm" label-width="80px" inline>
-            <el-form-item label="权益编号:">
-              <el-input v-model="searchForm.bizId" placeholder="请输入" style="width: 150px"></el-input>
-            </el-form-item>
-            <div class="button-group" style="margin-left: 20px;">
-              <el-button type="primary" @click="loadData">查询</el-button>
-              <el-button @click="handleReset">重置</el-button>
-            </div>
-          </el-form>
-        </div>
-      </div>
-
-      <el-table :data="tableData" stripe style="width: 100%">
-        <el-table-column label="操作" min-width="140" fixed="left">
-          <template slot-scope="scope">
-            <el-button
-              v-if="activeTab === 'pending'"
-              type="text"
-              size="small"
-              @click="goDetail(scope.row.id)"
-            >审批</el-button>
-            <el-button
-              v-else
-              type="text"
-              size="small"
-              @click="goDetail(scope.row.id)"
-            >查看</el-button>
-          </template>
-        </el-table-column>
-        <el-table-column prop="bizName" label="权益名称" min-width="140" show-overflow-tooltip></el-table-column>
-        <el-table-column prop="bizId" label="权益编号" min-width="140"></el-table-column>
-        <el-table-column label="审核类型" min-width="140">
-          <template slot-scope="scope">
-            <el-tag :type="auditTypeTag(scope.row.auditType)" size="small">
-              {{ auditTypeLabel(scope.row.auditType) }}
-            </el-tag>
-          </template>
-        </el-table-column>
-        <el-table-column prop="bizModuleName" label="业务模块" min-width="140"></el-table-column>
-        <el-table-column prop="submitterName" label="提交人" min-width="140"></el-table-column>
-        <el-table-column prop="submitTime" label="提交时间" min-width="180"></el-table-column>
-        <el-table-column prop="auditorName" label="审核人" min-width="140">
-          <template slot-scope="scope">
-            <span>{{ scope.row.auditorName || '-' }}</span>
-          </template>
-        </el-table-column>
-        <el-table-column prop="auditTime" label="审核时间" min-width="180">
-          <template slot-scope="scope">
-            <span>{{ scope.row.auditTime || '-' }}</span>
-          </template>
-        </el-table-column>
-      </el-table>
-
-      <div class="pagination-section">
-        <span>Total {{ tableData.length }}</span>
-        <el-pagination
-          layout="prev, pager, next, sizes, jumper"
-          :page-size="10"
-          :page-sizes="[10, 20, 50]"
-          :total="tableData.length"
-        ></el-pagination>
-      </div>
-    </el-card>
-  </div>
-</template>
-
-<script>
-import request from '@/utils/request'
-
-export default {
-  name: 'AuditList',
-  data() {
-    return {
-      activeTab: 'pending',
-      searchForm: {
-        bizId: ''
-      },
-      tableData: []
-    }
-  },
-  created() {
-    this.loadData()
-  },
-  activated() {
-    this.loadData()
-  },
-  watch: {
-    $route(to) {
-      if (to.path === '/customization/hsbc/audit') {
-        this.loadData()
-      }
-    }
-  },
-  methods: {
-    loadData() {
-      const params = new URLSearchParams()
-      params.append('auditStatus', this.activeTab)
-      if (this.searchForm.bizId) params.append('bizId', this.searchForm.bizId)
-      request.get('/api/audit/list?' + params.toString()).then(res => {
-        this.tableData = res.data || []
-      }).catch(() => {})
-    },
-    handleReset() {
-      this.searchForm = { bizId: '' }
-      this.loadData()
-    },
-    auditTypeLabel(t) {
-      return { new: '新增', offshelf: '下架' }[t] || t
-    },
-    auditTypeTag(t) {
-      return { new: 'success', offshelf: 'danger' }[t] || ''
-    },
-    auditStatusLabel(s) {
-      return { pending: '待审核', approved: '已通过', rejected: '已驳回' }[s] || s
-    },
-    auditStatusTag(s) {
-      return { pending: 'warning', approved: 'success', rejected: 'danger' }[s] || ''
-    },
-    goDetail(id) {
-      this.$router.push(`/customization/hsbc/audit/${id}`)
-    }
-  }
-}
-</script>
-
-<style scoped>
-.audit-list {
-  padding: 0;
-}
-.search-section {
-  display: flex;
-  flex-direction: column;
-}
-.search-row {
-  margin-bottom: 0;
-}
-.search-row .el-form {
-  display: flex;
-  flex-wrap: wrap;
-  gap: 0;
-}
-.search-row .el-form-item {
-  margin-right: 0;
-  margin-bottom: 8px;
-}
-.button-group {
-  display: flex;
-  gap: 5px;
-}
-.pagination-section {
-  display: flex;
-  justify-content: flex-end;
-  align-items: center;
-  gap: 20px;
-  margin-top: 20px;
-}
-</style>

+ 0 - 144
pc/src/views/CampaignManagement.vue

@@ -1,144 +0,0 @@
-<template>
-  <div class="campaign-management">
-    <el-card>
-      <div slot="header" class="search-section">
-        <div class="search-row">
-          <el-form :model="searchForm" label-width="130px" inline>
-            <el-form-item label="Campaign ID:">
-              <el-input v-model="searchForm.id" placeholder="支持精确查询" clearable style="width:150px" />
-            </el-form-item>
-            <el-form-item label="Campaign 名称:">
-              <el-input v-model="searchForm.name" placeholder="关键字" clearable style="width:150px" />
-            </el-form-item>
-            <el-form-item label="Campaign 负责人:">
-              <el-input v-model="searchForm.owner" placeholder="关键字" clearable style="width:150px" />
-            </el-form-item>
-            <el-form-item label="跟进员工:">
-              <el-input v-model="searchForm.staff" placeholder="员工姓名" clearable style="width:150px" />
-            </el-form-item>
-            <div class="button-group">
-              <el-button type="primary" @click="handleSearch">查询</el-button>
-              <el-button @click="handleReset">重置</el-button>
-            </div>
-          </el-form>
-        </div>
-        <div class="action-row">
-          <el-button type="primary" icon="el-icon-plus" @click="openModal('create')">新建 Campaign</el-button>
-        </div>
-      </div>
-
-      <el-table :data="tableData" stripe border style="width:100%">
-        <el-table-column label="操作" width="200" fixed="left">
-          <template slot-scope="scope">
-            <el-button type="text" size="small" @click="openModal('edit', scope.row)">编辑</el-button>
-            <el-button type="text" size="small" @click="openModal('view', scope.row)">查看</el-button>
-            <el-button type="text" size="small" style="color:#F56C6C" @click="handleDelete(scope.row)">删除</el-button>
-          </template>
-        </el-table-column>
-        <el-table-column prop="id" label="Campaign ID" width="120" align="center" />
-        <el-table-column prop="name" label="Campaign 名称" width="190" />
-        <el-table-column prop="owner" label="Campaign 负责人" width="160" align="center" />
-        <el-table-column label="跟进员工" width="120" align="center">
-          <template slot-scope="scope">
-            <span :class="{ 'empty-text': !scope.row.staff }">{{ scope.row.staff || '-' }}</span>
-          </template>
-        </el-table-column>
-        <el-table-column prop="createTime" label="创建时间" width="180" align="center" />
-        <el-table-column prop="updateTime" label="更新时间" width="180" align="center" />
-      </el-table>
-    </el-card>
-
-    <!-- 新建/编辑/查看弹窗 -->
-    <el-dialog :title="modalTitle" :visible.sync="modalVisible" width="520px" :close-on-click-modal="false">
-      <el-form label-width="130px">
-        <el-form-item v-if="modalMode !== 'create'" label="Campaign ID">
-          <el-input :value="form.id" disabled />
-        </el-form-item>
-        <el-form-item label="Campaign 名称" required>
-          <el-input v-model="form.name" placeholder="请输入Campaign 名称" :disabled="isViewMode" />
-        </el-form-item>
-        <el-form-item label="Campaign 负责人" required>
-          <el-input v-model="form.owner" placeholder="请输入Campaign 负责人" :disabled="isViewMode" />
-        </el-form-item>
-        <el-form-item label="跟进员工">
-          <el-input v-model="form.staff" placeholder="请输入跟进员工姓名" :disabled="isViewMode" />
-        </el-form-item>
-      </el-form>
-      <span v-if="!isViewMode" slot="footer">
-        <el-button @click="modalVisible = false">取消</el-button>
-        <el-button type="primary" @click="submitModal">确定</el-button>
-      </span>
-    </el-dialog>
-  </div>
-</template>
-
-<script>
-import request from '@/utils/request'
-
-export default {
-  name: 'CampaignManagement',
-  data() {
-    return {
-      searchForm: { id: '', name: '', owner: '', staff: '' },
-      tableData: [],
-      modalVisible: false,
-      modalMode: 'create',
-      form: { id: '', name: '', owner: '', staff: '' }
-    }
-  },
-  computed: {
-    modalTitle() {
-      return this.modalMode === 'create' ? '新建 Campaign' : this.modalMode === 'edit' ? '编辑 Campaign' : '查看 Campaign'
-    },
-    isViewMode() {
-      return this.modalMode === 'view'
-    }
-  },
-  created() {
-    this.fetchList()
-  },
-  methods: {
-    async fetchList() {
-      const res = await request.get('/api/campaign/list', { params: this.searchForm })
-      this.tableData = res.data
-    },
-    handleSearch() { this.fetchList() },
-    handleReset() {
-      this.searchForm = { id: '', name: '', owner: '', staff: '' }
-      this.fetchList()
-    },
-    async handleDelete(row) {
-      await this.$confirm('您确定要删除此条数据吗?删除后将无法恢复。', '确认删除', { type: 'warning', confirmButtonText: '确认删除', cancelButtonText: '取消' })
-      await request.post('/api/campaign/delete', { id: row.id })
-      this.$message.success('删除成功')
-      this.fetchList()
-    },
-    openModal(mode, row) {
-      this.modalMode = mode
-      if (mode === 'create') {
-        this.form = { id: '', name: '', owner: '', staff: '' }
-      } else {
-        this.form = { ...row }
-      }
-      this.modalVisible = true
-    },
-    async submitModal() {
-      if (!this.form.name || !this.form.owner) {
-        this.$message.warning('请填写Campaign 名称和负责人')
-        return
-      }
-      await request.post('/api/campaign/save', this.form)
-      this.$message.success(this.modalMode === 'create' ? '新建成功' : '保存成功')
-      this.modalVisible = false
-      this.fetchList()
-    }
-  }
-}
-</script>
-
-<style scoped>
-.search-section .search-row { margin-bottom: 0; }
-.search-section .action-row { margin-top: 15px; }
-.button-group { display: inline-block; margin-left: 20px; }
-.empty-text { color: #c0c4cc; }
-</style>

+ 0 - 230
pc/src/views/CashDiscountGrant.vue

@@ -1,230 +0,0 @@
-<template>
-  <div class="cash-discount-grant">
-    <el-card>
-      <div slot="header" class="search-section">
-        <el-form :model="searchForm" label-width="100px">
-          <el-row :gutter="16">
-            <el-col :span="8">
-              <el-form-item label="客户号:">
-                <el-input v-model="searchForm.customerId" placeholder="请输入客户号" clearable></el-input>
-              </el-form-item>
-            </el-col>
-            <el-col :span="8">
-              <el-form-item label="立减金类型:">
-                <el-select v-model="searchForm.discountType" placeholder="请选择立减金类型" clearable style="width: 100%;">
-                  <el-option v-for="item in discountTypeOptions" :key="item" :label="item" :value="item"></el-option>
-                </el-select>
-              </el-form-item>
-            </el-col>
-            <el-col :span="8">
-              <el-form-item label="发放时间:">
-                <el-date-picker
-                  v-model="searchForm.grantDateRange"
-                  type="daterange"
-                  range-separator="至"
-                  start-placeholder="开始日期"
-                  end-placeholder="结束日期"
-                  value-format="yyyy-MM-dd"
-                  style="width: 100%;"
-                ></el-date-picker>
-              </el-form-item>
-            </el-col>
-          </el-row>
-          <el-row :gutter="16">
-            <el-col :span="8">
-              <el-form-item label="是否领取:">
-                <el-select v-model="searchForm.claimed" placeholder="请选择" clearable style="width: 100%;">
-                  <el-option label="是" value="是"></el-option>
-                  <el-option label="否" value="否"></el-option>
-                </el-select>
-              </el-form-item>
-            </el-col>
-            <el-col :span="8">
-              <el-form-item label="是否使用:">
-                <el-select v-model="searchForm.used" placeholder="请选择" clearable style="width: 100%;">
-                  <el-option label="是" value="是"></el-option>
-                  <el-option label="否" value="否"></el-option>
-                </el-select>
-              </el-form-item>
-            </el-col>
-            <el-col :span="8" class="action-col">
-              <el-button type="primary" @click="handleSearch">查询</el-button>
-              <el-button @click="handleReset">重置</el-button>
-              <el-button type="success" icon="el-icon-download" @click="handleExport">导出</el-button>
-            </el-col>
-          </el-row>
-        </el-form>
-      </div>
-
-      <el-table :data="pagedData" stripe border style="width: 100%;" v-loading="loading">
-        <el-table-column prop="grantTime" label="发放时间" width="170"></el-table-column>
-        <el-table-column prop="customerId" label="客户号" width="160"></el-table-column>
-        <el-table-column prop="discountType" label="立减金类型" width="130"></el-table-column>
-        <el-table-column prop="binRestriction" label="是否有bin卡限制" width="140">
-          <template slot-scope="scope">
-            <el-tag :type="scope.row.binRestriction === '是' ? 'warning' : 'info'" size="small">{{ scope.row.binRestriction }}</el-tag>
-          </template>
-        </el-table-column>
-        <el-table-column prop="grantAmount" label="发放金额" width="110">
-          <template slot-scope="scope">
-            <span>¥{{ scope.row.grantAmount }}</span>
-          </template>
-        </el-table-column>
-        <el-table-column prop="grantReason" label="发放事由" min-width="160" show-overflow-tooltip></el-table-column>
-        <el-table-column prop="claimed" label="是否领取" width="100">
-          <template slot-scope="scope">
-            <el-tag :type="scope.row.claimed === '是' ? 'success' : 'danger'" size="small">{{ scope.row.claimed }}</el-tag>
-          </template>
-        </el-table-column>
-        <el-table-column prop="claimTime" label="领取时间" width="160"></el-table-column>
-        <el-table-column prop="validPeriod" label="使用有效期" width="220"></el-table-column>
-        <el-table-column prop="used" label="是否使用" width="100">
-          <template slot-scope="scope">
-            <el-tag :type="scope.row.used === '是' ? 'success' : 'danger'" size="small">{{ scope.row.used }}</el-tag>
-          </template>
-        </el-table-column>
-        <el-table-column prop="useTime" label="使用时间" width="160"></el-table-column>
-      </el-table>
-
-      <el-pagination
-        style="margin-top: 20px; text-align: right;"
-        @current-change="handlePageChange"
-        :current-page="currentPage"
-        :page-size="pageSize"
-        layout="total, prev, pager, next, jumper"
-        :total="filteredData.length"
-      ></el-pagination>
-    </el-card>
-  </div>
-</template>
-
-<script>
-const DISCOUNT_TYPES = ['借记卡', '信用卡']
-const GRANT_REASONS = ['新开户礼遇', '月度消费达标奖励', '生日专属福利', '双十一营销活动', '服务补偿', '升级Premier礼遇', '推荐好友奖励', '季度活跃奖励']
-
-function pad(n) { return String(n).padStart(2, '0') }
-
-function randomDateTime(baseDate, offsetDays) {
-  const d = new Date(baseDate)
-  d.setDate(d.getDate() - Math.floor(Math.random() * offsetDays))
-  d.setHours(Math.floor(Math.random() * 14 + 8), Math.floor(Math.random() * 60), Math.floor(Math.random() * 60))
-  return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())} ${pad(d.getHours())}:${pad(d.getMinutes())}:${pad(d.getSeconds())}`
-}
-
-function generateMockData() {
-  const list = []
-  const base = new Date(2026, 4, 15)
-  const amounts = [10, 20, 30, 50, 88, 100, 128, 200]
-
-  for (let i = 1; i <= 65; i++) {
-    const grantTime = randomDateTime(base, 90)
-    const grantDate = grantTime.slice(0, 10)
-    const validEnd = new Date(grantDate)
-    validEnd.setDate(validEnd.getDate() + 30 + Math.floor(Math.random() * 60))
-    const validEndStr = `${validEnd.getFullYear()}-${pad(validEnd.getMonth() + 1)}-${pad(validEnd.getDate())}`
-
-    const claimed = Math.random() > 0.25 ? '是' : '否'
-    const used = claimed === '是' && Math.random() > 0.35 ? '是' : '否'
-
-    list.push({
-      id: i,
-      customerId: 'CUST' + String(100000 + Math.floor(Math.random() * 900000)),
-      discountType: DISCOUNT_TYPES[Math.floor(Math.random() * DISCOUNT_TYPES.length)],
-      binRestriction: Math.random() > 0.5 ? '是' : '否',
-      grantTime: grantTime,
-      grantDate: grantDate,
-      grantAmount: amounts[Math.floor(Math.random() * amounts.length)],
-      grantReason: GRANT_REASONS[Math.floor(Math.random() * GRANT_REASONS.length)],
-      validPeriod: claimed === '是' ? `${grantDate} 至 ${validEndStr}` : '-',
-      claimed: claimed,
-      claimTime: claimed === '是' ? randomDateTime(base, 60) : '-',
-      used: used,
-      useTime: used === '是' ? randomDateTime(base, 30) : '-'
-    })
-  }
-  list.sort((a, b) => b.grantTime.localeCompare(a.grantTime))
-  return list
-}
-
-export default {
-  name: 'CashDiscountGrant',
-  data() {
-    return {
-      searchForm: {
-        customerId: '',
-        discountType: '',
-        grantDateRange: [],
-        claimed: '',
-        used: ''
-      },
-      loading: false,
-      currentPage: 1,
-      pageSize: 10,
-      tableData: generateMockData(),
-      discountTypeOptions: DISCOUNT_TYPES
-    }
-  },
-  computed: {
-    filteredData() {
-      let list = this.tableData
-      const customerId = this.searchForm.customerId.trim()
-      if (customerId) {
-        list = list.filter(item => item.customerId.includes(customerId))
-      }
-      if (this.searchForm.discountType) {
-        list = list.filter(item => item.discountType === this.searchForm.discountType)
-      }
-      if (this.searchForm.grantDateRange && this.searchForm.grantDateRange.length === 2) {
-        const [start, end] = this.searchForm.grantDateRange
-        list = list.filter(item => item.grantDate >= start && item.grantDate <= end)
-      }
-      if (this.searchForm.claimed) {
-        list = list.filter(item => item.claimed === this.searchForm.claimed)
-      }
-      if (this.searchForm.used) {
-        list = list.filter(item => item.used === this.searchForm.used)
-      }
-      return list
-    },
-    pagedData() {
-      const start = (this.currentPage - 1) * this.pageSize
-      return this.filteredData.slice(start, start + this.pageSize)
-    }
-  },
-  methods: {
-    handleSearch() {
-      this.currentPage = 1
-    },
-    handleReset() {
-      this.searchForm = {
-        customerId: '',
-        discountType: '',
-        grantDateRange: [],
-        claimed: '',
-        used: ''
-      }
-      this.currentPage = 1
-    },
-    handlePageChange(page) {
-      this.currentPage = page
-    },
-    handleExport() {
-      this.$message.success('导出任务已提交,请稍后在下载中心查看')
-    }
-  }
-}
-</script>
-
-<style scoped>
-.search-section {
-  display: flex;
-  flex-direction: column;
-  align-items: stretch;
-}
-
-.action-col {
-  display: flex;
-  align-items: center;
-  gap: 8px;
-}
-</style>

+ 0 - 313
pc/src/views/ChannelManagement.vue

@@ -1,313 +0,0 @@
-<template>
-  <div class="channel-management">
-    <el-card>
-      <div slot="header" class="search-section">
-        <el-form :model="searchForm" label-width="120px" inline class="search-form">
-          <div class="search-row">
-            <el-form-item label="Channel ID:">
-              <el-input v-model="searchForm.id" placeholder="支持精确查询" clearable style="width:160px" />
-            </el-form-item>
-            <el-form-item label="Channel 类型:">
-              <el-select v-model="searchForm.type" clearable placeholder="全部" style="width:160px">
-                <el-option v-for="t in typeOptions" :key="t" :label="t" :value="t" />
-              </el-select>
-            </el-form-item>
-            <el-form-item label="Channel 类别:">
-              <el-input v-model="searchForm.category" placeholder="关键字" clearable style="width:160px" />
-            </el-form-item>
-            <el-form-item label="Channel 名称:">
-              <el-input v-model="searchForm.name" placeholder="关键字" clearable style="width:160px" />
-            </el-form-item>
-          </div>
-          <div class="search-row">
-            <el-form-item label="Channel 子名称:">
-              <el-input v-model="searchForm.subName" placeholder="关键字" clearable style="width:160px" />
-            </el-form-item>
-            <el-form-item label="跟进员工:">
-              <el-input v-model="searchForm.owner" placeholder="员工姓名" clearable style="width:160px" />
-            </el-form-item>
-            <el-form-item label=" ">
-              <el-button type="primary" @click="handleSearch">查询</el-button>
-              <el-button @click="handleReset">重置</el-button>
-            </el-form-item>
-          </div>
-        </el-form>
-        <div class="action-row">
-          <el-button type="primary" icon="el-icon-plus" @click="openModal('create')">新建 Channel</el-button>
-        </div>
-      </div>
-
-      <el-table :data="tableData" stripe border style="width:100%">
-        <el-table-column label="操作" width="200" fixed="left">
-          <template slot-scope="scope">
-            <el-button type="text" size="small" @click="openModal('edit', scope.row)">编辑</el-button>
-            <el-button type="text" size="small" @click="openModal('view', scope.row)">查看</el-button>
-            <el-button type="text" size="small" style="color:#F56C6C" @click="handleDelete(scope.row)">删除</el-button>
-          </template>
-        </el-table-column>
-        <el-table-column prop="id" label="Channel ID" width="120" align="center" />
-        <el-table-column prop="type" label="类型" width="150" align="center" />
-        <el-table-column prop="category" label="类别" width="190" />
-        <el-table-column prop="name" label="名称" width="190" />
-        <el-table-column label="子名称" width="160">
-          <template slot-scope="scope">
-            <span :class="{ 'empty-text': !scope.row.subName }">{{ scope.row.subName || '-' }}</span>
-          </template>
-        </el-table-column>
-        <el-table-column label="跟进员工" width="120" align="center">
-          <template slot-scope="scope">
-            <span :class="{ 'empty-text': !scope.row.owner }">{{ scope.row.owner || '-' }}</span>
-          </template>
-        </el-table-column>
-        <el-table-column prop="createTime" label="创建时间" width="180" align="center" />
-        <el-table-column prop="updateTime" label="更新时间" width="180" align="center" />
-      </el-table>
-    </el-card>
-
-    <!-- 新建/编辑/查看弹窗 -->
-    <el-dialog :title="modalTitle" :visible.sync="modalVisible" width="560px" :close-on-click-modal="false">
-      <el-form label-width="130px">
-        <el-form-item v-if="modalMode !== 'create'" label="Channel ID">
-          <el-input :value="form.id" disabled />
-        </el-form-item>
-
-        <!-- Channel 类型 -->
-        <el-form-item label="Channel 类型" required>
-          <div class="inline-field">
-            <el-select v-model="form.type" placeholder="请选择类型" :disabled="isViewMode" @change="onTypeChange" style="flex:1">
-              <el-option v-for="t in typeOptions" :key="t" :label="t" :value="t" />
-            </el-select>
-            <template v-if="!isViewMode">
-              <el-button size="small" @click="openPanel('type', 'add')">新增</el-button>
-              <el-button size="small" @click="openPanel('type', 'edit')">编辑</el-button>
-            </template>
-          </div>
-          <div v-if="activePanel === 'type'" class="quick-panel">
-            <el-input v-model="panelInput" :placeholder="panelMode === 'edit' ? '请输入新的内容' : '请输入新的类型'" size="small" />
-            <el-button size="small" type="primary" @click="savePanel('type')">保存</el-button>
-            <el-button size="small" @click="activePanel = ''">取消</el-button>
-          </div>
-        </el-form-item>
-
-        <!-- Channel 类别 -->
-        <el-form-item label="Channel 类别" required>
-          <div class="inline-field">
-            <el-select v-model="form.category" placeholder="请先选择类型" :disabled="isViewMode || !form.type" @change="onCategoryChange" style="flex:1">
-              <el-option v-for="c in categoryOptions" :key="c" :label="c" :value="c" />
-            </el-select>
-            <template v-if="!isViewMode">
-              <el-button size="small" @click="openPanel('category', 'add')">新增</el-button>
-              <el-button size="small" @click="openPanel('category', 'edit')">编辑</el-button>
-            </template>
-          </div>
-          <div v-if="activePanel === 'category'" class="quick-panel">
-            <el-input v-model="panelInput" :placeholder="panelMode === 'edit' ? '请输入新的内容' : '请输入新的类别'" size="small" />
-            <el-button size="small" type="primary" @click="savePanel('category')">保存</el-button>
-            <el-button size="small" @click="activePanel = ''">取消</el-button>
-          </div>
-        </el-form-item>
-
-        <!-- Channel 名称 -->
-        <el-form-item label="Channel 名称" required>
-          <div class="inline-field">
-            <el-select v-model="form.name" placeholder="请先选择类别" :disabled="isViewMode || !form.category" style="flex:1">
-              <el-option v-for="n in nameOptions" :key="n" :label="n" :value="n" />
-            </el-select>
-            <template v-if="!isViewMode">
-              <el-button size="small" @click="openPanel('name', 'add')">新增</el-button>
-              <el-button size="small" @click="openPanel('name', 'edit')">编辑</el-button>
-            </template>
-          </div>
-          <div v-if="activePanel === 'name'" class="quick-panel">
-            <el-input v-model="panelInput" :placeholder="panelMode === 'edit' ? '请输入新的内容' : '请输入新的名称'" size="small" />
-            <el-button size="small" type="primary" @click="savePanel('name')">保存</el-button>
-            <el-button size="small" @click="activePanel = ''">取消</el-button>
-          </div>
-        </el-form-item>
-
-        <el-form-item label="Channel 子名称">
-          <el-input v-model="form.subName" placeholder="请输入Channel 子名称" :disabled="isViewMode" />
-        </el-form-item>
-        <el-form-item label="跟进员工">
-          <el-input v-model="form.owner" placeholder="请输入跟进员工姓名" :disabled="isViewMode" />
-        </el-form-item>
-      </el-form>
-      <span v-if="!isViewMode" slot="footer">
-        <el-button @click="modalVisible = false">取消</el-button>
-        <el-button type="primary" @click="submitModal">确定</el-button>
-      </span>
-    </el-dialog>
-  </div>
-</template>
-
-<script>
-import request from '@/utils/request'
-
-export default {
-  name: 'ChannelManagement',
-  data() {
-    return {
-      searchForm: { id: '', type: '', category: '', name: '', subName: '', owner: '' },
-      tableData: [],
-      channelDict: {},
-      modalVisible: false,
-      modalMode: 'create',
-      form: { id: '', type: '', category: '', name: '', subName: '', owner: '' },
-      activePanel: '',
-      panelMode: 'add',
-      panelInput: ''
-    }
-  },
-  computed: {
-    modalTitle() {
-      return this.modalMode === 'create' ? '新建 Channel' : this.modalMode === 'edit' ? '编辑 Channel' : '查看 Channel'
-    },
-    isViewMode() {
-      return this.modalMode === 'view'
-    },
-    typeOptions() {
-      return Object.keys(this.channelDict)
-    },
-    categoryOptions() {
-      if (!this.form.type || !this.channelDict[this.form.type]) return []
-      return Object.keys(this.channelDict[this.form.type])
-    },
-    nameOptions() {
-      if (!this.form.type || !this.form.category) return []
-      return (this.channelDict[this.form.type] || {})[this.form.category] || []
-    }
-  },
-  created() {
-    this.fetchDict()
-    this.fetchList()
-  },
-  methods: {
-    async fetchDict() {
-      const res = await request.get('/api/channel/dict')
-      this.channelDict = res.data
-    },
-    async fetchList() {
-      const res = await request.get('/api/channel/list', { params: this.searchForm })
-      this.tableData = res.data
-    },
-    handleSearch() { this.fetchList() },
-    handleReset() {
-      this.searchForm = { id: '', type: '', category: '', name: '', subName: '', owner: '' }
-      this.fetchList()
-    },
-    async handleDelete(row) {
-      await this.$confirm('您确定要删除此条数据吗?删除后将无法恢复。', '确认删除', { type: 'warning', confirmButtonText: '确认删除', cancelButtonText: '取消' })
-      await request.post('/api/channel/delete', { id: row.id })
-      this.$message.success('删除成功')
-      this.fetchList()
-    },
-    openModal(mode, row) {
-      this.modalMode = mode
-      this.activePanel = ''
-      this.panelInput = ''
-      if (mode === 'create') {
-        this.form = { id: '', type: '', category: '', name: '', subName: '', owner: '' }
-      } else {
-        this.form = { ...row }
-      }
-      this.modalVisible = true
-    },
-    onTypeChange() {
-      this.form.category = ''
-      this.form.name = ''
-    },
-    onCategoryChange() {
-      this.form.name = ''
-    },
-    openPanel(target, mode) {
-      if (target === 'category' && !this.form.type) {
-        this.$message.warning('请先选择 Channel 类型')
-        return
-      }
-      if (target === 'name' && (!this.form.type || !this.form.category)) {
-        this.$message.warning('请先选择 Channel 类型和类别')
-        return
-      }
-      if (mode === 'edit') {
-        if (target === 'type' && !this.form.type) { this.$message.warning('请先选择要编辑的类型'); return }
-        if (target === 'category' && !this.form.category) { this.$message.warning('请先选择要编辑的类别'); return }
-        if (target === 'name' && !this.form.name) { this.$message.warning('请先选择要编辑的名称'); return }
-      }
-      this.activePanel = target
-      this.panelMode = mode
-      this.panelInput = mode === 'edit'
-        ? (target === 'type' ? this.form.type : target === 'category' ? this.form.category : this.form.name)
-        : ''
-    },
-    async savePanel(target) {
-      const value = this.panelInput.trim()
-      if (!value) { this.$message.warning('请输入内容'); return }
-      const oldValue = target === 'type' ? this.form.type : target === 'category' ? this.form.category : this.form.name
-      const existingValues = this.getPanelExistingValues(target)
-      const exists = existingValues.some(item => item === value)
-      if (this.panelMode === 'add' && exists) {
-        this.$message.warning(`${this.getPanelTargetName(target)}已存在,不能重复新增`)
-        return
-      }
-      if (this.panelMode === 'edit' && value !== oldValue && exists) {
-        this.$message.warning(`${this.getPanelTargetName(target)}已存在,不能修改为重复值`)
-        return
-      }
-      const res = await request.post('/api/channel/dict/save', {
-        target,
-        type: this.form.type,
-        category: this.form.category,
-        oldValue,
-        newValue: value,
-        mode: this.panelMode
-      })
-      this.channelDict = res.data
-      if (this.panelMode === 'add') {
-        if (target === 'type') this.form.type = value
-        else if (target === 'category') this.form.category = value
-        else this.form.name = value
-      } else {
-        if (target === 'type') this.form.type = value
-        else if (target === 'category') this.form.category = value
-        else this.form.name = value
-      }
-      this.activePanel = ''
-      this.$message.success(this.panelMode === 'add' ? '新增成功' : '编辑成功')
-      this.fetchList()
-    },
-    getPanelExistingValues(target) {
-      if (target === 'type') return Object.keys(this.channelDict)
-      if (target === 'category') return Object.keys(this.channelDict[this.form.type] || {})
-      return ((this.channelDict[this.form.type] || {})[this.form.category] || [])
-    },
-    getPanelTargetName(target) {
-      if (target === 'type') return 'Channel 类型'
-      if (target === 'category') return 'Channel 类别'
-      return 'Channel 名称'
-    },
-    async submitModal() {
-      if (!this.form.type || !this.form.category || !this.form.name) {
-        this.$message.warning('请完整选择类型、类别和名称')
-        return
-      }
-      await request.post('/api/channel/save', this.form)
-      this.$message.success(this.modalMode === 'create' ? '新建成功' : '保存成功')
-      this.modalVisible = false
-      this.fetchList()
-    }
-  }
-}
-</script>
-
-<style scoped>
-.search-form .search-row { display: flex; flex-wrap: wrap; margin-bottom: 0; }
-.search-section .action-row { margin-top: 15px; }
-.empty-text { color: #c0c4cc; }
-
-.inline-field { display: flex; gap: 8px; align-items: center; }
-.quick-panel {
-  display: flex; gap: 8px; align-items: center;
-  margin-top: 8px; padding: 10px;
-  background: #f0f7ff; border: 1px solid #d9ecff; border-radius: 4px;
-}
-</style>

+ 0 - 348
pc/src/views/CreditCardEquity.vue

@@ -1,348 +0,0 @@
-<template>
-  <div class="credit-card-equity">
-    <el-card>
-      <div class="search-section" slot="header">
-        <div class="search-row">
-          <el-form :model="searchForm" label-width="80px" inline>
-            <el-form-item label="权益名称:">
-              <el-input v-model="searchForm.equityName" placeholder="请输入" style="width: 150px"></el-input>
-            </el-form-item>
-            <div class="button-group" style="margin-left: 20px;">
-              <el-button type="primary" @click="handleSearch">查询</el-button>
-              <el-button @click="handleReset">重置</el-button>
-            </div>
-          </el-form>
-        </div>
-      </div>
-
-      <el-table :data="filteredTableData" stripe style="width: 100%">
-        <el-table-column label="操作" width="280" fixed="left">
-          <template slot-scope="scope">
-            <div class="row-actions">
-              <el-popover
-                placement="bottom"
-                trigger="click"
-                width="240"
-                @show="onAdjustOrderShow(scope.$index)"
-                :ref="`adjustPopover-${scope.$index}`"
-              >
-                <div class="adjust-order-form">
-                  <div class="adjust-order-title">调整显示顺序</div>
-                  <div class="adjust-order-tip">移动端权益首页和列表页按照该顺序展示</div>
-                  <div class="adjust-order-input">
-                    <span>调整到第</span>
-                    <el-input-number
-                      v-model.number="adjustOrderPosition"
-                      :min="1"
-                      :max="tableData.length"
-                      size="mini"
-                      :controls="false"
-                      style="width: 70px;margin: 0 6px;"
-                    />
-                    <span>位</span>
-                  </div>
-                  <div class="adjust-order-actions">
-                    <el-button size="mini" @click="closeAdjustOrder(scope.$index)">取消</el-button>
-                    <el-button size="mini" type="primary" @click="confirmAdjustOrder(scope.$index)">确定</el-button>
-                  </div>
-                </div>
-                <el-button slot="reference" type="text" size="small">调整显示顺序</el-button>
-              </el-popover>
-              <el-button type="text" size="small" @click="openShowPagesDialog(scope.row)">设置显示页面</el-button>
-              <el-popover
-                placement="bottom"
-                trigger="click"
-                :ref="`morePopover-${scope.$index}`"
-              >
-                <div class="more-actions-menu">
-                  <div
-                    class="more-actions-item"
-                    @click="openLinkDialogFromMore(scope.row, scope.$index)"
-                  >生成链接</div>
-                </div>
-                <el-button slot="reference" type="text" size="small" icon="el-icon-more"></el-button>
-              </el-popover>
-            </div>
-          </template>
-        </el-table-column>
-        <el-table-column prop="equityName" label="权益名称" min-width="220" show-overflow-tooltip></el-table-column>
-        <el-table-column label="显示页面" min-width="240">
-          <template slot-scope="scope">
-            <template v-if="visiblePages(scope.row).length">
-              <el-tag
-                v-for="page in visiblePages(scope.row)"
-                :key="page"
-                size="small"
-                style="margin-right: 4px;"
-              >{{ page }}</el-tag>
-            </template>
-            <span v-else class="empty-text">-</span>
-          </template>
-        </el-table-column>
-      </el-table>
-
-      <div class="pagination-section">
-        <span>Total {{ filteredTableData.length }}</span>
-        <el-pagination
-          layout="prev, pager, next, sizes, jumper"
-          :page-size="10"
-          :page-sizes="[10, 20, 50]"
-          :total="filteredTableData.length"
-        ></el-pagination>
-      </div>
-    </el-card>
-
-    <show-pages-dialog
-      :visible.sync="showPagesDialogVisible"
-      :row="showPagesRow"
-      @confirm="onShowPagesConfirm"
-    ></show-pages-dialog>
-
-    <equity-link-dialog
-      :visible.sync="linkDialogVisible"
-      :id="linkTargetId"
-      path-name="equity/credit"
-      title="生成信用卡权益链接"
-    ></equity-link-dialog>
-  </div>
-</template>
-
-<script>
-import ShowPagesDialog from '@/components/ShowPagesDialog.vue'
-import EquityLinkDialog from '@/components/EquityLinkDialog.vue'
-
-export default {
-  name: 'CreditCardEquity',
-  components: { ShowPagesDialog, EquityLinkDialog },
-  data() {
-    return {
-      searchForm: {
-        equityName: ''
-      },
-      showPagesDialogVisible: false,
-      showPagesRow: null,
-      linkDialogVisible: false,
-      linkTargetId: '',
-      adjustOrderPosition: 1,
-      tableData: [
-        {
-          equityName: '白金卡机场贵宾厅',
-          productId: 'CC001',
-          skuNo: 'CCSKU001',
-          merchantName: '环亚机场贵宾',
-          createTime: '2026-01-10 09:32:15',
-          showOnHome: '显示',
-          showOnAssembly: '不显示',
-          assemblyPages: []
-        },
-        {
-          equityName: '钻石卡高尔夫体验',
-          productId: 'CC002',
-          skuNo: 'CCSKU002',
-          merchantName: '尊享高尔夫俱乐部',
-          createTime: '2026-01-15 14:08:42',
-          showOnHome: '显示',
-          showOnAssembly: '不显示',
-          assemblyPages: []
-        },
-        {
-          equityName: '金卡洗车服务',
-          productId: 'CC003',
-          skuNo: 'CCSKU003',
-          merchantName: '车之家精洗服务',
-          createTime: '2026-02-01 11:20:05',
-          showOnHome: '显示',
-          showOnAssembly: '不显示',
-          assemblyPages: []
-        }
-      ]
-    }
-  },
-  computed: {
-    visiblePages() {
-      return row => {
-        const pages = []
-        if (row.showOnHome === '显示') pages.push("权益首页/列表页")
-        if (row.showOnAssembly === '显示' && Array.isArray(row.assemblyPages)) {
-          pages.push(...row.assemblyPages)
-        }
-        return pages
-      }
-    },
-    filteredTableData() {
-      return this.tableData.filter(item => {
-        if (this.searchForm.equityName && !item.equityName.includes(this.searchForm.equityName)) return false
-        return true
-      })
-    }
-  },
-  methods: {
-    handleSearch() {
-      this.$message.success('已搜索')
-    },
-    handleReset() {
-      this.searchForm = {
-        equityName: ''
-      }
-    },
-    openShowPagesDialog(row) {
-      this.showPagesRow = row
-      this.showPagesDialogVisible = true
-    },
-    showLinkDialog(row) {
-      this.linkTargetId = row.productId
-      this.linkDialogVisible = true
-    },
-    openLinkDialogFromMore(row, index) {
-      const moreRef = this.getPopoverInstance(`morePopover-${index}`)
-      if (moreRef) {
-        moreRef.doClose()
-      }
-      this.showLinkDialog(row)
-    },
-    onShowPagesConfirm(values) {
-      if (this.showPagesRow) {
-        this.$set(this.showPagesRow, 'showOnHome', values.showOnHome)
-        this.$set(this.showPagesRow, 'showOnAssembly', values.showOnAssembly)
-        this.$set(this.showPagesRow, 'assemblyPages', [...values.assemblyPages])
-        this.$message.success('显示页面已更新')
-      }
-    },
-    getPopoverInstance(name) {
-      const ref = this.$refs[name]
-      if (!ref) return null
-      return Array.isArray(ref) ? ref[0] : ref
-    },
-    onAdjustOrderShow(index) {
-      this.adjustOrderPosition = index + 1
-    },
-    closeAdjustOrder(index) {
-      const adjustRef = this.getPopoverInstance(`adjustPopover-${index}`)
-      if (adjustRef) {
-        adjustRef.doClose()
-      }
-    },
-    confirmAdjustOrder(index) {
-      const target = Number(this.adjustOrderPosition)
-      if (!target || target < 1 || target > this.tableData.length) {
-        this.$message.warning('请输入有效的顺序位置')
-        return
-      }
-      const targetIndex = target - 1
-      if (targetIndex !== index) {
-        const moved = this.tableData.splice(index, 1)[0]
-        this.tableData.splice(targetIndex, 0, moved)
-      }
-      this.$message.success('顺序已调整')
-      this.closeAdjustOrder(index)
-    }
-  }
-}
-</script>
-
-<style scoped>
-.credit-card-equity {
-  padding: 0;
-}
-
-.search-section {
-  display: flex;
-  flex-direction: column;
-}
-
-.search-row {
-  margin-bottom: 15px;
-}
-
-.search-row .el-form {
-  display: flex;
-  flex-wrap: wrap;
-  gap: 0;
-}
-
-.search-row .el-form-item {
-  margin-right: 0;
-  margin-bottom: 0;
-}
-
-.button-group {
-  display: flex;
-  gap: 5px;
-}
-
-.pagination-section {
-  display: flex;
-  justify-content: flex-end;
-  align-items: center;
-  gap: 20px;
-  margin-top: 20px;
-}
-
-.row-actions {
-  display: flex;
-  align-items: center;
-  gap: 12px;
-}
-
-.empty-text {
-  color: #c0c4cc;
-}
-
-.row-actions .el-button + .el-button {
-  margin-left: 0;
-}
-</style>
-
-<style>
-.more-actions-menu {
-  display: flex;
-  flex-direction: column;
-  margin: -12px;
-}
-
-.more-actions-item {
-  padding: 8px 16px;
-  cursor: pointer;
-  font-size: 14px;
-  color: #606266;
-}
-
-.more-actions-item:hover {
-  background: #f5f7fa;
-  color: #409eff;
-}
-
-.adjust-order-form {
-  display: flex;
-  flex-direction: column;
-  gap: 12px;
-}
-
-.adjust-order-title {
-  font-size: 14px;
-  font-weight: 500;
-  color: #303133;
-}
-
-.adjust-order-tip {
-  font-size: 12px;
-  color: #909399;
-  line-height: 1.5;
-  padding: 8px 10px;
-  background: #f5f7fa;
-  border-radius: 4px;
-}
-
-.adjust-order-input {
-  display: flex;
-  align-items: center;
-  font-size: 14px;
-  color: #606266;
-}
-
-.adjust-order-actions {
-  display: flex;
-  justify-content: flex-end;
-  gap: 8px;
-}
-</style>

+ 0 - 268
pc/src/views/CrowdNameListEdit.vue

@@ -1,268 +0,0 @@
-<template>
-  <div class="crowd-name-list-edit">
-    <el-card>
-      <div slot="header" class="header">
-        <span class="title">{{ pageTitle }}</span>
-      </div>
-
-      <el-steps v-if="!isEdit" :active="step" finish-status="success" align-center class="steps">
-        <el-step title="基本信息" icon="el-icon-edit-outline" />
-        <el-step title="初始人员" icon="el-icon-user" />
-      </el-steps>
-
-      <!-- Step 1: 基本信息 -->
-      <div v-show="step === 0" class="section">
-        <div class="section-title">基本信息</div>
-        <el-form
-          ref="baseForm"
-          :model="form"
-          :rules="rules"
-          label-width="120px"
-          style="max-width: 1000px; margin: 0 auto"
-        >
-          <el-row :gutter="32">
-            <el-col :span="12">
-              <el-form-item label="客群名称" prop="name">
-                <el-input v-model="form.name" placeholder="请输入" maxlength="40" show-word-limit />
-              </el-form-item>
-            </el-col>
-            <el-col :span="12">
-              <el-form-item label="客群编号">
-                <el-input v-model="form.id" disabled suffix-icon="el-icon-circle-close" />
-              </el-form-item>
-            </el-col>
-            <el-col :span="24">
-              <el-form-item label="客群描述" prop="description">
-                <el-input
-                  v-model="form.description"
-                  type="textarea"
-                  :rows="4"
-                  placeholder="请输入"
-                  maxlength="200"
-                  show-word-limit
-                />
-              </el-form-item>
-            </el-col>
-          </el-row>
-        </el-form>
-      </div>
-
-      <!-- Step 2: 初始人员(仅新增可见) -->
-      <div v-show="step === 1 && !isEdit" class="section">
-        <div class="section-title">初始人员</div>
-        <div class="hint">至少添加 1 个客户号;可选择手动输入或上传文件。</div>
-
-        <div class="mode-switch">
-          <el-radio-group v-model="inputMode">
-            <el-radio-button label="manual">手动输入</el-radio-button>
-            <el-radio-button label="file">上传文件</el-radio-button>
-          </el-radio-group>
-        </div>
-
-        <div class="mode-content">
-          <el-input
-            v-if="inputMode === 'manual'"
-            v-model="form.manualText"
-            type="textarea"
-            :rows="8"
-            :placeholder="manualPlaceholder"
-          />
-
-          <el-upload
-            v-else
-            drag
-            action="#"
-            :auto-upload="false"
-            :show-file-list="false"
-            :on-change="handleFile"
-            accept=".txt,.csv"
-          >
-            <i class="el-icon-upload"></i>
-            <div class="el-upload__text">将文件拖到此处,或<em>点击选择文件</em></div>
-            <div slot="tip" class="el-upload__tip">
-              仅支持 .txt / .csv,每行一个客户号,UTF-8 编码
-              <span v-if="form.fileName" class="file-name">已选择:{{ form.fileName }}</span>
-            </div>
-          </el-upload>
-
-          <div class="member-stats" v-if="parsedMembers.length > 0">
-            已识别 <b>{{ parsedMembers.length }}</b> 个有效客户号
-          </div>
-        </div>
-      </div>
-
-      <div class="footer">
-        <el-button v-if="step === 0" @click="handleCancel">取消</el-button>
-        <el-button v-if="step === 1" @click="step = 0">上一步</el-button>
-        <el-button v-if="!isEdit && step === 0" type="primary" @click="handleNext">下一步</el-button>
-        <el-button v-if="isEdit && step === 0" type="primary" @click="handleSave">保存</el-button>
-        <el-button v-if="step === 1" type="primary" @click="handleSave">完成</el-button>
-      </div>
-    </el-card>
-  </div>
-</template>
-
-<script>
-import crowdNameListStore from '@/store/crowdNameListStore'
-
-const emptyForm = () => ({
-  id: '',
-  name: '',
-  description: '',
-  manualText: '',
-  fileMembers: [],
-  fileName: ''
-})
-
-export default {
-  name: 'CrowdNameListEdit',
-  data() {
-    return {
-      step: 0,
-      inputMode: 'manual',
-      form: emptyForm(),
-      current: null,
-      rules: {
-        name: [{ required: true, message: '请输入客群名称', trigger: 'blur' }],
-        description: [{ required: true, message: '请输入客群描述', trigger: 'blur' }]
-      }
-    }
-  },
-  computed: {
-    isEdit() {
-      return !!this.$route.params.id
-    },
-    pageTitle() {
-      return this.isEdit ? '编辑客群' : '新增客群'
-    },
-    manualPlaceholder() {
-      return '示例:\n00001\n00002\n00003'
-    },
-    parsedMembers() {
-      if (this.inputMode === 'file') return this.form.fileMembers
-      const text = this.form.manualText || ''
-      const seen = new Set()
-      const out = []
-      text.split(/\r?\n/).forEach((l) => {
-        const v = l.trim()
-        if (v && !seen.has(v)) {
-          seen.add(v)
-          out.push(v)
-        }
-      })
-      return out
-    }
-  },
-  created() {
-    if (this.isEdit) {
-      const target = crowdNameListStore.getById(this.$route.params.id)
-      if (!target) {
-        this.$message.error('客群不存在')
-        this.$router.replace('/customization/hsbc/crowd-tasks')
-        return
-      }
-      if (target.status === 'stopped') {
-        this.$message.warning('已停止名单不可编辑')
-        this.$router.replace('/customization/hsbc/crowd-tasks')
-        return
-      }
-      this.current = target
-      this.form = {
-        id: target.id,
-        name: target.name,
-        description: target.description,
-        manualText: '',
-        fileMembers: [],
-        fileName: ''
-      }
-    } else {
-      this.form.id = '保存后系统自动生成'
-    }
-  },
-  methods: {
-    handleNext() {
-      this.$refs.baseForm.validate((ok) => {
-        if (ok) this.step = 1
-      })
-    },
-    handleFile(file) {
-      const reader = new FileReader()
-      reader.onload = (e) => {
-        const text = e.target.result || ''
-        const seen = new Set()
-        const out = []
-        text.split(/\r?\n/).forEach((l) => {
-          const v = l.trim()
-          if (v && !seen.has(v)) {
-            seen.add(v)
-            out.push(v)
-          }
-        })
-        this.form.fileMembers = out
-        this.form.fileName = file.name
-        if (out.length === 0) {
-          this.$message.warning('文件未解析出有效客户号')
-        } else {
-          this.$message.success(`已解析 ${out.length} 个有效客户号`)
-        }
-      }
-      reader.readAsText(file.raw, 'utf-8')
-    },
-    handleSave() {
-      if (this.isEdit) {
-        this.$refs.baseForm.validate((ok) => {
-          if (!ok) return
-          crowdNameListStore.update(this.current.id, {
-            name: this.form.name,
-            description: this.form.description
-          })
-          this.$message.success('保存成功')
-          this.$router.push('/customization/hsbc/crowd-tasks')
-        })
-        return
-      }
-      // 新增路径
-      if (this.parsedMembers.length === 0) {
-        this.$message.error(
-          this.inputMode === 'file' ? '请上传文件并确认包含至少 1 个有效客户号' : '请至少输入 1 个客户号'
-        )
-        return
-      }
-      crowdNameListStore.create({
-        name: this.form.name,
-        description: this.form.description,
-        members: this.parsedMembers
-      })
-      this.$message.success(`创建成功,初始人员 ${this.parsedMembers.length} 人`)
-      this.$router.push('/customization/hsbc/crowd-tasks')
-    },
-    handleCancel() {
-      this.$router.push('/customization/hsbc/crowd-tasks')
-    }
-  }
-}
-</script>
-
-<style scoped>
-.crowd-name-list-edit { padding: 0; }
-.header { display: flex; align-items: center; justify-content: center; }
-.title { font-size: 16px; font-weight: 600; color: #409EFF; }
-
-.steps { margin: 16px 0 32px; }
-
-.section { margin-bottom: 24px; max-width: 1000px; margin-left: auto; margin-right: auto; }
-.section-title {
-  font-size: 14px; font-weight: 600; color: #303133;
-  background: #f5f7fa; padding: 10px 14px; border-radius: 4px;
-  margin-bottom: 16px;
-}
-.hint { color: #909399; font-size: 12px; margin-bottom: 12px; }
-.mode-switch { margin-bottom: 16px; }
-.mode-content { max-width: 480px; }
-.mode-content >>> .el-textarea__inner { font-family: 'Menlo', 'Consolas', monospace; }
-.file-name { margin-left: 10px; color: #67C23A; }
-.member-stats { margin-top: 12px; color: #67C23A; font-size: 13px; }
-
-.footer { text-align: center; margin-top: 24px; padding-top: 16px; border-top: 1px solid #ebeef5; }
-.footer .el-button { min-width: 100px; }
-</style>

+ 0 - 274
pc/src/views/CrowdTaskEdit.vue

@@ -1,274 +0,0 @@
-<template>
-  <div class="crowd-task-edit">
-    <el-card>
-      <div slot="header" class="header">
-        <span class="title">{{ pageTitle }}</span>
-      </div>
-
-      <el-steps :active="step" finish-status="success" align-center class="steps">
-        <el-step title="基本信息" icon="el-icon-edit-outline" />
-        <el-step title="规则组/奖励信息" icon="el-icon-set-up" />
-      </el-steps>
-
-      <!-- Step 1: 基本信息 -->
-      <div v-show="step === 0" class="section">
-        <el-form
-          ref="baseForm"
-          :model="form"
-          :rules="rules"
-          label-width="140px"
-          style="max-width: 1000px; margin: 0 auto"
-        >
-          <el-row :gutter="32">
-            <el-col :span="12">
-              <el-form-item label="客群名称" prop="name">
-                <el-input v-model="form.name" placeholder="请输入" maxlength="40" show-word-limit />
-              </el-form-item>
-            </el-col>
-            <el-col :span="12">
-              <el-form-item label="客群编号" prop="id">
-                <el-input v-model="form.id" disabled placeholder="系统自动生成" suffix-icon="el-icon-circle-close" />
-              </el-form-item>
-            </el-col>
-            <el-col :span="24">
-              <el-form-item label="客群描述" prop="description">
-                <el-input
-                  v-model="form.description"
-                  type="textarea"
-                  :rows="4"
-                  placeholder="请输入"
-                  maxlength="200"
-                  show-word-limit
-                />
-              </el-form-item>
-            </el-col>
-          </el-row>
-          <el-row :gutter="32">
-            <el-col :span="12">
-              <el-form-item label="客群任务开始日期" prop="startDate">
-                <el-date-picker
-                  v-model="form.startDate"
-                  type="date"
-                  placeholder="请选择日期"
-                  value-format="yyyy-MM-dd"
-                  style="width: 100%"
-                />
-              </el-form-item>
-            </el-col>
-            <el-col :span="12">
-              <el-form-item label="客群任务结束日期" prop="endDate">
-                <el-date-picker
-                  v-model="form.endDate"
-                  type="date"
-                  placeholder="请选择日期"
-                  value-format="yyyy-MM-dd"
-                  :picker-options="endDateOptions"
-                  style="width: 100%"
-                />
-              </el-form-item>
-            </el-col>
-          </el-row>
-        </el-form>
-      </div>
-
-      <!-- Step 2: 规则组信息 -->
-      <div v-show="step === 1" class="section">
-        <div class="section-title">规则组配置</div>
-        <el-form label-width="140px" style="max-width: 1000px; margin: 0 auto 16px">
-          <el-form-item label="规则组间运算逻辑" required>
-            <el-select v-model="form.ruleGroupLogic" placeholder="请选择" style="width: 320px">
-              <el-option label="and/且" value="AND" />
-              <el-option label="or/或" value="OR" />
-            </el-select>
-          </el-form-item>
-        </el-form>
-
-        <el-table :data="form.ruleGroups" border style="width: 100%" empty-text="暂无规则组,请点击下方加号新增">
-          <el-table-column prop="id" label="规则组编号" width="170" align="center" />
-          <el-table-column prop="name" label="规则组名称" min-width="160" show-overflow-tooltip />
-          <el-table-column prop="description" label="规则组描述" min-width="200" show-overflow-tooltip />
-          <el-table-column label="规则组内逻辑" width="120" align="center">
-            <template slot-scope="scope">{{ scope.row.logic === 'AND' ? 'and/且' : 'or/或' }}</template>
-          </el-table-column>
-          <el-table-column prop="ruleCount" label="规则个数" width="90" align="center" />
-          <el-table-column label="操作" width="90" align="center">
-            <template slot-scope="scope">
-              <el-button
-                type="danger" circle size="mini" icon="el-icon-delete" title="删除"
-                @click="removeRuleGroup(scope.$index)"
-              />
-            </template>
-          </el-table-column>
-        </el-table>
-
-        <div class="add-row">
-          <el-button type="primary" circle icon="el-icon-plus" @click="openAddDialog" />
-        </div>
-      </div>
-
-      <div class="footer">
-        <el-button v-if="step === 0" @click="handleCancel">取消</el-button>
-        <el-button v-if="step === 1" @click="step = 0">上一步</el-button>
-        <el-button v-if="step === 0" type="primary" @click="handleNext">下一步</el-button>
-        <el-button v-if="step === 1" type="primary" @click="handleSave">完成</el-button>
-      </div>
-    </el-card>
-
-    <add-rule-group-dialog
-      :visible.sync="dialogVisible"
-      :exclude-ids="selectedRuleGroupIds"
-      @save="onRuleGroupAdded"
-    />
-  </div>
-</template>
-
-<script>
-import crowdTaskStore from '@/store/crowdTaskStore'
-import AddRuleGroupDialog from '@/components/crowdTask/AddRuleGroupDialog.vue'
-
-const emptyForm = () => ({
-  name: '',
-  id: '',
-  description: '',
-  startDate: '',
-  endDate: '',
-  ruleGroupLogic: '',
-  ruleGroups: []
-})
-
-export default {
-  name: 'CrowdTaskEdit',
-  components: { AddRuleGroupDialog },
-  data() {
-    return {
-      step: 0,
-      form: emptyForm(),
-      current: null,
-      dialogVisible: false,
-      rules: {
-        name: [{ required: true, message: '请输入客群名称', trigger: 'blur' }],
-        description: [{ required: true, message: '请输入客群描述', trigger: 'blur' }],
-        startDate: [{ required: true, message: '请选择开始日期', trigger: 'change' }],
-        endDate: [{ required: true, message: '请选择结束日期', trigger: 'change' }]
-      }
-    }
-  },
-  computed: {
-    isEdit() {
-      return this.$route.name === 'CrowdTaskEdit'
-    },
-    isCopy() {
-      return this.$route.name === 'CrowdTaskCopy'
-    },
-    pageTitle() {
-      if (this.isEdit) return '编辑客群任务'
-      if (this.isCopy) return '复制客群任务'
-      return '新建客群任务'
-    },
-    selectedRuleGroupIds() {
-      return this.form.ruleGroups.map((r) => r.id)
-    },
-    endDateOptions() {
-      const start = this.form.startDate
-      return {
-        disabledDate(d) {
-          if (!start) return false
-          return d.getTime() < new Date(start).getTime()
-        }
-      }
-    }
-  },
-  created() {
-    if (this.isEdit || this.isCopy) {
-      const source = crowdTaskStore.getById(this.$route.params.id)
-      if (!source) {
-        this.$message.error('任务不存在')
-        this.$router.replace('/customization/hsbc/crowd-tasks')
-        return
-      }
-      this.current = source
-      this.form = {
-        name: this.isCopy ? source.name + '-副本' : source.name,
-        id: this.isCopy ? '复制保存后生成' : source.id,
-        description: source.description,
-        startDate: source.startDate,
-        endDate: source.endDate,
-        ruleGroupLogic: source.ruleGroupLogic,
-        ruleGroups: JSON.parse(JSON.stringify(source.ruleGroups || []))
-      }
-    } else {
-      this.form.id = '保存后系统自动生成'
-    }
-  },
-  methods: {
-    handleNext() {
-      this.$refs.baseForm.validate((ok) => {
-        if (!ok) return
-        if (this.form.startDate && this.form.endDate && this.form.endDate < this.form.startDate) {
-          this.$message.error('结束日期不能早于开始日期')
-          return
-        }
-        this.step = 1
-      })
-    },
-    openAddDialog() {
-      this.dialogVisible = true
-    },
-    onRuleGroupAdded(rg) {
-      this.form.ruleGroups.push(rg)
-      this.$message.success('已添加规则组')
-    },
-    removeRuleGroup(idx) {
-      this.$confirm('确定移除该规则组吗?', '提示', {
-        confirmButtonText: '移除', cancelButtonText: '取消', type: 'warning'
-      }).then(() => {
-        this.form.ruleGroups.splice(idx, 1)
-      }).catch(() => {})
-    },
-    handleSave() {
-      if (!this.form.ruleGroupLogic) {
-        this.$message.error('请选择规则组间运算逻辑')
-        return
-      }
-      if (this.form.ruleGroups.length === 0) {
-        this.$message.error('请至少添加一条规则组')
-        return
-      }
-      const payload = { ...this.form }
-      if (this.isEdit) {
-        // 校验状态下的编辑需回退到新建并重置运行信息
-        const resetRun = this.current.status === 'verifying'
-        crowdTaskStore.update(this.current.id, payload, { resetRun })
-        this.$message.success(resetRun ? '保存成功,任务回退至新建状态,需重新运行' : '保存成功')
-      } else {
-        crowdTaskStore.create(payload)
-        this.$message.success(this.isCopy ? '复制成功' : '创建成功')
-      }
-      this.$router.push('/customization/hsbc/crowd-tasks')
-    },
-    handleCancel() {
-      this.$router.push('/customization/hsbc/crowd-tasks')
-    }
-  }
-}
-</script>
-
-<style scoped>
-.crowd-task-edit { padding: 0; }
-.header { display: flex; align-items: center; justify-content: center; }
-.title { font-size: 16px; font-weight: 600; color: #409EFF; }
-
-.steps { margin: 16px 0 32px; }
-
-.section { margin-bottom: 24px; }
-.section-title {
-  font-size: 14px; font-weight: 600; color: #303133;
-  background: #f5f7fa; padding: 10px 14px; border-radius: 4px;
-  margin-bottom: 16px;
-}
-
-.add-row { display: flex; padding: 16px 0; }
-
-.footer { text-align: center; margin-top: 24px; padding-top: 16px; border-top: 1px solid #ebeef5; }
-.footer .el-button { min-width: 100px; }
-</style>

+ 0 - 329
pc/src/views/CrowdTaskList.vue

@@ -1,329 +0,0 @@
-<template>
-  <div class="crowd-task-list">
-    <el-card>
-      <div slot="header" class="search-section">
-        <div class="search-row">
-          <el-form :model="searchForm" label-width="90px" inline>
-            <el-form-item label="客群编号:">
-              <el-input v-model="searchForm.id" placeholder="请输入" clearable style="width: 180px" />
-            </el-form-item>
-            <el-form-item label="客群名称:">
-              <el-input v-model="searchForm.name" placeholder="请输入" clearable style="width: 180px" />
-            </el-form-item>
-            <el-form-item label="类型:">
-              <el-select v-model="searchForm.type" placeholder="全部" clearable style="width: 160px">
-                <el-option label="规则组圈选" value="rule_group" />
-                <el-option label="指定客户号" value="name_list" />
-              </el-select>
-            </el-form-item>
-            <el-form-item>
-              <el-button type="primary" @click="handleSearch">查询</el-button>
-            </el-form-item>
-          </el-form>
-        </div>
-        <div class="action-row">
-          <el-dropdown trigger="click" @command="handleCreateByType">
-            <el-button type="primary" icon="el-icon-plus">
-              新增任务<i class="el-icon-arrow-down el-icon--right"></i>
-            </el-button>
-            <el-dropdown-menu slot="dropdown">
-              <el-dropdown-item command="rule_group">规则组圈选</el-dropdown-item>
-              <el-dropdown-item command="name_list">指定客户号</el-dropdown-item>
-            </el-dropdown-menu>
-          </el-dropdown>
-        </div>
-      </div>
-
-      <el-table
-        :data="pagedData"
-        stripe border
-        style="width: 100%"
-      >
-        <el-table-column prop="id" label="客群编号" width="160" />
-        <el-table-column prop="name" label="客群名称" min-width="160" show-overflow-tooltip />
-        <el-table-column label="类型" width="120" align="center">
-          <template slot-scope="scope">
-            <el-tag :type="scope.row.type === 'rule_group' ? '' : 'warning'" size="small">
-              {{ typeLabel(scope.row.type) }}
-            </el-tag>
-          </template>
-        </el-table-column>
-        <el-table-column label="状态" width="100" align="center">
-          <template slot-scope="scope">
-            <el-tag :type="statusTagType(scope.row)" size="small">
-              {{ statusLabel(scope.row) }}
-            </el-tag>
-          </template>
-        </el-table-column>
-        <el-table-column label="人数" width="80" align="center">
-          <template slot-scope="scope">
-            <span v-if="scope.row.type === 'name_list'">{{ (scope.row.members || []).length }}</span>
-            <span v-else class="muted">-</span>
-          </template>
-        </el-table-column>
-        <el-table-column prop="creator" label="创建人" width="100" />
-        <el-table-column label="审核人" width="100">
-          <template slot-scope="scope">{{ scope.row.reviewer || '-' }}</template>
-        </el-table-column>
-        <el-table-column prop="createdAt" label="任务创建时间" width="170" />
-        <el-table-column label="任务开始/结束日期" width="190" align="center">
-          <template slot-scope="scope">
-            <span v-if="scope.row.type === 'rule_group'">
-              {{ scope.row.startDate }} / {{ scope.row.endDate }}
-            </span>
-            <span v-else class="muted">-</span>
-          </template>
-        </el-table-column>
-        <el-table-column prop="updatedAt" label="更新时间" width="170" />
-        <el-table-column label="操作" width="280" fixed="right" align="center">
-          <template slot-scope="scope">
-            <!-- 规则组圈选 -->
-            <template v-if="scope.row.type === 'rule_group'">
-              <el-button
-                v-if="canDoTask(scope.row, 'edit')"
-                type="primary" circle size="mini" icon="el-icon-edit" title="编辑"
-                @click.stop="handleEditTask(scope.row)"
-              />
-              <el-button
-                v-if="canDoTask(scope.row, 'run')"
-                type="success" circle size="mini" icon="el-icon-video-play" title="运行"
-                @click.stop="handleRun(scope.row)"
-              />
-              <el-button
-                v-if="canDoTask(scope.row, 'publish')"
-                type="success" circle size="mini" icon="el-icon-upload2" title="上线"
-                @click.stop="handlePublish(scope.row)"
-              />
-              <el-button
-                v-if="canDoTask(scope.row, 'stop')"
-                type="warning" circle size="mini" icon="el-icon-close" title="停止"
-                @click.stop="handleStop(scope.row)"
-              />
-              <el-button
-                v-if="canDoTask(scope.row, 'detail')"
-                type="info" circle size="mini" icon="el-icon-view" title="详情"
-                @click.stop="handleDetail(scope.row)"
-              />
-              <el-button
-                type="primary" plain circle size="mini" icon="el-icon-document-copy" title="复制"
-                @click.stop="handleCopy(scope.row)"
-              />
-            </template>
-            <!-- 指定客户号 -->
-            <template v-else>
-              <el-button
-                v-if="scope.row.status === 'draft'"
-                type="success" circle size="mini" icon="el-icon-check" title="启用"
-                @click.stop="handleActivate(scope.row)"
-              />
-              <el-button
-                v-if="scope.row.status !== 'stopped'"
-                type="primary" circle size="mini" icon="el-icon-edit" title="编辑"
-                @click.stop="handleEditNameList(scope.row)"
-              />
-              <el-button
-                v-if="scope.row.status !== 'stopped'"
-                type="primary" plain circle size="mini" icon="el-icon-user" title="人员调整"
-                @click.stop="openAdjust(scope.row)"
-              />
-              <el-button
-                v-if="scope.row.status !== 'stopped'"
-                type="primary" plain circle size="mini" icon="el-icon-upload2" title="上传调整文件"
-                @click.stop="openUpload(scope.row)"
-              />
-              <el-button
-                v-if="scope.row.status !== 'stopped'"
-                type="warning" circle size="mini" icon="el-icon-close" title="停止"
-                @click.stop="handleStopNameList(scope.row)"
-              />
-              <span v-if="scope.row.status === 'stopped'" class="muted">无可执行操作</span>
-            </template>
-          </template>
-        </el-table-column>
-      </el-table>
-
-      <div class="pagination-section">
-        <span class="total">共 {{ filteredData.length }} 条</span>
-        <el-pagination
-          layout="prev, pager, next, sizes, jumper"
-          :current-page.sync="page.current"
-          :page-size.sync="page.size"
-          :page-sizes="[10, 20, 50]"
-          :total="filteredData.length"
-        />
-      </div>
-    </el-card>
-
-    <adjust-member-dialog
-      :visible.sync="adjustVisible"
-      :default-list-id="adjustListId"
-    />
-    <upload-adjust-dialog
-      :visible.sync="uploadVisible"
-      :default-list-id="adjustListId"
-    />
-  </div>
-</template>
-
-<script>
-import crowdTaskStore from '@/store/crowdTaskStore'
-import crowdNameListStore from '@/store/crowdNameListStore'
-import AdjustMemberDialog from '@/components/crowdNameList/AdjustMemberDialog.vue'
-import UploadAdjustDialog from '@/components/crowdNameList/UploadAdjustDialog.vue'
-
-export default {
-  name: 'CrowdTaskList',
-  components: { AdjustMemberDialog, UploadAdjustDialog },
-  data() {
-    return {
-      searchForm: { id: '', name: '', type: '' },
-      page: { current: 1, size: 10 },
-      adjustVisible: false,
-      adjustListId: '',
-      uploadVisible: false
-    }
-  },
-  computed: {
-    filteredData() {
-      const { id, name, type } = this.searchForm
-      const tasks = type === 'name_list'
-        ? []
-        : crowdTaskStore.query({ id, name }).map((r) => ({ ...r, type: 'rule_group' }))
-      const lists = type === 'rule_group'
-        ? []
-        : crowdNameListStore.query({ id, name }).map((r) => ({ ...r, type: 'name_list' }))
-      const merged = [...tasks, ...lists]
-      merged.sort((a, b) => (b.updatedAt || '').localeCompare(a.updatedAt || ''))
-      return merged
-    },
-    pagedData() {
-      const start = (this.page.current - 1) * this.page.size
-      return this.filteredData.slice(start, start + this.page.size)
-    }
-  },
-  methods: {
-    typeLabel(t) {
-      return t === 'rule_group' ? '规则组圈选' : '指定客户号'
-    },
-    statusLabel(row) {
-      if (row.type === 'name_list') {
-        return { draft: '新建', active: '已生效', stopped: '已停止' }[row.status] || row.status
-      }
-      return { draft: '新建', verifying: '校验', online: '上线', stopped: '已停止' }[row.status] || row.status
-    },
-    statusTagType(row) {
-      if (row.type === 'name_list') {
-        return { draft: 'info', active: 'success', stopped: 'danger' }[row.status] || ''
-      }
-      return { draft: 'info', verifying: 'warning', online: 'success', stopped: 'danger' }[row.status] || ''
-    },
-    canDoTask(row, action) {
-      return crowdTaskStore.allowedActions(row).includes(action)
-    },
-    handleSearch() {
-      this.page.current = 1
-    },
-    handleCreateByType(type) {
-      if (type === 'rule_group') {
-        this.$router.push('/customization/hsbc/crowd-tasks/add')
-      } else {
-        this.$router.push('/customization/hsbc/crowd-name-lists/add')
-      }
-    },
-    handleCopy(row) {
-      this.$confirm(
-        `确定要复制任务「${row.name}」吗?将生成一条状态为「新建」的副本。`,
-        '复制任务',
-        { confirmButtonText: '复制', cancelButtonText: '取消', type: 'info' }
-      ).then(() => {
-        const copy = crowdTaskStore.copyFrom(row.id)
-        if (copy) this.$message.success(`已复制为 ${copy.id}`)
-      }).catch(() => {})
-    },
-    openAdjust(row) {
-      this.adjustListId = row.id
-      this.adjustVisible = true
-    },
-    openUpload(row) {
-      this.adjustListId = row.id
-      this.uploadVisible = true
-    },
-    handleEditTask(row) {
-      this.$router.push(`/customization/hsbc/crowd-tasks/edit/${row.id}`)
-    },
-    handleEditNameList(row) {
-      this.$router.push(`/customization/hsbc/crowd-name-lists/edit/${row.id}`)
-    },
-    handleDetail(row) {
-      this.$router.push(`/customization/hsbc/crowd-tasks/view/${row.id}`)
-    },
-    handleRun(row) {
-      this.$confirm(
-        '点击运行后将向行内提交规则,等待返回 count/sample 结果。',
-        '运行任务',
-        { confirmButtonText: '运行', cancelButtonText: '取消', type: 'info' }
-      ).then(() => {
-        crowdTaskStore.run(row.id)
-        this.$message.success('已触发运行,请稍候查看详情')
-      }).catch(() => {})
-    },
-    handlePublish(row) {
-      this.$confirm(
-        '点击上线后将运行 fullist,审核通过后任务进入上线状态。',
-        '上线任务',
-        { confirmButtonText: '上线', cancelButtonText: '取消', type: 'warning' }
-      ).then(() => {
-        crowdTaskStore.publish(row.id)
-        this.$message.success('已提交审核并触发上线运行')
-      }).catch(() => {})
-    },
-    handleStop(row) {
-      this.$confirm(
-        `停止后任务不可恢复,仅可查看详情。确认停止「${row.name}」?`,
-        '停止任务',
-        { confirmButtonText: '停止', cancelButtonText: '取消', type: 'warning' }
-      ).then(() => {
-        crowdTaskStore.stop(row.id)
-        this.$message.success('已停止')
-      }).catch(() => {})
-    },
-    handleActivate(row) {
-      this.$confirm(`启用名单「${row.name}」需通过审核后生效,确认提交?`, '启用', {
-        confirmButtonText: '提交', cancelButtonText: '取消', type: 'info'
-      }).then(() => {
-        crowdNameListStore.activate(row.id)
-        this.$message.success('已生效(demo 默认通过审核)')
-      }).catch(() => {})
-    },
-    handleStopNameList(row) {
-      this.$confirm(`停止后名单不可再调整,确认停止「${row.name}」?`, '停止', {
-        confirmButtonText: '停止', cancelButtonText: '取消', type: 'warning'
-      }).then(() => {
-        crowdNameListStore.stop(row.id)
-        this.$message.success('已停止')
-      }).catch(() => {})
-    }
-  }
-}
-</script>
-
-<style scoped>
-.crowd-task-list { padding: 0; }
-
-.search-section { display: flex; flex-direction: column; }
-.search-row .el-form { display: flex; flex-wrap: wrap; gap: 8px 0; }
-.search-row .el-form-item { margin-right: 8px; margin-bottom: 8px; }
-
-.action-row { margin-top: 8px; display: flex; gap: 8px; }
-
-.muted { color: #c0c4cc; font-size: 12px; }
-
-.pagination-section {
-  display: flex;
-  justify-content: flex-end;
-  align-items: center;
-  gap: 20px;
-  margin-top: 20px;
-}
-.pagination-section .total { color: #606266; font-size: 13px; }
-</style>

+ 0 - 188
pc/src/views/CrowdTaskView.vue

@@ -1,188 +0,0 @@
-<template>
-  <div class="crowd-task-view">
-    <el-card v-if="current">
-      <div slot="header" class="header">
-        <span class="title">客群任务详情</span>
-      </div>
-
-      <!-- 基本信息 -->
-      <div class="section-title">基本信息</div>
-      <el-descriptions :column="2" border>
-        <el-descriptions-item label="客群名称">{{ current.name }}</el-descriptions-item>
-        <el-descriptions-item label="客群编号">{{ current.id }}</el-descriptions-item>
-        <el-descriptions-item label="状态">
-          <el-tag :type="statusTagType(current.status)" size="small">{{ statusLabel(current.status) }}</el-tag>
-        </el-descriptions-item>
-        <el-descriptions-item label="客群描述" :span="2">{{ current.description || '-' }}</el-descriptions-item>
-        <el-descriptions-item label="任务开始/结束日期" :span="2">
-          {{ current.startDate }} ~ {{ current.endDate }}
-        </el-descriptions-item>
-        <el-descriptions-item label="任务创建时间">{{ current.createdAt }}</el-descriptions-item>
-        <el-descriptions-item label="任务创建用户">{{ current.creator }}</el-descriptions-item>
-        <el-descriptions-item label="审核人">{{ current.reviewer || '-' }}</el-descriptions-item>
-        <el-descriptions-item label="最近更新时间">{{ current.updatedAt }}</el-descriptions-item>
-      </el-descriptions>
-
-      <!-- 规则组信息 -->
-      <div class="section-title" style="margin-top: 24px">规则组信息</div>
-      <div class="rule-group-meta">
-        <span class="meta-label">规则组间逻辑:</span>
-        <span>{{ current.ruleGroupLogic === 'AND' ? 'and/且' : 'or/或' }}</span>
-      </div>
-      <div class="rule-group-meta">
-        <span class="meta-label">规则组详情:</span>
-      </div>
-      <el-table :data="current.ruleGroups || []" border style="width: 100%" empty-text="无规则组">
-        <el-table-column prop="id" label="规则组编号" width="170" align="center" />
-        <el-table-column prop="name" label="规则组名称" min-width="160" show-overflow-tooltip />
-        <el-table-column prop="description" label="规则组描述" min-width="200" show-overflow-tooltip />
-        <el-table-column label="规则组内逻辑" width="120" align="center">
-          <template slot-scope="scope">{{ scope.row.logic === 'AND' ? 'and/且' : 'or/或' }}</template>
-        </el-table-column>
-        <el-table-column prop="ruleCount" label="规则个数" width="90" align="center" />
-      </el-table>
-
-      <!-- 运行信息 -->
-      <div class="section-title" style="margin-top: 24px">运行信息</div>
-      <div class="run-info">
-        <div class="run-row">
-          <span class="run-label">总数(count):</span>
-          <span class="run-value">{{ countDisplay }}</span>
-        </div>
-        <div class="run-row">
-          <span class="run-label">校验(sample):</span>
-          <span class="run-value">
-            <el-link
-              v-if="sampleAvailable"
-              type="primary"
-              :underline="false"
-              @click="showSample = true"
-            >详情</el-link>
-            <span v-else-if="current.run.sample === 'running'" class="muted">详情</span>
-            <span v-else class="muted">未运行</span>
-          </span>
-          <el-button
-            size="small"
-            type="primary"
-            :disabled="!canRerun"
-            style="margin-left: 16px"
-            @click="handleRerun"
-          >重新校验</el-button>
-        </div>
-        <div class="run-row">
-          <span class="run-label">全量圈客(fulllist):</span>
-          <span :class="['run-value', fulllistClass]">{{ fulllistDisplay }}</span>
-        </div>
-      </div>
-
-      <div class="footer">
-        <el-button type="primary" @click="$router.push('/customization/hsbc/crowd-tasks')">返回</el-button>
-      </div>
-    </el-card>
-
-    <sample-detail-dialog
-      :visible.sync="showSample"
-      :rows="sampleRows"
-    />
-  </div>
-</template>
-
-<script>
-import crowdTaskStore from '@/store/crowdTaskStore'
-import SampleDetailDialog from '@/components/crowdTask/SampleDetailDialog.vue'
-
-export default {
-  name: 'CrowdTaskView',
-  components: { SampleDetailDialog },
-  data() {
-    return { current: null, showSample: false }
-  },
-  computed: {
-    sampleAvailable() {
-      return this.current && Array.isArray(this.current.run.sample) && this.current.run.sample.length > 0
-    },
-    sampleRows() {
-      return this.sampleAvailable ? this.current.run.sample : []
-    },
-    countDisplay() {
-      const c = this.current && this.current.run.count
-      if (c === null || c === undefined) return '未运行'
-      if (c === 'running') return '正在运行'
-      return c
-    },
-    fulllistDisplay() {
-      const f = this.current && this.current.run.fulllist
-      if (f === null || f === undefined) return '未运行'
-      if (f === 'running') return '正在运行'
-      if (f === 'completed') return '已完成'
-      return f
-    },
-    fulllistClass() {
-      const f = this.current && this.current.run.fulllist
-      if (!f) return 'red'
-      if (f === 'running') return 'muted'
-      return 'green'
-    },
-    canRerun() {
-      if (!this.current) return false
-      if (this.current.run.count === 'running' || this.current.run.sample === 'running') return false
-      if (this.current.status === 'stopped') return false
-      // 任意非"正在运行"且非"已停止"状态都支持运维人员重跑
-      return this.current.run.count !== null
-    }
-  },
-  created() {
-    const target = crowdTaskStore.getById(this.$route.params.id)
-    if (!target) {
-      this.$message.error('任务不存在')
-      this.$router.replace('/customization/hsbc/crowd-tasks')
-      return
-    }
-    this.current = target
-  },
-  methods: {
-    statusLabel(s) {
-      return { draft: '新建', verifying: '校验', online: '上线', stopped: '已停止' }[s] || s
-    },
-    statusTagType(s) {
-      return { draft: 'info', verifying: 'warning', online: 'success', stopped: 'danger' }[s] || ''
-    },
-    handleRerun() {
-      this.$confirm('重新校验将重新运行 count 和 sample,不影响 fullist。', '重新校验', {
-        confirmButtonText: '运行', cancelButtonText: '取消', type: 'info'
-      }).then(() => {
-        crowdTaskStore.rerunCountSample(this.current.id)
-        this.$message.success('已触发重新校验,请稍候查看结果')
-      }).catch(() => {})
-    }
-  }
-}
-</script>
-
-<style scoped>
-.crowd-task-view { padding: 0; }
-.header { display: flex; align-items: center; justify-content: center; }
-.title { font-size: 16px; font-weight: 600; color: #409EFF; }
-
-.section-title {
-  font-size: 14px; font-weight: 600; color: #303133;
-  background: #f5f7fa; padding: 10px 14px; border-radius: 4px;
-  margin-bottom: 16px;
-}
-
-.rule-group-meta { margin: 8px 0; font-size: 13px; color: #606266; }
-.rule-group-meta .meta-label { font-weight: 600; color: #303133; }
-
-.run-info {
-  border: 1px solid #fbbfbf; border-radius: 4px;
-  background: #fffaf0; padding: 16px 24px;
-}
-.run-row { display: flex; align-items: center; padding: 6px 0; font-size: 14px; }
-.run-label { width: 160px; color: #303133; font-weight: 600; }
-.run-value { color: #606266; }
-.run-value.red { color: #F56C6C; }
-.run-value.green { color: #67C23A; }
-.muted { color: #c0c4cc; }
-
-.footer { text-align: center; margin-top: 24px; padding-top: 16px; border-top: 1px solid #ebeef5; }
-</style>

+ 0 - 253
pc/src/views/CustomerEquityGrant.vue

@@ -1,253 +0,0 @@
-<template>
-  <div class="customer-equity-grant">
-    <el-card>
-      <div class="search-section" slot="header">
-        <el-form :model="searchForm" label-width="130px">
-          <el-row :gutter="16">
-            <el-col :span="8">
-              <el-form-item label="客户号:">
-                <el-input v-model="searchForm.customerId" placeholder="请输入客户号" clearable></el-input>
-              </el-form-item>
-            </el-col>
-            <el-col :span="8">
-              <el-form-item label="权益编号:">
-                <el-input v-model="searchForm.equityCode" placeholder="请输入权益编号" clearable></el-input>
-              </el-form-item>
-            </el-col>
-            <el-col :span="8">
-              <el-form-item label="发放时间:">
-                <el-date-picker
-                  v-model="searchForm.grantTimeRange"
-                  type="daterange"
-                  range-separator="至"
-                  start-placeholder="开始日期"
-                  end-placeholder="结束日期"
-                  value-format="yyyy-MM-dd"
-                  style="width: 100%;"
-                ></el-date-picker>
-              </el-form-item>
-            </el-col>
-          </el-row>
-          <el-row :gutter="16">
-            <el-col :span="8">
-              <el-form-item label="Channel 名称:">
-                <el-select v-model="searchForm.channelName" placeholder="请选择 Channel 名称" clearable filterable style="width: 100%;">
-                  <el-option v-for="item in channelOptions" :key="item.id" :label="item.name" :value="item.name"></el-option>
-                </el-select>
-              </el-form-item>
-            </el-col>
-            <el-col :span="8">
-              <el-form-item label="Campaign 名称:">
-                <el-select v-model="searchForm.campaignName" placeholder="请选择 Campaign 名称" clearable filterable style="width: 100%;">
-                  <el-option v-for="item in campaignOptions" :key="item.id" :label="item.name" :value="item.name"></el-option>
-                </el-select>
-              </el-form-item>
-            </el-col>
-            <el-col :span="8">
-              <el-form-item label="客户账户类型:">
-                <el-select v-model="searchForm.accountType" placeholder="请选择客户账户类型" clearable style="width: 100%;">
-                  <el-option v-for="item in accountTypeOptions" :key="item" :label="item" :value="item"></el-option>
-                </el-select>
-              </el-form-item>
-            </el-col>
-          </el-row>
-          <el-row :gutter="16">
-            <el-col :span="24" class="action-col">
-              <el-button type="primary" @click="handleSearch">查询</el-button>
-              <el-button @click="handleReset">重置</el-button>
-              <el-button type="success" icon="el-icon-download" @click="handleExport">导出</el-button>
-            </el-col>
-          </el-row>
-        </el-form>
-      </div>
-
-      <el-table :data="pagedData" stripe style="width: 100%;" v-loading="loading">
-        <el-table-column prop="grantTime" label="发放时间" width="170"></el-table-column>
-        <el-table-column prop="equityName" label="权益名称"></el-table-column>
-        <el-table-column prop="equityCode" label="权益编号" width="130"></el-table-column>
-        <el-table-column prop="campaignId" label="Campaign ID" width="120"></el-table-column>
-        <el-table-column prop="campaignName" label="Campaign 名称" show-overflow-tooltip></el-table-column>
-        <el-table-column prop="channelId" label="Channel ID" width="120"></el-table-column>
-        <el-table-column prop="channelName" label="Channel 名称"></el-table-column>
-        <el-table-column prop="customerId" label="客户号" width="140"></el-table-column>
-        <el-table-column prop="accountType" label="客户账户类型" width="130">
-          <template slot-scope="scope">
-            <el-tag :type="accountTypeTag[scope.row.accountType]" size="small">{{ scope.row.accountType }}</el-tag>
-          </template>
-        </el-table-column>
-      </el-table>
-
-      <el-pagination
-        style="margin-top: 20px; text-align: right;"
-        @current-change="handlePageChange"
-        :current-page="currentPage"
-        :page-size="pageSize"
-        layout="total, prev, pager, next, jumper"
-        :total="filteredData.length"
-      ></el-pagination>
-    </el-card>
-  </div>
-</template>
-
-<script>
-const ACCOUNT_TYPES = ['Premier', 'Elite', 'PB', 'Prospect']
-const CHANNELS = [
-  { id: 'CH0001', name: '汇丰中国客户服务号' },
-  { id: 'CH0002', name: 'MGM - Mortgage' },
-  { id: 'CH0003', name: 'MGM - Premier' },
-  { id: 'CH0004', name: 'CCSS SMS' },
-  { id: 'CH0005', name: 'GPB Referral' },
-  { id: 'CH0006', name: 'HK referral' },
-  { id: 'CH0007', name: 'Non WPB staff Referral' },
-  { id: 'CH0008', name: 'Pinnacle Referral' },
-  { id: 'CH0009', name: 'RBB Refferal' },
-  { id: 'CH0010', name: 'New staff' },
-  { id: 'CH0011', name: 'IBC referral staff code' },
-  { id: 'CH0012', name: 'HTC' },
-  { id: 'CH0013', name: 'NIVIDA' },
-  { id: 'CH0014', name: 'Mobile X' },
-  { id: 'CH0015', name: 'PWS site' },
-  { id: 'CH0016', name: '汇丰中国小程序' },
-  { id: 'CH0017', name: '汇丰中国订阅号' },
-  { id: 'CH0018', name: '推文尾部入口' },
-  { id: 'CH0019', name: 'Chatbot' },
-  { id: 'CH0020', name: 'SMS' },
-  { id: 'CH0021', name: 'River APP' }
-]
-const EQUITY_NAMES = [
-  '机场贵宾厅', '酒店权益', '高尔夫权益', '健康体检', '保险权益',
-  '积分兑换', '消费返现', '生日礼遇', '出行权益', '餐饮优惠'
-]
-const CAMPAIGNS = [
-  { id: 'CP9001', name: 'Q3 大客户回馈' },
-  { id: 'CP9002', name: '新春开户活动' },
-  { id: 'CP9003', name: '618 消费季营销' },
-  { id: 'CP9004', name: '双十一专享权益' },
-  { id: 'CP9005', name: 'Premier 升级礼遇' }
-]
-
-function generateMockData() {
-  const list = []
-  for (let i = 1; i <= 86; i++) {
-    const d = new Date(2026, 4, 15)
-    d.setDate(d.getDate() - Math.floor(Math.random() * 60))
-    d.setHours(Math.floor(Math.random() * 24), Math.floor(Math.random() * 60), Math.floor(Math.random() * 60))
-    const pad = n => String(n).padStart(2, '0')
-    const timeStr = `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())} ${pad(d.getHours())}:${pad(d.getMinutes())}:${pad(d.getSeconds())}`
-
-    const campaign = CAMPAIGNS[Math.floor(Math.random() * CAMPAIGNS.length)]
-    const channel = CHANNELS[Math.floor(Math.random() * CHANNELS.length)]
-    list.push({
-      id: i,
-      grantTime: timeStr,
-      equityName: EQUITY_NAMES[Math.floor(Math.random() * EQUITY_NAMES.length)],
-      equityCode: 'Q' + String(202600000 + i),
-      campaignId: campaign.id,
-      campaignName: campaign.name,
-      channelId: channel.id,
-      channelName: channel.name,
-      customerId: 'CUST' + String(100000 + Math.floor(Math.random() * 900000)),
-      accountType: ACCOUNT_TYPES[Math.floor(Math.random() * ACCOUNT_TYPES.length)]
-    })
-  }
-  list.sort((a, b) => b.grantTime.localeCompare(a.grantTime))
-  return list
-}
-
-export default {
-  name: 'CustomerEquityGrant',
-  data() {
-    return {
-      searchForm: {
-        customerId: '',
-        grantTimeRange: [],
-        channelName: '',
-        campaignName: '',
-        equityCode: '',
-        accountType: ''
-      },
-      loading: false,
-      currentPage: 1,
-      pageSize: 10,
-      tableData: generateMockData(),
-      channelOptions: CHANNELS,
-      campaignOptions: CAMPAIGNS,
-      accountTypeOptions: ACCOUNT_TYPES,
-      accountTypeTag: {
-        Premier: '',
-        Elite: 'success',
-        PB: 'warning',
-        Prospect: 'info'
-      }
-    }
-  },
-  computed: {
-    filteredData() {
-      let list = this.tableData
-      const customerId = this.searchForm.customerId.trim()
-      if (customerId) {
-        list = list.filter(item => item.customerId.includes(customerId))
-      }
-      if (this.searchForm.grantTimeRange && this.searchForm.grantTimeRange.length === 2) {
-        const start = this.searchForm.grantTimeRange[0]
-        const end = this.searchForm.grantTimeRange[1] + ' 23:59:59'
-        list = list.filter(item => item.grantTime >= start && item.grantTime <= end)
-      }
-      if (this.searchForm.channelName) {
-        list = list.filter(item => item.channelName === this.searchForm.channelName)
-      }
-      if (this.searchForm.campaignName) {
-        list = list.filter(item => item.campaignName === this.searchForm.campaignName)
-      }
-      const equityCode = this.searchForm.equityCode.trim()
-      if (equityCode) {
-        list = list.filter(item => item.equityCode.includes(equityCode))
-      }
-      if (this.searchForm.accountType) {
-        list = list.filter(item => item.accountType === this.searchForm.accountType)
-      }
-      return list
-    },
-    pagedData() {
-      const start = (this.currentPage - 1) * this.pageSize
-      return this.filteredData.slice(start, start + this.pageSize)
-    }
-  },
-  methods: {
-    handleSearch() {
-      this.currentPage = 1
-    },
-    handleReset() {
-      this.searchForm = {
-        customerId: '',
-        grantTimeRange: [],
-        channelName: '',
-        campaignName: '',
-        equityCode: '',
-        accountType: ''
-      }
-      this.currentPage = 1
-    },
-    handlePageChange(page) {
-      this.currentPage = page
-    },
-    handleExport() {
-      this.$message.success('导出任务已提交,请稍后在下载中心查看')
-    }
-  }
-}
-</script>
-
-<style scoped>
-.search-section {
-  display: flex;
-  flex-direction: column;
-  align-items: stretch;
-}
-
-.action-col {
-  display: flex;
-  align-items: center;
-  gap: 8px;
-}
-</style>

+ 0 - 966
pc/src/views/EditEquityProduct.vue

@@ -1,966 +0,0 @@
-<template>
-  <div class="edit-equity-product">
-    <el-card>
-      <div slot="header" class="header-title">
-        <span>编辑权益</span>
-      </div>
-
-      <!-- 步骤条 -->
-      <el-steps :active="activeStep" simple style="margin-bottom: 20px;">
-        <el-step title="填写权益信息" icon="el-icon-info"></el-step>
-        <el-step title="设置规则" icon="el-icon-setting"></el-step>
-        <el-step title="更多设置" icon="el-icon-monitor"></el-step>
-      </el-steps>
-
-      <!-- 步骤内容 -->
-      <div v-show="activeStep === 0">
-        <!-- 基本信息 -->
-        <el-card shadow="never" class="section-card">
-          <div slot="header" class="section-title">基本信息</div>
-        <el-form :model="form" label-width="120px" label-position="left">
-          <el-row :gutter="20">
-            <el-col :span="8">
-              <el-form-item label="权益来源" required>
-                <!-- 编辑时不允许修改权益来源 -->
-                <el-radio-group v-model="form.source" disabled>
-                  <el-radio label="银行自有"></el-radio>
-                  <el-radio label="荣数商品"></el-radio>
-                  <el-radio label="第三方"></el-radio>
-                </el-radio-group>
-              </el-form-item>
-            </el-col>
-            <el-col :span="8" v-if="form.source === '荣数商品'">
-              <el-form-item label="选择权益" required>
-                <el-input
-                  v-model="form.rongshuProduct"
-                  placeholder="请选择权益"
-                  readonly
-                >
-                  <el-button slot="append" @click="rongshuDialogVisible = true">选择</el-button>
-                </el-input>
-              </el-form-item>
-            </el-col>
-            <el-col :span="8" v-if="form.source === '银行自有'">
-              <el-form-item label="权益类型" required>
-                <el-radio-group v-model="form.productType">
-                  <el-radio label="券码"></el-radio>
-                </el-radio-group>
-              </el-form-item>
-            </el-col>
-          </el-row>
-          <el-row :gutter="20">
-            <el-col :span="8" v-if="form.source === '第三方'">
-              <el-form-item label="外部权益链接" required>
-                <el-select v-model="form.externalLink" placeholder="请选择外部权益链接">
-                  <el-option label="太古地产" value="太古地产"></el-option>
-                  <el-option label="龙腾" value="龙腾"></el-option>
-                  <el-option label="h5链接" value="h5链接"></el-option>
-                  <el-option label="小程序链接" value="小程序链接"></el-option>
-                </el-select>
-              </el-form-item>
-            </el-col>
-            <el-col :span="8" v-if="form.source === '第三方' && form.externalLink === 'h5链接'">
-              <el-form-item label="h5链接" required>
-                <el-input v-model="form.customLink" placeholder="请输入h5链接"></el-input>
-              </el-form-item>
-            </el-col>
-            <el-col :span="8" v-if="form.source === '第三方'">
-              <el-form-item required label-width="140px">
-                <span slot="label" style="white-space: nowrap;">是否有详情页<el-tooltip content="没有详情页时直接从首页/列表页跳转" placement="top"><i class="el-icon-question tip-icon"></i></el-tooltip></span>
-                <el-radio-group v-model="form.externalJumpMethod">
-                  <el-radio-button label="没有"></el-radio-button>
-                  <el-radio-button label="有"></el-radio-button>
-                </el-radio-group>
-              </el-form-item>
-            </el-col>
-          </el-row>
-          <el-row :gutter="20" v-if="form.source === '第三方' && form.externalLink === '小程序链接'">
-            <el-col :span="8">
-              <el-form-item label="小程序appid" required>
-                <el-input v-model="form.miniAppId" placeholder="请输入小程序appid"></el-input>
-              </el-form-item>
-            </el-col>
-            <el-col :span="8">
-              <el-form-item label="小程序页面地址" required label-width="140px">
-                <el-input v-model="form.miniAppPath" placeholder="请输入小程序页面地址"></el-input>
-              </el-form-item>
-            </el-col>
-          </el-row>
-
-          <template v-if="form.source === '银行自有' || form.source === '荣数商品'">
-            <el-row :gutter="20">
-              <el-col :span="8">
-                <el-form-item label="权益名称" required>
-                  <el-input v-model="form.equityName" maxlength="40" placeholder="请输入权益名称"></el-input>
-                </el-form-item>
-              </el-col>
-              <el-col :span="8">
-                <el-form-item label="权益副标题">
-                  <el-input v-model="form.subTitle" maxlength="40" placeholder="请输入权益副标题"></el-input>
-                </el-form-item>
-              </el-col>
-            </el-row>
-            <el-row :gutter="20" v-if="form.source === '荣数商品'">
-              <el-col :span="8">
-                <el-form-item label="库存数" required>
-                  <el-input-number
-                    v-model="form.stockCount"
-                    :min="0"
-                    :max="100000"
-                    style="width: 100%"
-                  ></el-input-number>
-                </el-form-item>
-              </el-col>
-            </el-row>
-            <el-row :gutter="20">
-              <el-col :span="8">
-                <el-form-item required>
-                  <span slot="label">权益图标
-                    <el-tooltip content="展示在权益首页和权益列表页" placement="top">
-                      <i class="el-icon-question tip-icon"></i>
-                    </el-tooltip>
-                  </span>
-                  <el-upload
-                    action=""
-                    :auto-upload="false"
-                    :file-list="form.listIcon"
-                    :on-change="file => handleUpload(file, 'listIcon')"
-                    :on-preview="handlePreviewImage"
-                    :on-remove="() => form.listIcon = []"
-                    :class="{ 'upload-hide-trigger': form.listIcon.length >= 1 }"
-                    :limit="1"
-                    list-type="picture-card"
-                  >
-                    <i class="el-icon-plus"></i>
-                  </el-upload>
-                  <div class="upload-size-tip">建议尺寸:80x80px</div>
-                </el-form-item>
-              </el-col>
-            </el-row>
-          </template>
-
-          <template v-else-if="form.source === '第三方'">
-            <el-row :gutter="20">
-              <el-col :span="8">
-                <el-form-item label="权益名称" required>
-                  <el-input v-model="form.equityName" maxlength="40" placeholder="请输入权益名称"></el-input>
-                </el-form-item>
-              </el-col>
-              <el-col :span="8">
-                <el-form-item label="权益副标题">
-                  <el-input v-model="form.subTitle" maxlength="40" placeholder="请输入权益副标题"></el-input>
-                </el-form-item>
-              </el-col>
-            </el-row>
-            <el-row :gutter="20" v-if="form.externalJumpMethod === '没有'">
-              <el-col :span="8">
-                <el-form-item required>
-                  <span slot="label">权益图标
-                    <el-tooltip content="展示在权益首页和权益列表页" placement="top">
-                      <i class="el-icon-question tip-icon"></i>
-                    </el-tooltip>
-                  </span>
-                  <el-upload
-                    action=""
-                    :auto-upload="false"
-                    :file-list="form.listIcon"
-                    :on-change="file => handleUpload(file, 'listIcon')"
-                    :on-preview="handlePreviewImage"
-                    :on-remove="() => form.listIcon = []"
-                    :class="{ 'upload-hide-trigger': form.listIcon.length >= 1 }"
-                    :limit="1"
-                    list-type="picture-card"
-                  >
-                    <i class="el-icon-plus"></i>
-                  </el-upload>
-                  <div class="upload-size-tip">建议尺寸:80x80px</div>
-                </el-form-item>
-              </el-col>
-            </el-row>
-            <el-row v-if="form.externalJumpMethod === '有'" :gutter="20">
-              <el-col :span="8">
-                <el-form-item required>
-                  <span slot="label">权益图标
-                    <el-tooltip content="展示在权益首页和权益列表页" placement="top">
-                      <i class="el-icon-question tip-icon"></i>
-                    </el-tooltip>
-                  </span>
-                  <el-upload
-                    action=""
-                    :auto-upload="false"
-                    :file-list="form.listIcon"
-                    :on-change="file => handleUpload(file, 'listIcon')"
-                    :on-preview="handlePreviewImage"
-                    :on-remove="() => form.listIcon = []"
-                    :class="{ 'upload-hide-trigger': form.listIcon.length >= 1 }"
-                    :limit="1"
-                    list-type="picture-card"
-                  >
-                    <i class="el-icon-plus"></i>
-                  </el-upload>
-                  <div class="upload-size-tip">建议尺寸:80x80px</div>
-                </el-form-item>
-              </el-col>
-            </el-row>
-          </template>
-        </el-form>
-      </el-card>
-
-      <!-- 详情信息 -->
-      <el-card v-if="shouldShowDetailCard" shadow="never" class="section-card">
-        <div slot="header" class="section-title">详情信息</div>
-        <el-form :model="form" label-width="120px" label-position="left">
-          <el-row :gutter="20">
-            <el-col :span="8">
-              <el-form-item label="权益主图" required>
-                <el-upload
-                  action=""
-                  :auto-upload="false"
-                  :file-list="form.mainImage"
-                  :on-change="file => handleUpload(file, 'mainImage')"
-                  :on-preview="handlePreviewImage"
-                  :on-remove="() => form.mainImage = []"
-                  :class="{ 'upload-hide-trigger': form.mainImage.length >= 1 }"
-                  :limit="1"
-                  list-type="picture-card"
-                >
-                  <i class="el-icon-plus"></i>
-                </el-upload>
-                <div class="upload-size-tip">建议尺寸:716x400px</div>
-              </el-form-item>
-            </el-col>
-          </el-row>
-          <el-row :gutter="20">
-            <el-col :span="12">
-              <el-form-item label="权益介绍" required>
-                <el-input
-                  v-model="form.introduction"
-                  type="textarea"
-                  :rows="4"
-                  maxlength="500"
-                  show-word-limit
-                  placeholder="请输入权益介绍"
-                ></el-input>
-              </el-form-item>
-            </el-col>
-          </el-row>
-          <el-row :gutter="20">
-            <el-col :span="12">
-              <el-form-item label="权益有效期" required>
-                <template v-if="form.source === '银行自有' || form.source === '荣数商品'">
-                  <el-radio-group v-model="form.validityType">
-                    <el-radio label="fromGrantPlan">取自发放计划</el-radio>
-                    <el-radio label="custom">自定义输入</el-radio>
-                  </el-radio-group>
-                  <el-input
-                    v-if="form.validityType === 'custom'"
-                    v-model="form.validityText"
-                    maxlength="50"
-                    show-word-limit
-                    placeholder="请输入权益有效期"
-                    style="margin-top: 8px;"
-                  ></el-input>
-                </template>
-                <el-input
-                  v-else
-                  v-model="form.validityText"
-                  maxlength="50"
-                  show-word-limit
-                  placeholder="请输入权益有效期"
-                ></el-input>
-              </el-form-item>
-            </el-col>
-          </el-row>
-          <el-row :gutter="20">
-            <el-col :span="12">
-              <el-form-item label="权益内容" required>
-                <quill-editor v-model="form.overview" :options="quillOptions"></quill-editor>
-              </el-form-item>
-            </el-col>
-            <el-col :span="12">
-              <el-form-item label="权益说明" required>
-                <quill-editor v-model="form.details" :options="quillOptions"></quill-editor>
-              </el-form-item>
-            </el-col>
-          </el-row>
-          <template v-if="form.source === '银行自有' || form.source === '荣数商品'">
-            <el-row :gutter="20">
-              <el-col :span="24">
-                <el-form-item>
-                  <span slot="label">下单成功短信
-                    <el-tooltip placement="top">
-                      <div slot="content">
-                        此模板为权益支付成功,发送给持卡人的短信模板;<br/>
-                        开启后需选择短信模板,关闭则不发送短信。<br/>
-                        占位符如下:<br/>
-                        权益名称:${productName}<br/>
-                        结束日期:${expireDate}<br/>
-                        卡号:${exchangeNum}<br/>
-                        卡密:${exchangePwd}<br/>
-                        券码路径:${qrCodeDownPaths}
-                      </div>
-                      <i class="el-icon-question tip-icon"></i>
-                    </el-tooltip>
-                  </span>
-                  <el-switch v-model="form.smsEnabled" @change="handleSmsEnabledChange"></el-switch>
-                  <template v-if="form.smsEnabled">
-                    <div style="width: 600px;background: #f1f2f5;padding: 20px;margin-top: 10px;">
-                      <el-select
-                        v-model="form.smsTemplateId"
-                        placeholder="请选择短信模板"
-                        style="width: 100%"
-                        @change="handleSmsTemplateChange"
-                      >
-                        <el-option
-                          v-for="item in smsTemplateOptions"
-                          :key="item.id"
-                          :label="item.name"
-                          :value="item.id"
-                        />
-                      </el-select>
-                      <div v-if="form.smsTemplate" class="sms-template-preview">{{ form.smsTemplate }}</div>
-                    </div>
-                  </template>
-                </el-form-item>
-              </el-col>
-            </el-row>
-            <el-row :gutter="20">
-              <el-col :span="8">
-                <el-form-item label="领取成功按钮" required>
-                  <el-input v-model="form.successButtonText" placeholder="请输入按钮文案"></el-input>
-                </el-form-item>
-              </el-col>
-            </el-row>
-          </template>
-        </el-form>
-      </el-card>
-
-      </div>
-
-      <div v-show="activeStep === 1">
-        <!-- 设置规则 -->
-        <el-card shadow="never" class="section-card">
-          <div slot="header" class="section-title">设置规则</div>
-        <div slot="header" class="section-title">展示规则</div>
-        <el-form :model="form" label-width="120px" label-position="left">
-          <el-form-item label="展示时间限制">
-            <el-switch v-model="form.showTimeEnabled"></el-switch>
-            <template v-if="form.showTimeEnabled">
-              <div style="display: flex;width: 600px;background: #f1f2f5;padding: 20px;margin-top: 10px;">
-                <el-date-picker
-                  v-model="form.showStartTime"
-                  type="datetime"
-                  placeholder="请选择开始时间"
-                  format="yyyy-MM-dd HH:mm:ss"
-                  value-format="yyyy-MM-dd HH:mm:ss"
-                  style="width: 100%"
-                ></el-date-picker>
-                <div style="width: 50px;text-align: center;">-</div>
-                <el-date-picker
-                  v-model="form.showEndTime"
-                  type="datetime"
-                  placeholder="请选择结束时间"
-                  format="yyyy-MM-dd HH:mm:ss"
-                  value-format="yyyy-MM-dd HH:mm:ss"
-                  style="width: 100%"
-                ></el-date-picker>
-              </div>
-            </template>
-          </el-form-item>
-          <el-form-item label="展示客群限制">
-            <el-switch v-model="form.showGroupEnabled"></el-switch>
-            <template v-if="form.showGroupEnabled">
-              <div style="display: flex;width: 600px;background: #f1f2f5;padding: 20px;margin-top: 10px;">
-                <el-select v-model="form.showGroupTagIds" multiple filterable placeholder="请选择客群" style="width: 100%">
-                  <el-option label="客群A" value="客群A"></el-option>
-                  <el-option label="客群B" value="客群B"></el-option>
-                  <el-option label="客群C" value="客群C"></el-option>
-                </el-select>
-              </div>
-            </template>
-          </el-form-item>
-        </el-form>
-      </el-card>
-
-      <!-- 领取规则 -->
-      <el-card shadow="never" class="section-card">
-        <div slot="header" class="section-title">领取规则</div>
-        <el-form :model="form" label-width="120px" label-position="left">
-          <el-form-item label="领取时间限制">
-            <el-switch v-model="form.claimTimeEnabled"></el-switch>
-            <template v-if="form.claimTimeEnabled">
-              <div style="display: flex;width: 600px;background: #f1f2f5;padding: 20px;margin-top: 10px;">
-                <el-date-picker
-                  v-model="form.claimStartTime"
-                  type="datetime"
-                  placeholder="请选择开始时间"
-                  format="yyyy-MM-dd HH:mm:ss"
-                  value-format="yyyy-MM-dd HH:mm:ss"
-                  style="width: 100%"
-                ></el-date-picker>
-                <div style="width: 50px;text-align: center;">-</div>
-                <el-date-picker
-                  v-model="form.claimEndTime"
-                  type="datetime"
-                  placeholder="请选择结束时间"
-                  format="yyyy-MM-dd HH:mm:ss"
-                  value-format="yyyy-MM-dd HH:mm:ss"
-                  style="width: 100%"
-                ></el-date-picker>
-              </div>
-            </template>
-          </el-form-item>
-          <el-form-item label="领取客群限制">
-            <el-switch v-model="form.claimGroupEnabled"></el-switch>
-            <template v-if="form.claimGroupEnabled">
-              <div style="width: 600px;background: #f1f2f5;padding: 20px;margin-top: 10px;">
-                <el-alert
-                  title="需在【发放计划】中选择权益发放,未发放的权益移动端不展示"
-                  type="info"
-                  :closable="false"
-                  show-icon
-                  style="margin-bottom: 12px;"
-                ></el-alert>
-                <div style="font-size: 14px;color: #606266;margin-bottom: 8px;font-weight: 500;">不满足提示</div>
-                <el-form-item label="提示内容" label-width="80px" style="margin-bottom: 12px;">
-                  <el-input v-model="form.claimDenyMessage" maxlength="200" placeholder="请输入提示内容"></el-input>
-                </el-form-item>
-                <el-form-item label="跳转按钮文案" label-width="100px" style="margin-bottom: 12px;">
-                  <el-input v-model="form.claimDenyButtonText" maxlength="10" placeholder="请输入按钮文案"></el-input>
-                </el-form-item>
-                <el-form-item label="跳转按钮链接" label-width="100px" style="margin-bottom: 0;">
-                  <el-input v-model="form.claimDenyButtonLink" maxlength="200" placeholder="请输入按钮链接"></el-input>
-                </el-form-item>
-              </div>
-            </template>
-          </el-form-item>
-        </el-form>
-      </el-card>
-      </div>
-
-      <div v-show="activeStep === 2">
-        <!-- 显示页面 -->
-        <el-card shadow="never" class="section-card">
-          <div slot="header" class="section-title">显示页面</div>
-          <el-form :model="form" label-width="120px" label-position="left">
-            <el-form-item label="权益首页/列表页">
-              <el-radio-group v-model="form.showOnHome">
-                <el-radio label="显示">显示</el-radio>
-                <el-radio label="不显示">不显示</el-radio>
-              </el-radio-group>
-            </el-form-item>
-            <el-form-item label="组装页">
-              <el-radio-group v-model="form.showOnAssembly">
-                <el-radio label="显示">显示</el-radio>
-                <el-radio label="不显示">不显示</el-radio>
-              </el-radio-group>
-              <div
-                v-if="form.showOnAssembly === '显示'"
-                style="width: 600px;background: #f1f2f5;padding: 20px;margin-top: 10px;"
-              >
-                <el-select
-                  v-model="form.assemblyPages"
-                  multiple
-                  filterable
-                  placeholder="请选择组装页"
-                  style="width: 100%"
-                >
-                  <el-option label="组装页A" value="组装页A"></el-option>
-                  <el-option label="组装页B" value="组装页B"></el-option>
-                  <el-option label="组装页C" value="组装页C"></el-option>
-                  <el-option label="组装页D" value="组装页D"></el-option>
-                </el-select>
-              </div>
-            </el-form-item>
-          </el-form>
-        </el-card>
-
-        <!-- 分享设置 -->
-        <el-card shadow="never" class="section-card">
-          <div slot="header" class="section-title">分享设置</div>
-          <el-form :model="form" label-width="120px" label-position="left">
-            <el-form-item label="是否支持分享">
-              <el-switch v-model="form.shareEnabled" @change="handleShareEnabledChange"></el-switch>
-              <template v-if="form.shareEnabled">
-                <div style="width: 600px;background: #f1f2f5;padding: 20px;margin-top: 10px;">
-                  <el-form-item label="分享名称" label-width="100px" required style="margin-bottom: 16px;">
-                    <el-input
-                      v-model="form.shareName"
-                      maxlength="40"
-                      placeholder="请输入分享名称"
-                    ></el-input>
-                  </el-form-item>
-                  <el-form-item label="分享图片" label-width="100px" required style="margin-bottom: 0;">
-                    <el-upload
-                      action=""
-                      :auto-upload="false"
-                      :file-list="form.shareImage"
-                      :on-change="file => handleUpload(file, 'shareImage')"
-                      :limit="1"
-                      list-type="picture-card"
-                    >
-                      <i class="el-icon-plus"></i>
-                    </el-upload>
-                  </el-form-item>
-                </div>
-              </template>
-            </el-form-item>
-          </el-form>
-        </el-card>
-      </div>
-
-      <!-- 操作栏 -->
-      <div class="action-bar">
-        <div class="action-left">
-          <el-button
-            v-if="activeStep > 0"
-            @click="prevStep"
-            icon="el-icon-arrow-left"
-          >上一步</el-button>
-        </div>
-        <div class="action-center">
-          <el-button @click="goBack">取消</el-button>
-          <el-button
-            v-if="currentAuditStatus === '草稿'"
-            type="primary"
-            plain
-            @click="handleSaveDraft"
-          >保存草稿</el-button>
-          <el-button
-            v-if="activeStep === 2"
-            type="primary"
-            @click="handleSubmitAudit"
-          >提交审核</el-button>
-        </div>
-        <div class="action-right">
-          <el-button
-            v-if="activeStep < 2"
-            type="primary"
-            @click="nextStep"
-          >下一步<i class="el-icon-arrow-right el-icon--right"></i></el-button>
-        </div>
-      </div>
-    </el-card>
-
-    <rongshu-product-dialog
-      :visible.sync="rongshuDialogVisible"
-      @select="onRongshuSelect"
-    />
-
-    <el-dialog :visible.sync="previewVisible" title="图片预览" width="640px" append-to-body>
-      <img v-if="previewImageUrl" :src="previewImageUrl" style="display:block; max-width:100%; margin:0 auto;" />
-    </el-dialog>
-  </div>
-</template>
-
-<script>
-import RongshuProductDialog from '@/components/RongshuProductDialog.vue'
-import request from '@/utils/request'
-
-export default {
-  name: 'EditEquityProduct',
-  components: { RongshuProductDialog },
-  data() {
-    return {
-      activeStep: 0,
-      rongshuDialogVisible: false,
-      previewVisible: false,
-      previewImageUrl: '',
-      currentShelfStatus: '',
-      currentAuditStatus: '',
-      quillOptions: {
-        placeholder: '请输入内容...',
-        modules: {
-          toolbar: [
-            ['bold', 'italic', 'underline'],
-            [{ list: 'ordered' }, { list: 'bullet' }],
-            [{ header: [1, 2, 3, false] }],
-            ['link', 'image'],
-            ['clean']
-          ]
-        }
-      },
-      smsTemplateOptions: [
-        { id: 'SMS_TPL_001', name: '通用权益支付成功模板', content: '【银行】您购买的${productName}已支付成功,卡号:${exchangeNum},卡密:${exchangePwd},有效期至${expireDate},详情:${qrCodeDownPaths}' },
-        { id: 'SMS_TPL_002', name: '券码类短信模板', content: '【银行】尊敬的客户,您的${productName}购买成功,券码为${exchangeNum},请于${expireDate}前使用。' },
-        { id: 'SMS_TPL_003', name: '实物类短信模板', content: '【银行】您购买的${productName}已受理,我们将尽快安排发货,感谢您的支持。' }
-      ],
-      form: {
-        source: '银行自有',
-        rongshuProduct: '',
-        productType: '券码',
-        faceValueThreshold: '',
-        faceValueDiscount: '',
-        stockCount: null,
-        shareEnabled: false,
-        shareImage: [],
-        shareName: '',
-        externalJumpMethod: '没有',
-        externalLink: '太古地产',
-        customLink: '',
-        miniAppId: '',
-        miniAppPath: '',
-        skuNo: '',
-        equityName: '',
-        productId: '',
-        merchantName: '',
-        subTitle: '',
-        listIcon: [],
-        mainImage: [],
-        introduction: '',
-        validityType: 'fromGrantPlan',
-        validityText: '',
-        overview: '',
-        details: '',
-        smsEnabled: false,
-        smsTemplateId: '',
-        smsTemplate: '',
-        successButtonText: '',
-        showTimeEnabled: false,
-        showStartTime: '',
-        showEndTime: '',
-        showGroupEnabled: false,
-        showGroupTagIds: [],
-        claimTimeEnabled: false,
-        claimStartTime: '',
-        claimEndTime: '',
-        claimGroupEnabled: false,
-        claimGroupIsPotential: false,
-        claimGroupTagId: '',
-        claimDenyMessage: '',
-        claimDenyButtonText: '',
-        claimDenyButtonLink: '',
-        showOnHome: '显示',
-        showOnAssembly: '不显示',
-        assemblyPages: []
-      }
-    }
-  },
-  computed: {
-    shouldShowDetailCard() {
-      const s = this.form.source
-      if (s === '银行自有' || s === '荣数商品') return true
-      if (s === '第三方' && this.form.externalJumpMethod === '有') return true
-      return false
-    }
-  },
-  created() {
-    this.loadData()
-    this.loadStatus()
-  },
-  methods: {
-    loadStatus() {
-      const id = this.$route.params.id
-      if (!id) return
-      request.get(`/api/equity/detail?id=${id}`).then(res => {
-        if (res.data) {
-          this.currentShelfStatus = res.data.shelfStatus || ''
-          this.currentAuditStatus = res.data.auditStatus || ''
-          // 用 API 返回的核心字段覆盖(保证编辑/已上架的内容是当前线上版本)
-          const fields = ['equityName', 'source', 'productType', 'skuNo', 'merchantName', 'subTitle']
-          fields.forEach(k => {
-            if (res.data[k] !== undefined && res.data[k] !== null && res.data[k] !== '') {
-              this.$set(this.form, k, res.data[k])
-            }
-          })
-          this.form.productId = id
-        }
-      }).catch(() => {})
-    },
-    buildPayload() {
-      const f = this.form
-      const isCustomValidity = f.source === '第三方' || f.validityType === 'custom'
-      return {
-        productId: f.productId || this.$route.params.id,
-        equityName: f.equityName,
-        source: f.source,
-        productType: f.productType || '',
-        skuNo: f.skuNo || '',
-        merchantName: f.merchantName || '',
-        subTitle: f.subTitle || '',
-        jumpMethod: f.source === '第三方' ? f.externalJumpMethod : '-',
-        jumpLink: f.source === '第三方' ? f.externalLink : '-',
-        exchangeMethod: f.source === '第三方'
-          ? (f.externalJumpMethod === '有' ? '第三方(详情页跳转)' : '第三方(列表跳转)')
-          : '权益平台',
-        validityType: isCustomValidity ? 'custom' : 'fromGrantPlan',
-        validityText: isCustomValidity ? (f.validityText || '') : '',
-        showOnHome: f.showOnHome,
-        showOnAssembly: f.showOnAssembly,
-        assemblyPages: [...(f.assemblyPages || [])]
-      }
-    },
-    handleSaveDraft() {
-      request.post('/api/equity/save-draft', this.buildPayload()).then(() => {
-        this.$message.success('草稿已保存')
-        this.$router.push('/customization/hsbc/equity-products')
-      }).catch(() => {})
-    },
-    handleSubmitAudit() {
-      const tip = '提交后将进入审核流程,期间不可再编辑。'
-      this.$prompt(tip + '\n请填写提交说明(可选)', '提交审核', {
-        confirmButtonText: '提交',
-        cancelButtonText: '取消',
-        inputType: 'textarea',
-        inputPlaceholder: '可填写本次提交的关键变更点',
-        inputValidator: () => true
-      }).then(({ value }) => {
-        const payload = { ...this.buildPayload(), submitReason: value || '' }
-        request.post('/api/equity/submit', payload).then(() => {
-          this.$message.success('已提交审核')
-          this.$router.push('/customization/hsbc/equity-products')
-        }).catch(() => {})
-      }).catch(() => {})
-    },
-    loadData() {
-      const id = this.$route.params.id
-      const mockMap = {
-        'Q100001': {
-          source: '银行自有',
-          externalJumpMethod: '',
-          externalLink: '',
-          customLink: '',
-        miniAppId: '',
-        miniAppPath: '',
-          skuNo: 'SKU001',
-          equityName: '权益A',
-          subTitle: '副标题示例',
-          listIcon: [],
-          mainImage: [],
-          overview: '<p>权益内容内容</p>',
-          details: '<p>权益说明内容</p>',
-          smsEnabled: true,
-          smsTemplateId: 'SMS_TPL_001',
-          smsTemplate: '【银行】您购买的${productName}已支付成功,卡号:${exchangeNum},卡密:${exchangePwd},有效期至${expireDate},详情:${qrCodeDownPaths}',
-          successButtonText: '马上去使用',
-          showTimeEnabled: true,
-          showStartTime: '2026-01-01 00:00:00',
-          showEndTime: '2026-12-31 23:59:59',
-          showGroupEnabled: false,
-          showGroupTagIds: [],
-          claimTimeEnabled: true,
-          claimStartTime: '2026-01-01 00:00:00',
-          claimEndTime: '2026-12-31 23:59:59',
-          claimGroupEnabled: false,
-          claimGroupTagId: '',
-          claimDenyMessage: '',
-          claimDenyButtonText: '',
-          claimDenyButtonLink: ''
-        },
-        'Q100002': {
-          source: '第三方',
-          externalJumpMethod: '没有',
-          externalLink: '太古地产',
-          customLink: '',
-        miniAppId: '',
-        miniAppPath: '',
-          skuNo: 'SKU002',
-          equityName: '权益B',
-          subTitle: '外部列表页副标题',
-          listIcon: [],
-          mainImage: [],
-          overview: '',
-          details: '',
-          smsEnabled: false,
-          smsTemplateId: '',
-          smsTemplate: '',
-          successButtonText: '',
-          showTimeEnabled: false,
-          showStartTime: '',
-          showEndTime: '',
-          showGroupEnabled: true,
-          showGroupTagIds: ['客群A'],
-          claimTimeEnabled: true,
-          claimStartTime: '2026-02-01 00:00:00',
-          claimEndTime: '2026-11-30 23:59:59',
-          claimGroupEnabled: true,
-          claimGroupIsPotential: false,
-          claimGroupTagId: '客群奖励活动A',
-          claimDenyMessage: '您暂不满足领取条件',
-          claimDenyButtonText: '去升级',
-          claimDenyButtonLink: 'https://example.com/upgrade'
-        },
-        'Q100003': {
-          source: '第三方',
-          externalJumpMethod: '有',
-          externalLink: '龙腾',
-          customLink: '',
-        miniAppId: '',
-        miniAppPath: '',
-          skuNo: 'SKU003',
-          equityName: '权益C',
-          subTitle: '外部详情页副标题',
-          listIcon: [],
-          mainImage: [],
-          overview: '<p>外部详情页权益内容</p>',
-          details: '<p>外部详情页权益说明</p>',
-          smsEnabled: false,
-          smsTemplateId: '',
-          smsTemplate: '',
-          successButtonText: '',
-          showTimeEnabled: false,
-          showStartTime: '',
-          showEndTime: '',
-          showGroupEnabled: false,
-          showGroupTagIds: [],
-          claimTimeEnabled: false,
-          claimStartTime: '',
-          claimEndTime: '',
-          claimGroupEnabled: false,
-          claimGroupTagId: '',
-          claimDenyMessage: '',
-          claimDenyButtonText: '',
-          claimDenyButtonLink: ''
-        },
-        'Q100004': {
-          source: '第三方',
-          externalJumpMethod: '有',
-          externalLink: 'h5链接',
-          customLink: 'https://example.com/custom',
-          skuNo: 'SKU004',
-          equityName: '权益D',
-          subTitle: '外部自定义链接副标题',
-          listIcon: [],
-          mainImage: [],
-          overview: '<p>自定义链接权益内容</p>',
-          details: '<p>自定义链接权益说明</p>',
-          smsEnabled: false,
-          smsTemplateId: '',
-          smsTemplate: '',
-          successButtonText: '',
-          showTimeEnabled: false,
-          showStartTime: '',
-          showEndTime: '',
-          showGroupEnabled: false,
-          showGroupTagIds: [],
-          claimTimeEnabled: false,
-          claimStartTime: '',
-          claimEndTime: '',
-          claimGroupEnabled: false,
-          claimGroupTagId: '',
-          claimDenyMessage: '',
-          claimDenyButtonText: '',
-          claimDenyButtonLink: ''
-        }
-      }
-      if (id && mockMap[id]) {
-        Object.assign(this.form, mockMap[id])
-      }
-    },
-    nextStep() {
-      if (this.activeStep < 2) {
-        this.activeStep++
-      }
-    },
-    prevStep() {
-      if (this.activeStep > 0) {
-        this.activeStep--
-      }
-    },
-    goBack() {
-      this.$router.push('/customization/hsbc/equity-products')
-    },
-    handleSmsEnabledChange(val) {
-      if (!val) {
-        this.form.smsTemplateId = ''
-        this.form.smsTemplate = ''
-      }
-    },
-    handleShareEnabledChange(val) {
-      if (val) {
-        if (!this.form.shareName) {
-          this.form.shareName = this.form.equityName || ''
-        }
-      }
-    },
-    handleSmsTemplateChange(id) {
-      const tpl = this.smsTemplateOptions.find(i => i.id === id)
-      this.form.smsTemplate = tpl ? tpl.content : ''
-    },
-    handleUpload(file, field) {
-      if (!file || !file.raw) return
-      const reader = new FileReader()
-      reader.onload = (e) => {
-        this.form[field] = [{ name: file.name, url: e.target.result, uid: file.uid }]
-      }
-      reader.readAsDataURL(file.raw)
-    },
-    handlePreviewImage(file) {
-      this.previewImageUrl = file.url || ''
-      this.previewVisible = !!this.previewImageUrl
-    },
-    onRongshuSelect(product) {
-      this.form.rongshuProduct = product.name
-      this.form.equityName = product.name
-      this.form.merchantName = product.merchant
-      this.form.stockCount = product.stock
-      this.form.mainImage = product.mainImage ? [...product.mainImage] : []
-    }
-  }
-}
-</script>
-
-<style scoped>
-.edit-equity-product {
-  padding: 20px;
-}
-.header-title {
-  font-size: 18px;
-  font-weight: 500;
-}
-.section-card {
-  margin-bottom: 16px;
-  border: none;
-}
-.upload-hide-trigger >>> .el-upload--picture-card { display: none; }
-
-.section-card >>> .el-card__header {
-  padding: 12px 20px;
-  background: #fafafa;
-  border-bottom: 1px solid #ebeef5;
-}
-.section-title {
-  font-size: 16px;
-  font-weight: 600;
-  color: #303133;
-}
-.action-bar {
-  display: flex;
-  align-items: center;
-  justify-content: space-between;
-  margin-top: 20px;
-  padding-top: 20px;
-  border-top: 1px solid #ebeef5;
-}
-.action-left, .action-right {
-  width: 120px;
-}
-.action-center {
-  display: flex;
-  justify-content: center;
-  gap: 12px;
-}
-.tip-icon {
-  color: #909399;
-  cursor: pointer;
-  font-size: 14px;
-}
-.upload-size-tip {
-  margin-top: 4px;
-  font-size: 12px;
-  color: #909399;
-  line-height: 1.5;
-}
-.sms-template-preview {
-  margin-top: 12px;
-  padding: 12px;
-  background: #fff;
-  border: 1px dashed #dcdfe6;
-  border-radius: 4px;
-  font-size: 13px;
-  color: #606266;
-  line-height: 1.6;
-  white-space: pre-wrap;
-  word-break: break-all;
-}
-</style>

+ 0 - 209
pc/src/views/EquityGrantCampaign.vue

@@ -1,209 +0,0 @@
-<template>
-  <div class="equity-grant-campaign">
-    <el-card>
-      <div slot="header" class="search-section">
-        <div class="search-row">
-          <el-form :model="searchForm" label-width="100px" inline>
-            <el-form-item label="发放计划名称:">
-              <el-input v-model="searchForm.name" placeholder="关键字" clearable style="width:220px" />
-            </el-form-item>
-            <el-form-item label="发放计划时间:">
-              <el-date-picker
-                v-model="searchForm.dateRange"
-                type="daterange"
-                range-separator="至"
-                start-placeholder="开始日期"
-                end-placeholder="结束日期"
-                value-format="yyyy-MM-dd"
-                style="width:360px"
-              />
-            </el-form-item>
-            <el-form-item label="状态:">
-              <el-select v-model="searchForm.status" placeholder="全部" clearable style="width:140px">
-                <el-option label="未开始" value="未开始" />
-                <el-option label="进行中" value="进行中" />
-                <el-option label="已结束" value="已结束" />
-              </el-select>
-            </el-form-item>
-            <div class="button-group">
-              <el-button type="primary" @click="handleSearch">查询</el-button>
-              <el-button @click="handleReset">重置</el-button>
-            </div>
-          </el-form>
-        </div>
-        <div class="action-row">
-          <el-button type="primary" icon="el-icon-plus" @click="handleCreate">新建发放计划</el-button>
-        </div>
-      </div>
-
-      <el-table :data="filteredData" stripe border style="width:100%">
-        <el-table-column label="操作" width="240" fixed="left">
-          <template slot-scope="scope">
-            <el-button type="text" size="small" @click="handleView(scope.row)">查看</el-button>
-            <el-button
-              v-if="statusLabel(scope.row) !== '进行中'"
-              type="text"
-              size="small"
-              @click="handleEdit(scope.row)"
-            >编辑</el-button>
-            <el-button
-              v-if="statusLabel(scope.row) === '进行中'"
-              type="text"
-              size="small"
-              style="color:#E6A23C"
-              @click="handleEnd(scope.row)"
-            >结束</el-button>
-            <el-button
-              v-if="statusLabel(scope.row) !== '进行中'"
-              type="text"
-              size="small"
-              style="color:#F56C6C"
-              @click="handleDelete(scope.row)"
-            >删除</el-button>
-          </template>
-        </el-table-column>
-        <el-table-column prop="id" label="发放计划 ID" width="160" align="center" />
-        <el-table-column prop="name" label="发放计划名称" min-width="200" show-overflow-tooltip />
-        <el-table-column prop="startTime" label="开始时间" width="170" align="center" />
-        <el-table-column prop="endTime" label="结束时间" width="170" align="center" />
-        <el-table-column label="状态" width="100" align="center">
-          <template slot-scope="scope">
-            <el-tag :type="statusTag(scope.row)" size="small">{{ statusLabel(scope.row) }}</el-tag>
-          </template>
-        </el-table-column>
-        <el-table-column label="获取方式" width="110" align="center">
-          <template slot-scope="scope">
-            <el-tag :type="scope.row.grantMode === 'direct' ? 'warning' : 'success'" size="small">
-              {{ scope.row.grantMode === 'direct' ? '直接发放' : '用户领取' }}
-            </el-tag>
-          </template>
-        </el-table-column>
-        <el-table-column label="权益数量" width="150" align="center">
-          <template slot-scope="scope">
-            {{ grantSummary(scope.row) }}
-          </template>
-        </el-table-column>
-        <el-table-column label="权益有效期" min-width="200" align="center">
-          <template slot-scope="scope">
-            {{ validitySummary(scope.row.validity) }}
-          </template>
-        </el-table-column>
-        <el-table-column prop="createTime" label="创建时间" width="170" align="center" />
-      </el-table>
-    </el-card>
-  </div>
-</template>
-
-<script>
-import request from '@/utils/request'
-
-const grantCycleLabelMap = { total: '整个周期', monthly: '每月', quarterly: '每季度', yearly: '每年' }
-
-export default {
-  name: 'EquityGrantCampaign',
-  data() {
-    return {
-      searchForm: { name: '', dateRange: [], status: '' },
-      tableData: []
-    }
-  },
-  computed: {
-    filteredData() {
-      if (!this.searchForm.status) return this.tableData
-      return this.tableData.filter(r => this.statusLabel(r) === this.searchForm.status)
-    }
-  },
-  created() {
-    this.fetchList()
-  },
-  activated() {
-    this.fetchList()
-  },
-  watch: {
-    $route(to) {
-      if (to.path === '/customization/hsbc/equity-grant-campaign') {
-        this.fetchList()
-      }
-    }
-  },
-  methods: {
-    async fetchList() {
-      const params = { name: this.searchForm.name }
-      if (this.searchForm.dateRange && this.searchForm.dateRange.length === 2) {
-        params.startDate = this.searchForm.dateRange[0]
-        params.endDate = this.searchForm.dateRange[1]
-      }
-      const res = await request.get('/api/equity-grant-campaign/list', { params })
-      this.tableData = res.data || []
-    },
-    handleSearch() { this.fetchList() },
-    handleReset() {
-      this.searchForm = { name: '', dateRange: [], status: '' }
-      this.fetchList()
-    },
-    handleCreate() {
-      this.$router.push('/customization/hsbc/equity-grant-campaign/add')
-    },
-    handleEdit(row) {
-      this.$router.push(`/customization/hsbc/equity-grant-campaign/edit/${row.id}`)
-    },
-    handleView(row) {
-      this.$router.push(`/customization/hsbc/equity-grant-campaign/view/${row.id}`)
-    },
-    async handleEnd(row) {
-      await this.$confirm(
-        `确定结束发放计划「${row.name}」吗?结束后将立即生效,且不再继续发放权益。`,
-        '结束发放计划',
-        { type: 'warning', confirmButtonText: '确认结束', cancelButtonText: '取消' }
-      )
-      await request.post('/api/equity-grant-campaign/end', { id: row.id })
-      this.$message.success('已结束')
-      this.fetchList()
-    },
-    async handleDelete(row) {
-      await this.$confirm('您确定要删除此条发放计划吗?删除后将无法恢复。', '确认删除', {
-        type: 'warning',
-        confirmButtonText: '确认删除',
-        cancelButtonText: '取消'
-      })
-      await request.post('/api/equity-grant-campaign/delete', { id: row.id })
-      this.$message.success('删除成功')
-      this.fetchList()
-    },
-    validitySummary(v) {
-      if (!v) return '-'
-      if (v.type === 'fixed') {
-        if (!v.fixedRange || v.fixedRange.length !== 2) return '固定日期'
-        return `${v.fixedRange[0]} 至 ${v.fixedRange[1]}`
-      }
-      if (v.type === 'relative') {
-        return `自发放起 ${v.relativeDays || 0} 天内有效`
-      }
-      return '-'
-    },
-    grantSummary(row) {
-      if (!row) return '-'
-      const cycle = grantCycleLabelMap[row.grantCycle] || '整个周期'
-      return `${cycle} ${row.grantQuantity || 0} 个`
-    },
-    statusLabel(row) {
-      const now = Date.now()
-      const s = new Date(row.startTime).getTime()
-      const e = new Date(row.endTime).getTime()
-      if (now < s) return '未开始'
-      if (now > e) return '已结束'
-      return '进行中'
-    },
-    statusTag(row) {
-      const l = this.statusLabel(row)
-      return { 未开始: 'info', 进行中: 'success', 已结束: '' }[l] || ''
-    }
-  }
-}
-</script>
-
-<style scoped>
-.search-section .search-row { margin-bottom: 0; }
-.search-section .action-row { margin-top: 15px; }
-.button-group { display: inline-block; margin-left: 20px; }
-</style>

+ 0 - 375
pc/src/views/EquityGrantCampaignEdit.vue

@@ -1,375 +0,0 @@
-<template>
-  <div class="equity-grant-campaign-edit">
-    <el-card>
-      <div slot="header" class="header">
-        <span class="title">{{ pageTitle }}</span>
-      </div>
-
-      <el-form
-        ref="form"
-        :model="form"
-        :rules="rules"
-        label-width="150px"
-        style="max-width: 920px; margin: 0 auto"
-      >
-        <el-form-item v-if="isEdit" label="发放计划 ID">
-          <el-input :value="form.id" disabled style="width:320px" />
-        </el-form-item>
-        <el-form-item label="发放计划名称" prop="name">
-          <el-input
-            v-model="form.name"
-            placeholder="请输入发放计划名称"
-            maxlength="50"
-            show-word-limit
-            style="width:420px"
-          />
-        </el-form-item>
-        <el-form-item label="发放计划开始时间" prop="startTime">
-          <el-date-picker
-            v-model="form.startTime"
-            type="datetime"
-            placeholder="选择开始时间"
-            value-format="yyyy-MM-dd HH:mm:ss"
-            style="width:320px"
-          />
-        </el-form-item>
-        <el-form-item label="发放计划结束时间" prop="endTime">
-          <el-date-picker
-            v-model="form.endTime"
-            type="datetime"
-            placeholder="选择结束时间"
-            value-format="yyyy-MM-dd HH:mm:ss"
-            style="width:320px"
-          />
-        </el-form-item>
-        <el-form-item label="选择客群" prop="crowdList">
-          <el-select
-            v-model="form.crowdList"
-            multiple
-            filterable
-            collapse-tags
-            placeholder="请选择客群(可多选,支持搜索)"
-            style="width:100%"
-          >
-            <el-option
-              v-for="item in crowdOptions"
-              :key="item.id"
-              :label="`${item.name} (${item.id})`"
-              :value="item.id"
-            />
-          </el-select>
-        </el-form-item>
-        <el-form-item label="获取方式" prop="grantMode" required>
-          <el-radio-group v-model="form.grantMode" @change="handleGrantModeChange">
-            <el-radio label="claim">用户领取</el-radio>
-            <el-radio label="direct">直接发放</el-radio>
-          </el-radio-group>
-        </el-form-item>
-        <el-form-item label="选择权益" prop="equityList">
-          <el-select
-            v-if="isClaimMode"
-            v-model="form.equityList"
-            multiple
-            filterable
-            collapse-tags
-            placeholder="请选择借记卡权益(可多选)"
-            style="width:100%"
-          >
-            <el-option
-              v-for="item in equityOptions"
-              :key="item.productId"
-              :label="`${item.equityName} (${item.productId})`"
-              :value="item.productId"
-            />
-          </el-select>
-          <el-select
-            v-else
-            v-model="singleEquityId"
-            filterable
-            placeholder="请选择借记卡权益(仅可单选)"
-            style="width:100%"
-          >
-            <el-option
-              v-for="item in equityOptions"
-              :key="item.productId"
-              :label="`${item.equityName} (${item.productId})`"
-              :value="item.productId"
-            />
-          </el-select>
-        </el-form-item>
-        <el-form-item label="权益数量" required>
-          <div class="grant-row">
-            <el-select v-model="form.grantCycle" placeholder="类型" style="width:140px">
-              <el-option label="整个周期" value="total" />
-              <el-option label="每月" value="monthly" />
-              <el-option label="每季度" value="quarterly" />
-              <el-option label="每年" value="yearly" />
-            </el-select>
-            <el-input-number
-              v-model="form.grantQuantity"
-              :min="1"
-              :max="9999"
-              :controls-position="'right'"
-              style="width:200px;margin-left:12px"
-            />
-            <span class="grant-suffix">个</span>
-          </div>
-        </el-form-item>
-        <el-form-item label="权益有效期" required>
-          <el-radio-group v-model="form.validity.type">
-            <el-radio label="fixed">固定日期</el-radio>
-            <el-radio label="relative">相对日期</el-radio>
-          </el-radio-group>
-          <div v-if="form.validity.type === 'fixed'" class="validity-row">
-            <el-date-picker
-              v-model="form.validity.fixedRange"
-              type="daterange"
-              range-separator="至"
-              start-placeholder="开始日期"
-              end-placeholder="结束日期"
-              value-format="yyyy-MM-dd"
-              style="width:360px"
-            />
-          </div>
-          <div v-else class="validity-row">
-            <el-input-number
-              v-model="form.validity.relativeDays"
-              :min="1"
-              :max="3650"
-              :controls-position="'right'"
-              style="width:200px"
-            />
-            <span class="grant-suffix">天(自发放起算)</span>
-          </div>
-        </el-form-item>
-        <el-form-item label="短信通知" prop="smsTemplateId" required>
-          <el-select
-            v-model="form.smsTemplateId"
-            placeholder="请选择短信模板"
-            style="width:420px"
-            @change="handleSmsTemplateChange"
-          >
-            <el-option
-              v-for="item in smsTemplateOptions"
-              :key="item.id"
-              :label="item.name"
-              :value="item.id"
-            />
-          </el-select>
-          <div v-if="form.smsTemplate" class="sms-template-preview">{{ form.smsTemplate }}</div>
-        </el-form-item>
-      </el-form>
-
-      <div class="footer">
-        <el-button @click="handleCancel">取消</el-button>
-        <el-button type="primary" @click="handleSubmit">保存</el-button>
-      </div>
-    </el-card>
-  </div>
-</template>
-
-<script>
-import request from '@/utils/request'
-import crowdNameListStore from '@/store/crowdNameListStore'
-
-function emptyForm() {
-  return {
-    id: '',
-    name: '',
-    startTime: '',
-    endTime: '',
-    crowdList: [],
-    grantMode: 'claim',
-    equityList: [],
-    grantCycle: 'total',
-    grantQuantity: 1,
-    validity: { type: 'fixed', fixedRange: [], relativeDays: 30 },
-    smsTemplateId: '',
-    smsTemplate: ''
-  }
-}
-
-const SMS_TEMPLATE_OPTIONS = [
-  { id: 'GRANT_SMS_001', name: '发放成功通用模板', content: '【银行】尊敬的${customerName},您已获得${productName}权益,发放时间:${grantTime},请于${expireDate}前使用。' },
-  { id: 'GRANT_SMS_002', name: '发放成功-含查看入口', content: '【银行】尊敬的${customerName},您已获得${productName}权益,有效期至${expireDate},请登录APP查看详情。' },
-  { id: 'GRANT_SMS_003', name: '发放成功-简洁版', content: '【银行】${customerName}您好,${productName}已为您发放成功,有效期至${expireDate}。' }
-]
-
-export default {
-  name: 'EquityGrantCampaignEdit',
-  data() {
-    return {
-      form: emptyForm(),
-      equityOptions: [],
-      smsTemplateOptions: SMS_TEMPLATE_OPTIONS,
-      rules: {
-        name: [{ required: true, message: '请输入发放计划名称', trigger: 'blur' }],
-        startTime: [{ required: true, message: '请选择开始时间', trigger: 'change' }],
-        endTime: [{ required: true, message: '请选择结束时间', trigger: 'change' }],
-        crowdList: [
-          { type: 'array', required: true, message: '请至少选择一个客群', trigger: 'change' }
-        ],
-        grantMode: [
-          { required: true, message: '请选择获取方式', trigger: 'change' }
-        ],
-        equityList: [
-          {
-            validator: (rule, value, cb) => {
-              if (!value || value.length === 0) {
-                return cb(new Error(this.isClaimMode ? '请至少选择一个权益' : '请选择一个权益'))
-              }
-              cb()
-            },
-            trigger: 'change'
-          }
-        ],
-        smsTemplateId: [
-          { required: true, message: '请选择短信模板', trigger: 'change' }
-        ]
-      }
-    }
-  },
-  computed: {
-    isEdit() {
-      return this.$route.name === 'EquityGrantCampaignEdit'
-    },
-    pageTitle() {
-      return this.isEdit ? '编辑发放计划' : '新建发放计划'
-    },
-    crowdOptions() {
-      return crowdNameListStore.list
-    },
-    isClaimMode() {
-      return this.form.grantMode === 'claim'
-    },
-    singleEquityId: {
-      get() {
-        return (this.form.equityList && this.form.equityList[0]) || ''
-      },
-      set(val) {
-        this.form.equityList = val ? [val] : []
-      }
-    }
-  },
-  async created() {
-    await this.fetchEquityOptions()
-    if (this.isEdit) await this.loadDetail()
-  },
-  methods: {
-    async fetchEquityOptions() {
-      const res = await request.get('/api/equity/list', { params: { shelfStatus: '已上架' } })
-      this.equityOptions = res.data || []
-    },
-    async loadDetail() {
-      const id = this.$route.params.id
-      const res = await request.get('/api/equity-grant-campaign/detail', { params: { id } })
-      const row = res.data
-      if (!row) {
-        this.$message.error('发放计划不存在')
-        this.$router.replace('/customization/hsbc/equity-grant-campaign')
-        return
-      }
-      const now = Date.now()
-      const s = new Date(row.startTime).getTime()
-      const e = new Date(row.endTime).getTime()
-      if (now >= s && now <= e) {
-        this.$message.warning('进行中的发放计划不可编辑')
-        this.$router.replace('/customization/hsbc/equity-grant-campaign')
-        return
-      }
-      const v = row.validity || { type: 'fixed', fixedRange: [], relativeDays: 30 }
-      this.form = {
-        id: row.id,
-        name: row.name,
-        startTime: row.startTime,
-        endTime: row.endTime,
-        crowdList: [...(row.crowdList || [])],
-        grantMode: row.grantMode || 'claim',
-        equityList: [...(row.equityList || [])],
-        grantCycle: row.grantCycle || 'total',
-        grantQuantity: row.grantQuantity || 1,
-        validity: {
-          type: v.type || 'fixed',
-          fixedRange: Array.isArray(v.fixedRange) ? [...v.fixedRange] : [],
-          relativeDays: v.relativeDays || 30
-        },
-        smsTemplateId: row.smsTemplateId || '',
-        smsTemplate: row.smsTemplate || ''
-      }
-    },
-    handleGrantModeChange(mode) {
-      if (mode === 'direct' && this.form.equityList.length > 1) {
-        this.form.equityList = [this.form.equityList[0]]
-      }
-    },
-    handleSmsTemplateChange(id) {
-      const tpl = this.smsTemplateOptions.find(i => i.id === id)
-      this.form.smsTemplate = tpl ? tpl.content : ''
-    },
-    handleSubmit() {
-      this.$refs.form.validate(async valid => {
-        if (!valid) return
-        if (this.form.startTime && this.form.endTime && this.form.startTime >= this.form.endTime) {
-          this.$message.warning('结束时间必须晚于开始时间')
-          return
-        }
-        const v = this.form.validity
-        if (v.type === 'fixed') {
-          if (!v.fixedRange || v.fixedRange.length !== 2) {
-            this.$message.warning('请选择权益有效期的开始与结束日期')
-            return
-          }
-        } else if (!v.relativeDays || v.relativeDays < 1) {
-          this.$message.warning('请输入权益有效期的天数')
-          return
-        }
-        if (!this.form.grantQuantity || this.form.grantQuantity < 1) {
-          this.$message.warning('请输入权益数量')
-          return
-        }
-        if (!this.isClaimMode && this.form.equityList.length !== 1) {
-          this.$message.warning('直接发放仅支持选择一个权益')
-          return
-        }
-        if (!this.form.smsTemplateId) {
-          this.$message.warning('请选择短信模板')
-          return
-        }
-        await request.post('/api/equity-grant-campaign/save', this.form)
-        this.$message.success(this.isEdit ? '保存成功' : '新建成功')
-        this.$router.push('/customization/hsbc/equity-grant-campaign')
-      })
-    },
-    handleCancel() {
-      this.$router.push('/customization/hsbc/equity-grant-campaign')
-    }
-  }
-}
-</script>
-
-<style scoped>
-.equity-grant-campaign-edit { padding: 0; }
-.header { display: flex; align-items: center; justify-content: center; }
-.title { font-size: 16px; font-weight: 600; color: #409EFF; }
-.grant-row { display: flex; align-items: center; }
-.grant-suffix { margin-left: 8px; color: #606266; }
-.validity-row { margin-top: 8px; display: flex; align-items: center; }
-.footer {
-  text-align: center;
-  margin-top: 24px;
-  padding-top: 16px;
-  border-top: 1px solid #ebeef5;
-}
-.footer .el-button { min-width: 100px; }
-.sms-template-preview {
-  margin-top: 8px;
-  padding: 10px 12px;
-  background: #f5f7fa;
-  border: 1px dashed #dcdfe6;
-  border-radius: 4px;
-  font-size: 13px;
-  color: #606266;
-  line-height: 1.6;
-  word-break: break-all;
-}
-</style>

+ 0 - 175
pc/src/views/EquityGrantCampaignView.vue

@@ -1,175 +0,0 @@
-<template>
-  <div class="equity-grant-campaign-view">
-    <el-card v-if="current">
-      <div slot="header" class="header">
-        <span class="title">查看发放计划</span>
-      </div>
-
-      <el-descriptions :column="2" border>
-        <el-descriptions-item label="发放计划 ID">{{ current.id }}</el-descriptions-item>
-        <el-descriptions-item label="发放计划名称">{{ current.name }}</el-descriptions-item>
-        <el-descriptions-item label="状态">
-          <el-tag :type="statusTag" size="small">{{ statusLabel }}</el-tag>
-        </el-descriptions-item>
-        <el-descriptions-item label="获取方式">
-          <el-tag :type="current.grantMode === 'direct' ? 'warning' : 'success'" size="small">{{ grantModeLabel }}</el-tag>
-        </el-descriptions-item>
-        <el-descriptions-item label="权益数量">{{ grantSummary }}</el-descriptions-item>
-        <el-descriptions-item label="开始时间">{{ current.startTime || '-' }}</el-descriptions-item>
-        <el-descriptions-item label="结束时间">{{ current.endTime || '-' }}</el-descriptions-item>
-        <el-descriptions-item label="权益有效期" :span="2">{{ validitySummary }}</el-descriptions-item>
-        <el-descriptions-item label="选择客群" :span="2">
-          <template v-if="(current.crowdList || []).length">
-            <el-tag
-              v-for="cid in current.crowdList"
-              :key="cid"
-              size="small"
-              type="info"
-              style="margin-right:6px;margin-top:4px"
-            >{{ crowdLabel(cid) }}</el-tag>
-          </template>
-          <span v-else>-</span>
-        </el-descriptions-item>
-        <el-descriptions-item label="选择权益" :span="2">
-          <template v-if="(current.equityList || []).length">
-            <el-tag
-              v-for="pid in current.equityList"
-              :key="pid"
-              size="small"
-              style="margin-right:6px;margin-top:4px"
-            >{{ equityLabel(pid) }}</el-tag>
-          </template>
-          <span v-else>-</span>
-        </el-descriptions-item>
-        <el-descriptions-item label="短信通知" :span="2">
-          <div class="sms-info-row">
-            <span class="sms-tpl-name">{{ smsTemplateName }}</span>
-          </div>
-          <div v-if="current.smsTemplate" class="sms-template-preview">{{ current.smsTemplate }}</div>
-        </el-descriptions-item>
-        <el-descriptions-item label="创建时间">{{ current.createTime || '-' }}</el-descriptions-item>
-        <el-descriptions-item label="更新时间">{{ current.updateTime || '-' }}</el-descriptions-item>
-      </el-descriptions>
-
-      <div class="footer">
-        <el-button type="primary" @click="goBack">返回</el-button>
-      </div>
-    </el-card>
-  </div>
-</template>
-
-<script>
-import request from '@/utils/request'
-import crowdNameListStore from '@/store/crowdNameListStore'
-
-const grantCycleLabelMap = { total: '整个周期', monthly: '每月', quarterly: '每季度', yearly: '每年' }
-
-const SMS_TEMPLATE_NAME_MAP = {
-  GRANT_SMS_001: '发放成功通用模板',
-  GRANT_SMS_002: '发放成功-含查看入口',
-  GRANT_SMS_003: '发放成功-简洁版'
-}
-
-export default {
-  name: 'EquityGrantCampaignView',
-  data() {
-    return { current: null, equityOptions: [] }
-  },
-  computed: {
-    crowdOptions() {
-      return crowdNameListStore.list
-    },
-    statusLabel() {
-      if (!this.current) return ''
-      const now = Date.now()
-      const s = new Date(this.current.startTime).getTime()
-      const e = new Date(this.current.endTime).getTime()
-      if (now < s) return '未开始'
-      if (now > e) return '已结束'
-      return '进行中'
-    },
-    statusTag() {
-      return { 未开始: 'info', 进行中: 'success', 已结束: '' }[this.statusLabel] || ''
-    },
-    grantSummary() {
-      if (!this.current) return '-'
-      const cycle = grantCycleLabelMap[this.current.grantCycle] || '整个周期'
-      return `${cycle} ${this.current.grantQuantity || 0} 个`
-    },
-    grantModeLabel() {
-      return this.current && this.current.grantMode === 'direct' ? '直接发放' : '用户领取'
-    },
-    smsTemplateName() {
-      if (!this.current) return '-'
-      return SMS_TEMPLATE_NAME_MAP[this.current.smsTemplateId] || this.current.smsTemplateId || '-'
-    },
-    validitySummary() {
-      const v = this.current && this.current.validity
-      if (!v) return '-'
-      if (v.type === 'fixed') {
-        if (!v.fixedRange || v.fixedRange.length !== 2) return '固定日期'
-        return `${v.fixedRange[0]} 至 ${v.fixedRange[1]}`
-      }
-      if (v.type === 'relative') {
-        return `自发放起 ${v.relativeDays || 0} 天内有效`
-      }
-      return '-'
-    }
-  },
-  async created() {
-    await this.fetchEquityOptions()
-    await this.loadDetail()
-  },
-  methods: {
-    async fetchEquityOptions() {
-      const res = await request.get('/api/equity/list', { params: { shelfStatus: '已上架' } })
-      this.equityOptions = res.data || []
-    },
-    async loadDetail() {
-      const id = this.$route.params.id
-      const res = await request.get('/api/equity-grant-campaign/detail', { params: { id } })
-      this.current = res.data
-      if (!this.current) {
-        this.$message.error('发放计划不存在')
-        this.$router.replace('/customization/hsbc/equity-grant-campaign')
-      }
-    },
-    equityLabel(productId) {
-      const m = this.equityOptions.find(i => i.productId === productId)
-      return m ? `${m.equityName} (${m.productId})` : productId
-    },
-    crowdLabel(crowdId) {
-      const m = this.crowdOptions.find(i => i.id === crowdId)
-      return m ? `${m.name} (${m.id})` : crowdId
-    },
-    goBack() {
-      this.$router.push('/customization/hsbc/equity-grant-campaign')
-    }
-  }
-}
-</script>
-
-<style scoped>
-.equity-grant-campaign-view { padding: 0; }
-.header { display: flex; align-items: center; justify-content: center; }
-.title { font-size: 16px; font-weight: 600; color: #409EFF; }
-.footer {
-  text-align: center;
-  margin-top: 24px;
-  padding-top: 16px;
-  border-top: 1px solid #ebeef5;
-}
-.sms-info-row { display: flex; align-items: center; gap: 8px; }
-.sms-tpl-name { color: #303133; }
-.sms-template-preview {
-  margin-top: 8px;
-  padding: 10px 12px;
-  background: #f5f7fa;
-  border: 1px dashed #dcdfe6;
-  border-radius: 4px;
-  font-size: 13px;
-  color: #606266;
-  line-height: 1.6;
-  word-break: break-all;
-}
-</style>

+ 0 - 502
pc/src/views/EquityProductCoupons.vue

@@ -1,502 +0,0 @@
-<template>
-  <div class="equity-product-coupons">
-    <el-card>
-      <div slot="header" class="header-title">
-        <span>券码管理</span>
-        <span class="product-id">权益名称:{{ equityName }}</span>
-      </div>
-
-      <div class="toolbar">
-        <el-button type="primary" icon="el-icon-upload2" @click="openImportDialog">导入券码</el-button>
-      </div>
-
-      <el-table :data="batchList" stripe style="width: 100%">
-        <el-table-column prop="importCount" label="导入数量" width="100"></el-table-column>
-        <el-table-column prop="actualImportCount" label="实际导入数量" width="120"></el-table-column>
-        <el-table-column prop="failCount" label="导入失败数量" width="120"></el-table-column>
-        <el-table-column prop="remainingCount" label="券码剩余数量" width="120"></el-table-column>
-        <el-table-column prop="effectiveTime" label="生效时间" width="180"></el-table-column>
-        <el-table-column prop="expireTime" label="过期时间" width="180"></el-table-column>
-        <el-table-column prop="stopIssueTime" label="停止发放时间" width="180"></el-table-column>
-        <el-table-column label="文件处理状态" width="120">
-          <template slot-scope="scope">
-            <el-tag :type="statusTagType(scope.row.fileStatus)" size="small">{{ scope.row.fileStatus }}</el-tag>
-          </template>
-        </el-table-column>
-        <el-table-column prop="importTime" label="导入时间" width="180"></el-table-column>
-        <el-table-column prop="creator" label="创建人" width="100"></el-table-column>
-        <el-table-column label="操作" width="220" fixed="right">
-          <template slot-scope="scope">
-            <el-button type="text" size="small" @click="handleEdit(scope.row)">编辑</el-button>
-            <el-button
-              v-if="Number(scope.row.remainingCount) > 0"
-              type="text"
-              size="small"
-              @click="handleRecycle(scope.row)"
-            >回收</el-button>
-            <el-button
-              v-if="Number(scope.row.failCount) > 0"
-              type="text"
-              size="small"
-              @click="handleExportFail(scope.row)"
-            >导出失败券码</el-button>
-          </template>
-        </el-table-column>
-      </el-table>
-    </el-card>
-
-    <el-dialog
-      title="编辑券码批次"
-      :visible.sync="editDialogVisible"
-      width="520px"
-      @closed="resetEditForm"
-    >
-      <el-form :model="editForm" label-width="120px" label-position="left">
-        <el-form-item label="生效时间" required>
-          <el-date-picker
-            v-model="editForm.effectiveTime"
-            type="datetime"
-            placeholder="请选择生效时间"
-            format="yyyy-MM-dd HH:mm:ss"
-            value-format="yyyy-MM-dd HH:mm:ss"
-            style="width: 100%"
-          ></el-date-picker>
-        </el-form-item>
-        <el-form-item label="过期时间" required>
-          <el-date-picker
-            v-model="editForm.expireTime"
-            type="datetime"
-            placeholder="请选择过期时间"
-            format="yyyy-MM-dd HH:mm:ss"
-            value-format="yyyy-MM-dd HH:mm:ss"
-            style="width: 100%"
-          ></el-date-picker>
-        </el-form-item>
-        <el-form-item label="停止发放时间" required>
-          <el-date-picker
-            v-model="editForm.stopIssueTime"
-            type="datetime"
-            placeholder="请选择停止发放时间"
-            format="yyyy-MM-dd HH:mm:ss"
-            value-format="yyyy-MM-dd HH:mm:ss"
-            style="width: 100%"
-          ></el-date-picker>
-        </el-form-item>
-      </el-form>
-      <span slot="footer">
-        <el-button @click="editDialogVisible = false">取消</el-button>
-        <el-button type="primary" @click="confirmEdit">确定</el-button>
-      </span>
-    </el-dialog>
-
-    <el-dialog
-      title="券码批次回收"
-      :visible.sync="recycleDialogVisible"
-      width="640px"
-      @closed="resetRecycleForm"
-    >
-      <div class="recycle-types">
-        <div
-          v-for="opt in recycleTypeOptions"
-          :key="opt.value"
-          :class="['recycle-type-item', { active: recycleForm.type === opt.value }]"
-          @click="recycleForm.type = opt.value"
-        >
-          <el-radio v-model="recycleForm.type" :label="opt.value">{{ opt.label }}</el-radio>
-        </div>
-      </div>
-
-      <el-form label-width="100px" label-position="left" style="margin-top: 24px;">
-        <el-form-item label="回收数量" required>
-          <el-input-number
-            v-model="recycleForm.amount"
-            :min="1"
-            :max="recycleMaxAmount"
-            :placeholder="`输入范围1-${recycleMaxAmount}`"
-            :controls-position="''"
-            style="width: 100%"
-          ></el-input-number>
-          <div class="recycle-tip">本批次{{ currentRecycleTypeLabel }}最多可回收数量:{{ recycleMaxAmount }}</div>
-        </el-form-item>
-      </el-form>
-
-      <span slot="footer" class="dialog-footer-center">
-        <el-button @click="recycleDialogVisible = false">取消</el-button>
-        <el-button type="primary" @click="confirmRecycle">提交</el-button>
-      </span>
-    </el-dialog>
-
-    <el-dialog title="导入券码" :visible.sync="importDialogVisible" width="520px" @closed="resetImportForm">
-      <el-form :model="importForm" label-width="120px" label-position="left">
-        <el-form-item label="券码文件" required>
-          <el-upload
-            action=""
-            :auto-upload="false"
-            :file-list="importForm.files"
-            :on-change="handleFileChange"
-            :on-remove="handleFileRemove"
-            :limit="1"
-          >
-            <el-button icon="el-icon-upload2">选择文件</el-button>
-            <div slot="tip" class="el-upload__tip">支持 .xlsx / .csv,仅可上传一个文件</div>
-          </el-upload>
-          <el-button type="text" icon="el-icon-download" @click="downloadTemplate">模板下载</el-button>
-        </el-form-item>
-        <el-form-item label="生效时间" required>
-          <el-date-picker
-            v-model="importForm.effectiveTime"
-            type="datetime"
-            placeholder="请选择生效时间"
-            format="yyyy-MM-dd HH:mm:ss"
-            value-format="yyyy-MM-dd HH:mm:ss"
-            style="width: 100%"
-          ></el-date-picker>
-        </el-form-item>
-        <el-form-item label="过期时间" required>
-          <el-date-picker
-            v-model="importForm.expireTime"
-            type="datetime"
-            placeholder="请选择过期时间"
-            format="yyyy-MM-dd HH:mm:ss"
-            value-format="yyyy-MM-dd HH:mm:ss"
-            style="width: 100%"
-          ></el-date-picker>
-        </el-form-item>
-        <el-form-item label="停止发放时间" required>
-          <el-date-picker
-            v-model="importForm.stopIssueTime"
-            type="datetime"
-            placeholder="请选择停止发放时间"
-            format="yyyy-MM-dd HH:mm:ss"
-            value-format="yyyy-MM-dd HH:mm:ss"
-            style="width: 100%"
-          ></el-date-picker>
-        </el-form-item>
-      </el-form>
-      <span slot="footer">
-        <el-button @click="importDialogVisible = false">取消</el-button>
-        <el-button type="primary" @click="confirmImport">确定</el-button>
-      </span>
-    </el-dialog>
-  </div>
-</template>
-
-<script>
-export default {
-  name: 'EquityProductCoupons',
-  data() {
-    return {
-      importDialogVisible: false,
-      editDialogVisible: false,
-      editingRow: null,
-      editForm: {
-        effectiveTime: '',
-        expireTime: '',
-        stopIssueTime: ''
-      },
-      recycleDialogVisible: false,
-      recycleRow: null,
-      recycleForm: {
-        type: 'unissued',
-        amount: undefined
-      },
-      recycleTypeOptions: [
-        { label: '未发放券码回收', value: 'unissued' },
-        { label: '已过期券码回收', value: 'expired' },
-        { label: '导错码券码回收', value: 'wrong' }
-      ],
-      importForm: {
-        files: [],
-        effectiveTime: '',
-        expireTime: '',
-        stopIssueTime: ''
-      },
-      batchList: [
-        {
-          importCount: 1000,
-          actualImportCount: 998,
-          failCount: 2,
-          remainingCount: 720,
-          effectiveTime: '2026-04-16 00:00:00',
-          expireTime: '2026-12-31 23:59:59',
-          stopIssueTime: '2026-11-30 23:59:59',
-          fileStatus: '处理成功',
-          importTime: '2026-04-15 10:32:18',
-          creator: '张三'
-        },
-        {
-          importCount: 500,
-          actualImportCount: 500,
-          failCount: 0,
-          remainingCount: 320,
-          effectiveTime: '2026-04-21 00:00:00',
-          expireTime: '2026-09-30 23:59:59',
-          stopIssueTime: '2026-08-31 23:59:59',
-          fileStatus: '处理成功',
-          importTime: '2026-04-20 14:15:42',
-          creator: '李四'
-        },
-        {
-          importCount: 800,
-          actualImportCount: 0,
-          failCount: 0,
-          remainingCount: 0,
-          effectiveTime: '2026-05-08 00:00:00',
-          expireTime: '2026-12-31 23:59:59',
-          stopIssueTime: '2026-11-30 23:59:59',
-          fileStatus: '处理中',
-          importTime: '2026-05-07 09:08:11',
-          creator: '王五'
-        },
-        {
-          importCount: 200,
-          actualImportCount: 0,
-          failCount: 200,
-          remainingCount: 0,
-          effectiveTime: '2026-05-02 00:00:00',
-          expireTime: '2026-10-31 23:59:59',
-          stopIssueTime: '2026-09-30 23:59:59',
-          fileStatus: '处理失败',
-          importTime: '2026-05-01 16:45:30',
-          creator: '赵六'
-        }
-      ]
-    }
-  },
-  computed: {
-    productId() {
-      return this.$route.params.id || ''
-    },
-    equityName() {
-      const map = {
-        'Q100001': '权益A',
-        'Q100002': '权益B',
-        'Q100003': '权益C',
-        'Q100004': '权益D',
-        'Q100005': '权益E'
-      }
-      return map[this.productId] || this.productId
-    },
-    currentRecycleTypeLabel() {
-      const opt = this.recycleTypeOptions.find(o => o.value === this.recycleForm.type)
-      if (!opt) return ''
-      return opt.label.replace('回收', '')
-    },
-    recycleMaxAmount() {
-      const row = this.recycleRow
-      if (!row) return 0
-      switch (this.recycleForm.type) {
-        case 'unissued': return Number(row.remainingCount) || 0
-        case 'expired': {
-          const ts = row.expireTime ? new Date(row.expireTime).getTime() : 0
-          return ts && ts < Date.now() ? Number(row.remainingCount) || 0 : 0
-        }
-        case 'wrong': return Number(row.failCount) || 0
-        default: return 0
-      }
-    }
-  },
-  methods: {
-    statusTagType(status) {
-      switch (status) {
-        case '处理成功': return 'success'
-        case '处理中': return 'warning'
-        case '处理失败': return 'danger'
-        default: return 'info'
-      }
-    },
-    openImportDialog() {
-      this.importDialogVisible = true
-    },
-    downloadTemplate() {
-      this.$message.success('模板已开始下载')
-    },
-    handleFileChange(file) {
-      this.importForm.files = [file]
-    },
-    handleFileRemove() {
-      this.importForm.files = []
-    },
-    resetImportForm() {
-      this.importForm = {
-        files: [],
-        effectiveTime: '',
-        expireTime: '',
-        stopIssueTime: ''
-      }
-    },
-    confirmImport() {
-      if (!this.importForm.files.length) {
-        this.$message.warning('请选择券码文件')
-        return
-      }
-      if (!this.importForm.effectiveTime) {
-        this.$message.warning('请选择生效时间')
-        return
-      }
-      if (!this.importForm.expireTime) {
-        this.$message.warning('请选择过期时间')
-        return
-      }
-      if (!this.importForm.stopIssueTime) {
-        this.$message.warning('请选择停止发放时间')
-        return
-      }
-      this.batchList.unshift({
-        importCount: 0,
-        actualImportCount: 0,
-        failCount: 0,
-        remainingCount: 0,
-        effectiveTime: this.importForm.effectiveTime,
-        expireTime: this.importForm.expireTime,
-        stopIssueTime: this.importForm.stopIssueTime,
-        fileStatus: '处理中',
-        importTime: this.formatNow(),
-        creator: '当前用户'
-      })
-      this.$message.success('券码已提交,正在处理中')
-      this.importDialogVisible = false
-    },
-    formatNow() {
-      const d = new Date()
-      const pad = n => String(n).padStart(2, '0')
-      return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())} ${pad(d.getHours())}:${pad(d.getMinutes())}:${pad(d.getSeconds())}`
-    },
-    handleEdit(row) {
-      this.editingRow = row
-      this.editForm = {
-        effectiveTime: row.effectiveTime || '',
-        expireTime: row.expireTime || '',
-        stopIssueTime: row.stopIssueTime || ''
-      }
-      this.editDialogVisible = true
-    },
-    resetEditForm() {
-      this.editingRow = null
-      this.editForm = { effectiveTime: '', expireTime: '', stopIssueTime: '' }
-    },
-    confirmEdit() {
-      if (!this.editForm.effectiveTime) {
-        this.$message.warning('请选择生效时间')
-        return
-      }
-      if (!this.editForm.expireTime) {
-        this.$message.warning('请选择过期时间')
-        return
-      }
-      if (!this.editForm.stopIssueTime) {
-        this.$message.warning('请选择停止发放时间')
-        return
-      }
-      if (this.editingRow) {
-        this.$set(this.editingRow, 'effectiveTime', this.editForm.effectiveTime)
-        this.$set(this.editingRow, 'expireTime', this.editForm.expireTime)
-        this.$set(this.editingRow, 'stopIssueTime', this.editForm.stopIssueTime)
-        this.$message.success('保存成功')
-      }
-      this.editDialogVisible = false
-    },
-    handleRecycle(row) {
-      this.recycleRow = row
-      this.recycleForm = { type: 'unissued', amount: undefined }
-      this.recycleDialogVisible = true
-    },
-    handleExportFail(row) {
-      this.$message.success(`已开始导出失败券码(${row.failCount} 条)`)
-    },
-    resetRecycleForm() {
-      this.recycleRow = null
-      this.recycleForm = { type: 'unissued', amount: undefined }
-    },
-    confirmRecycle() {
-      const max = this.recycleMaxAmount
-      const amount = Number(this.recycleForm.amount)
-      if (!amount || amount < 1) {
-        this.$message.warning('请输入回收数量')
-        return
-      }
-      if (amount > max) {
-        this.$message.error(`回收数量不能超过 ${max}`)
-        return
-      }
-      if (this.recycleRow && this.recycleForm.type === 'unissued') {
-        this.recycleRow.remainingCount = Math.max(0, Number(this.recycleRow.remainingCount) - amount)
-      }
-      this.$message.success('回收成功')
-      this.recycleDialogVisible = false
-    }
-  }
-}
-</script>
-
-<style scoped>
-.equity-product-coupons {
-  padding: 20px;
-}
-
-.header-title {
-  font-size: 18px;
-  font-weight: 500;
-  display: flex;
-  align-items: center;
-  gap: 16px;
-}
-
-.product-id {
-  font-size: 14px;
-  font-weight: normal;
-  color: #909399;
-  margin-left: 16px;
-}
-
-.toolbar {
-  display: flex;
-  align-items: center;
-  gap: 16px;
-  margin-bottom: 16px;
-}
-
-.toolbar-title {
-  font-size: 14px;
-  font-weight: 500;
-  color: #303133;
-  margin-left: auto;
-}
-
-.recycle-types {
-  display: flex;
-  gap: 16px;
-  justify-content: space-between;
-}
-
-.recycle-type-item {
-  flex: 1;
-  border: 1px solid #dcdfe6;
-  border-radius: 4px;
-  padding: 12px 16px;
-  cursor: pointer;
-  transition: all 0.2s;
-}
-
-.recycle-type-item.active {
-  border-color: #409eff;
-  background: #ecf5ff;
-}
-
-.recycle-type-item >>> .el-radio__label {
-  font-size: 14px;
-  color: #303133;
-}
-
-.recycle-tip {
-  margin-top: 8px;
-  font-size: 13px;
-  color: #909399;
-}
-
-.dialog-footer-center {
-  display: flex;
-  justify-content: center;
-  gap: 24px;
-}
-</style>

+ 0 - 455
pc/src/views/EquityProducts.vue

@@ -1,455 +0,0 @@
-<template>
-  <div class="equity-products">
-    <el-card>
-      <!-- 搜索区域 -->
-      <div class="search-section" slot="header">
-        <div class="search-row">
-          <el-form :model="searchForm" label-width="80px" inline>
-            <el-form-item label="权益名称:">
-              <el-input v-model="searchForm.equityName" placeholder="请输入" style="width: 150px"></el-input>
-            </el-form-item>
-            <el-form-item label="权益编号:">
-              <el-input v-model="searchForm.productId" placeholder="请输入" style="width: 150px"></el-input>
-            </el-form-item>
-            <el-form-item label="商品状态:">
-              <el-select v-model="searchForm.shelfStatus" placeholder="全部" clearable style="width: 130px">
-                <el-option label="未上架" value="未上架"></el-option>
-                <el-option label="已上架" value="已上架"></el-option>
-              </el-select>
-            </el-form-item>
-            <el-form-item label="审核状态:">
-              <el-select v-model="searchForm.auditStatus" placeholder="全部" clearable style="width: 150px">
-                <el-option label="草稿" value="草稿"></el-option>
-                <el-option label="上架待审核" value="上架待审核"></el-option>
-                <el-option label="下架待审核" value="下架待审核"></el-option>
-                <el-option label="审核通过" value="审核通过"></el-option>
-              </el-select>
-            </el-form-item>
-            <div class="button-group" style="margin-left: 20px;">
-              <el-button type="primary" @click="loadData">查询</el-button>
-              <el-button @click="handleReset">重置</el-button>
-            </div>
-          </el-form>
-        </div>
-        <div style="margin-top: 15px;">
-          <el-button type="primary" icon="el-icon-plus" @click="createProduct">添加权益</el-button>
-        </div>
-      </div>
-
-      <!-- 表格区域 -->
-      <el-table :data="tableData" stripe style="width: 100%">
-        <el-table-column label="操作" width="240" fixed="left">
-          <template slot-scope="scope">
-            <el-button type="text" size="small" @click="handleView(scope.row)">查看</el-button>
-            <el-button
-              v-if="canEdit(scope.row)"
-              type="text"
-              size="small"
-              @click="handleEdit(scope.row)"
-            >编辑</el-button>
-            <el-button
-              v-if="canRequestOffshelf(scope.row)"
-              type="text"
-              size="small"
-              @click="handleOffshelf(scope.row)"
-            >下架</el-button>
-            <el-popover
-              placement="bottom"
-              trigger="click"
-              :ref="`morePopover-${scope.$index}`"
-            >
-              <div class="more-actions-menu">
-                <el-popover
-                  placement="left"
-                  trigger="click"
-                  width="240"
-                  @show="onAdjustOrderShow(scope.$index)"
-                  :ref="`adjustPopover-${scope.$index}`"
-                >
-                  <div class="adjust-order-form">
-                    <div class="adjust-order-title">调整显示顺序</div>
-                    <div class="adjust-order-tip">移动端权益首页和列表页按照该顺序展示</div>
-                    <div class="adjust-order-input">
-                      <span>调整到第</span>
-                      <el-input-number
-                        v-model.number="adjustOrderPosition"
-                        :min="1"
-                        :max="tableData.length"
-                        size="mini"
-                        :controls="false"
-                        style="width: 70px;margin: 0 6px;"
-                      />
-                      <span>位</span>
-                    </div>
-                    <div class="adjust-order-actions">
-                      <el-button size="mini" @click="closeAdjustOrder(scope.$index)">取消</el-button>
-                      <el-button size="mini" type="primary" @click="confirmAdjustOrder(scope.$index)">确定</el-button>
-                    </div>
-                  </div>
-                  <div slot="reference" class="more-actions-item">调整显示顺序</div>
-                </el-popover>
-                <div
-                  class="more-actions-item"
-                  @click="handleCopyFromMore(scope.row, scope.$index)"
-                >复制</div>
-                <div
-                  class="more-actions-item"
-                  @click="openLinkDialogFromMore(scope.row, scope.$index)"
-                >生成链接</div>
-                <div
-                  v-if="scope.row.source === '银行自有' && scope.row.productType === '券码'"
-                  class="more-actions-item"
-                  @click="handleImportCouponsFromMore(scope.row, scope.$index)"
-                >导入券码</div>
-                <div
-                  v-if="scope.row.source === '银行自有' && scope.row.productType === '券码'"
-                  class="more-actions-item"
-                  @click="handleManageCouponsFromMore(scope.row, scope.$index)"
-                >券码管理</div>
-                <div
-                  v-if="canDelete(scope.row)"
-                  class="more-actions-item more-actions-item-danger"
-                  @click="handleDeleteFromMore(scope.row, scope.$index)"
-                >删除</div>
-              </div>
-              <el-button slot="reference" type="text" size="small" icon="el-icon-more" style="margin-left: 8px;"></el-button>
-            </el-popover>
-          </template>
-        </el-table-column>
-        <el-table-column prop="equityName" label="权益名称" width="140" show-overflow-tooltip></el-table-column>
-        <el-table-column label="商品状态" width="100">
-          <template slot-scope="scope">
-            <el-tag :type="getShelfTagType(scope.row.shelfStatus)" size="small">{{ scope.row.shelfStatus }}</el-tag>
-          </template>
-        </el-table-column>
-        <el-table-column label="审核状态" width="120">
-          <template slot-scope="scope">
-            <el-tag :type="getAuditTagType(scope.row.auditStatus)" size="small">{{ scope.row.auditStatus }}</el-tag>
-          </template>
-        </el-table-column>
-        <el-table-column prop="source" label="权益来源" width="100"></el-table-column>
-        <el-table-column prop="jumpLink" label="外部权益链接" width="120"></el-table-column>
-        <el-table-column prop="jumpMethod" label="是否有详情页" width="120"></el-table-column>
-        <el-table-column prop="productId" label="权益编号" width="100"></el-table-column>
-        <el-table-column label="显示页面" min-width="220">
-          <template slot-scope="scope">
-            <template v-if="visiblePages(scope.row).length">
-              <el-tag
-                v-for="page in visiblePages(scope.row)"
-                :key="page"
-                size="small"
-                style="margin-right: 4px;"
-              >{{ page }}</el-tag>
-            </template>
-            <span v-else class="empty-text">-</span>
-          </template>
-        </el-table-column>
-        <el-table-column prop="creator" label="创建人" width="100"></el-table-column>
-        <el-table-column prop="createTime" label="创建时间" width="170"></el-table-column>
-        <el-table-column prop="reviewer" label="审核人" width="100"></el-table-column>
-        <el-table-column prop="reviewTime" label="审核时间" width="170"></el-table-column>
-      </el-table>
-
-      <equity-link-dialog
-        :visible.sync="linkDialogVisible"
-        :id="linkTargetId"
-        path-name="equity/debit"
-        title="生成借记卡权益链接"
-      ></equity-link-dialog>
-
-      <!-- 分页 -->
-      <div class="pagination-section">
-        <span>Total {{ tableData.length }}</span>
-        <el-pagination
-          layout="prev, pager, next, sizes, jumper"
-          :page-size="10"
-          :page-sizes="[10, 20, 50]"
-          :total="tableData.length"
-        ></el-pagination>
-      </div>
-    </el-card>
-
-  </div>
-</template>
-
-<script>
-import request from '@/utils/request'
-import EquityLinkDialog from '@/components/EquityLinkDialog.vue'
-
-export default {
-  name: 'EquityProducts',
-  components: { EquityLinkDialog },
-  data() {
-    return {
-      searchForm: {
-        equityName: '',
-        productId: '',
-        shelfStatus: '',
-        auditStatus: ''
-      },
-      tableData: [],
-      adjustOrderPosition: 1,
-      linkDialogVisible: false,
-      linkTargetId: ''
-    }
-  },
-  created() {
-    this.loadData()
-  },
-  activated() {
-    this.loadData()
-  },
-  watch: {
-    $route(to) {
-      if (to.path === '/customization/hsbc/equity-products') {
-        this.loadData()
-      }
-    }
-  },
-  methods: {
-    loadData() {
-      const params = new URLSearchParams()
-      if (this.searchForm.equityName) params.append('equityName', this.searchForm.equityName)
-      if (this.searchForm.productId) params.append('productId', this.searchForm.productId)
-      if (this.searchForm.shelfStatus) params.append('shelfStatus', this.searchForm.shelfStatus)
-      if (this.searchForm.auditStatus) params.append('auditStatus', this.searchForm.auditStatus)
-      const qs = params.toString()
-      request.get('/api/equity/list' + (qs ? `?${qs}` : '')).then(res => {
-        this.tableData = res.data || []
-      }).catch(() => {})
-    },
-    handleReset() {
-      this.searchForm = { equityName: '', productId: '', shelfStatus: '', auditStatus: '' }
-      this.loadData()
-    },
-    // 按钮可见性
-    canEdit(row) {
-      return row.shelfStatus === '未上架' && row.auditStatus === '草稿'
-    },
-    canRequestOffshelf(row) {
-      return row.shelfStatus === '已上架' && row.auditStatus === '审核通过'
-    },
-    canDelete(row) {
-      return row.shelfStatus === '未上架' && (row.auditStatus === '草稿' || row.auditStatus === '审核通过')
-    },
-    // Tag 样式
-    getShelfTagType(s) {
-      if (s === '已上架') return 'success'
-      return 'info'
-    },
-    getAuditTagType(s) {
-      if (s === '审核通过') return 'success'
-      if (s === '草稿') return 'info'
-      if (s === '上架待审核') return ''
-      if (s === '下架待审核') return 'danger'
-      return ''
-    },
-    visiblePages(row) {
-      const pages = []
-      if (row.showOnHome === '显示') pages.push('权益首页/列表页')
-      if (row.showOnAssembly === '显示' && Array.isArray(row.assemblyPages)) {
-        pages.push(...row.assemblyPages)
-      }
-      return pages
-    },
-    // 路由跳转
-    createProduct() {
-      this.$router.push('/customization/hsbc/equity-products/add')
-    },
-    handleView(row) {
-      this.$router.push(`/customization/hsbc/equity-products/view/${row.productId}`)
-    },
-    handleEdit(row) {
-      this.$router.push(`/customization/hsbc/equity-products/edit/${row.productId}`)
-    },
-    // 下架(提交下架审核)
-    handleOffshelf(row) {
-      this.$confirm(`确定提交"${row.equityName}"的下架审核吗?审核通过后才会下架。`, '提示', {
-        confirmButtonText: '提交',
-        cancelButtonText: '取消',
-        type: 'warning'
-      }).then(() => {
-        request.post('/api/equity/request-offshelf', {
-          productId: row.productId
-        }).then(() => {
-          this.$message.success('已提交下架审核')
-          this.loadData()
-        }).catch(() => {})
-      }).catch(() => {})
-    },
-    // popover 工具方法
-    getPopoverInstance(name) {
-      const ref = this.$refs[name]
-      if (!ref) return null
-      return Array.isArray(ref) ? ref[0] : ref
-    },
-    onAdjustOrderShow(index) {
-      this.adjustOrderPosition = index + 1
-      const moreRef = this.getPopoverInstance(`morePopover-${index}`)
-      if (moreRef) moreRef.doClose()
-    },
-    closeAdjustOrder(index) {
-      const adjustRef = this.getPopoverInstance(`adjustPopover-${index}`)
-      if (adjustRef) adjustRef.doClose()
-      const moreRef = this.getPopoverInstance(`morePopover-${index}`)
-      if (moreRef) moreRef.doClose()
-    },
-    confirmAdjustOrder(index) {
-      const target = Number(this.adjustOrderPosition)
-      if (!target || target < 1 || target > this.tableData.length) {
-        this.$message.warning('请输入有效的顺序位置')
-        return
-      }
-      const targetIndex = target - 1
-      if (targetIndex !== index) {
-        const moved = this.tableData.splice(index, 1)[0]
-        this.tableData.splice(targetIndex, 0, moved)
-      }
-      this.$message.success('顺序已调整')
-      this.closeAdjustOrder(index)
-    },
-    showLinkDialog(row) {
-      this.linkTargetId = row.productId
-      this.linkDialogVisible = true
-    },
-    openLinkDialogFromMore(row, index) {
-      const moreRef = this.getPopoverInstance(`morePopover-${index}`)
-      if (moreRef) moreRef.doClose()
-      this.showLinkDialog(row)
-    },
-    handleCopyFromMore(row, index) {
-      const moreRef = this.getPopoverInstance(`morePopover-${index}`)
-      if (moreRef) moreRef.doClose()
-      this.$router.push({
-        path: '/customization/hsbc/equity-products/add',
-        query: { copyFrom: row.productId }
-      })
-    },
-    handleManageCouponsFromMore(row, index) {
-      const moreRef = this.getPopoverInstance(`morePopover-${index}`)
-      if (moreRef) moreRef.doClose()
-      window.open('https://uat.leagpoint.com/sipAdmin/coupon/inboundCouponList', '_blank')
-    },
-    handleImportCouponsFromMore(row, index) {
-      const moreRef = this.getPopoverInstance(`morePopover-${index}`)
-      if (moreRef) moreRef.doClose()
-      window.open('https://uat.leagpoint.com/sipAdmin/coupon/importCoupon', '_blank')
-    },
-    handleDeleteFromMore(row, index) {
-      const moreRef = this.getPopoverInstance(`morePopover-${index}`)
-      if (moreRef) moreRef.doClose()
-      this.$confirm(`确定删除权益"${row.equityName}"吗?`, '提示', {
-        confirmButtonText: '删除',
-        cancelButtonText: '取消',
-        type: 'warning'
-      }).then(() => {
-        request.post('/api/equity/delete', { productId: row.productId }).then(() => {
-          this.$message.success('删除成功')
-          this.loadData()
-        }).catch(() => {})
-      }).catch(() => {})
-    }
-  }
-}
-</script>
-
-<style scoped>
-.equity-products {
-  padding: 0;
-}
-
-.search-section {
-  display: flex;
-  flex-direction: column;
-}
-
-.search-row {
-  margin-bottom: 15px;
-}
-
-.search-row .el-form {
-  display: flex;
-  flex-wrap: wrap;
-  gap: 0;
-}
-
-.search-row .el-form-item {
-  margin-right: 0;
-  margin-bottom: 0;
-}
-
-.button-group {
-  display: flex;
-  gap: 5px;
-}
-
-.pagination-section {
-  display: flex;
-  justify-content: flex-end;
-  align-items: center;
-  gap: 20px;
-  margin-top: 20px;
-}
-
-.empty-text {
-  color: #c0c4cc;
-}
-</style>
-
-<style>
-.more-actions-menu {
-  display: flex;
-  flex-direction: column;
-  margin: -12px;
-}
-
-.more-actions-item {
-  padding: 8px 16px;
-  cursor: pointer;
-  font-size: 14px;
-  color: #606266;
-}
-
-.more-actions-item:hover {
-  background: #f5f7fa;
-  color: #409eff;
-}
-
-.more-actions-item-danger:hover {
-  color: #f56c6c;
-}
-
-.adjust-order-form {
-  display: flex;
-  flex-direction: column;
-  gap: 12px;
-}
-
-.adjust-order-title {
-  font-size: 14px;
-  font-weight: 500;
-  color: #303133;
-}
-
-.adjust-order-tip {
-  font-size: 12px;
-  color: #909399;
-  line-height: 1.5;
-  padding: 8px 10px;
-  background: #f5f7fa;
-  border-radius: 4px;
-}
-
-.adjust-order-input {
-  display: flex;
-  align-items: center;
-  font-size: 14px;
-  color: #606266;
-}
-
-.adjust-order-actions {
-  display: flex;
-  justify-content: flex-end;
-  gap: 8px;
-}
-</style>

+ 0 - 132
pc/src/views/Login.vue

@@ -1,132 +0,0 @@
-<template>
-  <div class="login-page">
-    <div class="login-box">
-      <div class="login-header">
-        <i class="el-icon-monitor"></i>
-        <span>管理后台</span>
-      </div>
-      <p class="login-tip">请使用账号密码登录</p>
-      <el-form
-        ref="form"
-        :model="form"
-        :rules="rules"
-        size="medium"
-        @submit.native.prevent="handleSubmit"
-      >
-        <el-form-item prop="username">
-          <el-input
-            v-model="form.username"
-            placeholder="请输入账号"
-            prefix-icon="el-icon-user"
-            autocomplete="username"
-          />
-        </el-form-item>
-        <el-form-item prop="password">
-          <el-input
-            v-model="form.password"
-            type="password"
-            placeholder="请输入密码"
-            prefix-icon="el-icon-lock"
-            autocomplete="current-password"
-            show-password
-            @keyup.enter.native="handleSubmit"
-          />
-        </el-form-item>
-        <el-form-item>
-          <el-button
-            type="primary"
-            :loading="loading"
-            class="login-btn"
-            native-type="submit"
-          >
-            登录
-          </el-button>
-        </el-form-item>
-      </el-form>
-      <p class="login-hint">演示账号:admin / 123456</p>
-    </div>
-  </div>
-</template>
-
-<script>
-import request from '@/utils/request'
-
-export default {
-  name: 'Login',
-  data() {
-    return {
-      loading: false,
-      form: { username: '', password: '' },
-      rules: {
-        username: [{ required: true, message: '请输入账号', trigger: 'blur' }],
-        password: [{ required: true, message: '请输入密码', trigger: 'blur' }]
-      }
-    }
-  },
-  methods: {
-    handleSubmit() {
-      this.$refs.form.validate(async (valid) => {
-        if (!valid) return
-        this.loading = true
-        try {
-          const res = await request.post('/api/login', this.form)
-          localStorage.setItem('token', res.data.token)
-          localStorage.setItem('username', res.data.username)
-          this.$message.success('登录成功')
-          const redirect = this.$route.query.redirect || '/'
-          this.$router.replace(redirect)
-        } finally {
-          this.loading = false
-        }
-      })
-    }
-  }
-}
-</script>
-
-<style scoped>
-.login-page {
-  height: 100vh;
-  display: flex;
-  align-items: center;
-  justify-content: center;
-  background: linear-gradient(135deg, #304156 0%, #1f2d3d 100%);
-}
-
-.login-box {
-  width: 380px;
-  padding: 36px 32px 24px;
-  background: #fff;
-  border-radius: 8px;
-  box-shadow: 0 6px 24px rgba(0, 0, 0, 0.2);
-}
-
-.login-header {
-  display: flex;
-  align-items: center;
-  justify-content: center;
-  gap: 10px;
-  color: #304156;
-  font-size: 22px;
-  font-weight: bold;
-}
-.login-header i { font-size: 26px; }
-
-.login-tip {
-  margin: 10px 0 24px;
-  color: #909399;
-  text-align: center;
-  font-size: 13px;
-}
-
-.login-btn {
-  width: 100%;
-}
-
-.login-hint {
-  margin: 8px 0 0;
-  color: #c0c4cc;
-  text-align: center;
-  font-size: 12px;
-}
-</style>

+ 0 - 726
pc/src/views/PageAssembly.vue

@@ -1,726 +0,0 @@
-<template>
-  <div class="page-assembly">
-    <!-- ==================== 列表视图 ==================== -->
-    <template v-if="viewState === 'list'">
-      <el-card>
-        <div slot="header" class="search-section">
-          <div class="search-row">
-            <el-form :model="searchForm" label-width="80px" inline>
-              <el-form-item label="页面ID:">
-                <el-input v-model="searchForm.id" placeholder="请输入" clearable style="width:160px" />
-              </el-form-item>
-              <el-form-item label="页面名称:">
-                <el-input v-model="searchForm.name" placeholder="请输入" clearable style="width:160px" />
-              </el-form-item>
-              <div class="button-group">
-                <el-button type="primary" @click="handleSearch">查询</el-button>
-                <el-button @click="handleReset">重置</el-button>
-              </div>
-            </el-form>
-          </div>
-          <div class="action-row">
-            <el-button type="primary" icon="el-icon-plus" @click="handleCreate">新建组装页</el-button>
-          </div>
-        </div>
-
-        <el-table :data="tableData" stripe border style="width:100%">
-          <el-table-column label="操作" width="200" fixed="left">
-            <template slot-scope="scope">
-              <el-button type="text" size="small" @click="handleEdit(scope.row)">编辑</el-button>
-              <el-button type="text" size="small" @click="showLinkDialog(scope.row.id)">链接</el-button>
-              <el-button type="text" size="small" style="color:#F56C6C" @click="handleDelete(scope.row)">删除</el-button>
-            </template>
-          </el-table-column>
-          <el-table-column prop="id" label="页面ID" width="120" align="center" />
-          <el-table-column prop="name" label="组装页名称" min-width="200" show-overflow-tooltip />
-          <el-table-column label="发布状态" width="100" align="center">
-            <template slot-scope="scope">
-              <el-tag :type="scope.row.status === 'published' ? 'success' : 'info'" size="small">
-                {{ scope.row.status === 'published' ? '已上架' : '草稿' }}
-              </el-tag>
-            </template>
-          </el-table-column>
-          <el-table-column prop="createTime" label="创建时间" width="180" align="center" />
-          <el-table-column prop="updateTime" label="更新时间" width="180" align="center" />
-        </el-table>
-      </el-card>
-    </template>
-
-    <!-- ==================== 编辑视图 ==================== -->
-    <template v-else>
-      <!-- 操作栏 -->
-      <div class="toolbar">
-        <div class="toolbar-left">
-          <el-button size="small" icon="el-icon-arrow-left" @click="backToList">返回</el-button>
-          <h2 class="toolbar-title">{{ isNew ? '新建组装页' : '编辑组装页' }}</h2>
-          <el-tag v-if="editData.status === 'published' && !isModified" type="success" size="small" effect="plain">正式线上环境</el-tag>
-          <el-tag v-else-if="isModified" type="warning" size="small" effect="plain">有未发布的修改</el-tag>
-          <el-tag v-else size="info" effect="plain">草稿 (未发布)</el-tag>
-        </div>
-        <div class="toolbar-right">
-          <el-button size="small" icon="el-icon-link" @click="showLinkDialog(editData.id)">获取链接</el-button>
-          <el-button size="small" type="primary" plain @click="handleSaveAssembly('draft')">保存草稿</el-button>
-          <el-button size="small" type="primary" @click="handleSaveAssembly('publish')">发布上线</el-button>
-        </div>
-      </div>
-
-      <!-- 基本信息 -->
-      <el-card class="section-card" shadow="hover">
-        <div class="basic-info">
-          <div class="info-item">
-            <span class="info-label">页面 ID:</span>
-            <el-tag size="small" effect="plain">{{ editData.id || '保存后生成' }}</el-tag>
-          </div>
-          <div class="info-item" style="flex:1">
-            <span class="info-label">组装页名称 <span style="color:#F56C6C">*</span></span>
-            <el-input v-model="editData.name" placeholder="请为该页面设置一个名称 (必填)" size="small" @input="isModified = true" />
-          </div>
-        </div>
-      </el-card>
-
-      <!-- 模块列表 -->
-      <div v-for="(block, bIdx) in editData.blocks" :key="block.id" class="block-card">
-        <!-- 模块操作栏 -->
-        <div class="block-actions">
-          <el-popover placement="bottom" trigger="click" width="180">
-            <div>
-              <div style="margin-bottom:8px;font-size:13px;color:#666">移动到序号:</div>
-              <div style="display:flex;gap:8px">
-                <el-input-number v-model="sortTarget" :min="1" :max="editData.blocks.length" size="mini" controls-position="right" style="width:100px" />
-                <el-button size="mini" type="primary" @click="moveBlock(bIdx, sortTarget - 1)">确定</el-button>
-              </div>
-            </div>
-            <el-button slot="reference" size="mini" icon="el-icon-sort" @click="sortTarget = bIdx + 1">排序</el-button>
-          </el-popover>
-          <el-button v-if="block.type !== 'privilege'" size="mini" icon="el-icon-copy-document" @click="duplicateBlock(bIdx)">复制</el-button>
-          <el-button size="mini" type="danger" icon="el-icon-delete" @click="removeBlock(bIdx)">删除</el-button>
-        </div>
-
-        <!-- 图片模块 -->
-        <template v-if="block.type === 'image'">
-          <div class="block-header"><i class="el-icon-picture-outline"></i> 图片模块</div>
-          <div class="block-body">
-            <div class="image-upload-area">
-              <div class="upload-box upload-box-wide" @click="triggerBlockUpload(bIdx)">
-                <img v-if="block.data.imageUrl" :src="block.data.imageUrl" class="preview-img" />
-                <div v-else class="upload-placeholder">
-                  <i class="el-icon-picture"></i><span>未上传图片</span>
-                </div>
-                <div class="upload-overlay"><i class="el-icon-upload2"></i><span>点击上传图片</span></div>
-              </div>
-              <div class="upload-tip">建议宽度:<b>750px</b><br/>高度不限,系统将按比例自适应展示</div>
-              <input :ref="'blockUpload' + bIdx" type="file" accept="image/*" style="display:none" @change="onBlockImageUpload($event, bIdx)" />
-            </div>
-          </div>
-        </template>
-
-        <!-- 权益商品模块 -->
-        <template v-else-if="block.type === 'privilege'">
-          <div class="block-header"><i class="el-icon-s-grid"></i> 权益商品模块 <span class="block-hint">(当前页面仅限添加一个)</span></div>
-          <div class="block-body">
-            <el-form label-width="100px" style="margin-bottom:16px;border-bottom:1px solid #ebeef5;padding-bottom:16px">
-              <el-form-item label="可配置大标题">
-                <el-input v-model="block.data.title" style="width:50%" @input="isModified = true" />
-              </el-form-item>
-            </el-form>
-            <div v-for="(pItem, pIdx) in block.data.items" :key="pIdx" class="privilege-row">
-              <el-tag size="mini" effect="plain" class="privilege-no">NO. {{ pIdx + 1 }}</el-tag>
-              <div class="privilege-icon"><i class="el-icon-document"></i></div>
-              <div class="privilege-info">
-                <div class="privilege-title">{{ pItem.title }}</div>
-                <div class="privilege-desc">{{ pItem.desc }}</div>
-              </div>
-              <el-popover placement="bottom" trigger="click" width="180">
-                <div>
-                  <div style="margin-bottom:8px;font-size:13px;color:#666">移动到序号:</div>
-                  <div style="display:flex;gap:8px">
-                    <el-input-number v-model="sortTarget" :min="1" :max="block.data.items.length" size="mini" controls-position="right" style="width:100px" />
-                    <el-button size="mini" type="primary" @click="movePrivilegeItem(bIdx, pIdx, sortTarget - 1)">确定</el-button>
-                  </div>
-                </div>
-                <el-button slot="reference" size="mini" icon="el-icon-sort" circle @click="sortTarget = pIdx + 1" />
-              </el-popover>
-            </div>
-          </div>
-        </template>
-
-        <!-- 图文模块 (富文本) -->
-        <template v-else-if="block.type === 'richtext'">
-          <div class="block-header"><i class="el-icon-notebook-2"></i> 图文模块</div>
-          <div class="block-body">
-            <quill-editor
-              v-model="block.data.content"
-              :options="quillOptions"
-              @change="onQuillChange"
-            />
-          </div>
-        </template>
-
-        <!-- 按钮模块 -->
-        <template v-else-if="block.type === 'buttonGroup'">
-          <div class="block-header button-module-header">
-            <span class="button-module-title"><i class="el-icon-thumb"></i> 按钮模块</span>
-            <el-button class="add-button-btn" type="primary" plain icon="el-icon-circle-plus-outline" size="small" @click="addButton(bIdx)">添加按钮</el-button>
-          </div>
-          <div class="block-body">
-            <div v-if="!block.data.buttons || block.data.buttons.length === 0" class="empty-tip">暂无按钮</div>
-            <div v-for="(btn, btnIdx) in (block.data.buttons || [])" :key="btnIdx" class="button-config-item">
-              <div class="btn-index">BTN {{ btnIdx + 1 }}</div>
-              <div class="btn-config-body">
-                <el-form class="button-form" label-position="top">
-                  <el-row :gutter="16">
-                    <el-col :span="8">
-                      <el-form-item label="按钮文案">
-                        <el-input v-model="btn.text" size="small" @input="isModified = true" />
-                      </el-form-item>
-                    </el-col>
-                    <el-col :span="8">
-                      <el-form-item label="点击动作">
-                        <el-select v-model="btn.actionType" size="small" @change="handleButtonActionChange(btn)">
-                          <el-option label="跳转链接" value="link" />
-                          <el-option label="弹窗提示" value="dialog" />
-                        </el-select>
-                      </el-form-item>
-                    </el-col>
-                    <el-col :span="8">
-                      <el-form-item label="按钮样式">
-                        <el-select v-model="btn.style" size="small" @change="isModified = true">
-                          <el-option label="突出显示(红色)" value="primary" />
-                          <el-option label="次要操作(白色)" value="secondary" />
-                        </el-select>
-                      </el-form-item>
-                    </el-col>
-                  </el-row>
-                  <!-- 跳转链接 -->
-                  <el-form-item v-if="btn.actionType === 'link'" label="跳转链接 *">
-                    <el-input v-model="btn.link" size="small" placeholder="必填,输入跳转URL" @input="isModified = true">
-                    </el-input>
-                  </el-form-item>
-                  <!-- 弹窗配置 -->
-                  <div v-if="btn.actionType === 'dialog' && btn.dialog" class="dialog-config">
-                    <div class="dialog-config-title"><i class="el-icon-document"></i> 弹窗内容配置(包含取消、确认按钮)</div>
-                    <el-form-item label="弹窗标题">
-                      <el-input v-model="btn.dialog.title" size="small" placeholder="弹窗标题" @input="isModified = true" />
-                    </el-form-item>
-                    <el-form-item label="弹窗描述">
-                      <el-input v-model="btn.dialog.desc" type="textarea" :rows="2" size="small" placeholder="弹窗正文提示..." @input="isModified = true" />
-                    </el-form-item>
-                    <el-row :gutter="16">
-                      <el-col :span="12">
-                        <el-form-item label="右侧确认按钮文案">
-                          <el-input v-model="btn.dialog.confirmText" size="small" placeholder="确认" @input="isModified = true" />
-                        </el-form-item>
-                      </el-col>
-                      <el-col :span="12">
-                        <el-form-item label="确认按钮跳转链接">
-                          <el-input v-model="btn.dialog.confirmLink" size="small" placeholder="选填,无链接则仅关闭弹窗" @input="isModified = true" />
-                        </el-form-item>
-                      </el-col>
-                    </el-row>
-                  </div>
-                </el-form>
-              </div>
-              <div class="btn-config-actions">
-                <el-popover placement="bottom" trigger="click" width="180">
-                  <div>
-                    <div style="margin-bottom:8px;font-size:13px;color:#666">移动到序号:</div>
-                    <div style="display:flex;gap:8px">
-                      <el-input-number v-model="sortTarget" :min="1" :max="block.data.buttons.length" size="mini" controls-position="right" style="width:100px" />
-                      <el-button size="mini" type="primary" @click="moveButton(bIdx, btnIdx, sortTarget - 1)">确定</el-button>
-                    </div>
-                  </div>
-                  <el-button slot="reference" size="mini" icon="el-icon-sort" circle @click="sortTarget = btnIdx + 1" />
-                </el-popover>
-                <el-button size="mini" type="danger" icon="el-icon-delete" circle @click="removeButton(bIdx, btnIdx)" />
-              </div>
-            </div>
-          </div>
-        </template>
-      </div>
-
-      <!-- 添加模块按钮 -->
-      <div class="add-block-bar">
-        <el-button type="text" icon="el-icon-picture-outline" @click="addBlock('image')">图片模块</el-button>
-        <el-button v-if="!hasPrivilegeBlock" type="text" icon="el-icon-s-grid" @click="addBlock('privilege')">权益商品模块</el-button>
-        <el-button type="text" icon="el-icon-notebook-2" @click="addBlock('richtext')">图文模块</el-button>
-        <el-button type="text" icon="el-icon-thumb" @click="addBlock('buttonGroup')">按钮模块</el-button>
-      </div>
-    </template>
-
-    <!-- 获取链接弹窗 -->
-    <el-dialog title="获取页面链接" :visible.sync="linkDialogVisible" width="680px">
-      <el-form label-width="100px" inline>
-        <el-form-item label="Campaign ID">
-          <el-select v-model="linkForm.campaignId" clearable filterable placeholder="请选择 (无)" style="width:240px">
-            <el-option v-for="opt in campaignOptions" :key="opt.value" :label="opt.label" :value="opt.value" />
-          </el-select>
-        </el-form-item>
-        <el-form-item label="Channel ID">
-          <el-select v-model="linkForm.channelId" clearable filterable placeholder="请选择 (无)" style="width:240px">
-            <el-option v-for="opt in channelOptions" :key="opt.value" :label="opt.label" :value="opt.value" />
-          </el-select>
-        </el-form-item>
-      </el-form>
-      <div v-if="linkTargetPublished" class="link-section">
-        <div class="link-label"><span class="dot dot-green"></span> 正式环境链接</div>
-        <div class="link-row">
-          <span class="link-type">H5链接</span>
-          <el-input :value="assemblyProdH5" readonly size="small" />
-          <el-button size="small" type="primary" plain @click="copyLink(assemblyProdH5)">复制</el-button>
-        </div>
-        <div class="link-row">
-          <span class="link-type">小程序链接</span>
-          <el-input :value="assemblyProdApp" readonly size="small" />
-          <el-button size="small" type="primary" plain @click="copyLink(assemblyProdApp)">复制</el-button>
-        </div>
-      </div>
-      <div class="link-section">
-        <div class="link-label"><span class="dot dot-blue"></span> 草稿预览链接 (测试)</div>
-        <div class="link-row">
-          <el-input :value="assemblyTestUrl" readonly size="small" />
-          <el-button size="small" type="primary" plain @click="copyLink(assemblyTestUrl)">复制</el-button>
-        </div>
-      </div>
-    </el-dialog>
-  </div>
-</template>
-
-<script>
-import request from '@/utils/request'
-import { fetchLinkOptions } from '@/utils/linkOptions'
-
-let blockIdCounter = 0
-function genBlockId() {
-  return 'b_' + (++blockIdCounter) + '_' + Math.random().toString(36).substr(2, 6)
-}
-
-export default {
-  name: 'PageAssembly',
-  data() {
-    return {
-      viewState: 'list',
-      tableData: [],
-      searchForm: { id: '', name: '' },
-      editData: this.createEmptyAssembly(),
-      isNew: true,
-      isModified: false,
-      quillReady: false,
-      sortTarget: 1,
-      linkDialogVisible: false,
-      linkForm: { campaignId: '', channelId: '' },
-      campaignOptions: [],
-      channelOptions: [],
-      linkTargetId: '',
-      linkTargetPublished: false,
-      quillOptions: {
-        placeholder: '请输入图文内容...',
-        modules: {
-          toolbar: [
-            ['bold', 'italic', 'underline'],
-            [{ list: 'ordered' }, { list: 'bullet' }],
-            [{ header: [1, 2, 3, false] }],
-            ['link', 'image'],
-            ['clean']
-          ]
-        }
-      }
-    }
-  },
-  computed: {
-    hasPrivilegeBlock() {
-      return this.editData.blocks.some(b => b.type === 'privilege')
-    },
-    linkQueryStr() {
-      const params = []
-      if (this.linkForm.campaignId) params.push(`CampaignId=${this.linkForm.campaignId}`)
-      if (this.linkForm.channelId) params.push(`Channelid=${this.linkForm.channelId}`)
-      return params.length ? '&' + params.join('&') : ''
-    },
-    assemblyProdH5() {
-      return `https://hsbc.com/assembly?id=${this.linkTargetId}${this.linkQueryStr}`
-    },
-    assemblyProdApp() {
-      return `hsbc-app://assembly?id=${this.linkTargetId}${this.linkQueryStr}`
-    },
-    assemblyTestUrl() {
-      return `https://test.hsbc.com/assembly?id=${this.linkTargetId}&draft=1${this.linkQueryStr}`
-    }
-  },
-  created() {
-    this.fetchList()
-  },
-  methods: {
-    createDefaultButtonDialog() {
-      return { title: '', desc: '', confirmText: '确认', confirmLink: '' }
-    },
-    createDefaultButton(overrides = {}) {
-      return {
-        text: '新按钮',
-        link: '',
-        style: 'secondary',
-        actionType: 'link',
-        ...overrides,
-        dialog: {
-          ...this.createDefaultButtonDialog(),
-          ...(overrides.dialog || {})
-        }
-      }
-    },
-    ensureReactiveField(target, key, value) {
-      if (Object.prototype.hasOwnProperty.call(target, key)) {
-        target[key] = value
-      } else {
-        this.$set(target, key, value)
-      }
-    },
-    normalizeButton(btn) {
-      if (!btn) return
-      if (!btn.text) this.ensureReactiveField(btn, 'text', '')
-      if (!btn.style) this.ensureReactiveField(btn, 'style', 'secondary')
-      if (!btn.actionType) this.ensureReactiveField(btn, 'actionType', 'link')
-      if (!Object.prototype.hasOwnProperty.call(btn, 'link')) this.ensureReactiveField(btn, 'link', '')
-      if (!btn.dialog) {
-        this.ensureReactiveField(btn, 'dialog', this.createDefaultButtonDialog())
-      } else {
-        const defaults = this.createDefaultButtonDialog()
-        Object.keys(defaults).forEach(key => {
-          if (!Object.prototype.hasOwnProperty.call(btn.dialog, key)) {
-            this.$set(btn.dialog, key, defaults[key])
-          }
-        })
-      }
-    },
-    normalizeAssemblyData(data) {
-      const blocks = data.blocks || []
-      blocks.forEach(block => {
-        if (block.type !== 'buttonGroup') return
-        if (!block.data) this.$set(block, 'data', { buttons: [] })
-        if (!block.data.buttons) this.$set(block.data, 'buttons', [])
-        block.data.buttons.forEach(btn => this.normalizeButton(btn))
-      })
-      return data
-    },
-    createEmptyAssembly() {
-      return {
-        id: '', name: '', status: 'draft',
-        blocks: [
-          { type: 'image', id: genBlockId(), data: { imageUrl: '' } },
-          { type: 'privilege', id: genBlockId(), data: { title: '权益标题', items: [{ id: '1', title: '权益名称', desc: '权益简介' }] } },
-          { type: 'richtext', id: genBlockId(), data: { content: '<p>组装页富文本区块内容...</p>' } },
-          { type: 'buttonGroup', id: genBlockId(), data: { buttons: [this.createDefaultButton({ text: '点击配置', style: 'primary' })] } }
-        ]
-      }
-    },
-    async fetchList() {
-      const res = await request.get('/api/assembly/list', { params: this.searchForm })
-      this.tableData = res.data
-    },
-    async loadLinkOptions() {
-      const { campaignOptions, channelOptions } = await fetchLinkOptions()
-      this.campaignOptions = campaignOptions
-      this.channelOptions = channelOptions
-    },
-    handleSearch() {
-      this.fetchList()
-    },
-    handleReset() {
-      this.searchForm = { id: '', name: '' }
-      this.fetchList()
-    },
-    handleCreate() {
-      this.editData = this.createEmptyAssembly()
-      this.isNew = true
-      this.isModified = true
-      this.quillReady = false
-      this.viewState = 'edit'
-      this.$nextTick(() => { this.quillReady = true })
-    },
-    handleEdit(row) {
-      this.editData = this.normalizeAssemblyData(JSON.parse(JSON.stringify(row)))
-      this.isNew = false
-      this.isModified = false
-      this.quillReady = false
-      this.viewState = 'edit'
-      this.$nextTick(() => { this.quillReady = true })
-    },
-    async handleDelete(row) {
-      await this.$confirm('确认要删除该组装页吗?此操作不可恢复。', '提示', { type: 'warning' })
-      await request.post('/api/assembly/delete', { id: row.id })
-      this.$message.success('删除成功')
-      this.fetchList()
-    },
-    backToList() {
-      this.viewState = 'list'
-      this.fetchList()
-    },
-    async handleSaveAssembly(type) {
-      const data = this.editData
-      this.normalizeAssemblyData(data)
-      let hasError = false
-      data.blocks.forEach(block => {
-        if (block.type === 'buttonGroup') {
-          block.data.buttons.forEach(btn => {
-            if (btn.actionType === 'link' && !(btn.link || '').trim()) hasError = true
-          })
-        }
-      })
-      if (hasError) {
-        this.$message.error('保存失败:按钮模块中存在未填写的跳转链接!')
-        return
-      }
-      if (!data.name.trim()) data.name = '未命名组装页'
-      data.status = type === 'publish' ? 'published' : 'draft'
-
-      const res = await request.post('/api/assembly/save', data)
-      if (!data.id) data.id = res.data.id
-      this.isModified = false
-      this.$message.success(type === 'publish' ? '已发布组装页配置!' : '已保存组装页配置草稿!')
-      if (type === 'publish') this.backToList()
-    },
-
-    onQuillChange() {
-      if (this.quillReady) this.isModified = true
-    },
-
-    // 模块操作
-    addBlock(type) {
-      const block = { type, id: genBlockId(), data: {} }
-      if (type === 'image') block.data = { imageUrl: '' }
-      if (type === 'privilege') block.data = { title: '新权益标题', items: [{ id: '1', title: '模拟权益', desc: '描述' }] }
-      if (type === 'richtext') block.data = { content: '<p>输入富文本内容...</p>' }
-      if (type === 'buttonGroup') block.data = { buttons: [this.createDefaultButton({ text: '点击跳转', style: 'primary' })] }
-      this.editData.blocks.push(block)
-      this.isModified = true
-    },
-    duplicateBlock(index) {
-      const block = this.editData.blocks[index]
-      if (block.type === 'privilege') {
-        this.$message.warning('权益商品模块每页仅限配置一个,无法复制!')
-        return
-      }
-      const copy = JSON.parse(JSON.stringify(block))
-      copy.id = genBlockId()
-      this.editData.blocks.splice(index + 1, 0, copy)
-      this.isModified = true
-    },
-    removeBlock(index) {
-      this.$confirm('确定删除该模块吗?', '提示', { type: 'warning' }).then(() => {
-        this.editData.blocks.splice(index, 1)
-        this.isModified = true
-      }).catch(() => {})
-    },
-    moveBlock(from, to) {
-      if (to < 0 || to >= this.editData.blocks.length || from === to) return
-      const item = this.editData.blocks.splice(from, 1)[0]
-      this.editData.blocks.splice(to, 0, item)
-      this.isModified = true
-    },
-    movePrivilegeItem(bIdx, from, to) {
-      const items = this.editData.blocks[bIdx].data.items
-      if (to < 0 || to >= items.length || from === to) return
-      const item = items.splice(from, 1)[0]
-      items.splice(to, 0, item)
-      this.isModified = true
-    },
-
-    // 按钮操作
-    addButton(bIdx) {
-      const block = this.editData.blocks[bIdx]
-      if (!block.data.buttons) this.$set(block.data, 'buttons', [])
-      block.data.buttons.push(this.createDefaultButton())
-      this.isModified = true
-    },
-    handleButtonActionChange(btn) {
-      this.normalizeButton(btn)
-      this.isModified = true
-    },
-    removeButton(bIdx, btnIdx) {
-      this.editData.blocks[bIdx].data.buttons.splice(btnIdx, 1)
-      this.isModified = true
-    },
-    moveButton(bIdx, from, to) {
-      const buttons = this.editData.blocks[bIdx].data.buttons
-      if (to < 0 || to >= buttons.length || from === to) return
-      const item = buttons.splice(from, 1)[0]
-      buttons.splice(to, 0, item)
-      this.isModified = true
-    },
-
-    // 图片上传
-    triggerBlockUpload(bIdx) {
-      const ref = this.$refs['blockUpload' + bIdx]
-      if (ref) (Array.isArray(ref) ? ref[0] : ref).click()
-    },
-    onBlockImageUpload(event, bIdx) {
-      const file = event.target.files[0]
-      if (!file) return
-      const reader = new FileReader()
-      reader.onload = (e) => {
-        this.$set(this.editData.blocks[bIdx].data, 'imageUrl', e.target.result)
-        this.isModified = true
-      }
-      reader.readAsDataURL(file)
-      event.target.value = ''
-    },
-
-    // 链接弹窗
-    async showLinkDialog(id) {
-      if (!id) {
-        this.$message.warning('请先保存组装页再获取链接!')
-        return
-      }
-      await this.loadLinkOptions()
-      this.linkTargetId = id
-      const item = this.tableData.find(a => a.id === id) || this.editData
-      this.linkTargetPublished = item && item.status === 'published'
-      this.linkForm = { campaignId: '', channelId: '' }
-      this.linkDialogVisible = true
-    },
-    copyLink(text) {
-      navigator.clipboard.writeText(text).then(() => {
-        this.$message.success('链接已复制到剪贴板')
-      })
-    }
-  }
-}
-</script>
-
-<style scoped>
-.search-section .search-row { margin-bottom: 0; }
-.search-section .action-row { margin-top: 15px; }
-.button-group { display: inline-block; margin-left: 20px; }
-
-.toolbar {
-  display: flex; justify-content: space-between; align-items: center;
-  margin-bottom: 16px; padding: 12px 16px;
-  background: #fff; border-radius: 4px; box-shadow: 0 1px 4px rgba(0,0,0,.06);
-}
-.toolbar-left { display: flex; align-items: center; gap: 12px; }
-.toolbar-title { margin: 0; font-size: 18px; font-weight: 600; }
-.toolbar-right { display: flex; gap: 8px; }
-
-.section-card { margin-bottom: 16px; }
-.basic-info { display: flex; align-items: center; gap: 24px; }
-.info-item { display: flex; align-items: center; gap: 8px; }
-.info-label { font-size: 13px; color: #909399; white-space: nowrap; }
-
-.block-card {
-  background: #fff; border-radius: 8px; border: 1px solid #ebeef5;
-  margin-bottom: 16px; overflow: hidden; position: relative;
-  box-shadow: 0 1px 4px rgba(0,0,0,.04);
-}
-.block-header {
-  background: #f5f7fa; padding: 10px 160px 10px 16px; min-height: 44px;
-  border-bottom: 1px solid #ebeef5; font-weight: 600; font-size: 14px;
-  display: flex; align-items: center;
-}
-.block-header i { margin-right: 6px; color: #909399; }
-.button-module-header { gap: 14px; }
-.button-module-title { display: inline-flex; align-items: center; }
-.add-button-btn {
-  padding: 7px 10px;
-  background: #ecf5ff;
-  border-color: #d9ecff;
-}
-.block-hint { font-size: 12px; color: #c0c4cc; font-weight: normal; margin-left: 8px; }
-.block-body { padding: 16px; }
-.block-actions {
-  position: absolute; right: 0; top: 0; z-index: 10;
-  display: flex; gap: 4px; background: #f5f7fa;
-  border-bottom: 1px solid #ebeef5; border-left: 1px solid #ebeef5;
-  padding: 6px 8px; border-radius: 0 0 0 8px;
-}
-
-.image-upload-area { display: flex; align-items: center; gap: 16px; }
-.upload-box {
-  width: 256px; height: 144px;
-  background: #eef6fc; border: 1px solid #dcdfe6; border-radius: 4px;
-  position: relative; overflow: hidden; cursor: pointer;
-  display: flex; align-items: center; justify-content: center; flex-shrink: 0;
-}
-.upload-box-wide { width: 360px; height: 160px; }
-.upload-box:hover .upload-overlay { display: flex; }
-.preview-img { width: 100%; height: 100%; object-fit: cover; }
-.upload-placeholder {
-  display: flex; flex-direction: column; align-items: center;
-  color: #c0c4cc; font-size: 12px;
-}
-.upload-placeholder i { font-size: 28px; margin-bottom: 4px; }
-.upload-overlay {
-  display: none; position: absolute; inset: 0;
-  background: rgba(0,0,0,.5); color: #fff;
-  flex-direction: column; align-items: center; justify-content: center; font-size: 14px;
-}
-.upload-overlay i { font-size: 24px; margin-bottom: 4px; }
-.upload-tip {
-  font-size: 12px; color: #909399; line-height: 1.6;
-  background: #f5f7fa; padding: 8px 12px; border-radius: 4px; border: 1px solid #ebeef5;
-}
-
-.privilege-row {
-  display: flex; align-items: center; gap: 12px;
-  padding: 10px 12px; border: 1px solid #ebeef5; border-radius: 6px;
-  background: #fafafa; margin-bottom: 8px;
-}
-.privilege-no { flex-shrink: 0; }
-.privilege-icon {
-  width: 40px; height: 40px; border-radius: 50%;
-  border: 1px solid #dcdfe6; background: #fff;
-  display: flex; align-items: center; justify-content: center;
-  color: #909399; font-size: 18px; flex-shrink: 0;
-}
-.privilege-info { flex: 1; opacity: .8; }
-.privilege-title { font-size: 13px; font-weight: 600; }
-.privilege-desc { font-size: 12px; color: #909399; margin-top: 2px; }
-
-.button-config-item {
-  position: relative; border: 1px solid #ebeef5; border-radius: 6px;
-  padding: 24px 122px 20px 20px; margin-bottom: 12px; background: #fafafa;
-}
-.btn-index {
-  position: absolute; left: 0; top: 0;
-  background: #606266; color: #fff; font-size: 10px; font-weight: 700;
-  padding: 2px 8px; border-radius: 6px 0 6px 0;
-}
-.btn-config-body { flex: 1; }
-.button-form .el-form-item { margin-bottom: 16px; }
-.button-form .el-form-item__label {
-  line-height: 20px;
-  padding: 0 0 6px;
-  color: #303133;
-  font-weight: 400;
-}
-.button-form .el-select { width: 100%; }
-.btn-config-actions {
-  position: absolute; right: 20px; top: 32px;
-  display: flex; gap: 6px;
-}
-.dialog-config {
-  background: #fff; padding: 18px 20px 4px; border: 1px solid #ebeef5; border-radius: 4px;
-  margin-top: 4px;
-}
-.dialog-config-title {
-  font-size: 14px; font-weight: 500; color: #303133;
-  border-bottom: 1px solid #ebeef5; padding-bottom: 12px; margin-bottom: 14px;
-}
-
-.add-block-bar {
-  display: flex; gap: 16px; justify-content: center;
-  padding: 20px; border: 2px dashed #dcdfe6; border-radius: 8px;
-  background: #fff; margin-bottom: 16px;
-}
-
-.empty-tip {
-  text-align: center; color: #c0c4cc; padding: 16px;
-  border: 1px dashed #dcdfe6; border-radius: 4px;
-}
-
-.link-section { margin-top: 16px; padding: 12px; background: #fafafa; border-radius: 6px; border: 1px solid #ebeef5; }
-.link-section + .link-section { margin-top: 12px; }
-.link-label { font-weight: 600; margin-bottom: 8px; display: flex; align-items: center; gap: 6px; }
-.link-row { display: flex; align-items: center; gap: 8px; margin-bottom: 6px; }
-.link-type { font-size: 12px; color: #909399; width: 70px; flex-shrink: 0; }
-.dot { width: 8px; height: 8px; border-radius: 50%; display: inline-block; }
-.dot-green { background: #67C23A; }
-.dot-blue { background: #409EFF; }
-</style>

+ 0 - 396
pc/src/views/PageHomeConfig.vue

@@ -1,396 +0,0 @@
-<template>
-  <div class="page-home-config">
-    <!-- 顶部操作栏 -->
-    <div class="toolbar">
-      <div class="toolbar-left">
-        <h2 class="toolbar-title">权益介绍页配置</h2>
-        <el-tag v-if="pageStatus === 'published'" type="success" size="small" effect="plain">正式线上环境</el-tag>
-        <el-tag v-else-if="pageStatus === 'modified'" type="warning" size="small" effect="plain">有未发布的修改</el-tag>
-        <el-tag v-else size="small" effect="plain">草稿 (未发布)</el-tag>
-      </div>
-      <div class="toolbar-right">
-        <el-button size="small" icon="el-icon-link" @click="showLinkDialog">获取链接</el-button>
-        <el-button size="small" type="primary" plain @click="handleSave('draft')">保存草稿</el-button>
-        <el-button size="small" type="primary" @click="handleSave('publish')">发布上线</el-button>
-      </div>
-    </div>
-
-    <!-- 客群Tab -->
-    <el-tabs v-model="currentTier" @tab-click="onTierChange">
-      <el-tab-pane
-        v-for="tier in tiers"
-        :key="tier.key"
-        :label="configData[tier.key] ? configData[tier.key].tabName : tier.label"
-        :name="tier.key"
-      />
-    </el-tabs>
-
-    <!-- 配置表单 -->
-    <div v-if="currentTierData" class="config-sections">
-      <!-- Tab名称配置 -->
-      <el-card class="section-card" shadow="hover">
-        <div slot="header" class="section-header">
-          <i class="el-icon-folder-opened"></i> Tab名称配置
-        </div>
-        <el-form label-width="80px">
-          <el-form-item label="Tab名称">
-            <el-input v-model="currentTierData.tabName" placeholder="例如:卓越理财" style="width: 50%" @input="markModified" />
-          </el-form-item>
-        </el-form>
-      </el-card>
-
-      <!-- 头部配置 -->
-      <el-card class="section-card" shadow="hover">
-        <div slot="header" class="section-header">
-          <i class="el-icon-picture-outline"></i> 头部配置
-        </div>
-        <el-form class="header-config-form" label-width="130px">
-          <el-form-item label="头图配图">
-            <div class="image-upload-area">
-              <div class="upload-box" @click="triggerUpload('header')">
-                <img v-if="currentTierData.header.imageUrl" :src="currentTierData.header.imageUrl" class="preview-img" />
-                <div v-else class="upload-placeholder">
-                  <i class="el-icon-picture"></i>
-                  <span>未上传图片</span>
-                </div>
-                <div class="upload-overlay">
-                  <i class="el-icon-upload2"></i>
-                  <span>点击上传</span>
-                </div>
-              </div>
-              <div class="upload-tip">建议图片尺寸:<b>686 × 296 px</b></div>
-            </div>
-            <input ref="headerUpload" type="file" accept="image/*" style="display:none" @change="onImageUpload($event, 'header')" />
-          </el-form-item>
-          <el-form-item label="主标题(粗体)">
-            <el-input v-model="currentTierData.header.mainTitle" @input="markModified" />
-          </el-form-item>
-          <el-form-item label="副标题(小字)">
-            <el-input v-model="currentTierData.header.subTitle" @input="markModified" />
-          </el-form-item>
-        </el-form>
-      </el-card>
-
-      <!-- 按钮配置 -->
-      <el-card class="section-card" shadow="hover">
-        <div slot="header" class="section-header">
-          <i class="el-icon-thumb"></i> 按钮配置
-        </div>
-        <el-form label-width="130px">
-          <el-row :gutter="20">
-            <el-col :span="12">
-              <el-form-item label="已解锁按钮文案">
-                <el-input v-model="currentTierData.buttons.unlocked" placeholder="例如:进入权益专区" @input="markModified" />
-              </el-form-item>
-            </el-col>
-            <el-col :span="12">
-              <el-form-item label="待解锁按钮文案">
-                <el-input v-model="currentTierData.buttons.locked" placeholder="例如:待解锁" @input="markModified" />
-              </el-form-item>
-            </el-col>
-          </el-row>
-        </el-form>
-      </el-card>
-
-      <!-- 权益列表配置 -->
-      <el-card class="section-card" shadow="hover">
-        <div slot="header" class="section-header">
-          <i class="el-icon-s-grid"></i> 权益列表配置
-          <el-button type="text" icon="el-icon-circle-plus-outline" @click="addPrivilege" style="float:right">添加权益</el-button>
-        </div>
-        <div v-if="currentTierData.privileges.length === 0" class="empty-tip">
-          暂无权益项
-        </div>
-        <div v-for="(item, index) in currentTierData.privileges" :key="index" class="privilege-item">
-          <div class="privilege-index">NO. {{ index + 1 }}</div>
-          <div class="privilege-actions">
-            <el-popover placement="bottom" trigger="click" width="180">
-              <div>
-                <div style="margin-bottom:8px;font-size:13px;color:#666">移动到序号:</div>
-                <div style="display:flex;gap:8px">
-                  <el-input-number v-model="sortTarget" :min="1" :max="currentTierData.privileges.length" size="mini" controls-position="right" style="width:100px" />
-                  <el-button size="mini" type="primary" @click="movePrivilege(index, sortTarget - 1)">确定</el-button>
-                </div>
-              </div>
-              <el-button slot="reference" size="mini" icon="el-icon-sort" circle @click="sortTarget = index + 1" />
-            </el-popover>
-            <el-button size="mini" type="danger" icon="el-icon-delete" circle @click="removePrivilege(index)" />
-          </div>
-          <div class="privilege-content">
-            <div class="privilege-image" @click="triggerUpload('privilege', index)">
-              <img v-if="item.imageUrl" :src="item.imageUrl" class="preview-img-sm" />
-              <div v-else class="upload-placeholder-sm">
-                <i class="el-icon-picture"></i>
-                <span>未上传</span>
-              </div>
-              <input :ref="'privilegeUpload' + index" type="file" accept="image/*" style="display:none" @change="onImageUpload($event, 'privilege', index)" />
-            </div>
-            <div class="privilege-text-tip">非必填<br/>无图展示圆点</div>
-            <div class="privilege-fields">
-              <el-form label-width="60px" inline>
-                <el-form-item label="标题">
-                  <el-input v-model="item.title" style="width:160px" @input="markModified" />
-                </el-form-item>
-                <el-form-item label="描述">
-                  <el-input v-model="item.desc" style="width:300px" @input="markModified" />
-                </el-form-item>
-              </el-form>
-            </div>
-          </div>
-        </div>
-      </el-card>
-    </div>
-
-    <!-- 获取链接弹窗 -->
-    <el-dialog title="获取页面链接" :visible.sync="linkDialogVisible" width="680px">
-      <el-form label-width="100px" inline>
-        <el-form-item label="Campaign ID">
-          <el-select v-model="linkForm.campaignId" clearable filterable placeholder="请选择 (无)" style="width:240px" @change="updateLinks">
-            <el-option v-for="opt in campaignOptions" :key="opt.value" :label="opt.label" :value="opt.value" />
-          </el-select>
-        </el-form-item>
-        <el-form-item label="Channel ID">
-          <el-select v-model="linkForm.channelId" clearable filterable placeholder="请选择 (无)" style="width:240px" @change="updateLinks">
-            <el-option v-for="opt in channelOptions" :key="opt.value" :label="opt.label" :value="opt.value" />
-          </el-select>
-        </el-form-item>
-      </el-form>
-      <div v-if="pageStatus === 'published'" class="link-section">
-        <div class="link-label"><span class="dot dot-green"></span> 正式环境链接</div>
-        <div class="link-row">
-          <span class="link-type">H5链接</span>
-          <el-input :value="prodH5Url" readonly size="small" />
-          <el-button size="small" type="primary" plain @click="copyLink(prodH5Url)">复制</el-button>
-        </div>
-        <div class="link-row">
-          <span class="link-type">小程序链接</span>
-          <el-input :value="prodAppUrl" readonly size="small" />
-          <el-button size="small" type="primary" plain @click="copyLink(prodAppUrl)">复制</el-button>
-        </div>
-      </div>
-      <div class="link-section">
-        <div class="link-label"><span class="dot dot-blue"></span> 草稿预览链接 (测试)</div>
-        <div class="link-row">
-          <el-input :value="testUrl" readonly size="small" />
-          <el-button size="small" type="primary" plain @click="copyLink(testUrl)">复制</el-button>
-        </div>
-      </div>
-    </el-dialog>
-  </div>
-</template>
-
-<script>
-import request from '@/utils/request'
-import { fetchLinkOptions } from '@/utils/linkOptions'
-
-export default {
-  name: 'PageHomeConfig',
-  data() {
-    return {
-      currentTier: 'premier',
-      tiers: [
-        { key: 'premier', label: '卓越理财' },
-        { key: 'elite', label: '卓越理财·尊尚' },
-        { key: 'global', label: '环球私人银行' }
-      ],
-      configData: {},
-      pageStatus: 'draft',
-      sortTarget: 1,
-      linkDialogVisible: false,
-      linkForm: { campaignId: '', channelId: '' },
-      campaignOptions: [],
-      channelOptions: []
-    }
-  },
-  computed: {
-    currentTierData() {
-      return this.configData[this.currentTier] || null
-    },
-    linkQueryStr() {
-      const params = []
-      if (this.linkForm.campaignId) params.push(`CampaignId=${this.linkForm.campaignId}`)
-      if (this.linkForm.channelId) params.push(`Channelid=${this.linkForm.channelId}`)
-      return params.length ? '?' + params.join('&') : ''
-    },
-    prodH5Url() {
-      return `https://hsbc.com/home${this.linkQueryStr}`
-    },
-    prodAppUrl() {
-      return `hsbc-app://home${this.linkQueryStr}`
-    },
-    testUrl() {
-      const q = this.linkQueryStr
-      return `https://test.hsbc.com/home${q ? q + '&' : '?'}draft=1`
-    }
-  },
-  created() {
-    this.fetchData()
-  },
-  methods: {
-    async fetchData() {
-      const res = await request.get('/api/page-config/home')
-      this.configData = res.data.config
-      this.pageStatus = res.data.status
-    },
-    async loadLinkOptions() {
-      const { campaignOptions, channelOptions } = await fetchLinkOptions()
-      this.campaignOptions = campaignOptions
-      this.channelOptions = channelOptions
-    },
-    markModified() {
-      if (this.pageStatus === 'published') {
-        this.pageStatus = 'modified'
-      }
-    },
-    onTierChange() {},
-    triggerUpload(type, index) {
-      if (type === 'header') {
-        this.$refs.headerUpload.click()
-      } else {
-        const ref = this.$refs['privilegeUpload' + index]
-        if (ref) (Array.isArray(ref) ? ref[0] : ref).click()
-      }
-    },
-    onImageUpload(event, type, index) {
-      const file = event.target.files[0]
-      if (!file) return
-      const reader = new FileReader()
-      reader.onload = (e) => {
-        if (type === 'header') {
-          this.currentTierData.header.imageUrl = e.target.result
-        } else {
-          this.$set(this.currentTierData.privileges[index], 'imageUrl', e.target.result)
-        }
-        this.markModified()
-      }
-      reader.readAsDataURL(file)
-      event.target.value = ''
-    },
-    addPrivilege() {
-      this.currentTierData.privileges.push({ imageUrl: '', title: '新权益', desc: '描述...' })
-      this.markModified()
-    },
-    removePrivilege(index) {
-      this.currentTierData.privileges.splice(index, 1)
-      this.markModified()
-    },
-    movePrivilege(from, to) {
-      if (to < 0 || to >= this.currentTierData.privileges.length || from === to) return
-      const item = this.currentTierData.privileges.splice(from, 1)[0]
-      this.currentTierData.privileges.splice(to, 0, item)
-      this.markModified()
-    },
-    async handleSave(type) {
-      const status = type === 'publish' ? 'published' : 'draft'
-      await request.put('/api/page-config/home', { config: this.configData, status })
-      this.pageStatus = status
-      this.$message.success(type === 'publish' ? '已发布权益介绍页配置!' : '已保存权益介绍页配置草稿!')
-    },
-    async showLinkDialog() {
-      await this.loadLinkOptions()
-      this.linkForm = { campaignId: '', channelId: '' }
-      this.linkDialogVisible = true
-    },
-    updateLinks() {},
-    copyLink(text) {
-      navigator.clipboard.writeText(text).then(() => {
-        this.$message.success('链接已复制到剪贴板')
-      })
-    }
-  }
-}
-</script>
-
-<style scoped>
-.toolbar {
-  display: flex;
-  justify-content: space-between;
-  align-items: center;
-  margin-bottom: 16px;
-  padding: 12px 16px;
-  background: #fff;
-  border-radius: 4px;
-  box-shadow: 0 1px 4px rgba(0,0,0,.06);
-}
-.toolbar-left { display: flex; align-items: center; gap: 12px; }
-.toolbar-title { margin: 0; font-size: 18px; font-weight: 600; }
-.toolbar-right { display: flex; gap: 8px; }
-
-.config-sections { margin-top: 16px; }
-.section-card { margin-bottom: 16px; }
-.section-header { font-weight: 600; }
-.section-header i { margin-right: 6px; color: #909399; }
-.header-config-form .el-form-item__label { white-space: nowrap; }
-
-.image-upload-area { display: flex; align-items: center; gap: 16px; }
-.upload-box {
-  width: 360px; height: 160px;
-  background: #eef6fc; border: 1px solid #dcdfe6; border-radius: 4px;
-  position: relative; overflow: hidden; cursor: pointer;
-  display: flex; align-items: center; justify-content: center;
-}
-.upload-box:hover .upload-overlay { display: flex; }
-.preview-img { width: 100%; height: 100%; object-fit: cover; }
-.upload-placeholder {
-  display: flex; flex-direction: column; align-items: center;
-  color: #c0c4cc; font-size: 12px;
-}
-.upload-placeholder i { font-size: 28px; margin-bottom: 4px; }
-.upload-overlay {
-  display: none; position: absolute; inset: 0;
-  background: rgba(0,0,0,.5); color: #fff;
-  flex-direction: column; align-items: center; justify-content: center;
-  font-size: 14px;
-}
-.upload-overlay i { font-size: 28px; margin-bottom: 6px; }
-.upload-tip {
-  font-size: 12px; color: #909399;
-  background: #f5f7fa; padding: 8px 12px; border-radius: 4px; border: 1px solid #ebeef5;
-}
-
-.privilege-item {
-  position: relative;
-  border: 1px solid #ebeef5; border-radius: 8px;
-  padding: 20px 16px 16px; margin-bottom: 12px;
-  background: #fff;
-}
-.privilege-index {
-  position: absolute; left: 0; top: 0;
-  background: #409EFF; color: #fff;
-  font-size: 11px; font-weight: 700;
-  padding: 2px 10px; border-radius: 8px 0 8px 0;
-}
-.privilege-actions {
-  position: absolute; right: 12px; top: 12px;
-  display: flex; gap: 6px;
-}
-.privilege-content { display: flex; align-items: flex-start; gap: 12px; margin-top: 8px; }
-.privilege-image {
-  width: 56px; height: 56px; flex-shrink: 0;
-  background: #eef6fc; border: 1px solid #dcdfe6; border-radius: 4px;
-  display: flex; align-items: center; justify-content: center;
-  cursor: pointer; overflow: hidden; position: relative;
-}
-.preview-img-sm { width: 100%; height: 100%; object-fit: cover; }
-.upload-placeholder-sm {
-  display: flex; flex-direction: column; align-items: center;
-  color: #c0c4cc; font-size: 10px;
-}
-.upload-placeholder-sm i { font-size: 18px; }
-.privilege-text-tip { font-size: 10px; color: #c0c4cc; text-align: center; line-height: 1.3; flex-shrink: 0; margin-top: 4px; }
-.privilege-fields { flex: 1; }
-
-.empty-tip {
-  text-align: center; color: #c0c4cc; padding: 24px;
-  border: 1px dashed #dcdfe6; border-radius: 4px;
-}
-
-.link-section { margin-top: 16px; padding: 12px; background: #fafafa; border-radius: 6px; border: 1px solid #ebeef5; }
-.link-section + .link-section { margin-top: 12px; }
-.link-label { font-weight: 600; margin-bottom: 8px; display: flex; align-items: center; gap: 6px; }
-.link-row { display: flex; align-items: center; gap: 8px; margin-bottom: 6px; }
-.link-type { font-size: 12px; color: #909399; width: 70px; flex-shrink: 0; }
-.dot { width: 8px; height: 8px; border-radius: 50%; display: inline-block; }
-.dot-green { background: #67C23A; }
-.dot-blue { background: #409EFF; }
-</style>

+ 0 - 22
pc/src/views/PageManagement.vue

@@ -1,22 +0,0 @@
-<template>
-  <div class="page-management">
-    <el-card>
-      <div slot="header">
-        <span>页面管理</span>
-      </div>
-      <el-empty description="页面管理(开发中)"></el-empty>
-    </el-card>
-  </div>
-</template>
-
-<script>
-export default {
-  name: 'PageManagement'
-}
-</script>
-
-<style scoped>
-.page-management {
-  padding: 0;
-}
-</style>

+ 0 - 598
pc/src/views/PageZoneConfig.vue

@@ -1,598 +0,0 @@
-<template>
-  <div class="page-zone-config">
-    <!-- 顶部操作栏 -->
-    <div class="toolbar">
-      <div class="toolbar-left">
-        <h2 class="toolbar-title">权益首页配置</h2>
-        <el-tag v-if="pageStatus === 'published'" type="success" size="small" effect="plain">正式线上环境</el-tag>
-        <el-tag v-else-if="pageStatus === 'modified'" type="warning" size="small" effect="plain">有未发布的修改</el-tag>
-        <el-tag v-else size="info" effect="plain">草稿 (未发布)</el-tag>
-      </div>
-      <div class="toolbar-right">
-        <el-button size="small" icon="el-icon-link" @click="showLinkDialog">获取链接</el-button>
-        <el-button size="small" type="primary" plain @click="handleSave('draft')">保存草稿</el-button>
-        <el-button size="small" type="primary" @click="handleSave('publish')">发布上线</el-button>
-      </div>
-    </div>
-
-    <!-- 客户身份模块配置 -->
-    <el-card class="section-card" shadow="hover">
-      <div slot="header" class="section-header">
-        <span><i class="el-icon-postcard"></i> 客户身份模块配置</span>
-      </div>
-      <el-alert
-        type="info"
-        :closable="false"
-        show-icon
-        title="前端将根据登录客户的实际客群 (MSC) 自动匹配展示以下对应的身份卡片内容。若关闭“显示账户月内日均余额”开关,则对应的计算规则弹窗配置项将自动隐藏,前端也不会展示。"
-        style="margin-bottom:16px"
-      />
-
-      <div v-for="m in identityMscList" :key="m.key" class="identity-card">
-        <div class="identity-title"><span class="title-bar"></span>{{ m.label }}</div>
-
-        <!-- 客群背景图 -->
-        <div class="identity-row">
-          <div class="identity-field-label">客群背景图</div>
-          <div class="bg-upload-area">
-            <div class="bg-upload-box" @click="triggerIdentityUpload(m.key)">
-              <img v-if="configData.identityModule[m.key].bgImage" :src="configData.identityModule[m.key].bgImage" class="preview-img" />
-              <div v-else class="upload-placeholder">
-                <i class="el-icon-picture"></i>
-                <span>未上传</span>
-              </div>
-              <div class="upload-overlay">
-                <i class="el-icon-upload2"></i>
-                <span>点击上传</span>
-              </div>
-            </div>
-            <div class="upload-tip">建议图片尺寸:<b>750 × 185 px</b></div>
-            <input :ref="'identityUpload_' + m.key" type="file" accept="image/*" style="display:none" @change="onIdentityImageUpload($event, m.key)" />
-          </div>
-        </div>
-
-        <!-- 身份标识文字 + 右侧开关 -->
-        <div class="identity-row identity-text-row">
-          <div class="identity-field">
-            <div class="identity-field-label">身份标识文字 <span class="muted">(显示于移动端卡片左上角)</span></div>
-            <el-input
-              v-model="configData.identityModule[m.key].identityText"
-              maxlength="20"
-              placeholder="请输入"
-              style="width:360px"
-              @input="markModified"
-            />
-          </div>
-          <div class="identity-toggles">
-            <template v-if="m.key !== 'privateBank'">
-              <el-checkbox v-model="configData.identityModule[m.key].showQualifyStatus" @change="markModified">显示合格状态 (及编号)</el-checkbox>
-              <el-checkbox v-model="configData.identityModule[m.key].showAvgBalance" @change="markModified">显示“账户月内日均余额”</el-checkbox>
-            </template>
-            <template v-else>
-              <el-checkbox v-model="configData.identityModule.privateBank.showPbLevel" @change="markModified">显示PB权益等级及编号 (支持复制)</el-checkbox>
-              <div class="pb-hint">(PB客群不显示月内日均余额)</div>
-            </template>
-          </div>
-        </div>
-
-        <!-- 日均余额计算规则说明 -->
-        <div
-          v-if="m.key !== 'privateBank' && configData.identityModule[m.key].showAvgBalance"
-          class="rule-block"
-        >
-          <div class="rule-label">
-            <i class="el-icon-question"></i>
-            日均余额计算规则说明 <span class="muted">(前端点击问号展示)</span>
-          </div>
-          <el-input
-            v-model="configData.identityModule[m.key].avgBalanceRule"
-            type="textarea"
-            :rows="3"
-            placeholder="请输入计算规则说明"
-            @input="markModified"
-          />
-        </div>
-      </div>
-    </el-card>
-
-    <!-- 活动广告轮播图 -->
-    <el-card class="section-card" shadow="hover">
-      <div slot="header" class="section-header">
-        <span><i class="el-icon-data-board"></i> 活动广告轮播图</span>
-        <el-button type="text" icon="el-icon-circle-plus-outline" @click="addCarousel" style="float:right">添加轮播</el-button>
-      </div>
-      <div v-if="configData.adCarousel.length === 0" class="empty-tip">暂无广告轮播图</div>
-      <div v-for="(item, index) in configData.adCarousel" :key="'c' + index" class="carousel-item">
-        <div class="item-index">NO. {{ index + 1 }}</div>
-        <div class="item-actions">
-          <el-popover placement="bottom" trigger="click" width="180">
-            <div>
-              <div style="margin-bottom:8px;font-size:13px;color:#666">移动到序号:</div>
-              <div style="display:flex;gap:8px">
-                <el-input-number v-model="sortTarget" :min="1" :max="configData.adCarousel.length" size="mini" controls-position="right" style="width:100px" />
-                <el-button size="mini" type="primary" @click="moveItem('adCarousel', index, sortTarget - 1)">确定</el-button>
-              </div>
-            </div>
-            <el-button slot="reference" size="mini" icon="el-icon-sort" circle @click="sortTarget = index + 1" />
-          </el-popover>
-          <el-button size="mini" type="danger" icon="el-icon-delete" circle @click="removeCarousel(index)" />
-        </div>
-
-        <!-- 图片上传 -->
-        <div class="carousel-body">
-          <div class="image-upload-area">
-            <div class="upload-box" @click="triggerUpload('carousel', index)">
-              <img v-if="item.imageUrl" :src="item.imageUrl" class="preview-img" />
-              <div v-else class="upload-placeholder">
-                <i class="el-icon-picture"></i>
-                <span>未上传</span>
-              </div>
-              <div class="upload-overlay">
-                <i class="el-icon-upload2"></i>
-                <span>点击上传</span>
-              </div>
-            </div>
-            <div class="upload-tip">建议图片尺寸:<b>686 × 296 px</b></div>
-            <input :ref="'carouselUpload' + index" type="file" accept="image/*" style="display:none" @change="onImageUpload($event, 'carousel', index)" />
-          </div>
-
-          <!-- 标题 -->
-          <el-form label-width="80px" style="margin-top:12px">
-            <el-form-item label="主标题">
-              <el-input v-model="item.title" @input="markModified" />
-            </el-form-item>
-            <el-form-item label="副标题">
-              <el-input v-model="item.subtitle" @input="markModified" />
-            </el-form-item>
-          </el-form>
-
-          <!-- 可见人群标签 -->
-          <el-form label-width="110px" style="margin-top:4px">
-            <el-form-item label="可见人群标签">
-              <el-select v-model="item.tags" multiple placeholder="点击选择..." filterable @change="markModified" style="width:100%">
-                <el-option v-for="tag in availableTags" :key="tag.id" :label="tag.name" :value="tag.id" />
-              </el-select>
-            </el-form-item>
-          </el-form>
-
-          <!-- 跳转链接配置 -->
-          <div class="link-config-block">
-            <div class="link-config-title"><i class="el-icon-link"></i> 跳转链接配置</div>
-            <el-form label-width="140px">
-              <el-form-item label="H5 跳转链接">
-                <el-input v-model="item.linkH5" @input="markModified" />
-              </el-form-item>
-              <el-form-item label="APP 跳转链接">
-                <el-input v-model="item.linkApp" @input="markModified" />
-              </el-form-item>
-              <el-row :gutter="20">
-                <el-col :span="12">
-                  <el-form-item label="微信小程序 AppID">
-                    <el-input v-model="item.wechatAppId" placeholder="例如:wx1234567890abcdef" @input="markModified" />
-                  </el-form-item>
-                </el-col>
-                <el-col :span="12">
-                  <el-form-item label="微信小程序页面路径">
-                    <el-input v-model="item.wechatPath" placeholder="例如:pages/index/index" @input="markModified" />
-                  </el-form-item>
-                </el-col>
-              </el-row>
-            </el-form>
-          </div>
-        </div>
-      </div>
-    </el-card>
-
-    <!-- 常用兑换配置 -->
-    <el-card class="section-card" shadow="hover">
-      <div slot="header" class="section-header">
-        <span><i class="el-icon-s-grid"></i> 常用兑换配置</span>
-        <el-button type="text" icon="el-icon-circle-plus-outline" @click="addExchangeItem" style="float:right">添加兑换项</el-button>
-      </div>
-      <div v-if="configData.exchangeConfig.items.length === 0" class="empty-tip">暂无常用兑换项</div>
-      <div v-for="(item, index) in configData.exchangeConfig.items" :key="'e' + index" class="exchange-item">
-        <div class="item-index">NO. {{ index + 1 }}</div>
-        <div class="item-actions">
-          <el-popover placement="bottom" trigger="click" width="180">
-            <div>
-              <div style="margin-bottom:8px;font-size:13px;color:#666">移动到序号:</div>
-              <div style="display:flex;gap:8px">
-                <el-input-number v-model="sortTarget" :min="1" :max="configData.exchangeConfig.items.length" size="mini" controls-position="right" style="width:100px" />
-                <el-button size="mini" type="primary" @click="moveItem('exchangeItems', index, sortTarget - 1)">确定</el-button>
-              </div>
-            </div>
-            <el-button slot="reference" size="mini" icon="el-icon-sort" circle @click="sortTarget = index + 1" />
-          </el-popover>
-          <el-button size="mini" type="danger" icon="el-icon-delete" circle @click="removeExchangeItem(index)" />
-        </div>
-        <div class="exchange-body">
-          <div class="exchange-image" @click="triggerUpload('exchange', index)">
-            <img v-if="item.imageUrl" :src="item.imageUrl" class="preview-img-sq" />
-            <div v-else class="upload-placeholder-sm">
-              <i class="el-icon-picture"></i>
-              <span>未上传</span>
-            </div>
-            <input :ref="'exchangeUpload' + index" type="file" accept="image/*" style="display:none" @change="onImageUpload($event, 'exchange', index)" />
-          </div>
-          <el-form label-width="80px" inline class="exchange-fields">
-            <el-form-item label="展示文字">
-              <el-input v-model="item.title" style="width:200px" @input="markModified" />
-            </el-form-item>
-            <el-form-item label="跳转链接">
-              <el-input v-model="item.link" style="width:280px" @input="markModified" />
-            </el-form-item>
-          </el-form>
-        </div>
-      </div>
-    </el-card>
-
-    <!-- 积分规则弹窗配置 -->
-    <el-card class="section-card" shadow="hover">
-      <div slot="header" class="section-header">
-        <i class="el-icon-document"></i> 积分规则弹窗配置
-      </div>
-      <el-form label-width="120px">
-        <el-form-item label="弹窗顶部名称">
-          <el-input v-model="configData.ruleModal.title" style="width:50%" @input="markModified" />
-        </el-form-item>
-        <el-form-item label="规则内容">
-          <quill-editor
-            v-model="configData.ruleModal.content"
-            :options="quillOptions"
-            @change="markModified"
-          />
-        </el-form-item>
-      </el-form>
-    </el-card>
-
-    <!-- 获取链接弹窗 -->
-    <el-dialog title="获取页面链接" :visible.sync="linkDialogVisible" width="680px">
-      <el-form label-width="100px" inline>
-        <el-form-item label="Campaign ID">
-          <el-select v-model="linkForm.campaignId" clearable filterable placeholder="请选择 (无)" style="width:240px">
-            <el-option v-for="opt in campaignOptions" :key="opt.value" :label="opt.label" :value="opt.value" />
-          </el-select>
-        </el-form-item>
-        <el-form-item label="Channel ID">
-          <el-select v-model="linkForm.channelId" clearable filterable placeholder="请选择 (无)" style="width:240px">
-            <el-option v-for="opt in channelOptions" :key="opt.value" :label="opt.label" :value="opt.value" />
-          </el-select>
-        </el-form-item>
-      </el-form>
-      <div v-if="pageStatus === 'published'" class="link-section">
-        <div class="link-label"><span class="dot dot-green"></span> 正式环境链接</div>
-        <div class="link-row">
-          <span class="link-type">H5链接</span>
-          <el-input :value="prodH5Url" readonly size="small" />
-          <el-button size="small" type="primary" plain @click="copyLink(prodH5Url)">复制</el-button>
-        </div>
-        <div class="link-row">
-          <span class="link-type">小程序链接</span>
-          <el-input :value="prodAppUrl" readonly size="small" />
-          <el-button size="small" type="primary" plain @click="copyLink(prodAppUrl)">复制</el-button>
-        </div>
-      </div>
-      <div class="link-section">
-        <div class="link-label"><span class="dot dot-blue"></span> 草稿预览链接 (测试)</div>
-        <div class="link-row">
-          <el-input :value="testUrl" readonly size="small" />
-          <el-button size="small" type="primary" plain @click="copyLink(testUrl)">复制</el-button>
-        </div>
-      </div>
-    </el-dialog>
-  </div>
-</template>
-
-<script>
-import request from '@/utils/request'
-import { fetchLinkOptions } from '@/utils/linkOptions'
-
-export default {
-  name: 'PageZoneConfig',
-  data() {
-    return {
-      configData: {
-        identityModule: this.defaultIdentityModule(),
-        adCarousel: [],
-        exchangeConfig: { items: [] },
-        ruleModal: { title: '', content: '' }
-      },
-      identityMscList: [
-        { key: 'advance', label: '运筹理财 (Advance)' },
-        { key: 'premier', label: '卓越理财 (Premier)' },
-        { key: 'premierElite', label: '卓越理财·尊尚 (Premier Elite)' },
-        { key: 'privateBank', label: '环球私人银行 (Private Bank)' }
-      ],
-      availableTags: [],
-      pageStatus: 'draft',
-      dataLoaded: false,
-      sortTarget: 1,
-      linkDialogVisible: false,
-      linkForm: { campaignId: '', channelId: '' },
-      campaignOptions: [],
-      channelOptions: [],
-      quillOptions: {
-        placeholder: '请输入积分规则内容...',
-        modules: {
-          toolbar: [
-            ['bold', 'italic', 'underline'],
-            [{ list: 'ordered' }, { list: 'bullet' }],
-            [{ header: [1, 2, 3, false] }],
-            ['link', 'image'],
-            ['clean']
-          ]
-        }
-      }
-    }
-  },
-  computed: {
-    linkQueryStr() {
-      const params = []
-      if (this.linkForm.campaignId) params.push(`CampaignId=${this.linkForm.campaignId}`)
-      if (this.linkForm.channelId) params.push(`Channelid=${this.linkForm.channelId}`)
-      return params.length ? '?' + params.join('&') : ''
-    },
-    prodH5Url() {
-      return `https://hsbc.com/zone${this.linkQueryStr}`
-    },
-    prodAppUrl() {
-      return `hsbc-app://zone${this.linkQueryStr}`
-    },
-    testUrl() {
-      const q = this.linkQueryStr
-      return `https://test.hsbc.com/zone${q ? q + '&' : '?'}draft=1`
-    }
-  },
-  created() {
-    this.fetchData()
-  },
-  methods: {
-    defaultIdentityModule() {
-      const base = { bgImage: '', identityText: '', showQualifyStatus: false, showAvgBalance: true, avgBalanceRule: '' }
-      return {
-        advance: { ...base },
-        premier: { ...base },
-        premierElite: { ...base },
-        privateBank: { bgImage: '', identityText: '', showPbLevel: true }
-      }
-    },
-    async fetchData() {
-      const res = await request.get('/api/page-config/zone')
-      const cfg = res.data.config || {}
-      if (!cfg.identityModule) cfg.identityModule = this.defaultIdentityModule()
-      this.configData = cfg
-      this.availableTags = res.data.availableTags
-      this.pageStatus = res.data.status
-      this.$nextTick(() => { this.dataLoaded = true })
-    },
-    triggerIdentityUpload(key) {
-      const ref = this.$refs['identityUpload_' + key]
-      if (ref) (Array.isArray(ref) ? ref[0] : ref).click()
-    },
-    onIdentityImageUpload(event, key) {
-      const file = event.target.files[0]
-      if (!file) return
-      const reader = new FileReader()
-      reader.onload = (e) => {
-        this.$set(this.configData.identityModule[key], 'bgImage', e.target.result)
-        this.markModified()
-      }
-      reader.readAsDataURL(file)
-      event.target.value = ''
-    },
-    async loadLinkOptions() {
-      const { campaignOptions, channelOptions } = await fetchLinkOptions()
-      this.campaignOptions = campaignOptions
-      this.channelOptions = channelOptions
-    },
-    markModified() {
-      if (!this.dataLoaded) return
-      if (this.pageStatus === 'published') {
-        this.pageStatus = 'modified'
-      }
-    },
-    triggerUpload(type, index) {
-      const ref = this.$refs[type + 'Upload' + index]
-      if (ref) (Array.isArray(ref) ? ref[0] : ref).click()
-    },
-    onImageUpload(event, type, index) {
-      const file = event.target.files[0]
-      if (!file) return
-      const reader = new FileReader()
-      reader.onload = (e) => {
-        if (type === 'carousel') {
-          this.$set(this.configData.adCarousel[index], 'imageUrl', e.target.result)
-        } else {
-          this.$set(this.configData.exchangeConfig.items[index], 'imageUrl', e.target.result)
-        }
-        this.markModified()
-      }
-      reader.readAsDataURL(file)
-      event.target.value = ''
-    },
-    addCarousel() {
-      this.configData.adCarousel.push({
-        imageUrl: '', linkH5: '', linkApp: '', wechatAppId: '', wechatPath: '',
-        title: '新广告', subtitle: '', tags: []
-      })
-      this.markModified()
-    },
-    removeCarousel(index) {
-      this.configData.adCarousel.splice(index, 1)
-      this.markModified()
-    },
-    addExchangeItem() {
-      this.configData.exchangeConfig.items.push({ imageUrl: '', title: '新兑换项', link: '' })
-      this.markModified()
-    },
-    removeExchangeItem(index) {
-      this.configData.exchangeConfig.items.splice(index, 1)
-      this.markModified()
-    },
-    moveItem(type, from, to) {
-      const arr = type === 'adCarousel' ? this.configData.adCarousel : this.configData.exchangeConfig.items
-      if (to < 0 || to >= arr.length || from === to) return
-      const item = arr.splice(from, 1)[0]
-      arr.splice(to, 0, item)
-      this.markModified()
-    },
-    async handleSave(type) {
-      const status = type === 'publish' ? 'published' : 'draft'
-      await request.put('/api/page-config/zone', { config: this.configData, status })
-      this.pageStatus = status
-      this.$message.success(type === 'publish' ? '已发布权益首页配置!' : '已保存权益首页配置草稿!')
-    },
-    async showLinkDialog() {
-      await this.loadLinkOptions()
-      this.linkForm = { campaignId: '', channelId: '' }
-      this.linkDialogVisible = true
-    },
-    copyLink(text) {
-      navigator.clipboard.writeText(text).then(() => {
-        this.$message.success('链接已复制到剪贴板')
-      })
-    }
-  }
-}
-</script>
-
-<style scoped>
-.toolbar {
-  display: flex; justify-content: space-between; align-items: center;
-  margin-bottom: 16px; padding: 12px 16px;
-  background: #fff; border-radius: 4px; box-shadow: 0 1px 4px rgba(0,0,0,.06);
-}
-.toolbar-left { display: flex; align-items: center; gap: 12px; }
-.toolbar-title { margin: 0; font-size: 18px; font-weight: 600; }
-.toolbar-right { display: flex; gap: 8px; }
-
-.section-card { margin-bottom: 16px; }
-.section-header { font-weight: 600; }
-.section-header i { margin-right: 6px; color: #909399; }
-
-.carousel-item, .exchange-item {
-  position: relative; border: 1px solid #ebeef5; border-radius: 8px;
-  padding: 24px 16px 16px; margin-bottom: 16px; background: #fff;
-}
-.item-index {
-  position: absolute; left: 0; top: 0;
-  background: #409EFF; color: #fff; font-size: 11px; font-weight: 700;
-  padding: 2px 10px; border-radius: 8px 0 8px 0;
-}
-.item-actions {
-  position: absolute; right: 12px; top: 12px;
-  display: flex; gap: 6px;
-}
-
-.image-upload-area { display: flex; align-items: center; gap: 16px; }
-.upload-box {
-  width: 256px; height: 144px;
-  background: #eef6fc; border: 1px solid #dcdfe6; border-radius: 4px;
-  position: relative; overflow: hidden; cursor: pointer;
-  display: flex; align-items: center; justify-content: center; flex-shrink: 0;
-}
-.upload-box:hover .upload-overlay { display: flex; }
-.preview-img { width: 100%; height: 100%; object-fit: cover; }
-.upload-placeholder {
-  display: flex; flex-direction: column; align-items: center;
-  color: #c0c4cc; font-size: 12px;
-}
-.upload-placeholder i { font-size: 28px; margin-bottom: 4px; }
-.upload-overlay {
-  display: none; position: absolute; inset: 0;
-  background: rgba(0,0,0,.5); color: #fff;
-  flex-direction: column; align-items: center; justify-content: center; font-size: 14px;
-}
-.upload-overlay i { font-size: 24px; margin-bottom: 4px; }
-.upload-tip {
-  font-size: 12px; color: #909399;
-  background: #f5f7fa; padding: 8px 12px; border-radius: 4px; border: 1px solid #ebeef5;
-}
-
-.link-config-block {
-  background: #f5f7fa; padding: 16px; border-radius: 6px;
-  border: 1px solid #ebeef5; margin-top: 8px;
-}
-.link-config-title {
-  font-size: 13px; font-weight: 600; color: #606266;
-  border-bottom: 1px solid #ebeef5; padding-bottom: 8px; margin-bottom: 12px;
-}
-
-.exchange-body { display: flex; align-items: flex-start; gap: 16px; margin-top: 8px; }
-.exchange-image {
-  width: 80px; height: 80px; flex-shrink: 0;
-  background: #eef6fc; border: 1px solid #dcdfe6; border-radius: 4px;
-  display: flex; align-items: center; justify-content: center;
-  cursor: pointer; overflow: hidden;
-}
-.preview-img-sq { width: 100%; height: 100%; object-fit: cover; }
-.upload-placeholder-sm {
-  display: flex; flex-direction: column; align-items: center;
-  color: #c0c4cc; font-size: 10px;
-}
-.upload-placeholder-sm i { font-size: 18px; }
-.exchange-fields { flex: 1; margin-top: 8px; }
-
-.empty-tip {
-  text-align: center; color: #c0c4cc; padding: 24px;
-  border: 1px dashed #dcdfe6; border-radius: 4px;
-}
-
-.link-section { margin-top: 16px; padding: 12px; background: #fafafa; border-radius: 6px; border: 1px solid #ebeef5; }
-.link-section + .link-section { margin-top: 12px; }
-.link-label { font-weight: 600; margin-bottom: 8px; display: flex; align-items: center; gap: 6px; }
-.link-row { display: flex; align-items: center; gap: 8px; margin-bottom: 6px; }
-.link-type { font-size: 12px; color: #909399; width: 70px; flex-shrink: 0; }
-.dot { width: 8px; height: 8px; border-radius: 50%; display: inline-block; }
-.dot-green { background: #67C23A; }
-.dot-blue { background: #409EFF; }
-
-/* 客户身份模块配置 */
-.identity-card {
-  border: 1px solid #ebeef5;
-  border-radius: 8px;
-  padding: 16px 20px;
-  margin-bottom: 16px;
-  background: #fff;
-}
-.identity-title {
-  display: flex; align-items: center;
-  font-size: 14px; font-weight: 600; color: #303133;
-  margin-bottom: 14px;
-}
-.title-bar {
-  display: inline-block; width: 4px; height: 14px;
-  background: #409EFF; border-radius: 2px; margin-right: 8px;
-}
-.identity-row { margin-bottom: 14px; }
-.identity-field-label {
-  font-size: 13px; color: #606266; margin-bottom: 6px;
-}
-.muted { color: #909399; font-weight: normal; font-size: 12px; }
-.bg-upload-area {
-  display: flex; align-items: center; gap: 16px;
-}
-.bg-upload-box {
-  width: 300px; height: 75px;
-  background: #eef6fc; border: 1px solid #dcdfe6; border-radius: 4px;
-  position: relative; overflow: hidden; cursor: pointer;
-  display: flex; align-items: center; justify-content: center; flex-shrink: 0;
-}
-.bg-upload-box:hover .upload-overlay { display: flex; }
-.identity-text-row {
-  display: flex; align-items: flex-start; justify-content: space-between; gap: 24px;
-}
-.identity-field { flex: 1; }
-.identity-toggles {
-  display: flex; flex-direction: column; gap: 6px; padding-top: 22px;
-}
-.pb-hint { font-size: 12px; color: #909399; margin-top: 4px; }
-.rule-block {
-  background: #f5f7fa; border: 1px solid #ebeef5; border-radius: 4px;
-  padding: 12px 14px;
-}
-.rule-label {
-  font-size: 13px; color: #606266; margin-bottom: 8px;
-}
-.rule-label i { color: #409EFF; margin-right: 4px; }
-</style>

+ 0 - 171
pc/src/views/PointsDetail.vue

@@ -1,171 +0,0 @@
-<template>
-  <div class="points-detail">
-    <el-card>
-      <div slot="header" class="search-section">
-        <el-form :model="searchForm" label-width="100px" inline>
-          <el-form-item label="客户号:">
-            <el-input v-model="searchForm.customerId" placeholder="请输入客户号" clearable style="width: 220px;"></el-input>
-          </el-form-item>
-          <el-form-item label="变动类型:">
-            <el-select v-model="searchForm.changeType" placeholder="请选择变动类型" clearable style="width: 220px;">
-              <el-option v-for="item in changeTypeOptions" :key="item" :label="item" :value="item"></el-option>
-            </el-select>
-          </el-form-item>
-          <el-form-item label="查询日期范围:">
-            <el-date-picker
-              v-model="searchForm.dateRange"
-              type="daterange"
-              range-separator="至"
-              start-placeholder="开始日期"
-              end-placeholder="结束日期"
-              value-format="yyyy-MM-dd"
-              style="width: 360px;"
-            ></el-date-picker>
-          </el-form-item>
-          <el-form-item>
-            <el-button type="primary" @click="handleSearch">查询</el-button>
-            <el-button @click="handleReset">重置</el-button>
-            <el-button type="success" icon="el-icon-download" @click="handleExport">导出</el-button>
-          </el-form-item>
-        </el-form>
-      </div>
-
-      <el-table :data="pagedData" stripe border style="width: 100%;" v-loading="loading">
-        <el-table-column prop="customerId" label="客户号"></el-table-column>
-        <el-table-column prop="changeDate" label="变动日期"></el-table-column>
-        <el-table-column prop="changeAmount" label="变动积分金额"></el-table-column>
-        <el-table-column prop="totalPoints" label="积分总数"></el-table-column>
-        <el-table-column prop="hyhPoints" label="HYH积分"></el-table-column>
-        <el-table-column prop="creditCardPoints" label="信用卡积分"></el-table-column>
-        <el-table-column prop="changeType" label="变动类别">
-          <template slot-scope="scope">
-            <el-tag :type="changeTypeTag[scope.row.changeType]" size="small">{{ scope.row.changeType }}</el-tag>
-          </template>
-        </el-table-column>
-        <el-table-column prop="projectName" label="积分项目类别" show-overflow-tooltip></el-table-column>
-      </el-table>
-
-      <el-pagination
-        style="margin-top: 20px; text-align: right;"
-        @current-change="handlePageChange"
-        :current-page="currentPage"
-        :page-size="pageSize"
-        layout="total, prev, pager, next, jumper"
-        :total="filteredData.length"
-      ></el-pagination>
-    </el-card>
-  </div>
-</template>
-
-<script>
-const CHANGE_TYPES = ['获取积分', '使用积分', '积分过期']
-const EARN_PROJECTS = ['消费返积分', '开户礼积分', '生日奖励积分', '活动赠送积分', '推荐好友奖励', '月度达标奖励']
-const SPEND_PROJECTS = ['积分兑换商品', '积分抵现', '积分兑换券码', '积分兑换里程', '积分兑换权益']
-
-function pad(n) { return String(n).padStart(2, '0') }
-
-function generateMockData() {
-  const list = []
-  const customerIds = ['CUST100231', 'CUST100458', 'CUST100672', 'CUST100899', 'CUST101024', 'CUST101337', 'CUST101582', 'CUST101790']
-
-  for (let i = 1; i <= 80; i++) {
-    const d = new Date(2026, 4, 15)
-    d.setDate(d.getDate() - Math.floor(Math.random() * 120))
-    const dateStr = `${d.getFullYear()}/${pad(d.getMonth() + 1)}/${pad(d.getDate())}`
-
-    const changeType = CHANGE_TYPES[Math.floor(Math.random() * CHANGE_TYPES.length)]
-    let changeAmount, projectName
-    if (changeType === '获取积分') {
-      changeAmount = Math.floor(Math.random() * 500 + 1)
-      projectName = EARN_PROJECTS[Math.floor(Math.random() * EARN_PROJECTS.length)]
-    } else if (changeType === '使用积分') {
-      changeAmount = Math.floor(Math.random() * 300 + 1)
-      projectName = SPEND_PROJECTS[Math.floor(Math.random() * SPEND_PROJECTS.length)]
-    } else {
-      changeAmount = Math.floor(Math.random() * 200 + 50)
-      projectName = '/'
-    }
-
-    const totalPoints = Math.floor(Math.random() * 5000 + 100)
-    const hyhPoints = Math.floor(totalPoints * (Math.random() * 0.4 + 0.1))
-    const creditCardPoints = totalPoints - hyhPoints
-
-    list.push({
-      id: i,
-      customerId: customerIds[Math.floor(Math.random() * customerIds.length)],
-      changeDate: dateStr,
-      changeAmount: changeAmount,
-      totalPoints: totalPoints,
-      hyhPoints: hyhPoints,
-      creditCardPoints: creditCardPoints,
-      changeType: changeType,
-      projectName: projectName
-    })
-  }
-  list.sort((a, b) => b.changeDate.localeCompare(a.changeDate))
-  return list
-}
-
-export default {
-  name: 'PointsDetail',
-  data() {
-    return {
-      searchForm: { customerId: '', changeType: '', dateRange: [] },
-      loading: false,
-      currentPage: 1,
-      pageSize: 10,
-      tableData: generateMockData(),
-      changeTypeOptions: CHANGE_TYPES,
-      changeTypeTag: {
-        '获取积分': 'success',
-        '使用积分': '',
-        '积分过期': 'info'
-      }
-    }
-  },
-  computed: {
-    filteredData() {
-      let list = this.tableData
-      const keyword = this.searchForm.customerId.trim()
-      if (keyword) {
-        list = list.filter(item => item.customerId.includes(keyword))
-      }
-      if (this.searchForm.changeType) {
-        list = list.filter(item => item.changeType === this.searchForm.changeType)
-      }
-      if (this.searchForm.dateRange && this.searchForm.dateRange.length === 2) {
-        const start = this.searchForm.dateRange[0].replace(/-/g, '/')
-        const end = this.searchForm.dateRange[1].replace(/-/g, '/')
-        list = list.filter(item => item.changeDate >= start && item.changeDate <= end)
-      }
-      return list
-    },
-    pagedData() {
-      const start = (this.currentPage - 1) * this.pageSize
-      return this.filteredData.slice(start, start + this.pageSize)
-    }
-  },
-  methods: {
-    handleSearch() {
-      this.currentPage = 1
-    },
-    handleReset() {
-      this.searchForm = { customerId: '', changeType: '', dateRange: [] }
-      this.currentPage = 1
-    },
-    handlePageChange(page) {
-      this.currentPage = page
-    },
-    handleExport() {
-      this.$message.success('导出任务已提交,请稍后在下载中心查看')
-    }
-  }
-}
-</script>
-
-<style scoped>
-.search-section {
-  display: flex;
-  align-items: center;
-}
-</style>

+ 0 - 291
pc/src/views/RuleGroupEdit.vue

@@ -1,291 +0,0 @@
-<template>
-  <div class="rule-group-edit">
-    <el-card>
-      <div slot="header" class="header">
-        <span class="title">{{ isEdit ? '编辑客群规则任务' : '新增客群规则任务' }}</span>
-        <span v-if="isEdit && current" class="rg-id">编号:{{ current.id }}</span>
-      </div>
-
-      <!-- 步骤指示 -->
-      <el-steps :active="step" finish-status="success" align-center class="steps">
-        <el-step title="基本信息" icon="el-icon-edit-outline" />
-        <el-step title="规则配置" icon="el-icon-set-up" />
-      </el-steps>
-
-      <!-- Step 1: 基本信息 -->
-      <div v-show="step === 0" class="section">
-        <div class="section-title">基本信息</div>
-        <el-form
-          ref="baseForm"
-          :model="form"
-          :rules="rules"
-          label-width="120px"
-          style="max-width: 760px; margin: 0 auto"
-        >
-          <el-form-item label="规则组名称" prop="name">
-            <el-input v-model="form.name" placeholder="最长 40 字符" maxlength="40" show-word-limit />
-          </el-form-item>
-          <el-form-item label="规则组类型" prop="type">
-            <el-select v-model="form.type" placeholder="请选择" style="width: 100%">
-              <el-option
-                v-for="opt in groupTypes"
-                :key="opt.value"
-                :label="opt.label"
-                :value="opt.value"
-              />
-            </el-select>
-          </el-form-item>
-          <el-form-item label="规则逻辑" prop="logic">
-            <el-radio-group v-model="form.logic">
-              <el-radio label="AND">且(AND,全部满足)</el-radio>
-              <el-radio label="OR">或(OR,任一满足)</el-radio>
-            </el-radio-group>
-          </el-form-item>
-          <el-form-item label="备注说明">
-            <el-input
-              v-model="form.description"
-              type="textarea"
-              :rows="2"
-              placeholder="规则组用途、命中场景等,最长 200 字符"
-              maxlength="200"
-              show-word-limit
-            />
-          </el-form-item>
-        </el-form>
-      </div>
-
-      <!-- Step 2: 规则配置 -->
-      <div v-show="step === 1" class="section">
-        <div class="section-title">
-          规则组配置
-          <span class="section-tip">规则之间使用「{{ form.logic === 'AND' ? '且' : '或' }}」逻辑组合</span>
-        </div>
-
-        <el-table :data="form.rules" border style="width: 100%" :empty-text="'暂无规则,请点击下方加号新增'">
-          <el-table-column prop="metaCode" label="元规则编号" width="110" align="center" />
-          <el-table-column label="元规则名称" min-width="180">
-            <template slot-scope="scope">{{ metaName(scope.row.metaCode) }}</template>
-          </el-table-column>
-          <el-table-column prop="ruleName" label="规则名称" min-width="160" show-overflow-tooltip />
-          <el-table-column label="规则描述" min-width="240" show-overflow-tooltip>
-            <template slot-scope="scope">{{ buildDesc(scope.row) }}</template>
-          </el-table-column>
-          <el-table-column label="规则参数" min-width="220" show-overflow-tooltip>
-            <template slot-scope="scope">
-              <span class="params-text">{{ buildParam(scope.row) }}</span>
-            </template>
-          </el-table-column>
-          <el-table-column label="操作" width="140" align="center">
-            <template slot-scope="scope">
-              <el-button
-                type="primary"
-                circle
-                size="mini"
-                icon="el-icon-edit"
-                title="编辑"
-                @click="openRuleDialog('edit', scope.row, scope.$index)"
-              />
-              <el-button
-                type="warning"
-                circle
-                size="mini"
-                icon="el-icon-view"
-                title="查看"
-                @click="openRuleDialog('view', scope.row)"
-              />
-              <el-button
-                type="danger"
-                circle
-                size="mini"
-                icon="el-icon-delete"
-                title="删除"
-                @click="removeRule(scope.$index)"
-              />
-            </template>
-          </el-table-column>
-        </el-table>
-
-        <div class="add-rule-row">
-          <el-button
-            type="primary"
-            circle
-            icon="el-icon-plus"
-            @click="openRuleDialog('add')"
-          />
-        </div>
-      </div>
-
-      <!-- 操作按钮 -->
-      <div class="footer">
-        <el-button v-if="step === 0" @click="handleCancel">取消</el-button>
-        <el-button v-if="step === 1" @click="step = 0">上一步</el-button>
-        <el-button v-if="step === 0" type="primary" @click="handleNext">下一步</el-button>
-        <el-button v-if="step === 1" type="primary" @click="handleSave">完成</el-button>
-      </div>
-    </el-card>
-
-    <rule-edit-dialog
-      :visible.sync="dialog.visible"
-      :rule="dialog.rule"
-      :mode="dialog.mode"
-      @save="onRuleSave"
-    />
-  </div>
-</template>
-
-<script>
-import ruleGroupStore from '@/store/ruleGroupStore'
-import {
-  GROUP_TYPES,
-  getMetaRule,
-  buildRuleDescription,
-  buildRuleParams
-} from '@/config/metaRules'
-import RuleEditDialog from '@/components/ruleGroup/RuleEditDialog.vue'
-
-export default {
-  name: 'RuleGroupEdit',
-  components: { RuleEditDialog },
-  data() {
-    return {
-      step: 0,
-      form: {
-        name: '',
-        type: '',
-        logic: 'AND',
-        description: '',
-        rules: []
-      },
-      rules: {
-        name: [
-          { required: true, message: '请输入规则组名称', trigger: 'blur' },
-          { max: 40, message: '最长 40 字符', trigger: 'blur' }
-        ],
-        type: [{ required: true, message: '请选择规则组类型', trigger: 'change' }],
-        logic: [{ required: true, message: '请选择规则逻辑', trigger: 'change' }]
-      },
-      groupTypes: GROUP_TYPES,
-      current: null,
-      dialog: { visible: false, mode: 'add', rule: null, editIndex: -1 }
-    }
-  },
-  computed: {
-    isEdit() {
-      return !!this.$route.params.id
-    }
-  },
-  created() {
-    if (this.isEdit) {
-      const target = ruleGroupStore.getById(this.$route.params.id)
-      if (!target) {
-        this.$message.error('规则组不存在')
-        this.$router.replace('/customization/hsbc/rule-groups')
-        return
-      }
-      if (target.status === 'online') {
-        this.$message.warning('已上线规则组不可编辑')
-        this.$router.replace(`/customization/hsbc/rule-groups/view/${target.id}`)
-        return
-      }
-      this.current = target
-      this.form = {
-        name: target.name,
-        type: target.type,
-        logic: target.logic,
-        description: target.description,
-        rules: JSON.parse(JSON.stringify(target.rules || []))
-      }
-    }
-  },
-  methods: {
-    metaName(code) {
-      const m = getMetaRule(code)
-      return m ? m.name : code
-    },
-    buildDesc(rule) {
-      return buildRuleDescription(rule.metaCode, rule.config) || '-'
-    },
-    buildParam(rule) {
-      return buildRuleParams(rule.metaCode, rule.config) || '-'
-    },
-    handleNext() {
-      this.$refs.baseForm.validate((ok) => {
-        if (ok) this.step = 1
-      })
-    },
-    openRuleDialog(mode, rule = null, index = -1) {
-      this.dialog = {
-        visible: true,
-        mode,
-        rule: rule ? JSON.parse(JSON.stringify(rule)) : null,
-        editIndex: index
-      }
-    },
-    onRuleSave(rule) {
-      if (this.dialog.mode === 'edit' && this.dialog.editIndex >= 0) {
-        this.$set(this.form.rules, this.dialog.editIndex, rule)
-        this.$message.success('已更新规则')
-      } else {
-        this.form.rules.push(rule)
-        this.$message.success('已添加规则')
-      }
-    },
-    removeRule(idx) {
-      this.$confirm('确定要删除该条规则吗?', '提示', {
-        confirmButtonText: '删除',
-        cancelButtonText: '取消',
-        type: 'warning'
-      }).then(() => {
-        this.form.rules.splice(idx, 1)
-      }).catch(() => {})
-    },
-    handleSave() {
-      if (this.form.rules.length === 0) {
-        this.$message.error('请至少添加一条规则')
-        return
-      }
-      if (this.isEdit) {
-        ruleGroupStore.update(this.current.id, this.form)
-        this.$message.success('保存成功')
-      } else {
-        ruleGroupStore.create(this.form)
-        this.$message.success('创建成功')
-      }
-      this.$router.push('/customization/hsbc/rule-groups')
-    },
-    handleCancel() {
-      this.$router.push('/customization/hsbc/rule-groups')
-    }
-  }
-}
-</script>
-
-<style scoped>
-.rule-group-edit { padding: 0; }
-.header { display: flex; align-items: center; justify-content: space-between; }
-.title { font-size: 16px; font-weight: 600; color: #409EFF; }
-.rg-id { color: #909399; font-size: 13px; }
-
-.steps { margin: 16px 0 32px; }
-
-.section { margin-bottom: 24px; }
-.section-title {
-  font-size: 14px; font-weight: 600; color: #303133;
-  background: #f5f7fa; padding: 10px 14px; border-radius: 4px;
-  margin-bottom: 16px;
-}
-.section-tip { font-weight: normal; color: #909399; margin-left: 12px; font-size: 12px; }
-
-.add-rule-row {
-  display: flex; align-items: center;
-  padding: 16px 0;
-}
-
-.params-text {
-  font-family: 'Menlo', 'Consolas', monospace;
-  color: #606266; font-size: 12px;
-}
-
-.footer { text-align: center; margin-top: 24px; padding-top: 16px; border-top: 1px solid #ebeef5; }
-.footer .el-button { min-width: 100px; }
-</style>

+ 0 - 175
pc/src/views/RuleGroupList.vue

@@ -1,175 +0,0 @@
-<template>
-  <div class="rule-group-list">
-    <el-card>
-      <div slot="header" class="search-section">
-        <div class="search-row">
-          <el-form :model="searchForm" label-width="100px" inline>
-            <el-form-item label="规则组编号:">
-              <el-input v-model="searchForm.id" placeholder="请输入" clearable style="width: 180px" />
-            </el-form-item>
-            <el-form-item label="规则组名称:">
-              <el-input v-model="searchForm.name" placeholder="请输入" clearable style="width: 180px" />
-            </el-form-item>
-            <el-form-item label="规则组类型:">
-              <el-select v-model="searchForm.type" clearable placeholder="全部" style="width: 160px">
-                <el-option
-                  v-for="opt in groupTypes"
-                  :key="opt.value"
-                  :label="opt.label"
-                  :value="opt.value"
-                />
-              </el-select>
-            </el-form-item>
-            <div class="button-group">
-              <el-button type="primary" @click="handleSearch">查询</el-button>
-              <el-button @click="handleReset">重置</el-button>
-            </div>
-          </el-form>
-        </div>
-        <div class="action-row">
-          <el-button type="primary" icon="el-icon-plus" @click="handleCreate">新增规则组</el-button>
-        </div>
-      </div>
-
-      <el-table :data="pagedData" stripe border style="width: 100%">
-        <el-table-column label="操作" width="220" fixed="left">
-          <template slot-scope="scope">
-            <el-button type="text" size="small" @click="handleView(scope.row)">查看详情</el-button>
-            <template v-if="scope.row.status === 'draft'">
-              <el-button type="text" size="small" @click="handleEdit(scope.row)">编辑</el-button>
-              <el-button type="text" size="small" @click="handlePublish(scope.row)">提交审核</el-button>
-              <el-button
-                type="text"
-                size="small"
-                style="color: #F56C6C"
-                @click="handleDelete(scope.row)"
-              >删除</el-button>
-            </template>
-          </template>
-        </el-table-column>
-        <el-table-column prop="id" label="规则组编号" width="170" />
-        <el-table-column prop="name" label="规则组名称" min-width="180" show-overflow-tooltip />
-        <el-table-column label="规则组类型" width="130">
-          <template slot-scope="scope">{{ typeLabel(scope.row.type) }}</template>
-        </el-table-column>
-        <el-table-column label="规则逻辑" width="110" align="center">
-          <template slot-scope="scope">
-            <el-tag size="small" :type="scope.row.logic === 'AND' ? '' : 'warning'">
-              {{ scope.row.logic === 'AND' ? '且(AND)' : '或(OR)' }}
-            </el-tag>
-          </template>
-        </el-table-column>
-        <el-table-column label="规则数量" width="90" align="center">
-          <template slot-scope="scope">{{ (scope.row.rules || []).length }}</template>
-        </el-table-column>
-        <el-table-column label="状态" width="100" align="center">
-          <template slot-scope="scope">
-            <el-tag :type="scope.row.status === 'online' ? 'success' : 'info'" size="small">
-              {{ scope.row.status === 'online' ? '已上线' : '新建' }}
-            </el-tag>
-          </template>
-        </el-table-column>
-        <el-table-column prop="creator" label="创建人" width="100" />
-        <el-table-column prop="createdAt" label="创建时间" width="170" />
-      </el-table>
-
-      <div class="pagination-section">
-        <span class="total">共 {{ filteredData.length }} 条</span>
-        <el-pagination
-          layout="prev, pager, next, sizes, jumper"
-          :current-page.sync="page.current"
-          :page-size.sync="page.size"
-          :page-sizes="[10, 20, 50]"
-          :total="filteredData.length"
-        />
-      </div>
-    </el-card>
-  </div>
-</template>
-
-<script>
-import ruleGroupStore from '@/store/ruleGroupStore'
-import { GROUP_TYPES } from '@/config/metaRules'
-
-export default {
-  name: 'RuleGroupList',
-  data() {
-    return {
-      searchForm: { id: '', name: '', type: '' },
-      page: { current: 1, size: 10 },
-      groupTypes: GROUP_TYPES
-    }
-  },
-  computed: {
-    filteredData() {
-      return ruleGroupStore.query(this.searchForm)
-    },
-    pagedData() {
-      const start = (this.page.current - 1) * this.page.size
-      return this.filteredData.slice(start, start + this.page.size)
-    }
-  },
-  methods: {
-    typeLabel(type) {
-      const item = this.groupTypes.find((g) => g.value === type)
-      return item ? item.label : type
-    },
-    handleSearch() {
-      this.page.current = 1
-    },
-    handleReset() {
-      this.searchForm = { id: '', name: '', type: '' }
-      this.page.current = 1
-    },
-    handleCreate() {
-      this.$router.push('/customization/hsbc/rule-groups/add')
-    },
-    handleView(row) {
-      this.$router.push(`/customization/hsbc/rule-groups/view/${row.id}`)
-    },
-    handleEdit(row) {
-      this.$router.push(`/customization/hsbc/rule-groups/edit/${row.id}`)
-    },
-    handlePublish(row) {
-      this.$confirm(`确定要提交规则组「${row.name}」审核吗?审核通过后将不可编辑/删除。`, '提示', {
-        confirmButtonText: '提交',
-        cancelButtonText: '取消',
-        type: 'warning'
-      }).then(() => {
-        ruleGroupStore.publish(row.id)
-        this.$message.success('已提交审核(demo 默认通过)')
-      }).catch(() => {})
-    },
-    handleDelete(row) {
-      this.$confirm(`确定要删除规则组「${row.name}」吗?删除后无法恢复`, '提示', {
-        confirmButtonText: '确定',
-        cancelButtonText: '取消',
-        type: 'warning'
-      }).then(() => {
-        ruleGroupStore.remove(row.id)
-        this.$message.success('删除成功')
-      }).catch(() => {})
-    }
-  }
-}
-</script>
-
-<style scoped>
-.rule-group-list { padding: 0; }
-
-.search-section { display: flex; flex-direction: column; }
-.search-row .el-form { display: flex; flex-wrap: wrap; gap: 8px 0; }
-.search-row .el-form-item { margin-right: 8px; margin-bottom: 8px; }
-
-.button-group { display: flex; gap: 5px; margin-top: 2px; }
-.action-row { margin-top: 12px; }
-
-.pagination-section {
-  display: flex;
-  justify-content: flex-end;
-  align-items: center;
-  gap: 20px;
-  margin-top: 20px;
-}
-.pagination-section .total { color: #606266; font-size: 13px; }
-</style>

+ 0 - 127
pc/src/views/RuleGroupView.vue

@@ -1,127 +0,0 @@
-<template>
-  <div class="rule-group-view">
-    <el-card>
-      <div slot="header" class="header">
-        <span class="title">客群规则任务详情</span>
-        <span v-if="current" class="rg-id">编号:{{ current.id }}</span>
-      </div>
-
-      <div v-if="current" class="section">
-        <div class="section-title">基本信息</div>
-        <el-descriptions :column="2" border>
-          <el-descriptions-item label="规则组编号">{{ current.id }}</el-descriptions-item>
-          <el-descriptions-item label="规则组名称">{{ current.name }}</el-descriptions-item>
-          <el-descriptions-item label="规则组类型">{{ typeLabel(current.type) }}</el-descriptions-item>
-          <el-descriptions-item label="规则逻辑">
-            <el-tag size="small" :type="current.logic === 'AND' ? '' : 'warning'">
-              {{ current.logic === 'AND' ? '且(AND)' : '或(OR)' }}
-            </el-tag>
-          </el-descriptions-item>
-          <el-descriptions-item label="状态">
-            <el-tag :type="current.status === 'online' ? 'success' : 'info'" size="small">
-              {{ current.status === 'online' ? '已上线' : '新建' }}
-            </el-tag>
-          </el-descriptions-item>
-          <el-descriptions-item label="规则数量">{{ (current.rules || []).length }}</el-descriptions-item>
-          <el-descriptions-item label="创建人">{{ current.creator }}</el-descriptions-item>
-          <el-descriptions-item label="创建时间">{{ current.createdAt }}</el-descriptions-item>
-          <el-descriptions-item label="最后更新人">{{ current.updater }}</el-descriptions-item>
-          <el-descriptions-item label="最后更新时间">{{ current.updatedAt }}</el-descriptions-item>
-          <el-descriptions-item label="备注说明" :span="2">
-            {{ current.description || '-' }}
-          </el-descriptions-item>
-        </el-descriptions>
-      </div>
-
-      <div v-if="current" class="section">
-        <div class="section-title">
-          规则组配置
-          <span class="section-tip">规则之间使用「{{ current.logic === 'AND' ? '且' : '或' }}」逻辑组合</span>
-        </div>
-        <el-table :data="current.rules || []" border style="width: 100%" empty-text="暂无规则">
-          <el-table-column prop="metaCode" label="元规则编号" width="110" align="center" />
-          <el-table-column label="元规则名称" min-width="180">
-            <template slot-scope="scope">{{ metaName(scope.row.metaCode) }}</template>
-          </el-table-column>
-          <el-table-column prop="ruleName" label="规则名称" min-width="160" show-overflow-tooltip />
-          <el-table-column label="规则描述" min-width="240" show-overflow-tooltip>
-            <template slot-scope="scope">{{ buildDesc(scope.row) }}</template>
-          </el-table-column>
-          <el-table-column label="规则参数" min-width="220" show-overflow-tooltip>
-            <template slot-scope="scope">
-              <span class="params-text">{{ buildParam(scope.row) }}</span>
-            </template>
-          </el-table-column>
-        </el-table>
-      </div>
-
-      <div class="footer">
-        <el-button @click="$router.push('/customization/hsbc/rule-groups')">返回</el-button>
-      </div>
-    </el-card>
-  </div>
-</template>
-
-<script>
-import ruleGroupStore from '@/store/ruleGroupStore'
-import {
-  GROUP_TYPES,
-  getMetaRule,
-  buildRuleDescription,
-  buildRuleParams
-} from '@/config/metaRules'
-
-export default {
-  name: 'RuleGroupView',
-  data() {
-    return { current: null }
-  },
-  created() {
-    const target = ruleGroupStore.getById(this.$route.params.id)
-    if (!target) {
-      this.$message.error('规则组不存在')
-      this.$router.replace('/customization/hsbc/rule-groups')
-      return
-    }
-    this.current = target
-  },
-  methods: {
-    typeLabel(type) {
-      const item = GROUP_TYPES.find((g) => g.value === type)
-      return item ? item.label : type
-    },
-    metaName(code) {
-      const m = getMetaRule(code)
-      return m ? m.name : code
-    },
-    buildDesc(rule) {
-      return buildRuleDescription(rule.metaCode, rule.config) || '-'
-    },
-    buildParam(rule) {
-      return buildRuleParams(rule.metaCode, rule.config) || '-'
-    }
-  }
-}
-</script>
-
-<style scoped>
-.rule-group-view { padding: 0; }
-.header { display: flex; align-items: center; justify-content: space-between; }
-.title { font-size: 16px; font-weight: 600; color: #409EFF; }
-.rg-id { color: #909399; font-size: 13px; }
-
-.section { margin-bottom: 24px; }
-.section-title {
-  font-size: 14px; font-weight: 600; color: #303133;
-  background: #f5f7fa; padding: 10px 14px; border-radius: 4px;
-  margin-bottom: 16px;
-}
-.section-tip { font-weight: normal; color: #909399; margin-left: 12px; font-size: 12px; }
-
-.params-text {
-  font-family: 'Menlo', 'Consolas', monospace;
-  color: #606266; font-size: 12px;
-}
-
-.footer { text-align: center; margin-top: 24px; padding-top: 16px; border-top: 1px solid #ebeef5; }
-</style>

+ 0 - 235
pc/src/views/SmsSendRecord.vue

@@ -1,235 +0,0 @@
-<template>
-  <div class="sms-send-record">
-    <el-card>
-      <!-- 搜索区域 -->
-      <div slot="header" class="search-section">
-        <div class="search-row">
-          <el-form :model="searchForm" label-width="100px" inline>
-            <el-form-item label="接收手机号:">
-              <el-input v-model="searchForm.mobile" placeholder="请输入" clearable style="width: 180px" />
-            </el-form-item>
-            <el-form-item label="模板名称:">
-              <el-select v-model="searchForm.templateId" clearable placeholder="全部" style="width: 200px">
-                <el-option
-                  v-for="item in templateOptions"
-                  :key="item.id"
-                  :label="item.name"
-                  :value="item.id"
-                />
-              </el-select>
-            </el-form-item>
-            <el-form-item label="发送状态:">
-              <el-select v-model="searchForm.status" clearable placeholder="全部" style="width: 120px">
-                <el-option label="成功" value="success" />
-                <el-option label="失败" value="failed" />
-              </el-select>
-            </el-form-item>
-            <el-form-item label="发送时间:">
-              <el-date-picker
-                v-model="searchForm.sentRange"
-                type="daterange"
-                range-separator="至"
-                start-placeholder="开始日期"
-                end-placeholder="结束日期"
-                value-format="yyyy-MM-dd"
-                style="width: 260px"
-              />
-            </el-form-item>
-            <div class="button-group">
-              <el-button type="primary" @click="handleSearch">查询</el-button>
-              <el-button @click="handleReset">重置</el-button>
-            </div>
-          </el-form>
-        </div>
-      </div>
-
-      <!-- 表格 -->
-      <el-table :data="pagedData" stripe style="width: 100%" border>
-        <el-table-column prop="id" label="发送 ID" width="180" />
-        <el-table-column prop="mobile" label="接收手机号" width="140" />
-        <el-table-column prop="templateName" label="模板名称" min-width="180" show-overflow-tooltip />
-        <el-table-column prop="content" label="发送内容" min-width="320" show-overflow-tooltip />
-        <el-table-column prop="productName" label="关联权益商品" width="160" show-overflow-tooltip />
-        <el-table-column label="发送状态" width="100">
-          <template slot-scope="scope">
-            <el-tag :type="scope.row.status === 'success' ? 'success' : 'danger'" size="small">
-              {{ scope.row.status === 'success' ? '成功' : '失败' }}
-            </el-tag>
-          </template>
-        </el-table-column>
-<el-table-column prop="sentAt" label="发送时间" width="160" />
-      </el-table>
-
-      <!-- 分页 -->
-      <div class="pagination-section">
-        <span class="total">共 {{ filteredData.length }} 条</span>
-        <el-pagination
-          layout="prev, pager, next, sizes, jumper"
-          :current-page.sync="page.current"
-          :page-size.sync="page.size"
-          :page-sizes="[10, 20, 50]"
-          :total="filteredData.length"
-        />
-      </div>
-    </el-card>
-  </div>
-</template>
-
-<script>
-import smsTemplateStore from '@/store/smsTemplateStore'
-
-export default {
-  name: 'SmsSendRecord',
-  data() {
-    return {
-      searchForm: {
-        mobile: '',
-        templateId: '',
-        status: '',
-        sentRange: []
-      },
-      page: { current: 1, size: 10 },
-      records: [
-        {
-          id: 'SMS20260510001',
-          mobile: '138****5821',
-          templateId: 'SMS_TPL_001',
-          templateName: '通用权益支付成功模板',
-          content: '【银行】您购买的星巴克电子券已支付成功,卡号:8821****3340,卡密:xc**29,有效期至2026-08-31,详情:https://m.bank.com/c/8821',
-          productName: '星巴克电子券',
-          status: 'success',
-          failReason: '',
-          sentAt: '2026-05-10 14:08:21'
-        },
-        {
-          id: 'SMS20260510002',
-          mobile: '139****0173',
-          templateId: 'SMS_TPL_002',
-          templateName: '券码类短信模板',
-          content: '【银行】尊敬的客户,您的肯德基早餐券购买成功,券码为KFC8821X,请于2026-06-30前使用。',
-          productName: '肯德基早餐券',
-          status: 'success',
-          failReason: '',
-          sentAt: '2026-05-10 15:22:09'
-        },
-        {
-          id: 'SMS20260510003',
-          mobile: '186****4502',
-          templateId: 'SMS_TPL_001',
-          templateName: '通用权益支付成功模板',
-          content: '【银行】您购买的爱奇艺会员月卡已支付成功,卡号:QY002****9981,卡密:iq**aa,有效期至2026-06-10,详情:https://m.bank.com/c/qy002',
-          productName: '爱奇艺会员月卡',
-          status: 'failed',
-          failReason: '号码格式错误',
-          sentAt: '2026-05-10 16:01:55'
-        },
-        {
-          id: 'SMS20260509001',
-          mobile: '135****7714',
-          templateId: 'SMS_TPL_003',
-          templateName: '实物类短信模板',
-          content: '【银行】您购买的小米保温杯已受理,我们将尽快安排发货,感谢您的支持。',
-          productName: '小米保温杯',
-          status: 'success',
-          failReason: '',
-          sentAt: '2026-05-09 10:45:33'
-        },
-        {
-          id: 'SMS20260509002',
-          mobile: '137****3398',
-          templateId: 'SMS_TPL_002',
-          templateName: '券码类短信模板',
-          content: '【银行】尊敬的客户,您的麦当劳套餐券购买成功,券码为MD0089Z,请于2026-07-31前使用。',
-          productName: '麦当劳套餐券',
-          status: 'failed',
-          failReason: '运营商通道拥塞',
-          sentAt: '2026-05-09 18:12:48'
-        },
-        {
-          id: 'SMS20260508001',
-          mobile: '152****6024',
-          templateId: 'SMS_TPL_001',
-          templateName: '通用权益支付成功模板',
-          content: '【银行】您购买的腾讯视频季卡已支付成功,卡号:TX007****4412,卡密:vp**xz,有效期至2026-08-08,详情:https://m.bank.com/c/tx007',
-          productName: '腾讯视频季卡',
-          status: 'success',
-          failReason: '',
-          sentAt: '2026-05-08 09:33:12'
-        },
-        {
-          id: 'SMS20260508002',
-          mobile: '188****1156',
-          templateId: 'SMS_TPL_003',
-          templateName: '实物类短信模板',
-          content: '【银行】您购买的网易严选毛巾已受理,我们将尽快安排发货,感谢您的支持。',
-          productName: '网易严选毛巾',
-          status: 'success',
-          failReason: '',
-          sentAt: '2026-05-08 13:47:50'
-        },
-        {
-          id: 'SMS20260507001',
-          mobile: '177****8809',
-          templateId: 'SMS_TPL_002',
-          templateName: '券码类短信模板',
-          content: '【银行】尊敬的客户,您的喜茶饮品券购买成功,券码为HC8821D,请于2026-06-15前使用。',
-          productName: '喜茶饮品券',
-          status: 'success',
-          failReason: '',
-          sentAt: '2026-05-07 11:09:22'
-        }
-      ]
-    }
-  },
-  computed: {
-    templateOptions() {
-      return smsTemplateStore.state.list.map(item => ({ id: item.id, name: item.name }))
-    },
-    filteredData() {
-      const { mobile, templateId, status, sentRange } = this.searchForm
-      return this.records.filter(row => {
-        if (mobile && !row.mobile.includes(mobile)) return false
-        if (templateId && row.templateId !== templateId) return false
-        if (status && row.status !== status) return false
-        if (sentRange && sentRange.length === 2) {
-          const day = row.sentAt.slice(0, 10)
-          if (day < sentRange[0] || day > sentRange[1]) return false
-        }
-        return true
-      })
-    },
-    pagedData() {
-      const start = (this.page.current - 1) * this.page.size
-      return this.filteredData.slice(start, start + this.page.size)
-    }
-  },
-  methods: {
-    handleSearch() {
-      this.page.current = 1
-    },
-    handleReset() {
-      this.searchForm = { mobile: '', templateId: '', status: '', sentRange: [] }
-      this.page.current = 1
-    }
-  }
-}
-</script>
-
-<style scoped>
-.sms-send-record { padding: 0; }
-
-.search-section { display: flex; flex-direction: column; }
-.search-row .el-form { display: flex; flex-wrap: wrap; gap: 8px 0; }
-.search-row .el-form-item { margin-right: 8px; margin-bottom: 8px; }
-
-.button-group { display: flex; gap: 5px; margin-top: 2px; }
-
-.pagination-section {
-  display: flex;
-  justify-content: flex-end;
-  align-items: center;
-  gap: 20px;
-  margin-top: 20px;
-}
-.pagination-section .total { color: #606266; font-size: 13px; }
-</style>

+ 0 - 262
pc/src/views/SmsTemplateList.vue

@@ -1,262 +0,0 @@
-<template>
-  <div class="sms-template-list">
-    <el-card>
-      <!-- 搜索区域 -->
-      <div slot="header" class="search-section">
-        <div class="search-row">
-          <el-form :model="searchForm" label-width="100px" inline>
-            <el-form-item label="模板名称:">
-              <el-input v-model="searchForm.name" placeholder="请输入" clearable style="width: 180px" />
-            </el-form-item>
-            <el-form-item label="模板 ID:">
-              <el-input v-model="searchForm.id" placeholder="请输入" clearable style="width: 180px" />
-            </el-form-item>
-            <div class="button-group">
-              <el-button type="primary" @click="handleSearch">查询</el-button>
-              <el-button @click="handleReset">重置</el-button>
-            </div>
-          </el-form>
-        </div>
-        <div class="action-row">
-          <el-button type="primary" icon="el-icon-plus" @click="handleCreate">新建短信模板</el-button>
-        </div>
-      </div>
-
-      <!-- 表格 -->
-      <el-table :data="pagedData" stripe style="width: 100%" border>
-        <el-table-column label="操作" width="140" fixed="left">
-          <template slot-scope="scope">
-            <el-button type="text" size="small" @click="handleEdit(scope.row)">编辑</el-button>
-            <el-button
-              type="text"
-              size="small"
-              style="color: #F56C6C"
-              @click="handleDelete(scope.row)"
-            >删除</el-button>
-          </template>
-        </el-table-column>
-        <el-table-column prop="id" label="模板 ID" width="160" />
-        <el-table-column prop="name" label="模板名称" min-width="200" show-overflow-tooltip />
-        <el-table-column prop="content" label="模板内容" min-width="320" show-overflow-tooltip />
-        <el-table-column prop="creator" label="创建人" width="100" />
-        <el-table-column prop="createdAt" label="创建时间" width="160" />
-        <el-table-column prop="updater" label="最后更新人" width="110" />
-        <el-table-column prop="updatedAt" label="更新时间" width="160" />
-      </el-table>
-
-      <!-- 分页 -->
-      <div class="pagination-section">
-        <span class="total">共 {{ filteredData.length }} 条</span>
-        <el-pagination
-          layout="prev, pager, next, sizes, jumper"
-          :current-page.sync="page.current"
-          :page-size.sync="page.size"
-          :page-sizes="[10, 20, 50]"
-          :total="filteredData.length"
-        />
-      </div>
-    </el-card>
-
-    <!-- 添加/编辑弹窗 -->
-    <el-dialog
-      :title="dialog.mode === 'create' ? '新建短信模板' : '编辑短信模板'"
-      :visible.sync="dialog.visible"
-      width="600px"
-      :close-on-click-modal="false"
-      @closed="resetForm"
-    >
-      <el-form
-        ref="form"
-        :model="form"
-        :rules="rules"
-        label-width="100px"
-      >
-        <el-form-item label="模板名称" prop="name">
-          <el-input
-            v-model="form.name"
-            maxlength="40"
-            show-word-limit
-            placeholder="请输入模板名称"
-          />
-        </el-form-item>
-        <el-form-item label="模板内容" prop="content">
-          <el-input
-            v-model="form.content"
-            type="textarea"
-            :rows="6"
-            maxlength="500"
-            show-word-limit
-            placeholder="请输入模板内容,支持下方占位符"
-          />
-          <div class="placeholder-tip">
-            <div class="placeholder-tip__title">占位符说明:</div>
-            <div class="placeholder-tip__item">
-              <span class="placeholder-tip__label">商品名称:</span>
-              <code>${productName}</code>
-            </div>
-            <div class="placeholder-tip__item">
-              <span class="placeholder-tip__label">结束日期:</span>
-              <code>${expireDate}</code>
-            </div>
-            <div class="placeholder-tip__item">
-              <span class="placeholder-tip__label">卡号:</span>
-              <code>${exchangeNum}</code>
-            </div>
-            <div class="placeholder-tip__item">
-              <span class="placeholder-tip__label">卡密:</span>
-              <code>${exchangePwd}</code>
-            </div>
-            <div class="placeholder-tip__item">
-              <span class="placeholder-tip__label">券码路径:</span>
-              <code>${qrCodeDownPaths}</code>
-            </div>
-          </div>
-        </el-form-item>
-      </el-form>
-      <div slot="footer">
-        <el-button @click="dialog.visible = false">取消</el-button>
-        <el-button type="primary" @click="handleSave">保存</el-button>
-      </div>
-    </el-dialog>
-  </div>
-</template>
-
-<script>
-import smsTemplateStore from '@/store/smsTemplateStore'
-
-export default {
-  name: 'SmsTemplateList',
-  data() {
-    return {
-      searchForm: {
-        name: '',
-        id: ''
-      },
-      page: { current: 1, size: 10 },
-      dialog: {
-        visible: false,
-        mode: 'create',
-        editingId: ''
-      },
-      form: {
-        name: '',
-        content: ''
-      },
-      rules: {
-        name: [
-          { required: true, message: '请输入模板名称', trigger: 'blur' },
-          { max: 40, message: '不超过 40 字', trigger: 'blur' }
-        ],
-        content: [
-          { required: true, message: '请输入模板内容', trigger: 'blur' },
-          { max: 500, message: '不超过 500 字', trigger: 'blur' }
-        ]
-      }
-    }
-  },
-  computed: {
-    filteredData() {
-      return smsTemplateStore.query(this.searchForm)
-    },
-    pagedData() {
-      const start = (this.page.current - 1) * this.page.size
-      return this.filteredData.slice(start, start + this.page.size)
-    }
-  },
-  methods: {
-    handleSearch() {
-      this.page.current = 1
-    },
-    handleReset() {
-      this.searchForm = { name: '', id: '' }
-      this.page.current = 1
-    },
-    handleCreate() {
-      this.dialog.mode = 'create'
-      this.dialog.editingId = ''
-      this.form = { name: '', content: '' }
-      this.dialog.visible = true
-    },
-    handleEdit(row) {
-      this.dialog.mode = 'edit'
-      this.dialog.editingId = row.id
-      this.form = { name: row.name, content: row.content }
-      this.dialog.visible = true
-    },
-    handleDelete(row) {
-      this.$confirm(`确定要删除模板「${row.name}」吗?删除后无法恢复`, '提示', {
-        confirmButtonText: '确定',
-        cancelButtonText: '取消',
-        type: 'warning'
-      }).then(() => {
-        smsTemplateStore.remove(row.id)
-        this.$message.success('删除成功')
-      }).catch(() => {})
-    },
-    handleSave() {
-      this.$refs.form.validate(valid => {
-        if (!valid) return
-        if (this.dialog.mode === 'create') {
-          smsTemplateStore.create({ name: this.form.name, content: this.form.content })
-          this.$message.success('新建成功')
-        } else {
-          smsTemplateStore.update(this.dialog.editingId, { name: this.form.name, content: this.form.content })
-          this.$message.success('保存成功')
-        }
-        this.dialog.visible = false
-      })
-    },
-    resetForm() {
-      if (this.$refs.form) this.$refs.form.clearValidate()
-      this.form = { name: '', content: '' }
-      this.dialog.editingId = ''
-    }
-  }
-}
-</script>
-
-<style scoped>
-.sms-template-list { padding: 0; }
-
-.search-section { display: flex; flex-direction: column; }
-.search-row .el-form { display: flex; flex-wrap: wrap; gap: 8px 0; }
-.search-row .el-form-item { margin-right: 8px; margin-bottom: 8px; }
-
-.button-group { display: flex; gap: 5px; margin-top: 2px; }
-.action-row { margin-top: 12px; }
-
-.pagination-section {
-  display: flex;
-  justify-content: flex-end;
-  align-items: center;
-  gap: 20px;
-  margin-top: 20px;
-}
-.pagination-section .total { color: #606266; font-size: 13px; }
-
-.placeholder-tip {
-  margin-top: 10px;
-  padding: 12px 14px;
-  background: #f5f7fa;
-  border: 1px dashed #dcdfe6;
-  border-radius: 4px;
-  font-size: 12px;
-  color: #606266;
-  line-height: 1.8;
-}
-.placeholder-tip__title {
-  font-weight: 500;
-  color: #303133;
-  margin-bottom: 4px;
-}
-.placeholder-tip__item { display: flex; align-items: center; gap: 4px; }
-.placeholder-tip__label { color: #606266; }
-.placeholder-tip code {
-  background: #fff;
-  padding: 1px 6px;
-  border: 1px solid #ebeef5;
-  border-radius: 3px;
-  color: #409EFF;
-  font-family: Menlo, Consolas, monospace;
-}
-</style>

+ 0 - 260
pc/src/views/TagEdit.vue

@@ -1,260 +0,0 @@
-<template>
-  <div class="tag-edit">
-    <el-card>
-      <div slot="header" class="header">
-        <span class="title">{{ isEdit ? '编辑标签' : '新建标签' }}</span>
-        <span v-if="isEdit && currentTag" class="tag-id">ID: {{ currentTag.id }}</span>
-      </div>
-
-      <!-- 基础信息 -->
-      <div class="section">
-        <div class="section-title">基础信息</div>
-        <el-form
-          ref="form"
-          :model="form"
-          :rules="rules"
-          label-width="100px"
-          style="max-width: 760px"
-        >
-          <el-form-item label="标签名称" prop="name">
-            <el-input v-model="form.name" placeholder="最长 40 字符" maxlength="40" show-word-limit />
-          </el-form-item>
-          <el-form-item label="标签描述" prop="description">
-            <el-input
-              v-model="form.description"
-              type="textarea"
-              :rows="3"
-              placeholder="便于识别标签用途,最长 200 字符"
-              maxlength="200"
-              show-word-limit
-            />
-          </el-form-item>
-          <el-form-item label="状态">
-            <el-switch v-model="form.status" active-text="启用" inactive-text="停用" />
-          </el-form-item>
-        </el-form>
-      </div>
-
-      <!-- 规则配置 -->
-      <div class="section">
-        <div class="section-title">规则配置</div>
-        <rule-builder v-model="form.groups" />
-      </div>
-
-      <!-- 操作按钮 -->
-      <div class="footer">
-        <el-button @click="handleCancel">取消</el-button>
-        <el-button type="primary" @click="handleSave">保存</el-button>
-      </div>
-    </el-card>
-  </div>
-</template>
-
-<script>
-import tagStore from '@/store/tagStore'
-import RuleBuilder from '@/components/tag/RuleBuilder.vue'
-import { PARAM_MAP, needValueInput } from '@/config/tagParams'
-
-export default {
-  name: 'TagEdit',
-  components: { RuleBuilder },
-  data() {
-    return {
-      form: {
-        name: '',
-        description: '',
-        status: true,
-        groups: []
-      },
-      rules: {
-        name: [
-          { required: true, message: '请输入标签名称', trigger: 'blur' },
-          { max: 40, message: '最长 40 字符', trigger: 'blur' }
-        ],
-        description: [
-          { max: 200, message: '最长 200 字符', trigger: 'blur' }
-        ]
-      },
-      currentTag: null,
-      initialSnapshot: ''
-    }
-  },
-  computed: {
-    isEdit() {
-      return !!this.$route.params.id
-    }
-  },
-  created() {
-    if (this.isEdit) {
-      const target = tagStore.getById(this.$route.params.id)
-      if (!target) {
-        this.$message.error('标签不存在')
-        this.$router.replace('/customization/hsbc/tags')
-        return
-      }
-      this.currentTag = target
-      this.form = {
-        name: target.name,
-        description: target.description,
-        status: target.status === 'enabled',
-        groups: JSON.parse(JSON.stringify(target.groups || []))
-      }
-    }
-    this.initialSnapshot = JSON.stringify(this.form)
-  },
-  methods: {
-    validateRules() {
-      if (!this.form.groups.length) {
-        this.$message.error('至少配置 1 个规则组')
-        return false
-      }
-      for (let i = 0; i < this.form.groups.length; i++) {
-        const group = this.form.groups[i]
-        if (!group.rules || !group.rules.length) {
-          this.$message.error(`规则组 ${i + 1} 至少配置 1 条规则`)
-          return false
-        }
-        for (let j = 0; j < group.rules.length; j++) {
-          const rule = group.rules[j]
-          const label = `规则组 ${i + 1} 第 ${j + 1} 条规则`
-          if (!rule.paramKey) {
-            this.$message.error(`${label}:请选择参数`)
-            return false
-          }
-          if (!rule.operator) {
-            this.$message.error(`${label}:请选择运算符`)
-            return false
-          }
-          if (needValueInput(rule.operator)) {
-            const param = PARAM_MAP[rule.paramKey]
-            // 相对日期:需要 N 值和单位
-            if (['withinLast', 'beforeLast'].includes(rule.operator)) {
-              if (rule.value === '' || rule.value === null || rule.value === undefined) {
-                this.$message.error(`${label}:请输入相对日期数值`)
-                return false
-              }
-            } else if (Array.isArray(rule.value)) {
-              if (!rule.value.length) {
-                this.$message.error(`${label}:请选择比较值`)
-                return false
-              }
-            } else if (rule.value === '' || rule.value === null || rule.value === undefined) {
-              this.$message.error(`${label}:请输入比较值`)
-              return false
-            }
-
-            if (rule.operator === 'between') {
-              if (rule.value2 === '' || rule.value2 === null || rule.value2 === undefined) {
-                this.$message.error(`${label}:请输入区间上限`)
-                return false
-              }
-              if (param && (param.type === 'BigDecimal' || param.type === 'List')
-                  && Number(rule.value) > Number(rule.value2)) {
-                this.$message.error(`${label}:区间下限不能大于上限`)
-                return false
-              }
-            }
-          }
-          // 时间窗口校验
-          if (param_hasHistory(rule.paramKey)) {
-            const tw = rule.timeWindow || {}
-            if (tw.mode === 'specificMonth' && !tw.month) {
-              this.$message.error(`${label}:请选择指定月份`)
-              return false
-            }
-            if (tw.mode === 'recentMonths') {
-              if (!tw.n || tw.n < 1 || tw.n > 12) {
-                this.$message.error(`${label}:近 N 个月 的 N 需在 1-12 之间`)
-                return false
-              }
-              if (tw.aggregator === 'atLeast') {
-                if (!tw.m || tw.m < 1 || tw.m > tw.n) {
-                  this.$message.error(`${label}:至少 M 个月 的 M 不能大于 N`)
-                  return false
-                }
-              }
-            }
-          }
-        }
-      }
-      return true
-    },
-    handleSave() {
-      this.$refs.form.validate((valid) => {
-        if (!valid) return
-        if (!this.validateRules()) return
-
-        const doSave = () => {
-          if (this.isEdit) {
-            tagStore.update(this.currentTag.id, this.form)
-            this.$message.success('保存成功')
-          } else {
-            tagStore.create(this.form)
-            this.$message.success('新建成功')
-          }
-          this.$router.push('/customization/hsbc/tags')
-        }
-
-        if (this.isEdit && this.currentTag && this.currentTag.referenceCount > 0) {
-          this.$confirm(
-            `该标签已被 ${this.currentTag.referenceCount} 个权益/活动引用,修改后将影响其客群匹配,确认保存?`,
-            '提示',
-            { type: 'warning' }
-          ).then(doSave).catch(() => {})
-        } else {
-          doSave()
-        }
-      })
-    },
-    handleCancel() {
-      const dirty = JSON.stringify(this.form) !== this.initialSnapshot
-      if (dirty) {
-        this.$confirm('未保存内容将丢失,确认退出?', '提示', { type: 'warning' })
-          .then(() => this.$router.push('/customization/hsbc/tags'))
-          .catch(() => {})
-      } else {
-        this.$router.push('/customization/hsbc/tags')
-      }
-    }
-  }
-}
-
-function param_hasHistory(key) {
-  const p = PARAM_MAP[key]
-  return p && p.hasHistory
-}
-</script>
-
-<style scoped>
-.tag-edit { padding: 0; }
-
-.header {
-  display: flex;
-  align-items: center;
-  gap: 16px;
-}
-.title { font-size: 16px; font-weight: 600; }
-.tag-id { color: #909399; font-size: 13px; }
-
-.section {
-  padding: 16px 0 20px;
-  border-bottom: 1px dashed #ebeef5;
-}
-.section:last-of-type { border-bottom: none; }
-
-.section-title {
-  font-size: 15px;
-  font-weight: 600;
-  color: #303133;
-  margin-bottom: 16px;
-  padding-left: 8px;
-  border-left: 3px solid #409EFF;
-}
-
-.footer {
-  display: flex;
-  justify-content: center;
-  gap: 16px;
-  padding-top: 20px;
-}
-</style>

+ 0 - 201
pc/src/views/TagList.vue

@@ -1,201 +0,0 @@
-<template>
-  <div class="tag-list">
-    <el-card>
-      <!-- 搜索区域 -->
-      <div slot="header" class="search-section">
-        <div class="search-row">
-          <el-form :model="searchForm" label-width="90px" inline>
-            <el-form-item label="标签名称:">
-              <el-input v-model="searchForm.name" placeholder="请输入" clearable style="width: 160px" />
-            </el-form-item>
-            <el-form-item label="标签 ID:">
-              <el-input v-model="searchForm.id" placeholder="请输入" clearable style="width: 180px" />
-            </el-form-item>
-            <el-form-item label="状态:">
-              <el-select v-model="searchForm.status" clearable placeholder="全部" style="width: 120px">
-                <el-option label="启用" value="enabled" />
-                <el-option label="停用" value="disabled" />
-              </el-select>
-            </el-form-item>
-            <el-form-item label="创建人:">
-              <el-input v-model="searchForm.creator" placeholder="请输入" clearable style="width: 140px" />
-            </el-form-item>
-            <el-form-item label="创建时间:">
-              <el-date-picker
-                v-model="searchForm.createdRange"
-                type="daterange"
-                range-separator="至"
-                start-placeholder="开始日期"
-                end-placeholder="结束日期"
-                value-format="yyyy-MM-dd"
-                style="width: 260px"
-              />
-            </el-form-item>
-            <div class="button-group">
-              <el-button type="primary" @click="handleSearch">查询</el-button>
-              <el-button @click="handleReset">重置</el-button>
-            </div>
-          </el-form>
-        </div>
-        <div class="action-row">
-          <el-button type="primary" icon="el-icon-plus" @click="handleCreate">新建标签</el-button>
-        </div>
-      </div>
-
-      <!-- 表格 -->
-      <el-table :data="pagedData" stripe style="width: 100%" border>
-        <el-table-column label="操作" width="200" fixed="left">
-          <template slot-scope="scope">
-            <el-button type="text" size="small" @click="handleView(scope.row)">查看</el-button>
-            <el-button type="text" size="small" @click="handleEdit(scope.row)">编辑</el-button>
-            <el-button
-              type="text"
-              size="small"
-              @click="handleToggle(scope.row)"
-            >{{ scope.row.status === 'enabled' ? '停用' : '启用' }}</el-button>
-            <el-button
-              type="text"
-              size="small"
-              style="color: #F56C6C"
-              @click="handleDelete(scope.row)"
-            >删除</el-button>
-          </template>
-        </el-table-column>
-        <el-table-column prop="id" label="标签 ID" width="160" />
-        <el-table-column prop="name" label="标签名称" min-width="160" show-overflow-tooltip />
-        <el-table-column prop="description" label="标签描述" min-width="200" show-overflow-tooltip />
-        <el-table-column label="规则摘要" width="110">
-          <template slot-scope="scope">
-            {{ ruleSummary(scope.row) }}
-          </template>
-        </el-table-column>
-        <el-table-column label="状态" width="90">
-          <template slot-scope="scope">
-            <el-tag :type="scope.row.status === 'enabled' ? 'success' : 'info'" size="small">
-              {{ scope.row.status === 'enabled' ? '启用' : '停用' }}
-            </el-tag>
-          </template>
-        </el-table-column>
-        <el-table-column prop="referenceCount" label="引用数" width="80" align="center" />
-        <el-table-column prop="creator" label="创建人" width="100" />
-        <el-table-column prop="createdAt" label="创建时间" width="160" />
-        <el-table-column prop="updater" label="最后更新人" width="110" />
-        <el-table-column prop="updatedAt" label="更新时间" width="160" />
-      </el-table>
-
-      <!-- 分页 -->
-      <div class="pagination-section">
-        <span class="total">共 {{ filteredData.length }} 条</span>
-        <el-pagination
-          layout="prev, pager, next, sizes, jumper"
-          :current-page.sync="page.current"
-          :page-size.sync="page.size"
-          :page-sizes="[10, 20, 50]"
-          :total="filteredData.length"
-        />
-      </div>
-    </el-card>
-  </div>
-</template>
-
-<script>
-import tagStore from '@/store/tagStore'
-
-export default {
-  name: 'TagList',
-  data() {
-    return {
-      searchForm: {
-        name: '',
-        id: '',
-        status: '',
-        creator: '',
-        createdRange: []
-      },
-      page: { current: 1, size: 10 }
-    }
-  },
-  computed: {
-    filteredData() {
-      return tagStore.query(this.searchForm)
-    },
-    pagedData() {
-      const start = (this.page.current - 1) * this.page.size
-      return this.filteredData.slice(start, start + this.page.size)
-    }
-  },
-  methods: {
-    ruleSummary(row) {
-      const g = (row.groups || []).length
-      const r = (row.groups || []).reduce((sum, gr) => sum + (gr.rules ? gr.rules.length : 0), 0)
-      return `${g} 组 / ${r} 条`
-    },
-    handleSearch() {
-      this.page.current = 1
-    },
-    handleReset() {
-      this.searchForm = { name: '', id: '', status: '', creator: '', createdRange: [] }
-      this.page.current = 1
-    },
-    handleCreate() {
-      this.$router.push('/customization/hsbc/tags/add')
-    },
-    handleView(row) {
-      this.$router.push(`/customization/hsbc/tags/view/${row.id}`)
-    },
-    handleEdit(row) {
-      this.$router.push(`/customization/hsbc/tags/edit/${row.id}`)
-    },
-    handleToggle(row) {
-      if (row.status === 'enabled') {
-        this.$confirm('停用后,引用该标签的业务将视为该客群不命中,确认停用?', '提示', { type: 'warning' })
-          .then(() => {
-            tagStore.toggleStatus(row.id)
-            this.$message.success('已停用')
-          }).catch(() => {})
-      } else {
-        tagStore.toggleStatus(row.id)
-        this.$message.success('已启用')
-      }
-    },
-    handleDelete(row) {
-      if (row.referenceCount > 0) {
-        this.$alert(
-          `该标签已被 ${row.referenceCount} 个权益/活动引用,请先在对应业务中解除引用后再删除`,
-          '无法删除',
-          { confirmButtonText: '我知道了', type: 'warning' }
-        )
-        return
-      }
-      this.$confirm(`确定要删除标签「${row.name}」吗?删除后无法恢复`, '提示', {
-        confirmButtonText: '确定',
-        cancelButtonText: '取消',
-        type: 'warning'
-      }).then(() => {
-        tagStore.remove(row.id)
-        this.$message.success('删除成功')
-      }).catch(() => {})
-    }
-  }
-}
-</script>
-
-<style scoped>
-.tag-list { padding: 0; }
-
-.search-section { display: flex; flex-direction: column; }
-.search-row .el-form { display: flex; flex-wrap: wrap; gap: 8px 0; }
-.search-row .el-form-item { margin-right: 8px; margin-bottom: 8px; }
-
-.button-group { display: flex; gap: 5px; margin-top: 2px; }
-.action-row { margin-top: 12px; }
-
-.pagination-section {
-  display: flex;
-  justify-content: flex-end;
-  align-items: center;
-  gap: 20px;
-  margin-top: 20px;
-}
-.pagination-section .total { color: #606266; font-size: 13px; }
-</style>

+ 0 - 104
pc/src/views/TagView.vue

@@ -1,104 +0,0 @@
-<template>
-  <div class="tag-view">
-    <el-card v-if="tag">
-      <div slot="header" class="header">
-        <span class="title">查看标签</span>
-        <span class="tag-id">ID: {{ tag.id }}</span>
-      </div>
-
-      <!-- 基础信息 -->
-      <div class="section">
-        <div class="section-title">基础信息</div>
-        <el-descriptions :column="2" border size="medium" label-class-name="desc-label">
-          <el-descriptions-item label="标签名称">{{ tag.name }}</el-descriptions-item>
-          <el-descriptions-item label="状态">
-            <el-tag :type="tag.status === 'enabled' ? 'success' : 'info'" size="small">
-              {{ tag.status === 'enabled' ? '启用' : '停用' }}
-            </el-tag>
-          </el-descriptions-item>
-          <el-descriptions-item label="标签描述" :span="2">
-            {{ tag.description || '-' }}
-          </el-descriptions-item>
-          <el-descriptions-item label="创建人">{{ tag.creator }}</el-descriptions-item>
-          <el-descriptions-item label="创建时间">{{ tag.createdAt }}</el-descriptions-item>
-          <el-descriptions-item label="最后更新人">{{ tag.updater }}</el-descriptions-item>
-          <el-descriptions-item label="更新时间">{{ tag.updatedAt }}</el-descriptions-item>
-          <el-descriptions-item label="被引用次数">{{ tag.referenceCount }}</el-descriptions-item>
-          <el-descriptions-item label="规则摘要">{{ ruleSummary }}</el-descriptions-item>
-        </el-descriptions>
-      </div>
-
-      <!-- 规则(只读) -->
-      <div class="section">
-        <div class="section-title">规则配置</div>
-        <rule-builder :value="tag.groups" readonly />
-      </div>
-
-      <div class="footer">
-        <el-button @click="handleBack">返回</el-button>
-      </div>
-    </el-card>
-
-    <el-empty v-else description="标签不存在" />
-  </div>
-</template>
-
-<script>
-import tagStore from '@/store/tagStore'
-import RuleBuilder from '@/components/tag/RuleBuilder.vue'
-
-export default {
-  name: 'TagView',
-  components: { RuleBuilder },
-  data() {
-    return { tag: null }
-  },
-  computed: {
-    ruleSummary() {
-      if (!this.tag) return ''
-      const g = (this.tag.groups || []).length
-      const r = (this.tag.groups || []).reduce((sum, gr) => sum + (gr.rules ? gr.rules.length : 0), 0)
-      return `${g} 组 / ${r} 条`
-    }
-  },
-  created() {
-    this.tag = tagStore.getById(this.$route.params.id)
-  },
-  methods: {
-    handleBack() {
-      this.$router.push('/customization/hsbc/tags')
-    }
-  }
-}
-</script>
-
-<style scoped>
-.tag-view { padding: 0; }
-
-.header { display: flex; align-items: center; gap: 16px; }
-.title { font-size: 16px; font-weight: 600; }
-.tag-id { color: #909399; font-size: 13px; }
-
-.section {
-  padding: 16px 0 20px;
-  border-bottom: 1px dashed #ebeef5;
-}
-.section:last-of-type { border-bottom: none; }
-
-.section-title {
-  font-size: 15px;
-  font-weight: 600;
-  color: #303133;
-  margin-bottom: 16px;
-  padding-left: 8px;
-  border-left: 3px solid #409EFF;
-}
-
-.footer {
-  display: flex;
-  justify-content: center;
-  padding-top: 20px;
-}
-
-::v-deep .desc-label { width: 120px; }
-</style>

+ 0 - 824
pc/src/views/ViewEquityProduct.vue

@@ -1,824 +0,0 @@
-<template>
-  <div class="view-equity-product">
-    <el-card>
-      <div slot="header" class="header-title">
-        <span>查看权益</span>
-      </div>
-
-      <!-- 步骤条 -->
-      <el-steps :active="activeStep" simple style="margin-bottom: 20px;">
-        <el-step title="填写权益信息" icon="el-icon-info"></el-step>
-        <el-step title="设置规则" icon="el-icon-setting"></el-step>
-        <el-step title="更多设置" icon="el-icon-monitor"></el-step>
-      </el-steps>
-
-      <!-- 步骤内容 -->
-      <div v-show="activeStep === 0">
-        <!-- 基本信息 -->
-        <el-card shadow="never" class="section-card">
-          <div slot="header" class="section-title">基本信息</div>
-          <el-form :model="form" label-width="120px" label-position="left">
-            <el-row :gutter="20">
-              <el-col :span="8">
-                <el-form-item label="权益来源">
-                  <el-radio-group v-model="form.source" disabled>
-                    <el-radio label="银行自有"></el-radio>
-                    <el-radio label="荣数商品"></el-radio>
-                    <el-radio label="第三方"></el-radio>
-                  </el-radio-group>
-                </el-form-item>
-              </el-col>
-              <el-col :span="8" v-if="form.source === '荣数商品'">
-                <el-form-item label="选择权益">
-                  <el-input :value="form.rongshuProduct" readonly></el-input>
-                </el-form-item>
-              </el-col>
-              <el-col :span="8" v-if="form.source === '银行自有'">
-                <el-form-item label="权益类型">
-                  <el-radio-group v-model="form.productType" disabled>
-                    <el-radio label="券码"></el-radio>
-                  </el-radio-group>
-                </el-form-item>
-              </el-col>
-              <el-col :span="8" v-if="form.source === '银行自有'">
-                <el-form-item label="商户名称">
-                  <el-input :value="form.merchantName" readonly></el-input>
-                </el-form-item>
-              </el-col>
-            </el-row>
-            <el-row :gutter="20">
-              <el-col :span="8" v-if="form.source === '第三方'">
-                <el-form-item label="外部权益链接">
-                  <el-select v-model="form.externalLink" placeholder="请选择外部权益链接" disabled>
-                    <el-option label="太古地产" value="太古地产"></el-option>
-                    <el-option label="龙腾" value="龙腾"></el-option>
-                    <el-option label="h5链接" value="h5链接"></el-option>
-                    <el-option label="小程序链接" value="小程序链接"></el-option>
-                  </el-select>
-                </el-form-item>
-              </el-col>
-              <el-col :span="8" v-if="form.source === '第三方' && form.externalLink === 'h5链接'" :gutter="20">
-                <el-form-item label="h5链接">
-                  <el-input v-model="form.customLink" placeholder="请输入h5链接" readonly></el-input>
-                </el-form-item>
-              </el-col>
-              <el-col :span="8" v-if="form.source === '第三方'">
-                <el-form-item label-width="140px">
-                  <span slot="label" style="white-space: nowrap;">是否有详情页<el-tooltip content="没有详情页时直接从首页/列表页跳转" placement="top"><i class="el-icon-question tip-icon"></i></el-tooltip></span>
-                  <el-radio-group v-model="form.externalJumpMethod" disabled>
-                    <el-radio-button label="没有"></el-radio-button>
-                    <el-radio-button label="有"></el-radio-button>
-                  </el-radio-group>
-                </el-form-item>
-              </el-col>
-            </el-row>
-            <el-row :gutter="20" v-if="form.source === '第三方' && form.externalLink === '小程序链接'">
-              <el-col :span="8">
-                <el-form-item label="小程序appid">
-                  <el-input v-model="form.miniAppId" readonly></el-input>
-                </el-form-item>
-              </el-col>
-              <el-col :span="8">
-                <el-form-item label="小程序页面地址" label-width="140px">
-                  <el-input v-model="form.miniAppPath" readonly></el-input>
-                </el-form-item>
-              </el-col>
-            </el-row>
-
-            <template v-if="form.source === '银行自有' || form.source === '荣数商品'">
-              <el-row :gutter="20" v-if="form.source === '荣数商品'">
-                <el-col :span="8">
-                  <el-form-item label="库存数">
-                    <el-input :value="form.stockCount" readonly></el-input>
-                  </el-form-item>
-                </el-col>
-              </el-row>
-              <el-row :gutter="20">
-                <el-col :span="8">
-                  <el-form-item label="权益名称">
-                    <el-input v-model="form.equityName" readonly></el-input>
-                  </el-form-item>
-                </el-col>
-                <el-col :span="8">
-                  <el-form-item label="权益副标题">
-                    <el-input v-model="form.subTitle" readonly></el-input>
-                  </el-form-item>
-                </el-col>
-              </el-row>
-              <el-row :gutter="20">
-                <el-col :span="8">
-                  <el-form-item>
-                    <span slot="label">权益图标
-                      <el-tooltip content="展示在权益首页和权益列表页" placement="top">
-                        <i class="el-icon-question tip-icon"></i>
-                      </el-tooltip>
-                    </span>
-                    <div v-if="form.listIcon && form.listIcon.length" class="image-preview">
-                      <img :src="form.listIcon[0]" class="preview-img" />
-                    </div>
-                    <span v-else class="no-data">暂无图片</span>
-                  </el-form-item>
-                </el-col>
-              </el-row>
-            </template>
-
-            <template v-else-if="form.source === '第三方'">
-              <el-row :gutter="20">
-                <el-col :span="8">
-                  <el-form-item label="权益名称">
-                    <el-input v-model="form.equityName" readonly></el-input>
-                  </el-form-item>
-                </el-col>
-                <el-col :span="8">
-                  <el-form-item label="权益副标题">
-                    <el-input v-model="form.subTitle" readonly></el-input>
-                  </el-form-item>
-                </el-col>
-              </el-row>
-              <el-row :gutter="20" v-if="form.externalJumpMethod === '没有'">
-                <el-col :span="8">
-                  <el-form-item>
-                    <span slot="label">权益图标
-                      <el-tooltip content="展示在权益首页和权益列表页" placement="top">
-                        <i class="el-icon-question tip-icon"></i>
-                      </el-tooltip>
-                    </span>
-                    <div v-if="form.listIcon && form.listIcon.length" class="image-preview">
-                      <img :src="form.listIcon[0]" class="preview-img" />
-                    </div>
-                    <span v-else class="no-data">暂无图片</span>
-                  </el-form-item>
-                </el-col>
-              </el-row>
-              <el-row v-if="form.externalJumpMethod === '有'" :gutter="20">
-                <el-col :span="8">
-                  <el-form-item>
-                    <span slot="label">权益图标
-                      <el-tooltip content="展示在权益首页和权益列表页" placement="top">
-                        <i class="el-icon-question tip-icon"></i>
-                      </el-tooltip>
-                    </span>
-                    <div v-if="form.listIcon && form.listIcon.length" class="image-preview">
-                      <img :src="form.listIcon[0]" class="preview-img" />
-                    </div>
-                    <span v-else class="no-data">暂无图片</span>
-                  </el-form-item>
-                </el-col>
-              </el-row>
-            </template>
-
-            <el-row :gutter="20">
-              <el-col :span="8">
-                <el-form-item label="SIP商品编号">
-                  <el-input v-model="form.skuNo" readonly></el-input>
-                </el-form-item>
-              </el-col>
-            </el-row>
-          </el-form>
-        </el-card>
-
-        <!-- 详情信息 -->
-        <el-card v-if="shouldShowDetailCard" shadow="never" class="section-card">
-          <div slot="header" class="section-title">详情信息</div>
-          <el-form :model="form" label-width="120px" label-position="left">
-            <el-row :gutter="20">
-              <el-col :span="8">
-                <el-form-item label="权益主图">
-                  <div v-if="form.mainImage && form.mainImage.length" class="image-preview">
-                    <img :src="form.mainImage[0]" class="preview-img" />
-                  </div>
-                  <span v-else class="no-data">暂无图片</span>
-                </el-form-item>
-              </el-col>
-            </el-row>
-            <el-row :gutter="20">
-              <el-col :span="12">
-                <el-form-item label="权益介绍">
-                  <el-input
-                    v-model="form.introduction"
-                    type="textarea"
-                    :rows="4"
-                    readonly
-                  ></el-input>
-                </el-form-item>
-              </el-col>
-            </el-row>
-            <el-row :gutter="20">
-              <el-col :span="12">
-                <el-form-item label="权益有效期">
-                  <el-input
-                    :value="form.validityType === 'fromGrantPlan' ? '取自发放计划' : (form.validityText || '-')"
-                    readonly
-                  ></el-input>
-                </el-form-item>
-              </el-col>
-            </el-row>
-            <el-row :gutter="20">
-              <el-col :span="12">
-                <el-form-item label="权益内容">
-                  <div class="rich-text-view" v-html="form.overview || '暂无内容'"></div>
-                </el-form-item>
-              </el-col>
-              <el-col :span="12">
-                <el-form-item label="权益说明">
-                  <div class="rich-text-view" v-html="form.details || '暂无内容'"></div>
-                </el-form-item>
-              </el-col>
-            </el-row>
-            <template v-if="form.source === '银行自有' || form.source === '荣数商品'">
-              <el-row :gutter="20">
-                <el-col :span="24">
-                  <el-form-item label="下单成功短信">
-                    <el-switch v-model="form.smsEnabled" disabled></el-switch>
-                    <template v-if="form.smsEnabled">
-                      <div style="width: 600px;background: #f1f2f5;padding: 20px;margin-top: 10px;">
-                        <el-select
-                          v-model="form.smsTemplateId"
-                          placeholder="请选择短信模板"
-                          style="width: 100%"
-                          disabled
-                        >
-                          <el-option
-                            v-for="item in smsTemplateOptions"
-                            :key="item.id"
-                            :label="item.name"
-                            :value="item.id"
-                          />
-                        </el-select>
-                        <div v-if="form.smsTemplate" class="sms-template-preview">{{ form.smsTemplate }}</div>
-                      </div>
-                    </template>
-                  </el-form-item>
-                </el-col>
-              </el-row>
-              <el-row :gutter="20">
-                <el-col :span="8">
-                  <el-form-item label="领取成功按钮">
-                    <el-input :value="form.successButtonText" readonly></el-input>
-                  </el-form-item>
-                </el-col>
-              </el-row>
-            </template>
-          </el-form>
-        </el-card>
-
-      </div>
-
-      <div v-show="activeStep === 1">
-        <!-- 设置规则 -->
-        <el-card shadow="never" class="section-card">
-          <div slot="header" class="section-title">设置规则</div>
-
-          <!-- 展示规则 -->
-          <div class="rule-section">
-            <h4>展示规则</h4>
-            <el-form :model="form" label-width="120px" label-position="left">
-              <el-form-item label="展示时间限制">
-                <el-switch v-model="form.showTimeEnabled" disabled></el-switch>
-                <template v-if="form.showTimeEnabled">
-                  <div style="display: flex;width: 600px;background: #f1f2f5;padding: 20px;margin-top: 10px;">
-                    <el-input :value="form.showStartTime" readonly style="width: 100%"></el-input>
-                    <div style="width: 50px;text-align: center;">-</div>
-                    <el-input :value="form.showEndTime" readonly style="width: 100%"></el-input>
-                  </div>
-                </template>
-              </el-form-item>
-              <el-form-item label="展示客群限制">
-                <el-switch v-model="form.showGroupEnabled" disabled></el-switch>
-                <template v-if="form.showGroupEnabled">
-                  <div style="display: flex;width: 600px;background: #f1f2f5;padding: 20px;margin-top: 10px;">
-                    <el-input :value="(form.showGroupTagIds || []).join('、')" readonly></el-input>
-                  </div>
-                </template>
-              </el-form-item>
-            </el-form>
-          </div>
-
-          <!-- 领取规则 -->
-          <div class="rule-section">
-            <h4>领取规则</h4>
-            <el-form :model="form" label-width="120px" label-position="left">
-              <el-form-item label="领取时间限制">
-                <el-switch v-model="form.claimTimeEnabled" disabled></el-switch>
-                <template v-if="form.claimTimeEnabled">
-                  <div style="display: flex;width: 600px;background: #f1f2f5;padding: 20px;margin-top: 10px;">
-                    <el-input :value="form.claimStartTime" readonly style="width: 100%"></el-input>
-                    <div style="width: 50px;text-align: center;">-</div>
-                    <el-input :value="form.claimEndTime" readonly style="width: 100%"></el-input>
-                  </div>
-                </template>
-              </el-form-item>
-              <el-form-item label="领取客群限制">
-                <el-switch v-model="form.claimGroupEnabled" disabled></el-switch>
-                <template v-if="form.claimGroupEnabled">
-                  <div style="width: 600px;background: #f1f2f5;padding: 20px;margin-top: 10px;">
-                    <el-alert
-                      title="需在【发放计划】中选择权益发放,未发放的权益移动端不展示"
-                      type="info"
-                      :closable="false"
-                      show-icon
-                      style="margin-bottom: 12px;"
-                    ></el-alert>
-                    <div style="font-size: 14px;color: #606266;margin-bottom: 8px;font-weight: 500;">不满足提示</div>
-                    <el-form-item label="提示内容" label-width="80px" style="margin-bottom: 12px;">
-                      <el-input :value="form.claimDenyMessage" readonly></el-input>
-                    </el-form-item>
-                    <el-form-item label="跳转按钮文案" label-width="100px" style="margin-bottom: 12px;">
-                      <el-input :value="form.claimDenyButtonText" readonly></el-input>
-                    </el-form-item>
-                    <el-form-item label="跳转按钮链接" label-width="100px" style="margin-bottom: 0;">
-                      <el-input :value="form.claimDenyButtonLink" readonly></el-input>
-                    </el-form-item>
-                  </div>
-                </template>
-              </el-form-item>
-            </el-form>
-          </div>
-        </el-card>
-      </div>
-
-      <div v-show="activeStep === 2">
-        <!-- 显示页面 -->
-        <el-card shadow="never" class="section-card">
-          <div slot="header" class="section-title">显示页面</div>
-          <el-form :model="form" label-width="120px" label-position="left">
-            <el-form-item label="权益首页/列表页">
-              <el-radio-group v-model="form.showOnHome" disabled>
-                <el-radio label="显示">显示</el-radio>
-                <el-radio label="不显示">不显示</el-radio>
-              </el-radio-group>
-            </el-form-item>
-            <el-form-item label="组装页">
-              <el-radio-group v-model="form.showOnAssembly" disabled>
-                <el-radio label="显示">显示</el-radio>
-                <el-radio label="不显示">不显示</el-radio>
-              </el-radio-group>
-              <div
-                v-if="form.showOnAssembly === '显示'"
-                style="width: 600px;background: #f1f2f5;padding: 20px;margin-top: 10px;"
-              >
-                <el-input :value="form.assemblyPages.join(',')" readonly></el-input>
-              </div>
-            </el-form-item>
-          </el-form>
-        </el-card>
-
-        <!-- 分享设置 -->
-        <el-card shadow="never" class="section-card">
-          <div slot="header" class="section-title">分享设置</div>
-          <el-form :model="form" label-width="120px" label-position="left">
-            <el-form-item label="是否支持分享">
-              <el-switch v-model="form.shareEnabled" disabled></el-switch>
-              <template v-if="form.shareEnabled">
-                <div style="width: 600px;background: #f1f2f5;padding: 20px;margin-top: 10px;">
-                  <el-form-item label="分享名称" label-width="100px" style="margin-bottom: 16px;">
-                    <el-input :value="form.shareName" readonly></el-input>
-                  </el-form-item>
-                  <el-form-item label="分享图片" label-width="100px" style="margin-bottom: 0;">
-                    <div v-if="form.shareImage && form.shareImage.length" class="image-preview">
-                      <img :src="(form.shareImage[0] && form.shareImage[0].url) || ''" class="preview-img" />
-                    </div>
-                    <span v-else class="no-data">暂无图片</span>
-                  </el-form-item>
-                </div>
-              </template>
-            </el-form-item>
-          </el-form>
-        </el-card>
-      </div>
-
-      <!-- 操作日志 -->
-      <el-card shadow="never" class="section-card operation-log-card">
-        <div slot="header" class="section-title">操作日志</div>
-        <el-table :data="operationLogs" border style="width: 100%" empty-text="暂无操作日志">
-          <el-table-column prop="operator" label="操作人" width="160"></el-table-column>
-          <el-table-column prop="operateTime" label="操作日期" width="200"></el-table-column>
-          <el-table-column prop="action" label="操作">
-            <template slot-scope="scope">
-              <el-tag :type="actionTagType(scope.row.action)" size="small">{{ scope.row.action }}</el-tag>
-            </template>
-          </el-table-column>
-        </el-table>
-      </el-card>
-
-      <!-- 操作栏 -->
-      <div class="action-bar">
-        <div class="action-left">
-          <el-button
-            v-if="activeStep > 0"
-            @click="prevStep"
-            icon="el-icon-arrow-left"
-          >上一步</el-button>
-        </div>
-        <div class="action-center">
-          <template v-if="activeStep === 2">
-            <el-button @click="goBack">返回</el-button>
-            <el-button
-              v-if="canRequestOffshelf"
-              type="danger"
-              plain
-              @click="handleOffshelf"
-            >下架</el-button>
-          </template>
-        </div>
-        <div class="action-right">
-          <el-button
-            v-if="activeStep < 2"
-            type="primary"
-            @click="nextStep"
-          >下一步<i class="el-icon-arrow-right el-icon--right"></i></el-button>
-        </div>
-      </div>
-    </el-card>
-
-  </div>
-</template>
-
-<script>
-import request from '@/utils/request'
-
-export default {
-  name: 'ViewEquityProduct',
-  data() {
-    return {
-      activeStep: 0,
-      currentShelfStatus: '',
-      currentAuditStatus: '',
-      smsTemplateOptions: [
-        { id: 'SMS_TPL_001', name: '通用权益支付成功模板', content: '【银行】您购买的${productName}已支付成功,卡号:${exchangeNum},卡密:${exchangePwd},有效期至${expireDate},详情:${qrCodeDownPaths}' },
-        { id: 'SMS_TPL_002', name: '券码类短信模板', content: '【银行】尊敬的客户,您的${productName}购买成功,券码为${exchangeNum},请于${expireDate}前使用。' },
-        { id: 'SMS_TPL_003', name: '实物类短信模板', content: '【银行】您购买的${productName}已受理,我们将尽快安排发货,感谢您的支持。' }
-      ],
-      form: {
-        source: '银行自有',
-        rongshuProduct: '',
-        productType: '券码',
-        faceValueThreshold: null,
-        faceValueDiscount: null,
-        stockCount: null,
-        shareEnabled: false,
-        shareImage: [],
-        shareName: '',
-        externalJumpMethod: '没有',
-        externalLink: '太古地产',
-        customLink: '',
-        miniAppId: '',
-        miniAppPath: '',
-        skuNo: '',
-        equityName: '',
-        productId: '',
-        merchantName: '',
-        subTitle: '',
-        listIcon: [],
-        mainImage: [],
-        introduction: '',
-        validityType: 'fromGrantPlan',
-        validityText: '',
-        overview: '',
-        details: '',
-        smsEnabled: false,
-        smsTemplateId: '',
-        smsTemplate: '',
-        successButtonText: '',
-        showTimeEnabled: false,
-        showStartTime: '',
-        showEndTime: '',
-        showGroupEnabled: false,
-        showGroupTagIds: [],
-        claimTimeEnabled: false,
-        claimStartTime: '',
-        claimEndTime: '',
-        claimGroupEnabled: false,
-        claimGroupIsPotential: false,
-        claimGroupTagId: '',
-        claimDenyMessage: '',
-        claimDenyButtonText: '',
-        claimDenyButtonLink: '',
-        showOnHome: '显示',
-        showOnAssembly: '不显示',
-        assemblyPages: []
-      },
-      operationLogs: []
-    }
-  },
-  computed: {
-    shouldShowDetailCard() {
-      const s = this.form.source
-      if (s === '银行自有' || s === '荣数商品') return true
-      if (s === '第三方' && this.form.externalJumpMethod === '有') return true
-      return false
-    },
-    canRequestOffshelf() {
-      return this.currentShelfStatus === '已上架' && this.currentAuditStatus === '审核通过'
-    }
-  },
-  created() {
-    this.loadData()
-    this.loadStatus()
-  },
-  methods: {
-    loadData() {
-      const id = this.$route.params.id
-      // TODO: 根据 id 从接口获取数据,这里用 mock 数据示例
-      const mockMap = {
-        'Q100001': {
-          source: '银行自有',
-          externalJumpMethod: '',
-          externalLink: '',
-          customLink: '',
-        miniAppId: '',
-        miniAppPath: '',
-          skuNo: 'SKU001',
-          equityName: '权益A',
-          subTitle: '副标题示例',
-          listIcon: [],
-          mainImage: [],
-          overview: '<p>权益内容内容</p>',
-          details: '<p>权益说明内容</p>',
-          smsEnabled: true,
-          smsTemplateId: 'SMS_TPL_001',
-          smsTemplate: '【银行】您购买的${productName}已支付成功,卡号:${exchangeNum},卡密:${exchangePwd},有效期至${expireDate},详情:${qrCodeDownPaths}',
-          successButtonText: '马上去使用',
-          showTimeEnabled: true,
-          showStartTime: '2026-01-01 00:00:00',
-          showEndTime: '2026-12-31 23:59:59',
-          showGroupEnabled: false,
-          showGroupTagIds: [],
-          claimTimeEnabled: true,
-          claimStartTime: '2026-01-01 00:00:00',
-          claimEndTime: '2026-12-31 23:59:59',
-          claimGroupEnabled: false,
-          claimGroupTagId: '',
-          claimDenyMessage: '',
-          claimDenyButtonText: '',
-          claimDenyButtonLink: ''
-        },
-        'Q100002': {
-          source: '第三方',
-          externalJumpMethod: '没有',
-          externalLink: '太古地产',
-          customLink: '',
-        miniAppId: '',
-        miniAppPath: '',
-          skuNo: 'SKU002',
-          equityName: '权益B',
-          subTitle: '外部列表页副标题',
-          listIcon: [],
-          mainImage: [],
-          overview: '',
-          details: '',
-          smsEnabled: false,
-          smsTemplateId: '',
-          smsTemplate: '',
-          successButtonText: '',
-          showTimeEnabled: false,
-          showStartTime: '',
-          showEndTime: '',
-          showGroupEnabled: true,
-          showGroupTagIds: ['客群A'],
-          claimTimeEnabled: true,
-          claimStartTime: '2026-02-01 00:00:00',
-          claimEndTime: '2026-11-30 23:59:59',
-          claimGroupEnabled: true,
-          claimGroupIsPotential: false,
-          claimGroupTagId: '客群奖励活动A',
-          claimDenyMessage: '您暂不满足领取条件',
-          claimDenyButtonText: '去升级',
-          claimDenyButtonLink: 'https://example.com/upgrade'
-        },
-        'Q100003': {
-          source: '第三方',
-          externalJumpMethod: '有',
-          externalLink: '龙腾',
-          customLink: '',
-        miniAppId: '',
-        miniAppPath: '',
-          skuNo: 'SKU003',
-          equityName: '权益C',
-          subTitle: '外部详情页副标题',
-          listIcon: [],
-          mainImage: [],
-          overview: '<p>外部详情页权益内容</p>',
-          details: '<p>外部详情页权益说明</p>',
-          smsEnabled: false,
-          smsTemplateId: '',
-          smsTemplate: '',
-          successButtonText: '',
-          showTimeEnabled: false,
-          showStartTime: '',
-          showEndTime: '',
-          showGroupEnabled: false,
-          showGroupTagIds: [],
-          claimTimeEnabled: false,
-          claimStartTime: '',
-          claimEndTime: '',
-          claimGroupEnabled: false,
-          claimGroupTagId: '',
-          claimDenyMessage: '',
-          claimDenyButtonText: '',
-          claimDenyButtonLink: ''
-        },
-        'Q100004': {
-          source: '第三方',
-          externalJumpMethod: '有',
-          externalLink: 'h5链接',
-          customLink: 'https://example.com/custom',
-          skuNo: 'SKU004',
-          equityName: '权益D',
-          subTitle: '外部自定义链接副标题',
-          listIcon: [],
-          mainImage: [],
-          overview: '<p>自定义链接权益内容</p>',
-          details: '<p>自定义链接权益说明</p>',
-          smsEnabled: false,
-          smsTemplateId: '',
-          smsTemplate: '',
-          successButtonText: '',
-          showTimeEnabled: false,
-          showStartTime: '',
-          showEndTime: '',
-          showGroupEnabled: false,
-          showGroupTagIds: [],
-          claimTimeEnabled: false,
-          claimStartTime: '',
-          claimEndTime: '',
-          claimGroupEnabled: false,
-          claimGroupTagId: '',
-          claimDenyMessage: '',
-          claimDenyButtonText: '',
-          claimDenyButtonLink: ''
-        }
-      }
-      if (id && mockMap[id]) {
-        Object.assign(this.form, mockMap[id])
-      } else if (id) {
-        Object.assign(this.form, mockMap['Q100001'])
-      }
-
-      const logsMap = {
-        'Q100001': [
-          { operator: '张三', operateTime: '2026-01-10 09:32:15', action: '创建' },
-          { operator: '李四', operateTime: '2026-01-10 14:08:42', action: '审核' },
-          { operator: '赵六', operateTime: '2026-03-01 16:45:30', action: '下架' }
-        ],
-        'Q100002': [
-          { operator: '张三', operateTime: '2026-01-12 10:15:00', action: '创建' },
-          { operator: '李四', operateTime: '2026-01-12 15:22:18', action: '审核' }
-        ],
-        'Q100003': [
-          { operator: '钱七', operateTime: '2026-01-15 11:03:27', action: '创建' },
-          { operator: '李四', operateTime: '2026-01-15 17:30:09', action: '审核' }
-        ],
-        'Q100004': [
-          { operator: '钱七', operateTime: '2026-02-01 09:48:12', action: '创建' },
-          { operator: '李四', operateTime: '2026-02-01 14:11:36', action: '审核' },
-          { operator: '赵六', operateTime: '2026-04-05 18:22:47', action: '下架' }
-        ]
-      }
-      this.operationLogs = (id && logsMap[id]) ? logsMap[id] : (id ? logsMap['Q100001'] : [])
-    },
-    actionTagType(action) {
-      const map = {
-        '创建': 'success',
-        '审核': 'primary',
-        '下架': 'danger'
-      }
-      return map[action] || 'info'
-    },
-    loadStatus() {
-      const id = this.$route.params.id
-      if (!id) return
-      request.get(`/api/equity/detail?id=${id}`).then(res => {
-        if (res.data) {
-          this.currentShelfStatus = res.data.shelfStatus || ''
-          this.currentAuditStatus = res.data.auditStatus || ''
-          if (res.data.equityName) this.form.equityName = res.data.equityName
-        }
-      }).catch(() => {})
-    },
-    handleOffshelf() {
-      this.$confirm(`确定提交"${this.form.equityName}"的下架审核吗?审核通过后才会下架。`, '提示', {
-        confirmButtonText: '提交',
-        cancelButtonText: '取消',
-        type: 'warning'
-      }).then(() => {
-        request.post('/api/equity/request-offshelf', {
-          productId: this.$route.params.id
-        }).then(() => {
-          this.$message.success('已提交下架审核')
-          this.$router.push('/customization/hsbc/equity-products')
-        }).catch(() => {})
-      }).catch(() => {})
-    },
-    nextStep() {
-      if (this.activeStep < 2) {
-        this.activeStep++
-      }
-    },
-    prevStep() {
-      if (this.activeStep > 0) {
-        this.activeStep--
-      }
-    },
-    goBack() {
-      this.$router.push('/customization/hsbc/equity-products')
-    }
-  }
-}
-</script>
-
-<style scoped>
-.view-equity-product {
-  padding: 20px;
-}
-.tip-icon {
-  color: #909399;
-  cursor: pointer;
-  font-size: 14px;
-}
-.header-title {
-  font-size: 18px;
-  font-weight: 500;
-}
-.section-card {
-  margin-bottom: 16px;
-  border: none;
-}
-.operation-log-card {
-  margin-top: 8px;
-}
-.section-card >>> .el-card__header {
-  padding: 12px 20px;
-  background: #fafafa;
-  border-bottom: 1px solid #ebeef5;
-}
-.section-title {
-  font-size: 16px;
-  font-weight: 600;
-  color: #303133;
-}
-.rule-section {
-  margin-bottom: 24px;
-}
-.rule-section h4 {
-  font-size: 14px;
-  font-weight: 600;
-  color: #606266;
-  margin-bottom: 16px;
-  border-bottom: 1px solid #ebeef5;
-  padding-bottom: 8px;
-}
-.action-bar {
-  display: flex;
-  align-items: center;
-  justify-content: space-between;
-  margin-top: 20px;
-  padding-top: 20px;
-  border-top: 1px solid #ebeef5;
-}
-.action-left, .action-right {
-  width: 120px;
-}
-.action-center {
-  display: flex;
-  justify-content: center;
-  gap: 12px;
-}
-.image-preview {
-  width: 80px;
-  height: 80px;
-  border: 1px solid #d9d9d9;
-  border-radius: 4px;
-  overflow: hidden;
-}
-.preview-img {
-  width: 100%;
-  height: 100%;
-  object-fit: cover;
-}
-.no-data {
-  color: #c0c4cc;
-  font-size: 13px;
-}
-.rich-text-view {
-  border: 1px solid #dcdfe6;
-  border-radius: 4px;
-  padding: 8px 12px;
-  min-height: 80px;
-  font-size: 14px;
-  color: #606266;
-  line-height: 1.6;
-}
-.sms-template-preview {
-  margin-top: 12px;
-  padding: 12px;
-  background: #fff;
-  border: 1px dashed #dcdfe6;
-  border-radius: 4px;
-  font-size: 13px;
-  color: #606266;
-  line-height: 1.6;
-  white-space: pre-wrap;
-  word-break: break-all;
-}
-</style>

+ 0 - 13
pc/vue.config.js

@@ -1,13 +0,0 @@
-const { defineConfig } = require('@vue/cli-service')
-module.exports = defineConfig({
-  transpileDependencies: true,
-  publicPath: './',
-  devServer: {
-    watchFiles: {
-      options: {
-        usePolling: true,
-        poll: 1000
-      }
-    }
-  }
-})