You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

44 lines
1.7 KiB

import { ref } from 'vue'
// 列表表头按字段排序。各页传一组「升序语义」的比较器,降序由这里统一取反;
// 比较器里用 `||` 串一个兜底字段(一般是 id),值相等时顺序才稳定、不会每次渲染乱跳。
//
// 用法:
// const { toggleSort, sortMark, applySort } = useTableSort<Row>({
// id: (a, b) => a.id - b.id,
// name: (a, b) => a.name.localeCompare(b.name, 'zh') || a.id - b.id,
// }, 'id')
// const viewRows = computed(() => applySort(rows.value.filter(...)))
// <th class="cursor-pointer select-none" @click="toggleSort('name')">名称{{ sortMark('name') }}</th>
export type SortDir = 'asc' | 'desc'
export type Comparator<T> = (a: T, b: T) => number
export function useTableSort<T>(
comparators: Record<string, Comparator<T>>,
defaultKey: string,
defaultDir: SortDir = 'asc'
) {
const sortKey = ref(defaultKey)
const sortDir = ref<SortDir>(defaultDir)
function toggleSort(key: string) {
if (!comparators[key]) return
if (sortKey.value === key) sortDir.value = sortDir.value === 'asc' ? 'desc' : 'asc'
else { sortKey.value = key; sortDir.value = 'asc' }
}
// 表头后缀的排序箭头;非当前排序列返回空串
function sortMark(key: string) {
return sortKey.value === key ? (sortDir.value === 'asc' ? ' ↑' : ' ↓') : ''
}
// 返回新数组,不改原列表(原列表常是 ref 里的接口数据)
function applySort(list: T[]): T[] {
const cmp = comparators[sortKey.value]
if (!cmp) return list
const dir = sortDir.value === 'asc' ? 1 : -1
return [...list].sort((a, b) => cmp(a, b) * dir)
}
return { sortKey, sortDir, toggleSort, sortMark, applySort }
}