This commit is contained in:
@@ -7,99 +7,77 @@ import { getCarriers } from '@/api/carrier'
|
|||||||
import type { CarrierInfo } from '@/api/carrier'
|
import type { CarrierInfo } from '@/api/carrier'
|
||||||
import SimplePicker from '@/components/SimplePicker.vue'
|
import SimplePicker from '@/components/SimplePicker.vue'
|
||||||
|
|
||||||
// 企业ID (用于调用企业接口)
|
|
||||||
const enterpriseId = ref<number>(0)
|
const enterpriseId = ref<number>(0)
|
||||||
|
|
||||||
// 卡片列表
|
|
||||||
const cardList = ref<CardInfo[]>([])
|
const cardList = ref<CardInfo[]>([])
|
||||||
|
|
||||||
// 加载状态
|
|
||||||
const loading = ref(false)
|
const loading = ref(false)
|
||||||
const loadingMore = ref(false)
|
|
||||||
|
|
||||||
// 搜索关键词
|
|
||||||
const searchKeyword = ref('')
|
const searchKeyword = ref('')
|
||||||
|
|
||||||
// 状态筛选 (undefined 表示全部)
|
|
||||||
const statusFilter = ref<number | undefined>(undefined)
|
const statusFilter = ref<number | undefined>(undefined)
|
||||||
|
|
||||||
// 运营商筛选
|
|
||||||
const carrierFilter = ref<number | undefined>(undefined)
|
const carrierFilter = ref<number | undefined>(undefined)
|
||||||
const showCarrierPicker = ref(false)
|
const showCarrierPicker = ref(false)
|
||||||
|
const showSearchTypePicker = ref(false)
|
||||||
// 运营商列表
|
|
||||||
const carrierList = ref<CarrierInfo[]>([])
|
const carrierList = ref<CarrierInfo[]>([])
|
||||||
|
const page = ref(1)
|
||||||
|
const pageSize = 20
|
||||||
|
const hasMore = ref(true)
|
||||||
|
|
||||||
|
const searchType = ref<'iccid' | 'virtual'>('iccid')
|
||||||
|
|
||||||
// 网络状态选项 (network_status: 0=停机, 1=正常)
|
|
||||||
const statusOptions = [
|
const statusOptions = [
|
||||||
{ label: '全部', value: undefined },
|
{ label: '全部', value: undefined },
|
||||||
{ label: '正常', value: 1 },
|
{ label: '正常', value: 1 },
|
||||||
{ label: '停机', value: 0 },
|
{ label: '停机', value: 0 },
|
||||||
]
|
]
|
||||||
|
|
||||||
// 运营商选项
|
const searchTypeOptions = [
|
||||||
|
{ label: 'ICCID', value: 'iccid' },
|
||||||
|
{ label: '虚拟号', value: 'virtual' }
|
||||||
|
]
|
||||||
|
|
||||||
const carrierOptions = computed(() => [
|
const carrierOptions = computed(() => [
|
||||||
{ label: '全部运营商', value: undefined },
|
{ label: '全部运营商', value: undefined },
|
||||||
...carrierList.value.map(c => ({ label: c.carrier_name, value: c.id }))
|
...carrierList.value.map(c => ({ label: c.carrier_name, value: c.id }))
|
||||||
])
|
])
|
||||||
|
|
||||||
// 获取当前选中的运营商名称
|
|
||||||
const selectedCarrierName = computed(() => {
|
const selectedCarrierName = computed(() => {
|
||||||
if (carrierFilter.value === undefined) return '运营商'
|
if (carrierFilter.value === undefined) return '运营商'
|
||||||
const option = carrierOptions.value.find(o => o.value === carrierFilter.value)
|
const option = carrierOptions.value.find(o => o.value === carrierFilter.value)
|
||||||
return option?.label || '运营商'
|
return option?.label || '运营商'
|
||||||
})
|
})
|
||||||
|
|
||||||
// 分页参数
|
|
||||||
const page = ref(1)
|
|
||||||
const pageSize = 20
|
|
||||||
const hasMore = ref(true)
|
|
||||||
|
|
||||||
// 直接显示卡片列表 (搜索由后端处理)
|
|
||||||
const filteredCards = computed(() => {
|
|
||||||
return cardList.value
|
|
||||||
})
|
|
||||||
|
|
||||||
// 统计数据
|
|
||||||
const stats = computed(() => {
|
const stats = computed(() => {
|
||||||
const total = cardList.value.length
|
const total = cardList.value.length
|
||||||
// network_status: 0=停机, 1=正常
|
|
||||||
const active = cardList.value.filter(c => c.network_status === 1).length
|
const active = cardList.value.filter(c => c.network_status === 1).length
|
||||||
const inactive = cardList.value.filter(c => c.network_status === 0).length
|
const inactive = cardList.value.filter(c => c.network_status === 0).length
|
||||||
// status: 1=可用, 2=已分销, 3=已停用等
|
|
||||||
const distributed = cardList.value.filter(c => c.status === 2).length
|
const distributed = cardList.value.filter(c => c.status === 2).length
|
||||||
|
|
||||||
return { total, active, inactive, distributed }
|
return { total, active, inactive, distributed }
|
||||||
})
|
})
|
||||||
|
|
||||||
// 加载运营商列表
|
|
||||||
async function loadCarriers() {
|
async function loadCarriers() {
|
||||||
try {
|
try {
|
||||||
const response = await getCarriers({ page: 1, size: 20 })
|
const response = await getCarriers({ page: 1, size: 50 })
|
||||||
carrierList.value = response?.items || []
|
carrierList.value = response?.items || []
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('加载运营商列表失败:', error)
|
console.error('加载运营商列表失败:', error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 页面加载
|
async function loadEnterpriseCards(isRefresh = false) {
|
||||||
onMounted(() => {
|
if (loading.value) return
|
||||||
loadCarriers()
|
|
||||||
loadEnterpriseCards()
|
if (isRefresh) {
|
||||||
})
|
page.value = 1
|
||||||
|
hasMore.value = true
|
||||||
|
cardList.value = []
|
||||||
|
}
|
||||||
|
|
||||||
// 加载企业卡列表
|
|
||||||
async function loadEnterpriseCards() {
|
|
||||||
loading.value = true
|
loading.value = true
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// 1. 获取企业ID (优先从本地存储获取)
|
|
||||||
if (!enterpriseId.value) {
|
if (!enterpriseId.value) {
|
||||||
const userInfo = uni.getStorageSync('user_info')
|
const userInfo = uni.getStorageSync('user_info')
|
||||||
if (userInfo?.enterprise_id) {
|
if (userInfo?.enterprise_id) {
|
||||||
enterpriseId.value = userInfo.enterprise_id
|
enterpriseId.value = userInfo.enterprise_id
|
||||||
} else {
|
} else {
|
||||||
// 如果本地存储没有,则调用接口获取
|
|
||||||
const apiUserInfo = await getUserInfo()
|
const apiUserInfo = await getUserInfo()
|
||||||
if (!apiUserInfo.enterprise_id) {
|
if (!apiUserInfo.enterprise_id) {
|
||||||
throw new Error('未找到企业ID')
|
throw new Error('未找到企业ID')
|
||||||
@@ -108,286 +86,276 @@ async function loadEnterpriseCards() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2. 获取企业卡片列表
|
|
||||||
const params: any = {
|
const params: any = {
|
||||||
page: page.value,
|
page: page.value,
|
||||||
page_size: pageSize,
|
page_size: pageSize,
|
||||||
}
|
}
|
||||||
// 只有选择了具体状态才传 status 参数
|
|
||||||
if (statusFilter.value !== undefined) {
|
if (statusFilter.value !== undefined) {
|
||||||
params.status = statusFilter.value
|
params.status = statusFilter.value
|
||||||
}
|
}
|
||||||
// 运营商筛选
|
|
||||||
if (carrierFilter.value !== undefined) {
|
if (carrierFilter.value !== undefined) {
|
||||||
params.carrier_id = carrierFilter.value
|
params.carrier_id = carrierFilter.value
|
||||||
}
|
}
|
||||||
// 添加搜索关键词支持 (根据接口文档)
|
|
||||||
if (searchKeyword.value) {
|
if (searchKeyword.value) {
|
||||||
// 如果关键词看起来像ICCID (纯数字)
|
if (searchType.value === 'iccid') {
|
||||||
if (/^\d+$/.test(searchKeyword.value)) {
|
|
||||||
params.iccid = searchKeyword.value
|
params.iccid = searchKeyword.value
|
||||||
} else {
|
} else {
|
||||||
params.virtual_no = searchKeyword.value
|
params.virtual_no = searchKeyword.value
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const cardsData = await getEnterpriseCards(enterpriseId.value, params)
|
const cardsData = await getEnterpriseCards(enterpriseId.value, params)
|
||||||
|
const items = cardsData?.items || []
|
||||||
|
|
||||||
if (page.value === 1) {
|
if (page.value === 1) {
|
||||||
cardList.value = cardsData?.items || []
|
cardList.value = items
|
||||||
}
|
} else {
|
||||||
else {
|
cardList.value.push(...items)
|
||||||
cardList.value.push(...(cardsData?.items || []))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 判断是否还有更多数据
|
|
||||||
const items = cardsData?.items || []
|
|
||||||
hasMore.value = items.length >= pageSize
|
hasMore.value = items.length >= pageSize
|
||||||
}
|
page.value++
|
||||||
catch (error: any) {
|
} catch (error: any) {
|
||||||
console.error('加载企业卡列表失败:', error)
|
console.error('加载企业卡列表失败:', error)
|
||||||
// 如果是前端逻辑错误(非 API 错误),需要手动显示提示
|
|
||||||
if (error instanceof Error && error.message && !error.statusCode) {
|
if (error instanceof Error && error.message && !error.statusCode) {
|
||||||
uni.$u.toast(error.message)
|
uni.$u.toast(error.message)
|
||||||
}
|
}
|
||||||
// API 请求错误已由拦截器统一处理
|
} finally {
|
||||||
}
|
|
||||||
finally {
|
|
||||||
loading.value = false
|
loading.value = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 查看卡片详情
|
|
||||||
function viewCardDetail(card: any) {
|
function viewCardDetail(card: any) {
|
||||||
// 跳转到资产详情页
|
|
||||||
uni.navigateTo({
|
uni.navigateTo({
|
||||||
url: `/pages/agent-system/asset-search/index?iccid=${card.iccid}`,
|
url: `/pages/agent-system/asset-search/index?iccid=${card.iccid}`,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取网络状态文本 (使用后端返回的 network_status_name)
|
|
||||||
function getNetworkStatusText(networkStatus: number, networkStatusName?: string) {
|
function getNetworkStatusText(networkStatus: number, networkStatusName?: string) {
|
||||||
// 优先使用后端返回的状态名称
|
if (networkStatusName) return networkStatusName
|
||||||
if (networkStatusName) {
|
return networkStatus === 1 ? '正常' : networkStatus === 0 ? '停机' : '未知'
|
||||||
return networkStatusName
|
|
||||||
}
|
|
||||||
// 降级处理
|
|
||||||
const map: Record<number, string> = {
|
|
||||||
0: '停机',
|
|
||||||
1: '正常',
|
|
||||||
}
|
|
||||||
return map[networkStatus] ?? '未知'
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取网络状态颜色
|
|
||||||
function getNetworkStatusColor(networkStatus: number) {
|
function getNetworkStatusColor(networkStatus: number) {
|
||||||
const map: Record<number, any> = {
|
return networkStatus === 1
|
||||||
1: { bg: '#e8f5e9', text: '#2e7d32' }, // 正常
|
? { bg: '#e8f5e9', text: '#2e7d32' }
|
||||||
0: { bg: '#ffebee', text: '#c62828' }, // 停机
|
: networkStatus === 0
|
||||||
}
|
? { bg: '#ffebee', text: '#c62828' }
|
||||||
return map[networkStatus] ?? { bg: '#f5f5f5', text: '#999' }
|
: { bg: '#f5f5f5', text: '#999' }
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取卡状态文本 (使用后端返回的 status_name)
|
|
||||||
function getCardStatusText(status: number, statusName?: string) {
|
function getCardStatusText(status: number, statusName?: string) {
|
||||||
// 优先使用后端返回的状态名称
|
if (statusName) return statusName
|
||||||
if (statusName) {
|
const map: Record<number, string> = { 1: '可用', 2: '已分销', 3: '已停用' }
|
||||||
return statusName
|
|
||||||
}
|
|
||||||
// 降级处理
|
|
||||||
const map: Record<number, string> = {
|
|
||||||
1: '可用',
|
|
||||||
2: '已分销',
|
|
||||||
3: '已停用',
|
|
||||||
}
|
|
||||||
return map[status] ?? '未知'
|
return map[status] ?? '未知'
|
||||||
}
|
}
|
||||||
|
|
||||||
// 清空搜索
|
|
||||||
function clearSearch() {
|
|
||||||
searchKeyword.value = ''
|
|
||||||
page.value = 1
|
|
||||||
hasMore.value = true
|
|
||||||
cardList.value = []
|
|
||||||
loadEnterpriseCards()
|
|
||||||
}
|
|
||||||
|
|
||||||
// 执行搜索
|
|
||||||
function handleSearch() {
|
function handleSearch() {
|
||||||
page.value = 1
|
loadEnterpriseCards(true)
|
||||||
hasMore.value = true
|
|
||||||
cardList.value = []
|
|
||||||
loadEnterpriseCards()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 刷新列表
|
|
||||||
function refreshList() {
|
|
||||||
page.value = 1
|
|
||||||
hasMore.value = true
|
|
||||||
cardList.value = []
|
|
||||||
loadEnterpriseCards()
|
|
||||||
}
|
|
||||||
|
|
||||||
// 状态筛选变化
|
|
||||||
function handleStatusChange(status: number | undefined) {
|
|
||||||
statusFilter.value = status
|
|
||||||
page.value = 1
|
|
||||||
hasMore.value = true
|
|
||||||
cardList.value = []
|
|
||||||
loadEnterpriseCards()
|
|
||||||
}
|
|
||||||
|
|
||||||
// 运营商筛选变化 (SimplePicker确认时触发)
|
|
||||||
function onCarrierConfirm(option: { label: string, value: any }) {
|
function onCarrierConfirm(option: { label: string, value: any }) {
|
||||||
carrierFilter.value = option.value
|
carrierFilter.value = option.value
|
||||||
page.value = 1
|
loadEnterpriseCards(true)
|
||||||
hasMore.value = true
|
|
||||||
cardList.value = []
|
|
||||||
loadEnterpriseCards()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function onSearchTypeConfirm(option: { label: string, value: any }) {
|
||||||
|
searchType.value = option.value
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleStatusChange(status: number | undefined) {
|
||||||
|
statusFilter.value = status
|
||||||
|
loadEnterpriseCards(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
function loadMore() {
|
||||||
|
if (hasMore.value && !loading.value) {
|
||||||
|
loadEnterpriseCards()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function onScroll(e: any) {
|
||||||
|
const { scrollTop, scrollHeight, clientHeight } = e.target
|
||||||
|
if (scrollHeight - scrollTop - clientHeight < 100) {
|
||||||
|
loadMore()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(async () => {
|
||||||
|
loadCarriers()
|
||||||
|
loadEnterpriseCards()
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<view class="bg-[var(--bg-page)] min-h-screen pb-safe">
|
<view class="bg-page min-h-screen flex flex-col">
|
||||||
<view v-if="loading" class="pt-20 center">
|
<!-- 搜索区域 -->
|
||||||
<text class="text-14px text-[var(--text-secondary)]">加载中...</text>
|
<view class="bg-white px-4 pt-4 pb-3">
|
||||||
|
<!-- 搜索框 -->
|
||||||
|
<view class="relative flex items-center">
|
||||||
|
<input
|
||||||
|
v-model="searchKeyword"
|
||||||
|
class="flex-1 h-10 bg-[#f1f5f9] rounded-full pl-4 pr-12 text-sm text-[#1e293b] placeholder-[#94a3b8]"
|
||||||
|
:placeholder="searchType === 'iccid' ? '搜索ICCID' : '搜索虚拟号'"
|
||||||
|
@confirm="handleSearch"
|
||||||
|
/>
|
||||||
|
<view class="absolute right-1 flex items-center">
|
||||||
|
<view v-if="searchKeyword" class="w-8 h-8 flex items-center justify-center text-[#94a3b8]"
|
||||||
|
@click="searchKeyword = ''">
|
||||||
|
<i class="i-mdi-close-circle text-lg" />
|
||||||
|
</view>
|
||||||
|
<view class="w-8 h-8 flex items-center justify-center text-brand" @click="handleSearch">
|
||||||
|
<i class="i-mdi-magnify text-xl" />
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 统计信息 -->
|
||||||
|
<view class="flex items-center justify-around gap-3 mt-3">
|
||||||
|
<view class="flex items-center gap-1">
|
||||||
|
<text class="text-12px text-[#999]">卡总数</text>
|
||||||
|
<text class="text-14px font-700 text-brand">{{ stats.total }}</text>
|
||||||
|
</view>
|
||||||
|
<view class="flex items-center gap-1">
|
||||||
|
<view class="w-2 h-2 rounded-full bg-[#52c41a]" />
|
||||||
|
<text class="text-12px text-[#666]">正常</text>
|
||||||
|
<text class="text-12px font-600 text-[#212121]">{{ stats.active }}</text>
|
||||||
|
</view>
|
||||||
|
<view class="flex items-center gap-1">
|
||||||
|
<view class="w-2 h-2 rounded-full bg-[#ff4d4f]" />
|
||||||
|
<text class="text-12px text-[#666]">停机</text>
|
||||||
|
<text class="text-12px font-600 text-[#212121]">{{ stats.inactive }}</text>
|
||||||
|
</view>
|
||||||
|
<view class="flex items-center gap-1">
|
||||||
|
<view class="w-2 h-2 rounded-full bg-brand" />
|
||||||
|
<text class="text-12px text-[#666]">已分销</text>
|
||||||
|
<text class="text-12px font-600 text-[#212121]">{{ stats.distributed }}</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 筛选器 -->
|
||||||
|
<view class="flex items-center gap-2 mt-3 flex-wrap">
|
||||||
|
|
||||||
|
<view class="flex items-center gap-1 px-3 py-1.5 bg-[#f8fafc] rounded-full flex-shrink-0"
|
||||||
|
@click="showSearchTypePicker = true">
|
||||||
|
<text class="text-12px" :class="searchType !== 'iccid' ? 'text-brand font-medium' : 'text-[#64748b]'">
|
||||||
|
{{ searchType === 'iccid' ? 'ICCID' : '虚拟号' }}
|
||||||
|
</text>
|
||||||
|
<i class="i-mdi-chevron-down text-12px text-[#94a3b8]" />
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="flex items-center gap-1.5 px-3 py-1.5 bg-[#f8fafc] rounded-full flex-shrink-0 max-w-36"
|
||||||
|
@click="showCarrierPicker = true">
|
||||||
|
<text class="text-12px truncate" :class="carrierFilter !== undefined ? 'text-brand font-medium' : 'text-[#64748b]'">
|
||||||
|
{{ selectedCarrierName }}
|
||||||
|
</text>
|
||||||
|
<i class="i-mdi-chevron-down text-12px text-[#94a3b8] flex-shrink-0" />
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="flex bg-[#f1f5f9] rounded-full flex-shrink-0">
|
||||||
|
<view
|
||||||
|
v-for="option in statusOptions"
|
||||||
|
:key="option.value"
|
||||||
|
class="px-3 py-1.5 text-12px font-medium transition-all rounded-full"
|
||||||
|
:class="statusFilter === option.value ? 'bg-brand text-white' : 'text-[#64748b]'"
|
||||||
|
@click="handleStatusChange(option.value)"
|
||||||
|
>
|
||||||
|
{{ option.label }}
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view
|
||||||
|
v-if="carrierFilter !== undefined || statusFilter !== undefined"
|
||||||
|
class="flex items-center gap-1 px-2 py-1.5 rounded-full bg-[#ff4d4f]/10 text-[#ff4d4f] flex-shrink-0"
|
||||||
|
@click="carrierFilter = undefined; statusFilter = undefined; loadEnterpriseCards(true)"
|
||||||
|
>
|
||||||
|
<i class="i-mdi-close text-12px" />
|
||||||
|
<text class="text-12px font-medium">清除</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<view v-else>
|
<!-- 列表内容 -->
|
||||||
<view class="bg-brand px-4 py-3 mx-4 mt-4 rounded-12px">
|
<view class="flex-1 overflow-y-auto min-h-0" @scroll="onScroll">
|
||||||
<text class="text-11px text-white/60">企业授权IoT卡</text>
|
<!-- 卡片列表 -->
|
||||||
<text class="text-24px font-700 text-white block mt-1">{{ stats.total }}</text>
|
<view v-if="loading && cardList.length === 0" class="pt-20 flex justify-center">
|
||||||
|
<text class="text-14px text-[#64748b]">加载中...</text>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<view class="px-4 mt-4">
|
<view v-else-if="cardList.length > 0" class="px-4 pb-4">
|
||||||
<view class="flex items-center gap-2 mb-3">
|
<view
|
||||||
<input
|
v-for="card in cardList"
|
||||||
v-model="searchKeyword"
|
:key="card.id"
|
||||||
class="flex-1 h-40px bg-[var(--bg-container)] rounded-full px-4 text-14px text-[var(--text-primary)]"
|
class="bg-white rounded-12px p-4 mt-3"
|
||||||
placeholder="搜索 ICCID / 虚拟号 / 手机号"
|
@click="viewCardDetail(card)"
|
||||||
placeholder-class="text-[var(--text-quaternary)]"
|
>
|
||||||
@confirm="handleSearch"
|
<view class="flex items-center justify-between mb-2">
|
||||||
>
|
<view class="flex items-center gap-2">
|
||||||
<view
|
<text class="text-15px font-600 text-[#212121]">{{ card.carrier_name || '-' }}</text>
|
||||||
class="px-4 h-40px bg-brand text-white text-14px rounded-full flex items-center justify-center"
|
<text class="text-12px text-[#999]">{{ getCardStatusText(card.status, card.status_name) }}</text>
|
||||||
@click="handleSearch"
|
|
||||||
>
|
|
||||||
搜索
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
|
|
||||||
<view class="flex items-center gap-2 mb-3">
|
|
||||||
<view class="flex-1 bg-[var(--bg-container)] rounded-8px px-3 h-40px flex items-center" @click="showCarrierPicker = true">
|
|
||||||
<text class="flex-1 text-14px text-[var(--text-primary)]">{{ selectedCarrierName }}</text>
|
|
||||||
<text class="text-12px text-[var(--text-tertiary)]">▼</text>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
|
|
||||||
<view class="px-4">
|
|
||||||
<view class="bg-[var(--bg-container)] rounded-12px p-4 mb-3">
|
|
||||||
<view class="grid grid-cols-3 gap-3">
|
|
||||||
<view class="text-center">
|
|
||||||
<text class="text-11px text-[var(--text-tertiary)] block">正常</text>
|
|
||||||
<text class="text-18px font-700 text-[#52c41a] mt-1">{{ stats.active }}</text>
|
|
||||||
</view>
|
</view>
|
||||||
<view class="text-center">
|
|
||||||
<text class="text-11px text-[var(--text-tertiary)] block">停机</text>
|
|
||||||
<text class="text-18px font-700 text-[#ff4d4f] mt-1">{{ stats.inactive }}</text>
|
|
||||||
</view>
|
|
||||||
<view class="text-center">
|
|
||||||
<text class="text-11px text-[var(--text-tertiary)] block">已分销</text>
|
|
||||||
<text class="text-18px font-700 text-brand mt-1">{{ stats.distributed }}</text>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
|
|
||||||
<view class="flex items-center gap-2 mt-4">
|
|
||||||
<view
|
<view
|
||||||
v-for="option in statusOptions"
|
:style="{
|
||||||
:key="option.value"
|
backgroundColor: getNetworkStatusColor(card.network_status).bg,
|
||||||
:class="[
|
color: getNetworkStatusColor(card.network_status).text,
|
||||||
'flex-1 text-center py-2 rounded-full text-13px transition-all',
|
}"
|
||||||
statusFilter === option.value
|
class="px-3 py-1 rounded-full text-12px font-600"
|
||||||
? 'bg-brand text-white font-600'
|
|
||||||
: 'bg-[var(--bg-muted)] text-[var(--text-secondary)]',
|
|
||||||
]"
|
|
||||||
@click="handleStatusChange(option.value)"
|
|
||||||
>
|
>
|
||||||
{{ option.label }}
|
{{ getNetworkStatusText(card.network_status, card.network_status_name) }}
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
|
||||||
|
|
||||||
<view v-if="filteredCards.length > 0" class="space-y-3 pb-4">
|
<view class="text-13px text-[#666] mb-2">
|
||||||
<view
|
{{ card.package_name || '暂无套餐' }}
|
||||||
v-for="card in filteredCards"
|
</view>
|
||||||
:key="card.id"
|
|
||||||
class="bg-[var(--bg-container)] rounded-12px p-4"
|
<view class="flex items-center justify-between text-12px text-[#999]">
|
||||||
@click="viewCardDetail(card)"
|
<text class="truncate flex-1">{{ card.iccid || '-' }}</text>
|
||||||
>
|
<text class="ml-2">{{ card.msisdn || '-' }}</text>
|
||||||
<view class="flex items-center justify-between">
|
</view>
|
||||||
<view>
|
|
||||||
<text class="text-15px font-600 text-[var(--text-primary)]">{{ card.carrier_name || '-' }}</text>
|
<view v-if="card.virtual_no" class="text-12px text-[#999] mt-1">
|
||||||
<text class="text-12px text-[var(--text-tertiary)] ml-2">{{ getCardStatusText(card.status, card.status_name) }}</text>
|
虚拟号: {{ card.virtual_no }}
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
|
<view v-if="card.data_total && parseFloat(card.data_total) > 0" class="mt-3 bg-[#f5f5f5] rounded-8px p-3">
|
||||||
|
<view class="flex items-center justify-between mb-2">
|
||||||
|
<text class="text-12px text-[#666]">流量</text>
|
||||||
|
<text class="text-12px font-600 text-[#212121]">
|
||||||
|
{{ card.data_usage || '0' }} / {{ card.data_total || '0' }}
|
||||||
|
</text>
|
||||||
|
</view>
|
||||||
|
<view class="h-2 bg-[#e0e0e0] rounded-full overflow-hidden">
|
||||||
<view
|
<view
|
||||||
:style="{
|
:style="{ width: `${Math.min(100, (parseFloat(card.data_usage || '0') / parseFloat(card.data_total || '1')) * 100)}%` }"
|
||||||
backgroundColor: getNetworkStatusColor(card.network_status).bg,
|
:class="[
|
||||||
color: getNetworkStatusColor(card.network_status).text,
|
'h-full rounded-full transition-all',
|
||||||
}"
|
(parseFloat(card.data_usage || '0') / parseFloat(card.data_total || '1')) > 0.9 ? 'bg-[#ff4d4f]' : 'bg-[#52c41a]',
|
||||||
class="px-3 py-1 rounded-full text-12px font-600"
|
]"
|
||||||
>
|
/>
|
||||||
{{ getNetworkStatusText(card.network_status, card.network_status_name) }}
|
|
||||||
</view>
|
|
||||||
</view>
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
<view class="mt-3 text-13px text-[var(--text-secondary)]">
|
<view class="mt-2 flex items-center justify-between text-11px text-[#999]">
|
||||||
{{ card.package_name || '暂无套餐' }}
|
<text>激活: {{ card.activate_time || '-' }}</text>
|
||||||
</view>
|
<text>到期: {{ card.expire_date || '-' }}</text>
|
||||||
|
|
||||||
<view class="mt-3 flex items-center justify-between text-12px text-[var(--text-tertiary)]">
|
|
||||||
<text>{{ card.iccid || '-' }}</text>
|
|
||||||
<text>{{ card.msisdn || '-' }}</text>
|
|
||||||
</view>
|
|
||||||
|
|
||||||
<view v-if="card.virtual_no" class="mt-1 text-12px text-[var(--text-tertiary)]">
|
|
||||||
虚拟号: {{ card.virtual_no }}
|
|
||||||
</view>
|
|
||||||
|
|
||||||
<view class="mt-3 bg-[var(--bg-muted)] rounded-8px p-3">
|
|
||||||
<view class="flex items-center justify-between mb-2">
|
|
||||||
<text class="text-12px text-[var(--text-secondary)]">流量</text>
|
|
||||||
<text class="text-12px font-600 text-[var(--text-primary)]">
|
|
||||||
{{ card.data_usage || '0' }} / {{ card.data_total || '0' }}
|
|
||||||
</text>
|
|
||||||
</view>
|
|
||||||
<view class="h-6px bg-[var(--border-primary)] rounded-full overflow-hidden">
|
|
||||||
<view
|
|
||||||
:style="{
|
|
||||||
width: `${(parseFloat(card.data_usage || '0') / parseFloat(card.data_total || '1')) * 100}%`,
|
|
||||||
}"
|
|
||||||
:class="[
|
|
||||||
'h-full rounded-full transition-all duration-300',
|
|
||||||
(parseFloat(card.data_usage || '0') / parseFloat(card.data_total || '1')) > 0.9
|
|
||||||
? 'bg-[#ff4d4f]'
|
|
||||||
: 'bg-[#52c41a]',
|
|
||||||
]"
|
|
||||||
/>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
|
|
||||||
<view class="mt-2 flex items-center justify-between text-11px text-[var(--text-tertiary)]">
|
|
||||||
<text>激活: {{ card.activate_time || '-' }}</text>
|
|
||||||
<text>到期: {{ card.expire_date || '-' }}</text>
|
|
||||||
</view>
|
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<view v-else class="pt-16 center">
|
<view v-if="loading" class="py-4 text-center">
|
||||||
<text class="text-14px text-[var(--text-secondary)]">{{ searchKeyword ? '未找到匹配的卡' : '暂无卡数据' }}</text>
|
<text class="text-sm text-[#64748b]">加载中...</text>
|
||||||
|
</view>
|
||||||
|
<view v-else-if="!hasMore" class="py-4 text-center">
|
||||||
|
<text class="text-sm text-[#94a3b8]">没有更多了</text>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<view class="h-16" />
|
<view v-else class="pt-16 flex flex-col items-center">
|
||||||
|
<i class="i-mdi-package-variant-closed text-6xl text-[#cbd5e1] mb-3" />
|
||||||
|
<text class="text-base text-[#64748b] mb-1">暂无数据</text>
|
||||||
|
<text class="text-sm text-[#94a3b8]">{{ searchKeyword ? '未找到相关结果' : '暂无卡数据' }}</text>
|
||||||
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<SimplePicker
|
<SimplePicker
|
||||||
@@ -396,38 +364,15 @@ function onCarrierConfirm(option: { label: string, value: any }) {
|
|||||||
title="选择运营商"
|
title="选择运营商"
|
||||||
@confirm="onCarrierConfirm"
|
@confirm="onCarrierConfirm"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<SimplePicker
|
||||||
|
v-model:show="showSearchTypePicker"
|
||||||
|
:options="searchTypeOptions"
|
||||||
|
title="选择搜索类型"
|
||||||
|
@confirm="onSearchTypeConfirm"
|
||||||
|
/>
|
||||||
</view>
|
</view>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.center {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.space-y-3 > view:not(:last-child) {
|
|
||||||
margin-bottom: 12px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.space-y-2 > view:not(:last-child) {
|
|
||||||
margin-bottom: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.break-all {
|
|
||||||
word-break: break-all;
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes spin {
|
|
||||||
from {
|
|
||||||
transform: rotate(0deg);
|
|
||||||
}
|
|
||||||
to {
|
|
||||||
transform: rotate(360deg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.animate-spin {
|
|
||||||
animation: spin 1s linear infinite;
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
Reference in New Issue
Block a user