改进事件的显示效果
This commit is contained in:
Binary file not shown.
Binary file not shown.
@@ -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 %}
|
||||
|
||||
@@ -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 %}
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user