<!-- eslint-disable vue/v-on-event-hyphenation --> <template> <div> <div class="search-input"> <a-input v-model:value="searchValue" :placeholder="placeholder" @pressEnter="onSearch" :bordered="false" /> <ns-icon class="iconfont" style="margin: auto" name="search" /> </div> <div class="checkbox-content"> <p class="selectLevel"> <span v-for="(item, index) in selectLevel" :key="index" @click="checkSelectLevel(item, index)"> <span v-if="index != 0">></span> <span :class="index == selectLevel.length - 1 ? 'selectLevel-last-span' : 'selectLevel-span'" >{{ item.name }}</span > </span> </p> <div class="candidate-content" v-if="!ifShowSearchMode"> <div style="padding: 4px 0px" v-if="filterMultiple"> <a-checkbox :indeterminate="state.indeterminate" v-model:checked="state.checkAll" @change="onCheckAllChange"> 全选 </a-checkbox> </div> <div v-for="(item, index) in candidateValues" :key="index" class="checkbox"> <a-checkbox v-if="filterMultiple" v-model:checked="item.checked" :disabled="functionType == 'component'" @change="onCheckChange($event, item)" >{{ item.name }}</a-checkbox > <a-radio v-else v-model:checked="item.checked" :disabled="searchType == 'account'" @click="onCheckChangeRadio($event, item)" >{{ item.name }}</a-radio > <span @click="checkSubordinate(item)" :class=" item.subordinateDisable ? 'checkbox-subordinate-disable' : 'checkbox-subordinate' "> <ns-icon class="iconfont" style="margin: auto; width: 12px; height: 12px" :name="item.subordinateDisable ? 'subordinate-dis' : 'subordinate'" /> 下级</span > </div> <div v-for="(item, index) in candidatePeopleValues" :key="index" class="checkbox"> <a-checkbox v-if="filterMultiple" v-model:checked="item.checked" @change="onCheckPeopleChange($event, item)"> <ns-icon class="iconfont" style="margin: auto; width: 12px; height: 12px" name="chengyuan" /> {{ item.accountName }}</a-checkbox > <a-radio v-else v-model:checked="item.checked" @click="onCheckPeopleChangeRadio($event, item, index)" >{{ item.accountName }}</a-radio > </div> </div> <div class="candidate-content-search" v-else> <div v-if="candidateValues.length > 0"> <p class="searchValue-title"> 部门 </p> <div v-for="(item, index) in candidateValues" :key="index" class="checkbox"> <a-checkbox v-if="filterMultiple" v-model:checked="item.checked" :disabled="functionType == 'component'" @change="onCheckChange($event, item)" >{{ item.name }}</a-checkbox > <a-radio v-else v-model:checked="item.checked" @change="onCheckChangeRadio($event, item)" >{{ item.name }}</a-radio > </div> </div> <div v-if="candidatePeopleValues.length > 0"> <p class="searchValue-title"> 人员 </p> <div v-for="(item, index) in candidatePeopleValues" :key="index" class="checkbox"> <a-checkbox v-if="filterMultiple" v-model:checked="item.checked" @change="onCheckPeopleChange($event, item)"> <ns-icon class="iconfont" style="margin: auto; width: 12px; height: 12px" name="chengyuan" /> {{ item.accountName }}</a-checkbox > <a-radio v-else v-model:checked="item.checked" @click="onCheckPeopleChangeRadio($event, item, index)" >{{ item.accountName }}</a-radio > </div> </div> <div v-if="candidateValues.length <= 0 && candidatePeopleValues.length <= 0"> <a-result title="暂无相关部门/人员"> <template #icon> <ns-icon class="iconfont" style="margin: auto; width: 120px; height: 120px" name="noSearchData" /> </template> </a-result> </div> </div> </div> </div> </template> <script lang="ts"> import { computed, inject, reactive } from 'vue'; import { watch } from 'vue'; import { defineComponent, ref, onMounted } from 'vue'; import { http } from '../../util/http'; import { remove } from 'lodash-es'; import { unref } from 'vue'; import { compareArrays } from './util'; import { Emitter } from 'mitt'; type selectedObj = { id: number; name?: string; type?: string; }; type checkedListObj = { type?: string; deptID?: number; accountID?: number; }; //需要部门和人员一起展示 单选/多选 //组件类型--选择人员 export default defineComponent({ name: 'NsShuttleFrameMemberAndDept', props: { //jurisdiction 权限 component 组件 //组件不可选择部门、搜索人员 权限可以选择部门、搜索部门/人员 functionType: { type: String, default: 'jurisdiction', }, searchType: { type: String, default: 'account', }, // componentRef: String, //是否多选 filterMultiple: { type: Boolean, default: false, }, api: { type: Object, // eslint-disable-next-line vue/require-valid-default-prop default: { account: '/api/passport/passport/objs/DepartmentAccounts', dept: '/api/passport/passport/objs/Department', }, }, }, emits: ['selected'], setup(props, ctx) { console.log('NsShuttleFrameMemberAndDept', props); const lmEmitter = inject('lmEmitter') as Emitter<emitEvents>; const placeholder = props.functionType == 'jurisdiction' ? '搜索部门或人员' : '搜索人员'; function initData() { organizationGet(); peopleGet(0); } initData(); lmEmitter.on('deleteAllSelected', (data) => { deleteAllSelected(); }); lmEmitter.on('deleteSelected', (data) => { deleteSelected(data); }); //全部的值 const departmentAllData = ref([]); //展示的候选值 const candidateValues = ref([]); //展示的人员候选值 const candidatePeopleValues = ref([]); //选中的层级 const selectLevel = ref<any>([]); //选中的值 const selectedValue = ref<any>([]); function organizationGet() { http .get(props.api.dept, { pageSize: 9999 }, { timeout: 120 * 1000 }) .then((data) => { if (data.data.length > 0) { selectLevel.value.push({ id: 0, name: data.data[0].tenant, }); departmentAllData.value = data.data; candidateValues.value = data.data.filter(function (item: { checked: boolean; levelID: number; }) { item.checked = false; return item.levelID == 0; }); } }) .catch((e) => {}); } function peopleGet(relatedID: any) { http .get( `/api/passport/passport/objs/DepartmentAccounts`, { relatedID: relatedID }, { timeout: 120 * 1000 }, ) .then((data) => { let selectedValueIdArr: any[] = []; selectedValue.value.map((elem: { id: any }) => { selectedValueIdArr.push(elem.id); }); data.data.map((elem: { ID: any; checked: boolean }) => { if (selectedValueIdArr.includes(elem.accountID)) elem.checked = true; }); candidatePeopleValues.value = data.data; }) .catch((e) => {}); } const state = reactive({ indeterminate: false, checkAll: false, checkedList: [], }); watch( () => state.checkedList, (val) => { // state.indeterminate = // !!val.length && // val.length < candidateValues.value.length + candidatePeopleValues.value.length; // state.checkAll = // val.length === candidateValues.value.length + candidatePeopleValues.value.length && // candidateValues.value.length + candidatePeopleValues.value.length != 0; state.indeterminate = !!val.length && val.length < candidatePeopleValues.value.length; state.checkAll = val.length === candidatePeopleValues.value.length && candidatePeopleValues.value.length != 0; }, { deep: true }, ); watch( () => selectedValue.value, (newData) => {}, { immediate: true, deep: true }, ); //发送数据 const selectedValueCopy = computed(() => JSON.parse(JSON.stringify(selectedValue.value))); watch( () => selectedValueCopy.value, (newData, old) => { let val = []; let delVal = []; if (!old) { return; } //单选 if (!props.filterMultiple) { ctx.emit('selected', old, 'del'); ctx.emit('selected', newData, 'add'); } //新增 else if (!old || newData.length > old.length) { val = compareArrays(newData, old); ctx.emit('selected', val, 'add'); } //删除 else { delVal = compareArrays(old, newData); ctx.emit('selected', delVal, 'del'); } }, { immediate: true, deep: true }, ); const onCheckAllChange = (e: any) => { Object.assign(state, { indeterminate: false, checkAll: e.target.checked, }); //全选 if (e.target.checked) { //selectedValue.value 需要不影响之前的选中 state.checkedList = []; //全选部门 // candidateValues.value.map((item) => { // state.checkedList.push({ // type: 'dept', // deptID: item.ID, // }); // const ret = selectedValue.value.find((ev: { id: any }) => { // return ev.id === item.ID; // }); // if (ret == undefined) { // selectedValue.value.push({ // id: item.ID, // name: item.name, // type: 'dept', // }); // } // item.subordinateDisable = true; // item.checked = e.target.checked; // }); //全选人员 candidatePeopleValues.value.map((item) => { state.checkedList.push({ type: 'account', accountID: item.accountID, }); const ret = selectedValue.value.find((ev: { id: any }) => { return ev.id === item.accountID; }); if (ret == undefined) { selectedValue.value.push({ id: item.accountID, name: item.accountName, type: 'account', }); } item.checked = e.target.checked; }); } else { state.checkedList = []; // //取消全选部门 // candidateValues.value.map((item) => { // remove(unref(selectedValue.value), (elem: object) => { // return elem.id === item.ID; // }); // item.subordinateDisable = false; // item.checked = e.target.checked; // }); //取消全选人员 candidatePeopleValues.value.map((item) => { remove(unref(selectedValue.value), (elem: object) => { return elem.id === item.accountID; }); item.checked = e.target.checked; }); } }; const onCheckChange = (e: { target: { checked: any } }, item: { ID: any; name: any }) => { if (e.target.checked) { state.checkedList.push({ type: 'dept', deptID: item.ID, }); selectedValue.value.push({ id: item.ID, name: item.name, type: 'dept', }); candidateValues.value.map((elem) => { if (item.ID === elem.ID) { elem.subordinateDisable = true; } }); } else { remove(unref(state.checkedList), (elem: object) => { return elem.deptID === item.ID; }); remove(unref(selectedValue.value), (elem: object) => { return elem.id === item.ID && elem.type == 'dept'; }); candidateValues.value.map((elem) => { if (item.ID === elem.ID) { elem.checked = false; elem.subordinateDisable = false; } }); } }; //部门单选 const onCheckChangeRadio = (e: any, item: any) => {}; //选中人员--多选 const onCheckPeopleChange = ( e: { target: { checked: any } }, item: { accountID: any; accountName: any }, ) => { // accountID; if (e.target.checked) { state.checkedList.push({ type: 'account', accountID: item.accountID, }); selectedValue.value.push({ id: item.accountID, name: item.accountName, type: 'account', }); } else { remove(unref(state.checkedList), (elem: object) => { return elem.accountID === item.accountID; }); remove(unref(selectedValue.value), (elem: object) => { return elem.id === item.accountID && elem.type == 'account'; }); } }; //选中人员--单选 const onCheckPeopleChangeRadio = ( e: any, item: { accountID: any; accountName: any }, index: string | number, ) => { // debugger; candidatePeopleValues.value[index].checked = item.checked ? !item.checked : true; if (item.checked) { if (selectedValue.value.length > 0) { try { candidatePeopleValues.value.map((elem, elemIndex) => { if (elem.accountID == selectedValue.value[0].id) { onCheckPeopleChangeRadio(null, elem, elemIndex); throw 'err'; } }); } catch (error) { selectedValue.value[0] = { id: item.accountID, name: item.accountName, type: 'account', }; } } else { selectedValue.value[0] = { id: item.accountID, name: item.accountName, type: 'account', }; } } else { remove(unref(selectedValue.value), (elem: object) => { return elem.id === item.accountID && elem.type == 'account'; }); } }; function checkSubordinate(item: { subordinateDisable: any; ID: any; name: any }) { if (item.subordinateDisable) return; peopleGet(item.ID); state.checkedList = []; // item.checked = true; // state.checkedList.push(item.ID); selectLevel.value.push({ id: item.ID, name: item.name, }); candidateValues.value = departmentAllData.value.filter(function (elem) { return elem.levelID == item.ID; }); } //选中层级切换 function checkSelectLevel(item: { id: any }, index: number, type) { if (type != 'search') { if (index == selectLevel.value.length - 1) { return; } else if (index == 0) { peopleGet(0); } candidateValues.value = departmentAllData.value.filter(function (elem) { return elem.levelID == item.id; }); } selectLevel.value.forEach((elem: { id: any }, index: number) => { if (elem.id == item.id) { selectLevel.value.splice(index + 1, selectLevel.value.length - 1); } }); } function getSelectedValue() { // selectedValue.value.map((elem: { componentRef: string | undefined }) => { // elem.componentRef = props.componentRef; // }); return selectedValue.value; } //删除已选择 function deleteSelected(item: { type: string; id: any }) { if (item.type == 'dept') { candidateValues.value.map((elem) => { if (elem.ID == item.id) { elem.subordinateDisable = false; elem.checked = false; } }); remove(unref(state.checkedList), (elem: object) => { return elem.deptID === item.id && elem.type == item.type; }); } else { candidatePeopleValues.value.map((elem) => { if (elem.accountID == item.id) { elem.checked = false; } }); remove(unref(state.checkedList), (elem: object) => { return elem.accountID === item.id && elem.type == item.type; }); } remove(unref(selectedValue.value), (elem: object) => { return elem.id === item.id && elem.type == item.type; }); } //清空已选择 function deleteAllSelected() { console.log('清空'); state.checkedList = []; selectedValue.value = []; candidatePeopleValues.value.map((elem) => { elem.checked = false; }); candidateValues.value.map((elem) => { elem.checked = false; }); } const searchValue = ref(); //是否展示为搜索模式 const ifShowSearchMode = ref(false); //全部人员 const accountAllData = ref([]); // getAccountAllData(); async function getAccountAllData() { let data = await http.get( '/api/passport/passport/objs/Account', { pageSize: 9999 }, { timeout: 120 * 1000 }, ); return data; } function onSearch() { console.log('e', searchValue.value); if (!searchValue.value) { ifShowSearchMode.value = false; selectLevel.value = []; initData(); return; } //两种搜索模式 1.权限 部门/人员 2.组件 人员 ifShowSearchMode.value = true; //权限 if (props.functionType == 'jurisdiction') { candidateValues.value = departmentAllData.value.filter(function (item) { return item.name.indexOf(searchValue.value) != -1; }); candidatePeopleValues.value = accountAllData.value.filter(function (item: { accountName: any; name: string | any[]; accountID: any; ID: any; }) { item.accountName = item.name; item.accountID = item.ID; return item.name.indexOf(searchValue.value) != -1; }); } //组件 else { candidateValues.value = []; candidatePeopleValues.value = accountAllData.value.filter(function (item: { accountName: any; name: string | any[]; accountID: any; ID: any; }) { item.accountName = item.name; item.accountID = item.ID; return item.name.indexOf(searchValue.value) != -1; }); } checkSelectLevel(selectLevel.value[0], 0, 'search'); } //初始化值 lmEmitter.on('setDefaultDataMAD', (data: any) => { getAccountAllData().then((res) => { if (res.data.length > 0) { accountAllData.value = res.data; } if (data.length > 0 && !data[0].id) { //单选 if (!props.filterMultiple) { if (props.searchType == 'account') { let item = accountAllData.value.filter(function (elem) { if (elem.name == data[0].name) elem.checked = true; return elem.name == data[0].name; }); data[0].id = item[0].ID; } else { let item = departmentAllData.value.filter(function (elem) { if (elem.name == data[0].name) elem.checked = true; return elem.name == data[0].name; }); data[0].id = item[0].ID; } } //多选 else { let nameList = []; data.map((item) => { nameList.push(item.name); }); let dataList = accountAllData.value.filter(function (elem) { elem.checked = nameList.includes(elem.name); return nameList.includes(elem.name); }); for (let index = 0; index < data.length; index++) { try { dataList.map((dl) => { if (dl.name == data[index].name) { throw dl; } }); } catch (error) { data[index].id = error.ID; } } } } selectedValue.value = data; }); }); return { candidateValues, ifShowSearchMode, onCheckAllChange, searchValue, onSearch, onCheckChange, onCheckChangeRadio, state, checkSubordinate, selectLevel, checkSelectLevel, selectedValue, getSelectedValue, candidatePeopleValues, onCheckPeopleChange, deleteSelected, deleteAllSelected, placeholder, onCheckPeopleChangeRadio, }; }, }); </script> <style lang="less" scoped> .search-input { display: flex; border: 1px solid #dcdfe2; border-radius: 8px; padding-right: 8px; margin-bottom: 8px; } .checkbox { padding: 4px 0; &-subordinate { float: right; color: @primary-color; cursor: pointer; } &-subordinate-disable { float: right; color: #9b9b9b; cursor: default; } } .selectLevel { margin-bottom: 2px; &-last-span { color: #9b9b9b; padding: 0px 4px; cursor: pointer; } &-span { padding: 0px 4px; cursor: pointer; } } .checkbox-content { // overflow-y: auto; // // min-height: 250px; // // max-height: 350px; // height: 250px; } .candidate-content { height: 250px; overflow-y: auto; } .candidate-content-search { height: 250px; overflow-y: auto; .searchValue-title { margin-bottom: 0px; color: #a2a3a5; } } :deep(.ant-result) { padding: 24px; } :deep(.ant-result-icon) { margin-bottom: 0px; } :deep(.ant-result-title) { color: #747677; text-align: center; font-size: 18px; } </style>