feat(报告系统): 添加汇总记录功能
新增汇总记录模型、表单、视图和模板,支持在报告中显示昨日汇总记录
This commit is contained in:
@@ -6,6 +6,8 @@ from .models import (
|
|||||||
Status,
|
Status,
|
||||||
PlanType,
|
PlanType,
|
||||||
FamilyMember,
|
FamilyMember,
|
||||||
|
SummaryCategory,
|
||||||
|
Summary,
|
||||||
ReadingRecord,
|
ReadingRecord,
|
||||||
InsightRecord,
|
InsightRecord,
|
||||||
FamilyTask,
|
FamilyTask,
|
||||||
@@ -14,6 +16,19 @@ from .models import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@admin.register(SummaryCategory)
|
||||||
|
class SummaryCategoryAdmin(admin.ModelAdmin):
|
||||||
|
list_display = ('name', 'created_at', 'updated_at')
|
||||||
|
search_fields = ('name',)
|
||||||
|
|
||||||
|
|
||||||
|
@admin.register(Summary)
|
||||||
|
class SummaryAdmin(admin.ModelAdmin):
|
||||||
|
list_display = ('date', 'category', 'speaker', 'source', 'created_at')
|
||||||
|
list_filter = ('date', 'category', 'speaker')
|
||||||
|
search_fields = ('content', 'source')
|
||||||
|
|
||||||
|
|
||||||
@admin.register(FamilyMember)
|
@admin.register(FamilyMember)
|
||||||
class FamilyMemberAdmin(admin.ModelAdmin):
|
class FamilyMemberAdmin(admin.ModelAdmin):
|
||||||
list_display = ('name', 'created_at', 'updated_at')
|
list_display = ('name', 'created_at', 'updated_at')
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ from django.utils import timezone
|
|||||||
from .models import (
|
from .models import (
|
||||||
ReadingRecord,
|
ReadingRecord,
|
||||||
InsightRecord,
|
InsightRecord,
|
||||||
|
Summary,
|
||||||
FamilyTask,
|
FamilyTask,
|
||||||
TodayPlan,
|
TodayPlan,
|
||||||
SystemConfig,
|
SystemConfig,
|
||||||
@@ -35,6 +36,20 @@ class InsightRecordForm(forms.ModelForm):
|
|||||||
'file': forms.FileInput(attrs={'class': 'form-control'}),
|
'file': forms.FileInput(attrs={'class': 'form-control'}),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class SummaryForm(forms.ModelForm):
|
||||||
|
"""汇总记录表单"""
|
||||||
|
class Meta:
|
||||||
|
model = Summary
|
||||||
|
fields = ['date', 'category', 'speaker', 'content', 'source', 'file']
|
||||||
|
widgets = {
|
||||||
|
'date': forms.DateInput(attrs={'type': 'date', 'class': 'form-control'}),
|
||||||
|
'category': forms.Select(attrs={'class': 'form-select'}),
|
||||||
|
'speaker': forms.Select(attrs={'class': 'form-select'}),
|
||||||
|
'content': forms.Textarea(attrs={'class': 'form-control', 'rows': 5, 'placeholder': '请输入汇总内容'}),
|
||||||
|
'source': forms.TextInput(attrs={'class': 'form-control', 'placeholder': '请输入来源'}),
|
||||||
|
'file': forms.FileInput(attrs={'class': 'form-control'}),
|
||||||
|
}
|
||||||
|
|
||||||
class FamilyTaskForm(forms.ModelForm):
|
class FamilyTaskForm(forms.ModelForm):
|
||||||
"""家庭事项表单"""
|
"""家庭事项表单"""
|
||||||
class Meta:
|
class Meta:
|
||||||
|
|||||||
47
core/migrations/0006_add_summary.py
Normal file
47
core/migrations/0006_add_summary.py
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
# Generated by Django 5.1.4 on 2026-01-23 15:06
|
||||||
|
|
||||||
|
import django.db.models.deletion
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('core', '0005_add_speaker_fields'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='SummaryCategory',
|
||||||
|
fields=[
|
||||||
|
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('name', models.CharField(max_length=20, unique=True, verbose_name='名称')),
|
||||||
|
('created_at', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')),
|
||||||
|
('updated_at', models.DateTimeField(auto_now=True, verbose_name='更新时间')),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'verbose_name': '汇总分类',
|
||||||
|
'verbose_name_plural': '汇总分类',
|
||||||
|
'ordering': ['name'],
|
||||||
|
},
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='Summary',
|
||||||
|
fields=[
|
||||||
|
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('date', models.DateField(default=django.utils.timezone.now, verbose_name='日期')),
|
||||||
|
('content', models.TextField(verbose_name='内容')),
|
||||||
|
('source', models.CharField(max_length=200, blank=True, null=True, verbose_name='来源')),
|
||||||
|
('file', models.FileField(blank=True, null=True, upload_to='summary_files/', verbose_name='上传文件')),
|
||||||
|
('created_at', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')),
|
||||||
|
('updated_at', models.DateTimeField(auto_now=True, verbose_name='更新时间')),
|
||||||
|
('category', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='core.summarycategory', verbose_name='分类')),
|
||||||
|
('speaker', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='core.familymember', verbose_name='发言人')),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'verbose_name': '汇总记录',
|
||||||
|
'verbose_name_plural': '汇总记录',
|
||||||
|
'ordering': ['-date', '-created_at'],
|
||||||
|
},
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -159,6 +159,39 @@ class TodayPlan(models.Model):
|
|||||||
def __str__(self):
|
def __str__(self):
|
||||||
return f"{self.date} - {self.content[:20]}..."
|
return f"{self.date} - {self.content[:20]}..."
|
||||||
|
|
||||||
|
class SummaryCategory(models.Model):
|
||||||
|
"""汇总分类"""
|
||||||
|
name = models.CharField(max_length=20, unique=True, verbose_name="名称")
|
||||||
|
created_at = models.DateTimeField(auto_now_add=True, verbose_name="创建时间")
|
||||||
|
updated_at = models.DateTimeField(auto_now=True, verbose_name="更新时间")
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
verbose_name = "汇总分类"
|
||||||
|
verbose_name_plural = "汇总分类"
|
||||||
|
ordering = ['name']
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return self.name
|
||||||
|
|
||||||
|
class Summary(models.Model):
|
||||||
|
"""汇总记录表"""
|
||||||
|
date = models.DateField(default=timezone.now, verbose_name="日期")
|
||||||
|
category = models.ForeignKey(SummaryCategory, on_delete=models.CASCADE, verbose_name="分类")
|
||||||
|
speaker = models.ForeignKey(FamilyMember, on_delete=models.CASCADE, verbose_name="发言人")
|
||||||
|
content = models.TextField(verbose_name="内容")
|
||||||
|
source = models.CharField(max_length=200, blank=True, null=True, verbose_name="来源")
|
||||||
|
file = models.FileField(upload_to='summary_files/', blank=True, null=True, verbose_name="上传文件")
|
||||||
|
created_at = models.DateTimeField(auto_now_add=True, verbose_name="创建时间")
|
||||||
|
updated_at = models.DateTimeField(auto_now=True, verbose_name="更新时间")
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
verbose_name = "汇总记录"
|
||||||
|
verbose_name_plural = "汇总记录"
|
||||||
|
ordering = ['-date', '-created_at']
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return f"{self.category.name} - {self.date}"
|
||||||
|
|
||||||
class SystemConfig(models.Model):
|
class SystemConfig(models.Model):
|
||||||
"""系统配置表"""
|
"""系统配置表"""
|
||||||
smtp_server = models.CharField(max_length=100, blank=True, null=True, verbose_name="SMTP服务器")
|
smtp_server = models.CharField(max_length=100, blank=True, null=True, verbose_name="SMTP服务器")
|
||||||
|
|||||||
@@ -201,4 +201,36 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- 昨日汇总记录 -->
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-header bg-info text-white">
|
||||||
|
<h5 class="card-title mb-0">昨日汇总记录</h5>
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
{% if yesterday_summary %}
|
||||||
|
<div class="row">
|
||||||
|
{% for summary in yesterday_summary %}
|
||||||
|
<div class="col-md-6 mb-3">
|
||||||
|
<div class="card h-100">
|
||||||
|
<div class="card-header d-flex justify-content-between align-items-center">
|
||||||
|
<span><span class="badge bg-primary">{{ summary.category.name }}</span></span>
|
||||||
|
<small class="text-muted">{{ summary.speaker.name }}</small>
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<p class="card-text">{{ summary.content }}</p>
|
||||||
|
{% if summary.source %}
|
||||||
|
<small class="text-muted">来源:{{ summary.source }}</small>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
{% else %}
|
||||||
|
<p class="text-muted">昨日没有汇总记录</p>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
@@ -141,6 +141,35 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- 昨日汇总记录 -->
|
||||||
|
<div class="section">
|
||||||
|
<h2>昨日汇总记录</h2>
|
||||||
|
{% if yesterday_summary %}
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>分类</th>
|
||||||
|
<th>发言人</th>
|
||||||
|
<th>内容</th>
|
||||||
|
<th>来源</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{% for summary in yesterday_summary %}
|
||||||
|
<tr>
|
||||||
|
<td>{{ summary.category.name }}</td>
|
||||||
|
<td>{{ summary.speaker.name }}</td>
|
||||||
|
<td>{{ summary.content }}</td>
|
||||||
|
<td>{{ summary.source|default:"-" }}</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
{% else %}
|
||||||
|
<div class="no-data">昨日没有汇总记录</div>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- 今日计划 -->
|
<!-- 今日计划 -->
|
||||||
<div class="section">
|
<div class="section">
|
||||||
<h2>今日计划</h2>
|
<h2>今日计划</h2>
|
||||||
|
|||||||
@@ -37,14 +37,17 @@ def is_weasyprint_available():
|
|||||||
from .models import (
|
from .models import (
|
||||||
ReadingRecord,
|
ReadingRecord,
|
||||||
InsightRecord,
|
InsightRecord,
|
||||||
|
Summary,
|
||||||
FamilyTask,
|
FamilyTask,
|
||||||
TodayPlan,
|
TodayPlan,
|
||||||
SystemConfig,
|
SystemConfig,
|
||||||
FamilyMember
|
FamilyMember,
|
||||||
|
SummaryCategory
|
||||||
)
|
)
|
||||||
from .forms import (
|
from .forms import (
|
||||||
ReadingRecordForm,
|
ReadingRecordForm,
|
||||||
InsightRecordForm,
|
InsightRecordForm,
|
||||||
|
SummaryForm,
|
||||||
FamilyTaskForm,
|
FamilyTaskForm,
|
||||||
TodayPlanForm,
|
TodayPlanForm,
|
||||||
SystemConfigForm
|
SystemConfigForm
|
||||||
@@ -304,6 +307,69 @@ def delete_today_insight(request, pk):
|
|||||||
context = {'insight': insight}
|
context = {'insight': insight}
|
||||||
return render(request, 'core/delete_insight.html', context)
|
return render(request, 'core/delete_insight.html', context)
|
||||||
|
|
||||||
|
# 汇总记录视图
|
||||||
|
def summaries(request):
|
||||||
|
"""汇总记录"""
|
||||||
|
logger.info("用户访问汇总记录页面")
|
||||||
|
today = timezone.now().date()
|
||||||
|
yesterday = today - timedelta(days=1)
|
||||||
|
|
||||||
|
summary_records = Summary.objects.filter(date=yesterday)
|
||||||
|
|
||||||
|
context = {
|
||||||
|
'yesterday': yesterday,
|
||||||
|
'summary_records': summary_records,
|
||||||
|
}
|
||||||
|
|
||||||
|
return render(request, 'core/summaries.html', context)
|
||||||
|
|
||||||
|
# 添加汇总记录
|
||||||
|
def add_summary(request):
|
||||||
|
"""添加汇总记录"""
|
||||||
|
family_members = FamilyMember.objects.all()
|
||||||
|
categories = SummaryCategory.objects.all()
|
||||||
|
if request.method == 'POST':
|
||||||
|
form = SummaryForm(request.POST, request.FILES)
|
||||||
|
if form.is_valid():
|
||||||
|
form.save()
|
||||||
|
logger.info(f"添加汇总记录: {form.cleaned_data['content'][:20]}...")
|
||||||
|
return redirect('summaries')
|
||||||
|
else:
|
||||||
|
form = SummaryForm()
|
||||||
|
|
||||||
|
context = {'form': form, 'family_members': family_members, 'categories': categories}
|
||||||
|
return render(request, 'core/add_summary.html', context)
|
||||||
|
|
||||||
|
# 编辑汇总记录
|
||||||
|
def edit_summary(request, pk):
|
||||||
|
"""编辑汇总记录"""
|
||||||
|
summary = get_object_or_404(Summary, pk=pk)
|
||||||
|
family_members = FamilyMember.objects.all()
|
||||||
|
categories = SummaryCategory.objects.all()
|
||||||
|
if request.method == 'POST':
|
||||||
|
form = SummaryForm(request.POST, request.FILES, instance=summary)
|
||||||
|
if form.is_valid():
|
||||||
|
form.save()
|
||||||
|
logger.info(f"编辑汇总记录: {form.cleaned_data['content'][:20]}...")
|
||||||
|
return redirect('summaries')
|
||||||
|
else:
|
||||||
|
form = SummaryForm(instance=summary)
|
||||||
|
|
||||||
|
context = {'form': form, 'summary': summary, 'family_members': family_members, 'categories': categories}
|
||||||
|
return render(request, 'core/edit_summary.html', context)
|
||||||
|
|
||||||
|
# 删除汇总记录
|
||||||
|
def delete_summary(request, pk):
|
||||||
|
"""删除汇总记录"""
|
||||||
|
summary = get_object_or_404(Summary, pk=pk)
|
||||||
|
if request.method == 'POST':
|
||||||
|
summary.delete()
|
||||||
|
logger.info(f"删除汇总记录: {summary.content[:20]}...")
|
||||||
|
return redirect('summaries')
|
||||||
|
|
||||||
|
context = {'summary': summary}
|
||||||
|
return render(request, 'core/delete_summary.html', context)
|
||||||
|
|
||||||
# 家庭事项视图
|
# 家庭事项视图
|
||||||
def family_tasks(request):
|
def family_tasks(request):
|
||||||
"""家庭事项"""
|
"""家庭事项"""
|
||||||
@@ -445,6 +511,7 @@ def generate_report(request):
|
|||||||
# 获取昨日记录
|
# 获取昨日记录
|
||||||
yesterday_reading = ReadingRecord.objects.filter(date=yesterday)
|
yesterday_reading = ReadingRecord.objects.filter(date=yesterday)
|
||||||
yesterday_insight = InsightRecord.objects.filter(date=yesterday)
|
yesterday_insight = InsightRecord.objects.filter(date=yesterday)
|
||||||
|
yesterday_summary = Summary.objects.filter(date=yesterday)
|
||||||
|
|
||||||
# 获取今日计划
|
# 获取今日计划
|
||||||
today_plan = TodayPlan.objects.filter(date=today)
|
today_plan = TodayPlan.objects.filter(date=today)
|
||||||
@@ -478,6 +545,7 @@ def generate_report(request):
|
|||||||
'yesterday': yesterday,
|
'yesterday': yesterday,
|
||||||
'yesterday_reading': yesterday_reading,
|
'yesterday_reading': yesterday_reading,
|
||||||
'yesterday_insight': yesterday_insight,
|
'yesterday_insight': yesterday_insight,
|
||||||
|
'yesterday_summary': yesterday_summary,
|
||||||
'today_plan': today_plan,
|
'today_plan': today_plan,
|
||||||
'family_task_stats': family_task_stats,
|
'family_task_stats': family_task_stats,
|
||||||
'historical_dates': historical_dates,
|
'historical_dates': historical_dates,
|
||||||
@@ -496,6 +564,7 @@ def view_report(request, date):
|
|||||||
# 获取指定日期的记录
|
# 获取指定日期的记录
|
||||||
yesterday_reading = ReadingRecord.objects.filter(date=yesterday)
|
yesterday_reading = ReadingRecord.objects.filter(date=yesterday)
|
||||||
yesterday_insight = InsightRecord.objects.filter(date=yesterday)
|
yesterday_insight = InsightRecord.objects.filter(date=yesterday)
|
||||||
|
yesterday_summary = Summary.objects.filter(date=yesterday)
|
||||||
today_plan = TodayPlan.objects.filter(date=report_date)
|
today_plan = TodayPlan.objects.filter(date=report_date)
|
||||||
|
|
||||||
# 获取家庭事项统计
|
# 获取家庭事项统计
|
||||||
@@ -506,6 +575,7 @@ def view_report(request, date):
|
|||||||
'yesterday': yesterday,
|
'yesterday': yesterday,
|
||||||
'yesterday_reading': yesterday_reading,
|
'yesterday_reading': yesterday_reading,
|
||||||
'yesterday_insight': yesterday_insight,
|
'yesterday_insight': yesterday_insight,
|
||||||
|
'yesterday_summary': yesterday_summary,
|
||||||
'today_plan': today_plan,
|
'today_plan': today_plan,
|
||||||
'family_task_stats': family_task_stats,
|
'family_task_stats': family_task_stats,
|
||||||
}
|
}
|
||||||
@@ -528,6 +598,7 @@ def generate_pdf_report(request, date):
|
|||||||
# 获取指定日期的记录
|
# 获取指定日期的记录
|
||||||
yesterday_reading = ReadingRecord.objects.filter(date=yesterday)
|
yesterday_reading = ReadingRecord.objects.filter(date=yesterday)
|
||||||
yesterday_insight = InsightRecord.objects.filter(date=yesterday)
|
yesterday_insight = InsightRecord.objects.filter(date=yesterday)
|
||||||
|
yesterday_summary = Summary.objects.filter(date=yesterday)
|
||||||
today_plan = TodayPlan.objects.filter(date=report_date)
|
today_plan = TodayPlan.objects.filter(date=report_date)
|
||||||
|
|
||||||
# 获取家庭事项统计
|
# 获取家庭事项统计
|
||||||
@@ -538,6 +609,7 @@ def generate_pdf_report(request, date):
|
|||||||
'yesterday': yesterday,
|
'yesterday': yesterday,
|
||||||
'yesterday_reading': yesterday_reading,
|
'yesterday_reading': yesterday_reading,
|
||||||
'yesterday_insight': yesterday_insight,
|
'yesterday_insight': yesterday_insight,
|
||||||
|
'yesterday_summary': yesterday_summary,
|
||||||
'today_plan': today_plan,
|
'today_plan': today_plan,
|
||||||
'family_task_stats': family_task_stats,
|
'family_task_stats': family_task_stats,
|
||||||
}
|
}
|
||||||
@@ -579,6 +651,7 @@ def preview_pdf_report(request, date):
|
|||||||
# 获取指定日期的记录
|
# 获取指定日期的记录
|
||||||
yesterday_reading = ReadingRecord.objects.filter(date=yesterday)
|
yesterday_reading = ReadingRecord.objects.filter(date=yesterday)
|
||||||
yesterday_insight = InsightRecord.objects.filter(date=yesterday)
|
yesterday_insight = InsightRecord.objects.filter(date=yesterday)
|
||||||
|
yesterday_summary = Summary.objects.filter(date=yesterday)
|
||||||
today_plan = TodayPlan.objects.filter(date=report_date)
|
today_plan = TodayPlan.objects.filter(date=report_date)
|
||||||
|
|
||||||
# 获取家庭事项统计
|
# 获取家庭事项统计
|
||||||
@@ -589,6 +662,7 @@ def preview_pdf_report(request, date):
|
|||||||
'yesterday': yesterday,
|
'yesterday': yesterday,
|
||||||
'yesterday_reading': yesterday_reading,
|
'yesterday_reading': yesterday_reading,
|
||||||
'yesterday_insight': yesterday_insight,
|
'yesterday_insight': yesterday_insight,
|
||||||
|
'yesterday_summary': yesterday_summary,
|
||||||
'today_plan': today_plan,
|
'today_plan': today_plan,
|
||||||
'family_task_stats': family_task_stats,
|
'family_task_stats': family_task_stats,
|
||||||
}
|
}
|
||||||
@@ -641,6 +715,7 @@ def send_email_view(request):
|
|||||||
|
|
||||||
yesterday_reading = ReadingRecord.objects.filter(date=yesterday)
|
yesterday_reading = ReadingRecord.objects.filter(date=yesterday)
|
||||||
yesterday_insight = InsightRecord.objects.filter(date=yesterday)
|
yesterday_insight = InsightRecord.objects.filter(date=yesterday)
|
||||||
|
yesterday_summary = Summary.objects.filter(date=yesterday)
|
||||||
today_plan = TodayPlan.objects.filter(date=report_date)
|
today_plan = TodayPlan.objects.filter(date=report_date)
|
||||||
family_task_stats = FamilyTask.objects.values('type').annotate(count=Count('id'))
|
family_task_stats = FamilyTask.objects.values('type').annotate(count=Count('id'))
|
||||||
|
|
||||||
@@ -649,6 +724,7 @@ def send_email_view(request):
|
|||||||
'yesterday': yesterday,
|
'yesterday': yesterday,
|
||||||
'yesterday_reading': yesterday_reading,
|
'yesterday_reading': yesterday_reading,
|
||||||
'yesterday_insight': yesterday_insight,
|
'yesterday_insight': yesterday_insight,
|
||||||
|
'yesterday_summary': yesterday_summary,
|
||||||
'today_plan': today_plan,
|
'today_plan': today_plan,
|
||||||
'family_task_stats': family_task_stats,
|
'family_task_stats': family_task_stats,
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user