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