Explorar o código

feat: 允许table head进行分组

xyh %!s(int64=2) %!d(string=hai) anos
pai
achega
fb556862c3

+ 46 - 41
packages/app/src/components/table/HeaderTh.tsx

@@ -21,14 +21,14 @@ const HeaderTh: FC<Props> = function({
   const {
     fixed,
     disabledSort: colDisabledSort,
-    fixedStyle,
+    fixedStyle = {},
     sort: dataSort,
   } = header.column.columnDef.meta as {
     fixed?: 'left' | 'right';
-    disabledSort: boolean;
-    fixedStyle: CSSProperties,
+    disabledSort?: boolean;
+    fixedStyle?: CSSProperties,
     sort?: boolean,
-  };
+  } ?? {};
 
   const disabledSort = colDisabledSort || disabledHeadSort;
   const sortedState = header.column.getIsSorted();
@@ -57,6 +57,7 @@ const HeaderTh: FC<Props> = function({
 
   return (
     <th
+      colSpan={header.colSpan}
       key={header.id}
       style={{...style, ...fixedStyle}}
       ref={setNodeRef}
@@ -67,46 +68,50 @@ const HeaderTh: FC<Props> = function({
         'ld-table-fixed-left ld-table-left-fixed-shadow': fixed === 'left',
       })}
     >
-      <div className="ld-table-head-th-info">
-        {flexRender(header.column.columnDef.header, header.getContext())}
+      {header.isPlaceholder
+        ? null
+        : <>
+          <div className="ld-table-head-th-info">
+            {flexRender(header.column.columnDef.header, header.getContext())}
+
+            {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>
 
-        {dataSort
-          ? (
-            <Tooltip
-              title={{
-                asc: '点击降序',
-                desc: '取消排序',
-                false: '点击升序',
-              }[sortedState.toString()]}
-            >
+          {!fixed && !disabledSizeChange && !preview
+            ? (
               <div
-                className="ld-table-head-sort-group"
+                onMouseDown={header.getResizeHandler()}
                 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}
+                className={classNames('ld-table-resizer', {
+                  'ld-table-resizing': isResizing,
+                })}
+              />
+            )
+            : null}
+        </>}
     </th>
   );
 };

+ 49 - 25
packages/app/src/components/table/hooks.tsx

@@ -28,12 +28,14 @@ import {useLatest} from 'ahooks';
 function parseColumn<T extends Record<string, unknown>>(
   columns: LDColumnsType<T>[],
   enableSelect?: boolean,
+  enableNo?: boolean,
 ) {
   const helper = createColumnHelper<Record<string, any>>();
-  let hasSort = false;
+  let hasSort = false, hasGroup = false;
 
-  const parseColumns: LDColumnsType<T>[] = [
-    {
+  const parseColumns: LDColumnsType<T>[] = [...columns];
+  if (enableNo) {
+    parseColumns.unshift({
       title: '序号',
       dataIndex: 'no',
       align: 'center',
@@ -41,11 +43,10 @@ function parseColumn<T extends Record<string, unknown>>(
       render(_, index) {
         return index + 1;
       },
-    },
-    ...columns,
-  ];
+    });
+  }
 
-  let leftPosition = enableSelect ? 48 : 0;
+  let leftPosition = enableSelect ? TABLE_CELL_WIDTH.checkbox : 0;
   let rightPosition = parseColumns.reduce(function(prev, next) {
     if (next.fixed !== 'right') return prev;
 
@@ -53,9 +54,7 @@ function parseColumn<T extends Record<string, unknown>>(
   }, 0);
 
   const columnList = parseColumns.map(function(val) {
-    const {dataIndex, title, render, align, fixed, width, sort} = val;
-
-    if (!hasSort && sort) hasSort = true;
+    const {dataIndex, title, render, align, fixed, width, sort, children} = val;
 
     let fixedStyle: CSSProperties = {};
 
@@ -77,10 +76,34 @@ function parseColumn<T extends Record<string, unknown>>(
       sort,
     };
 
+    if (children && children.length) {
+      if (!hasGroup) hasGroup = true;
+
+      const {columnList: columns} = parseColumn(
+        children,
+        false,
+        false,
+      );
+
+      const group = helper.group({
+        header: title,
+        columns,
+        minSize: 0,
+        size: width,
+        meta,
+      });
+
+      return group;
+    }
+
+    if (!hasSort && sort) hasSort = true;
+
     if (render) {
       return helper.display({
         id: dataIndex.toString(),
         header: title.toString(),
+        size: width,
+        minSize: 0,
         cell(props) {
           return render(
             props.row.original as T,
@@ -95,6 +118,7 @@ function parseColumn<T extends Record<string, unknown>>(
       header: title?.toString(),
       cell: props => props.getValue(),
       minSize: 0,
+      size: width,
       meta,
     });
   });
@@ -120,6 +144,7 @@ function parseColumn<T extends Record<string, unknown>>(
           />
         );
       },
+      size: TABLE_CELL_WIDTH.checkbox,
       meta: {
         align: 'center',
         fixed: 'left',
@@ -129,7 +154,11 @@ function parseColumn<T extends Record<string, unknown>>(
     }),
   );
 
-  return {columnList, hasSort};
+  return {
+    columnList,
+    hasSort,
+    hasGroup,
+  };
 }
 
 export function useTable<T extends Record<string, any>>(
@@ -151,9 +180,13 @@ export function useTable<T extends Record<string, any>>(
     onSortingChange,
   } = options;
 
-  const {columnList, hasSort} = useMemo(
+  const {columnList, hasSort, hasGroup} = useMemo(
     function() {
-      return parseColumn(columns, Boolean(setRowSelection));
+      return parseColumn(
+        columns,
+        Boolean(setRowSelection),
+        true,
+      );
     },
     [columns, setRowSelection],
   );
@@ -164,22 +197,13 @@ export function useTable<T extends Record<string, any>>(
     if (preloadData?.tableWidth)
       return JSON.parse(preloadData.tableWidth) as Record<string, number>;
 
-    const obj: Record<string, number> = {no: 64, select: 48};
-
-    columns.forEach(function({dataIndex, width}) {
-      if (dataIndex && width)
-        obj[dataIndex.toString()] = Number(width);
-    });
-
-    return obj;
+    return {};
   });
   const [columnOrder, setColumnOrder] = useState(function() {
     if (preloadData?.tableOrder)
       return JSON.parse(preloadData?.tableOrder) as string[];
 
-    const nextList = columns.map(val => val.dataIndex.toString());
-
-    return ['select', 'no', ...nextList];
+    return [];
   });
   const [sorting, setSorting] = useState<SortingState>([]);
   const onSortingChangeFn = useLatest(onSortingChange);
@@ -241,7 +265,7 @@ export function useTable<T extends Record<string, any>>(
   }
 
   return [
-    {...table, active, hasSort},
+    {...table, active, hasSort, hasGroup},
     {onDragStart, onDragEnd},
   ] as const;
 }

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

@@ -102,9 +102,6 @@
   }
 }
 
-/* .ld-table-tr-preview {
-} */
-
 .ld-table-enabled-right-fixed-shadow {
   & .ld-table-right-fixed-shadow {
     &::before {

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

@@ -23,14 +23,19 @@ import TableHead from './Header';
 import TableBody from './Body';
 import {klona} from 'klona/json';
 
+type GroupKey<T extends Record<string, unknown>> = keyof T extends string
+  ? `${keyof T}Group`
+  : never;
+
 export type LDColumnsType<T extends Record<string, unknown>> = {
-  dataIndex: keyof T,
+  dataIndex: keyof T | GroupKey<T>,
   title: string,
   width: number,
   render?: (info: T, index: number) => ReactNode,
   align?: 'left' | 'right' | 'center',
   fixed?: 'left' | 'right',
   sort?: boolean,
+  children?: LDColumnsType<T>[],
 };
 
 type Props<T extends Record<string, unknown>> = {
@@ -83,7 +88,10 @@ function LDTable<T extends Record<string, any>>(props: Props<T>): ReactElement {
   }, [data, enableRowDnd]);
 
   const [
-    {getHeaderGroups, getRowModel, getCenterTotalSize, active, hasSort},
+    {
+      getHeaderGroups, getRowModel, getCenterTotalSize,
+      active, hasSort, hasGroup,
+    },
     {onDragEnd, onDragStart},
   ] = useTable(
     columns,
@@ -113,7 +121,7 @@ function LDTable<T extends Record<string, any>>(props: Props<T>): ReactElement {
         <TableHead
           ref={headerTableRef}
           isSecondLevel={isSecondLevel}
-          disabledHeadSort={disabledHeadSort}
+          disabledHeadSort={disabledHeadSort || hasGroup}
           disabledSizeChange={disabledSizeChange}
           active={active}
           getHeaderGroups={getHeaderGroups}

+ 1 - 0
packages/app/src/utils/constants.ts

@@ -23,6 +23,7 @@ export const MENU_COLLAPSED_STORAGE = PROJECT_PREFIX + '-menuCollapsed';
 /** table宽度 */
 export const TABLE_CELL_WIDTH = Object.freeze({
   no: 64,
+  checkbox: 48,
   /** 双按钮菜单长度 */
   doubleBtn: 200,
   /**极小的table宽度 */