mirror of
https://github.com/cool-team-official/cool-admin-vue.git
synced 2024-11-01 22:20:27 +08:00
423 lines
7.6 KiB
Vue
423 lines
7.6 KiB
Vue
<template>
|
|
<div class="cl-dept-tree">
|
|
<div class="cl-dept-tree__header">
|
|
<div>组织架构</div>
|
|
|
|
<ul class="cl-dept-tree__op">
|
|
<li>
|
|
<el-tooltip content="刷新">
|
|
<i class="el-icon-refresh" @click="refresh()"></i>
|
|
</el-tooltip>
|
|
</li>
|
|
|
|
<li v-if="drag && isPc">
|
|
<el-tooltip content="拖动排序">
|
|
<i class="el-icon-s-operation" @click="isDrag = true"></i>
|
|
</el-tooltip>
|
|
</li>
|
|
|
|
<li class="no" v-show="isDrag">
|
|
<el-button type="text" size="mini" @click="treeOrder(true)">保存</el-button>
|
|
<el-button type="text" size="mini" @click="treeOrder(false)">取消</el-button>
|
|
</li>
|
|
</ul>
|
|
</div>
|
|
|
|
<div class="cl-dept-tree__container" @contextmenu.prevent="openCM">
|
|
<el-tree
|
|
node-key="id"
|
|
highlight-current
|
|
default-expand-all
|
|
:data="list"
|
|
:props="{
|
|
label: 'name'
|
|
}"
|
|
:draggable="isDrag"
|
|
:allow-drag="allowDrag"
|
|
:allow-drop="allowDrop"
|
|
:expand-on-click-node="false"
|
|
v-loading="loading"
|
|
@node-contextmenu="openCM"
|
|
>
|
|
<template slot-scope="{ node, data }">
|
|
<div class="cl-dept-tree__node">
|
|
<span class="cl-dept-tree__node-label" @click="rowClick(data)">{{
|
|
node.label
|
|
}}</span>
|
|
<span
|
|
class="cl-dept-tree__node-icon"
|
|
v-if="!isPc"
|
|
@click="openCM($event, data, node)"
|
|
>
|
|
<i class="el-icon-more"></i>
|
|
</span>
|
|
</div>
|
|
</template>
|
|
</el-tree>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<script>
|
|
import { deepTree, isArray, revDeepTree, isPc } from "cl-admin/utils";
|
|
import { ContextMenu, Form } from "cl-admin-crud";
|
|
|
|
export default {
|
|
name: "cl-dept-tree",
|
|
|
|
props: {
|
|
drag: {
|
|
type: Boolean,
|
|
default: true
|
|
},
|
|
level: {
|
|
type: Number,
|
|
default: 99
|
|
}
|
|
},
|
|
|
|
data() {
|
|
return {
|
|
list: [],
|
|
loading: false,
|
|
isDrag: false,
|
|
isPc: isPc()
|
|
};
|
|
},
|
|
|
|
created() {
|
|
this.refresh();
|
|
},
|
|
|
|
methods: {
|
|
openCM(e, d, n) {
|
|
let list = [
|
|
{
|
|
label: "新增",
|
|
"suffix-icon": "el-icon-plus",
|
|
hidden: n && n.level >= this.level,
|
|
callback: (item, done) => {
|
|
this.rowEdit({
|
|
name: "",
|
|
parentName: d.name,
|
|
parentId: d.id
|
|
});
|
|
done();
|
|
}
|
|
},
|
|
{
|
|
label: "编辑",
|
|
"suffix-icon": "el-icon-edit",
|
|
callback: (item, done) => {
|
|
this.rowEdit(d);
|
|
done();
|
|
}
|
|
}
|
|
];
|
|
|
|
if (!d) {
|
|
d = this.list[0];
|
|
}
|
|
|
|
if (d.parentId) {
|
|
list.push({
|
|
label: "删除",
|
|
"suffix-icon": "el-icon-delete",
|
|
callback: (item, done) => {
|
|
this.rowDel(d);
|
|
done();
|
|
}
|
|
});
|
|
}
|
|
|
|
list.push({
|
|
label: "新增成员",
|
|
"suffix-icon": "el-icon-user",
|
|
callback: (item, done) => {
|
|
this.$emit("user-add", d);
|
|
done();
|
|
}
|
|
});
|
|
|
|
ContextMenu.open(e, {
|
|
list
|
|
});
|
|
},
|
|
|
|
allowDrag({ data }) {
|
|
return data.parentId;
|
|
},
|
|
|
|
allowDrop(draggingNode, dropNode) {
|
|
return dropNode.data.parentId;
|
|
},
|
|
|
|
refresh() {
|
|
this.isDrag = false;
|
|
this.loading = true;
|
|
|
|
this.$service.system.dept
|
|
.list()
|
|
.then((res) => {
|
|
this.list = deepTree(res);
|
|
this.$emit("list-change", this.list);
|
|
})
|
|
.done(() => {
|
|
this.loading = false;
|
|
});
|
|
},
|
|
|
|
rowClick(e) {
|
|
ContextMenu.close();
|
|
let ids = e.children ? revDeepTree(e.children).map((e) => e.id) : [];
|
|
ids.unshift(e.id);
|
|
this.$emit("row-click", { item: e, ids });
|
|
},
|
|
|
|
rowEdit(e) {
|
|
const method = e.id ? "update" : "add";
|
|
|
|
Form.open({
|
|
title: "编辑部门",
|
|
width: "550px",
|
|
props: {
|
|
"label-width": "100px"
|
|
},
|
|
items: [
|
|
{
|
|
label: "部门名称",
|
|
prop: "name",
|
|
value: e.name,
|
|
component: {
|
|
name: "el-input",
|
|
attrs: {
|
|
placeholder: "请填写部门名称"
|
|
}
|
|
},
|
|
rules: {
|
|
required: true,
|
|
message: "部门名称不能为空"
|
|
}
|
|
},
|
|
{
|
|
label: "上级部门",
|
|
prop: "parentId",
|
|
value: e.parentName || "...",
|
|
component: {
|
|
name: "el-input",
|
|
attrs: {
|
|
disabled: true
|
|
}
|
|
}
|
|
},
|
|
{
|
|
label: "排序",
|
|
prop: "orderNum",
|
|
value: e.orderNum || 0,
|
|
component: {
|
|
name: "el-input-number",
|
|
props: {
|
|
"controls-position": "right",
|
|
min: 0,
|
|
max: 100
|
|
}
|
|
}
|
|
}
|
|
],
|
|
on: {
|
|
submit: (data, { done, close }) => {
|
|
this.$service.system.dept[method]({
|
|
id: e.id,
|
|
parentId: e.parentId,
|
|
name: data.name,
|
|
orderNum: data.orderNum
|
|
})
|
|
.then(() => {
|
|
this.$message.success(`新增部门${data.name}成功`);
|
|
close();
|
|
this.refresh();
|
|
})
|
|
.catch((err) => {
|
|
this.$message.error(err);
|
|
done();
|
|
});
|
|
}
|
|
}
|
|
});
|
|
},
|
|
|
|
rowDel(e) {
|
|
const del = (f) => {
|
|
this.$service.system.dept
|
|
.delete({
|
|
ids: e.id,
|
|
deleteUser: f
|
|
})
|
|
.then(() => {
|
|
if (f) {
|
|
this.$message.success("删除成功");
|
|
} else {
|
|
this.$confirm("该部门用户已移动到部门顶级", "删除成功");
|
|
}
|
|
})
|
|
.done(() => {
|
|
this.refresh();
|
|
});
|
|
};
|
|
|
|
this.$confirm("该操作会删除部门下的所有用户,是否确认?", "提示", {
|
|
type: "warning",
|
|
confirmButtonText: "直接删除",
|
|
cancelButtonText: "保留用户",
|
|
distinguishCancelAndClose: true
|
|
})
|
|
.then(() => {
|
|
del(true);
|
|
})
|
|
.catch((action) => {
|
|
if (action == "cancel") {
|
|
del(false);
|
|
}
|
|
});
|
|
},
|
|
|
|
treeOrder(f) {
|
|
if (f) {
|
|
this.$confirm("部门架构已发生改变,是否保存?", "提示", {
|
|
type: "warning"
|
|
})
|
|
.then(() => {
|
|
const deep = (list, pid) => {
|
|
list.forEach((e) => {
|
|
e.parentId = pid;
|
|
ids.push(e);
|
|
|
|
if (e.children && isArray(e.children)) {
|
|
deep(e.children, e.id);
|
|
}
|
|
});
|
|
};
|
|
|
|
let ids = [];
|
|
|
|
deep(this.list, null);
|
|
|
|
this.$service.system.dept
|
|
.order(
|
|
ids.map((e, i) => {
|
|
return {
|
|
id: e.id,
|
|
parentId: e.parentId,
|
|
orderNum: i
|
|
};
|
|
})
|
|
)
|
|
.then(() => {
|
|
this.$message.success("更新排序成功");
|
|
})
|
|
.catch((err) => {
|
|
this.$message.error(err);
|
|
})
|
|
.done(() => {
|
|
this.refresh();
|
|
this.isDrag = false;
|
|
});
|
|
})
|
|
.catch(() => {});
|
|
} else {
|
|
this.refresh();
|
|
}
|
|
}
|
|
}
|
|
};
|
|
</script>
|
|
|
|
<style lang="scss" scoped>
|
|
.cl-dept-tree {
|
|
height: 100%;
|
|
width: 100%;
|
|
|
|
&__header {
|
|
display: flex;
|
|
align-items: center;
|
|
height: 40px;
|
|
padding: 0 10px;
|
|
background-color: #fff;
|
|
letter-spacing: 1px;
|
|
position: relative;
|
|
|
|
div {
|
|
font-size: 14px;
|
|
color: $color-main;
|
|
flex: 1;
|
|
white-space: nowrap;
|
|
}
|
|
|
|
i {
|
|
font-size: 18px;
|
|
color: $color-main;
|
|
cursor: pointer;
|
|
}
|
|
}
|
|
|
|
/deep/.el-tree-node__content {
|
|
height: 36px;
|
|
}
|
|
|
|
&__op {
|
|
display: flex;
|
|
|
|
li {
|
|
display: flex;
|
|
justify-content: center;
|
|
align-items: center;
|
|
list-style: none;
|
|
margin-left: 5px;
|
|
padding: 5px;
|
|
cursor: pointer;
|
|
|
|
&:not(.no):hover {
|
|
background-color: #eee;
|
|
}
|
|
}
|
|
}
|
|
|
|
&__container {
|
|
height: calc(100% - 40px);
|
|
overflow-y: auto;
|
|
overflow-x: hidden;
|
|
|
|
/deep/.el-tree-node__content {
|
|
margin: 0 5px;
|
|
}
|
|
}
|
|
|
|
&__node {
|
|
display: flex;
|
|
align-items: center;
|
|
height: 100%;
|
|
width: 100%;
|
|
box-sizing: border-box;
|
|
|
|
&-label {
|
|
display: flex;
|
|
align-items: center;
|
|
flex: 1;
|
|
height: 100%;
|
|
font-size: 14px;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
white-space: nowrap;
|
|
}
|
|
|
|
&-icon {
|
|
height: 28px;
|
|
width: 28px;
|
|
line-height: 28px;
|
|
text-align: center;
|
|
margin-right: 5px;
|
|
}
|
|
}
|
|
}
|
|
</style>
|