Files
one-pipe-system/src/views/asset-management/asset-information/components/PackageListCard.vue
sexygoat a5e76313cb
All checks were successful
构建并部署前端到测试环境 / build-and-deploy (push) Successful in 2m9s
fix: bug
2026-04-23 14:59:05 +08:00

304 lines
9.3 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<ElCard shadow="never" class="info-card package-info">
<template #header>
<div class="card-header">
<span>套餐列表</span>
<div class="card-header-right">
<ElButton type="primary" @click="handleShowRecharge">套餐充值</ElButton>
</div>
</div>
</template>
<div v-loading="loading" class="package-table-wrapper">
<!-- 绑定设备提示 -->
<ElEmpty v-if="props.boundDeviceId" :image-size="100">
<template #description>
<span>该卡已绑定设备请点击 </span>
<ElButton
type="primary"
link
style="font-size: 15px; font-weight: 600"
@click="emit('navigateToDevice', props.boundDeviceNo!)"
>{{ props.boundDeviceNo }}</ElButton
>
<span> 进行查看</span>
</template>
</ElEmpty>
<div v-else-if="packageList && packageList.length > 0" class="table-scroll-container">
<ElTable :data="packageList" class="package-table" border>
<ElTableColumn label="套餐名称" width="150">
<template #default="scope">
<ElButton
type="primary"
link
class="package-name-link"
@click="handlePackageNameClick(scope.row)"
>
{{ scope.row.package_name }}
</ElButton>
</template>
</ElTableColumn>
<ElTableColumn label="套餐价格" width="120">
<template #default="scope">
{{ formatAmount(scope.row.paid_amount || scope.row.package_price || 0) }}
</template>
</ElTableColumn>
<ElTableColumn prop="status_name" label="套餐状态" width="100">
<template #default="scope">
<ElTag :type="getPackageStatusTagType(scope.row.status)" size="small">
{{ scope.row.status_name }}
</ElTag>
</template>
</ElTableColumn>
<ElTableColumn prop="data_limit_mb" label="总流量" width="120">
<template #default="scope">
{{ formatDataSize(scope.row.data_limit_mb) }}
</template>
</ElTableColumn>
<ElTableColumn label="流量" min-width="220">
<template #default="scope">
<div class="traffic-progress">
<div class="progress-text">
<template v-if="scope.row.enable_virtual_data">
<span class="used"
>已用: {{ formatDataSize(scope.row.virtual_used_mb || 0) }}</span
>
<span class="remaining"
>剩余: {{ formatDataSize(scope.row.virtual_remain_mb || 0) }}</span
>
<span class="total"
>总量: {{ formatDataSize(scope.row.virtual_limit_mb || 0) }}</span
>
</template>
<template v-else>
<span class="used">已用: {{ formatDataSize(scope.row.data_usage_mb) }}</span>
<span class="remaining"
>剩余:
{{ formatDataSize(scope.row.data_limit_mb - scope.row.data_usage_mb) }}</span
>
<span class="total">总量: {{ formatDataSize(scope.row.data_limit_mb) }}</span>
</template>
</div>
<ElProgress
:percentage="
scope.row.enable_virtual_data
? getUsageProgress(scope.row.virtual_used_mb, scope.row.virtual_limit_mb)
: getUsageProgress(scope.row.data_usage_mb, scope.row.data_limit_mb)
"
:color="
scope.row.enable_virtual_data
? getProgressColorByPercentage(
getUsageProgress(scope.row.virtual_used_mb, scope.row.virtual_limit_mb)
)
: getProgressColorByPercentage(
getUsageProgress(scope.row.data_usage_mb, scope.row.data_limit_mb)
)
"
/>
</div>
</template>
</ElTableColumn>
<ElTableColumn prop="activated_at" label="开始时间" width="170" show-overflow-tooltip>
<template #default="scope">
{{ formatDateTime(scope.row.activated_at) }}
</template>
</ElTableColumn>
<ElTableColumn prop="expires_at" label="到期时间" width="170" show-overflow-tooltip>
<template #default="scope">
{{ formatDateTime(scope.row.expires_at) }}
</template>
</ElTableColumn>
<ElTableColumn prop="created_at" label="创建时间" width="170" show-overflow-tooltip>
<template #default="scope">
{{ formatDateTime(scope.row.created_at) }}
</template>
</ElTableColumn>
</ElTable>
<ElPagination
v-model:current-page="pagination.page"
v-model:page-size="pagination.pageSize"
:total="pagination.total"
:page-sizes="[10, 20, 50, 100]"
layout="total, sizes, prev, pager, next, jumper"
class="pagination"
@current-change="handlePageChange"
@size-change="handleSizeChange"
/>
</div>
<ElEmpty v-else-if="!props.boundDeviceId" description="暂无套餐数据" :image-size="100" />
</div>
</ElCard>
</template>
<script setup lang="ts">
import { ref, watch } from 'vue'
import {
ElCard,
ElTable,
ElTableColumn,
ElTag,
ElButton,
ElProgress,
ElEmpty,
ElPagination
} from 'element-plus'
import { ElMessage } from 'element-plus'
import { useAssetFormatters } from '../composables/useAssetFormatters'
import { formatDateTime } from '@/utils/business/format'
import type { AssetPackageUsageRecord } from '@/types/api'
import { useAuth } from '@/composables/useAuth'
const { hasAuth } = useAuth()
// Props 使用 API 类型
type PackageInfo = AssetPackageUsageRecord
interface Props {
packageList: PackageInfo[]
total?: number
page?: number
pageSize?: number
boundDeviceId?: number
boundDeviceNo?: string
loading?: boolean
}
const props = withDefaults(defineProps<Props>(), {
total: 0,
page: 1,
pageSize: 10,
boundDeviceId: undefined,
boundDeviceNo: '',
loading: false
})
// Emits
interface Emits {
(e: 'showDailyRecords', payload: { packageRow: PackageInfo }): void
(e: 'showRecharge'): void
(e: 'page-change', page: number): void
(e: 'size-change', size: number): void
(e: 'navigateToDevice', deviceNo: string): void
}
const emit = defineEmits<Emits>()
// 分页状态
const pagination = ref({
page: props.page,
pageSize: props.pageSize,
total: props.total
})
// 监听props变化更新分页
watch(
() => [props.page, props.pageSize, props.total],
([newPage, newPageSize, newTotal]) => {
pagination.value.page = newPage
pagination.value.pageSize = newPageSize
pagination.value.total = newTotal
}
)
// 使用格式化工具
const {
formatDataSize,
formatAmount,
getPackageStatusTagType,
getUsageProgress,
getProgressColorByPercentage
} = useAssetFormatters()
// 套餐名称点击
const handlePackageNameClick = (packageRow: PackageInfo) => {
if (!hasAuth('package_usage:daily_records')) {
ElMessage.warning('您没有查看流量详单的权限')
return
}
emit('showDailyRecords', { packageRow })
}
// 显示流量详单
const handleShowDailyRecords = (packageRow: PackageInfo) => {
emit('showDailyRecords', { packageRow })
}
const handleShowRecharge = () => {
emit('showRecharge')
}
// 页码变化
const handlePageChange = (page: number) => {
emit('page-change', page)
}
// 每页条数变化
const handleSizeChange = (size: number) => {
emit('size-change', size)
}
</script>
<style scoped lang="scss">
.info-card {
&.package-info {
width: 100%;
overflow: hidden; // 防止内容溢出
.card-header {
display: flex;
align-items: center;
justify-content: space-between;
width: 100%;
}
.package-table-wrapper {
.table-scroll-container {
overflow-x: auto;
width: 100%;
}
.package-table {
:deep(.el-table__header) {
th {
font-weight: 600;
color: var(--el-text-color-primary, #374151);
}
}
.package-name-link {
font-weight: 500;
}
.traffic-progress {
.progress-text {
display: flex;
gap: 12px;
align-items: center;
justify-content: space-between;
margin-bottom: 8px;
font-size: 12px;
.used {
color: var(--el-color-primary);
}
.remaining {
color: var(--el-color-success);
}
.total {
color: var(--el-text-color-secondary);
}
}
}
}
}
.pagination {
margin-top: 16px;
justify-content: flex-end;
}
}
}
</style>