feat: 增加用户充值记录查询
This commit is contained in:
@@ -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>
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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>
|
||||||
@@ -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>
|
||||||
|
|||||||
4
Yi.Ai.Vue3/types/components.d.ts
vendored
4
Yi.Ai.Vue3/types/components.d.ts
vendored
@@ -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']
|
||||||
|
|||||||
Reference in New Issue
Block a user