Files
guoju0808/ry-vue3/src/layout/AdminLayout.vue
T
郭庆泰 44a020da68 feat: 整体废弃 leader 业务角色
leader 角色 (项目负责人) 业务上已不再使用, 整体下线。仅保留 sys_dept.leader 字段 (部门负责人, RuoYi 标准字段, 与角色无关)。

前端清理 (15 文件):
- 路由: 删除 /leader 块 + 4 子路由 (router L56-61)
- 菜单: 删除 AdminLayout 中 leader 菜单 (5 行)
- 视图: 删除整个 views/leader/ 目录 (Home/Projects/Meetings/Account.vue 共 ~32KB)
- Login.vue: 删默认 role='leader', 删 leader 角色选项, 删 autoRole 的 leader 分支, 删 roleHome 的 leader 项
- PortalShell.vue + 5 个 portal/* (Home/Publicity/ArticleView/PublicityDetail/SpecialPlanDetail): admin fallback '/leader/home' → '/admin/workbench', 删 leader 项, 同步更新 admin 入口 (/leader/home → /admin/workbench)
- 5 处 ROLE_LABEL 字典删 leader: '项目负责人' (AdminLayout/Account/Roles/Users/Workbench)
- Roles.vue 注释同步

后端清理 (7 文件):
- BizDashboardController: 删除 /leader dashboard 端点 (整个方法)
- BizPersonController: BUSINESS_MAIN_ROLES 删 leader
- BizExecutorController/BizProjectPlanController/ISysUserService/SysUserExtendVo/SysUser: 注释同步删 leader

测试:
- test_20rounds.py: 删 leader 路由断言 + view_map + role_home + role_view 共 5 处

未做 / 后续:
- DB: sys_user.role_type='leader' 现存账号是测试账号, 用户确认可忽略, 后续手动清理
- 文档: proto/html/leader.html 等原型文件保留 (历史)
- _self/*.md 笔记提及 leader 不动 (笔记)
2026-08-20 22:02:24 +08:00

187 lines
8.2 KiB
Vue

<template>
<el-container class="admin-layout">
<el-aside
v-if="!route.meta?.hideMenu"
width="220px"
class="aside"
>
<div class="aside-logo">
<div class="logo-text">{{ roleLabel }}</div>
<div class="logo-sub">BAHIM 工作台</div>
</div>
<el-menu :default-active="route.path" router class="aside-menu">
<template v-for="m in menu" :key="m.path">
<!-- 二级菜单: children 的用 el-sub-menu -->
<el-sub-menu v-if="m.children" :index="m.path">
<template #title>
<el-icon><component :is="m.icon" /></el-icon>
<span>{{ m.title }}</span>
</template>
<el-menu-item v-for="c in m.children" :key="c.path" :index="c.path">
<el-icon><component :is="c.icon" /></el-icon>
<span>{{ c.title }}</span>
</el-menu-item>
</el-sub-menu>
<!-- 一级菜单 -->
<el-menu-item v-else :index="m.path">
<el-icon><component :is="m.icon" /></el-icon>
<span>{{ m.title }}</span>
</el-menu-item>
</template>
</el-menu>
</el-aside>
<el-container>
<el-header v-if="!route.meta?.hideMenu" class="topbar">
<div class="topbar-left">
<span class="page-title">{{ route.meta?.title || '工作台' }}</span>
</div>
<div class="topbar-right">
<el-button text @click="$router.push('/')">门户首页</el-button>
<el-dropdown trigger="click" @command="onCommand">
<span class="user-name">{{ store.user?.userName || store.user?.displayName }}<el-icon><CaretBottom /></el-icon></span>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item command="logout">退出登录</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
</div>
</el-header>
<el-main class="main" :class="{ 'main-full': route.meta?.hideMenu }">
<router-view />
</el-main>
</el-container>
</el-container>
</template>
<script setup>
import { computed } from 'vue'
import { useRoute, useRouter } from 'vue-router'
import { useUserStore } from '@/store/user'
import { logout as logoutApi } from '@/api/auth'
import { House, Document, Calendar, User, List, OfficeBuilding, Setting, Bell, EditPen, DataAnalysis, Tickets, CaretBottom, Folder, Medal, UserFilled, Connection, Box, Star, Grid, Files, Compass, Collection } from '@element-plus/icons-vue'
const route = useRoute()
const router = useRouter()
const store = useUserStore()
const role = computed(() => route.meta?.role || store.role)
const ROLE_LABEL = { manager: '合规人员', admin: '后台管理员', doctor: '评审专家', executor: '执行人', sponsor: '支持方' }
const roleLabel = computed(() => ROLE_LABEL[role.value] || '工作台')
const MENU = {
admin: [
{ path: '/admin/workbench', title: '工作台', icon: House },
{ path: '/admin/users', title: '用户管理', icon: User },
{ path: '/admin/roles', title: '角色管理', icon: Setting },
{ path: '/admin/projects', title: '项目管理', icon: Document },
{ path: '/admin/library', title: '资料库管理', icon: Box, children: [
{ path: '/admin/experts', title: '专家管理', icon: UserFilled },
{ path: '/admin/sponsor-orgs', title: '支持单位管理', icon: Star },
{ path: '/admin/executor-orgs', title: '服务机构管理', icon: OfficeBuilding },
{ path: '/admin/department', title: '科室管理', icon: Grid },
{ path: '/admin/title', title: '职称管理', icon: Medal },
{ path: '/admin/project-category', title: '项目类别管理', icon: Collection }
]},
{ path: '/admin/manage', title: '网站管理', icon: Setting, children: [
{ path: '/admin/article', title: '协议管理', icon: Files },
{ path: '/admin/special-plan', title: '专项计划管理', icon: Compass },
{ path: '/admin/labor-protocol', title: '劳务协议配置', icon: Document }
]},
{ path: '/admin/account', title: '账号信息', icon: User }
],
manager: [
{ path: '/manager/workbench', title: '工作台', icon: House },
{ path: '/manager/plans', title: '项目策划方案', icon: EditPen },
{ path: '/manager/projects', title: '项目管理', icon: Document },
{ path: '/manager/meetings', title: '会议管理', icon: Calendar },
{ path: '/manager/experts', title: '专家审核', icon: User },
{ path: '/manager/sponsor-orgs', title: '支持单位管理', icon: Connection },
{ path: '/manager/executor-orgs', title: '服务机构管理', icon: OfficeBuilding },
{ path: '/manager/support-intent', title: '支持意向', icon: Tickets },
{ path: '/manager/exec-intent', title: '执行意向', icon: Tickets },
{ path: '/manager/accounts', title: '账号管理', icon: Setting }
],
doctor: [
{ path: '/doctor/home', title: '首页', icon: House },
{ path: '/doctor/meetings', title: '我参与的会议', icon: Calendar },
{ path: '/doctor/projects', title: '我报名的项目', icon: Document },
{ path: '/doctor/messages', title: '消息通知', icon: Bell },
{ path: '/doctor/submissions', title: '我的项目设计投稿', icon: EditPen },
{ path: '/doctor/account', title: '账号信息', icon: User }
],
executor: [
{ path: '/executor/overview', title: '首页', icon: House },
{ path: '/executor/meetings', title: '会议执行', icon: Calendar },
{ path: '/executor/projects', title: '项目列表', icon: Document },
{ path: '/executor/people', title: '人员管理', icon: User, requireMain: true },
{ path: '/executor/labor', title: '劳务凭证', icon: EditPen },
{ path: '/executor/account', title: '账号信息', icon: Setting }
],
sponsor: [
{ path: '/sponsor/home', title: '首页', icon: House },
{ path: '/sponsor/my-projects', title: '我的项目', icon: Document },
{ path: '/sponsor/meetings', title: '会议列表', icon: Calendar },
{ path: '/sponsor/people', title: '人员管理', icon: User, requireMain: true },
{ path: '/sponsor/account', title: '账号信息', icon: Setting }
]
}
const menu = computed(() => (MENU[role.value] || []).filter(item => !item.requireMain || store.isMain))
const logout = async () => {
try { await logoutApi() } catch {}
store.logout()
router.replace('/login')
}
const onCommand = (cmd) => { if (cmd === 'logout') logout() }
</script>
<style scoped>
.admin-layout { height: 100vh; }
.aside { background: #001529; color: #fff; }
.aside-logo { padding: 16px; text-align: center; border-bottom: 1px solid var(--brand-primary-darker); }
.logo-text { color: #fff; font-size: 16px; font-weight: 600; }
.logo-sub { color: #8a99b3; font-size: 12px; }
.aside-menu { background: #001529; border: none; }
/* 一级菜单 + 二级父标题: 统一默认/hover/active 样式 */
:deep(.el-menu-item),
:deep(.el-sub-menu__title) {
color: #c0c4cc;
}
:deep(.el-menu-item:hover),
:deep(.el-sub-menu__title:hover) {
background-color: #1f3a5f;
color: #fff;
}
/* 子项激活: 蓝底白字 */
:deep(.el-menu-item.is-active) {
background: #1890ff;
color: #fff;
}
/* 父标题在子项激活时高亮 (Element Plus 自动给 el-sub-menu 加 is-active) */
:deep(.el-sub-menu.is-active > .el-sub-menu__title) {
color: #fff;
}
/* 二级菜单容器: 跟随侧边栏深蓝底, 不要 Element Plus 默认白底 */
:deep(.el-menu--inline),
:deep(.el-menu--inline .el-menu-item) {
background-color: #001529;
}
:deep(.el-menu--inline .el-menu-item:hover) {
background-color: #1f3a5f;
}
:deep(.el-menu--inline .el-menu-item.is-active) {
background: #1890ff;
}
.topbar { background: #fff; display: flex; align-items: center; justify-content: space-between; padding: 0 24px; border-bottom: 1px solid #ebeef5; }
.topbar-left { display: flex; align-items: center; }
.page-title { font-size: 16px; font-weight: 600; }
.topbar-right { display: flex; align-items: center; gap: 12px; }
.user-name { cursor: pointer; display: inline-flex; align-items: center; gap: 4px; }
.main { background: #f5f7fa; padding: 16px; }
/* 手机页面 (hideMenu): 白底 + 0 padding, 让 form 直接撑满 */
.main-full { background: #fff; padding: 0; }
</style>