一 选择组件
效果:
1.单选
2.多选
<template>
<el-select
:disabled="disabled"
ref="chooseProjectName"
v-model="selectDept"
multiple
collapse-tags
:placeholder="props.placeholder"
@focus="handleTestTasks"
@visible-change="visibleType"
suffix-icon="MoreFilled"
>
<el-option v-for="item in deptList" :key="item.id" :label="item?.name" :value="item?.id" />
</el-select>
<el-dialog v-model="selectDeptVisible" append-to-body title="选择" width="1200" draggable>
<div class="selectPersonnelContent">
<div class="PersonneTree">
<div class="header">
<el-input v-model="searchName" placeholder="请输入施工段" :suffix-icon="Search" clearable maxlength="30" show-word-limit />
</div>
<div class="content">
<el-tree
ref="treeRef"
class="tree-line"
:indent="0"
:data="treeArray"
show-checkbox
check-strictly
:expand-on-click-node="false"
:default-expanded-keys="defaultExpandedKeys"
node-key="id"
@check="handleCheck"
>
<template #default="{ data }">
<span class="custom-tree-node">
<span class="tree-icon">
<SvgIcon name="local-work-area" :size="16" />
</span>
<span>{{ data.name }}</span>
</span>
</template>
</el-tree>
</div>
</div>
<div class="PersonneInfo">
<div class="header">
<span>已选施工段({{ selectionDept.length }})</span>
<span @click="clearPersonneInfo">清空</span>
</div>
<ul>
<li v-for="(item, index) in selectionDept" :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="selectDeptVisible = false">取消</el-button>
<el-button type="primary" @click="submit"> 确认 </el-button>
</div>
</template>
</el-dialog>
</template>
<script setup lang="ts" name="selectPersonnel">
const selectDept = ref([]) as any;
const chooseProjectName = ref();
const searchName = ref('');
const treeRef = ref();
const deptList = ref([]) as any;
const treeArray = ref([]) as any;
const selectionDept = ref([]) as any;
const selectDeptVisible = ref(false);
const defaultExpandedKeys = ref([]) as any;
import { getProjectTreeListWithoutAuth, getProjectTreeListList } from '../../api/components/selectProject';
import { Search } from '@element-plus/icons-vue';
const emits = defineEmits(['update:modelValue']);
const props = defineProps({
multiple: {
type: Boolean,
default: false,
},
modelValue: {
type: Array,
default: () => [],
},
placeholder: {
type: String,
default: '请选择所属施工段',
},
disabled: {
type: Boolean,
default: false,
},
all: {
required: false,
type: Boolean,
default: false,
},
type: {
required: false,
type: String,
default: '',
},
});
// 阻止下拉框 下拉事件
const handleTestTasks = () => {
chooseProjectName.value.blur();
};
// 删除
const delPersonneInfo = (row: { id: '' }) => {
treeRef.value.setCheckedKeys(
selectionDept.value.filter((item: any) => item.id != row.id).map((item: any) => item.id),
true
);
selectionDept.value.forEach((item: { id: '' }, i: number) => {
if (row?.id == item.id) {
selectionDept.value.splice(i, 1);
return;
}
});
};
// 清空
const clearPersonneInfo = () => {
nextTick(() => {
treeRef.value.setCheckedKeys([], true);
});
selectionDept.value = [];
};
// select 回显
watch(
() => props.modelValue,
() => {
// 校验v-model 数据类型
if (!Array.isArray(props.modelValue)) return;
deptList.value = props.modelValue;
selectDept.value = props.modelValue?.map((item: any) => item.id);
},
{ deep: true, immediate: true }
);
// 选中状态回显
watch(
() => selectDeptVisible.value,
() => {
if (!selectDeptVisible.value) return;
nextTick(() => {
selectionDept.value = props.modelValue;
treeRef.value!.setCheckedKeys(selectDept.value, true);
defaultExpandedKeys.value = treeArray.value?.map((item: any) => item.id);
});
}
);
const visibleType = () => {
// 弹框
nextTick(() => {
// 不显示下拉框
chooseProjectName.value.blur();
selectDeptVisible.value = true;
});
};
onBeforeMount(async () => {
getProjectTree();
});
// 获取数据
const getProjectTree = async () => {
if (props.all) {
let res = await getProjectTreeListWithoutAuth({ name: searchName.value, isTree: true, type: props.type });
treeArray.value = res.data;
} else {
let res = await getProjectTreeListList({ name: searchName.value, isTree: true, type: props.type });
treeArray.value = res.data;
}
};
// 输入查询
let time: any;
watch(
() => searchName.value,
() => {
if (time) {
clearTimeout(time);
time = setTimeout(() => {
getProjectTree();
}, 500);
} else {
time = setTimeout(() => {
getProjectTree();
}, 500);
}
}
);
// 多选
const handleCheck = (row: any) => {
if (!props.multiple) {
//实现单选关键就是这一点:在选中某个项之后,将选中的节点数组只设置成当前选中的节点,保证只有一个选项
treeRef.value.setCheckedKeys([row.id]);
deptList.value = treeRef.value!.getCheckedNodes();
let arr = JSON.parse(JSON.stringify(deptList.value));
selectionDept.value = arr;
} else {
deptList.value = treeRef.value!.getCheckedNodes();
let arr = JSON.parse(JSON.stringify(deptList.value));
selectionDept.value = arr;
}
nextTick(() => {
const nodes = treeRef.value.store._getAllNodes();
for (let i = 0; i < nodes.length; i++) {
if (row.id == nodes[i].data.id) {
nodes[i].expanded = true;
break;
}
}
});
};
// 确认
const submit = () => {
deptList.value = treeRef.value!.getCheckedNodes();
selectDept.value = treeRef.value!.getCheckedKeys();
let arr = JSON.parse(JSON.stringify(deptList.value));
emits(
'update:modelValue',
arr.map((el: any) => {
delete el.children;
return el;
})
);
selectDeptVisible.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 {
flex: 1;
height: 100%;
border: 1px solid #eee;
border-radius: 10px;
padding: 6px;
overflow: hidden;
overflow-y: auto;
margin-right: 15px;
.header {
margin-bottom: 10px;
}
.content {
height: calc(100% - 45px);
overflow: hidden;
overflow-y: auto;
}
}
.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>