From d0fa8553889df430711e64d7ed57af9f99f74da1 Mon Sep 17 00:00:00 2001 From: xiaji Date: Sun, 1 Feb 2026 18:24:18 +0800 Subject: [PATCH] =?UTF-8?q?feat(=E5=8E=86=E5=8F=B2=E8=AE=B0=E5=BD=95):=20?= =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E5=8E=86=E5=8F=B2=E8=AE=B0=E5=BD=95=E6=9F=A5?= =?UTF-8?q?=E8=AF=A2=E5=8A=9F=E8=83=BD=E5=8F=8APDF=E5=AF=BC=E5=87=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 新增历史记录查询页面,支持按时间范围筛选阅读记录、感悟记录、汇总记录、家庭事项和今日计划 添加历史记录PDF导出功能,生成包含所有记录的格式化PDF报告 --- core/history_views.py | 150 ++++++++++ core/templates/core/history_pdf.html | 344 +++++++++++++++++++++++ core/templates/core/history_records.html | 293 +++++++++++++++++++ core/templates/core/index.html | 93 ++++++ core/urls.py | 5 + 5 files changed, 885 insertions(+) create mode 100644 core/history_views.py create mode 100644 core/templates/core/history_pdf.html create mode 100644 core/templates/core/history_records.html diff --git a/core/history_views.py b/core/history_views.py new file mode 100644 index 0000000..7576756 --- /dev/null +++ b/core/history_views.py @@ -0,0 +1,150 @@ +from django.shortcuts import render +from django.http import HttpResponse +from django.utils import timezone +from django.contrib.auth.decorators import login_required +from django.conf import settings +from datetime import datetime, timedelta +from loguru import logger +import os + +from .models import ( + ReadingRecord, + InsightRecord, + Summary, + FamilyTask, + TodayPlan, +) +from .views import is_weasyprint_available + + +@login_required +def history_records(request): + """历史记录查询页面""" + logger.info("用户访问历史记录查询页面") + + start_date_str = request.GET.get('start_date') + end_date_str = request.GET.get('end_date') + + if not start_date_str or not end_date_str: + today = timezone.now().date() + start_date = datetime(today.year, 1, 1).date() + end_date = today + else: + start_date = datetime.strptime(start_date_str, '%Y-%m-%d').date() + end_date = datetime.strptime(end_date_str, '%Y-%m-%d').date() + + # 查询各个类型的记录 + reading_records = ReadingRecord.objects.filter( + date__range=[start_date, end_date] + ).order_by('-date') + + insight_records = InsightRecord.objects.filter( + date__range=[start_date, end_date] + ).order_by('-date') + + summary_records = Summary.objects.filter( + date__range=[start_date, end_date] + ).order_by('-date') + + family_tasks = FamilyTask.objects.filter( + created_at__date__range=[start_date, end_date] + ).order_by('-created_at') + + today_plans = TodayPlan.objects.filter( + date__range=[start_date, end_date] + ).order_by('-date') + + context = { + 'start_date': start_date, + 'end_date': end_date, + 'reading_records': reading_records, + 'insight_records': insight_records, + 'summary_records': summary_records, + 'family_tasks': family_tasks, + 'today_plans': today_plans, + } + + return render(request, 'core/history_records.html', context) + + +@login_required +def history_pdf(request): + """导出历史记录PDF""" + if not is_weasyprint_available(): + logger.error("WeasyPrint库不可用,无法生成PDF报告") + return HttpResponse("PDF功能不可用,请检查WeasyPrint库是否正确安装", status=500) + + start_date_str = request.GET.get('start_date') + end_date_str = request.GET.get('end_date') + + if not start_date_str or not end_date_str: + return HttpResponse("请提供开始时间和结束时间", status=400) + + try: + start_date = datetime.strptime(start_date_str, '%Y-%m-%d').date() + end_date = datetime.strptime(end_date_str, '%Y-%m-%d').date() + except ValueError: + return HttpResponse("日期格式错误", status=400) + + if start_date > end_date: + return HttpResponse("开始时间不能晚于结束时间", status=400) + + logger.info(f"用户导出历史记录PDF: {start_date} 至 {end_date}") + + try: + # 查询各个类型的记录 + reading_records = ReadingRecord.objects.filter( + date__range=[start_date, end_date] + ).order_by('-date') + + insight_records = InsightRecord.objects.filter( + date__range=[start_date, end_date] + ).order_by('-date') + + summary_records = Summary.objects.filter( + date__range=[start_date, end_date] + ).order_by('-date') + + family_tasks = FamilyTask.objects.filter( + created_at__date__range=[start_date, end_date] + ).order_by('-created_at') + + today_plans = TodayPlan.objects.filter( + date__range=[start_date, end_date] + ).order_by('-date') + + context = { + 'start_date': start_date, + 'end_date': end_date, + 'reading_records': reading_records, + 'insight_records': insight_records, + 'summary_records': summary_records, + 'family_tasks': family_tasks, + 'today_plans': today_plans, + } + + # 渲染HTML模板 + html_string = render(request, 'core/history_pdf.html', context).content.decode('utf-8') + + # 生成PDF + pdf_file = f"history_{start_date_str}_to_{end_date_str}.pdf" + pdf_path = os.path.join(settings.REPORTS_ROOT, pdf_file) + + # 确保报告目录存在 + os.makedirs(settings.REPORTS_ROOT, exist_ok=True) + + # 动态导入WeasyPrint + from weasyprint import HTML + HTML(string=html_string).write_pdf(pdf_path) + + logger.info(f"历史记录PDF生成成功: {pdf_path}") + + # 返回PDF文件 + with open(pdf_path, 'rb') as f: + response = HttpResponse(f.read(), content_type='application/pdf') + response['Content-Disposition'] = f'attachment; filename="{pdf_file}"' + return response + + except Exception as e: + logger.error(f"生成历史记录PDF失败: {str(e)}") + return HttpResponse(f"生成PDF失败: {str(e)}", status=500) diff --git a/core/templates/core/history_pdf.html b/core/templates/core/history_pdf.html new file mode 100644 index 0000000..2eb83ac --- /dev/null +++ b/core/templates/core/history_pdf.html @@ -0,0 +1,344 @@ + + + + + 历史记录 - {{ start_date|date:"Y-m-d" }} 至 {{ end_date|date:"Y-m-d" }} + + + +
+

