feat: 卡新增IMEI
All checks were successful
构建并部署前端到测试环境 / build-and-deploy (push) Successful in 5m47s

This commit is contained in:
sexygoat
2026-05-26 18:07:39 +08:00
parent 878d93a865
commit fe0093972f
10 changed files with 152 additions and 0 deletions

View File

@@ -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`

View File

@@ -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` 展示项

View File

@@ -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 显示 `-`

View 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` 时,卡资产详情显示 `-`,且设备资产详情不展示该字段。

View File

@@ -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

View File

@@ -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 (可选)

View File

@@ -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

View File

@@ -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
}

View File

@@ -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

View File

@@ -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',