fix: 修改功能用例关联需求包含子需求全选问题&补充表格带有子级全选等逻辑
This commit is contained in:
parent
2ce0076d64
commit
16866e7966
|
@ -1,5 +1,5 @@
|
||||||
<template>
|
<template>
|
||||||
<a-checkbox :model-value="checked" @change="handleChange"></a-checkbox>
|
<a-checkbox :model-value="checked" :indeterminate="props.indeterminate" @change="handleChange"></a-checkbox>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
@ -7,6 +7,7 @@
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
value: boolean;
|
value: boolean;
|
||||||
|
indeterminate: boolean; // 半选用于树形子级别选择状态
|
||||||
}>();
|
}>();
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
(e: 'change', value: boolean): void;
|
(e: 'change', value: boolean): void;
|
||||||
|
|
|
@ -260,9 +260,9 @@
|
||||||
});
|
});
|
||||||
|
|
||||||
const selectedKeys = computed(() => propsRes.value.data.filter((e) => e.enable).map((e) => e.id));
|
const selectedKeys = computed(() => propsRes.value.data.filter((e) => e.enable).map((e) => e.id));
|
||||||
propsEvent.value.rowSelectChange = (key: string) => {
|
propsEvent.value.rowSelectChange = (record: Record<string, any>) => {
|
||||||
propsRes.value.data = propsRes.value.data.map((e) => {
|
propsRes.value.data = propsRes.value.data.map((e) => {
|
||||||
if (e.id === key) {
|
if (e.id === record.id) {
|
||||||
e.enable = !e.enable;
|
e.enable = !e.enable;
|
||||||
}
|
}
|
||||||
return e;
|
return e;
|
||||||
|
|
|
@ -44,9 +44,10 @@
|
||||||
<template #cell="{ record }">
|
<template #cell="{ record }">
|
||||||
<MsCheckbox
|
<MsCheckbox
|
||||||
v-if="attrs.selectorType === 'checkbox'"
|
v-if="attrs.selectorType === 'checkbox'"
|
||||||
:value="props.selectedKeys.has(record[rowKey || 'id'])"
|
:value="getChecked(record)"
|
||||||
|
:indeterminate="getIndeterminate(record)"
|
||||||
@click.stop
|
@click.stop
|
||||||
@change="rowSelectChange(record[rowKey || 'id'])"
|
@change="rowSelectChange(record)"
|
||||||
/>
|
/>
|
||||||
<a-radio
|
<a-radio
|
||||||
v-else-if="attrs.selectorType === 'radio'"
|
v-else-if="attrs.selectorType === 'radio'"
|
||||||
|
@ -221,9 +222,16 @@
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<template #expand-icon="{ expanded, record }">
|
<template #expand-icon="{ expanded, record }">
|
||||||
<slot name="expand-icon" v-bind="{ expanded, record }">
|
<!-- @desc: 这里为了树级别展开折叠如果子级别children不存在不展示展开折叠,所以原本组件的隐藏掉,改成自定义便于控制展示隐藏 -->
|
||||||
<MsIcon v-if="!expanded" :size="8" type="icon-icon_right_outlined" class="text-[var(--color-text-4)]" />
|
<slot v-if="record.children && record.children.length" name="expand-icon" v-bind="{ expanded, record }">
|
||||||
<MsIcon v-else :size="8" class="text-[rgb(var(--primary-6))]" type="icon-icon_down_outlined" />
|
<div
|
||||||
|
:class="`${
|
||||||
|
expanded ? 'bg-[rgb(var(--primary-1))]' : 'bg-[var(--color-text-n8)]'
|
||||||
|
} expand-btn-wrapper flex items-center justify-center`"
|
||||||
|
>
|
||||||
|
<MsIcon v-if="!expanded" :size="8" type="icon-icon_right_outlined" class="text-[var(--color-text-4)]" />
|
||||||
|
<MsIcon v-else :size="8" class="text-[rgb(var(--primary-6))]" type="icon-icon_down_outlined" />
|
||||||
|
</div>
|
||||||
</slot>
|
</slot>
|
||||||
</template>
|
</template>
|
||||||
</a-table>
|
</a-table>
|
||||||
|
@ -305,6 +313,7 @@
|
||||||
MsTableDataItem,
|
MsTableDataItem,
|
||||||
MsTableProps,
|
MsTableProps,
|
||||||
} from './type';
|
} from './type';
|
||||||
|
import { getCurrentRecordChildrenIds } from './utils';
|
||||||
import type { TableChangeExtra, TableColumnData, TableData } from '@arco-design/web-vue';
|
import type { TableChangeExtra, TableColumnData, TableData } from '@arco-design/web-vue';
|
||||||
import type { TableOperationColumn } from '@arco-design/web-vue/es/table/interface';
|
import type { TableOperationColumn } from '@arco-design/web-vue/es/table/interface';
|
||||||
|
|
||||||
|
@ -344,7 +353,7 @@
|
||||||
(e: 'pageChange', value: number): void;
|
(e: 'pageChange', value: number): void;
|
||||||
(e: 'pageSizeChange', value: number): void;
|
(e: 'pageSizeChange', value: number): void;
|
||||||
(e: 'rowNameChange', value: TableData, cb: (v: boolean) => void): void;
|
(e: 'rowNameChange', value: TableData, cb: (v: boolean) => void): void;
|
||||||
(e: 'rowSelectChange', key: string): void;
|
(e: 'rowSelectChange', record: TableData): void;
|
||||||
(e: 'selectAllChange', value: SelectAllEnum, onlyCurrent: boolean): void;
|
(e: 'selectAllChange', value: SelectAllEnum, onlyCurrent: boolean): void;
|
||||||
(e: 'dragChange', value: DragSortParams): void;
|
(e: 'dragChange', value: DragSortParams): void;
|
||||||
(e: 'sorterChange', value: { [key: string]: string }): void;
|
(e: 'sorterChange', value: { [key: string]: string }): void;
|
||||||
|
@ -489,8 +498,8 @@
|
||||||
emit('selectAllChange', v, onlyCurrent);
|
emit('selectAllChange', v, onlyCurrent);
|
||||||
};
|
};
|
||||||
// 行选择器change事件
|
// 行选择器change事件
|
||||||
const rowSelectChange = (key: string) => {
|
const rowSelectChange = (record: TableData) => {
|
||||||
emit('rowSelectChange', key);
|
emit('rowSelectChange', record);
|
||||||
};
|
};
|
||||||
|
|
||||||
// 分页change事件
|
// 分页change事件
|
||||||
|
@ -661,6 +670,31 @@
|
||||||
emit('filterChange', dataIndex, value, isCustomParam);
|
emit('filterChange', dataIndex, value, isCustomParam);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function getChecked(record: TableData) {
|
||||||
|
if (!record.children) {
|
||||||
|
return props.selectedKeys.has(record[rowKey || 'id']);
|
||||||
|
}
|
||||||
|
|
||||||
|
const childKeyIds = getCurrentRecordChildrenIds(record.children, rowKey || 'id');
|
||||||
|
return childKeyIds.every((key) => props.selectedKeys.has(key));
|
||||||
|
}
|
||||||
|
|
||||||
|
function getIndeterminate(record: TableData) {
|
||||||
|
if (!record.children) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const childKeyIds = getCurrentRecordChildrenIds(record.children, rowKey || 'id');
|
||||||
|
// 判断是否有被选中的元素
|
||||||
|
const isSomeSelected = childKeyIds.some((key) => props.selectedKeys.has(key));
|
||||||
|
// 判断是否所有元素都被选中
|
||||||
|
const isEverySelected = childKeyIds.every((key) => props.selectedKeys.has(key));
|
||||||
|
|
||||||
|
if (isSomeSelected && !isEverySelected) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
await initColumn();
|
await initColumn();
|
||||||
batchLeft.value = getBatchLeft();
|
batchLeft.value = getBatchLeft();
|
||||||
|
@ -832,10 +866,18 @@
|
||||||
height: 16px;
|
height: 16px;
|
||||||
border: none;
|
border: none;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
background: var(--color-text-n8);
|
background: transparent;
|
||||||
|
.expand-btn-wrapper {
|
||||||
|
width: 16px;
|
||||||
|
height: 16px;
|
||||||
|
border: none;
|
||||||
|
border-radius: 50%;
|
||||||
|
// background: var(--color-text-n8);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
:deep(.arco-table .arco-table-expand-btn:hover) {
|
:deep(.arco-table .arco-table-expand-btn:hover) {
|
||||||
border-color: transparent;
|
border-color: transparent;
|
||||||
|
background: transparent;
|
||||||
}
|
}
|
||||||
:deep(.arco-table-drag-handle) {
|
:deep(.arco-table-drag-handle) {
|
||||||
.arco-icon-drag-dot-vertical {
|
.arco-icon-drag-dot-vertical {
|
||||||
|
|
|
@ -58,14 +58,30 @@
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// 是否具备子级children
|
||||||
|
const isHasChildren = computed(() => props.currentData.some((item) => item.children));
|
||||||
|
|
||||||
|
// 获取数据第一层级的ids,用来判断全选或者半选
|
||||||
|
const firstLevelAllIds = computed(() => {
|
||||||
|
if (isHasChildren) {
|
||||||
|
return props.currentData.map((item) => item[props.rowKey]);
|
||||||
|
}
|
||||||
|
return [];
|
||||||
|
});
|
||||||
|
|
||||||
const selectAllStatus = ref<SelectAllEnum>(SelectAllEnum.NONE);
|
const selectAllStatus = ref<SelectAllEnum>(SelectAllEnum.NONE);
|
||||||
const checked = computed({
|
const checked = computed({
|
||||||
get: () => {
|
get: () => {
|
||||||
// 如果是选中所有页则是全选状态(选中所有页分两种情况:一是直接通过下拉选项选中所有页;二是当前已选的数量等于表格总数)
|
// 如果是选中所有页则是全选状态(选中所有页分两种情况:一是直接通过下拉选项选中所有页;二是当前已选的数量等于表格总数)
|
||||||
return (
|
// 非子级全选条件
|
||||||
(props.selectedKeys.size > 0 && selectAllStatus.value === SelectAllEnum.ALL) ||
|
if (!isHasChildren.value) {
|
||||||
(props.selectedKeys.size > 0 && props.selectedKeys.size === props.total)
|
return (
|
||||||
);
|
(props.selectedKeys.size > 0 && selectAllStatus.value === SelectAllEnum.ALL) ||
|
||||||
|
(props.selectedKeys.size > 0 && props.selectedKeys.size === props.total)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
// 含有子级 children全选条件
|
||||||
|
return firstLevelAllIds.value.every((item) => props.selectedKeys.has(item));
|
||||||
},
|
},
|
||||||
set: (value) => {
|
set: (value) => {
|
||||||
return value;
|
return value;
|
||||||
|
@ -73,12 +89,23 @@
|
||||||
});
|
});
|
||||||
const indeterminate = computed(() => {
|
const indeterminate = computed(() => {
|
||||||
// 有无勾选的 key且是全选所有页,或非全选所有页且已选中的数量大于 0 且小于总数时是半选状态
|
// 有无勾选的 key且是全选所有页,或非全选所有页且已选中的数量大于 0 且小于总数时是半选状态
|
||||||
return (
|
// 非子级半选条件
|
||||||
(props.excludeKeys.length > 0 && selectAllStatus.value === SelectAllEnum.ALL) ||
|
if (!isHasChildren.value) {
|
||||||
(selectAllStatus.value !== SelectAllEnum.ALL &&
|
return (
|
||||||
props.selectedKeys.size > 0 &&
|
(props.excludeKeys.length > 0 && selectAllStatus.value === SelectAllEnum.ALL) ||
|
||||||
props.selectedKeys.size < props.total)
|
(selectAllStatus.value !== SelectAllEnum.ALL &&
|
||||||
);
|
props.selectedKeys.size > 0 &&
|
||||||
|
props.selectedKeys.size < props.total)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
// 包含子级半选条件
|
||||||
|
const isSomeSelected = firstLevelAllIds.value.some((key) => props.selectedKeys.has(key));
|
||||||
|
const isEverySelected = firstLevelAllIds.value.every((key) => props.selectedKeys.has(key));
|
||||||
|
|
||||||
|
if (isSomeSelected && !isEverySelected) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
});
|
});
|
||||||
|
|
||||||
const handleSelect = (v: SelectAllEnum, onlyCurrent = true) => {
|
const handleSelect = (v: SelectAllEnum, onlyCurrent = true) => {
|
||||||
|
|
|
@ -19,6 +19,7 @@ import type {
|
||||||
MsTableProps,
|
MsTableProps,
|
||||||
SetPaginationPrams,
|
SetPaginationPrams,
|
||||||
} from './type';
|
} from './type';
|
||||||
|
import { getCurrentRecordChildrenIds } from './utils';
|
||||||
import type { TableData } from '@arco-design/web-vue';
|
import type { TableData } from '@arco-design/web-vue';
|
||||||
|
|
||||||
export interface Pagination {
|
export interface Pagination {
|
||||||
|
@ -295,6 +296,22 @@ export default function useTableProps<T>(
|
||||||
propsRes.value.msPagination.pageSize = appStore.pageSize;
|
propsRes.value.msPagination.pageSize = appStore.pageSize;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
// 非全选级取消包含或者不包含子级别
|
||||||
|
const processChildren = (data: MsTableDataItem<T>[], rowKey: string) => {
|
||||||
|
data.forEach((item: MsTableDataItem<T>) => {
|
||||||
|
propsRes.value.selectedKeys.delete(item[rowKey]);
|
||||||
|
|
||||||
|
if (propsRes.value.selectorStatus === SelectAllEnum.ALL) {
|
||||||
|
propsRes.value.excludeKeys.add(item[rowKey]);
|
||||||
|
} else {
|
||||||
|
propsRes.value.excludeKeys.delete(item[rowKey]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (item.children && item.children.length > 0) {
|
||||||
|
processChildren(item.children as MsTableDataItem<T>[], rowKey);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
// 重置选择器
|
// 重置选择器
|
||||||
const resetSelector = (isNone = true) => {
|
const resetSelector = (isNone = true) => {
|
||||||
|
@ -306,14 +323,7 @@ export default function useTableProps<T>(
|
||||||
propsRes.value.excludeKeys.clear();
|
propsRes.value.excludeKeys.clear();
|
||||||
} else {
|
} else {
|
||||||
// 取消当前页的选中项
|
// 取消当前页的选中项
|
||||||
propsRes.value.data.forEach((item) => {
|
processChildren(propsRes.value.data as MsTableDataItem<T>[], rowKey);
|
||||||
propsRes.value.selectedKeys.delete(item[rowKey]);
|
|
||||||
if (propsRes.value.selectorStatus === SelectAllEnum.ALL) {
|
|
||||||
propsRes.value.excludeKeys.add(item[rowKey]);
|
|
||||||
} else {
|
|
||||||
propsRes.value.excludeKeys.delete(item[rowKey]);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -356,6 +366,8 @@ export default function useTableProps<T>(
|
||||||
propsRes.value.filter = cloneDeep(filterItem.value);
|
propsRes.value.filter = cloneDeep(filterItem.value);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const setChildren = () => {};
|
||||||
|
|
||||||
// 事件触发组
|
// 事件触发组
|
||||||
const propsEvent = ref({
|
const propsEvent = ref({
|
||||||
// 排序触发
|
// 排序触发
|
||||||
|
@ -448,17 +460,70 @@ export default function useTableProps<T>(
|
||||||
},
|
},
|
||||||
|
|
||||||
// 表格行的选中/取消事件
|
// 表格行的选中/取消事件
|
||||||
rowSelectChange: (key: string) => {
|
rowSelectChange: (record: MsTableDataItem<T>) => {
|
||||||
const { selectedKeys, excludeKeys } = propsRes.value;
|
const { rowKey } = propsRes.value;
|
||||||
if (selectedKeys.has(key)) {
|
const key = record[rowKey || 'id'];
|
||||||
// 当前已选中,取消选中
|
const { selectedKeys, excludeKeys, data } = propsRes.value;
|
||||||
selectedKeys.delete(key);
|
// 是否包含子级
|
||||||
excludeKeys.add(key);
|
const isHasChildrenData = data.some((item) => item.children);
|
||||||
} else {
|
let isSelectChildren;
|
||||||
// 当前未选中,选中
|
let currentALlParentChildrenIds: string[] = [];
|
||||||
selectedKeys.add(key);
|
// @desc: 如果存在子级获取当前同一级别所有的ids用来判断是否子级全部选择将父节点id也添加进来
|
||||||
if (excludeKeys.has(key)) {
|
if (isHasChildrenData) {
|
||||||
excludeKeys.delete(key);
|
const parentItemChildren: any = data.find((item) => item[rowKey] === record.parent)?.children || [];
|
||||||
|
currentALlParentChildrenIds = getCurrentRecordChildrenIds(parentItemChildren, rowKey || 'id');
|
||||||
|
}
|
||||||
|
// 非子级
|
||||||
|
if (!record.children) {
|
||||||
|
if (selectedKeys.has(key)) {
|
||||||
|
// 当前已选中,取消选中
|
||||||
|
selectedKeys.delete(key);
|
||||||
|
excludeKeys.add(key);
|
||||||
|
// @desc: 只要取消一个子级则取消他的父节点选择
|
||||||
|
if (record.parent) {
|
||||||
|
selectedKeys.delete(record.parent);
|
||||||
|
excludeKeys.add(record.parent);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 当前未选中,选中
|
||||||
|
selectedKeys.add(key);
|
||||||
|
if (excludeKeys.has(key)) {
|
||||||
|
excludeKeys.delete(key);
|
||||||
|
}
|
||||||
|
// @desc: 判断当前子级是否已经全选,全选则将上层父级也选择
|
||||||
|
isSelectChildren = currentALlParentChildrenIds.every((id) => selectedKeys.has(id));
|
||||||
|
if (isSelectChildren && record.parent) {
|
||||||
|
selectedKeys.add(record.parent);
|
||||||
|
excludeKeys.delete(record.parent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 包含子级
|
||||||
|
} else if (record.children) {
|
||||||
|
const childrenIds = getCurrentRecordChildrenIds(record.children, rowKey || 'id');
|
||||||
|
const isSelectAllChildren = childrenIds.every((id) => selectedKeys.has(id));
|
||||||
|
const includeCurrentIds = [key, ...childrenIds];
|
||||||
|
// 当前父节点已选中,取消选择父节点和父节点下所有子节点
|
||||||
|
if (isSelectAllChildren) {
|
||||||
|
// childrenIds.push(key);
|
||||||
|
includeCurrentIds.forEach((id) => {
|
||||||
|
selectedKeys.delete(id);
|
||||||
|
});
|
||||||
|
includeCurrentIds.forEach((id) => {
|
||||||
|
excludeKeys.add(id);
|
||||||
|
});
|
||||||
|
// selectedKeys.delete(key);
|
||||||
|
// excludeKeys.add(key);
|
||||||
|
// 未选中则全选父节点和下边所有子节点
|
||||||
|
} else {
|
||||||
|
selectedKeys.add(key);
|
||||||
|
collectIds(record.children, rowKey);
|
||||||
|
childrenIds.forEach((id) => {
|
||||||
|
excludeKeys.delete(id);
|
||||||
|
});
|
||||||
|
if (excludeKeys.has(key)) {
|
||||||
|
excludeKeys.delete(key);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (selectedKeys.size === 0 && propsRes.value.selectorStatus === SelectAllEnum.CURRENT) {
|
if (selectedKeys.size === 0 && propsRes.value.selectorStatus === SelectAllEnum.CURRENT) {
|
||||||
|
|
|
@ -0,0 +1,25 @@
|
||||||
|
import type { TableData } from '@arco-design/web-vue';
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {childrenData} 包含子级数据
|
||||||
|
* @param {rowKey}
|
||||||
|
* @returns 返回当前子级childrenIds
|
||||||
|
*/
|
||||||
|
export function getCurrentRecordChildrenIds(childrenData: TableData[], rowKey: string) {
|
||||||
|
const currentRecordChildrenIds: string[] = [];
|
||||||
|
|
||||||
|
function traverse(childrenRecord: TableData) {
|
||||||
|
currentRecordChildrenIds.push(childrenRecord[rowKey as string]);
|
||||||
|
if (childrenRecord.children && childrenRecord.children.length > 0) {
|
||||||
|
childrenRecord.children.forEach((child: TableData) => traverse(child));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
childrenData.forEach((childrenRecord: TableData) => {
|
||||||
|
traverse(childrenRecord);
|
||||||
|
});
|
||||||
|
|
||||||
|
return currentRecordChildrenIds;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {};
|
|
@ -424,11 +424,11 @@
|
||||||
/**
|
/**
|
||||||
* 表格单行选中事件处理
|
* 表格单行选中事件处理
|
||||||
*/
|
*/
|
||||||
function handleRowSelectChange(key: string) {
|
function handleRowSelectChange(record: ApiCaseDetail | ApiDefinitionDetail | ApiScenarioTableItem) {
|
||||||
const selectedData = currentTable.value.propsRes.value.data.find((e: any) => e.id === key);
|
const selectedData = currentTable.value.propsRes.value.data.find((e: any) => e.id === record.id);
|
||||||
if (tableSelectedKeys.value.includes(key)) {
|
if (tableSelectedKeys.value.includes(record.id)) {
|
||||||
// 取消选中
|
// 取消选中
|
||||||
tableSelectedData.value = tableSelectedData.value.filter((e) => e.id !== key);
|
tableSelectedData.value = tableSelectedData.value.filter((e) => e.id !== record.id);
|
||||||
} else if (selectedData) {
|
} else if (selectedData) {
|
||||||
tableSelectedData.value.push(selectedData);
|
tableSelectedData.value.push(selectedData);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue