fix(operator): fix operator edit issue
All checks were successful
构建并部署前端到测试环境 / build-and-deploy (push) Successful in 4m35s
All checks were successful
构建并部署前端到测试环境 / build-and-deploy (push) Successful in 4m35s
This commit is contained in:
@@ -0,0 +1,249 @@
|
||||
/**
|
||||
* 资产信息格式化工具函数
|
||||
*/
|
||||
|
||||
import type { Ref } from 'vue'
|
||||
|
||||
export function useAssetFormatters(deviceRealtime?: Ref<any>) {
|
||||
// 格式化数据大小(MB转GB/MB)
|
||||
const formatDataSize = (mb: number) => {
|
||||
if (mb >= 1024) {
|
||||
return `${(mb / 1024).toFixed(2)}GB`
|
||||
}
|
||||
return `${mb.toFixed(2)}MB`
|
||||
}
|
||||
|
||||
// 格式化金额(分转元)
|
||||
const formatAmount = (amount: number | undefined) => {
|
||||
if (amount === undefined || amount === null) return '¥0.00'
|
||||
const yuan = amount / 100
|
||||
return `¥${yuan.toFixed(2)}`
|
||||
}
|
||||
|
||||
// 格式化时长(秒转为时分秒)
|
||||
const formatDuration = (seconds?: string) => {
|
||||
if (!seconds) return '-'
|
||||
const sec = Number(seconds)
|
||||
if (isNaN(sec)) return '-'
|
||||
|
||||
const hours = Math.floor(sec / 3600)
|
||||
const minutes = Math.floor((sec % 3600) / 60)
|
||||
const secs = sec % 60
|
||||
|
||||
if (hours > 0) {
|
||||
return `${hours}小时${minutes}分${secs}秒`
|
||||
} else if (minutes > 0) {
|
||||
return `${minutes}分${secs}秒`
|
||||
} else {
|
||||
return `${secs}秒`
|
||||
}
|
||||
}
|
||||
|
||||
// 格式化ICCID为4位一组,用横杠分隔
|
||||
const formatIccidWithDashes = (iccid: string) => {
|
||||
if (!iccid) return ''
|
||||
return iccid.replace(/(\d{4})(?=\d)/g, '$1-')
|
||||
}
|
||||
|
||||
// 获取实名状态名称
|
||||
const getRealNameStatusName = (status?: number) => {
|
||||
if (status === undefined || status === null) return '未知'
|
||||
const statusMap: Record<number, string> = {
|
||||
0: '未实名',
|
||||
1: '已实名'
|
||||
}
|
||||
return statusMap[status] || '未知'
|
||||
}
|
||||
|
||||
// 获取实名状态标签类型
|
||||
const getRealNameStatusType = (status?: number): 'info' | 'success' | 'warning' | 'danger' => {
|
||||
if (status === undefined || status === null) return 'info'
|
||||
const map: Record<number, 'info' | 'success' | 'warning' | 'danger'> = {
|
||||
0: 'info',
|
||||
1: 'success'
|
||||
}
|
||||
return map[status] || 'info'
|
||||
}
|
||||
|
||||
// 获取资产状态名称
|
||||
const getAssetStatusName = (status?: number) => {
|
||||
if (status === undefined || status === null) return '未知'
|
||||
const map: Record<number, string> = {
|
||||
1: '在库',
|
||||
2: '已分销',
|
||||
3: '已激活',
|
||||
4: '已停用'
|
||||
}
|
||||
return map[status] || '未知'
|
||||
}
|
||||
|
||||
// 获取资产状态标签类型
|
||||
const getAssetStatusType = (
|
||||
status?: number
|
||||
): 'primary' | 'success' | 'warning' | 'info' | 'danger' => {
|
||||
if (status === undefined || status === null) return 'info'
|
||||
const map: Record<number, 'primary' | 'success' | 'warning' | 'info' | 'danger'> = {
|
||||
1: 'info', // 在库
|
||||
2: 'success', // 已分销
|
||||
3: 'success', // 已激活
|
||||
4: 'danger' // 已停用
|
||||
}
|
||||
return map[status] || 'info'
|
||||
}
|
||||
|
||||
// 获取在线状态名称
|
||||
const getOnlineStatusName = (status?: number) => {
|
||||
const map: Record<number, string> = {
|
||||
0: '未知',
|
||||
1: '在线',
|
||||
2: '离线'
|
||||
}
|
||||
return map[status ?? 0] || '未知'
|
||||
}
|
||||
|
||||
// 获取在线状态标签类型
|
||||
const getOnlineStatusType = (
|
||||
status?: number
|
||||
): 'primary' | 'success' | 'warning' | 'info' | 'danger' => {
|
||||
const map: Record<number, 'primary' | 'success' | 'warning' | 'info' | 'danger'> = {
|
||||
0: 'info', // 未知
|
||||
1: 'success', // 在线
|
||||
2: 'danger' // 离线
|
||||
}
|
||||
return map[status ?? 0] || 'info'
|
||||
}
|
||||
|
||||
// 获取钱包状态标签类型
|
||||
const getWalletStatusType = (status?: number) => {
|
||||
const map: Record<number, any> = {
|
||||
1: 'success', // 正常
|
||||
2: 'warning', // 冻结
|
||||
3: 'danger' // 关闭
|
||||
}
|
||||
return map[status || 1] || 'info'
|
||||
}
|
||||
|
||||
// 获取套餐状态标签类型
|
||||
const getPackageStatusTagType = (
|
||||
status?: number
|
||||
): 'primary' | 'success' | 'warning' | 'info' | 'danger' => {
|
||||
if (status === undefined || status === null) return 'info'
|
||||
const map: Record<number, 'primary' | 'success' | 'warning' | 'info' | 'danger'> = {
|
||||
0: 'info', // 待生效
|
||||
1: 'success', // 生效中
|
||||
2: 'warning', // 已用完
|
||||
3: 'danger', // 已过期
|
||||
4: 'info' // 已失效
|
||||
}
|
||||
return map[status] || 'info'
|
||||
}
|
||||
|
||||
// 计算信号强度等级(基于RSSI、RSRP、RSRQ)
|
||||
const calculateSignalStrength = () => {
|
||||
if (!deviceRealtime?.value) return '-'
|
||||
|
||||
const rssi = deviceRealtime.value.rssi ? Number(deviceRealtime.value.rssi) : null
|
||||
const rsrp = deviceRealtime.value.rsrp ? Number(deviceRealtime.value.rsrp) : null
|
||||
|
||||
// 优先使用RSRP判断(更准确)
|
||||
// RSRP范围通常在-140到-44 dBm之间,0或正值表示无效数据
|
||||
if (rsrp !== null && !isNaN(rsrp) && rsrp < 0) {
|
||||
if (rsrp >= -80) return '优秀'
|
||||
if (rsrp >= -90) return '良好'
|
||||
if (rsrp >= -100) return '一般'
|
||||
if (rsrp >= -110) return '较差'
|
||||
return '极差'
|
||||
}
|
||||
|
||||
// 其次使用RSSI
|
||||
// RSSI范围通常在-113到-51 dBm之间,0或正值表示无效数据
|
||||
if (rssi !== null && !isNaN(rssi) && rssi < 0) {
|
||||
if (rssi >= -70) return '优秀'
|
||||
if (rssi >= -85) return '良好'
|
||||
if (rssi >= -100) return '一般'
|
||||
if (rssi >= -110) return '较差'
|
||||
return '极差'
|
||||
}
|
||||
|
||||
return '-'
|
||||
}
|
||||
|
||||
// 获取信号强度标签类型
|
||||
const getSignalStrengthType = (): 'primary' | 'success' | 'warning' | 'info' | 'danger' => {
|
||||
const strength = calculateSignalStrength()
|
||||
const map: Record<string, 'primary' | 'success' | 'warning' | 'info' | 'danger'> = {
|
||||
优秀: 'success',
|
||||
良好: 'success',
|
||||
一般: 'warning',
|
||||
较差: 'danger',
|
||||
极差: 'danger'
|
||||
}
|
||||
return map[strength] || 'info'
|
||||
}
|
||||
|
||||
// 获取切卡模式名称
|
||||
const getSwitchModeName = (mode?: string) => {
|
||||
const map: Record<string, string> = {
|
||||
'0': '自动',
|
||||
'1': '手动'
|
||||
}
|
||||
return map[mode || ''] || '-'
|
||||
}
|
||||
|
||||
// 获取交易类型标签颜色
|
||||
const getTransactionTypeTag = (type: string | undefined) => {
|
||||
const map: Record<string, any> = {
|
||||
recharge: 'success', // 充值
|
||||
deduct: 'danger', // 扣款
|
||||
refund: 'warning' // 退款
|
||||
}
|
||||
return map[type || ''] || 'info'
|
||||
}
|
||||
|
||||
// 获取支付状态标签类型
|
||||
const getPaymentStatusType = (status: number): 'success' | 'warning' | 'danger' | 'info' => {
|
||||
const statusMap: Record<number, 'success' | 'warning' | 'danger' | 'info'> = {
|
||||
1: 'warning', // 待支付
|
||||
2: 'success', // 已支付
|
||||
3: 'danger', // 已取消
|
||||
4: 'info' // 已关闭
|
||||
}
|
||||
return statusMap[status] || 'info'
|
||||
}
|
||||
|
||||
// 计算使用进度百分比
|
||||
const getUsageProgress = (cumulative: number, total: number): number => {
|
||||
if (!total || total <= 0) return 0
|
||||
const percentage = (cumulative / total) * 100
|
||||
return Math.min(Math.round(percentage * 100) / 100, 100)
|
||||
}
|
||||
|
||||
// 根据百分比获取进度条颜色
|
||||
const getProgressColorByPercentage = (percentage: number): string => {
|
||||
if (percentage < 50) return '#67c23a'
|
||||
if (percentage < 80) return '#e6a23c'
|
||||
return '#f56c6c'
|
||||
}
|
||||
|
||||
return {
|
||||
formatDataSize,
|
||||
formatAmount,
|
||||
formatDuration,
|
||||
formatIccidWithDashes,
|
||||
getRealNameStatusName,
|
||||
getRealNameStatusType,
|
||||
getAssetStatusName,
|
||||
getAssetStatusType,
|
||||
getOnlineStatusName,
|
||||
getOnlineStatusType,
|
||||
getWalletStatusType,
|
||||
getPackageStatusTagType,
|
||||
calculateSignalStrength,
|
||||
getSignalStrengthType,
|
||||
getSwitchModeName,
|
||||
getTransactionTypeTag,
|
||||
getPaymentStatusType,
|
||||
getUsageProgress,
|
||||
getProgressColorByPercentage
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,352 @@
|
||||
/**
|
||||
* 资产信息数据管理 Composable
|
||||
* 负责资产详情数据的加载和状态管理
|
||||
*/
|
||||
|
||||
import { ref } from 'vue'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { AssetService } from '@/api/modules'
|
||||
import type { AssetWalletResponse } from '@/types/api'
|
||||
|
||||
/**
|
||||
* 格式化数据大小(MB -> GB/MB)
|
||||
*/
|
||||
const formatDataSize = (mb: number) => {
|
||||
if (mb >= 1024) {
|
||||
return `${(mb / 1024).toFixed(2)}GB`
|
||||
}
|
||||
return `${mb.toFixed(2)}MB`
|
||||
}
|
||||
|
||||
export function useAssetInfo() {
|
||||
// 状态管理
|
||||
const loading = ref(false)
|
||||
const cardInfo = ref<any>(null) // 使用 any 以保持与原始实现一致
|
||||
const deviceRealtime = ref<DeviceRealtimeInfo | null>(null)
|
||||
const currentPackage = ref<PackageInfo | null>(null)
|
||||
const currentPackageLoading = ref(false)
|
||||
const currentPackageErrorMsg = ref('')
|
||||
const packageList = ref<PackageInfo[]>([])
|
||||
const walletInfo = ref<AssetWalletResponse | null>(null)
|
||||
const walletLoading = ref(false)
|
||||
|
||||
/**
|
||||
* 获取资产详情
|
||||
* @param identifier - 资产标识符(ICCID、IMEI、SN、虚拟号或MSISDN)
|
||||
*/
|
||||
const fetchAssetDetail = async (identifier: string) => {
|
||||
try {
|
||||
loading.value = true
|
||||
const response = await AssetService.resolveAsset(identifier)
|
||||
|
||||
if (response.code === 0 && response.data) {
|
||||
const data = response.data
|
||||
|
||||
// 映射API数据到页面显示格式
|
||||
cardInfo.value = {
|
||||
// 基本信息
|
||||
asset_type: data.asset_type, // card 或 device
|
||||
asset_id: data.asset_id,
|
||||
virtual_no: data.virtual_no,
|
||||
status: data.status,
|
||||
batch_no: data.batch_no,
|
||||
shop_id: data.shop_id,
|
||||
shop_name: data.shop_name,
|
||||
series_id: data.series_id,
|
||||
series_name: data.series_name,
|
||||
real_name_status: data.real_name_status,
|
||||
activated_at: data.activated_at,
|
||||
created_at: data.created_at,
|
||||
updated_at: data.updated_at,
|
||||
accumulated_recharge: data.accumulated_recharge,
|
||||
first_commission_paid: data.first_commission_paid,
|
||||
|
||||
// 卡专属字段 (asset_type === 'card')
|
||||
iccid: data.iccid || '',
|
||||
msisdn: data.msisdn || '',
|
||||
imsi: data.imsi || '',
|
||||
carrier_id: data.carrier_id,
|
||||
carrier_type: data.carrier_type || '',
|
||||
carrier_name: data.carrier_name || '',
|
||||
supplier: data.supplier || '',
|
||||
network_status: data.network_status,
|
||||
bound_device_id: data.bound_device_id,
|
||||
bound_device_no: data.bound_device_no || '',
|
||||
bound_device_name: data.bound_device_name || '',
|
||||
card_category: data.card_category || '',
|
||||
activation_status: data.activation_status,
|
||||
enable_polling: data.enable_polling,
|
||||
|
||||
// 设备专属字段 (asset_type === 'device')
|
||||
device_name: data.device_name || '',
|
||||
device_model: data.device_model || '',
|
||||
device_type: data.device_type || '',
|
||||
manufacturer: data.manufacturer || '',
|
||||
imei: data.imei || '',
|
||||
sn: data.sn || '',
|
||||
max_sim_slots: data.max_sim_slots || 0,
|
||||
bound_card_count: data.bound_card_count || 0,
|
||||
cards: data.cards || [],
|
||||
device_protect_status: data.device_protect_status,
|
||||
online_status: data.online_status,
|
||||
last_online_time: data.last_online_time,
|
||||
software_version: data.software_version,
|
||||
switch_mode: data.switch_mode,
|
||||
last_gateway_sync_at: data.last_gateway_sync_at,
|
||||
|
||||
// 流量/套餐信息
|
||||
current_package: data.current_package || '',
|
||||
packageSeries: data.series_name || '-',
|
||||
packageTotalFlow: formatDataSize(data.package_total_mb || 0),
|
||||
usedFlow: formatDataSize(data.package_used_mb || 0),
|
||||
remainFlow: formatDataSize(data.package_remain_mb || 0),
|
||||
usedFlowPercentage:
|
||||
data.package_total_mb > 0
|
||||
? `${((data.package_used_mb / data.package_total_mb) * 100).toFixed(2)}%`
|
||||
: '0.00%',
|
||||
packageList: [] // 套餐列表需要单独调用API获取
|
||||
}
|
||||
|
||||
// 获取资产标识符并加载其他数据
|
||||
const assetIdentifier = data.asset_type === 'card' ? data.iccid : data.virtual_no
|
||||
if (assetIdentifier) {
|
||||
// 并行调用多个接口: 当前生效套餐、套餐列表、实时状态、钱包概况
|
||||
Promise.all([
|
||||
loadCurrentPackage(assetIdentifier),
|
||||
loadPackageList(assetIdentifier),
|
||||
loadRealtimeStatus(assetIdentifier, data.asset_type),
|
||||
loadAssetWallet(assetIdentifier)
|
||||
])
|
||||
}
|
||||
} else {
|
||||
ElMessage.error(response.msg || '查询失败')
|
||||
cardInfo.value = null
|
||||
}
|
||||
} catch (error: any) {
|
||||
console.error('获取卡片信息失败:', error)
|
||||
if (error?.response?.status === 404) {
|
||||
ElMessage.error('未找到该资产')
|
||||
} else if (error?.response?.status === 403) {
|
||||
ElMessage.error('无权限访问该资产')
|
||||
} else {
|
||||
ElMessage.error(error?.message || '获取卡片信息失败')
|
||||
}
|
||||
cardInfo.value = null
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载套餐列表
|
||||
* @param identifier - 资产标识符
|
||||
*/
|
||||
const loadPackageList = async (identifier: string) => {
|
||||
try {
|
||||
const response = await AssetService.getAssetPackages(identifier, {
|
||||
page: 1,
|
||||
page_size: 50
|
||||
})
|
||||
if (response.code === 0 && response.data) {
|
||||
// 使用分页响应中的 items 数组
|
||||
cardInfo.value.packageList = response.data.items || []
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取套餐列表失败:', error)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载当前生效套餐
|
||||
* @param identifier - 资产标识符
|
||||
*/
|
||||
const loadCurrentPackage = async (identifier: string) => {
|
||||
try {
|
||||
currentPackageLoading.value = true
|
||||
// 清空之前的错误信息
|
||||
currentPackageErrorMsg.value = ''
|
||||
|
||||
const response = await AssetService.getCurrentPackage(identifier)
|
||||
if (response.code === 0) {
|
||||
if (response.data) {
|
||||
const pkg = response.data
|
||||
// 保存完整的当前套餐数据
|
||||
cardInfo.value.currentPackageDetail = {
|
||||
package_usage_id: pkg.package_usage_id,
|
||||
package_id: pkg.package_id,
|
||||
package_name: pkg.package_name,
|
||||
package_type: pkg.package_type,
|
||||
status: pkg.status,
|
||||
status_name: pkg.status_name,
|
||||
data_limit_mb: pkg.data_limit_mb,
|
||||
data_usage_mb: pkg.data_usage_mb,
|
||||
virtual_limit_mb: pkg.virtual_limit_mb,
|
||||
virtual_used_mb: pkg.virtual_used_mb,
|
||||
virtual_remain_mb: pkg.virtual_remain_mb,
|
||||
virtual_ratio: pkg.virtual_ratio,
|
||||
activated_at: pkg.activated_at,
|
||||
expires_at: pkg.expires_at,
|
||||
created_at: pkg.created_at,
|
||||
master_usage_id: pkg.master_usage_id,
|
||||
priority: pkg.priority,
|
||||
usage_type: pkg.usage_type
|
||||
}
|
||||
// 同时更新流量显示信息
|
||||
cardInfo.value.current_package = pkg.package_name
|
||||
cardInfo.value.packageTotalFlow = formatDataSize(pkg.virtual_limit_mb || 0)
|
||||
cardInfo.value.usedFlow = formatDataSize(pkg.virtual_used_mb || 0)
|
||||
cardInfo.value.remainFlow = formatDataSize(pkg.virtual_remain_mb || 0)
|
||||
cardInfo.value.usedFlowPercentage =
|
||||
(pkg.virtual_limit_mb || 0) > 0
|
||||
? `${(((pkg.virtual_used_mb || 0) / (pkg.virtual_limit_mb || 1)) * 100).toFixed(2)}%`
|
||||
: '0.00%'
|
||||
} else {
|
||||
// code 为 0 但 data 为空,表示暂无当前生效套餐(正常情况)
|
||||
currentPackageErrorMsg.value = '暂无当前生效套餐'
|
||||
}
|
||||
} else {
|
||||
// 接口返回 code 不为 0,保存错误信息
|
||||
currentPackageErrorMsg.value = response.msg || '获取当前套餐失败'
|
||||
}
|
||||
} catch (error: any) {
|
||||
// 404表示无当前生效套餐,这是正常情况
|
||||
if (error?.response?.status === 404) {
|
||||
currentPackageErrorMsg.value = '暂无当前生效套餐'
|
||||
} else {
|
||||
console.error('获取当前套餐失败:', error)
|
||||
currentPackageErrorMsg.value = error?.message || '获取当前套餐失败'
|
||||
}
|
||||
} finally {
|
||||
currentPackageLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载实时状态
|
||||
* @param identifier - 资产标识符
|
||||
* @param assetType - 资产类型(card/device)
|
||||
*/
|
||||
const loadRealtimeStatus = async (identifier: string, assetType: string) => {
|
||||
try {
|
||||
const response = await AssetService.getRealtimeStatus(identifier)
|
||||
if (response.code === 0 && response.data) {
|
||||
const data = response.data
|
||||
// 更新实时状态数据
|
||||
if (assetType === 'card') {
|
||||
// 卡的实时状态
|
||||
if (data.network_status !== undefined) {
|
||||
cardInfo.value.network_status = data.network_status
|
||||
}
|
||||
if (data.real_name_status !== undefined) {
|
||||
cardInfo.value.real_name_status = data.real_name_status
|
||||
}
|
||||
if (data.last_sync_time !== undefined) {
|
||||
cardInfo.value.last_sync_time = data.last_sync_time
|
||||
}
|
||||
if (data.current_month_usage_mb !== undefined) {
|
||||
cardInfo.value.current_month_usage_mb = data.current_month_usage_mb
|
||||
}
|
||||
} else if (assetType === 'device') {
|
||||
// 设备的实时状态
|
||||
if (data.device_protect_status !== undefined) {
|
||||
cardInfo.value.device_protect_status = data.device_protect_status
|
||||
}
|
||||
if (data.cards && data.cards.length > 0) {
|
||||
cardInfo.value.cards = data.cards
|
||||
}
|
||||
if (data.online_status !== undefined) {
|
||||
cardInfo.value.online_status = data.online_status
|
||||
}
|
||||
if (data.last_online_time !== undefined) {
|
||||
cardInfo.value.last_online_time = data.last_online_time
|
||||
}
|
||||
if (data.software_version !== undefined) {
|
||||
cardInfo.value.software_version = data.software_version
|
||||
}
|
||||
if (data.switch_mode !== undefined) {
|
||||
cardInfo.value.switch_mode = data.switch_mode
|
||||
}
|
||||
if (data.last_gateway_sync_at !== undefined) {
|
||||
cardInfo.value.last_gateway_sync_at = data.last_gateway_sync_at
|
||||
}
|
||||
// 设备实时信息(Gateway 数据)
|
||||
if (data.device_realtime !== undefined) {
|
||||
deviceRealtime.value = data.device_realtime
|
||||
console.log('设备实时信息:', deviceRealtime.value)
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取实时状态失败:', error)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载钱包概况
|
||||
* @param identifier - 资产标识符
|
||||
*/
|
||||
const loadAssetWallet = async (identifier: string) => {
|
||||
try {
|
||||
walletLoading.value = true
|
||||
const response = await AssetService.getAssetWallet(identifier)
|
||||
if (response.code === 0 && response.data) {
|
||||
walletInfo.value = response.data
|
||||
}
|
||||
} catch (error: any) {
|
||||
if (error?.response?.status === 403) {
|
||||
// 企业账号禁止查看钱包信息,不显示错误
|
||||
walletInfo.value = null
|
||||
} else {
|
||||
console.error('获取钱包概况失败:', error)
|
||||
}
|
||||
} finally {
|
||||
walletLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 刷新资产数据(调用refresh接口后重新加载)
|
||||
* @param identifier - 资产标识符
|
||||
* @param assetType - 资产类型(card/device)
|
||||
*/
|
||||
const refreshAsset = async (identifier: string, assetType: string) => {
|
||||
try {
|
||||
await AssetService.refreshAsset(identifier)
|
||||
ElMessage.success('刷新成功')
|
||||
|
||||
// 刷新成功后重新加载实时状态数据
|
||||
if (identifier) {
|
||||
await loadRealtimeStatus(identifier, assetType)
|
||||
}
|
||||
} catch (error: any) {
|
||||
// 检查429错误(冷却期)
|
||||
if (error?.response?.status === 429) {
|
||||
ElMessage.warning('刷新过于频繁,请稍后再试')
|
||||
} else {
|
||||
ElMessage.error(error?.message || '刷新失败')
|
||||
}
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
// State
|
||||
loading,
|
||||
cardInfo,
|
||||
deviceRealtime,
|
||||
currentPackage,
|
||||
currentPackageLoading,
|
||||
currentPackageErrorMsg,
|
||||
packageList,
|
||||
walletInfo,
|
||||
walletLoading,
|
||||
|
||||
// Methods
|
||||
fetchAssetDetail,
|
||||
loadPackageList,
|
||||
loadCurrentPackage,
|
||||
loadRealtimeStatus,
|
||||
loadAssetWallet,
|
||||
refreshAsset
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,319 @@
|
||||
import { ref } from 'vue'
|
||||
import type { Ref } from 'vue'
|
||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||
import { AssetService, DeviceService } from '@/api/modules'
|
||||
|
||||
/**
|
||||
* Asset operations composable
|
||||
* Manages asset operation methods (enable/disable cards, device operations, etc.)
|
||||
*/
|
||||
export function useAssetOperations(cardInfo: Ref<any>, refreshAssetFn?: () => Promise<void>) {
|
||||
// Loading states
|
||||
const enableCardLoading = ref(false)
|
||||
const disableCardLoading = ref(false)
|
||||
const manualDeactivateCardLoading = ref(false)
|
||||
const rebootDeviceLoading = ref(false)
|
||||
const resetDeviceLoading = ref(false)
|
||||
const speedLimitLoading = ref(false)
|
||||
const switchCardLoading = ref(false)
|
||||
const setWiFiLoading = ref(false)
|
||||
const pollingLoading = ref(false)
|
||||
|
||||
/**
|
||||
* Enable card
|
||||
*/
|
||||
const enableCard = async () => {
|
||||
try {
|
||||
await ElMessageBox.confirm('确认要启用此卡吗?', '提示', {
|
||||
type: 'warning'
|
||||
})
|
||||
|
||||
enableCardLoading.value = true
|
||||
const res = await AssetService.startAsset(cardInfo.value.iccid)
|
||||
if (res.code === 0) {
|
||||
ElMessage.success('启用成功')
|
||||
if (refreshAssetFn) {
|
||||
await refreshAssetFn()
|
||||
}
|
||||
}
|
||||
} catch (error: any) {
|
||||
if (error !== 'cancel') {
|
||||
console.error('启用失败:', error)
|
||||
ElMessage.error(error?.message || '启用失败')
|
||||
}
|
||||
} finally {
|
||||
enableCardLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Disable card with confirmation
|
||||
*/
|
||||
const disableCard = async () => {
|
||||
try {
|
||||
await ElMessageBox.confirm('确认要停用此卡吗?', '提示', {
|
||||
type: 'warning'
|
||||
})
|
||||
|
||||
disableCardLoading.value = true
|
||||
const res = await AssetService.stopAsset(cardInfo.value.iccid)
|
||||
if (res.code === 0) {
|
||||
ElMessage.success('停用成功')
|
||||
if (refreshAssetFn) {
|
||||
await refreshAssetFn()
|
||||
}
|
||||
}
|
||||
} catch (error: any) {
|
||||
if (error !== 'cancel') {
|
||||
console.error('停用失败:', error)
|
||||
ElMessage.error(error?.message || '停用失败')
|
||||
}
|
||||
} finally {
|
||||
disableCardLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Manually deactivate card with strong warning confirmation
|
||||
*/
|
||||
const manualDeactivateCard = async () => {
|
||||
try {
|
||||
await ElMessageBox.confirm(
|
||||
'手动停用后卡片将永久停用,无法再次使用。此操作不可逆,请谨慎操作。确认要手动停用吗?',
|
||||
'警告',
|
||||
{
|
||||
type: 'error',
|
||||
confirmButtonText: '确认停用',
|
||||
cancelButtonText: '取消'
|
||||
}
|
||||
)
|
||||
|
||||
manualDeactivateCardLoading.value = true
|
||||
const identifier =
|
||||
cardInfo.value.asset_type === 'card' ? cardInfo.value.iccid : cardInfo.value.virtual_no
|
||||
const res = await AssetService.deactivateAsset(identifier)
|
||||
if (res.code === 0) {
|
||||
ElMessage.success('手动停用成功')
|
||||
if (refreshAssetFn) {
|
||||
await refreshAssetFn()
|
||||
}
|
||||
}
|
||||
} catch (error: any) {
|
||||
if (error !== 'cancel') {
|
||||
console.error('手动停用失败:', error)
|
||||
ElMessage.error(error?.message || '手动停用失败')
|
||||
}
|
||||
} finally {
|
||||
manualDeactivateCardLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reboot device with confirmation
|
||||
*/
|
||||
const rebootDevice = async () => {
|
||||
try {
|
||||
await ElMessageBox.confirm('确认要重启设备吗?', '提示', {
|
||||
type: 'warning'
|
||||
})
|
||||
|
||||
rebootDeviceLoading.value = true
|
||||
const res = await DeviceService.rebootDevice(cardInfo.value.imei)
|
||||
if (res.code === 0) {
|
||||
ElMessage.success(res.data?.message || '重启指令已发送')
|
||||
if (refreshAssetFn) {
|
||||
await refreshAssetFn()
|
||||
}
|
||||
}
|
||||
} catch (error: any) {
|
||||
if (error !== 'cancel') {
|
||||
console.error('重启失败:', error)
|
||||
ElMessage.error(error?.message || '重启失败')
|
||||
}
|
||||
} finally {
|
||||
rebootDeviceLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Factory reset device with strong warning confirmation
|
||||
*/
|
||||
const resetDevice = async () => {
|
||||
try {
|
||||
await ElMessageBox.confirm('恢复出厂设置将清除设备所有数据和配置,确认要继续吗?', '警告', {
|
||||
type: 'error',
|
||||
confirmButtonText: '确认恢复',
|
||||
cancelButtonText: '取消'
|
||||
})
|
||||
|
||||
resetDeviceLoading.value = true
|
||||
const res = await DeviceService.resetDevice(cardInfo.value.imei)
|
||||
if (res.code === 0) {
|
||||
ElMessage.success(res.data?.message || '恢复出厂设置指令已发送')
|
||||
if (refreshAssetFn) {
|
||||
await refreshAssetFn()
|
||||
}
|
||||
}
|
||||
} catch (error: any) {
|
||||
if (error !== 'cancel') {
|
||||
console.error('恢复出厂失败:', error)
|
||||
ElMessage.error(error?.message || '恢复出厂失败')
|
||||
}
|
||||
} finally {
|
||||
resetDeviceLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set device speed limit
|
||||
*/
|
||||
const setSpeedLimit = async (form: { download_speed: number; upload_speed: number }) => {
|
||||
try {
|
||||
speedLimitLoading.value = true
|
||||
const res = await DeviceService.setSpeedLimit(cardInfo.value.imei, {
|
||||
download_speed: form.download_speed,
|
||||
upload_speed: form.upload_speed
|
||||
})
|
||||
if (res.code === 0) {
|
||||
ElMessage.success('限速设置成功')
|
||||
if (refreshAssetFn) {
|
||||
await refreshAssetFn()
|
||||
}
|
||||
return true
|
||||
} else {
|
||||
ElMessage.error(res.msg || '设置失败')
|
||||
return false
|
||||
}
|
||||
} catch (error: any) {
|
||||
console.error('设置限速失败:', error)
|
||||
ElMessage.error(error?.message || '设置失败')
|
||||
return false
|
||||
} finally {
|
||||
speedLimitLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Switch to different card
|
||||
*/
|
||||
const switchCard = async (form: { target_iccid: string }) => {
|
||||
try {
|
||||
switchCardLoading.value = true
|
||||
const res = await DeviceService.switchCard(cardInfo.value.imei, {
|
||||
target_iccid: form.target_iccid
|
||||
})
|
||||
if (res.code === 0) {
|
||||
ElMessage.success('切换SIM卡指令已发送')
|
||||
if (refreshAssetFn) {
|
||||
await refreshAssetFn()
|
||||
}
|
||||
return true
|
||||
} else {
|
||||
ElMessage.error(res.msg || '切换失败')
|
||||
return false
|
||||
}
|
||||
} catch (error: any) {
|
||||
console.error('切换SIM卡失败:', error)
|
||||
ElMessage.error(error?.message || '切换失败')
|
||||
return false
|
||||
} finally {
|
||||
switchCardLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure WiFi settings
|
||||
*/
|
||||
const setWiFi = async (form: { enabled: number; ssid: string; password: string }) => {
|
||||
try {
|
||||
setWiFiLoading.value = true
|
||||
const res = await DeviceService.setWiFi(cardInfo.value.imei, {
|
||||
enabled: form.enabled,
|
||||
ssid: form.ssid,
|
||||
password: form.password
|
||||
})
|
||||
if (res.code === 0) {
|
||||
ElMessage.success('WiFi设置成功')
|
||||
if (refreshAssetFn) {
|
||||
await refreshAssetFn()
|
||||
}
|
||||
return true
|
||||
} else {
|
||||
ElMessage.error(res.msg || '设置失败')
|
||||
return false
|
||||
}
|
||||
} catch (error: any) {
|
||||
console.error('设置WiFi失败:', error)
|
||||
ElMessage.error(error?.message || '设置WiFi失败')
|
||||
return false
|
||||
} finally {
|
||||
setWiFiLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update polling status
|
||||
*/
|
||||
const updatePollingStatus = async (enabled: boolean) => {
|
||||
if (!cardInfo.value) {
|
||||
ElMessage.warning('无法更新轮询状态:缺少资产信息')
|
||||
return false
|
||||
}
|
||||
|
||||
try {
|
||||
pollingLoading.value = true
|
||||
const identifier =
|
||||
cardInfo.value.asset_type === 'card' ? cardInfo.value.iccid : cardInfo.value.virtual_no
|
||||
|
||||
const response = await AssetService.updatePollingStatus(identifier, {
|
||||
enable_polling: enabled
|
||||
})
|
||||
|
||||
if (response.code === 0) {
|
||||
ElMessage.success(enabled ? '已开启自动轮询' : '已关闭自动轮询')
|
||||
// Update local state
|
||||
if (cardInfo.value) {
|
||||
cardInfo.value.enable_polling = enabled
|
||||
}
|
||||
return true
|
||||
} else {
|
||||
ElMessage.error(response.msg || '更新轮询状态失败')
|
||||
return false
|
||||
}
|
||||
} catch (error: any) {
|
||||
console.error('更新轮询状态失败:', error)
|
||||
ElMessage.error(error?.message || '更新轮询状态失败')
|
||||
return false
|
||||
} finally {
|
||||
pollingLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
// Loading states
|
||||
enableCardLoading,
|
||||
disableCardLoading,
|
||||
manualDeactivateCardLoading,
|
||||
rebootDeviceLoading,
|
||||
resetDeviceLoading,
|
||||
speedLimitLoading,
|
||||
switchCardLoading,
|
||||
setWiFiLoading,
|
||||
pollingLoading,
|
||||
|
||||
// IoT card operations
|
||||
enableCard,
|
||||
disableCard,
|
||||
manualDeactivateCard,
|
||||
|
||||
// Device operations
|
||||
rebootDevice,
|
||||
resetDevice,
|
||||
setSpeedLimit,
|
||||
switchCard,
|
||||
setWiFi,
|
||||
|
||||
// Polling
|
||||
updatePollingStatus
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user