做苗木免费网站/神起网络游戏推广平台

做苗木免费网站,神起网络游戏推广平台,cumfoot wordpress,同江佳木斯网站建设快速搭建一个漂亮的AI家庭网站-相册/时间线/日历/多用户/个性化配色(中) 摘要 在上一篇文章中,我们介绍了如何搭建一个基础的家庭网站(V1.0版本),包含了用户管理、相册管理、时间线和日历等功能。本文将继续深入,详细…

快速搭建一个漂亮的AI家庭网站-相册/时间线/日历/多用户/个性化配色(中)

摘要

在上一篇文章中,我们介绍了如何搭建一个基础的家庭网站(V1.0版本),包含了用户管理、相册管理、时间线和日历等功能。本文将继续深入,详细介绍V1.1和V1.2版本中新增的功能,主要包括博客系统的实现、富文本编辑器的集成、评论和点赞功能,以及日程管理系统的优化。通过这些功能的添加,我们的家庭网站将更加完善,为家庭成员提供更丰富的交流和记录方式。

代码仓 https://github.com/wyg5208/family_website_V1_2
在这里插入图片描述

一、V1.1版本新增功能

1. 博客系统

V1.1版本最重要的更新是添加了完整的博客系统,让家庭成员可以发表文章,分享生活点滴和心得体会。

1.1 数据模型设计

首先,我们需要设计博客相关的数据模型,包括文章、评论和点赞:

# app/models.pyclass Post(db.Model):id = db.Column(db.Integer, primary_key=True)title = db.Column(db.String(100), nullable=False)content = db.Column(db.Text, nullable=False)summary = db.Column(db.String(200))cover_image = db.Column(db.String(120))created_at = db.Column(db.DateTime, default=datetime.utcnow)updated_at = db.Column(db.DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)user_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False)# 关系user = db.relationship('User', backref=db.backref('posts', lazy=True))comments = db.relationship('Comment', backref='post', lazy=True, cascade='all, delete-orphan')likes = db.relationship('Like', backref='post', lazy=True, cascade='all, delete-orphan')def __repr__(self):return f'<Post {self.title}>'@propertydef comment_count(self):return len(self.comments)@propertydef like_count(self):return len(self.likes)class Comment(db.Model):id = db.Column(db.Integer, primary_key=True)content = db.Column(db.Text, nullable=False)created_at = db.Column(db.DateTime, default=datetime.utcnow)user_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False)post_id = db.Column(db.Integer, db.ForeignKey('post.id'), nullable=False)parent_id = db.Column(db.Integer, db.ForeignKey('comment.id'))# 关系user = db.relationship('User', backref=db.backref('comments', lazy=True))replies = db.relationship('Comment', backref=db.backref('parent', remote_side=[id]), lazy=True)def __repr__(self):return f'<Comment {self.id}>'class Like(db.Model):id = db.Column(db.Integer, primary_key=True)created_at = db.Column(db.DateTime, default=datetime.utcnow)user_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False)post_id = db.Column(db.Integer, db.ForeignKey('post.id'), nullable=False)# 关系user = db.relationship('User', backref=db.backref('likes', lazy=True))__table_args__ = (db.UniqueConstraint('user_id', 'post_id', name='unique_user_post_like'),)def __repr__(self):return f'<Like {self.id}>'
1.2 博客首页实现

博客首页展示最新文章列表,包括文章标题、摘要、作者和发布时间等信息:

