考生登录和ExamineeHome页

This commit is contained in:
chenqiang 2025-09-01 15:44:33 +08:00
parent fd2a4bc6eb
commit 3f61b5eb72
6 changed files with 657 additions and 119 deletions

View File

@ -0,0 +1,41 @@
<template>
<el-container class="examinee-layout-container">
<!-- Header -->
<Header />
<!-- Main content area -->
<el-main class="content-container">
<router-view />
</el-main>
<!-- Footer -->
<Footer />
</el-container>
</template>
<script>
export default {
name: 'ExamineeLayout',
components: {
Header: require('../common/Header.vue').default,
Footer: require('../common/Footer.vue').default,
ElContainer: require('element-ui').Container,
ElMain: require('element-ui').Main
}
}
</script>
<style scoped>
.examinee-layout-container,
.el-container {
height: 100vh;
display: flex;
flex-direction: column;
}
.content-container {
flex: 1;
padding: 20px;
overflow-y: auto;
}
</style>

View File

@ -52,7 +52,8 @@ contextBridge.exposeInMainWorld('electronAPI', {
questionRemove: (questionId) => ipcRenderer.invoke('question-remove', questionId),
// 添加新的questionDelete方法调用主进程中已注册的'question-delete'通道
questionDelete: (questionId) => ipcRenderer.invoke('question-delete', questionId),
questionGetStatistics: () => ipcRenderer.invoke('question-get-statistics'),
// 修改后
questionGetStatistics: () => ipcRenderer.invoke('question-get-count-and-score'),
questionGetQuestionById: (questionId) => ipcRenderer.invoke('question-get-question-by-id', questionId),
// 考试服务相关接口

View File

@ -2,11 +2,13 @@ import Vue from 'vue'
import VueRouter from 'vue-router'
import WelcomeView from '../views/WelcomeView.vue'
import AdminLayout from '../components/admin/AdminLayout.vue'
import ExamineeLayout from '../components/user/ExamineeLayout.vue'
import AdminHomeView from '../views/admin/AdminHomeView.vue'
import QuestionManagementView from '../views/admin/QuestionManagementView.vue'
import ExamineeManagementView from '../views/admin/ExamineeManagementView.vue'
// 添加考试管理组件导入
import ExamManagementView from '../views/admin/ExamManagementView.vue'
// 导入考生相关组件
import ExamineeHomeView from '../views/user/ExamineeHomeView.vue'
Vue.use(VueRouter)
@ -20,45 +22,81 @@ const routes = [
path: '/admin',
name: 'AdminLayout',
component: AdminLayout,
meta: {
requiresAuth: true
},
meta: { requiresAuth: true },
children: [
{
path: 'home',
name: 'AdminHome',
component: AdminHomeView
},
{
path: 'question',
name: 'QuestionManagement',
component: QuestionManagementView
},
{
path: 'examinee',
name: 'ExamineeManagement',
component: ExamineeManagementView
},
{
path: 'exam',
name: 'ExamManagement',
component: ExamManagementView
}
// 可以在这里添加更多子路由
{ path: 'home', name: 'AdminHome', component: AdminHomeView },
{ path: 'question', name: 'QuestionManagement', component: QuestionManagementView },
{ path: 'examinee', name: 'ExamineeManagement', component: ExamineeManagementView },
{ path: 'exam', name: 'ExamManagement', component: ExamManagementView }
]
},
// 添加考生相关路由
{
path: '/examinee',
name: 'Examinee',
component: ExamineeLayout,
meta: { requiresAuth: true },
children: [
{ path: 'home', name: 'ExamineeHome', component: ExamineeHomeView },
// 可以在这里添加更多考生相关的路由
]
}
]
const router = new VueRouter({
mode: 'hash', // 将history改为hash
base: process.env.BASE_URL,
routes
})
// 添加路由守卫
// 添加路由守卫,检查登录状态
router.beforeEach((to, from, next) => {
// 这里可以添加实际的认证逻辑
next()
const store = require('../store/index.js').default
// 检查路由是否需要认证
if (to.matched.some(record => record.meta.requiresAuth)) {
// 检查是否已登录
if (!store.state.isLoggedIn) {
// 如果未登录且不是去欢迎页,则重定向到欢迎页
if (to.path !== '/') {
next({
path: '/',
query: { redirect: to.fullPath }
})
} else {
next()
}
} else {
next()
}
} else {
// 不需要认证的路由直接通过
next()
}
})
// 添加路由守卫,检查登录状态
router.beforeEach((to, from, next) => {
const store = require('../store/index.js').default
// 检查路由是否需要认证
if (to.matched.some(record => record.meta.requiresAuth)) {
// 检查是否已登录
if (!store.state.isLoggedIn) {
// 如果未登录且不是去欢迎页,则重定向到欢迎页
if (to.path !== '/') {
next({
path: '/',
query: { redirect: to.fullPath }
})
} else {
next()
}
} else {
next()
}
} else {
// 不需要认证的路由直接通过
next()
}
})
export default router

View File

@ -6,16 +6,28 @@ Vue.use(Vuex)
export default new Vuex.Store({
state: {
examinee: null, // 保存考生信息
isLoggedIn: false // 登录状态
admin: null, // 保存管理员信息
isLoggedIn: false, // 通用登录状态
userType: null // 用户类型: 'examinee' 或 'admin'
},
mutations: {
setExaminee(state, examineeInfo) {
state.examinee = examineeInfo
state.admin = null
state.isLoggedIn = true
state.userType = 'examinee'
},
clearExaminee(state) {
setAdmin(state, adminInfo) {
state.admin = adminInfo
state.examinee = null
state.isLoggedIn = true
state.userType = 'admin'
},
clearUser(state) {
state.examinee = null
state.admin = null
state.isLoggedIn = false
state.userType = null
}
},
actions: {

View File

@ -7,14 +7,16 @@
<el-main>
<div class="d-flex align-items-center justify-content-center p-4" style="padding: 0; width: 600px;">
<!-- 数据库初始化提示卡片 -->
<div class="login-card bg-white rounded shadow-lg p-5 w-100 max-w-md" id="init-section" v-show="!isDatabaseInitialized">
<div class="login-card bg-white rounded shadow-lg p-5 w-100 max-w-md" id="init-section"
v-show="!isDatabaseInitialized">
<div class="text-center">
<div class="mb-6">
<i class="fa fa-database text-primary" style="font-size: 64px;"></i>
</div>
<h2 class="display-6 mb-4">系统未初始化</h2>
<p class="fs-5 mb-6 text-muted">请点击下方按钮进行系统初始化初始化完成后将自动显示登录界面</p>
<button id="initialize-db" @click="initializeDatabase" class="btn btn-primary px-8 py-3 fs-5" :disabled="isInitializing">
<button id="initialize-db" @click="initializeDatabase" class="btn btn-primary px-8 py-3 fs-5"
:disabled="isInitializing">
<i v-if="isInitializing" class="fa fa-spinner fa-spin me-2"></i>
<i v-else class="fa fa-refresh me-2"></i>
{{ isInitializing ? '初始化中...' : '数据初始化' }}
@ -23,17 +25,20 @@
</div>
<!-- Bootstrap登录卡片 -->
<div class="login-card bg-white rounded shadow-lg p-5 w-100 max-w-md" id="login-section" style="height: 500px;" v-show="isDatabaseInitialized">
<div class="login-card bg-white rounded shadow-lg p-5 w-100 max-w-md" id="login-section"
style="height: 500px;" v-show="isDatabaseInitialized">
<!-- 登录类型切换标签页 -->
<ul class="nav nav-tabs fs-4" id="loginTab" role="tablist">
<li class="nav-item flex-fill" role="presentation">
<button class="nav-link rounded-0 active w-100" id="exam-tab" data-toggle="tab" data-target="#exam-login" type="button" role="tab" aria-controls="exam-login" aria-selected="true">
<button class="nav-link rounded-0 active w-100" id="exam-tab" data-toggle="tab"
data-target="#exam-login" type="button" role="tab" aria-controls="exam-login" aria-selected="true">
<i class="fa fa-graduation-cap me-2"></i>
考生登录
</button>
</li>
<li class="nav-item flex-fill" role="presentation">
<button class="nav-link rounded-0 w-100" id="admin-tab" data-toggle="tab" data-target="#admin-login" type="button" role="tab" aria-controls="admin-login" aria-selected="false">
<button class="nav-link rounded-0 w-100" id="admin-tab" data-toggle="tab" data-target="#admin-login"
type="button" role="tab" aria-controls="admin-login" aria-selected="false">
<i class="fa fa-cog me-2"></i>
系统管理
</button>
@ -41,7 +46,8 @@
</ul>
<!-- 登录表单内容 -->
<div class="tab-content fs-5 p-4 border border-left border-right border-bottom" id="loginTabContent" style="height: calc(100% - 60px);">
<div class="tab-content fs-5 p-4 border border-left border-right border-bottom" id="loginTabContent"
style="height: calc(100% - 60px);">
<!-- 考生登录表单 -->
<div class="h-100 tab-pane fade show active" id="exam-login" role="tabpanel" aria-labelledby="exam-tab">
<form @submit.prevent="handleExamineeLogin" class="d-flex flex-column h-100">
@ -60,7 +66,8 @@
<span class="input-group-text">
<font-awesome-icon :icon="['fas', 'key']" />
</span>
<input type="text" class="form-control" id="examineeAdmissionTicket" v-model="examineeAdmissionTicket" required>
<input type="text" class="form-control" id="examineeAdmissionTicket"
v-model="examineeAdmissionTicket" required>
</div>
</div>
<div class="mt-4 flex-grow-1 d-flex flex-column justify-content-end">
@ -109,6 +116,8 @@
import Header from '../components/common/Header.vue'
import Footer from '../components/common/Footer.vue'
import { Message } from 'element-ui'
// Vuex store
import store from '../store/index.js'
export default {
name: 'WelcomeView',
@ -116,7 +125,7 @@ export default {
Header,
Footer
},
data() {
data () {
return {
examineeIdCard: '', //
examineeAdmissionTicket: '', //
@ -126,158 +135,179 @@ export default {
isLoading: false //
}
},
mounted() {
mounted () {
this.checkDatabaseStatus()
},
methods: {
async checkDatabaseStatus() {
async checkDatabaseStatus () {
try {
console.log('组件挂载 - 开始检查数据库初始化状态');
const initialized = await window.electronAPI.checkDatabaseInitialized();
console.log('组件挂载 - 数据库初始化状态检查完成:', initialized);
this.isDatabaseInitialized = initialized;
console.log('组件挂载 - 开始检查数据库初始化状态')
const initialized = await window.electronAPI.checkDatabaseInitialized()
console.log('组件挂载 - 数据库初始化状态检查完成:', initialized)
this.isDatabaseInitialized = initialized
} catch (error) {
console.error('检查数据库初始化状态失败:', error);
Message.error('检查数据库初始化状态失败,请重试');
console.error('检查数据库初始化状态失败:', error)
Message.error('检查数据库初始化状态失败,请重试')
}
},
async initializeDatabase() {
async initializeDatabase () {
try {
console.log('初始化数据库 - 开始');
this.isInitializing = true;
Message.info('开始初始化数据库...');
console.log('初始化数据库 - 开始')
this.isInitializing = true
Message.info('开始初始化数据库...')
const result = await window.electronAPI.initializeDatabase();
console.log('初始化数据库 - 结果:', result);
const result = await window.electronAPI.initializeDatabase()
console.log('初始化数据库 - 结果:', result)
// truesuccess
if (result === true || (result && result.success)) {
Message.success('数据库初始化成功!');
console.log('初始化数据库 - 成功,更新初始化状态');
this.isDatabaseInitialized = true;
Message.success('数据库初始化成功!')
console.log('初始化数据库 - 成功,更新初始化状态')
this.isDatabaseInitialized = true
} else {
const errorMessage = result && result.error ? result.error : '未知错误';
Message.error(`数据库初始化失败: ${errorMessage}`);
console.error('初始化数据库 - 失败:', errorMessage);
const errorMessage = result && result.error ? result.error : '未知错误'
Message.error(`数据库初始化失败: ${errorMessage}`)
console.error('初始化数据库 - 失败:', errorMessage)
}
} catch (error) {
console.error('数据库初始化失败:', error);
Message.error(`数据库初始化失败: ${error.message || '未知错误'}`);
console.error('数据库初始化失败:', error)
Message.error(`数据库初始化失败: ${error.message || '未知错误'}`)
} finally {
console.log('初始化数据库 - 结束');
this.isInitializing = false;
console.log('初始化数据库 - 结束')
this.isInitializing = false
}
},
async handleExamineeLogin() {
async handleExamineeLogin () {
console.log('考生登录 - 开始', {
examineeIdCard: this.examineeIdCard,
examineeAdmissionTicket: this.examineeAdmissionTicket
});
})
//
const idCard = this.examineeIdCard.trim();
const admissionTicket = this.examineeAdmissionTicket.trim();
const idCard = this.examineeIdCard.trim()
const admissionTicket = this.examineeAdmissionTicket.trim()
//
if (!idCard || !admissionTicket) {
console.warn('考生登录 - 验证失败: 身份证号和准考证号不能为空');
Message.error('请输入身份证号和准考证号');
return;
console.warn('考生登录 - 验证失败: 身份证号和准考证号不能为空')
Message.error('请输入身份证号和准考证号')
return
}
//
this.isLoading = true;
this.isLoading = true
try {
// API
const result = await window.electronAPI.userLogin(idCard, admissionTicket);
console.log(result);
// API - 使
const result = await window.electronAPI.userLogin({ idCard, admissionTicket })
console.log('考生登录 - 结果:', result)
if (result && result.id) {
console.log('考生登录 - 成功', result);
// store - Vue 2使Vuex
if (this.$store && this.$store.commit) {
this.$store.commit('setExaminee', result);
}
Message.success('登录成功');
console.log('考生登录 - 成功', result)
// store
this.$store.commit('setExaminee', result)
Message.success('登录成功')
//
this.$router.push('/examinee/home');
this.$router.push('/examinee/home')
} else {
console.warn('考生登录 - 失败:', result);
Message.error(result.error || '登录失败,请检查身份证号和准考证号');
console.warn('考生登录 - 失败:', result)
Message.error(result && result.error ? result.error : '登录失败,请检查身份证号和准考证号')
}
} catch (error) {
console.error('考生登录 - 异常:', error);
Message.error('登录失败,请重试');
console.error('考生登录 - 异常:', error)
Message.error(`登录失败: ${error.message || '未知错误'}`)
} finally {
//
this.isLoading = false;
this.isLoading = false
}
},
async handleAdminLogin() {
console.log('管理员登录 - 开始', { passwordLength: this.adminPassword.length });
// handleAdminLogin
async handleAdminLogin () {
console.log('管理员登录 - 开始', { passwordLength: this.adminPassword.length })
//
const passwordError = this.validateAdminPassword(this.adminPassword);
const passwordError = this.validateAdminPassword(this.adminPassword)
if (passwordError) {
console.warn('管理员登录 - 验证失败:', passwordError);
const errorElement = document.getElementById('admin-error-message');
console.warn('管理员登录 - 验证失败:', passwordError)
const errorElement = document.getElementById('admin-error-message')
if (errorElement) {
errorElement.textContent = passwordError;
errorElement.style.display = 'block';
errorElement.textContent = passwordError
errorElement.style.display = 'block'
}
return;
return
}
//
const errorElement = document.getElementById('admin-error-message');
const errorElement = document.getElementById('admin-error-message')
if (errorElement) {
errorElement.style.display = 'none';
errorElement.style.display = 'none'
}
try {
console.log('管理员登录 - 调用主进程登录方法');
console.log('管理员登录 - 调用主进程登录方法')
// 使adminLogin
const result = await window.electronAPI.adminLogin({
username: 'admin',
password: this.adminPassword
});
console.log('管理员登录 - 登录结果:', result);
})
console.log('管理员登录 - 登录结果:', result)
if (result && result.success) {
console.log('管理员登录 - 成功,跳转到管理首页');
Message.success('登录成功');
this.$router.push('/admin/home');
console.log('管理员登录 - 成功更新store状态并跳转')
// 使setAdmin mutation
this.$store.commit('setAdmin', { username: 'admin' })
Message.success('登录成功')
this.$router.push('/admin/home')
} else {
const errorMessage = result && result.message ? result.message : '登录失败';
console.warn('管理员登录 - 失败:', errorMessage);
Message.error(errorMessage);
const errorMessage = result && result.message ? result.message : '登录失败'
console.warn('管理员登录 - 失败:', errorMessage)
Message.error(errorMessage)
}
} catch (error) {
console.error('管理员登录 - 异常:', error);
Message.error(`登录异常: ${error.message || '未知错误'}`);
console.error('管理员登录 - 异常:', error)
Message.error(`登录异常: ${error.message || '未知错误'}`)
}
},
validateAdminPassword(password) {
validateAdminPassword (password) {
//
if (!password) {
return '请输入管理员密码';
return '请输入管理员密码'
}
//
if (password.length < 4 || password.length > 32) {
return '密码长度必须在4-32个字符之间';
return '密码长度必须在4-32个字符之间'
}
//
const regex = /^[A-Za-z0-9]+$/;
const regex = /^[A-Za-z0-9]+$/
if (!regex.test(password)) {
return '密码只能包含英文大小写字母和数字';
return '密码只能包含英文大小写字母和数字'
}
return null;
}
return null
},
// exitExam
async exitExam () {
this.$confirm(
'确定要退出考试吗?',
'退出确认',
{
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}
).then(() => {
// store
this.$store.commit('clearUser')
//
this.$router.push('/')
}).catch(() => {
//
console.log('用户取消退出')
})
},
}
}
</script>
@ -311,9 +341,12 @@ export default {
.main-background {
background-image: url('../assets/bg.jpeg');
background-size: 100% 100%; /* 拉伸背景图以填满容器 */
background-position: center; /* 保持居中 */
background-repeat: no-repeat; /* 避免重复 */
background-size: 100% 100%;
/* 拉伸背景图以填满容器 */
background-position: center;
/* 保持居中 */
background-repeat: no-repeat;
/* 避免重复 */
}
/* 适配移动设备 */

View File

@ -0,0 +1,413 @@
<template>
<!-- 添加外层容器确保占满整个空间 -->
<div class="student-home-container">
<div class="content-wrapper">
<!-- 考试须知卡片 -->
<div class="exam-card rounded-lg shadow-lg p-4 border-2">
<div class="flex justify-between text-center mb-2">
<h2 class="text-2xl font-bold text-primary">考生信息</h2>
</div>
<!-- 考生信息部分 -->
<el-descriptions class="margin-top" :column="3" :size="size" border>
<el-descriptions-item>
<template slot="label">
<div class="cell-item">
<i class="fa fa-user mr-2" :style="iconStyle"></i>
姓名
</div>
</template>
{{ examinee && examinee.examinee_name }}
</el-descriptions-item>
<el-descriptions-item>
<template slot="label">
<div class="cell-item">
<i class="fa fa-id-card mr-2" :style="iconStyle"></i>
身份证号
</div>
</template>
{{ formatIdCard(examinee && examinee.examinee_id_card) }}
</el-descriptions-item>
<el-descriptions-item>
<template slot="label">
<div class="cell-item">
<i class="fa fa-ticket mr-2" :style="iconStyle"></i>
准考证号
</div>
</template>
{{ examinee && examinee.examinee_admission_ticket }}
</el-descriptions-item>
</el-descriptions>
<el-divider />
<div class="flex justify-between text-center mt-4 mb-2">
<h2 class="text-2xl font-bold text-primary">考试信息</h2>
</div>
<div class="space-y-4">
<el-descriptions class="margin-top" :column="2" :size="size" border>
<el-descriptions-item>
<template slot="label">
<div class="cell-item">
<i class="fa fa-clock-o mr-2" :style="iconStyle"></i>
考试时长
</div>
</template>
{{ lastExam && lastExam.exam_minutes }}分钟
</el-descriptions-item>
<el-descriptions-item>
<template slot="label">
<div class="cell-item">
<i class="fa fa-hourglass-half mr-2" :style="iconStyle"></i>
最短考试时长
</div>
</template>
{{ lastExam && lastExam.exam_minutes_min }}分钟
</el-descriptions-item>
<el-descriptions-item>
<template slot="label">
<div class="cell-item">
<i class="fa fa-list-ol mr-2" :style="iconStyle"></i>
考题数量
</div>
</template>
<span class="text-gray-500">{{ totalQuestions }}</span>
</el-descriptions-item>
<el-descriptions-item>
<template slot="label">
<div class="cell-item">
<i class="fa fa-list-ol mr-2" :style="iconStyle"></i>
考试总分
</div>
</template>
<span class="text-gray-500">{{ totalScore }}</span>
</el-descriptions-item>
</el-descriptions>
</div>
<el-divider />
<div class="flex justify-between text-center mt-4 mb-4">
<h2 class="text-2xl font-bold text-primary">考试须知</h2>
</div>
<div class="space-y-4">
<div v-if="!examLoading && examNotices.length > 0" class="space-y-2 pl-4">
<ol class="list-decimal pl-4 space-y-2">
<li v-for="(notice, index) in examNotices" :key="index" class="text-gray-700">
{{ notice }}
</li>
</ol>
</div>
<div v-else-if="examLoading" class="text-center text-gray-500 py-4">
加载考试信息中...
</div>
<div v-else class="text-center text-gray-500 py-4">
暂无考试须知
</div>
</div>
<div class="text-center mt-6">
<el-button type="info" @click="exitExam">返回</el-button>
<el-button type="primary" @click="startExam" :loading="isLoading">开始考试</el-button>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
name: 'ExamineeHomeView',
components: {
ElDescriptions: require('element-ui').Descriptions,
ElDescriptionsItem: require('element-ui').DescriptionsItem,
ElDivider: require('element-ui').Divider,
ElButton: require('element-ui').Button
},
data() {
return {
isLoading: false,
size: 'default',
lastExam: null,
examLoading: true,
examNotices: [],
totalQuestions: 0,
totalScore: 0
}
},
computed: {
examinee() {
return this.$store.state.examinee
},
isLoggedIn() {
return this.$store.state.isLoggedIn
},
iconStyle() {
const marginMap = {
large: '8px',
default: '6px',
small: '4px'
}
return {
marginRight: marginMap[this.size] || marginMap.default
}
}
},
watch: {
lastExam(newValue) {
if (newValue) {
this.examLoading = false
}
},
isLoggedIn(newVal) {
if (!newVal) {
//
this.$router.push('/')
}
}
},
mounted() {
//
if (!this.isLoggedIn) {
this.$router.push('/')
return
}
this.fetchLastExam()
this.getQuestionsCountAndScore()
},
methods: {
//
// 11-15*
formatIdCard(idCard) {
if (!idCard || idCard.length !== 18) return idCard
return idCard.substring(0, 9) + '*****' + idCard.substring(14)
},
//
formatExamDuration(minutes) {
if (!minutes) return '未知'
const hours = Math.floor(minutes / 60)
const mins = minutes % 60
return `${hours}小时${mins}分钟`
},
//
async getQuestionsCountAndScore() {
try {
//
//
const result = await window.electronAPI.questionGetStatistics()
this.totalQuestions = result.totalQuestions || 0
this.totalScore = result.totalScore || 0
} catch (error) {
console.error('获取考题数量和总分失败:', error)
this.$message.error(`获取考题数量和总分失败: ${error.message || '未知错误'}`)
}
},
//
async fetchLastExam() {
this.examLoading = true
try {
// Electron API
const examData = await window.electronAPI.examFetchLast()
this.lastExam = examData
//
if (this.lastExam && this.lastExam.exam_notice) {
try {
// JSON
if (typeof this.lastExam.exam_notice === 'string') {
this.examNotices = JSON.parse(this.lastExam.exam_notice)
} else if (Array.isArray(this.lastExam.exam_notice)) {
this.examNotices = this.lastExam.exam_notice
} else {
this.examNotices = []
}
} catch (error) {
console.warn('解析考试须知失败,使用原始数据:', error)
this.examNotices = [this.lastExam.exam_notice]
}
} else {
this.examNotices = []
}
} catch (error) {
console.error('获取考试信息失败:', error)
this.$message.error(`获取考试信息失败: ${error.message || '未知错误'}`)
} finally {
this.examLoading = false
}
},
// 退
exitExam() {
this.$confirm(
'确定要退出考试吗?',
'退出确认',
{
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}
).then(() => {
// store
this.$store.commit('clearExaminee')
//
this.$router.push('/')
}).catch(() => {
//
console.log('用户取消退出')
})
},
//
async startExam() {
if (!this.lastExam) {
this.$message.warning('请先获取考试信息')
return
}
if (!this.examinee) {
this.$message.warning('未获取到考生信息')
return
}
this.isLoading = true
try {
console.log('开始生成试卷...')
//
const examineeData = {
id: this.examinee.id,
examinee_name: this.examinee.examinee_name || '',
examinee_id_card: this.examinee.examinee_id_card || '',
examinee_admission_ticket: this.examinee.examinee_admission_ticket || '',
examinee_gender: this.examinee.examinee_gender || '',
examinee_unit: this.examinee.examinee_unit || '',
written_exam_room: this.examinee.written_exam_room || '',
written_exam_seat: this.examinee.written_exam_seat || '',
computer_exam_room: this.examinee.computer_exam_room || '',
computer_exam_seat: this.examinee.computer_exam_seat || ''
}
// 使JSON/
const examData = JSON.parse(JSON.stringify({
id: this.lastExam.id,
exam_name: this.lastExam.exam_name || '',
exam_description: this.lastExam.exam_description || '',
exam_minutes: this.lastExam.exam_minutes || 0,
exam_minutes_min: this.lastExam.exam_minutes_min || 0,
exam_notice: this.lastExam.exam_notice || []
}))
// API
const result = await window.electronAPI.examingGeneratePaper({
examineeId: examineeData.id,
examId: examData.id
})
if (result && result.success) {
console.log('生成试卷成功:', result)
// Vuex storesetPaper
this.$alert(
'已完成组卷,点击"进入考试"开始答题',
'组卷完成',
{
confirmButtonText: '进入考试',
type: 'success'
}
).then(() => {
// 使
this.$router.push({
name: 'examinee-examing',
params: { paperId: result.data.id }
})
})
} else {
console.error('生成试卷失败:', result)
this.$message.error(`生成试卷失败: ${result.message || '未知错误'}`)
}
} catch (error) {
console.error('生成试卷异常:', error)
this.$message.error(`无法生成试卷: ${error.message || '未知错误'}`)
} finally {
this.isLoading = false
}
}
}
}
</script>
<style scoped>
/* 自定义样式 */
.bg-primary {
background-color: #1E88E5 !important;
/* 蓝色主题与WelcomeView保持一致 */
}
.text-primary {
color: #1E88E5 !important;
}
/* 确保容器占满可用空间 */
.student-home-container {
height: 100%;
min-height: 100vh;
display: flex;
flex-direction: column;
}
/* 内容包装器,用于居中卡片并使其可扩展 */
.content-wrapper {
flex: 1;
width: 100%;
display: flex;
align-items: center;
justify-content: center;
padding: 1rem;
box-sizing: border-box;
}
/* 考试卡片样式 - 响应式设计 */
.exam-card {
width: 100%;
min-height: calc(100vh - 4rem);
background-color: white;
display: flex;
flex-direction: column;
box-sizing: border-box;
}
/* 适配中小屏幕 */
@media (max-width: 768px) {
.exam-card {
max-width: 100%;
min-height: calc(100vh - 2rem);
}
.content-wrapper {
padding: 0.5rem;
}
}
/* 适配大屏幕 - 适当增加最大宽度但保留边距 */
@media (min-width: 1200px) {
.exam-card {
max-width: 80vw;
max-height: 90vh;
}
}
/* 适配超大屏幕 - 进一步增加最大宽度 */
@media (min-width: 1600px) {
.exam-card {
max-width: 90vw;
max-height: 90vh;
}
}
.cell-item {
display: flex;
align-items: center;
}
.margin-top {
margin-top: 20px !important;
}
</style>