From d9d07422a91da5f8989cf319d79498ef0e0a8a05 Mon Sep 17 00:00:00 2001 From: luo Date: Tue, 18 Aug 2026 17:36:41 +0800 Subject: [PATCH] fix: update some files --- .../add-historical-approval-resend/design.md | 50 ++ .../proposal.md | 39 ++ .../specs/agent-recharge/spec.md | 56 +++ .../specs/refund-management/spec.md | 56 +++ .../add-historical-approval-resend/tasks.md | 23 + .../design.md | 14 + .../proposal.md | 24 + .../specs/notification-center/spec.md | 22 + .../tasks.md | 15 + scripts/tests/agentRechargeService.test.mjs | 2 +- src/api/modules/agentRecharge.ts | 10 + src/api/modules/refund.ts | 8 + .../core/layouts/art-notification/index.vue | 38 +- .../core/layouts/art-notification/style.scss | 12 + src/store/modules/notification.ts | 53 ++- .../asset-management/device-list/index.vue | 10 +- .../exchange-management/detail.vue | 60 --- .../exchange-management/index.vue | 8 +- .../expiring-assets/index.vue | 34 +- .../export-task-list/index.vue | 8 +- .../record-management/asset-assign/detail.vue | 48 +- .../record-management/asset-assign/index.vue | 8 +- .../authorization-records/index.vue | 6 +- .../task-management/device-task/index.vue | 4 +- .../task-management/iot-card-task/index.vue | 4 +- src/views/audit/events/index.vue | 36 +- src/views/audit/integrations/index.vue | 77 +++- src/views/audit/risks/index.vue | 20 +- .../agent-fund-overview/index.vue | 4 +- .../my-commission/index.vue | 20 +- .../withdrawal-approval/index.vue | 6 +- .../agent-recharge/agentRechargeActions.ts | 19 +- src/views/finance/agent-recharge/detail.vue | 126 ++--- src/views/finance/agent-recharge/index.vue | 334 ++++++++------ src/views/finance/refund/detail.vue | 138 +++--- src/views/finance/refund/index.vue | 434 ++++++++++-------- .../order-management/order-list/detail.vue | 34 -- .../order-management/order-list/index.vue | 8 +- 38 files changed, 1140 insertions(+), 728 deletions(-) create mode 100644 openspec/changes/add-historical-approval-resend/design.md create mode 100644 openspec/changes/add-historical-approval-resend/proposal.md create mode 100644 openspec/changes/add-historical-approval-resend/specs/agent-recharge/spec.md create mode 100644 openspec/changes/add-historical-approval-resend/specs/refund-management/spec.md create mode 100644 openspec/changes/add-historical-approval-resend/tasks.md create mode 100644 openspec/changes/update-notification-drawer-pagination/design.md create mode 100644 openspec/changes/update-notification-drawer-pagination/proposal.md create mode 100644 openspec/changes/update-notification-drawer-pagination/specs/notification-center/spec.md create mode 100644 openspec/changes/update-notification-drawer-pagination/tasks.md diff --git a/openspec/changes/add-historical-approval-resend/design.md b/openspec/changes/add-historical-approval-resend/design.md new file mode 100644 index 0000000..cf539b1 --- /dev/null +++ b/openspec/changes/add-historical-approval-resend/design.md @@ -0,0 +1,50 @@ +## Context + +退款和代理充值已经接入企微审批,但部分历史记录在审批流程上线前创建,列表中的 `approval_status` 为空。这些记录需要由运营人员手动触发一次审批补发,才能进入企微审批链路。 + +## Goals / Non-Goals + +- Goals: 提供代理充值和退款的历史审批补发入口。 +- Goals: 通过路径参数指定目标记录,并复用后端返回的完整记录与审批状态。 +- Goals: 由明确的状态规则控制入口展示,后端做最终资格校验。 +- Non-Goals: 不在前端实现审批提交、审批回调或审批引擎。 +- Non-Goals: 不修改历史记录的业务字段、金额或支付/退款结果。 +- Non-Goals: 不提供批量自动补发。 + +## Decisions + +- Decision: 两个模块分别新增 `triggerApproval(id)` 服务方法,返回完整业务记录并保留审批字段。 +- Rationale: 两个接口契约一致,成功后页面需要立即展示最新审批状态,完整记录可直接用于刷新。 + +- Decision: 代理充值仅在 `approval_status` 为空且 `status` 不属于已完成、已驳回、已关闭时显示补发入口;退款仅在 `status=待审批` 且 `approval_status` 为空时显示补发入口。 +- Rationale: 与业务规则一致,避免对已进入审批或已终结的记录重复补发;后端仍做最终资格校验。 + +- Decision: 引入独立按钮权限 `agent_recharge:trigger_approval` 和 `refund:trigger_approval`。 +- Rationale: 补发审批是财务相关敏感操作,需与现有确认支付、拒绝、重新申请权限区分。 + +- Decision: 补发审批与现有“确认支付/拒绝”和“重新申请”操作共存。 +- Rationale: 它们承担不同职责;补发审批只是把历史记录推进企微审批,不改变后续人工确认或重新申请的流程。 + +## Risks / Trade-offs + +- Risk: 代理充值中 `status=已支付`、`已退款` 等非终态记录是否允许补发,需要后端最终确认。 +- Mitigation: 前端按约定的审批状态与状态排除规则展示入口,后端对不允许的记录返回错误并稳定展示。 + +- Risk: 接口可能对部分状态返回拒绝。 +- Mitigation: 前端处理后端错误信息,不将失败记录标记为已补发。 + +- Risk: 重复点击可能触发多次审批提交。 +- Mitigation: 提交期间锁定按钮并禁用重复触发;后端应保证幂等或返回明确的已存在审批提示。 + +## Migration Plan + +1. 确认两个 `trigger-approval` 接口的响应字段与现有 `AgentRecharge`、`Refund` 类型一致。 +2. 新增服务方法和按钮权限。 +3. 在列表接入补发审批入口及资格判断。 +4. 联调补发成功、接口拒绝、权限缺失、审批状态为空与状态不符合条件等场景。 +5. 验证与现有确认支付、拒绝、重新申请操作不冲突。 + +## Open Questions + +- 后端对可补发记录的最终状态校验以及幂等性需确认。 +- 两个接口是否都需要独立权限码,还是复用现有审批/财务权限,需与后端权限配置对齐。 diff --git a/openspec/changes/add-historical-approval-resend/proposal.md b/openspec/changes/add-historical-approval-resend/proposal.md new file mode 100644 index 0000000..624231f --- /dev/null +++ b/openspec/changes/add-historical-approval-resend/proposal.md @@ -0,0 +1,39 @@ +# Change: 补发历史线下代理充值审批与补发历史退款审批 + +## Why + +历史线下代理充值记录和历史退款申请在企微审批流程上线前创建,列表中这些记录的审批状态为空,运营人员无法为它们补发审批流程。需要为这两类业务提供“补发审批”入口,调用后端触发审批接口,将历史记录纳入企微审批。 + +## What Changes + +- 在 `AgentRechargeService` 新增 `triggerApproval(id)`,调用 `POST /api/admin/agent-recharges/{id}/trigger-approval`。 +- 在 `RefundService` 新增 `triggerApproval(id)`,调用 `POST /api/admin/refunds/{id}/trigger-approval`。 +- 在代理充值列表为 `approval_status` 为空且 `status` 不属于已完成、已驳回、已关闭的充值记录增加“补发审批”操作。 +- 在退款列表为 `status=待审批` 且 `approval_status` 为空的退款申请增加“补发审批”操作。 +- 引入权限 `agent_recharge:trigger_approval` 和 `refund:trigger_approval`,仅对有权限的平台账号展示操作。 +- 补发成功后刷新列表并展示返回的最新审批状态;失败时展示后端错误信息。 +- 不改变现有“确认支付”“拒绝”“重新申请”等操作的资格和职责。 + +## Impact + +- Affected specs: + - `agent-recharge` + - `refund-management` +- Affected code: + - `src/api/modules/agentRecharge.ts` + - `src/api/modules/refund.ts` + - `src/types/api/agentRecharge.ts` + - `src/types/api/refund.ts` + - `src/views/finance/agent-recharge/agentRechargeActions.ts` + - `src/views/finance/agent-recharge/index.vue` + - `src/views/finance/refund/index.vue` +- API contracts: + - `POST /api/admin/agent-recharges/{id}/trigger-approval` + - `POST /api/admin/refunds/{id}/trigger-approval` +- Dependencies: + - 后端按文档返回完整业务记录及当前审批状态。 + - 后端负责校验记录是否可补发审批,前端仅控制入口展示并处理后端拒绝。 +- Out of scope: + - 企微审批的发起、撤回、通过、驳回或删除动作本身。 + - 修改历史记录的业务字段、金额或支付/退款结果。 + - 自动判断并批量补发历史审批。 diff --git a/openspec/changes/add-historical-approval-resend/specs/agent-recharge/spec.md b/openspec/changes/add-historical-approval-resend/specs/agent-recharge/spec.md new file mode 100644 index 0000000..aa68abb --- /dev/null +++ b/openspec/changes/add-historical-approval-resend/specs/agent-recharge/spec.md @@ -0,0 +1,56 @@ +## ADDED Requirements + +### Requirement: Agent Recharge Historical Approval Resend API + +The agent recharge service SHALL expose a historical approval resend operation through `POST /api/admin/agent-recharges/{id}/trigger-approval`. + +#### Scenario: Resend approval for a historical offline recharge + +- **GIVEN** 一个需要补发审批的历史线下代理充值记录 `id` +- **WHEN** 前端调用 `POST /api/admin/agent-recharges/{id}/trigger-approval` +- **THEN** 请求 MUST 在路径参数中携带该充值记录 `id` +- **AND** 成功响应 MUST 被解析为完整的 `AgentRecharge` 记录,并保留 `approval_provider`、`approval_instance_id`、`approval_status` 和 `approval_status_name` 字段 + +### Requirement: Agent Recharge Historical Approval Resend Entry + +The agent recharge list page SHALL provide a `补发审批` action only for recharge records whose `approval_status` is empty and whose business `status` is not completed, rejected, or closed, and SHALL gate it by permission. + +#### Scenario: Show resend action for an eligible recharge + +- **GIVEN** 平台账号拥有 `agent_recharge:trigger_approval` 权限 +- **AND** 充值记录的 `approval_status` 为空(`null` 或 `undefined`) +- **AND** 充值记录的 `status` 不属于已完成(3)、已驳回(6)、已关闭(4) +- **WHEN** 页面渲染代理充值列表 +- **THEN** 页面 MUST 为该记录显示“补发审批”操作 + +#### Scenario: Hide resend action when approval status is present + +- **GIVEN** 充值记录的 `approval_status` 不为空 +- **WHEN** 页面渲染该记录 +- **THEN** 页面 MUST NOT 显示“补发审批”操作 + +#### Scenario: Hide resend action for terminal recharge statuses + +- **GIVEN** 充值记录的 `status` 为已完成(3)、已驳回(6)或已关闭(4) +- **WHEN** 页面渲染该记录 +- **THEN** 页面 MUST NOT 显示“补发审批”操作 + +#### Scenario: Hide resend action without permission + +- **GIVEN** 当前账号不拥有 `agent_recharge:trigger_approval` 权限 +- **WHEN** 页面渲染代理充值记录 +- **THEN** 页面 MUST NOT 显示“补发审批”操作 + +#### Scenario: Resend approval succeeds + +- **GIVEN** 用户对符合条件的充值记录点击“补发审批” +- **WHEN** 接口返回 `code=0` +- **THEN** 页面 MUST 显示成功提示并刷新列表 +- **AND** 刷新后的记录 MUST 展示接口返回的最新审批状态 + +#### Scenario: Resend approval fails + +- **GIVEN** 接口返回非零 `code` 或请求失败 +- **WHEN** 用户触发“补发审批” +- **THEN** 页面 MUST 展示后端返回的错误信息 +- **AND** 页面 MUST NOT 将记录标记为已补发审批 diff --git a/openspec/changes/add-historical-approval-resend/specs/refund-management/spec.md b/openspec/changes/add-historical-approval-resend/specs/refund-management/spec.md new file mode 100644 index 0000000..7f95cd8 --- /dev/null +++ b/openspec/changes/add-historical-approval-resend/specs/refund-management/spec.md @@ -0,0 +1,56 @@ +## ADDED Requirements + +### Requirement: Refund Historical Approval Resend API + +The refund service SHALL expose a historical approval resend operation through `POST /api/admin/refunds/{id}/trigger-approval`. + +#### Scenario: Resend approval for a historical refund + +- **GIVEN** 一个需要补发审批的历史退款申请 `id` +- **WHEN** 前端调用 `POST /api/admin/refunds/{id}/trigger-approval` +- **THEN** 请求 MUST 在路径参数中携带该退款申请 `id` +- **AND** 成功响应 MUST 被解析为完整的 `Refund` 记录,并保留 `approval_provider`、`approval_instance_id`、`approval_status` 和 `approval_status_name` 字段 + +### Requirement: Refund Historical Approval Resend Entry + +The refund list page SHALL provide a `补发审批` action only for refund records whose `status` is pending approval and whose `approval_status` is empty, and SHALL gate it by permission. + +#### Scenario: Show resend action for an eligible refund + +- **GIVEN** 平台账号拥有 `refund:trigger_approval` 权限 +- **AND** 退款申请的 `status` 为待审批(1) +- **AND** 退款申请的 `approval_status` 为空(`null` 或 `undefined`) +- **WHEN** 页面渲染退款列表 +- **THEN** 页面 MUST 为该记录显示“补发审批”操作 + +#### Scenario: Hide resend action when refund status is not pending + +- **GIVEN** 退款申请的 `status` 不为待审批(1) +- **WHEN** 页面渲染该记录 +- **THEN** 页面 MUST NOT 显示“补发审批”操作 + +#### Scenario: Hide resend action when approval status is present + +- **GIVEN** 退款申请的 `approval_status` 不为空 +- **WHEN** 页面渲染该记录 +- **THEN** 页面 MUST NOT 显示“补发审批”操作 + +#### Scenario: Hide resend action without permission + +- **GIVEN** 当前账号不拥有 `refund:trigger_approval` 权限 +- **WHEN** 页面渲染退款记录 +- **THEN** 页面 MUST NOT 显示“补发审批”操作 + +#### Scenario: Resend approval succeeds + +- **GIVEN** 用户对符合条件的退款申请点击“补发审批” +- **WHEN** 接口返回 `code=0` +- **THEN** 页面 MUST 显示成功提示并刷新列表 +- **AND** 刷新后的记录 MUST 展示接口返回的最新审批状态 + +#### Scenario: Resend approval fails + +- **GIVEN** 接口返回非零 `code` 或请求失败 +- **WHEN** 用户触发“补发审批” +- **THEN** 页面 MUST 展示后端返回的错误信息 +- **AND** 页面 MUST NOT 将记录标记为已补发审批 diff --git a/openspec/changes/add-historical-approval-resend/tasks.md b/openspec/changes/add-historical-approval-resend/tasks.md new file mode 100644 index 0000000..8f4f5a9 --- /dev/null +++ b/openspec/changes/add-historical-approval-resend/tasks.md @@ -0,0 +1,23 @@ +## 1. 类型与 API 契约 + +- [x] 1.1 在 `src/api/modules/agentRecharge.ts` 新增 `triggerApproval(id)` 方法 +- [x] 1.2 在 `src/api/modules/refund.ts` 新增 `triggerApproval(id)` 方法 +- [x] 1.3 确认 `AgentRecharge` 和 `Refund` 类型包含 `approval_provider`、`approval_source`、`approval_instance_id`、`approval_status`、`approval_status_name` + +## 2. 代理充值补发审批入口 + +- [x] 2.1 在 `agentRechargeActions.ts` 增加“补发审批”动作及资格判断 +- [x] 2.2 在代理充值列表接入权限 `agent_recharge:trigger_approval` 与成功/失败处理 +- [x] 2.3 补发成功后刷新列表并展示最新审批状态 + +## 3. 退款补发审批入口 + +- [x] 3.1 在退款列表 `getActions` 增加“补发审批”动作及资格判断 +- [x] 3.2 接入权限 `refund:trigger_approval` 与成功/失败处理 +- [x] 3.3 补发成功后刷新列表并展示最新审批状态 + +## 4. 校验与验证 + +- [x] 4.1 运行 `openspec validate add-historical-approval-resend --strict` +- [x] 4.2 运行 ESLint、类型检查并修复 +- [ ] 4.3 手工验证有/无审批实例、线上/线下充值、权限开关等场景 diff --git a/openspec/changes/update-notification-drawer-pagination/design.md b/openspec/changes/update-notification-drawer-pagination/design.md new file mode 100644 index 0000000..98601bf --- /dev/null +++ b/openspec/changes/update-notification-drawer-pagination/design.md @@ -0,0 +1,14 @@ +## Context + +顶部通知抽屉已经通过通知列表接口加载最近 10 条,但当前共享状态没有保存列表分页信息。分页应复用现有通知列表接口,避免新增接口或改变通知数据契约。 + +## Decisions + +- Decision: 分页状态由通知 store 保存,包括当前页、每页数量和总条数;列表请求返回后整体替换 `recentNotifications`。 +- Decision: 页码切换使用 `GET /api/admin/notifications?page=&page_size=10`,加载期间沿用现有 loading 状态。 +- Decision: 分类切换将页码重置为 1,并重新加载列表;不使用滚动事件触发请求。 +- Decision: 分页控件放在通知列表底部,列表区域继续保持固定高度,避免抽屉整体布局跳动。 + +## Risks / Trade-offs + +- 分类当前由抽屉对已加载页面进行过滤,分类页的总数仍由通知列表接口返回的总体 `total` 表示;本次不扩展后端分类分页契约。 diff --git a/openspec/changes/update-notification-drawer-pagination/proposal.md b/openspec/changes/update-notification-drawer-pagination/proposal.md new file mode 100644 index 0000000..c761f4f --- /dev/null +++ b/openspec/changes/update-notification-drawer-pagination/proposal.md @@ -0,0 +1,24 @@ +# Change: 通知抽屉改为分页浏览 + +## Why + +顶部通知抽屉目前只展示最近 10 条通知,用户无法通过页码查看更早的通知。通知列表应使用明确的分页操作,避免依赖下滑加载更多的交互。 + +## What Changes + +- 顶部通知抽屉增加页码分页控件,默认每页展示 10 条。 +- 切换页码时调用通知列表接口并替换当前列表,不追加滚动加载。 +- 切换通知分类时重置到第 1 页,并保持现有分类统计、已读和跳转行为。 +- 保留 `/api/admin/notifications` 的 `page`、`page_size` 分页参数和后端返回的 `total`。 + +## Impact + +- Affected specs: + - `notification-center` +- Affected code: + - `src/components/core/layouts/art-notification/index.vue` + - `src/components/core/layouts/art-notification/style.scss` + - `src/store/modules/notification.ts` +- Out of scope: + - 不改变通知接口、通知分类统计和已读接口。 + - 不接入下滑加载、无限滚动或新的实时推送机制。 diff --git a/openspec/changes/update-notification-drawer-pagination/specs/notification-center/spec.md b/openspec/changes/update-notification-drawer-pagination/specs/notification-center/spec.md new file mode 100644 index 0000000..abf9a8f --- /dev/null +++ b/openspec/changes/update-notification-drawer-pagination/specs/notification-center/spec.md @@ -0,0 +1,22 @@ +## MODIFIED Requirements + +### Requirement: Global Notification Bell + +The admin frontend SHALL provide a global notification bell in the top navigation near the settings and user avatar entries. + +#### Scenario: Display unread count + +- **GIVEN** 用户已登录后台 +- **WHEN** 顶部导航加载未读通知数量 +- **THEN** 铃铛 MUST call `GET /api/admin/notifications/unread-count` +- **AND** 数量 MUST display as `0`, `1` through `99`, or `99+` + +#### Scenario: Open paginated notification drawer + +- **WHEN** 用户点击顶部通知铃铛 +- **THEN** 页面 MUST display notification items from `GET /api/admin/notifications` using `page=1` and `page_size=10` +- **AND** 抽屉 MUST provide 全部、审批、临期、同步/系统分类 +- **AND** 抽屉 MUST provide page controls based on the response `total` +- **AND** changing page MUST replace the visible items instead of appending items from a scroll event +- **AND** changing category MUST reset the page to 1 +- **AND** 抽屉 MUST provide an entry to `/notifications` diff --git a/openspec/changes/update-notification-drawer-pagination/tasks.md b/openspec/changes/update-notification-drawer-pagination/tasks.md new file mode 100644 index 0000000..b9278b1 --- /dev/null +++ b/openspec/changes/update-notification-drawer-pagination/tasks.md @@ -0,0 +1,15 @@ +## 1. Notification State + +- [x] 1.1 在通知 store 中保存当前页、每页数量和列表总数。 +- [x] 1.2 支持按指定页请求通知,并用新结果替换当前列表。 + +## 2. Notification Drawer + +- [x] 2.1 在通知列表底部增加页码分页控件,默认每页 10 条。 +- [x] 2.2 切换页码时重新请求列表并回到列表顶部。 +- [x] 2.3 切换分类时重置页码并保持现有分类、已读和跳转行为。 + +## 3. Verification + +- [x] 3.1 验证分页请求携带正确的 `page` 和 `page_size`,且列表不会累加旧页数据。 +- [x] 3.2 运行通知相关 lint、类型、格式、样式和编码检查;当前仓库暂无通知专项测试。 diff --git a/scripts/tests/agentRechargeService.test.mjs b/scripts/tests/agentRechargeService.test.mjs index 425f053..6010096 100644 --- a/scripts/tests/agentRechargeService.test.mjs +++ b/scripts/tests/agentRechargeService.test.mjs @@ -27,7 +27,7 @@ test('agent recharge rejection posts the rejection reason to the order rejection code: 0, data: { methods: ['wechat', 'alipay'], - min_amount: 10, + min_amount: 10000, max_amount: 100000000 } }) diff --git a/src/api/modules/agentRecharge.ts b/src/api/modules/agentRecharge.ts index 84e87b9..6b07651 100644 --- a/src/api/modules/agentRecharge.ts +++ b/src/api/modules/agentRecharge.ts @@ -88,4 +88,14 @@ export class AgentRechargeService extends BaseService { ): Promise> { return this.post>(`/api/admin/agent-recharges/${id}/reject`, data) } + + /** + * 补发历史线下代理充值审批 + * @param id 充值记录ID + */ + static triggerApproval(id: number): Promise> { + return this.post>( + `/api/admin/agent-recharges/${id}/trigger-approval` + ) + } } diff --git a/src/api/modules/refund.ts b/src/api/modules/refund.ts index 2752a7a..4b8f0de 100644 --- a/src/api/modules/refund.ts +++ b/src/api/modules/refund.ts @@ -45,4 +45,12 @@ export class RefundService extends BaseService { static resubmitRefund(id: number, data: ResubmitRefundRequest): Promise> { return this.post>(`/api/admin/refunds/${id}/resubmit`, data) } + + /** + * 补发历史退款审批 + * @param id 退款申请ID + */ + static triggerApproval(id: number): Promise> { + return this.post>(`/api/admin/refunds/${id}/trigger-approval`) + } } diff --git a/src/components/core/layouts/art-notification/index.vue b/src/components/core/layouts/art-notification/index.vue index 0848193..eab3816 100644 --- a/src/components/core/layouts/art-notification/index.vue +++ b/src/components/core/layouts/art-notification/index.vue @@ -12,14 +12,14 @@ :key="tab.value" type="button" :class="{ active: activeCategory === tab.value }" - @click="activeCategory = tab.value" + @click="handleCategoryChange(tab.value)" > {{ tab.label }} ({{ tab.count }})
-
+
+
@@ -64,6 +79,7 @@ const router = useRouter() const notificationStore = useNotificationStore() const activeCategory = ref<'all' | NotificationCategory>('all') + const notificationListRef = ref(null) const visible = computed(() => props.modelValue) const categoryMatches = (item: NotificationItem, category: string) => { @@ -96,6 +112,24 @@ const close = () => emit('update:modelValue', false) + const loadNotificationPage = async (page: number) => { + try { + await notificationStore.loadNotifications(page) + notificationListRef.value?.scrollTo({ top: 0 }) + } catch (error: any) { + ElMessage.error(error?.message || '获取通知失败') + } + } + + const handleCategoryChange = (category: 'all' | NotificationCategory) => { + activeCategory.value = category + void loadNotificationPage(1) + } + + const handlePageChange = (page: number) => { + void loadNotificationPage(page) + } + const handleMarkAllRead = async () => { try { const response = await notificationStore.markAllRead() diff --git a/src/components/core/layouts/art-notification/style.scss b/src/components/core/layouts/art-notification/style.scss index df92bd9..d043cf4 100644 --- a/src/components/core/layouts/art-notification/style.scss +++ b/src/components/core/layouts/art-notification/style.scss @@ -68,6 +68,18 @@ overflow-y: auto; } + .pagination { + display: flex; + justify-content: center; + padding: 12px 8px; + overflow-x: auto; + border-top: 1px solid var(--art-border-color); + + :deep(.el-pagination) { + justify-content: center; + } + } + .notification-item { display: flex; gap: 10px; diff --git a/src/store/modules/notification.ts b/src/store/modules/notification.ts index 564fe27..32f2225 100644 --- a/src/store/modules/notification.ts +++ b/src/store/modules/notification.ts @@ -1,7 +1,12 @@ import { defineStore } from 'pinia' import { ref } from 'vue' import { NotificationService } from '@/api/modules/notification' -import type { NotificationItem, NotificationUnreadSummary } from '@/types/api' +import type { + BaseResponse, + NotificationItem, + NotificationListResponse, + NotificationUnreadSummary +} from '@/types/api' const emptySummary = (): NotificationUnreadSummary => ({ approval: 0, @@ -16,8 +21,22 @@ export const useNotificationStore = defineStore('notificationStore', () => { const displayCount = ref('0') const summary = ref(emptySummary()) const recentNotifications = ref([]) + const notificationPage = ref(1) + const notificationPageSize = 10 + const notificationTotal = ref(0) const loading = ref(false) + const applyNotificationList = ( + response: BaseResponse, + fallbackPage: number + ) => { + if (response.code !== 0 || !response.data) return + + recentNotifications.value = response.data.items + notificationPage.value = response.data.page || fallbackPage + notificationTotal.value = Math.max(0, response.data.total || 0) + } + const refreshUnreadCount = async () => { const response = await NotificationService.getUnreadCount() if (response.code === 0 && response.data) { @@ -27,19 +46,33 @@ export const useNotificationStore = defineStore('notificationStore', () => { return unreadCount.value } - const refreshSummary = async () => { + const loadNotifications = async (page = notificationPage.value) => { + const targetPage = Math.max(1, page) + loading.value = true + try { + const response = await NotificationService.getNotifications({ + page: targetPage, + page_size: notificationPageSize + }) + applyNotificationList(response, targetPage) + return response + } finally { + loading.value = false + } + } + + const refreshSummary = async (page = 1) => { + const targetPage = Math.max(1, page) loading.value = true try { const [summaryResponse, listResponse] = await Promise.all([ NotificationService.getUnreadSummary(), - NotificationService.getNotifications({ page: 1, page_size: 10 }) + NotificationService.getNotifications({ page: targetPage, page_size: notificationPageSize }) ]) if (summaryResponse.code === 0 && summaryResponse.data) { summary.value = summaryResponse.data } - if (listResponse.code === 0 && listResponse.data) { - recentNotifications.value = listResponse.data.items.slice(0, 10) - } + applyNotificationList(listResponse, targetPage) await refreshUnreadCount() return summary.value } finally { @@ -50,7 +83,7 @@ export const useNotificationStore = defineStore('notificationStore', () => { const markRead = async (id: number) => { const response = await NotificationService.markRead(id) if (response.code === 0) { - await refreshSummary() + await refreshSummary(notificationPage.value) } return response } @@ -58,7 +91,7 @@ export const useNotificationStore = defineStore('notificationStore', () => { const markAllRead = async (category?: string) => { const response = await NotificationService.markAllRead(category ? { category } : {}) if (response.code === 0) { - await refreshSummary() + await refreshSummary(notificationPage.value) } return response } @@ -68,8 +101,12 @@ export const useNotificationStore = defineStore('notificationStore', () => { displayCount, summary, recentNotifications, + notificationPage, + notificationPageSize, + notificationTotal, loading, refreshUnreadCount, + loadNotifications, refreshSummary, markRead, markAllRead diff --git a/src/views/asset-management/device-list/index.vue b/src/views/asset-management/device-list/index.vue index 5f9da04..69e7088 100644 --- a/src/views/asset-management/device-list/index.vue +++ b/src/views/asset-management/device-list/index.vue @@ -397,11 +397,11 @@ > @@ -1537,13 +1537,13 @@ { label: '起止时间', prop: 'dateRange', - type: 'date', + type: 'datetimerange', config: { - type: 'daterange', + type: 'datetimerange', rangeSeparator: '至', startPlaceholder: '开始时间', endPlaceholder: '结束时间', - valueFormat: 'YYYY-MM-DD' + valueFormat: 'YYYY-MM-DDTHH:mm:ssZ' } } ] diff --git a/src/views/asset-management/exchange-management/detail.vue b/src/views/asset-management/exchange-management/detail.vue index 6724808..65b6882 100644 --- a/src/views/asset-management/exchange-management/detail.vue +++ b/src/views/asset-management/exchange-management/detail.vue @@ -10,30 +10,6 @@ 返回

换货单详情

- - {{ userType === 3 ? '活动记录' : '换货审计' }} - - - 旧资产审计 - - - 新资产审计 - @@ -59,51 +35,15 @@ import { formatDateTime } from '@/utils/business/format' import DetailPage from '@/components/common/DetailPage.vue' import type { DetailSection } from '@/components/common/DetailPage.vue' - import { useAuth } from '@/composables/useAuth' - import { useUserStore } from '@/store/modules/user' - import { AUDIT_PERMISSIONS } from '@/config/constants/audit' - import { resolveAuditResourceTarget } from '@/utils/business/auditNavigation' - import { openAuditInvestigation } from '@/components/business/audit/investigationController' defineOptions({ name: 'ExchangeDetail' }) const route = useRoute() const router = useRouter() - const { hasAuth } = useAuth() - const userStore = useUserStore() const loading = ref(false) const exchangeDetail = ref(null) const exchangeId = ref(0) - const userType = computed(() => Number(userStore.info.user_type)) - const exchangeAuditPermission = computed(() => - userType.value === 3 ? AUDIT_PERMISSIONS.agentExchangeActivity : AUDIT_PERMISSIONS.exchangeEntry - ) - const exchangeAuditTarget = computed(() => { - if (!exchangeDetail.value || ![1, 2, 3].includes(userType.value)) return null - return resolveAuditResourceTarget({ - userType: userType.value, - resourceType: 'exchange_order', - internalId: exchangeDetail.value.id, - businessIdentifier: exchangeDetail.value.exchange_no - }) - }) - const oldAssetAuditTarget = computed(() => { - if (!exchangeDetail.value || ![1, 2].includes(userType.value)) return null - return resolveAuditResourceTarget({ - userType: userType.value, - resourceType: exchangeDetail.value.old_asset_type, - internalId: exchangeDetail.value.old_asset_id - }) - }) - const newAssetAuditTarget = computed(() => { - if (!exchangeDetail.value || ![1, 2].includes(userType.value)) return null - return resolveAuditResourceTarget({ - userType: userType.value, - resourceType: exchangeDetail.value.new_asset_type || '', - internalId: exchangeDetail.value.new_asset_id - }) - }) const formatExchangeShopName = (shopName?: string | null, shopId?: number | null) => { if (shopName) return shopName diff --git a/src/views/asset-management/exchange-management/index.vue b/src/views/asset-management/exchange-management/index.vue index 68b7362..24e54e5 100644 --- a/src/views/asset-management/exchange-management/index.vue +++ b/src/views/asset-management/exchange-management/index.vue @@ -851,15 +851,15 @@ } }, { - label: '创建时间', + label: '起止时间', prop: 'created_at_range', - type: 'date', + type: 'datetimerange', config: { - type: 'daterange', + type: 'datetimerange', rangeSeparator: '至', startPlaceholder: '开始日期', endPlaceholder: '结束日期', - valueFormat: 'YYYY-MM-DD' + valueFormat: 'YYYY-MM-DDTHH:mm:ssZ' } } ]) diff --git a/src/views/asset-management/expiring-assets/index.vue b/src/views/asset-management/expiring-assets/index.vue index e8aa39a..84ec8f9 100644 --- a/src/views/asset-management/expiring-assets/index.vue +++ b/src/views/asset-management/expiring-assets/index.vue @@ -80,7 +80,7 @@ total_count: 0, window_days: 15 }) - const searchForm = reactive({ + const searchForm = reactive({ asset_type: undefined, keyword: '', shop_id: undefined, @@ -88,7 +88,8 @@ days_min: undefined, days_max: undefined, expires_from: undefined, - expires_to: undefined + expires_to: undefined, + expires_range: [] }) const pagination = reactive({ currentPage: 1, pageSize: 20, total: 0 }) @@ -166,16 +167,17 @@ } }, { - label: '预计到期起始', - prop: 'expires_from', - type: 'date', - config: { valueFormat: 'YYYY-MM-DD', clearable: true } - }, - { - label: '预计到期结束', - prop: 'expires_to', - type: 'date', - config: { valueFormat: 'YYYY-MM-DD', clearable: true } + label: '预计到期起止', + prop: 'expires_range', + type: 'datetimerange', + config: { + type: 'datetimerange', + rangeSeparator: '至', + startPlaceholder: '开始时间', + endPlaceholder: '结束时间', + valueFormat: 'YYYY-MM-DDTHH:mm:ssZ', + clearable: true + } } ]) @@ -314,8 +316,9 @@ const daysMax = toInteger(searchForm.days_max) if (daysMin !== undefined) params.days_min = daysMin if (daysMax !== undefined) params.days_max = daysMax - if (searchForm.expires_from) params.expires_from = searchForm.expires_from - if (searchForm.expires_to) params.expires_to = searchForm.expires_to + const [expiresFrom, expiresTo] = searchForm.expires_range || [] + if (expiresFrom) params.expires_from = expiresFrom + if (expiresTo) params.expires_to = expiresTo const response = await AssetService.getExpiringAssets(params) if (response.code === 0 && response.data) { @@ -347,7 +350,8 @@ days_min: undefined, days_max: undefined, expires_from: undefined, - expires_to: undefined + expires_to: undefined, + expires_range: [] }) pagination.currentPage = 1 void loadAssets() diff --git a/src/views/asset-management/export-task-management/export-task-list/index.vue b/src/views/asset-management/export-task-management/export-task-list/index.vue index d5ba9dc..1169b68 100644 --- a/src/views/asset-management/export-task-management/export-task-list/index.vue +++ b/src/views/asset-management/export-task-management/export-task-list/index.vue @@ -100,15 +100,15 @@ options: () => statusOptions }, { - label: '创建时间', + label: '起止时间', prop: 'dateRange', - type: 'date', + type: 'datetimerange', config: { - type: 'daterange', + type: 'datetimerange', rangeSeparator: '至', startPlaceholder: '开始时间', endPlaceholder: '结束时间', - valueFormat: 'YYYY-MM-DD' + valueFormat: 'YYYY-MM-DDTHH:mm:ssZ' } } ] diff --git a/src/views/asset-management/record-management/asset-assign/detail.vue b/src/views/asset-management/record-management/asset-assign/detail.vue index e7bccc3..4eee848 100644 --- a/src/views/asset-management/record-management/asset-assign/detail.vue +++ b/src/views/asset-management/record-management/asset-assign/detail.vue @@ -10,22 +10,6 @@ 返回

资产分配详情

- - {{ userType === 3 ? '活动记录' : '分配审计' }} - - - 资产审计 - @@ -41,7 +25,7 @@