Compare commits

..

15 Commits

Binary file not shown.

@ -0,0 +1,135 @@
from django.contrib.auth.decorators import login_required
from django.contrib.auth.mixins import LoginRequiredMixin
from django.shortcuts import redirect, get_object_or_404
from django.views.generic.edit import CreateView, UpdateView
from django.urls import reverse_lazy
from .models import Article, Category, Tag
from .forms import ArticleForm
class ArticleCreateView(LoginRequiredMixin, CreateView):
"""创建文章视图"""
model = Article
form_class = ArticleForm
template_name = 'blog/article_create.html'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['all_categories'] = Category.objects.all()
context['all_tags'] = Tag.objects.all()
return context
def form_valid(self, form):
# 先不保存表单,先处理分类
form.instance.author = self.request.user
# 处理分类
category_name = form.cleaned_data['category']
category, created = Category.objects.get_or_create(
name=category_name,
defaults={'parent_category': None}
)
# 直接设置分类对象,而不是名称
form.instance.category = category
# 先保存文章实例,以便可以添加多对多关系
# 但在保存前先从表单中移除category字段避免表单尝试保存它
category_temp = form.cleaned_data.pop('category')
tags_temp = form.cleaned_data.pop('tags')
response = super().form_valid(form)
# 处理标签
if tags_temp:
tag_names = [tag.strip() for tag in tags_temp.split(',') if tag.strip()]
for tag_name in tag_names:
tag, created = Tag.objects.get_or_create(name=tag_name)
form.instance.tags.add(tag)
return response
def get_success_url(self):
return reverse_lazy('blog:detailbyid', kwargs={
'article_id': self.object.id,
'year': self.object.creation_time.year,
'month': self.object.creation_time.month,
'day': self.object.creation_time.day
})
class ArticleUpdateView(LoginRequiredMixin, UpdateView):
"""更新文章视图"""
model = Article
form_class = ArticleForm
template_name = 'blog/article_edit.html'
pk_url_kwarg = 'article_id'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['all_categories'] = Category.objects.all()
context['all_tags'] = Tag.objects.all()
return context
def get_initial(self):
initial = super().get_initial()
# 设置分类初始值
if self.object.category:
initial['category'] = self.object.category.name
# 设置标签初始值
if self.object.tags.exists():
tag_names = [tag.name for tag in self.object.tags.all()]
initial['tags'] = ', '.join(tag_names)
return initial
def dispatch(self, request, *args, **kwargs):
obj = self.get_object()
# 只有文章作者或管理员可以编辑
if obj.author != request.user and not request.user.is_superuser:
return redirect('blog:detailbyid',
article_id=obj.id,
year=obj.creation_time.year,
month=obj.creation_time.month,
day=obj.creation_time.day)
return super().dispatch(request, *args, **kwargs)
def form_valid(self, form):
# 先不保存表单,先处理分类
form.instance.author = self.request.user
# 处理分类
category_name = form.cleaned_data['category']
category, created = Category.objects.get_or_create(
name=category_name,
defaults={'parent_category': None}
)
# 直接设置分类对象,而不是名称
form.instance.category = category
# 先保存文章实例,以便可以添加多对多关系
# 但在保存前先从表单中移除category字段避免表单尝试保存它
category_temp = form.cleaned_data.pop('category')
tags_temp = form.cleaned_data.pop('tags')
response = super().form_valid(form)
# 处理标签
if tags_temp:
tag_names = [tag.strip() for tag in tags_temp.split(',') if tag.strip()]
form.instance.tags.clear() # 清除现有标签
for tag_name in tag_names:
tag, created = Tag.objects.get_or_create(name=tag_name)
form.instance.tags.add(tag)
else:
form.instance.tags.clear() # 如果没有标签,清除所有标签
return response
def get_success_url(self):
return reverse_lazy('blog:detailbyid', kwargs={
'article_id': self.object.id,
'year': self.object.creation_time.year,
'month': self.object.creation_time.month,
'day': self.object.creation_time.day
})

@ -1,7 +1,9 @@
import logging
from django import forms
from django.utils.translation import gettext_lazy as _
from haystack.forms import SearchForm
from .models import Article, Category, Tag
logger = logging.getLogger(__name__)
@ -17,3 +19,62 @@ class BlogSearchForm(SearchForm):
if self.cleaned_data['querydata']:
logger.info(self.cleaned_data['querydata'])
return datas
class ArticleForm(forms.ModelForm):
"""文章表单"""
title = forms.CharField(label='标题', max_length=200, required=True)
body = forms.CharField(label='内容', widget=forms.Textarea, required=True)
status = forms.ChoiceField(
label='状态',
choices=[('p', '发布'), ('d', '草稿')],
initial='p',
widget=forms.Select,
required=True
)
comment_status = forms.ChoiceField(
label='评论状态',
choices=[('o', '开启'), ('c', '关闭')],
initial='o',
widget=forms.Select,
required=True
)
type = forms.ChoiceField(
label='类型',
choices=[('a', '文章'), ('p', '页面')],
initial='a',
widget=forms.Select,
required=True
)
show_toc = forms.BooleanField(label='显示目录', required=False)
category = forms.CharField(
label='分类',
required=True,
widget=forms.TextInput(attrs={
'class': 'form-control',
'list': 'category-list',
'placeholder': '选择或输入分类名称'
})
)
tags = forms.CharField(
label='标签',
required=False,
widget=forms.TextInput(attrs={
'class': 'form-control',
'list': 'tag-list',
'placeholder': '选择或输入标签,多个标签用逗号分隔'
})
)
class Meta:
model = Article
fields = ['title', 'body', 'status', 'comment_status', 'type', 'show_toc']
def __init__(self, *args, **kwargs):
super(ArticleForm, self).__init__(*args, **kwargs)
self.fields['title'].widget.attrs.update({'class': 'form-control'})
self.fields['body'].widget.attrs.update({'class': 'form-control', 'rows': 20})
self.fields['status'].widget.attrs.update({'class': 'form-control'})
self.fields['comment_status'].widget.attrs.update({'class': 'form-control'})
self.fields['type'].widget.attrs.update({'class': 'form-control'})
self.fields['show_toc'].widget.attrs.update({'class': 'form-check-input'})

@ -0,0 +1,41 @@
from django.core.management.base import BaseCommand
from blog.models import Category
class Command(BaseCommand):
help = '创建文章分类'
def handle(self, *args, **options):
categories = [
'技术',
'生活',
'旅行',
'美食',
'摄影',
'读书',
'电影',
'音乐',
'编程',
'设计',
'健康',
'教育',
'职场',
'财经',
'历史',
'文化',
'体育',
'游戏',
'科技'
]
for name in categories:
category, created = Category.objects.get_or_create(
name=name,
defaults={'parent_category': None}
)
if created:
self.stdout.write(self.style.SUCCESS(f'创建分类: {name}'))
else:
self.stdout.write(self.style.WARNING(f'分类已存在: {name}'))
self.stdout.write(self.style.SUCCESS('分类创建完成'))

