后台试卷管理完成

This commit is contained in:
chenqiang 2025-09-14 11:24:26 +08:00
parent 7c9fdc8d6f
commit 1db2e19fe8
8 changed files with 1746 additions and 83 deletions

289
background/db/paper.js Normal file
View 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;
}
}

View File

@ -1,7 +1,7 @@
"use strict"; "use strict";
import { app, protocol, BrowserWindow, ipcMain, dialog } from "electron"; const { app, protocol, BrowserWindow, ipcMain, dialog, shell } = require("electron");
import { createProtocol } from "vue-cli-plugin-electron-builder/lib"; const { createProtocol } = require("vue-cli-plugin-electron-builder/lib");
// 替换argon2为bcryptjs // 替换argon2为bcryptjs
const bcrypt = require("bcryptjs"); const bcrypt = require("bcryptjs");
const isDevelopment = process.env.NODE_ENV !== "production"; const isDevelopment = process.env.NODE_ENV !== "production";
@ -31,6 +31,8 @@ const { initExamineeIpc } = require("./service/examineeService.js");
const { initExamingIpc } = require("./service/examingService.js"); const { initExamingIpc } = require("./service/examingService.js");
// 导入文件服务 // 导入文件服务
const { initFileIpc } = require("./service/fileService.js"); const { initFileIpc } = require("./service/fileService.js");
// 试卷
const { initPaperIpc } = require("./service/paperService.js");
// Scheme must be registered before the app is ready // Scheme must be registered before the app is ready
protocol.registerSchemesAsPrivileged([ protocol.registerSchemesAsPrivileged([
@ -210,6 +212,8 @@ app.on("ready", async () => {
initExamingIpc(ipcMain); initExamingIpc(ipcMain);
// 初始化文件相关的IPC处理程序 // 初始化文件相关的IPC处理程序
initFileIpc(ipcMain); initFileIpc(ipcMain);
// 初始化试卷相关的IPC处理程序
initPaperIpc(ipcMain);
// 检查数据库是否初始化 // 检查数据库是否初始化
try { try {
@ -366,3 +370,23 @@ ipcMain.handle("get-database-paths", (event) => {
return { systemDbPath: '获取失败', userDbPath: '获取失败' }; 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 };
}
});

View File

@ -160,16 +160,23 @@ contextBridge.exposeInMainWorld("electronAPI", {
ipcRenderer.invoke("file-generate-pdf", pdfData, fileName), ipcRenderer.invoke("file-generate-pdf", pdfData, fileName),
fileCopyToDesktop: (filePath) => fileCopyToDesktop: (filePath) =>
ipcRenderer.invoke("file-copy-to-desktop", 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接口确保兼容性
ipcRenderer: { ipcRenderer: {
invoke: (channel, data) => ipcRenderer.invoke(channel, data), invoke: (channel, data) => ipcRenderer.invoke(channel, data),
}, },
// 新增获取可执行文件路径的API
getExePath: () => ipcRenderer.invoke("get-exe-path"),
// 新增获取数据库路径的API
getDatabasePaths: () => ipcRenderer.invoke("get-database-paths"),
}); });
// 也保留原来的electron对象确保现有功能正常 // 也保留原来的electron对象确保现有功能正常

View 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: []
};
}
});
}

View File

