Compare commits

...

31 Commits

Author SHA256 Message Date
5377272351 Merge develop into main 2025-08-01 14:40:14 +08:00
5cf22beafa Merge feature/tempfile into develop 2025-08-01 14:39:52 +08:00
f0674c29da Add tempfile 2025-08-01 14:39:26 +08:00
f65eb59b09 Merge develop into main 2025-07-28 17:49:16 +08:00
c1918b45b3 Merge bugfix/init_code into develop 2025-07-28 02:04:25 +08:00
a9389042d9 Init All Code 2025-07-28 01:58:12 +08:00
dd3a044b7c Merge develop into main 2025-07-28 00:54:43 +08:00
0f24bc5ec9 Merge feature/rss into develop 2025-07-28 00:54:18 +08:00
7ab9ed8e17 Add rss && Change web_view 2025-07-28 00:53:46 +08:00
127c101c70 Merge feature/link_me into develop 2025-07-28 00:33:14 +08:00
57531f8cdf Add link_me in the view 2025-07-28 00:32:48 +08:00
8cd9eecdc8 Merge bugfix into develop 2025-07-27 23:42:02 +08:00
9e4daa6dcf Add button with title 2025-07-27 23:40:35 +08:00
fb6e2df45d Merge develop into main 2025-07-27 23:28:43 +08:00
1f608c33ba Add change detail_view long in admin 2025-07-27 23:28:22 +08:00
200373e42f Merge develop into main 2025-07-27 23:23:24 +08:00
4d0ea1a55b Change index_view 200 into 20 2025-07-27 23:23:08 +08:00
6ed045e292 Merge develop into main 2025-07-27 23:15:02 +08:00
20d8180ad0 Merge feature/Category into develop 2025-07-27 23:14:43 +08:00
060388afe4 Updata python packages 2025-07-27 23:14:25 +08:00
55d873c450 Change view && setup category follow wiht details 2025-07-27 23:12:12 +08:00
35e03a8b70 Merge feature/Category into develop 2025-07-27 22:21:51 +08:00
c8970dc447 Add Category && Change webview theme 2025-07-27 22:21:29 +08:00
dca4f7a1d0 apart settings.py && add local_settings.py 2025-07-27 20:40:01 +08:00
742eec3a4d Merge develop into main 2025-07-27 18:24:30 +08:00
4d0d83bb0f Merge develop into main 2025-07-27 18:21:12 +08:00
3f85ceb0e8 Merge develop into main 2025-07-27 17:27:46 +08:00
2b571987be Merge develop into main 2025-07-27 15:05:40 +08:00
ee406be52b Merge develop into main 2025-07-26 23:45:28 +08:00
5c1f5f81d3 Merge develop into main 2025-07-26 20:38:24 +08:00
84e649081a Merge develop into main 2025-07-26 18:10:07 +08:00
17 changed files with 1580 additions and 105 deletions

View File

@@ -1,5 +1,5 @@
from django.contrib import admin from django.contrib import admin
from .models import Post from .models import Post, Category, SiteSettings
from django.db import models from django.db import models
from mdeditor.widgets import MDEditorWidget from mdeditor.widgets import MDEditorWidget
@@ -16,5 +16,21 @@ class PostAdmin(admin.ModelAdmin):
search_fields = ('title', 'content') search_fields = ('title', 'content')
class SiteSettingsAdmin(admin.ModelAdmin):
# 保持所有字段在列表中显示,方便直接编辑
list_display = ('id', 'summary_length', 'contact_email', 'contact_wechat', 'contact_linkedin', 'contact_github')
list_display_links = ('id',)
def has_add_permission(self, request):
# 限制只能有一个站点设置实例
return not SiteSettings.objects.exists()
def has_delete_permission(self, request, obj=None):
# 禁止删除站点设置
return False
# 注册自定义的PostAdmin # 注册自定义的PostAdmin
admin.site.register(Post, PostAdmin) admin.site.register(Post, PostAdmin)
admin.site.register(Category)
admin.site.register(SiteSettings, SiteSettingsAdmin)

96
myblog/blog/feeds.py Normal file
View File

@@ -0,0 +1,96 @@
from django.contrib.syndication.views import Feed
from django.urls import reverse
from django.shortcuts import get_object_or_404
from .models import Post, Category
class LatestPostsFeed(Feed):
title = "六桂流芳的com"
link = "/rss/"
description = "最新博客文章"
# 添加content_type使浏览器能正确显示RSS内容
content_type = 'application/xml; charset=utf-8'
def items(self):
return Post.objects.order_by('-publish_date')[:10]
def item_title(self, item):
return item.title
def item_description(self, item):
return item.get_markdown_content()
def item_link(self, item):
return reverse('detail', args=[item.pk])
# 添加分类RSS Feed
class CategoryPostsFeed(Feed):
# 添加content_type使浏览器能正确显示RSS内容
content_type = 'application/xml; charset=utf-8'
def get_object(self, request, category_id):
return get_object_or_404(Category, pk=category_id)
def title(self, obj):
return f"六桂流芳的com - {obj.name}分类"
def link(self, obj):
return reverse('category_feed', args=[obj.pk])
def description(self, obj):
return f"{obj.name}分类的最新博客文章"
def items(self, obj):
return Post.objects.filter(category=obj).order_by('-publish_date')[:10]
def item_title(self, item):
return item.title
def item_description(self, item):
return item.get_markdown_content()
def item_link(self, item):
return reverse('detail', args=[item.pk])
# 添加最新RSS Feed (与全部的区别在于数量限制)
class RecentPostsFeed(Feed):
title = "六桂流芳的com - 最新文章"
link = "/rss/recent/"
description = "最新的博客文章"
# 添加content_type使浏览器能正确显示RSS内容
content_type = 'application/xml; charset=utf-8'
def items(self):
return Post.objects.order_by('-publish_date')[:20]
def item_title(self, item):
return item.title
def item_description(self, item):
return item.get_markdown_content()
def item_link(self, item):
return reverse('detail', args=[item.pk])
# 添加全部RSS Feed
class AllPostsFeed(Feed):
title = "六桂流芳的com - 全部文章"
link = "/rss/all/"
description = "全部博客文章"
# 添加content_type使浏览器能正确显示RSS内容
content_type = 'application/xml; charset=utf-8'
def items(self):
return Post.objects.order_by('-publish_date')
def item_title(self, item):
return item.title
def item_description(self, item):
return item.get_markdown_content()
def item_link(self, item):
return reverse('detail', args=[item.pk])

