优化题库代码
This commit is contained in:
parent
ff9a181c99
commit
5fddba8a30
1
.gitignore
vendored
1
.gitignore
vendored
@ -24,6 +24,7 @@ dist
|
|||||||
dist-ssr
|
dist-ssr
|
||||||
*.local
|
*.local
|
||||||
server/uploads
|
server/uploads
|
||||||
|
server/conf/
|
||||||
|
|
||||||
# Editor directories and files
|
# Editor directories and files
|
||||||
.vscode/*
|
.vscode/*
|
||||||
|
|||||||
@ -0,0 +1,3 @@
|
|||||||
|
<template>
|
||||||
|
<div class="p-4">Certificate</div>
|
||||||
|
</template>
|
||||||
@ -0,0 +1,3 @@
|
|||||||
|
<template>
|
||||||
|
<div class="p-4">Create Exam Placeholder</div>
|
||||||
|
</template>
|
||||||
@ -124,7 +124,8 @@ import '@wangeditor/editor/dist/css/style.css'
|
|||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
visible: { type: Boolean, default: false },
|
visible: { type: Boolean, default: false },
|
||||||
mode: { type: String, default: 'create' },
|
mode: { type: String, default: 'create' },
|
||||||
model: { type: Object, default: null }
|
model: { type: Object, default: null },
|
||||||
|
bankId: { type: [Number, String], default: null }
|
||||||
})
|
})
|
||||||
|
|
||||||
const emit = defineEmits(['close', 'saved'])
|
const emit = defineEmits(['close', 'saved'])
|
||||||
@ -380,6 +381,7 @@ function handleSubmit() {
|
|||||||
options: showOptions.value ? form.options.map(o => ({ key: o.key, label: o.label, content: o.content })) : [],
|
options: showOptions.value ? form.options.map(o => ({ key: o.key, label: o.label, content: o.content })) : [],
|
||||||
answer: isMultipleType.value ? [...multiAnswer.value] : form.answer
|
answer: isMultipleType.value ? [...multiAnswer.value] : form.answer
|
||||||
}
|
}
|
||||||
|
if (props.bankId) payload.bank_id = props.bankId
|
||||||
emit('saved', payload)
|
emit('saved', payload)
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
ElMessage.error('保存失败')
|
ElMessage.error('保存失败')
|
||||||
|
|||||||
@ -4,7 +4,7 @@
|
|||||||
<!-- 顶部工具栏:返回 + 标题 -->
|
<!-- 顶部工具栏:返回 + 标题 -->
|
||||||
<div class="topbar">
|
<div class="topbar">
|
||||||
<el-button :icon="Back" @click="emit('back')">返回</el-button>
|
<el-button :icon="Back" @click="emit('back')">返回</el-button>
|
||||||
<div class="title">题目列表</div>
|
<div class="title">{{ bankName }}</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<el-divider />
|
<el-divider />
|
||||||
@ -16,36 +16,38 @@
|
|||||||
<el-input v-model="filters.keyword" placeholder="请输入题干关键词" clearable
|
<el-input v-model="filters.keyword" placeholder="请输入题干关键词" clearable
|
||||||
@keyup.enter="handleSearch" />
|
@keyup.enter="handleSearch" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="题型">
|
<el-divider />
|
||||||
<el-select v-model="filters.type" placeholder="请选择题型" clearable style="width: 200px;">
|
<div class="tools">
|
||||||
<el-option v-for="opt in typeOptions" :key="opt.dict_value" :label="opt.dict_label"
|
<el-form-item>
|
||||||
:value="opt.dict_value" />
|
<el-button type="primary" :icon="Plus" @click="handleCreate">新建题目</el-button>
|
||||||
</el-select>
|
<el-button type="primary" :icon="Plus" @click="handleSmartCreate">智能新建</el-button>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item>
|
<el-form-item>
|
||||||
<el-button type="primary" :icon="Search" @click="handleSearch">搜索</el-button>
|
<el-button type="primary" :icon="Search" @click="handleSearch">搜索</el-button>
|
||||||
<el-button @click="handleReset">重置</el-button>
|
<el-button @click="handleReset">重置</el-button>
|
||||||
</el-form-item>
|
<el-button :icon="Refresh" @click="fetchList">刷新</el-button>
|
||||||
|
<el-button :icon="Upload" @click="handleBatchImport">批量导入</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
</div>
|
||||||
</el-form>
|
</el-form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="header">
|
<!-- <div class="header">
|
||||||
<!-- 操作栏 -->
|
|
||||||
<div class="toolbar">
|
<div class="toolbar">
|
||||||
<el-button type="primary" :icon="Plus" @click="handleCreate">新建题目</el-button>
|
|
||||||
<el-button type="primary" :icon="Plus" @click="handleSmartCreate">智能新建</el-button>
|
|
||||||
<div style="flex:1" />
|
<div style="flex:1" />
|
||||||
<el-button :icon="Refresh" @click="fetchList">刷新</el-button>
|
|
||||||
<el-button :icon="Upload" @click="handleBatchImport">批量导入</el-button>
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div> -->
|
||||||
|
|
||||||
<!-- 列表 -->
|
<!-- 列表 -->
|
||||||
<el-table v-loading="loading" :data="list" stripe border>
|
<el-table v-loading="loading" :data="list" stripe border>
|
||||||
<el-table-column prop="id" label="ID" width="80" align="center" />
|
<el-table-column prop="id" label="ID" width="80" align="center" />
|
||||||
<el-table-column prop="question_title" label="题目" min-width="260" show-overflow-tooltip />
|
<el-table-column prop="question_title" label="题目" min-width="260" show-overflow-tooltip>
|
||||||
|
<template #default="{ row }">
|
||||||
|
<div v-html="row.question_title"></div>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
<el-table-column prop="question_type" label="题型" width="120" align="center">
|
<el-table-column prop="question_type" label="题型" width="120" align="center">
|
||||||
<template #default="{ row }">
|
<template #default="{ row }">
|
||||||
<el-tag>{{ getTypeLabel(row.question_type) }}</el-tag>
|
<el-tag>{{ getTypeLabel(row.question_type) }}</el-tag>
|
||||||
@ -72,7 +74,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 弹窗:仅用于创建/编辑题目与查看详情 -->
|
<!-- 弹窗:仅用于创建/编辑题目与查看详情 -->
|
||||||
<EditDialog :visible="editVisible" :mode="editMode" :model="editModel" @close="editVisible = false"
|
<EditDialog :visible="editVisible" :mode="editMode" :model="editModel" :bank-id="props.bankId" @close="editVisible = false"
|
||||||
@saved="handleSaved" />
|
@saved="handleSaved" />
|
||||||
<DetailDialog :visible="viewVisible" :model="viewModel" @close="viewVisible = false" />
|
<DetailDialog :visible="viewVisible" :model="viewModel" @close="viewVisible = false" />
|
||||||
|
|
||||||
@ -86,15 +88,11 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<el-divider content-position="left">上传 Excel 文件</el-divider>
|
<el-divider content-position="left">上传 Excel 文件</el-divider>
|
||||||
<el-upload
|
<el-upload class="upload-block" :show-file-list="false" accept=".xls,.xlsx" :auto-upload="false"
|
||||||
class="upload-block"
|
:on-change="handleImportFileChange">
|
||||||
:show-file-list="false"
|
|
||||||
accept=".xls,.xlsx"
|
|
||||||
:auto-upload="false"
|
|
||||||
:on-change="handleImportFileChange"
|
|
||||||
>
|
|
||||||
<el-button type="primary" :loading="importing">选择 Excel 文件</el-button>
|
<el-button type="primary" :loading="importing">选择 Excel 文件</el-button>
|
||||||
<span style="margin-left: 12px; color:#888; font-size:12px;">表头需包含:question_title, question_type, score, question_analysis, option_a, option_b, ... , answer</span>
|
<span style="margin-left: 12px; color:#888; font-size:12px;">表头需包含:question_title, question_type, score,
|
||||||
|
question_analysis, option_a, option_b, ... , answer</span>
|
||||||
</el-upload>
|
</el-upload>
|
||||||
|
|
||||||
<template #footer>
|
<template #footer>
|
||||||
@ -109,12 +107,8 @@
|
|||||||
<el-form label-width="80px">
|
<el-form label-width="80px">
|
||||||
<el-form-item label="题型">
|
<el-form-item label="题型">
|
||||||
<el-select v-model="smartType" placeholder="请选择题型" style="width: 260px;">
|
<el-select v-model="smartType" placeholder="请选择题型" style="width: 260px;">
|
||||||
<el-option
|
<el-option v-for="opt in typeOptions" :key="opt.dict_value" :label="opt.dict_label"
|
||||||
v-for="opt in typeOptions"
|
:value="String(opt.dict_value)" />
|
||||||
:key="opt.dict_value"
|
|
||||||
:label="opt.dict_label"
|
|
||||||
:value="String(opt.dict_value)"
|
|
||||||
/>
|
|
||||||
</el-select>
|
</el-select>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-form>
|
</el-form>
|
||||||
@ -122,13 +116,8 @@
|
|||||||
<p style="margin: 4px 0 8px; font-size: 13px; color: #666;">
|
<p style="margin: 4px 0 8px; font-size: 13px; color: #666;">
|
||||||
请粘贴完整的题目文本,包含题干、选项、答案和解析,例如:
|
请粘贴完整的题目文本,包含题干、选项、答案和解析,例如:
|
||||||
</p>
|
</p>
|
||||||
<el-input
|
<el-input v-model="smartRawText" type="textarea" :rows="10" placeholder="在这里粘贴题目文本..."
|
||||||
v-model="smartRawText"
|
style="margin-top: 12px;" />
|
||||||
type="textarea"
|
|
||||||
:rows="10"
|
|
||||||
placeholder="在这里粘贴题目文本..."
|
|
||||||
style="margin-top: 12px;"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<template #footer>
|
<template #footer>
|
||||||
<div class="dlg-actions">
|
<div class="dlg-actions">
|
||||||
@ -152,9 +141,6 @@ import { getExamQuestions, getExamQuestionDetail, createExamQuestion, updateExam
|
|||||||
import EditDialog from './edit.vue'
|
import EditDialog from './edit.vue'
|
||||||
import DetailDialog from './detail.vue'
|
import DetailDialog from './detail.vue'
|
||||||
|
|
||||||
const props = defineProps({
|
|
||||||
bankId: { type: [Number, String], default: null },
|
|
||||||
})
|
|
||||||
const emit = defineEmits(['back', 'saved'])
|
const emit = defineEmits(['back', 'saved'])
|
||||||
|
|
||||||
const auth = useAuthStore()
|
const auth = useAuthStore()
|
||||||
@ -173,6 +159,11 @@ const typeDictMap = computed(() => {
|
|||||||
return m
|
return m
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
bankId: { type: [Number, String], default: null },
|
||||||
|
bankName: { type: String, default: '' },
|
||||||
|
})
|
||||||
|
|
||||||
// 模板
|
// 模板
|
||||||
function handleDownloadTemplate() {
|
function handleDownloadTemplate() {
|
||||||
// 英文字段名(程序使用)
|
// 英文字段名(程序使用)
|
||||||
@ -451,7 +442,7 @@ function handleImportFileChange(file) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
importing.value = true
|
importing.value = true
|
||||||
const res = await batchImportExamQuestions({ items })
|
const res = await batchImportExamQuestions({ bank_id: props.bankId || undefined, items })
|
||||||
if (res && res.code === 0) {
|
if (res && res.code === 0) {
|
||||||
const data = res.data || {}
|
const data = res.data || {}
|
||||||
ElMessage.success(`导入完成:成功 ${data.success ?? 0} 条,失败 ${data.failed ?? 0} 条`)
|
ElMessage.success(`导入完成:成功 ${data.success ?? 0} 条,失败 ${data.failed ?? 0} 条`)
|
||||||
@ -668,11 +659,16 @@ onMounted(async () => {
|
|||||||
|
|
||||||
.topbar .title {
|
.topbar .title {
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
font-size: 16px;
|
font-size: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.search-bar {
|
.search-bar {
|
||||||
/* margin-bottom: 12px; */
|
/* margin-bottom: 12px; */
|
||||||
|
|
||||||
|
.tools {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.toolbar {
|
.toolbar {
|
||||||
|
|||||||
@ -1,75 +1,50 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="examination-questions">
|
<div class="examination-questions">
|
||||||
<template v-if="!examListVisible">
|
<template v-if="!examListVisible">
|
||||||
<!-- 搜索栏 -->
|
|
||||||
<div class="search-bar">
|
|
||||||
<el-form :inline="true" :model="filters">
|
|
||||||
<div class="form-fields">
|
|
||||||
<el-form-item label="关键词">
|
|
||||||
<el-input v-model="filters.keyword" placeholder="请输入题目关键词" clearable
|
|
||||||
@keyup.enter="handleSearch" />
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item label="题型">
|
|
||||||
<el-select v-model="filters.type" placeholder="请选择题型" clearable>
|
|
||||||
<el-option v-for="opt in dictTypeOptions" :key="opt.dict_value" :label="opt.dict_label"
|
|
||||||
:value="opt.dict_value" />
|
|
||||||
</el-select>
|
|
||||||
</el-form-item>
|
|
||||||
</div>
|
|
||||||
<el-divider />
|
|
||||||
<div class="form-actions">
|
|
||||||
<el-button type="primary" :icon="Search" @click="handleSearch">搜索</el-button>
|
|
||||||
<el-button @click="handleReset">重置</el-button>
|
|
||||||
</div>
|
|
||||||
</el-form>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- 操作栏 -->
|
<!-- 搜索栏 -->
|
||||||
<div class="toolbar">
|
<div class="search-bar">
|
||||||
<el-button type="primary" :icon="Plus" @click="handleCreate">新建题库</el-button>
|
<el-form :inline="true" :model="filters">
|
||||||
<div class="toolright">
|
<div class="form-actions">
|
||||||
<el-button @click="handleBatchImport" :icon="Upload">批量导入</el-button>
|
<div class="left">
|
||||||
<el-button @click="handleRefresh" :icon="Refresh">刷新</el-button>
|
<el-button type="primary" :icon="Plus" @click="handleCreate">新建题库</el-button>
|
||||||
|
</div>
|
||||||
|
<div class="right">
|
||||||
|
<el-button @click="handleBatchImport" :icon="Upload">批量导入</el-button>
|
||||||
|
<el-button @click="handleRefresh" :icon="Refresh">刷新</el-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</el-form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- 题库列表 -->
|
<!-- 题库列表 -->
|
||||||
<el-table v-loading="loading" :data="bankList" stripe border style="width: 100%">
|
<el-table v-loading="loading" :data="bankList" stripe border style="width: 100%">
|
||||||
<el-table-column prop="id" label="ID" width="80" align="center" />
|
<el-table-column prop="id" label="ID" width="80" align="center" />
|
||||||
<el-table-column prop="bank_name" label="题库名称" min-width="200" show-overflow-tooltip />
|
<el-table-column prop="bank_name" label="题库名称" min-width="200" show-overflow-tooltip />
|
||||||
<el-table-column prop="bank_desc" label="描述" min-width="200" show-overflow-tooltip />
|
<el-table-column prop="bank_desc" label="描述" min-width="200" show-overflow-tooltip />
|
||||||
<!-- <el-table-column prop="status" label="状态" width="100" align="center">
|
<el-table-column label="操作" width="200" fixed="right" align="center">
|
||||||
<template #default="{ row }">
|
<template #default="{ row }">
|
||||||
<el-tag :type="String(row.status)==='0' ? 'success' : 'info'">{{ String(row.status)==='0' ? '启用' : '禁用' }}</el-tag>
|
<el-button link type="primary" size="small" @click="handleEdit(row)">编辑</el-button>
|
||||||
</template>
|
<el-button link type="primary" size="small" @click="handleAdd(row)">题目管理</el-button>
|
||||||
</el-table-column> -->
|
<el-button link type="danger" size="small" @click="handleDelete(row)">删除</el-button>
|
||||||
<!-- <el-table-column prop="create_time" label="创建时间" width="180" align="center">
|
</template>
|
||||||
<template #default="{ row }">
|
</el-table-column>
|
||||||
{{ formatTime(row.create_time) }}
|
</el-table>
|
||||||
</template>
|
|
||||||
</el-table-column> -->
|
|
||||||
<el-table-column label="操作" width="200" fixed="right" align="center">
|
|
||||||
<template #default="{ row }">
|
|
||||||
<el-button link type="primary" size="small" @click="handleEdit(row)">编辑</el-button>
|
|
||||||
<el-button link type="primary" size="small" @click="handleAdd(row)">题目管理</el-button>
|
|
||||||
<el-button link type="danger" size="small" @click="handleDelete(row)">删除</el-button>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
</el-table>
|
|
||||||
|
|
||||||
<!-- 分页 -->
|
<!-- 分页 -->
|
||||||
<div class="pagination">
|
<div class="pagination">
|
||||||
<el-pagination v-model:current-page="currentPage" v-model:page-size="pageSize" :total="total"
|
<el-pagination v-model:current-page="currentPage" v-model:page-size="pageSize" :total="total"
|
||||||
:page-sizes="[10, 20, 50, 100]" layout="total, sizes, prev, pager, next, jumper"
|
:page-sizes="[10, 20, 50, 100]" layout="total, sizes, prev, pager, next, jumper"
|
||||||
@current-change="handlePageChange" @size-change="handleSizeChange" />
|
@current-change="handlePageChange" @size-change="handleSizeChange" />
|
||||||
</div>
|
</div>
|
||||||
<EditDialog :visible="editVisible" :mode="editMode" :model="editModel" @close="editVisible = false"
|
<EditDialog :visible="editVisible" :mode="editMode" :model="editModel" @close="editVisible = false"
|
||||||
@saved="handleSaved" />
|
@saved="handleSaved" />
|
||||||
<EditBanks :visible="bankVisible" :mode="bankMode" :model="bankModel" @close="bankVisible = false" @saved="handleBankSaved" />
|
<EditBanks :visible="bankVisible" :mode="bankMode" :model="bankModel" @close="bankVisible = false"
|
||||||
<DetailDialog :visible="viewVisible" :model="viewModel" @close="viewVisible = false" />
|
@saved="handleBankSaved" />
|
||||||
|
<DetailDialog :visible="viewVisible" :model="viewModel" @close="viewVisible = false" />
|
||||||
</template>
|
</template>
|
||||||
<template v-else>
|
<template v-else>
|
||||||
<ExamList :bank-id="examListBankId" @back="examListVisible = false" />
|
<ExamList :bank-id="examListBankId" :bank-name="examListBankName" @back="examListVisible = false" />
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@ -111,6 +86,7 @@ const bankModel = ref(null);
|
|||||||
|
|
||||||
const examListVisible = ref(false);
|
const examListVisible = ref(false);
|
||||||
const examListBankId = ref(null);
|
const examListBankId = ref(null);
|
||||||
|
const examListBankName = ref('');
|
||||||
|
|
||||||
const auth = useAuthStore();
|
const auth = useAuthStore();
|
||||||
|
|
||||||
@ -156,7 +132,7 @@ const handleBatchImport = () => {
|
|||||||
ElMessage.info('批量导入功能开发中');
|
ElMessage.info('批量导入功能开发中');
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleView = async (row) => {};
|
const handleView = async (row) => { };
|
||||||
|
|
||||||
const handleRefresh = () => {
|
const handleRefresh = () => {
|
||||||
fetchBankList();
|
fetchBankList();
|
||||||
@ -171,6 +147,7 @@ const handleBankSaved = async (payload) => {
|
|||||||
}
|
}
|
||||||
ElMessage.success('保存成功');
|
ElMessage.success('保存成功');
|
||||||
bankVisible.value = false;
|
bankVisible.value = false;
|
||||||
|
await fetchBankList();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
ElMessage.error('保存失败');
|
ElMessage.error('保存失败');
|
||||||
}
|
}
|
||||||
@ -184,6 +161,7 @@ const handleEdit = (row) => {
|
|||||||
|
|
||||||
const handleAdd = (row) => {
|
const handleAdd = (row) => {
|
||||||
examListBankId.value = row?.id || null;
|
examListBankId.value = row?.id || null;
|
||||||
|
examListBankName.value = row?.bank_name || '';
|
||||||
examListVisible.value = true;
|
examListVisible.value = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -301,6 +279,7 @@ onMounted(() => {
|
|||||||
.form-actions {
|
.form-actions {
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 12px;
|
gap: 12px;
|
||||||
|
justify-content: space-between;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -0,0 +1,3 @@
|
|||||||
|
<template>
|
||||||
|
<div class="p-4">Examinee</div>
|
||||||
|
</template>
|
||||||
@ -0,0 +1,3 @@
|
|||||||
|
<template>
|
||||||
|
<div class="p-4">Practice</div>
|
||||||
|
</template>
|
||||||
@ -0,0 +1,3 @@
|
|||||||
|
<template>
|
||||||
|
<div class="p-4">Training</div>
|
||||||
|
</template>
|
||||||
@ -6,7 +6,7 @@ runmode = dev
|
|||||||
# MySQL - 远程连接配置
|
# MySQL - 远程连接配置
|
||||||
mysqluser = gotest
|
mysqluser = gotest
|
||||||
mysqlpass = 2nZhRdMPCNZrdzsd
|
mysqlpass = 2nZhRdMPCNZrdzsd
|
||||||
mysqlurls = 192.168.31.10:3306
|
mysqlurls = 212.64.112.158:3388
|
||||||
mysqldb = gotest
|
mysqldb = gotest
|
||||||
|
|
||||||
# ORM配置
|
# ORM配置
|
||||||
|
|||||||
@ -24,6 +24,8 @@ func (c *ExamQuestionController) GetList() {
|
|||||||
tenantId, _ := c.Ctx.Input.GetData("tenantId").(int)
|
tenantId, _ := c.Ctx.Input.GetData("tenantId").(int)
|
||||||
keyword := c.GetString("keyword")
|
keyword := c.GetString("keyword")
|
||||||
typeStr := c.GetString("type")
|
typeStr := c.GetString("type")
|
||||||
|
bankId, _ := c.GetInt64("bank_id", 0)
|
||||||
|
|
||||||
var qtype *int8
|
var qtype *int8
|
||||||
if typeStr != "" {
|
if typeStr != "" {
|
||||||
if iv, err := strconv.Atoi(typeStr); err == nil {
|
if iv, err := strconv.Atoi(typeStr); err == nil {
|
||||||
@ -31,12 +33,15 @@ func (c *ExamQuestionController) GetList() {
|
|||||||
qtype = &v
|
qtype = &v
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
page, _ := c.GetInt("page", 1)
|
page, _ := c.GetInt("page", 1)
|
||||||
pageSize, _ := c.GetInt("pageSize", 10)
|
pageSize, _ := c.GetInt("pageSize", 10)
|
||||||
|
|
||||||
list, total, err := services.GetExamQuestions(services.QuestionListParams{
|
list, total, err := services.GetExamQuestions(services.QuestionListParams{
|
||||||
TenantId: tenantId,
|
TenantId: tenantId,
|
||||||
Keyword: keyword,
|
Keyword: keyword,
|
||||||
QuestionType: qtype,
|
QuestionType: qtype,
|
||||||
|
BankId: bankId,
|
||||||
Page: page,
|
Page: page,
|
||||||
PageSize: pageSize,
|
PageSize: pageSize,
|
||||||
})
|
})
|
||||||
@ -45,7 +50,7 @@ func (c *ExamQuestionController) GetList() {
|
|||||||
c.ServeJSON()
|
c.ServeJSON()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// build minimal DTO to limit fields in response
|
|
||||||
items := make([]map[string]interface{}, 0, len(list))
|
items := make([]map[string]interface{}, 0, len(list))
|
||||||
for _, q := range list {
|
for _, q := range list {
|
||||||
if q == nil {
|
if q == nil {
|
||||||
@ -59,6 +64,7 @@ func (c *ExamQuestionController) GetList() {
|
|||||||
"score": q.Score,
|
"score": q.Score,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
c.Data["json"] = map[string]interface{}{
|
c.Data["json"] = map[string]interface{}{
|
||||||
"code": 0,
|
"code": 0,
|
||||||
"message": "ok",
|
"message": "ok",
|
||||||
@ -101,6 +107,7 @@ func (c *ExamQuestionController) Create() {
|
|||||||
QuestionAnalysis string `json:"question_analysis"`
|
QuestionAnalysis string `json:"question_analysis"`
|
||||||
Options []map[string]string `json:"options"`
|
Options []map[string]string `json:"options"`
|
||||||
Answer interface{} `json:"answer"`
|
Answer interface{} `json:"answer"`
|
||||||
|
BankId int64 `json:"bank_id"`
|
||||||
}
|
}
|
||||||
if err := json.Unmarshal(c.Ctx.Input.RequestBody, &payload); err != nil {
|
if err := json.Unmarshal(c.Ctx.Input.RequestBody, &payload); err != nil {
|
||||||
c.Data["json"] = map[string]interface{}{"code": 1, "message": "请求参数错误: " + err.Error(), "data": nil}
|
c.Data["json"] = map[string]interface{}{"code": 1, "message": "请求参数错误: " + err.Error(), "data": nil}
|
||||||
@ -115,6 +122,9 @@ func (c *ExamQuestionController) Create() {
|
|||||||
Score: payload.Score,
|
Score: payload.Score,
|
||||||
Status: 1,
|
Status: 1,
|
||||||
}
|
}
|
||||||
|
if payload.BankId > 0 {
|
||||||
|
q.BankId = payload.BankId
|
||||||
|
}
|
||||||
var opts []models.ExamQuestionOption
|
var opts []models.ExamQuestionOption
|
||||||
for _, o := range payload.Options {
|
for _, o := range payload.Options {
|
||||||
opts = append(opts, models.ExamQuestionOption{OptionLabel: o["label"], OptionContent: o["content"]})
|
opts = append(opts, models.ExamQuestionOption{OptionLabel: o["label"], OptionContent: o["content"]})
|
||||||
@ -149,6 +159,7 @@ func (c *ExamQuestionController) Create() {
|
|||||||
func (c *ExamQuestionController) BatchCreate() {
|
func (c *ExamQuestionController) BatchCreate() {
|
||||||
tenantId, _ := c.Ctx.Input.GetData("tenantId").(int)
|
tenantId, _ := c.Ctx.Input.GetData("tenantId").(int)
|
||||||
var payload struct {
|
var payload struct {
|
||||||
|
BankId int64 `json:"bank_id"`
|
||||||
Items []struct {
|
Items []struct {
|
||||||
QuestionTitle string `json:"question_title"`
|
QuestionTitle string `json:"question_title"`
|
||||||
QuestionType int8 `json:"question_type"`
|
QuestionType int8 `json:"question_type"`
|
||||||
@ -182,6 +193,9 @@ func (c *ExamQuestionController) BatchCreate() {
|
|||||||
Score: item.Score,
|
Score: item.Score,
|
||||||
Status: 1,
|
Status: 1,
|
||||||
}
|
}
|
||||||
|
if payload.BankId > 0 {
|
||||||
|
q.BankId = payload.BankId
|
||||||
|
}
|
||||||
var opts []models.ExamQuestionOption
|
var opts []models.ExamQuestionOption
|
||||||
for _, o := range item.Options {
|
for _, o := range item.Options {
|
||||||
opts = append(opts, models.ExamQuestionOption{OptionLabel: o["label"], OptionContent: o["content"]})
|
opts = append(opts, models.ExamQuestionOption{OptionLabel: o["label"], OptionContent: o["content"]})
|
||||||
@ -251,6 +265,7 @@ func (c *ExamQuestionController) Update() {
|
|||||||
QuestionAnalysis string `json:"question_analysis"`
|
QuestionAnalysis string `json:"question_analysis"`
|
||||||
Options []map[string]string `json:"options"`
|
Options []map[string]string `json:"options"`
|
||||||
Answer interface{} `json:"answer"`
|
Answer interface{} `json:"answer"`
|
||||||
|
BankId int64 `json:"bank_id"`
|
||||||
}
|
}
|
||||||
if err := json.Unmarshal(c.Ctx.Input.RequestBody, &payload); err != nil {
|
if err := json.Unmarshal(c.Ctx.Input.RequestBody, &payload); err != nil {
|
||||||
c.Data["json"] = map[string]interface{}{"code": 1, "message": "请求参数错误: " + err.Error(), "data": nil}
|
c.Data["json"] = map[string]interface{}{"code": 1, "message": "请求参数错误: " + err.Error(), "data": nil}
|
||||||
@ -258,6 +273,9 @@ func (c *ExamQuestionController) Update() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
q := &models.ExamQuestion{QuestionType: payload.QuestionType, QuestionTitle: payload.QuestionTitle, QuestionAnalysis: payload.QuestionAnalysis, Score: payload.Score}
|
q := &models.ExamQuestion{QuestionType: payload.QuestionType, QuestionTitle: payload.QuestionTitle, QuestionAnalysis: payload.QuestionAnalysis, Score: payload.Score}
|
||||||
|
if payload.BankId > 0 {
|
||||||
|
q.BankId = payload.BankId
|
||||||
|
}
|
||||||
var opts []models.ExamQuestionOption
|
var opts []models.ExamQuestionOption
|
||||||
for _, o := range payload.Options {
|
for _, o := range payload.Options {
|
||||||
opts = append(opts, models.ExamQuestionOption{OptionLabel: o["label"], OptionContent: o["content"]})
|
opts = append(opts, models.ExamQuestionOption{OptionLabel: o["label"], OptionContent: o["content"]})
|
||||||
|
|||||||
@ -10,6 +10,7 @@ import (
|
|||||||
type ExamQuestion struct {
|
type ExamQuestion struct {
|
||||||
Id int64 `orm:"column(id);auto" json:"id"`
|
Id int64 `orm:"column(id);auto" json:"id"`
|
||||||
TenantId int `orm:"column(tenant_id)" json:"tenant_id"`
|
TenantId int `orm:"column(tenant_id)" json:"tenant_id"`
|
||||||
|
BankId int64 `orm:"column(bank_id);null" json:"bank_id"`
|
||||||
QuestionType int8 `orm:"column(question_type)" json:"question_type"`
|
QuestionType int8 `orm:"column(question_type)" json:"question_type"`
|
||||||
QuestionTitle string `orm:"column(question_title);size(1000)" json:"question_title"`
|
QuestionTitle string `orm:"column(question_title);size(1000)" json:"question_title"`
|
||||||
QuestionAnalysis string `orm:"column(question_analysis);size(2000);null" json:"question_analysis"`
|
QuestionAnalysis string `orm:"column(question_analysis);size(2000);null" json:"question_analysis"`
|
||||||
|
|||||||
@ -13,6 +13,7 @@ type QuestionListParams struct {
|
|||||||
TenantId int
|
TenantId int
|
||||||
Keyword string
|
Keyword string
|
||||||
QuestionType *int8
|
QuestionType *int8
|
||||||
|
BankId int64
|
||||||
Page int
|
Page int
|
||||||
PageSize int
|
PageSize int
|
||||||
}
|
}
|
||||||
@ -42,6 +43,9 @@ func GetExamQuestions(params QuestionListParams) ([]*models.ExamQuestion, int64,
|
|||||||
if params.QuestionType != nil {
|
if params.QuestionType != nil {
|
||||||
qs = qs.Filter("question_type", *params.QuestionType)
|
qs = qs.Filter("question_type", *params.QuestionType)
|
||||||
}
|
}
|
||||||
|
if params.BankId > 0 {
|
||||||
|
qs = qs.Filter("bank_id", params.BankId)
|
||||||
|
}
|
||||||
total, err := qs.Count()
|
total, err := qs.Count()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, 0, err
|
return nil, 0, err
|
||||||
@ -121,7 +125,13 @@ func UpdateExamQuestion(tenantId int, id int64, q *models.ExamQuestion, options
|
|||||||
existing.QuestionTitle = q.QuestionTitle
|
existing.QuestionTitle = q.QuestionTitle
|
||||||
existing.QuestionAnalysis = q.QuestionAnalysis
|
existing.QuestionAnalysis = q.QuestionAnalysis
|
||||||
existing.Score = q.Score
|
existing.Score = q.Score
|
||||||
if _, err := o.Update(&existing, "QuestionType", "QuestionTitle", "QuestionAnalysis", "Score"); err != nil {
|
// 可选更新题库归属
|
||||||
|
fields := []string{"QuestionType", "QuestionTitle", "QuestionAnalysis", "Score"}
|
||||||
|
if q.BankId > 0 {
|
||||||
|
existing.BankId = q.BankId
|
||||||
|
fields = append(fields, "BankId")
|
||||||
|
}
|
||||||
|
if _, err := o.Update(&existing, fields...); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
// soft delete old options and answer
|
// soft delete old options and answer
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user