Files
device-voice-h5/components/TrafficCard.vue
sexygoat c798cdba7f
All checks were successful
构建并部署前端到生产环境 / build-and-deploy (push) Successful in 52s
fix: 本月流量
2026-05-08 09:49:35 +08:00

189 lines
4.2 KiB
Vue
Raw Permalink 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>
<view class="card traffic-card">
<view class="card-header flex-row-sb mb-lg">
<view class="title">本月流量</view>
<view class="usage-percent">
<text class="title">{{ usedFlowPercent }}</text>
<text class="caption">%</text>
</view>
</view>
<view class="progress-section mb-lg">
<view class="progress-apple">
<view class="progress-fill" :style="{width: usedFlowPercent + '%'}"></view>
</view>
</view>
<view class="traffic-stats flex-row-sb">
<view class="stat-item flex-col-center">
<view class="caption mb-xs">已使用</view>
<view class="stat-value-container">
<text class="stat-value">{{ usedValue }}</text>
<text class="stat-unit">{{ usedUnit }}</text>
</view>
</view>
<view class="stat-item flex-col-center">
<view class="caption mb-xs">总流量</view>
<view class="stat-value-container">
<text class="stat-value">{{ totalValue }}</text>
<text class="stat-unit">{{ totalUnit }}</text>
</view>
</view>
<view class="stat-item flex-col-center">
<view class="caption mb-xs">剩余</view>
<view class="stat-value-container">
<text class="stat-value">{{ remainValue }}</text>
<text class="stat-unit">{{ remainUnit }}</text>
</view>
</view>
</view>
</view>
</template>
<script setup>
import {
ref,
computed,
watch
} from 'vue';
import {
assetApi
} from '@/api/index.js';
import {
useUserStore
} from '@/store/index.js';
const props = defineProps({
identifier: {
type: String,
default: ''
}
});
const emit = defineEmits(['packageLoaded']);
const userStore = useUserStore();
// 流量数据
const usedMB = ref(0);
const totalMB = ref(0);
const remainMB = ref(0);
const toMB = (value) => Number(value) || 0;
// 格式化流量数据MB 转 GB
const formatTraffic = (mb) => {
if (!mb || mb === 0) {
return {
value: '0.00',
unit: 'MB'
};
}
if (mb >= 1024) {
return {
value: (mb / 1024).toFixed(2),
unit: 'GB'
};
}
return {
value: mb.toFixed(2),
unit: 'MB'
};
};
// 已使用流量
const usedValue = computed(() => formatTraffic(usedMB.value).value);
const usedUnit = computed(() => formatTraffic(usedMB.value).unit);
// 总流量
const totalValue = computed(() => formatTraffic(totalMB.value).value);
const totalUnit = computed(() => formatTraffic(totalMB.value).unit);
// 剩余流量
const remainValue = computed(() => formatTraffic(remainMB.value).value);
const remainUnit = computed(() => formatTraffic(remainMB.value).unit);
// 使用百分比
const usedFlowPercent = computed(() => {
if (totalMB.value > 0) {
const percent = (usedMB.value / totalMB.value * 100);
return Math.min(percent, 100).toFixed(2);
}
return '0.00';
});
const getTrafficMetrics = (data) => {
const enableVirtualData = !!data.enable_virtual_data;
const used = enableVirtualData ? toMB(data.virtual_used_mb) : toMB(data.real_used_mb);
const total = toMB(data.real_total_mb);
const remain = Math.max(total - used, 0);
return {
used,
total,
remain
};
};
// 加载套餐流量数据
const loadPackageData = async () => {
const identifier = props.identifier || userStore.state.identifier;
if (!identifier) return;
try {
const data = await assetApi.getInfo(identifier);
if (data.current_package_usage_id) {
const metrics = getTrafficMetrics(data);
usedMB.value = metrics.used;
totalMB.value = metrics.total;
remainMB.value = metrics.remain;
emit('packageLoaded', data);
} else {
usedMB.value = 0;
totalMB.value = 0;
remainMB.value = 0;
emit('packageLoaded', null);
}
} catch (e) {
console.error('加载套餐流量数据失败', e);
}
};
// 监听 identifier 变化
watch(() => props.identifier || userStore.state.identifier, (newVal) => {
if (newVal) {
loadPackageData();
}
}, {
immediate: true
});
</script>
<style scoped lang="scss">
.traffic-card {
.usage-percent {
display: flex;
align-items: baseline;
gap: 4rpx;
.title {
font-size: 35rpx;
font-weight: 700;
margin-right: 5rpx;
}
}
.traffic-stats {
background: var(--gray-100);
border-radius: var(--radius-small);
padding: 20rpx;
margin-top: 16rpx;
.stat-item {
flex: 1;
}
}
}
</style>