feat:流量展示
All checks were successful
构建并部署前端到测试环境 / build-and-deploy (push) Successful in 4m30s

This commit is contained in:
sexygoat
2026-04-27 15:57:31 +08:00
parent a055ff166a
commit d52efaadcb
3 changed files with 299 additions and 97 deletions

View File

@@ -155,7 +155,7 @@ export function formatDateRange(startDate: string, endDate: string): string {
/** /**
* 格式化日期时间 * 格式化日期时间
* @param date 日期字符串或时间戳 * @param date 日期字符串或时间戳支持Unix时间戳秒自动检测
* @param format 格式化模板,默认 'YYYY-MM-DD HH:mm:ss' * @param format 格式化模板,默认 'YYYY-MM-DD HH:mm:ss'
* @returns 格式化后的日期字符串 * @returns 格式化后的日期字符串
*/ */
@@ -165,7 +165,14 @@ export function formatDateTime(
): string { ): string {
if (!date) return '-' if (!date) return '-'
const d = new Date(date) let d: Date
const numDate = typeof date === 'string' ? Number(date) : date
if (typeof numDate === 'number' && numDate > 1000000000 && numDate < 2000000000) {
d = new Date(numDate * 1000)
} else {
d = new Date(date)
}
if (isNaN(d.getTime())) return '-' if (isNaN(d.getTime())) return '-'
const year = d.getFullYear() const year = d.getFullYear()

View File

@@ -32,69 +32,140 @@
<template v-else-if="currentPackage"> <template v-else-if="currentPackage">
<!-- 流量使用情况 - 横向进度条卡片 --> <!-- 流量使用情况 - 横向进度条卡片 -->
<div class="flow-progress-card"> <div class="flow-progress-card">
<div class="flow-stats-row"> <!-- 管理员视角始终显示真流量和虚流量 -->
<div class="flow-stat-item"> <template v-if="isAdminOrPlatform">
<span class="flow-stat-label">总量</span> <div class="flow-stats-container">
<span class="flow-stat-value"> <!-- 真流量 -->
{{ formatDataSize(currentPackage.data_limit_mb || 0) }} <div class="flow-stat-section">
</span> <div class="flow-stat-label">真流量使用</div>
</div> <div class="flow-stat-row">
<div v-if="isAdminOrPlatform" class="flow-stat-item"> <span class="stat-item">
<span class="flow-stat-label">实际可</span> <span class="stat-label">已使</span>
<span class="flow-stat-value"> <span class="stat-value">{{ formatDataSize(currentPackage.data_usage_mb || 0) }}</span>
{{ </span>
formatDataSize( <span class="stat-item">
currentPackage.enable_virtual_data <span class="stat-label">总量</span>
? currentPackage.virtual_limit_mb || 0 <span class="stat-value">{{ formatDataSize(currentPackage.data_limit_mb || 0) }}</span>
: currentPackage.data_limit_mb || 0 </span>
) <span class="stat-item">
}} <span class="stat-label">剩余</span>
</span> <span class="stat-value">{{
</div> formatDataSize(
<div class="flow-stat-item"> (currentPackage.data_limit_mb || 0) - (currentPackage.data_usage_mb || 0)
<span class="flow-stat-label">剩余可用</span>
<span class="flow-stat-value">
{{
formatDataSize(
currentPackage.enable_virtual_data
? currentPackage.virtual_remain_mb || 0
: (currentPackage.data_limit_mb || 0) - (currentPackage.data_usage_mb || 0)
)
}}
</span>
</div>
</div>
<div class="flow-progress-bar">
<ElProgress
:percentage="
currentPackage.enable_virtual_data
? getUsageProgress(
currentPackage.virtual_data_used || 0,
currentPackage.virtual_data_total || 0
)
: getUsageProgress(
currentPackage.real_data_used || 0,
currentPackage.real_data_total || 0
)
"
:color="
currentPackage.enable_virtual_data
? getProgressColorByPercentage(
getUsageProgress(
currentPackage.virtual_data_used || 0,
currentPackage.virtual_data_total || 0
) )
}}</span>
</span>
</div>
<ElProgress
:percentage="
getUsageProgress(
currentPackage.data_usage_mb || 0,
currentPackage.data_limit_mb || 0
) )
: getProgressColorByPercentage( "
getUsageProgress( :color="getProgressColorByPercentage(
currentPackage.real_data_used || 0, getUsageProgress(
currentPackage.real_data_total || 0 currentPackage.data_usage_mb || 0,
currentPackage.data_limit_mb || 0
)
)"
:stroke-width="10"
/>
</div>
<!-- 虚流量 -->
<div class="flow-stat-section">
<div class="flow-stat-label">虚流量使用</div>
<div class="flow-stat-row">
<span class="stat-item">
<span class="stat-label">已使用</span>
<span class="stat-value">{{ formatDataSize(currentPackage.virtual_used_mb || 0) }}</span>
</span>
<span class="stat-item">
<span class="stat-label">总量</span>
<span class="stat-value">{{ formatDataSize(currentPackage.virtual_limit_mb || 0) }}</span>
</span>
<span class="stat-item">
<span class="stat-label">剩余</span>
<span class="stat-value">{{
formatDataSize(
(currentPackage.virtual_limit_mb || 0) - (currentPackage.virtual_used_mb || 0)
) )
}}</span>
</span>
</div>
<ElProgress
:percentage="
getUsageProgress(
currentPackage.virtual_used_mb || 0,
currentPackage.virtual_limit_mb || 0
) )
" "
:stroke-width="14" :color="getProgressColorByPercentage(
/> getUsageProgress(
</div> currentPackage.virtual_used_mb || 0,
currentPackage.virtual_limit_mb || 0
)
)"
:stroke-width="10"
/>
</div>
</div>
</template>
<!-- 非管理员 未启用虚流量 -->
<template v-else>
<div class="flow-stats-row">
<div class="flow-stat-item">
<span class="flow-stat-label">已使用</span>
<span class="flow-stat-value">
{{
formatDataSize(
currentPackage.enable_virtual_data
? currentPackage.virtual_used_mb || 0
: currentPackage.data_usage_mb || 0
)
}}
</span>
</div>
<div class="flow-stat-item">
<span class="flow-stat-label">总量</span>
<span class="flow-stat-value">
{{ formatDataSize(currentPackage.data_limit_mb || 0) }}
</span>
</div>
<div class="flow-stat-item">
<span class="flow-stat-label">剩余</span>
<span class="flow-stat-value">
{{
formatDataSize(
(currentPackage.data_limit_mb || 0) - (currentPackage.data_usage_mb || 0)
)
}}
</span>
</div>
</div>
<div class="flow-progress-bar" style="margin-top: 16px">
<ElProgress
:percentage="
getUsageProgress(
currentPackage.enable_virtual_data
? currentPackage.virtual_used_mb || 0
: currentPackage.data_usage_mb || 0,
currentPackage.data_limit_mb || 0
)
"
:color="getProgressColorByPercentage(
getUsageProgress(
currentPackage.enable_virtual_data
? currentPackage.virtual_used_mb || 0
: currentPackage.data_usage_mb || 0,
currentPackage.data_limit_mb || 0
)
)"
:stroke-width="14"
/>
</div>
</template>
</div> </div>
<!-- 套餐详情表格 --> <!-- 套餐详情表格 -->
@@ -111,12 +182,12 @@
<ElDescriptionsItem label="套餐类型"> <ElDescriptionsItem label="套餐类型">
{{ getPackageTypeName(currentPackage.package_type) }} {{ getPackageTypeName(currentPackage.package_type) }}
</ElDescriptionsItem> </ElDescriptionsItem>
<!--<ElDescriptionsItem--> <ElDescriptionsItem
<!-- v-if="isAdminOrPlatform && currentPackage.enable_virtual_data"--> v-if="isAdminOrPlatform && currentPackage.enable_virtual_data"
<!-- label="虚流量比例"--> label="虚流量比例"
<!--&gt;--> >
<!-- {{ currentPackage.virtual_ratio || '-' }}--> {{ currentPackage.virtual_ratio || '-' }}
<!--</ElDescriptionsItem>--> </ElDescriptionsItem>
<ElDescriptionsItem label="开始时间"> <ElDescriptionsItem label="开始时间">
{{ formatDateTime(currentPackage.start_time) || '-' }} {{ formatDateTime(currentPackage.start_time) || '-' }}
</ElDescriptionsItem> </ElDescriptionsItem>
@@ -212,6 +283,46 @@
border-radius: 8px; border-radius: 8px;
margin-bottom: 20px; margin-bottom: 20px;
.flow-stats-container {
display: flex;
gap: 40px;
.flow-stat-section {
flex: 1;
min-width: 0;
.flow-stat-label {
font-size: 13px;
color: var(--el-text-color-secondary);
margin-bottom: 8px;
font-weight: 500;
}
.flow-stat-row {
display: flex;
gap: 16px;
margin-bottom: 8px;
.stat-item {
display: flex;
align-items: baseline;
gap: 4px;
.stat-label {
font-size: 12px;
color: var(--el-text-color-secondary);
}
.stat-value {
font-size: 14px;
font-weight: 600;
color: var(--el-text-color-primary);
}
}
}
}
}
.flow-stats-row { .flow-stats-row {
display: flex; display: flex;
gap: 32px; gap: 32px;
@@ -237,6 +348,12 @@
.flow-progress-bar { .flow-progress-bar {
padding: 0 4px; padding: 0 4px;
.progress-label {
font-size: 12px;
color: var(--el-text-color-secondary);
margin-bottom: 4px;
}
} }
} }

