Browse Source

feat(demo->BasicTable): add TableSelectionBar and enable checkbox rowSelection demo (#3477)

* feat: add TableSelectionBar

* feat: enable TableSelectionBar for checkbox rowSelection demo
xachary 1 year ago
parent
commit
816553bfcd

+ 7 - 2
src/components/Table/src/BasicTable.vue

@@ -53,7 +53,7 @@
   import { BasicForm, useForm } from '@/components/Form';
   import { PageWrapperFixedHeightKey } from '@/enums/pageEnum';
   import HeaderCell from './components/HeaderCell.vue';
-  import { InnerHandlers } from './types/table';
+  import { InnerHandlers, InnerMethods } from './types/table';
   import { usePagination } from './hooks/usePagination';
   import { useColumns } from './hooks/useColumns';
   import { useDataSource } from './hooks/useDataSource';
@@ -221,7 +221,12 @@
     },
   };
 
-  const { getHeaderProps } = useTableHeader(getProps, slots, handlers);
+  const methods: InnerMethods = {
+    clearSelectedRowKeys,
+    getSelectRowKeys,
+  };
+
+  const { getHeaderProps } = useTableHeader(getProps, slots, handlers, methods);
 
   const { getFooterProps } = useTableFooter(getProps, getScrollRef, tableElRef, getDataSourceRef);
 

+ 18 - 2
src/components/Table/src/components/TableHeader.vue

@@ -3,6 +3,9 @@
     <div v-if="$slots.headerTop" style="margin: 5px">
       <slot name="headerTop"></slot>
     </div>
+    <div v-if="showSelectionBar" style="margin: 5px">
+      <TableSelectionBar :clearSelectedRowKeys="props.clearSelectedRowKeys!" :count="props.count" />
+    </div>
     <div class="flex items-center">
       <slot name="tableTitle" v-if="$slots.tableTitle"></slot>
       <TableTitle
@@ -23,16 +26,17 @@
   </div>
 </template>
 <script lang="ts" setup>
-  import type { TableSetting, ColumnChangeParam } from '../types/table';
+  import type { TableSetting, ColumnChangeParam, TableActionType } from '../types/table';
   import type { PropType } from 'vue';
   import { Divider } from 'ant-design-vue';
   import TableSettingComponent from './settings/index.vue';
   import TableTitle from './TableTitle.vue';
   import { useDesign } from '@/hooks/web/useDesign';
+  import TableSelectionBar from '../components/TableSelectionBar.vue';
 
   defineOptions({ name: 'BasicTableHeader' });
 
