添加主题切换功能:支持暗色/亮色玻璃主题

功能说明:
- 管理员可在系统设置中配置全局默认主题
- 普通用户可在设置页面选择:跟随全局/暗色/亮色
- 分享页面自动继承分享者的主题偏好
- 主题设置实时保存,刷新后保持

技术实现:
- 后端:数据库添加theme_preference字段,新增主题API
- 前端:CSS变量实现主题切换,localStorage缓存避免闪烁
- 分享页:加载时获取分享者主题设置

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-11-27 23:02:48 +08:00
parent 138bda9ae5
commit f12b9b7291
5 changed files with 374 additions and 8 deletions

View File

@@ -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配置不完整' });