View File

@@ -0,0 +1,25 @@
# Generated by Django 5.1 on 2025-07-27 13:26
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('blog', '0005_remove_post_image'),
]
operations = [
migrations.CreateModel(
name='Category',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=100, unique=True)),
('description', models.TextField(blank=True, null=True)),
('created_at', models.DateTimeField(auto_now_add=True)),
],
options={
'verbose_name_plural': 'Categories',
},
),
]

View File

@@ -0,0 +1,19 @@
# Generated by Django 5.1 on 2025-07-27 13:32
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('blog', '0006_category'),
]
operations = [
migrations.AddField(
model_name='post',
name='category',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='posts', to='blog.category'),
),
]

View File

@@ -0,0 +1,24 @@
# Generated by Django 5.1 on 2025-07-27 15:27
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('blog', '0007_post_category'),
]
operations = [
migrations.CreateModel(
name='SiteSettings',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('summary_length', models.IntegerField(default=50, help_text='文章摘要字符长度')),
],
options={
'verbose_name': '站点设置',
'verbose_name_plural': '站点设置',
},
),
]

View File

@@ -0,0 +1,33 @@
# Generated by Django 5.1 on 2025-07-27 15:53
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('blog', '0008_sitesettings'),
]
operations = [
migrations.AddField(
model_name='sitesettings',
name='contact_email',
field=models.EmailField(blank=True, help_text='联系邮箱', max_length=254, null=True),
),
migrations.AddField(
model_name='sitesettings',
name='contact_github',
field=models.URLField(blank=True, help_text='GitHub链接', null=True),
),
migrations.AddField(
model_name='sitesettings',
name='contact_linkedin',
field=models.URLField(blank=True, help_text='LinkedIn链接', null=True),
),
migrations.AddField(
model_name='sitesettings',
name='contact_wechat',
field=models.CharField(blank=True, help_text='微信号', max_length=100, null=True),
),
]

View File

@@ -0,0 +1,18 @@
# Generated by Django 5.1 on 2025-07-31 15:06
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('blog', '0009_sitesettings_contact_email_and_more'),
]
operations = [
migrations.AddField(
model_name='post',
name='status',
field=models.CharField(choices=[('draft', '草稿'), ('published', '已发布')], default='draft', max_length=10),
),
]

View File

@@ -6,12 +6,53 @@ from django.utils.safestring import mark_safe
from mdeditor.fields import MDTextField from mdeditor.fields import MDTextField
# 添加 Category 模型
class Category(models.Model):
name = models.CharField(max_length=100, unique=True)
description = models.TextField(blank=True, null=True)
created_at = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.name
class Meta:
verbose_name_plural = "Categories"
# 添加站点设置模型
class SiteSettings(models.Model):
summary_length = models.IntegerField(default=50, help_text="文章摘要字符长度")
contact_email = models.EmailField(blank=True, null=True, help_text="联系邮箱")
contact_wechat = models.CharField(max_length=100, blank=True, null=True, help_text="微信号")
contact_linkedin = models.URLField(blank=True, null=True, help_text="LinkedIn链接")
contact_github = models.URLField(blank=True, null=True, help_text="GitHub链接")
class Meta:
verbose_name = "站点设置"
verbose_name_plural = "站点设置"
def __str__(self):
return "站点设置"
class Post(models.Model): class Post(models.Model):
# 添加状态选项
DRAFT = 'draft'
PUBLISHED = 'published'
STATUS_CHOICES = [
(DRAFT, '草稿'),
(PUBLISHED, '已发布'),
]
title = models.CharField(max_length=100) title = models.CharField(max_length=100)
content = MDTextField() # ✅ 改成这里 content = MDTextField()
created_at = models.DateTimeField(auto_now_add=True) created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True) updated_at = models.DateTimeField(auto_now=True)
publish_date = models.DateTimeField(default=timezone.now) publish_date = models.DateTimeField(default=timezone.now)
# 添加分类字段,建立外键关系
category = models.ForeignKey(Category, on_delete=models.SET_NULL, null=True, blank=True, related_name='posts')
# 添加状态字段
status = models.CharField(max_length=10, choices=STATUS_CHOICES, default=DRAFT)
def __str__(self): def __str__(self):
return f"{self.title}" return f"{self.title}"

View File

