diff --git a/frontend/platform_admin/README.md b/frontend/platform_admin/README.md index 7bd86ab..d72355e 100644 --- a/frontend/platform_admin/README.md +++ b/frontend/platform_admin/README.md @@ -1,68 +1,40 @@ -# Arco Design Pro Vite +# 和气平台总后台 -基于 [Arco Design Pro](https://arco.design/pro/) 的 Vue 3 中后台模板,使用 Vite 8 + Pinia + TypeScript 构建。 +基于 Vue 3、Vite、TypeScript、Pinia 和 Arco Design 的平台管理端。前端资源、API 路径和操作能力以后端 `resource-contract` 为基准。 ## 环境要求 - Node.js >= 20.19.0 - pnpm +- Go(同步后端契约时使用) ## 常用命令 ```bash -pnpm install # 安装依赖 -pnpm dev # 开发服务器 -pnpm build # 生产构建 -pnpm report # 构建并生成 bundle 分析报告 -pnpm type:check # TypeScript 检查 -pnpm lint # Biome 代码检查 -pnpm lint:fix # 自动修复 +pnpm install +pnpm dev +pnpm type:check +pnpm lint +pnpm contract:sync +pnpm contract:check +pnpm build ``` -## 目录结构 +`contract:sync` 从 `backend/api/cmd/cli resource-contract` 生成 +`src/contracts/platform-resources.json`。修改后端模型或路由后必须重新同步。 -``` -src/ -├── api/ # 接口定义(按业务域) -├── assets/ # 静态资源与全局样式 -├── components/ # 全局 / 布局级组件 -├── directive/ # 自定义指令 -├── hooks/ # 组合式函数 -├── layout/ # 页面布局 -├── locale/ # i18n 入口与全局文案 -├── mocks/ # Mock 数据(开发环境) -│ ├── handlers/ # 全局 mock 处理器 -│ └── setup.ts # mock 启用与响应包装 -├── plugins/ # 应用插件(如 HTTP 拦截器) -├── router/ # 路由与守卫 -├── store/ # Pinia 状态 -├── types/ # 全局类型 -├── utils/ # 工具函数 -└── views/ # 页面(每页可含 components/、locale/、mock.ts) -config/ -└── vite.config.ts # Vite 配置 -public/ # 静态公共资源 -``` +## 实现约定 -## Mock 说明 - -仅在开发环境(`import.meta.env.DEV`)下,`main.ts` 会动态加载 `src/mocks/index.ts`;生产构建不会打入 mockjs。 - -- 全局 handler 位于 `mocks/handlers/` -- 页面级 mock 保留在 `views/**/mock.ts`,由 `import.meta.glob` 自动注册 +- 后端 API 使用扁平资源路径,例如 `/product_info`、`/wallet_basic`。 +- 浏览器路由按业务域分组,例如 `/product/product-info`。 +- 普通资源复用 `views/shared/ResourcePage.vue`。 +- 产品、钱包、气体配送订单的状态流转通过后端业务动作执行,不直接修改状态字段。 +- 关联资源统一提交业务 `identity`,不提交数据库自增 ID。 +- 管理端仅提供中文界面。 ## 环境变量 | 变量 | 说明 | -|------|------| -| `VITE_API_BASE_URL` | 后端 API 地址(见 `.env.development`) | -| `VITE_ERROR_REPORT_URL` | 可选,配置后启用前端错误上报(`utils/error-report.ts`) | - -## i18n 说明 - -- 菜单等全局文案:`locale/zh-CN.ts`、`locale/en-US.ts` -- 页面文案:`views/**/locale/` 与 `components/**/locale/`,通过 `import.meta.glob` 自动聚合 - -## 模板标记 - -路由与部分功能块带有 `/** simple */` … `/** simple end */` 注释,表示 Arco Pro「精简版 / 完整版」的可选模块边界。 +| --- | --- | +| `VITE_API_BASE_URL` | 后端 API 地址,默认 `http://localhost:12426/heqi/platform/v1` | +| `VITE_ERROR_REPORT_URL` | 可选的前端错误上报地址 | diff --git a/frontend/platform_admin/config/vite.config.ts b/frontend/platform_admin/config/vite.config.ts index 6daf4ef..0fc6655 100644 --- a/frontend/platform_admin/config/vite.config.ts +++ b/frontend/platform_admin/config/vite.config.ts @@ -7,13 +7,12 @@ import { ArcoResolver } from 'unplugin-vue-components/resolvers'; import Components from 'unplugin-vue-components/vite'; import { defineConfig, type PluginOption } from 'vite'; import compressPlugin from 'vite-plugin-compression'; -import { ViteImageOptimizer } from 'vite-plugin-image-optimizer'; import svgLoader from 'vite-svg-loader'; const manualChunkGroups: Record = { arco: ['@arco-design/web-vue'], chart: ['echarts', 'vue-echarts'], - vue: ['vue', 'vue-router', 'pinia', '@vueuse/core', 'vue-i18n'], + vue: ['vue', 'vue-router', 'pinia', '@vueuse/core'], }; function manualChunks(id: string) { @@ -46,12 +45,6 @@ export default defineConfig(({ command, mode }) => { resolvers: [ArcoResolver()], }), compressPlugin({ ext: '.gz' }), - ViteImageOptimizer({ - png: { quality: 80 }, - jpeg: { quality: 80 }, - jpg: { quality: 80 }, - webp: { quality: 80 }, - }), ); if (isReport) { @@ -72,10 +65,6 @@ export default defineConfig(({ command, mode }) => { alias: [ { find: '@', replacement: resolve(__dirname, '../src') }, { find: 'assets', replacement: resolve(__dirname, '../src/assets') }, - { - find: 'vue-i18n', - replacement: 'vue-i18n/dist/vue-i18n.runtime.esm-bundler.js', - }, { find: 'vue', replacement: 'vue/dist/vue.esm-bundler.js', diff --git a/frontend/platform_admin/index.html b/frontend/platform_admin/index.html index 52452ac..581f558 100644 --- a/frontend/platform_admin/index.html +++ b/frontend/platform_admin/index.html @@ -4,7 +4,7 @@ - Arco Design Pro - 开箱即用的中台前端/设计解决方案 + 和气平台总后台
diff --git a/frontend/platform_admin/package.json b/frontend/platform_admin/package.json index 7f7fe49..dcb4563 100644 --- a/frontend/platform_admin/package.json +++ b/frontend/platform_admin/package.json @@ -11,7 +11,9 @@ "report": "vite build --config ./config/vite.config.ts --mode report", "preview": "pnpm run build && vite preview --host", "type:check": "vue-tsc -p tsconfig.build.json --noEmit --skipLibCheck", - "audit:platform": "node scripts/audit-check.mjs", + "contract:sync": "node scripts/sync-backend-contract.mjs", + "contract:check": "node scripts/check-backend-contract.mjs", + "audit:platform": "node scripts/check-backend-contract.mjs", "lint": "biome lint .", "lint:fix": "biome lint --write .", "format": "biome format --write ." @@ -29,7 +31,6 @@ "sortablejs": "^1.15.6", "vue": "^3.5.13", "vue-echarts": "^8.0.1", - "vue-i18n": "^11.1.2", "vue-router": "^4.5.0" }, "devDependencies": { @@ -44,12 +45,10 @@ "less": "^4.2.2", "mockjs": "^1.1.0", "rollup-plugin-visualizer": "^6.0.3", - "sharp": "^0.34.1", "typescript": "^5.8.3", "unplugin-vue-components": "^28.8.0", "vite": "^8.0.0", "vite-plugin-compression": "^0.5.1", - "vite-plugin-image-optimizer": "^2.0.0", "vite-svg-loader": "^5.1.0", "vue-tsc": "^2.2.8" }, diff --git a/frontend/platform_admin/pnpm-lock.yaml b/frontend/platform_admin/pnpm-lock.yaml index 0b1b5b4..25ea559 100644 --- a/frontend/platform_admin/pnpm-lock.yaml +++ b/frontend/platform_admin/pnpm-lock.yaml @@ -16,7 +16,7 @@ importers: version: 13.9.0(vue@3.5.38(typescript@5.9.3)) axios: specifier: ^1.8.4 - version: 1.18.0 + version: 1.18.0(debug@4.4.3(supports-color@7.2.0))(supports-color@7.2.0) dayjs: specifier: ^1.11.13 version: 1.11.21 @@ -44,16 +44,13 @@ importers: vue-echarts: specifier: ^8.0.1 version: 8.0.1(echarts@6.1.0)(vue@3.5.38(typescript@5.9.3)) - vue-i18n: - specifier: ^11.1.2 - version: 11.4.6(vue@3.5.38(typescript@5.9.3)) vue-router: specifier: ^4.5.0 version: 4.6.4(vue@3.5.38(typescript@5.9.3)) devDependencies: '@arco-plugins/vite-vue': specifier: ^1.4.6 - version: 1.4.6 + version: 1.4.6(supports-color@7.2.0) '@biomejs/biome': specifier: ^2.5.0 version: 2.5.0 @@ -74,7 +71,7 @@ importers: version: 6.0.7(vite@8.0.16(@types/node@26.0.0)(jiti@2.6.1)(less@4.6.6)(yaml@2.9.0))(vue@3.5.38(typescript@5.9.3)) '@vitejs/plugin-vue-jsx': specifier: ^5.0.0 - version: 5.1.5(vite@8.0.16(@types/node@26.0.0)(jiti@2.6.1)(less@4.6.6)(yaml@2.9.0))(vue@3.5.38(typescript@5.9.3)) + version: 5.1.5(supports-color@7.2.0)(vite@8.0.16(@types/node@26.0.0)(jiti@2.6.1)(less@4.6.6)(yaml@2.9.0))(vue@3.5.38(typescript@5.9.3)) less: specifier: ^4.2.2 version: 4.6.6 @@ -84,27 +81,21 @@ importers: rollup-plugin-visualizer: specifier: ^6.0.3 version: 6.0.11(rolldown@1.0.3)(rollup@4.62.1) - sharp: - specifier: ^0.34.1 - version: 0.34.5 typescript: specifier: ^5.8.3 version: 5.9.3 unplugin-vue-components: specifier: ^28.8.0 - version: 28.8.0(@babel/parser@7.29.7)(vue@3.5.38(typescript@5.9.3)) + version: 28.8.0(@babel/parser@7.29.7)(supports-color@7.2.0)(vue@3.5.38(typescript@5.9.3)) vite: specifier: ^8.0.0 version: 8.0.16(@types/node@26.0.0)(jiti@2.6.1)(less@4.6.6)(yaml@2.9.0) vite-plugin-compression: specifier: ^0.5.1 - version: 0.5.1(vite@8.0.16(@types/node@26.0.0)(jiti@2.6.1)(less@4.6.6)(yaml@2.9.0)) - vite-plugin-image-optimizer: - specifier: ^2.0.0 - version: 2.0.3(sharp@0.34.5)(vite@8.0.16(@types/node@26.0.0)(jiti@2.6.1)(less@4.6.6)(yaml@2.9.0)) + version: 0.5.1(supports-color@7.2.0)(vite@8.0.16(@types/node@26.0.0)(jiti@2.6.1)(less@4.6.6)(yaml@2.9.0)) vite-svg-loader: specifier: ^5.1.0 - version: 5.1.1(vue@3.5.38(typescript@5.9.3)) + version: 5.1.1(supports-color@7.2.0)(vue@3.5.38(typescript@5.9.3)) vue-tsc: specifier: ^2.2.8 version: 2.2.12(typescript@5.9.3) @@ -302,181 +293,9 @@ packages: '@emnapi/runtime@1.10.0': resolution: {integrity: sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==} - '@emnapi/runtime@1.11.1': - resolution: {integrity: sha512-vgj7R3y3Wgx24IQaGPA/R6YFXLHVMOZ0uVEyIQPaWs+rd1AzfEMXlAC22FYwO1XkKR6NPsq7mUandH8oIRdZFw==} - '@emnapi/wasi-threads@1.2.1': resolution: {integrity: sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==} - '@img/colour@1.1.0': - resolution: {integrity: sha512-Td76q7j57o/tLVdgS746cYARfSyxk8iEfRxewL9h4OMzYhbW4TAcppl0mT4eyqXddh6L/jwoM75mo7ixa/pCeQ==} - engines: {node: '>=18'} - - '@img/sharp-darwin-arm64@0.34.5': - resolution: {integrity: sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} - cpu: [arm64] - os: [darwin] - - '@img/sharp-darwin-x64@0.34.5': - resolution: {integrity: sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} - cpu: [x64] - os: [darwin] - - '@img/sharp-libvips-darwin-arm64@1.2.4': - resolution: {integrity: sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g==} - cpu: [arm64] - os: [darwin] - - '@img/sharp-libvips-darwin-x64@1.2.4': - resolution: {integrity: sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg==} - cpu: [x64] - os: [darwin] - - '@img/sharp-libvips-linux-arm64@1.2.4': - resolution: {integrity: sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==} - cpu: [arm64] - os: [linux] - libc: [glibc] - - '@img/sharp-libvips-linux-arm@1.2.4': - resolution: {integrity: sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==} - cpu: [arm] - os: [linux] - libc: [glibc] - - '@img/sharp-libvips-linux-ppc64@1.2.4': - resolution: {integrity: sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==} - cpu: [ppc64] - os: [linux] - libc: [glibc] - - '@img/sharp-libvips-linux-riscv64@1.2.4': - resolution: {integrity: sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==} - cpu: [riscv64] - os: [linux] - libc: [glibc] - - '@img/sharp-libvips-linux-s390x@1.2.4': - resolution: {integrity: sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==} - cpu: [s390x] - os: [linux] - libc: [glibc] - - '@img/sharp-libvips-linux-x64@1.2.4': - resolution: {integrity: sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==} - cpu: [x64] - os: [linux] - libc: [glibc] - - '@img/sharp-libvips-linuxmusl-arm64@1.2.4': - resolution: {integrity: sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==} - cpu: [arm64] - os: [linux] - libc: [musl] - - '@img/sharp-libvips-linuxmusl-x64@1.2.4': - resolution: {integrity: sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==} - cpu: [x64] - os: [linux] - libc: [musl] - - '@img/sharp-linux-arm64@0.34.5': - resolution: {integrity: sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} - cpu: [arm64] - os: [linux] - libc: [glibc] - - '@img/sharp-linux-arm@0.34.5': - resolution: {integrity: sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} - cpu: [arm] - os: [linux] - libc: [glibc] - - '@img/sharp-linux-ppc64@0.34.5': - resolution: {integrity: sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} - cpu: [ppc64] - os: [linux] - libc: [glibc] - - '@img/sharp-linux-riscv64@0.34.5': - resolution: {integrity: sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} - cpu: [riscv64] - os: [linux] - libc: [glibc] - - '@img/sharp-linux-s390x@0.34.5': - resolution: {integrity: sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} - cpu: [s390x] - os: [linux] - libc: [glibc] - - '@img/sharp-linux-x64@0.34.5': - resolution: {integrity: sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} - cpu: [x64] - os: [linux] - libc: [glibc] - - '@img/sharp-linuxmusl-arm64@0.34.5': - resolution: {integrity: sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} - cpu: [arm64] - os: [linux] - libc: [musl] - - '@img/sharp-linuxmusl-x64@0.34.5': - resolution: {integrity: sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} - cpu: [x64] - os: [linux] - libc: [musl] - - '@img/sharp-wasm32@0.34.5': - resolution: {integrity: sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} - cpu: [wasm32] - - '@img/sharp-win32-arm64@0.34.5': - resolution: {integrity: sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} - cpu: [arm64] - os: [win32] - - '@img/sharp-win32-ia32@0.34.5': - resolution: {integrity: sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} - cpu: [ia32] - os: [win32] - - '@img/sharp-win32-x64@0.34.5': - resolution: {integrity: sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} - cpu: [x64] - os: [win32] - - '@intlify/core-base@11.4.6': - resolution: {integrity: sha512-EOeHO95XESK9IFHgHeZXunsM/WBAoCA0DlaWODvx14vKmetAuS97t+l6Xe9hTUqntPpF93vtVSjjUDafw3wXMw==} - engines: {node: '>= 22'} - - '@intlify/devtools-types@11.4.6': - resolution: {integrity: sha512-wowQPpNem56b2d43IJmqbrzG2FeBKe5f/kUGlpNuBmXs6OSqncF8m1+1lxHuW8ISZJF0ma2RkW3iLkw0g0G4VA==} - engines: {node: '>= 22'} - - '@intlify/message-compiler@11.4.6': - resolution: {integrity: sha512-5nj3jULqeTAC1WovwMs1LQWgatTa2pM/rXN9T3XW8rdOtXW9ZF6/GLSNFTKDQmPLwclhPdgUWLJ/4w3fMeeC/Q==} - engines: {node: '>= 22'} - - '@intlify/shared@11.4.6': - resolution: {integrity: sha512-m1p1HHAMLhqSpTRH7VnXdrN0CQ4y+9vunFkpLkbD8soIuBsnQdawZXqMCgvwI2UVF9Ww7sVaw7g9tV2VO7shoA==} - engines: {node: '>= 22'} - '@jridgewell/gen-mapping@0.3.13': resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==} @@ -884,10 +703,6 @@ packages: alien-signals@1.0.13: resolution: {integrity: sha512-OGj9yyTnJEttvzhTUWuscOvtqxq5vrhF7vL9oS0xJ2mK0ItPYP1/y+vCFebfxoEyAz0++1AIwJ5CMr+Fk3nDmg==} - ansi-colors@4.1.3: - resolution: {integrity: sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==} - engines: {node: '>=6'} - ansi-regex@5.0.1: resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} engines: {node: '>=8'} @@ -1560,15 +1375,6 @@ packages: resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} hasBin: true - semver@7.8.4: - resolution: {integrity: sha512-rUCObTnP32Q08R2uuIrt7r9PlEonuTmtuXYcW6s5kjdlj3xbnwe+21yXptAUYcMAABLkYYTtnmzb3w3EDZfueA==} - engines: {node: '>=10'} - hasBin: true - - sharp@0.34.5: - resolution: {integrity: sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} - simple-swizzle@0.2.4: resolution: {integrity: sha512-nAu1WFPQSMNr2Zn9PGSZK9AGn4t/y97lEm+MXTtUDwfP0ksAIX4nO+6ruD9Jwut4C49SB1Ws+fbXsm/yScWOHw==} @@ -1673,19 +1479,6 @@ packages: peerDependencies: vite: '>=2.0.0' - vite-plugin-image-optimizer@2.0.3: - resolution: {integrity: sha512-1vrFOTcpSvv6DCY7h8UXab4wqMAjTJB/ndOzG/Kmj1oDOuPF6mbjkNQoGzzCEYeWGe7qU93jc8oQqvoJ57al3A==} - engines: {node: '>=18.17.0'} - peerDependencies: - sharp: '>=0.34.0' - svgo: '>=4' - vite: '>=5' - peerDependenciesMeta: - sharp: - optional: true - svgo: - optional: true - vite-svg-loader@5.1.1: resolution: {integrity: sha512-RPzcXA/EpKJA0585x58DBgs7my2VfeJ+j2j1EoHY4Zh82Y7hV4cR1fElgy2aZi85+QSrcLLoTStQ5uZjD68u+Q==} peerDependencies: @@ -1743,12 +1536,6 @@ packages: echarts: ^6.0.0 vue: ^3.3.0 - vue-i18n@11.4.6: - resolution: {integrity: sha512-l0gE7Rfy0phCa5ChKYkOq543Wgd39BCK6hkktfr1Ed4D99oRkgPK9ffShASZdeC8OJxGfdWmpYoAaAH6iLEuIg==} - engines: {node: '>= 22'} - peerDependencies: - vue: ^3.0.0 - vue-router@4.6.4: resolution: {integrity: sha512-Hz9q5sa33Yhduglwz6g9skT8OBPii+4bFn88w6J+J4MfEo4KRRpmiNG/hHHkdbRFlLBOqxN8y8gf2Fb0MTUgVg==} peerDependencies: @@ -1816,12 +1603,12 @@ snapshots: scroll-into-view-if-needed: 2.2.31 vue: 3.5.38(typescript@5.9.3) - '@arco-plugins/vite-vue@1.4.6': + '@arco-plugins/vite-vue@1.4.6(supports-color@7.2.0)': dependencies: '@babel/generator': 7.29.7 - '@babel/helper-module-imports': 7.29.7 + '@babel/helper-module-imports': 7.29.7(supports-color@7.2.0) '@babel/parser': 7.29.7 - '@babel/traverse': 7.29.7 + '@babel/traverse': 7.29.7(supports-color@7.2.0) '@babel/types': 7.29.7 '@types/node': 16.18.126 transitivePeerDependencies: @@ -1835,20 +1622,20 @@ snapshots: '@babel/compat-data@7.29.7': {} - '@babel/core@7.29.7': + '@babel/core@7.29.7(supports-color@7.2.0)': dependencies: '@babel/code-frame': 7.29.7 '@babel/generator': 7.29.7 '@babel/helper-compilation-targets': 7.29.7 - '@babel/helper-module-transforms': 7.29.7(@babel/core@7.29.7) + '@babel/helper-module-transforms': 7.29.7(@babel/core@7.29.7(supports-color@7.2.0))(supports-color@7.2.0) '@babel/helpers': 7.29.7 '@babel/parser': 7.29.7 '@babel/template': 7.29.7 - '@babel/traverse': 7.29.7 + '@babel/traverse': 7.29.7(supports-color@7.2.0) '@babel/types': 7.29.7 '@jridgewell/remapping': 2.3.5 convert-source-map: 2.0.0 - debug: 4.4.3 + debug: 4.4.3(supports-color@7.2.0) gensync: 1.0.0-beta.2 json5: 2.2.3 semver: 6.3.1 @@ -1875,41 +1662,41 @@ snapshots: lru-cache: 5.1.1 semver: 6.3.1 - '@babel/helper-create-class-features-plugin@7.29.7(@babel/core@7.29.7)': + '@babel/helper-create-class-features-plugin@7.29.7(@babel/core@7.29.7(supports-color@7.2.0))(supports-color@7.2.0)': dependencies: - '@babel/core': 7.29.7 + '@babel/core': 7.29.7(supports-color@7.2.0) '@babel/helper-annotate-as-pure': 7.29.7 - '@babel/helper-member-expression-to-functions': 7.29.7 + '@babel/helper-member-expression-to-functions': 7.29.7(supports-color@7.2.0) '@babel/helper-optimise-call-expression': 7.29.7 - '@babel/helper-replace-supers': 7.29.7(@babel/core@7.29.7) - '@babel/helper-skip-transparent-expression-wrappers': 7.29.7 - '@babel/traverse': 7.29.7 + '@babel/helper-replace-supers': 7.29.7(@babel/core@7.29.7(supports-color@7.2.0))(supports-color@7.2.0) + '@babel/helper-skip-transparent-expression-wrappers': 7.29.7(supports-color@7.2.0) + '@babel/traverse': 7.29.7(supports-color@7.2.0) semver: 6.3.1 transitivePeerDependencies: - supports-color '@babel/helper-globals@7.29.7': {} - '@babel/helper-member-expression-to-functions@7.29.7': + '@babel/helper-member-expression-to-functions@7.29.7(supports-color@7.2.0)': dependencies: - '@babel/traverse': 7.29.7 + '@babel/traverse': 7.29.7(supports-color@7.2.0) '@babel/types': 7.29.7 transitivePeerDependencies: - supports-color - '@babel/helper-module-imports@7.29.7': + '@babel/helper-module-imports@7.29.7(supports-color@7.2.0)': dependencies: - '@babel/traverse': 7.29.7 + '@babel/traverse': 7.29.7(supports-color@7.2.0) '@babel/types': 7.29.7 transitivePeerDependencies: - supports-color - '@babel/helper-module-transforms@7.29.7(@babel/core@7.29.7)': + '@babel/helper-module-transforms@7.29.7(@babel/core@7.29.7(supports-color@7.2.0))(supports-color@7.2.0)': dependencies: - '@babel/core': 7.29.7 - '@babel/helper-module-imports': 7.29.7 + '@babel/core': 7.29.7(supports-color@7.2.0) + '@babel/helper-module-imports': 7.29.7(supports-color@7.2.0) '@babel/helper-validator-identifier': 7.29.7 - '@babel/traverse': 7.29.7 + '@babel/traverse': 7.29.7(supports-color@7.2.0) transitivePeerDependencies: - supports-color @@ -1919,18 +1706,18 @@ snapshots: '@babel/helper-plugin-utils@7.29.7': {} - '@babel/helper-replace-supers@7.29.7(@babel/core@7.29.7)': + '@babel/helper-replace-supers@7.29.7(@babel/core@7.29.7(supports-color@7.2.0))(supports-color@7.2.0)': dependencies: - '@babel/core': 7.29.7 - '@babel/helper-member-expression-to-functions': 7.29.7 + '@babel/core': 7.29.7(supports-color@7.2.0) + '@babel/helper-member-expression-to-functions': 7.29.7(supports-color@7.2.0) '@babel/helper-optimise-call-expression': 7.29.7 - '@babel/traverse': 7.29.7 + '@babel/traverse': 7.29.7(supports-color@7.2.0) transitivePeerDependencies: - supports-color - '@babel/helper-skip-transparent-expression-wrappers@7.29.7': + '@babel/helper-skip-transparent-expression-wrappers@7.29.7(supports-color@7.2.0)': dependencies: - '@babel/traverse': 7.29.7 + '@babel/traverse': 7.29.7(supports-color@7.2.0) '@babel/types': 7.29.7 transitivePeerDependencies: - supports-color @@ -1950,24 +1737,24 @@ snapshots: dependencies: '@babel/types': 7.29.7 - '@babel/plugin-syntax-jsx@7.29.7(@babel/core@7.29.7)': + '@babel/plugin-syntax-jsx@7.29.7(@babel/core@7.29.7(supports-color@7.2.0))': dependencies: - '@babel/core': 7.29.7 + '@babel/core': 7.29.7(supports-color@7.2.0) '@babel/helper-plugin-utils': 7.29.7 - '@babel/plugin-syntax-typescript@7.29.7(@babel/core@7.29.7)': + '@babel/plugin-syntax-typescript@7.29.7(@babel/core@7.29.7(supports-color@7.2.0))': dependencies: - '@babel/core': 7.29.7 + '@babel/core': 7.29.7(supports-color@7.2.0) '@babel/helper-plugin-utils': 7.29.7 - '@babel/plugin-transform-typescript@7.29.7(@babel/core@7.29.7)': + '@babel/plugin-transform-typescript@7.29.7(@babel/core@7.29.7(supports-color@7.2.0))(supports-color@7.2.0)': dependencies: - '@babel/core': 7.29.7 + '@babel/core': 7.29.7(supports-color@7.2.0) '@babel/helper-annotate-as-pure': 7.29.7 - '@babel/helper-create-class-features-plugin': 7.29.7(@babel/core@7.29.7) + '@babel/helper-create-class-features-plugin': 7.29.7(@babel/core@7.29.7(supports-color@7.2.0))(supports-color@7.2.0) '@babel/helper-plugin-utils': 7.29.7 - '@babel/helper-skip-transparent-expression-wrappers': 7.29.7 - '@babel/plugin-syntax-typescript': 7.29.7(@babel/core@7.29.7) + '@babel/helper-skip-transparent-expression-wrappers': 7.29.7(supports-color@7.2.0) + '@babel/plugin-syntax-typescript': 7.29.7(@babel/core@7.29.7(supports-color@7.2.0)) transitivePeerDependencies: - supports-color @@ -1977,7 +1764,7 @@ snapshots: '@babel/parser': 7.29.7 '@babel/types': 7.29.7 - '@babel/traverse@7.29.7': + '@babel/traverse@7.29.7(supports-color@7.2.0)': dependencies: '@babel/code-frame': 7.29.7 '@babel/generator': 7.29.7 @@ -1985,7 +1772,7 @@ snapshots: '@babel/parser': 7.29.7 '@babel/template': 7.29.7 '@babel/types': 7.29.7 - debug: 4.4.3 + debug: 4.4.3(supports-color@7.2.0) transitivePeerDependencies: - supports-color @@ -2040,130 +1827,11 @@ snapshots: tslib: 2.8.1 optional: true - '@emnapi/runtime@1.11.1': - dependencies: - tslib: 2.8.1 - optional: true - '@emnapi/wasi-threads@1.2.1': dependencies: tslib: 2.8.1 optional: true - '@img/colour@1.1.0': {} - - '@img/sharp-darwin-arm64@0.34.5': - optionalDependencies: - '@img/sharp-libvips-darwin-arm64': 1.2.4 - optional: true - - '@img/sharp-darwin-x64@0.34.5': - optionalDependencies: - '@img/sharp-libvips-darwin-x64': 1.2.4 - optional: true - - '@img/sharp-libvips-darwin-arm64@1.2.4': - optional: true - - '@img/sharp-libvips-darwin-x64@1.2.4': - optional: true - - '@img/sharp-libvips-linux-arm64@1.2.4': - optional: true - - '@img/sharp-libvips-linux-arm@1.2.4': - optional: true - - '@img/sharp-libvips-linux-ppc64@1.2.4': - optional: true - - '@img/sharp-libvips-linux-riscv64@1.2.4': - optional: true - - '@img/sharp-libvips-linux-s390x@1.2.4': - optional: true - - '@img/sharp-libvips-linux-x64@1.2.4': - optional: true - - '@img/sharp-libvips-linuxmusl-arm64@1.2.4': - optional: true - - '@img/sharp-libvips-linuxmusl-x64@1.2.4': - optional: true - - '@img/sharp-linux-arm64@0.34.5': - optionalDependencies: - '@img/sharp-libvips-linux-arm64': 1.2.4 - optional: true - - '@img/sharp-linux-arm@0.34.5': - optionalDependencies: - '@img/sharp-libvips-linux-arm': 1.2.4 - optional: true - - '@img/sharp-linux-ppc64@0.34.5': - optionalDependencies: - '@img/sharp-libvips-linux-ppc64': 1.2.4 - optional: true - - '@img/sharp-linux-riscv64@0.34.5': - optionalDependencies: - '@img/sharp-libvips-linux-riscv64': 1.2.4 - optional: true - - '@img/sharp-linux-s390x@0.34.5': - optionalDependencies: - '@img/sharp-libvips-linux-s390x': 1.2.4 - optional: true - - '@img/sharp-linux-x64@0.34.5': - optionalDependencies: - '@img/sharp-libvips-linux-x64': 1.2.4 - optional: true - - '@img/sharp-linuxmusl-arm64@0.34.5': - optionalDependencies: - '@img/sharp-libvips-linuxmusl-arm64': 1.2.4 - optional: true - - '@img/sharp-linuxmusl-x64@0.34.5': - optionalDependencies: - '@img/sharp-libvips-linuxmusl-x64': 1.2.4 - optional: true - - '@img/sharp-wasm32@0.34.5': - dependencies: - '@emnapi/runtime': 1.11.1 - optional: true - - '@img/sharp-win32-arm64@0.34.5': - optional: true - - '@img/sharp-win32-ia32@0.34.5': - optional: true - - '@img/sharp-win32-x64@0.34.5': - optional: true - - '@intlify/core-base@11.4.6': - dependencies: - '@intlify/devtools-types': 11.4.6 - '@intlify/message-compiler': 11.4.6 - '@intlify/shared': 11.4.6 - - '@intlify/devtools-types@11.4.6': - dependencies: - '@intlify/core-base': 11.4.6 - '@intlify/shared': 11.4.6 - - '@intlify/message-compiler@11.4.6': - dependencies: - '@intlify/shared': 11.4.6 - source-map-js: 1.2.1 - - '@intlify/shared@11.4.6': {} - '@jridgewell/gen-mapping@0.3.13': dependencies: '@jridgewell/sourcemap-codec': 1.5.5 @@ -2347,13 +2015,13 @@ snapshots: '@types/web-bluetooth@0.0.21': {} - '@vitejs/plugin-vue-jsx@5.1.5(vite@8.0.16(@types/node@26.0.0)(jiti@2.6.1)(less@4.6.6)(yaml@2.9.0))(vue@3.5.38(typescript@5.9.3))': + '@vitejs/plugin-vue-jsx@5.1.5(supports-color@7.2.0)(vite@8.0.16(@types/node@26.0.0)(jiti@2.6.1)(less@4.6.6)(yaml@2.9.0))(vue@3.5.38(typescript@5.9.3))': dependencies: - '@babel/core': 7.29.7 - '@babel/plugin-syntax-typescript': 7.29.7(@babel/core@7.29.7) - '@babel/plugin-transform-typescript': 7.29.7(@babel/core@7.29.7) + '@babel/core': 7.29.7(supports-color@7.2.0) + '@babel/plugin-syntax-typescript': 7.29.7(@babel/core@7.29.7(supports-color@7.2.0)) + '@babel/plugin-transform-typescript': 7.29.7(@babel/core@7.29.7(supports-color@7.2.0))(supports-color@7.2.0) '@rolldown/pluginutils': 1.0.1 - '@vue/babel-plugin-jsx': 2.0.1(@babel/core@7.29.7) + '@vue/babel-plugin-jsx': 2.0.1(@babel/core@7.29.7(supports-color@7.2.0))(supports-color@7.2.0) vite: 8.0.16(@types/node@26.0.0)(jiti@2.6.1)(less@4.6.6)(yaml@2.9.0) vue: 3.5.38(typescript@5.9.3) transitivePeerDependencies: @@ -2379,27 +2047,27 @@ snapshots: '@vue/babel-helper-vue-transform-on@2.0.1': {} - '@vue/babel-plugin-jsx@2.0.1(@babel/core@7.29.7)': + '@vue/babel-plugin-jsx@2.0.1(@babel/core@7.29.7(supports-color@7.2.0))(supports-color@7.2.0)': dependencies: - '@babel/helper-module-imports': 7.29.7 + '@babel/helper-module-imports': 7.29.7(supports-color@7.2.0) '@babel/helper-plugin-utils': 7.29.7 - '@babel/plugin-syntax-jsx': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-syntax-jsx': 7.29.7(@babel/core@7.29.7(supports-color@7.2.0)) '@babel/template': 7.29.7 - '@babel/traverse': 7.29.7 + '@babel/traverse': 7.29.7(supports-color@7.2.0) '@babel/types': 7.29.7 '@vue/babel-helper-vue-transform-on': 2.0.1 - '@vue/babel-plugin-resolve-type': 2.0.1(@babel/core@7.29.7) + '@vue/babel-plugin-resolve-type': 2.0.1(@babel/core@7.29.7(supports-color@7.2.0))(supports-color@7.2.0) '@vue/shared': 3.5.38 optionalDependencies: - '@babel/core': 7.29.7 + '@babel/core': 7.29.7(supports-color@7.2.0) transitivePeerDependencies: - supports-color - '@vue/babel-plugin-resolve-type@2.0.1(@babel/core@7.29.7)': + '@vue/babel-plugin-resolve-type@2.0.1(@babel/core@7.29.7(supports-color@7.2.0))(supports-color@7.2.0)': dependencies: '@babel/code-frame': 7.29.7 - '@babel/core': 7.29.7 - '@babel/helper-module-imports': 7.29.7 + '@babel/core': 7.29.7(supports-color@7.2.0) + '@babel/helper-module-imports': 7.29.7(supports-color@7.2.0) '@babel/helper-plugin-utils': 7.29.7 '@babel/parser': 7.29.7 '@vue/compiler-sfc': 3.5.38 @@ -2513,16 +2181,14 @@ snapshots: acorn@8.17.0: {} - agent-base@6.0.2: + agent-base@6.0.2(supports-color@7.2.0): dependencies: - debug: 4.4.3 + debug: 4.4.3(supports-color@7.2.0) transitivePeerDependencies: - supports-color alien-signals@1.0.13: {} - ansi-colors@4.1.3: {} - ansi-regex@5.0.1: {} ansi-styles@4.3.0: @@ -2536,11 +2202,11 @@ snapshots: asynckit@0.4.0: {} - axios@1.18.0: + axios@1.18.0(debug@4.4.3(supports-color@7.2.0))(supports-color@7.2.0): dependencies: - follow-redirects: 1.16.0 + follow-redirects: 1.16.0(debug@4.4.3(supports-color@7.2.0)) form-data: 4.0.6 - https-proxy-agent: 5.0.1 + https-proxy-agent: 5.0.1(supports-color@7.2.0) proxy-from-env: 2.1.0 transitivePeerDependencies: - debug @@ -2682,9 +2348,11 @@ snapshots: de-indent@1.0.2: {} - debug@4.4.3: + debug@4.4.3(supports-color@7.2.0): dependencies: ms: 2.1.3 + optionalDependencies: + supports-color: 7.2.0 define-lazy-prop@2.0.0: {} @@ -2763,7 +2431,9 @@ snapshots: dependencies: to-regex-range: 5.0.1 - follow-redirects@1.16.0: {} + follow-redirects@1.16.0(debug@4.4.3(supports-color@7.2.0)): + optionalDependencies: + debug: 4.4.3(supports-color@7.2.0) form-data@4.0.6: dependencies: @@ -2830,10 +2500,10 @@ snapshots: hookable@5.5.3: {} - https-proxy-agent@5.0.1: + https-proxy-agent@5.0.1(supports-color@7.2.0): dependencies: - agent-base: 6.0.2 - debug: 4.4.3 + agent-base: 6.0.2(supports-color@7.2.0) + debug: 4.4.3(supports-color@7.2.0) transitivePeerDependencies: - supports-color @@ -3159,39 +2829,6 @@ snapshots: semver@6.3.1: {} - semver@7.8.4: {} - - sharp@0.34.5: - dependencies: - '@img/colour': 1.1.0 - detect-libc: 2.1.2 - semver: 7.8.4 - optionalDependencies: - '@img/sharp-darwin-arm64': 0.34.5 - '@img/sharp-darwin-x64': 0.34.5 - '@img/sharp-libvips-darwin-arm64': 1.2.4 - '@img/sharp-libvips-darwin-x64': 1.2.4 - '@img/sharp-libvips-linux-arm': 1.2.4 - '@img/sharp-libvips-linux-arm64': 1.2.4 - '@img/sharp-libvips-linux-ppc64': 1.2.4 - '@img/sharp-libvips-linux-riscv64': 1.2.4 - '@img/sharp-libvips-linux-s390x': 1.2.4 - '@img/sharp-libvips-linux-x64': 1.2.4 - '@img/sharp-libvips-linuxmusl-arm64': 1.2.4 - '@img/sharp-libvips-linuxmusl-x64': 1.2.4 - '@img/sharp-linux-arm': 0.34.5 - '@img/sharp-linux-arm64': 0.34.5 - '@img/sharp-linux-ppc64': 0.34.5 - '@img/sharp-linux-riscv64': 0.34.5 - '@img/sharp-linux-s390x': 0.34.5 - '@img/sharp-linux-x64': 0.34.5 - '@img/sharp-linuxmusl-arm64': 0.34.5 - '@img/sharp-linuxmusl-x64': 0.34.5 - '@img/sharp-wasm32': 0.34.5 - '@img/sharp-win32-arm64': 0.34.5 - '@img/sharp-win32-ia32': 0.34.5 - '@img/sharp-win32-x64': 0.34.5 - simple-swizzle@0.2.4: dependencies: is-arrayish: 0.3.4 @@ -3263,10 +2900,10 @@ snapshots: pathe: 2.0.3 picomatch: 4.0.4 - unplugin-vue-components@28.8.0(@babel/parser@7.29.7)(vue@3.5.38(typescript@5.9.3)): + unplugin-vue-components@28.8.0(@babel/parser@7.29.7)(supports-color@7.2.0)(vue@3.5.38(typescript@5.9.3)): dependencies: chokidar: 3.6.0 - debug: 4.4.3 + debug: 4.4.3(supports-color@7.2.0) local-pkg: 1.2.1 magic-string: 0.30.21 mlly: 1.8.2 @@ -3292,26 +2929,18 @@ snapshots: escalade: 3.2.0 picocolors: 1.1.1 - vite-plugin-compression@0.5.1(vite@8.0.16(@types/node@26.0.0)(jiti@2.6.1)(less@4.6.6)(yaml@2.9.0)): + vite-plugin-compression@0.5.1(supports-color@7.2.0)(vite@8.0.16(@types/node@26.0.0)(jiti@2.6.1)(less@4.6.6)(yaml@2.9.0)): dependencies: chalk: 4.1.2 - debug: 4.4.3 + debug: 4.4.3(supports-color@7.2.0) fs-extra: 10.1.0 vite: 8.0.16(@types/node@26.0.0)(jiti@2.6.1)(less@4.6.6)(yaml@2.9.0) transitivePeerDependencies: - supports-color - vite-plugin-image-optimizer@2.0.3(sharp@0.34.5)(vite@8.0.16(@types/node@26.0.0)(jiti@2.6.1)(less@4.6.6)(yaml@2.9.0)): + vite-svg-loader@5.1.1(supports-color@7.2.0)(vue@3.5.38(typescript@5.9.3)): dependencies: - ansi-colors: 4.1.3 - pathe: 2.0.3 - vite: 8.0.16(@types/node@26.0.0)(jiti@2.6.1)(less@4.6.6)(yaml@2.9.0) - optionalDependencies: - sharp: 0.34.5 - - vite-svg-loader@5.1.1(vue@3.5.38(typescript@5.9.3)): - dependencies: - debug: 4.4.3 + debug: 4.4.3(supports-color@7.2.0) svgo: 3.3.3 vue: 3.5.38(typescript@5.9.3) transitivePeerDependencies: @@ -3338,14 +2967,6 @@ snapshots: echarts: 6.1.0 vue: 3.5.38(typescript@5.9.3) - vue-i18n@11.4.6(vue@3.5.38(typescript@5.9.3)): - dependencies: - '@intlify/core-base': 11.4.6 - '@intlify/devtools-types': 11.4.6 - '@intlify/shared': 11.4.6 - '@vue/devtools-api': 6.6.4 - vue: 3.5.38(typescript@5.9.3) - vue-router@4.6.4(vue@3.5.38(typescript@5.9.3)): dependencies: '@vue/devtools-api': 6.6.4 diff --git a/frontend/platform_admin/scripts/audit-check.mjs b/frontend/platform_admin/scripts/audit-check.mjs deleted file mode 100644 index 336f0c7..0000000 --- a/frontend/platform_admin/scripts/audit-check.mjs +++ /dev/null @@ -1,119 +0,0 @@ -import { execFileSync } from 'node:child_process'; -import fs from 'node:fs'; -import path from 'node:path'; -import vm from 'node:vm'; -import ts from 'typescript'; - -const files = (dir) => fs.existsSync(dir) ? fs.readdirSync(dir, { withFileTypes: true }).flatMap((entry) => entry.isDirectory() ? files(path.join(dir, entry.name)) : [path.join(dir, entry.name)]) : []; -const sourceFiles = (dir, extensions = ['.ts', '.vue']) => new Map(files(dir).filter((file) => extensions.includes(path.extname(file))).map((file) => [file.replaceAll('\\', '/'), fs.readFileSync(file, 'utf8')])); -const mutationActions = (source) => [...source.matchAll(/resourceApi\.(create|update|updateStatus|archive)\b/g)].map((match) => match[1]); - -function requiredBackendRoutes(contract) { - if (contract.mode === 'append_only') return [{ method: 'GET', path: contract.path }, { method: 'POST', path: contract.path }]; - const resource = contract.path; - const detail = `${resource}/:identity`; - if (contract.mode === 'readonly') return [{ method: 'GET', path: resource }, { method: 'GET', path: detail }]; - return [ - { method: 'GET', path: resource }, { method: 'POST', path: resource }, { method: 'GET', path: detail }, - { method: 'PUT', path: detail }, { method: 'PATCH', path: `${detail}/status` }, { method: 'DELETE', path: detail }, - ]; -} - -function routeCoverage(contract, routeSources, viewSources) { - const expectedView = new RegExp(`getResource\\(\\s*['\"]${escapeRegExp(contract.path)}['\"]\\s*\\)`); - let hasPage = false; - let hasMenu = false; - for (const [, source] of sourceEntries(routeSources, 'src/router')) { - for (const match of source.matchAll(/component:\s*\(\)\s*=>\s*import\(['\"]@\/views\/([^'\"]+)['\"]\)([\s\S]{0,260}?meta:\s*\{[^}]*\})?/g)) { - const view = viewSources.get(`src/views/${match[1]}`); - if (!view || !expectedView.test(view)) continue; - hasPage = true; - if (/locale:\s*['\"]menu\.platform\./.test(match[2] ?? '')) hasMenu = true; - } - } - return { hasPage, hasMenu }; -} - -function sourceEntries(sources, fallbackDirectory) { - if (sources instanceof Map) return [...sources]; - return sources.map((source, index) => [`${fallbackDirectory}/${index}`, source]); -} - -function escapeRegExp(value) { - return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); -} - -/** Scans user-facing API and view sources for auto-increment primary or relation IDs. */ -export function scanInternalIdLeaks(sources) { - const failures = []; - for (const [file, source] of sources) { - for (const line of source.split(/\r?\n/)) { - // Remove only the defensive predicates, then keep scanning the rest of the line. - const scanned = line.replace(/key\s*!==\s*['\"]id['\"]/g, '').replace(/key\.endsWith\(\s*['\"]_id['\"]\s*\)/g, ''); - const relation = scanned.match(/\b([A-Za-z][A-Za-z0-9_]*_id)\b/); - if (relation) failures.push(`${file}: internal identifier ${relation[1]}`); - else if (/\bdata-index\s*=\s*['\"]id['\"]|\.id\b|[,{]\s*id\s*:/.test(scanned)) failures.push(`${file}: internal identifier id`); - } - } - return failures; -} - -/** Evaluates every platform contract against its backend route, UI definition, page and menu route. */ -export function auditPlatform({ manifest, resources, readOnlyPage, routeSources, viewSources, apiSources, responseShapeVerified = true }) { - const failures = []; - if (!responseShapeVerified) failures.push('backend list response shape is not identity-only'); - if (resources.length !== manifest.resources.length) failures.push(`catalogue count: ${resources.length}/${manifest.resources.length}`); - for (const contract of manifest.resources) { - const resource = resources.find((item) => item.name === contract.name); - const pageKind = contract.name === 'ec_category' ? 'tree' : contract.pageKind; - const label = `${contract.domain}/${contract.name}`; - if (!resource || resource.resource !== contract.path || resource.mode !== contract.mode || resource.pageKind !== pageKind) { - failures.push(`${label}: missing frontend resource`); - continue; - } - if (!/^[\u4e00-\u9fff]/.test(resource.title) || resource.fields.length === 0 || resource.fields.some((field) => field.key === 'id' || field.key.endsWith('_id')) || resource.fields.some((field) => !/^[\u4e00-\u9fff]/.test(field.label))) failures.push(`${label}: invalid frontend allowlist`); - for (const expected of requiredBackendRoutes(contract)) if (!manifest.routes.some((route) => route.method === expected.method && route.path === expected.path)) failures.push(`${label}: missing backend ${expected.method}`); - const coverage = routeCoverage(contract, routeSources, viewSources); - if (!coverage.hasPage) failures.push(`${label}: missing page`); - if (!coverage.hasMenu) failures.push(`${label}: missing menu route`); - if (contract.mode === 'readonly') { - const statusCall = new RegExp(`resourceApi\\.updateStatus\\(\\s*['\"]${escapeRegExp(contract.path)}['\"]`); - for (const [file, source] of [...sourceEntries(routeSources, 'src/router'), ...sourceEntries(apiSources, 'src/api'), ...sourceEntries(viewSources, 'src/views')]) { - if (statusCall.test(source)) failures.push(`${label}: readonly status mutation in ${file}`); - } - } - } - if (JSON.stringify(resources.map((item) => item.name).sort()) !== JSON.stringify(manifest.resources.map((item) => item.name).sort())) failures.push('catalogue names differ from backend ExpectedResources'); - const readonlyMutations = mutationActions(readOnlyPage); - if (readonlyMutations.length) failures.push(`readonly: mutation action exposed (${readonlyMutations.join(', ')})`); - failures.push(...scanInternalIdLeaks(new Map([...apiSources, ...viewSources]))); - return failures; -} - -function loadResources() { - const compiled = ts.transpileModule(fs.readFileSync('src/api/resources.ts', 'utf8'), { compilerOptions: { module: ts.ModuleKind.CommonJS, target: ts.ScriptTarget.ES2020 } }).outputText; - const resourceModule = { exports: {} }; - vm.runInNewContext(compiled, { module: resourceModule, exports: resourceModule.exports }); - return resourceModule.exports.resources; -} - -function runAudit() { - const backendDirectory = path.resolve('../..', 'backend/api'); - const manifest = JSON.parse(execFileSync('go', ['run', './cmd/resource-contract'], { cwd: backendDirectory, encoding: 'utf8' })); - let responseShapeVerified = true; - try { execFileSync('go', ['test', '-count=1', './internal/logic/platform', '-run', '^TestListGasAccountProjectsGasBasicIdentityAndNeverReturnsRelationID$'], { cwd: backendDirectory, stdio: 'pipe' }); } catch { responseShapeVerified = false; } - const viewSources = sourceFiles('src/views', ['.vue']); - const failures = auditPlatform({ - manifest, - resources: loadResources(), - readOnlyPage: fs.readFileSync('src/views/shared/ReadOnlyListPage.vue', 'utf8'), - routeSources: sourceFiles('src/router', ['.ts']), - viewSources, - apiSources: sourceFiles('src/api', ['.ts']), - responseShapeVerified, - }); - for (const failure of failures) console.log(failure); - if (failures.length) process.exitCode = 1; -} - -if (import.meta.url === `file://${process.argv[1]?.replaceAll('\\', '/')}`) runAudit(); diff --git a/frontend/platform_admin/scripts/audit-check.test.mjs b/frontend/platform_admin/scripts/audit-check.test.mjs deleted file mode 100644 index 4d02987..0000000 --- a/frontend/platform_admin/scripts/audit-check.test.mjs +++ /dev/null @@ -1,89 +0,0 @@ -import assert from 'node:assert/strict'; -import { readFileSync } from 'node:fs'; -import test from 'node:test'; -import { auditPlatform, scanInternalIdLeaks } from './audit-check.mjs'; - -const resourcesSource = readFileSync('src/api/resources.ts', 'utf8'); -const legacySafetyPrefix = ['sa', 'f_'].join(''); -const legacyAuditPrefix = ['au', 'd_'].join(''); - -test('安全模块资源定义已完全删除', () => { - assert.doesNotMatch(resourcesSource, /safe_(?:rule|event|inspection|event_disposal)|\/safety\//); - - assert.doesNotMatch(resourcesSource, new RegExp(`define\\('${legacySafetyPrefix}(?:rule|event|inspection|event_disposal)',`)); - assert.doesNotMatch(resourcesSource, new RegExp(`action\\('${legacySafetyPrefix}event_disposal',`)); - assert.doesNotMatch(resourcesSource, new RegExp(`define\\('${legacyAuditPrefix}(?:operation_log|export_log|approval)',`)); - assert.doesNotMatch(resourcesSource, /audit_(?:operation_log|export_log|approval)/); -}); - -test('只读页面将状态变更视为违规写操作', () => { - const failures = auditPlatform({ - manifest: { resources: [], routes: [] }, - resources: [], - readOnlyPage: '', - routeSources: [], - viewSources: new Map(), - apiSources: new Map(), - }); - - assert.deepEqual(failures, ['readonly: mutation action exposed (updateStatus)']); -}); - -test('扫描 API 和页面中用于展示或请求的内部 ID', () => { - const failures = scanInternalIdLeaks(new Map([ - ['src/api/leak.ts', "resourceApi.create('/gas/gas_basic', { gas_basic_id: 7 })"], - ['src/views/leak.vue', ''], - ])); - - assert.deepEqual(failures, [ - 'src/api/leak.ts: internal identifier gas_basic_id', - 'src/views/leak.vue: internal identifier id', - ]); -}); - -test('防护表达式不能掩盖同一行的内部 ID 泄漏', () => { - const failures = scanInternalIdLeaks(new Map([ - ['src/views/leak.vue', "const visible = row.id; const safe = key !== 'id';"], - ['src/api/leak.ts', "send({ gas_basic_id: 7 }); const safe = key.endsWith('_id');"], - ])); - - assert.deepEqual(failures, [ - 'src/views/leak.vue: internal identifier id', - 'src/api/leak.ts: internal identifier gas_basic_id', - ]); -}); - -test('每个资源必须由带菜单元数据的路由实际加载对应页面', () => { - const failures = auditPlatform({ - manifest: { resources: [{ domain: 'gas', name: 'gas_basic', path: '/gas/gas_basic', mode: 'writable', pageKind: 'list' }], routes: [ - { method: 'GET', path: '/gas/gas_basic' }, - { method: 'POST', path: '/gas/gas_basic' }, - { method: 'GET', path: '/gas/gas_basic/:identity' }, - { method: 'PUT', path: '/gas/gas_basic/:identity' }, - { method: 'PATCH', path: '/gas/gas_basic/:identity/status' }, - { method: 'DELETE', path: '/gas/gas_basic/:identity' }, - ] }, - resources: [{ name: 'gas_basic', resource: '/gas/gas_basic', mode: 'writable', pageKind: 'list', title: '气站管理', fields: [{ key: 'name', label: '名称' }] }], - readOnlyPage: '', - routeSources: ["{ component: () => import('@/views/gas/gas_basic/ListPage.vue') }"], - viewSources: new Map([['src/views/gas/gas_basic/ListPage.vue', "getResource('/gas/gas_basic')"]]), - apiSources: new Map(), - }); - - assert.deepEqual(failures, ['gas/gas_basic: missing menu route']); -}); - -test('只读资源拒绝 API 或路由中的状态写入', () => { - const failures = auditPlatform({ - manifest: { resources: [{ domain: 'wallet', name: 'wallet', path: '/wallet/wallet', mode: 'readonly', pageKind: 'list' }], routes: [ - { method: 'GET', path: '/wallet/wallet' }, { method: 'GET', path: '/wallet/wallet/:identity' }, - ] }, - resources: [{ name: 'wallet', resource: '/wallet/wallet', mode: 'readonly', pageKind: 'list', title: '钱包', fields: [{ key: 'balance_amount', label: '余额' }] }], - readOnlyPage: '', - routeSources: ["{ component: () => import('@/views/wallet/wallet/ListPage.vue'), meta: { locale: 'menu.platform.wallet' } }"], - viewSources: new Map([['src/views/wallet/wallet/ListPage.vue', "getResource('/wallet/wallet')"]]), - apiSources: new Map([['src/api/wallet.ts', "resourceApi.updateStatus('/wallet/wallet', identity, 'disabled')"]]), - }); - - assert.deepEqual(failures, ['wallet/wallet: readonly status mutation in src/api/wallet.ts']); -}); diff --git a/frontend/platform_admin/scripts/check-backend-contract.mjs b/frontend/platform_admin/scripts/check-backend-contract.mjs new file mode 100644 index 0000000..3d717a9 --- /dev/null +++ b/frontend/platform_admin/scripts/check-backend-contract.mjs @@ -0,0 +1,35 @@ +import { readFileSync } from 'node:fs'; +import { dirname, resolve } from 'node:path'; +import { fileURLToPath } from 'node:url'; + +const root = resolve(dirname(fileURLToPath(import.meta.url)), '..'); +const source = readFileSync(resolve(root, 'src/api/resources.ts'), 'utf8'); +const routes = readFileSync(resolve(root, 'src/router/routes/modules/platform.ts'), 'utf8'); +const contract = JSON.parse( + readFileSync(resolve(root, 'src/contracts/platform-resources.json'), 'utf8'), +); +const backend = new Map(contract.resources.map((item) => [item.name, item])); +const frontendNames = [...source.matchAll(/define\('([^']+)'/g)].map((match) => match[1]); + +for (const name of frontendNames) { + const item = backend.get(name); + if (!item) throw new Error(`前端配置了后端不存在的资源:${name}`); + if (item.path !== `/${name}`) throw new Error(`资源路径不一致:${name}`); +} +for (const item of contract.resources) { + if (!frontendNames.includes(item.name)) + throw new Error(`缺少后端资源配置:${item.name}`); +} + +const forbidden = [ + '/platform/platform_', '/gas/gas_', '/delivery/delivery_', '/staff/', + '/user/', '/ec/ec_', '/finance/fin_', '/wallet/wallet', + 'delivery_task', 'delivery_track', 'delivery_track_point', + 'dev_device_binding', 'dev_smart_cylinder_valve', 'dev_telemetry', + 'wallet_ledger', 'wallet_recharge', 'wallet_withdrawal', +]; +for (const value of forbidden) { + if (source.includes(value) || routes.includes(value)) + throw new Error(`仍存在旧资源或旧路径:${value}`); +} +console.log(`契约检查通过:${frontendNames.length} 个资源`); diff --git a/frontend/platform_admin/scripts/final-important.test.mjs b/frontend/platform_admin/scripts/final-important.test.mjs deleted file mode 100644 index 6f8982c..0000000 --- a/frontend/platform_admin/scripts/final-important.test.mjs +++ /dev/null @@ -1,178 +0,0 @@ -import assert from 'node:assert/strict'; -import fs from 'node:fs'; -import path from 'node:path'; -import test from 'node:test'; -import { fileURLToPath } from 'node:url'; -import vm from 'node:vm'; -import ts from 'typescript'; - -const projectRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..'); -const fromProjectRoot = (...segments) => path.join(projectRoot, ...segments); - -function loadResources() { - const compiled = ts.transpileModule(fs.readFileSync(fromProjectRoot('src/api/resources.ts'), 'utf8'), { - compilerOptions: { module: ts.ModuleKind.CommonJS, target: ts.ScriptTarget.ES2020 }, - }).outputText; - const resourceModule = { exports: {} }; - vm.runInNewContext(compiled, { module: resourceModule, exports: resourceModule.exports }); - return resourceModule.exports.resources; -} - -function loadResourceForm() { - const source = fs.readFileSync(fromProjectRoot('src/api/resource-form.ts'), 'utf8'); - const compiled = ts.transpileModule(source, { - compilerOptions: { module: ts.ModuleKind.CommonJS, target: ts.ScriptTarget.ES2020 }, - }).outputText; - const resourceModule = { exports: {} }; - vm.runInNewContext(compiled, { module: resourceModule, exports: resourceModule.exports }); - return resourceModule.exports; -} - -test('资源字段声明保留数字、布尔、时间与文本类型', () => { - const resources = loadResources(); - const field = (resource, key) => resources.find((item) => item.name === resource).fields.find((item) => item.key === key); - assert.equal(field('ec_product', 'price_amount').type, 'number'); - assert.equal(field('ec_product_image', 'is_cover').type, 'boolean'); - assert.equal(field('delivery_track', 'started_at').type, 'datetime'); - assert.equal(field('dev_telemetry', 'payload').type, 'textarea'); -}); - -test('表单载荷构造器省略空的可选关系并转换字段类型', async () => { - const resourceFormPath = fromProjectRoot('src/api/resource-form.ts'); - assert.ok(fs.existsSync(resourceFormPath), 'resource-form.ts should define the payload boundary'); - const resourceForm = loadResourceForm(); - const fields = [ - { key: 'gas_basic_identity', label: '气站', type: 'identity' }, - { key: 'quantity', label: '数量', type: 'number' }, - { key: 'selected', label: '选中', type: 'boolean' }, - ]; - assert.deepEqual( - JSON.parse(JSON.stringify(resourceForm.buildResourcePayload(fields, { - gas_basic_identity: '', - quantity: '2', - selected: false, - }))), - { quantity: 2, selected: false }, - ); -}); - -test('订单明细文本字段允许任意文本提交', () => { - const resources = loadResources(); - const { buildResourcePayload } = loadResourceForm(); - const fields = (name) => resources.find((item) => item.name === name).fields; - - assert.deepEqual( - JSON.parse(JSON.stringify(buildResourcePayload(fields('ec_order_item'), { - ec_order_identity: 'order-a', - ec_product_identity: 'product-a', - product_snapshot: 'arbitrary product snapshot text', - quantity: 2, - sale_amount: 500, - }))), - { - ec_order_identity: 'order-a', - ec_product_identity: 'product-a', - product_snapshot: 'arbitrary product snapshot text', - quantity: 2, - sale_amount: 500, - }, - ); -}); - -test('日期按 RFC3339 提交,密码只在创建时必填并提交', () => { - const { buildResourcePayload, isResourceFieldRequired } = loadResourceForm(); - const fields = [ - { key: 'bill_date', label: '账单日期', type: 'date', required: true }, - { key: 'started_at', label: '开始时间', type: 'datetime' }, - { key: 'username', label: '用户名', type: 'text', required: true }, - { key: 'password', label: '密码', type: 'password', required: true }, - ]; - - assert.equal(isResourceFieldRequired(fields[3], 'create'), true); - assert.equal(isResourceFieldRequired(fields[3], 'edit'), false); - assert.deepEqual( - JSON.parse(JSON.stringify(buildResourcePayload(fields, { - bill_date: '2026-07-27', - started_at: '2026-07-27T10:30:00Z', - username: 'operator', - password: 'secret', - }, 'create'))), - { - bill_date: '2026-07-27T00:00:00.000Z', - started_at: '2026-07-27T10:30:00.000Z', - username: 'operator', - password: 'secret', - }, - ); - assert.deepEqual( - JSON.parse(JSON.stringify(buildResourcePayload(fields, { - bill_date: '2026-07-27', - username: 'operator', - password: 'replacement-must-not-be-sent', - }, 'edit'))), - { - bill_date: '2026-07-27T00:00:00.000Z', - username: 'operator', - }, - ); -}); - -test('已删除的审批模块不再保留前端资源或页面', () => { - const source = fs.readFileSync(fromProjectRoot('src/views/shared/ReadOnlyListPage.vue'), 'utf8'); - const resources = fs.readFileSync(fromProjectRoot('src/api/resources.ts'), 'utf8'); - assert.doesNotMatch(resources, /audit_approval|audit_operation_log|audit_export_log/); - assert.equal(fs.existsSync(fromProjectRoot('src/views/audit')), false); -}); - -test('树页面通过资源归档接口归档节点', () => { - const source = fs.readFileSync(fromProjectRoot('src/views/shared/TreePage.vue'), 'utf8'); - assert.match(source, /resourceApi\.archive/); - assert.match(source, /Modal\.(warning|confirm)/); -}); - -test('route access uses authenticated role and assigned menu codes', () => { - const routeDir = fromProjectRoot('src/router/routes/modules'); - const routeSource = fs.readdirSync(routeDir) - .filter((name) => name.endsWith('.ts')) - .map((name) => fs.readFileSync(path.join(routeDir, name), 'utf8')) - .join('\n'); - const userStore = fs.readFileSync(fromProjectRoot('src/store/modules/user/index.ts'), 'utf8'); - const permission = fs.readFileSync(fromProjectRoot('src/hooks/permission.ts'), 'utf8'); - - assert.doesNotMatch(routeSource, /roles:\s*\[\s*['"]\*['"]\s*\]/); - assert.match(routeSource, /menuCode:\s*['"]finance['"]/); - assert.match(userStore, /profile\.role_code/); - assert.match(userStore, /menuCodes/); - assert.match(permission, /menuCode/); -}); - -test('platform role UI replaces assigned menu identities through the singular contract URL', () => { - const resources = fs.readFileSync(fromProjectRoot('src/api/resources.ts'), 'utf8'); - const crudPage = fs.readFileSync(fromProjectRoot('src/views/shared/CrudListPage.vue'), 'utf8'); - const platformApi = fs.readFileSync(fromProjectRoot('src/api/platform.ts'), 'utf8'); - - assert.match(resources, /platform_role[\s\S]*\/platform\/platform_role\/:identity\/menu/); - assert.match(resources, /menu-identities/); - assert.match(crudPage, /multiple/); - assert.match(crudPage, /menu_identities/); - assert.match(platformApi, /\/platform\/platform_role\/\$\{identity\}\/menu/); - assert.match(platformApi, /method:\s*['"]PUT['"]/); -}); - -test('platform role UI permits an empty menu selection to revoke every assignment', () => { - const resources = loadResources(); - const { buildResourcePayload, isMissingField } = loadResourceForm(); - const role = resources.find((item) => item.name === 'platform_role'); - const menuField = role.detailActions - .flatMap((action) => action.fields) - .find((field) => field.key === 'menu_identities'); - - assert.equal(menuField.required, undefined); - assert.equal(isMissingField([]), true); - assert.deepEqual( - JSON.parse(JSON.stringify(buildResourcePayload([menuField], { - menu_identities: [], - }))), - { menu_identities: [] }, - ); -}); diff --git a/frontend/platform_admin/scripts/restore-p0-vue.mjs b/frontend/platform_admin/scripts/restore-p0-vue.mjs deleted file mode 100644 index f8580a9..0000000 --- a/frontend/platform_admin/scripts/restore-p0-vue.mjs +++ /dev/null @@ -1,67 +0,0 @@ -import fs from 'node:fs'; -import path from 'node:path'; - -const BASE = - 'https://raw.githubusercontent.com/arco-design/arco-design-pro-vue/main/arco-design-pro-vite/src'; - -const vueFiles = [ - 'views/visualization/multi-dimension-data-analysis/components/content-publishing-source.vue', - 'views/user/info/components/my-project.vue', - 'views/user/info/components/my-team.vue', - 'views/user/setting/components/enterprise-certification.vue', -]; - -async function download(relPath) { - const res = await fetch(`${BASE}/${relPath}`); - if (!res.ok) throw new Error(`${relPath}: HTTP ${res.status}`); - return res.text(); -} - -function patchForProject(content, relPath) { - let text = content; - - if (relPath.includes('my-project.vue')) { - text = text.replace( - "import { queryMyProjectList, MyProjectRecord } from '@/api/user-center';", - "import { type MyProjectRecord, queryMyProjectList } from '@/api/user';", - ); - text = text.replace(/\{\{ project\.contributors \}\}\s*/g, ''); - } - - if (relPath.includes('my-team.vue')) { - text = text.replace( - "import { queryMyTeamList, MyTeamRecord } from '@/api/user-center';", - "import { type MyTeamRecord, queryMyTeamList } from '@/api/user';", - ); - } - - if (relPath.includes('enterprise-certification.vue')) { - text = text.replace( - "import { EnterpriseCertificationModel } from '@/api/user-center';", - "import type { EnterpriseCertificationModel } from '@/api/user';", - ); - text = text.replace( - /type: Object as PropType/, - 'type: Object as PropType,', - ); - } - - return text; -} - -async function main() { - for (const relPath of vueFiles) { - let content = await download(relPath); - content = patchForProject(content, relPath); - const fullPath = path.join('src', relPath); - fs.mkdirSync(path.dirname(fullPath), { recursive: true }); - fs.writeFileSync(fullPath, content, 'utf8'); - const hasCn = /[\u4e00-\u9fff]/.test(content); - console.log(`OK ${relPath} (cn=${hasCn})`); - } -} - -main().catch((err) => { - console.error(err); - process.exit(1); -}); diff --git a/frontend/platform_admin/scripts/restore-zh-cn-locales.mjs b/frontend/platform_admin/scripts/restore-zh-cn-locales.mjs deleted file mode 100644 index 44e289a..0000000 --- a/frontend/platform_admin/scripts/restore-zh-cn-locales.mjs +++ /dev/null @@ -1,109 +0,0 @@ -import fs from 'node:fs'; -import path from 'node:path'; - -const BASE = - 'https://raw.githubusercontent.com/arco-design/arco-design-pro-vue/main/arco-design-pro-vite/src'; - -const localeFiles = [ - 'locale/zh-CN/settings.ts', - 'views/login/locale/zh-CN.ts', - 'views/form/group/locale/zh-CN.ts', - 'views/form/step/locale/zh-CN.ts', - 'views/dashboard/workplace/locale/zh-CN.ts', - 'views/dashboard/monitor/locale/zh-CN.ts', - 'views/list/card/locale/zh-CN.ts', - 'views/list/search-table/locale/zh-CN.ts', - 'views/profile/basic/locale/zh-CN.ts', - 'views/result/success/locale/zh-CN.ts', - 'views/result/error/locale/zh-CN.ts', - 'views/exception/403/locale/zh-CN.ts', - 'views/exception/404/locale/zh-CN.ts', - 'views/user/info/locale/zh-CN.ts', - 'views/user/setting/locale/zh-CN.ts', - 'views/visualization/data-analysis/locale/zh-CN.ts', - 'views/visualization/multi-dimension-data-analysis/locale/zh-CN.ts', -]; - -const rootZhCN = `import { mergeLocaleModules } from './merge-locales'; -import localeSettings from './zh-CN/settings'; - -const componentLocales = mergeLocaleModules( - import.meta.glob('@/components/**/locale/zh-CN.ts', { eager: true }), -); -const viewLocales = mergeLocaleModules( - import.meta.glob('@/views/**/locale/zh-CN.ts', { eager: true }), -); - -export default { - 'menu.dashboard': '仪表盘', - 'menu.server.dashboard': '仪表盘-服务端', - 'menu.server.workplace': '工作台-服务端', - 'menu.server.monitor': '实时监控-服务端', - 'menu.list': '列表页', - 'menu.result': '结果页', - 'menu.exception': '异常页', - 'menu.form': '表单页', - 'menu.profile': '详情页', - 'menu.visualization': '数据可视化', - 'menu.user': '个人中心', - 'menu.arcoWebsite': 'Arco Design', - 'menu.faq': '常见问题', - 'navbar.docs': '文档中心', - 'navbar.action.locale': '切换为中文', - ...localeSettings, - ...componentLocales, - ...viewLocales, -}; -`; - -async function download(relPath) { - const url = `${BASE}/${relPath}`; - const res = await fetch(url); - if (!res.ok) { - throw new Error(`${relPath}: HTTP ${res.status}`); - } - return res.text(); -} - -async function main() { - for (const relPath of localeFiles) { - const content = await download(relPath); - const dest = path.join('src', relPath.replace(/^locale\//, 'locale/')); - const fullPath = path.join('src', relPath); - fs.mkdirSync(path.dirname(fullPath), { recursive: true }); - fs.writeFileSync(fullPath, content, 'utf8'); - const hasCn = /[\u4e00-\u9fff]/.test(content); - console.log(`OK ${relPath} (cn=${hasCn})`); - } - - fs.writeFileSync(path.join('src', 'locale/zh-CN.ts'), rootZhCN, 'utf8'); - console.log('OK locale/zh-CN.ts (cn=true)'); - - // search-table column setting label - const stPath = path.join('src', 'views/list/search-table/index.vue'); - let st = fs.readFileSync(stPath, 'utf8'); - st = st.replace( - "{{ item.title === '#' ? '???' : item.title }}", - "{{ item.title === '#' ? '序列号' : item.title }}", - ); - fs.writeFileSync(stPath, st, 'utf8'); - console.log('OK search-table/index.vue'); - - // verify - let bad = 0; - for (const relPath of ['locale/zh-CN.ts', ...localeFiles]) { - const fullPath = path.join('src', relPath); - const text = fs.readFileSync(fullPath, 'utf8'); - if (text.includes("'???'") || text.includes("'??'")) { - console.error('STILL BAD:', relPath); - bad += 1; - } - } - if (bad) process.exit(1); - console.log('All locale files verified'); -} - -main().catch((err) => { - console.error(err); - process.exit(1); -}); diff --git a/frontend/platform_admin/scripts/sync-backend-contract.mjs b/frontend/platform_admin/scripts/sync-backend-contract.mjs new file mode 100644 index 0000000..64e6146 --- /dev/null +++ b/frontend/platform_admin/scripts/sync-backend-contract.mjs @@ -0,0 +1,16 @@ +import { execFileSync } from 'node:child_process'; +import { mkdirSync, writeFileSync } from 'node:fs'; +import { dirname, resolve } from 'node:path'; +import { fileURLToPath } from 'node:url'; + +const root = resolve(dirname(fileURLToPath(import.meta.url)), '..'); +const backend = resolve(root, '../../backend/api'); +const output = resolve(root, 'src/contracts/platform-resources.json'); +const contract = execFileSync('go', ['run', './cmd/cli', 'resource-contract'], { + cwd: backend, + encoding: 'utf8', +}); +JSON.parse(contract); +mkdirSync(dirname(output), { recursive: true }); +writeFileSync(output, `${contract.trim()}\n`); +console.log(`已同步后端资源契约:${output}`); diff --git a/frontend/platform_admin/src/App.vue b/frontend/platform_admin/src/App.vue deleted file mode 100644 index ea794a3..0000000 --- a/frontend/platform_admin/src/App.vue +++ /dev/null @@ -1,113 +0,0 @@ - - - - - diff --git a/frontend/platform_admin/src/api/delivery.ts b/frontend/platform_admin/src/api/delivery.ts deleted file mode 100644 index 1573358..0000000 --- a/frontend/platform_admin/src/api/delivery.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { resourceApi } from './resource'; - -/** 配送点管理接口。 */ -export type DeliveryBasic = { identity: string; delivery_code: string; name: string; principal: string; address: string; status: string }; -export const deliveryApi = { list: () => resourceApi.list('/delivery/delivery_basic'), create: (data: Record) => resourceApi.create('/delivery/delivery_basic', data) }; diff --git a/frontend/platform_admin/src/api/gas.ts b/frontend/platform_admin/src/api/gas.ts deleted file mode 100644 index 9972b0c..0000000 --- a/frontend/platform_admin/src/api/gas.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { resourceApi } from './resource'; - -/** 可燃气体站管理接口。 */ -export type GasBasic = { identity: string; code: string; name: string; principal: string; address: string; status: string }; -export const gasApi = { list: () => resourceApi.list('/gas/gas_basic'), create: (data: Record) => resourceApi.create('/gas/gas_basic', data) }; diff --git a/frontend/platform_admin/src/api/platform.ts b/frontend/platform_admin/src/api/platform.ts index 2473a15..ba5ac4b 100644 --- a/frontend/platform_admin/src/api/platform.ts +++ b/frontend/platform_admin/src/api/platform.ts @@ -7,17 +7,17 @@ export type PlatformMenu = { identity: string; parent_identity?: string; menu_co export const platformApi = { overview: () => request>('/dashboard/overview'), - listAccount: () => request<{ total: number; list: Record[] }>('/platform/platfrom_account'), - listRole: () => resourceApi.list('/platform/platform_role'), - createRole: (data: Record) => resourceApi.create('/platform/platform_role', data), - listMenu: () => request<{ total: number; list: PlatformMenu[] }>('/platform/platform_menu'), + listAccount: () => request<{ total: number; list: Record[] }>('/platfrom_account'), + listRole: () => resourceApi.list('/platform_role'), + createRole: (data: Record) => resourceApi.create('/platform_role', data), + listMenu: () => request<{ total: number; list: PlatformMenu[] }>('/platform_menu'), listRoleMenuIdentities: (identity: string) => request<{ menu_identities: string[] }>( - `/platform/platform_role/${identity}/menu`, + `/platform_role/${identity}/menu`, ), replaceRoleMenus: (identity: string, menuIdentities: string[]) => request<{ updated: boolean }>( - `/platform/platform_role/${identity}/menu`, + `/platform_role/${identity}/menu`, { method: 'PUT', body: JSON.stringify({ menu_identities: menuIdentities }), diff --git a/frontend/platform_admin/src/api/resource-form.ts b/frontend/platform_admin/src/api/resource-form.ts index d285415..93ec25e 100644 --- a/frontend/platform_admin/src/api/resource-form.ts +++ b/frontend/platform_admin/src/api/resource-form.ts @@ -23,7 +23,7 @@ export function buildResourcePayload( for (const field of fields) { if (mode === 'edit' && field.type === 'password') continue; const value = form[field.key]; - if (field.type === 'menu-identities' && Array.isArray(value)) { + if (field.type === 'identity-list' && Array.isArray(value)) { payload[field.key] = value; continue; } diff --git a/frontend/platform_admin/src/api/resource.ts b/frontend/platform_admin/src/api/resource.ts index 09067ee..270865d 100644 --- a/frontend/platform_admin/src/api/resource.ts +++ b/frontend/platform_admin/src/api/resource.ts @@ -11,4 +11,6 @@ export const resourceApi = { update: (resource: string, identity: string, data: Record) => request(`${resource}/${identity}`, { method: 'PUT', body: JSON.stringify(data) }), updateStatus: (resource: string, identity: string, status: string) => request<{ updated: boolean }>(`${resource}/${identity}/status`, { method: 'PATCH', body: JSON.stringify({ status }) }), archive: (resource: string, identity: string) => request<{ updated: boolean }>(`${resource}/${identity}`, { method: 'DELETE' }), + action: (resource: string, method: 'POST' | 'PUT' | 'PATCH', data: Record) => + request(resource, { method, body: JSON.stringify(data) }), }; diff --git a/frontend/platform_admin/src/api/resources.ts b/frontend/platform_admin/src/api/resources.ts index c626e59..12f7341 100644 --- a/frontend/platform_admin/src/api/resources.ts +++ b/frontend/platform_admin/src/api/resources.ts @@ -1,54 +1,61 @@ -export type ResourceMode = 'writable' | 'readonly' | 'append_only'; +export type ResourceMode = 'writable' | 'readonly' | 'append_only' | 'managed'; export type ResourcePageKind = 'list' | 'tree'; export type ResourceFieldType = | 'text' | 'password' | 'identity' - | 'role-code' - | 'menu-identities' + | 'identity-list' | 'number' | 'boolean' | 'date' | 'datetime' - | 'textarea'; + | 'textarea' + | 'select'; + export type ResourceField = { key: string; label: string; - type: ResourceFieldType; + type?: ResourceFieldType; required?: boolean; + relation?: string; + options?: Array<{ label: string; value: string }>; }; + export type DetailAction = { name: string; resource: string; - fields: ResourceField[]; - payload?: Record; + method?: 'POST' | 'PUT' | 'PATCH'; + danger?: boolean; + fields?: ResourceField[]; }; + export type ResourceUiDefinition = { key: string; name: string; - resource: string; + resource: `/${string}`; title: string; mode: ResourceMode; pageKind: ResourcePageKind; fields: ResourceField[]; - requiredIdentities: string[]; detailActions?: DetailAction[]; }; -const labels: Record = { +const fieldLabels: Record = { code: '编码', name: '名称', - credit_code: '统一信用代码', - principal: '负责人', - address: '地址', - longitude: '经度', - latitude: '纬度', username: '用户名', password: '密码', display_name: '显示名称', role_code: '角色编码', - delivery_code: '配送编码', + platform_role_code: '平台角色', + credit_code: '统一社会信用代码', + principal: '负责人', + manager: '库房负责人', phone: '联系电话', + address: '地址', + longitude: '经度', + latitude: '纬度', + delivery_code: '配送站编码', avatar: '头像', work_status: '工作状态', credential_type: '资质类型', @@ -56,483 +63,265 @@ const labels: Record = { expired_at: '到期时间', real_name: '实名姓名', is_default: '默认地址', - device_no: '设备编号', - model: '设备型号', - online_status: '在线状态', - effective_at: '生效时间', - reported_at: '上报时间', - payload: '遥测数据', - version_no: '版本号', + params: '产品参数', + produced_at: '生产时间', + enabled_at: '启用时间', + is_enabled: '是否启用', + repair_no: '检修单号', + repair_type: '检修类型', + started_at: '开始时间', + completed_at: '完成时间', + result: '检修结果', + target_status: '目标状态', + content: '内容', + operator: '操作人员', + remark: '备注', + action: '动作', + occurred_at: '发生时间', + reason: '原因', + operator_identity: '操作人标识', + operator_name: '操作人', + owner_type: '归属类型', + owner_identity: '归属标识', + alipay_id: '支付宝账号', + alipay_name: '支付宝账户名', + wxpay_id: '微信账号', + wxpay_name: '微信账户名', + balance: '余额(分)', + withdrawal_balance: '可提现余额(分)', + card_no_last4: '银行卡末四位', + bank_name: '银行名称', + card_owner: '持卡人', + payment_no: '支付单号', + order_no: '订单号', + trade_no: '第三方流水号', + payment_type: '支付业务类型', + pay_channel: '支付渠道', + pay_type: '支付类型', + amount: '金额(分)', + fee: '手续费(分)', + args: '支付参数', + callback_msg: '回调信息', + record_no: '流水号', + request_no: '请求流水号', + direction: '收支方向', + trade_type: '交易类型', + balance_after: '变动后余额(分)', + withdrawal_balance_after: '变动后可提现余额(分)', + refund_no: '退款单号', + cash_no: '提现单号', + channel: '渠道', + review_reason: '审核原因', + reviewed_at: '审核时间', + ymd: '日期', + ym: '月份', + contract_no: '合同编号', title: '标题', - reason: '处置原因', - sort_no: '排序号', + terms: '合同条款', + file_uri: '附件地址', + default_delivery_fee: '默认配送费(分)', + signed_at: '签署时间', + effective_at: '生效时间', + unit_price: '单价(分)', + unbound_at: '解绑时间', + revision_no: '修订号', + creator_type: '创建方类型', + creator_identity: '创建方标识', + contact_name: '联系人', + contact_phone: '联系电话', + discount_amount: '优惠金额(分)', + total_amount: '总金额(分)', + delivery_fee: '配送费(分)', + assigned_at: '分配时间', + from_status: '原状态', + to_status: '新状态', + track_no: '轨迹编号', + attempt_no: '尝试次数', + point_type: '轨迹点类型', + confirmed_at: '确认时间', + sort_no: '排序', product_code: '商品编码', - price_amount: '售价', + price_amount: '售价(分)', stock_quantity: '库存', value: '属性值', image_uri: '图片地址', is_cover: '封面图', quantity: '数量', selected: '是否选中', - order_no: '订单号', - total_amount: '订单金额', product_snapshot: '商品快照', - sale_amount: '成交金额', + sale_amount: '成交金额(分)', score: '评分', - channel: '渠道', - amount: '金额', paid_at: '支付时间', settlement_no: '结算单号', - subject_type: '结算对象类型', + subject_type: '结算主体类型', period_start: '结算开始时间', period_end: '结算结束时间', bill_date: '账单日期', - difference_amount: '差异金额', + difference_amount: '差异金额(分)', content_type: '内容类型', body: '正文', + version_no: '版本号', publish_status: '发布状态', - template_code: '模板编码', - content: '内容', ticket_no: '工单号', category: '分类', priority: '优先级', - platform_role_code: '平台角色', - menu_identities: '菜单权限', data_scope: '数据范围', + parent_identity: '父级', menu_code: '菜单编码', icon: '图标', - path: '路径', - balance_amount: '余额', - frozen_amount: '冻结金额', - balance_after: '变动后余额', - report_code: '报表编码', - report_type: '报表类型', - stat_period: '统计周期', - generated_at: '生成时间', - dimension: '维度', - metric_code: '指标编码', - metric_value: '指标值', - scope_type: '范围类型', - stat_at: '统计时间', - applicant_identity: '申请人标识', - business_type: '业务类型', - business_identity: '业务标识', - handler_identity: '处理人标识', - opinion: '审批意见', - purpose: '用途', - field_scope: '字段范围', - approved_at: '批准时间', - file_uri: '文件地址', - operator_identity: '操作人标识', - object_identity: '对象标识', - resource_type: '资源类型', - handled_at: '处理时间', - created_at: '创建时间', + path: '路由', + menu_identities: '菜单权限', status: '状态', }; -const numberFields = new Set([ - 'version_no', - 'level', - 'sort_no', - 'price_amount', - 'stock_quantity', - 'quantity', - 'total_amount', - 'sale_amount', - 'score', - 'amount', - 'difference_amount', - 'balance_amount', - 'frozen_amount', - 'balance_after', + +const numbers = new Set([ + 'sort_no', 'unit_price', 'amount', 'fee', 'balance', 'withdrawal_balance', + 'balance_after', 'withdrawal_balance_after', 'default_delivery_fee', + 'discount_amount', 'total_amount', 'delivery_fee', 'price_amount', + 'stock_quantity', 'quantity', 'sale_amount', 'score', 'version_no', + 'difference_amount', 'attempt_no', ]); -const booleanFields = new Set(['is_default', 'is_cover', 'selected']); -const dateFields = new Set(['bill_date']); -const datetimeFields = new Set([ - 'expired_at', - 'effective_at', - 'reported_at', - 'occurred_at', - 'started_at', - 'completed_at', - 'paid_at', - 'period_start', - 'period_end', - 'generated_at', - 'stat_at', - 'approved_at', - 'handled_at', - 'created_at', +const booleans = new Set(['is_default', 'is_enabled', 'is_cover', 'selected', 'withdrawable']); +const dates = new Set(['bill_date']); +const datetimes = new Set([ + 'expired_at', 'produced_at', 'enabled_at', 'started_at', 'completed_at', + 'occurred_at', 'reviewed_at', 'signed_at', 'effective_at', 'unbound_at', + 'assigned_at', 'confirmed_at', 'paid_at', 'period_start', 'period_end', ]); -// 大文本字段按普通文本处理,不再要求或解析 JSON。 -const textareaFields = new Set([ - 'body', - 'content', - 'reason', - 'opinion', - 'payload', - 'product_snapshot', +const textareas = new Set([ + 'params', 'content', 'remark', 'reason', 'terms', 'args', 'callback_msg', + 'review_reason', 'product_snapshot', 'body', ]); -const fieldType = (key: string): ResourceFieldType => { - if (key === 'platform_role_code') return 'role-code'; - if (key === 'menu_identities') return 'menu-identities'; - if (key.endsWith('_identity')) return 'identity'; - if (key === 'password') return 'password'; - if (numberFields.has(key)) return 'number'; - if (booleanFields.has(key)) return 'boolean'; - if (dateFields.has(key)) return 'date'; - if (datetimeFields.has(key)) return 'datetime'; - if (textareaFields.has(key)) return 'textarea'; - return 'text'; -}; -const field = (value: string): ResourceField => { - const key = value.replace(/!$/, ''); - return { - key, - label: - labels[key] ?? (key.endsWith('_identity') ? '关联业务标识' : '业务字段'), - type: fieldType(key), - required: value.endsWith('!') || undefined, - }; -}; -const titles: Record = { - gas_basic: '气站管理', - gas_account: '气站账户', - delivery_basic: '配送点管理', - delivery_account: '配送账户', - delivery_task: '配送任务', - delivery_track: '配送轨迹', - delivery_track_point: '轨迹点', - staff_account: '服务人员', - staff_credential: '人员资质', - user_account: '用户账户', - user_address: '用户地址', - user_service_relation: '用户服务关系', - dev_smart_cylinder_valve: '智能钢瓶阀', - dev_device_binding: '设备绑定', - dev_telemetry: '设备遥测', - ec_category: '商品分类', - ec_product: '商品管理', - ec_product_attribute: '商品属性', - ec_product_image: '商品图片', - ec_cart: '购物车', - ec_order: '订单管理', - ec_order_item: '订单明细', - ec_review: '商品评价', - fin_payment: '支付记录', - fin_settlement: '财务结算', - fin_reconciliation: '财务对账', - cms_content: '内容管理', - cs_ticket: '客服工单', - platfrom_account: '平台账户', - platform_role: '平台角色', - platform_menu: '平台菜单', - wallet: '钱包', - wallet_ledger: '钱包流水', - wallet_recharge: '钱包充值', - wallet_withdrawal: '钱包提现', -}; -const define = ( + +function f(key: string, options: Partial = {}): ResourceField { + const type: ResourceFieldType = key.endsWith('_identity') + ? 'identity' + : numbers.has(key) + ? 'number' + : booleans.has(key) + ? 'boolean' + : dates.has(key) + ? 'date' + : datetimes.has(key) + ? 'datetime' + : textareas.has(key) + ? 'textarea' + : key === 'password' + ? 'password' + : 'text'; + return { key, label: fieldLabels[key] ?? key, type, ...options }; +} + +function relation(key: string, resource: string, required = false): ResourceField { + return f(key, { type: 'identity', relation: resource, required }); +} + +function define( name: string, - resource: string, + title: string, mode: ResourceMode, - pageKind: ResourcePageKind, - keys: string[], + fields: ResourceField[], + pageKind: ResourcePageKind = 'list', detailActions?: DetailAction[], -): ResourceUiDefinition => { - const fields = keys.map(field); +): ResourceUiDefinition { return { key: name.replace(/_/g, '-'), name, - resource, - title: titles[name] ?? '业务资源', + resource: `/${name}`, + title, mode, pageKind, fields, - requiredIdentities: fields - .filter((item) => item.required && item.key.endsWith('_identity')) - .map((item) => item.key), ...(detailActions ? { detailActions } : {}), }; -}; -const action = ( - name: string, - resource: string, - keys: string[], - payload?: Record, -): DetailAction => ({ - name, - resource, - fields: keys.map(field), - ...(payload ? { payload } : {}), -}); +} + +const reason = [f('reason', { required: true })]; -/** Exact UI contract for every backend ExpectedResources entry. */ export const resources: ResourceUiDefinition[] = [ - define('gas_basic', '/gas/gas_basic', 'writable', 'list', [ - 'code!', - 'name!', - 'credit_code', - 'principal', - 'address', - 'longitude', - 'latitude', + define('gas_basic', '气站', 'writable', [f('code', { required: true }), f('name', { required: true }), f('credit_code'), f('principal'), f('address'), f('longitude'), f('latitude')]), + define('gas_account', '气站账户', 'writable', [f('username', { required: true }), f('password', { required: true }), f('display_name'), f('role_code'), relation('gas_basic_identity', '/gas_basic', true)]), + define('delivery_basic', '配送站', 'writable', [f('delivery_code', { required: true }), f('name', { required: true }), relation('gas_basic_identity', '/gas_basic'), f('principal'), f('address')]), + define('delivery_account', '配送站账户', 'writable', [f('username', { required: true }), f('password', { required: true }), f('display_name'), f('role_code'), relation('delivery_basic_identity', '/delivery_basic', true)]), + define('staff_account', '工作人员', 'writable', [f('username', { required: true }), f('password', { required: true }), f('name', { required: true }), f('phone'), f('avatar'), f('role_code'), relation('gas_basic_identity', '/gas_basic'), relation('delivery_basic_identity', '/delivery_basic'), f('work_status')]), + define('staff_credential', '工作人员资质', 'writable', [relation('staff_account_identity', '/staff_account', true), f('credential_type', { required: true }), f('credential_no'), f('expired_at')]), + define('user_account', '用户账户', 'writable', [f('username', { required: true }), f('password', { required: true }), f('name', { required: true }), f('phone'), f('avatar'), f('real_name')]), + define('user_address', '用户地址', 'writable', [relation('user_account_identity', '/user_account', true), f('address', { required: true }), f('longitude'), f('latitude'), f('is_default')]), + define('user_service_relation', '用户服务关系', 'writable', [relation('user_account_identity', '/user_account', true), relation('gas_basic_identity', '/gas_basic'), relation('delivery_basic_identity', '/delivery_basic'), relation('staff_account_identity', '/staff_account')]), + + define('product_type', '产品类型', 'writable', [f('code', { required: true }), f('name', { required: true })]), + define('product_warehouse', '库房', 'writable', [f('code', { required: true }), f('name', { required: true }), f('address'), f('manager'), f('phone'), f('is_enabled')]), + define('product_info', '产品信息', 'managed', [f('code', { required: true }), f('name', { required: true }), relation('product_type_identity', '/product_type', true), f('params', { required: true }), relation('warehouse_identity', '/product_warehouse'), relation('gas_basic_identity', '/gas_basic'), relation('delivery_basic_identity', '/delivery_basic'), relation('user_account_identity', '/user_account'), f('produced_at', { required: true }), f('is_enabled')], 'list', [ + { name: '修改启用状态', resource: '/product_info/:identity/status', method: 'PATCH', fields: [f('status', { required: true, type: 'select', options: [{ label: '启用', value: 'enabled' }, { label: '停用', value: 'disabled' }] })] }, ]), - define('gas_account', '/gas/gas_account', 'writable', 'list', [ - 'username!', - 'password!', - 'display_name', - 'role_code', - 'gas_basic_identity!', + define('product_repair', '产品检修记录', 'writable', [relation('product_info_identity', '/product_info', true), f('repair_no', { required: true }), f('repair_type', { required: true }), f('started_at', { required: true }), f('completed_at'), f('result'), f('target_status'), f('content'), f('operator'), f('remark')]), + define('product_owner', '产品归属记录', 'append_only', [relation('product_info_identity', '/product_info', true), relation('warehouse_identity', '/product_warehouse'), relation('gas_basic_identity', '/gas_basic'), relation('delivery_basic_identity', '/delivery_basic'), relation('user_account_identity', '/user_account'), f('action', { required: true }), f('occurred_at', { required: true }), f('reason'), f('remark')]), + + define('gasorder_contract', '配送合同', 'managed', [f('contract_no', { required: true }), relation('user_account_identity', '/user_account', true), relation('gas_basic_identity', '/gas_basic', true), relation('delivery_basic_identity', '/delivery_basic'), f('title', { required: true }), f('terms'), f('file_uri'), f('default_delivery_fee'), f('signed_at', { required: true }), f('effective_at', { required: true }), f('expired_at')], 'list', [ + { name: '启用合同', resource: '/gasorder_contract/:identity/activate', fields: reason }, + { name: '续签合同', resource: '/gasorder_contract/:identity/renew', fields: [f('effective_at', { required: true }), f('expired_at'), ...reason] }, + { name: '终止合同', resource: '/gasorder_contract/:identity/terminate', danger: true, fields: reason }, ]), - define('delivery_basic', '/delivery/delivery_basic', 'writable', 'list', [ - 'delivery_code!', - 'name!', - 'gas_basic_identity', - 'principal', - 'address', + define('gasorder_contract_product', '合同气瓶', 'append_only', [relation('gasorder_contract_identity', '/gasorder_contract', true), relation('product_info_identity', '/product_info', true), f('unit_price')], 'list', [ + { name: '解绑气瓶', resource: '/gasorder_contract_product/:identity/unbind', danger: true, fields: reason }, ]), - define('delivery_account', '/delivery/delivery_account', 'writable', 'list', [ - 'username!', - 'password!', - 'display_name', - 'role_code', - 'delivery_basic_identity!', + define('gasorder_contract_revision', '合同修订记录', 'readonly', []), + define('gasorder_basic', '气体配送订单', 'managed', [f('request_no', { required: true }), relation('gasorder_contract_identity', '/gasorder_contract', true), f('creator_type', { required: true, type: 'select', options: [{ label: '用户', value: 'user' }, { label: '工作人员', value: 'staff' }, { label: '配送站', value: 'delivery' }, { label: '气站', value: 'gas' }] }), f('creator_identity', { required: true }), relation('user_address_identity', '/user_address', true), f('gasorder_contract_product_identities', { required: true, type: 'identity-list', relation: '/gasorder_contract_product' }), f('contact_name', { required: true }), f('contact_phone', { required: true }), f('discount_amount'), f('remark')], 'list', [ + { name: '分配订单', resource: '/gasorder_basic/:identity/assign', fields: [relation('delivery_basic_identity', '/delivery_basic', true), relation('staff_account_identity', '/staff_account', true), ...reason] }, + { name: '开始罐装', resource: '/gasorder_basic/:identity/filling', fields: reason }, + { name: '待配送', resource: '/gasorder_basic/:identity/ready', fields: reason }, + { name: '标记异常', resource: '/gasorder_basic/:identity/exception', fields: reason }, + { name: '恢复订单', resource: '/gasorder_basic/:identity/recover', fields: reason }, + { name: '取消订单', resource: '/gasorder_basic/:identity/cancel', danger: true, fields: reason }, ]), - define('delivery_task', '/delivery/delivery_task', 'writable', 'list', [ - 'ec_order_identity!', - 'delivery_basic_identity!', - 'staff_account_identity', + define('gasorder_item', '订单明细', 'readonly', []), + define('gasorder_assign', '分配记录', 'readonly', []), + define('gasorder_status', '状态记录', 'readonly', []), + define('gasorder_track', '运行轨迹', 'readonly', []), + define('gasorder_track_point', '轨迹点', 'readonly', []), + define('gasorder_confirm', '确认记录', 'readonly', []), + define('gasorder_payment', '支付记录', 'readonly', []), + + define('ec_category', '商品分类', 'writable', [relation('parent_identity', '/ec_category'), f('name', { required: true }), f('sort_no')], 'tree'), + define('ec_product', '商品', 'writable', [relation('ec_category_identity', '/ec_category', true), f('product_code', { required: true }), f('name', { required: true }), f('price_amount', { required: true }), f('stock_quantity')]), + define('ec_product_attribute', '商品属性', 'writable', [relation('ec_product_identity', '/ec_product', true), f('name', { required: true }), f('value', { required: true }), f('sort_no')]), + define('ec_product_image', '商品图片', 'writable', [relation('ec_product_identity', '/ec_product', true), f('image_uri', { required: true }), f('sort_no'), f('is_cover')]), + define('ec_cart', '购物车', 'writable', [relation('user_account_identity', '/user_account', true), relation('ec_product_identity', '/ec_product', true), f('quantity', { required: true }), f('selected')]), + define('ec_order', '商城订单', 'writable', [relation('user_account_identity', '/user_account', true), relation('gas_basic_identity', '/gas_basic'), relation('delivery_basic_identity', '/delivery_basic'), f('order_no', { required: true }), f('total_amount', { required: true })]), + define('ec_order_item', '商城订单明细', 'writable', [relation('ec_order_identity', '/ec_order', true), relation('ec_product_identity', '/ec_product', true), f('product_snapshot', { required: true }), f('quantity', { required: true }), f('sale_amount', { required: true })]), + define('ec_review', '商品评价', 'writable', [relation('ec_order_identity', '/ec_order', true), relation('ec_product_identity', '/ec_product', true), relation('user_account_identity', '/user_account', true), f('score', { required: true }), f('content', { required: true })]), + + define('wallet_basic', '钱包', 'managed', [f('owner_type'), f('owner_identity'), f('alipay_id'), f('alipay_name'), f('wxpay_id'), f('wxpay_name'), f('balance'), f('withdrawal_balance')], 'list', [ + { name: '后台充值', resource: '/wallet_basic/:identity/recharge', fields: [f('request_no', { required: true }), f('amount', { required: true }), f('withdrawable'), ...reason, f('remark')] }, + { name: '修改钱包状态', resource: '/wallet_basic/:identity/status', method: 'PATCH', fields: [f('status', { required: true, type: 'select', options: [{ label: '启用', value: 'enabled' }, { label: '停用', value: 'disabled' }, { label: '冻结', value: 'frozen' }] })] }, ]), - define('delivery_track', '/delivery/delivery_track', 'writable', 'list', [ - 'delivery_task_identity!', - 'started_at', - 'completed_at', + define('wallet_bank', '银行卡', 'readonly', []), + define('wallet_payment', '钱包支付记录', 'readonly', []), + define('wallet_record', '钱包流水', 'readonly', []), + define('wallet_refund', '退款记录', 'readonly', []), + define('wallet_apply_cash', '提现申请', 'managed', [], 'list', [ + { name: '审核通过', resource: '/wallet_apply_cash/:identity/approve', fields: reason }, + { name: '审核驳回', resource: '/wallet_apply_cash/:identity/reject', danger: true, fields: reason }, ]), - define( - 'delivery_track_point', - '/delivery/delivery_track_point', - 'readonly', - 'list', - [ - 'delivery_track_identity', - 'point_type', - 'occurred_at', - 'longitude', - 'latitude', - ], - ), - define('staff_account', '/staff/account', 'writable', 'list', [ - 'username!', - 'password!', - 'name!', - 'phone', - 'avatar', - 'role_code', - 'gas_basic_identity', - 'delivery_basic_identity', - 'work_status', - ]), - define('staff_credential', '/staff/credential', 'writable', 'list', [ - 'staff_account_identity!', - 'credential_type!', - 'credential_no', - 'expired_at', - ]), - define('user_account', '/user/account', 'writable', 'list', [ - 'username!', - 'password!', - 'name!', - 'phone', - 'avatar', - 'real_name', - ]), - define('user_address', '/user/address', 'writable', 'list', [ - 'user_account_identity!', - 'address!', - 'longitude', - 'latitude', - 'is_default', - ]), - define( - 'user_service_relation', - '/user/service_relation', - 'writable', - 'list', - [ - 'user_account_identity!', - 'gas_basic_identity', - 'delivery_basic_identity', - 'staff_account_identity', - ], - ), - define( - 'dev_smart_cylinder_valve', - '/device/dev_smart_cylinder_valve', - 'writable', - 'list', - ['device_no!', 'model', 'online_status', 'owner_identity'], - ), - define( - 'dev_device_binding', - '/device/dev_device_binding', - 'writable', - 'list', - [ - 'smart_cylinder_valve_identity!', - 'user_account_identity!', - 'effective_at', - 'expired_at', - ], - ), - define('dev_telemetry', '/device/dev_telemetry', 'readonly', 'list', [ - 'smart_cylinder_valve_identity', - 'reported_at', - 'payload', - ]), - define('ec_category', '/ec/ec_category', 'writable', 'tree', [ - 'parent_identity', - 'name!', - 'sort_no', - ]), - define('ec_product', '/ec/ec_product', 'writable', 'list', [ - 'ec_category_identity!', - 'product_code!', - 'name!', - 'price_amount!', - 'stock_quantity', - ]), - define( - 'ec_product_attribute', - '/ec/ec_product_attribute', - 'writable', - 'list', - ['ec_product_identity!', 'name!', 'value!', 'sort_no'], - ), - define('ec_product_image', '/ec/ec_product_image', 'writable', 'list', [ - 'ec_product_identity!', - 'image_uri!', - 'sort_no', - 'is_cover', - ]), - define('ec_cart', '/ec/ec_cart', 'writable', 'list', [ - 'user_account_identity!', - 'ec_product_identity!', - 'quantity!', - 'selected', - ]), - define('ec_order', '/ec/ec_order', 'writable', 'list', [ - 'user_account_identity!', - 'gas_basic_identity', - 'delivery_basic_identity', - 'order_no!', - 'total_amount!', - ]), - define('ec_order_item', '/ec/ec_order_item', 'writable', 'list', [ - 'ec_order_identity!', - 'ec_product_identity!', - 'product_snapshot!', - 'quantity!', - 'sale_amount!', - ]), - define('ec_review', '/ec/ec_review', 'writable', 'list', [ - 'ec_order_identity!', - 'ec_product_identity!', - 'user_account_identity!', - 'score!', - 'content!', - ]), - define('fin_payment', '/finance/fin_payment', 'writable', 'list', [ - 'ec_order_identity!', - 'channel!', - 'amount!', - 'paid_at', - ]), - define('fin_settlement', '/finance/fin_settlement', 'writable', 'list', [ - 'settlement_no!', - 'subject_type!', - 'subject_identity!', - 'period_start!', - 'period_end!', - ]), - define( - 'fin_reconciliation', - '/finance/fin_reconciliation', - 'writable', - 'list', - ['channel!', 'bill_date!', 'difference_amount!'], - ), - define('cms_content', '/content/cms_content', 'writable', 'list', [ - 'content_type!', - 'title!', - 'body!', - 'version_no', - 'publish_status', - ]), - define('cs_ticket', '/customer_service/cs_ticket', 'writable', 'list', [ - 'user_account_identity!', - 'ticket_no!', - 'category!', - 'priority!', - ]), - define('platfrom_account', '/platform/platfrom_account', 'writable', 'list', [ - 'username!', - 'password!', - 'display_name', - 'avatar', - 'platform_role_code!', - 'phone', - ]), - define( - 'platform_role', - '/platform/platform_role', - 'writable', - 'list', - ['role_code!', 'name!', 'data_scope!'], - [ - action( - '分配菜单', - '/platform/platform_role/:identity/menu', - ['menu_identities'], - ), - ], - ), - define('platform_menu', '/platform/platform_menu', 'writable', 'tree', [ - 'parent_identity', - 'menu_code!', - 'name!', - 'icon', - 'path', - 'sort_no', - ]), - define('wallet', '/wallet/wallet', 'readonly', 'list', [ - 'owner_identity', - 'balance_amount', - 'frozen_amount', - 'status', - ]), - define('wallet_ledger', '/wallet/wallet_ledger', 'readonly', 'list', [ - 'wallet_identity', - 'amount', - 'balance_after', - ]), - define('wallet_recharge', '/wallet/wallet_recharge', 'readonly', 'list', [ - 'wallet_identity', - 'amount', - 'status', - ]), - define('wallet_withdrawal', '/wallet/wallet_withdrawal', 'readonly', 'list', [ - 'wallet_identity', - 'amount', - 'status', + + define('fin_payment', '财务支付记录', 'writable', [relation('ec_order_identity', '/ec_order', true), f('channel', { required: true }), f('amount', { required: true }), f('paid_at')]), + define('fin_settlement', '财务结算', 'writable', [f('settlement_no', { required: true }), f('subject_type', { required: true }), f('subject_identity', { required: true }), f('period_start', { required: true }), f('period_end', { required: true })]), + define('fin_reconciliation', '财务对账', 'writable', [f('channel', { required: true }), f('bill_date', { required: true }), f('difference_amount', { required: true })]), + define('cms_content', '内容', 'writable', [f('content_type', { required: true }), f('title', { required: true }), f('body', { required: true }), f('version_no'), f('publish_status')]), + define('cs_ticket', '客服工单', 'writable', [relation('user_account_identity', '/user_account', true), f('ticket_no', { required: true }), f('category', { required: true }), f('priority', { required: true })]), + define('platfrom_account', '平台账户', 'writable', [f('username', { required: true }), f('password', { required: true }), f('display_name'), f('avatar'), f('platform_role_code', { required: true }), f('phone')]), + define('platform_role', '平台角色', 'writable', [f('role_code', { required: true }), f('name', { required: true }), f('data_scope', { required: true })], 'list', [ + { name: '分配菜单', resource: '/platform_role/:identity/menu', method: 'PUT', fields: [f('menu_identities', { type: 'identity-list', relation: '/platform_menu' })] }, ]), + define('platform_menu', '平台菜单', 'writable', [relation('parent_identity', '/platform_menu'), f('menu_code', { required: true }), f('name', { required: true }), f('icon'), f('path'), f('sort_no')], 'tree'), ]; -export const resourceByName = Object.fromEntries( +export const resourceByPath = Object.fromEntries( resources.map((definition) => [definition.resource, definition]), ) as Record; + export function getResource(resourcePath: string): ResourceUiDefinition { - const definition = resourceByName[resourcePath]; - if (!definition) throw new Error('Unknown resource: ' + resourcePath); + const definition = resourceByPath[resourcePath]; + if (!definition) throw new Error(`未知资源:${resourcePath}`); return definition; } diff --git a/frontend/platform_admin/src/api/staff.ts b/frontend/platform_admin/src/api/staff.ts deleted file mode 100644 index c78a339..0000000 --- a/frontend/platform_admin/src/api/staff.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { resourceApi } from './resource'; - -/** 服务人员管理接口。 */ -export type Staff = { identity: string; name: string; phone: string; role_code: string; work_status: string; status: string }; -export const staffApi = { list: () => resourceApi.list('/staff/account'), create: (data: Record) => resourceApi.create('/staff/account', data) }; diff --git a/frontend/platform_admin/src/api/user.ts b/frontend/platform_admin/src/api/user.ts deleted file mode 100644 index 9d7a521..0000000 --- a/frontend/platform_admin/src/api/user.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { resourceApi } from './resource'; - -/** 业主客户管理接口。 */ -export type User = { identity: string; name: string; phone: string; real_name: string; status: string }; -export const userApi = { list: () => resourceApi.list('/user/account'), create: (data: Record) => resourceApi.create('/user/account', data) }; diff --git a/frontend/platform_admin/src/app/App.vue b/frontend/platform_admin/src/app/App.vue index 8351aa2..e2462b4 100644 --- a/frontend/platform_admin/src/app/App.vue +++ b/frontend/platform_admin/src/app/App.vue @@ -1,24 +1,9 @@ diff --git a/frontend/platform_admin/src/app/main.ts b/frontend/platform_admin/src/app/main.ts index 2b3f56f..7564046 100644 --- a/frontend/platform_admin/src/app/main.ts +++ b/frontend/platform_admin/src/app/main.ts @@ -5,7 +5,6 @@ import globalComponents from '@/components'; import '@/assets/style/global.less'; import { setupHttp } from '@/plugins/http'; import directive from '@/directive'; -import i18n from '@/locale'; import router from '@/router'; import store from '@/store'; import setupErrorReport from '@/utils/error-report'; @@ -22,7 +21,6 @@ async function bootstrap() { app.use(ArcoVueIcon); app.use(router); app.use(store); - app.use(i18n); app.use(globalComponents); app.use(directive); diff --git a/frontend/platform_admin/src/components/breadcrumb/index.vue b/frontend/platform_admin/src/components/breadcrumb/index.vue index 9ae683a..1816b31 100644 --- a/frontend/platform_admin/src/components/breadcrumb/index.vue +++ b/frontend/platform_admin/src/components/breadcrumb/index.vue @@ -4,7 +4,7 @@ - {{ $t(item) }} + {{ item }} diff --git a/frontend/platform_admin/src/components/footer/index.vue b/frontend/platform_admin/src/components/footer/index.vue index 9a250cc..adcb74b 100644 --- a/frontend/platform_admin/src/components/footer/index.vue +++ b/frontend/platform_admin/src/components/footer/index.vue @@ -1,5 +1,5 @@ diff --git a/frontend/platform_admin/src/components/menu/index.vue b/frontend/platform_admin/src/components/menu/index.vue index 46b25d5..cbec586 100644 --- a/frontend/platform_admin/src/components/menu/index.vue +++ b/frontend/platform_admin/src/components/menu/index.vue @@ -1,6 +1,5 @@ - - diff --git a/frontend/platform_admin/src/components/tab-bar/tab-item.vue b/frontend/platform_admin/src/components/tab-bar/tab-item.vue index 7524cbe..eeca55c 100644 --- a/frontend/platform_admin/src/components/tab-bar/tab-item.vue +++ b/frontend/platform_admin/src/components/tab-bar/tab-item.vue @@ -10,7 +10,7 @@ @click="goto(itemData)" > - {{ $t(itemData.title) }} + {{ itemData.title }} { - return i18.locale.value; - }); - const changeLocale = (value: string) => { - if (i18.locale.value === value) { - return; - } - i18.locale.value = value; - localStorage.setItem('arco-locale', value); - Message.success(i18.t('navbar.action.locale')); - }; - return { - currentLocale, - changeLocale, - }; -} diff --git a/frontend/platform_admin/src/locale/en-US.ts b/frontend/platform_admin/src/locale/en-US.ts deleted file mode 100644 index ca1ed13..0000000 --- a/frontend/platform_admin/src/locale/en-US.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { mergeLocaleModules } from './merge-locales'; -import localeSettings from './en-US/settings'; - -const componentLocales = mergeLocaleModules( - import.meta.glob('@/components/**/locale/en-US.ts', { eager: true }), -); -const viewLocales = mergeLocaleModules( - import.meta.glob('@/views/**/locale/en-US.ts', { eager: true }), -); - -export default { - 'menu.dashboard': 'Dashboard', - 'menu.server.dashboard': 'Dashboard-Server', - 'menu.server.workplace': 'Workplace-Server', - 'menu.server.monitor': 'Monitor-Server', - 'menu.list': 'List', - 'menu.result': 'Result', - 'menu.exception': 'Exception', - 'menu.form': 'Form', - 'menu.profile': 'Profile', - 'menu.visualization': 'Data Visualization', - 'menu.user': 'User Center', - 'menu.arcoWebsite': 'Arco Design', - 'menu.faq': 'FAQ', - 'navbar.docs': 'Docs', - 'navbar.action.locale': 'Switch to English', - ...localeSettings, - ...componentLocales, - ...viewLocales, -}; diff --git a/frontend/platform_admin/src/locale/en-US/settings.ts b/frontend/platform_admin/src/locale/en-US/settings.ts deleted file mode 100644 index ea9dfb0..0000000 --- a/frontend/platform_admin/src/locale/en-US/settings.ts +++ /dev/null @@ -1,16 +0,0 @@ -export default { - 'settings.search': 'Search', - 'settings.searchPlaceholder': 'Search menus, pages, features...', - 'settings.language': 'Language', - 'settings.navbar.theme.toLight': 'Click to use light mode', - 'settings.navbar.theme.toDark': 'Click to use dark mode', - 'settings.navbar.screen.toFull': 'Click to switch to full screen mode', - 'settings.navbar.screen.toExit': 'Click to exit the full screen mode', - 'settings.navbar.alerts': 'alerts', - 'http.error.default': 'Error', - 'http.error.request': 'Request Error', - 'http.logout.title': 'Confirm logout', - 'http.logout.content': - 'You have been logged out, you can cancel to stay on this page, or log in again', - 'http.logout.okText': 'Re-Login', -}; diff --git a/frontend/platform_admin/src/locale/index.ts b/frontend/platform_admin/src/locale/index.ts deleted file mode 100644 index 798da10..0000000 --- a/frontend/platform_admin/src/locale/index.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { createI18n } from 'vue-i18n'; -import en from './en-US'; -import cn from './zh-CN'; - -export const LOCALE_OPTIONS = [ - { label: '中文', value: 'zh-CN' }, - { label: 'English', value: 'en-US' }, -]; -const defaultLocale = localStorage.getItem('arco-locale') || 'zh-CN'; - -const i18n = createI18n({ - locale: defaultLocale, - fallbackLocale: 'en-US', - legacy: false, - messages: { - 'en-US': en, - 'zh-CN': cn, - }, -}); - -export default i18n; diff --git a/frontend/platform_admin/src/locale/merge-locales.ts b/frontend/platform_admin/src/locale/merge-locales.ts deleted file mode 100644 index 176159f..0000000 --- a/frontend/platform_admin/src/locale/merge-locales.ts +++ /dev/null @@ -1,10 +0,0 @@ -type LocaleModule = { default: Record }; - -export function mergeLocaleModules( - modules: Record, -): Record { - return Object.values(modules).reduce( - (messages, module) => ({ ...messages, ...module.default }), - {}, - ); -} diff --git a/frontend/platform_admin/src/locale/zh-CN.ts b/frontend/platform_admin/src/locale/zh-CN.ts deleted file mode 100644 index 439308c..0000000 --- a/frontend/platform_admin/src/locale/zh-CN.ts +++ /dev/null @@ -1,43 +0,0 @@ -import { mergeLocaleModules } from './merge-locales'; -import localeSettings from './zh-CN/settings'; - -const componentLocales = mergeLocaleModules( - import.meta.glob('@/components/**/locale/zh-CN.ts', { eager: true }), -); -const viewLocales = mergeLocaleModules( - import.meta.glob('@/views/**/locale/zh-CN.ts', { eager: true }), -); - -export default { - 'menu.platform.dashboard': '运营概览', - 'menu.platform.gas': '可燃气体站管理', - 'menu.platform.gas.basic': '可燃气体站', - 'menu.platform.delivery': '配送管理', - 'menu.platform.delivery.basic': '配送点', - 'menu.platform.staff': '服务人员', - 'menu.platform.staff.list': '人员档案', - 'menu.platform.user': '业主客户', - 'menu.platform.user.list': '客户档案', - 'menu.platform.config': '平台配置', - 'menu.platform.config.account': '平台账号', - 'menu.platform.config.role': '角色管理', - 'menu.platform.config.menu': '菜单管理', - 'menu.dashboard': '仪表盘', - 'menu.server.dashboard': '仪表盘-服务端', - 'menu.server.workplace': '工作台-服务端', - 'menu.server.monitor': '实时监控-服务端', - 'menu.list': '列表页', - 'menu.result': '结果页', - 'menu.exception': '异常页', - 'menu.form': '表单页', - 'menu.profile': '详情页', - 'menu.visualization': '数据可视化', - 'menu.user': '个人中心', - 'menu.arcoWebsite': 'Arco Design', - 'menu.faq': '常见问题', - 'navbar.docs': '文档中心', - 'navbar.action.locale': '切换为中文', - ...localeSettings, - ...componentLocales, - ...viewLocales, -}; diff --git a/frontend/platform_admin/src/locale/zh-CN/settings.ts b/frontend/platform_admin/src/locale/zh-CN/settings.ts deleted file mode 100644 index 585e452..0000000 --- a/frontend/platform_admin/src/locale/zh-CN/settings.ts +++ /dev/null @@ -1,16 +0,0 @@ -export default { - 'settings.search': '搜索', - 'settings.searchPlaceholder': '搜索菜单、页面、功能...', - 'settings.language': '语言', - 'settings.navbar.theme.toLight': '点击切换为亮色模式', - 'settings.navbar.theme.toDark': '点击切换为暗黑模式', - 'settings.navbar.screen.toFull': '点击切换全屏模式', - 'settings.navbar.screen.toExit': '点击退出全屏模式', - 'settings.navbar.alerts': '消息通知', - 'http.error.default': '错误', - 'http.error.request': '请求错误', - 'http.logout.title': '确认登出', - 'http.logout.content': - '您已被登出,您可以取消以停留在此页面,或重新登录', - 'http.logout.okText': '重新登录', -}; diff --git a/frontend/platform_admin/src/main.ts b/frontend/platform_admin/src/main.ts deleted file mode 100644 index 8dd6bc1..0000000 --- a/frontend/platform_admin/src/main.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { createApp } from 'vue'; -import App from './App.vue'; -import './style.css'; - -createApp(App).mount('#app'); diff --git a/frontend/platform_admin/src/plugins/http.ts b/frontend/platform_admin/src/plugins/http.ts index d8b20f3..944073e 100644 --- a/frontend/platform_admin/src/plugins/http.ts +++ b/frontend/platform_admin/src/plugins/http.ts @@ -1,7 +1,6 @@ import { Message, Modal } from '@arco-design/web-vue'; import type { AxiosResponse, InternalAxiosRequestConfig } from 'axios'; import axios from 'axios'; -import i18n from '@/locale'; import { useUserStore } from '@/store'; import { getToken } from '@/utils/auth'; @@ -14,10 +13,6 @@ export interface HttpResponse { let initialized = false; -function t(key: string) { - return i18n.global.t(key); -} - export function setupHttp() { if (initialized) { return; @@ -45,7 +40,7 @@ export function setupHttp() { const res = response.data; if (res.code !== 20000) { Message.error({ - content: res.msg || t('http.error.default'), + content: res.msg || '请求失败', duration: 5 * 1000, }); if ( @@ -53,9 +48,9 @@ export function setupHttp() { response.config.url !== '/api/user/info' ) { Modal.error({ - title: t('http.logout.title'), - content: t('http.logout.content'), - okText: t('http.logout.okText'), + title: '登录已失效', + content: '当前登录状态已失效,请重新登录。', + okText: '重新登录', async onOk() { const userStore = useUserStore(); await userStore.logout(); @@ -63,13 +58,13 @@ export function setupHttp() { }, }); } - return Promise.reject(new Error(res.msg || t('http.error.default'))); + return Promise.reject(new Error(res.msg || '请求失败')); } return res as unknown as AxiosResponse; }, (error) => { Message.error({ - content: error.msg || t('http.error.request'), + content: error.msg || '网络请求失败', duration: 5 * 1000, }); return Promise.reject(error); diff --git a/frontend/platform_admin/src/router/routes/modules/content.ts b/frontend/platform_admin/src/router/routes/modules/content.ts deleted file mode 100644 index 8527763..0000000 --- a/frontend/platform_admin/src/router/routes/modules/content.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { DEFAULT_LAYOUT } from '../base'; -import type { AppRouteRecordRaw } from '../types'; -const routes: AppRouteRecordRaw[] = [{ - path: '/content', name: 'content', component: DEFAULT_LAYOUT, - meta: { locale: 'menu.platform.content', requiresAuth: true, icon: 'icon-apps', order: 17 }, - children: [ - { path: 'cms-content', name: 'content-cms-content', component: () => import('@/views/content/cms_content/ListPage.vue'), meta: { locale: 'menu.platform.content.cms_content', requiresAuth: true, menuCode: 'content' } } - ], -}]; -export default routes; diff --git a/frontend/platform_admin/src/router/routes/modules/customer_service.ts b/frontend/platform_admin/src/router/routes/modules/customer_service.ts deleted file mode 100644 index 5423e12..0000000 --- a/frontend/platform_admin/src/router/routes/modules/customer_service.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { DEFAULT_LAYOUT } from '../base'; -import type { AppRouteRecordRaw } from '../types'; -const routes: AppRouteRecordRaw[] = [{ - path: '/customer_service', name: 'customer_service', component: DEFAULT_LAYOUT, - meta: { locale: 'menu.platform.customer_service', requiresAuth: true, icon: 'icon-apps', order: 19 }, - children: [ - { path: 'cs-ticket', name: 'customer_service-cs-ticket', component: () => import('@/views/customer_service/cs_ticket/ListPage.vue'), meta: { locale: 'menu.platform.customer_service.cs_ticket', requiresAuth: true, menuCode: 'customer_service' } } - ], -}]; -export default routes; diff --git a/frontend/platform_admin/src/router/routes/modules/delivery.ts b/frontend/platform_admin/src/router/routes/modules/delivery.ts deleted file mode 100644 index 84a882b..0000000 --- a/frontend/platform_admin/src/router/routes/modules/delivery.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { DEFAULT_LAYOUT } from '../base'; -import type { AppRouteRecordRaw } from '../types'; -const routes: AppRouteRecordRaw[] = [{ - path: '/delivery', name: 'delivery', component: DEFAULT_LAYOUT, - meta: { locale: 'menu.platform.delivery', requiresAuth: true, icon: 'icon-apps', order: 11 }, - children: [ - { path: 'delivery-basic', name: 'delivery-delivery-basic', component: () => import('@/views/delivery/delivery_basic/ListPage.vue'), meta: { locale: 'menu.platform.delivery.delivery_basic', requiresAuth: true, menuCode: 'delivery' } }, - { path: 'delivery-account', name: 'delivery-delivery-account', component: () => import('@/views/delivery/delivery_account/ListPage.vue'), meta: { locale: 'menu.platform.delivery.delivery_account', requiresAuth: true, menuCode: 'delivery' } }, - { path: 'delivery-task', name: 'delivery-delivery-task', component: () => import('@/views/delivery/delivery_task/ListPage.vue'), meta: { locale: 'menu.platform.delivery.delivery_task', requiresAuth: true, menuCode: 'delivery' } }, - { path: 'delivery-track', name: 'delivery-delivery-track', component: () => import('@/views/delivery/delivery_track/ListPage.vue'), meta: { locale: 'menu.platform.delivery.delivery_track', requiresAuth: true, menuCode: 'delivery' } }, - { path: 'delivery-track-point', name: 'delivery-delivery-track-point', component: () => import('@/views/delivery/delivery_track_point/ListPage.vue'), meta: { locale: 'menu.platform.delivery.delivery_track_point', requiresAuth: true, menuCode: 'delivery' } } - ], -}]; -export default routes; diff --git a/frontend/platform_admin/src/router/routes/modules/device.ts b/frontend/platform_admin/src/router/routes/modules/device.ts deleted file mode 100644 index d5f7c49..0000000 --- a/frontend/platform_admin/src/router/routes/modules/device.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { DEFAULT_LAYOUT } from '../base'; -import type { AppRouteRecordRaw } from '../types'; -const routes: AppRouteRecordRaw[] = [{ - path: '/device', name: 'device', component: DEFAULT_LAYOUT, - meta: { locale: 'menu.platform.device', requiresAuth: true, icon: 'icon-apps', order: 14 }, - children: [ - { path: 'dev-smart-cylinder-valve', name: 'device-dev-smart-cylinder-valve', component: () => import('@/views/device/dev_smart_cylinder_valve/ListPage.vue'), meta: { locale: 'menu.platform.device.dev_smart_cylinder_valve', requiresAuth: true, menuCode: 'device' } }, - { path: 'dev-device-binding', name: 'device-dev-device-binding', component: () => import('@/views/device/dev_device_binding/ListPage.vue'), meta: { locale: 'menu.platform.device.dev_device_binding', requiresAuth: true, menuCode: 'device' } }, - { path: 'dev-telemetry', name: 'device-dev-telemetry', component: () => import('@/views/device/dev_telemetry/ListPage.vue'), meta: { locale: 'menu.platform.device.dev_telemetry', requiresAuth: true, menuCode: 'device' } } - ], -}]; -export default routes; diff --git a/frontend/platform_admin/src/router/routes/modules/ec.ts b/frontend/platform_admin/src/router/routes/modules/ec.ts deleted file mode 100644 index 6d75423..0000000 --- a/frontend/platform_admin/src/router/routes/modules/ec.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { DEFAULT_LAYOUT } from '../base'; -import type { AppRouteRecordRaw } from '../types'; -const routes: AppRouteRecordRaw[] = [{ path: '/ec', name: 'ec', component: DEFAULT_LAYOUT, meta: { locale: 'menu.platform.ec', requiresAuth: true, icon: 'icon-apps', order: 16 }, children: [ - { path: 'ec-category', name: 'ec-ec-category', component: () => import('@/views/ec/ec_category/TreePage.vue'), meta: { locale: 'menu.platform.ec.ec_category', requiresAuth: true, menuCode: 'ec' } }, - { path: 'ec-product', name: 'ec-ec-product', component: () => import('@/views/ec/ec_product/ListPage.vue'), meta: { locale: 'menu.platform.ec.ec_product', requiresAuth: true, menuCode: 'ec' } }, - { path: 'ec-product-attribute', name: 'ec-ec-product-attribute', component: () => import('@/views/ec/ec_product_attribute/ListPage.vue'), meta: { locale: 'menu.platform.ec.ec_product_attribute', requiresAuth: true, menuCode: 'ec' } }, - { path: 'ec-product-image', name: 'ec-ec-product-image', component: () => import('@/views/ec/ec_product_image/ListPage.vue'), meta: { locale: 'menu.platform.ec.ec_product_image', requiresAuth: true, menuCode: 'ec' } }, - { path: 'ec-cart', name: 'ec-ec-cart', component: () => import('@/views/ec/ec_cart/ListPage.vue'), meta: { locale: 'menu.platform.ec.ec_cart', requiresAuth: true, menuCode: 'ec' } }, - { path: 'ec-order', name: 'ec-ec-order', component: () => import('@/views/ec/ec_order/ListPage.vue'), meta: { locale: 'menu.platform.ec.ec_order', requiresAuth: true, menuCode: 'ec' } }, - { path: 'ec-order-item', name: 'ec-ec-order-item', component: () => import('@/views/ec/ec_order_item/ListPage.vue'), meta: { locale: 'menu.platform.ec.ec_order_item', requiresAuth: true, menuCode: 'ec' } }, - { path: 'ec-review', name: 'ec-ec-review', component: () => import('@/views/ec/ec_review/ListPage.vue'), meta: { locale: 'menu.platform.ec.ec_review', requiresAuth: true, menuCode: 'ec' } } -] }]; -export default routes; diff --git a/frontend/platform_admin/src/router/routes/modules/finance.ts b/frontend/platform_admin/src/router/routes/modules/finance.ts deleted file mode 100644 index 59c1a66..0000000 --- a/frontend/platform_admin/src/router/routes/modules/finance.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { DEFAULT_LAYOUT } from '../base'; -import type { AppRouteRecordRaw } from '../types'; -const routes: AppRouteRecordRaw[] = [{ - path: '/finance', name: 'finance', component: DEFAULT_LAYOUT, - meta: { locale: 'menu.platform.finance', requiresAuth: true, icon: 'icon-apps', order: 16 }, - children: [ - { path: 'fin-payment', name: 'finance-fin-payment', component: () => import('@/views/finance/fin_payment/ListPage.vue'), meta: { locale: 'menu.platform.finance.fin_payment', requiresAuth: true, menuCode: 'finance' } }, - { path: 'fin-settlement', name: 'finance-fin-settlement', component: () => import('@/views/finance/fin_settlement/ListPage.vue'), meta: { locale: 'menu.platform.finance.fin_settlement', requiresAuth: true, menuCode: 'finance' } }, - { path: 'fin-reconciliation', name: 'finance-fin-reconciliation', component: () => import('@/views/finance/fin_reconciliation/ListPage.vue'), meta: { locale: 'menu.platform.finance.fin_reconciliation', requiresAuth: true, menuCode: 'finance' } } - ], -}]; -export default routes; diff --git a/frontend/platform_admin/src/router/routes/modules/gas.ts b/frontend/platform_admin/src/router/routes/modules/gas.ts deleted file mode 100644 index 50518ae..0000000 --- a/frontend/platform_admin/src/router/routes/modules/gas.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { DEFAULT_LAYOUT } from '../base'; -import type { AppRouteRecordRaw } from '../types'; -const routes: AppRouteRecordRaw[] = [{ - path: '/gas', name: 'gas', component: DEFAULT_LAYOUT, - meta: { locale: 'menu.platform.gas', requiresAuth: true, icon: 'icon-apps', order: 10 }, - children: [ - { path: 'gas-basic', name: 'gas-gas-basic', component: () => import('@/views/gas/gas_basic/ListPage.vue'), meta: { locale: 'menu.platform.gas.gas_basic', requiresAuth: true, menuCode: 'gas' } }, - { path: 'gas-account', name: 'gas-gas-account', component: () => import('@/views/gas/gas_account/ListPage.vue'), meta: { locale: 'menu.platform.gas.gas_account', requiresAuth: true, menuCode: 'gas' } } - ], -}]; -export default routes; diff --git a/frontend/platform_admin/src/router/routes/modules/platform.ts b/frontend/platform_admin/src/router/routes/modules/platform.ts index 7d9b4a4..f96b8d6 100644 --- a/frontend/platform_admin/src/router/routes/modules/platform.ts +++ b/frontend/platform_admin/src/router/routes/modules/platform.ts @@ -1,21 +1,118 @@ import { DEFAULT_LAYOUT } from '../base'; import type { AppRouteRecordRaw } from '../types'; -import gasRoutes from './gas'; -import deliveryRoutes from './delivery'; -import staffRoutes from './staff'; -import userRoutes from './user'; -import deviceRoutes from './device'; -import ecRoutes from './ec'; -import financeRoutes from './finance'; -import contentRoutes from './content'; -import customerServiceRoutes from './customer_service'; -import walletRoutes from './wallet'; -const platformRoutes: AppRouteRecordRaw[] = [ - { path: '/dashboard', name: 'dashboard', component: DEFAULT_LAYOUT, meta: { locale: 'menu.dashboard', requiresAuth: true, icon: 'icon-dashboard', order: 0 }, children: [{ path: 'overview', name: 'DashboardOverview', component: () => import('@/views/dashboard/DashboardPage.vue'), meta: { locale: 'menu.platform.dashboard', requiresAuth: true, menuCode: 'dashboard' } }] }, - { path: '/platform', name: 'platform', component: DEFAULT_LAYOUT, meta: { locale: 'menu.platform.config', requiresAuth: true, icon: 'icon-settings', order: 30 }, children: [ - { path: 'platfrom-account', name: 'platform-platfrom-account', component: () => import('@/views/platform/platfrom_account/ListPage.vue'), meta: { locale: 'menu.platform.platform.platfrom_account', requiresAuth: true, menuCode: 'platform' } }, - { path: 'platform-role', name: 'platform-platform-role', component: () => import('@/views/platform/platform_role/ListPage.vue'), meta: { locale: 'menu.platform.platform.platform_role', requiresAuth: true, menuCode: 'platform' } }, - { path: 'platform-menu', name: 'platform-platform-menu', component: () => import('@/views/platform/platform_menu/TreePage.vue'), meta: { locale: 'menu.platform.platform.platform_menu', requiresAuth: true, menuCode: 'platform' } }, - ] }, + +const resourcePage = () => import('@/views/shared/ResourcePage.vue'); + +function child( + domain: string, + path: string, + name: string, + title: string, + resource: string, + menuCode = domain, +): AppRouteRecordRaw { + return { + path, + name: `${domain}-${name}`, + component: resourcePage, + meta: { title, resource, requiresAuth: true, menuCode }, + }; +} + +function group( + path: string, + name: string, + title: string, + icon: string, + order: number, + children: AppRouteRecordRaw[], + menuCode = name, +): AppRouteRecordRaw { + return { + path: `/${path}`, + name, + component: DEFAULT_LAYOUT, + redirect: `/${path}/${children[0].path}`, + meta: { title, requiresAuth: true, icon, order, menuCode }, + children, + }; +} + +const routes: AppRouteRecordRaw[] = [ + { + path: '/dashboard', + name: 'dashboard', + component: DEFAULT_LAYOUT, + redirect: '/dashboard/overview', + meta: { title: '首页', requiresAuth: true, icon: 'icon-dashboard', order: 0, menuCode: 'dashboard' }, + children: [ + { path: 'overview', name: 'dashboard-overview', component: () => import('@/views/dashboard/DashboardPage.vue'), meta: { title: '数据概览', requiresAuth: true, menuCode: 'dashboard' } }, + { path: 'reports', name: 'dashboard-reports', component: () => import('@/views/dashboard/ReportPage.vue'), meta: { title: '统计报表', requiresAuth: true, menuCode: 'dashboard' } }, + ], + }, + group('gas', 'gas', '气站管理', 'icon-storage', 10, [ + child('gas', 'gas-basic', 'basic', '气站', '/gas_basic'), + child('gas', 'gas-account', 'account', '气站账户', '/gas_account'), + ]), + group('delivery', 'delivery', '配送站管理', 'icon-send', 20, [ + child('delivery', 'delivery-basic', 'basic', '配送站', '/delivery_basic'), + child('delivery', 'delivery-account', 'account', '配送站账户', '/delivery_account'), + ]), + group('staff', 'staff', '工作人员管理', 'icon-user-group', 30, [ + child('staff', 'staff-account', 'account', '工作人员', '/staff_account'), + child('staff', 'staff-credential', 'credential', '人员资质', '/staff_credential'), + ]), + group('user', 'user', '用户管理', 'icon-user', 40, [ + child('user', 'user-account', 'account', '用户账户', '/user_account'), + child('user', 'user-address', 'address', '用户地址', '/user_address'), + child('user', 'service-relation', 'service-relation', '服务关系', '/user_service_relation'), + ]), + group('product', 'product', '产品管理', 'icon-common', 50, [ + child('product', 'product-type', 'type', '产品类型', '/product_type', 'device'), + child('product', 'warehouse', 'warehouse', '库房', '/product_warehouse', 'device'), + child('product', 'product-info', 'info', '产品信息', '/product_info', 'device'), + child('product', 'repair', 'repair', '检修记录', '/product_repair', 'device'), + child('product', 'owner', 'owner', '归属记录', '/product_owner', 'device'), + ], 'device'), + group('gasorder', 'gasorder', '气体配送订单管理', 'icon-list', 60, [ + child('gasorder', 'contracts', 'contracts', '合同管理', '/gasorder_contract', 'delivery'), + child('gasorder', 'orders', 'orders', '配送订单', '/gasorder_basic', 'delivery'), + child('gasorder', 'tracks', 'tracks', '运行轨迹', '/gasorder_track', 'delivery'), + ], 'delivery'), + group('ec', 'ec', '商城管理', 'icon-shopping-cart', 70, [ + child('ec', 'categories', 'categories', '商品分类', '/ec_category'), + child('ec', 'products', 'products', '商品', '/ec_product'), + child('ec', 'attributes', 'attributes', '商品属性', '/ec_product_attribute'), + child('ec', 'images', 'images', '商品图片', '/ec_product_image'), + child('ec', 'carts', 'carts', '购物车', '/ec_cart'), + child('ec', 'orders', 'orders', '商城订单', '/ec_order'), + child('ec', 'order-items', 'order-items', '订单明细', '/ec_order_item'), + child('ec', 'reviews', 'reviews', '商品评价', '/ec_review'), + ]), + group('wallet', 'wallet', '钱包管理', 'icon-safe', 80, [ + child('wallet', 'wallet-basic', 'basic', '钱包', '/wallet_basic'), + child('wallet', 'banks', 'banks', '银行卡', '/wallet_bank'), + child('wallet', 'payments', 'payments', '支付记录', '/wallet_payment'), + child('wallet', 'records', 'records', '钱包流水', '/wallet_record'), + child('wallet', 'refunds', 'refunds', '退款记录', '/wallet_refund'), + child('wallet', 'apply-cash', 'apply-cash', '提现申请', '/wallet_apply_cash'), + ]), + group('finance', 'finance', '财务管理', 'icon-fund', 90, [ + child('finance', 'payments', 'payments', '支付记录', '/fin_payment'), + child('finance', 'settlements', 'settlements', '财务结算', '/fin_settlement'), + child('finance', 'reconciliations', 'reconciliations', '财务对账', '/fin_reconciliation'), + ]), + group('content', 'content', '内容管理', 'icon-file', 100, [ + child('content', 'contents', 'contents', '内容', '/cms_content'), + ]), + group('customer-service', 'customer_service', '客服管理', 'icon-customer-service', 110, [ + child('customer_service', 'tickets', 'tickets', '客服工单', '/cs_ticket'), + ]), + group('platform', 'platform', '平台管理', 'icon-settings', 120, [ + child('platform', 'accounts', 'accounts', '平台账户', '/platfrom_account'), + child('platform', 'roles', 'roles', '平台角色', '/platform_role'), + child('platform', 'menus', 'menus', '平台菜单', '/platform_menu'), + ]), ]; -export default [...platformRoutes, ...gasRoutes, ...deliveryRoutes, ...staffRoutes, ...userRoutes, ...deviceRoutes, ...ecRoutes, ...financeRoutes, ...contentRoutes, ...customerServiceRoutes, ...walletRoutes]; + +export default routes; diff --git a/frontend/platform_admin/src/router/routes/modules/staff.ts b/frontend/platform_admin/src/router/routes/modules/staff.ts deleted file mode 100644 index 1d42d6a..0000000 --- a/frontend/platform_admin/src/router/routes/modules/staff.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { DEFAULT_LAYOUT } from '../base'; -import type { AppRouteRecordRaw } from '../types'; -const routes: AppRouteRecordRaw[] = [{ - path: '/staff', name: 'staff', component: DEFAULT_LAYOUT, - meta: { locale: 'menu.platform.staff', requiresAuth: true, icon: 'icon-apps', order: 12 }, - children: [ - { path: 'account', name: 'staff-staff-account', component: () => import('@/views/staff/staff_account/ListPage.vue'), meta: { locale: 'menu.platform.staff.staff_account', requiresAuth: true, menuCode: 'staff' } }, - { path: 'credential', name: 'staff-staff-credential', component: () => import('@/views/staff/staff_credential/ListPage.vue'), meta: { locale: 'menu.platform.staff.staff_credential', requiresAuth: true, menuCode: 'staff' } } - ], -}]; -export default routes; diff --git a/frontend/platform_admin/src/router/routes/modules/user.ts b/frontend/platform_admin/src/router/routes/modules/user.ts deleted file mode 100644 index 3985f6c..0000000 --- a/frontend/platform_admin/src/router/routes/modules/user.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { DEFAULT_LAYOUT } from '../base'; -import type { AppRouteRecordRaw } from '../types'; -const routes: AppRouteRecordRaw[] = [{ - path: '/user', name: 'user', component: DEFAULT_LAYOUT, - meta: { locale: 'menu.platform.user', requiresAuth: true, icon: 'icon-apps', order: 13 }, - children: [ - { path: 'account', name: 'user-user-account', component: () => import('@/views/user/user_account/ListPage.vue'), meta: { locale: 'menu.platform.user.user_account', requiresAuth: true, menuCode: 'user' } }, - { path: 'address', name: 'user-user-address', component: () => import('@/views/user/user_address/ListPage.vue'), meta: { locale: 'menu.platform.user.user_address', requiresAuth: true, menuCode: 'user' } }, - { path: 'service-relation', name: 'user-user-service-relation', component: () => import('@/views/user/user_service_relation/ListPage.vue'), meta: { locale: 'menu.platform.user.user_service_relation', requiresAuth: true, menuCode: 'user' } } - ], -}]; -export default routes; diff --git a/frontend/platform_admin/src/router/routes/modules/wallet.ts b/frontend/platform_admin/src/router/routes/modules/wallet.ts deleted file mode 100644 index f015319..0000000 --- a/frontend/platform_admin/src/router/routes/modules/wallet.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { DEFAULT_LAYOUT } from '../base'; -import type { AppRouteRecordRaw } from '../types'; -const routes: AppRouteRecordRaw[] = [{ - path: '/wallet', name: 'wallet', component: DEFAULT_LAYOUT, - meta: { locale: 'menu.platform.wallet', requiresAuth: true, icon: 'icon-apps', order: 20 }, - children: [ - { path: 'wallet', name: 'wallet-wallet', component: () => import('@/views/wallet/wallet/ListPage.vue'), meta: { locale: 'menu.platform.wallet.wallet', requiresAuth: true, menuCode: 'wallet' } }, - { path: 'wallet-ledger', name: 'wallet-wallet-ledger', component: () => import('@/views/wallet/wallet_ledger/ListPage.vue'), meta: { locale: 'menu.platform.wallet.wallet_ledger', requiresAuth: true, menuCode: 'wallet' } }, - { path: 'wallet-recharge', name: 'wallet-wallet-recharge', component: () => import('@/views/wallet/wallet_recharge/ListPage.vue'), meta: { locale: 'menu.platform.wallet.wallet_recharge', requiresAuth: true, menuCode: 'wallet' } }, - { path: 'wallet-withdrawal', name: 'wallet-wallet-withdrawal', component: () => import('@/views/wallet/wallet_withdrawal/ListPage.vue'), meta: { locale: 'menu.platform.wallet.wallet_withdrawal', requiresAuth: true, menuCode: 'wallet' } } - ], -}]; -export default routes; diff --git a/frontend/platform_admin/src/style.css b/frontend/platform_admin/src/style.css deleted file mode 100644 index d72920d..0000000 --- a/frontend/platform_admin/src/style.css +++ /dev/null @@ -1,22 +0,0 @@ -:root { color: #18212f; background: #f4f7fb; font-family: Inter, "Microsoft YaHei", sans-serif; } -* { box-sizing: border-box; } -body { margin: 0; } -button, input { font: inherit; } -.login-page { min-height: 100vh; display: grid; place-items: center; padding: 24px; background: radial-gradient(circle at top right, #d8f3e7, transparent 42%), #f4f7fb; } -.login-card { width: min(420px, 100%); display: grid; gap: 14px; padding: 38px; background: #fff; border: 1px solid #e1e9f0; border-radius: 16px; box-shadow: 0 18px 50px rgb(15 42 67 / 12%); } -.login-card .brand-mark { margin-bottom: 6px; }.login-card h1 { margin: 0; color: #102a43; }.login-card p { color: #536575; line-height: 1.6; margin: 0; } -.shell { min-height: 100vh; display: grid; grid-template-columns: 240px 1fr; } -.sidebar { background: #102a43; color: #e8f1fb; padding: 28px 16px; display: flex; flex-direction: column; } -.brand { display: flex; align-items: center; gap: 12px; padding: 0 10px 30px; } -.brand-mark { background: #33a474; display: grid; place-items: center; border-radius: 10px; height: 38px; width: 38px; font-weight: 800; } -.avatar { width: 38px; height: 38px; border-radius: 10px; object-fit: cover; background: #e8f1fb; } -.brand strong, .brand small { display: block; }.brand small { color: #a8c2dc; margin-top: 3px; } -nav { display: grid; gap: 6px; } nav button { border: 0; border-radius: 8px; background: transparent; color: inherit; text-align: left; padding: 11px 14px; cursor: pointer; } nav button:hover, nav button.active { background: #1e4b70; } -.mock-note { font-size: 12px; color: #a8c2dc; line-height: 1.6; margin-top: auto; padding: 12px; background: #173b59; border-radius: 8px; } -.account { margin-top: auto; display: grid; gap: 5px; padding: 12px; background: #173b59; border-radius: 8px; }.account small { color: #a8c2dc; }.link-button { padding: 4px 0; text-align: left; background: transparent; color: #cde9dd; font-size: 12px; } -.content { padding: 34px; max-width: 1500px; width: 100%; }.content header { display: flex; align-items: center; justify-content: space-between; margin-bottom: 25px; }.eyebrow { color: #33a474; font-size: 12px; font-weight: 700; margin: 0 0 6px; }.content h1, .content h2 { margin: 0; }.content h1 { font-size: 28px; }.content h2 { font-size: 17px; } -.cards { display: grid; grid-template-columns: repeat(5, minmax(140px, 1fr)); gap: 16px; margin-bottom: 18px; }.cards article, .panel { border: 1px solid #e1e9f0; background: #fff; border-radius: 12px; box-shadow: 0 4px 12px rgb(15 42 67 / 4%); }.cards article { padding: 20px; }.cards span { color: #66788a; font-size: 13px; }.cards strong { display: block; font-size: 28px; margin-top: 12px; color: #0f3859; }.panel { padding: 22px; margin-bottom: 18px; }.panel p { color: #536575; line-height: 1.7; } -form { display: grid; grid-template-columns: repeat(4, minmax(140px, 1fr)) auto; gap: 10px; margin-top: 16px; } input { min-width: 0; border: 1px solid #cbd7e3; border-radius: 7px; padding: 10px 11px; } button { border: 0; border-radius: 7px; padding: 10px 15px; color: #fff; background: #1677c8; cursor: pointer; }button:disabled { opacity: .65; cursor: wait; }.secondary { background: #fff; color: #1677c8; border: 1px solid #a9c9e7; }.table-panel { padding: 0; overflow-x: auto; } table { border-collapse: collapse; width: 100%; min-width: 650px; } th, td { padding: 14px 18px; border-bottom: 1px solid #edf1f5; text-align: left; font-size: 14px; } th { background: #f8fafc; color: #4c6376; font-weight: 600; } td { color: #233446; }.error { background: #fff0f0; color: #b42318; padding: 11px 14px; border-radius: 8px; } -.warning { border-color: #ead4a7; background: #fffdf7; }.password-form { grid-template-columns: repeat(3, minmax(140px, 1fr)) auto; }.catalog { display: grid; grid-template-columns: repeat(2, minmax(280px, 1fr)); gap: 18px; }.catalog .panel { margin: 0; }.tags { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 16px; }.tags span { color: #176e4d; background: #e4f6ee; padding: 5px 9px; border-radius: 20px; font-size: 12px; } -@media (max-width: 900px) { .shell { grid-template-columns: 1fr; }.sidebar { min-height: auto; }.mock-note { display: none; } nav { grid-template-columns: repeat(3, 1fr); }.content { padding: 20px; }.cards, .catalog { grid-template-columns: repeat(2, 1fr); } form, .password-form { grid-template-columns: 1fr; } } -@media (max-width: 560px) { nav, .cards, .catalog { grid-template-columns: 1fr; }.login-card { padding: 28px 22px; } } diff --git a/frontend/platform_admin/src/views/content/cms_content/ListPage.vue b/frontend/platform_admin/src/views/content/cms_content/ListPage.vue deleted file mode 100644 index 19a149b..0000000 --- a/frontend/platform_admin/src/views/content/cms_content/ListPage.vue +++ /dev/null @@ -1,6 +0,0 @@ - - diff --git a/frontend/platform_admin/src/views/customer_service/cs_ticket/ListPage.vue b/frontend/platform_admin/src/views/customer_service/cs_ticket/ListPage.vue deleted file mode 100644 index f08cc68..0000000 --- a/frontend/platform_admin/src/views/customer_service/cs_ticket/ListPage.vue +++ /dev/null @@ -1,6 +0,0 @@ - - diff --git a/frontend/platform_admin/src/views/dashboard/ReportPage.vue b/frontend/platform_admin/src/views/dashboard/ReportPage.vue new file mode 100644 index 0000000..831eac7 --- /dev/null +++ b/frontend/platform_admin/src/views/dashboard/ReportPage.vue @@ -0,0 +1,41 @@ + + + diff --git a/frontend/platform_admin/src/views/delivery/basic/ListPage.vue b/frontend/platform_admin/src/views/delivery/basic/ListPage.vue deleted file mode 100644 index 048c291..0000000 --- a/frontend/platform_admin/src/views/delivery/basic/ListPage.vue +++ /dev/null @@ -1 +0,0 @@ - diff --git a/frontend/platform_admin/src/views/delivery/delivery_account/ListPage.vue b/frontend/platform_admin/src/views/delivery/delivery_account/ListPage.vue deleted file mode 100644 index 6cb3fd1..0000000 --- a/frontend/platform_admin/src/views/delivery/delivery_account/ListPage.vue +++ /dev/null @@ -1,6 +0,0 @@ - - diff --git a/frontend/platform_admin/src/views/delivery/delivery_basic/ListPage.vue b/frontend/platform_admin/src/views/delivery/delivery_basic/ListPage.vue deleted file mode 100644 index a8c03c6..0000000 --- a/frontend/platform_admin/src/views/delivery/delivery_basic/ListPage.vue +++ /dev/null @@ -1,6 +0,0 @@ - - diff --git a/frontend/platform_admin/src/views/delivery/delivery_task/ListPage.vue b/frontend/platform_admin/src/views/delivery/delivery_task/ListPage.vue deleted file mode 100644 index 73495a1..0000000 --- a/frontend/platform_admin/src/views/delivery/delivery_task/ListPage.vue +++ /dev/null @@ -1,6 +0,0 @@ - - diff --git a/frontend/platform_admin/src/views/delivery/delivery_track/ListPage.vue b/frontend/platform_admin/src/views/delivery/delivery_track/ListPage.vue deleted file mode 100644 index e525799..0000000 --- a/frontend/platform_admin/src/views/delivery/delivery_track/ListPage.vue +++ /dev/null @@ -1,6 +0,0 @@ - - diff --git a/frontend/platform_admin/src/views/delivery/delivery_track_point/ListPage.vue b/frontend/platform_admin/src/views/delivery/delivery_track_point/ListPage.vue deleted file mode 100644 index c7bc253..0000000 --- a/frontend/platform_admin/src/views/delivery/delivery_track_point/ListPage.vue +++ /dev/null @@ -1,6 +0,0 @@ - - diff --git a/frontend/platform_admin/src/views/device/dev_device_binding/ListPage.vue b/frontend/platform_admin/src/views/device/dev_device_binding/ListPage.vue deleted file mode 100644 index 65d0e05..0000000 --- a/frontend/platform_admin/src/views/device/dev_device_binding/ListPage.vue +++ /dev/null @@ -1,6 +0,0 @@ - - diff --git a/frontend/platform_admin/src/views/device/dev_smart_cylinder_valve/ListPage.vue b/frontend/platform_admin/src/views/device/dev_smart_cylinder_valve/ListPage.vue deleted file mode 100644 index 7600835..0000000 --- a/frontend/platform_admin/src/views/device/dev_smart_cylinder_valve/ListPage.vue +++ /dev/null @@ -1,6 +0,0 @@ - - diff --git a/frontend/platform_admin/src/views/device/dev_telemetry/ListPage.vue b/frontend/platform_admin/src/views/device/dev_telemetry/ListPage.vue deleted file mode 100644 index 6051038..0000000 --- a/frontend/platform_admin/src/views/device/dev_telemetry/ListPage.vue +++ /dev/null @@ -1,6 +0,0 @@ - - diff --git a/frontend/platform_admin/src/views/ec/ec_cart/ListPage.vue b/frontend/platform_admin/src/views/ec/ec_cart/ListPage.vue deleted file mode 100644 index b151c31..0000000 --- a/frontend/platform_admin/src/views/ec/ec_cart/ListPage.vue +++ /dev/null @@ -1,6 +0,0 @@ - - diff --git a/frontend/platform_admin/src/views/ec/ec_category/TreePage.vue b/frontend/platform_admin/src/views/ec/ec_category/TreePage.vue deleted file mode 100644 index e7de76b..0000000 --- a/frontend/platform_admin/src/views/ec/ec_category/TreePage.vue +++ /dev/null @@ -1,6 +0,0 @@ - - diff --git a/frontend/platform_admin/src/views/ec/ec_order/ListPage.vue b/frontend/platform_admin/src/views/ec/ec_order/ListPage.vue deleted file mode 100644 index 2224775..0000000 --- a/frontend/platform_admin/src/views/ec/ec_order/ListPage.vue +++ /dev/null @@ -1,6 +0,0 @@ - - diff --git a/frontend/platform_admin/src/views/ec/ec_order_item/ListPage.vue b/frontend/platform_admin/src/views/ec/ec_order_item/ListPage.vue deleted file mode 100644 index 0b17456..0000000 --- a/frontend/platform_admin/src/views/ec/ec_order_item/ListPage.vue +++ /dev/null @@ -1,6 +0,0 @@ - - diff --git a/frontend/platform_admin/src/views/ec/ec_product/ListPage.vue b/frontend/platform_admin/src/views/ec/ec_product/ListPage.vue deleted file mode 100644 index 6f8009d..0000000 --- a/frontend/platform_admin/src/views/ec/ec_product/ListPage.vue +++ /dev/null @@ -1,6 +0,0 @@ - - diff --git a/frontend/platform_admin/src/views/ec/ec_product_attribute/ListPage.vue b/frontend/platform_admin/src/views/ec/ec_product_attribute/ListPage.vue deleted file mode 100644 index dcf8426..0000000 --- a/frontend/platform_admin/src/views/ec/ec_product_attribute/ListPage.vue +++ /dev/null @@ -1,6 +0,0 @@ - - diff --git a/frontend/platform_admin/src/views/ec/ec_product_image/ListPage.vue b/frontend/platform_admin/src/views/ec/ec_product_image/ListPage.vue deleted file mode 100644 index 925a8af..0000000 --- a/frontend/platform_admin/src/views/ec/ec_product_image/ListPage.vue +++ /dev/null @@ -1,6 +0,0 @@ - - diff --git a/frontend/platform_admin/src/views/ec/ec_review/ListPage.vue b/frontend/platform_admin/src/views/ec/ec_review/ListPage.vue deleted file mode 100644 index 8e4e311..0000000 --- a/frontend/platform_admin/src/views/ec/ec_review/ListPage.vue +++ /dev/null @@ -1,6 +0,0 @@ - - diff --git a/frontend/platform_admin/src/views/finance/fin_payment/ListPage.vue b/frontend/platform_admin/src/views/finance/fin_payment/ListPage.vue deleted file mode 100644 index 8eb9ff5..0000000 --- a/frontend/platform_admin/src/views/finance/fin_payment/ListPage.vue +++ /dev/null @@ -1,6 +0,0 @@ - - diff --git a/frontend/platform_admin/src/views/finance/fin_reconciliation/ListPage.vue b/frontend/platform_admin/src/views/finance/fin_reconciliation/ListPage.vue deleted file mode 100644 index d67a38d..0000000 --- a/frontend/platform_admin/src/views/finance/fin_reconciliation/ListPage.vue +++ /dev/null @@ -1,6 +0,0 @@ - - diff --git a/frontend/platform_admin/src/views/finance/fin_settlement/ListPage.vue b/frontend/platform_admin/src/views/finance/fin_settlement/ListPage.vue deleted file mode 100644 index f316330..0000000 --- a/frontend/platform_admin/src/views/finance/fin_settlement/ListPage.vue +++ /dev/null @@ -1,6 +0,0 @@ - - diff --git a/frontend/platform_admin/src/views/gas/basic/ListPage.vue b/frontend/platform_admin/src/views/gas/basic/ListPage.vue deleted file mode 100644 index 06b6cd9..0000000 --- a/frontend/platform_admin/src/views/gas/basic/ListPage.vue +++ /dev/null @@ -1 +0,0 @@ - diff --git a/frontend/platform_admin/src/views/gas/gas_account/ListPage.vue b/frontend/platform_admin/src/views/gas/gas_account/ListPage.vue deleted file mode 100644 index 13ac838..0000000 --- a/frontend/platform_admin/src/views/gas/gas_account/ListPage.vue +++ /dev/null @@ -1,6 +0,0 @@ - - diff --git a/frontend/platform_admin/src/views/gas/gas_basic/ListPage.vue b/frontend/platform_admin/src/views/gas/gas_basic/ListPage.vue deleted file mode 100644 index 2eae051..0000000 --- a/frontend/platform_admin/src/views/gas/gas_basic/ListPage.vue +++ /dev/null @@ -1,6 +0,0 @@ - - diff --git a/frontend/platform_admin/src/views/login/components/login-banner.vue b/frontend/platform_admin/src/views/login/components/login-banner.vue index 5bdfd4a..ad10d04 100644 --- a/frontend/platform_admin/src/views/login/components/login-banner.vue +++ b/frontend/platform_admin/src/views/login/components/login-banner.vue @@ -15,28 +15,25 @@ diff --git a/frontend/platform_admin/src/views/shared/ResourcePage.vue b/frontend/platform_admin/src/views/shared/ResourcePage.vue new file mode 100644 index 0000000..ffdc5d0 --- /dev/null +++ b/frontend/platform_admin/src/views/shared/ResourcePage.vue @@ -0,0 +1,15 @@ + + + diff --git a/frontend/platform_admin/src/views/staff/list/ListPage.vue b/frontend/platform_admin/src/views/staff/list/ListPage.vue deleted file mode 100644 index a8f3423..0000000 --- a/frontend/platform_admin/src/views/staff/list/ListPage.vue +++ /dev/null @@ -1,6 +0,0 @@ - - diff --git a/frontend/platform_admin/src/views/staff/staff_account/ListPage.vue b/frontend/platform_admin/src/views/staff/staff_account/ListPage.vue deleted file mode 100644 index a8f3423..0000000 --- a/frontend/platform_admin/src/views/staff/staff_account/ListPage.vue +++ /dev/null @@ -1,6 +0,0 @@ - - diff --git a/frontend/platform_admin/src/views/staff/staff_credential/ListPage.vue b/frontend/platform_admin/src/views/staff/staff_credential/ListPage.vue deleted file mode 100644 index 233de6c..0000000 --- a/frontend/platform_admin/src/views/staff/staff_credential/ListPage.vue +++ /dev/null @@ -1,6 +0,0 @@ - - diff --git a/frontend/platform_admin/src/views/user/list/ListPage.vue b/frontend/platform_admin/src/views/user/list/ListPage.vue deleted file mode 100644 index ab4ba3d..0000000 --- a/frontend/platform_admin/src/views/user/list/ListPage.vue +++ /dev/null @@ -1,6 +0,0 @@ - - diff --git a/frontend/platform_admin/src/views/user/user_account/ListPage.vue b/frontend/platform_admin/src/views/user/user_account/ListPage.vue deleted file mode 100644 index ab4ba3d..0000000 --- a/frontend/platform_admin/src/views/user/user_account/ListPage.vue +++ /dev/null @@ -1,6 +0,0 @@ - - diff --git a/frontend/platform_admin/src/views/user/user_address/ListPage.vue b/frontend/platform_admin/src/views/user/user_address/ListPage.vue deleted file mode 100644 index 1891985..0000000 --- a/frontend/platform_admin/src/views/user/user_address/ListPage.vue +++ /dev/null @@ -1,6 +0,0 @@ - - diff --git a/frontend/platform_admin/src/views/user/user_service_relation/ListPage.vue b/frontend/platform_admin/src/views/user/user_service_relation/ListPage.vue deleted file mode 100644 index b4adbec..0000000 --- a/frontend/platform_admin/src/views/user/user_service_relation/ListPage.vue +++ /dev/null @@ -1,6 +0,0 @@ - - diff --git a/frontend/platform_admin/src/views/wallet/wallet/ListPage.vue b/frontend/platform_admin/src/views/wallet/wallet/ListPage.vue deleted file mode 100644 index 923935e..0000000 --- a/frontend/platform_admin/src/views/wallet/wallet/ListPage.vue +++ /dev/null @@ -1,6 +0,0 @@ - - diff --git a/frontend/platform_admin/src/views/wallet/wallet_ledger/ListPage.vue b/frontend/platform_admin/src/views/wallet/wallet_ledger/ListPage.vue deleted file mode 100644 index f93d19f..0000000 --- a/frontend/platform_admin/src/views/wallet/wallet_ledger/ListPage.vue +++ /dev/null @@ -1,6 +0,0 @@ - - diff --git a/frontend/platform_admin/src/views/wallet/wallet_recharge/ListPage.vue b/frontend/platform_admin/src/views/wallet/wallet_recharge/ListPage.vue deleted file mode 100644 index 80911e9..0000000 --- a/frontend/platform_admin/src/views/wallet/wallet_recharge/ListPage.vue +++ /dev/null @@ -1,6 +0,0 @@ - - diff --git a/frontend/platform_admin/src/views/wallet/wallet_withdrawal/ListPage.vue b/frontend/platform_admin/src/views/wallet/wallet_withdrawal/ListPage.vue deleted file mode 100644 index e987d4b..0000000 --- a/frontend/platform_admin/src/views/wallet/wallet_withdrawal/ListPage.vue +++ /dev/null @@ -1,6 +0,0 @@ - -