后台试卷管理完成
This commit is contained in:
parent
7c9fdc8d6f
commit
1db2e19fe8
289
background/db/paper.js
Normal file
289
background/db/paper.js
Normal file
@ -0,0 +1,289 @@
|
||||
const { getDbConnection } = require('./index.js');
|
||||
// 添加缺少的getUserDbPath导入
|
||||
const { getUserDbPath } = require('./path.js');
|
||||
|
||||
/**
|
||||
* 查询所有考生试卷记录
|
||||
* @returns {Promise<Array>} 试卷记录数组
|
||||
*/
|
||||
exports.getAllExamineePapers = async function getAllExamineePapers() {
|
||||
try {
|
||||
const db = await getDbConnection(getUserDbPath());
|
||||
const sql = `
|
||||
SELECT
|
||||
ep.*,
|
||||
e.examinee_name,
|
||||
e.examinee_id_card,
|
||||
e.examinee_admission_ticket,
|
||||
|
||||
-- 关联试卷问题
|
||||
GROUP_CONCAT(DISTINCT pq.question_name) as question_names,
|
||||
|
||||
-- 计算选择题总数
|
||||
(SELECT COUNT(*) FROM question_choices qc
|
||||
JOIN paper_questions pq1 ON qc.question_id = pq1.id
|
||||
WHERE pq1.paper_id = ep.id) as total_choices,
|
||||
|
||||
-- 计算填空题总数
|
||||
(SELECT COUNT(*) FROM question_fill_blanks qfb
|
||||
JOIN paper_questions pq1 ON qfb.question_id = pq1.id
|
||||
WHERE pq1.paper_id = ep.id) as total_fill_blanks,
|
||||
|
||||
-- 计算已回答的选择题数
|
||||
(SELECT COUNT(*) FROM question_choices qc
|
||||
JOIN paper_questions pq1 ON qc.question_id = pq1.id
|
||||
WHERE pq1.paper_id = ep.id AND qc.examinee_answers != '') as answered_choices,
|
||||
|
||||
-- 计算已回答的填空题数
|
||||
(SELECT COUNT(*) FROM question_fill_blanks qfb
|
||||
JOIN paper_questions pq1 ON qfb.question_id = pq1.id
|
||||
WHERE pq1.paper_id = ep.id AND qfb.examinee_answers != '') as answered_fill_blanks
|
||||
|
||||
FROM examinee_papers ep
|
||||
LEFT JOIN examinee e ON ep.examinee_id = e.id
|
||||
LEFT JOIN paper_questions pq ON ep.id = pq.paper_id
|
||||
GROUP BY ep.id
|
||||
ORDER BY ep.paper_start_time DESC
|
||||
`;
|
||||
const papers = await db.allAsync(sql);
|
||||
return papers;
|
||||
} catch (error) {
|
||||
console.error('查询所有考生试卷记录失败:', error);
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 根据试卷ID查询试卷详情
|
||||
* @param {number} paperId - 试卷ID
|
||||
* @returns {Promise<Object|null>} 试卷详情对象
|
||||
*/
|
||||
exports.getExamineePaperById = async function getExamineePaperById(paperId) {
|
||||
try {
|
||||
const db = await getDbConnection(getUserDbPath());
|
||||
const sql = `
|
||||
SELECT
|
||||
ep.*,
|
||||
e.examinee_name,
|
||||
e.examinee_id_card,
|
||||
e.examinee_admission_ticket,
|
||||
|
||||
-- 获取试卷问题详情
|
||||
pq.*,
|
||||
|
||||
-- 获取选择题详情
|
||||
qc.id as choice_id,
|
||||
qc.question_id,
|
||||
qc.choice_description,
|
||||
qc.choice_type,
|
||||
qc.choice_options,
|
||||
qc.correct_answers,
|
||||
qc.examinee_answers,
|
||||
qc.score as choice_score,
|
||||
qc.score_real as choice_score_real,
|
||||
|
||||
-- 获取填空题详情
|
||||
qfb.id as blank_id,
|
||||
qfb.question_id as blank_question_id,
|
||||
qfb.blank_description,
|
||||
qfb.blank_count,
|
||||
qfb.correct_answers as blank_correct_answers,
|
||||
qfb.examinee_answers as blank_examinee_answers,
|
||||
qfb.score as blank_score,
|
||||
qfb.score_real as blank_score_real
|
||||
|
||||
FROM examinee_papers ep
|
||||
LEFT JOIN examinee e ON ep.examinee_id = e.id
|
||||
LEFT JOIN paper_questions pq ON ep.id = pq.paper_id
|
||||
LEFT JOIN question_choices qc ON pq.id = qc.question_id
|
||||
LEFT JOIN question_fill_blanks qfb ON pq.id = qfb.question_id
|
||||
WHERE ep.id = ?
|
||||
`;
|
||||
const papers = await db.allAsync(sql, [paperId]);
|
||||
|
||||
// 格式化结果,将问题组织到paper对象中
|
||||
if (papers.length > 0) {
|
||||
const paper = {
|
||||
...papers[0],
|
||||
questions: [],
|
||||
// 直接添加question_choices和question_fill_blanks数组,方便前端使用
|
||||
question_choices: [],
|
||||
question_fill_blanks: []
|
||||
};
|
||||
|
||||
// 提取唯一的问题并添加相关的选择题和填空题
|
||||
const questionMap = new Map();
|
||||
|
||||
papers.forEach(row => {
|
||||
if (row.question_name && !questionMap.has(row.question_id)) {
|
||||
questionMap.set(row.question_id, {
|
||||
id: row.question_id,
|
||||
question_name: row.question_name,
|
||||
question_description: row.question_description,
|
||||
question_type: row.question_type,
|
||||
question_choices: [],
|
||||
question_fill_blanks: []
|
||||
});
|
||||
}
|
||||
|
||||
// 添加选择题
|
||||
if (row.choice_id) {
|
||||
const question = questionMap.get(row.question_id);
|
||||
const choiceData = {
|
||||
id: row.choice_id,
|
||||
question_id: row.question_id,
|
||||
question_content: row.question_description,
|
||||
option_a: row.choice_options?.split('|')[0] || '',
|
||||
option_b: row.choice_options?.split('|')[1] || '',
|
||||
option_c: row.choice_options?.split('|')[2] || '',
|
||||
option_d: row.choice_options?.split('|')[3] || '',
|
||||
correct_answer: row.correct_answers,
|
||||
user_answer: row.examinee_answers,
|
||||
score: row.choice_score,
|
||||
user_score: row.choice_score_real
|
||||
};
|
||||
// 添加检查,确保question存在再push到question.question_choices
|
||||
if (question) {
|
||||
question.question_choices.push(choiceData);
|
||||
}
|
||||
// 同时添加到顶层的question_choices数组
|
||||
paper.question_choices.push(choiceData);
|
||||
}
|
||||
|
||||
// 添加填空题
|
||||
if (row.blank_id) {
|
||||
const question = questionMap.get(row.blank_question_id);
|
||||
const blankData = {
|
||||
id: row.blank_id,
|
||||
question_id: row.blank_question_id,
|
||||
question_content: row.question_description,
|
||||
blank_count: row.blank_count,
|
||||
correct_answers: row.blank_correct_answers ? JSON.parse(row.blank_correct_answers) : [],
|
||||
user_answers: row.blank_examinee_answers ? JSON.parse(row.blank_examinee_answers) : [],
|
||||
score: row.blank_score,
|
||||
user_score: row.blank_score_real
|
||||
};
|
||||
if (question) {
|
||||
question.question_fill_blanks.push(blankData);
|
||||
}
|
||||
// 同时添加到顶层的question_fill_blanks数组
|
||||
paper.question_fill_blanks.push(blankData);
|
||||
}
|
||||
});
|
||||
|
||||
paper.questions = Array.from(questionMap.values());
|
||||
return paper;
|
||||
}
|
||||
|
||||
return null;
|
||||
} catch (error) {
|
||||
console.error(`根据ID查询试卷详情失败 (ID: ${paperId}):`, error);
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 查询考生的试卷记录
|
||||
* @param {number} examineeId - 考生ID
|
||||
* @returns {Promise<Array>} 该考生的试卷记录数组
|
||||
*/
|
||||
exports.getExamineePapersByExamineeId = async function getExamineePapersByExamineeId(examineeId) {
|
||||
try {
|
||||
const db = await getDbConnection(getUserDbPath());
|
||||
const sql = `
|
||||
SELECT
|
||||
ep.*,
|
||||
e.examinee_name,
|
||||
e.examinee_id_card,
|
||||
e.examinee_admission_ticket,
|
||||
|
||||
-- 关联试卷问题
|
||||
GROUP_CONCAT(DISTINCT pq.question_name) as question_names,
|
||||
|
||||
-- 计算选择题总数
|
||||
(SELECT COUNT(*) FROM question_choices qc
|
||||
JOIN paper_questions pq1 ON qc.question_id = pq1.id
|
||||
WHERE pq1.paper_id = ep.id) as total_choices,
|
||||
|
||||
-- 计算填空题总数
|
||||
(SELECT COUNT(*) FROM question_fill_blanks qfb
|
||||
JOIN paper_questions pq1 ON qfb.question_id = pq1.id
|
||||
WHERE pq1.paper_id = ep.id) as total_fill_blanks,
|
||||
|
||||
-- 计算已回答的选择题数
|
||||
(SELECT COUNT(*) FROM question_choices qc
|
||||
JOIN paper_questions pq1 ON qc.question_id = pq1.id
|
||||
WHERE pq1.paper_id = ep.id AND qc.examinee_answers != '') as answered_choices,
|
||||
|
||||
-- 计算已回答的填空题数
|
||||
(SELECT COUNT(*) FROM question_fill_blanks qfb
|
||||
JOIN paper_questions pq1 ON qfb.question_id = pq1.id
|
||||
WHERE pq1.paper_id = ep.id AND qfb.examinee_answers != '') as answered_fill_blanks
|
||||
|
||||
FROM examinee_papers ep
|
||||
LEFT JOIN examinee e ON ep.examinee_id = e.id
|
||||
LEFT JOIN paper_questions pq ON ep.id = pq.paper_id
|
||||
WHERE ep.examinee_id = ?
|
||||
GROUP BY ep.id
|
||||
ORDER BY ep.paper_start_time DESC
|
||||
`;
|
||||
const papers = await db.allAsync(sql, [examineeId]);
|
||||
return papers;
|
||||
} catch (error) {
|
||||
console.error(`查询考生试卷记录失败 (ExamineeID: ${examineeId}):`, error);
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 根据状态查询试卷记录
|
||||
* @param {number} status - 试卷状态
|
||||
* @returns {Promise<Array>} 符合条件的试卷记录数组
|
||||
*/
|
||||
exports.getExamineePapersByStatus = async function getExamineePapersByStatus(status) {
|
||||
try {
|
||||
const db = await getDbConnection(getUserDbPath());
|
||||
const sql = `
|
||||
SELECT
|
||||
ep.*,
|
||||
e.examinee_name,
|
||||
e.examinee_id_card,
|
||||
e.examinee_admission_ticket,
|
||||
|
||||
-- 关联试卷问题
|
||||
GROUP_CONCAT(DISTINCT pq.question_name) as question_names,
|
||||
|
||||
-- 计算选择题总数
|
||||
(SELECT COUNT(*) FROM question_choices qc
|
||||
JOIN paper_questions pq1 ON qc.question_id = pq1.id
|
||||
WHERE pq1.paper_id = ep.id) as total_choices,
|
||||
|
||||
-- 计算填空题总数
|
||||
(SELECT COUNT(*) FROM question_fill_blanks qfb
|
||||
JOIN paper_questions pq1 ON qfb.question_id = pq1.id
|
||||
WHERE pq1.paper_id = ep.id) as total_fill_blanks,
|
||||
|
||||
-- 计算已回答的选择题数
|
||||
(SELECT COUNT(*) FROM question_choices qc
|
||||
JOIN paper_questions pq1 ON qc.question_id = pq1.id
|
||||
WHERE pq1.paper_id = ep.id AND qc.examinee_answers != '') as answered_choices,
|
||||
|
||||
-- 计算已回答的填空题数
|
||||
(SELECT COUNT(*) FROM question_fill_blanks qfb
|
||||
JOIN paper_questions pq1 ON qfb.question_id = pq1.id
|
||||
WHERE pq1.paper_id = ep.id AND qfb.examinee_answers != '') as answered_fill_blanks
|
||||
|
||||
FROM examinee_papers ep
|
||||
LEFT JOIN examinee e ON ep.examinee_id = e.id
|
||||
LEFT JOIN paper_questions pq ON ep.id = pq.paper_id
|
||||
WHERE ep.paper_status = ?
|
||||
GROUP BY ep.id
|
||||
ORDER BY ep.paper_start_time DESC
|
||||
`;
|
||||
const papers = await db.allAsync(sql, [status]);
|
||||
return papers;
|
||||
} catch (error) {
|
||||
console.error(`根据状态查询试卷记录失败 (Status: ${status}):`, error);
|
||||
throw error;
|
||||
}
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
"use strict";
|
||||
|
||||
import { app, protocol, BrowserWindow, ipcMain, dialog } from "electron";
|
||||
import { createProtocol } from "vue-cli-plugin-electron-builder/lib";
|
||||
const { app, protocol, BrowserWindow, ipcMain, dialog, shell } = require("electron");
|
||||
const { createProtocol } = require("vue-cli-plugin-electron-builder/lib");
|
||||
// 替换argon2为bcryptjs
|
||||
const bcrypt = require("bcryptjs");
|
||||
const isDevelopment = process.env.NODE_ENV !== "production";
|
||||
@ -31,6 +31,8 @@ const { initExamineeIpc } = require("./service/examineeService.js");
|
||||
const { initExamingIpc } = require("./service/examingService.js");
|
||||
// 导入文件服务
|
||||
const { initFileIpc } = require("./service/fileService.js");
|
||||
// 试卷
|
||||
const { initPaperIpc } = require("./service/paperService.js");
|
||||
|
||||
// Scheme must be registered before the app is ready
|
||||
protocol.registerSchemesAsPrivileged([
|
||||
@ -210,6 +212,8 @@ app.on("ready", async () => {
|
||||
initExamingIpc(ipcMain);
|
||||
// 初始化文件相关的IPC处理程序
|
||||
initFileIpc(ipcMain);
|
||||
// 初始化试卷相关的IPC处理程序
|
||||
initPaperIpc(ipcMain);
|
||||
|
||||
// 检查数据库是否初始化
|
||||
try {
|
||||
@ -366,3 +370,23 @@ ipcMain.handle("get-database-paths", (event) => {
|
||||
return { systemDbPath: '获取失败', userDbPath: '获取失败' };
|
||||
}
|
||||
});
|
||||
|
||||
// 在现有的IPC处理程序后面添加
|
||||
ipcMain.handle("file-open-file", async (event, filePath) => {
|
||||
try {
|
||||
console.log('尝试打开文件:', filePath);
|
||||
// 使用shell.openPath打开文件,这会使用系统默认应用打开指定文件
|
||||
const result = await shell.openPath(filePath);
|
||||
// 在Windows上,result是打开的文件路径;在macOS和Linux上,成功时返回空字符串
|
||||
if (process.platform === 'win32' || result === '') {
|
||||
console.log('文件打开成功:', filePath);
|
||||
return { success: true, message: '文件打开成功' };
|
||||
} else {
|
||||
console.error('文件打开失败:', result);
|
||||
return { success: false, message: result };
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('打开文件时发生错误:', error);
|
||||
return { success: false, message: error.message };
|
||||
}
|
||||
});
|
||||
|
@ -160,16 +160,23 @@ contextBridge.exposeInMainWorld("electronAPI", {
|
||||
ipcRenderer.invoke("file-generate-pdf", pdfData, fileName),
|
||||
fileCopyToDesktop: (filePath) =>
|
||||
ipcRenderer.invoke("file-copy-to-desktop", filePath),
|
||||
// 试卷管理相关接口
|
||||
paperGetAll: () => ipcRenderer.invoke('paper-get-all'),
|
||||
paperGetById: (paperId) => ipcRenderer.invoke('paper-get-by-id', paperId),
|
||||
paperGetByExamineeId: (examineeId) => ipcRenderer.invoke('paper-get-by-examinee-id', examineeId),
|
||||
paperGetByStatus: (status) => ipcRenderer.invoke('paper-get-by-status', status),
|
||||
|
||||
// 新增:获取可执行文件路径的API
|
||||
getExePath: () => ipcRenderer.invoke("get-exe-path"),
|
||||
// 添加文件打开API
|
||||
fileOpenFile: (filePath) => ipcRenderer.invoke("file-open-file", filePath),
|
||||
|
||||
// 新增:获取数据库路径的API
|
||||
getDatabasePaths: () => ipcRenderer.invoke("get-database-paths"),
|
||||
// 保留原有的ipcRenderer接口,确保兼容性
|
||||
ipcRenderer: {
|
||||
invoke: (channel, data) => ipcRenderer.invoke(channel, data),
|
||||
},
|
||||
// 新增:获取可执行文件路径的API
|
||||
getExePath: () => ipcRenderer.invoke("get-exe-path"),
|
||||
|
||||
// 新增:获取数据库路径的API
|
||||
getDatabasePaths: () => ipcRenderer.invoke("get-database-paths"),
|
||||
});
|
||||
|
||||
// 也保留原来的electron对象,确保现有功能正常
|
||||
|
172
background/service/paperService.js
Normal file
172
background/service/paperService.js
Normal file
@ -0,0 +1,172 @@
|
||||
const {
|
||||
getAllExamineePapers,
|
||||
getExamineePaperById,
|
||||
getExamineePapersByExamineeId,
|
||||
getExamineePapersByStatus
|
||||
} = require('../db/paper.js');
|
||||
|
||||
/**
|
||||
* 服务层:查询所有考生试卷记录
|
||||
* @returns {Promise<Object>} 包含状态、消息和数据的对象
|
||||
*/
|
||||
exports.getAllExamineePapersService = async function getAllExamineePapersService() {
|
||||
try {
|
||||
const papers = await getAllExamineePapers();
|
||||
return {
|
||||
success: true,
|
||||
message: '查询成功',
|
||||
data: papers
|
||||
};
|
||||
} catch (error) {
|
||||
console.error('服务层: 查询所有考生试卷记录失败', error);
|
||||
return {
|
||||
success: false,
|
||||
message: `查询失败: ${error.message}`,
|
||||
data: []
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 服务层:根据试卷ID查询试卷详情
|
||||
* @param {number} paperId - 试卷ID
|
||||
* @returns {Promise<Object>} 包含状态、消息和数据的对象
|
||||
*/
|
||||
exports.getExamineePaperByIdService = async function getExamineePaperByIdService(paperId) {
|
||||
try {
|
||||
if (!paperId || paperId <= 0) {
|
||||
throw new Error('试卷ID必须为正整数');
|
||||
}
|
||||
const paper = await getExamineePaperById(paperId);
|
||||
if (!paper) {
|
||||
return {
|
||||
success: false,
|
||||
message: '未找到该试卷记录',
|
||||
data: null
|
||||
};
|
||||
}
|
||||
return {
|
||||
success: true,
|
||||
message: '查询成功',
|
||||
data: paper
|
||||
};
|
||||
} catch (error) {
|
||||
console.error('服务层: 根据ID查询试卷详情失败', error);
|
||||
return {
|
||||
success: false,
|
||||
message: `查询失败: ${error.message}`,
|
||||
data: null
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 服务层:查询考生的试卷记录
|
||||
* @param {number} examineeId - 考生ID
|
||||
* @returns {Promise<Object>} 包含状态、消息和数据的对象
|
||||
*/
|
||||
exports.getExamineePapersByExamineeIdService = async function getExamineePapersByExamineeIdService(examineeId) {
|
||||
try {
|
||||
if (!examineeId || examineeId <= 0) {
|
||||
throw new Error('考生ID必须为正整数');
|
||||
}
|
||||
const papers = await getExamineePapersByExamineeId(examineeId);
|
||||
return {
|
||||
success: true,
|
||||
message: '查询成功',
|
||||
data: papers
|
||||
};
|
||||
} catch (error) {
|
||||
console.error('服务层: 查询考生试卷记录失败', error);
|
||||
return {
|
||||
success: false,
|
||||
message: `查询失败: ${error.message}`,
|
||||
data: []
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 服务层:根据状态查询试卷记录
|
||||
* @param {number} status - 试卷状态
|
||||
* @returns {Promise<Object>} 包含状态、消息和数据的对象
|
||||
*/
|
||||
exports.getExamineePapersByStatusService = async function getExamineePapersByStatusService(status) {
|
||||
try {
|
||||
const papers = await getExamineePapersByStatus(status);
|
||||
return {
|
||||
success: true,
|
||||
message: '查询成功',
|
||||
data: papers
|
||||
};
|
||||
} catch (error) {
|
||||
console.error('服务层: 根据状态查询试卷记录失败', error);
|
||||
return {
|
||||
success: false,
|
||||
message: `查询失败: ${error.message}`,
|
||||
data: []
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 注册试卷管理相关的IPC处理程序
|
||||
* @param {Electron.IpcMain} ipcMain - Electron的IpcMain实例
|
||||
*/
|
||||
exports.initPaperIpc = function initPaperIpc(ipcMain) {
|
||||
// 查询所有考生试卷记录
|
||||
ipcMain.handle('paper-get-all', async (event) => {
|
||||
try {
|
||||
return await exports.getAllExamineePapersService();
|
||||
} catch (error) {
|
||||
console.error('IPC处理程序: 查询所有考生试卷记录失败', error);
|
||||
return {
|
||||
success: false,
|
||||
message: `查询失败: ${error.message}`,
|
||||
data: []
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
// 根据试卷ID查询试卷详情
|
||||
ipcMain.handle('paper-get-by-id', async (event, paperId) => {
|
||||
try {
|
||||
return await exports.getExamineePaperByIdService(paperId);
|
||||
} catch (error) {
|
||||
console.error('IPC处理程序: 根据ID查询试卷详情失败', error);
|
||||
return {
|
||||
success: false,
|
||||
message: `查询失败: ${error.message}`,
|
||||
data: null
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
// 查询考生的试卷记录
|
||||
ipcMain.handle('paper-get-by-examinee-id', async (event, examineeId) => {
|
||||
try {
|
||||
return await exports.getExamineePapersByExamineeIdService(examineeId);
|
||||
} catch (error) {
|
||||
console.error('IPC处理程序: 查询考生试卷记录失败', error);
|
||||
return {
|
||||
success: false,
|
||||
message: `查询失败: ${error.message}`,
|
||||
data: []
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
// 根据状态查询试卷记录
|
||||
ipcMain.handle('paper-get-by-status', async (event, status) => {
|
||||
try {
|
||||
return await exports.getExamineePapersByStatusService(status);
|
||||
} catch (error) {
|
||||
console.error('IPC处理程序: 根据状态查询试卷记录失败', error);
|
||||
return {
|
||||
success: false,
|
||||
message: `查询失败: ${error.message}`,
|
||||
data: []
|
||||
};
|
||||
}
|
||||
});
|
||||
}
|
@ -15,29 +15,42 @@
|
||||
active-text-color="#409EFF"
|
||||
:collapse="isCollapse"
|
||||
@select="handleMenuSelect"
|
||||
unique-opened
|
||||
>
|
||||
<el-menu-item index="/admin/home">
|
||||
<i class="el-icon-menu"></i>
|
||||
<span slot="title">后台首页</span>
|
||||
</el-menu-item>
|
||||
<el-menu-item index="/admin/question">
|
||||
<i class="el-icon-document-checked"></i>
|
||||
<span slot="title">试题管理</span>
|
||||
</el-menu-item>
|
||||
<el-menu-item index="/admin/examinee">
|
||||
<i class="el-icon-user-solid"></i>
|
||||
<span slot="title">考生管理</span>
|
||||
</el-menu-item>
|
||||
<el-menu-item index="/admin/exam">
|
||||
<i class="el-icon-date"></i>
|
||||
<span slot="title">考试管理</span>
|
||||
</el-menu-item>
|
||||
<el-menu-item index="logout">
|
||||
<!-- 系统管理组 -->
|
||||
<el-menu-item-group title="系统管理" v-if="!isCollapse">
|
||||
<el-menu-item index="/admin/home">
|
||||
<i class="el-icon-menu"></i>
|
||||
<span slot="title">后台首页</span>
|
||||
</el-menu-item>
|
||||
<el-menu-item index="/admin/question">
|
||||
<i class="el-icon-document-checked"></i>
|
||||
<span slot="title">试题管理</span>
|
||||
</el-menu-item>
|
||||
<el-menu-item index="/admin/examinee">
|
||||
<i class="el-icon-user-solid"></i>
|
||||
<span slot="title">考生管理</span>
|
||||
</el-menu-item>
|
||||
<el-menu-item index="/admin/exam">
|
||||
<i class="el-icon-date"></i>
|
||||
<span slot="title">考试管理</span>
|
||||
</el-menu-item>
|
||||
</el-menu-item-group>
|
||||
|
||||
<!-- 试卷管理组 -->
|
||||
<el-menu-item-group title="试卷管理" v-if="!isCollapse">
|
||||
<el-menu-item index="/admin/userPaper">
|
||||
<i class="el-icon-document-copy"></i>
|
||||
<span slot="title">用户试卷</span>
|
||||
</el-menu-item>
|
||||
</el-menu-item-group>
|
||||
|
||||
<!-- 退出登录 -->
|
||||
<el-menu-item index="logout" class="logout-item">
|
||||
<i class="el-icon-switch-button"></i>
|
||||
<span slot="title">退出登录</span>
|
||||
</el-menu-item>
|
||||
</el-menu>
|
||||
<!-- 删除折叠按钮 -->
|
||||
</el-aside>
|
||||
</template>
|
||||
|
||||
@ -47,7 +60,8 @@ export default {
|
||||
components: {
|
||||
ElAside: require('element-ui').Aside,
|
||||
ElMenu: require('element-ui').Menu,
|
||||
ElMenuItem: require('element-ui').MenuItem
|
||||
ElMenuItem: require('element-ui').MenuItem,
|
||||
ElMenuItemGroup: require('element-ui').MenuItemGroup
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
@ -132,11 +146,26 @@ export default {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
/* 菜单分组标题样式 */
|
||||
.el-menu-item-group__title {
|
||||
color: #8392a5 !important;
|
||||
font-size: 12px;
|
||||
padding: 12px 20px 8px;
|
||||
line-height: 1;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
/* 确保菜单中的图标和文字正确显示 */
|
||||
.el-menu-item i {
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
/* 退出登录菜单项样式 */
|
||||
.logout-item {
|
||||
margin-top: 20px;
|
||||
border-top: 1px solid #2d3a4b;
|
||||
}
|
||||
|
||||
/* 滚动条样式 */
|
||||
.sider-container::-webkit-scrollbar {
|
||||
width: 6px;
|
||||
@ -154,4 +183,9 @@ export default {
|
||||
.sider-container::-webkit-scrollbar-thumb:hover {
|
||||
background: #587ba0;
|
||||
}
|
||||
|
||||
/* 折叠状态下隐藏分组标题 */
|
||||
.el-menu--collapse .el-menu-item-group__title {
|
||||
display: none;
|
||||
}
|
||||
</style>
|
@ -13,6 +13,8 @@ import ExamineeHomeView from '../views/user/ExamineeHomeView.vue'
|
||||
import ExamingView from '../views/user/ExamingView.vue'
|
||||
// 添加EndView组件导入
|
||||
import EndView from '../views/user/EndView.vue'
|
||||
// 在文件顶部导入新组件
|
||||
import UserPaperManagementView from '../views/admin/UserPaperManagementView.vue'
|
||||
|
||||
Vue.use(VueRouter)
|
||||
|
||||
@ -31,7 +33,8 @@ const routes = [
|
||||
{ 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: 'exam', name: 'ExamManagement', component: ExamManagementView },
|
||||
{ path: 'userPaper', name: 'UserPaperManagement', component: UserPaperManagementView }
|
||||
]
|
||||
},
|
||||
// 添加考生相关路由
|
||||
|
@ -45,62 +45,6 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 快速操作区域 -->
|
||||
<div class="card mb-4">
|
||||
<div class="card-header">
|
||||
<h5 class="card-title">快速操作</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="row">
|
||||
<div class="col-md-2 mb-2">
|
||||
<el-button type="primary" icon="el-icon-user" size="medium" class="w-100">
|
||||
考生管理
|
||||
</el-button>
|
||||
</div>
|
||||
<div class="col-md-2 mb-2">
|
||||
<el-button type="primary" icon="el-icon-document" size="medium" class="w-100">
|
||||
试题管理
|
||||
</el-button>
|
||||
</div>
|
||||
<div class="col-md-2 mb-2">
|
||||
<el-button type="primary" icon="el-icon-date" size="medium" class="w-100">
|
||||
考试管理
|
||||
</el-button>
|
||||
</div>
|
||||
<div class="col-md-2 mb-2">
|
||||
<el-button type="primary" icon="el-icon-setting" size="medium" class="w-100">
|
||||
系统设置
|
||||
</el-button>
|
||||
</div>
|
||||
<div class="col-md-2 mb-2">
|
||||
<el-button type="primary" icon="el-icon-s-data" size="medium" class="w-100">
|
||||
数据统计
|
||||
</el-button>
|
||||
</div>
|
||||
<div class="col-md-2 mb-2">
|
||||
<el-button type="danger" icon="el-icon-switch-button" size="medium" class="w-100">
|
||||
退出登录
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 最近活动区域 -->
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h5 class="card-title">最近活动</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<el-table :data="recentActivities" style="width: 100%">
|
||||
<el-table-column prop="time" label="时间" width="180"></el-table-column>
|
||||
<el-table-column prop="user" label="用户" width="120"></el-table-column>
|
||||
<el-table-column prop="action" label="操作" width="150"></el-table-column>
|
||||
<el-table-column prop="details" label="详情"></el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
1190
src/views/admin/UserPaperManagementView.vue
Normal file
1190
src/views/admin/UserPaperManagementView.vue
Normal file
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user