@@ -0,0 +1,325 @@
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<title>联系我 - 六桂流芳的com</title>
<style>
body {
font-family: Arial, sans-serif;
margin: 0;
padding: 0;
}
.container {
display: flex;
max-width: 1200px;
margin: 0 auto;
padding: 20px;
min-height: 100vh;
}
.sidebar {
width: 250px;
padding-right: 20px;
border-right: 1px solid #eee;
position: -webkit-sticky;
position: sticky;
top: 20px;
align-self: flex-start;
height: fit-content;
}
.sidebar h3 {
color: #333;
border-bottom: 2px solid #007cba;
padding-bottom: 10px;
}
.sidebar ul {
list-style: none;
padding: 0;
}
.sidebar ul li {
margin: 10px 0;
}
.sidebar ul li a {
text-decoration: none;
color: #666;
display: block;
padding: 8px 12px;
border-radius: 4px;
transition: all 0.3s;
}
.sidebar ul li a:hover,
.sidebar ul li a.active {
background-color: #007cba;
color: white;
}
.search-box {
margin-bottom: 20px;
}
.search-box input[type="text"] {
width: 100%;
padding: 8px;
border: 1px solid #ddd;
border-radius: 4px;
box-sizing: border-box;
}
.search-box input[type="submit"] {
width: 100%;
padding: 8px;
background-color: #007cba;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
margin-top: 5px;
}
.search-box input[type="submit"]:hover {
background-color: #005a87;
}
.search-type {
margin: 10px 0;
}
.search-type label {
display: block;
margin: 5px 0;
font-size: 14px;
color: #666;
}
.search-type input[type="radio"] {
margin-right: 5px;
}
.main-content {
flex: 1;
padding-left: 20px;
}
.main-content h1 {
color: #333;
border-bottom: 2px solid #eee;
padding-bottom: 10px;
}
footer {
position: fixed;
bottom: 0;
width: 100%;
text-align: center;
font-size: 12px;
color: #999;
background-color: white;
padding: 5px 0;
}
.contact-info {
background-color: #f9f9f9;
padding: 15px;
border-radius: 5px;
margin-top: 20px;
border: 1px solid #eee;
}
.contact-info h3 {
margin-top: 0;
color: #333;
border-bottom: 2px solid #007cba;
padding-bottom: 10px;
}
.contact-info ul {
padding-left: 20px;
}
.contact-info li {
margin: 10px 0;
color: #666;
}
/* 添加联系我主内容区域样式 */
.contact-main {
background-color: #f9f9f9;
padding: 20px;
border-radius: 5px;
border: 1px solid #eee;
}
.contact-main h2 {
color: #333;
border-bottom: 2px solid #007cba;
padding-bottom: 10px;
}
.contact-description {
margin: 15px 0;
line-height: 1.6;
color: #666;
}
.contact-details {
list-style: none;
padding: 0;
}
.contact-details li {
padding: 10px 0;
border-bottom: 1px solid #eee;
}
.contact-details li:last-child {
border-bottom: none;
}
.contact-label {
font-weight: bold;
color: #333;
display: inline-block;
width: 80px;
}
.contact-value {
color: #666;
}
/* 添加导航栏样式 */
.top-nav {
background-color: #007cba;
padding: 10px 0;
margin-bottom: 20px;
}
.nav-container {
max-width: 1200px;
margin: 0 auto;
padding: 0 20px;
}
.nav-links {
list-style: none;
padding: 0;
margin: 0;
display: flex;
}
.nav-links li {
margin-right: 20px;
}
.nav-links li a {
color: white;
text-decoration: none;
padding: 8px 12px;
border-radius: 4px;
transition: background-color 0.3s;
}
.nav-links li a:hover {
background-color: rgba(255, 255, 255, 0.2);
}
</style>
</head>
<body>
<!-- 添加网站标题和导航栏 -->
<div class="top-nav">
<div class="nav-container">
<h1 style="text-align: left; margin: 0; padding: 0;">
<a href="{% url 'index' %}" style="text-decoration: none; color: white;">六桂流芳的com</a>
</h1>
<ul class="nav-links">
<li><a href="{% url 'index' %}">首页</a></li>
<li><a href="{% url 'rss_page' %}">RSS订阅</a></li>
<li><a href="{% url 'contact_page' %}">联系我</a></li>
</ul>
</div>
</div>
<div class="container">
<div class="sidebar">
<div class="search-box">
<form method="get" action="{% url 'index' %}">
<input type="text" name="q" placeholder="搜索文章..." value="{{ query|default:'' }}">
<div class="search-type">
<label>
<input type="radio" name="search_type" value="all" checked>
全文搜索
</label>
<label>
<input type="radio" name="search_type" value="title">
标题搜索
</label>
<label>
<input type="radio" name="search_type" value="content">
内容搜索
</label>
</div>
<input type="submit" value="搜索">
</form>
</div>
<h3>文章分类</h3>
<ul>
<li><a href="{% url 'index' %}">全部文章</a></li>
{% for category in categories %}
<li>
<a href="{% url 'index' %}?category={{ category.id }}">
{{ category.name }}
</a>
</li>
{% endfor %}
</ul>
</div>
<div class="main-content">
<div class="contact-main">
<h2>联系我</h2>
<div class="contact-description">
如果您有任何问题或想与我交流,可以通过以下方式联系我:
</div>
<ul class="contact-details">
{% if site_settings.contact_email %}
<li>
<span class="contact-label">邮箱:</span>
<span class="contact-value">{{ site_settings.contact_email }}</span>
</li>
{% endif %}
{% if site_settings.contact_wechat %}
<li>
<span class="contact-label">微信:</span>
<span class="contact-value">{{ site_settings.contact_wechat }}</span>
</li>
{% endif %}
{% if site_settings.contact_linkedin %}
<li>
<span class="contact-label">LinkedIn:</span>
<span class="contact-value">{{ site_settings.contact_linkedin }}</span>
</li>
{% endif %}
{% if site_settings.contact_github %}
<li>
<span class="contact-label">GitHub:</span>
<span class="contact-value">{{ site_settings.contact_github }}</span>
</li>
{% endif %}
</ul>
</div>
</div>
</div>
<footer>
<a href="https://beian.miit.gov.cn/" target="_blank">闽ICP备2023010767号-2</a>
</footer>
</body>
</html>

