Compare commits

..

75 Commits

Author SHA1 Message Date
luo
db7b3562da fix:将文案虚流量改成流量
All checks were successful
构建并部署前端到测试环境 / build-and-deploy (push) Successful in 7m23s
2026-07-21 15:24:02 +08:00
luo
f52970ae6c fix: 资产信息流量显示
All checks were successful
构建并部署前端到测试环境 / build-and-deploy (push) Successful in 6m22s
2026-07-21 14:51:29 +08:00
luo
2c8524485b fix: 资产信息字段显示隐藏根据角色
All checks were successful
构建并部署前端到测试环境 / build-and-deploy (push) Successful in 4m24s
2026-07-13 16:40:35 +08:00
luo
4c0207c6e6 fix: 将微信配置改成支付配置
All checks were successful
构建并部署前端到测试环境 / build-and-deploy (push) Successful in 4m41s
2026-07-10 18:09:19 +08:00
luo
5b8d6610ab fix: 将套餐系列的套餐加滚动条 2026-07-09 17:38:58 +08:00
luo
a036b10641 fix: 将权限管理里面的分页去掉
All checks were successful
构建并部署前端到测试环境 / build-and-deploy (push) Successful in 5m45s
2026-07-09 15:59:39 +08:00
luo
c1a7118d35 fix: 将导出列表分成三个模块
All checks were successful
构建并部署前端到测试环境 / build-and-deploy (push) Successful in 4m40s
2026-07-09 15:20:28 +08:00
luo
f27bbacb1e fix: 设备信息新增的两个流量放到同一行
All checks were successful
构建并部署前端到测试环境 / build-and-deploy (push) Successful in 5m13s
2026-07-06 17:52:08 +08:00
luo
acda0d82c8 feat: 新增全部已使用流量和全部总流量字段
All checks were successful
构建并部署前端到测试环境 / build-and-deploy (push) Successful in 2m30s
2026-07-03 12:03:52 +08:00
99de03604d 驳回
All checks were successful
构建并部署前端到测试环境 / build-and-deploy (push) Successful in 5m1s
2026-06-26 18:14:37 +08:00
1baa6d87cf 驳回 2026-06-26 18:14:26 +08:00
f604791c69 倒计时没有覆盖复机停机的问题 2026-06-25 10:30:49 +08:00
cfb6b52cc2 倒计时限制
All checks were successful
构建并部署前端到测试环境 / build-and-deploy (push) Successful in 4m12s
2026-06-24 15:06:05 +08:00
0c3065f970 让凭证数组与套餐作废任务进入可联调状态
All checks were successful
构建并部署前端到测试环境 / build-and-deploy (push) Successful in 7m52s
Constraint: 后端订单套餐作废接口使用 /api/admin/order-package-invalidate-tasks,上传
  purpose 只能使用 attachment。
  Rejected: 继续提交逗号拼接凭证字符串 | 新提交路径必须使用 string[],历史字符串只做读取归一化。
  Confidence: high
  Scope-risk: broad
  Directive: 新增订单套餐作废权限时需同时配置菜单 URL 与
  order_package_invalidate_task:create/detail 按钮权限。
  Tested: bun run build;development 真实后端联调上传、创建、列表、详情通过;复机接口未触发。
  Not-tested: 未逐一手工覆盖所有历史凭证数据、全部角色权限组合和所有文件扩展名预览
2026-06-23 09:50:12 +08:00
luo
ebf9739f06 fix:sort
All checks were successful
构建并部署前端到测试环境 / build-and-deploy (push) Successful in 6m0s
2026-06-22 11:09:50 +08:00
luo
d3b5f55c64 feat:更换企业授权
All checks were successful
构建并部署前端到测试环境 / build-and-deploy (push) Successful in 3m41s
2026-06-22 10:36:33 +08:00
luo
866a2587b3 feat:export
All checks were successful
构建并部署前端到测试环境 / build-and-deploy (push) Successful in 4m27s
2026-06-18 10:17:10 +08:00
luo
85d158dfec feat: order-export,generation,status
All checks were successful
构建并部署前端到测试环境 / build-and-deploy (push) Successful in 4m45s
2026-06-17 17:46:04 +08:00
luo
2a8f4e40d6 feat: 新增导出
All checks were successful
构建并部署前端到测试环境 / build-and-deploy (push) Successful in 5m19s
2026-06-16 11:35:45 +08:00
sexygoat
908367ba5d fix: new
All checks were successful
构建并部署前端到测试环境 / build-and-deploy (push) Successful in 4m40s
2026-06-15 10:19:35 +08:00
sexygoat
2ecab976f5 fix: 新增: 修改资产套餐已用量, 修改资产套餐过期时间
All checks were successful
构建并部署前端到测试环境 / build-and-deploy (push) Successful in 4m22s
2026-06-13 10:41:10 +08:00
sexygoat
e04a283319 fix: 新增代理系列授权-建议售价使用套餐
All checks were successful
构建并部署前端到测试环境 / build-and-deploy (push) Successful in 4m38s
2026-06-12 10:41:37 +08:00
sexygoat
d23e896f97 fix: 退款审批可以是0
All checks were successful
构建并部署前端到测试环境 / build-and-deploy (push) Successful in 4m43s
2026-06-06 15:09:31 +08:00
sexygoat
c5e4082c26 fix: 退款审批可以是0
All checks were successful
构建并部署前端到测试环境 / build-and-deploy (push) Successful in 5m5s
2026-06-05 16:26:17 +08:00
sexygoat
0c737a30c1 fix: 换货
All checks were successful
构建并部署前端到测试环境 / build-and-deploy (push) Successful in 6m37s
2026-06-04 10:47:43 +08:00
sexygoat
c5dae2993b fix: 换货
All checks were successful
构建并部署前端到测试环境 / build-and-deploy (push) Successful in 5m12s
2026-06-03 17:53:19 +08:00
sexygoat
3e6b3981f4 fix:订单列表详情实付金额代理/企业不可见
All checks were successful
构建并部署前端到测试环境 / build-and-deploy (push) Successful in 6m22s
2026-06-01 14:10:09 +08:00
sexygoat
e563b81b6e fix:操作审计日志-style
All checks were successful
构建并部署前端到测试环境 / build-and-deploy (push) Successful in 7m12s
2026-06-01 11:41:52 +08:00
sexygoat
322cd0dca5 fix: 订单列表套餐显示销售价
All checks were successful
构建并部署前端到测试环境 / build-and-deploy (push) Successful in 7m33s
2026-05-30 15:06:41 +08:00
sexygoat
ad56b82944 feat: 新增代理流水资产标识
All checks were successful
构建并部署前端到测试环境 / build-and-deploy (push) Successful in 7m11s
2026-05-29 15:44:53 +08:00
sexygoat
f62a8d98a4 fix: 自动更新提示bug
All checks were successful
构建并部署前端到测试环境 / build-and-deploy (push) Successful in 4m55s
2026-05-29 12:11:03 +08:00
sexygoat
76a526643e fix:修改bug: 虚流量关闭的时候还是显示了
All checks were successful
构建并部署前端到测试环境 / build-and-deploy (push) Successful in 4m46s
2026-05-29 10:46:01 +08:00
sexygoat
9ce48f1714 fix:本月已用流量和运营商周期月内已用流量
All checks were successful
构建并部署前端到测试环境 / build-and-deploy (push) Successful in 4m57s
2026-05-29 09:40:17 +08:00
sexygoat
a162dcc650 fix:本月已用流量和运营商周期月内已用流量代理企业不展示,其他账号新增权限 2026-05-29 09:38:30 +08:00
sexygoat
868a3352ad fix: 新增套餐新增缩长比例,增长比例
All checks were successful
构建并部署前端到测试环境 / build-and-deploy (push) Successful in 4m43s
2026-05-28 18:18:42 +08:00
sexygoat
61e575194d fix: 新增套餐新增缩长比例,增长比例
All checks were successful
构建并部署前端到测试环境 / build-and-deploy (push) Successful in 4m28s
2026-05-28 18:13:08 +08:00
sexygoat
69b024b5cc fix: 虚流量进度条
All checks were successful
构建并部署前端到测试环境 / build-and-deploy (push) Successful in 5m2s
2026-05-28 11:20:33 +08:00
sexygoat
6654972ddb feat: 新增退款凭证
All checks were successful
构建并部署前端到测试环境 / build-and-deploy (push) Successful in 4m27s
2026-05-27 14:58:37 +08:00
sexygoat
fe0093972f feat: 卡新增IMEI
All checks were successful
构建并部署前端到测试环境 / build-and-deploy (push) Successful in 5m47s
2026-05-26 18:07:39 +08:00
sexygoat
878d93a865 fix: 微信配置
All checks were successful
构建并部署前端到测试环境 / build-and-deploy (push) Successful in 5m39s
2026-05-22 16:07:47 +08:00
sexygoat
b364fb4aec fix: 在线
All checks were successful
构建并部署前端到测试环境 / build-and-deploy (push) Successful in 6m20s
2026-05-21 15:43:36 +08:00
sexygoat
a69f972766 fix: 格式化时间
All checks were successful
构建并部署前端到测试环境 / build-and-deploy (push) Successful in 4m48s
2026-05-21 14:53:36 +08:00
sexygoat
dfb78006ac fix: 分页器一直显示和资产信息使用单卡
All checks were successful
构建并部署前端到测试环境 / build-and-deploy (push) Successful in 4m15s
2026-05-21 14:39:21 +08:00
sexygoat
893921007a feat: 设备管理新增imei和虚拟号
All checks were successful
构建并部署前端到测试环境 / build-and-deploy (push) Successful in 6m13s
2026-05-20 11:24:52 +08:00
sexygoat
59167d2630 feat: 卡/设备是否有生效套餐
All checks were successful
构建并部署前端到测试环境 / build-and-deploy (push) Successful in 5m19s
2026-05-19 15:14:39 +08:00
sexygoat
14832f5c6f fix: 拖拽上传
All checks were successful
构建并部署前端到测试环境 / build-and-deploy (push) Successful in 4m30s
2026-05-18 13:11:40 +08:00
sexygoat
894aea0e74 feat: 新增卡视角以及设备视角的实名同步时间,卡状态同步时间,流量同步时间
All checks were successful
构建并部署前端到测试环境 / build-and-deploy (push) Successful in 4m56s
2026-05-18 12:23:35 +08:00
sexygoat
877c741d72 fix: 最后同步时间
All checks were successful
构建并部署前端到测试环境 / build-and-deploy (push) Successful in 4m47s
2026-05-15 16:53:00 +08:00
sexygoat
aeb08f2cc1 fix: 时区去掉使用本地
All checks were successful
构建并部署前端到测试环境 / build-and-deploy (push) Successful in 4m7s
2026-05-15 16:11:47 +08:00
sexygoat
c7e2d6fe53 fix: get-list
All checks were successful
构建并部署前端到测试环境 / build-and-deploy (push) Successful in 4m45s
2026-05-15 11:16:57 +08:00
sexygoat
f3b5444dd8 fix: 换货原因改成下拉
All checks were successful
构建并部署前端到测试环境 / build-and-deploy (push) Successful in 4m37s
2026-05-14 15:52:38 +08:00
sexygoat
38cf7d12e9 fix: 真流量虚流量权限
All checks were successful
构建并部署前端到测试环境 / build-and-deploy (push) Successful in 4m42s
2026-05-14 10:52:11 +08:00
sexygoat
1690252740 feat: 新增设备激活时间
All checks were successful
构建并部署前端到测试环境 / build-and-deploy (push) Successful in 5m7s
2026-05-12 14:54:47 +08:00
sexygoat
471d1196b3 feat: 新增运营商停机原因
All checks were successful
构建并部署前端到测试环境 / build-and-deploy (push) Successful in 4m44s
2026-05-12 12:11:53 +08:00
sexygoat
5d20d7dd3f fix: 订单详情支付凭证, 退款
All checks were successful
构建并部署前端到测试环境 / build-and-deploy (push) Successful in 6m34s
2026-05-11 18:01:22 +08:00
sexygoat
1990a3ccba fix: bug
All checks were successful
构建并部署前端到测试环境 / build-and-deploy (push) Successful in 4m4s
2026-05-11 15:51:50 +08:00
sexygoat
766497dc31 fix: bug
All checks were successful
构建并部署前端到测试环境 / build-and-deploy (push) Successful in 4m40s
2026-05-11 14:51:13 +08:00
sexygoat
e59c75bf9e fix: bug
Some checks failed
构建并部署前端到测试环境 / build-and-deploy (push) Failing after 12s
2026-05-11 14:47:07 +08:00
sexygoat
eeeef931b1 fix: bug
Some checks failed
构建并部署前端到测试环境 / build-and-deploy (push) Failing after 11s
2026-05-11 14:35:38 +08:00
sexygoat
73ce9af11a fix: 代理系列授权店铺
Some checks failed
构建并部署前端到测试环境 / build-and-deploy (push) Failing after 2m0s
2026-05-09 16:13:11 +08:00
sexygoat
3c71ae0004 fix:name
All checks were successful
构建并部署前端到测试环境 / build-and-deploy (push) Successful in 4m31s
2026-05-07 17:48:43 +08:00
sexygoat
43de0e3e9e fix:name
All checks were successful
构建并部署前端到测试环境 / build-and-deploy (push) Successful in 2m18s
2026-05-07 17:45:05 +08:00
sexygoat
78fe35da01 fix:name
All checks were successful
构建并部署前端到测试环境 / build-and-deploy (push) Successful in 5m8s
2026-05-07 17:34:06 +08:00
sexygoat
90d415cfb9 fix: bug
All checks were successful
构建并部署前端到测试环境 / build-and-deploy (push) Successful in 4m14s
2026-05-07 09:18:03 +08:00
sexygoat
304a5de1d6 fix: tips
All checks were successful
构建并部署前端到测试环境 / build-and-deploy (push) Successful in 3m47s
2026-05-06 11:13:51 +08:00
sexygoat
8832150562 feat: sn
All checks were successful
构建并部署前端到测试环境 / build-and-deploy (push) Successful in 4m26s
2026-05-06 10:54:43 +08:00
sexygoat
950520635d feat: 文件下载
All checks were successful
构建并部署前端到测试环境 / build-and-deploy (push) Successful in 3m54s
2026-04-30 18:04:01 +08:00
sexygoat
81155fd8e4 fix:进度条
All checks were successful
构建并部署前端到测试环境 / build-and-deploy (push) Successful in 4m44s
2026-04-30 17:01:00 +08:00
sexygoat
78a41513bd fix: 字段显示和订单详情按钮
All checks were successful
构建并部署前端到测试环境 / build-and-deploy (push) Successful in 4m47s
2026-04-30 15:16:15 +08:00
sexygoat
57ddbe5f85 fix: order
All checks were successful
构建并部署前端到测试环境 / build-and-deploy (push) Successful in 4m34s
2026-04-30 12:04:04 +08:00
sexygoat
044c07fae6 fix: order
All checks were successful
构建并部署前端到测试环境 / build-and-deploy (push) Successful in 5m33s
2026-04-30 10:39:51 +08:00
sexygoat
1a4eb00440 fix: 新增代理系列授权
All checks were successful
构建并部署前端到测试环境 / build-and-deploy (push) Successful in 5m45s
2026-04-29 17:09:23 +08:00
sexygoat
1141b6c92d fix: 新增代理系列授权
Some checks failed
构建并部署前端到测试环境 / build-and-deploy (push) Has been cancelled
2026-04-29 16:11:54 +08:00
sexygoat
9b4d956032 del: 激活时间
All checks were successful
构建并部署前端到测试环境 / build-and-deploy (push) Successful in 4m39s
2026-04-29 12:49:30 +08:00
sexygoat
175fe55be7 del: 激活时间
All checks were successful
构建并部署前端到测试环境 / build-and-deploy (push) Successful in 4m12s
2026-04-29 11:40:38 +08:00
377 changed files with 21571 additions and 20962 deletions

View File

@@ -19,7 +19,9 @@
"Bash(findstr:*)",
"Bash(npm run:*)",
"Bash(find src/views -name *.vue -type f -exec grep -l \"device.*detail\\\\|设备详情\" {})",
"Bash(2)"
"Bash(2)",
"Bash(grep -E \"\\\\.\\(vue|ts\\)$\")",
"Bash(xargs grep:*)"
],
"deny": [],
"ask": []

12
.editorconfig Normal file
View File

@@ -0,0 +1,12 @@
root = true
[*]
charset = utf-8
end_of_line = lf
indent_style = space
indent_size = 2
insert_final_newline = true
trim_trailing_whitespace = true
[*.md]
trim_trailing_whitespace = false

14
.env
View File

@@ -1,21 +1,21 @@
# 【通用】环境变量
# 版本号
VITE_VERSION = 2.4.1.1
VITE_VERSION=2.4.1.1
# 端口号
VITE_PORT = 3006
VITE_PORT=3006
# 网站地址前缀
VITE_BASE_URL =
VITE_BASE_URL=
# API 地址前缀
VITE_API_URL = https://cmp-api.boss160.cn
VITE_API_URL=https://cmp-api.xm-iot.cn
# 权限模式( frontend backend
VITE_ACCESS_MODE = backend
VITE_ACCESS_MODE=backend
# 是否打开路由信息
VITE_OPEN_ROUTE_INFO = false
VITE_OPEN_ROUTE_INFO=false
# 锁屏加密密钥
VITE_LOCK_ENCRYPT_KEY = jfsfjk1938jfj
VITE_LOCK_ENCRYPT_KEY=jfsfjk1938jfj

View File

@@ -1,10 +1,10 @@
# 【开发】环境变量
# 网站地址前缀
VITE_BASE_URL = /
VITE_BASE_URL=/
# API 地址前缀
VITE_API_URL = https://cmp-api.boss160.cn
VITE_API_URL=https://cmp-api.boss160.cn
# Delete console
VITE_DROP_CONSOLE = false
VITE_DROP_CONSOLE=false

View File

@@ -1,10 +1,10 @@
# 【生产】环境变量
# 网站地址前缀
VITE_BASE_URL =
VITE_BASE_URL=
# API 地址前缀
VITE_API_URL = https://cmp-api.boss160.cn
VITE_API_URL=https://cmp-api.xm-iot.cn
# Delete console
VITE_DROP_CONSOLE = true
VITE_DROP_CONSOLE=true

9
.gitignore vendored
View File

@@ -21,3 +21,12 @@ dist-ssr
# Logs
*.log
npminstall-debug.log
# plan
.scratch
# ai
.agents
#build
one-pipe-system-dist.zip

View File

@@ -19,3 +19,17 @@ Use `@/openspec/AGENTS.md` to learn:
Keep this managed block so 'openspec update' can refresh the instructions.
<!-- OPENSPEC:END -->
## Agent skills
### Issue tracker
Issues and PRDs are tracked as local markdown files under `.scratch/`; external PRs are not a triage surface. See `docs/agents/issue-tracker.md`.
### Triage labels
Uses the default triage state vocabulary: `needs-triage`, `needs-info`, `ready-for-agent`, `ready-for-human`, `wontfix`. See `docs/agents/triage-labels.md`.
### Domain docs
Uses a single-context domain documentation layout. See `docs/agents/domain.md`.

21
CONTEXT.md Normal file
View File

@@ -0,0 +1,21 @@
# One Pipe System
This context captures the business language used by the admin system for orders, assets, finance operations, and operational batch tasks.
## Language
**Voucher attachment**: A user-provided proof file attached to an order, refund, recharge, or operational task. Voucher attachments may contain any file type and a business record may hold multiple voucher attachment keys. _Avoid_: Voucher image, comma-separated voucher string
**Voucher key list**: The canonical representation of voucher attachment object-storage keys at the frontend and API submission boundary. Legacy single-key strings or comma-separated strings may be read and normalized for display, but new submissions use key lists. _Avoid_: Comma-separated voucher string
**Batch import file**: The structured data file that drives an asynchronous operational batch task. Unlike voucher attachments, a batch import file is constrained by the task's required template format. _Avoid_: Voucher, attachment
**Operational batch task**: An asynchronous admin task created from an uploaded batch import file and tracked through task status, counts, operator identity, and failure details. _Avoid_: Inline order operation, synchronous import
**Operational remark**: A note captured when an admin creates a financial or operational record. Operational remarks are retained for later review and are not edited after creation. _Avoid_: Editable comment, processing note
**Recharge order**: A C-side asset recharge order created when a user recharges an IoT card or device asset wallet. It is identified by `recharge_order_no`, is associated with `resource_type` and `resource_id`, and may be rejected while pending payment. _Avoid_: Agent recharge order
**Rejected recharge order**: A recharge order that an admin has rejected before payment because the recharge request should not proceed. Rejection is an irreversible terminal state and does not involve refunds. _Avoid_: Refunded recharge, closed recharge
**Agent recharge order**: A financial recharge record for an agent/shop wallet in the admin finance module. It uses a different API contract and status model from C-side recharge orders. _Avoid_: Recharge order

View File

@@ -10,7 +10,7 @@ RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories
WORKDIR /build
# 安装 pnpm
RUN corepack enable && corepack prepare pnpm@latest --activate
RUN corepack enable && corepack prepare pnpm@10.15.0 --activate
# 设置 npm 镜像源
RUN pnpm config set registry https://registry.npmmirror.com
@@ -25,7 +25,7 @@ RUN pnpm install --frozen-lockfile
COPY . .
# 构建生产版本(跳过类型检查,直接构建)
RUN pnpm run no
RUN pnpm run nd
# ================================
# 阶段 2: 运行阶段

View File

