新增章节编辑器、课件上传弹窗等功能

This commit is contained in:
Admin 2025-08-22 21:10:29 +08:00
parent 56f5e7077e
commit 2d8339ed4e
18 changed files with 1267 additions and 139 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 544 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 918 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 962 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 378 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 329 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 476 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 627 B

View File

@ -114,14 +114,15 @@ const navigateToCreateCourse = () => {
max-width: 100%;
height: auto;
min-height: 1181px;
margin-right: 28px;
margin: 0; /* 移除所有边距 */
box-sizing: border-box;
padding: 10px; /* 添加内边距以便更好地观察布局 */
padding: 5px; /* 减少内边距 */
position: relative; /* 添加相对定位,使分页按钮的绝对定位基于此容器 */
overflow-x: hidden; /* 防止水平滚动 */
}
.top {
white-space: nowrap; /* 防止链接换行 */
padding: 0 20px;
padding: 0 8px; /* 进一步减少左右padding */
height: 60px;
display: flex;
align-items: center;
@ -219,9 +220,13 @@ const navigateToCreateCourse = () => {
.course-grid {
display: grid;
grid-template-columns: repeat(6, 1fr);
gap: 20px;
grid-template-columns: repeat(4, 1fr); /* 默认4列确保100%缩放下不被截断 */
gap: 12px; /* 进一步减少间距 */
margin-bottom: 20px;
width: 100%;
max-width: 100%;
box-sizing: border-box;
padding: 0 3px; /* 减少内边距 */
}
.course-card {
@ -374,9 +379,11 @@ const navigateToCreateCourse = () => {
aspect-ratio: 150 / 105;
/* 居中 */
display: block;
margin: 5px auto 10px;
margin: 5px auto 20px; /* 增加图片下方间距从10px到20px */
position: relative;
top: -10px;
object-fit: cover;
object-position: center;
}
.course-name{
width: 80%;
@ -391,21 +398,24 @@ const navigateToCreateCourse = () => {
white-space: nowrap; /* 强制文本在一行显示 */
overflow: hidden; /* 隐藏超出容器的文本 */
text-overflow: ellipsis; /* 溢出部分用省略号表示 */
margin-top: -5px;
margin-top: 0; /* 移除负边距,让文字与图片保持正常距离 */
margin-bottom: 15px; /* 添加底部边距,让文字不挨着底部 */
}
/* 分页样式 */
.pagination {
display: flex;
justify-content: center;
position: absolute;
bottom: 61px;
position: relative;
margin-top: 40px;
margin-bottom: 40px;
left: 0;
right: 0;
}
.pagination-content {
width: 703px;
width: 100%;
max-width: 703px;
height: 38px;
display: flex;
justify-content: center;
@ -413,6 +423,7 @@ const navigateToCreateCourse = () => {
margin: 0 auto;
border-radius: 4px;
padding: 0 10px;
box-sizing: border-box;
}
.page-numbers {
@ -472,4 +483,304 @@ const navigateToCreateCourse = () => {
border-color: #1890ff;
}
/* 响应式设计 - 让卡片图片和文字响应式放大 */
/* 超大屏幕 (≥1920px) - 图片和文字放大 */
@media (min-width: 1920px) {
.course-category {
max-width: 1800px;
margin: 0 auto;
padding: 20px;
background: #f5f5f5;
border-radius: 8px;
}
.course-grid {
grid-template-columns: repeat(6, 1fr);
gap: 30px;
}
.course-card {
aspect-ratio: 200 / 220;
}
.course-info img {
width: 85%;
margin: 8px auto 25px;
}
.course-name {
font-size: 16px;
height: 24px;
line-height: 24px;
width: 85%;
margin-bottom: 18px;
}
.section-title {
width: 60px;
height: 24px;
font-size: 14px;
}
.more-icon {
font-size: 20px;
}
}
/* 超大屏幕 (1600px+) */
@media (min-width: 1600px) {
.course-category {
max-width: 1500px;
margin: 0 auto;
padding: 15px;
}
.course-grid {
grid-template-columns: repeat(6, 1fr);
gap: 20px;
}
.course-info img {
width: 82%;
margin: 6px auto 22px;
}
.course-name {
font-size: 15px;
height: 22px;
line-height: 22px;
width: 82%;
margin-bottom: 16px;
}
.section-title {
width: 55px;
height: 22px;
font-size: 13px;
}
}
/* 大屏幕 (1400px-1599px) */
@media (max-width: 1599px) and (min-width: 1400px) {
.course-grid {
grid-template-columns: repeat(5, 1fr);
gap: 15px;
}
}
/* 标准屏幕 (1300px-1399px) */
@media (max-width: 1399px) and (min-width: 1300px) {
.course-grid {
grid-template-columns: repeat(4, 1fr);
gap: 15px;
}
}
/* 中等屏幕 (1200px-1299px) */
@media (max-width: 1299px) and (min-width: 1200px) {
.course-grid {
grid-template-columns: repeat(4, 1fr);
gap: 15px;
}
.course-card {
aspect-ratio: 190 / 210;
}
.course-info img {
width: 80%;
}
.course-name {
font-size: 14px;
width: 80%;
margin-bottom: 15px;
}
}
/* 小屏幕 (900px-1199px) */
@media (max-width: 1199px) and (min-width: 900px) {
.course-grid {
grid-template-columns: repeat(4, 1fr);
gap: 18px;
}
.course-card {
aspect-ratio: 180 / 200;
}
.course-info img {
width: 78%;
margin: 5px auto 18px;
}
.course-name {
font-size: 13px;
height: 18px;
line-height: 18px;
width: 78%;
margin-bottom: 14px;
}
.section-title {
width: 48px;
height: 18px;
font-size: 11px;
}
}
/* 平板 (600px-899px) */
@media (max-width: 899px) and (min-width: 600px) {
.course-category {
padding: 10px;
}
.course-grid {
grid-template-columns: repeat(3, 1fr);
gap: 15px;
}
.course-card {
aspect-ratio: 170 / 190;
}
.course-info img {
width: 75%;
margin: 4px auto 15px;
}
.course-name {
font-size: 12px;
height: 16px;
line-height: 16px;
width: 75%;
margin-bottom: 12px;
}
.section-title {
width: 45px;
height: 16px;
font-size: 10px;
}
.more-icon {
font-size: 16px;
}
/* 调整顶部操作区域 */
.top {
flex-direction: column;
gap: 15px;
}
.actions {
flex-direction: column;
gap: 10px;
}
.search-container {
width: 100%;
}
.search-container input {
width: 70%;
}
}
/* 移动端 (≤599px) */
@media (max-width: 599px) {
.course-category {
padding: 8px;
}
.course-grid {
grid-template-columns: repeat(2, 1fr);
gap: 12px;
}
.course-card {
aspect-ratio: 160 / 180;
}
.course-info img {
width: 70%;
margin: 3px auto 12px;
}
.course-name {
font-size: 11px;
height: 14px;
line-height: 14px;
width: 70%;
margin-bottom: 10px;
}
.section-title {
width: 40px;
height: 14px;
font-size: 9px;
}
.more-icon {
font-size: 14px;
}
/* 顶部区域移动端优化 */
.top {
flex-direction: column;
gap: 10px;
padding: 10px 0;
}
.nav-links {
justify-content: center;
}
.nav-links a {
padding: 8px 12px;
font-size: 14px;
}
.actions {
flex-direction: column;
gap: 8px;
width: 100%;
}
.create-btn {
width: 100%;
padding: 10px;
}
.search-container {
width: 100%;
}
.search-container input {
width: 70%;
padding: 8px;
}
.search-btn {
padding: 8px 12px;
}
/* 分页区域移动端优化 */
.pagination-content {
width: 100%;
padding: 0 5px;
}
.page-numbers {
flex-wrap: wrap;
gap: 5px;
}
.page-number {
padding: 5px 8px;
font-size: 12px;
}
}
</style>

View File

@ -49,23 +49,21 @@
:render-tag="renderInstructorTag"
/>
</div>
<div class="course-sort-section flex-col">
<div class="course-sort-section flex-row">
<div class="course-sort-label">
<span class="required-asterisk">*</span>
<span class="label-text">排序</span>
</div>
<div class="course-sort-input-wrapper">
<n-input
v-model:value="formData.sort"
placeholder="数字越小越排序靠前"
class="form-input"
:bordered="false"
/>
</div>
</div>
</div>
<!-- 排序输入框 -->
<div class="course-sort-input-wrapper">
<n-input
v-model:value="formData.sort"
placeholder="数字越小越排序靠前"
class="form-input"
:bordered="false"
/>
</div>
<!-- 第三行课程开始时间和结束时间 -->
<div class="course-time-row flex-row">
@ -170,23 +168,24 @@
</div>
</div>
<!-- 课程简介标题和工具栏 -->
<div class="course-description-header flex-row">
<div class="course-description-label">
<span class="required-asterisk">*</span>
<span class="label-text">课程简介</span>
<!-- 课程简介标题和编辑器 -->
<div class="course-description-section flex-col">
<div class="course-description-header flex-row">
<div class="course-description-label">
<span class="required-asterisk">*</span>
<span class="label-text">课程简介</span>
</div>
</div>
</div>
<!-- 富文本编辑器区域 -->
<div class="description-editor-container flex-col">
<QuillEditor
v-model="formData.courseDescription"
:height="'280px'"
placeholder="请输入内容…"
class="rich-text-editor"
/>
<!-- 富文本编辑器区域 -->
<div class="description-editor-container flex-col">
<QuillEditor
v-model="formData.courseDescription"
:height="'280px'"
placeholder="请输入内容…"
class="rich-text-editor"
/>
</div>
</div>
<!-- 设置选项区域 -->

View File

@ -71,15 +71,17 @@
.course-create-page {
background-color: rgba(255, 255, 255, 1);
position: relative;
height: 1712px;
overflow: hidden;
min-height: 1712px;
height: auto;
overflow: visible;
}
.course-form-container {
height: 1211px;
min-height: 1211px;
height: auto;
background-size: 100% 100%;
/* width: 1565px; */
position: absolute;
position: relative;
}
/* 第一行:课程名称和分类 */
@ -233,7 +235,10 @@
.course-sort-section {
height: 42px;
margin-left: 159px;
width: 530px;
width: 630px;
display: flex;
align-items: center;
gap: 10px;
}
.course-sort-label {
@ -246,14 +251,14 @@
text-align: right;
white-space: nowrap;
line-height: 18px;
margin-top: 9px;
flex-shrink: 0;
}
/* 排序输入框 */
.course-sort-input-wrapper {
position: absolute;
left: 974px;
top: 102px;
position: relative;
left: 0;
top: 0;
width: 560px;
height: 42px;
background-size: 562px 44px;
@ -262,6 +267,7 @@
padding: 0 12px;
display: flex;
align-items: center;
flex-shrink: 0;
}
.sort-input {
@ -521,12 +527,18 @@
line-height: 18px;
}
/* 课程简介整体区域 */
.course-description-section {
width: 1452px;
margin: 42px 0 0 41px;
}
/* 课程简介标题 */
.course-description-header {
position: relative;
width: 1452px;
width: 100%;
height: 20px;
margin: 42px 0 0 41px;
margin-bottom: -30px;
}
.course-description-label {
@ -563,9 +575,10 @@
height: 336px;
background-size: 100% 100%;
width: 1340px;
position: absolute;
left: 170px;
top: 453px;
position: relative;
left: 0;
top: 0;
margin-left: 129px;
border-radius: 8px;
padding: 13px 19px;
background: white;
@ -617,13 +630,14 @@
/* 设置选项区域 */
.course-settings-section {
margin-top: 20px;
margin: 30px 0 0 41px;
width: 1400px;
}
.stop-on-leave-setting {
width: 350px;
height: 24px;
margin: 334px 0 0 31px;
margin: 20px 0 0 31px;
display: flex;
align-items: center;
justify-content: space-between;
@ -655,7 +669,7 @@
.video-speed-setting {
width: 350px;
height: 24px;
margin: 29px 0 0 63px;
margin: 20px 0 0 63px;
display: flex;
align-items: center;
justify-content: space-between;
@ -664,7 +678,7 @@
.video-text-setting {
width: 350px;
height: 24px;
margin: 31px 0 0 63px;
margin: 20px 0 0 63px;
display: flex;
align-items: center;
justify-content: space-between;
@ -674,7 +688,7 @@
.points-setting-row {
width: 800px;
height: 40px;
margin: 21px 0 140px 95px;
margin: 21px 0 40px 95px;
display: flex;
align-items: center;
gap: 10px;
@ -792,10 +806,10 @@
/* 底部按钮区域 */
.form-action-buttons {
position: absolute;
left: -325px;
top: 1149px;
width: 1917px;
position: relative;
left: 0;
top: 0;
width: 100%;
height: 91px;
background-size: 1920px 139px;
display: flex;
@ -803,6 +817,7 @@
justify-content: flex-end;
padding-right: 100px;
gap: 15px;
margin-top: 60px;
}
.button-spacer {
@ -861,17 +876,120 @@
font-size: 14px;
}
/* 响应式设计 */
@media (max-width: 1920px) {
/* 响应式设计 - 保持原有布局不变,只在特定屏幕尺寸下优化 */
/* 大屏幕优化 (≥1920px) - 让表单居中显示 */
@media (min-width: 1920px) {
.course-create-page {
width: 100%;
min-width: 1200px;
display: flex;
justify-content: center;
align-items: flex-start;
background-color: #f5f5f5;
}
.course-form-container {
background: white;
border-radius: 8px;
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.1);
padding: 40px;
margin: 0 auto;
}
}
@media (max-width: 1400px) {
.course-basic-info-row, .instructor-sort-row, .course-time-row {
width: 90%;
margin-left: 5%;
/* 中等屏幕 (1400px-1919px) - 保持原有布局 */
@media (max-width: 1919px) and (min-width: 1400px) {
.course-create-page {
width: 100%;
min-width: 1400px;
}
}
/* 小屏幕适配 (1200px-1399px) */
@media (max-width: 1399px) and (min-width: 1200px) {
.course-create-page {
width: 100%;
overflow-x: auto;
}
.course-form-container {
min-width: 1200px;
}
/* 稍微缩小一些元素的宽度 */
.course-basic-info-row {
width: 1100px;
margin-left: 50px;
}
.instructor-sort-row {
width: 1150px;
margin-left: 50px;
}
.course-time-row {
width: 1250px;
margin-left: 50px;
}
.course-description-section {
width: 1250px;
margin-left: 50px;
}
.description-editor-container {
width: 1150px;
margin-left: 80px;
}
}
/* 平板适配 (768px-1199px) */
@media (max-width: 1199px) and (min-width: 768px) {
.course-create-page {
width: 100%;
overflow-x: auto;
padding: 20px;
}
.course-form-container {
min-width: 800px;
position: relative;
transform: scale(0.8);
transform-origin: top left;
}
}
/* 移动端适配 (≤767px) */
@media (max-width: 767px) {
.course-create-page {
width: 100%;
overflow-x: auto;
padding: 10px;
}
.course-form-container {
min-width: 600px;
position: relative;
transform: scale(0.6);
transform-origin: top left;
}
/* 调整一些关键元素以适应移动端 */
.form-action-buttons {
position: fixed;
bottom: 0;
left: 0;
right: 0;
background: white;
border-top: 1px solid #e0e0e0;
z-index: 1000;
transform: none;
width: 100%;
justify-content: center;
padding: 15px;
}
.cancel-button,
.submit-button {
transform: scale(1.2);
}
}

View File

@ -204,8 +204,10 @@ import CourseCategory from './CourseComponents/CourseCategory.vue'
.content-container {
flex: 1;
padding: 20px 20px 20px 5px;
padding: 8px 8px 8px 5px; /* 进一步减少padding */
min-height: 100vh;
box-sizing: border-box;
max-width: calc(100vw - 274px - 21px); /* 更精确的宽度计算 */
overflow-x: hidden; /* 防止水平滚动 */
}
</style>

View File

@ -0,0 +1,199 @@
<template>
<div class="modal-overlay">
<div class="modal-container flex-col">
<div class="modal-header flex-row justify-between">
<img
class="modal-icon"
referrerpolicy="no-referrer"
src="/images/teacher/提醒,感叹号_jurassic.png.png"
/>
<span class="modal-title">操作确定</span>
</div>
<div class="modal-body flex-row">
<span class="modal-message">确定要删除该文件夹及里面包括的所有文件</span>
</div>
<div class="modal-footer flex-row">
<div class="button-group flex-row justify-between">
<div class="cancel-button-container flex-col">
<div class="cancel-button flex-col" @click="handleCancel">
<span class="cancel-button-text">取消</span>
</div>
</div>
<div class="confirm-button flex-col" @click="handleConfirm">
<span class="confirm-button-text">确定</span>
</div>
</div>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { defineProps, defineEmits } from 'vue'
// props
const props = defineProps({
show: {
type: Boolean,
required: true
}
})
// emits
const emit = defineEmits(['confirm', 'cancel'])
//
const handleConfirm = () => {
emit('confirm')
}
//
const handleCancel = () => {
emit('cancel')
}
</script>
<style scoped>
.flex-row {
display: flex;
flex-direction: row;
}
.justify-between {
display: flex;
justify-content: space-between;
}
.flex-col {
display: flex;
flex-direction: column;
}
.modal-overlay {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.5);
display: flex;
justify-content: center;
align-items: center;
z-index: 999;
}
.modal-container {
height: 176px;
background: #FFFFFF;
background-size: 100% 100%;
width: 474px;
justify-content: flex-center;
position: relative;
z-index: 1000;
border-radius: 4px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
}
.modal-header {
width: 117px;
height: 28px;
margin: 29px 0 0 41px;
}
.modal-icon {
width: 28px;
height: 28px;
}
.modal-title {
width: 80px;
height: 23px;
overflow-wrap: break-word;
color: rgba(6, 35, 51, 1);
font-size: 20px;
font-family: Helvetica, 'Microsoft YaHei', Arial, sans-serif;
font-weight: normal;
text-align: left;
white-space: nowrap;
line-height: 23px;
margin-top: 3px;
}
.modal-body {
width: 320px;
height: 22px;
margin: 9px 0 0 80px;
}
.modal-message {
width: 320px;
height: 22px;
overflow-wrap: break-word;
color: rgba(6, 35, 51, 1);
font-size: 16px;
font-family: Helvetica, 'Microsoft YaHei', Arial, sans-serif;
font-weight: normal;
text-align: left;
white-space: nowrap;
line-height: 22px;
}
.modal-footer {
width: 147px;
height: 32px;
margin: 27px 0 29px 299px;
}
.button-group {
width: 147px;
height: 32px;
}
.cancel-button-container {
height: 32px;
width: 66px;
}
.cancel-button {
height: 32px;
background-size: 68px 34px;
width: 66px;
cursor: pointer;
}
.cancel-button-text {
width: 32px;
height: 22px;
overflow-wrap: break-word;
color: rgba(6, 35, 51, 1);
font-size: 16px;
font-family: Helvetica, 'Microsoft YaHei', Arial, sans-serif;
font-weight: normal;
text-align: left;
white-space: nowrap;
line-height: 22px;
margin: 5px 0 0 17px;
}
.confirm-button {
height: 32px;
background: #0288D1;
background-size: 100% 100%;
width: 66px;
cursor: pointer;
}
.confirm-button-text {
width: 32px;
height: 22px;
overflow-wrap: break-word;
color: rgba(255, 255, 255, 1);
font-size: 16px;
font-family: Helvetica, 'Microsoft YaHei', Arial, sans-serif;
font-weight: normal;
text-align: left;
white-space: nowrap;
line-height: 22px;
margin: 5px 0 0 17px;
}
</style>

View File

@ -59,6 +59,8 @@ import ExamLibrary from '@/views/teacher/course/ExamPages/ExamLibrary.vue'
import MarkingCenter from '@/views/teacher/course/ExamPages/MarkingCenter.vue'
import AddExam from '@/views/teacher/course/ExamPages/AddExam.vue'
import ChapterEditor from '@/views/teacher/course/ChapterEditor.vue'
// ========== 路由配置 ==========
const routes: RouteRecordRaw[] = [
// 管理后台路由
@ -101,7 +103,8 @@ const routes: RouteRecordRaw[] = [
name: 'CourseAnalysis',
component: CourseAnalysis,
meta: { title: '课程分析' }
}
},
]
},
{
@ -129,6 +132,7 @@ const routes: RouteRecordRaw[] = [
component: ChapterManagement,
meta: { title: '章节管理' }
},
{
path: 'homework',
name: 'HomeworkManagement',
@ -231,10 +235,17 @@ const routes: RouteRecordRaw[] = [
name: 'StudentManagement',
component: StudentManagement,
meta: { title: '学员管理' }
},
{
path: 'chapter-editor-teacher/:courseId',
name: 'ChapterEditor',
component: ChapterEditor,
meta: { title: '章节编辑' }
}
]
},
// 帮助中心
{
path: '/help-center',

View File

@ -1,12 +1,26 @@
<template>
<div class="block_3 flex-row justify-between">
<div class="chapter-editor flex-col">
<div class="block_3 flex-row justify-between" :class="{
'mobile-layout': isMobile,
'tablet-layout': isTablet,
'sidebar-collapsed': sidebarCollapsed
}">
<!-- 移动端菜单按钮 -->
<div class="mobile-menu-toggle" v-if="isMobile" @click="toggleSidebar">
<i class="menu-icon"></i>
</div>
<div class="chapter-editor flex-col" :class="{
'mobile-sidebar': isMobile,
'collapsed': sidebarCollapsed && isMobile
}">
<!-- 移动端关闭按钮 -->
<div class="mobile-close" v-if="isMobile" @click="toggleSidebar">
<i class="close-icon">×</i>
</div>
<div class="header-section flex-row justify-between">
<img class="chapter-icon" referrerpolicy="no-referrer" src="/images/teacher/加号_4.png" />
<span class="section-title">添加章节</span>
</div>
<div class="chapter-item flex-row">
<img class="chapter-arrow-icon" referrerpolicy="no-referrer" src="/images/teacher/路径18.png" />
@ -41,7 +55,11 @@
<div class="chapter-detail-container flex-col">
<div class="chapter-detail-container flex-col" :class="{
'mobile-content': isMobile,
'tablet-content': isTablet,
'sidebar-collapsed-content': sidebarCollapsed && isMobile
}">
<div class="chapter-container">
<div class="chapter-header flex-row justify-between">
<span class="chapter-title-text">第一章</span>
@ -205,6 +223,11 @@ const isMenuVisible = ref(false);
//
const showDeleteModal = ref(false);
//
const isMobile = ref(false);
const isTablet = ref(false);
const sidebarCollapsed = ref(false);
//
const chapterName = ref('课前准备');
@ -304,13 +327,33 @@ const handleClickOutside = (event: Event) => {
}
}
//
const checkScreenSize = () => {
const width = window.innerWidth;
isMobile.value = width <= 768;
isTablet.value = width > 768 && width <= 1024;
//
if (isMobile.value) {
sidebarCollapsed.value = true;
}
};
//
const toggleSidebar = () => {
sidebarCollapsed.value = !sidebarCollapsed.value;
};
//
onMounted(() => {
document.addEventListener('click', handleClickOutside);
checkScreenSize();
window.addEventListener('resize', checkScreenSize);
});
onUnmounted(() => {
document.removeEventListener('click', handleClickOutside);
window.removeEventListener('resize', checkScreenSize);
});
//
@ -347,9 +390,56 @@ const handleLessonFocus = () => {
<style scoped>
.block_3 {
width: 1558px;
height: 1181px;
width: 100%;
max-width: 1558px;
min-height: 100vh;
margin-top: 15px;
position: relative;
transition: all 0.3s ease;
}
/* 移动端菜单按钮 */
.mobile-menu-toggle {
display: none;
position: fixed;
top: 20px;
left: 20px;
z-index: 1001;
background: #0288D1;
color: white;
border: none;
border-radius: 4px;
padding: 10px;
cursor: pointer;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
}
.menu-icon {
font-size: 18px;
font-style: normal;
}
/* 移动端关闭按钮 */
.mobile-close {
display: none;
position: absolute;
top: 15px;
right: 15px;
background: #f5f5f5;
border: none;
border-radius: 50%;
width: 30px;
height: 30px;
cursor: pointer;
align-items: center;
justify-content: center;
z-index: 1002;
}
.close-icon {
font-size: 20px;
font-style: normal;
color: #666;
}
.flex-col {
@ -359,12 +449,15 @@ const handleLessonFocus = () => {
.chapter-editor {
width: 273px;
height: 1181px;
min-height: 100vh;
border: none;
border-right: 1px solid #E6E6E6;
border-radius: 4px;
background: #FFFFFF 100% no-repeat;
background-size: 100% 100%;
transition: transform 0.3s ease;
position: relative;
flex-shrink: 0;
}
.header-section {
@ -579,10 +672,12 @@ const handleLessonFocus = () => {
.chapter-detail-container {
position: relative;
width: 1284px;
height: 1181px;
flex: 1;
min-height: 100vh;
background-size: 100% 100%;
padding: 0 20px;
box-sizing: border-box;
transition: margin-left 0.3s ease;
}
.chapter-header {
@ -1101,16 +1196,354 @@ const handleLessonFocus = () => {
margin-top: 1px;
}
.add-section-text {
width: 64px;
height: 18px;
overflow-wrap: break-word;
color: rgba(2, 136, 209, 1);
font-size: 16px;
font-family: Helvetica, 'Microsoft YaHei', Arial, sans-serif;
font-weight: normal;
text-align: left;
white-space: nowrap;
line-height: 18px;
.add-section-text {
width: 64px;
height: 18px;
overflow-wrap: break-word;
color: rgba(2, 136, 209, 1);
font-size: 16px;
font-family: Helvetica, 'Microsoft YaHei', Arial, sans-serif;
font-weight: normal;
text-align: left;
white-space: nowrap;
line-height: 18px;
}
/* 响应式样式 - 保持原有布局不变,只在特定屏幕尺寸下优化 */
/* 超大屏幕优化 (≥1920px) - 让内容居中显示 */
@media (min-width: 1920px) {
.block_3 {
max-width: 1800px;
margin: 15px auto 0;
background: #f5f5f5;
border-radius: 8px;
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.1);
overflow: hidden;
}
.chapter-detail-container {
padding: 0 40px;
}
.chapter-editor {
width: 320px;
}
}
/* 大屏幕优化 (1600px-1919px) */
@media (max-width: 1919px) and (min-width: 1600px) {
.block_3 {
max-width: 1600px;
margin: 15px auto 0;
}
.chapter-detail-container {
padding: 0 35px;
}
.chapter-editor {
width: 300px;
}
}
/* 中等屏幕适配 (1200px-1599px) */
@media (max-width: 1599px) and (min-width: 1200px) {
.block_3 {
max-width: 1400px;
margin: 15px auto 0;
}
.chapter-detail-container {
padding: 0 25px;
}
}
/* 小屏幕适配 (1025px-1199px) */
@media (max-width: 1199px) and (min-width: 1025px) {
.block_3 {
max-width: 100%;
margin: 10px 20px 0;
}
.chapter-editor {
width: 260px;
}
.chapter-detail-container {
padding: 0 20px;
}
/* 稍微调整一些元素以适应较小屏幕 */
.chapter-name-section {
width: 95%;
}
.lesson-section {
width: 95%;
}
}
/* 移动端样式 (≤768px) */
@media (max-width: 768px) {
.block_3 {
flex-direction: column;
margin-top: 0;
}
.mobile-menu-toggle {
display: flex;
align-items: center;
justify-content: center;
}
.chapter-editor {
position: fixed;
top: 0;
left: 0;
width: 280px;
height: 100vh;
z-index: 1000;
transform: translateX(-100%);
box-shadow: 2px 0 8px rgba(0, 0, 0, 0.15);
padding-top: 60px;
}
.chapter-editor.mobile-sidebar:not(.collapsed) {
transform: translateX(0);
}
.mobile-close {
display: flex;
}
.chapter-detail-container {
width: 100%;
padding: 60px 15px 20px;
margin-left: 0;
}
.chapter-detail-container.mobile-content {
padding-top: 80px;
}
/* 调整表单布局 */
.chapter-name-section {
flex-direction: column;
width: 100%;
margin: 21px 0 0 0;
gap: 10px;
}
.chapter-name-label {
width: 100%;
text-align: left;
margin-top: 0;
}
.chapter-name-input-container {
width: 100%;
}
.lesson-section {
flex-direction: column;
width: 100%;
margin: 0;
gap: 10px;
}
.lesson-label-wrapper {
width: 100%;
text-align: left;
margin: 0;
}
.lesson-input-container {
width: 100%;
margin-left: 0;
}
.courseware-section {
flex-direction: column;
width: 100%;
margin: 0;
gap: 10px;
}
.courseware-label {
width: 100%;
text-align: left;
margin: 0;
}
.courseware-input-container {
width: 100%;
margin: 0;
}
.exam-section {
flex-direction: column;
width: 100%;
margin: 0;
gap: 10px;
}
.exam-label {
width: 100%;
text-align: left;
margin-top: 0;
}
.exam-dropdown-container {
width: 100%;
margin: 0;
}
.homework-section {
flex-direction: column;
width: 100%;
margin: 0;
gap: 10px;
}
.homework-label {
width: 100%;
text-align: left;
margin-top: 0;
}
.homework-input-container {
width: 100%;
margin: 0;
}
.chapter-header {
width: 100%;
margin: 20px 0 0 0;
}
.chapter-divider {
width: 100%;
margin: 20px 0;
}
/* 调整字体大小 */
.section-title {
font-size: 14px;
}
.chapter-title {
font-size: 16px;
}
.content-title {
font-size: 14px;
}
.content-description {
font-size: 14px;
}
}
/* 平板样式 (769px-1024px) */
@media (max-width: 1024px) and (min-width: 769px) {
.block_3 {
max-width: 100%;
}
.chapter-editor {
width: 240px;
}
.chapter-detail-container {
padding: 0 15px;
}
.chapter-name-section {
width: 100%;
margin: 21px 0 0 20px;
}
.chapter-name-input-container {
width: 350px;
}
.lesson-section {
width: 100%;
margin: 0 0 0 20px;
}
.lesson-input-container {
width: 350px;
}
.courseware-section {
width: 100%;
margin: 0 0 0 20px;
}
.courseware-input-container {
width: 350px;
}
.exam-section {
width: 100%;
margin: 0 0 0 20px;
}
.exam-dropdown-container {
width: 350px;
}
.homework-section {
width: 100%;
margin: 0 0 0 20px;
}
.homework-input-container {
width: 350px;
}
.chapter-header {
width: 100%;
margin: 46px 0 0 20px;
}
.chapter-divider {
width: calc(100% - 40px);
margin: 31px auto 0;
}
}
/* 超大屏幕特殊优化 (≥2560px) - 4K显示器优化 */
@media (min-width: 2560px) {
.block_3 {
max-width: 2200px;
margin: 30px auto 0;
padding: 20px;
}
.chapter-editor {
width: 380px;
}
.chapter-detail-container {
padding: 0 60px;
}
/* 增大字体以适应4K屏幕 */
.chapter-title-text {
font-size: 20px;
}
.label-text {
font-size: 18px;
}
.chapter-name-input,
.lesson-input {
font-size: 16px;
height: 45px;
}
}
</style>

View File

@ -63,9 +63,10 @@
import { ref, computed, h } from 'vue'
import { NButton, useMessage, NDataTable, NConfigProvider, zhCN, dateZhCN } from 'naive-ui'
import type { DataTableColumns } from 'naive-ui'
import { useRouter } from 'vue-router'
import { useRouter, useRoute } from 'vue-router'
const router = useRouter()
const route = useRoute()
const message = useMessage()
@ -335,7 +336,13 @@ const toggleChapter = (chapter: Chapter) => {
//
const addChapter = () => {
router.push('/teacher/chapter-editor')
// ID
const courseId = route.params.id
console.log('当前课程ID:', courseId)
console.log('当前路由路径:', route.path)
//
router.push(`/teacher/chapter-editor-teacher/${courseId}`)
}
const importChapters = () => {

View File

@ -8,6 +8,7 @@
<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>
@ -58,11 +59,22 @@
</div>
<!-- 添加课件模态框 -->
<div v-if="showAddCoursewareModal" class="modal-overlay" @click="closeModal">
<div class="modal-wrapper" @click.stop>
<AddCoursewareModal @close="closeModal" />
</div>
<div v-if="showAddCoursewareModal" class="modal-overlay" @click="closeAddCoursewareModal">
<AddCoursewareModal @close="closeAddCoursewareModal" />
</div>
<!-- 上传文件模态框 -->
<div v-if="showUploadFileModal" class="modal-overlay" @click="closeUploadFileModal">
<UploadFileModal @close="closeUploadFileModal" />
</div>
<!-- 删除确认模态框 -->
<DeleteFolderConfirmModal
v-if="showDeleteConfirmModal"
:show="showDeleteConfirmModal"
@confirm="confirmDelete"
@cancel="cancelDelete"
/>
</div>
</template>
@ -71,12 +83,11 @@ import { ref, computed, h, onMounted, onUnmounted } from 'vue'
import { NButton, NDropdown, NTag, useMessage, NDataTable, NConfigProvider, zhCN, dateZhCN } from 'naive-ui'
import type { DataTableColumns, DropdownOption } from 'naive-ui'
import AddCoursewareModal from './AddCoursewareModal.vue'
import UploadFileModal from './UploadFileModal.vue'
import DeleteFolderConfirmModal from '@/components/common/DeleteFolderConfirmModal.vue'
const message = useMessage()
//
const showAddCoursewareModal = ref(false)
//
interface FileItem {
id: number
@ -96,6 +107,17 @@ const searchKeyword = ref('')
//
const selectedFiles = ref<number[]>([])
//
const showAddCoursewareModal = ref(false)
const showUploadFileModal = ref(false)
const showDeleteConfirmModal = ref(false)
//
const itemsToDelete = ref<{ type: 'single' | 'multiple', data: any }>({ type: 'single', data: null })
//
const currentUploadTarget = ref<FileItem | null>(null)
//
const fileList = ref<FileItem[]>([
{
@ -565,6 +587,11 @@ const addCourseware = () => {
showAddCoursewareModal.value = true
}
//
const closeAddCoursewareModal = () => {
showAddCoursewareModal.value = false
}
const createFolder = () => {
message.info('新建文件夹功能')
}
@ -576,16 +603,12 @@ const moveFiles = () => {
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('删除成功')
//
itemsToDelete.value = {
type: 'multiple',
data: selectedFiles.value
}
showDeleteConfirmModal.value = true
}
const searchFiles = () => {
@ -593,7 +616,14 @@ const searchFiles = () => {
}
const uploadFile = (file: FileItem) => {
message.info('上传文件: ' + file.name)
currentUploadTarget.value = file
showUploadFileModal.value = true
}
//
const closeUploadFileModal = () => {
showUploadFileModal.value = false
currentUploadTarget.value = null
}
const viewFile = (file: FileItem) => {
@ -601,13 +631,44 @@ const viewFile = (file: FileItem) => {
}
const deleteFile = (file: FileItem) => {
if (confirm('确定要删除这个文件吗?')) {
//
itemsToDelete.value = {
type: 'single',
data: file
}
showDeleteConfirmModal.value = true
}
//
const confirmDelete = () => {
if (itemsToDelete.value.type === 'multiple') {
//
const idsToDelete = itemsToDelete.value.data as number[]
idsToDelete.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(`成功删除 ${idsToDelete.length} 个文件`)
} else {
//
const file = itemsToDelete.value.data as FileItem
const index = fileList.value.findIndex((f: FileItem) => f.id === file.id)
if (index > -1) {
fileList.value.splice(index, 1)
message.success('删除成功')
}
}
showDeleteConfirmModal.value = false
itemsToDelete.value = { type: 'single', data: null }
}
//
const cancelDelete = () => {
showDeleteConfirmModal.value = false
itemsToDelete.value = { type: 'single', data: null }
}
const renameFile = (file: FileItem) => {
@ -689,11 +750,6 @@ const toggleFolder = (folder: FileItem) => {
folder.expanded = !folder.expanded
}
}
//
const closeModal = () => {
showAddCoursewareModal.value = false
}
</script>
<style scoped>
@ -778,6 +834,20 @@ const closeModal = () => {
/* cursor: not-allowed; */
}
/* 模态框遮罩层样式 */
.modal-overlay {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.5);
display: flex;
justify-content: center;
align-items: center;
z-index: 1000;
}
.search-box {
display: flex;
align-items: center;
@ -1261,26 +1331,4 @@ const closeModal = () => {
white-space: nowrap;
display: inline-block;
}
/* 模态框样式 */
.modal-overlay {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.5);
display: flex;
align-items: center;
justify-content: center;
z-index: 1000;
}
.modal-wrapper {
display: flex;
align-items: center;
justify-content: center;
width: 100%;
height: 100%;
}
</style>