一、先看效果
1.单选
显示效果:
2.多选
显示效果:
进入默认全部人员,也可以根据左侧部门树来筛选人员
二、代码
<template>
<template v-if="props.isbutton">
<span @click="selectPersonnelVisible = true">
<slot></slot>
</span>
</template>
<el-select
:disabled="disabled"
v-if="!props.isbutton"
ref="chooseProjectName"
v-model="selectPersonnelInfo"
multiple
collapse-tags-tooltip
:placeholder="props.placeholder"
style="width: 240px"
@focus="handleTestTasks"
@visible-change="visibleType"
>
<el-option v-for="item in personnel" :key="item.id" :label="item?.name" :value="item?.id" />
</el-select>
<el-dialog v-model="selectPersonnelVisible" append-to-body title="选择" width="60%" draggable>
<div class="selectPersonnelContent">
<div class="PersonneTree">
<div class="header">
<el-input v-model="departmentName" placeholder="请输入部门名称" :suffix-icon="Search" />
</div>
<el-tree v-if="treeFlag" highlight-current style="max-width: 600px" :props="prop" :load="loadNode" lazy @current-change="checkChangeTree" />
</div>
<div class="PersonneTable">
<div class="header">
<el-form :model="state.queryForm" ref="queryRef" :inline="true" @keyup.enter="getDataList">
<el-form-item label="姓名" prop="name">
<el-input placeholder="请输入姓名" v-model="state.queryForm.name" />
</el-form-item>
<el-form-item style="margin-bottom: 0 !important">
<el-button icon="search" type="primary" @click="getDataList"> 查询 </el-button>
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
</div>
<el-table
v-if="props.multiple"
ref="multipleTableRef"
:data="state.dataList"
style="width: 100%"
highlight-current-row
@selection-change="handleSelectionChange"
@select="onSelect"
@select-all="onSelectAll"
empty-text="请选择组织或更换组织"
>
<el-table-column type="selection" width="55" />
<el-table-column label="用户名" show-overflow-tooltip
><template #default="scope">{{ scope.row.username }}</template>
</el-table-column>
<el-table-column label="姓名" show-overflow-tooltip>
<template #default="scope">{{ scope.row.name }}</template>
</el-table-column>
</el-table>
<el-table
v-if="!props.multiple"
ref="multipleTableRef"
:data="state.dataList"
style="width: 100%"
highlight-current-row
empty-text="请选择组织或更换组织"
@current-change="handleCurrentChange"
>
<el-table-column label="用户名" show-overflow-tooltip
><template #default="scope">{{ scope.row.username }}</template>
</el-table-column>
<el-table-column label="姓名" show-overflow-tooltip>
<template #default="scope">{{ scope.row.name }}</template>
</el-table-column>
</el-table>
<pagination @size-change="sizeChangeHandle" @current-change="currentChangeHandle" v-bind="state.pagination" />
</div>
<div class="PersonneInfo">
<div class="header">
<span>已选人员({{ selectionPersonnel.length }})</span>
<span @click="clearPersonneInfo">清空</span>
</div>
<ul>
<li v-for="(item, index) in selectionPersonnel" :key="index">
{{ item?.name }}<el-icon class="icon" @click="delPersonneInfo(item)"><Delete /></el-icon>
</li>
</ul>
</div>
</div>
<template #footer>
<div class="dialog-footer">
<el-button @click="selectPersonnelVisible = false">取消</el-button>
<el-button type="primary" @click="submit"> 确认 </el-button>
</div>
</template>
</el-dialog>
</template>
<script setup lang="ts" name="selectPersonnel">
import { BasicTableProps, useTable } from '/@/hooks/table';
import { getDepartmentTree, getSimpleUserPage } from '/@/api/selectPersonnel/index';
import type Node from 'element-plus/es/components/tree/src/model/node';
import { Search } from '@element-plus/icons-vue';
const selectPersonnelInfo = ref([]) as any;
const selectPersonnelVisible = ref(false);
const chooseProjectName = ref();
const multipleTableRef = ref();
const queryRef = ref();
const departmentName = ref('');
const personnel = ref([]) as any;
const emit = defineEmits(['update:orgList', 'change']);
const treeFlag = ref(true);
const props = defineProps({
multiple: {
type: Boolean,
default: () => false,
},
orgList: {
type: Array,
default: () => [],
},
placeholder: {
type: String,
default: '请选择人员',
},
isbutton: {
type: Boolean,
detalut: () => false,
},
disabled: {
type: Boolean,
default: () => false,
},
});
interface Tree {
name: string;
leaf?: boolean;
}
const state: BasicTableProps = reactive<BasicTableProps>({
queryForm: {},
pageList: getSimpleUserPage,
});
// table hook
const { currentChangeHandle, sizeChangeHandle, getDataList } = useTable(state);
const prop = {
label: 'name',
// children: 'children',
isLeaf: 'leaf',
};
// 多选
const multipleSelection = ref([]) as any;
// 人员
const selectionPersonnel = ref([]) as any;
const handleSelectionChange = (val: any) => {
multipleSelection.value = val;
// 去重
val.forEach((el: any) => {
if (!selectionPersonnel.value.find((item: any) => item.id === el.id)) {
selectionPersonnel.value.push(el);
}
});
};
const onSelect = (rows: any, row: any) => {
// selected true就是选中,0或者false是取消选中
let selected = rows.length && rows.indexOf(row) !== -1;
if (!selected) {
selectionPersonnel.value.forEach((item: { id: '' }, i: number) => {
if (row?.id == item.id) {
nextTick(() => {
selectionPersonnel.value.splice(i, 1);
});
}
});
}
};
const onSelectAll = (rows: any) => {
if (!rows.length) {
for (let index = 0; index < selectionPersonnel.value.length; index++) {
const element = selectionPersonnel.value[index];
if (state.dataList?.find((el: any) => el.id === element.id)) {
selectionPersonnel.value.splice(index, 1);
index--;
}
}
}
};
// 单选
const handleCurrentChange = (row: any) => {
multipleSelection.value = [row];
// 去重
if (!selectionPersonnel.value.find((item: any) => item?.id === row?.id)) {
selectionPersonnel.value = [row];
}
};
// 阻止下拉框 下拉事件
const handleTestTasks = () => {
chooseProjectName.value.blur();
};
// 清空搜索条件
const resetQuery = () => {
// 清空搜索条件
queryRef.value?.resetFields();
getDataList();
};
// 删除
const delPersonneInfo = (row: { id: '' }) => {
state.dataList?.forEach((item: { id: '' }) => {
if (row?.id == item.id) {
nextTick(() => {
multipleTableRef.value!.toggleRowSelection(item, false);
});
return;
}
});
selectionPersonnel.value.forEach((item: { id: '' }, i: number) => {
if (row?.id == item.id) {
selectionPersonnel.value.splice(i, 1);
return;
}
});
};
// 清空
const clearPersonneInfo = () => {
nextTick(() => {
multipleTableRef.value?.clearSelection();
});
selectionPersonnel.value = [];
};
// select 回显
watch(
() => props.orgList,
() => {
personnel.value = [];
selectPersonnelInfo.value = [];
multipleSelection.value = [];
// 多选回显
if (props.orgList.length) {
multipleSelection.value = props.orgList;
selectionPersonnel.value = props.orgList;
personnel.value = props.orgList;
selectPersonnelInfo.value = multipleSelection.value.map((item: any) => item?.id);
} else {
selectionPersonnel.value = [];
}
},
{ deep: true, immediate: true }
);
// 选中状态回显
watch(
() => selectPersonnelVisible.value,
() => {
nextTick(() => {
multipleTableRef.value?.clearSelection();
});
if (!selectPersonnelVisible.value) return;
if (props.orgList.length) {
nextTick(() => {
state.dataList?.forEach((item: any) => {
if (selectPersonnelInfo.value.includes(item.id)) {
multipleTableRef.value?.toggleRowSelection(item, true);
}
});
});
} else {
nextTick(() => {
multipleTableRef.value?.clearSelection();
});
}
}
);
// 切换回显
watch(
() => state.dataList,
() => {
if (!props.multiple) return;
if (selectionPersonnel.value.length) {
state.dataList?.forEach((el: any) => {
if (selectionPersonnel.value.find((item: any) => item.id === el.id)) {
nextTick(() => {
multipleTableRef.value?.toggleRowSelection(el, true);
});
}
});
}
},
{ immediate: true }
);
const visibleType = () => {
// 弹框
nextTick(() => {
// 不显示下拉框
chooseProjectName.value.blur();
selectPersonnelVisible.value = true;
});
};
const departmentTree = ref();
onBeforeMount(() => {
selectionPersonnel.value = [];
getDepartment();
});
// 输入查询
let time: any;
watch(
() => departmentName.value,
() => {
if (time) {
clearTimeout(time);
time = setTimeout(() => {
getDepartment();
}, 500);
} else {
time = setTimeout(() => {
getDepartment();
}, 500);
}
}
);
// 获取部门
const getDepartment = () => {
treeFlag.value = false;
getDepartmentTree({ parentId: -1, deptName: departmentName.value }).then((res) => {
if (departmentName.value) {
departmentTree.value = res.data.map((item: any) => {
item.leaf = item.children ? false : true;
return item;
});
treeFlag.value = true;
} else {
res.data.unshift({
createTime: '',
id: '',
isLock: false,
name: '全部人员',
parentId: '',
weight: 9999,
leaf: true,
});
departmentTree.value = res.data.map((item: any) => {
item.leaf = item.children ? false : true;
return item;
});
treeFlag.value = true;
}
});
};
// 选择部门
const checkChangeTree = (data: any) => {
state.queryForm.deptId = data.id;
getDataList();
};
// 节点懒加载
const loadNode = async (node: Node, resolve: (data: Tree[]) => void, reject: () => void) => {
if (node.level === 0) {
return resolve(departmentTree.value);
}
if (node.level >= 1) {
try {
let res = await getDepartmentTree({ parentId: node.data.id });
res.data.forEach((element: any) => {
element.leaf = element.children ? false : true;
return resolve(res.data);
});
} catch (error) {
reject();
}
}
};
// 确认
const submit = () => {
if (props.isbutton) {
emit('change', selectionPersonnel.value);
selectPersonnelVisible.value = false;
return;
}
emit('update:orgList', selectionPersonnel.value);
emit('change', selectionPersonnel.value);
personnel.value = selectionPersonnel.value;
selectPersonnelInfo.value = selectionPersonnel.value.map((item: any) => item?.id);
selectPersonnelVisible.value = false;
};
</script>
<style lang="scss" scoped>
:deep(.el-input__wrapper) {
box-shadow: 0 0 0 1px #dcdfe6 !important;
}
:deep(.el-tooltip__trigger) {
box-shadow: 0 0 0 1px #dcdfe6 !important;
}
:deep(.el-tag__close) {
display: none;
}
.selectPersonnelContent {
width: 100%;
height: 700px;
display: flex;
.PersonneTree {
width: 350px;
height: 100%;
border: 1px solid #eee;
border-radius: 10px;
padding: 6px;
overflow: hidden;
overflow-y: auto;
margin-right: 15px;
.header {
margin-bottom: 10px;
}
}
.PersonneTable {
flex: 1;
height: 100%;
overflow: hidden;
overflow-y: auto;
border: 1px solid #eee;
padding: 6px;
border-radius: 10px;
margin-right: 15px;
.header {
margin: 10px 8px 18px;
}
}
.PersonneInfo {
padding: 10px;
width: 350px;
height: 100%;
border: 1px solid #eee;
padding: 6px;
border-radius: 10px;
overflow: hidden;
overflow-y: auto;
.header {
padding: 0 6px;
height: 30px;
border-bottom: 1px solid #eee;
display: flex;
align-items: center;
justify-content: space-between;
span:nth-child(2) {
color: rgb(183, 108, 108);
cursor: pointer;
}
}
ul {
li {
height: 25px;
display: flex;
align-items: center;
justify-content: space-between;
border: 1px solid #eee;
border-radius: 6px;
padding: 0 6px;
margin: 5px 0;
.icon {
color: red;
cursor: pointer;
}
}
li:hover {
background-color: #eee;
}
}
}
}
</style>
selectPersonnel人员组件
文件位置:src\components\selectPersonnel\index.vue
描述:用于选择人员
参数:
1.multiple 类型 Boolean 是否多选
2.orgList 类型 Array v-model:orgList 绑定 选中的人员
3.placeholder 类型 String 描述
4.isbutton 类型 Boolean 是否自定义button
5.disabled 类型 Boolean 是否禁用
事件:
change(data)=>{} data为选中的人员 该事件只有在isbutton为true时生效
三、优化Tree新增引导线
<template>
<template v-if="props.isbutton">
<span @click="selectPersonnelVisible = true">
<slot></slot>
</span>
</template>
<el-select
:disabled="disabled"
v-if="!props.isbutton"
ref="chooseProjectName"
v-model="selectPersonnelInfo"
multiple
collapse-tags
:placeholder="props.placeholder"
style="width: 240px"
@focus="handleTestTasks"
@visible-change="visibleType"
>
<el-option v-for="item in personnel" :key="item.id" :label="item?.name" :value="item?.id" />
</el-select>
<el-dialog v-model="selectPersonnelVisible" append-to-body title="选择" width="60%" draggable>
<div class="selectPersonnelContent">
<div class="PersonneTree">
<div class="header">
<el-input v-model="departmentName" placeholder="请输入部门名称" :suffix-icon="Search" />
</div>
<el-tree
class="tree-line"
ref="treeRef"
v-if="treeFlag"
highlight-current
style="max-width: 600px"
:expand-on-click-node="false"
:props="prop"
:load="loadNode"
:indent="0"
lazy
@current-change="checkChangeTree"
><template #default="{ data }">
<span class="custom-tree-node">
<span class="tree-icon">
<SvgIcon v-if="data.deptType === 0" name="local-all-personnel" :size="16" />
<SvgIcon v-if="data.deptType === 1" name="local-dept3" :size="16" />
<SvgIcon v-if="data.deptType === 2" name="local-company" :size="16" />
<SvgIcon v-if="data.deptType === 3" name="local-construction-team2" :size="16" />
</span>
<span>{{ data.name }}</span>
</span>
</template>
</el-tree>
</div>
<div class="PersonneTable">
<div class="header">
<el-form :model="state.queryForm" ref="queryRef" :inline="true" @keyup.enter="getDataList">
<el-form-item label="姓名" prop="name">
<el-input placeholder="请输入姓名" v-model="state.queryForm.name" />
</el-form-item>
<el-form-item>
<el-button icon="search" type="primary" @click="getDataList"> 查询 </el-button>
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
<el-button @click="selectAllPersonnel" v-if="props.multiple">
<SvgIcon name="local-all-personnel" :size="16" style="margin-right: 15px" /> 选中全部人员</el-button
>
</el-form-item>
</el-form>
</div>
<el-table
v-if="props.multiple"
ref="multipleTableRef"
:data="state.dataList"
style="width: 100%"
highlight-current-row
@selection-change="handleSelectionChange"
@select="onSelect"
@select-all="onSelectAll"
empty-text="请选择组织或更换组织"
>
<el-table-column type="selection" width="55" />
<el-table-column label="用户名" show-overflow-tooltip
><template #default="scope">{{ scope.row.username }}</template>
</el-table-column>
<el-table-column label="姓名" show-overflow-tooltip>
<template #default="scope">{{ scope.row.name }}</template>
</el-table-column>
</el-table>
<el-table
v-if="!props.multiple"
ref="multipleTableRef"
:data="state.dataList"
style="width: 100%"
highlight-current-row
empty-text="请选择组织或更换组织"
@current-change="handleCurrentChange"
>
<el-table-column label="用户名" show-overflow-tooltip
><template #default="scope">{{ scope.row.username }}</template>
</el-table-column>
<el-table-column label="姓名" show-overflow-tooltip>
<template #default="scope">{{ scope.row.name }}</template>
</el-table-column>
</el-table>
<pagination @size-change="sizeChangeHandle" @current-change="currentChangeHandle" v-bind="state.pagination" />
</div>
<div class="PersonneInfo">
<div class="header">
<span>已选人员({{ selectionPersonnel.length }})</span>
<span @click="clearPersonneInfo">清空</span>
</div>
<ul>
<li v-for="(item, index) in selectionPersonnel" :key="index">
{{ item?.name }}<el-icon class="icon" @click="delPersonneInfo(item)"><Delete /></el-icon>
</li>
</ul>
</div>
</div>
<template #footer>
<div class="dialog-footer">
<el-button @click="selectPersonnelVisible = false">取消</el-button>
<el-button type="primary" @click="submit"> 确认 </el-button>
</div>
</template>
</el-dialog>
</template>
<script setup lang="ts" name="selectPersonnel">
import { BasicTableProps, useTable } from '/@/hooks/table';
import { getDepartmentTree, getSimpleUserPage, getSimpleUserList } from '/@/api/selectPersonnel/index';
import type Node from 'element-plus/es/components/tree/src/model/node';
import { Search } from '@element-plus/icons-vue';
const selectPersonnelInfo = ref([]) as any;
const selectPersonnelVisible = ref(false);
const chooseProjectName = ref();
const multipleTableRef = ref();
const queryRef = ref();
const departmentName = ref('');
const treeRef = ref();
const personnel = ref([]) as any;
const emit = defineEmits(['update:orgList', 'change']);
const treeFlag = ref(true);
const props = defineProps({
multiple: {
type: Boolean,
default: () => false,
},
orgList: {
type: Array,
default: () => [],
},
placeholder: {
type: String,
default: '请选择人员',
},
isbutton: {
type: Boolean,
detalut: () => false,
},
disabled: {
type: Boolean,
default: () => false,
},
});
interface Tree {
name: string;
leaf?: boolean;
}
const state: BasicTableProps = reactive<BasicTableProps>({
queryForm: {},
pageList: getSimpleUserPage,
});
// table hook
const { currentChangeHandle, sizeChangeHandle, getDataList } = useTable(state);
const prop = {
label: 'name',
// children: 'children',
isLeaf: 'leaf',
};
// 选中全部人员
const selectAllPersonnel = async () => {
let res = await getSimpleUserList(state.queryForm);
selectionPersonnel.value = res.data;
if (selectionPersonnel.value.length) {
state.dataList?.forEach((el: any) => {
if (selectionPersonnel.value.find((item: any) => item.id === el.id)) {
nextTick(() => {
multipleTableRef.value?.toggleRowSelection(el, true);
});
}
});
}
};
// 多选
const multipleSelection = ref([]) as any;
// 人员
const selectionPersonnel = ref([]) as any;
const handleSelectionChange = (val: any) => {
multipleSelection.value = val;
// 去重
val.forEach((el: any) => {
if (!selectionPersonnel.value.find((item: any) => item.id === el.id)) {
selectionPersonnel.value.push(el);
}
});
};
const onSelect = (rows: any, row: any) => {
// selected true就是选中,0或者false是取消选中
let selected = rows.length && rows.indexOf(row) !== -1;
if (!selected) {
selectionPersonnel.value.forEach((item: { id: '' }, i: number) => {
if (row?.id == item.id) {
nextTick(() => {
selectionPersonnel.value.splice(i, 1);
});
}
});
}
};
const onSelectAll = (rows: any) => {
if (!rows.length) {
for (let index = 0; index < selectionPersonnel.value.length; index++) {
const element = selectionPersonnel.value[index];
if (state.dataList?.find((el: any) => el.id === element.id)) {
selectionPersonnel.value.splice(index, 1);
index--;
}
}
}
};
// 单选
const handleCurrentChange = (row: any) => {
multipleSelection.value = [row];
// 去重
if (!selectionPersonnel.value.find((item: any) => item?.id === row?.id)) {
selectionPersonnel.value = [row];
}
};
// 阻止下拉框 下拉事件
const handleTestTasks = () => {
chooseProjectName.value.blur();
};
// 清空搜索条件
const resetQuery = () => {
// 清空搜索条件
queryRef.value?.resetFields();
getDataList();
};
// 删除
const delPersonneInfo = (row: { id: '' }) => {
state.dataList?.forEach((item: { id: '' }) => {
if (row?.id == item.id) {
nextTick(() => {
multipleTableRef.value!.toggleRowSelection(item, false);
});
return;
}
});
selectionPersonnel.value.forEach((item: { id: '' }, i: number) => {
if (row?.id == item.id) {
selectionPersonnel.value.splice(i, 1);
return;
}
});
};
// 清空
const clearPersonneInfo = () => {
nextTick(() => {
multipleTableRef.value?.clearSelection();
});
selectionPersonnel.value = [];
};
// select 回显
watch(
() => props.orgList,
() => {
personnel.value = [];
selectPersonnelInfo.value = [];
multipleSelection.value = [];
// 多选回显
if (props.orgList.length) {
multipleSelection.value = props.orgList;
selectionPersonnel.value = props.orgList;
personnel.value = props.orgList;
selectPersonnelInfo.value = multipleSelection.value.map((item: any) => item?.id);
} else {
selectionPersonnel.value = [];
}
},
{ deep: true, immediate: true }
);
// 选中状态回显
watch(
() => selectPersonnelVisible.value,
() => {
nextTick(() => {
multipleTableRef.value?.clearSelection();
});
if (!selectPersonnelVisible.value) return;
if (props.orgList.length) {
nextTick(() => {
state.dataList?.forEach((item: any) => {
if (selectPersonnelInfo.value.includes(item.id)) {
multipleTableRef.value?.toggleRowSelection(item, true);
}
});
});
} else {
nextTick(() => {
multipleTableRef.value?.clearSelection();
});
}
}
);
// 切换回显
watch(
() => state.dataList,
() => {
if (!props.multiple) return;
if (selectionPersonnel.value.length) {
state.dataList?.forEach((el: any) => {
if (selectionPersonnel.value.find((item: any) => item.id === el.id)) {
nextTick(() => {
multipleTableRef.value?.toggleRowSelection(el, true);
});
}
});
}
},
{ immediate: true }
);
const visibleType = () => {
// 弹框
nextTick(() => {
// 不显示下拉框
chooseProjectName.value.blur();
selectPersonnelVisible.value = true;
});
};
const departmentTree = ref();
onBeforeMount(() => {
selectionPersonnel.value = [];
getDepartment();
});
// 输入查询
let time: any;
watch(
() => departmentName.value,
() => {
if (time) {
clearTimeout(time);
time = setTimeout(() => {
getDepartment();
}, 500);
} else {
time = setTimeout(() => {
getDepartment();
}, 500);
}
}
);
// 获取部门
const getDepartment = () => {
treeFlag.value = false;
getDepartmentTree({ parentId: -1, deptName: departmentName.value }).then((res) => {
if (departmentName.value) {
departmentTree.value = res.data.map((item: any) => {
item.leaf = item.children ? false : true;
return item;
});
treeFlag.value = true;
} else {
res.data.unshift({
createTime: '',
id: '',
isLock: false,
name: '全部人员',
parentId: '',
weight: 9999,
deptType: 0,
leaf: true,
});
departmentTree.value = res.data.map((item: any) => {
item.leaf = item.children ? false : true;
return item;
});
treeFlag.value = true;
}
});
};
// 选择部门
const checkChangeTree = (data: any) => {
state.queryForm.deptId = data.id;
getDataList();
};
// 节点懒加载
const loadNode = async (node: Node, resolve: (data: Tree[]) => void, reject: () => void) => {
if (node.level === 0) {
resolve(departmentTree.value);
nextTick(() => {
// 展开子节点
let node = treeRef.value.getNode(departmentTree.value[1]);
node.expand();
// node.expand(function () {
// for (let i = 0; i < node.childNodes.length; i++) {
// console.log(node.childNodes[i], '子节点');
// if (node.childNodes[i].data.children) {
// node.childNodes[i].expand();
// }
// }
// });
});
return;
}
if (node.level >= 1) {
try {
let res = await getDepartmentTree({ parentId: node.data.id });
res.data.forEach((element: any) => {
element.leaf = element.children ? false : true;
});
return resolve(res.data);
} catch (error) {
reject();
}
}
};
// 确认
const submit = () => {
if (props.isbutton) {
emit('change', selectionPersonnel.value);
selectPersonnelVisible.value = false;
return;
}
emit('update:orgList', selectionPersonnel.value);
emit('change', selectionPersonnel.value);
personnel.value = selectionPersonnel.value;
selectPersonnelInfo.value = selectionPersonnel.value.map((item: any) => item?.id);
selectPersonnelVisible.value = false;
};
</script>
<style lang="scss" scoped>
:deep(.el-input__wrapper) {
box-shadow: 0 0 0 1px #dcdfe6 !important;
}
:deep(.el-tooltip__trigger) {
box-shadow: 0 0 0 1px #dcdfe6 !important;
}
:deep(.el-tag__close) {
display: none;
}
.tree-icon {
width: 20px;
height: 20px;
display: inline-block;
margin-right: 8px;
}
.selectPersonnelContent {
width: 100%;
height: 700px;
display: flex;
.PersonneTree {
width: 350px;
height: 100%;
border: 1px solid #eee;
border-radius: 10px;
padding: 6px;
overflow: hidden;
overflow-y: auto;
margin-right: 15px;
.header {
margin-bottom: 10px;
}
}
.PersonneTable {
flex: 1;
height: 100%;
overflow: hidden;
overflow-y: auto;
border: 1px solid #eee;
padding: 6px;
border-radius: 10px;
margin-right: 15px;
.header {
margin: 10px 8px 18px;
}
}
.PersonneInfo {
padding: 10px;
width: 350px;
border: 1px solid #eee;
padding: 6px;
border-radius: 10px;
.header {
padding: 0 6px;
height: 30px;
border-bottom: 1px solid #eee;
display: flex;
align-items: center;
justify-content: space-between;
span:nth-child(2) {
color: rgb(183, 108, 108);
cursor: pointer;
}
}
ul {
height: calc(100% - 30px);
overflow: hidden;
overflow-y: auto;
li {
height: 25px;
display: flex;
align-items: center;
justify-content: space-between;
border: 1px solid #eee;
border-radius: 6px;
padding: 0 6px;
margin: 5px 0;
.icon {
color: red;
cursor: pointer;
}
}
li:hover {
background-color: #eee;
}
}
}
}
</style>
<style lang="scss">
// 重新修改tree组件样
.tree-line {
.el-tree-node {
position: relative;
padding-left: 16px;
}
.el-tree-node__content {
margin-top: 10px;
}
.el-tree-node__children {
padding-left: 16px;
}
.el-tree-node::before {
content: '';
height: 105%;
width: 1px;
position: absolute;
left: -3px;
top: -26px;
border-width: 1px;
border-left: 1px dashed #52627c;
}
.el-tree-node::after {
content: '';
width: 24px;
height: 20px;
position: absolute;
left: -3px;
top: 12px;
border-top: 1px dashed #52627c;
}
& > .el-tree-node::after {
border-top: none;
}
& > .el-tree-node::before {
border-left: none;
border-width: 0px;
}
.el-tree-node__expand-icon {
font-size: 18px;
color: #000;
&.is-leaf {
color: transparent;
// display: none;
}
}
}
</style>
四、二次优化
效果:
1.单选(利用复选框实现单选功能)
2.多选
3.完整代码
<template>
<template v-if="props.isbutton">
<span>
<slot></slot>
</span>
</template>
<el-select
:disabled="disabled"
v-if="!props.isbutton"
ref="chooseProjectName"
v-model="selectPersonnelInfo"
multiple
collapse-tags
:placeholder="props.placeholder"
:style="width"
@focus="handleTestTasks"
@visible-change="visibleType"
suffix-icon="MoreFilled"
>
<el-option v-for="item in personnel" :key="item.id" :label="item?.name" :value="item?.id" />
</el-select>
<el-dialog v-model="selectPersonnelVisible" append-to-body title="选择" width="60%" draggable>
<div class="selectPersonnelContent">
<div class="PersonneTree">
<div class="header">
<el-input v-model="departmentName" placeholder="请输入部门名称" :suffix-icon="Search" clearable maxlength="30" show-word-limit />
</div>
<el-tree
class="tree-line"
ref="treeRef"
v-if="treeFlag"
highlight-current
style="max-width: 600px"
:expand-on-click-node="false"
:props="prop"
:load="loadNode"
:indent="0"
lazy
@current-change="checkChangeTree"
><template #default="{ data }">
<span class="custom-tree-node">
<span class="tree-icon">
<SvgIcon v-if="data.deptType === 0" name="local-all-personnel" :size="16" />
<SvgIcon v-if="data.deptType === 1" name="local-dept3" :size="16" />
<SvgIcon v-if="data.deptType === 2" name="local-company" :size="16" />
<SvgIcon v-if="data.deptType === 3" name="local-construction-team2" :size="16" />
</span>
<span>{{ data.name }}</span>
</span>
</template>
</el-tree>
</div>
<div class="PersonneTable">
<div class="header">
<el-form :model="state.queryForm" ref="queryRef" :inline="true" @keyup.enter="getDataList">
<el-form-item label="姓名" prop="name">
<el-input placeholder="请输入姓名" v-model="state.queryForm.name" clearable maxlength="30" show-word-limit />
</el-form-item>
<el-form-item>
<el-button icon="search" type="primary" @click="getDataList"> 查询 </el-button>
<!-- <el-button icon="Refresh" @click="resetQuery">重置</el-button> -->
<el-button @click="selectAllPersonnel" v-if="props.multiple">
<SvgIcon name="local-all-personnel" :size="16" style="margin-right: 15px" /> 选中全部人员</el-button
>
</el-form-item>
</el-form>
</div>
<el-table
ref="multipleTableRef"
:data="state.dataList"
style="width: 100%"
@row-click="rowClick"
@selection-change="handleSelectionChange"
@select="onSelect"
@select-all="onSelectAll"
empty-text="请选择组织或更换组织"
:class="!props.multiple ? 'hide-selection' : ''"
>
<el-table-column type="selection" width="55" />
<el-table-column label="用户名" show-overflow-tooltip
><template #default="scope">{{ scope.row.username }}</template>
</el-table-column>
<el-table-column label="姓名" show-overflow-tooltip>
<template #default="scope">{{ scope.row.name }}</template>
</el-table-column>
<el-table-column label="手机号" show-overflow-tooltip>
<template #default="scope">{{ scope.row.phone }}</template>
</el-table-column>
</el-table>
<pagination @size-change="sizeChangeHandle" @current-change="currentChangeHandle" v-bind="state.pagination" />
</div>
<div class="PersonneInfo">
<div class="header">
<span>已选人员({{ selectionPersonnel.length }})</span>
<span @click="clearPersonneInfo">清空</span>
</div>
<ul>
<li v-for="(item, index) in selectionPersonnel" :key="index">
{{ item?.name }}<el-icon class="icon" @click="delPersonneInfo(item)"><Delete /></el-icon>
</li>
</ul>
</div>
</div>
<template #footer>
<div class="dialog-footer">
<el-button @click="selectPersonnelVisible = false">取消</el-button>
<el-button type="primary" @click="submit"> 确认 </el-button>
</div>
</template>
</el-dialog>
</template>
<script setup lang="ts" name="selectPersonnel">
import { BasicTableProps, useTable } from '/@/hooks/table';
import { getDepartmentTree, getSimpleUserPage, getSimpleUserByName } from '../../api/components/selectPersonnel/index';
import type Node from 'element-plus/es/components/tree/src/model/node';
import { Search } from '@element-plus/icons-vue';
const selectPersonnelInfo = ref([]) as any;
const selectPersonnelVisible = ref(false);
const chooseProjectName = ref();
const multipleTableRef = ref();
const queryRef = ref();
const departmentName = ref('');
const treeRef = ref();
const personnel = ref([]) as any;
const emit = defineEmits(['update:orgList', 'change']);
const treeFlag = ref(true);
const props = defineProps({
multiple: {
type: Boolean,
default: () => false,
},
orgList: {
type: Array,
default: () => [],
},
placeholder: {
type: String,
default: '请选择人员',
},
isbutton: {
type: Boolean,
detalut: () => false,
},
disabled: {
type: Boolean,
default: () => false,
},
source: {
type: String,
default: '',
},
width: {
type: Object,
default: () => ({ width: '240px' }),
},
});
interface Tree {
name: string;
leaf?: boolean;
}
const state: BasicTableProps = reactive<BasicTableProps>({
queryForm: {
source: props.source,
},
pageList: getSimpleUserPage,
});
// table hook
const { currentChangeHandle, sizeChangeHandle, getDataList } = useTable(state);
const prop = {
label: 'name',
// children: 'children',
isLeaf: 'leaf',
};
// 点击行选中
const rowClick = (row: any) => {
if (!selectionPersonnel.value.find((item: any) => item.id === row.id)) {
nextTick(() => {
multipleTableRef.value?.toggleRowSelection(row, true);
});
}
};
// 选中全部人员
const selectAllPersonnel = async () => {
let res = await getSimpleUserByName(state.queryForm);
selectionPersonnel.value = res.data;
if (selectionPersonnel.value.length) {
state.dataList?.forEach((el: any) => {
if (selectionPersonnel.value.find((item: any) => item.id === el.id)) {
nextTick(() => {
multipleTableRef.value?.toggleRowSelection(el, true);
});
}
});
}
};
// 多选
const multipleSelection = ref([]) as any;
// 人员
const selectionPersonnel = ref([]) as any;
const handleSelectionChange = (val: any) => {
if (props.multiple) {
multipleSelection.value = val;
// 去重
val.forEach((el: any) => {
if (!selectionPersonnel.value.find((item: any) => item.id === el.id)) {
selectionPersonnel.value.push(el);
}
});
} else {
// multipleSelection.value = val[val.length - 1];
if (!val.length) return;
selectionPersonnel.value = [val[val.length - 1]];
if (selectionPersonnel.value.length) {
state.dataList?.forEach((el: any) => {
if (selectionPersonnel.value.find((item: any) => item.id === el.id)) {
nextTick(() => {
multipleTableRef.value?.toggleRowSelection(el, true);
});
} else {
nextTick(() => {
multipleTableRef.value?.toggleRowSelection(el, false);
});
}
});
}
}
};
const onSelect = (rows: any, row: any) => {
// selected true就是选中,0或者false是取消选中
let selected = rows.length && rows.indexOf(row) !== -1;
if (!selected) {
selectionPersonnel.value.forEach((item: { id: '' }, i: number) => {
if (row?.id == item.id) {
nextTick(() => {
selectionPersonnel.value.splice(i, 1);
});
}
});
}
};
const onSelectAll = (rows: any) => {
if (!rows.length) {
for (let index = 0; index < selectionPersonnel.value.length; index++) {
const element = selectionPersonnel.value[index];
if (state.dataList?.find((el: any) => el.id === element.id)) {
selectionPersonnel.value.splice(index, 1);
index--;
}
}
}
};
// 阻止下拉框 下拉事件
const handleTestTasks = () => {
chooseProjectName.value.blur();
};
// // 清空搜索条件
// const resetQuery = () => {
// // 清空搜索条件
// queryRef.value?.resetFields();
// getDataList();
// };
// 删除
const delPersonneInfo = (row: { id: '' }) => {
state.dataList?.forEach((item: { id: '' }) => {
if (row?.id == item.id) {
nextTick(() => {
multipleTableRef.value!.toggleRowSelection(item, false);
});
return;
}
});
selectionPersonnel.value.forEach((item: { id: '' }, i: number) => {
if (row?.id == item.id) {
selectionPersonnel.value.splice(i, 1);
return;
}
});
};
// 清空
const clearPersonneInfo = () => {
nextTick(() => {
multipleTableRef.value?.clearSelection();
});
selectionPersonnel.value = [];
};
// select 回显
watch(
() => props.orgList,
() => {
// 校验v-model 数据类型
if (!Array.isArray(props.orgList)) return;
personnel.value = [];
selectPersonnelInfo.value = [];
multipleSelection.value = [];
nextTick(() => {
// 多选回显
if (props.orgList.length) {
multipleSelection.value = props.orgList;
selectionPersonnel.value = props.orgList;
personnel.value = props.orgList;
selectPersonnelInfo.value = multipleSelection.value.map((item: any) => item?.id);
} else {
selectionPersonnel.value = [];
}
});
},
{ deep: true, immediate: true }
);
// 选中状态回显
watch(
() => selectPersonnelVisible.value,
() => {
nextTick(() => {
multipleTableRef.value?.clearSelection();
});
if (!selectPersonnelVisible.value) return;
if (props.orgList.length) {
nextTick(() => {
state.dataList?.forEach((item: any) => {
if (selectPersonnelInfo.value.includes(item.id)) {
multipleTableRef.value?.toggleRowSelection(item, true);
}
});
});
} else {
nextTick(() => {
selectionPersonnel.value = [];
multipleTableRef.value?.clearSelection();
let node = treeRef.value.getNode(departmentTree.value[1]);
node.expand();
});
}
}
);
// 切换回显
watch(
() => state.dataList,
() => {
if (!props.multiple) return;
if (selectionPersonnel.value.length) {
state.dataList?.forEach((el: any) => {
if (selectionPersonnel.value.find((item: any) => item.id === el.id)) {
nextTick(() => {
multipleTableRef.value?.toggleRowSelection(el, true);
});
}
});
}
},
{ immediate: true }
);
const visibleType = () => {
// 弹框
nextTick(() => {
// 不显示下拉框
chooseProjectName.value.blur();
selectPersonnelVisible.value = true;
});
};
const departmentTree = ref();
onBeforeMount(() => {
selectionPersonnel.value = [];
getDepartment();
});
// 输入查询
let time: any;
watch(
() => departmentName.value,
() => {
if (time) {
clearTimeout(time);
time = setTimeout(() => {
getDepartment();
}, 500);
} else {
time = setTimeout(() => {
getDepartment();
}, 500);
}
}
);
// 获取部门
const getDepartment = () => {
treeFlag.value = false;
getDepartmentTree({ parentId: -1, deptName: departmentName.value }).then((res) => {
if (departmentName.value) {
departmentTree.value = res.data.map((item: any) => {
item.leaf = item.children ? false : true;
return item;
});
treeFlag.value = true;
} else {
res.data.unshift({
createTime: '',
id: '',
isLock: false,
name: '全部人员',
parentId: '',
weight: 9999,
deptType: 0,
leaf: true,
});
departmentTree.value = res.data.map((item: any) => {
item.leaf = item.children ? false : true;
return item;
});
treeFlag.value = true;
}
});
};
// 选择部门
const checkChangeTree = (data: any) => {
state.queryForm.deptId = data.id;
getDataList();
};
// 节点懒加载
const loadNode = async (node: Node, resolve: (data: Tree[]) => void, reject: () => void) => {
if (node.level === 0) {
resolve(departmentTree.value);
nextTick(() => {
// 展开子节点
let node = treeRef.value.getNode(departmentTree.value[1]);
node.expand();
// node.expand(function () {
// for (let i = 0; i < node.childNodes.length; i++) {
// console.log(node.childNodes[i], '子节点');
// if (node.childNodes[i].data.children) {
// node.childNodes[i].expand();
// }
// }
// });
});
return;
}
if (node.level >= 1) {
try {
let res = await getDepartmentTree({ parentId: node.data.id });
res.data.forEach((element: any) => {
element.leaf = element.children ? false : true;
});
return resolve(res.data);
} catch (error) {
reject();
}
}
};
// 确认
const submit = () => {
if (props.isbutton) {
emit('change', selectionPersonnel.value);
selectPersonnelVisible.value = false;
return;
}
emit('update:orgList', selectionPersonnel.value);
emit('change', selectionPersonnel.value);
personnel.value = selectionPersonnel.value;
selectPersonnelInfo.value = selectionPersonnel.value.map((item: any) => item?.id);
selectPersonnelVisible.value = false;
};
// 打开弹窗
const openDialog = () => {
selectPersonnelVisible.value = true;
getDataList();
};
defineExpose({ openDialog });
</script>
<style lang="scss" scoped>
:deep(.hide-selection .el-table__header-wrapper tr .el-table__cell .el-checkbox) {
display: none;
}
:deep(.el-input__wrapper) {
box-shadow: 0 0 0 1px #dcdfe6 !important;
}
:deep(.el-tooltip__trigger) {
box-shadow: 0 0 0 1px #dcdfe6 !important;
}
:deep(.el-tag__close) {
display: none;
}
.tree-icon {
width: 20px;
height: 20px;
display: inline-block;
margin-right: 8px;
}
.selectPersonnelContent {
width: 100%;
height: 700px;
display: flex;
.PersonneTree {
width: 350px;
height: 100%;
border: 1px solid #eee;
border-radius: 10px;
padding: 6px;
overflow: hidden;
overflow-y: auto;
margin-right: 15px;
.header {
margin-bottom: 10px;
}
}
.PersonneTable {
flex: 1;
height: 100%;
overflow: hidden;
overflow-y: auto;
border: 1px solid #eee;
padding: 6px;
border-radius: 10px;
margin-right: 15px;
.header {
margin: 10px 8px 18px;
}
}
.PersonneInfo {
padding: 10px;
width: 350px;
border: 1px solid #eee;
padding: 6px;
border-radius: 10px;
.header {
padding: 0 6px;
height: 30px;
border-bottom: 1px solid #eee;
display: flex;
align-items: center;
justify-content: space-between;
span:nth-child(2) {
color: rgb(183, 108, 108);
cursor: pointer;
}
}
ul {
height: calc(100% - 30px);
overflow: hidden;
overflow-y: auto;
li {
height: 25px;
display: flex;
align-items: center;
justify-content: space-between;
border: 1px solid #eee;
border-radius: 6px;
padding: 0 6px;
margin: 5px 0;
.icon {
color: red;
cursor: pointer;
}
}
li:hover {
background-color: #eee;
}
}
}
}
</style>