Przeglądaj źródła

feat(web-admin): add useColumnSort for CRM date headers.

Co-authored-by: Cursor <cursoragent@cursor.com>
wuzilong 2 miesięcy temu
rodzic
commit
58b5d9d7de

+ 29 - 0
web-admin/src/composables/useColumnSort.test.ts

@@ -0,0 +1,29 @@
+import { describe, expect, it } from 'vitest'
+import { nextSortState } from './useColumnSort'
+
+describe('nextSortState', () => {
+  it('sets asc when empty', () => {
+    expect(nextSortState(null, null, 'createTime', 'asc')).toEqual({
+      sortField: 'createTime',
+      sortOrder: 'asc'
+    })
+  })
+  it('clears when same field and same order', () => {
+    expect(nextSortState('createTime', 'asc', 'createTime', 'asc')).toEqual({
+      sortField: null,
+      sortOrder: null
+    })
+  })
+  it('switches order on same field', () => {
+    expect(nextSortState('createTime', 'asc', 'createTime', 'desc')).toEqual({
+      sortField: 'createTime',
+      sortOrder: 'desc'
+    })
+  })
+  it('replaces field', () => {
+    expect(nextSortState('createTime', 'asc', 'dealTime', 'desc')).toEqual({
+      sortField: 'dealTime',
+      sortOrder: 'desc'
+    })
+  })
+})

+ 77 - 0
web-admin/src/composables/useColumnSort.ts

@@ -0,0 +1,77 @@
+import { computed, ref, watch, type Ref } from 'vue'
+
+export type SortOrder = 'asc' | 'desc'
+
+export function nextSortState(
+  currentField: string | null,
+  currentOrder: SortOrder | null,
+  prop: string,
+  order: SortOrder
+): { sortField: string | null; sortOrder: SortOrder | null } {
+  if (currentField === prop && currentOrder === order) {
+    return { sortField: null, sortOrder: null }
+  }
+  return { sortField: prop, sortOrder: order }
+}
+
+export function useColumnSort(options?: { onChange?: () => void }) {
+  const sortField = ref<string | null>(null)
+  const sortOrder = ref<SortOrder | null>(null)
+
+  const hasActiveSort = computed(() => Boolean(sortField.value && sortOrder.value))
+
+  function setSort(prop: string, order: SortOrder) {
+    const next = nextSortState(sortField.value, sortOrder.value, prop, order)
+    sortField.value = next.sortField
+    sortOrder.value = next.sortOrder
+    options?.onChange?.()
+  }
+
+  function clearSort() {
+    if (!sortField.value && !sortOrder.value) return
+    sortField.value = null
+    sortOrder.value = null
+    options?.onChange?.()
+  }
+
+  function isSortActive(prop: string, order?: SortOrder) {
+    if (!sortField.value || sortField.value !== prop) return false
+    if (order == null) return true
+    return sortOrder.value === order
+  }
+
+  function toQueryParams(): Record<string, string> {
+    if (!sortField.value || !sortOrder.value) return {}
+    return { sortField: sortField.value, sortOrder: sortOrder.value }
+  }
+
+  function onColumnHidden(prop: string) {
+    if (sortField.value === prop) clearSort()
+  }
+
+  function watchVisibleColumns(visibleProps: Ref<string[]>) {
+    watch(
+      visibleProps,
+      (next, prev) => {
+        if (!prev) return
+        const nextSet = new Set(next || [])
+        for (const p of prev) {
+          if (!nextSet.has(p)) onColumnHidden(p)
+        }
+      },
+      { deep: true }
+    )
+  }
+
+  return {
+    sortField,
+    sortOrder,
+    hasActiveSort,
+    setSort,
+    clearSort,
+    isSortActive,
+    toQueryParams,
+    onColumnHidden,
+    watchVisibleColumns
+  }
+}