fix:修复课程删除报错,修复课程编辑字段保存回显问题

This commit is contained in:
yuk255 2025-09-16 19:42:33 +08:00
parent 568d8fdf47
commit bff716f6b6
4 changed files with 439 additions and 361 deletions

View File

@ -193,9 +193,7 @@ export class TeachCourseApi {
try { try {
console.log('🚀 发送删除课程请求:', { url: '/aiol/aiolCourse/delete', id }) console.log('🚀 发送删除课程请求:', { url: '/aiol/aiolCourse/delete', id })
const response = await ApiRequest.delete<any>('/aiol/aiolCourse/delete', { const response = await ApiRequest.delete<any>(`/aiol/aiolCourse/delete?id=${id}`, )
params: { id }
})
console.log('🗑️ 删除课程响应:', response) console.log('🗑️ 删除课程响应:', response)
return response return response

View File

@ -27,11 +27,7 @@
<!-- 错误状态 --> <!-- 错误状态 -->
<div v-else-if="error" class="error-container"> <div v-else-if="error" class="error-container">
<n-result <n-result status="error" title="加载失败" :description="error">
status="error"
title="加载失败"
:description="error"
>
<template #footer> <template #footer>
<n-space justify="center"> <n-space justify="center">
<n-button type="primary" @click="getCourseList(true)"> <n-button type="primary" @click="getCourseList(true)">
@ -49,10 +45,7 @@
<!-- 空状态 --> <!-- 空状态 -->
<div v-else-if="courseList.length === 0" class="empty-container"> <div v-else-if="courseList.length === 0" class="empty-container">
<n-empty <n-empty description="暂无课程数据" size="large">
description="暂无课程数据"
size="large"
>
<template #extra> <template #extra>
<n-button type="primary" @click="navigateToCreateCourse"> <n-button type="primary" @click="navigateToCreateCourse">
创建课程 创建课程
@ -67,13 +60,8 @@
<div class="course-image-container"> <div class="course-image-container">
<div class="section-title" :class="{ 'offline': course.status === 0 }">{{ course.statusText }} <div class="section-title" :class="{ 'offline': course.status === 0 }">{{ course.statusText }}
</div> </div>
<n-popselect <n-popselect :options="getOptionsForCourse(course)" trigger="hover" placement="bottom-end"
:options="getOptionsForCourse(course)" :render-label="renderOptionLabel" @update:value="(value: string) => handleOptionSelect(value, course)">
trigger="hover"
placement="bottom-end"
:render-label="renderOptionLabel"
@update:value="(value: string) => handleOptionSelect(value, course)"
>
<div class="more-options"> <div class="more-options">
<span class="more-icon"> <span class="more-icon">
<n-icon size="20"> <n-icon size="20">
@ -117,8 +105,17 @@
<script setup lang="ts"> <script setup lang="ts">
import { ref, h, onMounted, watch } from 'vue'; import { ref, h, onMounted, watch } from 'vue';
import { useRouter } from 'vue-router'; import { useRouter } from 'vue-router';
import { EllipsisVerticalSharp, Refresh } from '@vicons/ionicons5'; import {
import { useMessage, useDialog } from 'naive-ui'; EllipsisVerticalSharp,
Refresh,
CloudOfflineOutline, //
CreateOutline, //
CopyOutline, //
TrashOutline, //
CloudUploadOutline, //
AddOutline // /
} from '@vicons/ionicons5';
import { useMessage, useDialog, NIcon } from 'naive-ui';
import TeachCourseApi, { type TeachCourse } from '@/api/modules/teachCourse'; import TeachCourseApi, { type TeachCourse } from '@/api/modules/teachCourse';
import { useCourseStore } from '@/stores/course'; import { useCourseStore } from '@/stores/course';
@ -265,21 +262,24 @@ const handleSearch = async () => {
const getOptionsForCourse = (course: CourseDisplayItem) => { const getOptionsForCourse = (course: CourseDisplayItem) => {
if (course.status === 1) { // if (course.status === 1) { //
return [ return [
{ label: '下架', value: 'offline', icon: '/images/teacher/下架.png' }, { label: '下架', value: 'offline', icon: CloudOfflineOutline },
{ label: '编辑', value: 'edit', icon: '/images/teacher/小编辑.png' }, { label: '编辑', value: 'edit', icon: CreateOutline },
{ label: '删除', value: 'delete', icon: '/images/teacher/删除.png' } { label: '复制', value: 'copy', icon: CopyOutline },
{ label: '删除', value: 'delete', icon: TrashOutline }
]; ];
} else if (course.status === 0) { // /稿 } else if (course.status === 0) { // /稿
return [ return [
{ label: '发布', value: 'publish', icon: '/images/teacher/加号.png' }, { label: '发布', value: 'publish', icon: CloudUploadOutline },
{ label: '编辑', value: 'edit', icon: '/images/teacher/小编辑.png' }, { label: '编辑', value: 'edit', icon: CreateOutline },
{ label: '删除', value: 'delete', icon: '/images/teacher/删除.png' } { label: '复制', value: 'copy', icon: CopyOutline },
{ label: '删除', value: 'delete', icon: TrashOutline }
]; ];
} else if (course.status === 2) { // } else if (course.status === 2) { //
return [ return [
{ label: '发布', value: 'publish', icon: '/images/teacher/加号.png' }, { label: '发布', value: 'publish', icon: CloudUploadOutline },
{ label: '编辑', value: 'edit', icon: '/images/teacher/小编辑.png' }, { label: '编辑', value: 'edit', icon: CreateOutline },
{ label: '删除', value: 'delete', icon: '/images/teacher/删除.png' } { label: '复制', value: 'copy', icon: CopyOutline },
{ label: '删除', value: 'delete', icon: TrashOutline }
]; ];
} }
return []; return [];
@ -294,13 +294,9 @@ const renderOptionLabel = (option: any) => {
gap: '6px' gap: '6px'
} }
}, [ }, [
h('img', { h(NIcon, {
src: option.icon, size: 16,
alt: '', component: option.icon
style: {
width: '13px',
height: '13px'
}
}), }),
option.label option.label
]); ]);
@ -312,12 +308,8 @@ const handleOptionSelect = (value: string, course: any) => {
// value // value
switch (value) { switch (value) {
case 'edit': case 'edit':
// - store
console.log('✏️ 编辑课程,准备数据:', course);
// store // store
courseStore.setCourseEditData(course); courseStore.setCourseEditData(course);
// ID // ID
router.push(`/teacher/course-create/${course.id}`); router.push(`/teacher/course-create/${course.id}`);
break; break;
@ -333,6 +325,12 @@ const handleOptionSelect = (value: string, course: any) => {
// //
handlePublishCourse(course); handlePublishCourse(course);
break; break;
case 'copy':
//
courseStore.setCourseEditData(course);
// ID
router.push(`/teacher/course-create/${course.id}?type=copy`);
break;
default: default:
break; break;
} }

View File

@ -58,7 +58,7 @@
<div class="form-item" v-show="formData.studentType === 'partial'"> <div class="form-item" v-show="formData.studentType === 'partial'">
<label class="form-label required">选择班级:</label> <label class="form-label required">选择班级:</label>
<n-select v-model:value="formData.selectedClasses" multiple :options="classOptions" <n-select v-model:value="formData.selectedClasses" multiple :options="classOptions"
placeholder="选择班级(可多选)" class="form-input" /> placeholder="选择班级(可多选)" class="form-input" :disabled="isEditMode && !isCopyMode" />
</div> </div>
</div> </div>
@ -200,9 +200,19 @@ const courseStore = useCourseStore()
// //
const isEditMode = computed(() => !!route.params.id) const isEditMode = computed(() => !!route.params.id)
const courseId = computed(() => route.params.id as string) const courseId = computed(() => route.params.id as string)
//
const isCopyMode = computed(() => route.query.type === 'copy')
// //
const pageTitle = computed(() => isEditMode.value ? '编辑课程' : '创建课程') const pageTitle = computed(() => {
if (isCopyMode.value) {
return '复制课程'
} else if (isEditMode.value && !isCopyMode.value) {
return '编辑课程'
} else {
return '新建课程'
}
})
// shallowRef // shallowRef
const editorRef = shallowRef() const editorRef = shallowRef()
@ -214,6 +224,9 @@ const previewUrl = ref('')
// //
const pendingCourseDescription = ref('') const pendingCourseDescription = ref('')
//
const pendingSelectedClasses = ref<string[]>([])
const toolbarConfig = {} const toolbarConfig = {}
const editorConfig = { placeholder: '请输入内容...' } const editorConfig = { placeholder: '请输入内容...' }
const mode = 'default' const mode = 'default'
@ -326,7 +339,19 @@ const loadCourseData = async () => {
formData.startTime = storeCourseData.start_time || storeCourseData.startTime || null; formData.startTime = storeCourseData.start_time || storeCourseData.startTime || null;
formData.endTime = storeCourseData.end_time || storeCourseData.endTime || null; formData.endTime = storeCourseData.end_time || storeCourseData.endTime || null;
formData.studentType = (storeCourseData.type === 1 || storeCourseData.studentType === 'partial') ? 'partial' : 'all'; formData.studentType = (storeCourseData.type === 1 || storeCourseData.studentType === 'partial') ? 'partial' : 'all';
formData.selectedClasses = storeCourseData.target ? storeCourseData.target.split(',') : (storeCourseData.selectedClasses || []); console.log('huixian:',storeCourseData);
//
const classData = storeCourseData.classId ? storeCourseData.classId.split(',') : (storeCourseData.selectedClasses || []);
if (classOptions.value.length > 0) {
//
formData.selectedClasses = classData;
console.log('直接设置班级数据:', classData);
} else {
//
pendingSelectedClasses.value = classData;
console.log('保存班级数据到待回显变量:', classData);
}
const tempCourseDescription = storeCourseData.description || storeCourseData.courseDescription || ''; const tempCourseDescription = storeCourseData.description || storeCourseData.courseDescription || '';
if (tempCourseDescription) { if (tempCourseDescription) {
@ -389,7 +414,18 @@ const loadCourseData = async () => {
formData.startTime = courseData.start_time || courseData.startTime || null; formData.startTime = courseData.start_time || courseData.startTime || null;
formData.endTime = courseData.end_time || courseData.endTime || null; formData.endTime = courseData.end_time || courseData.endTime || null;
formData.studentType = (courseData.type === 1 || courseData.studentType === 'partial') ? 'partial' : 'all'; formData.studentType = (courseData.type === 1 || courseData.studentType === 'partial') ? 'partial' : 'all';
formData.selectedClasses = courseData.target ? courseData.target.split(',') : (courseData.selectedClasses || []);
//
const classData = courseData.target ? courseData.target.split(',') : (courseData.selectedClasses || []);
if (classOptions.value.length > 0) {
//
formData.selectedClasses = classData;
console.log('直接设置班级数据:', classData);
} else {
//
pendingSelectedClasses.value = classData;
console.log('保存班级数据到待回显变量:', classData);
}
// //
const tempCourseDescription = courseData.description || courseData.courseDescription || ''; const tempCourseDescription = courseData.description || courseData.courseDescription || '';
@ -676,7 +712,7 @@ const handleSubmit = async () => {
question: null question: null
} }
if (isEditMode.value) { if (isEditMode.value && !isCopyMode.value) {
// //
const editData = { const editData = {
id: courseId.value, id: courseId.value,
@ -702,7 +738,7 @@ const handleSubmit = async () => {
if (response.data.code === 200) { if (response.data.code === 200) {
// ID // ID
const newCourseId = response.data.data?.id || response.data.id; const newCourseId = response.data.result;
if (newCourseId && formData.instructors.length > 0) { if (newCourseId && formData.instructors.length > 0) {
await handleTeacherChanges(newCourseId.toString(), true); await handleTeacherChanges(newCourseId.toString(), true);
} }
@ -730,8 +766,9 @@ const goBack = () => {
} }
// //
const getCourseList = () => { const getCourseList = async () => {
CourseApi.getCategories().then(response => { try {
const response = await CourseApi.getCategories()
categoryOptions.value = response.data.map((category: any) => ({ categoryOptions.value = response.data.map((category: any) => ({
label: category.name, label: category.name,
value: category.id value: category.id
@ -740,50 +777,71 @@ const getCourseList = () => {
if (formData.courseCategory.length === 0 && categoryOptions.value.length > 0) { if (formData.courseCategory.length === 0 && categoryOptions.value.length > 0) {
formData.courseCategory = []; formData.courseCategory = [];
} }
}).catch(error => { console.log('课程分类选项加载完成:', categoryOptions.value);
} catch (error) {
console.error('获取课程列表失败:', error) console.error('获取课程列表失败:', error)
}) }
} }
// //
const getTeacherList = () => { const getTeacherList = async () => {
TeachCourseApi.getTeacherList().then(response => { try {
const response = await TeachCourseApi.getTeacherList()
instructorOptions.value = response.data.result.map((teacher: any) => ({ instructorOptions.value = response.data.result.map((teacher: any) => ({
label: teacher.realname, label: teacher.realname,
value: teacher.id value: teacher.id
})) }))
}).catch(error => { console.log('老师选项加载完成:', instructorOptions.value);
} catch (error) {
console.error('获取老师列表失败:', error) console.error('获取老师列表失败:', error)
}) }
} }
// //
const getClassList = () => { const getClassList = async () => {
ClassApi.queryClassList({course_id:null}).then(response => { try {
const response = await ClassApi.queryClassList({ course_id: null })
console.log('班级列表:', response.data.result); console.log('班级列表:', response.data.result);
//
classOptions.value = []
response.data.result.forEach((cls: any) => { response.data.result.forEach((cls: any) => {
classOptions.value.push({ classOptions.value.push({
label: cls.name, label: cls.name,
value: cls.id value: cls.id
}) })
}) })
}).catch(error => {
console.log('班级选项加载完成:', classOptions.value);
//
if (pendingSelectedClasses.value.length > 0) {
console.log('开始回显班级数据:', pendingSelectedClasses.value);
formData.selectedClasses = [...pendingSelectedClasses.value];
pendingSelectedClasses.value = []; //
console.log('班级回显完成:', formData.selectedClasses);
}
} catch (error) {
console.error('获取班级列表失败:', error) console.error('获取班级列表失败:', error)
}) }
} }
onMounted(() => { onMounted(async () => {
// courseIdstore //
await Promise.all([
getCourseList(),
getTeacherList(),
getClassList() //
]);
//
if (isEditMode.value && courseId.value) { if (isEditMode.value && courseId.value) {
loadCourseData() await loadCourseData()
} else if (route.query.courseData || courseStore.courseEditData) { } else if (route.query.courseData || courseStore.courseEditData) {
// 使IDstore // 使IDstore
loadCourseData() await loadCourseData()
} }
getCourseList()
getTeacherList()
getClassList()
}) })
</script> </script>

View File

@ -151,6 +151,14 @@ const activeSubNavItem = ref(''); // 子菜单激活状态
const examMenuExpanded = ref(false); // const examMenuExpanded = ref(false); //
const studentMenuExpanded = ref(false); // const studentMenuExpanded = ref(false); //
const showTopImage = ref(true); // / const showTopImage = ref(true); // /
//
const hideTopImageRoutes = [
'teacher/chapter-editor-teacher',
'teacher/course-editor'
//
];
const route = useRoute(); const route = useRoute();
const router = useRouter(); const router = useRouter();
const isAiHovered = ref(false); const isAiHovered = ref(false);
@ -654,13 +662,29 @@ onMounted(() => {
} else { } else {
document.documentElement.style.setProperty('--top-height', '0px'); document.documentElement.style.setProperty('--top-height', '0px');
} }
updateTopImageVisibility();
}); });
// 使watch // 使watch
watch(route, () => { watch(route, () => {
updateActiveNavItem(); updateActiveNavItem();
updateTopImageVisibility();
}); });
//
const updateTopImageVisibility = () => {
const currentPath = route.path;
console.log('检查路径是否需要隐藏顶部图片:', currentPath);
//
const shouldHideTopImage = hideTopImageRoutes.some(routePath =>
currentPath.includes(routePath)
);
showTopImage.value = !shouldHideTopImage;
console.log('顶部图片显示状态:', showTopImage.value);
};
// //
const updateActiveNavItem = () => { const updateActiveNavItem = () => {
const path = route.path; const path = route.path;
@ -704,7 +728,7 @@ const updateActiveNavItem = () => {
<style scoped> <style scoped>
.admin-dashboard { .admin-dashboard {
min-height: 100vh; /* min-height: 100vh; */
} }
.top-image-container { .top-image-container {