fix: update some files
All checks were successful
构建并部署前端到测试环境 / build-and-deploy (push) Successful in 7m7s

This commit is contained in:
luo
2026-08-19 14:18:55 +08:00
parent 2d6948010b
commit 4e3ea6c6d6
22 changed files with 760 additions and 419 deletions

View File

@@ -40,7 +40,7 @@
</div>
</div>
</template>
<ElDescriptions :column="descriptionsColumn" border>
<ElDescriptions :column="descriptionsColumn" direction="vertical" border>
<!-- 卡专属字段 -->
<template v-if="cardInfo?.asset_type === 'card'">
<ElDescriptionsItem label="ICCID">{{ cardInfo?.iccid || '-' }}</ElDescriptionsItem>
@@ -214,7 +214,7 @@
<ElDescriptions :column="descriptionsColumn" border>
<ElDescriptionsItem v-if="previousExchangeAsset" label="前代资产">
<div class="exchange-asset">
<ElTag type="success" size="small">换货新资产</ElTag>
<ElTag type="success" size="small">资产</ElTag>
<ElButton
v-if="previousExchangeAsset.can_view"
type="primary"
@@ -251,230 +251,118 @@
<!-- 设备绑定卡列表 -->
<template v-if="cardInfo?.asset_type === 'device'">
<ElDivider content-position="left">绑定卡列表</ElDivider>
<ElTable
v-if="cardInfo.cards && cardInfo.cards.length > 0"
:data="sortedDeviceCards"
border
style="width: 100%; margin-top: 16px"
>
<ElTableColumn label="ICCID" width="220" show-overflow-tooltip>
<template #default="scope">
<div style="display: flex; align-items: center; justify-content: space-between">
<ElButton type="primary" link @click="handleNavigateToCardInfo(scope.row.iccid)">
{{ scope.row.iccid }}
<ElDivider class="info-section-divider" content-position="left">
<span class="section-divider-title">绑定卡列表</span>
</ElDivider>
<div v-if="cardInfo.cards && cardInfo.cards.length > 0" class="binding-card-list">
<ElCard v-for="bindingCard in sortedDeviceCards" :key="bindingCard.iccid" shadow="never">
<div class="binding-card-header">
<div class="binding-card-identifier">
<ElButton type="primary" link @click="handleNavigateToCardInfo(bindingCard.iccid)">
{{ bindingCard.iccid }}
</ElButton>
<el-icon
class="copy-icon"
@click="handleCopyICCID(scope.row.iccid)"
style="color: var(--el-color-primary); cursor: pointer"
>
<el-icon class="copy-icon" @click="handleCopyICCID(bindingCard.iccid)" title="复制">
<CopyDocument />
</el-icon>
</div>
</template>
</ElTableColumn>
<ElTableColumn prop="msisdn" label="MSISDN" min-width="150" showOverflowTooltip />
<ElTag v-if="bindingCard.is_current" type="primary" size="small">当前</ElTag>
</div>
<ElTableColumn prop="carrier_name" label="运营商" min-width="120" showOverflowTooltip />
<ElTableColumn prop="slot_position" label="卡槽位置" width="130">
<template #default="scope">
<div style="display: flex; gap: 10px">
<span>SIM-{{ scope.row.slot_position }}</span>
<ElTag v-if="scope.row.is_current" type="primary" size="small">当前</ElTag>
</div>
</template>
</ElTableColumn>
<ElTableColumn label="网络状态" width="100">
<template #default="scope">
<ElTag :type="scope.row.network_status === 1 ? 'success' : 'danger'" size="small">
{{ scope.row.network_status === 1 ? '正常' : '停机' }}
</ElTag>
</template>
</ElTableColumn>
<ElTableColumn label="实名状态" width="100">
<template #default="scope">
<ElTag :type="getRealNameStatusType(scope.row.real_name_status)" size="small">
{{ getRealNameStatusName(scope.row.real_name_status) }}
</ElTag>
</template>
</ElTableColumn>
<ElTableColumn label="实名认证策略" width="120" showOverflowTooltip>
<template #default="scope">
{{ getRealnamePolicyName(scope.row.realname_policy) }}
</template>
</ElTableColumn>
<ElTableColumn label="实名时间" width="170">
<template #default="scope">
{{ scope.row.real_name_at ? formatDateTime(scope.row.real_name_at) : '-' }}
</template>
</ElTableColumn>
<ElTableColumn label="运营商停机原因" min-width="220" showOverflowTooltip>
<template #default="scope">
{{ scope.row.gateway_extend || '-' }}
</template>
</ElTableColumn>
<ElTableColumn label="操作" width="260" fixed="right">
<template #default="scope">
<!-- 根据网络状态显示启用或停用按钮 -->
<div class="binding-card-details">
<ElDescriptions :column="bindingCardColumns" direction="vertical">
<ElDescriptionsItem label="MSISDN">
{{ bindingCard.msisdn || '-' }}
</ElDescriptionsItem>
<ElDescriptionsItem label="运营商">
{{ bindingCard.carrier_name || '-' }}
</ElDescriptionsItem>
<ElDescriptionsItem label="卡槽位置">
SIM-{{ bindingCard.slot_position ?? '-' }}
</ElDescriptionsItem>
<ElDescriptionsItem label="网络状态">
<ElTag :type="bindingCard.network_status === 1 ? 'success' : 'danger'" size="small">
{{ bindingCard.network_status === 1 ? '正常' : '停机' }}
</ElTag>
</ElDescriptionsItem>
<ElDescriptionsItem label="实名状态">
<ElTag :type="getRealNameStatusType(bindingCard.real_name_status)" size="small">
{{ getRealNameStatusName(bindingCard.real_name_status) }}
</ElTag>
</ElDescriptionsItem>
<ElDescriptionsItem label="实名认证策略">
{{ getRealnamePolicyName(bindingCard.realname_policy) }}
</ElDescriptionsItem>
<ElDescriptionsItem label="实名时间">
{{ bindingCard.real_name_at ? formatDateTime(bindingCard.real_name_at) : '-' }}
</ElDescriptionsItem>
<ElDescriptionsItem label="运营商停机原因">
{{ bindingCard.gateway_extend || '-' }}
</ElDescriptionsItem>
</ElDescriptions>
</div>
<div class="binding-card-actions">
<ElButton
v-if="scope.row.network_status === 0"
v-if="bindingCard.network_status === 0"
type="success"
size="small"
link
:disabled="isBindingStartDisabled(scope.row)"
@click="handleEnableBindingCard(scope.row)"
:disabled="isBindingStartDisabled(bindingCard)"
@click="handleEnableBindingCard(bindingCard)"
v-permission="'iot_info:start'"
>
{{ getBindingStartButtonText(scope.row) }}
{{ getBindingStartButtonText(bindingCard) }}
</ElButton>
<ElButton
v-if="scope.row.network_status === 1"
v-if="bindingCard.network_status === 1"
type="warning"
size="small"
link
:disabled="isBindingStopDisabled(scope.row)"
@click="handleDisableBindingCard(scope.row)"
:disabled="isBindingStopDisabled(bindingCard)"
@click="handleDisableBindingCard(bindingCard)"
v-permission="'iot_info:stop'"
>
{{ getBindingStopButtonText(scope.row) }}
{{ getBindingStopButtonText(bindingCard) }}
</ElButton>
<ElButton
type="primary"
size="small"
link
@click="handleUpdateBindingCardRealnameStatus(scope.row)"
@click="handleUpdateBindingCardRealnameStatus(bindingCard)"
v-permission="'bound_card:update_realname_status'"
>
更新实名状态
</ElButton>
<ElButton
v-if="getBindingCardAuditTarget(scope.row)"
v-if="getBindingCardAuditTarget(bindingCard)"
type="primary"
size="small"
link
@click="openBindingCardAudit(scope.row)"
@click="openBindingCardAudit(bindingCard)"
>
卡审计
</ElButton>
</template>
</ElTableColumn>
</ElTable>
</div>
</ElCard>
</div>
<ElEmpty v-else description="暂无绑定卡" :image-size="80" style="margin-top: 16px" />
<!-- 设备实时信息 -->
<ElDivider content-position="left">设备实时信息</ElDivider>
<div v-if="hasDeviceRealtimeSection" v-loading="realtimeLoading" style="margin-top: 16px">
<ElDescriptions :column="descriptionsColumn" border>
<!-- 基本状态 -->
<ElDescriptionsItem label="在线状态">
<ElTag :type="getOnlineStatusType(deviceRealtime?.online_status)" size="small">
{{ getOnlineStatusName(deviceRealtime?.online_status) }}
<ElDivider class="info-section-divider" content-position="left">
<span class="section-divider-title">设备实时信息</span>
</ElDivider>
<div v-if="hasDeviceRealtimeSection" v-loading="realtimeLoading" class="realtime-section">
<ElDescriptions :column="descriptionsColumn" direction="vertical" border>
<ElDescriptionsItem
v-for="item in realtimeDescriptionItems"
:key="item.key"
:label="item.label"
:span="item.span ?? 1"
>
<ElTag v-if="item.tagType" :type="item.tagType" size="small">
{{ item.value }}
</ElTag>
</ElDescriptionsItem>
<ElDescriptionsItem label="电量">
{{
deviceRealtime?.battery_level !== null && deviceRealtime?.battery_level !== undefined
? `${deviceRealtime?.battery_level}%`
: '-'
}}
</ElDescriptionsItem>
<!-- 时间信息 -->
<ElDescriptionsItem label="本次开机时长">
{{ formatDuration(deviceRealtime?.run_time) }}
</ElDescriptionsItem>
<ElDescriptionsItem label="本次联网时长">
{{ formatDuration(deviceRealtime?.connect_time) }}
</ElDescriptionsItem>
<ElDescriptionsItem label="最后在线时间">
{{ formatDateTime(deviceRealtime?.last_online_time) || '-' }}
</ElDescriptionsItem>
<ElDescriptionsItem label="实名同步时间">
{{ formatDateTime(cardInfo?.last_real_name_check_at) || '-' }}
</ElDescriptionsItem>
<ElDescriptionsItem label="卡状态同步时间">
{{ formatDateTime(cardInfo?.last_card_status_check_at) || '-' }}
</ElDescriptionsItem>
<ElDescriptionsItem label="流量同步时间">
{{ formatDateTime(cardInfo?.last_data_check_at) || '-' }}
</ElDescriptionsItem>
<!-- 信号信息 -->
<ElDescriptionsItem label="信号综合判断">
<ElTag :type="getSignalQualityType(deviceRealtime?.signal_quality)" size="small">
{{ getSignalQualityText(deviceRealtime?.signal_quality) }}
</ElTag>
</ElDescriptionsItem>
<ElDescriptionsItem label="信号较弱原因" :span="3">
{{ getSignalBadReasonText(deviceRealtime?.signal_bad_reason) }}
</ElDescriptionsItem>
<ElDescriptionsItem label="已连接设备数">
{{ deviceRealtime?.client_number ?? '-' }} / {{ deviceRealtime?.max_clients ?? '-' }}
</ElDescriptionsItem>
<!-- WiFi 信息 -->
<ElDescriptionsItem label="WiFi 状态">
<ElTag :type="deviceRealtime?.wifi_enabled ? 'success' : 'info'" size="small">
{{
deviceRealtime?.wifi_enabled === undefined
? '-'
: deviceRealtime.wifi_enabled
? '已开启'
: '已关闭'
}}
</ElTag>
</ElDescriptionsItem>
<ElDescriptionsItem label="WiFi 名称">
{{ deviceRealtime?.ssid || '-' }}
</ElDescriptionsItem>
<ElDescriptionsItem label="WiFi 密码">
{{ deviceRealtime?.wifi_password || '-' }}
</ElDescriptionsItem>
<!-- 网络信息 -->
<ElDescriptionsItem label="WAN IP">
{{ deviceRealtime?.wan_ip || '-' }}
</ElDescriptionsItem>
<ElDescriptionsItem label="LAN IP">
{{ deviceRealtime?.lan_ip || '-' }}
</ElDescriptionsItem>
<ElDescriptionsItem label="MAC 地址">
{{ deviceRealtime?.mac_address || '-' }}
</ElDescriptionsItem>
<ElDescriptionsItem label="IMSI">
{{ deviceRealtime?.imsi || '-' }}
</ElDescriptionsItem>
<!-- 设备配置 -->
<ElDescriptionsItem label="限速">
{{
deviceRealtime?.limit_speed === undefined
? '-'
: deviceRealtime.limit_speed
? `${deviceRealtime.limit_speed} KB/s`
: '不限速'
}}
</ElDescriptionsItem>
<ElDescriptionsItem label="固件版本">
{{ deviceRealtime?.software_version || '-' }}
</ElDescriptionsItem>
<ElDescriptionsItem label="切卡模式">
{{
deviceRealtime?.switch_mode === '0'
? '自动'
: deviceRealtime?.switch_mode === '1'
? '手动'
: '-'
}}
</ElDescriptionsItem>
<ElDescriptionsItem label="上报周期">
{{ deviceRealtime?.sync_interval ? `${deviceRealtime.sync_interval} 秒` : '-' }}
</ElDescriptionsItem>
<ElDescriptionsItem label="信息更新时间">
{{ formatDateTime(deviceRealtime?.last_update_time) || '-' }}
<span v-else>{{ item.value }}</span>
</ElDescriptionsItem>
</ElDescriptions>
</div>
@@ -577,8 +465,6 @@
ElTooltip,
ElSwitch,
ElDivider,
ElTable,
ElTableColumn,
ElButton,
ElAlert,
ElEmpty,
@@ -825,9 +711,11 @@
const descriptionsColumn = computed(() => {
if (width.value <= 640) return 1
if (width.value <= 1024) return 2
return 4
return 5
})
const bindingCardColumns = computed(() => (width.value <= 640 ? 1 : 2))
// Emits
interface Emits {
(e: 'update:pollingEnabled', value: boolean): void
@@ -871,6 +759,127 @@
} = useAssetFormatters()
// 排序后的设备卡列表(按卡槽位置排序,当前卡排在最前面)
const realtimeDescriptionItems = computed(() => {
const realtime = props.deviceRealtime
return [
{
key: 'status',
label: '在线状态',
value: getOnlineStatusName(realtime?.online_status),
tagType: getOnlineStatusType(realtime?.online_status)
},
{
key: 'battery',
label: '电量',
value:
realtime?.battery_level !== null && realtime?.battery_level !== undefined
? `${realtime.battery_level}%`
: '-'
},
{
key: 'boot',
label: '本次开机时长',
value: formatDuration(realtime?.run_time)
},
{
key: 'connect',
label: '本次联网时长',
value: formatDuration(realtime?.connect_time)
},
{
key: 'online',
label: '最后在线时间',
value: formatDateTime(realtime?.last_online_time) || '-'
},
{
key: 'realname',
label: '实名同步时间',
value: formatDateTime(props.cardInfo?.last_real_name_check_at) || '-'
},
{
key: 'cardSync',
label: '卡状态同步时间',
value: formatDateTime(props.cardInfo?.last_card_status_check_at) || '-'
},
{
key: 'trafficSync',
label: '流量同步时间',
value: formatDateTime(props.cardInfo?.last_data_check_at) || '-'
},
{
key: 'signal',
label: '信号综合判断',
value: getSignalQualityText(realtime?.signal_quality),
tagType: getSignalQualityType(realtime?.signal_quality)
},
{
key: 'signalReason',
label: '信号较弱原因',
value: getSignalBadReasonText(realtime?.signal_bad_reason)
},
{
key: 'clients',
label: '已连接设备数',
value: `${realtime?.client_number ?? '-'} / ${realtime?.max_clients ?? '-'}`
},
{
key: 'wifi',
label: 'WiFi 状态',
value:
realtime?.wifi_enabled === undefined ? '-' : realtime.wifi_enabled ? '已开启' : '已关闭',
tagType: realtime?.wifi_enabled ? 'success' : 'info'
},
{
key: 'wifiName',
label: 'WiFi 名称',
value: realtime?.ssid || '-'
},
{
key: 'wifiPassword',
label: 'WiFi 密码',
value: realtime?.wifi_password || '-'
},
{ key: 'wan', label: 'WAN IP', value: realtime?.wan_ip || '-' },
{ key: 'lan', label: 'LAN IP', value: realtime?.lan_ip || '-' },
{
key: 'mac',
label: 'MAC 地址',
value: realtime?.mac_address || '-'
},
{
key: 'limit',
label: '限速',
value:
realtime?.limit_speed === undefined
? '-'
: realtime.limit_speed
? `${realtime.limit_speed} KB/s`
: '不限速'
},
{
key: 'switchMode',
label: '切卡模式',
value: realtime?.switch_mode === '0' ? '自动' : realtime?.switch_mode === '1' ? '手动' : '-'
},
{
key: 'interval',
label: '上报周期',
value: realtime?.sync_interval ? `${realtime.sync_interval} 秒` : '-'
},
{
key: 'firmware',
label: '固件版本',
value: realtime?.software_version || '-',
span: descriptionsColumn.value >= 5 ? 2 : 1
},
{
key: 'updated',
label: '信息更新时间',
value: formatDateTime(realtime?.last_update_time) || '-'
}
]
})
const sortedDeviceCards = computed(() => {
if (!props.cardInfo?.cards || props.cardInfo.cards.length === 0) {
return []
@@ -1084,16 +1093,96 @@
}
}
:deep(.el-descriptions__body) {
overflow-x: auto;
.info-section-divider {
display: flex;
height: 20px;
align-items: center;
border-top: 0;
:deep(.el-divider__text) {
position: static;
display: inline-flex;
padding: 0;
font-size: 15px;
font-weight: 600;
line-height: 20px;
color: var(--el-text-color-primary);
background-color: transparent;
transform: none;
}
.section-divider-title {
display: inline-flex;
gap: 8px;
align-items: center;
&::before {
width: 3px;
height: 16px;
content: '';
background-color: var(--el-color-primary);
border-radius: 2px;
}
}
}
:deep(.el-descriptions__cell) {
word-break: break-word;
.binding-card-list {
display: grid;
grid-template-columns: repeat(3, minmax(0, 1fr));
gap: 16px;
}
:deep(.el-table) {
min-width: 760px;
.binding-card-header,
.binding-card-identifier,
.binding-card-actions {
display: flex;
align-items: center;
}
.binding-card-header {
justify-content: space-between;
padding-bottom: 12px;
border-bottom: 1px solid var(--el-border-color-lighter);
}
.binding-card-details {
padding-top: 12px;
}
.binding-card-identifier {
min-width: 0;
.el-button {
max-width: 100%;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.copy-icon {
flex: 0 0 auto;
margin-left: 6px;
color: var(--el-color-primary);
cursor: pointer;
}
}
.binding-card-actions {
flex-wrap: wrap;
gap: 8px;
padding-top: 12px;
margin-top: 12px;
border-top: 1px solid var(--el-border-color-lighter);
.el-button {
margin-left: 0;
}
}
@media (width <= 1200px) {
.binding-card-list {
grid-template-columns: repeat(2, minmax(0, 1fr));
}
}
.card-operations,
@@ -1120,6 +1209,11 @@
}
@media (width <= 768px) {
.binding-card-list {
grid-template-columns: 1fr;
gap: 12px;
}
.card-header {
flex-direction: column;
align-items: flex-start;