fix: 取消vue原生钩子函数,对接系统管理页面中的接口

This commit is contained in:
ZhuRui
2026-08-05 14:33:21 +08:00
parent 2e6f7ca552
commit 503ee266b0
22 changed files with 569 additions and 447 deletions
+4 -8
View File
@@ -1,4 +1,4 @@
import { defineComponent, onMounted } from 'vue';
import { defineComponent } from 'vue';
import {
Button,
Input,
@@ -171,10 +171,6 @@ export default defineComponent({
},
];
onMounted(() => {
handleSearch();
});
return () => (
<div class={pageStyles.containerMain}>
<div class={pageStyles.filter}>
@@ -237,9 +233,9 @@ export default defineComponent({
<div class={pageStyles.pagination}>
<Pagination
current={pagination.value.current}
pageSize={pagination.value.pageSize}
total={pagination.value.total}
current={(pagination as any).current.value}
pageSize={(pagination as any).pageSize.value}
total={(pagination as any).total.value}
showSizeChanger
showTotal={(total: number) => `${total}`}
onChange={handlePageChange}
+32 -40
View File
@@ -2,6 +2,9 @@ import { computed, reactive, toRef, Ref, h } from 'vue';
import { message } from 'ant-design-vue';
import { StatusTag, type StatusTagTone } from '@/components';
import { useState, useDebounce, useThrottleFn } from '@/hooks';
import { useRequest } from '@/hooks/useRequest';
import { useEffect } from '@/hooks/useEffect';
import { usePagination } from '@/hooks/usePagination';
import {
getBannerList,
delBanner,
@@ -61,21 +64,32 @@ export function useBannerModel() {
{ delay: 300 },
);
// ===== 表格状态(API 返回全量,前端分页) =====
const [loading, setLoading] = useState<boolean>(false);
const [allData, setAllData] = useState<TournamentAdminBannerListVO[]>([]);
const [pagination, setPagination] = useState({
current: 1,
pageSize: 10,
total: 0,
// ===== 数据请求(API 返回全量,前端分页) =====
const {
data,
loading,
run: fetchList,
} = useRequest<TournamentAdminBannerListVO[]>(() => getBannerList(buildQueryParams()), {
refreshDeps: [],
formatResult: (res) => (res.code == 200 ? res.data : []),
});
const allData = computed(() => data.value || []);
// ===== 前端分页 =====
const pagination = usePagination({ defaultCurrent: 1, defaultPageSize: 10 });
/** 当前页数据(客户端切片) */
const dataSource = computed(() => {
const start = (pagination.value.current - 1) * pagination.value.pageSize;
return allData.value.slice(start, start + pagination.value.pageSize);
const start = ((pagination as any).current.value - 1) * (pagination as any).pageSize.value;
return allData.value.slice(start, start + (pagination as any).pageSize.value);
});
// 同步前端分页 total
useEffect(() => {
pagination.setTotal(allData.value.length);
}, [allData]);
// ===== 弹窗状态 =====
const [modalVisible, setModalVisible] = useState<boolean>(false);
const [editingRecord, setEditingRecord] = useState<any>(null);
@@ -119,37 +133,21 @@ export function useBannerModel() {
// ===== 方法 =====
/** 查询 */
const handleSearch = useThrottleFn(async () => {
setLoading(true);
try {
const queryParams = buildQueryParams();
console.log('Banner列表查询参数:', queryParams);
const res = await getBannerList(queryParams);
if (res.code == 200) {
setAllData(res.data);
setPagination({ current: 1, pageSize: 10, total: res.data.length });
} else {
message.error(res.msg || '查询失败');
}
} catch (error: any) {
console.error('Banner查询失败:', error);
} finally {
setLoading(false);
}
}, 500);
const handleSearch = () => fetchList();
/** 重置 */
const handleReset = useThrottleFn(() => {
filterForm.title = '';
filterForm.status = '';
setPagination({ current: 1, pageSize: 10, total: 0 });
setAllData([]);
setTimeout(() => handleSearch(), 350);
pagination.reset();
setTimeout(fetchList, 350);
}, 500);
const handlePageChange = (page: number, pageSize: number) => {
setPagination({ ...pagination.value, current: page, pageSize });
pagination.setCurrent(page);
if (pageSize !== (pagination as any).pageSize.value) {
pagination.setPageSize(pageSize);
}
};
/** 打开新增弹窗 */
@@ -214,39 +212,33 @@ export function useBannerModel() {
/** 删除 */
const handleDelete = useThrottleFn(async (record: any) => {
setLoading(true);
try {
const res = await delBanner(record.id);
if (res.code == 200) {
message.success('删除成功');
handleSearch();
fetchList();
} else {
message.error(res.msg || '删除失败');
}
} catch (e: any) {
console.error('Banner删除失败:', e);
} finally {
setLoading(false);
}
}, 500);
/** 启用/禁用切换 */
const handleToggleStatus = useThrottleFn(async (record: any) => {
setLoading(true);
try {
const newStatus = record.status === '1' ? '0' : '1';
const actionText = newStatus === '1' ? '启用' : '禁用';
const res = await toggleBannerActive({ id: record.id, status: newStatus });
if (res.code == 200) {
message.success(`${actionText}成功`);
handleSearch();
fetchList();
} else {
message.error(res.msg || '操作失败');
}
} catch (e: any) {
console.error('Banner状态切换失败:', e);
} finally {
setLoading(false);
}
}, 500);
+12 -10
View File
@@ -1,4 +1,4 @@
import { defineComponent, ref, onMounted, onUnmounted, nextTick } from 'vue';
import { defineComponent, ref, nextTick } from 'vue';
import {
Button,
Input,
@@ -12,7 +12,7 @@ import {
} from 'ant-design-vue';
import { useLogModel, ACTION_TYPE_OPTIONS, ACTION_SOURCE_OPTIONS } from './model/useLogModel';
import { useLogColumns } from './model/useLogColumns';
import { useState, useContainerSize } from '@/hooks';
import { useState, useEffect, useContainerSize } from '@/hooks';
import pageStyles from '@/assets/styles/pageLayout.module.less';
import './index.module.less';
@@ -29,15 +29,17 @@ const LogContentCell = defineComponent({
if (el) setOverflow(el.scrollHeight > el.clientHeight);
};
let observer: ResizeObserver | null = null;
onMounted(() => {
nextTick(checkOverflow);
useEffect(() => {
const el = textRef.value;
if (el) {
observer = new ResizeObserver(checkOverflow);
observer.observe(el);
}
});
onUnmounted(() => observer?.disconnect());
if (!el) return;
nextTick(checkOverflow);
observer = new ResizeObserver(checkOverflow);
observer.observe(el);
return () => {
observer?.disconnect();
observer = null;
};
}, [() => textRef.value]);
return () => {
const raw = props.text;
return (
+4 -8
View File
@@ -1,4 +1,4 @@
import { defineComponent, onMounted } from 'vue';
import { defineComponent } from 'vue';
import { Button, Input, Table, Form, Space, Pagination, Select, DatePicker } from 'ant-design-vue';
import { usePaymentsModel } from './model/usePaymentsModel';
import { useContainerSize } from '@/hooks';
@@ -24,10 +24,6 @@ export default defineComponent({
const { containerRef, height } = useContainerSize({ headerOffset: 55 });
onMounted(() => {
handleSearch();
});
return () => (
<div class={pageStyles.containerMain}>
<div class={pageStyles.filter}>
@@ -101,9 +97,9 @@ export default defineComponent({
</div>
<div class={pageStyles.pagination}>
<Pagination
current={pagination.value.current}
pageSize={pagination.value.pageSize}
total={pagination.value.total}
current={(pagination as any).current.value}
pageSize={(pagination as any).pageSize.value}
total={(pagination as any).total.value}
showSizeChanger
showTotal={(total: number) => `${total}`}
onChange={handlePageChange}
@@ -1,5 +1,5 @@
import { computed, reactive, toRef, Ref } from 'vue';
import { useState, useDebounce, useThrottleFn } from '@/hooks';
import { useDebounce, useThrottleFn, useRequest, usePagination, useEffect } from '@/hooks';
import { getPaymentFlowList, type PaymentFlowVO, type PaymentFlowQueryParams } from './services';
import dayjs from 'dayjs';
@@ -40,13 +40,50 @@ export function usePaymentsModel() {
{ delay: 300 },
);
// ===== 汇总 =====
const [summary, setSummary] = useState({ totalAmount: '0.00', totalRefundAmount: '0.00' });
// ===== 分页 =====
const pagination = usePagination({ defaultCurrent: 1, defaultPageSize: 10 });
// ===== 表格状态 =====
const [loading, setLoading] = useState<boolean>(false);
const [dataSource, setDataSource] = useState<PaymentFlowVO[]>([]);
const [pagination, setPagination] = useState({ current: 1, pageSize: 10, total: 0 });
// ===== 构建 API 查询参数 =====
const buildQueryParams = (): PaymentFlowQueryParams => {
const params: PaymentFlowQueryParams = {
page: String((pagination as any).current.value),
limit: String((pagination as any).pageSize.value),
};
if (filterForm.timeRange) {
const [start, end] = filterForm.timeRange;
if (start) params.createDateBegin = dayjs(start).format('YYYY-MM-DD');
if (end) params.createDateEnd = dayjs(end).format('YYYY-MM-DD');
}
if (debouncedNickname.value.trim()) params.nickname = debouncedNickname.value.trim();
if (debouncedPhone.value.trim()) params.phone = debouncedPhone.value.trim();
if (filterForm.type) params.type = filterForm.type;
return params;
};
// ===== 请求 =====
const {
data,
loading,
run: fetchList,
} = useRequest(() => getPaymentFlowList(buildQueryParams()), {
refreshDeps: [],
formatResult: (res) => res,
});
const listData = computed(() => (data.value as any)?.data);
const dataSource = computed(() => listData.value?.list || []);
// 汇总:从 listData(即 res.data,包含 exData)派生
const summary = computed(() => ({
totalAmount: listData.value?.exData?.totalAmount ?? '0.00',
totalRefundAmount: listData.value?.exData?.totalRefundAmount ?? '0.00',
}));
// 同步 total 到 pagination
useEffect(() => {
const total = (data.value as any)?.data?.total;
if (total !== undefined) pagination.setTotal(total);
}, [data]);
// ===== 表格列(dataIndex 对齐 API =====
const columns = [
@@ -81,59 +118,25 @@ export function usePaymentsModel() {
);
});
// ===== 构建 API 查询参数 =====
const buildQueryParams = (): PaymentFlowQueryParams => {
const params: PaymentFlowQueryParams = {
page: String(pagination.value.current),
limit: String(pagination.value.pageSize),
};
if (filterForm.timeRange) {
const [start, end] = filterForm.timeRange;
if (start) params.createDateBegin = dayjs(start).format('YYYY-MM-DD');
if (end) params.createDateEnd = dayjs(end).format('YYYY-MM-DD');
}
if (debouncedNickname.value.trim()) params.nickname = debouncedNickname.value.trim();
if (debouncedPhone.value.trim()) params.phone = debouncedPhone.value.trim();
if (filterForm.type) params.type = filterForm.type;
return params;
};
// ===== 方法 =====
const handleSearch = useThrottleFn(async () => {
setLoading(true);
try {
const queryParams = buildQueryParams();
const res = await getPaymentFlowList(queryParams);
if (res.code == 200) {
setDataSource(res.data.list);
setPagination({ ...pagination.value, total: res.data.total });
setSummary({
totalAmount: res.data.exData?.totalAmount ?? '0.00',
totalRefundAmount: res.data.exData?.totalRefundAmount ?? '0.00',
});
}
} catch {
// 网络层已统一提示
} finally {
setLoading(false);
}
}, 500);
const handleSearch = () => fetchList();
const handleReset = useThrottleFn(() => {
filterForm.timeRange = null;
filterForm.nickname = '';
filterForm.phone = '';
filterForm.type = '';
setPagination({ current: 1, pageSize: 10, total: 0 });
setDataSource([]);
setSummary({ totalAmount: '0.00', totalRefundAmount: '0.00' });
setTimeout(() => handleSearch(), 350);
pagination.reset();
setTimeout(fetchList, 350);
}, 500);
const handlePageChange = (page: number, pageSize: number) => {
setPagination({ ...pagination.value, current: page, pageSize });
handleSearch();
pagination.setCurrent(page);
if (pageSize !== (pagination as any).pageSize.value) {
pagination.setPageSize(pageSize);
}
setTimeout(fetchList, 0);
};
return {
+16 -15
View File
@@ -1,7 +1,8 @@
import { defineComponent, ref, onMounted, onBeforeUnmount, watch, nextTick } from 'vue';
import { defineComponent, ref, nextTick } from 'vue';
import { Select, Table } from 'ant-design-vue';
import * as echarts from 'echarts';
import { useReportsModel } from './model/useReportsModel';
import { useEffect } from '@/hooks';
import styles from './index.module.less';
/**
@@ -140,23 +141,23 @@ export default defineComponent({
chartInstance?.resize();
};
onMounted(() => {
nextTick(() => {
renderChart();
});
// ECharts 挂载/销毁 + 窗口 resize 监听
useEffect(() => {
const el = chartRef.value;
if (!el) return;
nextTick(() => renderChart());
window.addEventListener('resize', handleResize);
});
return () => {
window.removeEventListener('resize', handleResize);
chartInstance?.dispose();
chartInstance = null;
};
}, [() => chartRef.value]);
onBeforeUnmount(() => {
window.removeEventListener('resize', handleResize);
chartInstance?.dispose();
chartInstance = null;
});
// 时间范围切换时刷新图表(接入 API 后生效)
watch(range, () => {
// 时间范围切换时刷新图表
useEffect(() => {
renderChart();
});
}, [range]);
return () => {
/** 根据增长率构建箭头 + 颜色(正值 ↑ 绿,负值 ↓ 红,null 时固定文本) */
+4 -8
View File
@@ -1,4 +1,4 @@
import { defineComponent, onMounted } from 'vue';
import { defineComponent } from 'vue';
import { Button, Input, Table, Form, Space, Pagination } from 'ant-design-vue';
import { WalletOutlined, LockOutlined, RiseOutlined } from '@ant-design/icons-vue';
import { useWalletModel } from './model/useWalletModel';
@@ -68,10 +68,6 @@ export default defineComponent({
},
];
onMounted(() => {
handleSearch();
});
return () => {
const statCards: StatCardItem[] = [
{
@@ -206,9 +202,9 @@ export default defineComponent({
</div>
<div class={pageStyles.pagination}>
<Pagination
current={pagination.value.current}
pageSize={pagination.value.pageSize}
total={pagination.value.total}
current={(pagination as any).current.value}
pageSize={(pagination as any).pageSize.value}
total={(pagination as any).total.value}
showSizeChanger
showTotal={(total: number) => `${total}`}
onChange={handlePageChange}
@@ -1,5 +1,12 @@
import { computed, reactive, toRef, Ref } from 'vue';
import { useState, useDebounce, useThrottleFn } from '@/hooks';
import {
useState,
useDebounce,
useThrottleFn,
useRequest,
usePagination,
useEffect,
} from '@/hooks';
import { getWalletList, type WalletSummaryVO, type WalletQueryParams } from './services';
// ============================================================
@@ -58,17 +65,58 @@ export function useWalletModel() {
{ delay: 300 },
);
// ===== 顶部统计(汇总值从接口返回的第一条记录中提取) =====
const [summary, setSummary] = useState({
totalBalance: '0.00',
frozenAmount: '0.00',
totalWithdraw: '0.00',
// ===== 分页 =====
const pagination = usePagination({ defaultCurrent: 1, defaultPageSize: 10 });
// ===== 构建 API 查询参数 =====
const buildQueryParams = (): WalletQueryParams => {
// 余额筛选:若起始 > 截止,互换两值
const min = Number(filterForm.minBalance);
const max = Number(filterForm.maxBalance);
if (filterForm.minBalance && filterForm.maxBalance && min > max) {
filterForm.minBalance = filterForm.maxBalance;
filterForm.maxBalance = String(min);
}
const params: WalletQueryParams = {
page: String((pagination as any).current.value),
limit: String((pagination as any).pageSize.value),
};
if (debouncedNickname.value.trim()) params.nickname = debouncedNickname.value.trim();
if (debouncedPhone.value.trim()) params.phone = debouncedPhone.value.trim();
if (filterForm.minBalance) params.amountBegin = filterForm.minBalance;
if (filterForm.maxBalance) params.amountEnd = filterForm.maxBalance;
return params;
};
// ===== 请求 =====
const {
data,
loading,
run: fetchList,
} = useRequest(() => getWalletList(buildQueryParams()), {
refreshDeps: [],
formatResult: (res) => res,
});
// ===== 表格状态 =====
const [loading, setLoading] = useState<boolean>(false);
const [dataSource, setDataSource] = useState<WalletSummaryVO[]>([]);
const [pagination, setPagination] = useState({ current: 1, pageSize: 10, total: 0 });
const listData = computed(() => (data.value as any)?.data);
const dataSource = computed(() => listData.value?.list || []);
// 汇总:从第一条记录提取
const summary = computed(() => {
const first = dataSource.value?.[0] as any;
return {
totalBalance: first?.totalAmount ?? '0.00',
frozenAmount: first?.totalFrozenAmount ?? '0.00',
totalWithdraw: first?.totalWithdrawalAmount ?? '0.00',
};
});
// 同步 total 到 pagination
useEffect(() => {
const total = (data.value as any)?.data?.total;
if (total !== undefined) pagination.setTotal(total);
}, [data]);
// ===== 详情弹窗状态 =====
const [detailVisible, setDetailVisible] = useState<boolean>(false);
@@ -112,70 +160,25 @@ export function useWalletModel() {
);
});
// ===== 构建 API 查询参数 =====
const buildQueryParams = (): WalletQueryParams => {
const params: WalletQueryParams = {
page: String(pagination.value.current),
limit: String(pagination.value.pageSize),
};
if (debouncedNickname.value.trim()) params.nickname = debouncedNickname.value.trim();
if (debouncedPhone.value.trim()) params.phone = debouncedPhone.value.trim();
if (filterForm.minBalance) params.amountBegin = filterForm.minBalance;
if (filterForm.maxBalance) params.amountEnd = filterForm.maxBalance;
return params;
};
// ===== 方法 =====
const handleSearch = useThrottleFn(async () => {
setLoading(true);
try {
// 余额筛选:若起始 > 截止,互换两值
const min = Number(filterForm.minBalance);
const max = Number(filterForm.maxBalance);
if (filterForm.minBalance && filterForm.maxBalance && min > max) {
filterForm.minBalance = filterForm.maxBalance;
filterForm.maxBalance = String(min);
}
const params = buildQueryParams();
console.log('钱包列表查询参数:', params);
const res = await getWalletList(params);
if (res.code == 200) {
console.log('钱包列表查询结果:', { total: res.data.total, count: res.data.list.length });
setDataSource(res.data.list);
setPagination({ ...pagination.value, total: res.data.total });
// 从第一条记录提取汇总值
if (res.data.list.length > 0) {
const first = res.data.list[0];
setSummary({
totalBalance: first.totalAmount || '0.00',
frozenAmount: first.totalFrozenAmount || '0.00',
totalWithdraw: first.totalWithdrawalAmount || '0.00',
});
}
}
} catch {
// 网络层已统一提示
} finally {
setLoading(false);
}
}, 500);
const handleSearch = () => fetchList();
const handleReset = useThrottleFn(() => {
filterForm.nickname = '';
filterForm.phone = '';
filterForm.minBalance = '';
filterForm.maxBalance = '';
setPagination({ current: 1, pageSize: 10, total: 0 });
setDataSource([]);
setSummary({ totalBalance: '0.00', frozenAmount: '0.00', totalWithdraw: '0.00' });
setTimeout(() => handleSearch(), 350);
pagination.reset();
setTimeout(fetchList, 350);
}, 500);
const handlePageChange = (page: number, pageSize: number) => {
setPagination({ ...pagination.value, current: page, pageSize });
handleSearch();
pagination.setCurrent(page);
if (pageSize !== (pagination as any).pageSize.value) {
pagination.setPageSize(pageSize);
}
setTimeout(fetchList, 0);
};
const handleViewDetail = (record: any) => {
+4 -9
View File
@@ -1,4 +1,4 @@
import { defineComponent, onMounted } from 'vue';
import { defineComponent } from 'vue';
import { Button, Input, Table, Form, Space, Pagination, Select } from 'ant-design-vue';
import { useWithdrawModel, renderAuditStatus, renderPayStatus } from './model/useWithdrawModel';
import { useContainerSize } from '@/hooks';
@@ -82,11 +82,6 @@ export default defineComponent({
},
];
// 首次进入自动加载
onMounted(() => {
handleSearch();
});
return () => (
<div class={pageStyles.containerMain}>
{/* ===== 筛选区 ===== */}
@@ -175,9 +170,9 @@ export default defineComponent({
{/* 独立分页,右下方 */}
<div class={pageStyles.pagination}>
<Pagination
current={pagination.value.current}
pageSize={pagination.value.pageSize}
total={pagination.value.total}
current={(pagination as any).current.value}
pageSize={(pagination as any).pageSize.value}
total={(pagination as any).total.value}
showSizeChanger
showTotal={(total: number) => `${total}`}
onChange={handlePageChange}
@@ -1,6 +1,13 @@
import { computed, reactive, toRef, Ref, h } from 'vue';
import { StatusTag, type StatusTagTone } from '@/components';
import { useState, useDebounce, useThrottleFn } from '@/hooks';
import {
useState,
useDebounce,
useThrottleFn,
useRequest,
usePagination,
useEffect,
} from '@/hooks';
import { getWithdrawList, type WithdrawVO, type WithdrawQueryParams } from './services';
// ============================================================
@@ -93,10 +100,41 @@ export function useWithdrawModel() {
{ delay: 300 },
);
// ===== 表格状态 =====
const [loading, setLoading] = useState<boolean>(false);
const [dataSource, setDataSource] = useState<WithdrawVO[]>([]);
const [pagination, setPagination] = useState({ current: 1, pageSize: 10, total: 0 });
// ===== 分页 =====
const pagination = usePagination({ defaultCurrent: 1, defaultPageSize: 10 });
// ===== 构建 API 查询参数 =====
const buildQueryParams = (): WithdrawQueryParams => {
const params: WithdrawQueryParams = {
page: String((pagination as any).current.value),
limit: String((pagination as any).pageSize.value),
};
if (debouncedNickname.value.trim()) params.nickname = debouncedNickname.value.trim();
if (debouncedPhone.value.trim()) params.phone = debouncedPhone.value.trim();
if (debouncedRealName.value.trim()) params.realName = debouncedRealName.value.trim();
if (filterForm.auditStatus) params.auditStatus = filterForm.auditStatus;
if (filterForm.payStatus) params.payStatus = filterForm.payStatus;
return params;
};
// ===== 请求 =====
const {
data,
loading,
run: fetchList,
} = useRequest(() => getWithdrawList(buildQueryParams()), {
refreshDeps: [],
formatResult: (res) => res,
});
const listData = computed(() => (data.value as any)?.data);
const dataSource = computed(() => listData.value?.list || []);
// 同步 total 到 pagination
useEffect(() => {
const total = (data.value as any)?.data?.total;
if (total !== undefined) pagination.setTotal(total);
}, [data]);
// ===== 审核弹窗状态 =====
const [auditVisible, setAuditVisible] = useState<boolean>(false);
@@ -139,40 +177,9 @@ export function useWithdrawModel() {
);
});
// ===== 构建 API 查询参数 =====
const buildQueryParams = (): WithdrawQueryParams => {
const params: WithdrawQueryParams = {
page: String(pagination.value.current),
limit: String(pagination.value.pageSize),
};
if (debouncedNickname.value.trim()) params.nickname = debouncedNickname.value.trim();
if (debouncedPhone.value.trim()) params.phone = debouncedPhone.value.trim();
if (debouncedRealName.value.trim()) params.realName = debouncedRealName.value.trim();
if (filterForm.auditStatus) params.auditStatus = filterForm.auditStatus;
if (filterForm.payStatus) params.payStatus = filterForm.payStatus;
return params;
};
// ===== 方法 =====
/** 查询(节流 500ms */
const handleSearch = useThrottleFn(async () => {
setLoading(true);
try {
const queryParams = buildQueryParams();
console.log('提现列表查询参数:', queryParams);
const res = await getWithdrawList(queryParams);
if (res.code == 200) {
console.log('提现列表查询结果:', { total: res.data.total, count: res.data.list.length });
setDataSource(res.data.list);
setPagination({ ...pagination.value, total: res.data.total });
}
} catch {
// 网络层已统一提示
} finally {
setLoading(false);
}
}, 500);
const handleSearch = () => fetchList();
/** 重置(重置后自动查询) */
const handleReset = useThrottleFn(() => {
@@ -181,14 +188,16 @@ export function useWithdrawModel() {
filterForm.realName = '';
filterForm.auditStatus = '';
filterForm.payStatus = '';
setPagination({ current: 1, pageSize: 10, total: 0 });
setDataSource([]);
setTimeout(() => handleSearch(), 350);
pagination.reset();
setTimeout(fetchList, 350);
}, 500);
const handlePageChange = (page: number, pageSize: number) => {
setPagination({ ...pagination.value, current: page, pageSize });
handleSearch();
pagination.setCurrent(page);
if (pageSize !== (pagination as any).pageSize.value) {
pagination.setPageSize(pageSize);
}
setTimeout(fetchList, 0);
};
/** 打开审核弹窗 */
+4 -9
View File
@@ -1,4 +1,4 @@
import { defineComponent, onMounted } from 'vue';
import { defineComponent } from 'vue';
import { Button, Input, Table, DatePicker, Form, Space, Select, Pagination } from 'ant-design-vue';
import { useLogModel, ACTION_TYPE_OPTIONS, ACTION_TYPE_MAP } from './model/useLogModel';
import { useContainerSize } from '@/hooks';
@@ -35,11 +35,6 @@ export default defineComponent({
const { containerRef, height } = useContainerSize({ headerOffset: 55 });
// 首次进入自动加载
onMounted(() => {
handleSearch();
});
return () => (
<div class={pageStyles.containerMain}>
{/* ===== 筛选区 ===== */}
@@ -102,9 +97,9 @@ export default defineComponent({
{/* 独立分页 */}
<div class={pageStyles.pagination}>
<Pagination
current={pagination.value.current}
pageSize={pagination.value.pageSize}
total={pagination.value.total}
current={(pagination as any).current.value}
pageSize={(pagination as any).pageSize.value}
total={(pagination as any).total.value}
showSizeChanger
showTotal={(total: number) => `${total}`}
onChange={handlePageChange}
+55 -45
View File
@@ -1,10 +1,15 @@
import { computed, reactive, toRef, Ref } from 'vue';
import dayjs from 'dayjs';
import { useState, useDebounce, useThrottleFn } from '@/hooks';
import { useDebounce, useThrottleFn } from '@/hooks';
import { useRequest } from '@/hooks/useRequest';
import { useEffect } from '@/hooks/useEffect';
import { usePagination } from '@/hooks/usePagination';
import {
getOperationLogList,
type SysOperationLogVO,
type OperationLogQueryParams,
type PageData,
type ApiResult,
} from './services';
// ============================================================
@@ -49,33 +54,14 @@ export function useLogModel() {
{ delay: 300 },
);
// ===== 表格状态 =====
const [loading, setLoading] = useState<boolean>(false);
const [dataSource, setDataSource] = useState<SysOperationLogVO[]>([]);
const [pagination, setPagination] = useState({ current: 1, pageSize: 10, total: 0 });
// ===== 表格列配置(字段名对齐 API =====
const columns = [
{ title: '操作类型', dataIndex: 'type', key: 'type', width: 140 },
{ title: '操作人', dataIndex: 'nickname', key: 'nickname', width: 140 },
{ title: '操作内容', dataIndex: 'content', key: 'content', minWidth: 360 },
{ title: '操作时间', dataIndex: 'createDate', key: 'createDate', width: 180 },
];
// ===== 计算属性 =====
const hasFilter = computed(() => {
return (
filterForm.actionTimeRange !== null ||
filterForm.actionType !== '' ||
debouncedOperator.value.trim() !== ''
);
});
// ===== 分页 =====
const pagination = usePagination({ defaultCurrent: 1, defaultPageSize: 10 });
// ===== 构建 API 查询参数 =====
const buildQueryParams = (): OperationLogQueryParams => {
const params: OperationLogQueryParams = {
page: String(pagination.value.current),
limit: String(pagination.value.pageSize),
page: String((pagination as any).current.value),
limit: String((pagination as any).pageSize.value),
};
// 时间范围 → dateBegin / dateEnd
@@ -98,38 +84,62 @@ export function useLogModel() {
return params;
};
// ===== 数据请求 =====
const {
data,
loading,
run: fetchList,
} = useRequest<ApiResult<PageData<SysOperationLogVO>>>(
() => getOperationLogList(buildQueryParams()),
{ refreshDeps: [], formatResult: (res) => res },
);
const dataSource = computed(() => {
const res = data.value;
return res ? (res as any).data?.list || [] : [];
});
useEffect(() => {
const total = (data.value as any)?.data?.total;
if (total !== undefined) pagination.setTotal(total);
}, [data]);
// ===== 表格列配置(字段名对齐 API =====
const columns = [
{ title: '操作类型', dataIndex: 'type', key: 'type', width: 140 },
{ title: '操作人', dataIndex: 'nickname', key: 'nickname', width: 140 },
{ title: '操作内容', dataIndex: 'content', key: 'content', minWidth: 360 },
{ title: '操作时间', dataIndex: 'createDate', key: 'createDate', width: 180 },
];
// ===== 计算属性 =====
const hasFilter = computed(() => {
return (
filterForm.actionTimeRange !== null ||
filterForm.actionType !== '' ||
debouncedOperator.value.trim() !== ''
);
});
// ===== 方法 =====
/** 查询(节流 500ms */
const handleSearch = useThrottleFn(async () => {
setLoading(true);
try {
const queryParams = buildQueryParams();
const res = await getOperationLogList(queryParams);
if (res.code == 200) {
setDataSource(res.data.list);
setPagination({ ...pagination.value, total: res.data.total });
}
} catch {
// 网络层已统一提示
} finally {
setLoading(false);
}
}, 500);
const handleSearch = () => fetchList();
/** 重置(重置后自动查询) */
const handleReset = useThrottleFn(() => {
filterForm.actionTimeRange = null;
filterForm.actionType = '';
filterForm.searchOperator = '';
setPagination({ current: 1, pageSize: 10, total: 0 });
setDataSource([]);
setTimeout(() => handleSearch(), 350);
pagination.reset();
setTimeout(fetchList, 350);
}, 500);
const handlePageChange = (page: number, pageSize: number) => {
setPagination({ ...pagination.value, current: page, pageSize });
handleSearch();
pagination.setCurrent(page);
if (pageSize !== (pagination as any).pageSize.value) {
pagination.setPageSize(pageSize);
}
setTimeout(fetchList, 0);
};
return {
@@ -143,7 +143,6 @@ export default defineComponent({
columns={columns}
dataSource={dataSource.value}
loading={loading.value}
scroll={{ y: 300 }}
size="small"
pagination={false}
bordered
@@ -164,7 +163,6 @@ export default defineComponent({
pageSize={pagination.value.pageSize}
total={pagination.value.total}
size="small"
showSizeChanger
showTotal={(total: number) => `${total}`}
onChange={handlePageChange}
/>
+4 -9
View File
@@ -1,4 +1,4 @@
import { defineComponent, onMounted } from 'vue';
import { defineComponent } from 'vue';
import { Button, Input, Table, Form, Space, Select, Modal, Pagination } from 'ant-design-vue';
import type { ModalProps } from 'ant-design-vue';
import { useRoleModel } from './model/useRoleModel';
@@ -114,11 +114,6 @@ export default defineComponent({
},
];
// ===== 初始化 =====
onMounted(() => {
handleSearch();
});
return () => (
<div class={pageStyles.containerMain}>
{/* ===== 筛选区 ===== */}
@@ -173,9 +168,9 @@ export default defineComponent({
{/* 分页 */}
<div class={pageStyles.pagination}>
<Pagination
current={pagination.value.current}
pageSize={pagination.value.pageSize}
total={pagination.value.total}
current={(pagination as any).current.value}
pageSize={(pagination as any).pageSize.value}
total={(pagination as any).total.value}
showSizeChanger
showTotal={(total: number) => `${total}`}
onChange={handlePageChange}
+47 -43
View File
@@ -1,6 +1,9 @@
import { computed, reactive, toRef, Ref } from 'vue';
import { message } from 'ant-design-vue';
import { useState, useDebounce, useThrottleFn } from '@/hooks';
import { useRequest } from '@/hooks/useRequest';
import { useEffect } from '@/hooks/useEffect';
import { usePagination } from '@/hooks/usePagination';
import {
getRoleList,
saveRole,
@@ -8,6 +11,8 @@ import {
deleteRole,
type TournamentAdminRolePageVO,
type RoleListQueryParams,
type PageData,
type ApiResult,
} from './services';
// ============================================================
@@ -27,10 +32,38 @@ export function useRoleModel() {
delay: 300,
});
// ===== 表格状态 =====
const [loading, setLoading] = useState<boolean>(false);
const [dataSource, setDataSource] = useState<TournamentAdminRolePageVO[]>([]);
const [pagination, setPagination] = useState({ current: 1, pageSize: 10, total: 0 });
// ===== 分页 =====
const pagination = usePagination({ defaultCurrent: 1, defaultPageSize: 10 });
// ===== 构建 API 查询参数 =====
const buildQueryParams = (): RoleListQueryParams => {
const params: RoleListQueryParams = {
page: String((pagination as any).current.value),
limit: String((pagination as any).pageSize.value),
};
if (filterForm.name.trim()) params.name = filterForm.name.trim();
return params;
};
// ===== 数据请求 =====
const {
data,
loading,
run: fetchList,
} = useRequest<ApiResult<PageData<TournamentAdminRolePageVO>>>(
() => getRoleList(buildQueryParams()),
{ refreshDeps: [], formatResult: (res) => res },
);
const dataSource = computed(() => {
const res = data.value;
return res ? (res as any).data?.list || [] : [];
});
useEffect(() => {
const total = (data.value as any)?.data?.total;
if (total !== undefined) pagination.setTotal(total);
}, [data]);
// ===== 弹窗状态 =====
const [formVisible, setFormVisible] = useState<boolean>(false);
@@ -55,53 +88,27 @@ export function useRoleModel() {
{ title: '创建时间', dataIndex: 'createTime', key: 'createTime', width: 200 },
];
// ===== 构建 API 查询参数 =====
const buildQueryParams = (): RoleListQueryParams => {
const params: RoleListQueryParams = {
page: String(pagination.value.current),
limit: String(pagination.value.pageSize),
};
if (filterForm.name.trim()) params.name = filterForm.name.trim();
return params;
};
// ===== 计算属性 =====
const hasFilter = computed(() => debouncedName.value.trim() !== '');
const isEdit = computed(() => editingRecord.value !== null);
// ===== 方法 =====
/** 查询(节流 500ms */
const handleSearch = useThrottleFn(async () => {
setLoading(true);
try {
const queryParams = buildQueryParams();
console.log('角色列表查询参数:', queryParams);
const res = await getRoleList(queryParams);
if (res.code == 200) {
setDataSource(res.data.list);
setPagination({ ...pagination.value, total: res.data.total });
} else {
message.error(res.msg || '查询失败');
}
} catch (error: any) {
console.error('角色列表查询失败:', error);
} finally {
setLoading(false);
}
}, 500);
const handleSearch = () => fetchList();
/** 重置(重置后自动查询) */
const handleReset = useThrottleFn(() => {
filterForm.name = '';
setPagination({ current: 1, pageSize: 10, total: 0 });
setDataSource([]);
setTimeout(() => handleSearch(), 350);
pagination.reset();
setTimeout(fetchList, 350);
}, 500);
const handlePageChange = (page: number, pageSize: number) => {
setPagination({ ...pagination.value, current: page, pageSize });
handleSearch();
pagination.setCurrent(page);
if (pageSize !== (pagination as any).pageSize.value) {
pagination.setPageSize(pageSize);
}
setTimeout(fetchList, 0);
};
/** 新增 */
@@ -146,7 +153,7 @@ export function useRoleModel() {
if (res.code == 200) {
message.success(isEdit.value ? '编辑成功' : '新增成功');
handleCloseForm();
handleSearch();
fetchList();
} else {
message.error(res.msg || '操作失败');
}
@@ -159,19 +166,16 @@ export function useRoleModel() {
/** 删除 */
const handleDelete = useThrottleFn(async (record: any) => {
setLoading(true);
try {
const res = await deleteRole(record.roleId);
if (res.code == 200) {
setDataSource(dataSource.value.filter((item: any) => item.roleId !== record.roleId));
message.success('删除成功');
fetchList();
} else {
message.error(res.msg || '删除失败');
}
} catch {
// 网络层已统一提示,不再重复 message.error
} finally {
setLoading(false);
}
}, 500);
@@ -1,8 +1,8 @@
import { defineComponent, ref, reactive } from 'vue';
import { useEffect, useState } from '@/hooks';
import { Modal, Form, Input, Select, Button } from 'ant-design-vue';
import { getRoleList, type TournamentAdminRolePageVO } from '../../roles/model/services';
import {
ROLE_FORM_OPTIONS,
PASSWORD_PLACEHOLDER,
realNameRules,
phoneRules,
@@ -55,12 +55,84 @@ export default defineComponent({
/** 编辑模式下是否处于"重置密码"状态 */
const [isResetting, setIsResetting] = useState(false);
// ---- 角色搜索状态 ----
const roleKeyword = ref('');
const [roleOptions, setRoleOptions] = useState<{ value: string; label: string }[]>([]);
const [roleSearchLoading, setRoleSearchLoading] = useState(false);
/** 手动防抖计时器 */
let roleSearchTimer: ReturnType<typeof setTimeout> | null = null;
/** 已加载的全量角色缓存 */
const [allRoleOptions, setAllRoleOptions] = useState<{ value: string; label: string }[]>([]);
// ---- 角色搜索 API 调用(用户主动搜索,带 loading) ----
const doRoleSearch = async (keyword: string) => {
setRoleSearchLoading(true);
try {
const params: Record<string, string> = { page: '1', limit: '999', name: keyword.trim() };
const res = await getRoleList(params);
if (res.code == 200) {
const list: TournamentAdminRolePageVO[] = res.data?.list || [];
setRoleOptions(
list.map((item) => ({
value: String(item.roleId),
label: item.roleName,
})),
);
}
} catch (e) {
console.error('[UserForm] 搜索角色失败:', e);
} finally {
setRoleSearchLoading(false);
}
};
/** 初始加载全量角色(弹窗打开时调用,不带 loading,不覆盖已选值) */
const fetchInitialRoles = async () => {
try {
const res = await getRoleList({ page: '1', limit: '999' });
if (res.code == 200) {
const list: TournamentAdminRolePageVO[] = res.data?.list || [];
const opts = list.map((item) => ({
value: String(item.roleId),
label: item.roleName,
}));
// 确保已选角色在列表中
const selectedId = formData.roleId;
if (selectedId && !opts.some((o) => o.value === selectedId)) {
const cached = allRoleOptions.value.find((o) => o.value === selectedId);
if (cached) opts.unshift(cached);
}
setAllRoleOptions(opts);
setRoleOptions(opts);
}
} catch (e) {
console.error('[UserForm] 加载角色列表失败:', e);
}
};
/** 角色搜索输入(带 350ms 手动防抖) */
const onRoleSearch = (value: string) => {
roleKeyword.value = value;
if (roleSearchTimer) clearTimeout(roleSearchTimer);
if (!value.trim()) {
// 清空搜索:恢复全量角色
setRoleOptions(allRoleOptions.value);
return;
}
roleSearchTimer = setTimeout(() => {
doRoleSearch(value);
}, 350);
};
/** 根据 record 初始化表单 */
const initFormFromRecord = (record: any) => {
if (!record) {
// 新增:预填默认密码 + 确认密码
Object.assign(formData, getAddForm());
setIsResetting(false);
roleKeyword.value = '';
fetchInitialRoles();
return;
}
const fresh = getDefaultForm();
@@ -71,6 +143,22 @@ export default defineComponent({
roleId: record.roleId || undefined,
});
setIsResetting(false);
// 编辑模式:先回显已选角色,再静默加载全量角色
roleKeyword.value = '';
if (record.roleId) {
const selectedOpt = {
value: String(record.roleId),
label: record.roleName || String(record.roleId),
};
setAllRoleOptions([selectedOpt]);
setRoleOptions([selectedOpt]);
} else {
setAllRoleOptions([]);
setRoleOptions([]);
}
// 后台静默加载全量角色列表(API 返回后合并到选项,loading 为 false 不影响显示)
fetchInitialRoles();
};
/** 监听 visible 变化重置表单 */
@@ -82,6 +170,14 @@ export default defineComponent({
}
}, [() => props.visible]);
/** 关闭时清理防抖计时器 */
useEffect(() => {
if (!props.visible && roleSearchTimer) {
clearTimeout(roleSearchTimer);
roleSearchTimer = null;
}
}, [() => props.visible]);
/** 点击"重置密码" */
const handleResetPassword = () => {
setIsResetting(true);
@@ -172,9 +268,19 @@ export default defineComponent({
</Form.Item>
<Form.Item label="角色" name="roleId" rules={roleRules} class={styles.col}>
<Select
options={ROLE_FORM_OPTIONS as any}
placeholder="请选择角色"
v-model:value={formData.roleId}
showSearch
filterOption={false}
allowClear={!roleSearchLoading.value}
loading={roleSearchLoading.value}
placeholder="请输入关键字搜索角色"
options={roleOptions.value as any}
onSearch={onRoleSearch}
onFocus={() => {
if (formData.roleId && roleOptions.value.length === 0) {
doRoleSearch(formData.roleId);
}
}}
/>
</Form.Item>
</div>
@@ -210,9 +316,19 @@ export default defineComponent({
{/* 角色:单独一行 */}
<Form.Item label="角色" name="roleId" rules={roleRules}>
<Select
options={ROLE_FORM_OPTIONS as any}
placeholder="请选择角色"
v-model:value={formData.roleId}
showSearch
filterOption={false}
allowClear={!roleSearchLoading.value}
loading={roleSearchLoading.value}
placeholder="请输入关键字搜索角色"
options={roleOptions.value as any}
onSearch={onRoleSearch}
onFocus={() => {
if (formData.roleId && roleOptions.value.length === 0) {
doRoleSearch(formData.roleId);
}
}}
style={{ width: '200px' }}
/>
</Form.Item>
-8
View File
@@ -14,14 +14,6 @@ import { isChinaPhone, isValidPassword, isValidRealName } from '@/utils/form';
/** 密码占位符(写死长度,避免暴露真实密码长度) */
export const PASSWORD_PLACEHOLDER = '********';
/** 角色下拉选项(表单使用,value 对应 API roleId */
export const ROLE_FORM_OPTIONS = [
{ value: '1', label: '超级管理员' },
{ value: '2', label: '赛事管理员' },
{ value: '3', label: '裁判' },
{ value: '4', label: '财务' },
] as const;
// ============================================================
// 真实姓名规则
// ============================================================
+7 -11
View File
@@ -1,4 +1,4 @@
import { defineComponent, onMounted } from 'vue';
import { defineComponent } from 'vue';
import {
Button,
Input,
@@ -10,7 +10,7 @@ import {
Pagination,
message,
} from 'ant-design-vue';
import { useUserModel, ROLE_OPTIONS, USER_STATUS_OPTIONS } from './model/useUserModel';
import { useUserModel, USER_STATUS_OPTIONS } from './model/useUserModel';
import { toggleUserActive, saveUser, updateUser } from './model/services';
import { useState, useThrottleFn, useContainerSize } from '@/hooks';
import UserFormModal from './components/UserFormModal';
@@ -60,6 +60,7 @@ export default defineComponent({
dataSource,
columns,
pagination,
roleOptions,
handleSearch,
handleReset,
handlePageChange,
@@ -175,11 +176,6 @@ export default defineComponent({
},
];
// ===== 初始化 =====
onMounted(() => {
handleSearch();
});
return () => (
<div class={pageStyles.containerMain}>
{/* ===== 筛选区 ===== */}
@@ -198,7 +194,7 @@ export default defineComponent({
<Form.Item label="角色" name="roleId">
<Select
value={filterForm.roleId}
options={ROLE_OPTIONS as any}
options={roleOptions.value as any}
style={{ width: '140px' }}
allowClear
placeholder="全部"
@@ -252,9 +248,9 @@ export default defineComponent({
{/* 独立分页 */}
<div class={pageStyles.pagination}>
<Pagination
current={pagination.value.current}
pageSize={pagination.value.pageSize}
total={pagination.value.total}
current={(pagination as any).current.value}
pageSize={(pagination as any).pageSize.value}
total={(pagination as any).total.value}
showSizeChanger
showTotal={(total: number) => `${total}`}
onChange={handlePageChange}
+3 -3
View File
@@ -29,9 +29,9 @@ export type {
// URL 常量
// ============================================================
const userPage = '/admin/sys/user/page';
const userSave = '/sys/user/save';
const userUpdate = '/sys/user/update';
const userActive = '/sys/user/active';
const userSave = '/admin/sys/user/save';
const userUpdate = '/admin/sys/user/update';
const userActive = '/admin/sys/user/active';
const userUpdatePwd = '/sys/user/updatepwd';
// ============================================================
+78 -55
View File
@@ -1,22 +1,23 @@
import { computed, reactive, toRef, Ref, h } from 'vue';
import { computed, reactive, toRef, Ref, h, ref } from 'vue';
import { message } from 'ant-design-vue';
import { StatusTag, type StatusTagTone } from '@/components';
import { useState, useDebounce, useThrottleFn } from '@/hooks';
import { getUserList, type TournamentAdminUserVO, type UserListQueryParams } from './services';
import { useDebounce, useThrottleFn } from '@/hooks';
import { useRequest } from '@/hooks/useRequest';
import { useEffect } from '@/hooks/useEffect';
import { usePagination } from '@/hooks/usePagination';
import {
getUserList,
type TournamentAdminUserVO,
type UserListQueryParams,
type PageData,
type ApiResult,
} from './services';
import { getRoleList, type TournamentAdminRolePageVO } from '../../roles/model/services';
// ============================================================
// 常量
// ============================================================
/** 角色选项(筛选) */
export const ROLE_OPTIONS = [
{ value: '', label: '全部' },
{ value: '1', label: '超级管理员' },
{ value: '2', label: '赛事管理员' },
{ value: '3', label: '裁判' },
{ value: '4', label: '财务' },
] as const;
/** 状态选项(筛选) */
export const USER_STATUS_OPTIONS = [
{ value: '', label: '全部' },
@@ -45,20 +46,66 @@ export function useUserModel() {
status: '' as '' | '0' | '1',
});
// ===== 动态角色选项 =====
const roleOptions = ref<{ value: string; label: string }[]>([{ value: '', label: '全部' }]);
/** 加载全部角色列表(用于筛选下拉) */
const fetchRoleOptions = async () => {
try {
const res = await getRoleList({ page: '1', limit: '999' });
if (res.code == 200) {
const list: TournamentAdminRolePageVO[] = res.data?.list || [];
const opts = list.map((item) => ({
value: String(item.roleId),
label: item.roleName,
}));
roleOptions.value = [{ value: '', label: '全部' }, ...opts];
}
} catch (e) {
console.error('[UserModel] 加载角色列表失败:', e);
}
};
// 文本筛选防抖 300ms
const { debouncedValue: debouncedText } = useDebounce(toRef(filterForm, 'text') as Ref<string>, {
delay: 300,
});
// ===== 表格状态 =====
const [loading, setLoading] = useState<boolean>(false);
const [dataSource, setDataSource] = useState<TournamentAdminUserVO[]>([]);
const [pagination, setPagination] = useState({
current: 1,
pageSize: 10,
total: 0,
// ===== 分页 =====
const pagination = usePagination({ defaultCurrent: 1, defaultPageSize: 10 });
// ===== 构建 API 查询参数 =====
const buildQueryParams = (): UserListQueryParams => {
const params: UserListQueryParams = {
page: String((pagination as any).current.value),
limit: String((pagination as any).pageSize.value),
};
if (filterForm.text.trim()) params.text = filterForm.text.trim();
if (filterForm.roleId) params.roleId = filterForm.roleId;
if (filterForm.status) params.status = filterForm.status;
return params;
};
// ===== 数据请求 =====
const {
data,
loading,
run: fetchList,
} = useRequest<ApiResult<PageData<TournamentAdminUserVO>>>(
() => getUserList(buildQueryParams()),
{ refreshDeps: [], formatResult: (res) => res },
);
const dataSource = computed(() => {
const res = data.value;
return res ? (res as any).data?.list || [] : [];
});
useEffect(() => {
const total = (data.value as any)?.data?.total;
if (total !== undefined) pagination.setTotal(total);
}, [data]);
// ===== 表格列配置 =====
const columns = [
{ title: '姓名', dataIndex: 'realName', key: 'realName', width: 120 },
@@ -78,18 +125,6 @@ export function useUserModel() {
{ title: '创建时间', dataIndex: 'createTime', key: 'createTime', width: 180 },
];
// ===== 构建 API 查询参数 =====
const buildQueryParams = (): UserListQueryParams => {
const params: UserListQueryParams = {
page: String(pagination.value.current),
limit: String(pagination.value.pageSize),
};
if (filterForm.text.trim()) params.text = filterForm.text.trim();
if (filterForm.roleId) params.roleId = filterForm.roleId;
if (filterForm.status) params.status = filterForm.status;
return params;
};
// ===== 计算属性 =====
const hasFilter = computed(() => {
return (
@@ -99,49 +134,37 @@ export function useUserModel() {
// ===== 方法 =====
/** 查询(节流 500ms */
const handleSearch = useThrottleFn(async () => {
setLoading(true);
try {
const queryParams = buildQueryParams();
console.log('用户列表查询参数:', queryParams);
const res = await getUserList(queryParams);
if (res.code == 200) {
setDataSource(res.data.list);
setPagination({ ...pagination.value, total: res.data.total });
} else {
message.error(res.msg || '查询失败');
}
} catch (error: any) {
console.error('用户列表查询失败:', error);
} finally {
setLoading(false);
}
}, 500);
const handleSearch = () => fetchList();
/** 重置(节流 500ms,重置后自动查询) */
const handleReset = useThrottleFn(() => {
filterForm.text = '';
filterForm.roleId = '';
filterForm.status = '';
setPagination({ current: 1, pageSize: 10, total: 0 });
setDataSource([]);
pagination.reset();
// 等待 debounce(300ms) 生效后自动查询
setTimeout(() => handleSearch(), 350);
setTimeout(fetchList, 350);
}, 500);
/** 分页变更 */
const handlePageChange = (page: number, pageSize: number) => {
setPagination({ ...pagination.value, current: page, pageSize });
handleSearch();
pagination.setCurrent(page);
if (pageSize !== (pagination as any).pageSize.value) {
pagination.setPageSize(pageSize);
}
setTimeout(fetchList, 0);
};
// ===== 初始化:加载角色选项 =====
fetchRoleOptions();
return {
filterForm,
loading,
dataSource,
columns,
pagination,
roleOptions,
hasFilter,
handleSearch,
handleReset,