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.

758 lines
23 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="!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>