改进事件的显示效果

This commit is contained in:
2025-12-03 15:42:35 +08:00
parent 551128cd09
commit ddad32b481
5 changed files with 898 additions and 878 deletions

Binary file not shown.

View File

@@ -1,292 +1,305 @@
{% extends 'base.html' %}
{% load custom_filters %}
{% block content %}
<div class="w-full max-w-4xl mx-auto p-6">
<div class="relative">
<!-- 时间线 -->
<div class="absolute top-1/2 left-0 right-0 h-0.5 bg-blue-500 transform -translate-y-1/2"></div>
<div class="relative z-10 flex justify-between items-center">
<!-- 第一个卡片 -->
<div class="w-[calc(50%-2rem)] bg-white rounded-lg shadow-lg overflow-hidden transition-all duration-300 hover:shadow-xl">
<div class="p-6 flex flex-col h-full">
<div class="flex-grow">
<div class="text-xs font-semibold text-blue-600 mb-2">最初活动</div>
<h3 class="text-xl font-bold mb-3 text-gray-800">{{ earliest_act.branch }}{{ earliest_act.name }}</h3>
<p class="text-sm text-gray-600 mb-4">
{{ earliest_act.description }}
</p>
</div>
<div class="flex justify-end">
<span class="inline-flex items-center rounded-full bg-blue-100 px-3 py-1 text-xs font-medium text-blue-800 whitespace-nowrap">
<svg xmlns="http://www.w3.org/2000/svg" class="mr-1.5 h-3 w-3" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z" />
</svg>
{{ earliest_act.start_time|format_chinese_full_date }}
</span>
</div>
</div>
</div>
<!-- 中间的箭头 -->
<div class="bg-white rounded-full p-3 shadow-md z-20">
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6 text-blue-500" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M14 5l7 7m0 0l-7 7m7-7H3" />
</svg>
</div>
<!-- 第二个卡片 -->
<div class="w-[calc(50%-2rem)] bg-white rounded-lg shadow-lg overflow-hidden transition-all duration-300 hover:shadow-xl">
<div class="p-6 flex flex-col h-full">
<div class="flex-grow">
<div class="text-xs font-semibold text-blue-600 mb-2">最后活动</div>
<h3 class="text-xl font-bold mb-3 text-gray-800">{{ latest_act.branch }}{{ latest_act.name }}</h3>
<p class="text-sm text-gray-600 mb-4">
{{ latest_act.description }}
</p>
</div>
<div class="flex justify-end">
<span class="inline-flex items-center rounded-full bg-blue-100 px-3 py-1 text-xs font-medium text-blue-800 whitespace-nowrap">
<svg xmlns="http://www.w3.org/2000/svg" class="mr-1.5 h-3 w-3" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z" />
</svg>
{{ latest_act.start_time|format_chinese_full_date }} </span>
</div>
</div>
</div>
</div>
<!-- 装饰元素 -->
<div class="absolute top-1/2 left-0 w-4 h-4 bg-blue-500 rounded-full transform -translate-y-1/2 border-4 border-white"></div>
<div class="absolute top-1/2 right-0 w-4 h-4 bg-blue-500 rounded-full transform -translate-y-1/2 border-4 border-white"></div>
</div>
</div>
<table class="items-center bg-transparent w-full border-collapse ">
<thead>
<tr class="bg-gray-100">
<th class="px-6 bg-blueGray-50 text-blueGray-500 align-middle border border-solid border-blueGray-100 py-3 text-base uppercase border-l-0 border-r-0 whitespace-nowrap font-semibold text-left">
分支机构名称
<br>
新建-搬迁-装修
</th>
<th class="px-6 bg-blueGray-50 text-blueGray-500 align-middle border border-solid border-blueGray-100 py-3 text-base uppercase border-l-0 border-r-0 whitespace-nowrap font-semibold text-right">
活动的总数量
</th>
<th class="px-6 bg-blueGray-50 text-blueGray-500 align-middle border border-solid border-blueGray-100 py-3 text-base uppercase border-l-0 border-r-0 whitespace-nowrap font-semibold text-right">
活动进行中的数量
</th>
</tr>
</thead>
<tbody>
{% for item in branches %}
<!-- 这里是其他行的处理逻辑 -->
<tr class="{% if forloop.counter|divisibleby:2 %}bg-gray-100{% else %}bg-white{% endif %}">
<td class="px-6 py-4 whitespace-no-wrap text-left border-b border-gray-200">
<a href="{% url 'branch-detail' item.branch.pk %}"
class="bg-transparent hover:bg-blue-900 hover:text-white p-2 rounded-md">
{{ item.branch.name }}
</a>
</td>
<td class="px-6 py-4 whitespace-no-wrap border-b border-gray-200 text-right">
{{ item.total_count }}
</td>
<td class="px-6 py-4 whitespace-no-wrap border-b border-gray-200 text-right">
{{ item.onging_count }}
</td>
</tr>
{% endfor %}
<!-- 这里是最后一行的处理逻辑 -->
<tr>
<td class="px-6 py-4 whitespace-no-wrap text-left border-b border-gray-200 font-bold text-center">
总计: {{ total_branch_count }}
</td>
<td class="px-6 py-4 whitespace-no-wrap border-b border-gray-200 text-right">
{{ total_activities }}
</td>
<td class="px-6 py-4 whitespace-no-wrap border-b border-gray-200 text-right">
{{ ongoing_activities_count }}
</td>
</tr>
</tbody>
</table>
<hr class="h-px my-8 bg-red-500 border-1 dark:bg-red-700">
<!--插入事件-->
<div class="p-5 border border-gray-100 rounded-lg bg-gray-50 dark:bg-gray-800 dark:border-gray-700">
<time class="text-lg font-semibold text-gray-900 dark:text-white">事件</time>
<ol class="mt-3 divide-y divider-gray-200 dark:divide-gray-700">
{% for event in ongoing_events %}
<li>
<div href="#" class="items-center block p-3 sm:flex hover:bg-gray-100 dark:hover:bg-gray-700">
<div class="text-gray-600 dark:text-gray-400">
<span class="inline-flex items-center text-xs font-normal text-gray-400 dark:text-gray-400">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"
class="w-2.5 h-2.5 text-blue-800 dark:text-blue-300">
<path stroke-linecap="round" stroke-linejoin="round"
d="M15.75 9V5.25A2.25 2.25 0 0 0 13.5 3h-6a2.25 2.25 0 0 0-2.25 2.25v13.5A2.25 2.25 0 0 0 7.5 21h6a2.25 2.25 0 0 0 2.25-2.25V15m3 0 3-3m0 0-3-3m3 3H9"/>
</svg>
开始时间 {{ event.start_time| date:"Y年m月d日" }}
</span>
<div class="text-base font-normal">
<span class="font-medium text-gray-900 dark:text-white">
{% if not event.end_time %}
<span class="text-lg font-semibold text-red-900 dark:text-white">
{{ event.name }}-未完成
</span>
{% else %}
<span class="text-lg font-semibold text-blue-900 dark:text-white">
{{ event.name }}
</span>
{% endif %}
</span>
</div>
<div class="text-sm font-normal">
{{ event.description }}
</div>
<span class="inline-flex items-center text-xs font-normal text-gray-400 dark:text-gray-400">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"
class="w-2.5 h-2.5 text-blue-800 dark:text-blue-300">
<path stroke-linecap="round" stroke-linejoin="round"
d="M15.75 9V5.25A2.25 2.25 0 0 0 13.5 3h-6a2.25 2.25 0 0 0-2.25 2.25v13.5A2.25 2.25 0 0 0 7.5 21h6a2.25 2.25 0 0 0 2.25-2.25V15m3 0 3-3m0 0-3-3m3 3H9"/>
</svg>
结束时间 {{ event.end_time| date:"Y年m月d日" }}
</span>
</div>
</div>
</li>
{% endfor %}
</ol>
</div>
<hr class="h-px my-8 bg-red-500 border-1 dark:bg-red-700">
<div class="p-5 border border-gray-100 rounded-lg bg-gray-50 dark:bg-gray-800 dark:border-gray-700">
<time class="text-lg font-semibold text-gray-900 dark:text-white">活动中的</time>
<ol class="mt-3 divide-y divider-gray-200 dark:divide-gray-700">
{% for huodong in ongoing_activities %}
<li>
<div href="#" class="items-center block p-3 sm:flex hover:bg-gray-100 dark:hover:bg-gray-700">
<div class="text-gray-600 dark:text-gray-400">
<span class="inline-flex items-center text-xs font-normal text-gray-400 dark:text-gray-400">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"
class="w-2.5 h-2.5 text-blue-800 dark:text-blue-300">
<path stroke-linecap="round" stroke-linejoin="round"
d="M15.75 9V5.25A2.25 2.25 0 0 0 13.5 3h-6a2.25 2.25 0 0 0-2.25 2.25v13.5A2.25 2.25 0 0 0 7.5 21h6a2.25 2.25 0 0 0 2.25-2.25V15m3 0 3-3m0 0-3-3m3 3H9"/>
</svg>
开始时间 {{ huodong.start_time| date:"Y年m月d日" }}
</span>
<div class="text-base font-normal">
<span class="font-medium text-gray-900 dark:text-white">
{% if not huodong.end_time %}
<span class="text-lg font-semibold text-red-900 dark:text-white">
<a href="{% url 'branch-detail' huodong.branch.pk %}"
class="bg-transparent hover:bg-blue-900 hover:text-white p-2 rounded-md">
{{ huodong.branch }}
</a>
{{ huodong.name }}-未完成
</span>
{% endif %}
</span>
</div>
<div class="text-sm font-normal">
{{ huodong.description }}
</div>
</div>
</div>
</li>
{% endfor %}
</ol>
</div>
<hr class="h-px my-8 bg-gray-200 border-0 dark:bg-gray-700">
{% for scope, scope_data in grouped_activities.items %}
<div class="p-5 border border-gray-100 rounded-lg dark:bg-gray-800 dark:border-gray-700">
<time class="text-lg font-semibold text-gray-900 dark:text-white">今年的{{ scope }}(共{{ scope_data.branch_count }}个分支机构)</time>
<ol class="mt-3 divide-y divider-gray-200 dark:divide-gray-700">
{% for activity in scope_data.activities %}
<li style= "background-color: {{ activity.branch.background_color }};" >
<div class="items-center block p-3 sm:flex hover:bg-gray-100 dark:hover:bg-gray-700">
<div class="text-gray-600 dark:text-gray-400">
<span class="inline-flex items-center text-xs font-normal text-gray-400 dark:text-gray-400">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5"
stroke="currentColor"
class="w-2.5 h-2.5 text-blue-800 dark:text-blue-300">
<path stroke-linecap="round" stroke-linejoin="round"
d="M15.75 9V5.25A2.25 2.25 0 0 0 13.5 3h-6a2.25 2.25 0 0 0-2.25 2.25v13.5A2.25 2.25 0 0 0 7.5 21h6a2.25 2.25 0 0 0 2.25-2.25V15m3 0 3-3m0 0-3-3m3 3H9"/>
</svg>
开始时间 {{ activity.start_time| date:"Y年m月d日" }}
</span>
<div class="text-base font-normal">
<span class="font-medium text-gray-900 dark:text-white">
{% if not activity.end_time %}
<span class="text-lg font-semibold text-red-900 dark:text-white">
<a href="{% url 'branch-detail' activity.branch.pk %}"
class="bg-transparent hover:bg-blue-900 hover:text-white p-2 rounded-md">
{{ activity.branch }}
</a>
{{ activity.name }}-未完成
</span>
{% else %}
<span class="text-lg font-semibold text-gray-900 dark:text-white">
<a href="{% url 'branch-detail' activity.branch.pk %}"
class="bg-transparent hover:bg-blue-900 hover:text-white p-2 rounded-md">
{{ activity.branch }}
</a>
{{ activity.name }}
</span>
{% endif %}
</span>
</div>
<div class="text-sm font-normal">
{{ activity.description }}
</div>
<span class="inline-flex items-center text-xs font-normal text-gray-400 dark:text-gray-400">
<svg class="w-2.5 h-2.5 text-blue-800 dark:text-blue-300" aria-hidden="true" xmlns="http://www.w3.org/2000/svg"
fill="currentColor" viewBox="0 0 20 20">
<path d="M20 4a2 2 0 0 0-2-2h-2V1a1 1 0 0 0-2 0v1h-3V1a1 1 0 0 0-2 0v1H6V1a1 1 0 0 0-2 0v1H2a2 2 0 0 0-2 2v2h20V4ZM0 18a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2V8H0v10Zm5-8h10a1 1 0 0 1 0 2H5a1 1 0 0 1 0-2Z"/>
</svg>
结束时间 {{ activity.end_time| date:"Y年m月d日" }}
</span>
</div>
</div>
</li>
{% endfor %}
</ol>
</div>
<hr class="h-px my-8 bg-gray-200 border-0 dark:bg-gray-700">
{% endfor %}
<time class="text-lg font-semibold text-gray-900 dark:text-white">
今年的
{% for scope, scope_data in grouped_activities.items %}
{{ scope }}{{ scope_data.branch_count }}个{% if forloop.last %}。{% else %}{% endif %}
{% endfor %}
</time>
<hr class="h-px my-8 bg-gray-200 border-0 dark:bg-gray-700">
<!-- 历年活动统计 -->
{% for year in historical_years %}
{% with year_data=historical_grouped_activities|get_item:year %}
{% if year_data %}
<time class="text-lg font-semibold text-gray-900 dark:text-white">
{{ year }}年的
{% for scope, scope_data in year_data.items %}
{{ scope }}{{ scope_data.branch_count }}个{% if forloop.last %}。{% else %}{% endif %}
{% endfor %}
</time>
<hr class="h-px my-8 bg-gray-200 border-0 dark:bg-gray-700">
{% endif %}
{% endwith %}
{% endfor %}
{% endblock %}
{% extends 'base.html' %}
{% load custom_filters %}
{% block content %}
<div class="w-full max-w-4xl mx-auto p-6">
<div class="relative">
<!-- 时间线 -->
<div class="absolute top-1/2 left-0 right-0 h-0.5 bg-blue-500 transform -translate-y-1/2"></div>
<div class="relative z-10 flex justify-between items-center">
<!-- 第一个卡片 -->
<div class="w-[calc(50%-2rem)] bg-white rounded-lg shadow-lg overflow-hidden transition-all duration-300 hover:shadow-xl">
<div class="p-6 flex flex-col h-full">
<div class="flex-grow">
<div class="text-xs font-semibold text-blue-600 mb-2">最初活动</div>
<h3 class="text-xl font-bold mb-3 text-gray-800">{{ earliest_act.branch }}{{ earliest_act.name }}</h3>
<p class="text-sm text-gray-600 mb-4">
{{ earliest_act.description }}
</p>
</div>
<div class="flex justify-end">
<span class="inline-flex items-center rounded-full bg-blue-100 px-3 py-1 text-xs font-medium text-blue-800 whitespace-nowrap">
<svg xmlns="http://www.w3.org/2000/svg" class="mr-1.5 h-3 w-3" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z" />
</svg>
{{ earliest_act.start_time|format_chinese_full_date }}
</span>
</div>
</div>
</div>
<!-- 中间的箭头 -->
<div class="bg-white rounded-full p-3 shadow-md z-20">
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6 text-blue-500" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M14 5l7 7m0 0l-7 7m7-7H3" />
</svg>
</div>
<!-- 第二个卡片 -->
<div class="w-[calc(50%-2rem)] bg-white rounded-lg shadow-lg overflow-hidden transition-all duration-300 hover:shadow-xl">
<div class="p-6 flex flex-col h-full">
<div class="flex-grow">
<div class="text-xs font-semibold text-blue-600 mb-2">最后活动</div>
<h3 class="text-xl font-bold mb-3 text-gray-800">{{ latest_act.branch }}{{ latest_act.name }}</h3>
<p class="text-sm text-gray-600 mb-4">
{{ latest_act.description }}
</p>
</div>
<div class="flex justify-end">
<span class="inline-flex items-center rounded-full bg-blue-100 px-3 py-1 text-xs font-medium text-blue-800 whitespace-nowrap">
<svg xmlns="http://www.w3.org/2000/svg" class="mr-1.5 h-3 w-3" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z" />
</svg>
{{ latest_act.start_time|format_chinese_full_date }} </span>
</div>
</div>
</div>
</div>
<!-- 装饰元素 -->
<div class="absolute top-1/2 left-0 w-4 h-4 bg-blue-500 rounded-full transform -translate-y-1/2 border-4 border-white"></div>
<div class="absolute top-1/2 right-0 w-4 h-4 bg-blue-500 rounded-full transform -translate-y-1/2 border-4 border-white"></div>
</div>
</div>
<table class="items-center bg-transparent w-full border-collapse ">
<thead>
<tr class="bg-gray-100">
<th class="px-6 bg-blueGray-50 text-blueGray-500 align-middle border border-solid border-blueGray-100 py-3 text-base uppercase border-l-0 border-r-0 whitespace-nowrap font-semibold text-left">
分支机构名称
<br>
新建-搬迁-装修
</th>
<th class="px-6 bg-blueGray-50 text-blueGray-500 align-middle border border-solid border-blueGray-100 py-3 text-base uppercase border-l-0 border-r-0 whitespace-nowrap font-semibold text-right">
活动的总数量
</th>
<th class="px-6 bg-blueGray-50 text-blueGray-500 align-middle border border-solid border-blueGray-100 py-3 text-base uppercase border-l-0 border-r-0 whitespace-nowrap font-semibold text-right">
活动进行中的数量
</th>
</tr>
</thead>
<tbody>
{% for item in branches %}
<!-- 这里是其他行的处理逻辑 -->
<tr class="{% if forloop.counter|divisibleby:2 %}bg-gray-100{% else %}bg-white{% endif %}">
<td class="px-6 py-4 whitespace-no-wrap text-left border-b border-gray-200">
<a href="{% url 'branch-detail' item.branch.pk %}"
class="bg-transparent hover:bg-blue-900 hover:text-white p-2 rounded-md">
{{ item.branch.name }}
</a>
</td>
<td class="px-6 py-4 whitespace-no-wrap border-b border-gray-200 text-right">
{{ item.total_count }}
</td>
<td class="px-6 py-4 whitespace-no-wrap border-b border-gray-200 text-right">
{{ item.onging_count }}
</td>
</tr>
{% endfor %}
<!-- 这里是最后一行的处理逻辑 -->
<tr>
<td class="px-6 py-4 whitespace-no-wrap text-left border-b border-gray-200 font-bold text-center">
总计: {{ total_branch_count }}
</td>
<td class="px-6 py-4 whitespace-no-wrap border-b border-gray-200 text-right">
{{ total_activities }}
</td>
<td class="px-6 py-4 whitespace-no-wrap border-b border-gray-200 text-right">
{{ ongoing_activities_count }}
</td>
</tr>
</tbody>
</table>
<hr class="h-px my-8 bg-red-500 border-1 dark:bg-red-700">
<!--插入事件-->
<div class="p-5 border border-gray-100 rounded-lg bg-gray-50 dark:bg-gray-800 dark:border-gray-700">
<time class="text-lg font-semibold text-gray-900 dark:text-white">事件</time>
<ol class="mt-3 divide-y divider-gray-200 dark:divide-gray-700">
{% for event in ongoing_events %}
<li>
<div href="#" class="items-center block p-3 sm:flex hover:bg-gray-100 dark:hover:bg-gray-700">
<div class="text-gray-600 dark:text-gray-400">
<span class="inline-flex items-center text-xs font-normal text-gray-400 dark:text-gray-400">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"
class="w-2.5 h-2.5 text-blue-800 dark:text-blue-300">
<path stroke-linecap="round" stroke-linejoin="round"
d="M15.75 9V5.25A2.25 2.25 0 0 0 13.5 3h-6a2.25 2.25 0 0 0-2.25 2.25v13.5A2.25 2.25 0 0 0 7.5 21h6a2.25 2.25 0 0 0 2.25-2.25V15m3 0 3-3m0 0-3-3m3 3H9"/>
</svg>
开始时间 {{ event.start_time| date:"Y年m月d日" }}
</span>
<div class="text-base font-normal">
<span class="font-medium text-gray-900 dark:text-white">
{% if not event.end_time %}
<span class="text-lg font-semibold text-red-900 dark:text-white">
{{ event.name }}-未完成
</span>
{% else %}
<span class="text-lg font-semibold text-blue-900 dark:text-white">
{{ event.name }}
</span>
{% endif %}
</span>
</div>
<div class="text-sm font-normal">
{{ event.description }}
</div>
<div class="text-sm font-normal">
{% with branch_count=event.branches.count %}
{% if branch_count == all_branch_count %}
涉及分支机构:全辖分支机构
{% elif branch_count == 1 %}
涉及分支机构:{{ event.branches.first.name }}
{% elif branch_count > 1 %}
涉及分支机构:{{ event.branches.all.0.name }}、{{ event.branches.all.1.name }}等{{ branch_count }}个营业部
{% else %}
涉及分支机构:暂无
{% endif %}
{% endwith %}
</div>
<span class="inline-flex items-center text-xs font-normal text-gray-400 dark:text-gray-400">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"
class="w-2.5 h-2.5 text-blue-800 dark:text-blue-300">
<path stroke-linecap="round" stroke-linejoin="round"
d="M15.75 9V5.25A2.25 2.25 0 0 0 13.5 3h-6a2.25 2.25 0 0 0-2.25 2.25v13.5A2.25 2.25 0 0 0 7.5 21h6a2.25 2.25 0 0 0 2.25-2.25V15m3 0 3-3m0 0-3-3m3 3H9"/>
</svg>
结束时间 {{ event.end_time| date:"Y年m月d日" }}
</span>
</div>
</div>
</li>
{% endfor %}
</ol>
</div>
<hr class="h-px my-8 bg-red-500 border-1 dark:bg-red-700">
<div class="p-5 border border-gray-100 rounded-lg bg-gray-50 dark:bg-gray-800 dark:border-gray-700">
<time class="text-lg font-semibold text-gray-900 dark:text-white">活动中的</time>
<ol class="mt-3 divide-y divider-gray-200 dark:divide-gray-700">
{% for huodong in ongoing_activities %}
<li>
<div href="#" class="items-center block p-3 sm:flex hover:bg-gray-100 dark:hover:bg-gray-700">
<div class="text-gray-600 dark:text-gray-400">
<span class="inline-flex items-center text-xs font-normal text-gray-400 dark:text-gray-400">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"
class="w-2.5 h-2.5 text-blue-800 dark:text-blue-300">
<path stroke-linecap="round" stroke-linejoin="round"
d="M15.75 9V5.25A2.25 2.25 0 0 0 13.5 3h-6a2.25 2.25 0 0 0-2.25 2.25v13.5A2.25 2.25 0 0 0 7.5 21h6a2.25 2.25 0 0 0 2.25-2.25V15m3 0 3-3m0 0-3-3m3 3H9"/>
</svg>
开始时间 {{ huodong.start_time| date:"Y年m月d日" }}
</span>
<div class="text-base font-normal">
<span class="font-medium text-gray-900 dark:text-white">
{% if not huodong.end_time %}
<span class="text-lg font-semibold text-red-900 dark:text-white">
<a href="{% url 'branch-detail' huodong.branch.pk %}"
class="bg-transparent hover:bg-blue-900 hover:text-white p-2 rounded-md">
{{ huodong.branch }}
</a>
{{ huodong.name }}-未完成
</span>
{% endif %}
</span>
</div>
<div class="text-sm font-normal">
{{ huodong.description }}
</div>
</div>
</div>
</li>
{% endfor %}
</ol>
</div>
<hr class="h-px my-8 bg-gray-200 border-0 dark:bg-gray-700">
{% for scope, scope_data in grouped_activities.items %}
<div class="p-5 border border-gray-100 rounded-lg dark:bg-gray-800 dark:border-gray-700">
<time class="text-lg font-semibold text-gray-900 dark:text-white">今年的{{ scope }}(共{{ scope_data.branch_count }}个分支机构)</time>
<ol class="mt-3 divide-y divider-gray-200 dark:divide-gray-700">
{% for activity in scope_data.activities %}
<li style= "background-color: {{ activity.branch.background_color }};" >
<div class="items-center block p-3 sm:flex hover:bg-gray-100 dark:hover:bg-gray-700">
<div class="text-gray-600 dark:text-gray-400">
<span class="inline-flex items-center text-xs font-normal text-gray-400 dark:text-gray-400">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5"
stroke="currentColor"
class="w-2.5 h-2.5 text-blue-800 dark:text-blue-300">
<path stroke-linecap="round" stroke-linejoin="round"
d="M15.75 9V5.25A2.25 2.25 0 0 0 13.5 3h-6a2.25 2.25 0 0 0-2.25 2.25v13.5A2.25 2.25 0 0 0 7.5 21h6a2.25 2.25 0 0 0 2.25-2.25V15m3 0 3-3m0 0-3-3m3 3H9"/>
</svg>
开始时间 {{ activity.start_time| date:"Y年m月d日" }}
</span>
<div class="text-base font-normal">
<span class="font-medium text-gray-900 dark:text-white">
{% if not activity.end_time %}
<span class="text-lg font-semibold text-red-900 dark:text-white">
<a href="{% url 'branch-detail' activity.branch.pk %}"
class="bg-transparent hover:bg-blue-900 hover:text-white p-2 rounded-md">
{{ activity.branch }}
</a>
{{ activity.name }}-未完成
</span>
{% else %}
<span class="text-lg font-semibold text-gray-900 dark:text-white">
<a href="{% url 'branch-detail' activity.branch.pk %}"
class="bg-transparent hover:bg-blue-900 hover:text-white p-2 rounded-md">
{{ activity.branch }}
</a>
{{ activity.name }}
</span>
{% endif %}
</span>
</div>
<div class="text-sm font-normal">
{{ activity.description }}
</div>
<span class="inline-flex items-center text-xs font-normal text-gray-400 dark:text-gray-400">
<svg class="w-2.5 h-2.5 text-blue-800 dark:text-blue-300" aria-hidden="true" xmlns="http://www.w3.org/2000/svg"
fill="currentColor" viewBox="0 0 20 20">
<path d="M20 4a2 2 0 0 0-2-2h-2V1a1 1 0 0 0-2 0v1h-3V1a1 1 0 0 0-2 0v1H6V1a1 1 0 0 0-2 0v1H2a2 2 0 0 0-2 2v2h20V4ZM0 18a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2V8H0v10Zm5-8h10a1 1 0 0 1 0 2H5a1 1 0 0 1 0-2Z"/>
</svg>
结束时间 {{ activity.end_time| date:"Y年m月d日" }}
</span>
</div>
</div>
</li>
{% endfor %}
</ol>
</div>
<hr class="h-px my-8 bg-gray-200 border-0 dark:bg-gray-700">
{% endfor %}
<time class="text-lg font-semibold text-gray-900 dark:text-white">
今年的
{% for scope, scope_data in grouped_activities.items %}
{{ scope }}{{ scope_data.branch_count }}个{% if forloop.last %}。{% else %}{% endif %}
{% endfor %}
</time>
<hr class="h-px my-8 bg-gray-200 border-0 dark:bg-gray-700">
<!-- 历年活动统计 -->
{% for year in historical_years %}
{% with year_data=historical_grouped_activities|get_item:year %}
{% if year_data %}
<time class="text-lg font-semibold text-gray-900 dark:text-white">
{{ year }}年的
{% for scope, scope_data in year_data.items %}
{{ scope }}{{ scope_data.branch_count }}个{% if forloop.last %}。{% else %}{% endif %}
{% endfor %}
</time>
<hr class="h-px my-8 bg-gray-200 border-0 dark:bg-gray-700">
{% endif %}
{% endwith %}
{% endfor %}
{% endblock %}

