From c6b6bd2ce5d58a7aa78cde435839ed543e5f5158 Mon Sep 17 00:00:00 2001 From: SunWuyuan Date: Sat, 15 Mar 2025 09:11:11 +0800 Subject: [PATCH] 1 --- README.md | 2 +- package.json | 2 +- src/components/AppFooter.vue | 6 +- src/components/settings/AboutCard.vue | 8 +- src/pages/index.vue | 4 +- src/utils/dataProvider.js | 163 +++++++++--------- src/utils/message.js | 2 +- src/utils/settings.js | 228 +++++++++++++------------- 8 files changed, 211 insertions(+), 204 deletions(-) diff --git a/README.md b/README.md index 64bca29..3c4d77b 100644 --- a/README.md +++ b/README.md @@ -77,4 +77,4 @@ This project is built with [Vuetify](https://vuetifyjs.com/en/), a UI Library wi [MIT](http://opensource.org/licenses/MIT) Copyright (c) 2016-present Vuetify, LLC -# homeworkpage-frontend +# classworks-frontend diff --git a/package.json b/package.json index e993c0b..e9e2ac4 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,5 @@ { - "name": "HomeworkPage", + "name": "ClassworkS", "private": true, "type": "module", "version": "0.0.0", diff --git a/src/components/AppFooter.vue b/src/components/AppFooter.vue index 6745251..1d6fee6 100644 --- a/src/components/AppFooter.vue +++ b/src/components/AppFooter.vue @@ -23,11 +23,11 @@ — - HomeworkPage + ClassworkS @@ -48,7 +48,7 @@ { title: 'GitHub', icon: 'mdi-github', - href: 'https://github.com/sunwuyuan/homeworkpage-frontend', + href: 'https://github.com/sunwuyuan/classworks-frontend', } ] diff --git a/src/components/settings/AboutCard.vue b/src/components/settings/AboutCard.vue index 6d3984c..cde1a4e 100644 --- a/src/components/settings/AboutCard.vue +++ b/src/components/settings/AboutCard.vue @@ -17,7 +17,7 @@ /> -

HomeworkPage

+

ClassworkS

@@ -39,7 +39,7 @@ @@ -48,7 +48,7 @@ diff --git a/src/pages/index.vue b/src/pages/index.vue index 7db1812..6cc4fb7 100644 --- a/src/pages/index.vue +++ b/src/pages/index.vue @@ -589,7 +589,7 @@ export default { this.provider, this.dataKey, this.state.boardData, - this.state.dateString + this.state.dateString // 添加dateString参数 ); if (!response.success) { @@ -937,7 +937,7 @@ export default { async saveAttendance() { try { - await this.uploadData(); + await this.uploadData(); // 现在会自动使用当前选中的日期 this.state.attendanceDialog = false; } catch (error) { console.error("保存出勤状态失败:", error); diff --git a/src/utils/dataProvider.js b/src/utils/dataProvider.js index 0ed59ef..6d34790 100644 --- a/src/utils/dataProvider.js +++ b/src/utils/dataProvider.js @@ -1,17 +1,17 @@ -import axios from 'axios'; -import { openDB } from 'idb'; +import axios from "axios"; +import { openDB } from "idb"; -const DB_NAME = 'HomeworkDB'; +const DB_NAME = "HomeworkDB"; const DB_VERSION = 1; const initDB = async () => { return openDB(DB_NAME, DB_VERSION, { upgrade(db) { - if (!db.objectStoreNames.contains('homework')) { - db.createObjectStore('homework'); + if (!db.objectStoreNames.contains("homework")) { + db.createObjectStore("homework"); } - if (!db.objectStoreNames.contains('config')) { - db.createObjectStore('config'); + if (!db.objectStoreNames.contains("config")) { + db.createObjectStore("config"); } }, }); @@ -20,12 +20,12 @@ const initDB = async () => { const formatResponse = (data, message = null) => ({ success: true, data, - message + message, }); -const formatError = (message, code = 'UNKNOWN_ERROR') => ({ +const formatError = (message, code = "UNKNOWN_ERROR") => ({ success: false, - error: { code, message } + error: { code, message }, }); const providers = { @@ -33,9 +33,9 @@ const providers = { async loadData(key, date) { try { // 检查是否设置了班号 - const classNumber = key.split('/').pop(); + const classNumber = key.split("/").pop(); if (!classNumber) { - return formatError('请先设置班号', 'CONFIG_ERROR'); + return formatError("请先设置班号", "CONFIG_ERROR"); } // 使用班号作为本地存储的前缀 @@ -44,45 +44,44 @@ const providers = { if (!rawData) { // 如果是今天的数据且没有找到,返回空结构而不是null - const today = new Date().toISOString().split('T')[0]; + const today = new Date().toISOString().split("T")[0]; if (date === today) { return formatResponse({ homework: {}, - attendance: { absent: [], late: [] } + attendance: { absent: [], late: [] }, }); } - return formatError('数据不存在', 'NOT_FOUND'); + return formatError("数据不存在", "NOT_FOUND"); } return formatResponse(JSON.parse(rawData)); } catch (error) { - - return formatError('读取本地数据失败:'+error); + return formatError("读取本地数据失败:" + error); } }, async saveData(key, data, date) { try { // 检查是否设置了班号 - const classNumber = key.split('/').pop(); + const classNumber = key.split("/").pop(); if (!classNumber) { - return formatError('请先设置班号', 'CONFIG_ERROR'); + return formatError("请先设置班号", "CONFIG_ERROR"); } // 使用班号作为本地存储的前缀 - const storageKey = `homework_${classNumber}_${date}`; + const storageKey = `homework_${classNumber}_${date}`; // 使用传入的date参数 localStorage.setItem(storageKey, JSON.stringify(data)); - return formatResponse(null, '保存成功'); + return formatResponse(null, "保存成功"); } catch (error) { - return formatError('保存本地数据失败:'+error); + return formatError("保存本地数据失败:" + error); } }, async loadConfig(key) { try { - const classNumber = key.split('/').pop(); + const classNumber = key.split("/").pop(); if (!classNumber) { - return formatError('请先设置班号', 'CONFIG_ERROR'); + return formatError("请先设置班号", "CONFIG_ERROR"); } const storageKey = `config_${classNumber}`; @@ -91,30 +90,30 @@ const providers = { if (!rawData) { return formatResponse({ studentList: [], - displayOptions: {} + displayOptions: {}, }); } return formatResponse(JSON.parse(rawData)); } catch (error) { - return formatError('读取本地配置失败:'+error); + return formatError("读取本地配置失败:" + error); } }, async saveConfig(key, config) { try { - const classNumber = key.split('/').pop(); + const classNumber = key.split("/").pop(); if (!classNumber) { - return formatError('请先设置班号', 'CONFIG_ERROR'); + return formatError("请先设置班号", "CONFIG_ERROR"); } const storageKey = `config_${classNumber}`; localStorage.setItem(storageKey, JSON.stringify(config)); - return formatResponse(null, '保存成功'); + return formatResponse(null, "保存成功"); } catch (error) { - return formatError('保存本地配置失败:'+error); + return formatError("保存本地配置失败:" + error); } - } + }, }, server: { @@ -122,25 +121,27 @@ const providers = { try { const res = await axios.get(`${key}/homework?date=${date}`); if (res.data?.status === false) { - return formatError(res.data.msg || '获取数据失败', 'SERVER_ERROR'); + return formatError(res.data.msg || "获取数据失败", "SERVER_ERROR"); } return formatResponse(res.data); } catch (error) { return formatError( - error.response?.data?.message || '服务器连接失败', - 'NETWORK_ERROR' + error.response?.data?.message || "服务器连接失败", + "NETWORK_ERROR" ); } }, - async saveData(key, data) { + async saveData(key, data, date) { try { - await axios.post(`${key}/homework`, data); - return formatResponse(null, '保存成功'); + // 添加date参数到URL + const url = date ? `${key}/homework?date=${date}` : `${key}/homework`; + await axios.post(url, data); + return formatResponse(null, "保存成功"); } catch (error) { return formatError( - error.response?.data?.message || '保存失败', - 'SAVE_ERROR' + error.response?.data?.message || "保存失败", + "SAVE_ERROR" ); } }, @@ -149,13 +150,13 @@ const providers = { try { const res = await axios.get(`${key}/config`); if (res.data?.status === false) { - return formatError(res.data.msg || '获取配置失败', 'SERVER_ERROR'); + return formatError(res.data.msg || "获取配置失败", "SERVER_ERROR"); } return formatResponse(res.data); } catch (error) { return formatError( - error.response?.data?.message || '服务器连接失败', - 'NETWORK_ERROR' + error.response?.data?.message || "服务器连接失败", + "NETWORK_ERROR" ); } }, @@ -164,121 +165,123 @@ const providers = { try { const res = await axios.put(`${key}/config`, config); if (res.data?.status === false) { - return formatError(res.data.msg || '保存失败', 'SAVE_ERROR'); + return formatError(res.data.msg || "保存失败", "SAVE_ERROR"); } - return formatResponse(null, '保存成功'); + return formatResponse(null, "保存成功"); } catch (error) { return formatError( - error.response?.data?.message || '保存失败', - 'SAVE_ERROR' + error.response?.data?.message || "保存失败", + "SAVE_ERROR" ); } - } + }, }, indexedDB: { async loadData(key, date) { try { - const classNumber = key.split('/').pop(); + const classNumber = key.split("/").pop(); if (!classNumber) { - return formatError('请先设置班号', 'CONFIG_ERROR'); + return formatError("请先设置班号", "CONFIG_ERROR"); } const db = await initDB(); const storageKey = `homework_${classNumber}_${date}`; - const data = await db.get('homework', storageKey); + const data = await db.get("homework", storageKey); if (!data) { - const today = new Date().toISOString().split('T')[0]; + const today = new Date().toISOString().split("T")[0]; if (date === today) { return formatResponse({ homework: {}, - attendance: { absent: [], late: [] } + attendance: { absent: [], late: [] }, }); } - return formatError('数据不存在', 'NOT_FOUND'); + return formatError("数据不存在", "NOT_FOUND"); } // 从字符串解析数据 return formatResponse(JSON.parse(data)); } catch (error) { - return formatError('读取IndexedDB数据失败:' + error); + return formatError("读取IndexedDB数据失败:" + error); } }, async saveData(key, data, date) { try { - const classNumber = key.split('/').pop(); + const classNumber = key.split("/").pop(); if (!classNumber) { - return formatError('请先设置班号', 'CONFIG_ERROR'); + return formatError("请先设置班号", "CONFIG_ERROR"); } const db = await initDB(); - const storageKey = `homework_${classNumber}_${date}`; + const storageKey = `homework_${classNumber}_${date}`; // 使用传入的date参数 // 将数据序列化为字符串存储 - await db.put('homework', JSON.stringify(data), storageKey); - return formatResponse(null, '保存成功'); + await db.put("homework", JSON.stringify(data), storageKey); + return formatResponse(null, "保存成功"); } catch (error) { - return formatError('保存IndexedDB数据失败:' + error); + return formatError("保存IndexedDB数据失败:" + error); } }, async loadConfig(key) { try { - const classNumber = key.split('/').pop(); + const classNumber = key.split("/").pop(); if (!classNumber) { - return formatError('请先设置班号', 'CONFIG_ERROR'); + return formatError("请先设置班号", "CONFIG_ERROR"); } const db = await initDB(); const storageKey = `config_${classNumber}`; - const config = await db.get('config', storageKey); + const config = await db.get("config", storageKey); if (!config) { return formatResponse({ studentList: [], - displayOptions: {} + displayOptions: {}, }); } // 从字符串解析配置 return formatResponse(JSON.parse(config)); } catch (error) { - return formatError('读取IndexedDB配置失败:' + error); + return formatError("读取IndexedDB配置失败:" + error); } }, async saveConfig(key, config) { try { - const classNumber = key.split('/').pop(); + const classNumber = key.split("/").pop(); if (!classNumber) { - return formatError('请先设置班号', 'CONFIG_ERROR'); + return formatError("请先设置班号", "CONFIG_ERROR"); } const db = await initDB(); const storageKey = `config_${classNumber}`; // 将配置序列化为字符串存储 - await db.put('config', JSON.stringify(config), storageKey); - return formatResponse(null, '保存成功'); + await db.put("config", JSON.stringify(config), storageKey); + return formatResponse(null, "保存成功"); } catch (error) { - return formatError('保存IndexedDB配置失败:' + error); + return formatError("保存IndexedDB配置失败:" + error); } - } - } + }, + }, }; export default { loadData: (provider, key, date) => providers[provider]?.loadData(key, date), - saveData: (provider, key, data, date) => providers[provider]?.saveData(key, data, date), + saveData: (provider, key, data, date) => + providers[provider]?.saveData(key, data, date), loadConfig: (provider, key) => providers[provider]?.loadConfig(key), - saveConfig: (provider, key, config) => providers[provider]?.saveConfig(key, config) + saveConfig: (provider, key, config) => + providers[provider]?.saveConfig(key, config), }; export const ErrorCodes = { - NOT_FOUND: '数据不存在', - NETWORK_ERROR: '网络连接失败', - SERVER_ERROR: '服务器错误', - SAVE_ERROR: '保存失败', - CONFIG_ERROR: '配置错误', - UNKNOWN_ERROR: '未知错误' + NOT_FOUND: "数据不存在", + NETWORK_ERROR: "网络连接失败", + SERVER_ERROR: "服务器错误", + SAVE_ERROR: "保存失败", + CONFIG_ERROR: "配置错误", + UNKNOWN_ERROR: "未知错误", }; diff --git a/src/utils/message.js b/src/utils/message.js index 4d73f98..d0052dc 100644 --- a/src/utils/message.js +++ b/src/utils/message.js @@ -15,7 +15,7 @@ const defaultOptions = { addToLog: true }; -const STORAGE_KEY = 'homeworkpage_messages'; +const STORAGE_KEY = 'classworks_messages'; const MAX_MESSAGES = 100; // 最大消息数量 const MAX_STORAGE_SIZE = 1024 * 1024; // 1MB 存储限制 diff --git a/src/utils/settings.js b/src/utils/settings.js index 9782044..3c26ffc 100644 --- a/src/utils/settings.js +++ b/src/utils/settings.js @@ -10,7 +10,7 @@ */ // 存储所有设置的localStorage键名 -const SETTINGS_STORAGE_KEY = 'homeworkpage_settings'; +const SETTINGS_STORAGE_KEY = "classworks_settings"; /** * 所有配置项的定义 @@ -18,115 +18,116 @@ const SETTINGS_STORAGE_KEY = 'homeworkpage_settings'; */ const settingsDefinitions = { // 显示设置 - 'display.emptySubjectDisplay': { - type: 'string', - default: 'button', // 修改默认值为 'button' - validate: value => ['card', 'button'].includes(value), - description: '空科目的显示方式:卡片或按钮' + "display.emptySubjectDisplay": { + type: "string", + default: "button", // 修改默认值为 'button' + validate: (value) => ["card", "button"].includes(value), + description: "空科目的显示方式:卡片或按钮", }, - 'display.dynamicSort': { - type: 'boolean', + "display.dynamicSort": { + type: "boolean", default: true, - description: '是否启用动态排序以优化显示效果' + description: "是否启用动态排序以优化显示效果", }, - 'display.showRandomButton': { - type: 'boolean', + "display.showRandomButton": { + type: "boolean", default: false, - description: '是否显示随机按钮' + description: "是否显示随机按钮", }, // 服务器设置(合并了数据提供者设置) - 'server.domain': { - type: 'string', - default: '', - validate: value => !value || /^https?:\/\//.test(value), - 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.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: '数据提供者,用于决定数据存储方式' + "server.provider": { + type: "string", + default: "indexedDB", + validate: (value) => + ["server", "localStorage", "indexedDB"].includes(value), + description: "数据提供者,用于决定数据存储方式", }, // 刷新设置 - 'refresh.auto': { - type: 'boolean', + "refresh.auto": { + type: "boolean", default: false, - description: '是否启用自动刷新' + description: "是否启用自动刷新", }, - 'refresh.interval': { - type: 'number', + "refresh.interval": { + type: "number", default: 300, - validate: value => value >= 10 && value <= 3600, - description: '自动刷新间隔(秒)' + validate: (value) => value >= 10 && value <= 3600, + description: "自动刷新间隔(秒)", }, // 字体设置 - 'font.size': { - type: 'number', + "font.size": { + type: "number", default: 28, - validate: value => value >= 16 && value <= 100, - description: '字体大小(像素)' + validate: (value) => value >= 16 && value <= 100, + description: "字体大小(像素)", }, // 编辑设置 - 'edit.autoSave': { - type: 'boolean', + "edit.autoSave": { + type: "boolean", default: true, - description: '是否启用自动保存' + description: "是否启用自动保存", }, - 'edit.refreshBeforeEdit': { - type: 'boolean', + "edit.refreshBeforeEdit": { + type: "boolean", default: true, - description: '编辑前是否自动刷新' + description: "编辑前是否自动刷新", }, // 开发者选项 - 'developer.enabled': { - type: 'boolean', + "developer.enabled": { + type: "boolean", default: false, - description: '是否启用开发者选项' + description: "是否启用开发者选项", }, - 'developer.showDebugConfig': { - type: 'boolean', + "developer.showDebugConfig": { + type: "boolean", default: false, - description: '是否显示调试配置' + description: "是否显示调试配置", }, // 消息设置 - 'message.showSidebar': { - type: 'boolean', + "message.showSidebar": { + type: "boolean", default: true, - description: '是否显示消息记录侧栏', - requireDeveloper: true // 添加标记 + description: "是否显示消息记录侧栏", + requireDeveloper: true, // 添加标记 }, - 'message.maxActiveMessages': { - type: 'number', + "message.maxActiveMessages": { + type: "number", default: 5, - validate: value => value >= 1 && value <= 10, - description: '同时显示的最大消息数量', - requireDeveloper: true + validate: (value) => value >= 1 && value <= 10, + description: "同时显示的最大消息数量", + requireDeveloper: true, }, - 'message.timeout': { - type: 'number', + "message.timeout": { + type: "number", default: 5000, - validate: value => value >= 1000 && value <= 30000, - description: '消息自动关闭时间(毫秒)', - requireDeveloper: true + validate: (value) => value >= 1000 && value <= 30000, + description: "消息自动关闭时间(毫秒)", + requireDeveloper: true, }, - 'message.saveHistory': { - type: 'boolean', + "message.saveHistory": { + type: "boolean", default: true, - description: '是否保存消息历史记录', - requireDeveloper: true - } + description: "是否保存消息历史记录", + requireDeveloper: true, + }, }; // 内存中缓存的设置值 @@ -146,7 +147,7 @@ function loadSettings() { settingsCache = migrateFromLegacy(); } } catch (error) { - console.error('加载设置失败:', error); + console.error("加载设置失败:", error); settingsCache = {}; } @@ -164,42 +165,38 @@ function loadSettings() { * 从旧版本的localStorage迁移数据 */ function migrateFromLegacy() { - const settings = {}; - const legacyKeyMap = { - 'server.domain': 'backendServerDomain', - 'server.classNumber': 'classNumber', - 'refresh.auto': 'autoRefresh', - 'refresh.interval': 'refreshInterval', - 'font.size': 'fontSize', - 'edit.autoSave': 'autoSave', - 'edit.refreshBeforeEdit': 'refreshBeforeEdit', - 'display.emptySubjectDisplay': 'emptySubjectDisplay', - 'display.dynamicSort': 'dynamicSort' - }; + const LEGACY_SETTINGS_KEY = "homeworkpage_settings"; + const LEGACY_MESSAGE_KEY = "homeworkpage_messages"; - // 迁移旧数据 - for (const [newKey, oldKey] of Object.entries(legacyKeyMap)) { - const oldValue = localStorage.getItem(oldKey); - if (oldValue !== null) { - const definition = settingsDefinitions[newKey]; - switch (definition.type) { - case 'boolean': - settings[newKey] = oldValue === 'true'; - break; - case 'number': - settings[newKey] = Number(oldValue); - break; - default: - settings[newKey] = oldValue; - } - // 可选:删除旧的localStorage项 - // localStorage.removeItem(oldKey); + // 尝试从旧版本的设置中迁移 + 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); } } - // 保存迁移后的数据 - localStorage.setItem(SETTINGS_STORAGE_KEY, JSON.stringify(settings)); - return settings; + // 如果没有旧设置或迁移失败,返回空对象 + return {}; } /** @@ -209,7 +206,7 @@ function saveSettings() { try { localStorage.setItem(SETTINGS_STORAGE_KEY, JSON.stringify(settingsCache)); } catch (error) { - console.error('保存设置失败:', error); + console.error("保存设置失败:", error); } } @@ -230,7 +227,7 @@ function getSetting(key) { } // 添加对开发者选项依赖的检查 - if (definition.requireDeveloper && !settingsCache['developer.enabled']) { + if (definition.requireDeveloper && !settingsCache["developer.enabled"]) { return definition.default; } @@ -240,11 +237,14 @@ function getSetting(key) { // 添加设置变更日志函数 function logSettingsChange(key, oldValue, newValue) { - if (settingsCache['developer.enabled'] && settingsCache['developer.showDebugConfig']) { + if ( + settingsCache["developer.enabled"] && + settingsCache["developer.showDebugConfig"] + ) { console.log(`[Settings] ${key}:`, { old: oldValue, new: newValue, - time: new Date().toLocaleTimeString() + time: new Date().toLocaleTimeString(), }); } } @@ -263,7 +263,7 @@ function setSetting(key, value) { } // 添加对开发者选项依赖的检查 - if (definition.requireDeveloper && !settingsCache['developer.enabled']) { + if (definition.requireDeveloper && !settingsCache["developer.enabled"]) { console.warn(`设置项 ${key} 需要启用开发者选项`); return false; } @@ -272,8 +272,12 @@ function setSetting(key, value) { const oldValue = settingsCache[key]; // 类型转换 if (typeof value !== definition.type) { - value = definition.type === 'boolean' ? Boolean(value) : - definition.type === 'number' ? Number(value) : String(value); + value = + definition.type === "boolean" + ? Boolean(value) + : definition.type === "number" + ? Number(value) + : String(value); } // 验证 @@ -346,8 +350,8 @@ function watchSettings(callback) { } }; - window.addEventListener('storage', handler); - return () => window.removeEventListener('storage', handler); + window.addEventListener("storage", handler); + return () => window.removeEventListener("storage", handler); } // 初始化设置 @@ -359,5 +363,5 @@ export { setSetting, resetSetting, resetAllSettings, - watchSettings + watchSettings, };