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

* feat: add TableSelectionBar

* feat: enable TableSelectionBar for checkbox rowSelection demo
This commit is contained in:
xachary 2023-12-29 09:20:02 +08:00 committed by GitHub
parent 00b8d169cb
commit 816553bfcd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 123 additions and 97 deletions

View File

@ -53,7 +53,7 @@
import { BasicForm, useForm } from '@/components/Form'; import { BasicForm, useForm } from '@/components/Form';
import { PageWrapperFixedHeightKey } from '@/enums/pageEnum'; import { PageWrapperFixedHeightKey } from '@/enums/pageEnum';
import HeaderCell from './components/HeaderCell.vue'; import HeaderCell from './components/HeaderCell.vue';
import { InnerHandlers } from './types/table'; import { InnerHandlers, InnerMethods } from './types/table';
import { usePagination } from './hooks/usePagination'; import { usePagination } from './hooks/usePagination';
import { useColumns } from './hooks/useColumns'; import { useColumns } from './hooks/useColumns';
import { useDataSource } from './hooks/useDataSource'; 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); const { getFooterProps } = useTableFooter(getProps, getScrollRef, tableElRef, getDataSourceRef);

View File

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

View File

@ -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>

View File

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

View File

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

View File

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

View File

@ -312,6 +312,12 @@ export interface BasicTableProps<T = any> {
*/ */
rowSelection?: TableRowSelection; 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. * 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, * 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; onColumnsChange: (data: ColumnChangeParam[]) => void;
} }
export interface InnerMethods {
clearSelectedRowKeys: TableActionType['clearSelectedRowKeys'];
getSelectRowKeys: TableActionType['getSelectRowKeys'];
}
export interface ColumnOptionsType { export interface ColumnOptionsType {
value: string; value: string;
label: string; label: string;

View File

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

View File

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

View File

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

View File

@ -36,5 +36,6 @@
summaryFunc: handleSummary, summaryFunc: handleSummary,
scroll: { x: 2000 }, scroll: { x: 2000 },
canResize: false, canResize: false,
showSelectionBar: true, //
}); });
</script> </script>

View File

@ -1,102 +1,18 @@
<template> <template>
<BasicTable @register="registerTable" @fetch-success="checkedRecordsUpdate"> <BasicTable @register="registerTable">
<template #form-custom> custom-slot </template> <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> <template #toolbar>
<a-button type="primary" @click="getFormValues">获取表单数据</a-button> <a-button type="primary" @click="getFormValues">获取表单数据</a-button>
</template> </template>
</BasicTable> </BasicTable>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { ref, nextTick } from 'vue';
import { BasicTable, useTable } from '@/components/Table'; import { BasicTable, useTable } from '@/components/Table';
import { getBasicColumns, getFormConfig } from './tableData'; 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'; import { demoListApi } from '@/api/demo/table';
const checkedRecords = ref<Key[]>([]); const [registerTable, { getForm }] = useTable({
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({
title: '开启搜索区域', title: '开启搜索区域',
api: demoListApi, api: demoListApi,
columns: getBasicColumns(), columns: getBasicColumns(),
@ -108,8 +24,8 @@
rowKey: 'id', rowKey: 'id',
rowSelection: { rowSelection: {
type: 'checkbox', type: 'checkbox',
onChange: rowSelectionOnChange,
}, },
showSelectionBar: true, //
}); });
function getFormValues() { function getFormValues() {

View File

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

View File

@ -32,5 +32,6 @@
columns: getBasicColumns(), columns: getBasicColumns(),
dataSource: getTreeTableData(), dataSource: getTreeTableData(),
rowKey: 'id', rowKey: 'id',
showSelectionBar: true, //
}); });
</script> </script>

View File

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