refactor: 系统权限模块重构,迁移至PostgreSQL并对齐新设计
- 切换数据库为PostgreSQL,更新依赖与配置 - 重构权限体系,使用sys_user/sys_role/sys_menu等新实体替代旧模型 - 移除旧的用户角色/权限/系统设置相关代码与实体 - 新增字典、菜单管理模块与基础CRUD接口 - 优化鉴权逻辑,基于菜单权限标识进行接口校验 - 前端同步更新API、路由与权限控制逻辑 - 删除冗余的v-permission指令与相关代码
This commit is contained in:
1 parent
98c22f6afb
commit
0a8103f33d
67 files changed
+3263
-1924
No files matched your search
@@ -91,24 +91,29 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, watch, h } from 'vue'
|
||||
import { ref, computed, watch, h, type Component, type VNode } from 'vue'
|
||||
import { useRouter, useRoute } from 'vue-router'
|
||||
import {
|
||||
DashboardOutlined,
|
||||
SettingOutlined,
|
||||
UserOutlined,
|
||||
SafetyCertificateOutlined,
|
||||
ToolOutlined,
|
||||
InfoCircleOutlined,
|
||||
LockOutlined,
|
||||
MenuOutlined,
|
||||
TeamOutlined,
|
||||
ProjectOutlined,
|
||||
ScheduleOutlined,
|
||||
MonitorOutlined,
|
||||
BarChartOutlined,
|
||||
BookOutlined,
|
||||
FileTextOutlined,
|
||||
MenuFoldOutlined,
|
||||
MenuUnfoldOutlined,
|
||||
HomeOutlined,
|
||||
DownOutlined,
|
||||
LogoutOutlined,
|
||||
LockOutlined,
|
||||
} from '@ant-design/icons-vue'
|
||||
import { usePermissionStore } from '@/stores/permission'
|
||||
import { usePermissionStore, type MenuNode } from '@/stores/permission'
|
||||
|
||||
const router = useRouter()
|
||||
const route = useRoute()
|
||||
@@ -119,57 +124,79 @@ const collapsed = ref(false)
|
||||
const selectedKeys = ref<string[]>([])
|
||||
const openKeys = ref<string[]>([])
|
||||
|
||||
// 菜单配置
|
||||
const menuItems = computed(() => [
|
||||
// 菜单图标映射:菜单 icon 字符串 → 图标组件,未知图标显示默认图标
|
||||
const iconMap: Record<string, Component> = {
|
||||
TeamOutlined,
|
||||
ProjectOutlined,
|
||||
ScheduleOutlined,
|
||||
MonitorOutlined,
|
||||
BarChartOutlined,
|
||||
SettingOutlined,
|
||||
UserOutlined,
|
||||
SafetyCertificateOutlined,
|
||||
MenuOutlined,
|
||||
BookOutlined,
|
||||
FileTextOutlined,
|
||||
DashboardOutlined,
|
||||
}
|
||||
|
||||
function resolveIcon(icon?: string | null) {
|
||||
const iconComponent = icon ? iconMap[icon] : undefined
|
||||
return () => h(iconComponent || MenuOutlined)
|
||||
}
|
||||
|
||||
// 侧边栏菜单项结构(与 a-menu items 兼容)
|
||||
interface MenuItem {
|
||||
key: string
|
||||
icon: () => VNode
|
||||
label: string
|
||||
children?: MenuItem[]
|
||||
}
|
||||
|
||||
// 由后端菜单树构建侧边栏菜单:menuType=1 目录 → 子菜单组,menuType=2 菜单 → 菜单项
|
||||
function buildMenuItems(nodes: MenuNode[]): MenuItem[] {
|
||||
const items: MenuItem[] = []
|
||||
for (const node of nodes) {
|
||||
if (node.menuType === 1) {
|
||||
const children = (node.children || [])
|
||||
.filter(child => child.menuType === 2 && child.menuPath)
|
||||
.map(child => ({
|
||||
key: child.menuPath as string,
|
||||
icon: resolveIcon(child.icon),
|
||||
label: child.menuName,
|
||||
}))
|
||||
if (children.length) {
|
||||
items.push({
|
||||
key: node.menuPath || `dir-${node.id}`,
|
||||
icon: resolveIcon(node.icon),
|
||||
label: node.menuName,
|
||||
children,
|
||||
})
|
||||
}
|
||||
} else if (node.menuType === 2 && node.menuPath) {
|
||||
items.push({
|
||||
key: node.menuPath,
|
||||
icon: resolveIcon(node.icon),
|
||||
label: node.menuName,
|
||||
})
|
||||
}
|
||||
}
|
||||
return items
|
||||
}
|
||||
|
||||
// 菜单配置:仪表盘固定展示在最前 + 后端菜单树动态渲染
|
||||
const menuItems = computed<MenuItem[]>(() => [
|
||||
{
|
||||
key: '/dashboard',
|
||||
icon: () => h(DashboardOutlined),
|
||||
label: '仪表盘',
|
||||
},
|
||||
{
|
||||
key: '/system',
|
||||
icon: () => h(SettingOutlined),
|
||||
label: '系统管理',
|
||||
children: [
|
||||
{
|
||||
key: '/system/user',
|
||||
icon: () => h(UserOutlined),
|
||||
label: '用户管理',
|
||||
},
|
||||
{
|
||||
key: '/system/role',
|
||||
icon: () => h(SafetyCertificateOutlined),
|
||||
label: '角色权限',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
key: '/settings',
|
||||
icon: () => h(ToolOutlined),
|
||||
label: '系统设置',
|
||||
children: [
|
||||
{
|
||||
key: '/settings/basic',
|
||||
icon: () => h(InfoCircleOutlined),
|
||||
label: '基本设置',
|
||||
},
|
||||
{
|
||||
key: '/settings/security',
|
||||
icon: () => h(LockOutlined),
|
||||
label: '安全设置',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
key: '/log/operation',
|
||||
icon: () => h(FileTextOutlined),
|
||||
label: '操作日志',
|
||||
},
|
||||
...buildMenuItems(permissionStore.menus),
|
||||
])
|
||||
|
||||
// 计算属性
|
||||
const userInfo = computed(() => permissionStore.userInfo)
|
||||
const displayName = computed(() => userInfo.value?.nickname || userInfo.value?.username || '管理员')
|
||||
const displayName = computed(() => userInfo.value?.realName || userInfo.value?.username || '管理员')
|
||||
const userInitial = computed(() => (displayName.value?.charAt(0) || 'A').toUpperCase())
|
||||
|
||||
// 面包屑列表
|
||||
@@ -381,15 +408,15 @@ function handleUserMenuClick({ key }: { key: string }) {
|
||||
}
|
||||
|
||||
.content {
|
||||
margin: 16px;
|
||||
min-height: calc(100vh - 64px - 32px);
|
||||
margin: 0;
|
||||
min-height: calc(100vh - 64px);
|
||||
|
||||
.view-wrapper {
|
||||
background: #fff;
|
||||
padding: 24px;
|
||||
border-radius: 8px;
|
||||
min-height: calc(100vh - 64px - 32px - 48px); // 减去header、margin和padding
|
||||
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.03);
|
||||
padding: 0;
|
||||
border-radius: 0;
|
||||
min-height: calc(100vh - 64px);
|
||||
box-shadow: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -435,11 +462,11 @@ function handleUserMenuClick({ key }: { key: string }) {
|
||||
}
|
||||
|
||||
.content {
|
||||
margin: 12px;
|
||||
margin: 0;
|
||||
|
||||
.view-wrapper {
|
||||
padding: 16px;
|
||||
border-radius: 4px;
|
||||
padding: 0;
|
||||
border-radius: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user