448 lines
16 KiB
Vue
448 lines
16 KiB
Vue
<template>
|
|
<Modal v-model="modal" title="选择用户" width="720px" :mask-closable="false" :closable="false">
|
|
<Tabs class="tabs-panel" type="card" v-if="firstTabIndex && firstTabIndex.length > 1" v-model="firstTabIndex" @on-click="firstTabChange">
|
|
<TabPane v-for="(item, index) in firstTabData" :label="item" :name="index + ''"></TabPane>
|
|
</Tabs>
|
|
<AutoComplete @on-search="handleSearch" @on-select="handleSelect" placeholder="输入名字搜索" v-model="searchTreeName" :clearable="true" class="auto-box">
|
|
<Option v-for="item in currentFlatten" :value="item.name">
|
|
<span>{{ item.name }}</span>
|
|
<span class="fr info">{{ item.group }}</span>
|
|
</Option>
|
|
</AutoComplete>
|
|
<div class="tree-wrap fix">
|
|
<div class="tree-panel fl">
|
|
<ul class="tree-tab-ul fix">
|
|
<li
|
|
@click="secondTabChange(index)"
|
|
:style="{ width: 100 / secondTabData.length + '%' }"
|
|
:class="{ active: index == secondTabIndex, fl: true }"
|
|
v-for="(item, index) in secondTabData"
|
|
>
|
|
{{ item }}
|
|
</li>
|
|
</ul>
|
|
<div class="tree-box">
|
|
<div :id="ztreeDomId" class="ztree"></div>
|
|
</div>
|
|
</div>
|
|
<div class="arrow-box fl">
|
|
<svg t="1626916477711" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="10021" width="36" height="36">
|
|
<path
|
|
d="M138.935 256.078v511.844h63.42V256.078h-63.42z m126.842 0v511.844h127.215V256.078H265.777zM885.064 512L583.254 64.323v191.755H456.414v511.844h126.842v191.755L885.064 512z"
|
|
p-id="10022"
|
|
fill="#2d8cf0"
|
|
></path>
|
|
</svg>
|
|
</div>
|
|
<div class="user-panel fr">
|
|
<div class="title-box fix">
|
|
<Checkbox @on-change="checkAllUser" v-model="isCheckUserAll">全选</Checkbox>
|
|
<div class="fr">{{ tempUserList.length }}</div>
|
|
<a href="javascript:;" @click="delCheckUser" v-if="tempUserList.findIndex(x => x.isCheck) > -1">删除选中</a>
|
|
</div>
|
|
<ul class="user-box">
|
|
<li :class="{ active: item.isCheck }" v-for="(item, index) in tempUserList" @click="checkUserOne(item)">
|
|
{{ item.name }}
|
|
<span class="info">{{ item.group }}</span>
|
|
</li>
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
<div slot="footer">
|
|
<i-Button type="primary" @click="submit">提交</i-Button>
|
|
<i-Button @click="modal = false" style="margin-left: 8px">取消</i-Button>
|
|
</div>
|
|
</Modal>
|
|
</template>
|
|
|
|
<script>
|
|
require("@public/libs/ztree/zTreeStyle.css");
|
|
import "@public/libs/ztree/jquery.ztree.all.min.js";
|
|
export default {
|
|
props: {
|
|
treeData: {
|
|
type: Array,
|
|
default() {
|
|
return [];
|
|
},
|
|
},
|
|
userids: {
|
|
type: Array,
|
|
default() {
|
|
return [];
|
|
},
|
|
},
|
|
ignoreIds: {
|
|
type: Array,
|
|
default() {
|
|
return [];
|
|
},
|
|
},
|
|
},
|
|
model: {
|
|
prop: "userids",
|
|
event: "change",
|
|
},
|
|
data() {
|
|
return {
|
|
ztreeDomId: null,
|
|
modal: false,
|
|
isCheckUserAll: false,
|
|
firstTabIndex: 0,
|
|
secondTabIndex: 0,
|
|
cacheMap: new Map(),
|
|
currentTree: [],
|
|
currentFlatten: [],
|
|
searchTreeName: null,
|
|
tempUserList: [],
|
|
isDisabled: false,
|
|
};
|
|
},
|
|
watch: {
|
|
searchTreeName(val) {
|
|
this.handleSearch(val);
|
|
},
|
|
},
|
|
computed: {
|
|
firstTabData() {
|
|
if (this.treeData.length > 1) return this.treeData.map(x => x.name);
|
|
else return [];
|
|
},
|
|
secondTabData() {
|
|
if (this.treeData.length == 0) return [];
|
|
return this.treeData[this.firstTabIndex].data.map(x => x.name);
|
|
},
|
|
currentTreeFlatten() {
|
|
const flatten = (data, pid, pname) => {
|
|
return data.reduce((arr, { id, name, children = [], type }) => arr.concat([{ id, name, pid, type, group: pname }], flatten(children, id, name)), []);
|
|
};
|
|
if (!this.currentTree) return [];
|
|
var newArr = flatten(this.currentTree, 0, "");
|
|
return newArr;
|
|
},
|
|
},
|
|
|
|
mounted() {
|
|
this.isDisabled = this.ignoreIds.length > 0;
|
|
this.ztreeDomId = this.uuid();
|
|
},
|
|
methods: {
|
|
show() {
|
|
this.modal = true;
|
|
this.firstTabChange("0");
|
|
},
|
|
firstTabChange(index) {
|
|
this.firstTabIndex = index;
|
|
this.secondTabChange(0);
|
|
},
|
|
secondTabChange(index) {
|
|
const _this = this;
|
|
if (index != 0 && this.secondTabIndex == index) return;
|
|
this.secondTabIndex = index;
|
|
this.changeEmpty();
|
|
this.initTree();
|
|
},
|
|
handleSearch(query) {
|
|
const _this = this;
|
|
let arr = [],
|
|
searchArr = this.currentTreeFlatten.filter(x => x.type == 2 && x.name.indexOf(query) != -1);
|
|
for (const item of searchArr) {
|
|
!arr.map(x => _this.getSplitId(x.id)).includes(_this.getSplitId(item.id)) && arr.push(item);
|
|
}
|
|
this.currentFlatten = arr;
|
|
},
|
|
handleSelect(query) {
|
|
const _this = this;
|
|
if (query.indexOf(",") != -1) query = query.split(",")[0];
|
|
var treeObj = $.fn.zTree.getZTreeObj(this.ztreeDomId);
|
|
var node = treeObj.getNodeByParam("name", query, null);
|
|
if (!!node) treeObj.checkNode(node, true, true, true);
|
|
},
|
|
formatTree(tree, level = 0) {
|
|
const _this = this;
|
|
tree.forEach(element => {
|
|
element.open = level == 0;
|
|
element.type = 1;
|
|
element.id = element.id + "," + element.pid;
|
|
element.checked = false;
|
|
if (typeof element.children !== "undefined" && element.children.length > 0) element.children = _this.formatTree(element.children, level + 1);
|
|
else {
|
|
element.type = 2;
|
|
}
|
|
});
|
|
return tree;
|
|
},
|
|
initTree() {
|
|
const _this = this;
|
|
let first = _this.firstTabIndex,
|
|
second = _this.secondTabIndex,
|
|
cacheKey = first + "," + second,
|
|
cacheMap = _this.cacheMap,
|
|
tree = this.treeData[first].data[second].data;
|
|
|
|
if (!!tree && typeof tree === "function") {
|
|
var cacheData = cacheMap.get(cacheKey);
|
|
if (!cacheData) {
|
|
tree().then(data => {
|
|
cacheMap.set(cacheKey, data);
|
|
_this.createTree(data, _this.ztreeDomId);
|
|
});
|
|
} else {
|
|
_this.createTree(cacheData, _this.ztreeDomId);
|
|
}
|
|
} else {
|
|
_this.createTree(tree, _this.ztreeDomId);
|
|
}
|
|
},
|
|
createTree(_data, _domId) {
|
|
const _this = this;
|
|
var jtree = JSON.parse(JSON.stringify(_data));
|
|
var ftree = _this.formatTree(jtree);
|
|
_this.currentTree = ftree;
|
|
//_this.tempUserList = JSON.parse(JSON.stringify(_this.userids));
|
|
|
|
let treeSetting = {
|
|
view: {
|
|
dblClickExpand: false,
|
|
showIcon: false,
|
|
},
|
|
check: {
|
|
enable: true,
|
|
chkboxType: { Y: "ps", N: "ps" },
|
|
},
|
|
callback: {},
|
|
};
|
|
function formatUser(tree, checked) {
|
|
const recursionCheckIds = tree => {
|
|
let arrIds = [];
|
|
for (const key in tree) {
|
|
const element = tree[key];
|
|
if (element.type == 2 && !element.chkDisabled) arrIds.push({ id: element.id, tid: element.tId });
|
|
if (typeof element.children !== "undefined" && element.children) {
|
|
var childIds = recursionCheckIds(element.children);
|
|
arrIds = [...arrIds, ...childIds];
|
|
}
|
|
}
|
|
return arrIds;
|
|
};
|
|
var checkIds = recursionCheckIds([tree]);
|
|
var checkUserList = _this.currentTreeFlatten.filter(x => checkIds.map(x => x.id).includes(x.id));
|
|
let checkUserArr = [];
|
|
for (const element of checkUserList) {
|
|
var findObj = checkIds.find(x => x.id == element.id);
|
|
if (!!findObj && checkUserArr.findIndex(x => x.id == element.id) === -1 && _this.tempUserList.findIndex(x => x.id == element.id) === -1) {
|
|
checkUserArr.push({ ...element, isCheck: false, tid: findObj.tid });
|
|
}
|
|
}
|
|
if (checked) _this.tempUserList = _this.tempUserList.concat(checkUserArr);
|
|
else {
|
|
var ids = checkUserList.map(x => x.id);
|
|
_this.tempUserList = _this.tempUserList.filter(x => !ids.includes(x.id));
|
|
}
|
|
}
|
|
|
|
//当禁止时,是否还显示在右侧
|
|
var isCheckFormat = treeNode => {
|
|
let id = _this.getSplitId(treeNode.id);
|
|
return _this.ignoreIds.findIndex(x => x == id) === -1;
|
|
};
|
|
|
|
treeSetting.callback.onClick = (event, treeId, treeNode, clickFlag) => {
|
|
let check = treeNode.checked;
|
|
let treeObj = $.fn.zTree.getZTreeObj(treeId);
|
|
treeObj.checkNode(treeNode, !check, true);
|
|
if (isCheckFormat(treeNode)) formatUser(treeNode, treeNode.checked);
|
|
};
|
|
treeSetting.callback.onCheck = (event, treeId, treeNode) => {
|
|
if (isCheckFormat(treeNode)) formatUser(treeNode, treeNode.checked);
|
|
};
|
|
var treeObj = $.fn.zTree.init($("#" + _domId), treeSetting, ftree);
|
|
//checkUser
|
|
var nodes = treeObj.getNodesByFilter(x => _this.userids.includes(_this.getSplitId(x.id)));
|
|
var arr = [];
|
|
nodes.forEach(element => {
|
|
var splitId = _this.getSplitId(element.id); //重复id只选择第一个
|
|
if (!arr.includes(splitId)) treeObj.checkNode(element, true, true, true);
|
|
arr.push(splitId);
|
|
});
|
|
|
|
var ignorNodes = treeObj.getNodesByFilter(x => _this.ignoreIds.includes(_this.getSplitId(x.id)));
|
|
ignorNodes.forEach(element => {
|
|
treeObj.checkNode(element, true, true, true);
|
|
treeObj.setChkDisabled(element, true);
|
|
});
|
|
},
|
|
checkAllUser(check) {
|
|
//全选用户
|
|
const _this = this;
|
|
_this.tempUserList.forEach(element => {
|
|
element.isCheck = check;
|
|
});
|
|
},
|
|
checkUserOne(item) {
|
|
item.isCheck = !item.isCheck;
|
|
let tlist = this.tempUserList;
|
|
this.isCheckUserAll = tlist.length > 0 && tlist.filter(x => x.isCheck).length == tlist.length;
|
|
},
|
|
delCheckUser() {
|
|
//删除选中用户
|
|
const _this = this;
|
|
let tlist = _this.tempUserList,
|
|
delArr = [];
|
|
for (const item of tlist) {
|
|
item.isCheck && delArr.push(item);
|
|
}
|
|
var treeObj = $.fn.zTree.getZTreeObj(_this.ztreeDomId);
|
|
delArr.forEach(element => {
|
|
var node = treeObj.getNodeByTId(element.tid);
|
|
treeObj.checkNode(node, false, true, true);
|
|
});
|
|
_this.isCheckUserAll = false;
|
|
},
|
|
changeEmpty() {
|
|
this.tempUserList = [];
|
|
this.searchTreeName = "";
|
|
},
|
|
submit() {
|
|
const _this = this;
|
|
let objArr = [],
|
|
idsArr = this.tempUserList.map(x => _this.getSplitId(x.id));
|
|
idsArr = Array.from(new Set(idsArr));
|
|
this.$emit("change", idsArr);
|
|
function delKey(obj, ...args) {
|
|
args.forEach(v => {
|
|
delete obj[v];
|
|
});
|
|
return obj;
|
|
}
|
|
idsArr.forEach(element => {
|
|
let findObj = _this.tempUserList.find(x => _this.getSplitId(x.id) == element);
|
|
delKey(findObj, "id", "isCheck", "tid", "pid", "type");
|
|
let item = { ...findObj, id: element };
|
|
if (_this.isDisabled) item.disabled = this.userids.findIndex(x => x == element) !== -1;
|
|
objArr.push(item);
|
|
});
|
|
this.$emit("on-submit", objArr);
|
|
this.modal = false;
|
|
},
|
|
getSplitId(strId) {
|
|
if (typeof strId === "string" && strId.indexOf(",") != -1) return parseInt(strId.split(",")[0]);
|
|
else return strId;
|
|
},
|
|
uuid() {
|
|
return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, function (c) {
|
|
var r = (Math.random() * 16) | 0,
|
|
v = c == "x" ? r : (r & 0x3) | 0x8;
|
|
return v.toString(16);
|
|
});
|
|
},
|
|
},
|
|
};
|
|
</script>
|
|
|
|
<style lang="scss" scoped>
|
|
.tree-wrap {
|
|
height: 430px;
|
|
position: relative;
|
|
}
|
|
.tree-panel {
|
|
border: 1px solid #eee;
|
|
width: 280px;
|
|
height: 430px;
|
|
overflow: hidden;
|
|
}
|
|
.tree-tab-ul {
|
|
li {
|
|
background: #f7f7f7;
|
|
border-bottom: 1px solid #eee;
|
|
height: 34px;
|
|
line-height: 34px;
|
|
text-align: center;
|
|
cursor: pointer;
|
|
}
|
|
li.active {
|
|
background: #fff;
|
|
color: #2d8cf0;
|
|
border-bottom: none;
|
|
cursor: default;
|
|
}
|
|
}
|
|
.tree-box {
|
|
padding: 4px 0;
|
|
height: 394px;
|
|
width: auto;
|
|
overflow-y: auto;
|
|
border: none;
|
|
}
|
|
.user-panel {
|
|
border: 1px solid #eee;
|
|
width: 280px;
|
|
.title-box {
|
|
background: #f7f7f7;
|
|
height: 34px;
|
|
line-height: 34px;
|
|
padding: 0 12px;
|
|
a {
|
|
text-decoration: none;
|
|
float: right;
|
|
margin-right: 20px;
|
|
outline: none;
|
|
}
|
|
}
|
|
.user-box {
|
|
height: 396px;
|
|
overflow-y: auto;
|
|
li {
|
|
line-height: 32px;
|
|
padding-left: 12px;
|
|
cursor: pointer;
|
|
.info {
|
|
float: right;
|
|
margin-right: 12px;
|
|
font-size: 12px;
|
|
color: gray;
|
|
}
|
|
}
|
|
li:hover {
|
|
background: #f3f3f3;
|
|
}
|
|
li.active {
|
|
background: #e4edf6;
|
|
}
|
|
}
|
|
}
|
|
.arrow-box {
|
|
width: 128px;
|
|
text-align: center;
|
|
position: relative;
|
|
top: 50%;
|
|
margin-top: -17px;
|
|
}
|
|
|
|
.auto-box {
|
|
width: 200px;
|
|
margin-bottom: 10px;
|
|
/deep/.ivu-select-item .info {
|
|
font-size: 12px;
|
|
color: gray;
|
|
position: relative;
|
|
top: 2px;
|
|
}
|
|
}
|
|
|
|
/deep/ .ztree li {
|
|
overflow: hidden;
|
|
}
|
|
/deep/ .ztree li a {
|
|
width: 100%;
|
|
}
|
|
/deep/ .ztree li a:hover {
|
|
text-decoration: none;
|
|
}
|
|
|
|
/deep/ .ivu-auto-complete .ivu-select-dropdown-list {
|
|
max-height: 310px;
|
|
overflow-y: auto;
|
|
}
|
|
</style>
|