This commit is contained in:
@@ -30,6 +30,8 @@ export default {
|
|||||||
'SysGroup.move': ['POST', '/api/sysgroup/Move'],
|
'SysGroup.move': ['POST', '/api/sysgroup/Move'],
|
||||||
'SysGroup.sysGroupEdit': ['POST', '/api/sysgroup/Edit/{id}'],
|
'SysGroup.sysGroupEdit': ['POST', '/api/sysgroup/Edit/{id}'],
|
||||||
'SysGroup.sysGroupRemove': ['POST', '/api/sysgroup/Delete/{id}'],
|
'SysGroup.sysGroupRemove': ['POST', '/api/sysgroup/Delete/{id}'],
|
||||||
|
'SysGroup.disable': ['POST', '/api/sysgroup/Disable/{id}'],
|
||||||
|
'SysGroup.enable': ['POST', '/api/sysgroup/Enable/{id}'],
|
||||||
'SysGroup.batchAdd': ['POST', '/api/sysgroup/BatchAdd'],
|
'SysGroup.batchAdd': ['POST', '/api/sysgroup/BatchAdd'],
|
||||||
'SysGroup.sysGroupAdd': ['POST', '/api/sysgroup/Add'],
|
'SysGroup.sysGroupAdd': ['POST', '/api/sysgroup/Add'],
|
||||||
'PushSubscription.test': ['POST', '/api/push/Test'],
|
'PushSubscription.test': ['POST', '/api/push/Test'],
|
||||||
|
|||||||
Vendored
+46
@@ -1538,6 +1538,52 @@ declare global {
|
|||||||
>(
|
>(
|
||||||
config: Config
|
config: Config
|
||||||
): Alova2Method<null, 'SysGroup.sysGroupRemove', Config>;
|
): Alova2Method<null, 'SysGroup.sysGroupRemove', Config>;
|
||||||
|
/**
|
||||||
|
* ---
|
||||||
|
*
|
||||||
|
* [POST]
|
||||||
|
*
|
||||||
|
* **path:** /api/sysgroup/Disable/{id}
|
||||||
|
*
|
||||||
|
* ---
|
||||||
|
*
|
||||||
|
* **Response**
|
||||||
|
* ```ts
|
||||||
|
* type Response = null
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
disable<
|
||||||
|
Config extends Alova2MethodConfig<null> & {
|
||||||
|
pathParams: {
|
||||||
|
id: number;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
>(
|
||||||
|
config: Config
|
||||||
|
): Alova2Method<null, 'SysGroup.disable', Config>;
|
||||||
|
/**
|
||||||
|
* ---
|
||||||
|
*
|
||||||
|
* [POST]
|
||||||
|
*
|
||||||
|
* **path:** /api/sysgroup/Enable/{id}
|
||||||
|
*
|
||||||
|
* ---
|
||||||
|
*
|
||||||
|
* **Response**
|
||||||
|
* ```ts
|
||||||
|
* type Response = null
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
enable<
|
||||||
|
Config extends Alova2MethodConfig<null> & {
|
||||||
|
pathParams: {
|
||||||
|
id: number;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
>(
|
||||||
|
config: Config
|
||||||
|
): Alova2Method<null, 'SysGroup.enable', Config>;
|
||||||
/**
|
/**
|
||||||
* ---
|
* ---
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -48,6 +48,7 @@ export function getMenuList(userRole?: UserRoleEnum): Promise<IModule[]> {
|
|||||||
{ id: 42, modId: 1104, name: "组授权", page: "/group-auth", roles: [UserRoleEnum.SuperAdmin, UserRoleEnum.Bureau, UserRoleEnum.School] },
|
{ id: 42, modId: 1104, name: "组授权", page: "/group-auth", roles: [UserRoleEnum.SuperAdmin, UserRoleEnum.Bureau, UserRoleEnum.School] },
|
||||||
{ id: 44, modId: 1110, name: "角色授权", page: "/role-authz/list", roles: [UserRoleEnum.SuperAdmin, UserRoleEnum.Bureau, UserRoleEnum.School] },
|
{ id: 44, modId: 1110, name: "角色授权", page: "/role-authz/list", roles: [UserRoleEnum.SuperAdmin, UserRoleEnum.Bureau, UserRoleEnum.School] },
|
||||||
{ id: 43, modId: 1105, name: "权限一览", page: "/permission-view", roles: [UserRoleEnum.SuperAdmin, UserRoleEnum.Bureau, UserRoleEnum.School] },
|
{ id: 43, modId: 1105, name: "权限一览", page: "/permission-view", roles: [UserRoleEnum.SuperAdmin, UserRoleEnum.Bureau, UserRoleEnum.School] },
|
||||||
|
{ id: 45, modId: 1106, name: "部门管理", page: "/dept-manage", roles: [UserRoleEnum.SuperAdmin, UserRoleEnum.Bureau, UserRoleEnum.School] },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
// 4. 日志管理(父目录)
|
// 4. 日志管理(父目录)
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import PermissionView from "@/views/PermissionView.vue";
|
|||||||
import OperationLogList from "@/views/oplog/OperationLogList.vue";
|
import OperationLogList from "@/views/oplog/OperationLogList.vue";
|
||||||
import OapiLogList from "@/views/oapilog/OapiLogList.vue";
|
import OapiLogList from "@/views/oapilog/OapiLogList.vue";
|
||||||
import PushLog from "@/views/push/PushLog.vue";
|
import PushLog from "@/views/push/PushLog.vue";
|
||||||
|
import DeptManage from "@/views/dept-manage/index.vue";
|
||||||
import NoAuth from "@/views/NoAuth.vue";
|
import NoAuth from "@/views/NoAuth.vue";
|
||||||
|
|
||||||
// 管理端四级访问控制:meta.roles 为空/未声明 = 登录即可访问
|
// 管理端四级访问控制:meta.roles 为空/未声明 = 登录即可访问
|
||||||
@@ -105,6 +106,14 @@ const routes: RouteRecordRaw[] = [
|
|||||||
component: PermissionView,
|
component: PermissionView,
|
||||||
meta: { title: "权限一览", icon: "icon-list", modId: 1105, roles: ALL_ADMIN },
|
meta: { title: "权限一览", icon: "icon-list", modId: 1105, roles: ALL_ADMIN },
|
||||||
},
|
},
|
||||||
|
// ===== 部门管理(2026-10-20 部门线 P0 最小批:只读树 + 状态徽标 + 启停)=====
|
||||||
|
// 挂「权限管理」分组(菜单 id=45);无删除/成员/职务/新建入口——建部门=建组回「组,用户管理」
|
||||||
|
{
|
||||||
|
path: "/dept-manage",
|
||||||
|
name: "DeptManage",
|
||||||
|
component: DeptManage,
|
||||||
|
meta: { title: "部门管理", icon: "icon-list", modId: 1106, parentId: 1108, roles: ALL_ADMIN },
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: "/push-log",
|
path: "/push-log",
|
||||||
name: "PushLog",
|
name: "PushLog",
|
||||||
|
|||||||
@@ -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