import { computed, ref, toRaw, type Ref } from 'vue';
import { DEFAULT_SORT_CONFIG } from './const';
import type { Order, SortConfig, SortOption, SortState, StkTableColumn, UniqKey } from './types/index';
import { tableSort } from './utils/index';
/**
* 排序切换顺序
* 循环顺序:null → desc → asc → null → ...
*/
const SORT_SWITCH_ORDER: Order[] = [null, 'desc', 'asc'] as const;
/**
* 排序 Hook
* 管理表格排序状态和相关操作
* @param props 表格 props
* @param colKeyGen 列 key 生成函数
* @param tableHeaderLast 表头最后一行(叶子节点)
* @param dataSourceCopy 数据源副本 ref
* @param initDataSource 初始化数据源函数
* @param emits 事件发射函数
* @returns 排序相关状态和方法
*/
export function useSorter
>(
props: any,
emits: any,
colKeyGen: Ref<(col: StkTableColumn) => string>,
tableHeaderLast: Ref[]>,
dataSourceCopy: Ref,
initDataSource: (data?: DT[], option?: { forceSort?: boolean }) => void,
) {
/** 多列排序状态数组 */
const sortStates = ref[]>([]);
/** 是否启用多列排序 */
const isMultiSort = computed(() => props.sortConfig.multiSort ?? false);
/** 多列排序限制 */
const multiSortLimit = computed(() => props.sortConfig.multiSortLimit ?? 3);
/** 对外暴露:当前排序的列 key(只读计算属性) */
const sortCol = computed(() => sortStates.value[0]?.dataIndex);
/**
* 获取列的排序状态
*/
function getColumnSortState(colKey: UniqKey): SortState | undefined {
return sortStates.value[getSortStateIndex(colKey)] as SortState | undefined;
}
/**
* 获取列的排序状态索引
*/
function getSortStateIndex(colKey: UniqKey): number {
return sortStates.value.findIndex(s => s.key === colKey || s.dataIndex === colKey);
}
function getTableCol(state: { key?: SortState['key']; dataIndex: SortState['dataIndex'] }) {
return tableHeaderLast.value.find(c => (state.key && colKeyGen.value(c) === state.key) || c.dataIndex === state.dataIndex);
}
/**
* 获取排序列信息
*/
function getSortColumns(): { key: keyof DT | undefined; order: Order }[] {
return sortStates.value.map(s => ({ key: s.key || s.dataIndex, order: s.order }));
}
/**
* 添加或更新排序状态到 sortStates
* @param newState 新的排序状态
* @param mode '1' - 追加模式(多列排序),0 - 替换模式(单列排序)
*/
function addOrUpdateSortState(newState: SortState, mode?: 1 | 0) {
const existingIndex = getSortStateIndex(newState.key || newState.dataIndex);
if (existingIndex >= 0) {
// 移除已存在的相同列
sortStates.value.splice(existingIndex, 1);
}
if (mode && isMultiSort.value) {
// 多列排序模式:检查数量限制,然后添加到最前面
if (sortStates.value.length >= multiSortLimit.value) {
sortStates.value.pop();
}
sortStates.value.unshift(newState as any);
} else {
sortStates.value = [newState as any];
}
}
/**
* 更新排序状态(点击表头时调用)
*/
function updateSortState(col: StkTableColumn, sortConfig: SortConfig): Order {
const colKey = colKeyGen.value(col);
const existingIndex = getSortStateIndex(colKey);
let newOrder: Order;
const defaultSort = sortConfig.defaultSort;
if (existingIndex >= 0) {
const currentOrder = sortStates.value[existingIndex].order;
if (currentOrder && defaultSort && (defaultSort.key === colKey || defaultSort.dataIndex === col.dataIndex)) {
// If click default sort column
const defaultSwitchOrder = SORT_SWITCH_ORDER.filter(order => order !== null);
const currentIndex = defaultSwitchOrder.indexOf(currentOrder);
newOrder = defaultSwitchOrder[(currentIndex + 1) % defaultSwitchOrder.length];
} else {
// Click other column
const currentIndex = SORT_SWITCH_ORDER.indexOf(currentOrder);
newOrder = SORT_SWITCH_ORDER[(currentIndex + 1) % 3];
}
if (newOrder) {
const updatedState = { ...sortStates.value[existingIndex], order: newOrder };
addOrUpdateSortState(updatedState as any, 1);
} else {
sortStates.value.splice(existingIndex, 1); // Delete sort state
if (defaultSort?.order) {
// Has default sort
const defaultSortCol = getTableCol(defaultSort);
const { key, sortField, sortType } = defaultSortCol || {};
addOrUpdateSortState({ key, sortField, sortType, ...defaultSort }, 1);
}
}
} else {
newOrder = SORT_SWITCH_ORDER[1];
const newState: SortState = {
key: colKey,
dataIndex: col.dataIndex,
sortField: col.sortField,
sortType: col.sortType,
order: newOrder,
};
addOrUpdateSortState(newState, 1);
}
return newOrder;
}
/**
* 对数据源执行排序
* tableSort 内部会根据 sortChildren 配置自动处理树形递归排序
*/
function sortData(dataSource: DT[]): DT[] {
if (!sortStates.value.length) return dataSource;
const sortConfig = { ...DEFAULT_SORT_CONFIG, ...props.sortConfig };
let result = dataSource.slice();
// 从后往前排序,这样前面的排序优先级更高
for (let i = sortStates.value.length - 1; i >= 0; i--) {
const state = sortStates.value[i];
const col = getTableCol(state);
if (col && state.order) {
const colSortConfig = { ...sortConfig, ...col.sortConfig };
result = tableSort(col, state.order, result, colSortConfig);
}
}
return result;
}
/**
* 表头点击排序
*/
function onColumnSort(col: StkTableColumn | undefined | null) {
if (!col) {
console.warn('onColumnSort: not found col:', col);
return;
}
if (!col.sorter) {
// 点击表头触发的排序,如果列没有配置 sorter 则不处理。setSorter 触发的排序则保持通行。
return;
}
const sortConfig: SortConfig = { ...DEFAULT_SORT_CONFIG, ...props.sortConfig, ...col.sortConfig };
const order = updateSortState(col, sortConfig);
if (!props.sortRemote) {
initDataSource();
}
emits('sort-change', col, order, toRaw(dataSourceCopy.value), sortConfig);
}
/**
* 设置表头排序状态
*/
function setSorter(
colKey: string,
order: Order,
option: { sortOption?: SortOption; force?: boolean; silent?: boolean; sort?: boolean; append?: boolean } = {},
): DT[] {
const newOption = { silent: true, sortOption: null, sort: true, append: false, ...option };
const colKeyGenValue = colKeyGen.value;
let column: StkTableColumn | undefined;
if (order) {
column = newOption.sortOption || tableHeaderLast.value.find(it => colKeyGenValue(it) === colKey);
if (column) {
const newState: SortState = {
key: colKey,
dataIndex: column.dataIndex,
sortField: column.sortField,
sortType: column.sortType,
order,
};
const mode = newOption.append && isMultiSort.value ? 1 : 0;
addOrUpdateSortState(newState, mode);
}
} else {
sortStates.value = [];
}
if (newOption.sort && dataSourceCopy.value?.length) {
if (!props.sortRemote || newOption.force) {
initDataSource(props.dataSource, { forceSort: newOption.force });
}
}
if (!newOption.silent) {
if (!column) {
column = newOption.sortOption || tableHeaderLast.value.find(it => colKeyGenValue(it) === colKey);
}
if (column) {
emits('sort-change', column, order, toRaw(dataSourceCopy.value), props.sortConfig);
} else {
console.warn('Can not find column by key:', colKey);
}
}
return dataSourceCopy.value;
}
/**
* 重置排序器
*/
function resetSorter() {
sortStates.value = [];
initDataSource();
}
/**
* 处理默认排序
*/
function dealDefaultSorter() {
if (!props.sortConfig.defaultSort) return;
const { key, dataIndex, order, silent } = { silent: true, ...props.sortConfig.defaultSort };
setSorter((key || dataIndex) as string, order, { force: false, silent });
}
// 只返回需要在组件外部使用的方法和状态
return [sortStates, sortCol, onColumnSort, setSorter, resetSorter, getSortColumns, dealDefaultSorter, getColumnSortState, sortData] as const;
}