From 90f48adaeee3ad4a1e2cb802f2d4529c61b31b55 Mon Sep 17 00:00:00 2001 From: xdqw166 Date: Mon, 17 Nov 2025 16:09:13 +0800 Subject: [PATCH 1/2] =?UTF-8?q?=E5=8A=9F=E8=83=BD=E5=A2=9E=E5=8A=A0?= =?UTF-8?q?=E5=8F=98=E6=8D=A2=E8=83=8C=E6=99=AF=E9=A2=9C=E8=89=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/DjangoBlog/blog/static/blog/css/theme.css | 598 ++++++++++++++++++ .../blog/static/blog/js/theme-switcher.js | 132 ++++ .../templates/share_layout/base.html | 99 ++- 3 files changed, 813 insertions(+), 16 deletions(-) create mode 100644 src/DjangoBlog/blog/static/blog/css/theme.css create mode 100644 src/DjangoBlog/blog/static/blog/js/theme-switcher.js diff --git a/src/DjangoBlog/blog/static/blog/css/theme.css b/src/DjangoBlog/blog/static/blog/css/theme.css new file mode 100644 index 0000000..29ceeac --- /dev/null +++ b/src/DjangoBlog/blog/static/blog/css/theme.css @@ -0,0 +1,598 @@ +/* + * DjangoBlog 主题切换样式 + * theme.css - 深色/浅色主题支持 + * 适配 DjangoBlog 实际类名 + */ + +:root { + /* ====== 浅色主题变量 ====== */ + --bg-primary: #ffffff; + --bg-secondary: #f8f9fa; + --bg-tertiary: #e9ecef; + --bg-card: #ffffff; + --text-primary: #333333; + --text-secondary: #666666; + --text-muted: #999999; + --border-color: #dee2e6; + --link-color: #007bff; + --link-hover-color: #0056b3; + --code-bg: #f8f9fa; + --blockquote-border: #e9ecef; + --shadow: 0 2px 4px rgba(0, 0, 0, 0.1); + --transition: all 0.3s ease; + + /* 导航栏 */ + --nav-bg: #f8f9fa; + --nav-text: #333333; + --nav-border: #dee2e6; + + /* 按钮 */ + --btn-primary-bg: #007bff; + --btn-primary-text: #ffffff; + --btn-secondary-bg: #6c757d; + --btn-secondary-text: #ffffff; + + /* 输入框 */ + --input-bg: #ffffff; + --input-text: #333333; + --input-border: #ced4da; + --input-focus-border: #007bff; +} + +[data-theme="dark"] { + /* ====== 深色主题变量 ====== */ + --bg-primary: #1a1a1a; + --bg-secondary: #2d2d2d; + --bg-tertiary: #404040; + --bg-card: #2d2d2d; + --text-primary: #e9ecef; + --text-secondary: #adb5bd; + --text-muted: #6c757d; + --border-color: #495057; + --link-color: #4dabf7; + --link-hover-color: #74c0fc; + --code-bg: #2d2d2d; + --blockquote-border: #495057; + --shadow: 0 2px 4px rgba(0, 0, 0, 0.3); + + /* 导航栏 */ + --nav-bg: #2d2d2d; + --nav-text: #e9ecef; + --nav-border: #495057; + + /* 按钮 */ + --btn-primary-bg: #4dabf7; + --btn-primary-text: #ffffff; + --btn-secondary-bg: #6c757d; + --btn-secondary-text: #ffffff; + + /* 输入框 */ + --input-bg: #2d2d2d; + --input-text: #e9ecef; + --input-border: #495057; + --input-focus-border: #4dabf7; +} + +/* ====== 基础元素样式 ====== */ +body { + background-color: var(--bg-primary); + color: var(--text-primary); + transition: var(--transition); + min-height: 100vh; +} + +/* ====== DjangoBlog 特定样式适配 ====== */ + +/* 站点头部 */ +.site-header { + background-color: var(--nav-bg) !important; + border-bottom: 1px solid var(--nav-border); +} + +.site-title a, +.site-description { + color: var(--nav-text) !important; +} + +/* 主导航 */ +.main-navigation { + background-color: var(--nav-bg); + border-color: var(--nav-border); +} + +.main-navigation a { + color: var(--nav-text) !important; +} + +.main-navigation a:hover { + color: var(--link-hover-color) !important; +} + +/* 主要内容区域 */ +#page { + background-color: var(--bg-primary); +} + +#main { + background-color: var(--bg-primary); +} + +/* 文章和卡片样式 */ +.entry-content, +.entry-header, +.entry-summary, +.type-post, +.widget, +.comment-list, +.comment-body { + background-color: var(--bg-card); + color: var(--text-primary); + border-color: var(--border-color); +} + +/* 文章标题 */ +.entry-title, +.entry-title a { + color: var(--text-primary) !important; +} + +.entry-title a:hover { + color: var(--link-hover-color) !important; +} + +/* 元信息 */ +.entry-meta, +.comment-metadata { + color: var(--text-muted) !important; +} + +/* 小工具 */ +.widget-title { + color: var(--text-primary); + border-bottom-color: var(--border-color); +} + +/* 链接 */ +a { + color: var(--link-color); +} + +a:hover { + color: var(--link-hover-color); +} + +/* 代码块 */ +pre, code { + background-color: var(--code-bg) !important; + color: var(--text-primary) !important; + border-color: var(--border-color); +} + +/* 引用 */ +blockquote { + border-left-color: var(--blockquote-border); + background-color: var(--bg-secondary); + color: var(--text-secondary); +} + +/* 表格 */ +table { + background-color: var(--bg-card); + color: var(--text-primary); + border-color: var(--border-color); +} + +th, td { + border-color: var(--border-color); + background-color: var(--bg-card); + color: var(--text-primary); +} + +/* 表单 */ +input, textarea, select { + background-color: var(--input-bg); + border-color: var(--input-border); + color: var(--input-text); +} + +input:focus, textarea:focus, select:focus { + background-color: var(--input-bg); + border-color: var(--input-focus-border); + color: var(--input-text); +} + +/* 按钮 */ +button, .button, input[type="submit"], input[type="button"] { + background-color: var(--btn-primary-bg); + color: var(--btn-primary-text); + border-color: var(--btn-primary-bg); +} + +/* 页脚 */ +.site-footer { + background-color: var(--bg-secondary) !important; + border-top-color: var(--border-color); + color: var(--text-secondary); +} + +/* ====== 主题切换按钮样式 ====== */ +.theme-toggle-btn { + background: var(--bg-secondary); + border: 1px solid var(--border-color); + border-radius: 50%; + width: 40px; + height: 40px; + cursor: pointer; + display: flex; + align-items: center; + justify-content: center; + font-size: 1.2rem; + transition: var(--transition); + color: var(--text-primary); + margin-left: 10px; +} + +.theme-toggle-btn:hover { + transform: scale(1.1); + background-color: var(--bg-tertiary); + border-color: var(--link-color); +} + +.theme-toggle-btn:focus { + outline: none; + box-shadow: 0 0 0 2px var(--link-color); +} + +/* ====== 导航栏集成样式 ====== */ +.nav-container { + display: flex; + align-items: center; + justify-content: space-between; + flex-wrap: wrap; +} + +.theme-toggle-container { + margin: 0 15px; + display: flex; + align-items: center; +} + +.user-menu { + display: flex; + align-items: center; +} + +.user-dropdown { + position: relative; + display: inline-block; +} + +.username { + padding: 8px 15px; + color: var(--text-primary); + cursor: pointer; + border: 1px solid var(--border-color); + border-radius: 4px; + background: var(--bg-secondary); + transition: var(--transition); + font-size: 14px; +} + +.username:hover { + background: var(--bg-tertiary); +} + +.dropdown-content { + display: none; + position: absolute; + right: 0; + background-color: var(--bg-card); + min-width: 160px; + box-shadow: var(--shadow); + border: 1px solid var(--border-color); + border-radius: 4px; + z-index: 1000; +} + +.dropdown-content a { + color: var(--text-primary); + padding: 12px 16px; + text-decoration: none; + display: block; + transition: var(--transition); + border: none; +} + +.dropdown-content a:hover { + background-color: var(--bg-secondary); + text-decoration: none; +} + +.user-dropdown:hover .dropdown-content { + display: block; +} + +.dropdown-divider { + height: 1px; + background-color: var(--border-color); + margin: 5px 0; +} + +.login-link { + padding: 8px 15px; + color: var(--text-primary); + text-decoration: none; + border: 1px solid var(--border-color); + border-radius: 4px; + background: var(--bg-secondary); + transition: var(--transition); + font-size: 14px; +} + +.login-link:hover { + background: var(--bg-tertiary); + color: var(--link-hover-color); + text-decoration: none; +} + +/* ====== 平滑过渡效果 ====== */ +body, +.site-header, +.main-navigation, +.entry-content, +.entry-header, +.widget, +.comment-list, +.site-footer, +a, +button, +input, +textarea, +select, +table, +pre, +code { + transition: var(--transition); +} + +/* 图片过渡 */ +img { + transition: opacity 0.3s ease; +} + +[data-theme="dark"] img { + opacity: 0.9; +} + +[data-theme="dark"] img:hover { + opacity: 1; +} + +/* ====== 响应式调整 ====== */ +@media (max-width: 768px) { + .theme-toggle-btn { + width: 36px; + height: 36px; + font-size: 1.1rem; + } + + .nav-container { + flex-direction: column; + gap: 10px; + } + + .theme-toggle-container { + margin: 10px 0; + } + + .user-menu { + width: 100%; + justify-content: center; + } +} +/* ====== 导航栏集成优化样式 ====== */ + +/* 导航容器布局 */ +.nav-container { + display: flex; + align-items: center; + justify-content: space-between; + width: 100%; + max-width: 1200px; + margin: 0 auto; + padding: 0 20px; +} + +.primary-nav { + flex: 1; +} + +.nav-actions { + display: flex; + align-items: center; + gap: 15px; +} + +/* 主题切换按钮优化 */ +.theme-toggle-container { + display: flex; + align-items: center; +} + +.theme-toggle-btn { + background: var(--bg-secondary); + border: 1px solid var(--border-color); + border-radius: 50%; + width: 40px; + height: 40px; + cursor: pointer; + display: flex; + align-items: center; + justify-content: center; + font-size: 1.2rem; + transition: var(--transition); + color: var(--text-primary); +} + +.theme-toggle-btn:hover { + transform: scale(1.1); + background-color: var(--bg-tertiary); + border-color: var(--link-color); +} + +/* 用户菜单优化 */ +.user-menu { + display: flex; + align-items: center; +} + +.user-dropdown { + position: relative; + display: inline-block; +} + +.username { + padding: 8px 15px; + color: var(--text-primary); + cursor: pointer; + border: 1px solid var(--border-color); + border-radius: 20px; + background: var(--bg-secondary); + transition: var(--transition); + font-size: 14px; + display: flex; + align-items: center; + gap: 5px; +} + +.username:hover { + background: var(--bg-tertiary); +} + +.user-icon { + font-size: 12px; +} + +.dropdown-content { + display: none; + position: absolute; + right: 0; + top: 100%; + background-color: var(--bg-card); + min-width: 140px; + box-shadow: var(--shadow); + border: 1px solid var(--border-color); + border-radius: 8px; + z-index: 1000; + margin-top: 5px; +} + +.dropdown-content a { + color: var(--text-primary); + padding: 10px 15px; + text-decoration: none; + display: flex; + align-items: center; + gap: 8px; + transition: var(--transition); + border: none; + font-size: 14px; +} + +.dropdown-content a:hover { + background-color: var(--bg-secondary); + text-decoration: none; +} + +.user-dropdown:hover .dropdown-content { + display: block; +} + +.dropdown-divider { + height: 1px; + background-color: var(--border-color); + margin: 5px 0; +} + +.icon { + font-size: 12px; +} + +.login-link { + padding: 8px 15px; + color: var(--text-primary); + text-decoration: none; + border: 1px solid var(--border-color); + border-radius: 20px; + background: var(--bg-secondary); + transition: var(--transition); + font-size: 14px; + display: flex; + align-items: center; + gap: 5px; +} + +.login-link:hover { + background: var(--bg-tertiary); + color: var(--link-hover-color); + text-decoration: none; +} + +/* 响应式设计 */ +@media (max-width: 768px) { + .nav-container { + flex-direction: column; + gap: 10px; + padding: 10px; + } + + .nav-actions { + width: 100%; + justify-content: center; + gap: 10px; + } + + .theme-toggle-btn { + width: 36px; + height: 36px; + font-size: 1.1rem; + } + + .primary-nav { + width: 100%; + text-align: center; + } +} + +/* 确保原有导航样式兼容 */ +.main-navigation { + background-color: var(--nav-bg); + border-bottom: 1px solid var(--nav-border); + transition: var(--transition); + padding: 10px 0; +} + +.main-navigation div { + background: transparent !important; +} + +.main-navigation a { + color: var(--nav-text) !important; + transition: var(--transition); +} + +.main-navigation a:hover { + color: var(--link-hover-color) !important; + background: transparent !important; +} + +/* 修复可能的分层问题 */ +.site-header { + position: relative; + z-index: 100; +} + +.main-navigation { + position: relative; + z-index: 99; +} \ No newline at end of file diff --git a/src/DjangoBlog/blog/static/blog/js/theme-switcher.js b/src/DjangoBlog/blog/static/blog/js/theme-switcher.js new file mode 100644 index 0000000..a5ab74b --- /dev/null +++ b/src/DjangoBlog/blog/static/blog/js/theme-switcher.js @@ -0,0 +1,132 @@ +// blog/static/blog/js/theme-switcher.js + +(function() { + 'use strict'; + + class ThemeSwitcher { + constructor() { + this.themeToggle = null; + this.currentTheme = this.getPreferredTheme(); + this.init(); + } + + init() { + console.log('初始化主题切换器,当前主题:', this.currentTheme); + this.setTheme(this.currentTheme); + this.bindEvents(); + this.watchSystemTheme(); + } + + getPreferredTheme() { + try { + const storedTheme = localStorage.getItem('theme'); + if (storedTheme) { + return storedTheme; + } + + if (window.matchMedia('(prefers-color-scheme: dark)').matches) { + return 'dark'; + } + return 'light'; + } catch (error) { + console.warn('获取主题偏好失败:', error); + return 'light'; + } + } + + setTheme(theme) { + try { + document.documentElement.setAttribute('data-theme', theme); + localStorage.setItem('theme', theme); + this.currentTheme = theme; + this.updateToggleIcon(); + + console.log('主题已设置为:', theme); + + const event = new CustomEvent('themeChanged', { + detail: { theme: theme } + }); + document.dispatchEvent(event); + + } catch (error) { + console.error('设置主题失败:', error); + } + } + + toggleTheme() { + const newTheme = this.currentTheme === 'light' ? 'dark' : 'light'; + console.log('切换主题:', this.currentTheme, '->', newTheme); + this.setTheme(newTheme); + } + + bindEvents() { + this.themeToggle = document.getElementById('theme-toggle'); + + if (this.themeToggle) { + this.themeToggle.addEventListener('click', () => { + this.toggleTheme(); + }); + this.updateToggleIcon(); + } else { + console.warn('未找到主题切换按钮 #theme-toggle'); + + setTimeout(() => { + this.themeToggle = document.getElementById('theme-toggle'); + if (this.themeToggle) { + this.themeToggle.addEventListener('click', () => { + this.toggleTheme(); + }); + this.updateToggleIcon(); + } + }, 1000); + } + } + + updateToggleIcon() { + if (!this.themeToggle) return; + + const iconElement = this.themeToggle.querySelector('.theme-icon'); + if (iconElement) { + const icon = this.currentTheme === 'light' ? '🌙' : '☀️'; + const title = this.currentTheme === 'light' ? '切换到深色模式' : '切换到浅色模式'; + + iconElement.textContent = icon; + this.themeToggle.title = title; + this.themeToggle.setAttribute('aria-label', title); + } + } + + watchSystemTheme() { + const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)'); + + const handleSystemThemeChange = (e) => { + if (!localStorage.getItem('theme')) { + const newTheme = e.matches ? 'dark' : 'light'; + console.log('系统主题变化,自动切换至:', newTheme); + this.setTheme(newTheme); + } + }; + + if (mediaQuery.addEventListener) { + mediaQuery.addEventListener('change', handleSystemThemeChange); + } else { + mediaQuery.addListener(handleSystemThemeChange); + } + } + } + + if (document.readyState === 'loading') { + document.addEventListener('DOMContentLoaded', function() { + window.themeSwitcher = new ThemeSwitcher(); + }); + } else { + window.themeSwitcher = new ThemeSwitcher(); + } + + window.toggleTheme = function() { + if (window.themeSwitcher) { + window.themeSwitcher.toggleTheme(); + } + }; + +})(); \ No newline at end of file diff --git a/src/DjangoBlog/templates/share_layout/base.html b/src/DjangoBlog/templates/share_layout/base.html index bb17933..7defcac 100644 --- a/src/DjangoBlog/templates/share_layout/base.html +++ b/src/DjangoBlog/templates/share_layout/base.html @@ -19,11 +19,14 @@ + {% load blog_tags %} {% head_meta %} + {% block header %} {% endblock %} + @@ -31,7 +34,6 @@ - @@ -44,21 +46,40 @@ - {% compress css %} - + + + + + + + + + + {% comment %}{% endcomment %} + + + {% block compress_css %} {% endblock %} + {% plugin_compressed_css %} - {% endcompress %} + {% if GLOBAL_HEADER %} {{ GLOBAL_HEADER|safe }} @@ -66,32 +87,72 @@ {% plugin_head_resources %} + + {% block extra_css %}{% endblock %} - +
-
+
{% block content %} {% endblock %} - {% block sidebar %} {% endblock %} - -
+ {% include 'share_layout/footer.html' %}
@@ -101,8 +162,12 @@ + + + {% block compress_js %} {% endblock %} + {% plugin_compressed_js %} {% endcompress %} @@ -115,5 +180,7 @@ {% plugin_body_resources %} + +{% block extra_js %}{% endblock %} - + \ No newline at end of file From f30135dfd853e317a200e47bf8d1a9ea27bc9f2b Mon Sep 17 00:00:00 2001 From: xdqw166 Date: Mon, 17 Nov 2025 20:17:50 +0800 Subject: [PATCH 2/2] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E4=BA=86base=20html?= =?UTF-8?q?=E7=9A=84=E6=8A=A5=E9=94=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/DjangoBlog/templates/share_layout/base.html | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/DjangoBlog/templates/share_layout/base.html b/src/DjangoBlog/templates/share_layout/base.html index 7defcac..666ce06 100644 --- a/src/DjangoBlog/templates/share_layout/base.html +++ b/src/DjangoBlog/templates/share_layout/base.html @@ -116,7 +116,7 @@
- +
{% if user.is_authenticated %}
@@ -125,17 +125,18 @@ {{ user.username }}
{% else %} - {% endif %}