You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
597 lines
18 KiB
597 lines
18 KiB
7 months ago
|
<!-- 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>
|