View File

@@ -3,21 +3,121 @@
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<title>{{ post.title }}</title> <title>{{ post.title }}</title>
</head>
<body>
<!-- 添加样式使整个页面内容居中显示 -->
<div style="max-width: 800px; margin: 0 auto; padding: 0 20px;">
<!-- 添加网站标题 -->
<h1 style="text-align: left;">六桂流芳的com</h1>
<h1 style="text-align: center;">{{ post.title }}</h1>
<!-- 将返回首页链接放在发布时间的右侧 -->
<div style="display: flex; align-items: center; justify-content: space-between;">
<p>发布时间:{{ post.publish_date|date:"Y年n月j日 H:i" }}</p>
<a href="{% url 'index' %}" style="margin-left: 20px; white-space: nowrap;">返回首页</a>
</div>
<!-- 添加CSS样式限制图片大小 -->
<div style="max-width: 100%;">
<style> <style>
body {
font-family: Arial, sans-serif;
margin: 0;
padding: 0;
}
.container {
display: flex;
max-width: 1200px;
margin: 0 auto;
padding: 20px;
min-height: 100vh;
}
.sidebar {
width: 250px;
padding-right: 20px;
border-right: 1px solid #eee;
position: -webkit-sticky;
position: sticky;
top: 20px;
align-self: flex-start;
height: fit-content;
}
.sidebar h3 {
color: #333;
border-bottom: 2px solid #007cba;
padding-bottom: 10px;
}
.sidebar ul {
list-style: none;
padding: 0;
}
.sidebar ul li {
margin: 10px 0;
}
.sidebar ul li a {
text-decoration: none;
color: #666;
display: block;
padding: 8px 12px;
border-radius: 4px;
transition: all 0.3s;
}
.sidebar ul li a:hover,
.sidebar ul li a.active {
background-color: #007cba;
color: white;
}
.search-box {
margin-bottom: 20px;
}
.search-box input[type="text"] {
width: 100%;
padding: 8px;
border: 1px solid #ddd;
border-radius: 4px;
box-sizing: border-box;
}
.search-box input[type="submit"] {
width: 100%;
padding: 8px;
background-color: #007cba;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
margin-top: 5px;
}
.search-box input[type="submit"]:hover {
background-color: #005a87;
}
.search-type {
margin: 10px 0;
}
.search-type label {
display: block;
margin: 5px 0;
font-size: 14px;
color: #666;
}
.search-type input[type="radio"] {
margin-right: 5px;
}
.main-content {
flex: 1;
padding-left: 20px;
}
.main-content h1 {
color: #333;
border-bottom: 2px solid #eee;
padding-bottom: 10px;
}
.post-meta {
font-size: 14px;
color: #999;
margin-bottom: 20px;
}
.post-content img { .post-content img {
max-width: 100%; max-width: 100%;
height: auto; height: auto;
@@ -48,14 +148,123 @@
border-radius: 0; border-radius: 0;
font-size: 0.9em; font-size: 0.9em;
} }
footer {
position: fixed;
bottom: 0;
width: 100%;
text-align: center;
font-size: 12px;
color: #999;
background-color: white;
padding: 5px 0;
}
/* 添加导航栏样式 */
.top-nav {
background-color: #007cba;
padding: 10px 0;
margin-bottom: 20px;
}
.nav-container {
max-width: 1200px;
margin: 0 auto;
padding: 0 20px;
}
.nav-links {
list-style: none;
padding: 0;
margin: 0;
display: flex;
}
.nav-links li {
margin-right: 20px;
}
.nav-links li a {
color: white;
text-decoration: none;
padding: 8px 12px;
border-radius: 4px;
transition: background-color 0.3s;
}
.nav-links li a:hover {
background-color: rgba(255, 255, 255, 0.2);
}
</style> </style>
<div class="post-content">{{ post.get_markdown_content }}</div> </head>
<body>
<!-- 添加网站标题和导航栏 -->
<div class="top-nav">
<div class="nav-container">
<h1 style="text-align: left; margin: 0; padding: 0;">
<a href="{% url 'index' %}" style="text-decoration: none; color: white;">六桂流芳的com</a>
</h1>
<ul class="nav-links">
<li><a href="{% url 'index' %}">首页</a></li>
<li><a href="{% url 'rss_page' %}">RSS订阅</a></li>
<li><a href="{% url 'contact_page' %}">联系我</a></li>
</ul>
</div> </div>
<br>
</div> </div>
<footer style="position: fixed; bottom: 0; width: 100%; text-align: center; font-size: 12px; color: #999; background-color: white; padding: 5px 0;">
<div class="container">
<div class="sidebar">
<div class="search-box">
<form method="get" action="{% url 'index' %}">
<input type="text" name="q" placeholder="搜索文章..." value="{{ query|default:'' }}">
<div class="search-type">
<label>
<input type="radio" name="search_type" value="all" checked>
全文搜索
</label>
<label>
<input type="radio" name="search_type" value="title">
标题搜索
</label>
<label>
<input type="radio" name="search_type" value="content">
内容搜索
</label>
</div>
<input type="submit" value="搜索">
</form>
</div>
<h3>文章分类</h3>
<ul>
<li><a href="{% url 'index' %}" {% if not post.category %}class="active"{% endif %}>全部文章</a></li>
{% for category in categories %}
<li>
<a href="{% url 'index' %}?category={{ category.id }}"
{% if post.category and post.category.id == category.id %}class="active"{% endif %}>
{{ category.name }}
</a>
</li>
{% endfor %}
</ul>
</div>
<div class="main-content">
<h1 style="text-align: center;">{{ post.title }}</h1>
<div class="post-meta">
<div style="display: flex; justify-content: space-between; align-items: center;">
<span>发布时间:{{ post.publish_date|date:"Y年n月j日 H:i" }}</span>
<a href="{% url 'index' %}" style="white-space: nowrap;">返回首页</a>
</div>
</div>
<div class="post-content">{{ post.get_markdown_content }}</div>
<br>
</div>
</div>
<footer>
<a href="https://beian.miit.gov.cn/" target="_blank">闽ICP备2023010767号-2</a> <a href="https://beian.miit.gov.cn/" target="_blank">闽ICP备2023010767号-2</a>
</footer> </footer>
</body> </body>
</html> </html>

