1.2.1更新,详情请看readme.md
This commit is contained in:
parent
796302a954
commit
fb69ea4172
Binary file not shown.
@ -34,6 +34,7 @@ INSTALLED_APPS = [
|
|||||||
'simpleui',
|
'simpleui',
|
||||||
'home',
|
'home',
|
||||||
'api',
|
'api',
|
||||||
|
'comment',
|
||||||
'django.contrib.admin',
|
'django.contrib.admin',
|
||||||
'django.contrib.auth',
|
'django.contrib.auth',
|
||||||
'django.contrib.contenttypes',
|
'django.contrib.contenttypes',
|
||||||
|
12
README.md
12
README.md
@ -6,10 +6,12 @@
|
|||||||
# 目录结构描述
|
# 目录结构描述
|
||||||
├── ReadMe.md // 帮助文档
|
├── ReadMe.md // 帮助文档
|
||||||
|
|
||||||
├── home // 首页app
|
├── home // 文章相关app
|
||||||
|
|
||||||
├── api // 项目api
|
├── api // 项目api
|
||||||
|
|
||||||
|
├── comment // 评论app
|
||||||
|
|
||||||
└── Echo-Z // 项目配置等目录
|
└── Echo-Z // 项目配置等目录
|
||||||
|
|
||||||
# 使用说明
|
# 使用说明
|
||||||
@ -35,3 +37,11 @@ python manage.py runserver 9999
|
|||||||
###### 1.2.0
|
###### 1.2.0
|
||||||
2025年6月19日
|
2025年6月19日
|
||||||
完成文章的基础管理、文章的列表显示、文章的详细信息的功能编写,功能有待完善
|
完成文章的基础管理、文章的列表显示、文章的详细信息的功能编写,功能有待完善
|
||||||
|
|
||||||
|
###### 1.2.1
|
||||||
|
2025年6月23日
|
||||||
|
新增
|
||||||
|
1.文章详情页上一篇、下一篇功能
|
||||||
|
2.文章详情页评论列表显示
|
||||||
|
3.文章详情页评论发布
|
||||||
|
4.文章详情页点赞与取消点赞
|
||||||
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
api/__pycache__/views.cpython-39.pyc
Normal file
BIN
api/__pycache__/views.cpython-39.pyc
Normal file
Binary file not shown.
@ -1,6 +1,9 @@
|
|||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
from django.urls import path
|
from django.urls import path
|
||||||
|
from . import views as api_views
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
|
path('create_comment/<id>', api_views.add_comment),
|
||||||
|
path('link/<id>/<uuid>', api_views.like),
|
||||||
|
path('unlink/<id>/<uuid>', api_views.un_like),
|
||||||
|
path('check_like/<id>/<uuid>', api_views.check_like),
|
||||||
]
|
]
|
45
api/views.py
45
api/views.py
@ -1,3 +1,46 @@
|
|||||||
from django.shortcuts import render
|
from datetime import datetime
|
||||||
|
|
||||||
|
from django.shortcuts import render,HttpResponse
|
||||||
|
from django.http import JsonResponse,HttpResponseNotAllowed,HttpResponseRedirect,JsonResponse
|
||||||
|
from django.views.decorators.http import require_http_methods
|
||||||
|
|
||||||
|
from comment.models import comment
|
||||||
|
from home.models import *
|
||||||
# Create your views here.
|
# Create your views here.
|
||||||
|
|
||||||
|
@require_http_methods(["POST"])
|
||||||
|
def add_comment(request, id):
|
||||||
|
create_comment = comment.objects.create(comment_Content=request.POST.get("comment"),comment_User=request.POST.get("username"), comment_Time=datetime.now(), archives_Id=id, qq=request.POST.get("qqNum"))
|
||||||
|
create_comment.save()
|
||||||
|
return HttpResponseRedirect("/archives/"+id)
|
||||||
|
|
||||||
|
@require_http_methods(["POST"])
|
||||||
|
def check_like(request, id, uuid):
|
||||||
|
articles_likes = ArticlesLike.objects.filter(articles_id=id,uuid=uuid)
|
||||||
|
if articles_likes:
|
||||||
|
return JsonResponse({"liked":True})
|
||||||
|
else:
|
||||||
|
return JsonResponse({"liked":False})
|
||||||
|
|
||||||
|
@require_http_methods(["POST"])
|
||||||
|
def like(request, id, uuid):
|
||||||
|
article_stat = 0
|
||||||
|
if not ArticlesLike.objects.filter(articles_id=id, uuid=uuid):
|
||||||
|
ArticlesLike.objects.create(articles_id=id, uuid=uuid)
|
||||||
|
article = Articles.objects.get(id=id)
|
||||||
|
article.stat += 1
|
||||||
|
article_stat = article.stat
|
||||||
|
article.save()
|
||||||
|
return JsonResponse({"success":True,"new_count": article_stat})
|
||||||
|
|
||||||
|
@require_http_methods(["POST"])
|
||||||
|
def un_like(request,id, uuid):
|
||||||
|
article_stat = 0
|
||||||
|
if ArticlesLike.objects.filter(articles_id=id, uuid=uuid):
|
||||||
|
ArticlesLike.objects.filter(articles_id=id, uuid=uuid).delete()
|
||||||
|
article = Articles.objects.get(id=id)
|
||||||
|
article.stat -= 1
|
||||||
|
article_stat = article.stat
|
||||||
|
article.save()
|
||||||
|
|
||||||
|
return JsonResponse({"success":True,"new_count": article_stat})
|
||||||
|
0
comment/__init__.py
Normal file
0
comment/__init__.py
Normal file
BIN
comment/__pycache__/__init__.cpython-39.pyc
Normal file
BIN
comment/__pycache__/__init__.cpython-39.pyc
Normal file
Binary file not shown.
BIN
comment/__pycache__/admin.cpython-39.pyc
Normal file
BIN
comment/__pycache__/admin.cpython-39.pyc
Normal file
Binary file not shown.
BIN
comment/__pycache__/apps.cpython-39.pyc
Normal file
BIN
comment/__pycache__/apps.cpython-39.pyc
Normal file
Binary file not shown.
BIN
comment/__pycache__/models.cpython-39.pyc
Normal file
BIN
comment/__pycache__/models.cpython-39.pyc
Normal file
Binary file not shown.
7
comment/admin.py
Normal file
7
comment/admin.py
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
from django.contrib import admin
|
||||||
|
from comment.models import comment
|
||||||
|
# Register your models here.
|
||||||
|
|
||||||
|
@admin.register(comment)
|
||||||
|
class CommentAdmin(admin.ModelAdmin):
|
||||||
|
list_display = ["comment_Content","comment_User","comment_Time", "archives_Id"]
|
7
comment/apps.py
Normal file
7
comment/apps.py
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
from django.apps import AppConfig
|
||||||
|
|
||||||
|
|
||||||
|
class CommentConfig(AppConfig):
|
||||||
|
default_auto_field = 'django.db.models.BigAutoField'
|
||||||
|
name = 'comment'
|
||||||
|
verbose_name = "评论"
|
27
comment/migrations/0001_initial.py
Normal file
27
comment/migrations/0001_initial.py
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
# Generated by Django 4.2.23 on 2025-06-20 02:43
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
initial = True
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='comment',
|
||||||
|
fields=[
|
||||||
|
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('comment_Content', models.TextField(verbose_name='评论内容')),
|
||||||
|
('comment_User', models.CharField(max_length=100, verbose_name='评论者')),
|
||||||
|
('comment_Time', models.DateTimeField(verbose_name='评论时间')),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'verbose_name': '评论',
|
||||||
|
'verbose_name_plural': '评论管理',
|
||||||
|
},
|
||||||
|
),
|
||||||
|
]
|
18
comment/migrations/0002_comment_archives_id.py
Normal file
18
comment/migrations/0002_comment_archives_id.py
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
# Generated by Django 4.2.23 on 2025-06-20 03:01
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('comment', '0001_initial'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='comment',
|
||||||
|
name='archives_Id',
|
||||||
|
field=models.IntegerField(default=0, verbose_name='文章idßß'),
|
||||||
|
),
|
||||||
|
]
|
@ -0,0 +1,23 @@
|
|||||||
|
# Generated by Django 4.2.23 on 2025-06-20 04:53
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('comment', '0002_comment_archives_id'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='comment',
|
||||||
|
name='qq',
|
||||||
|
field=models.IntegerField(default=1000, verbose_name='评论者qq号'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='comment',
|
||||||
|
name='archives_Id',
|
||||||
|
field=models.IntegerField(default=0, verbose_name='文章id'),
|
||||||
|
),
|
||||||
|
]
|
0
comment/migrations/__init__.py
Normal file
0
comment/migrations/__init__.py
Normal file
BIN
comment/migrations/__pycache__/0001_initial.cpython-39.pyc
Normal file
BIN
comment/migrations/__pycache__/0001_initial.cpython-39.pyc
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
comment/migrations/__pycache__/__init__.cpython-39.pyc
Normal file
BIN
comment/migrations/__pycache__/__init__.cpython-39.pyc
Normal file
Binary file not shown.
16
comment/models.py
Normal file
16
comment/models.py
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
from django.db import models
|
||||||
|
|
||||||
|
# Create your models here.
|
||||||
|
class comment(models.Model):
|
||||||
|
comment_Content = models.TextField(verbose_name="评论内容")
|
||||||
|
comment_User = models.CharField(max_length=100,verbose_name="评论者")
|
||||||
|
comment_Time = models.DateTimeField(verbose_name="评论时间")
|
||||||
|
archives_Id = models.IntegerField(default=0, verbose_name="文章id")
|
||||||
|
qq = models.IntegerField(default=1000, verbose_name="评论者qq号")
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return self.comment_content
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
verbose_name = "评论"
|
||||||
|
verbose_name_plural = "评论管理"
|
3
comment/tests.py
Normal file
3
comment/tests.py
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
from django.test import TestCase
|
||||||
|
|
||||||
|
# Create your tests here.
|
4
comment/views.py
Normal file
4
comment/views.py
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
from django.shortcuts import render
|
||||||
|
|
||||||
|
# Create your views here.
|
||||||
|
|
BIN
db.sqlite3
BIN
db.sqlite3
Binary file not shown.
BIN
home/.DS_Store
vendored
Normal file
BIN
home/.DS_Store
vendored
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -1,10 +1,9 @@
|
|||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
|
|
||||||
from home.models import articles
|
from home.models import *
|
||||||
|
|
||||||
|
|
||||||
# Register your models here.
|
# Register your models here.
|
||||||
@admin.register(articles)
|
@admin.register(Articles)
|
||||||
class DepartmentAdmin(admin.ModelAdmin):
|
class DepartmentAdmin(admin.ModelAdmin):
|
||||||
# 要显示的字段
|
# 要显示的字段
|
||||||
list_display = ('title', 'abstract', 'created','stat')
|
list_display = ('title', 'abstract', 'created','stat')
|
||||||
@ -14,4 +13,13 @@ class DepartmentAdmin(admin.ModelAdmin):
|
|||||||
# 分页显示,一页的数量
|
# 分页显示,一页的数量
|
||||||
list_per_page = 10
|
list_per_page = 10
|
||||||
|
|
||||||
|
actions_on_top = True
|
||||||
|
|
||||||
|
@admin.register(ArticlesLike)
|
||||||
|
class ArticlesLikeAdmin(admin.ModelAdmin):
|
||||||
|
list_display = ('articles_id', 'uuid')
|
||||||
|
readonly_fields = ('articles_id', 'uuid')
|
||||||
|
# 分页显示,一页的数量
|
||||||
|
list_per_page = 10
|
||||||
|
|
||||||
actions_on_top = True
|
actions_on_top = True
|
@ -4,4 +4,4 @@ from django.apps import AppConfig
|
|||||||
class HomeConfig(AppConfig):
|
class HomeConfig(AppConfig):
|
||||||
default_auto_field = 'django.db.models.BigAutoField'
|
default_auto_field = 'django.db.models.BigAutoField'
|
||||||
name = 'home'
|
name = 'home'
|
||||||
verbose_name = "文章管理"
|
verbose_name = "文章"
|
||||||
|
25
home/migrations/0005_articleslike.py
Normal file
25
home/migrations/0005_articleslike.py
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
# Generated by Django 4.2.23 on 2025-06-22 18:14
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('home', '0004_articles_author'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='ArticlesLike',
|
||||||
|
fields=[
|
||||||
|
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('articles_id', models.IntegerField(default=0, verbose_name='文章id')),
|
||||||
|
('uuid', models.CharField(max_length=100, verbose_name='点赞用户标识')),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'verbose_name': '点赞',
|
||||||
|
'verbose_name_plural': '点赞管理',
|
||||||
|
},
|
||||||
|
),
|
||||||
|
]
|
BIN
home/migrations/__pycache__/0005_articleslike.cpython-39.pyc
Normal file
BIN
home/migrations/__pycache__/0005_articleslike.cpython-39.pyc
Normal file
Binary file not shown.
@ -2,7 +2,7 @@ from django.db import models
|
|||||||
|
|
||||||
# Create your models here.
|
# Create your models here.
|
||||||
|
|
||||||
class articles(models.Model):
|
class Articles(models.Model):
|
||||||
title = models.CharField(max_length=100,verbose_name="文章标题")
|
title = models.CharField(max_length=100,verbose_name="文章标题")
|
||||||
content = models.TextField(verbose_name="文章内容")
|
content = models.TextField(verbose_name="文章内容")
|
||||||
abstract = models.TextField(verbose_name="文章摘要")
|
abstract = models.TextField(verbose_name="文章摘要")
|
||||||
@ -20,3 +20,13 @@ class articles(models.Model):
|
|||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.title
|
return self.title
|
||||||
|
|
||||||
|
class ArticlesLike(models.Model):
|
||||||
|
articles_id = models.IntegerField(default=0,verbose_name="文章id")
|
||||||
|
uuid = models.CharField(max_length=100,verbose_name="点赞用户标识")
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
verbose_name = "点赞"
|
||||||
|
verbose_name_plural = "点赞管理"
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return self.uuid
|
12
home/static/aaa.js
Normal file
12
home/static/aaa.js
Normal file
File diff suppressed because one or more lines are too long
@ -41,6 +41,15 @@
|
|||||||
color: #bbb;
|
color: #bbb;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.article-like {
|
||||||
|
margin: 20px 0;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.link {
|
||||||
|
margin: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
/* 文章封面图 */
|
/* 文章封面图 */
|
||||||
.article-cover {
|
.article-cover {
|
||||||
margin: 30px 0;
|
margin: 30px 0;
|
||||||
@ -267,6 +276,12 @@
|
|||||||
|
|
||||||
.form-group {
|
.form-group {
|
||||||
margin-bottom: 20px;
|
margin-bottom: 20px;
|
||||||
|
|
||||||
|
}
|
||||||
|
.form-group {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
flex-wrap: wrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
.comment-form textarea {
|
.comment-form textarea {
|
||||||
@ -285,6 +300,16 @@
|
|||||||
outline: none;
|
outline: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.form-group > .comment-input {
|
||||||
|
width: 49.5%;
|
||||||
|
height: 30px;
|
||||||
|
border: 1px solid #eee;
|
||||||
|
border-radius: 6px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
padding: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
.submit-btn {
|
.submit-btn {
|
||||||
background-color: #3498db;
|
background-color: #3498db;
|
||||||
color: #fff;
|
color: #fff;
|
||||||
|
6
home/static/marked.min.js
vendored
Normal file
6
home/static/marked.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
@ -2,8 +2,12 @@
|
|||||||
{% load static %}
|
{% load static %}
|
||||||
|
|
||||||
{#文章详情#}
|
{#文章详情#}
|
||||||
|
{% block sitename %}
|
||||||
|
<title>{{ title }}-我的个人博客</title>
|
||||||
|
{% endblock %}
|
||||||
{% block stylesheethref %}
|
{% block stylesheethref %}
|
||||||
<link rel="stylesheet" href="{% static 'archives.css' %}">
|
<link rel="stylesheet" href="{% static 'archives.css' %}">
|
||||||
|
<script src="{% static 'marked.min.js' %}"></script>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="article-detail-container">
|
<div class="article-detail-container">
|
||||||
@ -13,24 +17,21 @@
|
|||||||
<h1 class="article-title">{{ title }}</h1>
|
<h1 class="article-title">{{ title }}</h1>
|
||||||
<div class="article-meta">
|
<div class="article-meta">
|
||||||
<span class="author">
|
<span class="author">
|
||||||
<i class="fas fa-user"></i>
|
作者 {{ author }}
|
||||||
{{ author }}
|
|
||||||
</span>
|
</span>
|
||||||
<span class="date">
|
<span class="date">
|
||||||
<i class="fas fa-calendar-alt"></i>
|
发布时间 {{ created|date:"Y-m-d" }}
|
||||||
{{ created|date:"Y-m-d" }}
|
|
||||||
</span>
|
</span>
|
||||||
<span class="category">
|
<span class="category">
|
||||||
<i class="fas fa-folder"></i>
|
{{ stat }} 点赞
|
||||||
{{ article.category }}
|
|
||||||
分类
|
|
||||||
</span>
|
</span>
|
||||||
<span class="views">
|
<span class="views">
|
||||||
<i class="fas fa-eye"></i>
|
|
||||||
{{ read }} 浏览
|
{{ read }} 浏览
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="article-cover">
|
<div class="article-cover">
|
||||||
<img src="http://iph.href.lu/200x150?text=封面功能,正在开发中" alt="{{ title }}封面图">
|
<img src="http://iph.href.lu/200x150?text=封面功能,正在开发中" alt="{{ title }}封面图">
|
||||||
</div>
|
</div>
|
||||||
@ -39,6 +40,145 @@
|
|||||||
<div class="article-content">
|
<div class="article-content">
|
||||||
{{ content|safe }}
|
{{ content|safe }}
|
||||||
</div>
|
</div>
|
||||||
|
<div class="dz" style="width: 42px;height: 42px;margin: 0 auto">
|
||||||
|
<div class="dz-icon" id="likeBtn" style="background-color: red;width: 42px;height: 42px;border-radius: 50%">
|
||||||
|
<div>
|
||||||
|
<svg viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg" width="38" height="38" style="margin: 2px">
|
||||||
|
<path id="link_svg" d="M512 928c-28.928 0-57.92-12.672-86.624-41.376L106.272 564C68.064 516.352 32 471.328 32 384c0-141.152 114.848-256 256-256 53.088 0 104 16.096 147.296 46.592 14.432 10.176 17.92 30.144 7.712 44.608-10.176 14.432-30.08 17.92-44.608 7.712C366.016 204.064 327.808 192 288 192c-105.888 0-192 86.112-192 192 0 61.408 20.288 90.112 59.168 138.688l315.584 318.816C486.72 857.472 499.616 863.808 512 864c12.704.192 24.928-6.176 41.376-22.624l316.672-319.904C896.064 493.28 928 445.696 928 384c0-105.888-86.112-192-192-192-48.064 0-94.08 17.856-129.536 50.272l-134.08 134.112c-12.512 12.512-32.736 12.512-45.248 0s-12.512-32.736 0-45.248L562.24 196c48.32-44.192 109.664-68 173.76-68 141.152 0 256 114.848 256 256 0 82.368-41.152 144.288-75.68 181.696l-317.568 320.8C569.952 915.328 540.96 928 512 928z" fill="#fff"></path>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
<p style="text-align: center" id="likeCount">{{ stat }}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</article>
|
</article>
|
||||||
|
<!-- 文章导航 -->
|
||||||
|
<nav class="article-nav">
|
||||||
|
{% if previous %}
|
||||||
|
<a href="/archives/{{ previous.id }}" class="prev">
|
||||||
|
<i class="fas fa-chevron-left"></i>
|
||||||
|
上一篇: {{ previous.title }}
|
||||||
|
</a>
|
||||||
|
{% endif %}
|
||||||
|
{% if next %}
|
||||||
|
<a href="/archives/{{ next.id }}" class="next">
|
||||||
|
下一篇: {{ next.title }}
|
||||||
|
<i class="fas fa-chevron-right"></i>
|
||||||
|
</a>
|
||||||
|
{% endif %}
|
||||||
|
</nav>
|
||||||
|
<!-- 评论区域 -->
|
||||||
|
<section class="comment-section">
|
||||||
|
<h3 class="section-title">评论</h3>
|
||||||
|
<div class="comment-form">
|
||||||
|
<form id="comment-form" action="/api/create_comment/{{ id }}" method="post">
|
||||||
|
{% csrf_token %}
|
||||||
|
<div class="form-group">
|
||||||
|
<input name="username" class="comment-input" placeholder="昵称">
|
||||||
|
<input name="qqNum" class="comment-input" placeholder="qq号(仅用于头像显示)">
|
||||||
|
<textarea name="comment" rows="5" placeholder="写下你的评论..."></textarea>
|
||||||
|
</div>
|
||||||
|
<button type="submit" class="submit-btn">提交评论</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
<div class="comment-list">
|
||||||
|
{% for comment in comments %}
|
||||||
|
<div class="comment-item">
|
||||||
|
<div class="comment-avatar">
|
||||||
|
<img src="http://q1.qlogo.cn/g?b=qq&nk={{ comment.qq }}&s=100" alt="用户头像">
|
||||||
|
</div>
|
||||||
|
<div class="comment-content">
|
||||||
|
<div class="comment-header">
|
||||||
|
<span class="comment-user">{{ comment.comment_User }}</span>
|
||||||
|
<span class="comment-time">{{ comment.comment_Time|timesince }}</span>
|
||||||
|
</div>
|
||||||
|
<p>{{ comment.comment_Content }}</p>
|
||||||
|
{% if comment.replies.all %}
|
||||||
|
<div class="comment-replies">
|
||||||
|
{% for reply in comment.replies.all %}
|
||||||
|
<div class="reply-item">
|
||||||
|
<div class="reply-avatar">
|
||||||
|
<img src="{{ reply.user.profile.avatar.url|default:'https://via.placeholder.com/40' }}" alt="用户头像">
|
||||||
|
</div>
|
||||||
|
<div class="reply-content">
|
||||||
|
<div class="reply-header">
|
||||||
|
<span class="reply-user">{{ reply.user.username }}</span>
|
||||||
|
<span class="reply-time">{{ reply.created_at|timesince }}前</span>
|
||||||
|
</div>
|
||||||
|
<p>{{ reply.content }}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% empty %}
|
||||||
|
<div class="no-comments">暂无评论,快来抢沙发吧~</div>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
</div>
|
</div>
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block script %}
|
||||||
|
const no_active_path = "M512 928c-28.928 0-57.92-12.672-86.624-41.376L106.272 564C68.064 516.352 32 471.328 32 384c0-141.152 114.848-256 256-256 53.088 0 104 16.096 147.296 46.592 14.432 10.176 17.92 30.144 7.712 44.608-10.176 14.432-30.08 17.92-44.608 7.712C366.016 204.064 327.808 192 288 192c-105.888 0-192 86.112-192 192 0 61.408 20.288 90.112 59.168 138.688l315.584 318.816C486.72 857.472 499.616 863.808 512 864c12.704.192 24.928-6.176 41.376-22.624l316.672-319.904C896.064 493.28 928 445.696 928 384c0-105.888-86.112-192-192-192-48.064 0-94.08 17.856-129.536 50.272l-134.08 134.112c-12.512 12.512-32.736 12.512-45.248 0s-12.512-32.736 0-45.248L562.24 196c48.32-44.192 109.664-68 173.76-68 141.152 0 256 114.848 256 256 0 82.368-41.152 144.288-75.68 181.696l-317.568 320.8C569.952 915.328 540.96 928 512 928z"
|
||||||
|
const active_path = "M736 128c-65.952 0-128.576 25.024-176.384 70.464-4.576 4.32-28.672 28.736-47.328 47.68L464.96 199.04C417.12 153.216 354.272 128 288 128 146.848 128 32 242.848 32 384c0 82.432 41.184 144.288 76.48 182.496l316.896 320.128C450.464 911.68 478.304 928 512 928s61.568-16.32 86.752-41.504l316.736-320 2.208-2.464C955.904 516.384 992 471.392 992 384c0-141.152-114.848-256-256-256z"
|
||||||
|
const path = document.getElementById('link_svg');
|
||||||
|
|
||||||
|
const fpPromise = import('{% static "aaa.js" %}').then(FingerprintJS => FingerprintJS.load());
|
||||||
|
fpPromise.then(fp => fp.get()).then(result => {localStorage.setItem("uuid",result.visitorId)});
|
||||||
|
|
||||||
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
|
const likeBtn = document.getElementsByClassName('dz')[0];
|
||||||
|
if (!likeBtn) return;
|
||||||
|
fetch(`/api/check_like/{{ id }}/${localStorage.getItem("uuid")}`,{
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'X-CSRFToken': '{{ csrf_token }}'
|
||||||
|
},
|
||||||
|
credentials: 'include'
|
||||||
|
}).then(response => response.json()).then(data => {
|
||||||
|
if (data.liked) {
|
||||||
|
path.setAttribute('d', active_path)
|
||||||
|
likeBtn.classList.add('active');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
const dz_icon = document.getElementsByClassName('dz-icon')[0];
|
||||||
|
dz_icon.addEventListener('click', function() {
|
||||||
|
if (likeBtn.classList.contains('active')) {
|
||||||
|
fetch(`/api/unlink/{{ id }}/${localStorage.getItem("uuid")}`,{
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'X-CSRFToken': '{{ csrf_token }}'
|
||||||
|
},
|
||||||
|
credentials: 'include'
|
||||||
|
})
|
||||||
|
.then(response => response.json())
|
||||||
|
.then(data => {
|
||||||
|
if (data.success) {
|
||||||
|
likeBtn.classList.remove('active');
|
||||||
|
path.setAttribute('d', no_active_path)
|
||||||
|
document.getElementById('likeCount').textContent = data.new_count;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
fetch(`/api/link/{{ id }}/${localStorage.getItem("uuid")}`, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'X-CSRFToken': '{{ csrf_token }}'
|
||||||
|
},
|
||||||
|
credentials: 'include'
|
||||||
|
})
|
||||||
|
.then(response => response.json())
|
||||||
|
.then(data => {
|
||||||
|
if (data.success) {
|
||||||
|
likeBtn.classList.add('active');
|
||||||
|
path.setAttribute('d', active_path)
|
||||||
|
document.getElementById('likeCount').textContent = data.new_count;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
@ -4,7 +4,9 @@
|
|||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<title>我的个人博客</title>
|
{% block sitename %}
|
||||||
|
<title>我的个人博客</title>
|
||||||
|
{% endblock %}
|
||||||
<link rel="stylesheet" href="{% static 'bottom.css' %}">
|
<link rel="stylesheet" href="{% static 'bottom.css' %}">
|
||||||
{% block stylesheethref %}
|
{% block stylesheethref %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
@ -92,40 +94,8 @@
|
|||||||
|
|
||||||
<!-- 简单的交互功能 -->
|
<!-- 简单的交互功能 -->
|
||||||
<script>
|
<script>
|
||||||
document.addEventListener('DOMContentLoaded', function() {
|
{% block script %}
|
||||||
// 搜索功能
|
{% endblock %}
|
||||||
const searchBtn = document.querySelector('.search-box button');
|
|
||||||
const searchInput = document.querySelector('.search-box input');
|
|
||||||
|
|
||||||
searchBtn.addEventListener('click', function() {
|
|
||||||
const query = searchInput.value.trim();
|
|
||||||
if (query) {
|
|
||||||
alert('搜索: ' + query);
|
|
||||||
// 实际应用中这里应该是跳转到搜索页面或执行搜索操作
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
searchInput.addEventListener('keypress', function(e) {
|
|
||||||
if (e.key === 'Enter') {
|
|
||||||
const query = searchInput.value.trim();
|
|
||||||
if (query) {
|
|
||||||
alert('搜索: ' + query);
|
|
||||||
// 实际应用中这里应该是跳转到搜索页面或执行搜索操作
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// 文章卡片交互
|
|
||||||
const articleCards = document.querySelectorAll('.article-card');
|
|
||||||
articleCards.forEach(card => {
|
|
||||||
card.addEventListener('click', function(e) {
|
|
||||||
if (!e.target.closest('.read-more')) {
|
|
||||||
const id = this.querySelector('.article-id').textContent;
|
|
||||||
window.open(`./archives/${id}`)
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
@ -34,4 +34,40 @@
|
|||||||
{% endfor %}
|
{% endfor %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
{% endblock %}
|
||||||
|
{% block script %}
|
||||||
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
|
// 搜索功能
|
||||||
|
const searchBtn = document.querySelector('.search-box button');
|
||||||
|
const searchInput = document.querySelector('.search-box input');
|
||||||
|
|
||||||
|
searchBtn.addEventListener('click', function() {
|
||||||
|
const query = searchInput.value.trim();
|
||||||
|
if (query) {
|
||||||
|
alert('搜索: ' + query);
|
||||||
|
// 实际应用中这里应该是跳转到搜索页面或执行搜索操作
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
searchInput.addEventListener('keypress', function(e) {
|
||||||
|
if (e.key === 'Enter') {
|
||||||
|
const query = searchInput.value.trim();
|
||||||
|
if (query) {
|
||||||
|
alert('搜索: ' + query);
|
||||||
|
// 实际应用中这里应该是跳转到搜索页面或执行搜索操作
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 文章卡片交互
|
||||||
|
const articleCards = document.querySelectorAll('.article-card');
|
||||||
|
articleCards.forEach(card => {
|
||||||
|
card.addEventListener('click', function(e) {
|
||||||
|
if (!e.target.closest('.read-more')) {
|
||||||
|
const id = this.querySelector('.article-id').textContent;
|
||||||
|
window.open(`/archives/${id}`)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
{% endblock %}
|
{% endblock %}
|
@ -1,11 +1,12 @@
|
|||||||
from django.shortcuts import render, HttpResponse
|
from django.shortcuts import render, HttpResponse
|
||||||
from . import models
|
from home.models import *
|
||||||
|
from comment.models import comment
|
||||||
# Create your views here.
|
# Create your views here.
|
||||||
|
|
||||||
def index(request):
|
def index(request):
|
||||||
artchle = models.articles.objects.all()
|
artchle = Articles.objects.all()
|
||||||
|
|
||||||
artchles = {"artchles": []}
|
artchles = {"artchles": []}
|
||||||
print(artchle)
|
|
||||||
for i in artchle:
|
for i in artchle:
|
||||||
a = {
|
a = {
|
||||||
"id":i.id,
|
"id":i.id,
|
||||||
@ -17,13 +18,19 @@ def index(request):
|
|||||||
}
|
}
|
||||||
artchles["artchles"].append(a)
|
artchles["artchles"].append(a)
|
||||||
return render(request, 'index.html', context=artchles)
|
return render(request, 'index.html', context=artchles)
|
||||||
# return HttpResponse("Hello, world. You're at the polls index.")
|
|
||||||
|
|
||||||
|
|
||||||
def archives(request, id):
|
def archives(request, id):
|
||||||
i = models.articles.objects.get(id=id)
|
i = Articles.objects.get(id=id)
|
||||||
i.read += 1
|
i.read += 1
|
||||||
i.save()
|
i.save()
|
||||||
|
previous_article = Articles.objects.filter(id__lt=id).order_by('-id').first()
|
||||||
|
previous_id = previous_article.id if previous_article else id
|
||||||
|
previous_id_title = previous_article.title if previous_article else "暂无上一篇"
|
||||||
|
next_article = Articles.objects.filter(id__gt=id).order_by('id').first()
|
||||||
|
next_id = next_article.id if next_article else id
|
||||||
|
next_id_title = next_article.title if next_article else "暂无下一篇"
|
||||||
|
comments = comment.objects.filter(archives_Id=id).order_by("-comment_Time").all()
|
||||||
a = {
|
a = {
|
||||||
"id": i.id,
|
"id": i.id,
|
||||||
"title": i.title,
|
"title": i.title,
|
||||||
@ -32,6 +39,22 @@ def archives(request, id):
|
|||||||
"stat": i.stat,
|
"stat": i.stat,
|
||||||
"read": i.read,
|
"read": i.read,
|
||||||
"content": i.content,
|
"content": i.content,
|
||||||
"author": i.author
|
"author": i.author,
|
||||||
|
"previous":{
|
||||||
|
"id": previous_id,
|
||||||
|
"title": previous_id_title,
|
||||||
|
},"next":{
|
||||||
|
"id": next_id,
|
||||||
|
"title": next_id_title,
|
||||||
|
},"comments": []
|
||||||
}
|
}
|
||||||
|
for c in comments:
|
||||||
|
com = {
|
||||||
|
"id": c.id,
|
||||||
|
"comment_Content": c.comment_Content,
|
||||||
|
"comment_User": c.comment_User,
|
||||||
|
"comment_Time": c.comment_Time,
|
||||||
|
"qq": c.qq,
|
||||||
|
}
|
||||||
|
a["comments"].append(com)
|
||||||
return render(request, 'archives.html', a)
|
return render(request, 'archives.html', a)
|
15
requirements.txt
Normal file
15
requirements.txt
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
asgiref==3.8.1
|
||||||
|
diff-match-patch==20241021
|
||||||
|
Django==4.2.23
|
||||||
|
django-import-export==4.3.7
|
||||||
|
django-mdeditor==0.1.20
|
||||||
|
django-simpleui==2025.5.262
|
||||||
|
docutils==0.21.2
|
||||||
|
importlib_metadata==8.7.0
|
||||||
|
Markdown==3.8
|
||||||
|
pillow==11.2.1
|
||||||
|
Pygments==2.19.1
|
||||||
|
sqlparse==0.5.3
|
||||||
|
tablib==3.8.0
|
||||||
|
typing_extensions==4.14.0
|
||||||
|
zipp==3.23.0
|
Loading…
x
Reference in New Issue
Block a user