From fac05db39afc818668193f4928f51f70e5b5554f Mon Sep 17 00:00:00 2001 From: luo Date: Mon, 20 Jul 2026 10:50:23 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=8D=A2=E8=B4=A7=E6=96=B0=E8=B5=84?= =?UTF-8?q?=E4=BA=A7=E5=BD=92=E5=B1=9E=E7=BB=A7=E6=89=BF=E6=8F=90=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../proposal.md | 22 ++ .../specs/exchange-management/spec.md | 88 +++++ .../tasks.md | 22 ++ src/api/modules/asset.ts | 9 +- src/api/modules/exchange.ts | 6 +- .../exchange-management/detail.vue | 11 + .../exchange-management/index.vue | 344 +++++++++++++++++- 7 files changed, 485 insertions(+), 17 deletions(-) create mode 100644 openspec/changes/update-exchange-inherited-shop-hint/proposal.md create mode 100644 openspec/changes/update-exchange-inherited-shop-hint/specs/exchange-management/spec.md create mode 100644 openspec/changes/update-exchange-inherited-shop-hint/tasks.md diff --git a/openspec/changes/update-exchange-inherited-shop-hint/proposal.md b/openspec/changes/update-exchange-inherited-shop-hint/proposal.md new file mode 100644 index 0000000..dd2597d --- /dev/null +++ b/openspec/changes/update-exchange-inherited-shop-hint/proposal.md @@ -0,0 +1,22 @@ +# Change: 换货新资产归属继承提示 + +## Why + +当前换货流程在选择新资产后,页面没有明确告知换货完成后的最终归属店铺,运营人员容易误以为可以手工选择目标店铺,进而对换货结果产生误判。 + +## What Changes + +- 在换货创建入口、发货确认入口、换货详情页补充旧资产所属店铺和新资产继承归属店铺的展示。 +- 保持现有换货创建、发货、确认完成接口和入参语义不变,不新增目标店铺选择控件或目标店铺提交字段。 +- 扩展换货响应数据,返回 `inherited_shop_id` 和 `inherited_shop_name` 供前端统一展示最终归属店铺。 +- 在创建、发货、确认完成流程中补充提交中禁用、防重复提交、后端归属校验失败提示,以及成功后的详情刷新。 + +## Impact + +- Affected specs: `exchange-management` +- Related active changes: `update-exchange-flow-direct-type`(本提案建立在现有换货流程类型改造之上,需合并考虑) +- Affected code: + - `src/api/modules/exchange.ts` + - `src/views/asset-management/exchange-management/index.vue` + - `src/views/asset-management/exchange-management/detail.vue` + - 相关旧资产/新资产搜索结果类型或映射逻辑(如需补充店铺名称展示数据) diff --git a/openspec/changes/update-exchange-inherited-shop-hint/specs/exchange-management/spec.md b/openspec/changes/update-exchange-inherited-shop-hint/specs/exchange-management/spec.md new file mode 100644 index 0000000..05a5e7d --- /dev/null +++ b/openspec/changes/update-exchange-inherited-shop-hint/specs/exchange-management/spec.md @@ -0,0 +1,88 @@ +## ADDED Requirements + +### Requirement: 换货归属继承上下文展示 + +系统 SHALL 在换货创建和发货确认流程中展示旧资产当前所属店铺,并在用户选定新资产后以只读方式提示换货完成后的继承归属店铺,避免用户误认为可以手工指定目标店铺。 + +#### Scenario: 创建入口展示旧资产所属店铺 + +- **GIVEN** 用户进入现有换货创建入口 +- **WHEN** 用户选择旧资产 +- **THEN** 系统 MUST 显示该旧资产当前所属店铺名称 +- **AND** 该所属店铺信息 MUST be read-only + +#### Scenario: 选择新资产后展示继承归属提示 + +- **GIVEN** 用户正在换货创建或发货确认流程中操作 +- **WHEN** 用户选择新资产 +- **THEN** 系统 MUST 显示只读提示 `换货完成后将归属:{店铺名称}` +- **AND** `{店铺名称}` MUST 表示该新资产换货完成后的最终归属店铺 + +#### Scenario: 页面不提供目标店铺选择控件 + +- **WHEN** 用户查看换货创建或发货确认入口 +- **THEN** 系统 MUST NOT 渲染目标店铺选择控件 +- **AND** 用户 MUST NOT 能够手工覆盖继承归属店铺 + +### Requirement: 换货归属继承接口契约 + +系统 SHALL 复用现有换货创建、发货和确认完成接口,并在换货数据模型中返回继承后的店铺标识和店铺名称,供创建结果、发货结果、完成结果和详情页统一展示。 + +#### Scenario: 创建请求保持既有字段语义 + +- **WHEN** 前端调用 `POST /api/admin/exchanges` +- **THEN** 请求体 MUST 继续沿用旧资产、新资产、换货类型和资料迁移字段 +- **AND** 请求体 MUST NOT 新增目标店铺字段 + +#### Scenario: 发货和完成请求不新增目标店铺字段 + +- **WHEN** 前端调用 `POST /api/admin/exchanges/{id}/ship` 或 `POST /api/admin/exchanges/{id}/complete` +- **THEN** 请求体 MUST NOT 新增目标店铺字段 +- **AND** 后端 MUST 继续按现有换货流程处理归属继承 + +#### Scenario: 换货响应返回继承店铺字段 + +- **WHEN** 前端收到换货创建、发货、确认完成结果或读取换货详情 +- **THEN** 返回的 `data` MUST 包含 `inherited_shop_id:int64` 和 `inherited_shop_name:string` +- **AND** `inherited_shop_name` MUST 表示换货完成后新资产最终归属的店铺名称 + +### Requirement: 换货详情继承归属展示 + +系统 SHALL 在换货详情页展示继承后的店铺名称,并在关键操作成功后刷新详情数据,保证页面展示的归属信息与后端一致。 + +#### Scenario: 详情页展示继承后的店铺名称 + +- **GIVEN** 用户进入换货详情页 +- **WHEN** 详情数据加载成功 +- **THEN** 页面 MUST 展示继承后的店铺名称 +- **AND** 页面 MUST NOT 隐藏该归属字段 + +#### Scenario: 发货或确认完成成功后刷新详情 + +- **GIVEN** 用户完成换货发货或确认完成操作 +- **WHEN** 接口返回成功 +- **THEN** 系统 MUST 重新拉取该换货单详情 +- **AND** 刷新后的详情 MUST 展示最新的继承归属店铺信息 + +### Requirement: 换货归属继承错误与提交状态处理 + +系统 SHALL 在新资产归属校验失败时直接展示后端错误,并在创建、发货、确认完成提交期间禁用操作按钮,防止重复提交,同时保证失败后可以重试。 + +#### Scenario: 新资产属于其他店铺时展示后端错误 + +- **GIVEN** 用户选择了不允许用于当前换货流程的新资产 +- **WHEN** 后端返回“新资产属于其他店铺”或等价归属校验错误 +- **THEN** 系统 MUST 直接展示后端错误信息 +- **AND** 系统 MUST NOT 提供手工修改目标店铺的替代入口 + +#### Scenario: 提交期间禁用按钮防止重复提交 + +- **WHEN** 用户提交创建、发货或确认完成操作 +- **THEN** 当前操作按钮 MUST 进入禁用或 loading 状态直到请求结束 +- **AND** 在请求完成前的重复点击 MUST NOT 触发第二次提交 + +#### Scenario: 请求失败后恢复可重试状态 + +- **WHEN** 创建、发货或确认完成请求失败 +- **THEN** 系统 MUST 恢复当前操作按钮可用状态 +- **AND** 页面 MUST 保留当前已展示的资产与归属提示,便于用户修正后重试 diff --git a/openspec/changes/update-exchange-inherited-shop-hint/tasks.md b/openspec/changes/update-exchange-inherited-shop-hint/tasks.md new file mode 100644 index 0000000..b058979 --- /dev/null +++ b/openspec/changes/update-exchange-inherited-shop-hint/tasks.md @@ -0,0 +1,22 @@ +## 1. API Contract + +- [x] 1.1 更新换货数据模型,补充 `inherited_shop_id` 和 `inherited_shop_name` 响应字段,并覆盖创建、发货、确认完成及详情读取场景。 +- [x] 1.2 保持 `POST /api/admin/exchanges`、`POST /api/admin/exchanges/{id}/ship`、`POST /api/admin/exchanges/{id}/complete` 的既有入参语义,不新增目标店铺字段。 + +## 2. Exchange Create And Ship UI + +- [x] 2.1 在换货创建入口中,选择旧资产后展示旧资产所属店铺。 +- [x] 2.2 在换货创建入口和发货确认入口中,选择新资产后展示只读提示 `换货完成后将归属:{店铺名称}`。 +- [x] 2.3 确保上述页面不新增目标店铺选择控件,也不允许手工覆盖继承归属。 + +## 3. Exchange Detail And Submission States + +- [x] 3.1 在换货详情页展示继承后的店铺名称,并在发货或确认完成成功后重新拉取详情。 +- [x] 3.2 在创建、发货、确认完成提交期间禁用操作按钮,防止重复提交。 +- [x] 3.3 在新资产归属校验失败时展示后端错误,并在失败后恢复可重试状态。 + +## 4. Verification + +- [x] 4.1 验证创建、发货确认、详情三个入口均能正确展示旧资产所属店铺和继承归属店铺。 +- [x] 4.2 验证加载中、失败提示、重复提交防护和成功后详情刷新的交互状态。 +- [x] 4.3 运行相关前端校验,并执行 `openspec validate update-exchange-inherited-shop-hint --strict`。 diff --git a/src/api/modules/asset.ts b/src/api/modules/asset.ts index 5e83a46..540e15d 100644 --- a/src/api/modules/asset.ts +++ b/src/api/modules/asset.ts @@ -54,9 +54,14 @@ export class AssetService extends BaseService { */ static resolveAsset( identifier: string, - params?: AssetResolveParams + params?: AssetResolveParams, + config?: Record ): Promise> { - return this.getOne(`/api/admin/assets/resolve/${identifier}`, params) + return this.getOne( + `/api/admin/assets/resolve/${identifier}`, + params, + config + ) } /** diff --git a/src/api/modules/exchange.ts b/src/api/modules/exchange.ts index d6ae11f..5bc8dcb 100644 --- a/src/api/modules/exchange.ts +++ b/src/api/modules/exchange.ts @@ -47,6 +47,8 @@ export interface ExchangeResponse { recipient_address?: string express_company?: string express_no?: string + inherited_shop_id?: number | null + inherited_shop_name?: string | null remark?: string created_at: string updated_at: string @@ -114,8 +116,8 @@ export class ExchangeService extends BaseService { * POST /api/admin/exchanges/{id}/complete * @param id 换货单ID */ - static completeExchange(id: number): Promise { - return this.post(`/api/admin/exchanges/${id}/complete`, {}) + static completeExchange(id: number): Promise> { + return this.post>(`/api/admin/exchanges/${id}/complete`, {}) } /** diff --git a/src/views/asset-management/exchange-management/detail.vue b/src/views/asset-management/exchange-management/detail.vue index c574693..f468540 100644 --- a/src/views/asset-management/exchange-management/detail.vue +++ b/src/views/asset-management/exchange-management/detail.vue @@ -45,6 +45,12 @@ const exchangeDetail = ref(null) const exchangeId = ref(0) + const formatExchangeShopName = (shopName?: string | null, shopId?: number | null) => { + if (shopName) return shopName + if (shopId === null || shopId === 0) return '平台' + return '--' + } + const getStatusType = (status: number): TagProps['type'] => { const types: Record = { 1: 'warning', @@ -96,6 +102,11 @@ label: '新资产标识符', formatter: (value) => value || '--', prop: 'new_asset_identifier' + }, + { + label: '继承归属店铺', + formatter: (_, data) => + formatExchangeShopName(data.inherited_shop_name, data.inherited_shop_id) } ] }) diff --git a/src/views/asset-management/exchange-management/index.vue b/src/views/asset-management/exchange-management/index.vue index a341a31..4adffdc 100644 --- a/src/views/asset-management/exchange-management/index.vue +++ b/src/views/asset-management/exchange-management/index.vue @@ -170,6 +170,9 @@ + + + + +
{{ createDirectInheritedShopHint }}
+
@@ -250,7 +256,9 @@