Compare commits
4 Commits
3bd9e1fac3
...
8d2b4037d4
Author | SHA1 | Date | |
---|---|---|---|
![]() |
8d2b4037d4 | ||
![]() |
00ae37c216 | ||
![]() |
dd1c8de407 | ||
![]() |
4c43226c91 |
Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 1.0 KiB |
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.2 KiB |
Before Width: | Height: | Size: 490 B After Width: | Height: | Size: 490 B |
Before Width: | Height: | Size: 712 B After Width: | Height: | Size: 712 B |
@ -127,6 +127,23 @@ export interface QueryCourseSectionParams {
|
|||||||
*/
|
*/
|
||||||
export class TeachCourseApi {
|
export class TeachCourseApi {
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 菜单列表接口
|
||||||
|
*/
|
||||||
|
static async getTeacherMenuList(): Promise<ApiResponse<any>> {
|
||||||
|
try{
|
||||||
|
const response = await ApiRequest.get<any>('/aiol/aiolMenu/getTeacherMenus')
|
||||||
|
console.log('📝 获取教师菜单响应:', response);
|
||||||
|
|
||||||
|
return response
|
||||||
|
} catch(error) {
|
||||||
|
console.error('❌ 获取教师菜单失败:', error)
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 新建课程
|
* 新建课程
|
||||||
*/
|
*/
|
||||||
|
929
src/components/profile/CourseContent.vue
Normal file
@ -0,0 +1,929 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<!-- 课程筛选标签 -->
|
||||||
|
<div class="text-wrapper_1 flex-row">
|
||||||
|
<span
|
||||||
|
class="text_12"
|
||||||
|
:class="{ active: activeCourseTab === 'all' }"
|
||||||
|
@click="handleCourseTabChange('all')"
|
||||||
|
>全部课程</span
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="text_13"
|
||||||
|
:class="{ active: activeCourseTab === 'learning' }"
|
||||||
|
@click="handleCourseTabChange('learning')"
|
||||||
|
>学习中</span
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="text_14"
|
||||||
|
:class="{ active: activeCourseTab === 'completed' }"
|
||||||
|
@click="handleCourseTabChange('completed')"
|
||||||
|
>已完结</span
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 分割线 -->
|
||||||
|
<div class="course-divider"></div>
|
||||||
|
|
||||||
|
<!-- 课程列表 -->
|
||||||
|
<div class="course-list">
|
||||||
|
<!-- 加载状态 -->
|
||||||
|
<div v-if="loading" class="loading-wrapper">
|
||||||
|
<span>正在加载课程数据...</span>
|
||||||
|
</div>
|
||||||
|
<!-- 课程卡片 -->
|
||||||
|
<div
|
||||||
|
v-else-if="filteredCourses.length > 0"
|
||||||
|
v-for="course in filteredCourses"
|
||||||
|
:key="course.id"
|
||||||
|
class="box_2 flex-row justify-between"
|
||||||
|
>
|
||||||
|
<div class="block_4 flex-col">
|
||||||
|
<div class="box_3 flex-row justify-between">
|
||||||
|
<div class="status-image-container">
|
||||||
|
<img
|
||||||
|
class="status-image"
|
||||||
|
referrerpolicy="no-referrer"
|
||||||
|
:src="
|
||||||
|
getCourseStatusClass(course) === 'learning'
|
||||||
|
? '/images/icon/learning.png'
|
||||||
|
: '/images/icon/finish.png'
|
||||||
|
"
|
||||||
|
:alt="getStatusText(course)"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<img
|
||||||
|
class="thumbnail_4"
|
||||||
|
referrerpolicy="no-referrer"
|
||||||
|
:src="course.thumbnail"
|
||||||
|
:alt="course.name"
|
||||||
|
/>
|
||||||
|
<span :class="['status-text', getCourseStatusClass(course)]">{{
|
||||||
|
getStatusText(course)
|
||||||
|
}}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="block_5 flex-col">
|
||||||
|
<div class="group_6 flex-row">
|
||||||
|
<span class="text_16">{{ course.title }}</span>
|
||||||
|
|
||||||
|
<n-icon size="1.04vw" class="thumbnail_5">
|
||||||
|
<PersonOutline />
|
||||||
|
</n-icon>
|
||||||
|
<span class="text_17">{{ course.enrollmentCount || 0 }}</span>
|
||||||
|
</div>
|
||||||
|
<span class="text_18"
|
||||||
|
>讲师:{{
|
||||||
|
getInstructorNames(course.teacherList) || "暂无讲师"
|
||||||
|
}}</span
|
||||||
|
>
|
||||||
|
<span class="text_19">{{
|
||||||
|
formatDescription(course.description)
|
||||||
|
}}</span>
|
||||||
|
|
||||||
|
<div class="group_7 flex-row">
|
||||||
|
<img
|
||||||
|
class="thumbnail_6"
|
||||||
|
referrerpolicy="no-referrer"
|
||||||
|
src="/images/profile/11.png"
|
||||||
|
/>
|
||||||
|
<span class="text_20"
|
||||||
|
>共{{ course.chapterCount || 0 }}章{{
|
||||||
|
course.sectionCount || 0
|
||||||
|
}}节</span
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
class="thumbnail_7"
|
||||||
|
referrerpolicy="no-referrer"
|
||||||
|
src="/images/profile/22.png"
|
||||||
|
/>
|
||||||
|
<span class="text_21"
|
||||||
|
>{{
|
||||||
|
course.startDate
|
||||||
|
? new Date(course.startDate).toLocaleDateString()
|
||||||
|
: "时间待定"
|
||||||
|
}}
|
||||||
|
-
|
||||||
|
{{
|
||||||
|
course.endDate
|
||||||
|
? new Date(course.endDate).toLocaleDateString()
|
||||||
|
: "时间待定"
|
||||||
|
}}</span
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
class="thumbnail_8"
|
||||||
|
referrerpolicy="no-referrer"
|
||||||
|
src="/images/profile/33.png"
|
||||||
|
/>
|
||||||
|
<!-- <span class="text_22">报名:{{ course.enrollCount }}/{{ course.maxEnroll }}</span> -->
|
||||||
|
<div
|
||||||
|
class="text-wrapper_2 flex-col"
|
||||||
|
@click="goToCourseDetail(course)"
|
||||||
|
>
|
||||||
|
<span class="text_23">{{
|
||||||
|
getCourseStatusClass(course) === "learning"
|
||||||
|
? "去学习"
|
||||||
|
: getCourseStatusClass(course) === "completed"
|
||||||
|
? "去复习"
|
||||||
|
: "去报名"
|
||||||
|
}}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 空状态 -->
|
||||||
|
<div v-else class="empty-state">
|
||||||
|
<span>暂无课程数据</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 分页器 -->
|
||||||
|
<div class="pagination-wrapper" v-if="totalPages > 1">
|
||||||
|
<div class="pagination">
|
||||||
|
<span
|
||||||
|
class="pagination-item nav-button"
|
||||||
|
:class="{ disabled: currentPage === 1 }"
|
||||||
|
@click="goToPage('first')"
|
||||||
|
>
|
||||||
|
首页
|
||||||
|
</span>
|
||||||
|
<span
|
||||||
|
class="pagination-item nav-button"
|
||||||
|
:class="{ disabled: currentPage === 1 }"
|
||||||
|
@click="goToPage('prev')"
|
||||||
|
>
|
||||||
|
上一页
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<span
|
||||||
|
v-for="page in totalPages"
|
||||||
|
:key="page"
|
||||||
|
class="pagination-item page-number"
|
||||||
|
:class="{ active: page === currentPage }"
|
||||||
|
@click="goToPage(page)"
|
||||||
|
>
|
||||||
|
{{ page }}
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<span
|
||||||
|
class="pagination-item nav-button"
|
||||||
|
:class="{ disabled: currentPage === totalPages }"
|
||||||
|
@click="goToPage('next')"
|
||||||
|
>
|
||||||
|
下一页
|
||||||
|
</span>
|
||||||
|
<span
|
||||||
|
class="pagination-item nav-button"
|
||||||
|
:class="{ disabled: currentPage === totalPages }"
|
||||||
|
@click="goToPage('last')"
|
||||||
|
>
|
||||||
|
尾页
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { ref, computed, onMounted } from "vue";
|
||||||
|
// import { useI18n } from "vue-i18n";
|
||||||
|
import { useMessage } from "naive-ui";
|
||||||
|
import { useRouter } from "vue-router";
|
||||||
|
import { PersonOutline } from "@vicons/ionicons5";
|
||||||
|
|
||||||
|
import { CourseApi } from "@/api/modules/course";
|
||||||
|
import { TeachCourseApi } from "@/api/modules/teachCourse";
|
||||||
|
import { useUserStore } from "@/stores/user";
|
||||||
|
|
||||||
|
const userStore = useUserStore();
|
||||||
|
const router = useRouter();
|
||||||
|
|
||||||
|
// const { locale } = useI18n();
|
||||||
|
const message = useMessage();
|
||||||
|
|
||||||
|
// 定义章节接口
|
||||||
|
interface Section {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
level: number; // 1为章,2为节
|
||||||
|
parentId?: string;
|
||||||
|
courseId: string;
|
||||||
|
sortOrder?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 定义教师接口
|
||||||
|
interface Teacher {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
avatar: string;
|
||||||
|
title: string;
|
||||||
|
tag: string;
|
||||||
|
sortOrder: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 定义课程接口
|
||||||
|
interface Course {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
cover: string;
|
||||||
|
video: string;
|
||||||
|
school: string;
|
||||||
|
description: string;
|
||||||
|
type: number;
|
||||||
|
target: string;
|
||||||
|
difficulty: number;
|
||||||
|
subject: string;
|
||||||
|
outline: string;
|
||||||
|
prerequisite: string;
|
||||||
|
reference: string;
|
||||||
|
arrangement: string;
|
||||||
|
startTime: string;
|
||||||
|
endTime: string;
|
||||||
|
enrollCount: number;
|
||||||
|
maxEnroll: number;
|
||||||
|
status: number;
|
||||||
|
question: string;
|
||||||
|
izAi: number;
|
||||||
|
pauseExit: number;
|
||||||
|
allowSpeed: number;
|
||||||
|
showSubtitle: number;
|
||||||
|
publishStatus: number;
|
||||||
|
semester: string | null;
|
||||||
|
allowDownload: number;
|
||||||
|
createBy: string;
|
||||||
|
createTime: string;
|
||||||
|
updateBy: string;
|
||||||
|
updateTime: string;
|
||||||
|
teacherList: Teacher[];
|
||||||
|
isEnrolled: boolean;
|
||||||
|
// 新增章节信息
|
||||||
|
chapterCount?: number; // 章数
|
||||||
|
sectionCount?: number; // 节数
|
||||||
|
sections?: Section[]; // 章节列表
|
||||||
|
}
|
||||||
|
|
||||||
|
// 课程筛选状态
|
||||||
|
const activeCourseTab = ref("all");
|
||||||
|
|
||||||
|
// 分页相关状态
|
||||||
|
const currentPage = ref(1);
|
||||||
|
const pageSize = ref(10); // 每页显示5个课程
|
||||||
|
|
||||||
|
// 课程数据状态
|
||||||
|
const courses = ref<any[]>([]);
|
||||||
|
const loading = ref(false);
|
||||||
|
const total = ref(0);
|
||||||
|
|
||||||
|
// banner图片
|
||||||
|
// const bannerImage = computed(() => {
|
||||||
|
// return locale.value === "zh"
|
||||||
|
// ? "/banners/banner8.png"
|
||||||
|
// : "/banners/banner1-en.png";
|
||||||
|
// });
|
||||||
|
|
||||||
|
// const bannerAlt = computed(() => {
|
||||||
|
// return t('home.banner.alt')
|
||||||
|
// })
|
||||||
|
|
||||||
|
// 获取课程章节信息
|
||||||
|
const getCourseSections = async (courseId: string) => {
|
||||||
|
try {
|
||||||
|
const response = await TeachCourseApi.getCourseSections(courseId);
|
||||||
|
return response.data.result || [];
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`获取课程${courseId}章节信息失败:`, error);
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 计算章节数量
|
||||||
|
const calculateChapterAndSectionCount = (sections: any) => {
|
||||||
|
const chapterCount = sections.filter(
|
||||||
|
(section: any) => section.level === 1
|
||||||
|
).length;
|
||||||
|
const sectionCount = sections.filter(
|
||||||
|
(section: any) => section.level === 2
|
||||||
|
).length;
|
||||||
|
return { chapterCount, sectionCount };
|
||||||
|
};
|
||||||
|
// 获取课程数据(一次性获取所有数据)
|
||||||
|
const fetchCourses = async () => {
|
||||||
|
try {
|
||||||
|
loading.value = true;
|
||||||
|
// 移除分页参数,一次性获取所有课程
|
||||||
|
const response = await CourseApi.getCourses({}, true);
|
||||||
|
console.log("Fetched courses:", response.data);
|
||||||
|
|
||||||
|
if (response.data && Array.isArray(response.data)) {
|
||||||
|
// 为每个课程获取章节信息
|
||||||
|
const coursesWithSections = await Promise.all(
|
||||||
|
response.data.map(async (course: any) => {
|
||||||
|
// 获取章节信息
|
||||||
|
const sections = await getCourseSections(course.id);
|
||||||
|
const { chapterCount, sectionCount } =
|
||||||
|
calculateChapterAndSectionCount(sections);
|
||||||
|
|
||||||
|
return {
|
||||||
|
...course,
|
||||||
|
sections,
|
||||||
|
chapterCount,
|
||||||
|
sectionCount,
|
||||||
|
};
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
courses.value = coursesWithSections;
|
||||||
|
total.value = coursesWithSections.length;
|
||||||
|
console.log("Courses with sections:", coursesWithSections);
|
||||||
|
} else {
|
||||||
|
courses.value = [];
|
||||||
|
total.value = 0;
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
message.error("获取课程数据失败,请稍后重试。");
|
||||||
|
console.error("Error fetching courses:", error);
|
||||||
|
courses.value = [];
|
||||||
|
total.value = 0;
|
||||||
|
} finally {
|
||||||
|
loading.value = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 获取筛选后的所有课程
|
||||||
|
const allFilteredCourses = computed(() => {
|
||||||
|
if (activeCourseTab.value === "learning") {
|
||||||
|
// 筛选学习中的课程
|
||||||
|
return courses.value.filter(
|
||||||
|
(course) => getCourseStatusClass(course) === "learning"
|
||||||
|
);
|
||||||
|
} else if (activeCourseTab.value === "completed") {
|
||||||
|
// 筛选已完结的课程(时间区间已结束)
|
||||||
|
return courses.value.filter(
|
||||||
|
(course) => getCourseStatusClass(course) === "completed"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return courses.value;
|
||||||
|
});
|
||||||
|
|
||||||
|
// 计算总页数(基于筛选后的数据)
|
||||||
|
const totalPages = computed(() => {
|
||||||
|
return Math.ceil(allFilteredCourses.value.length / pageSize.value);
|
||||||
|
});
|
||||||
|
|
||||||
|
// 当前页显示的课程(前端分页)
|
||||||
|
const filteredCourses = computed(() => {
|
||||||
|
const start = (currentPage.value - 1) * pageSize.value;
|
||||||
|
const end = start + pageSize.value;
|
||||||
|
return allFilteredCourses.value.slice(start, end);
|
||||||
|
});
|
||||||
|
|
||||||
|
// 获取状态文本
|
||||||
|
const getStatusText = (course: Course) => {
|
||||||
|
const now = new Date();
|
||||||
|
const endTime = new Date(course.endTime);
|
||||||
|
|
||||||
|
if (course.isEnrolled) {
|
||||||
|
if (endTime < now) {
|
||||||
|
return "已完结";
|
||||||
|
} else {
|
||||||
|
return "学习中";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "未报名";
|
||||||
|
};
|
||||||
|
|
||||||
|
// 获取课程状态类名
|
||||||
|
const getCourseStatusClass = (course: Course) => {
|
||||||
|
const now = new Date();
|
||||||
|
const endTime = new Date(course.endTime);
|
||||||
|
|
||||||
|
if (course.isEnrolled) {
|
||||||
|
if (endTime < now) {
|
||||||
|
return "completed";
|
||||||
|
} else {
|
||||||
|
return "learning";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "not-enrolled";
|
||||||
|
};
|
||||||
|
|
||||||
|
// 获取教师名称
|
||||||
|
const getInstructorNames = (teacherList: Teacher[]) => {
|
||||||
|
return teacherList.map((teacher) => teacher.name).join(" ");
|
||||||
|
};
|
||||||
|
|
||||||
|
// 格式化课程描述(移除HTML标签)
|
||||||
|
const formatDescription = (description: string) => {
|
||||||
|
return description.replace(/<[^>]*>/g, "").substring(0, 200) + "...";
|
||||||
|
};
|
||||||
|
|
||||||
|
// 跳转到课程详情页
|
||||||
|
const goToCourseDetail = async (course: Course) => {
|
||||||
|
try {
|
||||||
|
// 检查用户是否已登录
|
||||||
|
if (!userStore.isLoggedIn) {
|
||||||
|
console.log("用户未登录,跳转到AI伴学页面");
|
||||||
|
router.push(`/ai-companion?courseId=${course.id}`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log("检查课程报名状态,课程ID:", course.id);
|
||||||
|
|
||||||
|
// 调用报名状态检查接口
|
||||||
|
const response = await CourseApi.checkEnrollmentStatus(String(course.id));
|
||||||
|
|
||||||
|
if ((response.code === 0 || response.code === 200) && response.data) {
|
||||||
|
const isEnrolled = response.data.result;
|
||||||
|
|
||||||
|
if (isEnrolled) {
|
||||||
|
// 已报名,跳转到已兑换页面
|
||||||
|
console.log("用户已报名,跳转到已兑换页面");
|
||||||
|
router.push(`/course/${course.id}/exchanged`);
|
||||||
|
} else {
|
||||||
|
// 未报名,跳转到AI伴学页面
|
||||||
|
console.log("用户未报名,跳转到AI伴学页面");
|
||||||
|
router.push(`/ai-companion?courseId=${course.id}`);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 查询失败,默认跳转到AI伴学页面
|
||||||
|
console.warn("查询报名状态失败,跳转到AI伴学页面");
|
||||||
|
router.push(`/ai-companion?courseId=${course.id}`);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error("检查报名状态时发生错误:", error);
|
||||||
|
// 发生错误时,默认跳转到AI伴学页面
|
||||||
|
router.push(`/ai-companion?courseId=${course.id}`);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 监听筛选变化,重置到第一页(不重新调用接口)
|
||||||
|
const handleCourseTabChange = (tab: string) => {
|
||||||
|
activeCourseTab.value = tab;
|
||||||
|
currentPage.value = 1; // 切换tab时重置到第一页
|
||||||
|
};
|
||||||
|
|
||||||
|
// 分页器方法(前端分页,不调用API)
|
||||||
|
const goToPage = (page: number | string) => {
|
||||||
|
let newPage = currentPage.value;
|
||||||
|
|
||||||
|
if (typeof page === "number") {
|
||||||
|
if (page >= 1 && page <= totalPages.value) {
|
||||||
|
newPage = page;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
switch (page) {
|
||||||
|
case "first":
|
||||||
|
if (currentPage.value > 1) {
|
||||||
|
newPage = 1;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "prev":
|
||||||
|
if (currentPage.value > 1) {
|
||||||
|
newPage = currentPage.value - 1;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "next":
|
||||||
|
if (currentPage.value < totalPages.value) {
|
||||||
|
newPage = currentPage.value + 1;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "last":
|
||||||
|
if (currentPage.value < totalPages.value) {
|
||||||
|
newPage = totalPages.value;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (newPage !== currentPage.value) {
|
||||||
|
currentPage.value = newPage;
|
||||||
|
// 不再调用fetchCourses(),纯前端分页
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
onMounted(async () => {
|
||||||
|
await fetchCourses();
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.text-wrapper_1 {
|
||||||
|
display: flex;
|
||||||
|
gap: 2.08vw;
|
||||||
|
padding-bottom: 1.04vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
.text_12,
|
||||||
|
.text_13,
|
||||||
|
.text_14,
|
||||||
|
.text_15 {
|
||||||
|
font-size: 0.94vw;
|
||||||
|
color: #000;
|
||||||
|
font-family: "Microsoft YaHei", Arial, sans-serif;
|
||||||
|
font-weight: normal;
|
||||||
|
cursor: pointer;
|
||||||
|
padding: 0.42vh 0;
|
||||||
|
transition: color 0.3s ease;
|
||||||
|
border-radius: 0.31vw;
|
||||||
|
}
|
||||||
|
|
||||||
|
.text_12.active,
|
||||||
|
.text_13.active,
|
||||||
|
.text_14.active,
|
||||||
|
.text_15.active {
|
||||||
|
color: rgba(2, 134, 206, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.text_12:hover,
|
||||||
|
.text_13:hover,
|
||||||
|
.text_14:hover,
|
||||||
|
.text_15:hover {
|
||||||
|
color: rgba(2, 134, 206, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.course-divider {
|
||||||
|
width: 100%;
|
||||||
|
height: 1.5px;
|
||||||
|
background: #e6e6e6;
|
||||||
|
margin-bottom: 1.67vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
.course-list {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 1.25vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
.box_2 {
|
||||||
|
width: 100%;
|
||||||
|
min-height: 10.42vh;
|
||||||
|
background: rgba(255, 255, 255, 1);
|
||||||
|
border: none;
|
||||||
|
border-radius: 0.6vw;
|
||||||
|
padding: 1.04vh 1.04vw;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.box_2:hover {
|
||||||
|
box-shadow: 0 0.21vh 1.04vh rgba(0, 0, 0, 0.1);
|
||||||
|
transform: translateY(-0.1vh);
|
||||||
|
}
|
||||||
|
|
||||||
|
.block_4 {
|
||||||
|
margin-right: 1.04vw;
|
||||||
|
}
|
||||||
|
|
||||||
|
.box_3 {
|
||||||
|
width: 202px;
|
||||||
|
height: 156px;
|
||||||
|
position: relative;
|
||||||
|
border-radius: 5px;
|
||||||
|
overflow: hidden;
|
||||||
|
background: rgba(243, 243, 243, 1);
|
||||||
|
box-shadow: 0 0.1vh 0.31vh rgba(0, 0, 0, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.thumbnail_4 {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
object-fit: cover;
|
||||||
|
border-radius: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-image-container {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
z-index: 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-image {
|
||||||
|
width: 66px;
|
||||||
|
height: 22px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-text {
|
||||||
|
position: absolute;
|
||||||
|
top: 26vh;
|
||||||
|
right: 0.63vw;
|
||||||
|
color: #fff;
|
||||||
|
font-size: 0.6vw;
|
||||||
|
font-family: "Microsoft YaHei", Arial, sans-serif;
|
||||||
|
font-weight: normal;
|
||||||
|
z-index: 12;
|
||||||
|
padding: 0.21vh 0.52vw;
|
||||||
|
border-radius: 0.21vw;
|
||||||
|
background: rgba(0, 0, 0, 0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-text.learning {
|
||||||
|
background: rgba(2, 134, 206, 0.8);
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-text.completed {
|
||||||
|
background: rgba(76, 175, 80, 0.8);
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-text.not-enrolled {
|
||||||
|
background: rgba(158, 158, 158, 0.8);
|
||||||
|
}
|
||||||
|
|
||||||
|
.block_5 {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.group_6 {
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 0.42vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
.text_16 {
|
||||||
|
font-size: 1.04vw;
|
||||||
|
font-weight: bold;
|
||||||
|
color: #333;
|
||||||
|
font-family: "Microsoft YaHei", Arial, sans-serif;
|
||||||
|
flex: 1;
|
||||||
|
margin-right: 0.52vw;
|
||||||
|
}
|
||||||
|
|
||||||
|
.thumbnail_5 {
|
||||||
|
width: 1.04vw;
|
||||||
|
height: 1.04vw;
|
||||||
|
margin-right: 0.26vw;
|
||||||
|
}
|
||||||
|
|
||||||
|
.text_17 {
|
||||||
|
font-size: 0.78vw;
|
||||||
|
color: #666;
|
||||||
|
font-family: "Microsoft YaHei", Arial, sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
.text_18 {
|
||||||
|
font-size: 0.78vw;
|
||||||
|
color: #999;
|
||||||
|
font-family: "Microsoft YaHei", Arial, sans-serif;
|
||||||
|
margin-bottom: 0.42vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
.text_19 {
|
||||||
|
font-size: 0.73vw;
|
||||||
|
color: #666;
|
||||||
|
font-family: "Microsoft YaHei", Arial, sans-serif;
|
||||||
|
margin-bottom: 0.63vh;
|
||||||
|
line-height: 1.4;
|
||||||
|
display: -webkit-box;
|
||||||
|
-webkit-line-clamp: 2;
|
||||||
|
-webkit-box-orient: vertical;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.group_7 {
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.52vw;
|
||||||
|
}
|
||||||
|
|
||||||
|
.thumbnail_6,
|
||||||
|
.thumbnail_7,
|
||||||
|
.thumbnail_8 {
|
||||||
|
width: 0.83vw;
|
||||||
|
height: 0.83vw;
|
||||||
|
}
|
||||||
|
|
||||||
|
.text_20,
|
||||||
|
.text_21,
|
||||||
|
.text_22 {
|
||||||
|
font-size: 0.68vw;
|
||||||
|
color: #999;
|
||||||
|
font-family: "Microsoft YaHei", Arial, sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
.text-wrapper_2 {
|
||||||
|
margin-left: auto;
|
||||||
|
background: rgba(2, 134, 206, 1);
|
||||||
|
border-radius: 0.31vw;
|
||||||
|
padding: 0.31vh 0.78vw;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.text-wrapper_2:hover {
|
||||||
|
background: rgba(1, 120, 185, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.text_23 {
|
||||||
|
font-size: 0.73vw;
|
||||||
|
color: #fff;
|
||||||
|
font-family: "Microsoft YaHei", Arial, sans-serif;
|
||||||
|
font-weight: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagination-wrapper {
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
margin-top: 4.17vh;
|
||||||
|
padding: 1.04vh 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagination {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.52vw;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagination-item {
|
||||||
|
padding: 0.52vh 0.78vw;
|
||||||
|
font-size: 0.73vw;
|
||||||
|
color: #333;
|
||||||
|
cursor: pointer;
|
||||||
|
border-radius: 0.26vw;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
border: 1px solid #ddd;
|
||||||
|
background: #fff;
|
||||||
|
font-family: "Microsoft YaHei", Arial, sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagination-item:hover {
|
||||||
|
color: rgba(2, 134, 206, 1);
|
||||||
|
border-color: rgba(2, 134, 206, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagination-item.active {
|
||||||
|
background: rgba(2, 134, 206, 1);
|
||||||
|
color: #fff;
|
||||||
|
border-color: rgba(2, 134, 206, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagination-item.disabled {
|
||||||
|
color: #ccc;
|
||||||
|
cursor: not-allowed;
|
||||||
|
border-color: #f0f0f0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagination-item.disabled:hover {
|
||||||
|
color: #ccc;
|
||||||
|
border-color: #f0f0f0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-button {
|
||||||
|
padding: 0.52vh 1.04vw;
|
||||||
|
}
|
||||||
|
|
||||||
|
.page-number {
|
||||||
|
min-width: 2.08vw;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 响应式设计 */
|
||||||
|
@media (max-width: 1024px) {
|
||||||
|
.box_2 {
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.block_4 {
|
||||||
|
width: 100%;
|
||||||
|
margin-right: 0;
|
||||||
|
margin-bottom: 0.83vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
.box_3 {
|
||||||
|
width: 100%;
|
||||||
|
height: 18vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
.text_16 {
|
||||||
|
font-size: 1.25vw;
|
||||||
|
}
|
||||||
|
|
||||||
|
.text_17,
|
||||||
|
.text_18,
|
||||||
|
.text_19 {
|
||||||
|
font-size: 1vw;
|
||||||
|
}
|
||||||
|
|
||||||
|
.text_20,
|
||||||
|
.text_21,
|
||||||
|
.text_22,
|
||||||
|
.text_23 {
|
||||||
|
font-size: 0.9vw;
|
||||||
|
}
|
||||||
|
|
||||||
|
.thumbnail_5,
|
||||||
|
.thumbnail_6,
|
||||||
|
.thumbnail_7,
|
||||||
|
.thumbnail_8 {
|
||||||
|
width: 1.25vw;
|
||||||
|
height: 1.25vw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.text_12,
|
||||||
|
.text_13,
|
||||||
|
.text_14 {
|
||||||
|
font-size: 2.16px;
|
||||||
|
padding: 10px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.course-divider {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagination-wrapper {
|
||||||
|
margin-top: 6vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagination {
|
||||||
|
gap: 2vw;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagination-item {
|
||||||
|
font-size: 3vw;
|
||||||
|
padding: 1vh 2vw;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-button {
|
||||||
|
padding: 1vh 3vw;
|
||||||
|
}
|
||||||
|
|
||||||
|
.page-number {
|
||||||
|
min-width: 8vw;
|
||||||
|
}
|
||||||
|
|
||||||
|
.box_2 {
|
||||||
|
padding: 0.83vh 0.83vw;
|
||||||
|
}
|
||||||
|
|
||||||
|
.block_4 {
|
||||||
|
margin-bottom: 0.63vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
.box_3 {
|
||||||
|
height: 20vh;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 480px) {
|
||||||
|
.text_12,
|
||||||
|
.text_13,
|
||||||
|
.text_14 {
|
||||||
|
font-size: 0.83vw;
|
||||||
|
padding: 0.31vh 0.63vw;
|
||||||
|
}
|
||||||
|
|
||||||
|
.box_2 {
|
||||||
|
padding: 0.63vh 0.63vw;
|
||||||
|
}
|
||||||
|
|
||||||
|
.text_16 {
|
||||||
|
font-size: 0.83vw;
|
||||||
|
}
|
||||||
|
|
||||||
|
.text_19 {
|
||||||
|
font-size: 0.73vw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 通用样式类 */
|
||||||
|
.flex-row {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
}
|
||||||
|
|
||||||
|
.flex-col {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.justify-between {
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 加载状态样式 */
|
||||||
|
.loading-wrapper {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
padding: 2vh 0;
|
||||||
|
font-size: 0.83vw;
|
||||||
|
color: #666;
|
||||||
|
font-family: "Microsoft YaHei", Arial, sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 空状态样式 */
|
||||||
|
.empty-state {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
padding: 4vh 0;
|
||||||
|
font-size: 0.94vw;
|
||||||
|
color: #999;
|
||||||
|
font-family: "Microsoft YaHei", Arial, sans-serif;
|
||||||
|
}
|
||||||
|
</style>
|
@ -93,8 +93,7 @@
|
|||||||
<!-- 右侧内容区域 -->
|
<!-- 右侧内容区域 -->
|
||||||
<div class="group_5 flex-col">
|
<div class="group_5 flex-col">
|
||||||
<!-- 课程内容 -->
|
<!-- 课程内容 -->
|
||||||
<div v-if="isCoursesTab">
|
<!-- <div>
|
||||||
<!-- 课程筛选标签 -->
|
|
||||||
<div class="text-wrapper_1 flex-row">
|
<div class="text-wrapper_1 flex-row">
|
||||||
<span class="text_12" :class="{ active: activeCourseTab === 'all' }"
|
<span class="text_12" :class="{ active: activeCourseTab === 'all' }"
|
||||||
@click="handleCourseTabChange('all')">全部课程</span>
|
@click="handleCourseTabChange('all')">全部课程</span>
|
||||||
@ -104,12 +103,9 @@
|
|||||||
@click="handleCourseTabChange('completed')">已完结</span>
|
@click="handleCourseTabChange('completed')">已完结</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 分割线 -->
|
|
||||||
<div class="course-divider"></div>
|
<div class="course-divider"></div>
|
||||||
|
|
||||||
<!-- 课程列表 -->
|
|
||||||
<div class="course-list">
|
<div class="course-list">
|
||||||
<!-- 学习中的课程 -->
|
|
||||||
<div v-for="course in filteredCourses" :key="course.id" class="box_2 flex-row justify-between">
|
<div v-for="course in filteredCourses" :key="course.id" class="box_2 flex-row justify-between">
|
||||||
<div class="block_4 flex-col">
|
<div class="block_4 flex-col">
|
||||||
<div class="box_3 flex-row justify-between">
|
<div class="box_3 flex-row justify-between">
|
||||||
@ -149,7 +145,6 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 分页器 -->
|
|
||||||
<div class="pagination-wrapper" v-if="totalPages > 1">
|
<div class="pagination-wrapper" v-if="totalPages > 1">
|
||||||
<div class="pagination">
|
<div class="pagination">
|
||||||
<span class="pagination-item nav-button" :class="{ disabled: currentPage === 1 }"
|
<span class="pagination-item nav-button" :class="{ disabled: currentPage === 1 }"
|
||||||
@ -176,7 +171,9 @@
|
|||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div> -->
|
||||||
|
|
||||||
|
<CourseContent v-if="isCoursesTab"></CourseContent>
|
||||||
|
|
||||||
|
|
||||||
<!-- 作业内容 -->
|
<!-- 作业内容 -->
|
||||||
@ -1191,6 +1188,7 @@ import QuillEditor from '@/components/common/QuillEditor.vue'
|
|||||||
import InstantMessage from '@/components/InstantMessage.vue'
|
import InstantMessage from '@/components/InstantMessage.vue'
|
||||||
import { useRouter, useRoute } from 'vue-router'
|
import { useRouter, useRoute } from 'vue-router'
|
||||||
import { MessageApi, type BackendMessageItem } from '@/api'
|
import { MessageApi, type BackendMessageItem } from '@/api'
|
||||||
|
import CourseContent from '@/components/profile/CourseContent.vue'
|
||||||
|
|
||||||
const { t, locale } = useI18n()
|
const { t, locale } = useI18n()
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
|
@ -21,79 +21,44 @@
|
|||||||
|
|
||||||
<!-- 导航栏 -->
|
<!-- 导航栏 -->
|
||||||
<div class="nav-container">
|
<div class="nav-container">
|
||||||
<router-link to="/teacher/course-management" class="nav-item" :class="{ active: activeNavItem === 0 }"
|
<!-- 动态菜单渲染 -->
|
||||||
@click="setActiveNavItem(0)">
|
<template v-for="menu in menuList" :key="menu.id">
|
||||||
<img :src="activeNavItem === 0 ? '/images/teacher/课程管理(选中).png' : '/images/teacher/课程管理.png'" alt="">
|
<!-- 有子菜单的父级菜单 -->
|
||||||
<span>课程管理</span>
|
<div v-if="menu.children && menu.children.length > 0"
|
||||||
</router-link>
|
class="nav-item"
|
||||||
|
:class="{ active: activeNavItem === menu.id }"
|
||||||
<!-- 考试管理 - 可展开菜单 -->
|
@click="toggleMenu(menu)">
|
||||||
<div class="nav-item" :class="{ active: activeNavItem === 4 }" @click="toggleExamMenu">
|
<img :src="getMenuIcon(menu)" :alt="menu.name" v-if="menu.icon">
|
||||||
<img
|
<span>{{ menu.name }}</span>
|
||||||
:src="activeNavItem === 4 ? '/images/teacher/examination-active.png' : '/images/teacher/examination.png'"
|
<n-icon class="expand-icon" :class="{ expanded: isMenuExpanded(menu.id) }">
|
||||||
alt="">
|
|
||||||
<span>考试管理</span>
|
|
||||||
<n-icon class="expand-icon" :class="{ expanded: examMenuExpanded }">
|
|
||||||
<ChevronDownOutline />
|
<ChevronDownOutline />
|
||||||
</n-icon>
|
</n-icon>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 考试管理子菜单 -->
|
<!-- 子菜单容器 -->
|
||||||
<div class="submenu-container" :class="{ expanded: examMenuExpanded }">
|
<div v-if="menu.children && menu.children.length > 0"
|
||||||
<router-link to="/teacher/exam-management/question-bank" class="submenu-item"
|
class="submenu-container"
|
||||||
:class="{ active: activeSubNavItem === 'question-bank' }" @click="setActiveSubNavItem('question-bank')">
|
:class="{ expanded: isMenuExpanded(menu.id) }">
|
||||||
<span>题库管理</span>
|
<router-link v-for="subMenu in menu.children"
|
||||||
</router-link>
|
:key="subMenu.id"
|
||||||
<router-link to="/teacher/exam-management/exam-library" class="submenu-item"
|
:to="subMenu.path"
|
||||||
:class="{ active: activeSubNavItem === 'exam-library' }" @click="setActiveSubNavItem('exam-library')">
|
class="submenu-item"
|
||||||
<span>试卷管理</span>
|
:class="{ active: activeSubNavItem === subMenu.id }"
|
||||||
</router-link>
|
@click="handleSubMenuClick(subMenu, menu)">
|
||||||
<router-link to="/teacher/exam-management/marking-center/list" class="submenu-item"
|
<span>{{ subMenu.name }}</span>
|
||||||
:class="{ active: activeSubNavItem === 'marking-center' }" @click="setActiveSubNavItem('marking-center')">
|
|
||||||
<span>阅卷中心</span>
|
|
||||||
</router-link>
|
</router-link>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- 没有子菜单的直接链接 -->
|
||||||
<!-- 学员中心 - 可展开菜单 -->
|
<router-link v-else
|
||||||
<div class="nav-item" :class="{ active: activeNavItem === 1 }"
|
:to="menu.path"
|
||||||
@click="toggleStudentMenu('/teacher/student-management/student-library')">
|
class="nav-item"
|
||||||
<img :src="activeNavItem === 1 ? '/images/teacher/学院管理(选中).png' : '/images/teacher/学员管理.png'" alt="">
|
:class="{ active: activeNavItem === menu.id }"
|
||||||
<span>学员中心</span>
|
@click="toggleMenu(menu)">
|
||||||
<n-icon class="expand-icon" :class="{ expanded: studentMenuExpanded }">
|
<img :src="getMenuIcon(menu)" :alt="menu.name" v-if="menu.icon">
|
||||||
<ChevronDownOutline />
|
<span>{{ menu.name }}</span>
|
||||||
</n-icon>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- 学员中心子菜单 -->
|
|
||||||
<div class="submenu-container" :class="{ expanded: studentMenuExpanded }">
|
|
||||||
<router-link to="/teacher/student-management/student-library" class="submenu-item"
|
|
||||||
:class="{ active: activeSubNavItem === 'student-library' }"
|
|
||||||
@click="setActiveSubNavItem('student-library')">
|
|
||||||
<span>学员库</span>
|
|
||||||
</router-link>
|
|
||||||
<router-link to="/teacher/student-management/class-management" class="submenu-item"
|
|
||||||
:class="{ active: activeSubNavItem === 'class-management' }"
|
|
||||||
@click="setActiveSubNavItem('class-management')">
|
|
||||||
<span>班级管理</span>
|
|
||||||
</router-link>
|
|
||||||
</div>
|
|
||||||
<!-- <router-link to="/teacher/my-resources" class="nav-item" :class="{ active: activeNavItem === 2 }"
|
|
||||||
@click="setActiveNavItem(2)">
|
|
||||||
<img :src="activeNavItem === 2 ? '/images/teacher/我的资源(选中).png' : '/images/teacher/我的资源.png'" alt="">
|
|
||||||
<span>我的资源</span>
|
|
||||||
</router-link> -->
|
|
||||||
<router-link to="/teacher/message-center" class="nav-item" :class="{ active: activeNavItem === 5 }"
|
|
||||||
@click="setActiveNavItem(5)">
|
|
||||||
<img :src="activeNavItem === 5 ? '/images/profile/message-active.png' : '/images/profile/message.png'"
|
|
||||||
alt="">
|
|
||||||
<span>消息中心</span>
|
|
||||||
</router-link>
|
|
||||||
<router-link to="/teacher/personal-center" class="nav-item" :class="{ active: activeNavItem === 3 }"
|
|
||||||
@click="setActiveNavItem(3)">
|
|
||||||
<img :src="activeNavItem === 3 ? '/images/teacher/个人中心(选中).png' : '/images/teacher/个人中心.png'" alt="">
|
|
||||||
<span>个人中心</span>
|
|
||||||
</router-link>
|
</router-link>
|
||||||
|
</template>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- ai助教 - 已注释 -->
|
<!-- ai助教 - 已注释 -->
|
||||||
@ -188,6 +153,7 @@ import { useRoute, useRouter } from 'vue-router'
|
|||||||
import { ChevronDownOutline } from '@vicons/ionicons5'
|
import { ChevronDownOutline } from '@vicons/ionicons5'
|
||||||
import { useUserStore } from '@/stores/user'
|
import { useUserStore } from '@/stores/user'
|
||||||
import { ChevronBackSharp } from '@vicons/ionicons5'
|
import { ChevronBackSharp } from '@vicons/ionicons5'
|
||||||
|
import { TeachCourseApi } from '@/api/modules/teachCourse'
|
||||||
|
|
||||||
const userStore = useUserStore()
|
const userStore = useUserStore()
|
||||||
|
|
||||||
@ -197,11 +163,9 @@ const height = window.innerHeight;
|
|||||||
console.log(`当前屏幕宽度: ${width}px, 高度: ${height}px`);
|
console.log(`当前屏幕宽度: ${width}px, 高度: ${height}px`);
|
||||||
|
|
||||||
// 添加导航项激活状态管理
|
// 添加导航项激活状态管理
|
||||||
const activeNavItem = ref(0); // 0: 课程管理, 1: 学员管理, 2: 我的资源, 3: 个人中心, 4: 考试管理, 5: 消息中心, 6: 智能体编排
|
const activeNavItem = ref(''); // 当前激活的菜单ID
|
||||||
const activeSubNavItem = ref(''); // 子菜单激活状态
|
const activeSubNavItem = ref(''); // 子菜单激活状态
|
||||||
const examMenuExpanded = ref(false); // 考试管理菜单展开状态
|
const expandedMenus = ref(new Set()); // 展开的菜单ID集合
|
||||||
const studentMenuExpanded = ref(false); // 学员中心菜单展开状态
|
|
||||||
const orchestrationMenuExpanded = ref(false); // 智能体编排菜单展开状态
|
|
||||||
const showTopImage = ref(false); // 控制顶部图片显示/隐藏
|
const showTopImage = ref(false); // 控制顶部图片显示/隐藏
|
||||||
|
|
||||||
// 需要隐藏顶部图片的路由路径数组
|
// 需要隐藏顶部图片的路由路径数组
|
||||||
@ -227,96 +191,77 @@ const breadcrumbDisplay = computed(() => {
|
|||||||
|
|
||||||
const isCourseEditor = computed(() => route.path.includes('course-editor') || route.path.includes('chapter-editor-teacher'));
|
const isCourseEditor = computed(() => route.path.includes('course-editor') || route.path.includes('chapter-editor-teacher'));
|
||||||
|
|
||||||
const setActiveNavItem = (index: number) => {
|
// 检查菜单是否展开
|
||||||
activeNavItem.value = index;
|
const isMenuExpanded = (menuId) => {
|
||||||
// 如果不是考试管理,关闭考试管理子菜单
|
return expandedMenus.value.has(menuId)
|
||||||
if (index !== 4) {
|
|
||||||
examMenuExpanded.value = false;
|
|
||||||
}
|
}
|
||||||
// 如果不是学员中心,关闭学员中心子菜单
|
|
||||||
if (index !== 1) {
|
// 切换菜单展开状态
|
||||||
studentMenuExpanded.value = false;
|
const toggleMenu = (menu) => {
|
||||||
}
|
if (menu.children && menu.children.length > 0) {
|
||||||
// 如果不是智能体编排,关闭智能体编排子菜单
|
if (expandedMenus.value.has(menu.id)) {
|
||||||
if (index !== 6) {
|
expandedMenus.value.delete(menu.id)
|
||||||
orchestrationMenuExpanded.value = false;
|
} else {
|
||||||
}
|
expandedMenus.value.add(menu.id)
|
||||||
// 如果切换到其他菜单,清空子菜单选中状态
|
|
||||||
if (index !== 4 && index !== 1 && index !== 6) {
|
|
||||||
activeSubNavItem.value = '';
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 考试管理菜单切换
|
// 设置当前菜单为激活状态
|
||||||
const toggleExamMenu = () => {
|
activeNavItem.value = menu.id
|
||||||
// 如果当前菜单要展开,先关闭其他菜单
|
|
||||||
if (!examMenuExpanded.value) {
|
|
||||||
studentMenuExpanded.value = false;
|
|
||||||
orchestrationMenuExpanded.value = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
examMenuExpanded.value = !examMenuExpanded.value;
|
// 如果有路径且没有子菜单,则导航
|
||||||
activeNavItem.value = 4;
|
if (menu.path && (!menu.children || menu.children.length === 0)) {
|
||||||
|
router.push(menu.path)
|
||||||
// 如果展开且没有选中子菜单,默认选中第一个
|
|
||||||
if (examMenuExpanded.value && !activeSubNavItem.value) {
|
|
||||||
activeSubNavItem.value = 'question-management';
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 学员中心菜单切换
|
// 处理子菜单点击
|
||||||
const toggleStudentMenu = (path: string) => {
|
const handleSubMenuClick = (subMenu, parentMenu) => {
|
||||||
// 如果当前菜单要展开,先关闭其他菜单
|
activeSubNavItem.value = subMenu.id
|
||||||
if (!studentMenuExpanded.value) {
|
activeNavItem.value = parentMenu.id
|
||||||
examMenuExpanded.value = false;
|
if (subMenu.path) {
|
||||||
orchestrationMenuExpanded.value = false;
|
router.push(subMenu.path)
|
||||||
}
|
|
||||||
|
|
||||||
studentMenuExpanded.value = !studentMenuExpanded.value;
|
|
||||||
activeNavItem.value = 1;
|
|
||||||
|
|
||||||
// 如果展开且没有选中子菜单,默认选中第一个
|
|
||||||
if (studentMenuExpanded.value && !activeSubNavItem.value) {
|
|
||||||
activeSubNavItem.value = 'student-library';
|
|
||||||
router.push(path);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 智能体编排菜单切换
|
// 获取菜单图标,处理激活状态
|
||||||
const toggleOrchestrationMenu = () => {
|
const getMenuIcon = (menu) => {
|
||||||
// 如果当前菜单要展开,先关闭其他菜单
|
if (!menu.icon) return null
|
||||||
if (!orchestrationMenuExpanded.value) {
|
|
||||||
examMenuExpanded.value = false;
|
// 如果是激活状态且有对应的激活图标
|
||||||
studentMenuExpanded.value = false;
|
if (activeNavItem.value === menu.id) {
|
||||||
|
return menu.icon.replace('.png', '-active.png')
|
||||||
}
|
}
|
||||||
|
|
||||||
orchestrationMenuExpanded.value = !orchestrationMenuExpanded.value;
|
return menu.icon
|
||||||
activeNavItem.value = 6;
|
|
||||||
|
|
||||||
// 如果展开且没有选中子菜单,默认选中第一个
|
|
||||||
if (orchestrationMenuExpanded.value && !activeSubNavItem.value) {
|
|
||||||
activeSubNavItem.value = 'app-management';
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 设置子菜单激活状态
|
// 更新激活的导航项
|
||||||
const setActiveSubNavItem = (subItem: string) => {
|
const updateActiveNavItem = () => {
|
||||||
activeSubNavItem.value = subItem;
|
const path = route.path;
|
||||||
|
console.log('当前路径:', path);
|
||||||
|
|
||||||
// 根据子菜单项判断属于哪个主菜单
|
// 遍历菜单找到匹配的路径
|
||||||
if (subItem === 'question-bank' || subItem === 'exam-library' || subItem === 'marking-center') {
|
const findActiveMenu = (menus, parentId = null) => {
|
||||||
// 考试管理子菜单
|
for (const menu of menus) {
|
||||||
activeNavItem.value = 4;
|
if (menu.path && path.includes(menu.path.replace('/teacher/', ''))) {
|
||||||
examMenuExpanded.value = true;
|
activeNavItem.value = parentId || menu.id
|
||||||
} else if (subItem === 'student-library' || subItem === 'class-management') {
|
if (parentId) {
|
||||||
// 学员中心子菜单
|
activeSubNavItem.value = menu.id
|
||||||
activeNavItem.value = 1;
|
expandedMenus.value.add(parentId)
|
||||||
studentMenuExpanded.value = true;
|
|
||||||
} else if (subItem === 'app-management' || subItem === 'knowledge-base' || subItem === 'process-design' || subItem === 'model-config' || subItem === 'ocr-recognition') {
|
|
||||||
// 智能体编排子菜单
|
|
||||||
activeNavItem.value = 6;
|
|
||||||
orchestrationMenuExpanded.value = true;
|
|
||||||
}
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if (menu.children && menu.children.length > 0) {
|
||||||
|
if (findActiveMenu(menu.children, menu.id)) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
findActiveMenu(menuList.value)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 处理面包屑点击
|
// 处理面包屑点击
|
||||||
@ -343,6 +288,50 @@ const hideSidebar = computed(() => {
|
|||||||
return currentPath.includes('course-editor') || currentPath.includes('chapter-editor-teacher') || currentPath.includes('certificate')
|
return currentPath.includes('course-editor') || currentPath.includes('chapter-editor-teacher') || currentPath.includes('certificate')
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// 定义菜单数据
|
||||||
|
const menuList = ref([])
|
||||||
|
|
||||||
|
// 处理菜单数据,构建层级关系
|
||||||
|
const processMenuData = (menuData) => {
|
||||||
|
const menuMap = new Map()
|
||||||
|
const rootMenus = []
|
||||||
|
|
||||||
|
// 先创建所有菜单项的映射
|
||||||
|
menuData.forEach(menu => {
|
||||||
|
menuMap.set(menu.id, {
|
||||||
|
...menu,
|
||||||
|
children: []
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
// 构建层级关系
|
||||||
|
menuData.forEach(menu => {
|
||||||
|
if (menu.parentId === null) {
|
||||||
|
// 根级菜单
|
||||||
|
rootMenus.push(menuMap.get(menu.id))
|
||||||
|
} else {
|
||||||
|
// 子菜单
|
||||||
|
const parent = menuMap.get(menu.parentId)
|
||||||
|
if (parent) {
|
||||||
|
parent.children.push(menuMap.get(menu.id))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// 按sortOrder排序
|
||||||
|
const sortMenus = (menus) => {
|
||||||
|
menus.sort((a, b) => b.sortOrder - a.sortOrder)
|
||||||
|
menus.forEach(menu => {
|
||||||
|
if (menu.children.length > 0) {
|
||||||
|
sortMenus(menu.children)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
sortMenus(rootMenus)
|
||||||
|
return rootMenus
|
||||||
|
}
|
||||||
|
|
||||||
// 动态生成面包屑路径项(右侧部分)
|
// 动态生成面包屑路径项(右侧部分)
|
||||||
const breadcrumbPathItems = computed(() => {
|
const breadcrumbPathItems = computed(() => {
|
||||||
const currentPath = route.path;
|
const currentPath = route.path;
|
||||||
@ -803,7 +792,20 @@ const breadcrumbPathItems = computed(() => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// 监听路由变化,更新激活的导航项
|
// 监听路由变化,更新激活的导航项
|
||||||
onMounted(() => {
|
onMounted(async () => {
|
||||||
|
try {
|
||||||
|
// 获取菜单数据
|
||||||
|
const menuResponse = await TeachCourseApi.getTeacherMenuList()
|
||||||
|
const rawMenuData = menuResponse.data.result
|
||||||
|
console.log('原始菜单数据:', rawMenuData);
|
||||||
|
|
||||||
|
// 处理菜单数据
|
||||||
|
menuList.value = processMenuData(rawMenuData)
|
||||||
|
console.log('处理后的菜单数据:', menuList.value);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取菜单数据失败:', error)
|
||||||
|
}
|
||||||
|
|
||||||
// 初始设置
|
// 初始设置
|
||||||
updateActiveNavItem();
|
updateActiveNavItem();
|
||||||
|
|
||||||
@ -840,68 +842,6 @@ const updateTopImageVisibility = () => {
|
|||||||
console.log('顶部图片显示状态:', showTopImage.value);
|
console.log('顶部图片显示状态:', showTopImage.value);
|
||||||
};
|
};
|
||||||
|
|
||||||
// 更新激活的导航项
|
|
||||||
const updateActiveNavItem = () => {
|
|
||||||
const path = route.path;
|
|
||||||
console.log('当前路径:', path); // 添加调试信息
|
|
||||||
if (path.includes('course-management')) {
|
|
||||||
activeNavItem.value = 0; // 课程管理
|
|
||||||
} else if (path.includes('student-management')) {
|
|
||||||
activeNavItem.value = 1; // 学员管理
|
|
||||||
studentMenuExpanded.value = true;
|
|
||||||
|
|
||||||
// 根据路径设置子菜单激活状态
|
|
||||||
if (path.includes('student-library')) {
|
|
||||||
activeSubNavItem.value = 'student-library';
|
|
||||||
} else if (path.includes('class-management')) {
|
|
||||||
activeSubNavItem.value = 'class-management';
|
|
||||||
}
|
|
||||||
} else if (path.includes('my-resources')) {
|
|
||||||
activeNavItem.value = 2; // 我的资源
|
|
||||||
} else if (path.includes('personal-center')) {
|
|
||||||
activeNavItem.value = 3; // 个人中心
|
|
||||||
} else if (path.includes('exam-management')) {
|
|
||||||
activeNavItem.value = 4; // 考试管理
|
|
||||||
examMenuExpanded.value = true;
|
|
||||||
|
|
||||||
const arr = ['question-bank', 'exam-library', 'marking-center'];
|
|
||||||
const found = arr.find(item => path.includes(item));
|
|
||||||
activeSubNavItem.value = found || '';
|
|
||||||
} else if (path.includes('message-center')) {
|
|
||||||
activeNavItem.value = 5; // 消息中心
|
|
||||||
} else if (path.includes('ai-assistant')) {
|
|
||||||
// AI助教页面,清空所有导航项选中状态
|
|
||||||
console.log('检测到AI助教页面,清空导航选中状态');
|
|
||||||
activeNavItem.value = -1;
|
|
||||||
activeSubNavItem.value = '';
|
|
||||||
examMenuExpanded.value = false;
|
|
||||||
studentMenuExpanded.value = false;
|
|
||||||
orchestrationMenuExpanded.value = false;
|
|
||||||
} else if (path.includes('airag')) {
|
|
||||||
// 智能体编排相关页面
|
|
||||||
activeNavItem.value = 6; // 智能体编排
|
|
||||||
orchestrationMenuExpanded.value = true;
|
|
||||||
|
|
||||||
// 根据路径设置子菜单激活状态
|
|
||||||
if (path.includes('aiapp')) {
|
|
||||||
activeSubNavItem.value = 'app-management';
|
|
||||||
} else if (path.includes('aiknowledge')) {
|
|
||||||
activeSubNavItem.value = 'knowledge-base';
|
|
||||||
} else if (path.includes('aiflow')) {
|
|
||||||
activeSubNavItem.value = 'process-design';
|
|
||||||
} else if (path.includes('aimodel')) {
|
|
||||||
activeSubNavItem.value = 'model-config';
|
|
||||||
} else if (path.includes('ocr')) {
|
|
||||||
activeSubNavItem.value = 'ocr-recognition';
|
|
||||||
}
|
|
||||||
} else if (path.includes('/teacher/ai/app')) {
|
|
||||||
// AI应用管理页面
|
|
||||||
activeNavItem.value = 6; // 智能体编排
|
|
||||||
orchestrationMenuExpanded.value = true;
|
|
||||||
activeSubNavItem.value = 'app-management';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|