View File

@@ -55,46 +55,117 @@
</ElTag> </ElTag>
</template> </template>
</ElTableColumn> </ElTableColumn>
<ElTableColumn label="流量" min-width="320"> <ElTableColumn label="流量" min-width="450">
<template #default="scope"> <template #default="scope">
<div class="traffic-progress"> <div class="traffic-progress">
<div class="progress-text"> <template v-if="isAdminOrPlatform">
<template v-if="scope.row.enable_virtual_data"> <div class="progress-text">
<span class="used" <span class="used"
>: {{ formatDataSize(scope.row.virtual_used_mb || 0) }}</span >使用真流量: {{ formatDataSize(scope.row.data_usage_mb || 0) }}</span
>
<span class="remaining"
>剩余: {{ formatDataSize(scope.row.virtual_remain_mb || 0) }}</span
> >
<span class="total" <span class="total"
>总量: {{ formatDataSize(scope.row.virtual_limit_mb || 0) }}</span >真流量总量: {{ formatDataSize(scope.row.data_limit_mb || 0) }}</span
>
<span class="remaining"
>真流量剩余:
{{
formatDataSize(
(scope.row.data_limit_mb || 0) - (scope.row.data_usage_mb || 0)
)
}}</span
>
</div>
<ElProgress
style="margin-top: 4px"
:percentage="
getUsageProgress(scope.row.data_usage_mb || 0, scope.row.data_limit_mb || 0)
"
:color="
getProgressColorByPercentage(
getUsageProgress(scope.row.data_usage_mb || 0, scope.row.data_limit_mb || 0)
)
"
/>
<div class="progress-text" style="margin-top: 8px">
<span class="used"
>已使用虚流量: {{ formatDataSize(scope.row.virtual_used_mb || 0) }}</span
>
<span class="total"
>虚流量总量: {{ formatDataSize(scope.row.virtual_limit_mb || 0) }}</span
>
<span class="remaining"
>虚流量剩余:
{{
formatDataSize(
(scope.row.virtual_limit_mb || 0) - (scope.row.virtual_used_mb || 0)
)
}}</span
>
</div>
<ElProgress
style="margin-top: 4px"
:percentage="
getUsageProgress(
scope.row.virtual_used_mb || 0,
scope.row.virtual_limit_mb || 0
)
"
:color="
getProgressColorByPercentage(
getUsageProgress(
scope.row.virtual_used_mb || 0,
scope.row.virtual_limit_mb || 0
)
)
"
/>
</template>
<template v-else>
<div class="progress-text">
<span class="used"
>已使用:
{{
formatDataSize(
scope.row.enable_virtual_data
? scope.row.virtual_used_mb || 0
: scope.row.data_usage_mb || 0
)
}}</span
>
<span class="total"
>总量: {{ formatDataSize(scope.row.data_limit_mb || 0) }}</span
> >
</template>
<template v-else>
<span class="used">已用: {{ formatDataSize(scope.row.data_usage_mb) }}</span>
<span class="remaining" <span class="remaining"
>剩余: >剩余:
{{ formatDataSize(scope.row.data_limit_mb - scope.row.data_usage_mb) }}</span {{
formatDataSize(
(scope.row.data_limit_mb || 0) - (scope.row.data_usage_mb || 0)
)
}}</span
> >
<span class="total">总量: {{ formatDataSize(scope.row.data_limit_mb) }}</span> </div>
</template> <ElProgress
</div> style="margin-top: 8px"
<ElProgress :percentage="
:percentage=" getUsageProgress(
scope.row.enable_virtual_data scope.row.enable_virtual_data
? getUsageProgress(scope.row.virtual_used_mb, scope.row.virtual_limit_mb) ? scope.row.virtual_used_mb || 0
: getUsageProgress(scope.row.data_usage_mb, scope.row.data_limit_mb) : scope.row.data_usage_mb || 0,
" scope.row.data_limit_mb || 0
:color=" )
scope.row.enable_virtual_data "
? getProgressColorByPercentage( :color="
getUsageProgress(scope.row.virtual_used_mb, scope.row.virtual_limit_mb) getProgressColorByPercentage(
getUsageProgress(
scope.row.enable_virtual_data
? scope.row.virtual_used_mb || 0
: scope.row.data_usage_mb || 0,
scope.row.data_limit_mb || 0
) )
: getProgressColorByPercentage( )
getUsageProgress(scope.row.data_usage_mb, scope.row.data_limit_mb) "
) />
" </template>
/>
</div> </div>
</template> </template>
</ElTableColumn> </ElTableColumn>
@@ -187,9 +258,16 @@
import { formatDateTime } from '@/utils/business/format' import { formatDateTime } from '@/utils/business/format'
import type { AssetPackageUsageRecord } from '@/types/api' import type { AssetPackageUsageRecord } from '@/types/api'
import { useAuth } from '@/composables/useAuth' import { useAuth } from '@/composables/useAuth'
import { useUserStore } from '@/store/modules/user'
import { storeToRefs } from 'pinia'
import CreateRefundDialog from '@/components/business/CreateRefundDialog.vue' import CreateRefundDialog from '@/components/business/CreateRefundDialog.vue'
const { hasAuth } = useAuth() const { hasAuth } = useAuth()
const userStore = useUserStore()
const { info: userInfo } = storeToRefs(userStore)
const isAdminOrPlatform = computed(
() => userInfo.value.user_type === 1 || userInfo.value.user_type === 2
)
// 退款申请对话框 // 退款申请对话框
const createRefundDialogVisible = ref(false) const createRefundDialogVisible = ref(false)