This commit is contained in:
@@ -0,0 +1,169 @@
|
||||
<template>
|
||||
<div class="page-wrapper tree-wrapper dept-manage-page">
|
||||
<!-- 左侧:只读组树(复用 GroupTreeSearchPanel,无新增/编辑/删除操作栏) -->
|
||||
<div class="tree-container dept-manage-tree">
|
||||
<GroupTreeSearchPanel ref="refTreePanel" v-model:curTreeNode="selectedNode" :treeApi="treeApi"
|
||||
:defaultSearchVisible="true" @on-change="onTreeChange" @on-tree-data="onTreeData" />
|
||||
</div>
|
||||
|
||||
<!-- 右侧:部门管理面板(P0 最小批:只读 + 状态徽标 + 启停) -->
|
||||
<div class="dept-manage-main">
|
||||
<Card dis-hover :bordered="false" class="mb-12">
|
||||
<template #title>
|
||||
<span>部门管理</span>
|
||||
<Tag v-if="deptContainerName" color="primary" class="ml-8">{{ deptContainerName }}</Tag>
|
||||
</template>
|
||||
<Alert v-if="!selectedNode?.id" show-icon type="info">请在左侧选择组织节点</Alert>
|
||||
<template v-else-if="!isDeptContainer(selectedNode)">
|
||||
<Alert show-icon type="warning">
|
||||
当前节点【{{ selectedNode.name }}】不是部门容器(教师组),部门 = 教师组的直接子节点。
|
||||
请选择学校下的「教师组」节点。
|
||||
</Alert>
|
||||
</template>
|
||||
<template v-else>
|
||||
<Alert v-if="deptRows.length === 0" show-icon type="info">
|
||||
该教师组暂无子节点:按口径「空容器自身算部门」,容器本身即可作为部门使用。
|
||||
</Alert>
|
||||
<Table v-else :columns="columns" :data="deptRows" size="small" :border="false">
|
||||
<template #status="{ row }">
|
||||
<Tag :color="isDisabled(row) ? 'error' : 'success'">{{ isDisabled(row) ? '已停用' : '启用' }}</Tag>
|
||||
</template>
|
||||
<template #action="{ row }">
|
||||
<Button v-if="!isDisabled(row)" size="small" type="error" ghost @click="onDisable(row)">停用</Button>
|
||||
<Button v-else size="small" type="success" ghost @click="onEnable(row)">启用</Button>
|
||||
</template>
|
||||
</Table>
|
||||
</template>
|
||||
</Card>
|
||||
<Alert show-icon type="info" class="usage-note">
|
||||
说明:停用只影响「选」(新建事项/流程绑定时选不到),不影响「看」(已有事项展示)与「办」(在途流程照常解析)。
|
||||
新建部门请前往「组,用户管理」建组;本页不提供删除。
|
||||
</Alert>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { computed, ref } from "vue";
|
||||
import Apis from "@/api";
|
||||
import GroupTreeSearchPanel from "@/components/GroupTreeSearchPanel.vue";
|
||||
import MyConfirm from "@/components/MyConfirm";
|
||||
import { Message } from "view-ui-plus";
|
||||
|
||||
/**
|
||||
* 部门管理(2026-10-20 部门线 P0 最小批)。
|
||||
* 口径:部门 = type=7(教师组)的直接子节点;空教师组自身算部门(与 flow-center collectDeptGroups 同源)。
|
||||
* 只做:只读组树 + 状态徽标 + 停用(确认弹窗)/启用(免确认)。
|
||||
* 不做(P1~P3):删除入口、占用弹窗、成员区块、「职务」列、新建部门、「已停用」筛选。
|
||||
*/
|
||||
const TEACHER_GROUP_TYPE = 7;
|
||||
|
||||
const treeApi = () => Apis.SysGroup.getTree();
|
||||
const refTreePanel = ref<InstanceType<typeof GroupTreeSearchPanel>>();
|
||||
const selectedNode = ref<any>({});
|
||||
const treeData = ref<any[]>([]);
|
||||
|
||||
const columns = [
|
||||
{ title: '部门名称', key: 'name', tree: true },
|
||||
{ title: '状态', slot: 'status', width: 100 },
|
||||
{ title: '操作', slot: 'action', width: 100 },
|
||||
];
|
||||
|
||||
const deptContainerName = computed(() =>
|
||||
selectedNode.value?.type === TEACHER_GROUP_TYPE ? selectedNode.value.name : '');
|
||||
|
||||
function onTreeChange(node: any) {
|
||||
selectedNode.value = node || {};
|
||||
}
|
||||
|
||||
function onTreeData(tree: any[]) {
|
||||
treeData.value = tree || [];
|
||||
}
|
||||
|
||||
/** 部门口径判定:type=7 教师组是部门容器 */
|
||||
function isDeptContainer(node: any): boolean {
|
||||
return node?.type === TEACHER_GROUP_TYPE;
|
||||
}
|
||||
|
||||
/** 教师组无子节点时空容器自身算部门(flow-center 同口径) */
|
||||
const deptRows = computed<any[]>(() => {
|
||||
if (!isDeptContainer(selectedNode.value)) return [];
|
||||
const children = selectedNode.value?.children || [];
|
||||
if (children.length === 0) return [selectedNode.value];
|
||||
return children;
|
||||
});
|
||||
|
||||
function isDisabled(row: any): boolean {
|
||||
return row?.status === 1;
|
||||
}
|
||||
|
||||
function onDisable(row: any) {
|
||||
MyConfirm({
|
||||
title: '停用部门',
|
||||
msg: `确定停用部门【${row.name}】?`,
|
||||
info: '停用只影响「选」(新建时选不到),不影响「看」和「办」(已有事项展示、在途流程照常解析)。',
|
||||
async onOk() {
|
||||
try {
|
||||
await Apis.SysGroup.disable({ pathParams: { id: row.id } });
|
||||
Message.success('已停用');
|
||||
await refTreePanel.value?.refreshTree();
|
||||
reselect(row.id);
|
||||
} catch (e: any) {
|
||||
console.warn(e?.message || '停用失败');
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
async function onEnable(row: any) {
|
||||
try {
|
||||
await Apis.SysGroup.enable({ pathParams: { id: row.id } });
|
||||
Message.success('已启用');
|
||||
await refTreePanel.value?.refreshTree();
|
||||
reselect(row.id);
|
||||
} catch (e: any) {
|
||||
console.warn(e?.message || '启用失败');
|
||||
}
|
||||
}
|
||||
|
||||
/** 刷新后尽量回到原选中节点(找不回则清空,用户重选) */
|
||||
function reselect(nodeId: number) {
|
||||
const found = findNodeById(treeData.value, nodeId);
|
||||
if (found) {
|
||||
selectedNode.value = found;
|
||||
refTreePanel.value?.selectNode?.(found);
|
||||
}
|
||||
}
|
||||
|
||||
function findNodeById(list: any[], id: number): any {
|
||||
for (const n of list || []) {
|
||||
if (n?.id === id) return n;
|
||||
const hit = findNodeById(n?.children || [], id);
|
||||
if (hit) return hit;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.dept-manage-page {
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
align-items: stretch;
|
||||
|
||||
.dept-manage-tree {
|
||||
flex: 0 0 320px;
|
||||
min-height: 100%;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.dept-manage-main {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
|
||||
.usage-note {
|
||||
margin-top: 4px;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user