Compare commits
55 Commits
shw_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 |
|
|
b48a67b203 | 3 months ago |
|
|
cd96dfe561 | 3 months ago |
|
|
26efdcb6f4 | 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 |
|
|
7fc1b25b73 | 3 months ago |
|
|
e8698c02a4 | 3 months ago |
|
|
5ef0f8fb0f | 3 months ago |
|
|
0eb7232c46 | 3 months ago |
|
|
78de182afd | 3 months ago |
|
|
ae476bbab2 | 3 months ago |
|
|
5614ee69bc | 3 months ago |
|
|
6be53f48bb | 3 months ago |
|
|
f91c8887e6 | 3 months ago |
|
|
249e858738 | 3 months ago |
@ -1 +1,8 @@
|
|||||||
.idea
|
/.idea/
|
||||||
|
|
||||||
|
# DjangoBlog
|
||||||
|
.env
|
||||||
|
/logs/
|
||||||
|
/collectedstatic/
|
||||||
|
/djangoblog/
|
||||||
|
/static/
|
||||||
|
|||||||
@ -1,3 +0,0 @@
|
|||||||
[submodule "src/DjangoBlog"]
|
|
||||||
path = src/DjangoBlog
|
|
||||||
url = https://github.com/ETOofficial/DjangoBlog.git
|
|
||||||
@ -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
|
||||||
@ -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();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
})();
|
||||||
@ -1,5 +1,8 @@
|
|||||||
|
# zy: 评论应用配置模块 - 定义comments应用的配置信息
|
||||||
from django.apps import AppConfig
|
from django.apps import AppConfig
|
||||||
|
|
||||||
|
# zy: 评论应用配置类 - 继承自Django的AppConfig基类
|
||||||
class CommentsConfig(AppConfig):
|
class CommentsConfig(AppConfig):
|
||||||
|
# zy: 应用名称 - 指定Django内部使用的应用标识
|
||||||
|
# 这个名称应该与INSTALLED_APPS中的名称一致
|
||||||
name = 'comments'
|
name = 'comments'
|
||||||
|
|||||||
@ -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配置'},
|
||||||
|
),
|
||||||
|
]
|
||||||
@ -0,0 +1,67 @@
|
|||||||
|
# Create your models here.
|
||||||
|
from django.conf import settings
|
||||||
|
from django.core.exceptions import ValidationError
|
||||||
|
from django.db import models
|
||||||
|
from django.utils.timezone import now
|
||||||
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
|
|
||||||
|
class OAuthUser(models.Model):
|
||||||
|
author = models.ForeignKey(
|
||||||
|
settings.AUTH_USER_MODEL,
|
||||||
|
verbose_name=_('author'),
|
||||||
|
blank=True,
|
||||||
|
null=True,
|
||||||
|
on_delete=models.CASCADE)
|
||||||
|
openid = models.CharField(max_length=50)
|
||||||
|
nickname = models.CharField(max_length=50, verbose_name=_('nick name'))
|
||||||
|
token = models.CharField(max_length=150, null=True, blank=True)
|
||||||
|
picture = models.CharField(max_length=350, blank=True, null=True)
|
||||||
|
type = models.CharField(blank=False, null=False, max_length=50)
|
||||||
|
email = models.CharField(max_length=50, null=True, blank=True)
|
||||||
|
metadata = models.TextField(null=True, blank=True)
|
||||||
|
creation_time = models.DateTimeField(_('creation time'), default=now)
|
||||||
|
last_modify_time = models.DateTimeField(_('last modify time'), default=now)
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return self.nickname
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
verbose_name = _('oauth user')
|
||||||
|
verbose_name_plural = verbose_name
|
||||||
|
ordering = ['-creation_time']
|
||||||
|
|
||||||
|
|
||||||
|
class OAuthConfig(models.Model):
|
||||||
|
TYPE = (
|
||||||
|
('weibo', _('weibo')),
|
||||||
|
('google', _('google')),
|
||||||
|
('github', 'GitHub'),
|
||||||
|
('facebook', 'FaceBook'),
|
||||||
|
('qq', 'QQ'),
|
||||||
|
)
|
||||||
|
type = models.CharField(_('type'), max_length=10, choices=TYPE, default='a')
|
||||||
|
appkey = models.CharField(max_length=200, verbose_name='AppKey')
|
||||||
|
appsecret = models.CharField(max_length=200, verbose_name='AppSecret')
|
||||||
|
callback_url = models.CharField(
|
||||||
|
max_length=200,
|
||||||
|
verbose_name=_('callback url'),
|
||||||
|
blank=False,
|
||||||
|
default='')
|
||||||
|
is_enable = models.BooleanField(
|
||||||
|
_('is enable'), default=True, blank=False, null=False)
|
||||||
|
creation_time = models.DateTimeField(_('creation time'), default=now)
|
||||||
|
last_modify_time = models.DateTimeField(_('last modify time'), default=now)
|
||||||
|
|
||||||
|
def clean(self):
|
||||||
|
if OAuthConfig.objects.filter(
|
||||||
|
type=self.type).exclude(id=self.id).count():
|
||||||
|
raise ValidationError(_(self.type + _('already exists')))
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return self.type
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
verbose_name = 'oauth配置'
|
||||||
|
verbose_name_plural = verbose_name
|
||||||
|
ordering = ['-creation_time']
|
||||||
File diff suppressed because it is too large
Load Diff
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