View File

@@ -3,22 +3,281 @@
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<title>六桂流芳的com</title> <title>六桂流芳的com</title>
<style>
body {
font-family: Arial, sans-serif;
margin: 0;
padding: 0;
}
.container {
display: flex;
max-width: 1200px;
margin: 0 auto;
padding: 20px;
min-height: 100vh;
}
.sidebar {
width: 250px;
padding-right: 20px;
border-right: 1px solid #eee;
position: -webkit-sticky;
position: sticky;
top: 20px;
align-self: flex-start;
height: fit-content;
}
.sidebar h3 {
color: #333;
border-bottom: 2px solid #007cba;
padding-bottom: 10px;
}
.sidebar ul {
list-style: none;
padding: 0;
}
.sidebar ul li {
margin: 10px 0;
}
.sidebar ul li a {
text-decoration: none;
color: #666;
display: block;
padding: 8px 12px;
border-radius: 4px;
transition: all 0.3s;
}
.sidebar ul li a:hover,
.sidebar ul li a.active {
background-color: #007cba;
color: white;
}
.search-box {
margin-bottom: 20px;
}
.search-box input[type="text"] {
width: 100%;
padding: 8px;
border: 1px solid #ddd;
border-radius: 4px;
box-sizing: border-box;
}
.search-box input[type="submit"] {
width: 100%;
padding: 8px;
background-color: #007cba;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
margin-top: 5px;
}
.search-box input[type="submit"]:hover {
background-color: #005a87;
}
.search-type {
margin: 10px 0;
}
.search-type label {
display: block;
margin: 5px 0;
font-size: 14px;
color: #666;
}
.search-type input[type="radio"] {
margin-right: 5px;
}
.main-content {
flex: 1;
padding-left: 20px;
}
.main-content h1 {
color: #333;
border-bottom: 2px solid #eee;
padding-bottom: 10px;
}
.posts-list {
list-style: none;
padding: 0;
}
.posts-list li {
padding: 15px 0;
border-bottom: 1px solid #eee;
}
.posts-list li:last-child {
border-bottom: none;
}
.post-title {
font-size: 18px;
font-weight: bold;
margin-bottom: 8px;
}
.post-title a {
text-decoration: none;
color: #007cba;
}
.post-meta {
font-size: 14px;
color: #999;
}
.post-category {
display: inline-block;
background-color: #f0f0f0;
padding: 2px 6px;
border-radius: 3px;
font-size: 12px;
margin-top: 5px;
}
.post-summary {
margin: 10px 0;
line-height: 1.6;
color: #666;
}
footer {
}
/* 添加导航栏样式 */
.top-nav {
background-color: #007cba;
padding: 10px 0;
margin-bottom: 20px;
}
.nav-container {
max-width: 1200px;
margin: 0 auto;
padding: 0 20px;
}
.nav-links {
list-style: none;
padding: 0;
margin: 0;
display: flex;
}
.nav-links li {
margin-right: 20px;
}
.nav-links li a {
color: white;
text-decoration: none;
padding: 8px 12px;
border-radius: 4px;
transition: background-color 0.3s;
}
.nav-links li a:hover {
background-color: rgba(255, 255, 255, 0.2);
}
</style>
</head> </head>
<body> <body>
<!-- 修改样式使页面内容靠左对齐,标题显示在左上角 --> <!-- 添加网站标题和导航栏 -->
<div style="max-width: 800px; margin: 0 auto; padding: 0 20px; text-align: left;"> <div class="top-nav">
<h1 style="text-align: left;">六桂流芳的com</h1> <div class="nav-container">
<ul style="list-style: none; padding: 0;"> <h1 style="text-align: left; margin: 0; padding: 0;">
{% for post in posts %} <a href="{% url 'index' %}" style="text-decoration: none; color: white;">六桂流芳的com</a>
<li style="margin: 10px 0; text-align: left;"> </h1>
<div style="display: flex; justify-content: space-between; align-items: center;"> <ul class="nav-links">
<a href="{% url 'detail' post.id %}">{{ post.title }}</a> <li><a href="{% url 'index' %}">首页</a></li>
<span style="margin-left: 10px; white-space: nowrap;">发布时间:{{ post.publish_date|date:"Y年n月j日 H:i" }}</span> <li><a href="{% url 'rss_page' %}">RSS订阅</a></li>
<li><a href="{% url 'contact_page' %}">联系我</a></li>
</ul>
</div> </div>
</div>
<div class="container">
<div class="sidebar">
<div class="search-box">
<form method="get">
<input type="text" name="q" placeholder="搜索文章..." value="{{ query|default:'' }}">
<div class="search-type">
<label>
<input type="radio" name="search_type" value="all"
{% if search_type != 'title' and search_type != 'content' %}checked{% endif %}>
全文搜索
</label>
<label>
<input type="radio" name="search_type" value="title"
{% if search_type == 'title' %}checked{% endif %}>
标题搜索
</label>
<label>
<input type="radio" name="search_type" value="content"
{% if search_type == 'content' %}checked{% endif %}>
内容搜索
</label>
</div>
<input type="submit" value="搜索">
</form>
</div>
<h3>文章分类</h3>
<ul>
<li><a href="{% url 'index' %}" {% if not selected_category %}class="active"{% endif %}>全部文章</a></li>
{% for category in categories %}
<li>
<a href="?category={{ category.id }}"
{% if selected_category == category.id|stringformat:"s" %}class="active"{% endif %}>
{{ category.name }}
</a>
</li> </li>
{% endfor %} {% endfor %}
</ul> </ul>
</div>
<div class="main-content">
<ul class="posts-list">
{% for post in posts %}
<li>
<div class="post-title">
<a href="{% url 'detail' post.id %}">{{ post.title }}</a>
</div>
<div class="post-meta">
发布时间:{{ post.publish_date|date:"Y年n月j日 H:i" }}
</div>
<div class="post-summary">
{{ post.summary|safe }}
</div>
{% if post.category %}
<div class="post-category">
{{ post.category.name }}
</div>
{% endif %}
</li>
{% endfor %}
</ul>
</div>
</div> </div>
<footer style="position: fixed; bottom: 0; width: 100%; text-align: center; font-size: 12px; color: #999; background-color: white; padding: 5px 0;"> <footer style="position: fixed; bottom: 0; width: 100%; text-align: center; font-size: 12px; color: #999; background-color: white; padding: 5px 0;">
<a href="https://beian.miit.gov.cn/" target="_blank">闽ICP备2023010767号-2</a> <a href="https://beian.miit.gov.cn/" target="_blank">闽ICP备2023010767号-2</a>
</footer> </footer>

