refactor(ui): 界面升级与代码规范优化
This commit is contained in:
@@ -5,7 +5,5 @@ export type { DictMessage, DictChangeMessage, DictChangeCallback } from "./sse";
|
||||
|
||||
// 表格相关
|
||||
export { useTableSelection } from "./useTableSelection";
|
||||
|
||||
// 最近访问菜单
|
||||
export { useRecentMenus } from "./useRecentMenus";
|
||||
export type { RecentMenuItem } from "./useRecentMenus";
|
||||
export { usePageTable } from "./usePageTable";
|
||||
export type { UsePageTableOptions, UsePageTableReturn } from "./usePageTable";
|
||||
|
||||
114
src/composables/usePageTable.ts
Normal file
114
src/composables/usePageTable.ts
Normal file
@@ -0,0 +1,114 @@
|
||||
import { reactive, ref, type Ref } from "vue";
|
||||
|
||||
import type { BaseQueryParams, PageResult } from "@/api/common";
|
||||
|
||||
/**
|
||||
* 分页列表页通用状态管理
|
||||
*
|
||||
* 只管理请求、分页数据和查询参数,不处理勾选、弹窗、表单
|
||||
*/
|
||||
export function usePageTable<T, Q extends BaseQueryParams = BaseQueryParams>(
|
||||
options: UsePageTableOptions<T, Q>
|
||||
): UsePageTableReturn<T, Q> {
|
||||
const { initialParams, request, onBeforeReset } = options;
|
||||
|
||||
const loading = ref(false);
|
||||
const list = ref<T[]>([]) as Ref<T[]>;
|
||||
const total = ref(0);
|
||||
const params = reactive({ ...initialParams }) as Q;
|
||||
|
||||
/**
|
||||
* 拉取当前查询参数对应的分页数据
|
||||
*
|
||||
* 只负责请求和回填,不处理弹窗、路由或消息提示
|
||||
*/
|
||||
async function fetchData(): Promise<void> {
|
||||
loading.value = true;
|
||||
try {
|
||||
const data = await request(params);
|
||||
list.value = data.list ?? [];
|
||||
total.value = data.total ?? 0;
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 回到第一页并查询
|
||||
*/
|
||||
function handleQuery(): void {
|
||||
params.pageNum = 1;
|
||||
fetchData();
|
||||
}
|
||||
|
||||
/**
|
||||
* 恢复初始查询参数
|
||||
*
|
||||
* 保持响应式引用不变,不触发查询
|
||||
*/
|
||||
function resetParams(): void {
|
||||
Object.assign(params, initialParams);
|
||||
}
|
||||
|
||||
/**
|
||||
* 恢复初始查询参数并重新查询
|
||||
*/
|
||||
function handleResetQuery(): void {
|
||||
onBeforeReset?.();
|
||||
resetParams();
|
||||
fetchData();
|
||||
}
|
||||
|
||||
return {
|
||||
loading,
|
||||
list,
|
||||
total,
|
||||
params,
|
||||
fetchData,
|
||||
handleQuery,
|
||||
handleResetQuery,
|
||||
resetParams,
|
||||
};
|
||||
}
|
||||
|
||||
export interface UsePageTableOptions<T, Q extends BaseQueryParams> {
|
||||
/** 初始查询参数,同时作为重置基准 */
|
||||
initialParams: Q;
|
||||
/**
|
||||
* 分页请求函数
|
||||
*/
|
||||
request: (params: Q) => Promise<PageResult<T>>;
|
||||
/**
|
||||
* 重置查询前的回调
|
||||
*
|
||||
* 常用于同步重置搜索表单字段
|
||||
*/
|
||||
onBeforeReset?: () => void;
|
||||
}
|
||||
|
||||
export interface UsePageTableReturn<T, Q extends BaseQueryParams> {
|
||||
/** 加载状态 */
|
||||
loading: Ref<boolean>;
|
||||
/** 当前页数据列表 */
|
||||
list: Ref<T[]>;
|
||||
/** 数据总条数 */
|
||||
total: Ref<number>;
|
||||
/** 当前查询参数 */
|
||||
params: Q;
|
||||
/**
|
||||
* 拉取分页数据
|
||||
*/
|
||||
fetchData: () => Promise<void>;
|
||||
/**
|
||||
* 回到第一页并查询
|
||||
*/
|
||||
handleQuery: () => void;
|
||||
/**
|
||||
* 恢复初始参数并重新查询
|
||||
*/
|
||||
handleResetQuery: () => void;
|
||||
/**
|
||||
* 恢复初始参数但不触发查询
|
||||
*/
|
||||
resetParams: () => void;
|
||||
}
|
||||
@@ -1,99 +0,0 @@
|
||||
import { ref } from "vue";
|
||||
|
||||
/**
|
||||
* 最近访问菜单项
|
||||
*/
|
||||
export interface RecentMenuItem {
|
||||
path: string;
|
||||
title: string;
|
||||
icon?: string;
|
||||
visitedAt: number;
|
||||
}
|
||||
|
||||
const STORAGE_KEY = "recent_menus";
|
||||
const MAX_COUNT = 8;
|
||||
|
||||
// 全局状态
|
||||
const recentMenus = ref<RecentMenuItem[]>([]);
|
||||
|
||||
/**
|
||||
* 从 localStorage 加载数据
|
||||
*/
|
||||
function loadFromStorage(): RecentMenuItem[] {
|
||||
try {
|
||||
const data = localStorage.getItem(STORAGE_KEY);
|
||||
return data ? JSON.parse(data) : [];
|
||||
} catch {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存到 localStorage
|
||||
*/
|
||||
function saveToStorage(menus: RecentMenuItem[]) {
|
||||
localStorage.setItem(STORAGE_KEY, JSON.stringify(menus));
|
||||
}
|
||||
|
||||
// 初始化
|
||||
recentMenus.value = loadFromStorage();
|
||||
|
||||
/**
|
||||
* 最近访问菜单 composable
|
||||
*/
|
||||
export function useRecentMenus() {
|
||||
/**
|
||||
* 清空所有记录
|
||||
*/
|
||||
function clearRecentMenus() {
|
||||
recentMenus.value = [];
|
||||
localStorage.removeItem(STORAGE_KEY);
|
||||
}
|
||||
|
||||
/**
|
||||
* 格式化访问时间
|
||||
*/
|
||||
function formatVisitTime(timestamp: number): string {
|
||||
const now = Date.now();
|
||||
const diff = now - timestamp;
|
||||
|
||||
if (diff < 60000) return "刚刚";
|
||||
if (diff < 3600000) return `${Math.floor(diff / 60000)}分钟前`;
|
||||
if (diff < 86400000) return `${Math.floor(diff / 3600000)}小时前`;
|
||||
if (diff < 604800000) return `${Math.floor(diff / 86400000)}天前`;
|
||||
|
||||
const date = new Date(timestamp);
|
||||
return `${date.getMonth() + 1}-${date.getDate()}`;
|
||||
}
|
||||
|
||||
return {
|
||||
recentMenus,
|
||||
clearRecentMenus,
|
||||
formatVisitTime,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加最近访问记录(全局方法,供路由守卫调用)
|
||||
*/
|
||||
export function addRecentMenu(path: string, title: string, icon?: string) {
|
||||
if (!path || !title) return;
|
||||
|
||||
// 过滤掉不需要记录的路径
|
||||
const excludePaths = ["/dashboard", "/redirect", "/404", "/401", "/login", "/"];
|
||||
if (excludePaths.some((p) => path === p || path.startsWith(p + "/"))) return;
|
||||
|
||||
// 移除已存在的相同路径
|
||||
const filtered = recentMenus.value.filter((item) => item.path !== path);
|
||||
|
||||
// 添加到开头
|
||||
const newItem: RecentMenuItem = {
|
||||
path,
|
||||
title,
|
||||
icon,
|
||||
visitedAt: Date.now(),
|
||||
};
|
||||
|
||||
recentMenus.value = [newItem, ...filtered].slice(0, MAX_COUNT);
|
||||
saveToStorage(recentMenus.value);
|
||||
}
|
||||
@@ -1,56 +1,55 @@
|
||||
import { computed, ref } from "vue";
|
||||
|
||||
/**
|
||||
* 表格行选择 Composable
|
||||
* 表格行选择 Composable。
|
||||
*
|
||||
* @description 提供统一的表格行选择逻辑,包括选中ID管理和清空选择
|
||||
* @template T 数据项类型,必须包含 id 属性
|
||||
* @returns 返回选中的ID列表、选择变化处理函数、清空选择函数
|
||||
* 提供统一的表格行选择逻辑,包括选中 ID 管理和清空选择。
|
||||
*
|
||||
* 规则:
|
||||
*
|
||||
* - 不和分页状态合并。
|
||||
* - 不和弹窗状态合并。
|
||||
* - 不和 API 请求合并。
|
||||
*
|
||||
* @template T 数据项类型,必须包含可选 `id` 属性
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* const { selectedIds, handleSelectionChange, clearSelection } = useTableSelection<UserVO>();
|
||||
* ```ts
|
||||
* const { selectedIds, hasSelection, handleSelectionChange } = useTableSelection<RoleItem>();
|
||||
* ```
|
||||
*/
|
||||
export function useTableSelection<T extends { id: string | number }>() {
|
||||
/**
|
||||
* 选中的数据项ID列表
|
||||
*/
|
||||
export function useTableSelection<T extends { id?: string | number }>() {
|
||||
/** 选中的数据项 ID 列表。 */
|
||||
const selectedIds = ref<(string | number)[]>([]);
|
||||
|
||||
/** 选中数量。 */
|
||||
const selectedCount = computed(() => selectedIds.value.length);
|
||||
|
||||
/** 是否有选中项。 */
|
||||
const hasSelection = computed(() => selectedIds.value.length > 0);
|
||||
|
||||
/**
|
||||
* 表格选中项变化处理
|
||||
* 表格选中项变化处理。
|
||||
*
|
||||
* @param selection 选中的行数据列表
|
||||
*/
|
||||
function handleSelectionChange(selection: T[]): void {
|
||||
selectedIds.value = selection.map((item) => item.id);
|
||||
selectedIds.value = selection.flatMap((item) => (item.id ? [item.id] : []));
|
||||
}
|
||||
|
||||
/**
|
||||
* 清空选择
|
||||
*/
|
||||
function clearSelection(): void {
|
||||
selectedIds.value = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查指定ID是否被选中
|
||||
* @param id 要检查的ID
|
||||
* @returns 是否被选中
|
||||
* 检查指定 ID 是否被选中。
|
||||
*
|
||||
* @param id 要检查的 ID
|
||||
*/
|
||||
function isSelected(id: string | number): boolean {
|
||||
return selectedIds.value.includes(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取选中的数量
|
||||
*/
|
||||
const selectedCount = computed(() => selectedIds.value.length);
|
||||
|
||||
/**
|
||||
* 是否有选中项
|
||||
*/
|
||||
const hasSelection = computed(() => selectedIds.value.length > 0);
|
||||
/** 清空选中 ID。注意:不会清空表格 UI 上的勾选状态,需页面自行调用表格实例的 `clearSelection`。 */
|
||||
function clearSelection(): void {
|
||||
selectedIds.value = [];
|
||||
}
|
||||
|
||||
return {
|
||||
selectedIds,
|
||||
|
||||
Reference in New Issue
Block a user