fix: some
All checks were successful
构建并部署前端到测试环境 / deploy (push) Successful in 54s

This commit is contained in:
luo
2026-10-12 11:43:12 +08:00
parent f1006187f6
commit 52268859aa
9 changed files with 1216 additions and 76 deletions

View File

@@ -95,6 +95,7 @@ declare module 'vue' {
ElAlert: typeof import('element-plus/es')['ElAlert']
ElAvatar: typeof import('element-plus/es')['ElAvatar']
ElButton: typeof import('element-plus/es')['ElButton']
ElCalendar: typeof import('element-plus/es')['ElCalendar']
ElCard: typeof import('element-plus/es')['ElCard']
ElCascader: typeof import('element-plus/es')['ElCascader']
ElCheckbox: typeof import('element-plus/es')['ElCheckbox']
@@ -124,12 +125,15 @@ declare module 'vue' {
ElOption: typeof import('element-plus/es')['ElOption']
ElPagination: typeof import('element-plus/es')['ElPagination']
ElPopover: typeof import('element-plus/es')['ElPopover']
ElProgress: typeof import('element-plus/es')['ElProgress']
ElRadio: typeof import('element-plus/es')['ElRadio']
ElRadioButton: typeof import('element-plus/es')['ElRadioButton']
ElRadioGroup: typeof import('element-plus/es')['ElRadioGroup']
ElResult: typeof import('element-plus/es')['ElResult']
ElRow: typeof import('element-plus/es')['ElRow']
ElScrollbar: typeof import('element-plus/es')['ElScrollbar']
ElSelect: typeof import('element-plus/es')['ElSelect']
ElSkeleton: typeof import('element-plus/es')['ElSkeleton']
ElSubMenu: typeof import('element-plus/es')['ElSubMenu']
ElSwitch: typeof import('element-plus/es')['ElSwitch']
ElTable: typeof import('element-plus/es')['ElTable']

View File

@@ -453,8 +453,8 @@
<div class="binding-card-field">
<span class="binding-card-field-label">网络状态</span>
<span class="binding-card-field-value">
<ElTag :type="bindingCard.network_status === 1 ? 'success' : 'danger'" size="small">
{{ bindingCard.network_status === 1 ? '正常' : '停机' }}
<ElTag :type="getNetworkStatusType(bindingCard.network_status)" size="small">
{{ getNetworkStatusName(bindingCard.network_status) }}
</ElTag>
</span>
</div>
@@ -486,30 +486,28 @@
<div class="binding-card-actions">
<ElButton
v-if="bindingCard.network_status === 0"
v-if="bindingCard.network_status === 0 && hasAuth('iot_info:start')"
type="success"
size="default"
:disabled="isBindingStartDisabled(bindingCard)"
@click="handleEnableBindingCard(bindingCard)"
v-permission="'iot_info:start'"
>
{{ getBindingStartButtonText(bindingCard) }}
</ElButton>
<ElButton
v-if="bindingCard.network_status === 1"
v-if="bindingCard.network_status === 1 && hasAuth('iot_info:stop')"
type="warning"
size="default"
:disabled="isBindingStopDisabled(bindingCard)"
@click="handleDisableBindingCard(bindingCard)"
v-permission="'iot_info:stop'"
>
{{ getBindingStopButtonText(bindingCard) }}
</ElButton>
<ElButton
v-if="hasAuth('bound_card:update_realname_status')"
type="primary"
size="default"
@click="handleUpdateBindingCardRealnameStatus(bindingCard)"
v-permission="'bound_card:update_realname_status'"
>
更新实名状态
</ElButton>
@@ -580,8 +578,7 @@
style="margin-top: 16px; text-align: right"
>
<ElButton
v-if="cardInfo?.network_status === 0"
v-permission="'iot_info:start'"
v-if="cardInfo?.network_status === 0 && hasAuth('iot_info:start')"
type="success"
:disabled="startDisabled"
@click="handleEnableCard"
@@ -589,8 +586,7 @@
{{ startRemainingText ? `启用此卡(${startRemainingText})` : '启用此卡' }}
</ElButton>
<ElButton
v-if="cardInfo?.network_status === 1"
v-permission="'iot_info:stop'"
v-if="cardInfo?.network_status === 1 && hasAuth('iot_info:stop')"
type="warning"
:disabled="stopDisabled"
@click="handleDisableCard"
@@ -601,15 +597,14 @@
<!-- 手动停用暂时不开放 -->
<!--suppress VueUnrecognizedDirective -->
<ElButton
v-if="canShowRealnamePolicy"
v-permission="'asset:card_realname_policy'"
v-if="canShowRealnamePolicy && hasAuth('asset:card_realname_policy')"
type="primary"
@click="handleShowRealnamePolicy"
>
实名认证策略
</ElButton>
<ElButton
v-permission="'iot_info:update_realname_status'"
v-if="hasAuth('iot_info:update_realname_status')"
type="primary"
@click="handleShowUpdateRealnameStatus"
>
@@ -619,28 +614,27 @@
<!-- 设备操作按钮 -->
<div v-else class="device-operations" style="margin-top: 16px; text-align: right">
<ElButton v-permission="'device_info:switch_mode'" type="info" @click="handleShowSwitchMode"
<ElButton v-if="hasAuth('device_info:switch_mode')" type="info" @click="handleShowSwitchMode"
>设置切卡模式</ElButton
>
<ElButton v-permission="'device_info:reboot'" type="primary" @click="handleRebootDevice">
<ElButton v-if="hasAuth('device_info:reboot')" type="primary" @click="handleRebootDevice">
重启设备
</ElButton>
<ElButton v-permission="'device_info:reset'" type="warning" @click="handleResetDevice">
<ElButton v-if="hasAuth('device_info:reset')" type="warning" @click="handleResetDevice">
恢复出厂
</ElButton>
<ElButton
v-permission="'device_info:switch_card'"
v-if="hasAuth('device_info:switch_card')"
type="success"
@click="handleShowSwitchCard"
>
切换SIM卡
</ElButton>
<ElButton v-permission="'device_info:set_wifi'" type="primary" @click="handleShowSetWiFi">
<ElButton v-if="hasAuth('device_info:set_wifi')" type="primary" @click="handleShowSetWiFi">
设置WiFi
</ElButton>
<ElButton
v-if="canShowRealnamePolicy"
v-permission="'asset_info:device_realname_policy'"
v-if="canShowRealnamePolicy && hasAuth('asset_info:device_realname_policy')"
type="primary"
@click="handleShowRealnamePolicy"
>
@@ -944,6 +938,8 @@
formatDuration,
getRealNameStatusName,
getRealNameStatusType,
getNetworkStatusName,
getNetworkStatusType,
getRealnamePolicyName,
getCarrierTypeName,
getAssetStatusName,

View File

@@ -68,6 +68,19 @@ export function useAssetFormatters() {
return map[status] || 'info'
}
// 获取网络状态名称和标签类型,缺失状态不能误报为停机。
const getNetworkStatusName = (status?: number) => {
if (status === 1) return '正常'
if (status === 0) return '停机'
return '未知'
}
const getNetworkStatusType = (status?: number): TagType => {
if (status === 1) return 'success'
if (status === 0) return 'danger'
return 'info'
}
// 获取实名认证策略名称
const getRealnamePolicyName = (policy?: string) => {
if (!policy || policy === '') return '未知'
@@ -261,6 +274,8 @@ export function useAssetFormatters() {
formatIccidWithDashes,
getRealNameStatusName,
getRealNameStatusType,
getNetworkStatusName,
getNetworkStatusType,
getRealnamePolicyName,
getCarrierTypeName,
getAssetStatusName,

View File

@@ -3,9 +3,10 @@
* 负责资产详情数据的加载和状态管理
*/
import { ref } from 'vue'
import { getCurrentScope, onScopeDispose, ref, watch } from 'vue'
import { ElMessage } from 'element-plus'
import { AssetService } from '@/api/modules'
import { useUserStore } from '@/store/modules/user'
import { FrontendRateLimitError } from '@/utils/business/apiRateLimit'
import type {
AssetBoundCard,
@@ -25,13 +26,19 @@ const normalizeOptionalText = (value?: string | null) =>
typeof value === 'string' ? value.trim() : ''
const NULLABLE_REALTIME_CARD_FIELDS = new Set([
'gateway_extend',
'last_data_check_at',
'last_real_name_check_at',
'last_card_status_check_at'
])
// 详情接口是绑定卡实名时间的唯一来源;实时状态接口不应覆盖或保留旧值。
const DETAIL_ONLY_BOUND_CARD_FIELDS = new Set(['real_name_at'])
// 绑定关系和静态信息以详情接口为准,实时接口只补充同一张卡的状态。
const REALTIME_BOUND_CARD_FIELDS = new Set([
'network_status',
'real_name_status',
'is_current',
...NULLABLE_REALTIME_CARD_FIELDS
])
/**
* 格式化数据大小(MB -> GB/MB)
@@ -60,22 +67,24 @@ const getDisplayedUsagePercentage = (traffic?: TrafficDisplaySource | null) => {
const mergeAssetBoundCards = (
existingCards: AssetBoundCard[] = [],
realtimeCards: AssetBoundCard[] = []
): AssetBoundCard[] =>
realtimeCards.map((realtimeCard) => {
const existingCard = existingCards.find((card) => card.iccid === realtimeCard.iccid)
if (!existingCard) {
return { ...realtimeCard, real_name_at: null }
}
): AssetBoundCard[] => {
const realtimeByIccid = new Map(realtimeCards.map((card) => [card.iccid, card]))
const currentCard = existingCards.find(
(card) => realtimeByIccid.get(card.iccid)?.is_current === true
)
return existingCards.map((existingCard) => {
const mergedCard = { ...existingCard } as unknown as Record<string, unknown>
for (const [key, value] of Object.entries(realtimeCard)) {
if (DETAIL_ONLY_BOUND_CARD_FIELDS.has(key)) continue
for (const [key, value] of Object.entries(realtimeByIccid.get(existingCard.iccid) || {})) {
if (!REALTIME_BOUND_CARD_FIELDS.has(key)) continue
if (value !== undefined && (value !== null || NULLABLE_REALTIME_CARD_FIELDS.has(key))) {
mergedCard[key] = value
}
}
if (currentCard) mergedCard.is_current = existingCard.iccid === currentCard.iccid
return mergedCard as unknown as AssetBoundCard
})
}
export function useAssetInfo() {
// 状态管理
@@ -90,22 +99,93 @@ export function useAssetInfo() {
const walletLoading = ref(false)
const packageLoading = ref(false)
const realtimeLoading = ref(false)
let assetVersion = 0
let packageRequestId = 0
let currentPackageRequestId = 0
let realtimeRequestId = 0
let walletRequestId = 0
let disposed = false
const resetAssetInfo = () => {
assetVersion += 1
cardInfo.value = null
deviceRealtime.value = null
currentPackage.value = null
currentPackageErrorMsg.value = ''
packageList.value = []
walletInfo.value = null
loading.value = false
currentPackageLoading.value = false
packageLoading.value = false
realtimeLoading.value = false
walletLoading.value = false
}
// 同一标识符也可能属于不同搜索或登录会话,不能仅比较 identifier。
const captureAssetRequest = (identifier: string) => {
const version = assetVersion
const asset = cardInfo.value
return () =>
!disposed &&
version === assetVersion &&
asset !== null &&
cardInfo.value === asset &&
asset.identifier === identifier
}
const userStore = useUserStore()
watch(
[
() => userStore.info.id,
() => userStore.info.user_type,
() => userStore.info.shop_id,
() => userStore.info.enterprise_id,
() => userStore.isLogin
],
resetAssetInfo,
{ flush: 'sync' }
)
if (getCurrentScope()) {
onScopeDispose(() => {
disposed = true
resetAssetInfo()
})
}
/**
* 获取资产详情
* @param identifier - 资产标识符(ICCID、IMEI、SN、虚拟号或MSISDN)
*/
const fetchAssetDetail = async (identifier: string) => {
const fetchAssetDetail = async (
identifier: string,
{ preservePackageList = false }: { preservePackageList?: boolean } = {}
) => {
if (disposed) return null
const previousAsset = cardInfo.value
const previousPackages =
preservePackageList && cardInfo.value?.identifier === identifier ? packageList.value : []
resetAssetInfo()
packageList.value = previousPackages
const version = assetVersion
try {
loading.value = true
const response = await AssetService.resolveAsset(identifier, { include_usage_summary: true })
if (version !== assetVersion) return null
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
const assetIdentifier =
(data.asset_type === 'card' ? data.iccid : data.virtual_no) || identifier
if (
previousAsset?.asset_type !== data.asset_type ||
previousAsset?.asset_id !== data.asset_id ||
previousAsset?.identifier !== assetIdentifier
) {
packageList.value = []
}
// 映射API数据到页面显示格式
cardInfo.value = {
@@ -168,7 +248,7 @@ export function useAssetInfo() {
sn: data.sn || '',
max_sim_slots: data.max_sim_slots || 0,
bound_card_count: data.bound_card_count || 0,
cards: data.cards || [],
cards: (data.cards || []).map((card) => ({ ...card })),
device_protect_status: data.device_protect_status,
online_status: data.online_status,
last_online_time: data.last_online_time,
@@ -196,15 +276,17 @@ export function useAssetInfo() {
loadAssetWallet(assetIdentifier)
])
}
return version === assetVersion ? cardInfo.value : null
} else {
ElMessage.error(response.msg || '查询失败')
cardInfo.value = null
}
} catch {
cardInfo.value = null
if (version === assetVersion) resetAssetInfo()
} finally {
loading.value = false
if (version === assetVersion) loading.value = false
}
return null
}
/**
@@ -213,14 +295,24 @@ export function useAssetInfo() {
* @param page - 页码
* @param pageSize - 每页条数
*/
const loadPackageList = async (identifier: string, page: number = 1, pageSize: number = 10, status?: number) => {
const loadPackageList = async (
identifier: string,
page: number = 1,
pageSize: number = 10,
status?: number
) => {
const isCurrentAsset = captureAssetRequest(identifier)
if (!isCurrentAsset()) return null
const requestId = ++packageRequestId
const isCurrent = () => isCurrentAsset() && requestId === packageRequestId
try {
packageLoading.value = true
const response = await AssetService.getAssetPackages(identifier, {
page,
page_size: pageSize,
status
page_size: pageSize,
status
})
if (!isCurrent()) return null
if (response.code === 0 && response.data) {
// items 代表顶层主套餐关系组,必须保留其 children,不得展平。
const items = response.data.items || []
@@ -233,12 +325,15 @@ export function useAssetInfo() {
pageSize: response.data.page_size || pageSize
}
}
packageList.value = []
return { items: [], total: 0, page: 1, pageSize }
} catch (error) {
if (!isCurrent()) return null
console.error('获取套餐列表失败:', error)
packageList.value = []
return { items: [], total: 0, page: 1, pageSize }
} finally {
packageLoading.value = false
if (isCurrent()) packageLoading.value = false
}
}
@@ -247,11 +342,16 @@ export function useAssetInfo() {
* @param identifier - 资产标识符
*/
const loadCurrentPackage = async (identifier: string) => {
const isCurrentAsset = captureAssetRequest(identifier)
if (!isCurrentAsset()) return
const requestId = ++currentPackageRequestId
const isCurrent = () => isCurrentAsset() && requestId === currentPackageRequestId
try {
currentPackageLoading.value = true
currentPackageErrorMsg.value = ''
const response = await AssetService.getCurrentPackage(identifier)
if (!isCurrent()) return
if (response.code === 0) {
if (response.data) {
@@ -300,6 +400,7 @@ export function useAssetInfo() {
currentPackageErrorMsg.value = response.msg || '获取当前套餐失败'
}
} catch (error: any) {
if (!isCurrent()) return
// 404表示无当前生效套餐,这是正常情况
currentPackage.value = null
if (error?.response?.status === 404) {
@@ -309,7 +410,7 @@ export function useAssetInfo() {
currentPackageErrorMsg.value = error?.message || '获取当前套餐失败'
}
} finally {
currentPackageLoading.value = false
if (isCurrent()) currentPackageLoading.value = false
}
}
@@ -319,11 +420,22 @@ export function useAssetInfo() {
* @param assetType - 资产类型(card/device)
*/
const loadRealtimeStatus = async (identifier: string, assetType: string) => {
const isCurrentAsset = captureAssetRequest(identifier)
if (!isCurrentAsset() || cardInfo.value.asset_type !== assetType) return
const requestId = ++realtimeRequestId
const isCurrent = () => isCurrentAsset() && requestId === realtimeRequestId
try {
realtimeLoading.value = true
const response = await AssetService.getRealtimeStatus(identifier)
if (!isCurrent()) return
if (response.code === 0 && response.data) {
const data = response.data
if (
(data.asset_type !== undefined && data.asset_type !== assetType) ||
(data.asset_id !== undefined && String(data.asset_id) !== String(cardInfo.value.asset_id))
) {
return
}
// 更新实时状态数据
if (assetType === 'card') {
// 卡的实时状态
@@ -364,8 +476,7 @@ export function useAssetInfo() {
cardInfo.value.device_protect_status = data.device_protect_status
}
if (data.cards && data.cards.length > 0) {
// 合并实时卡数据与原始卡数据,保留 real_name_at 等静态字段。
// 设备详情里的实名状态、实名时间依赖选中的绑定卡,因此不能在实时刷新时丢掉这些字段。
// 实时 cards 可能缺少部分卡或静态字段,不能替换详情里的完整绑定列表。
cardInfo.value.cards = mergeAssetBoundCards(cardInfo.value.cards || [], data.cards)
}
if (data.last_data_check_at !== undefined) {
@@ -399,9 +510,9 @@ export function useAssetInfo() {
}
}
} catch (error) {
console.error('获取实时状态失败:', error)
if (isCurrent()) console.error('获取实时状态失败:', error)
} finally {
realtimeLoading.value = false
if (isCurrent()) realtimeLoading.value = false
}
}
@@ -410,13 +521,22 @@ export function useAssetInfo() {
* @param identifier - 资产标识符
*/
const loadAssetWallet = async (identifier: string) => {
const isCurrentAsset = captureAssetRequest(identifier)
if (!isCurrentAsset()) return
const requestId = ++walletRequestId
const isCurrent = () => isCurrentAsset() && requestId === walletRequestId
try {
walletLoading.value = true
const response = await AssetService.getAssetWallet(identifier)
if (!isCurrent()) return
if (response.code === 0 && response.data) {
walletInfo.value = response.data
} else {
walletInfo.value = null
}
} catch (error: any) {
if (!isCurrent()) return
walletInfo.value = null
if (error?.response?.status === 403) {
// 企业账号禁止查看钱包信息,不显示错误
walletInfo.value = null
@@ -424,7 +544,7 @@ export function useAssetInfo() {
console.error('获取钱包概况失败:', error)
}
} finally {
walletLoading.value = false
if (isCurrent()) walletLoading.value = false
}
}
@@ -435,16 +555,18 @@ export function useAssetInfo() {
* @returns true if refresh was successful, false if rate limited
*/
const refreshAsset = async (identifier: string, assetType: string): Promise<boolean> => {
const isCurrent = captureAssetRequest(identifier)
if (!isCurrent() || cardInfo.value.asset_type !== assetType) return false
try {
// 主动同步网关的接口仅允许由右上角“同步”按钮触发,搜索和普通数据重载不能调用。
await AssetService.refreshAsset(identifier)
if (!isCurrent()) return false
ElMessage.success('刷新成功')
// 刷新成功后重新加载实时状态数据
if (identifier) {
await loadRealtimeStatus(identifier, assetType)
}
return true
// 绑定/解绑和实名时间由详情接口确认,不能只刷新实时状态而保留过期绑定关系。
return Boolean(await fetchAssetDetail(identifier, { preservePackageList: true }))
} catch (error: any) {
if (!isCurrent()) return false
if (error instanceof FrontendRateLimitError) {
ElMessage.warning('操作过于频繁,请稍后重试')
return false

View File

@@ -16,6 +16,7 @@
<!-- 第一层:资产概览与当前生效套餐,按信息高度纵向展开 -->
<div class="row overview-primary">
<BasicInfoCard
:key="`${cardInfo.asset_type}:${cardInfo.asset_id}`"
:card-info="cardInfo"
:device-realtime="deviceRealtime"
v-model:polling-enabled="pollingEnabled"
@@ -295,6 +296,8 @@
const speedTierSubmitting = ref(false)
const bindingCardRealnameStatusIccid = ref('')
const bindingCardRealnameStatusValue = ref<number>(0)
let realnameStatusAsset: any = null
let bindingCardRealnameStatusAsset: any = null
const switchModeCards = computed<SwitchModeCardOption[]>(() =>
(cardInfo.value?.cards || [])
@@ -402,34 +405,33 @@
/**
* 处理资产搜索
*/
let searchRequestId = 0
const handleSearch = async ({ identifier }: { identifier: string }) => {
const requestId = ++searchRequestId
// 更新搜索框的值
assetSearchCardRef.value?.updateSearchValue(identifier)
startFailureMessage.value = ''
packagePagination.value.page = 1
packagePagination.value.total = 0
packagePagination.value.status = undefined
// fetchAssetDetail 内部已经会并行加载所有相关数据,不需要重复调用
await fetchAssetDetail(identifier)
// 如果是设备类型,需要再次调用搜索接口获取完整数据
if (cardInfo.value?.asset_type === 'device') {
const deviceIdentifier = cardInfo.value.virtual_no || cardInfo.value.iccid || identifier
await fetchAssetDetail(deviceIdentifier)
}
if (cardInfo.value) {
walletTransactionSearchToken.value += 1
}
// resolve 已返回完整详情;设备不再重复搜索,避免多轮响应相互覆盖。
const asset = await fetchAssetDetail(identifier)
if (requestId !== searchRequestId || !asset || cardInfo.value !== asset) return
walletTransactionSearchToken.value += 1
// 从接口数据初始化轮询开关,不触发更新接口
pollingInitializing = true
pollingEnabled.value = cardInfo.value?.enable_polling ?? false
pollingEnabled.value = asset.enable_polling ?? false
await nextTick()
pollingInitializing = false
if (requestId !== searchRequestId || cardInfo.value !== asset) return
// 重置分页并加载套餐列表
packagePagination.value.page = 1
const result = await loadPackageList(identifier, 1, packagePagination.value.pageSize)
packagePagination.value.total = result.total
// 套餐也使用解析后的规范标识符,支持通过 ICCID、IMEI、SN 等入口查同一设备。
const result = await loadPackageList(asset.identifier, 1, packagePagination.value.pageSize)
if (result && requestId === searchRequestId && cardInfo.value === asset) {
packagePagination.value.total = result.total
}
}
/**
@@ -455,7 +457,7 @@
try {
const started = await enableCardOp()
if (started && cardInfo.value) {
await fetchAssetDetail(cardInfo.value.identifier)
await fetchAssetDetail(cardInfo.value.identifier, { preservePackageList: true })
}
} finally {
updateRateLimitNow()
@@ -568,18 +570,24 @@
* 显示更新实名状态对话框
*/
const showUpdateRealnameStatusDialog = () => {
realnameStatusAsset = cardInfo.value
updateRealnameStatusDialogVisible.value = true
}
/**
* 更新实名状态成功
*/
const handleUpdateRealnameStatusSuccess = async () => {}
const handleUpdateRealnameStatusSuccess = async () => {
if (realnameStatusAsset && cardInfo.value === realnameStatusAsset) {
await fetchAssetDetail(realnameStatusAsset.identifier, { preservePackageList: true })
}
}
/**
* 显示绑定卡更新实名状态对话框
*/
const handleUpdateBindingCardRealnameStatus = (payload: { card: any }) => {
bindingCardRealnameStatusAsset = cardInfo.value
bindingCardRealnameStatusIccid.value = payload.card.iccid
bindingCardRealnameStatusValue.value = payload.card.real_name_status ?? 0
bindingCardRealnameStatusDialogVisible.value = true
@@ -588,7 +596,11 @@
/**
* 绑定卡更新实名状态成功
*/
const handleBindingCardRealnameStatusSuccess = async () => {}
const handleBindingCardRealnameStatusSuccess = async () => {
if (bindingCardRealnameStatusAsset && cardInfo.value === bindingCardRealnameStatusAsset) {
await fetchAssetDetail(bindingCardRealnameStatusAsset.identifier, { preservePackageList: true })
}
}
/**
* 确认实名认证策略
@@ -676,6 +688,7 @@
packagePagination.value.page,
packagePagination.value.pageSize
)
if (!result) return
packagePagination.value.total = result.total
await Promise.all([
loadCurrentPackage(cardInfo.value.identifier),
@@ -696,7 +709,7 @@
page,
packagePagination.value.pageSize
)
packagePagination.value.total = result.total
if (result) packagePagination.value.total = result.total
}
/**
@@ -707,7 +720,7 @@
packagePagination.value.pageSize = size
packagePagination.value.page = 1 // 重置到第一页
const result = await loadPackageList(cardInfo.value.identifier, 1, size)
packagePagination.value.total = result.total
if (result) packagePagination.value.total = result.total
}
/**
@@ -720,7 +733,7 @@
packagePagination.value.page,
packagePagination.value.pageSize
)
packagePagination.value.total = result.total
if (result) packagePagination.value.total = result.total
}
const handlePackageStatusChange = async (status: number | undefined) => {
@@ -733,7 +746,7 @@
packagePagination.value.pageSize,
status
)
packagePagination.value.total = result.total
if (result) packagePagination.value.total = result.total
}
/**

View File

@@ -1618,6 +1618,7 @@
{ label: 'IMEI', prop: 'imei' },
{ label: '设备名称', prop: 'device_name' },
{ label: '关联手机号', prop: 'associated_phones' },
{ label: '授权企业', prop: 'authorized_enterprise_name' },
{ label: '店铺名称', prop: 'shop_name' },
{ label: '套餐系列', prop: 'series_name' },
{ label: '预计套餐到期时间', prop: 'estimated_final_expires_at' },
@@ -1903,6 +1904,13 @@
formatter: (row: Device) =>
row.associated_phones?.length ? row.associated_phones.join('、') : '-'
},
{
prop: 'authorized_enterprise_name',
label: '授权企业',
minWidth: 160,
showOverflowTooltip: true,
formatter: (row: Device) => row.authorized_enterprise_name || '-'
},
{
prop: 'shop_name',
label: '店铺名称',

View File

@@ -1949,6 +1949,7 @@
{ label: '关联手机号', prop: 'associated_phones' },
{ label: '卡虚拟号', prop: 'virtual_no' },
{ label: '运营商', prop: 'carrier_name' },
{ label: '授权企业', prop: 'authorized_enterprise_name' },
{ label: '卡业务类型', prop: 'card_category' },
{ label: '状态', prop: 'status' },
{ label: '资产世代', prop: 'generation' },
@@ -2128,6 +2129,13 @@
width: 150,
showOverflowTooltip: true
},
{
prop: 'authorized_enterprise_name',
label: '授权企业',
minWidth: 160,
showOverflowTooltip: true,
formatter: (row: StandaloneIotCard) => row.authorized_enterprise_name || '-'
},
{
prop: 'shop_name',
label: '店铺名称',