feat: 对接部分题库接口;添加考试界面;部分界面样式优化
This commit is contained in:
parent
57eb57d0ce
commit
590af0951f
@ -2,6 +2,7 @@
|
||||
import { ApiRequest } from '../request'
|
||||
import type {
|
||||
ApiResponse,
|
||||
ApiResponseWithResult,
|
||||
Repo,
|
||||
Question,
|
||||
CreateRepoRequest,
|
||||
@ -36,19 +37,54 @@ export class ExamApi {
|
||||
/**
|
||||
* 获取课程题库
|
||||
*/
|
||||
static async getCourseRepoList(): Promise<ApiResponse<Repo[]>> {
|
||||
const response = await ApiRequest.get<Repo[]>(`/biz/repo/repoList`)
|
||||
static async getCourseRepoList(): Promise<ApiResponseWithResult<Repo[]>> {
|
||||
const response = await ApiRequest.get<{ result: Repo[] }>(`/biz/repo/repoList`)
|
||||
console.log('✅ 获取课程题库列表成功:', response)
|
||||
return response
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取课程列表(用于题库筛选)
|
||||
*/
|
||||
static async getCourseList(): Promise<ApiResponse<{ id: string; name: string }[]>> {
|
||||
try {
|
||||
// 调用现有的课程列表API,但只返回id和name字段
|
||||
const response = await ApiRequest.get<any>('/biz/course/list')
|
||||
console.log('✅ 获取课程列表成功:', response)
|
||||
|
||||
// 处理响应数据,只提取id和name
|
||||
if (response.data && response.data.success && response.data.result) {
|
||||
const courseList = response.data.result.map((course: any) => ({
|
||||
id: course.id,
|
||||
name: course.name || course.title || '未命名课程'
|
||||
}))
|
||||
|
||||
return {
|
||||
code: response.data.code || 200,
|
||||
message: response.data.message || 'success',
|
||||
data: courseList
|
||||
}
|
||||
}
|
||||
|
||||
// 如果响应格式不符合预期,返回空数组
|
||||
return {
|
||||
code: 200,
|
||||
message: 'success',
|
||||
data: []
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取课程列表失败:', error)
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除题库
|
||||
*/
|
||||
static async deleteRepo(id: string): Promise<ApiResponse<string>> {
|
||||
console.log('🚀 删除题库:', { id })
|
||||
const response = await ApiRequest.delete<string>('/gen/repo/repo/delete', {
|
||||
params: { id }
|
||||
id
|
||||
})
|
||||
console.log('✅ 删除题库成功:', response)
|
||||
return response
|
||||
|
@ -8,6 +8,16 @@ export interface ApiResponse<T = any> {
|
||||
timestamp?: string
|
||||
}
|
||||
|
||||
// 带有 result 包装的响应类型
|
||||
export interface ApiResponseWithResult<T = any> {
|
||||
code: number
|
||||
message: string
|
||||
data: {
|
||||
result: T
|
||||
}
|
||||
timestamp?: string
|
||||
}
|
||||
|
||||
// 分页响应类型
|
||||
export interface PaginationResponse<T> {
|
||||
list: T[]
|
||||
@ -695,6 +705,8 @@ export interface Repo {
|
||||
createTime: string
|
||||
updateBy: string
|
||||
updateTime: string
|
||||
courseId?: string // 所属课程ID
|
||||
courseName?: string // 所属课程名称
|
||||
}
|
||||
|
||||
export interface Question {
|
||||
|
301
src/data/mockExamData.ts
Normal file
301
src/data/mockExamData.ts
Normal file
@ -0,0 +1,301 @@
|
||||
// 模拟考试数据
|
||||
export const mockExamData = {
|
||||
id: "exam_001",
|
||||
examName: "计算机基础知识测试",
|
||||
duration: 90, // 考试时长(分钟)
|
||||
totalScore: 100,
|
||||
questions: [
|
||||
{
|
||||
id: "section_1",
|
||||
title: "第一大题:单选题",
|
||||
type: "section",
|
||||
description: "请从四个选项中选择一个正确答案",
|
||||
subQuestions: [
|
||||
{
|
||||
id: "q1",
|
||||
type: "single_choice",
|
||||
title: "在HTML中,用于定义无序列表的标签是?",
|
||||
score: 2,
|
||||
options: [
|
||||
{
|
||||
id: "q1_a",
|
||||
content: "<ol>"
|
||||
},
|
||||
{
|
||||
id: "q1_b",
|
||||
content: "<ul>"
|
||||
},
|
||||
{
|
||||
id: "q1_c",
|
||||
content: "<li>"
|
||||
},
|
||||
{
|
||||
id: "q1_d",
|
||||
content: "<dl>"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: "q2",
|
||||
type: "single_choice",
|
||||
title: "CSS中用于设置字体大小的属性是?",
|
||||
score: 2,
|
||||
options: [
|
||||
{
|
||||
id: "q2_a",
|
||||
content: "font-weight"
|
||||
},
|
||||
{
|
||||
id: "q2_b",
|
||||
content: "font-size"
|
||||
},
|
||||
{
|
||||
id: "q2_c",
|
||||
content: "font-family"
|
||||
},
|
||||
{
|
||||
id: "q2_d",
|
||||
content: "font-style"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: "q3",
|
||||
type: "single_choice",
|
||||
title: "JavaScript中声明变量使用的关键字是?",
|
||||
score: 2,
|
||||
options: [
|
||||
{
|
||||
id: "q3_a",
|
||||
content: "var"
|
||||
},
|
||||
{
|
||||
id: "q3_b",
|
||||
content: "let"
|
||||
},
|
||||
{
|
||||
id: "q3_c",
|
||||
content: "const"
|
||||
},
|
||||
{
|
||||
id: "q3_d",
|
||||
content: "以上都是"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: "q4",
|
||||
type: "single_choice",
|
||||
title: "HTTP协议默认端口号是?",
|
||||
score: 2,
|
||||
options: [
|
||||
{
|
||||
id: "q4_a",
|
||||
content: "21"
|
||||
},
|
||||
{
|
||||
id: "q4_b",
|
||||
content: "80"
|
||||
},
|
||||
{
|
||||
id: "q4_c",
|
||||
content: "443"
|
||||
},
|
||||
{
|
||||
id: "q4_d",
|
||||
content: "8080"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: "q5",
|
||||
type: "single_choice",
|
||||
title: "下列哪个不是Vue.js的生命周期钩子?",
|
||||
score: 2,
|
||||
options: [
|
||||
{
|
||||
id: "q5_a",
|
||||
content: "mounted"
|
||||
},
|
||||
{
|
||||
id: "q5_b",
|
||||
content: "created"
|
||||
},
|
||||
{
|
||||
id: "q5_c",
|
||||
content: "destroyed"
|
||||
},
|
||||
{
|
||||
id: "q5_d",
|
||||
content: "rendered"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: "section_2",
|
||||
title: "第二大题:多选题",
|
||||
type: "section",
|
||||
description: "请从选项中选择所有正确答案",
|
||||
subQuestions: [
|
||||
{
|
||||
id: "q6",
|
||||
type: "multiple_choice",
|
||||
title: "下列哪些是前端开发常用的框架?",
|
||||
score: 3,
|
||||
options: [
|
||||
{
|
||||
id: "q6_a",
|
||||
content: "React"
|
||||
},
|
||||
{
|
||||
id: "q6_b",
|
||||
content: "Vue.js"
|
||||
},
|
||||
{
|
||||
id: "q6_c",
|
||||
content: "Angular"
|
||||
},
|
||||
{
|
||||
id: "q6_d",
|
||||
content: "Express.js"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: "q7",
|
||||
type: "multiple_choice",
|
||||
title: "CSS布局方式包括哪些?",
|
||||
score: 3,
|
||||
options: [
|
||||
{
|
||||
id: "q7_a",
|
||||
content: "Flexbox"
|
||||
},
|
||||
{
|
||||
id: "q7_b",
|
||||
content: "Grid"
|
||||
},
|
||||
{
|
||||
id: "q7_c",
|
||||
content: "Float"
|
||||
},
|
||||
{
|
||||
id: "q7_d",
|
||||
content: "Position"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: "q8",
|
||||
type: "multiple_choice",
|
||||
title: "HTTP状态码中,表示成功的有哪些?",
|
||||
score: 3,
|
||||
options: [
|
||||
{
|
||||
id: "q8_a",
|
||||
content: "200"
|
||||
},
|
||||
{
|
||||
id: "q8_b",
|
||||
content: "201"
|
||||
},
|
||||
{
|
||||
id: "q8_c",
|
||||
content: "404"
|
||||
},
|
||||
{
|
||||
id: "q8_d",
|
||||
content: "500"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: "section_3",
|
||||
title: "第三大题:判断题",
|
||||
type: "section",
|
||||
description: "请判断下列说法是否正确",
|
||||
subQuestions: [
|
||||
{
|
||||
id: "q9",
|
||||
type: "true_false",
|
||||
title: "HTML是一种编程语言。",
|
||||
score: 2
|
||||
},
|
||||
{
|
||||
id: "q10",
|
||||
type: "true_false",
|
||||
title: "CSS可以用来控制网页的布局和样式。",
|
||||
score: 2
|
||||
},
|
||||
{
|
||||
id: "q11",
|
||||
type: "true_false",
|
||||
title: "JavaScript只能在浏览器中运行。",
|
||||
score: 2
|
||||
},
|
||||
{
|
||||
id: "q12",
|
||||
type: "true_false",
|
||||
title: "Git是一个版本控制系统。",
|
||||
score: 2
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: "section_4",
|
||||
title: "第四大题:填空题",
|
||||
type: "section",
|
||||
description: "请在空白处填入正确答案",
|
||||
subQuestions: [
|
||||
{
|
||||
id: "q13",
|
||||
type: "fill_blank",
|
||||
title: "HTML文档的基本结构包括_____、_____和_____三个主要部分。",
|
||||
score: 3,
|
||||
fillBlanks: [
|
||||
{ id: "blank1", answer: "DOCTYPE声明" },
|
||||
{ id: "blank2", answer: "head" },
|
||||
{ id: "blank3", answer: "body" }
|
||||
]
|
||||
},
|
||||
{
|
||||
id: "q14",
|
||||
type: "fill_blank",
|
||||
title: "CSS选择器中,_____选择器用于选择具有特定ID的元素,_____选择器用于选择具有特定类名的元素。",
|
||||
score: 2,
|
||||
fillBlanks: [
|
||||
{ id: "blank4", answer: "ID" },
|
||||
{ id: "blank5", answer: "类" }
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: "section_5",
|
||||
title: "第五大题:简答题",
|
||||
type: "section",
|
||||
description: "请根据题目要求作答",
|
||||
subQuestions: [
|
||||
{
|
||||
id: "q15",
|
||||
type: "short_answer",
|
||||
title: "请简述响应式网页设计的基本原理和实现方法。",
|
||||
score: 5
|
||||
},
|
||||
{
|
||||
id: "q16",
|
||||
type: "short_answer",
|
||||
title: "解释JavaScript中异步编程的概念,并举例说明。",
|
||||
score: 5
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
// 导出考试数据用于在组件中使用
|
||||
export default mockExamData;
|
@ -72,6 +72,7 @@ import AddQuestion from '@/views/teacher/ExamPages/AddQuestion.vue'
|
||||
import StudentList from '@/views/teacher/ExamPages/StudentList.vue'
|
||||
import GradingPage from '@/views/teacher/ExamPages/GradingPage.vue'
|
||||
import ExamTaking from '@/views/teacher/ExamPages/ExamTaking.vue'
|
||||
import ExamNoticeBeforeStart from '@/views/teacher/ExamPages/ExamNoticeBeforeStart.vue'
|
||||
|
||||
import ChapterEditor from '@/views/teacher/course/ChapterEditor.vue'
|
||||
|
||||
@ -366,6 +367,12 @@ const routes: RouteRecordRaw[] = [
|
||||
|
||||
]
|
||||
},
|
||||
{
|
||||
path: '/exam/notice/:id',
|
||||
name: 'ExamNoticeBeforeStart',
|
||||
component: ExamNoticeBeforeStart,
|
||||
meta: { title: '考前须知' }
|
||||
},
|
||||
{
|
||||
path: '/taking/:id',
|
||||
name: 'ExamTaking',
|
||||
@ -457,7 +464,7 @@ const routes: RouteRecordRaw[] = [
|
||||
meta: { title: '学习中心', requiresAuth: true }
|
||||
},
|
||||
{
|
||||
path: '/profile',
|
||||
path: '/profile/:tabKey?',
|
||||
name: 'Profile',
|
||||
component: Profile,
|
||||
meta: { title: '个人中心', requiresAuth: true }
|
||||
|
@ -11,8 +11,6 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<!-- 考试说明页面 -->
|
||||
<div class="exam-instructions" v-if="!examStarted">
|
||||
<div class="container">
|
||||
@ -881,8 +879,15 @@ const questionsByType = computed(() => {
|
||||
|
||||
|
||||
|
||||
// 开始考试
|
||||
// 开始考试(从考前须知页面跳转到这里)
|
||||
const startExam = () => {
|
||||
// 跳转到考前须知页面
|
||||
const examId = sectionId.value || 1 // 使用 sectionId 作为考试ID,如果没有则默认为1
|
||||
router.push(`/exam/notice/${examId}`)
|
||||
}
|
||||
|
||||
// 实际开始考试
|
||||
const beginExam = () => {
|
||||
examStarted.value = true
|
||||
remainingTime.value = examDuration.value * 60
|
||||
startTimer()
|
||||
@ -1220,7 +1225,7 @@ onMounted(() => {
|
||||
// 检查是否从考前须知页面跳转过来,如果是则直接开始考试
|
||||
const fromNotice = route.query.fromNotice
|
||||
if (fromNotice === 'true') {
|
||||
startExam()
|
||||
beginExam()
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
@ -1071,14 +1071,14 @@
|
||||
import { ref, computed, onMounted, onActivated, reactive } from 'vue'
|
||||
import { useMessage, NInput, NForm, NFormItem } from 'naive-ui'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { useUserStore } from '@/stores/user'
|
||||
import SafeAvatar from '@/components/common/SafeAvatar.vue'
|
||||
import QuillEditor from '@/components/common/QuillEditor.vue'
|
||||
import { useRouter, useRoute } from 'vue-router'
|
||||
|
||||
const { t, locale } = useI18n()
|
||||
const router = useRouter()
|
||||
|
||||
const route = useRoute()
|
||||
// 轮播图根据语言动态切换
|
||||
const bannerImage = computed(() => {
|
||||
return locale.value === 'zh' ? '/banners/banner8.png' : '/banners/banner1-en.png'
|
||||
@ -2288,6 +2288,7 @@ const isMessageTab = computed(() => activeTab.value === 'message')
|
||||
// 处理菜单选择
|
||||
const handleMenuSelect = (key: TabType) => {
|
||||
activeTab.value = key
|
||||
router.push(`/profile/${key}`)
|
||||
// message.info(`切换到${getTabTitle(key)}`)
|
||||
}
|
||||
|
||||
@ -2392,7 +2393,8 @@ const startExam = (examId: number) => {
|
||||
|
||||
// 继续考试
|
||||
const continueExam = (examId: number) => {
|
||||
message.info(`继续考试 ${examId}`)
|
||||
// message.info(`继续考试 ${examId}`)
|
||||
router.push(`/exam/notice/${examId}`)
|
||||
}
|
||||
|
||||
// 查看考试结果
|
||||
@ -2759,6 +2761,9 @@ onMounted(() => {
|
||||
window.location.reload()
|
||||
}, 100)
|
||||
}
|
||||
|
||||
const tabKey = <TabType>route.params.tabKey || 'courses'
|
||||
handleMenuSelect(tabKey)
|
||||
})
|
||||
|
||||
onActivated(() => {
|
||||
|
333
src/views/teacher/ExamPages/ExamNoticeBeforeStart.vue
Normal file
333
src/views/teacher/ExamPages/ExamNoticeBeforeStart.vue
Normal file
@ -0,0 +1,333 @@
|
||||
<template>
|
||||
<div class="exam-notice-container">
|
||||
<!-- 页面标题 -->
|
||||
<div class="page-header">
|
||||
<h1 class="title">考试中心</h1>
|
||||
<span class="subTit">涵盖多种题型,全方位考核,AI智能阅卷</span>
|
||||
</div>
|
||||
<div class="exam-notice-content">
|
||||
<!-- 考前须知主体 -->
|
||||
<div class="notice-main">
|
||||
<h1 class="notice-title">考前须知</h1>
|
||||
|
||||
<!-- 考试信息 -->
|
||||
<div class="exam-info">
|
||||
<div class="exam-meta">
|
||||
<span class="exam-time">考试时间:2025年8月1日-8月16日</span>
|
||||
<span class="exam-name">考试名称:[2025] 教学技能提高暨自主学习课程期末考试</span>
|
||||
<span class="exam-duration">考试时长:120分钟</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 考试规则列表 -->
|
||||
<div class="rules-list">
|
||||
<div class="rule-item">
|
||||
<span class="rule-number">1.</span>
|
||||
<span class="rule-text">考试时间:2025年8月1日-8月16日,在此期间考生可自行安排开始考试,考试时长共120分钟。</span>
|
||||
</div>
|
||||
|
||||
<div class="rule-item">
|
||||
<span class="rule-number">2.</span>
|
||||
<span class="rule-text">考生必须持本人身份证明准证入场,两证缺一者不得参加考试。</span>
|
||||
</div>
|
||||
|
||||
<div class="rule-item">
|
||||
<span class="rule-number">3.</span>
|
||||
<span class="rule-text">考试时严禁考生携带相关文书籍、资料、笔记本电脑、自带纸张等。</span>
|
||||
</div>
|
||||
|
||||
<div class="rule-item">
|
||||
<span class="rule-number">4.</span>
|
||||
<span
|
||||
class="rule-text">本次考试采用机读卡答题方式,考生在机读卡上填写姓名,用2B铅笔在规定位置填涂考号、答案。未按规定在机读卡上作答造成本次考试无效者记录的,责任自负。</span>
|
||||
</div>
|
||||
|
||||
<div class="rule-item">
|
||||
<span class="rule-number">5.</span>
|
||||
<span class="rule-text">考生若需要借用考试用具,须举手示意监考人员呼叫,不得向其他考生直接借取。</span>
|
||||
</div>
|
||||
|
||||
<div class="rule-item">
|
||||
<span class="rule-number">6.</span>
|
||||
<span
|
||||
class="rule-text">考试时,请考生自觉关闭手机,并将携带的随身证明件、签字笔、2B铅笔、橡皮除以外的物品存放在指定位置,若手机响过在考生座位上响起按作弊处理。</span>
|
||||
</div>
|
||||
|
||||
<div class="rule-item">
|
||||
<span class="rule-number">7.</span>
|
||||
<span class="rule-text">考生答题完毕不得诵背清。机读卡带离考场,否则现场作弊处理。</span>
|
||||
</div>
|
||||
|
||||
<div class="rule-item">
|
||||
<span class="rule-number">8.</span>
|
||||
<span class="rule-text">找人代考者,按作弊处理,直至替考者离开考场,假证件予以没收。</span>
|
||||
</div>
|
||||
|
||||
<div class="rule-item">
|
||||
<span class="rule-number">9.</span>
|
||||
<span class="rule-text">必须服从监考人员的管理,严格遵守考场纪律,对不服从监考人员管教,抗拒者按作弊的,取消考试资格。</span>
|
||||
</div>
|
||||
|
||||
<div class="rule-item">
|
||||
<span class="rule-number">10.</span>
|
||||
<span
|
||||
class="rule-text">考生违反考生须知有关规定或或违反考场纪律进行警察时,监考人员可要求该名考生离开考场,收回试卷,,取消考试资格,并在《考场记录单》上作好记录。</span>
|
||||
</div>
|
||||
|
||||
<div class="rule-item">
|
||||
<span class="rule-number">11.</span>
|
||||
<span class="rule-text">考试成绩将于十个工作日完成此间进行公示,查询成绩网址:www.baidu.com。</span>
|
||||
</div>
|
||||
|
||||
<div class="contact-info">
|
||||
<span>*如有疑问,可致电:0871-5533221。</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 按钮区域 -->
|
||||
<div class="button-area">
|
||||
<n-button size="large" :type="countdown > 0 ? 'default' : 'primary'" :disabled="countdown > 0"
|
||||
@click="handleStartExam">
|
||||
我已知晓,开始考试 <span v-if="countdown > 0">({{ countdown }})秒</span>
|
||||
</n-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted, onUnmounted } from 'vue'
|
||||
import { useRouter, useRoute } from 'vue-router'
|
||||
import { NButton } from 'naive-ui'
|
||||
|
||||
const router = useRouter()
|
||||
const route = useRoute()
|
||||
const countdown = ref(10)
|
||||
let timer: NodeJS.Timeout | null = null
|
||||
|
||||
// 倒计时功能
|
||||
onMounted(() => {
|
||||
timer = setInterval(() => {
|
||||
if (countdown.value > 0) {
|
||||
countdown.value--
|
||||
} else {
|
||||
// 倒计时结束,可以开始考试
|
||||
if (timer) {
|
||||
clearInterval(timer)
|
||||
timer = null
|
||||
}
|
||||
}
|
||||
}, 1000)
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
if (timer) {
|
||||
clearInterval(timer)
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
// 开始考试
|
||||
const handleStartExam = () => {
|
||||
if (countdown.value > 0) {
|
||||
return // 倒计时未结束不能开始
|
||||
}
|
||||
|
||||
// 设置考试数据到 sessionStorage(模拟从API获取的数据)
|
||||
const examData = {
|
||||
examId: route.params.id,
|
||||
examName: '[2025] 教学技能提高暨自主学习课程期末考试',
|
||||
duration: 120, // 120分钟
|
||||
questions: [] // 这里可以设置真实的考试题目数据
|
||||
}
|
||||
|
||||
sessionStorage.setItem('examData', JSON.stringify(examData))
|
||||
|
||||
// 获取考试ID并跳转到考试页面
|
||||
const examId = route.params.id
|
||||
router.replace(`/taking/${examId}`)
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.exam-notice-container {
|
||||
min-height: 100vh;
|
||||
background-color: #f5f5f5;
|
||||
padding: 0 20px;
|
||||
}
|
||||
|
||||
.exam-notice-content {
|
||||
max-width: 1000px;
|
||||
margin: 10px auto;
|
||||
background: white;
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.page-header {
|
||||
background: white;
|
||||
padding: 40px 0;
|
||||
text-align: center;
|
||||
border-bottom: 1px solid #e8e8e8;
|
||||
background-size: 100% 100%;
|
||||
background-image: url('/banners/考前须知.png');
|
||||
}
|
||||
|
||||
.page-header>.title{
|
||||
font-family: AlimamaShuHeiTi, AlimamaShuHeiTi;
|
||||
font-weight: bold;
|
||||
font-size: 32px;
|
||||
color: #000000;
|
||||
}
|
||||
|
||||
.page-header>.subtitle{
|
||||
font-family: PingFangSC, PingFang SC;
|
||||
font-weight: 400;
|
||||
font-size: 14px;
|
||||
color: #000000;
|
||||
}
|
||||
|
||||
.course-name {
|
||||
color: #666;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.notice-main {
|
||||
padding: 40px 60px 60px;
|
||||
}
|
||||
|
||||
.notice-title {
|
||||
text-align: center;
|
||||
font-size: 28px;
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
margin-bottom: 40px;
|
||||
}
|
||||
|
||||
.exam-info {
|
||||
margin-bottom: 40px;
|
||||
}
|
||||
|
||||
.exam-meta {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 16px 0;
|
||||
border-bottom: 1px solid #e9ecef;
|
||||
flex-wrap: wrap;
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
.exam-time,
|
||||
.exam-name,
|
||||
.exam-duration {
|
||||
color: #666;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.exam-name {
|
||||
flex: 1;
|
||||
text-align: center;
|
||||
min-width: 300px;
|
||||
}
|
||||
|
||||
.rules-list {
|
||||
margin-bottom: 50px;
|
||||
}
|
||||
|
||||
.rule-item {
|
||||
display: flex;
|
||||
margin-bottom: 10px;
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
.rule-number {
|
||||
color: #333;
|
||||
font-weight: 500;
|
||||
min-width: 24px;
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
.rule-text {
|
||||
color: #555;
|
||||
font-size: 14px;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.contact-info {
|
||||
margin-top: 30px;
|
||||
color: #666;
|
||||
font-size: 14px;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.button-area {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
gap: 20px;
|
||||
margin-top: 40px;
|
||||
}
|
||||
|
||||
.cancel-button,
|
||||
.start-button {
|
||||
padding: 12px 32px;
|
||||
font-size: 16px;
|
||||
border-radius: 6px;
|
||||
min-width: 180px;
|
||||
}
|
||||
|
||||
.cancel-button {
|
||||
background-color: #f8f9fa;
|
||||
border: 1px solid #dee2e6;
|
||||
color: #6c757d;
|
||||
}
|
||||
|
||||
.cancel-button:hover {
|
||||
background-color: #e9ecef;
|
||||
}
|
||||
|
||||
.start-button {
|
||||
background-color: #007bff;
|
||||
border-color: #007bff;
|
||||
}
|
||||
|
||||
.start-button:hover {
|
||||
background-color: #0056b3;
|
||||
border-color: #0056b3;
|
||||
}
|
||||
|
||||
.start-button:disabled {
|
||||
opacity: 0.6;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
/* 响应式设计 */
|
||||
@media (max-width: 768px) {
|
||||
.notice-main {
|
||||
padding: 30px 20px 40px;
|
||||
}
|
||||
|
||||
.exam-meta {
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.exam-name {
|
||||
text-align: left;
|
||||
min-width: auto;
|
||||
}
|
||||
|
||||
.button-area {
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.cancel-button,
|
||||
.start-button {
|
||||
width: 100%;
|
||||
max-width: 300px;
|
||||
}
|
||||
}
|
||||
</style>
|
File diff suppressed because it is too large
Load Diff
@ -6,59 +6,35 @@
|
||||
<n-button type="primary" @click="addQuestionBank">新建题库</n-button>
|
||||
<n-button ghost @click="importQuestionBank">导入题库</n-button>
|
||||
<n-button ghost @click="exportQuestionBank">导出题库</n-button>
|
||||
<n-button type="error" ghost @click="deleteSelected" :disabled="selectedRowKeys.length === 0">删除</n-button>
|
||||
<n-input
|
||||
v-model:value="filters.keyword"
|
||||
placeholder="请输入想要搜索的内容"
|
||||
style="width: 200px"
|
||||
clearable
|
||||
/>
|
||||
<n-button type="error" ghost @click="deleteSelected"
|
||||
:disabled="selectedRowKeys.length === 0">删除</n-button>
|
||||
<n-select v-model:value="filters.courseId" placeholder="所属课程" :options="courseOptions" clearable
|
||||
style="width: 150px" @update:value="searchQuestionBanks" />
|
||||
<n-input v-model:value="filters.keyword" placeholder="请输入想要搜索的内容" style="width: 200px" clearable />
|
||||
<n-button type="primary" @click="searchQuestionBanks">搜索</n-button>
|
||||
</n-space>
|
||||
</div>
|
||||
|
||||
<n-data-table
|
||||
ref="tableRef"
|
||||
:columns="columns"
|
||||
:data="questionBankList"
|
||||
:loading="loading"
|
||||
:pagination="paginationConfig"
|
||||
:row-key="(row: QuestionBank) => row.id"
|
||||
:checked-row-keys="selectedRowKeys"
|
||||
@update:checked-row-keys="handleCheck"
|
||||
class="question-bank-table"
|
||||
:single-line="false"
|
||||
/>
|
||||
<n-data-table ref="tableRef" :columns="columns" :data="questionBankList" :loading="loading"
|
||||
:pagination="paginationConfig" :row-key="(row: QuestionBank) => row.id" :checked-row-keys="selectedRowKeys"
|
||||
@update:checked-row-keys="handleCheck" class="question-bank-table" :single-line="false" />
|
||||
|
||||
<!-- 新建/编辑题库弹窗 -->
|
||||
<n-modal
|
||||
v-model:show="showCreateModal"
|
||||
preset="dialog"
|
||||
:title="isEditMode ? '编辑题库' : '新建题库'"
|
||||
style="width: 500px;"
|
||||
>
|
||||
<n-modal v-model:show="showCreateModal" preset="dialog" :title="isEditMode ? '编辑题库' : '新建题库'"
|
||||
style="width: 500px;">
|
||||
<div class="create-modal-content">
|
||||
<div class="form-item">
|
||||
<label>题库名称:</label>
|
||||
<n-input
|
||||
v-model:value="createForm.name"
|
||||
placeholder="请输入题库名称"
|
||||
style="width: 100%;"
|
||||
/>
|
||||
<n-input v-model:value="createForm.name" placeholder="请输入题库名称" style="width: 100%;" />
|
||||
</div>
|
||||
|
||||
|
||||
<div class="form-item">
|
||||
<label>题库描述:</label>
|
||||
<n-input
|
||||
v-model:value="createForm.description"
|
||||
type="textarea"
|
||||
placeholder="请输入题库描述"
|
||||
style="width: 100%;"
|
||||
:rows="3"
|
||||
/>
|
||||
<n-input v-model:value="createForm.description" type="textarea" placeholder="请输入题库描述"
|
||||
style="width: 100%;" :rows="3" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<template #action>
|
||||
<n-space>
|
||||
<n-button @click="closeCreateModal">取消</n-button>
|
||||
@ -70,22 +46,18 @@
|
||||
</n-modal>
|
||||
|
||||
<!-- 导入弹窗 -->
|
||||
<ImportModal
|
||||
v-model:show="showImportModal"
|
||||
template-name="question_bank_template.xlsx"
|
||||
import-type="questionBank"
|
||||
@success="handleImportSuccess"
|
||||
@template-download="handleTemplateDownload"
|
||||
/>
|
||||
<ImportModal v-model:show="showImportModal" template-name="question_bank_template.xlsx"
|
||||
import-type="questionBank" @success="handleImportSuccess" @template-download="handleTemplateDownload" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, reactive, computed, onMounted, h, VNode } from 'vue';
|
||||
import { NButton, NSpace, useMessage, useDialog } from 'naive-ui';
|
||||
import { NButton, NSpace, NSelect, useMessage, useDialog } from 'naive-ui';
|
||||
import { useRouter } from 'vue-router';
|
||||
import ImportModal from '@/components/common/ImportModal.vue';
|
||||
import { ExamApi } from '@/api'
|
||||
import { ExamApi } from '@/api';
|
||||
import type { Repo } from '@/api/types';
|
||||
|
||||
// 消息提示和对话框
|
||||
const message = useMessage();
|
||||
@ -104,11 +76,19 @@ interface QuestionBank {
|
||||
creator: string;
|
||||
createTime: string;
|
||||
lastModified: string;
|
||||
courseName?: string; // 所属课程名称
|
||||
}
|
||||
|
||||
// 课程选项接口
|
||||
interface CourseOption {
|
||||
label: string;
|
||||
value: string;
|
||||
}
|
||||
|
||||
// 筛选条件
|
||||
const filters = reactive({
|
||||
keyword: ''
|
||||
keyword: '',
|
||||
courseId: '' // 所属课程筛选
|
||||
});
|
||||
|
||||
|
||||
@ -117,6 +97,9 @@ const loading = ref(false);
|
||||
const selectedRowKeys = ref<string[]>([]);
|
||||
const questionBankList = ref<QuestionBank[]>([]);
|
||||
|
||||
// 课程列表状态
|
||||
const courseOptions = ref<CourseOption[]>([]);
|
||||
|
||||
// 新建/编辑题库相关状态
|
||||
const showCreateModal = ref(false);
|
||||
const isEditMode = ref(false);
|
||||
@ -178,7 +161,7 @@ const createColumns = ({
|
||||
{
|
||||
title: '题库名称',
|
||||
key: 'name',
|
||||
width: 200,
|
||||
width: 180,
|
||||
ellipsis: {
|
||||
tooltip: true
|
||||
},
|
||||
@ -196,7 +179,7 @@ const createColumns = ({
|
||||
{
|
||||
title: '题库描述',
|
||||
key: 'description',
|
||||
width: 250,
|
||||
width: 200,
|
||||
ellipsis: {
|
||||
tooltip: true
|
||||
}
|
||||
@ -204,12 +187,27 @@ const createColumns = ({
|
||||
{
|
||||
title: '题目数量',
|
||||
key: 'questionCount',
|
||||
width: 100,
|
||||
width: 80,
|
||||
align: 'center' as const,
|
||||
},
|
||||
{
|
||||
title: '所属课程',
|
||||
key: 'courseName',
|
||||
width: 160,
|
||||
align: 'center' as const,
|
||||
ellipsis: {
|
||||
tooltip: true
|
||||
},
|
||||
render(row: QuestionBank) {
|
||||
return h('span', { style: 'color: #1890ff; font-weight: 500;' }, row.questionCount.toString());
|
||||
return row.courseName || '暂无课程';
|
||||
}
|
||||
},
|
||||
{
|
||||
title: '权限',
|
||||
key: 'permissions',
|
||||
width: 160,
|
||||
align: 'center' as const
|
||||
},
|
||||
{
|
||||
title: '创建人',
|
||||
key: 'creator',
|
||||
@ -219,52 +217,43 @@ const createColumns = ({
|
||||
{
|
||||
title: '创建时间',
|
||||
key: 'createTime',
|
||||
width: 160,
|
||||
align: 'center' as const
|
||||
},
|
||||
{
|
||||
title: '最后修改',
|
||||
key: 'lastModified',
|
||||
width: 160,
|
||||
width: 150,
|
||||
align: 'center' as const
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
key: 'actions',
|
||||
width: 150,
|
||||
width: 180,
|
||||
align: 'center' as const,
|
||||
render(row: QuestionBank) {
|
||||
const buttons: VNode[] = [];
|
||||
|
||||
|
||||
buttons.push(
|
||||
h(NButton, {
|
||||
size: 'small',
|
||||
type: 'primary',
|
||||
ghost: true,
|
||||
style: 'margin: 0 3px;',
|
||||
onClick: () => handleAction('进入', row)
|
||||
h(NButton, {
|
||||
size: 'small',
|
||||
type: 'primary',
|
||||
ghost: true,
|
||||
onClick: () => handleAction('进入', row)
|
||||
}, { default: () => '进入' })
|
||||
);
|
||||
|
||||
|
||||
buttons.push(
|
||||
h(NButton, {
|
||||
size: 'small',
|
||||
ghost: true,
|
||||
style: 'margin: 0 3px;',
|
||||
onClick: () => handleAction('编辑', row)
|
||||
h(NButton, {
|
||||
size: 'small',
|
||||
ghost: true,
|
||||
onClick: () => handleAction('编辑', row)
|
||||
}, { default: () => '编辑' })
|
||||
);
|
||||
|
||||
|
||||
buttons.push(
|
||||
h(NButton, {
|
||||
size: 'small',
|
||||
type: 'error',
|
||||
ghost: true,
|
||||
style: 'margin: 0 3px;',
|
||||
onClick: () => handleAction('删除', row)
|
||||
h(NButton, {
|
||||
size: 'small',
|
||||
type: 'error',
|
||||
ghost: true,
|
||||
onClick: () => handleAction('删除', row)
|
||||
}, { default: () => '删除' })
|
||||
);
|
||||
|
||||
|
||||
return h(NSpace, {}, { default: () => buttons });
|
||||
}
|
||||
}
|
||||
@ -275,7 +264,7 @@ const createColumns = ({
|
||||
const columns = createColumns({
|
||||
handleAction: (action, row) => {
|
||||
if (action === '进入') {
|
||||
enterQuestionBank(row.id);
|
||||
enterQuestionBank(row.id, row.name);
|
||||
} else if (action === '编辑') {
|
||||
editQuestionBank(row.id);
|
||||
} else if (action === '删除') {
|
||||
@ -284,27 +273,6 @@ const columns = createColumns({
|
||||
},
|
||||
});
|
||||
|
||||
// 生成模拟数据
|
||||
const generateMockData = (): QuestionBank[] => {
|
||||
const mockData: QuestionBank[] = [];
|
||||
const creators = ['王建国', '李明', '张三', '刘老师', '陈教授'];
|
||||
const names = ['计算机基础题库', '数学专业题库', '英语考试题库', '物理练习题库', '化学综合题库'];
|
||||
|
||||
for (let i = 1; i <= 20; i++) {
|
||||
mockData.push({
|
||||
id: '1960998116632399873',
|
||||
sequence: i,
|
||||
name: names[Math.floor(Math.random() * names.length)] + ` ${i}`,
|
||||
description: `这是一个包含多种题型的综合性题库,适用于教学和考试场景...`,
|
||||
questionCount: Math.floor(Math.random() * 500) + 50,
|
||||
creator: creators[Math.floor(Math.random() * creators.length)],
|
||||
createTime: '2025.08.20 09:20',
|
||||
lastModified: '2025.08.28 15:30'
|
||||
});
|
||||
}
|
||||
return mockData;
|
||||
};
|
||||
|
||||
// 处理复选框选择
|
||||
const handleCheck = (rowKeys: string[]) => {
|
||||
selectedRowKeys.value = rowKeys;
|
||||
@ -316,31 +284,77 @@ const searchQuestionBanks = () => {
|
||||
loadQuestionBanks();
|
||||
};
|
||||
|
||||
// 根据课程ID获取课程名称
|
||||
const getCourseNameById = (courseId: string): string => {
|
||||
const course = courseOptions.value.find(option => option.value === courseId);
|
||||
return course ? course.label : '';
|
||||
};
|
||||
|
||||
// 加载课程列表
|
||||
const loadCourseList = async () => {
|
||||
try {
|
||||
const res = await ExamApi.getCourseList();
|
||||
console.log('✅ 获取课程列表:', res.data);
|
||||
|
||||
courseOptions.value = [
|
||||
{ label: '全部课程', value: '' },
|
||||
...res.data.map(course => ({
|
||||
label: course.name,
|
||||
value: course.id
|
||||
}))
|
||||
];
|
||||
} catch (error) {
|
||||
console.error('加载课程列表失败:', error);
|
||||
// 如果加载失败,至少提供一个默认选项
|
||||
courseOptions.value = [{ label: '全部课程', value: '' }];
|
||||
}
|
||||
};
|
||||
|
||||
// 加载题库列表
|
||||
const loadQuestionBanks = async () => {
|
||||
loading.value = true;
|
||||
try {
|
||||
// TODO API调用
|
||||
// await ExamApi.getCourseRepoList();
|
||||
|
||||
const allData = generateMockData();
|
||||
// 模拟筛选
|
||||
let filteredData = allData;
|
||||
|
||||
// API调用获取题库列表
|
||||
const res = await ExamApi.getCourseRepoList();
|
||||
console.log('✅ 获取题库列表:', res.data.result);
|
||||
|
||||
// 将API返回的Repo[]类型映射为QuestionBank[]类型
|
||||
const apiData: QuestionBank[] = res.data.result.map((repo: Repo, index: number) => ({
|
||||
id: repo.id,
|
||||
sequence: index + 1,
|
||||
name: repo.title,
|
||||
description: repo.remark || '暂无描述',
|
||||
questionCount: repo.questionCount || 0,
|
||||
creator: repo.createBy || '未知',
|
||||
createTime: repo.createTime || '',
|
||||
lastModified: repo.updateTime || '',
|
||||
courseName: repo.courseName || '暂无课程'
|
||||
}));
|
||||
|
||||
// 应用搜索筛选
|
||||
let filteredData = apiData;
|
||||
if (filters.keyword) {
|
||||
filteredData = filteredData.filter(item =>
|
||||
item.name.includes(filters.keyword) ||
|
||||
filteredData = filteredData.filter(item =>
|
||||
item.name.includes(filters.keyword) ||
|
||||
item.description.includes(filters.keyword) ||
|
||||
item.creator.includes(filters.keyword)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
// 应用课程筛选
|
||||
if (filters.courseId) {
|
||||
const courseName = getCourseNameById(filters.courseId);
|
||||
filteredData = filteredData.filter(item =>
|
||||
item.courseName === courseName
|
||||
);
|
||||
}
|
||||
|
||||
// 分页处理
|
||||
pagination.total = filteredData.length;
|
||||
const start = (pagination.page - 1) * pagination.pageSize;
|
||||
const end = start + pagination.pageSize;
|
||||
questionBankList.value = filteredData.slice(start, end);
|
||||
|
||||
|
||||
} catch (error) {
|
||||
console.error('加载题库失败:', error);
|
||||
message.error('加载题库失败');
|
||||
@ -375,10 +389,10 @@ const deleteSelected = () => {
|
||||
}
|
||||
|
||||
// 获取选中的题库信息
|
||||
const selectedBanks = questionBankList.value.filter(item =>
|
||||
const selectedBanks = questionBankList.value.filter(item =>
|
||||
selectedRowKeys.value.includes(item.id)
|
||||
);
|
||||
|
||||
|
||||
const bankNames = selectedBanks.map(bank => bank.name).join('、');
|
||||
|
||||
// 二次确认弹窗
|
||||
@ -394,20 +408,20 @@ const deleteSelected = () => {
|
||||
onPositiveClick: async () => {
|
||||
try {
|
||||
// 批量调用删除接口
|
||||
const deletePromises = selectedRowKeys.value.map(id =>
|
||||
const deletePromises = selectedRowKeys.value.map(id =>
|
||||
ExamApi.deleteRepo(id)
|
||||
);
|
||||
|
||||
|
||||
await Promise.all(deletePromises);
|
||||
|
||||
|
||||
message.success(`成功删除 ${selectedRowKeys.value.length} 个题库`);
|
||||
|
||||
|
||||
// 清空选中状态
|
||||
selectedRowKeys.value = [];
|
||||
|
||||
|
||||
// 重新加载列表
|
||||
loadQuestionBanks();
|
||||
|
||||
|
||||
} catch (error) {
|
||||
console.error('批量删除题库失败:', error);
|
||||
message.error('批量删除失败,请重试');
|
||||
@ -420,28 +434,28 @@ const deleteSelected = () => {
|
||||
};
|
||||
|
||||
// 进入题库(跳转到试题管理页面)
|
||||
const enterQuestionBank = (bankId: string) => {
|
||||
router.push(`/teacher/exam-management/question-bank/${bankId}/questions`);
|
||||
const enterQuestionBank = (bankId: string, bankTitle: string) => {
|
||||
router.push(`/teacher/exam-management/question-bank/${bankId}/questions?title=${bankTitle}`);
|
||||
};
|
||||
|
||||
const editQuestionBank = (id: string) => {
|
||||
console.log('编辑题库:', id);
|
||||
|
||||
|
||||
// 查找要编辑的题库数据
|
||||
const questionBank = questionBankList.value.find(item => item.id === id);
|
||||
if (!questionBank) {
|
||||
message.error('未找到要编辑的题库');
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// 设置编辑模式
|
||||
isEditMode.value = true;
|
||||
editingId.value = id;
|
||||
|
||||
|
||||
// 回显数据
|
||||
createForm.name = questionBank.name;
|
||||
createForm.description = questionBank.description;
|
||||
|
||||
|
||||
// 显示弹窗
|
||||
showCreateModal.value = true;
|
||||
};
|
||||
@ -465,13 +479,13 @@ const deleteQuestionBank = (id: string) => {
|
||||
// 调用删除接口
|
||||
await ExamApi.deleteRepo(id);
|
||||
message.success('题库删除成功');
|
||||
|
||||
|
||||
// 重新加载列表
|
||||
loadQuestionBanks();
|
||||
|
||||
|
||||
// 清空选中状态
|
||||
selectedRowKeys.value = selectedRowKeys.value.filter(key => key !== id);
|
||||
|
||||
|
||||
} catch (error) {
|
||||
console.error('删除题库失败:', error);
|
||||
message.error('删除题库失败,请重试');
|
||||
@ -498,7 +512,7 @@ const submitQuestionBank = async () => {
|
||||
message.warning('请输入题库名称');
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
try {
|
||||
if (isEditMode.value) {
|
||||
// 编辑模式
|
||||
@ -516,10 +530,10 @@ const submitQuestionBank = async () => {
|
||||
});
|
||||
message.success('题库创建成功');
|
||||
}
|
||||
|
||||
|
||||
closeCreateModal();
|
||||
loadQuestionBanks();
|
||||
|
||||
|
||||
} catch (error) {
|
||||
console.error('提交题库失败:', error);
|
||||
message.error(isEditMode.value ? '更新题库失败,请重试' : '创建题库失败,请重试');
|
||||
@ -541,6 +555,7 @@ const handleTemplateDownload = (type?: string) => {
|
||||
|
||||
// 组件挂载时加载数据
|
||||
onMounted(() => {
|
||||
loadCourseList();
|
||||
loadQuestionBanks();
|
||||
});
|
||||
</script>
|
||||
@ -599,7 +614,7 @@ onMounted(() => {
|
||||
align-items: stretch;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
|
||||
.actions-group {
|
||||
justify-content: center;
|
||||
flex-wrap: wrap;
|
||||
@ -610,15 +625,15 @@ onMounted(() => {
|
||||
.question-bank-container {
|
||||
padding: 12px;
|
||||
}
|
||||
|
||||
|
||||
.actions-group {
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
|
||||
.actions-group .n-input {
|
||||
width: 150px !important;
|
||||
}
|
||||
|
||||
|
||||
.actions-group .n-select {
|
||||
width: 100px !important;
|
||||
}
|
||||
@ -628,19 +643,19 @@ onMounted(() => {
|
||||
.title {
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
|
||||
.header-section {
|
||||
padding: 12px 0;
|
||||
}
|
||||
|
||||
|
||||
.actions-group {
|
||||
justify-content: space-around;
|
||||
}
|
||||
|
||||
|
||||
.actions-group .n-input {
|
||||
width: 120px !important;
|
||||
}
|
||||
|
||||
|
||||
.actions-group .n-select {
|
||||
width: 80px !important;
|
||||
}
|
||||
|
@ -6,12 +6,12 @@
|
||||
<n-icon><ChevronBackOutline /></n-icon>
|
||||
题库管理
|
||||
</n-breadcrumb-item>
|
||||
<n-breadcrumb-item>{{ currentBankName }}</n-breadcrumb-item>
|
||||
<n-breadcrumb-item>{{ currentBankTitle }}</n-breadcrumb-item>
|
||||
</n-breadcrumb>
|
||||
</div>
|
||||
|
||||
<div class="header-section">
|
||||
<h1 class="title">{{ currentBankName }} - 试题管理</h1>
|
||||
<h1 class="title">{{ currentBankTitle }}</h1>
|
||||
<n-space class="actions-group">
|
||||
<n-button type="primary" @click="addQuestion">添加试题</n-button>
|
||||
<n-button ghost @click="importQuestions">导入</n-button>
|
||||
@ -190,6 +190,8 @@ const route = useRoute();
|
||||
|
||||
// 当前题库信息
|
||||
const currentBankId = computed(() => route.params.bankId as string);
|
||||
const currentBankTitle = computed(() => route.query.title as string);
|
||||
|
||||
const currentBankName = ref('加载中...');
|
||||
|
||||
// 返回题库管理页面
|
||||
|
Loading…
x
Reference in New Issue
Block a user