diff --git a/admin-system/src/api/dashboard.ts b/admin-system/src/api/dashboard.ts new file mode 100644 index 0000000..761a42d --- /dev/null +++ b/admin-system/src/api/dashboard.ts @@ -0,0 +1,92 @@ +import { request } from '.' +import { mockAPI } from './mockData' +import { createMockWrapper } from '@/config/mock' + +// 定义仪表板数据类型 +export interface DashboardStats { + // 用户统计 + userCount: number + newUserCount: number + activeUserCount: number + + // 商家统计 + merchantCount: number + newMerchantCount: number + activeMerchantCount: number + + // 旅行统计 + travelCount: number + newTravelCount: number + + // 动物统计 + animalCount: number + newAnimalCount: number + + // 订单统计 + orderCount: number + newOrderCount: number + orderAmount: number + + // 系统统计 + onlineUserCount: number + systemLoad: number +} + +export interface UserGrowthData { + date: string + count: number +} + +export interface OrderStatsData { + date: string + count: number + amount: number +} + +export interface ActivityLog { + id: number + title: string + description: string + avatar: string + time: string +} + +export interface DashboardData { + stats: DashboardStats + userGrowth: UserGrowthData[] + orderStats: OrderStatsData[] + activities: ActivityLog[] +} + +export interface ApiResponse { + success: boolean + code: number + message: string + data: T +} + +// 获取仪表板数据 +export const getDashboardData = () => + request.get>('/admin/dashboard') + +// 获取用户增长数据 +export const getUserGrowthData = (days: number = 7) => + request.get>(`/admin/dashboard/user-growth?days=${days}`) + +// 获取订单统计数据 +export const getOrderStatsData = (days: number = 7) => + request.get>(`/admin/dashboard/order-stats?days=${days}`) + +// 获取最近活动日志 +export const getRecentActivities = () => + request.get>('/admin/dashboard/activities') + +// 开发环境使用模拟数据 +const dashboardAPI = createMockWrapper({ + getDashboardData, + getUserGrowthData, + getOrderStatsData, + getRecentActivities +}, mockAPI.dashboard) + +export default dashboardAPI \ No newline at end of file diff --git a/admin-system/src/api/mockData.ts b/admin-system/src/api/mockData.ts index b21e10f..62eabae 100644 --- a/admin-system/src/api/mockData.ts +++ b/admin-system/src/api/mockData.ts @@ -35,6 +35,24 @@ const mockOrders = [ { id: 2, userId: 2, merchantId: 1, amount: 299, status: 'pending', createdAt: '2024-01-21' } ] +// 模拟权限数据 +const mockPermissions = [ + { id: 1, name: '用户读取', code: 'user:read', description: '查看用户信息', resource_type: 'user', created_at: '2024-01-01', updated_at: '2024-01-01' }, + { id: 2, name: '用户写入', code: 'user:write', description: '创建/编辑用户信息', resource_type: 'user', created_at: '2024-01-01', updated_at: '2024-01-01' }, + { id: 3, name: '商家读取', code: 'merchant:read', description: '查看商家信息', resource_type: 'merchant', created_at: '2024-01-01', updated_at: '2024-01-01' }, + { id: 4, name: '商家写入', code: 'merchant:write', description: '创建/编辑商家信息', resource_type: 'merchant', created_at: '2024-01-01', updated_at: '2024-01-01' }, + { id: 5, name: '旅行读取', code: 'travel:read', description: '查看旅行信息', resource_type: 'travel', created_at: '2024-01-01', updated_at: '2024-01-01' }, + { id: 6, name: '旅行写入', code: 'travel:write', description: '创建/编辑旅行信息', resource_type: 'travel', created_at: '2024-01-01', updated_at: '2024-01-01' }, + { id: 7, name: '动物读取', code: 'animal:read', description: '查看动物信息', resource_type: 'animal', created_at: '2024-01-01', updated_at: '2024-01-01' }, + { id: 8, name: '动物写入', code: 'animal:write', description: '创建/编辑动物信息', resource_type: 'animal', created_at: '2024-01-01', updated_at: '2024-01-01' }, + { id: 9, name: '订单读取', code: 'order:read', description: '查看订单信息', resource_type: 'order', created_at: '2024-01-01', updated_at: '2024-01-01' }, + { id: 10, name: '订单写入', code: 'order:write', description: '创建/编辑订单信息', resource_type: 'order', created_at: '2024-01-01', updated_at: '2024-01-01' }, + { id: 11, name: '推广读取', code: 'promotion:read', description: '查看推广信息', resource_type: 'promotion', created_at: '2024-01-01', updated_at: '2024-01-01' }, + { id: 12, name: '推广写入', code: 'promotion:write', description: '创建/编辑推广信息', resource_type: 'promotion', created_at: '2024-01-01', updated_at: '2024-01-01' }, + { id: 13, name: '系统读取', code: 'system:read', description: '查看系统信息', resource_type: 'system', created_at: '2024-01-01', updated_at: '2024-01-01' }, + { id: 14, name: '系统写入', code: 'system:write', description: '创建/编辑系统信息', resource_type: 'system', created_at: '2024-01-01', updated_at: '2024-01-01' } +] + // 模拟API响应格式 const createSuccessResponse = (data: any) => ({ success: true, @@ -173,6 +191,188 @@ export const mockSystemAPI = { } } +// 模拟权限API +export const mockPermissionAPI = { + getPermissions: async (params: any = {}) => { + await delay(800) + const { page = 1, pageSize = 10, keyword = '' } = params + + // 根据关键词过滤权限 + let filteredPermissions = mockPermissions + if (keyword) { + filteredPermissions = mockPermissions.filter(p => + p.name.includes(keyword) || + p.code.includes(keyword) || + p.description.includes(keyword) + ) + } + + const start = (page - 1) * pageSize + const end = start + pageSize + const paginatedData = filteredPermissions.slice(start, end) + + return createPaginatedResponse(paginatedData, page, pageSize, filteredPermissions.length) + }, + + getPermission: async (id: number) => { + await delay(500) + const permission = mockPermissions.find(p => p.id === id) + if (permission) { + return createSuccessResponse(permission) + } + message.error('权限不存在') + throw new Error('权限不存在') + }, + + createPermission: async (data: any) => { + await delay(500) + const newPermission = { + id: mockPermissions.length + 1, + ...data, + created_at: new Date().toISOString().split('T')[0], + updated_at: new Date().toISOString().split('T')[0] + } + mockPermissions.push(newPermission) + return createSuccessResponse(newPermission) + }, + + updatePermission: async (id: number, data: any) => { + await delay(500) + const index = mockPermissions.findIndex(p => p.id === id) + if (index !== -1) { + mockPermissions[index] = { + ...mockPermissions[index], + ...data, + updated_at: new Date().toISOString().split('T')[0] + } + return createSuccessResponse(mockPermissions[index]) + } + message.error('权限不存在') + throw new Error('权限不存在') + }, + + deletePermission: async (id: number) => { + await delay(500) + const index = mockPermissions.findIndex(p => p.id === id) + if (index !== -1) { + mockPermissions.splice(index, 1) + return createSuccessResponse(null) + } + message.error('权限不存在') + throw new Error('权限不存在') + }, + + batchDeletePermissions: async (ids: number[]) => { + await delay(500) + const deletedCount = ids.filter(id => { + const index = mockPermissions.findIndex(p => p.id === id) + if (index !== -1) { + mockPermissions.splice(index, 1) + return true + } + return false + }).length + + return createSuccessResponse({ + message: `成功删除${deletedCount}个权限`, + affectedRows: deletedCount + }) + } +} + +// 模拟仪表板数据 +const mockDashboardData = { + stats: { + userCount: 1280, + newUserCount: 25, + activeUserCount: 860, + merchantCount: 42, + newMerchantCount: 3, + activeMerchantCount: 38, + travelCount: 156, + newTravelCount: 8, + animalCount: 89, + newAnimalCount: 5, + orderCount: 342, + newOrderCount: 12, + orderAmount: 25680, + onlineUserCount: 142, + systemLoad: 45 + }, + userGrowth: [ + { date: '2024-03-01', count: 1200 }, + { date: '2024-03-02', count: 1210 }, + { date: '2024-03-03', count: 1225 }, + { date: '2024-03-04', count: 1235 }, + { date: '2024-03-05', count: 1248 }, + { date: '2024-03-06', count: 1262 }, + { date: '2024-03-07', count: 1280 } + ], + orderStats: [ + { date: '2024-03-01', count: 28, amount: 2100 }, + { date: '2024-03-02', count: 32, amount: 2450 }, + { date: '2024-03-03', count: 25, amount: 1890 }, + { date: '2024-03-04', count: 35, amount: 2680 }, + { date: '2024-03-05', count: 42, amount: 3200 }, + { date: '2024-03-06', count: 38, amount: 2890 }, + { date: '2024-03-07', count: 45, amount: 3420 } + ], + activities: [ + { + id: 1, + title: '新用户注册', + description: '用户"旅行爱好者"完成了注册', + avatar: 'https://api.dicebear.com/7.x/miniavs/svg?seed=1', + time: '2分钟前' + }, + { + title: '旅行计划创建', + description: '用户"探险家"发布了西藏旅行计划', + avatar: 'https://api.dicebear.com/7.x/miniavs/svg?seed=2', + time: '5分钟前' + }, + { + title: '动物认领', + description: '用户"动物之友"认领了一只羊驼', + avatar: 'https://api.dicebear.com/7.x/miniavs/svg?seed=3', + time: '10分钟前' + }, + { + title: '订单完成', + description: '花店"鲜花坊"完成了一笔鲜花订单', + avatar: 'https://api.dicebear.com/7.x/miniavs/svg?seed=4', + time: '15分钟前' + } + ] +} + +// 模拟仪表板API +export const mockDashboardAPI = { + getDashboardData: async () => { + await delay(800) + return createSuccessResponse(mockDashboardData) + }, + + getUserGrowthData: async (days: number = 7) => { + await delay(500) + // 根据天数返回相应的数据 + const data = mockDashboardData.userGrowth.slice(-days) + return createSuccessResponse(data) + }, + + getOrderStatsData: async (days: number = 7) => { + await delay(500) + // 根据天数返回相应的数据 + const data = mockDashboardData.orderStats.slice(-days) + return createSuccessResponse(data) + }, + + getRecentActivities: async () => { + await delay(500) + return createSuccessResponse(mockDashboardData.activities) + } +} + // 导出所有模拟API export const mockAPI = { auth: mockAuthAPI, @@ -181,7 +381,9 @@ export const mockAPI = { travel: mockTravelAPI, animal: mockAnimalAPI, order: mockOrderAPI, - system: mockSystemAPI + system: mockSystemAPI, + permission: mockPermissionAPI, + dashboard: mockDashboardAPI // 添加仪表板API } export default mockAPI \ No newline at end of file diff --git a/admin-system/src/api/permission.ts b/admin-system/src/api/permission.ts new file mode 100644 index 0000000..f7bdfaf --- /dev/null +++ b/admin-system/src/api/permission.ts @@ -0,0 +1,87 @@ +import { request } from '.' +import { mockAPI } from './mockData' +import { createMockWrapper } from '@/config/mock' + +// 定义权限相关类型 +export interface Permission { + id: number + name: string + code: string + description: string + resource_type: string + created_at: string + updated_at: string +} + +export interface PermissionQueryParams { + page?: number + pageSize?: number + keyword?: string +} + +export interface PermissionCreateData { + name: string + code: string + description: string + resource_type: string +} + +export interface PermissionUpdateData { + name?: string + code?: string + description?: string + resource_type?: string +} + +export interface ApiResponse { + success: boolean + code: number + message: string + data: T +} + +export interface PermissionListResponse { + permissions: Permission[] + pagination: { + page: number + pageSize: number + total: number + totalPages: number + } +} + +// 获取权限列表 +export const getPermissions = (params?: PermissionQueryParams) => + request.get>('/admin/permissions', { params }) + +// 获取权限详情 +export const getPermission = (id: number) => + request.get>(`/admin/permissions/${id}`) + +// 创建权限 +export const createPermission = (data: PermissionCreateData) => + request.post>('/admin/permissions', data) + +// 更新权限 +export const updatePermission = (id: number, data: PermissionUpdateData) => + request.put>(`/admin/permissions/${id}`, data) + +// 删除权限 +export const deletePermission = (id: number) => + request.delete>(`/admin/permissions/${id}`) + +// 批量删除权限 +export const batchDeletePermissions = (ids: number[]) => + request.post>('/admin/permissions/batch-delete', { ids }) + +// 开发环境使用模拟数据 +const permissionAPI = createMockWrapper({ + getPermissions, + getPermission, + createPermission, + updatePermission, + deletePermission, + batchDeletePermissions +}, mockAPI.permission) + +export default permissionAPI \ No newline at end of file diff --git a/admin-system/src/layouts/MainLayout.vue b/admin-system/src/layouts/MainLayout.vue index 2c13fae..704e1e1 100644 --- a/admin-system/src/layouts/MainLayout.vue +++ b/admin-system/src/layouts/MainLayout.vue @@ -28,7 +28,7 @@ - + @@ -36,7 +36,7 @@ - + @@ -44,7 +44,7 @@ - + @@ -52,7 +52,7 @@ - + @@ -60,7 +60,7 @@ - + @@ -68,7 +68,7 @@ - + @@ -76,7 +76,7 @@ - + @@ -194,6 +194,11 @@ const currentRouteMeta = computed(() => route.meta || {}) const userName = computed(() => appStore.state.user?.nickname || '管理员') const userAvatar = computed(() => appStore.state.user?.avatar || 'https://api.dicebear.com/7.x/miniavs/svg?seed=admin') +// 权限检查方法 +const hasPermission = (permission: string) => { + return appStore.hasPermission(permission) +} + // 监听路由变化 router.afterEach((to) => { selectedKeys.value = [to.name as string] diff --git a/admin-system/src/pages/NoPermission.vue b/admin-system/src/pages/NoPermission.vue new file mode 100644 index 0000000..b58c454 --- /dev/null +++ b/admin-system/src/pages/NoPermission.vue @@ -0,0 +1,33 @@ + + + + + \ No newline at end of file diff --git a/admin-system/src/pages/animal/index.vue b/admin-system/src/pages/animal/index.vue index cdb0ee0..ced08d0 100644 --- a/admin-system/src/pages/animal/index.vue +++ b/admin-system/src/pages/animal/index.vue @@ -12,7 +12,11 @@ 刷新 - + @@ -120,12 +124,21 @@ 查看 - + 编辑 - + 删除 @@ -204,7 +217,7 @@