Browse Source

wip(table): perf table

vben 4 years ago
parent
commit
3549043f37

+ 3 - 0
CHANGELOG.zh_CN.md

@@ -12,10 +12,13 @@
 - form: 新增远程下拉`ApiSelect`及示例
 - form: 新增`autoFocusFirstItem`配置。用于配置是否聚焦表单第一个输入框
 - useForm: 支持动态改变参数。可以传入`Ref`类型与`Computed`类型进行动态更改
+- table: 新增`clickToRowSelect`属性。用于控制点击行是否选中勾选狂
+- table: 监听行点击事件
 
 ### ⚡ Performance Improvements
 
 - 优化`modal`与`drawer`滚动条组件
+- table: 移除 `isTreeTable`属性
 
 ### 🎫 Chores
 

+ 4 - 0
src/components/Drawer/src/BasicDrawer.vue

@@ -225,6 +225,10 @@
         padding: 16px !important;
         margin-bottom: 0 !important;
       }
+
+      > .scrollbar > .scrollbar__bar.is-horizontal {
+        display: none;
+      }
     }
   }
 

+ 2 - 2
src/components/Icon/src/index.vue

@@ -43,7 +43,7 @@
         if (el) {
           await nextTick();
           const icon = unref(getIconRef);
-
+          if (!icon) return;
           const svg = Iconify.renderSVG(icon, {});
 
           if (svg) {
@@ -74,7 +74,7 @@
         }
       );
 
-      watch(() => props.icon, update, { flush: 'post' });
+      // watch(() => props.icon, update, { flush: 'post' });
 
       onMounted(update);
 

+ 4 - 0
src/components/Modal/src/index.less

