feat: 更新侧边栏和二级菜单逻辑,新增题库选择功能
This commit is contained in:
parent
2d8339ed4e
commit
59faaa25cb
@ -278,7 +278,7 @@ const confirmBatchSet = () => {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
gap: 12px;
|
||||
margin-top: 12px;
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
/* 滚动条样式 */
|
||||
|
@ -1,6 +1,10 @@
|
||||
<template>
|
||||
<n-modal v-model:show="showModal" class="exam-settings-modal" preset="dialog" title="试卷设置" :mask-closable="false"
|
||||
:closable="true" :style="{ width: '1000px' }">
|
||||
<n-modal v-model:show="showModal" class="exam-settings-modal" preset="card" :mask-closable="false"
|
||||
:closable="false" :style="{ width: '1000px' }">
|
||||
<div class="header">
|
||||
<span class="header-title">试卷设置</span>
|
||||
</div>
|
||||
<n-divider />
|
||||
|
||||
<div class="exam-settings-content">
|
||||
<!-- 试卷名称 -->
|
||||
@ -258,12 +262,10 @@
|
||||
</div>
|
||||
|
||||
<!-- 底部按钮 -->
|
||||
<template #action>
|
||||
<div class="modal-actions">
|
||||
<n-button @click="cancelSettings">取消</n-button>
|
||||
<n-button type="primary" @click="confirmSettings">确定</n-button>
|
||||
</div>
|
||||
</template>
|
||||
<div class="modal-actions">
|
||||
<n-button @click="cancelSettings">取消</n-button>
|
||||
<n-button type="primary" @click="confirmSettings">确定</n-button>
|
||||
</div>
|
||||
</n-modal>
|
||||
</template>
|
||||
|
||||
@ -454,6 +456,12 @@ const confirmSettings = () => {
|
||||
--n-color: #ffffff;
|
||||
}
|
||||
|
||||
.header-title{
|
||||
color: #000;
|
||||
font-weight: 400;
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
.exam-settings-content {
|
||||
max-height: 800px;
|
||||
overflow-y: auto;
|
||||
@ -713,6 +721,7 @@ const confirmSettings = () => {
|
||||
}
|
||||
|
||||
.modal-actions {
|
||||
margin-top: 20px;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
gap: 12px;
|
||||
|
459
src/components/admin/ExamComponents/QuestionBankModal.vue
Normal file
459
src/components/admin/ExamComponents/QuestionBankModal.vue
Normal file
@ -0,0 +1,459 @@
|
||||
<template>
|
||||
<n-modal
|
||||
v-model:show="showModal"
|
||||
class="question-bank-modal"
|
||||
preset="card"
|
||||
:mask-closable="false"
|
||||
:closable="false"
|
||||
:style="{ width: '1200px' }">
|
||||
|
||||
<div class="header">
|
||||
<span class="header-title">题库</span>
|
||||
</div>
|
||||
<n-divider />
|
||||
|
||||
<div class="question-bank-content">
|
||||
<!-- 筛选条件 -->
|
||||
<div class="filter-section">
|
||||
<div class="filter-row">
|
||||
<div class="filter-item">
|
||||
<label>试题分类:</label>
|
||||
<n-select
|
||||
v-model:value="filters.category"
|
||||
placeholder="全部"
|
||||
:options="categoryOptions"
|
||||
style="width: 150px"
|
||||
/>
|
||||
</div>
|
||||
<div class="filter-item">
|
||||
<label>试题难度:</label>
|
||||
<n-select
|
||||
v-model:value="filters.difficulty"
|
||||
placeholder="全部"
|
||||
:options="difficultyOptions"
|
||||
style="width: 150px"
|
||||
/>
|
||||
</div>
|
||||
<div class="filter-item">
|
||||
<label>试题题型:</label>
|
||||
<n-select
|
||||
v-model:value="filters.type"
|
||||
placeholder="全部"
|
||||
:options="typeOptions"
|
||||
style="width: 150px"
|
||||
/>
|
||||
</div>
|
||||
<div class="filter-actions">
|
||||
<span class="tip">已全部加载,共{{ pagination.itemCount }}试题</span>
|
||||
<n-button type="primary" @click="addNewQuestion">
|
||||
<template #icon>
|
||||
<n-icon>
|
||||
<Add />
|
||||
</n-icon>
|
||||
</template>
|
||||
导入试题
|
||||
</n-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 题目列表 -->
|
||||
<div class="question-list-section">
|
||||
<n-data-table
|
||||
ref="tableRef"
|
||||
:columns="columns"
|
||||
:data="questionList"
|
||||
:pagination="pagination"
|
||||
:loading="loading"
|
||||
:row-key="(row: any) => row.id"
|
||||
:checked-row-keys="selectedRowKeys"
|
||||
@update:checked-row-keys="handleCheck"
|
||||
striped
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- 已选择题目统计 -->
|
||||
<div class="selected-info">
|
||||
<span>已选择{{ selectedRowKeys.length }}道题目</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 底部按钮 -->
|
||||
<div class="modal-actions">
|
||||
<n-button @click="cancelSelection">取消</n-button>
|
||||
<n-button type="primary" @click="confirmSelection" :disabled="selectedRowKeys.length === 0">
|
||||
确定
|
||||
</n-button>
|
||||
</div>
|
||||
</n-modal>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, watch, onMounted } from 'vue';
|
||||
import { createDiscreteApi } from 'naive-ui';
|
||||
import { Add } from '@vicons/ionicons5';
|
||||
|
||||
// 创建独立的 message API
|
||||
const { message } = createDiscreteApi(['message']);
|
||||
|
||||
// 题目接口定义
|
||||
interface QuestionItem {
|
||||
id: string;
|
||||
number: number;
|
||||
title: string;
|
||||
type: string;
|
||||
difficulty: string;
|
||||
category: string;
|
||||
score: number;
|
||||
creator: string;
|
||||
createTime: string;
|
||||
// 题目详细内容
|
||||
content?: any;
|
||||
}
|
||||
|
||||
// Props 定义
|
||||
interface Props {
|
||||
visible: boolean;
|
||||
questionType?: string; // 指定要选择的题型
|
||||
}
|
||||
|
||||
// Emits 定义
|
||||
interface Emits {
|
||||
(e: 'update:visible', value: boolean): void;
|
||||
(e: 'confirm', questions: QuestionItem[]): void;
|
||||
(e: 'cancel'): void;
|
||||
}
|
||||
|
||||
const props = defineProps<Props>();
|
||||
const emit = defineEmits<Emits>();
|
||||
|
||||
// 内部状态
|
||||
const showModal = computed({
|
||||
get: () => props.visible,
|
||||
set: (value) => emit('update:visible', value)
|
||||
});
|
||||
|
||||
// 筛选条件
|
||||
const filters = ref({
|
||||
category: '',
|
||||
difficulty: '',
|
||||
type: props.questionType || '',
|
||||
keyword: ''
|
||||
});
|
||||
|
||||
// 筛选选项
|
||||
const categoryOptions = ref([
|
||||
{ label: '全部', value: '' },
|
||||
{ label: '计算机基础', value: 'computer' },
|
||||
{ label: '数学', value: 'math' },
|
||||
{ label: '英语', value: 'english' }
|
||||
]);
|
||||
|
||||
const difficultyOptions = ref([
|
||||
{ label: '全部', value: '' },
|
||||
{ label: '易', value: 'easy' },
|
||||
{ label: '中', value: 'medium' },
|
||||
{ label: '难', value: 'hard' }
|
||||
]);
|
||||
|
||||
const typeOptions = ref([
|
||||
{ label: '全部', value: '' },
|
||||
{ label: '单选题', value: 'single_choice' },
|
||||
{ label: '多选题', value: 'multiple_choice' },
|
||||
{ label: '判断题', value: 'true_false' },
|
||||
{ label: '填空题', value: 'fill_blank' },
|
||||
{ label: '简答题', value: 'short_answer' }
|
||||
]);
|
||||
|
||||
// 表格配置
|
||||
const loading = ref(false);
|
||||
const selectedRowKeys = ref<string[]>([]);
|
||||
|
||||
// 题目列表数据 (模拟数据)
|
||||
const questionList = ref<QuestionItem[]>([]);
|
||||
|
||||
// 生成模拟数据
|
||||
const generateMockData = () => {
|
||||
const mockData: QuestionItem[] = [];
|
||||
const types = ['单选题', '多选题', '判断题', '填空题', '简答题'];
|
||||
const difficulties = ['易', '中', '难'];
|
||||
const categories = ['计算机基础', '数学', '英语'];
|
||||
|
||||
for (let i = 1; i <= 15; i++) {
|
||||
mockData.push({
|
||||
id: `question_${i}`,
|
||||
number: i,
|
||||
title: `在数据库的三级模式结构中,内模式有...`,
|
||||
type: types[Math.floor(Math.random() * types.length)],
|
||||
difficulty: difficulties[Math.floor(Math.random() * difficulties.length)],
|
||||
category: categories[Math.floor(Math.random() * categories.length)],
|
||||
score: 10,
|
||||
creator: '王建国',
|
||||
createTime: '2025.08.20 09:20'
|
||||
});
|
||||
}
|
||||
return mockData;
|
||||
};
|
||||
|
||||
// 表格列配置
|
||||
const columns = [
|
||||
{
|
||||
type: 'selection'
|
||||
},
|
||||
{
|
||||
title: '序号',
|
||||
key: 'number',
|
||||
width: 80,
|
||||
align: 'center' as const
|
||||
},
|
||||
{
|
||||
title: '试题内容',
|
||||
key: 'title',
|
||||
width: 300,
|
||||
ellipsis: {
|
||||
tooltip: true
|
||||
}
|
||||
},
|
||||
{
|
||||
title: '题型',
|
||||
key: 'type',
|
||||
width: 100,
|
||||
align: 'center' as const
|
||||
},
|
||||
{
|
||||
title: '分数',
|
||||
key: 'score',
|
||||
width: 80,
|
||||
align: 'center' as const
|
||||
},
|
||||
{
|
||||
title: '难度',
|
||||
key: 'difficulty',
|
||||
width: 80,
|
||||
align: 'center' as const
|
||||
},
|
||||
{
|
||||
title: '分值',
|
||||
key: 'score',
|
||||
width: 80,
|
||||
align: 'center' as const
|
||||
},
|
||||
{
|
||||
title: '创建人',
|
||||
key: 'creator',
|
||||
width: 100,
|
||||
align: 'center' as const
|
||||
},
|
||||
{
|
||||
title: '创建时间',
|
||||
key: 'createTime',
|
||||
width: 150,
|
||||
align: 'center' as const
|
||||
}
|
||||
];
|
||||
|
||||
// 分页配置
|
||||
const pagination = ref({
|
||||
page: 1,
|
||||
pageSize: 10,
|
||||
showSizePicker: true,
|
||||
pageSizes: [10, 20, 50],
|
||||
itemCount: 0,
|
||||
onChange: (page: number) => {
|
||||
pagination.value.page = page;
|
||||
loadQuestions();
|
||||
},
|
||||
onUpdatePageSize: (pageSize: number) => {
|
||||
pagination.value.pageSize = pageSize;
|
||||
pagination.value.page = 1;
|
||||
loadQuestions();
|
||||
},
|
||||
prefix: ({ itemCount }: { itemCount: number }) => `共${itemCount}试题`
|
||||
});
|
||||
|
||||
// 处理复选框选择
|
||||
const handleCheck = (rowKeys: string[]) => {
|
||||
selectedRowKeys.value = rowKeys;
|
||||
};
|
||||
|
||||
// 加载题目列表
|
||||
const loadQuestions = async () => {
|
||||
loading.value = true;
|
||||
try {
|
||||
// 模拟API调用
|
||||
await new Promise(resolve => setTimeout(resolve, 500));
|
||||
questionList.value = generateMockData();
|
||||
pagination.value.itemCount = questionList.value.length;
|
||||
} catch (error) {
|
||||
message.error('加载题目失败');
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
// 添加新题目
|
||||
const addNewQuestion = () => {
|
||||
message.info('导入试题功能待开发');
|
||||
};
|
||||
|
||||
// 取消选择
|
||||
const cancelSelection = () => {
|
||||
selectedRowKeys.value = [];
|
||||
emit('cancel');
|
||||
};
|
||||
|
||||
// 确认选择
|
||||
const confirmSelection = () => {
|
||||
const selectedQuestions = questionList.value.filter(q => selectedRowKeys.value.includes(q.id));
|
||||
emit('confirm', selectedQuestions);
|
||||
selectedRowKeys.value = [];
|
||||
};
|
||||
|
||||
// 监听visible变化,重置状态
|
||||
watch(() => props.visible, (visible) => {
|
||||
if (visible) {
|
||||
loadQuestions();
|
||||
selectedRowKeys.value = [];
|
||||
// 如果指定了题型,设置筛选条件
|
||||
if (props.questionType) {
|
||||
filters.value.type = props.questionType;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// 组件挂载时加载数据
|
||||
onMounted(() => {
|
||||
if (props.visible) {
|
||||
loadQuestions();
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.question-bank-modal {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.header-title{
|
||||
color: #000;
|
||||
font-weight: 400;
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
.question-bank-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 16px;
|
||||
max-height: 60vh;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.filter-section {
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
padding-bottom: 16px;
|
||||
}
|
||||
|
||||
.filter-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 16px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.filter-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.filter-item label {
|
||||
font-size: 14px;
|
||||
color: #333;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.filter-actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
.tip{
|
||||
font-size: 12px;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.question-list-section {
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.selected-info {
|
||||
padding: 12px 0;
|
||||
border-top: 1px solid #f0f0f0;
|
||||
color: #666;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.modal-actions {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
gap: 12px;
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
/* 表格样式优化 */
|
||||
:deep(.n-data-table-th) {
|
||||
background-color: #fafafa;
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
:deep(.n-data-table-td) {
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
padding: 12px 16px;
|
||||
}
|
||||
|
||||
:deep(.n-data-table-tr:hover .n-data-table-td) {
|
||||
background-color: #f8f9fa;
|
||||
}
|
||||
|
||||
:deep(.n-data-table-tbody .n-data-table-tr--checked .n-data-table-td) {
|
||||
background-color: #e6f7ff;
|
||||
}
|
||||
|
||||
:deep(.n-data-table .n-data-table-th) {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
:deep(.n-data-table .n-data-table-td) {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
/* 筛选区域样式 */
|
||||
.filter-section {
|
||||
background-color: #fafafa;
|
||||
border-radius: 6px;
|
||||
padding: 16px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
/* 响应式设计 */
|
||||
@media (max-width: 1024px) {
|
||||
.question-bank-content {
|
||||
max-height: 50vh;
|
||||
}
|
||||
|
||||
.filter-row {
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.filter-actions {
|
||||
margin-left: 0;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
</style>
|
@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<div class="course-editor">
|
||||
<!-- 左侧导航菜单 -->
|
||||
<div class="sidebar">
|
||||
<div class="sidebar" v-if="showSidebar">
|
||||
<router-link :to="`/teacher/course-editor/${courseId}/courseware`" class="menu-item"
|
||||
:class="{ active: $route.path.includes('courseware') }">
|
||||
<img :src="$route.path.includes('courseware') ? '/images/teacher/课件-选中.png' : '/images/teacher/课件.png'"
|
||||
@ -16,11 +16,11 @@
|
||||
</router-link>
|
||||
<!-- 作业二级导航 -->
|
||||
<div class="menu-group">
|
||||
<div class="menu-header" @click="toggleHomework">
|
||||
<div class="menu-header" @click="toggleHomework('homework')">
|
||||
<img :src="$route.path.includes('homework') ? '/images/teacher/作业-选中.png' : '/images/teacher/作业.png'"
|
||||
alt="作业" />
|
||||
<span>作业</span>
|
||||
<i class="n-base-icon" :class="{ 'expanded': homeworkExpanded }">
|
||||
<i class="n-base-icon" :class="{ 'expanded': subMenuArr.homework }">
|
||||
<svg viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
d="M5.64645 3.14645C5.45118 3.34171 5.45118 3.65829 5.64645 3.85355L9.79289 8L5.64645 12.1464C5.45118 12.3417 5.45118 12.6583 5.64645 12.8536C5.84171 13.0488 6.15829 13.0488 6.35355 12.8536L10.8536 8.35355C11.0488 8.15829 11.0488 7.84171 10.8536 7.64645L6.35355 3.14645C6.15829 2.95118 5.84171 2.95118 5.64645 3.14645Z"
|
||||
@ -28,7 +28,7 @@
|
||||
</svg>
|
||||
</i>
|
||||
</div>
|
||||
<div class="submenu" v-show="homeworkExpanded">
|
||||
<div class="submenu" v-show="subMenuArr.homework">
|
||||
<router-link :to="`/teacher/course-editor/${courseId}/homework/library`" class="submenu-item"
|
||||
:class="{ active: $route.path.includes('homework/library') }">
|
||||
<span>作业库</span>
|
||||
@ -39,12 +39,32 @@
|
||||
</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>
|
||||
<!-- 练考通父菜单 -->
|
||||
<div class="menu-group">
|
||||
<div class="menu-header" @click="toggleHomework('practice')">
|
||||
<img :src="$route.path.includes('practice') ? '/images/teacher/练考通-选中.png' : '/images/teacher/练考通.png'"
|
||||
alt="练考通" />
|
||||
<span>练考通</span>
|
||||
<i class="n-base-icon" :class="{ 'expanded': subMenuArr.practice }">
|
||||
<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="subMenuArr.practice">
|
||||
<router-link :to="`/teacher/course-editor/${courseId}/practice/exam-library`" class="submenu-item"
|
||||
:class="{ active: $route.path.includes('exam-library') }">
|
||||
<span>试卷库</span>
|
||||
</router-link>
|
||||
<router-link :to="`/teacher/course-editor/${courseId}/practice/marking-center`" class="submenu-item"
|
||||
:class="{ active: $route.path.includes('marking-center') }">
|
||||
<span>阅卷中心</span>
|
||||
</router-link>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<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'"
|
||||
@ -92,20 +112,38 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useRoute } from 'vue-router'
|
||||
import { ref } from 'vue'
|
||||
import { computed, ref, watch } from 'vue'
|
||||
|
||||
const route = useRoute()
|
||||
|
||||
// 获取课程ID
|
||||
const courseId = route.params.id
|
||||
|
||||
// 作业菜单展开状态
|
||||
const homeworkExpanded = ref(false)
|
||||
// 二级菜单展开状态
|
||||
const subMenuArr = ref({
|
||||
homework: false,
|
||||
practice: false
|
||||
})
|
||||
|
||||
// 切换作业菜单展开/收起
|
||||
const toggleHomework = () => {
|
||||
homeworkExpanded.value = !homeworkExpanded.value
|
||||
const toggleHomework = (e: 'homework' | 'practice') => {
|
||||
subMenuArr.value[e] = !subMenuArr.value[e]
|
||||
}
|
||||
|
||||
// 监听路由变化,如果当前路由是子菜单,自动展开父菜单
|
||||
watch(() => route.path, (newPath) => {
|
||||
if (newPath.includes('practice')) {
|
||||
subMenuArr.value.practice = true
|
||||
}else if (newPath.includes('homework')) {
|
||||
subMenuArr.value.homework = true
|
||||
}
|
||||
}, { immediate: true })
|
||||
|
||||
|
||||
// 判断是否显示侧边菜单栏
|
||||
const showSidebar = computed(() => {
|
||||
return route.meta.hideSidebar !== true
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
@ -258,6 +296,9 @@ const toggleHomework = () => {
|
||||
font-size: 16px;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* 右侧内容区域 */
|
||||
.content-area {
|
||||
flex: 1;
|
||||
|
@ -364,13 +364,13 @@
|
||||
</n-button>
|
||||
</n-popselect>
|
||||
<div class="mr-10"></div>
|
||||
<n-button type="primary" ghost>
|
||||
<n-button type="primary" ghost @click="openQuestionBankModal(index)">
|
||||
<template #icon>
|
||||
<n-icon>
|
||||
<BookSharp />
|
||||
</n-icon>
|
||||
</template>
|
||||
题库题库选择
|
||||
题库选择题目
|
||||
</n-button>
|
||||
</n-row>
|
||||
</n-card>
|
||||
@ -400,6 +400,11 @@
|
||||
{{ examForm.useAIGrading ? '已启用AI阅卷' : '使用AI阅卷功能' }}
|
||||
</n-button>
|
||||
<n-button type="primary" ghost size="large" @click="openExamSettingsModal">
|
||||
<template #icon>
|
||||
<n-icon>
|
||||
<SettingsOutline />
|
||||
</n-icon>
|
||||
</template>
|
||||
试卷设置
|
||||
</n-button>
|
||||
<n-button type="primary" ghost size="large">
|
||||
@ -420,7 +425,7 @@
|
||||
<n-button strong type="primary" secondary size="large">
|
||||
取消
|
||||
</n-button>
|
||||
<n-button strong type="primary" secondary size="large">
|
||||
<n-button strong type="primary" secondary size="large" @click="previewSubQuestion">
|
||||
预览
|
||||
</n-button>
|
||||
<n-button strong type="primary" size="large" @click="saveExam">
|
||||
@ -447,6 +452,13 @@
|
||||
@confirm="handleExamSettingsConfirm"
|
||||
@cancel="handleExamSettingsCancel"
|
||||
/>
|
||||
|
||||
<!-- 题库选择模态框 -->
|
||||
<QuestionBankModal
|
||||
v-model:visible="showQuestionBankModal"
|
||||
@confirm="handleQuestionBankConfirm"
|
||||
@cancel="handleQuestionBankCancel"
|
||||
/>
|
||||
</div>
|
||||
</n-config-provider>
|
||||
</template>
|
||||
@ -458,6 +470,7 @@ import type { GlobalThemeOverrides } from 'naive-ui';
|
||||
import { AddCircle, SettingsOutline, TrashOutline, ChevronUpSharp, BookSharp } from '@vicons/ionicons5'
|
||||
import BatchSetScoreModal from '@/components/admin/ExamComponents/BatchSetScoreModal.vue';
|
||||
import ExamSettingsModal from '@/components/admin/ExamComponents/ExamSettingsModal.vue';
|
||||
import QuestionBankModal from '@/components/admin/ExamComponents/QuestionBankModal.vue';
|
||||
|
||||
// 自定义主题颜色
|
||||
const themeOverrides: GlobalThemeOverrides = {
|
||||
@ -955,6 +968,10 @@ const handleBatchScoreCancel = () => {
|
||||
// 试卷设置相关状态和方法
|
||||
const showExamSettingsModal = ref(false);
|
||||
|
||||
// 题库选择相关状态和方法
|
||||
const showQuestionBankModal = ref(false);
|
||||
const currentBigQuestionIndex = ref(0);
|
||||
|
||||
// 试卷设置数据
|
||||
const examSettingsData = computed(() => ({
|
||||
title: examForm.title,
|
||||
@ -1020,7 +1037,7 @@ const handleExamSettingsConfirm = (settings: any) => {
|
||||
examForm.instructions = settings.instructions;
|
||||
examForm.duration = settings.timerDuration || examForm.duration;
|
||||
|
||||
// 可以根据需要更新更多字段
|
||||
// TODO 可以根据需要更新更多字段
|
||||
console.log('试卷设置数据:', settings);
|
||||
};
|
||||
|
||||
@ -1029,6 +1046,87 @@ const handleExamSettingsCancel = () => {
|
||||
// 取消操作,不需要特殊处理
|
||||
};
|
||||
|
||||
// 题库选择相关方法
|
||||
const openQuestionBankModal = (bigQuestionIndex: number) => {
|
||||
currentBigQuestionIndex.value = bigQuestionIndex;
|
||||
showQuestionBankModal.value = true;
|
||||
};
|
||||
|
||||
// 处理题库选择确认
|
||||
const handleQuestionBankConfirm = (selectedQuestions: any[]) => {
|
||||
const bigQuestionIndex = currentBigQuestionIndex.value;
|
||||
|
||||
// 将选择的题目转换为当前系统的题目格式并添加到对应大题
|
||||
selectedQuestions.forEach(question => {
|
||||
const questionType = getQuestionTypeFromString(question.type);
|
||||
const newSubQuestion: SubQuestion = {
|
||||
id: `${Date.now()}_${Math.random().toString(36).substr(2, 9)}`,
|
||||
type: questionType as QuestionType,
|
||||
title: question.title,
|
||||
score: question.score,
|
||||
difficulty: question.difficulty,
|
||||
required: true,
|
||||
createTime: new Date().toISOString()
|
||||
};
|
||||
|
||||
// 根据题型初始化不同的字段
|
||||
if (questionType === 'single_choice') {
|
||||
newSubQuestion.options = [
|
||||
{ id: '1', content: '选项A', isCorrect: false },
|
||||
{ id: '2', content: '选项B', isCorrect: false },
|
||||
{ id: '3', content: '选项C', isCorrect: false },
|
||||
{ id: '4', content: '选项D', isCorrect: false }
|
||||
];
|
||||
newSubQuestion.correctAnswer = '';
|
||||
} else if (questionType === 'multiple_choice') {
|
||||
newSubQuestion.options = [
|
||||
{ id: '1', content: '选项A', isCorrect: false },
|
||||
{ id: '2', content: '选项B', isCorrect: false },
|
||||
{ id: '3', content: '选项C', isCorrect: false },
|
||||
{ id: '4', content: '选项D', isCorrect: false }
|
||||
];
|
||||
newSubQuestion.correctAnswer = [];
|
||||
} else if (questionType === 'true_false') {
|
||||
newSubQuestion.trueFalseAnswer = undefined;
|
||||
} else if (questionType === 'fill_blank') {
|
||||
newSubQuestion.fillBlanks = [
|
||||
{ id: '1', content: '', position: 1 }
|
||||
];
|
||||
} else if (questionType === 'short_answer') {
|
||||
newSubQuestion.textAnswer = '';
|
||||
}
|
||||
|
||||
examForm.questions[bigQuestionIndex].subQuestions.push(newSubQuestion);
|
||||
});
|
||||
|
||||
// 重新计算总分
|
||||
updateBigQuestionScore(bigQuestionIndex);
|
||||
|
||||
dialog.success({
|
||||
title: '成功',
|
||||
content: `成功导入${selectedQuestions.length}道题目`,
|
||||
positiveText: '确定'
|
||||
});
|
||||
showQuestionBankModal.value = false;
|
||||
};
|
||||
|
||||
// 处理题库选择取消
|
||||
const handleQuestionBankCancel = () => {
|
||||
showQuestionBankModal.value = false;
|
||||
};
|
||||
|
||||
// 辅助函数:将字符串题型转换为系统题型
|
||||
const getQuestionTypeFromString = (typeString: string) => {
|
||||
const typeMap: { [key: string]: string } = {
|
||||
'单选题': 'single_choice',
|
||||
'多选题': 'multiple_choice',
|
||||
'判断题': 'true_false',
|
||||
'填空题': 'fill_blank',
|
||||
'简答题': 'short_answer'
|
||||
};
|
||||
return typeMap[typeString] || 'single_choice';
|
||||
};
|
||||
|
||||
// 保存试卷
|
||||
const saveExam = () => {
|
||||
// 验证数据
|
||||
@ -1293,11 +1391,11 @@ const changeCompositeSubQuestionType = (bigQuestionIndex: number, subQuestionInd
|
||||
// }
|
||||
|
||||
// 预览题目
|
||||
// const previewSubQuestion = (bigQuestionIndex: number, subQuestionIndex: number) => {
|
||||
// const subQuestion = examForm.questions[bigQuestionIndex].subQuestions[subQuestionIndex];
|
||||
// console.log('预览题目:', subQuestion);
|
||||
// // 这里可以实现题目预览功能,比如打开一个模态框显示题目
|
||||
// }
|
||||
const previewSubQuestion = (bigQuestionIndex: number, subQuestionIndex: number) => {
|
||||
const subQuestion = examForm.questions[bigQuestionIndex].subQuestions[subQuestionIndex];
|
||||
console.log('预览题目:', subQuestion);
|
||||
// 这里可以实现题目预览功能,比如打开一个模态框显示题目
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
@ -1,9 +1,6 @@
|
||||
<template>
|
||||
<div class="practice-management">
|
||||
<div class="content-placeholder">
|
||||
<h2>练考通管理</h2>
|
||||
<p>练考通管理功能正在开发中...</p>
|
||||
</div>
|
||||
<router-view></router-view>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user