历史记录汇总

+
+ {{ start_date|date:"Y年m月d日" }} 至 {{ end_date|date:"Y年m月d日" }} +
+
+ + +
+
阅读记录 ({{ reading_records|length }} 条)
+ {% if reading_records %} + + + + + + + + + + + + {% for record in reading_records %} + + + + + + + + {% endfor %} + +
日期类型标题来源进度
{{ record.date|date:"Y-m-d" }}{{ record.type.name }}{{ record.title }}{{ record.source|default:"-" }} + {% if record.progress %} +
+
+
+ {{ record.progress }}% + {% else %} + - + {% endif %} +
+ {% else %} +
该时间段没有阅读记录
+ {% endif %} +
+ + +
+
感悟记录 ({{ insight_records|length }} 条)
+ {% if insight_records %} + {% for record in insight_records %} +
+
+ {{ record.date|date:"Y-m-d" }} + 发言人: {{ record.speaker.name }} +
+
+ {{ record.content|linebreaks }} +
+
+ {% endfor %} + {% else %} +
该时间段没有感悟记录
+ {% endif %} +
+ + +
+
汇总记录 ({{ summary_records|length }} 条)
+ {% if summary_records %} + {% for record in summary_records %} +
+
+ {{ record.date|date:"Y-m-d" }} + + {{ record.category.name }} + 发言人: {{ record.speaker.name }} + +
+
+ {{ record.content|linebreaks }} + {% if record.source %} +

来源: {{ record.source }}

+ {% endif %} +
+
+ {% endfor %} + {% else %} +
该时间段没有汇总记录
+ {% endif %} +
+ + +
+
家庭事项 ({{ family_tasks|length }} 条)
+ {% if family_tasks %} + + + + + + + + + + + + + {% for task in family_tasks %} + + + + + + + + + {% endfor %} + +
创建时间类型内容优先级状态截止日期
{{ task.created_at|date:"Y-m-d H:i" }}{{ task.type.name }}{{ task.content|truncatechars:50 }} + + {{ task.priority.name }} + + + + {{ task.status.name }} + + {{ task.deadline|date:"Y-m-d"|default:"-" }}
+ {% else %} +
该时间段没有家庭事项
+ {% endif %} +
+ + +
+
今日计划 ({{ today_plans|length }} 条)
+ {% if today_plans %} + + + + + + + + + + + + + {% for plan in today_plans %} + + + + + + + + + {% endfor %} + +
日期发言人内容类型优先级状态
{{ plan.date|date:"Y-m-d" }}{{ plan.speaker.name }}{{ plan.content|truncatechars:50 }}{{ plan.type.name }} + + {{ plan.priority.name }} + + + + {{ plan.status.name }} + +
+ {% else %} +
该时间段没有今日计划
+ {% endif %} +
+ + + + diff --git a/core/templates/core/history_records.html b/core/templates/core/history_records.html new file mode 100644 index 0000000..2200b5c --- /dev/null +++ b/core/templates/core/history_records.html @@ -0,0 +1,293 @@ +{% extends 'core/base.html' %} + +{% block content %} + +
+
+
+
+
+

