课程强制发布
This commit is contained in:
@@ -114,6 +114,7 @@ INSERT INTO `tb_sys_permission` (id, permission_id, name, code, description, mod
|
|||||||
|
|
||||||
-- 1100-1199: 文章管理
|
-- 1100-1199: 文章管理
|
||||||
('1100', 'perm_admin_article_manage', '文章管理', 'admin:article:manage', '访问文章管理视图权限', 'module_news', '1', now()),
|
('1100', 'perm_admin_article_manage', '文章管理', 'admin:article:manage', '访问文章管理视图权限', 'module_news', '1', now()),
|
||||||
|
('1101', 'perm_admin_article_force_publish', '文章强制发布', 'admin:article:force-publish', '强制发布文章权限(跳过敏感词校验)', 'module_news', '1', now()),
|
||||||
|
|
||||||
-- 1200-1299: Banner管理
|
-- 1200-1299: Banner管理
|
||||||
('1200', 'perm_admin_banner_manage', 'Banner管理', 'admin:banner:manage', '访问Banner管理视图权限', 'module_news', '1', now()),
|
('1200', 'perm_admin_banner_manage', 'Banner管理', 'admin:banner:manage', '访问Banner管理视图权限', 'module_news', '1', now()),
|
||||||
@@ -135,6 +136,7 @@ INSERT INTO `tb_sys_permission` (id, permission_id, name, code, description, mod
|
|||||||
|
|
||||||
-- 2200-2299: 课程管理
|
-- 2200-2299: 课程管理
|
||||||
('2200', 'perm_admin_course_manage', '课程管理', 'admin:course:manage', '访问课程管理视图权限', 'module_study', '1', now()),
|
('2200', 'perm_admin_course_manage', '课程管理', 'admin:course:manage', '访问课程管理视图权限', 'module_study', '1', now()),
|
||||||
|
('2201', 'perm_admin_course_force_publish', '课程强制发布', 'admin:course:force-publish', '强制发布课程权限(跳过敏感词校验)', 'module_study', '1', now()),
|
||||||
|
|
||||||
-- 2300-2399: 成就管理
|
-- 2300-2399: 成就管理
|
||||||
('2300', 'perm_admin_achievement_manage', '成就管理', 'admin:achievement:manage', '访问成就管理视图权限', 'module_study', '1', now()),
|
('2300', 'perm_admin_achievement_manage', '成就管理', 'admin:achievement:manage', '访问成就管理视图权限', 'module_study', '1', now()),
|
||||||
@@ -200,6 +202,7 @@ INSERT INTO `tb_sys_role_permission` (id, role_id, permission_id, creator, creat
|
|||||||
-- 普通管理员权限 (1000-8999)
|
-- 普通管理员权限 (1000-8999)
|
||||||
('29', 'superadmin', 'perm_admin_resource_manage', '1', now()),
|
('29', 'superadmin', 'perm_admin_resource_manage', '1', now()),
|
||||||
('30', 'superadmin', 'perm_admin_article_manage', '1', now()),
|
('30', 'superadmin', 'perm_admin_article_manage', '1', now()),
|
||||||
|
('46', 'superadmin', 'perm_admin_article_force_publish', '1', now()),
|
||||||
('31', 'superadmin', 'perm_admin_banner_manage', '1', now()),
|
('31', 'superadmin', 'perm_admin_banner_manage', '1', now()),
|
||||||
('32', 'superadmin', 'perm_admin_tag_manage', '1', now()),
|
('32', 'superadmin', 'perm_admin_tag_manage', '1', now()),
|
||||||
('33', 'superadmin', 'perm_admin_column_manage', '1', now()),
|
('33', 'superadmin', 'perm_admin_column_manage', '1', now()),
|
||||||
@@ -207,6 +210,7 @@ INSERT INTO `tb_sys_role_permission` (id, role_id, permission_id, creator, creat
|
|||||||
('35', 'superadmin', 'perm_admin_task_manage', '1', now()),
|
('35', 'superadmin', 'perm_admin_task_manage', '1', now()),
|
||||||
('36', 'superadmin', 'perm_admin_study_records', '1', now()),
|
('36', 'superadmin', 'perm_admin_study_records', '1', now()),
|
||||||
('37', 'superadmin', 'perm_admin_course_manage', '1', now()),
|
('37', 'superadmin', 'perm_admin_course_manage', '1', now()),
|
||||||
|
('47', 'superadmin', 'perm_admin_course_force_publish', '1', now()),
|
||||||
('38', 'superadmin', 'perm_admin_achievement_manage', '1', now()),
|
('38', 'superadmin', 'perm_admin_achievement_manage', '1', now()),
|
||||||
('39', 'superadmin', 'perm_admin_ai_config', '1', now()),
|
('39', 'superadmin', 'perm_admin_ai_config', '1', now()),
|
||||||
('40', 'superadmin', 'perm_admin_knowledge_manage', '1', now()),
|
('40', 'superadmin', 'perm_admin_knowledge_manage', '1', now()),
|
||||||
@@ -249,6 +253,7 @@ INSERT INTO `tb_sys_role_permission` (id, role_id, permission_id, creator, creat
|
|||||||
-- 普通管理员权限 (1000-8999)
|
-- 普通管理员权限 (1000-8999)
|
||||||
('122', 'admin', 'perm_admin_resource_manage', '1', now()),
|
('122', 'admin', 'perm_admin_resource_manage', '1', now()),
|
||||||
('123', 'admin', 'perm_admin_article_manage', '1', now()),
|
('123', 'admin', 'perm_admin_article_manage', '1', now()),
|
||||||
|
('140', 'admin', 'perm_admin_article_force_publish', '1', now()),
|
||||||
('124', 'admin', 'perm_admin_banner_manage', '1', now()),
|
('124', 'admin', 'perm_admin_banner_manage', '1', now()),
|
||||||
('125', 'admin', 'perm_admin_tag_manage', '1', now()),
|
('125', 'admin', 'perm_admin_tag_manage', '1', now()),
|
||||||
('126', 'admin', 'perm_admin_column_manage', '1', now()),
|
('126', 'admin', 'perm_admin_column_manage', '1', now()),
|
||||||
@@ -256,6 +261,7 @@ INSERT INTO `tb_sys_role_permission` (id, role_id, permission_id, creator, creat
|
|||||||
('128', 'admin', 'perm_admin_task_manage', '1', now()),
|
('128', 'admin', 'perm_admin_task_manage', '1', now()),
|
||||||
('129', 'admin', 'perm_admin_study_records', '1', now()),
|
('129', 'admin', 'perm_admin_study_records', '1', now()),
|
||||||
('130', 'admin', 'perm_admin_course_manage', '1', now()),
|
('130', 'admin', 'perm_admin_course_manage', '1', now()),
|
||||||
|
('141', 'admin', 'perm_admin_course_force_publish', '1', now()),
|
||||||
('131', 'admin', 'perm_admin_achievement_manage', '1', now()),
|
('131', 'admin', 'perm_admin_achievement_manage', '1', now()),
|
||||||
('132', 'admin', 'perm_admin_ai_config', '1', now()),
|
('132', 'admin', 'perm_admin_ai_config', '1', now()),
|
||||||
('133', 'admin', 'perm_admin_knowledge_manage', '1', now()),
|
('133', 'admin', 'perm_admin_knowledge_manage', '1', now()),
|
||||||
|
|||||||
@@ -0,0 +1,41 @@
|
|||||||
|
-- ============================================
|
||||||
|
-- 强制发布权限更新脚本
|
||||||
|
-- 用于更新现有数据库,添加文章和课程的强制发布权限
|
||||||
|
-- 执行时间:2026-01-14
|
||||||
|
-- ============================================
|
||||||
|
|
||||||
|
USE school_news;
|
||||||
|
|
||||||
|
-- 1. 插入新的权限数据
|
||||||
|
INSERT INTO `tb_sys_permission` (id, permission_id, name, code, description, module_id, creator, create_time) VALUES
|
||||||
|
('1101', 'perm_admin_article_force_publish', '文章强制发布', 'admin:article:force-publish', '强制发布文章权限(跳过敏感词校验)', 'module_news', '1', now()),
|
||||||
|
('2201', 'perm_admin_course_force_publish', '课程强制发布', 'admin:course:force-publish', '强制发布课程权限(跳过敏感词校验)', 'module_study', '1', now())
|
||||||
|
ON DUPLICATE KEY UPDATE
|
||||||
|
name = VALUES(name),
|
||||||
|
code = VALUES(code),
|
||||||
|
description = VALUES(description),
|
||||||
|
update_time = now();
|
||||||
|
|
||||||
|
-- 2. 为超级管理员角色添加强制发布权限
|
||||||
|
INSERT INTO `tb_sys_role_permission` (id, role_id, permission_id, creator, create_time) VALUES
|
||||||
|
('46', 'superadmin', 'perm_admin_article_force_publish', '1', now()),
|
||||||
|
('47', 'superadmin', 'perm_admin_course_force_publish', '1', now())
|
||||||
|
ON DUPLICATE KEY UPDATE update_time = now();
|
||||||
|
|
||||||
|
-- 3. 为管理员角色添加强制发布权限
|
||||||
|
INSERT INTO `tb_sys_role_permission` (id, role_id, permission_id, creator, create_time) VALUES
|
||||||
|
('140', 'admin', 'perm_admin_article_force_publish', '1', now()),
|
||||||
|
('141', 'admin', 'perm_admin_course_force_publish', '1', now())
|
||||||
|
ON DUPLICATE KEY UPDATE update_time = now();
|
||||||
|
|
||||||
|
-- 验证插入结果
|
||||||
|
SELECT '=== 新增权限 ===' AS info;
|
||||||
|
SELECT permission_id, name, code, description FROM tb_sys_permission
|
||||||
|
WHERE permission_id IN ('perm_admin_article_force_publish', 'perm_admin_course_force_publish');
|
||||||
|
|
||||||
|
SELECT '=== 角色权限关联 ===' AS info;
|
||||||
|
SELECT r.name AS role_name, p.name AS permission_name, p.code
|
||||||
|
FROM tb_sys_role_permission rp
|
||||||
|
JOIN tb_sys_role r ON r.role_id = rp.role_id
|
||||||
|
JOIN tb_sys_permission p ON p.permission_id = rp.permission_id
|
||||||
|
WHERE p.permission_id IN ('perm_admin_article_force_publish', 'perm_admin_course_force_publish');
|
||||||
@@ -199,4 +199,13 @@ public interface CourseService {
|
|||||||
* @since 2025-11-14
|
* @since 2025-11-14
|
||||||
*/
|
*/
|
||||||
ResultDomain<Integer> getCourseCount(TbCourse filter);
|
ResultDomain<Integer> getCourseCount(TbCourse filter);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 强制发布课程(跳过敏感词校验)
|
||||||
|
* @param courseID 课程ID
|
||||||
|
* @return ResultDomain<TbCourse> 发布结果
|
||||||
|
* @author kiro
|
||||||
|
* @since 2026-01-14
|
||||||
|
*/
|
||||||
|
ResultDomain<TbCourse> forcePublishCourse(String courseID);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -166,4 +166,12 @@ public class CourseController {
|
|||||||
return courseService.incrementLearnCount(courseID);
|
return courseService.incrementLearnCount(courseID);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 强制发布课程(跳过敏感词校验)
|
||||||
|
*/
|
||||||
|
@PostMapping("/{courseID}/force-publish")
|
||||||
|
public ResultDomain<TbCourse> forcePublishCourse(@PathVariable("courseID") String courseID) {
|
||||||
|
return courseService.forcePublishCourse(courseID);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -154,6 +154,72 @@ public class SCCourseServiceImpl implements SCCourseService {
|
|||||||
return resultDomain;
|
return resultDomain;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 验证课程基本信息
|
||||||
|
if (courseItemVO.getName() == null || courseItemVO.getName().trim().isEmpty()) {
|
||||||
|
resultDomain.fail("课程名称不能为空");
|
||||||
|
return resultDomain;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 验证章节
|
||||||
|
List<CourseItemVO> chapterVOs = courseItemVO.getChapters();
|
||||||
|
if (chapterVOs == null || chapterVOs.isEmpty()) {
|
||||||
|
resultDomain.fail("课程至少需要一个章节");
|
||||||
|
return resultDomain;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 验证每个章节
|
||||||
|
for (int i = 0; i < chapterVOs.size(); i++) {
|
||||||
|
CourseItemVO chapterVO = chapterVOs.get(i);
|
||||||
|
if (chapterVO.getName() == null || chapterVO.getName().trim().isEmpty()) {
|
||||||
|
resultDomain.fail("第" + (i + 1) + "个章节名称不能为空");
|
||||||
|
return resultDomain;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 验证章节节点
|
||||||
|
List<CourseItemVO> nodeVOs = chapterVO.getChapters();
|
||||||
|
if (nodeVOs == null || nodeVOs.isEmpty()) {
|
||||||
|
resultDomain.fail("章节「" + chapterVO.getName() + "」至少需要一个学习节点");
|
||||||
|
return resultDomain;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 验证每个节点
|
||||||
|
for (int j = 0; j < nodeVOs.size(); j++) {
|
||||||
|
CourseItemVO nodeVO = nodeVOs.get(j);
|
||||||
|
if (nodeVO.getName() == null || nodeVO.getName().trim().isEmpty()) {
|
||||||
|
resultDomain.fail("章节「" + chapterVO.getName() + "」的第" + (j + 1) + "个节点名称不能为空");
|
||||||
|
return resultDomain;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 根据节点类型验证内容
|
||||||
|
Integer nodeType = nodeVO.getNodeType();
|
||||||
|
if (nodeType == null) {
|
||||||
|
resultDomain.fail("章节「" + chapterVO.getName() + "」的节点「" + nodeVO.getName() + "」类型不能为空");
|
||||||
|
return resultDomain;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 类型1:文章,需要resourceID
|
||||||
|
// 类型2:文件/视频,需要resourceID或videoUrl
|
||||||
|
// 类型3:文本,需要content
|
||||||
|
if (nodeType == 1) {
|
||||||
|
if (nodeVO.getResourceID() == null || nodeVO.getResourceID().trim().isEmpty()) {
|
||||||
|
resultDomain.fail("章节「" + chapterVO.getName() + "」的节点「" + nodeVO.getName() + "」需要关联文章");
|
||||||
|
return resultDomain;
|
||||||
|
}
|
||||||
|
} else if (nodeType == 2) {
|
||||||
|
if ((nodeVO.getResourceID() == null || nodeVO.getResourceID().trim().isEmpty())
|
||||||
|
&& (nodeVO.getVideoUrl() == null || nodeVO.getVideoUrl().trim().isEmpty())) {
|
||||||
|
resultDomain.fail("章节「" + chapterVO.getName() + "」的节点「" + nodeVO.getName() + "」需要上传文件或视频");
|
||||||
|
return resultDomain;
|
||||||
|
}
|
||||||
|
} else if (nodeType == 3) {
|
||||||
|
if (nodeVO.getContent() == null || nodeVO.getContent().trim().isEmpty()) {
|
||||||
|
resultDomain.fail("章节「" + chapterVO.getName() + "」的节点「" + nodeVO.getName() + "」内容不能为空");
|
||||||
|
return resultDomain;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 转换为课程实体并保存
|
// 转换为课程实体并保存
|
||||||
TbCourse course = courseItemVO.toCourse();
|
TbCourse course = courseItemVO.toCourse();
|
||||||
String courseID = IDUtils.generateID();
|
String courseID = IDUtils.generateID();
|
||||||
@@ -763,4 +829,72 @@ public class SCCourseServiceImpl implements SCCourseService {
|
|||||||
}
|
}
|
||||||
return resultDomain;
|
return resultDomain;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Transactional(rollbackFor = Exception.class)
|
||||||
|
public ResultDomain<TbCourse> forcePublishCourse(String courseID) {
|
||||||
|
ResultDomain<TbCourse> resultDomain = new ResultDomain<>();
|
||||||
|
try {
|
||||||
|
// 参数验证
|
||||||
|
if (courseID == null || courseID.isEmpty()) {
|
||||||
|
resultDomain.fail("课程ID不能为空");
|
||||||
|
return resultDomain;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查询课程
|
||||||
|
TbCourse course = courseMapper.selectByCourseId(courseID);
|
||||||
|
if (course == null) {
|
||||||
|
resultDomain.fail("课程不存在");
|
||||||
|
return resultDomain;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取所有课程节点,强制设置为已审核
|
||||||
|
List<TbCourseNode> nodeList = courseNodeMapper.selectByCourseId(courseID);
|
||||||
|
if (nodeList != null && !nodeList.isEmpty()) {
|
||||||
|
List<TbCourseNode> nodesToUpdate = new ArrayList<>();
|
||||||
|
for (TbCourseNode node : nodeList) {
|
||||||
|
if (!node.getIsAudited()) {
|
||||||
|
node.setIsAudited(true);
|
||||||
|
nodesToUpdate.add(node);
|
||||||
|
|
||||||
|
// 如果是文章类型节点,同时更新文章的审核状态
|
||||||
|
if (node.getNodeType() == 1 && node.getResourceID() != null) {
|
||||||
|
try {
|
||||||
|
ResourceVO resource = new ResourceVO();
|
||||||
|
resource.setResource(new TbResource());
|
||||||
|
resource.getResource().setResourceID(node.getResourceID());
|
||||||
|
resource.getResource().setIsAudited(true);
|
||||||
|
resourceService.updateResource(resource);
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.warn("更新节点关联文章审核状态失败: {}", e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 批量更新节点审核状态
|
||||||
|
if (!nodesToUpdate.isEmpty()) {
|
||||||
|
courseNodeMapper.batchUpdateNodeAudited(nodesToUpdate);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 强制发布:跳过敏感词校验,直接设置为已发布
|
||||||
|
course.setStatus(1);
|
||||||
|
course.setUpdateTime(new Date());
|
||||||
|
int result = courseMapper.updateCourse(course);
|
||||||
|
|
||||||
|
if (result > 0) {
|
||||||
|
logger.info("强制发布课程成功: {}", courseID);
|
||||||
|
// 重新查询返回完整数据
|
||||||
|
TbCourse updated = courseMapper.selectByCourseId(courseID);
|
||||||
|
resultDomain.success("强制发布课程成功", updated);
|
||||||
|
} else {
|
||||||
|
resultDomain.fail("强制发布课程失败");
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.error("强制发布课程异常: {}", e.getMessage(), e);
|
||||||
|
resultDomain.fail("强制发布课程失败: " + e.getMessage());
|
||||||
|
}
|
||||||
|
return resultDomain;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -176,6 +176,16 @@ export const courseApi = {
|
|||||||
return response.data;
|
return response.data;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 强制发布课程(跳过敏感词校验)
|
||||||
|
* @param courseID 课程ID
|
||||||
|
* @returns Promise<ResultDomain<Course>>
|
||||||
|
*/
|
||||||
|
async forcePublishCourse(courseID: string): Promise<ResultDomain<Course>> {
|
||||||
|
const response = await api.post<Course>(`${this.prefixCourse}/${courseID}/force-publish`);
|
||||||
|
return response.data;
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取课程章节列表
|
* 获取课程章节列表
|
||||||
* @param courseID 课程ID
|
* @param courseID 课程ID
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import App from "./App.vue";
|
|||||||
import "./registerServiceWorker";
|
import "./registerServiceWorker";
|
||||||
import router from "./router";
|
import router from "./router";
|
||||||
import store from "./store";
|
import store from "./store";
|
||||||
import { setupRouterGuards, setupTokenRefresh } from "@/utils/permission";
|
import { setupRouterGuards, setupTokenRefresh, setupPermissionUtils } from "@/utils/permission";
|
||||||
import { setupPermissionDirectives } from "@/directives/permission";
|
import { setupPermissionDirectives } from "@/directives/permission";
|
||||||
|
|
||||||
// 引入 Quill 富文本编辑器样式(全局)
|
// 引入 Quill 富文本编辑器样式(全局)
|
||||||
@@ -49,6 +49,9 @@ async function initApp() {
|
|||||||
// 设置权限指令
|
// 设置权限指令
|
||||||
setupPermissionDirectives(app, store);
|
setupPermissionDirectives(app, store);
|
||||||
|
|
||||||
|
// 设置权限工具
|
||||||
|
setupPermissionUtils(store);
|
||||||
|
|
||||||
// 设置路由守卫
|
// 设置路由守卫
|
||||||
setupRouterGuards(router, store);
|
setupRouterGuards(router, store);
|
||||||
|
|
||||||
|
|||||||
@@ -266,3 +266,64 @@ export class PermissionChecker {
|
|||||||
return roleCodes.some(code => this.hasRole(code));
|
return roleCodes.some(code => this.hasRole(code));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 全局store引用,由setupPermissionUtils初始化
|
||||||
|
let globalStore: Store<any> | null = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 初始化权限工具(在main.ts中调用)
|
||||||
|
*/
|
||||||
|
export function setupPermissionUtils(store: Store<any>) {
|
||||||
|
globalStore = store;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 权限检查 Composition API(用于Vue组件)
|
||||||
|
*/
|
||||||
|
export function usePermission() {
|
||||||
|
return {
|
||||||
|
/**
|
||||||
|
* 检查是否有指定权限
|
||||||
|
*/
|
||||||
|
hasPermission: (permissionCode: string): boolean => {
|
||||||
|
if (!globalStore) return false;
|
||||||
|
return globalStore.getters['auth/hasPermission'](permissionCode);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查是否有任意一个权限
|
||||||
|
*/
|
||||||
|
hasAnyPermission: (permissionCodes: string[]): boolean => {
|
||||||
|
if (!globalStore) return false;
|
||||||
|
return globalStore.getters['auth/hasAnyPermission'](permissionCodes);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查是否有所有权限
|
||||||
|
*/
|
||||||
|
hasAllPermissions: (permissionCodes: string[]): boolean => {
|
||||||
|
if (!globalStore) return false;
|
||||||
|
return globalStore.getters['auth/hasAllPermissions'](permissionCodes);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查是否有指定角色
|
||||||
|
*/
|
||||||
|
hasRole: (roleCode: string): boolean => {
|
||||||
|
if (!globalStore) return false;
|
||||||
|
const userRoles = globalStore.getters['auth/userRoles'] || [];
|
||||||
|
return userRoles.some((role: any) => role.code === roleCode);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查是否有任意一个角色
|
||||||
|
*/
|
||||||
|
hasAnyRole: (roleCodes: string[]): boolean => {
|
||||||
|
if (!globalStore) return false;
|
||||||
|
const userRoles = globalStore.getters['auth/userRoles'] || [];
|
||||||
|
return roleCodes.some(code =>
|
||||||
|
userRoles.some((role: any) => role.code === code)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|||||||
@@ -53,7 +53,7 @@
|
|||||||
{{ getActionButtonText(row.status) }}
|
{{ getActionButtonText(row.status) }}
|
||||||
</el-button>
|
</el-button>
|
||||||
<el-button
|
<el-button
|
||||||
v-if="row.status === ResourceStatus.SENSITIVE_FAILED"
|
v-if="row.status === ResourceStatus.SENSITIVE_FAILED && canForcePublish"
|
||||||
size="small"
|
size="small"
|
||||||
type="warning"
|
type="warning"
|
||||||
@click="forcePublishArticle(row)"
|
@click="forcePublishArticle(row)"
|
||||||
@@ -108,7 +108,7 @@ import { AdminLayout } from '@/views/admin';
|
|||||||
defineOptions({
|
defineOptions({
|
||||||
name: 'ArticleManagementView'
|
name: 'ArticleManagementView'
|
||||||
});
|
});
|
||||||
import { ref, onMounted } from 'vue';
|
import { ref, onMounted, computed } from 'vue';
|
||||||
import { ElButton, ElInput, ElTable, ElTableColumn, ElTag, ElPagination, ElMessage, ElMessageBox, ElIcon } from 'element-plus';
|
import { ElButton, ElInput, ElTable, ElTableColumn, ElTag, ElPagination, ElMessage, ElMessageBox, ElIcon } from 'element-plus';
|
||||||
import { Search } from '@element-plus/icons-vue';
|
import { Search } from '@element-plus/icons-vue';
|
||||||
import { useRouter } from 'vue-router';
|
import { useRouter } from 'vue-router';
|
||||||
@@ -116,8 +116,14 @@ import { resourceApi, resourceTagApi } from '@/apis/resource'
|
|||||||
import type { PageParam, ResourceSearchParams, Resource, Tag } from '@/types';
|
import type { PageParam, ResourceSearchParams, Resource, Tag } from '@/types';
|
||||||
import { ArticleShowView } from '@/views/public/article';
|
import { ArticleShowView } from '@/views/public/article';
|
||||||
import { ResourceStatus } from '@/types/enums';
|
import { ResourceStatus } from '@/types/enums';
|
||||||
|
import { usePermission } from '@/utils/permission';
|
||||||
|
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
|
||||||
|
// 权限检查
|
||||||
|
const { hasPermission } = usePermission();
|
||||||
|
const canForcePublish = computed(() => hasPermission('admin:article:force-publish'));
|
||||||
|
|
||||||
const searchKeyword = ref('');
|
const searchKeyword = ref('');
|
||||||
const pageParam = ref<PageParam>({
|
const pageParam = ref<PageParam>({
|
||||||
pageNumber: 1,
|
pageNumber: 1,
|
||||||
|
|||||||
@@ -73,7 +73,7 @@
|
|||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column prop="orderNum" label="排序" width="80" />
|
<el-table-column prop="orderNum" label="排序" width="80" />
|
||||||
<el-table-column label="操作" width="250" fixed="right">
|
<el-table-column label="操作" width="300" fixed="right">
|
||||||
<template #default="{ row }">
|
<template #default="{ row }">
|
||||||
<el-button type="primary" size="small" link @click="handleEdit(row)">
|
<el-button type="primary" size="small" link @click="handleEdit(row)">
|
||||||
编辑
|
编辑
|
||||||
@@ -87,6 +87,15 @@
|
|||||||
>
|
>
|
||||||
发布
|
发布
|
||||||
</el-button>
|
</el-button>
|
||||||
|
<el-button
|
||||||
|
v-if="row.status === 4 && canForcePublish"
|
||||||
|
type="warning"
|
||||||
|
size="small"
|
||||||
|
link
|
||||||
|
@click="handleForcePublish(row)"
|
||||||
|
>
|
||||||
|
强制发布
|
||||||
|
</el-button>
|
||||||
<el-button
|
<el-button
|
||||||
v-if="row.status === 1"
|
v-if="row.status === 1"
|
||||||
type="warning"
|
type="warning"
|
||||||
@@ -121,12 +130,13 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, reactive, onMounted } from 'vue';
|
import { ref, reactive, onMounted, computed } from 'vue';
|
||||||
import { ElMessage, ElMessageBox } from 'element-plus';
|
import { ElMessage, ElMessageBox } from 'element-plus';
|
||||||
import { Search, Plus } from '@element-plus/icons-vue';
|
import { Search, Plus } from '@element-plus/icons-vue';
|
||||||
import { courseApi } from '@/apis/study';
|
import { courseApi } from '@/apis/study';
|
||||||
import { FILE_DOWNLOAD_URL } from '@/config';
|
import { FILE_DOWNLOAD_URL } from '@/config';
|
||||||
import type { Course } from '@/types';
|
import type { Course } from '@/types';
|
||||||
|
import { usePermission } from '@/utils/permission';
|
||||||
|
|
||||||
defineOptions({
|
defineOptions({
|
||||||
name: 'CourseList'
|
name: 'CourseList'
|
||||||
@@ -137,6 +147,10 @@ const emit = defineEmits<{
|
|||||||
edit: [course: Course];
|
edit: [course: Course];
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
|
// 权限检查
|
||||||
|
const { hasPermission } = usePermission();
|
||||||
|
const canForcePublish = computed(() => hasPermission('admin:course:force-publish'));
|
||||||
|
|
||||||
const loading = ref(false);
|
const loading = ref(false);
|
||||||
const courseList = ref<Course[]>([]);
|
const courseList = ref<Course[]>([]);
|
||||||
const total = ref(0);
|
const total = ref(0);
|
||||||
@@ -211,7 +225,7 @@ async function handleUpdateStatus(course: Course, status: number) {
|
|||||||
ElMessage.success(`${statusText}成功`);
|
ElMessage.success(`${statusText}成功`);
|
||||||
loadCourses();
|
loadCourses();
|
||||||
} else {
|
} else {
|
||||||
ElMessage.error(`${statusText}失败`);
|
ElMessage.error(res.message || `${statusText}失败`);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (error !== 'cancel') {
|
if (error !== 'cancel') {
|
||||||
@@ -221,6 +235,33 @@ async function handleUpdateStatus(course: Course, status: number) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 强制发布
|
||||||
|
async function handleForcePublish(course: Course) {
|
||||||
|
try {
|
||||||
|
await ElMessageBox.confirm(
|
||||||
|
`确定要强制发布课程「${course.name}」吗?此操作将跳过敏感词校验。`,
|
||||||
|
'强制发布确认',
|
||||||
|
{
|
||||||
|
confirmButtonText: '确定',
|
||||||
|
cancelButtonText: '取消',
|
||||||
|
type: 'warning'
|
||||||
|
}
|
||||||
|
);
|
||||||
|
const res = await courseApi.forcePublishCourse(course.courseID!);
|
||||||
|
if (res.success) {
|
||||||
|
ElMessage.success('强制发布成功');
|
||||||
|
loadCourses();
|
||||||
|
} else {
|
||||||
|
ElMessage.error(res.message || '强制发布失败');
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
if (error !== 'cancel') {
|
||||||
|
console.error('强制发布失败:', error);
|
||||||
|
ElMessage.error('强制发布失败');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 删除
|
// 删除
|
||||||
async function handleDelete(course: Course) {
|
async function handleDelete(course: Course) {
|
||||||
try {
|
try {
|
||||||
|
|||||||
Reference in New Issue
Block a user