迁移config, dict, question接口
This commit is contained in:
parent
f3b783bf86
commit
6022e8e17a
143
src/background/db/config.js
Normal file
143
src/background/db/config.js
Normal file
@ -0,0 +1,143 @@
|
|||||||
|
const { getSystemDbPath } = require('./path.js');
|
||||||
|
const { executeWithRetry } = require('./utils.js');
|
||||||
|
const { getDbConnection } = require('./index.js');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从config表获取配置项
|
||||||
|
* @param {string} key - 配置项键名
|
||||||
|
* @returns {Promise<{key: string, value: string} | null>} 配置项对象,如果不存在返回null
|
||||||
|
*/
|
||||||
|
async function getConfig(key) {
|
||||||
|
try {
|
||||||
|
const db = await getDbConnection(getSystemDbPath());
|
||||||
|
|
||||||
|
const result = await executeWithRetry(db, async () => {
|
||||||
|
return await db.getAsync('SELECT * FROM config WHERE key = ?', [key]);
|
||||||
|
});
|
||||||
|
|
||||||
|
return result;
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`获取配置项${key}失败:`, error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取所有配置项列表
|
||||||
|
* @returns {Promise<Array<{id: number, key: string, value: string, protected: number}>>} 配置项列表
|
||||||
|
*/
|
||||||
|
async function getAllConfigs() {
|
||||||
|
try {
|
||||||
|
const db = await getDbConnection(getSystemDbPath());
|
||||||
|
|
||||||
|
const result = await executeWithRetry(db, async () => {
|
||||||
|
return await db.allAsync('SELECT * FROM config');
|
||||||
|
});
|
||||||
|
|
||||||
|
return result;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取配置项列表失败:', error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 通过ID获取配置项
|
||||||
|
* @param {number} id - 配置项ID
|
||||||
|
* @returns {Promise<{id: number, key: string, value: string, protected: number} | null>} 配置项对象,如果不存在返回null
|
||||||
|
*/
|
||||||
|
async function getConfigById(id) {
|
||||||
|
try {
|
||||||
|
const db = await getDbConnection(getSystemDbPath());
|
||||||
|
|
||||||
|
const result = await executeWithRetry(db, async () => {
|
||||||
|
return await db.getAsync('SELECT * FROM config WHERE id = ?', [id]);
|
||||||
|
});
|
||||||
|
|
||||||
|
return result;
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`通过ID获取配置项${id}失败:`, error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新或插入配置项
|
||||||
|
* @param {string} key - 配置项键名
|
||||||
|
* @param {string} value - 配置项值
|
||||||
|
* @returns {Promise<void>}
|
||||||
|
*/
|
||||||
|
async function setConfig(key, value) {
|
||||||
|
try {
|
||||||
|
const db = await getDbConnection(getSystemDbPath());
|
||||||
|
|
||||||
|
// 先检查是否存在
|
||||||
|
const existing = await executeWithRetry(db, async () => {
|
||||||
|
return await db.getAsync('SELECT * FROM config WHERE key = ?', [key]);
|
||||||
|
});
|
||||||
|
|
||||||
|
if (existing) {
|
||||||
|
// 检查是否受保护
|
||||||
|
// if (existing.protected === 1) {
|
||||||
|
// throw new Error(`配置项${key}是受保护的,无法修改`);
|
||||||
|
// }
|
||||||
|
// 更新
|
||||||
|
await executeWithRetry(db, async () => {
|
||||||
|
await db.runAsync('UPDATE config SET value = ? WHERE key = ?', [value, key]);
|
||||||
|
console.log(`成功更新配置项: ${key}`);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// 插入 (默认不保护)
|
||||||
|
await executeWithRetry(db, async () => {
|
||||||
|
await db.runAsync('INSERT INTO config (key, value, protected) VALUES (?, ?, 0)', [key, value]);
|
||||||
|
console.log(`成功插入配置项: ${key}`);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`设置配置项${key}失败:`, error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除配置项
|
||||||
|
* @param {number} id - 配置项ID
|
||||||
|
* @returns {Promise<void>}
|
||||||
|
*/
|
||||||
|
async function deleteConfig(id) {
|
||||||
|
try {
|
||||||
|
const db = await getDbConnection(getSystemDbPath());
|
||||||
|
|
||||||
|
// 先检查是否存在
|
||||||
|
const existing = await executeWithRetry(db, async () => {
|
||||||
|
return await db.getAsync('SELECT * FROM config WHERE id = ?', [id]);
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!existing) {
|
||||||
|
throw new Error(`配置项ID ${id} 不存在`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查是否受保护
|
||||||
|
if (existing.protected === 1) {
|
||||||
|
throw new Error(`配置项 ${existing.key} 是受保护的,无法删除`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除
|
||||||
|
await executeWithRetry(db, async () => {
|
||||||
|
await db.runAsync('DELETE FROM config WHERE id = ?', [id]);
|
||||||
|
console.log(`成功删除配置项ID: ${id}`);
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`删除配置项ID ${id} 失败:`, error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 导出使用CommonJS格式
|
||||||
|
module.exports = {
|
||||||
|
getConfig,
|
||||||
|
setConfig,
|
||||||
|
getAllConfigs,
|
||||||
|
getConfigById,
|
||||||
|
deleteConfig
|
||||||
|
};
|
259
src/background/db/dict.js
Normal file
259
src/background/db/dict.js
Normal file
@ -0,0 +1,259 @@
|
|||||||
|
const { getSystemDbPath } = require('./path.js');
|
||||||
|
const { executeWithRetry } = require('./utils.js');
|
||||||
|
const { getDbConnection } = require('./index.js');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询dict_items连接dict_types后的记录列表
|
||||||
|
* @returns {Promise<Array>} 记录列表
|
||||||
|
*/
|
||||||
|
async function getDictItemsWithTypes() {
|
||||||
|
const db = await getDbConnection(getSystemDbPath());
|
||||||
|
return executeWithRetry(db, async () => {
|
||||||
|
const sql = `
|
||||||
|
SELECT di.*, dt.type_name
|
||||||
|
FROM dict_items di
|
||||||
|
JOIN dict_types dt ON di.type_code = dt.type_code
|
||||||
|
ORDER BY dt.type_code, di.item_code
|
||||||
|
`;
|
||||||
|
return await db.allAsync(sql);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询dict_types列表
|
||||||
|
* @returns {Promise<Array>} 类型列表
|
||||||
|
*/
|
||||||
|
async function getDictTypes() {
|
||||||
|
const db = await getDbConnection(getSystemDbPath());
|
||||||
|
return executeWithRetry(db, async () => {
|
||||||
|
const sql = 'SELECT * FROM dict_types ORDER BY type_code';
|
||||||
|
return await db.allAsync(sql);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 添加dict_types
|
||||||
|
* @param {Object} typeData 类型数据
|
||||||
|
* @returns {Promise<Object>} 添加的类型
|
||||||
|
*/
|
||||||
|
async function addDictType(typeData) {
|
||||||
|
const { type_code, type_name, description } = typeData;
|
||||||
|
const db = await getDbConnection(getSystemDbPath());
|
||||||
|
return executeWithRetry(db, async () => {
|
||||||
|
const sql = 'INSERT INTO dict_types (type_code, type_name, description) VALUES (?, ?, ?)';
|
||||||
|
const result = await db.runAsync(sql, [type_code, type_name, description || null]);
|
||||||
|
return { id: result.lastID, ...typeData };
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 添加dict_items
|
||||||
|
* @param {Object} itemData 字典项数据
|
||||||
|
* @returns {Promise<Object>} 添加的字典项
|
||||||
|
*/
|
||||||
|
async function addDictItem(itemData) {
|
||||||
|
const { type_code, item_code, item_name, item_description, parent_code, is_active } = itemData;
|
||||||
|
const db = await getDbConnection(getSystemDbPath());
|
||||||
|
return executeWithRetry(db, async () => {
|
||||||
|
const sql = 'INSERT INTO dict_items (type_code, item_code, item_name, item_description, parent_code, is_active) VALUES (?, ?, ?, ?, ?, ?)';
|
||||||
|
const result = await db.runAsync(sql, [
|
||||||
|
type_code,
|
||||||
|
item_code,
|
||||||
|
item_name,
|
||||||
|
item_description || null,
|
||||||
|
parent_code || null,
|
||||||
|
is_active !== undefined ? is_active : 1
|
||||||
|
]);
|
||||||
|
|
||||||
|
// 检查result是否存在,如果不存在则获取最后插入的ID
|
||||||
|
let lastId;
|
||||||
|
if (result && result.lastID) {
|
||||||
|
lastId = result.lastID;
|
||||||
|
} else {
|
||||||
|
// 使用另一种方式获取最后插入的ID
|
||||||
|
const idResult = await db.getAsync('SELECT last_insert_rowid() as id');
|
||||||
|
lastId = idResult ? idResult.id : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!lastId) {
|
||||||
|
throw new Error('无法获取插入的字典项ID');
|
||||||
|
}
|
||||||
|
|
||||||
|
return { id: lastId, ...itemData };
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询一条dict_types
|
||||||
|
* @param {number} id 类型ID
|
||||||
|
* @returns {Promise<Object|null>} 类型数据或null
|
||||||
|
*/
|
||||||
|
async function getDictTypeById(id) {
|
||||||
|
const db = await getDbConnection(getSystemDbPath());
|
||||||
|
return executeWithRetry(db, async () => {
|
||||||
|
const sql = 'SELECT * FROM dict_types WHERE id = ?';
|
||||||
|
return await db.getAsync(sql, [id]);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询一条dict_items
|
||||||
|
* @param {number} id 字典项ID
|
||||||
|
* @returns {Promise<Object|null>} 字典项数据或null
|
||||||
|
*/
|
||||||
|
async function getDictItemById(id) {
|
||||||
|
const db = await getDbConnection(getSystemDbPath());
|
||||||
|
return executeWithRetry(db, async () => {
|
||||||
|
const sql = 'SELECT * FROM dict_items WHERE id = ?';
|
||||||
|
return await db.getAsync(sql, [id]);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据dict_types的type_code查询dict_items列表
|
||||||
|
* @param {string} typeCode 类型编码
|
||||||
|
* @param {number} isActive 是否激活(1=激活, 0=未激活, 不传=全部)
|
||||||
|
* @returns {Promise<Array>} 字典项列表
|
||||||
|
*/
|
||||||
|
async function getDictItemsByTypeCode(typeCode, isActive = undefined) {
|
||||||
|
const db = await getDbConnection(getSystemDbPath());
|
||||||
|
return executeWithRetry(db, async () => {
|
||||||
|
let sql = 'SELECT * FROM dict_items WHERE type_code = ?';
|
||||||
|
const params = [typeCode];
|
||||||
|
|
||||||
|
if (isActive !== undefined) {
|
||||||
|
sql += ' AND is_active = ?';
|
||||||
|
params.push(isActive);
|
||||||
|
}
|
||||||
|
|
||||||
|
sql += ' ORDER BY item_code';
|
||||||
|
return await db.allAsync(sql, params);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新一条dict_types
|
||||||
|
* @param {number} id 类型ID
|
||||||
|
* @param {Object} typeData 更新的数据
|
||||||
|
* @returns {Promise<boolean>} 是否更新成功
|
||||||
|
*/
|
||||||
|
async function updateDictType(id, typeData) {
|
||||||
|
const { type_code, type_name, description } = typeData;
|
||||||
|
const db = await getDbConnection(getSystemDbPath());
|
||||||
|
return executeWithRetry(db, async () => {
|
||||||
|
const sql = 'UPDATE dict_types SET type_code = ?, type_name = ?, description = ? WHERE id = ?';
|
||||||
|
const result = await db.runAsync(sql, [type_code, type_name, description || null, id]);
|
||||||
|
return result.changes > 0;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新一条dict_items
|
||||||
|
* @param {number} id 字典项ID
|
||||||
|
* @param {Object} itemData 更新的数据
|
||||||
|
* @returns {Promise<boolean>} 是否更新成功
|
||||||
|
*/
|
||||||
|
async function updateDictItem(id, itemData) {
|
||||||
|
const { type_code, item_code, item_name, item_description, parent_code, is_active } = itemData;
|
||||||
|
const db = await getDbConnection(getSystemDbPath());
|
||||||
|
return executeWithRetry(db, async () => {
|
||||||
|
const sql = 'UPDATE dict_items SET type_code = ?, item_code = ?, item_name = ?, item_description = ?, parent_code = ?, is_active = ? WHERE id = ?';
|
||||||
|
const result = await db.runAsync(sql, [
|
||||||
|
type_code,
|
||||||
|
item_code,
|
||||||
|
item_name,
|
||||||
|
item_description || null,
|
||||||
|
parent_code || null,
|
||||||
|
is_active !== undefined ? is_active : 1,
|
||||||
|
id
|
||||||
|
]);
|
||||||
|
|
||||||
|
// 检查result是否存在以及是否有changes属性
|
||||||
|
if (!result || result.changes === undefined) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result.changes > 0;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除一条dict_types
|
||||||
|
* @param {number} id 类型ID
|
||||||
|
* @returns {Promise<boolean>} 是否删除成功
|
||||||
|
*/
|
||||||
|
async function deleteDictType(id) {
|
||||||
|
const db = await getDbConnection(getSystemDbPath());
|
||||||
|
return executeWithRetry(db, async () => {
|
||||||
|
// 先检查是否有关联的字典项
|
||||||
|
const checkSql = 'SELECT COUNT(*) as count FROM dict_items WHERE type_code = (SELECT type_code FROM dict_types WHERE id = ?)';
|
||||||
|
const result = await db.getAsync(checkSql, [id]);
|
||||||
|
if (result.count > 0) {
|
||||||
|
throw new Error('该字典类型下有关联的字典项,不允许删除');
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除字典类型
|
||||||
|
const deleteSql = 'DELETE FROM dict_types WHERE id = ?';
|
||||||
|
const deleteResult = await db.runAsync(deleteSql, [id]);
|
||||||
|
return deleteResult.changes > 0;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除一条dict_items
|
||||||
|
* @param {number} id 字典项ID
|
||||||
|
* @returns {Promise<boolean>} 是否删除成功
|
||||||
|
*/
|
||||||
|
async function deleteDictItem(id) {
|
||||||
|
const db = await getDbConnection(getSystemDbPath());
|
||||||
|
return executeWithRetry(db, async () => {
|
||||||
|
const sql = 'DELETE FROM dict_items WHERE id = ?';
|
||||||
|
const result = await db.runAsync(sql, [id]);
|
||||||
|
|
||||||
|
// 检查result是否存在以及是否有changes属性
|
||||||
|
if (!result || result.changes === undefined) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result.changes > 0;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查parent_code是否存在
|
||||||
|
async function checkParentCodeExists(parentCode) {
|
||||||
|
if (!parentCode) return true; // 允许空的parent_code
|
||||||
|
|
||||||
|
const db = await getDbConnection(getSystemDbPath());
|
||||||
|
return executeWithRetry(db, async () => {
|
||||||
|
const sql = 'SELECT COUNT(*) as count FROM dict_items WHERE item_code = ?';
|
||||||
|
const result = await db.getAsync(sql, [parentCode]);
|
||||||
|
return result.count > 0;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查是否有其他记录引用了该item_code作为parent_code
|
||||||
|
async function hasChildReferences(itemCode) {
|
||||||
|
const db = await getDbConnection(getSystemDbPath());
|
||||||
|
return executeWithRetry(db, async () => {
|
||||||
|
const sql = 'SELECT COUNT(*) as count FROM dict_items WHERE parent_code = ?';
|
||||||
|
const result = await db.getAsync(sql, [itemCode]);
|
||||||
|
return result.count > 0;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 导出使用CommonJS格式
|
||||||
|
module.exports = {
|
||||||
|
getDictItemsWithTypes,
|
||||||
|
getDictTypes,
|
||||||
|
addDictType,
|
||||||
|
addDictItem,
|
||||||
|
getDictTypeById,
|
||||||
|
getDictItemById,
|
||||||
|
getDictItemsByTypeCode,
|
||||||
|
updateDictType,
|
||||||
|
updateDictItem,
|
||||||
|
deleteDictType,
|
||||||
|
deleteDictItem,
|
||||||
|
checkParentCodeExists,
|
||||||
|
hasChildReferences
|
||||||
|
};
|
423
src/background/db/question.js
Normal file
423
src/background/db/question.js
Normal file
@ -0,0 +1,423 @@
|
|||||||
|
const { executeWithRetry } = require('./utils.js');
|
||||||
|
const { getDbConnection } = require('./index.js');
|
||||||
|
const { getSystemDbPath } = require('./path.js');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 添加新题干及相关数据
|
||||||
|
* @param {Object} questionData - 题干数据
|
||||||
|
* @param {string} questionData.question_type - 题型代码
|
||||||
|
* @param {string} questionData.question_description - 题干描述
|
||||||
|
* @param {Array} questionData.images - 图片数据数组
|
||||||
|
* @param {Array} questionData.datasets - 数据集数据数组
|
||||||
|
* @returns {Promise<number>} 新创建的题干ID
|
||||||
|
*/
|
||||||
|
async function addQuestion(questionData) {
|
||||||
|
const { question_type, question_description, images = [], datasets = [] } = questionData;
|
||||||
|
|
||||||
|
const db = await getDbConnection(getSystemDbPath());
|
||||||
|
return executeWithRetry(db, async () => {
|
||||||
|
// 开始事务
|
||||||
|
await db.runAsync('BEGIN TRANSACTION');
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 1. 插入题干基本信息
|
||||||
|
const questionResult = await db.runAsync(
|
||||||
|
'INSERT INTO questions (question_type, question_name, question_description, created_at, updated_at) VALUES (?, ?, ?, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)',
|
||||||
|
[question_type, '', question_description]
|
||||||
|
);
|
||||||
|
|
||||||
|
const questionId = questionResult.lastID;
|
||||||
|
|
||||||
|
// 2. 插入图片数据
|
||||||
|
if (images.length > 0) {
|
||||||
|
for (const image of images) {
|
||||||
|
await db.runAsync(
|
||||||
|
'INSERT INTO question_images (question_id, image_name, image_base64, created_at, updated_at) VALUES (?, ?, ?, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)',
|
||||||
|
[questionId, image.image || 'image', image.base64]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. 插入数据集数据
|
||||||
|
if (datasets.length > 0) {
|
||||||
|
for (const dataset of datasets) {
|
||||||
|
await db.runAsync(
|
||||||
|
'INSERT INTO question_datasets (question_id, dataset_name, dataset_data, created_at, updated_at) VALUES (?, ?, ?, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)',
|
||||||
|
[questionId, dataset.name || 'dataset', dataset.content]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 提交事务
|
||||||
|
await db.runAsync('COMMIT');
|
||||||
|
return questionId;
|
||||||
|
} catch (error) {
|
||||||
|
// 回滚事务
|
||||||
|
await db.runAsync('ROLLBACK');
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取所有题干
|
||||||
|
* @returns {Promise<Array>} 题干列表
|
||||||
|
*/
|
||||||
|
async function getAllQuestions() {
|
||||||
|
const db = await getDbConnection(getSystemDbPath());
|
||||||
|
return executeWithRetry(db, async () => {
|
||||||
|
return await db.allAsync('SELECT * FROM questions ORDER BY id DESC');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据ID获取题干详情
|
||||||
|
* @param {number} id - 题干ID
|
||||||
|
* @returns {Promise<Object>} 题干详情
|
||||||
|
*/
|
||||||
|
async function getQuestionById(id) {
|
||||||
|
const db = await getDbConnection(getSystemDbPath());
|
||||||
|
return executeWithRetry(db, async () => {
|
||||||
|
return await db.getAsync('SELECT * FROM questions WHERE id = ?', [id]);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新题干信息
|
||||||
|
* @param {number} id - 题干ID
|
||||||
|
* @param {Object} questionData - 题干数据
|
||||||
|
* @returns {Promise<boolean>} 是否更新成功
|
||||||
|
*/
|
||||||
|
async function updateQuestion(id, questionData) {
|
||||||
|
const { question_type, question_description } = questionData;
|
||||||
|
const db = await getDbConnection(getSystemDbPath());
|
||||||
|
return executeWithRetry(db, async () => {
|
||||||
|
const result = await db.runAsync(
|
||||||
|
'UPDATE questions SET question_type = ?, question_description = ?, updated_at = CURRENT_TIMESTAMP WHERE id = ?',
|
||||||
|
[question_type, question_description, id]
|
||||||
|
);
|
||||||
|
return result.changes > 0;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除题干
|
||||||
|
* @param {number} id - 题干ID
|
||||||
|
* @returns {Promise<boolean>} 是否删除成功
|
||||||
|
*/
|
||||||
|
async function deleteQuestion(id) {
|
||||||
|
const db = await getDbConnection(getSystemDbPath());
|
||||||
|
return executeWithRetry(db, async () => {
|
||||||
|
// 开始事务
|
||||||
|
await db.runAsync('BEGIN TRANSACTION');
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 先删除关联数据
|
||||||
|
await db.runAsync('DELETE FROM question_images WHERE question_id = ?', [id]);
|
||||||
|
await db.runAsync('DELETE FROM question_datasets WHERE question_id = ?', [id]);
|
||||||
|
|
||||||
|
// 删除关联的试题数据
|
||||||
|
await db.runAsync('DELETE FROM question_choices WHERE question_id = ?', [id]);
|
||||||
|
await db.runAsync('DELETE FROM question_fill_blanks WHERE question_id = ?', [id]);
|
||||||
|
await db.runAsync('DELETE FROM question_fill_table WHERE question_id = ?', [id]);
|
||||||
|
await db.runAsync('DELETE FROM question_fill_table_blanks WHERE question_id = ?', [id]);
|
||||||
|
await db.runAsync('DELETE FROM question_judge WHERE question_id = ?', [id]);
|
||||||
|
await db.runAsync('DELETE FROM question_short WHERE question_id = ?', [id]);
|
||||||
|
|
||||||
|
// 再删除题干
|
||||||
|
const result = await db.runAsync('DELETE FROM questions WHERE id = ?', [id]);
|
||||||
|
|
||||||
|
// 提交事务
|
||||||
|
await db.runAsync('COMMIT');
|
||||||
|
return result.changes > 0;
|
||||||
|
} catch (error) {
|
||||||
|
// 回滚事务
|
||||||
|
await db.runAsync('ROLLBACK');
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取所有题干及其关联信息
|
||||||
|
* @returns {Promise<Array>} 包含关联信息的题干列表
|
||||||
|
*/
|
||||||
|
async function getAllQuestionsWithRelations() {
|
||||||
|
const db = await getDbConnection(getSystemDbPath());
|
||||||
|
return executeWithRetry(db, async () => {
|
||||||
|
// 1. 查询所有题干基本信息及关联的题型名称
|
||||||
|
const questions = await db.allAsync(`
|
||||||
|
SELECT q.*, di.item_name as question_type_name
|
||||||
|
FROM questions q
|
||||||
|
LEFT JOIN dict_items di ON q.question_type = di.item_code AND di.type_code = 'question_type'
|
||||||
|
ORDER BY q.id DESC
|
||||||
|
`);
|
||||||
|
|
||||||
|
// 2. 为每个题干查询关联的图片、数据集和特定类型的问题数据
|
||||||
|
for (const question of questions) {
|
||||||
|
// 查询关联的图片
|
||||||
|
const images = await db.allAsync(
|
||||||
|
'SELECT * FROM question_images WHERE question_id = ?',
|
||||||
|
[question.id]
|
||||||
|
);
|
||||||
|
question.images = images;
|
||||||
|
|
||||||
|
// 查询关联的数据集
|
||||||
|
const datasets = await db.allAsync(
|
||||||
|
'SELECT * FROM question_datasets WHERE question_id = ?',
|
||||||
|
[question.id]
|
||||||
|
);
|
||||||
|
datasets.forEach(dataset => {
|
||||||
|
try {
|
||||||
|
dataset.dataset_data = JSON.parse(dataset.dataset_data);
|
||||||
|
} catch (e) {
|
||||||
|
console.error('解析数据集失败:', e);
|
||||||
|
dataset.dataset_data = null;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
question.datasets = datasets;
|
||||||
|
|
||||||
|
// 根据question_type关联不同的问题表
|
||||||
|
switch (question.question_type) {
|
||||||
|
case 'choice':
|
||||||
|
// 关联选择题表
|
||||||
|
question.choices = await db.allAsync(
|
||||||
|
'SELECT * FROM question_choices WHERE question_id = ?',
|
||||||
|
[question.id]
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
case 'fill_blank':
|
||||||
|
// 关联填空题表
|
||||||
|
question.fillBlanks = await db.allAsync(
|
||||||
|
'SELECT * FROM question_fill_blanks WHERE question_id = ?',
|
||||||
|
[question.id]
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
case 'fill_table':
|
||||||
|
// 关联表格填空题表和表格填空项表
|
||||||
|
question.fillTables = await db.allAsync(
|
||||||
|
'SELECT * FROM question_fill_table WHERE question_id = ?',
|
||||||
|
[question.id]
|
||||||
|
);
|
||||||
|
|
||||||
|
// 对每个表格查询其填空项
|
||||||
|
for (let table of question.fillTables) {
|
||||||
|
table.blanks = await db.allAsync(
|
||||||
|
'SELECT * FROM question_fill_table_blanks WHERE table_id = ?',
|
||||||
|
[table.id]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'true_false':
|
||||||
|
// 关联判断题表
|
||||||
|
question.judges = await db.allAsync(
|
||||||
|
'SELECT * FROM question_judge WHERE question_id = ?',
|
||||||
|
[question.id]
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
case 'short_answer':
|
||||||
|
case 'analysis':
|
||||||
|
case 'essay':
|
||||||
|
// 关联简答题表
|
||||||
|
question.shorts = await db.allAsync(
|
||||||
|
'SELECT * FROM question_short WHERE question_id = ?',
|
||||||
|
[question.id]
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
// 未知题型,不关联任何表
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return questions;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新题干描述
|
||||||
|
* @param {number} id - 题干ID
|
||||||
|
* @param {string} questionDescription - 新的题干描述
|
||||||
|
* @returns {Promise<boolean>} 是否更新成功
|
||||||
|
*/
|
||||||
|
async function updateQuestionDescription(id, questionDescription) {
|
||||||
|
const db = await getDbConnection(getSystemDbPath());
|
||||||
|
return executeWithRetry(db, async () => {
|
||||||
|
const result = await db.runAsync(
|
||||||
|
'UPDATE questions SET question_description = ?, updated_at = CURRENT_TIMESTAMP WHERE id = ?',
|
||||||
|
[questionDescription, id]
|
||||||
|
);
|
||||||
|
return result.changes > 0;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 添加选择题问题
|
||||||
|
* @param {Object} choiceData - 选择题数据
|
||||||
|
* @param {number} choiceData.question_id - 题干ID
|
||||||
|
* @param {string} choiceData.choice_description - 问题描述
|
||||||
|
* @param {string} choiceData.choice_type - 题型(single/multiple)
|
||||||
|
* @param {Array} choiceData.choice_options - 候选项数组
|
||||||
|
* @param {Array} choiceData.correct_answers - 正确答案序号数组
|
||||||
|
* @returns {Promise<number>} 新创建的选择题ID
|
||||||
|
*/
|
||||||
|
async function addChoiceQuestion(choiceData) {
|
||||||
|
const { question_id, choice_description, choice_type, score, choice_options, correct_answers } = choiceData;
|
||||||
|
|
||||||
|
const db = await getDbConnection(getSystemDbPath());
|
||||||
|
return executeWithRetry(db, async () => {
|
||||||
|
const result = await db.runAsync(
|
||||||
|
'INSERT INTO question_choices (question_id, choice_description, choice_type, score, choice_options, correct_answers, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)',
|
||||||
|
[question_id, choice_description, choice_type, score, JSON.stringify(choice_options), JSON.stringify(correct_answers)]
|
||||||
|
);
|
||||||
|
return result.lastID;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 添加选择题问题
|
||||||
|
* @param {number} id - 选择题ID
|
||||||
|
* @param {Object} choiceData - 选择题数据
|
||||||
|
* @returns {Promise<boolean>} 是否更新成功
|
||||||
|
*/
|
||||||
|
async function updateChoiceQuestion(id, choiceData) {
|
||||||
|
const { choice_description, choice_type, score, choice_options, correct_answers } = choiceData;
|
||||||
|
|
||||||
|
const db = await getDbConnection(getSystemDbPath());
|
||||||
|
return executeWithRetry(db, async () => {
|
||||||
|
const result = await db.runAsync(
|
||||||
|
'UPDATE question_choices SET choice_description = ?, choice_type = ?, score = ?, choice_options = ?, correct_answers = ?, updated_at = CURRENT_TIMESTAMP WHERE id = ?',
|
||||||
|
[choice_description, choice_type, score, JSON.stringify(choice_options), JSON.stringify(correct_answers), id]
|
||||||
|
);
|
||||||
|
return result.changes > 0;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 添加填空题问题
|
||||||
|
* @param {Object} fillBlankData - 填空题数据
|
||||||
|
* @param {number} fillBlankData.question_id - 题干ID
|
||||||
|
* @param {string} fillBlankData.blank_description - 问题描述
|
||||||
|
* @param {number} fillBlankData.blank_count - 空白数量
|
||||||
|
* @param {Array} fillBlankData.correct_answers - 正确答案数组
|
||||||
|
* @param {number} fillBlankData.score - 分值
|
||||||
|
* @returns {Promise<number>} 新创建的填空题ID
|
||||||
|
*/
|
||||||
|
async function addFillBlankQuestion(fillBlankData) {
|
||||||
|
const { question_id, blank_description, blank_count, correct_answers, score } = fillBlankData;
|
||||||
|
|
||||||
|
const db = await getDbConnection(getSystemDbPath());
|
||||||
|
return executeWithRetry(db, async () => {
|
||||||
|
const result = await db.runAsync(
|
||||||
|
'INSERT INTO question_fill_blanks (question_id, blank_description, blank_count, correct_answers, score, created_at, updated_at) VALUES (?, ?, ?, ?, ?, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)',
|
||||||
|
[question_id, blank_description, blank_count, JSON.stringify(correct_answers), score]
|
||||||
|
);
|
||||||
|
return result.lastID;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新填空题问题
|
||||||
|
* @param {number} id - 填空题ID
|
||||||
|
* @param {Object} fillBlankData - 填空题数据
|
||||||
|
* @returns {Promise<boolean>} 是否更新成功
|
||||||
|
*/
|
||||||
|
async function updateFillBlankQuestion(id, fillBlankData) {
|
||||||
|
const { blank_description, blank_count, correct_answers, score } = fillBlankData;
|
||||||
|
|
||||||
|
const db = await getDbConnection(getSystemDbPath());
|
||||||
|
return executeWithRetry(db, async () => {
|
||||||
|
const result = await db.runAsync(
|
||||||
|
'UPDATE question_fill_blanks SET blank_description = ?, blank_count = ?, correct_answers = ?, score = ?, updated_at = CURRENT_TIMESTAMP WHERE id = ?',
|
||||||
|
[blank_description, blank_count, JSON.stringify(correct_answers), score, id]
|
||||||
|
);
|
||||||
|
return result.changes > 0;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除填空题问题
|
||||||
|
* @param {number} id - 填空题ID
|
||||||
|
* @returns {Promise<boolean>} 是否删除成功
|
||||||
|
*/
|
||||||
|
async function deleteFillBlankQuestion(id) {
|
||||||
|
const db = await getDbConnection(getSystemDbPath());
|
||||||
|
return executeWithRetry(db, async () => {
|
||||||
|
const result = await db.runAsync('DELETE FROM question_fill_blanks WHERE id = ?', [id]);
|
||||||
|
return result.changes > 0;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据题干ID查询填空题问题
|
||||||
|
* @param {number} questionId - 题干ID
|
||||||
|
* @returns {Promise<Array>} 填空题列表
|
||||||
|
*/
|
||||||
|
async function getFillBlankQuestionsByQuestionId(questionId) {
|
||||||
|
const db = await getDbConnection(getSystemDbPath());
|
||||||
|
return executeWithRetry(db, async () => {
|
||||||
|
const questions = await db.allAsync('SELECT * FROM question_fill_blanks WHERE question_id = ?', [questionId]);
|
||||||
|
// 解析correct_answers为数组
|
||||||
|
questions.forEach(question => {
|
||||||
|
try {
|
||||||
|
question.correct_answers = JSON.parse(question.correct_answers);
|
||||||
|
} catch (e) {
|
||||||
|
console.error('解析填空题答案失败:', e);
|
||||||
|
question.correct_answers = [];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return questions;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询试题总数和总分
|
||||||
|
* @returns {Promise<{totalQuestions: number, totalScore: number}>} 包含试题总数和总分的对象
|
||||||
|
*/
|
||||||
|
async function getQuestionsCountAndScore() {
|
||||||
|
const db = await getDbConnection(getSystemDbPath());
|
||||||
|
return executeWithRetry(db, async () => {
|
||||||
|
// 查询选择题总数和总分
|
||||||
|
const choiceResult = await db.getAsync(`
|
||||||
|
SELECT COUNT(*) as count, SUM(qc.score) as score
|
||||||
|
FROM questions q
|
||||||
|
LEFT JOIN question_choices qc ON q.id = qc.question_id
|
||||||
|
WHERE q.question_type = 'choice'
|
||||||
|
`);
|
||||||
|
|
||||||
|
// 查询填空题总数和总分
|
||||||
|
const fillBlankResult = await db.getAsync(`
|
||||||
|
SELECT COUNT(*) as count, SUM(qfb.score) as score
|
||||||
|
FROM questions q
|
||||||
|
LEFT JOIN question_fill_blanks qfb ON q.id = qfb.question_id
|
||||||
|
WHERE q.question_type = 'fill_blank'
|
||||||
|
`);
|
||||||
|
|
||||||
|
// 计算总题数和总分
|
||||||
|
const totalQuestions = (choiceResult.count || 0) + (fillBlankResult.count || 0);
|
||||||
|
const totalScore = (choiceResult.score || 0) + (fillBlankResult.score || 0);
|
||||||
|
|
||||||
|
return {
|
||||||
|
totalQuestions,
|
||||||
|
totalScore
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 导出使用CommonJS格式
|
||||||
|
module.exports = {
|
||||||
|
addQuestion,
|
||||||
|
getAllQuestions,
|
||||||
|
getQuestionById,
|
||||||
|
updateQuestion,
|
||||||
|
deleteQuestion,
|
||||||
|
getAllQuestionsWithRelations,
|
||||||
|
updateQuestionDescription,
|
||||||
|
addChoiceQuestion,
|
||||||
|
updateChoiceQuestion,
|
||||||
|
addFillBlankQuestion,
|
||||||
|
updateFillBlankQuestion,
|
||||||
|
deleteFillBlankQuestion,
|
||||||
|
getFillBlankQuestionsByQuestionId,
|
||||||
|
getQuestionsCountAndScore
|
||||||
|
};
|
@ -9,6 +9,12 @@ const isDevelopment = process.env.NODE_ENV !== 'production'
|
|||||||
|
|
||||||
// 导入数据库相关函数
|
// 导入数据库相关函数
|
||||||
const { checkDatabaseInitialized, initializeDatabase } = require('./db/index.js');
|
const { checkDatabaseInitialized, initializeDatabase } = require('./db/index.js');
|
||||||
|
// 导入配置服务
|
||||||
|
const { initConfigIpc } = require('./service/configService.js');
|
||||||
|
// 导入字典服务
|
||||||
|
const { initDictIpc } = require('./service/dictService.js');
|
||||||
|
// 导入试题服务
|
||||||
|
const { initQuestionIpc } = require('./service/questionService.js');
|
||||||
|
|
||||||
// Scheme must be registered before the app is ready
|
// Scheme must be registered before the app is ready
|
||||||
protocol.registerSchemesAsPrivileged([
|
protocol.registerSchemesAsPrivileged([
|
||||||
@ -18,12 +24,10 @@ protocol.registerSchemesAsPrivileged([
|
|||||||
async function createWindow() {
|
async function createWindow() {
|
||||||
// Create the browser window.
|
// Create the browser window.
|
||||||
const win = new BrowserWindow({
|
const win = new BrowserWindow({
|
||||||
width: 800,
|
width: 800, // 默认宽度(实际会被最大化覆盖)
|
||||||
height: 600,
|
height: 600, // 默认高度(实际会被最大化覆盖)
|
||||||
|
show: false, // 先隐藏窗口,避免闪烁
|
||||||
webPreferences: {
|
webPreferences: {
|
||||||
// 修改这一行
|
|
||||||
preload: require('path').join(__dirname, 'preload.js'),
|
|
||||||
|
|
||||||
// 改为使用绝对路径解析
|
// 改为使用绝对路径解析
|
||||||
preload: require('path').join(process.cwd(), 'src/preload.js'),
|
preload: require('path').join(process.cwd(), 'src/preload.js'),
|
||||||
nodeIntegration: false,
|
nodeIntegration: false,
|
||||||
@ -31,6 +35,11 @@ async function createWindow() {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// 在窗口显示前设置最大化
|
||||||
|
win.maximize();
|
||||||
|
// 然后显示窗口
|
||||||
|
win.show();
|
||||||
|
|
||||||
if (process.env.WEBPACK_DEV_SERVER_URL) {
|
if (process.env.WEBPACK_DEV_SERVER_URL) {
|
||||||
// Load the url of the dev server if in development mode
|
// Load the url of the dev server if in development mode
|
||||||
await win.loadURL(process.env.WEBPACK_DEV_SERVER_URL)
|
await win.loadURL(process.env.WEBPACK_DEV_SERVER_URL)
|
||||||
@ -119,6 +128,13 @@ app.on('ready', async () => {
|
|||||||
console.error('SQLite测试异常:', error);
|
console.error('SQLite测试异常:', error);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 初始化配置相关的IPC处理程序
|
||||||
|
initConfigIpc(ipcMain);
|
||||||
|
// 初始化字典相关的IPC处理程序
|
||||||
|
initDictIpc(ipcMain);
|
||||||
|
// 初始化试题相关的IPC处理程序
|
||||||
|
await initQuestionIpc(ipcMain);
|
||||||
|
|
||||||
createWindow();
|
createWindow();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
250
src/background/service/configService.js
Normal file
250
src/background/service/configService.js
Normal file
@ -0,0 +1,250 @@
|
|||||||
|
const {
|
||||||
|
getConfig,
|
||||||
|
setConfig,
|
||||||
|
getAllConfigs,
|
||||||
|
getConfigById,
|
||||||
|
deleteConfig,
|
||||||
|
} = require('../db/config.js');
|
||||||
|
const bcrypt = require('bcryptjs');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 服务层:获取配置项
|
||||||
|
* @param {string} key - 配置项键名
|
||||||
|
* @returns {Promise<{key: string, value: string} | null>}
|
||||||
|
*/
|
||||||
|
async function fetchConfig(key) {
|
||||||
|
try {
|
||||||
|
return await getConfig(key);
|
||||||
|
} catch (error) {
|
||||||
|
console.error("服务层: 获取配置项失败", error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 服务层:获取所有配置项
|
||||||
|
* @returns {Promise<Array<{id: number, key: string, value: string, protected: number}>>}
|
||||||
|
*/
|
||||||
|
async function fetchAllConfigs() {
|
||||||
|
try {
|
||||||
|
return await getAllConfigs();
|
||||||
|
} catch (error) {
|
||||||
|
console.error("服务层: 获取所有配置项失败", error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 服务层:通过ID获取配置项
|
||||||
|
* @param {number} id - 配置项ID
|
||||||
|
* @returns {Promise<{id: number, key: string, value: string, protected: number} | null>}
|
||||||
|
*/
|
||||||
|
async function fetchConfigById(id) {
|
||||||
|
try {
|
||||||
|
return await getConfigById(id);
|
||||||
|
} catch (error) {
|
||||||
|
console.error("服务层: 通过ID获取配置项失败", error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 服务层:保存配置项
|
||||||
|
* @param {string} key - 配置项键名
|
||||||
|
* @param {string} value - 配置项值
|
||||||
|
* @returns {Promise<void>}
|
||||||
|
*/
|
||||||
|
async function saveConfig(key, value) {
|
||||||
|
try {
|
||||||
|
await setConfig(key, value);
|
||||||
|
} catch (error) {
|
||||||
|
console.error("服务层: 保存配置项失败", error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 服务层:删除配置项
|
||||||
|
* @param {number} id - 配置项ID
|
||||||
|
* @returns {Promise<void>}
|
||||||
|
*/
|
||||||
|
async function removeConfig(id) {
|
||||||
|
try {
|
||||||
|
await deleteConfig(id);
|
||||||
|
} catch (error) {
|
||||||
|
console.error("服务层: 删除配置项失败", error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取系统配置并转为Map
|
||||||
|
* @returns {Promise<{[key: string]: string}>}
|
||||||
|
*/
|
||||||
|
async function getSystemConfig() {
|
||||||
|
try {
|
||||||
|
const configs = await getAllConfigs();
|
||||||
|
const configMap = {};
|
||||||
|
configs.forEach((config) => {
|
||||||
|
configMap[config.key] = config.value;
|
||||||
|
});
|
||||||
|
return configMap;
|
||||||
|
} catch (error) {
|
||||||
|
console.error("获取系统配置失败:", error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 批量更新系统配置
|
||||||
|
* @param {{[key: string]: string}} config - 配置对象
|
||||||
|
* @returns {Promise<boolean>}
|
||||||
|
*/
|
||||||
|
async function updateSystemConfig(config) {
|
||||||
|
try {
|
||||||
|
for (const [key, value] of Object.entries(config)) {
|
||||||
|
await setConfig(key, value);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
} catch (error) {
|
||||||
|
console.error("更新系统配置失败:", error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 增加题库版本号
|
||||||
|
* @returns {Promise<boolean>}
|
||||||
|
*/
|
||||||
|
async function increaseQuestionBankVersion() {
|
||||||
|
try {
|
||||||
|
const currentVersion = await getConfig("question_bank_version");
|
||||||
|
const newVersion = currentVersion ? parseInt(currentVersion.value) + 1 : 1;
|
||||||
|
await setConfig("question_bank_version", newVersion.toString());
|
||||||
|
return true;
|
||||||
|
} catch (error) {
|
||||||
|
console.error("增加题库版本号失败:", error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 管理员登录验证
|
||||||
|
* @param {string} password - 用户输入的密码
|
||||||
|
* @returns {Promise<{success: boolean, message: string}>}
|
||||||
|
*/
|
||||||
|
async function verifyAdminPassword(password) {
|
||||||
|
try {
|
||||||
|
const config = await getConfig("admin_password");
|
||||||
|
if (!config || !config.value) {
|
||||||
|
return { success: false, message: "管理员密码未设置" };
|
||||||
|
}
|
||||||
|
|
||||||
|
const isMatch = await bcrypt.compare(password, config.value);
|
||||||
|
if (isMatch) {
|
||||||
|
return { success: true, message: "登录成功" };
|
||||||
|
} else {
|
||||||
|
return { success: false, message: "密码错误" };
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error("验证管理员密码失败:", error);
|
||||||
|
return { success: false, message: "验证过程发生错误" };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 初始化配置相关的IPC处理程序
|
||||||
|
* @param {import('electron').IpcMain} ipcMain - IPC主进程实例
|
||||||
|
*/
|
||||||
|
function initConfigIpc(ipcMain) {
|
||||||
|
// 管理员登录验证
|
||||||
|
ipcMain.handle("admin-login", async (event, credentials) => {
|
||||||
|
try {
|
||||||
|
return await verifyAdminPassword(credentials.password);
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Failed to verify admin password:", error);
|
||||||
|
return { success: false, message: "验证过程发生错误" };
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 系统相关
|
||||||
|
ipcMain.handle("system-get-config", async () => {
|
||||||
|
try {
|
||||||
|
return await getSystemConfig();
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Failed to get system config:", error);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
ipcMain.handle("system-update-config", async (event, config) => {
|
||||||
|
try {
|
||||||
|
return await updateSystemConfig(config);
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Failed to update system config:", error);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
ipcMain.handle("system-increase-question-band-version", async () => {
|
||||||
|
try {
|
||||||
|
return await increaseQuestionBankVersion();
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Failed to increase question band version:", error);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 配置项管理相关IPC
|
||||||
|
ipcMain.handle("config-fetch-all", async () => {
|
||||||
|
try {
|
||||||
|
return await fetchAllConfigs();
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Failed to fetch all configs:", error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
ipcMain.handle("config-fetch-by-id", async (event, id) => {
|
||||||
|
try {
|
||||||
|
return await fetchConfigById(id);
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`Failed to fetch config by id ${id}:`, error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
ipcMain.handle("config-save", async (event, { key, value }) => {
|
||||||
|
try {
|
||||||
|
await saveConfig(key, value);
|
||||||
|
return true;
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`Failed to save config ${key}:`, error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
ipcMain.handle("config-delete", async (event, id) => {
|
||||||
|
try {
|
||||||
|
await removeConfig(id);
|
||||||
|
return true;
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`Failed to delete config ${id}:`, error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 导出使用CommonJS格式
|
||||||
|
module.exports = {
|
||||||
|
fetchConfig,
|
||||||
|
fetchAllConfigs,
|
||||||
|
fetchConfigById,
|
||||||
|
saveConfig,
|
||||||
|
removeConfig,
|
||||||
|
getSystemConfig,
|
||||||
|
updateSystemConfig,
|
||||||
|
increaseQuestionBankVersion,
|
||||||
|
verifyAdminPassword,
|
||||||
|
initConfigIpc
|
||||||
|
};
|
332
src/background/service/dictService.js
Normal file
332
src/background/service/dictService.js
Normal file
@ -0,0 +1,332 @@
|
|||||||
|
const {
|
||||||
|
getDictItemsWithTypes,
|
||||||
|
getDictTypes,
|
||||||
|
addDictType,
|
||||||
|
addDictItem,
|
||||||
|
getDictTypeById,
|
||||||
|
getDictItemById,
|
||||||
|
getDictItemsByTypeCode,
|
||||||
|
updateDictType,
|
||||||
|
updateDictItem,
|
||||||
|
deleteDictType,
|
||||||
|
deleteDictItem,
|
||||||
|
checkParentCodeExists,
|
||||||
|
hasChildReferences,
|
||||||
|
} = require('../db/dict.js');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 服务层:查询字典项及其类型
|
||||||
|
* @returns {Promise<Array>} 字典项列表
|
||||||
|
*/
|
||||||
|
async function fetchDictItemsWithTypes() {
|
||||||
|
try {
|
||||||
|
return await getDictItemsWithTypes();
|
||||||
|
} catch (error) {
|
||||||
|
console.error("服务层: 查询字典项及其类型失败", error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 服务层:查询字典类型列表
|
||||||
|
* @returns {Promise<Array>} 字典类型列表
|
||||||
|
*/
|
||||||
|
async function fetchDictTypes() {
|
||||||
|
try {
|
||||||
|
return await getDictTypes();
|
||||||
|
} catch (error) {
|
||||||
|
console.error("服务层: 查询字典类型列表失败", error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 服务层:添加字典类型
|
||||||
|
* @param {Object} typeData 字典类型数据
|
||||||
|
* @returns {Promise<Object>} 添加的字典类型
|
||||||
|
*/
|
||||||
|
async function createDictType(typeData) {
|
||||||
|
try {
|
||||||
|
return await addDictType(typeData);
|
||||||
|
} catch (error) {
|
||||||
|
console.error("服务层: 添加字典类型失败", error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 服务层:添加字典项
|
||||||
|
* @param {Object} itemData 字典项数据
|
||||||
|
* @returns {Promise<Object>} 添加的字典项
|
||||||
|
*/
|
||||||
|
async function createDictItem(itemData) {
|
||||||
|
try {
|
||||||
|
return await addDictItem(itemData);
|
||||||
|
} catch (error) {
|
||||||
|
console.error("服务层: 添加字典项失败", error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 服务层:根据ID查询字典类型
|
||||||
|
* @param {number} id 字典类型ID
|
||||||
|
* @returns {Promise<Object|null>} 字典类型数据
|
||||||
|
*/
|
||||||
|
async function fetchDictTypeById(id) {
|
||||||
|
try {
|
||||||
|
return await getDictTypeById(id);
|
||||||
|
} catch (error) {
|
||||||
|
console.error("服务层: 根据ID查询字典类型失败", error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 服务层:根据ID查询字典项
|
||||||
|
* @param {number} id 字典项ID
|
||||||
|
* @returns {Promise<Object|null>} 字典项数据
|
||||||
|
*/
|
||||||
|
async function fetchDictItemById(id) {
|
||||||
|
try {
|
||||||
|
return await getDictItemById(id);
|
||||||
|
} catch (error) {
|
||||||
|
console.error("服务层: 根据ID查询字典项失败", error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 服务层:根据类型编码查询字典项
|
||||||
|
* @param {string} typeCode 类型编码
|
||||||
|
* @param {number} isActive 是否激活(1=激活, 0=未激活, 不传=全部)
|
||||||
|
* @returns {Promise<Array>} 字典项列表
|
||||||
|
*/
|
||||||
|
async function fetchDictItemsByTypeCode(typeCode, isActive = undefined) {
|
||||||
|
try {
|
||||||
|
return await getDictItemsByTypeCode(typeCode, isActive);
|
||||||
|
} catch (error) {
|
||||||
|
console.error("服务层: 根据类型编码查询字典项失败", error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 服务层:更新字典类型
|
||||||
|
* @param {number} id 字典类型ID
|
||||||
|
* @param {Object} typeData 更新的数据
|
||||||
|
* @returns {Promise<boolean>} 是否更新成功
|
||||||
|
*/
|
||||||
|
async function modifyDictType(id, typeData) {
|
||||||
|
try {
|
||||||
|
return await updateDictType(id, typeData);
|
||||||
|
} catch (error) {
|
||||||
|
console.error("服务层: 更新字典类型失败", error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 服务层:更新字典项
|
||||||
|
* @param {number} id 字典项ID
|
||||||
|
* @param {Object} itemData 更新的数据
|
||||||
|
* @returns {Promise<boolean>} 是否更新成功
|
||||||
|
*/
|
||||||
|
async function modifyDictItem(id, itemData) {
|
||||||
|
try {
|
||||||
|
return await updateDictItem(id, itemData);
|
||||||
|
} catch (error) {
|
||||||
|
console.error("服务层: 更新字典项失败", error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 服务层:删除字典类型
|
||||||
|
* @param {number} id 字典类型ID
|
||||||
|
* @returns {Promise<boolean>} 是否删除成功
|
||||||
|
*/
|
||||||
|
async function removeDictType(id) {
|
||||||
|
try {
|
||||||
|
return await deleteDictType(id);
|
||||||
|
} catch (error) {
|
||||||
|
console.error("服务层: 删除字典类型失败", error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 服务层:删除字典项
|
||||||
|
* @param {number} id 字典项ID
|
||||||
|
* @returns {Promise<boolean>} 是否删除成功
|
||||||
|
*/
|
||||||
|
async function removeDictItem(id) {
|
||||||
|
try {
|
||||||
|
return await deleteDictItem(id);
|
||||||
|
} catch (error) {
|
||||||
|
console.error("服务层: 删除字典项失败", error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 服务层:检查父级编码是否存在
|
||||||
|
* @param {string} parentCode 父级编码
|
||||||
|
* @returns {Promise<boolean>} 是否存在
|
||||||
|
*/
|
||||||
|
async function checkDictParentCode(parentCode) {
|
||||||
|
try {
|
||||||
|
return await checkParentCodeExists(parentCode);
|
||||||
|
} catch (error) {
|
||||||
|
console.error("服务层: 检查父级编码失败", error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 服务层:检查是否有子引用
|
||||||
|
* @param {string} itemCode 字典项编码
|
||||||
|
* @returns {Promise<boolean>} 是否有子引用
|
||||||
|
*/
|
||||||
|
async function checkDictChildReferences(itemCode) {
|
||||||
|
try {
|
||||||
|
return await hasChildReferences(itemCode);
|
||||||
|
} catch (error) {
|
||||||
|
console.error("服务层: 检查子引用失败", error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 初始化字典相关的IPC处理程序
|
||||||
|
* @param {import('electron').IpcMain} ipcMain - IPC主进程实例
|
||||||
|
*/
|
||||||
|
function initDictIpc(ipcMain) {
|
||||||
|
// 字典管理相关IPC
|
||||||
|
ipcMain.handle("dict-fetch-types", async () => {
|
||||||
|
try {
|
||||||
|
return await fetchDictTypes();
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Failed to fetch dict types:", error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
ipcMain.handle(
|
||||||
|
"dict-fetch-items-by-type",
|
||||||
|
async (event, typeCode, isActive = undefined) => {
|
||||||
|
try {
|
||||||
|
return await fetchDictItemsByTypeCode(typeCode, isActive);
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`Failed to fetch dict items by type ${typeCode}:`, error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
ipcMain.handle("dict-create-type", async (event, dictType) => {
|
||||||
|
try {
|
||||||
|
return await createDictType(dictType);
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Failed to create dict type:", error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 将 updateDictType 改为 modifyDictType
|
||||||
|
ipcMain.handle("dict-update-type", async (event, dictType) => {
|
||||||
|
try {
|
||||||
|
return await modifyDictType(dictType.id, dictType);
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Failed to update dict type:", error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 将 deleteDictType 改为 removeDictType
|
||||||
|
ipcMain.handle("dict-delete-type", async (event, typeCode) => {
|
||||||
|
try {
|
||||||
|
return await removeDictType(typeCode);
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`Failed to delete dict type ${typeCode}:`, error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
ipcMain.handle("dict-create-item", async (event, dictItem) => {
|
||||||
|
try {
|
||||||
|
return await createDictItem(dictItem);
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Failed to create dict item:", error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 将 updateDictItem 改为 modifyDictItem
|
||||||
|
ipcMain.handle("dict-update-item", async (event, dictItem) => {
|
||||||
|
try {
|
||||||
|
return await modifyDictItem(dictItem.id, dictItem);
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Failed to update dict item:", error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 将 deleteDictItem 改为 removeDictItem
|
||||||
|
ipcMain.handle("dict-delete-item", async (event, id) => {
|
||||||
|
try {
|
||||||
|
return await removeDictItem(id);
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`Failed to delete dict item ${id}:`, error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 将 fetchAllDictItemsWithTypes 改为 fetchDictItemsWithTypes
|
||||||
|
ipcMain.handle("dict-fetch-all-items-with-types", async () => {
|
||||||
|
try {
|
||||||
|
return await fetchDictItemsWithTypes();
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Failed to fetch all dict items with types:", error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 检查parent_code是否存在
|
||||||
|
ipcMain.handle("dict-check-parent-code", async (event, parentCode) => {
|
||||||
|
try {
|
||||||
|
return await checkDictParentCode(parentCode);
|
||||||
|
} catch (error) {
|
||||||
|
console.error("检查parent_code失败:", error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 检查是否有子引用
|
||||||
|
ipcMain.handle("dict-check-child-references", async (event, itemCode) => {
|
||||||
|
try {
|
||||||
|
return await checkDictChildReferences(itemCode);
|
||||||
|
} catch (error) {
|
||||||
|
console.error("检查子引用失败:", error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 导出使用CommonJS格式
|
||||||
|
module.exports = {
|
||||||
|
fetchDictItemsWithTypes,
|
||||||
|
fetchDictTypes,
|
||||||
|
createDictType,
|
||||||
|
createDictItem,
|
||||||
|
fetchDictTypeById,
|
||||||
|
fetchDictItemById,
|
||||||
|
fetchDictItemsByTypeCode,
|
||||||
|
modifyDictType,
|
||||||
|
modifyDictItem,
|
||||||
|
removeDictType,
|
||||||
|
removeDictItem,
|
||||||
|
checkDictParentCode,
|
||||||
|
checkDictChildReferences,
|
||||||
|
initDictIpc
|
||||||
|
};
|
407
src/background/service/questionService.js
Normal file
407
src/background/service/questionService.js
Normal file
@ -0,0 +1,407 @@
|
|||||||
|
const {
|
||||||
|
addQuestion,
|
||||||
|
getAllQuestions,
|
||||||
|
getQuestionById,
|
||||||
|
updateQuestion,
|
||||||
|
deleteQuestion,
|
||||||
|
getAllQuestionsWithRelations,
|
||||||
|
updateQuestionDescription,
|
||||||
|
addChoiceQuestion,
|
||||||
|
updateChoiceQuestion,
|
||||||
|
addFillBlankQuestion,
|
||||||
|
updateFillBlankQuestion,
|
||||||
|
deleteFillBlankQuestion,
|
||||||
|
getFillBlankQuestionsByQuestionId,
|
||||||
|
getQuestionsCountAndScore
|
||||||
|
} = require('../db/question.js');
|
||||||
|
|
||||||
|
// 导入configService中的increaseQuestionBankVersion方法
|
||||||
|
const {
|
||||||
|
increaseQuestionBankVersion
|
||||||
|
} = require('./configService.js');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 服务层:添加题干
|
||||||
|
* @param {Object} questionData - 题干数据
|
||||||
|
* @returns {Promise<number>} 新创建的题干ID
|
||||||
|
*/
|
||||||
|
async function createQuestion(questionData) {
|
||||||
|
try {
|
||||||
|
const result = await addQuestion(questionData);
|
||||||
|
// 调用增加题库版本号的方法
|
||||||
|
await increaseQuestionBankVersion();
|
||||||
|
return result;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('服务层: 添加题干失败', error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 服务层:获取所有题干
|
||||||
|
* @returns {Promise<Array>} 题干列表
|
||||||
|
*/
|
||||||
|
async function fetchAllQuestions() {
|
||||||
|
try {
|
||||||
|
return await getAllQuestions();
|
||||||
|
} catch (error) {
|
||||||
|
console.error('服务层: 获取所有题干失败', error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 服务层:获取所有题干及其关联信息
|
||||||
|
* @returns {Promise<Array>} 包含关联信息的题干列表
|
||||||
|
*/
|
||||||
|
async function fetchAllQuestionsWithRelations() {
|
||||||
|
try {
|
||||||
|
return await getAllQuestionsWithRelations();
|
||||||
|
} catch (error) {
|
||||||
|
console.error('服务层: 获取所有题干及其关联信息失败', error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 服务层:根据ID获取题干
|
||||||
|
* @param {number} id - 题干ID
|
||||||
|
* @returns {Promise<Object|null>} 题干详情
|
||||||
|
*/
|
||||||
|
async function fetchQuestionById(id) {
|
||||||
|
try {
|
||||||
|
return await getQuestionById(id);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('服务层: 根据ID获取题干失败', error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 服务层:更新题干
|
||||||
|
* @param {number} id - 题干ID
|
||||||
|
* @param {Object} questionData - 题干数据
|
||||||
|
* @returns {Promise<boolean>} 是否更新成功
|
||||||
|
*/
|
||||||
|
async function modifyQuestion(id, questionData) {
|
||||||
|
try {
|
||||||
|
const result = await updateQuestion(id, questionData);
|
||||||
|
// 调用增加题库版本号的方法
|
||||||
|
await increaseQuestionBankVersion();
|
||||||
|
return result;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('服务层: 更新题干失败', error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 服务层:更新题干描述
|
||||||
|
* @param {number} id - 题干ID
|
||||||
|
* @param {string} questionDescription - 新的题干描述
|
||||||
|
* @returns {Promise<boolean>} 是否更新成功
|
||||||
|
*/
|
||||||
|
async function modifyQuestionDescription(id, questionDescription) {
|
||||||
|
try {
|
||||||
|
const result = await updateQuestionDescription(id, questionDescription);
|
||||||
|
// 调用增加题库版本号的方法
|
||||||
|
await increaseQuestionBankVersion();
|
||||||
|
return result;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('服务层: 更新题干描述失败', error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 服务层:删除题干
|
||||||
|
* @param {number} id - 题干ID
|
||||||
|
* @returns {Promise<boolean>} 是否删除成功
|
||||||
|
*/
|
||||||
|
async function removeQuestion(id) {
|
||||||
|
try {
|
||||||
|
const result = await deleteQuestion(id);
|
||||||
|
// 调用增加题库版本号的方法
|
||||||
|
await increaseQuestionBankVersion();
|
||||||
|
return result;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('服务层: 删除题干失败', error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 服务层:添加选择题问题
|
||||||
|
* @param {Object} choiceData - 选择题数据
|
||||||
|
* @returns {Promise<number>} 新创建的选择题ID
|
||||||
|
*/
|
||||||
|
async function createChoiceQuestion(choiceData) {
|
||||||
|
try {
|
||||||
|
const result = await addChoiceQuestion(choiceData);
|
||||||
|
// 调用增加题库版本号的方法
|
||||||
|
await increaseQuestionBankVersion();
|
||||||
|
return result;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('服务层: 添加选择题失败', error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 服务层:添加选择题问题
|
||||||
|
* @param {number} id - 选择题ID
|
||||||
|
* @param {Object} choiceData - 选择题数据
|
||||||
|
* @returns {Promise<boolean>} 是否更新成功
|
||||||
|
*/
|
||||||
|
async function modifyChoiceQuestion(id, choiceData) {
|
||||||
|
try {
|
||||||
|
const result = await updateChoiceQuestion(id, choiceData);
|
||||||
|
// 调用增加题库版本号的方法
|
||||||
|
await increaseQuestionBankVersion();
|
||||||
|
return result;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('服务层: 更新选择题失败', error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 服务层:添加填空题问题
|
||||||
|
* @param {Object} fillBlankData - 填空题数据
|
||||||
|
* @returns {Promise<number>} 新创建的填空题ID
|
||||||
|
*/
|
||||||
|
async function createFillBlankQuestion(fillBlankData) {
|
||||||
|
try {
|
||||||
|
const result = await addFillBlankQuestion(fillBlankData);
|
||||||
|
// 调用增加题库版本号的方法
|
||||||
|
await increaseQuestionBankVersion();
|
||||||
|
return result;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('服务层: 添加填空题失败', error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 服务层:更新填空题问题
|
||||||
|
* @param {number} id - 填空题ID
|
||||||
|
* @param {Object} fillBlankData - 填空题数据
|
||||||
|
* @returns {Promise<boolean>} 是否更新成功
|
||||||
|
*/
|
||||||
|
async function modifyFillBlankQuestion(id, fillBlankData) {
|
||||||
|
try {
|
||||||
|
const result = await updateFillBlankQuestion(id, fillBlankData);
|
||||||
|
// 调用增加题库版本号的方法
|
||||||
|
await increaseQuestionBankVersion();
|
||||||
|
return result;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('服务层: 更新填空题失败', error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 服务层:删除填空题问题
|
||||||
|
* @param {number} id - 填空题ID
|
||||||
|
* @returns {Promise<boolean>} 是否删除成功
|
||||||
|
*/
|
||||||
|
async function removeFillBlankQuestion(id) {
|
||||||
|
try {
|
||||||
|
const result = await deleteFillBlankQuestion(id);
|
||||||
|
// 调用增加题库版本号的方法
|
||||||
|
await increaseQuestionBankVersion();
|
||||||
|
return result;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('服务层: 删除填空题失败', error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 服务层:根据题干ID查询填空题问题
|
||||||
|
* @param {number} questionId - 题干ID
|
||||||
|
* @returns {Promise<Array>} 填空题列表
|
||||||
|
*/
|
||||||
|
async function fetchFillBlankQuestionsByQuestionId(questionId) {
|
||||||
|
try {
|
||||||
|
return await getFillBlankQuestionsByQuestionId(questionId);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('服务层: 根据题干ID查询填空题失败', error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 服务层:查询试题总数和总分
|
||||||
|
* @returns {Promise<{totalQuestions: number, totalScore: number}>} 包含试题总数和总分的对象
|
||||||
|
*/
|
||||||
|
async function fetchQuestionsCountAndScore() {
|
||||||
|
try {
|
||||||
|
return await getQuestionsCountAndScore();
|
||||||
|
} catch (error) {
|
||||||
|
console.error('服务层: 查询试题总数和总分失败', error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 初始化试题相关IPC通信
|
||||||
|
* @param {Electron.IpcMain} ipcMain - IPC主进程实例
|
||||||
|
*/
|
||||||
|
async function initQuestionIpc(ipcMain) {
|
||||||
|
// 题干管理相关IPC
|
||||||
|
ipcMain.handle("question-create", async (event, questionData) => {
|
||||||
|
try {
|
||||||
|
return await createQuestion(questionData);
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Failed to create question:", error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
ipcMain.handle("question-fetch-all", async () => {
|
||||||
|
try {
|
||||||
|
return await fetchAllQuestions();
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Failed to fetch questions:", error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
ipcMain.handle("question-fetch-by-id", async (event, id) => {
|
||||||
|
try {
|
||||||
|
return await fetchQuestionById(id);
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`Failed to fetch question by id ${id}:`, error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
ipcMain.handle("question-update", async (event, id, questionData) => {
|
||||||
|
try {
|
||||||
|
return await modifyQuestion(id, questionData);
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Failed to update question:", error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
ipcMain.handle("question-delete", async (event, id) => {
|
||||||
|
try {
|
||||||
|
return await removeQuestion(id);
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`Failed to delete question ${id}:`, error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 在已有的 question 相关 IPC 处理程序区域添加
|
||||||
|
ipcMain.handle("question-fetch-all-with-relations", async (event) => {
|
||||||
|
try {
|
||||||
|
return await fetchAllQuestionsWithRelations();
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Failed to fetch all questions with relations:", error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 添加更新题干描述的 IPC 处理程序
|
||||||
|
ipcMain.handle("question-update-description", async (event, id, questionDescription) => {
|
||||||
|
try {
|
||||||
|
return await modifyQuestionDescription(id, questionDescription);
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Failed to update question description:", error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
// 添加选择题问题的IPC处理程序
|
||||||
|
ipcMain.handle('question-update-choice', async (event, id, choiceData) => {
|
||||||
|
try {
|
||||||
|
return await modifyChoiceQuestion(id, choiceData);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to update choice question:', error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 添加选择题问题的IPC处理程序
|
||||||
|
ipcMain.handle("question-create-choice", async (event, choiceData) => {
|
||||||
|
try {
|
||||||
|
return await createChoiceQuestion(choiceData);
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Failed to create choice question:", error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 添加填空题问题的IPC处理程序
|
||||||
|
ipcMain.handle('question-create-fill-blank', async (event, fillBlankData) => {
|
||||||
|
try {
|
||||||
|
return await createFillBlankQuestion(fillBlankData);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to create fill blank question:', error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 更新填空题问题的IPC处理程序
|
||||||
|
ipcMain.handle('question-update-fill-blank', async (event, id, fillBlankData) => {
|
||||||
|
try {
|
||||||
|
return await modifyFillBlankQuestion(id, fillBlankData);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to update fill blank question:', error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 删除填空题问题的IPC处理程序
|
||||||
|
ipcMain.handle('question-delete-fill-blank', async (event, id) => {
|
||||||
|
try {
|
||||||
|
return await removeFillBlankQuestion(id);
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`Failed to delete fill blank question ${id}:`, error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 根据题干ID查询填空题问题的IPC处理程序
|
||||||
|
ipcMain.handle('question-fetch-fill-blank-by-question-id', async (event, questionId) => {
|
||||||
|
try {
|
||||||
|
return await fetchFillBlankQuestionsByQuestionId(questionId);
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`Failed to fetch fill blank questions by question id ${questionId}:`, error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 注册查询试题总数和总分的IPC处理程序
|
||||||
|
ipcMain.handle('question-get-count-and-score', async () => {
|
||||||
|
try {
|
||||||
|
return await fetchQuestionsCountAndScore();
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to fetch questions count and score:', error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 导出使用CommonJS格式
|
||||||
|
module.exports = {
|
||||||
|
createQuestion,
|
||||||
|
fetchAllQuestions,
|
||||||
|
fetchAllQuestionsWithRelations,
|
||||||
|
fetchQuestionById,
|
||||||
|
modifyQuestion,
|
||||||
|
modifyQuestionDescription,
|
||||||
|
removeQuestion,
|
||||||
|
createChoiceQuestion,
|
||||||
|
modifyChoiceQuestion,
|
||||||
|
createFillBlankQuestion,
|
||||||
|
modifyFillBlankQuestion,
|
||||||
|
removeFillBlankQuestion,
|
||||||
|
fetchFillBlankQuestionsByQuestionId,
|
||||||
|
fetchQuestionsCountAndScore,
|
||||||
|
initQuestionIpc
|
||||||
|
};
|
@ -7,6 +7,46 @@ contextBridge.exposeInMainWorld('electronAPI', {
|
|||||||
checkDatabaseInitialized: () => ipcRenderer.invoke('check-database-initialized'),
|
checkDatabaseInitialized: () => ipcRenderer.invoke('check-database-initialized'),
|
||||||
initializeDatabase: () => ipcRenderer.invoke('initialize-database'),
|
initializeDatabase: () => ipcRenderer.invoke('initialize-database'),
|
||||||
|
|
||||||
|
// 配置服务相关接口
|
||||||
|
adminLogin: (credentials) => ipcRenderer.invoke('admin-login', credentials),
|
||||||
|
systemGetConfig: () => ipcRenderer.invoke('system-get-config'),
|
||||||
|
systemUpdateConfig: (config) => ipcRenderer.invoke('system-update-config', config),
|
||||||
|
systemIncreaseQuestionBankVersion: () => ipcRenderer.invoke('system-increase-question-band-version'),
|
||||||
|
configFetchAll: () => ipcRenderer.invoke('config-fetch-all'),
|
||||||
|
configFetchById: (id) => ipcRenderer.invoke('config-fetch-by-id', id),
|
||||||
|
configSave: (key, value) => ipcRenderer.invoke('config-save', { key, value }),
|
||||||
|
configDelete: (id) => ipcRenderer.invoke('config-delete', id),
|
||||||
|
|
||||||
|
// 字典服务相关接口
|
||||||
|
dictFetchTypes: () => ipcRenderer.invoke('dict-fetch-types'),
|
||||||
|
dictFetchItemsByType: (typeCode, isActive = undefined) => ipcRenderer.invoke('dict-fetch-items-by-type', typeCode, isActive),
|
||||||
|
dictCreateType: (dictType) => ipcRenderer.invoke('dict-create-type', dictType),
|
||||||
|
dictUpdateType: (dictType) => ipcRenderer.invoke('dict-update-type', dictType),
|
||||||
|
dictDeleteType: (typeCode) => ipcRenderer.invoke('dict-delete-type', typeCode),
|
||||||
|
dictCreateItem: (dictItem) => ipcRenderer.invoke('dict-create-item', dictItem),
|
||||||
|
dictUpdateItem: (dictItem) => ipcRenderer.invoke('dict-update-item', dictItem),
|
||||||
|
dictDeleteItem: (id) => ipcRenderer.invoke('dict-delete-item', id),
|
||||||
|
dictFetchAllItemsWithTypes: () => ipcRenderer.invoke('dict-fetch-all-items-with-types'),
|
||||||
|
dictCheckParentCode: (parentCode) => ipcRenderer.invoke('dict-check-parent-code', parentCode),
|
||||||
|
dictCheckChildReferences: (itemCode) => ipcRenderer.invoke('dict-check-child-references', itemCode),
|
||||||
|
|
||||||
|
// 试题服务相关接口
|
||||||
|
questionCreate: (questionData) => ipcRenderer.invoke('question-create', questionData),
|
||||||
|
questionFetchAll: () => ipcRenderer.invoke('question-fetch-all'),
|
||||||
|
questionFetchAllWithRelations: () => ipcRenderer.invoke('question-fetch-all-with-relations'),
|
||||||
|
questionUpdateDescription: (questionData) => ipcRenderer.invoke('question-update-description', questionData),
|
||||||
|
questionAddChoice: (choiceData) => ipcRenderer.invoke('question-add-choice', choiceData),
|
||||||
|
questionUpdateChoice: (choiceData) => ipcRenderer.invoke('question-update-choice', choiceData),
|
||||||
|
questionDeleteChoice: (id) => ipcRenderer.invoke('question-delete-choice', id),
|
||||||
|
questionAddFillBlank: (fillBlankData) => ipcRenderer.invoke('question-add-fill-blank', fillBlankData),
|
||||||
|
questionUpdateFillBlank: (fillBlankData) => ipcRenderer.invoke('question-update-fill-blank', fillBlankData),
|
||||||
|
questionDeleteFillBlank: (id) => ipcRenderer.invoke('question-delete-fill-blank', id),
|
||||||
|
questionGetQuestionWithChoices: (questionId) => ipcRenderer.invoke('question-get-question-with-choices', questionId),
|
||||||
|
questionGetQuestionWithFillBlanks: (questionId) => ipcRenderer.invoke('question-get-question-with-fill-blanks', questionId),
|
||||||
|
questionRemove: (questionId) => ipcRenderer.invoke('question-remove', questionId),
|
||||||
|
questionGetStatistics: () => ipcRenderer.invoke('question-get-statistics'),
|
||||||
|
questionGetQuestionById: (questionId) => ipcRenderer.invoke('question-get-question-by-id', questionId),
|
||||||
|
|
||||||
// 保留原有的ipcRenderer接口,确保兼容性
|
// 保留原有的ipcRenderer接口,确保兼容性
|
||||||
ipcRenderer: {
|
ipcRenderer: {
|
||||||
invoke: (channel, data) => ipcRenderer.invoke(channel, data)
|
invoke: (channel, data) => ipcRenderer.invoke(channel, data)
|
||||||
|
@ -130,6 +130,68 @@
|
|||||||
</el-card>
|
</el-card>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- 配置列表显示 -->
|
||||||
|
<div class="config-list-test" v-if="dbInitialized">
|
||||||
|
<h3>配置列表</h3>
|
||||||
|
<el-card class="card-test">
|
||||||
|
<template slot="header">
|
||||||
|
<div class="card-header">
|
||||||
|
<span>系统配置项</span>
|
||||||
|
<el-button type="primary" size="small" @click="fetchConfigList">刷新列表</el-button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<div v-if="loadingConfigList" class="loading-container">
|
||||||
|
<el-row type="flex" justify="center">
|
||||||
|
<el-col :span="6">
|
||||||
|
<el-empty description="正在加载配置..." />
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-else-if="configList.length === 0" class="empty-container">
|
||||||
|
<el-row type="flex" justify="center">
|
||||||
|
<el-col :span="6">
|
||||||
|
<el-empty description="暂无配置项" />
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-else class="config-table-container">
|
||||||
|
<el-table :data="configList" stripe border style="width: 100%">
|
||||||
|
<el-table-column prop="id" label="ID" width="80" align="center" />
|
||||||
|
<el-table-column prop="key" label="配置键" min-width="180" />
|
||||||
|
<el-table-column prop="value" label="配置值" min-width="200">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<div v-if="editingConfigId === scope.row.id">
|
||||||
|
<el-input v-model="configEdit[scope.row.id]" placeholder="配置值" />
|
||||||
|
</div>
|
||||||
|
<div v-else class="config-value-display">
|
||||||
|
{{ scope.row.value }}
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="description" label="描述" min-width="250" />
|
||||||
|
<el-table-column prop="created_at" label="创建时间" width="180" align="center" />
|
||||||
|
<el-table-column prop="updated_at" label="更新时间" width="180" align="center" />
|
||||||
|
<el-table-column label="操作" width="150" align="center">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<el-button v-if="editingConfigId !== scope.row.id" type="primary" size="small" @click="startEditConfig(scope.row)">编辑</el-button>
|
||||||
|
<div v-else>
|
||||||
|
<el-button type="success" size="small" @click="saveConfig(scope.row)">保存</el-button>
|
||||||
|
<el-button size="small" @click="cancelEdit">取消</el-button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-if="configListError" class="config-list-error">
|
||||||
|
<el-alert :title="`获取配置列表失败: ${configListError}`" type="error" show-icon :closable="false" />
|
||||||
|
</div>
|
||||||
|
</el-card>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- Bootstrap组件测试 -->
|
<!-- Bootstrap组件测试 -->
|
||||||
<div class="bootstrap-test">
|
<div class="bootstrap-test">
|
||||||
<h3>Bootstrap 组件测试</h3>
|
<h3>Bootstrap 组件测试</h3>
|
||||||
@ -260,7 +322,13 @@ export default {
|
|||||||
dbInitialized: null,
|
dbInitialized: null,
|
||||||
dbInitResult: null,
|
dbInitResult: null,
|
||||||
dbInitError: null,
|
dbInitError: null,
|
||||||
initializing: false
|
initializing: false,
|
||||||
|
// 配置列表相关状态
|
||||||
|
configList: [],
|
||||||
|
loadingConfigList: false,
|
||||||
|
configListError: null,
|
||||||
|
editingConfigId: null,
|
||||||
|
configEdit: {}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
@ -336,6 +404,8 @@ export default {
|
|||||||
// 如果初始化成功,更新状态
|
// 如果初始化成功,更新状态
|
||||||
if (result && result.success) {
|
if (result && result.success) {
|
||||||
this.dbInitialized = true
|
this.dbInitialized = true
|
||||||
|
// 初始化成功后立即获取配置列表
|
||||||
|
await this.fetchConfigList()
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
throw new Error('Electron API 不可用')
|
throw new Error('Electron API 不可用')
|
||||||
@ -346,12 +416,82 @@ export default {
|
|||||||
} finally {
|
} finally {
|
||||||
this.initializing = false
|
this.initializing = false
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// 获取配置列表
|
||||||
|
async fetchConfigList() {
|
||||||
|
try {
|
||||||
|
this.loadingConfigList = true
|
||||||
|
this.configListError = null
|
||||||
|
|
||||||
|
// 安全地检查 window.electronAPI 是否可用
|
||||||
|
if (typeof window !== 'undefined' && window.electronAPI) {
|
||||||
|
const configList = await window.electronAPI.configFetchAll()
|
||||||
|
this.configList = configList
|
||||||
|
} else {
|
||||||
|
throw new Error('Electron API 不可用')
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
this.configListError = err.message
|
||||||
|
console.error('获取配置列表失败:', err)
|
||||||
|
} finally {
|
||||||
|
this.loadingConfigList = false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// 开始编辑配置
|
||||||
|
startEditConfig(config) {
|
||||||
|
this.editingConfigId = config.id
|
||||||
|
// 存储当前值用于编辑
|
||||||
|
this.$set(this.configEdit, config.id, config.value)
|
||||||
|
},
|
||||||
|
|
||||||
|
// 保存配置
|
||||||
|
async saveConfig(config) {
|
||||||
|
try {
|
||||||
|
// 安全地检查 window.electronAPI 是否可用
|
||||||
|
if (typeof window !== 'undefined' && window.electronAPI) {
|
||||||
|
const updatedConfig = {
|
||||||
|
id: config.id,
|
||||||
|
key: config.key,
|
||||||
|
value: this.configEdit[config.id],
|
||||||
|
description: config.description
|
||||||
|
}
|
||||||
|
|
||||||
|
await window.electronAPI.configSave(updatedConfig)
|
||||||
|
|
||||||
|
// 保存成功后更新本地数据
|
||||||
|
const index = this.configList.findIndex(item => item.id === config.id)
|
||||||
|
if (index !== -1) {
|
||||||
|
this.configList[index].value = this.configEdit[config.id]
|
||||||
|
this.configList[index].updated_at = new Date().toLocaleString()
|
||||||
|
}
|
||||||
|
|
||||||
|
this.$message.success('配置保存成功')
|
||||||
|
this.cancelEdit()
|
||||||
|
} else {
|
||||||
|
throw new Error('Electron API 不可用')
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
this.$message.error(`保存失败: ${err.message}`)
|
||||||
|
console.error('保存配置失败:', err)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// 取消编辑
|
||||||
|
cancelEdit() {
|
||||||
|
this.editingConfigId = null
|
||||||
|
this.configEdit = {}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
// 组件挂载后检查数据库状态
|
// 组件挂载后检查数据库状态
|
||||||
async mounted() {
|
async mounted() {
|
||||||
await this.checkDatabaseInitialized()
|
await this.checkDatabaseInitialized()
|
||||||
|
// 数据库初始化成功后,获取配置列表
|
||||||
|
if (this.dbInitialized) {
|
||||||
|
await this.fetchConfigList()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
@ -460,4 +600,28 @@ pre {
|
|||||||
.bootstrap-test .btn {
|
.bootstrap-test .btn {
|
||||||
margin-bottom: 5px;
|
margin-bottom: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 配置列表样式 */
|
||||||
|
.config-list-test {
|
||||||
|
margin-top: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.loading-container,
|
||||||
|
.empty-container {
|
||||||
|
padding: 40px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.config-table-container {
|
||||||
|
margin-top: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.config-list-error {
|
||||||
|
margin-top: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 配置值显示样式 */
|
||||||
|
.config-value-display {
|
||||||
|
padding: 5px 0;
|
||||||
|
word-break: break-all;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
Loading…
Reference in New Issue
Block a user