历史记录查询

+

+ + {{ start_date|date:"Y年m月d日" }} 至 {{ end_date|date:"Y年m月d日" }} +

+
+
+ +
+
+
+
+
+ + +
+
+
+
+
阅读记录
+ {{ reading_records|length }} 条 +
+
+ {% if reading_records %} +
+ + + + + + + + + + + + {% for record in reading_records %} + + + + + + + + {% endfor %} + +
日期类型标题来源进度
{{ record.date|date:"Y-m-d" }}{{ record.type.name }}{{ record.title }}{{ record.source|default:"-" }} + {% if record.progress %} +
+
+
+ {{ record.progress }}% + {% else %} + - + {% endif %} +
+
+ {% else %} +
+ +

该时间段没有阅读记录

+
+ {% endif %} +
+
+
+
+ + +
+
+
+
+
感悟记录
+ {{ insight_records|length }} 条 +
+
+ {% if insight_records %} +
+ {% for record in insight_records %} +
+
+
+ {{ record.date|date:"Y-m-d" }} + {{ record.speaker.name }} +
+
+

{{ record.content|linebreaks }}

+ {% if record.file %} + + 查看附件 + + {% endif %} +
+ {% endfor %} +
+ {% else %} +
+ +

该时间段没有感悟记录

+
+ {% endif %} +
+
+
+
+ + +
+
+
+
+
汇总记录
+ {{ summary_records|length }} 条 +
+
+ {% if summary_records %} +
+ {% for record in summary_records %} +
+
+
+ {{ record.date|date:"Y-m-d" }} + {{ record.category.name }} + {{ record.speaker.name }} +
+
+

{{ record.content|linebreaks }}

+ {% if record.source %} +
+ {{ record.source }} +
+ {% endif %} + {% if record.file %} + + 查看附件 + + {% endif %} +
+ {% endfor %} +
+ {% else %} +
+ +

该时间段没有汇总记录

+
+ {% endif %} +
+
+
+
+ + +
+
+
+
+
家庭事项
+ {{ family_tasks|length }} 条 +
+
+ {% if family_tasks %} +
+ + + + + + + + + + + + + {% for task in family_tasks %} + + + + + + + + + {% endfor %} + +
创建时间类型内容优先级状态截止日期
{{ task.created_at|date:"Y-m-d H:i" }}{{ task.type.name }}{{ task.content|truncatechars:50 }} + + {{ task.priority.name }} + + + + {{ task.status.name }} + + + {% if task.deadline %} + {% if task.is_overdue %} + {{ task.deadline|date:"Y-m-d" }} (已逾期) + {% else %} + {{ task.deadline|date:"Y-m-d" }} + {% endif %} + {% else %} + - + {% endif %} +
+
+ {% else %} +
+ +

该时间段没有家庭事项

+
+ {% endif %} +
+
+
+
+ + +
+
+
+
+
今日计划
+ {{ today_plans|length }} 条 +
+
+ {% if today_plans %} +
+ + + + + + + + + + + + + {% for plan in today_plans %} + + + + + + + + + {% endfor %} + +
日期发言人内容类型优先级状态
{{ plan.date|date:"Y-m-d" }}{{ plan.speaker.name }}{{ plan.content|truncatechars:50 }}{{ plan.type.name }} + + {{ plan.priority.name }} + + + + {{ plan.status.name }} + +
+
+ {% else %} +
+ +

该时间段没有今日计划

+
+ {% endif %} +
+
+
+
+ + +
+ +
+{% endblock %} diff --git a/core/templates/core/index.html b/core/templates/core/index.html index 12d7eec..5c294a8 100644 --- a/core/templates/core/index.html +++ b/core/templates/core/index.html @@ -279,4 +279,97 @@ + + +
+
+
+
+
历史记录查询
+ 导出PDF +
+
+
+
+
+ + +
+
+ + +
+
+
+ + + + +
+
+
+
+
+
+
+
+ + {% endblock %} diff --git a/core/urls.py b/core/urls.py index 9a48529..a7e193c 100644 --- a/core/urls.py +++ b/core/urls.py @@ -1,5 +1,6 @@ from django.urls import path from . import views +from . import history_views urlpatterns = [ # 登录和注销 @@ -63,4 +64,8 @@ urlpatterns = [ # PDF文件列表 path('pdf-list/', views.pdf_list, name='pdf_list'), + + # 历史记录查询 + path('history/', history_views.history_records, name='history_records'), + path('history/pdf/', history_views.history_pdf, name='history_pdf'), ] \ No newline at end of file