232 lines
5.7 KiB
Vue
232 lines
5.7 KiB
Vue
<template>
|
|
<div style="height: 100%">
|
|
<vue3-tree-org ref="treeOrgRef" :props="props" :data="data" :label-style="style" :define-menus="getMenus"
|
|
:default-expand-level="expandLevel" center :horizontal="horizontal" :collapsable="collapsable"
|
|
:only-one-node="onlyOneNode" :filter-node-method="filterNodeMethod" :clone-node-drag="cloneNodeDrag"
|
|
:node-add="addNode" :node-delete="delNode" :node-edit="editNode" @on-node-click="onNodeClick" />
|
|
</div>
|
|
<dept-form ref="deptDialogRef" @refresh="getOrgData()" />
|
|
</template>
|
|
|
|
<script lang="ts" name="treeView" setup>
|
|
import { useMessage, useMessageBox } from "/@/hooks/message"
|
|
import { delObj, deptTree } from "/@/api/admin/dept"
|
|
import { getObj } from "/@/api/admin/tenant"
|
|
import { Session } from "/@/utils/storage"
|
|
import { useI18n } from "vue-i18n"
|
|
import { storeToRefs } from "pinia"
|
|
import { useThemeConfig } from "/@/stores/themeConfig"
|
|
import { auth } from "/@/utils/authFunction"
|
|
import { useUserInfo } from "/@/stores/userInfo"
|
|
|
|
const DeptForm = defineAsyncComponent(() => import("./form.vue"))
|
|
|
|
const { t } = useI18n()
|
|
|
|
const stores = useUserInfo();
|
|
|
|
// 定义org组件key-value定义
|
|
const props = reactive({ id: "id", pid: "parentId", label: "name", expand: "expand", children: "children" })
|
|
const data = reactive({})
|
|
|
|
// 定义org组件右键定义
|
|
const defineMenus = reactive([
|
|
{ name: t("sysdept.addNodeText"), command: "add" },
|
|
{ name: t("sysdept.editNodeText"), command: "edit" },
|
|
{ name: t("sysdept.delNodeText"), command: "delete" },
|
|
])
|
|
|
|
const cloneNodeDrag = ref(true)
|
|
const collapsable = ref(false)
|
|
const expandLevel = ref(2) //默认展开层级
|
|
const horizontal = ref(false)
|
|
const onlyOneNode = ref(false)
|
|
const treeOrgRef = ref()
|
|
const deptDialogRef = ref()
|
|
|
|
// 添加主题配置
|
|
const themeConfig = useThemeConfig()
|
|
const { themeConfig: theme } = storeToRefs(themeConfig)
|
|
|
|
// 添加 style 定义
|
|
const style = computed(() => ({
|
|
background: theme.value.isDark ? "var(--el-bg-color-overlay)" : "var(--el-bg-color-page)",
|
|
color: theme.value.isDark ? "var(--el-text-color-primary)" : "#5e6d82",
|
|
}))
|
|
|
|
/**
|
|
* 过滤节点
|
|
*/
|
|
const filter = (deptName: string) => {
|
|
treeOrgRef.value.filter(deptName)
|
|
}
|
|
|
|
/**
|
|
* 节点过滤方法
|
|
* @param {string} value 过滤条件
|
|
* @param {object} data 节点数据
|
|
* @returns {boolean} 返回过滤结果
|
|
*/
|
|
const filterNodeMethod = (value, data) => {
|
|
if (!value) return true
|
|
return data.label.indexOf(value) !== -1
|
|
}
|
|
|
|
const getMenus = (event: any, v: any) => {
|
|
const menus = []
|
|
if (stores.userInfos.roleCodes.includes('DEPT_ADMIN')) {
|
|
if (v.id == stores.userInfos.user.deptId) {
|
|
return [{ name: t("sysdept.editNodeText"), command: "edit" }]
|
|
}
|
|
return []
|
|
}
|
|
if (auth("sys_dept_add")) {
|
|
menus.push({ name: t("sysdept.addNodeText"), command: "add" })
|
|
}
|
|
if (auth("sys_dept_edit")) {
|
|
menus.push({ name: t("sysdept.editNodeText"), command: "edit" })
|
|
}
|
|
if (auth("sys_dept_del")) {
|
|
menus.push({ name: t("sysdept.delNodeText"), command: "delete" })
|
|
}
|
|
return menus
|
|
}
|
|
|
|
/**
|
|
* 处理展开/折叠树
|
|
*/
|
|
const handleExpand = async () => {
|
|
collapsable.value = !collapsable.value
|
|
}
|
|
|
|
/**
|
|
* 检查节点是否是根租户节点
|
|
* @param {object} node 节点对象
|
|
* @returns {boolean} 如果节点是根租户节点,返回true;否则返回false
|
|
*/
|
|
const checkNode = (node) => {
|
|
if (node?.id === "0") {
|
|
useMessage().error(t("sysdept.tenantNodeErrorText"))
|
|
return false
|
|
}
|
|
return true
|
|
}
|
|
|
|
/**
|
|
* 当用户左键点击节点,模拟触发组件的右键事件
|
|
* @param e
|
|
*/
|
|
const onNodeClick = (e: any) => {
|
|
const { clientX, clientY } = e
|
|
const rightFun = new MouseEvent("contextmenu", {
|
|
bubbles: true,
|
|
cancelable: false,
|
|
view: window,
|
|
button: 2,
|
|
buttons: 0,
|
|
clientX,
|
|
clientY,
|
|
})
|
|
e.target.dispatchEvent(rightFun)
|
|
}
|
|
|
|
/**
|
|
* 添加部门
|
|
* @param {object} node 节点对象
|
|
*/
|
|
const addNode = (node) => {
|
|
deptDialogRef.value.openDialog("add", node?.id)
|
|
}
|
|
|
|
/**
|
|
* 编辑部门
|
|
* @param {object} node 节点对象
|
|
*/
|
|
const editNode = (node) => {
|
|
if (!checkNode(node)) {
|
|
return
|
|
}
|
|
deptDialogRef.value.openDialog("edit", node?.id)
|
|
}
|
|
|
|
/**
|
|
* 删除部门
|
|
* @param {object} node 节点对象
|
|
*/
|
|
const delNode = async (node) => {
|
|
if (!checkNode(node)) {
|
|
return
|
|
}
|
|
|
|
try {
|
|
await useMessageBox().confirm(t("common.delConfirmText"))
|
|
} catch {
|
|
return
|
|
}
|
|
|
|
try {
|
|
await delObj(node.id)
|
|
await getOrgData()
|
|
useMessage().success(t("common.delSuccessText"))
|
|
} catch (err: any) {
|
|
useMessage().error(err.msg)
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 查询部门数据
|
|
*/
|
|
const getOrgData = async () => {
|
|
// 查询当前租户信息
|
|
const tenant = await getObj(Session.getTenant())
|
|
deptTree().then((res) => {
|
|
Object.assign(data, { id: "0", name: tenant.data.name })
|
|
data.children = res.data
|
|
if (data.children && data.children.length && stores.userInfos.roleCodes.includes('DEPT_ADMIN')) {
|
|
data.children = filterVisibleNodes(data.children)
|
|
}
|
|
})
|
|
}
|
|
|
|
const filterVisibleNodes = (nodes) => {
|
|
if (!Array.isArray(nodes)) return [];
|
|
return nodes.reduce((filtered, node) => {
|
|
if (node.visible) {
|
|
const filteredNode = { ...node };
|
|
if (node.children?.length) {
|
|
filteredNode.children = filterVisibleNodes(node.children);
|
|
}
|
|
filtered.push(filteredNode);
|
|
} else if (node.children?.length) {
|
|
// 如果当前节点不是visible,但是它的子节点可能是visible,所以需要继续检查
|
|
const filteredChildren = filterVisibleNodes(node.children);
|
|
if (filteredChildren.length > 0) {
|
|
filtered.push({ ...node, children: filteredChildren });
|
|
}
|
|
}
|
|
return filtered;
|
|
}, []);
|
|
}
|
|
|
|
onMounted(() => {
|
|
getOrgData()
|
|
})
|
|
|
|
// 暴露变量
|
|
defineExpose({
|
|
getOrgData,
|
|
handleExpand,
|
|
filter,
|
|
})
|
|
</script>
|
|
|
|
<style lang="scss" scoped>
|
|
:deep(.zm-tree-org) {
|
|
height: 100%;
|
|
padding: 15px;
|
|
position: relative;
|
|
background: var(--el-bg-color);
|
|
box-sizing: border-box;
|
|
}
|
|
</style>
|