@ -15,29 +15,42 @@
active-text-color="#409EFF" active-text-color="#409EFF"
:collapse="isCollapse" :collapse="isCollapse"
@select="handleMenuSelect" @select="handleMenuSelect"
unique-opened
> >
<el-menu-item index="/admin/home"> <!-- 系统管理组 -->
<i class="el-icon-menu"></i> <el-menu-item-group title="系统管理" v-if="!isCollapse">
<span slot="title">后台首页</span> <el-menu-item index="/admin/home">
</el-menu-item> <i class="el-icon-menu"></i>
<el-menu-item index="/admin/question"> <span slot="title">后台首页</span>
<i class="el-icon-document-checked"></i> </el-menu-item>
<span slot="title">试题管理</span> <el-menu-item index="/admin/question">
</el-menu-item> <i class="el-icon-document-checked"></i>
<el-menu-item index="/admin/examinee"> <span slot="title">试题管理</span>
<i class="el-icon-user-solid"></i> </el-menu-item>
<span slot="title">考生管理</span> <el-menu-item index="/admin/examinee">
</el-menu-item> <i class="el-icon-user-solid"></i>
<el-menu-item index="/admin/exam"> <span slot="title">考生管理</span>
<i class="el-icon-date"></i> </el-menu-item>
<span slot="title">考试管理</span> <el-menu-item index="/admin/exam">
</el-menu-item> <i class="el-icon-date"></i>
<el-menu-item index="logout"> <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> <i class="el-icon-switch-button"></i>
<span slot="title">退出登录</span> <span slot="title">退出登录</span>
</el-menu-item> </el-menu-item>
</el-menu> </el-menu>
<!-- 删除折叠按钮 -->
</el-aside> </el-aside>
</template> </template>
@ -47,7 +60,8 @@ export default {
components: { components: {
ElAside: require('element-ui').Aside, ElAside: require('element-ui').Aside,
ElMenu: require('element-ui').Menu, ElMenu: require('element-ui').Menu,
ElMenuItem: require('element-ui').MenuItem ElMenuItem: require('element-ui').MenuItem,
ElMenuItemGroup: require('element-ui').MenuItemGroup
}, },
data() { data() {
return { return {
@ -132,11 +146,26 @@ export default {
width: 100%; 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 { .el-menu-item i {
margin-right: 10px; margin-right: 10px;
} }
/* 退出登录菜单项样式 */
.logout-item {
margin-top: 20px;
border-top: 1px solid #2d3a4b;
}
/* 滚动条样式 */ /* 滚动条样式 */
.sider-container::-webkit-scrollbar { .sider-container::-webkit-scrollbar {
width: 6px; width: 6px;
@ -154,4 +183,9 @@ export default {
.sider-container::-webkit-scrollbar-thumb:hover { .sider-container::-webkit-scrollbar-thumb:hover {
background: #587ba0; background: #587ba0;
} }
/* 折叠状态下隐藏分组标题 */
.el-menu--collapse .el-menu-item-group__title {
display: none;
}
</style> </style>

View File

@ -13,6 +13,8 @@ import ExamineeHomeView from '../views/user/ExamineeHomeView.vue'
import ExamingView from '../views/user/ExamingView.vue' import ExamingView from '../views/user/ExamingView.vue'
// 添加EndView组件导入 // 添加EndView组件导入
import EndView from '../views/user/EndView.vue' import EndView from '../views/user/EndView.vue'
// 在文件顶部导入新组件
import UserPaperManagementView from '../views/admin/UserPaperManagementView.vue'
Vue.use(VueRouter) Vue.use(VueRouter)
@ -31,7 +33,8 @@ const routes = [
{ path: 'home', name: 'AdminHome', component: AdminHomeView }, { path: 'home', name: 'AdminHome', component: AdminHomeView },
{ path: 'question', name: 'QuestionManagement', component: QuestionManagementView }, { path: 'question', name: 'QuestionManagement', component: QuestionManagementView },
{ path: 'examinee', name: 'ExamineeManagement', component: ExamineeManagementView }, { path: 'examinee', name: 'ExamineeManagement', component: ExamineeManagementView },
{ path: 'exam', name: 'ExamManagement', component: ExamManagementView } { path: 'exam', name: 'ExamManagement', component: ExamManagementView },
{ path: 'userPaper', name: 'UserPaperManagement', component: UserPaperManagementView }
] ]
}, },
// 添加考生相关路由 // 添加考生相关路由

View File

@ -45,62 +45,6 @@
</div> </div>
</div> </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> </div>
</template> </template>

File diff suppressed because it is too large Load Diff