<!-- 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="candidateValues.length > 0"> <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" @change="onCheckChange($event, item)" >{{ item.name }}</a-checkbox > <a-radio v-else v-model:checked="item.checked" @click="onCheckChangeRadio(item)">{{ item.name }}</a-radio> <span v-if="searchType == 'dept'" @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> <div v-if="candidateValues.length <= 0 && ifLoading"> <a-result title="暂无相关部门/人员"> <template #icon> <ns-icon class="iconfont" style="margin: auto; width: 120px; height: 120px" name="noSearchData" /> </template> </a-result> </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'; //单独部门/人员 只区分单选或多选 export default defineComponent({ name: 'NsShuttleFrameDeptOrMember', props: { //是否多选 filterMultiple: { type: Boolean, default: false, }, placeholder: String, searchType: { type: String, default: 'account', }, // componentRef: String, 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) { const lmEmitter = inject('lmEmitter') as Emitter<emitEvents>; // function initData() { // if (props.searchType == 'dept') { // organizationGet(); // } else { // accountGet(); // } // } // initData(); lmEmitter.on('deleteAllSelected', (data) => { deleteAllSelected(); }); lmEmitter.on('deleteSelected', (data) => { deleteSelected(data); }); //全部的值 const departmentAllData = ref([]); const accountAllData = ref([]); const ifLoading = ref(false); //展示的候选值 const candidateValues = ref([]); //选中的层级 const selectLevel = ref<any>([]); //选中的值 const selectedValue = ref<any>([]); //获取部门的值 async function organizationGet() { let data = await http.get( props.api.dept, { pageSize: 9999, levelID: 0 }, { timeout: 120 * 1000 }, ); return data; } //获取人员的值 async function accountGet() { await http.get(props.api.account, { pageSize: 9999 }, { timeout: 120 * 1000 }); } const state = reactive({ indeterminate: false, checkAll: false, checkedList: [], }); 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(item.ID); const ret = selectedValue.value.find((ev: { id: any }) => { return ev.id === item.ID; }); if (ret == undefined) { selectedValue.value.push({ type: props.searchType, id: item.ID, name: item.name, }); } item.subordinateDisable = true; 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; }); } }; watch( () => state.checkedList, (val) => { state.indeterminate = !!val.length && val.length < candidateValues.value.length; state.checkAll = val.length === candidateValues.value.length && candidateValues.value.length > 0; }, { deep: true }, ); // watch( // () => selectedValue.value, // (val) => { // val.map((elem) => { // elem.componentRef = props.componentRef; // }); // }, // { immediate: true, deep: true }, // ); const selectedValueCopy = computed(() => JSON.parse(JSON.stringify(selectedValue.value))); watch( () => selectedValueCopy.value, (newData, old) => { let val = []; let delVal = []; if (props.filterMultiple) { //新增 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'); } } else { //取消选择 if (newData.length == 0 && old) { ctx.emit('selected', old, 'del'); } //切换选择 else if (old) { ctx.emit('selected', newData, 'add'); ctx.emit('selected', old, 'del'); } } }, { immediate: true, deep: true }, ); const onCheckChange = (e: any, item: object) => { if (e.target.checked) { state.checkedList.push(item.ID); selectedValue.value.push({ type: props.searchType, id: item.ID, name: item.name, }); candidateValues.value.map((elem) => { if (item.ID === elem.ID) { elem.subordinateDisable = true; } }); } else { remove(unref(state.checkedList), (elem: object) => { return elem === item.ID; }); remove(unref(selectedValue.value), (elem: object) => { return elem.id === item.ID; }); candidateValues.value.map((elem) => { if (item.ID === elem.ID) { elem.checked = false; elem.subordinateDisable = false; } }); } }; function checkSubordinate(item: { subordinateDisable: any; ID: any; name: any }) { if (item.subordinateDisable) return; state.checkedList = []; 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) { if (index == selectLevel.value.length - 1) return; selectLevel.value.forEach((elem: { id: any }, index: number) => { if (elem.id == item.id) { selectLevel.value.splice(index + 1, selectLevel.value.length - 1); } }); candidateValues.value = departmentAllData.value.filter(function (elem) { return elem.levelID == item.id; }); if (!props.filterMultiple) { let ifSelected = candidateValues.value.filter(function (elem) { return elem.checked == true; }); //说明选中了,之前的选中要删除 if (ifSelected.length > 0 && ifSelected[0].ID !== selectedValue.value[0].id) { candidateValues.value.map((elem) => { elem.checked = false; elem.subordinateDisable = false; }); } } } function getSelectedValue() { // selectedValue.value.map((elem) => { // elem.componentRef = props.componentRef; // }); return selectedValue.value; } //单选场景 const onCheckChangeRadio = (item: object) => { console.log(item.checked); if (!item.checked) { selectedValue.value = [ { type: props.searchType, id: item.ID, name: item.name, }, ]; candidateValues.value.map((elem) => { elem.checked = item.ID === elem.ID; elem.subordinateDisable = item.ID === elem.ID; }); } else { remove(unref(selectedValue.value), (elem: object) => { return elem.id === item.ID; }); candidateValues.value.map((elem) => { if (item.ID === elem.ID) { elem.checked = false; elem.subordinateDisable = false; } }); } console.log('selectedValue', selectedValue.value); }; //删除已选择 function deleteSelected(item: { type: string; id: object }) { console.log('deleteSelected', item); 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 { candidateValues.value.map((elem) => { if (elem.ID == item.id) { elem.checked = false; } }); remove(unref(state.checkedList), (elem: object) => { return elem === item.id; }); } remove(unref(selectedValue.value), (elem: object) => { return elem.id === item.id && elem.type == item.type; }); } //清空已选择 function deleteAllSelected() { state.checkedList = []; selectedValue.value = []; candidateValues.value.map((elem) => { elem.checked = false; }); } const searchValue = ref(); function onSearch() { if (selectLevel.value.length > 1) { selectLevel.value = selectLevel.value.splice(0, selectLevel.value.length - 1); } if (props.searchType == 'account') { candidateValues.value = accountAllData.value.filter(function (item) { return item.name.includes(searchValue.value); }); } else { candidateValues.value = departmentAllData.value.filter(function (item) { return item.name.includes(searchValue.value); }); } } function formatOrganizationData(data: { data: string | any[] }) { if (data.data.length > 0) { selectLevel.value.push({ id: 0, name: data.data[0].tenant, }); let selectedList: any[] = []; selectedValue.value.map((item: { id: any }) => { selectedList.push(item.id); }); departmentAllData.value = data.data; initSelectedOrgan(); } } function initSelectedOrgan() { let selectedList: any[] = []; selectedValue.value.map((item: { id: any }) => { selectedList.push(item.id); }); departmentAllData.value.map((item) => { item.subordinateDisable = selectedList.includes(item.ID); item.checked = selectedList.includes(item.ID); }); candidateValues.value = departmentAllData.value.filter(function (item: { checked: boolean; ID: any; levelID: number; }) { return item.levelID == 0; }); } function formatAccountData(data: { data: { tenant: any }[] }) { if (data.data.length > 0) { selectLevel.value.push({ id: 0, name: data.data[0].tenant, }); let selectedList: any[] = []; selectedValue.value.map((item: { id: any }) => { selectedList.push(item.id); }); data.data.map((item: { subordinateDisable: boolean; ID: any; checked: boolean }) => { item.subordinateDisable = selectedList.includes(item.ID); item.checked = selectedList.includes(item.ID); }); accountAllData.value = data.data; candidateValues.value = data.data.filter(function (item: { checked: boolean; levelID: number; }) { return item.levelID == 0; }); candidateValues.value = data.data; } } //初始化值 lmEmitter.on('setDefaultDataMOD', (data: any) => { if (props.searchType == 'account') { accountGet().then((res) => { formatAccountData(res); if (data.length <= 0) { return; } setTimeout(() => { //单选 initSelectedValue(accountAllData.value, data, 'account'); }, 100); }); } else { organizationGet().then((res) => { formatOrganizationData(res); if (data.length <= 0) { return; } setTimeout(() => { initSelectedValue(departmentAllData.value, data, 'dept'); }, 100); }); } }); function initSelectedValue(list: any[], data: any[], type: string) { //单选 if (!props.filterMultiple) { let item = list.filter(function (elem: { name: any }) { return elem.name == data[0].name; }); data[0].id = item[0].ID; } //多选 else { let nameList: any[] = []; data.map((item: { name: any }) => { nameList.push(item.name); }); let dataList = list.filter(function (elem: { name: any }) { return nameList.includes(elem.name); }); for (let index = 0; index < data.length; index++) { try { dataList.map((dl: { name: any }) => { if (dl.name == data[index].name) { throw dl; } }); } catch (error) { data[index].id = error.ID; } } } selectedValue.value = data; ifLoading.value = true; if (type == 'dept') { initSelectedOrgan(); } } return { candidateValues, onCheckAllChange, searchValue, onSearch, onCheckChange, state, checkSubordinate, selectLevel, checkSelectLevel, selectedValue, getSelectedValue, onCheckChangeRadio, deleteSelected, deleteAllSelected, ifLoading, }; }, }); </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; } :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>