feat(权限): 实现基于角色的路由权限控制
- 新增路由元信息类型定义 (requiresAuth, roles, title) - 实现路由守卫中的角色权限校验逻辑 - 新增 403 禁止访问页面 - 提取权限校验函数 checkRoutePermission,提高可测试性 - 修复 JSON.parse 异常处理,增强健壮性 - 优化页面标题动态设置 测试优化: - 重构 global-setup.ts,支持 JAR 文件启动后端服务 - 优化测试用例等待逻辑,减少硬编码延迟 - 简化 playwright 配置,移除多浏览器支持 - 新增路由权限守卫单元测试 关联需求:权限系统完善
This commit is contained in:
@@ -1,53 +1,44 @@
|
||||
export const formatDateTime = (dateTime: string | Date | null | undefined): string => {
|
||||
if (!dateTime) return '-'
|
||||
import { format, parseISO } from 'date-fns'
|
||||
import { zhCN } from 'date-fns/locale'
|
||||
|
||||
export function formatDateTime(date: string | Date | null | undefined): string {
|
||||
if (!date) {
|
||||
return '-'
|
||||
}
|
||||
|
||||
try {
|
||||
const date = typeof dateTime === 'string' ? new Date(dateTime) : dateTime
|
||||
|
||||
const year = date.getFullYear()
|
||||
const month = String(date.getMonth() + 1).padStart(2, '0')
|
||||
const day = String(date.getDate()).padStart(2, '0')
|
||||
const hours = String(date.getHours()).padStart(2, '0')
|
||||
const minutes = String(date.getMinutes()).padStart(2, '0')
|
||||
const seconds = String(date.getSeconds()).padStart(2, '0')
|
||||
|
||||
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`
|
||||
const dateObj = typeof date === 'string' ? parseISO(date) : date
|
||||
return format(dateObj, 'yyyy-MM-dd HH:mm:ss', { locale: zhCN })
|
||||
} catch (error) {
|
||||
console.error('时间格式化失败:', error)
|
||||
return String(dateTime)
|
||||
return '-'
|
||||
}
|
||||
}
|
||||
|
||||
export const formatDate = (date: string | Date | null | undefined): string => {
|
||||
if (!date) return '-'
|
||||
|
||||
export function formatDate(date: string | Date | null | undefined): string {
|
||||
if (!date) {
|
||||
return '-'
|
||||
}
|
||||
|
||||
try {
|
||||
const d = typeof date === 'string' ? new Date(date) : date
|
||||
|
||||
const year = d.getFullYear()
|
||||
const month = String(d.getMonth() + 1).padStart(2, '0')
|
||||
const day = String(d.getDate()).padStart(2, '0')
|
||||
|
||||
return `${year}-${month}-${day}`
|
||||
const dateObj = typeof date === 'string' ? parseISO(date) : date
|
||||
return format(dateObj, 'yyyy-MM-dd', { locale: zhCN })
|
||||
} catch (error) {
|
||||
console.error('日期格式化失败:', error)
|
||||
return String(date)
|
||||
return '-'
|
||||
}
|
||||
}
|
||||
|
||||
export const formatTime = (time: string | Date | null | undefined): string => {
|
||||
if (!time) return '-'
|
||||
|
||||
export function formatTime(date: string | Date | null | undefined): string {
|
||||
if (!date) {
|
||||
return '-'
|
||||
}
|
||||
|
||||
try {
|
||||
const t = typeof time === 'string' ? new Date(time) : time
|
||||
|
||||
const hours = String(t.getHours()).padStart(2, '0')
|
||||
const minutes = String(t.getMinutes()).padStart(2, '0')
|
||||
const seconds = String(t.getSeconds()).padStart(2, '0')
|
||||
|
||||
return `${hours}:${minutes}:${seconds}`
|
||||
const dateObj = typeof date === 'string' ? parseISO(date) : date
|
||||
return format(dateObj, 'HH:mm:ss', { locale: zhCN })
|
||||
} catch (error) {
|
||||
console.error('时间格式化失败:', error)
|
||||
return String(time)
|
||||
return '-'
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,26 +5,29 @@ export interface PermissionMapping {
|
||||
}
|
||||
|
||||
const permissionMapping: PermissionMapping = {
|
||||
'GET /users': 'user:list',
|
||||
'POST /users': 'user:create',
|
||||
'PUT /users': 'user:update',
|
||||
'DELETE /users': 'user:delete',
|
||||
'GET /roles': 'role:list',
|
||||
'POST /roles': 'role:create',
|
||||
'PUT /roles': 'role:update',
|
||||
'DELETE /roles': 'role:delete',
|
||||
'GET /menus': 'menu:list',
|
||||
'POST /menus': 'menu:create',
|
||||
'PUT /menus': 'menu:update',
|
||||
'DELETE /menus': 'menu:delete',
|
||||
'GET /dict': 'dict:list',
|
||||
'POST /dict': 'dict:create',
|
||||
'PUT /dict': 'dict:update',
|
||||
'DELETE /dict': 'dict:delete',
|
||||
'GET /sys/config': 'config:list',
|
||||
'POST /sys/config': 'config:create',
|
||||
'PUT /sys/config': 'config:update',
|
||||
'DELETE /sys/config': 'config:delete',
|
||||
'GET /users': 'system:user:list',
|
||||
'POST /users': 'system:user:add',
|
||||
'PUT /users': 'system:user:edit',
|
||||
'DELETE /users': 'system:user:remove',
|
||||
'GET /roles': 'system:role:list',
|
||||
'POST /roles': 'system:role:add',
|
||||
'PUT /roles': 'system:role:edit',
|
||||
'DELETE /roles': 'system:role:remove',
|
||||
'GET /menus': 'system:menu:list',
|
||||
'POST /menus': 'system:menu:add',
|
||||
'PUT /menus': 'system:menu:edit',
|
||||
'DELETE /menus': 'system:menu:remove',
|
||||
'GET /dict': 'system:dict:list',
|
||||
'POST /dict': 'system:dict:add',
|
||||
'PUT /dict': 'system:dict:edit',
|
||||
'DELETE /dict': 'system:dict:remove',
|
||||
'GET /sys/config': 'system:config:list',
|
||||
'POST /sys/config': 'system:config:add',
|
||||
'PUT /sys/config': 'system:config:edit',
|
||||
'DELETE /sys/config': 'system:config:remove',
|
||||
'GET /files': 'system:file:list',
|
||||
'POST /files': 'system:file:upload',
|
||||
'DELETE /files': 'system:file:delete',
|
||||
}
|
||||
|
||||
export function checkApiPermission(method: string, url: string): boolean {
|
||||
@@ -37,6 +40,10 @@ export function checkApiPermission(method: string, url: string): boolean {
|
||||
return true
|
||||
}
|
||||
|
||||
if (key === 'GET /menus') {
|
||||
return true
|
||||
}
|
||||
|
||||
if (Array.isArray(requiredPermission)) {
|
||||
return requiredPermission.some(p => permissionStore.hasPermission(p))
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import CryptoJS from 'crypto-js'
|
||||
|
||||
const SIGNATURE_SECRET = 'NovalonManageSystemSecretKey2026'
|
||||
const SIGNATURE_SECRET = import.meta.env.VITE_SIGNATURE_SECRET || 'NovalonManageSystemSecretKey2026'
|
||||
|
||||
export interface SignatureHeaders {
|
||||
'X-Signature': string
|
||||
|
||||
Reference in New Issue
Block a user