Files
pj_admin_cloud/src/views/admin/system/dept/tree-view.vue
T
2025-07-04 18:03:33 +08:00

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>