课程强制发布
This commit is contained in:
@@ -114,6 +114,7 @@ INSERT INTO `tb_sys_permission` (id, permission_id, name, code, description, mod
|
||||
|
||||
-- 1100-1199: 文章管理
|
||||
('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', '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', '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', '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)
|
||||
('29', 'superadmin', 'perm_admin_resource_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()),
|
||||
('32', 'superadmin', 'perm_admin_tag_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()),
|
||||
('36', 'superadmin', 'perm_admin_study_records', '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()),
|
||||
('39', 'superadmin', 'perm_admin_ai_config', '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)
|
||||
('122', 'admin', 'perm_admin_resource_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()),
|
||||
('125', 'admin', 'perm_admin_tag_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()),
|
||||
('129', 'admin', 'perm_admin_study_records', '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()),
|
||||
('132', 'admin', 'perm_admin_ai_config', '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
|
||||
*/
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
* 强制发布课程(跳过敏感词校验)
|
||||
*/
|
||||
@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;
|
||||
}
|
||||
|
||||
// 验证课程基本信息
|
||||
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();
|
||||
String courseID = IDUtils.generateID();
|
||||
@@ -763,4 +829,72 @@ public class SCCourseServiceImpl implements SCCourseService {
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -241,8 +241,8 @@
|
||||
<!-- batchUpdateNodeAudited -->
|
||||
<update id="batchUpdateNodeAudited">
|
||||
<foreach collection="courseNodeList" item="item" separator=";">
|
||||
UPDATE tb_course_node
|
||||
SET is_audited = #{item.isAudited}
|
||||
UPDATE tb_course_node
|
||||
SET is_audited = #{item.isAudited}
|
||||
WHERE node_id = #{item.nodeID}
|
||||
</foreach>
|
||||
</update>
|
||||
|
||||
@@ -176,6 +176,16 @@ export const courseApi = {
|
||||
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
|
||||
|
||||
@@ -6,7 +6,7 @@ import App from "./App.vue";
|
||||
import "./registerServiceWorker";
|
||||
import router from "./router";
|
||||
import store from "./store";
|
||||
import { setupRouterGuards, setupTokenRefresh } from "@/utils/permission";
|
||||
import { setupRouterGuards, setupTokenRefresh, setupPermissionUtils } from "@/utils/permission";
|
||||
import { setupPermissionDirectives } from "@/directives/permission";
|
||||
|
||||
// 引入 Quill 富文本编辑器样式(全局)
|
||||
@@ -49,6 +49,9 @@ async function initApp() {
|
||||
// 设置权限指令
|
||||
setupPermissionDirectives(app, store);
|
||||
|
||||
// 设置权限工具
|
||||
setupPermissionUtils(store);
|
||||
|
||||
// 设置路由守卫
|
||||
setupRouterGuards(router, store);
|
||||
|
||||
|
||||
@@ -266,3 +266,64 @@ export class PermissionChecker {
|
||||
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) }}
|
||||
</el-button>
|
||||
<el-button
|
||||
v-if="row.status === ResourceStatus.SENSITIVE_FAILED"
|
||||
v-if="row.status === ResourceStatus.SENSITIVE_FAILED && canForcePublish"
|
||||
size="small"
|
||||
type="warning"
|
||||
@click="forcePublishArticle(row)"
|
||||
@@ -108,7 +108,7 @@ import { AdminLayout } from '@/views/admin';
|
||||
defineOptions({
|
||||
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 { Search } from '@element-plus/icons-vue';
|
||||
import { useRouter } from 'vue-router';
|
||||
@@ -116,8 +116,14 @@ import { resourceApi, resourceTagApi } from '@/apis/resource'
|
||||
import type { PageParam, ResourceSearchParams, Resource, Tag } from '@/types';
|
||||
import { ArticleShowView } from '@/views/public/article';
|
||||
import { ResourceStatus } from '@/types/enums';
|
||||
import { usePermission } from '@/utils/permission';
|
||||
|
||||
const router = useRouter();
|
||||
|
||||
// 权限检查
|
||||
const { hasPermission } = usePermission();
|
||||
const canForcePublish = computed(() => hasPermission('admin:article:force-publish'));
|
||||
|
||||
const searchKeyword = ref('');
|
||||
const pageParam = ref<PageParam>({
|
||||
pageNumber: 1,
|
||||
|
||||
@@ -73,7 +73,7 @@
|
||||
</template>
|
||||
</el-table-column>
|
||||
<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 }">
|
||||
<el-button type="primary" size="small" link @click="handleEdit(row)">
|
||||
编辑
|
||||
@@ -87,6 +87,15 @@
|
||||
>
|
||||
发布
|
||||
</el-button>
|
||||
<el-button
|
||||
v-if="row.status === 4 && canForcePublish"
|
||||
type="warning"
|
||||
size="small"
|
||||
link
|
||||
@click="handleForcePublish(row)"
|
||||
>
|
||||
强制发布
|
||||
</el-button>
|
||||
<el-button
|
||||
v-if="row.status === 1"
|
||||
type="warning"
|
||||
@@ -121,12 +130,13 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, reactive, onMounted } from 'vue';
|
||||
import { ref, reactive, onMounted, computed } from 'vue';
|
||||
import { ElMessage, ElMessageBox } from 'element-plus';
|
||||
import { Search, Plus } from '@element-plus/icons-vue';
|
||||
import { courseApi } from '@/apis/study';
|
||||
import { FILE_DOWNLOAD_URL } from '@/config';
|
||||
import type { Course } from '@/types';
|
||||
import { usePermission } from '@/utils/permission';
|
||||
|
||||
defineOptions({
|
||||
name: 'CourseList'
|
||||
@@ -137,6 +147,10 @@ const emit = defineEmits<{
|
||||
edit: [course: Course];
|
||||
}>();
|
||||
|
||||
// 权限检查
|
||||
const { hasPermission } = usePermission();
|
||||
const canForcePublish = computed(() => hasPermission('admin:course:force-publish'));
|
||||
|
||||
const loading = ref(false);
|
||||
const courseList = ref<Course[]>([]);
|
||||
const total = ref(0);
|
||||
@@ -211,7 +225,7 @@ async function handleUpdateStatus(course: Course, status: number) {
|
||||
ElMessage.success(`${statusText}成功`);
|
||||
loadCourses();
|
||||
} else {
|
||||
ElMessage.error(`${statusText}失败`);
|
||||
ElMessage.error(res.message || `${statusText}失败`);
|
||||
}
|
||||
} catch (error) {
|
||||
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) {
|
||||
try {
|
||||
|
||||
Reference in New Issue
Block a user