<!-- app/templates/blog/index.html -->
{% extends 'base.html' %}{% block title %}家庭博客 - 家庭网站{% endblock %}{% block content %}
<div class="d-flex justify-content-between align-items-center mb-4"><h1><i class="fas fa-blog me-2"></i>家庭博客</h1>{% if current_user.is_authenticated %}<a href="{{ url_for('blog.create') }}" class="btn btn-primary"><i class="fas fa-pen-to-square me-1"></i>写文章</a>{% endif %}
</div>{% if posts %}
<div class="row row-cols-1 row-cols-md-2 g-4">{% for post in posts %}<div class="col"><div class="card h-100 blog-card">{% if post.cover_image %}<img src="{{ url_for('static', filename='uploads/blog/' + post.cover_image) }}" class="card-img-top" alt="{{ post.title }}">{% endif %}<div class="card-body"><h5 class="card-title">{{ post.title }}</h5><p class="card-text text-muted small"><i class="far fa-user me-1"></i>{{ post.user.username }} | <i class="far fa-calendar me-1"></i>{{ post.created_at.strftime('%Y-%m-%d') }}</p><p class="card-text">{{ post.summary or (post.content|striptags|truncate(150)) }}</p></div><div class="card-footer d-flex justify-content-between align-items-center"><div><span class="badge bg-primary me-1"><i class="far fa-comment me-1"></i>{{ post.comment_count }}</span><span class="badge bg-danger"><i class="far fa-heart me-1"></i>{{ post.like_count }}</span></div><a href="{{ url_for('blog.view', post_id=post.id) }}" class="btn btn-sm btn-outline-primary">阅读全文</a></div></div></div>{% endfor %}
</div><!-- 分页控件 -->
<nav aria-label="Page navigation" class="mt-4"><ul class="pagination justify-content-center">{% if pagination.has_prev %}<li class="page-item"><a class="page-link" href="{{ url_for('blog.index', page=pagination.prev_num) }}" aria-label="Previous"><span aria-hidden="true">&laquo;</span></a></li>{% else %}<li class="page-item disabled"><a class="page-link" href="#" aria-label="Previous"><span aria-hidden="true">&laquo;</span></a></li>{% endif %}{% for page in pagination.iter_pages() %}{% if page %}{% if page != pagination.page %}<li class="page-item"><a class="page-link" href="{{ url_for('blog.index', page=page) }}">{{ page }}</a></li>{% else %}<li class="page-item active"><a class="page-link" href="#">{{ page }}</a></li>{% endif %}{% else %}<li class="page-item disabled"><a class="page-link" href="#">...</a></li>{% endif %}{% endfor %}{% if pagination.has_next %}<li class="page-item"><a class="page-link" href="{{ url_for('blog.index', page=pagination.next_num) }}" aria-label="Next"><span aria-hidden="true">&raquo;</span></a></li>{% else %}<li class="page-item disabled"><a class="page-link" href="#" aria-label="Next"><span aria-hidden="true">&raquo;</span></a></li>{% endif %}</ul>
</nav>
{% else %}
<div class="alert alert-info"><i class="fas fa-info-circle me-2"></i>目前还没有博客文章。{% if current_user.is_authenticated %}<a href="{{ url_for('blog.create') }}" class="alert-link">点击这里</a>创建第一篇文章!{% endif %}
</div>
{% endif %}
{% endblock %}
1.3 集成CKEditor 5富文本编辑器

为了提供更好的写作体验,我们集成了CKEditor 5富文本编辑器:

