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
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 }
|
|
}
|
|
|