From f12b9b7291b2a83ad2a5ab1f90b80f3bc35b9479 Mon Sep 17 00:00:00 2001 From: yuyx <237899745@qq.com> Date: Thu, 27 Nov 2025 23:02:48 +0800 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20=E6=B7=BB=E5=8A=A0=E4=B8=BB?= =?UTF-8?q?=E9=A2=98=E5=88=87=E6=8D=A2=E5=8A=9F=E8=83=BD=EF=BC=9A=E6=94=AF?= =?UTF-8?q?=E6=8C=81=E6=9A=97=E8=89=B2/=E4=BA=AE=E8=89=B2=E7=8E=BB?= =?UTF-8?q?=E7=92=83=E4=B8=BB=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 功能说明: - 管理员可在系统设置中配置全局默认主题 - 普通用户可在设置页面选择:跟随全局/暗色/亮色 - 分享页面自动继承分享者的主题偏好 - 主题设置实时保存,刷新后保持 技术实现: - 后端:数据库添加theme_preference字段,新增主题API - 前端:CSS变量实现主题切换,localStorage缓存避免闪烁 - 分享页:加载时获取分享者主题设置 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- backend/database.js | 23 +++++++++- backend/server.js | 104 +++++++++++++++++++++++++++++++++++++++++++- frontend/app.html | 97 +++++++++++++++++++++++++++++++++++++++-- frontend/app.js | 103 ++++++++++++++++++++++++++++++++++++++++++- frontend/share.html | 55 ++++++++++++++++++++++- 5 files changed, 374 insertions(+), 8 deletions(-) diff --git a/backend/database.js b/backend/database.js index 7c429b9..679be1b 100644 --- a/backend/database.js +++ b/backend/database.js @@ -446,7 +446,7 @@ const ShareDB = { }); const result = db.prepare(` - SELECT s.*, u.username, u.ftp_host, u.ftp_port, u.ftp_user, u.ftp_password, u.http_download_base_url + SELECT s.*, u.username, u.ftp_host, u.ftp_port, u.ftp_user, u.ftp_password, u.http_download_base_url, u.theme_preference FROM shares s JOIN users u ON s.user_id = u.id WHERE s.share_code = ? @@ -616,6 +616,26 @@ function initDefaultSettings() { if (!SettingsDB.get('max_upload_size')) { SettingsDB.set('max_upload_size', '10737418240'); // 10GB in bytes } + // 默认全局主题为暗色 + if (!SettingsDB.get('global_theme')) { + SettingsDB.set('global_theme', 'dark'); + } +} + +// 数据库迁移 - 主题偏好字段 +function migrateThemePreference() { + try { + const columns = db.prepare("PRAGMA table_info(users)").all(); + const hasThemePreference = columns.some(col => col.name === 'theme_preference'); + + if (!hasThemePreference) { + console.log('[数据库迁移] 添加主题偏好字段...'); + db.exec(`ALTER TABLE users ADD COLUMN theme_preference TEXT DEFAULT NULL`); + console.log('[数据库迁移] ✓ 主题偏好字段已添加'); + } + } catch (error) { + console.error('[数据库迁移] 主题偏好迁移失败:', error); + } } // 数据库版本迁移 - v2.0 本地存储功能 @@ -798,6 +818,7 @@ initDatabase(); createDefaultAdmin(); initDefaultSettings(); migrateToV2(); // 执行数据库迁移 +migrateThemePreference(); // 主题偏好迁移 module.exports = { db, diff --git a/backend/server.js b/backend/server.js index 5935d5a..d4041d7 100644 --- a/backend/server.js +++ b/backend/server.js @@ -1628,6 +1628,56 @@ app.get('/api/user/profile', authMiddleware, (req, res) => { }); }); +// 获取用户主题偏好(包含全局默认主题) +app.get('/api/user/theme', authMiddleware, (req, res) => { + try { + const globalTheme = SettingsDB.get('global_theme') || 'dark'; + const userTheme = req.user.theme_preference; // null表示跟随全局 + + res.json({ + success: true, + theme: { + global: globalTheme, + user: userTheme, + effective: userTheme || globalTheme // 用户设置优先,否则使用全局 + } + }); + } catch (error) { + res.status(500).json({ success: false, message: '获取主题失败' }); + } +}); + +// 设置用户主题偏好 +app.post('/api/user/theme', authMiddleware, (req, res) => { + try { + const { theme } = req.body; + const validThemes = ['dark', 'light', null]; // null表示跟随全局 + + if (!validThemes.includes(theme)) { + return res.status(400).json({ + success: false, + message: '无效的主题设置,可选: dark, light, null(跟随全局)' + }); + } + + UserDB.update(req.user.id, { theme_preference: theme }); + + const globalTheme = SettingsDB.get('global_theme') || 'dark'; + res.json({ + success: true, + message: '主题偏好已更新', + theme: { + global: globalTheme, + user: theme, + effective: theme || globalTheme + } + }); + } catch (error) { + console.error('更新主题失败:', error); + res.status(500).json({ success: false, message: '更新主题失败' }); + } +}); + // 更新FTP配置 app.post('/api/user/update-ftp', authMiddleware, @@ -2888,6 +2938,44 @@ app.delete('/api/share/:id', authMiddleware, (req, res) => { // ===== 分享链接访问(公开) ===== +// 获取公共主题设置(用于分享页面,无需认证) +app.get('/api/public/theme', (req, res) => { + try { + const globalTheme = SettingsDB.get('global_theme') || 'dark'; + res.json({ + success: true, + theme: globalTheme + }); + } catch (error) { + res.json({ success: true, theme: 'dark' }); // 出错默认暗色 + } +}); + +// 获取分享页面主题(基于分享者偏好或全局设置) +app.get('/api/share/:code/theme', (req, res) => { + try { + const { code } = req.params; + const share = ShareDB.findByCode(code); + const globalTheme = SettingsDB.get('global_theme') || 'dark'; + + if (!share) { + return res.json({ + success: true, + theme: globalTheme + }); + } + + // 优先使用分享者的主题偏好,否则使用全局主题 + const effectiveTheme = share.theme_preference || globalTheme; + res.json({ + success: true, + theme: effectiveTheme + }); + } catch (error) { + res.json({ success: true, theme: 'dark' }); + } +}); + // 访问分享链接 - 验证密码(支持本地存储和SFTP) app.post('/api/share/:code/verify', shareRateLimitMiddleware, async (req, res) => { const { code } = req.params; @@ -3418,11 +3506,13 @@ app.get('/api/admin/settings', authMiddleware, adminMiddleware, (req, res) => { const smtpUser = SettingsDB.get('smtp_user'); const smtpFrom = SettingsDB.get('smtp_from') || smtpUser; const smtpHasPassword = !!SettingsDB.get('smtp_password'); + const globalTheme = SettingsDB.get('global_theme') || 'dark'; res.json({ success: true, settings: { max_upload_size: maxUploadSize, + global_theme: globalTheme, smtp: { host: smtpHost || '', port: smtpPort ? parseInt(smtpPort, 10) : 465, @@ -3445,7 +3535,7 @@ app.get('/api/admin/settings', authMiddleware, adminMiddleware, (req, res) => { // 更新系统设置 app.post('/api/admin/settings', authMiddleware, adminMiddleware, (req, res) => { try { - const { max_upload_size, smtp } = req.body; + const { max_upload_size, smtp, global_theme } = req.body; if (max_upload_size !== undefined) { const size = parseInt(max_upload_size); @@ -3458,6 +3548,18 @@ app.post('/api/admin/settings', authMiddleware, adminMiddleware, (req, res) => { SettingsDB.set('max_upload_size', size.toString()); } + // 更新全局主题 + if (global_theme !== undefined) { + const validThemes = ['dark', 'light']; + if (!validThemes.includes(global_theme)) { + return res.status(400).json({ + success: false, + message: '无效的主题设置' + }); + } + SettingsDB.set('global_theme', global_theme); + } + if (smtp) { if (!smtp.host || !smtp.port || !smtp.user) { return res.status(400).json({ success: false, message: 'SMTP配置不完整' }); diff --git a/frontend/app.html b/frontend/app.html index 96cdd87..be6469d 100644 --- a/frontend/app.html +++ b/frontend/app.html @@ -107,7 +107,7 @@