refactor(device): 重构设备详情页,按面板拆分信息并接入新接口

- 新增设备基本信息、硬件信息、网络信息、安全信息、其他信息独立接口和类型定义
- 将设备详情页从单接口加载改为分面板(el-collapse)独立请求和展示
- 优化数据展示,使用 formatText/formatBool 处理空值和布尔值
- 移除旧的 deviceDeveloper 接口
- 更新 getPage 和 getFormData 接口路径
This commit is contained in:
TongTongStudio
2026-08-05 02:11:04 +08:00
parent d698dd7395
commit 6e700851c4
3 changed files with 791 additions and 385 deletions

View File

@@ -10,6 +10,11 @@ import type {
DevicePasswordVerifyForm,
DeviceMobileUpdateForm,
DeviceEmailUpdateForm,
DeviceBasicInfo,
DeviceHardwareInfo,
DeviceNetworkInfo,
DeviceOtherInfo,
DeviceSecurityInfo,
} from "./types";
import type { ExcelResult, PageResult, OptionItem } from "@/api/common";
@@ -36,21 +41,22 @@ const DeviceAPI = {
*/
getPage(queryParams: DeviceQueryParams) {
return request<any, PageResult<DeviceItem>>({
url: `${DEVICE_BASE_URL}`,
url: `${DEVICE_BASE_URL}/page`,
method: "get",
params: queryParams,
});
},
/**
* 获取设备表单详情
* 获取设备头部信息(设备主表详情)
* 对应后端 GET /{sn}/detail (getSnBindInfo)
*
* @param sn 设备SN
* @returns 设备表单详情
* @returns 设备头部信息(含在线状态、IMEI、所有者、部门等)
*/
getFormData(sn: string) {
return request<any, DeviceForm>({
url: `${DEVICE_BASE_URL}/${sn}/info`,
url: `${DEVICE_BASE_URL}/${sn}/detail`,
method: "get",
});
},
@@ -308,15 +314,45 @@ const DeviceAPI = {
});
},
/**
* 推送开发者模式指令到设备
* @param sn 设备SN号
*/
deviceDeveloper(sn: string) {
return request({
url: `${DEVICE_BASE_URL}/developer`,
method: "post",
params: { sn },
// ===================== 设备系统信息(分面板独立接口) =====================
/** 设备基本信息 */
getBasicInfo(serialno: string) {
return request<any, DeviceBasicInfo>({
url: `${DEVICE_BASE_URL}/${serialno}/basic_info`,
method: "get",
});
},
/** 设备硬件信息 */
getHardwareInfo(serialno: string) {
return request<any, DeviceHardwareInfo>({
url: `${DEVICE_BASE_URL}/${serialno}/hardware_info`,
method: "get",
});
},
/** 设备网络信息 */
getNetworkInfo(serialno: string) {
return request<any, DeviceNetworkInfo>({
url: `${DEVICE_BASE_URL}/${serialno}/network_info`,
method: "get",
});
},
/** 设备安全信息 */
getSecurityInfo(serialno: string) {
return request<any, DeviceSecurityInfo>({
url: `${DEVICE_BASE_URL}/${serialno}/security_info`,
method: "get",
});
},
/** 设备其他信息 */
getOtherInfo(serialno: string) {
return request<any, DeviceOtherInfo>({
url: `${DEVICE_BASE_URL}/${serialno}/other_info`,
method: "get",
});
},
};

View File

@@ -161,3 +161,171 @@ export interface DeviceEmailUpdateForm {
/** 当前密码 */
password?: string;
}
// ===================== 设备系统信息(分面板) =====================
/** 设备基本信息 */
export interface DeviceBasicInfo {
id?: number;
serialno?: string;
deviceName?: string;
deviceBrand?: string;
deviceModel?: string;
deviceBoard?: string;
deviceManufacturer?: string;
hardware?: string;
host?: string;
androidVersion?: string;
androidApi?: number;
androidId?: string;
buildId?: string;
buildDisplayId?: string;
buildFingerprint?: string;
buildType?: string;
buildUser?: string;
buildHost?: string;
buildTags?: string;
buildTime?: string;
language?: string;
timezone?: string;
bootTime?: string;
screenResolution?: string;
screenDensity?: number;
kernelVersion?: string;
romVersion?: string;
// ===================== 补充基本信息字段 =====================
productName?: string;
productModel?: string;
productDevice?: string;
boardPlatform?: string;
deviceType?: string;
isTablet?: number;
releaseVersion?: string;
displayVersion?: string;
bootloader?: string;
basebandVersion?: string;
radioVersion?: string;
screenSize?: string;
screenInch?: number;
refreshRate?: number;
osType?: string;
osVersion?: string;
firmwareVersion?: string;
}
/** 设备硬件信息 */
export interface DeviceHardwareInfo {
id?: number;
serialno?: string;
cpuAbi?: string;
cpuAbi2?: string;
cpuMin?: string;
cpuModel?: string;
cpuCores?: number;
cpuMaxFreq?: string;
cpuMinFreq?: string;
totalRam?: string;
availableRam?: string;
internalStorage?: string;
availableStorage?: string;
externalStorage?: string;
batteryCapacity?: string;
batteryLevel?: number;
batteryStatus?: string;
batteryHealth?: string;
batteryTemp?: string;
batteryVoltage?: string;
batteryTechnology?: string;
gpuRenderer?: string;
gpuVendor?: string;
gpuVersion?: string;
sensors?: string;
supportAbi?: string;
supportAbi64?: string;
}
/** 设备网络信息 */
export interface DeviceNetworkInfo {
id?: number;
serialno?: string;
networkType?: string;
networkSubtype?: string;
isConnected?: number;
wifiSsid?: string;
wifiBssid?: string;
wifiIp?: string;
wifiMac?: string;
wifiGateway?: string;
wifiNetmask?: string;
wifiDns?: string;
wifiRssi?: number;
wifiSpeed?: string;
mobileIp?: string;
mobileMac?: string;
mobileImei?: string;
mobileImsi?: string;
mobileOperator?: string;
mobileSimSerial?: string;
mobileNetworkType?: string;
bluetoothMac?: string;
bluetoothName?: string;
isVpn?: number;
isProxy?: number;
proxyHost?: string;
proxyPort?: string;
}
/** 设备安全信息 */
export interface DeviceSecurityInfo {
id?: number;
serialno?: string;
isRoot?: number;
isJailbreak?: number;
rootStatus?: number;
hasSuBinary?: number;
hasBusybox?: number;
isEmulator?: number;
isDebuggable?: number;
isUsbDebug?: number;
isUnknownSources?: number;
isVerifyApps?: number;
isEncrypted?: number;
lockScreenType?: string;
hasPassword?: number;
passwordType?: string;
securityPatch?: string;
googlePlayProtect?: number;
adbStatus?: number;
isVpnActive?: number;
isDeviceOwner?: number;
androidVersion?: string;
kernelVersion?: string;
}
/** 设备其他信息 */
export interface DeviceOtherInfo {
id?: number;
serialno?: string;
appVersion?: string;
appVersionCode?: number;
installTime?: string;
updateTimeApp?: string;
packageName?: string;
channel?: string;
deviceId?: string;
gaid?: string;
oaid?: string;
phoneNumber?: string;
iccid?: string;
imsi?: string;
cameraInfo?: string;
sensorCount?: number;
appListMd5?: string;
isCharging?: number;
chargingType?: string;
temperature?: string;
freeStorage?: string;
totalStorage?: string;
freeRam?: string;
totalRam?: string;
}

View File

@@ -18,39 +18,39 @@
</div>
<div class="info-item">
<span class="label">IMEI:</span>
<span class="value">{{ deviceData.imei }}</span>
<span class="value">{{ formatText(deviceData.imei) }}</span>
</div>
<div class="info-item">
<span class="label">设备所有者:</span>
<span class="value">{{ deviceData.owner }}</span>
<span class="value">{{ formatText(deviceData.owner) }}</span>
</div>
<div class="info-item">
<span class="label">所属部门:</span>
<span class="value">{{ deviceData.department }}</span>
<span class="value">{{ formatText(deviceData.department) }}</span>
</div>
<div class="info-item">
<span class="label">分组:</span>
<span class="value">{{ deviceData.group }}</span>
<span class="value">{{ formatText(deviceData.group) }}</span>
</div>
<div class="info-item">
<span class="label">设备类型:</span>
<span class="value">{{ deviceData.deviceType }}</span>
<span class="value">{{ formatText(deviceData.deviceType) }}</span>
</div>
<div class="info-item">
<span class="label">设备来源:</span>
<span class="value">{{ deviceData.source }}</span>
<span class="value">{{ formatText(deviceData.source) }}</span>
</div>
<div class="info-item">
<span class="label">激活时间:</span>
<span class="value">{{ deviceData.activationTime }}</span>
<span class="value">{{ formatText(deviceData.activationTime) }}</span>
</div>
<div class="info-item">
<span class="label">最后上线:</span>
<span class="value">{{ deviceData.lastOnline }}</span>
<span class="value">{{ formatText(deviceData.lastOnline) }}</span>
</div>
<div class="info-item">
<span class="label">设备编号:</span>
<span class="value">{{ deviceData.deviceSn }}</span>
<span class="value">{{ formatText(deviceData.deviceSn) }}</span>
</div>
</div>
</div>
@@ -109,220 +109,349 @@
</div>
</div>
<!-- 标签页内容 -->
<div class="tabs-container">
<el-tabs v-model="activeTab" class="device-tabs">
<!-- 设备信息标签页:第一个 tab,内含基本信息等分面板独立加载 -->
<el-tabs v-model="activeTab" class="tabs-container device-tabs">
<el-tab-pane label="设备信息" name="deviceInfo">
<div class="tab-content">
<el-collapse v-model="activeNames" class="device-sn-detail">
<!-- 基本信息 -->
<div class="info-section">
<h3 class="section-title">基本信息</h3>
<el-descriptions :column="2" border>
<el-collapse-item title="基本信息" name="basic">
<template #title>
<div class="panel-title">
基本信息
<el-tag v-if="loading.basic" size="small" type="info" effect="plain">
加载中…
</el-tag>
</div>
</template>
<el-descriptions v-loading="loading.basic" :column="2" border label-width="130px">
<el-descriptions-item label="设备名称">
{{ basicInfo.deviceName }}
{{ formatText(basicInfo.deviceName) }}
</el-descriptions-item>
<el-descriptions-item label="设备品牌">
{{ formatText(basicInfo.deviceBrand) }}
</el-descriptions-item>
<el-descriptions-item label="设备型号">
{{ basicInfo.deviceModel }}
{{ formatText(basicInfo.deviceModel) }}
</el-descriptions-item>
<el-descriptions-item label="操作系统">{{ basicInfo.os }}</el-descriptions-item>
<el-descriptions-item label="操作系统版本号">
{{ basicInfo.osVersion }}
<el-descriptions-item label="主板名称">
{{ formatText(basicInfo.deviceBoard) }}
</el-descriptions-item>
<el-descriptions-item label="设备语言">
{{ basicInfo.language }}
<el-descriptions-item label="制造商">
{{ formatText(basicInfo.deviceManufacturer) }}
</el-descriptions-item>
<el-descriptions-item label="时区">{{ basicInfo.timezone }}</el-descriptions-item>
<el-descriptions-item label="设备标识(IMEI)">
{{ basicInfo.imei }}
<el-descriptions-item label="硬件信息">
{{ formatText(basicInfo.hardware) }}
</el-descriptions-item>
<el-descriptions-item label="蓝牙地址">
{{ basicInfo.bluetoothMac }}
<el-descriptions-item label="主机名">
{{ formatText(basicInfo.host) }}
</el-descriptions-item>
<el-descriptions-item label="Wi-Fi MAC地址">
{{ basicInfo.wifiMac }}
<el-descriptions-item label="Android 版本">
{{ formatText(basicInfo.androidVersion) }}
</el-descriptions-item>
<el-descriptions-item label="设备颜色">{{ basicInfo.color }}</el-descriptions-item>
<el-descriptions-item label="设备容量">
{{ basicInfo.capacity }}
<el-descriptions-item label="Android API">
{{ formatText(basicInfo.androidApi) }}
</el-descriptions-item>
<el-descriptions-item label="可用容量">
{{ basicInfo.availableCapacity }}
<el-descriptions-item label="Android ID">
{{ formatText(basicInfo.androidId) }}
</el-descriptions-item>
<el-descriptions-item label="内存容量">
{{ basicInfo.memoryCapacity }}
<el-descriptions-item label="Build ID">
{{ formatText(basicInfo.buildId) }}
</el-descriptions-item>
<el-descriptions-item label="可用内存">
{{ basicInfo.availableMemory }}
<el-descriptions-item label="Build Display ID">
{{ formatText(basicInfo.buildDisplayId) }}
</el-descriptions-item>
<el-descriptions-item label="电池电量">
{{ basicInfo.batteryLevel }}
<el-descriptions-item label="Build 类型">
{{ formatText(basicInfo.buildType) }}
</el-descriptions-item>
<el-descriptions-item label="电池健康度">
{{ basicInfo.batteryHealth }}
<el-descriptions-item label="系统语言">
{{ formatText(basicInfo.language) }}
</el-descriptions-item>
<el-descriptions-item label="设备重量">{{ basicInfo.weight }}</el-descriptions-item>
<el-descriptions-item label="设备尺寸">
{{ basicInfo.dimensions }}
</el-descriptions-item>
<el-descriptions-item
label="SIM卡状态
"
>
{{ basicInfo.simStatus }}
</el-descriptions-item>
<el-descriptions-item label="电话号码">
{{ basicInfo.phoneNumber }}
</el-descriptions-item>
<el-descriptions-item label="ICCID">
{{ basicInfo.iccid }}
</el-descriptions-item>
<el-descriptions-item label="MEID">
{{ basicInfo.meid }}
</el-descriptions-item>
</el-descriptions>
</div>
<!-- 硬件信息 -->
<div class="info-section">
<h3 class="section-title">硬件信息</h3>
<el-descriptions :column="2" border>
<el-descriptions-item label="处理器">
{{ hardwareInfo.processor }}
</el-descriptions-item>
<el-descriptions-item label="处理器核心数">
{{ hardwareInfo.processorCores }}
</el-descriptions-item>
<el-descriptions-item label="GPU">
{{ hardwareInfo.gpu }}
</el-descriptions-item>
<el-descriptions-item label="屏幕尺寸">
{{ hardwareInfo.screenSize }}
<el-descriptions-item label="时区">
{{ formatText(basicInfo.timezone) }}
</el-descriptions-item>
<el-descriptions-item label="屏幕分辨率">
{{ hardwareInfo.screenResolution }}
{{ formatText(basicInfo.screenResolution) }}
</el-descriptions-item>
<el-descriptions-item label="屏幕像素密度">
{{ hardwareInfo.screenDensity }}
<el-descriptions-item label="屏幕密度">
{{ formatText(basicInfo.screenDensity) }}
</el-descriptions-item>
<el-descriptions-item label="摄像头">
{{ hardwareInfo.camera }}
<el-descriptions-item label="内核版本">
{{ formatText(basicInfo.kernelVersion) }}
</el-descriptions-item>
<el-descriptions-item label="设备传感器">
{{ hardwareInfo.sensors }}
</el-descriptions-item>
<el-descriptions-item label="NFC">
{{ hardwareInfo.nfc }}
</el-descriptions-item>
<el-descriptions-item label="Face ID">
{{ hardwareInfo.faceId }}
<el-descriptions-item label="ROM 版本">
{{ formatText(basicInfo.romVersion) }}
</el-descriptions-item>
</el-descriptions>
</el-collapse-item>
<!-- 硬件信息 -->
<el-collapse-item title="硬件信息" name="hardware">
<template #title>
<div class="panel-title">
硬件信息
<el-tag v-if="loading.hardware" size="small" type="info" effect="plain">
加载中…
</el-tag>
</div>
</template>
<el-descriptions v-loading="loading.hardware" :column="2" border label-width="130px">
<el-descriptions-item label="CPU ABI">
{{ formatText(hardwareInfo.cpuAbi) }}
</el-descriptions-item>
<el-descriptions-item label="CPU 型号">
{{ formatText(hardwareInfo.cpuModel) }}
</el-descriptions-item>
<el-descriptions-item label="CPU 核心数">
{{ formatText(hardwareInfo.cpuCores) }}
</el-descriptions-item>
<el-descriptions-item label="CPU 最大频率">
{{ formatText(hardwareInfo.cpuMaxFreq) }}
</el-descriptions-item>
<el-descriptions-item label="CPU 最小频率">
{{ formatText(hardwareInfo.cpuMinFreq) }}
</el-descriptions-item>
<el-descriptions-item label="总内存">
{{ formatText(hardwareInfo.totalRam) }}
</el-descriptions-item>
<el-descriptions-item label="可用内存">
{{ formatText(hardwareInfo.availableRam) }}
</el-descriptions-item>
<el-descriptions-item label="内部存储">
{{ formatText(hardwareInfo.internalStorage) }}
</el-descriptions-item>
<el-descriptions-item label="可用存储">
{{ formatText(hardwareInfo.availableStorage) }}
</el-descriptions-item>
<el-descriptions-item label="外部存储">
{{ formatText(hardwareInfo.externalStorage) }}
</el-descriptions-item>
<el-descriptions-item label="电池容量">
{{ formatText(hardwareInfo.batteryCapacity) }}
</el-descriptions-item>
<el-descriptions-item label="电池电量">
{{ formatText(hardwareInfo.batteryLevel) }}
</el-descriptions-item>
<el-descriptions-item label="电池状态">
{{ formatText(hardwareInfo.batteryStatus) }}
</el-descriptions-item>
<el-descriptions-item label="电池健康度">
{{ formatText(hardwareInfo.batteryHealth) }}
</el-descriptions-item>
<el-descriptions-item label="电池温度">
{{ formatText(hardwareInfo.batteryTemp) }}
</el-descriptions-item>
<el-descriptions-item label="GPU 渲染器">
{{ formatText(hardwareInfo.gpuRenderer) }}
</el-descriptions-item>
<el-descriptions-item label="GPU 厂商">
{{ formatText(hardwareInfo.gpuVendor) }}
</el-descriptions-item>
<el-descriptions-item label="GPU 版本">
{{ formatText(hardwareInfo.gpuVersion) }}
</el-descriptions-item>
<el-descriptions-item label="传感器">
{{ formatText(hardwareInfo.sensors) }}
</el-descriptions-item>
</el-descriptions>
</el-collapse-item>
<!-- 网络信息 -->
<div class="info-section">
<h3 class="section-title">网络信息</h3>
<el-descriptions :column="2" border>
<el-descriptions-item label="当前网络">
{{ networkInfo.currentNetwork }}
<el-collapse-item title="网络信息" name="network">
<template #title>
<div class="panel-title">
网络信息
<el-tag v-if="loading.network" size="small" type="info" effect="plain">
加载中…
</el-tag>
</div>
</template>
<el-descriptions v-loading="loading.network" :column="2" border label-width="130px">
<el-descriptions-item label="网络类型">
{{ formatText(networkInfo.networkType) }}
</el-descriptions-item>
<el-descriptions-item label="是否联网">
{{ formatBool(networkInfo.isConnected) }}
</el-descriptions-item>
<el-descriptions-item label="WiFi SSID">
{{ formatText(networkInfo.wifiSsid) }}
</el-descriptions-item>
<el-descriptions-item label="WiFi BSSID">
{{ formatText(networkInfo.wifiBssid) }}
</el-descriptions-item>
<el-descriptions-item label="WiFi IP">
{{ formatText(networkInfo.wifiIp) }}
</el-descriptions-item>
<el-descriptions-item label="WiFi MAC">
{{ formatText(networkInfo.wifiMac) }}
</el-descriptions-item>
<el-descriptions-item label="WiFi 网关">
{{ formatText(networkInfo.wifiGateway) }}
</el-descriptions-item>
<el-descriptions-item label="WiFi 信号">
{{ formatText(networkInfo.wifiRssi) }}
</el-descriptions-item>
<el-descriptions-item label="运营商">
{{ networkInfo.carrier }}
{{ formatText(networkInfo.mobileOperator) }}
</el-descriptions-item>
<el-descriptions-item label="信号强度">
{{ networkInfo.signalStrength }}
<el-descriptions-item label="IMEI">
{{ formatText(networkInfo.mobileImei) }}
</el-descriptions-item>
<el-descriptions-item label="IP地址">
{{ networkInfo.ipAddress }}
<el-descriptions-item label="IMSI">
{{ formatText(networkInfo.mobileImsi) }}
</el-descriptions-item>
<el-descriptions-item label="Wi-Fi名称">
{{ networkInfo.wifiName }}
<el-descriptions-item label="蓝牙 MAC">
{{ formatText(networkInfo.bluetoothMac) }}
</el-descriptions-item>
<el-descriptions-item label="Wi-Fi MAC地址">
{{ networkInfo.wifiMac }}
<el-descriptions-item label="蓝牙名称">
{{ formatText(networkInfo.bluetoothName) }}
</el-descriptions-item>
<el-descriptions-item label="蜂窝网络类型">
{{ networkInfo.cellularType }}
<el-descriptions-item label="VPN">
{{ formatBool(networkInfo.isVpn) }}
</el-descriptions-item>
<el-descriptions-item label="漫游状态">
{{ networkInfo.roamingStatus }}
<el-descriptions-item label="代理">
{{ formatBool(networkInfo.isProxy) }}
</el-descriptions-item>
<el-descriptions-item label="代理主机">
{{ formatText(networkInfo.proxyHost) }}
</el-descriptions-item>
<el-descriptions-item label="代理端口">
{{ formatText(networkInfo.proxyPort) }}
</el-descriptions-item>
</el-descriptions>
</div>
</el-collapse-item>
<!-- 安全信息 -->
<div class="info-section">
<h3 class="section-title">安全信息</h3>
<el-descriptions :column="2" border>
<el-descriptions-item label="锁屏密码">
{{ securityInfo.lockScreenPassword }}
<el-collapse-item title="安全信息" name="security">
<template #title>
<div class="panel-title">
安全信息
<el-tag v-if="loading.security" size="small" type="info" effect="plain">
加载中…
</el-tag>
</div>
</template>
<el-descriptions v-loading="loading.security" :column="2" border label-width="130px">
<el-descriptions-item label="Root 状态">
{{ formatBool(securityInfo.rootStatus) }}
</el-descriptions-item>
<el-descriptions-item label="密码复杂度">
{{ securityInfo.passwordComplexity }}
</el-descriptions-item>
<el-descriptions-item label="数据保护">
{{ securityInfo.dataProtection }}
</el-descriptions-item>
<el-descriptions-item label="查找我的iPhone">
{{ securityInfo.findMyIPhone }}
</el-descriptions-item>
<el-descriptions-item label="设备加密">
{{ securityInfo.deviceEncryption }}
<el-descriptions-item label="已 Root">
{{ formatBool(securityInfo.isRoot) }}
</el-descriptions-item>
<el-descriptions-item label="越狱状态">
{{ securityInfo.jailbreakStatus }}
{{ formatBool(securityInfo.isJailbreak) }}
</el-descriptions-item>
<el-descriptions-item label="受监管状态">
{{ securityInfo.supervisedStatus }}
<el-descriptions-item label="su 二进制">
{{ formatBool(securityInfo.hasSuBinary) }}
</el-descriptions-item>
<el-descriptions-item label="最后安全扫描时间">
{{ securityInfo.lastSecurityScan }}
<el-descriptions-item label="BusyBox">
{{ formatBool(securityInfo.hasBusybox) }}
</el-descriptions-item>
<el-descriptions-item label="安全补丁版本">
{{ securityInfo.securityPatchVersion }}
<el-descriptions-item label="模拟器">
{{ formatBool(securityInfo.isEmulator) }}
</el-descriptions-item>
<el-descriptions-item label="可调试">
{{ formatBool(securityInfo.isDebuggable) }}
</el-descriptions-item>
<el-descriptions-item label="USB 调试">
{{ formatBool(securityInfo.isUsbDebug) }}
</el-descriptions-item>
<el-descriptions-item label="未知来源">
{{ formatBool(securityInfo.isUnknownSources) }}
</el-descriptions-item>
<el-descriptions-item label="加密">
{{ formatBool(securityInfo.isEncrypted) }}
</el-descriptions-item>
<el-descriptions-item label="密码保护">
{{ formatBool(securityInfo.hasPassword) }}
</el-descriptions-item>
<el-descriptions-item label="锁屏类型">
{{ formatText(securityInfo.lockScreenType) }}
</el-descriptions-item>
<el-descriptions-item label="安全补丁">
{{ formatText(securityInfo.securityPatch) }}
</el-descriptions-item>
<el-descriptions-item label="ADB 状态">
{{ formatBool(securityInfo.adbStatus) }}
</el-descriptions-item>
<el-descriptions-item label="VPN 激活">
{{ formatBool(securityInfo.isVpnActive) }}
</el-descriptions-item>
<el-descriptions-item label="设备所有者">
{{ formatBool(securityInfo.isDeviceOwner) }}
</el-descriptions-item>
</el-descriptions>
</div>
</el-collapse-item>
<!-- 其他信息 -->
<div class="info-section">
<h3 class="section-title">其他信息</h3>
<el-descriptions :column="2" border>
<el-descriptions-item label="购买日期">
{{ otherInfo.purchaseDate }}
<el-collapse-item title="其他信息" name="other">
<template #title>
<div class="panel-title">
其他信息
<el-tag v-if="loading.other" size="small" type="info" effect="plain">
加载中…
</el-tag>
</div>
</template>
<el-descriptions v-loading="loading.other" :column="2" border label-width="130px">
<el-descriptions-item label="App 版本">
{{ formatText(otherInfo.appVersion) }}
</el-descriptions-item>
<el-descriptions-item label="保修到期日">
{{ otherInfo.warrantyExpiry }}
<el-descriptions-item label="App 版本号">
{{ formatText(otherInfo.appVersionCode) }}
</el-descriptions-item>
<el-descriptions-item label="设备来源">{{ otherInfo.source }}</el-descriptions-item>
<el-descriptions-item label="分配日期">
{{ otherInfo.assignDate }}
<el-descriptions-item label="包名">
{{ formatText(otherInfo.packageName) }}
</el-descriptions-item>
<el-descriptions-item label="备注">{{ otherInfo.remark }}</el-descriptions-item>
<el-descriptions-item label="标签">{{ otherInfo.tags }}</el-descriptions-item>
<el-descriptions-item label="自定义字段1">
{{ otherInfo.customField1 }}
<el-descriptions-item label="渠道">
{{ formatText(otherInfo.channel) }}
</el-descriptions-item>
<el-descriptions-item label="自定义字段2">
{{ otherInfo.customField2 }}
<el-descriptions-item label="设备 ID">
{{ formatText(otherInfo.deviceId) }}
</el-descriptions-item>
<el-descriptions-item label="GAID">
{{ formatText(otherInfo.gaid) }}
</el-descriptions-item>
<el-descriptions-item label="OAID">
{{ formatText(otherInfo.oaid) }}
</el-descriptions-item>
<el-descriptions-item label="手机号">
{{ formatText(otherInfo.phoneNumber) }}
</el-descriptions-item>
<el-descriptions-item label="ICCID">
{{ formatText(otherInfo.iccid) }}
</el-descriptions-item>
<el-descriptions-item label="IMSI">
{{ formatText(otherInfo.imsi) }}
</el-descriptions-item>
<el-descriptions-item label="充电中">
{{ formatBool(otherInfo.isCharging) }}
</el-descriptions-item>
<el-descriptions-item label="充电类型">
{{ formatText(otherInfo.chargingType) }}
</el-descriptions-item>
<el-descriptions-item label="设备温度">
{{ formatText(otherInfo.temperature) }}
</el-descriptions-item>
<el-descriptions-item label="剩余存储">
{{ formatText(otherInfo.freeStorage) }}
</el-descriptions-item>
<el-descriptions-item label="剩余内存">
{{ formatText(otherInfo.freeRam) }}
</el-descriptions-item>
<el-descriptions-item label="传感器数量">
{{ formatText(otherInfo.sensorCount) }}
</el-descriptions-item>
<el-descriptions-item label="应用列表 MD5" :span="2">
{{ formatText(otherInfo.appListMd5) }}
</el-descriptions-item>
</el-descriptions>
</div>
</div>
</el-collapse-item>
</el-collapse>
</el-tab-pane>
<!-- 扩展标签页(应用信息 / 策略信息 / 合规状态 / 操作日志 / 定位信息) -->
<el-tab-pane label="应用信息" name="appInfo">
<div class="ta,b-content">
<div class="tab-content">
<el-empty description="应用信息加载中..." />
</div>
</el-tab-pane>
@@ -349,17 +478,21 @@
<div class="tab-content">
<el-empty description="定位信息加载中..." />
</div>
,
</el-tab-pane>
</el-tabs>
</div>
</div>
</template>
<script setup lang="ts">
import DeviceAPI from "@/api/system/device";
import { ref, reactive, watch, onMounted } from "vue";
import type {
DeviceBasicInfo,
DeviceHardwareInfo,
DeviceNetworkInfo,
DeviceOtherInfo,
DeviceSecurityInfo,
} from "@/api/system/device/types";
import { reactive, ref, watch } from "vue";
import {
Back,
Edit,
@@ -372,12 +505,6 @@ import {
} from "@element-plus/icons-vue";
import { ElMessage, ElMessageBox } from "element-plus";
onMounted(() => {
if (props.serialno) {
loadDeviceDetail(props.serialno);
}
});
// 接收传入的 props
interface Props {
modelValue?: boolean; // 控制对话框显示隐藏
@@ -398,20 +525,77 @@ const emit = defineEmits<{
// 监听 serialno 变化,加载对应的数据
watch(
() => props.serialno,
async (newSerialno) => {
(newSerialno) => {
if (newSerialno) {
await loadDeviceDetail(newSerialno);
loadAll(newSerialno);
}
},
{ immediate: true }
);
// 加载设备详情的方法
const loadDeviceDetail = async (serialno: string) => {
const activeNames = ref(["basic", "hardware", "network", "security", "other"]);
/** 标签页当前选中项(设备信息为第一个 tab,内含基本/硬件/网络/安全/其他分面板) */
const activeTab = ref("deviceInfo");
/** 设备头部数据 */
const deviceData = reactive({
snName: "",
onlineStatus: 0,
owner: "",
department: "",
group: "",
deviceType: "",
source: "",
activationTime: "",
lastOnline: "",
deviceSn: "",
imei: "",
serialNumber: "",
});
/** 设备图片 */
const deviceImage = ref("./src/assets/images/iphone-17-pro.webp");
/** 分面板数据 */
const basicInfo = reactive<Partial<DeviceBasicInfo>>({});
const hardwareInfo = reactive<Partial<DeviceHardwareInfo>>({});
const networkInfo = reactive<Partial<DeviceNetworkInfo>>({});
const securityInfo = reactive<Partial<DeviceSecurityInfo>>({});
const otherInfo = reactive<Partial<DeviceOtherInfo>>({});
const loading = reactive({
basic: false,
hardware: false,
network: false,
security: false,
other: false,
});
/** 空值统一显示占位符 */
function formatText(value?: string | number | null): string {
return value === undefined || value === null || value === "" ? "-" : String(value);
}
/** 0/1 枚举转文案 */
function formatBool(value?: number | null): string {
if (value === undefined || value === null) return "-";
return value === 1 ? "是" : "否";
}
function assignTo<T extends object>(reactiveObj: T, data?: object) {
Object.keys(reactiveObj as object).forEach(
(key) => delete (reactiveObj as Record<string, unknown>)[key]
);
if (data) {
Object.assign(reactiveObj, data);
}
}
/** 加载设备头部信息 */
async function loadDeviceDetail(serialno: string) {
try {
// 调用 API 获取设备详细信息
const response = await DeviceAPI.getFormData(serialno);
// 更新 deviceData 等响应式数据
if (response) {
Object.assign(deviceData, {
snName: response.snName,
@@ -430,118 +614,97 @@ const loadDeviceDetail = async (serialno: string) => {
ElMessage.error("加载设备详情失败");
console.error("Error loading device detail:", error);
}
};
}
// 设备数据
const deviceData = reactive({
snName: "iPhone 15 Pro Max",
onlineStatus: 1,
owner: "张三",
department: "销售部",
group: "华东区域 / 上海分公司",
deviceType: "iOS",
source: "企业注册",
activationTime: "2024-02-18 09:32:21",
lastOnline: "10:15:42",
deviceSn: "550e8400-e29b-41d4-a716-446655440000",
imei: "35455016276432",
serialNumber: "H0FJXOY7XO",
});
/** 基本信息 */
async function loadBasicInfo(serialno: string) {
loading.basic = true;
try {
const data = await DeviceAPI.getBasicInfo(serialno);
assignTo(basicInfo, data);
} catch (e) {
console.error("加载基本信息失败", e);
} finally {
loading.basic = false;
}
}
// 设备图片
const deviceImage = ref("./src/assets/images/iphone-17-pro.webp");
/** 硬件信息 */
async function loadHardwareInfo(serialno: string) {
loading.hardware = true;
try {
const data = await DeviceAPI.getHardwareInfo(serialno);
assignTo(hardwareInfo, data);
} catch (e) {
console.error("加载硬件信息失败", e);
} finally {
loading.hardware = false;
}
}
// 标签页
const activeTab = ref("deviceInfo");
/** 网络信息 */
async function loadNetworkInfo(serialno: string) {
loading.network = true;
try {
const data = await DeviceAPI.getNetworkInfo(serialno);
assignTo(networkInfo, data);
} catch (e) {
console.error("加载网络信息失败", e);
} finally {
loading.network = false;
}
}
// 基本信息
const basicInfo = reactive({
deviceName: "iPhone 15 Pro Max",
deviceModel: "iPhone16,2",
os: "iOS 17.4.1",
osVersion: "21E236",
language: "zh-Hans-CN",
timezone: "Asia/Shanghai",
imei: "00008110-001C195E3EA1802E",
bluetoothMac: "90:81:5B:1B:4A:7F",
wifiMac: "90:81:5B:1B:4A:7F",
color: "原色钛金属",
capacity: "256 GB",
availableCapacity: "128.35 GB (50.1%)",
memoryCapacity: "8 GB",
availableMemory: "3.12 GB (39.0%)",
batteryLevel: "87%",
batteryHealth: "98%",
weight: "221 g",
dimensions: "159.9 x 76.7 x 8.25 mm",
simStatus: "单卡",
phoneNumber: "+86 138 0000 0000",
iccid: "8986 0112 3456 7890 1234",
meid: "35455016276543",
});
/** 安全信息 */
async function loadSecurityInfo(serialno: string) {
loading.security = true;
try {
const data = await DeviceAPI.getSecurityInfo(serialno);
assignTo(securityInfo, data);
} catch (e) {
console.error("加载安全信息失败", e);
} finally {
loading.security = false;
}
}
// 硬件信息
const hardwareInfo = reactive({
processor: "A17 Pro",
processorCores: "6",
gpu: "Apple GPU",
screenSize: "6.7 英寸",
screenResolution: "2796 x 1290",
screenDensity: "4,60 ppi",
camera: "后置三摄 48MP + 12MP + 12MP,前置 12MP",
sensors: "加速度计,陀螺仪,指南针,气压计,距离传感器,环境光传感器,面容 ID",
nfc: "支持",
faceId: "支持",
});
/** 其他信息 */
async function loadOtherInfo(serialno: string) {
loading.other = true;
try {
const data = await DeviceAPI.getOtherInfo(serialno);
assignTo(otherInfo, data);
} catch (e) {
console.error("加载其他信息失败", e);
} finally {
loading.other = false;
}
}
// 网络信息
const networkInfo = reactive({
currentNetwork: "Wi-Fi",
carrier: "中国移动",
signalStrength: "强",
ipAddress: "192.168.1.102",
wifiName: "Office-WiFi",
wifiMac: "90:81:5B:1B:4A:7F",
cellularType: "5G",
roamingStatus: "否",
});
/** 进入界面时分开加载各面板(含头部) */
async function loadAll(serialno?: string) {
if (!serialno) return;
loadDeviceDetail(serialno);
loadBasicInfo(serialno);
loadHardwareInfo(serialno);
loadNetworkInfo(serialno);
loadSecurityInfo(serialno);
loadOtherInfo(serialno);
}
// 安全信息
const securityInfo = reactive({
lockScreenPassword: "已设置",
passwordComplexity: "高",
dataProtection: "已开启",
findMyIPhone: "已开启",
deviceEncryption: "已开启",
jailbreakStatus: "否",
supervisedStatus: "是",
lastSecurityScan: "2024-05-23 09:50:11",
securityPatchVersion: "iOS 17.4.1 (21E236)",
});
defineExpose({ loadAll });
// 其他信息
const otherInfo = reactive({
purchaseDate: "2024-02-10",
warrantyExpiry: "2025-02-09",
source: "企业注册",
assignDate: "2024-02-18",
remark: "销售部员工配发设备",
tags: "重要设备,外勤设备",
customField1: "-",
customField2: "-",
});
// 更新返回按钮事件
/** 操作按钮事件 */
const handleReturn = () => {
emit("update:modelValue", false); // 隐藏对话框
emit("returnClick"); // 触发返回事件
emit("update:modelValue", false);
emit("returnClick");
};
const handleEdit = () => {
ElMessage.success("编辑功能");
};
const handleRefresh = () => {
loadDeviceDetail(props.serialno).then(() => {
loadAll(props.serialno).then(() => {
ElMessage.success("刷新数据成功");
});
};
@@ -695,6 +858,61 @@ const handleMoreActions = (command: string) => {
}
}
.device-sn-detail {
padding: 20px;
margin-bottom: 20px;
background: #fff;
border-radius: 8px;
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.05);
// 各分面板的描述列表统一对齐:
// 采用固定表格布局,让「标签列 / 内容列」在所有面板中列宽完全一致。
// 这样即使某个面板的条目数为奇数(最后一行只有一项)或存在 :span="2" 的跨列项,
// 该单元格也不会拉伸占满整行,从而避免各面板之间竖向分割线参差不齐。
:deep(.el-descriptions) {
--el-descriptions-item-bordered-label-width: 130px;
.el-descriptions__body .el-descriptions__table {
width: 100%;
table-layout: fixed;
}
// 2 列布局共 4 个单元格:标签 130px / 内容自适应 / 标签 130px / 内容自适应
.el-descriptions__body .el-descriptions__table .el-descriptions__cell {
width: calc((100% - 260px) / 2);
}
.el-descriptions__label {
width: 130px;
min-width: 130px;
font-weight: 500;
vertical-align: middle;
color: #606266;
text-align: left;
background-color: #fafafa;
}
.el-descriptions__content {
vertical-align: middle;
word-break: break-all;
}
}
// 分面板之间保留统一间距,避免参差不齐
:deep(.el-collapse-item) {
& + .el-collapse-item {
margin-top: 16px;
}
}
.panel-title {
display: flex;
gap: 8px;
align-items: center;
font-weight: 600;
}
}
.tabs-container {
padding: 20px;
background: #fff;
@@ -712,23 +930,7 @@ const handleMoreActions = (command: string) => {
}
.tab-content {
.info-section {
margin-bottom: 24px;
&:last-child {
margin-bottom: 0;
}
.section-title {
display: inline-block;
padding-bottom: 8px;
margin-bottom: 16px;
font-size: 16px;
font-weight: 600;
color: #303133;
border-bottom: 2px solid #409eff;
}
}
min-height: 120px;
}
}
}