This commit is contained in:
@@ -0,0 +1,41 @@
|
||||
# Change: 补充 IoT 卡网关 IMEI 展示
|
||||
|
||||
## Why
|
||||
|
||||
后端新增了 `gateway_card_imei` 字段,但后台当前还没有完整消费这组返回:
|
||||
|
||||
- `GET /api/admin/iot-cards/standalone` 已为 IoT 卡列表返回 `gateway_card_imei`
|
||||
- `GET /api/admin/assets/resolve/:identifier` 已可为卡资产详情返回 `gateway_card_imei`
|
||||
- `GET /api/admin/assets/:identifier/realtime-status` 也可返回 `gateway_card_imei`
|
||||
|
||||
如果前端不补齐这部分契约与展示规范,运营人员无法在 IoT 卡列表和卡资产详情中稳定查看对应 IMEI,也无法在资产详情首个接口缺值时回退到实时状态结果。
|
||||
|
||||
## What Changes
|
||||
|
||||
- 为 `iot-card-management` 新增 `gateway_card_imei` 列展示规范:
|
||||
- IoT 卡管理列表新增 `IMEI` 列
|
||||
- 列值直接展示 `GET /api/admin/iot-cards/standalone` 返回的 `gateway_card_imei`
|
||||
- 当字段为空时显示 `-`
|
||||
- 为 `asset-information` 新增卡资产 `gateway_card_imei` 契约与展示规范:
|
||||
- 仅卡资产详情在“卡信息”区域新增 `IMEI` 展示项
|
||||
- 优先使用 `GET /api/admin/assets/resolve/:identifier` 返回的 `gateway_card_imei`
|
||||
- 仅当详情响应未返回有效 `gateway_card_imei` 时,回退使用 `GET /api/admin/assets/:identifier/realtime-status`
|
||||
- 当两个接口都没有有效值时显示 `-`
|
||||
- 设备资产详情不新增该字段展示
|
||||
|
||||
## Impact
|
||||
|
||||
- Affected specs:
|
||||
- `iot-card-management`
|
||||
- `asset-information`
|
||||
- Affected code:
|
||||
- `src/types/api/card.ts`
|
||||
- `src/views/asset-management/iot-card-management/index.vue`
|
||||
- `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/iot-cards/standalone`
|
||||
- `GET /api/admin/assets/resolve/:identifier`
|
||||
- `GET /api/admin/assets/:identifier/realtime-status`
|
||||
@@ -0,0 +1,46 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Admin Card Asset Gateway IMEI Contract
|
||||
|
||||
The admin asset information integration SHALL accept and preserve `gateway_card_imei` for card assets, using the asset detail response as the primary source and the realtime-status response only as a fallback.
|
||||
|
||||
#### Scenario: Card asset detail preserves gateway_card_imei from resolve
|
||||
|
||||
- **GIVEN** 用户正在查看后台“资产信息”中的卡资产详情
|
||||
- **AND** `GET /api/admin/assets/resolve/:identifier` 返回了有效 `gateway_card_imei`
|
||||
- **WHEN** 前端解析卡资产详情响应
|
||||
- **THEN** 页面状态 MUST 保留该 `gateway_card_imei`
|
||||
- **AND** 后续展示 MUST 优先使用该详情值
|
||||
|
||||
#### Scenario: Card asset detail falls back to realtime-status when resolve lacks gateway_card_imei
|
||||
|
||||
- **GIVEN** 用户正在查看后台“资产信息”中的卡资产详情
|
||||
- **AND** `GET /api/admin/assets/resolve/:identifier` 未返回有效 `gateway_card_imei`
|
||||
- **AND** `GET /api/admin/assets/:identifier/realtime-status` 返回了有效 `gateway_card_imei`
|
||||
- **WHEN** 前端合并实时状态到当前卡资产详情状态
|
||||
- **THEN** 页面状态 MUST 使用 realtime-status 返回的 `gateway_card_imei` 作为回退值
|
||||
|
||||
### Requirement: Admin Card Asset Gateway IMEI Display
|
||||
|
||||
The admin asset information view SHALL display `gateway_card_imei` as `IMEI` only within the card asset `卡信息` section.
|
||||
|
||||
#### Scenario: Show gateway_card_imei in card information
|
||||
|
||||
- **GIVEN** 用户正在查看后台“资产信息”中的卡资产详情
|
||||
- **AND** 当前卡资产详情状态中存在有效 `gateway_card_imei`
|
||||
- **WHEN** 页面渲染“卡信息”区域
|
||||
- **THEN** 页面 MUST 提供 `IMEI` 展示项
|
||||
- **AND** 展示内容 MUST 与当前状态中的 `gateway_card_imei` 保持一致
|
||||
|
||||
#### Scenario: Show placeholder when no gateway_card_imei is available
|
||||
|
||||
- **GIVEN** 用户正在查看后台“资产信息”中的卡资产详情
|
||||
- **AND** `GET /api/admin/assets/resolve/:identifier` 与 `GET /api/admin/assets/:identifier/realtime-status` 都未返回有效 `gateway_card_imei`
|
||||
- **WHEN** 页面渲染“卡信息”区域中的 `IMEI`
|
||||
- **THEN** 页面 MUST 显示 `-`
|
||||
|
||||
#### Scenario: Device asset detail does not render gateway_card_imei
|
||||
|
||||
- **GIVEN** 用户正在查看后台“资产信息”中的设备资产详情
|
||||
- **WHEN** 页面渲染设备基础信息区域
|
||||
- **THEN** 页面 MUST NOT 因 `gateway_card_imei` 新增 `IMEI` 展示项
|
||||
@@ -0,0 +1,20 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: IoT Card Management Gateway IMEI Column
|
||||
|
||||
The IoT card management page SHALL display `gateway_card_imei` from `GET /api/admin/iot-cards/standalone` as an `IMEI` column in the list.
|
||||
|
||||
#### Scenario: Show IMEI when standalone list returns gateway_card_imei
|
||||
|
||||
- **GIVEN** 用户正在使用 IoT 卡管理列表
|
||||
- **AND** `GET /api/admin/iot-cards/standalone` 返回的某条卡记录包含 `gateway_card_imei`
|
||||
- **WHEN** 页面渲染列表行数据
|
||||
- **THEN** 页面 MUST 提供 `IMEI` 列
|
||||
- **AND** 对应行 MUST 直接展示该条记录返回的 `gateway_card_imei`
|
||||
|
||||
#### Scenario: Show placeholder when gateway_card_imei is missing
|
||||
|
||||
- **GIVEN** 用户正在使用 IoT 卡管理列表
|
||||
- **AND** `GET /api/admin/iot-cards/standalone` 返回的某条卡记录未返回有效 `gateway_card_imei`
|
||||
- **WHEN** 页面渲染该列表行
|
||||
- **THEN** 该行的 `IMEI` 列 MUST 显示 `-`
|
||||
18
openspec/changes/update-card-gateway-imei-display/tasks.md
Normal file
18
openspec/changes/update-card-gateway-imei-display/tasks.md
Normal file
@@ -0,0 +1,18 @@
|
||||
## 1. Contract And State
|
||||
|
||||
- [x] 1.1 扩展 IoT 卡列表类型契约,支持 `gateway_card_imei` 并在列表数据映射中保留该字段。
|
||||
- [x] 1.2 扩展卡资产详情与实时状态相关类型契约,支持 `gateway_card_imei`。
|
||||
- [x] 1.3 调整卡资产详情状态合并逻辑,确保优先使用 `resolve` 返回值,仅在其缺失时回退到 `realtime-status` 返回值。
|
||||
|
||||
## 2. UI
|
||||
|
||||
- [x] 2.1 在 IoT 卡管理列表新增 `IMEI` 列,展示 `gateway_card_imei`,空值显示 `-`。
|
||||
- [x] 2.2 在卡资产详情“卡信息”区域新增 `IMEI` 展示项,按 `resolve -> realtime-status -> -` 的顺序取值。
|
||||
- [x] 2.3 确认设备资产详情不因 `gateway_card_imei` 新增任何基础信息展示项。
|
||||
|
||||
## 3. Verification
|
||||
|
||||
- [ ] 3.1 验证 `GET /api/admin/iot-cards/standalone` 返回 `gateway_card_imei` 时,IoT 卡列表 `IMEI` 列展示正确。
|
||||
- [ ] 3.2 验证卡资产详情在 `GET /api/admin/assets/resolve/:identifier` 返回 `gateway_card_imei` 时,页面优先展示该值。
|
||||
- [ ] 3.3 验证卡资产详情在 `resolve` 未返回有效值、但 `GET /api/admin/assets/:identifier/realtime-status` 返回 `gateway_card_imei` 时,页面回退展示实时值。
|
||||
- [ ] 3.4 验证两个资产接口都未返回有效 `gateway_card_imei` 时,卡资产详情显示 `-`,且设备资产详情不展示该字段。
|
||||
@@ -89,6 +89,7 @@ export interface AssetResolveResponse {
|
||||
supplier?: string // 供应商
|
||||
activation_status?: number // 激活状态
|
||||
enable_polling?: boolean // 是否参与轮询
|
||||
gateway_card_imei?: string // Gateway 返回的卡 IMEI
|
||||
gateway_extend?: string // Gateway 卡状态扩展字段,原样返回上游 extend
|
||||
|
||||
// ===== 设备专属字段 (asset_type === 'device' 时) =====
|
||||
@@ -181,6 +182,7 @@ export interface AssetRealtimeStatusResponse {
|
||||
network_status?: NetworkStatus // 网络状态(仅 card)
|
||||
real_name_status?: RealNameStatus // 实名状态(仅 card)
|
||||
realname_policy?: string // 实名认证策略(仅 card)
|
||||
gateway_card_imei?: string // Gateway 返回的卡 IMEI
|
||||
current_month_usage_mb?: number // 本月已用流量 MB(仅 card)
|
||||
last_gateway_reading_mb?: number // 运营商周期月内已用流量 MB(仅 card)
|
||||
last_sync_time?: string | null // 最后同步时间(仅 card)
|
||||
|
||||
@@ -353,6 +353,7 @@ export interface StandaloneCardQueryParams extends PaginationParams {
|
||||
|
||||
// 单卡信息
|
||||
export interface StandaloneIotCard {
|
||||
gateway_card_imei?: string // 网关侧返回的卡 IMEI(可空)
|
||||
id: number // 卡ID
|
||||
iccid: string // ICCID
|
||||
imsi?: string // IMSI (可选)
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
<!-- 卡专属字段 -->
|
||||
<template v-if="cardInfo?.asset_type === 'card'">
|
||||
<ElDescriptionsItem label="ICCID">{{ cardInfo?.iccid || '-' }}</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="IMEI">{{ cardInfo?.gateway_card_imei || '-' }}</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="运营商账户">{{
|
||||
cardInfo?.carrier_name || '-'
|
||||
}}</ElDescriptionsItem>
|
||||
@@ -521,6 +522,7 @@
|
||||
series_name?: string
|
||||
real_name_at?: string | null
|
||||
supplier?: string
|
||||
gateway_card_imei?: string
|
||||
gateway_extend?: string
|
||||
last_sync_time?: string | null
|
||||
last_data_check_at?: string | null
|
||||
|
||||
@@ -20,6 +20,9 @@ type TrafficDisplaySource = {
|
||||
enable_virtual_data?: boolean | null
|
||||
}
|
||||
|
||||
const normalizeOptionalText = (value?: string | null) =>
|
||||
typeof value === 'string' ? value.trim() : ''
|
||||
|
||||
const NULLABLE_REALTIME_CARD_FIELDS = new Set([
|
||||
'last_data_check_at',
|
||||
'last_real_name_check_at',
|
||||
@@ -92,6 +95,7 @@ export function useAssetInfo() {
|
||||
|
||||
if (response.code === 0 && response.data) {
|
||||
const data = response.data
|
||||
const resolvedGatewayCardImei = normalizeOptionalText(data.gateway_card_imei)
|
||||
|
||||
// 获取资产标识符
|
||||
const assetIdentifier = data.asset_type === 'card' ? data.iccid : data.virtual_no
|
||||
@@ -128,6 +132,8 @@ export function useAssetInfo() {
|
||||
carrier_name: data.carrier_name || '',
|
||||
supplier: data.supplier || '',
|
||||
network_status: data.network_status,
|
||||
gateway_card_imei: resolvedGatewayCardImei,
|
||||
resolved_gateway_card_imei: resolvedGatewayCardImei,
|
||||
gateway_extend: data.gateway_extend ?? '',
|
||||
bound_device_id: data.bound_device_id,
|
||||
bound_device_no: data.bound_device_no || '',
|
||||
@@ -307,6 +313,13 @@ export function useAssetInfo() {
|
||||
if (data.real_name_status !== undefined) {
|
||||
cardInfo.value.real_name_status = data.real_name_status
|
||||
}
|
||||
if ('gateway_card_imei' in data) {
|
||||
const resolvedGatewayCardImei = normalizeOptionalText(
|
||||
cardInfo.value?.resolved_gateway_card_imei
|
||||
)
|
||||
cardInfo.value.gateway_card_imei =
|
||||
resolvedGatewayCardImei || normalizeOptionalText(data.gateway_card_imei)
|
||||
}
|
||||
if (data.last_sync_time !== undefined) {
|
||||
cardInfo.value.last_sync_time = data.last_sync_time
|
||||
}
|
||||
|
||||
@@ -28,6 +28,7 @@ export interface AssetInfo {
|
||||
sn?: string
|
||||
cards?: BindingCard[]
|
||||
status?: number
|
||||
gateway_card_imei?: string
|
||||
gateway_extend?: string
|
||||
last_sync_time?: string | null
|
||||
last_data_check_at?: string | null
|
||||
|
||||
@@ -1094,6 +1094,7 @@ const formItems: SearchFormItem[] = baseFormItems
|
||||
// 列配置
|
||||
const columnOptions = [
|
||||
{ label: 'ICCID', prop: 'iccid' },
|
||||
{ label: 'IMEI', prop: 'gateway_card_imei' },
|
||||
{ label: 'MSISDN', prop: 'msisdn' },
|
||||
{ label: '卡虚拟号', prop: 'virtual_no' },
|
||||
{ label: '运营商', prop: 'carrier_name' },
|
||||
@@ -1226,6 +1227,13 @@ const { columnChecks, columns } = useCheckedColumns(() => [
|
||||
)
|
||||
}
|
||||
},
|
||||
{
|
||||
prop: 'gateway_card_imei',
|
||||
label: 'IMEI',
|
||||
width: 180,
|
||||
showOverflowTooltip: true,
|
||||
formatter: (row: StandaloneIotCard) => row.gateway_card_imei || '-'
|
||||
},
|
||||
{
|
||||
prop: 'msisdn',
|
||||
label: 'MSISDN',
|
||||
|
||||
Reference in New Issue
Block a user