style: 题库首页,课件弹框

This commit is contained in:
guoan 2025-08-23 19:20:14 +08:00
parent 844d5721b7
commit 4d9b5eec2b
23 changed files with 4843 additions and 1995 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 74 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 554 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 515 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 552 B

File diff suppressed because it is too large Load Diff

View File

@ -2,43 +2,54 @@
<div class="course-management-container">
<!-- 左侧导航栏 -->
<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>
<span class="icon icon-status icon-edit"></span>
<span class="icon icon-status icon-delete"></span>
<!-- 全部课程可折叠项 -->
<div class="nav-group">
<router-link to="/teacher/course-management" class="nav-item nav-header"
:class="{ active: $route.path === '/teacher/course-management' }">
<span>全部课程</span><i>(10)</i>
<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>
<div class="arrow-container" @click.prevent="toggleMainNav">
<svg viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg" class="collapse-arrow"
:style="{ transform: isMainNavExpanded ? 'rotate(-90deg)' : 'rotate(90deg)' }">
<path
d="M5.64645 3.14645C5.45118 3.34171 5.45118 3.65829 5.64645 3.85355L9.79289 8L5.64645 12.1464C5.45118 12.3417 5.45118 12.6583 5.64645 12.8536C5.84171 13.0488 6.15829 13.0488 6.35355 12.8536L10.8536 8.35355C11.0488 8.15829 11.0488 7.84171 10.8536 7.64645L6.35355 3.14645C6.15829 2.95118 5.84171 2.95118 5.64645 3.14645Z"
fill="currentColor"></path>
</svg>
</div>
</router-link>
<div class="sub-nav-container" v-show="isMainNavExpanded">
<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>
<span class="icon icon-status icon-edit"></span>
<span class="icon icon-status icon-delete"></span>
</div>
</router-link>
<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>
<span class="icon icon-status icon-edit"></span>
<span class="icon icon-status icon-delete"></span>
</div>
</router-link>
<router-link to="/teacher/course-management/course-analysis" class="nav-item sub-nav-item"
:class="{ active: $route.path === '/teacher/course-management/course-analysis' }">
<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>
</router-link>
<div class="sub-nav-container">
<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>
<span class="icon icon-status icon-edit"></span>
<span class="icon icon-status icon-delete"></span>
</div>
</router-link>
<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>
<span class="icon icon-status icon-edit"></span>
<span class="icon icon-status icon-delete"></span>
</div>
</router-link>
<router-link to="/teacher/course-management/course-analysis" class="nav-item sub-nav-item"
:class="{ active: $route.path === '/teacher/course-management/course-analysis' }">
<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>
@ -55,7 +66,16 @@
</template>
<script setup lang="ts">
import { ref } from 'vue'
import CourseCategory from './CourseComponents/CourseCategory.vue'
//
const isMainNavExpanded = ref(true)
//
const toggleMainNav = () => {
isMainNavExpanded.value = !isMainNavExpanded.value
}
</script>
@ -65,35 +85,65 @@ import CourseCategory from './CourseComponents/CourseCategory.vue'
}
.nav-container {
margin-top: 20px;
margin-top: 8px;
padding: 15px;
width: 274px;
width: 220px;
background-color: #fff;
}
.nav-group {
margin-bottom: 5px;
}
.nav-header {
position: relative;
}
.arrow-container {
position: absolute;
right: 50%;
bottom: 4%;
transform: translateX(50%);
width: 16px;
height: 16px;
cursor: pointer;
z-index: 10;
}
.collapse-arrow {
width: 16px;
height: 16px;
color: #666;
transition: transform 0.2s ease;
}
.collapse-arrow:hover {
color: #0288D1;
}
.sub-nav-container {
margin-left: 10px;
transition: all 0.3s ease;
}
.nav-container .nav-item {
width: 240px;
height: 54px;
margin-top: 5px;
margin-bottom: 13px;
display: flex;
align-items: center;
padding: 0 15px;
border-radius: 8px;
background-color: transparent;
color: #333;
text-decoration: none;
transition: all 0.3s ease;
font-size: 18px;
}
.sub-nav-item {
width: 220px !important;
font-size: 16px !important;
width: 190px;
height: 50px;
margin-top: 5px;
margin-bottom: 13px;
display: flex;
align-items: center;
padding: 0 15px;
border-radius: 8px;
background-color: transparent;
color: #333;
text-decoration: none;
transition: all 0.3s ease;
font-size: 16px;
}
.sub-nav-item {
width: 170px !important;
font-size: 14px !important;
}
.nav-container .nav-item:hover {
@ -142,7 +192,7 @@ import CourseCategory from './CourseComponents/CourseCategory.vue'
background-repeat: no-repeat;
background-position: center;
display: inline-block;
margin-left: 8px;
margin-left: 4px;
opacity: 0; /* 默认不显示图标 */
transition: opacity 0.3s ease, background-image 0.3s ease;
}

View File

@ -278,7 +278,7 @@ const confirmBatchSet = () => {
display: flex;
justify-content: flex-end;
gap: 12px;
margin-top: 20px;
margin-top: 12px;
}
/* 滚动条样式 */

View File

