parent
09b3357510
commit
784b1424df
Binary file not shown.
Binary file not shown.
@ -0,0 +1,76 @@
|
||||
# Generated by Django 5.2.4 on 2025-11-21 17:04
|
||||
|
||||
import django.db.models.deletion
|
||||
import django.utils.timezone
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('accounts', '0002_alter_bloguser_options_remove_bloguser_created_time_and_more'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='bloguser',
|
||||
name='avatar',
|
||||
field=models.ImageField(blank=True, null=True, upload_to='avatars/', verbose_name='avatar'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='bloguser',
|
||||
name='bio',
|
||||
field=models.TextField(blank=True, max_length=500, verbose_name='biography'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='bloguser',
|
||||
name='birth_date',
|
||||
field=models.DateField(blank=True, null=True, verbose_name='birth date'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='bloguser',
|
||||
name='location',
|
||||
field=models.CharField(blank=True, max_length=100, verbose_name='location'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='bloguser',
|
||||
name='website',
|
||||
field=models.URLField(blank=True, verbose_name='website'),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Notification',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('notification_type', models.CharField(choices=[('comment', 'comment notification'), ('like', 'like notification'), ('follow', 'follow notification'), ('system', 'system notification')], max_length=20, verbose_name='notification type')),
|
||||
('message', models.TextField(verbose_name='message')),
|
||||
('target_url', models.URLField(blank=True, verbose_name='target url')),
|
||||
('target_content', models.CharField(blank=True, max_length=200, verbose_name='target content')),
|
||||
('is_read', models.BooleanField(default=False, verbose_name='is read')),
|
||||
('created_time', models.DateTimeField(default=django.utils.timezone.now, verbose_name='creation time')),
|
||||
('recipient', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='notifications', to=settings.AUTH_USER_MODEL, verbose_name='recipient')),
|
||||
('sender', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='sent_notifications', to=settings.AUTH_USER_MODEL, verbose_name='sender')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'notification',
|
||||
'verbose_name_plural': 'notifications',
|
||||
'ordering': ['-created_time'],
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='UserFollowing',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('created_time', models.DateTimeField(default=django.utils.timezone.now, verbose_name='creation time')),
|
||||
('is_confirmed', models.BooleanField(default=True, verbose_name='is confirmed')),
|
||||
('following_user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='followers', to=settings.AUTH_USER_MODEL, verbose_name='followed user')),
|
||||
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='following', to=settings.AUTH_USER_MODEL, verbose_name='follower user')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'user following',
|
||||
'verbose_name_plural': 'user followings',
|
||||
'ordering': ['-created_time'],
|
||||
'unique_together': {('user', 'following_user')},
|
||||
},
|
||||
),
|
||||
]
|
||||
@ -0,0 +1,63 @@
|
||||
# Generated by Django 5.2.4 on 2025-11-20 14:58
|
||||
|
||||
import django.db.models.deletion
|
||||
import django.utils.timezone
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('blog', '0006_alter_blogsettings_options'),
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='article',
|
||||
name='dislikes_count',
|
||||
field=models.PositiveIntegerField(default=0, verbose_name='dislikes count'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='article',
|
||||
name='favorites_count',
|
||||
field=models.PositiveIntegerField(default=0, verbose_name='favorites count'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='article',
|
||||
name='likes_count',
|
||||
field=models.PositiveIntegerField(default=0, verbose_name='likes count'),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='ArticleFavorite',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('creation_time', models.DateTimeField(default=django.utils.timezone.now, verbose_name='creation time')),
|
||||
('article', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='favorites', to='blog.article', verbose_name='article')),
|
||||
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='favorite_articles', to=settings.AUTH_USER_MODEL, verbose_name='user')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'article favorite',
|
||||
'verbose_name_plural': 'article favorite',
|
||||
'ordering': ['-creation_time'],
|
||||
'unique_together': {('article', 'user')},
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='ArticleReaction',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('reaction', models.CharField(choices=[('like', 'Like'), ('dislike', 'Dislike')], max_length=7, verbose_name='reaction')),
|
||||
('creation_time', models.DateTimeField(default=django.utils.timezone.now, verbose_name='creation time')),
|
||||
('article', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='reactions', to='blog.article', verbose_name='article')),
|
||||
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='article_reactions', to=settings.AUTH_USER_MODEL, verbose_name='user')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'article reaction',
|
||||
'verbose_name_plural': 'article reaction',
|
||||
'ordering': ['-creation_time'],
|
||||
'unique_together': {('article', 'user')},
|
||||
},
|
||||
),
|
||||
]
|
||||
@ -0,0 +1,47 @@
|
||||
{% extends 'share_layout/base_account.html' %}
|
||||
{% block title %}编辑个人资料 - DjangoBlog{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-md-8 offset-md-2">
|
||||
<div class="card">
|
||||
<div class="card-header bg-primary text-white">
|
||||
<h3 class="mb-0">个人信息</h3>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="row mb-4">
|
||||
<div class="col-md-4 text-center">
|
||||
<img src="{% static 'img/default_avatar.png' %}"
|
||||
alt="默认头像" class="img-thumbnail rounded-circle"
|
||||
style="width: 150px; height: 150px; object-fit: cover;">
|
||||
</div>
|
||||
<div class="col-md-8">
|
||||
<div class="form-group">
|
||||
<label for="id_username">用户名</label>
|
||||
<input type="text" id="id_username" class="form-control" value="{{ user.username }}" disabled>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="id_email">邮箱地址</label>
|
||||
<input type="email" id="id_email" class="form-control"
|
||||
value="{{ user.email }}" disabled>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="alert alert-info">
|
||||
<p>当前仅保留基本用户信息展示功能。</p>
|
||||
</div>
|
||||
|
||||
<div class="d-flex justify-content-center mt-4">
|
||||
<a href="/" class="btn btn-secondary">
|
||||
<i class="fas fa-arrow-left mr-1"></i> 返回首页
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@ -0,0 +1,44 @@
|
||||
{% extends 'share_layout/base.html' %}
|
||||
{% load blog_tags %}
|
||||
{% block content %}
|
||||
<div id="primary" class="site-content">
|
||||
<div id="content" role="main">
|
||||
<article class="post">
|
||||
<header class="entry-header">
|
||||
<h1 class="entry-title">我的收藏</h1>
|
||||
</header>
|
||||
<div class="entry-content">
|
||||
{% if favorites %}
|
||||
<ul class="favorite-list">
|
||||
{% for favorite in favorites %}
|
||||
<li>
|
||||
<a href="{{ favorite.article.get_absolute_url }}">
|
||||
{{ favorite.article.title }}
|
||||
</a>
|
||||
<small>收藏时间:{{ favorite.creation_time|date:"Y-m-d H:i" }}</small>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% if is_paginated %}
|
||||
<div class="navigation">
|
||||
{% if page_obj.has_previous %}
|
||||
<a href="?page={{ page_obj.previous_page_number }}">上一页</a>
|
||||
{% endif %}
|
||||
<span>第 {{ page_obj.number }} / {{ page_obj.paginator.num_pages }} 页</span>
|
||||
{% if page_obj.has_next %}
|
||||
<a href="?page={{ page_obj.next_page_number }}">下一页</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endif %}
|
||||
{% else %}
|
||||
<p>您还没有收藏任何文章。</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
</article>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
{% block sidebar %}
|
||||
{% load_sidebar user "p" %}
|
||||
{% endblock %}
|
||||
|
||||
@ -0,0 +1,173 @@
|
||||
{% extends 'share_layout/base_account.html' %}
|
||||
{% load static %}
|
||||
{% block title %}{{ user_profile.username }} - 个人信息中心{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="container my-5">
|
||||
<!-- 头部信息卡片 -->
|
||||
<div class="card shadow-lg rounded-lg overflow-hidden mb-5">
|
||||
<div class="card-header bg-primary text-white p-4">
|
||||
<h3 class="mb-0">个人资料</h3>
|
||||
</div>
|
||||
|
||||
<div class="card-body">
|
||||
<div class="row">
|
||||
<!-- 左侧信息区域 - 头像和操作按钮 -->
|
||||
<div class="col-md-4 mb-4">
|
||||
<div class="text-center p-3 bg-light rounded-lg">
|
||||
<!-- 头像 -->
|
||||
<div class="mb-3">
|
||||
{% if user_profile.profile.avatar %}
|
||||
<img src="{{ user_profile.profile.avatar.url }}" alt="{{ user_profile.username }} 的头像"
|
||||
class="rounded-circle border-4 border-white shadow" style="width: 160px; height: 160px; object-fit: cover;">
|
||||
{% else %}
|
||||
<img src="{% static 'img/default_avatar.png' %}" alt="默认头像"
|
||||
class="rounded-circle border-4 border-white shadow" style="width: 160px; height: 160px; object-fit: cover;">
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<!-- 用户名 -->
|
||||
<h4 class="font-weight-bold">{{ user_profile.username }}</h4>
|
||||
{% if user_profile.nickname %}
|
||||
<p class="text-muted">{{ user_profile.nickname }}</p>
|
||||
{% endif %}
|
||||
|
||||
<!-- 操作按钮 -->
|
||||
<div class="mt-4">
|
||||
{% if is_own_profile %}
|
||||
<a href="{% url 'accounts:edit_profile' %}" class="btn btn-primary btn-block">
|
||||
<i class="fas fa-edit mr-2"></i> 编辑资料
|
||||
</a>
|
||||
{% else %}
|
||||
{% if user.is_authenticated %}
|
||||
{% if user_profile.email %}
|
||||
<a href="mailto:{{ user_profile.email }}" class="btn btn-outline-primary btn-block">
|
||||
<i class="fas fa-envelope mr-2"></i> 发消息
|
||||
</a>
|
||||
{% endif %}
|
||||
{% else %}
|
||||
<a href="{% url 'accounts:login' %}" class="btn btn-primary btn-block">
|
||||
<i class="fas fa-sign-in-alt mr-2"></i> 登录
|
||||
</a>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 中间区域 - 详细信息 -->
|
||||
<div class="col-md-4">
|
||||
<div class="bg-white p-4 rounded-lg shadow-sm mb-4">
|
||||
<h4 class="border-bottom pb-2 mb-3">关于我</h4>
|
||||
<p class="text-muted">
|
||||
{% if user_profile.profile.bio %}
|
||||
{{ user_profile.profile.bio }}
|
||||
{% else %}
|
||||
这个人很懒,还没有填写个人介绍...
|
||||
{% endif %}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="bg-white p-4 rounded-lg shadow-sm">
|
||||
<h4 class="border-bottom pb-2 mb-3">基本信息</h4>
|
||||
<ul class="list-unstyled">
|
||||
<li class="mb-3 d-flex items-center">
|
||||
<i class="fas fa-envelope text-primary mr-3"></i>
|
||||
<span>邮箱: {{ user_profile.email }}</span>
|
||||
</li>
|
||||
{% if user_profile.profile.location %}
|
||||
<li class="mb-3 d-flex items-center">
|
||||
<i class="fas fa-map-marker-alt text-primary mr-3"></i>
|
||||
<span>所在地: {{ user_profile.profile.location }}</span>
|
||||
</li>
|
||||
{% endif %}
|
||||
{% if user_profile.profile.website %}
|
||||
<li class="mb-3 d-flex items-center">
|
||||
<i class="fas fa-globe text-primary mr-3"></i>
|
||||
<span>个人网站: <a href="{{ user_profile.profile.website }}" target="_blank" class="text-primary hover:underline">{{ user_profile.profile.website }}</a></span>
|
||||
</li>
|
||||
{% endif %}
|
||||
<li class="mb-3 d-flex items-center">
|
||||
<i class="fas fa-calendar-alt text-primary mr-3"></i>
|
||||
<span>注册时间: {{ user_profile.date_joined|date:"Y年m月d日" }}</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 右侧区域 - 统计数据 -->
|
||||
<div class="col-md-4">
|
||||
<div class="bg-white p-4 rounded-lg shadow-sm">
|
||||
<h4 class="border-bottom pb-2 mb-4 text-center">统计数据</h4>
|
||||
|
||||
<div class="row text-center mb-4">
|
||||
<div class="col-6 mb-4">
|
||||
<a href="{% url 'accounts:user_articles' username=user_profile.username %}" class="text-decoration-none text-dark hover:text-primary">
|
||||
<div class="stat-number font-bold text-3xl">{{ article_count|default:0 }}</div>
|
||||
<div class="stat-label text-sm text-muted">文章</div>
|
||||
</a>
|
||||
</div>
|
||||
<div class="col-6 mb-4">
|
||||
<a href="{% url 'accounts:favorites' %}" class="text-decoration-none text-dark hover:text-primary">
|
||||
<div class="stat-number font-bold text-3xl">{{ favorite_count }}</div>
|
||||
<div class="stat-label text-sm text-muted">收藏</div>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row text-center">
|
||||
<div class="col-12">
|
||||
<a href="{% url 'accounts:user_likes' username=user_profile.username %}" class="text-decoration-none text-dark hover:text-primary">
|
||||
<div class="stat-number font-bold text-3xl">{{ likes_given_count|default:0 }}</div>
|
||||
<div class="stat-label text-sm text-muted">点赞</div>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.stat-item a {
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
transition: color 0.3s ease;
|
||||
}
|
||||
|
||||
.stat-item a:hover {
|
||||
color: #007bff;
|
||||
}
|
||||
|
||||
.stat-number {
|
||||
font-size: 1.5rem;
|
||||
font-weight: bold;
|
||||
margin-bottom: 0.25rem;
|
||||
}
|
||||
|
||||
.stat-label {
|
||||
font-size: 0.875rem;
|
||||
color: #6c757d;
|
||||
}
|
||||
|
||||
.btn-block {
|
||||
display: block;
|
||||
width: 100%;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
/* 响应式设计 */
|
||||
@media (max-width: 768px) {
|
||||
.col-md-4 {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
img {
|
||||
width: 120px !important;
|
||||
height: 120px !important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
{% endblock %}
|
||||
@ -0,0 +1,155 @@
|
||||
{% extends 'share_layout/base_account.html' %}
|
||||
{% load static %}
|
||||
{% block title %}{{ user_profile.username }} - 发布的文章{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<div class="card mb-4 shadow-sm">
|
||||
<div class="card-header bg-primary text-white">
|
||||
<h3 class="mb-0">{{ user_profile.username }} 的文章 ({{ article_count }})</h3>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<ul class="nav nav-tabs">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link {% if active_tab == 'articles' %}active{% endif %}"
|
||||
href="{% url 'accounts:user_articles' username=user_profile.username %}">
|
||||
<i class="fas fa-list-alt"></i> 文章 ({{ article_count }})
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link {% if active_tab == 'favorites' %}active{% endif %}"
|
||||
href="{% url 'accounts:user_favorites' username=user_profile.username %}">
|
||||
<i class="fas fa-star"></i> 收藏 ({{ favorite_count }})
|
||||
</a>
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
|
||||
<div class="tab-content mt-4">
|
||||
<div class="tab-pane fade show active" id="articles">
|
||||
{% if article_list %}
|
||||
<div class="row">
|
||||
{% for article in article_list %}
|
||||
<div class="col-md-12 mb-4">
|
||||
<div class="card h-100 border-l-4 {% if article.is_top %}border-l-danger{% else %}border-l-primary{% endif %}">
|
||||
<div class="card-body">
|
||||
<div class="d-flex justify-content-between align-items-start mb-2">
|
||||
<a href="{{ article.get_absolute_url }}" class="text-dark font-weight-bold text-decoration-none hover:text-primary">
|
||||
<h4 class="card-title mb-1">
|
||||
{% if article.is_top %}
|
||||
<i class="fas fa-thumbtack text-danger mr-2"></i>
|
||||
{% endif %}
|
||||
{{ article.title }}
|
||||
</h4>
|
||||
</a>
|
||||
{% if user == user_profile %}
|
||||
<div class="dropdown">
|
||||
<button class="btn btn-sm btn-outline-secondary dropdown-toggle">
|
||||
<i class="fas fa-ellipsis-h"></i>
|
||||
</button>
|
||||
<div class="dropdown-menu dropdown-menu-right">
|
||||
<a href="{% url 'blog:article_update' article.id %}" class="dropdown-item">
|
||||
<i class="fas fa-edit mr-2"></i>编辑
|
||||
</a>
|
||||
<a href="{% url 'blog:article_delete' article.id %}" class="dropdown-item text-danger">
|
||||
<i class="fas fa-trash-alt mr-2"></i>删除
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
<p class="card-text text-muted mb-3">
|
||||
{{ article.body|striptags|truncatechars:150 }}
|
||||
</p>
|
||||
<div class="d-flex flex-wrap justify-content-between align-items-center text-sm text-muted">
|
||||
<div class="d-flex items-center mb-2 mb-md-0">
|
||||
<div class="text-muted">
|
||||
{% for tag in article.tags.all %}
|
||||
<span class="badge badge-secondary mr-1">{{ tag.name }}</span>
|
||||
{% endfor %}
|
||||
</div>
|
||||
<small class="ml-3">{{ article.pub_time|date:"Y-m-d H:i" }}</small>
|
||||
</div>
|
||||
<div class="d-flex items-center">
|
||||
<i class="far fa-eye mr-1"></i>
|
||||
<span class="ml-1 mr-3">{{ article.views|default:0 }}</span>
|
||||
<i class="far fa-comment mr-1"></i>
|
||||
<span class="ml-1 mr-3">{{ article.comment_set.count }}</span>
|
||||
<i class="far fa-thumbs-up mr-1"></i>
|
||||
<span class="ml-1">{{ article.likes.count }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
||||
<!-- 分页控件 -->
|
||||
<nav aria-label="Page navigation" class="mt-4">
|
||||
<ul class="pagination justify-content-center">
|
||||
{% if page_obj.has_previous %}
|
||||
<li class="page-item">
|
||||
<a class="page-link" href="?page={{ page_obj.previous_page_number }}">上一页</a>
|
||||
</li>
|
||||
{% else %}
|
||||
<li class="page-item disabled">
|
||||
<a class="page-link" href="#">上一页</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
|
||||
{% for page_num in page_obj.paginator.page_range %}
|
||||
{% if page_num == page_obj.number %}
|
||||
<li class="page-item active">
|
||||
<a class="page-link" href="?page={{ page_num }}">{{ page_num }}</a>
|
||||
</li>
|
||||
{% elif page_num > page_obj.number|add:'-3' and page_num < page_obj.number|add:'3' %}
|
||||
<li class="page-item">
|
||||
<a class="page-link" href="?page={{ page_num }}">{{ page_num }}</a>
|
||||
</li>
|
||||
{% elif page_num == 1 or page_num == page_obj.paginator.num_pages %}
|
||||
<li class="page-item">
|
||||
<a class="page-link" href="?page={{ page_num }}">{{ page_num }}</a>
|
||||
</li>
|
||||
{% elif page_num == page_obj.number|add:'-4' or page_num == page_obj.number|add:'4' %}
|
||||
<li class="page-item disabled">
|
||||
<a class="page-link" href="#">...</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
|
||||
{% if page_obj.has_next %}
|
||||
<li class="page-item">
|
||||
<a class="page-link" href="?page={{ page_obj.next_page_number }}">下一页</a>
|
||||
</li>
|
||||
{% else %}
|
||||
<li class="page-item disabled">
|
||||
<a class="page-link" href="#">下一页</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
</nav>
|
||||
{% else %}
|
||||
<div class="text-center py-8 text-muted">
|
||||
<i class="fas fa-file-alt fa-5x mb-3"></i>
|
||||
<h4 class="mb-2">暂无文章</h4>
|
||||
{% if user == user_profile %}
|
||||
<a href="{% url 'blog:article_create' %}" class="btn btn-primary mt-2">
|
||||
<i class="fas fa-edit mr-1"></i>开始写文章
|
||||
</a>
|
||||
{% else %}
|
||||
<p>等待精彩内容更新...</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@ -0,0 +1,106 @@
|
||||
{% extends 'share_layout/base_account.html' %}
|
||||
{% block title %}{{ user_profile.username }} - 收藏的文章{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<!-- 内容区域 -->
|
||||
<div class="col-md-12">
|
||||
<!-- 用户资料头部卡片 -->
|
||||
<div class="card mb-4">
|
||||
<div class="card-header bg-primary text-white">
|
||||
<h3 class="mb-0">{{ user_profile.username }} 收藏的文章列表</h3>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<!-- 导航标签页 -->
|
||||
<ul class="nav nav-tabs">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link {% if active_tab == 'articles' %}active{% endif %}"
|
||||
href="{% url 'accounts:user_articles' username=user_profile.username %}">
|
||||
<i class="fas fa-list-alt"></i> 文章
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link {% if active_tab == 'favorites' %}active{% endif %}"
|
||||
href="{% url 'accounts:user_favorites' username=user_profile.username %}">
|
||||
<i class="fas fa-star"></i> 收藏
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 收藏文章列表 -->
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
{% if article_list %}
|
||||
<div class="list-group">
|
||||
{% for article in article_list %}
|
||||
<a href="{{ article.get_absolute_url }}" class="list-group-item list-group-item-action flex-column align-items-start mb-3">
|
||||
<div class="d-flex w-100 justify-content-between">
|
||||
<h5 class="mb-1 text-primary">{{ article.title }}</h5>
|
||||
<small class="text-muted">收藏于 {{ article.favorites.first.creation_time|date:"Y-m-d H:i" }}</small>
|
||||
</div>
|
||||
<p class="mb-1 text-muted">{{ article.body|striptags|truncatechars:150 }}</p>
|
||||
<div class="d-flex w-100 justify-content-between align-items-center">
|
||||
<small class="text-muted">
|
||||
作者: {{ article.author.username }} |
|
||||
发布于: {{ article.pub_time|date:"Y-m-d" }}
|
||||
</small>
|
||||
<div class="text-muted">
|
||||
<i class="far fa-eye"></i> {{ article.views }}
|
||||
<i class="far fa-comment ml-2"></i> {{ article.comment_set.count }}
|
||||
<i class="far fa-thumbs-up ml-2"></i> {{ article.likes.count }}
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
||||
<!-- 分页控件 -->
|
||||
<nav aria-label="Page navigation" class="mt-4">
|
||||
<ul class="pagination justify-content-center">
|
||||
{% if page_obj.has_previous %}
|
||||
<li class="page-item">
|
||||
<a class="page-link" href="?page={{ page_obj.previous_page_number }}">上一页</a>
|
||||
</li>
|
||||
{% else %}
|
||||
<li class="page-item disabled">
|
||||
<a class="page-link" href="#">上一页</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
|
||||
{% for page_num in page_obj.paginator.page_range %}
|
||||
{% if page_num == page_obj.number %}
|
||||
<li class="page-item active">
|
||||
<a class="page-link" href="?page={{ page_num }}">{{ page_num }}</a>
|
||||
</li>
|
||||
{% else %}
|
||||
<li class="page-item">
|
||||
<a class="page-link" href="?page={{ page_num }}">{{ page_num }}</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
|
||||
{% if page_obj.has_next %}
|
||||
<li class="page-item">
|
||||
<a class="page-link" href="?page={{ page_obj.next_page_number }}">下一页</a>
|
||||
</li>
|
||||
{% else %}
|
||||
<li class="page-item disabled">
|
||||
<a class="page-link" href="#">下一页</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
</nav>
|
||||
{% else %}
|
||||
<div class="alert alert-info text-center">
|
||||
<i class="fas fa-info-circle"></i> 暂无收藏的文章
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
Loading…
Reference in new issue