@ -0,0 +1,13 @@
from django.core.management.base import BaseCommand
from blog.models import Category
class Command(BaseCommand):
help = '列出所有文章分类'
def handle(self, *args, **options):
categories = Category.objects.all()
self.stdout.write('当前数据库中的分类:')
for category in categories:
self.stdout.write(f'- {category.name} (ID: {category.id})')
self.stdout.write(f'总计: {categories.count()} 个分类')

@ -1,162 +0,0 @@
/* 文章归档页面样式 */
/* 归档标题样式 */
.archive-header {
margin-bottom: 2rem;
padding: 1.5rem;
background: linear-gradient(135deg, var(--primary-color), #2980b9);
border-radius: 8px;
box-shadow: var(--box-shadow);
position: relative;
overflow: hidden;
}
.archive-header::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="100" height="100" viewBox="0 0 100 100"><rect width="100" height="100" fill="none"/><path d="M0 0L50 50M50 0L100 50M0 50L50 100M50 50L100 100" stroke="rgba(255,255,255,0.1)" stroke-width="0.5"/></svg>');
background-size: 100px 100px;
opacity: 0.5;
}
.archive-title {
font-size: 1.8rem;
font-weight: 600;
color: #fff;
margin: 0;
position: relative;
z-index: 1;
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
/* 归档内容样式 */
.entry-content {
background-color: #fff;
border-radius: 8px;
box-shadow: var(--box-shadow);
padding: 2rem;
}
/* 年份样式 */
.archive-year {
margin-bottom: 1.5rem;
list-style-type: none;
position: relative;
padding-left: 2.5rem;
}
.archive-year::before {
content: '📅';
position: absolute;
left: 0;
top: 0.5rem;
width: 1.5rem;
height: 1.5rem;
background-color: var(--primary-color);
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
color: #fff;
font-weight: bold;
font-size: 0.8rem;
}
.year-label {
font-size: 1.3rem;
font-weight: 600;
color: var(--dark-color);
margin-bottom: 0.5rem;
display: block;
}
/* 月份样式 */
.archive-month {
margin-bottom: 1rem;
list-style-type: none;
position: relative;
padding-left: 2rem;
}
.archive-month::before {
content: '';
position: absolute;
left: 0;
top: 0.5rem;
width: 0.8rem;
height: 0.8rem;
background-color: var(--secondary-color);
border-radius: 50%;
}
.month-label {
font-size: 1.1rem;
font-weight: 500;
color: var(--dark-color);
margin-bottom: 0.5rem;
display: block;
}
/* 文章列表样式 */
.article-list {
margin-top: 0.5rem;
padding-left: 1rem;
}
.article-list > li {
margin-bottom: 0.5rem;
list-style-type: none;
position: relative;
padding-left: 1.5rem;
}
.article-list > li::before {
content: '📄';
position: absolute;
left: 0;
top: 0.2rem;
font-size: 0.9rem;
}
.article-list > li a {
color: var(--text-color);
text-decoration: none;
transition: var(--transition);
display: inline-block;
padding: 0.3rem 0.5rem;
border-radius: 4px;
}
.article-list > li a:hover {
color: var(--primary-color);
background-color: rgba(52, 152, 219, 0.1);
transform: translateX(5px);
}
/* 响应式设计 */
@media screen and (max-width: 768px) {
.archive-header {
padding: 1rem;
}
.archive-title {
font-size: 1.5rem;
}
.entry-content {
padding: 1rem;
}
.entry-content > ul > li {
padding-left: 2rem;
}
.entry-content > ul > li > ul > li {
padding-left: 1.5rem;
}
}

@ -1,22 +0,0 @@
/* 网页背景选项 - 默认背景和自定义图片背景 */
/* 自定义图片背景 - 请将图片放在 /blog/static/blog/images/ 目录下 */
.bg-custom-image {
background-image: url('{% static "blog/images/background.jpg" %}');
background-attachment: fixed;
background-size: cover;
background-position: center;
}
/* 半透明遮罩层,用于自定义图片背景上 */
.bg-overlay::before {
content: '';
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(255, 255, 255, 0.7);
z-index: -1;
}

@ -1,640 +0,0 @@
/* 现代化样式 - DjangoBlog美化 */
/* 全局变量 */
:root {
--primary-color: #3498db;
--secondary-color: #2c3e50;
--accent-color: #e74c3c;
--light-color: #ecf0f1;
--dark-color: #2c3e50;
--text-color: #333;
--text-light: #777;
--border-color: #ddd;
--box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
--transition: all 0.3s ease;
}
/* 基础样式重置和优化 */
body {
font-family: 'Segoe UI', 'Roboto', 'Helvetica Neue', Arial, sans-serif;
line-height: 1.6;
color: var(--text-color);
background-color: #f8f9fa;
background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="100" height="100" viewBox="0 0 100 100"><rect width="100" height="100" fill="%23f8f9fa"/><path d="M0 0L50 50M50 0L100 50M0 50L50 100M50 50L100 100" stroke="%23e9ecef" stroke-width="0.5"/></svg>');
background-attachment: fixed;
background-size: 100px 100px;
}
/* 网站容器 */
.site {
max-width: 1200px;
margin: 0 auto;
background-color: #fff;
box-shadow: var(--box-shadow);
border-radius: 8px;
overflow: hidden;
}
/* 头部样式 */
.site-header {
background: linear-gradient(135deg, var(--secondary-color), var(--primary-color));
color: #fff;
padding: 3rem 2rem;
text-align: center;
position: relative;
overflow: hidden;
}
.site-header::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1440 320"><path fill="rgba(255,255,255,0.1)" d="M0,96L48,112C96,128,192,160,288,160C384,160,480,128,576,112C672,96,768,96,864,112C960,128,1056,160,1152,160C1248,160,1344,128,1392,112L1440,96L1440,320L1392,320C1344,320,1248,320,1152,320C1056,320,960,320,864,320C768,320,672,320,576,320C480,320,384,320,288,320C192,320,96,320,48,320L0,320Z"></path></svg>') no-repeat bottom;
background-size: cover;
opacity: 0.5;
}
.site-header h1, .site-header h2 {
position: relative;
z-index: 1;
margin: 0;
padding: 0;
}
.site-header h1 {
font-size: 2.5rem;
font-weight: 700;
margin-bottom: 0.5rem;
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
}
.site-header h1 a {
color: #fff;
text-decoration: none;
transition: var(--transition);
}
.site-header h1 a:hover {
opacity: 0.8;
transform: translateY(-2px);
}
.site-header h2 {
font-size: 1.2rem;
font-weight: 300;
opacity: 0.9;
}
/* 导航菜单样式 */
.main-navigation {
background-color: #fff;
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
position: sticky;
top: 0;
z-index: 100;
border-top: 1px solid var(--border-color);
}
.main-navigation ul.nav-menu {
display: flex;
justify-content: center;
flex-wrap: wrap;
padding: 0;
margin: 0;
list-style: none;
}
.main-navigation li {
margin: 0;
position: relative;
}
.main-navigation a {
display: block;
padding: 1rem 1.5rem;
color: var(--dark-color);
text-decoration: none;
font-weight: 500;
transition: var(--transition);
position: relative;
}
.main-navigation a::after {
content: '';
position: absolute;
bottom: 0;
left: 50%;
width: 0;
height: 3px;
background-color: var(--primary-color);
transition: var(--transition);
transform: translateX(-50%);
}
.main-navigation a:hover::after,
.main-navigation a:focus::after {
width: 80%;
}
.main-navigation a:hover,
.main-navigation a:focus {
color: var(--primary-color);
}
/* 主要内容区域 - 保持经典两栏布局 */
#main {
display: flex;
padding: 2rem;
gap: 2rem;
}
#primary {
flex: 0 0 65%;
max-width: 65%;
}
/* 文章样式 - 优化列表布局 */
article {
margin-bottom: 2rem;
padding: 1.5rem;
background-color: #fff;
border-radius: 8px;
box-shadow: var(--box-shadow);
transition: var(--transition);
border-left: 4px solid var(--primary-color);
}
article:hover {
transform: translateY(-3px);
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.1);
}
.entry-header {
margin-bottom: 1.5rem;
border-bottom: 1px solid var(--border-color);
padding-bottom: 1rem;
}
.entry-title {
font-size: 1.8rem;
margin-bottom: 0.5rem;
line-height: 1.3;
}
.entry-title a {
color: var(--dark-color);
text-decoration: none;
transition: var(--transition);
}
.entry-title a:hover {
color: var(--primary-color);
}
.entry-meta {
color: var(--text-light);
font-size: 0.9rem;
margin-bottom: 1rem;
}
.entry-meta a {
color: var(--primary-color);
text-decoration: none;
}
.entry-meta a:hover {
text-decoration: underline;
}
.entry-content {
line-height: 1.7;
}
.entry-content p {
margin-bottom: 1.2rem;
}
.entry-content img {
max-width: 100%;
height: auto;
border-radius: 4px;
margin: 1.5rem 0;
}
.read-more {
display: inline-block;
margin-top: 1rem;
padding: 0.5rem 1.2rem;
background-color: var(--primary-color);
color: #fff;
border-radius: 4px;
text-decoration: none;
transition: var(--transition);
font-weight: 500;
}
.read-more:hover {
background-color: #2980b9;
transform: translateY(-2px);
}
/* 侧边栏样式 - 保持右侧布局并优化外观 */
.widget-area {
flex: 0 0 30%;
max-width: 30%;
}
.widget {
margin-bottom: 2rem;
background-color: #fff;
border-radius: 8px;
box-shadow: var(--box-shadow);
padding: 1.5rem;
border-top: 3px solid var(--primary-color);
transition: var(--transition);
}
.widget:hover {
transform: translateY(-2px);
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.1);
}
.widget-title {
font-size: 1.2rem;
margin-bottom: 1rem;
padding-bottom: 0.5rem;
border-bottom: 2px solid var(--primary-color);
color: var(--dark-color);
position: relative;
padding-left: 10px;
}
.widget-title::before {
content: '';
position: absolute;
left: 0;
top: 0;
bottom: 0.5rem;
width: 4px;
background-color: var(--primary-color);
border-radius: 2px;
}
.widget ul {
list-style: none;
padding: 0;
margin: 0;
}
.widget li {
margin-bottom: 0.5rem;
padding-bottom: 0.5rem;
border-bottom: 1px solid var(--border-color);
}
.widget li:last-child {
border-bottom: none;
}
.widget a {
color: var(--text-color);
text-decoration: none;
transition: var(--transition);
}
.widget a:hover {
color: var(--primary-color);
}
/* 分页样式 */
.pagination {
margin: 2rem 0;
display: flex;
justify-content: center;
gap: 0.5rem;
}
.pagination a, .pagination span {
display: block;
padding: 0.5rem 1rem;
border: 1px solid var(--border-color);
border-radius: 4px;
text-decoration: none;
transition: var(--transition);
}
.pagination a:hover {
background-color: var(--primary-color);
color: #fff;
border-color: var(--primary-color);
}
.pagination span.current {
background-color: var(--primary-color);
color: #fff;
border-color: var(--primary-color);
}
/* 评论区域 */
.comments-area {
margin-top: 2rem;
padding: 1.5rem;
background-color: #fff;
border-radius: 8px;
box-shadow: var(--box-shadow);
}
.comment-list {
list-style: none;
padding: 0;
margin: 0;
}
.comment {
margin-bottom: 1.5rem;
padding-bottom: 1.5rem;
border-bottom: 1px solid var(--border-color);
}
.comment:last-child {
border-bottom: none;
}
.comment-author {
display: flex;
align-items: center;
margin-bottom: 0.5rem;
}
.comment-author img {
border-radius: 50%;
margin-right: 1rem;
}
.comment-meta {
font-size: 0.9rem;
color: var(--text-light);
margin-bottom: 1rem;
}
.comment-content {
line-height: 1.7;
}
/* 表单样式 */
form {
margin-top: 1.5rem;
}
.form-group {
margin-bottom: 1rem;
}
label {
display: block;
margin-bottom: 0.5rem;
font-weight: 500;
}
input[type="text"],
input[type="email"],
input[type="url"],
textarea {
width: 100%;
padding: 0.75rem;
border: 1px solid var(--border-color);
border-radius: 4px;
font-family: inherit;
font-size: 1rem;
transition: var(--transition);
}
input[type="text"]:focus,
input[type="email"]:focus,
input[type="url"]:focus,
textarea:focus {
border-color: var(--primary-color);
outline: none;
box-shadow: 0 0 0 2px rgba(52, 152, 219, 0.2);
}
textarea {
min-height: 120px;
resize: vertical;
}
button, input[type="submit"] {
display: inline-block;
padding: 0.75rem 1.5rem;
background-color: var(--primary-color);
color: #fff;
border: none;
border-radius: 4px;
font-family: inherit;
font-size: 1rem;
font-weight: 500;
cursor: pointer;
transition: var(--transition);
}
button:hover, input[type="submit"]:hover {
background-color: #2980b9;
transform: translateY(-2px);
}
/* 页脚样式 */
#colophon {
background-color: var(--dark-color);
color: #fff;
padding: 2rem;
text-align: center;
}
.site-info {
margin-bottom: 1rem;
}
.site-info:last-child {
margin-bottom: 0;
}
.site-info a {
color: #fff;
text-decoration: none;
transition: var(--transition);
}
.site-info a:hover {
color: var(--primary-color);
text-decoration: underline;
}
/* 响应式设计 - 保持两栏布局直到移动设备 */
@media screen and (max-width: 992px) {
#main {
flex-direction: column;
}
#primary {
flex: 0 0 100%;
max-width: 100%;
padding-right: 0;
margin-bottom: 2rem;
}
.widget-area {
flex: 0 0 100%;
max-width: 100%;
}
}
@media screen and (max-width: 768px) {
.site-header {
padding: 2rem 1rem;
}
.site-header h1 {
font-size: 2rem;
}
.main-navigation ul.nav-menu {
flex-direction: column;
}
.main-navigation li {
width: 100%;
text-align: center;
}
#main {
padding: 1rem;
}
article, .widget {
padding: 1rem;
}
}
@media screen and (max-width: 480px) {
.site-header h1 {
font-size: 1.5rem;
}
.site-header h2 {
font-size: 1rem;
}
.entry-title {
font-size: 1.5rem;
}
}
/* 动画效果 */
@keyframes fadeIn {
from { opacity: 0; transform: translateY(20px); }
to { opacity: 1; transform: translateY(0); }
}
article {
animation: fadeIn 0.6s ease-out;
}
/* 滚动到顶部按钮 */
#back-to-top {
position: fixed;
bottom: 2rem;
right: 2rem;
width: 50px;
height: 50px;
background-color: var(--primary-color);
color: #fff;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
text-decoration: none;
opacity: 0;
visibility: hidden;
transition: var(--transition);
z-index: 1000;
box-shadow: var(--box-shadow);
}
#back-to-top.visible {
opacity: 1;
visibility: visible;
}
#back-to-top:hover {
background-color: #2980b9;
transform: translateY(-5px);
}
/* 搜索框样式 - 优化为侧边栏设计 */
.search-form {
position: relative;
margin-bottom: 1.5rem;
}
.search-form input[type="search"] {
width: 100%;
padding: 0.75rem 3rem 0.75rem 1.2rem;
border: 1px solid var(--border-color);
border-radius: 25px;
font-family: inherit;
font-size: 0.9rem;
transition: var(--transition);
background-color: #f9f9f9;
}
.search-form input[type="search"]:focus {
border-color: var(--primary-color);
outline: none;
box-shadow: 0 0 0 2px rgba(52, 152, 219, 0.2);
background-color: #fff;
}
.search-form button {
position: absolute;
top: 50%;
right: 8px;
transform: translateY(-50%);
background: none;
border: none;
padding: 0.5rem;
color: var(--primary-color);
cursor: pointer;
border-radius: 50%;
transition: var(--transition);
}
.search-form button:hover {
background-color: rgba(52, 152, 219, 0.1);
}
/* 标签云样式 */
.tagcloud {
display: flex;
flex-wrap: wrap;
gap: 0.5rem;
}
.tagcloud a {
display: inline-block;
padding: 0.3rem 0.8rem;
background-color: var(--light-color);
color: var(--dark-color);
border-radius: 15px;
font-size: 0.9rem;
text-decoration: none;
transition: var(--transition);
}
.tagcloud a:hover {
background-color: var(--primary-color);
color: #fff;
}

