fix: 充值记录支持分页查询
This commit is contained in:
@@ -60,9 +60,57 @@ export function getApiKey() {
|
||||
return get<any>('/token').json();
|
||||
}
|
||||
|
||||
// 充值记录查询参数类型
|
||||
export interface RechargeLogQueryParams {
|
||||
skipCount?: number;
|
||||
maxResultCount?: number;
|
||||
isFree?: boolean;
|
||||
minRechargeAmount?: number;
|
||||
maxRechargeAmount?: number;
|
||||
startTime?: string;
|
||||
endTime?: string;
|
||||
orderByColumn?: string;
|
||||
isAsc?: string;
|
||||
isAscending?: boolean;
|
||||
}
|
||||
|
||||
// 查询充值记录
|
||||
export function getRechargeLog() {
|
||||
return get<any>('/recharge/account').json();
|
||||
export function getRechargeLog(params?: RechargeLogQueryParams) {
|
||||
const queryParams = new URLSearchParams();
|
||||
if (params?.skipCount !== undefined) {
|
||||
queryParams.append('SkipCount', params.skipCount.toString());
|
||||
}
|
||||
if (params?.maxResultCount !== undefined) {
|
||||
queryParams.append('MaxResultCount', params.maxResultCount.toString());
|
||||
}
|
||||
if (params?.isFree !== undefined) {
|
||||
queryParams.append('IsFree', params.isFree.toString());
|
||||
}
|
||||
if (params?.minRechargeAmount !== undefined) {
|
||||
queryParams.append('MinRechargeAmount', params.minRechargeAmount.toString());
|
||||
}
|
||||
if (params?.maxRechargeAmount !== undefined) {
|
||||
queryParams.append('MaxRechargeAmount', params.maxRechargeAmount.toString());
|
||||
}
|
||||
if (params?.startTime) {
|
||||
queryParams.append('StartTime', params.startTime);
|
||||
}
|
||||
if (params?.endTime) {
|
||||
queryParams.append('EndTime', params.endTime);
|
||||
}
|
||||
if (params?.orderByColumn) {
|
||||
queryParams.append('OrderByColumn', params.orderByColumn);
|
||||
}
|
||||
if (params?.isAsc) {
|
||||
queryParams.append('IsAsc', params.isAsc);
|
||||
}
|
||||
if (params?.isAscending !== undefined) {
|
||||
queryParams.append('IsAscending', params.isAscending.toString());
|
||||
}
|
||||
|
||||
const queryString = queryParams.toString();
|
||||
const url = queryString ? `/recharge/account?${queryString}` : '/recharge/account';
|
||||
return get<any>(url).json();
|
||||
}
|
||||
|
||||
// 查询用户近7天token消耗
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
<script lang="ts" setup>
|
||||
import { ChatLineRound, List, Refresh, Search } from '@element-plus/icons-vue';
|
||||
import { ChatLineRound, List, Refresh } from '@element-plus/icons-vue';
|
||||
import { ElMessage } from 'element-plus';
|
||||
import { computed, onMounted, onUnmounted, ref } from 'vue';
|
||||
import { getRechargeLog } from '@/api/model/index.ts';
|
||||
import type { RechargeLogQueryParams } from '@/api/model/index.ts';
|
||||
import { isUserVip } from '@/utils/user.ts';
|
||||
import { showContactUs } from '@/utils/contact-us.ts';
|
||||
|
||||
@@ -19,11 +20,53 @@ interface RechargeLog {
|
||||
|
||||
const loading = ref(false);
|
||||
const logData = ref<RechargeLog[]>([]);
|
||||
const searchText = ref('');
|
||||
const currentSort = ref({ prop: '', order: '' });
|
||||
const totalCount = ref(0);
|
||||
const currentPage = ref(1);
|
||||
const pageSize = ref(10);
|
||||
|
||||
// 查询参数
|
||||
const queryParams = ref<RechargeLogQueryParams>({
|
||||
skipCount: 0,
|
||||
maxResultCount: 10,
|
||||
});
|
||||
|
||||
// 筛选条件
|
||||
const dateRange = ref<[Date, Date] | null>(null);
|
||||
const freeFilter = ref<boolean | null>(null);
|
||||
const minAmount = ref<number | undefined>(undefined);
|
||||
const maxAmount = ref<number | undefined>(undefined);
|
||||
|
||||
// 快捷时间选择
|
||||
const shortcuts = [
|
||||
{
|
||||
text: '最近一周',
|
||||
value: () => {
|
||||
const end = new Date();
|
||||
const start = new Date();
|
||||
start.setTime(start.getTime() - 3600 * 1000 * 24 * 7);
|
||||
return [start, end];
|
||||
},
|
||||
},
|
||||
{
|
||||
text: '最近一个月',
|
||||
value: () => {
|
||||
const end = new Date();
|
||||
const start = new Date();
|
||||
start.setTime(start.getTime() - 3600 * 1000 * 24 * 30);
|
||||
return [start, end];
|
||||
},
|
||||
},
|
||||
{
|
||||
text: '最近三个月',
|
||||
value: () => {
|
||||
const end = new Date();
|
||||
const start = new Date();
|
||||
start.setTime(start.getTime() - 3600 * 1000 * 24 * 90);
|
||||
return [start, end];
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
// 移动端检测
|
||||
const isMobile = ref(false);
|
||||
|
||||
@@ -32,15 +75,36 @@ function checkMobile() {
|
||||
}
|
||||
|
||||
// 模拟数据获取
|
||||
async function fetchRechargeLog() {
|
||||
try {
|
||||
async function fetchRechargeLog(resetPage = false) {
|
||||
if (resetPage) {
|
||||
currentPage.value = 1;
|
||||
}
|
||||
|
||||
loading.value = true;
|
||||
const res = await getRechargeLog();
|
||||
logData.value = res.data || [];
|
||||
try {
|
||||
const params: RechargeLogQueryParams = {
|
||||
skipCount: currentPage.value,
|
||||
maxResultCount: queryParams.value.maxResultCount,
|
||||
startTime: queryParams.value.startTime,
|
||||
endTime: queryParams.value.endTime,
|
||||
orderByColumn: queryParams.value.orderByColumn,
|
||||
isAsc: queryParams.value.isAsc,
|
||||
isFree: queryParams.value.isFree,
|
||||
minRechargeAmount: queryParams.value.minRechargeAmount,
|
||||
maxRechargeAmount: queryParams.value.maxRechargeAmount,
|
||||
};
|
||||
|
||||
const res = await getRechargeLog(params);
|
||||
if (res.data) {
|
||||
logData.value = res.data.items || [];
|
||||
totalCount.value = res.data.totalCount || 0;
|
||||
}
|
||||
}
|
||||
catch (error) {
|
||||
console.error('获取充值记录失败:', error);
|
||||
ElMessage.error('获取充值记录失败');
|
||||
logData.value = [];
|
||||
totalCount.value = 0;
|
||||
}
|
||||
finally {
|
||||
loading.value = false;
|
||||
@@ -48,26 +112,89 @@ async function fetchRechargeLog() {
|
||||
}
|
||||
|
||||
// 排序处理
|
||||
function handleSortChange({ prop, order }: { prop: string; order: string }) {
|
||||
currentSort.value = { prop, order };
|
||||
function handleSortChange({ prop, order }: { prop: string; order: string | null }) {
|
||||
if (order) {
|
||||
queryParams.value.orderByColumn = prop;
|
||||
queryParams.value.isAsc = order === 'ascending' ? 'ascending' : 'descending';
|
||||
}
|
||||
else {
|
||||
queryParams.value.orderByColumn = undefined;
|
||||
queryParams.value.isAsc = undefined;
|
||||
}
|
||||
fetchRechargeLog(true);
|
||||
}
|
||||
|
||||
// 搜索处理
|
||||
function handleSearch() {
|
||||
currentPage.value = 1;
|
||||
// 处理分页
|
||||
function handlePageChange(page: number) {
|
||||
currentPage.value = page;
|
||||
fetchRechargeLog();
|
||||
}
|
||||
|
||||
function handleSearchClear() {
|
||||
searchText.value = '';
|
||||
// 处理每页大小变化
|
||||
function handleSizeChange(size: number) {
|
||||
pageSize.value = size;
|
||||
queryParams.value.maxResultCount = size;
|
||||
currentPage.value = 1;
|
||||
fetchRechargeLog();
|
||||
}
|
||||
|
||||
// 处理时间筛选
|
||||
function handleDateChange(value: [Date, Date] | null) {
|
||||
if (value && value.length === 2) {
|
||||
const startDate = new Date(value[0]);
|
||||
startDate.setHours(0, 0, 0, 0);
|
||||
const endDate = new Date(value[1]);
|
||||
endDate.setHours(23, 59, 59, 999);
|
||||
queryParams.value.startTime = startDate.toISOString();
|
||||
queryParams.value.endTime = endDate.toISOString();
|
||||
}
|
||||
else {
|
||||
queryParams.value.startTime = undefined;
|
||||
queryParams.value.endTime = undefined;
|
||||
}
|
||||
fetchRechargeLog(true);
|
||||
}
|
||||
|
||||
// 处理是否免费筛选
|
||||
function handleFreeFilterChange(value: boolean | null) {
|
||||
queryParams.value.isFree = value === null ? undefined : value;
|
||||
fetchRechargeLog(true);
|
||||
}
|
||||
|
||||
// 处理金额筛选
|
||||
function handleAmountChange() {
|
||||
queryParams.value.minRechargeAmount = minAmount.value;
|
||||
queryParams.value.maxRechargeAmount = maxAmount.value;
|
||||
fetchRechargeLog(true);
|
||||
}
|
||||
|
||||
// 重置筛选
|
||||
function resetFilters() {
|
||||
dateRange.value = null;
|
||||
freeFilter.value = null;
|
||||
minAmount.value = undefined;
|
||||
maxAmount.value = undefined;
|
||||
queryParams.value = {
|
||||
maxResultCount: pageSize.value,
|
||||
orderByColumn: undefined,
|
||||
isAsc: undefined,
|
||||
startTime: undefined,
|
||||
endTime: undefined,
|
||||
isFree: undefined,
|
||||
minRechargeAmount: undefined,
|
||||
maxRechargeAmount: undefined,
|
||||
};
|
||||
fetchRechargeLog(true);
|
||||
}
|
||||
|
||||
// 判断是否有活动的筛选条件
|
||||
const hasActiveFilters = computed(() => {
|
||||
return !!dateRange.value || freeFilter.value !== null || minAmount.value !== undefined || maxAmount.value !== undefined;
|
||||
});
|
||||
|
||||
// 刷新数据
|
||||
function refreshLog() {
|
||||
fetchRechargeLog();
|
||||
currentPage.value = 1;
|
||||
searchText.value = '';
|
||||
currentSort.value = { prop: '', order: '' };
|
||||
}
|
||||
|
||||
// 联系售后弹窗
|
||||
@@ -84,44 +211,7 @@ function contactCustomerService() {
|
||||
// 暴露方法给父组件使用
|
||||
defineExpose({
|
||||
contactCustomerService,
|
||||
});
|
||||
|
||||
// 过滤和排序后的数据
|
||||
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;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return data;
|
||||
});
|
||||
|
||||
// 是否显示分页 暂时不需要分页功能
|
||||
const showPagination = computed(() => {
|
||||
return false;
|
||||
refresh: fetchRechargeLog,
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
@@ -143,6 +233,9 @@ onUnmounted(() => {
|
||||
充值记录
|
||||
</h2>
|
||||
<div class="header-actions">
|
||||
<el-tag v-if="totalCount > 0" type="primary" size="default" class="count-tag" effect="plain">
|
||||
共 {{ totalCount }} 条记录
|
||||
</el-tag>
|
||||
<el-tooltip v-if="isUserVip()" content="联系售后" placement="top">
|
||||
<el-button circle :loading="loading" @click="contactCustomerService">
|
||||
<el-icon color="#07c160">
|
||||
@@ -155,26 +248,80 @@ onUnmounted(() => {
|
||||
<el-icon><Refresh /></el-icon>
|
||||
</el-button>
|
||||
</el-tooltip>
|
||||
<el-input
|
||||
v-model="searchText"
|
||||
placeholder="搜索备注"
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 筛选工具栏 -->
|
||||
<div class="filter-toolbar">
|
||||
<div class="filter-row">
|
||||
<div class="filter-item">
|
||||
<el-date-picker
|
||||
v-model="dateRange"
|
||||
type="daterange"
|
||||
range-separator="至"
|
||||
start-placeholder="开始日期"
|
||||
end-placeholder="结束日期"
|
||||
size="default"
|
||||
:shortcuts="shortcuts"
|
||||
class="date-picker"
|
||||
format="YYYY-MM-DD"
|
||||
value-format="YYYY-MM-DD"
|
||||
:editable="false"
|
||||
@change="handleDateChange"
|
||||
/>
|
||||
</div>
|
||||
<div class="filter-item">
|
||||
<el-select
|
||||
v-model="freeFilter"
|
||||
placeholder="全部类型"
|
||||
clearable
|
||||
style="width: 200px; margin-left: 10px;"
|
||||
@clear="handleSearchClear"
|
||||
@keyup.enter="handleSearch"
|
||||
size="default"
|
||||
class="free-select"
|
||||
@change="handleFreeFilterChange"
|
||||
>
|
||||
<template #append>
|
||||
<el-button @click="handleSearch">
|
||||
<el-icon><Search /></el-icon>
|
||||
<el-option label="免费" :value="true" />
|
||||
<el-option label="付费" :value="false" />
|
||||
</el-select>
|
||||
</div>
|
||||
<div class="filter-item amount-range">
|
||||
<el-input-number
|
||||
v-model="minAmount"
|
||||
:min="0"
|
||||
:precision="2"
|
||||
:controls="false"
|
||||
placeholder="最小金额"
|
||||
size="default"
|
||||
class="amount-input"
|
||||
@change="handleAmountChange"
|
||||
/>
|
||||
<span class="range-separator">-</span>
|
||||
<el-input-number
|
||||
v-model="maxAmount"
|
||||
:min="0"
|
||||
:precision="2"
|
||||
:controls="false"
|
||||
placeholder="最大金额"
|
||||
size="default"
|
||||
class="amount-input"
|
||||
@change="handleAmountChange"
|
||||
/>
|
||||
</div>
|
||||
<div class="filter-actions">
|
||||
<el-button
|
||||
v-if="hasActiveFilters"
|
||||
size="default"
|
||||
@click="resetFilters"
|
||||
>
|
||||
<el-icon><i-ep-refresh-left /></el-icon>
|
||||
重置
|
||||
</el-button>
|
||||
</template>
|
||||
</el-input>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<el-table
|
||||
v-loading="loading"
|
||||
:data="filteredData"
|
||||
:data="logData"
|
||||
style="width: 100%"
|
||||
border
|
||||
stripe
|
||||
@@ -185,7 +332,7 @@ onUnmounted(() => {
|
||||
prop="content"
|
||||
label="套餐类型"
|
||||
min-width="150"
|
||||
sortable="custom"
|
||||
sortable
|
||||
show-overflow-tooltip
|
||||
/>
|
||||
<el-table-column
|
||||
@@ -193,7 +340,7 @@ onUnmounted(() => {
|
||||
prop="rechargeAmount"
|
||||
label="金额(元)"
|
||||
min-width="110"
|
||||
sortable="custom"
|
||||
sortable
|
||||
>
|
||||
<template #default="{ row }">
|
||||
<span class="amount-cell">¥{{ row.rechargeAmount.toFixed(2) }}</span>
|
||||
@@ -203,14 +350,14 @@ onUnmounted(() => {
|
||||
prop="creationTime"
|
||||
label="充值时间"
|
||||
min-width="160"
|
||||
sortable="custom"
|
||||
sortable
|
||||
show-overflow-tooltip
|
||||
/>
|
||||
<el-table-column
|
||||
prop="expireDateTime"
|
||||
label="到期时间"
|
||||
min-width="160"
|
||||
sortable="custom"
|
||||
sortable
|
||||
show-overflow-tooltip
|
||||
/>
|
||||
<!-- <el-table-column show-overflow-tooltip prop="contactInfo" width="100" label="联系方式">
|
||||
@@ -232,20 +379,31 @@ onUnmounted(() => {
|
||||
</el-table>
|
||||
|
||||
<div class="log-footer">
|
||||
<div class="summary">
|
||||
共 <span class="highlight">{{ filteredData.length }}</span> 条记录
|
||||
<!-- 空状态 -->
|
||||
<div v-if="!logData.length && !loading" class="empty-container">
|
||||
<el-empty :description="hasActiveFilters ? '当前筛选条件下无数据' : '暂无充值记录'">
|
||||
<el-button v-if="hasActiveFilters" type="primary" @click="resetFilters">
|
||||
清除筛选
|
||||
</el-button>
|
||||
</el-empty>
|
||||
</div>
|
||||
|
||||
<!-- 分页 -->
|
||||
<div v-if="totalCount > 0" class="pagination-container">
|
||||
<el-pagination
|
||||
v-if="showPagination"
|
||||
v-model:current-page="currentPage"
|
||||
v-model:page-size="pageSize"
|
||||
:total="filteredData.length"
|
||||
:page-sizes="[10, 20, 50, 100]"
|
||||
:total="totalCount"
|
||||
layout="total, sizes, prev, pager, next, jumper"
|
||||
background
|
||||
prev-text="上一页"
|
||||
next-text="下一页"
|
||||
@size-change="handleSizeChange"
|
||||
@current-change="handlePageChange"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
@@ -355,14 +513,86 @@ onUnmounted(() => {
|
||||
}
|
||||
|
||||
.log-footer {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
flex-direction: column;
|
||||
margin-top: 25px;
|
||||
padding-top: 20px;
|
||||
border-top: 2px solid #e9ecef;
|
||||
}
|
||||
|
||||
/* 筛选工具栏 */
|
||||
.filter-toolbar {
|
||||
margin-bottom: 20px;
|
||||
padding: 16px;
|
||||
background: linear-gradient(135deg, #f8f9fa 0%, #ffffff 100%);
|
||||
border-radius: 12px;
|
||||
border: 1px solid #e8eaed;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.04);
|
||||
}
|
||||
|
||||
.filter-row {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 12px;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.filter-item {
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
|
||||
.date-picker {
|
||||
width: 320px;
|
||||
}
|
||||
|
||||
.free-select {
|
||||
width: 140px;
|
||||
}
|
||||
|
||||
/* 金额范围筛选 */
|
||||
.amount-range {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.amount-input {
|
||||
width: 120px;
|
||||
}
|
||||
|
||||
.range-separator {
|
||||
color: #909399;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.filter-actions {
|
||||
margin-left: auto;
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
/* 记录数标签 */
|
||||
.count-tag {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
padding: 6px 12px;
|
||||
font-weight: 600;
|
||||
border-radius: 16px;
|
||||
}
|
||||
|
||||
/* 空状态 */
|
||||
.empty-container {
|
||||
padding: 40px 0;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
/* 分页容器 */
|
||||
.pagination-container {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.summary {
|
||||
color: #666;
|
||||
font-size: 15px;
|
||||
@@ -455,10 +685,43 @@ onUnmounted(() => {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.header-actions .el-input {
|
||||
width: 100% !important;
|
||||
margin-left: 0 !important;
|
||||
margin-top: 8px;
|
||||
/* 移动端筛选工具栏 */
|
||||
.filter-toolbar {
|
||||
padding: 12px;
|
||||
}
|
||||
|
||||
.filter-row {
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.filter-item {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.date-picker,
|
||||
.free-select {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.amount-range {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.amount-input {
|
||||
flex: 1;
|
||||
width: auto;
|
||||
}
|
||||
|
||||
.filter-actions {
|
||||
margin-left: 0;
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.filter-actions .el-button {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
/* 表格移动端优化 */
|
||||
|
||||
1
Yi.Ai.Vue3/types/import_meta.d.ts
vendored
1
Yi.Ai.Vue3/types/import_meta.d.ts
vendored
@@ -7,6 +7,7 @@ interface ImportMetaEnv {
|
||||
readonly VITE_WEB_BASE_API: string;
|
||||
readonly VITE_API_URL: string;
|
||||
readonly VITE_FILE_UPLOAD_API: string;
|
||||
readonly VITE_BUILD_COMPRESS: string;
|
||||
readonly VITE_SSO_SEVER_URL: string;
|
||||
readonly VITE_APP_VERSION: string;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user