You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
spring-boot-online-exam/frontend/src/views/list/QuestionTableList.vue

499 lines
19 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

<template>
<!-- 使用Ant Design的a-card组件作为外层容器设置无边框 -->
<a-card :bordered="false">
<!-- 定义操作按钮的工具栏区域 -->
<div id="toolbar">
<!-- 新建按钮类型为主要按钮primary带有加号图标点击时调用$refs.createQuestionModal.create()方法来触发新建操作 -->
<a-button type="primary" icon="plus" @click="$refs.createQuestionModal.create()"></a-button>&nbsp;
<!-- 全量刷新按钮类型为主要按钮primary带有刷新图标点击时调用loadAll()方法来重新加载所有题目数据 -->
<a-button type="primary" icon="reload" @click="loadAll()"></a-button>
</div>
<BootstrapTable
ref="table"
:columns="columns"
:data="tableData"
:options="options"
/>
<!-- ref是为了方便用this.$refs.modal直接引用下同 -->
<step-by-step-question-modal ref="createQuestionModal" @ok="handleOk" />
<!-- 更新题目模态框 -->
<summernote-update-modal ref="questionUpdateModal" @ok="handleOk" />
<!-- 查看题目模态框 -->
<question-view-modal ref="modalView" @ok="handleOk" />
<!-- 编辑题目模态框 -->
<question-edit-modal ref="modalEdit" @ok="handleOk" />
</a-card>
</template>
<script>
import '../../plugins/bootstrap-table'
import QuestionViewModal from './modules/QuestionViewModal'
import QuestionEditModal from './modules/QuestionEditModal'
import StepByStepQuestionModal from './modules/StepByStepQuestionModal'
import { getQuestionAll, questionUpdate, getQuestionSelection } from '../../api/exam'
import SummernoteUpdateModal from '@views/list/modules/SummernoteUpdateModal'
import $ from 'jquery'
export default {
/**
* 名称QuestionTableList 组件
* 描述:此组件用于显示问题表格列表,并提供对问题进行详细操作的功能
* 组件包含:
* - SummernoteUpdateModal: 用于更新问题描述的模态框
* - StepByStepQuestionModal: 用于逐步解答问题的模态框
* - QuestionViewModal: 用于查看问题详情的模态框
* - QuestionEditModal: 用于编辑问题的模态框
*/
name: 'QuestionTableList',
components: {
SummernoteUpdateModal,
StepByStepQuestionModal,
QuestionViewModal,
QuestionEditModal
},
data () {
const that = this // 方便在bootstrap-table中引用methods
return {
// 表头
columns: [
{
title: '序号',
field: 'serial',
formatter: function (value, row, index) {
// 格式化函数,用于生成序号
// 参数:
// value: 当前单元格的值
// row: 当前行的数据
// index: 当前行的索引
return index + 1 // 这样的话每翻一页都会重新从1开始
// 注释解释了代码的功能和潜在的行为特点
}
},
// 定义表格列的配置对象
{
// 列标题
title: '题干',
// 对应的数据字段
field: 'name',
// 列宽度
width: 200,
formatter: (value, row) => {
// 返回格式化后的HTML包装题干文本到一个div中
return '<div class="question-name" style="height: 100%;width: 100%">' + value + '</div>'
},
// 定义列相关的事件处理函数
events: {
'click .question-name': function (e, value, row, index) {
// 调用编辑题干的模态框组件,传递所需参数和回调函数
that.$refs.questionUpdateModal.edit('summernote-question-name-update', row, 'name', '更新题干', questionUpdate)
}
}
},
{
// 定义表格列的标题
title: '解析',
// 定义表格列对应的数据字段
field: 'description',
// 设置列的宽度
width: 200,
formatter: (value, row) => {
// 将当前行的描述值包装在名为question-desc的div中
return '<div class="question-desc">' + value + '</div>'
},
// 定义列相关事件的处理函数
events: {
// 定义点击question-desc类元素的事件处理函数
'click .question-desc': function (e, value, row, index) {
that.$refs.questionUpdateModal.edit('summernote-question-desc-update', row, 'description', '更新题目解析', questionUpdate)
}
}
},
{
// 定义表格列的标题
title: '分数',
// 定义表格列的数据字段
field: 'score',
// 定义单元格内容的格式化函数
formatter: (value, row) => {
// 将分数值包裹在div中以便于后续的事件处理
return '<div class="question-score">' + value + '</div>'
},
// 定义与列相关的事件处理函数
events: {
// 当点击分数所在的单元格时触发的事件
'click .question-score': function (e, value, row, index) {
// 将事件目标元素转换为jQuery对象以便使用jQuery方法
const $element = $(e.target) // 把元素转换成html对象
$element.html('<input type="text" value="' + value + '">')
}
}
},
{
/**
* 配置表格列标题和数据字段
*
* @param {String} title - 表格列的标题,用于显示在表头
* @param {String} field - 表格列的数据字段,用于绑定数据源中的属性
*/
title: '创建人',
field: 'creator'
},
{
// 列的标题
title: '难度',
// 列的数据字段
field: 'level',
formatter: (value, row) => {
// 格式化列的显示内容
// 参数 value: 当前单元格的值
// 参数 row: 当前行的数据
return '<div class="question-level">' + value + '</div>'
},
events: {
'click .question-level': function (e, value, row, index) {
// 点击难度列的事件处理函数
// 参数 e: 事件对象
// 参数 value: 当前单元格的值
// 参数 row: 当前行的数据
// 参数 index: 当前行的索引
const $element = $(e.target) // 把元素转换成html对象
if ($element.children().length > 0) return // 防止重复渲染
getQuestionSelection().then(res => {
// 获取问题下拉选项的异步处理函数
console.log(res)
if (res.code === 0) {
console.log(res.data)
const levels = res.data.levels
let inner = '<select>'
for (let i = 0; i < levels.length; i++) {
// 生成下拉选项
if (levels[i].description === value) {
// 设置默认的选中值为当前的值
inner += '<option value ="' + levels[i].id + '" name="' + levels[i].name + '" selected="selected">' + levels[i].description + '</option>'
} else {
inner += '<option value ="' + levels[i].id + '" name="' + levels[i].name + '">' + levels[i].description + '</option>'
}
}
inner += '</select>'
$element.html(inner)
} else {
// 显示错误通知
that.$notification.error({
message: '获取问题下拉选项失败',
description: res.msg
})
}
})
}
}
},
{
// 列的标题
title: '题型',
// 对应的数据字段
field: 'type',
formatter: (value, row) => {
// 格式化函数,用于渲染单元格内容
// value: 当前单元格的值
// row: 当前行的数据
return '<div class="question-type">' + value + '</div>'
},
events: {
'click .question-type': function (e, value, row, index) {
// 点击事件处理函数
// e: 事件对象
// value: 当前单元格的值
// row: 当前行的数据
// index: 当前行的索引
const $element = $(e.target) // 把元素转换成html对象
if ($element.children().length > 0) return // 防止重复渲染
getQuestionSelection().then(res => {
// 获取问题下拉选项
console.log(res)
if (res.code === 0) {
console.log(res.data)
const types = res.data.types
let inner = '<select>'
for (let i = 0; i < types.length; i++) {
if (types[i].description === value) {
// 设置默认的选中值为当前的值
inner += '<option value ="' + types[i].id + '" name="' + types[i].name + '" selected="selected">' + types[i].description + '</option>'
} else {
inner += '<option value ="' + types[i].id + '" name="' + types[i].name + '">' + types[i].description + '</option>'
}
}
inner += '</select>'
$element.html(inner)
} else {
that.$notification.error({
message: '获取问题下拉选项失败',
description: res.msg
})
}
})
}
}
},
{
// 列的标题
title: '学科',
// 对应的数据字段
field: 'category',
formatter: (value, row) => {
// 数据格式化函数
// 返回学科列的HTML用于在界面上显示
return '<div class="question-category">' + value + '</div>'
},
events: {
'click .question-category': function (e, value, row, index) {
const $element = $(e.target) // 把元素转换成html对象
if ($element.children().length > 0) return // 防止重复渲染
// 异步获取问题选择数据
getQuestionSelection().then(res => {
console.log(res)
if (res.code === 0) {
console.log(res.data)
const categories = res.data.categories
let inner = '<select>'
// 遍历学科类别数组,生成<option>元素
for (let i = 0; i < categories.length; i++) {
if (categories[i].name === value) { // 学科还是用名字吧
// 设置默认的选中值为当前的值
inner += '<option value ="' + categories[i].id + '" name="' + categories[i].description + '" selected="selected">' + categories[i].name + '</option>'
} else {
inner += '<option value ="' + categories[i].id + '" name="' + categories[i].description + '">' + categories[i].name + '</option>'
}
}
inner += '</select>'
$element.html(inner)
} else {
// 如果获取数据失败,显示错误通知
that.$notification.error({
message: '获取问题下拉选项失败',
description: res.msg
})
}
})
}
}
},
{
// 列标题
title: '更新时间',
// 列数据字段
field: 'updateTime'
},
{
// 列标题
title: '操作',
// 列数据字段
field: 'action',
// 列对齐方式
align: 'center',
// 自定义列内容格式化函数
formatter: (value, row) => {
// 返回操作按钮组的HTML字符串
return '<button type="button" class="btn btn-success view-question">详情</button>' +
'&nbsp;&nbsp;' +
'<button type="button" class="btn btn-success edit-question">编辑</button>'
},
// 定义列内元素的事件监听器
events: {
// 点击详情按钮事件处理函数
'click .view-question': function (e, value, row, index) {
that.handleSub(row)
},
// 点击编辑按钮事件处理函数
'click .edit-question': function (e, value, row, index) {
that.handleEdit(row)
}
}
}
],
tableData: [], // bootstrap-table的数据
// custom bootstrap-table
// 自定义bootstrap-table配置选项
options: {
// 启用搜索功能
search: true,
// 显示列选择下拉框
showColumns: true,
// 显示导出按钮
showExport: true,
// 启用分页
pagination: true,
toolbar: '#toolbar',
// 下面两行是支持高级搜索,即按照字段搜索
advancedSearch: true,
// 下面两行是支持高级搜索,即按照字段搜索
idTable: 'advancedTable',
// 下面是常用的事件更多的点击事件可以参考http://www.itxst.com/bootstrap-table-events/tutorial.html
// onClickRow: that.clickRow,
// onClickCell: that.clickCell // 单元格单击事件
onDblClickCell: that.dblClickCell // 单元格双击事件
}
}
},
mounted () {
this.loadAll() // 加载所有问题的数据
},
methods: {
/**
* 编辑问题
* @param {Object} record - 问题记录
*/
handleEdit (record) {
this.$refs.modalEdit.edit(record)
},
handleSub (record) {
// 查看题目
/**
* 查看问题
* @param {Object} record - 问题记录
*/
console.log(record)
this.$refs.modalView.edit(record)
},
/**
* 确认操作后重新加载数据
*/
handleOk () {
this.loadAll() // 加载所有问题的数据
},
/**
* 双击表格单元格进行编辑
* @param {String} field - 字段名
* @param {String} value - 字段值
* @param {Object} row - 行记录
* @param {Object} $element - DOM元素
*/
dblClickCell (field, value, row, $element) {
if (field === 'score') { // 更新分数
const childrenInput = $element.children('.question-score').children('input') // 获取输入框的值
if (childrenInput.length === 0) return
row.score = childrenInput[0].value
const that = this
questionUpdate(row).then(res => {
// 成功就跳转到结果页面
console.log(res)
if (res.code === 0) {
$element.children('.question-score').text(row.score)
that.$notification.success({
message: '更新成功',
description: '更新成功'
})
}
})
}
if (field === 'level') { // 更新难度
// 获取输入框的值
const childrenSelect = $element.children('.question-level').children('select') // 获取输入框的值
if (childrenSelect.length === 0) return
// 找到选中的选项
const optionSelected = $(childrenSelect[0]).find('option:selected')
// 更新行数据的难度ID和难度名称
row.levelId = optionSelected.val()
console.log(row.levelId)
// 调用问题更新API进行异步更新
row.level = optionSelected.text()
// 成功就跳转到结果页面
console.log(row.level)
const that = this
questionUpdate(row).then(res => {
// 成功就跳转到结果页面
console.log(res)
if (res.code === 0) {
// 更新成功后,修改页面显示的难度,并显示成功通知
$element.children('.question-level').text(row.level)
that.$notification.success({
message: '更新成功',
description: '更新成功'
})
}
})
}
if (field === 'type') { // 更新题型
// 获取输入框的值
const childrenSelect = $element.children('.question-type').children('select') // 获取输入框的值
if (childrenSelect.length === 0) return
// 找到选中的选项
const optionSelected = $(childrenSelect[0]).find('option:selected')
// 更新row对象的typeId和type属性
row.typeId = optionSelected.val()
row.type = optionSelected.text()
// 保存this的引用以便在promise中使用
const that = this
// 调用questionUpdate函数更新问题然后根据结果进行处理
questionUpdate(row).then(res => {
// 成功就跳转到结果页面
console.log(res)
if (res.code === 0) {
// 更新页面上显示的题型
$element.children('.question-type').text(row.type)
// 显示成功通知
that.$notification.success({
message: '更新成功',
description: '更新成功'
})
}
})
}
if (field === 'category') { // 更新学科
// 获取输入框的值
const childrenSelect = $element.children('.question-category').children('select') // 获取输入框的值
console.log(childrenSelect)
if (childrenSelect.length === 0) return
// 找到选中的选项
const optionSelected = $(childrenSelect[0]).find('option:selected')
// 更新行数据的学科ID和学科名称
row.categoryId = optionSelected.val()
row.category = optionSelected.text()
const that = this
// 调用问题更新API
questionUpdate(row).then(res => {
// 成功就跳转到结果页面
console.log(res)
if (res.code === 0) {
// 更新成功后,修改界面上的学科名称
$element.children('.question-category').text(row.category)
// 显示成功的通知
that.$notification.success({
message: '更新成功',
description: '更新成功'
})
}
})
}
},
/**
* 加载所有问题数据
* 此方法通过调用后端API来获取所有问题的列表并将其用于更新表格数据
*/
loadAll () {
// 创建一个指向当前组件实例的引用以避免在回调函数中直接使用this
const that = this
// 调用获取所有问题的API
getQuestionAll()
.then(res => {
// 检查返回的响应码
if (res.code === 0) {
// 如果成功,更新表格数据
that.tableData = res.data
// 调用表格组件的初始化方法,以应用新的数据
that.$refs.table._initTable()
} else {
// 如果失败,显示错误通知
that.$notification.error({
message: '获取全部问题的列表失败',
description: res.msg
})
}
})
}
}
}
</script>