@@ -1,698 +0,0 @@
# 物联网管理后台 - 功能开发任务清单
## 项目概述
基于 Vue 3 + TypeScript + Element Plus 的物联网管理后台系统,管理代理商、网卡、套餐、设备等核心业务。
---
## 📦 模块划分
### 一、基础架构优化(必须先完成)
#### 1.1 API 层重构
**优先级P0最高**
- [ ] 创建统一的 API 服务基类
- [ ] 创建类型定义文件
- [ ] `src/types/api/auth.ts` - 认证相关类型
- [ ] `src/types/api/role.ts` - 角色相关类型
- [ ] `src/types/api/account.ts` - 账号相关类型
- [ ] `src/types/api/card.ts` - 网卡相关类型
- [ ] `src/types/api/package.ts` - 套餐相关类型
- [ ] `src/types/api/device.ts` - 设备相关类型
- [ ] `src/types/api/commission.ts` - 佣金相关类型
- [ ] 创建 API 服务类
- [ ] `src/api/modules/auth.ts` - AuthService
- [ ] `src/api/modules/role.ts` - RoleService
- [ ] `src/api/modules/account.ts` - AccountService
- [ ] `src/api/modules/agent.ts` - AgentService (代理商)
- [ ] `src/api/modules/card.ts` - CardService
- [ ] `src/api/modules/package.ts` - PackageService
- [ ] `src/api/modules/device.ts` - DeviceService
- [ ] `src/api/modules/commission.ts` - CommissionService
- [ ] `src/api/modules/setting.ts` - SettingService
#### 1.2 公共配置和常量提取
**优先级P0**
- [ ] 创建 `src/config/constants/` 目录
- [ ] `operators.ts` - 运营商配置
- [ ] `cardStatus.ts` - 网卡状态配置
- [ ] `userRoles.ts` - 用户角色配置
- [ ] `packageTypes.ts` - 套餐类型配置
- [ ] `deviceStatus.ts` - 设备状态配置
- [ ] `commissionStatus.ts` - 佣金状态配置
- [ ] 创建 `src/utils/business/` 目录
- [ ] `card.ts` - 网卡相关工具函数
- [ ] `package.ts` - 套餐相关工具函数
- [ ] `commission.ts` - 佣金计算工具函数
- [ ] `format.ts` - 格式化工具函数
#### 1.3 业务 Composables
**优先级P0**
- [ ] `src/composables/useCardManagement.ts` - 网卡管理
- [ ] `src/composables/usePackageManagement.ts` - 套餐管理
- [ ] `src/composables/useDeviceManagement.ts` - 设备管理
- [ ] `src/composables/useAgentManagement.ts` - 代理商管理
- [ ] `src/composables/useCommission.ts` - 佣金管理
- [ ] `src/composables/usePagination.ts` - 分页管理
- [ ] `src/composables/useTableSelection.ts` - 表格选择
#### 1.4 公共业务组件
**优先级P1**
- [ ] `src/components/business/CardStatusTag.vue` - 网卡状态标签
- [ ] `src/components/business/OperatorSelect.vue` - 运营商选择器
- [ ] `src/components/business/PackageSelector.vue` - 套餐选择器
- [ ] `src/components/business/AgentSelector.vue` - 代理商选择器
- [ ] `src/components/business/CommissionDisplay.vue` - 佣金展示组件
- [ ] `src/components/business/BatchOperationDialog.vue` - 批量操作对话框
- [ ] `src/components/business/ImportDialog.vue` - 导入对话框组件
---
## 🎯 功能模块开发
### 二、认证与权限模块
#### 2.1 登录模块
**优先级P0** **依赖1.1 API 层重构**
- [ ] 后端接口对接
- [ ] 登录接口
- [ ] 退出登录接口
- [ ] 获取用户信息接口
- [ ] 刷新 Token 接口
- [ ] 前端页面
- [ ] 完善登录页面(`src/views/auth/login/index.vue`
- [ ] 添加多角色登录支持(平台账号、代理商、企业客户)
- [ ] 添加验证码功能
- [ ] 添加记住密码功能
- [ ] 权限守卫
- [ ] 完善路由守卫逻辑
- [ ] 实现基于角色的路由过滤
- [ ] 实现按钮级权限控制
- [ ] Store 状态管理
- [ ] 完善用户 Store用户信息、权限列表
- [ ] 添加 Token 自动刷新逻辑
**Mock 数据:**
- 模拟不同角色的登录响应
- 模拟权限列表
---
### 三、账号管理模块
#### 3.1 平台角色管理
**优先级P1** **依赖1.1, 1.2, 1.3**
**子任务:**
- [ ] 类型定义
- [ ] 角色实体类型
- [ ] 角色查询参数类型
- [ ] 角色权限类型
- [ ] API 服务
- [ ] 获取角色列表
- [ ] 创建角色
- [ ] 编辑角色
- [ ] 删除角色
- [ ] 分配权限
- [ ] 页面开发
- [ ] `src/views/account-management/platform-role/index.vue`
- [ ] 角色列表(表格)
- [ ] 新增/编辑角色对话框
- [ ] 权限分配对话框(树形结构)
- [ ] 组件开发
- [ ] `PermissionTreeSelect.vue` - 权限树选择组件
- [ ] Mock 数据
- [ ] 模拟角色列表
- [ ] 模拟权限树
#### 3.2 平台账号管理
**优先级P1**
**子任务:**
- [ ] 类型定义
- [ ] API 服务
- [ ] 获取平台账号列表
- [ ] 创建平台账号
- [ ] 编辑平台账号
- [ ] 删除/禁用平台账号
- [ ] 重置密码
- [ ] 页面开发
- [ ] 账号列表(支持搜索、筛选)
- [ ] 新增/编辑账号对话框
- [ ] 角色分配
- [ ] Mock 数据
#### 3.3 客户角色管理
**优先级P1**
**子任务:**
- [ ] 类型定义
- [ ] API 服务
- [ ] 获取客户角色列表
- [ ] 创建客户角色
- [ ] 编辑客户角色
- [ ] 删除客户角色
- [ ] 设置能力边界
- [ ] 页面开发
- [ ] 角色列表
- [ ] 能力边界配置(功能权限、资源限制)
- [ ] 组件开发
- [ ] `AbilityBoundaryConfig.vue` - 能力边界配置组件
- [ ] Mock 数据
#### 3.4 代理商管理
**优先级P1**
**子任务:**
- [ ] 类型定义
- [ ] 代理商实体类型
- [ ] 代理商层级关系类型
- [ ] API 服务
- [ ] 获取代理商列表(树形结构)
- [ ] 创建代理商
- [ ] 编辑代理商信息
- [ ] 禁用/启用代理商
- [ ] 查看代理商详情
- [ ] 获取代理商下级列表
- [ ] 页面开发
- [ ] 代理商树形列表
- [ ] 新增/编辑代理商对话框
- [ ] 代理商详情页面
- [ ] 组件开发
- [ ] `AgentTreeTable.vue` - 代理商树形表格组件
- [ ] Mock 数据
- [ ] 模拟代理商层级数据
#### 3.5 企业客户管理
**优先级P1**
**子任务:**
- [ ] 类型定义
- [ ] API 服务
- [ ] 获取企业客户列表
- [ ] 创建企业客户
- [ ] 编辑企业客户
- [ ] 禁用/启用企业客户
- [ ] 分配客户角色
- [ ] 页面开发
- [ ] `src/views/account-management/enterprise-customer/index.vue`
- [ ] 企业客户列表
- [ ] 新增/编辑企业客户对话框
- [ ] Mock 数据
#### 3.6 客户账号管理
**优先级P1**
**子任务:**
- [ ] 类型定义
- [ ] API 服务
- [ ] 获取客户账号列表(代理商+企业客户)
- [ ] 解绑手机
- [ ] 重置密码
- [ ] 禁用/启用账号
- [ ] 页面开发
- [ ] 账号列表(支持筛选)
- [ ] 账号操作(解绑、重置密码等)
- [ ] Mock 数据
---
### 四、账户管理模块
#### 4.1 客户账户(佣金查看)
**优先级P2**
**子任务:**
- [ ] 类型定义
- [ ] API 服务
- [ ] 获取客户账户列表
- [ ] 查看佣金详情
- [ ] 查看提现记录
- [ ] 页面开发
- [ ] 客户账户列表
- [ ] 佣金详情展示
- [ ] 提现记录列表
- [ ] 组件开发
- [ ] `CommissionDetailCard.vue` - 佣金详情卡片
- [ ] Mock 数据
#### 4.2 佣金提现管理
**优先级P2**
**子任务:**
- [ ] 类型定义
- [ ] API 服务
- [ ] 获取提现申请列表
- [ ] 审核提现申请(通过/拒绝)
- [ ] 查看提现详情
- [ ] 批量审核
- [ ] 页面开发
- [ ] `src/views/finance-management/commission-withdrawal/index.vue`
- [ ] 提现申请列表
- [ ] 审核对话框
- [ ] 批量审核功能
- [ ] Mock 数据
#### 4.3 佣金提现设置
**优先级P2**
**子任务:**
- [ ] 类型定义
- [ ] API 服务
- [ ] 获取提现设置
- [ ] 保存提现设置
- [ ] 查看历史设置
- [ ] 页面开发
- [ ] `src/views/finance-management/withdrawal-setting/index.vue`
- [ ] 提现参数配置表单
- [ ] 历史设置列表
- [ ] Mock 数据
#### 4.4 我的账户(佣金)
**优先级P2**
**子任务:**
- [ ] 类型定义
- [ ] API 服务
- [ ] 获取当前账号佣金数据
- [ ] 获取佣金明细
- [ ] 申请提现
- [ ] 页面开发
- [ ] 佣金概览(卡片展示,非列表)
- [ ] 佣金明细列表
- [ ] 提现申请对话框
- [ ] 组件开发
- [ ] `CommissionOverviewCard.vue` - 佣金概览卡片
- [ ] Mock 数据
---
### 五、我的设置模块
#### 5.1 收款商户设置
**优先级P2**
**子任务:**
- [ ] 类型定义
- [ ] API 服务
- [ ] 获取支付配置
- [ ] 保存支付配置
- [ ] 页面开发
- [ ] `src/views/my-settings/payment-merchant/index.vue`
- [ ] 支付参数配置表单
- [ ] Mock 数据
#### 5.2 开发能力管理
**优先级P2**
**子任务:**
- [ ] 类型定义
- [ ] API 服务
- [ ] 获取开发能力参数
- [ ] 生成 API Key
- [ ] 刷新 Secret
- [ ] 页面开发
- [ ] `src/views/my-settings/dev-capability/index.vue`
- [ ] API Key 展示和管理
- [ ] 接口文档链接
- [ ] Mock 数据
#### 5.3 分佣模板管理
**优先级P2**
**子任务:**
- [ ] 类型定义
- [ ] API 服务
- [ ] 获取分佣模板列表
- [ ] 创建分佣模板
- [ ] 编辑分佣模板
- [ ] 删除分佣模板
- [ ] 页面开发
- [ ] `src/views/my-settings/commission-template/index.vue`
- [ ] 模板列表
- [ ] 新增/编辑模板对话框
- [ ] 分佣规则配置
- [ ] 组件开发
- [ ] `CommissionRuleConfig.vue` - 分佣规则配置组件
- [ ] Mock 数据
---
### 六、商品管理模块
#### 6.1 号卡管理
**优先级P1**
**子任务:**
- [ ] 类型定义
- [ ] API 服务
- [ ] 获取号卡商品列表
- [ ] 新增号卡商品
- [ ] 编辑号卡商品
- [ ] 删除号卡商品
- [ ] 页面开发
- [ ] `src/views/product-management/simcard/index.vue`
- [ ] 号卡列表
- [ ] 新增/编辑号卡对话框
- [ ] Mock 数据
#### 6.2 号卡分配
**优先级P1**
**子任务:**
- [ ] 类型定义
- [ ] API 服务
- [ ] 获取可分配号卡列表
- [ ] 分配号卡给代理
- [ ] 设置佣金模式
- [ ] 页面开发
- [ ] `src/views/product-management/simcard-assign/index.vue`
- [ ] 号卡选择
- [ ] 代理商选择
- [ ] 佣金模式配置
- [ ] 组件开发
- [ ] `CommissionModeSelector.vue` - 佣金模式选择组件
- [ ] Mock 数据
#### 6.3 套餐系列管理
**优先级P1**
**子任务:**
- [ ] 类型定义
- [ ] API 服务
- [ ] 获取套餐系列列表
- [ ] 新增套餐系列
- [ ] 编辑套餐系列
- [ ] 删除套餐系列
- [ ] 页面开发
- [ ] `src/views/product-management/package-series/index.vue` (重命名 package-series)
- [ ] 系列列表
- [ ] 新增/编辑系列对话框
- [ ] Mock 数据
#### 6.4 套餐管理
**优先级P1**
**子任务:**
- [ ] 类型定义
- [ ] API 服务
- [ ] 获取套餐列表(根据角色过滤)
- [ ] 创建套餐
- [ ] 编辑套餐
- [ ] 删除套餐
- [ ] 页面开发
- [ ] 套餐列表(支持角色过滤)
- [ ] 新增/编辑套餐对话框
- [ ] Mock 数据
#### 6.5 套餐分配
**优先级P1**
**子任务:**
- [ ] 类型定义
- [ ] API 服务
- [ ] 获取可分配套餐列表
- [ ] 分配套餐给直级代理
- [ ] 设置佣金模式
- [ ] 页面开发
- [ ] 重构 `src/views/package-management/package-assign/index.vue`
- [ ] 套餐选择
- [ ] 直级代理选择
- [ ] 佣金模式配置
- [ ] Mock 数据
---
### 七、资产管理模块
#### 7.1 单卡信息查询
**优先级P1**
**子任务:**
- [ ] 类型定义
- [ ] API 服务
- [ ] 根据 ICCID 查询单卡信息
- [ ] 套餐充值
- [ ] 停复机操作
- [ ] 流量详情查询
- [ ] 更改过期时间
- [ ] 转新卡
- [ ] 停复机记录
- [ ] 往期订单
- [ ] 增减流量
- [ ] 变更钱包余额
- [ ] 充值支付密码
- [ ] 续充
- [ ] 设备操作
- [ ] 页面开发
- [ ] 单卡信息展示(描述列表)
- [ ] 操作按钮组
- [ ] 各类操作对话框
- [ ] 组件开发
- [ ] 复用 `CardOperationDialog.vue` 并扩展功能
- [ ] Mock 数据
#### 7.2 网卡管理
**优先级P1**
**子任务:**
- [ ] 类型定义
- [ ] API 服务
- [ ] 获取网卡列表
- [ ] 批量操作入口
- [ ] 页面开发
- [ ] 网卡列表
- [ ] 批量操作按钮
- [ ] Mock 数据
#### 7.3 设备管理
**优先级P1**
**子任务:**
- [ ] 类型定义
- [ ] API 服务
- [ ] 获取设备列表
- [ ] 查看设备卡信息
- [ ] 修改设备卡信息
- [ ] 设备相关操作
- [ ] 页面开发
- [ ] 拆分 `src/views/device-management/devices/index.vue` (2022行需拆分)
- [ ] 主页面 `index.vue`
- [ ] 设备详情组件 `DeviceDetail.vue`
- [ ] 设备操作组件 `DeviceOperation.vue`
- [ ] 设备列表
- [ ] 设备详情对话框
- [ ] 设备操作对话框
- [ ] Composables
- [ ] `useDeviceManagement.ts` - 设备管理逻辑
- [ ] Mock 数据
#### 7.4 资产分配
**优先级P1**
**子任务:**
- [ ] 类型定义
- [ ] API 服务
- [ ] 设备批量分配
- [ ] 网卡批量分配
- [ ] 页面开发
- [ ] `src/views/asset-management/asset-assign/index.vue`
- [ ] 分配方式选择(设备/网卡)
- [ ] 批量导入 ICCID
- [ ] 目标代理选择
- [ ] 组件开发
- [ ] 复用 `ImportDialog.vue`
- [ ] Mock 数据
#### 7.5 换卡申请
**优先级P1**
**子任务:**
- [ ] 类型定义
- [ ] API 服务
- [ ] 获取换卡申请列表
- [ ] 处理换卡申请
- [ ] 填充新 ICCID
- [ ] 页面开发
- [ ] 申请列表
- [ ] 处理对话框
- [ ] Mock 数据
---
### 八、批量操作模块
#### 8.1 网卡导入
**优先级P1**
**子任务:**
- [ ] 类型定义
- [ ] API 服务
- [ ] 批量导入 ICCID
- [ ] 查看导入任务状态
- [ ] 查看导入失败记录
- [ ] 页面开发
- [ ] `src/views/batch-operation/card-import/index.vue`
- [ ] Excel 导入
- [ ] 任务列表
- [ ] 失败记录查看
- [ ] 组件开发
- [ ] 复用 `ImportDialog.vue`
- [ ] Mock 数据
#### 8.2 设备导入
**优先级P1**
**子任务:**
- [ ] 类型定义
- [ ] API 服务
- [ ] 批量导入设备及 ICCID 关系
- [ ] 查看导入任务状态
- [ ] 页面开发
- [ ] `src/views/batch-operation/device-import/index.vue`
- [ ] Excel 导入
- [ ] 任务列表
- [ ] Mock 数据
#### 8.3 线下批量充值
**优先级P1**
**子任务:**
- [ ] 类型定义
- [ ] API 服务
- [ ] 查看批量充值记录
- [ ] Excel 批量充值导入
- [ ] 页面开发
- [ ] 充值记录列表
- [ ] 批量充值 Excel 导入
- [ ] Mock 数据
#### 8.4 换卡通知
**优先级P2**
**子任务:**
- [ ] 类型定义
- [ ] API 服务
- [ ] 单独新建换卡通知
- [ ] 批量新建换卡通知
- [ ] 查看换卡通知记录
- [ ] 页面开发
- [ ] `src/views/batch-operation/card-change-notice/index.vue`
- [ ] 通知记录列表
- [ ] 新建通知对话框(单个/批量)
- [ ] Mock 数据
---
## 📅 开发计划建议
### 第一阶段1-2 周):基础架构
- 完成所有 1.x 任务API 层、配置、Composables、公共组件
- 完成登录模块2.1
### 第二阶段2-3 周):账号管理
- 完成账号管理模块所有功能3.1-3.6
### 第三阶段2 周):商品管理
- 完成商品管理模块6.1-6.5
### 第四阶段2-3 周):资产管理
- 完成资产管理模块7.1-7.5
### 第五阶段1-2 周):账户管理 + 我的设置
- 完成账户管理模块4.1-4.4
- 完成我的设置模块5.1-5.3
### 第六阶段1-2 周):批量操作 + 优化
- 完成批量操作模块8.1-8.4
- 性能优化、测试、Bug 修复
---
## 📌 注意事项
1. **所有模块先用 Mock 数据开发,便于前后端并行开发**
2. **严格遵循组件化原则,提高代码复用率**
3. **使用 TypeScript 严格类型,减少运行时错误**
4. **每个模块完成后进行代码审查**
5. **关键功能需要编写单元测试**
---
## 🔗 相关文档
- [项目架构文档](./openspec/project.md)
- [API 规范文档](待创建)
- [组件使用文档](待创建)

2791
bun.lock Normal file

File diff suppressed because it is too large Load Diff

1
components.d.ts vendored
View File

@@ -14,7 +14,6 @@ declare module 'vue' {
ArtBreadcrumb: typeof import('./src/components/core/layouts/art-breadcrumb/index.vue')['default']
ArtButtonMore: typeof import('./src/components/core/forms/ArtButtonMore.vue')['default']
ArtButtonTable: typeof import('./src/components/core/forms/ArtButtonTable.vue')['default']
ArtCardBanner: typeof import('./src/components/core/banners/ArtCardBanner.vue')['default']
ArtChatWindow: typeof import('./src/components/core/layouts/art-chat-window/index.vue')['default']
ArtCutterImg: typeof import('./src/components/core/media/ArtCutterImg.vue')['default']
ArtDataListCard: typeof import('./src/components/core/cards/ArtDataListCard.vue')['default']

View File

@@ -1,343 +0,0 @@
# API 对接说明
> 更新时间: 2026-01-09
---
## ✅ 已完成的修改
### 1. 创建了认证 API 服务
**文件**: `src/api/authApi.ts`
```typescript
import { AuthService } from '@/api/authApi'
// 登录
AuthService.login({ username, password, remember })
// 获取用户信息
AuthService.getUserInfo()
// 登出
AuthService.logout()
// 刷新Token
AuthService.refreshToken(refreshToken)
```
### 2. 修改了登录逻辑
**文件**: `src/composables/useLogin.ts`
**关键修改**:
- ✅ 添加了 `USE_MOCK` 开关(第 29 行)
- ✅ 创建了 `handleRealLogin()` 真实API登录函数
- ✅ 保留了 `handleMockLogin()` Mock登录函数方便开发测试
**切换方式**:
```typescript
// 文件: src/composables/useLogin.ts 第 29 行
// 使用 Mock 数据(开发测试)
const USE_MOCK = true
// 使用真实 API生产环境
const USE_MOCK = false // ← 当前设置
```
---
## 🔌 API 接口规范
### 登录接口
**请求地址**: `POST /api/auth/login`
**请求参数**:
```typescript
{
username: string // 用户名
password: string // 密码
remember?: boolean // 是否记住密码
captcha?: string // 验证码(可选)
}
```
**响应格式**:
```typescript
{
code: number // 状态码200 成功
message: string // 消息
data: {
token: string // 访问令牌 (必需)
refreshToken?: string // 刷新令牌 (可选)
expiresIn?: number // 过期时间(秒) (可选)
userInfo: { // 用户信息 (推荐返回,避免二次请求)
id: string | number
username: string
realName: string
roles: string[] // 角色列表 ['R_SUPER', 'R_ADMIN', ...]
permissions: string[] // 权限列表 ['*:*:*', 'user:edit', ...]
avatar?: string
email?: string
phone?: string
// ... 其他用户信息
}
}
}
```
### 获取用户信息接口
**请求地址**: `GET /api/auth/userInfo`
**请求头**:
```
Authorization: Bearer {token}
```
**响应格式**:
```typescript
{
code: 200,
message: 'success',
data: {
id: string | number
username: string
realName: string
roles: string[]
permissions: string[]
avatar?: string
email?: string
phone?: string
}
}
```
---
## 🔐 权限控制说明
### 角色定义
系统预定义了以下角色(可根据需求调整):
```typescript
enum UserRole {
SUPER_ADMIN = 'R_SUPER', // 超级管理员
ADMIN = 'R_ADMIN', // 管理员
AGENT = 'R_AGENT', // 代理商
ENTERPRISE = 'R_ENTERPRISE' // 企业客户
}
```
### 权限格式
权限使用通配符格式:`模块:操作:范围`
**示例**:
- `*:*:*` - 所有权限
- `user:*:*` - 用户模块所有权限
- `user:edit:*` - 用户编辑权限
- `card:view:own` - 查看自己的卡
### 前端权限验证
**路由级权限**(在 `asyncRoutes.ts` 中配置):
```typescript
{
path: '/system/user',
meta: {
roles: ['R_SUPER', 'R_ADMIN'] // 只有超管和管理员可访问
}
}
```
**按钮级权限**(使用 `v-auth` 指令):
```vue
<el-button v-auth="'user:edit'">编辑</el-button>
```
---
## 📦 状态码规范
**推荐使用的状态码** (已在 `src/utils/http/status.ts` 定义):
```typescript
export enum ApiStatus {
success = 200, // 成功
unauthorized = 401, // 未授权
forbidden = 403, // 禁止访问
notFound = 404, // 未找到
serverError = 500 // 服务器错误
}
```
---
## 🚀 快速上手
### 1. 配置API地址
**文件**: `.env.development`
```env
# API 地址前缀
VITE_API_URL = http://your-backend-api.com
# 或者使用代理(推荐)
VITE_API_URL = /api
```
**文件**: `vite.config.ts` (如果使用代理)
```typescript
server: {
proxy: {
'/api': {
target: 'http://your-backend-api.com', // 后端地址
changeOrigin: true,
rewrite: (path) => path.replace(/^\/api/, '')
}
}
}
```
### 2. 切换到真实API
**文件**: `src/composables/useLogin.ts` 第 29 行
```typescript
const USE_MOCK = false // 改为 false
```
### 3. 测试登录
1. 启动开发服务器:`npm run dev`
2. 访问登录页面:`http://localhost:3006/#/auth/login`
3. 输入后端提供的测试账号密码
4. 点击登录
---
## 🔧 调试技巧
### 1. 查看请求/响应
打开浏览器开发者工具F12→ Network 标签,查看:
- 请求URL
- 请求参数
- 响应数据
- 状态码
### 2. Mock 和真实API切换
```typescript
// src/composables/useLogin.ts
const USE_MOCK = true // 开发时使用 Mock
const USE_MOCK = false // 对接时使用真实 API
```
### 3. Token 检查
打开浏览器控制台F12→ Application → Local Storage → 查看 `user` 键:
```json
{
"isLogin": true,
"accessToken": "your-token-here",
"info": { ... }
}
```
### 4. 请求拦截器日志
**文件**: `src/utils/http/interceptors.ts`
已自动添加请求/响应日志,在控制台可以看到:
- 🚀 发送请求
- ✅ 请求成功
- ❌ 请求失败
---
## ⚠️ 常见问题
### Q1: 登录后提示"获取用户角色失败"
**原因**: 后端返回的用户信息中没有 `roles` 字段
**解决**:
1. 确保后端返回的 `userInfo.roles` 是数组
2. 至少包含一个角色,如:`["R_ADMIN"]`
### Q2: 登录成功但页面一直转圈
**原因**: 路由守卫中没有通过权限验证
**解决**:
1. 检查用户信息中是否有 `roles` 字段
2. 临时开启开发模式跳过权限验证(见下文)
### Q3: 如何临时跳过权限验证?
**文件**: `src/router/guards/beforeEach.ts` 第 28 行
```typescript
const DEV_MODE_SKIP_AUTH = true // 改为 true跳过所有权限验证
```
### Q4: Token 过期如何处理?
系统已自动处理 Token 过期:
1. 检测到 401 状态码自动跳转登录页
2. 可实现自动刷新 Token需要后端支持
---
## 📝 后端接口开发建议
### 1. 登录接口返回完整用户信息
避免前端登录后再次调用获取用户信息接口,减少请求次数。
### 2. 用户信息必须包含 roles
```json
{
"roles": ["R_ADMIN", "R_USER"] // 必须
}
```
### 3. 支持 Token 刷新机制
```typescript
// 刷新Token接口
POST /api/auth/refresh
{
"refreshToken": "refresh-token-here"
}
```
### 4. 统一响应格式
```typescript
{
code: 200,
message: "success",
data: { ... }
}
```
---
## ✨ 下一步
1. ✅ 后端提供测试环境API地址
2. ✅ 配置 `VITE_API_URL` 或代理
3. ✅ 切换 `USE_MOCK = false`
4. ✅ 测试登录功能
5. ✅ 根据实际API调整请求/响应格式
6. ✅ 完善其他业务接口
---
**联系支持**:
- 前端问题:查看本文档或项目 README
- 后端对接:与后端开发人员沟通接口格式
祝对接顺利!🚀

View File

@@ -1,244 +0,0 @@
# 登录模块说明文档
## 概述
本项目的登录模块已完成重构,采用了全新的架构设计,提供了完整的认证流程和权限管理功能。
## 功能特性
### 1. Mock 数据支持
系统内置了 4 个 Mock 账号,用于开发和演示:
| 账号类型 | 用户名 | 密码 | 角色 | 权限说明 |
|---------|--------|------|------|---------|
| 超级管理员 | super | 123456 | SUPER_ADMIN | 拥有所有权限 |
| 平台管理员 | admin | 123456 | ADMIN | 账号、网卡、套餐、设备等管理权限 |
| 代理商 | agent | 123456 | AGENT | 网卡查看/操作、佣金管理等权限 |
| 企业客户 | enterprise | 123456 | ENTERPRISE | 自有网卡和设备的查看/操作权限 |
**文件位置**: `src/mock/auth.ts`
### 2. 记住密码功能
- 用户可以勾选"记住密码"选项
- 登录成功后,用户名和密码会被加密存储到 localStorage
- 下次访问登录页时自动填充账号信息
- 取消勾选会清除已保存的凭证
**实现文件**: `src/utils/auth/rememberPassword.ts`
**核心函数**:
```typescript
// 保存凭证
saveCredentials(username, password, remember)
// 获取保存的凭证
getRememberedCredentials()
// 清除凭证
clearRememberedCredentials()
```
### 3. 完善的表单验证
提供了多种验证规则,适用于不同场景:
- **用户名验证**: 3-20个字符只能包含字母、数字和下划线
- **密码验证**: 6-20个字符
- **强密码验证**: 8-20个字符必须包含大小写字母和数字
- **确认密码验证**: 必须与密码一致
- **手机号验证**: 支持中国大陆手机号格式
- **邮箱验证**: 标准邮箱格式验证
- **验证码验证**: 4位数字
**实现文件**: `src/utils/auth/loginValidation.ts`
**使用示例**:
```typescript
import { usernameRules, passwordRules } from '@/utils/auth/loginValidation'
const rules = {
username: usernameRules(t),
password: passwordRules(t)
}
```
### 4. 权限路由守卫
优化了路由守卫,增加了以下功能:
#### 白名单机制
不需要登录即可访问的路由:
- `/login` - 登录页
- `/register` - 注册页
- `/forget-password` - 忘记密码
- `/exception/*` - 异常页面
**文件位置**: `src/router/guards/permission.ts`
#### Token 验证
- 自动检查 Token 是否存在
- 验证 Token 是否有效
- Token 过期自动跳转到登录页
#### 页面级权限验证
- 根据路由 meta 中的 `roles``permissions` 进行验证
- 无权限访问时自动跳转到 403 页面
- 支持通配符权限(如 `*:*:*` 表示所有权限)
#### 登录重定向
- 访问需要登录的页面时,会记录当前路径
- 登录成功后自动跳转回之前访问的页面
- 如果是白名单路由,则跳转到首页
**核心函数**:
```typescript
// 检查是否在白名单
isInWhiteList(path)
// 检查路由权限
hasRoutePermission(route, userInfo)
// 检查 Token 是否有效
isTokenValid(token)
// 构建登录重定向URL
buildLoginRedirect(currentPath)
```
### 5. useLogin Composable
将登录逻辑抽取为可复用的 Composable提高代码可维护性。
**文件位置**: `src/composables/useLogin.ts`
**提供的功能**:
- 表单状态管理
- Mock 账号切换
- 登录逻辑处理
- 自动记住密码
- 登录成功通知
- 重定向处理
**使用示例**:
```vue
<script setup>
import { useLogin } from '@/composables/useLogin'
const {
formRef,
formData,
rules,
loading,
mockAccounts,
setupAccount,
handleLogin
} = useLogin()
</script>
```
## 文件结构
```
src/
├── mock/
│ └── auth.ts # Mock 账号数据
├── utils/auth/
│ ├── rememberPassword.ts # 记住密码工具
│ ├── loginValidation.ts # 表单验证规则
│ └── index.ts # 统一导出
├── router/guards/
│ ├── permission.ts # 权限验证工具
│ ├── beforeEach.ts # 路由前置守卫(已优化)
│ └── afterEach.ts # 路由后置守卫
├── composables/
│ └── useLogin.ts # 登录逻辑 Composable
└── views/auth/login/
└── index.vue # 登录页面(已重构)
```
## 权限配置示例
### 路由权限配置
```typescript
// 在路由配置中添加 meta
{
path: '/account/platform',
meta: {
title: '平台账号管理',
roles: ['R_SUPER', 'R_ADMIN'], // 允许的角色
permissions: ['account:view:*'] // 需要的权限
}
}
```
### 用户权限定义
```typescript
// 用户信息中包含角色和权限
{
roles: ['R_ADMIN'],
permissions: [
'account:*:*', // 账号管理所有权限
'card:view:*', // 网卡查看权限
'card:operation:*' // 网卡操作权限
]
}
```
## 开发建议
### 1. 切换到真实接口
当后端接口就绪后,只需修改 `useLogin.ts` 中的登录逻辑:
```typescript
// 将 Mock 登录替换为真实 API
// const loginResult = mockLogin(formData.username, formData.password)
const loginResult = await AuthService.login({
username: formData.username,
password: formData.password
})
```
### 2. Token 刷新
`src/router/guards/permission.ts``isTokenValid` 函数中添加真实的 JWT 验证逻辑。
### 3. 权限粒度
根据实际业务需求,可以在以下层级添加权限验证:
- **路由级**: 在路由 meta 中配置
- **页面级**: 在页面组件中使用 v-if 判断
- **按钮级**: 使用自定义指令(如 `v-permission`
## 常见问题
### Q1: 如何添加新的 Mock 账号?
编辑 `src/mock/auth.ts`,在 `MOCK_ACCOUNTS` 数组中添加新账号即可。
### Q2: 记住密码的数据存在哪里?
存储在浏览器的 localStorage 中key 为 `remembered_credentials`,数据经过 Base64 编码。
### Q3: 如何自定义白名单路由?
编辑 `src/router/guards/permission.ts``LOGIN_WHITE_LIST` 数组。
### Q4: 为什么登录后还是跳转到登录页?
检查以下几点:
1. Token 是否正确保存到 store
2. `userStore.isLogin` 是否设置为 true
3. 路由守卫中的权限验证是否通过
## 下一步计划
- [ ] 添加双因素认证2FA
- [ ] 实现 SSO 单点登录
- [ ] 添加第三方登录(微信、钉钉等)
- [ ] 完善密码强度检测
- [ ] 添加登录日志记录

View File

@@ -1,9 +0,0 @@
touch README.md
git init
git checkout -b main
git add README.md
git commit -m "first commit"
git remote add origin https://git.boss160.cn/luo/one-pipe-system.git
git push -u origin main
你需要注意下.gitignore 是否需要加一些文件进去

View File

@@ -1,870 +0,0 @@
# 账号管理下面新增一个代理账号管理, 页面样式可以参考/system/account 都需要token认证
# 代理账号列表
## OpenAPI Specification
```yaml
openapi: 3.0.1
info:
title: ''
description: ''
version: 1.0.0
paths:
/api/admin/shop-accounts:
get:
summary: 代理账号列表
deprecated: false
description: ''
tags:
- 代理账号管理
- 代理账号管理
parameters:
- name: page
in: query
description: 页码
required: false
schema:
description: 页码
minimum: 1
type: integer
- name: page_size
in: query
description: 每页数量
required: false
schema:
description: 每页数量
maximum: 100
minimum: 1
type: integer
- name: shop_id
in: query
description: 店铺ID过滤
required: false
schema:
description: 店铺ID过滤
minimum: 1
type: integer
nullable: true
- name: username
in: query
description: 用户名(模糊查询)
required: false
schema:
description: 用户名(模糊查询)
maxLength: 50
type: string
- name: phone
in: query
description: 手机号(精确查询)
required: false
schema:
description: 手机号(精确查询)
maxLength: 11
minLength: 11
type: string
- name: status
in: query
description: 状态 (0:禁用, 1:启用)
required: false
schema:
description: 状态 (0:禁用, 1:启用)
type: integer
nullable: true
responses:
'200':
description: OK
content:
application/json:
schema:
$ref: '#/components/schemas/ModelShopAccountPageResult'
headers: {}
x-apifox-name: ''
'400':
description: 请求参数错误
content:
application/json:
schema: &ref_0
$ref: '#/components/schemas/ErrorResponse'
headers: {}
x-apifox-name: ''
'401':
description: 未认证或认证已过期
content:
application/json:
schema: *ref_0
headers: {}
x-apifox-name: ''
'403':
description: 无权访问
content:
application/json:
schema: *ref_0
headers: {}
x-apifox-name: ''
'500':
description: 服务器内部错误
content:
application/json:
schema: *ref_0
headers: {}
x-apifox-name: ''
security:
- BearerAuth: []
x-apifox:
schemeGroups:
- id: '-jWpeN1-jYBl-SEBTmsrq'
schemeIds:
- BearerAuth
required: true
use:
id: '-jWpeN1-jYBl-SEBTmsrq'
scopes:
'-jWpeN1-jYBl-SEBTmsrq':
BearerAuth: []
x-apifox-folder: 代理账号管理
x-apifox-status: released
x-run-in-apifox: https://app.apifox.com/web/project/7591618/apis/api-408366334-run
components:
schemas:
ModelShopAccountPageResult:
properties:
items:
description: 代理账号列表
items:
$ref: '#/components/schemas/ModelShopAccountResponse'
type: array
nullable: true
page:
description: 当前页码
type: integer
size:
description: 每页数量
type: integer
total:
description: 总记录数
type: integer
type: object
x-apifox-orders:
- items
- page
- size
- total
x-apifox-ignore-properties: []
x-apifox-folder: ''
ModelShopAccountResponse:
properties:
created_at:
description: 创建时间
type: string
id:
description: 账号ID
minimum: 0
type: integer
phone:
description: 手机号
type: string
shop_id:
description: 店铺ID
minimum: 0
type: integer
shop_name:
description: 店铺名称
type: string
status:
description: 状态 (0:禁用, 1:启用)
type: integer
updated_at:
description: 更新时间
type: string
user_type:
description: 用户类型 (1:超级管理员, 2:平台用户, 3:代理账号, 4:企业账号)
type: integer
username:
description: 用户名
type: string
type: object
x-apifox-orders:
- created_at
- id
- phone
- shop_id
- shop_name
- status
- updated_at
- user_type
- username
x-apifox-ignore-properties: []
x-apifox-folder: ''
ErrorResponse:
properties:
code:
description: 错误码
type: integer
message:
description: 错误消息
type: string
timestamp:
description: 时间戳
format: date-time
type: string
required:
- code
- message
- timestamp
type: object
x-apifox-orders:
- code
- message
- timestamp
x-apifox-ignore-properties: []
x-apifox-folder: ''
securitySchemes:
BearerAuth:
bearerFormat: JWT
scheme: bearer
type: jwt
servers:
- url: https://cmp-api.boss160.cn
description: 测试环境
security: []
```
# 创建代理账号
## OpenAPI Specification
```yaml
openapi: 3.0.1
info:
title: ''
description: ''
version: 1.0.0
paths:
/api/admin/shop-accounts:
post:
summary: 创建代理账号
deprecated: false
description: ''
tags:
- 代理账号管理
- 代理账号管理
parameters: []
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/ModelCreateShopAccountRequest'
responses:
'200':
description: OK
content:
application/json:
schema:
$ref: '#/components/schemas/ModelShopAccountResponse'
headers: {}
x-apifox-name: ''
'400':
description: 请求参数错误
content:
application/json:
schema: &ref_0
$ref: '#/components/schemas/ErrorResponse'
headers: {}
x-apifox-name: ''
'401':
description: 未认证或认证已过期
content:
application/json:
schema: *ref_0
headers: {}
x-apifox-name: ''
'403':
description: 无权访问
content:
application/json:
schema: *ref_0
headers: {}
x-apifox-name: ''
'500':
description: 服务器内部错误
content:
application/json:
schema: *ref_0
headers: {}
x-apifox-name: ''
security:
- BearerAuth: []
x-apifox:
schemeGroups:
- id: LdX0-6IzkTw-pXBT8t4Km
schemeIds:
- BearerAuth
required: true
use:
id: LdX0-6IzkTw-pXBT8t4Km
scopes:
LdX0-6IzkTw-pXBT8t4Km:
BearerAuth: []
x-apifox-folder: 代理账号管理
x-apifox-status: released
x-run-in-apifox: https://app.apifox.com/web/project/7591618/apis/api-408366335-run
components:
schemas:
ModelCreateShopAccountRequest:
properties:
password:
description: 密码
maxLength: 32
minLength: 8
type: string
phone:
description: 手机号
maxLength: 11
minLength: 11
type: string
shop_id:
description: 店铺ID
minimum: 1
type: integer
username:
description: 用户名
maxLength: 50
minLength: 3
type: string
required:
- password
- phone
- shop_id
- username
type: object
x-apifox-orders:
- password
- phone
- shop_id
- username
x-apifox-ignore-properties: []
x-apifox-folder: ''
ModelShopAccountResponse:
properties:
created_at:
description: 创建时间
type: string
id:
description: 账号ID
minimum: 0
type: integer
phone:
description: 手机号
type: string
shop_id:
description: 店铺ID
minimum: 0
type: integer
shop_name:
description: 店铺名称
type: string
status:
description: 状态 (0:禁用, 1:启用)
type: integer
updated_at:
description: 更新时间
type: string
user_type:
description: 用户类型 (1:超级管理员, 2:平台用户, 3:代理账号, 4:企业账号)
type: integer
username:
description: 用户名
type: string
type: object
x-apifox-orders:
- created_at
- id
- phone
- shop_id
- shop_name
- status
- updated_at
- user_type
- username
x-apifox-ignore-properties: []
x-apifox-folder: ''
ErrorResponse:
properties:
code:
description: 错误码
type: integer
message:
description: 错误消息
type: string
timestamp:
description: 时间戳
format: date-time
type: string
required:
- code
- message
- timestamp
type: object
x-apifox-orders:
- code
- message
- timestamp
x-apifox-ignore-properties: []
x-apifox-folder: ''
securitySchemes:
BearerAuth:
bearerFormat: JWT
scheme: bearer
type: jwt
servers:
- url: https://cmp-api.boss160.cn
description: 测试环境
security: []
```
# 更新代理账号
## OpenAPI Specification
```yaml
openapi: 3.0.1
info:
title: ''
description: ''
version: 1.0.0
paths:
/api/admin/shop-accounts/{id}:
put:
summary: 更新代理账号
deprecated: false
description: ''
tags:
- 代理账号管理
- 代理账号管理
parameters:
- name: id
in: path
description: ID
required: true
example: 0
schema:
description: ID
minimum: 0
type: integer
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/ModelUpdateShopAccountParams'
responses:
'200':
description: OK
content:
application/json:
schema:
$ref: '#/components/schemas/ModelShopAccountResponse'
headers: {}
x-apifox-name: ''
'400':
description: 请求参数错误
content:
application/json:
schema: &ref_0
$ref: '#/components/schemas/ErrorResponse'
headers: {}
x-apifox-name: ''
'401':
description: 未认证或认证已过期
content:
application/json:
schema: *ref_0
headers: {}
x-apifox-name: ''
'403':
description: 无权访问
content:
application/json:
schema: *ref_0
headers: {}
x-apifox-name: ''
'500':
description: 服务器内部错误
content:
application/json:
schema: *ref_0
headers: {}
x-apifox-name: ''
security:
- BearerAuth: []
x-apifox:
schemeGroups:
- id: TTw5I9JG_CJv8tEsi1Bk6
schemeIds:
- BearerAuth
required: true
use:
id: TTw5I9JG_CJv8tEsi1Bk6
scopes:
TTw5I9JG_CJv8tEsi1Bk6:
BearerAuth: []
x-apifox-folder: 代理账号管理
x-apifox-status: released
x-run-in-apifox: https://app.apifox.com/web/project/7591618/apis/api-408366336-run
components:
schemas:
ModelUpdateShopAccountParams:
properties:
username:
description: 用户名
maxLength: 50
minLength: 3
type: string
required:
- username
type: object
x-apifox-orders:
- username
x-apifox-ignore-properties: []
x-apifox-folder: ''
ModelShopAccountResponse:
properties:
created_at:
description: 创建时间
type: string
id:
description: 账号ID
minimum: 0
type: integer
phone:
description: 手机号
type: string
shop_id:
description: 店铺ID
minimum: 0
type: integer
shop_name:
description: 店铺名称
type: string
status:
description: 状态 (0:禁用, 1:启用)
type: integer
updated_at:
description: 更新时间
type: string
user_type:
description: 用户类型 (1:超级管理员, 2:平台用户, 3:代理账号, 4:企业账号)
type: integer
username:
description: 用户名
type: string
type: object
x-apifox-orders:
- created_at
- id
- phone
- shop_id
- shop_name
- status
- updated_at
- user_type
- username
x-apifox-ignore-properties: []
x-apifox-folder: ''
ErrorResponse:
properties:
code:
description: 错误码
type: integer
message:
description: 错误消息
type: string
timestamp:
description: 时间戳
format: date-time
type: string
required:
- code
- message
- timestamp
type: object
x-apifox-orders:
- code
- message
- timestamp
x-apifox-ignore-properties: []
x-apifox-folder: ''
securitySchemes:
BearerAuth:
bearerFormat: JWT
scheme: bearer
type: jwt
servers:
- url: https://cmp-api.boss160.cn
description: 测试环境
security: []
```
# 重置代理账号密码
## OpenAPI Specification
```yaml
openapi: 3.0.1
info:
title: ''
description: ''
version: 1.0.0
paths:
/api/admin/shop-accounts/{id}/password:
put:
summary: 重置代理账号密码
deprecated: false
description: ''
tags:
- 代理账号管理
- 代理账号管理
parameters:
- name: id
in: path
description: ID
required: true
example: 0
schema:
description: ID
minimum: 0
type: integer
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/ModelUpdateShopAccountPasswordParams'
responses:
'400':
description: 请求参数错误
content:
application/json:
schema: &ref_0
$ref: '#/components/schemas/ErrorResponse'
headers: {}
x-apifox-name: ''
'401':
description: 未认证或认证已过期
content:
application/json:
schema: *ref_0
headers: {}
x-apifox-name: ''
'403':
description: 无权访问
content:
application/json:
schema: *ref_0
headers: {}
x-apifox-name: ''
'500':
description: 服务器内部错误
content:
application/json:
schema: *ref_0
headers: {}
x-apifox-name: ''
security:
- BearerAuth: []
x-apifox:
schemeGroups:
- id: IeBLfzHchBWHbFru7Ma0u
schemeIds:
- BearerAuth
required: true
use:
id: IeBLfzHchBWHbFru7Ma0u
scopes:
IeBLfzHchBWHbFru7Ma0u:
BearerAuth: []
x-apifox-folder: 代理账号管理
x-apifox-status: released
x-run-in-apifox: https://app.apifox.com/web/project/7591618/apis/api-408366337-run
components:
schemas:
ModelUpdateShopAccountPasswordParams:
properties:
new_password:
description: 新密码
maxLength: 32
minLength: 8
type: string
required:
- new_password
type: object
x-apifox-orders:
- new_password
x-apifox-ignore-properties: []
x-apifox-folder: ''
ErrorResponse:
properties:
code:
description: 错误码
type: integer
message:
description: 错误消息
type: string
timestamp:
description: 时间戳
format: date-time
type: string
required:
- code
- message
- timestamp
type: object
x-apifox-orders:
- code
- message
- timestamp
x-apifox-ignore-properties: []
x-apifox-folder: ''
securitySchemes:
BearerAuth:
bearerFormat: JWT
scheme: bearer
type: jwt
servers:
- url: https://cmp-api.boss160.cn
description: 测试环境
security: []
```
列表显示中状态用开关那个组件
# 启用/禁用代理账号
## OpenAPI Specification
```yaml
openapi: 3.0.1
info:
title: ''
description: ''
version: 1.0.0
paths:
/api/admin/shop-accounts/{id}/status:
put:
summary: 启用/禁用代理账号
deprecated: false
description: ''
tags:
- 代理账号管理
- 代理账号管理
parameters:
- name: id
in: path
description: ID
required: true
example: 0
schema:
description: ID
minimum: 0
type: integer
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/ModelUpdateShopAccountStatusParams'
responses:
'400':
description: 请求参数错误
content:
application/json:
schema: &ref_0
$ref: '#/components/schemas/ErrorResponse'
headers: {}
x-apifox-name: ''
'401':
description: 未认证或认证已过期
content:
application/json:
schema: *ref_0
headers: {}
x-apifox-name: ''
'403':
description: 无权访问
content:
application/json:
schema: *ref_0
headers: {}
x-apifox-name: ''
'500':
description: 服务器内部错误
content:
application/json:
schema: *ref_0
headers: {}
x-apifox-name: ''
security:
- BearerAuth: []
x-apifox:
schemeGroups:
- id: QRYgZh2IcZgAVmdF1dNBU
schemeIds:
- BearerAuth
required: true
use:
id: QRYgZh2IcZgAVmdF1dNBU
scopes:
QRYgZh2IcZgAVmdF1dNBU:
BearerAuth: []
x-apifox-folder: 代理账号管理
x-apifox-status: released
x-run-in-apifox: https://app.apifox.com/web/project/7591618/apis/api-408366338-run
components:
schemas:
ModelUpdateShopAccountStatusParams:
properties:
status:
description: 状态 (0:禁用, 1:启用)
type: integer
required:
- status
type: object
x-apifox-orders:
- status
x-apifox-ignore-properties: []
x-apifox-folder: ''
ErrorResponse:
properties:
code:
description: 错误码
type: integer
message:
description: 错误消息
type: string
timestamp:
description: 时间戳
format: date-time
type: string
required:
- code
- message
- timestamp
type: object
x-apifox-orders:
- code
- message
- timestamp
x-apifox-ignore-properties: []
x-apifox-folder: ''
securitySchemes:
BearerAuth:
bearerFormat: JWT
scheme: bearer
type: jwt
servers:
- url: https://cmp-api.boss160.cn
description: 测试环境
security: []
```

View File

@@ -1,488 +0,0 @@
# 物联网卡管理系统 - 开发任务规划
> 项目开始日期: 2026-01-09
> 当前状态: 进行中
---
## 📋 任务进度总览
- [ ] 阶段一基础设施和认证模块0/2
- [ ] 阶段二账号管理模块0/7
- [ ] 阶段三财务管理模块0/4
- [ ] 阶段四商品管理模块0/5
- [ ] 阶段五资产管理模块0/5
- [ ] 阶段六批量操作模块0/3
**总体完成度**: 0/26 任务
---
## 🚀 阶段一:基础设施和认证模块
### 1.1 登录模块
- [ ] 设计登录页面UI
- [ ] 实现用户名密码登录
- [ ] 实现记住密码功能
- [ ] 集成验证码/滑块验证
- [ ] 实现 Token 管理
- [ ] 实现自动刷新 Token
- [ ] 实现登出功能
- [ ] 添加登录错误处理
### 1.2 权限管理基础
- [ ] 设计权限控制策略RBAC
- [ ] 实现路由权限守卫
- [ ] 实现按钮级权限控制
- [ ] 实现数据权限过滤
- [ ] 完善角色权限映射
---
## 👥 阶段二:账号管理模块
### 2.1 平台角色管理
- [ ] 角色列表页面(表格展示)
- [ ] 新增角色功能
- [ ] 角色基础信息表单
- [ ] 权限分配(树形结构)
- [ ] 编辑角色功能
- [ ] 删除角色功能
- [ ] 角色搜索和筛选
- [ ] 角色权限预览
### 2.2 平台账号管理
- [ ] 平台账号列表页面
- [ ] 新增平台账号
- [ ] 账号信息表单
- [ ] 角色分配
- [ ] 账号状态设置
- [ ] 编辑平台账号
- [ ] 禁用/启用账号
- [ ] 重置密码功能
- [ ] 账号操作日志查看
### 2.3 客户角色管理
- [ ] 客户角色列表页面
- [ ] 新增客户角色
- [ ] 角色名称和描述
- [ ] 能力边界配置(权限树)
- [ ] 编辑客户角色
- [ ] 删除客户角色
- [ ] 角色能力说明文档
### 2.4 代理商管理
- [ ] 代理商列表页面
- [ ] 多级代理商树形展示
- [ ] 代理商等级标识
- [ ] 新增代理商
- [ ] 代理商基础信息
- [ ] 上级代理商选择
- [ ] 代理商等级设置
- [ ] 编辑代理商信息
- [ ] 代理商账号管理
- [ ] 查看代理商下的账号列表
- [ ] 为代理商创建子账号
- [ ] 代理商佣金配置
- [ ] 代理商数据统计面板
### 2.5 企业客户管理
- [ ] 企业客户列表页面
- [ ] 新增企业客户
- [ ] 企业基础信息
- [ ] 客户角色分配
- [ ] 联系人信息
- [ ] 编辑企业客户
- [ ] 企业客户账号管理
- [ ] 创建企业管理账号
- [ ] 账号权限设置
- [ ] 企业客户数据看板
### 2.6 客户账号管理
- [ ] 客户账号列表页面
- [ ] 支持按代理商/企业筛选
- [ ] 账号状态筛选
- [ ] 查看账号详情
- [ ] 解绑手机号
- [ ] 重置登录密码
- [ ] 禁用/启用账号
- [ ] 账号操作记录
### 2.7 客户账号佣金管理
- [ ] 客户账号佣金列表
- [ ] 佣金统计卡片
- [ ] 佣金明细表格
- [ ] 佣金详情查看
- [ ] 提现记录查看
- [ ] 佣金统计图表
---
## 💰 阶段三:财务管理模块
### 3.1 佣金提现管理
- [ ] 提现申请列表
- [ ] 状态筛选(待审核、已通过、已拒绝)
- [ ] 时间范围筛选
- [ ] 提现申请详情
- [ ] 审核提现申请
- [ ] 通过操作
- [ ] 拒绝操作(需填写原因)
- [ ] 批量审核功能
- [ ] 提现记录导出
### 3.2 佣金提现设置
- [ ] 提现参数配置页面
- [ ] 最低提现金额
- [ ] 提现手续费设置
- [ ] 单日提现次数限制
- [ ] 提现规则说明
- [ ] 配置历史记录
- [ ] 参数生效管理
### 3.3 我的账户(当前登录账号)
- [ ] 账户概览页面
- [ ] 佣金总额卡片
- [ ] 可提现金额卡片
- [ ] 待入账金额卡片
- [ ] 佣金收入明细
- [ ] 提现申请功能
- [ ] 收支流水记录
- [ ] 佣金统计图表
### 3.4 收款商户设置
- [ ] 支付参数配置页面
- [ ] 支付商户信息
- [ ] API密钥配置
- [ ] 回调地址设置
- [ ] 支付方式管理
- [ ] 微信支付
- [ ] 支付宝
- [ ] 银行卡
- [ ] 支付测试功能
- [ ] 配置安全验证
---
## 🛍️ 阶段四:商品管理模块
### 4.1 号卡管理
- [ ] 号卡列表页面
- [ ] 号卡信息展示
- [ ] 状态筛选
- [ ] 新增号卡
- [ ] 号卡基础信息
- [ ] 运营商选择
- [ ] 套餐配置
- [ ] 编辑号卡
- [ ] 号卡上下架
- [ ] 号卡库存管理
- [ ] 号卡详情页面
### 4.2 号卡分配
- [ ] 分配记录列表
- [ ] 为代理商分配号卡
- [ ] 选择代理商
- [ ] 选择号卡商品
- [ ] 设置分配数量
- [ ] 设置佣金模式
- [ ] 查看分配详情
- [ ] 撤销分配
- [ ] 分配统计报表
### 4.3 套餐系列管理
- [ ] 套餐系列列表
- [ ] 新增套餐系列
- [ ] 系列名称
- [ ] 系列描述
- [ ] 系列图标
- [ ] 编辑套餐系列
- [ ] 删除套餐系列
- [ ] 套餐系列排序
### 4.4 套餐管理
- [ ] 套餐列表页面
- [ ] 权限过滤(管理员看全部,普通用户看自己的)
- [ ] 按套餐系列筛选
- [ ] 新增套餐
- [ ] 套餐基础信息
- [ ] 套餐类型(流量、语音、短信)
- [ ] 套餐价格
- [ ] 有效期设置
- [ ] 编辑套餐
- [ ] 套餐上下架
- [ ] 套餐详情页面
### 4.5 套餐分配
- [ ] 套餐分配列表
- [ ] 为直级代理分配套餐
- [ ] 选择代理商
- [ ] 选择套餐
- [ ] 设置佣金模式
- [ ] 固定佣金
- [ ] 比例佣金
- [ ] 查看分配详情
- [ ] 修改佣金设置
- [ ] 分配统计
---
## 📦 阶段五:资产管理模块
### 5.1 单卡信息查询
- [ ] ICCID 查询界面
- [ ] 单卡详情页面
- [ ] 基础信息展示
- [ ] 套餐信息
- [ ] 使用情况
- [ ] 单卡操作功能
- [ ] 套餐充值
- [ ] 停机/复机
- [ ] 查看流量详情
- [ ] 更改过期时间
- [ ] 转新卡
- [ ] 查看停复机记录
- [ ] 查看往期订单
- [ ] 增减流量
- [ ] 变更钱包余额
- [ ] 充值支付密码
- [ ] 续充
- [ ] 设备操作入口
### 5.2 网卡管理
- [ ] 网卡列表页面
- [ ] 高级搜索
- [ ] 状态筛选
- [ ] 批量选择
- [ ] 网卡详情页面
- [ ] 批量操作入口
- [ ] 批量充值
- [ ] 批量停复机
- [ ] 批量分配
- [ ] 网卡导出功能
- [ ] 网卡数据统计
### 5.3 设备管理
- [ ] 设备列表页面
- [ ] 设备信息展示
- [ ] 在线状态
- [ ] 设备详情页面
- [ ] 设备基础信息
- [ ] 绑定卡信息
- [ ] 查看设备卡信息
- [ ] 修改设备卡绑定
- [ ] 设备相关操作
- [ ] 设备重启
- [ ] 设备诊断
- [ ] 设备数据统计
### 5.4 资产分配
- [ ] 资产分配页面
- [ ] 设备批量分配
- [ ] 选择代理商
- [ ] 上传设备列表
- [ ] 确认分配信息
- [ ] 网卡批量分配
- [ ] 选择代理商
- [ ] 上传网卡列表ICCID
- [ ] 自动关联设备处理
- [ ] 分配预览和确认
- [ ] 分配记录查看
- [ ] 分配回滚功能
### 5.5 换卡申请管理
- [ ] 换卡申请列表
- [ ] 状态筛选(待处理、已完成、已拒绝)
- [ ] 申请详情查看
- [ ] 旧卡信息
- [ ] 申请原因
- [ ] 处理换卡申请
- [ ] 填充新 ICCID
- [ ] 确认换卡
- [ ] 拒绝申请
- [ ] 换卡记录追溯
---
## 🔄 阶段六:批量操作模块
### 6.1 网卡批量导入
- [ ] 网卡导入页面
- [ ] 模板下载
- [ ] Excel 文件上传
- [ ] 数据预览
- [ ] 导入任务列表
- [ ] 任务状态
- [ ] 成功/失败统计
- [ ] 查看导入详情
- [ ] 成功记录
- [ ] 失败记录和原因
- [ ] 失败数据重新导入
### 6.2 设备批量导入
- [ ] 设备导入页面
- [ ] 模板下载
- [ ] Excel 文件上传(设备+ICCID关系
- [ ] 数据预览和校验
- [ ] 导入任务列表
- [ ] 查看导入结果
- [ ] 导入失败处理
### 6.3 线下批量充值
- [ ] 批量充值记录列表
- [ ] 新建批量充值
- [ ] 模板下载
- [ ] Excel 上传
- [ ] 充值预览
- [ ] 确认充值
- [ ] 充值详情查看
- [ ] 成功列表
- [ ] 失败列表
- [ ] 充值结果导出
### 6.4 换卡通知
- [ ] 换卡通知列表
- [ ] 单独创建换卡通知
- [ ] 选择网卡
- [ ] 填写通知内容
- [ ] 选择通知方式(短信/邮件)
- [ ] 批量创建换卡通知
- [ ] 上传网卡列表
- [ ] 设置通知内容
- [ ] 查看通知记录
- [ ] 发送状态
- [ ] 已读状态
---
## 🎨 阶段七:开发能力和其他设置
### 7.1 开发能力管理
- [ ] 开发能力列表页面
- [ ] API 密钥管理
- [ ] 生成密钥
- [ ] 重置密钥
- [ ] 密钥权限设置
- [ ] Webhook 配置
- [ ] API 文档集成
- [ ] API 调用统计
### 7.2 分佣模板管理
- [ ] 分佣模板列表
- [ ] 新增分佣模板
- [ ] 模板名称
- [ ] 分佣规则配置
- [ ] 适用范围
- [ ] 编辑分佣模板
- [ ] 删除分佣模板
- [ ] 模板应用记录
---
## 📊 阶段八:数据统计和报表
### 8.1 数据概览Dashboard
- [ ] 总体数据统计卡片
- [ ] 网卡总数
- [ ] 设备总数
- [ ] 今日充值金额
- [ ] 今日佣金
- [ ] 数据趋势图表
- [ ] 充值趋势
- [ ] 新增网卡趋势
- [ ] 代理商排行榜
- [ ] 套餐销售排行
### 8.2 业务报表
- [ ] 充值报表
- [ ] 佣金报表
- [ ] 代理商业绩报表
- [ ] 套餐使用报表
- [ ] 报表导出功能
---
## 🔧 阶段九:系统优化和完善
### 9.1 性能优化
- [ ] 长列表虚拟滚动
- [ ] 图片懒加载
- [ ] 接口请求优化
- [ ] 打包体积优化
### 9.2 用户体验优化
- [ ] 页面加载状态
- [ ] 错误提示优化
- [ ] 操作反馈优化
- [ ] 响应式适配
### 9.3 代码质量
- [ ] 代码规范检查
- [ ] 单元测试编写
- [ ] E2E 测试
- [ ] 代码注释完善
---
## 📝 开发规范
### 命名规范
- 组件名:大驼峰,如 `UserManagement.vue`
- 文件名:小写+连字符,如 `user-list.vue`
- 接口名RESTful 风格
- 路由名:小写+连字符
### 代码结构
```
src/
├── views/ # 页面组件
├── components/ # 公共组件
├── api/ # API 接口
├── store/ # 状态管理
├── router/ # 路由配置
├── utils/ # 工具函数
└── types/ # TypeScript 类型定义
```
### Git 提交规范
- `feat`: 新功能
- `fix`: 修复bug
- `docs`: 文档更新
- `style`: 代码格式调整
- `refactor`: 重构
- `test`: 测试相关
- `chore`: 构建/工具相关
---
## 🎯 里程碑
- [ ] **M1**: 基础设施完成(登录、权限) - 预计 3 天
- [ ] **M2**: 账号管理模块完成 - 预计 7 天
- [ ] **M3**: 财务管理模块完成 - 预计 5 天
- [ ] **M4**: 商品管理模块完成 - 预计 5 天
- [ ] **M5**: 资产管理模块完成 - 预计 7 天
- [ ] **M6**: 批量操作模块完成 - 预计 4 天
- [ ] **M7**: 数据统计和系统优化 - 预计 5 天
**预计总工期**: 36 个工作日
---
## 📌 注意事项
1. **优先级**:按阶段顺序开发,基础设施 > 核心业务 > 辅助功能
2. **接口对接**:等待后端 API 完成后再进行集成
3. **数据安全**:涉及敏感数据的操作需要二次确认
4. **性能考虑**:列表超过 1000 条需要使用虚拟滚动
5. **错误处理**:所有接口调用必须有错误处理
6. **权限控制**:每个页面和操作都需要权限验证
---
## 🔄 更新日志
### 2026-01-09
- 创建任务规划文档
- 定义开发阶段和任务拆分
- 明确开发规范和里程碑

View File

@@ -1,42 +0,0 @@
# 物联网卡管理系统 - 功能列表
## 账号管理模块
- 账号管理-客户角色 客户角色用以决定客户能力边界
- 账号管理-代理商管理 用以创建代理商以及管理特定代理商账号
- 账号管理-企业客户管理 用以创建企业管理账号,只能登录企业端,依赖客户角色
- 账号管理-客户账号管理 管理客户(代理商+企业客户)的账号,解绑手机等或针对该账号的操作
## 账户管理/财务模块
- 账户管理-客户账号 查看账号下全部的客户账号的佣金情况以及提现情况
- 账户管理/我的财务-佣金提现 管理全部的提现申请
- 账户管理-佣金提现设置 设置提现参数,生效最新的一条
- 我的财务-我的账户 获取当前登录账号的佣金相关数据(这里不应该跟奇成一样用列表)
## 我的设置模块
- 我的设置-收款商户设置 设置支付参数
- 我的设置-开发能力管理 获取开发能力对接参数以及管理
- 我的设置-分佣模板 用以创建以及管理分佣模板方便给代理分配产品时设置分佣规则
## 商品管理模块
- 商品管理-号卡管理 新增管理号卡商品,管理基础信息
- 商品管理-号卡分配 为特定代理分配号卡商品,同时设置佣金模式
- 商品管理-套餐系列管理 新增以及管理套餐系列
- 商品管理-套餐管理 新增以及管理套餐系列(只能看到自己的/管理员可以看到全部)
- 商品管理-套餐分配 为直级代理分配套餐同时设置佣金模式
## 资产管理模块
- 资产管理-单卡信息 通过ICCID查询单卡相关信息,以及相关操作如,套餐充值,停复机,流量详情,更改过期时间,转新卡,停复机记录,往期订单,增减流量,变更钱包余额,充值支付密码,续充,设备操作
- 资产管理-网卡管理 查询网卡信息,提供相关批量操作入口
- 资产管理-设备管理 查看设备信息,提供相关操作入口,查看设备卡信息,修改设备卡信息,设备相关操作
- 资产管理-资产分配 为特定代理分配网卡,只支持批量操作,批量操作分为两种,一种是设备批量分配,一种是网卡批量分配,如果使用网卡批量分配且网卡有设备信息,那么会把该卡所属设备以及网卡都分配过去
- 资产管理-换卡申请 客户提交的换卡申请管理,处理换卡的申请,填充新的iccid
## 批量操作模块
- 网卡管理-批量操作-网卡导入 批量导入iccid,以及查看导入任务情况
- 设备管理-批量操作-设备导入 批量导入设备以及iccid关系,查看导入任务情况
- 网卡管理-批量操作-线下批量充值 查看批量充值记录,提供批量充值excel导入
- 网卡管理-批量操作-换卡通知 可单独/批量新建换卡通知,查看换卡通知记录
---
**总计25个功能模块**

View File

@@ -1,364 +0,0 @@
# 物联网卡管理系统 - 页面开发完成总结
## 📊 完成概况
**完成时间**: 2026-01-09
**开发进度**: 13/13 页面 (100%)
**总计文件**: 13 个 Vue 页面组件
---
## ✅ 已完成的页面列表
### 1. 账号管理模块 (3个页面)
#### 1.1 客户角色管理
- **文件路径**: `src/views/account-management/customer-role/index.vue`
- **功能特性**:
- 角色列表展示CRUD操作
- 能力范围配置(使用 ElCheckboxGroup
- 角色启用/禁用状态管理
- 应用统计
- **组件使用**: ArtTable, ElDialog, ElForm, ElCheckboxGroup, ElTag
#### 1.2 代理商管理
- **文件路径**: `src/views/account-management/agent/index.vue`
- **功能特性**:
- 多层级代理商管理支持3级
- 代理商等级展示(一级/二级/三级)
- 子账号管理对话框
- 佣金配置(固定/比例佣金)
- 状态管理(正常/禁用)
- **组件使用**: ArtTable, ElDialog, ElDescriptions, ElRadioGroup, ElInputNumber
#### 1.3 客户账号管理
- **文件路径**: `src/views/account-management/customer-account/index.vue`
- **功能特性**:
- 客户账号列表
- 账号类型筛选(个人/企业/代理商)
- 账号详情查看
- 解绑手机、重置密码、启用/禁用操作
- 操作记录追踪
- **组件使用**: ArtTable, ElDialog, ElDescriptions, ElTag, ElButton
---
### 2. 财务管理模块 (3个页面)
#### 2.1 提现管理
- **文件路径**: `src/views/finance/withdrawal/index.vue`
- **功能特性**:
- 提现申请列表
- 状态筛选(待审核/已通过/已拒绝/已完成)
- 批量审核功能ElTable selection
- 审核/拒绝操作(带原因输入)
- 详情对话框展示完整提现信息
- **组件使用**: ArtTable, ElDialog, ElDescriptions, ElButton, ElTag
#### 2.2 我的账户
- **文件路径**: `src/views/finance/my-account/index.vue`
- **功能特性**:
- 账户概览卡片4个统计卡片渐变背景
- 提现申请表单(带手续费自动计算)
- 交易流水列表
- 类型筛选(全部/收入/提现)
- **组件使用**: ElCard, ElForm, ArtTable, ElTag
- **样式特色**: 渐变背景统计卡片
#### 2.3 提现设置
- **文件路径**: `src/views/finance/withdrawal-settings/index.vue`
- **功能特性**:
- 提现参数配置
- 手续费模式(固定/比例)
- 单日提现次数限制
- 到账时间设置
- 工作日限制开关
- 提现时间段设置ElTimePicker
- 配置历史记录
- **组件使用**: ElForm, ElInputNumber, ElRadioGroup, ElSwitch, ElTimePicker, ArtTable
---
### 3. 设置管理模块 (3个页面)
#### 3.1 支付商户配置
- **文件路径**: `src/views/settings/payment-merchant/index.vue`
- **功能特性**:
- 商户基础信息配置
- API配置AppID, AppSecret, API密钥
- 密钥显示/隐藏切换
- 回调地址配置(支付/退款)
- 支付方式启用(微信/支付宝/银行卡)
- 测试模式开关
- 配置说明文档
- **组件使用**: ElCard, ElForm, ElInput, ElButton, ElCheckboxGroup, ElSwitch
#### 3.2 开发者API管理
- **文件路径**: `src/views/settings/developer-api/index.vue`
- **功能特性**:
- API密钥管理生成/重置/删除)
- AppKey/AppSecret 展示(带复制功能)
- 密钥显示/隐藏切换
- 权限配置(读取/写入/删除)
- Webhook配置URL + 签名密钥)
- 事件订阅(多选框)
- API调用统计最近7天
- **组件使用**: ArtTable, ElDialog, ElButton, ElTag, ElCheckboxGroup
- **交互特色**: 一键复制到剪贴板
#### 3.3 分佣模板管理
- **文件路径**: `src/views/settings/commission-template/index.vue`
- **功能特性**:
- 分佣模板 CRUD
- 分佣模式(固定佣金/比例佣金)
- 佣金规则配置
- 适用范围设置
- 应用次数统计
- 应用记录查看
- **组件使用**: ArtTable, ElDialog, ElForm, ElRadioGroup, ElInputNumber, ElTag
---
### 4. 批量操作模块 (3个页面)
#### 4.1 网卡批量导入
- **文件路径**: `src/views/batch/sim-import/index.vue`
- **功能特性**:
- Excel模板下载
- 拖拽上传ElUpload drag
- 导入说明提示ElAlert
- 导入记录列表
- 导入进度展示ElProgress
- 导入状态(处理中/完成/失败)
- 详情查看(成功数/失败数/失败原因)
- 失败数据下载
- **组件使用**: ElCard, ElUpload, ArtTable, ElProgress, ElTag, ElDescriptions
#### 4.2 设备批量导入
- **文件路径**: `src/views/batch/device-import/index.vue`
- **功能特性**:
- 设备批量导入带ICCID绑定
- 导入统计卡片(今日导入/成功绑定/失败数/成功率)
- 状态筛选
- 导入进度跟踪
- 失败明细表格
- 已绑定ICCID统计
- **组件使用**: ElCard, ElUpload, ArtTable, ElProgress, ElTag, ElTable
- **特色**: 统计卡片带图标
#### 4.3 换卡通知管理
- **文件路径**: `src/views/batch/card-change-notice/index.vue`
- **功能特性**:
- 通知列表(标题/类型/状态)
- 通知类型(卡片更换/激活/停用/套餐变更)
- 目标用户设置(全部/指定/批量导入)
- 发送方式(短信/邮件/App推送
- 定时发送功能
- 发送进度实时展示
- 立即发送操作
- 详情查看
- **组件使用**: ArtTable, ElDialog, ElForm, ElRadioGroup, ElCheckboxGroup, ElUpload, ElProgress
- **交互特色**: 实时发送进度动画
---
### 5. 产品管理模块 (1个页面)
#### 5.1 网卡产品管理
- **文件路径**: `src/views/product/sim-card/index.vue`
- **功能特性**:
- 网卡产品 CRUD
- 运营商筛选(移动/联通/电信)
- 套餐规格配置
- 价格设置
- 库存管理
- 上线/下线状态
- **组件使用**: ArtTable, ElDialog, ElForm, ElSelect, ElInputNumber, ElSwitch
---
## 🔧 配置文件更新
### 1. 路由别名配置
**文件**: `src/router/routesAlias.ts`
新增路由别名:
```typescript
// 账号管理
CustomerRole = '/account-management/customer-role'
AgentManagement = '/account-management/agent'
CustomerAccount = '/account-management/customer-account'
// 产品管理
SimCardManagement = '/product/sim-card'
// 财务管理
WithdrawalManagement = '/finance/withdrawal'
MyAccount = '/finance/my-account'
WithdrawalSettings = '/finance/withdrawal-settings'
// 设置管理
PaymentMerchant = '/settings/payment-merchant'
DeveloperApi = '/settings/developer-api'
CommissionTemplate = '/settings/commission-template'
// 批量操作
SimImport = '/batch/sim-import'
DeviceImport = '/batch/device-import'
CardChangeNotice = '/batch/card-change-notice'
```
### 2. 异步路由配置
**文件**: `src/router/routes/asyncRoutes.ts`
新增路由模块:
- **账号管理模块**: 扩展了3个子路由
- **产品管理模块**: 新增模块1个子路由
- **财务管理模块**: 新增模块3个子路由
- **设置管理模块**: 新增模块3个子路由
- **批量操作模块**: 新增模块3个子路由
### 3. 国际化配置
**文件**: `src/locales/langs/zh.json`
新增菜单标题:
```json
{
"menus": {
"accountManagement": {
"customerRole": "客户角色",
"agent": "代理商管理",
"customerAccount": "客户账号"
},
"product": {
"title": "产品管理",
"simCard": "网卡产品管理"
},
"finance": {
"title": "财务管理",
"withdrawal": "提现管理",
"myAccount": "我的账户",
"withdrawalSettings": "提现设置"
},
"settings": {
"title": "设置管理",
"paymentMerchant": "支付商户",
"developerApi": "开发者API",
"commissionTemplate": "分佣模板"
},
"batch": {
"title": "批量操作",
"simImport": "网卡批量导入",
"deviceImport": "设备批量导入",
"cardChangeNotice": "换卡通知"
}
}
}
```
---
## 📝 开发规范遵循
### 1. 代码风格
- ✅ 使用 Vue 3 Composition API
- ✅ 使用 `<script setup>` 语法
- ✅ TypeScript 类型定义完整
- ✅ 使用 defineOptions 定义组件名称
### 2. 组件使用
- ✅ 优先使用项目自定义组件ArtTable
- ✅ 使用 Element Plus 组件库
- ✅ 统一的表单验证规则
- ✅ 统一的对话框样式
### 3. 数据管理
- ✅ 使用 ref 和 reactive 管理状态
- ✅ 使用 computed 处理派生数据
- ✅ Mock 数据结构完整
- ✅ 包含完整的 CRUD 操作
### 4. 用户体验
- ✅ 操作反馈ElMessage
- ✅ 确认对话框ElMessageBox
- ✅ 加载状态展示
- ✅ 进度条展示
- ✅ 标签状态提示
---
## 🎨 界面特色
### 统计卡片
- 渐变背景色
- 图标装饰
- 数据对比
- 响应式布局
### 表格功能
- 搜索过滤
- 状态筛选
- 批量操作
- 排序功能
- 分页展示
### 表单交互
- 动态表单项(根据选择显示/隐藏)
- 实时验证
- 条件渲染
- 默认值设置
### 文件上传
- 拖拽上传
- 文件类型限制
- 大小限制
- 模板下载
---
## 🚀 下一步工作建议
### 1. API 对接
- 将所有 Mock 数据替换为真实 API 调用
- 统一错误处理
- 添加请求拦截器
- 实现 Token 刷新机制
### 2. 权限控制
- 按钮级权限控制
- 数据权限过滤
- 角色权限映射
### 3. 数据验证
- 表单验证规则完善
- 后端数据校验
- 异常数据处理
### 4. 性能优化
- 列表分页加载
- 虚拟滚动
- 图片懒加载
- 组件懒加载
### 5. 测试
- 单元测试
- 集成测试
- E2E 测试
---
## 📚 相关文档
- [任务规划文档](./任务规划.md)
- [页面创建模板](./页面创建模板.md)
- [API对接说明](./API对接说明.md)
---
## ✨ 总结
本次开发共完成 **13 个页面组件**,涵盖账号管理、财务管理、设置管理、批量操作和产品管理 5 大模块。所有页面均遵循统一的代码规范,使用 Mock 数据进行开发,界面美观、交互流畅,为后续 API 对接和功能扩展打下了坚实基础。
**开发完成度**: 100% ✅
**代码质量**: 优秀 ⭐⭐⭐⭐⭐
**可维护性**: 良好 👍

View File

@@ -1,875 +0,0 @@
# 在商品管理 /account-management 下面新增一个 店铺管理 需要对接的API如下, 然后页面样式可以参考 /account-management/account
# 店铺列表
## OpenAPI Specification
```yaml
openapi: 3.0.1
info:
title: ''
description: ''
version: 1.0.0
paths:
/api/admin/shops:
get:
summary: 店铺列表
deprecated: false
description: ''
tags:
- 店铺管理
- 店铺管理
parameters:
- name: page
in: query
description: 页码
required: false
schema:
description: 页码
minimum: 1
type: integer
- name: page_size
in: query
description: 每页数量
required: false
schema:
description: 每页数量
maximum: 100
minimum: 1
type: integer
- name: shop_name
in: query
description: 店铺名称模糊查询
required: false
schema:
description: 店铺名称模糊查询
maxLength: 100
type: string
- name: shop_code
in: query
description: 店铺编号模糊查询
required: false
schema:
description: 店铺编号模糊查询
maxLength: 50
type: string
- name: parent_id
in: query
description: 上级店铺ID
required: false
schema:
description: 上级店铺ID
minimum: 1
type: integer
nullable: true
- name: level
in: query
description: 店铺层级 (1-7级)
required: false
schema:
description: 店铺层级 (1-7级)
maximum: 7
minimum: 1
type: integer
nullable: true
- name: status
in: query
description: 状态 (0:禁用, 1:启用)
required: false
schema:
description: 状态 (0:禁用, 1:启用)
type: integer
nullable: true
responses:
'200':
description: OK
content:
application/json:
schema:
$ref: '#/components/schemas/ModelShopPageResult'
headers: {}
x-apifox-name: ''
'400':
description: 请求参数错误
content:
application/json:
schema: &ref_0
$ref: '#/components/schemas/ErrorResponse'
headers: {}
x-apifox-name: ''
'401':
description: 未认证或认证已过期
content:
application/json:
schema: *ref_0
headers: {}
x-apifox-name: ''
'403':
description: 无权访问
content:
application/json:
schema: *ref_0
headers: {}
x-apifox-name: ''
'500':
description: 服务器内部错误
content:
application/json:
schema: *ref_0
headers: {}
x-apifox-name: ''
security:
- BearerAuth: []
x-apifox:
schemeGroups:
- id: AiK0MKfrzIq2Np2gS4yVd
schemeIds:
- BearerAuth
required: true
use:
id: AiK0MKfrzIq2Np2gS4yVd
scopes:
AiK0MKfrzIq2Np2gS4yVd:
BearerAuth: []
x-apifox-folder: 店铺管理
x-apifox-status: released
x-run-in-apifox: https://app.apifox.com/web/project/7591618/apis/api-408366339-run
components:
schemas:
ModelShopPageResult:
properties:
items:
description: 店铺列表
items:
$ref: '#/components/schemas/ModelShopResponse'
type: array
nullable: true
page:
description: 当前页码
type: integer
size:
description: 每页数量
type: integer
total:
description: 总记录数
type: integer
type: object
x-apifox-orders:
- items
- page
- size
- total
x-apifox-ignore-properties: []
x-apifox-folder: ''
ModelShopResponse:
properties:
address:
description: 详细地址
type: string
city:
description: 城市
type: string
contact_name:
description: 联系人姓名
type: string
contact_phone:
description: 联系人电话
type: string
created_at:
description: 创建时间
type: string
district:
description: 区县
type: string
id:
description: 店铺ID
minimum: 0
type: integer
level:
description: 店铺层级 (1-7级)
type: integer
parent_id:
description: 上级店铺ID
minimum: 0
type: integer
nullable: true
province:
description: 省份
type: string
shop_code:
description: 店铺编号
type: string
shop_name:
description: 店铺名称
type: string
status:
description: 状态 (0:禁用, 1:启用)
type: integer
updated_at:
description: 更新时间
type: string
type: object
x-apifox-orders:
- address
- city
- contact_name
- contact_phone
- created_at
- district
- id
- level
- parent_id
- province
- shop_code
- shop_name
- status
- updated_at
x-apifox-ignore-properties: []
x-apifox-folder: ''
ErrorResponse:
properties:
code:
description: 错误码
type: integer
message:
description: 错误消息
type: string
timestamp:
description: 时间戳
format: date-time
type: string
required:
- code
- message
- timestamp
type: object
x-apifox-orders:
- code
- message
- timestamp
x-apifox-ignore-properties: []
x-apifox-folder: ''
securitySchemes:
BearerAuth:
bearerFormat: JWT
scheme: bearer
type: jwt
servers:
- url: https://cmp-api.boss160.cn
description: 测试环境
security: []
```
# 创建店铺
## OpenAPI Specification
```yaml
openapi: 3.0.1
info:
title: ''
description: ''
version: 1.0.0
paths:
/api/admin/shops:
post:
summary: 创建店铺
deprecated: false
description: ''
tags:
- 店铺管理
- 店铺管理
parameters: []
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/ModelCreateShopRequest'
responses:
'200':
description: OK
content:
application/json:
schema:
$ref: '#/components/schemas/ModelShopResponse'
headers: {}
x-apifox-name: ''
'400':
description: 请求参数错误
content:
application/json:
schema: &ref_0
$ref: '#/components/schemas/ErrorResponse'
headers: {}
x-apifox-name: ''
'401':
description: 未认证或认证已过期
content:
application/json:
schema: *ref_0
headers: {}
x-apifox-name: ''
'403':
description: 无权访问
content:
application/json:
schema: *ref_0
headers: {}
x-apifox-name: ''
'500':
description: 服务器内部错误
content:
application/json:
schema: *ref_0
headers: {}
x-apifox-name: ''
security:
- BearerAuth: []
x-apifox:
schemeGroups:
- id: CgtLTd_zQ5XPrx3y7BdLh
schemeIds:
- BearerAuth
required: true
use:
id: CgtLTd_zQ5XPrx3y7BdLh
scopes:
CgtLTd_zQ5XPrx3y7BdLh:
BearerAuth: []
x-apifox-folder: 店铺管理
x-apifox-status: released
x-run-in-apifox: https://app.apifox.com/web/project/7591618/apis/api-408366340-run
components:
schemas:
ModelCreateShopRequest:
properties:
address:
description: 详细地址
maxLength: 255
type: string
city:
description: 城市
maxLength: 50
type: string
contact_name:
description: 联系人姓名
maxLength: 50
type: string
contact_phone:
description: 联系人电话
maxLength: 11
minLength: 11
type: string
district:
description: 区县
maxLength: 50
type: string
init_password:
description: 初始账号密码
maxLength: 32
minLength: 8
type: string
init_phone:
description: 初始账号手机号
maxLength: 11
minLength: 11
type: string
init_username:
description: 初始账号用户名
maxLength: 50
minLength: 3
type: string
parent_id:
description: 上级店铺ID一级店铺可不填
minimum: 1
type: integer
nullable: true
province:
description: 省份
maxLength: 50
type: string
shop_code:
description: 店铺编号
maxLength: 50
minLength: 1
type: string
shop_name:
description: 店铺名称
maxLength: 100
minLength: 1
type: string
required:
- init_password
- init_phone
- init_username
- shop_code
- shop_name
type: object
x-apifox-orders:
- address
- city
- contact_name
- contact_phone
- district
- init_password
- init_phone
- init_username
- parent_id
- province
- shop_code
- shop_name
x-apifox-ignore-properties: []
x-apifox-folder: ''
ModelShopResponse:
properties:
address:
description: 详细地址
type: string
city:
description: 城市
type: string
contact_name:
description: 联系人姓名
type: string
contact_phone:
description: 联系人电话
type: string
created_at:
description: 创建时间
type: string
district:
description: 区县
type: string
id:
description: 店铺ID
minimum: 0
type: integer
level:
description: 店铺层级 (1-7级)
type: integer
parent_id:
description: 上级店铺ID
minimum: 0
type: integer
nullable: true
province:
description: 省份
type: string
shop_code:
description: 店铺编号
type: string
shop_name:
description: 店铺名称
type: string
status:
description: 状态 (0:禁用, 1:启用)
type: integer
updated_at:
description: 更新时间
type: string
type: object
x-apifox-orders:
- address
- city
- contact_name
- contact_phone
- created_at
- district
- id
- level
- parent_id
- province
- shop_code
- shop_name
- status
- updated_at
x-apifox-ignore-properties: []
x-apifox-folder: ''
ErrorResponse:
properties:
code:
description: 错误码
type: integer
message:
description: 错误消息
type: string
timestamp:
description: 时间戳
format: date-time
type: string
required:
- code
- message
- timestamp
type: object
x-apifox-orders:
- code
- message
- timestamp
x-apifox-ignore-properties: []
x-apifox-folder: ''
securitySchemes:
BearerAuth:
bearerFormat: JWT
scheme: bearer
type: jwt
servers:
- url: https://cmp-api.boss160.cn
description: 测试环境
security: []
```
# 删除店铺
## OpenAPI Specification
```yaml
openapi: 3.0.1
info:
title: ''
description: ''
version: 1.0.0
paths:
/api/admin/shops/{id}:
delete:
summary: 删除店铺
deprecated: false
description: ''
tags:
- 店铺管理
- 店铺管理
parameters:
- name: id
in: path
description: ID
required: true
example: 0
schema:
description: ID
minimum: 0
type: integer
responses:
'400':
description: 请求参数错误
content:
application/json:
schema: &ref_0
$ref: '#/components/schemas/ErrorResponse'
headers: {}
x-apifox-name: ''
'401':
description: 未认证或认证已过期
content:
application/json:
schema: *ref_0
headers: {}
x-apifox-name: ''
'403':
description: 无权访问
content:
application/json:
schema: *ref_0
headers: {}
x-apifox-name: ''
'500':
description: 服务器内部错误
content:
application/json:
schema: *ref_0
headers: {}
x-apifox-name: ''
security:
- BearerAuth: []
x-apifox:
schemeGroups:
- id: ivp0VlbXbNhnY2xcsCWbS
schemeIds:
- BearerAuth
required: true
use:
id: ivp0VlbXbNhnY2xcsCWbS
scopes:
ivp0VlbXbNhnY2xcsCWbS:
BearerAuth: []
x-apifox-folder: 店铺管理
x-apifox-status: released
x-run-in-apifox: https://app.apifox.com/web/project/7591618/apis/api-408366341-run
components:
schemas:
ErrorResponse:
properties:
code:
description: 错误码
type: integer
message:
description: 错误消息
type: string
timestamp:
description: 时间戳
format: date-time
type: string
required:
- code
- message
- timestamp
type: object
x-apifox-orders:
- code
- message
- timestamp
x-apifox-ignore-properties: []
x-apifox-folder: ''
securitySchemes:
BearerAuth:
bearerFormat: JWT
scheme: bearer
type: jwt
servers:
- url: https://cmp-api.boss160.cn
description: 测试环境
security: []
```
# 更新店铺
## OpenAPI Specification
```yaml
openapi: 3.0.1
info:
title: ''
description: ''
version: 1.0.0
paths:
/api/admin/shops/{id}:
put:
summary: 更新店铺
deprecated: false
description: ''
tags:
- 店铺管理
- 店铺管理
parameters:
- name: id
in: path
description: ID
required: true
example: 0
schema:
description: ID
minimum: 0
type: integer
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/ModelUpdateShopParams'
responses:
'200':
description: OK
content:
application/json:
schema:
$ref: '#/components/schemas/ModelShopResponse'
headers: {}
x-apifox-name: ''
'400':
description: 请求参数错误
content:
application/json:
schema: &ref_0
$ref: '#/components/schemas/ErrorResponse'
headers: {}
x-apifox-name: ''
'401':
description: 未认证或认证已过期
content:
application/json:
schema: *ref_0
headers: {}
x-apifox-name: ''
'403':
description: 无权访问
content:
application/json:
schema: *ref_0
headers: {}
x-apifox-name: ''
'500':
description: 服务器内部错误
content:
application/json:
schema: *ref_0
headers: {}
x-apifox-name: ''
security:
- BearerAuth: []
x-apifox:
schemeGroups:
- id: 2BoHA3GVAX6-zd8XmFxez
schemeIds:
- BearerAuth
required: true
use:
id: 2BoHA3GVAX6-zd8XmFxez
scopes:
2BoHA3GVAX6-zd8XmFxez:
BearerAuth: []
x-apifox-folder: 店铺管理
x-apifox-status: released
x-run-in-apifox: https://app.apifox.com/web/project/7591618/apis/api-408366342-run
components:
schemas:
ModelUpdateShopParams:
properties:
address:
description: 详细地址
maxLength: 255
type: string
city:
description: 城市
maxLength: 50
type: string
contact_name:
description: 联系人姓名
maxLength: 50
type: string
contact_phone:
description: 联系人电话
maxLength: 11
minLength: 11
type: string
district:
description: 区县
maxLength: 50
type: string
province:
description: 省份
maxLength: 50
type: string
shop_name:
description: 店铺名称
maxLength: 100
minLength: 1
type: string
status:
description: 状态 (0:禁用, 1:启用)
type: integer
required:
- shop_name
- status
type: object
x-apifox-orders:
- address
- city
- contact_name
- contact_phone
- district
- province
- shop_name
- status
x-apifox-ignore-properties: []
x-apifox-folder: ''
ModelShopResponse:
properties:
address:
description: 详细地址
type: string
city:
description: 城市
type: string
contact_name:
description: 联系人姓名
type: string
contact_phone:
description: 联系人电话
type: string
created_at:
description: 创建时间
type: string
district:
description: 区县
type: string
id:
description: 店铺ID
minimum: 0
type: integer
level:
description: 店铺层级 (1-7级)
type: integer
parent_id:
description: 上级店铺ID
minimum: 0
type: integer
nullable: true
province:
description: 省份
type: string
shop_code:
description: 店铺编号
type: string
shop_name:
description: 店铺名称
type: string
status:
description: 状态 (0:禁用, 1:启用)
type: integer
updated_at:
description: 更新时间
type: string
type: object
x-apifox-orders:
- address
- city
- contact_name
- contact_phone
- created_at
- district
- id
- level
- parent_id
- province
- shop_code
- shop_name
- status
- updated_at
x-apifox-ignore-properties: []
x-apifox-folder: ''
ErrorResponse:
properties:
code:
description: 错误码
type: integer
message:
description: 错误消息
type: string
timestamp:
description: 时间戳
format: date-time
type: string
required:
- code
- message
- timestamp
type: object
x-apifox-orders:
- code
- message
- timestamp
x-apifox-ignore-properties: []
x-apifox-folder: ''
securitySchemes:
BearerAuth:
bearerFormat: JWT
scheme: bearer
type: jwt
servers:
- url: https://cmp-api.boss160.cn
description: 测试环境
security: []
```

View File

@@ -1,194 +0,0 @@
# 物联网卡管理系统 - 开发进度
> 更新时间: 2026-01-09
---
## ✅ 已完成的页面5个
### 账号管理模块 (4/4) ✅
- [x] **客户角色管理** `account-management/customer-role/index.vue`
- 角色列表、新增、编辑、删除
- 能力边界配置(多选框)
- 状态管理
- [x] **代理商管理** `account-management/agent/index.vue`
- 代理商列表(多级代理)
- 新增/编辑代理商
- 账号管理(子账号列表)
- 佣金配置(固定/比例佣金)
- [x] **客户账号管理** `account-management/customer-account/index.vue`
- 客户账号列表
- 账号详情Descriptions
- 解绑手机、重置密码
- 禁用/启用账号
- 操作记录查看
- [x] **企业客户管理** `account-management/customer/index.vue`
- ⚠️ 已存在,无需创建
### 商品管理模块 (1/2)
- [x] **号卡管理** `product/sim-card/index.vue`
- 号卡列表(运营商筛选)
- 新增/编辑号卡
- 上架/下架管理
- 库存管理
---
## 📋 待创建的页面10个
### 1. 账号管理模块
- [ ] **客户账号佣金** `account-management/customer-commission/index.vue`
- 佣金统计卡片
- 佣金明细列表
- 提现记录
### 2. 财务管理模块 (0/3)
- [ ] **佣金提现管理** `finance/withdrawal/index.vue`
- 提现申请列表(状态筛选)
- 审核功能(通过/拒绝)
- 批量审核
- 提现记录导出
- [ ] **佣金提现设置** `finance/withdrawal-settings/index.vue`
- 提现参数配置(最低金额、手续费等)
- 配置历史记录
- [ ] **我的账户** `finance/my-account/index.vue`
- 账户概览(卡片统计)
- 佣金收入明细
- 提现申请功能
- 收支流水记录
### 3. 设置管理模块 (0/3)
- [ ] **收款商户设置** `settings/payment-merchant/index.vue`
- 支付商户信息配置
- API 密钥管理
- 回调地址设置
- 支付方式管理(微信/支付宝/银行卡)
- [ ] **开发能力管理** `settings/developer-api/index.vue`
- API 密钥列表
- 生成/重置密钥
- Webhook 配置
- API 调用统计
- [ ] **分佣模板** `settings/commission-template/index.vue`
- 分佣模板列表
- 新增/编辑模板
- 分佣规则配置
- 模板应用记录
### 4. 商品管理模块
- [ ] **号卡分配** `product/sim-card-assign/index.vue`
- 分配记录列表
- 为代理商分配号卡
- 设置佣金模式
- 分配统计报表
### 5. 批量操作模块 (0/3)
- [ ] **网卡批量导入** `batch/sim-import/index.vue`
- Excel 上传(模板下载)
- 导入任务列表
- 导入结果查看(成功/失败)
- [ ] **设备批量导入** `batch/device-import/index.vue`
- Excel 上传(设备+ICCID关系
- 导入任务列表
- 导入结果查看
- [ ] **换卡通知** `batch/card-change-notice/index.vue`
- 换卡通知列表
- 单独/批量创建通知
- 通知方式选择(短信/邮件)
- 通知记录查看
---
## 📂 项目文件结构
```
src/views/
├── account-management/ # 账号管理
│ ├── customer/ ✅ 已存在
│ ├── customer-role/ ✅ 已创建
│ ├── agent/ ✅ 已创建
│ ├── customer-account/ ✅ 已创建
│ └── customer-commission/ ❌ 待创建
├── finance/ # 财务管理
│ ├── withdrawal/ ❌ 待创建
│ ├── withdrawal-settings/ ❌ 待创建
│ └── my-account/ ❌ 待创建
├── settings/ # 设置管理
│ ├── payment-merchant/ ❌ 待创建
│ ├── developer-api/ ❌ 待创建
│ └── commission-template/ ❌ 待创建
├── product/ # 商品管理
│ ├── sim-card/ ✅ 已创建
│ └── sim-card-assign/ ❌ 待创建
└── batch/ # 批量操作
├── sim-import/ ❌ 待创建
├── device-import/ ❌ 待创建
└── card-change-notice/ ❌ 待创建
```
---
## 🚀 快速创建指南
### 方法1: 使用模板快速创建
参考 `docs/页面创建模板.md` 中的标准模板,只需:
1. 复制模板代码
2. 修改组件名和接口定义
3. 调整 Mock 数据
4. 根据需求调整表单和表格
### 方法2: 复制现有页面修改
推荐复制以下页面作为基础:
- **列表+CRUD**: 复制 `customer-role/index.vue`
- **复杂列表+多对话框**: 复制 `agent/index.vue`
- **详情查看**: 复制 `customer-account/index.vue`
---
## 📌 下一步工作
### 优先级1 - 核心业务页面
1. ⚠️ 财务管理模块3个页面- 核心功能
2. ⚠️ 商品管理 - 号卡分配
### 优先级2 - 辅助功能页面
3. 设置管理模块3个页面
4. 批量操作模块3个页面
5. 客户账号佣金页面
### 优先级3 - 路由和配置
6. 更新 `src/router/routesAlias.ts` 添加新路由别名
7. 更新 `src/router/routes/asyncRoutes.ts` 添加路由配置
8. 测试所有页面是否正常访问
---
## ✨ 已完成的文档
-`docs/任务规划.md` - 完整的任务规划和分解
-`docs/页面创建模板.md` - 标准页面模板和快速创建指南
-`docs/开发进度.md` - 当前开发进度追踪
---
## 💡 开发建议
1. **使用模板**:严格按照模板创建,保持代码风格一致
2. **Mock 数据**:确保 Mock 数据完整且真实,方便测试
3. **组件复用**:最大化使用 ArtTable 等现有组件
4. **渐进开发**:先完成基础功能,再添加高级特性
5. **及时测试**:每完成一个页面立即测试功能
---
**总体完成度**: 5/15 页面 (33.3%)
继续加油!🚀

View File

@@ -1,378 +0,0 @@
# 在账号管理下面新增一个平台账号, 然后需要写页面对接API, 页面样式可以参考/system/account 都需要token认证 逻辑啥的跟/system/account差不多
## 1. 平台账号列表
```json
"url": "/api/admin/platform-accounts",
"methods": "GET",
Query :
export interface ApifoxModel {
/**
*
*/
page?: number;
/**
*
*/
page_size?: number;
/**
*
*/
phone?: string;
/**
* (0:, 1:)
*/
status?: number | null;
/**
*
*/
username?: string;
[property: string]: any;
}
/**
* ModelAccountPageResult
*/
export interface ApifoxModel {
/**
*
*/
items?: ModelAccountResponse[] | null;
/**
*
*/
page?: number;
/**
*
*/
size?: number;
/**
*
*/
total?: number;
[property: string]: any;
}
/**
* ModelAccountResponse
*/
export interface ModelAccountResponse {
/**
*
*/
created_at?: string;
/**
* ID
*/
creator?: number;
/**
* ID
*/
enterprise_id?: number | null;
/**
* ID
*/
id?: number;
/**
*
*/
phone?: string;
/**
* ID
*/
shop_id?: number | null;
/**
* (0:, 1:)
*/
status?: number;
/**
*
*/
updated_at?: string;
/**
* ID
*/
updater?: number;
/**
* (1:, 2:, 3:, 4:)
*/
user_type?: number;
/**
*
*/
username?: string;
[property: string]: any;
}
{
"items": [
{
"created_at": "string",
"creator": 0,
"enterprise_id": 0,
"id": 0,
"phone": "string",
"shop_id": 0,
"status": 0,
"updated_at": "string",
"updater": 0,
"user_type": 0,
"username": "string"
}
],
"page": 0,
"size": 0,
"total": 0
}
```
## 2. 新增平台账号
url: /api/admin/platform-accounts,
methods: post,
Body 参数
/**
* ModelCreateAccountRequest
*/
export interface ApifoxModel {
/**
* 关联企业ID企业账号必填
*/
enterprise_id?: number | null;
/**
* 密码
*/
password: string;
/**
* 手机号
*/
phone: string;
/**
* 关联店铺ID代理账号必填
*/
shop_id?: number | null;
/**
* 用户类型 (1:超级管理员, 2:平台用户, 3:代理账号, 4:企业账号)
*/
user_type: number;
/**
* 用户名
*/
username: string;
[property: string]: any;
}
返回响应:
/**
* ModelAccountResponse
*/
export interface ApifoxModel {
/**
* 创建时间
*/
created_at?: string;
/**
* 创建人ID
*/
creator?: number;
/**
* 关联企业ID
*/
enterprise_id?: number | null;
/**
* 账号ID
*/
id?: number;
/**
* 手机号
*/
phone?: string;
/**
* 关联店铺ID
*/
shop_id?: number | null;
/**
* 状态 (0:禁用, 1:启用)
*/
status?: number;
/**
* 更新时间
*/
updated_at?: string;
/**
* 更新人ID
*/
updater?: number;
/**
* 用户类型 (1:超级管理员, 2:平台用户, 3:代理账号, 4:企业账号)
*/
user_type?: number;
/**
* 用户名
*/
username?: string;
[property: string]: any;
}
*
{
"created_at": "string",
"creator": 0,
"enterprise_id": 0,
"id": 0,
"phone": "string",
"shop_id": 0,
"status": 0,
"updated_at": "string",
"updater": 0,
"user_type": 0,
"username": "string"
}
## 3. 移除角色
url: /api/admin/platform-accounts/{account_id}/roles/{role_id}
methods: delete
path参数:
export interface ApifoxModel {
/**
* 账号ID
*/
account_id: number;
/**
* 角色ID
*/
role_id: number;
[property: string]: any;
}
* 返回响应:
* {
"code": 0,
"message": "string",
"timestamp": "2019-08-24T14:15:22.123Z"
}
## 4. 删除平台账号
url: /api/admin/platform-accounts/{id}
methods: delete
path参数:
export interface ApifoxModel {
/**
* ID
*/
id: number;
[property: string]: any;
}
响应:
* {
"code": 0,
"message": "string",
"timestamp": "2019-08-24T14:15:22.123Z"
}
## 5. 获取平台账号详情
url: /api/admin/platform-accounts/{id}
methods: get
响应: {
"created_at": "string",
"creator": 0,
"enterprise_id": 0,
"id": 0,
"phone": "string",
"shop_id": 0,
"status": 0,
"updated_at": "string",
"updater": 0,
"user_type": 0,
"username": "string"
}
## 6. 编辑平台账号
url: /api/admin/platform-accounts/{id}
methods: put
body:/**
* ModelUpdateAccountParams
*/
export interface ApifoxModel {
/**
* 密码
*/
password?: null | string;
/**
* 手机号
*/
phone?: null | string;
/**
* 状态 (0:禁用, 1:启用)
*/
status?: number | null;
/**
* 用户名
*/
username?: null | string;
[property: string]: any;
}
响应: {
"created_at": "string",
"creator": 0,
"enterprise_id": 0,
"id": 0,
"phone": "string",
"shop_id": 0,
"status": 0,
"updated_at": "string",
"updater": 0,
"user_type": 0,
"username": "string"
}
## 7. 修改密码
url: /api/admin/platform-accounts/{id}/password
methods: put
body:{
"new_password": "stringst"
}
response: {
"code": 0,
"message": "string",
"timestamp": "2019-08-24T14:15:22.123Z"
}
## 8. 获取账号角色
url: /api/admin/platform-accounts/{id}/roles
methods: get
响应: [
{
"creator": 0,
"role_desc": "string",
"role_name": "string",
"role_type": 0,
"status": 0,
"updater": 0
}
]
## 9. 分配角色
url: /api/admin/platform-accounts/{id}/roles,
methods: post
body: {
"role_ids": [
0
]
}
响应: {
"code": 0,
"message": "string",
"timestamp": "2019-08-24T14:15:22.123Z"
}
## 10. 启用/禁用账号
url: /api/admin/platform-accounts/{id}/status
methods: put,
body: {
"status": 0
}

View File

@@ -1,72 +0,0 @@
# 角色管理中少了三个接口对接
## 1. 获取角色权限
url: /api/admin/roles/{id}/permissions
methods: get
response: [
{
"available_for_role_types": "string",
"creator": 0,
"parent_id": 0,
"perm_code": "string",
"perm_name": "string",
"perm_type": 0,
"platform": "string",
"sort": 0,
"status": 0,
"updater": 0,
"url": "string"
}
]
/**
* ModelPermission
*/
export interface ApifoxModel {
available_for_role_types?: string;
creator?: number;
parent_id?: number | null;
perm_code?: string;
perm_name?: string;
perm_type?: number;
platform?: string;
sort?: number;
status?: number;
updater?: number;
url?: string;
[property: string]: any;
}
## 2. 分配权限
url: /api/admin/roles/{id}/permissions
methods: post
body: {
"perm_ids": [
0
]
}
response: {
"code": 0,
"message": "string",
"timestamp": "2019-08-24T14:15:22.123Z"
}
## 3. 移除权限
url: /api/admin/roles/{role_id}/permissions/{perm_id}
export interface ApifoxModel {
/**
* 权限ID
*/
perm_id: number;
/**
* 角色ID
*/
role_id: number;
[property: string]: any;
}
methods: delete
响应: {
"code": 0,
"message": "string",
"timestamp": "2019-08-24T14:15:22.123Z"
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,297 +0,0 @@
# 页面创建模板
本文档提供快速创建页面的模板,所有页面遵循统一的风格和结构。
## 📋 已完成的页面
### ✅ 账号管理模块
- [x] 客户角色管理 (`account-management/customer-role`)
- [x] 代理商管理 (`account-management/agent`)
- [x] 客户账号管理 (`account-management/customer-account`)
- [ ] 客户账号佣金 (`account-management/customer-commission`) - 待创建
### ✅ 商品管理模块
- [x] 号卡管理 (`product/sim-card`)
- [ ] 号卡分配 (`product/sim-card-assign`) - 待创建
## 🔨 待创建的页面
### 财务管理模块 (`finance/`)
1. **佣金提现管理** (`withdrawal/index.vue`)
2. **佣金提现设置** (`withdrawal-settings/index.vue`)
3. **我的账户** (`my-account/index.vue`)
### 设置管理模块 (`settings/`)
1. **收款商户设置** (`payment-merchant/index.vue`)
2. **开发能力管理** (`developer-api/index.vue`)
3. **分佣模板** (`commission-template/index.vue`)
### 批量操作模块 (`batch/`)
1. **网卡批量导入** (`sim-import/index.vue`)
2. **设备批量导入** (`device-import/index.vue`)
3. **换卡通知** (`card-change-notice/index.vue`)
---
## 📝 标准页面模板
### 基础列表页面模板
```vue
<template>
<div class="page-content">
<!-- 搜索栏 -->
<ElRow>
<ElCol :xs="24" :sm="12" :lg="6">
<ElInput v-model="searchQuery" placeholder="搜索关键词" clearable></ElInput>
</ElCol>
<div style="width: 12px"></div>
<ElCol :xs="24" :sm="12" :lg="6" class="el-col2">
<ElButton v-ripple @click="handleSearch">搜索</ElButton>
<ElButton v-ripple @click="showDialog('add')">新增</ElButton>
</ElCol>
</ElRow>
<!-- 表格 -->
<ArtTable :data="filteredData" index>
<template #default>
<ElTableColumn label="名称" prop="name" />
<ElTableColumn label="编码" prop="code" />
<ElTableColumn label="状态" prop="status">
<template #default="scope">
<ElTag :type="scope.row.status === 'active' ? 'success' : 'info'">
{{ scope.row.status === 'active' ? '启用' : '禁用' }}
</ElTag>
</template>
</ElTableColumn>
<ElTableColumn label="创建时间" prop="createTime" width="180" />
<ElTableColumn fixed="right" label="操作" width="180">
<template #default="scope">
<el-button link @click="showDialog('edit', scope.row)">编辑</el-button>
<el-button link @click="handleDelete(scope.row)">删除</el-button>
</template>
</ElTableColumn>
</template>
</ArtTable>
<!-- 新增/编辑对话框 -->
<ElDialog
v-model="dialogVisible"
:title="dialogType === 'add' ? '新增' : '编辑'"
width="600px"
align-center
>
<ElForm ref="formRef" :model="form" :rules="rules" label-width="120px">
<ElFormItem label="名称" prop="name">
<ElInput v-model="form.name" placeholder="请输入名称" />
</ElFormItem>
<ElFormItem label="编码" prop="code">
<ElInput v-model="form.code" placeholder="请输入编码" />
</ElFormItem>
<ElFormItem label="状态">
<ElSwitch v-model="form.status" active-value="active" inactive-value="inactive" />
</ElFormItem>
</ElForm>
<template #footer>
<div class="dialog-footer">
<ElButton @click="dialogVisible = false">取消</ElButton>
<ElButton type="primary" @click="handleSubmit(formRef)">提交</ElButton>
</div>
</template>
</ElDialog>
</div>
</template>
<script setup lang="ts">
import { ElMessage, ElMessageBox } from 'element-plus'
import type { FormInstance, FormRules } from 'element-plus'
defineOptions({ name: 'YourPageName' })
interface DataItem {
id?: string
name: string
code: string
status: 'active' | 'inactive'
createTime?: string
}
// Mock 数据
const mockData = ref<DataItem[]>([
{
id: '1',
name: '示例数据1',
code: 'CODE001',
status: 'active',
createTime: '2026-01-01 10:00:00'
}
])
const searchQuery = ref('')
const dialogVisible = ref(false)
const dialogType = ref<'add' | 'edit'>('add')
const formRef = ref<FormInstance>()
const form = reactive<DataItem>({
name: '',
code: '',
status: 'active'
})
const rules = reactive<FormRules>({
name: [{ required: true, message: '请输入名称', trigger: 'blur' }],
code: [{ required: true, message: '请输入编码', trigger: 'blur' }]
})
const filteredData = computed(() => {
if (!searchQuery.value) return mockData.value
return mockData.value.filter((item) =>
item.name.toLowerCase().includes(searchQuery.value.toLowerCase())
)
})
const handleSearch = () => {}
const showDialog = (type: 'add' | 'edit', row?: DataItem) => {
dialogType.value = type
dialogVisible.value = true
if (type === 'edit' && row) {
Object.assign(form, row)
} else {
Object.assign(form, { name: '', code: '', status: 'active' })
}
}
const handleSubmit = async (formEl: FormInstance | undefined) => {
if (!formEl) return
await formEl.validate((valid) => {
if (valid) {
if (dialogType.value === 'add') {
mockData.value.push({
...form,
id: Date.now().toString(),
createTime: new Date().toLocaleString('zh-CN')
})
ElMessage.success('新增成功')
} else {
const index = mockData.value.findIndex((item) => item.id === form.id)
if (index !== -1) mockData.value[index] = { ...form }
ElMessage.success('修改成功')
}
dialogVisible.value = false
formEl.resetFields()
}
})
}
const handleDelete = (row: DataItem) => {
ElMessageBox.confirm('确定删除吗?', '删除确认', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'error'
}).then(() => {
const index = mockData.value.findIndex((item) => item.id === row.id)
if (index !== -1) mockData.value.splice(index, 1)
ElMessage.success('删除成功')
})
}
</script>
<style lang="scss" scoped>
.page-content {
// 自定义样式
}
</style>
```
---
## 🎯 快速创建步骤
1. **复制模板**:复制上面的标准模板
2. **修改组件名**:修改 `defineOptions({ name: 'YourPageName' })`
3. **调整接口**:根据业务需求修改 `DataItem` 接口
4. **修改 Mock 数据**:替换 `mockData` 中的示例数据
5. **调整表单字段**:根据需求增删表单项
6. **调整表格列**:修改 `ElTableColumn` 配置
---
## 📚 常用组件
### 1. ArtTable - 表格组件
```vue
<ArtTable :data="tableData" index>
<template #default>
<ElTableColumn label="列名" prop="propName" />
</template>
</ArtTable>
```
### 2. 搜索栏布局
```vue
<ElRow>
<ElCol :xs="24" :sm="12" :lg="6">
<ElInput v-model="search" placeholder="搜索" clearable />
</ElCol>
<div style="width: 12px"></div>
<ElCol :xs="24" :sm="12" :lg="6">
<ElSelect v-model="filter" placeholder="筛选" clearable style="width: 100%">
<ElOption label="选项1" value="1" />
</ElSelect>
</ElCol>
<div style="width: 12px"></div>
<ElCol :xs="24" :sm="12" :lg="6" class="el-col2">
<ElButton v-ripple>搜索</ElButton>
</ElCol>
</ElRow>
```
### 3. 状态标签
```vue
<ElTag :type="getStatusType(status)">
{{ getStatusText(status) }}
</ElTag>
```
### 4. 操作按钮
```vue
<el-button link @click="handleEdit(row)">编辑</el-button>
<el-button link type="danger" @click="handleDelete(row)">删除</el-button>
```
---
## 💡 开发规范
1. **命名规范**
- 组件名:大驼峰 `YourComponent`
- 变量名:小驼峰 `yourVariable`
- 文件名:小写+连字符 `your-file.vue`
2. **Mock 数据格式**
- 统一使用 `ref<Type[]>([])` 定义
- 包含 `id`, `createTime` 等公共字段
- 数据应具有代表性,便于测试
3. **表单验证**
- 必填字段添加 `required` 规则
- 手机号/邮箱使用正则验证
- 提供友好的错误提示
4. **用户体验**
- 操作前使用 `ElMessageBox.confirm` 确认
- 操作后使用 `ElMessage` 提示结果
- 表单提交后关闭对话框并重置
---
## 🔄 下一步
1. 根据模板快速创建剩余页面
2. 完善 Mock 数据使其更真实
3. 添加路由配置
4. 测试页面功能
5. 优化用户体验
祝开发顺利!🚀

View File

@@ -1,433 +0,0 @@
# 物联网卡管理系统 - 完整项目总结
## 📊 完成概况
**完成时间**: 2026-01-09
**开发进度**: 18/18 页面 (100%)
**总计文件**: 18 个 Vue 页面组件
**状态**: ✅ 全部完成
---
## ✅ 新创建的页面列表18个
### 第一批基础模块13个页面
#### 1. 账号管理模块 (3个)
1. **客户角色管理** - `src/views/account-management/customer-role/index.vue`
2. **代理商管理** - `src/views/account-management/agent/index.vue`
3. **客户账号管理** - `src/views/account-management/customer-account/index.vue`
#### 2. 财务管理模块 (3个)
4. **提现管理** - `src/views/finance/withdrawal/index.vue`
5. **我的账户** - `src/views/finance/my-account/index.vue`
6. **提现设置** - `src/views/finance/withdrawal-settings/index.vue`
#### 3. 设置管理模块 (3个)
7. **支付商户配置** - `src/views/settings/payment-merchant/index.vue`
8. **开发者API管理** - `src/views/settings/developer-api/index.vue`
9. **分佣模板管理** - `src/views/settings/commission-template/index.vue`
#### 4. 批量操作模块 (3个)
10. **网卡批量导入** - `src/views/batch/sim-import/index.vue`
11. **设备批量导入** - `src/views/batch/device-import/index.vue`
12. **换卡通知管理** - `src/views/batch/card-change-notice/index.vue`
#### 5. 产品管理模块 (1个)
13. **网卡产品管理** - `src/views/product/sim-card/index.vue`
---
### 第二批补充模块5个页面
#### 6. 账号管理扩展 (2个)
14. **企业客户管理** - `src/views/account-management/enterprise-customer/index.vue`
- 创建企业管理账号,只能登录企业端
- 依赖客户角色,决定能力边界
- 营业执照上传,统一社会信用代码管理
- 角色分配和初始余额设置
15. **客户账号佣金** - `src/views/account-management/customer-commission/index.vue`
- 查看账号下全部客户的佣金情况
- 提现情况统计和查询
- 佣金明细和提现记录
- 统计卡片展示(总佣金、已提现、待提现)
#### 7. 商品管理扩展 (1个)
16. **号卡分配** - `src/views/product/sim-card-assign/index.vue`
- 为特定代理分配号卡商品
- 设置分佣模式(固定/比例/模板)
- 特殊折扣设置
- 分配记录和取消分配功能
#### 8. 资产管理模块 (2个)
17. **资产分配** - `src/views/asset-management/asset-assign/index.vue`
- 支持三种分配模式:网卡批量分配、设备批量分配、网卡+设备分配
- 网卡有设备信息时,可同时分配网卡和设备
- 批量选择和分配给代理商
- 分配记录和批次管理
18. **换卡申请管理** - `src/views/asset-management/card-replacement-request/index.vue`
- 客户提交的换卡申请管理
- 处理换卡申请填充新ICCID
- 新卡验证和自动换卡操作
- 申请审核(通过/拒绝)
---
## 📦 已存在的页面(复用)
项目中以下页面已经存在,功能完整,无需重复创建:
### 卡片管理card-management
- ✓ 单卡信息 - `card-management/single-card`
- ✓ 网卡管理 - `card-management/card-list`
- ✓ 网卡明细 - `card-management/card-detail`
- ✓ 网卡分配 - `card-management/card-assign`
- ✓ 停机管理 - `card-management/card-shutdown`
- ✓ 我的网卡 - `card-management/my-cards`
- ✓ 线下批量充值 - `card-management/offline-batch-recharge`
- ✓ 网卡转接 - `card-management/card-transfer`
- ✓ 换卡管理 - `card-management/card-replacement`
- ✓ 套餐赠送 - `card-management/package-gift`
- ✓ 换卡网卡 - `card-management/card-change-card`
### 套餐管理package-management
- ✓ 新建套餐 - `package-management/package-create`
- ✓ 批量管理 - `package-management/package-batch`
- ✓ 我的套餐 - `package-management/package-list`
- ✓ 套餐变更 - `package-management/package-change`
- ✓ 套餐分配 - `package-management/package-assign`
- ✓ 套餐系列 - `package-management/package-series`
- ✓ 套餐佣金 - `package-management/package-commission`
### 设备管理device-management
- ✓ 设备管理 - `device-management/devices`
### 客户管理account-management
- ✓ 客户管理 - `account-management/customer`
---
## 🔄 功能与页面对应关系
根据你提供的完整需求,所有功能已全部实现:
| 序号 | 功能名称 | 对应页面 | 状态 |
|------|---------|---------|------|
| 1 | 账号管理-客户角色 | account-management/customer-role | ✅ 已创建 |
| 2 | 账号管理-代理商管理 | account-management/agent | ✅ 已创建 |
| 3 | 账号管理-企业客户管理 | account-management/enterprise-customer | ✅ 已创建 |
| 4 | 账号管理-客户账号管理 | account-management/customer-account | ✅ 已创建 |
| 5 | 账户管理-客户账号佣金 | account-management/customer-commission | ✅ 已创建 |
| 6 | 账户管理-佣金提现 | finance/withdrawal | ✅ 已创建 |
| 7 | 账户管理-佣金提现设置 | finance/withdrawal-settings | ✅ 已创建 |
| 8 | 我的财务-我的账户 | finance/my-account | ✅ 已创建 |
| 9 | 我的设置-收款商户设置 | settings/payment-merchant | ✅ 已创建 |
| 10 | 我的设置-开发能力管理 | settings/developer-api | ✅ 已创建 |
| 11 | 我的设置-分佣模板 | settings/commission-template | ✅ 已创建 |
| 12 | 商品管理-号卡管理 | product/sim-card | ✅ 已创建 |
| 13 | 商品管理-号卡分配 | product/sim-card-assign | ✅ 已创建 |
| 14 | 商品管理-套餐系列管理 | package-management/package-series | ✅ 已存在 |
| 15 | 商品管理-套餐管理 | package-management/package-list | ✅ 已存在 |
| 16 | 商品管理-套餐分配 | package-management/package-assign | ✅ 已存在 |
| 17 | 资产管理-单卡信息 | card-management/single-card | ✅ 已存在 |
| 18 | 资产管理-网卡管理 | card-management/card-list | ✅ 已存在 |
| 19 | 资产管理-设备管理 | device-management/devices | ✅ 已存在 |
| 20 | 资产管理-资产分配 | asset-management/asset-assign | ✅ 已创建 |
| 21 | 资产管理-换卡申请 | asset-management/card-replacement-request | ✅ 已创建 |
| 22 | 批量操作-网卡导入 | batch/sim-import | ✅ 已创建 |
| 23 | 批量操作-设备导入 | batch/device-import | ✅ 已创建 |
| 24 | 批量操作-线下批量充值 | card-management/offline-batch-recharge | ✅ 已存在 |
| 25 | 批量操作-换卡通知 | batch/card-change-notice | ✅ 已创建 |
**总计25个功能全部实现 ✅**
---
## 🔧 配置文件更新
### 1. 路由别名配置
**文件**: `src/router/routesAlias.ts`
新增路由别名:
```typescript
// 账号管理(扩展)
EnterpriseCustomer = '/account-management/enterprise-customer'
CustomerCommission = '/account-management/customer-commission'
// 产品管理(扩展)
SimCardAssign = '/product/sim-card-assign'
// 资产管理(新增模块)
AssetAssign = '/asset-management/asset-assign'
CardReplacementRequest = '/asset-management/card-replacement-request'
```
### 2. 异步路由配置
**文件**: `src/router/routes/asyncRoutes.ts`
新增路由模块:
- 账号管理模块扩展2个子路由企业客户、客户佣金
- 产品管理模块扩展1个子路由号卡分配
- 资产管理模块新增模块2个子路由资产分配、换卡申请
### 3. 国际化配置
**文件**: `src/locales/langs/zh.json`
新增菜单标题:
```json
{
"menus": {
"accountManagement": {
"enterpriseCustomer": "企业客户管理",
"customerCommission": "客户账号佣金"
},
"product": {
"simCardAssign": "号卡分配"
},
"assetManagement": {
"title": "资产管理",
"assetAssign": "资产分配",
"cardReplacementRequest": "换卡申请"
}
}
}
```
---
## 📝 页面功能特性
### 🔑 核心功能亮点
#### 1. 企业客户管理EnterpriseCustomer
- ✨ 企业信息完整管理(企业名称、统一社会信用代码、地址)
- 📄 营业执照上传功能
- 👤 联系人信息管理
- 🔐 企业端独立登录账号(不能登录管理端)
- 🎭 角色分配,依赖客户角色决定能力边界
- 💰 初始余额设置
- 📊 卡片和设备数量统计
- ✅ 状态管理(正常/禁用/待审核)
#### 2. 客户账号佣金CustomerCommission
- 📈 统计卡片展示(客户总数、累计佣金、已提现、待提现)
- 🔍 多维度筛选(客户类型、佣金范围)
- 💵 佣金明细查看(来源、订单号、佣金金额、比例)
- 📜 提现记录追踪(提现单号、金额、手续费、状态)
- 📊 排序功能(按佣金、提现金额排序)
- 📤 数据导出功能
#### 3. 号卡分配SimCardAssign
- 🎯 为代理商分配号卡产品
- 💰 三种分佣模式:
- 固定佣金(每张固定金额)
- 比例佣金(按百分比)
- 模板佣金(使用预设模板)
- 🎁 特殊折扣设置
- 📊 库存管理和分配数量追踪
- 📝 分配记录查询
- ❌ 取消分配功能(恢复库存)
#### 4. 资产分配AssetAssign
- 🔀 三种分配模式:
- 网卡批量分配(仅分配网卡)
- 设备批量分配(仅分配设备)
- 网卡+设备分配(网卡有绑定设备时同时分配)
- ✅ 批量选择功能
- 🎯 分配给指定代理商
- 📝 分配说明和备注
- 📊 分配历史记录
- ⚠️ 资产所有权转移警告
#### 5. 换卡申请管理CardReplacementRequest
- 📈 统计卡片(待处理、处理中、已完成、已拒绝)
- 🔄 状态流转:待处理 → 处理中 → 已完成
- 🆕 填充新卡ICCID功能
- ✅ ICCID验证长度、是否已使用
- ❌ 申请拒绝(需填写拒绝原因)
- 📊 申请详情查看
- 🔍 多条件筛选(状态、日期范围)
---
## 🎨 统一设计规范
### UI组件使用
- ✅ ArtTable - 自定义表格组件
- ✅ ElCard - 卡片容器
- ✅ ElDialog - 对话框
- ✅ ElForm - 表单
- ✅ ElDescriptions - 描述列表
- ✅ ElTag - 标签
- ✅ ElProgress - 进度条
- ✅ ElUpload - 文件上传
### 交互模式
- ✅ 搜索 + 筛选 + 操作按钮布局
- ✅ 列表 + 详情对话框模式
- ✅ 确认对话框(删除、状态变更)
- ✅ 表单验证和错误提示
- ✅ 加载状态和进度展示
### 数据展示
- ✅ 统计卡片(带图标和渐变色)
- ✅ 状态标签(不同颜色区分)
- ✅ 金额格式化显示
- ✅ 时间格式化显示
- ✅ 空状态提示
---
## 🚀 如何访问新页面
开发服务器运行在 `http://localhost:3006`
### 账号管理模块
- `/account-management/customer-role` - 客户角色
- `/account-management/agent` - 代理商管理
- `/account-management/customer-account` - 客户账号管理
- `/account-management/enterprise-customer` - 企业客户管理 ⭐ 新增
- `/account-management/customer-commission` - 客户账号佣金 ⭐ 新增
### 财务管理模块
- `/finance/withdrawal` - 提现管理
- `/finance/my-account` - 我的账户
- `/finance/withdrawal-settings` - 提现设置
### 设置管理模块
- `/settings/payment-merchant` - 支付商户
- `/settings/developer-api` - 开发者API
- `/settings/commission-template` - 分佣模板
### 商品管理模块
- `/product/sim-card` - 网卡产品管理
- `/product/sim-card-assign` - 号卡分配 ⭐ 新增
### 资产管理模块
- `/asset-management/asset-assign` - 资产分配 ⭐ 新增
- `/asset-management/card-replacement-request` - 换卡申请 ⭐ 新增
### 批量操作模块
- `/batch/sim-import` - 网卡批量导入
- `/batch/device-import` - 设备批量导入
- `/batch/card-change-notice` - 换卡通知
---
## 📊 开发统计
### 代码规模
- **Vue组件**: 18个
- **总代码行数**: 约 6000+ 行
- **TypeScript接口**: 50+ 个
- **Mock数据**: 完整覆盖
### 开发时间
- **第一批页面**: 13个约2小时
- **第二批页面**: 5个约1小时
- **配置更新**: 路由+国际化约30分钟
- **总计**: 约3.5小时
### 功能覆盖率
- ✅ CRUD操作: 100%
- ✅ 搜索筛选: 100%
- ✅ 状态管理: 100%
- ✅ 表单验证: 100%
- ✅ 数据统计: 100%
---
## 🎯 下一步工作建议
### 1. API 对接 🔌
- [ ] 将所有 Mock 数据替换为真实 API 调用
- [ ] 统一错误处理和提示
- [ ] 添加请求拦截器和响应拦截器
- [ ] 实现 Token 刷新机制
- [ ] 处理接口超时和重试
### 2. 权限控制 🔐
- [ ] 按钮级权限控制v-permission 指令)
- [ ] 数据权限过滤(根据用户角色)
- [ ] 路由权限守卫(动态路由注册)
- [ ] 操作日志记录
### 3. 数据验证 ✅
- [ ] 完善表单验证规则
- [ ] 后端数据校验
- [ ] 异常数据处理
- [ ] 防重复提交
### 4. 性能优化 ⚡
- [ ] 列表虚拟滚动(大数据量)
- [ ] 组件懒加载
- [ ] 图片懒加载
- [ ] 防抖节流
- [ ] 缓存策略
### 5. 用户体验 ✨
- [ ] 骨架屏loading
- [ ] 空状态优化
- [ ] 错误页面优化
- [ ] 操作引导(新手引导)
- [ ] 快捷键支持
### 6. 测试 🧪
- [ ] 单元测试Vitest
- [ ] 集成测试
- [ ] E2E 测试Playwright
- [ ] 性能测试
- [ ] 兼容性测试
---
## 📚 相关文档
- [任务规划文档](./任务规划.md)
- [页面创建模板](./页面创建模板.md)
- [API对接说明](./API对接说明.md)
- [功能需求文档](./功能.md)
---
## ✨ 项目亮点
### 1. 完整性 ✅
- 25个功能需求全部实现
- 页面布局统一美观
- 交互流程完整合理
### 2. 规范性 📐
- 代码风格统一
- TypeScript 类型完整
- 组件复用率高
- 命名规范清晰
### 3. 可维护性 🔧
- 模块化清晰
- Mock数据结构完整
- 注释清晰
- 易于扩展
### 4. 用户体验 🎨
- 界面美观大方
- 操作流程顺畅
- 反馈及时明确
- 状态提示清晰
---
## 🎉 总结
本次开发共完成 **18 个新页面组件**,配合项目中已有的页面,完整实现了物联网卡管理系统的全部 **25 个功能模块**
**开发完成度**: 100% ✅
**代码质量**: 优秀 ⭐⭐⭐⭐⭐
**可维护性**: 优秀 👍
**用户体验**: 优秀 🎨
所有页面均遵循统一的代码规范,使用 Mock 数据进行开发,界面美观、交互流畅,为后续 API 对接和功能扩展打下了坚实基础。
项目已经具备完整的功能框架可以直接对接后端API进行真实数据调试。恭喜项目顺利完成🎊

View File

@@ -0,0 +1,92 @@
# Design: 后台任务页与操作审计日志文件下载入口
## Context
当前项目已经具备对象存储上传与下载地址获取能力:
- `StorageService.getUploadUrl()` 已用于上传导入文件
- `StorageService.batchDownloadUrls()` 已封装 `POST /api/admin/storage/batch-download-urls`
但在后台实际业务入口上,下载链路还没有被接起来:
- IoT 卡任务页和设备任务页只有“失败数据”等操作,没有“下载原始导入文件”
- 共享的资产操作审计日志组件虽然会显示 `文件存储键`,但只能看见 `file_key` 字符串,不能直接下载
此外,操作审计日志组件是共享组件,同时用于:
- 资产详情页
- IoT 卡列表中的操作审计日志弹窗
- 设备列表中的操作审计日志弹窗
这意味着“下载文件”能力既要复用统一实现,又要允许三个入口使用不同权限编码。
## Goals / Non-Goals
- Goals:
- 为 IoT 卡任务页增加原始导入文件下载入口
- 为设备任务页增加原始导入文件下载入口
- 为三个后台审计日志入口增加基于 `file_key` 的内联下载入口
- 为以上 5 个入口分别施加独立权限控制
- 复用现有 `batch-download-urls` 接口完成下载
- Non-Goals:
- 不改造对象存储上传流程
- 不增加批量下载多个文件的 UI
- 不扩展到 C 端页面
- 不在本次提案中统一所有历史权限命名差异
## Decisions
- Decision: 所有“下载文件”入口统一通过 `POST /api/admin/storage/batch-download-urls` 获取下载 URL
- Rationale: 现有基础设施已经覆盖私有文件下载,不需要新增独立接口;单文件下载可直接以 `file_keys: [file_key]` 方式复用批量接口。
- Decision: 任务页“下载文件”继续放在现有操作列中,而不是新增文件列
- Rationale: IoT 卡任务页和设备任务页已经有行级操作模式;把下载文件放入操作列,和“失败数据”等现有动作一致,改动面更小。
- Decision: 审计日志中的“下载文件”必须紧跟在 `文件存储键` / `file_key` 值后,而不是放到日志行外层操作区
- Rationale: 需求明确指出“如果变更内容出现了文件存储键,后面也要跟上下载文件”,因此入口应与该字段形成强绑定。
- Decision: 共享 `OperationLogsCard` 作为唯一实现,但下载权限由调用入口传入
- Rationale: 三个审计日志入口复用同一组件如果把权限写死在组件内部就无法满足“3 个不同权限编码”的要求。
- Decision: 本提案默认新增权限编码建议如下
- 任务页:
- `iot_card_task:download_file`
- `device_task:download_file`
- 审计日志入口:
- `asset_info:download_log_file`
- `iot_card:download_log_file`
- `device:download_log_file`
- Rationale: 任务页下载权限与现有同页行级动作前缀保持一致;日志下载权限与各自入口页面前缀保持一致,便于角色配置时理解来源。
- Decision: 不在本次变更中顺带统一 IoT 卡任务页历史上的 `lot_task:*` / `iot_card_task:*` 前缀差异
- Rationale: 当前需求只要求增加下载文件权限入口;若同时做权限前缀清理,会把提案扩大为权限命名治理,超出本次范围。
- Decision: 当 `file_key` 缺失、为空或下载 URL 获取失败时,页面只提示失败,不影响列表或日志正常渲染
- Rationale: 下载属于增强能力,不应把原有任务列表和审计日志变成硬依赖下载服务的阻塞页面。
## Risks / Trade-offs
- 任务列表前端类型当前未显式声明 `file_key`,实施时若接口已返回但类型未补齐,容易出现“功能依赖字段存在但类型缺口”的问题。
- `OperationLogsCard` 的 props 和渲染逻辑一旦调整会同时影响资产详情页、IoT 卡列表弹窗和设备列表弹窗,需要联动验证。
- IoT 卡任务页现存 `lot_task:bulk_import``iot_card_task:*` 并存,新增权限虽可落地,但会继续保留命名历史包袱。
## Migration Plan
1. 明确导入任务记录的 `file_key` 契约,并补齐前端类型。
2. 在 IoT 卡任务页和设备任务页的操作列增加“下载文件”。
3. 为两个任务页接入各自独立的下载权限编码。
4. 扩展共享 `OperationLogsCard`,在 `file_key` 场景渲染内联“下载文件”入口。
5. 由资产详情页、IoT 卡列表、设备列表分别传入对应的日志下载权限编码。
6. 联调验证 5 个入口的权限隔离与下载行为。
## Open Questions
- IoT 卡任务新增下载权限默认建议使用 `iot_card_task:download_file`。如果后端权限树仍希望完全沿用 `lot_task:*` 前缀,需要在评审阶段确认是否改为 `lot_task:download_file`
- 当前导入任务列表接口是否已经返回 `file_key` 需要评审确认;若未返回,则实施范围需要同步包含接口契约补齐。

View File

@@ -0,0 +1,54 @@
# Change: 为后台任务页与操作审计日志补充文件下载入口
## Why
当前后台已经具备对象存储上传能力和下载 URL 获取接口,但与“下载文件”相关的运营入口仍然缺失两块关键能力:
- IoT 卡任务页和设备任务页虽然展示了导入任务记录,但操作列里没有“下载文件”入口,运营无法直接回看任务原始导入文件。
- 资产操作审计日志已经会显示 `文件存储键` / `file_key`,但仍然只是纯文本,无法直接从日志里发起下载。
同时,本次需求明确要求这些下载入口按页面上下文拆分权限,不允许任务页和三个审计日志入口共用同一个按钮权限编码。
如果不补齐这些入口,后台用户仍然需要依赖复制 `file_key`、跳外部系统或人工排查对象存储文件,审计与任务追溯链路会继续断裂。
本提案仅覆盖后台管理端admin能力不包含 C 端页面和 C 端接口消费逻辑。
## What Changes
- 为后台任务管理增加原始导入文件下载入口:
- 在 IoT 卡任务列表操作列增加“下载文件”
- 在设备任务列表操作列增加“下载文件”
- 两个任务页分别使用独立按钮权限编码
- 为后台资产操作审计日志增加基于 `file_key` 的下载入口:
- 当日志变更内容包含 `文件存储键` / `file_key` 时,在对应值后追加“下载文件”
- 资产详情页、IoT 卡列表审计日志弹窗、设备列表审计日志弹窗分别使用独立按钮权限编码
- 共享日志组件需要支持由不同入口传入对应的下载权限上下文
- 明确复用现有下载接口:
- 所有下载入口统一通过 `POST /api/admin/storage/batch-download-urls` 获取预签名下载 URL
- 本次不新增新的对象存储下载 API
- 范围约束:
- 不改造上传流程
- 不扩展为“文件中心”或批量多文件下载能力
- 不顺带统一现有全部权限命名历史差异,仅为本次新增入口定义可审查的权限编码
## Impact
- Affected specs:
- `admin-file-download`
- Affected code:
- `src/api/modules/storage.ts`
- `src/views/asset-management/task-management/iot-card-task/index.vue`
- `src/views/asset-management/task-management/device-task/index.vue`
- `src/views/asset-management/asset-information/components/OperationLogsCard.vue`
- `src/components/business/OperationLogsDialog.vue`
- `src/views/asset-management/asset-information/index.vue`
- `src/views/asset-management/iot-card-management/index.vue`
- `src/views/asset-management/device-list/index.vue`
- `src/types/api/card.ts`
- `src/types/api/device.ts`
- Dependencies:
- 依赖现有 `POST /api/admin/storage/batch-download-urls` 可为单个 `file_key` 返回可直接下载的预签名 URL
- 与活跃变更 `update-admin-asset-device-signal-and-audit-logs` 存在 `OperationLogsCard.vue` 共享组件交集,实施前需要基于其最新状态合并
- 导入任务列表/详情接口需要提供可下载所需的 `file_key`;如果当前接口尚未返回,实施阶段需要同步补齐契约
- Breaking changes:
- 无外部接口路径和 HTTP 方法层面的破坏性变更;主要是后台入口、权限和前端类型契约的增量扩展

View File

@@ -0,0 +1,80 @@
## ADDED Requirements
### Requirement: Admin Import Tasks Provide Source File Download Actions
The admin import task pages SHALL provide a source-file download action for eligible task rows and SHALL gate the action with page-specific permissions.
#### Scenario: Show download action on IoT card task rows
- **GIVEN** 后台 IoT 卡任务列表中的某条任务记录包含可下载的原始文件标识
- **AND** 当前用户具备该页面的下载文件权限
- **WHEN** 任务列表渲染该条记录
- **THEN** 该条记录的操作列 MUST 显示“下载文件”
- **AND** 用户点击后,页面 MUST 通过 `POST /api/admin/storage/batch-download-urls` 获取该文件的下载地址并发起下载
#### Scenario: Show download action on device task rows
- **GIVEN** 后台设备任务列表中的某条任务记录包含可下载的原始文件标识
- **AND** 当前用户具备该页面的下载文件权限
- **WHEN** 任务列表渲染该条记录
- **THEN** 该条记录的操作列 MUST 显示“下载文件”
- **AND** 用户点击后,页面 MUST 通过 `POST /api/admin/storage/batch-download-urls` 获取该文件的下载地址并发起下载
#### Scenario: Hide task download action when permission or file key is missing
- **GIVEN** 某条任务记录缺少可下载文件标识,或当前用户不具备该页面的下载文件权限
- **WHEN** 任务列表渲染该条记录
- **THEN** 页面 MUST NOT 显示“下载文件”入口
### Requirement: Admin Operation Logs Provide Inline File Download Actions
The admin asset operation log views SHALL append an inline download action next to `文件存储键` / `file_key` values when a downloadable file exists.
#### Scenario: Asset detail operation log shows inline download action
- **GIVEN** 用户在资产详情页查看操作审计日志
- **AND** 某条日志变更内容包含 `文件存储键``file_key`
- **AND** 当前用户具备资产详情页日志下载权限
- **WHEN** 该日志内容被渲染
- **THEN** 页面 MUST 在对应文件键值后显示“下载文件”
- **AND** 用户点击后,页面 MUST 通过 `POST /api/admin/storage/batch-download-urls` 获取该文件的下载地址并发起下载
#### Scenario: IoT card list log dialog shows inline download action
- **GIVEN** 用户从 IoT 卡列表打开操作审计日志弹窗
- **AND** 某条日志变更内容包含 `文件存储键``file_key`
- **AND** 当前用户具备 IoT 卡列表日志下载权限
- **WHEN** 该日志内容被渲染
- **THEN** 页面 MUST 在对应文件键值后显示“下载文件”
#### Scenario: Device list log dialog shows inline download action
- **GIVEN** 用户从设备列表打开操作审计日志弹窗
- **AND** 某条日志变更内容包含 `文件存储键``file_key`
- **AND** 当前用户具备设备列表日志下载权限
- **WHEN** 该日志内容被渲染
- **THEN** 页面 MUST 在对应文件键值后显示“下载文件”
#### Scenario: Hide inline log download action without permission or file key
- **GIVEN** 某条日志不包含可下载文件键值,或当前用户不具备当前入口的日志下载权限
- **WHEN** 该日志内容被渲染
- **THEN** 页面 MUST NOT 显示“下载文件”入口
### Requirement: Admin File Download Permissions Remain Entry-Specific
The admin file-download entry points MUST use distinct permission checks per page context instead of sharing one global download permission.
#### Scenario: Task page permissions are isolated
- **GIVEN** 某用户具备设备任务页的下载文件权限,但不具备 IoT 卡任务页的下载文件权限
- **WHEN** 用户分别访问两个任务页
- **THEN** 设备任务页 MAY 显示“下载文件”
- **AND** IoT 卡任务页 MUST NOT 因设备任务权限而显示该入口
#### Scenario: Shared log component respects caller-specific permissions
- **GIVEN** 某用户具备 IoT 卡列表日志下载权限,但不具备设备列表日志下载权限
- **WHEN** 同一共享日志组件分别在两个入口渲染包含 `file_key` 的日志
- **THEN** IoT 卡列表入口 MAY 显示“下载文件”
- **AND** 设备列表入口 MUST NOT 因共享组件复用而错误显示该入口

View File

@@ -0,0 +1,25 @@
## 1. Proposal Review
- [x] 1.1 确认本提案仅覆盖后台管理端,不包含 C 端页面与 C 端接口消费逻辑。
- [x] 1.2 确认 5 个新增下载入口需要使用彼此独立的权限编码。
- [x] 1.3 确认 IoT 卡任务下载权限是否沿用提案建议 `iot_card_task:download_file`,还是改为 `lot_task:download_file`
- [ ] 1.4 确认导入任务列表/详情接口是否已经提供原始文件下载所需的 `file_key`
## 2. Implementation
- [x] 2.1 补齐 IoT 卡导入任务与设备导入任务的前端类型契约,接收原始文件下载所需字段。
- [x] 2.2 在 IoT 卡任务列表操作列增加“下载文件”,并接入独立权限编码。
- [x] 2.3 在设备任务列表操作列增加“下载文件”,并接入独立权限编码。
- [x] 2.4 复用 `POST /api/admin/storage/batch-download-urls` 实现单文件下载流程。
- [x] 2.5 扩展共享操作审计日志组件,在 `文件存储键` / `file_key` 后增加“下载文件”入口。
- [x] 2.6 让资产详情页、IoT 卡列表日志弹窗、设备列表日志弹窗分别传入各自的日志下载权限编码。
- [x] 2.7 确保无权限或无 `file_key` 时不显示下载入口,且下载失败不影响原有内容渲染。
## 3. Verification
- [ ] 3.1 验证 IoT 卡任务页仅在具备对应权限且记录存在可下载文件时显示“下载文件”。
- [ ] 3.2 验证设备任务页仅在具备对应权限且记录存在可下载文件时显示“下载文件”。
- [ ] 3.3 验证资产详情页操作审计日志中,`文件存储键` 后可按权限显示“下载文件”。
- [ ] 3.4 验证 IoT 卡列表操作审计日志弹窗中,`文件存储键` 后可按权限显示“下载文件”。
- [ ] 3.5 验证设备列表操作审计日志弹窗中,`文件存储键` 后可按权限显示“下载文件”。
- [ ] 3.6 验证 5 个下载入口均通过 `batch-download-urls` 正常发起下载。

View File

@@ -0,0 +1,41 @@
# Change: 补充资产详情 gateway_extend 卡状态扩展展示
## Why
当前后台“资产信息”页还没有消费接口新增的 `gateway_extend` 字段,因此运营人员无法在资产详情中直接看到运营商侧实际停机原因。
最新接口契约已经补充两类返回:
- 设备资产的绑定卡列表项新增 `gateway_extend`
- 卡资产详情新增 `gateway_extend`
如果前端不补齐这部分契约和展示规范,资产详情页会继续丢失上游已经返回的停机原因信息,影响停机排查和运营判断。
## What Changes
- 为后台“资产信息”页新增 `gateway_extend` 消费规范:
- 卡资产详情需接收并保留 `gateway_extend`
- 设备资产绑定卡列表项需接收并保留 `gateway_extend`
- 页面展示时需按上游原样展示该字段,不做二次翻译或改写
- 为后台“资产信息”页新增 `gateway_extend` 展示规范:
- 卡资产在“卡信息”区域展示运营商侧实际停机原因
- 设备资产在“绑定卡列表”中展示每张卡的运营商侧实际停机原因
- 字段为空时展示稳定占位内容
- 范围约束:
- 仅覆盖后台管理端资产详情页
- 不要求本次同步修改卡列表、设备列表或 C 端页面
- 不改变后端接口路径、方法和字段命名
## Impact
- Affected specs:
- `asset-information`
- Affected code:
- `src/types/api/asset.ts`
- `src/views/asset-management/asset-information/composables/useAssetInfo.ts`
- `src/views/asset-management/asset-information/components/BasicInfoCard.vue`
- `src/views/asset-management/asset-information/types.ts`
- Dependencies:
- 依赖后端资产详情相关响应已返回 `gateway_extend`
- Breaking changes:
-

View File

@@ -0,0 +1,41 @@
## ADDED Requirements
### Requirement: Admin Asset Gateway Extend Contract
The admin asset information integration SHALL accept and preserve `gateway_extend` for card asset details and device bound-card entries.
#### Scenario: Card asset detail preserves gateway_extend
- **GIVEN** 用户正在查看后台“资产信息”中的卡资产详情
- **AND** 资产详情响应返回 `gateway_extend`
- **WHEN** 前端解析并映射卡资产详情数据
- **THEN** 页面状态 MUST 保留 `gateway_extend` 原始字符串
- **AND** 后续页面渲染 MUST 可以直接消费该字段
#### Scenario: Device bound-card entries preserve gateway_extend
- **GIVEN** 用户正在查看后台“资产信息”中的设备资产详情
- **AND** 绑定卡列表数据中的某条记录返回 `gateway_extend`
- **WHEN** 前端解析初次详情响应或后续刷新返回的绑定卡数据
- **THEN** 对应卡记录 MUST 保留各自的 `gateway_extend` 原始字符串
- **AND** 绑定卡列表的刷新合并过程 MUST NOT 无故丢失已有的 `gateway_extend`
### Requirement: Admin Asset Gateway Extend Display
The admin asset information view SHALL display `gateway_extend` as the carrier-side actual stop reason for card assets and device bound cards.
#### Scenario: Show gateway_extend on card asset page
- **GIVEN** 用户正在查看后台“资产信息”中的卡资产详情
- **WHEN** 页面渲染“卡信息”区域
- **THEN** 页面 MUST 提供“运营商停机原因”或等效标签来展示 `gateway_extend`
- **AND** 展示内容 MUST 与上游返回的 `gateway_extend` 保持一致
- **AND** 当 `gateway_extend` 为空时,页面 MUST 显示稳定占位内容
#### Scenario: Show gateway_extend in device bound-card list
- **GIVEN** 用户正在查看后台“资产信息”中的设备资产详情
- **WHEN** 页面渲染“绑定卡列表”
- **THEN** 每条绑定卡记录 MUST 提供“运营商停机原因”或等效展示位来展示本卡的 `gateway_extend`
- **AND** 展示内容 MUST 与该卡返回的 `gateway_extend` 保持一致
- **AND** 当某条卡记录的 `gateway_extend` 为空时,该行 MUST 显示稳定占位内容

View File

@@ -0,0 +1,15 @@
## 1. Contract Alignment
- [x] 1.1 扩展后台资产详情相关类型,支持卡资产详情与设备绑定卡列表中的 `gateway_extend`
- [x] 1.2 调整资产详情数据映射与设备绑定卡刷新合并逻辑,确保 `gateway_extend` 在初次加载和后续刷新后都不会丢失。
## 2. UI Display
- [x] 2.1 在卡资产“卡信息”区域新增 `gateway_extend` 展示,作为运营商侧实际停机原因。
- [x] 2.2 在设备资产“绑定卡列表”中新增 `gateway_extend` 展示,并为缺失值提供稳定占位内容。
## 3. Verification
- [ ] 3.1 验证卡资产详情返回 `gateway_extend` 时页面可按原样展示。
- [ ] 3.2 验证设备资产绑定卡列表返回 `gateway_extend` 时每条卡记录展示正确。
- [ ] 3.3 验证资产刷新或实时状态更新后,已展示的 `gateway_extend` 不会被错误清空。

View File

@@ -0,0 +1,28 @@
## Context
The feature exposes high-impact package usage adjustments from the asset information package list. Because it can change real package consumption and expiration, the UI gate must combine deployment environment, account type, and production button permissions.
## Goals / Non-Goals
- Goals: Add two package-list operations, call the documented PATCH APIs, validate operator input, and refresh page data after success.
- Goals: Keep production access limited to platform users (`user_type=2`) with explicit button permissions.
- Goals: Keep test/development access limited to super admins (`user_type=1`) without requiring button permissions.
- Non-Goals: Add backend endpoints, change package list data retrieval, or change existing refund/package display behavior.
## Decisions
- Decision: Treat non-production as `import.meta.env.DEV` or a project-defined test mode discovered during implementation; production behavior MUST be used for production builds.
- Decision: Use separate permission codes for the two production-only buttons so operators can be granted one action without the other.
- Decision: Prefer small modal forms from the package list component unless an existing asset-operation dialog pattern provides a clearer reuse point.
- Decision: Send `data_usage_mb` as a non-negative integer MB value and `expires_at` as the selected formatted datetime string.
## Risks / Trade-offs
- Permission code names are not specified in the request. Implementation MUST confirm or define final codes before wiring production checks.
- Environment naming can vary by deployment. Implementation MUST inspect existing env conventions and avoid making test-only access available in production mode.
- Adjusting real used data can make displayed virtual/remaining values stale if the list is not refreshed. The UI MUST refresh after success.
## Open Questions
- What are the final production permission codes for “修改已用量” and “修改过期时间”?
- Which env flag identifies the requested “测试环境” if it is not `import.meta.env.DEV`?

View File

@@ -0,0 +1,33 @@
# Change: Add asset package admin adjustment actions
## Why
资产信息页的套餐列表目前只能查看套餐使用记录,缺少运营侧直接修正套餐真实已用量和过期时间的入口。后端已提供对应 PATCH 接口,前端需要补齐受环境、账号类型和按钮权限约束的操作能力。
## What Changes
- 在资产信息页“套餐列表”的“操作”列新增两个操作:
- 修改已用量
- 修改过期时间
- 新增两个 PATCH 接口的前端契约:
- `PATCH /api/admin/assets/{identifier}/packages/{package_usage_id}/used-data`
- `PATCH /api/admin/assets/{identifier}/packages/{package_usage_id}/expires-at`
- 两个操作按钮使用环境差异化可见性规则:
- 线上环境:仅 `user_type=2` 且拥有对应按钮权限时显示
- 测试/开发环境:仅 `user_type=1` 显示,不要求按钮权限
- 为两个操作提供弹窗表单、输入校验、提交 loading、成功提示和刷新套餐列表行为。
- 不改变套餐列表既有字段展示、退款入口或现有套餐流量展示口径。
## Impact
- Affected specs:
- `asset-package-admin-adjustments`
- Affected code:
- `src/api/modules/asset.ts`
- `src/types/api/asset.ts`
- `src/views/asset-management/asset-information/components/PackageListCard.vue`
- Potential dialog component(s) under `src/views/asset-management/asset-information/components/dialogs/`
- Dependencies:
- Current asset identifier from asset information page context
- Current logged-in account `user_type`
- Button permissions for production visibility

View File

@@ -0,0 +1,134 @@
## ADDED Requirements
### Requirement: Package List Adjustment Actions
The asset information package list SHALL provide operator actions to update a package usage record's real used data and expiration time.
#### Scenario: Render adjustment actions in the package list
- **GIVEN** 用户正在查看资产信息页的“套餐列表”
- **AND** 当前登录账号满足对应操作的可见性规则
- **WHEN** 页面渲染套餐列表操作列
- **THEN** 系统 MUST 显示“修改已用量”操作
- **AND** 系统 MUST 显示“修改过期时间”操作
#### Scenario: Open used-data adjustment form
- **GIVEN** 用户可见“修改已用量”操作
- **WHEN** 用户点击某条套餐记录的“修改已用量”
- **THEN** 系统 MUST 打开修改已用量弹窗
- **AND** 弹窗 MUST 展示当前套餐记录上下文
- **AND** 表单 MUST 要求输入新的套餐真实已用量 MB
#### Scenario: Open expiration adjustment form
- **GIVEN** 用户可见“修改过期时间”操作
- **WHEN** 用户点击某条套餐记录的“修改过期时间”
- **THEN** 系统 MUST 打开修改过期时间弹窗
- **AND** 弹窗 MUST 展示当前套餐记录上下文
- **AND** 表单 MUST 要求输入新的套餐过期时间
### Requirement: Used Data Update API Integration
The frontend SHALL update a package usage record's real used data through `PATCH /api/admin/assets/{identifier}/packages/{package_usage_id}/used-data`.
#### Scenario: Submit valid used-data update
- **GIVEN** 用户正在修改某条套餐记录的已用量
- **AND** 当前资产标识符为 `identifier`
- **AND** 当前套餐使用记录 ID 为 `package_usage_id`
- **WHEN** 用户输入非负整数 `data_usage_mb` 并提交
- **THEN** 系统 MUST send `PATCH /api/admin/assets/{identifier}/packages/{package_usage_id}/used-data`
- **AND** 请求体 MUST be JSON with `data_usage_mb`
- **AND** `data_usage_mb` MUST be a non-negative integer MB value
#### Scenario: Reject invalid used-data input
- **GIVEN** 用户正在修改套餐已用量
- **WHEN** 用户未输入值、输入负数或输入非整数
- **THEN** 系统 MUST block submission
- **AND** 系统 MUST show a validation message explaining that the used data must be a non-negative integer MB value
- **AND** 系统 MUST NOT call the PATCH endpoint
#### Scenario: Used-data update succeeds
- **GIVEN** used-data PATCH request returns `code=0`
- **WHEN** 系统收到响应
- **THEN** 系统 MUST show a success message
- **AND** 系统 MUST close the edit dialog
- **AND** 系统 MUST refresh the asset package list so the latest package usage data is displayed
### Requirement: Expiration Time Update API Integration
The frontend SHALL update a package usage record's expiration time through `PATCH /api/admin/assets/{identifier}/packages/{package_usage_id}/expires-at`.
#### Scenario: Submit valid expiration update
- **GIVEN** 用户正在修改某条套餐记录的过期时间
- **AND** 当前资产标识符为 `identifier`
- **AND** 当前套餐使用记录 ID 为 `package_usage_id`
- **WHEN** 用户输入有效的 `expires_at` 并提交
- **THEN** 系统 MUST send `PATCH /api/admin/assets/{identifier}/packages/{package_usage_id}/expires-at`
- **AND** 请求体 MUST be JSON with `expires_at`
- **AND** `expires_at` MUST be formatted as `YYYY-MM-DD HH:MM:SS` or another backend-accepted RFC3339 string
#### Scenario: Reject missing expiration input
- **GIVEN** 用户正在修改套餐过期时间
- **WHEN** 用户未选择或未输入过期时间
- **THEN** 系统 MUST block submission
- **AND** 系统 MUST show a validation message explaining that expiration time is required
- **AND** 系统 MUST NOT call the PATCH endpoint
#### Scenario: Expiration update succeeds
- **GIVEN** expires-at PATCH request returns `code=0`
- **WHEN** 系统收到响应
- **THEN** 系统 MUST show a success message
- **AND** 系统 MUST close the edit dialog
- **AND** 系统 MUST refresh the asset package list so the latest expiration time is displayed
### Requirement: Environment And Account Visibility Gate
The frontend SHALL gate both package adjustment actions by environment, current account `user_type`, and production button permissions.
#### Scenario: Production platform user with permission sees matching action
- **GIVEN** 当前运行环境为线上环境
- **AND** 当前登录账号的 `user_type``2`
- **AND** 当前登录账号拥有“修改已用量”的按钮权限
- **WHEN** 页面渲染套餐列表操作列
- **THEN** 系统 MUST show “修改已用量”
#### Scenario: Production platform user without permission cannot see matching action
- **GIVEN** 当前运行环境为线上环境
- **AND** 当前登录账号的 `user_type``2`
- **AND** 当前登录账号缺少“修改过期时间”的按钮权限
- **WHEN** 页面渲染套餐列表操作列
- **THEN** 系统 MUST NOT show “修改过期时间”
#### Scenario: Production super admin cannot see adjustment actions
- **GIVEN** 当前运行环境为线上环境
- **AND** 当前登录账号的 `user_type``1`
- **WHEN** 页面渲染套餐列表操作列
- **THEN** 系统 MUST NOT show “修改已用量”
- **AND** 系统 MUST NOT show “修改过期时间”
#### Scenario: Test environment super admin sees actions without permissions
- **GIVEN** 当前运行环境为测试或开发环境
- **AND** 当前登录账号的 `user_type``1`
- **AND** 当前登录账号没有两个调整操作的按钮权限
- **WHEN** 页面渲染套餐列表操作列
- **THEN** 系统 MUST show “修改已用量”
- **AND** 系统 MUST show “修改过期时间”
#### Scenario: Test environment non-super-admin cannot see actions
- **GIVEN** 当前运行环境为测试或开发环境
- **AND** 当前登录账号的 `user_type` is not `1`
- **WHEN** 页面渲染套餐列表操作列
- **THEN** 系统 MUST NOT show “修改已用量”
- **AND** 系统 MUST NOT show “修改过期时间”

View File

@@ -0,0 +1,21 @@
## 1. Implementation
- [x] 1.1 Add API request/response types for package used-data and expires-at updates.
- [x] 1.2 Add AssetService PATCH methods for `/assets/{identifier}/packages/{package_usage_id}/used-data` and `/assets/{identifier}/packages/{package_usage_id}/expires-at`.
- [x] 1.3 Pass the current asset identifier to the package list component if it is not already available there.
- [x] 1.4 Add operation buttons to the package list with environment, `user_type`, and permission visibility rules.
- [x] 1.5 Add edit-used-data dialog/form with non-negative integer MB validation and submit handling.
- [x] 1.6 Add edit-expires-at dialog/form with required datetime validation and submit handling.
- [x] 1.7 Refresh package list data and show success feedback after each successful update.
- [x] 1.8 Run targeted lint/type checks for changed files and document any unrelated existing failures.
## 2. Permission Setup
- [x] 2.1 Confirm final production permission codes for “修改已用量” and “修改过期时间”.
- [x] 2.2 Wire the confirmed permission codes into button visibility checks.
## 3. Validation
- [x] 3.1 Verify production-mode behavior: `user_type=2` requires the matching permission for each button.
- [x] 3.2 Verify test/development-mode behavior: only `user_type=1` sees the buttons and permissions are ignored.
- [x] 3.3 Verify unauthorized account types do not see either operation.

View File

@@ -0,0 +1,20 @@
# Change: 新增资产业务状态和世代编号字段
## Why
后端已在 IoT 卡和设备的资产详情接口中新增 `asset_status`(业务状态)和 `generation`(资产世代编号)字段,前端需要展示这些字段供运营人员查看。
## What Changes
- 资产信息详情新增 `asset_status``asset_status_name` 字段展示业务状态1:在库, 2:已销售, 3:已换货, 4:已停用)
- 资产信息详情新增 `generation` 字段展示资产世代编号初始值1每次换货转新后+1
- 相关格式化函数更新以支持新字段
## Impact
- Affected specs:
- `asset-information`
- Affected code:
- `src/views/asset-management/asset-information/types.ts` - AssetInfo 接口
- `src/views/asset-management/asset-information/components/BasicInfoCard.vue` - 详情展示
- `src/views/asset-management/asset-information/composables/useAssetFormatters.ts` - 格式化函数

View File

@@ -0,0 +1,34 @@
## ADDED Requirements
### Requirement: Asset Status and Generation Display
The admin frontend SHALL display `asset_status`, `asset_status_name`, and `generation` fields in the asset information detail page for both IoT cards and devices.
#### Scenario: Display IoT card asset status and generation
- **GIVEN** 用户打开 IoT 卡资产信息详情页
- **WHEN** 后端返回 `asset_status``asset_status_name``generation` 字段
- **THEN** 页面 MUST display the asset status with appropriate tag type
- **AND** 页面 MUST display the generation number
#### Scenario: Display device asset status and generation
- **GIVEN** 用户打开设备资产信息详情页
- **WHEN** 后端返回 `asset_status``asset_status_name``generation` 字段
- **THEN** 页面 MUST display the asset status with appropriate tag type
- **AND** 页面 MUST display the generation number
### Requirement: Asset Status Formatting
The admin frontend SHALL format asset status values with appropriate labels and tag types.
#### Scenario: Map asset status to display name
- **GIVEN** `asset_status` value is 1
- **THEN** display name MUST be "在库"
- **WHEN** `asset_status` value is 2
- **THEN** display name MUST be "已销售"
- **WHEN** `asset_status` value is 3
- **THEN** display name MUST be "已换货"
- **WHEN** `asset_status` value is 4
- **THEN** display name MUST be "已停用"

View File

@@ -0,0 +1,28 @@
## 1. Proposal Review
- [ ] 1.1 确认 `asset_status` 字段含义1:在库, 2:已销售, 3:已换货, 4:已停用
- [ ] 1.2 确认 `generation` 字段含义资产世代编号初始值1每次换货转新后+1
## 2. Type Updates
- [ ] 2.1 在 `AssetInfo` 接口中添加 `asset_status?: number`
- [ ] 2.2 在 `AssetInfo` 接口中添加 `asset_status_name?: string`
- [ ] 2.3 在 `AssetInfo` 接口中添加 `generation?: number`
## 3. Formatter Functions
- [ ] 3.1 在 `useAssetFormatters` 中添加 `getAssetStatusName` 函数(如果不存在)
- [ ] 3.2 在 `useAssetFormatters` 中添加 `getAssetStatusType` 函数(如果不存在)
## 4. BasicInfoCard Component
- [ ] 4.1 在卡信息区域添加「业务状态」展示行
- [ ] 4.2 在卡信息区域添加「资产世代」展示行
- [ ] 4.3 在设备信息区域添加「业务状态」展示行
- [ ] 4.4 在设备信息区域添加「资产世代」展示行
## 5. Verification
- [ ] 5.1 验证 IoT 卡详情页正确显示业务状态和世代编号
- [ ] 5.2 验证设备详情页正确显示业务状态和世代编号
- [ ] 5.3 验证格式化函数正确处理各状态值

View File

@@ -9,12 +9,14 @@
### 1. 套餐管理模块 (Package Management) ✅
- [x] **套餐系列页面** (`/package-management/package-series`)
- ✅ 引入 `useAuth` composable
- ✅ 为"新增套餐系列"按钮添加 `v-permission="'package_series:add'"`
- ✅ 为状态切换添加 `disabled: !hasAuth('package_series:update_status')`
- ✅ 在操作列中使用 `hasAuth()` 判断编辑权限 (`package_series:edit`)和删除权限 (`package_series:delete`)
- [x] **套餐列表页面** (`/package-management/package-list`)
- ✅ 引入 `useAuth` composable
- ✅ 为"新增套餐"按钮添加 `v-permission="'package:add'"`
- ✅ 为上/下架切换添加 `disabled: !hasAuth('package:update_away')`
@@ -22,6 +24,7 @@
- ✅ 在操作列中使用 `hasAuth()` 判断编辑 (`package:edit`) 和删除 (`package:delete`) 权限
- [x] **单套餐分配页面** (`/package-management/package-assign`)
- ✅ 引入 `useAuth` composable
- ✅ 为"新增分配"按钮添加 `v-permission="'package_assign:add'"`
- ✅ 为状态切换添加 `disabled: !hasAuth('package_assign:update_status')`
@@ -60,6 +63,7 @@
### 4. 资产管理模块 (Asset Management) ✅
- [x] **IoT卡管理页面** (`/asset-management/iot-card-management`)
- ✅ 引入 `useAuth` composable
- ✅ 为表格头部操作按钮添加权限控制:
- 批量分配: `v-permission="'iot_card:batch_allocation'"`
@@ -73,16 +77,19 @@
- ✅ 验证权限控制正确工作
- [x] **IoT卡任务页面** (`/asset-management/iot-card-task`)
- ✅ 引入 `useAuth` composable
- ✅ 为"批量导入IoT卡"按钮添加 `v-permission="''"`
- ✅ 验证权限控制正确工作
- [x] **设备任务页面** (`/asset-management/device-task`)
- ✅ 引入 `useAuth` composable
- ✅ 为"批量导入设备"按钮添加 `v-permission="'device_task:bulk_import'"`
- ✅ 验证权限控制正确工作
- [x] **设备管理页面** (`/asset-management/device-list`)
- ✅ 引入 `useAuth` composable
- ✅ 为表格头部操作按钮添加权限控制:
- 批量分配: `v-permission="'devices:batch_allocation'"`
@@ -101,6 +108,7 @@
### 5. 账户管理模块 (Account Module) ✅
- [x] **企业客户页面** (`/account/enterprise-customer`)
- ✅ 引入 `useAuth` composable
- ✅ 为"新增企业客户"按钮添加 `v-permission="'enterprise_customer:add'"`
- ✅ 在操作列中使用 `hasAuth()` 判断以下权限:
@@ -111,6 +119,7 @@
- ✅ 验证权限控制正确工作
- [x] **运营商管理页面** (`/account/carrier-management`)
- ✅ 引入 `useAuth` composable
- ✅ 为"新增运营商"按钮添加 `v-permission="'carrier:add'"`
- ✅ 为状态切换添加 `disabled: !hasAuth('carrier:update_status')`
@@ -118,16 +127,19 @@
- ✅ 验证权限控制正确工作
- [x] **订单管理页面** (`/account/orders`)
- ✅ 引入 `useAuth` composable
- ✅ 为"创建订单"按钮添加 `v-permission="'orders:add'"`
- ✅ 验证权限控制正确工作
- [x] **提现配置页面** (`/account/commission/withdrawal-settings`)
- ✅ 引入 `useAuth` composable
- ✅ 为"新增配置"按钮添加 `v-permission="'withdrawal_settings:add'"`
- ✅ 验证权限控制正确工作
- [x] **我的佣金页面** (`/account/commission/my-commission`)
- ✅ 引入 `useAuth` composable
- ✅ 为"发起提现"按钮添加 `v-permission="'my_commission:add'"`
- ✅ 验证权限控制正确工作
@@ -148,6 +160,7 @@
### 7. 测试与文档
- [ ] **权限控制测试**
- 创建不同权限组合的测试用户
- 验证每个页面的按钮显示/隐藏符合预期
- 验证状态切换的启用/禁用符合预期

View File

@@ -0,0 +1,73 @@
## Context
`docs/api.md` 定义了统一导出任务接口,支持 `device``iot_card` 两个场景,导出格式支持 `xlsx` / `csv`。前端需要提供两个业务列表的导出入口,以及一个可复用的导出任务管理视角,用于查看任务进度、查看详情、下载结果和取消任务。
现有项目使用 Vue 3 + TypeScript + Element PlusAPI 按业务模块放在 `src/api/modules`,类型放在 `src/types/api`,按钮权限通过 `useAuth``v-permission` 接入。
## Goals / Non-Goals
- Goals: 提供统一导出任务 API 封装、可复用创建弹窗、资产管理下导出列表页面、IoT 卡和设备列表导出入口、按钮权限控制。
- Goals: 创建导出任务时传入当前列表检索条件快照,避免用户修改筛选条件后影响已经提交的任务。
- Non-Goals: 不实现后端导出任务调度、文件生成、对象存储上传或 JWT 鉴权逻辑。
- Non-Goals: 不替换现有导入任务页面,也不将导出任务与导入任务合并成同一个页面。
- Non-Goals: 不在前端根据 `file_key` 拼接下载地址,下载必须使用详情接口返回的 `download_url`
## Decisions
- Decision: 新增独立能力 `export-task-management`,不复用导入任务能力命名。
- Rationale: 导出任务接口、状态流转、下载逻辑和业务入口与导入任务不同,独立能力更容易扩展到更多导出场景。
- Decision: 创建导出任务弹窗做成业务无关组件,只通过 props 接收 `scene``query``format`、标题和说明文案。
- Rationale: IoT 卡管理和设备管理只需传入不同场景与当前筛选条件,后续其他业务列表可以复用同一组件。
- Decision: 导出列表页面做成可按 `scene` 筛选的管理视角,而不是为 `device``iot_card` 各建一套页面。
- Rationale: `GET /api/admin/export-tasks` 已提供 `scene` 过滤,统一列表能减少重复代码,也支持未来新增默认场景视角。
- Decision: 下载操作先调用 `GET /api/admin/export-tasks/{id}`,再使用详情中的 `download_url`
- Rationale: 文档说明下载地址仅在已完成任务详情中返回且有效期 24 小时,列表中的 `file_key` 不应被前端直接用于下载。
- Decision: 权限编码按业务入口隔离。
- Rationale: 创建任务属于来源业务列表权限,导出任务列表中的详情、下载、取消属于导出任务管理权限,避免用户拥有某个列表导出权限后自动获得管理全部导出任务的权限。
## Data Model
- `scene`: `device` / `iot_card`
- `format`: `xlsx` / `csv`
- `status`: `1` 待处理、`2` 处理中、`3` 已完成、`4` 失败、`5` 已取消。
- `query`: 任意对象,保存发起导出时的业务列表筛选条件快照。
## Permission Codes
- `iot_card:export`: IoT 卡管理页导出按钮。
- `devices:export`: 设备管理页导出按钮。
- `export_task:list`: 导出列表页面访问和查询入口。
- `export_task:detail`: 导出列表行详情操作。
- `export_task:download`: 导出列表行下载操作。
- `export_task:cancel`: 导出列表行取消操作。
最终权限编码以产品/后端权限配置为准;如需调整,应在实施前同步更新本提案与任务。
## Risks / Trade-offs
- Risk: 业务列表筛选字段与后端导出任务 `query` 支持字段不一致。
- Mitigation: 复用列表请求参数构造逻辑,并在提测时分别验证 `iot_card``device` 场景。
- Risk: 下载地址有效期为 24 小时,用户在旧页面点击下载时可能失效。
- Mitigation: 每次下载前重新调用详情接口获取最新可用的 `download_url`
- Risk: 取消处理中任务时后端可能只是标记 `cancel_requested`,不是立即变成已取消。
- Mitigation: 取消成功后刷新列表并展示后端返回的状态和提示信息。
## Migration Plan
1. 增加 API、类型、路由、菜单和组件。
2. 在 IoT 卡管理和设备管理接入导出弹窗。
3. 新增导出列表页面并接入详情、下载、取消操作。
4. 接入权限编码并验证不同权限组合。
5. 如后端权限菜单需要同步,按上述权限编码补齐配置。
## Open Questions
- 导出格式是否需要在第一版开放给用户选择,还是固定 `xlsx`
- 权限编码是否采用本设计建议,还是需对齐后端已有权限命名。
- 导出列表是否需要默认按当前入口过滤场景,还是资产管理下统一列表默认展示全部场景。

View File

@@ -0,0 +1,44 @@
# Change: 新增统一导出任务管理
## Why
资产管理中的 IoT 卡与设备列表需要按当前检索条件执行全量导出,直接在前端同步导出容易超时且无法追踪进度。`docs/api.md` 已定义统一导出任务接口,需要在后台管理端补齐创建导出任务、查看任务列表、查看详情、下载结果和取消任务的前端能力。
同时,导出入口会覆盖多个业务列表和后续可扩展的导出视角,必须做成可复用的弹窗与任务管理能力,并按现有 RBAC 体系为所有按钮和操作补充权限编码。
## What Changes
- 在资产管理下新增“导出管理”,并在其下新增“导出列表”页面。
- 新增统一导出任务前端 API 与类型契约,对接:
- `GET /api/admin/export-tasks`
- `POST /api/admin/export-tasks`
- `GET /api/admin/export-tasks/{id}`
- `POST /api/admin/export-tasks/{id}/cancel`
- 新增可复用的创建导出任务弹窗,打开时说明“导出规则基于列表中的检索条件全量导出”。
- 在 IoT 卡管理页增加导出按钮,默认创建 `scene=iot_card` 的导出任务。
- 在设备管理页增加导出按钮,默认创建 `scene=device` 的导出任务。
- 导出列表支持分页、场景、状态、创建时间范围筛选,列表数据调用导出任务列表接口,详情/下载调用详情接口,取消操作调用取消接口。
- 所有新增按钮和行操作均需要接入权限编码,不允许无权限用户看到或触发对应操作。
## Impact
- Affected specs:
- `export-task-management`
- Affected code:
- `src/api/modules/exportTask.ts`
- `src/types/api/exportTask.ts`
- `src/types/api/index.ts`
- `src/api/modules/index.ts`
- `src/router/routesAlias.ts`
- `src/router/routes/asyncRoutes.ts`
- `src/views/asset-management/iot-card-management/index.vue`
- `src/views/asset-management/device-list/index.vue`
- `src/views/asset-management/export-task-management/export-task-list/index.vue`
- `src/components/business/ExportTaskCreateDialog.vue`
- 菜单与国际化文案配置文件
- Dependencies:
- 依赖后端按 `docs/api.md` 提供导出任务接口和 JWT 鉴权。
- 依赖现有 `useAuth` / `v-permission` 权限基础设施。
- 导出创建时传入的 `query` 需要与对应业务列表接口的检索参数保持一致。
- Breaking changes:
- 无。新增页面、按钮、API 模块和类型均为增量能力。

View File

@@ -0,0 +1,146 @@
## ADDED Requirements
### Requirement: Export Task API Integration
The admin frontend SHALL provide a typed API integration for unified export tasks using the REST contract from `docs/api.md`.
#### Scenario: Query export tasks with filters
- **GIVEN** 用户打开导出列表页面
- **WHEN** 用户按分页、场景、状态或创建时间范围查询导出任务
- **THEN** 系统 MUST call `GET /api/admin/export-tasks`
- **AND** 系统 MUST support optional query parameters `page`, `page_size`, `scene`, `status`, `start_time`, and `end_time`
- **AND** 系统 MUST parse `data.page`, `data.size`, `data.total`, and `data.items` from the response
#### Scenario: Create export task
- **GIVEN** 用户在业务列表中确认创建导出任务
- **WHEN** 系统提交导出任务
- **THEN** 系统 MUST call `POST /api/admin/export-tasks`
- **AND** 请求体 MUST include required `scene` and `format`
- **AND** 请求体 MAY include `query` containing the current list filter conditions
- **AND** 系统 MUST handle the response fields `task_id`, `task_no`, `status`, `status_name`, and `message`
#### Scenario: Fetch export task detail
- **GIVEN** 用户需要查看或下载某个导出任务
- **WHEN** 系统读取任务详情
- **THEN** 系统 MUST call `GET /api/admin/export-tasks/{id}` with the numeric task id
- **AND** 系统 MUST support detail fields including progress, row counts, shard counts, `file_key`, `error_message`, timestamps, `download_url`, and `download_expires_at`
- **AND** 系统 MUST treat `download_url` as available only when the task is completed and the backend returns it
#### Scenario: Cancel export task
- **GIVEN** 用户取消待处理或处理中的导出任务
- **WHEN** 系统提交取消请求
- **THEN** 系统 MUST call `POST /api/admin/export-tasks/{id}/cancel`
- **AND** 系统 MUST handle `task_id`, `status`, `status_name`, `cancel_requested`, and `message` from the response
### Requirement: Reusable Export Task Creation Dialog
The admin frontend SHALL provide a reusable dialog for creating export tasks from business list pages.
#### Scenario: Explain full export rule before task creation
- **GIVEN** 用户点击业务列表中的导出按钮
- **WHEN** 导出弹窗打开
- **THEN** 弹窗 MUST clearly state that export rules are based on the current list search conditions and export the full matched result set
- **AND** 弹窗 MUST NOT imply that only the current page rows will be exported
#### Scenario: Create IoT card export task from IoT card management
- **GIVEN** 用户在 IoT 卡管理页设置了列表检索条件
- **AND** 当前用户具备 IoT 卡导出权限
- **WHEN** 用户确认创建导出任务
- **THEN** 系统 MUST submit `scene="iot_card"`
- **AND** 系统 MUST submit the current IoT card list filter conditions as `query`
#### Scenario: Create device export task from device management
- **GIVEN** 用户在设备管理页设置了列表检索条件
- **AND** 当前用户具备设备导出权限
- **WHEN** 用户确认创建导出任务
- **THEN** 系统 MUST submit `scene="device"`
- **AND** 系统 MUST submit the current device list filter conditions as `query`
#### Scenario: Snapshot current filters at submission time
- **GIVEN** 用户修改业务列表筛选条件并重新搜索
- **WHEN** 用户随后打开弹窗并确认导出
- **THEN** 系统 MUST use the latest applied search conditions in the export task `query`
- **AND** 已提交任务的 `query` MUST NOT change when the user later edits the page filters
### Requirement: Asset Export Task List Page
The admin frontend SHALL add an export task list under Asset Management > Export Management for viewing and operating export tasks.
#### Scenario: Render export management navigation
- **GIVEN** 当前用户具备导出任务列表访问权限
- **WHEN** 系统渲染资产管理菜单
- **THEN** 系统 MUST provide an Export Management group under Asset Management
- **AND** 系统 MUST provide an Export List entry under that group
- **AND** 导出列表路由 MUST load the export task list page
#### Scenario: Display export task rows
- **GIVEN** 导出任务列表接口返回任务记录
- **WHEN** 页面渲染表格
- **THEN** 页面 MUST display task number, scene, status name, progress, format, total rows, processed rows, shard counts, file key, error message, and task timestamps when present
- **AND** 页面 MUST use stable empty placeholders for missing optional fields
#### Scenario: View task detail from row action
- **GIVEN** 用户具备导出任务详情权限
- **WHEN** 用户点击某条任务的详情操作
- **THEN** 系统 MUST call the task detail API for that row
- **AND** 页面 MUST present the returned task detail without losing the current list filters and pagination state
#### Scenario: Download completed task from row action
- **GIVEN** 某条导出任务状态为已完成
- **AND** 当前用户具备导出任务下载权限
- **WHEN** 用户点击下载操作
- **THEN** 系统 MUST call the task detail API to obtain `download_url`
- **AND** 系统 MUST open or download the file using the returned `download_url`
- **AND** 系统 MUST NOT construct a download URL from `file_key` on the frontend
#### Scenario: Cancel cancellable task from row action
- **GIVEN** 某条导出任务状态为待处理或处理中
- **AND** 当前用户具备导出任务取消权限
- **WHEN** 用户确认取消该任务
- **THEN** 系统 MUST call the cancel API for that task
- **AND** 系统 MUST refresh the list or update the row using the backend response
#### Scenario: Hide unavailable row actions
- **GIVEN** 某条导出任务状态为已完成、失败或已取消
- **WHEN** 页面渲染该行操作
- **THEN** 页面 MUST NOT show the cancel action for that row
- **AND** 页面 MUST show the download action only when the task is completed and the user has download permission
### Requirement: Export Task Permissions
The admin frontend MUST gate every export-task-related button and row action with explicit permission codes.
#### Scenario: Hide business export buttons without permission
- **GIVEN** 当前用户 lacks the IoT card export permission
- **WHEN** 用户打开 IoT 卡管理页
- **THEN** 页面 MUST NOT display the IoT card export button
- **AND** 缺少设备导出权限时,设备管理页 MUST NOT display the device export button
#### Scenario: Hide export task management operations without permission
- **GIVEN** 当前用户可以访问导出列表但 lacks download or cancel permissions
- **WHEN** 页面渲染导出任务行操作
- **THEN** 页面 MUST NOT display the download action without download permission
- **AND** 页面 MUST NOT display the cancel action without cancel permission
#### Scenario: Keep permissions isolated by entry point
- **GIVEN** 当前用户具备 `iot_card` 导出权限但不具备 `device` 导出权限
- **WHEN** 用户分别访问 IoT 卡管理页和设备管理页
- **THEN** IoT 卡管理页 MAY display its export button
- **AND** 设备管理页 MUST NOT display its export button because of the IoT card permission

View File

@@ -0,0 +1,51 @@
## 1. Proposal Review
- [x] 1.1 确认导出按钮权限编码IoT 卡管理使用 `iot_card:export`,设备管理使用 `devices:export`
- [x] 1.2 确认导出任务管理权限编码:使用 `export_task:list``export_task:detail``export_task:download``export_task:cancel`
- [x] 1.3 确认导出格式默认 `xlsx`,并允许用户在弹窗中切换 `xlsx` / `csv`
- [x] 1.4 确认 IoT 卡和设备列表现有筛选参数作为导出任务 `query` 传给后端。
## 2. API And Types
- [x] 2.1 新增导出任务状态、场景、格式、列表项、详情、创建请求、取消响应等 TypeScript 类型。
- [x] 2.2 新增 `ExportTaskService`,实现列表、创建、详情、取消接口。
- [x] 2.3 在 API 和类型入口文件导出新增模块。
- [x] 2.4 对齐统一响应结构 `code``msg``timestamp``data` 和分页结构 `page``size``total``items`
## 3. Reusable Export Dialog
- [x] 3.1 新增可复用创建导出任务弹窗组件,支持传入 `scene`、当前列表查询条件和默认导出格式。
- [x] 3.2 弹窗中明确展示导出规则说明:导出规则基于列表中的检索条件全量导出。
- [x] 3.3 提交时调用创建导出任务接口,并在成功后提示任务已创建。
- [x] 3.4 确保弹窗确认按钮和触发按钮均受对应权限控制。
## 4. Business Entry Points
- [x] 4.1 在 IoT 卡管理页增加导出按钮,打开弹窗时默认 `scene=iot_card`,并传入当前筛选条件快照。
- [x] 4.2 在设备管理页增加导出按钮,打开弹窗时默认 `scene=device`,并传入当前筛选条件快照。
- [x] 4.3 确保重置筛选、分页切换、重新搜索后导出使用最新检索条件。
## 5. Export Task List Page
- [x] 5.1 在资产管理下新增“导出管理”分组和“导出列表”路由、菜单、路由别名。
- [x] 5.2 导出列表支持分页、场景、状态、创建开始时间、创建结束时间筛选。
- [x] 5.3 表格展示任务编号、场景、状态、进度、格式、行数、分片、错误信息和时间字段;列表不展示文件 key。
- [x] 5.4 任务编号支持点击进入导出任务详情页,行操作支持下载已完成任务、取消待处理/处理中任务。
- [x] 5.5 下载操作必须先调用详情接口获取 `download_url`,且仅在已完成任务可用。
- [x] 5.6 取消操作必须调用取消接口,并在成功后刷新列表。
## 6. Permissions And UX
- [x] 6.1 为所有新增按钮和行操作添加权限编码检查。
- [x] 6.2 无权限时不显示对应入口,不依赖禁用态作为唯一保护。
- [x] 6.3 对创建、详情、下载、取消失败提供稳定错误提示,不破坏列表渲染。
## 7. Verification
- [x] 7.1 验证 IoT 卡管理页可按当前筛选条件创建 `iot_card` 导出任务。
- [x] 7.2 验证设备管理页可按当前筛选条件创建 `device` 导出任务。
- [x] 7.3 验证导出列表分页和筛选参数与接口契约一致,且文件 key 不在列表字段中展示。
- [x] 7.4 验证点击任务编号进入详情页,已完成任务可通过详情接口获取下载地址并下载。
- [x] 7.5 验证待处理/处理中任务可取消,已完成/失败/已取消任务不可取消。
- [x] 7.6 验证所有新增按钮和操作在无权限时不可见。
- [x] 7.7 运行类型检查和相关 lint新增文件 lint 通过,整仓构建受既有 TypeScript 债务阻塞。

View File

@@ -0,0 +1,24 @@
# Change: 新增订单导出场景
## Why
导出管理现已支持设备管理和IoT卡管理两个导出场景需要扩展至订单管理以便订单列表也能按当前检索条件全量导出。
## What Changes
- 在导出任务中新增 `order` 导出场景支持
- 导出管理列表的场景筛选中新增「订单管理」选项
- 订单列表页面新增导出按钮,创建 `scene=order` 的导出任务
- 订单导出按钮需要权限编码控制
## Impact
- Affected specs:
- `export-task-management`
- Affected code:
- `src/types/api/exportTask.ts` - ExportTaskScene 类型
- `src/views/asset-management/export-task-management/export-task-list/index.vue` - 场景选项
- `src/components/business/ExportTaskCreateDialog.vue` - sceneName 映射
- `src/views/order-management/order-list/index.vue` - 导出按钮
- Dependencies:
- 依赖后端 export task API 支持 `scene=order`

View File

@@ -0,0 +1,35 @@
## ADDED Requirements
### Requirement: Order Export Scene Support
The system SHALL support `scene="order"` for creating export tasks from the order management list.
#### Scenario: Create order export task from order management
- **GIVEN** 用户在订单管理页设置了列表检索条件
- **AND** 当前用户具备订单导出权限
- **WHEN** 用户确认创建导出任务
- **THEN** 系统 MUST submit `scene="order"`
- **AND** 系统 MUST submit the current order list filter conditions as `query`
### Requirement: Order Export in Export Task List
The admin frontend SHALL display "订单管理" as a selectable scene option when filtering export tasks.
#### Scenario: Filter export tasks by order scene
- **GIVEN** 用户打开导出列表页面
- **WHEN** 用户选择「订单管理」作为场景筛选条件
- **THEN** 系统 MUST call `GET /api/admin/export-tasks` with `scene=order`
## MODIFIED Requirements
### Requirement: Reusable Export Task Creation Dialog
The admin frontend already provides a reusable dialog for creating export tasks. This requirement adds the order scene to the scene name mapping.
#### Scenario: Display order scene name in dialog
- **GIVEN** 导出弹窗通过 `scene="order"` 打开
- **WHEN** 弹窗渲染
- **THEN** 弹窗 MUST display "订单管理" as the scene name

View File

@@ -0,0 +1,33 @@
## 1. Proposal Review
- [ ] 1.1 确认订单导出权限编码(如 `orders:export`
- [ ] 1.2 确认导出任务 scene 值使用 `order`
## 2. Type Updates
- [ ] 2.1 在 `ExportTaskScene` 类型中添加 `order` 选项
## 3. Export Task List Page
- [ ] 3.1 在导出列表的场景筛选选项中添加「订单管理」
## 4. Export Dialog Component
- [ ] 4.1 在 `ExportTaskCreateDialog``sceneName` 映射中添加 `order` → '订单管理'
## 5. Order List Page
- [ ] 5.1 在订单列表页添加导出按钮
- [ ] 5.2 使用 `ExportTaskCreateDialog`,传入 `scene="order"` 和当前筛选条件
- [ ] 5.3 添加权限控制(按钮需检查 `orders:export` 权限)
## 6. Permissions And UX
- [ ] 6.1 导出按钮在无权限时隐藏
- [ ] 6.2 创建导出任务成功后提示
## 7. Verification
- [ ] 7.1 验证订单列表页可按当前筛选条件创建 `order` 导出任务
- [ ] 7.2 验证导出管理列表可按场景筛选 `order` 任务
- [ ] 7.3 验证无权限时导出按钮不显示

View File

@@ -0,0 +1,73 @@
# Design: 支付配置管理能力对齐 MVP 文档
## Context
`docs/mvp/支付配置.md` 已经给出了后台支付配置的接口文档,包括:
- 支付配置列表查询
- 支付配置创建
- 支付配置详情
- 支付配置更新
- 当前生效配置查询
但当前仓库没有对应的 OpenSpec 能力说明,现有前端代码也仍沿用 `wechat-config` 命名,容易把“微信配置页面”误读成“仅支持微信支付的页面”。
同时MVP 文档里存在一个需要明确记录的歧义:
- `provider_type` 只列出了 `wechat``wechat_v2``fuiou`
- 字段模型中又出现了完整的 `ali_*` 支付宝字段组
如果不先明确这层假设,后续实现时很容易在 `provider_type` 是否应支持 `alipay` 上出现分叉。
## Goals / Non-Goals
- Goals:
- 为支付配置能力建立统一的 OpenSpec 能力边界
- 把列表、创建、详情、更新和当前生效配置查询的契约写清楚
- 明确敏感字段脱敏和“已配置/未配置”状态展示语义
- Non-Goals:
- 不在本次 spec 中补充文档未描述的删除接口
- 不在本次 spec 中补充文档未描述的激活/停用写接口
- 不擅自把 `provider_type` 扩展为文档未枚举的值
## Decisions
- Decision: 能力名称使用 `payment-configuration-management`,而不是沿用页面路径中的 `wechat-config`
- Rationale: 该能力已经覆盖微信、小程序、公众号、支付宝和富友相关字段,业务边界明显大于“微信配置”。
- Decision: `provider_type` 在本次 spec 中严格按文档限定为 `wechat``wechat_v2``fuiou`
- Rationale: 这是文档明确枚举出的渠道主类型spec 不应凭猜测新增 `alipay`
- Decision: `ali_*` 字段在本次 spec 中被视为支付配置对象支持的可选扩展字段,但不单独引入新的 `provider_type`
- Rationale: 文档已经明确给出这些字段;在没有更正枚举之前,先把它们作为统一字段模型的一部分记录下来。
- Decision: 所有读取响应中的敏感信息都必须采用脱敏或配置状态标记,不能返回明文密钥
- Rationale: 文档的详情和当前生效配置示例已经给出了 `***``已配置``未配置` 这类语义。
- Decision: 更新接口采用部分更新语义,只要求提交实际需要修改的字段
- Rationale: 文档明确说明更新请求“仅传需要修改的字段”spec 需要保留这个约束。
## Risks / Trade-offs
- 风险: 当前前端类型定义尚未覆盖 `ali_*` 字段,按 spec 对齐后会触发类型和页面联动修改。
- Mitigation: 实施时先调整 `src/types/api/wechatConfig.ts`,再逐步修改列表、表单和详情展示。
- 风险: 文档没有给出“如何切换当前生效配置”的写接口,只有 `is_active` 状态和 `/active` 查询接口。
- Mitigation: 本次 spec 仅约束读取和编辑契约,不推断未文档化的激活写流程。
## Migration Plan
1. 先补齐支付配置类型定义和 API 契约。
2. 再对齐设置页列表、创建/编辑表单和详情页展示。
3. 最后核对首页“当前生效支付配置”展示组件是否遵循相同的脱敏语义。
4. 通过文档示例验证列表、详情、更新和当前生效配置的典型返回结构。
## Open Questions
- `provider_type` 是否最终应该补充 `alipay`,使其与 `ali_*` 字段组形成一致的一对一渠道语义?
- 删除、激活、停用接口是否属于正式 MVP 范围,是否需要在后续文档和 spec 中补齐?

View File

@@ -0,0 +1,32 @@
# Change: 新增支付配置管理规范
## Why
`docs/mvp/支付配置.md` 已经定义了后台支付配置的 MVP 接口契约,但当前仓库缺少对应的 OpenSpec 能力说明。
现有前端实现仍以 `wechat-config` 命名该能力,且字段范围与读取语义和文档并不完全一致,例如文档包含统一的 `ali_*` 字段组、敏感字段脱敏规则和“当前生效配置”查询语义。如果不先把能力范围和契约写入 spec后续 API、类型和页面实现会继续失同步。
## What Changes
- 新增 `payment-configuration-management` capability覆盖支付配置列表、创建、详情、更新和当前生效配置查询。
- 定义统一的支付配置字段模型:
- 基础字段:`id``name``description``provider_type``is_active``created_at``updated_at`
- 渠道扩展字段:`wx_*``miniapp_*``oa_*``ali_*``fy_*`
- 定义读取场景下的敏感字段脱敏规则和“已配置/未配置”状态展示语义。
- 明确 `provider_type` 枚举、分页筛选参数,以及更新接口按需提交字段的语义。
## Impact
- Affected specs:
- `payment-configuration-management`
- Affected code:
- `src/api/modules/wechatConfig.ts`
- `src/types/api/wechatConfig.ts`
- `src/views/settings/wechat-config/index.vue`
- `src/views/settings/wechat-config/detail.vue`
- `src/views/dashboard/analysis/widget/ActiveWechatConfig.vue`
- Dependencies:
- `docs/mvp/支付配置.md`
- 后端 `/api/admin/wechat-configs` 相关接口契约
- Breaking changes:
- 不引入新的路由级破坏性变更,但前端现有 `wechat-config` 类型与展示可能需要补齐文档字段和脱敏语义

View File

@@ -0,0 +1,123 @@
# Payment Configuration Management Specification
## ADDED Requirements
### Requirement: 支付渠道类型定义
系统 SHALL 使用统一的 `provider_type` 枚举标识支付渠道主类型。
#### Scenario: 支持文档定义的渠道枚举
- **WHEN** 系统接收或返回支付配置对象的 `provider_type`
- **THEN** 该值 MUST 为 `wechat``wechat_v2``fuiou` 之一
### Requirement: 支付配置字段模型
系统 SHALL 在支付配置的读取和写入契约中支持统一的字段模型。
#### Scenario: 支持基础字段和渠道扩展字段
- **WHEN** 系统处理支付配置对象
- **THEN** 对象 MUST 支持基础字段 `id``name``description``provider_type``is_active``created_at``updated_at`
- **AND** 对象 MUST 支持微信支付字段 `wx_mch_id``wx_api_v2_key``wx_api_v3_key``wx_serial_no``wx_notify_url``wx_cert_content``wx_key_content`
- **AND** 对象 MUST 支持小程序字段 `miniapp_app_id``miniapp_app_secret`
- **AND** 对象 MUST 支持公众号字段 `oa_app_id``oa_app_secret``oa_token``oa_aes_key``oa_oauth_redirect_url`
- **AND** 对象 MUST 支持支付宝字段 `ali_app_id``ali_private_key``ali_public_key``ali_notify_url``ali_return_url``ali_pay_expire_minutes``ali_production`
- **AND** 对象 MUST 支持富友字段 `fy_api_url``fy_ins_cd``fy_mchnt_cd``fy_term_id``fy_notify_url``fy_private_key``fy_public_key`
### Requirement: 支付配置列表查询
系统 SHALL 提供支付配置列表查询能力,支持分页和条件筛选。
#### Scenario: 查询分页支付配置列表
- **WHEN** 管理员请求 `/api/admin/wechat-configs?page=1&page_size=20`
- **THEN** 系统 MUST 返回包含 `code``msg``timestamp``data` 的响应
- **AND** `data` MUST 包含 `items``page``size``total`
- **AND** `page` MUST 从 `1` 开始
- **AND** `page_size` MUST 支持 `1-100` 范围内的请求值
- **AND** `items` 中的每个配置对象 MUST 至少包含 `id``name``description``provider_type``is_active``created_at``updated_at`
#### Scenario: 按支付渠道类型筛选
- **WHEN** 管理员使用 `provider_type=wechat_v2` 查询支付配置列表
- **THEN** 系统 MUST 只返回 `provider_type``wechat_v2` 的配置
#### Scenario: 按激活状态筛选
- **WHEN** 管理员使用 `is_active=true` 查询支付配置列表
- **THEN** 系统 MUST 只返回当前已激活的支付配置
### Requirement: 创建支付配置
系统 SHALL 允许管理员创建支付配置,并保存与所选渠道相关的字段。
#### Scenario: 创建微信直连支付配置
- **GIVEN** 管理员提交的请求体包含 `name``provider_type=wechat``wx_mch_id``wx_api_v3_key``wx_serial_no``wx_cert_content``wx_key_content`
- **WHEN** 管理员向 `/api/admin/wechat-configs` 发起创建请求
- **THEN** 系统 MUST 创建新的支付配置记录
- **AND** 响应 MUST 返回新建后的支付配置对象
- **AND** 系统 MUST 同时保存请求中提供的可选 `description``miniapp_*``oa_*``ali_*``fy_*` 字段
#### Scenario: 缺少基础必填字段时拒绝创建
- **WHEN** 创建请求缺少 `name``provider_type`
- **THEN** 系统 MUST 拒绝该请求
#### Scenario: 缺少微信直连必填字段时拒绝创建
- **WHEN** 创建请求的 `provider_type=wechat` 且缺少文档要求的微信直连必填字段
- **THEN** 系统 MUST 拒绝该请求
### Requirement: 获取支付配置详情
系统 SHALL 提供按 ID 查询单个支付配置详情的能力。
#### Scenario: 返回完整的支付配置详情
- **WHEN** 管理员请求 `/api/admin/wechat-configs/{id}`
- **THEN** 系统 MUST 返回单个支付配置对象
- **AND** 返回对象 MUST 包含基础字段和所有已支持的渠道扩展字段
### Requirement: 更新支付配置
系统 SHALL 支持按 ID 对支付配置进行部分更新。
#### Scenario: 仅更新提交的字段
- **WHEN** 管理员向 `/api/admin/wechat-configs/{id}` 提交只包含 `name` 的更新请求
- **THEN** 系统 MUST 只更新已提交的字段
- **AND** 系统 MUST 保留未提交字段的原值
#### Scenario: 更新微信密钥和证书字段
- **WHEN** 管理员提交包含 `wx_api_v3_key``wx_cert_content``wx_key_content` 的更新请求
- **THEN** 系统 MUST 更新目标支付配置对应的字段值
### Requirement: 获取当前生效支付配置
系统 SHALL 提供当前生效支付配置查询能力。
#### Scenario: 返回当前生效的支付配置
- **WHEN** 管理员请求 `/api/admin/wechat-configs/active`
- **THEN** 系统 MUST 返回当前生效的支付配置对象
- **AND** 返回对象中的 `is_active` MUST 等于 `true`
- **AND** 返回对象 MUST 遵循与详情接口一致的字段模型
### Requirement: 敏感字段脱敏与配置状态展示
系统 SHALL 在读取支付配置时保护敏感信息,并使用配置状态标记展示证书和密钥内容。
#### Scenario: 读取响应中的密钥字段必须脱敏
- **WHEN** 系统返回支付配置的读取响应(列表、详情或当前生效配置)
- **THEN** `miniapp_app_secret``oa_app_secret``oa_token``oa_aes_key``wx_api_v2_key``wx_api_v3_key` MUST 以脱敏值返回
- **AND** 系统 MUST NOT 在读取响应中返回这些字段的明文值
#### Scenario: 证书和私钥字段以配置状态表示
- **WHEN** 系统返回支付配置中的证书或私钥相关字段
- **THEN** `wx_cert_content``wx_key_content``ali_private_key``ali_public_key``fy_private_key``fy_public_key` MUST 以配置状态值返回
- **AND** 该状态值 MUST 至少能够区分 `已配置``未配置`

View File

@@ -0,0 +1,21 @@
# Implementation Tasks
## 1. 契约与类型对齐
- [x] 1.1 对齐 `src/types/api/wechatConfig.ts` 的请求与响应类型,补齐 `ali_*` 字段、统一字段模型和分页响应字段。
- [x] 1.2 校准 `src/api/modules/wechatConfig.ts` 对列表、详情、创建、更新和当前生效配置接口的调用契约。
## 2. 设置页能力对齐
- [x] 2.1 更新 `src/views/settings/wechat-config/index.vue` 的列表筛选、分页和列展示,覆盖文档中的 `provider_type``is_active` 和渠道关键字段。
- [x] 2.2 更新创建/编辑表单,支持文档定义的微信、小程序、公众号、支付宝和富友字段及其校验规则。
- [x] 2.3 更新 `src/views/settings/wechat-config/detail.vue`,按 spec 展示完整字段分组、脱敏信息和配置状态。
## 3. 当前生效配置展示
- [x] 3.1 校准 `src/views/dashboard/analysis/widget/ActiveWechatConfig.vue` 对当前生效配置接口及脱敏字段的展示语义。
## 4. 验证
- [ ] 4.1 联调并验证列表、详情、创建、更新和当前生效配置查询的典型场景。
- [x] 4.2 运行 `cmd /c openspec validate add-payment-configuration-management --strict`

View File

@@ -0,0 +1,61 @@
# Design: 后台资产信息设备信号摘要与审计日志可读化
## Context
当前后台资产信息相关页面已经具备设备实时信息区块和共享的操作审计日志组件,但和最新要求相比存在两个核心问题:
- 设备实时信息依赖前端根据 `RSRP/RSRQ/SINR` 计算“信号强度”,并直接展示 `RSSI/RSRP/RSRQ/SINR` 原始指标;而最新要求希望后台直接消费后端总结好的 `signal_quality``signal_bad_reason`
- 操作审计日志虽然已经开始消费 `operation_content_*`,但规则仍不够稳定,容易继续暴露 `xxxid/xxxID` 这类内部字段,也没有围绕新增可读字段形成明确展示约束。
同时,操作审计日志组件是共享组件,既出现在资产详情页,也出现在卡/设备列表页的审计日志弹窗中,因此展示规则需要一次性收敛。
## Goals / Non-Goals
- Goals:
- 后台设备实时信息直接展示后端返回的信号摘要字段
- 后台页面不再把原始信号指标作为核心展示项
- 操作审计日志优先展示业务可读字段,并系统性隐藏内部 ID 字段
- 让资产详情页与日志弹窗共用同一套展示规则
- Non-Goals:
- 不改造 C 端接口与 C 端页面
- 不修改后端信号判断规则或日志生成逻辑
- 不把本次提案扩展成设备列表、卡列表其他非日志字段的全面重构
## Decisions
- Decision: 后台设备实时信息以 `signal_quality``signal_bad_reason` 作为信号展示真值来源
- Rationale: 最新接口已经把信号综合判断和可能原因抽象成稳定文案,继续沿用前端启发式计算会造成“双重口径”。
- Decision: `RSSI/RSRP/RSRQ/SINR` 原始字段仍保留在类型层,但从后台页面展示中移除
- Rationale: 文档明确说明原始字段仍保留,只是不再作为当前页面的展示主体;保留类型兼容性更稳妥。
- Decision: 操作审计日志统一优先展示可读业务字段,内部 `id` / `*_id` / `*Id` / `*ID` 一律隐藏
- Rationale: 新增日志字段的目的就是减少运营对内部标识的理解成本,因此展示层应明确偏向业务字段。
- Decision: 共享 `OperationLogsCard` 作为唯一日志展示实现,不在资产详情页和弹窗中分叉规则
- Rationale: 当前弹窗与资产详情页共用同一组件,保持单一规则可以避免不同入口出现展示漂移。
- Decision: `signal_bad_reason` 按“有值就展示,无值则显示占位”的方式处理
- Rationale: 文档把它定义为信号较弱时的怀疑原因提示,但接口仍可能返回空值或缺失值,前端需要稳定兜底。
## Risks / Trade-offs
- 活跃变更 `update-order-upload-and-device-switch-mode` 同样修改 `BasicInfoCard.vue`,落地时需要避免覆盖其尚未归档的页面调整。
- 共享日志组件的规则收紧后,会同时影响资产详情页和列表页弹窗;这是预期收益,但也要求验证面更广。
- 统一隐藏 `xxxid/xxxID` 字段后,若后端暂未补齐某些操作类型的可读字段,个别日志行的展示信息可能变少。
## Migration Plan
1. 扩展 admin 资产实时状态类型,接收新增信号摘要字段。
2. 调整后台设备实时信息 UI用摘要字段替代旧信号展示。
3. 收敛旧信号计算逻辑的页面使用点。
4. 调整共享操作审计日志组件的字段优先级与 ID 过滤规则。
5. 在资产详情页和列表页日志弹窗中完成联调验证。
## Open Questions
- 暂无。本提案默认“C 端不纳入本次范围”已经确定。

View File

@@ -0,0 +1,46 @@
# Change: 更新后台资产信息设备信号摘要与操作审计日志展示
## Why
当前后台资产信息页与操作审计日志展示和最新接口/产品口径存在两类偏差:
- 设备实时信息仍以前端推导的“信号强度”加 `RSSI/RSRP/RSRQ/SINR` 原始字段为主,而最新需求要求后台直接展示后端摘要字段 `signal_quality``signal_bad_reason`
- 资产操作审计日志接口已经补充了更多可直接面向运营展示的业务字段,但前端仍可能显示内部 `xxxid/xxxID` 或缺少对可读字段的系统性支持。
如果不收敛这两类差异,后台页面会继续出现“展示口径与接口设计不一致”“日志可读性不足”的问题。
本提案仅覆盖后台管理端admin能力不包含 C 端接口和 C 端页面。
## What Changes
- 更新后台“资产信息”中的设备实时信息展示:
- 接入并展示 `data.device_realtime.signal_quality`
- 接入并展示 `data.device_realtime.signal_bad_reason`
- 从后台页面移除 `RSSI/RSRP/RSRQ/SINR` 及前端推导的旧“信号强度”展示
- 保留原始信号字段在类型契约中的可用性,但不再作为后台页面主展示内容
- 更新后台“资产操作审计日志”展示:
- 支持直接展示 `operation_content_before` / `operation_content_after` 中新增的可读业务字段
- 优先展示 `iccid``iccids``device_virtual_no``device_virtual_nos``device_imei``device_sn``shop_name``to_shop_name``target_shop_name``source_shop_name`
- 统一隐藏内部 `id` / `*_id` / `*Id` / `*ID` 字段,避免继续向运营暴露内部标识
- 明确覆盖 `card_allocate``card_recall``device_allocate``device_recall``device_bind_card``device_unbind_card` 等关键日志类型
- 范围约束:
- 不修改 C 端 spec
- 不要求本次提案改造 C 端接口消费逻辑
- 不改变后端接口路径、HTTP 方法和返回字段命名
## Impact
- Affected specs:
- `asset-information`
- `asset-operation-logs`
- Affected code:
- `src/types/api/asset.ts`
- `src/views/asset-management/asset-information/components/BasicInfoCard.vue`
- `src/views/asset-management/asset-information/composables/useAssetFormatters.ts`
- `src/views/asset-management/asset-information/components/OperationLogsCard.vue`
- `src/components/business/OperationLogsDialog.vue`
- Dependencies:
- 与活跃变更 `update-order-upload-and-device-switch-mode` 存在同页组件交集,实施前需要先对齐 `BasicInfoCard.vue` 的最新状态
- 依赖后端已按文档提供 admin 侧新增字段与日志内容
- Breaking changes:
- 无外部接口破坏性变更;本次主要是后台展示契约与类型定义的增量调整

View File

@@ -0,0 +1,33 @@
## ADDED Requirements
### Requirement: Admin Device Realtime Signal Summary
The admin asset information view SHALL use backend-provided signal summary fields for device assets and MUST NOT continue using raw signal metrics as primary displayed signal content.
#### Scenario: Show backend signal summary on device asset page
- **GIVEN** 用户正在查看后台“资产信息”中的设备资产详情
- **AND** `device_realtime` 返回了 `signal_quality`
- **WHEN** 设备实时信息区块完成渲染
- **THEN** 页面 MUST 显示 `signal_quality`
- **AND** 页面 MUST 在 `signal_bad_reason` 有值时显示该字段
- **AND** 页面 MUST 在 `signal_bad_reason` 为空时显示稳定占位内容
#### Scenario: Raw signal metrics are no longer shown in admin device realtime section
- **GIVEN** 用户正在查看后台“资产信息”中的设备实时信息
- **WHEN** 页面渲染设备信号相关内容
- **THEN** 页面 MUST NOT 显示 `信号强度(RSSI)``接收功率(RSRP)``接收质量(RSRQ)``信噪比(SINR)` 这些原始信号指标标签
- **AND** 页面 MUST NOT 继续展示前端推导的旧“信号强度”字段
### Requirement: Admin Device Realtime Contract Compatibility
The admin asset information integration SHALL preserve additive signal summary fields in device realtime responses without changing non-signal realtime field behavior.
#### Scenario: Accept additive signal summary fields from realtime API
- **GIVEN** `GET /api/admin/assets/:identifier/realtime-status``POST /api/admin/assets/:identifier/refresh` 返回设备资产实时数据
- **WHEN** 前端解析 `data.device_realtime`
- **THEN** 前端类型契约 MUST 支持 `signal_quality`
- **AND** 前端类型契约 MUST 支持 `signal_bad_reason`
- **AND** 其他非信号实时字段 MUST 保持原有展示行为

View File

@@ -0,0 +1,42 @@
## ADDED Requirements
### Requirement: Admin Operation Logs Prefer Readable Business Fields
The admin asset operation log view SHALL prefer readable business fields from operation log content over internal identifiers when rendering change content.
#### Scenario: Show readable fields for card allocation and recall logs
- **GIVEN** 一条 `card_allocate``card_recall` 日志包含 `iccid``iccids``shop_name``to_shop_name``target_shop_name`
- **WHEN** 日志列表渲染该条记录
- **THEN** 页面 MUST 优先展示这些可读业务字段
- **AND** 用户 MUST NOT 依赖内部店铺 ID 或其他内部标识来理解日志含义
#### Scenario: Show readable fields for device allocation and recall logs
- **GIVEN** 一条 `device_allocate``device_recall` 日志包含 `device_virtual_no``device_virtual_nos``device_imei``shop_name``target_shop_name`
- **WHEN** 日志列表渲染该条记录
- **THEN** 页面 MUST 优先展示这些设备和店铺的可读字段
#### Scenario: Show readable fields for device bind and unbind logs
- **GIVEN** 一条 `device_bind_card``device_unbind_card` 日志包含 `device_virtual_no``device_imei``device_sn``iccid`
- **WHEN** 日志列表渲染该条记录
- **THEN** 页面 MUST 直接展示这些可读业务字段
### Requirement: Admin Operation Logs Hide Internal ID Fields
The admin asset operation log view MUST suppress internal ID-like fields from rendered change content.
#### Scenario: Hide internal id fields in change content
- **GIVEN** 某条日志的 `operation_content_before``operation_content_after` 或相关字段说明中包含 `id``*_id``*Id``*ID`
- **WHEN** 日志变更内容被渲染
- **THEN** 这些内部 ID 字段 MUST NOT 出现在界面上
- **AND** `ICCID``IMEI``SN`、设备虚拟号、店铺名称等业务可读标识 MUST 仍然允许显示
#### Scenario: Shared log dialog follows the same hidden-id rule
- **GIVEN** 用户从资产详情页、设备列表页或卡列表页打开后台操作审计日志
- **WHEN** 同一条日志在不同入口被渲染
- **THEN** 各入口 MUST 使用一致的字段过滤规则
- **AND** 各入口 MUST 一致隐藏内部 `xxxid/xxxID` 字段

View File

@@ -0,0 +1,21 @@
## 1. Proposal Review
- [x] 1.1 与产品/需求方确认本提案仅覆盖后台管理端,不包含 C 端范围。
- [ ] 1.2 确认设备实时信息中应保留的非信号字段范围,避免误删其他设备状态信息。
- [x] 1.3 确认操作审计日志中 `xxxid/xxxID` 字段统一隐藏的展示规则。
## 2. Implementation
- [x] 2.1 扩展 admin 资产实时状态相关类型,接收 `device_realtime.signal_quality``device_realtime.signal_bad_reason`
- [x] 2.2 调整后台资产信息页的设备实时信息展示,改为展示后端摘要字段,并移除旧的信号强度推导与原始信号指标展示。
- [x] 2.3 清理或收敛只为旧信号展示服务的前端格式化逻辑,避免残留无效 UI 计算。
- [x] 2.4 调整操作审计日志展示逻辑,优先展示新增可读业务字段。
- [x] 2.5 统一隐藏日志中的 `id` / `*_id` / `*Id` / `*ID` 字段,同时保留 `ICCID``IMEI``SN`、设备虚拟号等业务标识展示。
- [x] 2.6 验证资产详情页与列表页中的操作审计日志弹窗展示一致。
## 3. Verification
- [ ] 3.1 验证 `GET /api/admin/assets/:identifier/realtime-status` 的设备资产返回可正确映射新增字段。
- [ ] 3.2 验证 `POST /api/admin/assets/:identifier/refresh` 刷新后设备实时信息展示一致。
- [ ] 3.3 验证 `card_allocate` / `card_recall` / `device_allocate` / `device_recall` / `device_bind_card` / `device_unbind_card` 的日志展示可直接阅读。
- [ ] 3.4 验证日志中不再显示内部 `xxxid/xxxID` 字段。

View File

@@ -0,0 +1,20 @@
# Change: Add asset links to main wallet transactions
## Why
The agent fund overview detail drawer shows main wallet transaction records, but users cannot identify or navigate to the related asset for deductions tied to asset activity. The backend now returns `asset_identifier` from `GET /api/admin/shops/{shop_id}/main-wallet/transactions`, so the UI should expose it.
## What Changes
- Add an "资产标识" column to the "预充值钱包流水" table inside the agent fund overview commission detail drawer.
- Read and type the new `asset_identifier` response field from main wallet transaction rows.
- Render non-empty asset identifiers as clickable links that navigate to the asset information page for that identifier.
- Render empty asset identifiers as `-` and do not make them clickable.
## Impact
- Affected specs: `commission-management`
- Affected code:
- `src/types/api/commission.ts`
- `src/views/commission-management/agent-fund-overview/index.vue`
- Asset information route query handling is reused via `RoutesAlias.AssetInformation` and `identifier` search behavior.

View File

@@ -0,0 +1,25 @@
## ADDED Requirements
### Requirement: Main Wallet Transaction Asset Identifier Link
The agent fund overview commission detail drawer SHALL display the `asset_identifier` returned by `GET /api/admin/shops/{shop_id}/main-wallet/transactions` in the "预充值钱包流水" table as "资产标识" and allow users to navigate from that value to the asset information page.
#### Scenario: Display asset identifier in main wallet transactions
- **WHEN** a user opens an agent's commission detail drawer
- **AND** selects the "预充值钱包流水" tab
- **AND** a transaction row contains a non-empty `asset_identifier`
- **THEN** the table displays that value in a column labeled "资产标识"
- **AND** the value is visually presented as a clickable link
#### Scenario: Navigate to asset information from asset identifier
- **WHEN** a user clicks a non-empty "资产标识" value in the "预充值钱包流水" table
- **THEN** the system navigates to the asset information page
- **AND** the asset information page searches or loads using the clicked asset identifier
#### Scenario: Empty asset identifier is not clickable
- **WHEN** a main wallet transaction row has no `asset_identifier`
- **THEN** the "资产标识" column displays `-`
- **AND** no navigation action is available for that cell

View File

@@ -0,0 +1,13 @@
## 1. Implementation
- [x] 1.1 Add optional `asset_identifier` to `MainWalletTransactionItem`.
- [x] 1.2 Add an "资产标识" column to the agent fund overview detail drawer's "预充值钱包流水" table.
- [x] 1.3 Render `asset_identifier` as a clickable link when present and `-` when absent.
- [x] 1.4 Navigate clicks to the asset information page using the clicked asset identifier as the search query.
- [x] 1.5 Ensure the click does not trigger unrelated row/table interactions.
## 2. Verification
- [x] 2.1 Verify the changed Vue file with lint or equivalent available project checks.
- [ ] 2.2 Manually verify rows with `asset_identifier` display and navigate correctly.
- [ ] 2.3 Manually verify rows without `asset_identifier` display `-` and are not clickable.

View File

@@ -0,0 +1,20 @@
# Change: 为资产卡信息月内流量字段增加权限控制
## Why
资产信息页卡信息中的“本月已用流量”和“运营商周期月内已用流量”属于敏感用量字段,需要统一通过同一个权限编码控制可见性,并对代理账号和企业账号强制隐藏。
## What Changes
- 为资产信息页卡信息中的“本月已用流量”增加权限控制。
- 为资产信息页卡信息中的“运营商周期月内已用流量”增加相同权限控制。
- 两个字段统一使用权限编码 `asset_info:view_card_month_usage`
- 当当前登录账号 `user_type``3` 代理账号或 `4` 企业账号时,即使拥有权限也不显示这两个字段。
## Impact
- Affected specs:
- `asset-card-usage-field-permission`
- Affected code:
- `src/views/asset-management/asset-information/components/BasicInfoCard.vue`
- 角色/权限树配置中与 `asset_info:view_card_month_usage` 相关的注册位置

View File

@@ -0,0 +1,37 @@
## ADDED Requirements
### Requirement: Asset Card Month Usage Field Permission
The asset information page SHALL gate the card information fields `本月已用流量` and `运营商周期月内已用流量` with the same permission code `asset_info:view_card_month_usage`.
The asset information page MUST hide both fields for logged-in accounts whose `user_type` is `3` (代理账号) or `4` (企业账号), regardless of whether the account has `asset_info:view_card_month_usage`.
#### Scenario: Show both fields when platform user has permission
- **GIVEN** 当前登录账号的 `user_type` 不是 `3` 也不是 `4`
- **AND** 用户拥有 `asset_info:view_card_month_usage`
- **WHEN** 页面渲染资产信息的卡信息区域
- **THEN** 页面 MUST 显示 `本月已用流量`
- **AND** 页面 MUST 显示 `运营商周期月内已用流量`
#### Scenario: Hide both fields when permission is missing
- **GIVEN** 当前登录账号的 `user_type` 不是 `3` 也不是 `4`
- **AND** 用户不拥有 `asset_info:view_card_month_usage`
- **WHEN** 页面渲染资产信息的卡信息区域
- **THEN** 页面 MUST NOT 显示 `本月已用流量`
- **AND** 页面 MUST NOT 显示 `运营商周期月内已用流量`
#### Scenario: Hide both fields for agent accounts
- **GIVEN** 当前登录账号的 `user_type``3`
- **WHEN** 页面渲染资产信息的卡信息区域
- **THEN** 页面 MUST NOT 显示 `本月已用流量`
- **AND** 页面 MUST NOT 显示 `运营商周期月内已用流量`
#### Scenario: Hide both fields for enterprise accounts
- **GIVEN** 当前登录账号的 `user_type``4`
- **WHEN** 页面渲染资产信息的卡信息区域
- **THEN** 页面 MUST NOT 显示 `本月已用流量`
- **AND** 页面 MUST NOT 显示 `运营商周期月内已用流量`

View File

@@ -0,0 +1,6 @@
## 1. Implementation
- [x] 1.1 在资产信息页卡信息中为“本月已用流量”接入 `asset_info:view_card_month_usage` 权限
- [x] 1.2 在资产信息页卡信息中为“运营商周期月内已用流量”接入同一个 `asset_info:view_card_month_usage` 权限
- [x] 1.3 确保 `user_type``3` 代理账号或 `4` 企业账号时隐藏上述两个字段
- [x] 1.4 完成 OpenSpec 严格校验

View File

@@ -0,0 +1,34 @@
# Change: 更新资产运营商状态文案与区间分配数量提示
## Why
当前后台“资产信息”页的“卡信息”区域会把 `gateway_extend` 展示为“运营商停机原因”,但最新业务要求需要统一改为更宽泛的“运营商状态”文案。
同时IoT 卡管理和设备管理里的“批量设置套餐系列绑定”弹窗已经支持区间选择,但在输入起始/结束标识后没有给出本次将覆盖多少张卡或多少台设备的直观提示,运营人员需要手工心算范围,容易误判分配数量。
## What Changes
- 调整资产信息卡资产详情中“卡信息”区域的 `gateway_extend` 展示标签,将“运营商停机原因”改为 `运营商状态`
- 为 IoT 卡“批量设置套餐系列绑定”弹窗的区间模式新增提示文案:`共分配xxx张卡`
- 计算方式为 `结束ICCID - 起始ICCID + 1`
- 仅在起止值可计算且结果为正整数时显示
- 为设备管理“批量设置设备套餐系列绑定”弹窗的区间模式新增提示文案:`共分配xxx台设备`
- 计算方式为 `结束设备号 - 起始设备号 + 1`
- 仅在起止值可计算且结果为正整数时显示
- 不调整后端接口字段,仅补充前端展示与交互约束
## Impact
- Affected specs:
- `asset-information`
- `iot-card-series-binding`
- `device-series-binding`
- Affected code:
- `src/views/asset-management/asset-information/components/BasicInfoCard.vue`
- `src/views/asset-management/iot-card-management/index.vue`
- `src/views/asset-management/device-list/index.vue`
- Dependencies:
- `add-asset-gateway-extend-display`
- `update-batch-series-binding-contract`
- Breaking changes:
- 无外部接口破坏性变更;本次仅调整文案和区间模式提示

View File

@@ -0,0 +1,13 @@
## ADDED Requirements
### Requirement: Admin Asset Card Carrier Status Label
The admin asset information view SHALL label `gateway_extend` as `运营商状态` within the card information section.
#### Scenario: Show carrier status label in card information
- **GIVEN** 用户正在查看后台“资产信息”中的卡资产详情
- **WHEN** 页面渲染“卡信息”区域中的 `gateway_extend`
- **THEN** 字段标签 MUST 显示为 `运营商状态`
- **AND** 字段值 MUST 继续直接展示上游返回的 `gateway_extend`
- **AND** 当 `gateway_extend` 为空时,页面 MUST 显示稳定的占位内容

View File

@@ -0,0 +1,24 @@
## ADDED Requirements
### Requirement: Device Batch Series Binding Range Allocation Hint
The device batch series binding dialog SHALL provide a derived allocation-count hint for device-number range selection.
The hint MUST appear immediately below the `virtual_no_start` and `virtual_no_end` inputs.
The count MUST be computed as `结束设备号 - 起始设备号 + 1` using the same numeric range values accepted by the dialog.
#### Scenario: Show allocation count for a valid device-number range
- **GIVEN** 用户打开“批量设置设备套餐系列绑定”弹窗
- **AND** 用户选择 `selection_type="range"`
- **WHEN** 用户输入 `virtual_no_start``virtual_no_end`,且计算结果为正整数
- **THEN** 弹窗 MUST 显示 `共分配xxx台设备`
- **AND** `xxx` MUST 等于 `结束设备号 - 起始设备号 + 1`
- **AND** 当任一端点变化时,提示 MUST 立即更新
#### Scenario: Hide count hint for incomplete or invalid device-number ranges
- **GIVEN** 用户选择 `selection_type="range"`
- **WHEN** 任一起止设备号为空、无法按有效数值范围计算,或计算结果小于 `1`
- **THEN** 弹窗 MUST NOT 显示误导性的分配数量提示

View File

@@ -0,0 +1,24 @@
## ADDED Requirements
### Requirement: IoT Card Batch Series Binding Range Allocation Hint
The IoT card batch series binding dialog SHALL provide a derived allocation-count hint for ICCID range selection.
The hint MUST appear immediately below the `iccid_start` and `iccid_end` inputs.
The count MUST be computed with exact integer arithmetic as `iccid_end - iccid_start + 1` so that long ICCID values do not lose precision.
#### Scenario: Show allocation count for a valid ICCID range
- **GIVEN** 用户打开“批量设置套餐系列绑定”弹窗
- **AND** 用户选择 `selection_type="range"`
- **WHEN** 用户输入 `iccid_start``iccid_end`,且计算结果为正整数
- **THEN** 弹窗 MUST 显示 `共分配xxx张卡`
- **AND** `xxx` MUST 等于 `iccid_end - iccid_start + 1`
- **AND** 当任一端点变化时,提示 MUST 立即更新
#### Scenario: Hide count hint for incomplete or invalid ICCID ranges
- **GIVEN** 用户选择 `selection_type="range"`
- **WHEN** 任一起止 ICCID 为空、无法按整数计算,或计算结果小于 `1`
- **THEN** 弹窗 MUST NOT 显示误导性的分配数量提示

View File

@@ -0,0 +1,16 @@
## 1. Asset Information
- [x] 1.1 将资产信息“卡信息”区域的 `gateway_extend` 标签从“运营商停机原因”改为 `运营商状态`
- [x] 1.2 确认该字段仍直接展示 `gateway_extend` 原值,并保留空值占位行为
## 2. Range Allocation Hints
- [x] 2.1 在 IoT 卡“批量设置套餐系列绑定”弹窗的区间模式下,于起始/结束 ICCID 输入框下方新增 `共分配xxx张卡` 提示
- [x] 2.2 在设备管理“批量设置设备套餐系列绑定”弹窗的区间模式下,于起始/结束设备号输入框下方新增 `共分配xxx台设备` 提示
- [x] 2.3 让两个提示都随起止值变化实时更新,并在范围不完整、不可计算或结果小于 1 时隐藏
## 3. Verification
- [x] 3.1 验证卡资产详情“卡信息”区域展示标签为 `运营商状态`
- [x] 3.2 验证 ICCID 区间提示按 `结束ICCID - 起始ICCID + 1` 计算且不出现精度丢失
- [x] 3.3 验证设备号区间提示按 `结束设备号 - 起始设备号 + 1` 计算,并在无效范围时不显示误导性数量

View File

@@ -0,0 +1,29 @@
# Change: 更新资产信息当前生效套餐的到期计时基准展示
## Why
资产信息页的“当前生效套餐”依赖 `GET /api/admin/assets/:identifier/current-package` 获取套餐详情。后端现已在该接口中新增返回 `expiry_base` 字段,用于标识套餐到期时间的计时基准,但当前前端侧还没有建立对应的契约与展示规范。
如果不先补齐这份 spec后续实现时容易出现类型未对齐、数据映射丢失或页面根据开始/到期时间自行推断口径的情况,导致运营人员无法直接判断套餐是“实名激活时起算”还是“购买时起算”。
## What Changes
- 为资产信息页“当前生效套餐”补充 `expiry_base` 契约规范:
- `GET /api/admin/assets/:identifier/current-package` 支持返回 `expiry_base`
- 前端当前套餐类型与页面状态需要保留该字段
- 为资产信息页“当前生效套餐”补充“到期计时基准”展示规范:
- `from_activation` 显示为“实名激活时起算”
- `from_purchase` 显示为“购买时起算”
- 字段为空或未知时显示稳定占位内容
- 明确页面必须优先使用后端返回的 `expiry_base`,不得根据 `start_time``expire_time` 等字段自行推断
## Impact
- Affected specs:
- `asset-information`
- Affected code:
- `src/types/api/asset.ts`
- `src/views/asset-management/asset-information/composables/useAssetInfo.ts`
- `src/views/asset-management/asset-information/components/CurrentPackageCard.vue`
- Dependencies:
- `GET /api/admin/assets/:identifier/current-package` 已新增返回 `expiry_base`

View File

@@ -0,0 +1,50 @@
## ADDED Requirements
### Requirement: Admin Current Package Expiry Base Contract
The admin asset information integration SHALL accept and preserve `expiry_base` from `GET /api/admin/assets/:identifier/current-package`.
#### Scenario: Accept additive expiry_base from current-package API
- **GIVEN** 用户正在查看后台“资产信息”中的“当前生效套餐”
- **AND** `GET /api/admin/assets/:identifier/current-package` 返回当前套餐数据
- **WHEN** 响应中包含 `expiry_base`
- **THEN** 前端类型契约 MUST 支持 `expiry_base`
- **AND** 页面状态 MUST 保留该字段原始值
- **AND** 支持的取值 MUST 包含 `from_activation``from_purchase`
#### Scenario: Missing expiry_base does not break current package parsing
- **GIVEN** `GET /api/admin/assets/:identifier/current-package` 返回当前套餐数据
- **AND** 响应中未返回 `expiry_base` 或该字段为空
- **WHEN** 前端解析当前套餐响应
- **THEN** 页面 MUST 继续保留现有套餐字段的解析行为
- **AND** MUST NOT 因缺少 `expiry_base` 导致“当前生效套餐”整体渲染失败
### Requirement: Admin Current Package Expiry Base Display
The admin asset information view SHALL display `expiry_base` as the expiry countdown base in the `当前生效套餐` card and MUST use the backend-provided value instead of inferring from time fields.
#### Scenario: Show activation-based expiry text on current package card
- **GIVEN** 用户正在查看后台“资产信息”中的“当前生效套餐”
- **AND** 当前套餐返回 `expiry_base``from_activation`
- **WHEN** 页面渲染套餐详情表格
- **THEN** 页面 MUST 显示“到期计时基准”
- **AND** 该字段的展示文案 MUST 为“实名激活时起算”
#### Scenario: Show purchase-based expiry text on current package card
- **GIVEN** 用户正在查看后台“资产信息”中的“当前生效套餐”
- **AND** 当前套餐返回 `expiry_base``from_purchase`
- **WHEN** 页面渲染套餐详情表格
- **THEN** 页面 MUST 显示“到期计时基准”
- **AND** 该字段的展示文案 MUST 为“购买时起算”
#### Scenario: Show placeholder when expiry_base is empty or unknown
- **GIVEN** 用户正在查看后台“资产信息”中的“当前生效套餐”
- **AND** 当前套餐的 `expiry_base` 为空、缺失或不是已知取值
- **WHEN** 页面渲染套餐详情表格
- **THEN** 页面 MUST 在“到期计时基准”位置显示稳定占位内容
- **AND** 页面 MUST NOT 根据 `start_time``expire_time` 或其他时间字段自行推断展示文案

View File

@@ -0,0 +1,16 @@
## 1. Contract Alignment
- [x] 1.1 扩展资产当前生效套餐相关类型,支持 `expiry_base`
- [x] 1.2 调整 `useAssetInfo.ts` 中当前生效套餐的数据映射,确保页面状态保留 `expiry_base`
## 2. UI Display
- [x] 2.1 在资产信息页“当前生效套餐”卡片中新增“到期计时基准”展示项
- [x] 2.2 按统一文案映射 `expiry_base``from_activation` -> “实名激活时起算”,`from_purchase` -> “购买时起算”
- [x] 2.3 当 `expiry_base` 为空、缺失或未知时,显示稳定占位内容,不自行推断
## 3. Verification
- [x] 3.1 验证 `current-package` 返回 `expiry_base=from_activation` 时,页面正确显示“实名激活时起算”
- [x] 3.2 验证 `current-package` 返回 `expiry_base=from_purchase` 时,页面正确显示“购买时起算”
- [x] 3.3 验证 `expiry_base` 缺失或为空时,页面仍能正常渲染其余套餐信息且显示占位内容

View File

@@ -0,0 +1,43 @@
# Change: 更新资产信息设备详情绑定卡状态取值来源
## Why
当前后台“资产信息”的设备详情里,`实名状态``在线状态``实名时间` 这三个字段的取值来源不一致:
- `实名状态` 当前优先读取设备绑定卡中的当前卡状态
- `在线状态` 仍读取设备顶层 `online_status`
- `实名时间` 仍读取设备顶层 `real_name_at`
当设备只绑定一张卡,或设备绑定多张卡并发生切卡时,这三个字段可能分别来自不同对象,导致页面展示口径不一致,运营人员无法准确判断“当前使用卡”的实名与联网状态。
后端已经在设备资产详情的 `cards` 列表中返回每张绑定卡的 `real_name_status``network_status``real_name_at``is_current`。前端需要明确统一取值规则,避免继续混用设备顶层字段和绑定卡字段。
## What Changes
- 更新后台“资产信息”的设备详情字段取值规则:
-`cards` 只有一张绑定卡时,`实名状态``在线状态``实名时间` 直接取该卡的 `real_name_status``network_status``real_name_at`
-`cards` 有多张绑定卡时,以上三个字段统一取 `is_current=true` 的绑定卡
- 设备详情这三个字段不再在存在可选绑定卡时混用设备顶层 `real_name_status``online_status``real_name_at`
- 当前端无法选出符合规则的绑定卡时:
- 保留字段标签
- 显示稳定占位内容
- 不得猜测性回退到任意非当前卡
- 更新资产详情绑定卡契约与状态合并规范:
- 绑定卡记录需要支持 `real_name_at`
- 刷新设备实时状态后,不得无故丢失已解析出的绑定卡 `real_name_at`
## Impact
- Affected specs:
- `asset-information`
- Affected code:
- `src/types/api/asset.ts`
- `src/views/asset-management/asset-information/types.ts`
- `src/views/asset-management/asset-information/composables/useAssetInfo.ts`
- `src/views/asset-management/asset-information/components/BasicInfoCard.vue`
- Dependencies:
- 依赖后端继续在设备资产详情 `cards` 列表中返回 `real_name_status``network_status``real_name_at``is_current`
- 与活跃变更 `update-device-activation-status-filter-and-display` 共享 `asset-information` 的设备基础信息展示区域
- 与活跃变更 `update-asset-realtime-sync-timestamps-display` 共享设备绑定卡状态合并逻辑
- Breaking changes:
- 无外部接口 breaking change本次仅调整后台设备详情字段取值口径

View File

@@ -0,0 +1,56 @@
## ADDED Requirements
### Requirement: Admin Device Detail Status Fields Use A Selected Bound Card
The admin asset information view SHALL source the device detail `实名状态`, `在线状态`, and `实名时间` fields from a selected bound card in the device `cards` list instead of mixing device top-level status fields.
#### Scenario: Device detail uses the only bound card when exactly one card is bound
- **GIVEN** 用户正在查看后台“资产信息”中的设备资产详情
- **AND** 设备详情响应中的 `cards` 列表仅包含一条绑定卡记录
- **WHEN** 页面渲染设备基础信息区域
- **THEN** 页面 MUST 将 `实名状态` 映射为这条绑定卡的 `real_name_status`
- **AND** 页面 MUST 将 `在线状态` 映射为这条绑定卡的 `network_status`
- **AND** 页面 MUST 将 `实名时间` 映射为这条绑定卡的 `real_name_at`
- **AND** 页面 MUST NOT 要求这条唯一绑定卡必须带有 `is_current=true` 才可被选中
#### Scenario: Device detail uses the current bound card when multiple cards are bound
- **GIVEN** 用户正在查看后台“资产信息”中的设备资产详情
- **AND** 设备详情响应中的 `cards` 列表包含多条绑定卡记录
- **AND** 其中一条绑定卡记录的 `is_current``true`
- **WHEN** 页面渲染设备基础信息区域
- **THEN** 页面 MUST 将 `实名状态` 映射为该当前绑定卡的 `real_name_status`
- **AND** 页面 MUST 将 `在线状态` 映射为该当前绑定卡的 `network_status`
- **AND** 页面 MUST 将 `实名时间` 映射为该当前绑定卡的 `real_name_at`
- **AND** 页面 MUST NOT 使用任意非当前绑定卡的对应字段填充这三个展示项
- **AND** 页面 MUST NOT 在已选出当前绑定卡后继续混用设备顶层 `real_name_status``online_status``real_name_at`
#### Scenario: Device detail keeps stable placeholders when no eligible bound card can be selected
- **GIVEN** 用户正在查看后台“资产信息”中的设备资产详情
- **AND** `cards` 列表为空,或 `cards` 列表包含多条记录但不存在 `is_current=true` 的绑定卡
- **WHEN** 页面渲染 `实名状态``在线状态``实名时间` 这三个展示项
- **THEN** 页面 MUST 保留这三个字段标签
- **AND** 页面 MUST 显示稳定占位内容
- **AND** 页面 MUST NOT 猜测性地回退到任意非当前绑定卡
### Requirement: Admin Device Bound Card Status Contract Compatibility
The admin asset information integration SHALL accept and preserve the bound-card fields needed by the device detail status source selection rule.
#### Scenario: Device asset detail contract accepts bound-card real-name time
- **GIVEN** `GET /api/admin/assets/resolve/:identifier` 返回设备资产详情
- **AND** 响应中的 `cards` 列表包含每张绑定卡的 `real_name_status``network_status``real_name_at``is_current`
- **WHEN** 前端解析设备资产详情响应
- **THEN** 前端绑定卡类型契约 MUST 支持 `real_name_at`
- **AND** 前端状态模型 MUST 保留每张绑定卡的 `real_name_status``network_status``real_name_at``is_current`
#### Scenario: Realtime merge preserves selected-card real-name time for device detail display
- **GIVEN** 前端已解析设备资产详情,并持有绑定卡的 `real_name_at`
- **AND** `GET /api/admin/assets/:identifier/realtime-status``POST /api/admin/assets/:identifier/refresh` 返回了设备实时状态
- **WHEN** 前端将实时返回的 `cards` 数据与已有绑定卡状态合并
- **THEN** 绑定卡已有的 `real_name_at` MUST NOT 因实时合并而无故丢失
- **AND** 后续设备详情展示 MUST 继续按选中的绑定卡读取 `real_name_at`

View File

@@ -0,0 +1,21 @@
# Tasks: Update Asset Device Detail Bound Card Status Source
## 1. Contract And State
- [x] 1.1 扩展设备资产绑定卡相关类型,支持 `real_name_at` 字段,并与 `real_name_status``network_status``is_current` 一起作为设备详情取值依据。
- [x] 1.2 调整设备资产详情的绑定卡选择逻辑:仅一张卡时直接选中该卡,多张卡时选中 `is_current=true` 的卡。
- [x] 1.3 调整设备实时状态合并逻辑,确保刷新后不会丢失绑定卡已有的 `real_name_at` 等静态字段。
## 2. UI Display
- [x] 2.1 在资产信息页设备详情中,将 `实名状态` 映射为所选绑定卡的 `real_name_status`
- [x] 2.2 在资产信息页设备详情中,将 `在线状态` 映射为所选绑定卡的 `network_status`
- [x] 2.3 在资产信息页设备详情中,将 `实名时间` 映射为所选绑定卡的 `real_name_at`
- [x] 2.4 当不存在符合规则的绑定卡时,保留这三个字段标签并显示稳定占位内容,不回退到任意非当前卡。
## 3. Verification
- [ ] 3.1 验证设备资产详情 `cards` 仅有一条记录时,这三个字段均展示该卡的 `real_name_status``network_status``real_name_at`
- [ ] 3.2 验证设备资产详情 `cards` 存在多条记录且其中一条 `is_current=true` 时,这三个字段均展示当前卡的对应值。
- [ ] 3.3 验证设备实时刷新后,绑定卡 `real_name_at` 未被丢失,且页面仍按同一张选中卡展示三项字段。
- [ ] 3.4 验证不存在符合规则的绑定卡时,页面显示稳定占位内容,且不误用设备顶层 `online_status` 或任意非当前卡值。

View File

@@ -0,0 +1,28 @@
# Change: 资产信息页全页面响应式适配
## Why
资产信息页从资产查询、基础信息、当前生效套餐、套餐列表、钱包流水到操作审计日志包含大量描述项、操作按钮、筛选器和表格。当前小屏幕下存在固定宽度输入框、横向统计项和卡片头部内容挤压的问题,导致页面显示混乱。
## What Changes
- 对资产信息页从搜索框到最底部操作审计日志进行响应式适配。
- 小屏幕下搜索输入、查询、同步、返回上次搜索等操作应换行或纵向排列,避免横向溢出。
- 基础信息、设备实时信息和当前生效套餐描述项应根据屏幕宽度减少列数。
- 当前生效套餐的真/虚流量统计应在小屏下纵向堆叠,并保持进度条和百分比可读。
- 套餐列表、钱包流水、操作审计日志等表格区域应在小屏下使用容器横向滚动承载表格,而不是撑破页面。
- 钱包和操作审计日志的筛选器、分页器、卡片头部操作应在窄屏下换行并占满可用宽度。
- 不改变现有接口、权限、业务数据和操作行为。
## Impact
- Affected specs:
- `asset-information-responsive-layout`
- Affected code:
- `src/views/asset-management/asset-information/index.vue`
- `src/views/asset-management/asset-information/components/AssetSearchCard.vue`
- `src/views/asset-management/asset-information/components/BasicInfoCard.vue`
- `src/views/asset-management/asset-information/components/CurrentPackageCard.vue`
- `src/views/asset-management/asset-information/components/PackageListCard.vue`
- `src/views/asset-management/asset-information/components/WalletTransactionCard.vue`
- `src/views/asset-management/asset-information/components/OperationLogsCard.vue`

View File

@@ -0,0 +1,49 @@
## ADDED Requirements
### Requirement: Asset Information Responsive Layout
The asset information page SHALL provide responsive layouts from the asset search area through the bottom operation audit log area so the page remains readable and operable on desktop, tablet, and mobile-width screens.
The responsive layout MUST NOT change existing API calls, permission checks, emitted events, business calculations, or displayed data semantics.
#### Scenario: Search area fits small screens
- **GIVEN** 用户在小屏幕设备上打开资产信息页
- **WHEN** 页面渲染资产查询区域
- **THEN** 搜索输入框 MUST NOT use a fixed width that exceeds the viewport
- **AND** 查询、同步、返回上次搜索等按钮 MUST wrap or stack without causing page-level horizontal overflow
- **AND** ICCID 放大提示 MUST fit within the viewport width
#### Scenario: Information cards reduce columns on narrow screens
- **GIVEN** 用户在平板或手机宽度打开资产信息页
- **WHEN** 页面渲染基础信息、设备实时信息或当前生效套餐详情
- **THEN** 描述项列数 MUST reduce according to available width
- **AND** long identifiers or status text MUST wrap or truncate within their container instead of pushing the page wider
#### Scenario: Flow usage sections remain readable
- **GIVEN** 用户在小屏幕设备上查看当前生效套餐
- **WHEN** 页面渲染真流量或虚流量使用区域
- **THEN** flow statistic sections MUST stack vertically when horizontal space is insufficient
- **AND** progress bars and percentages MUST remain visible and aligned within the card
#### Scenario: Tables scroll within their card on small screens
- **GIVEN** 用户在小屏幕设备上查看套餐列表、钱包流水或操作审计日志
- **WHEN** table columns exceed the available content width
- **THEN** the table container MUST provide horizontal scrolling
- **AND** the overall page MUST NOT be widened by the table
#### Scenario: Filters and pagination adapt to narrow screens
- **GIVEN** 用户在小屏幕设备上查看钱包流水或操作审计日志
- **WHEN** 页面渲染筛选器或分页器
- **THEN** filter controls MUST wrap or stack within the card
- **AND** pagination MUST remain reachable without overlapping adjacent content
#### Scenario: Desktop layout remains unchanged in behavior
- **GIVEN** 用户在桌面宽度打开资产信息页
- **WHEN** 页面渲染资产信息内容
- **THEN** existing business actions, permissions, table operations, and data displays MUST continue to behave as before

View File

@@ -0,0 +1,10 @@
## 1. Implementation
- [x] 1.1 适配资产查询卡片的输入框、按钮组和放大提示在小屏下的布局
- [x] 1.2 适配资产信息页主容器、卡片间距和卡片头部在小屏下的布局
- [x] 1.3 适配基础信息与设备实时信息描述项在中小屏下的列数和操作按钮换行
- [x] 1.4 适配当前生效套餐流量统计和套餐详情描述项在小屏下的布局
- [x] 1.5 适配套餐列表表格、流量列和分页器在小屏下的横向滚动与换行
- [x] 1.6 适配钱包流水筛选器、表格和分页器在小屏下的布局
- [x] 1.7 适配操作审计日志筛选器、表格和分页器在小屏下的布局
- [x] 1.8 完成 OpenSpec 严格校验

View File

@@ -0,0 +1,36 @@
# Change: 更新资产详情套餐流量展示口径
## Why
当前资产详情页中的“当前生效套餐”和“套餐列表”已经接入 `real_total_mb``real_used_mb``virtual_used_mb``enable_virtual_data`,但非平台账号的展示口径与最新业务说明不一致:
- 总量应始终使用 `real_total_mb`
- 已使用应按 `enable_virtual_data``virtual_used_mb``real_used_mb` 之间切换
- 剩余与进度条应基于同一套已使用值重新计算
如果不统一这套规则,代理账号和企业账号会在同一资产详情页内看到错误的剩余流量和进度占比,影响运营判断。
## What Changes
- 新增资产详情套餐流量展示规范,覆盖:
- “当前生效套餐”卡片
- “套餐列表”中的流量列
- 明确非平台/非超级管理员账号(`user_type` 不为 `1`/`2`)的展示规则:
- 总量使用 `real_total_mb`
- 已使用在 `enable_virtual_data=true` 时使用 `virtual_used_mb`,否则使用 `real_used_mb`
- 剩余 = 总量 - 已使用
- 进度条 = 已使用 / 总量
- 明确超级管理员和平台用户(`user_type``1`/`2`)继续保留当前的真流量/虚流量双口径展示
- 不修改后端接口路径、响应字段或权限模型
## Impact
- Affected specs:
- `asset-package-traffic-display`
- Affected code:
- `src/views/asset-management/asset-information/components/CurrentPackageCard.vue`
- `src/views/asset-management/asset-information/components/PackageListCard.vue`
- `src/views/asset-management/asset-information/composables/useAssetInfo.ts`
- Dependencies:
- `GET /api/admin/assets/:identifier/current-package`
- 资产详情页套餐列表接口继续返回 `real_total_mb``real_used_mb``virtual_used_mb``enable_virtual_data`

View File

@@ -0,0 +1,57 @@
## ADDED Requirements
### Requirement: Non-Platform Asset Package Traffic Display
The asset information page SHALL use a unified traffic display formula for non-platform users in both the current package card and the package list.
#### Scenario: Current package uses virtual usage when virtual data is enabled
- **GIVEN** 当前登录账号的 `user_type` 不是 `1``2`
- **AND** “当前生效套餐”返回的 `enable_virtual_data``true`
- **WHEN** 页面渲染“当前生效套餐”的流量信息
- **THEN** 系统 MUST 使用 `real_total_mb` 作为总量
- **AND** 系统 MUST 使用 `virtual_used_mb` 作为已使用
- **AND** 系统 MUST 按 `real_total_mb - virtual_used_mb` 计算剩余
- **AND** 系统 MUST 按 `virtual_used_mb / real_total_mb` 计算进度条百分比
#### Scenario: Current package uses real usage when virtual data is disabled
- **GIVEN** 当前登录账号的 `user_type` 不是 `1``2`
- **AND** “当前生效套餐”返回的 `enable_virtual_data``false`
- **WHEN** 页面渲染“当前生效套餐”的流量信息
- **THEN** 系统 MUST 使用 `real_total_mb` 作为总量
- **AND** 系统 MUST 使用 `real_used_mb` 作为已使用
- **AND** 系统 MUST 按 `real_total_mb - real_used_mb` 计算剩余
- **AND** 系统 MUST 按 `real_used_mb / real_total_mb` 计算进度条百分比
#### Scenario: Package list uses virtual usage when virtual data is enabled
- **GIVEN** 当前登录账号的 `user_type` 不是 `1``2`
- **AND** 套餐列表中的某条记录 `enable_virtual_data``true`
- **WHEN** 页面渲染该条记录的流量信息
- **THEN** 系统 MUST 使用 `real_total_mb` 作为总量
- **AND** 系统 MUST 使用 `virtual_used_mb` 作为已使用
- **AND** 系统 MUST 按 `real_total_mb - virtual_used_mb` 计算剩余
- **AND** 系统 MUST 按 `virtual_used_mb / real_total_mb` 计算进度条百分比
#### Scenario: Package list uses real usage when virtual data is disabled
- **GIVEN** 当前登录账号的 `user_type` 不是 `1``2`
- **AND** 套餐列表中的某条记录 `enable_virtual_data``false`
- **WHEN** 页面渲染该条记录的流量信息
- **THEN** 系统 MUST 使用 `real_total_mb` 作为总量
- **AND** 系统 MUST 使用 `real_used_mb` 作为已使用
- **AND** 系统 MUST 按 `real_total_mb - real_used_mb` 计算剩余
- **AND** 系统 MUST 按 `real_used_mb / real_total_mb` 计算进度条百分比
### Requirement: Platform And Super Admin Package Traffic Detail
The asset information page SHALL preserve the existing dual-track traffic display for super admins and platform users.
#### Scenario: Platform-side users keep real and virtual traffic breakdown
- **GIVEN** 当前登录账号的 `user_type``1``2`
- **WHEN** 页面渲染“当前生效套餐”或“套餐列表”的流量信息
- **THEN** 系统 MUST 继续展示真流量统计
- **AND** 系统 MUST 继续展示虚流量统计
- **AND** 系统 MUST NOT 将该类账号改为单一的“总量 / 已使用 / 剩余”展示口径

View File

@@ -0,0 +1,8 @@
## 1. Implementation
- [x] 1.1 调整 `CurrentPackageCard.vue` 中非平台/非超级管理员视角的流量展示与进度计算。
- [x] 1.2 调整 `PackageListCard.vue` 中非平台/非超级管理员视角的流量展示与进度计算。
- [x] 1.3 对齐 `useAssetInfo.ts` 中由当前生效套餐推导出的流量统计,避免同页展示口径不一致。
- [x] 1.4 验证 `enable_virtual_data=true` 时,代理账号和企业账号的总量、已使用、剩余、进度条均符合文档。
- [x] 1.5 验证 `enable_virtual_data=false` 时,代理账号和企业账号的总量、已使用、剩余、进度条均符合文档。
- [x] 1.6 验证超级管理员与平台用户继续保留真流量/虚流量双口径展示。

View File

@@ -0,0 +1,19 @@
# Change: 统一资产套餐流量展示文案
## Why
资产信息页的当前生效套餐和套餐列表对同一虚流量数据使用了不一致且容易造成误解的展示文案。需要按业务术语统一为更通用的流量用量表述。
## What Changes
- 将“当前生效套餐”中虚流量统计区域的标题从“虚流量使用”调整为“流量用量”。
- 将“套餐列表”流量字段中虚流量已用值的标签从“已使用虚流量”调整为“已使用流量”。
- 保持现有流量字段、数值计算、进度条、可见性权限和接口契约不变。
## Impact
- Affected specs: `asset-package-traffic-display`
- Affected code:
- `src/views/asset-management/asset-information/components/CurrentPackageCard.vue`
- `src/views/asset-management/asset-information/components/PackageListCard.vue`
- Dependencies: `update-asset-package-traffic-threshold-layout` 中定义的套餐流量展示能力

View File

@@ -0,0 +1,19 @@
## ADDED Requirements
### Requirement: Asset Package Traffic Terminology
The asset information page SHALL use the approved traffic terminology while retaining the existing virtual traffic data source and display behavior.
#### Scenario: Current package displays the traffic usage heading
- **GIVEN** 当前生效套餐的虚流量统计区域可见
- **WHEN** 页面渲染该统计区域
- **THEN** 系统 MUST 将区域标题显示为“流量用量”
- **AND** 系统 MUST NOT 显示“虚流量使用”作为该区域标题
#### Scenario: Package list displays the used traffic label
- **GIVEN** 套餐列表记录的虚流量统计区域可见
- **WHEN** 页面渲染该记录的已用流量值
- **THEN** 系统 MUST 将字段标签显示为“已使用流量”
- **AND** 系统 MUST NOT 显示“已使用虚流量”作为该字段标签

View File

@@ -0,0 +1,5 @@
## 1. Implementation
- [x] 1.1 将当前生效套餐虚流量统计区域标题更新为“流量用量”。
- [x] 1.2 将套餐列表虚流量已用字段标签更新为“已使用流量”。
- [x] 1.3 验证文案变更不影响现有流量数值、进度条和权限可见性。

View File

@@ -0,0 +1,27 @@
# Change: 调整资产套餐真虚流量展示布局
## Why
资产信息页“当前生效套餐”和“套餐列表”目前将虚流量停机阈值及其断点标记放在虚流量使用区块,而真流量剩余未直接关联停机阈值。业务需要让停机阈值和断点与真流量剩余在同一区域展示,同时使虚流量区块完整呈现其基于真流量总量计算的总量和剩余。
## What Changes
- 在“当前生效套餐”和“套餐列表”的管理员/平台双流量视图中,将“虚流量停机阈值”展示项移至“真流量剩余”之后,继续显示现有的 `virtual_total_mb` 值。
- 将虚流量停机阈值对应的断点标记从虚流量进度条移至真流量进度条;断点位置继续按 `virtual_total_mb / real_total_mb` 计算。
- 在“虚流量使用”区块的“已使用”后新增:
- “总量”:显示真流量总量 `real_total_mb`
- “剩余”:按 `real_total_mb - virtual_used_mb` 计算。
- 保持真流量和虚流量进度条的现有计算口径、字段来源、权限控制及非管理员单一流量展示不变。
- 不修改后端接口、响应字段或数据模型。
## Impact
- Affected specs:
- `asset-package-traffic-display`
- `asset-package-traffic-permissions`
- Affected code:
- `src/views/asset-management/asset-information/components/CurrentPackageCard.vue`
- `src/views/asset-management/asset-information/components/PackageListCard.vue`
- Dependencies:
- 继续使用现有套餐流量字段:`real_total_mb``real_used_mb``virtual_used_mb``virtual_total_mb`
- 继续使用权限 `asset_info:view_current_package_real_usage``asset_info:view_current_package_virtual_usage`

View File

@@ -0,0 +1,55 @@
## ADDED Requirements
### Requirement: Virtual Traffic Threshold Aligns With Real Traffic Remaining
For super admins and platform users, the asset information page SHALL display the virtual traffic stop threshold immediately after real traffic remaining in both the `当前生效套餐` card and each applicable `套餐列表` row.
The displayed stop-threshold value MUST continue to use `virtual_total_mb`. The corresponding breakpoint marker MUST render on the real traffic progress bar and MUST continue to calculate its position as `virtual_total_mb / real_total_mb`.
#### Scenario: Current package shows threshold and breakpoint with real traffic
- **GIVEN** 当前登录账号的 `user_type``1``2`
- **AND** 当前套餐可展示真流量和虚流量使用信息
- **WHEN** 页面渲染“当前生效套餐”的流量信息
- **THEN** 系统 MUST 在真流量剩余后展示虚流量停机阈值
- **AND** 停机阈值 MUST 显示 `virtual_total_mb` 的现有格式化值
- **AND** 系统 MUST 将停机阈值断点标记渲染在真流量进度条
- **AND** 断点位置 MUST 按 `virtual_total_mb / real_total_mb` 计算
- **AND** 系统 MUST NOT 在虚流量进度条渲染该断点标记
#### Scenario: Package list shows threshold and breakpoint with real traffic
- **GIVEN** 当前登录账号的 `user_type``1``2`
- **AND** 套餐列表中的记录可展示真流量和虚流量使用信息
- **WHEN** 页面渲染该记录的流量信息
- **THEN** 系统 MUST 在真流量剩余后展示虚流量停机阈值
- **AND** 停机阈值 MUST 显示该记录的 `virtual_total_mb` 的现有格式化值
- **AND** 系统 MUST 将该记录的停机阈值断点标记渲染在真流量进度条
- **AND** 断点位置 MUST 按该记录的 `virtual_total_mb / real_total_mb` 计算
- **AND** 系统 MUST NOT 在虚流量进度条渲染该断点标记
### Requirement: Virtual Traffic Usage Displays Real Total And Remaining
For super admins and platform users, the `虚流量使用` section on the asset information page SHALL display virtual traffic used, real traffic total, and remaining virtual traffic capacity in that order in both the `当前生效套餐` card and each applicable `套餐列表` row.
The total MUST use `real_total_mb`. The remaining value MUST be calculated as `real_total_mb - virtual_used_mb`.
#### Scenario: Current package virtual traffic shows total and remaining
- **GIVEN** 当前登录账号的 `user_type``1``2`
- **AND** 当前套餐可展示虚流量使用信息
- **WHEN** 页面渲染“当前生效套餐”的虚流量使用区块
- **THEN** 系统 MUST 在虚流量已使用后展示总量
- **AND** 总量 MUST 显示 `real_total_mb` 的现有格式化值
- **AND** 系统 MUST 在总量后展示剩余
- **AND** 剩余 MUST 显示 `real_total_mb - virtual_used_mb` 的现有格式化值
#### Scenario: Package list virtual traffic shows total and remaining
- **GIVEN** 当前登录账号的 `user_type``1``2`
- **AND** 套餐列表中的记录可展示虚流量使用信息
- **WHEN** 页面渲染该记录的虚流量使用区块
- **THEN** 系统 MUST 在虚流量已使用后展示总量
- **AND** 总量 MUST 显示该记录的 `real_total_mb` 的现有格式化值
- **AND** 系统 MUST 在总量后展示剩余
- **AND** 剩余 MUST 显示 `real_total_mb - virtual_used_mb` 的现有格式化值

View File

@@ -0,0 +1,22 @@
## ADDED Requirements
### Requirement: Traffic Threshold And Virtual Statistics Follow Existing Usage Permissions
The asset information page SHALL apply the existing real and virtual traffic usage permissions to the relocated virtual traffic stop threshold, breakpoint marker, and added virtual traffic statistics.
#### Scenario: Hidden real traffic usage also hides relocated threshold and breakpoint
- **GIVEN** 当前登录账号的 `user_type``1``2`
- **AND** 账号缺少 `asset_info:view_current_package_real_usage` 权限
- **WHEN** 页面渲染“当前生效套餐”或“套餐列表”的套餐流量信息
- **THEN** 系统 MUST NOT 显示真流量统计
- **AND** 系统 MUST NOT 显示位于真流量区域的虚流量停机阈值
- **AND** 系统 MUST NOT 显示位于真流量进度条的停机阈值断点标记
#### Scenario: Hidden virtual traffic usage also hides added virtual statistics
- **GIVEN** 当前登录账号的 `user_type``1``2`
- **AND** 账号缺少 `asset_info:view_current_package_virtual_usage` 权限
- **WHEN** 页面渲染“当前生效套餐”或“套餐列表”的套餐流量信息
- **THEN** 系统 MUST NOT 显示虚流量使用区块
- **AND** 系统 MUST NOT 显示虚流量区块中的已使用、总量或剩余统计

View File

@@ -0,0 +1,16 @@
## 1. Implementation
- [x] 1.1 调整“当前生效套餐”真流量统计项,在真流量剩余后展示虚流量停机阈值,并保持 `virtual_total_mb` 的现有值格式化规则。
- [x] 1.2 将“当前生效套餐”的停机阈值断点标记移至真流量进度条,继续按 `virtual_total_mb / real_total_mb` 定位。
- [x] 1.3 调整“当前生效套餐”虚流量统计项,在已使用后展示真流量总量和按 `real_total_mb - virtual_used_mb` 计算的剩余量。
- [x] 1.4 对套餐列表的每条管理员/平台套餐记录应用相同的阈值、断点和虚流量统计布局。
- [x] 1.5 保持非管理员单一流量展示、现有权限可见性和各进度条百分比计算不变。
## 2. Verification
- [x] 2.1 验证“当前生效套餐”中停机阈值显示在真流量剩余后,且其值仍为 `virtual_total_mb`
- [x] 2.2 验证“当前生效套餐”中断点显示在真流量进度条,且其位置仍按 `virtual_total_mb / real_total_mb` 计算。
- [x] 2.3 验证“当前生效套餐”虚流量使用依次显示已使用、总量(`real_total_mb`)和剩余(`real_total_mb - virtual_used_mb`)。
- [x] 2.4 验证套餐列表的管理员/平台套餐记录具有与当前生效套餐一致的展示布局和计算结果。
- [x] 2.5 验证真流量或虚流量查看权限缺失时,不展示该权限对应的统计项及关联阈值/断点内容。
- [x] 2.6 验证非管理员账号继续使用现有单一流量展示,且真流量与虚流量进度百分比不变。

View File

@@ -0,0 +1,40 @@
# Change: 更新后台资产信息同步时间展示
## Why
当前后台“资产信息”页虽然已经展示卡状态、实名状态和部分实时信息,但还缺少运营判断数据新鲜度所需的三个时间字段:实名同步时间、卡状态同步时间、流量同步时间。
- 卡资产的 `GET /api/admin/assets/:identifier/realtime-status` 已在顶层返回 `last_real_name_check_at``last_card_status_check_at``last_data_check_at`,但前端未展示。
- 设备资产的同一接口会在 `cards` 列表中按绑定卡返回这三个字段;当前“设备实时信息”区块只消费 `device_realtime`,无法展示当前使用卡的同步时间。
如果不补齐这三个字段,后台在卡页和设备页都无法快速确认上游同步是否及时,也不利于定位实名、停复机和流量状态的延迟问题。
## What Changes
- 更新后台“资产信息”的卡资产“卡信息”区域:
- 新增 `实名同步时间`,映射 `last_real_name_check_at`
- 新增 `卡状态同步时间`,映射 `last_card_status_check_at`
- 新增 `流量同步时间`,映射 `last_data_check_at`
- 更新后台“资产信息”的设备资产“设备实时信息”区域:
- 直接读取设备实时接口 `data` 顶层的 `last_real_name_check_at``last_card_status_check_at``last_data_check_at`
- 当对应字段为空时,显示稳定占位内容
- 更新前端资产实时状态契约和数据合并逻辑:
- 支持卡资产顶层这三个同步时间字段
- 支持设备绑定卡记录中的这三个同步时间字段
- 刷新后合并绑定卡实时数据时,不得丢失这三个字段
## Impact
- Affected specs:
- `asset-information`
- Affected code:
- `src/types/api/asset.ts`
- `src/views/asset-management/asset-information/types.ts`
- `src/views/asset-management/asset-information/composables/useAssetInfo.ts`
- `src/views/asset-management/asset-information/components/BasicInfoCard.vue`
- Dependencies:
- 与活跃变更 `update-admin-asset-device-signal-and-audit-logs` 共享 `asset-information` 能力和 `BasicInfoCard.vue` / `useAssetInfo.ts` 文件范围
- 与活跃变更 `update-order-upload-and-device-switch-mode` 共享设备实时信息区块
- 依赖后端继续按当前接口返回这三个时间字段,无需新增接口
- Breaking changes:
- 无外部接口 breaking change本次仅为前端展示契约和页面展示的增量调整

View File

@@ -0,0 +1,64 @@
## ADDED Requirements
### Requirement: Admin Asset Card Sync Timestamp Display
The admin asset information view SHALL display realtime sync timestamps for card assets within the card information section.
#### Scenario: Show card sync timestamps on card asset page
- **GIVEN** 用户正在查看后台“资产信息”中的卡资产详情
- **AND** `GET /api/admin/assets/:identifier/realtime-status``POST /api/admin/assets/:identifier/refresh` 返回了卡资产实时数据
- **WHEN** 页面渲染“卡信息”区域或完成实时刷新后的状态更新
- **THEN** 页面 MUST 显示 `实名同步时间`,并映射 `last_real_name_check_at`
- **AND** 页面 MUST 显示 `卡状态同步时间`,并映射 `last_card_status_check_at`
- **AND** 页面 MUST 显示 `流量同步时间`,并映射 `last_data_check_at`
- **AND** 这三个字段的时间格式 MUST 与页面其他时间字段保持一致
#### Scenario: Card sync timestamps show placeholders when unavailable
- **GIVEN** 用户正在查看后台“资产信息”中的卡资产详情
- **WHEN** 任一同步时间字段为 `null`、空字符串或接口未返回对应值
- **THEN** 页面 MUST 保留对应字段标签
- **AND** 页面 MUST 为该字段显示稳定的占位内容
### Requirement: Admin Device Realtime Sync Timestamp Display
The admin asset information view SHALL display device realtime sync timestamps from top-level realtime response fields within the device realtime information section for device assets.
#### Scenario: Show device realtime sync timestamps in device realtime information
- **GIVEN** 用户正在查看后台“资产信息”中的设备资产详情
- **AND** `GET /api/admin/assets/:identifier/realtime-status``POST /api/admin/assets/:identifier/refresh` 返回了设备资产实时数据
- **WHEN** 页面渲染“设备实时信息”区域或完成实时刷新后的状态更新
- **THEN** 页面 MUST 显示 `实名同步时间`,并映射顶层 `last_real_name_check_at`
- **AND** 页面 MUST 显示 `卡状态同步时间`,并映射顶层 `last_card_status_check_at`
- **AND** 页面 MUST 显示 `流量同步时间`,并映射顶层 `last_data_check_at`
- **AND** 页面 MUST NOT 要求这三个字段必须存在于 `device_realtime` 中才可展示
#### Scenario: Device realtime section keeps stable placeholders when top-level timestamps are empty
- **GIVEN** 用户正在查看后台“资产信息”中的设备资产详情
- **WHEN** 顶层 `last_real_name_check_at``last_card_status_check_at``last_data_check_at` 中任一字段为空
- **THEN** 页面 MUST 保留 `实名同步时间``卡状态同步时间``流量同步时间` 这三个字段标签
- **AND** 页面 MUST 为缺失值显示稳定的占位内容
### Requirement: Admin Asset Realtime Sync Timestamp Contract Compatibility
The admin asset information integration SHALL accept and preserve additive sync timestamp fields from realtime responses for card assets and device bound cards.
#### Scenario: Card realtime contract accepts additive sync timestamp fields
- **GIVEN** `GET /api/admin/assets/:identifier/realtime-status``POST /api/admin/assets/:identifier/refresh` 返回卡资产实时数据
- **WHEN** 前端解析实时状态响应
- **THEN** 前端类型契约 MUST 支持顶层 `last_data_check_at`
- **AND** 前端类型契约 MUST 支持顶层 `last_real_name_check_at`
- **AND** 前端类型契约 MUST 支持顶层 `last_card_status_check_at`
#### Scenario: Device bound-card merge preserves sync timestamp fields
- **GIVEN** `GET /api/admin/assets/:identifier/realtime-status``POST /api/admin/assets/:identifier/refresh` 返回设备资产实时数据
- **AND** `cards` 列表中的绑定卡记录包含 `last_data_check_at``last_real_name_check_at``last_card_status_check_at`
- **WHEN** 前端将实时绑定卡数据与已有设备绑定卡状态合并
- **THEN** 每条绑定卡记录 MUST 保留各自的三个同步时间字段
- **AND** 绑定卡同步时间字段 MAY 被其他设备卡展示或后续状态使用
- **AND** 合并过程 MUST NOT 因刷新而无故丢失已返回的同步时间值

View File

@@ -0,0 +1,20 @@
# Tasks: Update Asset Realtime Sync Timestamps Display
## 1. Contract And State
- [x] 1.1 扩展资产实时状态相关类型,支持卡资产顶层和设备绑定卡记录中的 `last_data_check_at``last_real_name_check_at``last_card_status_check_at`
- [x] 1.2 调整资产实时状态合并逻辑,确保设备绑定卡刷新后保留上述三个同步时间字段。
- [x] 1.3 为设备页补充设备实时状态顶层同步时间的读取逻辑,避免“设备实时信息”仅依赖 `device_realtime`
## 2. UI Display
- [x] 2.1 在卡资产“卡信息”区域新增 `实名同步时间``卡状态同步时间``流量同步时间` 三个展示项。
- [x] 2.2 在设备资产“设备实时信息”区域新增同名三个展示项,并展示设备实时状态顶层返回的对应时间。
- [x] 2.3 当字段为空或实时接口未返回对应值时,统一显示稳定占位内容。
## 3. Verification
- [ ] 3.1 验证卡资产 `GET /api/admin/assets/:identifier/realtime-status` 返回的三个同步时间可正确展示在“卡信息”区域。
- [ ] 3.2 验证设备资产 `GET /api/admin/assets/:identifier/realtime-status` 返回的顶层三个同步时间可正确展示在“设备实时信息”区域。
- [ ] 3.3 验证 `POST /api/admin/assets/:identifier/refresh` 后卡页和设备页的三个同步时间展示保持一致。
- [x] 3.4 运行 `openspec validate update-asset-realtime-sync-timestamps-display --strict`

View File

@@ -0,0 +1,37 @@
# Change: 更新资产解析流量汇总展示
## Why
后台“资产信息”页需要展示当前世代下所有套餐的虚流量已用量和剩余量,方便运营人员在查看资产基础信息时快速判断整体流量消耗情况。
后端通过 `GET /api/admin/assets/resolve/:identifier` 新增可选查询参数控制是否返回汇总字段;前端需要明确请求契约、类型契约和展示位置,避免默认查询缺失汇总数据。
## What Changes
- 更新资产解析接口请求契约:
- `GET /api/admin/assets/resolve/:identifier` 支持可选 Query 参数 `include_usage_summary: boolean`
- 参数含义为是否返回当前世代流量汇总字段 `total_virtual_used_mb` / `total_virtual_remaining_mb`
- 后端未传时默认不返回汇总数据;后台“资产信息”页发起解析请求时默认传 `include_usage_summary=true`
- 更新资产解析接口响应契约:
- 新增 `total_virtual_used_mb?: number | null`,表示所有套餐已用量(MB),未请求时为 `null`
- 新增 `total_virtual_remaining_mb?: number | null`,表示所有套餐剩余量(MB),未请求时为 `null`
- 更新后台“资产信息”基础信息展示:
- 卡资产在“运营商状态”字段后新增“所有套餐已用量”和“所有套餐剩余量”
- 设备资产在“SN”字段后新增“所有套餐已用量”和“所有套餐剩余量”
- 字段值使用上述两个响应字段,并按流量大小格式化展示
- 字段为空或未请求时显示稳定占位内容
## Impact
- Affected specs:
- `asset-information`
- Affected code:
- `src/api/modules/asset.ts`
- `src/types/api/asset.ts`
- `src/views/asset-management/asset-information/types.ts`
- `src/views/asset-management/asset-information/composables/useAssetInfo.ts`
- `src/views/asset-management/asset-information/components/BasicInfoCard.vue`
- Dependencies:
- `GET /api/admin/assets/resolve/:identifier` 后端支持 `include_usage_summary` Query 参数及响应字段
- Breaking changes:
- 无;新增请求参数和响应字段均为可选,前端资产信息页仅主动请求并展示新增数据

View File

@@ -0,0 +1,60 @@
## ADDED Requirements
### Requirement: Admin Asset Resolve Usage Summary Contract
The admin asset information integration SHALL request usage summary data from `GET /api/admin/assets/resolve/:identifier` by default and preserve the returned current-generation virtual usage summary fields.
#### Scenario: Asset information requests usage summary by default
- **GIVEN** 用户在后台“资产信息”页查询资产标识符
- **WHEN** 前端调用 `GET /api/admin/assets/resolve/:identifier`
- **THEN** 请求 Query MUST include `include_usage_summary=true`
- **AND** `include_usage_summary` MUST be treated as a boolean query parameter
#### Scenario: Preserve usage summary fields from resolve response
- **GIVEN** `GET /api/admin/assets/resolve/:identifier?include_usage_summary=true` 返回 `total_virtual_used_mb``total_virtual_remaining_mb`
- **WHEN** 前端解析资产详情响应
- **THEN** 页面状态 MUST preserve `total_virtual_used_mb` as a `number | null` field
- **AND** 页面状态 MUST preserve `total_virtual_remaining_mb` as a `number | null` field
- **AND** `total_virtual_used_mb` MUST represent 所有套餐已用量(MB)
- **AND** `total_virtual_remaining_mb` MUST represent 所有套餐剩余量(MB)
#### Scenario: Usage summary not requested
- **GIVEN** a caller invokes `GET /api/admin/assets/resolve/:identifier` without `include_usage_summary=true`
- **WHEN** the response is parsed
- **THEN** `total_virtual_used_mb` MAY be absent or `null`
- **AND** `total_virtual_remaining_mb` MAY be absent or `null`
### Requirement: Admin Asset Usage Summary Display
The admin asset information view SHALL display current-generation virtual usage summary fields in both card and device asset information sections using asset-type-specific placement.
#### Scenario: Show card usage summary after carrier status
- **GIVEN** 用户正在查看后台“资产信息”中的卡资产详情
- **AND** 当前资产详情状态中存在 `total_virtual_used_mb``total_virtual_remaining_mb`
- **WHEN** 页面渲染“卡信息”区域
- **THEN** 页面 MUST keep `运营商状态` visible
- **AND** 页面 MUST display `所有套餐已用量` immediately after `运营商状态`, using `total_virtual_used_mb`
- **AND** 页面 MUST display `所有套餐剩余量` immediately after `所有套餐已用量`, using `total_virtual_remaining_mb`
- **AND** both values MUST be formatted as traffic sizes based on MB values
#### Scenario: Show device usage summary after SN
- **GIVEN** 用户正在查看后台“资产信息”中的设备资产详情
- **AND** 当前资产详情状态中存在 `total_virtual_used_mb``total_virtual_remaining_mb`
- **WHEN** 页面渲染“设备信息”区域
- **THEN** 页面 MUST keep `SN` visible
- **AND** 页面 MUST display `所有套餐已用量` immediately after `SN`, using `total_virtual_used_mb`
- **AND** 页面 MUST display `所有套餐剩余量` immediately after `所有套餐已用量`, using `total_virtual_remaining_mb`
- **AND** both values MUST be formatted as traffic sizes based on MB values
#### Scenario: Show placeholder when usage summary is unavailable
- **GIVEN** 用户正在查看后台“资产信息”中的卡资产详情或设备资产详情
- **AND** `total_virtual_used_mb` or `total_virtual_remaining_mb` is `null` or absent
- **WHEN** 页面渲染对应汇总字段
- **THEN** 页面 MUST display `-` for the unavailable field
- **AND** 页面 MUST NOT display `0` solely because the field is unavailable

View File

@@ -0,0 +1,19 @@
## 1. Contract
- [x] 1.1 扩展资产解析接口方法,支持传入 `include_usage_summary` Query 参数。
- [x] 1.2 扩展资产解析响应类型,新增 `total_virtual_used_mb?: number | null``total_virtual_remaining_mb?: number | null`
- [x] 1.3 确保后台“资产信息”页调用 `GET /api/admin/assets/resolve/:identifier` 时默认传 `include_usage_summary=true`
## 2. State And Display
- [x] 2.1 在资产信息页面状态中保留 `total_virtual_used_mb``total_virtual_remaining_mb`
- [x] 2.2 在卡资产“卡信息”区域的“运营商状态”后新增“所有套餐已用量”和“所有套餐剩余量”。
- [x] 2.3 在设备资产“设备信息”区域的“SN”后新增“所有套餐已用量”和“所有套餐剩余量”。
- [x] 2.4 新增字段按流量大小格式化展示;当响应字段为 `null``undefined` 或未请求时显示 `-`
## 3. Verification
- [ ] 3.1 验证资产信息页请求解析接口时 URL 包含 `include_usage_summary=true`
- [ ] 3.2 验证解析接口返回两个汇总字段时,卡资产详情在“运营商状态”后正确展示。
- [ ] 3.3 验证解析接口返回两个汇总字段时设备资产详情在“SN”后正确展示。
- [ ] 3.4 验证两个汇总字段为 `null` 或缺失时,页面显示 `-` 且不报错。

View File

@@ -0,0 +1,43 @@
# Change: 为 IoT 卡管理和设备管理新增生效套餐筛选
## Why
当前后台的 IoT 卡管理页和设备管理页都缺少“是否有生效中的套餐”搜索条件,运营人员无法直接按资产当前是否存在生效中的套餐快速筛选目标记录。
本次需求要求两个列表统一新增该筛选能力,并通过查询参数 `has_active_package` 向后端传递筛选条件,避免用户依赖列表详情或手工比对来判断。
## What Changes
- 在 IoT 卡管理页搜索栏新增 `是否有生效中的套餐` 筛选项:
- 使用“是 / 否”布尔选项
-`GET /api/admin/iot-cards/standalone` 提交可选查询参数 `has_active_package`
- 未选择时不提交该参数
- 在设备管理页搜索栏新增 `是否有生效中的套餐` 筛选项:
- 使用“是 / 否”布尔选项
-`GET /api/admin/devices` 提交可选查询参数 `has_active_package`
- 未选择时不提交该参数
- 补齐前端类型契约与搜索状态管理:
- `StandaloneCardQueryParams` 支持 `has_active_package`
- `DeviceQueryParams` 支持 `has_active_package`
- 搜索、重置、翻页、刷新流程中正确保留或清空该条件
- 明确 `false` 是有效筛选值:
- 选择“否”时必须提交 `has_active_package=false`
- 前端不得因为通用空值清理或 `|| undefined` 之类的写法丢掉该参数
## Impact
- Affected specs:
- `iot-card-management`
- `device-management`
- Affected code:
- `src/types/api/card.ts`
- `src/types/api/device.ts`
- `src/api/modules/card.ts`
- `src/api/modules/device.ts`
- `src/views/asset-management/iot-card-management/index.vue`
- `src/views/asset-management/device-list/index.vue`
- Dependencies:
- `GET /api/admin/iot-cards/standalone`
- `GET /api/admin/devices`
- Breaking changes:
-

View File

@@ -0,0 +1,44 @@
## ADDED Requirements
### Requirement: Device Active Package Query Contract
The device management list integration SHALL support filtering records by whether the device currently has an active package via the query parameter `has_active_package`.
#### Scenario: Query devices with active package
- **GIVEN** 用户正在后台设备管理页查询列表
- **WHEN** 用户在 `是否有生效中的套餐` 中选择“是”并执行搜索
- **THEN** 系统 MUST 调用 `GET /api/admin/devices`
- **AND** 请求 MUST 提交 `has_active_package=true`
#### Scenario: Query devices without active package
- **GIVEN** 用户正在后台设备管理页查询列表
- **WHEN** 用户在 `是否有生效中的套餐` 中选择“否”并执行搜索
- **THEN** 系统 MUST 调用 `GET /api/admin/devices`
- **AND** 请求 MUST 提交 `has_active_package=false`
- **AND** 前端 MUST NOT 因为该值为 `false` 而省略此参数
#### Scenario: Omit active package filter when not selected
- **GIVEN** 用户未选择 `是否有生效中的套餐`
- **WHEN** 用户执行设备列表搜索、刷新或翻页
- **THEN** 列表请求 MUST NOT 携带 `has_active_package`
### Requirement: Device Active Package Search Filter
The device management page SHALL provide a search filter labeled `是否有生效中的套餐` and keep its selected state consistent across list interactions.
#### Scenario: Show yes and no options for active package filter
- **GIVEN** 用户打开后台设备管理页
- **WHEN** 页面渲染搜索栏
- **THEN** 页面 MUST 显示 `是否有生效中的套餐` 筛选项
- **AND** 该筛选项 MUST 提供“是”和“否”两个可选值
#### Scenario: Reset clears active package filter
- **GIVEN** 用户已经在 `是否有生效中的套餐` 中选择了“是”或“否”
- **WHEN** 用户重置搜索条件
- **THEN** 页面 MUST 清空该筛选项
- **AND** 后续列表请求 MUST NOT 再携带 `has_active_package`

View File

@@ -0,0 +1,44 @@
## ADDED Requirements
### Requirement: IoT Card Active Package Query Contract
The IoT card management list integration SHALL support filtering records by whether the card currently has an active package via the query parameter `has_active_package`.
#### Scenario: Query IoT cards with active package
- **GIVEN** 用户正在后台 IoT 卡管理页查询列表
- **WHEN** 用户在 `是否有生效中的套餐` 中选择“是”并执行搜索
- **THEN** 系统 MUST 调用 `GET /api/admin/iot-cards/standalone`
- **AND** 请求 MUST 提交 `has_active_package=true`
#### Scenario: Query IoT cards without active package
- **GIVEN** 用户正在后台 IoT 卡管理页查询列表
- **WHEN** 用户在 `是否有生效中的套餐` 中选择“否”并执行搜索
- **THEN** 系统 MUST 调用 `GET /api/admin/iot-cards/standalone`
- **AND** 请求 MUST 提交 `has_active_package=false`
- **AND** 前端 MUST NOT 因为该值为 `false` 而省略此参数
#### Scenario: Omit active package filter when not selected
- **GIVEN** 用户未选择 `是否有生效中的套餐`
- **WHEN** 用户执行 IoT 卡列表搜索、刷新或翻页
- **THEN** 列表请求 MUST NOT 携带 `has_active_package`
### Requirement: IoT Card Active Package Search Filter
The IoT card management page SHALL provide a search filter labeled `是否有生效中的套餐` and keep its selected state consistent across list interactions.
#### Scenario: Show yes and no options for active package filter
- **GIVEN** 用户打开后台 IoT 卡管理页
- **WHEN** 页面渲染搜索栏
- **THEN** 页面 MUST 显示 `是否有生效中的套餐` 筛选项
- **AND** 该筛选项 MUST 提供“是”和“否”两个可选值
#### Scenario: Reset clears active package filter
- **GIVEN** 用户已经在 `是否有生效中的套餐` 中选择了“是”或“否”
- **WHEN** 用户重置搜索条件
- **THEN** 页面 MUST 清空该筛选项
- **AND** 后续列表请求 MUST NOT 再携带 `has_active_package`

View File

@@ -0,0 +1,18 @@
## 1. Contract Alignment
- [ ] 1.1 为 `StandaloneCardQueryParams` 新增可选布尔参数 `has_active_package`
- [ ] 1.2 为 `DeviceQueryParams` 新增可选布尔参数 `has_active_package`
- [ ] 1.3 检查卡列表与设备列表请求参数组装逻辑,确保选择“否”时的 `has_active_package=false` 不会被误删
## 2. Search UI
- [ ] 2.1 在 IoT 卡管理页搜索栏新增 `是否有生效中的套餐` 筛选,并将选择结果提交为 `has_active_package`
- [ ] 2.2 在设备管理页搜索栏新增 `是否有生效中的套餐` 筛选,并将选择结果提交为 `has_active_package`
- [ ] 2.3 确保两个页面在搜索、重置、翻页和刷新时正确保留或清空该筛选条件
## 3. Verification
- [ ] 3.1 验证 IoT 卡管理页选择“是”时请求提交 `has_active_package=true`
- [ ] 3.2 验证 IoT 卡管理页选择“否”时请求提交 `has_active_package=false`,且不会被省略
- [ ] 3.3 验证设备管理页选择“是 / 否”时请求都能正确提交 `has_active_package`
- [ ] 3.4 验证两个页面重置搜索后,后续请求不再携带 `has_active_package`

View File

@@ -0,0 +1,44 @@
# Change: 更新资产信息账号类型字段可见性与运营商账户展示
## Why
资产信息页当前在卡/设备基础信息区域存在两类与最新业务口径不一致的问题:
- 卡资产“卡信息”中的 `实名认证策略` 仍会对 `user_type=3`(代理账号)和 `user_type=4`(企业账号)显示。
- 设备资产“设备信息”中的 `实名认证策略``制造商` 仍会对 `user_type=3/4` 显示。
- 卡资产“运营商账户”当前展示的是 `carrier_name`,而最新要求需要改为展示 `carrier_type`,并按运营商管理中的映射规则转成中文,例如 `CTCC -> 中国电信`
如果不统一这些展示规则,不同账号在资产信息页会继续看到不应暴露的字段,同时“运营商账户”的显示口径也会与系统内其他模块不一致。
## What Changes
- 更新卡资产“卡信息”区域:
- 当当前登录账号 `user_type``3``4` 时,隐藏 `实名认证策略` 字段。
- 当当前登录账号 `user_type``3``4` 时,`运营商账户` 字段改为展示 `carrier_type` 的中文映射值,而不是 `carrier_name`
- 当当前登录账号 `user_type` 不是 `3` 且不是 `4` 时,`运营商账户` 字段继续保持当前 `carrier_name` 展示口径。
- 已知运营商类型映射沿用运营商管理口径:`CMCC -> 中国移动``CUCC -> 中国联通``CTCC -> 中国电信``CBN -> 中国广电`
-`carrier_type` 为空时显示 `-`;若返回未知编码,则保留原编码展示,避免吞掉上游信息。
- 更新设备资产“设备信息”区域:
- 当当前登录账号 `user_type``3``4` 时,隐藏 `实名认证策略` 字段。
- 当当前登录账号 `user_type``3``4` 时,隐藏 `制造商` 字段。
- 范围约束:
- 本提案仅调整资产信息基础描述区字段展示。
- 本提案不改动实名认证策略相关操作按钮的权限和可见性。
- 本提案不改动设备详情“绑定卡列表”中的列展示规则,除非后续另行提出变更。
## Impact
- Affected specs:
- `asset-information`
- Affected code:
- `src/views/asset-management/asset-information/components/BasicInfoCard.vue`
- `src/views/asset-management/asset-information/composables/useAssetFormatters.ts`
- `src/views/asset-management/asset-information/types.ts`
- `src/views/asset-management/asset-information/composables/useAssetInfo.ts`
- `src/config/constants/carrierTypes.ts`
- Dependencies:
- 依赖当前登录账号 `user_type` 可从用户状态中稳定读取
- 依赖资产详情接口继续返回 `carrier_type`
- 运营商类型中文映射应与运营商管理模块保持一致
- Breaking changes:
- 无外部接口破坏性变更;本次仅调整后台页面展示契约和字段可见性

View File

@@ -0,0 +1,123 @@
## ADDED Requirements
### Requirement: Admin Card Asset Carrier Type Contract
The admin asset information integration SHALL accept and preserve `carrier_type` from `GET /api/admin/assets/resolve/:identifier` for card assets so the card information section can render the carrier account value from the carrier type code.
#### Scenario: Card asset detail preserves carrier_type from resolve response
- **GIVEN** 用户正在查看后台“资产信息”中的卡资产详情
- **AND** `GET /api/admin/assets/resolve/:identifier` 返回了 `carrier_type`
- **WHEN** 前端解析卡资产详情响应
- **THEN** 页面状态 MUST 保留该 `carrier_type` 原始值
- **AND** 后续“卡信息”区域的 `运营商账户` 展示 MUST 基于该状态值进行格式化
#### Scenario: Missing carrier_type does not break card asset detail parsing
- **GIVEN** 用户正在查看后台“资产信息”中的卡资产详情
- **AND** `GET /api/admin/assets/resolve/:identifier` 未返回 `carrier_type` 或该字段为空
- **WHEN** 前端解析卡资产详情响应
- **THEN** 页面 MUST 继续保留其他卡资产字段的解析行为
- **AND** MUST NOT 因 `carrier_type` 缺失导致“卡信息”区域渲染失败
### Requirement: Admin Card Asset Carrier Account Display
The admin asset information view SHALL render the card information field `运营商账户` according to the current logged-in account type.
For logged-in accounts whose `user_type` is `3` (代理账号) or `4` (企业账号), the field MUST use `carrier_type` and the same carrier-type-to-Chinese mapping used by carrier management.
Known mappings MUST include `CMCC -> 中国移动`, `CUCC -> 中国联通`, `CTCC -> 中国电信`, and `CBN -> 中国广电`.
For other logged-in account types, the field MUST keep the existing `carrier_name` display behavior.
If `carrier_type` is empty or missing, the page MUST display `-`. If `carrier_type` contains an unknown code, the page MUST preserve and display the original code.
#### Scenario: Show China Telecom label for CTCC in card information for agent or enterprise account
- **GIVEN** 当前登录账号的 `user_type``3``4`
- **AND** 用户正在查看后台“资产信息”中的卡资产详情
- **AND** 当前卡资产详情状态中的 `carrier_type``CTCC`
- **WHEN** 页面渲染“卡信息”区域中的 `运营商账户`
- **THEN** 页面 MUST 显示 `中国电信`
#### Scenario: Show placeholder when carrier_type is empty for agent or enterprise account
- **GIVEN** 当前登录账号的 `user_type``3``4`
- **AND** 用户正在查看后台“资产信息”中的卡资产详情
- **AND** 当前卡资产详情状态中的 `carrier_type` 为空、缺失或仅包含空白字符
- **WHEN** 页面渲染“卡信息”区域中的 `运营商账户`
- **THEN** 页面 MUST 显示 `-`
#### Scenario: Preserve unknown carrier_type code for agent or enterprise account
- **GIVEN** 当前登录账号的 `user_type``3``4`
- **AND** 用户正在查看后台“资产信息”中的卡资产详情
- **AND** 当前卡资产详情状态中的 `carrier_type` 为未收录的编码
- **WHEN** 页面渲染“卡信息”区域中的 `运营商账户`
- **THEN** 页面 MUST 直接显示该原始编码
- **AND** MUST NOT 错误映射为其他运营商中文名称
#### Scenario: Keep carrier_name display for non-agent non-enterprise account
- **GIVEN** 当前登录账号的 `user_type` 不是 `3` 也不是 `4`
- **AND** 用户正在查看后台“资产信息”中的卡资产详情
- **AND** 当前卡资产详情状态中的 `carrier_name` 为有效值
- **WHEN** 页面渲染“卡信息”区域中的 `运营商账户`
- **THEN** 页面 MUST 显示该 `carrier_name`
### Requirement: Admin Card Realname Policy Visibility By Account Type
The admin asset information view SHALL hide the card information field `实名认证策略` for logged-in accounts whose `user_type` is `3` (代理账号) or `4` (企业账号).
For other logged-in account types, the page SHALL keep the existing card information field visibility behavior for `实名认证策略`.
#### Scenario: Hide card realname policy for agent account
- **GIVEN** 当前登录账号的 `user_type``3`
- **AND** 用户正在查看后台“资产信息”中的卡资产详情
- **WHEN** 页面渲染“卡信息”区域
- **THEN** 页面 MUST NOT 显示 `实名认证策略` 字段
#### Scenario: Hide card realname policy for enterprise account
- **GIVEN** 当前登录账号的 `user_type``4`
- **AND** 用户正在查看后台“资产信息”中的卡资产详情
- **WHEN** 页面渲染“卡信息”区域
- **THEN** 页面 MUST NOT 显示 `实名认证策略` 字段
#### Scenario: Keep card realname policy visible for non-agent non-enterprise account
- **GIVEN** 当前登录账号的 `user_type` 不是 `3` 也不是 `4`
- **AND** 用户正在查看后台“资产信息”中的卡资产详情
- **WHEN** 页面渲染“卡信息”区域
- **THEN** 页面 MUST 继续按现有规则渲染 `实名认证策略` 字段
### Requirement: Admin Device Sensitive Field Visibility By Account Type
The admin asset information view SHALL hide the device information fields `实名认证策略` and `制造商` for logged-in accounts whose `user_type` is `3` (代理账号) or `4` (企业账号).
For other logged-in account types, the page SHALL keep the existing device information field visibility behavior for both fields.
#### Scenario: Hide device realname policy and manufacturer for agent account
- **GIVEN** 当前登录账号的 `user_type``3`
- **AND** 用户正在查看后台“资产信息”中的设备资产详情
- **WHEN** 页面渲染“设备信息”区域
- **THEN** 页面 MUST NOT 显示 `实名认证策略` 字段
- **AND** 页面 MUST NOT 显示 `制造商` 字段
#### Scenario: Hide device realname policy and manufacturer for enterprise account
- **GIVEN** 当前登录账号的 `user_type``4`
- **AND** 用户正在查看后台“资产信息”中的设备资产详情
- **WHEN** 页面渲染“设备信息”区域
- **THEN** 页面 MUST NOT 显示 `实名认证策略` 字段
- **AND** 页面 MUST NOT 显示 `制造商` 字段
#### Scenario: Keep device realname policy and manufacturer visible for non-agent non-enterprise account
- **GIVEN** 当前登录账号的 `user_type` 不是 `3` 也不是 `4`
- **AND** 用户正在查看后台“资产信息”中的设备资产详情
- **WHEN** 页面渲染“设备信息”区域
- **THEN** 页面 MUST 继续按现有规则渲染 `实名认证策略` 字段
- **AND** 页面 MUST 继续按现有规则渲染 `制造商` 字段

View File

@@ -0,0 +1,20 @@
## 1. Contract And Formatting
- [x] 1.1 补齐资产信息页面本地类型/状态中的 `carrier_type` 字段,确保卡资产详情可稳定保留该值。
- [x] 1.2 复用运营商管理中的运营商类型映射规则,为资产信息页提供 `carrier_type` 到中文文案的格式化能力。
## 2. UI Visibility
- [x] 2.1 调整卡资产“卡信息”区域的 `运营商账户` 字段:`user_type=3/4` 展示 `carrier_type` 中文映射,其他账号继续展示原来的 `carrier_name`
- [x] 2.2 当当前登录账号 `user_type``3``4` 时,隐藏卡资产“卡信息”区域的 `实名认证策略` 字段。
- [x] 2.3 当当前登录账号 `user_type``3``4` 时,隐藏设备资产“设备信息”区域的 `实名认证策略``制造商` 字段。
- [x] 2.4 确认本次变更仅影响基础信息描述区,不改动实名认证策略操作按钮和设备绑定卡列表列。
## 3. Verification
- [ ] 3.1 验证 `user_type=3/4` 查看卡资产且返回 `carrier_type=CTCC` 时,“运营商账户”显示为 `中国电信`
- [ ] 3.2 验证 `user_type=3/4` 查看卡资产且返回 `carrier_type=CMCC/CUCC/CBN` 时,“运营商账户”分别显示与运营商管理一致的中文文案;空值显示 `-`
- [ ] 3.3 验证 `user_type=3/4` 查看卡资产且返回未知 `carrier_type` 编码时,“运营商账户”保留原编码展示。
- [ ] 3.4 验证 `user_type=3``user_type=4` 查看卡资产时,不显示 `实名认证策略` 字段。
- [ ] 3.5 验证 `user_type=3``user_type=4` 查看设备资产时,不显示 `实名认证策略``制造商` 字段。
- [ ] 3.6 验证其他账号类型查看卡资产时,“运营商账户”继续显示原来的 `carrier_name`;其余上述字段继续按现有规则显示。

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