feat: 增加用户充值记录查询

This commit is contained in:
Gsh
2025-07-08 22:59:24 +08:00
parent 0d2bc585a9
commit ca72024a68
5 changed files with 331 additions and 4 deletions

View File

@@ -95,8 +95,8 @@ function handleConfirm() {
<template #footer> <template #footer>
<span class="dialog-footer"> <span class="dialog-footer">
<el-button @click="handleClose">取消</el-button> <el-button type="primary" @click="handleClose">关闭</el-button>
<el-button type="primary" @click="handleConfirm"></el-button> <el-button v-if="false" type="primary" @click="handleConfirm"></el-button>
</span> </span>
</template> </template>
</el-dialog> </el-dialog>

View File

@@ -3,7 +3,7 @@ import { CircleCheck } from '@element-plus/icons-vue';
import { ElMessage, ElMessageBox } from 'element-plus'; import { ElMessage, ElMessageBox } from 'element-plus';
import { onMounted, ref } from 'vue'; import { onMounted, ref } from 'vue';
import { useRouter } from 'vue-router'; import { useRouter } from 'vue-router';
import { applyApiKey, getApiKey } from '@/api/model/index.ts'; import { applyApiKey, getApiKey, getRechargeLog } from '@/api/model/index.ts';
import { isUserVip } from '@/utils/user'; import { isUserVip } from '@/utils/user';
const apiKey = ref(''); const apiKey = ref('');
@@ -20,6 +20,8 @@ const router = useRouter();
async function fetchApiKey() { async function fetchApiKey() {
try { try {
const res = await getApiKey(); const res = await getApiKey();
const res2 = await getRechargeLog();
console.log('re2', res2);
if (res.data?.apiKey) { if (res.data?.apiKey) {
apiKey.value = res.data.apiKey; apiKey.value = res.data.apiKey;
displayedKey.value = res.data.apiKey; displayedKey.value = res.data.apiKey;

View File

@@ -0,0 +1,317 @@
<script lang="ts" setup>
import { List, Refresh, Search } from '@element-plus/icons-vue';
import { computed, onMounted, ref } from 'vue';
import { getRechargeLog } from '@/api/model/index.ts';
interface RechargeLog {
id: string;
content: string;
rechargeAmount: number;
creationTime: string;
expireDateTime: string;
contactInfo: string;
remark: string;
userId: string;
}
const loading = ref(false);
const logData = ref<RechargeLog[]>([]);
const searchText = ref('');
const currentSort = ref({ prop: '', order: '' });
const currentPage = ref(1);
const pageSize = ref(10);
// 模拟数据获取
async function fetchRechargeLog() {
try {
loading.value = true;
const res = await getRechargeLog();
logData.value = res.data || [];
}
catch (error) {
console.error('获取充值记录失败:', error);
ElMessage.error('获取充值记录失败');
}
finally {
loading.value = false;
}
}
// 排序处理
function handleSortChange({ prop, order }: { prop: string; order: string }) {
currentSort.value = { prop, order };
}
// 搜索处理
function handleSearch() {
currentPage.value = 1;
}
function handleSearchClear() {
searchText.value = '';
currentPage.value = 1;
}
// 刷新数据
function refreshLog() {
fetchRechargeLog();
currentPage.value = 1;
searchText.value = '';
currentSort.value = { prop: '', order: '' };
}
// 过滤和排序后的数据
const filteredData = computed(() => {
let data = [...logData.value];
// 搜索过滤
if (searchText.value) {
const search = searchText.value.toLowerCase();
data = data.filter(item =>
(item.contactInfo && item.contactInfo.toLowerCase().includes(search))
|| (item.remark && item.remark.toLowerCase().includes(search)),
);
}
// 排序处理
if (currentSort.value.prop) {
const { prop, order } = currentSort.value;
data.sort((a, b) => {
// 处理可能为null的值
const valA = a[prop as keyof RechargeLog] || '';
const valB = b[prop as keyof RechargeLog] || '';
if (order === 'ascending') {
return valA > valB ? 1 : valA < valB ? -1 : 0;
}
else {
return valA < valB ? 1 : valA > valB ? -1 : 0;
}
});
}
// 分页处理
// if (showPagination.value) {
// const start = (currentPage.value - 1) * pageSize.value;
// return data.slice(start, start + pageSize.value);
// }
return data;
});
// 是否显示分页 暂时不需要分页功能
const showPagination = computed(() => {
// return logData.value.length > 10;
return false;
});
onMounted(() => {
fetchRechargeLog();
});
</script>
<template>
<div class="recharge-log-container">
<div class="log-header">
<h2 class="log-title">
<el-icon><List /></el-icon>
充值记录
</h2>
<div class="header-actions">
<el-tooltip content="刷新数据" placement="top">
<el-button circle :loading="loading" @click="refreshLog">
<el-icon><Refresh /></el-icon>
</el-button>
</el-tooltip>
<el-input
v-model="searchText"
placeholder="搜索联系方式/备注"
clearable
style="width: 200px; margin-left: 10px;"
@clear="handleSearchClear"
@keyup.enter="handleSearch"
>
<template #append>
<el-button @click="handleSearch">
<el-icon><Search /></el-icon>
</el-button>
</template>
</el-input>
</div>
</div>
<el-table
v-loading="loading"
:data="filteredData"
style="width: 100%"
border
stripe
highlight-current-row
@sort-change="handleSortChange"
>
<el-table-column
prop="content"
label="套餐类型"
width="120"
sortable="custom"
/>
<el-table-column
prop="rechargeAmount"
label="金额(元)"
width="120"
align="right"
sortable="custom"
>
<template #default="{ row }">
<span class="amount-cell">¥{{ row.rechargeAmount.toFixed(2) }}</span>
</template>
</el-table-column>
<el-table-column
prop="creationTime"
label="充值时间"
width="180"
sortable="custom"
/>
<el-table-column
prop="expireDateTime"
label="到期时间"
width="180"
sortable="custom"
/>
<el-table-column prop="contactInfo" width="100" label="联系方式">
<template #default="{ row }">
<el-tooltip v-if="row.contactInfo && row.contactInfo.length > 8" :content="row.contactInfo" placement="top">
<span class="ellipsis-text">{{ row.contactInfo }}</span>
</el-tooltip>
<span v-else>{{ row.contactInfo || '-' }}</span>
</template>
</el-table-column>
<el-table-column prop="remark" label="备注">
<template #default="{ row }">
<el-tooltip v-if="row.remark && row.remark.length > 10" :content="row.remark" placement="top">
<span class="ellipsis-text">{{ row.remark }}</span>
</el-tooltip>
<span v-else>{{ row.remark || '-' }}</span>
</template>
</el-table-column>
</el-table>
<div class="log-footer">
<div class="summary">
<span class="highlight">{{ filteredData.length }}</span> 条记录
</div>
<el-pagination
v-if="showPagination"
v-model:current-page="currentPage"
v-model:page-size="pageSize"
:total="filteredData.length"
:page-sizes="[10, 20, 50, 100]"
layout="total, sizes, prev, pager, next, jumper"
background
/>
</div>
</div>
</template>
<style scoped>
.recharge-log-container {
padding: 20px;
background: #fff;
border-radius: 8px;
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.08);
}
.log-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
padding-bottom: 15px;
border-bottom: 1px solid #f0f0f0;
}
.log-title {
display: flex;
align-items: center;
margin: 0;
font-size: 18px;
color: #333;
}
.log-title .el-icon {
margin-right: 8px;
color: #409eff;
}
.header-actions {
display: flex;
align-items: center;
}
.amount-cell {
font-family: 'Arial', sans-serif;
font-weight: bold;
color: #e74c3c;
}
.ellipsis-text {
display: inline-block;
max-width: 120px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
vertical-align: middle;
}
.log-footer {
display: flex;
justify-content: space-between;
align-items: center;
margin-top: 20px;
padding-top: 15px;
border-top: 1px solid #f0f0f0;
}
.summary {
color: #666;
font-size: 14px;
}
.highlight {
color: #409eff;
font-weight: bold;
}
/* 表格样式优化 */
:deep(.el-table) {
font-size: 14px;
}
:deep(.el-table th) {
background-color: #f8fafc;
color: #333;
font-weight: 600;
}
:deep(.el-table--striped .el-table__body tr.el-table__row--striped td) {
background-color: #fafafa;
}
:deep(.el-table .cell) {
padding: 8px 12px;
}
/* 分页样式优化 */
:deep(.el-pagination) {
padding: 0;
}
:deep(.el-pagination.is-background .el-pager li:not(.is-disabled).is-active) {
background-color: #409eff;
color: #fff;
}
:deep(.el-pagination.is-background .el-pager li:not(.is-disabled):hover) {
color: #409eff;
}
</style>

View File

@@ -65,6 +65,7 @@ const navItems = [
// { name: 'role', label: '角色管理', icon: 'Avatar' }, // { name: 'role', label: '角色管理', icon: 'Avatar' },
// { name: 'permission', label: '权限管理', icon: 'Key' }, // { name: 'permission', label: '权限管理', icon: 'Key' },
{ name: 'apiKey', label: 'API密钥', icon: 'Key' }, { name: 'apiKey', label: 'API密钥', icon: 'Key' },
{ name: 'rechargeLog', label: '充值记录', icon: 'Document' },
]; ];
function openDialog() { function openDialog() {
dialogVisible.value = true; dialogVisible.value = true;
@@ -266,7 +267,7 @@ function openVipGuide() {
<!-- 角色管理内容 --> <!-- 角色管理内容 -->
<template #role> <template #role>
<!-- <role-management /> --> <!-- < /> -->
</template> </template>
<!-- 权限管理内容 --> <!-- 权限管理内容 -->
@@ -277,6 +278,9 @@ function openVipGuide() {
<template #apiKey> <template #apiKey>
<APIKeyManagement /> <APIKeyManagement />
</template> </template>
<template #rechargeLog>
<recharge-log />
</template>
</nav-dialog> </nav-dialog>
</div> </div>
</template> </template>

View File

@@ -19,6 +19,8 @@ declare module 'vue' {
ElCollapse: typeof import('element-plus/es')['ElCollapse'] ElCollapse: typeof import('element-plus/es')['ElCollapse']
ElCollapseItem: typeof import('element-plus/es')['ElCollapseItem'] ElCollapseItem: typeof import('element-plus/es')['ElCollapseItem']
ElContainer: typeof import('element-plus/es')['ElContainer'] ElContainer: typeof import('element-plus/es')['ElContainer']
ElDescriptions: typeof import('element-plus/es')['ElDescriptions']
ElDescriptionsItem: typeof import('element-plus/es')['ElDescriptionsItem']
ElDialog: typeof import('element-plus/es')['ElDialog'] ElDialog: typeof import('element-plus/es')['ElDialog']
ElDivider: typeof import('element-plus/es')['ElDivider'] ElDivider: typeof import('element-plus/es')['ElDivider']
ElEmpty: typeof import('element-plus/es')['ElEmpty'] ElEmpty: typeof import('element-plus/es')['ElEmpty']
@@ -31,6 +33,7 @@ declare module 'vue' {
ElMain: typeof import('element-plus/es')['ElMain'] ElMain: typeof import('element-plus/es')['ElMain']
ElMenu: typeof import('element-plus/es')['ElMenu'] ElMenu: typeof import('element-plus/es')['ElMenu']
ElMenuItem: typeof import('element-plus/es')['ElMenuItem'] ElMenuItem: typeof import('element-plus/es')['ElMenuItem']
ElPagination: typeof import('element-plus/es')['ElPagination']
ElTable: typeof import('element-plus/es')['ElTable'] ElTable: typeof import('element-plus/es')['ElTable']
ElTableColumn: typeof import('element-plus/es')['ElTableColumn'] ElTableColumn: typeof import('element-plus/es')['ElTableColumn']
ElTooltip: typeof import('element-plus/es')['ElTooltip'] ElTooltip: typeof import('element-plus/es')['ElTooltip']
@@ -43,6 +46,7 @@ declare module 'vue' {
NavDialog: typeof import('./../src/components/userPersonalCenter/NavDialog.vue')['default'] NavDialog: typeof import('./../src/components/userPersonalCenter/NavDialog.vue')['default']
Popover: typeof import('./../src/components/Popover/index.vue')['default'] Popover: typeof import('./../src/components/Popover/index.vue')['default']
QrCodeLogin: typeof import('./../src/components/LoginDialog/components/QrCodeLogin/index.vue')['default'] QrCodeLogin: typeof import('./../src/components/LoginDialog/components/QrCodeLogin/index.vue')['default']
RechargeLog: typeof import('./../src/components/userPersonalCenter/components/RechargeLog.vue')['default']
RegistrationForm: typeof import('./../src/components/LoginDialog/components/FormLogin/RegistrationForm.vue')['default'] RegistrationForm: typeof import('./../src/components/LoginDialog/components/FormLogin/RegistrationForm.vue')['default']
RouterLink: typeof import('vue-router')['RouterLink'] RouterLink: typeof import('vue-router')['RouterLink']
RouterView: typeof import('vue-router')['RouterView'] RouterView: typeof import('vue-router')['RouterView']