@ -1,16 +0,0 @@
# 博客背景图片
将您想用作背景的图片放在此目录下然后在background-options.css中更新路径。
支持的图片格式:
- JPG
- PNG
- GIF
- WebP
建议的图片尺寸:
- 宽度1920px或更大
- 高度1080px或更大
- 分辨率72dpi或更高
推荐使用有轻微纹理或图案的图片,避免过于复杂的图像,以免影响内容可读性。

Binary file not shown.

Before

Width:  |  Height:  |  Size: 440 KiB

@ -1,185 +0,0 @@
// 背景切换工具
document.addEventListener('DOMContentLoaded', function() {
// 创建背景切换器
function createBackgroundSwitcher() {
// 创建背景切换器容器
const switcher = document.createElement('div');
switcher.className = 'bg-switcher';
switcher.innerHTML = `
<button class="bg-switcher-toggle" title="切换背景">
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M12 16C14.2091 16 16 14.2091 16 12C16 9.79086 14.2091 8 12 8C9.79086 8 8 9.79086 8 12C8 14.2091 9.79086 16 12 16Z" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M12 2V4" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M12 20V22" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M4.93 4.93L6.34 6.34" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M17.66 17.66L19.07 19.07" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M2 12H4" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M20 12H22" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M6.34 17.66L4.93 19.07" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M19.07 4.93L17.66 6.34" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
</button>
<div class="bg-switcher-panel">
<h4>选择背景</h4>
<div class="bg-options">
<button class="bg-option active" data-bg="default">默认</button>
<button class="bg-option" data-bg="custom-image">自定义背景</button>
</div>
</div>
`;
// 添加样式
const style = document.createElement('style');
style.textContent = `
.bg-switcher {
position: fixed;
bottom: 20px;
left: 20px;
z-index: 1000;
}
.bg-switcher-toggle {
width: 50px;
height: 50px;
border-radius: 50%;
background-color: var(--primary-color);
color: white;
border: none;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
box-shadow: var(--box-shadow);
transition: var(--transition);
}
.bg-switcher-toggle:hover {
background-color: #2980b9;
transform: scale(1.1);
}
.bg-switcher-panel {
position: absolute;
bottom: 60px;
left: 0;
background-color: white;
border-radius: 8px;
box-shadow: var(--box-shadow);
padding: 15px;
width: 200px;
opacity: 0;
visibility: hidden;
transform: translateY(10px);
transition: var(--transition);
}
.bg-switcher.active .bg-switcher-panel {
opacity: 1;
visibility: visible;
transform: translateY(0);
}
.bg-switcher-panel h4 {
margin: 0 0 10px;
font-size: 16px;
color: var(--dark-color);
}
.bg-options {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 8px;
}
.bg-option {
padding: 8px;
border: 1px solid var(--border-color);
border-radius: 4px;
background-color: #f9f9f9;
cursor: pointer;
transition: var(--transition);
font-size: 14px;
}
.bg-option:hover {
background-color: #f0f0f0;
border-color: var(--primary-color);
}
.bg-option.active {
background-color: var(--primary-color);
color: white;
border-color: var(--primary-color);
}
`;
document.head.appendChild(style);
// 添加到页面
document.body.appendChild(switcher);
// 切换面板显示/隐藏
const toggle = switcher.querySelector('.bg-switcher-toggle');
toggle.addEventListener('click', function() {
switcher.classList.toggle('active');
});
// 点击其他地方关闭面板
document.addEventListener('click', function(e) {
if (!switcher.contains(e.target)) {
switcher.classList.remove('active');
}
});
// 背景切换功能
const options = switcher.querySelectorAll('.bg-option');
options.forEach(option => {
option.addEventListener('click', function() {
// 移除所有active类
options.forEach(opt => opt.classList.remove('active'));
// 添加active类到当前选项
this.classList.add('active');
// 切换背景
const bgType = this.getAttribute('data-bg');
switchBackground(bgType);
// 保存用户选择
localStorage.setItem('preferredBackground', bgType);
});
});
// 检查用户之前的选择
const savedBg = localStorage.getItem('preferredBackground');
if (savedBg) {
// 设置对应的选项为active
options.forEach(opt => {
if (opt.getAttribute('data-bg') === savedBg) {
opt.classList.add('active');
} else {
opt.classList.remove('active');
}
});
// 应用保存的背景
switchBackground(savedBg);
}
}
// 切换背景函数
function switchBackground(bgType) {
const body = document.body;
// 移除所有背景类
body.classList.remove('bg-custom-image', 'bg-overlay');
// 根据类型添加对应的背景类
if (bgType === 'custom-image') {
body.classList.add('bg-custom-image', 'bg-overlay');
}
}
// 初始化背景切换器
createBackgroundSwitcher();
});

