Compare commits
46 Commits
lrj_branch
...
main
| Author | SHA1 | Date |
|---|---|---|
|
|
263d370607 | 2 months ago |
|
|
756a12fe29 | 2 months ago |
|
|
e209e1b2d4 | 2 months ago |
|
|
1de159624a | 2 months ago |
|
|
bc085a06cc | 2 months ago |
|
|
d8c637ba15 | 3 months ago |
|
|
8dda6211ec | 3 months ago |
|
|
78ead977fa | 3 months ago |
|
|
f8bd1d0438 | 3 months ago |
|
|
d106cb2792 | 3 months ago |
|
|
eee1b8c98e | 3 months ago |
|
|
265045fe65 | 3 months ago |
|
|
f30135dfd8 | 3 months ago |
|
|
90f48adaee | 3 months ago |
|
|
7316b4c8b6 | 3 months ago |
|
|
46862ad679 | 3 months ago |
|
|
8fa2a1d76f | 3 months ago |
|
|
654d036823 | 3 months ago |
|
|
60561750c0 | 3 months ago |
|
|
fedfe15b64 | 3 months ago |
|
|
d71b49f1ca | 3 months ago |
|
|
e33542c8a0 | 3 months ago |
|
|
43ea653c11 | 3 months ago |
|
|
72fddfe377 | 3 months ago |
|
|
af79acffbc | 3 months ago |
|
|
8a37d8cc06 | 3 months ago |
|
|
151535a74e | 3 months ago |
|
|
ef8f3f3d19 | 3 months ago |
|
|
dcc31a2bdf | 3 months ago |
|
|
afaacc22cf | 3 months ago |
|
|
741cac2e1f | 3 months ago |
|
|
854e8e28c7 | 3 months ago |
|
|
400192beb7 | 3 months ago |
|
|
aadcbfbfc3 | 3 months ago |
|
|
f285750263 | 3 months ago |
|
|
a5637dad09 | 3 months ago |
|
|
eac243818d | 3 months ago |
|
|
3240542cb9 | 3 months ago |
|
|
b85f85125b | 3 months ago |
|
|
01f9792c1c | 3 months ago |
|
|
001bc85a81 | 3 months ago |
|
|
cf73d21b06 | 3 months ago |
|
|
cf58b9ef18 | 3 months ago |
|
|
7c41266ac6 | 3 months ago |
|
|
285ac07e41 | 3 months ago |
|
|
7e7ba6f503 | 3 months ago |
@ -1 +1,8 @@
|
||||
.idea
|
||||
/.idea/
|
||||
|
||||
# DjangoBlog
|
||||
.env
|
||||
/logs/
|
||||
/collectedstatic/
|
||||
/djangoblog/
|
||||
/static/
|
||||
|
||||
@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2025
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
@ -1,2 +1,4 @@
|
||||
# 阅读和分析开源软件
|
||||
|
||||
- 软件名:[**DjangoBlog**](https://github.com/liangliangyy/DjangoBlog)
|
||||
- 协议:[**MIT License**](src/DjangoBlog/LICENSE)
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -0,0 +1,22 @@
|
||||
@echo off
|
||||
chcp 65001 >nul
|
||||
echo 正在检查将要清理的文件和文件夹...
|
||||
git clean -fd -n
|
||||
|
||||
set /p confirm=是否确认清理这些文件?(y/N):
|
||||
if /i "%confirm%" neq "y" (
|
||||
echo 操作已取消
|
||||
pause
|
||||
exit /b
|
||||
)
|
||||
|
||||
echo 正在执行清理操作...
|
||||
git clean -fd
|
||||
|
||||
if %errorlevel% equ 0 (
|
||||
echo 清理完成!
|
||||
) else (
|
||||
echo 清理过程中出现错误,错误代码: %errorlevel%
|
||||
)
|
||||
|
||||
pause
|
||||
@ -0,0 +1,9 @@
|
||||
@echo off
|
||||
echo Pushing Git subtree...
|
||||
git subtree push --prefix=src/DjangoBlog DjangoBlog g3f-CodeEdit
|
||||
if %errorlevel% equ 0 (
|
||||
echo Subtree push successful!
|
||||
) else (
|
||||
echo Subtree push failed!
|
||||
)
|
||||
pause
|
||||
@ -0,0 +1,7 @@
|
||||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: "pip"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
open-pull-requests-limit: 5
|
||||
@ -1,5 +1,11 @@
|
||||
#shw 导入Django的应用配置基类
|
||||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class AccountsConfig(AppConfig):
|
||||
#shw 这是accounts应用的配置类。
|
||||
#shw 它用于定义该应用的各种元数据和行为。
|
||||
|
||||
#shw 指定这个配置类所属的应用的完整Python路径。
|
||||
#shw Django通过这个name来找到并加载这个应用。
|
||||
name = 'accounts'
|
||||
|
||||
@ -0,0 +1,20 @@
|
||||
# Generated by Django 5.2.7 on 2025-11-13 13:53
|
||||
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('blog', '0006_alter_blogsettings_options'),
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='article',
|
||||
name='users_like',
|
||||
field=models.ManyToManyField(blank=True, related_name='articles_liked', to=settings.AUTH_USER_MODEL, verbose_name='点赞用户'),
|
||||
),
|
||||
]
|
||||
@ -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;
|
||||
}
|
||||
@ -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();
|
||||
}
|
||||
};
|
||||
|
||||
})();
|
||||
@ -0,0 +1,17 @@
|
||||
# Generated by Django 5.2.7 on 2025-11-11 10:33
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('oauth', '0003_alter_oauthuser_nickname'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterModelOptions(
|
||||
name='oauthconfig',
|
||||
options={'ordering': ['-creation_time'], 'verbose_name': 'OAuth配置', 'verbose_name_plural': 'OAuth配置'},
|
||||
),
|
||||
]
|
||||
Binary file not shown.
@ -0,0 +1,9 @@
|
||||
@echo off
|
||||
echo Updating Git subtree...
|
||||
git subtree pull --prefix=src/DjangoBlog DjangoBlog g3f-CodeEdit --squash -m "update subtree"
|
||||
if %errorlevel% equ 0 (
|
||||
echo Subtree update successful!
|
||||
) else (
|
||||
echo Subtree update failed!
|
||||
)
|
||||
pause
|
||||
Loading…
Reference in new issue