feat(web): 迁移前端源代码(任务 T4.1)
- 删除 novalon 前端 src/ 下所有文件 - 从 gym-manage 复制前端 src/ 完整目录树 - 替换 gym-manage-api → novalon-manage-api - 替换 gym_system → manage_system - 无 gym 残留引用
This commit is contained in:
@@ -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<PageResponse<User>>('/users/page', { params }),
|
||||
|
||||
getById: (id: number) =>
|
||||
getById: (id: string) =>
|
||||
request.get<User>(`/users/${id}`),
|
||||
|
||||
create: (data: CreateUserRequest) =>
|
||||
request.post<User>('/users', data),
|
||||
|
||||
update: (id: number, data: UpdateUserRequest) =>
|
||||
update: (id: string, data: UpdateUserRequest) =>
|
||||
request.put<User>(`/users/${id}`, data),
|
||||
|
||||
delete: (id: number) =>
|
||||
delete: (id: string) =>
|
||||
request.delete<void>(`/users/${id}`),
|
||||
|
||||
batchDelete: (ids: number[]) =>
|
||||
batchDelete: (ids: string[]) =>
|
||||
request.post<void>('/users/batch-delete', { ids }),
|
||||
|
||||
resetPassword: (id: number) =>
|
||||
resetPassword: (id: string) =>
|
||||
request.post<void>(`/users/${id}/reset-password`),
|
||||
|
||||
updateStatus: (id: number, status: UserStatus) =>
|
||||
updateStatus: (id: string, status: UserStatus) =>
|
||||
request.put<void>(`/users/${id}/status`, { status }),
|
||||
|
||||
assignRoles: (id: number, roleIds: number[]) =>
|
||||
assignRoles: (id: string, roleIds: string[]) =>
|
||||
request.post<void>(`/users/${id}/roles`, { roleIds }),
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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<number, MenuItem>()
|
||||
const menuMap = new Map<string, MenuItem>()
|
||||
const rootMenus: MenuItem[] = []
|
||||
|
||||
const componentToPathMap: Record<string, string> = {
|
||||
@@ -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)
|
||||
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
import { AxiosRequestConfig, AxiosInstance } from 'axios'
|
||||
|
||||
declare module 'axios' {
|
||||
interface AxiosInstance {
|
||||
request<T = any>(config: AxiosRequestConfig): Promise<T>
|
||||
get<T = any>(url: string, config?: AxiosRequestConfig): Promise<T>
|
||||
delete<T = any>(url: string, config?: AxiosRequestConfig): Promise<T>
|
||||
head<T = any>(url: string, config?: AxiosRequestConfig): Promise<T>
|
||||
post<T = any>(url: string, data?: any, config?: AxiosRequestConfig): Promise<T>
|
||||
put<T = any>(url: string, data?: any, config?: AxiosRequestConfig): Promise<T>
|
||||
patch<T = any>(url: string, data?: any, config?: AxiosRequestConfig): Promise<T>
|
||||
}
|
||||
}
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 || {}
|
||||
|
||||
@@ -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
|
||||
)
|
||||
|
||||
@@ -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('操作失败')
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 || '登录失败')
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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<number[]>([])
|
||||
const allRoles = ref<{ key: number; label: string }[]>([])
|
||||
const currentUserId = ref<number | null>(null)
|
||||
const selectedRoles = ref<string[]>([])
|
||||
const allRoles = ref<{ key: string; label: string }[]>([])
|
||||
const currentUserId = ref<string | null>(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)
|
||||
}
|
||||
}
|
||||
|
||||
Vendored
+10
@@ -0,0 +1,10 @@
|
||||
/// <reference types="vite/client" />
|
||||
|
||||
interface ImportMetaEnv {
|
||||
readonly VITE_SIGNATURE_SECRET: string
|
||||
readonly VITE_API_BASE_URL?: string
|
||||
}
|
||||
|
||||
interface ImportMeta {
|
||||
readonly env: ImportMetaEnv
|
||||
}
|
||||
Reference in New Issue
Block a user