Files
cpms_operation_platform/src/hooks/usePagination.ts
T

186 lines
4.7 KiB
TypeScript
Raw Normal View History

2026-07-14 10:31:17 +08:00
/* eslint-disable @typescript-eslint/ban-ts-comment */
// src/hooks/usePagination.ts
import { computed, ref, watch, Ref, triggerRef } from 'vue';
/**
* 分页配置项
*/
export interface UsePaginationOptions {
/** 当前页码 (支持 v-model 双向绑定) */
current?: number;
/** 每页条数 (支持 v-model 双向绑定) */
pageSize?: number;
/** 数据总条数 (支持 v-model 双向绑定) */
total?: number;
/** 默认页码 (非受控模式下使用) */
defaultCurrent?: number;
/** 默认每页条数 (非受控模式下使用) */
defaultPageSize?: number;
/** 每页条数切换选项 (用于生成下拉菜单) */
pageSizeOptions?: number[];
/** 是否显示快速跳转 (可选功能) */
showQuickJumper?: boolean;
}
/**
* 分页操作 API
*/
export interface UsePaginationActions {
/** 设置当前页 */
setCurrent: (current: number) => void;
/** 设置每页条数 (会自动重置页码为 1) */
setPageSize: (size: number) => void;
/** 设置总条数 */
setTotal: (total: number) => void;
/** 下一页 */
next: () => void;
/** 上一页 */
prev: () => void;
/** 跳转到指定页 */
jump: (page: number) => void;
/** 重置到初始状态 */
reset: () => void;
/** 强制刷新 (用于某些极端响应式场景) */
refresh: () => void;
}
/**
* 分页计算属性
*/
export interface UsePaginationComputed {
/** 响应式请求参数对象 (给 API 用的) */
params: Ref<{ page: number; pageSize: number }>;
/** 总页数 */
totalPages: Ref<number>;
/** 是否有上一页 */
hasPrev: Ref<boolean>;
/** 是否有下一页 */
hasNext: Ref<boolean>;
/** 当前页起始索引 (用于显示 "显示 1-10 条") */
startIndex: Ref<number>;
/** 当前页结束索引 */
endIndex: Ref<number>;
/** 是否为第一页 */
isFirstPage: Ref<boolean>;
/** 是否为最后一页 */
isLastPage: Ref<boolean>;
}
export type UsePaginationReturn = UsePaginationOptions &
UsePaginationActions &
UsePaginationComputed;
export function usePagination(options: UsePaginationOptions = {}): UsePaginationReturn {
const { defaultCurrent = 1, defaultPageSize = 10, pageSizeOptions = [10, 20, 50, 100] } = options;
const currentRef = ref(options.current ?? defaultCurrent);
const pageSizeRef = ref(options.pageSize ?? defaultPageSize);
const totalRef = ref(options.total ?? 0);
watch(
() => options.current,
(val) => {
if (val !== undefined) currentRef.value = val;
},
);
watch(
() => options.pageSize,
(val) => {
if (val !== undefined) pageSizeRef.value = val;
},
);
watch(
() => options.total,
(val) => {
if (val !== undefined) totalRef.value = val;
},
);
const totalPages = computed(() => {
const total = totalRef.value;
const size = pageSizeRef.value;
return size === 0 ? 0 : Math.ceil(total / size);
});
const hasPrev = computed(() => currentRef.value > 1);
const hasNext = computed(() => currentRef.value < totalPages.value);
const isFirstPage = computed(() => currentRef.value === 1);
const isLastPage = computed(() => currentRef.value === totalPages.value);
const startIndex = computed(() =>
totalPages.value === 0 ? 0 : (currentRef.value - 1) * pageSizeRef.value + 1,
);
const endIndex = computed(() => Math.min(currentRef.value * pageSizeRef.value, totalRef.value));
const params = computed(() => ({
page: currentRef.value,
pageSize: pageSizeRef.value,
}));
const setCurrent = (page: number) => {
const safePage = Math.max(1, Math.min(page, totalPages.value || 1));
if (currentRef.value !== safePage) {
currentRef.value = safePage;
}
};
const setPageSize = (size: number) => {
if (pageSizeRef.value !== size) {
pageSizeRef.value = size;
currentRef.value = 1;
}
};
const setTotal = (total: number) => {
totalRef.value = total;
const maxPage = Math.max(1, Math.ceil(total / pageSizeRef.value));
if (currentRef.value > maxPage) {
currentRef.value = maxPage;
}
};
const next = () => setCurrent(currentRef.value + 1);
const prev = () => setCurrent(currentRef.value - 1);
const jump = (page: number) => {
if (!Number.isInteger(page) || page < 1) return;
setCurrent(page);
};
const reset = () => {
currentRef.value = defaultCurrent;
pageSizeRef.value = defaultPageSize;
};
const refresh = () => {
triggerRef(currentRef);
};
return {
// @ts-ignore
current: currentRef,
// @ts-ignore
pageSize: pageSizeRef,
// @ts-ignore
total: totalRef,
pageSizeOptions,
params,
totalPages,
hasPrev,
hasNext,
isFirstPage,
isLastPage,
startIndex,
endIndex,
setCurrent,
setPageSize,
setTotal,
next,
prev,
jump,
reset,
refresh,
};
}