完成资源案例

This commit is contained in:
2025-11-04 11:53:16 +08:00
parent 0bec4c0129
commit 6001c8d918

View File

@@ -32,7 +32,7 @@
<div class="courses-grid"> <div class="courses-grid">
<div <div
class="course-card" class="course-card"
v-for="course in initCourses" v-for="(course, index) in initCourses"
:key="course.id" :key="course.id"
@click="selectCourse(course)" @click="selectCourse(course)"
style="cursor: pointer" style="cursor: pointer"
@@ -69,7 +69,7 @@
<div class="courses-grid"> <div class="courses-grid">
<div <div
class="course-card" class="course-card"
v-for="course in onlineCourses" v-for="(course, index) in onlineCourses"
:key="course.id" :key="course.id"
@click="selectCourse(course)" @click="selectCourse(course)"
style="cursor: pointer" style="cursor: pointer"
@@ -142,10 +142,10 @@
</div> </div>
</div> </div>
<div v-if="chapters[chapter.id]?.expanded && chapter.children?.length" class="sub-chapters-container"> <div v-if="chapters[chapter.id]?.expanded && chapter.children?.length" class="sub-chapters-container">
<div v-for="sub in chapter.children.sort((a,b)=>a.sort-b.sort)" :key="sub.id" class="sub-chapter-item"> <div v-for="sub in chapter.children.sort((a: Chapter, b: Chapter) => a.sort - b.sort)" :key="sub.id" class="sub-chapter-item">
<div class="sub-chapter-title">{{ sub.title }}</div> <div class="sub-chapter-title">{{ sub.title }}</div>
<div v-if="sub.children?.length" class="grand-children-container"> <div v-if="sub.children?.length" class="grand-children-container">
<div v-for="grand in sub.children.sort((a,b)=>a.sort-b.sort)" :key="grand.id" class="grand-child-title"> <div v-for="grand in sub.children.sort((a: Chapter, b: Chapter) => a.sort - b.sort)" :key="grand.id" class="grand-child-title">
{{ grand.title }} {{ grand.title }}
</div> </div>
</div> </div>
@@ -417,21 +417,105 @@
</div> </div>
</template> </template>
<script setup> <script setup lang="ts">
import { ref, onMounted } from 'vue'; import { ref, onMounted } from 'vue';
import axios from 'axios'; import axios from 'axios';
// ==================== 核心类型定义 ====================
/** 线上课程类型 */
interface Course {
id: number;
cover_url: string;
title: string;
subtitle?: string;
intro: string;
[key: string]: any;
}
/** 教学案例类型 */
interface TeachingCase {
id: number;
title: string;
tutor_name: string;
tutor_title: string;
student_names?: string;
content: string;
cover_url: string;
create_time: string;
update_time: string;
[key: string]: any;
}
/** 视频案例类型 */
interface VideoCase {
id: number;
video_url: string;
title: string;
intro: string;
designer_names: string;
tutor_names: string;
create_time: string;
update_time: string;
[key: string]: any;
}
/** 课程章节类型 */
interface Chapter {
id: number;
title: string;
children?: Chapter[];
sort: number;
[key: string]: any;
}
/** 拓展资源类型 */
interface Resource {
id: number;
title: string;
resource_url: string;
[key: string]: any;
}
/** 学生活动类型 */
interface Activity {
id: number;
title: string;
content: string;
start_time: string;
end_time: string;
[key: string]: any;
}
/** 教师类型 */
interface Teacher {
id: number;
avatar: string;
name: string;
title: string;
intro: string;
[key: string]: any;
}
/** 接口响应基础类型 */
interface ApiResponse<T = any> {
code?: number;
message?: string;
data?: T;
list?: T[];
total?: number;
}
// ==================== 全局状态 ==================== // ==================== 全局状态 ====================
const activeTab = ref('online'); const activeTab = ref<'online' | 'teaching' | 'video'>('online');
const selectedCourse = ref(null); const selectedCourse = ref<Course | null>(null);
const selectedCase = ref(null); const selectedCase = ref<TeachingCase | null>(null);
const playingVideo = ref(null); const playingVideo = ref<VideoCase | null>(null);
const activeDetailTab = ref('content'); const activeDetailTab = ref<'content' | 'resource' | 'activity' | 'team'>('content');
const chapters = ref({}); const chapters = ref<Record<number, { expanded: boolean }>>({});
// 线上课程 // 线上课程
const onlineCourses = ref([]); const onlineCourses = ref<Course[]>([]);
const initCourses = ref([]); const initCourses = ref<Course[]>([]);
const currentPage = ref(1); const currentPage = ref(1);
const pageSize = ref(10); const pageSize = ref(10);
const totalCount = ref(0); const totalCount = ref(0);
@@ -443,7 +527,7 @@ const showFullCourseList = ref(false);
const searchKeyword = ref(''); const searchKeyword = ref('');
// 教学案例 // 教学案例
const caseList = ref([]); const caseList = ref<TeachingCase[]>([]);
const casePage = ref(1); const casePage = ref(1);
const caseSize = 20; const caseSize = 20;
const caseTotal = ref(0); const caseTotal = ref(0);
@@ -454,7 +538,7 @@ const caseError = ref('');
const caseKeyword = ref(''); const caseKeyword = ref('');
// 视频案例 // 视频案例
const videoList = ref([]); const videoList = ref<VideoCase[]>([]);
const videoPage = ref(1); const videoPage = ref(1);
const videoSize = 10; const videoSize = 10;
const videoTotal = ref(0); const videoTotal = ref(0);
@@ -465,53 +549,53 @@ const videoError = ref('');
const videoKeyword = ref(''); const videoKeyword = ref('');
// 课程详情 // 课程详情
const courseChapters = ref([]); const courseChapters = ref<Chapter[]>([]);
const loadingChapters = ref(false); const loadingChapters = ref(false);
const chapterError = ref(''); const chapterError = ref('');
const resourceList = ref([]); const resourceList = ref<Resource[]>([]);
const resourcePage = ref(1); const resourcePage = ref(1);
const resourcePageSize = 20; const resourcePageSize = 20;
const hasMoreResources = ref(true); const hasMoreResources = ref(true);
const loadingResources = ref(false); const loadingResources = ref(false);
const loadingMoreResources = ref(false); const loadingMoreResources = ref(false);
const resourceError = ref(''); const resourceError = ref('');
const activityList = ref([]); const activityList = ref<Activity[]>([]);
const activityPage = ref(1); const activityPage = ref(1);
const activityPageSize = 10; const activityPageSize = 10;
const hasMoreActivities = ref(true); const hasMoreActivities = ref(true);
const loadingActivities = ref(false); const loadingActivities = ref(false);
const loadingMoreActivities = ref(false); const loadingMoreActivities = ref(false);
const activityError = ref(''); const activityError = ref('');
const teacherList = ref([]); const teacherList = ref<Teacher[]>([]);
const teacherPage = ref(1); const teacherPage = ref(1);
const teacherPageSize = 10; const teacherPageSize = 10;
const hasMoreTeachers = ref(true); const hasMoreTeachers = ref(true);
const loadingTeachers = ref(false); const loadingTeachers = ref(false);
const loadingMoreTeachers = ref(false); const loadingMoreTeachers = ref(false);
const teacherError = ref(''); const teacherError = ref('');
const currentCourseId = ref(null); const currentCourseId = ref<number | null>(null);
// ==================== 工具函数 ==================== // ==================== 工具函数 ====================
const formatDate = (time) => (time ? time.split(' ')[0] : ''); const formatDate = (time: string | undefined) => (time ? time.split(' ')[0] : '');
const formatTime = (timeStr) => (timeStr ? timeStr.split('.')[0].replace(' ', ' ') : ''); const formatTime = (timeStr: string | undefined) => (timeStr ? timeStr.split('.')[0]?.replace(' ', ' ') : '');
const onAvatarError = (e) => { e.target.src = 'https://via.placeholder.com/80?text=头像'; }; const onAvatarError = (e: Event) => { (e.target as HTMLImageElement).src = 'https://via.placeholder.com/80?text=头像'; };
const onVideoError = (e) => { console.error('视频加载失败:', e); alert('视频加载失败'); }; const onVideoError = (e: Event) => { console.error('视频加载失败:', e); alert('视频加载失败'); };
const onVideoLoaded = (e) => { e.target.currentTime = 0.1; }; const onVideoLoaded = (e: Event) => { (e.target as HTMLVideoElement).currentTime = 0.1; };
const getFileExtension = (url) => { const getFileExtension = (url: string | undefined) => {
if (!url) return ''; if (!url) return '';
const fileName = url.split('/').pop().split('?')[0]; const fileName = url.split('/').pop()?.split('?')[0] || '';
const parts = fileName.split('.'); const parts = fileName.split('.');
return parts.length > 1 ? parts.pop().toLowerCase() : ''; return parts.length > 1 ? parts.pop()?.toLowerCase() || '' : '';
}; };
const getDisplayTitle = (item) => { const getDisplayTitle = (item: Resource) => {
const ext = getFileExtension(item.resource_url); const ext = getFileExtension(item.resource_url);
return ext ? `${item.title}.${ext}` : item.title; return ext ? `${item.title}.${ext}` : item.title;
}; };
const openResource = (url) => { if (url) window.open(url, '_blank'); }; const openResource = (url: string | undefined) => { if (url) window.open(url, '_blank'); };
// ==================== 统一 tab 切换 ==================== // ==================== 统一 tab 切换 ====================
const setActiveTab = (tab) => { const setActiveTab = (tab: 'content' | 'resource' | 'activity' | 'team') => {
if (activeDetailTab.value === tab) return; if (activeDetailTab.value === tab) return;
activeDetailTab.value = tab; activeDetailTab.value = tab;
if (!selectedCourse.value) return; if (!selectedCourse.value) return;
@@ -522,7 +606,7 @@ const setActiveTab = (tab) => {
}; };
// ==================== 导航切换 ==================== // ==================== 导航切换 ====================
const handleTabChange = (tab) => { const handleTabChange = (tab: 'online' | 'teaching' | 'video') => {
activeTab.value = tab; activeTab.value = tab;
selectedCourse.value = null; selectedCourse.value = null;
selectedCase.value = null; selectedCase.value = null;
@@ -538,16 +622,16 @@ const initOnlineCourses = async () => {
loadingInit.value = true; loadingInit.value = true;
try { try {
const res = await fetchCoursePage(1, ''); const res = await fetchCoursePage(1, '');
onlineCourses.value = res.list; onlineCourses.value = res.list as Course[];
totalCount.value = res.total; totalCount.value = res.total || 0;
currentPage.value = 1; currentPage.value = 1;
hasMore.value = currentPage.value * pageSize.value < totalCount.value; hasMore.value = currentPage.value * pageSize.value < totalCount.value;
initCourses.value = onlineCourses.value.slice(0, 3); initCourses.value = onlineCourses.value.slice(0, 3);
} catch { errorMsg.value = '加载失败'; } finally { loadingInit.value = false; } } catch { errorMsg.value = '加载失败'; } finally { loadingInit.value = false; }
}; };
const fetchCoursePage = async (page, keyword) => { const fetchCoursePage = async (page: number, keyword: string): Promise<ApiResponse<Course>> => {
const { data } = await axios.post('http://localhost:8080/api/courses/list', { const { data } = await axios.post<ApiResponse<Course>>('http://localhost:8080/api/courses/list', {
page, size: pageSize.value, status: 1, keyword: keyword || '' page, size: pageSize.value, status: 1, keyword: keyword || ''
}); });
if (!data?.list) throw new Error(); if (!data?.list) throw new Error();
@@ -558,8 +642,8 @@ const handleCourseSearch = async () => {
loadingInit.value = true; loadingInit.value = true;
try { try {
const res = await fetchCoursePage(1, searchKeyword.value); const res = await fetchCoursePage(1, searchKeyword.value);
onlineCourses.value = res.list; onlineCourses.value = res.list as Course[];
totalCount.value = res.total; totalCount.value = res.total || 0;
currentPage.value = 1; currentPage.value = 1;
hasMore.value = currentPage.value * pageSize.value < totalCount.value; hasMore.value = currentPage.value * pageSize.value < totalCount.value;
} catch { errorMsg.value = '搜索失败'; } finally { loadingInit.value = false; } } catch { errorMsg.value = '搜索失败'; } finally { loadingInit.value = false; }
@@ -570,9 +654,10 @@ const loadNextPage = async () => {
loadingMore.value = true; loadingMore.value = true;
try { try {
const res = await fetchCoursePage(currentPage.value + 1, searchKeyword.value); const res = await fetchCoursePage(currentPage.value + 1, searchKeyword.value);
onlineCourses.value.push(...res.list); const newCourses = res.list as Course[];
onlineCourses.value.push(...newCourses);
currentPage.value++; currentPage.value++;
hasMore.value = currentPage.value * pageSize.value < totalCount.value; hasMore.value = currentPage.value * pageSize.value < (res.total || 0);
} catch { errorMsg.value = '加载失败'; } finally { loadingMore.value = false; } } catch { errorMsg.value = '加载失败'; } finally { loadingMore.value = false; }
}; };
@@ -587,15 +672,15 @@ const initCases = async () => {
caseError.value = ''; caseError.value = '';
try { try {
const res = await fetchCasePage(1, caseKeyword.value); const res = await fetchCasePage(1, caseKeyword.value);
caseList.value = res.list; caseList.value = res.list as TeachingCase[];
caseTotal.value = res.total; caseTotal.value = res.total || 0;
casePage.value = 1; casePage.value = 1;
caseHasMore.value = casePage.value * caseSize < caseTotal.value; caseHasMore.value = casePage.value * caseSize < caseTotal.value;
} catch { caseError.value = '加载失败'; } finally { caseLoading.value = false; } } catch { caseError.value = '加载失败'; } finally { caseLoading.value = false; }
}; };
const fetchCasePage = async (page, keyword) => { const fetchCasePage = async (page: number, keyword: string): Promise<ApiResponse<TeachingCase>> => {
const { data } = await axios.post('http://localhost:8080/api/teaching-cases/list', { const { data } = await axios.post<ApiResponse<TeachingCase>>('http://localhost:8080/api/teaching-cases/list', {
page, size: caseSize, keyword: keyword || '', sort: 0 page, size: caseSize, keyword: keyword || '', sort: 0
}); });
if (!data?.list || typeof data.total !== 'number') throw new Error(); if (!data?.list || typeof data.total !== 'number') throw new Error();
@@ -606,8 +691,8 @@ const searchCases = async () => {
caseLoading.value = true; caseLoading.value = true;
try { try {
const res = await fetchCasePage(1, caseKeyword.value); const res = await fetchCasePage(1, caseKeyword.value);
caseList.value = res.list; caseList.value = res.list as TeachingCase[];
caseTotal.value = res.total; caseTotal.value = res.total || 0;
casePage.value = 1; casePage.value = 1;
caseHasMore.value = casePage.value * caseSize < caseTotal.value; caseHasMore.value = casePage.value * caseSize < caseTotal.value;
} catch { caseError.value = '搜索失败'; } finally { caseLoading.value = false; } } catch { caseError.value = '搜索失败'; } finally { caseLoading.value = false; }
@@ -618,13 +703,14 @@ const loadMoreCases = async () => {
caseLoadingMore.value = true; caseLoadingMore.value = true;
try { try {
const res = await fetchCasePage(casePage.value + 1, caseKeyword.value); const res = await fetchCasePage(casePage.value + 1, caseKeyword.value);
caseList.value.push(...res.list); const newCases = res.list as TeachingCase[];
caseList.value.push(...newCases);
casePage.value++; casePage.value++;
caseHasMore.value = casePage.value * caseSize < caseTotal.value; caseHasMore.value = casePage.value * caseSize < (res.total || 0);
} catch { caseError.value = '加载更多失败'; } finally { caseLoadingMore.value = false; } } catch { caseError.value = '加载更多失败'; } finally { caseLoadingMore.value = false; }
}; };
const selectCase = (item) => { selectedCase.value = item; }; const selectCase = (item: TeachingCase) => { selectedCase.value = item; };
const backToCaseList = () => { selectedCase.value = null; }; const backToCaseList = () => { selectedCase.value = null; };
// ==================== 视频案例 ==================== // ==================== 视频案例 ====================
@@ -633,15 +719,15 @@ const initVideos = async () => {
videoError.value = ''; videoError.value = '';
try { try {
const res = await fetchVideoPage(1, videoKeyword.value); const res = await fetchVideoPage(1, videoKeyword.value);
videoList.value = res.list; videoList.value = res.list as VideoCase[];
videoTotal.value = res.total; videoTotal.value = res.total || 0;
videoPage.value = 1; videoPage.value = 1;
videoHasMore.value = videoPage.value * videoSize < videoTotal.value; videoHasMore.value = videoPage.value * videoSize < videoTotal.value;
} catch { videoError.value = '加载失败'; } finally { videoLoading.value = false; } } catch { videoError.value = '加载失败'; } finally { videoLoading.value = false; }
}; };
const fetchVideoPage = async (page, keyword) => { const fetchVideoPage = async (page: number, keyword: string): Promise<ApiResponse<VideoCase>> => {
const { data } = await axios.post('http://localhost:8080/api/video-cases/list', { const { data } = await axios.post<ApiResponse<VideoCase>>('http://localhost:8080/api/video-cases/list', {
page, size: videoSize, keyword: keyword || '', sort: 0 page, size: videoSize, keyword: keyword || '', sort: 0
}); });
if (!data?.list || typeof data.total !== 'number') throw new Error(); if (!data?.list || typeof data.total !== 'number') throw new Error();
@@ -652,8 +738,8 @@ const searchVideos = async () => {
videoLoading.value = true; videoLoading.value = true;
try { try {
const res = await fetchVideoPage(1, videoKeyword.value); const res = await fetchVideoPage(1, videoKeyword.value);
videoList.value = res.list; videoList.value = res.list as VideoCase[];
videoTotal.value = res.total; videoTotal.value = res.total || 0;
videoPage.value = 1; videoPage.value = 1;
videoHasMore.value = videoPage.value * videoSize < videoTotal.value; videoHasMore.value = videoPage.value * videoSize < videoTotal.value;
} catch { videoError.value = '搜索失败'; } finally { videoLoading.value = false; } } catch { videoError.value = '搜索失败'; } finally { videoLoading.value = false; }
@@ -664,95 +750,160 @@ const loadMoreVideos = async () => {
videoLoadingMore.value = true; videoLoadingMore.value = true;
try { try {
const res = await fetchVideoPage(videoPage.value + 1, videoKeyword.value); const res = await fetchVideoPage(videoPage.value + 1, videoKeyword.value);
videoList.value.push(...res.list); const newVideos = res.list as VideoCase[];
videoList.value.push(...newVideos);
videoPage.value++; videoPage.value++;
videoHasMore.value = videoPage.value * videoSize < videoTotal.value; videoHasMore.value = videoPage.value * videoSize < (res.total || 0);
} catch { videoError.value = '加载更多失败'; } finally { videoLoadingMore.value = false; } } catch { videoError.value = '加载更多失败'; } finally { videoLoadingMore.value = false; }
}; };
const playVideo = (item) => { playingVideo.value = item; }; const playVideo = (item: VideoCase) => { playingVideo.value = item; };
const backToVideoList = () => { playingVideo.value = null; }; const backToVideoList = () => { playingVideo.value = null; };
// ==================== 课程详情 ==================== // ==================== 课程详情 ====================
const fetchCourseContent = async (id) => { const fetchCourseContent = async (id: number) => {
loadingChapters.value = true; loadingChapters.value = true;
try { try {
const { data } = await axios.post('http://localhost:8080/api/course-content/list', { course_id: id, parent_id: 0 }); const { data } = await axios.post<ApiResponse<Chapter>>('http://localhost:8080/api/course-content/list', { course_id: id, parent_id: 0 });
if (data.code === 0 && Array.isArray(data.data)) { if (data.code === 0 && Array.isArray(data.data)) {
courseChapters.value = data.data.sort((a,b)=>a.sort-b.sort); courseChapters.value = data.data.sort((a: Chapter, b: Chapter) => a.sort - b.sort);
courseChapters.value.forEach(c => chapters.value[c.id] = { expanded: false }); courseChapters.value.forEach(c => chapters.value[c.id] = { expanded: false });
} }
} catch { chapterError.value = '加载失败'; } finally { loadingChapters.value = false; } } catch { chapterError.value = '加载失败'; } finally { loadingChapters.value = false; }
}; };
const toggleChapter = (id) => { const toggleChapter = (id: number) => {
if (!chapters.value[id]) chapters.value[id] = { expanded: false }; if (!chapters.value[id]) chapters.value[id] = { expanded: false };
chapters.value[id].expanded = !chapters.value[id].expanded; chapters.value[id].expanded = !chapters.value[id].expanded;
}; };
const fetchResources = async (page = 1, reset = false) => { const fetchResources = async (page = 1, reset = false) => {
if (reset) { resourceList.value = []; resourcePage.value = 1; hasMoreResources.value = true; resourceError.value = ''; loadingResources.value = true; } if (!selectedCourse.value) return;
else loadingMoreResources.value = true; if (reset) {
resourceList.value = [];
resourcePage.value = 1;
hasMoreResources.value = true;
resourceError.value = '';
loadingResources.value = true;
} else {
loadingMoreResources.value = true;
}
try { try {
const { data } = await axios.post('http://localhost:8080/api/course-resource/list', { const { data } = await axios.post<ApiResponse<Resource>>('http://localhost:8080/api/course-resource/list', {
course_id: selectedCourse.value.id, page, page_size: resourcePageSize course_id: selectedCourse.value.id,
page,
page_size: resourcePageSize
}); });
if (!data?.list || typeof data.total !== 'number') throw new Error(); if (!data?.list || typeof data.total !== 'number') throw new Error();
if (reset) resourceList.value = data.list; else resourceList.value.push(...data.list); const newResources = data.list as Resource[];
if (reset) resourceList.value = newResources;
else resourceList.value.push(...newResources);
resourcePage.value = page; resourcePage.value = page;
hasMoreResources.value = page * resourcePageSize < data.total; hasMoreResources.value = page * resourcePageSize < data.total;
currentCourseId.value = selectedCourse.value.id; currentCourseId.value = selectedCourse.value.id;
} catch { resourceError.value = reset ? '加载失败' : '加载更多失败'; } } catch {
finally { loadingResources.value = false; loadingMoreResources.value = false; } resourceError.value = reset ? '加载失败' : '加载更多失败';
} finally {
loadingResources.value = false;
loadingMoreResources.value = false;
}
}; };
const loadMoreResources = () => { if (loadingMoreResources.value || !hasMoreResources.value) return; fetchResources(resourcePage.value + 1); }; const loadMoreResources = () => {
if (loadingMoreResources.value || !hasMoreResources.value) return;
fetchResources(resourcePage.value + 1);
};
const fetchActivities = async (page = 1, reset = false) => { const fetchActivities = async (page = 1, reset = false) => {
if (reset) { activityList.value = []; activityPage.value = 1; hasMoreActivities.value = true; activityError.value = ''; loadingActivities.value = true; } if (!selectedCourse.value) return;
else loadingMoreActivities.value = true; if (reset) {
activityList.value = [];
activityPage.value = 1;
hasMoreActivities.value = true;
activityError.value = '';
loadingActivities.value = true;
} else {
loadingMoreActivities.value = true;
}
try { try {
const { data } = await axios.post('http://localhost:8080/api/course-activity/list', { const { data } = await axios.post<ApiResponse<Activity>>('http://localhost:8080/api/course-activity/list', {
course_id: selectedCourse.value.id, page, page_size: activityPageSize course_id: selectedCourse.value.id,
page,
page_size: activityPageSize
}); });
if (!data || typeof data.total !== 'number' || !Array.isArray(data.list)) throw new Error(); if (!data || typeof data.total !== 'number' || !Array.isArray(data.list)) throw new Error();
if (reset) activityList.value = data.list; else activityList.value.push(...data.list); const newActivities = data.list as Activity[];
if (reset) activityList.value = newActivities;
else activityList.value.push(...newActivities);
activityPage.value = page; activityPage.value = page;
hasMoreActivities.value = page * activityPageSize < data.total; hasMoreActivities.value = page * activityPageSize < data.total;
currentCourseId.value = selectedCourse.value.id; currentCourseId.value = selectedCourse.value.id;
} catch { activityError.value = reset ? '加载失败' : '加载更多失败'; } } catch {
finally { loadingActivities.value = false; loadingMoreActivities.value = false; } activityError.value = reset ? '加载失败' : '加载更多失败';
} finally {
loadingActivities.value = false;
loadingMoreActivities.value = false;
}
}; };
const loadMoreActivities = () => { if (loadingMoreActivities.value || !hasMoreActivities.value) return; fetchActivities(activityPage.value + 1); }; const loadMoreActivities = () => {
if (loadingMoreActivities.value || !hasMoreActivities.value) return;
fetchActivities(activityPage.value + 1);
};
const fetchTeachers = async (page = 1, reset = false) => { const fetchTeachers = async (page = 1, reset = false) => {
if (reset) { teacherList.value = []; teacherPage.value = 1; hasMoreTeachers.value = true; teacherError.value = ''; loadingTeachers.value = true; } if (!selectedCourse.value) return;
else loadingMoreTeachers.value = true; if (reset) {
teacherList.value = [];
teacherPage.value = 1;
hasMoreTeachers.value = true;
teacherError.value = '';
loadingTeachers.value = true;
} else {
loadingMoreTeachers.value = true;
}
try { try {
const { data } = await axios.post('http://localhost:8080/api/course-teacher/list', { const { data } = await axios.post<ApiResponse<Teacher>>('http://localhost:8080/api/course-teacher/list', {
course_id: selectedCourse.value.id, page, page_size: teacherPageSize course_id: selectedCourse.value.id,
page,
page_size: teacherPageSize
}); });
if (!data || typeof data.total !== 'number' || !Array.isArray(data.list)) throw new Error(); if (!data || typeof data.total !== 'number' || !Array.isArray(data.list)) throw new Error();
if (reset) teacherList.value = data.list; else teacherList.value.push(...data.list); const newTeachers = data.list as Teacher[];
if (reset) teacherList.value = newTeachers;
else teacherList.value.push(...newTeachers);
teacherPage.value = page; teacherPage.value = page;
hasMoreTeachers.value = page * teacherPageSize < data.total; hasMoreTeachers.value = page * teacherPageSize < data.total;
currentCourseId.value = selectedCourse.value.id; currentCourseId.value = selectedCourse.value.id;
} catch { teacherError.value = reset ? '加载失败' : '加载更多失败'; } } catch {
finally { loadingTeachers.value = false; loadingMoreTeachers.value = false; } teacherError.value = reset ? '加载失败' : '加载更多失败';
} finally {
loadingTeachers.value = false;
loadingMoreTeachers.value = false;
}
}; };
const loadMoreTeachers = () => { if (loadingMoreTeachers.value || !hasMoreTeachers.value) return; fetchTeachers(teacherPage.value + 1); }; const loadMoreTeachers = () => {
if (loadingMoreTeachers.value || !hasMoreTeachers.value) return;
fetchTeachers(teacherPage.value + 1);
};
const selectCourse = (course) => { const selectCourse = (course: Course) => {
selectedCourse.value = course; selectedCourse.value = course;
currentCourseId.value = course.id; currentCourseId.value = course.id;
courseChapters.value = []; resourceList.value = []; activityList.value = []; teacherList.value = []; chapters.value = {}; courseChapters.value = [];
resourceList.value = [];
activityList.value = [];
teacherList.value = [];
chapters.value = {};
fetchCourseContent(course.id); fetchCourseContent(course.id);
activeDetailTab.value = 'content'; activeDetailTab.value = 'content';
}; };
const handleBackToList = () => { selectedCourse.value = null; currentCourseId.value = null; }; const handleBackToList = () => {
selectedCourse.value = null;
currentCourseId.value = null;
};
// ==================== 初始化 ==================== // ==================== 初始化 ====================
onMounted(() => { onMounted(() => {