@ -1,10 +1,6 @@
<template>
<n-modal v-model:show="showModal" class="exam-settings-modal" preset="card" :mask-closable="false"
:closable="false" :style="{ width: '1000px' }">
<div class="header">
<span class="header-title">试卷设置</span>
</div>
<n-divider />
<n-modal v-model:show="showModal" class="exam-settings-modal" preset="dialog" title="试卷设置" :mask-closable="false"
:closable="true" :style="{ width: '1000px' }">
<div class="exam-settings-content">
<!-- 试卷名称 -->
@ -25,12 +21,6 @@
</div>
</div>
<!-- 考试人数 -->
<div class="setting-row">
<label class="setting-label">考试人数</label>
<n-input v-model:value="formData.examCount" placeholder="请输入考试人数" class="setting-input" />
</div>
<!-- 试卷分类 -->
<div class="setting-row">
<label class="setting-label">试卷分类</label>
@ -268,10 +258,12 @@
</div>
<!-- 底部按钮 -->
<div class="modal-actions">
<n-button @click="cancelSettings">取消</n-button>
<n-button type="primary" @click="confirmSettings">确定</n-button>
</div>
<template #action>
<div class="modal-actions">
<n-button @click="cancelSettings">取消</n-button>
<n-button type="primary" @click="confirmSettings">确定</n-button>
</div>
</template>
</n-modal>
</template>
@ -298,7 +290,6 @@ interface ExamSettings {
participants: 'all' | 'by_school';
selectedClasses: string[];
instructions: string;
examCount: number;
//
enforceOrder: boolean;
@ -372,7 +363,6 @@ const formData = ref<ExamSettings>({
participants: 'all',
selectedClasses: [],
instructions: '',
examCount: 0,
//
enforceOrder: false,
@ -464,12 +454,6 @@ const confirmSettings = () => {
--n-color: #ffffff;
}
.header-title{
color: #000;
font-weight: 400;
font-size: 20px;
}
.exam-settings-content {
max-height: 800px;
overflow-y: auto;
@ -729,7 +713,6 @@ const confirmSettings = () => {
}
.modal-actions {
margin-top: 20px;
display: flex;
justify-content: flex-end;
gap: 12px;

View File

@ -29,15 +29,15 @@
</template>
<script setup lang="ts">
import { defineProps, defineEmits } from 'vue'
import { defineEmits } from 'vue'
// props
const props = defineProps({
show: {
type: Boolean,
required: true
}
})
// const props = defineProps({
// show: {
// type: Boolean,
// required: true
// }
// })
// emits
const emit = defineEmits(['confirm', 'cancel'])

View File

@ -24,44 +24,6 @@
<img :src="activeNavItem === 0 ? '/images/teacher/课程管理(选中).png' : '/images/teacher/课程管理.png'" alt="">
<span>课程管理</span>
</router-link>
<!-- 考试管理 - 可展开菜单 -->
<div class="nav-item" :class="{ active: activeNavItem === 4 }" @click="toggleExamMenu">
<img :src="activeNavItem === 4 ? '/images/teacher/练考通-选中.png' : '/images/teacher/练考通.png'" alt="">
<span>考试管理</span>
<n-icon class="expand-icon" :class="{ expanded: examMenuExpanded }">
<ChevronDownOutline />
</n-icon>
</div>
<!-- 考试管理子菜单 -->
<div class="submenu-container" :class="{ expanded: examMenuExpanded }">
<router-link
to="/teacher/exam-management/question-management"
class="submenu-item"
:class="{ active: activeSubNavItem === 'question-management' }"
@click="setActiveSubNavItem('question-management')"
>
<span>试题管理</span>
</router-link>
<router-link
to="/teacher/exam-management/exam-library"
class="submenu-item"
:class="{ active: activeSubNavItem === 'exam-library' }"
@click="setActiveSubNavItem('exam-library')"
>
<span>试卷管理</span>
</router-link>
<router-link
to="/teacher/exam-management/marking-center"
class="submenu-item"
:class="{ active: activeSubNavItem === 'marking-center' }"
@click="setActiveSubNavItem('marking-center')"
>
<span>阅卷中心</span>
</router-link>
</div>
<router-link to="/teacher/student-management" class="nav-item" :class="{ active: activeNavItem === 1 }"
@click="setActiveNavItem(1)">
@ -88,7 +50,7 @@
<div class="breadcrumb">
<span class="breadcrumb-separator"></span>
<n-breadcrumb>
<n-breadcrumb-item v-for="(item, index) in breadcrumbItems" :key="index" :href="item.path">
<n-breadcrumb-item v-for="(item, index) in breadcrumbItems" :key="index" :to="item.path">
{{ item.title }}
</n-breadcrumb-item>
</n-breadcrumb>
@ -103,43 +65,17 @@
<script setup lang="ts">
import { ref, onMounted, computed, watch } from 'vue'
import { useRoute } from 'vue-router'
import { ChevronDownOutline } from '@vicons/ionicons5'
const width = window.innerWidth;
const height = window.innerHeight;
console.log(`当前屏幕宽度: ${width}px, 高度: ${height}px`);
//
const activeNavItem = ref(0); // 0: , 1: , 2: , 3: , 4:
const activeSubNavItem = ref(''); //
const examMenuExpanded = ref(false); //
const activeNavItem = ref(0); // 0: , 1: , 2: , 3:
const route = useRoute();
const setActiveNavItem = (index: number) => {
activeNavItem.value = index;
//
if (index !== 4) {
examMenuExpanded.value = false;
activeSubNavItem.value = '';
}
}
//
const toggleExamMenu = () => {
examMenuExpanded.value = !examMenuExpanded.value;
activeNavItem.value = 4;
//
if (examMenuExpanded.value && !activeSubNavItem.value) {
activeSubNavItem.value = 'question-management';
}
}
//
const setActiveSubNavItem = (subItem: string) => {
activeSubNavItem.value = subItem;
activeNavItem.value = 4;
examMenuExpanded.value = true;
}
//
@ -172,35 +108,12 @@ const updateActiveNavItem = () => {
const path = route.path;
if (path.includes('course-management')) {
activeNavItem.value = 0; //
examMenuExpanded.value = false;
activeSubNavItem.value = '';
} else if (path.includes('student-management')) {
activeNavItem.value = 1; //
examMenuExpanded.value = false;
activeSubNavItem.value = '';
} else if (path.includes('my-resources')) {
activeNavItem.value = 2; //
examMenuExpanded.value = false;
activeSubNavItem.value = '';
} else if (path.includes('personal-center')) {
activeNavItem.value = 3; //
examMenuExpanded.value = false;
activeSubNavItem.value = '';
} else if (path.includes('exam-management')) {
activeNavItem.value = 4; //
examMenuExpanded.value = true;
//
if (path.includes('question-management')) {
activeSubNavItem.value = 'question-management';
} else if (path.includes('exam-library')) {
activeSubNavItem.value = 'exam-library';
} else if (path.includes('marking-center')) {
activeSubNavItem.value = 'marking-center';
} else {
//
activeSubNavItem.value = 'question-management';
}
}
}
@ -259,7 +172,7 @@ const updateActiveNavItem = () => {
}
.sidebar-container {
width: 294px;
width: 240px;
height: calc(100vh - var(--top-height, 130px));
background: #FFFFFF;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
@ -274,7 +187,7 @@ const updateActiveNavItem = () => {
@media screen and (max-width: 480px) {
.sidebar-container {
--top-height: 80px;
width: 240px;
width: 200px;
}
}
@ -289,166 +202,138 @@ const updateActiveNavItem = () => {
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-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;
}
@media screen and (max-width: 768px) {
.avatar-container {
height: 180px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.avatar-container img {
margin: 0 0 15px 0;
}
.avatar-text {
margin: 0;
text-align: center;
}
}
@media screen and (max-width: 480px) {
.avatar-container {
height: 150px;
left: 15px;
right: 15px;
height: 1px;
background-color: #E6E6E6;
}
.avatar-container img {
width: 80px;
height: 80px;
/* 圆角 */
border-radius: 50%;
margin-top: 55px;
margin-left: 80px;
margin-right: 80px;
}
.avatar-text {
font-size: 18px;
margin-left: 80px;
height: 31px;
font-family: AppleSystemUIFont;
font-size: 20px;
color: #000000;
line-height: 26px;
text-align: left;
font-style: normal;
text-transform: none;
}
}
.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;
gap: 8px;
}
@media screen and (max-width: 768px) {
@media screen and (max-width: 768px) {
.avatar-container {
height: 180px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.avatar-container img {
margin: 0 0 15px 0;
}
.avatar-text {
margin: 0;
text-align: center;
}
}
@media screen and (max-width: 480px) {
.avatar-container {
height: 150px;
}
.avatar-container img {
width: 80px;
height: 80px;
}
.avatar-text {
font-size: 18px;
}
}
.nav-container {
margin-top: 30px;
/* 鼠标变小手 */
cursor: pointer;
}
.nav-container .nav-item {
margin-left: 15px;
width: 210px;
height: 50px;
margin-bottom: 15px;
/* 圆角 */
border-radius: 10px;
display: flex;
flex-wrap: wrap;
justify-content: center;
margin-top: 15px;
align-items: center;
transition: all 0.3s ease;
gap: 8px;
}
.nav-container .nav-item {
width: 200px;
margin: 0 10px 15px;
@media screen and (max-width: 768px) {
.nav-container {
display: flex;
flex-wrap: wrap;
justify-content: center;
margin-top: 15px;
}
.nav-container .nav-item {
width: 200px;
margin: 0 10px 15px;
}
}
.submenu-container {
margin-left: 10px;
width: 200px;
@media screen and (max-width: 480px) {
.nav-container .nav-item {
width: 150px;
height: 45px;
margin: 0 5px 10px;
}
.nav-container .nav-item img {
margin-left: 20px;
}
}
.submenu-item {
margin-left: 20px;
padding-left: 30px;
font-size: 14px;
.nav-container .nav-item:hover {
background: rgba(102, 183, 227, 0.05);
}
}
@media screen and (max-width: 480px) {
.nav-container .nav-item {
width: 150px;
height: 45px;
margin: 0 5px 10px;
/* 添加激活状态样式 */
.nav-container .nav-item.active {
background: rgba(102, 183, 227, 0.1);
}
.nav-container .nav-item.active span {
color: #0C99DA;
}
.nav-container .nav-item img {
margin-left: 20px;
height: 18px;
margin-left: 40px;
margin-top: 0;
margin-right: 5px;
}
.submenu-container {
margin-left: 5px;
width: 150px;
}
.submenu-item {
margin-left: 10px;
padding-left: 25px;
font-size: 13px;
height: 35px;
}
.expand-icon {
margin-right: 10px;
}
}
.nav-container .nav-item:hover {
background: rgba(102, 183, 227, 0.05);
}
/* 添加激活状态样式 */
.nav-container .nav-item.active {
background: rgba(102, 183, 227, 0.1);
}
.nav-container .nav-item.active span {
color: #0C99DA;
}
.nav-container .nav-item img {
height: 20px;
margin-left: 50px;
margin-top: 0;
margin-right: 5px;
}
.nav-container .nav-item span {
width: 80px;
height: 28px;
.nav-container .nav-item span {
height: 26px;
font-family: AppleSystemUIFont;
font-size: 20px;
font-size: 18px;
color: #666666;
line-height: 23px;
text-align: left;
@ -456,77 +341,12 @@ const updateActiveNavItem = () => {
text-transform: none;
}
/* 展开图标样式 */
.expand-icon {
margin-left: auto;
margin-right: 20px;
transition: transform 0.3s ease;
color: #666;
}
.expand-icon.expanded {
transform: rotate(180deg);
}
/* 子菜单容器 */
.submenu-container {
overflow: hidden;
max-height: 0;
transition: max-height 0.3s ease-out;
margin-left: 20px;
width: 254px;
}
.submenu-container.expanded {
max-height: 200px;
}
/* 子菜单项样式 */
.submenu-item {
display: flex;
align-items: center;
height: 40px;
margin-bottom: 8px;
margin-left: 30px;
padding-left: 40px;
border-radius: 8px;
transition: all 0.3s ease;
text-decoration: none;
color: #666;
font-size: 16px;
position: relative;
}
.submenu-item:hover {
background: rgba(102, 183, 227, 0.05);
}
.submenu-item.active {
background: rgba(102, 183, 227, 0.1);
color: #0C99DA;
}
/* 子菜单项前的小圆点 */
.submenu-item::before {
content: '';
width: 6px;
height: 6px;
background-color: #ccc;
border-radius: 50%;
position: absolute;
left: 20px;
transition: background-color 0.3s ease;
}
.submenu-item.active::before {
background-color: #0C99DA;
}
.router-view-container {
flex: 1;
padding: 20px;
padding: 10px 25px;
background: #F5F7FA;
height: calc(100vh - var(--top-height, 130px));
overflow-y: auto;
}
@media screen and (max-width: 768px) {

View File

@ -440,6 +440,7 @@ const handleConfirm = () => {
/* 字体居中 */
display: flex;
align-items: center;
justify-content: center;
/* 小手 */
cursor: pointer;
border-radius: 4px;

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@ -1,7 +1,7 @@
<template>
<div class="course-editor">
<!-- 左侧导航菜单 -->
<div class="sidebar" v-if="showSidebar">
<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'"
@ -16,11 +16,11 @@
</router-link>
<!-- 作业二级导航 -->
<div class="menu-group">
<div class="menu-header" @click="toggleHomework('homework')">
<div class="menu-header" @click="toggleHomework">
<img :src="$route.path.includes('homework') ? '/images/teacher/作业-选中.png' : '/images/teacher/作业.png'"
alt="作业" />
<span>作业</span>
<i class="n-base-icon" :class="{ 'expanded': subMenuArr.homework }">
<i class="n-base-icon" :class="{ 'expanded': homeworkExpanded }">
<svg viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path
d="M5.64645 3.14645C5.45118 3.34171 5.45118 3.65829 5.64645 3.85355L9.79289 8L5.64645 12.1464C5.45118 12.3417 5.45118 12.6583 5.64645 12.8536C5.84171 13.0488 6.15829 13.0488 6.35355 12.8536L10.8536 8.35355C11.0488 8.15829 11.0488 7.84171 10.8536 7.64645L6.35355 3.14645C6.15829 2.95118 5.84171 2.95118 5.64645 3.14645Z"
@ -28,7 +28,7 @@
</svg>
</i>
</div>
<div class="submenu" v-show="subMenuArr.homework">
<div class="submenu" v-show="homeworkExpanded">
<router-link :to="`/teacher/course-editor/${courseId}/homework/library`" class="submenu-item"
:class="{ active: $route.path.includes('homework/library') }">
<span>作业库</span>
@ -42,10 +42,9 @@
<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>
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'"
@ -93,38 +92,20 @@
<script setup lang="ts">
import { useRoute } from 'vue-router'
import { computed, ref, watch } from 'vue'
import { ref } from 'vue'
const route = useRoute()
// ID
const courseId = route.params.id
//
const subMenuArr = ref({
homework: false,
practice: false
})
//
const homeworkExpanded = ref(false)
// /
const toggleHomework = (e: 'homework' | 'practice') => {
subMenuArr.value[e] = !subMenuArr.value[e]
const toggleHomework = () => {
homeworkExpanded.value = !homeworkExpanded.value
}
//
watch(() => route.path, (newPath) => {
if (newPath.includes('practice')) {
subMenuArr.value.practice = true
}else if (newPath.includes('homework')) {
subMenuArr.value.homework = true
}
}, { immediate: true })
//
const showSidebar = computed(() => {
return route.meta.hideSidebar !== true
})
</script>
<style scoped>
@ -137,7 +118,7 @@ const showSidebar = computed(() => {
/* 左侧导航 */
.sidebar {
width: 273px;
width: 220px;
background: #fff;
border-right: 1px solid #e8e8e8;
padding: 20px 15px;
@ -147,14 +128,14 @@ const showSidebar = computed(() => {
.menu-item {
display: flex;
align-items: center;
padding: 15px 20px;
padding: 12px 15px;
margin-bottom: 5px;
cursor: pointer;
transition: all 0.3s ease;
border-left: 3px solid transparent;
text-decoration: none;
color: inherit;
font-size: 18px;
font-size: 16px;
color: #666;
border-radius: 5px;
}
@ -173,15 +154,15 @@ const showSidebar = computed(() => {
}
.menu-item img {
margin-left: 40px;
margin-left: 30px;
margin-top: 1px;
width: 18px;
height: 18px;
margin-right: 10px;
width: 16px;
height: 16px;
margin-right: 14px;
}
.menu-item span {
font-size: 18px;
font-size: 16px;
color: #666;
}
@ -193,11 +174,11 @@ const showSidebar = computed(() => {
.menu-header {
display: flex;
align-items: center;
padding: 15px 20px;
padding: 12px 15px;
cursor: pointer;
transition: all 0.3s ease;
border-left: 3px solid transparent;
font-size: 18px;
font-size: 16px;
color: #666;
border-radius: 5px;
position: relative;
@ -208,15 +189,15 @@ const showSidebar = computed(() => {
}
.menu-header img {
margin-left: 40px;
margin-left: 30px;
margin-top: 1px;
width: 18px;
height: 18px;
margin-right: 10px;
width: 16px;
height: 16px;
margin-right: 14px;
}
.menu-header span {
font-size: 18px;
font-size: 16px;
color: #666;
flex: 1;
}
@ -239,24 +220,24 @@ const showSidebar = computed(() => {
.submenu-item {
display: flex;
align-items: center;
padding: 12px 20px;
padding: 10px 15px;
margin-bottom: 2px;
cursor: pointer;
transition: all 0.3s ease;
border-left: 3px solid transparent;
text-decoration: none;
color: inherit;
font-size: 16px;
font-size: 14px;
color: #666;
border-radius: 5px;
}
.submenu-item::before {
content: '';
width: 18px;
height: 18px;
margin-left: 40px;
margin-right: 10px;
width: 16px;
height: 16px;
margin-left: 30px;
margin-right: 8px;
flex-shrink: 0;
}
@ -274,12 +255,10 @@ const showSidebar = computed(() => {
}
.submenu-item span {
font-size: 16px;
font-size: 14px;
color: #666;
}
/* 右侧内容区域 */
.content-area {
flex: 1;

View File

@ -21,7 +21,7 @@
<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" />
class="file-data-table" :row-class-name="rowClassName" style="width: 100%" />
</n-config-provider>
<!-- 自定义分页器 -->
@ -69,12 +69,8 @@
</div>
<!-- 删除确认模态框 -->
<DeleteFolderConfirmModal
v-if="showDeleteConfirmModal"
:show="showDeleteConfirmModal"
@confirm="confirmDelete"
@cancel="cancelDelete"
/>
<DeleteFolderConfirmModal v-if="showDeleteConfirmModal" :show="showDeleteConfirmModal" @confirm="confirmDelete"
@cancel="cancelDelete" />
</div>
</template>
@ -411,48 +407,48 @@ const handleResize = () => {
}
//
const responsiveColumns = computed(() => {
const width = screenWidth.value
// const responsiveColumns = computed(() => {
// const width = screenWidth.value
//
const baseColumns = {
selection: 40,
index: 80,
name: 270,
size: 100,
creator: 120,
createTime: 180,
actions: 320
}
// // -
// const baseColumns = {
// selection: 50,
// index: 70,
// name: 400, //
// size: 80, //
// creator: 100, //
// createTime: 160, //
// actions: 300 //
// }
//
if (width < 1200) {
//
return {
selection: 35,
index: 60,
name: Math.max(200, width * 0.25),
size: 80,
creator: 100,
createTime: 140,
actions: Math.max(280, width * 0.3)
}
} else if (width < 1600) {
//
return baseColumns
} else {
//
return {
selection: 45,
index: 90,
name: 320,
size: 110,
creator: 130,
createTime: 200,
actions: 360
}
}
})
// //
// if (width < 1200) {
// //
// return {
// selection: 40,
// index: 60,
// name: Math.max(250, width * 0.35), //
// size: 70,
// creator: 80,
// createTime: 130,
// actions: Math.max(250, width * 0.25)
// }
// } else if (width < 1600) {
// //
// return baseColumns
// } else {
// //
// return {
// selection: 50,
// index: 80,
// name: 500, //
// size: 90,
// creator: 120,
// createTime: 180,
// actions: 350
// }
// }
// })
//
onMounted(() => {
@ -468,18 +464,18 @@ onUnmounted(() => {
const columns = computed((): DataTableColumns<FileItem> => [
{
type: 'selection',
width: responsiveColumns.value.selection
width: 50 //
},
{
title: '序号',
key: 'index',
width: responsiveColumns.value.index,
width: 70, //
render: (_row: FileItem, index: number) => index + 1
},
{
title: '名称',
key: 'name',
width: responsiveColumns.value.name,
//
ellipsis: {
tooltip: true
},
@ -524,22 +520,22 @@ const columns = computed((): DataTableColumns<FileItem> => [
{
title: '大小',
key: 'size',
width: responsiveColumns.value.size
width: 80 //
},
{
title: '创建人',
key: 'creator',
width: responsiveColumns.value.creator
width: 100 //
},
{
title: '创建时间',
key: 'createTime',
width: responsiveColumns.value.createTime
width: 160 //
},
{
title: '操作',
key: 'actions',
width: responsiveColumns.value.actions,
width: 300, //
render: (row: FileItem) => {
const buttons = []
@ -891,6 +887,23 @@ const toggleFolder = (folder: FileItem) => {
padding: 40px;
}
/* 表格自适应布局 */
:deep(.file-data-table .n-data-table-wrapper) {
overflow-x: auto;
}
:deep(.file-data-table .n-data-table-base-table) {
width: 100%;
table-layout: fixed;
}
/* 名称列自动占用剩余空间 */
:deep(.file-data-table .n-data-table-td[data-col-key="name"]),
:deep(.file-data-table .n-data-table-th[data-col-key="name"]) {
max-width: 0;
width: auto;
}
/* 表格头部样式 */
:deep(.file-data-table .n-data-table-thead) {
background: #fafafa;
@ -914,9 +927,30 @@ const toggleFolder = (folder: FileItem) => {
padding: 12px 8px;
vertical-align: middle;
text-align: center;
word-break: break-word;
white-space: normal;
}
/* 名称列左对齐 */
/* 序号列居中对齐 */
:deep(.file-data-table .n-data-table-td[data-col-key="index"]) {
text-align: center !important;
}
/* 隐藏序号列的展开占位符,避免影响居中 */
:deep(.file-data-table .n-data-table-td[data-col-key="index"] .n-data-table-expand-placeholder) {
display: none;
}
/* 确保序号列的展开触发器也居中 */
:deep(.file-data-table .n-data-table-td[data-col-key="index"] .n-data-table-expand-trigger) {
margin-right: 4px;
}
:deep(.file-data-table .n-data-table-th[data-col-key="index"]) {
text-align: center !important;
}
/* 名称列居中对齐 */
:deep(.file-data-table .n-data-table-td[data-col-key="name"]) {
text-align: center;
}
@ -925,6 +959,21 @@ const toggleFolder = (folder: FileItem) => {
text-align: center;
}
/* 其他列居中对齐 */
:deep(.file-data-table .n-data-table-td[data-col-key="size"]),
:deep(.file-data-table .n-data-table-td[data-col-key="creator"]),
:deep(.file-data-table .n-data-table-td[data-col-key="createTime"]),
:deep(.file-data-table .n-data-table-td[data-col-key="actions"]) {
text-align: center !important;
}
:deep(.file-data-table .n-data-table-th[data-col-key="size"]),
:deep(.file-data-table .n-data-table-th[data-col-key="creator"]),
:deep(.file-data-table .n-data-table-th[data-col-key="createTime"]),
:deep(.file-data-table .n-data-table-th[data-col-key="actions"]) {
text-align: center !important;
}
:deep(.file-data-table .n-data-table-tr:hover) {
background: #fafafa;
}
@ -1212,6 +1261,11 @@ const toggleFolder = (folder: FileItem) => {
padding: 20px;
}
/* 确保表格横向滚动正常 */
:deep(.file-data-table .n-data-table-wrapper) {
overflow-x: auto;
-webkit-overflow-scrolling: touch;
}
/* 操作按钮在小屏幕下换行 */
:deep(.file-data-table .n-data-table-td:last-child .n-button) {
margin: 2px;

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,210 @@
<template>
<div>
<div class="exam-library-container">
<div class="header-section">
<h1 class="title">试卷库</h1>
<n-space class="actions-group">
<n-button type="primary" @click="handleAddExam">添加试卷</n-button>
<n-button ghost>导入</n-button>
<n-button ghost>导出</n-button>
<n-button type="error" ghost>删除</n-button>
<n-input placeholder="请输入想要搜索的内容" />
<n-button type="primary">搜索</n-button>
</n-space>
</div>
<n-data-table :columns="columns" :data="examData" :row-key="(row: Exam) => row.id"
@update:checked-row-keys="handleCheck" class="exam-table" :single-line="false" />
<div class="pagination-container">
<n-pagination v-model:page="currentPage" :page-count="totalPages" />
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { h, ref, VNode } from 'vue';
import { NButton, NSpace, useMessage, NDataTable, NPagination, NInput } from 'naive-ui';
import type { DataTableColumns } from 'naive-ui';
import { useRouter } from 'vue-router';
const router = useRouter();
//
type Exam = {
id: number;
name: string;
category: '练习' | '考试';
questionCount: number;
chapter: string;
totalScore: number;
difficulty: '易' | '中' | '难';
status: '发布中' | '未发布' | '已结束';
startTime: string;
endTime: string;
creator: string;
creationTime: string;
};
//
const message = useMessage();
//
const createColumns = ({
handleAction,
}: {
handleAction: (action: string, rowData: Exam) => void;
}): DataTableColumns<Exam> => {
return [
{
type: 'selection',
},
{
title: '序号',
key: 'id',
width: 60,
align: 'center',
},
{
title: '试卷名称',
key: 'name',
},
{
title: '分类',
key: 'category',
},
{
title: '题量',
key: 'questionCount',
},
{
title: '所属章节',
key: 'chapter',
},
{
title: '总分',
key: 'totalScore',
},
{
title: '难度',
key: 'difficulty',
},
{
title: '状态',
key: 'status',
},
{
title: '起止时间',
key: 'startTime',
render(row) {
return `${row.startTime} - ${row.endTime}`;
},
},
{
title: '创建人',
key: 'creator',
},
{
title: '创建时间',
key: 'creationTime',
},
{
title: '操作',
key: 'actions',
render(row) {
const buttons: VNode[] = [];
if (row.status === '发布中') {
buttons.push(
h(NButton, { size: 'small', type: 'primary', ghost: true, style: 'margin: 0 3px;', onClick: () => handleAction('批阅', row) }, { default: () => '批阅' })
);
} else if (row.status === '未发布') {
buttons.push(
h(NButton, { size: 'small', type: 'primary', style: 'margin: 0 3px;', onClick: () => handleAction('发布', row) }, { default: () => '发布' })
);
}
buttons.push(
h(NButton, { size: 'small', type: 'primary', ghost: true, style: 'margin: 0 3px;', onClick: () => handleAction('编辑', row) }, { default: () => '编辑' })
);
buttons.push(
h(NButton, { size: 'small', type: 'error', ghost: true, style: 'margin: 0 3px;', onClick: () => handleAction('删除', row) }, { default: () => '删除' })
);
return h(NSpace, {}, { default: () => buttons });
},
},
];
};
//
const examData = ref<Exam[]>([
{ id: 1, name: '试卷名称试卷名称', category: '练习', questionCount: 100, chapter: '第一节 开课前准备', totalScore: 150, difficulty: '易', status: '发布中', startTime: '2025.07.25 09:20', endTime: '2025.07.25 09:20', creator: '王建国', creationTime: '2025.07.25 9:20' },
{ id: 2, name: '试卷名称试卷名称', category: '考试', questionCount: 100, chapter: '第一节 开课前准备', totalScore: 150, difficulty: '易', status: '未发布', startTime: '2025.07.25 09:20', endTime: '2025.07.25 09:20', creator: '王建国', creationTime: '2025.07.25 9:20' },
{ id: 3, name: '试卷名称试卷名称', category: '练习', questionCount: 100, chapter: '第一节 开课前准备', totalScore: 150, difficulty: '易', status: '发布中', startTime: '2025.07.25 09:20', endTime: '2025.07.25 09:20', creator: '王建国', creationTime: '2025.07.25 9:20' },
{ id: 4, name: '试卷名称试卷名称', category: '考试', questionCount: 100, chapter: '第一节 开课前准备', totalScore: 150, difficulty: '易', status: '发布中', startTime: '2025.07.25 09:20', endTime: '2025.07.25 09:20', creator: '王建国', creationTime: '2025.07.25 9:20' },
{ id: 5, name: '试卷名称试卷名称', category: '练习', questionCount: 100, chapter: '第一节 开课前准备', totalScore: 150, difficulty: '易', status: '发布中', startTime: '2025.07.25 09:20', endTime: '2025.07.25 09:20', creator: '王建国', creationTime: '2025.07.25 9:20' },
{ id: 6, name: '试卷名称试卷名称', category: '考试', questionCount: 100, chapter: '第一节 开课前准备', totalScore: 150, difficulty: '易', status: '未发布', startTime: '2025.07.25 09:20', endTime: '2025.07.25 09:20', creator: '王建国', creationTime: '2025.07.25 9:20' },
{ id: 7, name: '试卷名称试卷名称', category: '练习', questionCount: 100, chapter: '第一节 开课前准备', totalScore: 150, difficulty: '易', status: '发布中', startTime: '2025.07.25 09:20', endTime: '2025.07.25 09:20', creator: '王建国', creationTime: '2025.07.25 9:20' },
{ id: 8, name: '试卷名称试卷名称', category: '考试', questionCount: 100, chapter: '第一节 开课前准备', totalScore: 150, difficulty: '易', status: '未发布', startTime: '2025.07.25 09:20', endTime: '2025.07.25 09:20', creator: '王建国', creationTime: '2025.07.25 9:20' },
]);
const columns = createColumns({
handleAction: (action, row) => {
message.info(`执行操作: ${action} on row ${row.id}`);
},
});
const checkedRowKeys = ref<Array<string | number>>([]);
const handleCheck = (rowKeys: Array<string | number>) => {
checkedRowKeys.value = rowKeys;
};
//
const currentPage = ref(1);
const totalPages = ref(29); //
const handleAddExam = () => {
//
router.push({ name: 'AddExam' });
};
</script>
<style scoped>
.exam-library-container {
background-color: #fff;
padding: 20px;
border-radius: 8px;
}
.header-section {
display: flex;
justify-content: space-between;
align-items: center;
padding-bottom: 20px;
border-bottom: 1px solid #E6E6E6;
}
.title {
margin: 0;
font-size: 20px;
font-weight: 500;
}
.actions-group {
display: flex;
align-items: center;
gap: 10px;
}
.exam-table {
margin-top: 20px;
}
.pagination-container {
display: flex;
justify-content: center;
margin-top: 20px;
}
</style>

View File

@ -0,0 +1,11 @@
<template>
<div>
<h1>阅卷中心</h1>
</div>
</template>
<script setup lang="ts">
</script>
<style scoped>
</style>

View File

@ -19,7 +19,7 @@
<n-config-provider :locale="zhCN" :date-locale="dateZhCN">
<n-data-table :columns="columns" :data="sortedHomeworkList" :row-key="rowKey"
:checked-row-keys="selectedHomework" @update:checked-row-keys="handleCheck" :bordered="false"
:single-line="false" size="medium" class="homework-data-table" />
:single-line="false" size="medium" class="homework-data-table" scroll-x="true" />
</n-config-provider>
</div>
@ -94,51 +94,51 @@ const handleResize = () => {
}
//
const responsiveColumns = computed(() => {
const width = screenWidth.value
// const responsiveColumns = computed(() => {
// const width = screenWidth.value
//
const baseColumns = {
selection: 50,
index: 80,
name: 200,
chapter: 180,
class: 150,
creator: 120,
createTime: 180,
actions: 200
}
// //
// const baseColumns = {
// selection: 50,
// index: 80,
// name: 200,
// chapter: 180,
// class: 150,
// creator: 120,
// createTime: 180,
// actions: 200
// }
//
if (width < 1200) {
//
return {
selection: 40,
index: 60,
name: Math.max(150, width * 0.2),
chapter: Math.max(120, width * 0.15),
class: Math.max(100, width * 0.12),
creator: 80,
createTime: 140,
actions: Math.max(160, width * 0.2)
}
} else if (width < 1600) {
//
return baseColumns
} else {
//
return {
selection: 55,
index: 90,
name: 250,
chapter: 220,
class: 180,
creator: 140,
createTime: 200,
actions: 240
}
}
})
// //
// if (width < 1200) {
// //
// return {
// selection: 40,
// index: 60,
// name: Math.max(150, width * 0.2),
// chapter: Math.max(120, width * 0.15),
// class: Math.max(100, width * 0.12),
// creator: 80,
// createTime: 140,
// actions: Math.max(160, width * 0.2)
// }
// } else if (width < 1600) {
// //
// return baseColumns
// } else {
// //
// return {
// selection: 55,
// index: 90,
// name: 250,
// chapter: 220,
// class: 180,
// creator: 140,
// createTime: 200,
// actions: 240
// }
// }
// })
//
onMounted(() => {
@ -272,22 +272,24 @@ const handleCheck = (keys: number[]) => {
selectedHomework.value = keys
}
//
// - 使 minWidth
const columns = computed((): DataTableColumns<HomeworkItem> => [
{
type: 'selection',
width: responsiveColumns.value.selection
minWidth: 50
},
{
title: '序号',
key: 'index',
width: responsiveColumns.value.index,
minWidth: 60,
width: 60,
render: (_, index) => index + 1
},
{
title: '作业名称',
key: 'name',
width: responsiveColumns.value.name,
minWidth: 300,
width: 300,
ellipsis: {
tooltip: true
},
@ -301,7 +303,8 @@ const columns = computed((): DataTableColumns<HomeworkItem> => [
{
title: '所属章节',
key: 'chapter',
width: responsiveColumns.value.chapter,
minWidth: 200,
width: 200,
ellipsis: {
tooltip: true
}
@ -309,7 +312,8 @@ const columns = computed((): DataTableColumns<HomeworkItem> => [
{
title: '绑定班级',
key: 'class',
width: responsiveColumns.value.class,
minWidth: 150,
width: 150,
ellipsis: {
tooltip: true
}
@ -317,29 +321,34 @@ const columns = computed((): DataTableColumns<HomeworkItem> => [
{
title: '创建人',
key: 'creator',
width: responsiveColumns.value.creator
minWidth: 100,
width: 100
},
{
title: '创建时间',
key: 'createTime',
width: responsiveColumns.value.createTime
minWidth: 140,
width: 140
},
{
title: '操作',
key: 'actions',
width: responsiveColumns.value.actions,
minWidth: 200,
width: 200,
render: (row) => {
return h('div', { style: 'display: flex; gap: 8px; align-items: center;' }, [
return h('div', { style: 'display: flex; gap: 6px; align-items: center; justify-content: center; flex-wrap: wrap;' }, [
h(NButton, {
size: 'small',
type: 'info',
secondary: true,
style: 'min-width: 50px;',
onClick: () => editHomework(row.id)
}, { default: () => '编辑' }),
h(NButton, {
size: 'small',
type: 'error',
secondary: true,
style: 'min-width: 50px;',
onClick: () => deleteHomework(row.id)
}, { default: () => '删除' }),
h(NDropdown, {
@ -371,7 +380,8 @@ const columns = computed((): DataTableColumns<HomeworkItem> => [
default: () => h(NButton, {
size: 'small',
type: 'info',
secondary: true
secondary: true,
style: 'min-width: 50px;'
}, { default: () => '更多' })
})
])

View File

@ -1,40 +1,12 @@
<template>
<div class="practice-management">
<div class="content-placeholder">
<h2>考试管理</h2>
<p>考试管理功能正在修改...</p>
<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>
<script setup lang="ts">
//
</script>

View File

@ -1,36 +1,850 @@
<template>
<div class="question-bank-management">
<div class="content-placeholder">
<h2>题库管理</h2>
<p>题库管理功能正在开发中...</p>
<!-- 顶部操作栏 -->
<div class="toolbar">
<h2>全部试题</h2>
<div class="toolbar-actions">
<button class="btn btn-primary" @click="addQuestion">添加试题</button>
<button class="btn btn-new" @click="importQuestions">导入</button>
<div class="dropdown-container">
<button class="btn btn-new" @click="toggleMoreDropdown">更多</button>
<div v-if="showMoreDropdown" class="more-dropdown">
<div class="dropdown-item" @click="categorySettings">分类设置</div>
<div class="dropdown-item" @click="exportQuestions">导出试题</div>
</div>
</div>
<button class="btn btn-danger" @click="deleteSelected">删除</button>
<select v-model="selectedCategory" class="category-select" @change="filterByCategory">
<option value="">分类</option>
<option value="试题分类">试题分类</option>
<option value="分类设置">分类设置</option>
</select>
<div class="search-box">
<input type="text" placeholder="请输入关键词" v-model="searchKeyword" @keyup.enter="searchQuestions" />
<button class="btn btn-search" @click="searchQuestions">搜索</button>
</div>
</div>
</div>
<!-- 题目列表表格 -->
<div class="table-box">
<n-config-provider :locale="zhCN" :date-locale="dateZhCN">
<n-data-table :columns="columns" :data="filteredQuestions" :row-key="rowKey"
:checked-row-keys="selectedQuestions" @update:checked-row-keys="handleCheck" :bordered="false"
:single-line="false" size="medium" class="question-data-table" :row-class-name="rowClassName" />
</n-config-provider>
</div>
</div>
</template>
<script setup lang="ts">
//
import { ref, computed, h, onMounted, onUnmounted } from 'vue'
import {
NButton,
NDataTable,
NConfigProvider,
NTag,
useMessage,
zhCN,
dateZhCN
} from 'naive-ui'
import type { DataTableColumns } from 'naive-ui'
const message = useMessage()
//
interface Question {
id: number
content: string
type: '单选题' | '填空题' | '多选题' | '判断题' | '简答题'
category: string
difficulty: '易' | '中' | '难'
score: number
creator: string
createTime: string
}
//
const screenWidth = ref(window.innerWidth)
const searchKeyword = ref('')
const selectedCategory = ref('')
const selectedQuestions = ref<number[]>([])
const showMoreDropdown = ref(false)
//
const questionList = ref<Question[]>([
{
id: 1,
content: '在数据库的三级模式结构中,内模式有...',
type: '单选题',
category: '试题分类',
difficulty: '易',
score: 10,
creator: '王建国',
createTime: '2025.08.20 09:20'
},
{
id: 2,
content: '在数据库的三级模式结构中,内模式有...',
type: '填空题',
category: '试题分类',
difficulty: '易',
score: 10,
creator: '王建国',
createTime: '2025.08.20 09:20'
},
{
id: 3,
content: '在数据库的三级模式结构中,内模式有...',
type: '单选题',
category: '试题分类',
difficulty: '易',
score: 10,
creator: '王建国',
createTime: '2025.08.20 09:20'
},
{
id: 4,
content: '在数据库的三级模式结构中,内模式有...',
type: '填空题',
category: '试题分类',
difficulty: '易',
score: 10,
creator: '王建国',
createTime: '2025.08.20 09:20'
},
{
id: 5,
content: '在数据库的三级模式结构中,内模式有...',
type: '单选题',
category: '试题分类',
difficulty: '易',
score: 10,
creator: '王建国',
createTime: '2025.08.20 09:20'
},
{
id: 6,
content: '在数据库的三级模式结构中,内模式有...',
type: '填空题',
category: '试题分类',
difficulty: '易',
score: 10,
creator: '王建国',
createTime: '2025.08.20 09:20'
},
{
id: 7,
content: '在数据库的三级模式结构中,内模式有...',
type: '单选题',
category: '试题分类',
difficulty: '易',
score: 10,
creator: '王建国',
createTime: '2025.08.20 09:20'
},
{
id: 8,
content: '在数据库的三级模式结构中,内模式有...',
type: '填空题',
category: '试题分类',
difficulty: '易',
score: 10,
creator: '王建国',
createTime: '2025.08.20 09:20'
},
{
id: 9,
content: '在数据库的三级模式结构中,内模式有...',
type: '单选题',
category: '试题分类',
difficulty: '易',
score: 10,
creator: '王建国',
createTime: '2025.08.20 09:20'
},
{
id: 10,
content: '在数据库的三级模式结构中,内模式有...',
type: '填空题',
category: '试题分类',
difficulty: '易',
score: 10,
creator: '王建国',
createTime: '2025.08.20 09:20'
},
{
id: 11,
content: '在数据库的三级模式结构中,内模式有...',
type: '单选题',
category: '试题分类',
difficulty: '易',
score: 10,
creator: '王建国',
createTime: '2025.08.20 09:20'
},
{
id: 12,
content: '在数据库的三级模式结构中,内模式有...',
type: '填空题',
category: '试题分类',
difficulty: '易',
score: 10,
creator: '王建国',
createTime: '2025.08.20 09:20'
},
{
id: 13,
content: '在数据库的三级模式结构中,内模式有...',
type: '单选题',
category: '试题分类',
difficulty: '易',
score: 10,
creator: '王建国',
createTime: '2025.08.20 09:20'
},
{
id: 14,
content: '在数据库的三级模式结构中,内模式有...',
type: '填空题',
category: '试题分类',
difficulty: '易',
score: 10,
creator: '王建国',
createTime: '2025.08.20 09:20'
},
{
id: 15,
content: '在数据库的三级模式结构中,内模式有...',
type: '单选题',
category: '试题分类',
difficulty: '易',
score: 10,
creator: '王建国',
createTime: '2025.08.20 09:20'
}
])
//
const responsiveColumns = computed(() => {
if (screenWidth.value < 1200) {
return {
selection: 40,
index: 60,
content: 200,
type: 80,
category: 100,
difficulty: 60,
score: 60,
creator: 80,
createTime: 120,
actions: 120
}
} else if (screenWidth.value < 1600) {
return {
selection: 50,
index: 80,
content: 280,
type: 100,
category: 120,
difficulty: 80,
score: 80,
creator: 100,
createTime: 150,
actions: 160
}
} else {
return {
selection: 50,
index: 80,
content: 350,
type: 120,
category: 140,
difficulty: 100,
score: 100,
creator: 120,
createTime: 180,
actions: 200
}
}
})
//
const handleResize = () => {
screenWidth.value = window.innerWidth
}
onMounted(() => {
window.addEventListener('resize', handleResize)
})
onUnmounted(() => {
window.removeEventListener('resize', handleResize)
})
//
const columns = computed((): DataTableColumns<Question> => [
{
type: 'selection',
width: responsiveColumns.value.selection
},
{
title: '序号',
key: 'index',
width: responsiveColumns.value.index,
render: (_row: Question, index: number) => index + 1
},
{
title: '试题内容',
key: 'content',
width: responsiveColumns.value.content,
ellipsis: {
tooltip: true
}
},
{
title: '题型',
key: 'type',
width: responsiveColumns.value.type,
render: (row: Question) => {
return h(NTag, {
type: 'info',
style: {
backgroundColor: 'transparent !important',
color: '#062333 !important',
fontSize: '12px !important',
border: 'none !important',
'--n-border': 'none !important',
'--n-color': 'transparent !important'
}
}, () => row.type)
}
},
{
title: '分类',
key: 'category',
width: responsiveColumns.value.category,
ellipsis: {
tooltip: true
}
},
{
title: '难度',
key: 'difficulty',
width: responsiveColumns.value.difficulty,
render: (row: Question) => {
return h(NTag, {
type: 'info',
style: {
backgroundColor: 'transparent !important',
color: '#062333 !important',
fontSize: '12px !important',
border: 'none !important',
'--n-border': 'none !important',
'--n-color': 'transparent !important'
}
}, () => row.difficulty)
}
},
{
title: '分值',
key: 'score',
width: responsiveColumns.value.score
},
{
title: '创建人',
key: 'creator',
width: responsiveColumns.value.creator,
ellipsis: {
tooltip: true
}
},
{
title: '创建时间',
key: 'createTime',
width: responsiveColumns.value.createTime,
ellipsis: {
tooltip: true
}
},
{
title: '操作',
key: 'actions',
width: responsiveColumns.value.actions,
render: (row: Question) => {
return h('div', { style: { display: 'flex', gap: '8px' } }, [
h(NButton, {
size: 'small',
type: 'info',
ghost: true,
onClick: () => editQuestion(row)
}, () => '编辑'),
h(NButton, {
size: 'small',
type: 'error',
ghost: true,
onClick: () => deleteQuestion(row)
}, () => '删除')
])
}
}
])
//
const filteredQuestions = computed(() => {
let filtered = questionList.value
//
if (selectedCategory.value) {
filtered = filtered.filter(q => q.category === selectedCategory.value)
}
//
if (searchKeyword.value) {
filtered = filtered.filter(q =>
q.content.includes(searchKeyword.value) ||
q.creator.includes(searchKeyword.value)
)
}
return filtered
})
//
// computed
//
const rowKey = (row: Question) => row.id
//
const rowClassName = (row: Question) => {
return selectedQuestions.value.includes(row.id) ? 'selected-row' : ''
}
//
const handleCheck = (keys: number[]) => {
selectedQuestions.value = keys
}
//
const filterByCategory = () => {
//
}
const searchQuestions = () => {
message.info('搜索: ' + searchKeyword.value)
}
const addQuestion = () => {
message.info('添加试题功能')
}
const importQuestions = () => {
message.info('导入试题功能')
}
const toggleMoreDropdown = () => {
showMoreDropdown.value = !showMoreDropdown.value
}
const categorySettings = () => {
showMoreDropdown.value = false
message.info('分类设置功能')
}
const exportQuestions = () => {
showMoreDropdown.value = false
message.info('导出试题功能')
}
const deleteSelected = () => {
if (selectedQuestions.value.length === 0) return
if (confirm(`确定要删除选中的 ${selectedQuestions.value.length} 道试题吗?`)) {
selectedQuestions.value.forEach((id: number) => {
const index = questionList.value.findIndex((q: Question) => q.id === id)
if (index > -1) {
questionList.value.splice(index, 1)
}
})
selectedQuestions.value = []
message.success('删除成功')
}
}
const editQuestion = (question: Question) => {
message.info('编辑试题: ' + question.content.substring(0, 20) + '...')
}
const deleteQuestion = (question: Question) => {
if (confirm('确定要删除这道试题吗?')) {
const index = questionList.value.findIndex(q => q.id === question.id)
if (index > -1) {
questionList.value.splice(index, 1)
message.success('删除成功')
}
}
}
</script>
<style scoped>
.question-bank-management {
padding: 20px;
background: #fff;
height: 100%;
}
.content-placeholder {
text-align: center;
padding: 60px 20px;
/* 顶部工具栏 */
.toolbar {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
padding: 15px 0 30px 20px;
background: #fff;
border-bottom: 2px solid #F5F7FA;
margin-right: 20px;
}
.content-placeholder h2 {
font-size: 24px;
.toolbar h2 {
margin: 0;
font-size: 18px;
color: #333;
font-weight: 500;
}
.toolbar-actions {
display: flex;
align-items: center;
gap: 12px;
}
.category-select {
padding: 6px 12px;
border: 1px solid #F1F3F4;
border-radius: 4px;
background: white;
font-size: 14px;
min-width: 160px;
cursor: pointer;
}
.category-select:focus {
border-color: #0088D1;
outline: none;
}
/* 更多下拉菜单样式 */
.dropdown-container {
position: relative;
}
.more-dropdown {
position: absolute;
top: 100%;
left: 0;
background: white;
min-width: 60px;
z-index: 1000;
margin-top: 4px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
border-radius: 4px;
}
.dropdown-item {
padding: 8px 8px;
cursor: pointer;
font-size: 10px;
color: #333;
transition: background-color 0.2s;
}
.dropdown-item:hover {
background-color: #f5f5f5;
}
/* 表格容器 */
.table-box {
background: #fff;
padding: 20px;
}
.question-data-table {
margin-bottom: 20px;
}
.content-placeholder p {
font-size: 16px;
/* 按钮样式 */
.btn {
padding: 6px 16px;
border: none;
border-radius: 2px;
cursor: pointer;
font-size: 14px;
font-weight: 400;
transition: all 0.3s ease;
text-decoration: none;
display: inline-flex;
align-items: center;
justify-content: center;
min-height: 32px;
}
.btn-primary {
background: #0088D1;
color: white;
}
.btn-primary:hover:not(:disabled) {
background: #0077BB;
}
.btn-new {
background: #fff;
color: #0288D1;
border: 1px solid #0288D1;
}
.btn-new:hover:not(:disabled) {
border-color: #0088D1;
color: #0088D1;
}
.btn-default {
background: #fff;
color: #666;
border: 1px solid #d9d9d9;
}
.btn-default:hover:not(:disabled) {
border-color: #0088D1;
color: #0088D1;
}
.btn-default:disabled {
background: #f5f5f5;
color: #bfbfbf;
cursor: not-allowed;
}
.btn-danger {
background: white;
color: #FF4D4F;
border: 1px solid #FF4D4F;
}
.btn-danger:hover:not(:disabled) {
background: #ff7875;
}
.btn-danger:disabled {
background: #f5f5f5;
color: #bfbfbf;
cursor: not-allowed;
}
.btn-search {
background: #0088D1;
color: white;
border-top-left-radius: 0;
border-bottom-left-radius: 0;
}
.btn-search:hover {
background: #0077BB;
}
/* 搜索框 */
.search-box {
display: flex;
align-items: center;
}
.search-box input {
padding: 6px 12px;
border: 1px solid #d9d9d9;
border-right: none;
border-top-left-radius: 4px;
border-bottom-left-radius: 4px;
font-size: 14px;
outline: none;
min-width: 200px;
}
.search-box input:focus {
border-color: #0088D1;
}
/* 不再需要分页器样式 */
/* 响应式布局 */
@media (max-width: 1200px) {
.toolbar {
flex-direction: column;
gap: 15px;
align-items: flex-start;
}
.toolbar-actions {
flex-wrap: wrap;
gap: 8px;
}
.search-box input {
width: 150px;
}
/* 表格在小屏幕下的优化 */
:deep(.question-data-table) {
padding: 20px;
}
/* 操作按钮在小屏幕下换行 */
:deep(.question-data-table .n-data-table-td:last-child .n-button) {
margin: 2px;
font-size: 11px;
padding: 0 8px;
}
}
@media (max-width: 768px) {
.question-bank-management {
padding: 10px;
}
.toolbar {
padding: 15px;
}
.toolbar h2 {
font-size: 16px;
}
.btn {
padding: 6px 12px;
font-size: 12px;
}
.search-box input {
width: 120px;
font-size: 12px;
}
.table-box {
padding: 12px;
}
/* 表格在移动端的优化 */
:deep(.question-data-table) {
padding: 10px;
}
/* 隐藏某些列在移动端 */
:deep(.question-data-table .n-data-table-th[data-col-key="creator"]),
:deep(.question-data-table .n-data-table-td[data-col-key="creator"]) {
display: none;
}
/* 操作按钮在移动端垂直排列 */
:deep(.question-data-table .n-data-table-td:last-child > div) {
flex-direction: column;
gap: 4px;
}
:deep(.question-data-table .n-data-table-td:last-child .n-button) {
width: 100%;
margin: 1px 0;
}
/* 不再需要分页器移动端样式 */
}
@media (max-width: 480px) {
.toolbar-actions {
flex-direction: column;
width: 100%;
}
.search-box {
width: 100%;
}
.search-box input {
width: 100%;
}
.category-select {
width: 100%;
}
/* 进一步隐藏列 */
:deep(.question-data-table .n-data-table-th[data-col-key="createTime"]),
:deep(.question-data-table .n-data-table-td[data-col-key="createTime"]) {
display: none;
}
/* 表格内容在超小屏幕下的优化 */
:deep(.question-data-table .n-data-table-td) {
padding: 8px 4px;
font-size: 12px;
}
:deep(.question-data-table .n-data-table-th) {
padding: 8px 4px;
font-size: 12px;
}
}
/* 表格行样式 */
:deep(.selected-row) {
background-color: #f0f8ff;
}
/* 表格横向滚动优化 */
:deep(.question-data-table .n-data-table-wrapper) {
overflow-x: auto;
}
:deep(.question-data-table .n-data-table-base-table) {
min-width: 100%;
}
/* 确保表格内容不会溢出 */
:deep(.question-data-table .n-data-table-td) {
word-break: break-word;
white-space: normal;
text-align: center;
font-size: 12px;
color: #062333;
}
/* 表头居中对齐 */
:deep(.question-data-table .n-data-table-th) {
text-align: center;
font-size: 12px;
color: #062333;
}
/* 内容列文本溢出处理 */
:deep(.question-data-table .n-data-table-td[data-col-key="content"] span) {
max-width: 100%;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
display: inline-block;
}
/* 操作列按钮居中 */
:deep(.question-data-table .n-data-table-td[data-col-key="actions"] > div) {
justify-content: center;
}
</style>

View File

@ -0,0 +1,384 @@
<template>
<n-modal :show="show" @update:show="handleUpdateShow" preset="card" title="我的资源" style="width: 900px; max-width: 90vw;" :closable="false">
<div class="resource-modal-content">
<!-- 筛选和搜索栏 -->
<div class="filter-search-bar">
<div class="filter-section">
<span class="filter-label">类型:</span>
<n-select v-model:value="selectedFileType" :options="fileTypeOptions" placeholder="全部" style="width: 120px;" />
</div>
<div class="search-section">
<span class="search-label">搜索:</span>
<n-input v-model:value="searchKeyword" placeholder="请输入文档名称" style="width: 200px;">
<template #suffix>
<img src="/public/images/teacher/搜索.png" alt="搜索" class="search-icon" @click="searchFiles" />
</template>
</n-input>
</div>
<div class="file-info">
<span class="file-count">已全部加载,{{ filteredFiles.length }}个文件</span>
<n-button type="primary" size="small" @click="uploadNewFile" class="upload-new-file-btn">
<img src="/public/images/teacher/上传2.png" alt="上传" class="upload-icon" />
上传新文件
</n-button>
</div>
</div>
<!-- 文件网格 -->
<div class="file-grid">
<div v-for="file in filteredFiles" :key="file.id" class="file-item" @click="selectFile(file, $event)">
<div class="file-thumbnail">
<img :src="file.thumbnail" :alt="file.name" />
</div>
<div class="file-name">{{ file.name }}</div>
<div class="file-options">
<n-dropdown :options="fileMenuOptions" @select="(key: string) => handleFileMenuSelect(key, file)">
<n-button quaternary size="small" class="file-menu-btn">
<template #icon>
<n-icon size="16">
<svg viewBox="0 0 24 24">
<path fill="currentColor" d="M12 8c1.1 0 2-.9 2-2s-.9-2-2-2-2 .9-2 2 .9 2 2 2zm0 2c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm0 6c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2z"/>
</svg>
</n-icon>
</template>
</n-button>
</n-dropdown>
</div>
</div>
</div>
</div>
<template #footer>
<div class="modal-footer">
<n-button @click="handleCancel">取消</n-button>
<n-button type="primary" @click="handleConfirm" :disabled="!selectedFile">确定</n-button>
</div>
</template>
</n-modal>
</template>
<script setup lang="ts">
import { ref, computed, watch } from 'vue'
// Props
interface Props {
show: boolean
}
const props = defineProps<Props>()
// Emits
const emit = defineEmits<{
'update:show': [value: boolean]
'select': [file: any]
}>()
//
const handleUpdateShow = (value: boolean) => {
emit('update:show', value)
}
//
const selectedFileType = ref('all')
const fileTypeOptions = [
{ label: '全部', value: 'all' },
{ label: '文档', value: 'document' },
{ label: '视频', value: 'video' },
{ label: '音频', value: 'audio' },
{ label: '其他', value: 'other' }
]
//
const searchKeyword = ref('')
//
const files = ref([
{ id: 1, name: '开课彩蛋:新开始.pptx', type: 'document', thumbnail: '/images/teacher/fj.png' },
{ id: 2, name: '课程定位与目标.pdf', type: 'document', thumbnail: '/images/teacher/fj.png' },
{ id: 3, name: '教学安排及学习建议.docx', type: 'document', thumbnail: '/images/teacher/fj.png' },
{ id: 4, name: '风景.mp4', type: 'video', thumbnail: '/images/teacher/fj.png' },
{ id: 5, name: '风景.mp4', type: 'video', thumbnail: '/images/teacher/fj.png' },
{ id: 6, name: '风景.mp4', type: 'video', thumbnail: '/images/teacher/fj.png' },
{ id: 7, name: '风景.mp4', type: 'video', thumbnail: '/images/teacher/fj.png' },
{ id: 8, name: '风景.mp4', type: 'video', thumbnail: '/images/teacher/fj.png' },
{ id: 9, name: '风景.mp4', type: 'video', thumbnail: '/images/teacher/fj.png' },
{ id: 10, name: '风景.mp4', type: 'video', thumbnail: '/images/teacher/fj.png' },
{ id: 11, name: '风景.mp4', type: 'video', thumbnail: '/images/teacher/fj.png' },
{ id: 12, name: '风景.mp4', type: 'video', thumbnail: '/images/teacher/fj.png' },
{ id: 13, name: '风景.mp4', type: 'video', thumbnail: '/images/teacher/fj.png' },
{ id: 14, name: '风景.mp4', type: 'video', thumbnail: '/images/teacher/fj.png' },
{ id: 15, name: '风景.mp4', type: 'video', thumbnail: '/images/teacher/fj.png' },
{ id: 16, name: '风景.mp4', type: 'video', thumbnail: '/images/teacher/fj.png' },
{ id: 17, name: '风景.mp4', type: 'video', thumbnail: '/images/teacher/fj.png' },
{ id: 18, name: '风景.mp4', type: 'video', thumbnail: '/images/teacher/fj.png' },
])
//
const filteredFiles = computed(() => {
let result = files.value
//
if (selectedFileType.value !== 'all') {
result = result.filter(file => file.type === selectedFileType.value)
}
//
if (searchKeyword.value.trim()) {
const keyword = searchKeyword.value.toLowerCase()
result = result.filter(file => file.name.toLowerCase().includes(keyword))
}
return result
})
//
const selectedFile = ref<any>(null)
//
const fileMenuOptions = [
{ label: '重命名', key: 'rename' },
{ label: '删除', key: 'delete' }
]
//
const searchFiles = () => {
console.log('搜索文件')
}
//
const uploadNewFile = () => {
console.log('上传新文件')
const newFile = {
id: files.value.length + 1,
name: '新文件.pdf',
type: 'document',
thumbnail: '/images/teacher/document.png'
}
files.value.push(newFile)
}
//
const selectFile = (file: any, event?: Event) => {
selectedFile.value = file
if (event && event.target) {
const target = event.target as HTMLElement
const fileItem = target.closest('.file-item')
if (fileItem) {
document.querySelectorAll('.file-item').forEach(item => {
item.classList.remove('selected')
})
fileItem.classList.add('selected')
}
}
}
//
const handleFileMenuSelect = (key: string, file: any) => {
if (key === 'delete') {
console.log('删除文件:', file)
files.value = files.value.filter(f => f.id !== file.id)
if (selectedFile.value && selectedFile.value.id === file.id) {
selectedFile.value = null
}
} else if (key === 'rename') {
console.log('重命名文件:', file)
}
}
//
const handleCancel = () => {
emit('update:show', false)
selectedFile.value = null
}
//
const handleConfirm = () => {
if (selectedFile.value) {
emit('select', selectedFile.value)
emit('update:show', false)
selectedFile.value = null
}
}
// show
watch(() => props.show, (newVal) => {
if (!newVal) {
selectedFile.value = null
}
})
</script>
<style scoped>
/* 资源选择模态框样式 */
.resource-modal-content {
border-top: 1.5px solid #E6E6E6;
padding: 20px 0;
}
.filter-search-bar {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 20px;
gap: 30px;
}
.filter-section, .search-section {
display: flex;
align-items: center;
gap: 8px;
}
.filter-label, .search-label {
font-size: 14px;
color: #333;
font-weight: 500;
}
.file-info {
display: flex;
align-items: center;
justify-content: flex-end;
gap: 16px;
flex: 1;
}
.file-count {
font-size: 10px;
color: #999;
}
.file-grid {
padding: 10px 0;
display: grid;
grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));
gap: 16px;
max-height: 400px;
overflow-y: auto;
}
.file-item {
min-height: 200px;
background: white;
border: 1.5px solid #D8D8D8;
border-radius: 2px;
padding: 18px;
cursor: pointer;
transition: all 0.3s ease;
position: relative;
display: flex;
flex-direction: column;
align-items: center;
justify-content: flex-end;
gap: 8px;
}
.file-item:hover {
border-color: #0288D1;
box-shadow: 0 2px 8px rgba(2, 136, 209, 0.15);
transform: translateY(-2px);
}
.file-item.selected {
border-color: #0288D1;
background: rgba(2, 136, 209, 0.05);
}
.file-thumbnail {
height: 105px;
display: flex;
align-items: center;
justify-content: center;
overflow: hidden;
}
.file-thumbnail img {
width: 100%;
height: 100%;
object-fit: cover;
}
.file-name {
font-size: 12px;
color: #333;
text-align: center;
line-height: 1.4;
max-width: 100%;
overflow: hidden;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
}
.file-options {
position: absolute;
top: 0px;
right: 0px;
opacity: 1;
transition: opacity 0.3s ease;
}
.file-item:hover .file-options {
opacity: 1;
}
.file-menu-btn {
width: 32px !important;
height: 32px !important;
padding: 0 !important;
background: rgba(255, 255, 255, 0.9) !important;
border: none !important;
border-radius: 0 !important;
}
.upload-new-file-btn {
border-radius: 0;
height: 32px;
}
.upload-icon {
width: 10px;
height: 10px;
margin-right: 4px;
vertical-align: middle;
}
.search-icon {
width: 16px;
height: 16px;
cursor: pointer;
vertical-align: middle;
}
.modal-footer {
display: flex;
justify-content: flex-end;
gap: 12px;
padding-top: 16px;
border-top: 1px solid #e0e0e0;
}
/* 响应式设计 */
@media (max-width: 768px) {
.filter-search-bar {
flex-direction: column;
align-items: stretch;
gap: 16px;
}
.file-grid {
grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));
gap: 12px;
}
.file-item {
padding: 8px;
}
.file-thumbnail {
width: 50px;
height: 50px;
}
}
</style>

View File

@ -1,57 +1,53 @@
<template>
<div class="modal-container flex-col" @click.stop>
<span class="modal-title">上传文件</span>
<div class="upload-area flex-row">
<span class="upload-label">上传文件</span>
<div class="select-file-btn flex-col" @click="toggleDropdown" style="position: relative;">
<span class="btn-text">选择文件</span>
<!-- 下拉选项 -->
<div v-show="showDropdown" class="upload-methods flex-col">
<label class="local-upload">
<input
type="file"
@change="handleLocalUpload"
style="display: none;"
accept=".doc,.docx,.pdf,.xls,.xlsx,.ppt,.pptx,.mp3,.mp4"
/>
本地上传
</label>
<label class="resource-upload">
<input
type="file"
@change="handleResourceUpload"
style="display: none;"
accept=".doc,.docx,.pdf,.xls,.xlsx,.ppt,.pptx,.mp3,.mp4"
/>
资源上传
</label>
</div>
</div>
</div>
<div class="supported-formats flex-row">
<span class="formats-label">支持格式</span>
<span class="document-formats">文本文.doc.docx.pdf表格文件.xls.xlsx<br />演示文稿.ppt.pptx</span>
<span class="media-formats">音频文件.mp3<br />视频文件.mp4<br /></span>
</div>
<div class="upload-limits flex-row justify-between">
<span class="limits-label">上传限制</span>
<span class="limits-description">wordexcelppt文件需小于100MB其他文件需小于2GB.mp4格式编码<br />说明上传mp4格式文件请查看编码说明</span>
</div>
<div class="action-buttons flex-row justify-between">
<div class="cancel-btn-container flex-col">
<div class="cancel-btn flex-col" @click="handleCancel">
<span class="cancel-btn-text">取消</span>
</div>
</div>
<div class="confirm-btn flex-col" @click="handleConfirm">
<span class="confirm-btn-text">确定</span>
</div>
<div class="modal-container flex-col" @click.stop>
<span class="modal-title">上传文件</span>
<div class="upload-area flex-row">
<span class="upload-label">上传文件</span>
<div class="select-file-btn flex-col" @click="toggleDropdown" style="position: relative;">
<span class="btn-text">选择文件</span>
<!-- 下拉选项 -->
<div v-show="showDropdown" class="upload-methods flex-col">
<label class="local-upload">
<input type="file" @change="handleLocalUpload" style="display: none;"
accept=".doc,.docx,.pdf,.xls,.xlsx,.ppt,.pptx,.mp3,.mp4" />
本地上传
</label>
<label class="resource-upload" @click="openResourceModal">
<!-- <input type="file" @change="handleResourceUpload" style="display: none;"
accept=".doc,.docx,.pdf,.xls,.xlsx,.ppt,.pptx,.mp3,.mp4" /> -->
资源上传
</label>
</div>
</div>
</div>
<div class="supported-formats flex-row">
<span class="formats-label">支持格式</span>
<span class="document-formats">文本文.doc.docx.pdf表格文件.xls.xlsx<br />演示文稿.ppt.pptx</span>
<span class="media-formats">音频文件.mp3<br />视频文件.mp4<br /></span>
</div>
<div class="upload-limits flex-row justify-between">
<span class="limits-label">上传限制</span>
<span class="limits-description">wordexcelppt文件需小于100MB其他文件需小于2GB.mp4格式编码<br />说明上传mp4格式文件请查看编码说明</span>
</div>
<div class="action-buttons flex-row justify-between">
<div class="cancel-btn-container flex-col">
<div class="cancel-btn flex-col" @click="handleCancel">
<span class="cancel-btn-text">取消</span>
</div>
</div>
<div class="confirm-btn flex-col" @click="handleConfirm">
<span class="confirm-btn-text">确定</span>
</div>
</div>
<!-- 资源选择模态框 -->
<ResourceSelectionModal v-model:show="showResourceModal" @select="handleResourceSelection" />
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import ResourceSelectionModal from './ResourceSelectionModal.vue'
//
const showDropdown = ref(false)
@ -73,14 +69,31 @@ const handleLocalUpload = (event: Event) => {
}
//
const handleResourceUpload = (event: Event) => {
const target = event.target as HTMLInputElement
const files = target.files
if (files && files.length > 0) {
console.log('资源上传文件:', files[0])
//
showDropdown.value = false
}
// const handleResourceUpload = (event: Event) => {
// const target = event.target as HTMLInputElement
// const files = target.files
// if (files && files.length > 0) {
// console.log(':', files[0])
// //
// showDropdown.value = false
// }
// }
//
const openResourceModal = () => {
console.log('打开资源选择模态框')
showResourceModal.value = true //
showDropdown.value = false //
}
//
const showResourceModal = ref(false)
//
const handleResourceSelection = (selectedResource: any) => {
console.log('资源选择:', selectedResource)
//
showResourceModal.value = false
}
//