diff --git a/novalon-manage-web/src/test/components/ConfigManagement.test.ts b/novalon-manage-web/src/__tests__/components/ConfigManagement.test.ts similarity index 100% rename from novalon-manage-web/src/test/components/ConfigManagement.test.ts rename to novalon-manage-web/src/__tests__/components/ConfigManagement.test.ts diff --git a/novalon-manage-web/src/test/components/Dashboard.test.ts b/novalon-manage-web/src/__tests__/components/Dashboard.test.ts similarity index 100% rename from novalon-manage-web/src/test/components/Dashboard.test.ts rename to novalon-manage-web/src/__tests__/components/Dashboard.test.ts diff --git a/novalon-manage-web/src/test/components/DictManagement.test.ts b/novalon-manage-web/src/__tests__/components/DictManagement.test.ts similarity index 100% rename from novalon-manage-web/src/test/components/DictManagement.test.ts rename to novalon-manage-web/src/__tests__/components/DictManagement.test.ts diff --git a/novalon-manage-web/src/test/components/ExceptionLog.test.ts b/novalon-manage-web/src/__tests__/components/ExceptionLog.test.ts similarity index 100% rename from novalon-manage-web/src/test/components/ExceptionLog.test.ts rename to novalon-manage-web/src/__tests__/components/ExceptionLog.test.ts diff --git a/novalon-manage-web/src/test/components/FileManagement.test.ts b/novalon-manage-web/src/__tests__/components/FileManagement.test.ts similarity index 100% rename from novalon-manage-web/src/test/components/FileManagement.test.ts rename to novalon-manage-web/src/__tests__/components/FileManagement.test.ts diff --git a/novalon-manage-web/src/test/components/Login.test.ts b/novalon-manage-web/src/__tests__/components/Login.test.ts similarity index 100% rename from novalon-manage-web/src/test/components/Login.test.ts rename to novalon-manage-web/src/__tests__/components/Login.test.ts diff --git a/novalon-manage-web/src/test/components/LoginLog.test.ts b/novalon-manage-web/src/__tests__/components/LoginLog.test.ts similarity index 100% rename from novalon-manage-web/src/test/components/LoginLog.test.ts rename to novalon-manage-web/src/__tests__/components/LoginLog.test.ts diff --git a/novalon-manage-web/src/test/components/MenuManagement.test.ts b/novalon-manage-web/src/__tests__/components/MenuManagement.test.ts similarity index 100% rename from novalon-manage-web/src/test/components/MenuManagement.test.ts rename to novalon-manage-web/src/__tests__/components/MenuManagement.test.ts diff --git a/novalon-manage-web/src/test/components/NoticeManagement.test.ts b/novalon-manage-web/src/__tests__/components/NoticeManagement.test.ts similarity index 100% rename from novalon-manage-web/src/test/components/NoticeManagement.test.ts rename to novalon-manage-web/src/__tests__/components/NoticeManagement.test.ts diff --git a/novalon-manage-web/src/test/components/OperationLog.test.ts b/novalon-manage-web/src/__tests__/components/OperationLog.test.ts similarity index 100% rename from novalon-manage-web/src/test/components/OperationLog.test.ts rename to novalon-manage-web/src/__tests__/components/OperationLog.test.ts diff --git a/novalon-manage-web/src/test/components/RoleManagement.test.ts b/novalon-manage-web/src/__tests__/components/RoleManagement.test.ts similarity index 100% rename from novalon-manage-web/src/test/components/RoleManagement.test.ts rename to novalon-manage-web/src/__tests__/components/RoleManagement.test.ts diff --git a/novalon-manage-web/src/test/components/UserManagement.test.ts b/novalon-manage-web/src/__tests__/components/UserManagement.test.ts similarity index 100% rename from novalon-manage-web/src/test/components/UserManagement.test.ts rename to novalon-manage-web/src/__tests__/components/UserManagement.test.ts diff --git a/novalon-manage-web/src/test/config.test.ts b/novalon-manage-web/src/__tests__/config.test.ts similarity index 100% rename from novalon-manage-web/src/test/config.test.ts rename to novalon-manage-web/src/__tests__/config.test.ts diff --git a/novalon-manage-web/src/test/fixtures.ts b/novalon-manage-web/src/__tests__/fixtures.ts similarity index 100% rename from novalon-manage-web/src/test/fixtures.ts rename to novalon-manage-web/src/__tests__/fixtures.ts diff --git a/novalon-manage-web/src/test/setup.ts b/novalon-manage-web/src/__tests__/setup.ts similarity index 100% rename from novalon-manage-web/src/test/setup.ts rename to novalon-manage-web/src/__tests__/setup.ts diff --git a/novalon-manage-web/src/test/utils.ts b/novalon-manage-web/src/__tests__/utils.ts similarity index 100% rename from novalon-manage-web/src/test/utils.ts rename to novalon-manage-web/src/__tests__/utils.ts diff --git a/novalon-manage-web/src/test/utils/errorHandler.test.ts b/novalon-manage-web/src/__tests__/utils/errorHandler.test.ts similarity index 100% rename from novalon-manage-web/src/test/utils/errorHandler.test.ts rename to novalon-manage-web/src/__tests__/utils/errorHandler.test.ts diff --git a/novalon-manage-web/src/api/user.api.ts b/novalon-manage-web/src/api/user.api.ts index c06e4de..7eac5d2 100644 --- a/novalon-manage-web/src/api/user.api.ts +++ b/novalon-manage-web/src/api/user.api.ts @@ -2,14 +2,14 @@ import request from '@/utils/request' import { UserStatus } from '@/constants/status' export interface User { - id: number + id: string username: string nickname: string email: string phone: string avatar: string status: UserStatus - roles: number[] + roles: string[] createdAt: string updatedAt: string } @@ -20,7 +20,7 @@ export interface CreateUserRequest { nickname: string email: string phone: string - roles?: number[] + roles?: string[] } export interface UpdateUserRequest { @@ -29,7 +29,7 @@ export interface UpdateUserRequest { phone?: string avatar?: string status?: UserStatus - roles?: number[] + roles?: string[] } export interface UserPageRequest { @@ -60,27 +60,27 @@ export const userApi = { getPage: (params: UserPageRequest) => request.get>('/users/page', { params }), - getById: (id: number) => + getById: (id: string) => request.get(`/users/${id}`), create: (data: CreateUserRequest) => request.post('/users', data), - update: (id: number, data: UpdateUserRequest) => + update: (id: string, data: UpdateUserRequest) => request.put(`/users/${id}`, data), - delete: (id: number) => + delete: (id: string) => request.delete(`/users/${id}`), - batchDelete: (ids: number[]) => + batchDelete: (ids: string[]) => request.post('/users/batch-delete', { ids }), - resetPassword: (id: number) => + resetPassword: (id: string) => request.post(`/users/${id}/reset-password`), - updateStatus: (id: number, status: UserStatus) => + updateStatus: (id: string, status: UserStatus) => request.put(`/users/${id}/status`, { status }), - assignRoles: (id: number, roleIds: number[]) => + assignRoles: (id: string, roleIds: string[]) => request.post(`/users/${id}/roles`, { roleIds }), } diff --git a/novalon-manage-web/src/router/index.ts b/novalon-manage-web/src/router/index.ts index 7985b60..13b66b8 100644 --- a/novalon-manage-web/src/router/index.ts +++ b/novalon-manage-web/src/router/index.ts @@ -1,5 +1,5 @@ import { createRouter, createWebHistory } from 'vue-router' -import type { RouteRecordRaw } from 'vue-router' +import type { RouteRecordRaw, RouteLocationNormalized } from 'vue-router' declare module 'vue-router' { interface RouteMeta { diff --git a/novalon-manage-web/src/stores/permission.ts b/novalon-manage-web/src/stores/permission.ts index 62a1bb1..119fff9 100644 --- a/novalon-manage-web/src/stores/permission.ts +++ b/novalon-manage-web/src/stores/permission.ts @@ -2,19 +2,19 @@ import { defineStore } from 'pinia' import request from '@/utils/request' export interface MenuItem { - id: number + id: string name: string path: string icon?: string - parentId?: number + parentId?: string sort: number children?: MenuItem[] } interface BackendMenuItem { - id: number + id: string menuName: string - parentId: number + parentId: string orderNum: number menuType: string perms?: string @@ -24,7 +24,7 @@ interface BackendMenuItem { } function transformMenuData(backendMenus: BackendMenuItem[]): MenuItem[] { - const menuMap = new Map() + const menuMap = new Map() const rootMenus: MenuItem[] = [] const componentToPathMap: Record = { @@ -48,7 +48,7 @@ function transformMenuData(backendMenus: BackendMenuItem[]): MenuItem[] { name: menu.menuName, path: menu.component ? (componentToPathMap[menu.component] || `/${menu.component.replace('/index', '').replace('system/', '')}`) : '', icon: getMenuIcon(menu.menuName), - parentId: menu.parentId === 0 ? undefined : menu.parentId, + parentId: menu.parentId === '0' ? undefined : menu.parentId, sort: menu.orderNum } menuMap.set(menu.id, menuItem) @@ -56,7 +56,7 @@ function transformMenuData(backendMenus: BackendMenuItem[]): MenuItem[] { filteredMenus.forEach(menu => { const menuItem = menuMap.get(menu.id)! - if (menu.parentId === 0) { + if (menu.parentId === '0') { rootMenus.push(menuItem) } else { const parentMenu = menuMap.get(menu.parentId) diff --git a/novalon-manage-web/src/types/axios.d.ts b/novalon-manage-web/src/types/axios.d.ts new file mode 100644 index 0000000..4a5cf60 --- /dev/null +++ b/novalon-manage-web/src/types/axios.d.ts @@ -0,0 +1,13 @@ +import { AxiosRequestConfig, AxiosInstance } from 'axios' + +declare module 'axios' { + interface AxiosInstance { + request(config: AxiosRequestConfig): Promise + get(url: string, config?: AxiosRequestConfig): Promise + delete(url: string, config?: AxiosRequestConfig): Promise + head(url: string, config?: AxiosRequestConfig): Promise + post(url: string, data?: any, config?: AxiosRequestConfig): Promise + put(url: string, data?: any, config?: AxiosRequestConfig): Promise + patch(url: string, data?: any, config?: AxiosRequestConfig): Promise + } +} diff --git a/novalon-manage-web/src/utils/permission.ts b/novalon-manage-web/src/utils/permission.ts index de17deb..f5c800d 100644 --- a/novalon-manage-web/src/utils/permission.ts +++ b/novalon-manage-web/src/utils/permission.ts @@ -21,20 +21,28 @@ const permissionMapping: PermissionMapping = { 'POST /dict': 'system:dict:add', 'PUT /dict': 'system:dict:edit', 'DELETE /dict': 'system:dict:remove', + 'GET /config': 'system:config:list', + 'POST /config': 'system:config:list', + 'PUT /config': 'system:config:list', + 'DELETE /config': 'system:config:list', 'GET /sys/config': 'system:config:list', - 'POST /sys/config': 'system:config:add', - 'PUT /sys/config': 'system:config:edit', - 'DELETE /sys/config': 'system:config:remove', + 'POST /sys/config': 'system:config:list', + 'PUT /sys/config': 'system:config:list', + 'DELETE /sys/config': 'system:config:list', 'GET /files': 'system:file:list', 'POST /files': 'system:file:upload', 'DELETE /files': 'system:file:delete', } export function checkApiPermission(method: string, url: string): boolean { - const permissionStore = usePermissionStore() - const key = `${method.toUpperCase()} ${url.split('?')[0]}` - const requiredPermission = permissionMapping[key] + let requiredPermission = permissionMapping[key] + + if (!requiredPermission) { + const baseUrl = url.split('?')[0].replace(/\/\d+$/, '') + const baseKey = `${method.toUpperCase()} ${baseUrl}` + requiredPermission = permissionMapping[baseKey] + } if (!requiredPermission) { return true @@ -44,11 +52,24 @@ export function checkApiPermission(method: string, url: string): boolean { return true } - if (Array.isArray(requiredPermission)) { - return requiredPermission.some(p => permissionStore.hasPermission(p)) + const stored = localStorage.getItem('permission') + if (!stored) { + return true } - return permissionStore.hasPermission(requiredPermission) + try { + const data = JSON.parse(stored) + const permissions = data.permissions || [] + + if (Array.isArray(requiredPermission)) { + return requiredPermission.some(p => permissions.includes(p)) + } + + return permissions.includes(requiredPermission) + } catch (error) { + console.error('解析权限数据失败:', error) + return true + } } export function getRequiredPermission(method: string, url: string): string | string[] | null { diff --git a/novalon-manage-web/src/utils/request.ts b/novalon-manage-web/src/utils/request.ts index 8202357..9d14ac4 100644 --- a/novalon-manage-web/src/utils/request.ts +++ b/novalon-manage-web/src/utils/request.ts @@ -1,4 +1,4 @@ -import axios, { AxiosRequestConfig } from 'axios' +import axios, { InternalAxiosRequestConfig } from 'axios' import { generateSignatureHeaders } from './signature' import { checkApiPermission } from './permission' @@ -8,7 +8,7 @@ const request = axios.create({ }) request.interceptors.request.use( - (config: AxiosRequestConfig) => { + (config: InternalAxiosRequestConfig) => { const token = localStorage.getItem('token') if (token) { config.headers = config.headers || {} diff --git a/novalon-manage-web/src/utils/signature.ts b/novalon-manage-web/src/utils/signature.ts index bd0a3b6..5d9ddac 100644 --- a/novalon-manage-web/src/utils/signature.ts +++ b/novalon-manage-web/src/utils/signature.ts @@ -16,7 +16,7 @@ export function generateSignature( timestamp: number, nonce: string ): string { - const stringToSign = buildStringToSign(method, path, query, '', timestamp, nonce) + const stringToSign = buildStringToSign(method, path, query, body, timestamp, nonce) const signature = CryptoJS.HmacSHA256(stringToSign, SIGNATURE_SECRET) const signatureBase64 = CryptoJS.enc.Base64.stringify(signature) @@ -33,13 +33,12 @@ export function generateSignatureHeaders( const nonce = generateNonce() const { path, query } = parseUrl(url) - const bodyString = body ? JSON.stringify(body) : '' const signature = generateSignature( method.toUpperCase(), path, query || '', - bodyString, + '', timestamp, nonce ) diff --git a/novalon-manage-web/src/views/config/ConfigManagement.vue b/novalon-manage-web/src/views/config/ConfigManagement.vue index 51ac3c8..0221931 100644 --- a/novalon-manage-web/src/views/config/ConfigManagement.vue +++ b/novalon-manage-web/src/views/config/ConfigManagement.vue @@ -154,15 +154,21 @@ const handleDelete = async (row: any) => { const handleModalOk = async () => { try { + console.log('handleModalOk called, formState:', formState) if (formState.id) { - await request.put(`/config/${formState.id}`, formState) + console.log('Sending PUT request to /config/' + formState.id) + const response = await request.put(`/config/${formState.id}`, formState) + console.log('PUT response:', response) } else { - await request.post('/config', formState) + console.log('Sending POST request to /config') + const response = await request.post('/config', formState) + console.log('POST response:', response) } ElMessage.success('操作成功') modalVisible.value = false fetchData() - } catch { + } catch (error) { + console.error('handleModalOk error:', error) ElMessage.error('操作失败') } } diff --git a/novalon-manage-web/src/views/config/DictManagement.vue b/novalon-manage-web/src/views/config/DictManagement.vue index 42ebde0..4f922c1 100644 --- a/novalon-manage-web/src/views/config/DictManagement.vue +++ b/novalon-manage-web/src/views/config/DictManagement.vue @@ -159,7 +159,7 @@ const handleDelete = async (row: any) => { cancelButtonText: '取消', type: 'warning' }) - await request.delete(`/dict/${row.id}`) + await request.delete(`/dict/types/${row.id}`) ElMessage.success('删除成功') fetchData() } catch (error) { diff --git a/novalon-manage-web/src/views/system/Dashboard.vue b/novalon-manage-web/src/views/system/Dashboard.vue index d7a2029..e0fb54f 100644 --- a/novalon-manage-web/src/views/system/Dashboard.vue +++ b/novalon-manage-web/src/views/system/Dashboard.vue @@ -199,10 +199,10 @@ const fetchStats = async () => { request.get('/logs/operation/count') ]) - stats.userCount = userCountRes.status === 'fulfilled' ? (userCountRes.value || 0) : 0 - stats.roleCount = roleCountRes.status === 'fulfilled' ? (roleCountRes.value || 0) : 0 - stats.todayLogin = todayLoginRes.status === 'fulfilled' ? (todayLoginRes.value || 0) : 0 - stats.operationLog = operationLogRes.status === 'fulfilled' ? (operationLogRes.value || 0) : 0 + stats.userCount = userCountRes.status === 'fulfilled' ? Number(userCountRes.value || 0) : 0 + stats.roleCount = roleCountRes.status === 'fulfilled' ? Number(roleCountRes.value || 0) : 0 + stats.todayLogin = todayLoginRes.status === 'fulfilled' ? Number(todayLoginRes.value || 0) : 0 + stats.operationLog = operationLogRes.status === 'fulfilled' ? Number(operationLogRes.value || 0) : 0 } catch (error) { console.error('Failed to fetch stats:', error) } finally { diff --git a/novalon-manage-web/src/views/system/Login.vue b/novalon-manage-web/src/views/system/Login.vue index 9b8e320..dc6b466 100644 --- a/novalon-manage-web/src/views/system/Login.vue +++ b/novalon-manage-web/src/views/system/Login.vue @@ -79,9 +79,12 @@ interface JwtPayload { const onFinish = async () => { loading.value = true try { + console.log('开始登录请求...') const res: any = await request.post('/auth/login', formState) + console.log('登录响应:', res) if (!res || !res.token) { + console.error('登录失败:未收到有效响应') ElMessage.error('登录失败:未收到有效响应') return } @@ -103,15 +106,19 @@ const onFinish = async () => { console.warn('解析Token中的角色信息失败:', decodeError) } + console.log('开始获取用户菜单...') try { await permissionStore.fetchUserMenus() + console.log('获取用户菜单成功') } catch (menuError) { console.error('获取用户菜单失败:', menuError) } ElMessage.success('登录成功') + console.log('准备跳转到首页...') await router.push('/') + console.log('跳转完成') } catch (error: any) { console.error('登录错误:', error) ElMessage.error(error.response?.data?.message || error.message || '登录失败') diff --git a/novalon-manage-web/src/views/system/RoleManagement.vue b/novalon-manage-web/src/views/system/RoleManagement.vue index e6f4a58..3ffd751 100644 --- a/novalon-manage-web/src/views/system/RoleManagement.vue +++ b/novalon-manage-web/src/views/system/RoleManagement.vue @@ -279,10 +279,10 @@ const fetchData = async () => { size: pagination.pageSize, sortBy: sortInfo.sortBy, sortOrder: sortInfo.sortOrder, - name: searchKeyword.value || undefined + roleName: searchKeyword.value || undefined }) dataSource.value = res.content - pagination.total = res.totalElements + pagination.total = Number(res.totalElements) || 0 } catch (error) { handleApiError(error) } finally { diff --git a/novalon-manage-web/src/views/system/UserManagement.vue b/novalon-manage-web/src/views/system/UserManagement.vue index 5d22391..475fc4a 100644 --- a/novalon-manage-web/src/views/system/UserManagement.vue +++ b/novalon-manage-web/src/views/system/UserManagement.vue @@ -226,7 +226,7 @@ import { Search } from '@element-plus/icons-vue' import { userApi, type User, type CreateUserRequest, type UpdateUserRequest } from '@/api/user.api' import { roleApi, type Role } from '@/api/role.api' import { handleApiError } from '@/utils/errorHandler' -import { UserStatus, StatusHelper } from '@/constants/status' +import { UserStatus } from '@/constants/status' import { formatDateTime } from '@/utils/dateFormat' const loading = ref(false) @@ -279,9 +279,9 @@ const formRules = { } const roleDialogVisible = ref(false) -const selectedRoles = ref([]) -const allRoles = ref<{ key: number; label: string }[]>([]) -const currentUserId = ref(null) +const selectedRoles = ref([]) +const allRoles = ref<{ key: string; label: string }[]>([]) +const currentUserId = ref(null) const fetchData = async () => { loading.value = true @@ -294,7 +294,7 @@ const fetchData = async () => { keyword: searchKeyword.value || undefined }) dataSource.value = res.content - pagination.total = res.totalElements + pagination.total = Number(res.totalElements) || 0 } catch (error) { handleApiError(error) } finally { @@ -433,6 +433,7 @@ const handleAssignRolesOk = async () => { roleDialogVisible.value = false fetchData() } catch (error) { + roleDialogVisible.value = false handleApiError(error) } } diff --git a/novalon-manage-web/src/vite-env.d.ts b/novalon-manage-web/src/vite-env.d.ts new file mode 100644 index 0000000..9a6804d --- /dev/null +++ b/novalon-manage-web/src/vite-env.d.ts @@ -0,0 +1,10 @@ +/// + +interface ImportMetaEnv { + readonly VITE_SIGNATURE_SECRET: string + readonly VITE_API_BASE_URL?: string +} + +interface ImportMeta { + readonly env: ImportMetaEnv +}