-  defineProps({
+  const props = defineProps({
     title: {
       type: [Function, String] as PropType<string | ((data) => string)>,
     },
@@ -46,6 +50,18 @@
       type: [String, Array] as PropType<string | string[]>,
       default: '',
     },
+    //
+    clearSelectedRowKeys: {
+      type: Function as PropType<TableActionType['clearSelectedRowKeys']>,
+    },
+    count: {
+      type: Number,
+      default: 0,
+    },
+    showSelectionBar: {
+      type: Boolean,
+      default: false,
+    },
   });
 
   const emit = defineEmits(['columns-change']);

+ 56 - 0
src/components/Table/src/components/TableSelectionBar.vue

@@ -0,0 +1,56 @@
+<template>
+  <a-alert type="info" showIcon :class="[prefixCls]">
+    <template #message>
+      <span v-if="props.count > 0">
+        {{ t('component.table.selectionBarTips', { count: props.count }) }}
+      </span>
+      <span v-else>
+        {{ t('component.table.selectionBarEmpty') }}
+      </span>
+      <a-button type="link" @click="clearSelectedRowKeys" size="small" v-show="props.count > 0">
+        {{ t('component.table.selectionBarClear') }}
+      </a-button>
+    </template>
+  </a-alert>
+</template>
+
+<script lang="ts" setup>
+  import { useI18n } from '@/hooks/web/useI18n';
+  import { useDesign } from '@/hooks/web/useDesign';
+
+  import type { TableActionType } from '../types/table';
+  import { Alert as AAlert } from 'ant-design-vue';
+
+  const { t } = useI18n();
+
+  const { prefixCls } = useDesign('table-select-bar');
+
+  defineOptions({
+    name: 'TableSelectBar',
+  });
+
+  const props = withDefaults(
+    defineProps<{
+      count?: number;
+      //
+      clearSelectedRowKeys: TableActionType['clearSelectedRowKeys'];
+    }>(),
+    {
+      count: () => 0,
+    },
+  );
+</script>
+
+<style lang="less" scoped>
+  @prefix-cls: ~'@{namespace}-table-select-bar';
+
+  .@{prefix-cls} {
+    flex-grow: 1;
+    padding: 2px 8px;
+
+    :deep(.ant-btn-link) {
+      height: 20px;
+      line-height: 20px;
+    }
+  }
+</style>

+ 5 - 0
src/components/Table/src/hooks/useRowSelection.ts

@@ -83,7 +83,12 @@ export function useRowSelection(
   }
 
   function setSelectedRows(rows: Recordable[]) {
+    const { rowKey } = unref(propsRef);
     selectedRowRef.value = rows;
+    selectedRowKeysRef.value = selectedRowRef.value.map((o) => {
+      const key = (isFunction(rowKey) ? rowKey(o) : rowKey) || 'key';
+      return o[key];
+    });
   }
 
   function clearSelectedRowKeys() {

+ 9 - 2
src/components/Table/src/hooks/useTableHeader.ts

@@ -1,5 +1,5 @@
 import type { ComputedRef, Slots } from 'vue';
-import type { BasicTableProps, InnerHandlers } from '../types/table';
+import type { BasicTableProps, InnerHandlers, InnerMethods } from '../types/table';
 import { unref, computed, h } from 'vue';
 import TableHeader from '../components/TableHeader.vue';
 import { isString } from '@/utils/is';
@@ -9,9 +9,12 @@ export function useTableHeader(
   propsRef: ComputedRef<BasicTableProps>,
   slots: Slots,
   handlers: InnerHandlers,
+  //
+  methods: InnerMethods,
 ) {
   const getHeaderProps = computed((): Recordable => {
-    const { title, showTableSetting, titleHelpMessage, tableSetting } = unref(propsRef);
+    const { title, showTableSetting, titleHelpMessage, tableSetting, showSelectionBar } =
+      unref(propsRef);
     const hideTitle = !slots.tableTitle && !title && !slots.toolbar && !showTableSetting;
     if (hideTitle && !isString(title)) {
       return {};
@@ -29,6 +32,10 @@ export function useTableHeader(
                 showTableSetting,
                 tableSetting,
                 onColumnsChange: handlers.onColumnsChange,
+                //
+                clearSelectedRowKeys: methods.clearSelectedRowKeys,
+                count: methods.getSelectRowKeys().length,
+                showSelectionBar,
               } as Recordable,
               {
                 ...(slots.toolbar

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

@@ -105,6 +105,7 @@ export const basicProps = {
     type: Object as PropType<TableRowSelection | null>,
     default: null,
   },
+  showSelectionBar: propTypes.bool,
   title: {
     type: [String, Function] as PropType<string | ((data: Recordable) => string)>,
     default: null,

+ 11 - 0
src/components/Table/src/types/table.ts

@@ -312,6 +312,12 @@ export interface BasicTableProps<T = any> {
    */
   rowSelection?: TableRowSelection;
 
+  /**
+   * Show table selection bar(显示多选状态栏)
+   * @type boolean
+   */
+  showSelectionBar?: boolean;
+
   /**
    * Set horizontal or vertical scrolling, can also be used to specify the width and height of the scroll area.
    * It is recommended to set a number for x, if you want to set it to true,
@@ -489,6 +495,11 @@ export interface InnerHandlers {
   onColumnsChange: (data: ColumnChangeParam[]) => void;
 }
 
+export interface InnerMethods {
+  clearSelectedRowKeys: TableActionType['clearSelectedRowKeys'];
+  getSelectRowKeys: TableActionType['getSelectRowKeys'];
+}
+
 export interface ColumnOptionsType {
   value: string;
   label: string;

+ 4 - 1
src/locales/lang/en/component.json

@@ -67,7 +67,10 @@
     "settingFixedRight": "Fixed Right",
     "settingFullScreen": "Full Screen",
     "index": "Index",
-    "total": "total of {total}"
+    "total": "total of {total}",
+    "selectionBarTips": "{count} records selected.",
+    "selectionBarClear": "Clear",
+    "selectionBarEmpty": "No records selected."
   },
   "time": {
     "before": " ago",

+ 4 - 1
src/locales/lang/zh-CN/component.json

@@ -67,7 +67,10 @@
     "settingFixedRight": "固定到右侧",
     "settingFullScreen": "全屏",
     "index": "序号",
-    "total": "共 {total} 条数据"
+    "total": "共 {total} 条数据",
+    "selectionBarTips": "已选择{count}条记录",
+    "selectionBarClear": "清空",
+    "selectionBarEmpty": "未选中任何记录"
   },
   "time": {
     "before": "前",

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

@@ -137,6 +137,7 @@
       dataIndex: 'action',
       // slots: { customRender: 'action' },
     },
+    showSelectionBar: true, // 显示多选状态栏
   });
   function handleEdit(record: Recordable) {
     console.log('点击了编辑', record);

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

@@ -36,5 +36,6 @@
     summaryFunc: handleSummary,
     scroll: { x: 2000 },
     canResize: false,
+    showSelectionBar: true, // 显示多选状态栏
   });
 </script>

+ 3 - 87
src/views/demo/table/FormTable.vue

@@ -1,102 +1,18 @@
 <template>
-  <BasicTable @register="registerTable" @fetch-success="checkedRecordsUpdate">
+  <BasicTable @register="registerTable">
     <template #form-custom> custom-slot </template>
-    <template #headerTop>
-      <Alert type="info" show-icon>
-        <template #message>
-          <template v-if="checkedRecords.length > 0">
-            <span>已选中{{ checkedRecords.length }}条记录(可跨页)</span>
-            <a-button type="link" @click="tableSelectBarClear" size="small">清空</a-button>
-          </template>
-          <template v-else>
-            <span>未选中任何项目</span>
-          </template>
-        </template>
-      </Alert>
-    </template>
     <template #toolbar>
       <a-button type="primary" @click="getFormValues">获取表单数据</a-button>
     </template>
   </BasicTable>
 </template>
 <script lang="ts" setup>
-  import { ref, nextTick } from 'vue';
   import { BasicTable, useTable } from '@/components/Table';
   import { getBasicColumns, getFormConfig } from './tableData';
-  import { Alert } from 'ant-design-vue';
-  import type { Key } from 'ant-design-vue/lib/table/interface';
-  import type { TableRowSelection } from '@/components/Table/src/types/table';
 
   import { demoListApi } from '@/api/demo/table';
 
-  const checkedRecords = ref<Key[]>([]);
-  const checkedPageRecords = ref<Key[]>([]);
-
-  const rowSelectionOnChange: TableRowSelection['onChange'] = (selectedRowKeys) => {
-    // 本页新出现的
-    const adds = selectedRowKeys.filter((key) => !checkedPageRecords.value.includes(key));
-    // 本页已消失的
-    const removes = checkedPageRecords.value.filter((key) => !selectedRowKeys.includes(key));
-
-    // 添加/更新到全部
-    for (const k of adds) {
-      const index = checkedRecords.value.findIndex((key) => key === k);
-      if (index > -1) {
-        checkedRecords.value.splice(index, 1, k);
-      } else {
-        checkedRecords.value.push(k);
-      }
-    }
-
-    // 从全部删除
-    for (const k of removes) {
-      const index = checkedRecords.value.findIndex((key) => key === k);
-      if (index > -1) {
-        checkedRecords.value.splice(index, 1);
-      }
-    }
-
-    // 刷新本页记录
-    checkedPageRecords.value = [...selectedRowKeys];
-  };
-
-  // 清空选择
-  const tableSelectBarClear = () => {
-    checkedRecords.value = [];
-    setSelectedRowKeys([]);
-  };
-
-  // 移除记录(如果存在删除记录的操作)
-  // const checkedRecordsRemove = (ids: (string | number)[]) => {
-  //   for (const id of ids) {
-  //     const index = checkedRecords.value.findIndex((o) => o.id === id);
-  //     if (index > -1) {
-  //       checkedRecords.value.splice(index, 1);
-  //     }
-  //   }
-  // };
-
-  const checkedRecordsUpdate = () => {
-    // 当前页数据
-    const dataSourceKeys = getDataSource().map((o) => o.id) as Array<Key>;
-    for (const record of getDataSource()) {
-      const index = checkedRecords.value.findIndex((key) => key === record.id);
-      if (index > -1) {
-        // 如果全部里存在,就更新它
-        checkedRecords.value.splice(index, 1, record.id as Key);
-      }
-    }
-    // 当前页存在全部里的
-    const pageRecords = checkedRecords.value.filter((key) => dataSourceKeys.includes(key));
-    // 刷新
-    checkedPageRecords.value = pageRecords;
-    nextTick(() => {
-      // 选中
-      setSelectedRowKeys(pageRecords);
-    });
-  };
-
-  const [registerTable, { getForm, setSelectedRowKeys, getDataSource }] = useTable({
+  const [registerTable, { getForm }] = useTable({
     title: '开启搜索区域',
     api: demoListApi,
     columns: getBasicColumns(),
@@ -108,8 +24,8 @@
     rowKey: 'id',
     rowSelection: {
       type: 'checkbox',
-      onChange: rowSelectionOnChange,
     },
+    showSelectionBar: true, // 显示多选状态栏
   });
 
   function getFormValues() {

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

@@ -25,6 +25,7 @@
       :columns="columns"
       rowKey="id"
       :rowSelection="{ type: 'checkbox' }"
+      showSelectionBar
     />
   </div>
 </template>
@@ -60,7 +61,6 @@
       rowSelection: {
         type: 'checkbox',
       },
-      showIndexColumn: true,
     });
   }
   function reloadTable() {
@@ -69,7 +69,6 @@
       rowSelection: {
         type: 'checkbox',
       },
-      showIndexColumn: true,
     });
 
     getTableAction().reload({

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

@@ -32,5 +32,6 @@
     columns: getBasicColumns(),
     dataSource: getTreeTableData(),
     rowKey: 'id',
+    showSelectionBar: true, // 显示多选状态栏
   });
 </script>

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

@@ -64,6 +64,7 @@
     onColumnsChange: (data: ColumnChangeParam[]) => {
       console.log('ColumnsChanged', data);
     },
+    showSelectionBar: true, // 显示多选状态栏
   });
 
   function changeLoading() {