feat: 表格跨页选择

This commit is contained in:
RubyLiu 2023-09-20 19:04:34 +08:00 committed by 刘瑞斌
parent 97db41771b
commit be7d73ada4
5 changed files with 209 additions and 193 deletions

View File

@ -0,0 +1,21 @@
<template>
<a-checkbox :model-value="checked" @change="handleChange"></a-checkbox>
</template>
<script setup lang="ts">
import { ref, watchEffect } from 'vue';
const props = defineProps<{
value: boolean;
}>();
const emit = defineEmits<{
(e: 'change', value: boolean): void;
}>();
const checked = ref(false);
const handleChange = (v: boolean | (string | number | boolean)[]) => {
emit('change', v as boolean);
};
watchEffect(() => {
checked.value = props.value;
});
</script>

View File

@ -1,25 +1,32 @@
<template>
<div class="ms-base-table">
<select-all
v-if="attrs.selectable && attrs.showSelectAll"
class="custom-action"
:total="selectTotal"
:current="selectCurrent"
@change="handleChange"
/>
<a-table
v-bind="$attrs"
:row-class="getRowClass"
:selected-keys="props.selectedKeys"
:span-method="spanMethod"
:columns="currentColumns"
@selection-change="(e) => selectionChange(e, true)"
@sorter-change="(dataIndex: string,direction: string) => handleSortChange(dataIndex, direction)"
>
<template #optional="{ rowIndex, record }">
<slot name="optional" v-bind="{ rowIndex, record }" />
</template>
<template #columns>
<a-table-column v-if="attrs.selectable && props.selectedKeys" :width="60">
<template #title>
<select-all
:total="selectTotal"
:current="selectCurrent"
:type="attrs.selectorType as ('checkbox' | 'radio')"
@change="handleSelectAllChange"
/>
</template>
<template #cell="{ record }">
<MsCheckbox
:value="props.selectedKeys.has(record[rowKey || 'id'])"
@change="rowSelectChange(record[rowKey || 'id'])"
/>
</template>
</a-table-column>
<a-table-column
v-for="(item, idx) in currentColumns"
:key="idx"
@ -57,16 +64,16 @@
<div class="flex flex-row flex-nowrap items-center">
<template v-if="item.dataIndex === SpecialColumnEnum.ENABLE">
<slot name="enable" v-bind="{ record }">
<template v-if="record.enable">
<icon-check-circle-fill class="mr-[2px] text-[rgb(var(--success-6))]" />
{{ item.enableTitle ? t(item.enableTitle) : t('msTable.enable') }}
</template>
<template v-else>
<MsIcon type="icon-icon_disable" class="mr-[2px] text-[var(--color-text-4)]" />
<span class="text-[var(--color-text-1)]">
<div v-if="record.enable" class="flex flex-row flex-nowrap items-center gap-[2px]">
<icon-check-circle-fill class="text-[rgb(var(--success-6))]" />
<div>{{ item.enableTitle ? t(item.enableTitle) : t('msTable.enable') }}</div>
</div>
<div v-else class="flex flex-row flex-nowrap items-center gap-[2px]">
<MsIcon type="icon-icon_disable" class="text-[var(--color-text-4)]" />
<div class="text-[var(--color-text-1)]">
{{ item.disableTitle ? t(item.disableTitle) : t('msTable.disable') }}
</span>
</template>
</div>
</div>
</slot>
</template>
<template v-else-if="item.isTag">
@ -146,10 +153,10 @@
:select-row-count="selectCurrent"
:action-config="props.actionConfig"
@batch-action="(item: BatchActionParams) => emit('batchAction', item)"
@clear="selectionChange([], true)"
@clear="emit('clearSelector')"
/>
<ms-pagination
v-else-if="attrs.showPagination"
v-if="attrs.showPagination"
size="small"
v-bind="attrs.msPagination"
hide-on-single-page
@ -171,17 +178,20 @@
import ColumnSelector from './columnSelector.vue';
import MsIcon from '@/components/pure/ms-icon-font/index.vue';
import type { MsTableProps, MsPaginationI, BatchActionParams, BatchActionConfig, MsTableColumn } from './type';
import type { MsTableProps, BatchActionParams, BatchActionConfig, MsTableColumn, MsPaginationI } from './type';
import type { TableData } from '@arco-design/web-vue';
import MsCheckbox from '../ms-checkbox/MsCheckbox.vue';
const batchleft = ref('10px');
const { t } = useI18n();
const tableStore = useTableStore();
const appStore = useAppStore();
const currentColumns = ref<MsTableColumn>([]);
const appStore = useAppStore();
const props = defineProps<{
selectedKeys?: (string | number)[];
selectedKeys: Set<string>;
excludeKeys: Set<string>;
selectorStatus: SelectAllEnum;
actionConfig?: BatchActionConfig;
noDisable?: boolean;
showSetting?: boolean;
@ -189,19 +199,35 @@
spanMethod?: (params: { record: TableData; rowIndex: number; columnIndex: number }) => void;
}>();
const emit = defineEmits<{
(e: 'selectedChange', value: (string | number)[]): void;
(e: 'batchAction', value: BatchActionParams): void;
(e: 'pageChange', value: number): void;
(e: 'pageSizeChange', value: number): void;
(e: 'rowNameChange', value: TableData): void;
(e: 'rowSelectChange', key: string): void;
(e: 'selectAllChange', value: SelectAllEnum): void;
(e: 'sorterChange', value: { [key: string]: string }): void;
(e: 'clearSelector'): void;
}>();
const isSelectAll = ref(false);
const attrs = useAttrs();
// -
const selectCurrent = ref(0);
// -
const selectTotal = ref(0);
const selectTotal = computed(() => {
const { selectorStatus } = props;
if (selectorStatus === SelectAllEnum.ALL) {
return (attrs.msPagination as MsPaginationI)?.total || appStore.pageSize;
}
return (attrs.msPagination as MsPaginationI).pageSize || appStore.pageSize;
});
// -
const selectCurrent = computed(() => {
const { selectorStatus, excludeKeys, selectedKeys } = props;
if (selectorStatus === SelectAllEnum.ALL) {
return selectTotal.value - excludeKeys.size;
}
return selectedKeys.size;
});
// Active
const editActiveKey = ref<string>('');
// inputRef
@ -233,15 +259,6 @@
return undefined;
});
const setSelectAllTotal = (isAll: boolean) => {
const { data, msPagination }: Partial<MsTableProps<any>> = attrs;
if (isAll) {
selectTotal.value = msPagination?.total || data?.length || appStore.pageSize;
} else {
selectTotal.value = data ? data.length : appStore.pageSize;
}
};
const initColumn = () => {
let tmpArr: MsTableColumn = [];
if (props.showSetting) {
@ -252,41 +269,13 @@
currentColumns.value = tmpArr;
};
//
const selectionChange = (arr: (string | number)[], setCurrentSelect: boolean, isAll = false) => {
setSelectAllTotal(isAll);
emit('selectedChange', arr);
if (setCurrentSelect) {
selectCurrent.value = arr.length;
}
};
// change
const handleChange = (v: string) => {
const { data, msPagination }: Partial<MsTableProps<any>> = attrs;
isSelectAll.value = v === SelectAllEnum.ALL;
if (v === SelectAllEnum.NONE) {
selectionChange([], true);
}
if (v === SelectAllEnum.CURRENT) {
if (data && data.length > 0) {
selectionChange(
data.map((item: any) => item[rowKey || 'id']),
true
);
}
}
if (v === SelectAllEnum.ALL) {
const { total } = msPagination as MsPaginationI;
if (data && data.length > 0) {
selectionChange(
data.map((item: any) => item[rowKey || 'id']),
false,
true
);
selectCurrent.value = total;
}
}
const handleSelectAllChange = (v: SelectAllEnum) => {
emit('selectAllChange', v);
};
// change
const rowSelectChange = (key: string) => {
emit('rowSelectChange', key);
};
// change
@ -376,15 +365,6 @@
<style lang="less" scoped>
.ms-base-table {
position: relative;
.custom-action {
position: absolute;
top: 13px;
left: v-bind(batchleft);
z-index: 99;
border-radius: 2px;
line-height: 40px;
cursor: pointer;
}
.batch-action {
justify-content: flex-start;
}
@ -425,11 +405,6 @@
background: none;
}
}
:deep(.ms-table-select-all) {
.arco-checkbox {
margin-left: 0.5rem;
}
}
:deep(.arco-checkbox-icon) {
width: 16px;
height: 16px;

View File

@ -20,18 +20,12 @@
const { t } = useI18n();
const emit = defineEmits<{
(e: 'change', value: string): void;
(e: 'change', value: SelectAllEnum): void;
}>();
const props = defineProps({
current: {
type: Number,
default: 0,
},
total: {
type: Number,
default: 0,
},
const props = withDefaults(defineProps<{ current: number; total: number; type: 'checkbox' | 'radio' }>(), {
current: 0,
total: 0,
});
const checked = ref(false);
@ -44,14 +38,14 @@
} else if (props.current < props.total) {
checked.value = false;
indeterminate.value = true;
} else if (props.current >= props.total) {
} else if (props.current === props.total) {
checked.value = true;
indeterminate.value = false;
}
});
const handleSelect = (v: string | number | Record<string, any> | undefined) => {
emit('change', v as string);
emit('change', v as SelectAllEnum);
};
const handleCheckChange = () => {

View File

@ -1,4 +1,4 @@
import { ColumnEditTypeEnum } from '@/enums/tableEnum';
import { ColumnEditTypeEnum, SelectAllEnum } from '@/enums/tableEnum';
import { TableColumnData, TableData, TableDraggable, TableChangeExtra } from '@arco-design/web-vue';
export interface MsPaginationI {
@ -42,56 +42,37 @@ export type MsTableDataItem<T> = T & {
export interface MsTableProps<T> {
// 表格数据 - 详见 TableData https://arco.design/web-vue/components/table-data;
data: MsTableDataItem<T>[];
// 表格key, 用于存储表格列配置
tableKey: string;
tableKey: string; // 表格key, 用于存储表格列配置,pageSize等
rowKey: string; // 表格行的key rowId
// 表格列 - 详见 TableColumn https://arco.design/web-vue/components/table-column;
columns: MsTableColumnData[];
// 表格尺寸
size?: 'mini' | 'small' | 'default' | 'large';
// 表格是否可滚动
size?: 'mini' | 'small' | 'default' | 'large'; // 表格尺寸
scroll?: {
x?: number | string;
y?: number | string;
maxHeight?: number | string;
minWidth?: number | string;
};
// 表格是否可拖拽
enableDrag?: boolean;
}; // 表格是否可滚动
heightUsed?: number; // 已经使用的高度
enableDrag?: boolean; // 表格是否可拖拽
draggable?: TableDraggable;
// 表格是否可编辑
editable?: boolean;
// 表格是否可筛选
filterable?: boolean;
// 表格是否可排序
sortable?: boolean;
// 表格是否可选中
selectable?: boolean;
// 展示自定义全选
showSelectAll?: boolean;
// 表格是否可固定表头
fixedHeader?: boolean;
// 表格是否可固定列
fixedColumns?: boolean;
// rowKey
rowKey?: string;
// loading
loading?: boolean;
bordered?: boolean;
/** 选择器相关 */
selectable?: boolean; // 是否显示选择器
selectorType: 'none' | 'checkbox' | 'radio'; // 选择器类型
selectedKeys: Set<string>; // 选中的key
excludeKeys: Set<string>; // 排除的key
selectorStatus: SelectAllEnum; // 选择器状态
/** end */
loading?: boolean; // 加载效果
bordered?: boolean; // 是否显示边框
msPagination?: MsPaginationI;
// 展示列表选择按钮
showSetting?: boolean;
// 分页是否是简单模式
pageSimple?: boolean;
// 是否展示禁用的行
noDisable?: boolean;
// 表格的错误状态默认为false
tableErrorStatus?: MsTableErrorStatus;
// debug模式开启后会打印表格所有state
debug?: boolean;
// 是否展示第一行的操作
showFirstOperation?: boolean;
// 已经使用的高度
heightUsed?: number;
showSetting?: boolean; // 展示列表选择按钮
pageSimple?: boolean; // 分页是否是简单模式
noDisable?: boolean; // 是否展示禁用的行
tableErrorStatus?: MsTableErrorStatus; // 表格的错误状态默认为false
debug?: boolean; // debug模式开启后会打印表格所有state
showFirstOperation?: boolean; // 是否展示第一行的操作
[key: string]: any;
}
@ -124,3 +105,8 @@ export interface BatchActionConfig {
export interface renamePopconfirmVisibleType {
[key: string]: boolean;
}
export interface SetPaginationPrams {
current: number;
total?: number;
}

View File

@ -6,7 +6,8 @@ import dayjs from 'dayjs';
import { useAppStore, useTableStore } from '@/store';
import type { TableData } from '@arco-design/web-vue';
import type { TableQueryParams, CommonList } from '@/models/common';
import type { MsTableProps, MsTableDataItem, MsTableColumn, MsTableErrorStatus } from './type';
import { SelectAllEnum } from '@/enums/tableEnum';
import type { MsTableProps, MsTableDataItem, MsTableColumn, MsTableErrorStatus, SetPaginationPrams } from './type';
export interface Pagination {
current: number;
@ -24,39 +25,38 @@ export default function useTableProps<T>(
// 编辑操作的保存回调函数
saveCallBack?: (item: T) => Promise<any>
) {
// 行选择
const rowSelection = {
type: 'checkbox',
showCheckedAll: false,
};
const defaultProps: MsTableProps<T> = {
tableKey: '',
bordered: true,
showPagination: true,
size: 'default',
heightUsed: 294,
scroll: { x: 1400, y: appStore.innerHeight - 294 },
checkable: true,
loading: false,
data: [],
tableKey: '', // 缓存pageSize 或 column 的 key
bordered: true, // 是否显示边框
showPagination: true, // 是否显示分页
size: 'default', // 表格大小
heightUsed: 294, // 表格所在的页面已经使用的高度
scroll: { x: 1400, y: appStore.innerHeight - 294 }, // 表格滚动配置
loading: false, // 加载效果
data: [], // 表格数据
/**
*
* showSetting为true时,TableStore.initColumnsk(tableKey: string, column: MsTableColumn)
* showSetting为false时
*/
columns: [] as MsTableColumn,
rowKey: 'id',
selectedKeys: [],
selectedAll: false,
enableDrag: false,
showSelectAll: true,
showSetting: false,
rowKey: 'id', // 表格行的key
/** 选择器相关 */
rowSelection: null, // 禁用表格默认的选择器
selectable: false, // 是否显示选择器
selectorType: 'checkbox', // 选择器类型
selectedKeys: new Set<string>(), // 选中的key
excludeKeys: new Set<string>(), // 排除的key
selectorStatus: SelectAllEnum.NONE, // 选择器状态
/** end */
enableDrag: false, // 是否可拖拽
showSetting: false, // 是否展示列选择器
columnResizable: true,
// 禁用 arco-table 的分页
pagination: false,
// 简易分页模式
pageSimple: false,
// 表格的错误状态
tableErrorStatus: false,
debug: false,
// 展示第一行的操作
showFirstOperation: false,
pagination: false, // 禁用 arco-table 的分页
pageSimple: false, // 简易分页模式
tableErrorStatus: false, // 表格的错误状态
debug: false, // debug 模式
showFirstOperation: false, // 展示第一行的操作
...props,
};
@ -96,11 +96,6 @@ export default function useTableProps<T>(
};
}
// 是否可选中
if (propsRes.value.selectable) {
propsRes.value.rowSelection = rowSelection;
}
// 是否可拖拽
if (propsRes.value.enableDrag) {
propsRes.value.draggable = { type: 'handle' };
@ -126,13 +121,7 @@ export default function useTableProps<T>(
*
* @param current //当前页数
* @param total //总页数默认是0条可选
* @param fetchData ,
*/
interface SetPaginationPrams {
current: number;
total?: number;
}
const setPagination = ({ current, total }: SetPaginationPrams) => {
if (propsRes.value.msPagination && typeof propsRes.value.msPagination === 'object') {
propsRes.value.msPagination.current = current;
@ -161,9 +150,19 @@ export default function useTableProps<T>(
keyword.value = v;
};
// 给表格设置选中项 - add rowKey to selectedKeys
const setTableSelected = (key: string) => {
const { selectedKeys } = propsRes.value;
if (!selectedKeys.has(key)) {
selectedKeys.add(key);
}
propsRes.value.selectedKeys = selectedKeys;
};
// 加载分页列表数据
const loadList = async () => {
const { current, pageSize } = propsRes.value.msPagination as Pagination;
const { rowKey, selectorStatus, excludeKeys } = propsRes.value;
setLoading(true);
try {
const data = await loadListFunc({
@ -185,6 +184,12 @@ export default function useTableProps<T>(
if (dataTransform) {
item = dataTransform(item);
}
if (selectorStatus === SelectAllEnum.ALL) {
if (!excludeKeys.has(item[rowKey])) {
setTableSelected(item[rowKey]);
}
}
return item;
});
if (data.total === 0) {
@ -201,7 +206,7 @@ export default function useTableProps<T>(
// debug 模式下打印属性
if (propsRes.value.debug && import.meta.env.DEV) {
// eslint-disable-next-line no-console
console.log('Table propsRes: ', propsRes.value);
// console.log('Table propsRes: ', propsRes.value);
}
}
};
@ -214,6 +219,13 @@ export default function useTableProps<T>(
}
};
// 重置选择器
const resetSelector = () => {
propsRes.value.selectedKeys.clear();
propsRes.value.excludeKeys.clear();
propsRes.value.selectorStatus = SelectAllEnum.NONE;
};
// 事件触发组
const propsEvent = ref({
// 排序触发
@ -243,17 +255,6 @@ export default function useTableProps<T>(
}
loadList();
},
// 选择触发
selectedChange: (arr: (string | number)[]) => {
if (arr.length === 0) {
propsRes.value.showPagination = true;
propsRes.value.msPagination = oldPagination.value;
} else {
oldPagination.value = propsRes.value.msPagination as Pagination;
propsRes.value.showPagination = false;
}
propsRes.value.selectedKeys = arr;
},
change: (_data: MsTableDataItem<T>[]) => {
if (propsRes.value.draggable && _data instanceof Array) {
(propsRes.value.data as MsTableDataItem<T>[]) = _data;
@ -269,6 +270,45 @@ export default function useTableProps<T>(
resetSort: () => {
sortItem.value = {};
},
// 重置筛选
clearSelector: () => {
resetSelector();
},
// 表格SelectAll change
selectAllChange: (v: SelectAllEnum) => {
propsRes.value.selectorStatus = v;
const { data, rowKey, selectedKeys } = propsRes.value;
if (v === SelectAllEnum.NONE) {
// 清空选中项
resetSelector();
} else {
data.forEach((item) => {
if (item[rowKey] && !selectedKeys.has(item[rowKey])) {
selectedKeys.add(item[rowKey]);
}
});
propsRes.value.selectedKeys = selectedKeys;
}
},
// 表格行的选中/取消事件
rowSelectChange: (key: string) => {
const { selectedKeys, excludeKeys } = propsRes.value;
if (selectedKeys.has(key)) {
// 当前已选中,取消选中
selectedKeys.delete(key);
excludeKeys.add(key);
} else {
// 当前未选中,选中
selectedKeys.add(key);
if (excludeKeys.has(key)) {
excludeKeys.delete(key);
}
}
propsRes.value.selectedKeys = selectedKeys;
propsRes.value.excludeKeys = excludeKeys;
},
});
watchEffect(() => {