@@ -40,6 +40,10 @@
 
   .ant-modal-body {
     padding: 0;
+
+    > .scrollbar > .scrollbar__bar.is-horizontal {
+      display: none;
+    }
   }
 
   &-large {

+ 95 - 114
src/components/Table/src/BasicTable.vue

@@ -11,7 +11,7 @@
       :submitOnReset="true"
       v-bind="getFormProps"
       v-if="getBindValues.useSearchForm"
-      :submitButtonOptions="{ loading }"
+      :submitButtonOptions="{ loading: getLoading }"
       :tableAction="tableAction"
       @register="registerForm"
       @submit="handleSearchInfoChange"
@@ -35,18 +35,10 @@
   </div>
 </template>
 <script lang="ts">
-  import type {
-    BasicTableProps,
-    FetchParams,
-    GetColumnsParams,
-    TableActionType,
-    SizeType,
-    SorterResult,
-    TableCustomRecord,
-  } from './types/table';
+  import type { BasicTableProps, TableActionType, SizeType, SorterResult } from './types/table';
   import { PaginationProps } from './types/pagination';
 
-  import { defineComponent, ref, computed, unref, watch, nextTick, toRaw } from 'vue';
+  import { defineComponent, ref, computed, unref, watch, nextTick } from 'vue';
   import { Table } from 'ant-design-vue';
   import renderTitle from './components/renderTitle';
   import renderFooter from './components/renderFooter';
@@ -64,51 +56,64 @@
   import { useRowSelection } from './hooks/useRowSelection';
   import { useTableScroll } from './hooks/useTableScroll';
   import { provideTable } from './hooks/useProvinceTable';
+  import { useCustomRow } from './hooks/useCustomRow';
+  import { useTableStyle } from './hooks/useTableStyle';
 
   import { useEventListener } from '/@/hooks/event/useEventListener';
   import { basicProps } from './props';
-  import { ROW_KEY } from './const';
   import { useExpose } from '/@/hooks/core/useExpose';
 
   import './style/index.less';
   export default defineComponent({
     props: basicProps,
     components: { Table, BasicForm },
-    emits: ['fetch-success', 'fetch-error', 'selection-change', 'register'],
+    emits: [
+      'fetch-success',
+      'fetch-error',
+      'selection-change',
+      'register',
+      'row-click',
+      'row-dbClick',
+      'row-contextmenu',
+      'row-mouseenter',
+      'row-mouseleave',
+    ],
     setup(props, { attrs, emit, slots }) {
       const tableElRef = ref<ComponentRef>(null);
+
       const wrapRef = ref<Nullable<HTMLDivElement>>(null);
       const innerPropsRef = ref<Partial<BasicTableProps>>();
+
       const [registerForm, { getFieldsValue }] = useForm();
 
-      const getMergeProps = computed(() => {
-        return {
-          ...props,
-          ...unref(innerPropsRef),
-        } as BasicTableProps;
+      const getProps = computed(() => {
+        return { ...props, ...unref(innerPropsRef) } as BasicTableProps;
       });
 
-      //  const getProps = computed(
-      //   (): FormProps => {
-      //     return deepMerge(toRaw(props), unref(innerPropsRef));
-      //   }
-      // );
-
-      const { loadingRef } = useLoading(getMergeProps);
-      const { getPaginationRef, setPagination } = usePagination(getMergeProps);
-      const { getColumnsRef, setColumns } = useColumns(getMergeProps, getPaginationRef);
-      const { getDataSourceRef, setTableData, fetch, getAutoCreateKey } = useDataSource(
-        getMergeProps,
+      const { getLoading, setLoading } = useLoading(getProps);
+      const { getPaginationInfo, getPagination, setPagination } = usePagination(getProps);
+      const { getColumnsRef, getColumns, setColumns } = useColumns(getProps, getPaginationInfo);
+      const {
+        getDataSourceRef,
+        getDataSource,
+        setTableData,
+        fetch,
+        getRowKey,
+        reload,
+        getAutoCreateKey,
+      } = useDataSource(
+        getProps,
         {
-          getPaginationRef,
-          loadingRef,
+          getPaginationInfo,
+          setLoading,
           setPagination,
           getFieldsValue,
         },
         emit
       );
 
-      const { getScrollRef, redoHeight } = useTableScroll(getMergeProps, tableElRef);
+      const { getScrollRef, redoHeight } = useTableScroll(getProps, tableElRef);
+
       const {
         getRowSelectionRef,
         getSelectRows,
@@ -116,55 +121,58 @@
         getSelectRowKeys,
         deleteSelectRowByKey,
         setSelectedRowKeys,
-      } = useRowSelection(getMergeProps, emit);
-
-      const getRowKey = computed(() => {
-        const { rowKey } = unref(getMergeProps);
+      } = useRowSelection(getProps, emit);
 
-        return unref(getAutoCreateKey) ? ROW_KEY : rowKey;
+      const { customRow } = useCustomRow(getProps, {
+        setSelectedRowKeys,
+        getSelectRowKeys,
+        clearSelectedRowKeys,
+        getAutoCreateKey,
+        emit,
       });
 
+      const { getRowClassName } = useTableStyle(getProps);
+
+      const getTitleProps = computed(
+        (): Recordable => {
+          const { title, showTableSetting, titleHelpMessage, tableSetting } = unref(getProps);
+          const hideTitle = !slots.tableTitle && !title && !slots.toolbar && !showTableSetting;
+          if (hideTitle && !isString(title)) {
+            return {};
+          }
+          return {
+            title: hideTitle
+              ? null
+              : renderTitle.bind(
+                  null,
+                  title,
+                  titleHelpMessage,
+                  slots,
+                  showTableSetting,
+                  tableSetting
+                ),
+          };
+        }
+      );
+
       const getBindValues = computed(() => {
-        const { title, titleHelpMessage, showSummary, showTableSetting, tableSetting } = unref(
-          getMergeProps
-        );
-        const hideTitle = !slots.tableTitle && !title && !slots.toolbar && !showTableSetting;
-        const titleData: Recordable =
-          hideTitle && !isString(title)
-            ? {}
-            : {
-                title: hideTitle
-                  ? null
-                  : renderTitle.bind(
-                      null,
-                      title,
-                      titleHelpMessage,
-                      slots,
-                      showTableSetting,
-                      tableSetting
-                    ),
-              };
-        const pagination = unref(getPaginationRef);
-        const rowSelection = unref(getRowSelectionRef);
-        const scroll = unref(getScrollRef);
-        const loading = unref(loadingRef);
-        const rowKey = unref(getRowKey);
-        const columns = unref(getColumnsRef);
-        const dataSource = unref(getDataSourceRef);
-        let propsData = {
+        const { showSummary } = unref(getProps);
+
+        let propsData: Recordable = {
           size: 'middle',
           ...(slots.expandedRowRender ? { expandIcon: renderExpandIcon() } : {}),
           ...attrs,
-          ...unref(getMergeProps),
-          ...titleData,
-          scroll,
-          loading,
+          customRow,
+          ...unref(getProps),
+          ...unref(getTitleProps),
+          scroll: unref(getScrollRef),
+          loading: unref(getLoading),
           tableLayout: 'fixed',
-          rowSelection,
-          rowKey,
-          columns,
-          pagination,
-          dataSource,
+          rowSelection: unref(getRowSelectionRef),
+          rowKey: unref(getRowKey),
+          columns: unref(getColumnsRef),
+          pagination: unref(getPaginationInfo),
+          dataSource: unref(getDataSourceRef),
         };
         if (slots.expandedRowRender) {
           propsData = omit(propsData, 'scroll');
@@ -173,7 +181,7 @@
           propsData.footer = renderFooter.bind(null, {
             scroll: scroll as any,
             columnsRef: getColumnsRef,
-            summaryFunc: unref(getMergeProps).summaryFunc,
+            summaryFunc: unref(getProps).summaryFunc,
             dataSourceRef: getDataSourceRef,
             rowSelectionRef: getRowSelectionRef,
           });
@@ -182,17 +190,17 @@
       });
 
       const getFormProps = computed(() => {
-        const { formConfig } = unref(getBindValues);
-        const formProps: FormProps = {
+        const { formConfig } = unref(getProps);
+        const formProps: Partial<FormProps> = {
           showAdvancedButton: true,
-          ...(formConfig as FormProps),
+          ...formConfig,
           compact: true,
         };
         return formProps;
       });
 
       const getEmptyDataIsShowTable = computed(() => {
-        const { emptyDataIsShowTable, useSearchForm } = unref(getMergeProps);
+        const { emptyDataIsShowTable, useSearchForm } = unref(getProps);
         if (emptyDataIsShowTable || !useSearchForm) {
           return true;
         }
@@ -207,17 +215,8 @@
         { immediate: true }
       );
 
-      function getRowClassName(record: TableCustomRecord, index: number) {
-        const { striped, rowClassName } = unref(getMergeProps);
-        if (!striped) return;
-        if (rowClassName && isFunction(rowClassName)) {
-          return rowClassName(record);
-        }
-        return (index || 0) % 2 === 1 ? 'basic-table-row__striped' : '';
-      }
-
       function handleSearchInfoChange(info: any) {
-        const { handleSearchInfoFn } = unref(getMergeProps);
+        const { handleSearchInfoFn } = unref(getProps);
         if (handleSearchInfoFn && isFunction(handleSearchInfoFn)) {
           info = handleSearchInfoFn(info) || info;
         }
@@ -230,7 +229,7 @@
         filters: Partial<Recordable<string[]>>,
         sorter: SorterResult
       ) {
-        const { clearSelectOnPageChange, sortFn } = unref(getMergeProps);
+        const { clearSelectOnPageChange, sortFn } = unref(getProps);
         if (clearSelectOnPageChange) {
           clearSelectedRowKeys();
         }
@@ -245,7 +244,7 @@
       }
 
       function handleSummary() {
-        if (unref(getMergeProps).showSummary) {
+        if (unref(getProps).showSummary) {
           nextTick(() => {
             const tableEl = unref(tableElRef);
             if (!tableEl) return;
@@ -273,9 +272,7 @@
       }
 
       const tableAction: TableActionType = {
-        reload: async (opt?: FetchParams) => {
-          await fetch(opt);
-        },
+        reload,
         getSelectRows,
         clearSelectedRowKeys,
         getSelectRowKeys,
@@ -285,27 +282,11 @@
         redoHeight,
         setSelectedRowKeys,
         setColumns,
-        getPaginationRef: () => {
-          return unref(getPaginationRef);
-        },
-        getColumns: (opt?: GetColumnsParams) => {
-          const { ignoreIndex, ignoreAction } = opt || {};
-          let columns = toRaw(unref(getColumnsRef));
-          if (ignoreIndex) {
-            columns = columns.filter((item) => item.flag !== 'INDEX');
-          }
-          if (ignoreAction) {
-            columns = columns.filter((item) => item.flag !== 'ACTION');
-          }
-          return columns;
-        },
-        getDataSource: () => {
-          return unref(getDataSourceRef);
-        },
-        setLoading: (loading: boolean) => {
-          loadingRef.value = loading;
-        },
+        setLoading,
+        getDataSource,
         setProps,
+        getPaginationRef: getPagination,
+        getColumns,
         getSize: () => {
           return unref(getBindValues).size as SizeType;
         },
@@ -323,7 +304,7 @@
       return {
         tableElRef,
         getBindValues,
-        loading: loadingRef,
+        getLoading,
         registerForm,
         handleSearchInfoChange,
         getFormProps,

+ 6 - 0
src/components/Table/src/const.ts

@@ -31,3 +31,9 @@ export function DEFAULT_SORT_FN(sortInfo: SorterResult) {
     order,
   };
 }
+
+//  表格单元格默认布局
+export const DEFAULT_ALIGN = 'center';
+
+export const INDEX_COLUMN_FLAG = 'INDEX';
+export const ACTION_COLUMN_FLAG = 'ACTION';

+ 125 - 90
src/components/Table/src/hooks/useColumns.ts

@@ -1,113 +1,133 @@
-import { BasicColumn, BasicTableProps } from '../types/table';
+import { BasicColumn, BasicTableProps, GetColumnsParams } from '../types/table';
 import { PaginationProps } from '../types/pagination';
 import { unref, ComputedRef, Ref, computed, watchEffect, ref, toRaw } from 'vue';
 import { isBoolean, isArray, isObject } from '/@/utils/is';
-import { PAGE_SIZE } from '../const';
-import { useProps } from './useProps';
+import { DEFAULT_ALIGN, PAGE_SIZE, INDEX_COLUMN_FLAG, ACTION_COLUMN_FLAG } from '../const';
 import { useI18n } from '/@/hooks/web/useI18n';
 
 const { t } = useI18n();
+
+function handleItem(item: BasicColumn, ellipsis: boolean) {
+  const { key, dataIndex, children } = item;
+  item.align = item.align || DEFAULT_ALIGN;
+  if (ellipsis) {
+    if (!key) {
+      item.key = dataIndex;
+    }
+    if (!isBoolean(item.ellipsis)) {
+      Object.assign(item, {
+        ellipsis,
+      });
+    }
+  }
+  if (children && children.length) {
+    handleChildren(children, !!ellipsis);
+  }
+}
+
+function handleChildren(children: BasicColumn[] | undefined, ellipsis: boolean) {
+  if (!children) return;
+  children.forEach((item) => {
+    const { children } = item;
+    handleItem(item, ellipsis);
+    handleChildren(children, ellipsis);
+  });
+}
+
+function handleIndexColumn(
+  propsRef: ComputedRef<BasicTableProps>,
+  getPaginationRef: ComputedRef<boolean | PaginationProps>,
+  columns: BasicColumn[]
+) {
+  const { showIndexColumn, indexColumnProps, ellipsis } = unref(propsRef);
+
+  let pushIndexColumns = false;
+  columns.forEach((item) => {
+    const { children } = item;
+    handleItem(item, !!ellipsis);
+    const isTreeTable = children && children.length;
+
+    const indIndex = columns.findIndex((column) => column.flag === INDEX_COLUMN_FLAG);
+
+    if (showIndexColumn && !isTreeTable) {
+      pushIndexColumns = indIndex === -1;
+    } else if (!showIndexColumn && !isTreeTable && indIndex !== -1) {
+      columns.splice(indIndex, 1);
+    }
+  });
+
+  if (!pushIndexColumns) return;
+
+  const isFixedLeft = columns.some((item) => item.fixed === 'left');
+
+  columns.unshift({
+    flag: INDEX_COLUMN_FLAG,
+    width: 50,
+    title: t('component.table.index'),
+    align: 'center',
+    customRender: ({ index }) => {
+      const getPagination = unref(getPaginationRef);
+      if (isBoolean(getPagination)) {
+        return `${index + 1}`;
+      }
+      const { current = 1, pageSize = PAGE_SIZE } = getPagination;
+      const currentIndex = (current - 1) * pageSize + index + 1;
+      return currentIndex;
+    },
+    ...(isFixedLeft
+      ? {
+          fixed: 'left',
+        }
+      : {}),
+    ...indexColumnProps,
+  });
+}
+
+function handleActionColumn(propsRef: ComputedRef<BasicTableProps>, columns: BasicColumn[]) {
+  const { actionColumn } = unref(propsRef);
+  if (!actionColumn) return;
+
+  const hasIndex = columns.findIndex((column) => column.flag === ACTION_COLUMN_FLAG);
+  if (hasIndex === -1) {
+    columns.push({
+      ...columns[hasIndex],
+      fixed: 'right',
+      ...actionColumn,
+      flag: ACTION_COLUMN_FLAG,
+    });
+  }
+}
+
 export function useColumns(
-  refProps: ComputedRef<BasicTableProps>,
-  getPaginationRef: ComputedRef<false | PaginationProps>
+  propsRef: ComputedRef<BasicTableProps>,
+  getPaginationRef: ComputedRef<boolean | PaginationProps>
 ) {
-  const { propsRef } = useProps(refProps);
   const columnsRef = (ref(unref(propsRef).columns) as unknown) as Ref<BasicColumn[]>;
-  const cacheColumnsRef = (ref(unref(propsRef).columns) as unknown) as Ref<BasicColumn[]>;
+  let cacheColumns = unref(propsRef).columns;
 
   const getColumnsRef = computed(() => {
-    const props = unref(propsRef);
-    const { showIndexColumn, indexColumnProps, ellipsis, actionColumn, isTreeTable } = props;
-
     const columns = unref(columnsRef);
     if (!columns) {
       return [];
     }
-    let pushIndexColumns = false;
-    columns.forEach((item) => {
-      const { children } = item;
-      handleItem(item, !!ellipsis);
-
-      handleChildren(children, !!ellipsis);
-
-      const indIndex = columns.findIndex((column) => column.flag === 'INDEX');
-      if (showIndexColumn && !isTreeTable) {
-        pushIndexColumns = indIndex === -1;
-      } else if (!showIndexColumn && !isTreeTable && indIndex !== -1) {
-        columns.splice(indIndex, 1);
-      }
-    });
 
-    if (pushIndexColumns) {
-      const isFixedLeft = columns.some((item) => item.fixed === 'left');
-
-      columns.unshift({
-        flag: 'INDEX',
-        width: 50,
-        title: t('component.table.index'),
-        align: 'center',
-        customRender: ({ index }) => {
-          const getPagination = unref(getPaginationRef);
-          if (isBoolean(getPagination)) {
-            return `${index + 1}`;
-          }
-          const { current = 1, pageSize = PAGE_SIZE } = getPagination;
-          const currentIndex = (current - 1) * pageSize + index + 1;
-          return currentIndex;
-        },
-        ...(isFixedLeft
-          ? {
-              fixed: 'left',
-            }
-          : {}),
-        ...indexColumnProps,
-      });
-    }
-    if (actionColumn) {
-      const hasIndex = columns.findIndex((column) => column.flag === 'ACTION');
-      if (hasIndex === -1) {
-        columns.push({
-          ...columns[hasIndex],
-          fixed: 'right',
-          ...actionColumn,
-          flag: 'ACTION',
-        });
-      }
-    }
+    handleIndexColumn(propsRef, getPaginationRef, columns);
+    handleActionColumn(propsRef, columns);
+
     return columns;
   });
 
   watchEffect(() => {
     const columns = toRaw(unref(propsRef).columns);
     columnsRef.value = columns;
-    cacheColumnsRef.value = columns;
+    cacheColumns = columns;
   });
 
-  function handleItem(item: BasicColumn, ellipsis: boolean) {
-    const { key, dataIndex } = item;
-    item.align = item.align || 'center';
-    if (ellipsis) {
-      if (!key) {
-        item.key = dataIndex;
-      }
-      if (!isBoolean(item.ellipsis)) {
-        Object.assign(item, {
-          ellipsis,
-        });
-      }
-    }
-  }
-
-  function handleChildren(children: BasicColumn[] | undefined, ellipsis: boolean) {
-    if (!children) return;
-    children.forEach((item) => {
-      const { children } = item;
-      handleItem(item, ellipsis);
-      handleChildren(children, ellipsis);
-    });
-  }
-
-  function setColumns(columns: BasicColumn[] | string[]) {
+  /**
+   * set columns
+   * @param columns key|column
+   */
+  function setColumns(columns: Partial<BasicColumn>[] | string[]) {
     if (!isArray(columns)) return;
 
     if (columns.length <= 0) {
@@ -116,15 +136,30 @@ export function useColumns(
     }
 
     const firstColumn = columns[0];
+
     if (isObject(firstColumn)) {
-      columnsRef.value = columns as any;
+      columnsRef.value = columns as BasicColumn[];
     } else {
-      const newColumns = unref(cacheColumnsRef).filter((item) =>
-        (columns as string[]).includes(`${item.key}`! || item.dataIndex!)
+      const newColumns = cacheColumns.filter(
+        (item) =>
+          (item.dataIndex || `${item.key}`) &&
+          (columns as string[]).includes(`${item.key}`! || item.dataIndex!)
       );
       columnsRef.value = newColumns;
     }
   }
 
-  return { getColumnsRef, setColumns };
+  function getColumns(opt?: GetColumnsParams) {
+    const { ignoreIndex, ignoreAction } = opt || {};
+    let columns = toRaw(unref(getColumnsRef));
+    if (ignoreIndex) {
+      columns = columns.filter((item) => item.flag !== INDEX_COLUMN_FLAG);
+    }
+    if (ignoreAction) {
+      columns = columns.filter((item) => item.flag !== ACTION_COLUMN_FLAG);
+    }
+    return columns;
+  }
+
+  return { getColumnsRef, getColumns, setColumns };
 }

+ 90 - 0
src/components/Table/src/hooks/useCustomRow.ts

@@ -0,0 +1,90 @@
+import type { ComputedRef } from 'vue';
+import type { BasicTableProps } from '../types/table';
+import { unref } from 'vue';
+import { ROW_KEY } from '../const';
+import { isString, isFunction } from '/@/utils/is';
+
+interface Options {
+  setSelectedRowKeys: (keys: string[]) => void;
+  getSelectRowKeys: () => string[];
+  clearSelectedRowKeys: () => void;
+  emit: EmitType;
+  getAutoCreateKey: ComputedRef<boolean | undefined>;
+}
+
+function getKey(
+  record: Recordable,
+  rowKey: string | ((record: Record<string, any>) => string) | undefined,
+  autoCreateKey?: boolean
+) {
+  if (!rowKey || autoCreateKey) {
+    return record[ROW_KEY];
+  }
+  if (isString(rowKey)) {
+    return record[rowKey];
+  }
+  if (isFunction(rowKey)) {
+    return record[rowKey(record)];
+  }
+  return null;
+}
+
+export function useCustomRow(
+  propsRef: ComputedRef<BasicTableProps>,
+  { setSelectedRowKeys, getSelectRowKeys, getAutoCreateKey, clearSelectedRowKeys, emit }: Options
+) {
+  const customRow = (record: Recordable, index: number) => {
+    return {
+      onClick: (e: Event) => {
+        emit('row-click', record, index, e);
+        e?.stopPropagation();
+        const { rowSelection, rowKey, clickToRowSelect } = unref(propsRef);
+        if (!rowSelection || !clickToRowSelect) return;
+        const keys = getSelectRowKeys();
+        const key = getKey(record, rowKey, unref(getAutoCreateKey));
+        if (!key) return;
+
+        const isCheckbox = rowSelection.type === 'checkbox';
+
+        if (isCheckbox) {
+          if (!keys.includes(key)) {
+            setSelectedRowKeys([...keys, key]);
+            return;
+          }
+          const keyIndex = keys.findIndex((item) => item === key);
+          keys.splice(keyIndex, 1);
+          setSelectedRowKeys(keys);
+          return;
+        }
+
+        const isRadio = rowSelection.type === 'radio';
+        if (isRadio) {
+          if (!keys.includes(key)) {
+            if (keys.length) {
+              clearSelectedRowKeys();
+            }
+            setSelectedRowKeys([key]);
+            return;
+          }
+          clearSelectedRowKeys();
+        }
+      },
+      onDblclick: (event: Event) => {
+        emit('row-dbClick', record, index, event);
+      },
+      onContextmenu: (event: Event) => {
+        emit('row-contextmenu', record, index, event);
+      },
+      onMouseenter: (event: Event) => {
+        emit('row-mouseenter', record, index, event);
+      },
+      onMouseleave: (event: Event) => {
+        emit('row-mouseleave', record, index, event);
+      },
+    };
+  };
+
+  return {
+    customRow,
+  };
+}

+ 58 - 45
src/components/Table/src/hooks/useDataSource.ts

@@ -1,7 +1,7 @@
 import type { BasicTableProps, FetchParams } from '../types/table';
 import type { PaginationProps } from '../types/pagination';
 
-import { watch, ref, unref, ComputedRef, computed, onMounted, Ref } from 'vue';
+import { ref, unref, ComputedRef, computed, onMounted, watchEffect } from 'vue';
 
 import { useTimeoutFn } from '/@/hooks/core/useTimeout';
 
@@ -9,39 +9,28 @@ import { buildUUID } from '/@/utils/uuid';
 import { isFunction, isBoolean } from '/@/utils/is';
 import { get } from 'lodash-es';
 
-import { useProps } from './useProps';
+import { FETCH_SETTING, ROW_KEY, PAGE_SIZE } from '../const';
 
-import { FETCH_SETTING, ROW_KEY } from '../const';
 interface ActionType {
-  getPaginationRef: ComputedRef<false | PaginationProps>;
+  getPaginationInfo: ComputedRef<boolean | PaginationProps>;
   setPagination: (info: Partial<PaginationProps>) => void;
-  loadingRef: Ref<boolean | undefined>;
-  getFieldsValue: () => {
-    [field: string]: any;
-  };
+  setLoading: (loading: boolean) => void;
+  getFieldsValue: () => Recordable;
 }
 export function useDataSource(
-  refProps: ComputedRef<BasicTableProps>,
-  { getPaginationRef, setPagination, loadingRef, getFieldsValue }: ActionType,
+  propsRef: ComputedRef<BasicTableProps>,
+  { getPaginationInfo, setPagination, setLoading, getFieldsValue }: ActionType,
   emit: EmitType
 ) {
-  const { propsRef } = useProps(refProps);
-
-  const dataSourceRef = ref<any[]>([]);
+  const dataSourceRef = ref<Recordable[]>([]);
 
-  watch(
-    () => unref(propsRef).dataSource,
-    (data: any[]) => {
-      const { api } = unref(propsRef);
-      !api && (dataSourceRef.value = data);
-    },
-    { immediate: true }
-  );
+  watchEffect(() => {
+    const { dataSource, api } = unref(propsRef);
+    !api && dataSource && (dataSourceRef.value = dataSource);
+  });
 
   function setTableKey(items: any[]) {
-    if (!items || !Array.isArray(items)) {
-      return;
-    }
+    if (!items || !Array.isArray(items)) return;
     items.forEach((item) => {
       if (!item[ROW_KEY]) {
         item[ROW_KEY] = buildUUID();
@@ -51,10 +40,16 @@ export function useDataSource(
       }
     });
   }
+
   const getAutoCreateKey = computed(() => {
     return unref(propsRef).autoCreateKey && !unref(propsRef).rowKey;
   });
 
+  const getRowKey = computed(() => {
+    const { rowKey } = unref(propsRef);
+    return unref(getAutoCreateKey) ? ROW_KEY : rowKey;
+  });
+
   const getDataSourceRef = computed(() => {
     const dataSource = unref(dataSourceRef);
     if (!dataSource || dataSource.length === 0) {
@@ -86,20 +81,20 @@ export function useDataSource(
     );
     if (!api || !isFunction(api)) return;
     try {
-      loadingRef.value = true;
+      setLoading(true);
       const { pageField, sizeField, listField, totalField } = fetchSetting || FETCH_SETTING;
-      let pageParams: any = {};
-      
-      const { current, pageSize } = unref(getPaginationRef) as PaginationProps;
-      
-      if (isBoolean(getPaginationRef)) {
+      let pageParams: Recordable = {};
+
+      const { current = 1, pageSize = PAGE_SIZE } = unref(getPaginationInfo) as PaginationProps;
+
+      if (isBoolean(getPaginationInfo)) {
         pageParams = {};
       } else {
         pageParams[pageField] = (opt && opt.page) || current;
         pageParams[sizeField] = pageSize;
       }
 
-      let params: any = {
+      let params: Recordable = {
         ...pageParams,
         ...(useSearchForm ? getFieldsValue() : {}),
         ...searchInfo,
@@ -112,18 +107,21 @@ export function useDataSource(
       }
 
       const res = await api(params);
-      let resultItems: any[] = get(res, listField);
-      const resultTotal: number = get(res, totalField);
-      
+
+      const isArrayResult = Array.isArray(res);
+
+      let resultItems: Recordable[] = isArrayResult ? res : get(res, listField);
+      const resultTotal: number = isArrayResult ? 0 : get(res, totalField);
+
       // 假如数据变少,导致总页数变少并小于当前选中页码,通过getPaginationRef获取到的页码是不正确的,需获取正确的页码再次执行
-      var currentTotalPage = Math.ceil(resultTotal / pageSize);
+      const currentTotalPage = Math.ceil(resultTotal / pageSize);
       if (current > currentTotalPage) {
-          setPagination({
-            current: currentTotalPage,
-          });
-          fetch(opt);
+        setPagination({
+          current: currentTotalPage,
+        });
+        fetch(opt);
       }
-      
+
       if (afterFetch && isFunction(afterFetch)) {
         resultItems = afterFetch(resultItems) || resultItems;
       }
@@ -147,20 +145,35 @@ export function useDataSource(
         total: 0,
       });
     } finally {
-      loadingRef.value = false;
-      // setSearchFormLoading(false);
+      setLoading(false);
     }
   }
 
-  function setTableData(values: any[]) {
+  function setTableData<T = Recordable>(values: T[]) {
     dataSourceRef.value = values;
   }
+
+  function getDataSource<T = Recordable>() {
+    return getDataSourceRef.value as T[];
+  }
+
+  async function reload(opt?: FetchParams) {
+    await fetch(opt);
+  }
+
   onMounted(() => {
-    // 转异步任务
     useTimeoutFn(() => {
       unref(propsRef).immediate && fetch();
     }, 0);
   });
 
-  return { getDataSourceRef, setTableData, getAutoCreateKey, fetch: fetch };
+  return {
+    getDataSourceRef,
+    getDataSource,
+    getRowKey,
+    setTableData,
+    getAutoCreateKey,
+    fetch,
+    reload,
+  };
 }

+ 19 - 14
src/components/Table/src/hooks/useLoading.ts

@@ -1,15 +1,20 @@
-import { watch, ref, ComputedRef, unref } from 'vue';
-import { BasicTableProps } from '../types/table';
-import { useProps } from './useProps';
-export function useLoading(refProps: ComputedRef<BasicTableProps>) {
-  const { propsRef } = useProps(refProps);
-
-  const loadingRef = ref(unref(propsRef).loading);
-  watch(
-    () => unref(propsRef).loading,
-    (v: boolean) => {
-      loadingRef.value = v;
-    }
-  );
-  return { loadingRef };
+import { ref, ComputedRef, unref, computed, watchEffect } from 'vue';
+import type { BasicTableProps } from '../types/table';
+
+export function useLoading(props: ComputedRef<BasicTableProps>) {
+  const loadingRef = ref(unref(props).loading);
+
+  watchEffect(() => {
+    loadingRef.value = unref(props).loading;
+  });
+
+  const getLoading = computed(() => {
+    return unref(loadingRef);
+  });
+
+  function setLoading(loading: boolean) {
+    loadingRef.value = loading;
+  }
+
+  return { getLoading, setLoading };
 }

+ 27 - 21
src/components/Table/src/hooks/usePagination.tsx

@@ -7,16 +7,30 @@ import { LeftOutlined, RightOutlined } from '@ant-design/icons-vue';
 import { isBoolean } from '/@/utils/is';
 
 import { PAGE_SIZE, PAGE_SIZE_OPTIONS } from '../const';
-import { useProps } from './useProps';
 import { useI18n } from '/@/hooks/web/useI18n';
 
-const { t } = useI18n();
+interface ItemRender {
+  page: number;
+  type: 'page' | 'prev' | 'next';
+  originalElement: any;
+}
+
+function itemRender({ page, type, originalElement }: ItemRender) {
+  if (type === 'prev') {
+    return page === 0 ? null : <LeftOutlined />;
+  } else if (type === 'next') {
+    return page === 1 ? null : <RightOutlined />;
+  }
+  return originalElement;
+}
+
 export function usePagination(refProps: ComputedRef<BasicTableProps>) {
   const configRef = ref<PaginationProps>({});
-  const { propsRef } = useProps(refProps);
 
-  const getPaginationRef = computed((): PaginationProps | false => {
-    const { pagination } = unref(propsRef);
+  const { t } = useI18n();
+  const getPaginationInfo = computed((): PaginationProps | boolean => {
+    const { pagination } = unref(refProps);
+
     if (isBoolean(pagination) && !pagination) {
       return false;
     }
@@ -28,20 +42,7 @@ export function usePagination(refProps: ComputedRef<BasicTableProps>) {
       showTotal: (total) => t('component.table.total', { total }),
       showSizeChanger: true,
       pageSizeOptions: PAGE_SIZE_OPTIONS,
-      itemRender: ({ page, type, originalElement }) => {
-        if (type === 'prev') {
-          if (page === 0) {
-            return null;
-          }
-          return <LeftOutlined />;
-        } else if (type === 'next') {
-          if (page === 1) {
-            return null;
-          }
-          return <RightOutlined />;
-        }
-        return originalElement;
-      },
+      itemRender: itemRender,
       showQuickJumper: true,
       ...(isBoolean(pagination) ? {} : pagination),
       ...unref(configRef),
@@ -49,10 +50,15 @@ export function usePagination(refProps: ComputedRef<BasicTableProps>) {
   });
 
   function setPagination(info: Partial<PaginationProps>) {
+    const paginationInfo = unref(getPaginationInfo);
     configRef.value = {
-      ...unref(getPaginationRef),
+      ...(!isBoolean(paginationInfo) ? paginationInfo : {}),
       ...info,
     };
   }
-  return { getPaginationRef, setPagination };
+
+  function getPagination() {
+    return unref(getPaginationInfo);
+  }
+  return { getPagination, getPaginationInfo, setPagination };
 }

+ 0 - 21
src/components/Table/src/hooks/useProps.ts

@@ -1,21 +0,0 @@
-import { Ref, ref, watch, unref } from 'vue';
-
-import type { BasicTableProps } from '../types/table';
-
-/**
- * @description:
- * @Date: 2020-05-12 13:20:37
- */
-export function useProps(props: Readonly<Ref<BasicTableProps>>) {
-  const propsRef = (ref<BasicTableProps>(unref(props)) as unknown) as Ref<BasicTableProps>;
-  watch(
-    () => props.value,
-    (v) => {
-      propsRef.value = unref(v);
-    },
-    {
-      immediate: false,
-    }
-  );
-  return { propsRef };
-}

+ 8 - 8
src/components/Table/src/hooks/useRowSelection.ts

@@ -1,17 +1,14 @@
 import type { BasicTableProps, TableRowSelection } from '../types/table';
 
 import { computed, ref, unref, ComputedRef } from 'vue';
-import { useProps } from './useProps';
 
 /* eslint-disable */
-export function useRowSelection(refProps: ComputedRef<BasicTableProps>, emit: EmitType) {
-  const { propsRef } = useProps(refProps);
-
+export function useRowSelection(propsRef: ComputedRef<BasicTableProps>, emit: EmitType) {
   const selectedRowKeysRef = ref<string[]>([]);
-  const selectedRowRef = ref<any[]>([]);
+  const selectedRowRef = ref<Recordable[]>([]);
 
   const getRowSelectionRef = computed((): TableRowSelection | null => {
-    const rowSelection = unref(propsRef).rowSelection;
+    const { rowSelection } = unref(propsRef);
     if (!rowSelection) {
       return null;
     }
@@ -46,11 +43,14 @@ export function useRowSelection(refProps: ComputedRef<BasicTableProps>, emit: Em
       unref(selectedRowKeysRef).splice(index, 1);
     }
   }
+
   function getSelectRowKeys() {
     return unref(selectedRowKeysRef);
   }
-  function getSelectRows() {
-    return unref(selectedRowRef);
+
+  function getSelectRows<T = Recordable>() {
+    // const ret = toRaw(unref(selectedRowRef)).map((item) => toRaw(item));
+    return unref(selectedRowRef) as T[];
   }
 
   return {

+ 5 - 4
src/components/Table/src/hooks/useTable.ts

@@ -14,10 +14,11 @@ export function useTable(
   const loadedRef = ref<Nullable<boolean>>(false);
 
   function register(instance: TableActionType) {
-    onUnmounted(() => {
-      tableRef.value = null;
-      loadedRef.value = null;
-    });
+    isProdMode() &&
+      onUnmounted(() => {
+        tableRef.value = null;
+        loadedRef.value = null;
+      });
 
     if (unref(loadedRef) && isProdMode() && instance === unref(tableRef)) {
       return;

+ 37 - 41
src/components/Table/src/hooks/useTableScroll.ts

@@ -1,57 +1,63 @@
 import type { BasicTableProps } from '../types/table';
-import { computed, Ref, onMounted, unref, ref, nextTick, ComputedRef, watch } from 'vue';
+import type { Ref, ComputedRef } from 'vue';
+import { computed, unref, ref, nextTick, watchEffect } from 'vue';
 
 import { getViewportOffset } from '/@/utils/domUtils';
 import { isBoolean } from '/@/utils/is';
 
 import { useWindowSizeFn } from '/@/hooks/event/useWindowSizeFn';
-import { useProps } from './useProps';
 import { useModalContext } from '/@/components/Modal';
 
-export function useTableScroll(refProps: ComputedRef<BasicTableProps>, tableElRef: Ref<any>) {
-  const { propsRef } = useProps(refProps);
-
-  const tableHeightRef: Ref<number | null> = ref(null);
+export function useTableScroll(
+  propsRef: ComputedRef<BasicTableProps>,
+  tableElRef: Ref<ComponentRef>
+) {
+  const tableHeightRef: Ref<Nullable<number>> = ref(null);
 
   const modalFn = useModalContext();
 
-  watch(
-    () => unref(propsRef).canResize,
-    () => {
-      redoHeight();
-    }
-  );
+  const getCanResize = computed(() => {
+    const { canResize, scroll } = unref(propsRef);
+    return canResize && !(scroll || {}).y;
+  });
 
-  function redoHeight() {
-    const { canResize } = unref(propsRef);
+  watchEffect(() => {
+    redoHeight();
+  });
 
-    if (!canResize) return;
-    calcTableHeight();
+  function redoHeight() {
+    if (unref(getCanResize)) {
+      nextTick(() => {
+        calcTableHeight();
+      });
+    }
   }
 
+  // No need to repeat queries
   let paginationEl: HTMLElement | null;
   let footerEl: HTMLElement | null;
+
   async function calcTableHeight() {
-    const { canResize, resizeHeightOffset, pagination, maxHeight } = unref(propsRef);
-    if (!canResize) return;
+    const { resizeHeightOffset, pagination, maxHeight } = unref(propsRef);
+    if (!unref(getCanResize)) return;
 
     await nextTick();
-    const table = unref(tableElRef) as any;
+    const table = unref(tableElRef);
     if (!table) return;
 
     const tableEl: Element = table.$el;
     if (!tableEl) return;
+
     const headEl = tableEl.querySelector('.ant-table-thead ');
     if (!headEl) return;
 
-    // 表格距离底部高度
+    // Table height from bottom
     const { bottomIncludeBody } = getViewportOffset(headEl);
-    // 表格高度+距离底部高度-自定义偏移量
+    // Table height from bottom height-custom offset
 
     const paddingHeight = 32;
     const borderHeight = 2 * 2;
-    // 分页器高度
-
+    // Pager height
     let paginationHeight = 2;
     if (!isBoolean(pagination)) {
       if (!paginationEl) {
@@ -61,7 +67,7 @@ export function useTableScroll(refProps: ComputedRef<BasicTableProps>, tableElRe
         const offsetHeight = paginationEl.offsetHeight;
         paginationHeight += offsetHeight || 0;
       } else {
-        // TODO 先固定24
+        // TODO First fix 24
         paginationHeight += 24;
       }
     }
@@ -75,11 +81,13 @@ export function useTableScroll(refProps: ComputedRef<BasicTableProps>, tableElRe
         footerHeight += offsetHeight || 0;
       }
     }
+
     let headerHeight = 0;
     if (headEl) {
       headerHeight = (headEl as HTMLElement).offsetHeight;
     }
-    tableHeightRef.value =
+
+    const height =
       bottomIncludeBody -
       (resizeHeightOffset || 0) -
       paddingHeight -
@@ -89,27 +97,14 @@ export function useTableScroll(refProps: ComputedRef<BasicTableProps>, tableElRe
       headerHeight;
 
     setTimeout(() => {
-      tableHeightRef.value =
-        tableHeightRef.value! > maxHeight! ? (maxHeight as number) : tableHeightRef.value;
-      //  解决表格放modal内的时候,modal自适应高度计算问题
+      tableHeightRef.value = (height > maxHeight! ? (maxHeight as number) : height) ?? height;
+      //  Solve the problem of modal adaptive height calculation when the form is placed in the modal
       modalFn?.redoModalHeight?.();
-    }, 16);
+    }, 0);
   }
 
-  const getCanResize = computed(() => {
-    const { canResize, scroll } = unref(propsRef);
-    return canResize && !(scroll || {}).y;
-  });
-
   useWindowSizeFn(calcTableHeight, 100);
 
-  onMounted(() => {
-    if (unref(getCanResize)) {
-      nextTick(() => {
-        calcTableHeight();
-      });
-    }
-  });
   const getScrollRef = computed(() => {
     const tableHeight = unref(tableHeightRef);
     const { canResize, scroll } = unref(propsRef);
@@ -121,5 +116,6 @@ export function useTableScroll(refProps: ComputedRef<BasicTableProps>, tableElRe
       ...scroll,
     };
   });
+
   return { getScrollRef, redoHeight };
 }

+ 18 - 0
src/components/Table/src/hooks/useTableStyle.ts

@@ -0,0 +1,18 @@
+import type { ComputedRef } from 'vue';
+import type { BasicTableProps, TableCustomRecord } from '../types/table';
+import { unref } from 'vue';
+import { isFunction } from '/@/utils/is';
+export function useTableStyle(propsRef: ComputedRef<BasicTableProps>) {
+  function getRowClassName(record: TableCustomRecord, index: number) {
+    const { striped, rowClassName } = unref(propsRef);
+    if (!striped) return;
+    if (rowClassName && isFunction(rowClassName)) {
+      return rowClassName(record);
+    }
+    return (index || 0) % 2 === 1 ? 'basic-table-row__striped' : '';
+  }
+
+  return {
+    getRowClassName,
+  };
+}

+ 1 - 1
src/components/Table/src/props.ts

@@ -14,6 +14,7 @@ import { propTypes } from '/@/utils/propTypes';
 
 // 注释看 types/table
 export const basicProps = {
+  clickToRowSelect: propTypes.bool.def(true),
   tableSetting: {
     type: Object as PropType<TableSetting>,
   },
@@ -34,7 +35,6 @@ export const basicProps = {
   },
 
   canColDrag: propTypes.bool.def(true),
-  isTreeTable: propTypes.bool,
   api: {
     type: Function as PropType<(...arg: any[]) => Promise<any>>,
     default: null,

+ 0 - 14
src/components/Table/src/style/index.less

@@ -167,20 +167,6 @@
     }
   }
 
-  .ant-radio {
-    &-inner {
-      border-color: @text-color-base;
-    }
-  }
-
-  .ant-checkbox {
-    &:not(.ant-checkbox-checked) {
-      .ant-checkbox-inner {
-        border-color: @text-color-base;
-      }
-    }
-  }
-
   .ant-table-bordered .ant-table-thead > tr:not(:last-child) > th,
   .ant-table-tbody > tr > td {
     word-break: break-word;

+ 5 - 5
src/components/Table/src/types/table.ts

@@ -124,6 +124,8 @@ export interface TableSetting {
 }
 
 export interface BasicTableProps<T = any> {
+  // 点击行选中
+  clickToRowSelect?: boolean;
   // 自定义排序方法
   sortFn?: (sortInfo: SorterResult) => any;
   // 取消表格的默认padding
@@ -141,8 +143,6 @@ export interface BasicTableProps<T = any> {
   showSummary?: boolean;
   // 是否可拖拽列
   canColDrag?: boolean;
-  // 是否树表
-  isTreeTable?: boolean;
   // 接口请求对象
   api?: (...arg: any) => Promise<any>;
   // 请求之前处理参数
@@ -158,7 +158,7 @@ export interface BasicTableProps<T = any> {
   // 在开起搜索表单的时候,如果没有数据是否显示表格
   emptyDataIsShowTable?: boolean;
   // 额外的请求参数
-  searchInfo?: any;
+  searchInfo?: Recordable;
   // 使用搜索表单
   useSearchForm?: boolean;
   // 表单配置
@@ -180,9 +180,9 @@ export interface BasicTableProps<T = any> {
   // 在分页改变的时候清空选项
   clearSelectOnPageChange?: boolean;
   //
-  rowKey?: string | ((record: any) => string);
+  rowKey?: string | ((record: Recordable) => string);
   // 数据
-  dataSource?: any[];
+  dataSource?: Recordable[];
   // 标题右侧提示
   titleHelpMessage?: string | string[];
   // 表格滚动最大高度

+ 5 - 1
src/views/demo/table/Basic.vue

@@ -9,7 +9,8 @@
       :loading="loading"
       :striped="striped"
       :bordered="border"
-      :pagination="{ pageSize: 20 }"
+      showTableSetting
+      :pagination="pagination"
     >
       <template #toolbar>
         <a-button type="primary" @click="toggleCanResize">
@@ -38,6 +39,7 @@
       const loading = ref(false);
       const striped = ref(true);
       const border = ref(true);
+      const pagination = ref<any>(false);
       function toggleCanResize() {
         canResize.value = !canResize.value;
       }
@@ -48,6 +50,7 @@
         loading.value = true;
         setTimeout(() => {
           loading.value = false;
+          pagination.value = { pageSize: 20 };
         }, 3000);
       }
       function toggleBorder() {
@@ -64,6 +67,7 @@
         toggleCanResize,
         toggleLoading,
         toggleBorder,
+        pagination,
       };
     },
   });

+ 2 - 2
src/views/demo/table/FixedColumn.vue

@@ -74,10 +74,10 @@
           slots: { customRender: 'action' },
         },
       });
-      function handleDelete(record: any) {
+      function handleDelete(record: Recordable) {
         console.log('点击了删除', record);
       }
-      function handleOpen(record: any) {
+      function handleOpen(record: Recordable) {
         console.log('点击了启用', record);
       }
       return {

+ 0 - 1
src/views/demo/table/TreeTable.vue

@@ -2,7 +2,6 @@
   <div class="p-4">
     <BasicTable
       :rowSelection="{ type: 'checkbox' }"
-      :isTreeTable="true"
       title="树形表格"
       titleHelpMessage="树形组件不能和序列号列同时存在"
       :columns="columns"

+ 2 - 1
src/views/demo/table/UseTable.vue

@@ -44,12 +44,13 @@
           clearSelectedRowKeys,
         },
       ] = useTable({
-        canResize: false,
+        canResize: true,
         title: 'useTable示例',
         titleHelpMessage: '使用useTable调用表格内方法',
         api: demoListApi,
         columns: getBasicColumns(),
         rowKey: 'id',
+        showTableSetting: true,
         rowSelection: {
           type: 'checkbox',
         },

+ 5 - 3
src/views/demo/table/tableData.tsx

@@ -5,13 +5,13 @@ export function getBasicColumns(): BasicColumn[] {
   return [
     {
       title: 'ID',
-      width: 150,
       dataIndex: 'id',
+      width: 150,
     },
     {
       title: '姓名',
       dataIndex: 'name',
-      width: 120,
+      width: 150,
     },
     {
       title: '地址',
@@ -20,14 +20,16 @@ export function getBasicColumns(): BasicColumn[] {
     {
       title: '编号',
       dataIndex: 'no',
-      width: 80,
+      width: 150,
     },
     {
       title: '开始时间',
+      width: 120,
       dataIndex: 'beginTime',
     },
     {
       title: '结束时间',
+      width: 120,
       sorter: true,
       dataIndex: 'endTime',
     },