View File

@@ -1,145 +1,149 @@
{% extends 'base.html' %}
{% block content %}
<!-- Component starts here -->
<h2 class="flex flex-row flex-nowrap items-center my-8">
<span class="flex-grow block border-t border-black" aria-hidden="true" role="presentation"></span>
<span class="flex-none block mx-4 px-4 py-2.5 text-base leading-none font-medium uppercase bg-black text-white">
{{ branch.name }}
</span>
<span class="flex-grow block border-t border-black" aria-hidden="true" role="presentation"></span>
</h2>
<!-- Component ends here -->
<div class="bg-white rounded-lg shadow-md p-4 mb-6">
<h2 class="text-2xl font-bold mb-4">基本信息</h2>
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<div>
<p class="text-gray-700"><strong>信息系统分类:</strong> {{ branch.category|default:"暂无" }}</p>
<p class="text-gray-700"><strong>联系人:</strong> {{ branch.contact_person|default:"暂无" }}</p>
<p class="text-gray-700"><strong>联系电话:</strong> {{ branch.contact_phone|default:"暂无" }}</p>
</div>
<div>
<p class="text-gray-700"><strong>地址:</strong> {{ branch.address|default:"暂无" }}</p>
<p class="text-gray-700"><strong>成立时间:</strong> {{ branch.established_date|date:"Y-m-d"|default:"暂无" }}</p>
</div>
</div>
</div>
<!-- 事件信息 -->
<div class="bg-white rounded-lg shadow-md p-4 mb-6">
<h2 class="text-2xl font-bold mb-4">关联事件</h2>
{% if events %}
<div class="space-y-3">
{% for event in events %}
<div class="border-l-4 border-blue-500 pl-4 py-2 bg-blue-50 rounded">
<h3 class="font-semibold text-lg">{{ event.name }}</h3>
<p class="text-sm text-gray-600">
{{ event.start_time|date:"Y-m-d H:i" }} - {{ event.end_time|date:"Y-m-d H:i"|default:"进行中" }}
</p>
<p class="mt-1 text-gray-700">{{ event.description|truncatechars:100 }}</p>
</div>
{% endfor %}
</div>
{% else %}
<p class="text-gray-500 italic">暂无事件记录</p>
{% endif %}
</div>
<!-- 设备间图片 -->
<div class="bg-white rounded-lg shadow-md p-4 mb-6">
<h2 class="text-2xl font-bold mb-4">设备间图片</h2>
{% if equipment_images %}
<div class="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 gap-4">
{% for image in equipment_images %}
<div class="overflow-hidden rounded-lg shadow">
<img src="{{ image.image.url }}" alt="设备间图片" class="w-full h-48 object-cover">
<div class="p-2 bg-gray-50">
<p class="text-sm text-gray-700">{{ image.description|default:"无描述" }}</p>
<p class="text-xs text-gray-500 mt-1">{{ image.upload_date|date:"Y-m-d" }}</p>
</div>
</div>
{% endfor %}
</div>
{% else %}
<p class="text-gray-500 italic">暂无设备间图片</p>
{% endif %}
</div>
<!-- 公共电子屏 -->
<div class="bg-white rounded-lg shadow-md p-4 mb-6">
<h2 class="text-2xl font-bold mb-4">公共电子屏</h2>
{% if public_screens %}
<div class="overflow-x-auto">
<table class="min-w-full divide-y divide-gray-200">
<thead class="bg-gray-50">
<tr>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">类型</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">描述</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">最后演练</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">更新时间</th>
</tr>
</thead>
<tbody class="bg-white divide-y divide-gray-200">
{% for screen in public_screens %}
<tr>
<td class="px-6 py-4 whitespace-nowrap">{{ screen.get_screen_type_display }}</td>
<td class="px-6 py-4">{{ screen.description|default:"无" }}</td>
<td class="px-6 py-4 whitespace-nowrap">{{ screen.last_drill.date|date:"Y-m-d"|default:"未演练" }}</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">{{ screen.updated_at|date:"Y-m-d H:i" }}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% else %}
<p class="text-gray-500 italic">暂无公共电子屏信息</p>
{% endif %}
</div>
{% for scope, activities in grouped_activities.items %}
<div class="p-5 border border-gray-100 rounded-lg bg-gray-50 dark:bg-gray-800 dark:border-gray-700">
<time class="text-lg font-semibold text-gray-900 dark:text-white">{{ scope }}</time>
<ol class="mt-3 divide-y divider-gray-200 dark:divide-gray-700">
{% for huodong in activities %}
<li style="background-color: {{ huodong.branch.background_color }};">
<div class="items-center block p-3 sm:flex hover:bg-gray-100 dark:hover:bg-gray-700">
<div class="text-gray-600 dark:text-gray-400">
<span class="inline-flex items-center text-xs font-normal text-gray-400 dark:text-gray-400">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-2.5 h-2.5 text-blue-800 dark:text-blue-300">
<path stroke-linecap="round" stroke-linejoin="round" d="M15.75 9V5.25A2.25 2.25 0 0 0 13.5 3h-6a2.25 2.25 0 0 0-2.25 2.25v13.5A2.25 2.25 0 0 0 7.5 21h6a2.25 2.25 0 0 0 2.25-2.25V15m3 0 3-3m0 0-3-3m3 3H9" />
</svg>
开始时间 {{ huodong.start_time| date:"Y年m月d日" }}
</span>
<div class="text-base font-normal">
<span class="font-medium text-gray-900 dark:text-white">
{% if not huodong.end_time %}
<span class="text-lg font-semibold text-red-900 dark:text-white">
{{ huodong.name }}-未完成
</span>
{% else %}
<span class="text-lg font-semibold text-gray-900 dark:text-white">
{{ huodong.name }}
</span>
{% endif %}
</span>
</div>
<div class="text-sm font-normal">
{{ huodong.description }}
</div>
<span class="inline-flex items-center text-xs font-normal text-gray-400 dark:text-gray-400">
<svg class="w-2.5 h-2.5 text-blue-800 dark:text-blue-300" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 20 20">
<path d="M20 4a2 2 0 0 0-2-2h-2V1a1 1 0 0 0-2 0v1h-3V1a1 1 0 0 0-2 0v1H6V1a1 1 0 0 0-2 0v1H2a2 2 0 0 0-2 2v2h20V4ZM0 18a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2V8H0v10Zm5-8h10a1 1 0 0 1 0 2H5a1 1 0 0 1 0-2Z"/>
</svg>
结束时间 {{ huodong.end_time| date:"Y年m月d日" }}
</span>
</div>
</div>
</li>
{% endfor %}
</ol>
</div>
<hr class="h-px my-8 bg-gray-200 border-0 dark:bg-gray-700">
{% endfor %}
{% endblock %}
{% extends 'base.html' %}
{% block content %}
<!-- Component starts here -->
<h2 class="flex flex-row flex-nowrap items-center my-8">
<span class="flex-grow block border-t border-black" aria-hidden="true" role="presentation"></span>
<span class="flex-none block mx-4 px-4 py-2.5 text-base leading-none font-medium uppercase bg-black text-white">
{{ branch.name }}
</span>
<span class="flex-grow block border-t border-black" aria-hidden="true" role="presentation"></span>
</h2>
<!-- Component ends here -->
<div class="bg-white rounded-lg shadow-md p-4 mb-6">
<h2 class="text-2xl font-bold mb-4">基本信息</h2>
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<div>
<p class="text-gray-700"><strong>信息系统分类:</strong> {{ branch.category|default:"暂无" }}</p>
<p class="text-gray-700"><strong>联系人:</strong> {{ branch.contact_person|default:"暂无" }}</p>
<p class="text-gray-700"><strong>联系电话:</strong> {{ branch.contact_phone|default:"暂无" }}</p>
</div>
<div>
<p class="text-gray-700"><strong>地址:</strong> {{ branch.address|default:"暂无" }}</p>
<p class="text-gray-700"><strong>成立时间:</strong> {{ branch.established_date|date:"Y-m-d"|default:"暂无" }}</p>
</div>
</div>
</div>
<!-- 事件信息 -->
<div class="bg-white rounded-lg shadow-md p-4 mb-6">
<h2 class="text-2xl font-bold mb-4">关联事件</h2>
{% if events %}
<div class="space-y-3">
{% for event in events %}
<div class="border-l-4 border-blue-500 pl-4 py-2 bg-blue-50 rounded">
<h3 class="font-semibold text-lg">{{ event.name }}</h3>
<p class="text-sm text-gray-600">
{% if event.end_time %}
开始日期{{ event.start_time|date:"Y年m月d日" }} 结束日期{{ event.end_time|date:"Y年m月d日" }}
{% else %}
开始日期{{ event.start_time|date:"Y年m月d日" }},现在进行中
{% endif %}
</p>
<p class="mt-1 text-gray-700">{{ event.description|truncatechars:100 }}</p>
</div>
{% endfor %}
</div>
{% else %}
<p class="text-gray-500 italic">暂无事件记录</p>
{% endif %}
</div>
<!-- 设备间图片 -->
<div class="bg-white rounded-lg shadow-md p-4 mb-6">
<h2 class="text-2xl font-bold mb-4">设备间图片</h2>
{% if equipment_images %}
<div class="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 gap-4">
{% for image in equipment_images %}
<div class="overflow-hidden rounded-lg shadow">
<img src="{{ image.image.url }}" alt="设备间图片" class="w-full h-48 object-cover">
<div class="p-2 bg-gray-50">
<p class="text-sm text-gray-700">{{ image.description|default:"无描述" }}</p>
<p class="text-xs text-gray-500 mt-1">{{ image.upload_date|date:"Y-m-d" }}</p>
</div>
</div>
{% endfor %}
</div>
{% else %}
<p class="text-gray-500 italic">暂无设备间图片</p>
{% endif %}
</div>
<!-- 公共电子屏 -->
<div class="bg-white rounded-lg shadow-md p-4 mb-6">
<h2 class="text-2xl font-bold mb-4">公共电子屏</h2>
{% if public_screens %}
<div class="overflow-x-auto">
<table class="min-w-full divide-y divide-gray-200">
<thead class="bg-gray-50">
<tr>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">类型</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">描述</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">最后演练</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">更新时间</th>
</tr>
</thead>
<tbody class="bg-white divide-y divide-gray-200">
{% for screen in public_screens %}
<tr>
<td class="px-6 py-4 whitespace-nowrap">{{ screen.get_screen_type_display }}</td>
<td class="px-6 py-4">{{ screen.description|default:"无" }}</td>
<td class="px-6 py-4 whitespace-nowrap">{{ screen.last_drill.date|date:"Y-m-d"|default:"未演练" }}</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">{{ screen.updated_at|date:"Y-m-d H:i" }}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% else %}
<p class="text-gray-500 italic">暂无公共电子屏信息</p>
{% endif %}
</div>
{% for scope, activities in grouped_activities.items %}
<div class="p-5 border border-gray-100 rounded-lg bg-gray-50 dark:bg-gray-800 dark:border-gray-700">
<time class="text-lg font-semibold text-gray-900 dark:text-white">{{ scope }}</time>
<ol class="mt-3 divide-y divider-gray-200 dark:divide-gray-700">
{% for huodong in activities %}
<li style="background-color: {{ huodong.branch.background_color }};">
<div class="items-center block p-3 sm:flex hover:bg-gray-100 dark:hover:bg-gray-700">
<div class="text-gray-600 dark:text-gray-400">
<span class="inline-flex items-center text-xs font-normal text-gray-400 dark:text-gray-400">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-2.5 h-2.5 text-blue-800 dark:text-blue-300">
<path stroke-linecap="round" stroke-linejoin="round" d="M15.75 9V5.25A2.25 2.25 0 0 0 13.5 3h-6a2.25 2.25 0 0 0-2.25 2.25v13.5A2.25 2.25 0 0 0 7.5 21h6a2.25 2.25 0 0 0 2.25-2.25V15m3 0 3-3m0 0-3-3m3 3H9" />
</svg>
开始时间 {{ huodong.start_time| date:"Y年m月d日" }}
</span>
<div class="text-base font-normal">
<span class="font-medium text-gray-900 dark:text-white">
{% if not huodong.end_time %}
<span class="text-lg font-semibold text-red-900 dark:text-white">
{{ huodong.name }}-未完成
</span>
{% else %}
<span class="text-lg font-semibold text-gray-900 dark:text-white">
{{ huodong.name }}
</span>
{% endif %}
</span>
</div>
<div class="text-sm font-normal">
{{ huodong.description }}
</div>
<span class="inline-flex items-center text-xs font-normal text-gray-400 dark:text-gray-400">
<svg class="w-2.5 h-2.5 text-blue-800 dark:text-blue-300" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 20 20">
<path d="M20 4a2 2 0 0 0-2-2h-2V1a1 1 0 0 0-2 0v1h-3V1a1 1 0 0 0-2 0v1H6V1a1 1 0 0 0-2 0v1H2a2 2 0 0 0-2 2v2h20V4ZM0 18a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2V8H0v10Zm5-8h10a1 1 0 0 1 0 2H5a1 1 0 0 1 0-2Z"/>
</svg>
结束时间 {{ huodong.end_time| date:"Y年m月d日" }}
</span>
</div>
</div>
</li>
{% endfor %}
</ol>
</div>
<hr class="h-px my-8 bg-gray-200 border-0 dark:bg-gray-700">
{% endfor %}
{% endblock %}

