|
@@ -0,0 +1,204 @@
|
|
|
+<template>
|
|
|
+ <el-dialog v-model="visible" title="选择消息接收人" width="780px" append-to-body @close="closeDialog">
|
|
|
+ <div class="container">
|
|
|
+ <div class="left-content">
|
|
|
+ <div>人员选择列表</div>
|
|
|
+ <el-input v-model="filterText" placeholder="输入关键字进行搜索" class="input" />
|
|
|
+ <el-tree
|
|
|
+ ref="treeRef"
|
|
|
+ class="filter-tree"
|
|
|
+ :data="treeData"
|
|
|
+ show-checkbox
|
|
|
+ :props="defaultProps"
|
|
|
+ :filter-node-method="filterNode"
|
|
|
+ :default-checked-keys="defaultCheckData"
|
|
|
+ node-key="id"
|
|
|
+ @check-change="handleCheckChange"
|
|
|
+ >
|
|
|
+ <template #default="{ node, data }">
|
|
|
+ <div class="custom-tree-node">
|
|
|
+ <img class="icon" :src="getImage(data)" alt="" />
|
|
|
+ <span>{{ node.label }}</span>
|
|
|
+ </div>
|
|
|
+ </template>
|
|
|
+ </el-tree>
|
|
|
+ </div>
|
|
|
+ <div class="right-content">
|
|
|
+ <div style="display: flex">
|
|
|
+ 已选择:
|
|
|
+ <div v-show="checkData.length > 0">{{ checkData.length }}人</div>
|
|
|
+ </div>
|
|
|
+ <div class="select-box">
|
|
|
+ <div v-for="(item, index) in checkData" :key="index" class="select-item">
|
|
|
+ <div class="item-left">
|
|
|
+ <img class="icon" :src="getImage(item)" alt="" />
|
|
|
+ {{ item.label }}
|
|
|
+ </div>
|
|
|
+ <i class="icon-close" @click="uncheckNodeById(item.userId, index)" />
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <template #footer>
|
|
|
+ <div class="dialog-footer">
|
|
|
+ <el-button @click="closeDialog">取 消</el-button>
|
|
|
+ <el-button type="primary" @click="confirm">确 定</el-button>
|
|
|
+ </div>
|
|
|
+ </template>
|
|
|
+ </el-dialog>
|
|
|
+</template>
|
|
|
+
|
|
|
+<script setup name="Contact">
|
|
|
+import { deepClone } from '@/utils/index';
|
|
|
+import userImg from '@/assets/images/user.png';
|
|
|
+import fileImg from '@/assets/images/file.png';
|
|
|
+
|
|
|
+const props = defineProps({
|
|
|
+ modelValue: Boolean,
|
|
|
+ treeData: Array,
|
|
|
+ defaultCheckData: Array
|
|
|
+});
|
|
|
+const emits = defineEmits(['update:modelValue', 'confirm']);
|
|
|
+let visible = ref(false);
|
|
|
+const treeRef = ref();
|
|
|
+let filterText = ref('');
|
|
|
+let defaultProps = reactive({
|
|
|
+ children: 'children',
|
|
|
+ label: 'label'
|
|
|
+});
|
|
|
+let checkData = ref([]);
|
|
|
+
|
|
|
+watch(filterText, (val) => {
|
|
|
+ treeRef.value.filter(val);
|
|
|
+});
|
|
|
+const initData = () => {
|
|
|
+ if (!!props.defaultCheckData) {
|
|
|
+ props.defaultCheckData.forEach((item) => {
|
|
|
+ treeRef.value.setChecked(item.id, true, true);
|
|
|
+ });
|
|
|
+ checkData.value = deepClone(props.defaultCheckData);
|
|
|
+ }
|
|
|
+};
|
|
|
+watch(
|
|
|
+ () => props.modelValue,
|
|
|
+ () => {
|
|
|
+ if (!!props.modelValue) {
|
|
|
+ initData();
|
|
|
+ }
|
|
|
+ visible.value = props.modelValue;
|
|
|
+ },
|
|
|
+ {
|
|
|
+ immediate: true
|
|
|
+ }
|
|
|
+);
|
|
|
+const filterNode = (value, data) => {
|
|
|
+ if (!value) return true;
|
|
|
+ return data.label.indexOf(value) !== -1;
|
|
|
+};
|
|
|
+const getImage = (item) => {
|
|
|
+ if (item.img) {
|
|
|
+ return item.img;
|
|
|
+ } else if (item.deptType) {
|
|
|
+ return fileImg;
|
|
|
+ } else {
|
|
|
+ return userImg;
|
|
|
+ }
|
|
|
+};
|
|
|
+const handleCheckChange = () => {
|
|
|
+ const data = [];
|
|
|
+ const treeData = treeRef.value.getCheckedNodes(false, false);
|
|
|
+ treeData.forEach((item) => {
|
|
|
+ if (!item.deptType) {
|
|
|
+ if (!data.some((obj) => obj.userId === item.userId)) {
|
|
|
+ data.push(item);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ });
|
|
|
+ checkData.value = data;
|
|
|
+};
|
|
|
+const uncheckNodeById = (nodeId, index) => {
|
|
|
+ const nodes = treeRef.value.getCheckedNodes();
|
|
|
+ checkData.value.splice(index, 1);
|
|
|
+ nodes.forEach((item) => {
|
|
|
+ if (item.userId === nodeId) {
|
|
|
+ treeRef.value.setChecked(item.id, false, true);
|
|
|
+ }
|
|
|
+ });
|
|
|
+};
|
|
|
+const closeDialog = () => {
|
|
|
+ emits('update:modelValue', false);
|
|
|
+ treeRef.value.setCheckedKeys([], false);
|
|
|
+ checkData.value = [];
|
|
|
+};
|
|
|
+const confirm = () => {
|
|
|
+ const data = deepClone(checkData.value);
|
|
|
+ closeDialog();
|
|
|
+ emits('confirm', data);
|
|
|
+};
|
|
|
+</script>
|
|
|
+
|
|
|
+<style lang="scss" scoped>
|
|
|
+.container {
|
|
|
+ display: flex;
|
|
|
+ .left-content {
|
|
|
+ flex: 1;
|
|
|
+ border: 1px solid #dcdfe6;
|
|
|
+ border-radius: 5px;
|
|
|
+ height: 400px;
|
|
|
+ overflow-y: auto;
|
|
|
+ padding: 10px;
|
|
|
+ .input {
|
|
|
+ margin: 15px 0 5px;
|
|
|
+ }
|
|
|
+ .filter-tree {
|
|
|
+ :deep(.el-tree-node__content) {
|
|
|
+ height: 45px;
|
|
|
+ }
|
|
|
+ .custom-tree-node {
|
|
|
+ display: flex;
|
|
|
+ width: 100%;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ .right-content {
|
|
|
+ margin-left: 20px;
|
|
|
+ width: 300px;
|
|
|
+ flex-shrink: 0;
|
|
|
+ border: 1px solid #dcdfe6;
|
|
|
+ border-radius: 5px;
|
|
|
+ height: 400px;
|
|
|
+ overflow-y: auto;
|
|
|
+ padding: 10px;
|
|
|
+ .select-box {
|
|
|
+ .select-item {
|
|
|
+ width: 100%;
|
|
|
+ display: flex;
|
|
|
+ justify-content: space-between;
|
|
|
+ align-items: center;
|
|
|
+ padding: 10px 0;
|
|
|
+ .item-left {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ }
|
|
|
+ .icon-close {
|
|
|
+ width: 12px;
|
|
|
+ height: 12px;
|
|
|
+ background: url('@/assets/images/close2.png') no-repeat;
|
|
|
+ background-size: 100% 100%;
|
|
|
+ cursor: pointer;
|
|
|
+ &:hover {
|
|
|
+ background: url('@/assets/images/close3.png') no-repeat;
|
|
|
+ background-size: 100% 100%;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+.icon {
|
|
|
+ width: 20px;
|
|
|
+ height: 20px;
|
|
|
+ margin-right: 8px;
|
|
|
+ object-fit: cover;
|
|
|
+}
|
|
|
+</style>
|