View File

@@ -0,0 +1,303 @@
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<title>RSS订阅 - 六桂流芳的com</title>
<style>
body {
font-family: Arial, sans-serif;
margin: 0;
padding: 0;
}
.container {
display: flex;
max-width: 1200px;
margin: 0 auto;
padding: 20px;
min-height: 100vh;
}
.sidebar {
width: 250px;
padding-right: 20px;
border-right: 1px solid #eee;
position: -webkit-sticky;
position: sticky;
top: 20px;
align-self: flex-start;
height: fit-content;
}
.sidebar h3 {
color: #333;
border-bottom: 2px solid #007cba;
padding-bottom: 10px;
}
.sidebar ul {
list-style: none;
padding: 0;
}
.sidebar ul li {
margin: 10px 0;
}
.sidebar ul li a {
text-decoration: none;
color: #666;
display: block;
padding: 8px 12px;
border-radius: 4px;
transition: all 0.3s;
}
.sidebar ul li a:hover,
.sidebar ul li a.active {
background-color: #007cba;
color: white;
}
.search-box {
margin-bottom: 20px;
}
.search-box input[type="text"] {
width: 100%;
padding: 8px;
border: 1px solid #ddd;
border-radius: 4px;
box-sizing: border-box;
}
.search-box input[type="submit"] {
width: 100%;
padding: 8px;
background-color: #007cba;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
margin-top: 5px;
}
.search-box input[type="submit"]:hover {
background-color: #005a87;
}
.search-type {
margin: 10px 0;
}
.search-type label {
display: block;
margin: 5px 0;
font-size: 14px;
color: #666;
}
.search-type input[type="radio"] {
margin-right: 5px;
}
.main-content {
flex: 1;
padding-left: 20px;
}
.main-content h1 {
color: #333;
border-bottom: 2px solid #eee;
padding-bottom: 10px;
}
footer {
position: fixed;
bottom: 0;
width: 100%;
text-align: center;
font-size: 12px;
color: #999;
background-color: white;
padding: 5px 0;
}
/* 添加导航栏样式 */
.top-nav {
background-color: #007cba;
padding: 10px 0;
margin-bottom: 20px;
}
.nav-container {
max-width: 1200px;
margin: 0 auto;
padding: 0 20px;
}
.nav-links {
list-style: none;
padding: 0;
margin: 0;
display: flex;
}
.nav-links li {
margin-right: 20px;
}
.nav-links li a {
color: white;
text-decoration: none;
padding: 8px 12px;
border-radius: 4px;
transition: background-color 0.3s;
}
.nav-links li a:hover {
background-color: rgba(255, 255, 255, 0.2);
}
/* RSS主内容区域样式 */
.rss-main {
background-color: #f9f9f9;
padding: 20px;
border-radius: 5px;
border: 1px solid #eee;
}
.rss-main h2 {
color: #333;
border-bottom: 2px solid #ff6600;
padding-bottom: 10px;
}
.rss-description {
margin: 15px 0;
line-height: 1.6;
color: #666;
}
.rss-feed-list {
list-style: none;
padding: 0;
}
.rss-feed-list li {
padding: 10px 0;
border-bottom: 1px solid #eee;
}
.rss-feed-list li:last-child {
border-bottom: none;
}
.rss-feed-link {
font-size: 16px;
font-weight: bold;
color: #007cba;
text-decoration: none;
}
.rss-feed-link:hover {
text-decoration: underline;
}
.rss-feed-description {
font-size: 14px;
color: #999;
margin-top: 5px;
}
</style>
</head>
<body>
<!-- 添加网站标题和导航栏 -->
<div class="top-nav">
<div class="nav-container">
<h1 style="text-align: left; margin: 0; padding: 0;">
<a href="{% url 'index' %}" style="text-decoration: none; color: white;">六桂流芳的com</a>
</h1>
<ul class="nav-links">
<li><a href="{% url 'index' %}">首页</a></li>
<li><a href="{% url 'rss_page' %}">RSS订阅</a></li>
<li><a href="{% url 'contact_page' %}">联系我</a></li>
</ul>
</div>
</div>
<div class="container">
<div class="sidebar">
<div class="search-box">
<form method="get" action="{% url 'index' %}">
<input type="text" name="q" placeholder="搜索文章..." value="{{ query|default:'' }}">
<div class="search-type">
<label>
<input type="radio" name="search_type" value="all" checked>
全文搜索
</label>
<label>
<input type="radio" name="search_type" value="title">
标题搜索
</label>
<label>
<input type="radio" name="search_type" value="content">
内容搜索
</label>
</div>
<input type="submit" value="搜索">
</form>
</div>
<h3>文章分类</h3>
<ul>
<li><a href="{% url 'index' %}">全部文章</a></li>
{% for category in categories %}
<li>
<a href="{% url 'index' %}?category={{ category.id }}">
{{ category.name }}
</a>
</li>
{% endfor %}
</ul>
</div>
<div class="main-content">
<div class="rss-main">
<h2>RSS订阅</h2>
<div class="rss-description">
RSS是一种用于发布经常更新的内容的网页格式。通过RSS阅读器您可以订阅我们的内容及时获取最新文章更新。
点击下面的链接可以在浏览器中查看RSS内容使用RSS阅读器订阅时请复制链接地址。
</div>
<ul class="rss-feed-list">
<li>
<a href="{% url 'rss_feed' %}" class="rss-feed-link" target="_blank">最新文章</a>
<div class="rss-feed-description">包含最新的10篇博客文章</div>
</li>
<li>
<a href="{% url 'recent_feed' %}" class="rss-feed-link" target="_blank">最近更新</a>
<div class="rss-feed-description">包含最新的20篇博客文章</div>
</li>
<li>
<a href="{% url 'all_feed' %}" class="rss-feed-link" target="_blank">全部文章</a>
<div class="rss-feed-description">包含所有博客文章</div>
</li>
{% for category in categories %}
<li>
<a href="{% url 'category_feed' category.id %}" class="rss-feed-link"
target="_blank">{{ category.name }}分类</a>
<div class="rss-feed-description">{{ category.name }}分类下的最新博客文章</div>
</li>
{% endfor %}
</ul>
</div>
</div>
</div>
<footer>
<a href="https://beian.miit.gov.cn/" target="_blank">闽ICP备2023010767号-2</a>
</footer>
</body>
</html>

