Compare commits

..

No commits in common. "10a9268bbb178a6f4450697b8550337a95111956" and "60e84f5ef2f0f23e71aa58c2ceebba25b982ff83" have entirely different histories.

17 changed files with 731 additions and 485 deletions

View File

@ -2,5 +2,5 @@
NODE_ENV=development NODE_ENV=development
# api # api
VITE_API_BASIC="http://10.10.1.33:8888" VITE_API_BASIC="http://10.10.1.32:8888"

View File

@ -27,10 +27,4 @@ export const getMessageList = (params:any) => request.get('/v1/message/message_l
export const CodeAuth=(params:any)=>request.put("/v1/auth/update_password_auth",params) export const CodeAuth=(params:any)=>request.put("/v1/auth/update_password_auth",params)
//提现 //提现
export const tixian=(params:any)=>request.put("/v1/balance/withdraw",params) export const tixian=(params:any)=>request.put("/v1/balance/withdraw",params)
//充值记录
export const getUpList=(params:any)=>request.get("/v1/balance/top_up_list",{params})
//提现记录
export const getWithdrawList=(params:any)=>request.get("/v1/balance/withdraw_list",{params})

View File

@ -1,5 +1,5 @@
<template> <template>
<div style="margin: 20px;"> <div>
<a-row :gutter="[16, 16]"> <a-row :gutter="[16, 16]">
<a-col :span="24"> <a-col :span="24">
<a-card title="基本设置"> <a-card title="基本设置">

View File

@ -607,12 +607,11 @@ const showUnbindAgreement = () => {
<style scoped> <style scoped>
.bank-card-container { .bank-card-container {
max-width: 100%; max-width: 100%;
margin: 20px; margin: 0 auto;
padding: 32px; padding: 32px;
background-color: #fff; background-color: #fff;
border-radius: 12px; border-radius: 12px;
border: 1px solid #f0f0f0; box-shadow: 0 2px 12px rgba(0, 0, 0, 0.08);
/* box-shadow: 0 2px 12px rgba(0, 0, 0, 0.08); */
min-height: 400px; min-height: 400px;
} }

View File

@ -1,49 +1,61 @@
<template> <template>
<div class="recharge-page"> <div class="recharge-page">
<!-- 面包屑导航 --> <!-- 面包屑导航 -->
<!-- <a-breadcrumb class="breadcrumb"> <a-breadcrumb class="breadcrumb">
<a-breadcrumb-item> <a-breadcrumb-item>
<router-link to="/">首页</router-link> <router-link to="/">首页</router-link>
</a-breadcrumb-item> </a-breadcrumb-item>
<a-breadcrumb-item>我的余额</a-breadcrumb-item> <a-breadcrumb-item>我的余额</a-breadcrumb-item>
</a-breadcrumb> --> </a-breadcrumb>
<!-- 页面标题 --> <!-- 页面标题 -->
<!-- <div class="page-header"> <div class="page-header">
<h1 class="page-title">我的余额</h1> <h1 class="page-title">我的余额</h1>
</div> --> </div>
<div class="main-content"> <div class="main-content">
<!-- 余额和充值部分 --> <!-- 余额和充值部分 -->
<div class="balance-section"> <div class="balance-section">
<!-- 余额卡片带充值方式 --> <!-- 余额卡片带充值方式 -->
<a-card > <a-card class="balance-card" :bordered="false">
<div class="balance-header"> <div class="balance-header">
<div class="balance-info"> <div class="balance-info">
<div class="balance-amount"> <div class="balance-amount">
<div class="amount-label">账户余额</div> <div class="amount-label">账户余额</div>
<div class="amount-value">¥{{ formatCurrency(userInfo.balance || 0) }}</div> <div class="amount-value">¥{{ formatCurrency(balance) }}</div>
</div> </div>
</div> </div>
</div> </div>
<div class="recharge-content"> <div class="recharge-content">
<!-- 充值金额选择 --> <!-- 充值金额选择 -->
<div class="recharge-section"> <div class="recharge-section">
<h3 class="section-title">充值金额¥</h3> <h3 class="section-title">充值金额¥</h3>
<div class="amount-options"> <div class="amount-options">
<div v-for="option in amountOptions" :key="option.value" <div
v-for="option in amountOptions"
:key="option.value"
:class="['amount-option', { 'selected': selectedAmount === option.value }]" :class="['amount-option', { 'selected': selectedAmount === option.value }]"
@click="selectAmount(option.value)"> @click="selectAmount(option.value)"
>
<div class="amount-number">¥{{ option.value }}</div> <div class="amount-number">¥{{ option.value }}</div>
</div> </div>
</div> </div>
</div> </div>
<!-- 自定义金额输入 --> <!-- 自定义金额输入 -->
<div class="recharge-section"> <div class="recharge-section">
<h3 class="section-title">自定义充值金额</h3> <h3 class="section-title">自定义充值金额</h3>
<div class="custom-amount"> <div class="custom-amount">
<a-input v-model:value="customAmount" placeholder="请输入金额" size="large" :disabled="loading" <a-input
@change="handleCustomAmountChange"> v-model:value="customAmount"
placeholder="请输入金额"
size="large"
:disabled="loading"
@change="handleCustomAmountChange"
>
<template #prefix> <template #prefix>
<span class="input-prefix">¥</span> <span class="input-prefix">¥</span>
</template> </template>
@ -58,9 +70,12 @@
<div class="payment-methods-container"> <div class="payment-methods-container">
<div class="payment-methods-title">充值方式</div> <div class="payment-methods-title">充值方式</div>
<div class="payment-methods-row"> <div class="payment-methods-row">
<div v-for="method in paymentMethods" :key="method.value" <div
v-for="method in paymentMethods"
:key="method.value"
:class="['payment-method-item', { 'selected': selectedPaymentMethod === method.value }]" :class="['payment-method-item', { 'selected': selectedPaymentMethod === method.value }]"
@click="selectPaymentMethod(method.value)"> @click="selectPaymentMethod(method.value)"
>
<div class="method-icon"> <div class="method-icon">
<component :is="method.icon" /> <component :is="method.icon" />
</div> </div>
@ -77,8 +92,14 @@
<!-- 操作按钮 --> <!-- 操作按钮 -->
<div class="action-section"> <div class="action-section">
<a-button type="primary" size="large" :loading="loading" :disabled="!canRecharge" @click="handleRecharge" <a-button
class="recharge-btn"> type="primary"
size="large"
:loading="loading"
:disabled="!canRecharge"
@click="handleRecharge"
class="recharge-btn"
>
立即充值 立即充值
</a-button> </a-button>
</div> </div>
@ -87,15 +108,17 @@
</div> </div>
<!-- 充值记录 --> <!-- 充值记录 -->
<a-card title="充值记录"> <a-card class="records-card" title="充值记录">
<!-- 筛选条件 --> <!-- 筛选条件 -->
<div class="filter-section"> <div class="filter-section">
<a-range-picker v-model:value="dateRange" format="YYYY-MM-DD" placeholder="开始时间 - 结束时间" <a-range-picker
style="width: 300px; margin-right: 16px;" /> v-model:value="dateRange"
format="YYYY-MM-DD"
placeholder="开始时间 - 结束时间"
style="width: 300px; margin-right: 16px;"
/>
<a-button type="primary" @click="handleSearch" :loading="searchLoading"> <a-button type="primary" @click="handleSearch" :loading="searchLoading">
<template #icon> <template #icon><SearchOutlined /></template>
<SearchOutlined />
</template>
查询 查询
</a-button> </a-button>
<a-button style="margin-left: 8px" @click="handleReset"> <a-button style="margin-left: 8px" @click="handleReset">
@ -105,8 +128,15 @@
<!-- 记录表格 --> <!-- 记录表格 -->
<div class="records-table"> <div class="records-table">
<a-table :columns="columns" :data-source="rechargeRecords" :pagination="pagination" :loading="tableLoading" <a-table
@change="handleTableChange" size="middle" bordered> :columns="columns"
:data-source="rechargeRecords"
:pagination="pagination"
:loading="tableLoading"
@change="handleTableChange"
size="middle"
bordered
>
<template #bodyCell="{ column, record }"> <template #bodyCell="{ column, record }">
<template v-if="column.key === 'amount'"> <template v-if="column.key === 'amount'">
<span class="amount-cell">¥{{ record.amount }}</span> <span class="amount-cell">¥{{ record.amount }}</span>
@ -143,8 +173,14 @@
</div> </div>
<!-- 充值确认模态框 --> <!-- 充值确认模态框 -->
<a-modal v-model:visible="rechargeModalVisible" title="确认充值" :confirm-loading="modalLoading" <a-modal
@ok="handleRechargeConfirm" @cancel="handleRechargeCancel" centered> v-model:visible="rechargeModalVisible"
title="确认充值"
:confirm-loading="modalLoading"
@ok="handleRechargeConfirm"
@cancel="handleRechargeCancel"
centered
>
<div class="recharge-confirm"> <div class="recharge-confirm">
<div class="confirm-item"> <div class="confirm-item">
<span class="confirm-label">充值金额</span> <span class="confirm-label">充值金额</span>
@ -156,11 +192,11 @@
</div> </div>
<div class="confirm-item"> <div class="confirm-item">
<span class="confirm-label">当前余额</span> <span class="confirm-label">当前余额</span>
<span class="confirm-value">¥{{ formatCurrency(userInfo.balance) }}</span> <span class="confirm-value">¥{{ formatCurrency(balance) }}</span>
</div> </div>
<div class="confirm-item"> <div class="confirm-item">
<span class="confirm-label">充值后余额</span> <span class="confirm-label">充值后余额</span>
<span class="confirm-value highlight">¥{{ formatCurrency(userInfo.balance + selectedRechargeAmount) }}</span> <span class="confirm-value highlight">¥{{ formatCurrency(balance + selectedRechargeAmount) }}</span>
</div> </div>
<div class="confirm-tips"> <div class="confirm-tips">
<InfoCircleOutlined /> <InfoCircleOutlined />
@ -172,8 +208,8 @@
</template> </template>
<script setup> <script setup>
import { ref, reactive, computed, onMounted, } from 'vue' import { ref, reactive, computed, onMounted } from 'vue'
import { import {
SearchOutlined, SearchOutlined,
CheckCircleFilled, CheckCircleFilled,
InfoCircleOutlined, InfoCircleOutlined,
@ -183,10 +219,9 @@ import {
} from '@ant-design/icons-vue' } from '@ant-design/icons-vue'
import { message } from 'ant-design-vue' import { message } from 'ant-design-vue'
import dayjs from 'dayjs' import dayjs from 'dayjs'
import { getUpList } from "@/apis/home"
// //
const balance = ref(0) const balance = ref(5.00)
// //
const amountOptions = ref([ const amountOptions = ref([
@ -213,7 +248,7 @@ const loading = ref(false)
const tableLoading = ref(false) const tableLoading = ref(false)
const searchLoading = ref(false) const searchLoading = ref(false)
const modalLoading = ref(false) const modalLoading = ref(false)
const userInfo = ref({})
// //
const rechargeModalVisible = ref(false) const rechargeModalVisible = ref(false)
@ -327,7 +362,6 @@ const handleRecharge = () => {
// //
const handleRechargeConfirm = () => { const handleRechargeConfirm = () => {
modalLoading.value = true modalLoading.value = true
setTimeout(() => { setTimeout(() => {
balance.value += selectedRechargeAmount.value balance.value += selectedRechargeAmount.value
const now = new Date() const now = new Date()
@ -341,10 +375,12 @@ const handleRechargeConfirm = () => {
} }
rechargeRecords.value.unshift(newRecord) rechargeRecords.value.unshift(newRecord)
pagination.total += 1 pagination.total += 1
// //
selectedAmount.value = 0 selectedAmount.value = 0
customAmount.value = '' customAmount.value = ''
customAmountError.value = '' customAmountError.value = ''
modalLoading.value = false modalLoading.value = false
rechargeModalVisible.value = false rechargeModalVisible.value = false
message.success(`充值成功!¥${selectedRechargeAmount.value} 已到账`) message.success(`充值成功!¥${selectedRechargeAmount.value} 已到账`)
@ -375,41 +411,69 @@ const viewDetail = (record) => {
message.info(`充值¥${record.amount},时间:${formatDateTime(record.rechargeTime)}`) message.info(`充值¥${record.amount},时间:${formatDateTime(record.rechargeTime)}`)
} }
//
const loadRechargeRecords = () => {
tableLoading.value = true
setTimeout(() => {
const mockData = generateMockData(35)
let filtered = [...mockData]
if (dateRange.value?.length === 2) {
const start = dayjs(dateRange.value[0]).startOf('day')
const end = dayjs(dateRange.value[1]).endOf('day')
filtered = filtered.filter(r => {
const d = dayjs(r.rechargeTime)
return d.isAfter(start) && d.isBefore(end)
})
}
const startIdx = (pagination.current - 1) * pagination.pageSize
rechargeRecords.value = filtered.slice(startIdx, startIdx + pagination.pageSize)
pagination.total = filtered.length
tableLoading.value = false
searchLoading.value = false
}, 500)
}
// //
const loadRechargeRecords = async () => { const generateMockData = (count) => {
try { const methods = ['alipay', 'wechat', 'bank']
tableLoading.value = true const statuses = ['success', 'processing', 'failed', 'pending']
const params = { let currentBalance = 5.00
start_at: dateRange.value[0], const data = []
end_at: dateRange.value[1],
page_num: pagination.current, for (let i = 1; i <= count; i++) {
page_size: pagination.pageSize const amount = [100, 500, 1000, 5000][Math.floor(Math.random() * 4)]
} const method = methods[Math.floor(Math.random() * methods.length)]
const res = await getUpList(params) const status = statuses[Math.floor(Math.random() * statuses.length)]
rechargeRecords.value = res.data
pagination.total = res.total if (status === 'success') currentBalance += amount
tableLoading.value = false
} catch (error) { data.push({
tableLoading.value = false key: i,
amount: amount,
rechargeTime: dayjs().subtract(Math.floor(Math.random() * 30), 'day').toDate(),
paymentMethod: method,
accountBalance: currentBalance,
status: status
})
} }
return data.sort((a, b) => new Date(b.rechargeTime) - new Date(a.rechargeTime))
} }
onMounted(() => { onMounted(() => {
const userInfoStr = localStorage.getItem('userInfo'); rechargeRecords.value = generateMockData(15)
if (userInfoStr) { pagination.total = rechargeRecords.value.length
userInfo.value = JSON.parse(userInfoStr);
}
loadRechargeRecords()
}) })
</script> </script>
<style scoped> <style scoped>
.recharge-page { .recharge-page {
padding: 20px; padding: 24px;
background-color: #ffffff; background-color: #fafafa;
/* min-height: 100vh; */ min-height: 100vh;
} }
.breadcrumb { .breadcrumb {
@ -428,8 +492,8 @@ onMounted(() => {
} }
.main-content { .main-content {
/* max-width: 1200px; */ /* max-width: 1200px;
margin: 0 auto; margin: 0 auto; */
} }
/* 余额区域 */ /* 余额区域 */
@ -545,17 +609,9 @@ onMounted(() => {
font-size: 18px; font-size: 18px;
} }
.payment-method-item:nth-child(1) .method-icon { .payment-method-item:nth-child(1) .method-icon { color: #1677ff; }
color: #1677ff; .payment-method-item:nth-child(2) .method-icon { color: #07c160; }
} .payment-method-item:nth-child(3) .method-icon { color: #722ed1; }
.payment-method-item:nth-child(2) .method-icon {
color: #07c160;
}
.payment-method-item:nth-child(3) .method-icon {
color: #722ed1;
}
.method-info { .method-info {
flex: 1; flex: 1;

View File

@ -2,7 +2,7 @@
<div class="withdrawal-management"> <div class="withdrawal-management">
<!-- 提现账户信息卡片 --> <!-- 提现账户信息卡片 -->
<a-row :gutter="24" class="mb-6"> <a-row :gutter="24" class="mb-6">
<a-col :span="24"> <a-col :span="24">
<a-card title="可提现金额"> <a-card title="可提现金额">
<div class="balance-info"> <div class="balance-info">
@ -13,8 +13,13 @@
<div class="balance-desc"> <div class="balance-desc">
当前可提现金额提现将在1-3个工作日内到账 当前可提现金额提现将在1-3个工作日内到账
</div> </div>
<a-button type="primary" class="w-full mt-4" @click="showWithdrawalModal" <a-button
:disabled="accountInfo.availableBalance <= 0"> type="primary"
size="small"
class="w-full mt-4"
@click="showWithdrawalModal"
:disabled="accountInfo.availableBalance <= 0"
>
去提现 去提现
</a-button> </a-button>
</div> </div>
@ -25,17 +30,49 @@
<!-- 提现记录查询 --> <!-- 提现记录查询 -->
<a-card title="提现记录" class="mb-6"> <a-card title="提现记录" class="mb-6">
<template #extra> <template #extra>
<a-range-picker
v-model:value="searchParams.dateRange"
:format="dateFormat"
@change="handleDateChange"
/>
</template> </template>
<!-- 查询条件 --> <!-- 查询条件 -->
<a-form layout="inline" :model="searchParams" class="mb-4"> <a-form layout="inline" :model="searchParams" class="mb-4">
<a-form-item label="提现单号"> <a-form-item label="提现单号">
<a-input v-model:value="searchParams.orderNo" placeholder="请输入提现单号" @pressEnter="handleSearch" /> <a-input
v-model:value="searchParams.orderNo"
placeholder="请输入提现单号"
@pressEnter="handleSearch"
/>
</a-form-item> </a-form-item>
<a-form-item label="时间">
<a-range-picker v-model:value="searchParams.dateRange" :format="dateFormat" @change="handleDateChange" /> <a-form-item label="账户">
<a-select
v-model:value="searchParams.account"
placeholder="请选择账户"
style="width: 150px"
allowClear
>
<a-select-option v-for="account in accountOptions" :key="account.value">
{{ account.label }}
</a-select-option>
</a-select>
</a-form-item> </a-form-item>
<a-form-item label="状态">
<a-select
v-model:value="searchParams.status"
placeholder="请选择状态"
style="width: 150px"
allowClear
>
<a-select-option value="pending">处理中</a-select-option>
<a-select-option value="success">成功</a-select-option>
<a-select-option value="failed">失败</a-select-option>
</a-select>
</a-form-item>
<a-form-item> <a-form-item>
<a-button type="primary" @click="handleSearch">查询</a-button> <a-button type="primary" @click="handleSearch">查询</a-button>
<a-button @click="handleReset" class="ml-2">重置</a-button> <a-button @click="handleReset" class="ml-2">重置</a-button>
@ -43,27 +80,33 @@
</a-form> </a-form>
<!-- 提现记录表格 --> <!-- 提现记录表格 -->
<a-table :columns="columns" :data-source="withdrawalRecords" :pagination="pagination" @change="handleTableChange" <a-table
:loading="loading" rowKey="id"> :columns="columns"
:data-source="withdrawalRecords"
:pagination="pagination"
@change="handleTableChange"
:loading="loading"
rowKey="id"
>
<template #bodyCell="{ column, record }"> <template #bodyCell="{ column, record }">
<template v-if="column.key === 'amount'"> <template v-if="column.key === 'amount'">
<span class="amount-cell">{{ formatCurrency(record.amount) }}</span> <span class="amount-cell">{{ formatCurrency(record.amount) }}</span>
</template> </template>
<template v-else-if="column.key === 'status'"> <template v-else-if="column.key === 'status'">
<a-tag :color="getStatusColor(record.status)"> <a-tag :color="getStatusColor(record.status)">
{{ getStatusText(record.status) }} {{ getStatusText(record.status) }}
</a-tag> </a-tag>
</template> </template>
<template v-else-if="column.key === 'createdAt'"> <template v-else-if="column.key === 'createdAt'">
{{ formatDate(record.createdAt) }} {{ formatDate(record.createdAt) }}
</template> </template>
<template v-else-if="column.key === 'completedAt'"> <template v-else-if="column.key === 'completedAt'">
{{ record.completedAt ? formatDate(record.completedAt) : '-' }} {{ record.completedAt ? formatDate(record.completedAt) : '-' }}
</template> </template>
<template v-else-if="column.key === 'actions'"> <template v-else-if="column.key === 'actions'">
<a-button type="link" @click="viewDetail(record)">查看详情</a-button> <a-button type="link" @click="viewDetail(record)">查看详情</a-button>
</template> </template>
@ -72,31 +115,53 @@
</a-card> </a-card>
<!-- 提现弹窗 --> <!-- 提现弹窗 -->
<a-modal v-model:visible="withdrawalModal.visible" title="提现申请" @ok="handleWithdrawalSubmit" <a-modal
@cancel="handleWithdrawalCancel" :confirm-loading="withdrawalModal.confirming"> v-model:visible="withdrawalModal.visible"
<a-form ref="withdrawalFormRef" :model="withdrawalForm" :rules="withdrawalRules" layout="vertical"> title="提现申请"
@ok="handleWithdrawalSubmit"
@cancel="handleWithdrawalCancel"
:confirm-loading="withdrawalModal.confirming"
>
<a-form
ref="withdrawalFormRef"
:model="withdrawalForm"
:rules="withdrawalRules"
layout="vertical"
>
<a-form-item label="提现金额" name="amount"> <a-form-item label="提现金额" name="amount">
<a-input-number v-model:value="withdrawalForm.amount" placeholder="请输入提现金额" :min="1" <a-input-number
:max="accountInfo.availableBalance" style="width: 100%" v-model:value="withdrawalForm.amount"
placeholder="请输入提现金额"
:min="1"
:max="accountInfo.availableBalance"
style="width: 100%"
:formatter="value => `¥ ${value}`.replace(/\B(?=(\d{3})+(?!\d))/g, ',')" :formatter="value => `¥ ${value}`.replace(/\B(?=(\d{3})+(?!\d))/g, ',')"
:parser="value => value.replace(/¥\s?|(,*)/g, '')" /> :parser="value => value.replace(/¥\s?|(,*)/g, '')"
/>
<div class="amount-hint mt-2"> <div class="amount-hint mt-2">
<div>可提现金额: {{ formatCurrency(accountInfo.availableBalance) }}</div> <div>可提现金额: {{ formatCurrency(accountInfo.availableBalance) }}</div>
<div>单笔最低提现: 1</div> <div>单笔最低提现: 1</div>
</div> </div>
</a-form-item> </a-form-item>
<a-form-item label="提现到账账户" name="accountId"> <a-form-item label="提现到账账户" name="accountId">
<a-select v-model:value="withdrawalForm.accountId" placeholder="请选择提现账户"> <a-select
v-model:value="withdrawalForm.accountId"
placeholder="请选择提现账户"
>
<a-select-option :value="accountInfo.id"> <a-select-option :value="accountInfo.id">
{{ accountInfo.bank }} ({{ accountInfo.number }}) {{ accountInfo.bank }} ({{ accountInfo.number }})
</a-select-option> </a-select-option>
</a-select> </a-select>
</a-form-item> </a-form-item>
<a-alert v-if="withdrawalForm.amount" <a-alert
:message="`预计到账金额: ${formatCurrency(calculateActualAmount(withdrawalForm.amount))}元`" type="info" show-icon v-if="withdrawalForm.amount"
class="mb-4" /> :message="`预计到账金额: ${formatCurrency(calculateActualAmount(withdrawalForm.amount))}元`"
type="info"
show-icon
class="mb-4"
/>
</a-form> </a-form>
</a-modal> </a-modal>
</div> </div>
@ -106,7 +171,7 @@
import { ref, reactive, onMounted } from 'vue' import { ref, reactive, onMounted } from 'vue'
import { message } from 'ant-design-vue' import { message } from 'ant-design-vue'
import dayjs from 'dayjs' import dayjs from 'dayjs'
import { getWithdrawList } from "@/apis/home"
// //
const accountInfo = reactive({ const accountInfo = reactive({
id: '1', id: '1',
@ -229,19 +294,51 @@ onMounted(() => {
// //
const fetchWithdrawalRecords = async () => { const fetchWithdrawalRecords = async () => {
loading.value = true loading.value = true
console.log(searchParams)
try { try {
let params = { // API
withdraw_number: searchParams.orderNo, await new Promise(resolve => setTimeout(resolve, 500))
page_num: pagination.current,
page_size: pagination.pageSize, //
start_at: formatDate(searchParams.dateRange[0]), const mockData = Array.from({ length: 35 }, (_, index) => ({
end_at: formatDate(searchParams.dateRange[1]), id: index + 1,
orderNo: `TX${Date.now()}${index}`,
bank: ['工商银行', '建设银行', '农业银行'][index % 3],
accountNumber: `6228 **** **** ${1000 + index}`,
amount: Math.floor(Math.random() * 10000) + 100,
fee: Math.floor(Math.random() * 10),
status: ['pending', 'success', 'failed'][index % 3],
createdAt: dayjs().subtract(index, 'day').format('YYYY-MM-DD HH:mm:ss'),
completedAt: index % 3 !== 0 ? dayjs().subtract(index, 'day').add(1, 'hour').format('YYYY-MM-DD HH:mm:ss') : null
}))
//
let filteredData = mockData
if (searchParams.orderNo) {
filteredData = filteredData.filter(item => item.orderNo.includes(searchParams.orderNo))
} }
const res = await getWithdrawList(params) if (searchParams.account) {
console.log(res) filteredData = filteredData.filter(item => item.accountNumber.endsWith(searchParams.account))
}
if (searchParams.status) {
filteredData = filteredData.filter(item => item.status === searchParams.status)
}
//
if (searchParams.dateRange && searchParams.dateRange[0] && searchParams.dateRange[1]) {
const start = searchParams.dateRange[0]
const end = searchParams.dateRange[1]
filteredData = filteredData.filter(item => {
const date = dayjs(item.createdAt)
return date.isAfter(start) && date.isBefore(end)
})
}
//
const start = (pagination.current - 1) * pagination.pageSize
const end = start + pagination.pageSize
withdrawalRecords.value = filteredData.slice(start, end)
pagination.total = filteredData.length
} catch (error) { } catch (error) {
console.log(error)
message.error('获取提现记录失败') message.error('获取提现记录失败')
} finally { } finally {
loading.value = false loading.value = false
@ -285,19 +382,19 @@ const handleWithdrawalSubmit = async () => {
try { try {
await withdrawalFormRef.value.validate() await withdrawalFormRef.value.validate()
withdrawalModal.confirming = true withdrawalModal.confirming = true
// API // API
await new Promise(resolve => setTimeout(resolve, 1000)) await new Promise(resolve => setTimeout(resolve, 1000))
message.success('提现申请提交成功') message.success('提现申请提交成功')
// //
accountInfo.availableBalance -= withdrawalForm.amount accountInfo.availableBalance -= withdrawalForm.amount
// //
withdrawalFormRef.value.resetFields() withdrawalFormRef.value.resetFields()
withdrawalModal.visible = false withdrawalModal.visible = false
// //
fetchWithdrawalRecords() fetchWithdrawalRecords()
} catch (error) { } catch (error) {
@ -331,7 +428,7 @@ const formatCurrency = (amount) => {
// //
const formatDate = (dateString) => { const formatDate = (dateString) => {
return dayjs(dateString).format('YYYY-MM-DD') return dayjs(dateString).format('YYYY-MM-DD HH:mm:ss')
} }
// //

View File

@ -1,87 +1,87 @@
<template> <template>
<div style="margin: 20px;"> <a-row :gutter="[18, 18]">
<a-row :gutter="[18, 18]">
<a-col :span="24"> <a-col :span="24">
<a-card title="开票记录"> <a-card title="开票记录">
<a-table :dataSource="listData" :columns="columns" bordered :pagination="paginationState" <a-table :dataSource="listData" :columns="columns" bordered :pagination="paginationState"
@change="onTableChange" /> @change="onTableChange" />
</a-card>
</a-col>
</a-row>
<a-modal v-model:open="visibleOpen" title="发票信息" @ok="handleOk" style="width: 800px;">
<a-card>
<a-form ref="formRef" :model="formData" labelAlign="left" :rules="rules">
<a-row :gutter="24">
<a-col :span="12">
<a-form-item label="发票抬头" name="title">
<a-input v-model:value="formData.title" />
</a-form-item>
</a-col>
<a-col :span="12">
<a-form-item label="发票类型" name="type">
<a-radio-group v-model:value="formData.type" button-style="solid">
<a-radio-button value="a">增值税发票</a-radio-button>
<a-radio-button value="b">增值税专用发票</a-radio-button>
</a-radio-group>
</a-form-item>
</a-col>
<a-col :span="12">
<a-form-item label="纳税人识别号" name="number">
<a-input v-model:value="formData.number" />
</a-form-item>
</a-col>
<a-col :span="12">
<a-form-item label="开户银行名称" name="bankName">
<a-input v-model:value="formData.bankName" />
</a-form-item>
</a-col>
<a-col :span="12">
<a-form-item label="基本开户账号" name="bankNumber">
<a-input v-model:value="formData.bankNumber" />
</a-form-item>
</a-col>
<a-col :span="12">
<a-form-item label="注册场所地址" name="address">
<a-input v-model:value="formData.address" />
</a-form-item>
</a-col>
<a-col :span="12">
<a-form-item label="注册固定电话" name="phone">
<a-input v-model:value="formData.phone" />
</a-form-item>
</a-col>
<a-col :span="12">
<a-form-item label="收件人姓名" name="recipientName">
<a-input v-model:value="formData.recipientName" />
</a-form-item>
</a-col>
<a-col :span="12">
<a-form-item label="收件人手机号" name="recipientPhone">
<a-input v-model:value="formData.recipientPhone" />
</a-form-item>
</a-col>
<a-col :span="12">
<a-form-item label="收件人地址" name="recipientAddress">
<a-input v-model:value="formData.recipientAddress" />
</a-form-item>
</a-col>
<a-col :span="12">
<a-form-item label="邮箱地址" name="emailAddress">
<a-input v-model:value="formData.emailAddress" />
</a-form-item>
</a-col>
</a-row>
</a-form>
</a-card> </a-card>
</a-modal> </a-col>
</div> </a-row>
<a-modal v-model:open="visibleOpen" title="发票信息" @ok="handleOk" style="width: 800px;">
<a-card>
<a-form ref="formRef" :model="formData" labelAlign="left" :rules="rules">
<a-row :gutter="24">
<a-col :span="12">
<a-form-item label="发票抬头" name="title">
<a-input v-model:value="formData.title" />
</a-form-item>
</a-col>
<a-col :span="12">
<a-form-item label="发票类型" name="type">
<a-radio-group v-model:value="formData.type" button-style="solid">
<a-radio-button value="a">增值税发票</a-radio-button>
<a-radio-button value="b">增值税专用发票</a-radio-button>
</a-radio-group>
</a-form-item>
</a-col>
<a-col :span="12">
<a-form-item label="纳税人识别号" name="number">
<a-input v-model:value="formData.number" />
</a-form-item>
</a-col>
<a-col :span="12">
<a-form-item label="开户银行名称" name="bankName">
<a-input v-model:value="formData.bankName" />
</a-form-item>
</a-col>
<a-col :span="12">
<a-form-item label="基本开户账号" name="bankNumber">
<a-input v-model:value="formData.bankNumber" />
</a-form-item>
</a-col>
<a-col :span="12">
<a-form-item label="注册场所地址" name="address">
<a-input v-model:value="formData.address" />
</a-form-item>
</a-col>
<a-col :span="12">
<a-form-item label="注册固定电话" name="phone">
<a-input v-model:value="formData.phone" />
</a-form-item>
</a-col>
<a-col :span="12">
<a-form-item label="收件人姓名" name="recipientName">
<a-input v-model:value="formData.recipientName" />
</a-form-item>
</a-col>
<a-col :span="12">
<a-form-item label="收件人手机号" name="recipientPhone">
<a-input v-model:value="formData.recipientPhone" />
</a-form-item>
</a-col>
<a-col :span="12">
<a-form-item label="收件人地址" name="recipientAddress">
<a-input v-model:value="formData.recipientAddress" />
</a-form-item>
</a-col>
<a-col :span="12">
<a-form-item label="邮箱地址" name="emailAddress">
<a-input v-model:value="formData.emailAddress" />
</a-form-item>
</a-col>
</a-row>
</a-form>
</a-card>
</a-modal>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { ref, onBeforeMount, computed } from 'vue' import { ref, onBeforeMount, computed } from 'vue'
import { usePagination } from '@/hooks' import { usePagination } from '@/hooks'
import { import {
invoiceList, invoiceList,
invoiceDetail invoiceDetail
} from '@/apis/admin' } from '@/apis/admin'
// const listData=ref([ {title:1}]) // const listData=ref([ {title:1}])
@ -94,8 +94,8 @@ import {
// }) // })
const { listData, loading, showLoading, hideLoading, paginationState, resetPagination, searchFormData } = usePagination() const { listData, loading, showLoading, hideLoading, paginationState, resetPagination, searchFormData } = usePagination()
const visibleOpen = ref(false) const visibleOpen = ref(false)
const formData = ref<any>({ type: 'a' }) const formData = ref({ type: 'a' })
const formRef = ref(null) const formRef=ref(null)
const rules = computed(() => ({ const rules = computed(() => ({
title: [{ required: true, message: '请输入发票标题', trigger: 'blur' }], title: [{ required: true, message: '请输入发票标题', trigger: 'blur' }],
type: [{ required: true, message: '请选择发票类型', trigger: 'change' }], type: [{ required: true, message: '请选择发票类型', trigger: 'change' }],
@ -155,24 +155,24 @@ onBeforeMount(() => {
}) })
const getPageList = async () => { const getPageList = async () => {
try { try {
const params = { const params = {
page_num: 1, page_num: 1,
page_size: 10, // page_size: 10, //
}; };
const response: any = await invoiceList(params); const response: any = await invoiceList(params);
if (response.data && Array.isArray(response.data)) { if (response.data && Array.isArray(response.data)) {
listData.value = response.data listData.value = response.data
console.log('发票列表:', response.data); console.log('发票列表:', response.data);
}
} catch (error) {
console.error('获取全部消息失败:', error);
listData.value = [];
} }
} catch (error) {
console.error('获取全部消息失败:', error);
listData.value = [];
}
}; };
/** /**
* 分页 * 分页
@ -200,11 +200,11 @@ function handleResetSearch() {
function handleOk() { function handleOk() {
visibleOpen.value = false visibleOpen.value = false
formRef.value.validateFields().then(async (values) => { formRef.value.validateFields().then(async(values)=>{
try { try {
} catch (error) { } catch (error) {
} }
}) })
} }

View File

@ -4,13 +4,13 @@
<a-row :gutter="24" class="asset-cards"> <a-row :gutter="24" class="asset-cards">
<!-- 左侧可用余额卡片 --> <!-- 左侧可用余额卡片 -->
<a-col :span="8"> <a-col :span="8">
<a-card class="card balance-card" title="可用余额"> <a-card :bordered="false" class="card balance-card" title="可用余额">
<template #extra><a-button type="link" size="small" class="fee-titleb" <template #extra><a-button type="link" size="small" class="fee-titleb"
@click="goToBills">查看消费明细</a-button></template> @click="goToBills">查看消费明细</a-button></template>
<div class="money">¥ {{ formatAmount(userInfo.balance || 0) }}</div> <div class="money">¥ {{ formatAmount(userInfo.balance || 0) }}</div>
<div class="money-btn"> <div class="money-btn">
<div><a-button type="primary" size="small" @click="goToRecharge">充值</a-button></div> <div><a-button type="primary" size="small" @click="goToRecharge">充值</a-button></div>
<div><a-button size="small" @click="router.push('/layout/admin/deposit')">提现</a-button></div> <div><a-button size="small" @click="dialogWithDrawal = true">提现</a-button></div>
</div> </div>
</a-card> </a-card>
</a-col> </a-col>
@ -18,11 +18,10 @@
<!-- 右侧算力点和算力券卡片 --> <!-- 右侧算力点和算力券卡片 -->
<a-col :span="16"> <a-col :span="16">
<!-- 算力点卡片 --> <!-- 算力点卡片 -->
<a-card title="算力点" class="card computing-card"> <a-card title="算力点" :bordered="false" class="card computing-card">
<div class="computing-content"> <div class="computing-content">
<div class="computing-amount"> <div class="computing-amount">
<span class="amount-value">{{ '¥' + formatAmount(userInfo.computingPowerPoint || 0) <span class="amount-value">{{ '¥' + formatAmount(userInfo.computingPowerPoint || 0) }}</span>
}}</span>
<!-- <span class="amount-unit"></span> --> <!-- <span class="amount-unit"></span> -->
</div> </div>
<div class="computing-actions"> <div class="computing-actions">
@ -32,7 +31,7 @@
</a-card> </a-card>
<!-- 算力券卡片 --> <!-- 算力券卡片 -->
<a-card title="算力券" class="card coupon-card"> <a-card title="算力券" :bordered="false" class="card coupon-card">
<div class="coupon-content"> <div class="coupon-content">
<div class="coupon-amount"> <div class="coupon-amount">
<span class="amount-value">{{ userInfo.voucherNum }}</span> <span class="amount-value">{{ userInfo.voucherNum }}</span>
@ -46,48 +45,47 @@
</a-col> </a-col>
</a-row> </a-row>
<!-- <div class="bill-section"> <!-- 底部账单区域 -->
<a-tabs v-model:activeKey="activeKey"> <div class="bill-section">
<a-tab-pane key="1" tab="充值记录"> <div class="bill-header">
<h3 class="bill-title">消费明细</h3>
<a-table :columns="columns" :data-source="billData" :pagination="pagination" </div>
@change="handleTableChange" :loading="loading" class="bill-table" :scroll="{ x: 1200 }"> <!-- 账单表格 -->
<a-table :columns="columns" :data-source="billData" :pagination="pagination" @change="handleTableChange"
<template #bodyCell="{ column, record }"> :loading="loading" class="bill-table" :scroll="{ x: 1200 }">
<template v-if="column.key === 'serialNumber'"> <!-- 流水号列 -->
<span class="serial-number">{{ record.serialNumber }}</span> <template #bodyCell="{ column, record }">
</template> <template v-if="column.key === 'serialNumber'">
<span class="serial-number">{{ record.serialNumber }}</span>
</template>
<!-- 交易类型列 -->
<template v-else-if="column.key === 'transactionType'"> <template v-else-if="column.key === 'transactionType'">
<a-tag :color="getTransactionTypeColor(record.transactionType)"> <a-tag :color="getTransactionTypeColor(record.transactionType)">
{{ record.transactionType }} {{ record.transactionType }}
</a-tag> </a-tag>
</template> </template>
<template <!-- 金额相关列 -->
v-else-if="['transactionAmount', 'originalPrice', 'discountAmount', 'balancePayment', 'voucherDeduction'].includes(column.key)"> <template
<span :class="{ v-else-if="['transactionAmount', 'originalPrice', 'discountAmount', 'balancePayment', 'voucherDeduction'].includes(column.key)">
'amount-positive': record[column.key] > 0, <span :class="{
'amount-negative': record[column.key] < 0 'amount-positive': record[column.key] > 0,
}"> 'amount-negative': record[column.key] < 0
¥ {{ formatAmount(Math.abs(record[column.key])) }} }">
</span> ¥ {{ formatAmount(Math.abs(record[column.key])) }}
</template> </span>
</template> </template>
</a-table> </template>
</a-tab-pane> </a-table>
<a-tab-pane key="2" tab="提现记录">Content of Tab Pane 2</a-tab-pane> </div>
</a-tabs>
</div> -->
</div> </div>
<a-modal v-model:open="dialogWithDrawal" title="提现" @ok="handleOk"> <a-modal v-model:open="dialogWithDrawal" title="提现" @ok="handleOk">
<a-card> <a-card>
<div style="display: flex;justify-content: flex-start;align-items: center;"> <div style="display: flex;justify-content: flex-start;align-items: center;">
<span style="width: 120px;">提现金额</span> <span style="width: 120px;">提现金额</span>
<a-input-number id="inputNumber" v-model:value="withDrawalCount" placeholder="请输入提现金额" <a-input-number id="inputNumber" v-model:value="withDrawalCount" placeholder="请输入提现金额" style="width: 100%;"/>
style="width: 100%;" /> <a-button type="link" @click="withDrawalCount=userInfo.balance" :loading="btnLoading">全部提现</a-button>
<a-button type="link" @click="withDrawalCount = userInfo.balance" :loading="btnLoading">全部提现</a-button>
</div> </div>
</a-card> </a-card>
</a-modal> </a-modal>
@ -96,14 +94,13 @@
<script setup lang="ts"> <script setup lang="ts">
import { ref, onMounted, computed, } from 'vue' import { ref, onMounted, computed, } from 'vue'
import { useRouter } from 'vue-router' import { useRouter } from 'vue-router'
import { message, TableProps } from 'ant-design-vue' import {message,TableProps} from 'ant-design-vue'
import type { Dayjs } from 'dayjs' import type { Dayjs } from 'dayjs'
import { tixian } from "@/apis/home" import{tixian} from "@/apis/home"
import { fetchUserInfo } from '@/apis/modules/login'; import { fetchUserInfo } from '@/apis/modules/login';
const router = useRouter() const router = useRouter()
const activeKey = ref('1')
// //
const btnLoading = ref(false) const btnLoading=ref(false)
const dialogWithDrawal = ref(false) const dialogWithDrawal = ref(false)
const withDrawalCount = ref(0) const withDrawalCount = ref(0)
// //
@ -135,40 +132,66 @@ const pagination = ref({
// //
const columns = computed(() => [ const columns = computed(() => [
{ {
title: '账户', title: '流水号',
dataIndex: 'account', dataIndex: 'serialNumber',
key: 'account', key: 'serialNumber',
width: 180, width: 180,
ellipsis: true ellipsis: true
}, },
{ {
title: '充值时间', title: '交易时间',
dataIndex: 'CreatedAt', dataIndex: 'transactionTime',
key: 'CreatedAt', key: 'transactionTime',
width: 170, width: 170,
sorter: true sorter: true
}, },
{ {
title: '余额', title: '交易类型',
dataIndex: 'balance', dataIndex: 'transactionType',
key: 'balance', key: 'transactionType',
width: 120, width: 120,
filters: [
{ text: '充值', value: '充值' },
{ text: '消费', value: '消费' },
{ text: '提现', value: '提现' },
{ text: '退款', value: '退款' }
]
}, },
{ {
title: '支付方式', title: '收支类型',
dataIndex: 'payMethod', dataIndex: 'productName',
key: 'payMethod', key: 'productName',
width: 150, width: 150,
ellipsis: true ellipsis: true
}, },
{ {
title: '充值金额', title: '交易渠道',
dataIndex: 'topUpAmount', dataIndex: 'transactionAmount',
key: 'topUpAmount', key: 'transactionAmount',
width: 120, width: 120,
align: 'left' align: 'right'
}, },
{
title: '交易金额',
dataIndex: 'transactionAmount',
key: 'transactionAmount',
width: 120,
align: 'right'
},
{
title: '账户余额',
dataIndex: 'balancePayment',
key: 'balancePayment',
width: 120,
align: 'right'
},
{
title: '备注',
dataIndex: 'voucherDeduction',
key: 'voucherDeduction',
width: 120,
align: 'right'
}
]) ])
// //
@ -222,13 +245,94 @@ const handleTableChange: TableProps['onChange'] = (pag, filters, sorter) => {
// //
const fetchBillData = () => { const fetchBillData = () => {
loading.value = true loading.value = true
// API // API
setTimeout(() => {
//
const mockData: BillRecord[] = [
{
key: '1',
serialNumber: 'TX202401010001',
transactionTime: '2024-01-01 10:30:25',
transactionType: '充值',
productName: '账户充值',
transactionAmount: 100.00,
originalPrice: 100.00,
discountAmount: 0.00,
balancePayment: 0.00,
voucherDeduction: 0.00
},
{
key: '2',
serialNumber: 'TX202401010002',
transactionTime: '2024-01-01 14:20:15',
transactionType: '消费',
productName: 'GPU算力服务',
transactionAmount: -50.00,
originalPrice: 60.00,
discountAmount: 10.00,
balancePayment: 40.00,
voucherDeduction: 10.00
},
{
key: '3',
serialNumber: 'TX202401020001',
transactionTime: '2024-01-02 09:15:30',
transactionType: '充值',
productName: '账户充值',
transactionAmount: 200.00,
originalPrice: 200.00,
discountAmount: 0.00,
balancePayment: 0.00,
voucherDeduction: 0.00
},
{
key: '4',
serialNumber: 'TX202401030001',
transactionTime: '2024-01-03 16:45:20',
transactionType: '消费',
productName: '存储空间',
transactionAmount: -30.00,
originalPrice: 30.00,
discountAmount: 0.00,
balancePayment: 30.00,
voucherDeduction: 0.00
},
{
key: '5',
serialNumber: 'TX202401040001',
transactionTime: '2024-01-04 11:10:05',
transactionType: '提现',
productName: '余额提现',
transactionAmount: -100.00,
originalPrice: 100.00,
discountAmount: 0.00,
balancePayment: 100.00,
voucherDeduction: 0.00
},
{
key: '6',
serialNumber: 'TX202401050001',
transactionTime: '2024-01-05 15:30:40',
transactionType: '退款',
productName: 'GPU算力服务',
transactionAmount: 25.00,
originalPrice: 25.00,
discountAmount: 0.00,
balancePayment: 0.00,
voucherDeduction: 0.00
}
]
billData.value = mockData
pagination.value.total = mockData.length
loading.value = false
}, 500)
} }
// //
const goToRecharge = () => { const goToRecharge = () => {
router.push('/layout/admin/balance') router.push('/recharge')
} }
const goToBills = () => { const goToBills = () => {
@ -244,27 +348,28 @@ const goToExchange = () => {
// //
router.push('/layout/admin/exchange') router.push('/layout/admin/exchange')
} }
const handleOk = async () => { const handleOk=async ()=>{
try { try {
btnLoading.value = true btnLoading.value=true
const res = await tixian({ amount: withDrawalCount.value }) const res=await tixian({amount:withDrawalCount.value})
if (res) { if(res){
message.success("提现成功") message.success("提现成功")
const userRes = await fetchUserInfo(); const userRes = await fetchUserInfo();
localStorage.setItem('userInfo', JSON.stringify(userRes)); localStorage.setItem('userInfo', JSON.stringify(userRes));
} }
btnLoading.value = false btnLoading.value=false
} catch (error: any) { } catch (error:any) {
message.error(error) message.error(error)
btnLoading.value = false btnLoading.value=false
} }
} }
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.home-page { .home-page {
padding: 20px; padding: 24px;
background: #ffffff; background: #f0f2f5;
min-height: 100vh;
} }
.asset-cards { .asset-cards {
@ -279,7 +384,7 @@ const handleOk = async () => {
.card { .card {
border-radius: 8px; border-radius: 8px;
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.08);
background: #fff; background: #fff;
border: 1px solid #e8e8e8; border: 1px solid #e8e8e8;
height: 100%; height: 100%;

View File

@ -1,5 +1,4 @@
<template> <template>
<div style="margin: 20px;">
<a-row :gutter="[24, 18]"> <a-row :gutter="[24, 18]">
<a-col :span="24"> <a-col :span="24">
<a-card> <a-card>
@ -328,7 +327,6 @@
</a-form-item> </a-form-item>
</a-form> </a-form>
</a-modal> </a-modal>
</div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">

View File

@ -1,72 +1,97 @@
<template> <template>
<div style="margin: 20px;"> <a-card title="我的消息">
<a-card title="我的消息"> <a-tabs v-model:activeKey="activeKey">
<a-tabs v-model:activeKey="activeKey"> <!-- 使用customRender来渲染带徽章的标签 -->
<!-- 使用customRender来渲染带徽章的标签 --> <a-tab-pane key="all">
<a-tab-pane key="all"> <template #tab>
<template #tab> <span class="tab-label">
<span class="tab-label"> 全部
全部 <a-badge
<a-badge v-if="allUnreadCount > 0" :count="allUnreadCount" class="tab-badge" :offset="[5, -2]" /> v-if="allUnreadCount > 0"
</span> :count="allUnreadCount"
</template> class="tab-badge"
</a-tab-pane> :offset="[5, -2]"
/>
<a-tab-pane key="money"> </span>
<template #tab>
<span class="tab-label">
资金消息
<a-badge v-if="moneyUnreadCount > 0" :count="moneyUnreadCount" class="tab-badge" :offset="[5, -2]" />
</span>
</template>
</a-tab-pane>
<a-tab-pane key="secure">
<template #tab>
<span class="tab-label">
安全消息
<a-badge v-if="secureUnreadCount > 0" :count="secureUnreadCount" class="tab-badge" :offset="[5, -2]" />
</span>
</template>
</a-tab-pane>
<a-tab-pane key="active">
<template #tab>
<span class="tab-label">
活动消息
<a-badge v-if="activeUnreadCount > 0" :count="activeUnreadCount" class="tab-badge" :offset="[5, -2]" />
</span>
</template>
</a-tab-pane>
</a-tabs>
<a-table :columns="columns" :data-source="billData" :pagination="pagination" @change="handleTableChange"
:loading="loading" class="bill-table" :scroll="{ x: 1200 }" bordered>
<!-- 消息类型列自定义渲染 -->
<template #bodyCell="{ column, record }">
<template v-if="column.dataIndex === 'messageType'">
<span :class="{ 'unread-title': !record.is_read }">
{{ getMessageTypeText(record.messageType) }}
</span>
</template>
<template v-else-if="column.dataIndex === 'title'">
<span :class="{ 'unread-title': !record.is_read, 'read-title': record.is_read }">
{{ record.title }}
</span>
</template>
<template v-else-if="column.dataIndex === 'is_read'">
<a-tag :class="record.is_read ? 'status-read' : 'status-unread'">
{{ record.is_read ? '已读' : '未读' }}
</a-tag>
</template>
</template> </template>
</a-tab-pane>
</a-table>
</a-card> <a-tab-pane key="money">
</div> <template #tab>
<span class="tab-label">
资金消息
<a-badge
v-if="moneyUnreadCount > 0"
:count="moneyUnreadCount"
class="tab-badge"
:offset="[5, -2]"
/>
</span>
</template>
</a-tab-pane>
<a-tab-pane key="secure">
<template #tab>
<span class="tab-label">
安全消息
<a-badge
v-if="secureUnreadCount > 0"
:count="secureUnreadCount"
class="tab-badge"
:offset="[5, -2]"
/>
</span>
</template>
</a-tab-pane>
<a-tab-pane key="active">
<template #tab>
<span class="tab-label">
活动消息
<a-badge
v-if="activeUnreadCount > 0"
:count="activeUnreadCount"
class="tab-badge"
:offset="[5, -2]"
/>
</span>
</template>
</a-tab-pane>
</a-tabs>
<a-table
:columns="columns"
:data-source="billData"
:pagination="pagination"
@change="handleTableChange"
:loading="loading"
class="bill-table"
:scroll="{ x: 1200 }"
bordered>
<!-- 消息类型列自定义渲染 -->
<template #bodyCell="{ column, record }">
<template v-if="column.dataIndex === 'messageType'">
<span :class="{ 'unread-title': !record.is_read }">
{{ getMessageTypeText(record.messageType) }}
</span>
</template>
<template v-else-if="column.dataIndex === 'title'">
<span :class="{ 'unread-title': !record.is_read, 'read-title': record.is_read }">
{{ record.title }}
</span>
</template>
<template v-else-if="column.dataIndex === 'is_read'">
<a-tag :class="record.is_read ? 'status-read' : 'status-unread'">
{{ record.is_read ? '已读' : '未读' }}
</a-tag>
</template>
</template>
</a-table>
</a-card>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
@ -115,19 +140,19 @@ const allUnreadCount = computed(() => {
}); });
const moneyUnreadCount = computed(() => { const moneyUnreadCount = computed(() => {
return allMessages.value.filter(msg => return allMessages.value.filter(msg =>
msg.messageType === 'FinancialMessage' && !msg.is_read msg.messageType === 'FinancialMessage' && !msg.is_read
).length; ).length;
}); });
const secureUnreadCount = computed(() => { const secureUnreadCount = computed(() => {
return allMessages.value.filter(msg => return allMessages.value.filter(msg =>
msg.messageType === 'SecurityMessage' && !msg.is_read msg.messageType === 'SecurityMessage' && !msg.is_read
).length; ).length;
}); });
const activeUnreadCount = computed(() => { const activeUnreadCount = computed(() => {
return allMessages.value.filter(msg => return allMessages.value.filter(msg =>
msg.messageType === 'ActivityMessage' && !msg.is_read msg.messageType === 'ActivityMessage' && !msg.is_read
).length; ).length;
}); });
@ -187,7 +212,7 @@ const getAllMessages = async () => {
}; };
const response: any = await getMessageList(params); const response: any = await getMessageList(params);
if (response.data && Array.isArray(response.data)) { if (response.data && Array.isArray(response.data)) {
allMessages.value = response.data; allMessages.value = response.data;
} else if (response.data?.messages) { } else if (response.data?.messages) {
@ -197,7 +222,7 @@ const getAllMessages = async () => {
} else { } else {
allMessages.value = []; allMessages.value = [];
} }
console.log('全部消息统计:', { console.log('全部消息统计:', {
total: allMessages.value.length, total: allMessages.value.length,
allUnread: allUnreadCount.value, allUnread: allUnreadCount.value,
@ -237,13 +262,13 @@ const getDataList = async () => {
billData.value = []; billData.value = [];
pagination.value.total = 0; pagination.value.total = 0;
} }
console.log('当前标签页消息:', { console.log('当前标签页消息:', {
type: messageType.value, type: messageType.value,
count: billData.value.length, count: billData.value.length,
data: billData.value data: billData.value
}); });
} catch (error) { } catch (error) {
console.error('获取消息列表失败:', error); console.error('获取消息列表失败:', error);
billData.value = []; billData.value = [];

View File

@ -1,26 +1,24 @@
<template> <template>
<div style="margin: 20px;"> <a-card title="算力券管理">
<a-card title="算力券管理"> <div style="width: 300px;border: 1px solid #e8e8e8;line-height: 45px;border-radius: 5px;">
<div style="width: 300px;border: 1px solid #e8e8e8;line-height: 45px;border-radius: 5px;"> <div
<div style="background: linear-gradient(90deg, rgba(240, 245, 255, 1) 0%, rgba(255, 255, 255, 0) 100%);height: 45px;line-height: 45px;padding: 0 10px;">
style="background: linear-gradient(90deg, rgba(240, 245, 255, 1) 0%, rgba(255, 255, 255, 0) 100%);height: 45px;line-height: 45px;padding: 0 10px;"> 可用算力券</div>
可用算力券</div> <div style="font-size:18px;font-weight: bold;padding: 0 10px;">{{ couponTotal }}</div>
<div style="font-size:18px;font-weight: bold;padding: 0 10px;">{{ couponTotal }}</div> </div>
</div> <!-- 账单表格 -->
<!-- 账单表格 --> <div style="margin-top: 20px;">
<div style="margin-top: 20px;"> <a-table :columns="columns" :data-source="billData" :pagination="pagination" @change="handleTableChange"
<a-table :columns="columns" :data-source="billData" :pagination="pagination" @change="handleTableChange" :loading="loading" class="bill-table" :scroll="{ x: 1200 }" bordered>
:loading="loading" class="bill-table" :scroll="{ x: 1200 }" bordered> <template #bodyCell="{ column, record }">
<template #bodyCell="{ column, record }"> <template v-if="column.key === 'status'">
<template v-if="column.key === 'status'"> <a-tag v-if="record.status === 'enabled'" color="green">有效</a-tag>
<a-tag v-if="record.status === 'enabled'" color="green">有效</a-tag> <a-tag v-else color="red">失效</a-tag>
<a-tag v-else color="red">失效</a-tag>
</template>
</template> </template>
</a-table> </template>
</div> </a-table>
</a-card> </div>
</div> </a-card>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { ref, onBeforeMount, h } from 'vue'; import { ref, onBeforeMount, h } from 'vue';

View File

@ -1,53 +1,42 @@
<template> <template>
<div style="margin: 20px;"> <a-card title="我的邀请">
<a-card title="我的邀请"> <div style="border-radius: 5px;display: flex;justify-content: space-between;border: 1px solid #e8e8e8;">
<div style="border-radius: 5px;display: flex;justify-content: space-between;border: 1px solid #e8e8e8;"> <div>
<div> <div style="font-size:18px;padding: 0 20px;margin-top: 20px;">累计奖金</div>
<div style="font-size:18px;padding: 0 20px;margin-top: 20px;">累计奖金</div> <div style="font-size:18px;font-weight: bold;padding: 0 20px;margin: 20px 0;">0.00</div>
<div style="font-size:18px;font-weight: bold;padding: 0 20px;margin: 20px 0;">0.00</div> <div style="display: flex;justify-content: space-between;align-items: center;gap: 60px;padding: 0 20px;line-height: 30px;">
<div <div>
style="display: flex;justify-content: space-between;align-items: center;gap: 60px;padding: 0 20px;line-height: 30px;"> <div>方式一复制邀请链接</div>
<div> <div><span>http://example.com/invite/123456</span>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color:#1677ff;"><CopyOutlined /> </span></div>
<div>方式一复制邀请链接</div>
<div><span>http://example.com/invite/123456</span>&nbsp;&nbsp;&nbsp;&nbsp;<span
style="color:#1677ff;">
<CopyOutlined />
</span></div>
</div>
<div>
<div>方式二复制邀请码</div>
<div><span>http://example.com/invite/123456</span>&nbsp;&nbsp;&nbsp;&nbsp;<span
style="color:#1677ff;">
<CopyOutlined />
</span></div>
</div>
</div> </div>
</div> <div>
<div class="leftImg"> <div>方式二复制邀请码</div>
<div style="font-size:14px;padding: 0 20px;margin-top: 10px;text-align: right;color: #1677ff;">活动规则 <div><span>http://example.com/invite/123456</span>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color:#1677ff;"><CopyOutlined /> </span></div>
</div> </div>
</div> </div>
</div> </div>
<div <div class="leftImg">
style="width: 100%;display: flex;justify-content: space-around;align-items: center;height: 150px;border: 1px solid #e8e8e8;margin-top: 20px;border-radius: 5px;"> <div style="font-size:14px;padding: 0 20px;margin-top: 10px;text-align: right;color: #1677ff;">活动规则</div>
<div style="display: flex;flex-direction: column;align-items: center;line-height: 35px;"> </div>
<div>累计成功邀请人数</div> </div>
<div><span style="font-weight: bold;font-size: 18px;">0</span> </div> <div style="width: 100%;display: flex;justify-content: space-around;align-items: center;height: 150px;border: 1px solid #e8e8e8;margin-top: 20px;border-radius: 5px;">
</div> <div style="display: flex;flex-direction: column;align-items: center;line-height: 35px;">
<div style="display: flex;flex-direction: column;align-items: center;line-height: 35px;"> <div>累计成功邀请人数</div>
<div>累计获得奖金</div> <div ><span style="font-weight: bold;font-size: 18px;">0</span> </div>
<div style="font-weight: bold;"><span style="font-weight: bold;font-size: 18px;">0.00</span></div>
</div>
</div> </div>
<!-- 账单表格 --> <div style="display: flex;flex-direction: column;align-items: center;line-height: 35px;">
<div style="margin-top: 20px;"> <div>累计获得奖金</div>
<div style="margin: 30px 0px 10px 0px;font-weight: bold;">邀请记录</div> <div style="font-weight: bold;"><span style="font-weight: bold;font-size: 18px;">0.00</span></div>
<a-table :columns="columns" :data-source="billData" :pagination="pagination" @change="handleTableChange"
:loading="loading" class="bill-table" :scroll="{ x: 1200 }" bordered>
</a-table>
</div> </div>
</a-card> </div>
</div> <!-- 账单表格 -->
<div style="margin-top: 20px;">
<div style="margin: 30px 0px 10px 0px;font-weight: bold;">邀请记录</div>
<a-table :columns="columns" :data-source="billData" :pagination="pagination" @change="handleTableChange"
:loading="loading" class="bill-table" :scroll="{ x: 1200 }" bordered>
</a-table>
</div>
</a-card>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { ref } from 'vue'; import { ref } from 'vue';

View File

@ -18,14 +18,14 @@
<div> <div>
<p><span>认证类型</span>&nbsp;&nbsp;&nbsp;&nbsp;<span>{{ certificationInfo.certificationType }}</span></p> <p><span>认证类型</span>&nbsp;&nbsp;&nbsp;&nbsp;<span>{{ certificationInfo.certificationType }}</span></p>
<p><span>证件类型</span>&nbsp;&nbsp;&nbsp;&nbsp;<span></span><span>{{ certificationInfo.documentType }}</span></p> <p><span>证件类型</span>&nbsp;&nbsp;&nbsp;&nbsp;<span></span><span>{{ certificationInfo.documentType }}</span></p>
<p><span>认证人</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span></span><span>{{ certificationInfo.name }}</span></p> <p><span>认证人</span>&nbsp;&nbsp;&nbsp;&nbsp;<span></span><span>{{ certificationInfo.name }}</span></p>
<p> <p>
<a-button type="primary" v-if="certificationInfo.certificationType==='个人认证'" @click="transOpen=true">变更为企业认证</a-button> <a-button type="primary" v-if="certificationInfo.certificationType==='个人认证'" @click="transOpen=true">变更为企业认证</a-button>
</p> </p>
</div> </div>
<div> <div>
<p><span>认证时间</span>&nbsp;&nbsp;&nbsp;&nbsp;<span>{{ certificationInfo.certificationTime }}</span></p> <p><span>认证时间</span><span>{{ certificationInfo.certificationTime }}</span></p>
<p><span>身份证号</span>&nbsp;&nbsp;&nbsp;&nbsp;<span>{{ certificationInfo.idCard }}</span></p> <p><span>身份证号</span><span>{{ certificationInfo.idCard }}</span></p>
<!-- <p><span>认证人</span><span>用户名</span></p> --> <!-- <p><span>认证人</span><span>用户名</span></p> -->
</div> </div>
<div> <div>
@ -163,8 +163,8 @@ onMounted(() => {
<style scoped> <style scoped>
.real-name-auth-page { .real-name-auth-page {
padding: 20px; padding: 24px;
background-color: #ffffff; background-color: #f5f7fa;
/* min-height: 100vh; */ /* min-height: 100vh; */
} }

View File

@ -5,7 +5,7 @@
<!-- 左侧栏实例数据邀请好友 --> <!-- 左侧栏实例数据邀请好友 -->
<a-col :span="16"> <a-col :span="16">
<!-- 实例卡片 --> <!-- 实例卡片 -->
<a-card title="GPU容器实例" class="card"> <a-card title="GPU容器实例" :bordered="false" class="card">
<a-row :gutter="32" align="middle"> <a-row :gutter="32" align="middle">
<!-- 第一栏容器实例和运行中 --> <!-- 第一栏容器实例和运行中 -->
<a-col :span="18"> <a-col :span="18">
@ -43,7 +43,7 @@
<!-- 邀请好友卡片 --> <!-- 邀请好友卡片 -->
<a-card title="邀请好友得算力券" class="card margin-top"> <a-card title="邀请好友得算力券" :bordered="false" class="card margin-top">
<a-row :gutter="24" class="invite-container"> <a-row :gutter="24" class="invite-container">
<!-- 左侧邀请信息和链接 --> <!-- 左侧邀请信息和链接 -->
<a-col :span="24"> <a-col :span="24">
@ -95,7 +95,7 @@
<!-- 右侧栏用户信息 + 费用信息 --> <!-- 右侧栏用户信息 + 费用信息 -->
<a-col :span="8"> <a-col :span="8">
<!-- 费用信息卡片 --> <!-- 费用信息卡片 -->
<a-card class="card "> <a-card :bordered="false" class="card ">
<div class="fee-header"> <div class="fee-header">
<div class="fee-title">我的账号</div> <div class="fee-title">我的账号</div>
</div> </div>
@ -174,10 +174,10 @@
</div> </div>
</div> </div>
</div> </div>
<!-- <a-button type="link" class="view-coupons-btn" @click="goToCoupons"> <a-button type="link" class="view-coupons-btn" @click="goToCoupons">
查看全部权益 查看全部权益
<RightOutlined style="font-size: 10px; margin-left: 2px;" /> <RightOutlined style="font-size: 10px; margin-left: 2px;" />
</a-button> --> </a-button>
</div> </div>
</div> </div>
@ -276,7 +276,7 @@ const goToRules = () => {
// //
const goToRecharge = () => { const goToRecharge = () => {
router.push('/layout/admin/balance') router.push('/recharge')
} }
// //
@ -302,11 +302,16 @@ const goToInvoice = () => {
<style lang="scss" scoped> <style lang="scss" scoped>
.home-page { .home-page {
margin: 20px; padding: 15px;
background: #fff; background: #fff;
} }
.card {
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
background: #fff;
margin-bottom: 20px;
}
.margin-top { .margin-top {
margin-top: 20px; margin-top: 20px;

View File

@ -294,7 +294,7 @@ onMounted(() => {
.image-table { .image-table {
background: white; background: white;
// box-shadow: 0 1px 4px rgba(0, 0, 0, 0.1); box-shadow: 0 1px 4px rgba(0, 0, 0, 0.1);
border-radius: 4px; border-radius: 4px;
overflow: hidden; overflow: hidden;
height: 100%; height: 100%;
@ -311,7 +311,7 @@ onMounted(() => {
margin-top: auto; margin-top: auto;
padding-top: 24px; padding-top: 24px;
text-align: right; text-align: right;
// border-top: 1px solid #e8e8e8; border-top: 1px solid #e8e8e8;
} }
} }

View File

@ -25,7 +25,7 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { h, reactive, computed,watch } from 'vue'; import { h, reactive, computed } from 'vue';
import { useRouter } from 'vue-router'; import { useRouter } from 'vue-router';
import { import {
HomeOutlined, HomeOutlined,
@ -57,8 +57,8 @@ const menuItems: MenuItem[] = [
// { path: '/layout/admin/home', name: '', icon: HomeOutlined, visible: true }, // { path: '/layout/admin/home', name: '', icon: HomeOutlined, visible: true },
{ {
path: '/fee', path: '',
name: '费用中心', name: '费用',
icon: MoneyCollectOutlined, icon: MoneyCollectOutlined,
visible: true, visible: true,
children: [ children: [
@ -78,13 +78,13 @@ const menuItems: MenuItem[] = [
], ],
}, },
{ {
path: '/account', path: '',
name: '我的账号', name: '账号',
icon: TeamOutlined, icon: TeamOutlined,
visible: true, visible: true,
children: [ children: [
// { path: '/layout/admin/security', name: '', visible: true }, { path: '/layout/admin/security', name: '账号安全', visible: true },
// { path: '/layout/admin/history', name: '访', visible: true }, { path: '/layout/admin/history', name: '访问记录', visible: true },
{ path: '/layout/admin/accountSet', name: '账户设置', visible: true }, { path: '/layout/admin/accountSet', name: '账户设置', visible: true },
{ path: '/layout/admin/realnameAuth', name: '实名认证', visible: true }, { path: '/layout/admin/realnameAuth', name: '实名认证', visible: true },
{ path: '/layout/admin/myCertificate', name: '我的算力券', visible: true }, { path: '/layout/admin/myCertificate', name: '我的算力券', visible: true },
@ -184,28 +184,8 @@ const items = computed(() => {
}) })
.filter(Boolean) as MenuItemType[]; .filter(Boolean) as MenuItemType[];
}); });
watch(
() => router.currentRoute.value.path,
(currentPath) => {
//
state.selectedKeys = [currentPath];
//
const openKeys: string[] = [];
menuItems.forEach((item) => {
if (item.children && item.children.some(child => child.path === currentPath)) {
openKeys.push(item.path || ''); // key path key
}
});
state.openKeys = openKeys;
},
{ immediate: true } //
);
const handleMenuSelect = ({ key }: { key: string }) => { const handleMenuSelect = ({ key }: { key: string }) => {
//
if (key === '/fee' || key === '/account') {
return;
}
// //
const allItems = flattenMenuItems(menuItems); const allItems = flattenMenuItems(menuItems);
const targetItem = allItems.find(item => item.path === key); const targetItem = allItems.find(item => item.path === key);
@ -259,8 +239,8 @@ const changeTheme = (checked: boolean) => {
.admin-contain { .admin-contain {
flex: 1; flex: 1;
/* padding: 20px; */ padding: 20px;
overflow-y: auto; overflow-y: auto;
background-color: #ffffff; background-color: #f0f2f5;
} }
</style> </style>

View File

@ -389,9 +389,9 @@ onMounted(() => {
<style scoped lang="scss"> <style scoped lang="scss">
.instance-list { .instance-list {
padding: 20px; padding: 24px;
background: #ffffff; background: #f5f7fa;
// min-height: calc(100vh - 48px); min-height: calc(100vh - 48px);
} }
.header-section { .header-section {