diff --git a/schoolNewsWeb/src/assets/imgs/v1-icon.svg b/schoolNewsWeb/public/img/achievement/v1-icon.svg similarity index 100% rename from schoolNewsWeb/src/assets/imgs/v1-icon.svg rename to schoolNewsWeb/public/img/achievement/v1-icon.svg diff --git a/schoolNewsWeb/src/assets/imgs/v1.svg b/schoolNewsWeb/public/img/achievement/v1.svg similarity index 100% rename from schoolNewsWeb/src/assets/imgs/v1.svg rename to schoolNewsWeb/public/img/achievement/v1.svg diff --git a/schoolNewsWeb/src/assets/imgs/v2-icon.svg b/schoolNewsWeb/public/img/achievement/v2-icon.svg similarity index 100% rename from schoolNewsWeb/src/assets/imgs/v2-icon.svg rename to schoolNewsWeb/public/img/achievement/v2-icon.svg diff --git a/schoolNewsWeb/src/assets/imgs/v2.svg b/schoolNewsWeb/public/img/achievement/v2.svg similarity index 100% rename from schoolNewsWeb/src/assets/imgs/v2.svg rename to schoolNewsWeb/public/img/achievement/v2.svg diff --git a/schoolNewsWeb/src/assets/imgs/v3-icon.svg b/schoolNewsWeb/public/img/achievement/v3-icon.svg similarity index 100% rename from schoolNewsWeb/src/assets/imgs/v3-icon.svg rename to schoolNewsWeb/public/img/achievement/v3-icon.svg diff --git a/schoolNewsWeb/src/assets/imgs/v3.svg b/schoolNewsWeb/public/img/achievement/v3.svg similarity index 100% rename from schoolNewsWeb/src/assets/imgs/v3.svg rename to schoolNewsWeb/public/img/achievement/v3.svg diff --git a/schoolNewsWeb/src/assets/imgs/v4-icon.svg b/schoolNewsWeb/public/img/achievement/v4-icon.svg similarity index 100% rename from schoolNewsWeb/src/assets/imgs/v4-icon.svg rename to schoolNewsWeb/public/img/achievement/v4-icon.svg diff --git a/schoolNewsWeb/src/assets/imgs/v4.svg b/schoolNewsWeb/public/img/achievement/v4.svg similarity index 100% rename from schoolNewsWeb/src/assets/imgs/v4.svg rename to schoolNewsWeb/public/img/achievement/v4.svg diff --git a/schoolNewsWeb/public/img/achievement/v5-icon.svg b/schoolNewsWeb/public/img/achievement/v5-icon.svg new file mode 100644 index 0000000..74f892c --- /dev/null +++ b/schoolNewsWeb/public/img/achievement/v5-icon.svg @@ -0,0 +1,414 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/schoolNewsWeb/public/img/achievement/v6-icon.svg b/schoolNewsWeb/public/img/achievement/v6-icon.svg new file mode 100644 index 0000000..265a582 --- /dev/null +++ b/schoolNewsWeb/public/img/achievement/v6-icon.svg @@ -0,0 +1,414 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/schoolNewsWeb/src/apis/achievement/achievement.ts b/schoolNewsWeb/src/apis/achievement/achievement.ts new file mode 100644 index 0000000..37c83d0 --- /dev/null +++ b/schoolNewsWeb/src/apis/achievement/achievement.ts @@ -0,0 +1,233 @@ +/** + * @description 成就相关API + * @author yslg + * @since 2025-10-24 + */ + +import { api } from '@/apis/index'; +import type { Achievement, UserAchievement, UserAchievementProgress, AchievementVO, AchievementEvent, ResultDomain, PageParam } from '@/types'; + +/** + * 成就API服务 + */ +export const achievementApi = { + // ==================== 成就定义管理(管理员)==================== + + /** + * 创建成就 + * @param achievement 成就信息 + * @returns Promise> + */ + async createAchievement(achievement: Achievement): Promise> { + const response = await api.post('/achievements/achievement', achievement); + return response.data; + }, + + /** + * 更新成就 + * @param achievement 成就信息 + * @returns Promise> + */ + async updateAchievement(achievement: Achievement): Promise> { + const response = await api.put('/achievements/achievement', achievement); + return response.data; + }, + + /** + * 删除成就 + * @param achievement 成就信息(包含achievementID) + * @returns Promise> + */ + async deleteAchievement(achievement: Achievement): Promise> { + const response = await api.delete('/achievements/achievement', achievement); + return response.data; + }, + + /** + * 获取所有成就列表 + * @param filter 过滤条件(type: 成就类型, level: 成就等级) + * @returns Promise> + */ + async getAllAchievements(filter?: Partial): Promise> { + const response = await api.post('/achievements/list', filter || {}); + return response.data; + }, + + /** + * 分页查询成就 + * @param filter 过滤条件 + * @param pageParam 分页参数 + * @returns Promise> + */ + async getAchievementPage(filter?: Partial, pageParam?: PageParam): Promise> { + const response = await api.post('/achievements/page', { filter, pageParam }); + return response.data; + }, + + /** + * 获取成就详情 + * @param achievementID 成就ID + * @returns Promise> + */ + async getAchievementDetail(achievementID: string): Promise> { + const response = await api.get(`/achievements/detail/${achievementID}`); + return response.data; + }, + + // ==================== 用户成就查询 ==================== + + /** + * 获取用户已获得的成就 + * @param userID 用户ID + * @param type 成就类型(可选) + * @returns Promise> + */ + async getUserAchievements(userID: string, type?: number): Promise> { + const params: Record = {}; + if (type !== undefined) { + params.type = type; + } + const response = await api.get(`/achievements/user/${userID}`, params); + return response.data; + }, + + /** + * 获取当前用户的成就列表(含进度信息) + * 返回所有成就,包括: + * - 已获得的成就:显示获得时间 + * - 未获得的成就:显示当前进度 + * @param type 成就类型(可选) + * @returns Promise> + */ + async getMyAchievements(type?: number): Promise> { + const params: Record = {}; + if (type !== undefined) { + params.type = type; + } + const response = await api.get('/achievements/my', params); + return response.data; + }, + + /** + * 检查用户是否已获得成就 + * @param userID 用户ID + * @param achievementID 成就ID + * @returns Promise> + */ + async hasAchievement(userID: string, achievementID: string): Promise> { + const response = await api.get(`/achievements/check/${userID}/${achievementID}`); + return response.data; + }, + + // ==================== 成就授予(管理员)==================== + + /** + * 手动授予用户成就 + * @param userID 用户ID + * @param achievementID 成就ID + * @returns Promise> + */ + async grantAchievement(userID: string, achievementID: string): Promise> { + const response = await api.post('/achievements/grant', null, { + params: { userID, achievementID } + }); + return response.data; + }, + + /** + * 撤销用户成就 + * @param userID 用户ID + * @param achievementID 成就ID + * @returns Promise> + */ + async revokeAchievement(userID: string, achievementID: string): Promise> { + const response = await api.delete('/achievements/revoke', null, { + params: { userID, achievementID } + }); + return response.data; + }, + + // ==================== 成就进度查询 ==================== + + /** + * 获取用户成就进度 + * @param userID 用户ID + * @param achievementID 成就ID(可选,为空则获取所有进度) + * @returns Promise> + */ + async getUserAchievementProgress(userID: string, achievementID?: string): Promise> { + const params: Record = {}; + if (achievementID) { + params.achievementID = achievementID; + } + const response = await api.get(`/achievements/progress/${userID}`, params); + return response.data; + }, + + // ==================== 成就检测 ==================== + + /** + * 处理成就事件(内部接口,由其他服务调用) + * @param event 成就事件 + * @returns Promise> + */ + async processAchievementEvent(event: AchievementEvent): Promise> { + const response = await api.post('/achievements/event/process', event); + return response.data; + }, + + /** + * 检查用户是否满足成就条件 + * @param userID 用户ID + * @param achievementID 成就ID + * @returns Promise> + */ + async checkAchievementCondition(userID: string, achievementID: string): Promise> { + const response = await api.get(`/achievements/condition/check/${userID}/${achievementID}`); + return response.data; + }, + + /** + * 批量检查用户可获得的成就 + * @param userID 用户ID + * @returns Promise> + */ + async checkAvailableAchievements(userID: string): Promise> { + const response = await api.get(`/achievements/available/${userID}`); + return response.data; + }, + + // ==================== 成就统计 ==================== + + /** + * 获取用户成就统计 + * @param userID 用户ID + * @returns Promise>> + */ + async getUserAchievementStatistics(userID: string): Promise>> { + const response = await api.get>(`/achievements/statistics/${userID}`); + return response.data; + }, + + /** + * 获取成就排行榜 + * @param limit 排行榜条数(默认10条) + * @returns Promise>> + */ + async getAchievementRanking(limit = 10): Promise>> { + const response = await api.get>('/achievements/ranking', { limit }); + return response.data; + }, + + /** + * 获取最近获得成就的用户 + * @param pageParam 分页参数 + * @param filter 过滤条件 + * @param limit 查询条数(默认10条) + * @returns Promise> + */ + async getRecentAchievers(pageParam: PageParam, filter?:Achievement ): Promise> { + const response = await api.post(`/achievements/recent`, {pageParam, filter }); + return response.data; + } +}; diff --git a/schoolNewsWeb/src/apis/achievement/index.ts b/schoolNewsWeb/src/apis/achievement/index.ts new file mode 100644 index 0000000..3e98de6 --- /dev/null +++ b/schoolNewsWeb/src/apis/achievement/index.ts @@ -0,0 +1 @@ +export * from './achievement'; \ No newline at end of file diff --git a/schoolNewsWeb/src/apis/index.ts b/schoolNewsWeb/src/apis/index.ts index ddc750c..b2acf9d 100644 --- a/schoolNewsWeb/src/apis/index.ts +++ b/schoolNewsWeb/src/apis/index.ts @@ -178,9 +178,6 @@ request.interceptors.response.use( } ); -/** - * API封装 - */ /** * API封装 */ diff --git a/schoolNewsWeb/src/apis/system/index.ts b/schoolNewsWeb/src/apis/system/index.ts index 7f1a884..3dadd75 100644 --- a/schoolNewsWeb/src/apis/system/index.ts +++ b/schoolNewsWeb/src/apis/system/index.ts @@ -12,4 +12,5 @@ export { menuApi } from './menu'; export { permissionApi } from './permission'; export { authApi } from './auth'; export { fileApi } from './file'; +export { moduleApi } from './module'; diff --git a/schoolNewsWeb/src/apis/system/module.ts b/schoolNewsWeb/src/apis/system/module.ts new file mode 100644 index 0000000..4a5cf85 --- /dev/null +++ b/schoolNewsWeb/src/apis/system/module.ts @@ -0,0 +1,197 @@ +/** + * @description 系统模块相关API + * @author yslg + * @since 2025-10-25 + */ + +import { api } from '@/apis/index'; +import type { SysModule, ResultDomain, PageParam, SysPermission } from '@/types'; + +/** + * 系统模块API服务 + */ +export const moduleApi = { + baseUrl: '/system/modules', + /** + * 查询模块列表 + * @param filter 过滤条件 + * @returns Promise> + */ + async getModuleList(filter?: Partial): Promise> { + const response = await api.post(`${this.baseUrl}/list`, filter); + return response.data; + }, + + /** + * 根据模块ID查询模块信息 + * @param moduleID 模块ID + * @returns Promise> + */ + async getModuleById(moduleID: string): Promise> { + const response = await api.get(`${this.baseUrl}/${moduleID}`); + return response.data; + }, + + /** + * 根据模块代码查询模块信息 + * @param code 模块代码 + * @returns Promise> + */ + async getModuleByCode(code: string): Promise> { + const response = await api.get(`${this.baseUrl}/code/${code}`); + return response.data; + }, + + /** + * 查询启用的模块列表 + * @returns Promise> + */ + async getActiveModules(): Promise> { + const response = await api.get(`${this.baseUrl}/active`); + return response.data; + }, + + /** + * 创建模块 + * @param module 模块信息 + * @returns Promise> + */ + async createModule(module: SysModule): Promise> { + const response = await api.post(`${this.baseUrl}/module`, module); + return response.data; + }, + + /** + * 更新模块 + * @param module 模块信息 + * @returns Promise> + */ + async updateModule(module: SysModule): Promise> { + const response = await api.put(`${this.baseUrl}/module`, module); + return response.data; + }, + + /** + * 删除模块 + * @param moduleID 模块ID + * @returns Promise> + */ + async deleteModule(moduleID: string): Promise> { + const response = await api.delete(`${this.baseUrl}/module`, { moduleID }); + return response.data; + }, + + /** + * 批量删除模块 + * @param moduleIDs 模块ID列表 + * @returns Promise> + */ + async batchDeleteModules(moduleIDs: string[]): Promise> { + const response = await api.delete(`${this.baseUrl}/batch`, { moduleIDs }); + return response.data; + }, + + /** + * 更新模块状态 + * @param moduleID 模块ID + * @param status 状态(0禁用 1启用) + * @returns Promise> + */ + async updateModuleStatus(moduleID: string, status: number): Promise> { + const response = await api.put(`${this.baseUrl}/${moduleID}/status/${status}`); + return response.data; + }, + + /** + * 更新模块排序 + * @param moduleID 模块ID + * @param orderNum 排序号 + * @returns Promise> + */ + async updateModuleOrder(moduleID: string, orderNum: number): Promise> { + const response = await api.put(`${this.baseUrl}/${moduleID}/order/${orderNum}`); + return response.data; + }, + + /** + * 分页查询模块列表 + * @param filter 过滤条件 + * @param pageParam 分页参数 + * @returns Promise> + */ + async getModuleListPage(filter?: Partial, pageParam?: PageParam): Promise> { + const response = await api.post(`${this.baseUrl}/page`, { + filter, + pageParam: {pageNumber: pageParam?.page || 1, pageSize: pageParam?.size || 10} + }); + return response.data; + }, + + /** + * 统计模块数量 + * @param filter 过滤条件 + * @returns Promise> + */ + async countModules(filter?: Partial): Promise> { + const response = await api.post(`${this.baseUrl}/count`, filter); + return response.data; + }, + + /** + * 检查模块代码是否存在 + * @param code 模块代码 + * @param excludeID 排除的模块ID + * @returns Promise> + */ + async checkModuleCodeExists(code: string, excludeID?: string): Promise> { + const response = await api.get(`${this.baseUrl}/check-code`, { + code, + excludeID + }); + return response.data; + }, + + /** + * 在模块中创建权限 + * @param moduleID 模块ID + * @param permission 权限信息 + * @returns Promise> + */ + async createPermissionInModule(moduleID: string, permission: SysPermission): Promise> { + const response = await api.post(`${this.baseUrl}/${moduleID}/permissions`, permission); + return response.data; + }, + + /** + * 更新模块中的权限 + * @param moduleID 模块ID + * @param permission 权限信息 + * @returns Promise> + */ + async updatePermissionInModule(moduleID: string, permission: SysPermission): Promise> { + const response = await api.put(`${this.baseUrl}/${moduleID}/permissions`, permission); + return response.data; + }, + + /** + * 删除模块中的权限 + * @param moduleID 模块ID + * @param permissionID 权限ID + * @returns Promise> + */ + async deletePermissionInModule(moduleID: string, permissionID: string): Promise> { + const response = await api.delete(`${this.baseUrl}/${moduleID}/permissions/${permissionID}`); + return response.data; + }, + + /** + * 获取模块的权限列表 + * @param moduleID 模块ID + * @returns Promise> + */ + async getModulePermissions(moduleID: string): Promise> { + const response = await api.get(`${this.baseUrl}/${moduleID}/permissions`); + return response.data; + } +}; + diff --git a/schoolNewsWeb/src/apis/usercenter/achievement.ts b/schoolNewsWeb/src/apis/usercenter/achievement.ts deleted file mode 100644 index 5224072..0000000 --- a/schoolNewsWeb/src/apis/usercenter/achievement.ts +++ /dev/null @@ -1,46 +0,0 @@ -/** - * @description 用户成就相关API - * @author yslg - * @since 2025-10-15 - */ - -import { api } from '@/apis/index'; -import type { UserAchievement, Achievement, ResultDomain } from '@/types'; - -/** - * 用户成就API服务 - */ -export const userAchievementApi = { - /** - * 获取用户成就列表 - * @param userID 用户ID - * @returns Promise> - */ - async getUserAchievements(userID: string): Promise> { - const response = await api.get('/usercenter/achievement/user-list', { userID }); - return response.data; - }, - - /** - * 获取所有成就列表 - * @returns Promise> - */ - async getAllAchievements(): Promise> { - const response = await api.get('/usercenter/achievement/list'); - return response.data; - }, - - /** - * 检查用户成就进度 - * @param userID 用户ID - * @param achievementID 成就ID - * @returns Promise> - */ - async checkAchievementProgress(userID: string, achievementID: string): Promise> { - const response = await api.get<{ progress: number; isCompleted: boolean }>('/usercenter/achievement/progress', { - userID, - achievementID - }); - return response.data; - } -}; diff --git a/schoolNewsWeb/src/apis/usercenter/index.ts b/schoolNewsWeb/src/apis/usercenter/index.ts index 98dbe43..7d79c27 100644 --- a/schoolNewsWeb/src/apis/usercenter/index.ts +++ b/schoolNewsWeb/src/apis/usercenter/index.ts @@ -8,5 +8,4 @@ export { userCollectionApi } from './collection'; export { userBrowseRecordApi } from './browse-record'; export { userPointsApi } from './points'; -export { userAchievementApi } from './achievement'; export { userProfileApi } from './profile'; diff --git a/schoolNewsWeb/src/config/index.ts b/schoolNewsWeb/src/config/index.ts index 5faf353..9127809 100644 --- a/schoolNewsWeb/src/config/index.ts +++ b/schoolNewsWeb/src/config/index.ts @@ -51,6 +51,6 @@ export const APP_CONFIG = { refreshThreshold: 5 * 60 * 1000 // 提前5分钟刷新 } }; - +export const PUBLIC_IMG_PATH = '/schoolNewsWeb/img'; export default APP_CONFIG; diff --git a/schoolNewsWeb/src/types/achievement/achievement.ts b/schoolNewsWeb/src/types/achievement/achievement.ts new file mode 100644 index 0000000..87a89db --- /dev/null +++ b/schoolNewsWeb/src/types/achievement/achievement.ts @@ -0,0 +1,114 @@ + +import { BaseDTO } from '../base'; +import { AchievementEventType } from '../enums'; + +/** + * 成就实体 + */ +export interface Achievement extends BaseDTO { + /** 成就唯一标识 */ + achievementID?: string; + /** 成就名称 */ + name?: string; + /** 成就描述 */ + description?: string; + /** 成就图标 */ + icon?: string; + /** 成就类型(1勋章 2等级) */ + type?: number; + /** 成就等级 */ + level?: number; + /** 获取条件类型(1学习时长 2资源数量 3课程数量 4连续学习天数) */ + conditionType?: number; + /** 条件值 */ + conditionValue?: number; + /** 获得积分 */ + points?: number; + /** 排序号 */ + orderNum?: number; + /** 创建者 */ + creator?: string; + /** 更新者 */ + updater?: string; + } + + /** + * 用户成就实体 + */ + export interface UserAchievement extends BaseDTO { + /** 用户ID */ + userID?: string; + /** 成就ID */ + achievementID?: string; + /** 获得时间 */ + obtainTime?: string; + } + + /** + * 用户成就进度实体 + */ + export interface UserAchievementProgress extends BaseDTO { + /** 用户ID */ + userID?: string; + /** 成就ID */ + achievementID?: string; + /** 当前进度值 */ + currentValue?: number; + /** 目标进度值 */ + targetValue?: number; + /** 进度百分比 (0-100) */ + progressPercentage?: number; + /** 是否已完成 */ + completed?: boolean; + /** 最后更新时间 */ + lastUpdateTime?: string; + } + + /** + * 成就视图对象 - 包含成就信息、获得状态和进度信息 + */ + export interface AchievementVO extends Achievement { + // ==================== 用户成就表的字段 ==================== + /** 用户成就记录ID */ + userAchievementID?: string; + /** 用户ID */ + userID?: string; + /** 获得时间 */ + obtainTime?: string; + + // ==================== 成就进度表的字段 ==================== + /** 进度记录ID */ + progressID?: string; + /** 当前进度值 */ + currentValue?: number; + /** 目标进度值 */ + targetValue?: number; + /** 进度百分比 (0-100) */ + progressPercentage?: number; + /** 是否已完成 */ + completed?: boolean; + /** 最后更新时间 */ + lastUpdateTime?: string; + + // ==================== 扩展字段 ==================== + /** 是否已获得该成就 */ + obtained?: boolean; + } + + /** + * 成就事件实体 + * 用于触发成就检测和授予 + * @since 2025-10-24 + */ + export interface AchievementEvent { + /** 用户ID */ + userID: string; + /** 事件类型 */ + eventType: AchievementEventType; + /** 事件值(如学习时长、资源数量等) */ + eventValue?: number; + /** 事件时间 */ + eventTime?: string; + /** 额外数据 */ + extraData?: Record; + } \ No newline at end of file diff --git a/schoolNewsWeb/src/types/achievement/index.ts b/schoolNewsWeb/src/types/achievement/index.ts new file mode 100644 index 0000000..3e98de6 --- /dev/null +++ b/schoolNewsWeb/src/types/achievement/index.ts @@ -0,0 +1 @@ +export * from './achievement'; \ No newline at end of file diff --git a/schoolNewsWeb/src/types/enums/achievement-enums.ts b/schoolNewsWeb/src/types/enums/achievement-enums.ts new file mode 100644 index 0000000..79e928e --- /dev/null +++ b/schoolNewsWeb/src/types/enums/achievement-enums.ts @@ -0,0 +1,186 @@ +/** + * @description 成就相关枚举辅助工具 + * @author yslg + * @since 2025-10-25 + */ + +import { AchievementType, AchievementConditionType, AchievementEventType } from './index'; + +/** + * 成就类型描述映射 + */ +export const AchievementTypeDescriptions: Record = { + [AchievementType.BADGE]: '勋章', + [AchievementType.LEVEL]: '等级' +}; + +/** + * 成就条件类型描述映射 + */ +export const AchievementConditionTypeDescriptions: Record = { + [AchievementConditionType.LEARNING_TIME]: '学习时长', + [AchievementConditionType.RESOURCE_VIEW_COUNT]: '浏览资源数量', + [AchievementConditionType.COURSE_COMPLETE_COUNT]: '完成课程数量', + [AchievementConditionType.CONTINUOUS_LOGIN_DAYS]: '连续登录天数', + [AchievementConditionType.RESOURCE_COLLECT_COUNT]: '收藏资源数量', + [AchievementConditionType.TASK_COMPLETE_COUNT]: '完成任务数量', + [AchievementConditionType.POINTS_EARNED]: '获得积分数量', + [AchievementConditionType.COMMENT_COUNT]: '发表评论数量', + [AchievementConditionType.CHAPTER_COMPLETE_COUNT]: '完成章节数量', + [AchievementConditionType.TOTAL_LOGIN_DAYS]: '累计登录天数' +}; + +/** + * 成就事件类型描述映射 + */ +export const AchievementEventTypeDescriptions: Record = { + // 学习相关事件 + [AchievementEventType.LEARNING_TIME_UPDATED]: '学习时长更新', + [AchievementEventType.COURSE_COMPLETED]: '课程完成', + [AchievementEventType.COURSE_STARTED]: '开始学习课程', + [AchievementEventType.CHAPTER_COMPLETED]: '章节完成', + // 资源相关事件 + [AchievementEventType.RESOURCE_VIEWED]: '浏览资源', + [AchievementEventType.RESOURCE_COLLECTED]: '收藏资源', + [AchievementEventType.RESOURCE_SHARED]: '分享资源', + // 任务相关事件 + [AchievementEventType.TASK_COMPLETED]: '任务完成', + [AchievementEventType.TASK_ITEM_COMPLETED]: '任务项完成', + // 互动相关事件 + [AchievementEventType.COMMENT_POSTED]: '发表评论', + [AchievementEventType.LIKE_GIVEN]: '点赞', + // 登录相关事件 + [AchievementEventType.USER_LOGIN]: '用户登录', + [AchievementEventType.CONTINUOUS_LOGIN]: '连续登录', + // 积分相关事件 + [AchievementEventType.POINTS_EARNED_EVENT]: '获得积分', + // 测试相关事件 + [AchievementEventType.TEST_PASSED]: '测试通过', + [AchievementEventType.TEST_PERFECT_SCORE]: '测试满分' +}; + +/** + * 成就枚举辅助类 + */ +export class AchievementEnumHelper { + + /** + * 获取成就类型描述 + * @param type 成就类型 + * @returns 描述文本 + */ + static getAchievementTypeDescription(type: AchievementType): string { + return AchievementTypeDescriptions[type] || '未知类型'; + } + + /** + * 获取成就条件类型描述 + * @param type 条件类型 + * @returns 描述文本 + */ + static getConditionTypeDescription(type: AchievementConditionType): string { + return AchievementConditionTypeDescriptions[type] || '未知条件'; + } + + /** + * 获取成就事件类型描述 + * @param type 事件类型 + * @returns 描述文本 + */ + static getEventTypeDescription(type: AchievementEventType): string { + return AchievementEventTypeDescriptions[type] || '未知事件'; + } + + /** + * 根据code获取成就条件类型 + * @param code 条件类型代码 + * @returns 条件类型枚举值或null + */ + static getConditionTypeFromCode(code: number): AchievementConditionType | null { + const types = Object.values(AchievementConditionType).filter( + (value): value is AchievementConditionType => typeof value === 'number' + ); + return types.find(type => type === code) || null; + } + + /** + * 根据code获取成就事件类型 + * @param code 事件类型代码 + * @returns 事件类型枚举值或null + */ + static getEventTypeFromCode(code: string): AchievementEventType | null { + const types = Object.values(AchievementEventType).filter( + (value): value is AchievementEventType => typeof value === 'string' + ); + return types.find(type => type === code) || null; + } + + /** + * 获取所有成就条件类型选项(用于下拉框等) + * @returns 选项数组 + */ + static getAllConditionTypeOptions(): Array<{ value: AchievementConditionType; label: string }> { + return Object.entries(AchievementConditionTypeDescriptions).map(([value, label]) => ({ + value: Number(value) as AchievementConditionType, + label + })); + } + + /** + * 获取所有成就类型选项(用于下拉框等) + * @returns 选项数组 + */ + static getAllAchievementTypeOptions(): Array<{ value: AchievementType; label: string }> { + return Object.entries(AchievementTypeDescriptions).map(([value, label]) => ({ + value: Number(value) as AchievementType, + label + })); + } + + /** + * 获取所有成就事件类型选项(用于下拉框等) + * @returns 选项数组 + */ + static getAllEventTypeOptions(): Array<{ value: AchievementEventType; label: string }> { + return Object.entries(AchievementEventTypeDescriptions).map(([value, label]) => ({ + value: value as AchievementEventType, + label + })); + } + + /** + * 格式化条件值显示 + * @param conditionType 条件类型 + * @param conditionValue 条件值 + * @returns 格式化后的显示文本 + */ + static formatConditionValue(conditionType: AchievementConditionType, conditionValue: number): string { + const typeDesc = this.getConditionTypeDescription(conditionType); + + switch (conditionType) { + case AchievementConditionType.LEARNING_TIME: + return `${typeDesc}达到${conditionValue}分钟`; + case AchievementConditionType.RESOURCE_VIEW_COUNT: + return `${typeDesc}达到${conditionValue}个`; + case AchievementConditionType.COURSE_COMPLETE_COUNT: + return `${typeDesc}达到${conditionValue}门`; + case AchievementConditionType.CONTINUOUS_LOGIN_DAYS: + return `${typeDesc}达到${conditionValue}天`; + case AchievementConditionType.RESOURCE_COLLECT_COUNT: + return `${typeDesc}达到${conditionValue}个`; + case AchievementConditionType.TASK_COMPLETE_COUNT: + return `${typeDesc}达到${conditionValue}个`; + case AchievementConditionType.POINTS_EARNED: + return `${typeDesc}达到${conditionValue}分`; + case AchievementConditionType.COMMENT_COUNT: + return `${typeDesc}达到${conditionValue}条`; + case AchievementConditionType.CHAPTER_COMPLETE_COUNT: + return `${typeDesc}达到${conditionValue}个`; + case AchievementConditionType.TOTAL_LOGIN_DAYS: + return `${typeDesc}达到${conditionValue}天`; + default: + return `${typeDesc}达到${conditionValue}`; + } + } +} + diff --git a/schoolNewsWeb/src/types/enums/index.ts b/schoolNewsWeb/src/types/enums/index.ts index 276b0b3..ee1b951 100644 --- a/schoolNewsWeb/src/types/enums/index.ts +++ b/schoolNewsWeb/src/types/enums/index.ts @@ -210,3 +210,90 @@ export enum TaskItemType { /** 课程类型 */ COURSE = 2 } + +/** + * 成就类型枚举 + */ +export enum AchievementType { + /** 勋章 */ + BADGE = 1, + /** 等级 */ + LEVEL = 2 +} + +/** + * 成就条件类型枚举 + */ +export enum AchievementConditionType { + /** 学习时长(分钟) */ + LEARNING_TIME = 1, + /** 浏览资源数量 */ + RESOURCE_VIEW_COUNT = 2, + /** 完成课程数量 */ + COURSE_COMPLETE_COUNT = 3, + /** 连续登录天数 */ + CONTINUOUS_LOGIN_DAYS = 4, + /** 收藏资源数量 */ + RESOURCE_COLLECT_COUNT = 5, + /** 完成任务数量 */ + TASK_COMPLETE_COUNT = 6, + /** 获得积分数量 */ + POINTS_EARNED = 7, + /** 发表评论数量 */ + COMMENT_COUNT = 8, + /** 完成章节数量 */ + CHAPTER_COMPLETE_COUNT = 9, + /** 累计登录天数 */ + TOTAL_LOGIN_DAYS = 10 +} + +/** + * 成就事件类型枚举 + */ +export enum AchievementEventType { + // ==================== 学习相关事件 ==================== + /** 学习时长更新 */ + LEARNING_TIME_UPDATED = 'learning_time_updated', + /** 课程完成 */ + COURSE_COMPLETED = 'course_completed', + /** 开始学习课程 */ + COURSE_STARTED = 'course_started', + /** 章节完成 */ + CHAPTER_COMPLETED = 'chapter_completed', + + // ==================== 资源相关事件 ==================== + /** 浏览资源 */ + RESOURCE_VIEWED = 'resource_viewed', + /** 收藏资源 */ + RESOURCE_COLLECTED = 'resource_collected', + /** 分享资源 */ + RESOURCE_SHARED = 'resource_shared', + + // ==================== 任务相关事件 ==================== + /** 任务完成 */ + TASK_COMPLETED = 'task_completed', + /** 任务项完成 */ + TASK_ITEM_COMPLETED = 'task_item_completed', + + // ==================== 互动相关事件 ==================== + /** 发表评论 */ + COMMENT_POSTED = 'comment_posted', + /** 点赞 */ + LIKE_GIVEN = 'like_given', + + // ==================== 登录相关事件 ==================== + /** 用户登录 */ + USER_LOGIN = 'user_login', + /** 连续登录 */ + CONTINUOUS_LOGIN = 'continuous_login', + + // ==================== 积分相关事件 ==================== + /** 获得积分 */ + POINTS_EARNED_EVENT = 'points_earned', + + // ==================== 测试相关事件 ==================== + /** 测试通过 */ + TEST_PASSED = 'test_passed', + /** 测试满分 */ + TEST_PERFECT_SCORE = 'test_perfect_score' +} \ No newline at end of file diff --git a/schoolNewsWeb/src/types/index.ts b/schoolNewsWeb/src/types/index.ts index af0b25c..8410af4 100644 --- a/schoolNewsWeb/src/types/index.ts +++ b/schoolNewsWeb/src/types/index.ts @@ -22,6 +22,11 @@ export * from './menu'; // 权限相关 export * from './permission'; +// 系统相关 +export * from './module'; + +export * from './achievement'; + // 认证相关 export * from './auth'; @@ -42,6 +47,7 @@ export * from './usercenter'; // 枚举类型 export * from './enums'; +export * from './enums/achievement-enums'; // 常量 export * from './constants'; diff --git a/schoolNewsWeb/src/types/module/index.ts b/schoolNewsWeb/src/types/module/index.ts new file mode 100644 index 0000000..0e34805 --- /dev/null +++ b/schoolNewsWeb/src/types/module/index.ts @@ -0,0 +1,162 @@ +/** + * @description 系统相关类型定义 + * @author yslg + * @since 2025-10-25 + */ + +import { BaseDTO } from '../base'; + +/** + * 系统模块 + */ +export interface SysModule extends BaseDTO { + /** 模块ID */ + moduleID?: string; + /** 模块名称 */ + name?: string; + /** 模块代码 */ + code?: string; + /** 模块描述 */ + description?: string; + /** 模块图标 */ + icon?: string; + /** 模块排序号 */ + orderNum?: number; + /** 模块状态(0禁用 1启用) */ + status?: number; + /** 创建者 */ + creator?: string; + /** 更新者 */ + updater?: string; +} + +/** + * 系统配置 + */ +export interface SysConfig extends BaseDTO { + /** 配置键 */ + configKey?: string; + /** 配置值 */ + configValue?: string; + /** 配置名称 */ + configName?: string; + /** 配置描述 */ + description?: string; + /** 配置类型 */ + configType?: string; + /** 是否系统内置 */ + isSystem?: boolean; + /** 创建者 */ + creator?: string; + /** 更新者 */ + updater?: string; +} + +/** + * 系统字典类型 + */ +export interface SysDictType extends BaseDTO { + /** 字典类型 */ + dictType?: string; + /** 字典名称 */ + dictName?: string; + /** 状态(0禁用 1启用) */ + status?: number; + /** 备注 */ + remark?: string; + /** 创建者 */ + creator?: string; + /** 更新者 */ + updater?: string; +} + +/** + * 系统字典数据 + */ +export interface SysDictData extends BaseDTO { + /** 字典排序 */ + dictSort?: number; + /** 字典标签 */ + dictLabel?: string; + /** 字典键值 */ + dictValue?: string; + /** 字典类型 */ + dictType?: string; + /** 状态(0禁用 1启用) */ + status?: number; + /** 是否默认 */ + isDefault?: boolean; + /** 备注 */ + remark?: string; + /** 创建者 */ + creator?: string; + /** 更新者 */ + updater?: string; +} + +/** + * 系统通知 + */ +export interface SysNotification extends BaseDTO { + /** 通知ID */ + notificationID?: string; + /** 通知标题 */ + title?: string; + /** 通知内容 */ + content?: string; + /** 通知类型 */ + type?: number; + /** 接收用户ID */ + receiverID?: string; + /** 是否已读 */ + isRead?: boolean; + /** 已读时间 */ + readTime?: string; + /** 创建者 */ + creator?: string; +} + +/** + * 系统操作日志 + */ +export interface SysOperationLog extends BaseDTO { + /** 操作用户 */ + operator?: string; + /** 操作模块 */ + module?: string; + /** 操作类型 */ + operationType?: string; + /** 操作方法 */ + method?: string; + /** 请求参数 */ + requestParams?: string; + /** 返回结果 */ + responseResult?: string; + /** 操作状态(0失败 1成功) */ + status?: number; + /** 错误消息 */ + errorMsg?: string; + /** 操作IP */ + operationIP?: string; + /** 操作时间 */ + operationTime?: string; + /** 执行时长(毫秒) */ + duration?: number; +} + +/** + * 系统访问统计 + */ +export interface SysVisitStatistics extends BaseDTO { + /** 统计日期 */ + statisticsDate?: string; + /** 访问量 */ + visitCount?: number; + /** 独立访客数 */ + uniqueVisitors?: number; + /** 页面浏览量 */ + pageViews?: number; + /** 平均访问时长(秒) */ + avgDuration?: number; +} + diff --git a/schoolNewsWeb/src/types/permission/index.ts b/schoolNewsWeb/src/types/permission/index.ts index e5ef849..69e60e1 100644 --- a/schoolNewsWeb/src/types/permission/index.ts +++ b/schoolNewsWeb/src/types/permission/index.ts @@ -7,6 +7,7 @@ import { BaseDTO } from '../base'; import { SysMenu } from '../menu'; import { SysRole } from '../role'; +import { SysModule } from '../module'; /** * 系统权限 @@ -16,6 +17,7 @@ export interface SysPermission extends BaseDTO { id?:string; /** 权限ID */ permissionID?: string; + moduleID?: string; /** 权限名称 */ name?: string; /** 权限描述 */ @@ -34,5 +36,5 @@ export interface SysPermission extends BaseDTO { menus?: SysMenu[]; roles?: SysRole[]; permissions?: SysPermission[]; -} - + module?: SysModule; +} \ No newline at end of file diff --git a/schoolNewsWeb/src/types/usercenter/index.ts b/schoolNewsWeb/src/types/usercenter/index.ts index 2f15c3b..8eaf9ea 100644 --- a/schoolNewsWeb/src/types/usercenter/index.ts +++ b/schoolNewsWeb/src/types/usercenter/index.ts @@ -68,38 +68,6 @@ export interface PointsRecord extends BaseDTO { relatedType?: number; } -/** - * 用户成就实体 - */ -export interface UserAchievement extends BaseDTO { - /** 用户ID */ - userID?: string; - /** 成就ID */ - achievementID?: string; - /** 获得时间 */ - achieveTime?: string; -} - -/** - * 成就实体 - */ -export interface Achievement extends BaseDTO { - /** 成就名称 */ - name?: string; - /** 成就描述 */ - description?: string; - /** 成就图标 */ - icon?: string; - /** 成就类型 */ - type?: number; - /** 获得条件 */ - condition?: string; - /** 奖励积分 */ - rewardPoints?: number; - /** 状态(0禁用 1启用) */ - status?: number; -} - /** * 个人中心统计信息 */ diff --git a/schoolNewsWeb/src/views/admin/manage/achievement/AchievementManagementView.vue b/schoolNewsWeb/src/views/admin/manage/achievement/AchievementManagementView.vue new file mode 100644 index 0000000..ef976b2 --- /dev/null +++ b/schoolNewsWeb/src/views/admin/manage/achievement/AchievementManagementView.vue @@ -0,0 +1,618 @@ + + + + 成就管理 + + + 新增成就 + + + + + + + 成就类型 + + + + + + 成就等级 + + + + 条件类型 + + + + + + 查询 + 重置 + + + + + + + + + + + + + + + + + + + + + + {{ getAchievementTypeLabel(row.type) }} + + + + + + + {{ getConditionTypeLabel(row.conditionType) }} + + + + + + + + + + 编辑 + + + 查看获得者 + + + 删除 + + + + + + + + + + + + + + + + + + 上传 + + + + + + + + + + + + + 路径: {{ currentAchievement.icon }} + 完整URL: {{ getIconUrl(currentAchievement.icon) }} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 取消 + + {{ isEdit ? '更新' : '创建' }} + + + + + + + + + + + + + + + + + + + 撤销成就 + + + + + + + + + + + + diff --git a/schoolNewsWeb/src/views/admin/manage/achievement/index.ts b/schoolNewsWeb/src/views/admin/manage/achievement/index.ts new file mode 100644 index 0000000..765895a --- /dev/null +++ b/schoolNewsWeb/src/views/admin/manage/achievement/index.ts @@ -0,0 +1,2 @@ +export { default as AchievementManagementView } from './AchievementManagementView.vue'; + diff --git a/schoolNewsWeb/src/views/admin/manage/system/ModuleManageView.vue b/schoolNewsWeb/src/views/admin/manage/system/ModuleManageView.vue deleted file mode 100644 index e69de29..0000000 diff --git a/schoolNewsWeb/src/views/admin/manage/system/ModulePermissionManageView.vue b/schoolNewsWeb/src/views/admin/manage/system/ModulePermissionManageView.vue new file mode 100644 index 0000000..a3b2b4d --- /dev/null +++ b/schoolNewsWeb/src/views/admin/manage/system/ModulePermissionManageView.vue @@ -0,0 +1,1159 @@ + + + + 模块权限管理 + + + 新增模块 + + + + + + + + 模块列表 + + + + + + + + + {{ module.name }} + {{ module.code }} + + + {{ module.status === 1 ? '启用' : '禁用' }} + + + + + 编辑 + + + 删除 + + + + + + + + + + + + + + + + + {{ currentModule.name }} + + 模块代码:{{ currentModule.code }} + 模块ID:{{ currentModule.moduleID }} + + + + + 创建权限 + + + + + + + + + {{ permission.name }} + 已绑定 + + + + 权限编码: + {{ permission.code }} + + + 描述: + {{ permission.description }} + + + + + + 编辑 + + + 绑定菜单 + + + 绑定角色 + + + 删除 + + + + + + + + + + + + + + + + 模块名称 + + + + 模块代码 + + 模块代码必须以小写字母开头,只能包含小写字母、数字和下划线 + + + 模块图标 + + + + + + + + 排序号 + + + + 模块状态 + + 启用 + 禁用 + + + + 模块描述 + + + + + + 取消 + + 确定 + + + + + + + + + 权限名称 + + + + 权限编码 + + 建议格式:模块:功能:操作,如 system:user:create + + + 权限描述 + + + + + + 取消 + + 确定 + + + + + + + + + 权限信息:{{ currentPermission.name }} + 权限编码:{{ currentPermission.code }} + + + + + + + {{ isMenuSelected(row.menuID) ? '已绑定' : '未绑定' }} + + + + + + + + + + {{ isMenuSelected(row.menuID) ? '解绑' : '绑定' }} + + + + + + + + + + + + 取消 + + 保存 + + + + + + + + + 权限信息:{{ currentPermission.name }} + 权限编码:{{ currentPermission.code }} + + + + + + + {{ isRoleSelected(row.roleID) ? '已绑定' : '未绑定' }} + + + + + + + + + + {{ isRoleSelected(row.roleID) ? '解绑' : '绑定' }} + + + + + + + + + + + + 取消 + + 保存 + + + + + + + + + + diff --git a/schoolNewsWeb/src/views/admin/manage/system/PermissionManageView.vue b/schoolNewsWeb/src/views/admin/manage/system/PermissionManageView.vue deleted file mode 100644 index 1263140..0000000 --- a/schoolNewsWeb/src/views/admin/manage/system/PermissionManageView.vue +++ /dev/null @@ -1,721 +0,0 @@ - - - - 权限管理 - - - 新增权限 - - - - - - - - - - - - - 编辑 - - - 绑定菜单 - - - 绑定角色 - - - 删除 - - - - - - - - - - - - - - - - - - - - - 取消 - - 确定 - - - - - - - - - - 权限信息:{{ currentPermission.name }} - 权限编码:{{ currentPermission.code }} - - - - - - - - {{ isMenuSelected(row.menuID) ? '已绑定' : '未绑定' }} - - - - - - - - - - - {{ isMenuSelected(row.menuID) ? '解绑' : '绑定' }} - - - - - - - - - - - - - 取消 - - 保存 - - - - - - - - - - 权限信息:{{ currentPermission.name }} - 权限编码:{{ currentPermission.code }} - - - - - - - - {{ isRoleSelected(row.id) ? '已绑定' : '未绑定' }} - - - - - - - - - - {{ isRoleSelected(row.id) ? '解绑' : '绑定' }} - - - - - - - - - - - - - 取消 - - 保存 - - - - - - - - - \ No newline at end of file diff --git a/schoolNewsWeb/src/views/user-center/MyAchievementsView.vue b/schoolNewsWeb/src/views/user-center/MyAchievementsView.vue index d5d8449..5f51e90 100644 --- a/schoolNewsWeb/src/views/user-center/MyAchievementsView.vue +++ b/schoolNewsWeb/src/views/user-center/MyAchievementsView.vue @@ -3,59 +3,257 @@ 我的成就 - 已获得 {{ earnedCount }} / {{ totalCount }} 个成就 + + 已获得 + {{ earnedCount }} + / {{ totalCount }} + + + 完成率 + {{ completionRate }}% + - + + + + 全部 + + {{ option.label }} + + + + + 仅显示已获得 + + + + + + + + + + - - ✓ + + + + + + + + + + + + Lv.{{ achievement.level }} + - {{ achievement.name }} - {{ achievement.description }} - - - - - {{ achievement.progress }}% + + {{ achievement.name }} + + {{ getAchievementTypeLabel(achievement.type) }} + - - 获得时间:{{ achievement.earnedDate }} + {{ achievement.description }} + + + + + {{ formatConditionValue(achievement.conditionType, achievement.conditionValue) }} + + + + + + 进度 + + {{ achievement.currentValue || 0 }} / {{ achievement.targetValue || achievement.conditionValue }} + + + + + + + + + + + + 奖励 {{ achievement.points }} 积分 + + +
权限编码:{{ currentPermission.code }}
{{ achievement.description }}