From dfb78006acf14b71eb268ac2e0ed86ce084d894c Mon Sep 17 00:00:00 2001 From: sexygoat <1538832180@qq.com> Date: Thu, 21 May 2026 14:39:21 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E5=88=86=E9=A1=B5=E5=99=A8=E4=B8=80?= =?UTF-8?q?=E7=9B=B4=E6=98=BE=E7=A4=BA=E5=92=8C=E8=B5=84=E4=BA=A7=E4=BF=A1?= =?UTF-8?q?=E6=81=AF=E4=BD=BF=E7=94=A8=E5=8D=95=E5=8D=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../proposal.md | 43 ++++++++++++++ .../specs/asset-information/spec.md | 56 +++++++++++++++++++ .../tasks.md | 21 +++++++ src/components/core/tables/ArtTable.vue | 2 +- src/types/api/asset.ts | 1 + .../components/BasicInfoCard.vue | 36 +++++++++--- .../composables/useAssetInfo.ts | 4 +- .../asset-information/types.ts | 1 + 8 files changed, 152 insertions(+), 12 deletions(-) create mode 100644 openspec/changes/update-asset-device-detail-bound-card-status-source/proposal.md create mode 100644 openspec/changes/update-asset-device-detail-bound-card-status-source/specs/asset-information/spec.md create mode 100644 openspec/changes/update-asset-device-detail-bound-card-status-source/tasks.md diff --git a/openspec/changes/update-asset-device-detail-bound-card-status-source/proposal.md b/openspec/changes/update-asset-device-detail-bound-card-status-source/proposal.md new file mode 100644 index 0000000..b27006f --- /dev/null +++ b/openspec/changes/update-asset-device-detail-bound-card-status-source/proposal.md @@ -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;本次仅调整后台设备详情字段取值口径 diff --git a/openspec/changes/update-asset-device-detail-bound-card-status-source/specs/asset-information/spec.md b/openspec/changes/update-asset-device-detail-bound-card-status-source/specs/asset-information/spec.md new file mode 100644 index 0000000..187115f --- /dev/null +++ b/openspec/changes/update-asset-device-detail-bound-card-status-source/specs/asset-information/spec.md @@ -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` diff --git a/openspec/changes/update-asset-device-detail-bound-card-status-source/tasks.md b/openspec/changes/update-asset-device-detail-bound-card-status-source/tasks.md new file mode 100644 index 0000000..6f92df5 --- /dev/null +++ b/openspec/changes/update-asset-device-detail-bound-card-status-source/tasks.md @@ -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` 或任意非当前卡值。 diff --git a/src/components/core/tables/ArtTable.vue b/src/components/core/tables/ArtTable.vue index 800c1aa..756ed3a 100644 --- a/src/components/core/tables/ArtTable.vue +++ b/src/components/core/tables/ArtTable.vue @@ -184,7 +184,7 @@ total: 0, currentPage: 1, pageSize: 20, - hideOnSinglePage: true, + hideOnSinglePage: false, pageSizes: () => [10, 20, 30, 50], paginationAlign: 'center', paginationSize: 'default', diff --git a/src/types/api/asset.ts b/src/types/api/asset.ts index e6ed1b9..8014be3 100644 --- a/src/types/api/asset.ts +++ b/src/types/api/asset.ts @@ -118,6 +118,7 @@ export interface AssetBoundCard { msisdn: string // 手机号 network_status: NetworkStatus // 网络状态 real_name_status: RealNameStatus // 实名状态 + real_name_at?: string | null // 实名时间 realname_policy?: string // 实名认证策略 slot_position: number // 插槽位置 is_current?: boolean // 是否为设备当前使用的卡 diff --git a/src/views/asset-management/asset-information/components/BasicInfoCard.vue b/src/views/asset-management/asset-information/components/BasicInfoCard.vue index 63fee91..9ee8e47 100644 --- a/src/views/asset-management/asset-information/components/BasicInfoCard.vue +++ b/src/views/asset-management/asset-information/components/BasicInfoCard.vue @@ -107,9 +107,14 @@ - - {{ getRealNameStatusName(currentCard?.real_name_status) }} + + {{ getRealNameStatusName(selectedDeviceStatusCard.real_name_status) }} + - {{ cardInfo?.activation_status_name || '-' }} @@ -119,12 +124,19 @@ cardInfo?.device_model || '-' }} {{ - cardInfo?.real_name_at ? formatDateTime(cardInfo.real_name_at) : '-' + selectedDeviceStatusCard?.real_name_at + ? formatDateTime(selectedDeviceStatusCard.real_name_at) + : '-' }} - - {{ getOnlineStatusName(cardInfo?.online_status) }} + + {{ selectedDeviceStatusCard.network_status === 1 ? '正常' : '停机' }} + - {{ cardInfo?.virtual_no || '-' }} {{ @@ -478,7 +490,7 @@ is_current: boolean network_status?: number real_name_status?: number - real_name_at?: string + real_name_at?: string | null realname_policy?: string gateway_extend?: string last_data_check_at?: string | null @@ -507,7 +519,7 @@ status?: number shop_name?: string series_name?: string - real_name_at?: string + real_name_at?: string | null supplier?: string gateway_extend?: string last_sync_time?: string | null @@ -639,11 +651,17 @@ }) }) - // 获取当前使用的卡 - const currentCard = computed(() => { + // 设备详情状态字段统一从选中的绑定卡取值: + // 只有一张卡时直接取该卡,多张卡时仅取 is_current=true 的卡。 + const selectedDeviceStatusCard = computed(() => { if (!props.cardInfo?.cards || props.cardInfo.cards.length === 0) { return null } + + if (props.cardInfo.cards.length === 1) { + return props.cardInfo.cards[0] + } + return props.cardInfo.cards.find((card: BindingCard) => card.is_current) || null }) diff --git a/src/views/asset-management/asset-information/composables/useAssetInfo.ts b/src/views/asset-management/asset-information/composables/useAssetInfo.ts index 4f9164f..c8ae6dd 100644 --- a/src/views/asset-management/asset-information/composables/useAssetInfo.ts +++ b/src/views/asset-management/asset-information/composables/useAssetInfo.ts @@ -331,8 +331,8 @@ export function useAssetInfo() { cardInfo.value.device_protect_status = data.device_protect_status } if (data.cards && data.cards.length > 0) { - // 合并实时卡数据与原始卡数据,保留 real_name_at 等静态字段 - // 仅用实时接口中非 null/undefined 的字段覆盖,避免清掉 resolve 接口返回的静态数据 + // 合并实时卡数据与原始卡数据,保留 real_name_at 等静态字段。 + // 设备详情的实名状态、在线状态、实名时间统一依赖选中的绑定卡,因此不能在实时刷新时丢掉这些字段。 cardInfo.value.cards = mergeAssetBoundCards(cardInfo.value.cards || [], data.cards) } if (data.last_data_check_at !== undefined) { diff --git a/src/views/asset-management/asset-information/types.ts b/src/views/asset-management/asset-information/types.ts index d674621..2223046 100644 --- a/src/views/asset-management/asset-information/types.ts +++ b/src/views/asset-management/asset-information/types.ts @@ -47,6 +47,7 @@ export interface BindingCard { slot_position?: number network_status?: number real_name_status?: number + real_name_at?: string | null realname_policy?: string gateway_extend?: string last_data_check_at?: string | null