feat: 资产详情前代后代换货标识
All checks were successful
构建并部署前端到测试环境 / build-and-deploy (push) Successful in 5m17s

This commit is contained in:
luo
2026-07-21 11:13:20 +08:00
parent eddfd157ad
commit 45d255cabf
7 changed files with 196 additions and 2 deletions

View File

@@ -55,6 +55,21 @@ export interface AssetPollingStatus {
next_poll_at?: string | null // 下次轮询时间
}
// 换货关联资产
export interface AssetExchangeTraceAsset {
asset_type: AssetType
asset_id: number | null
identifier: string
exchange_no: string
can_view: boolean
}
// 换货前后代链路
export interface AssetExchangeTrace {
previous_asset?: AssetExchangeTraceAsset | null
next_asset?: AssetExchangeTraceAsset | null
}
/**
* 资产解析/详情响应
* 对应接口GET /api/admin/assets/resolve/:identifier
@@ -90,6 +105,7 @@ export interface AssetResolveResponse {
accumulated_recharge?: number // 累计充值金额(分)
first_commission_paid?: boolean // 一次性佣金是否已发放
polling?: AssetPollingStatus | null // 资产轮询状态
exchange_trace?: AssetExchangeTrace | null // 换货前后代链路
// ===== 卡专属字段 (asset_type === 'card' 时) =====
iccid?: string // ICCID

View File

@@ -214,6 +214,46 @@
</ElDescriptionsItem>
</ElDescriptions>
<template v-if="previousExchangeAsset || nextExchangeAsset">
<ElDivider content-position="left">换货链路</ElDivider>
<ElDescriptions :column="descriptionsColumn" border>
<ElDescriptionsItem v-if="previousExchangeAsset" label="前代资产">
<div class="exchange-asset">
<ElTag type="success" size="small">换货新资产</ElTag>
<ElButton
v-if="previousExchangeAsset.can_view"
type="primary"
link
@click="handleNavigateToExchangeAsset(previousExchangeAsset)"
>
{{ previousExchangeAsset.identifier }}
</ElButton>
<span v-else>{{ previousExchangeAsset.identifier }}</span>
<span v-if="previousExchangeAsset.exchange_no" class="exchange-no">
换货单{{ previousExchangeAsset.exchange_no }}
</span>
</div>
</ElDescriptionsItem>
<ElDescriptionsItem v-if="nextExchangeAsset" label="后代资产">
<div class="exchange-asset">
<ElTag type="warning" size="small">已换出旧资产</ElTag>
<ElButton
v-if="nextExchangeAsset.can_view"
type="primary"
link
@click="handleNavigateToExchangeAsset(nextExchangeAsset)"
>
{{ nextExchangeAsset.identifier }}
</ElButton>
<span v-else>{{ nextExchangeAsset.identifier }}</span>
<span v-if="nextExchangeAsset.exchange_no" class="exchange-no">
换货单{{ nextExchangeAsset.exchange_no }}
</span>
</div>
</ElDescriptionsItem>
</ElDescriptions>
</template>
<!-- 设备绑定卡列表 -->
<template v-if="cardInfo?.asset_type === 'device'">
<ElDivider content-position="left">绑定卡列表</ElDivider>
@@ -532,7 +572,7 @@
ElMessage
} from 'element-plus'
import { CopyDocument } from '@element-plus/icons-vue'
import type { AssetPollingStatus } from '@/types/api'
import type { AssetExchangeTrace, AssetExchangeTraceAsset, AssetPollingStatus } from '@/types/api'
import { useAssetFormatters } from '../composables/useAssetFormatters'
import { useAuth } from '@/composables/useAuth'
import { useUserStore } from '@/store/modules/user'
@@ -589,6 +629,7 @@
last_real_name_check_at?: string | null
last_card_status_check_at?: string | null
polling?: AssetPollingStatus | null
exchange_trace?: AssetExchangeTrace | null
bound_device_id?: number
bound_device_no?: string
bound_device_name?: string
@@ -759,6 +800,11 @@
})
})
const previousExchangeAsset = computed(
() => props.cardInfo.exchange_trace?.previous_asset ?? null
)
const nextExchangeAsset = computed(() => props.cardInfo.exchange_trace?.next_asset ?? null)
// 设备详情里的实名状态和实名时间从选中的绑定卡取值:
// 只有一张卡时直接取该卡,多张卡时仅取 is_current=true 的卡。
const selectedDeviceStatusCard = computed(() => {
@@ -797,6 +843,17 @@
emit('navigateToCard', iccid)
}
const handleNavigateToExchangeAsset = (asset: AssetExchangeTraceAsset) => {
if (!asset.can_view) return
if (asset.asset_type === 'card') {
emit('navigateToCard', asset.identifier)
return
}
emit('navigateToDevice', asset.identifier)
}
// 复制ICCID
const handleCopyICCID = async (iccid: string) => {
try {
@@ -956,6 +1013,17 @@
}
}
.exchange-asset {
display: flex;
flex-wrap: wrap;
gap: 8px;
align-items: center;
.exchange-no {
color: var(--el-text-color-secondary);
}
}
@media (width <= 768px) {
.card-header {
flex-direction: column;

View File

@@ -124,6 +124,7 @@ export function useAssetInfo() {
accumulated_recharge: data.accumulated_recharge,
first_commission_paid: data.first_commission_paid,
polling: data.polling ?? null,
exchange_trace: data.exchange_trace ?? null,
// 卡专属字段 (asset_type === 'card')
iccid: data.iccid || '',

View File

@@ -2,7 +2,7 @@
* 资产信息页面类型定义
*/
import type { AssetPollingStatus } from '@/types/api'
import type { AssetExchangeTrace, AssetPollingStatus } from '@/types/api'
// 资产类型枚举
export type AssetType = 'card' | 'device'
@@ -40,6 +40,7 @@ export interface AssetInfo {
last_real_name_check_at?: string | null
last_card_status_check_at?: string | null
polling?: AssetPollingStatus | null
exchange_trace?: AssetExchangeTrace | null
}
// 设备绑定卡信息