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:
parent
00b8d169cb
commit
816553bfcd
|
|
@ -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);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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']);
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
|
|
@ -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() {
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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",
|
||||||
|
|
|
||||||
|
|
@ -67,7 +67,10 @@
|
||||||
"settingFixedRight": "固定到右侧",
|
"settingFixedRight": "固定到右侧",
|
||||||
"settingFullScreen": "全屏",
|
"settingFullScreen": "全屏",
|
||||||
"index": "序号",
|
"index": "序号",
|
||||||
"total": "共 {total} 条数据"
|
"total": "共 {total} 条数据",
|
||||||
|
"selectionBarTips": "已选择{count}条记录",
|
||||||
|
"selectionBarClear": "清空",
|
||||||
|
"selectionBarEmpty": "未选中任何记录"
|
||||||
},
|
},
|
||||||
"time": {
|
"time": {
|
||||||
"before": "前",
|
"before": "前",
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
|
|
||||||
|
|
@ -36,5 +36,6 @@
|
||||||
summaryFunc: handleSummary,
|
summaryFunc: handleSummary,
|
||||||
scroll: { x: 2000 },
|
scroll: { x: 2000 },
|
||||||
canResize: false,
|
canResize: false,
|
||||||
|
showSelectionBar: true, // 显示多选状态栏
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
|
||||||
|
|
@ -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() {
|
||||||
|
|
|
||||||
|
|
@ -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({
|
||||||
|
|
|
||||||
|
|
@ -32,5 +32,6 @@
|
||||||
columns: getBasicColumns(),
|
columns: getBasicColumns(),
|
||||||
dataSource: getTreeTableData(),
|
dataSource: getTreeTableData(),
|
||||||
rowKey: 'id',
|
rowKey: 'id',
|
||||||
|
showSelectionBar: true, // 显示多选状态栏
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
|
||||||
|
|
@ -64,6 +64,7 @@
|
||||||
onColumnsChange: (data: ColumnChangeParam[]) => {
|
onColumnsChange: (data: ColumnChangeParam[]) => {
|
||||||
console.log('ColumnsChanged', data);
|
console.log('ColumnsChanged', data);
|
||||||
},
|
},
|
||||||
|
showSelectionBar: true, // 显示多选状态栏
|
||||||
});
|
});
|
||||||
|
|
||||||
function changeLoading() {
|
function changeLoading() {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue