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

@ -2,7 +2,7 @@
<div class="course-category"> <div class="course-category">
<!-- 顶部 --> <!-- 顶部 -->
<div class="top"> <div class="top">
<n-tabs v-model:value="activeTab" size="large"> <n-tabs v-model:value="activeTab" size="large">
<n-tab-pane name="ongoing" tab="进行中" /> <n-tab-pane name="ongoing" tab="进行中" />
<n-tab-pane name="draft" tab="草稿箱" /> <n-tab-pane name="draft" tab="草稿箱" />
<n-tab-pane name="finished" tab="已结束" /> <n-tab-pane name="finished" tab="已结束" />
@ -24,14 +24,10 @@
<n-spin size="large" :show="loading"> <n-spin size="large" :show="loading">
</n-spin> </n-spin>
</div> </div>
<!-- 错误状态 --> <!-- 错误状态 -->
<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)">
@ -46,13 +42,10 @@
</template> </template>
</n-result> </n-result>
</div> </div>
<!-- 空状态 --> <!-- 空状态 -->
<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">
创建课程 创建课程
@ -60,20 +53,15 @@
</template> </template>
</n-empty> </n-empty>
</div> </div>
<!-- 课程列表 --> <!-- 课程列表 -->
<div v-if="courseList.length > 0" class="course-grid" key="course-list"> <div v-if="courseList.length > 0" class="course-grid" key="course-list">
<div class="course-card" v-for="course in courseList" :key="course.id"> <div class="course-card" v-for="course in courseList" :key="course.id">
<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';
@ -148,20 +145,20 @@ const getCourseList = async (forceRefresh: boolean = false) => {
loading.value = true; loading.value = true;
error.value = ''; // error.value = ''; //
courseList.value = []; courseList.value = [];
const params = { const params = {
keyword: searchValue.value, keyword: searchValue.value,
status: getStatusByTab(activeTab.value) status: getStatusByTab(activeTab.value)
}; };
console.log('🔄 获取课程列表 - Tab:', activeTab.value, 'Status:', params.status, 'Keyword:', params.keyword); console.log('🔄 获取课程列表 - Tab:', activeTab.value, 'Status:', params.status, 'Keyword:', params.keyword);
const response = await TeachCourseApi.getTeacherCourseList(params); const response = await TeachCourseApi.getTeacherCourseList(params);
loading.value = false; loading.value = false;
if (response && response.data) { if (response && response.data) {
const courseData = response.data.result.map((course: TeachCourse): CourseDisplayItem => ({ const courseData = response.data.result.map((course: TeachCourse): CourseDisplayItem => ({
...course, ...course,
// //
statusText: getStatusText(course.status), statusText: getStatusText(course.status),
image: course.cover || '/images/teacher/fj.png' image: course.cover || '/images/teacher/fj.png'
})); }));
@ -179,18 +176,18 @@ const getCourseList = async (forceRefresh: boolean = false) => {
} catch (err: any) { } catch (err: any) {
console.error('获取课程列表失败:', err); console.error('获取课程列表失败:', err);
loading.value = false; loading.value = false;
// //
error.value = err.message || '' error.value = err.message || ''
// //
originalCourseList.value = []; originalCourseList.value = [];
courseList.value = []; courseList.value = [];
// //
message.error(error.value); message.error(error.value);
} finally { } finally {
loading.value = false; loading.value = false;
} }
}; };
@ -217,16 +214,16 @@ const getStatusByTab = (tab: string): string => {
// //
const filterCourseList = () => { const filterCourseList = () => {
let filtered = [...originalCourseList.value]; let filtered = [...originalCourseList.value];
// API // API
if (searchValue.value?.trim()) { if (searchValue.value?.trim()) {
const keyword = searchValue.value.trim().toLowerCase(); const keyword = searchValue.value.trim().toLowerCase();
filtered = filtered.filter(course => filtered = filtered.filter(course =>
course.name?.toLowerCase().includes(keyword) || course.name?.toLowerCase().includes(keyword) ||
course.description?.toLowerCase().includes(keyword) course.description?.toLowerCase().includes(keyword)
); );
} }
courseList.value = filtered; courseList.value = filtered;
console.log('📊 过滤后的课程数量:', filtered.length); console.log('📊 过滤后的课程数量:', filtered.length);
}; };
@ -236,10 +233,10 @@ const activeTab = ref<string>('ongoing')
// //
watch(activeTab, async () => { watch(activeTab, async () => {
courseList.value = []; courseList.value = [];
originalCourseList.value = []; originalCourseList.value = [];
await getCourseList(true); await getCourseList(true);
}); });
@ -251,7 +248,7 @@ watch(searchValue, () => {
// //
const navigateToCreateCourse = () => { const navigateToCreateCourse = () => {
router.push('/teacher/course-create'); router.push('/teacher/course-create');
}; };
// //
@ -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;
} }
@ -365,7 +363,7 @@ const handleDeleteCourse = (course: any) => {
try { try {
// //
await TeachCourseApi.deleteCourse(course.id); await TeachCourseApi.deleteCourse(course.id);
// //
const index = courseList.value.findIndex(c => c.id === course.id); const index = courseList.value.findIndex(c => c.id === course.id);
if (index > -1) { if (index > -1) {
@ -376,7 +374,7 @@ const handleDeleteCourse = (course: any) => {
originalCourseList.value.splice(originalIndex, 1); originalCourseList.value.splice(originalIndex, 1);
} }
} }
message.success(`课程"${course.name}"已删除`); message.success(`课程"${course.name}"已删除`);
} catch (error) { } catch (error) {
console.error('删除课程失败:', error); console.error('删除课程失败:', error);
@ -403,13 +401,13 @@ const handleOfflineCourse = (course: CourseDisplayItem) => {
// API - // API -
const updatedData = { const updatedData = {
id: course.id!, id: course.id!,
status: '0', status: '0',
}; };
await TeachCourseApi.editCourse(updatedData); await TeachCourseApi.editCourse(updatedData);
await getCourseList(); await getCourseList();
message.success(`课程"${course.name}"已下架,现在可以删除了`); message.success(`课程"${course.name}"已下架,现在可以删除了`);
} catch (error) { } catch (error) {
console.error('下架课程失败:', error); console.error('下架课程失败:', error);
@ -436,13 +434,13 @@ const handlePublishCourse = (course: CourseDisplayItem) => {
// API - // API -
const updatedData = { const updatedData = {
id: course.id!, id: course.id!,
status: '1', status: '1',
}; };
await TeachCourseApi.editCourse(updatedData); await TeachCourseApi.editCourse(updatedData);
await getCourseList(); await getCourseList();
message.success(`课程"${course.name}"已发布`); message.success(`课程"${course.name}"已发布`);
} catch (error) { } catch (error) {
console.error('发布课程失败:', error); console.error('发布课程失败:', error);
@ -475,206 +473,206 @@ onMounted(async () => {
overflow-x: hidden; overflow-x: hidden;
/* 防止水平滚动 */ /* 防止水平滚动 */
display: flex; display: flex;
flex-direction: column; flex-direction: column;
} }
.top {
padding: 10px 20px;
white-space: nowrap;
/* 进一步减少左右padding */
display: flex;
align-items: center;
justify-content: space-between;
border-bottom: 1px solid #f0f0f0;
width: 100%;
/* 确保宽度充满父容器 */
box-sizing: border-box;
}
.nav-links {
flex: 1;
}
.actions {
display: flex;
align-items: center;
flex-shrink: 0;
gap: 12px;
}
.top {
.search-container { padding: 10px 20px;
display: flex; white-space: nowrap;
align-items: center; /* 进一步减少左右padding */
} display: flex;
align-items: center;
.course-container { justify-content: space-between;
width: 100%; border-bottom: 1px solid #f0f0f0;
padding: 20px; width: 100%;
box-sizing: border-box; /* 确保宽度充满父容器 */
flex: 1; box-sizing: border-box;
display: flex; }
flex-direction: column;
} .nav-links {
flex: 1;
.loading-container { }
display: contents;
} .actions {
display: flex;
.error-container { align-items: center;
display: flex; flex-shrink: 0;
justify-content: center; gap: 12px;
align-items: center; }
min-height: 400px;
width: 100%;
padding: 40px 20px;
} .search-container {
display: flex;
.empty-container { align-items: center;
display: flex; }
justify-content: center;
align-items: center; .course-container {
min-height: 400px; width: 100%;
width: 100%; padding: 20px;
padding: 40px 20px; box-sizing: border-box;
} flex: 1;
display: flex;
.course-grid { flex-direction: column;
display: grid; }
grid-template-columns: repeat(4, 1fr);
/* 默认4列确保100%缩放下不被截断 */ .loading-container {
gap: 12px; display: contents;
/* 进一步减少间距 */ }
margin-bottom: 20px;
width: 100%; .error-container {
max-width: 100%; display: flex;
box-sizing: border-box; justify-content: center;
padding: 0 3px; align-items: center;
/* 减少内边距 */ min-height: 400px;
} width: 100%;
padding: 40px 20px;
.course-card { }
width: 100%;
height: auto; .empty-container {
/* aspect-ratio: 190 / 201; */ display: flex;
background: #FFFFFF; justify-content: center;
border: 1px solid #D8D8D8; align-items: center;
box-sizing: border-box; min-height: 400px;
transition: all 0.3s ease; width: 100%;
display: flex; padding: 40px 20px;
flex-direction: column; }
}
.course-grid {
.course-card:hover { display: grid;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1); grid-template-columns: repeat(4, 1fr);
/* transform: translateY(-5px); */ /* 默认4列确保100%缩放下不被截断 */
} gap: 12px;
/* 进一步减少间距 */
.course-image-container { margin-bottom: 20px;
position: relative; width: 100%;
padding: 0; max-width: 100%;
width: 100%; box-sizing: border-box;
height: 30px; padding: 0 3px;
box-sizing: border-box; /* 减少内边距 */
display: flex; }
justify-content: space-between;
align-items: flex-start; .course-card {
} width: 100%;
height: auto;
.section-title { /* aspect-ratio: 190 / 201; */
width: 50px; background: #FFFFFF;
height: 20px; border: 1px solid #D8D8D8;
background: #0288D1; box-sizing: border-box;
color: white; transition: all 0.3s ease;
font-size: 12px; display: flex;
display: flex; flex-direction: column;
align-items: center; }
justify-content: center;
margin: 0; .course-card:hover {
padding: 0; box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
} /* transform: translateY(-5px); */
}
.section-title.offline {
background: #FF6C17; .course-image-container {
} position: relative;
padding: 0;
.more-options { width: 100%;
cursor: pointer; height: 30px;
margin: 0; box-sizing: border-box;
padding: 0; display: flex;
} justify-content: space-between;
align-items: flex-start;
.more-icon { }
font-size: 18px;
font-weight: bold; .section-title {
color: #333; width: 50px;
display: flex; height: 20px;
line-height: 1; background: #0288D1;
width: 32px; color: white;
height: 32px; font-size: 12px;
justify-content: center; display: flex;
align-items: center; align-items: center;
} justify-content: center;
margin: 0;
.course-image-container img { padding: 0;
width: 13px; }
height: 13px;
} .section-title.offline {
background: #FF6C17;
.course-info { }
display: flex;
flex-direction: column; .more-options {
align-items: center; cursor: pointer;
justify-content: flex-start; margin: 0;
flex: 1; padding: 0;
box-sizing: border-box; }
padding: 0;
} .more-icon {
font-size: 18px;
.course-info img { font-weight: bold;
width: 80%; color: #333;
height: auto; display: flex;
aspect-ratio: 150 / 105; line-height: 1;
/* 居中 */ width: 32px;
display: block; height: 32px;
margin: 8px auto 10px; justify-content: center;
position: relative; align-items: center;
top: -5px; }
object-fit: cover;
object-position: center; .course-image-container img {
} width: 13px;
height: 13px;
.course-name { }
width: 80%;
font-family: AppleSystemUIFont; .course-info {
font-size: 14px; display: flex;
color: #333333; flex-direction: column;
line-height: 1.5; align-items: center;
text-align: center; justify-content: flex-start;
font-style: normal; flex: 1;
text-transform: none; box-sizing: border-box;
white-space: nowrap; padding: 0;
/* 强制文本在一行显示 */ }
overflow: hidden;
/* 隐藏超出容器的文本 */ .course-info img {
text-overflow: ellipsis; width: 80%;
/* 溢出部分用省略号表示 */ height: auto;
margin-top: 0px; aspect-ratio: 150 / 105;
/* 文字往上移动:添加负上边距 */ /* 居中 */
margin-bottom: 10px; display: block;
/* 减小底部边距从15px到10px */ margin: 8px auto 10px;
} position: relative;
top: -5px;
/* 分页样式 */ object-fit: cover;
.pagination { object-position: center;
display: flex; }
justify-content: center;
position: relative; .course-name {
z-index: 100; width: 80%;
margin: 0; font-family: AppleSystemUIFont;
margin-top: auto; font-size: 14px;
flex-shrink: 0; color: #333333;
line-height: 1.5;
text-align: center;
font-style: normal;
text-transform: none;
white-space: nowrap;
/* 强制文本在一行显示 */
overflow: hidden;
/* 隐藏超出容器的文本 */
text-overflow: ellipsis;
/* 溢出部分用省略号表示 */
margin-top: 0px;
/* 文字往上移动:添加负上边距 */
margin-bottom: 10px;
/* 减小底部边距从15px到10px */
}
/* 分页样式 */
.pagination {
display: flex;
justify-content: center;
position: relative;
z-index: 100;
margin: 0;
margin-top: auto;
flex-shrink: 0;
} }
.pagination-content { .pagination-content {
@ -749,7 +747,7 @@ onMounted(async () => {
border-color: #1890ff; border-color: #1890ff;
} }
:deep(.n-tabs.n-tabs--top .n-tab-pane){ :deep(.n-tabs.n-tabs--top .n-tab-pane) {
padding: 0; padding: 0;
} }

View File

@ -3,16 +3,16 @@
<div class="form-container"> <div class="form-container">
<!-- 表单内容 --> <!-- 表单内容 -->
<div class="form-content"> <div class="form-content">
<div class="header-left"> <div class="header-left">
<n-button quaternary circle size="large" @click="goBack"> <n-button quaternary circle size="large" @click="goBack">
<template #icon> <template #icon>
<n-icon> <n-icon>
<ArrowBackOutline /> <ArrowBackOutline />
</n-icon> </n-icon>
</template> </template>
</n-button> </n-button>
<h2 class="page-title">{{ pageTitle }}</h2> <h2 class="page-title">{{ pageTitle }}</h2>
</div> </div>
<!-- 上半部分两列布局 --> <!-- 上半部分两列布局 -->
<div class="form-row"> <div class="form-row">
@ -24,7 +24,7 @@
<n-input v-model:value="formData.courseName" placeholder="请输入课程名称" class="form-input" /> <n-input v-model:value="formData.courseName" placeholder="请输入课程名称" class="form-input" />
</div> </div>
<!-- 课程开始时间 --> <!-- 课程开始时间 -->
<div class="form-item"> <div class="form-item">
<label class="form-label required">课程开始时间:</label> <label class="form-label required">课程开始时间:</label>
<n-date-picker v-model:value="formData.startTime" type="datetime" placeholder="选择时间" class="form-input" /> <n-date-picker v-model:value="formData.startTime" type="datetime" placeholder="选择时间" class="form-input" />
@ -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'
@ -227,7 +240,7 @@ onBeforeUnmount(() => {
const handleCreated = (editor: any) => { const handleCreated = (editor: any) => {
editorRef.value = editor // editor editorRef.value = editor // editor
// //
if (pendingCourseDescription.value) { if (pendingCourseDescription.value) {
editor.setHtml(pendingCourseDescription.value); editor.setHtml(pendingCourseDescription.value);
@ -265,21 +278,21 @@ const originalInstructors = ref<string[]>([])
const loadCourseTeachers = async (courseId: string) => { const loadCourseTeachers = async (courseId: string) => {
try { try {
console.log('🔄 开始获取课程老师列表课程ID:', courseId) console.log('🔄 开始获取课程老师列表课程ID:', courseId)
const response = await TeachCourseApi.getTeacherListInCourse(courseId) const response = await TeachCourseApi.getTeacherListInCourse(courseId)
console.log('🔍 课程老师API响应:', response) console.log('🔍 课程老师API响应:', response)
if (response.data && response.data.result) { if (response.data && response.data.result) {
const teachers = response.data.result const teachers = response.data.result
console.log('✅ 获取到课程老师列表:', teachers) console.log('✅ 获取到课程老师列表:', teachers)
// ID // ID
const teacherIds = teachers.map((teacher: any) => teacher.id || teacher.userId) const teacherIds = teachers.map((teacher: any) => teacher.id || teacher.userId)
formData.instructors = teacherIds formData.instructors = teacherIds
originalInstructors.value = [...teacherIds] originalInstructors.value = [...teacherIds]
console.log('📝 已设置课程老师ID列表:', teacherIds) console.log('📝 已设置课程老师ID列表:', teacherIds)
} else { } else {
console.log('⚠️ 课程暂无绑定老师') console.log('⚠️ 课程暂无绑定老师')
@ -301,7 +314,7 @@ const loadCourseData = async () => {
try { try {
// store // store
const storeCourseData = courseStore.courseEditData; const storeCourseData = courseStore.courseEditData;
if (storeCourseData) { if (storeCourseData) {
// //
formData.courseName = storeCourseData.name || storeCourseData.courseName || ''; formData.courseName = storeCourseData.name || storeCourseData.courseName || '';
@ -314,7 +327,7 @@ const loadCourseData = async () => {
} else { } else {
formData.courseCategory = []; formData.courseCategory = [];
} }
// IDAPI // IDAPI
if (isEditMode.value && courseId.value) { if (isEditMode.value && courseId.value) {
await loadCourseTeachers(courseId.value); await loadCourseTeachers(courseId.value);
@ -326,8 +339,20 @@ 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) {
if (editorRef.value) { if (editorRef.value) {
@ -337,35 +362,35 @@ const loadCourseData = async () => {
pendingCourseDescription.value = tempCourseDescription; pendingCourseDescription.value = tempCourseDescription;
} }
} }
// 0/1 // 0/1
formData.stopOnLeave = storeCourseData.pauseExit !== undefined ? formData.stopOnLeave = storeCourseData.pauseExit !== undefined ?
Boolean(Number(storeCourseData.pauseExit)) : true; Boolean(Number(storeCourseData.pauseExit)) : true;
formData.videoSpeedControl = storeCourseData.allowSpeed !== undefined ? formData.videoSpeedControl = storeCourseData.allowSpeed !== undefined ?
Boolean(Number(storeCourseData.allowSpeed)) : false; Boolean(Number(storeCourseData.allowSpeed)) : false;
formData.showVideoText = storeCourseData.showSubtitle !== undefined ? formData.showVideoText = storeCourseData.showSubtitle !== undefined ?
Boolean(Number(storeCourseData.showSubtitle)) : true; Boolean(Number(storeCourseData.showSubtitle)) : true;
// //
formData.pointsEnabled = storeCourseData.pointsEnabled !== undefined ? storeCourseData.pointsEnabled : true; formData.pointsEnabled = storeCourseData.pointsEnabled !== undefined ? storeCourseData.pointsEnabled : true;
formData.earnPoints = storeCourseData.earnPoints || 60; formData.earnPoints = storeCourseData.earnPoints || 60;
formData.requiredPoints = storeCourseData.requiredPoints || 60; formData.requiredPoints = storeCourseData.requiredPoints || 60;
if (storeCourseData.cover || storeCourseData.courseCover) { if (storeCourseData.cover || storeCourseData.courseCover) {
previewUrl.value = storeCourseData.cover || storeCourseData.courseCover; previewUrl.value = storeCourseData.cover || storeCourseData.courseCover;
formData.courseCover = null; formData.courseCover = null;
} }
message.success('课程数据加载成功'); message.success('课程数据加载成功');
return; // storeAPI return; // storeAPI
} }
// store // store
const routeCourseData = route.query.courseData; const routeCourseData = route.query.courseData;
if (routeCourseData) { if (routeCourseData) {
try { try {
const courseData = JSON.parse(routeCourseData as string); const courseData = JSON.parse(routeCourseData as string);
// //
formData.courseName = courseData.name || courseData.courseName || ''; formData.courseName = courseData.name || courseData.courseName || '';
// 使 // 使
@ -377,7 +402,7 @@ const loadCourseData = async () => {
} else { } else {
formData.courseCategory = []; formData.courseCategory = [];
} }
// IDAPI // IDAPI
if (isEditMode.value && courseId.value) { if (isEditMode.value && courseId.value) {
await loadCourseTeachers(courseId.value); await loadCourseTeachers(courseId.value);
@ -389,8 +414,19 @@ 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 || '';
if (tempCourseDescription) { if (tempCourseDescription) {
@ -403,33 +439,33 @@ const loadCourseData = async () => {
pendingCourseDescription.value = tempCourseDescription; pendingCourseDescription.value = tempCourseDescription;
} }
} }
// 0/1 // 0/1
formData.stopOnLeave = courseData.pauseExit !== undefined ? formData.stopOnLeave = courseData.pauseExit !== undefined ?
Boolean(Number(courseData.pauseExit)) : true; Boolean(Number(courseData.pauseExit)) : true;
formData.videoSpeedControl = courseData.allowSpeed !== undefined ? formData.videoSpeedControl = courseData.allowSpeed !== undefined ?
Boolean(Number(courseData.allowSpeed)) : false; Boolean(Number(courseData.allowSpeed)) : false;
formData.showVideoText = courseData.showSubtitle !== undefined ? formData.showVideoText = courseData.showSubtitle !== undefined ?
Boolean(Number(courseData.showSubtitle)) : true; Boolean(Number(courseData.showSubtitle)) : true;
// 使 // 使
formData.pointsEnabled = courseData.pointsEnabled !== undefined ? courseData.pointsEnabled : true; formData.pointsEnabled = courseData.pointsEnabled !== undefined ? courseData.pointsEnabled : true;
formData.earnPoints = courseData.earnPoints || 60; formData.earnPoints = courseData.earnPoints || 60;
formData.requiredPoints = courseData.requiredPoints || 60; formData.requiredPoints = courseData.requiredPoints || 60;
// URL // URL
if (courseData.cover || courseData.courseCover) { if (courseData.cover || courseData.courseCover) {
previewUrl.value = courseData.cover || courseData.courseCover; previewUrl.value = courseData.cover || courseData.courseCover;
formData.courseCover = null; // URL formData.courseCover = null; // URL
} }
message.success('课程数据加载成功'); message.success('课程数据加载成功');
return; // API return; // API
} catch (parseError) { } catch (parseError) {
// 使 // 使
} }
} }
// store // store
} catch (error) { } catch (error) {
message.error('加载课程数据失败') message.error('加载课程数据失败')
@ -500,14 +536,14 @@ const handleCancel = () => {
// YYYY-MM-DD HH:mm:ss // YYYY-MM-DD HH:mm:ss
const formatDateTime = (timestamp: number): string => { const formatDateTime = (timestamp: number): string => {
const date = new Date(timestamp) const date = new Date(timestamp)
const year = date.getFullYear() const year = date.getFullYear()
const month = (date.getMonth() + 1).toString().padStart(2, '0') const month = (date.getMonth() + 1).toString().padStart(2, '0')
const day = date.getDate().toString().padStart(2, '0') const day = date.getDate().toString().padStart(2, '0')
const hours = date.getHours().toString().padStart(2, '0') const hours = date.getHours().toString().padStart(2, '0')
const minutes = date.getMinutes().toString().padStart(2, '0') const minutes = date.getMinutes().toString().padStart(2, '0')
const seconds = date.getSeconds().toString().padStart(2, '0') const seconds = date.getSeconds().toString().padStart(2, '0')
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}` return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`
} }
@ -518,7 +554,7 @@ const handleTeacherChanges = async (courseId: string, isCreateMode: boolean = fa
console.log('课程ID:', courseId) console.log('课程ID:', courseId)
console.log('当前老师列表:', formData.instructors) console.log('当前老师列表:', formData.instructors)
console.log('原始老师列表:', originalInstructors.value) console.log('原始老师列表:', originalInstructors.value)
if (isCreateMode) { if (isCreateMode) {
// //
console.log('📝 创建模式:绑定所有老师') console.log('📝 创建模式:绑定所有老师')
@ -538,15 +574,15 @@ const handleTeacherChanges = async (courseId: string, isCreateMode: boolean = fa
} else { } else {
// //
console.log('✏️ 编辑模式:比较老师变更') console.log('✏️ 编辑模式:比较老师变更')
// //
const teachersToAdd = formData.instructors.filter(id => !originalInstructors.value.includes(id)) const teachersToAdd = formData.instructors.filter(id => !originalInstructors.value.includes(id))
console.log('需要添加的老师:', teachersToAdd) console.log('需要添加的老师:', teachersToAdd)
// //
const teachersToRemove = originalInstructors.value.filter(id => !formData.instructors.includes(id)) const teachersToRemove = originalInstructors.value.filter(id => !formData.instructors.includes(id))
console.log('需要移除的老师:', teachersToRemove) console.log('需要移除的老师:', teachersToRemove)
// //
for (const instructorId of teachersToAdd) { for (const instructorId of teachersToAdd) {
try { try {
@ -560,7 +596,7 @@ const handleTeacherChanges = async (courseId: string, isCreateMode: boolean = fa
console.error('❌ 老师添加失败:', instructorId, error) console.error('❌ 老师添加失败:', instructorId, error)
} }
} }
// //
for (const instructorId of teachersToRemove) { for (const instructorId of teachersToRemove) {
try { try {
@ -575,7 +611,7 @@ const handleTeacherChanges = async (courseId: string, isCreateMode: boolean = fa
} }
} }
} }
console.log('✅ 老师变更处理完成') console.log('✅ 老师变更处理完成')
} catch (error) { } catch (error) {
console.error('❌ 处理老师变更失败:', error) console.error('❌ 处理老师变更失败:', error)
@ -630,11 +666,11 @@ const handleSubmit = async () => {
// File // File
// courseCover File // courseCover File
// courseCover null previewUrl 使 // courseCover null previewUrl 使
if (formData.courseCover && formData.courseCover instanceof File) { if (formData.courseCover && formData.courseCover instanceof File) {
try { try {
const uploadResponse = await UploadApi.uploadCourseThumbnail(formData.courseCover) const uploadResponse = await UploadApi.uploadCourseThumbnail(formData.courseCover)
if (uploadResponse.data.success) { if (uploadResponse.data.success) {
coverUrl = uploadResponse.data.message coverUrl = uploadResponse.data.message
} else { } else {
@ -675,19 +711,19 @@ const handleSubmit = async () => {
status: 1, // status: 1, //
question: null question: null
} }
if (isEditMode.value) { if (isEditMode.value && !isCopyMode.value) {
// //
const editData = { const editData = {
id: courseId.value, id: courseId.value,
...createCourseData ...createCourseData
} }
const response = await TeachCourseApi.editCourse(editData) const response = await TeachCourseApi.editCourse(editData)
if (response.data.code === 200) { if (response.data.code === 200) {
// //
await handleTeacherChanges(courseId.value); await handleTeacherChanges(courseId.value);
message.success('课程更新成功!') message.success('课程更新成功!')
// //
courseStore.clearCourseEditData(); courseStore.clearCourseEditData();
@ -699,14 +735,14 @@ const handleSubmit = async () => {
} else { } else {
// //
const response = await TeachCourseApi.createCourse(createCourseData) const response = await TeachCourseApi.createCourse(createCourseData)
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);
} }
message.success('课程创建成功!') message.success('课程创建成功!')
// //
courseStore.clearCourseEditData(); courseStore.clearCourseEditData();
@ -716,7 +752,7 @@ const handleSubmit = async () => {
message.error(response.message || '创建失败,请重试') message.error(response.message || '创建失败,请重试')
} }
} }
} catch (error) { } catch (error) {
const errorMessage = isEditMode.value ? '更新失败,请重试' : '创建失败,请重试' const errorMessage = isEditMode.value ? '更新失败,请重试' : '创建失败,请重试'
message.error(errorMessage) message.error(errorMessage)
@ -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,27 +777,34 @@ 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({
@ -768,22 +812,36 @@ const getClassList = () => {
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>
@ -804,28 +862,28 @@ onMounted(() => {
.header-left { .header-left {
display: flex; display: flex;
align-items: center; align-items: center;
gap: 16px; gap: 16px;
} }
.page-title { .page-title {
margin: 0; margin: 0;
font-size: 18px; font-size: 18px;
font-weight: 600; font-weight: 600;
color: #333; color: #333;
} }
.title { .title {
margin: 0; margin: 0;
font-size: 20px; font-size: 20px;
font-weight: 500; font-weight: 500;
color: #333; color: #333;
} }
.header-actions { .header-actions {
display: flex; display: flex;
gap: 12px; gap: 12px;
} }
.form-row { .form-row {

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 {