@ -1,134 +0,0 @@
// 现代化UI交互效果
document.addEventListener('DOMContentLoaded', function() {
// 返回顶部按钮功能
const backToTopButton = document.getElementById('back-to-top');
if (backToTopButton) {
// 监听滚动事件
window.addEventListener('scroll', function() {
// 当页面滚动超过300px时显示返回顶部按钮
if (window.pageYOffset > 300) {
backToTopButton.classList.add('visible');
} else {
backToTopButton.classList.remove('visible');
}
});
// 点击返回顶部
backToTopButton.addEventListener('click', function(e) {
e.preventDefault();
window.scrollTo({
top: 0,
behavior: 'smooth'
});
});
}
// 为所有外部链接添加target="_blank"属性
const links = document.querySelectorAll('a[href^="http"]:not([href*="' + window.location.hostname + '"])');
links.forEach(function(link) {
link.setAttribute('target', '_blank');
link.setAttribute('rel', 'noopener noreferrer');
});
// 文章内容图片添加懒加载效果
const articleImages = document.querySelectorAll('.entry-content img');
if ('IntersectionObserver' in window) {
const imageObserver = new IntersectionObserver(function(entries, observer) {
entries.forEach(function(entry) {
if (entry.isIntersecting) {
const img = entry.target;
img.classList.add('fade-in');
imageObserver.unobserve(img);
}
});
});
articleImages.forEach(function(img) {
imageObserver.observe(img);
});
}
// 搜索框交互效果
const searchForms = document.querySelectorAll('.search-form');
searchForms.forEach(function(form) {
const input = form.querySelector('input[type="search"]');
const button = form.querySelector('button');
if (input && button) {
input.addEventListener('focus', function() {
form.classList.add('active');
});
input.addEventListener('blur', function() {
if (!input.value) {
form.classList.remove('active');
}
});
}
});
// 为代码块添加复制按钮
const codeBlocks = document.querySelectorAll('pre code');
codeBlocks.forEach(function(block) {
const button = document.createElement('button');
button.className = 'copy-button';
button.textContent = '复制';
button.setAttribute('title', '复制代码');
const pre = block.parentElement;
pre.style.position = 'relative';
pre.appendChild(button);
button.addEventListener('click', function() {
navigator.clipboard.writeText(block.textContent).then(function() {
button.textContent = '已复制!';
setTimeout(function() {
button.textContent = '复制';
}, 2000);
}).catch(function(err) {
console.error('复制失败:', err);
});
});
});
});
// 添加代码块复制按钮的CSS
const style = document.createElement('style');
style.textContent = `
.copy-button {
position: absolute;
top: 8px;
right: 8px;
background-color: rgba(0, 0, 0, 0.7);
color: white;
border: none;
border-radius: 4px;
padding: 4px 8px;
font-size: 12px;
cursor: pointer;
opacity: 0;
transition: opacity 0.2s;
}
pre:hover .copy-button {
opacity: 1;
}
.fade-in {
animation: fadeIn 0.6s ease-out;
}
@keyframes fadeIn {
from { opacity: 0; transform: translateY(20px); }
to { opacity: 1; transform: translateY(0); }
}
.search-form.active input[type="search"] {
border-color: var(--primary-color);
box-shadow: 0 0 0 2px rgba(52, 152, 219, 0.2);
}
`;
document.head.appendChild(style);

