From 59a90877287a289f746eec97d12c2d3a1d5476b0 Mon Sep 17 00:00:00 2001
From: frezs <frezsen@gmail.com>
Date: 星期一, 06 九月 2021 09:14:12 +0800
Subject: [PATCH] feat(table):  添加和支持动态删除和插入数据 (#1152)

---
 src/components/Table/src/BasicTable.vue |  261 ++++++++++++++++++++++++++++++++++++++++++---------
 1 files changed, 213 insertions(+), 48 deletions(-)

diff --git a/src/components/Table/src/BasicTable.vue b/src/components/Table/src/BasicTable.vue
index 4d02ce1..1b74089 100644
--- a/src/components/Table/src/BasicTable.vue
+++ b/src/components/Table/src/BasicTable.vue
@@ -1,26 +1,16 @@
 <template>
-  <div
-    ref="wrapRef"
-    :class="[
-      prefixCls,
-      {
-        [`${prefixCls}-form-container`]: getBindValues.useSearchForm,
-        [`${prefixCls}--inset`]: getBindValues.inset,
-      },
-    ]"
-  >
+  <div ref="wrapRef" :class="getWrapperClass">
     <BasicForm
       submitOnReset
       v-bind="getFormProps"
       v-if="getBindValues.useSearchForm"
-      :submitButtonOptions="{ loading: getLoading }"
       :tableAction="tableAction"
       @register="registerForm"
       @submit="handleSearchInfoChange"
       @advanced-change="redoHeight"
     >
       <template #[replaceFormSlotKey(item)]="data" v-for="item in getFormSlotKeys">
-        <slot :name="item" v-bind="data" />
+        <slot :name="item" v-bind="data || {}"></slot>
       </template>
     </BasicForm>
 
@@ -31,9 +21,10 @@
       v-show="getEmptyDataIsShowTable"
       @change="handleTableChange"
     >
-      <template #[item]="data" v-for="item in Object.keys($slots)">
-        <slot :name="item" v-bind="data" />
+      <template #[item]="data" v-for="item in Object.keys($slots)" :key="item">
+        <slot :name="item" v-bind="data || {}"></slot>
       </template>
+
       <template #[`header-${column.dataIndex}`] v-for="column in columns" :key="column.dataIndex">
         <HeaderCell :column="column" />
       </template>
@@ -41,13 +32,20 @@
   </div>
 </template>
 <script lang="ts">
-  import type { BasicTableProps, TableActionType, SizeType } from './types/table';
+  import type {
+    BasicTableProps,
+    TableActionType,
+    SizeType,
+    ColumnChangeParam,
+  } from './types/table';
 
-  import { defineComponent, ref, computed, unref } from 'vue';
+  import { defineComponent, ref, computed, unref, toRaw, inject, watchEffect } from 'vue';
   import { Table } from 'ant-design-vue';
   import { BasicForm, useForm } from '/@/components/Form/index';
-
-  import { omit } from 'lodash-es';
+  import { PageWrapperFixedHeightKey } from '/@/components/Page';
+  import expandIcon from './components/ExpandIcon';
+  import HeaderCell from './components/HeaderCell.vue';
+  import { InnerHandlers } from './types/table';
 
   import { usePagination } from './hooks/usePagination';
   import { useColumns } from './hooks/useColumns';
@@ -58,23 +56,24 @@
   import { useCustomRow } from './hooks/useCustomRow';
   import { useTableStyle } from './hooks/useTableStyle';
   import { useTableHeader } from './hooks/useTableHeader';
+  import { useTableExpand } from './hooks/useTableExpand';
   import { createTableContext } from './hooks/useTableContext';
   import { useTableFooter } from './hooks/useTableFooter';
   import { useTableForm } from './hooks/useTableForm';
-  import { useExpose } from '/@/hooks/core/useExpose';
   import { useDesign } from '/@/hooks/web/useDesign';
 
+  import { omit } from 'lodash-es';
   import { basicProps } from './props';
-  import { createAsyncComponent } from '/@/utils/factory/createAsyncComponent';
+  import { isFunction } from '/@/utils/is';
+  import { warn } from '/@/utils/log';
 