View File

@@ -1,7 +1,16 @@
from django.urls import path from django.urls import path
from . import views from . import views
from .feeds import LatestPostsFeed, CategoryPostsFeed, RecentPostsFeed, AllPostsFeed
urlpatterns = [ urlpatterns = [
path('', views.index, name='index'), path('', views.index, name='index'),
path('post/<int:post_id>/', views.detail, name='detail'), path('post/<int:post_id>/', views.detail, name='detail'),
# 添加RSS页面路由
path('rss/page/', views.rss_page, name='rss_page'),
# 添加联系我页面路由
path('contact/', views.contact_page, name='contact_page'),
path('rss/', LatestPostsFeed(), name='rss_feed'),
path('rss/category/<int:category_id>/', CategoryPostsFeed(), name='category_feed'),
path('rss/recent/', RecentPostsFeed(), name='recent_feed'),
path('rss/all/', AllPostsFeed(), name='all_feed'),
] ]

View File

@@ -1,14 +1,137 @@
from django.shortcuts import render, get_object_or_404 from django.shortcuts import render, get_object_or_404
from .models import Post from .models import Post, Category, SiteSettings
def post_list(request):
# 只显示已发布的文章
posts = Post.objects.filter(status=Post.PUBLISHED).order_by('-publish_date')
categories = Category.objects.all()
# 获取站点设置
site_settings = SiteSettings.objects.first()
summary_length = site_settings.summary_length if site_settings else 50
return render(request, 'blog/post_list.html', {
'posts': posts,
'categories': categories,
'summary_length': summary_length
})
def post_detail(request, pk):
# 只允许查看已发布的文章
post = get_object_or_404(Post, pk=pk, status=Post.PUBLISHED)
categories = Category.objects.all()
return render(request, 'blog/post_detail.html', {
'post': post,
'categories': categories
})
def category_posts(request, category_id):
category = get_object_or_404(Category, id=category_id)
# 只显示该分类下已发布的文章
posts = Post.objects.filter(category=category, status=Post.PUBLISHED).order_by('-publish_date')
categories = Category.objects.all()
# 获取站点设置
site_settings = SiteSettings.objects.first()
summary_length = site_settings.summary_length if site_settings else 50
return render(request, 'blog/post_list.html', {
'posts': posts,
'categories': categories,
'current_category': category,
'summary_length': summary_length
})
# Create your views here. # Create your views here.
def index(request): def index(request):
posts = Post.objects.order_by('created_at') # 获取所有分类
return render(request, 'blog/index.html', {'posts': posts}) categories = Category.objects.all()
# 获取查询参数中的分类ID
category_id = request.GET.get('category')
# 获取搜索关键词
query = request.GET.get('q')
# 获取搜索类型参数
search_type = request.GET.get('search_type', 'all')
# 获取站点设置,如果不存在则使用默认值
try:
site_settings = SiteSettings.objects.first()
summary_length = site_settings.summary_length if site_settings else 50
except SiteSettings.DoesNotExist:
site_settings = None
summary_length = 50
# 根据分类和搜索关键词筛选文章
if query:
# 根据搜索类型执行不同的搜索
if search_type == 'title':
posts = Post.objects.filter(title__icontains=query, status=Post.PUBLISHED)
elif search_type == 'content':
posts = Post.objects.filter(content__icontains=query, status=Post.PUBLISHED)
else:
# 默认按标题和内容搜索
posts = Post.objects.filter(title__icontains=query, status=Post.PUBLISHED) | Post.objects.filter(content__icontains=query, status=Post.PUBLISHED)
elif category_id:
posts = Post.objects.filter(category_id=category_id, status=Post.PUBLISHED)
else:
posts = Post.objects.filter(status=Post.PUBLISHED)
posts = posts.order_by('-publish_date').distinct()
# 为每篇文章添加摘要(根据设置的字符长度)
for post in posts:
import re
clean_content = re.sub(r'<[^>]+>', '', post.get_markdown_content())
post.summary = clean_content[:summary_length] + '...' if len(clean_content) > summary_length else clean_content
return render(request, 'blog/index.html', {
'posts': posts,
'categories': categories,
'selected_category': category_id,
'query': query,
'search_type': search_type,
'site_settings': site_settings
})
def detail(request, post_id): def detail(request, post_id):
post = get_object_or_404(Post, pk=post_id) post = get_object_or_404(Post, pk=post_id, status=Post.PUBLISHED)
return render(request, 'blog/detail.html', {'post': post}) categories = Category.objects.all() # 获取所有分类用于侧边栏
try:
site_settings = SiteSettings.objects.first()
except SiteSettings.DoesNotExist:
site_settings = None
return render(request, 'blog/detail.html', {
'post': post,
'categories': categories,
'site_settings': site_settings})
# 添加RSS页面视图
def rss_page(request):
categories = Category.objects.all()
try:
site_settings = SiteSettings.objects.first()
except SiteSettings.DoesNotExist:
site_settings = None
return render(request, 'blog/rss.html', {
'categories': categories,
'site_settings': site_settings
})
# 添加联系我页面视图
def contact_page(request):
try:
site_settings = SiteSettings.objects.first()
except SiteSettings.DoesNotExist:
site_settings = None
return render(request, 'blog/contact.html', {
'site_settings': site_settings
})