View File

@@ -1,441 +1,444 @@
from rest_framework import viewsets
from .models import Branch, Activity, Evaluation, Event, VideoTerminal
from .serializers import BranchSerializer, ActivitySerializer, EvaluationSerializer
from django.shortcuts import render, redirect
from .models import PublicScreen
from collections import defaultdict
from datetime import datetime
from django.db.models import Q
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
from .models import Contact
from django.template.defaulttags import register
from django.http import HttpResponse
import openpyxl
from reportlab.lib import colors
from reportlab.lib.pagesizes import letter
from reportlab.platypus import SimpleDocTemplate, Table, TableStyle, Paragraph
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
import os
@register.filter
def get_item(dictionary, key):
return dictionary.get(key)
class BranchViewSet(viewsets.ModelViewSet):
queryset = Branch.objects.all()
serializer_class = BranchSerializer
class ActivityViewSet(viewsets.ModelViewSet):
queryset = Activity.objects.all()
serializer_class = ActivitySerializer
class EvaluationViewSet(viewsets.ModelViewSet):
queryset = Evaluation.objects.all()
serializer_class = EvaluationSerializer
# 在页面上显示id为branch的详细信息
def branch_detail(request, branch_id):
# 获取分支机构及其所有关联数据
branch = Branch.objects.get(pk=branch_id)
# 获取并分组活动
activities = Activity.objects.filter(branch=branch).order_by('start_time')
grouped_activities = defaultdict(list)
for activity in activities:
grouped_activities[activity.scope].append(activity)
grouped_activities = dict(grouped_activities)
# 获取其他关联数据
events = branch.events.all().order_by('-start_time')
equipment_images = branch.equipment_images.all()
public_screens = branch.public_screens.all()
# 准备上下文数据
context = {
'branch': branch,
'grouped_activities': grouped_activities,
'events': events,
'equipment_images': equipment_images,
'public_screens': public_screens,
}
return render(request, 'branch_detail.html', context)
# 在页面上显示所有的branch以及active的数量首页显示
def BranchAll(request):
branches = Branch.objects.exclude(activity__isnull=True).order_by('name')
branches_with_counts = [{'branch': branch,
'total_count': Activity.objects.filter(branch=branch).count(),
'onging_count': Activity.objects.filter(branch=branch, end_time__isnull=True).count()}
for branch in branches]
# 返回总数
total_branch_count = len(branches_with_counts)
# 比如说end_time()的year是2024年但是now()是2025年的2月fileter出来但是如果now()是2025年3月则不显示
now = datetime.now()
print(f"当前月份是{now.month},年是{now.year}")
if now.month <= 2:
two_months_ago_year = now.year - 1
activities = Activity.objects.filter(
Q(start_time__year=now.year) | Q(start_time__year=two_months_ago_year)).order_by('branch')
else:
activities = Activity.objects.filter(start_time__year=now.year).order_by('branch')
# 返回起止时间
earliest_act = activities.order_by('start_time').first()
print(earliest_act)
latest_act = activities.order_by('start_time').last()
print(latest_act)
# 确保earliest_act和latest_act不为None时才使用
# 按activities按照scope分组
grouped_activities = defaultdict(lambda: {'activities': [], 'branch_count': 0})
for activity in activities:
grouped_activities[activity.scope]['activities'].append(activity)
# 计算每个scope的分支机构数量
for scope_data in grouped_activities.values():
activity_ids = [a.id for a in scope_data['activities']]
scope_data['branch_count'] = Activity.objects.filter(id__in=activity_ids).values('branch').distinct().count()
# 转换为普通字典以便模板遍历
grouped_activities = dict(grouped_activities)
# 统计活动中的事件即end_time为空的活动
ongoing_activities = Activity.objects.filter(end_time__isnull=True).order_by('branch','start_time')
# 统计活动的总的数量
total_activities = Activity.objects.count()
# 统计活动中的事件的总数
ongoing_activities_count = ongoing_activities.count()
branch_count = activities.values('branch').distinct().count()
# 事件的展示
ongoing_events = Event.objects.all().order_by('start_time')
# 获取历年活动数据
historical_years = []
historical_grouped_activities = {}
# 获取最早的年份
earliest_activity = Activity.objects.order_by('start_time').first()
if earliest_activity:
earliest_year = earliest_activity.start_time.year
current_year = datetime.now().year
# 从去年开始,逐年递减,直到最早的年份
for year in range(current_year - 1, earliest_year - 1, -1):
# 获取该年份的活动
year_activities = Activity.objects.filter(start_time__year=year).order_by('branch')
# 如果该年份有活动数据
if year_activities.exists():
historical_years.append(year)
# 按照scope分组
year_grouped_activities = defaultdict(lambda: {'activities': [], 'branch_count': 0})
for activity in year_activities:
year_grouped_activities[activity.scope]['activities'].append(activity)
# 计算每个scope的分支机构数量
for scope_data in year_grouped_activities.values():
activity_ids = [a.id for a in scope_data['activities']]
scope_data['branch_count'] = Activity.objects.filter(id__in=activity_ids).values('branch').distinct().count()
# 转换为普通字典
historical_grouped_activities[year] = dict(year_grouped_activities)
# 把带有活动数量的branches字典列表传递给模板
context = {'branches': branches_with_counts,
'grouped_activities': grouped_activities,
'earliest_act': earliest_act,
'latest_act': latest_act,
'total_branch_count':total_branch_count,
'branch_count': branch_count,
'ongoing_activities': ongoing_activities,
'total_activities': total_activities,
'ongoing_activities_count': ongoing_activities_count,
'ongoing_events': ongoing_events,
'historical_years': historical_years,
'historical_grouped_activities': historical_grouped_activities
}
return render(request, 'branch_all.html', context)
# 生成branchinfo的视图
def Branchinfo(request):
branches = Branch.objects.all
context = {'branches': branches, }
return render(request, 'branch_info.html', context)
def Statistics(request):
# 默认筛选条件2024年、全部分支机构、完成状态end_time不为空
selected_year = request.GET.get('year', '2025')
selected_branch = request.GET.get('branch', 'all') # 'all'表示全部分支机构
selected_status = request.GET.get('status', 'completed') # 'completed'表示完成状态
# 构建查询条件
filters = Q()
# 年份筛选
if selected_year != 'all':
filters &= Q(start_time__year=selected_year)
# 分支机构筛选
if selected_branch != 'all':
filters &= Q(branch_id=selected_branch)
# 状态筛选完成状态end_time不为空未完成end_time为空
if selected_status == 'completed':
filters &= ~Q(end_time__isnull=True)
else:
filters &= Q(end_time__isnull=True)
# 获取符合条件的活动
activities = Activity.objects.filter(filters).order_by('start_time')
# 获取所有分支机构用于下拉选择
branches = Branch.objects.all()
context = {
'activities': activities,
'branches': branches,
'selected_year': selected_year,
'selected_branch': selected_branch,
'selected_status': selected_status
}
return render(request, 'statistics.html', context)
# 导出分支机构信息为XLS文件
def export_branches_xls(request):
# 创建一个工作簿
workbook = openpyxl.Workbook()
worksheet = workbook.active
worksheet.title = "分支机构信息"
# 添加表头
headers = ['分支机构名称', '信息系统类别']
for col_num, header in enumerate(headers, 1):
worksheet.cell(row=1, column=col_num, value=header)
# 获取所有分支机构数据
branches = Branch.objects.all()
# 填充数据
for row_num, branch in enumerate(branches, 2):
worksheet.cell(row=row_num, column=1, value=branch.name)
worksheet.cell(row=row_num, column=2, value=branch.category)
# 设置响应
response = HttpResponse(content_type='application/vnd.ms-excel')
response['Content-Disposition'] = 'attachment; filename="branches_info.xls"'
# 保存工作簿到响应
workbook.save(response)
return response
# 导出分支机构信息为PDF文件
def export_branches_pdf(request):
# 创建响应对象
response = HttpResponse(content_type='application/pdf')
response['Content-Disposition'] = 'attachment; filename="branches_info.pdf"'
# 注册中文字体
from reportlab.pdfbase import pdfmetrics
from reportlab.pdfbase.ttfonts import TTFont
# 尝试注册系统中文字体这里使用Windows系统自带的中文字体
try:
# 尝试注册微软雅黑字体
pdfmetrics.registerFont(TTFont('MSYaHei', 'C:/Windows/Fonts/msyh.ttc'))
chinese_font = 'MSYaHei'
except:
try:
# 如果微软雅黑不存在,尝试注册宋体
pdfmetrics.registerFont(TTFont('SimSun', 'C:/Windows/Fonts/simsun.ttc'))
chinese_font = 'SimSun'
except:
# 如果都找不到,使用默认字体(可能仍会有乱码)
chinese_font = 'Helvetica'
# 创建PDF文档
doc = SimpleDocTemplate(response, pagesize=letter)
elements = []
# 添加标题
styles = getSampleStyleSheet()
# 复制Title样式并修改字体
title_style = ParagraphStyle(
'ChineseTitle',
parent=styles['Title'],
fontName=chinese_font,
fontSize=18,
spaceAfter=30,
alignment=1 # 1表示居中
)
title = Paragraph("分支机构信息", title_style)
elements.append(title)
# 获取所有分支机构数据
branches = Branch.objects.all()
# 准备表格数据
data = [['分支机构名称', '信息系统类别']] # 表头
for branch in branches:
data.append([branch.name, branch.category])
# 创建表格
table = Table(data)
# 设置表格样式
style = TableStyle([
('BACKGROUND', (0, 0), (-1, 0), colors.grey),
('TEXTCOLOR', (0, 0), (-1, 0), colors.whitesmoke),
('ALIGN', (0, 0), (-1, -1), 'CENTER'),
('FONTNAME', (0, 0), (-1, 0), chinese_font), # 使用中文字体
('FONTSIZE', (0, 0), (-1, 0), 14),
('BOTTOMPADDING', (0, 0), (-1, 0), 12),
('BACKGROUND', (0, 1), (-1, -1), colors.beige),
('FONTNAME', (0, 1), (-1, -1), chinese_font), # 使用中文字体
('GRID', (0, 0), (-1, -1), 1, colors.black)
])
table.setStyle(style)
# 添加表格到文档
elements.append(table)
# 构建PDF
doc.build(elements)
return response
def public_screens(request):
branches = Branch.objects.all()
public_screens = PublicScreen.objects.all().order_by('-created_at')
return render(request, 'public_screens.html', {
'branches': branches,
'public_screens': public_screens,
})
def equipment_images(request):
# 获取搜索参数
search_query = request.GET.get('search', '')
# 根据搜索参数筛选分支机构
if search_query:
branches_with_images = Branch.objects.prefetch_related('equipment_images', 'drawings').filter(name__icontains=search_query)
else:
branches_with_images = Branch.objects.prefetch_related('equipment_images', 'drawings').all()
# 分页处理每页显示12个分支机构
paginator = Paginator(branches_with_images, 12)
page = request.GET.get('page')
try:
branches = paginator.page(page)
except PageNotAnInteger:
# 如果页码不是整数,返回第一页
branches = paginator.page(1)
except EmptyPage:
# 如果页码超出范围,返回最后一页
branches = paginator.page(paginator.num_pages)
context = {
'branches': branches,
'total_count': branches_with_images.count(),
'search_query': search_query
}
return render(request, 'equipment_images.html', context)
def contact_list(request):
# 获取筛选参数
branches_param = request.GET.get('branches') # 支持多机构选择使用逗号分隔的ID字符串
category = request.GET.get('category')
contact_name = request.GET.get('contact_name')
# 构建查询条件
filters = Q()
# 分支机构筛选(支持多选)
if branches_param:
# 将逗号分隔的字符串转换为ID列表
branch_ids = [bid.strip() for bid in branches_param.split(',') if bid.strip().isdigit()]
if branch_ids:
filters &= Q(branch_id__in=branch_ids)
# 联系人类别筛选
if category:
filters &= Q(category__contains=category)
# 联系人姓名筛选
if contact_name:
filters &= Q(name__icontains=contact_name)
# 获取筛选后的联系人
contacts = Contact.objects.filter(filters)
# 获取所有分支机构和联系人类别用于下拉选择
branches = Branch.objects.all()
categories = [choice[0] for choice in Contact.CATEGORY_CHOICES]
context = {
'contacts': contacts,
'branches': branches,
'categories': categories,
'selected_branches': branches_param, # 传递选中的机构ID字符串
'selected_category': category,
'selected_contact_name': contact_name
}
return render(request, 'contact_list.html', context)
def video_terminal_list(request):
# 获取筛选参数
selected_branch = request.GET.get('branch', '')
selected_type = request.GET.get('type', '')
# 获取选中项的名称用于前端显示
selected_branch_name = ''
if selected_branch:
try:
selected_branch_name = Branch.objects.get(id=selected_branch).name
except Branch.DoesNotExist:
pass
selected_type_name = ''
if selected_type:
for code, name in VideoTerminal.TERMINAL_TYPE_CHOICES:
if code == selected_type:
selected_type_name = name
break
# 基础查询集
terminals = VideoTerminal.objects.all().order_by('branch__name', 'terminal_type')
# 应用筛选条件
if selected_branch:
terminals = terminals.filter(branch_id=selected_branch)
if selected_type:
terminals = terminals.filter(terminal_type=selected_type)
# 获取所有分支机构和终端类型用于筛选下拉框
branches = Branch.objects.all()
terminal_types = VideoTerminal.TERMINAL_TYPES
context = {
'terminals': terminals,
'branches': branches,
'terminal_types': terminal_types,
'selected_branch': selected_branch,
'selected_type': selected_type,
'selected_branch_name': selected_branch_name,
'selected_type_name': selected_type_name,
}
return render(request, 'video_terminals.html', context)
from rest_framework import viewsets
from .models import Branch, Activity, Evaluation, Event, VideoTerminal
from .serializers import BranchSerializer, ActivitySerializer, EvaluationSerializer
from django.shortcuts import render, redirect
from .models import PublicScreen
from collections import defaultdict
from datetime import datetime
from django.db.models import Q
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
from .models import Contact
from django.template.defaulttags import register
from django.http import HttpResponse
import openpyxl
from reportlab.lib import colors
from reportlab.lib.pagesizes import letter
from reportlab.platypus import SimpleDocTemplate, Table, TableStyle, Paragraph
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
import os
@register.filter
def get_item(dictionary, key):
return dictionary.get(key)
class BranchViewSet(viewsets.ModelViewSet):
queryset = Branch.objects.all()
serializer_class = BranchSerializer
class ActivityViewSet(viewsets.ModelViewSet):
queryset = Activity.objects.all()
serializer_class = ActivitySerializer
class EvaluationViewSet(viewsets.ModelViewSet):
queryset = Evaluation.objects.all()
serializer_class = EvaluationSerializer
# 在页面上显示id为branch的详细信息
def branch_detail(request, branch_id):
# 获取分支机构及其所有关联数据
branch = Branch.objects.get(pk=branch_id)
# 获取并分组活动
activities = Activity.objects.filter(branch=branch).order_by('start_time')
grouped_activities = defaultdict(list)
for activity in activities:
grouped_activities[activity.scope].append(activity)
grouped_activities = dict(grouped_activities)
# 获取其他关联数据
events = branch.events.all().order_by('-start_time')
equipment_images = branch.equipment_images.all()
public_screens = branch.public_screens.all()
# 准备上下文数据
context = {
'branch': branch,
'grouped_activities': grouped_activities,
'events': events,
'equipment_images': equipment_images,
'public_screens': public_screens,
}
return render(request, 'branch_detail.html', context)
# 在页面上显示所有的branch以及active的数量首页显示
def BranchAll(request):
branches = Branch.objects.exclude(activity__isnull=True).order_by('name')
branches_with_counts = [{'branch': branch,
'total_count': Activity.objects.filter(branch=branch).count(),
'onging_count': Activity.objects.filter(branch=branch, end_time__isnull=True).count()}
for branch in branches]
# 返回总数
total_branch_count = len(branches_with_counts)
# 获取所有分支机构总数(包括没有活动的)
all_branch_count = Branch.objects.count()
# 比如说end_time()的year是2024年但是now()是2025年的2月fileter出来但是如果now()是2025年3月则不显示
now = datetime.now()
print(f"当前月份是{now.month},年是{now.year}")
if now.month <= 2:
two_months_ago_year = now.year - 1
activities = Activity.objects.filter(
Q(start_time__year=now.year) | Q(start_time__year=two_months_ago_year)).order_by('branch')
else:
activities = Activity.objects.filter(start_time__year=now.year).order_by('branch')
# 返回起止时间
earliest_act = activities.order_by('start_time').first()
print(earliest_act)
latest_act = activities.order_by('start_time').last()
print(latest_act)
# 确保earliest_act和latest_act不为None时才使用
# 按activities按照scope分组
grouped_activities = defaultdict(lambda: {'activities': [], 'branch_count': 0})
for activity in activities:
grouped_activities[activity.scope]['activities'].append(activity)
# 计算每个scope的分支机构数量
for scope_data in grouped_activities.values():
activity_ids = [a.id for a in scope_data['activities']]
scope_data['branch_count'] = Activity.objects.filter(id__in=activity_ids).values('branch').distinct().count()
# 转换为普通字典以便模板遍历
grouped_activities = dict(grouped_activities)
# 统计活动中的事件即end_time为空的活动
ongoing_activities = Activity.objects.filter(end_time__isnull=True).order_by('branch','start_time')
# 统计活动的总的数量
total_activities = Activity.objects.count()
# 统计活动中的事件的总数
ongoing_activities_count = ongoing_activities.count()
branch_count = activities.values('branch').distinct().count()
# 事件的展示
ongoing_events = Event.objects.all().order_by('start_time')
# 获取历年活动数据
historical_years = []
historical_grouped_activities = {}
# 获取最早的年份
earliest_activity = Activity.objects.order_by('start_time').first()
if earliest_activity:
earliest_year = earliest_activity.start_time.year
current_year = datetime.now().year
# 从去年开始,逐年递减,直到最早的年份
for year in range(current_year - 1, earliest_year - 1, -1):
# 获取该年份的活动
year_activities = Activity.objects.filter(start_time__year=year).order_by('branch')
# 如果该年份有活动数据
if year_activities.exists():
historical_years.append(year)
# 按照scope分组
year_grouped_activities = defaultdict(lambda: {'activities': [], 'branch_count': 0})
for activity in year_activities:
year_grouped_activities[activity.scope]['activities'].append(activity)
# 计算每个scope的分支机构数量
for scope_data in year_grouped_activities.values():
activity_ids = [a.id for a in scope_data['activities']]
scope_data['branch_count'] = Activity.objects.filter(id__in=activity_ids).values('branch').distinct().count()
# 转换为普通字典
historical_grouped_activities[year] = dict(year_grouped_activities)
# 把带有活动数量的branches字典列表传递给模板
context = {'branches': branches_with_counts,
'grouped_activities': grouped_activities,
'earliest_act': earliest_act,
'latest_act': latest_act,
'total_branch_count':total_branch_count,
'all_branch_count': all_branch_count,
'branch_count': branch_count,
'ongoing_activities': ongoing_activities,
'total_activities': total_activities,
'ongoing_activities_count': ongoing_activities_count,
'ongoing_events': ongoing_events,
'historical_years': historical_years,
'historical_grouped_activities': historical_grouped_activities
}
return render(request, 'branch_all.html', context)
# 生成branchinfo的视图
def Branchinfo(request):
branches = Branch.objects.all
context = {'branches': branches, }
return render(request, 'branch_info.html', context)
def Statistics(request):
# 默认筛选条件2024年、全部分支机构、完成状态end_time不为空
selected_year = request.GET.get('year', '2025')
selected_branch = request.GET.get('branch', 'all') # 'all'表示全部分支机构
selected_status = request.GET.get('status', 'completed') # 'completed'表示完成状态
# 构建查询条件
filters = Q()
# 年份筛选
if selected_year != 'all':
filters &= Q(start_time__year=selected_year)
# 分支机构筛选
if selected_branch != 'all':
filters &= Q(branch_id=selected_branch)
# 状态筛选完成状态end_time不为空未完成end_time为空
if selected_status == 'completed':
filters &= ~Q(end_time__isnull=True)
else:
filters &= Q(end_time__isnull=True)
# 获取符合条件的活动
activities = Activity.objects.filter(filters).order_by('start_time')
# 获取所有分支机构用于下拉选择
branches = Branch.objects.all()
context = {
'activities': activities,
'branches': branches,
'selected_year': selected_year,
'selected_branch': selected_branch,
'selected_status': selected_status
}
return render(request, 'statistics.html', context)
# 导出分支机构信息为XLS文件
def export_branches_xls(request):
# 创建一个工作簿
workbook = openpyxl.Workbook()
worksheet = workbook.active
worksheet.title = "分支机构信息"
# 添加表头
headers = ['分支机构名称', '信息系统类别']
for col_num, header in enumerate(headers, 1):
worksheet.cell(row=1, column=col_num, value=header)
# 获取所有分支机构数据
branches = Branch.objects.all()
# 填充数据
for row_num, branch in enumerate(branches, 2):
worksheet.cell(row=row_num, column=1, value=branch.name)
worksheet.cell(row=row_num, column=2, value=branch.category)
# 设置响应
response = HttpResponse(content_type='application/vnd.ms-excel')
response['Content-Disposition'] = 'attachment; filename="branches_info.xls"'
# 保存工作簿到响应
workbook.save(response)
return response
# 导出分支机构信息为PDF文件
def export_branches_pdf(request):
# 创建响应对象
response = HttpResponse(content_type='application/pdf')
response['Content-Disposition'] = 'attachment; filename="branches_info.pdf"'
# 注册中文字体
from reportlab.pdfbase import pdfmetrics
from reportlab.pdfbase.ttfonts import TTFont
# 尝试注册系统中文字体这里使用Windows系统自带的中文字体
try:
# 尝试注册微软雅黑字体
pdfmetrics.registerFont(TTFont('MSYaHei', 'C:/Windows/Fonts/msyh.ttc'))
chinese_font = 'MSYaHei'
except:
try:
# 如果微软雅黑不存在,尝试注册宋体
pdfmetrics.registerFont(TTFont('SimSun', 'C:/Windows/Fonts/simsun.ttc'))
chinese_font = 'SimSun'
except:
# 如果都找不到,使用默认字体(可能仍会有乱码)
chinese_font = 'Helvetica'
# 创建PDF文档
doc = SimpleDocTemplate(response, pagesize=letter)
elements = []
# 添加标题
styles = getSampleStyleSheet()
# 复制Title样式并修改字体
title_style = ParagraphStyle(
'ChineseTitle',
parent=styles['Title'],
fontName=chinese_font,
fontSize=18,
spaceAfter=30,
alignment=1 # 1表示居中
)
title = Paragraph("分支机构信息", title_style)
elements.append(title)
# 获取所有分支机构数据
branches = Branch.objects.all()
# 准备表格数据
data = [['分支机构名称', '信息系统类别']] # 表头
for branch in branches:
data.append([branch.name, branch.category])
# 创建表格
table = Table(data)
# 设置表格样式
style = TableStyle([
('BACKGROUND', (0, 0), (-1, 0), colors.grey),
('TEXTCOLOR', (0, 0), (-1, 0), colors.whitesmoke),
('ALIGN', (0, 0), (-1, -1), 'CENTER'),
('FONTNAME', (0, 0), (-1, 0), chinese_font), # 使用中文字体
('FONTSIZE', (0, 0), (-1, 0), 14),
('BOTTOMPADDING', (0, 0), (-1, 0), 12),
('BACKGROUND', (0, 1), (-1, -1), colors.beige),
('FONTNAME', (0, 1), (-1, -1), chinese_font), # 使用中文字体
('GRID', (0, 0), (-1, -1), 1, colors.black)
])
table.setStyle(style)
# 添加表格到文档
elements.append(table)
# 构建PDF
doc.build(elements)
return response
def public_screens(request):
branches = Branch.objects.all()
public_screens = PublicScreen.objects.all().order_by('-created_at')
return render(request, 'public_screens.html', {
'branches': branches,
'public_screens': public_screens,
})
def equipment_images(request):
# 获取搜索参数
search_query = request.GET.get('search', '')
# 根据搜索参数筛选分支机构
if search_query:
branches_with_images = Branch.objects.prefetch_related('equipment_images', 'drawings').filter(name__icontains=search_query)
else:
branches_with_images = Branch.objects.prefetch_related('equipment_images', 'drawings').all()
# 分页处理每页显示12个分支机构
paginator = Paginator(branches_with_images, 12)
page = request.GET.get('page')
try:
branches = paginator.page(page)
except PageNotAnInteger:
# 如果页码不是整数,返回一页
branches = paginator.page(1)
except EmptyPage:
# 如果页码超出范围,返回最后一页
branches = paginator.page(paginator.num_pages)
context = {
'branches': branches,
'total_count': branches_with_images.count(),
'search_query': search_query
}
return render(request, 'equipment_images.html', context)
def contact_list(request):
# 获取筛选参数
branches_param = request.GET.get('branches') # 支持多机构选择使用逗号分隔的ID字符串
category = request.GET.get('category')
contact_name = request.GET.get('contact_name')
# 构建查询条件
filters = Q()
# 分支机构筛选(支持多选)
if branches_param:
# 将逗号分隔的字符串转换为ID列表
branch_ids = [bid.strip() for bid in branches_param.split(',') if bid.strip().isdigit()]
if branch_ids:
filters &= Q(branch_id__in=branch_ids)
# 联系人类别筛选
if category:
filters &= Q(category__contains=category)
# 联系人姓名筛选
if contact_name:
filters &= Q(name__icontains=contact_name)
# 获取筛选后的联系人
contacts = Contact.objects.filter(filters)
# 获取所有分支机构和联系人类别用于下拉选择
branches = Branch.objects.all()
categories = [choice[0] for choice in Contact.CATEGORY_CHOICES]
context = {
'contacts': contacts,
'branches': branches,
'categories': categories,
'selected_branches': branches_param, # 传递选中的机构ID字符串
'selected_category': category,
'selected_contact_name': contact_name
}
return render(request, 'contact_list.html', context)
def video_terminal_list(request):
# 获取筛选参数
selected_branch = request.GET.get('branch', '')
selected_type = request.GET.get('type', '')
# 获取选中项的名称用于前端显示
selected_branch_name = ''
if selected_branch:
try:
selected_branch_name = Branch.objects.get(id=selected_branch).name
except Branch.DoesNotExist:
pass
selected_type_name = ''
if selected_type:
for code, name in VideoTerminal.TERMINAL_TYPE_CHOICES:
if code == selected_type:
selected_type_name = name
break
# 基础查询集
terminals = VideoTerminal.objects.all().order_by('branch__name', 'terminal_type')
# 应用筛选条件
if selected_branch:
terminals = terminals.filter(branch_id=selected_branch)
if selected_type:
terminals = terminals.filter(terminal_type=selected_type)
# 获取所有分支机构和终端类型用于筛选下拉框
branches = Branch.objects.all()
terminal_types = VideoTerminal.TERMINAL_TYPES
context = {
'terminals': terminals,
'branches': branches,
'terminal_types': terminal_types,
'selected_branch': selected_branch,
'selected_type': selected_type,
'selected_branch_name': selected_branch_name,
'selected_type_name': selected_type_name,
}
return render(request, 'video_terminals.html', context)