Jelajahi Sumber

feat: table增加排序功能

xyh 2 tahun lalu
induk
melakukan
1965eaf3da

+ 3 - 3
packages/app/src/components/table/Header.tsx

@@ -87,12 +87,12 @@ export default forwardRef<HTMLDivElement, Props>(function TableHead(
         <DragOverlay>
           {active ? (
             <table
-              className={classNames('width-full', {
+              className={classNames('width-full', 'height-full', {
                 'ld-table-preview-transform': isSecondLevel,
               })}
             >
-              <thead className="width-full">
-                <tr className="width-full">
+              <thead className="width-full height-full">
+                <tr className="width-full height-full">
                   <HeaderTh
                     header={active}
                     preview

+ 48 - 12
packages/app/src/components/table/HeaderTh.tsx

@@ -2,6 +2,8 @@ import {Header, flexRender} from '@tanstack/react-table';
 import classNames from 'classnames';
 import {FC, CSSProperties} from 'react';
 import {useSortable} from '@dnd-kit/sortable';
+import {CaretUpFilled, CaretDownFilled} from '@ant-design/icons';
+import {Tooltip} from 'antd';
 
 type Props = {
   header: Header<Record<string, any>, unknown>;
@@ -20,13 +22,16 @@ const HeaderTh: FC<Props> = function({
     fixed,
     disabledSort: colDisabledSort,
     fixedStyle,
+    sort: dataSort,
   } = header.column.columnDef.meta as {
     fixed?: 'left' | 'right';
     disabledSort: boolean;
     fixedStyle: CSSProperties,
+    sort?: boolean,
   };
 
   const disabledSort = colDisabledSort || disabledHeadSort;
+  const sortedState = header.column.getIsSorted();
 
   const {setNodeRef, attributes, listeners} = useSortable({
     id: header.id,
@@ -43,10 +48,12 @@ const HeaderTh: FC<Props> = function({
       : '',
   };
 
-  if (preview)
+  if (preview) {
     style.width = '100%';
-  else
+    style.height = '100%';
+  } else {
     style.width = `${header.column.getSize()}px`;
+  }
 
   return (
     <th
@@ -60,17 +67,46 @@ const HeaderTh: FC<Props> = function({
         'ld-table-fixed-left ld-table-left-fixed-shadow': fixed === 'left',
       })}
     >
-      {flexRender(header.column.columnDef.header, header.getContext())}
+      <div className="ld-table-head-th-info">
+        {flexRender(header.column.columnDef.header, header.getContext())}
 
-      {!fixed && !disabledSizeChange && !preview ? (
-        <div
-          onMouseDown={header.getResizeHandler()}
-          onPointerDown={e => e.stopPropagation()}
-          className={classNames('ld-table-resizer', {
-            'ld-table-resizing': isResizing,
-          })}
-        />
-      ) : null}
+        {dataSort
+          ? (
+            <Tooltip
+              title={{
+                asc: '点击降序',
+                desc: '取消排序',
+                false: '点击升序',
+              }[sortedState.toString()]}
+            >
+              <div
+                className="ld-table-head-sort-group"
+                onPointerDown={e => e.stopPropagation()}
+                onClick={header.column.getToggleSortingHandler()}
+              >
+                <CaretUpFilled
+                  className={classNames({'ld-table-head-sort-active': sortedState === 'asc'})}
+                />
+                <CaretDownFilled
+                  className={classNames({'ld-table-head-sort-active': sortedState === 'desc'})}
+                />
+              </div>
+            </Tooltip>
+          )
+          : null}
+      </div>
+
+      {!fixed && !disabledSizeChange && !preview
+        ? (
+          <div
+            onMouseDown={header.getResizeHandler()}
+            onPointerDown={e => e.stopPropagation()}
+            className={classNames('ld-table-resizer', {
+              'ld-table-resizing': isResizing,
+            })}
+          />
+        )
+        : null}
     </th>
   );
 };

+ 26 - 7
packages/app/src/components/table/hooks.tsx

@@ -4,6 +4,9 @@ import {
   getCoreRowModel,
   Header,
   RowSelectionState,
+  SortingState,
+  getSortedRowModel,
+  ColumnSort,
 } from '@tanstack/react-table';
 import {
   CSSProperties,
@@ -20,12 +23,14 @@ import {createTableSettingContext, useContextSection} from '@hooks';
 import TableCheck from './Check';
 import {LDColumnsType} from '.';
 import {TABLE_CELL_WIDTH} from '@utils';
+import {useLatest} from 'ahooks';
 
 function parseColumn<T extends Record<string, unknown>>(
   columns: LDColumnsType<T>[],
   enableSelect?: boolean,
 ) {
   const helper = createColumnHelper<Record<string, any>>();
+  let hasSort = false;
 
   const parseColumns: LDColumnsType<T>[] = [
     {
@@ -47,8 +52,11 @@ function parseColumn<T extends Record<string, unknown>>(
     return prev + next.width;
   }, 0);
 
-  const tableColumns = parseColumns.map(function(val) {
-    const {dataIndex, title, render, align, fixed, width} = val;
+  const columnList = parseColumns.map(function(val) {
+    const {dataIndex, title, render, align, fixed, width, sort} = val;
+
+    if (!hasSort && sort) hasSort = true;
+
     let fixedStyle: CSSProperties = {};
 
     if (fixed === 'left') {
@@ -66,6 +74,7 @@ function parseColumn<T extends Record<string, unknown>>(
       fixed,
       fixedStyle,
       disabledSort: Boolean(fixed) || dataIndex === 'no',
+      sort,
     };
 
     if (render) {
@@ -90,7 +99,7 @@ function parseColumn<T extends Record<string, unknown>>(
     });
   });
 
-  enableSelect && tableColumns.unshift(
+  enableSelect && columnList.unshift(
     helper.accessor('select', {
       header({table}) {
         return (
@@ -120,7 +129,7 @@ function parseColumn<T extends Record<string, unknown>>(
     }),
   );
 
-  return tableColumns;
+  return {columnList, hasSort};
 }
 
 export function useTable<T extends Record<string, any>>(
@@ -131,6 +140,7 @@ export function useTable<T extends Record<string, any>>(
     rowSelection?: RowSelectionState;
     setRowSelection?: Dispatch<SetStateAction<RowSelectionState>>;
     rawKey?: keyof T;
+    onSortingChange?: (state: ColumnSort | null) => void;
   },
 ) {
   const {
@@ -138,9 +148,10 @@ export function useTable<T extends Record<string, any>>(
     rowSelection,
     setRowSelection,
     rawKey,
+    onSortingChange,
   } = options;
 
-  const columnList = useMemo(
+  const {columnList, hasSort} = useMemo(
     function() {
       return parseColumn(columns, Boolean(setRowSelection));
     },
@@ -148,6 +159,7 @@ export function useTable<T extends Record<string, any>>(
   );
 
   const preloadData = useContextSection(settingContext, state => state[0]);
+
   const [columnSizing, setColumnSizing] = useState(function() {
     if (preloadData?.tableWidth)
       return JSON.parse(preloadData.tableWidth) as Record<string, number>;
@@ -161,7 +173,6 @@ export function useTable<T extends Record<string, any>>(
 
     return obj;
   });
-
   const [columnOrder, setColumnOrder] = useState(function() {
     if (preloadData?.tableOrder)
       return JSON.parse(preloadData?.tableOrder) as string[];
@@ -170,6 +181,11 @@ export function useTable<T extends Record<string, any>>(
 
     return ['select', 'no', ...nextList];
   });
+  const [sorting, setSorting] = useState<SortingState>([]);
+  const onSortingChangeFn = useLatest(onSortingChange);
+  useEffect(function() {
+    onSortingChangeFn.current?.(sorting.length ? sorting[0] : null);
+  }, [onSortingChangeFn, sorting]);
 
   const table = useReactTable({
     data,
@@ -178,13 +194,16 @@ export function useTable<T extends Record<string, any>>(
       columnSizing,
       columnOrder,
       rowSelection,
+      sorting,
     },
+    getSortedRowModel: onSortingChange ? void 0 : getSortedRowModel(),
     getCoreRowModel: getCoreRowModel(),
     onColumnSizingChange: setColumnSizing,
     columnResizeMode: 'onChange',
     onColumnOrderChange: setColumnOrder,
     onRowSelectionChange: setRowSelection,
     getRowId: row => row[rawKey as string | undefined ?? 'id'],
+    onSortingChange: setSorting,
   });
 
   type ActiveState = Header<Record<string, any>, unknown> | null;
@@ -222,7 +241,7 @@ export function useTable<T extends Record<string, any>>(
   }
 
   return [
-    {...table, active},
+    {...table, active, hasSort},
     {onDragStart, onDragEnd},
   ] as const;
 }

+ 21 - 0
packages/app/src/components/table/index.css

@@ -173,6 +173,27 @@
   }
 }
 
+.ld-table-head-th-info {
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  width: 100%;
+  height: 100%;
+}
+
+.ld-table-head-sort-group {
+  display: flex;
+  flex-direction: column;
+  margin-left: 4px;
+  font-size: 12px;
+  color: var(--tip-font-color);
+  cursor: pointer;
+}
+
+.ld-table-head-sort-active {
+  color: var(--primary-color);
+}
+
 .ld-table-preview-transform {
   transform: translate3d(-5vw, -5vh, 0) !important;
 }

+ 7 - 3
packages/app/src/components/table/index.tsx

@@ -16,7 +16,7 @@ import {
   useState,
 } from 'react';
 import {useTable, useTableShadow} from './hooks';
-import {RowSelectionState} from '@tanstack/react-table';
+import {ColumnSort, RowSelectionState} from '@tanstack/react-table';
 import './index.css';
 import {ModifyData} from '@models';
 import TableHead from './Header';
@@ -30,6 +30,7 @@ export type LDColumnsType<T extends Record<string, unknown>> = {
   render?: (info: T, index: number) => ReactNode,
   align?: 'left' | 'right' | 'center',
   fixed?: 'left' | 'right',
+  sort?: boolean,
 };
 
 type Props<T extends Record<string, unknown>> = {
@@ -50,6 +51,7 @@ type Props<T extends Record<string, unknown>> = {
   hightlightKey?: keyof T;
   enableRowDnd?: boolean;
   rawKey?: keyof T;
+  onSortingChange?: (state: ColumnSort | null) => void;
 };
 
 function LDTable<T extends Record<string, any>>(props: Props<T>): ReactElement {
@@ -70,6 +72,7 @@ function LDTable<T extends Record<string, any>>(props: Props<T>): ReactElement {
     hightlightKey,
     rawKey,
     enableRowDnd,
+    onSortingChange,
   } = props;
   const [klonaData, setKlonaData] = useState(klona(data));
   const [{page, pageSize}, {setPageContext}] = useTablePageContext(pageContext);
@@ -80,7 +83,7 @@ function LDTable<T extends Record<string, any>>(props: Props<T>): ReactElement {
   }, [data, enableRowDnd]);
 
   const [
-    {getHeaderGroups, getRowModel, getCenterTotalSize, active},
+    {getHeaderGroups, getRowModel, getCenterTotalSize, active, hasSort},
     {onDragEnd, onDragStart},
   ] = useTable(
     columns,
@@ -90,6 +93,7 @@ function LDTable<T extends Record<string, any>>(props: Props<T>): ReactElement {
       rowSelection,
       setRowSelection,
       rawKey,
+      onSortingChange,
     },
   );
 
@@ -130,7 +134,7 @@ function LDTable<T extends Record<string, any>>(props: Props<T>): ReactElement {
           getHeaderGroups={getHeaderGroups}
           data={klonaData}
           rawKey={rawKey}
-          enableDnd={enableRowDnd}
+          enableDnd={enableRowDnd && (!hasSort || Boolean(onSortingChange))}
           setData={setKlonaData}
           isSecondLevel={isSecondLevel}
         />

+ 1 - 0
packages/app/src/pages/role/table/hooks.tsx

@@ -9,6 +9,7 @@ const tableColumns: LDColumnsType<RoleListData>[] = [
     title: '角色名称',
     dataIndex: 'roleName',
     width: TABLE_CELL_WIDTH.normal,
+    sort: true,
   },
   {
     title: '备注信息',