@ -2,6 +2,7 @@ from django.urls import path
from django.views.decorators.cache import cache_page
from . import views
from . import article_views
app_name = "blog"
urlpatterns = [
@ -17,6 +18,18 @@ urlpatterns = [
r'article/<int:year>/<int:month>/<int:day>/<int:article_id>.html',
views.ArticleDetailView.as_view(),
name='detailbyid'),
path(
r'article/create/',
article_views.ArticleCreateView.as_view(),
name='article_create'),
path(
r'article/edit/<int:article_id>/',
article_views.ArticleUpdateView.as_view(),
name='article_edit'),
path(
r'my-articles/',
views.my_articles,
name='my_articles'),
path(
r'category/<slug:category_name>.html',
views.CategoryDetailView.as_view(),

@ -378,3 +378,82 @@ def permission_denied_view(
def clean_cache_view(request):
cache.clear()
return HttpResponse('ok')
from django.contrib.auth.decorators import login_required
from django.contrib.auth.mixins import LoginRequiredMixin
from django.shortcuts import redirect
from django.views.generic.edit import CreateView, UpdateView
from django.urls import reverse_lazy
from .forms import ArticleForm
class ArticleCreateView(LoginRequiredMixin, CreateView):
"""创建文章视图"""
model = Article
form_class = ArticleForm
template_name = 'blog/article_create.html'
def form_valid(self, form):
form.instance.author = self.request.user
# 处理分类
category_name = form.cleaned_data['category']
category, created = Category.objects.get_or_create(
name=category_name,
defaults={'parent_category': None}
)
form.instance.category = category
# 处理标签
tags_str = form.cleaned_data['tags']
if tags_str:
tag_names = [tag.strip() for tag in tags_str.split(',') if tag.strip()]
form.instance.save() # 保存文章实例,以便可以添加多对多关系
for tag_name in tag_names:
tag, created = Tag.objects.get_or_create(name=tag_name)
form.instance.tags.add(tag)
return super().form_valid(form)
def get_success_url(self):
return reverse_lazy('blog:detailbyid', kwargs={
'article_id': self.object.id,
'year': self.object.creation_time.year,
'month': self.object.creation_time.month,
'day': self.object.creation_time.day
})
class ArticleUpdateView(LoginRequiredMixin, UpdateView):
"""更新文章视图"""
model = Article
form_class = ArticleForm
template_name = 'blog/article_edit.html'
pk_url_kwarg = 'article_id'
def dispatch(self, request, *args, **kwargs):
obj = self.get_object()
# 只有文章作者或管理员可以编辑
if obj.author != request.user and not request.user.is_superuser:
return redirect('blog:detailbyid',
article_id=obj.id,
year=obj.creation_time.year,
month=obj.creation_time.month,
day=obj.creation_time.day)
return super().dispatch(request, *args, **kwargs)
def get_success_url(self):
return reverse_lazy('blog:detailbyid', kwargs={
'article_id': self.object.id,
'year': self.object.creation_time.year,
'month': self.object.creation_time.month,
'day': self.object.creation_time.day
})
@login_required
def my_articles(request):
"""显示当前用户的文章列表"""
articles = Article.objects.filter(author=request.user)
return render(request, 'blog/my_articles.html', {'articles': articles})

@ -665,3 +665,95 @@ msgstr "快捷登录"
#: .\templates\share_layout\nav.html:26
msgid "Article archive"
msgstr "文章归档"
#: .\templates\share_layout\nav.html:29
msgid "Create Article"
msgstr "创建文章"
#: .\templates\share_layout\nav.html:33
msgid "My Articles"
msgstr "我的文章"
#: .\templates\blog\article_create.html:5
msgid "Create Article"
msgstr "创建文章"
#: .\templates\blog\article_edit.html:5
msgid "Edit Article"
msgstr "编辑文章"
#: .\templates\blog\my_articles.html:5
msgid "My Articles"
msgstr "我的文章"
#: .\templates\share_layout\nav.html:29
msgid "发布文章"
msgstr "发布文章"
#: .\templates\share_layout\nav.html:33
msgid "我的文章"
msgstr "我的文章"
#: .\templates\blog\article_create.html:97
msgid "发布"
msgstr "发布"
#: .\templates\blog\article_create.html:98
msgid "取消"
msgstr "取消"
#: .\templates\blog\article_edit.html:97
msgid "更新"
msgstr "更新"
#: .\templates\blog\article_edit.html:98
msgid "取消"
msgstr "取消"
#: .\templates\blog\my_articles.html:13
msgid "发布新文章"
msgstr "发布新文章"
#: .\templates\blog\my_articles.html:22
msgid "标题"
msgstr "标题"
#: .\templates\blog\my_articles.html:23
msgid "状态"
msgstr "状态"
#: .\templates\blog\my_articles.html:24
msgid "分类"
msgstr "分类"
#: .\templates\blog\my_articles.html:25
msgid "创建时间"
msgstr "创建时间"
#: .\templates\blog\my_articles.html:26
msgid "浏览量"
msgstr "浏览量"
#: .\templates\blog\my_articles.html:27
msgid "操作"
msgstr "操作"
#: .\templates\blog\my_articles.html:38
msgid "已发布"
msgstr "已发布"
#: .\templates\blog\my_articles.html:40
msgid "草稿"
msgstr "草稿"
#: .\templates\blog\my_articles.html:48
msgid "编辑"
msgstr "编辑"
#: .\templates\blog\my_articles.html:58
msgid "您还没有创建任何文章。"
msgstr "您还没有创建任何文章。"
#: .\templates\blog\my_articles.html:59
msgid "创建您的第一篇文章"
msgstr "创建您的第一篇文章"

@ -20,24 +20,23 @@
<div id="content" role="main">
<header class="archive-header">
<h1 class="archive-title">{% trans 'article archive' %}</h1>
<p class="archive-title">{% trans 'article archive' %}</p>
</header><!-- .archive-header -->
<div class="entry-content">
{% regroup article_list by pub_time.year as year_post_group %}
<ul class="archive-list">
<ul>
{% for year in year_post_group %}
<li class="archive-year">
<span class="year-label">{{ year.grouper }} {% trans 'year' %}</span>
<li>{{ year.grouper }} {% trans 'year' %}
{% regroup year.list by pub_time.month as month_post_group %}
<ul class="month-list">
<ul>
{% for month in month_post_group %}
<li class="archive-month">
<span class="month-label">{{ month.grouper }} {% trans 'month' %}</span>
<ul class="article-list">
<li>{{ month.grouper }} {% trans 'month' %}
<ul>
{% for article in month.list %}
<li>
<a href="{{ article.get_absolute_url }}" title="{{ article.title }}">{{ article.title }}</a>
<li><a href="{{ article.get_absolute_url }}">{{ article.title }}</a>
</li>
{% endfor %}
</ul>

@ -0,0 +1,116 @@
{% extends "share_layout/base.html" %}
{% load static %}
{% load i18n %}
{% block title %}{% trans "Create Article" %}{% endblock %}
{% block content %}
<div class="container">
<div class="row">
<div class="col-md-12">
<h2>{% trans "Create Article" %}</h2>
<form method="post" enctype="multipart/form-data">
{% csrf_token %}
<div class="mb-3">
<label for="{{ form.title.id_for_label }}" class="form-label">{{ form.title.label }}</label>
{{ form.title }}
{% if form.title.errors %}
<div class="text-danger">
{{ form.title.errors }}
</div>
{% endif %}
</div>
<div class="mb-3">
<label for="{{ form.body.id_for_label }}" class="form-label">{{ form.body.label }}</label>
{{ form.body }}
{% if form.body.errors %}
<div class="text-danger">
{{ form.body.errors }}
</div>
{% endif %}
</div>
<div class="row">
<div class="col-md-6 mb-3">
<label for="{{ form.status.id_for_label }}" class="form-label">{{ form.status.label }}</label>
{{ form.status }}
{% if form.status.errors %}
<div class="text-danger">
{{ form.status.errors }}
</div>
{% endif %}
</div>
<div class="col-md-6 mb-3">
<label for="{{ form.comment_status.id_for_label }}" class="form-label">{{ form.comment_status.label }}</label>
{{ form.comment_status }}
{% if form.comment_status.errors %}
<div class="text-danger">
{{ form.comment_status.errors }}
</div>
{% endif %}
</div>
</div>
<div class="row">
<div class="col-md-6 mb-3">
<label for="{{ form.type.id_for_label }}" class="form-label">{{ form.type.label }}</label>
{{ form.type }}
{% if form.type.errors %}
<div class="text-danger">
{{ form.type.errors }}
</div>
{% endif %}
</div>
<div class="col-md-6 mb-3">
<label for="{{ form.category.id_for_label }}" class="form-label">{{ form.category.label }}</label>
{{ form.category }}
<datalist id="category-list">
{% for category in all_categories %}
<option value="{{ category.name }}">
{% endfor %}
</datalist>
{% if form.category.errors %}
<div class="text-danger">
{{ form.category.errors }}
</div>
{% endif %}
</div>
</div>
<div class="mb-3">
<label for="{{ form.tags.id_for_label }}" class="form-label">{{ form.tags.label }}</label>
{{ form.tags }}
<datalist id="tag-list">
{% for tag in all_tags %}
<option value="{{ tag.name }}">
{% endfor %}
</datalist>
{% if form.tags.errors %}
<div class="text-danger">
{{ form.tags.errors }}
</div>
{% endif %}
</div>
<div class="mb-3 form-check">
{{ form.show_toc }}
<label for="{{ form.show_toc.id_for_label }}" class="form-check-label">{{ form.show_toc.label }}</label>
{% if form.show_toc.errors %}
<div class="text-danger">
{{ form.show_toc.errors }}
</div>
{% endif %}
</div>
<div class="mb-3">
<button type="submit" class="btn btn-primary">{% trans "发布" %}</button>
<a href="{% url 'blog:index' %}" class="btn btn-secondary">{% trans "取消" %}</a>
</div>
</form>
</div>
</div>
</div>
{% endblock %}

@ -0,0 +1,118 @@
{% extends "share_layout/base.html" %}
{% load static %}
{% load i18n %}
{% block title %}{% trans "Edit Article" %}{% endblock %}
{% block content %}
<div class="container">
<div class="row">
<div class="col-md-12">
<h2>{% trans "Edit Article" %}: {{ article.title }}</h2>
<form method="post" enctype="multipart/form-data">
{% csrf_token %}
<div class="mb-3">
<label for="{{ form.title.id_for_label }}" class="form-label">{{ form.title.label }}</label>
{{ form.title }}
{% if form.title.errors %}
<div class="text-danger">
{{ form.title.errors }}
</div>
{% endif %}
</div>
<div class="mb-3">
<label for="{{ form.body.id_for_label }}" class="form-label">{{ form.body.label }}</label>
{{ form.body }}
{% if form.body.errors %}
<div class="text-danger">
{{ form.body.errors }}
</div>
{% endif %}
</div>
<div class="row">
<div class="col-md-6 mb-3">
<label for="{{ form.status.id_for_label }}" class="form-label">{{ form.status.label }}</label>
{{ form.status }}
{% if form.status.errors %}
<div class="text-danger">
{{ form.status.errors }}
</div>
{% endif %}
</div>
<div class="col-md-6 mb-3">
<label for="{{ form.comment_status.id_for_label }}" class="form-label">{{ form.comment_status.label }}</label>
{{ form.comment_status }}
{% if form.comment_status.errors %}
<div class="text-danger">
{{ form.comment_status.errors }}
</div>
{% endif %}
</div>
</div>
<div class="row">
<div class="col-md-6 mb-3">
<label for="{{ form.type.id_for_label }}" class="form-label">{{ form.type.label }}</label>
{{ form.type }}
{% if form.type.errors %}
<div class="text-danger">
{{ form.type.errors }}
</div>
{% endif %}
</div>
<div class="col-md-6 mb-3">
<label for="{{ form.category.id_for_label }}" class="form-label">{{ form.category.label }}</label>
{{ form.category }}
<datalist id="category-list">
{% for category in all_categories %}
<option value="{{ category.name }}">
{% endfor %}
</datalist>
<div class="form-text">选择现有分类或创建新分类</div>
{% if form.category.errors %}
<div class="text-danger">
{{ form.category.errors }}
</div>
{% endif %}
</div>
</div>
<div class="mb-3">
<label for="{{ form.tags.id_for_label }}" class="form-label">{{ form.tags.label }}</label>
{{ form.tags }}
<datalist id="tag-list">
{% for tag in all_tags %}
<option value="{{ tag.name }}">
{% endfor %}
</datalist>
<div class="form-text">选择现有标签或创建新标签,多个标签用逗号分隔</div>
{% if form.tags.errors %}
<div class="text-danger">
{{ form.tags.errors }}
</div>
{% endif %}
</div>
<div class="mb-3 form-check">
{{ form.show_toc }}
<label for="{{ form.show_toc.id_for_label }}" class="form-check-label">{{ form.show_toc.label }}</label>
{% if form.show_toc.errors %}
<div class="text-danger">
{{ form.show_toc.errors }}
</div>
{% endif %}
</div>
<div class="mb-3">
<button type="submit" class="btn btn-primary">{% trans "更新" %}</button>
<a href="{{ article.get_absolute_url }}" class="btn btn-secondary">{% trans "取消" %}</a>
</div>
</form>
</div>
</div>
</div>
{% endblock %}

@ -0,0 +1,66 @@
{% extends "share_layout/base.html" %}
{% load static %}
{% load i18n %}
{% block title %}{% trans "My Articles" %}{% endblock %}
{% block content %}
<div class="container">
<div class="row">
<div class="col-md-12">
<div class="d-flex justify-content-between align-items-center mb-4">
<h2>{% trans "My Articles" %}</h2>
<a href="{% url 'blog:article_create' %}" class="btn btn-primary">
<i class="fas fa-plus"></i> {% trans "发布新文章" %}
</a>
</div>
{% if articles %}
<div class="table-responsive">
<table class="table table-striped">
<thead>
<tr>
<th>{% trans "标题" %}</th>
<th>{% trans "状态" %}</th>
<th>{% trans "分类" %}</th>
<th>{% trans "创建时间" %}</th>
<th>{% trans "浏览量" %}</th>
<th>{% trans "操作" %}</th>
</tr>
</thead>
<tbody>
{% for article in articles %}
<tr>
<td>
<a href="{{ article.get_absolute_url }}">{{ article.title }}</a>
</td>
<td>
{% if article.status == 'p' %}
<span class="badge bg-success">{% trans "已发布" %}</span>
{% else %}
<span class="badge bg-secondary">{% trans "草稿" %}</span>
{% endif %}
</td>
<td>{{ article.category.name }}</td>
<td>{{ article.creation_time|date:"Y-m-d H:i" }}</td>
<td>{{ article.views }}</td>
<td>
<a href="{% url 'blog:article_edit' article.id %}" class="btn btn-sm btn-outline-primary">
<i class="fas fa-edit"></i> {% trans "编辑" %}
</a>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% else %}
<div class="alert alert-info">
{% trans "您还没有创建任何文章。" %}
<a href="{% url 'blog:article_create' %}">{% trans "创建您的第一篇文章" %}</a>
</div>
{% endif %}
</div>
</div>
</div>
{% endblock %}

@ -50,6 +50,8 @@
</a>
{% if user.is_superuser %}
<a href="{{ article.get_admin_url }}">{% trans 'edit' %}</a>
{% elif user.is_authenticated and user == article.author %}
<a href="{% url 'blog:article_edit' article.id %}">{% trans 'edit' %}</a>
{% endif %}
</span>
</footer><!-- .entry-meta -->

@ -119,6 +119,8 @@
<ul>
<li><a href="/admin/" rel="nofollow">{% trans 'management site' %}</a></li>
{% if user.is_authenticated %}
<li><a href="{% url "blog:article_create" %}" rel="nofollow">{% trans '发布文章' %}</a></li>
<li><a href="{% url "blog:my_articles" %}" rel="nofollow">{% trans '我的文章' %}</a></li>
<li><a href="{% url "account:logout" %}" rel="nofollow">{% trans 'logout' %}</a>
</li>

@ -54,11 +54,6 @@
<![endif]-->
<link rel="stylesheet" href="{% static 'pygments/default.css' %}"/>
<link rel="stylesheet" href="{% static 'blog/css/nprogress.css' %}">
<link rel="stylesheet" href="{% static 'blog/css/modern-style.css' %}">
<link rel="stylesheet" href="{% static 'blog/css/background-options.css' %}">
{% if request.resolver_match.url_name == 'blog:archives' %}
<link rel="stylesheet" href="{% static 'blog/css/archives-style.css' %}">
{% endif %}
{% block compress_css %}
{% endblock %}
<!-- 插件CSS文件 - 集成到压缩系统 -->
@ -106,8 +101,6 @@
<script src="{% static 'blog/js/nprogress.js' %}"></script>
<script src="{% static 'blog/js/blog.js' %}"></script>
<script src="{% static 'blog/js/navigation.js' %}"></script>
<script src="{% static 'blog/js/modern-ui.js' %}"></script>
<script src="{% static 'blog/js/background-switcher.js' %}"></script>
{% block compress_js %}
{% endblock %}
<!-- 插件JS文件 - 集成到压缩系统 -->
@ -120,13 +113,6 @@
{% block footer %}
{% endblock %}
<!-- 返回顶部按钮 -->
<a href="#" id="back-to-top" aria-label="返回顶部">
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M7 14L12 9L17 14" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
</a>
<!-- 插件body资源 -->
{% plugin_body_resources %}
</body>

@ -1,17 +1,11 @@
{% load i18n %}
<nav id="site-navigation" class="main-navigation" role="navigation">
<div class="nav-container">
<ul id="primary-menu" class="nav-menu">
<li class="menu-item home-item">
<a href="/" title="{% trans 'index' %}">
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M3 9L12 2L21 9V20C21 20.5304 20.7893 21.0391 20.4142 21.4142C20.0391 21.7893 19.5304 22 19 22H5C4.46957 22 3.96086 21.7893 3.58579 21.4142C3.21071 21.0391 3 20.5304 3 20V9Z" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M9 22V12H15V22" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
<span>{% trans 'index' %}</span>
</a>
</li>
<div class="menu-%e8%8f%9c%e5%8d%95-container">
<ul id="menu-%e8%8f%9c%e5%8d%95" class="nav-menu">
<li id="menu-item-3498"
class="menu-item menu-item-type-custom menu-item-object-custom current-menu-item current_page_item menu-item-home menu-item-3498">
<a href="/">{% trans 'index' %}</a></li>
{% load blog_tags %}
{% query nav_category_list parent_category=None as root_categorys %}
@ -20,22 +14,18 @@
{% endfor %}
{% if nav_pages %}
{% for node in nav_pages %}
<li class="menu-item page-item">
<a href="{{ node.get_absolute_url }}" title="{{ node.title }}">{{ node.title }}</a>
<li id="menu-item-{{ node.pk }}"
class="menu-item menu-item-type-taxonomy menu-item-object-category menu-item-has-children menu-item-{{ node.pk }}">
<a href="{{ node.get_absolute_url }}">{{ node.title }}</a>
</li>
{% endfor %}
{% endif %}
<li class="menu-item archive-item">
<a href="{% url "blog:archives" %}" title="{% trans 'Article archive' %}">
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M3 7V19C3 20.1046 3.89543 21 5 21H19C20.1046 21 21 20.1046 21 19V7" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M8 3V8" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M16 3V8" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M3 11H21" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
<span>{% trans 'Article archive' %}</span>
</a>
<li class="menu-item menu-item-type-taxonomy menu-item-object-category menu-item-has-children">
<a href="{% url "blog:archives" %}">{% trans 'Article archive' %}</a>
</li>
</ul>
</div>
</nav><!-- #site-navigation -->

@ -1,26 +1,17 @@
<li class="menu-item category-item">
<a href="{{ node.get_absolute_url }}" title="{{ node.name }}">
{% if node.is_leaf_node %}
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M4 19.5A2.5 2.5 0 0 1 6.5 17H20" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M6.5 2H20v20H6.5A2.5 2.5 0 0 1 4 19.5v-15A2.5 2.5 0 0 1 6.5 2z" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
{% else %}
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
{% endif %}
<span>{{ node.name }}</span>
</a>
<li id="menu-item-{{ node.pk }}"
class="menu-item menu-item-type-taxonomy menu-item-object-category menu-item-has-children menu-item-{{ node.pk }}">
<a href="{{ node.get_absolute_url }}">{{ node.name }}</a>
{% load blog_tags %}
{% query nav_category_list parent_category=node as child_categorys %}
{% if child_categorys %}
<ul class="sub-menu">
{% for child in child_categorys %}
{% with node=child template_name="share_layout/nav_node.html" %}
{% include template_name %}
{% endwith %}
{% endfor %}
</ul>
{% endif %}
</li>

Loading…
Cancel
Save