2025-08-21 19:39:07 +08:00
|
|
|
|
<template>
|
|
|
|
|
<div class="chapter-management">
|
2025-08-22 16:59:07 +08:00
|
|
|
|
<!-- 顶部操作栏 -->
|
|
|
|
|
<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>
|
2025-08-21 19:39:07 +08:00
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
<script setup lang="ts">
|
2025-08-22 16:59:07 +08:00
|
|
|
|
import { ref, computed, h } from 'vue'
|
|
|
|
|
import { NButton, useMessage, NDataTable, NConfigProvider, zhCN, dateZhCN } from 'naive-ui'
|
|
|
|
|
import type { DataTableColumns } from 'naive-ui'
|
2025-08-21 19:39:07 +08:00
|
|
|
|
|
2025-08-22 16:59:07 +08:00
|
|
|
|
const message = useMessage()
|
|
|
|
|
|
|
|
|
|
// 章节类型定义
|
|
|
|
|
interface Chapter {
|
|
|
|
|
id: number
|
|
|
|
|
name: string
|
|
|
|
|
type: string
|
|
|
|
|
sort: string | number
|
|
|
|
|
createTime: string
|
|
|
|
|
isParent: boolean
|
|
|
|
|
children?: Chapter[]
|
|
|
|
|
expanded?: boolean
|
2025-08-21 19:39:07 +08:00
|
|
|
|
}
|
|
|
|
|
|
2025-08-22 16:59:07 +08:00
|
|
|
|
// 搜索关键词
|
|
|
|
|
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
|
2025-08-21 19:39:07 +08:00
|
|
|
|
}
|
|
|
|
|
|
2025-08-22 16:59:07 +08:00
|
|
|
|
// 行样式名称
|
|
|
|
|
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 {
|
|
|
|
|
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;
|
2025-08-21 19:39:07 +08:00
|
|
|
|
}
|
|
|
|
|
|
2025-08-22 16:59:07 +08:00
|
|
|
|
.nav-button:hover:not(.disabled) {
|
|
|
|
|
color: #0088D1;
|
2025-08-21 19:39:07 +08:00
|
|
|
|
}
|
|
|
|
|
</style>
|