考试管理
This commit is contained in:
parent
96476e6127
commit
b6bc4b3a04
21833
package-lock.json
generated
21833
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -35,7 +35,7 @@ async function createWindow() {
|
||||
width: 800, // 默认宽度(实际会被最大化覆盖)
|
||||
height: 600, // 默认高度(实际会被最大化覆盖)
|
||||
show: false, // 先隐藏窗口,避免闪烁
|
||||
frame: false, // 无边框窗口(可选,根据需求决定是否保留)
|
||||
// frame: false, // 无边框窗口(可选,根据需求决定是否保留)
|
||||
webPreferences: {
|
||||
// 改为使用绝对路径解析
|
||||
preload: require('path').join(process.cwd(), 'src/preload.js'),
|
||||
|
@ -25,8 +25,8 @@
|
||||
<i class="el-icon-arrow-down el-icon--right"></i>
|
||||
</span>
|
||||
<el-dropdown-menu slot="dropdown">
|
||||
<el-dropdown-item>个人中心</el-dropdown-item>
|
||||
<el-dropdown-item>修改密码</el-dropdown-item>
|
||||
<!-- <el-dropdown-item>个人中心</el-dropdown-item> -->
|
||||
<!-- <el-dropdown-item>修改密码</el-dropdown-item> -->
|
||||
<el-dropdown-item divided @click.native="handleLogout">退出登录</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</el-dropdown>
|
||||
|
@ -4,7 +4,9 @@ import WelcomeView from '../views/WelcomeView.vue'
|
||||
import AdminLayout from '../components/admin/AdminLayout.vue'
|
||||
import AdminHomeView from '../views/admin/AdminHomeView.vue'
|
||||
import QuestionManagementView from '../views/admin/QuestionManagementView.vue'
|
||||
import ExamineeManagementView from '../views/admin/ExamineeManagementView.vue' // 添加这行
|
||||
import ExamineeManagementView from '../views/admin/ExamineeManagementView.vue'
|
||||
// 添加考试管理组件导入
|
||||
import ExamManagementView from '../views/admin/ExamManagementView.vue'
|
||||
|
||||
Vue.use(VueRouter)
|
||||
|
||||
@ -33,9 +35,14 @@ const routes = [
|
||||
component: QuestionManagementView
|
||||
},
|
||||
{
|
||||
path: 'examinee', // 添加考生管理路由
|
||||
path: 'examinee',
|
||||
name: 'ExamineeManagement',
|
||||
component: ExamineeManagementView
|
||||
},
|
||||
{
|
||||
path: 'exam',
|
||||
name: 'ExamManagement',
|
||||
component: ExamManagementView
|
||||
}
|
||||
// 可以在这里添加更多子路由
|
||||
]
|
||||
|
355
src/views/admin/ExamManagementView.vue
Normal file
355
src/views/admin/ExamManagementView.vue
Normal file
@ -0,0 +1,355 @@
|
||||
<template>
|
||||
<div class="exam-management-container">
|
||||
<div class="exam-header">
|
||||
<h1>考试管理</h1>
|
||||
<!-- <el-button type="primary" @click="handleAddExam">+ 添加考试</el-button> -->
|
||||
</div>
|
||||
<div class="exam-content">
|
||||
<el-table :data="examList" style="width: 100%" v-loading="loading">
|
||||
<el-table-column prop="id" label="ID" width="80" />
|
||||
<el-table-column prop="exam_minutes" label="考试时长(分钟)" width="180" />
|
||||
<el-table-column prop="exam_minutes_min" label="最短考试时长(分钟)" width="180" />
|
||||
<el-table-column label="考试须知" min-width="200">
|
||||
<template slot-scope="scope">
|
||||
<div class="exam-notice-cell" v-if="scope.row.exam_notice">
|
||||
<div v-if="Array.isArray(scope.row.exam_notice)" class="notice-list">
|
||||
<div v-for="(item, index) in scope.row.exam_notice" :key="index" class="notice-item">
|
||||
{{ item }}
|
||||
</div>
|
||||
</div>
|
||||
<div v-else-if="typeof scope.row.exam_notice === 'string'">
|
||||
<!-- 尝试将字符串按换行符分割显示 -->
|
||||
<div v-if="scope.row.exam_notice.includes('\n')" class="notice-list">
|
||||
<div v-for="(item, index) in scope.row.exam_notice.split('\n')" :key="index" class="notice-item">
|
||||
{{ item }}
|
||||
</div>
|
||||
</div>
|
||||
<!-- 普通字符串直接显示 -->
|
||||
<div v-else :title="scope.row.exam_notice" class="ellipsis-cell">
|
||||
{{ scope.row.exam_notice }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<span v-else>-</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="created_at" label="创建时间" width="180" />
|
||||
<el-table-column prop="updated_at" label="更新时间" width="180" />
|
||||
<el-table-column label="操作" width="180" fixed="right">
|
||||
<template slot-scope="scope">
|
||||
<el-button type="primary" size="small" @click="handleEditExam(scope.row)">
|
||||
编辑
|
||||
</el-button>
|
||||
<!-- <el-button
|
||||
type="danger"
|
||||
size="small"
|
||||
@click="handleDeleteExam(scope.row.id)"
|
||||
>
|
||||
删除
|
||||
</el-button> -->
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
|
||||
<!-- 添加/编辑考试弹窗 -->
|
||||
<el-dialog :title="dialogTitle" :visible.sync="dialogVisible" width="600px" append-to-body show-close>
|
||||
<el-form :model="formData" label-width="120px">
|
||||
<el-form-item label="考试名称" prop="exam_name">
|
||||
<el-input v-model="formData.exam_name" placeholder="请输入考试名称(选填)" />
|
||||
</el-form-item>
|
||||
<el-form-item label="考试描述" prop="exam_description">
|
||||
<el-input v-model="formData.exam_description" type="textarea" placeholder="请输入考试描述" :rows="4" />
|
||||
</el-form-item>
|
||||
<el-form-item label="考试须知" prop="exam_notice">
|
||||
<el-input v-model="formData.exam_notice" type="textarea" placeholder="请输入考试须知,每行一条" :rows="3" />
|
||||
</el-form-item>
|
||||
<el-form-item label="考试时长(分钟)" prop="exam_minutes"
|
||||
:rules="[{ required: true, message: '请输入考试时长', trigger: 'blur' }, { type: 'number', min: 1, message: '考试时长必须大于0分钟' }]">
|
||||
<el-input v-model.number="formData.exam_minutes" type="number" placeholder="请输入考试时长" />
|
||||
</el-form-item>
|
||||
<el-form-item label="最少考试时间(分钟)" prop="exam_minutes_min"
|
||||
:rules="[{ required: true, message: '请输入最少考试时间', trigger: 'blur' }, { type: 'number', min: 0, message: '最少考试时间不能小于0分钟' }]">
|
||||
<el-input v-model.number="formData.exam_minutes_min" type="number" placeholder="请输入最少考试时间" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<div slot="footer">
|
||||
<el-button @click="dialogVisible = false">取消</el-button>
|
||||
<el-button type="primary" @click="handleSaveExam">保存</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'ExamManagementView',
|
||||
data () {
|
||||
return {
|
||||
examList: [],
|
||||
loading: false,
|
||||
dialogVisible: false,
|
||||
dialogTitle: '添加考试',
|
||||
isEdit: false,
|
||||
formData: {
|
||||
id: null,
|
||||
exam_name: '',
|
||||
exam_description: '',
|
||||
exam_notice: '',
|
||||
exam_minutes: null,
|
||||
exam_minutes_min: null,
|
||||
exam_examinee_type: ''
|
||||
}
|
||||
}
|
||||
},
|
||||
mounted () {
|
||||
this.fetchExams()
|
||||
},
|
||||
methods: {
|
||||
// 获取考试列表
|
||||
// 修复fetchExams方法中的返回值处理逻辑
|
||||
async fetchExams () {
|
||||
this.loading = true
|
||||
try {
|
||||
const result = await window.electronAPI.examFetchAll()
|
||||
|
||||
// 检查返回值类型来确定是成功还是失败
|
||||
if (Array.isArray(result)) {
|
||||
// 成功时:result本身就是考试列表数组
|
||||
// 格式化日期
|
||||
const formattedExams = result.map(exam => ({
|
||||
...exam
|
||||
// created_at: this.formatDate(exam.created_at),
|
||||
// updated_at: this.formatDate(exam.updated_at)
|
||||
}))
|
||||
this.examList = formattedExams
|
||||
} else if (result && !result.success) {
|
||||
// 失败时:result是包含success: false和error的对象
|
||||
this.$message.error('获取考试列表失败: ' + result.error)
|
||||
console.error('获取考试列表失败', result.error)
|
||||
} else {
|
||||
// 意外情况
|
||||
this.$message.error('获取考试列表失败: 返回格式异常')
|
||||
console.error('获取考试列表失败,返回格式异常', result)
|
||||
}
|
||||
} catch (error) {
|
||||
this.$message.error('获取考试列表失败: ' + error.message)
|
||||
console.error('获取考试列表失败', error)
|
||||
} finally {
|
||||
this.loading = false
|
||||
}
|
||||
},
|
||||
|
||||
// 处理保存考试
|
||||
async handleSaveExam () {
|
||||
try {
|
||||
// 将数据转换为普通对象
|
||||
const examData = { ...this.formData }
|
||||
|
||||
// 将考试须知字符串转换为数组并序列化
|
||||
if (examData.exam_notice) {
|
||||
const noticeArray = examData.exam_notice.split('\n').filter(item => item.trim() !== '')
|
||||
examData.exam_notice = JSON.stringify(noticeArray)
|
||||
}
|
||||
|
||||
if (this.isEdit) {
|
||||
await window.electronAPI.examUpdate(examData.id, examData)
|
||||
this.$message.success('考试更新成功')
|
||||
} else {
|
||||
await window.electronAPI.examCreate(examData)
|
||||
this.$message.success('考试添加成功')
|
||||
}
|
||||
this.dialogVisible = false
|
||||
this.fetchExams() // 重新加载列表
|
||||
} catch (error) {
|
||||
this.$message.error('保存考试失败: ' + error.message)
|
||||
console.error('保存考试失败', error)
|
||||
}
|
||||
},
|
||||
|
||||
// 处理删除考试
|
||||
handleDeleteExam (id) {
|
||||
this.$confirm(
|
||||
'确定要删除该考试吗?',
|
||||
'确认删除',
|
||||
{
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
}
|
||||
)
|
||||
.then(async () => {
|
||||
try {
|
||||
await window.electronAPI.examDelete(id)
|
||||
this.$message.success('考试删除成功')
|
||||
this.fetchExams() // 重新加载列表
|
||||
} catch (error) {
|
||||
this.$message.error('删除考试失败: ' + error.message)
|
||||
console.error('删除考试失败', error)
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
// 取消删除
|
||||
})
|
||||
},
|
||||
// 格式化日期
|
||||
formatDate (dateString) {
|
||||
if (!dateString) return ''
|
||||
const date = new Date(dateString)
|
||||
return date.toLocaleString()
|
||||
},
|
||||
|
||||
// 处理添加考试
|
||||
handleAddExam () {
|
||||
this.isEdit = false
|
||||
this.dialogTitle = '添加考试'
|
||||
this.formData = {
|
||||
id: null,
|
||||
exam_name: '',
|
||||
exam_description: '',
|
||||
exam_notice: '',
|
||||
exam_minutes: null,
|
||||
exam_minutes_min: null,
|
||||
exam_examinee_type: ''
|
||||
}
|
||||
this.dialogVisible = true
|
||||
},
|
||||
|
||||
// 处理编辑考试
|
||||
handleEditExam (row) {
|
||||
this.isEdit = true
|
||||
this.dialogTitle = '编辑考试'
|
||||
// 深拷贝row对象
|
||||
const examData = { ...row }
|
||||
|
||||
// 将考试须知JSON数组转换为换行分隔的字符串
|
||||
if (examData.exam_notice) {
|
||||
try {
|
||||
// 首先检查是否已经是字符串,如果是,不需要解析
|
||||
if (typeof examData.exam_notice === 'string') {
|
||||
// 检查是否是有效的JSON字符串
|
||||
try {
|
||||
const noticeArray = JSON.parse(examData.exam_notice)
|
||||
if (Array.isArray(noticeArray)) {
|
||||
// 如果是JSON数组,转换为换行分隔的字符串
|
||||
examData.exam_notice = noticeArray.join('\n')
|
||||
}
|
||||
// 如果不是数组,则保持原字符串不变
|
||||
} catch (e) {
|
||||
// 如果解析失败,说明不是JSON字符串,保持原字符串不变
|
||||
console.log('考试须知不是JSON字符串,保持原样:', examData.exam_notice)
|
||||
}
|
||||
} else if (Array.isArray(examData.exam_notice)) {
|
||||
// 如果已经是数组,直接转换为换行分隔的字符串
|
||||
examData.exam_notice = examData.exam_notice.join('\n')
|
||||
}
|
||||
} catch (error) {
|
||||
// 如果解析失败,保持原字符串不变
|
||||
console.error('解析考试须知失败:', error)
|
||||
}
|
||||
}
|
||||
|
||||
this.formData = examData
|
||||
this.dialogVisible = true
|
||||
},
|
||||
|
||||
// 处理保存考试
|
||||
async handleSaveExam () {
|
||||
try {
|
||||
// 将数据转换为普通对象
|
||||
const examData = { ...this.formData }
|
||||
|
||||
// 将考试须知字符串转换为数组并序列化
|
||||
if (examData.exam_notice) {
|
||||
const noticeArray = examData.exam_notice.split('\n').filter(item => item.trim() !== '')
|
||||
examData.exam_notice = JSON.stringify(noticeArray)
|
||||
}
|
||||
|
||||
if (this.isEdit) {
|
||||
// 修改为正确的API名称:examUpdate
|
||||
await window.electronAPI.examUpdate(examData.id, examData)
|
||||
this.$message.success('考试更新成功')
|
||||
} else {
|
||||
// 修改为正确的API名称:examCreate
|
||||
await window.electronAPI.examCreate(examData)
|
||||
this.$message.success('考试添加成功')
|
||||
}
|
||||
this.dialogVisible = false
|
||||
this.fetchExams() // 重新加载列表
|
||||
} catch (error) {
|
||||
this.$message.error('保存考试失败: ' + error.message)
|
||||
console.error('保存考试失败', error)
|
||||
}
|
||||
},
|
||||
|
||||
// 处理删除考试
|
||||
handleDeleteExam (id) {
|
||||
this.$confirm(
|
||||
'确定要删除该考试吗?',
|
||||
'确认删除',
|
||||
{
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
}
|
||||
)
|
||||
.then(async () => {
|
||||
try {
|
||||
await window.electronAPI.deleteExam(id)
|
||||
this.$message.success('考试删除成功')
|
||||
this.fetchExams() // 重新加载列表
|
||||
} catch (error) {
|
||||
this.$message.error('删除考试失败: ' + error.message)
|
||||
console.error('删除考试失败', error)
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
// 取消删除
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.exam-management-container {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.exam-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.exam-content {
|
||||
padding: 20px;
|
||||
background-color: #fff;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
/* 考试须知样式 */
|
||||
.exam-notice-cell {
|
||||
padding: 5px 0;
|
||||
}
|
||||
|
||||
.notice-list {
|
||||
max-height: 100px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.notice-item {
|
||||
padding: 2px 0;
|
||||
white-space: normal;
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
.ellipsis-cell {
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
</style>
|
Loading…
Reference in New Issue
Block a user