View File

@@ -11,6 +11,7 @@ https://docs.djangoproject.com/en/5.2/ref/settings/
""" """
from pathlib import Path from pathlib import Path
import os
# Build paths inside the project like this: BASE_DIR / 'subdir'. # Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent.parent BASE_DIR = Path(__file__).resolve().parent.parent
@@ -112,7 +113,6 @@ USE_TZ = True
STATIC_URL = 'static/' STATIC_URL = 'static/'
# 添加媒体文件配置 # 添加媒体文件配置
import os
MEDIA_URL = '/media/' MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'media') MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
@@ -157,3 +157,9 @@ MDEDITOR_CONFIGS = {
} }
X_FRAME_OPTIONS = 'SAMEORIGIN' X_FRAME_OPTIONS = 'SAMEORIGIN'
# settings.py 末尾加上这段
try:
from .local_settings import *
except ImportError:
pass

View File

@@ -1,31 +0,0 @@
asgiref==3.9.1
asttokens==3.0.0
click==8.2.1
configparser==7.2.0
decorator==5.2.1
executing==2.2.0
h11==0.16.0
ipython==9.4.0
ipython_pygments_lexers==1.1.1
jedi==0.19.2
matplotlib-inline==0.1.7
numpy==2.3.2
packaging==25.0
pandas==2.3.1
parso==0.8.4
pexpect==4.9.0
prompt_toolkit==3.0.51
psutil==7.0.0
ptyprocess==0.7.0
pure_eval==0.2.3
Pygments==2.19.2
python-dateutil==2.9.0.post0
pytz==2025.2
six==1.17.0
sqlparse==0.5.3
stack-data==0.6.3
traitlets==5.14.3
tzdata==2025.2
uv==0.8.3
uvicorn==0.35.0
wcwidth==0.2.13

View File

@@ -5,9 +5,9 @@ certifi==2025.7.14
charset-normalizer==3.4.2 charset-normalizer==3.4.2
click==8.2.1 click==8.2.1
decorator==5.2.1 decorator==5.2.1
Django==5.2.4 Django==5.1
django-mdeditor==0.1.20 django-mdeditor==0.1.20
django-summernote==0.8.20.0 emoji==2.14.1
executing==2.2.0 executing==2.2.0
gunicorn==23.0.0 gunicorn==23.0.0
h11==0.16.0 h11==0.16.0
@@ -15,7 +15,7 @@ idna==3.10
ipython==9.4.0 ipython==9.4.0
ipython_pygments_lexers==1.1.1 ipython_pygments_lexers==1.1.1
jedi==0.19.2 jedi==0.19.2
Markdown Markdown==3.5.2
martor==1.6.45 martor==1.6.45
matplotlib-inline==0.1.7 matplotlib-inline==0.1.7
numpy==2.3.2 numpy==2.3.2