-  import './style/index.less';
   export default defineComponent({
-    props: basicProps,
     components: {
       Table,
       BasicForm,
-      HeaderCell: createAsyncComponent(() => import('./components/HeaderCell.vue')),
+      HeaderCell,
     },
+    props: basicProps,
     emits: [
       'fetch-success',
       'fetch-error',
@@ -87,11 +86,17 @@
       'row-mouseleave',
       'edit-end',
       'edit-cancel',
+      'edit-row-end',
+      'edit-change',
+      'expanded-rows-change',
+      'change',
+      'columns-change',
     ],
-    setup(props, { attrs, emit, slots }) {
-      const tableElRef = ref<ComponentRef>(null);
+    setup(props, { attrs, emit, slots, expose }) {
+      const tableElRef = ref(null);
+      const tableData = ref<Recordable[]>([]);
 
-      const wrapRef = ref<Nullable<HTMLDivElement>>(null);
+      const wrapRef = ref(null);
       const innerPropsRef = ref<Partial<BasicTableProps>>();
 
       const { prefixCls } = useDesign('basic-table');
@@ -99,6 +104,15 @@
 
       const getProps = computed(() => {
         return { ...props, ...unref(innerPropsRef) } as BasicTableProps;
+      });
+
+      const isFixedHeightPage = inject(PageWrapperFixedHeightKey, false);
+      watchEffect(() => {
+        unref(isFixedHeightPage) &&
+          props.canResize &&
+          warn(
+            "'canResize' of BasicTable may not work in PageWrapper with 'fixedHeight' (especially in hot updates)",
+          );
       });
 
       const { getLoading, setLoading } = useLoading(getProps);
@@ -118,13 +132,18 @@
         getSelectRowKeys,
         deleteSelectRowByKey,
         setSelectedRowKeys,
-      } = useRowSelection(getProps, emit);
+      } = useRowSelection(getProps, tableData, emit);
 
       const {
-        handleTableChange,
+        handleTableChange: onTableChange,
         getDataSourceRef,
         getDataSource,
+        getRawDataSource,
         setTableData,
+        updateTableDataRecord,
+        deleteTableDataRecord,
+        insertTableDataRecord,
+        findTableDataRecord,
         fetch,
         getRowKey,
         reload,
@@ -133,25 +152,39 @@
       } = useDataSource(
         getProps,
         {
+          tableData,
           getPaginationInfo,
           setLoading,
           setPagination,
           getFieldsValue: formActions.getFieldsValue,
           clearSelectedRowKeys,
         },
-        emit
+        emit,
       );
 
-      const { getViewColumns, getColumns, setColumns, getColumnsRef, getCacheColumns } = useColumns(
-        getProps,
-        getPaginationInfo
-      );
+      function handleTableChange(...args) {
+        onTableChange.call(undefined, ...args);
+        emit('change', ...args);
+        // 瑙e喅閫氳繃useTable娉ㄥ唽onChange鏃朵笉璧蜂綔鐢ㄧ殑闂
+        const { onChange } = unref(getProps);
+        onChange && isFunction(onChange) && onChange.call(undefined, ...args);
+      }
+
+      const {
+        getViewColumns,
+        getColumns,
+        setCacheColumnsByField,
+        setColumns,
+        getColumnsRef,
+        getCacheColumns,
+      } = useColumns(getProps, getPaginationInfo);
 
       const { getScrollRef, redoHeight } = useTableScroll(
         getProps,
         tableElRef,
         getColumnsRef,
-        getRowSelectionRef
+        getRowSelectionRef,
+        getDataSourceRef,
       );
 
       const { customRow } = useCustomRow(getProps, {
@@ -164,27 +197,36 @@
 
       const { getRowClassName } = useTableStyle(getProps, prefixCls);
 
-      const { getHeaderProps } = useTableHeader(getProps, slots);
+      const { getExpandOption, expandAll, collapseAll } = useTableExpand(getProps, tableData, emit);
+
+      const handlers: InnerHandlers = {
+        onColumnsChange: (data: ColumnChangeParam[]) => {
+          emit('columns-change', data);
+          // support useTable
+          unref(getProps).onColumnsChange?.(data);
+        },
+      };
+
+      const { getHeaderProps } = useTableHeader(getProps, slots, handlers);
 
       const { getFooterProps } = useTableFooter(
         getProps,
         getScrollRef,
         tableElRef,
-        getDataSourceRef
+        getDataSourceRef,
       );
 
-      const {
-        getFormProps,
-        replaceFormSlotKey,
-        getFormSlotKeys,
-        handleSearchInfoChange,
-      } = useTableForm(getProps, slots, fetch);
+      const { getFormProps, replaceFormSlotKey, getFormSlotKeys, handleSearchInfoChange } =
+        useTableForm(getProps, slots, fetch, getLoading);
 
       const getBindValues = computed(() => {
+        const dataSource = unref(getDataSourceRef);
         let propsData: Recordable = {
           size: 'middle',
+          // ...(dataSource.length === 0 ? { getPopupContainer: () => document.body } : {}),
           ...attrs,
           customRow,
+          expandIcon: slots.expandIcon ? null : expandIcon(),
           ...unref(getProps),
           ...unref(getHeaderProps),
           scroll: unref(getScrollRef),
@@ -192,15 +234,30 @@
           tableLayout: 'fixed',
           rowSelection: unref(getRowSelectionRef),
           rowKey: unref(getRowKey),
-          columns: unref(getViewColumns),
-          pagination: unref(getPaginationInfo),
-          dataSource: unref(getDataSourceRef),
+          columns: toRaw(unref(getViewColumns)),
+          pagination: toRaw(unref(getPaginationInfo)),
+          dataSource,
           footer: unref(getFooterProps),
+          ...unref(getExpandOption),
         };
         if (slots.expandedRowRender) {
           propsData = omit(propsData, 'scroll');
         }
+
+        propsData = omit(propsData, ['class', 'onChange']);
         return propsData;
+      });
+
+      const getWrapperClass = computed(() => {
+        const values = unref(getBindValues);
+        return [
+          prefixCls,
+          attrs.class,
+          {
+            [`${prefixCls}-form-container`]: values.useSearchForm,
+            [`${prefixCls}--inset`]: values.inset,
+          },
+        ];
       });
 
       const getEmptyDataIsShowTable = computed(() => {
@@ -223,11 +280,16 @@
         deleteSelectRowByKey,
         setPagination,
         setTableData,
+        updateTableDataRecord,
+        deleteTableDataRecord,
+        insertTableDataRecord,
+        findTableDataRecord,
         redoHeight,
         setSelectedRowKeys,
         setColumns,
         setLoading,
         getDataSource,
+        getRawDataSource,
         setProps,
         getRowSelection,
         getPaginationRef: getPagination,
@@ -237,13 +299,16 @@
         updateTableData,
         setShowPagination,
         getShowPagination,
+        setCacheColumnsByField,
+        expandAll,
+        collapseAll,
         getSize: () => {
           return unref(getBindValues).size as SizeType;
         },
       };
       createTableContext({ ...tableAction, wrapRef, getBindValues });
 
-      useExpose<TableActionType>(tableAction);
+      expose(tableAction);
 
       emit('register', tableAction, formActions);
 
@@ -259,12 +324,112 @@
         wrapRef,
         tableAction,
         redoHeight,
-        getFormProps,
+        getFormProps: getFormProps as any,
         replaceFormSlotKey,
         getFormSlotKeys,
-        prefixCls,
+        getWrapperClass,
         columns: getViewColumns,
       };
     },
   });
 </script>
+<style lang="less">
+  @border-color: #cecece4d;
+
+  @prefix-cls: ~'@{namespace}-basic-table';
+
+  [data-theme='dark'] {
+    .ant-table-tbody > tr:hover.ant-table-row-selected > td,
+    .ant-table-tbody > tr.ant-table-row-selected td {
+      background-color: #262626;
+    }
+  }
+
+  .@{prefix-cls} {
+    max-width: 100%;
+
+    &-row__striped {
+      td {
+        background-color: @app-content-background;
+      }
+    }
+
+    &-form-container {
+      padding: 16px;
+
+      .ant-form {
+        padding: 12px 10px 6px 10px;
+        margin-bottom: 16px;
+        background-color: @component-background;
+        border-radius: 2px;
+      }
+    }
+
+    &--inset {
+      .ant-table-wrapper {
+        padding: 0;
+      }
+    }
+
+    .ant-tag {
+      margin-right: 0;
+    }
+
+    .ant-table-wrapper {
+      padding: 6px;
+      background-color: @component-background;
+      border-radius: 2px;
+
+      .ant-table-title {
+        min-height: 40px;
+        padding: 0 0 8px 0 !important;
+      }
+
+      .ant-table.ant-table-bordered .ant-table-title {
+        border: none !important;
+      }
+    }
+
+    .ant-table {
+      width: 100%;
+      overflow-x: hidden;
+
+      &-title {
+        display: flex;
+        padding: 8px 6px;
+        border-bottom: none;
+        justify-content: space-between;
+        align-items: center;
+      }
+
+      //.ant-table-tbody > tr.ant-table-row-selected td {
+      //background-color: fade(@primary-color, 8%) !important;
+      //}
+    }
+
+    .ant-pagination {
+      margin: 10px 0 0 0;
+    }
+
+    .ant-table-footer {
+      padding: 0;
+
+      .ant-table-wrapper {
+        padding: 0;
+      }
+
+      table {
+        border: none !important;
+      }
+
+      .ant-table-body {
+        overflow-x: hidden !important;
+        //  overflow-y: scroll !important;
+      }
+
+      td {
+        padding: 12px 8px;
+      }
+    }
+  }
+</style>

--
Gitblit v1.8.0