Merge branch 'dev' of http://110.42.96.65:19890/GoCo/OL-LearnPlatform-Frontend into dev
2382
pnpm-lock.yaml
generated
BIN
public/images/teacher/下载-选中.png
Normal file
After Width: | Height: | Size: 452 B |
BIN
public/images/teacher/下载.png
Normal file
After Width: | Height: | Size: 473 B |
Before Width: | Height: | Size: 683 B |
Before Width: | Height: | Size: 1.2 KiB |
Before Width: | Height: | Size: 719 B |
Before Width: | Height: | Size: 1.2 KiB |
Before Width: | Height: | Size: 524 B |
Before Width: | Height: | Size: 400 B |
Before Width: | Height: | Size: 416 B |
Before Width: | Height: | Size: 359 B After Width: | Height: | Size: 358 B |
Before Width: | Height: | Size: 504 B After Width: | Height: | Size: 537 B |
Before Width: | Height: | Size: 494 B After Width: | Height: | Size: 505 B |
Before Width: | Height: | Size: 476 B After Width: | Height: | Size: 427 B |
Before Width: | Height: | Size: 578 B After Width: | Height: | Size: 488 B |
Before Width: | Height: | Size: 387 B After Width: | Height: | Size: 359 B |
Before Width: | Height: | Size: 578 B After Width: | Height: | Size: 479 B |
BIN
public/images/teacher/权限设置-选中.png
Normal file
After Width: | Height: | Size: 662 B |
BIN
public/images/teacher/权限设置.png
Normal file
After Width: | Height: | Size: 729 B |
BIN
public/images/teacher/置顶-选中.png
Normal file
After Width: | Height: | Size: 354 B |
BIN
public/images/teacher/置顶.png
Normal file
After Width: | Height: | Size: 356 B |
Before Width: | Height: | Size: 712 B |
Before Width: | Height: | Size: 332 B |
BIN
public/images/teacher/重命名-选中.png
Normal file
After Width: | Height: | Size: 406 B |
BIN
public/images/teacher/重命名.png
Normal file
After Width: | Height: | Size: 420 B |
@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<div class="course-category">
|
||||
<!-- 顶部 -->
|
||||
<div class="top">
|
||||
<div class="top">
|
||||
<div class="nav-links">
|
||||
<a href="" class="active">全部</a>
|
||||
<a href="">发布中</a>
|
||||
@ -16,61 +16,64 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- 主体 -->
|
||||
<div class="course-container">
|
||||
<!-- 主体 -->
|
||||
<div class="course-container">
|
||||
<div class="course-grid">
|
||||
<div class="course-card" v-for="course in courseList" :key="course.id" @click="navigateToCourseDetail(course.id)">
|
||||
<div class="course-image-container">
|
||||
<div class="section-title" :class="{'offline': course.status === '下架中'}">{{ course.status }}</div>
|
||||
<div class="more-options">
|
||||
<span class="more-icon">⋮</span>
|
||||
<div class="options-menu">
|
||||
<template v-if="course.status === '发布中'">
|
||||
<a href="#" class="option-item"><img src="/images/teacher/下架.png" alt="">下架</a>
|
||||
<a href="#" class="option-item"><img src="/images/teacher/小编辑.png" alt="">编辑</a>
|
||||
<a href="#" class="option-item"><img src="/images/teacher/移动.png" alt="">移动</a>
|
||||
<a href="#" class="option-item"><img src="/images/teacher/删除.png" alt="">删除</a>
|
||||
</template>
|
||||
<template v-else-if="course.status === '下架中'">
|
||||
<a href="#" class="option-item"><img src="/images/teacher/加号.png" alt="">发布</a>
|
||||
<a href="#" class="option-item"><img src="/images/teacher/小编辑.png" alt="">编辑</a>
|
||||
<a href="#" class="option-item"><img src="/images/teacher/移动.png" alt="">移动</a>
|
||||
<a href="#" class="option-item"><img src="/images/teacher/删除.png" alt="">删除</a>
|
||||
</template>
|
||||
</div>
|
||||
<div class="course-card" v-for="course in courseList" :key="course.id">
|
||||
<div class="course-image-container">
|
||||
<div class="section-title" :class="{ 'offline': course.status === '下架中' }">{{ course.status }}
|
||||
</div>
|
||||
<div class="more-options">
|
||||
<span class="more-icon">⋮</span>
|
||||
<div class="options-menu">
|
||||
<template v-if="course.status === '发布中'">
|
||||
<a href="#" class="option-item"><img src="/images/teacher/下架.png" alt="">下架</a>
|
||||
<a href="#" class="option-item" @click.prevent="editCourse(course.id)"><img
|
||||
src="/images/teacher/小编辑.png" alt="">编辑</a>
|
||||
<a href="#" class="option-item"><img src="/images/teacher/移动.png" alt="">移动</a>
|
||||
<a href="#" class="option-item"><img src="/images/teacher/删除.png" alt="">删除</a>
|
||||
</template>
|
||||
<template v-else-if="course.status === '下架中'">
|
||||
<a href="#" class="option-item"><img src="/images/teacher/加号.png" alt="">发布</a>
|
||||
<a href="#" class="option-item" @click.prevent="editCourse(course.id)"><img
|
||||
src="/images/teacher/小编辑.png" alt="">编辑</a>
|
||||
<a href="#" class="option-item"><img src="/images/teacher/移动.png" alt="">移动</a>
|
||||
<a href="#" class="option-item"><img src="/images/teacher/删除.png" alt="">删除</a>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="course-info">
|
||||
<img :src="course.image" alt="">
|
||||
<a href="#" class="course-name">{{ course.name }}</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="course-info">
|
||||
<img :src="course.image" alt="">
|
||||
<a href="#" class="course-name">{{ course.name }}</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- 底部翻页按钮 -->
|
||||
<div class="pagination">
|
||||
<!-- 底部翻页按钮 -->
|
||||
<div class="pagination">
|
||||
<div class="pagination-content">
|
||||
<div class="page-numbers">
|
||||
<a href="#" class="page-number">首页</a>
|
||||
<a href="#" class="page-number">上一页</a>
|
||||
<a href="#" class="page-number page-number-bordered active">1</a>
|
||||
<a href="#" class="page-number page-number-bordered">2</a>
|
||||
<a href="#" class="page-number page-number-bordered">3</a>
|
||||
<a href="#" class="page-number page-number-bordered">4</a>
|
||||
<a href="#" class="page-number page-number-bordered">5</a>
|
||||
<a href="#" class="page-number">...</a>
|
||||
<a href="#" class="page-number page-number-bordered">29</a>
|
||||
<a href="#" class="page-number page-number-bordered">下一页</a>
|
||||
<a href="#" class="page-number">下一页</a>
|
||||
<a href="#" class="page-number">尾页</a>
|
||||
</div>
|
||||
<div class="page-numbers">
|
||||
<a href="#" class="page-number">首页</a>
|
||||
<a href="#" class="page-number">上一页</a>
|
||||
<a href="#" class="page-number page-number-bordered active">1</a>
|
||||
<a href="#" class="page-number page-number-bordered">2</a>
|
||||
<a href="#" class="page-number page-number-bordered">3</a>
|
||||
<a href="#" class="page-number page-number-bordered">4</a>
|
||||
<a href="#" class="page-number page-number-bordered">5</a>
|
||||
<a href="#" class="page-number">...</a>
|
||||
<a href="#" class="page-number page-number-bordered">29</a>
|
||||
<a href="#" class="page-number page-number-bordered">下一页</a>
|
||||
<a href="#" class="page-number">下一页</a>
|
||||
<a href="#" class="page-number">尾页</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@ -78,32 +81,33 @@
|
||||
import { ref } from 'vue';
|
||||
import { useRouter } from 'vue-router';
|
||||
|
||||
// 路由跳转
|
||||
const router = useRouter();
|
||||
const navigateToCreateCourse = () => {
|
||||
router.push('/teacher/course-create');
|
||||
}
|
||||
|
||||
// 跳转到课程详情页
|
||||
const navigateToCourseDetail = (courseId: number) => {
|
||||
router.push(`/teacher/course-detail?id=${courseId}`);
|
||||
}
|
||||
|
||||
// 模拟课程数据
|
||||
const courseList = ref([
|
||||
{ id: 1, name: '前端开发基础课程', status: '发布中', image: 'https://picsum.photos/200/200?random=1' },
|
||||
{ id: 2, name: 'Vue.js 实战教程', status: '发布中', image: 'https://picsum.photos/200/200?random=2' },
|
||||
{ id: 3, name: 'React 入门到精通', status: '发布中', image: 'https://picsum.photos/200/200?random=3' },
|
||||
{ id: 4, name: 'Node.js 后端开发', status: '下架中', image: 'https://picsum.photos/200/200?random=4' },
|
||||
{ id: 5, name: 'TypeScript 高级教程', status: '发布中', image: 'https://picsum.photos/200/200?random=5' },
|
||||
{ id: 6, name: 'JavaScript 设计模式', status: '发布中', image: 'https://picsum.photos/200/200?random=6' },
|
||||
{ id: 7, name: 'CSS 动画与特效', status: '下架中', image: 'https://picsum.photos/200/200?random=7' },
|
||||
{ id: 8, name: 'HTML5 新特性详解', status: '发布中', image: 'https://picsum.photos/200/200?random=8' },
|
||||
{ id: 9, name: 'Web 性能优化指南', status: '发布中', image: 'https://picsum.photos/200/200?random=9' },
|
||||
{ id: 10, name: '移动端适配实战', status: '发布中', image: 'https://picsum.photos/200/200?random=10' },
|
||||
{ id: 11, name: '微信小程序开发', status: '下架中', image: 'https://picsum.photos/200/200?random=11' },
|
||||
{ id: 12, name: 'Flutter 跨平台开发', status: '发布中', image: 'https://picsum.photos/200/200?random=12' },
|
||||
{ id: 1, name: '前端开发基础课程', status: '发布中', image: 'https://picsum.photos/200/200?random=1' },
|
||||
{ id: 2, name: 'Vue.js 实战教程', status: '发布中', image: 'https://picsum.photos/200/200?random=2' },
|
||||
{ id: 3, name: 'React 入门到精通', status: '发布中', image: 'https://picsum.photos/200/200?random=3' },
|
||||
{ id: 4, name: 'Node.js 后端开发', status: '下架中', image: 'https://picsum.photos/200/200?random=4' },
|
||||
{ id: 5, name: 'TypeScript 高级教程', status: '发布中', image: 'https://picsum.photos/200/200?random=5' },
|
||||
{ id: 6, name: 'JavaScript 设计模式', status: '发布中', image: 'https://picsum.photos/200/200?random=6' },
|
||||
{ id: 7, name: 'CSS 动画与特效', status: '下架中', image: 'https://picsum.photos/200/200?random=7' },
|
||||
{ id: 8, name: 'HTML5 新特性详解', status: '发布中', image: 'https://picsum.photos/200/200?random=8' },
|
||||
{ id: 9, name: 'Web 性能优化指南', status: '发布中', image: 'https://picsum.photos/200/200?random=9' },
|
||||
{ id: 10, name: '移动端适配实战', status: '发布中', image: 'https://picsum.photos/200/200?random=10' },
|
||||
{ id: 11, name: '微信小程序开发', status: '下架中', image: 'https://picsum.photos/200/200?random=11' },
|
||||
{ id: 12, name: 'Flutter 跨平台开发', status: '发布中', image: 'https://picsum.photos/200/200?random=12' },
|
||||
]);
|
||||
|
||||
// 编辑课程
|
||||
const editCourse = (courseId: number) => {
|
||||
router.push(`/teacher/course-editor/${courseId}`);
|
||||
};
|
||||
|
||||
// 跳转到创建课程页面
|
||||
const navigateToCreateCourse = () => {
|
||||
router.push('/teacher/course-create');
|
||||
};
|
||||
</script>
|
||||
|
||||
|
||||
|
@ -1,14 +1,511 @@
|
||||
<template>
|
||||
<div>
|
||||
<h1>课程创建</h1>
|
||||
<div class="page">
|
||||
<div class="course-form-container flex-col">
|
||||
<!-- 第一行:课程名称和课程分类 -->
|
||||
<div class="form-row-name-category flex-row">
|
||||
<div class="label-required">
|
||||
<span class="text_17">*</span>
|
||||
<span class="text_18">课程名称:</span>
|
||||
</div>
|
||||
<div class="course-name-input-wrapper flex-col">
|
||||
<n-input
|
||||
v-model:value="formData.courseName"
|
||||
placeholder="请输入课程名称"
|
||||
class="form-input"
|
||||
:bordered="false"
|
||||
/>
|
||||
</div>
|
||||
<div class="category-section flex-col">
|
||||
<div class="label-category">
|
||||
<span class="text_20">*</span>
|
||||
<span class="text_21">课程分类:</span>
|
||||
</div>
|
||||
<div class="course-category-select-wrapper flex-col">
|
||||
<n-select
|
||||
v-model:value="formData.courseCategory"
|
||||
placeholder="分类名称"
|
||||
:options="categoryOptions"
|
||||
class="form-select"
|
||||
:bordered="false"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 第二行:主讲老师和排序 -->
|
||||
<div class="form-row-instructor flex-row">
|
||||
<div class="label-instructor">
|
||||
<span class="text_23">*</span>
|
||||
<span class="text_24">主讲老师:</span>
|
||||
</div>
|
||||
<div class="form-select-wrapper flex-row">
|
||||
<n-select
|
||||
v-model:value="formData.instructors"
|
||||
multiple
|
||||
placeholder="请选择主讲老师"
|
||||
:options="instructorOptions"
|
||||
class="form-select"
|
||||
:bordered="false"
|
||||
:render-tag="renderInstructorTag"
|
||||
/>
|
||||
</div>
|
||||
<div class="sort-section flex-col">
|
||||
<div class="label-sort">
|
||||
<span class="text_28">*</span>
|
||||
<span class="text_29">排序:</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 排序输入框 -->
|
||||
<div class="form-input-wrapper">
|
||||
<n-input
|
||||
v-model:value="formData.sort"
|
||||
placeholder="数字越小越排序靠前"
|
||||
class="form-input"
|
||||
:bordered="false"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- 第三行:课程开始时间和结束时间 -->
|
||||
<div class="form-row-date flex-row">
|
||||
<div class="label-date">
|
||||
<span class="text_30">*</span>
|
||||
<span class="text_31">课程开始时间:</span>
|
||||
</div>
|
||||
<div class="start-date-wrapper flex-row">
|
||||
<n-date-picker
|
||||
v-model:value="formData.startTime"
|
||||
type="datetime"
|
||||
placeholder="选择时间"
|
||||
class="form-datepicker"
|
||||
:bordered="false"
|
||||
/>
|
||||
</div>
|
||||
<div class="label-end-date">
|
||||
<span class="text_33">*</span>
|
||||
<span class="text_34">课程结束时间:</span>
|
||||
</div>
|
||||
<div class="end-date-wrapper flex-row">
|
||||
<n-date-picker
|
||||
v-model:value="formData.endTime"
|
||||
type="datetime"
|
||||
placeholder="选择时间"
|
||||
class="form-datepicker"
|
||||
:bordered="false"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 第四行:参与学员 -->
|
||||
<div class="form-row-students flex-row">
|
||||
<div class="label-students">
|
||||
<span class="text_36">*</span>
|
||||
<span class="text_37">参与学员:</span>
|
||||
</div>
|
||||
<div class="radio-group flex-row">
|
||||
<label class="radio-option" @click="formData.studentType = 'all'">
|
||||
<div class="radio-circle" :class="{ active: formData.studentType === 'all' }">
|
||||
<div class="radio-dot" v-if="formData.studentType === 'all'"></div>
|
||||
</div>
|
||||
<span class="text_38">全部学员</span>
|
||||
</label>
|
||||
<label class="radio-option" @click="formData.studentType = 'partial'">
|
||||
<div class="radio-circle" :class="{ active: formData.studentType === 'partial' }">
|
||||
<div class="radio-dot" v-if="formData.studentType === 'partial'"></div>
|
||||
</div>
|
||||
<span class="text_39">仅部分学员</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 第五行:选择班级(仅在选择部分学员时显示) -->
|
||||
<div class="form-row-class" v-if="formData.studentType === 'partial'">
|
||||
<div class="form-select-wrapper flex-row">
|
||||
<n-select
|
||||
v-model:value="formData.selectedClasses"
|
||||
multiple
|
||||
placeholder="选择班级(可多选)"
|
||||
:options="classOptions"
|
||||
class="form-select"
|
||||
:bordered="false"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 第六行:课程封面 -->
|
||||
<div class="form-row-cover flex-row justify-between">
|
||||
<div class="label-cover">
|
||||
<span class="text_41">*</span>
|
||||
<span class="text_42">课程封面:</span>
|
||||
</div>
|
||||
<div class="cover-upload-box flex-row">
|
||||
<div class="image-text_6 flex-col justify-between" style="width: 100%; height: 100%; display: flex; align-items: center; justify-content: center;">
|
||||
<div class="native-upload">
|
||||
<input
|
||||
type="file"
|
||||
id="coverUpload"
|
||||
accept="image/*"
|
||||
@change="handleNativeFileChange"
|
||||
style="display: none;"
|
||||
/>
|
||||
<label for="coverUpload" class="upload-content" style="cursor: pointer; width: 100%; height: 100%; display: flex; flex-direction: column; align-items: center; justify-content: center;">
|
||||
<template v-if="!previewUrl">
|
||||
<div class="upload-plus-icon" style="font-size: 32px; display: block; text-align: center;">+</div>
|
||||
<span class="upload-text" style="display: block; text-align: center;">上传</span>
|
||||
</template>
|
||||
<div v-if="previewUrl" style="position: relative; width: 100%; height: 100%;">
|
||||
<img :src="previewUrl" style="max-width: 100%; max-height: 100%; object-fit: contain;" />
|
||||
<div
|
||||
class="delete-icon"
|
||||
@click.stop="clearUpload"
|
||||
style="position: absolute; top: 5px; right: 5px; background-color: rgba(0,0,0,0.5); color: white; width: 20px; height: 20px; border-radius: 50%; display: flex; align-items: center; justify-content: center; cursor: pointer;"
|
||||
>
|
||||
×
|
||||
</div>
|
||||
</div>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 课程简介标题和工具栏 -->
|
||||
<div class="form-row-description flex-row">
|
||||
<div class="label-description">
|
||||
<span class="text_43">*</span>
|
||||
<span class="text_44">课程简介:</span>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<!-- 富文本编辑器区域 -->
|
||||
<div class="editor-container flex-col">
|
||||
<QuillEditor
|
||||
v-model="formData.courseDescription"
|
||||
:height="'280px'"
|
||||
placeholder="请输入内容…"
|
||||
class="rich-text-editor"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- 设置选项区域 -->
|
||||
<div class="form-settings">
|
||||
<!-- 离开页面停止播放 -->
|
||||
<div class="form-row-settings flex-row">
|
||||
<div class="settings-label flex-row justify-between">
|
||||
<div class="label-settings">
|
||||
<span class="text_47">*</span>
|
||||
<span class="text_48">离开页面停止播放:</span>
|
||||
</div>
|
||||
<button
|
||||
@click="formData.stopOnLeave = !formData.stopOnLeave"
|
||||
class="toggle-button"
|
||||
:class="{ 'toggle-button-active': formData.stopOnLeave }"
|
||||
>
|
||||
{{ formData.stopOnLeave ? '开启' : '关闭' }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 视频倍数播放 -->
|
||||
<div class="form-row-setting1 flex-row">
|
||||
<div class="setting1-label flex-row justify-between">
|
||||
<div class="label-setting1">
|
||||
<span class="text_49">*</span>
|
||||
<span class="text_50">视频倍数播放:</span>
|
||||
</div>
|
||||
<button
|
||||
@click="formData.videoSpeedControl = !formData.videoSpeedControl"
|
||||
class="toggle-button"
|
||||
:class="{ 'toggle-button-active': formData.videoSpeedControl }"
|
||||
>
|
||||
{{ formData.videoSpeedControl ? '开启' : '关闭' }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 显示视频文本 -->
|
||||
<div class="form-row-setting2 flex-row">
|
||||
<div class="setting2-label flex-row justify-between">
|
||||
<div class="label-setting2">
|
||||
<span class="text_51">*</span>
|
||||
<span class="text_52">显示视频文本:</span>
|
||||
</div>
|
||||
<button
|
||||
@click="formData.showVideoText = !formData.showVideoText"
|
||||
class="toggle-button"
|
||||
:class="{ 'toggle-button-active': formData.showVideoText }"
|
||||
>
|
||||
{{ formData.showVideoText ? '开启' : '关闭' }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 积分设置 -->
|
||||
<div class="form-row-points flex-row">
|
||||
<div class="points-label flex-row justify-between">
|
||||
<div class="label-points">
|
||||
<span class="text_53">*</span>
|
||||
<span class="text_54">积分设置:</span>
|
||||
</div>
|
||||
<button
|
||||
@click="formData.pointsEnabled = !formData.pointsEnabled"
|
||||
class="toggle-button"
|
||||
:class="{ 'toggle-button-active': formData.pointsEnabled }"
|
||||
>
|
||||
{{ formData.pointsEnabled ? '开启' : '关闭' }}
|
||||
</button>
|
||||
</div>
|
||||
<span class="text_55">学完可获得</span>
|
||||
<div class="form-input-container">
|
||||
<input
|
||||
type="number"
|
||||
v-model="formData.earnPoints"
|
||||
min="0"
|
||||
max="1000"
|
||||
class="native-input"
|
||||
/>
|
||||
</div>
|
||||
<span class="text_57">积分</span>
|
||||
<span class="text_58">本课程需</span>
|
||||
<div class="points-input-wrapper">
|
||||
<input
|
||||
type="number"
|
||||
v-model="formData.requiredPoints"
|
||||
min="0"
|
||||
max="1000"
|
||||
class="native-input"
|
||||
/>
|
||||
</div>
|
||||
<span class="text_60">积分兑换</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 底部按钮区域 -->
|
||||
<div class="form-actions flex-row">
|
||||
<div class="action-button-group flex-col"></div>
|
||||
<div class="btn-cancel flex-col" @click="handleCancel">
|
||||
<span class="text_67">取消</span>
|
||||
</div>
|
||||
<div class="btn-submit flex-col" @click="handleSubmit">
|
||||
<span class="text_68">保存</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
</script>
|
||||
<script setup lang="ts">
|
||||
import { ref, reactive, h } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { useMessage } from 'naive-ui'
|
||||
import { NInput, NSelect, NDatePicker } from 'naive-ui'
|
||||
import QuillEditor from '@/components/common/QuillEditor.vue'
|
||||
|
||||
const router = useRouter()
|
||||
const message = useMessage()
|
||||
|
||||
// 表单数据
|
||||
const formData = reactive({
|
||||
courseName: '',
|
||||
courseCategory: '',
|
||||
instructors: [], // 改为数组,支持多选
|
||||
sort: '',
|
||||
startTime: null,
|
||||
endTime: null,
|
||||
studentType: 'all', // 'all' 或 'partial'
|
||||
selectedClasses: [],
|
||||
courseCover: '',
|
||||
courseDescription: '',
|
||||
// 开关设置
|
||||
allowDiscussion: true,
|
||||
contentProtection: false,
|
||||
allowTextContent: true,
|
||||
studySettings: true,
|
||||
// 视频设置
|
||||
stopOnLeave: true,
|
||||
videoSpeedControl: false,
|
||||
showVideoText: true,
|
||||
// 积分设置
|
||||
pointsEnabled: true,
|
||||
earnPoints: 60,
|
||||
requiredPoints: 60,
|
||||
// 学习设置详细配置
|
||||
studyDuration: 60,
|
||||
studyProgress: 80,
|
||||
totalLessons: 0,
|
||||
studyDeadline: 0
|
||||
})
|
||||
|
||||
// 课程分类选项
|
||||
const categoryOptions = [
|
||||
{ label: '前端开发', value: 'frontend' },
|
||||
{ label: '后端开发', value: 'backend' },
|
||||
{ label: '移动开发', value: 'mobile' },
|
||||
{ label: '人工智能', value: 'ai' },
|
||||
{ label: '数据库', value: 'database' },
|
||||
{ label: '运维部署', value: 'devops' },
|
||||
{ label: '测试', value: 'testing' },
|
||||
{ label: '产品设计', value: 'design' }
|
||||
]
|
||||
|
||||
// 讲师选项
|
||||
const instructorOptions = [
|
||||
{ label: '李清林', value: '李清林' },
|
||||
{ label: '刘树光', value: '刘树光' },
|
||||
{ label: '肖蒙', value: '肖蒙' },
|
||||
{ label: '张老师', value: '张老师' },
|
||||
{ label: '王老师', value: '王老师' }
|
||||
]
|
||||
|
||||
// 班级选项
|
||||
const classOptions = [
|
||||
{ label: '前端开发班', value: 'frontend-class' },
|
||||
{ label: '后端开发班', value: 'backend-class' },
|
||||
{ label: 'AI算法班', value: 'ai-class' },
|
||||
{ label: '全栈开发班', value: 'fullstack-class' }
|
||||
]
|
||||
|
||||
// 文件上传
|
||||
const previewUrl = ref('')
|
||||
|
||||
// 自定义渲染选中的讲师标签
|
||||
const renderInstructorTag = ({ option, handleClose }: any) => {
|
||||
return h('div', {
|
||||
style: {
|
||||
display: 'inline-flex',
|
||||
alignItems: 'center',
|
||||
padding: '2px 8px',
|
||||
margin: '2px',
|
||||
background: '#f0f0f0',
|
||||
borderRadius: '4px',
|
||||
fontSize: '14px'
|
||||
}
|
||||
}, [
|
||||
option.label,
|
||||
h('span', {
|
||||
style: {
|
||||
marginLeft: '4px',
|
||||
cursor: 'pointer',
|
||||
fontSize: '12px'
|
||||
},
|
||||
onClick: handleClose
|
||||
}, '×')
|
||||
])
|
||||
}
|
||||
|
||||
// 原生文件上传处理
|
||||
const handleNativeFileChange = (event: Event) => {
|
||||
const input = event.target as HTMLInputElement
|
||||
if (input.files && input.files.length > 0) {
|
||||
const file = input.files[0]
|
||||
|
||||
// 验证文件类型
|
||||
if (!file.type.startsWith('image/')) {
|
||||
message.error('请上传图片文件')
|
||||
return
|
||||
}
|
||||
|
||||
// 验证文件大小(限制为2MB)
|
||||
if (file.size > 2 * 1024 * 1024) {
|
||||
message.error('图片大小不能超过2MB')
|
||||
return
|
||||
}
|
||||
|
||||
// 创建预览URL
|
||||
previewUrl.value = URL.createObjectURL(file)
|
||||
|
||||
// // 保存文件到表单数据
|
||||
// formData.courseCover = file
|
||||
|
||||
console.log('文件上传成功:', file.name)
|
||||
}
|
||||
}
|
||||
|
||||
// 清除上传的图片
|
||||
const clearUpload = (event: Event) => {
|
||||
event.preventDefault()
|
||||
event.stopPropagation()
|
||||
|
||||
// 释放预览URL
|
||||
if (previewUrl.value) {
|
||||
URL.revokeObjectURL(previewUrl.value)
|
||||
}
|
||||
|
||||
// 清除预览和表单数据
|
||||
previewUrl.value = ''
|
||||
formData.courseCover = ''
|
||||
|
||||
// 重置文件输入框
|
||||
const fileInput = document.getElementById('coverUpload') as HTMLInputElement
|
||||
if (fileInput) {
|
||||
fileInput.value = ''
|
||||
}
|
||||
}
|
||||
|
||||
// 提交表单
|
||||
const handleSubmit = async () => {
|
||||
try {
|
||||
// 表单验证
|
||||
if (!formData.courseName) {
|
||||
message.error('请输入课程名称')
|
||||
return
|
||||
}
|
||||
|
||||
if (!formData.courseCategory) {
|
||||
message.error('请选择课程分类')
|
||||
return
|
||||
}
|
||||
|
||||
if (formData.instructors.length === 0) {
|
||||
message.error('请选择主讲老师')
|
||||
return
|
||||
}
|
||||
|
||||
if (!formData.courseCover) {
|
||||
message.error('请上传课程封面')
|
||||
return
|
||||
}
|
||||
|
||||
// 创建FormData对象处理文件上传
|
||||
// 表单提交逻辑已注释,如需使用可取消注释
|
||||
// const formDataToSubmit = new FormData()
|
||||
|
||||
// // 添加表单字段
|
||||
// Object.keys(formData).forEach(key => {
|
||||
// if (key === 'courseCover' && formData[key] instanceof File) {
|
||||
// formDataToSubmit.append('courseCover', formData[key])
|
||||
// } else if (key === 'instructors' || key === 'selectedClasses') {
|
||||
// // 数组类型需要特殊处理
|
||||
// formData[key].forEach((item) => {
|
||||
// formDataToSubmit.append(`${key}[]`, item)
|
||||
// })
|
||||
// } else {
|
||||
// formDataToSubmit.append(key, String(formData[key]))
|
||||
// }
|
||||
// })
|
||||
|
||||
console.log('表单数据:', formData)
|
||||
message.success('课程创建成功!')
|
||||
// 这里可以调用API提交数据
|
||||
// const response = await api.createCourse(formDataToSubmit)
|
||||
router.push('/teacher/course-management')
|
||||
} catch (error) {
|
||||
console.error('提交失败:', error)
|
||||
message.error('提交失败,请重试')
|
||||
}
|
||||
}
|
||||
|
||||
// 取消操作
|
||||
const handleCancel = () => {
|
||||
router.back()
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
<style scoped>
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
<style src="@/components/admin/CourseComponents/css/CourseCreate.css"></style>
|
||||
|
||||
</style>
|
||||
|
1250
src/components/admin/CourseComponents/css/CourseCreate.css
Normal file
@ -1,8 +1,9 @@
|
||||
<template>
|
||||
<div class="course-management-container">
|
||||
<!-- 左侧导航栏 -->
|
||||
<div class="nav-container">
|
||||
<router-link to="/teacher/course-management" class="nav-item">
|
||||
<div class="nav-container">
|
||||
<router-link to="/teacher/course-management" class="nav-item"
|
||||
:class="{ active: $route.path === '/teacher/course-management' }">
|
||||
<span>全部课程</span><i>(10)</i>
|
||||
<div class="icon-container">
|
||||
<span class="icon icon-status icon-add"></span>
|
||||
@ -11,7 +12,8 @@
|
||||
</div>
|
||||
</router-link>
|
||||
<div class="sub-nav-container">
|
||||
<router-link to="/teacher/course-management/course-category" class="nav-item">
|
||||
<router-link to="/teacher/course-management/course-category" class="nav-item sub-nav-item"
|
||||
:class="{ active: $route.path === '/teacher/course-management/course-category' }">
|
||||
<span>课程分类</span>
|
||||
<div class="icon-container">
|
||||
<span class="icon icon-status icon-add"></span>
|
||||
@ -19,7 +21,8 @@
|
||||
<span class="icon icon-status icon-delete"></span>
|
||||
</div>
|
||||
</router-link>
|
||||
<router-link to="/teacher/course-management/material-category" class="nav-item">
|
||||
<router-link to="/teacher/course-management/material-category" class="nav-item sub-nav-item"
|
||||
:class="{ active: $route.path === '/teacher/course-management/material-category' }">
|
||||
<span>资料分类</span>
|
||||
<div class="icon-container">
|
||||
<span class="icon icon-status icon-add"></span>
|
||||
@ -27,69 +30,98 @@
|
||||
<span class="icon icon-status icon-delete"></span>
|
||||
</div>
|
||||
</router-link>
|
||||
<router-link to="/teacher/course-management/course-analysis" class="nav-item">
|
||||
<span>课程分析</span>
|
||||
<div class="icon-container">
|
||||
<span class="icon icon-status icon-add"></span>
|
||||
<span class="icon icon-status icon-edit"></span>
|
||||
<span class="icon icon-status icon-delete"></span>
|
||||
</div>
|
||||
</router-link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 右侧内容区域 -->
|
||||
<div class="content-container">
|
||||
<router-view></router-view>
|
||||
<!-- 全部课程页面直接显示课程分类组件 -->
|
||||
<div v-if="$route.path === '/teacher/course-management'">
|
||||
<CourseCategory />
|
||||
</div>
|
||||
<!-- 其他子路由内容 -->
|
||||
<router-view v-else></router-view>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import CourseCategory from './CourseComponents/CourseCategory.vue'
|
||||
</script>
|
||||
|
||||
|
||||
<style scoped>
|
||||
.course-management-container{
|
||||
.course-management-container {
|
||||
display: flex;
|
||||
}
|
||||
.nav-container{
|
||||
margin-top: 30px;
|
||||
width: 266px;
|
||||
|
||||
.nav-container {
|
||||
margin-top: 20px;
|
||||
padding: 15px;
|
||||
width: 274px;
|
||||
background-color: #fff;
|
||||
}
|
||||
.sub-nav-container{
|
||||
margin-left: 20px;
|
||||
|
||||
.sub-nav-container {
|
||||
margin-left: 10px;
|
||||
}
|
||||
.nav-container .nav-item{
|
||||
|
||||
.nav-container .nav-item {
|
||||
width: 240px;
|
||||
height: 54px;
|
||||
margin-left: 13px;
|
||||
margin-top: 15px;
|
||||
margin-top: 5px;
|
||||
margin-bottom: 13px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 0 15px;
|
||||
border-radius: 8px;
|
||||
background-color: #f5f7fa;
|
||||
background-color: transparent;
|
||||
color: #333;
|
||||
text-decoration: none;
|
||||
transition: all 0.3s ease;
|
||||
font-size: 18px;
|
||||
}
|
||||
.nav-container .nav-item:hover{
|
||||
|
||||
.sub-nav-item {
|
||||
width: 220px !important;
|
||||
font-size: 16px !important;
|
||||
}
|
||||
|
||||
.nav-container .nav-item:hover {
|
||||
background-color: #e6f7ff;
|
||||
}
|
||||
.nav-container .nav-item span{
|
||||
margin-right: 8px;
|
||||
|
||||
/* 选中状态样式 */
|
||||
.nav-container .nav-item.active {
|
||||
background-color: #F5F8FB;
|
||||
color: #0288D1;
|
||||
}
|
||||
.nav-container .nav-item i{
|
||||
|
||||
.nav-container .nav-item.active span {
|
||||
color: #0288D1;
|
||||
}
|
||||
|
||||
.nav-container .nav-item.active i {
|
||||
color: #0288D1;
|
||||
}
|
||||
|
||||
.nav-container .nav-item span {
|
||||
margin-right: 2px;
|
||||
color: #666666;
|
||||
}
|
||||
|
||||
.nav-container .nav-item i {
|
||||
font-style: normal;
|
||||
color: #66b7e3;
|
||||
margin-right: 10px;
|
||||
}
|
||||
.icon-container{
|
||||
|
||||
.icon-container {
|
||||
display: flex;
|
||||
margin-left: auto;
|
||||
}
|
||||
.icon.icon-status{
|
||||
|
||||
.icon.icon-status {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
background-size: contain;
|
||||
@ -97,37 +129,60 @@
|
||||
background-position: center;
|
||||
display: inline-block;
|
||||
margin-left: 8px;
|
||||
opacity: 0;
|
||||
opacity: 0.6;
|
||||
transition: opacity 0.3s ease, background-image 0.3s ease;
|
||||
}
|
||||
|
||||
/* 加号图标 */
|
||||
.icon.icon-add {
|
||||
background-image: url('/images/teacher/加号.png');
|
||||
}
|
||||
|
||||
/* 编辑图标 */
|
||||
.icon.icon-edit {
|
||||
background-image: url('/images/teacher/编辑.png');
|
||||
}
|
||||
|
||||
/* 删除图标 */
|
||||
.icon.icon-delete {
|
||||
background-image: url('/images/teacher/删除.png');
|
||||
}
|
||||
/* 鼠标悬停时各自切换为对应的选中图标 */
|
||||
|
||||
/* 鼠标悬停时显示有颜色的图标 */
|
||||
.nav-item:hover .icon.icon-add {
|
||||
background-image: url('/images/teacher/加号(选中).png');
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.nav-item:hover .icon.icon-edit {
|
||||
background-image: url('/images/teacher/编辑(选中).png');
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.nav-item:hover .icon.icon-delete {
|
||||
background-image: url('/images/teacher/删除(选中).png');
|
||||
opacity: 1;
|
||||
}
|
||||
.content-container{
|
||||
|
||||
/* 选中状态下显示有颜色的图标 */
|
||||
.nav-item.active .icon.icon-add {
|
||||
background-image: url('/images/teacher/加号(选中).png');
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.nav-item.active .icon.icon-edit {
|
||||
background-image: url('/images/teacher/编辑(选中).png');
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.nav-item.active .icon.icon-delete {
|
||||
background-image: url('/images/teacher/删除(选中).png');
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.content-container {
|
||||
flex: 1;
|
||||
padding: 20px;
|
||||
padding: 20px 20px 20px 5px;
|
||||
min-height: 100vh;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { createRouter, createWebHistory } from 'vue-router'
|
||||
import type { RouteRecordRaw } from 'vue-router'
|
||||
|
||||
// 导入页面组件
|
||||
// ========== 前台页面组件 ==========
|
||||
import Home from '@/views/Home.vue'
|
||||
import Courses from '@/views/Courses.vue'
|
||||
import CourseDetail from '@/views/CourseDetail.vue'
|
||||
@ -26,317 +26,327 @@ import SpecialTraining from '@/views/SpecialTraining.vue'
|
||||
import SpecialTrainingDetail from '@/views/SpecialTrainingDetail.vue'
|
||||
import HelpCenter from '@/views/HelpCenter.vue'
|
||||
|
||||
// 管理员路由
|
||||
// ========== 管理员后台组件 ==========
|
||||
import AdminDashboard from '@/views/teacher/AdminDashboard.vue'
|
||||
import PersonalCenter from '@/components/admin/PersonalCenter.vue'
|
||||
import CourseManagement from '@/components/admin/CourseManagement.vue'
|
||||
import MyResources from '@/components/admin/MyResources.vue'
|
||||
import StudentManagement from '@/components/admin/StudentManagement.vue'
|
||||
// 课程管理子路由组件
|
||||
|
||||
// 课程管理子组件
|
||||
import CourseCategory from '@/components/admin/CourseComponents/CourseCategory.vue'
|
||||
import MaterialCategory from '@/components/admin/CourseComponents/MaterialCategory.vue'
|
||||
import CourseAnalysis from '@/components/admin/CourseComponents/CourseAnalysis.vue'
|
||||
import CourseCreate from '@/components/admin/CourseComponents/CourseCreate.vue'
|
||||
import CourseDetails from '@/components/admin/CourseComponents/CourseDetail.vue'
|
||||
|
||||
// 课程编辑子组件(新增)
|
||||
import CourseEditor from '@/views/teacher/course/CourseEditor.vue'
|
||||
import CoursewareManagement from '@/views/teacher/course/CoursewareManagement.vue'
|
||||
import ChapterManagement from '@/views/teacher/course/ChapterManagement.vue'
|
||||
import HomeworkManagement from '@/views/teacher/course/HomeworkManagement.vue'
|
||||
import PracticeManagement from '@/views/teacher/course/PracticeManagement.vue'
|
||||
import QuestionBankManagement from '@/views/teacher/course/QuestionBankManagement.vue'
|
||||
import CertificateManagement from '@/views/teacher/course/CertificateManagement.vue'
|
||||
import DiscussionManagement from '@/views/teacher/course/DiscussionManagement.vue'
|
||||
import StatisticsManagement from '@/views/teacher/course/StatisticsManagement.vue'
|
||||
import NotificationManagement from '@/views/teacher/course/NotificationManagement.vue'
|
||||
import GeneralManagement from '@/views/teacher/course/GeneralManagement.vue'
|
||||
|
||||
// ========== 路由配置 ==========
|
||||
const routes: RouteRecordRaw[] = [
|
||||
// 管理后台路由
|
||||
{
|
||||
path: '/teacher',
|
||||
|
||||
name: 'AdminDashboard',
|
||||
component: AdminDashboard,
|
||||
meta: {
|
||||
title: '管理后台',
|
||||
requiresAuth: true
|
||||
},
|
||||
redirect: '/teacher/personal-center', // 添加默认重定向
|
||||
redirect: '/teacher/course-management',
|
||||
children: [
|
||||
{
|
||||
path: 'personal-center',
|
||||
name: 'PersonalCenter',
|
||||
component: PersonalCenter,
|
||||
meta: {
|
||||
title: '个人中心'
|
||||
}
|
||||
meta: { title: '个人中心' }
|
||||
},
|
||||
{
|
||||
path: 'course-management',
|
||||
name: 'CourseManagement',
|
||||
component: CourseManagement,
|
||||
meta: {
|
||||
title: '课程管理'
|
||||
},
|
||||
redirect: '/teacher/course-management/course-category', // 添加默认重定向
|
||||
meta: { title: '课程管理' },
|
||||
children: [
|
||||
{
|
||||
path: 'course-category',
|
||||
name: 'CourseCategory',
|
||||
component: CourseCategory,
|
||||
meta: {
|
||||
title: '课程分类'
|
||||
}
|
||||
meta: { title: '课程分类' }
|
||||
},
|
||||
{
|
||||
path: 'material-category',
|
||||
name: 'MaterialCategory',
|
||||
component: MaterialCategory,
|
||||
meta: {
|
||||
title: '资料分类'
|
||||
}
|
||||
meta: { title: '资料分类' }
|
||||
},
|
||||
{
|
||||
path: 'course-analysis',
|
||||
name: 'CourseAnalysis',
|
||||
component: CourseAnalysis,
|
||||
meta: {
|
||||
title: '课程分析'
|
||||
}
|
||||
meta: { title: '课程分析' }
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: 'course-create',
|
||||
name: 'CourseCreate',
|
||||
component: CourseCreate,
|
||||
meta: { title: '创建课程' }
|
||||
},
|
||||
{
|
||||
path: 'course-editor/:id',
|
||||
name: 'CourseEditor',
|
||||
component: CourseEditor,
|
||||
meta: { title: '编辑课程' },
|
||||
redirect: (to) => `/teacher/course-editor/${to.params.id}/courseware`,
|
||||
children: [
|
||||
{
|
||||
path: 'courseware',
|
||||
name: 'CoursewareManagement',
|
||||
component: CoursewareManagement,
|
||||
meta: { title: '课件管理' }
|
||||
},
|
||||
{
|
||||
path: 'chapters',
|
||||
name: 'ChapterManagement',
|
||||
component: ChapterManagement,
|
||||
meta: { title: '章节管理' }
|
||||
},
|
||||
{
|
||||
path: 'homework',
|
||||
name: 'HomeworkManagement',
|
||||
component: HomeworkManagement,
|
||||
meta: { title: '作业管理' }
|
||||
},
|
||||
{
|
||||
path: 'practice',
|
||||
name: 'PracticeManagement',
|
||||
component: PracticeManagement,
|
||||
meta: { title: '练考通管理' }
|
||||
},
|
||||
{
|
||||
path: 'question-bank',
|
||||
name: 'QuestionBankManagement',
|
||||
component: QuestionBankManagement,
|
||||
meta: { title: '题库管理' }
|
||||
},
|
||||
{
|
||||
path: 'certificate',
|
||||
name: 'CertificateManagement',
|
||||
component: CertificateManagement,
|
||||
meta: { title: '证书管理' }
|
||||
},
|
||||
{
|
||||
path: 'discussion',
|
||||
name: 'DiscussionManagement',
|
||||
component: DiscussionManagement,
|
||||
meta: { title: '讨论管理' }
|
||||
},
|
||||
{
|
||||
path: 'statistics',
|
||||
name: 'StatisticsManagement',
|
||||
component: StatisticsManagement,
|
||||
meta: { title: '统计管理' }
|
||||
},
|
||||
{
|
||||
path: 'notification',
|
||||
name: 'NotificationManagement',
|
||||
component: NotificationManagement,
|
||||
meta: { title: '通知管理' }
|
||||
},
|
||||
{
|
||||
path: 'management',
|
||||
name: 'GeneralManagement',
|
||||
component: GeneralManagement,
|
||||
meta: { title: '综合管理' }
|
||||
}
|
||||
|
||||
]
|
||||
},
|
||||
{
|
||||
path: 'my-resources',
|
||||
name: 'MyResources',
|
||||
component: MyResources,
|
||||
meta: {
|
||||
title: '我的资源'
|
||||
}
|
||||
meta: { title: '我的资源' }
|
||||
},
|
||||
{
|
||||
path: 'student-management',
|
||||
name: 'StudentManagement',
|
||||
component: StudentManagement,
|
||||
meta: {
|
||||
title: '学员管理'
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'course-create',
|
||||
name: 'CourseCreate',
|
||||
component: CourseCreate,
|
||||
meta: {
|
||||
title: '创建课程'
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'course-detail',
|
||||
name: 'TeacherCourseDetail',
|
||||
component: CourseDetails,
|
||||
meta: {
|
||||
title: '课程详情'
|
||||
}
|
||||
meta: { title: '学员管理' }
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
// 帮助中心
|
||||
{
|
||||
path: '/help-center',
|
||||
name: 'HelpCenter',
|
||||
component: HelpCenter,
|
||||
meta: {
|
||||
title: '帮助中心'
|
||||
}
|
||||
meta: { title: '帮助中心' }
|
||||
},
|
||||
|
||||
// 首页与课程
|
||||
{
|
||||
path: '/',
|
||||
name: 'Home',
|
||||
component: Home,
|
||||
meta: {
|
||||
title: '首页'
|
||||
}
|
||||
meta: { title: '首页' }
|
||||
},
|
||||
{
|
||||
path: '/courses',
|
||||
name: 'Courses',
|
||||
component: Courses,
|
||||
meta: {
|
||||
title: '课程列表'
|
||||
}
|
||||
meta: { title: '课程列表' }
|
||||
},
|
||||
{
|
||||
path: '/course/:id',
|
||||
name: 'CourseDetail',
|
||||
component: CourseDetail,
|
||||
meta: {
|
||||
title: '课程详情'
|
||||
}
|
||||
meta: { title: '课程详情' }
|
||||
},
|
||||
{
|
||||
path: '/course/:id/enrolled',
|
||||
name: 'CourseDetailEnrolled',
|
||||
component: CourseDetailEnrolled,
|
||||
meta: {
|
||||
title: '课程详情 - 已报名'
|
||||
}
|
||||
meta: { title: '课程详情 - 已报名' }
|
||||
},
|
||||
{
|
||||
path: '/course/study/:id',
|
||||
name: 'CourseStudy',
|
||||
component: CourseStudy,
|
||||
meta: {
|
||||
title: '课程学习',
|
||||
requiresAuth: true
|
||||
}
|
||||
meta: { title: '课程学习', requiresAuth: true }
|
||||
},
|
||||
{
|
||||
path: '/learning/:id',
|
||||
name: 'Learning',
|
||||
component: Learning,
|
||||
meta: {
|
||||
title: '学习中心',
|
||||
requiresAuth: true
|
||||
}
|
||||
meta: { title: '学习中心', requiresAuth: true }
|
||||
},
|
||||
{
|
||||
path: '/profile',
|
||||
name: 'Profile',
|
||||
component: Profile,
|
||||
meta: {
|
||||
title: '个人中心',
|
||||
requiresAuth: true
|
||||
}
|
||||
meta: { title: '个人中心', requiresAuth: true }
|
||||
},
|
||||
|
||||
// 其他功能页面
|
||||
{
|
||||
path: '/learning-paths',
|
||||
name: 'LearningPaths',
|
||||
component: LearningPaths,
|
||||
meta: {
|
||||
title: '学习路径'
|
||||
}
|
||||
meta: { title: '学习路径' }
|
||||
},
|
||||
{
|
||||
path: '/faculty',
|
||||
name: 'Faculty',
|
||||
component: Faculty,
|
||||
meta: {
|
||||
title: '师资力量'
|
||||
}
|
||||
meta: { title: '师资力量' }
|
||||
},
|
||||
{
|
||||
path: '/teacher/:id',
|
||||
name: 'TeacherDetail',
|
||||
component: TeacherDetail,
|
||||
meta: {
|
||||
title: '讲师详情'
|
||||
}
|
||||
meta: { title: '讲师详情' }
|
||||
},
|
||||
{
|
||||
path: '/resources',
|
||||
name: 'Resources',
|
||||
component: Resources,
|
||||
meta: {
|
||||
title: '精选资源'
|
||||
}
|
||||
meta: { title: '精选资源' }
|
||||
},
|
||||
{
|
||||
path: '/special-training',
|
||||
name: 'SpecialTraining',
|
||||
component: SpecialTraining,
|
||||
meta: {
|
||||
title: '专题训练'
|
||||
}
|
||||
meta: { title: '专题训练' }
|
||||
},
|
||||
{
|
||||
path: '/special-training/:id',
|
||||
name: 'SpecialTrainingDetail',
|
||||
component: SpecialTrainingDetail,
|
||||
meta: {
|
||||
title: '专题训练详情'
|
||||
}
|
||||
meta: { title: '专题训练详情' }
|
||||
},
|
||||
{
|
||||
path: '/activities',
|
||||
name: 'Activities',
|
||||
component: Activities,
|
||||
meta: {
|
||||
title: '全部活动'
|
||||
}
|
||||
meta: { title: '全部活动' }
|
||||
},
|
||||
{
|
||||
path: '/activity/:id',
|
||||
name: 'ActivityDetail',
|
||||
component: ActivityDetail,
|
||||
meta: {
|
||||
title: '活动详情'
|
||||
}
|
||||
meta: { title: '活动详情' }
|
||||
},
|
||||
{
|
||||
path: '/activity/:id/register',
|
||||
name: 'ActivityRegistration',
|
||||
component: ActivityRegistration,
|
||||
meta: {
|
||||
title: '活动报名'
|
||||
}
|
||||
meta: { title: '活动报名' }
|
||||
},
|
||||
{
|
||||
path: '/course/:courseId/exam/:sectionId/notice',
|
||||
name: 'ExamNotice',
|
||||
component: ExamNotice,
|
||||
meta: {
|
||||
title: '考前须知'
|
||||
}
|
||||
meta: { title: '考前须知' }
|
||||
},
|
||||
{
|
||||
path: '/course/:courseId/exam/:sectionId',
|
||||
name: 'Exam',
|
||||
component: Exam,
|
||||
meta: {
|
||||
title: '在线考试'
|
||||
}
|
||||
meta: { title: '在线考试' }
|
||||
},
|
||||
{
|
||||
path: '/exam/submitted',
|
||||
name: 'ExamSubmitted',
|
||||
component: ExamSubmitted,
|
||||
meta: {
|
||||
title: '考试提交成功'
|
||||
}
|
||||
meta: { title: '考试提交成功' }
|
||||
},
|
||||
{
|
||||
path: '/ai',
|
||||
name: 'AI',
|
||||
component: () => import('@/views/Ai.vue'),
|
||||
meta: {
|
||||
title: 'AI'
|
||||
}
|
||||
meta: { title: 'AI' }
|
||||
},
|
||||
{
|
||||
path: '/ai-demo',
|
||||
name: 'AIDemo',
|
||||
component: () => import('@/views/DetailView.vue'),
|
||||
meta: {
|
||||
title: 'AI演示'
|
||||
}
|
||||
meta: { title: 'AI演示' }
|
||||
},
|
||||
{
|
||||
path: '/test-sections',
|
||||
name: 'TestSections',
|
||||
component: TestSections,
|
||||
meta: {
|
||||
title: '测试章节API'
|
||||
}
|
||||
meta: { title: '测试章节API' }
|
||||
},
|
||||
{
|
||||
path: '/local-video-demo',
|
||||
name: 'LocalVideoDemo',
|
||||
component: LocalVideoDemo,
|
||||
meta: {
|
||||
title: '本地视频播放演示'
|
||||
}
|
||||
meta: { title: '本地视频播放演示' }
|
||||
},
|
||||
|
||||
// 404 路由
|
||||
{
|
||||
path: '/:pathMatch(.*)*',
|
||||
name: 'NotFound',
|
||||
component: () => import('@/views/NotFound.vue'),
|
||||
meta: {
|
||||
title: '页面未找到'
|
||||
},
|
||||
|
||||
meta: { title: '页面未找到' }
|
||||
}
|
||||
]
|
||||
|
||||
// ========== 创建路由实例 ==========
|
||||
const router = createRouter({
|
||||
history: createWebHistory(),
|
||||
routes,
|
||||
@ -349,7 +359,7 @@ const router = createRouter({
|
||||
}
|
||||
})
|
||||
|
||||
// 路由守卫
|
||||
// ========== 路由守卫 ==========
|
||||
router.beforeEach((to, _from, next) => {
|
||||
// 设置页面标题
|
||||
if (to.meta.title) {
|
||||
@ -358,10 +368,8 @@ router.beforeEach((to, _from, next) => {
|
||||
|
||||
// 检查是否需要登录
|
||||
if (to.meta.requiresAuth) {
|
||||
// 检查用户登录状态
|
||||
const token = localStorage.getItem('token')
|
||||
if (!token) {
|
||||
// 未登录时跳转到首页,用户可以通过模态框登录
|
||||
next('/')
|
||||
return
|
||||
}
|
||||
|
@ -1,55 +1,59 @@
|
||||
<template>
|
||||
<div class="admin-dashboard">
|
||||
<!-- 顶部图片 -->
|
||||
<div class="top-image-container">
|
||||
<img src="/images/teacher/顶部.png" alt="顶部图片" class="top-image">
|
||||
<div class="top-image-container">
|
||||
<img src="/images/teacher/顶部.png" alt="顶部图片" class="top-image">
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="main-content">
|
||||
<!-- 侧边栏 -->
|
||||
<div class="sidebar-container">
|
||||
<!-- 头像 -->
|
||||
<div class="avatar-container">
|
||||
<img src="https://picsum.photos/200/200" alt="头像" class="avatar">
|
||||
<div class="avatar-text">
|
||||
用户昵称~
|
||||
</div>
|
||||
</div>
|
||||
<!-- 头像 -->
|
||||
<div class="avatar-container">
|
||||
<img src="https://picsum.photos/200/200" alt="头像" class="avatar">
|
||||
<div class="avatar-text">
|
||||
用户昵称~
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 导航栏 -->
|
||||
<div class="nav-container">
|
||||
<router-link to="/teacher/course-management" class="nav-item" :class="{ active: activeNavItem === 0 }" @click="setActiveNavItem(0)">
|
||||
<img :src="activeNavItem === 0 ? '/images/teacher/课程管理(选中).png' : '/images/teacher/课程管理.png'" alt="">
|
||||
<span>课程管理</span>
|
||||
</router-link>
|
||||
<router-link to="/teacher/student-management" class="nav-item" :class="{ active: activeNavItem === 1 }" @click="setActiveNavItem(1)">
|
||||
<!-- 导航栏 -->
|
||||
<div class="nav-container">
|
||||
<router-link to="/teacher/course-management" class="nav-item" :class="{ active: activeNavItem === 0 }"
|
||||
@click="setActiveNavItem(0)">
|
||||
<img :src="activeNavItem === 0 ? '/images/teacher/课程管理(选中).png' : '/images/teacher/课程管理.png'" alt="">
|
||||
<span>课程管理</span>
|
||||
</router-link>
|
||||
<router-link to="/teacher/student-management" class="nav-item" :class="{ active: activeNavItem === 1 }"
|
||||
@click="setActiveNavItem(1)">
|
||||
|
||||
<img :src="activeNavItem === 1 ? '/images/teacher/学院管理(选中).png' : '/images/teacher/学员管理.png'" alt="">
|
||||
<span>学员管理</span>
|
||||
</router-link>
|
||||
<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/personal-center" class="nav-item" :class="{ active: activeNavItem === 3 }" @click="setActiveNavItem(3)">
|
||||
<img :src="activeNavItem === 1 ? '/images/teacher/学院管理(选中).png' : '/images/teacher/学员管理.png'" alt="">
|
||||
<span>学员管理</span>
|
||||
</router-link>
|
||||
<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/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>
|
||||
</div>
|
||||
<img :src="activeNavItem === 3 ? '/images/teacher/个人中心(选中).png' : '/images/teacher/个人中心.png'" alt="">
|
||||
<span>个人中心</span>
|
||||
</router-link>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 右侧路由视图 -->
|
||||
<div class="router-view-container">
|
||||
<!-- 面包屑 -->
|
||||
<div class="breadcrumb">
|
||||
<span class="breadcrumb-separator">|</span>
|
||||
<n-breadcrumb>
|
||||
<n-breadcrumb-item v-for="(item, index) in breadcrumbItems" :key="index" :to="item.path">
|
||||
{{ item.title }}
|
||||
</n-breadcrumb-item>
|
||||
</n-breadcrumb>
|
||||
<span class="breadcrumb-separator"></span>
|
||||
<n-breadcrumb>
|
||||
<n-breadcrumb-item v-for="(item, index) in breadcrumbItems" :key="index" :to="item.path">
|
||||
{{ item.title }}
|
||||
</n-breadcrumb-item>
|
||||
</n-breadcrumb>
|
||||
</div>
|
||||
<router-view></router-view>
|
||||
</div>
|
||||
@ -67,7 +71,7 @@ const height = window.innerHeight;
|
||||
console.log(`当前屏幕宽度: ${width}px, 高度: ${height}px`);
|
||||
|
||||
// 添加导航项激活状态管理
|
||||
const activeNavItem = ref(0);
|
||||
const activeNavItem = ref(0); // 0: 课程管理, 1: 学员管理, 2: 我的资源, 3: 个人中心
|
||||
const route = useRoute();
|
||||
|
||||
const setActiveNavItem = (index: number) => {
|
||||
@ -103,13 +107,13 @@ watch(route, () => {
|
||||
const updateActiveNavItem = () => {
|
||||
const path = route.path;
|
||||
if (path.includes('course-management')) {
|
||||
activeNavItem.value = 0;
|
||||
activeNavItem.value = 0; // 课程管理
|
||||
} else if (path.includes('student-management')) {
|
||||
activeNavItem.value = 1;
|
||||
activeNavItem.value = 1; // 学员管理
|
||||
} else if (path.includes('my-resources')) {
|
||||
activeNavItem.value = 2;
|
||||
activeNavItem.value = 2; // 我的资源
|
||||
} else if (path.includes('personal-center')) {
|
||||
activeNavItem.value = 3;
|
||||
activeNavItem.value = 3; // 个人中心
|
||||
}
|
||||
}
|
||||
|
||||
@ -137,109 +141,121 @@ const updateActiveNavItem = () => {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.sidebar-container{
|
||||
width: 294px;
|
||||
height: calc(100vh - 130px);
|
||||
background: #FFFFFF;
|
||||
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
|
||||
.sidebar-container {
|
||||
width: 294px;
|
||||
height: calc(100vh - 130px);
|
||||
background: #FFFFFF;
|
||||
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
.avatar-container{
|
||||
height: 230px;
|
||||
position: relative;
|
||||
/* 移除原来的边框 */
|
||||
|
||||
.avatar-container {
|
||||
height: 230px;
|
||||
position: relative;
|
||||
/* 移除原来的边框 */
|
||||
}
|
||||
|
||||
/* 添加带间距的横线 */
|
||||
.avatar-container::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 20px;
|
||||
right: 20px;
|
||||
height: 1px;
|
||||
background-color: #E6E6E6;
|
||||
content: '';
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 20px;
|
||||
right: 20px;
|
||||
height: 1px;
|
||||
background-color: #E6E6E6;
|
||||
}
|
||||
.avatar-container img{
|
||||
width: 95px;
|
||||
height: 95px;
|
||||
/* 圆角 */
|
||||
border-radius: 50%;
|
||||
margin-top: 55px;
|
||||
margin-left: 100px;
|
||||
margin-right: 99;
|
||||
|
||||
.avatar-container img {
|
||||
width: 95px;
|
||||
height: 95px;
|
||||
/* 圆角 */
|
||||
border-radius: 50%;
|
||||
margin-top: 55px;
|
||||
margin-left: 100px;
|
||||
margin-right: 99;
|
||||
}
|
||||
.avatar-text{
|
||||
margin-left: 98px;
|
||||
height: 31px;
|
||||
font-family: AppleSystemUIFont;
|
||||
font-size: 22px;
|
||||
color: #000000;
|
||||
line-height: 26px;
|
||||
text-align: left;
|
||||
font-style: normal;
|
||||
text-transform: none;
|
||||
|
||||
.avatar-text {
|
||||
margin-left: 98px;
|
||||
height: 31px;
|
||||
font-family: AppleSystemUIFont;
|
||||
font-size: 22px;
|
||||
color: #000000;
|
||||
line-height: 26px;
|
||||
text-align: left;
|
||||
font-style: normal;
|
||||
text-transform: none;
|
||||
}
|
||||
.nav-container{
|
||||
margin-top: 30px;
|
||||
/* 鼠标变小手 */
|
||||
cursor: pointer;
|
||||
|
||||
.nav-container {
|
||||
margin-top: 30px;
|
||||
/* 鼠标变小手 */
|
||||
cursor: pointer;
|
||||
|
||||
}
|
||||
.nav-container .nav-item{
|
||||
margin-left: 20px;
|
||||
width: 254px;
|
||||
height: 54px;
|
||||
margin-bottom: 20px;
|
||||
/* 圆角 */
|
||||
border-radius: 10px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
transition: all 0.3s ease;
|
||||
|
||||
.nav-container .nav-item {
|
||||
margin-left: 20px;
|
||||
width: 254px;
|
||||
height: 54px;
|
||||
margin-bottom: 20px;
|
||||
/* 圆角 */
|
||||
border-radius: 10px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.nav-container .nav-item:hover {
|
||||
background: rgba(102,183,227,0.05);
|
||||
background: rgba(102, 183, 227, 0.05);
|
||||
}
|
||||
|
||||
/* 添加激活状态样式 */
|
||||
.nav-container .nav-item.active{
|
||||
background: rgba(102,183,227,0.1);
|
||||
.nav-container .nav-item.active {
|
||||
background: rgba(102, 183, 227, 0.1);
|
||||
}
|
||||
.nav-container .nav-item img{
|
||||
width: 17px;
|
||||
height: 20px;
|
||||
margin-left: 50px;
|
||||
margin-top: 0;
|
||||
margin-right: 5px;
|
||||
|
||||
.nav-container .nav-item.active span {
|
||||
color: #0C99DA;
|
||||
}
|
||||
.nav-container .nav-item span{
|
||||
width: 80px;
|
||||
height: 28px;
|
||||
font-family: AppleSystemUIFont;
|
||||
font-size: 20px;
|
||||
color: #0C99DA;
|
||||
line-height: 23px;
|
||||
text-align: left;
|
||||
font-style: normal;
|
||||
text-transform: none;
|
||||
|
||||
.nav-container .nav-item img {
|
||||
width: 17px;
|
||||
height: 20px;
|
||||
margin-left: 50px;
|
||||
margin-top: 0;
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
.nav-container .nav-item span {
|
||||
width: 80px;
|
||||
height: 28px;
|
||||
font-family: AppleSystemUIFont;
|
||||
font-size: 20px;
|
||||
color: #666666;
|
||||
line-height: 23px;
|
||||
text-align: left;
|
||||
font-style: normal;
|
||||
text-transform: none;
|
||||
}
|
||||
|
||||
.router-view-container {
|
||||
flex: 1;
|
||||
padding: 20px;
|
||||
background: #F5F7FA;
|
||||
height: calc(100vh - 130px);
|
||||
overflow-y: auto;
|
||||
flex: 1;
|
||||
padding: 20px;
|
||||
background: #F5F7FA;
|
||||
height: calc(100vh - 130px);
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.breadcrumb {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.breadcrumb-separator {
|
||||
margin: 0 8px;
|
||||
color: #0C99DA;
|
||||
font-size: 16px;
|
||||
content: '>';
|
||||
width: 4px;
|
||||
height: 17px;
|
||||
margin-right: 10px;
|
||||
background-color: #0C99DA;
|
||||
}
|
||||
</style>
|
||||
|
36
src/views/teacher/course/CertificateManagement.vue
Normal file
@ -0,0 +1,36 @@
|
||||
<template>
|
||||
<div class="certificate-management">
|
||||
<div class="content-placeholder">
|
||||
<h2>证书管理</h2>
|
||||
<p>证书管理功能正在开发中...</p>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
// 证书管理逻辑
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.certificate-management {
|
||||
padding: 20px;
|
||||
background: #fff;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.content-placeholder {
|
||||
text-align: center;
|
||||
padding: 60px 20px;
|
||||
}
|
||||
|
||||
.content-placeholder h2 {
|
||||
font-size: 24px;
|
||||
color: #333;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.content-placeholder p {
|
||||
font-size: 16px;
|
||||
color: #666;
|
||||
}
|
||||
</style>
|
36
src/views/teacher/course/ChapterManagement.vue
Normal file
@ -0,0 +1,36 @@
|
||||
<template>
|
||||
<div class="chapter-management">
|
||||
<div class="content-placeholder">
|
||||
<h2>章节管理</h2>
|
||||
<p>章节管理功能正在开发中...</p>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
// 章节管理逻辑
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.chapter-management {
|
||||
padding: 20px;
|
||||
background: #fff;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.content-placeholder {
|
||||
text-align: center;
|
||||
padding: 60px 20px;
|
||||
}
|
||||
|
||||
.content-placeholder h2 {
|
||||
font-size: 24px;
|
||||
color: #333;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.content-placeholder p {
|
||||
font-size: 16px;
|
||||
color: #666;
|
||||
}
|
||||
</style>
|
197
src/views/teacher/course/CourseEditor.vue
Normal file
@ -0,0 +1,197 @@
|
||||
<template>
|
||||
<div class="course-editor">
|
||||
<!-- 左侧导航菜单 -->
|
||||
<div class="sidebar">
|
||||
<router-link
|
||||
:to="`/teacher/course-editor/${courseId}/courseware`"
|
||||
class="menu-item"
|
||||
:class="{ active: $route.path.includes('courseware') }"
|
||||
>
|
||||
<img
|
||||
:src="$route.path.includes('courseware') ? '/images/teacher/课件-选中.png' : '/images/teacher/课件.png'"
|
||||
alt="课件"
|
||||
/>
|
||||
<span>课件</span>
|
||||
</router-link>
|
||||
<router-link
|
||||
:to="`/teacher/course-editor/${courseId}/chapters`"
|
||||
class="menu-item"
|
||||
:class="{ active: $route.path.includes('chapters') }"
|
||||
>
|
||||
<img
|
||||
:src="$route.path.includes('chapters') ? '/images/teacher/章节-选中.png' : '/images/teacher/章节.png'"
|
||||
alt="章节"
|
||||
/>
|
||||
<span>章节</span>
|
||||
</router-link>
|
||||
<router-link
|
||||
:to="`/teacher/course-editor/${courseId}/homework`"
|
||||
class="menu-item"
|
||||
:class="{ active: $route.path.includes('homework') }"
|
||||
>
|
||||
<img
|
||||
:src="$route.path.includes('homework') ? '/images/teacher/作业-选中.png' : '/images/teacher/作业.png'"
|
||||
alt="作业"
|
||||
/>
|
||||
<span>作业</span>
|
||||
</router-link>
|
||||
<router-link
|
||||
:to="`/teacher/course-editor/${courseId}/practice`"
|
||||
class="menu-item"
|
||||
:class="{ active: $route.path.includes('practice') }"
|
||||
>
|
||||
<img
|
||||
:src="$route.path.includes('practice') ? '/images/teacher/练考通-选中.png' : '/images/teacher/练考通.png'"
|
||||
alt="练考通"
|
||||
/>
|
||||
<span>练考通</span>
|
||||
</router-link>
|
||||
<router-link
|
||||
:to="`/teacher/course-editor/${courseId}/question-bank`"
|
||||
class="menu-item"
|
||||
:class="{ active: $route.path.includes('question-bank') }"
|
||||
>
|
||||
<img
|
||||
:src="$route.path.includes('question-bank') ? '/images/teacher/题库-选中.png' : '/images/teacher/题库.png'"
|
||||
alt="题库"
|
||||
/>
|
||||
<span>题库</span>
|
||||
</router-link>
|
||||
<router-link
|
||||
:to="`/teacher/course-editor/${courseId}/certificate`"
|
||||
class="menu-item"
|
||||
:class="{ active: $route.path.includes('certificate') }"
|
||||
>
|
||||
<img
|
||||
:src="$route.path.includes('certificate') ? '/images/teacher/证书-选中.png' : '/images/teacher/证书.png'"
|
||||
alt="证书"
|
||||
/>
|
||||
<span>证书</span>
|
||||
</router-link>
|
||||
<router-link
|
||||
:to="`/teacher/course-editor/${courseId}/discussion`"
|
||||
class="menu-item"
|
||||
:class="{ active: $route.path.includes('discussion') }"
|
||||
>
|
||||
<img
|
||||
:src="$route.path.includes('discussion') ? '/images/teacher/讨论-选中.png' : '/images/teacher/讨论.png'"
|
||||
alt="讨论"
|
||||
/>
|
||||
<span>讨论</span>
|
||||
</router-link>
|
||||
<router-link
|
||||
:to="`/teacher/course-editor/${courseId}/statistics`"
|
||||
class="menu-item"
|
||||
:class="{ active: $route.path.includes('statistics') }"
|
||||
>
|
||||
<img
|
||||
:src="$route.path.includes('statistics') ? '/images/teacher/统计-选中.png' : '/images/teacher/统计.png'"
|
||||
alt="统计"
|
||||
/>
|
||||
<span>统计</span>
|
||||
</router-link>
|
||||
<router-link
|
||||
:to="`/teacher/course-editor/${courseId}/notification`"
|
||||
class="menu-item"
|
||||
:class="{ active: $route.path.includes('notification') }"
|
||||
>
|
||||
<img
|
||||
:src="$route.path.includes('notification') ? '/images/teacher/通知-选中.png' : '/images/teacher/通知.png'"
|
||||
alt="通知"
|
||||
/>
|
||||
<span>通知</span>
|
||||
</router-link>
|
||||
<router-link
|
||||
:to="`/teacher/course-editor/${courseId}/management`"
|
||||
class="menu-item"
|
||||
:class="{ active: $route.path.includes('management') }"
|
||||
>
|
||||
<img
|
||||
:src="$route.path.includes('management') ? '/images/teacher/管理-选中.png' : '/images/teacher/管理.png'"
|
||||
alt="管理"
|
||||
/>
|
||||
<span>管理</span>
|
||||
</router-link>
|
||||
</div>
|
||||
|
||||
<!-- 右侧内容区域 -->
|
||||
<div class="content-area">
|
||||
<router-view />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useRoute } from 'vue-router'
|
||||
|
||||
const route = useRoute()
|
||||
|
||||
// 获取课程ID
|
||||
const courseId = route.params.id
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.course-editor {
|
||||
display: flex;
|
||||
margin-top: 5px;
|
||||
height: 100vh;
|
||||
background: #f5f5f5;
|
||||
}
|
||||
|
||||
/* 左侧导航 */
|
||||
.sidebar {
|
||||
width: 273px;
|
||||
background: #fff;
|
||||
border-right: 1px solid #e8e8e8;
|
||||
padding: 20px 15px;
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
.menu-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 15px 20px;
|
||||
margin-bottom: 5px;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
border-left: 3px solid transparent;
|
||||
text-decoration: none;
|
||||
color: inherit;
|
||||
font-size: 18px;
|
||||
color: #666;
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
.menu-item:hover {
|
||||
background: #f5f5f5;
|
||||
}
|
||||
|
||||
.menu-item.active {
|
||||
background: #F5F8FB;
|
||||
color: #0288D1;
|
||||
}
|
||||
|
||||
.menu-item.active span {
|
||||
color: #0288D1;
|
||||
}
|
||||
|
||||
.menu-item img {
|
||||
margin-left: 40px;
|
||||
margin-top: 1px;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.menu-item span {
|
||||
font-size: 16px;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
/* 右侧内容区域 */
|
||||
.content-area {
|
||||
flex: 1;
|
||||
background-color: #fff;
|
||||
overflow: auto;
|
||||
}
|
||||
</style>
|
870
src/views/teacher/course/CoursewareManagement.vue
Normal file
@ -0,0 +1,870 @@
|
||||
<template>
|
||||
<div class="courseware-management">
|
||||
<!-- 顶部操作栏 -->
|
||||
<div class="toolbar">
|
||||
<h2>全部课件</h2>
|
||||
<div class="toolbar-actions">
|
||||
<button class="btn btn-primary" @click="addCourseware">添加课件</button>
|
||||
<button class="btn btn-new" @click="createFolder">新建文件夹</button>
|
||||
<button class="btn btn-default" @click="moveFiles" :disabled="selectedFiles.length === 0">移动</button>
|
||||
<button class="btn btn-danger" @click="deleteSelected" :disabled="selectedFiles.length === 0">删除</button>
|
||||
<div class="search-box">
|
||||
<input type="text" placeholder="请输入想要搜索的内容" v-model="searchKeyword" />
|
||||
<button class="btn btn-search" @click="searchFiles">搜索</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 文件列表表格 -->
|
||||
<div class="table-box">
|
||||
<n-config-provider :locale="zhCN" :date-locale="dateZhCN">
|
||||
<n-data-table :columns="columns" :data="paginatedFiles" :row-key="rowKey" :checked-row-keys="selectedFiles"
|
||||
@update:checked-row-keys="handleCheck" :bordered="false" :single-line="false" size="medium"
|
||||
class="file-data-table" :row-class-name="rowClassName" />
|
||||
</n-config-provider>
|
||||
|
||||
<!-- 自定义分页器 -->
|
||||
<div class="custom-pagination">
|
||||
<div class="pagination-content">
|
||||
<div class="page-numbers">
|
||||
<span class="page-number nav-button" :class="{ disabled: currentPage === 1 }" @click="goToPage('first')">
|
||||
首页
|
||||
</span>
|
||||
<span class="page-number nav-button" :class="{ disabled: currentPage === 1 }" @click="goToPage('prev')">
|
||||
上一页
|
||||
</span>
|
||||
|
||||
<span v-for="page in visiblePages" :key="page" class="page-number page-number-bordered"
|
||||
:class="{ active: page === currentPage }" @click="goToPage(page)">
|
||||
{{ page }}
|
||||
</span>
|
||||
|
||||
<span v-if="showRightEllipsis" class="page-number">...</span>
|
||||
<span v-if="totalPages > 1" class="page-number page-number-bordered" @click="goToPage(totalPages)">
|
||||
{{ totalPages }}
|
||||
</span>
|
||||
|
||||
<span class="page-number nav-button" :class="{ disabled: currentPage === totalPages }"
|
||||
@click="goToPage('next')">
|
||||
下一页
|
||||
</span>
|
||||
<span class="page-number nav-button" :class="{ disabled: currentPage === totalPages }"
|
||||
@click="goToPage('last')">
|
||||
尾页
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, h } from 'vue'
|
||||
import { NButton, NDropdown, NTag, useMessage, NDataTable, NConfigProvider, zhCN, dateZhCN } from 'naive-ui'
|
||||
import type { DataTableColumns, DropdownOption } from 'naive-ui'
|
||||
|
||||
const message = useMessage()
|
||||
|
||||
// 文件类型定义
|
||||
interface FileItem {
|
||||
id: number
|
||||
name: string
|
||||
type: string
|
||||
size: string
|
||||
creator: string
|
||||
createTime: string
|
||||
isTop: boolean
|
||||
}
|
||||
|
||||
// 搜索关键词
|
||||
const searchKeyword = ref('')
|
||||
|
||||
// 选中的文件行
|
||||
const selectedFiles = ref<number[]>([])
|
||||
|
||||
// 文件列表数据
|
||||
const fileList = ref<FileItem[]>([
|
||||
{
|
||||
id: 1,
|
||||
name: '文件名称文件名称文件名称',
|
||||
type: 'folder',
|
||||
size: '1MB',
|
||||
creator: '王建国',
|
||||
createTime: '2025.07.25 09:20',
|
||||
isTop: true
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
name: '这是一个表格文件.xlsx',
|
||||
type: 'excel',
|
||||
size: '1MB',
|
||||
creator: '王建国',
|
||||
createTime: '2025.07.25 09:20',
|
||||
isTop: false
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
name: '这是一个表格文件.xlsx',
|
||||
type: 'excel',
|
||||
size: '1MB',
|
||||
creator: '王建国',
|
||||
createTime: '2025.07.25 09:20',
|
||||
isTop: false
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
name: '这是一个表格文件.xlsx',
|
||||
type: 'word',
|
||||
size: '1MB',
|
||||
creator: '王建国',
|
||||
createTime: '2025.07.25 09:20',
|
||||
isTop: false
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
name: '这是一个表格文件.xlsx',
|
||||
type: 'pdf',
|
||||
size: '1MB',
|
||||
creator: '王建国',
|
||||
createTime: '2025.07.25 09:20',
|
||||
isTop: false
|
||||
},
|
||||
{
|
||||
id: 6,
|
||||
name: '这是一个表格文件.xlsx',
|
||||
type: 'video',
|
||||
size: '1MB',
|
||||
creator: '王建国',
|
||||
createTime: '2025.07.25 09:20',
|
||||
isTop: false
|
||||
}
|
||||
])
|
||||
|
||||
// 过滤后的文件列表
|
||||
const filteredFiles = computed(() => {
|
||||
if (!searchKeyword.value) {
|
||||
return fileList.value
|
||||
}
|
||||
return fileList.value.filter((file: FileItem) =>
|
||||
file.name.toLowerCase().includes(searchKeyword.value.toLowerCase())
|
||||
)
|
||||
})
|
||||
|
||||
// 获取文件图标
|
||||
const getFileIcon = (type: string) => {
|
||||
const iconMap: { [key: string]: string } = {
|
||||
folder: '/images/activity/file.png',
|
||||
excel: '/images/activity/xls.png',
|
||||
word: '/images/activity/wrod.png',
|
||||
pdf: '/images/activity/pdf.png',
|
||||
ppt: '/images/activity/ppt.png',
|
||||
video: '/images/activity/file.png'
|
||||
}
|
||||
return iconMap[type] || '/images/activity/file.png'
|
||||
}
|
||||
|
||||
// 表格行键
|
||||
const rowKey = (row: FileItem) => row.id
|
||||
|
||||
// 表格选择处理
|
||||
const handleCheck = (rowKeys: number[]) => {
|
||||
selectedFiles.value = rowKeys
|
||||
}
|
||||
|
||||
// 行样式名称
|
||||
const rowClassName = () => {
|
||||
return 'file-table-row'
|
||||
}
|
||||
|
||||
// 分页相关状态
|
||||
const currentPage = ref(1)
|
||||
const pageSize = ref(10)
|
||||
const totalPages = computed(() => Math.ceil(filteredFiles.value.length / pageSize.value))
|
||||
|
||||
// 可见的页码范围
|
||||
const visiblePages = computed(() => {
|
||||
const pages = []
|
||||
const current = currentPage.value
|
||||
const total = totalPages.value
|
||||
|
||||
if (total <= 7) {
|
||||
// 如果总页数小于等于7,显示所有页码
|
||||
for (let i = 1; i <= total; i++) {
|
||||
pages.push(i)
|
||||
}
|
||||
} else {
|
||||
// 显示当前页附近的页码
|
||||
const start = Math.max(1, current - 2)
|
||||
const end = Math.min(total, current + 2)
|
||||
|
||||
for (let i = start; i <= end; i++) {
|
||||
pages.push(i)
|
||||
}
|
||||
}
|
||||
|
||||
return pages
|
||||
})
|
||||
|
||||
// 是否显示右侧省略号
|
||||
const showRightEllipsis = computed(() => {
|
||||
return currentPage.value < totalPages.value - 3
|
||||
})
|
||||
|
||||
// 分页方法
|
||||
const goToPage = (page: string | number) => {
|
||||
if (typeof page === 'string') {
|
||||
switch (page) {
|
||||
case 'first':
|
||||
currentPage.value = 1
|
||||
break
|
||||
case 'prev':
|
||||
if (currentPage.value > 1) currentPage.value--
|
||||
break
|
||||
case 'next':
|
||||
if (currentPage.value < totalPages.value) currentPage.value++
|
||||
break
|
||||
case 'last':
|
||||
currentPage.value = totalPages.value
|
||||
break
|
||||
}
|
||||
} else {
|
||||
currentPage.value = page
|
||||
}
|
||||
}
|
||||
|
||||
// 分页后的数据
|
||||
const paginatedFiles = computed(() => {
|
||||
const start = (currentPage.value - 1) * pageSize.value
|
||||
const end = start + pageSize.value
|
||||
return filteredFiles.value.slice(start, end)
|
||||
})
|
||||
|
||||
// 更多操作菜单选项
|
||||
const getMoreOptions = (row: FileItem): DropdownOption[] => [
|
||||
{
|
||||
label: '重命名',
|
||||
key: 'rename',
|
||||
icon: () => h('img', {
|
||||
src: '/images/teacher/重命名.png',
|
||||
alt: '重命名',
|
||||
style: { width: '16px', height: '16px' }
|
||||
})
|
||||
},
|
||||
{
|
||||
label: row.isTop ? '取消置顶' : '置顶',
|
||||
key: 'toggle-top',
|
||||
icon: () => h('img', {
|
||||
src: '/images/teacher/置顶.png',
|
||||
alt: '置顶',
|
||||
style: { width: '16px', height: '16px' }
|
||||
})
|
||||
},
|
||||
{
|
||||
label: '权限设置',
|
||||
key: 'permissions',
|
||||
icon: () => h('img', {
|
||||
src: '/images/teacher/权限设置.png',
|
||||
alt: '权限设置',
|
||||
style: { width: '16px', height: '16px' }
|
||||
})
|
||||
},
|
||||
{
|
||||
label: '下载',
|
||||
key: 'download',
|
||||
icon: () => h('img', {
|
||||
src: '/images/teacher/下载.png',
|
||||
alt: '下载',
|
||||
style: { width: '16px', height: '16px' }
|
||||
})
|
||||
}
|
||||
]
|
||||
|
||||
// 处理更多操作
|
||||
const handleMoreAction = (key: string, row: FileItem) => {
|
||||
switch (key) {
|
||||
case 'rename':
|
||||
renameFile(row)
|
||||
break
|
||||
case 'toggle-top':
|
||||
toggleTop(row)
|
||||
break
|
||||
case 'permissions':
|
||||
setPermissions(row)
|
||||
break
|
||||
case 'download':
|
||||
downloadFile(row)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// 表格列配置
|
||||
const columns: DataTableColumns<FileItem> = [
|
||||
{
|
||||
type: 'selection'
|
||||
},
|
||||
{
|
||||
title: '序号',
|
||||
key: 'index',
|
||||
width: 80,
|
||||
render: (_row: FileItem, index: number) => index + 1
|
||||
},
|
||||
{
|
||||
title: '名称',
|
||||
key: 'name',
|
||||
minWidth: 200,
|
||||
render: (row: FileItem) => {
|
||||
return h('div', { style: { display: 'flex', alignItems: 'center', gap: '8px' } }, [
|
||||
h('img', {
|
||||
src: getFileIcon(row.type),
|
||||
alt: row.type,
|
||||
style: { width: '16px', height: '16px' }
|
||||
}),
|
||||
h('div', { style: { display: 'flex', alignItems: 'center', gap: '8px' } }, [
|
||||
h('span', { style: { color: '#062333' } }, row.name),
|
||||
row.isTop ? h(NTag, { type: 'info', size: 'small' }, { default: () => '置顶' }) : null
|
||||
])
|
||||
])
|
||||
}
|
||||
},
|
||||
{
|
||||
title: '大小',
|
||||
key: 'size',
|
||||
width: 100
|
||||
},
|
||||
{
|
||||
title: '创建人',
|
||||
key: 'creator',
|
||||
width: 120
|
||||
},
|
||||
{
|
||||
title: '创建时间',
|
||||
key: 'createTime',
|
||||
width: 180
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
key: 'actions',
|
||||
width: 320,
|
||||
render: (row: FileItem) => {
|
||||
return h('div', { style: { display: 'flex', gap: '8px', alignItems: 'center', flexWrap: 'wrap' } }, [
|
||||
h(NButton, {
|
||||
size: 'small',
|
||||
type: 'info',
|
||||
secondary: true,
|
||||
onClick: () => uploadFile(row)
|
||||
}, { default: () => '上传文件' }),
|
||||
h(NButton, {
|
||||
size: 'small',
|
||||
type: 'primary',
|
||||
secondary: true,
|
||||
onClick: () => viewFile(row)
|
||||
}, { default: () => '查看' }),
|
||||
h(NButton, {
|
||||
size: 'small',
|
||||
type: 'error',
|
||||
secondary: true,
|
||||
onClick: () => deleteFile(row)
|
||||
}, { default: () => '删除' }),
|
||||
h(NDropdown, {
|
||||
options: getMoreOptions(row),
|
||||
onSelect: (key: string) => handleMoreAction(key, row)
|
||||
}, {
|
||||
default: () => h(NButton, { size: 'small', secondary: true }, { default: () => '更多' })
|
||||
})
|
||||
])
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
// 文件操作方法
|
||||
const addCourseware = () => {
|
||||
message.info('添加课件功能')
|
||||
}
|
||||
|
||||
const createFolder = () => {
|
||||
message.info('新建文件夹功能')
|
||||
}
|
||||
|
||||
const moveFiles = () => {
|
||||
if (selectedFiles.value.length === 0) return
|
||||
message.info(`移动 ${selectedFiles.value.length} 个文件`)
|
||||
}
|
||||
|
||||
const deleteSelected = () => {
|
||||
if (selectedFiles.value.length === 0) return
|
||||
if (confirm(`确定要删除选中的 ${selectedFiles.value.length} 个文件吗?`)) {
|
||||
selectedFiles.value.forEach((id: number) => {
|
||||
const index = fileList.value.findIndex((f: FileItem) => f.id === id)
|
||||
if (index > -1) {
|
||||
fileList.value.splice(index, 1)
|
||||
}
|
||||
})
|
||||
selectedFiles.value = []
|
||||
message.success('删除成功')
|
||||
}
|
||||
}
|
||||
|
||||
const searchFiles = () => {
|
||||
message.info('搜索文件: ' + searchKeyword.value)
|
||||
}
|
||||
|
||||
const uploadFile = (file: FileItem) => {
|
||||
message.info('上传文件: ' + file.name)
|
||||
}
|
||||
|
||||
const viewFile = (file: FileItem) => {
|
||||
message.info('查看文件: ' + file.name)
|
||||
}
|
||||
|
||||
const deleteFile = (file: FileItem) => {
|
||||
if (confirm('确定要删除这个文件吗?')) {
|
||||
const index = fileList.value.findIndex((f: FileItem) => f.id === file.id)
|
||||
if (index > -1) {
|
||||
fileList.value.splice(index, 1)
|
||||
message.success('删除成功')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const renameFile = (file: FileItem) => {
|
||||
const newName = prompt('请输入新的文件名:', file.name)
|
||||
if (newName && newName !== file.name) {
|
||||
file.name = newName
|
||||
message.success('重命名成功')
|
||||
}
|
||||
}
|
||||
|
||||
const toggleTop = (file: FileItem) => {
|
||||
file.isTop = !file.isTop
|
||||
message.success(file.isTop ? '已置顶' : '已取消置顶')
|
||||
}
|
||||
|
||||
const setPermissions = (file: FileItem) => {
|
||||
message.info('权限设置: ' + file.name)
|
||||
}
|
||||
|
||||
const downloadFile = (file: FileItem) => {
|
||||
message.info('下载文件: ' + file.name)
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.courseware-management {
|
||||
width: 1293px;
|
||||
background: #fff;
|
||||
height: 100%;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
/* 顶部工具栏 */
|
||||
.toolbar {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-right: 25px;
|
||||
background: #fff;
|
||||
padding: 30px 0 20px 30px;
|
||||
border-bottom: 2px solid #F6F6F6;
|
||||
}
|
||||
|
||||
.toolbar h2 {
|
||||
margin: 0;
|
||||
font-size: 18px;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.toolbar-actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.btn {
|
||||
padding: 7px 16px;
|
||||
border: none;
|
||||
font-size: 14px;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
background: #0288D1;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.btn-primary:hover {
|
||||
background: #40a9ff;
|
||||
}
|
||||
|
||||
.btn-new {
|
||||
background-color: #fff;
|
||||
border: 1px solid #0288D1;
|
||||
color: #0288D1;
|
||||
}
|
||||
|
||||
.btn-default {
|
||||
background: white;
|
||||
color: #999999;
|
||||
border: 1px solid #999999;
|
||||
}
|
||||
|
||||
.btn-default:hover {
|
||||
border-color: #1890ff;
|
||||
color: #1890ff;
|
||||
}
|
||||
|
||||
.btn-danger {
|
||||
background: white;
|
||||
color: #FF4D4F;
|
||||
border: 1px solid #FF4D4F;
|
||||
}
|
||||
|
||||
.btn-danger:hover {
|
||||
background: #ff7875;
|
||||
}
|
||||
|
||||
.btn-default:disabled,
|
||||
.btn-danger:disabled {
|
||||
/* opacity: 0.5; */
|
||||
/* cursor: not-allowed; */
|
||||
}
|
||||
|
||||
.search-box {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
border: 1px solid #d9d9d9;
|
||||
border-radius: 4px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.search-box input {
|
||||
border: none;
|
||||
padding: 6px 12px;
|
||||
outline: none;
|
||||
width: 200px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.btn-search {
|
||||
background: #0288D1;
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 6px 16px;
|
||||
cursor: pointer;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.btn-search:hover {
|
||||
background: #0277bd;
|
||||
}
|
||||
|
||||
.table-box {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 1181px;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
/* Naive UI 表格样式定制 */
|
||||
:deep(.file-data-table) {
|
||||
background: #fff;
|
||||
border-radius: 8px;
|
||||
padding: 40px;
|
||||
}
|
||||
|
||||
/* 表格头部样式 */
|
||||
:deep(.file-data-table .n-data-table-thead) {
|
||||
background: #fafafa;
|
||||
}
|
||||
|
||||
:deep(.file-data-table .n-data-table-th) {
|
||||
background: #fafafa;
|
||||
font-weight: 500;
|
||||
color: #062333;
|
||||
font-size: 14px;
|
||||
border-bottom: 1px solid #e8e8e8;
|
||||
padding: 12px 8px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
/* 表格行样式 */
|
||||
:deep(.file-data-table .n-data-table-td) {
|
||||
font-size: 13px;
|
||||
color: #062333;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
padding: 12px 8px;
|
||||
vertical-align: middle;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
/* 名称列左对齐 */
|
||||
:deep(.file-data-table .n-data-table-td[data-col-key="name"]) {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
:deep(.file-data-table .n-data-table-th[data-col-key="name"]) {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
:deep(.file-data-table .n-data-table-tr:hover) {
|
||||
background: #fafafa;
|
||||
}
|
||||
|
||||
/* 复选框样式 */
|
||||
:deep(.file-data-table .n-checkbox) {
|
||||
--n-size: 16px;
|
||||
}
|
||||
|
||||
/* 按钮组样式调整 */
|
||||
:deep(.file-data-table .n-button) {
|
||||
font-size: 12px;
|
||||
height: 28px;
|
||||
padding: 0 12px;
|
||||
margin: 2px;
|
||||
}
|
||||
|
||||
/* 下拉菜单样式 */
|
||||
:deep(.n-dropdown-option) {
|
||||
font-size: 12px;
|
||||
padding: 8px 12px;
|
||||
}
|
||||
|
||||
/* 分页样式 */
|
||||
:deep(.n-data-table__pagination) {
|
||||
position: relative;
|
||||
padding: 20px 0;
|
||||
margin-top: 20px;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
:deep(.n-pagination) {
|
||||
justify-content: center;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/* 当前页面颜色调整 */
|
||||
:deep(.n-pagination-item--active) {
|
||||
background-color: #0088D1 !important;
|
||||
border-color: #0088D1 !important;
|
||||
color: #fff !important;
|
||||
}
|
||||
|
||||
:deep(.n-pagination-item--active:hover) {
|
||||
background-color: #0077B8 !important;
|
||||
border-color: #0077B8 !important;
|
||||
}
|
||||
|
||||
/* 分页按钮悬停效果 */
|
||||
:deep(.n-pagination-item:hover:not(.n-pagination-item--disabled):not(.n-pagination-item--active)) {
|
||||
background-color: #f5f5f5;
|
||||
border-color: #0088D1;
|
||||
color: #0088D1;
|
||||
}
|
||||
|
||||
/* 快速跳转输入框样式 */
|
||||
:deep(.n-pagination-quick-jumper .n-input) {
|
||||
width: 50px;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* 页面大小选择器样式 */
|
||||
:deep(.n-select .n-base-selection) {
|
||||
border-color: #e0e0e0;
|
||||
}
|
||||
|
||||
:deep(.n-select .n-base-selection:hover) {
|
||||
border-color: #0088D1;
|
||||
}
|
||||
|
||||
/* 标签样式 */
|
||||
:deep(.n-tag) {
|
||||
font-size: 10px;
|
||||
height: 18px;
|
||||
line-height: 16px;
|
||||
padding: 0 6px;
|
||||
}
|
||||
|
||||
/* 输入框样式 */
|
||||
:deep(.n-input) {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
/* 表格行样式 */
|
||||
:deep(.file-table-row) {
|
||||
transition: background-color 0.2s;
|
||||
}
|
||||
|
||||
:deep(.file-table-row:hover) {
|
||||
background-color: #f8f9fa;
|
||||
}
|
||||
|
||||
/* 操作按钮容器 */
|
||||
:deep(.file-data-table .n-data-table-td:last-child) {
|
||||
padding: 8px;
|
||||
}
|
||||
|
||||
:deep(.file-data-table .n-data-table-table) {
|
||||
border-spacing: 0;
|
||||
}
|
||||
|
||||
:deep(.file-data-table .n-data-table-th:first-child),
|
||||
:deep(.file-data-table .n-data-table-td:first-child) {
|
||||
padding-left: 16px;
|
||||
}
|
||||
|
||||
:deep(.file-data-table .n-data-table-th:last-child),
|
||||
:deep(.file-data-table .n-data-table-td:last-child) {
|
||||
padding-right: 16px;
|
||||
}
|
||||
|
||||
/* 表格边框 */
|
||||
:deep(.file-data-table .n-data-table-table) {
|
||||
border: 1px solid #f0f0f0;
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
/* 操作按钮样式 - 只有边框和文字颜色,无背景色 */
|
||||
:deep(.file-data-table .n-button--info-type.n-button--secondary) {
|
||||
background-color: transparent !important;
|
||||
border: 1px solid #0288D1;
|
||||
color: #0288D1;
|
||||
}
|
||||
|
||||
:deep(.file-data-table .n-button--info-type.n-button--secondary:hover) {
|
||||
background-color: rgba(32, 128, 240, 0.05) !important;
|
||||
border: 1px solid #0288D1;
|
||||
color: #248DD3;
|
||||
}
|
||||
|
||||
:deep(.file-data-table .n-button--primary-type.n-button--secondary) {
|
||||
background-color: transparent !important;
|
||||
border: 1px solid #0288D1;
|
||||
color: #0288D1;
|
||||
}
|
||||
|
||||
:deep(.file-data-table .n-button--primary-type.n-button--secondary:hover) {
|
||||
background-color: rgba(24, 160, 88, 0.05) !important;
|
||||
border: 1px solid #0288D1;
|
||||
color: #0288D1;
|
||||
}
|
||||
|
||||
:deep(.file-data-table .n-button--error-type.n-button--secondary) {
|
||||
background-color: transparent !important;
|
||||
border: 1px solid #FF4D4F;
|
||||
color: #FD8485;
|
||||
}
|
||||
|
||||
:deep(.file-data-table .n-button--error-type.n-button--secondary:hover) {
|
||||
background-color: rgba(208, 48, 80, 0.05) !important;
|
||||
border: 1px solid #FF4D4F;
|
||||
color: #FD8485;
|
||||
}
|
||||
|
||||
:deep(.file-data-table .n-button--default-type.n-button--secondary) {
|
||||
background-color: transparent !important;
|
||||
border: 1px solid #4165D7;
|
||||
color: #4165D7;
|
||||
}
|
||||
|
||||
:deep(.file-data-table .n-button--default-type.n-button--secondary:hover) {
|
||||
background-color: rgba(0, 0, 0, 0.02) !important;
|
||||
border: 1px solid #4165D7;
|
||||
color: #4165D7;
|
||||
}
|
||||
|
||||
/* 下拉菜单图标悬停效果 */
|
||||
:deep(.n-dropdown-option:hover img[alt="重命名"]) {
|
||||
content: url('/images/teacher/重命名-选中.png');
|
||||
}
|
||||
|
||||
:deep(.n-dropdown-option:hover img[alt="置顶"]) {
|
||||
content: url('/images/teacher/置顶-选中.png');
|
||||
}
|
||||
|
||||
:deep(.n-dropdown-option:hover img[alt="权限设置"]) {
|
||||
content: url('/images/teacher/权限设置-选中.png');
|
||||
}
|
||||
|
||||
:deep(.n-dropdown-option:hover img[alt="下载"]) {
|
||||
content: url('/images/teacher/下载-选中.png');
|
||||
}
|
||||
|
||||
/* 自定义分页器样式 */
|
||||
.custom-pagination {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
background: #fff;
|
||||
padding: 20px 0;
|
||||
margin-top: auto;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.pagination-content {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
margin: 0 auto;
|
||||
padding: 0 10px;
|
||||
}
|
||||
|
||||
.page-numbers {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 0;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.page-number {
|
||||
display: inline-block;
|
||||
min-width: 38px;
|
||||
height: 38px;
|
||||
line-height: 38px;
|
||||
text-align: center;
|
||||
color: #333;
|
||||
text-decoration: none;
|
||||
font-size: 14px;
|
||||
padding: 0 5px;
|
||||
margin: 0 4px;
|
||||
border-radius: 5px;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.page-number-bordered {
|
||||
border: 1px solid #d9d9d9;
|
||||
}
|
||||
|
||||
.page-number.active {
|
||||
background-color: #0088D1;
|
||||
color: white;
|
||||
border-color: #0088D1;
|
||||
}
|
||||
|
||||
.page-number:hover:not(.disabled) {
|
||||
color: #0088D1;
|
||||
border-color: #0088D1;
|
||||
}
|
||||
|
||||
.page-number.disabled {
|
||||
color: #ccc;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.page-number.disabled:hover {
|
||||
color: #ccc;
|
||||
border-color: #d9d9d9;
|
||||
}
|
||||
|
||||
.nav-button {
|
||||
padding: 0 8px;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.nav-button:hover:not(.disabled) {
|
||||
color: #0088D1;
|
||||
}
|
||||
</style>
|
36
src/views/teacher/course/DiscussionManagement.vue
Normal file
@ -0,0 +1,36 @@
|
||||
<template>
|
||||
<div class="discussion-management">
|
||||
<div class="content-placeholder">
|
||||
<h2>讨论管理</h2>
|
||||
<p>讨论管理功能正在开发中...</p>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
// 讨论管理逻辑
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.discussion-management {
|
||||
padding: 20px;
|
||||
background: #fff;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.content-placeholder {
|
||||
text-align: center;
|
||||
padding: 60px 20px;
|
||||
}
|
||||
|
||||
.content-placeholder h2 {
|
||||
font-size: 24px;
|
||||
color: #333;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.content-placeholder p {
|
||||
font-size: 16px;
|
||||
color: #666;
|
||||
}
|
||||
</style>
|
36
src/views/teacher/course/GeneralManagement.vue
Normal file
@ -0,0 +1,36 @@
|
||||
<template>
|
||||
<div class="general-management">
|
||||
<div class="content-placeholder">
|
||||
<h2>综合管理</h2>
|
||||
<p>综合管理功能正在开发中...</p>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
// 综合管理逻辑
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.general-management {
|
||||
padding: 20px;
|
||||
background: #fff;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.content-placeholder {
|
||||
text-align: center;
|
||||
padding: 60px 20px;
|
||||
}
|
||||
|
||||
.content-placeholder h2 {
|
||||
font-size: 24px;
|
||||
color: #333;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.content-placeholder p {
|
||||
font-size: 16px;
|
||||
color: #666;
|
||||
}
|
||||
</style>
|
36
src/views/teacher/course/HomeworkManagement.vue
Normal file
@ -0,0 +1,36 @@
|
||||
<template>
|
||||
<div class="homework-management">
|
||||
<div class="content-placeholder">
|
||||
<h2>作业管理</h2>
|
||||
<p>作业管理功能正在开发中...</p>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
// 作业管理逻辑
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.homework-management {
|
||||
padding: 20px;
|
||||
background: #fff;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.content-placeholder {
|
||||
text-align: center;
|
||||
padding: 60px 20px;
|
||||
}
|
||||
|
||||
.content-placeholder h2 {
|
||||
font-size: 24px;
|
||||
color: #333;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.content-placeholder p {
|
||||
font-size: 16px;
|
||||
color: #666;
|
||||
}
|
||||
</style>
|
36
src/views/teacher/course/NotificationManagement.vue
Normal file
@ -0,0 +1,36 @@
|
||||
<template>
|
||||
<div class="notification-management">
|
||||
<div class="content-placeholder">
|
||||
<h2>通知管理</h2>
|
||||
<p>通知管理功能正在开发中...</p>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
// 通知管理逻辑
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.notification-management {
|
||||
padding: 20px;
|
||||
background: #fff;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.content-placeholder {
|
||||
text-align: center;
|
||||
padding: 60px 20px;
|
||||
}
|
||||
|
||||
.content-placeholder h2 {
|
||||
font-size: 24px;
|
||||
color: #333;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.content-placeholder p {
|
||||
font-size: 16px;
|
||||
color: #666;
|
||||
}
|
||||
</style>
|
36
src/views/teacher/course/PracticeManagement.vue
Normal file
@ -0,0 +1,36 @@
|
||||
<template>
|
||||
<div class="practice-management">
|
||||
<div class="content-placeholder">
|
||||
<h2>练考通管理</h2>
|
||||
<p>练考通管理功能正在开发中...</p>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
// 练考通管理逻辑
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.practice-management {
|
||||
padding: 20px;
|
||||
background: #fff;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.content-placeholder {
|
||||
text-align: center;
|
||||
padding: 60px 20px;
|
||||
}
|
||||
|
||||
.content-placeholder h2 {
|
||||
font-size: 24px;
|
||||
color: #333;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.content-placeholder p {
|
||||
font-size: 16px;
|
||||
color: #666;
|
||||
}
|
||||
</style>
|
36
src/views/teacher/course/QuestionBankManagement.vue
Normal file
@ -0,0 +1,36 @@
|
||||
<template>
|
||||
<div class="question-bank-management">
|
||||
<div class="content-placeholder">
|
||||
<h2>题库管理</h2>
|
||||
<p>题库管理功能正在开发中...</p>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
// 题库管理逻辑
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.question-bank-management {
|
||||
padding: 20px;
|
||||
background: #fff;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.content-placeholder {
|
||||
text-align: center;
|
||||
padding: 60px 20px;
|
||||
}
|
||||
|
||||
.content-placeholder h2 {
|
||||
font-size: 24px;
|
||||
color: #333;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.content-placeholder p {
|
||||
font-size: 16px;
|
||||
color: #666;
|
||||
}
|
||||
</style>
|
36
src/views/teacher/course/StatisticsManagement.vue
Normal file
@ -0,0 +1,36 @@
|
||||
<template>
|
||||
<div class="statistics-management">
|
||||
<div class="content-placeholder">
|
||||
<h2>统计管理</h2>
|
||||
<p>统计管理功能正在开发中...</p>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
// 统计管理逻辑
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.statistics-management {
|
||||
padding: 20px;
|
||||
background: #fff;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.content-placeholder {
|
||||
text-align: center;
|
||||
padding: 60px 20px;
|
||||
}
|
||||
|
||||
.content-placeholder h2 {
|
||||
font-size: 24px;
|
||||
color: #333;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.content-placeholder p {
|
||||
font-size: 16px;
|
||||
color: #666;
|
||||
}
|
||||
</style>
|