<!-- app/templates/blog/edit.html -->
{% extends 'base.html' %}{% block title %}{{ '编辑文章' if post else '创建文章' }} - 家庭网站{% endblock %}{% block styles %}
<style>.ck-editor__editable {min-height: 300px;}
</style>
{% endblock %}{% block content %}
<nav aria-label="breadcrumb" class="mb-4"><ol class="breadcrumb"><li class="breadcrumb-item"><a href="{{ url_for('main.index') }}">首页</a></li><li class="breadcrumb-item"><a href="{{ url_for('blog.index') }}">博客</a></li><li class="breadcrumb-item active" aria-current="page">{{ '编辑文章' if post else '创建文章' }}</li></ol>
</nav><div class="card"><div class="card-header"><h2>{{ '编辑文章' if post else '创建文章' }}</h2></div><div class="card-body"><form id="postForm" method="POST" enctype="multipart/form-data">{{ form.hidden_tag() }}<div class="mb-3">{{ form.title.label(class="form-label") }}{{ form.title(class="form-control" + (" is-invalid" if form.title.errors else "")) }}{% for error in form.title.errors %}<div class="invalid-feedback">{{ error }}</div>{% endfor %}</div><div class="mb-3">{{ form.summary.label(class="form-label") }}{{ form.summary(class="form-control" + (" is-invalid" if form.summary.errors else "")) }}{% for error in form.summary.errors %}<div class="invalid-feedback">{{ error }}</div>{% endfor %}<div class="form-text">简短摘要,不超过200字符。如不填写,将自动从内容中提取。</div></div><div class="mb-3">{{ form.cover_image.label(class="form-label") }}{{ form.cover_image(class="form-control" + (" is-invalid" if form.cover_image.errors else "")) }}{% for error in form.cover_image.errors %}<div class="invalid-feedback">{{ error }}</div>{% endfor %}{% if post and post.cover_image %}<div class="mt-2"><img src="{{ url_for('static', filename='uploads/blog/' + post.cover_image) }}" alt="当前封面图" class="img-thumbnail" style="max-height: 100px;"><div class="form-text">当前封面图。上传新图片将替换此图。</div></div>{% endif %}</div><div class="mb-3">{{ form.content.label(class="form-label") }}{{ form.content(class="form-control" + (" is-invalid" if form.content.errors else ""), id="editor") }}{% for error in form.content.errors %}<div class="invalid-feedback">{{ error }}</div>{% endfor %}</div><div class="d-flex justify-content-between"><a href="{{ url_for('blog.view', post_id=post.id) if post else url_for('blog.index') }}" class="btn btn-secondary">取消</a><button type="submit" class="btn btn-primary">保存文章</button></div></form></div>
</div>
{% endblock %}{% block scripts %}
<script src="https://cdn.ckeditor.com/ckeditor5/35.1.0/classic/ckeditor.js"></script>
<script>document.addEventListener('DOMContentLoaded', function() {let editor;ClassicEditor.create(document.querySelector('#editor')).then(newEditor => {editor = newEditor;}).catch(error => {console.error(error);});const form = document.getElementById('postForm');const contentTextarea = document.querySelector('#editor');form.addEventListener('submit', function(e) {e.preventDefault();// 获取CKEditor内容const editorData = editor.getData();// 检查内容是否为空if (!editorData.trim()) {alert('文章内容不能为空!');return;}// 将CKEditor内容设置到原始textareacontentTextarea.value = editorData;// 提交表单this.submit();});});
</script>
{% endblock %}
1.4 评论和回复功能

博客系统支持评论和嵌套回复功能,让家庭成员可以互动交流:

<!-- 评论部分 (app/templates/blog/view.html的一部分) -->
<div class="card mt-4"><div class="card-header"><h3><i class="far fa-comments me-2"></i>评论 ({{ post.comment_count }})</h3></div><div class="card-body">{% if current_user.is_authenticated %}<form method="POST" action="{{ url_for('blog.add_comment', post_id=post.id) }}">{{ comment_form.hidden_tag() }}<div class="mb-3">{{ comment_form.content.label(class="form-label") }}{{ comment_form.content(class="form-control" + (" is-invalid" if comment_form.content.errors else ""), rows=3) }}{% for error in comment_form.content.errors %}<div class="invalid-feedback">{{ error }}</div>{% endfor %}</div><button type="submit" class="btn btn-primary">发表评论</button></form>{% else %}<div class="alert alert-info"><a href="{{ url_for('auth.login', next=request.path) }}">登录</a>后发表评论。</div>{% endif %}<hr><!-- 评论列表 -->{% if post.comments %}<div class="comments-container">{% for comment in top_level_comments %}<div class="comment mb-3"><div class="d-flex"><div class="flex-shrink-0"><img src="{{ url_for('static', filename='img/default-avatar.png') }}" class="rounded-circle" width="50" height="50" alt="{{ comment.user.username }}"></div><div class="flex-grow-1 ms-3"><div class="d-flex justify-content-between"><h5 class="mt-0">{{ comment.user.username }}</h5><small class="text-muted">{{ comment.created_at.strftime('%Y-%m-%d %H:%M') }}</small></div><p>{{ comment.content }}</p>{% if current_user.is_authenticated %}<button class="btn btn-sm btn-outline-secondary reply-btn" data-comment-id="{{ comment.id }}"><i class="fas fa-reply me-1"></i>回复</button><div class="reply-form mt-2 d-none" id="reply-form-{{ comment.id }}"><form method="POST" action="{{ url_for('blog.add_reply', post_id=post.id, comment_id=comment.id) }}">{{ reply_form.hidden_tag() }}<div class="mb-2">{{ reply_form.content(class="form-control", rows=2, placeholder="回复 " + comment.user.username) }}</div><div class="d-flex justify-content-end"><button type="button" class="btn btn-sm btn-secondary me-2 cancel-reply-btn">取消</button><button type="submit" class="btn btn-sm btn-primary">提交回复</button></div></form></div>{% endif %}<!-- 嵌套回复 -->{% if comment.replies %}<div class="replies mt-3">{% for reply in comment.replies %}<div class="reply d-flex mt-2"><div class="flex-shrink-0"><img src="{{ url_for('static', filename='img/default-avatar.png') }}" class="rounded-circle" width="40" height="40" alt="{{ reply.user.username }}"></div><div class="flex-grow-1 ms-2"><div class="d-flex justify-content-between"><h6 class="mt-0">{{ reply.user.username }}</h6><small class="text-muted">{{ reply.created_at.strftime('%Y-%m-%d %H:%M') }}</small></div><p>{{ reply.content }}</p></div></div>{% endfor %}</div>{% endif %}</div></div></div>{% endfor %}</div>{% else %}<div class="alert alert-light">暂无评论,快来发表第一条评论吧!</div>{% endif %}</div>
</div>
1.5 点赞功能

为了增加互动性,我们添加了文章点赞功能:

# app/blog/routes.py@bp.route('/like/<int:post_id>', methods=['POST'])
@login_required
def like_post(post_id):post = Post.query.get_or_404(post_id)# 检查用户是否已经点赞existing_like = Like.query.filter_by(user_id=current_user.id, post_id=post_id).first()if existing_like:# 如果已经点赞,则取消点赞db.session.delete(existing_like)flash('已取消点赞', 'info')else:# 如果未点赞,则添加点赞like = Like(user_id=current_user.id, post_id=post_id)db.session.add(like)flash('点赞成功', 'success')db.session.commit()return redirect(url_for('blog.view', post_id=post_id))
<!-- 点赞按钮 (app/templates/blog/view.html的一部分) -->
<div class="d-flex justify-content-between align-items-center mt-4"><div><span class="badge bg-primary me-2"><i class="far fa-comment me-1"></i>{{ post.comment_count }} 评论</span><span class="badge bg-danger"><i class="far fa-heart me-1"></i>{{ post.like_count }} 点赞</span></div>{% if current_user.is_authenticated %}<form method="POST" action="{{ url_for('blog.like_post', post_id=post.id) }}"><input type="hidden" name="csrf_token" value="{{ csrf_token() }}"><button type="submit" class="btn btn-sm {% if has_liked %}btn-danger{% else %}btn-outline-danger{% endif %}"><i class="{% if has_liked %}fas{% else %}far{% endif %} fa-heart me-1"></i>{% if has_liked %}已点赞{% else %}点赞{% endif %}</button></form>{% endif %}
</div>
1.6 用户博客页面

我们还添加了用户博客页面,展示特定用户发表的所有文章:

<!-- app/templates/blog/user_posts.html -->
{% extends 'base.html' %}{% block title %}{{ user.username }}的博客 - 家庭网站{% endblock %}{% block styles %}
<style>.user-info {background-color: var(--bs-tertiary-bg);border-radius: 10px;padding: 20px;margin-bottom: 30px;}.user-avatar {width: 100px;height: 100px;border-radius: 50%;object-fit: cover;border: 3px solid var(--bs-primary);}.user-stats {display: flex;gap: 20px;margin-top: 15px;}.stat-item {text-align: center;}.stat-value {font-size: 1.5rem;font-weight: bold;color: var(--bs-primary);}.stat-label {font-size: 0.9rem;color: var(--bs-secondary-color);}.blog-card {transition: transform 0.3s ease, box-shadow 0.3s ease;}.blog-card:hover {transform: translateY(-5px);box-shadow: 0 10px 20px rgba(0,0,0,0.1);}.blog-card .card-img-top {height: 180px;object-fit: cover;}
</style>
{% endblock %}{% block content %}
<nav aria-label="breadcrumb" class="mb-4"><ol class="breadcrumb"><li class="breadcrumb-item"><a href="{{ url_for('main.index') }}">首页</a></li><li class="breadcrumb-item"><a href="{{ url_for('blog.index') }}">博客</a></li><li class="breadcrumb-item active" aria-current="page">{{ user.username }}的博客</li></ol>
</nav><!-- 用户信息 -->
<div class="user-info"><div class="d-flex align-items-center"><img src="{{ url_for('static', filename='img/default-avatar.png') }}" alt="{{ user.username }}" class="user-avatar me-4"><div><h2>{{ user.username }}</h2><p class="text-muted">{{ user.email }}</p><div class="user-stats"><div class="stat-item"><div class="stat-value">{{ user.posts|length }}</div><div class="stat-label">文章</div></div><div class="stat-item"><div class="stat-value">{{ total_comments }}</div><div class="stat-label">收到的评论</div></div><div class="stat-item"><div class="stat-value">{{ total_likes }}</div><div class="stat-label">收到的点赞</div></div></div></div></div>
</div><h3 class="mb-4">{{ user.username }}的文章</h3>{% if current_user.is_authenticated and current_user.id == user.id %}
<div class="mb-4"><a href="{{ url_for('blog.create') }}" class="btn btn-primary"><i class="fas fa-pen-to-square me-1"></i>写新文章</a>
</div>
{% endif %}{% if posts %}
<div class="row row-cols-1 row-cols-md-2 g-4">{% for post in posts %}<div class="col"><div class="card h-100 blog-card">{% if post.cover_image %}<img src="{{ url_for('static', filename='uploads/blog/' + post.cover_image) }}" class="card-img-top" alt="{{ post.title }}">{% endif %}<div class="card-body"><h5 class="card-title">{{ post.title }}</h5><p class="card-text text-muted small"><i class="far fa-calendar me-1"></i>{{ post.created_at.strftime('%Y-%m-%d') }}</p><p class="card-text">{{ post.summary or (post.content|striptags|truncate(150)) }}</p></div><div class="card-footer d-flex justify-content-between align-items-center"><div><span class="badge bg-primary me-1"><i class="far fa-comment me-1"></i>{{ post.comment_count }}</span><span class="badge bg-danger"><i class="far fa-heart me-1"></i>{{ post.like_count }}</span></div><a href="{{ url_for('blog.view', post_id=post.id) }}" class="btn btn-sm btn-outline-primary">阅读全文</a></div></div></div>{% endfor %}
</div><!-- 分页控件 -->
<nav aria-label="Page navigation" class="mt-4"><ul class="pagination justify-content-center">{% if pagination.has_prev %}<li class="page-item"><a class="page-link" href="{{ url_for('blog.user_posts', username=user.username, page=pagination.prev_num) }}" aria-label="Previous"><span aria-hidden="true">&laquo;</span></a></li>{% else %}<li class="page-item disabled"><a class="page-link" href="#" aria-label="Previous"><span aria-hidden="true">&laquo;</span></a></li>{% endif %}{% for page in pagination.iter_pages() %}{% if page %}{% if page != pagination.page %}<li class="page-item"><a class="page-link" href="{{ url_for('blog.user_posts', username=user.username, page=page) }}">{{ page }}</a></li>{% else %}<li class="page-item active"><a class="page-link" href="#">{{ page }}</a></li>{% endif %}{% else %}<li class="page-item disabled"><a class="page-link" href="#">...</a></li>{% endif %}{% endfor %}{% if pagination.has_next %}<li class="page-item"><a class="page-link" href="{{ url_for('blog.user_posts', username=user.username, page=pagination.next_num) }}" aria-label="Next"><span aria-hidden="true">&raquo;</span></a></li>{% else %}<li class="page-item disabled"><a class="page-link" href="#" aria-label="Next"><span aria-hidden="true">&raquo;</span></a></li>{% endif %}</ul>
</nav>
{% else %}
<div class="alert alert-info"><i class="fas fa-info-circle me-2"></i>{{ user.username }}还没有发表任何文章。
</div>
{% endif %}
{% endblock %}

二、V1.2版本新增功能

1. 日程管理系统优化

V1.2版本主要对日程管理系统进行了优化,解决了数据模型不一致的问题。

1.1 统一日程模型

在之前的版本中,我们有两个不同的模型(ScheduleCalendarEvent)来表示日程事件,这导致了数据不一致的问题。在V1.2版本中,我们统一使用Schedule模型:

# app/main/routes.py@bp.route('/')
def index():# 获取轮播图carousel_items = CarouselItem.query.order_by(CarouselItem.order).all()# 获取即将到来的事件# 如果用户已登录,只显示当前用户的日程if current_user.is_authenticated:upcoming_events = Schedule.query.filter(Schedule.user_id == current_user.id,Schedule.start_time >= datetime.now()).order_by(Schedule.start_time).limit(3).all()else:upcoming_events = []# 获取最近的时间线事件recent_events = TimelineEvent.query.order_by(TimelineEvent.date.desc()).limit(3).all()# 获取最新博客文章latest_posts = Post.query.order_by(Post.created_at.desc()).limit(3).all()return render_template('main/index.html', carousel_items=carousel_items,upcoming_events=upcoming_events,recent_events=recent_events,latest_posts=latest_posts)
1.2 改进日历视图

在V1.2版本中,我们移除了日历视图的月份限制,现在可以显示所有日程:

# app/calendar/routes.py@bp.route('/')
@login_required
def index():# 获取当前年月year = request.args.get('year', datetime.now().year, type=int)month = request.args.get('month', datetime.now().month, type=int)# 计算当前月的第一天和最后一天first_day = datetime(year, month, 1)# 获取下一个月的第一天if month == 12:next_month_first_day = datetime(year + 1, 1, 1)else:next_month_first_day = datetime(year, month + 1, 1)# 获取当前用户的所有日程,不再限制月份schedules = Schedule.query.filter_by(user_id=current_user.id).order_by(Schedule.start_time).all()# 计算上个月和下个月的链接prev_month = month - 1 if month > 1 else 12prev_year = year if month > 1 else year - 1next_month = month + 1 if month < 12 else 1next_year = year if month < 12 else year + 1# 生成日历数据cal = calendar.monthcalendar(year, month)return render_template('calendar/index.html',year=year,month=month,calendar=cal,schedules=schedules,prev_month=prev_month,prev_year=prev_year,next_month=next_month,next_year=next_year,current_date=datetime.now().date())
1.3 扩展"即将到来的日程"页面时间范围

我们将"即将到来的日程"页面的时间范围从7天扩展到30天,以显示更多的日程:

# app/calendar/routes.py@bp.route('/upcoming')
@login_required
def upcoming():# 获取当前日期和30天后的日期today = datetime.now().date()next_month = today + timedelta(days=30)# 获取未来30天内的日程schedules = Schedule.query.filter(Schedule.user_id == current_user.id,Schedule.start_time >= datetime.combine(today, time.min),Schedule.start_time <= datetime.combine(next_month, time.max)).order_by(Schedule.start_time).all()return render_template('calendar/upcoming.html', schedules=schedules)
1.4 修复jQuery加载问题

在V1.2版本中,我们修复了jQuery加载问题,确保页面正常加载:

<!-- app/templates/base.html -->
<!DOCTYPE html>
<html lang="zh-CN" data-bs-theme="dark" data-bs-theme-color="blue">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>{% block title %}家庭网站{% endblock %}</title><!-- Bootstrap CSS --><link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet"><!-- Font Awesome --><link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"><!-- 自定义CSS --><link rel="stylesheet" href="{{ url_for('static', filename='css/style.css') }}">{% block styles %}{% endblock %}
</head>
<body><!-- 导航栏 -->{% include 'includes/navbar.html' %}<!-- 主内容区 --><main class="container mt-4"><!-- 闪现消息 -->{% include 'includes/flash_messages.html' %}<!-- 页面内容 -->{% block content %}{% endblock %}</main><!-- 页脚 -->{% include 'includes/footer.html' %}<!-- Bootstrap JS --><script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script><!-- jQuery --><script src="https://code.jquery.com/jquery-3.6.0.min.js"></script><!-- 主题切换JS --><script src="{{ url_for('static', filename='js/theme.js') }}"></script><!-- 色彩主题切换JS --><script src="{{ url_for('static', filename='js/color-theme.js') }}"></script><!-- 自定义JS --><script src="{{ url_for('static', filename='js/main.js') }}"></script>{% block scripts %}{% endblock %}
</body>
</html>
1.5 改进日程删除功能

我们使用现代JavaScript事件处理方式改进了日程删除功能:

<!-- app/templates/calendar/upcoming.html -->
{% block scripts %}
<script>
document.addEventListener('DOMContentLoaded', function() {const deleteButtons = document.querySelectorAll('.delete-btn');deleteButtons.forEach(button => {button.addEventListener('click', function(e) {e.preventDefault();if (confirm('确定要删除这个日程吗?此操作不可撤销。')) {window.location.href = this.getAttribute('data-delete-url');}});});
});
</script>
{% endblock %}
<!-- 删除按钮部分 -->
<a href="#" class="btn btn-sm btn-outline-danger delete-btn" data-delete-url="{{ url_for('calendar.delete_schedule', schedule_id=schedule.id) }}"><i class="fas fa-trash-alt"></i>
</a>

2. 首页"即将到来的事件"显示优化

我们优化了首页"即将到来的事件"的显示,使其更加美观和信息丰富:

<!-- app/templates/main/index.html (部分) -->
<!-- 即将到来的事件 -->
<div class="col-md-4"><div class="card h-100"><div class="card-header bg-primary text-white"><h5 class="card-title mb-0"><i class="fas fa-calendar-alt me-2"></i>即将到来的事件</h5></div><div class="card-body">{% if upcoming_events %}<div class="list-group list-group-flush">{% for event in upcoming_events %}<div class="list-group-item px-0"><div class="d-flex w-100 justify-content-between"><h5 class="mb-1">{{ event.title }}</h5><small class="text-muted">{% if event.all_day %}<span class="badge bg-info">全天</span>{% else %}{{ event.start_time.strftime('%H:%M') }}{% endif %}</small></div><p class="mb-1 text-muted"><i class="far fa-calendar me-1"></i>{{ event.start_time.strftime('%Y-%m-%d') }}</p>{% if event.description %}<p class="mb-1">{{ event.description|truncate(100) }}</p>{% endif %}<a href="{{ url_for('calendar.view_schedule', schedule_id=event.id) }}" class="btn btn-sm btn-outline-primary mt-2"><i class="fas fa-eye me-1"></i>查看详情</a></div>{% endfor %}</div>{% else %}<div class="alert alert-info"><i class="fas fa-info-circle me-2"></i>未来没有即将到来的事件。{% if current_user.is_authenticated %}<a href="{{ url_for('calendar.create_schedule') }}" class="alert-link">点击这里</a>添加新日程!{% endif %}</div>{% endif %}</div><div class="card-footer"><a href="{{ url_for('calendar.upcoming') }}" class="btn btn-outline-primary btn-sm w-100"><i class="fas fa-calendar-week me-1"></i>查看更多日程</a></div></div>
</div>

三、技术实现细节

1. CKEditor 5集成

CKEditor 5是一个现代化的富文本编辑器,我们通过以下步骤集成到博客系统中:

  1. 在模板中引入CKEditor 5的CDN:
<script src="https://cdn.ckeditor.com/ckeditor5/35.1.0/classic/ckeditor.js"></script>
  1. 初始化编辑器并处理表单提交:
ClassicEditor.create(document.querySelector('#editor')).then(newEditor => {editor = newEditor;}).catch(error => {console.error(error);});const form = document.getElementById('postForm');
const contentTextarea = document.querySelector('#editor');form.addEventListener('submit', function(e) {e.preventDefault();// 获取CKEditor内容const editorData = editor.getData();// 检查内容是否为空if (!editorData.trim()) {alert('文章内容不能为空!');return;}// 将CKEditor内容设置到原始textareacontentTextarea.value = editorData;// 提交表单this.submit();
});

2. 嵌套评论实现

嵌套评论功能通过以下数据模型和查询实现:

class Comment(db.Model):id = db.Column(db.Integer, primary_key=True)content = db.Column(db.Text, nullable=False)created_at = db.Column(db.DateTime, default=datetime.utcnow)user_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False)post_id = db.Column(db.Integer, db.ForeignKey('post.id'), nullable=False)parent_id = db.Column(db.Integer, db.ForeignKey('comment.id'))# 关系user = db.relationship('User', backref=db.backref('comments', lazy=True))replies = db.relationship('Comment', backref=db.backref('parent', remote_side=[id]), lazy=True)

在视图函数中,我们获取顶级评论和它们的回复:

@bp.route('/view/<int:post_id>')
def view(post_id):post = Post.query.get_or_404(post_id)# 获取顶级评论(没有父评论的评论)top_level_comments = Comment.query.filter_by(post_id=post_id, parent_id=None).order_by(Comment.created_at.desc()).all()# 创建评论表单和回复表单comment_form = CommentForm()reply_form = ReplyForm()# 检查当前用户是否已点赞has_liked = Falseif current_user.is_authenticated:has_liked = Like.query.filter_by(user_id=current_user.id, post_id=post_id).first() is not Nonereturn render_template('blog/view.html', post=post, top_level_comments=top_level_comments,comment_form=comment_form,reply_form=reply_form,has_liked=has_liked)

3. 日程管理系统优化

日程管理系统的优化主要涉及以下几个方面:

  1. 统一使用Schedule模型,避免数据不一致
  2. 修改查询逻辑,确保正确显示日程
  3. 改进前端交互,提升用户体验

关键代码示例:

# 获取未来30天内的日程
schedules = Schedule.query.filter(Schedule.user_id == current_user.id,Schedule.start_time >= datetime.combine(today, time.min),Schedule.start_time <= datetime.combine(next_month, time.max)
).order_by(Schedule.start_time).all()

4. 现代JavaScript事件处理

我们使用现代JavaScript事件处理方式改进了交互体验:

document.addEventListener('DOMContentLoaded', function() {const deleteButtons = document.querySelectorAll('.delete-btn');deleteButtons.forEach(button => {button.addEventListener('click', function(e) {e.preventDefault();if (confirm('确定要删除这个日程吗?此操作不可撤销。')) {window.location.href = this.getAttribute('data-delete-url');}});});
});

在这里插入图片描述

四、总结

在V1.1和V1.2版本中,我们对家庭网站进行了全面升级和优化:

  1. V1.1版本添加了完整的博客系统,包括:

    • 文章创建、编辑和删除功能
    • CKEditor 5富文本编辑器集成
    • 评论系统,支持嵌套回复
    • 点赞功能
    • 用户博客页面
  2. V1.2版本优化了日程管理系统,包括:

    • 统一日程模型,解决数据不一致问题
    • 改进日历视图,移除月份限制
    • 优化首页"即将到来的事件"显示
    • 扩展"即将到来的日程"页面时间范围
    • 修复jQuery加载问题
    • 改进日程删除功能

这些功能的添加和优化使我们的家庭网站更加完善,为家庭成员提供了更丰富的交流和记录方式。博客系统让家庭成员可以分享生活点滴和心得体会,评论和点赞功能增强了互动性。日程管理系统的优化则提高了用户体验,使日程安排更加便捷和可靠。

在下一篇文章中,我们将继续探索如何进一步扩展家庭网站的功能,包括添加家庭成员管理、家庭财务管理、家庭聊天功能等,敬请期待!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/72406.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

软件需求分类、需求获取(高软46)

系列文章目录 软件需求分类&#xff0c;需求获取 文章目录 系列文章目录前言一、软件需求二、获取需求三、真题总结 前言 本节讲明软件需求分类、需求获取的相关知识。 一、软件需求 二、获取需求 三、真题 总结 就是高软笔记&#xff0c;大佬请略过&#xff01;

Zabbix7.0+DeepSeek大模型实现人工智能告警分析

一、方案概述 本方案基于Zabbix7.0监控系统,通过底层webhook脚本机制集成Deepseek做故障分析提供解决方案,构建智能化运维体系。 其核心架构包括: Zabbix监控平台:负责实时监控和告警触发 Webhook接口:实现告警信息的传递 Deepseek AI平台:提供故障智能分析能力 二、…

CPU相关:实时cpu信息接口

[rootxxx ~]# cat /proc/cpuinfo #通过实时cpu信息接口查看cpu信息

A SURVEY ON POST-TRAINING OF LARGE LANGUAGE MODELS——大型语言模型的训练后优化综述——第2部分

3、微调&#xff08;上一部分内容&#xff09; 4、LLMs的对齐 大型语言模型&#xff08;LLMs&#xff09;中的对齐涉及引导模型输出以符合人类预期和偏好&#xff0c;特别是在安全关键或用户面对的应用程序中。本章讨论了实现对齐的三个主要范式&#xff1a; 带有反馈的人工…

热key探测技术架构设计与实践

参考&#xff1a; 得物热点探测技术架构设计与实践 Redis数据倾斜与JD开源hotkey源码分析揭秘 京东热点检测 HotKey 学习笔记 hotkey: 京东App后台中间件&#xff0c;毫秒级探测热点数据&#xff0c;毫秒级推送至服务器集群内存&#xff0c;大幅降低热key对数据层查询压力 …

Windows 环境图形化安装 Oracle 23ai

文章目录 Windows 环境安装23ai下载Oracle 23ai安装包安装安装详细图形界面连接Oracle 23ai 安装过程中遇到的错误安装过其他版本数据库&#xff0c;设置了ORACLE_HOME或 TNS_ADMIN解决方法 无法访问Windows Installer Serviece (error 1719)解决方法 其他注意 参考&#xff1a…

基于SpringBoot3+Druid数据库连接池与外部PostgreSQL的Kubernetes Pod YAML全解析

说明 一个基于Spring Boot 3 Druid 外部PostgreSQL的Kubernetes Pod YAML详细解析&#xff0c;包含最佳实践和关键配置说明&#xff1a; YAML apiVersion: apps/v1 kind: Deployment metadata:name: springboot-applabels:app: springboot-app spec:replicas: 2selector:ma…

PyTorch 深度学习实战(14):Deep Deterministic Policy Gradient (DDPG) 算法

在上一篇文章中&#xff0c;我们介绍了 Proximal Policy Optimization (PPO) 算法&#xff0c;并使用它解决了 CartPole 问题。本文将深入探讨 Deep Deterministic Policy Gradient (DDPG) 算法&#xff0c;这是一种用于连续动作空间的强化学习算法。我们将使用 PyTorch 实现 D…

【Agent实战】货物上架位置推荐助手(RAG方式+结构化prompt(CoT)+API工具结合ChatGPT4o能力Agent项目实践)

本文原创作者:姚瑞南 AI-agent 大模型运营专家,先后任职于美团、猎聘等中大厂AI训练专家和智能运营专家岗;多年人工智能行业智能产品运营及大模型落地经验,拥有AI外呼方向国家专利与PMP项目管理证书。(转载需经授权) 目录 结论 效果图示 1.prompt 2. API工具封…

HarmonyOS NEXT开发进阶(十二):build-profile.json5 文件解析

文章目录 一、前言二、Hvigor脚本文件三、任务与任务依赖图四、多模块管理4.1 静态配置模块 五、分模块编译六、配置多目标产物七、配置APP多目标构建产物八、定义 product 中包含的 target九、拓展阅读 一、前言 编译构建工具DevEco Hvigor&#xff08;以下简称Hvigor&#x…

基于SSM + JSP 的图书商城系统

基于SSM的图书商城 网上书城、图书销售系统、图书销售平台 &#xff5c;Java&#xff5c;SSM&#xff5c;HTML&#xff5c;JSP&#xff5c; 项目采用技术&#xff1a; ①&#xff1a;开发环境&#xff1a;IDEA、JDK1.8、Maven、Tomcat ②&#xff1a;技术栈&#xff1a;Java、…

色板在数据可视化中的创新应用

色板在数据可视化中的创新应用&#xff1a;基于色彩感知理论的优化实践 引言 在数据可视化领域&#xff0c;色彩编码系统的设计已成为决定信息传递效能的核心要素。根据《Nature》期刊2024年发布的视觉认知研究&#xff0c;人类大脑对色彩的识别速度比形状快40%&#xff0c;色…

K8S学习之基础二十七:k8s中daemonset控制器

k8s中DaemonSet控制器 ​ DaemonSet控制器确保k8s集群中&#xff0c;所有节点都运行一个相同的pod&#xff0c;当node节点增加时&#xff0c;新节点也会自动创建一个pod&#xff0c;当node节点从集群移除&#xff0c;对应的pod也会自动删除。删除DaemonSet也会删除创建的pod。…

PyTorch 系列教程:使用CNN实现图像分类

图像分类是计算机视觉领域的一项基本任务&#xff0c;也是深度学习技术的一个常见应用。近年来&#xff0c;卷积神经网络&#xff08;cnn&#xff09;和PyTorch库的结合由于其易用性和鲁棒性已经成为执行图像分类的流行选择。 理解卷积神经网络&#xff08;cnn&#xff09; 卷…

Spring Cloud Stream - 构建高可靠消息驱动与事件溯源架构

一、引言 在分布式系统中&#xff0c;传统的 REST 调用模式往往导致耦合&#xff0c;难以满足高并发和异步解耦的需求。消息驱动架构&#xff08;EDA, Event-Driven Architecture&#xff09;通过异步通信、事件溯源等模式&#xff0c;提高了系统的扩展性与可观测性。 作为 S…

王者荣耀道具页面爬虫(json格式数据)

首先这个和英雄页面是不一样的&#xff0c;英雄页面的图片链接是直接放在源代码里面的&#xff0c;直接就可以请求到&#xff0c;但是这个源代码里面是没有的 虽然在检查页面能够搜索到&#xff0c;但是应该是动态加载的&#xff0c;源码中搜不到该链接 然后就去看看是不是某…

【一起来学kubernetes】12、k8s中的Endpoint详解

一、Endpoint的定义与作用二、Endpoint的创建与管理三、Endpoint的查看与组成四、EndpointSlice五、Endpoint的使用场景六、Endpoint与Service的关系1、定义与功能2、创建与管理3、关系与交互4、使用场景与特点 七、Endpoint的kubectl命令1. 查看Endpoint2. 创建Endpoint3. 编辑…

C# Enumerable类 之 集合操作

总目录 前言 在 C# 中&#xff0c;System.Linq.Enumerable 类是 LINQ&#xff08;Language Integrated Query&#xff09;的核心组成部分&#xff0c;它提供了一系列静态方法&#xff0c;用于操作实现了 IEnumerable 接口的集合。通过这些方法&#xff0c;我们可以轻松地对集合…

51c自动驾驶~合集54

我自己的原文哦~ https://blog.51cto.com/whaosoft/13517811 #Chameleon 快慢双系统&#xff01;清华&博世最新&#xff1a;无需训练即可解决复杂道路拓扑 在自动驾驶技术中&#xff0c;车道拓扑提取是实现无地图导航的核心任务之一。它要求系统不仅能检测出车道和交…

Spring Cloud Eureka - 高可用服务注册与发现解决方案

在微服务架构中&#xff0c;服务注册与发现是确保系统动态扩展和高效通信的关键。Eureka 作为 Spring Cloud 生态的核心组件&#xff0c;不仅提供去中心化的服务治理能力&#xff0c;还通过自我保护、健康检查等机制提升系统的稳定性&#xff0c;使其成为微服务架构中的重要支撑…