1
0
mirror of https://github.com/ZeroCatDev/Classworks.git synced 2025-07-05 02:59:23 +00:00
Classworks/src/utils/settings.js
SunWuyuan 6b3ee0074e
1
2025-03-15 16:09:06 +08:00

393 lines
9.9 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* 配置项定义
* @typedef {Object} SettingDefinition
* @property {string} type - 配置项类型 ('boolean' | 'number' | 'string')
* @property {any} default - 默认值
* @property {Function} [validate] - 可选的验证函数
* @property {string} [description] - 配置项描述
* @property {string} [legacyKey] - 旧版本localStorage键名(用于迁移)
* @property {boolean} [requireDeveloper] - 是否需要开发者选项启用
*/
// 存储所有设置的localStorage键名
const SETTINGS_STORAGE_KEY = "classworks_settings";
/**
* 所有配置项的定义
* @type {Object.<string, SettingDefinition>}
*/
const settingsDefinitions = {
// 显示设置
"display.emptySubjectDisplay": {
type: "string",
default: "button", // 修改默认值为 'button'
validate: (value) => ["card", "button"].includes(value),
description: "空科目的显示方式:卡片或按钮",
},
"display.dynamicSort": {
type: "boolean",
default: true,
description: "是否启用动态排序以优化显示效果",
},
"display.showRandomButton": {
type: "boolean",
default: false,
description: "是否显示随机按钮",
},
// 服务器设置(合并了数据提供者设置)
"server.domain": {
type: "string",
default: "",
validate: (value) => !value || /^https?:\/\//.test(value),
description: "后端服务器域名",
},
"server.classNumber": {
type: "string",
default: "",
validate: (value) => /^[A-Za-z0-9]*$/.test(value),
description: "班级编号(无论使用哪种存储方式都需要设置)",
},
"server.provider": {
type: "string",
default: "indexedDB",
validate: (value) =>
["server", "localStorage", "indexedDB"].includes(value),
description: "数据提供者,用于决定数据存储方式",
},
// 刷新设置
"refresh.auto": {
type: "boolean",
default: false,
description: "是否启用自动刷新",
},
"refresh.interval": {
type: "number",
default: 300,
validate: (value) => value >= 10 && value <= 3600,
description: "自动刷新间隔(秒)",
},
// 字体设置
"font.size": {
type: "number",
default: 28,
validate: (value) => value >= 16 && value <= 100,
description: "字体大小(像素)",
},
// 编辑设置
"edit.autoSave": {
type: "boolean",
default: true,
description: "是否启用自动保存",
},
"edit.blockNonTodayAutoSave": { // 添加新选项
type: "boolean",
default: true,
description: "禁止自动保存非当天数据",
},
"edit.refreshBeforeEdit": {
type: "boolean",
default: true,
description: "编辑前是否自动刷新",
},
"edit.confirmNonTodaySave": { // 添加新选项
type: "boolean",
default: true,
description: "保存非当天数据时显示确认对话框,禁用则允许直接保存",
},
// 开发者选项
"developer.enabled": {
type: "boolean",
default: false,
description: "是否启用开发者选项",
},
"developer.showDebugConfig": {
type: "boolean",
default: false,
description: "是否显示调试配置",
},
"developer.disableMessageLog": { // 添加新的设置项
type: "boolean",
default: false,
description: "禁用消息日志记录",
requireDeveloper: true,
},
// 消息设置
"message.showSidebar": {
type: "boolean",
default: true,
description: "是否显示消息记录侧栏",
requireDeveloper: true, // 添加标记
},
"message.maxActiveMessages": {
type: "number",
default: 5,
validate: (value) => value >= 1 && value <= 10,
description: "同时显示的最大消息数量",
requireDeveloper: true,
},
"message.timeout": {
type: "number",
default: 5000,
validate: (value) => value >= 1000 && value <= 30000,
description: "消息自动关闭时间(毫秒)",
requireDeveloper: true,
},
"message.saveHistory": {
type: "boolean",
default: true,
description: "是否保存消息历史记录",
requireDeveloper: true,
},
};
// 内存中缓存的设置值
let settingsCache = null;
/**
* 从localStorage加载所有设置
* @returns {Object} 所有设置的值
*/
function loadSettings() {
try {
const stored = localStorage.getItem(SETTINGS_STORAGE_KEY);
if (stored) {
settingsCache = JSON.parse(stored);
} else {
// 首次使用或迁移旧数据
settingsCache = migrateFromLegacy();
}
} catch (error) {
console.error("加载设置失败:", error);
settingsCache = {};
}
// 确保所有设置项都有值(使用默认值填充)
for (const [key, definition] of Object.entries(settingsDefinitions)) {
if (!(key in settingsCache)) {
settingsCache[key] = definition.default;
}
}
return settingsCache;
}
/**
* 从旧版本的localStorage迁移数据
*/
function migrateFromLegacy() {
const LEGACY_SETTINGS_KEY = "homeworkpage_settings";
const LEGACY_MESSAGE_KEY = "homeworkpage_messages";
// 尝试从旧版本的设置中迁移
const legacySettings = localStorage.getItem(LEGACY_SETTINGS_KEY);
if (legacySettings) {
try {
const settings = JSON.parse(legacySettings);
localStorage.setItem(SETTINGS_STORAGE_KEY, JSON.stringify(settings));
// 可选:删除旧的设置
localStorage.removeItem(LEGACY_SETTINGS_KEY);
return settings;
} catch (error) {
console.error("迁移旧设置失败:", error);
}
}
// 尝试从旧版本的message中迁移
const legacyMessages = localStorage.getItem(LEGACY_MESSAGE_KEY);
if (legacyMessages) {
try {
const messages = JSON.parse(legacyMessages);
localStorage.setItem(SETTINGS_STORAGE_KEY, JSON.stringify(messages));
// 可选删除旧的message
localStorage.removeItem(LEGACY_MESSAGE_KEY);
return messages; // 返回迁移后的消息
} catch (error) {
console.error("迁移旧消息失败:", error);
}
}
// 如果没有旧设置或迁移失败,返回空对象
return {};
}
/**
* 保存所有设置到localStorage
*/
function saveSettings() {
try {
localStorage.setItem(SETTINGS_STORAGE_KEY, JSON.stringify(settingsCache));
} catch (error) {
console.error("保存设置失败:", error);
}
}
/**
* 获取设置项的值
* @param {string} key - 设置项键名
* @returns {any} 设置项的值
*/
function getSetting(key) {
if (!settingsCache) {
loadSettings();
}
const definition = settingsDefinitions[key];
if (!definition) {
console.warn(`未定义的设置项: ${key}`);
return null;
}
// 确保开发者相关设置正确处理
if (definition.requireDeveloper) {
const devEnabled = settingsCache['developer.enabled'];
if (!devEnabled) {
return definition.default;
}
}
const value = settingsCache[key];
return value !== undefined ? value : definition.default;
}
// 修改 logSettingsChange 函数,优化检查逻辑
function logSettingsChange(key, oldValue, newValue) {
// 确保设置已加载
if (!settingsCache) {
loadSettings();
}
const shouldLog =
settingsCache['developer.enabled'] &&
settingsCache['developer.showDebugConfig'];
if (shouldLog) {
console.log(`[Settings] ${key}:`, {
old: oldValue,
new: newValue,
time: new Date().toLocaleTimeString()
});
}
}
/**
* 设置配置项的值
* @param {string} key - 设置项键名
* @param {any} value - 要设置的值
* @returns {boolean} 是否设置成功
*/
function setSetting(key, value) {
const definition = settingsDefinitions[key];
if (!definition) {
console.warn(`未定义的设置项: ${key}`);
return false;
}
// 添加对开发者选项依赖的检查
if (definition.requireDeveloper && !settingsCache["developer.enabled"]) {
console.warn(`设置项 ${key} 需要启用开发者选项`);
return false;
}
try {
const oldValue = settingsCache[key];
// 类型转换
if (typeof value !== definition.type) {
value =
definition.type === "boolean"
? Boolean(value)
: definition.type === "number"
? Number(value)
: String(value);
}
// 验证
if (definition.validate && !definition.validate(value)) {
console.warn(`设置项 ${key} 的值无效`);
return false;
}
if (!settingsCache) {
loadSettings();
}
settingsCache[key] = value;
saveSettings();
logSettingsChange(key, oldValue, value);
// 为了保持向后兼容同时更新旧的localStorage键
const legacyKey = definition.legacyKey;
if (legacyKey) {
localStorage.setItem(legacyKey, value.toString());
}
return true;
} catch (error) {
console.error(`设置配置项 ${key} 失败:`, error);
return false;
}
}
/**
* 重置指定设置项到默认值
* @param {string} key - 设置项键名
*/
function resetSetting(key) {
const definition = settingsDefinitions[key];
if (!definition) {
console.warn(`未定义的设置项: ${key}`);
return;
}
if (!settingsCache) {
loadSettings();
}
settingsCache[key] = definition.default;
saveSettings();
}
/**
* 重置所有设置项到默认值
*/
function resetAllSettings() {
settingsCache = {};
for (const [key, definition] of Object.entries(settingsDefinitions)) {
settingsCache[key] = definition.default;
}
saveSettings();
}
/**
* 监听设置变化
* @param {Function} callback - 当设置改变时调用的回调函数
* @returns {Function} 取消监听的函数
*/
function watchSettings(callback) {
const handler = (event) => {
if (event.key === SETTINGS_STORAGE_KEY) {
settingsCache = JSON.parse(event.newValue);
callback(settingsCache);
}
};
window.addEventListener("storage", handler);
return () => window.removeEventListener("storage", handler);
}
// 初始化设置
loadSettings();
export {
settingsDefinitions,
getSetting,
setSetting,
resetSetting,
resetAllSettings,
watchSettings,
};