This commit is contained in:
Leo_Ding 2026-01-16 17:41:28 +08:00
commit 2712949a96
4 changed files with 224 additions and 442 deletions

View File

@ -52,4 +52,10 @@ export const invoiceList = (params:any) => request.get('/v1/invoice/invoice_list
export const invoiceDetail = (id:any) => request.delete(`/v1/invoice/invoice_detail/${id}`) export const invoiceDetail = (id:any) => request.delete(`/v1/invoice/invoice_detail/${id}`)
// 获取订单列表 // 获取订单列表
export const getOrderList = (params:any) => request.get('/v1/order/order_list',{params}) export const getOrderList = (params:any) => request.get('/v1/order/order_list',{params})
// 算力点兑换界面
export const getExchangeList = (params:any) => request.get('/v1/balance/exchange_list', { params })
// 兑换算力点
export const exchangePoint = (params:any) => request.put('/v1/balance/exchange_point',params)

View File

@ -47,4 +47,5 @@ export const getTotalCost=(params:any)=>request.get('/v1/host/host_amount',{para
export const getHostInfo=(params:any)=>request.get('/v1/host/info',{params}) export const getHostInfo=(params:any)=>request.get('/v1/host/info',{params})
//兑换算力点 //兑换算力点
export const exchangePoint=(params:any)=>request.put('/v1/balance/exchange_point',params) export const exchangePoint=(params:any)=>request.put('/v1/balance/exchange_point',params)

View File

@ -8,7 +8,6 @@
<a-breadcrumb-item>算力点兑换</a-breadcrumb-item> <a-breadcrumb-item>算力点兑换</a-breadcrumb-item>
</a-breadcrumb> </a-breadcrumb>
<!-- 余额卡片 --> <!-- 余额卡片 -->
<a-card class="balance-card"> <a-card class="balance-card">
<div class="balance-info"> <div class="balance-info">
@ -23,19 +22,15 @@
</div> </div>
<div class="balance-item"> <div class="balance-item">
<div class="balance-label"> <div class="balance-label">
<span class="label-text">算力点</span> <span class="label-text">算力点</span>
</div> </div>
<div class="balance-value"> <div class="balance-value">
<span class="points">{{ userInfo.computingPowerPoint }}</span> <span class="points">{{ formatPoints(points) }}</span>
</div> </div>
</div> </div>
</div> </div>
</a-card> </a-card>
<!-- 兑换卡片 --> <!-- 兑换卡片 -->
<a-card class="exchange-card" title="兑换算力点"> <a-card class="exchange-card" title="兑换算力点">
<div class="exchange-content"> <div class="exchange-content">
@ -55,12 +50,8 @@
<!-- 自定义输入 --> <!-- 自定义输入 -->
<div class="custom-amount"> <div class="custom-amount">
<a-input-group compact> <a-input-group compact>
<a-input v-model:value="customAmount" placeholder="输入其他金额" size="large" style="width: calc(100% - 120px)" <a-input v-model:value="customAmount" placeholder="输入其他金额" size="large" @change="handleCustomAmountChange"
@change="handleCustomAmountChange" :disabled="loading" /> :disabled="loading" />
<!-- <a-button type="primary" size="large" style="width: 120px" :disabled="!isCustomAmountValid || loading"
@click="handleCustomAmountConfirm">
确认
</a-button> -->
</a-input-group> </a-input-group>
<p class="input-name">用户自定义输入算力点数量</p> <p class="input-name">用户自定义输入算力点数量</p>
<div v-if="customAmountError" class="error-text"> <div v-if="customAmountError" class="error-text">
@ -68,60 +59,7 @@
{{ customAmountError }} {{ customAmountError }}
</div> </div>
</div> </div>
</div> <div class="title">兑换说明算力点换算比例为人民币1 = 1 算力点算力点兑换后不可退</div>
<!-- 兑换规则说明 -->
<div class="exchange-section">
<h3 class="section-title">兑换规则</h3>
<div class="exchange-rules">
<div class="rule-item">
<CheckCircleOutlined class="rule-icon" />
<span>算力点兑换比例为人民币 <strong>1 = 1算力点</strong></span>
</div>
<div class="rule-item">
<ExclamationCircleOutlined class="rule-icon" />
<span><strong>算力点不可退</strong>兑换前请确认需求</span>
</div>
<div class="rule-item">
<InfoCircleOutlined class="rule-icon" />
<span>兑换后算力点立即生效可用于平台所有计算服务</span>
</div>
<div class="rule-item">
<SafetyOutlined class="rule-icon" />
<span>兑换过程安全加密保障您的资金安全</span>
</div>
</div>
</div>
<!-- 兑换信息展示 -->
<div v-if="selectedExchangeAmount > 0" class="exchange-preview">
<div class="preview-card">
<div class="preview-header">
<div class="preview-title">兑换明细</div>
<a-button type="text" @click="clearSelection">
<CloseOutlined />
</a-button>
</div>
<div class="preview-content">
<div class="preview-item">
<span class="preview-label">兑换金额</span>
<span class="preview-value">¥{{ selectedExchangeAmount }}</span>
</div>
<div class="preview-item">
<span class="preview-label">获得算力点</span>
<span class="preview-value points-value">{{ selectedExchangeAmount }} 算力点</span>
</div>
<div class="preview-item">
<span class="preview-label">兑换后余额</span>
<span class="preview-value">¥{{ formatCurrency(userInfo.balance - selectedExchangeAmount) }}</span>
</div>
<div class="preview-item">
<span class="preview-label">兑换后算力点</span>
<span class="preview-value points-value">{{ formatPoints(userInfo.computingPowerPoint + selectedExchangeAmount) }}
算力点</span>
</div>
</div>
</div>
</div> </div>
<!-- 用户协议确认 --> <!-- 用户协议确认 -->
@ -137,16 +75,9 @@
<!-- 操作按钮 --> <!-- 操作按钮 -->
<div class="action-buttons"> <div class="action-buttons">
<a-button type="primary" size="large" :loading="loading" :disabled="!canExchange" @click="handleExchange" <a-button type="primary" size="large" @click="handleExchange" :loading="loading" :disabled="!canExchange" class="exchange-button">
class="exchange-button">
<template #icon>
<ThunderboltOutlined />
</template>
确认兑换 确认兑换
</a-button> </a-button>
<a-button size="large" @click="handleCancel" :disabled="loading" class="cancel-button">
取消
</a-button>
</div> </div>
</div> </div>
</a-card> </a-card>
@ -154,8 +85,11 @@
<!-- 主要内容区域 --> <!-- 主要内容区域 -->
<div class="main-content"> <div class="main-content">
<a-card class="history-card" title="算力点兑换历史"> <a-card class="history-card" title="算力点兑换历史">
<a-table :columns="historyColumns" bordered :data-source="exchangeHistory" row-key="key" :pagination="false"> <a-table :columns="historyColumns"
<!-- 自定义状态列的渲染 --> :data-source="listData"
row-key="key"
:pagination="paginationState"
@change="onTableChange">
<template #bodyCell="{ column, record }"> <template #bodyCell="{ column, record }">
<template v-if="column.key === 'status'"> <template v-if="column.key === 'status'">
<span :style="{ color: getStatusColor(record.status) }">{{ getStatusText(record.status) }}</span> <span :style="{ color: getStatusColor(record.status) }">{{ getStatusText(record.status) }}</span>
@ -169,12 +103,6 @@
</template> </template>
</a-table> </a-table>
</a-card> </a-card>
</div> </div>
<!-- 用户协议模态框 --> <!-- 用户协议模态框 -->
@ -185,7 +113,7 @@
<h4>兑换规则</h4> <h4>兑换规则</h4>
<p>1. 算力点兑换比例为1元人民币兑换1算力点</p> <p>1. 算力点兑换比例为1元人民币兑换1算力点</p>
<p>2. 小兑换金额为1元大单次兑换金额为10000元</p> <p>2. 大单次兑换金额为10000元</p>
<p>3. 兑换操作一经确认不可撤销或退款</p> <p>3. 兑换操作一经确认不可撤销或退款</p>
<h4>使用规则</h4> <h4>使用规则</h4>
@ -210,24 +138,21 @@
</div> </div>
</template> </template>
<script setup> <script setup lang="ts">
import { ref, computed, onMounted } from 'vue' import { ref, computed, onMounted } from 'vue'
import { import {
DollarCircleOutlined, ExclamationCircleOutlined
ThunderboltOutlined,
InfoCircleOutlined,
CheckCircleOutlined,
ExclamationCircleOutlined,
SafetyOutlined,
CloseOutlined
} from '@ant-design/icons-vue' } from '@ant-design/icons-vue'
import { message, Modal, Divider } from 'ant-design-vue' import { message, Modal } from 'ant-design-vue'
import {exchangePoint} from '@/apis/home' import { getExchangeList, exchangePoint } from '@/apis/admin'
import { usePagination } from '@/hooks'
const { listData, paginationState, resetPagination, searchFormData } = usePagination()
// //
const balance = ref(3568.5) const balance = ref(0)
const points = ref(1250) const points = ref(0)
const userInfo=ref({})
// //
const amountOptions = ref([ const amountOptions = ref([
{ value: 100, label: '100' }, { value: 100, label: '100' },
@ -236,31 +161,25 @@ const amountOptions = ref([
{ value: 5000, label: '5000' } { value: 5000, label: '5000' }
]) ])
// columns customRender //
const historyColumns = [ const historyColumns = [
{ {
title: '兑换金额', title: '兑换金额',
dataIndex: 'amount', dataIndex: 'exchange_amount',
key: 'amount', key: 'exchange_amount',
align: 'right' align: 'center'
}, },
{ {
title: '获得算力点', title: '获得算力点',
dataIndex: 'points', dataIndex: 'exchange_point',
key: 'points', key: 'exchange_point',
align: 'right' align: 'center'
}, },
{ {
title: '兑换时间', title: '兑换时间',
dataIndex: 'time', dataIndex: 'created_at',
key: 'time' key: 'created_at'
}, },
{
title: '状态',
dataIndex: 'status',
key: 'status',
align: 'center'
}
] ]
// //
@ -279,15 +198,6 @@ const loading = ref(false)
const agreementModalVisible = ref(false) const agreementModalVisible = ref(false)
const modalAgreementChecked = ref(false) const modalAgreementChecked = ref(false)
//
const exchangeHistory = ref([
{ key: 1, amount: 1000, points: 1000, time: '2024-03-15 14:30:22', status: 'success' },
{ key: 2, amount: 500, points: 500, time: '2024-03-10 09:15:45', status: 'success' },
{ key: 3, amount: 2000, points: 2000, time: '2024-03-05 16:20:33', status: 'success' },
{ key: 4, amount: 100, points: 100, time: '2024-02-28 11:45:12', status: 'success' },
{ key: 5, amount: 5000, points: 5000, time: '2024-02-20 13:05:27', status: 'success' }
])
// //
const selectedExchangeAmount = computed(() => { const selectedExchangeAmount = computed(() => {
if (selectedAmount.value > 0) { if (selectedAmount.value > 0) {
@ -311,10 +221,7 @@ const isCustomAmountValid = computed(() => {
if (isNaN(amount) || amount <= 0) { if (isNaN(amount) || amount <= 0) {
return false return false
} }
if (amount < 100) {
customAmountError.value = '最小兑换金额为100元'
return false
}
if (amount > 10000) { if (amount > 10000) {
customAmountError.value = '单次最大兑换金额为10000元' customAmountError.value = '单次最大兑换金额为10000元'
return false return false
@ -328,17 +235,20 @@ const isCustomAmountValid = computed(() => {
}) })
// //
const formatCurrency = (value) => { const formatCurrency = (value: any): string => {
return value.toFixed(2).replace(/\B(?=(\d{3})+(?!\d))/g, ',') const num = typeof value === 'number' ? value : parseFloat(value);
if (isNaN(num)) return '0.00';
return num.toFixed(2).replace(/\B(?=(\d{3})+(?!\d))/g, ',');
} }
// const formatPoints = (value: any): string => {
const formatPoints = (value) => { const num = typeof value === 'number' ? value : parseFloat(value);
return value.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ',') if (isNaN(num)) return '0';
return Math.floor(num).toString().replace(/\B(?=(\d{3})+(?!\d))/g, ',');
} }
// //
const selectAmount = (amount) => { const selectAmount = (amount: number) => {
selectedAmount.value = amount selectedAmount.value = amount
customAmount.value = '' customAmount.value = ''
customAmountError.value = '' customAmountError.value = ''
@ -350,9 +260,7 @@ const handleCustomAmountChange = () => {
if (customAmount.value) { if (customAmount.value) {
const amount = parseFloat(customAmount.value) const amount = parseFloat(customAmount.value)
if (!isNaN(amount)) { if (!isNaN(amount)) {
if (amount < 1) { if (amount > 10000) {
customAmountError.value = '最小兑换金额为1元'
} else if (amount > 10000) {
customAmountError.value = '单次最大兑换金额为10000元' customAmountError.value = '单次最大兑换金额为10000元'
} else if (amount > userInfo.value.balance) { } else if (amount > userInfo.value.balance) {
customAmountError.value = '兑换金额不能超过余额' customAmountError.value = '兑换金额不能超过余额'
@ -365,21 +273,6 @@ const handleCustomAmountChange = () => {
} }
} }
//
const handleCustomAmountConfirm = () => {
if (isCustomAmountValid.value) {
selectedAmount.value = 0
message.success(`已选择兑换¥${customAmount.value}`)
}
}
//
const clearSelection = () => {
selectedAmount.value = 0
customAmount.value = ''
customAmountError.value = ''
}
// //
const showAgreementModal = () => { const showAgreementModal = () => {
agreementModalVisible.value = true agreementModalVisible.value = true
@ -394,7 +287,7 @@ const handleAgreementConfirm = () => {
} }
// //
const handleExchange = () => { const handleExchange = async () => {
showAgreementError.value = false showAgreementError.value = false
if (!agreementChecked.value) { if (!agreementChecked.value) {
showAgreementError.value = true showAgreementError.value = true
@ -410,37 +303,63 @@ const handleExchange = () => {
content: `您确定要兑换 ¥${selectedExchangeAmount.value} 获得 ${selectedExchangeAmount.value} 算力点吗?此操作不可撤销。`, content: `您确定要兑换 ¥${selectedExchangeAmount.value} 获得 ${selectedExchangeAmount.value} 算力点吗?此操作不可撤销。`,
okText: '确认兑换', okText: '确认兑换',
cancelText: '取消', cancelText: '取消',
onOk: performExchange async onOk() {
await performExchange()
}
}) })
} }
// //
const performExchange = async() => { const performExchange = async () => {
loading.value = true loading.value = true
try { try {
const params={ //
exchange_value:selectedExchangeAmount.value const response:any = await exchangePoint({
exchange_value: selectedExchangeAmount.value
})
console.log('兑换结果:', response)
if (response.code === 1) {
//
const userInfo = JSON.parse(localStorage.getItem('userInfo') || '{}');
userInfo.balance -= selectedExchangeAmount.value;
userInfo.computingPowerPoint += selectedExchangeAmount.value;
localStorage.setItem('userInfo', JSON.stringify(userInfo));
//
balance.value = userInfo.balance;
points.value = userInfo.computingPowerPoint;
//
await getPageList();
//
clearSelection();
agreementChecked.value = false;
message.success(`兑换成功`);
} else {
message.error(response.msg || '兑换失败');
} }
const res=await exchangePoint(params) } catch (error: any) {
console.log(res) console.error('兑换失败:', error);
message.success(`兑换成功!获得 ${selectedExchangeAmount.value} 算力点`) message.error(error.msg || '兑换失败,请稍后重试');
loading.value=false } finally {
} catch (error) { loading.value = false;
loading.value=false
} }
} }
// //
const handleCancel = () => { const clearSelection = () => {
clearSelection() selectedAmount.value = 0
agreementChecked.value = false customAmount.value = ''
showAgreementError.value = false customAmountError.value = ''
} }
// //
const getStatusColor = (status) => { const getStatusColor = (status: string) => {
const colors = { const colors: Record<string, string> = {
success: 'green', success: 'green',
pending: 'orange', pending: 'orange',
failed: 'red' failed: 'red'
@ -449,8 +368,8 @@ const getStatusColor = (status) => {
} }
// //
const getStatusText = (status) => { const getStatusText = (status: string) => {
const texts = { const texts: Record<string, string> = {
success: '成功', success: '成功',
pending: '处理中', pending: '处理中',
failed: '失败' failed: '失败'
@ -458,16 +377,50 @@ const getStatusText = (status) => {
return texts[status] || status return texts[status] || status
} }
onMounted(() => { //
const userInfoStr=localStorage.getItem("userInfo") const getPageList = async () => {
if(userInfoStr){ try {
userInfo.value = JSON.parse(userInfoStr); const { pageSize, current } = paginationState
const res: any = await getExchangeList({
page_size: pageSize,
page_num: current,
})
if (res.data && Array.isArray(res.data)) {
listData.value = res.data
paginationState.total = res.total || res.data.length
} else {
listData.value = []
paginationState.total = 0
}
} catch (error: any) {
console.error('兑换历史请求失败:', error)
listData.value = []
paginationState.total = 0
} }
}
//
function onTableChange({ current, pageSize }: { current: number, pageSize: number }) {
paginationState.current = current
paginationState.pageSize = pageSize
getPageList()
}
//
onMounted(() => {
const userInfo = JSON.parse(localStorage.getItem('userInfo') || '{}');
if (userInfo.balance && userInfo.computingPowerPoint) {
balance.value = userInfo.balance;
points.value = userInfo.computingPowerPoint;
}
getPageList();
}) })
</script> </script>
<style scoped> <style scoped>
/* 你的样式保持不变 */ /* 样式保持不变,同上 */
.points-exchange-page { .points-exchange-page {
padding: 20px; padding: 20px;
/* min-height: 100vh; */ /* min-height: 100vh; */
@ -477,25 +430,6 @@ onMounted(() => {
margin-bottom: 16px; margin-bottom: 16px;
} }
.page-header {
margin-bottom: 24px;
}
.page-title {
font-size: 24px;
font-weight: 600;
color: rgba(0, 0, 0, 0.85);
margin-bottom: 8px;
}
.page-description {
color: rgba(0, 0, 0, 0.45);
font-size: 14px;
margin: 0;
}
.main-content {}
.balance-card { .balance-card {
margin-bottom: 20px; margin-bottom: 20px;
border-radius: 12px; border-radius: 12px;
@ -518,19 +452,6 @@ onMounted(() => {
margin-bottom: 8px; margin-bottom: 8px;
} }
.icon-wrapper {
display: flex;
align-items: center;
justify-content: center;
width: 32px;
height: 32px;
background: #f0f5ff;
border-radius: 8px;
margin-right: 12px;
color: #1890ff;
font-size: 18px;
}
.label-text { .label-text {
font-size: 20px; font-size: 20px;
font-weight: 500; font-weight: 500;
@ -553,24 +474,6 @@ onMounted(() => {
color: rgba(56, 56, 56, 1); color: rgba(56, 56, 56, 1);
} }
.balance-value .points-unit {
font-size: 16px;
color: rgba(0, 0, 0, 0.45);
margin-left: 8px;
}
.balance-tips {
padding: 12px 16px;
background: #f6ffed;
border-radius: 8px;
color: #52c41a;
font-size: 14px;
}
.balance-tips :deep(.anticon) {
margin-right: 8px;
}
.exchange-card { .exchange-card {
margin-bottom: 20px; margin-bottom: 20px;
border-radius: 12px; border-radius: 12px;
@ -593,12 +496,6 @@ onMounted(() => {
width: 120px; width: 120px;
} }
.section-subtitle {
color: rgba(0, 0, 0, 0.45);
font-size: 14px;
margin-bottom: 20px;
}
.amount-options { .amount-options {
display: grid; display: grid;
grid-template-columns: repeat(8, 1fr); grid-template-columns: repeat(8, 1fr);
@ -614,7 +511,6 @@ onMounted(() => {
.amount-option { .amount-option {
height: auto !important; height: auto !important;
/* padding: 6px !important; */
border-radius: 8px !important; border-radius: 8px !important;
border: 2px solid #f0f0f0 !important; border: 2px solid #f0f0f0 !important;
background: #fff !important; background: #fff !important;
@ -643,30 +539,32 @@ onMounted(() => {
margin-bottom: 4px; margin-bottom: 4px;
} }
.amount-points { .custom-amount {
font-size: 12px; width: 345px;
color: rgba(0, 0, 0, 0.45); margin-bottom: 24px;
margin-left: 180px;
} }
.custom-amount { .custom-amount input {
width: 460px; height: 45px;
margin-bottom: 24px; }
margin-left: 170px;
input { .input-name {
height: 45px; width: 350px;
} text-align: center;
font-size: 14px;
.input-name { font-weight: 400;
width: 350px; color: rgba(166, 166, 166, 1);
text-align: center; height: 35px;
font-size: 14px; line-height: 35px;
font-weight: 400; }
color: rgba(166, 166, 166, 1);
height: 35px;
line-height: 35px;
}
.title {
font-size: 14px;
font-weight: 400;
letter-spacing: 0px;
line-height: 32.93px;
color: rgba(166, 166, 166, 1);
} }
.error-text { .error-text {
@ -678,99 +576,6 @@ onMounted(() => {
gap: 4px; gap: 4px;
} }
.exchange-rules {
background: #fafafa;
padding: 20px;
border-radius: 8px;
}
.rule-item {
display: flex;
align-items: flex-start;
margin-bottom: 12px;
}
.rule-item:last-child {
margin-bottom: 0;
}
.rule-icon {
margin-right: 12px;
margin-top: 2px;
font-size: 16px;
}
.rule-item:nth-child(1) .rule-icon {
color: #52c41a;
}
.rule-item:nth-child(2) .rule-icon {
color: #faad14;
}
.rule-item:nth-child(3) .rule-icon {
color: #1890ff;
}
.rule-item:nth-child(4) .rule-icon {
color: #722ed1;
}
.exchange-preview {
margin: 24px 0;
}
.preview-card {
background: #f6ffed;
border: 1px solid #b7eb8f;
border-radius: 8px;
padding: 20px;
}
.preview-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 16px;
}
.preview-title {
font-size: 16px;
font-weight: 600;
color: rgba(0, 0, 0, 0.85);
}
.preview-content {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 10px 60px;
}
@media (max-width: 576px) {
.preview-content {
grid-template-columns: 1fr;
}
}
.preview-item {
display: flex;
justify-content: space-between;
align-items: center;
}
.preview-label {
color: rgba(0, 0, 0, 0.45);
}
.preview-value {
font-weight: 500;
color: rgba(0, 0, 0, 0.85);
}
.preview-value.points-value {
color: #722ed1;
}
.agreement-section { .agreement-section {
margin: 24px 0; margin: 24px 0;
padding: 16px; padding: 16px;
@ -789,18 +594,12 @@ onMounted(() => {
.action-buttons { .action-buttons {
display: flex; display: flex;
justify-content: flex-end;
gap: 16px; gap: 16px;
margin-top: 32px; margin-top: 32px;
} }
.exchange-button { .exchange-button {
flex: 1;
height: 48px;
font-size: 16px;
font-weight: 500;
}
.cancel-button {
width: 120px; width: 120px;
height: 48px; height: 48px;
} }
@ -810,23 +609,6 @@ onMounted(() => {
/* box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06); */ /* box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06); */
} }
.exchange-amount {
color: #1890ff;
font-weight: 500;
}
.exchange-points {
color: #722ed1;
font-weight: 500;
}
.view-all-link {
text-align: center;
padding-top: 16px;
border-top: 1px solid #f0f0f0;
margin-top: 16px;
}
.agreement-content { .agreement-content {
max-height: 60vh; max-height: 60vh;
overflow-y: auto; overflow-y: auto;

View File

@ -5,7 +5,7 @@
<!-- 左侧栏实例数据邀请好友 --> <!-- 左侧栏实例数据邀请好友 -->
<a-col :span="16"> <a-col :span="16">
<!-- 实例卡片 --> <!-- 实例卡片 -->
<a-card title="GPU容器实例" class="card"> <a-card title="GPU容器实例" class="card gpu-card">
<a-row :gutter="32" align="middle"> <a-row :gutter="32" align="middle">
<!-- 第一栏容器实例和运行中 --> <!-- 第一栏容器实例和运行中 -->
<a-col :span="18"> <a-col :span="18">
@ -44,14 +44,17 @@
<!-- 邀请好友卡片 --> <!-- 邀请好友卡片 -->
<a-card title="邀请好友得算力券" class="card margin-top"> <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-row :gutter="24" class="invite-container">
<!-- 左侧邀请信息和链接 --> <!-- 左侧邀请信息和链接 -->
<a-col :span="24"> <a-col :span="24">
<div class="invite-content"> <div class="invite-content">
<div class="invite-status"> <div class="invite-status">
<div class="status-icon"> <!-- <div class="status-icon">
<LinkOutlined /> <LinkOutlined />
</div> </div> -->
<div class="status-text"> <div class="status-text">
<div class="status-title">暂无邀请链接</div> <div class="status-title">暂无邀请链接</div>
<div class="status-desc"> <div class="status-desc">
@ -59,35 +62,17 @@
</div> </div>
</div> </div>
</div> </div>
<div class="invite-actions"> <div class="invite-actions">
<a-button <a-button type="primary" class="generate-btn" @click="generateInviteLink">
type="primary"
class="generate-btn"
@click="generateInviteLink"
>
<template #icon> <template #icon>
<PlusOutlined /> <PlusOutlined />
</template> </template>
生成邀请链接 生成邀请链接
</a-button> </a-button>
<!-- 新增活动规则链接 -->
<a-button
type="link"
class="rules-link"
@click="goToRules"
>
<template #icon>
<QuestionCircleOutlined />
</template>
查看活动规则
</a-button>
</div> </div>
</div> </div>
</a-col> </a-col>
</a-row> </a-row>
</a-card> </a-card>
</a-col> </a-col>
@ -95,7 +80,7 @@
<!-- 右侧栏用户信息 + 费用信息 --> <!-- 右侧栏用户信息 + 费用信息 -->
<a-col :span="8"> <a-col :span="8">
<!-- 费用信息卡片 --> <!-- 费用信息卡片 -->
<a-card class="card "> <a-card class="card ">
<div class="fee-header"> <div class="fee-header">
<div class="fee-title">我的账号</div> <div class="fee-title">我的账号</div>
</div> </div>
@ -105,29 +90,31 @@
<img src="../../../assets/nav.png" /> <img src="../../../assets/nav.png" />
</span> </span>
<div class="nav-account"> <div class="nav-account">
<p> <p>
{{ userInfo.userName }} {{ userInfo.userName }}
<i class="fee-value"> <i class="fee-value">
<CopyOutlined /> <CopyOutlined />
</i> </i>
</p> </p>
<a-tag :color="getCertifyName(userInfo.certificationStatus).color">{{ getCertifyName(userInfo.certificationStatus).name }}</a-tag> <a-tag :color="getCertifyName(userInfo.certificationStatus).color">{{
getCertifyName(userInfo.certificationStatus).name }}</a-tag>
<!-- <p>{{getCertifyName(userInfo.certificationStatus).name}}</p> --> <!-- <p>{{getCertifyName(userInfo.certificationStatus).name}}</p> -->
</div> </div>
<span class="btn-item" style="cursor: pointer;" @click="router.push('/layout/admin/accountSet')"> <span class="btn-item" style="cursor: pointer;"
@click="router.push('/layout/admin/accountSet')">
账户设置 账户设置
<ArrowRightOutlined /> <ArrowRightOutlined />
</span> </span>
</div> </div>
<a-divider /> <a-divider />
<div class="fee-title" style="padding-bottom: 15px;">资产账户</div> <div class="fee-title" style="padding-bottom: 15px;">资产账户</div>
<!-- 我的余额 --> <!-- 我的余额 -->
<div class="asset-item"> <div class="asset-item">
<div class="asset-icon-box balance-icon"> <!-- <div class="asset-icon-box balance-icon">
<WalletOutlined class="asset-icon" /> <WalletOutlined class="asset-icon" />
</div> </div> -->
<div class="asset-content"> <div class="asset-content">
<div class="asset-name">我的余额</div> <div class="asset-name">我的余额</div>
<div class="asset-value" v-if="userInfo.balance >= 0"> <div class="asset-value" v-if="userInfo.balance >= 0">
@ -140,12 +127,12 @@
</div> </div>
</div> </div>
</div> </div>
<!-- 我的权益 --> <!-- 我的权益 -->
<div class="asset-item"> <div class="asset-item">
<div class="asset-icon-box coupon-icon"> <!-- <div class="asset-icon-box coupon-icon">
<TagOutlined class="asset-icon" /> <TagOutlined class="asset-icon" />
</div> </div> -->
<div class="asset-content"> <div class="asset-content">
<div class="asset-name">我的权益</div> <div class="asset-name">我的权益</div>
<div class="rights-info"> <div class="rights-info">
@ -155,14 +142,14 @@
<span class="rights-text">算力点</span> <span class="rights-text">算力点</span>
</div> </div>
<div class="rights-value"> <div class="rights-value">
<span class="rights-amount">{{ userInfo.computingPowerPoint}}</span> <span class="rights-amount">{{ userInfo.computingPowerPoint }}</span>
<span class="rights-unit"></span> <span class="rights-unit"></span>
</div> </div>
</div> </div>
<!-- 分割线 --> <!-- 分割线 -->
<div class="rights-divider"></div> <div class="rights-divider"></div>
<!-- 可用算力券 --> <!-- 可用算力券 -->
<div class="rights-item"> <div class="rights-item">
<div class="rights-label"> <div class="rights-label">
@ -180,7 +167,7 @@
</a-button> --> </a-button> -->
</div> </div>
</div> </div>
</div> </div>
<!-- <a-divider /> --> <!-- <a-divider /> -->
<!-- <div class="fee-links"> <!-- <div class="fee-links">
@ -234,14 +221,14 @@ const formatAmount = (amount: number): string => {
return amount.toFixed(2) return amount.toFixed(2)
} }
const certificationStatus = new Map<string, { name: string; color: string }>([ const certificationStatus = new Map<string, { name: string; color: string }>([
['PENDING_CERTIFICATION', { name: '待认证', color: '#faad14' }], ['PENDING_CERTIFICATION', { name: '待认证', color: '#faad14' }],
['CERTIFICATION_DFFILED', { name: '已提交', color: '#d9d9d9' }], // DFFILED FAILED? ['CERTIFICATION_DFFILED', { name: '已提交', color: '#d9d9d9' }], // DFFILED FAILED?
['CERTIFICATION_PASSED', { name: '认证通过', color: '#52c41a' }], ['CERTIFICATION_PASSED', { name: '认证通过', color: '#52c41a' }],
['CERTIFICATION_FAILED', { name: '认证失败', color: '#ff4d4f' }], ['CERTIFICATION_FAILED', { name: '认证失败', color: '#ff4d4f' }],
]); ]);
const getCertifyName = (status: string): { name: string; color: string } => { const getCertifyName = (status: string): { name: string; color: string } => {
return certificationStatus.get(status) ?? { name: '未认证', color: 'gray' }; return certificationStatus.get(status) ?? { name: '未认证', color: 'gray' };
}; };
// //
const formatComputingPoints = (points: number): string => { const formatComputingPoints = (points: number): string => {
@ -269,7 +256,7 @@ const goToRules = () => {
// '/invite/rules' // '/invite/rules'
// 使 window.open('/invite/rules', '_blank') // 使 window.open('/invite/rules', '_blank')
router.push('/invite/rules') router.push('/invite/rules')
// 使 // 使
// window.open('https://your-domain.com/invite-rules', '_blank') // window.open('https://your-domain.com/invite-rules', '_blank')
} }
@ -306,12 +293,15 @@ const goToInvoice = () => {
background: #fff; background: #fff;
} }
.gpu-card {
background: linear-gradient(180deg, rgba(250, 252, 255, 1) 0%, rgba(255, 255, 255, 1) 100%);
}
.margin-top { .margin-top {
margin-top: 20px; margin-top: 20px;
} }
/* 实例卡片样式 */ /* 实例卡片样式 */
.stats-column { .stats-column {
display: flex; display: flex;
@ -387,7 +377,7 @@ const goToInvoice = () => {
justify-content: center; justify-content: center;
margin-right: 16px; margin-right: 16px;
flex-shrink: 0; flex-shrink: 0;
.anticon { .anticon {
font-size: 24px; font-size: 24px;
color: #1890ff; color: #1890ff;
@ -395,7 +385,9 @@ const goToInvoice = () => {
} }
.status-text { .status-text {
width: 100%;
flex: 1; flex: 1;
text-align: center;
} }
.status-title { .status-title {
@ -413,10 +405,8 @@ const goToInvoice = () => {
} }
.invite-actions { .invite-actions {
display: flex; width: 100%;
align-items: center; text-align: center;
gap: 16px;
margin-top: 8px;
} }
.generate-btn { .generate-btn {
@ -428,18 +418,18 @@ const goToInvoice = () => {
border: none; border: none;
box-shadow: 0 2px 0 rgba(5, 145, 255, 0.1); box-shadow: 0 2px 0 rgba(5, 145, 255, 0.1);
transition: all 0.3s ease; transition: all 0.3s ease;
&:hover { &:hover {
transform: translateY(-2px); transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(24, 144, 255, 0.3); box-shadow: 0 4px 12px rgba(24, 144, 255, 0.3);
background: linear-gradient(90deg, #40a9ff, #5cdbd3); background: linear-gradient(90deg, #40a9ff, #5cdbd3);
} }
&:active { &:active {
transform: translateY(0); transform: translateY(0);
box-shadow: 0 2px 0 rgba(5, 145, 255, 0.1); box-shadow: 0 2px 0 rgba(5, 145, 255, 0.1);
} }
.anticon { .anticon {
margin-right: 6px; margin-right: 6px;
} }
@ -451,11 +441,11 @@ const goToInvoice = () => {
height: auto; height: auto;
font-size: 14px; font-size: 14px;
color: #1890ff; color: #1890ff;
&:hover { &:hover {
color: #40a9ff; color: #40a9ff;
} }
.anticon { .anticon {
margin-right: 4px; margin-right: 4px;
font-size: 14px; font-size: 14px;
@ -474,13 +464,13 @@ const goToInvoice = () => {
margin-bottom: 20px; margin-bottom: 20px;
padding-bottom: 12px; padding-bottom: 12px;
border-bottom: 1px solid #f0f0f0; border-bottom: 1px solid #f0f0f0;
.anticon { .anticon {
color: #ff4d4f; color: #ff4d4f;
margin-right: 8px; margin-right: 8px;
font-size: 16px; font-size: 16px;
} }
span { span {
font-size: 16px; font-size: 16px;
font-weight: 600; font-weight: 600;
@ -499,7 +489,7 @@ const goToInvoice = () => {
align-items: flex-start; align-items: flex-start;
margin-bottom: 16px; margin-bottom: 16px;
line-height: 1.5; line-height: 1.5;
&:last-child { &:last-child {
margin-bottom: 0; margin-bottom: 0;
} }
@ -550,7 +540,7 @@ const goToInvoice = () => {
display: flex; display: flex;
align-items: flex-start; align-items: flex-start;
padding: 12px 0; padding: 12px 0;
&:not(:last-child) { &:not(:last-child) {
border-bottom: 1px solid #f5f5f5; border-bottom: 1px solid #f5f5f5;
} }
@ -566,11 +556,11 @@ const goToInvoice = () => {
margin-right: 12px; margin-right: 12px;
margin-top: 2px; margin-top: 2px;
flex-shrink: 0; flex-shrink: 0;
&.balance-icon { &.balance-icon {
background: #1890ff; background: #1890ff;
} }
&.coupon-icon { &.coupon-icon {
background: #fa8c16; background: #fa8c16;
} }
@ -606,7 +596,7 @@ const goToInvoice = () => {
font-weight: 600; font-weight: 600;
color: #1890ff; color: #1890ff;
line-height: 1; line-height: 1;
&:before { &:before {
content: '¥'; content: '¥';
font-size: 14px; font-size: 14px;
@ -620,7 +610,7 @@ const goToInvoice = () => {
font-size: 12px; font-size: 12px;
color: #1890ff; color: #1890ff;
line-height: 1; line-height: 1;
&:hover { &:hover {
color: #40a9ff; color: #40a9ff;
} }
@ -641,7 +631,7 @@ const goToInvoice = () => {
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: center;
padding: 4px 0; padding: 4px 0;
} }
.rights-label { .rights-label {
@ -689,14 +679,16 @@ const goToInvoice = () => {
/* 算力点特殊样式 */ /* 算力点特殊样式 */
.rights-item:first-child { .rights-item:first-child {
.rights-amount { .rights-amount {
color: #fa8c16; /* 算力点用橙色 */ color: #fa8c16;
/* 算力点用橙色 */
} }
} }
/* 算力券特殊样式 */ /* 算力券特殊样式 */
.rights-item:last-child { .rights-item:last-child {
.rights-amount { .rights-amount {
color: #1890ff; /* 算力券用蓝色 */ color: #1890ff;
/* 算力券用蓝色 */
} }
} }
@ -707,7 +699,7 @@ const goToInvoice = () => {
color: #999; color: #999;
line-height: 1; line-height: 1;
margin-top: 4px; margin-top: 4px;
&:hover { &:hover {
color: #1890ff; color: #1890ff;
} }
@ -731,6 +723,7 @@ const goToInvoice = () => {
.nav-account { .nav-account {
padding-left: 10px; padding-left: 10px;
margin-top: -20px;
:first-child { :first-child {
font-weight: 500; font-weight: 500;
@ -767,7 +760,7 @@ const goToInvoice = () => {
height: 20px; height: 20px;
font-size: 10px; font-size: 10px;
padding: 2px 5px; padding: 2px 5px;
margin-top: 42px; margin-top: 30px;
margin-left: 10px; margin-left: 10px;
border-radius: 5px; border-radius: 5px;
border: 1px solid rgba(166, 166, 166, 1); border: 1px solid rgba(166, 166, 166, 1);
@ -786,7 +779,7 @@ const goToInvoice = () => {
height: auto; height: auto;
color: #1890ff; color: #1890ff;
font-size: 12px; font-size: 12px;
&:hover { &:hover {
color: #40a9ff; color: #40a9ff;
} }
@ -802,7 +795,7 @@ const goToInvoice = () => {
.invite-container { .invite-container {
flex-direction: column; flex-direction: column;
} }
.invite-content { .invite-content {
padding-right: 0; padding-right: 0;
border-right: none; border-right: none;
@ -810,17 +803,17 @@ const goToInvoice = () => {
padding-bottom: 24px; padding-bottom: 24px;
margin-bottom: 24px; margin-bottom: 24px;
} }
.rules-section { .rules-section {
padding-left: 0; padding-left: 0;
} }
.invite-actions { .invite-actions {
flex-direction: column; flex-direction: column;
align-items: flex-start; align-items: flex-start;
gap: 12px; gap: 12px;
} }
.rules-link { .rules-link {
margin-left: 0; margin-left: 0;
} }