【QQYUN-5571】自封装选择列,解决BasicTable数据多,行选择卡顿问题
parent
72a0e7dbe3
commit
e9c2854b43
|
@ -113,18 +113,18 @@
|
|||
{
|
||||
title: '职务编码',
|
||||
dataIndex: 'code',
|
||||
width: 40,
|
||||
width: 180,
|
||||
align: 'left',
|
||||
},
|
||||
{
|
||||
title: '职务名称',
|
||||
dataIndex: 'name',
|
||||
width: 40,
|
||||
// width: 180,
|
||||
},
|
||||
{
|
||||
title: '职务等级',
|
||||
dataIndex: 'postRank_dictText',
|
||||
width: 40,
|
||||
width: 180,
|
||||
},
|
||||
];
|
||||
//已选择的table信息
|
||||
|
|
|
@ -80,13 +80,13 @@
|
|||
{
|
||||
title: '角色名称',
|
||||
dataIndex: 'roleName',
|
||||
width: 40,
|
||||
width: 240,
|
||||
align: 'left',
|
||||
},
|
||||
{
|
||||
title: '角色编码',
|
||||
dataIndex: 'roleCode',
|
||||
width: 40,
|
||||
// width: 40,
|
||||
},
|
||||
];
|
||||
|
||||
|
|
|
@ -76,22 +76,22 @@
|
|||
{
|
||||
title: '用户账号',
|
||||
dataIndex: 'username',
|
||||
width: 50,
|
||||
width: 180,
|
||||
},
|
||||
{
|
||||
title: '用户姓名',
|
||||
dataIndex: 'realname',
|
||||
width: 50,
|
||||
width: 180,
|
||||
},
|
||||
{
|
||||
title: '性别',
|
||||
dataIndex: 'sex_dictText',
|
||||
width: 50,
|
||||
width: 80,
|
||||
},
|
||||
{
|
||||
title: '手机号码',
|
||||
dataIndex: 'phone',
|
||||
width: 50,
|
||||
// width: 50,
|
||||
},
|
||||
],
|
||||
useSearchForm: true,
|
||||
|
|
|
@ -145,33 +145,33 @@
|
|||
{
|
||||
title: '用户账号',
|
||||
dataIndex: 'username',
|
||||
width: 40,
|
||||
width: 120,
|
||||
align: 'left',
|
||||
},
|
||||
{
|
||||
title: '用户姓名',
|
||||
dataIndex: 'realname',
|
||||
width: 40,
|
||||
width: 120,
|
||||
},
|
||||
{
|
||||
title: '性别',
|
||||
dataIndex: 'sex_dictText',
|
||||
width: 20,
|
||||
width: 50,
|
||||
},
|
||||
{
|
||||
title: '手机号码',
|
||||
dataIndex: 'phone',
|
||||
width: 30,
|
||||
width: 120,
|
||||
},
|
||||
{
|
||||
title: '邮箱',
|
||||
dataIndex: 'email',
|
||||
width: 40,
|
||||
// width: 40,
|
||||
},
|
||||
{
|
||||
title: '状态',
|
||||
dataIndex: 'status_dictText',
|
||||
width: 20,
|
||||
width: 80,
|
||||
},
|
||||
];
|
||||
//已选择的table信息
|
||||
|
|
|
@ -79,7 +79,7 @@ export function useSelectBiz(getList, props) {
|
|||
*/
|
||||
const indexColumnProps = {
|
||||
dataIndex: 'index',
|
||||
width: 20,
|
||||
width: 50,
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -22,7 +22,10 @@
|
|||
<slot :name="item" v-bind="data || {}"></slot>
|
||||
</template>
|
||||
<template #headerCell="{ column }">
|
||||
<HeaderCell :column="column" />
|
||||
<!-- update-begin--author:sunjianlei---date:220230630---for:【QQYUN-5571】自封装选择列,解决数据行选择卡顿问题 -->
|
||||
<CustomSelectHeader v-if="isCustomSelection(column)" v-bind="selectHeaderProps"/>
|
||||
<HeaderCell v-else :column="column" />
|
||||
<!-- update-end--author:sunjianlei---date:220230630---for:【QQYUN-5571】自封装选择列,解决数据行选择卡顿问题 -->
|
||||
</template>
|
||||
<!-- 增加对antdv3.x兼容 -->
|
||||
<template #bodyCell="data">
|
||||
|
@ -39,6 +42,7 @@
|
|||
import { Table } from 'ant-design-vue';
|
||||
import { BasicForm, useForm } from '/@/components/Form/index';
|
||||
import { PageWrapperFixedHeightKey } from '/@/components/Page/injectionKey';
|
||||
import CustomSelectHeader from './components/CustomSelectHeader.vue'
|
||||
import expandIcon from './components/ExpandIcon';
|
||||
import HeaderCell from './components/HeaderCell.vue';
|
||||
import { InnerHandlers } from './types/table';
|
||||
|
@ -56,6 +60,7 @@
|
|||
import { useTableFooter } from './hooks/useTableFooter';
|
||||
import { useTableForm } from './hooks/useTableForm';
|
||||
import { useDesign } from '/@/hooks/web/useDesign';
|
||||
import { useCustomSelection } from "./hooks/useCustomSelection";
|
||||
|
||||
import { omit } from 'lodash-es';
|
||||
import { basicProps } from './props';
|
||||
|
@ -67,6 +72,7 @@
|
|||
Table,
|
||||
BasicForm,
|
||||
HeaderCell,
|
||||
CustomSelectHeader,
|
||||
},
|
||||
props: basicProps,
|
||||
emits: [
|
||||
|
@ -112,8 +118,34 @@
|
|||
const { getLoading, setLoading } = useLoading(getProps);
|
||||
const { getPaginationInfo, getPagination, setPagination, setShowPagination, getShowPagination } = usePagination(getProps);
|
||||
|
||||
const { getRowSelection, getRowSelectionRef, getSelectRows, clearSelectedRowKeys, getSelectRowKeys, deleteSelectRowByKey, setSelectedRowKeys } =
|
||||
useRowSelection(getProps, tableData, emit);
|
||||
// update-begin--author:sunjianlei---date:220230630---for:【QQYUN-5571】自封装选择列,解决数据行选择卡顿问题
|
||||
|
||||
// const { getRowSelection, getRowSelectionRef, getSelectRows, clearSelectedRowKeys, getSelectRowKeys, deleteSelectRowByKey, setSelectedRowKeys } =
|
||||
// useRowSelection(getProps, tableData, emit);
|
||||
|
||||
// 子级列名
|
||||
const childrenColumnName = computed(() => getProps.value.childrenColumnName || 'children');
|
||||
|
||||
// 自定义选择列
|
||||
const {
|
||||
getRowSelection,
|
||||
getSelectRows,
|
||||
getSelectRowKeys,
|
||||
setSelectedRowKeys,
|
||||
getRowSelectionRef,
|
||||
selectHeaderProps,
|
||||
isCustomSelection,
|
||||
handleCustomSelectColumn,
|
||||
clearSelectedRowKeys,
|
||||
deleteSelectRowByKey,
|
||||
} = useCustomSelection(
|
||||
getProps,
|
||||
wrapRef,
|
||||
getPaginationInfo,
|
||||
tableData,
|
||||
childrenColumnName
|
||||
)
|
||||
// update-end--author:sunjianlei---date:220230630---for:【QQYUN-5571】自封装选择列,解决数据行选择卡顿问题
|
||||
|
||||
const {
|
||||
handleTableChange: onTableChange,
|
||||
|
@ -153,7 +185,10 @@
|
|||
|
||||
const { getViewColumns, getColumns, setCacheColumnsByField, setColumns, getColumnsRef, getCacheColumns } = useColumns(
|
||||
getProps,
|
||||
getPaginationInfo
|
||||
getPaginationInfo,
|
||||
// update-begin--author:sunjianlei---date:220230630---for:【QQYUN-5571】自封装选择列,解决数据行选择卡顿问题
|
||||
handleCustomSelectColumn,
|
||||
// update-end--author:sunjianlei---date:220230630---for:【QQYUN-5571】自封装选择列,解决数据行选择卡顿问题
|
||||
);
|
||||
|
||||
const { getScrollRef, redoHeight } = useTableScroll(getProps, tableElRef, getColumnsRef, getRowSelectionRef, getDataSourceRef);
|
||||
|
@ -213,10 +248,21 @@
|
|||
}*/
|
||||
//update-end---author:wangshuai ---date:20230214 for:[QQYUN-4237]代码生成 内嵌子表模式 没有滚动条------------
|
||||
|
||||
// update-begin--author:sunjianlei---date:220230630---for:【QQYUN-5571】自封装选择列,解决数据行选择卡顿问题
|
||||
// 自定义选择列,需要去掉原生的
|
||||
delete propsData.rowSelection
|
||||
// update-end--author:sunjianlei---date:220230630---for:【QQYUN-5571】自封装选择列,解决数据行选择卡顿问题
|
||||
|
||||
propsData = omit(propsData, ['class', 'onChange']);
|
||||
return propsData;
|
||||
});
|
||||
|
||||
// 统一设置表格列宽度
|
||||
const getMaxColumnWidth = computed(() => {
|
||||
const values = unref(getBindValues);
|
||||
return values.maxColumnWidth > 0 ? values.maxColumnWidth + 'px' : null;
|
||||
});
|
||||
|
||||
const getWrapperClass = computed(() => {
|
||||
const values = unref(getBindValues);
|
||||
return [
|
||||
|
@ -225,6 +271,7 @@
|
|||
{
|
||||
[`${prefixCls}-form-container`]: values.useSearchForm,
|
||||
[`${prefixCls}--inset`]: values.inset,
|
||||
[`${prefixCls}-col-max-width`]: getMaxColumnWidth.value != null,
|
||||
},
|
||||
];
|
||||
});
|
||||
|
@ -300,7 +347,14 @@
|
|||
replaceFormSlotKey,
|
||||
getFormSlotKeys,
|
||||
getWrapperClass,
|
||||
getMaxColumnWidth,
|
||||
columns: getViewColumns,
|
||||
|
||||
// update-begin--author:sunjianlei---date:220230630---for:【QQYUN-5571】自封装选择列,解决数据行选择卡顿问题
|
||||
selectHeaderProps,
|
||||
isCustomSelection,
|
||||
// update-end--author:sunjianlei---date:220230630---for:【QQYUN-5571】自封装选择列,解决数据行选择卡顿问题
|
||||
|
||||
};
|
||||
},
|
||||
});
|
||||
|
@ -429,5 +483,15 @@
|
|||
padding: 0;
|
||||
}
|
||||
}
|
||||
|
||||
// ------ 统一设置表格列最大宽度 ------
|
||||
&-col-max-width {
|
||||
.ant-table-thead tr th,
|
||||
.ant-table-tbody tr td {
|
||||
max-width: v-bind(getMaxColumnWidth);
|
||||
}
|
||||
}
|
||||
// ------ 统一设置表格列最大宽度 ------
|
||||
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -0,0 +1,36 @@
|
|||
<!-- 自定义选择列,表头实现部分 -->
|
||||
<template>
|
||||
<a-checkbox :checked="checked" :indeterminate="isHalf" @update:checked="onChange" />
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue';
|
||||
|
||||
const props = defineProps({
|
||||
selectedLength: {
|
||||
type: Number,
|
||||
required: true,
|
||||
},
|
||||
// 当前页条目数
|
||||
pageSize: {
|
||||
type: Number,
|
||||
required: true,
|
||||
},
|
||||
});
|
||||
const emit = defineEmits(['select-all']);
|
||||
|
||||
// 是否全选
|
||||
const checked = computed(() => {
|
||||
return props.selectedLength > 0 && props.selectedLength >= props.pageSize;
|
||||
});
|
||||
|
||||
// 是否半选
|
||||
const isHalf = computed(() => {
|
||||
return props.selectedLength > 0 && props.selectedLength < props.pageSize;
|
||||
});
|
||||
|
||||
function onChange(checked: boolean) {
|
||||
emit('select-all', checked);
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss"></style>
|
|
@ -9,6 +9,7 @@ import { isArray, isBoolean, isFunction, isMap, isString } from '/@/utils/is';
|
|||
import { cloneDeep, isEqual } from 'lodash-es';
|
||||
import { formatToDate } from '/@/utils/dateUtil';
|
||||
import { ACTION_COLUMN_FLAG, DEFAULT_ALIGN, INDEX_COLUMN_FLAG, PAGE_SIZE } from '../const';
|
||||
import { CUS_SEL_COLUMN_KEY } from "./useCustomSelection";
|
||||
|
||||
function handleItem(item: BasicColumn, ellipsis: boolean) {
|
||||
const { key, dataIndex, children } = item;
|
||||
|
@ -95,7 +96,11 @@ function handleActionColumn(propsRef: ComputedRef<BasicTableProps>, columns: Bas
|
|||
}
|
||||
}
|
||||
|
||||
export function useColumns(propsRef: ComputedRef<BasicTableProps>, getPaginationRef: ComputedRef<boolean | PaginationProps>) {
|
||||
export function useColumns(
|
||||
propsRef: ComputedRef<BasicTableProps>,
|
||||
getPaginationRef: ComputedRef<boolean | PaginationProps>,
|
||||
handleCustomSelectColumn: Fn,
|
||||
) {
|
||||
const columnsRef = ref(unref(propsRef).columns) as unknown as Ref<BasicColumn[]>;
|
||||
let cacheColumns = unref(propsRef).columns;
|
||||
|
||||
|
@ -104,6 +109,10 @@ export function useColumns(propsRef: ComputedRef<BasicTableProps>, getPagination
|
|||
|
||||
handleIndexColumn(propsRef, getPaginationRef, columns);
|
||||
handleActionColumn(propsRef, columns);
|
||||
// update-begin--author:sunjianlei---date:220230630---for:【QQYUN-5571】自封装选择列,解决数据行选择卡顿问题
|
||||
handleCustomSelectColumn(columns);
|
||||
// update-end--author:sunjianlei---date:220230630---for:【QQYUN-5571】自封装选择列,解决数据行选择卡顿问题
|
||||
|
||||
if (!columns) {
|
||||
return [];
|
||||
}
|
||||
|
@ -238,6 +247,10 @@ export function useColumns(propsRef: ComputedRef<BasicTableProps>, getPagination
|
|||
if (ignoreAction) {
|
||||
columns = columns.filter((item) => item.flag !== ACTION_COLUMN_FLAG);
|
||||
}
|
||||
// update-begin--author:sunjianlei---date:220230630---for:【QQYUN-5571】自封装选择列,解决数据行选择卡顿问题
|
||||
// 过滤自定义选择列
|
||||
columns = columns.filter((item) => item.key !== CUS_SEL_COLUMN_KEY);
|
||||
// update-enb--author:sunjianlei---date:220230630---for:【QQYUN-5571】自封装选择列,解决数据行选择卡顿问题
|
||||
|
||||
if (sort) {
|
||||
columns = sortFixedColumn(columns);
|
||||
|
|
|
@ -0,0 +1,396 @@
|
|||
import type { BasicColumn } from '/@/components/Table';
|
||||
import type { Ref, ComputedRef } from 'vue';
|
||||
import type { BasicTableProps, PaginationProps, TableRowSelection } from '/@/components/Table';
|
||||
import { computed, onUnmounted, ref, toRaw, unref, watchEffect } from 'vue';
|
||||
import { omit } from 'lodash-es';
|
||||
import { throttle } from 'lodash-es';
|
||||
import { Checkbox } from 'ant-design-vue';
|
||||
import { isFunction } from '/@/utils/is';
|
||||
import { findNodeAll } from '/@/utils/helper/treeHelper';
|
||||
import { ROW_KEY } from '/@/components/Table/src/const';
|
||||
import { onMountedOrActivated } from '/@/hooks/core/onMountedOrActivated';
|
||||
import { useMessage } from '/@/hooks/web/useMessage';
|
||||
import { ModalFunc } from 'ant-design-vue/lib/modal/Modal';
|
||||
|
||||
// 自定义选择列的key
|
||||
export const CUS_SEL_COLUMN_KEY = 'j-custom-selected-column';
|
||||
|
||||
/**
|
||||
* 自定义选择列
|
||||
*/
|
||||
export function useCustomSelection(
|
||||
propsRef: ComputedRef<BasicTableProps>,
|
||||
wrapRef: Ref<null | HTMLDivElement>,
|
||||
getPaginationRef: ComputedRef<boolean | PaginationProps>,
|
||||
tableData: Ref<Recordable[]>,
|
||||
childrenColumnName: ComputedRef<string>
|
||||
) {
|
||||
const { createConfirm } = useMessage();
|
||||
// 表格body元素
|
||||
const bodyEl = ref<HTMLDivElement>();
|
||||
// body元素高度
|
||||
const bodyHeight = ref<number>(0);
|
||||
// 表格tr高度
|
||||
const rowHeight = ref<number>(0);
|
||||
// body 滚动高度
|
||||
const scrollTop = ref(0);
|
||||
// 选择的key
|
||||
const selectedKeys = ref<string[]>([]);
|
||||
// 选择的行
|
||||
const selectedRows = ref<Recordable[]>([]);
|
||||
|
||||
// 扁平化数据,children数据也会放到一起
|
||||
const flattedData = computed(() => {
|
||||
return flattenData(tableData.value, childrenColumnName.value);
|
||||
});
|
||||
|
||||
const getRowSelectionRef = computed((): TableRowSelection | null => {
|
||||
const { rowSelection } = unref(propsRef);
|
||||
if (!rowSelection) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return {
|
||||
preserveSelectedRowKeys: true,
|
||||
// selectedRowKeys: unref(selectedKeys),
|
||||
// onChange: (selectedRowKeys: string[]) => {
|
||||
// setSelectedRowKeys(selectedRowKeys);
|
||||
// },
|
||||
...omit(rowSelection, ['onChange', 'selectedRowKeys']),
|
||||
};
|
||||
});
|
||||
|
||||
const getAutoCreateKey = computed(() => {
|
||||
return unref(propsRef).autoCreateKey && !unref(propsRef).rowKey;
|
||||
});
|
||||
|
||||
// 列key字段
|
||||
const getRowKey = computed(() => {
|
||||
const { rowKey } = unref(propsRef);
|
||||
return unref(getAutoCreateKey) ? ROW_KEY : rowKey;
|
||||
});
|
||||
// 获取行的key字段数据
|
||||
const getRecordKey = (record) => {
|
||||
if (!getRowKey.value) {
|
||||
return record[ROW_KEY];
|
||||
} else if (isFunction(getRowKey.value)) {
|
||||
return getRowKey.value(record);
|
||||
} else {
|
||||
return record[getRowKey.value];
|
||||
}
|
||||
};
|
||||
|
||||
// 分页配置
|
||||
const getPagination = computed<PaginationProps>(() => {
|
||||
return typeof getPaginationRef.value === 'boolean' ? {} : getPaginationRef.value;
|
||||
});
|
||||
// 当前页条目数量
|
||||
const currentPageSize = computed(() => {
|
||||
const { pageSize = 10, total = flattedData.value.length } = getPagination.value;
|
||||
return pageSize > total ? total : pageSize;
|
||||
});
|
||||
|
||||
// 选择列表头props
|
||||
const selectHeaderProps = computed(() => {
|
||||
return {
|
||||
onSelectAll,
|
||||
selectedLength: flattedData.value.filter((data) => selectedKeys.value.includes(getRecordKey(data))).length,
|
||||
pageSize: currentPageSize.value,
|
||||
};
|
||||
});
|
||||
|
||||
// 监听滚动条事件
|
||||
const onScrollTopChange = throttle((e) => (scrollTop.value = e?.target?.scrollTop), 150);
|
||||
|
||||
// 获取首行行高
|
||||
watchEffect(() => {
|
||||
if (bodyEl.value) {
|
||||
bodyHeight.value = bodyEl.value.offsetHeight;
|
||||
const el = bodyEl.value?.querySelector('tbody.ant-table-tbody tr.ant-table-row') as HTMLDivElement;
|
||||
if (el) {
|
||||
rowHeight.value = el.offsetHeight;
|
||||
return;
|
||||
}
|
||||
}
|
||||
rowHeight.value = 50;
|
||||
// 这种写法是为了监听到 size 的变化
|
||||
propsRef.value.size && void 0;
|
||||
});
|
||||
|
||||
onMountedOrActivated(async () => {
|
||||
bodyEl.value = await getTableBody(wrapRef.value!);
|
||||
bodyEl.value.addEventListener('scroll', onScrollTopChange);
|
||||
});
|
||||
onUnmounted(() => {
|
||||
if (bodyEl.value) {
|
||||
bodyEl.value?.removeEventListener('scroll', onScrollTopChange);
|
||||
}
|
||||
});
|
||||
|
||||
// 选择全部
|
||||
function onSelectAll(checked: boolean) {
|
||||
// 取消全选
|
||||
if (!checked) {
|
||||
selectedKeys.value = [];
|
||||
selectedRows.value = [];
|
||||
emitChange();
|
||||
return;
|
||||
}
|
||||
let modal: Nullable<ReturnType<ModalFunc>> = null;
|
||||
// 全选
|
||||
const checkAll = () => {
|
||||
if (modal != null) {
|
||||
modal.update({
|
||||
content: '正在分批全选,请稍后……',
|
||||
cancelButtonProps: { disabled: true },
|
||||
});
|
||||
}
|
||||
let showCount = 0;
|
||||
// 最小选中数量
|
||||
let minSelect = 100;
|
||||
const hidden: Recordable[] = [];
|
||||
flattedData.value.forEach((item, index, array) => {
|
||||
if (array.length > 120) {
|
||||
if (showCount <= minSelect && recordIsShow(index, Math.max((minSelect - 10) / 2, 3))) {
|
||||
showCount++;
|
||||
updateSelected(item, checked);
|
||||
} else {
|
||||
hidden.push(item);
|
||||
}
|
||||
} else {
|
||||
updateSelected(item, checked);
|
||||
}
|
||||
});
|
||||
if (hidden.length > 0) {
|
||||
return batchesSelectAll(hidden, checked, minSelect);
|
||||
} else {
|
||||
emitChange();
|
||||
}
|
||||
};
|
||||
|
||||
// 当数据量大于120条时,全选会导致页面卡顿,需进行慢速全选
|
||||
if (flattedData.value.length > 120) {
|
||||
modal = createConfirm({
|
||||
title: '全选',
|
||||
content: '当前数据量较大,全选可能会导致页面卡顿,确定要执行此操作吗?',
|
||||
iconType: 'warning',
|
||||
onOk: () => checkAll(),
|
||||
});
|
||||
} else {
|
||||
checkAll();
|
||||
}
|
||||
}
|
||||
|
||||
// 分批全选
|
||||
function batchesSelectAll(hidden: Recordable[], checked: boolean, minSelect: number) {
|
||||
return new Promise<void>((resolve) => {
|
||||
(function call() {
|
||||
// 每隔半秒钟,选择100条数据
|
||||
setTimeout(() => {
|
||||
const list = hidden.splice(0, minSelect);
|
||||
if (list.length > 0) {
|
||||
list.forEach((item) => {
|
||||
updateSelected(item, checked);
|
||||
});
|
||||
call();
|
||||
} else {
|
||||
setTimeout(() => {
|
||||
emitChange();
|
||||
resolve();
|
||||
}, 500);
|
||||
}
|
||||
}, 300);
|
||||
})();
|
||||
});
|
||||
}
|
||||
|
||||
// 选中单个
|
||||
function onSelect(record, checked) {
|
||||
updateSelected(record, checked);
|
||||
emitChange();
|
||||
}
|
||||
|
||||
function updateSelected(record, checked) {
|
||||
const recordKey = getRecordKey(record);
|
||||
const index = selectedKeys.value.findIndex((key) => key === recordKey);
|
||||
if (checked) {
|
||||
if (index === -1) {
|
||||
selectedKeys.value.push(recordKey);
|
||||
selectedRows.value.push(record);
|
||||
}
|
||||
} else {
|
||||
if (index !== -1) {
|
||||
selectedKeys.value.splice(index, 1);
|
||||
selectedRows.value.splice(index, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 调用用户自定义的onChange事件
|
||||
function emitChange() {
|
||||
const { rowSelection } = unref(propsRef);
|
||||
if (rowSelection) {
|
||||
const { onChange } = rowSelection;
|
||||
if (onChange && isFunction(onChange)) {
|
||||
setTimeout(() => {
|
||||
onChange(selectedKeys.value, selectedRows.value);
|
||||
}, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 用于判断是否是自定义选择列
|
||||
function isCustomSelection(column: BasicColumn) {
|
||||
return column.key === CUS_SEL_COLUMN_KEY;
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断当前行是否可视,虚拟滚动用
|
||||
* @param index 行下标
|
||||
* @param threshold 前后阈值,默认可视区域前后显示3条
|
||||
*/
|
||||
function recordIsShow(index: number, threshold = 3) {
|
||||
// 只有数据量大于50条时,才会进行虚拟滚动
|
||||
const isVirtual = flattedData.value.length > 50;
|
||||
if (isVirtual) {
|
||||
// 根据 scrollTop、bodyHeight、rowHeight 计算出当前行是否可视(阈值前后3条)
|
||||
// flag1 = 判断当前行是否在可视区域上方3条
|
||||
const flag1 = scrollTop.value - rowHeight.value * threshold < index * rowHeight.value;
|
||||
// flag2 = 判断当前行是否在可视区域下方3条
|
||||
const flag2 = index * rowHeight.value < scrollTop.value + bodyHeight.value + rowHeight.value * threshold;
|
||||
// 全部条件满足时,才显示当前行
|
||||
return flag1 && flag2;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// 自定义渲染Body
|
||||
function bodyCustomRender({ record, index }) {
|
||||
if (!recordIsShow(index)) {
|
||||
return '';
|
||||
}
|
||||
const recordKey = getRecordKey(record);
|
||||
// 获取用户自定义checkboxProps
|
||||
const checkboxProps = ((getCheckboxProps) => {
|
||||
if (typeof getCheckboxProps === 'function') {
|
||||
try {
|
||||
return getCheckboxProps(record) ?? {};
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
}
|
||||
return {};
|
||||
})(propsRef.value.rowSelection?.getCheckboxProps);
|
||||
return (
|
||||
<Checkbox
|
||||
{...checkboxProps}
|
||||
data-index={index}
|
||||
key={'j-select__' + recordKey}
|
||||
checked={selectedKeys.value.includes(recordKey)}
|
||||
onUpdate:checked={(checked) => onSelect(record, checked)}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
// 创建选择列
|
||||
function handleCustomSelectColumn(columns: BasicColumn[]) {
|
||||
if (!propsRef.value.rowSelection) {
|
||||
return;
|
||||
}
|
||||
const isFixedLeft = columns.some((item) => item.fixed === 'left');
|
||||
|
||||
columns.unshift({
|
||||
title: '选择列',
|
||||
flag: 'CHECKBOX',
|
||||
key: CUS_SEL_COLUMN_KEY,
|
||||
width: 50,
|
||||
minWidth: 50,
|
||||
maxWidth: 50,
|
||||
align: 'center',
|
||||
...(isFixedLeft ? { fixed: 'left' } : {}),
|
||||
customRender: bodyCustomRender,
|
||||
});
|
||||
}
|
||||
|
||||
// 清空所有选择
|
||||
function clearSelectedRowKeys() {
|
||||
onSelectAll(false);
|
||||
}
|
||||
|
||||
// 设置选择的key
|
||||
function setSelectedRowKeys(rowKeys: string[]) {
|
||||
selectedKeys.value = rowKeys;
|
||||
const allSelectedRows = findNodeAll(
|
||||
toRaw(unref(flattedData)).concat(toRaw(unref(selectedRows))),
|
||||
(item) => rowKeys.includes(getRecordKey(item)),
|
||||
{
|
||||
children: propsRef.value.childrenColumnName ?? 'children',
|
||||
}
|
||||
);
|
||||
const trueSelectedRows: any[] = [];
|
||||
rowKeys.forEach((key: string) => {
|
||||
const found = allSelectedRows.find((item) => getRecordKey(item) === key);
|
||||
found && trueSelectedRows.push(found);
|
||||
});
|
||||
selectedRows.value = trueSelectedRows;
|
||||
}
|
||||
|
||||
function getSelectRows<T = Recordable>() {
|
||||
return unref(selectedRows) as T[];
|
||||
}
|
||||
|
||||
function getSelectRowKeys() {
|
||||
return unref(selectedKeys);
|
||||
}
|
||||
|
||||
function getRowSelection() {
|
||||
return unref(getRowSelectionRef)!;
|
||||
}
|
||||
|
||||
function deleteSelectRowByKey(key: string) {
|
||||
const index = selectedKeys.value.findIndex((item) => item === key);
|
||||
if (index !== -1) {
|
||||
selectedKeys.value.splice(index, 1);
|
||||
selectedRows.value.splice(index, 1);
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
getRowSelection,
|
||||
getRowSelectionRef,
|
||||
getSelectRows,
|
||||
getSelectRowKeys,
|
||||
setSelectedRowKeys,
|
||||
deleteSelectRowByKey,
|
||||
selectHeaderProps,
|
||||
isCustomSelection,
|
||||
handleCustomSelectColumn,
|
||||
clearSelectedRowKeys,
|
||||
};
|
||||
}
|
||||
|
||||
function getTableBody(wrap: HTMLDivElement) {
|
||||
return new Promise<HTMLDivElement>((resolve) => {
|
||||
(function fn() {
|
||||
const bodyEl = wrap.querySelector('.ant-table-wrapper .ant-table-body') as HTMLDivElement;
|
||||
if (bodyEl) {
|
||||
resolve(bodyEl);
|
||||
} else {
|
||||
setTimeout(fn, 100);
|
||||
}
|
||||
})();
|
||||
});
|
||||
}
|
||||
|
||||
function flattenData<RecordType>(data: RecordType[] | undefined, childrenColumnName: string): RecordType[] {
|
||||
let list: RecordType[] = [];
|
||||
(data || []).forEach((record) => {
|
||||
list.push(record);
|
||||
|
||||
if (record && typeof record === 'object' && childrenColumnName in record) {
|
||||
list = [...list, ...flattenData<RecordType>((record as any)[childrenColumnName], childrenColumnName)];
|
||||
}
|
||||
});
|
||||
|
||||
return list;
|
||||
}
|
|
@ -19,6 +19,7 @@
|
|||
const [register, { expandAll, collapseAll }] = useTable({
|
||||
title: '树形表格',
|
||||
isTreeTable: true,
|
||||
expandIconColumnIndex: 1,
|
||||
rowSelection: {
|
||||
type: 'checkbox',
|
||||
getCheckboxProps(record: Recordable) {
|
||||
|
|
|
@ -52,6 +52,7 @@
|
|||
size: 'small',
|
||||
pagination: false,
|
||||
isTreeTable: true,
|
||||
expandIconColumnIndex: 1,
|
||||
striped: true,
|
||||
useSearchForm: true,
|
||||
showTableSetting: true,
|
||||
|
|
|
@ -49,6 +49,10 @@
|
|||
formConfig: {
|
||||
schemas: searchFormSchema,
|
||||
},
|
||||
actionColumn: {
|
||||
width: 180,
|
||||
},
|
||||
showIndexColumn: true,
|
||||
},
|
||||
exportConfig: {
|
||||
name: '职务列表',
|
||||
|
|
|
@ -11,7 +11,8 @@ export const columns: BasicColumn[] = [
|
|||
{
|
||||
title: '职务名称',
|
||||
dataIndex: 'name',
|
||||
width: 200,
|
||||
align: 'left'
|
||||
// width: 200,
|
||||
},
|
||||
// {
|
||||
// title: '职务等级',
|
||||
|
|
Loading…
Reference in New Issue