style: 章节,作业部分页面

This commit is contained in:
Wxp 2025-08-22 16:59:07 +08:00
parent 1193c017ee
commit 8ce7c66fea
8 changed files with 2092 additions and 127 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 831 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 668 B

View File

@ -43,7 +43,6 @@ import CourseCreate from '@/components/admin/CourseComponents/CourseCreate.vue'
import CourseEditor from '@/views/teacher/course/CourseEditor.vue'
import CoursewareManagement from '@/views/teacher/course/CoursewareManagement.vue'
import ChapterManagement from '@/views/teacher/course/ChapterManagement.vue'
import HomeworkManagement from '@/views/teacher/course/HomeworkManagement.vue'
import PracticeManagement from '@/views/teacher/course/PracticeManagement.vue'
import QuestionBankManagement from '@/views/teacher/course/QuestionBankManagement.vue'
import CertificateManagement from '@/views/teacher/course/CertificateManagement.vue'
@ -52,6 +51,10 @@ import StatisticsManagement from '@/views/teacher/course/StatisticsManagement.vu
import NotificationManagement from '@/views/teacher/course/NotificationManagement.vue'
import GeneralManagement from '@/views/teacher/course/GeneralManagement.vue'
// 作业管理子组件
import HomeworkLibrary from '@/views/teacher/course/HomeworkLibrary.vue'
import HomeworkReview from '@/views/teacher/course/HomeworkReview.vue'
// ========== 路由配置 ==========
const routes: RouteRecordRaw[] = [
// 管理后台路由
@ -125,8 +128,21 @@ const routes: RouteRecordRaw[] = [
{
path: 'homework',
name: 'HomeworkManagement',
component: HomeworkManagement,
meta: { title: '作业管理' }
redirect: (to) => `/teacher/course-editor/${to.params.id}/homework/library`,
children: [
{
path: 'library',
name: 'HomeworkLibrary',
component: HomeworkLibrary,
meta: { title: '作业库' }
},
{
path: 'review',
name: 'HomeworkReview',
component: HomeworkReview,
meta: { title: '批阅作业' }
}
]
},
{
path: 'practice',

View File

@ -129,13 +129,13 @@ const navigateToTeacherDetail = (teacherId: number) => {
}
//
const toggleCourseInfo = (teacherId: number) => {
if (expandedTeacherId.value === teacherId) {
expandedTeacherId.value = null
} else {
expandedTeacherId.value = teacherId
}
}
// const toggleCourseInfo = (teacherId: number) => {
// if (expandedTeacherId.value === teacherId) {
// expandedTeacherId.value = null
// } else {
// expandedTeacherId.value = teacherId
// }
// }
//
const showCourseInfo = (teacherId: number) => {
@ -143,7 +143,7 @@ const showCourseInfo = (teacherId: number) => {
}
//
const hideCourseInfo = (teacherId: number) => {
const hideCourseInfo = (_: number) => {
expandedTeacherId.value = null
}

View File

@ -1,36 +1,786 @@
<template>
<div class="chapter-management">
<div class="content-placeholder">
<h2>章节管理</h2>
<p>章节管理功能正在开发中...</p>
<!-- 顶部操作栏 -->
<div class="toolbar">
<h2>全部章节</h2>
<div class="toolbar-actions">
<button class="btn btn-primary" @click="addChapter">添加章节</button>
<button class="btn btn-new" @click="importChapters">导入</button>
<button class="btn btn-new" @click="exportChapters">导出</button>
<button class="btn btn-danger" @click="deleteSelected" :disabled="selectedChapters.length === 0">删除</button>
<div class="search-box">
<input type="text" placeholder="请输入想要搜索的内容" v-model="searchKeyword" />
<button class="btn btn-search" @click="searchChapters">搜索</button>
</div>
</div>
</div>
<!-- 章节列表表格 -->
<div class="table-box">
<n-config-provider :locale="zhCN" :date-locale="dateZhCN">
<n-data-table :columns="columns" :data="paginatedChapters" :row-key="rowKey"
:checked-row-keys="selectedChapters" @update:checked-row-keys="handleCheck" :bordered="false"
:single-line="false" size="medium" class="chapter-data-table" :row-class-name="rowClassName" />
</n-config-provider>
<!-- 自定义分页器 -->
<div class="custom-pagination">
<div class="pagination-content">
<div class="page-numbers">
<span class="page-number nav-button" :class="{ disabled: currentPage === 1 }" @click="goToPage('first')">
首页
</span>
<span class="page-number nav-button" :class="{ disabled: currentPage === 1 }" @click="goToPage('prev')">
上一页
</span>
<span v-for="page in visiblePages" :key="page" class="page-number page-number-bordered"
:class="{ active: page === currentPage }" @click="goToPage(page)">
{{ page }}
</span>
<span v-if="showRightEllipsis" class="page-number">...</span>
<span v-if="totalPages > 1" class="page-number page-number-bordered" @click="goToPage(totalPages)">
{{ totalPages }}
</span>
<span class="page-number nav-button" :class="{ disabled: currentPage === totalPages }"
@click="goToPage('next')">
下一页
</span>
<span class="page-number nav-button" :class="{ disabled: currentPage === totalPages }"
@click="goToPage('last')">
尾页
</span>
</div>
</div>
</div>
</div>
</div>
</template>
<script setup lang="ts">
//
import { ref, computed, h } from 'vue'
import { NButton, useMessage, NDataTable, NConfigProvider, zhCN, dateZhCN } from 'naive-ui'
import type { DataTableColumns } from 'naive-ui'
const message = useMessage()
//
interface Chapter {
id: number
name: string
type: string
sort: string | number
createTime: string
isParent: boolean
children?: Chapter[]
expanded?: boolean
}
//
const searchKeyword = ref('')
//
const selectedChapters = ref<number[]>([])
//
const chapterList = ref<Chapter[]>([
{
id: 1,
name: '第一章 课前准备',
type: '-',
sort: '-',
createTime: '2025.07.25 09:20',
isParent: true,
expanded: false,
children: [
{
id: 2,
name: '开课彩蛋:新开始新征程',
type: '视频',
sort: 1,
createTime: '2025.07.25 09:20',
isParent: false
},
{
id: 3,
name: '课件准备PPT',
type: '课件',
sort: 2,
createTime: '2025.07.25 09:20',
isParent: false
},
{
id: 4,
name: '第一节 课程定位与目标',
type: '视频',
sort: 3,
createTime: '2025.07.25 09:20',
isParent: false
},
{
id: 5,
name: '第二节 教学安排及学习建议',
type: '作业',
sort: 4,
createTime: '2025.07.25 09:20',
isParent: false
},
{
id: 6,
name: '第三节 教学安排及学习建议',
type: '考试',
sort: 5,
createTime: '2025.07.25 09:20',
isParent: false
}
]
},
{
id: 7,
name: '第二章 课前准备',
type: '-',
sort: '-',
createTime: '2025.07.25 09:20',
isParent: true,
expanded: false,
children: [
{
id: 8,
name: '第一节 新开始新征程',
type: '视频',
sort: 1,
createTime: '2025.07.25 09:20',
isParent: false
},
{
id: 9,
name: '第二节 教学安排及学习建议',
type: '课件',
sort: 2,
createTime: '2025.07.25 09:20',
isParent: false
}
]
},
{
id: 10,
name: '第三章 课前准备',
type: '-',
sort: '-',
createTime: '2025.07.25 09:20',
isParent: true,
expanded: false,
children: [
{
id: 12,
name: '第一节 新开始新征程',
type: '视频',
sort: 1,
createTime: '2025.07.25 09:20',
isParent: false
},
{
id: 13,
name: '第二节 教学安排及学习建议',
type: '课件',
sort: 2,
createTime: '2025.07.25 09:20',
isParent: false
}
]
},
{
id: 11,
name: '第四章 课前准备',
type: '-',
sort: '-',
createTime: '2025.07.25 09:20',
isParent: true,
expanded: false,
children: []
}
])
//
const flattenedChapters = computed(() => {
const result: Chapter[] = []
const flatten = (chapters: Chapter[]) => {
chapters.forEach(chapter => {
result.push(chapter)
if (chapter.children && chapter.expanded) {
flatten(chapter.children)
}
})
}
flatten(chapterList.value)
return result
})
//
const filteredChapters = computed(() => {
if (!searchKeyword.value) {
return flattenedChapters.value
}
return flattenedChapters.value.filter((chapter: Chapter) =>
chapter.name.toLowerCase().includes(searchKeyword.value.toLowerCase())
)
})
//
const currentPage = ref(1)
const pageSize = ref(10)
const totalPages = computed(() => Math.ceil(filteredChapters.value.length / pageSize.value))
//
const visiblePages = computed(() => {
const pages = []
const current = currentPage.value
const total = totalPages.value
if (total <= 7) {
// 7
for (let i = 1; i <= total; i++) {
pages.push(i)
}
} else {
//
const start = Math.max(1, current - 2)
const end = Math.min(total, current + 2)
for (let i = start; i <= end; i++) {
pages.push(i)
}
}
return pages
})
//
const showRightEllipsis = computed(() => {
return currentPage.value < totalPages.value - 3
})
//
const paginatedChapters = computed(() => {
const start = (currentPage.value - 1) * pageSize.value
const end = start + pageSize.value
return filteredChapters.value.slice(start, end)
})
//
const rowKey = (row: Chapter) => row.id
//
const handleCheck = (rowKeys: number[]) => {
selectedChapters.value = rowKeys
}
//
const rowClassName = () => {
return 'chapter-table-row'
}
//
// const getChapterIcon = (type: string) => {
// return '/images/teacher/.png'
// }
//
// const getTypeIcon = (type: string) => {
// const iconMap: { [key: string]: string } = {
// '': '/images/teacher/.png',
// '': '/images/teacher/.png',
// '': '/images/teacher/.png',
// '': '/images/teacher/.png'
// }
// return iconMap[type] || '/images/teacher/.png'
// }
//
const goToPage = (page: string | number) => {
if (typeof page === 'string') {
switch (page) {
case 'first':
currentPage.value = 1
break
case 'prev':
if (currentPage.value > 1) currentPage.value--
break
case 'next':
if (currentPage.value < totalPages.value) currentPage.value++
break
case 'last':
currentPage.value = totalPages.value
break
}
} else {
currentPage.value = page
}
}
// /
const toggleChapter = (chapter: Chapter) => {
if (chapter.isParent && chapter.children) {
chapter.expanded = !chapter.expanded
}
}
//
const addChapter = () => {
message.info('添加章节功能')
}
const importChapters = () => {
message.info('导入章节功能')
}
const exportChapters = () => {
message.info('导出章节功能')
}
const deleteSelected = () => {
if (selectedChapters.value.length === 0) return
if (confirm(`确定要删除选中的 ${selectedChapters.value.length} 个章节吗?`)) {
selectedChapters.value.forEach((id: number) => {
const index = chapterList.value.findIndex((c: Chapter) => c.id === id)
if (index > -1) {
chapterList.value.splice(index, 1)
}
})
selectedChapters.value = []
message.success('删除成功')
}
}
const searchChapters = () => {
message.info('搜索章节: ' + searchKeyword.value)
currentPage.value = 1
}
const editChapter = (chapter: Chapter) => {
message.info('编辑章节: ' + chapter.name)
}
const deleteChapter = (chapter: Chapter) => {
if (confirm('确定要删除这个章节吗?')) {
const index = chapterList.value.findIndex((c: Chapter) => c.id === chapter.id)
if (index > -1) {
chapterList.value.splice(index, 1)
message.success('删除成功')
}
}
}
//
const columns: DataTableColumns<Chapter> = [
{
type: 'selection'
},
{
title: '章节名称',
key: 'name',
minWidth: 350,
render: (row: Chapter) => {
return h('div', {
style: {
display: 'flex',
alignItems: 'center',
gap: '20px',
cursor: row.isParent ? 'pointer' : 'default',
marginLeft: row.isParent ? '40px' : '12px'
},
onClick: row.isParent ? () => toggleChapter(row) : undefined
}, [
row.isParent ? h('i', {
class: 'n-base-icon',
style: {
transition: 'transform 0.2s',
transform: row.expanded ? 'rotate(90deg)' : 'rotate(0deg)',
cursor: 'pointer'
}
}, [
h('svg', {
viewBox: '0 0 16 16',
fill: 'none',
xmlns: 'http://www.w3.org/2000/svg'
}, [
h('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'
})
])
]) : h('div', { style: { width: '16px' } }),
h('span', {
style: {
color: row.isParent ? '#062333' : '#666666',
fontSize: '14px',
fontWeight: row.isParent ? '500' : 'normal',
marginLeft: row.isParent ? '0' : '24px'
}
}, row.name)
])
}
},
{
title: '类型',
key: 'type',
width: 156,
render: (row: Chapter) => {
if (row.type === '-') {
return h('span', { style: { color: '#BABABA' } }, '-')
}
return h('div', {
style: {
display: 'inline-block',
padding: '4px 8px',
backgroundColor: 'transparent',
border: '1px solid #BABABA',
borderRadius: '4px',
fontSize: '12px',
color: '#BABABA'
}
}, row.type)
}
},
{
title: '排序',
key: 'sort',
width: 120,
render: (row: Chapter) => {
return h('span', { style: { color: '#062333', fontSize: '12px' } }, row.sort)
}
},
{
title: '创建时间',
key: 'createTime',
width: 235,
render: (row: Chapter) => {
return h('span', { style: { color: '#062333', fontSize: '12px' } }, row.createTime)
}
},
{
title: '操作',
key: 'actions',
width: 200,
render: (row: Chapter) => {
return h('div', { style: { display: 'flex', gap: '16px', alignItems: 'center', justifyContent: 'center' } }, [
h(NButton, {
size: 'small',
type: 'info',
secondary: true,
onClick: () => editChapter(row)
}, { default: () => '编辑' }),
h(NButton, {
size: 'small',
type: 'error',
secondary: true,
onClick: () => deleteChapter(row)
}, { default: () => '删除' })
])
}
}
]
</script>
<style scoped>
.chapter-management {
padding: 20px;
background: #fff;
height: 100%;
width: 1293px;
background: #fff;
height: 100%;
overflow: auto;
}
/* 顶部工具栏 */
.toolbar {
display: flex;
justify-content: space-between;
align-items: center;
margin-right: 25px;
background: #fff;
padding: 30px 0 20px 30px;
border-bottom: 2px solid #F6F6F6;
}
.toolbar h2 {
margin: 0;
font-size: 18px;
color: #333;
}
.toolbar-actions {
display: flex;
align-items: center;
gap: 10px;
}
.btn {
padding: 7px 16px;
border: none;
font-size: 14px;
cursor: pointer;
transition: all 0.3s ease;
border-radius: 2px;
}
.btn-primary {
background: #0288D1;
color: white;
}
.btn-primary:hover {
background: #40a9ff;
}
.btn-new {
background-color: #fff;
border: 1px solid #0288D1;
color: #0288D1;
}
.btn-default {
background: white;
color: #999999;
border: 1px solid #999999;
}
.btn-default:hover {
border-color: #1890ff;
color: #1890ff;
}
.btn-danger {
background: white;
color: #FF4D4F;
border: 1px solid #FF4D4F;
}
.btn-danger:hover {
background: #ff7875;
}
.search-box {
display: flex;
align-items: center;
border: 1px solid #F1F3F4;
border-radius: 2px;
overflow: hidden;
}
.search-box input {
border: none;
padding: 6px 12px;
outline: none;
width: 200px;
font-size: 14px;
}
.btn-search {
background: #0288D1;
color: white;
border: none;
padding: 6px 16px;
cursor: pointer;
font-size: 16px;
}
.btn-search:hover {
background: #0277bd;
}
.table-box {
height: 100%;
position: relative;
display: flex;
flex-direction: column;
}
/* Naive UI 表格样式定制 */
:deep(.chapter-data-table) {
background: #fff;
border-radius: 8px;
padding: 40px;
height: 100%;
min-height: 500px;
position: relative;
}
/* 表格头部样式 */
:deep(.chapter-data-table .n-data-table-thead) {
background: #fafafa;
}
:deep(.chapter-data-table .n-data-table-th) {
background: #fafafa;
font-weight: 500;
color: #062333;
font-size: 14px;
border-bottom: 1px solid #e8e8e8;
padding: 12px 8px;
text-align: center;
}
/* 表格行样式 */
:deep(.chapter-data-table .n-data-table-td) {
font-size: 13px;
color: #062333;
border-bottom: 1px solid #f0f0f0;
padding: 12px 8px;
vertical-align: middle;
text-align: center;
}
/* 所有列居中对齐 */
:deep(.chapter-data-table .n-data-table-td) {
text-align: center !important;
}
:deep(.chapter-data-table .n-data-table-th) {
text-align: center !important;
}
:deep(.chapter-data-table .n-data-table-tr:hover) {
background: #fafafa;
}
/* 复选框样式 */
:deep(.chapter-data-table .n-checkbox) {
--n-size: 16px;
}
/* 隐藏Naive UI默认的展开触发器 */
:deep(.chapter-data-table .n-data-table-expand-trigger) {
display: none !important;
}
/* 隐藏Naive UI默认的展开占位符 */
:deep(.chapter-data-table .n-data-table-expand-placeholder) {
display: none !important;
}
/* 按钮组样式调整 */
:deep(.chapter-data-table .n-button) {
font-size: 12px;
height: 28px;
padding: 0 12px;
margin: 2px;
}
/* 操作按钮样式 - 只有边框和文字颜色,无背景色 */
:deep(.chapter-data-table .n-button--info-type.n-button--secondary) {
background-color: transparent !important;
border: 1px solid #0288D1;
color: #0288D1;
}
:deep(.chapter-data-table .n-button--info-type.n-button--secondary:hover) {
background-color: rgba(32, 128, 240, 0.05) !important;
border: 1px solid #0288D1;
color: #248DD3;
}
:deep(.chapter-data-table .n-button--error-type.n-button--secondary) {
background-color: transparent !important;
border: 1px solid #FF4D4F;
color: #FD8485;
}
:deep(.chapter-data-table .n-button--error-type.n-button--secondary:hover) {
background-color: rgba(208, 48, 80, 0.05) !important;
border: 1px solid #FF4D4F;
color: #FD8485;
}
/* 表格行样式 */
:deep(.chapter-table-row) {
transition: background-color 0.2s;
}
:deep(.chapter-table-row:hover) {
background-color: #f8f9fa;
}
/* 自定义分页器样式 */
.custom-pagination {
display: flex;
justify-content: center;
background: #fff;
padding: 20px 0;
margin-top: auto;
width: 100%;
position: absolute;
bottom: 0;
left: 0;
right: 0;
}
.pagination-content {
display: flex;
justify-content: center;
align-items: center;
margin: 0 auto;
padding: 0 10px;
}
.page-numbers {
display: flex;
align-items: center;
justify-content: center;
gap: 0;
white-space: nowrap;
}
.page-number {
display: inline-block;
min-width: 38px;
height: 38px;
line-height: 38px;
text-align: center;
color: #333;
text-decoration: none;
font-size: 14px;
padding: 0 5px;
margin: 0 4px;
border-radius: 5px;
cursor: pointer;
transition: all 0.3s ease;
}
.page-number-bordered {
border: 1px solid #d9d9d9;
}
.page-number.active {
background-color: #0088D1;
color: white;
border-color: #0088D1;
}
.page-number:hover:not(.disabled) {
color: #0088D1;
border-color: #0088D1;
}
.page-number.disabled {
color: #ccc;
cursor: not-allowed;
}
.page-number.disabled:hover {
color: #ccc;
border-color: #d9d9d9;
}
.nav-button {
padding: 0 8px;
border: none;
}
.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;
.nav-button:hover:not(.disabled) {
color: #0088D1;
}
</style>

View File

@ -2,114 +2,83 @@
<div class="course-editor">
<!-- 左侧导航菜单 -->
<div class="sidebar">
<router-link
:to="`/teacher/course-editor/${courseId}/courseware`"
class="menu-item"
:class="{ active: $route.path.includes('courseware') }"
>
<img
:src="$route.path.includes('courseware') ? '/images/teacher/课件-选中.png' : '/images/teacher/课件.png'"
alt="课件"
/>
<router-link :to="`/teacher/course-editor/${courseId}/courseware`" class="menu-item"
:class="{ active: $route.path.includes('courseware') }">
<img :src="$route.path.includes('courseware') ? '/images/teacher/课件-选中.png' : '/images/teacher/课件.png'"
alt="课件" />
<span>课件</span>
</router-link>
<router-link
:to="`/teacher/course-editor/${courseId}/chapters`"
class="menu-item"
:class="{ active: $route.path.includes('chapters') }"
>
<img
:src="$route.path.includes('chapters') ? '/images/teacher/章节-选中.png' : '/images/teacher/章节.png'"
alt="章节"
/>
<router-link :to="`/teacher/course-editor/${courseId}/chapters`" class="menu-item"
:class="{ active: $route.path.includes('chapters') }">
<img :src="$route.path.includes('chapters') ? '/images/teacher/章节-选中.png' : '/images/teacher/章节.png'"
alt="章节" />
<span>章节</span>
</router-link>
<router-link
:to="`/teacher/course-editor/${courseId}/homework`"
class="menu-item"
:class="{ active: $route.path.includes('homework') }"
>
<img
:src="$route.path.includes('homework') ? '/images/teacher/作业-选中.png' : '/images/teacher/作业.png'"
alt="作业"
/>
<span>作业</span>
</router-link>
<router-link
:to="`/teacher/course-editor/${courseId}/practice`"
class="menu-item"
:class="{ active: $route.path.includes('practice') }"
>
<img
:src="$route.path.includes('practice') ? '/images/teacher/练考通-选中.png' : '/images/teacher/练考通.png'"
alt="练考通"
/>
<!-- 作业二级导航 -->
<div class="menu-group">
<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': 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"
fill="#C2C2C2"></path>
</svg>
</i>
</div>
<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>
</router-link>
<router-link :to="`/teacher/course-editor/${courseId}/homework/review`" class="submenu-item"
:class="{ active: $route.path.includes('homework/review') }">
<span>批阅作业</span>
</router-link>
</div>
</div>
<router-link :to="`/teacher/course-editor/${courseId}/practice`" class="menu-item"
:class="{ active: $route.path.includes('practice') }">
<img :src="$route.path.includes('practice') ? '/images/teacher/练考通-选中.png' : '/images/teacher/练考通.png'"
alt="练考通" />
<span>练考通</span>
</router-link>
<router-link
:to="`/teacher/course-editor/${courseId}/question-bank`"
class="menu-item"
:class="{ active: $route.path.includes('question-bank') }"
>
<img
:src="$route.path.includes('question-bank') ? '/images/teacher/题库-选中.png' : '/images/teacher/题库.png'"
alt="题库"
/>
<router-link :to="`/teacher/course-editor/${courseId}/question-bank`" class="menu-item"
:class="{ active: $route.path.includes('question-bank') }">
<img :src="$route.path.includes('question-bank') ? '/images/teacher/题库-选中.png' : '/images/teacher/题库.png'"
alt="题库" />
<span>题库</span>
</router-link>
<router-link
:to="`/teacher/course-editor/${courseId}/certificate`"
class="menu-item"
:class="{ active: $route.path.includes('certificate') }"
>
<img
:src="$route.path.includes('certificate') ? '/images/teacher/证书-选中.png' : '/images/teacher/证书.png'"
alt="证书"
/>
<router-link :to="`/teacher/course-editor/${courseId}/certificate`" class="menu-item"
:class="{ active: $route.path.includes('certificate') }">
<img :src="$route.path.includes('certificate') ? '/images/teacher/证书-选中.png' : '/images/teacher/证书.png'"
alt="证书" />
<span>证书</span>
</router-link>
<router-link
:to="`/teacher/course-editor/${courseId}/discussion`"
class="menu-item"
:class="{ active: $route.path.includes('discussion') }"
>
<img
:src="$route.path.includes('discussion') ? '/images/teacher/讨论-选中.png' : '/images/teacher/讨论.png'"
alt="讨论"
/>
<router-link :to="`/teacher/course-editor/${courseId}/discussion`" class="menu-item"
:class="{ active: $route.path.includes('discussion') }">
<img :src="$route.path.includes('discussion') ? '/images/teacher/讨论-选中.png' : '/images/teacher/讨论.png'"
alt="讨论" />
<span>讨论</span>
</router-link>
<router-link
:to="`/teacher/course-editor/${courseId}/statistics`"
class="menu-item"
:class="{ active: $route.path.includes('statistics') }"
>
<img
:src="$route.path.includes('statistics') ? '/images/teacher/统计-选中.png' : '/images/teacher/统计.png'"
alt="统计"
/>
<router-link :to="`/teacher/course-editor/${courseId}/statistics`" class="menu-item"
:class="{ active: $route.path.includes('statistics') }">
<img :src="$route.path.includes('statistics') ? '/images/teacher/统计-选中.png' : '/images/teacher/统计.png'"
alt="统计" />
<span>统计</span>
</router-link>
<router-link
:to="`/teacher/course-editor/${courseId}/notification`"
class="menu-item"
:class="{ active: $route.path.includes('notification') }"
>
<img
:src="$route.path.includes('notification') ? '/images/teacher/通知-选中.png' : '/images/teacher/通知.png'"
alt="通知"
/>
<router-link :to="`/teacher/course-editor/${courseId}/notification`" class="menu-item"
:class="{ active: $route.path.includes('notification') }">
<img :src="$route.path.includes('notification') ? '/images/teacher/通知-选中.png' : '/images/teacher/通知.png'"
alt="通知" />
<span>通知</span>
</router-link>
<router-link
:to="`/teacher/course-editor/${courseId}/management`"
class="menu-item"
:class="{ active: $route.path.includes('management') }"
>
<img
:src="$route.path.includes('management') ? '/images/teacher/管理-选中.png' : '/images/teacher/管理.png'"
alt="管理"
/>
<router-link :to="`/teacher/course-editor/${courseId}/management`" class="menu-item"
:class="{ active: $route.path.includes('management') }">
<img :src="$route.path.includes('management') ? '/images/teacher/管理-选中.png' : '/images/teacher/管理.png'"
alt="管理" />
<span>管理</span>
</router-link>
</div>
@ -123,11 +92,20 @@
<script setup lang="ts">
import { useRoute } from 'vue-router'
import { ref } from 'vue'
const route = useRoute()
// ID
const courseId = route.params.id
//
const homeworkExpanded = ref(false)
// /
const toggleHomework = () => {
homeworkExpanded.value = !homeworkExpanded.value
}
</script>
<style scoped>
@ -178,16 +156,108 @@ const courseId = route.params.id
.menu-item img {
margin-left: 40px;
margin-top: 1px;
width: 16px;
height: 16px;
width: 18px;
height: 18px;
margin-right: 10px;
}
.menu-item span {
font-size: 16px;
font-size: 18px;
color: #666;
}
/* 作业二级导航样式 */
.menu-group {
margin-bottom: 5px;
}
.menu-header {
display: flex;
align-items: center;
padding: 15px 20px;
cursor: pointer;
transition: all 0.3s ease;
border-left: 3px solid transparent;
font-size: 18px;
color: #666;
border-radius: 5px;
position: relative;
}
.menu-header:hover {
background: #f5f5f5;
}
.menu-header img {
margin-left: 40px;
margin-top: 1px;
width: 18px;
height: 18px;
margin-right: 10px;
}
.menu-header span {
font-size: 18px;
color: #666;
flex: 1;
}
.menu-header .n-base-icon {
width: 16px;
height: 16px;
transition: transform 0.2s ease;
margin-right: 10px;
}
.menu-header .n-base-icon.expanded {
transform: rotate(90deg);
}
.submenu {
margin-left: 0;
}
.submenu-item {
display: flex;
align-items: center;
padding: 12px 20px;
margin-bottom: 2px;
cursor: pointer;
transition: all 0.3s ease;
border-left: 3px solid transparent;
text-decoration: none;
color: inherit;
font-size: 16px;
color: #666;
border-radius: 5px;
}
.submenu-item::before {
content: '';
width: 18px;
height: 18px;
margin-left: 40px;
margin-right: 10px;
flex-shrink: 0;
}
.submenu-item:hover {
background: #f5f5f5;
}
.submenu-item.active {
background: #F5F8FB;
color: #0288D1;
}
.submenu-item.active span {
color: #0288D1;
}
.submenu-item span {
font-size: 16px;
color: #666;
}
/* 右侧内容区域 */
.content-area {
flex: 1;

View File

@ -0,0 +1,720 @@
<template>
<div class="homework-library">
<div class="toolbar">
<h2>作业库</h2>
<div class="toolbar-actions">
<button class="btn btn-primary">添加作业</button>
<button class="btn btn-new">导入</button>
<button class="btn btn-new">导出</button>
<button class="btn btn-danger">删除</button>
<div class="search-box">
<input type="text" placeholder="请输入想要搜索的内容" />
<button class="btn btn-search">搜索</button>
</div>
</div>
</div>
<div class="content-area">
<div class="table-container">
<n-config-provider :locale="zhCN" :date-locale="dateZhCN">
<n-data-table :columns="columns" :data="homeworkList" :row-key="rowKey" :checked-row-keys="selectedHomework"
@update:checked-row-keys="handleCheck" :bordered="false" :single-line="false" size="medium"
class="homework-data-table" />
</n-config-provider>
</div>
<div class="pagination-container">
<!-- 自定义分页器 -->
<div class="custom-pagination">
<div class="pagination-content">
<div class="page-numbers">
<span class="page-number nav-button" :class="{ disabled: currentPage === 1 }" @click="goToPage('first')">
首页
</span>
<span class="page-number nav-button" :class="{ disabled: currentPage === 1 }" @click="goToPage('prev')">
上一页
</span>
<span v-for="page in visiblePages" :key="page" class="page-number page-number-bordered"
:class="{ active: page === currentPage }" @click="goToPage(page)">
{{ page }}
</span>
<span v-if="showRightEllipsis" class="page-number">...</span>
<span v-if="totalPages > 1" class="page-number page-number-bordered" @click="goToPage(totalPages)">
{{ totalPages }}
</span>
<span class="page-number nav-button" :class="{ disabled: currentPage === totalPages }"
@click="goToPage('next')">
下一页
</span>
<span class="page-number nav-button" :class="{ disabled: currentPage === totalPages }"
@click="goToPage('last')">
尾页
</span>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { ref, h, computed } from 'vue'
import { NButton, NDropdown, NDataTable, NConfigProvider, zhCN, dateZhCN } from 'naive-ui'
import type { DataTableColumns } from 'naive-ui'
//
interface HomeworkItem {
id: number
name: string
chapter: string
class: string
creator: string
createTime: string
isTop: boolean
}
//
const selectedHomework = ref<number[]>([])
//
const currentPage = ref(1)
const pageSize = ref(10)
const totalCount = ref(6)
//
const totalPages = computed(() => Math.ceil(totalCount.value / pageSize.value))
//
const visiblePages = computed(() => {
const pages = []
const maxVisible = 5
let start = Math.max(1, currentPage.value - Math.floor(maxVisible / 2))
let end = Math.min(totalPages.value, start + maxVisible - 1)
if (end - start + 1 < maxVisible) {
start = Math.max(1, end - maxVisible + 1)
}
for (let i = start; i <= end; i++) {
pages.push(i)
}
return pages
})
//
const showRightEllipsis = computed(() => {
return totalPages.value > 5 && currentPage.value < totalPages.value - 2
})
//
const goToPage = (target: number | string) => {
if (typeof target === 'string') {
switch (target) {
case 'first':
currentPage.value = 1
break
case 'prev':
if (currentPage.value > 1) currentPage.value--
break
case 'next':
if (currentPage.value < totalPages.value) currentPage.value++
break
case 'last':
currentPage.value = totalPages.value
break
}
} else {
currentPage.value = target
}
}
//
const homeworkList = ref<HomeworkItem[]>([
{
id: 1,
name: '作业名称作业名称作业名称',
chapter: '第一节 开课彩蛋新开始',
class: '班级一、班级二',
creator: '王建国',
createTime: '2025.07.25 09:20',
isTop: true
},
{
id: 2,
name: '作业名称作业名称作业名称',
chapter: '第一节 开课彩蛋新开始',
class: '班级一、班级二',
creator: '王建国',
createTime: '2025.07.25 09:20',
isTop: false
},
{
id: 3,
name: '作业名称作业名称作业名称',
chapter: '第一节 开课彩蛋新开始',
class: '班级一、班级二',
creator: '王建国',
createTime: '2025.07.25 09:20',
isTop: false
},
{
id: 4,
name: '作业名称作业名称作业名称',
chapter: '-',
class: '班级一、班级二',
creator: '王建国',
createTime: '2025.07.25 09:20',
isTop: false
},
{
id: 5,
name: '作业名称作业名称作业名称',
chapter: '第一节 开课彩蛋新开始',
class: '班级一、班级二',
creator: '王建国',
createTime: '2025.07.25 09:20',
isTop: false
},
{
id: 6,
name: '作业名称作业名称作业名称',
chapter: '第一节 开课彩蛋新开始',
class: '班级一、班级二',
creator: '王建国',
createTime: '2025.07.25 09:20',
isTop: false
}
])
//
const rowKey = (row: HomeworkItem) => row.id
//
const handleCheck = (keys: number[]) => {
selectedHomework.value = keys
}
//
const columns: DataTableColumns<HomeworkItem> = [
{
type: 'selection',
width: 50
},
{
title: '序号',
key: 'index',
width: 80,
render: (_, index) => index + 1
},
{
title: '作业名称',
key: 'name',
render: (row) => {
return h('div', { style: 'display: flex; align-items: center; gap: 8px;' }, [
h('span', { class: 'homework-name' }, row.name),
row.isTop ? h('span', { class: 'tag tag-pinned' }, '置顶') : null
])
}
},
{
title: '所属章节',
key: 'chapter'
},
{
title: '绑定班级',
key: 'class'
},
{
title: '创建人',
key: 'creator'
},
{
title: '创建时间',
key: 'createTime'
},
{
title: '操作',
key: 'actions',
width: 200,
render: (row) => {
return h('div', { style: 'display: flex; gap: 8px; align-items: center;' }, [
h(NButton, {
size: 'small',
type: 'info',
secondary: true,
onClick: () => editHomework(row.id)
}, { default: () => '编辑' }),
h(NButton, {
size: 'small',
type: 'error',
secondary: true,
onClick: () => deleteHomework(row.id)
}, { default: () => '删除' }),
h(NDropdown, {
trigger: 'click',
options: [
{
label: '重命名',
key: 'rename',
icon: () => h('img', { src: '/public/images/teacher/重命名.png', style: 'width: 10px; height: 10px;' })
},
{
label: row.isTop ? '取消置顶' : '置顶',
key: 'toggleTop',
icon: () => h('img', { src: '/public/images/teacher/置顶.png', style: 'width: 10px; height: 10px;' })
},
{
label: '权限设置',
key: 'permissions',
icon: () => h('img', { src: '/public/images/teacher/权限设置.png', style: 'width: 10px; height: 10px;' })
},
{
label: '下载',
key: 'download',
icon: () => h('img', { src: '/public/images/teacher/下载.png', style: 'width: 10px; height: 10px;' })
}
],
onSelect: (key) => handleAction(key, row)
}, {
default: () => h(NButton, {
size: 'small',
type: 'info',
secondary: true
}, { default: () => '更多' })
})
])
}
}
]
//
const editHomework = (id: number) => {
console.log('编辑作业:', id)
}
//
const deleteHomework = (id: number) => {
console.log('删除作业:', id)
}
//
const handleAction = (key: string, row: HomeworkItem) => {
console.log('操作:', key, row)
}
</script>
<style scoped>
.homework-library {
width: 1293px;
padding: 10px 20px 20px 0;
height: 100%;
background: #fff;
}
.toolbar {
display: flex;
justify-content: space-between;
align-items: center;
padding: 20px;
border-bottom: 2px solid #F6F6F6;
}
.toolbar h2 {
font-size: 18px;
font-weight: 500;
color: #333;
margin: 0;
}
.toolbar-actions {
display: flex;
align-items: center;
gap: 12px;
}
.content-area {
height: calc(100% - 80px);
display: flex;
flex-direction: column;
position: relative;
}
.table-container {
flex: 1;
overflow: auto;
margin-bottom: 16px;
padding-bottom: 80px;
}
.btn {
padding: 7px 16px;
border: 1px solid #d9d9d9;
border-radius: 2px;
font-size: 14px;
cursor: pointer;
transition: all 0.3s;
background: #fff;
color: #666;
}
.btn:hover {
border-color: #40a9ff;
color: #40a9ff;
}
.btn-primary {
background: #0288D1;
border-color: #0288D1;
color: #fff;
}
.btn-primary:hover {
background: #40a9ff;
border-color: #40a9ff;
}
.btn-new {
background: #fff;
border-color: #0288D1;
color: #0288D1;
}
.btn-new:hover {
background: #0288D1;
color: #fff;
}
.btn-default {
background: #fff;
border-color: #0288D1;
color: #666;
}
.btn-default:disabled {
cursor: not-allowed;
opacity: 0.5;
}
.btn-danger {
border-color: #FF4D4F;
color: #FF4D4F;
}
.btn-danger:hover {
background: #FF4D4F;
color: #fff;
}
.btn-danger:disabled {
cursor: not-allowed;
opacity: 0.5;
}
.btn-small {
padding: 4px 8px;
font-size: 12px;
}
.btn-info {
border-color: #0288D1;
color: #0288D1;
}
.btn-more {
border-color: #0288D1;
color: #0288D1;
position: relative;
}
.search-box {
display: flex;
align-items: center;
border: 1px solid #F1F3F4;
border-radius: 2px;
overflow: hidden;
}
.search-box input {
border: none;
padding: 6px 12px;
outline: none;
width: 200px;
font-size: 14px;
color: #333;
}
.search-box input::placeholder {
color: #999;
}
.btn-search {
background: #0288D1;
color: white;
border: none;
padding: 6px 16px;
cursor: pointer;
font-size: 16px;
}
.btn-search:hover {
background: #0288D1;
}
/* Naive UI 表格样式定制 */
:deep(.homework-data-table) {
background: #fff;
border-radius: 8px;
padding: 30px;
}
:deep(.n-data-table-table) {
border: 1px solid #F1F3F4;
}
/* 表格头部样式 */
:deep(.homework-data-table .n-data-table-thead) {
background: #fafafa;
}
:deep(.homework-data-table .n-data-table-th) {
background: #fafafa;
font-weight: 500;
color: #062333;
font-size: 14px;
border-bottom: 1px solid #e8e8e8;
padding: 12px 8px;
text-align: center;
}
/* 表格行样式 */
:deep(.homework-data-table .n-data-table-td) {
font-size: 12px;
color: #062333;
border-bottom: 1px solid #f0f0f0;
padding: 12px 8px;
vertical-align: middle;
text-align: center;
}
/* 名称列也居中 */
:deep(.homework-data-table .n-data-table-td[data-col-key="name"]) {
text-align: center;
}
:deep(.homework-data-table .n-data-table-th[data-col-key="name"]) {
text-align: center;
}
:deep(.homework-data-table .n-data-table-tr:hover) {
background: #fafafa;
}
/* 复选框样式 */
:deep(.homework-data-table .n-checkbox) {
--n-size: 16px;
}
/* 按钮组样式调整 */
:deep(.homework-data-table .n-button) {
font-size: 12px;
height: 28px;
padding: 0 12px;
margin: 2px;
background: transparent !important;
}
/* 编辑按钮样式 */
:deep(.homework-data-table .n-button--info-type) {
border: 1px solid #0288D1 !important;
color: #0288D1 !important;
background: transparent !important;
}
:deep(.homework-data-table .n-button--info-type:hover) {
border-color: #0288D1 !important;
color: #0288D1 !important;
background: transparent !important;
}
/* 删除按钮样式 */
:deep(.homework-data-table .n-button--error-type) {
border: 1px solid #FF4D4F !important;
color: #FF4D4F !important;
background: transparent !important;
}
:deep(.homework-data-table .n-button--error-type:hover) {
border-color: #FF4D4F !important;
color: #FF4D4F !important;
background: transparent !important;
}
/* 更多按钮样式 */
:deep(.homework-data-table .n-button--info-type:last-child) {
border: 1px solid #4165D7 !important;
color: #4165D7 !important;
background: transparent !important;
}
:deep(.homework-data-table .n-button--info-type:last-child:hover) {
border-color: #4165D7 !important;
color: #4165D7 !important;
background: transparent !important;
}
/* 下拉菜单样式 */
:deep(.n-dropdown-option) {
padding: 8px 12px;
font-size: 12px;
color: #062333;
}
/* 分页器容器 */
.pagination-container {
display: flex;
justify-content: center;
padding: 20px 0;
border-top: 1px solid #f0f0f0;
position: absolute;
bottom: 0;
left: 0;
right: 0;
background: #fff;
width: 100%;
}
/* 自定义分页器样式 */
.custom-pagination {
display: flex;
justify-content: center;
background: #fff;
padding: 20px 0;
margin-top: auto;
width: 100%;
position: absolute;
bottom: 0;
left: 0;
right: 0;
}
.pagination-content {
display: flex;
justify-content: center;
align-items: center;
margin: 0 auto;
padding: 0 10px;
}
.page-numbers {
display: flex;
align-items: center;
justify-content: center;
gap: 0;
white-space: nowrap;
}
.page-number {
display: inline-block;
min-width: 38px;
height: 38px;
line-height: 38px;
text-align: center;
color: #333;
text-decoration: none;
font-size: 14px;
padding: 0 5px;
margin: 0 4px;
border-radius: 5px;
cursor: pointer;
transition: all 0.3s ease;
}
.page-number-bordered {
border: 1px solid #d9d9d9;
}
.page-number.active {
background-color: #0088D1;
color: white;
border-color: #0088D1;
}
.page-number:hover:not(.disabled) {
color: #0088D1;
border-color: #0088D1;
}
.page-number.disabled {
color: #ccc;
cursor: not-allowed;
}
.page-number.disabled:hover {
color: #ccc;
border-color: #d9d9d9;
}
.nav-button {
padding: 0 8px;
border: none;
}
.nav-button:hover:not(.disabled) {
color: #0088D1;
}
/* 表格内所有文字颜色统一 */
:deep(.homework-data-table .n-data-table-th__title) {
color: #062333;
font-size: 14px;
}
:deep(.homework-data-table .n-data-table-td) {
color: #062333;
font-size: 12px;
}
/* 作业名称和标签样式 */
.homework-name {
display: inline-block;
margin-right: 8px;
color: #062333;
font-size: 12px;
}
.tag {
display: inline-block;
padding: 2px 6px;
border-radius: 4px;
font-size: 10px;
color: #9ac1d6;
background: transparent;
border: 1px solid #9ac1d6;
}
.tag-pinned {
color: #9ac1d6;
border-color: #9ac1d6;
}
/* 确保在Naive UI表格中标签样式正确显示 */
:deep(.homework-data-table .tag) {
display: inline-block !important;
padding: 2px 6px !important;
border-radius: 4px !important;
font-size: 10px !important;
color: #9ac1d6 !important;
background: transparent !important;
border: 1px solid #9ac1d6 !important;
}
:deep(.homework-data-table .tag-pinned) {
color: #9ac1d6 !important;
border-color: #9ac1d6 !important;
font-size: 10px !important;
}
</style>

View File

@ -0,0 +1,409 @@
<template>
<div class="homework-review">
<!-- 顶部筛选区域 -->
<div class="top-section">
<div class="filter-tabs">
<span class="tab-item" :class="{ active: activeTab === 'all' }" @click="setActiveTab('all')">全部</span>
<span class="tab-item" :class="{ active: activeTab === 'publishing' }"
@click="setActiveTab('publishing')">发布中</span>
<span class="tab-item" :class="{ active: activeTab === 'ended' }" @click="setActiveTab('ended')">已结束</span>
</div>
<div class="class-dropdown">
<span class="dropdown-text">班级名称</span>
<svg class="dropdown-arrow" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path
d="M3.14645 5.64645C3.34171 5.45118 3.65829 5.45118 3.85355 5.64645L8 9.79289L12.1464 5.64645C12.3417 5.45118 12.6583 5.45118 12.8536 5.64645C13.0488 5.84171 13.0488 6.15829 12.8536 6.35355L8.35355 10.8536C8.15829 11.0488 7.84171 11.0488 7.64645 10.8536L3.14645 6.35355C2.95118 6.15829 2.95118 5.84171 3.14645 5.64645Z"
fill="#C2C2C2"></path>
</svg>
</div>
</div>
<!-- 作业列表 -->
<div class="homework-list">
<!-- 已结束作业 -->
<div v-for="homework in filteredHomeworks" :key="homework.id">
<div v-if="homework.status === 'ended'" class="homework-card ended">
<a class="delete-link" @click="deleteHomework(homework.id)">删除</a>
<div class="card-header">
<h3 class="homework-title">{{ homework.title }}</h3>
<span class="status-badge ended">已结束</span>
</div>
<div class="card-content">
<div class="content-left">
<p class="homework-desc">{{ homework.content }}</p>
<div class="homework-info">
<div class="info-item">
<img class="icon" src="/images/teacher/发布人.png" alt="发布人" />
<span class="text">发布人{{ homework.publisher }}</span>
</div>
<div class="info-item">
<img class="icon" src="/images/teacher/起点时间.png" alt="时间" />
<span class="text">起止时间{{ homework.timeRange }}</span>
</div>
</div>
</div>
<div class="content-right">
<div class="stats-area">
<span class="big-number">{{ homework.pendingCount }}</span>
<span class="label primary">待批</span>
<span class="label secondary">{{ homework.submittedCount }}已交</span>
<span class="label secondary">{{ homework.unsubmittedCount }}未交</span>
</div>
<button class="action-button review" @click="reviewHomework(homework.id)">批阅</button>
</div>
</div>
</div>
<!-- 发布中作业 -->
<div v-if="homework.status === 'publishing'" class="homework-card publishing">
<a class="delete-link" @click="deleteHomework(homework.id)">删除</a>
<div class="card-header">
<h3 class="homework-title">{{ homework.title }}</h3>
<span class="status-badge publishing">发布中</span>
</div>
<div class="card-content">
<div class="content-left">
<p class="homework-desc">{{ homework.content }}</p>
<div class="homework-info">
<div class="info-item">
<img class="icon" src="/images/teacher/发布人.png" alt="发布人" />
<span class="text">发布人{{ homework.publisher }}</span>
</div>
<div class="info-item">
<img class="icon" src="/images/teacher/起点时间.png" alt="时间" />
<span class="text">起止时间{{ homework.timeRange }}</span>
</div>
</div>
</div>
<div class="content-right">
<div class="stats-area">
<span class="big-number">{{ homework.pendingCount }}</span>
<span class="label primary">待批</span>
<span class="label secondary">{{ homework.submittedCount }}已交</span>
<span class="label secondary">{{ homework.unsubmittedCount }}未交</span>
</div>
<button class="action-button view" @click="viewHomework(homework.id)">查看</button>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { ref, computed } from 'vue'
interface HomeworkItem {
id: number
title: string
content: string
publisher: string
timeRange: string
status: 'publishing' | 'ended'
pendingCount: number
submittedCount: number
unsubmittedCount: number
}
const activeTab = ref<'all' | 'publishing' | 'ended'>('all')
const homeworks = ref<HomeworkItem[]>([
{
id: 1,
title: '作业名称作业名称作业名称作业名称',
content: '作业内容作业内容作业内容作业内容作业内容作业内容作业内容作业内容作业内容',
publisher: '王建国',
timeRange: '2025.8.18-2025.9.18',
status: 'ended',
pendingCount: 10,
submittedCount: 0,
unsubmittedCount: 0
},
{
id: 2,
title: '作业名称作业名称作业名称作业名称',
content: '作业内容作业内容作业内容作业内容作业内容作业内容作业内容作业内容作业内容',
publisher: '王建国',
timeRange: '2025.8.18-2025.9.18',
status: 'publishing',
pendingCount: 0,
submittedCount: 0,
unsubmittedCount: 0
}
])
const setActiveTab = (tab: 'all' | 'publishing' | 'ended') => {
activeTab.value = tab
}
const filteredHomeworks = computed(() => {
if (activeTab.value === 'all') {
return homeworks.value
}
return homeworks.value.filter(homework => homework.status === activeTab.value)
})
const reviewHomework = (id: number) => {
console.log('批阅作业:', id)
}
const viewHomework = (id: number) => {
console.log('查看作业:', id)
}
const deleteHomework = (id: number) => {
console.log('删除作业:', id)
}
</script>
<style scoped>
.homework-review {
width: 1293px;
background: #fff;
min-height: 100vh;
}
/* 顶部筛选区域 */
.top-section {
display: flex;
justify-content: space-between;
align-items: center;
padding: 30px 0 0 25px;
margin-right: 30px;
background: #fff;
border-bottom: 1px solid #e5e5e5;
}
.filter-tabs {
display: flex;
gap: 50px;
}
.tab-item {
font-size: 18px;
font-weight: 400;
color: #333333;
cursor: pointer;
padding-top: 6px;
padding-bottom: 20px;
border-bottom: 3px solid transparent;
transition: all 0.3s ease;
position: relative;
}
.tab-item.active {
color: #5BADD9;
border-bottom: 4px solid #0288D1;
}
.tab-item:hover {
color: #2196F3;
}
.class-dropdown {
display: flex;
align-items: center;
padding: 8px 16px;
background: #fff;
border: 1px solid #e0e0e0;
border-radius: 2px;
margin-bottom: 20px;
cursor: pointer;
width: 276px;
justify-content: space-between;
}
.dropdown-text {
font-size: 14px;
color: #062333;
}
.dropdown-arrow {
width: 20px;
height: 20px;
}
/* 作业列表 */
.homework-list {
padding: 0;
}
.homework-card {
background: #fff;
margin: 0;
border-bottom: 1px solid #f0f0f0;
padding: 20px 25px;
position: relative;
transition: background-color 0.3s ease;
}
.homework-card:hover {
background-color: #F5F5F5;
}
.homework-card:hover .delete-link {
opacity: 1;
}
.homework-card:last-child {
border-bottom: none;
}
/* 卡片头部 */
.card-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 16px;
}
.homework-title {
font-size: 16px;
font-weight: 400;
color: #062333;
margin: 0;
}
.status-badge {
font-size: 16px;
font-weight: 400;
padding: 0;
}
.status-badge.ended {
color: #999999;
}
.status-badge.publishing {
color: #FF9800;
}
/* 卡片内容 */
.card-content {
display: flex;
justify-content: space-between;
gap: 20px;
}
.content-left {
flex: 1;
}
.homework-desc {
font-size: 14px;
color: #666;
line-height: 1.5;
margin: 0 0 20px 0;
}
.homework-info {
display: flex;
gap: 30px;
}
.info-item {
display: flex;
align-items: center;
gap: 6px;
}
.info-item .icon {
width: 12px;
height: 12px;
}
.info-item .text {
font-size: 12px;
color: #999;
}
/* 右侧内容 */
.content-right {
display: flex;
align-items: flex-end;
gap: 80px;
}
.delete-link {
position: absolute;
top: 50%;
right: 35%;
transform: translateY(-50%);
color: #2196F3;
font-size: 14px;
text-decoration: none;
cursor: pointer;
align-self: flex-start;
opacity: 0;
}
.delete-link:hover {
text-decoration: underline;
}
.stats-area {
display: flex;
align-items: flex-end;
gap: 15px;
}
.big-number {
font-size: 30px;
font-weight: bold;
color: #062333;
line-height: 0.9;
}
.label {
font-size: 12px;
line-height: 1.2;
}
.label.primary {
color: #062333;
font-weight: 500;
}
.label.secondary {
color: #999;
}
/* 操作按钮 */
.action-button {
background: #0288D1;
color: white;
border: none;
padding: 6px 24px;
border-radius: 2px;
font-size: 16px;
cursor: pointer;
transition: background 0.3s ease;
min-width: 92px;
}
.action-button:hover {
background: #0288D1;
}
.action-button.review {}
.action-button.view {
background: #0288D1;
}
/* 发布中状态特殊布局调整 */
.homework-card.publishing .content-right {
position: relative;
}
.homework-card.publishing .stats-area {
margin-top: 20px;
}
</style>