从树形element中(tree-table)表格上做的改变
业务需求:
需要一个对象对应一个或者多个模块
最终点击确认选择后的获取的数据结构为 例:
[
{
"group_id": 99,
"module_id": [30]
},
{
"group_id": 97,
"module_id": [23,30]
},
{
"group_id": 96,
"module_id": [3,5]
}
]
最终效果
示例demo:vue-table-tree: 基于element-plus的树形表格
git clone https://gitee.com/a__hang/vue-table-tree.git
可呈现效果实现一行的半选状态 一行全选状态和全表格数据的全选状态
逻辑代码:
HTML:
<!-- oneToMany -->
<template>
<div>
<el-dialog v-model="dialogVisibleTask" title="关联任务对象" width="70%" draggable top="5vh" @close="closeDialog" :destroy-on-close="destroyOnClose">
<el-card shadow="never">
<template #header>
<!-- 此处组件是自己封装的搜索组件 可以忽略 -->
<GlobalSearch @search="dialogHandleSearch" :showOnBack="false" shadow="never" :showReset1="true" :showReset="false"></GlobalSearch>
</template>
<el-table border :data="tableData" ref="multipleTableRefDialog" :row-class-name="rowClassNameFun" :header-row-class-name="headerRowClassName" row-key="id" class="mt-1" max-height="500" style="width: 100%" :header-cell-style="{ background: '#fafafa' }" @select="selectFun" size="small" @selection-change="dialogSelectChange" @select-all="selectAllFun">
<el-table-column type="selection" align="center" :reserve-selection="true"></el-table-column>
<el-table-column label="序号" type="index" width="50" align="center"></el-table-column>
<el-table-column align="center" property="serial_no" label="任务对象编号"></el-table-column>
<el-table-column align="center" property="description" label="任务对象描述"></el-table-column>
<el-table-column align="center" property="name" label="任务客体名称">
<template #default="scope">
<el-check-tag checked :type="scope.row.module ? 'primary' : 'info'">
{{ scope.row.name }}
</el-check-tag>
</template>
</el-table-column>
<el-table-column align="center" property="module.name" label="任务客体模块">
<template #="{ row }">
<el-tag v-for="item in row.module" :key="item.id" :type="item.type" :effect="item.type == 'info' ? 'plain' : 'dark'" class="pointer" style="margin: 5px 5px" @click="changeColor(row, item)">
{{ item.name }}
</el-tag>
</template>
</el-table-column>
</el-table>
<el-pagination style="margin-top: 10px" v-model:current-page="dialogPageNo" v-model:page-size="dialogPageSize" :page-sizes="[5, 10, 15, 20]" :background="true" layout="prev, pager, next,->,total" :total="dialogTotal" @size-change="handleSizeChange" @current-change="getObjData" />
<template #footer>
<div class="float-r mb-2">
<el-button @click="dialogVisibleTask = false">取消</el-button>
<el-button type="primary" @click="confirmTask">确定</el-button>
</div>
</template>
</el-card>
</el-dialog>
</div>
</template>
tableData数据:
[
{
"id": 99,
"created_at": "2024-05-11 16:12:19",
"updated_at": "2024-05-11 16:12:19",
"serial_no": "DX10003",
"name": "测试对象3",
"description": "123456",
"create_user__id": 2,
"create_user__name": "admins",
"module": [
{
"created_at": "2024-04-23T11:44:00+08:00",
"serial_no": "mk001",
"id": 23,
"updated_at": "2024-04-23T11:44:00+08:00",
"name": "基本信息",
"create_user_id": 37,
"delete_flag": 0,
"description": "基本信息",
"is_finished": 0,
"daily": []
},
{
"created_at": "2024-03-12T10:27:14+08:00",
"serial_no": "mk0002",
"id": 2,
"updated_at": "2024-03-12T10:27:14+08:00",
"name": "工艺",
"create_user_id": 2,
"delete_flag": 0,
"description": "洗面奶工艺",
"is_finished": 0,
"daily": []
},
{
"created_at": "2024-04-26T18:45:41+08:00",
"serial_no": "mk003",
"id": 30,
"updated_at": "2024-04-26T18:45:41+08:00",
"name": "第三课",
"create_user_id": 40,
"delete_flag": 0,
"description": "第三课",
"is_finished": 0,
"daily": []
},
{
"created_at": "2024-04-24T10:11:33+08:00",
"serial_no": "mk004",
"id": 26,
"updated_at": "2024-04-24T10:11:33+08:00",
"name": "产品名称",
"create_user_id": 37,
"delete_flag": 0,
"description": "产品名称",
"is_finished": 0,
"daily": []
}
]
},
{
"id": 97,
"created_at": "2024-05-07 12:27:33",
"updated_at": "2024-05-09 15:09:29",
"serial_no": "DX10002",
"name": "测试对象2",
"description": "123456",
"create_user__id": 43,
"create_user__name": "test003",
"module": [
{
"created_at": "2024-03-12T10:26:50+08:00",
"serial_no": "mk0001",
"id": 1,
"updated_at": "2024-05-10T09:05:08+08:00",
"name": "流程",
"create_user_id": 2,
"delete_flag": 0,
"description": "洗面奶流程",
"is_finished": 0,
"daily": []
},
{
"created_at": "2024-03-12T10:27:14+08:00",
"serial_no": "mk0002",
"id": 2,
"updated_at": "2024-03-12T10:27:14+08:00",
"name": "工艺",
"create_user_id": 2,
"delete_flag": 0,
"description": "洗面奶工艺",
"is_finished": 0,
"daily": []
},
{
"created_at": "2024-03-13T17:07:49+08:00",
"serial_no": "mk0003",
"id": 3,
"updated_at": "2024-03-20T09:21:58+08:00",
"name": "尺寸",
"create_user_id": 1,
"delete_flag": 0,
"description": "尺寸200*100",
"is_finished": 0,
"daily": []
},
{
"created_at": "2024-03-13T17:08:01+08:00",
"serial_no": "mk0004",
"id": 4,
"updated_at": "2024-03-13T17:08:01+08:00",
"name": "颜色",
"create_user_id": 1,
"delete_flag": 0,
"description": "蓝色",
"is_finished": 0,
"daily": []
},
{
"created_at": "2024-03-13T17:08:33+08:00",
"serial_no": "mk0005",
"id": 5,
"updated_at": "2024-03-25T13:07:18+08:00",
"name": "牙膏",
"create_user_id": 1,
"delete_flag": 0,
"description": "竹炭清洁",
"is_finished": 0,
"daily": []
},
{
"created_at": "2024-03-13T17:08:49+08:00",
"serial_no": "mk0006",
"id": 6,
"updated_at": "2024-03-27T17:10:35+08:00",
"name": "类型",
"create_user_id": 1,
"delete_flag": 0,
"description": "日常",
"is_finished": 0,
"daily": []
},
{
"created_at": "2024-04-23T11:44:00+08:00",
"serial_no": "mk001",
"id": 23,
"updated_at": "2024-04-23T11:44:00+08:00",
"name": "基本信息",
"create_user_id": 37,
"delete_flag": 0,
"description": "基本信息",
"is_finished": 0,
"daily": []
},
{
"created_at": "2024-04-23T11:44:24+08:00",
"serial_no": "mk002",
"id": 24,
"updated_at": "2024-04-23T11:44:39+08:00",
"name": "产品配方",
"create_user_id": 37,
"delete_flag": 0,
"description": "产品配方",
"is_finished": 0,
"daily": []
},
{
"created_at": "2024-04-23T11:45:07+08:00",
"serial_no": "mk003",
"id": 25,
"updated_at": "2024-04-23T11:45:07+08:00",
"name": "生产工艺",
"create_user_id": 37,
"delete_flag": 0,
"description": "生产工艺",
"is_finished": 0,
"daily": []
},
{
"created_at": "2024-04-24T10:11:33+08:00",
"serial_no": "mk004",
"id": 26,
"updated_at": "2024-04-24T10:11:33+08:00",
"name": "产品名称",
"create_user_id": 37,
"delete_flag": 0,
"description": "产品名称",
"is_finished": 0,
"daily": []
},
{
"created_at": "2024-04-24T10:12:01+08:00",
"serial_no": "mk005",
"id": 27,
"updated_at": "2024-04-24T10:12:01+08:00",
"name": "配方相关信息",
"create_user_id": 37,
"delete_flag": 0,
"description": "配方相关信息",
"is_finished": 0,
"daily": []
},
{
"created_at": "2024-04-25T17:54:57+08:00",
"serial_no": "mk001",
"id": 28,
"updated_at": "2024-04-25T17:54:57+08:00",
"name": "第一课",
"create_user_id": 40,
"delete_flag": 0,
"description": "第一课",
"is_finished": 0,
"daily": []
},
{
"created_at": "2024-04-25T17:55:17+08:00",
"serial_no": "mk002",
"id": 29,
"updated_at": "2024-04-25T17:55:17+08:00",
"name": "第二课",
"create_user_id": 40,
"delete_flag": 0,
"description": "第二课",
"is_finished": 0,
"daily": []
},
{
"created_at": "2024-04-26T18:45:41+08:00",
"serial_no": "mk003",
"id": 30,
"updated_at": "2024-04-26T18:45:41+08:00",
"name": "第三课",
"create_user_id": 40,
"delete_flag": 0,
"description": "第三课",
"is_finished": 0,
"daily": []
},
{
"created_at": "2024-04-26T18:46:01+08:00",
"serial_no": "mk004",
"id": 31,
"updated_at": "2024-04-26T18:46:01+08:00",
"name": "第四课",
"create_user_id": 40,
"delete_flag": 0,
"description": "第四课",
"is_finished": 0,
"daily": []
}
]
},
{
"id": 96,
"created_at": "2024-05-07 12:27:13",
"updated_at": "2024-05-11 16:09:28",
"serial_no": "DX10001",
"name": "测试对象1",
"description": "123456",
"create_user__id": 43,
"create_user__name": "test003",
"module": [
{
"created_at": "2024-03-13T17:07:49+08:00",
"serial_no": "mk0003",
"id": 3,
"updated_at": "2024-03-20T09:21:58+08:00",
"name": "尺寸",
"create_user_id": 1,
"delete_flag": 0,
"description": "尺寸200*100",
"is_finished": 0,
"daily": []
},
{
"created_at": "2024-03-13T17:08:33+08:00",
"serial_no": "mk0005",
"id": 5,
"updated_at": "2024-03-25T13:07:18+08:00",
"name": "牙膏",
"create_user_id": 1,
"delete_flag": 0,
"description": "竹炭清洁",
"is_finished": 0,
"daily": []
}
]
}
]
<script setup lang="ts">
import { ref, onMounted, watchEffect, nextTick, reactive, watch, onBeforeUpdate } from 'vue';
import { ElMessage } from 'element-plus';
import type { ElTable, CheckboxValueType } from 'element-plus';
import { reqObjInfo } from '@/api/home/index';
const tableData = ref<any>();
const dialogVisibleTask = ref<boolean>(false);
const destroyOnClose=ref<boolean>(false)
const dialogKeyword = ref<any>('');
const dialogPageNo = ref<any>(1);
const dialogPageSize = ref<any>(9999999);
const dialogTotal = ref<any>();
const multipleTableRefDialog = ref<InstanceType<typeof ElTable>>();
const selectDataArr = ref<any>();
const props = defineProps({
isShowDialog: {
type: Boolean,
default: false,
},
row: {
type: Object as () => { module: Array<{ id: string; name: string; type: string; isSelect: boolean }> },
default: () => ({ module: [] }),
},
paramsDataDialog: {
type: [Array, Object],
default: () => {
return [];
},
},
paramsDataDialogId:{
type:Number,
default: () => {
return 0;
},
},
drawerState:{
type:Boolean,
default: () => {
return false;
},
}
});
const isShow=ref(true)
onBeforeUpdate(() => {
if (props.paramsDataDialogId!==0) {
setTimeout(() => {
props.paramsDataDialog.forEach((item: any) => {
tableData.value.forEach((row: any) => {
if (item.id == row.id) {
row.isSelect = true;
// 根据 selectImpowerIdArr 中的数组筛选 tableData 中的行,并将其标记为选中状态
multipleTableRefDialog.value!.toggleRowSelection(row, true);
item.module.forEach((itemItems: any) => {
itemItems.isSelect = true;
row.module.forEach((rowItems: any) => {
if (itemItems.id == rowItems.id) {
rowItems.isSelect = true;
rowItems.type = 'success';
if (row.module.every((item: any) => item.type === 'success')) {
row.isSelect = true;
} else if (row.module.every((item: any) => item.type === 'info')) {
row.isSelect = false;
} else {
row.isSelect = '';
}
multipleTableRefDialog.value!.toggleRowSelection(rowItems, true);
}
});
});
}
});
});
}, 150);
} else if (props.paramsDataDialogId==0 && groups.length == 0) {
if (isShow.value) {
initData();
if (multipleTableRefDialog.value!==undefined) {
// destroyOnClose.value=true
multipleTableRefDialog.value!.clearSelection();
}
isShow.value = false;
}
}
});
onMounted(() => {
getObjData();
});
const getObjData = async () => {
let resObj: any = await reqObjInfo(1, 9999999, dialogKeyword.value);
if (resObj.code == 0) {
tableData.value = resObj.data;
dialogTotal.value = resObj.total;
initData();
} else {
ElMessage({
message: '获取错误!',
type: 'error',
});
}
};
const initData = () => {
tableData.value.forEach((item: any) => {
item.isSelect = false; //默认为不选中
item.parentId = 0;
item.module.forEach((moduleItem: any) => {
moduleItem.isSelect = false;
moduleItem.parentId = item.id;
moduleItem.type = 'info';
});
});
};
// 组合搜索按钮的回调
const dialogHandleSearch = (search: string) => {
dialogKeyword.value = search;
getObjData();
multipleTableRefDialog.value!.clearSelection();
initData();
};
const changeColor = (row: any, item: { id: string; name: string; type: string; isSelect: boolean }) => {
if (item.type == 'info') {
item.type = 'success';
item.isSelect = true;
} else {
item.type = 'info';
item.isSelect = false;
}
if (row.module.every((item: any) => item.type === 'success')) {
row.isSelect = true;
multipleTableRefDialog.value!.toggleRowSelection(row, row.isSelect);
} else if (row.module.every((item: any) => item.type === 'info')) {
row.isSelect = false;
multipleTableRefDialog.value!.toggleRowSelection(row, row.isSelect);
} else if (row.module.some((item: any) => item.type === 'info')) {
row.isSelect = '';
}
};
const selectFun = (selection: any, row: any) => {
setRowIsSelect(row);
};
const setRowIsSelect = (row: any) => {
//当点击父级复选框时,当前的状态可能为未知状态,所以当前行状态设为false并选中,即可实现子级点全选效果
if (row.isSelect == '') {
row.isSelect = false;
row.module.forEach((item: any) => {
item.type = 'success';
});
multipleTableRefDialog.value!.toggleRowSelection(row, true);
}
row.isSelect = !row.isSelect;
if (row.module && row.module.length > 0) {
row.module.forEach((item: any) => {
item.isSelect = row.isSelect;
if (row.isSelect) {
item.type = 'success';
} else {
item.type = 'info';
}
});
}
};
// 检测表格数据是否全选
const oneProductIsSelect = ref();
const checkIsAllSelect = () => {
oneProductIsSelect.value = [];
tableData.value.forEach((item: any) => {
oneProductIsSelect.value.push(item.isSelect);
});
//判断一级是否是全选.如果一级全为true,则设置为取消全选,否则全选
let isAllSelect = oneProductIsSelect.value.every((selectStatusItem: any) => {
return true == selectStatusItem;
});
return isAllSelect;
};
const selectAllFun = (selection: any) => {
let isAllSelect = checkIsAllSelect();
tableData.value.forEach((item: any) => {
item.isSelect = isAllSelect;
multipleTableRefDialog.value!.toggleRowSelection(item, !isAllSelect);
selectFun(selection, item);
});
};
// 表格行样式 当当前行的状态为不明确状态时,添加样式,使其复选框为不明确状态样式
const rowClassNameFun = ({ row }: { row: any }) => {
if (row.isSelect === '') {
return 'indeterminate';
}
};
// 表格标题样式 当一级目录有为不明确状态时,添加样式,使其全选复选框为不明确状态样式
const headerRowClassName = ({ row }: { row: any }) => {
let oneProductIsSelect: any = [];
tableData.value.forEach((item: any) => {
oneProductIsSelect.push(item.isSelect);
});
if (oneProductIsSelect.includes('')) {
return 'indeterminate';
}
return '';
};
const dialogSelectChange = (value: any) => {
selectDataArr.value = value;
};
const handleSizeChange = (val: number) => {
dialogPageSize.value = val;
getObjData();
};
// 定义组件发出的事件
const emit = defineEmits(['taskObj', 'closeDialog']);
let groups: any = [];
let groupName:any=[]
const confirmTask = () => {
// groups = [];
tableData.value.forEach((item: any) => {
if (item.isSelect || item.isSelect === '') {
let groupItems: { name: string; module: any[] } = {
name: item.name,
module: [],
};
let groupItem: { group_id: number; module_id: number[] } = {
group_id: item.id,
module_id: [],
};
item.module.forEach((moduleItem: any) => {
if (moduleItem.isSelect) {
groupItems.module.push({ name: moduleItem.name });
}
});
item.module.forEach((moduleItem: any) => {
if (moduleItem.isSelect) {
groupItem.module_id.push(moduleItem.id);
}
});
groups.push(groupItem);
groupName.push(groupItems);
}
});
if (groups.length > 0) {
ElMessage({
message: '关联成功!',
type: 'success',
});
// console.log(props.paramsDataDialogId);
emit('taskObj', groups,groupName);
dialogVisibleTask.value = false;
groups=[]
groupName=[]
} else {
ElMessage({
message: '请选择任务对象!',
type: 'error',
});
}
};
watch(
() => props.isShowDialog,
(val) => {
dialogVisibleTask.value = val;
},
{ immediate: true },
);
watchEffect(()=>{
if (props.drawerState==false) {
isShow.value = true;
}
if (props.paramsDataDialogId!==0) {
setTimeout(()=>{
multipleTableRefDialog.value!.clearSelection();
initData();
},100)
}
})
const closeDialog = () => {
emit('closeDialog', false);
};
</script>
以上代码是自己封装的一个子组件 是由父组件传的渲染数据
受到博客Element Table 表格树形结构多选框选中父级时会选中子级(递归多级)_element table tree 选择子级-CSDN博客的启发做出改变
学习共勉