Compare commits
No commits in common. 'master' and 'main' have entirely different histories.
@ -1,5 +1,3 @@
|
|||||||
__pycache__/
|
__pycache__/
|
||||||
*.pyc
|
*.pyc
|
||||||
db.sqlite3
|
*.sqlite3
|
||||||
*.log
|
|
||||||
media/
|
|
||||||
|
|||||||
@ -1,21 +0,0 @@
|
|||||||
from django.contrib import admin
|
|
||||||
from django.contrib.auth.admin import UserAdmin
|
|
||||||
from .models import CustomUser
|
|
||||||
|
|
||||||
# HJJ: 使用装饰器注册CustomUser模型到Django管理后台
|
|
||||||
@admin.register(CustomUser)
|
|
||||||
class CustomUserAdmin(UserAdmin):
|
|
||||||
# HJJ: 指定管理的模型为CustomUser
|
|
||||||
model = CustomUser
|
|
||||||
# HJJ: 定义管理后台列表页显示的字段
|
|
||||||
list_display = ['username', 'email', 'is_staff', 'date_joined']
|
|
||||||
# HJJ: 定义管理后台可用的过滤器
|
|
||||||
list_filter = ['is_staff', 'is_active', 'date_joined']
|
|
||||||
# HJJ: 在原有字段集基础上添加扩展信息字段集
|
|
||||||
fieldsets = UserAdmin.fieldsets + (
|
|
||||||
('扩展信息', {'fields': ('bio', 'avatar', 'location', 'website')}),
|
|
||||||
)
|
|
||||||
# HJJ: 在添加用户表单中添加扩展信息字段集
|
|
||||||
add_fieldsets = UserAdmin.add_fieldsets + (
|
|
||||||
('扩展信息', {'fields': ('bio', 'avatar', 'location', 'website')}),
|
|
||||||
)
|
|
||||||
@ -1,6 +0,0 @@
|
|||||||
from django.apps import AppConfig
|
|
||||||
|
|
||||||
|
|
||||||
class AccountsConfig(AppConfig):
|
|
||||||
default_auto_field = 'django.db.models.BigAutoField'
|
|
||||||
name = 'accounts'
|
|
||||||
@ -1,47 +0,0 @@
|
|||||||
# Generated by Django 5.2.7 on 2025-11-01 09:09
|
|
||||||
|
|
||||||
import django.contrib.auth.models
|
|
||||||
import django.contrib.auth.validators
|
|
||||||
import django.utils.timezone
|
|
||||||
from django.db import migrations, models
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
initial = True
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
('auth', '0012_alter_user_first_name_max_length'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.CreateModel(
|
|
||||||
name='CustomUser',
|
|
||||||
fields=[
|
|
||||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
|
||||||
('password', models.CharField(max_length=128, verbose_name='password')),
|
|
||||||
('last_login', models.DateTimeField(blank=True, null=True, verbose_name='last login')),
|
|
||||||
('is_superuser', models.BooleanField(default=False, help_text='Designates that this user has all permissions without explicitly assigning them.', verbose_name='superuser status')),
|
|
||||||
('username', models.CharField(error_messages={'unique': 'A user with that username already exists.'}, help_text='Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.', max_length=150, unique=True, validators=[django.contrib.auth.validators.UnicodeUsernameValidator()], verbose_name='username')),
|
|
||||||
('first_name', models.CharField(blank=True, max_length=150, verbose_name='first name')),
|
|
||||||
('last_name', models.CharField(blank=True, max_length=150, verbose_name='last name')),
|
|
||||||
('email', models.EmailField(blank=True, max_length=254, verbose_name='email address')),
|
|
||||||
('is_staff', models.BooleanField(default=False, help_text='Designates whether the user can log into this admin site.', verbose_name='staff status')),
|
|
||||||
('is_active', models.BooleanField(default=True, help_text='Designates whether this user should be treated as active. Unselect this instead of deleting accounts.', verbose_name='active')),
|
|
||||||
('date_joined', models.DateTimeField(default=django.utils.timezone.now, verbose_name='date joined')),
|
|
||||||
('bio', models.TextField(blank=True, max_length=500, verbose_name='个人简介')),
|
|
||||||
('avatar', models.ImageField(blank=True, null=True, upload_to='avatars/%Y/%m/', verbose_name='头像')),
|
|
||||||
('location', models.CharField(blank=True, max_length=100, verbose_name='所在地')),
|
|
||||||
('website', models.URLField(blank=True, verbose_name='个人网站')),
|
|
||||||
('groups', models.ManyToManyField(blank=True, help_text='The groups this user belongs to. A user will get all permissions granted to each of their groups.', related_name='user_set', related_query_name='user', to='auth.group', verbose_name='groups')),
|
|
||||||
('user_permissions', models.ManyToManyField(blank=True, help_text='Specific permissions for this user.', related_name='user_set', related_query_name='user', to='auth.permission', verbose_name='user permissions')),
|
|
||||||
],
|
|
||||||
options={
|
|
||||||
'verbose_name': '用户',
|
|
||||||
'verbose_name_plural': '用户',
|
|
||||||
},
|
|
||||||
managers=[
|
|
||||||
('objects', django.contrib.auth.models.UserManager()),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
]
|
|
||||||
@ -1,326 +0,0 @@
|
|||||||
{% extends 'base.html' %}
|
|
||||||
|
|
||||||
{% block content %}
|
|
||||||
<div class="auth-page">
|
|
||||||
<div class="auth-container">
|
|
||||||
<div class="auth-card">
|
|
||||||
<!-- 非遗主题装饰 -->
|
|
||||||
<div class="auth-header">
|
|
||||||
<div class="auth-icon">
|
|
||||||
<span>🎭</span>
|
|
||||||
</div>
|
|
||||||
<h2>登录金陵非遗</h2>
|
|
||||||
<p>探索南京非物质文化遗产的魅力</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- 登录表单 -->
|
|
||||||
<form method="post" class="auth-form">
|
|
||||||
{% csrf_token %}
|
|
||||||
|
|
||||||
{% if form.errors %}
|
|
||||||
<div class="alert alert-error">
|
|
||||||
<span>⚠️</span>
|
|
||||||
用户名或密码错误,请重试
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
{% if messages %}
|
|
||||||
<div class="messages">
|
|
||||||
{% for message in messages %}
|
|
||||||
<div class="alert alert-{{ message.tags }}">
|
|
||||||
<span class="alert-icon">
|
|
||||||
{% if message.tags == 'success' %}✅{% else %}⚠️{% endif %}
|
|
||||||
</span>
|
|
||||||
<span class="alert-content">{{ message }}</span>
|
|
||||||
</div>
|
|
||||||
{% endfor %}
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
<div class="form-group">
|
|
||||||
<label for="id_username">用户名</label>
|
|
||||||
<div class="input-with-icon">
|
|
||||||
<span class="input-icon">👤</span>
|
|
||||||
<input type="text" name="username" id="id_username"
|
|
||||||
placeholder="请输入用户名" required
|
|
||||||
value="{{ form.username.value|default:'' }}">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="form-group">
|
|
||||||
<label for="id_password">密码</label>
|
|
||||||
<div class="input-with-icon">
|
|
||||||
<span class="input-icon">🔒</span>
|
|
||||||
<input type="password" name="password" id="id_password"
|
|
||||||
placeholder="请输入密码" required>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<button type="submit" class="auth-btn primary-btn">
|
|
||||||
<span>登录</span>
|
|
||||||
<span class="btn-icon">→</span>
|
|
||||||
</button>
|
|
||||||
</form>
|
|
||||||
|
|
||||||
<!-- 其他选项 -->
|
|
||||||
<div class="auth-footer">
|
|
||||||
<div class="divider">
|
|
||||||
<span>或</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="auth-links">
|
|
||||||
<p>还没有账号?<a href="{% url 'register' %}" class="link">立即注册</a></p>
|
|
||||||
<p><a href="{% url 'password_reset_request' %}" class="link">忘记密码?</a></p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<style>
|
|
||||||
.auth-page {
|
|
||||||
min-height: 80vh;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
padding: 40px 20px;
|
|
||||||
background: linear-gradient(135deg, #faf3e8 0%, #fffaf0 100%);
|
|
||||||
}
|
|
||||||
|
|
||||||
.auth-container {
|
|
||||||
width: 100%;
|
|
||||||
max-width: 420px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.auth-card {
|
|
||||||
background: var(--bg-white);
|
|
||||||
padding: 40px 35px;
|
|
||||||
border-radius: 20px;
|
|
||||||
box-shadow: 0 15px 35px rgba(139, 69, 19, 0.15);
|
|
||||||
border: 1px solid var(--border-color);
|
|
||||||
position: relative;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
.auth-card::before {
|
|
||||||
content: "";
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
height: 4px;
|
|
||||||
background: linear-gradient(90deg, var(--nj-red), var(--nj-gold), var(--nj-brown));
|
|
||||||
}
|
|
||||||
|
|
||||||
.auth-header {
|
|
||||||
text-align: center;
|
|
||||||
margin-bottom: 35px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.auth-icon {
|
|
||||||
width: 80px;
|
|
||||||
height: 80px;
|
|
||||||
margin: 0 auto 20px;
|
|
||||||
background: linear-gradient(135deg, var(--nj-gold), var(--nj-brown));
|
|
||||||
border-radius: 50%;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
font-size: 2em;
|
|
||||||
box-shadow: 0 8px 20px rgba(212, 175, 55, 0.3);
|
|
||||||
}
|
|
||||||
|
|
||||||
.auth-header h2 {
|
|
||||||
color: var(--text-dark);
|
|
||||||
font-size: 1.8em;
|
|
||||||
margin-bottom: 8px;
|
|
||||||
font-weight: 600;
|
|
||||||
}
|
|
||||||
|
|
||||||
.auth-header p {
|
|
||||||
color: var(--text-light);
|
|
||||||
font-size: 1em;
|
|
||||||
opacity: 0.8;
|
|
||||||
}
|
|
||||||
|
|
||||||
.auth-form {
|
|
||||||
margin-bottom: 25px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.form-group {
|
|
||||||
margin-bottom: 25px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.form-group label {
|
|
||||||
display: block;
|
|
||||||
margin-bottom: 8px;
|
|
||||||
color: var(--text-dark);
|
|
||||||
font-weight: 500;
|
|
||||||
font-size: 14px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.input-with-icon {
|
|
||||||
position: relative;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.input-icon {
|
|
||||||
position: absolute;
|
|
||||||
left: 15px;
|
|
||||||
color: var(--text-light);
|
|
||||||
font-size: 16px;
|
|
||||||
z-index: 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
.input-with-icon input {
|
|
||||||
width: 100%;
|
|
||||||
padding: 15px 15px 15px 45px;
|
|
||||||
border: 2px solid var(--border-color);
|
|
||||||
border-radius: 12px;
|
|
||||||
font-size: 15px;
|
|
||||||
transition: all 0.3s ease;
|
|
||||||
background: var(--bg-white);
|
|
||||||
}
|
|
||||||
|
|
||||||
.input-with-icon input:focus {
|
|
||||||
outline: none;
|
|
||||||
border-color: var(--nj-gold);
|
|
||||||
box-shadow: 0 0 0 3px rgba(212, 175, 55, 0.1);
|
|
||||||
transform: translateY(-1px);
|
|
||||||
}
|
|
||||||
|
|
||||||
.alert {
|
|
||||||
padding: 12px 16px;
|
|
||||||
border-radius: 10px;
|
|
||||||
margin-bottom: 20px;
|
|
||||||
font-size: 14px;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.alert-error {
|
|
||||||
background: rgba(198, 47, 47, 0.1);
|
|
||||||
color: var(--nj-red);
|
|
||||||
border: 1px solid rgba(198, 47, 47, 0.2);
|
|
||||||
}
|
|
||||||
|
|
||||||
.alert-success {
|
|
||||||
background: rgba(76, 175, 80, 0.1);
|
|
||||||
color: #4caf50;
|
|
||||||
border: 1px solid rgba(76, 175, 80, 0.2);
|
|
||||||
}
|
|
||||||
|
|
||||||
.alert-info {
|
|
||||||
background: rgba(33, 150, 243, 0.1);
|
|
||||||
color: #2196f3;
|
|
||||||
border: 1px solid rgba(33, 150, 243, 0.2);
|
|
||||||
}
|
|
||||||
|
|
||||||
.alert-icon {
|
|
||||||
font-size: 1.2em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.alert-content {
|
|
||||||
flex: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.primary-btn {
|
|
||||||
width: 100%;
|
|
||||||
background: linear-gradient(135deg, var(--nj-brown), var(--nj-light-brown));
|
|
||||||
color: white;
|
|
||||||
border: none;
|
|
||||||
padding: 16px 24px;
|
|
||||||
border-radius: 12px;
|
|
||||||
font-size: 16px;
|
|
||||||
font-weight: 600;
|
|
||||||
cursor: pointer;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: space-between;
|
|
||||||
transition: all 0.3s ease;
|
|
||||||
margin-top: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.primary-btn:hover {
|
|
||||||
transform: translateY(-2px);
|
|
||||||
box-shadow: 0 8px 25px rgba(139, 69, 19, 0.3);
|
|
||||||
background: linear-gradient(135deg, var(--nj-light-brown), var(--nj-brown));
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-icon {
|
|
||||||
font-size: 1.2em;
|
|
||||||
transition: transform 0.3s ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
.primary-btn:hover .btn-icon {
|
|
||||||
transform: translateX(3px);
|
|
||||||
}
|
|
||||||
|
|
||||||
.auth-footer {
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.divider {
|
|
||||||
position: relative;
|
|
||||||
margin: 25px 0;
|
|
||||||
color: var(--text-light);
|
|
||||||
font-size: 14px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.divider::before {
|
|
||||||
content: "";
|
|
||||||
position: absolute;
|
|
||||||
top: 50%;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
height: 1px;
|
|
||||||
background: var(--border-color);
|
|
||||||
z-index: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.divider span {
|
|
||||||
background: var(--bg-white);
|
|
||||||
padding: 0 15px;
|
|
||||||
position: relative;
|
|
||||||
z-index: 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
.auth-links p {
|
|
||||||
margin: 12px 0;
|
|
||||||
color: var(--text-light);
|
|
||||||
font-size: 14px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.link {
|
|
||||||
color: var(--nj-brown);
|
|
||||||
text-decoration: none;
|
|
||||||
font-weight: 500;
|
|
||||||
transition: color 0.3s ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
.link:hover {
|
|
||||||
color: var(--nj-red);
|
|
||||||
text-decoration: underline;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 响应式设计 */
|
|
||||||
@media (max-width: 480px) {
|
|
||||||
.auth-card {
|
|
||||||
padding: 30px 25px;
|
|
||||||
margin: 0 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.auth-icon {
|
|
||||||
width: 70px;
|
|
||||||
height: 70px;
|
|
||||||
font-size: 1.8em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.auth-header h2 {
|
|
||||||
font-size: 1.6em;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
{% endblock %}
|
|
||||||
@ -1,244 +0,0 @@
|
|||||||
{% extends 'base.html' %}
|
|
||||||
|
|
||||||
{% block content %}
|
|
||||||
<div class="auth-page">
|
|
||||||
<div class="auth-container">
|
|
||||||
<div class="auth-card">
|
|
||||||
<div class="auth-header">
|
|
||||||
<div class="auth-icon">🔐</div>
|
|
||||||
<h2>重置密码</h2>
|
|
||||||
<p>请输入您注册时使用的邮箱地址</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<form method="post" class="auth-form">
|
|
||||||
{% csrf_token %}
|
|
||||||
|
|
||||||
{% if messages %}
|
|
||||||
<div class="messages">
|
|
||||||
{% for message in messages %}
|
|
||||||
<div class="alert alert-{{ message.tags }}">
|
|
||||||
<div class="alert-icon">
|
|
||||||
{% if message.tags == 'success' %}✅{% else %}⚠️{% endif %}
|
|
||||||
</div>
|
|
||||||
<div class="alert-content">{{ message }}</div>
|
|
||||||
</div>
|
|
||||||
{% endfor %}
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
<div class="form-group">
|
|
||||||
<label for="email" class="form-label">邮箱地址</label>
|
|
||||||
<div class="input-with-icon">
|
|
||||||
<span class="input-icon">📧</span>
|
|
||||||
<input type="email" name="email" id="email"
|
|
||||||
placeholder="请输入注册时使用的邮箱" required
|
|
||||||
class="form-control">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<button type="submit" class="auth-btn primary-btn">
|
|
||||||
<span>发送验证码</span>
|
|
||||||
<span class="btn-icon">→</span>
|
|
||||||
</button>
|
|
||||||
</form>
|
|
||||||
|
|
||||||
<div class="auth-footer">
|
|
||||||
<div class="auth-links">
|
|
||||||
<p><a href="{% url 'login' %}" class="link">返回登录</a></p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<style>
|
|
||||||
/* 复用之前的登录页面样式 */
|
|
||||||
.auth-page {
|
|
||||||
min-height: 80vh;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
padding: 40px 20px;
|
|
||||||
background: linear-gradient(135deg, #faf3e8 0%, #fffaf0 100%);
|
|
||||||
}
|
|
||||||
|
|
||||||
.auth-container {
|
|
||||||
width: 100%;
|
|
||||||
max-width: 420px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.auth-card {
|
|
||||||
background: var(--bg-white);
|
|
||||||
padding: 40px 35px;
|
|
||||||
border-radius: 20px;
|
|
||||||
box-shadow: 0 15px 35px rgba(139, 69, 19, 0.15);
|
|
||||||
border: 1px solid var(--border-color);
|
|
||||||
position: relative;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
.auth-card::before {
|
|
||||||
content: "";
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
height: 4px;
|
|
||||||
background: linear-gradient(90deg, var(--nj-red), var(--nj-gold), var(--nj-brown));
|
|
||||||
}
|
|
||||||
|
|
||||||
.auth-header {
|
|
||||||
text-align: center;
|
|
||||||
margin-bottom: 35px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.auth-icon {
|
|
||||||
width: 80px;
|
|
||||||
height: 80px;
|
|
||||||
margin: 0 auto 20px;
|
|
||||||
background: linear-gradient(135deg, var(--nj-gold), var(--nj-brown));
|
|
||||||
border-radius: 50%;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
font-size: 2em;
|
|
||||||
box-shadow: 0 8px 20px rgba(212, 175, 55, 0.3);
|
|
||||||
}
|
|
||||||
|
|
||||||
.auth-header h2 {
|
|
||||||
color: var(--text-dark);
|
|
||||||
font-size: 1.8em;
|
|
||||||
margin-bottom: 8px;
|
|
||||||
font-weight: 600;
|
|
||||||
}
|
|
||||||
|
|
||||||
.auth-header p {
|
|
||||||
color: var(--text-light);
|
|
||||||
font-size: 1em;
|
|
||||||
opacity: 0.8;
|
|
||||||
}
|
|
||||||
|
|
||||||
.auth-form {
|
|
||||||
margin-bottom: 25px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.form-group {
|
|
||||||
margin-bottom: 25px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.form-label {
|
|
||||||
display: block;
|
|
||||||
margin-bottom: 8px;
|
|
||||||
color: var(--text-dark);
|
|
||||||
font-weight: 500;
|
|
||||||
font-size: 14px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.input-with-icon {
|
|
||||||
position: relative;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.input-icon {
|
|
||||||
position: absolute;
|
|
||||||
left: 15px;
|
|
||||||
color: var(--text-light);
|
|
||||||
font-size: 16px;
|
|
||||||
z-index: 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
.form-control {
|
|
||||||
width: 100%;
|
|
||||||
padding: 15px 15px 15px 45px;
|
|
||||||
border: 2px solid var(--border-color);
|
|
||||||
border-radius: 12px;
|
|
||||||
font-size: 15px;
|
|
||||||
transition: all 0.3s ease;
|
|
||||||
background: var(--bg-white);
|
|
||||||
}
|
|
||||||
|
|
||||||
.form-control:focus {
|
|
||||||
outline: none;
|
|
||||||
border-color: var(--nj-gold);
|
|
||||||
box-shadow: 0 0 0 3px rgba(212, 175, 55, 0.1);
|
|
||||||
transform: translateY(-1px);
|
|
||||||
}
|
|
||||||
|
|
||||||
.alert {
|
|
||||||
padding: 12px 16px;
|
|
||||||
border-radius: 10px;
|
|
||||||
margin-bottom: 20px;
|
|
||||||
font-size: 14px;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.alert-error {
|
|
||||||
background: rgba(198, 47, 47, 0.1);
|
|
||||||
color: var(--nj-red);
|
|
||||||
border: 1px solid rgba(198, 47, 47, 0.2);
|
|
||||||
}
|
|
||||||
|
|
||||||
.alert-success {
|
|
||||||
background: rgba(76, 175, 80, 0.1);
|
|
||||||
color: #4caf50;
|
|
||||||
border: 1px solid rgba(76, 175, 80, 0.2);
|
|
||||||
}
|
|
||||||
|
|
||||||
.primary-btn {
|
|
||||||
width: 100%;
|
|
||||||
background: linear-gradient(135deg, var(--nj-brown), var(--nj-light-brown));
|
|
||||||
color: white;
|
|
||||||
border: none;
|
|
||||||
padding: 16px 24px;
|
|
||||||
border-radius: 12px;
|
|
||||||
font-size: 16px;
|
|
||||||
font-weight: 600;
|
|
||||||
cursor: pointer;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: space-between;
|
|
||||||
transition: all 0.3s ease;
|
|
||||||
margin-top: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.primary-btn:hover {
|
|
||||||
transform: translateY(-2px);
|
|
||||||
box-shadow: 0 8px 25px rgba(139, 69, 19, 0.3);
|
|
||||||
background: linear-gradient(135deg, var(--nj-light-brown), var(--nj-brown));
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-icon {
|
|
||||||
font-size: 1.2em;
|
|
||||||
transition: transform 0.3s ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
.primary-btn:hover .btn-icon {
|
|
||||||
transform: translateX(3px);
|
|
||||||
}
|
|
||||||
|
|
||||||
.auth-footer {
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.auth-links p {
|
|
||||||
margin: 12px 0;
|
|
||||||
color: var(--text-light);
|
|
||||||
font-size: 14px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.link {
|
|
||||||
color: var(--nj-brown);
|
|
||||||
text-decoration: none;
|
|
||||||
font-weight: 500;
|
|
||||||
transition: color 0.3s ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
.link:hover {
|
|
||||||
color: var(--nj-red);
|
|
||||||
text-decoration: underline;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
{% endblock %}
|
|
||||||
@ -1,507 +0,0 @@
|
|||||||
{% extends 'base.html' %}
|
|
||||||
|
|
||||||
{% block content %}
|
|
||||||
<div class="profile-page">
|
|
||||||
<div class="profile-container">
|
|
||||||
<!-- 用户信息卡片 -->
|
|
||||||
<div class="profile-card">
|
|
||||||
<div class="profile-header">
|
|
||||||
<div class="avatar-section">
|
|
||||||
{% if user.avatar %}
|
|
||||||
<img src="{{ user.avatar.url }}" alt="头像" class="profile-avatar">
|
|
||||||
{% else %}
|
|
||||||
<div class="avatar-placeholder">
|
|
||||||
{{ user.username|first|upper }}
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
<div class="avatar-overlay">
|
|
||||||
<a href="{% url 'profile_edit' %}" class="edit-avatar-btn">更换头像</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="profile-info">
|
|
||||||
<h1 class="profile-name">{{ user.username }}</h1>
|
|
||||||
<p class="profile-bio">
|
|
||||||
{% if user.bio %}
|
|
||||||
{{ user.bio }}
|
|
||||||
{% else %}
|
|
||||||
这个人很懒,什么都没有写...
|
|
||||||
{% endif %}
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<div class="profile-meta">
|
|
||||||
<div class="meta-item">
|
|
||||||
<span class="meta-icon">📅</span>
|
|
||||||
<span class="meta-text">加入时间:{{ user.date_joined|date:"Y年m月d日" }}</span>
|
|
||||||
</div>
|
|
||||||
{% if user.location %}
|
|
||||||
<div class="meta-item">
|
|
||||||
<span class="meta-icon">📍</span>
|
|
||||||
<span class="meta-text">{{ user.location }}</span>
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
{% if user.website %}
|
|
||||||
<div class="meta-item">
|
|
||||||
<span class="meta-icon">🌐</span>
|
|
||||||
<a href="{{ user.website }}" class="meta-link" target="_blank">个人网站</a>
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<a href="{% url 'profile_edit' %}" class="edit-profile-btn">
|
|
||||||
<span class="btn-icon">⚙️</span>
|
|
||||||
编辑资料
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- 数据统计 -->
|
|
||||||
<div class="stats-grid">
|
|
||||||
<div class="stat-card">
|
|
||||||
<div class="stat-icon">📝</div>
|
|
||||||
<div class="stat-content">
|
|
||||||
<div class="stat-number">{{ user.post_set.count }}</div>
|
|
||||||
<div class="stat-label">发表文章</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="stat-card">
|
|
||||||
<div class="stat-icon">💬</div>
|
|
||||||
<div class="stat-content">
|
|
||||||
<div class="stat-number">{{ user.comments.count }}</div>
|
|
||||||
<div class="stat-label">评论数量</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="stat-card">
|
|
||||||
<div class="stat-icon">❤️</div>
|
|
||||||
<div class="stat-content">
|
|
||||||
<div class="stat-number">{{ user.post_likes.count }}</div>
|
|
||||||
<div class="stat-label">获赞数量</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="stat-card">
|
|
||||||
<div class="stat-icon">⭐</div>
|
|
||||||
<div class="stat-content">
|
|
||||||
<div class="stat-number">{{ user.post_favorites.count }}</div>
|
|
||||||
<div class="stat-label">文章收藏</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- 快速操作 -->
|
|
||||||
<div class="actions-section">
|
|
||||||
<h2 class="section-title">快速操作</h2>
|
|
||||||
<div class="action-buttons">
|
|
||||||
<a href="{% url 'create_post' %}" class="action-btn primary">
|
|
||||||
<span class="action-icon">✍️</span>
|
|
||||||
<span class="action-text">发表文章</span>
|
|
||||||
<span class="action-desc">分享非遗故事</span>
|
|
||||||
</a>
|
|
||||||
|
|
||||||
<a href="{% url 'user_posts' %}" class="action-btn secondary">
|
|
||||||
<span class="action-icon">📚</span>
|
|
||||||
<span class="action-text">我的文章</span>
|
|
||||||
<span class="action-desc">管理我的创作</span>
|
|
||||||
</a>
|
|
||||||
|
|
||||||
<a href="{% url 'index' %}" class="action-btn tertiary">
|
|
||||||
<span class="action-icon">🏠</span>
|
|
||||||
<span class="action-text">返回首页</span>
|
|
||||||
<span class="action-desc">浏览更多内容</span>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- 最近活动 -->
|
|
||||||
<div class="activity-section">
|
|
||||||
<h2 class="section-title">最近活动</h2>
|
|
||||||
<div class="activity-list">
|
|
||||||
{% if user.comments.all %}
|
|
||||||
{% for comment in user.comments.all|slice:":5" %}
|
|
||||||
<div class="activity-item">
|
|
||||||
<div class="activity-icon">💬</div>
|
|
||||||
<div class="activity-content">
|
|
||||||
<p>评论了文章 <a href="{% url 'detail' comment.post.id %}">{{ comment.post.title }}</a></p>
|
|
||||||
<span class="activity-time">{{ comment.created_time|timesince }}前</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{% endfor %}
|
|
||||||
{% else %}
|
|
||||||
<div class="empty-state">
|
|
||||||
<div class="empty-icon">📝</div>
|
|
||||||
<p>暂无活动记录</p>
|
|
||||||
<p class="empty-desc">快去发表文章或评论吧!</p>
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<style>
|
|
||||||
.profile-page {
|
|
||||||
min-height: 100vh;
|
|
||||||
background: linear-gradient(135deg, #faf3e8 0%, #fffaf0 100%);
|
|
||||||
padding: 30px 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.profile-container {
|
|
||||||
max-width: 1000px;
|
|
||||||
margin: 0 auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 用户信息卡片 */
|
|
||||||
.profile-card {
|
|
||||||
background: var(--bg-white);
|
|
||||||
border-radius: 20px;
|
|
||||||
box-shadow: 0 15px 35px rgba(139, 69, 19, 0.1);
|
|
||||||
border: 1px solid var(--border-color);
|
|
||||||
overflow: hidden;
|
|
||||||
margin-bottom: 30px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.profile-header {
|
|
||||||
padding: 40px;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 30px;
|
|
||||||
background: linear-gradient(135deg, var(--primary-color), var(--secondary-color));
|
|
||||||
color: white;
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
|
|
||||||
.avatar-section {
|
|
||||||
position: relative;
|
|
||||||
flex-shrink: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.profile-avatar {
|
|
||||||
width: 120px;
|
|
||||||
height: 120px;
|
|
||||||
border-radius: 50%;
|
|
||||||
object-fit: cover;
|
|
||||||
border: 4px solid rgba(255, 255, 255, 0.3);
|
|
||||||
box-shadow: 0 8px 25px rgba(0, 0, 0, 0.2);
|
|
||||||
}
|
|
||||||
|
|
||||||
.avatar-placeholder {
|
|
||||||
width: 120px;
|
|
||||||
height: 120px;
|
|
||||||
border-radius: 50%;
|
|
||||||
background: linear-gradient(135deg, var(--nj-gold), var(--nj-red));
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
color: white;
|
|
||||||
font-size: 2.5em;
|
|
||||||
font-weight: bold;
|
|
||||||
border: 4px solid rgba(255, 255, 255, 0.3);
|
|
||||||
}
|
|
||||||
|
|
||||||
.avatar-overlay {
|
|
||||||
position: absolute;
|
|
||||||
bottom: 0;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
background: rgba(0, 0, 0, 0.7);
|
|
||||||
padding: 8px;
|
|
||||||
text-align: center;
|
|
||||||
opacity: 0;
|
|
||||||
transition: opacity 0.3s ease;
|
|
||||||
border-radius: 0 0 60px 60px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.avatar-section:hover .avatar-overlay {
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.edit-avatar-btn {
|
|
||||||
color: white;
|
|
||||||
font-size: 12px;
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.profile-info {
|
|
||||||
flex: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.profile-name {
|
|
||||||
font-size: 2.2em;
|
|
||||||
margin-bottom: 10px;
|
|
||||||
font-weight: 700;
|
|
||||||
text-shadow: 1px 1px 3px rgba(0, 0, 0, 0.3);
|
|
||||||
}
|
|
||||||
|
|
||||||
.profile-bio {
|
|
||||||
font-size: 1.1em;
|
|
||||||
opacity: 0.9;
|
|
||||||
margin-bottom: 20px;
|
|
||||||
line-height: 1.5;
|
|
||||||
font-style: italic;
|
|
||||||
}
|
|
||||||
|
|
||||||
.profile-meta {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.meta-item {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 10px;
|
|
||||||
font-size: 14px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.meta-link {
|
|
||||||
color: var(--nj-gold);
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.meta-link:hover {
|
|
||||||
text-decoration: underline;
|
|
||||||
}
|
|
||||||
|
|
||||||
.edit-profile-btn {
|
|
||||||
background: rgba(255, 255, 255, 0.2);
|
|
||||||
color: white;
|
|
||||||
padding: 12px 20px;
|
|
||||||
border-radius: 25px;
|
|
||||||
text-decoration: none;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 8px;
|
|
||||||
transition: all 0.3s ease;
|
|
||||||
border: 1px solid rgba(255, 255, 255, 0.3);
|
|
||||||
backdrop-filter: blur(10px);
|
|
||||||
}
|
|
||||||
|
|
||||||
.edit-profile-btn:hover {
|
|
||||||
background: rgba(255, 255, 255, 0.3);
|
|
||||||
transform: translateY(-2px);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 数据统计 */
|
|
||||||
.stats-grid {
|
|
||||||
display: grid;
|
|
||||||
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
|
||||||
gap: 20px;
|
|
||||||
margin-bottom: 30px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.stat-card {
|
|
||||||
background: var(--bg-white);
|
|
||||||
padding: 25px;
|
|
||||||
border-radius: 15px;
|
|
||||||
box-shadow: 0 8px 25px rgba(139, 69, 19, 0.1);
|
|
||||||
border: 1px solid var(--border-color);
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 20px;
|
|
||||||
transition: transform 0.3s ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
.stat-card:hover {
|
|
||||||
transform: translateY(-5px);
|
|
||||||
}
|
|
||||||
|
|
||||||
.stat-icon {
|
|
||||||
font-size: 2.5em;
|
|
||||||
opacity: 0.8;
|
|
||||||
}
|
|
||||||
|
|
||||||
.stat-content {
|
|
||||||
flex: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.stat-number {
|
|
||||||
font-size: 2em;
|
|
||||||
font-weight: bold;
|
|
||||||
color: var(--primary-color);
|
|
||||||
margin-bottom: 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.stat-label {
|
|
||||||
color: var(--text-light);
|
|
||||||
font-size: 14px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 快速操作 */
|
|
||||||
.actions-section {
|
|
||||||
margin-bottom: 30px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.section-title {
|
|
||||||
color: var(--primary-color);
|
|
||||||
font-size: 1.5em;
|
|
||||||
margin-bottom: 20px;
|
|
||||||
padding-bottom: 10px;
|
|
||||||
border-bottom: 2px solid var(--accent-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
.action-buttons {
|
|
||||||
display: grid;
|
|
||||||
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
|
|
||||||
gap: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.action-btn {
|
|
||||||
background: var(--bg-white);
|
|
||||||
padding: 25px;
|
|
||||||
border-radius: 15px;
|
|
||||||
text-decoration: none;
|
|
||||||
box-shadow: 0 8px 25px rgba(139, 69, 19, 0.1);
|
|
||||||
border: 1px solid var(--border-color);
|
|
||||||
transition: all 0.3s ease;
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
|
|
||||||
.action-btn:hover {
|
|
||||||
transform: translateY(-5px);
|
|
||||||
box-shadow: 0 15px 35px rgba(139, 69, 19, 0.15);
|
|
||||||
}
|
|
||||||
|
|
||||||
.action-btn.primary {
|
|
||||||
border-left: 4px solid var(--nj-red);
|
|
||||||
}
|
|
||||||
|
|
||||||
.action-btn.secondary {
|
|
||||||
border-left: 4px solid var(--nj-gold);
|
|
||||||
}
|
|
||||||
|
|
||||||
.action-btn.tertiary {
|
|
||||||
border-left: 4px solid var(--nj-brown);
|
|
||||||
}
|
|
||||||
|
|
||||||
.action-icon {
|
|
||||||
font-size: 2em;
|
|
||||||
display: block;
|
|
||||||
margin-bottom: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.action-text {
|
|
||||||
display: block;
|
|
||||||
font-size: 1.2em;
|
|
||||||
font-weight: 600;
|
|
||||||
color: var(--text-dark);
|
|
||||||
margin-bottom: 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.action-desc {
|
|
||||||
display: block;
|
|
||||||
color: var(--text-light);
|
|
||||||
font-size: 0.9em;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 最近活动 */
|
|
||||||
.activity-section {
|
|
||||||
background: var(--bg-white);
|
|
||||||
padding: 30px;
|
|
||||||
border-radius: 15px;
|
|
||||||
box-shadow: 0 8px 25px rgba(139, 69, 19, 0.1);
|
|
||||||
border: 1px solid var(--border-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
.activity-list {
|
|
||||||
max-height: 300px;
|
|
||||||
overflow-y: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.activity-item {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 15px;
|
|
||||||
padding: 15px 0;
|
|
||||||
border-bottom: 1px solid var(--border-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
.activity-item:last-child {
|
|
||||||
border-bottom: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.activity-icon {
|
|
||||||
font-size: 1.2em;
|
|
||||||
flex-shrink: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.activity-content {
|
|
||||||
flex: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.activity-content p {
|
|
||||||
margin: 0;
|
|
||||||
color: var(--text-dark);
|
|
||||||
}
|
|
||||||
|
|
||||||
.activity-content a {
|
|
||||||
color: var(--primary-color);
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.activity-content a:hover {
|
|
||||||
text-decoration: underline;
|
|
||||||
}
|
|
||||||
|
|
||||||
.activity-time {
|
|
||||||
color: var(--text-light);
|
|
||||||
font-size: 0.9em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.empty-state {
|
|
||||||
text-align: center;
|
|
||||||
padding: 40px 20px;
|
|
||||||
color: var(--text-light);
|
|
||||||
}
|
|
||||||
|
|
||||||
.empty-icon {
|
|
||||||
font-size: 3em;
|
|
||||||
margin-bottom: 15px;
|
|
||||||
opacity: 0.5;
|
|
||||||
}
|
|
||||||
|
|
||||||
.empty-desc {
|
|
||||||
font-size: 0.9em;
|
|
||||||
margin-top: 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 响应式设计 */
|
|
||||||
@media (max-width: 768px) {
|
|
||||||
.profile-header {
|
|
||||||
flex-direction: column;
|
|
||||||
text-align: center;
|
|
||||||
padding: 30px 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.profile-meta {
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.stats-grid {
|
|
||||||
grid-template-columns: 1fr 1fr;
|
|
||||||
}
|
|
||||||
|
|
||||||
.action-buttons {
|
|
||||||
grid-template-columns: 1fr;
|
|
||||||
}
|
|
||||||
|
|
||||||
.profile-name {
|
|
||||||
font-size: 1.8em;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: 480px) {
|
|
||||||
.stats-grid {
|
|
||||||
grid-template-columns: 1fr;
|
|
||||||
}
|
|
||||||
|
|
||||||
.profile-avatar,
|
|
||||||
.avatar-placeholder {
|
|
||||||
width: 100px;
|
|
||||||
height: 100px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
{% endblock %}
|
|
||||||
@ -1,110 +0,0 @@
|
|||||||
{% extends 'base.html' %}
|
|
||||||
|
|
||||||
{% block content %}
|
|
||||||
<div class="auth-container">
|
|
||||||
<div class="auth-form">
|
|
||||||
<h2>用户注册</h2>
|
|
||||||
<form method="post" enctype="multipart/form-data">
|
|
||||||
{% csrf_token %}
|
|
||||||
{% for field in form %}
|
|
||||||
<div class="form-group">
|
|
||||||
{{ field.label_tag }}
|
|
||||||
{{ field }}
|
|
||||||
{% if field.errors %}
|
|
||||||
<div class="error">{{ field.errors }}</div>
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
{% endfor %}
|
|
||||||
<button type="submit" class="auth-btn">注册</button>
|
|
||||||
</form>
|
|
||||||
<p class="auth-link">已有账号?<a href="{% url 'login' %}">立即登录</a></p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<style>
|
|
||||||
.auth-container {
|
|
||||||
max-width: 500px;
|
|
||||||
margin: 50px auto;
|
|
||||||
padding: 0 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.auth-form {
|
|
||||||
background: var(--bg-white);
|
|
||||||
padding: 40px;
|
|
||||||
border-radius: 15px;
|
|
||||||
box-shadow: 0 10px 30px rgba(139, 69, 19, 0.1);
|
|
||||||
border: 1px solid var(--border-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
.auth-form h2 {
|
|
||||||
text-align: center;
|
|
||||||
color: var(--primary-color);
|
|
||||||
margin-bottom: 30px;
|
|
||||||
font-size: 1.8em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.form-group {
|
|
||||||
margin-bottom: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.form-group label {
|
|
||||||
display: block;
|
|
||||||
margin-bottom: 8px;
|
|
||||||
color: var(--text-dark);
|
|
||||||
font-weight: 500;
|
|
||||||
}
|
|
||||||
|
|
||||||
.form-group input, .form-group textarea, .form-group select {
|
|
||||||
width: 100%;
|
|
||||||
padding: 12px 15px;
|
|
||||||
border: 1px solid var(--border-color);
|
|
||||||
border-radius: 8px;
|
|
||||||
font-size: 16px;
|
|
||||||
transition: border-color 0.3s;
|
|
||||||
}
|
|
||||||
|
|
||||||
.form-group input:focus, .form-group textarea:focus {
|
|
||||||
outline: none;
|
|
||||||
border-color: var(--primary-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
.error {
|
|
||||||
color: #e74c3c;
|
|
||||||
font-size: 14px;
|
|
||||||
margin-top: 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.auth-btn {
|
|
||||||
width: 100%;
|
|
||||||
background: linear-gradient(135deg, var(--primary-color), var(--secondary-color));
|
|
||||||
color: white;
|
|
||||||
border: none;
|
|
||||||
padding: 15px;
|
|
||||||
border-radius: 8px;
|
|
||||||
font-size: 16px;
|
|
||||||
font-weight: 600;
|
|
||||||
cursor: pointer;
|
|
||||||
transition: transform 0.3s;
|
|
||||||
}
|
|
||||||
|
|
||||||
.auth-btn:hover {
|
|
||||||
transform: translateY(-2px);
|
|
||||||
}
|
|
||||||
|
|
||||||
.auth-link {
|
|
||||||
text-align: center;
|
|
||||||
margin-top: 20px;
|
|
||||||
color: var(--text-light);
|
|
||||||
}
|
|
||||||
|
|
||||||
.auth-link a {
|
|
||||||
color: var(--primary-color);
|
|
||||||
text-decoration: none;
|
|
||||||
font-weight: 600;
|
|
||||||
}
|
|
||||||
|
|
||||||
.auth-link a:hover {
|
|
||||||
text-decoration: underline;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
{% endblock %}
|
|
||||||
@ -1,3 +0,0 @@
|
|||||||
from django.test import TestCase
|
|
||||||
|
|
||||||
# Create your tests here.
|
|
||||||
@ -1,17 +0,0 @@
|
|||||||
from django.urls import path
|
|
||||||
from django.contrib.auth import views as auth_views
|
|
||||||
from . import views
|
|
||||||
|
|
||||||
urlpatterns = [
|
|
||||||
path('register/', views.register, name='register'),
|
|
||||||
path('login/', views.user_login, name='login'),
|
|
||||||
path('logout/', auth_views.LogoutView.as_view(), name='logout'),
|
|
||||||
path('profile/', views.profile, name='profile'),
|
|
||||||
path('profile/edit/', views.profile_edit, name='profile_edit'),
|
|
||||||
|
|
||||||
# 邮箱验证密码重置
|
|
||||||
path('password-reset/', views.password_reset_request, name='password_reset_request'),
|
|
||||||
path('password-reset/verify/', views.password_reset_verify, name='password_reset_verify'),
|
|
||||||
path('password-reset/confirm/', views.password_reset_confirm, name='password_reset_confirm'),
|
|
||||||
path('password-reset/complete/', views.password_reset_complete, name='password_reset_complete'),
|
|
||||||
]
|
|
||||||
@ -1,56 +0,0 @@
|
|||||||
# Generated by Django 5.2.7 on 2025-10-08 15:36
|
|
||||||
|
|
||||||
import django.db.models.deletion
|
|
||||||
from django.conf import settings
|
|
||||||
from django.db import migrations, models
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
initial = True
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
|
||||||
]
|
|
||||||
|
|
||||||
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)),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
migrations.CreateModel(
|
|
||||||
name='Tag',
|
|
||||||
fields=[
|
|
||||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
|
||||||
('name', models.CharField(max_length=100, unique=True)),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
migrations.CreateModel(
|
|
||||||
name='Post',
|
|
||||||
fields=[
|
|
||||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
|
||||||
('title', models.CharField(max_length=200)),
|
|
||||||
('content', models.TextField()),
|
|
||||||
('excerpt', models.TextField(blank=True, max_length=200)),
|
|
||||||
('views', models.PositiveIntegerField(default=0)),
|
|
||||||
('created_time', models.DateTimeField(auto_now_add=True)),
|
|
||||||
('updated_time', models.DateTimeField(auto_now=True)),
|
|
||||||
('author', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
|
|
||||||
('category', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='blog.category')),
|
|
||||||
('tags', models.ManyToManyField(blank=True, to='blog.tag')),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
migrations.CreateModel(
|
|
||||||
name='Comment',
|
|
||||||
fields=[
|
|
||||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
|
||||||
('author', models.CharField(max_length=100)),
|
|
||||||
('content', models.TextField()),
|
|
||||||
('created_time', models.DateTimeField(auto_now_add=True)),
|
|
||||||
('post', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='blog.post')),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
]
|
|
||||||
@ -1,160 +0,0 @@
|
|||||||
# Generated by Django 5.2.7 on 2025-10-09 09:21
|
|
||||||
|
|
||||||
import django.db.models.deletion
|
|
||||||
from django.conf import settings
|
|
||||||
from django.db import migrations, models
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
('blog', '0001_initial'),
|
|
||||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.CreateModel(
|
|
||||||
name='PrimaryTag',
|
|
||||||
fields=[
|
|
||||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
|
||||||
('name', models.CharField(max_length=100, unique=True, verbose_name='标签名称')),
|
|
||||||
('color', models.CharField(default='#8b4513', max_length=7, verbose_name='标签颜色')),
|
|
||||||
],
|
|
||||||
options={
|
|
||||||
'verbose_name': '一级标签',
|
|
||||||
'verbose_name_plural': '一级标签',
|
|
||||||
},
|
|
||||||
),
|
|
||||||
migrations.RemoveField(
|
|
||||||
model_name='post',
|
|
||||||
name='tags',
|
|
||||||
),
|
|
||||||
migrations.AlterModelOptions(
|
|
||||||
name='category',
|
|
||||||
options={'ordering': ['order'], 'verbose_name': '非遗分类', 'verbose_name_plural': '非遗分类'},
|
|
||||||
),
|
|
||||||
migrations.AlterModelOptions(
|
|
||||||
name='comment',
|
|
||||||
options={'ordering': ['-created_time'], 'verbose_name': '评论', 'verbose_name_plural': '评论'},
|
|
||||||
),
|
|
||||||
migrations.AlterModelOptions(
|
|
||||||
name='post',
|
|
||||||
options={'ordering': ['-created_time'], 'verbose_name': '非遗文章', 'verbose_name_plural': '非遗文章'},
|
|
||||||
),
|
|
||||||
migrations.AddField(
|
|
||||||
model_name='category',
|
|
||||||
name='description',
|
|
||||||
field=models.TextField(blank=True, verbose_name='分类描述'),
|
|
||||||
),
|
|
||||||
migrations.AddField(
|
|
||||||
model_name='category',
|
|
||||||
name='icon',
|
|
||||||
field=models.CharField(default='🏮', max_length=50, verbose_name='分类图标'),
|
|
||||||
),
|
|
||||||
migrations.AddField(
|
|
||||||
model_name='category',
|
|
||||||
name='order',
|
|
||||||
field=models.IntegerField(default=0, verbose_name='显示顺序'),
|
|
||||||
),
|
|
||||||
migrations.AddField(
|
|
||||||
model_name='post',
|
|
||||||
name='featured_image',
|
|
||||||
field=models.ImageField(blank=True, null=True, upload_to='posts/%Y/%m/', verbose_name='特色图片'),
|
|
||||||
),
|
|
||||||
migrations.AddField(
|
|
||||||
model_name='post',
|
|
||||||
name='status',
|
|
||||||
field=models.CharField(choices=[('draft', '草稿'), ('published', '已发布')], default='published', max_length=10, verbose_name='状态'),
|
|
||||||
),
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='category',
|
|
||||||
name='name',
|
|
||||||
field=models.CharField(max_length=100, unique=True, verbose_name='分类名称'),
|
|
||||||
),
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='comment',
|
|
||||||
name='author',
|
|
||||||
field=models.CharField(max_length=100, verbose_name='评论者'),
|
|
||||||
),
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='comment',
|
|
||||||
name='content',
|
|
||||||
field=models.TextField(verbose_name='评论内容'),
|
|
||||||
),
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='comment',
|
|
||||||
name='created_time',
|
|
||||||
field=models.DateTimeField(auto_now_add=True, verbose_name='评论时间'),
|
|
||||||
),
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='comment',
|
|
||||||
name='post',
|
|
||||||
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='blog.post', verbose_name='所属文章'),
|
|
||||||
),
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='post',
|
|
||||||
name='author',
|
|
||||||
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='作者'),
|
|
||||||
),
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='post',
|
|
||||||
name='category',
|
|
||||||
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='blog.category', verbose_name='分类'),
|
|
||||||
),
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='post',
|
|
||||||
name='content',
|
|
||||||
field=models.TextField(verbose_name='文章内容'),
|
|
||||||
),
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='post',
|
|
||||||
name='created_time',
|
|
||||||
field=models.DateTimeField(auto_now_add=True, verbose_name='创建时间'),
|
|
||||||
),
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='post',
|
|
||||||
name='excerpt',
|
|
||||||
field=models.TextField(blank=True, max_length=300, verbose_name='文章摘要'),
|
|
||||||
),
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='post',
|
|
||||||
name='title',
|
|
||||||
field=models.CharField(max_length=200, verbose_name='文章标题'),
|
|
||||||
),
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='post',
|
|
||||||
name='updated_time',
|
|
||||||
field=models.DateTimeField(auto_now=True, verbose_name='更新时间'),
|
|
||||||
),
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='post',
|
|
||||||
name='views',
|
|
||||||
field=models.PositiveIntegerField(default=0, verbose_name='阅读量'),
|
|
||||||
),
|
|
||||||
migrations.AddField(
|
|
||||||
model_name='post',
|
|
||||||
name='primary_tags',
|
|
||||||
field=models.ManyToManyField(blank=True, to='blog.primarytag', verbose_name='一级标签'),
|
|
||||||
),
|
|
||||||
migrations.CreateModel(
|
|
||||||
name='SecondaryTag',
|
|
||||||
fields=[
|
|
||||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
|
||||||
('name', models.CharField(max_length=100, verbose_name='标签名称')),
|
|
||||||
('tag_type', models.CharField(choices=[('geo', '地理空间'), ('theme', '主题维度'), ('project', '非遗项目'), ('person', '人物传承'), ('time', '时间节庆')], max_length=20, verbose_name='标签类型')),
|
|
||||||
('parent_tag', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='blog.primarytag', verbose_name='父级标签')),
|
|
||||||
],
|
|
||||||
options={
|
|
||||||
'verbose_name': '二级标签',
|
|
||||||
'verbose_name_plural': '二级标签',
|
|
||||||
},
|
|
||||||
),
|
|
||||||
migrations.AddField(
|
|
||||||
model_name='post',
|
|
||||||
name='secondary_tags',
|
|
||||||
field=models.ManyToManyField(blank=True, to='blog.secondarytag', verbose_name='二级标签'),
|
|
||||||
),
|
|
||||||
migrations.DeleteModel(
|
|
||||||
name='Tag',
|
|
||||||
),
|
|
||||||
]
|
|
||||||
@ -1,21 +0,0 @@
|
|||||||
# Generated by Django 5.2.7 on 2025-11-01 19:43
|
|
||||||
|
|
||||||
import django.db.models.deletion
|
|
||||||
from django.conf import settings
|
|
||||||
from django.db import migrations, models
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
('blog', '0002_primarytag_remove_post_tags_alter_category_options_and_more'),
|
|
||||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='comment',
|
|
||||||
name='author',
|
|
||||||
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='评论者'),
|
|
||||||
),
|
|
||||||
]
|
|
||||||
@ -1,16 +0,0 @@
|
|||||||
# Generated by Django 5.2.7 on 2025-11-01 20:12
|
|
||||||
|
|
||||||
from django.db import migrations
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
('blog', '0003_alter_comment_author'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.DeleteModel(
|
|
||||||
name='Comment',
|
|
||||||
),
|
|
||||||
]
|
|
||||||
@ -1,15 +0,0 @@
|
|||||||
{% extends 'base.html' %}
|
|
||||||
|
|
||||||
{% block title %}{{ category.name }} - 我的博客{% endblock %}
|
|
||||||
|
|
||||||
{% block content %}
|
|
||||||
<h1>分类: {{ category.name }}</h1>
|
|
||||||
{% for post in post_list %}
|
|
||||||
<article class="post">
|
|
||||||
<h2><a href="{% url 'detail' post.id %}">{{ post.title }}</a></h2>
|
|
||||||
<!-- 和首页相同的文章显示 -->
|
|
||||||
</article>
|
|
||||||
{% empty %}
|
|
||||||
<p>该分类下还没有文章。</p>
|
|
||||||
{% endfor %}
|
|
||||||
{% endblock %}
|
|
||||||
@ -1,462 +0,0 @@
|
|||||||
{% extends 'base.html' %}
|
|
||||||
|
|
||||||
{% block content %}
|
|
||||||
<div class="create-post-page">
|
|
||||||
<div class="create-post-container">
|
|
||||||
<div class="create-post-header">
|
|
||||||
<div class="header-icon">✍️</div>
|
|
||||||
<h1>发表非遗文章</h1>
|
|
||||||
<p>分享您对南京非物质文化遗产的独特见解和故事</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="create-post-card">
|
|
||||||
<form method="post" enctype="multipart/form-data" class="create-post-form">
|
|
||||||
{% csrf_token %}
|
|
||||||
|
|
||||||
{% if form.errors %}
|
|
||||||
<div class="alert alert-error">
|
|
||||||
<div class="alert-icon">⚠️</div>
|
|
||||||
<div class="alert-content">
|
|
||||||
<strong>请修正以下错误:</strong>
|
|
||||||
<ul>
|
|
||||||
{% for field in form %}
|
|
||||||
{% for error in field.errors %}
|
|
||||||
<li>{{ field.label }}: {{ error }}</li>
|
|
||||||
{% endfor %}
|
|
||||||
{% endfor %}
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
<!-- 基本信息 -->
|
|
||||||
<div class="form-section">
|
|
||||||
<h3 class="section-title">
|
|
||||||
<span class="title-icon">📄</span>
|
|
||||||
文章基本信息
|
|
||||||
</h3>
|
|
||||||
|
|
||||||
<div class="form-group">
|
|
||||||
<label for="{{ form.title.id_for_label }}" class="form-label">
|
|
||||||
文章标题
|
|
||||||
<span class="required">*</span>
|
|
||||||
</label>
|
|
||||||
{{ form.title }}
|
|
||||||
<div class="help-text">请输入吸引人的文章标题</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="form-group">
|
|
||||||
<label for="{{ form.excerpt.id_for_label }}" class="form-label">
|
|
||||||
文章摘要
|
|
||||||
</label>
|
|
||||||
{{ form.excerpt }}
|
|
||||||
<div class="help-text">简要描述文章内容,吸引读者阅读(可选)</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- 分类和标签 -->
|
|
||||||
<div class="form-section">
|
|
||||||
<h3 class="section-title">
|
|
||||||
<span class="title-icon">🏷️</span>
|
|
||||||
分类与标签
|
|
||||||
</h3>
|
|
||||||
|
|
||||||
<div class="form-grid">
|
|
||||||
<div class="form-group">
|
|
||||||
<label for="{{ form.category.id_for_label }}" class="form-label">
|
|
||||||
文章分类
|
|
||||||
<span class="required">*</span>
|
|
||||||
</label>
|
|
||||||
{{ form.category }}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="form-group">
|
|
||||||
<label for="{{ form.featured_image.id_for_label }}" class="form-label">
|
|
||||||
特色图片
|
|
||||||
</label>
|
|
||||||
<div class="file-upload">
|
|
||||||
{{ form.featured_image }}
|
|
||||||
<div class="file-help">支持 JPG、PNG 格式,最大 5MB</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="form-grid">
|
|
||||||
<div class="form-group">
|
|
||||||
<label for="{{ form.primary_tags.id_for_label }}" class="form-label">
|
|
||||||
一级标签
|
|
||||||
</label>
|
|
||||||
{{ form.primary_tags }}
|
|
||||||
<div class="help-text">可多选,按住 Ctrl 键选择多个标签</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="form-group">
|
|
||||||
<label for="{{ form.secondary_tags.id_for_label }}" class="form-label">
|
|
||||||
二级标签
|
|
||||||
</label>
|
|
||||||
{{ form.secondary_tags }}
|
|
||||||
<div class="help-text">可多选,按住 Ctrl 键选择多个标签</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- 文章内容 -->
|
|
||||||
<div class="form-section">
|
|
||||||
<h3 class="section-title">
|
|
||||||
<span class="title-icon">📝</span>
|
|
||||||
文章内容
|
|
||||||
<span class="required">*</span>
|
|
||||||
</h3>
|
|
||||||
|
|
||||||
<div class="form-group">
|
|
||||||
<label for="{{ form.content.id_for_label }}" class="form-label">
|
|
||||||
详细内容
|
|
||||||
</label>
|
|
||||||
{{ form.content }}
|
|
||||||
<div class="help-text">
|
|
||||||
<strong>写作提示:</strong>
|
|
||||||
<ul>
|
|
||||||
<li>详细描述非遗项目的起源、特点和现状</li>
|
|
||||||
<li>可以插入图片、视频等多媒体内容</li>
|
|
||||||
<li>分享个人见解和传承故事</li>
|
|
||||||
<li>确保内容真实准确</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- 表单操作 -->
|
|
||||||
<div class="form-actions">
|
|
||||||
<button type="submit" class="btn btn-primary">
|
|
||||||
<span class="btn-icon">💾</span>
|
|
||||||
保存草稿
|
|
||||||
</button>
|
|
||||||
<a href="{% url 'index' %}" class="btn btn-secondary">
|
|
||||||
<span class="btn-icon">↩️</span>
|
|
||||||
取消返回
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- 写作提示 -->
|
|
||||||
<div class="writing-tips">
|
|
||||||
<div class="tips-icon">💡</div>
|
|
||||||
<div class="tips-content">
|
|
||||||
<h4>写作提示</h4>
|
|
||||||
<ul>
|
|
||||||
<li>确保内容真实准确,尊重传统文化</li>
|
|
||||||
<li>可以结合图片、视频等多媒体素材</li>
|
|
||||||
<li>文章保存为草稿后,可以在"我的文章"中编辑和发布</li>
|
|
||||||
<li>发布前请仔细检查内容</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<style>
|
|
||||||
.create-post-page {
|
|
||||||
min-height: 100vh;
|
|
||||||
background: linear-gradient(135deg, #faf3e8 0%, #fffaf0 100%);
|
|
||||||
padding: 30px 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.create-post-container {
|
|
||||||
max-width: 900px;
|
|
||||||
margin: 0 auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.create-post-header {
|
|
||||||
text-align: center;
|
|
||||||
margin-bottom: 40px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.header-icon {
|
|
||||||
font-size: 4em;
|
|
||||||
margin-bottom: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.create-post-header h1 {
|
|
||||||
color: var(--primary-color);
|
|
||||||
font-size: 2.5em;
|
|
||||||
margin-bottom: 10px;
|
|
||||||
font-weight: 700;
|
|
||||||
}
|
|
||||||
|
|
||||||
.create-post-header p {
|
|
||||||
color: var(--text-light);
|
|
||||||
font-size: 1.1em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.create-post-card {
|
|
||||||
background: var(--bg-white);
|
|
||||||
border-radius: 20px;
|
|
||||||
box-shadow: 0 15px 35px rgba(139, 69, 19, 0.1);
|
|
||||||
border: 1px solid var(--border-color);
|
|
||||||
overflow: hidden;
|
|
||||||
margin-bottom: 30px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.create-post-form {
|
|
||||||
padding: 40px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 复用编辑资料页面的样式,这里简化显示 */
|
|
||||||
.form-section {
|
|
||||||
margin-bottom: 40px;
|
|
||||||
padding-bottom: 30px;
|
|
||||||
border-bottom: 1px solid var(--border-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
.form-section:last-child {
|
|
||||||
border-bottom: none;
|
|
||||||
margin-bottom: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.section-title {
|
|
||||||
color: var(--primary-color);
|
|
||||||
font-size: 1.3em;
|
|
||||||
margin-bottom: 25px;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.title-icon {
|
|
||||||
font-size: 1.2em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.form-grid {
|
|
||||||
display: grid;
|
|
||||||
grid-template-columns: 1fr 1fr;
|
|
||||||
gap: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.form-group {
|
|
||||||
margin-bottom: 25px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.form-label {
|
|
||||||
display: block;
|
|
||||||
margin-bottom: 8px;
|
|
||||||
color: var(--text-dark);
|
|
||||||
font-weight: 600;
|
|
||||||
font-size: 14px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.required {
|
|
||||||
color: var(--nj-red);
|
|
||||||
}
|
|
||||||
|
|
||||||
.form-control {
|
|
||||||
width: 100%;
|
|
||||||
padding: 15px;
|
|
||||||
border: 2px solid var(--border-color);
|
|
||||||
border-radius: 10px;
|
|
||||||
font-size: 15px;
|
|
||||||
transition: all 0.3s ease;
|
|
||||||
background: var(--bg-white);
|
|
||||||
font-family: inherit;
|
|
||||||
}
|
|
||||||
|
|
||||||
.form-control:focus {
|
|
||||||
outline: none;
|
|
||||||
border-color: var(--nj-gold);
|
|
||||||
box-shadow: 0 0 0 3px rgba(212, 175, 55, 0.1);
|
|
||||||
transform: translateY(-1px);
|
|
||||||
}
|
|
||||||
|
|
||||||
textarea.form-control {
|
|
||||||
resize: vertical;
|
|
||||||
min-height: 120px;
|
|
||||||
line-height: 1.5;
|
|
||||||
}
|
|
||||||
|
|
||||||
#id_content {
|
|
||||||
min-height: 300px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.help-text {
|
|
||||||
font-size: 12px;
|
|
||||||
color: var(--text-light);
|
|
||||||
margin-top: 5px;
|
|
||||||
line-height: 1.4;
|
|
||||||
}
|
|
||||||
|
|
||||||
.help-text ul {
|
|
||||||
margin: 5px 0 0 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.help-text li {
|
|
||||||
margin: 3px 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.file-upload {
|
|
||||||
border: 2px dashed var(--border-color);
|
|
||||||
border-radius: 10px;
|
|
||||||
padding: 20px;
|
|
||||||
text-align: center;
|
|
||||||
transition: all 0.3s ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
.file-upload:hover {
|
|
||||||
border-color: var(--nj-gold);
|
|
||||||
background: rgba(212, 175, 55, 0.05);
|
|
||||||
}
|
|
||||||
|
|
||||||
.file-help {
|
|
||||||
font-size: 12px;
|
|
||||||
color: var(--text-light);
|
|
||||||
margin-top: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.alert {
|
|
||||||
padding: 20px;
|
|
||||||
border-radius: 10px;
|
|
||||||
margin-bottom: 25px;
|
|
||||||
display: flex;
|
|
||||||
align-items: flex-start;
|
|
||||||
gap: 15px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.alert-error {
|
|
||||||
background: rgba(198, 47, 47, 0.1);
|
|
||||||
border: 1px solid rgba(198, 47, 47, 0.2);
|
|
||||||
color: var(--nj-red);
|
|
||||||
}
|
|
||||||
|
|
||||||
.alert-icon {
|
|
||||||
font-size: 1.5em;
|
|
||||||
flex-shrink: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.alert-content {
|
|
||||||
flex: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.alert-content ul {
|
|
||||||
margin: 10px 0 0 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.alert-content li {
|
|
||||||
margin: 5px 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.form-actions {
|
|
||||||
display: flex;
|
|
||||||
gap: 15px;
|
|
||||||
justify-content: center;
|
|
||||||
margin-top: 40px;
|
|
||||||
padding-top: 30px;
|
|
||||||
border-top: 1px solid var(--border-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn {
|
|
||||||
padding: 15px 30px;
|
|
||||||
border: none;
|
|
||||||
border-radius: 10px;
|
|
||||||
font-size: 16px;
|
|
||||||
font-weight: 600;
|
|
||||||
cursor: pointer;
|
|
||||||
text-decoration: none;
|
|
||||||
display: inline-flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 8px;
|
|
||||||
transition: all 0.3s ease;
|
|
||||||
min-width: 140px;
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-primary {
|
|
||||||
background: linear-gradient(135deg, var(--nj-brown), var(--nj-light-brown));
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-primary:hover {
|
|
||||||
transform: translateY(-2px);
|
|
||||||
box-shadow: 0 8px 20px rgba(139, 69, 19, 0.3);
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-secondary {
|
|
||||||
background: var(--border-color);
|
|
||||||
color: var(--text-dark);
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-secondary:hover {
|
|
||||||
background: var(--text-light);
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
|
|
||||||
.writing-tips {
|
|
||||||
background: var(--bg-white);
|
|
||||||
padding: 25px;
|
|
||||||
border-radius: 15px;
|
|
||||||
box-shadow: 0 8px 25px rgba(139, 69, 19, 0.1);
|
|
||||||
border: 1px solid var(--border-color);
|
|
||||||
display: flex;
|
|
||||||
align-items: flex-start;
|
|
||||||
gap: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tips-icon {
|
|
||||||
font-size: 2.5em;
|
|
||||||
flex-shrink: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tips-content h4 {
|
|
||||||
color: var(--primary-color);
|
|
||||||
margin-bottom: 15px;
|
|
||||||
font-size: 1.2em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tips-content ul {
|
|
||||||
margin: 0;
|
|
||||||
padding-left: 20px;
|
|
||||||
color: var(--text-light);
|
|
||||||
}
|
|
||||||
|
|
||||||
.tips-content li {
|
|
||||||
margin: 8px 0;
|
|
||||||
line-height: 1.5;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 响应式设计 */
|
|
||||||
@media (max-width: 768px) {
|
|
||||||
.create-post-form {
|
|
||||||
padding: 25px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.form-grid {
|
|
||||||
grid-template-columns: 1fr;
|
|
||||||
gap: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.form-actions {
|
|
||||||
flex-direction: column;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.create-post-header h1 {
|
|
||||||
font-size: 2em;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: 480px) {
|
|
||||||
.create-post-form {
|
|
||||||
padding: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.create-post-header h1 {
|
|
||||||
font-size: 1.8em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.header-icon {
|
|
||||||
font-size: 3em;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
{% endblock %}
|
|
||||||
@ -1,561 +0,0 @@
|
|||||||
{% extends 'base.html' %}
|
|
||||||
|
|
||||||
{% block content %}
|
|
||||||
<div class="edit-post-page">
|
|
||||||
<div class="edit-post-container">
|
|
||||||
<div class="edit-post-header">
|
|
||||||
<div class="header-icon">✏️</div>
|
|
||||||
<h1>编辑文章</h1>
|
|
||||||
<p>修改您的非遗文章内容</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="edit-post-card">
|
|
||||||
<form method="post" enctype="multipart/form-data" class="edit-post-form">
|
|
||||||
{% csrf_token %}
|
|
||||||
|
|
||||||
{% if form.errors %}
|
|
||||||
<div class="alert alert-error">
|
|
||||||
<div class="alert-icon">⚠️</div>
|
|
||||||
<div class="alert-content">
|
|
||||||
<strong>请修正以下错误:</strong>
|
|
||||||
<ul>
|
|
||||||
{% for field in form %}
|
|
||||||
{% for error in field.errors %}
|
|
||||||
<li>{{ field.label }}: {{ error }}</li>
|
|
||||||
{% endfor %}
|
|
||||||
{% endfor %}
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
{% if messages %}
|
|
||||||
<div class="messages">
|
|
||||||
{% for message in messages %}
|
|
||||||
<div class="alert alert-{{ message.tags }}">
|
|
||||||
<div class="alert-icon">✅</div>
|
|
||||||
<div class="alert-content">{{ message }}</div>
|
|
||||||
</div>
|
|
||||||
{% endfor %}
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
<!-- 文章状态信息 -->
|
|
||||||
<div class="post-status-info">
|
|
||||||
<div class="status-item">
|
|
||||||
<span class="status-label">当前状态:</span>
|
|
||||||
<span class="status-value {% if post.status == 'published' %}published{% else %}draft{% endif %}">
|
|
||||||
{% if post.status == 'published' %}已发布{% else %}草稿{% endif %}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<div class="status-item">
|
|
||||||
<span class="status-label">创建时间:</span>
|
|
||||||
<span class="status-value">{{ post.created_time|date:"Y-m-d H:i" }}</span>
|
|
||||||
</div>
|
|
||||||
<div class="status-item">
|
|
||||||
<span class="status-label">最后更新:</span>
|
|
||||||
<span class="status-value">{{ post.updated_time|date:"Y-m-d H:i" }}</span>
|
|
||||||
</div>
|
|
||||||
<div class="status-item">
|
|
||||||
<span class="status-label">阅读量:</span>
|
|
||||||
<span class="status-value">{{ post.views }}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- 基本信息 -->
|
|
||||||
<div class="form-section">
|
|
||||||
<h3 class="section-title">
|
|
||||||
<span class="title-icon">📄</span>
|
|
||||||
文章基本信息
|
|
||||||
</h3>
|
|
||||||
|
|
||||||
<div class="form-group">
|
|
||||||
<label for="{{ form.title.id_for_label }}" class="form-label">
|
|
||||||
文章标题
|
|
||||||
<span class="required">*</span>
|
|
||||||
</label>
|
|
||||||
{{ form.title }}
|
|
||||||
<div class="help-text">请输入吸引人的文章标题</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="form-group">
|
|
||||||
<label for="{{ form.excerpt.id_for_label }}" class="form-label">
|
|
||||||
文章摘要
|
|
||||||
</label>
|
|
||||||
{{ form.excerpt }}
|
|
||||||
<div class="help-text">简要描述文章内容,吸引读者阅读(可选)</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- 分类和标签 -->
|
|
||||||
<div class="form-section">
|
|
||||||
<h3 class="section-title">
|
|
||||||
<span class="title-icon">🏷️</span>
|
|
||||||
分类与标签
|
|
||||||
</h3>
|
|
||||||
|
|
||||||
<div class="form-grid">
|
|
||||||
<div class="form-group">
|
|
||||||
<label for="{{ form.category.id_for_label }}" class="form-label">
|
|
||||||
文章分类
|
|
||||||
<span class="required">*</span>
|
|
||||||
</label>
|
|
||||||
{{ form.category }}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="form-group">
|
|
||||||
<label for="{{ form.featured_image.id_for_label }}" class="form-label">
|
|
||||||
特色图片
|
|
||||||
</label>
|
|
||||||
<div class="file-upload">
|
|
||||||
{% if post.featured_image %}
|
|
||||||
<div class="current-image">
|
|
||||||
<img src="{{ post.featured_image.url }}" alt="当前图片" class="image-preview">
|
|
||||||
<div class="image-info">当前图片</div>
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
{{ form.featured_image }}
|
|
||||||
<div class="file-help">支持 JPG、PNG 格式,最大 5MB</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="form-grid">
|
|
||||||
<div class="form-group">
|
|
||||||
<label for="{{ form.primary_tags.id_for_label }}" class="form-label">
|
|
||||||
一级标签
|
|
||||||
</label>
|
|
||||||
{{ form.primary_tags }}
|
|
||||||
<div class="help-text">可多选,按住 Ctrl 键选择多个标签</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="form-group">
|
|
||||||
<label for="{{ form.secondary_tags.id_for_label }}" class="form-label">
|
|
||||||
二级标签
|
|
||||||
</label>
|
|
||||||
{{ form.secondary_tags }}
|
|
||||||
<div class="help-text">可多选,按住 Ctrl 键选择多个标签</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- 文章内容 -->
|
|
||||||
<div class="form-section">
|
|
||||||
<h3 class="section-title">
|
|
||||||
<span class="title-icon">📝</span>
|
|
||||||
文章内容
|
|
||||||
<span class="required">*</span>
|
|
||||||
</h3>
|
|
||||||
|
|
||||||
<div class="form-group">
|
|
||||||
<label for="{{ form.content.id_for_label }}" class="form-label">
|
|
||||||
详细内容
|
|
||||||
</label>
|
|
||||||
{{ form.content }}
|
|
||||||
<div class="help-text">
|
|
||||||
<strong>写作提示:</strong>
|
|
||||||
<ul>
|
|
||||||
<li>详细描述非遗项目的起源、特点和现状</li>
|
|
||||||
<li>可以插入图片、视频等多媒体内容</li>
|
|
||||||
<li>分享个人见解和传承故事</li>
|
|
||||||
<li>确保内容真实准确</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- 表单操作 -->
|
|
||||||
<div class="form-actions">
|
|
||||||
<button type="submit" class="btn btn-primary">
|
|
||||||
<span class="btn-icon">💾</span>
|
|
||||||
保存更改
|
|
||||||
</button>
|
|
||||||
|
|
||||||
{% if post.status == 'draft' %}
|
|
||||||
<form method="post" action="{% url 'publish_post' post.id %}" class="action-form">
|
|
||||||
{% csrf_token %}
|
|
||||||
<button type="submit" class="btn btn-success">
|
|
||||||
<span class="btn-icon">🚀</span>
|
|
||||||
立即发布
|
|
||||||
</button>
|
|
||||||
</form>
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
<a href="{% url 'user_posts' %}" class="btn btn-secondary">
|
|
||||||
<span class="btn-icon">↩️</span>
|
|
||||||
返回列表
|
|
||||||
</a>
|
|
||||||
|
|
||||||
<a href="{% url 'detail' post.id %}" class="btn btn-info">
|
|
||||||
<span class="btn-icon">👀</span>
|
|
||||||
查看文章
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<style>
|
|
||||||
.edit-post-page {
|
|
||||||
min-height: 100vh;
|
|
||||||
background: linear-gradient(135deg, #faf3e8 0%, #fffaf0 100%);
|
|
||||||
padding: 30px 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.edit-post-container {
|
|
||||||
max-width: 900px;
|
|
||||||
margin: 0 auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.edit-post-header {
|
|
||||||
text-align: center;
|
|
||||||
margin-bottom: 40px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.header-icon {
|
|
||||||
font-size: 4em;
|
|
||||||
margin-bottom: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.edit-post-header h1 {
|
|
||||||
color: var(--primary-color);
|
|
||||||
font-size: 2.5em;
|
|
||||||
margin-bottom: 10px;
|
|
||||||
font-weight: 700;
|
|
||||||
}
|
|
||||||
|
|
||||||
.edit-post-header p {
|
|
||||||
color: var(--text-light);
|
|
||||||
font-size: 1.1em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.edit-post-card {
|
|
||||||
background: var(--bg-white);
|
|
||||||
border-radius: 20px;
|
|
||||||
box-shadow: 0 15px 35px rgba(139, 69, 19, 0.1);
|
|
||||||
border: 1px solid var(--border-color);
|
|
||||||
overflow: hidden;
|
|
||||||
margin-bottom: 30px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.edit-post-form {
|
|
||||||
padding: 40px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 文章状态信息 */
|
|
||||||
.post-status-info {
|
|
||||||
background: var(--bg-light);
|
|
||||||
padding: 20px;
|
|
||||||
border-radius: 10px;
|
|
||||||
margin-bottom: 30px;
|
|
||||||
display: grid;
|
|
||||||
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
|
||||||
gap: 15px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.status-item {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.status-label {
|
|
||||||
font-size: 0.9em;
|
|
||||||
color: var(--text-light);
|
|
||||||
font-weight: 600;
|
|
||||||
}
|
|
||||||
|
|
||||||
.status-value {
|
|
||||||
font-size: 1.1em;
|
|
||||||
font-weight: 600;
|
|
||||||
}
|
|
||||||
|
|
||||||
.status-value.published {
|
|
||||||
color: #4caf50;
|
|
||||||
}
|
|
||||||
|
|
||||||
.status-value.draft {
|
|
||||||
color: var(--text-light);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 表单样式(复用之前的样式) */
|
|
||||||
.form-section {
|
|
||||||
margin-bottom: 40px;
|
|
||||||
padding-bottom: 30px;
|
|
||||||
border-bottom: 1px solid var(--border-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
.form-section:last-child {
|
|
||||||
border-bottom: none;
|
|
||||||
margin-bottom: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.section-title {
|
|
||||||
color: var(--primary-color);
|
|
||||||
font-size: 1.3em;
|
|
||||||
margin-bottom: 25px;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.title-icon {
|
|
||||||
font-size: 1.2em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.form-grid {
|
|
||||||
display: grid;
|
|
||||||
grid-template-columns: 1fr 1fr;
|
|
||||||
gap: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.form-group {
|
|
||||||
margin-bottom: 25px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.form-label {
|
|
||||||
display: block;
|
|
||||||
margin-bottom: 8px;
|
|
||||||
color: var(--text-dark);
|
|
||||||
font-weight: 600;
|
|
||||||
font-size: 14px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.required {
|
|
||||||
color: var(--nj-red);
|
|
||||||
}
|
|
||||||
|
|
||||||
.form-control {
|
|
||||||
width: 100%;
|
|
||||||
padding: 15px;
|
|
||||||
border: 2px solid var(--border-color);
|
|
||||||
border-radius: 10px;
|
|
||||||
font-size: 15px;
|
|
||||||
transition: all 0.3s ease;
|
|
||||||
background: var(--bg-white);
|
|
||||||
font-family: inherit;
|
|
||||||
}
|
|
||||||
|
|
||||||
.form-control:focus {
|
|
||||||
outline: none;
|
|
||||||
border-color: var(--nj-gold);
|
|
||||||
box-shadow: 0 0 0 3px rgba(212, 175, 55, 0.1);
|
|
||||||
transform: translateY(-1px);
|
|
||||||
}
|
|
||||||
|
|
||||||
textarea.form-control {
|
|
||||||
resize: vertical;
|
|
||||||
min-height: 120px;
|
|
||||||
line-height: 1.5;
|
|
||||||
}
|
|
||||||
|
|
||||||
#id_content {
|
|
||||||
min-height: 300px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.help-text {
|
|
||||||
font-size: 12px;
|
|
||||||
color: var(--text-light);
|
|
||||||
margin-top: 5px;
|
|
||||||
line-height: 1.4;
|
|
||||||
}
|
|
||||||
|
|
||||||
.help-text ul {
|
|
||||||
margin: 5px 0 0 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.help-text li {
|
|
||||||
margin: 3px 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 图片上传 */
|
|
||||||
.file-upload {
|
|
||||||
border: 2px dashed var(--border-color);
|
|
||||||
border-radius: 10px;
|
|
||||||
padding: 20px;
|
|
||||||
text-align: center;
|
|
||||||
transition: all 0.3s ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
.file-upload:hover {
|
|
||||||
border-color: var(--nj-gold);
|
|
||||||
background: rgba(212, 175, 55, 0.05);
|
|
||||||
}
|
|
||||||
|
|
||||||
.current-image {
|
|
||||||
margin-bottom: 15px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.image-preview {
|
|
||||||
max-width: 200px;
|
|
||||||
max-height: 150px;
|
|
||||||
border-radius: 8px;
|
|
||||||
border: 2px solid var(--border-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
.image-info {
|
|
||||||
font-size: 0.9em;
|
|
||||||
color: var(--text-light);
|
|
||||||
margin-top: 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.file-help {
|
|
||||||
font-size: 12px;
|
|
||||||
color: var(--text-light);
|
|
||||||
margin-top: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 警告和消息 */
|
|
||||||
.alert {
|
|
||||||
padding: 20px;
|
|
||||||
border-radius: 10px;
|
|
||||||
margin-bottom: 25px;
|
|
||||||
display: flex;
|
|
||||||
align-items: flex-start;
|
|
||||||
gap: 15px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.alert-error {
|
|
||||||
background: rgba(198, 47, 47, 0.1);
|
|
||||||
border: 1px solid rgba(198, 47, 47, 0.2);
|
|
||||||
color: var(--nj-red);
|
|
||||||
}
|
|
||||||
|
|
||||||
.alert-success {
|
|
||||||
background: rgba(76, 175, 80, 0.1);
|
|
||||||
border: 1px solid rgba(76, 175, 80, 0.2);
|
|
||||||
color: #4caf50;
|
|
||||||
}
|
|
||||||
|
|
||||||
.alert-icon {
|
|
||||||
font-size: 1.5em;
|
|
||||||
flex-shrink: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.alert-content {
|
|
||||||
flex: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.alert-content ul {
|
|
||||||
margin: 10px 0 0 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.alert-content li {
|
|
||||||
margin: 5px 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 表单操作 */
|
|
||||||
.form-actions {
|
|
||||||
display: flex;
|
|
||||||
gap: 15px;
|
|
||||||
justify-content: center;
|
|
||||||
margin-top: 40px;
|
|
||||||
padding-top: 30px;
|
|
||||||
border-top: 1px solid var(--border-color);
|
|
||||||
flex-wrap: wrap;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn {
|
|
||||||
padding: 15px 25px;
|
|
||||||
border: none;
|
|
||||||
border-radius: 10px;
|
|
||||||
font-size: 16px;
|
|
||||||
font-weight: 600;
|
|
||||||
cursor: pointer;
|
|
||||||
text-decoration: none;
|
|
||||||
display: inline-flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 8px;
|
|
||||||
transition: all 0.3s ease;
|
|
||||||
min-width: 120px;
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-primary {
|
|
||||||
background: linear-gradient(135deg, var(--nj-brown), var(--nj-light-brown));
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-primary:hover {
|
|
||||||
transform: translateY(-2px);
|
|
||||||
box-shadow: 0 8px 20px rgba(139, 69, 19, 0.3);
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-success {
|
|
||||||
background: #4caf50;
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-success:hover {
|
|
||||||
background: #45a049;
|
|
||||||
transform: translateY(-2px);
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-secondary {
|
|
||||||
background: var(--border-color);
|
|
||||||
color: var(--text-dark);
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-secondary:hover {
|
|
||||||
background: var(--text-light);
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-info {
|
|
||||||
background: var(--primary-color);
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-info:hover {
|
|
||||||
background: var(--secondary-color);
|
|
||||||
transform: translateY(-2px);
|
|
||||||
}
|
|
||||||
|
|
||||||
.action-form {
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 响应式设计 */
|
|
||||||
@media (max-width: 768px) {
|
|
||||||
.edit-post-form {
|
|
||||||
padding: 25px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.form-grid {
|
|
||||||
grid-template-columns: 1fr;
|
|
||||||
gap: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.form-actions {
|
|
||||||
flex-direction: column;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.edit-post-header h1 {
|
|
||||||
font-size: 2em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.post-status-info {
|
|
||||||
grid-template-columns: 1fr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: 480px) {
|
|
||||||
.edit-post-form {
|
|
||||||
padding: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.edit-post-header h1 {
|
|
||||||
font-size: 1.8em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.header-icon {
|
|
||||||
font-size: 3em;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
{% endblock %}
|
|
||||||
@ -1,24 +0,0 @@
|
|||||||
{% extends 'base.html' %}
|
|
||||||
|
|
||||||
{% block content %}
|
|
||||||
<h1>最新文章</h1>
|
|
||||||
{% for post in post_list %}
|
|
||||||
<article class="post">
|
|
||||||
<h2><a href="{% url 'detail' post.id %}">{{ post.title }}</a></h2>
|
|
||||||
<div class="post-meta">
|
|
||||||
<span>{{ post.comment_set.count }} 评论</span> | <span>{{ post.views }} views</span>
|
|
||||||
</div>
|
|
||||||
<p>{{ post.excerpt|default:post.content|truncatewords:30 }}</p>
|
|
||||||
<a href="{% url 'detail' post.id %}" class="read-more">Read more</a>
|
|
||||||
<div class="post-footer">
|
|
||||||
发布于 {{ post.category.name }} 并标记为
|
|
||||||
{% for tag in post.tags.all %}
|
|
||||||
{{ tag.name }}{% if not forloop.last %}, {% endif %}
|
|
||||||
{% endfor %}
|
|
||||||
由 {{ post.author.username }} 在 {{ post.created_time|date:"Y-m-d" }}
|
|
||||||
</div>
|
|
||||||
</article>
|
|
||||||
{% empty %}
|
|
||||||
<p>还没有发布任何文章。</p>
|
|
||||||
{% endfor %}
|
|
||||||
{% endblock %}
|
|
||||||
@ -1,375 +0,0 @@
|
|||||||
{% extends 'base.html' %}
|
|
||||||
|
|
||||||
{% block content %}
|
|
||||||
<div class="user-posts-page">
|
|
||||||
<div class="user-posts-container">
|
|
||||||
<div class="user-posts-header">
|
|
||||||
<div class="header-icon">📚</div>
|
|
||||||
<h1>我的文章</h1>
|
|
||||||
<p>管理您发表的非遗文章</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{% if messages %}
|
|
||||||
<div class="messages">
|
|
||||||
{% for message in messages %}
|
|
||||||
<div class="alert alert-{{ message.tags }}">
|
|
||||||
<div class="alert-icon">✅</div>
|
|
||||||
<div class="alert-content">{{ message }}</div>
|
|
||||||
</div>
|
|
||||||
{% endfor %}
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
<div class="posts-actions">
|
|
||||||
<a href="{% url 'create_post' %}" class="btn btn-primary">
|
|
||||||
<span class="btn-icon">✍️</span>
|
|
||||||
发表新文章
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{% if posts %}
|
|
||||||
<div class="posts-list">
|
|
||||||
{% for post in posts %}
|
|
||||||
<div class="post-card {% if post.status == 'draft' %}draft{% endif %}">
|
|
||||||
<div class="post-header">
|
|
||||||
<h3 class="post-title">
|
|
||||||
<a href="{% url 'detail' post.id %}">{{ post.title }}</a>
|
|
||||||
{% if post.status == 'draft' %}
|
|
||||||
<span class="status-badge draft">草稿</span>
|
|
||||||
{% else %}
|
|
||||||
<span class="status-badge published">已发布</span>
|
|
||||||
{% endif %}
|
|
||||||
</h3>
|
|
||||||
<div class="post-meta">
|
|
||||||
<span class="meta-item">
|
|
||||||
<span class="meta-icon">📅</span>
|
|
||||||
{{ post.created_time|date:"Y-m-d H:i" }}
|
|
||||||
</span>
|
|
||||||
<span class="meta-item">
|
|
||||||
<span class="meta-icon">👁️</span>
|
|
||||||
{{ post.views }} 阅读
|
|
||||||
</span>
|
|
||||||
<span class="meta-item">
|
|
||||||
<span class="meta-icon">💬</span>
|
|
||||||
{{ post.comments.count }} 评论
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{% if post.excerpt %}
|
|
||||||
<div class="post-excerpt">
|
|
||||||
{{ post.excerpt }}
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
<div class="post-actions">
|
|
||||||
<a href="{% url 'edit_post' post.id %}" class="action-btn edit">
|
|
||||||
<span class="action-icon">✏️</span>
|
|
||||||
编辑
|
|
||||||
</a>
|
|
||||||
|
|
||||||
{% if post.status == 'draft' %}
|
|
||||||
<form method="post" action="{% url 'publish_post' post.id %}" class="action-form">
|
|
||||||
{% csrf_token %}
|
|
||||||
<button type="submit" class="action-btn publish">
|
|
||||||
<span class="action-icon">🚀</span>
|
|
||||||
发布
|
|
||||||
</button>
|
|
||||||
</form>
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
<form method="post" action="{% url 'delete_post' post.id %}" class="action-form"
|
|
||||||
onsubmit="return confirm('确定要删除这篇文章吗?此操作不可恢复。');">
|
|
||||||
{% csrf_token %}
|
|
||||||
<button type="submit" class="action-btn delete">
|
|
||||||
<span class="action-icon">🗑️</span>
|
|
||||||
删除
|
|
||||||
</button>
|
|
||||||
</form>
|
|
||||||
|
|
||||||
<a href="{% url 'detail' post.id %}" class="action-btn view">
|
|
||||||
<span class="action-icon">👀</span>
|
|
||||||
查看
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{% endfor %}
|
|
||||||
</div>
|
|
||||||
{% else %}
|
|
||||||
<div class="empty-state">
|
|
||||||
<div class="empty-icon">📝</div>
|
|
||||||
<h3>还没有发表过文章</h3>
|
|
||||||
<p>分享您对非物质文化遗产的见解,让更多人了解传统文化的魅力</p>
|
|
||||||
<a href="{% url 'create_post' %}" class="btn btn-primary">
|
|
||||||
<span class="btn-icon">✍️</span>
|
|
||||||
开始创作第一篇文章
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<style>
|
|
||||||
.user-posts-page {
|
|
||||||
min-height: 100vh;
|
|
||||||
background: linear-gradient(135deg, #faf3e8 0%, #fffaf0 100%);
|
|
||||||
padding: 30px 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.user-posts-container {
|
|
||||||
max-width: 900px;
|
|
||||||
margin: 0 auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.user-posts-header {
|
|
||||||
text-align: center;
|
|
||||||
margin-bottom: 40px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.header-icon {
|
|
||||||
font-size: 4em;
|
|
||||||
margin-bottom: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.user-posts-header h1 {
|
|
||||||
color: var(--primary-color);
|
|
||||||
font-size: 2.5em;
|
|
||||||
margin-bottom: 10px;
|
|
||||||
font-weight: 700;
|
|
||||||
}
|
|
||||||
|
|
||||||
.user-posts-header p {
|
|
||||||
color: var(--text-light);
|
|
||||||
font-size: 1.1em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.posts-actions {
|
|
||||||
margin-bottom: 30px;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 复用之前的按钮样式 */
|
|
||||||
.btn {
|
|
||||||
padding: 15px 30px;
|
|
||||||
border: none;
|
|
||||||
border-radius: 10px;
|
|
||||||
font-size: 16px;
|
|
||||||
font-weight: 600;
|
|
||||||
cursor: pointer;
|
|
||||||
text-decoration: none;
|
|
||||||
display: inline-flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 8px;
|
|
||||||
transition: all 0.3s ease;
|
|
||||||
min-width: 140px;
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-primary {
|
|
||||||
background: linear-gradient(135deg, var(--nj-brown), var(--nj-light-brown));
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-primary:hover {
|
|
||||||
transform: translateY(-2px);
|
|
||||||
box-shadow: 0 8px 20px rgba(139, 69, 19, 0.3);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 文章卡片 */
|
|
||||||
.posts-list {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.post-card {
|
|
||||||
background: var(--bg-white);
|
|
||||||
border-radius: 15px;
|
|
||||||
box-shadow: 0 8px 25px rgba(139, 69, 19, 0.1);
|
|
||||||
border: 1px solid var(--border-color);
|
|
||||||
padding: 25px;
|
|
||||||
transition: all 0.3s ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
.post-card:hover {
|
|
||||||
transform: translateY(-5px);
|
|
||||||
box-shadow: 0 15px 35px rgba(139, 69, 19, 0.15);
|
|
||||||
}
|
|
||||||
|
|
||||||
.post-card.draft {
|
|
||||||
border-left: 4px solid var(--text-light);
|
|
||||||
opacity: 0.8;
|
|
||||||
}
|
|
||||||
|
|
||||||
.post-header {
|
|
||||||
margin-bottom: 15px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.post-title {
|
|
||||||
margin: 0 0 10px 0;
|
|
||||||
font-size: 1.4em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.post-title a {
|
|
||||||
color: var(--text-dark);
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.post-title a:hover {
|
|
||||||
color: var(--primary-color);
|
|
||||||
text-decoration: underline;
|
|
||||||
}
|
|
||||||
|
|
||||||
.status-badge {
|
|
||||||
font-size: 0.7em;
|
|
||||||
padding: 4px 8px;
|
|
||||||
border-radius: 12px;
|
|
||||||
margin-left: 10px;
|
|
||||||
font-weight: 600;
|
|
||||||
}
|
|
||||||
|
|
||||||
.status-badge.draft {
|
|
||||||
background: var(--text-light);
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
|
|
||||||
.status-badge.published {
|
|
||||||
background: var(--nj-gold);
|
|
||||||
color: var(--text-dark);
|
|
||||||
}
|
|
||||||
|
|
||||||
.post-meta {
|
|
||||||
display: flex;
|
|
||||||
gap: 20px;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
}
|
|
||||||
|
|
||||||
.meta-item {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 5px;
|
|
||||||
color: var(--text-light);
|
|
||||||
font-size: 0.9em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.post-excerpt {
|
|
||||||
color: var(--text-light);
|
|
||||||
line-height: 1.6;
|
|
||||||
margin-bottom: 20px;
|
|
||||||
padding: 15px;
|
|
||||||
background: var(--bg-light);
|
|
||||||
border-radius: 8px;
|
|
||||||
border-left: 3px solid var(--accent-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
.post-actions {
|
|
||||||
display: flex;
|
|
||||||
gap: 10px;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
}
|
|
||||||
|
|
||||||
.action-btn {
|
|
||||||
padding: 8px 15px;
|
|
||||||
border: none;
|
|
||||||
border-radius: 6px;
|
|
||||||
font-size: 14px;
|
|
||||||
text-decoration: none;
|
|
||||||
display: inline-flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 5px;
|
|
||||||
transition: all 0.3s ease;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
.action-btn.edit {
|
|
||||||
background: var(--nj-gold);
|
|
||||||
color: var(--text-dark);
|
|
||||||
}
|
|
||||||
|
|
||||||
.action-btn.publish {
|
|
||||||
background: #4caf50;
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
|
|
||||||
.action-btn.delete {
|
|
||||||
background: var(--nj-red);
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
|
|
||||||
.action-btn.view {
|
|
||||||
background: var(--primary-color);
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
|
|
||||||
.action-btn:hover {
|
|
||||||
transform: translateY(-2px);
|
|
||||||
opacity: 0.9;
|
|
||||||
}
|
|
||||||
|
|
||||||
.action-form {
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 空状态 */
|
|
||||||
.empty-state {
|
|
||||||
text-align: center;
|
|
||||||
padding: 60px 20px;
|
|
||||||
background: var(--bg-white);
|
|
||||||
border-radius: 15px;
|
|
||||||
box-shadow: 0 8px 25px rgba(139, 69, 19, 0.1);
|
|
||||||
}
|
|
||||||
|
|
||||||
.empty-icon {
|
|
||||||
font-size: 4em;
|
|
||||||
margin-bottom: 20px;
|
|
||||||
opacity: 0.5;
|
|
||||||
}
|
|
||||||
|
|
||||||
.empty-state h3 {
|
|
||||||
color: var(--text-dark);
|
|
||||||
margin-bottom: 10px;
|
|
||||||
font-size: 1.5em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.empty-state p {
|
|
||||||
color: var(--text-light);
|
|
||||||
margin-bottom: 30px;
|
|
||||||
font-size: 1.1em;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 响应式设计 */
|
|
||||||
@media (max-width: 768px) {
|
|
||||||
.user-posts-header h1 {
|
|
||||||
font-size: 2em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.post-actions {
|
|
||||||
flex-direction: column;
|
|
||||||
}
|
|
||||||
|
|
||||||
.action-btn {
|
|
||||||
width: 100%;
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.post-meta {
|
|
||||||
flex-direction: column;
|
|
||||||
gap: 5px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: 480px) {
|
|
||||||
.user-posts-header h1 {
|
|
||||||
font-size: 1.8em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.header-icon {
|
|
||||||
font-size: 3em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.post-card {
|
|
||||||
padding: 20px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
{% endblock %}
|
|
||||||
@ -1,3 +0,0 @@
|
|||||||
from django.test import TestCase
|
|
||||||
|
|
||||||
# Create your tests here.
|
|
||||||
@ -1,21 +0,0 @@
|
|||||||
from django.urls import path
|
|
||||||
from . import views
|
|
||||||
|
|
||||||
urlpatterns = [
|
|
||||||
path('', views.index, name='index'),
|
|
||||||
path('category/<int:category_id>/', views.category_view, name='category'),
|
|
||||||
path('post/<int:post_id>/', views.detail, name='detail'),
|
|
||||||
path('search/', views.search, name='search'),
|
|
||||||
path('about/', views.about, name='about'),
|
|
||||||
|
|
||||||
# 文章管理路由
|
|
||||||
path('create/', views.create_post, name='create_post'),
|
|
||||||
path('my-posts/', views.user_posts, name='user_posts'),
|
|
||||||
path('edit/<int:post_id>/', views.edit_post, name='edit_post'),
|
|
||||||
path('publish/<int:post_id>/', views.publish_post, name='publish_post'),
|
|
||||||
path('delete/<int:post_id>/', views.delete_post, name='delete_post'),
|
|
||||||
|
|
||||||
# 点赞收藏路由
|
|
||||||
path('post/<int:post_id>/like/', views.like_post, name='like_post'),
|
|
||||||
path('post/<int:post_id>/favorite/', views.favorite_post, name='favorite_post'),
|
|
||||||
]
|
|
||||||
@ -1,7 +0,0 @@
|
|||||||
# comments/apps.py
|
|
||||||
from django.apps import AppConfig
|
|
||||||
|
|
||||||
class CommentsConfig(AppConfig):
|
|
||||||
default_auto_field = 'django.db.models.BigAutoField'
|
|
||||||
name = 'comments'
|
|
||||||
verbose_name = '评论管理'
|
|
||||||
@ -1,33 +0,0 @@
|
|||||||
# Generated by Django 5.2.7 on 2025-11-01 20:12
|
|
||||||
|
|
||||||
import django.db.models.deletion
|
|
||||||
from django.conf import settings
|
|
||||||
from django.db import migrations, models
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
initial = True
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
('blog', '0004_delete_comment'),
|
|
||||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.CreateModel(
|
|
||||||
name='Comment',
|
|
||||||
fields=[
|
|
||||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
|
||||||
('content', models.TextField(verbose_name='评论内容')),
|
|
||||||
('created_time', models.DateTimeField(auto_now_add=True, verbose_name='评论时间')),
|
|
||||||
('author', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='comments', to=settings.AUTH_USER_MODEL, verbose_name='评论者')),
|
|
||||||
('post', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='comments', to='blog.post', verbose_name='所属文章')),
|
|
||||||
],
|
|
||||||
options={
|
|
||||||
'verbose_name': '评论',
|
|
||||||
'verbose_name_plural': '评论',
|
|
||||||
'ordering': ['-created_time'],
|
|
||||||
},
|
|
||||||
),
|
|
||||||
]
|
|
||||||
@ -1,3 +0,0 @@
|
|||||||
from django.test import TestCase
|
|
||||||
|
|
||||||
# Create your tests here.
|
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -1,22 +0,0 @@
|
|||||||
#!/usr/bin/env python
|
|
||||||
"""Django's command-line utility for administrative tasks."""
|
|
||||||
import os
|
|
||||||
import sys
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
|
||||||
"""Run administrative tasks."""
|
|
||||||
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'mysite.settings')
|
|
||||||
try:
|
|
||||||
from django.core.management import execute_from_command_line
|
|
||||||
except ImportError as exc:
|
|
||||||
raise ImportError(
|
|
||||||
"Couldn't import Django. Are you sure it's installed and "
|
|
||||||
"available on your PYTHONPATH environment variable? Did you "
|
|
||||||
"forget to activate a virtual environment?"
|
|
||||||
) from exc
|
|
||||||
execute_from_command_line(sys.argv)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
main()
|
|
||||||
@ -1,16 +0,0 @@
|
|||||||
"""
|
|
||||||
ASGI config for mysite project.
|
|
||||||
|
|
||||||
It exposes the ASGI callable as a module-level variable named ``application``.
|
|
||||||
|
|
||||||
For more information on this file, see
|
|
||||||
https://docs.djangoproject.com/en/5.2/howto/deployment/asgi/
|
|
||||||
"""
|
|
||||||
|
|
||||||
import os
|
|
||||||
|
|
||||||
from django.core.asgi import get_asgi_application
|
|
||||||
|
|
||||||
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'mysite.settings')
|
|
||||||
|
|
||||||
application = get_asgi_application()
|
|
||||||
@ -1,15 +0,0 @@
|
|||||||
from django.conf import settings
|
|
||||||
from django.conf.urls.static import static
|
|
||||||
from django.contrib import admin
|
|
||||||
from django.urls import include, path
|
|
||||||
|
|
||||||
urlpatterns = [
|
|
||||||
path('admin/', admin.site.urls),
|
|
||||||
path('', include('blog.urls')),
|
|
||||||
path('accounts/', include('accounts.urls')),
|
|
||||||
path('comments/', include('comments.urls', namespace='comments')),
|
|
||||||
]
|
|
||||||
|
|
||||||
# 开发环境服务媒体文件
|
|
||||||
if settings.DEBUG:
|
|
||||||
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
|
|
||||||
@ -1,16 +0,0 @@
|
|||||||
"""
|
|
||||||
WSGI config for mysite project.
|
|
||||||
|
|
||||||
It exposes the WSGI callable as a module-level variable named ``application``.
|
|
||||||
|
|
||||||
For more information on this file, see
|
|
||||||
https://docs.djangoproject.com/en/5.2/howto/deployment/wsgi/
|
|
||||||
"""
|
|
||||||
|
|
||||||
import os
|
|
||||||
|
|
||||||
from django.core.wsgi import get_wsgi_application
|
|
||||||
|
|
||||||
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'mysite.settings')
|
|
||||||
|
|
||||||
application = get_wsgi_application()
|
|
||||||
Binary file not shown.
@ -0,0 +1 @@
|
|||||||
|
Subproject commit 76918f2c7f4bd4db4ad3877be1ee20c256446d21
|
||||||
@ -1,714 +0,0 @@
|
|||||||
/* 南京非遗主题自定义样式 */
|
|
||||||
:root {
|
|
||||||
--nj-red: #c62f2f;
|
|
||||||
--nj-gold: #d4af37;
|
|
||||||
--nj-brown: #8b4513;
|
|
||||||
--nj-light-brown: #a0522d;
|
|
||||||
|
|
||||||
--primary-color: var(--nj-brown);
|
|
||||||
--secondary-color: var(--nj-light-brown);
|
|
||||||
--accent-color: var(--nj-gold);
|
|
||||||
--text-dark: #2c1810;
|
|
||||||
--text-light: #5d4037;
|
|
||||||
--bg-light: #faf3e8;
|
|
||||||
--bg-white: #fffaf0;
|
|
||||||
--border-color: #e8d5c4;
|
|
||||||
}
|
|
||||||
|
|
||||||
* {
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
box-sizing: border-box;
|
|
||||||
}
|
|
||||||
|
|
||||||
body {
|
|
||||||
font-family: "Noto Serif SC", "Source Han Serif SC", "Microsoft YaHei", serif;
|
|
||||||
line-height: 1.7;
|
|
||||||
color: var(--text-dark);
|
|
||||||
background: linear-gradient(135deg, #faf3e8 0%, #fffaf0 100%);
|
|
||||||
min-height: 100vh;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 头部样式 - 中国风设计 */
|
|
||||||
.header {
|
|
||||||
background: linear-gradient(135deg, var(--primary-color) 0%, var(--secondary-color) 100%);
|
|
||||||
padding: 0;
|
|
||||||
box-shadow: 0 4px 20px rgba(139, 69, 19, 0.3);
|
|
||||||
position: relative;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
.header::before {
|
|
||||||
content: "";
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
height: 4px;
|
|
||||||
background: linear-gradient(90deg, var(--nj-gold), var(--nj-red), var(--nj-gold));
|
|
||||||
animation: shimmer 3s infinite;
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes shimmer {
|
|
||||||
0% { background-position: -200% 0; }
|
|
||||||
100% { background-position: 200% 0; }
|
|
||||||
}
|
|
||||||
|
|
||||||
.nav-container {
|
|
||||||
max-width: 1400px;
|
|
||||||
margin: 0 auto;
|
|
||||||
padding: 0 30px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.site-brand {
|
|
||||||
text-align: center;
|
|
||||||
padding: 20px 0;
|
|
||||||
border-bottom: 1px solid rgba(255,255,255,0.2);
|
|
||||||
}
|
|
||||||
|
|
||||||
.site-title {
|
|
||||||
color: #fff;
|
|
||||||
font-size: 2.5em;
|
|
||||||
font-weight: 700;
|
|
||||||
text-shadow: 2px 2px 4px rgba(0,0,0,0.3);
|
|
||||||
letter-spacing: 4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.site-subtitle {
|
|
||||||
color: var(--nj-gold);
|
|
||||||
font-size: 1.1em;
|
|
||||||
margin-top: 8px;
|
|
||||||
font-style: italic;
|
|
||||||
}
|
|
||||||
|
|
||||||
.main-nav {
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
list-style: none;
|
|
||||||
padding: 15px 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.main-nav a {
|
|
||||||
display: block;
|
|
||||||
padding: 12px 25px;
|
|
||||||
color: #fff;
|
|
||||||
text-decoration: none;
|
|
||||||
font-size: 16px;
|
|
||||||
font-weight: 500;
|
|
||||||
transition: all 0.3s ease;
|
|
||||||
border-radius: 25px;
|
|
||||||
margin: 0 8px;
|
|
||||||
position: relative;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
.main-nav a::before {
|
|
||||||
content: "";
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
left: -100%;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
background: linear-gradient(90deg, transparent, rgba(255,255,255,0.2), transparent);
|
|
||||||
transition: left 0.5s;
|
|
||||||
}
|
|
||||||
|
|
||||||
.main-nav a:hover::before {
|
|
||||||
left: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.main-nav a:hover {
|
|
||||||
background: rgba(255,255,255,0.1);
|
|
||||||
transform: translateY(-2px);
|
|
||||||
box-shadow: 0 5px 15px rgba(0,0,0,0.2);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 当前分类高亮样式 */
|
|
||||||
.current-category {
|
|
||||||
background: rgba(255, 255, 255, 0.25);
|
|
||||||
border: 2px solid var(--nj-gold);
|
|
||||||
font-weight: 600;
|
|
||||||
transform: translateY(-2px);
|
|
||||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 搜索框样式 */
|
|
||||||
.search-box {
|
|
||||||
text-align: center;
|
|
||||||
padding: 15px 0;
|
|
||||||
border-top: 1px solid rgba(255,255,255,0.2);
|
|
||||||
}
|
|
||||||
|
|
||||||
.search-box form {
|
|
||||||
display: inline-flex;
|
|
||||||
max-width: 500px;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.search-box input {
|
|
||||||
flex: 1;
|
|
||||||
padding: 12px 20px;
|
|
||||||
border: none;
|
|
||||||
border-radius: 25px 0 0 25px;
|
|
||||||
font-size: 14px;
|
|
||||||
outline: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.search-box button {
|
|
||||||
background: var(--nj-gold);
|
|
||||||
color: var(--text-dark);
|
|
||||||
border: none;
|
|
||||||
padding: 12px 25px;
|
|
||||||
border-radius: 0 25px 25px 0;
|
|
||||||
cursor: pointer;
|
|
||||||
font-weight: 600;
|
|
||||||
transition: background 0.3s;
|
|
||||||
}
|
|
||||||
|
|
||||||
.search-box button:hover {
|
|
||||||
background: #ffd700;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 主要内容布局 */
|
|
||||||
.main-container {
|
|
||||||
max-width: 1400px;
|
|
||||||
margin: 40px auto;
|
|
||||||
padding: 0 30px;
|
|
||||||
display: grid;
|
|
||||||
grid-template-columns: 1fr 350px;
|
|
||||||
gap: 40px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 文章内容区域 */
|
|
||||||
.content {
|
|
||||||
background: var(--bg-white);
|
|
||||||
border-radius: 15px;
|
|
||||||
box-shadow: 0 10px 30px rgba(139, 69, 19, 0.1);
|
|
||||||
overflow: hidden;
|
|
||||||
border: 1px solid var(--border-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
.content-header {
|
|
||||||
background: linear-gradient(135deg, var(--primary-color), var(--secondary-color));
|
|
||||||
color: white;
|
|
||||||
padding: 25px 30px;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 分类标签样式 */
|
|
||||||
.category-label {
|
|
||||||
display: inline-block;
|
|
||||||
background: rgba(255, 255, 255, 0.2);
|
|
||||||
padding: 6px 16px;
|
|
||||||
border-radius: 20px;
|
|
||||||
font-size: 0.9em;
|
|
||||||
margin-bottom: 12px;
|
|
||||||
border: 1px solid var(--nj-gold);
|
|
||||||
font-weight: 500;
|
|
||||||
}
|
|
||||||
|
|
||||||
.content-header h1 {
|
|
||||||
font-size: 2em;
|
|
||||||
margin-bottom: 8px;
|
|
||||||
text-shadow: 1px 1px 3px rgba(0,0,0,0.3);
|
|
||||||
}
|
|
||||||
|
|
||||||
.content-intro {
|
|
||||||
font-size: 1.1em;
|
|
||||||
opacity: 0.95;
|
|
||||||
max-width: 800px;
|
|
||||||
margin: 0 auto;
|
|
||||||
line-height: 1.6;
|
|
||||||
padding: 0 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.posts-list {
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 文章项样式 */
|
|
||||||
.post-item {
|
|
||||||
padding: 35px 30px;
|
|
||||||
border-bottom: 1px solid var(--border-color);
|
|
||||||
transition: all 0.3s ease;
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
|
|
||||||
.post-item::before {
|
|
||||||
content: "";
|
|
||||||
position: absolute;
|
|
||||||
left: 0;
|
|
||||||
top: 0;
|
|
||||||
bottom: 0;
|
|
||||||
width: 4px;
|
|
||||||
background: linear-gradient(to bottom, var(--primary-color), var(--accent-color));
|
|
||||||
opacity: 0;
|
|
||||||
transition: opacity 0.3s ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
.post-item:hover {
|
|
||||||
background: linear-gradient(135deg, #fffaf0, #faf3e8);
|
|
||||||
transform: translateX(5px);
|
|
||||||
}
|
|
||||||
|
|
||||||
.post-item:hover::before {
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.post-item:last-child {
|
|
||||||
border-bottom: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.post-title {
|
|
||||||
margin-bottom: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.post-title a {
|
|
||||||
color: var(--text-dark);
|
|
||||||
text-decoration: none;
|
|
||||||
font-size: 1.8em;
|
|
||||||
font-weight: 600;
|
|
||||||
line-height: 1.3;
|
|
||||||
transition: color 0.3s ease;
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
|
|
||||||
.post-title a:hover {
|
|
||||||
color: var(--primary-color);
|
|
||||||
text-decoration: underline;
|
|
||||||
text-underline-offset: 3px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.post-meta {
|
|
||||||
color: var(--text-light);
|
|
||||||
font-size: 14px;
|
|
||||||
margin-bottom: 20px;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 25px;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
}
|
|
||||||
|
|
||||||
.post-meta span {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 6px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.post-meta i {
|
|
||||||
font-style: normal;
|
|
||||||
opacity: 0.7;
|
|
||||||
}
|
|
||||||
|
|
||||||
.post-content {
|
|
||||||
color: var(--text-light);
|
|
||||||
font-size: 16px;
|
|
||||||
line-height: 1.8;
|
|
||||||
margin-bottom: 25px;
|
|
||||||
text-align: justify;
|
|
||||||
}
|
|
||||||
|
|
||||||
.read-more {
|
|
||||||
color: var(--primary-color);
|
|
||||||
text-decoration: none;
|
|
||||||
font-weight: 600;
|
|
||||||
font-size: 15px;
|
|
||||||
display: inline-flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 8px;
|
|
||||||
transition: all 0.3s ease;
|
|
||||||
padding: 8px 16px;
|
|
||||||
border: 2px solid var(--primary-color);
|
|
||||||
border-radius: 25px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.read-more:hover {
|
|
||||||
background: var(--primary-color);
|
|
||||||
color: white;
|
|
||||||
transform: translateX(5px);
|
|
||||||
}
|
|
||||||
|
|
||||||
.post-footer {
|
|
||||||
color: var(--text-light);
|
|
||||||
font-size: 13px;
|
|
||||||
margin-top: 25px;
|
|
||||||
padding-top: 20px;
|
|
||||||
border-top: 1px dashed var(--border-color);
|
|
||||||
display: flex;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
gap: 15px;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.post-tags {
|
|
||||||
display: flex;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
gap: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tag {
|
|
||||||
background: linear-gradient(135deg, var(--accent-color), var(--secondary-color));
|
|
||||||
color: white;
|
|
||||||
padding: 4px 12px;
|
|
||||||
border-radius: 15px;
|
|
||||||
font-size: 12px;
|
|
||||||
font-weight: 500;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 非遗徽章样式 */
|
|
||||||
.heritage-badge {
|
|
||||||
background: linear-gradient(135deg, var(--nj-red), var(--nj-brown));
|
|
||||||
color: white;
|
|
||||||
padding: 4px 12px;
|
|
||||||
border-radius: 20px;
|
|
||||||
font-size: 12px;
|
|
||||||
font-weight: bold;
|
|
||||||
border: 1px solid var(--nj-gold);
|
|
||||||
}
|
|
||||||
|
|
||||||
.heritage-item {
|
|
||||||
border-left: 3px solid var(--nj-red);
|
|
||||||
background: linear-gradient(135deg, #fffaf0, #faf3e8);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 侧边栏样式 */
|
|
||||||
.sidebar {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: 25px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sidebar-widget {
|
|
||||||
background: var(--bg-white);
|
|
||||||
border-radius: 15px;
|
|
||||||
box-shadow: 0 8px 25px rgba(139, 69, 19, 0.1);
|
|
||||||
padding: 25px;
|
|
||||||
border: 1px solid var(--border-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
.widget-title {
|
|
||||||
color: var(--primary-color);
|
|
||||||
font-size: 1.3em;
|
|
||||||
font-weight: 600;
|
|
||||||
margin-bottom: 20px;
|
|
||||||
padding-bottom: 12px;
|
|
||||||
border-bottom: 2px solid var(--accent-color);
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.widget-title::before {
|
|
||||||
content: "◆";
|
|
||||||
color: var(--secondary-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
.views-list {
|
|
||||||
list-style: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.views-list li {
|
|
||||||
padding: 12px 0;
|
|
||||||
border-bottom: 1px solid var(--border-color);
|
|
||||||
color: var(--text-light);
|
|
||||||
font-size: 14px;
|
|
||||||
transition: all 0.3s ease;
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.views-list li:hover {
|
|
||||||
color: var(--primary-color);
|
|
||||||
transform: translateX(5px);
|
|
||||||
}
|
|
||||||
|
|
||||||
.views-list li:last-child {
|
|
||||||
border-bottom: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.view-count {
|
|
||||||
background: var(--accent-color);
|
|
||||||
color: white;
|
|
||||||
padding: 2px 8px;
|
|
||||||
border-radius: 10px;
|
|
||||||
font-size: 12px;
|
|
||||||
font-weight: 500;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 分类标签云 */
|
|
||||||
.tag-cloud {
|
|
||||||
display: flex;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
gap: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cloud-tag {
|
|
||||||
background: linear-gradient(135deg, #e8d5c4, #d7ccc8);
|
|
||||||
color: var(--text-light);
|
|
||||||
padding: 6px 12px;
|
|
||||||
border-radius: 15px;
|
|
||||||
font-size: 12px;
|
|
||||||
text-decoration: none;
|
|
||||||
transition: all 0.3s ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cloud-tag:hover {
|
|
||||||
background: linear-gradient(135deg, var(--accent-color), var(--secondary-color));
|
|
||||||
color: white;
|
|
||||||
transform: translateY(-2px);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 文章详情页特定样式 */
|
|
||||||
.post-detail {
|
|
||||||
padding: 40px;
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.post-header {
|
|
||||||
text-align: center;
|
|
||||||
margin-bottom: 40px;
|
|
||||||
border-bottom: 2px solid var(--border-color);
|
|
||||||
padding-bottom: 30px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.post-detail .post-title {
|
|
||||||
font-size: 2.2em;
|
|
||||||
color: var(--text-dark);
|
|
||||||
margin-bottom: 20px;
|
|
||||||
line-height: 1.3;
|
|
||||||
}
|
|
||||||
|
|
||||||
.post-detail .post-meta {
|
|
||||||
color: var(--text-light);
|
|
||||||
font-size: 14px;
|
|
||||||
margin-bottom: 20px;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 25px;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.post-image img {
|
|
||||||
max-width: 100%;
|
|
||||||
height: auto;
|
|
||||||
border-radius: 10px;
|
|
||||||
margin-top: 20px;
|
|
||||||
box-shadow: 0 5px 15px rgba(0,0,0,0.1);
|
|
||||||
}
|
|
||||||
|
|
||||||
.post-detail .post-content {
|
|
||||||
font-size: 16px;
|
|
||||||
line-height: 1.8;
|
|
||||||
color: var(--text-light);
|
|
||||||
margin-bottom: 30px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.post-detail .post-content p {
|
|
||||||
margin-bottom: 20px;
|
|
||||||
text-align: justify;
|
|
||||||
}
|
|
||||||
|
|
||||||
.post-detail .post-footer {
|
|
||||||
color: var(--text-light);
|
|
||||||
font-size: 13px;
|
|
||||||
margin-top: 25px;
|
|
||||||
padding-top: 20px;
|
|
||||||
border-top: 1px dashed var(--border-color);
|
|
||||||
display: flex;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
gap: 15px;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 评论区域样式 */
|
|
||||||
.comments-section {
|
|
||||||
margin-top: 50px;
|
|
||||||
padding-top: 30px;
|
|
||||||
border-top: 2px solid var(--border-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
.comment-form {
|
|
||||||
margin-bottom: 30px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.comment-form textarea {
|
|
||||||
width: 100%;
|
|
||||||
padding: 15px;
|
|
||||||
border: 1px solid var(--border-color);
|
|
||||||
border-radius: 8px;
|
|
||||||
resize: vertical;
|
|
||||||
min-height: 100px;
|
|
||||||
font-family: inherit;
|
|
||||||
font-size: 14px;
|
|
||||||
transition: border-color 0.3s;
|
|
||||||
}
|
|
||||||
|
|
||||||
.comment-form textarea:focus {
|
|
||||||
outline: none;
|
|
||||||
border-color: var(--primary-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
.submit-btn {
|
|
||||||
background: var(--primary-color);
|
|
||||||
color: white;
|
|
||||||
border: none;
|
|
||||||
padding: 12px 30px;
|
|
||||||
border-radius: 25px;
|
|
||||||
cursor: pointer;
|
|
||||||
margin-top: 15px;
|
|
||||||
font-weight: 600;
|
|
||||||
transition: all 0.3s;
|
|
||||||
}
|
|
||||||
|
|
||||||
.submit-btn:hover {
|
|
||||||
background: var(--secondary-color);
|
|
||||||
transform: translateY(-2px);
|
|
||||||
}
|
|
||||||
|
|
||||||
.comment-item {
|
|
||||||
background: var(--bg-light);
|
|
||||||
padding: 20px;
|
|
||||||
border-radius: 8px;
|
|
||||||
margin-bottom: 15px;
|
|
||||||
border-left: 4px solid var(--accent-color);
|
|
||||||
transition: transform 0.3s;
|
|
||||||
}
|
|
||||||
|
|
||||||
.comment-item:hover {
|
|
||||||
transform: translateX(5px);
|
|
||||||
}
|
|
||||||
|
|
||||||
.comment-header {
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
margin-bottom: 10px;
|
|
||||||
font-size: 14px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.comment-author {
|
|
||||||
font-weight: 600;
|
|
||||||
color: var(--primary-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
.comment-time {
|
|
||||||
color: var(--text-light);
|
|
||||||
}
|
|
||||||
|
|
||||||
.comment-content {
|
|
||||||
line-height: 1.6;
|
|
||||||
color: var(--text-dark);
|
|
||||||
}
|
|
||||||
|
|
||||||
.no-comments {
|
|
||||||
text-align: center;
|
|
||||||
color: var(--text-light);
|
|
||||||
padding: 40px;
|
|
||||||
font-style: italic;
|
|
||||||
background: var(--bg-light);
|
|
||||||
border-radius: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.login-prompt {
|
|
||||||
text-align: center;
|
|
||||||
padding: 20px;
|
|
||||||
background: var(--bg-light);
|
|
||||||
border-radius: 8px;
|
|
||||||
margin-bottom: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.login-prompt a {
|
|
||||||
color: var(--primary-color);
|
|
||||||
text-decoration: none;
|
|
||||||
font-weight: 600;
|
|
||||||
}
|
|
||||||
|
|
||||||
.login-prompt a:hover {
|
|
||||||
text-decoration: underline;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 响应式设计 */
|
|
||||||
@media (max-width: 1024px) {
|
|
||||||
.main-container {
|
|
||||||
grid-template-columns: 1fr;
|
|
||||||
gap: 25px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sidebar {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: 768px) {
|
|
||||||
.nav-container {
|
|
||||||
padding: 0 15px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.main-nav {
|
|
||||||
flex-wrap: wrap;
|
|
||||||
}
|
|
||||||
|
|
||||||
.main-nav a {
|
|
||||||
padding: 10px 15px;
|
|
||||||
font-size: 14px;
|
|
||||||
margin: 4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.site-title {
|
|
||||||
font-size: 2em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.main-container {
|
|
||||||
padding: 0 15px;
|
|
||||||
margin: 20px auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.post-item {
|
|
||||||
padding: 25px 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.post-title a {
|
|
||||||
font-size: 1.5em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.content-header {
|
|
||||||
padding: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.content-header h1 {
|
|
||||||
font-size: 1.6em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.post-detail {
|
|
||||||
padding: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.post-detail .post-title {
|
|
||||||
font-size: 1.8em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.post-detail .post-meta {
|
|
||||||
gap: 15px;
|
|
||||||
font-size: 12px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.comment-header {
|
|
||||||
flex-direction: column;
|
|
||||||
gap: 5px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 页脚样式 */
|
|
||||||
.footer {
|
|
||||||
background: linear-gradient(135deg, var(--primary-color), var(--text-dark));
|
|
||||||
color: white;
|
|
||||||
text-align: center;
|
|
||||||
padding: 30px 20px;
|
|
||||||
margin-top: 60px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.footer p {
|
|
||||||
opacity: 0.8;
|
|
||||||
font-size: 14px;
|
|
||||||
}
|
|
||||||
@ -1,283 +0,0 @@
|
|||||||
{% extends 'base.html' %}
|
|
||||||
|
|
||||||
{% block content %}
|
|
||||||
<article class="post-detail heritage-item">
|
|
||||||
<header class="post-header">
|
|
||||||
<h1 class="post-title">{{ post.title }}</h1>
|
|
||||||
<div class="post-meta">
|
|
||||||
<span><i>👁️</i> {{ post.views }} 次阅读</span>
|
|
||||||
<span><i>💬</i> {{ post.comments.count }} 条评论</span>
|
|
||||||
<span><i>📅</i> {{ post.created_time|date:"Y-m-d" }}</span>
|
|
||||||
<span><i>🏷️</i> {{ post.category.name }}</span>
|
|
||||||
<span><i>✍️</i> {{ post.author.username }}</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{% if post.featured_image %}
|
|
||||||
<div class="post-image">
|
|
||||||
<img src="{{ post.featured_image.url }}" alt="{{ post.title }}" loading="lazy">
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
<!-- 非遗徽章 -->
|
|
||||||
<div class="post-tags" style="margin-top: 15px;">
|
|
||||||
<span class="heritage-badge">非遗传承</span>
|
|
||||||
{% for tag in post.primary_tags.all %}
|
|
||||||
<span class="tag">{{ tag.name }}</span>
|
|
||||||
{% endfor %}
|
|
||||||
</div>
|
|
||||||
</header>
|
|
||||||
|
|
||||||
<div class="post-content">
|
|
||||||
{{ post.content|linebreaks }}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="post-footer">
|
|
||||||
<div class="post-tags">
|
|
||||||
<span class="heritage-badge">{{ post.category.name }}</span>
|
|
||||||
{% for tag in post.primary_tags.all %}
|
|
||||||
<span class="tag">{{ tag.name }}</span>
|
|
||||||
{% endfor %}
|
|
||||||
{% for tag in post.secondary_tags.all %}
|
|
||||||
<span class="tag">#{{ tag.name }}</span>
|
|
||||||
{% endfor %}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</article>
|
|
||||||
|
|
||||||
<!-- 评论区域 -->
|
|
||||||
<section class="comments-section">
|
|
||||||
<h3 style="color: var(--primary-color); margin-bottom: 25px; font-size: 1.5em;">
|
|
||||||
<i>💬</i> 评论 ({{ comments.count }})
|
|
||||||
</h3>
|
|
||||||
|
|
||||||
<!-- 评论表单 -->
|
|
||||||
{% if user.is_authenticated %}
|
|
||||||
<form method="post" action="{% url 'comments:add_comment' post.id %}" class="comment-form">
|
|
||||||
{% csrf_token %}
|
|
||||||
<div class="form-group">
|
|
||||||
{{ comment_form.content }}
|
|
||||||
</div>
|
|
||||||
<button type="submit" class="submit-btn">发表评论</button>
|
|
||||||
</form>
|
|
||||||
{% else %}
|
|
||||||
<div class="login-prompt">
|
|
||||||
<p>请 <a href="{% url 'login' %}?next={{ request.path }}">登录</a> 后发表评论,或 <a href="{% url 'register' %}?next={{ request.path }}">注册</a> 账号</p>
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
<!-- 评论列表 -->
|
|
||||||
<div class="comments-list">
|
|
||||||
{% for comment in comments %}
|
|
||||||
<div class="comment-item">
|
|
||||||
<div class="comment-header">
|
|
||||||
<span class="comment-author">{{ comment.author.username }}</span>
|
|
||||||
<span class="comment-time">{{ comment.created_time|date:"Y-m-d H:i" }}</span>
|
|
||||||
</div>
|
|
||||||
<div class="comment-content">
|
|
||||||
{{ comment.content|linebreaks }}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{% empty %}
|
|
||||||
<div class="no-comments">
|
|
||||||
<p>暂无评论,快来抢沙发吧~</p>
|
|
||||||
</div>
|
|
||||||
{% endfor %}
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<style>
|
|
||||||
.post-detail {
|
|
||||||
padding: 40px;
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.post-header {
|
|
||||||
text-align: center;
|
|
||||||
margin-bottom: 40px;
|
|
||||||
border-bottom: 2px solid var(--border-color);
|
|
||||||
padding-bottom: 30px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.post-title {
|
|
||||||
font-size: 2.2em;
|
|
||||||
color: var(--text-dark);
|
|
||||||
margin-bottom: 20px;
|
|
||||||
line-height: 1.3;
|
|
||||||
}
|
|
||||||
|
|
||||||
.post-meta {
|
|
||||||
color: var(--text-light);
|
|
||||||
font-size: 14px;
|
|
||||||
margin-bottom: 20px;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 25px;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.post-meta span {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 6px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.post-image img {
|
|
||||||
max-width: 100%;
|
|
||||||
height: auto;
|
|
||||||
border-radius: 10px;
|
|
||||||
margin-top: 20px;
|
|
||||||
box-shadow: 0 5px 15px rgba(0,0,0,0.1);
|
|
||||||
}
|
|
||||||
|
|
||||||
.post-content {
|
|
||||||
font-size: 16px;
|
|
||||||
line-height: 1.8;
|
|
||||||
color: var(--text-light);
|
|
||||||
margin-bottom: 30px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.post-content p {
|
|
||||||
margin-bottom: 20px;
|
|
||||||
text-align: justify;
|
|
||||||
}
|
|
||||||
|
|
||||||
.post-footer {
|
|
||||||
color: var(--text-light);
|
|
||||||
font-size: 13px;
|
|
||||||
margin-top: 25px;
|
|
||||||
padding-top: 20px;
|
|
||||||
border-top: 1px dashed var(--border-color);
|
|
||||||
display: flex;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
gap: 15px;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.comments-section {
|
|
||||||
margin-top: 50px;
|
|
||||||
padding-top: 30px;
|
|
||||||
border-top: 2px solid var(--border-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
.comment-form {
|
|
||||||
margin-bottom: 30px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.comment-form textarea {
|
|
||||||
width: 100%;
|
|
||||||
padding: 15px;
|
|
||||||
border: 1px solid var(--border-color);
|
|
||||||
border-radius: 8px;
|
|
||||||
resize: vertical;
|
|
||||||
min-height: 100px;
|
|
||||||
font-family: inherit;
|
|
||||||
font-size: 14px;
|
|
||||||
transition: border-color 0.3s;
|
|
||||||
}
|
|
||||||
|
|
||||||
.comment-form textarea:focus {
|
|
||||||
outline: none;
|
|
||||||
border-color: var(--primary-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
.submit-btn {
|
|
||||||
background: var(--primary-color);
|
|
||||||
color: white;
|
|
||||||
border: none;
|
|
||||||
padding: 12px 30px;
|
|
||||||
border-radius: 25px;
|
|
||||||
cursor: pointer;
|
|
||||||
margin-top: 15px;
|
|
||||||
font-weight: 600;
|
|
||||||
transition: all 0.3s;
|
|
||||||
}
|
|
||||||
|
|
||||||
.submit-btn:hover {
|
|
||||||
background: var(--secondary-color);
|
|
||||||
transform: translateY(-2px);
|
|
||||||
}
|
|
||||||
|
|
||||||
.comment-item {
|
|
||||||
background: var(--bg-light);
|
|
||||||
padding: 20px;
|
|
||||||
border-radius: 8px;
|
|
||||||
margin-bottom: 15px;
|
|
||||||
border-left: 4px solid var(--accent-color);
|
|
||||||
transition: transform 0.3s;
|
|
||||||
}
|
|
||||||
|
|
||||||
.comment-item:hover {
|
|
||||||
transform: translateX(5px);
|
|
||||||
}
|
|
||||||
|
|
||||||
.comment-header {
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
margin-bottom: 10px;
|
|
||||||
font-size: 14px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.comment-author {
|
|
||||||
font-weight: 600;
|
|
||||||
color: var(--primary-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
.comment-time {
|
|
||||||
color: var(--text-light);
|
|
||||||
}
|
|
||||||
|
|
||||||
.comment-content {
|
|
||||||
line-height: 1.6;
|
|
||||||
color: var(--text-dark);
|
|
||||||
}
|
|
||||||
|
|
||||||
.no-comments {
|
|
||||||
text-align: center;
|
|
||||||
color: var(--text-light);
|
|
||||||
padding: 40px;
|
|
||||||
font-style: italic;
|
|
||||||
background: var(--bg-light);
|
|
||||||
border-radius: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.login-prompt {
|
|
||||||
text-align: center;
|
|
||||||
padding: 20px;
|
|
||||||
background: var(--bg-light);
|
|
||||||
border-radius: 8px;
|
|
||||||
margin-bottom: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.login-prompt a {
|
|
||||||
color: var(--primary-color);
|
|
||||||
text-decoration: none;
|
|
||||||
font-weight: 600;
|
|
||||||
}
|
|
||||||
|
|
||||||
.login-prompt a:hover {
|
|
||||||
text-decoration: underline;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 响应式设计 */
|
|
||||||
@media (max-width: 768px) {
|
|
||||||
.post-detail {
|
|
||||||
padding: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.post-title {
|
|
||||||
font-size: 1.8em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.post-meta {
|
|
||||||
gap: 15px;
|
|
||||||
font-size: 12px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.comment-header {
|
|
||||||
flex-direction: column;
|
|
||||||
gap: 5px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
{% endblock %}
|
|
||||||
@ -1,70 +0,0 @@
|
|||||||
{% extends 'base.html' %}
|
|
||||||
|
|
||||||
{% block content %}
|
|
||||||
{% for post in post_list %}
|
|
||||||
<article class="post-item">
|
|
||||||
<div class="post-title">
|
|
||||||
<h2><a href="{% url 'detail' post.id %}">{{ post.title }}</a></h2>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="post-meta">
|
|
||||||
<span><i>👁️</i> {{ post.views }} 次阅读</span>
|
|
||||||
<span><i>💬</i> {{ post.comments.count }} 条评论</span>
|
|
||||||
<span><i>📅</i> {{ post.created_time|date:"Y-m-d" }}</span>
|
|
||||||
<!-- 显示文章所属分类 -->
|
|
||||||
<span><i>🏷️</i> {{ post.category.name }}</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="post-content">
|
|
||||||
{{ post.excerpt|default:post.content|truncatewords:35 }}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<a href="{% url 'detail' post.id %}" class="read-more">
|
|
||||||
阅读全文 →
|
|
||||||
</a>
|
|
||||||
|
|
||||||
<div class="post-footer">
|
|
||||||
<div class="post-tags">
|
|
||||||
<span class="tag">{{ post.category.name }}</span>
|
|
||||||
{% for tag in post.primary_tags.all %}
|
|
||||||
<span class="tag">{{ tag.name }}</span>
|
|
||||||
{% endfor %}
|
|
||||||
{% for tag in post.secondary_tags.all|slice:":3" %}
|
|
||||||
<span class="tag">#{{ tag.name }}</span>
|
|
||||||
{% endfor %}
|
|
||||||
</div>
|
|
||||||
<div class="post-author">
|
|
||||||
作者:{{ post.author.username }}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</article>
|
|
||||||
{% empty %}
|
|
||||||
<div class="post-item" style="text-align: center; padding: 60px 30px;">
|
|
||||||
<h3 style="color: var(--text-light); margin-bottom: 15px;">
|
|
||||||
{% if current_category_id %}
|
|
||||||
该分类下暂无文章
|
|
||||||
{% else %}
|
|
||||||
暂无文章
|
|
||||||
{% endif %}
|
|
||||||
</h3>
|
|
||||||
<p style="color: var(--text-light);">欢迎关注南京非物质文化遗产,精彩内容即将呈现...</p>
|
|
||||||
</div>
|
|
||||||
{% endfor %}
|
|
||||||
|
|
||||||
<!-- 分页功能(可选) -->
|
|
||||||
{% if post_list.has_other_pages %}
|
|
||||||
<div class="pagination" style="padding: 30px; text-align: center;">
|
|
||||||
{% if post_list.has_previous %}
|
|
||||||
<a href="?page={{ post_list.previous_page_number }}" class="read-more">上一页</a>
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
<span style="margin: 0 20px; color: var(--text-light);">
|
|
||||||
第 {{ post_list.number }} 页,共 {{ post_list.paginator.num_pages }} 页
|
|
||||||
</span>
|
|
||||||
|
|
||||||
{% if post_list.has_next %}
|
|
||||||
<a href="?page={{ post_list.next_page_number }}" class="read-more">下一页</a>
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
{% endblock %}
|
|
||||||
Loading…
Reference in new issue