mirror of
https://gitee.com/ccnetcore/Yi
synced 2026-04-06 09:16:39 +08:00
feat: 个人中心新增尊享服务、模型列表区分
This commit is contained in:
@@ -0,0 +1,457 @@
|
||||
<script lang="ts" setup>
|
||||
import { Clock, Coin, TrophyBase, WarningFilled } from '@element-plus/icons-vue';
|
||||
import { getPremiumTokenPackage } from '@/api/user';
|
||||
|
||||
// 尊享服务数据
|
||||
const loading = ref(false);
|
||||
const packageData = ref<any>({
|
||||
totalQuota: 0, // 购买总额度
|
||||
usedQuota: 0, // 已使用额度
|
||||
remainingQuota: 0, // 剩余额度
|
||||
usagePercentage: 0, // 使用百分比
|
||||
packageName: 'Token包', // 套餐名称
|
||||
expireDate: '', // 过期时间
|
||||
});
|
||||
|
||||
// 计算属性
|
||||
const usagePercent = computed(() => {
|
||||
if (packageData.value.totalQuota === 0)
|
||||
return 0;
|
||||
return Math.round((packageData.value.usedQuota / packageData.value.totalQuota) * 100);
|
||||
});
|
||||
|
||||
const remainingPercent = computed(() => {
|
||||
return 100 - usagePercent.value;
|
||||
});
|
||||
|
||||
// 获取进度条颜色
|
||||
const progressColor = computed(() => {
|
||||
const percent = usagePercent.value;
|
||||
if (percent >= 90)
|
||||
return '#f56c6c'; // 红色
|
||||
if (percent >= 70)
|
||||
return '#e6a23c'; // 橙色
|
||||
return '#67c23a'; // 绿色
|
||||
});
|
||||
|
||||
// 格式化数字
|
||||
function formatNumber(num: number): string {
|
||||
return num.toLocaleString();
|
||||
}
|
||||
|
||||
// 获取尊享服务Token包数据
|
||||
async function fetchPremiumTokenPackage() {
|
||||
loading.value = true;
|
||||
try {
|
||||
const res = await getPremiumTokenPackage();
|
||||
if (res.success && res.data) {
|
||||
packageData.value = {
|
||||
totalQuota: res.data.totalQuota || 0,
|
||||
usedQuota: res.data.usedQuota || 0,
|
||||
remainingQuota: res.data.remainingQuota || 0,
|
||||
usagePercentage: res.data.usagePercentage || 0,
|
||||
packageName: res.data.packageName || 'Token包',
|
||||
expireDate: res.data.expireDate || '',
|
||||
};
|
||||
|
||||
// 计算剩余额度(如果接口没返回)
|
||||
if (!packageData.value.remainingQuota) {
|
||||
packageData.value.remainingQuota = packageData.value.totalQuota - packageData.value.usedQuota;
|
||||
}
|
||||
}
|
||||
else {
|
||||
ElMessage.warning('暂无尊享服务数据');
|
||||
}
|
||||
}
|
||||
catch (error) {
|
||||
console.error('获取尊享服务数据失败:', error);
|
||||
ElMessage.error('获取尊享服务数据失败');
|
||||
}
|
||||
finally {
|
||||
loading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
// 刷新数据
|
||||
function refreshData() {
|
||||
fetchPremiumTokenPackage();
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
fetchPremiumTokenPackage();
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="premium-service" v-loading="loading">
|
||||
<div class="header">
|
||||
<h2>
|
||||
<el-icon><TrophyBase /></el-icon>
|
||||
尊享服务
|
||||
</h2>
|
||||
<el-button
|
||||
type="primary"
|
||||
size="small"
|
||||
@click="refreshData"
|
||||
>
|
||||
刷新数据
|
||||
</el-button>
|
||||
</div>
|
||||
|
||||
<!-- 套餐信息卡片 -->
|
||||
<el-card class="package-card" shadow="hover">
|
||||
<template #header>
|
||||
<div class="card-header">
|
||||
<el-icon class="header-icon"><Coin /></el-icon>
|
||||
<span class="header-title">{{ packageData.packageName }}</span>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<div class="package-content">
|
||||
<!-- 统计数据 -->
|
||||
<div class="stats-grid">
|
||||
<div class="stat-item total">
|
||||
<div class="stat-label">购买总额度</div>
|
||||
<div class="stat-value">{{ formatNumber(packageData.totalQuota) }}</div>
|
||||
<div class="stat-unit">Tokens</div>
|
||||
</div>
|
||||
|
||||
<div class="stat-item used">
|
||||
<div class="stat-label">已用额度</div>
|
||||
<div class="stat-value">{{ formatNumber(packageData.usedQuota) }}</div>
|
||||
<div class="stat-unit">Tokens</div>
|
||||
</div>
|
||||
|
||||
<div class="stat-item remaining">
|
||||
<div class="stat-label">剩余额度</div>
|
||||
<div class="stat-value">{{ formatNumber(packageData.remainingQuota) }}</div>
|
||||
<div class="stat-unit">Tokens</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 进度条 -->
|
||||
<div class="progress-section">
|
||||
<div class="progress-header">
|
||||
<span class="progress-label">使用进度</span>
|
||||
<span class="progress-percent" :style="{ color: progressColor }">
|
||||
{{ usagePercent }}%
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<el-progress
|
||||
:percentage="usagePercent"
|
||||
:color="progressColor"
|
||||
:stroke-width="20"
|
||||
:show-text="false"
|
||||
/>
|
||||
|
||||
<div class="progress-legend">
|
||||
<div class="legend-item">
|
||||
<span class="legend-dot used-dot"></span>
|
||||
<span class="legend-text">已使用: {{ usagePercent }}%</span>
|
||||
</div>
|
||||
<div class="legend-item">
|
||||
<span class="legend-dot remaining-dot"></span>
|
||||
<span class="legend-text">剩余: {{ remainingPercent }}%</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 过期时间 -->
|
||||
<div v-if="packageData.expireDate" class="expire-info">
|
||||
<el-icon><Clock /></el-icon>
|
||||
<span>有效期至: {{ packageData.expireDate }}</span>
|
||||
</div>
|
||||
|
||||
<!-- 温馨提示 -->
|
||||
<el-alert
|
||||
class="tips-alert"
|
||||
type="info"
|
||||
:closable="false"
|
||||
show-icon
|
||||
>
|
||||
<template #title>
|
||||
<div class="tips-content">
|
||||
<p>温馨提示:</p>
|
||||
<ul>
|
||||
<li>Token额度根据不同模型消耗速率不同</li>
|
||||
<li>建议合理使用,避免额度过快消耗</li>
|
||||
<li>额度不足时请及时充值,避免影响使用</li>
|
||||
</ul>
|
||||
</div>
|
||||
</template>
|
||||
</el-alert>
|
||||
</div>
|
||||
</el-card>
|
||||
|
||||
<!-- 购买提示卡片(额度不足时显示) -->
|
||||
<el-card
|
||||
v-if="remainingPercent < 20"
|
||||
class="warning-card"
|
||||
shadow="hover"
|
||||
>
|
||||
<div class="warning-content">
|
||||
<el-icon class="warning-icon" color="#e6a23c"><WarningFilled /></el-icon>
|
||||
<div class="warning-text">
|
||||
<h3>额度即将用完</h3>
|
||||
<p>您的Token额度已使用{{ usagePercent }}%,剩余额度较少,建议及时充值。</p>
|
||||
</div>
|
||||
<el-button type="warning" @click="$router.push('/products')">
|
||||
立即充值
|
||||
</el-button>
|
||||
</div>
|
||||
</el-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.premium-service {
|
||||
padding: 20px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.header h2 {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin: 0;
|
||||
font-size: 20px;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.header .el-icon {
|
||||
margin-right: 8px;
|
||||
color: #f59e0b;
|
||||
}
|
||||
|
||||
/* 套餐卡片 */
|
||||
.package-card {
|
||||
margin-bottom: 20px;
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.card-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.header-icon {
|
||||
font-size: 20px;
|
||||
color: #f59e0b;
|
||||
}
|
||||
|
||||
.package-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 24px;
|
||||
}
|
||||
|
||||
/* 统计数据网格 */
|
||||
.stats-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.stat-item {
|
||||
padding: 20px;
|
||||
border-radius: 8px;
|
||||
text-align: center;
|
||||
transition: transform 0.2s;
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.stat-item:hover {
|
||||
transform: translateY(-4px);
|
||||
}
|
||||
|
||||
.stat-item.total {
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
}
|
||||
|
||||
.stat-item.used {
|
||||
background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
|
||||
}
|
||||
|
||||
.stat-item.remaining {
|
||||
background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%);
|
||||
}
|
||||
|
||||
.stat-label {
|
||||
font-size: 14px;
|
||||
opacity: 0.9;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.stat-value {
|
||||
font-size: 28px;
|
||||
font-weight: 700;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.stat-unit {
|
||||
font-size: 12px;
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
/* 进度条部分 */
|
||||
.progress-section {
|
||||
padding: 20px;
|
||||
background: #f7f8fa;
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
.progress-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.progress-label {
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.progress-percent {
|
||||
font-size: 20px;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.progress-legend {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
gap: 24px;
|
||||
margin-top: 12px;
|
||||
}
|
||||
|
||||
.legend-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.legend-dot {
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.used-dot {
|
||||
background: #409eff;
|
||||
}
|
||||
|
||||
.remaining-dot {
|
||||
background: #e4e7ed;
|
||||
}
|
||||
|
||||
.legend-text {
|
||||
font-size: 14px;
|
||||
color: #606266;
|
||||
}
|
||||
|
||||
/* 过期信息 */
|
||||
.expire-info {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
padding: 12px 16px;
|
||||
background: #f0f9ff;
|
||||
border-radius: 6px;
|
||||
color: #0369a1;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
/* 温馨提示 */
|
||||
.tips-alert {
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
.tips-content p {
|
||||
margin: 0 0 8px 0;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.tips-content ul {
|
||||
margin: 0;
|
||||
padding-left: 20px;
|
||||
}
|
||||
|
||||
.tips-content li {
|
||||
margin: 4px 0;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
/* 警告卡片 */
|
||||
.warning-card {
|
||||
border-radius: 12px;
|
||||
border: 2px solid #e6a23c;
|
||||
background: #fef3e9;
|
||||
}
|
||||
|
||||
.warning-content {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.warning-icon {
|
||||
font-size: 40px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.warning-text {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.warning-text h3 {
|
||||
margin: 0 0 8px 0;
|
||||
color: #e6a23c;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.warning-text p {
|
||||
margin: 0;
|
||||
color: #606266;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
/* 响应式布局 */
|
||||
@media (max-width: 768px) {
|
||||
.premium-service {
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.stats-grid {
|
||||
grid-template-columns: 1fr;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.stat-value {
|
||||
font-size: 24px;
|
||||
}
|
||||
|
||||
.progress-legend {
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.warning-content {
|
||||
flex-direction: column;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user