GPU_Web/src/views/admin/home/index.vue
2026-01-15 15:31:42 +08:00

821 lines
21 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>
<div class="home-page">
<!-- 主布局左右两栏 -->
<a-row :gutter="24">
<!-- 左侧栏实例数据邀请好友 -->
<a-col :span="16">
<!-- 实例卡片 -->
<a-card title="GPU容器实例" class="card gpu-card">
<a-row :gutter="32" align="middle">
<!-- 第一栏容器实例和运行中 -->
<a-col :span="18">
<div class="stats-column">
<div class="stats-row">
<div class="stat-item">
<div class="stat-label">进行中</div>
<div class="stat-value">0</div>
</div>
<div class="stat-item">
<div class="stat-label">即将到期</div>
<div class="stat-value">0</div>
</div>
<div class="stat-item">
<div class="stat-label">自动续费</div>
<div class="stat-value">0</div>
</div>
</div>
</div>
</a-col>
<!-- 第二栏即将到期和即将释放 -->
<a-col :span="6">
<div class="stats-column">
<div class="stats-row">
<img src="../../../assets/rognqishili.png" alt="warning" class="warning-icon">
</div>
</div>
</a-col>
</a-row>
</a-card>
<!-- 邀请好友卡片 -->
<a-card title="邀请好友得算力券" class="card margin-top">
<template #extra>
<a-button type="link" @click="goToRules">活动规则</a-button>
</template>
<a-row :gutter="24" class="invite-container">
<!-- 左侧邀请信息和链接 -->
<a-col :span="24">
<div class="invite-content">
<div class="invite-status">
<!-- <div class="status-icon">
<LinkOutlined />
</div> -->
<div class="status-text">
<div class="status-title">暂无邀请链接</div>
<div class="status-desc">
通过参加邀请好友生成邀请码使用后可得等额优惠券
</div>
</div>
</div>
<div class="invite-actions">
<a-button type="primary" class="generate-btn" @click="generateInviteLink">
<template #icon>
<PlusOutlined />
</template>
生成邀请链接
</a-button>
</div>
</div>
</a-col>
</a-row>
</a-card>
</a-col>
<!-- 右侧栏用户信息 + 费用信息 -->
<a-col :span="8">
<!-- 费用信息卡片 -->
<a-card class="card ">
<div class="fee-header">
<div class="fee-title">我的账号</div>
</div>
<div class="fee-info">
<div class="fee-item">
<span class="nav-item">
<img src="../../../assets/nav.png" />
</span>
<div class="nav-account">
<p>
{{ userInfo.userName }}
<i class="fee-value">
<CopyOutlined />
</i>
</p>
<a-tag :color="getCertifyName(userInfo.certificationStatus).color">{{
getCertifyName(userInfo.certificationStatus).name }}</a-tag>
<!-- <p>{{getCertifyName(userInfo.certificationStatus).name}}</p> -->
</div>
<span class="btn-item" style="cursor: pointer;"
@click="router.push('/layout/admin/accountSet')">
账户设置
<ArrowRightOutlined />
</span>
</div>
<a-divider />
<div class="fee-title" style="padding-bottom: 15px;">资产账户</div>
<!-- 我的余额 -->
<div class="asset-item">
<!-- <div class="asset-icon-box balance-icon">
<WalletOutlined class="asset-icon" />
</div> -->
<div class="asset-content">
<div class="asset-name">我的余额</div>
<div class="asset-value" v-if="userInfo.balance >= 0">
<span class="amount">{{ formatAmount(userInfo.balance) }}</span>
<a-button type="link" class="recharge-btn" @click="goToRecharge">去充值</a-button>
</div>
<div class="asset-value" v-else>
<span class="amount">--</span>
<a-button type="link" class="recharge-btn" @click="goToRecharge">去充值</a-button>
</div>
</div>
</div>
<!-- 我的权益 -->
<div class="asset-item">
<!-- <div class="asset-icon-box coupon-icon">
<TagOutlined class="asset-icon" />
</div> -->
<div class="asset-content">
<div class="asset-name">我的权益</div>
<div class="rights-info">
<!-- 算力点 -->
<div class="rights-item">
<div class="rights-label">
<span class="rights-text">算力点</span>
</div>
<div class="rights-value">
<span class="rights-amount">{{ userInfo.computingPowerPoint }}</span>
<span class="rights-unit"></span>
</div>
</div>
<!-- 分割线 -->
<div class="rights-divider"></div>
<!-- 可用算力券 -->
<div class="rights-item">
<div class="rights-label">
<span class="rights-text">可用算力券</span>
</div>
<div class="rights-value">
<span class="rights-amount">{{ userInfo.voucherNum }}</span>
<span class="rights-unit"></span>
</div>
</div>
</div>
<!-- <a-button type="link" class="view-coupons-btn" @click="goToCoupons">
查看全部权益
<RightOutlined style="font-size: 10px; margin-left: 2px;" />
</a-button> -->
</div>
</div>
</div>
<!-- <a-divider /> -->
<!-- <div class="fee-links">
<a-button type="link" class="fee-link" @click="goToOrders">我的订单></a-button>
<a-button type="link" class="fee-link" @click="goToBills">我的账单></a-button>
<a-button type="link" class="fee-link" @click="goToCoupons">我的代金券></a-button>
<a-button type="link" class="fee-link" @click="goToInvoice">开发票></a-button>
</div> -->
</a-card>
</a-col>
</a-row>
</div>
</template>
<script setup lang="ts">
import { ref, onBeforeMount } from 'vue'
import {
UserOutlined,
GiftOutlined,
TagOutlined,
CreditCardOutlined,
ArrowRightOutlined,
CopyOutlined,
WalletOutlined,
RightOutlined,
LinkOutlined,
PlusOutlined,
QuestionCircleOutlined // 新增图标
} from '@ant-design/icons-vue'
import { useRouter } from 'vue-router'
import { message } from 'ant-design-vue'
const router = useRouter()
// 实例卡片的开关状态
const switch1 = ref(true)
const switch2 = ref(false)
const userInfo = ref<any>({})
// 资产数据
const balance = ref<number>(0) // 余额
const computingPoints = ref<number>(5000) // 算力点
const availableCoupons = ref<number>(3) // 可用算力券数量
onBeforeMount(() => {
userInfo.value = JSON.parse(localStorage.getItem('userInfo') || '{}');
// 模拟从API获取数据
fetchUserAssets()
})
// 格式化金额显示
const formatAmount = (amount: number): string => {
return amount.toFixed(2)
}
const certificationStatus = new Map<string, { name: string; color: string }>([
['PENDING_CERTIFICATION', { name: '待认证', color: '#faad14' }],
['CERTIFICATION_DFFILED', { name: '已提交', color: '#d9d9d9' }], // 注意拼写DFFILED → FAILED?
['CERTIFICATION_PASSED', { name: '认证通过', color: '#52c41a' }],
['CERTIFICATION_FAILED', { name: '认证失败', color: '#ff4d4f' }],
]);
const getCertifyName = (status: string): { name: string; color: string } => {
return certificationStatus.get(status) ?? { name: '未认证', color: 'gray' };
};
// 格式化算力点显示(添加千分位)
const formatComputingPoints = (points: number): string => {
return points.toLocaleString()
}
// 获取用户资产信息
const fetchUserAssets = () => {
// 这里应该调用API获取真实数据
// 模拟数据
computingPoints.value = 5000 // 5,000算力点
availableCoupons.value = 3 // 3张算力券
}
// 生成邀请链接
const generateInviteLink = () => {
// 这里应该是调用API生成邀请链接的逻辑
message.success('邀请链接生成成功!')
// 实际开发中应该调用API然后显示生成的链接
// const link = await api.generateInviteLink()
}
// 新增:跳转到活动规则页面
const goToRules = () => {
// 跳转到活动规则页面,假设路由为 '/invite/rules'
// 如果是在新窗口打开,可以使用 window.open('/invite/rules', '_blank')
router.push('/invite/rules')
// 或者使用外部链接
// window.open('https://your-domain.com/invite-rules', '_blank')
}
// 跳转到充值页面
const goToRecharge = () => {
router.push('/layout/admin/balance')
}
// 跳转到权益页面
const goToCoupons = () => {
router.push('/rights')
}
// 跳转到订单页面
const goToOrders = () => {
router.push('/orders')
}
// 跳转到账单页面
const goToBills = () => {
router.push('/bills')
}
// 跳转到发票页面
const goToInvoice = () => {
router.push('/invoice')
}
</script>
<style lang="scss" scoped>
.home-page {
margin: 20px;
background: #fff;
}
.gpu-card {
background: linear-gradient(180deg, rgba(250, 252, 255, 1) 0%, rgba(255, 255, 255, 1) 100%);
}
.margin-top {
margin-top: 20px;
}
/* 实例卡片样式 */
.stats-column {
display: flex;
align-items: center;
height: 100%;
position: relative;
margin-top: -10px;
}
.stats-row {
display: flex;
gap: 10px;
align-items: flex-start;
width: 100%;
img {
width: 150px;
height: 150px;
}
}
.stat-item {
text-align: center;
flex: 1;
display: flex;
flex-direction: column;
align-items: center;
}
.stat-label {
font-size: 14px;
color: #666;
margin-bottom: 8px;
display: flex;
align-items: center;
justify-content: center;
gap: 4px;
}
.stat-value {
font-size: 24px;
font-weight: 600;
color: #1890ff;
}
/* 邀请好友卡片样式 */
.invite-container {
min-height: 200px;
}
.invite-content {
display: flex;
flex-direction: column;
height: 100%;
justify-content: center;
padding-right: 24px;
// border-right: 1px solid #f0f0f0;
}
.invite-status {
display: flex;
align-items: flex-start;
margin-bottom: 24px;
}
.status-icon {
width: 48px;
height: 48px;
background: linear-gradient(135deg, #f0f7ff 0%, #e6f7ff 100%);
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
margin-right: 16px;
flex-shrink: 0;
.anticon {
font-size: 24px;
color: #1890ff;
}
}
.status-text {
width: 100%;
flex: 1;
text-align: center;
}
.status-title {
font-size: 18px;
font-weight: 600;
color: #333;
margin-bottom: 8px;
line-height: 1.4;
}
.status-desc {
font-size: 14px;
color: #666;
line-height: 1.6;
}
.invite-actions {
width: 100%;
text-align: center;
}
.generate-btn {
height: 40px;
font-size: 14px;
font-weight: 500;
border-radius: 6px;
background: linear-gradient(90deg, #1890ff, #36cfc9);
border: none;
box-shadow: 0 2px 0 rgba(5, 145, 255, 0.1);
transition: all 0.3s ease;
&:hover {
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(24, 144, 255, 0.3);
background: linear-gradient(90deg, #40a9ff, #5cdbd3);
}
&:active {
transform: translateY(0);
box-shadow: 0 2px 0 rgba(5, 145, 255, 0.1);
}
.anticon {
margin-right: 6px;
}
}
/* 新增:活动规则链接样式 */
.rules-link {
padding: 0;
height: auto;
font-size: 14px;
color: #1890ff;
&:hover {
color: #40a9ff;
}
.anticon {
margin-right: 4px;
font-size: 14px;
}
}
/* 活动规则样式 */
.rules-section {
padding-left: 24px;
height: 100%;
}
.rules-title {
display: flex;
align-items: center;
margin-bottom: 20px;
padding-bottom: 12px;
border-bottom: 1px solid #f0f0f0;
.anticon {
color: #ff4d4f;
margin-right: 8px;
font-size: 16px;
}
span {
font-size: 16px;
font-weight: 600;
color: #333;
}
}
.rules-list {
list-style: none;
padding: 0;
margin: 0;
}
.rule-item {
display: flex;
align-items: flex-start;
margin-bottom: 16px;
line-height: 1.5;
&:last-child {
margin-bottom: 0;
}
}
.rule-number {
display: inline-block;
width: 20px;
height: 20px;
background: linear-gradient(135deg, #1890ff, #36cfc9);
border-radius: 50%;
color: white;
font-size: 12px;
font-weight: 600;
text-align: center;
line-height: 20px;
margin-right: 12px;
flex-shrink: 0;
margin-top: 1px;
}
.rule-item span:last-child {
flex: 1;
font-size: 13px;
color: #666;
line-height: 1.6;
}
/* 资产账户样式 */
.fee-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 16px;
}
.fee-title {
font-size: 16px;
font-weight: 600;
}
.fee-info {
margin-bottom: 16px;
}
/* 资产项样式 */
.asset-item {
display: flex;
align-items: flex-start;
padding: 12px 0;
&:not(:last-child) {
border-bottom: 1px solid #f5f5f5;
}
}
.asset-icon-box {
width: 24px;
height: 24px;
border-radius: 6px;
display: flex;
align-items: center;
justify-content: center;
margin-right: 12px;
margin-top: 2px;
flex-shrink: 0;
&.balance-icon {
background: #1890ff;
}
&.coupon-icon {
background: #fa8c16;
}
}
.asset-icon {
font-size: 14px;
color: white;
}
.asset-content {
flex: 1;
min-width: 0;
}
.asset-name {
font-size: 14px;
color: #666;
margin-bottom: 8px;
line-height: 1;
font-weight: 500;
}
.asset-value {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 8px;
}
.amount {
font-size: 18px;
font-weight: 600;
color: #1890ff;
line-height: 1;
&:before {
content: '¥';
font-size: 14px;
margin-right: 2px;
}
}
.recharge-btn {
padding: 0;
height: auto;
font-size: 12px;
color: #1890ff;
line-height: 1;
&:hover {
color: #40a9ff;
}
}
/* 权益信息样式 */
.rights-info {
margin-top: 10px;
background: #fafafa;
border-radius: 6px;
padding: 10px;
margin-bottom: 8px;
border: 1px solid #f0f0f0;
}
.rights-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 4px 0;
}
.rights-label {
display: flex;
align-items: center;
gap: 6px;
}
.rights-icon {
font-size: 14px;
line-height: 1;
}
.rights-text {
font-size: 12px;
color: #666;
line-height: 1;
}
.rights-value {
display: flex;
align-items: baseline;
gap: 2px;
}
.rights-amount {
font-size: 16px;
font-weight: 600;
color: #1890ff;
line-height: 1;
}
.rights-unit {
font-size: 12px;
color: #999;
line-height: 1;
}
.rights-divider {
height: 1px;
background: #f0f0f0;
margin: 6px 0;
}
/* 算力点特殊样式 */
.rights-item:first-child {
.rights-amount {
color: #fa8c16;
/* 算力点用橙色 */
}
}
/* 算力券特殊样式 */
.rights-item:last-child {
.rights-amount {
color: #1890ff;
/* 算力券用蓝色 */
}
}
.view-coupons-btn {
padding: 0;
height: auto;
font-size: 12px;
color: #999;
line-height: 1;
margin-top: 4px;
&:hover {
color: #1890ff;
}
}
/* 用户信息样式 */
.fee-item {
display: flex;
align-items: center;
margin-bottom: 12px;
font-size: 14px;
.nav-item {
width: 35px;
height: 35px;
img {
width: 100%;
}
}
.nav-account {
padding-left: 10px;
margin-top: -20px;
:first-child {
font-weight: 500;
margin-top: 10px;
margin-bottom: 0;
line-height: 1.4;
.fee-value {
color: rgba(166, 166, 166, 1);
margin-left: 4px;
font-weight: 900;
padding-left: 10px;
font-size: 10px;
}
}
:nth-child(2) {
width: 60px;
height: 20px;
line-height: 20px;
text-align: center;
opacity: 1;
color: #fff;
font-weight: 600;
border-radius: 5px;
font-size: 10px;
background: rgba(28, 89, 255, 0.85);
margin-top: 5px;
}
}
}
.btn-item {
height: 20px;
font-size: 10px;
padding: 2px 5px;
margin-top: 30px;
margin-left: 10px;
border-radius: 5px;
border: 1px solid rgba(166, 166, 166, 1);
color: rgba(56, 56, 56, 1);
}
/* 链接样式 */
.fee-links {
display: flex;
flex-wrap: wrap;
gap: 5px;
}
.fee-link {
padding: 0;
height: auto;
color: #1890ff;
font-size: 12px;
&:hover {
color: #40a9ff;
}
}
/* 分割线样式 */
:deep(.ant-divider) {
margin: 16px 0;
}
/* 响应式设计 */
@media (max-width: 768px) {
.invite-container {
flex-direction: column;
}
.invite-content {
padding-right: 0;
border-right: none;
border-bottom: 1px solid #f0f0f0;
padding-bottom: 24px;
margin-bottom: 24px;
}
.rules-section {
padding-left: 0;
}
.invite-actions {
flex-direction: column;
align-items: flex-start;
gap: 12px;
}
.rules-link {
margin-left: 0;
}
}
</style>