Files
django-vue3-admin/web/src/views/system/dept/components/TreeCom.vue
2023-07-31 14:49:12 +08:00

304 lines
7.0 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<el-input v-model="filterVal" :prefix-icon="Search" placeholder="请输入部门名称" />
<div class="dept-tree-com">
<div class="tc-head">
<el-icon size="16" color="#606266" class="tc-head-icon">
<HomeFilled />
</el-icon>
<span class="tc-head-txt">部门架构</span>
<el-icon size="16" color="#606266" @click="() => (showTotalNum = !showTotalNum)" class="tc-head-icon">
<View v-show="!showTotalNum" />
<Hide v-show="showTotalNum" />
</el-icon>
</div>
<el-tree
ref="treeRef"
:data="treeData"
:props="defaultTreeProps"
:filter-node-method="handleFilterTreeNode"
:load="handleLoadNode"
lazy
:indent="38"
@node-click="handleNodeClick"
highlight-current
>
<template #default="{ node, data }">
<element-tree-line :node="node" :showLabelLine="false" :indent="32">
<span v-if="data.status" class="text-center font-black font-normal">
<SvgIcon name="iconfont icon-shouye" color="var(--el-color-primary)" />&nbsp;{{ node.label }}
<span v-show="showTotalNum">{{ data.dept_user_count }}</span>
</span>
<span v-else color="var(--el-color-primary)"> <SvgIcon name="iconfont icon-shouye" />&nbsp;{{ node.label }} </span>
</element-tree-line>
</template>
</el-tree>
<div class="tree-tags">
<el-tooltip effect="dark" content="新增">
<el-icon size="16" @click="handleUpdateMenu('create')" class="mlt-icon">
<Plus />
</el-icon>
</el-tooltip>
<el-tooltip effect="dark" content="编辑">
<el-icon size="16" @click="handleUpdateMenu('update')" class="mlt-icon">
<Edit />
</el-icon>
</el-tooltip>
<el-tooltip effect="dark" content="上移">
<el-icon size="16" @click="handleSort('up')" class="mlt-icon">
<Top />
</el-icon>
</el-tooltip>
<el-tooltip effect="dark" content="下移">
<el-icon size="16" @click="handleSort('down')" class="mlt-icon">
<Bottom />
</el-icon>
</el-tooltip>
<el-tooltip effect="dark" content="删除">
<el-icon size="16" @click="handleDeleteDept" class="mlt-icon">
<Delete />
</el-icon>
</el-tooltip>
</div>
</div>
</template>
<script lang="ts" setup>
import { ref, watch, toRaw, h } from 'vue';
import { ElTree } from 'element-plus';
import { getElementLabelLine } from 'element-tree-line';
import { Search } from '@element-plus/icons-vue';
import { lazyLoadDept, deptMoveUp, deptMoveDown } from '../api';
import { warningNotification } from '../../../../utils/message';
import { TreeItemType, APIResponseData } from '../types';
import type Node from 'element-plus/es/components/tree/src/model/node';
interface IProps {
treeData: TreeItemType[];
}
const ElementTreeLine = getElementLabelLine(h);
const defaultTreeProps: any = {
children: 'children',
label: 'name',
isLeaf: (data: TreeItemType[], node: Node) => {
if (node.data.hasChild) {
return false;
} else {
return true;
}
},
};
withDefaults(defineProps<IProps>(), {
treeData: () => [],
});
const emit = defineEmits(['treeClick', 'deleteDept', 'updateDept']);
let filterVal = ref('');
let showTotalNum = ref(false);
let sortDisable = ref(false);
let treeSelectDept = ref<TreeItemType>({});
let treeSelectNode = ref<Node | null>(null);
const treeRef = ref<InstanceType<typeof ElTree>>();
watch(filterVal, (val) => {
treeRef.value!.filter(val);
});
/**
* 部门树的搜索事件
*/
const handleFilterTreeNode = (value: string, data: TreeItemType) => {
if (!value) return true;
return toRaw(data).name?.indexOf(value) !== -1;
};
/**
* 部门树的懒加载
*/
const handleLoadNode = (node: Node, resolve: Function) => {
if (node.level !== 0) {
lazyLoadDept({ parent: node.data.id }).then((res: APIResponseData) => {
resolve(res.data);
});
}
};
/**
* 部门的点击事件
*/
const handleNodeClick = (data: TreeItemType, node: Node) => {
treeSelectDept.value = data;
treeSelectNode.value = node;
emit('treeClick', data.id);
};
/**
* 新增 or 编辑 操作
*/
const handleUpdateMenu = (type: string) => {
if (type === 'update') {
if (!treeSelectDept.value.id) {
warningNotification('请选择菜单!');
return;
}
emit('updateDept', type, treeSelectDept.value);
} else {
emit('updateDept', type);
}
};
/**
* 删除部门
*/
const handleDeleteDept = () => {
if (!treeSelectDept.value.id) {
warningNotification('请选择菜单!');
return;
}
emit('deleteDept', treeSelectDept.value.id, () => {
treeSelectDept.value = {};
});
};
/**
* 部门上下移动操作
*/
const handleSort = async (type: string) => {
if (!treeSelectDept.value.id) {
warningNotification('请选择菜单!');
return;
}
if (sortDisable.value) return;
const parentList = treeSelectNode.value?.parent.childNodes || [];
const index = parentList.findIndex((i) => i.data.id === treeSelectDept.value.id);
const record = parentList.find((i) => i.data.id === treeSelectDept.value.id);
if (type === 'up') {
if (index === 0) return;
parentList.splice(index - 1, 0, record as any);
parentList.splice(index + 1, 1);
sortDisable.value = true;
await deptMoveUp({ dept_id: treeSelectDept.value.id });
sortDisable.value = false;
}
if (type === 'down') {
parentList.splice(index + 2, 0, record as any);
parentList.splice(index, 1);
sortDisable.value = true;
await deptMoveDown({ dept_id: treeSelectDept.value.id });
sortDisable.value = false;
}
};
</script>
<style lang="scss" scoped>
.tc-head {
display: flex;
align-items: center;
margin-left: -8px;
color: #606266;
font-weight: 600;
.tc-head-txt {
margin: 0 8px;
}
.tc-head-icon {
position: relative;
top: -1px;
cursor: pointer;
}
}
.tree-tags {
height: 40px;
position: absolute;
bottom: 0;
left: 0;
right: 0;
padding: 0 20px;
display: flex;
align-items: center;
justify-content: space-around;
box-sizing: border-box;
.mlt-icon {
cursor: pointer;
color: var(--el-color-primary);
}
}
</style>
<style lang="scss">
.dept-tree-com {
height: calc(100% - 60px);
padding: 20px;
box-sizing: border-box;
overflow-y: auto;
.el-tree-node__content {
height: 32px !important;
}
.el-tree .el-tree-node__expand-icon svg {
display: none !important;
height: 0;
width: 0;
}
.el-tree-node__expand-icon {
font-size: 16px;
}
.el-tree-node__content > .el-tree-node__expand-icon {
padding: 0;
box-sizing: border-box;
margin-right: 5px;
margin-left: 20px;
}
.el-tree .el-tree-node__expand-icon.expanded {
-webkit-transform: rotate(0deg);
transform: rotate(0deg);
}
.el-tree .el-tree-node__expand-icon.is-leaf {
margin-left: 0;
}
.el-tree .el-tree-node__expand-icon:before {
background: url('../../../../assets/img/menu-tree-show-icon.png') no-repeat center / 100%;
content: '';
display: block;
width: 24px;
height: 24px;
}
.el-tree .el-tree-node__expand-icon.expanded:before {
background: url('../../../../assets/img/menu-tree-hidden-icon.png') no-repeat center / 100%;
content: '';
display: block;
width: 24px;
height: 24px;
}
.el-tree .is-leaf.el-tree-node__expand-icon::before {
display: block;
background: none !important;
content: '';
width: 18px;
height: 18px;
border: none;
}
}
</style>