Files
fzjg_local/fzjgact/huodong/templates/contact_list.html
xiaji 8eba3f0160 feat(联系人): 添加联系人信息导出为PDF功能
在联系人列表页面添加导出为PDF按钮,实现将筛选后的联系人信息导出为PDF文件。新增视图函数处理PDF生成逻辑,支持中文显示并保留原有筛选条件。
2026-01-12 15:48:51 +08:00

345 lines
16 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{% extends "base.html" %}
{% block content %}
<div class="container mx-auto p-4">
<h2 class="text-xl font-bold mb-4">联系人信息</h2>
<!-- 新增筛选表单 -->
<form method="GET" class="mb-4 p-4 bg-gray-100 rounded-lg" id="filterForm">
<div class="flex flex-wrap gap-4">
<!-- 分支机构筛选(带搜索的多选下拉框) -->
<div class="flex flex-col w-full md:w-1/2">
<label class="text-sm font-medium">分支机构(可多选)</label>
<div class="relative" id="branchSelectContainer">
<!-- 选择框主体 -->
<div class="relative">
<input
type="text"
id="branchSearchInput"
class="w-full pl-4 pr-10 py-2.5 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-300 focus:border-blue-500 outline-none text-sm"
placeholder="点击选择分支机构(可多选)..."
readonly
onclick="toggleBranchDropdown()"
>
<span class="absolute right-3 top-1/2 transform -translate-y-1/2 text-gray-400 transition-transform duration-200" id="branchArrowIcon">
<i class="fa fa-chevron-down"></i>
</span>
<!-- 选中的机构显示 -->
<div id="selectedBranches" class="absolute z-5 mt-1 w-full bg-white rounded-lg shadow-md hidden max-h-32 overflow-y-auto">
<div class="p-2">
<div class="flex flex-wrap gap-1" id="branchTags"></div>
</div>
</div>
</div>
<!-- 下拉内容 -->
<div id="branchDropdownContent" class="absolute z-10 mt-1 w-full bg-white rounded-lg shadow-md overflow-hidden hidden">
<!-- 搜索输入框 -->
<div class="p-2 border-b border-gray-100">
<input
type="text"
id="branchFilterInput"
class="w-full px-3 py-2 border border-gray-200 rounded-lg focus:ring-1 focus:ring-blue-300 focus:border-blue-500 outline-none text-sm"
placeholder="搜索分支机构..."
oninput="filterBranchOptions()"
autofocus
>
</div>
<!-- 选项列表 -->
<div id="branchOptionsList" class="max-h-60 overflow-y-auto">
<ul>
{% for branch in branches %}
<li class="branch-option px-4 py-2.5 hover:bg-gray-100 cursor-pointer text-sm transition-colors flex items-center"
data-id="{{ branch.id }}"
data-name="{{ branch.name }}">
<input type="checkbox" class="mr-2 branch-checkbox" value="{{ branch.id }}">
{{ branch.name }}
</li>
{% endfor %}
</ul>
</div>
<!-- 操作按钮 -->
<div class="p-2 border-t border-gray-100 flex gap-2">
<button type="button" class="flex-1 px-3 py-1 text-xs bg-blue-500 text-white rounded hover:bg-blue-600" onclick="selectAllBranches()">全选</button>
<button type="button" class="flex-1 px-3 py-1 text-xs bg-red-500 text-white rounded hover:bg-red-600" onclick="clearAllBranches()">清空</button>
</div>
<!-- 独立的清空全部选择按钮 -->
<div class="p-2 border-t border-gray-100">
<button type="button" class="w-full px-3 py-2 text-sm bg-orange-500 text-white rounded-lg hover:bg-orange-600 font-medium" onclick="clearAllBranches()" title="清空所有机构选择">
<i class="fa fa-refresh mr-1"></i>清空全部选择
</button>
</div>
<!-- 无结果提示 -->
<div id="branchNoResult" class="px-4 py-4 text-center text-gray-500 text-sm hidden">
没有找到匹配的分支机构
</div>
</div>
</div>
</div>
<!-- 联系人类别筛选(保持原结构) -->
<div class="flex flex-col w-full md:w-1/3">
<label class="text-sm font-medium">联系人类别</label>
<input type="text" name="category" class="mt-1 p-2 border rounded" placeholder="输入类别名称筛选" value="{{ selected_category|default:'' }}">
</div>
<!-- 联系人姓名筛选(保持原结构) -->
<div class="flex flex-col w-full md:w-1/3">
<label class="text-sm font-medium">联系人姓名</label>
<input type="text" name="contact_name" class="mt-1 p-2 border rounded" placeholder="输入联系人姓名筛选" value="{{ selected_contact_name|default:'' }}">
</div>
<div class="flex items-end gap-2">
<button type="submit" class="px-4 py-2 bg-blue-500 text-white rounded hover:bg-blue-600">筛选</button>
<button type="button" class="px-4 py-2 bg-red-500 text-white rounded hover:bg-red-600 transition-colors duration-200 font-medium" onclick="clearFilters()">
<i class="fa fa-times mr-1"></i>清空筛选
</button>
</div>
</div>
<!-- 隐藏的分支机构ID输入框用于表单提交 -->
<input type="hidden" name="branches" id="selectedBranchesInput">
</form>
<table class="min-w-full border-collapse border border-gray-300">
<thead>
<tr>
<th class="p-2 border border-gray-300 bg-gray-100">分支机构</th>
<th class="p-2 border border-gray-300 bg-gray-100">分类</th>
<th class="p-2 border border-gray-300 bg-gray-100">姓名</th>
<th class="p-2 border border-gray-300 bg-gray-100">电话</th>
<th class="p-2 border border-gray-300 bg-gray-100">邮箱</th>
</tr>
</thead>
<tbody>
{% for contact in contacts %}
<tr class="{% cycle 'bg-white' 'bg-gray-50' %}">
<td class="p-2 border border-gray-300">{{ contact.branch.name }}</td>
<td class="p-2 border border-gray-300">{{ contact.category|default:"" }}</td>
<td class="p-2 border border-gray-300">{{ contact.name|default:"" }}</td>
<td class="p-2 border border-gray-300">{{ contact.phone|default:"" }}</td>
<td class="p-2 border border-gray-300">{{ contact.email|default:"" }}</td>
</tr>
{% empty %}
<tr>
<td colspan="5" class="p-4 text-center text-gray-500">没有找到匹配的联系人</td>
</tr>
{% endfor %}
</tbody>
</table>
<div class="mt-4 flex justify-end">
<a href="{% url 'export-contacts-pdf' %}{% if request.GET.urlencode %}?{{ request.GET.urlencode }}{% endif %}" class="inline-flex items-center px-4 py-2 bg-purple-600 hover:bg-purple-800 text-white font-medium rounded-lg shadow-md transition duration-300 ease-in-out transform hover:scale-105 hover:shadow-lg">
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 mr-2" viewBox="0 0 20 20" fill="currentColor">
<path fill-rule="evenodd" d="M3 17a1 1 0 011-1h12a1 1 0 110 2H4a1 1 0 01-1-1zm3.293-7.707a1 1 0 011.414 0L9 10.586V3a1 1 0 112 0v7.586l1.293-1.293a1 1 0 111.414 1.414l-3 3a1 1 0 01-1.414 0l-3-3a1 1 0 010-1.414z" clip-rule="evenodd" />
</svg>
导出为PDF
</a>
</div>
</div>
<script>
// 选中的分支机构
let selectedBranches = new Set();
// 初始化页面
document.addEventListener('DOMContentLoaded', function() {
// 初始化选中的机构
initializeSelectedBranches();
// 绑定复选框事件
bindCheckboxEvents();
});
// 初始化选中的机构(从表单参数中读取)
function initializeSelectedBranches() {
const urlParams = new URLSearchParams(window.location.search);
const branchesParam = urlParams.get('branches');
if (branchesParam) {
const branchIds = branchesParam.split(',');
branchIds.forEach(id => {
selectedBranches.add(id);
const checkbox = document.querySelector(`input[value="${id}"]`);
if (checkbox) {
checkbox.checked = true;
}
});
}
updateBranchSelectionUI();
}
// 绑定复选框事件
function bindCheckboxEvents() {
document.querySelectorAll('.branch-checkbox').forEach(checkbox => {
checkbox.addEventListener('change', function() {
const branchId = this.value;
const branchName = this.closest('.branch-option').dataset.name;
if (this.checked) {
selectedBranches.add(branchId);
} else {
selectedBranches.delete(branchId);
}
updateBranchSelectionUI();
});
});
}
// 更新机构选择UI
function updateBranchSelectionUI() {
const selectedBranchesInput = document.getElementById('selectedBranchesInput');
const branchSearchInput = document.getElementById('branchSearchInput');
const selectedBranchesDiv = document.getElementById('selectedBranches');
const branchTags = document.getElementById('branchTags');
// 更新隐藏输入框
if (selectedBranches.size > 0) {
selectedBranchesInput.value = Array.from(selectedBranches).join(',');
branchSearchInput.placeholder = `已选择 ${selectedBranches.size} 个机构`;
} else {
selectedBranchesInput.value = '';
branchSearchInput.placeholder = '点击选择分支机构(可多选)...';
}
// 更新显示的标签
branchTags.innerHTML = '';
selectedBranches.forEach(branchId => {
const branchName = document.querySelector(`input[value="${branchId}"]`).closest('.branch-option').dataset.name;
const tag = document.createElement('span');
tag.className = 'inline-flex items-center px-2 py-1 rounded-full text-xs bg-blue-100 text-blue-800';
tag.innerHTML = `
${branchName}
<button type="button" class="ml-1 text-blue-600 hover:text-blue-800" onclick="removeBranch('${branchId}')">
<i class="fa fa-times"></i>
</button>
`;
branchTags.appendChild(tag);
});
// 控制选中机构显示区域
if (selectedBranches.size > 0) {
selectedBranchesDiv.classList.remove('hidden');
} else {
selectedBranchesDiv.classList.add('hidden');
}
}
// 移除机构
function removeBranch(branchId) {
selectedBranches.delete(branchId);
const checkbox = document.querySelector(`input[value="${branchId}"]`);
if (checkbox) {
checkbox.checked = false;
}
updateBranchSelectionUI();
}
// 全选分支机构
function selectAllBranches() {
selectedBranches.clear();
document.querySelectorAll('.branch-checkbox').forEach(checkbox => {
checkbox.checked = true;
selectedBranches.add(checkbox.value);
});
updateBranchSelectionUI();
}
// 清空所有分支机构选择
function clearAllBranches() {
selectedBranches.clear();
document.querySelectorAll('.branch-checkbox').forEach(checkbox => {
checkbox.checked = false;
});
updateBranchSelectionUI();
}
// 清空所有筛选
function clearFilters() {
// 清空所有表单字段
document.getElementById('filterForm').reset();
// 清空机构选择
clearAllBranches();
// 提交清空后的表单
window.location.href = window.location.pathname;
}
// 控制分支机构下拉框显示/隐藏
function toggleBranchDropdown() {
const dropdown = document.getElementById('branchDropdownContent');
const arrow = document.getElementById('branchArrowIcon');
if (dropdown.classList.contains('hidden')) {
dropdown.classList.remove('hidden');
arrow.classList.add('rotate-180');
document.getElementById('branchFilterInput').focus();
document.addEventListener('click', handleBranchOutsideClick);
} else {
closeBranchDropdown();
}
}
// 关闭分支机构下拉框
function closeBranchDropdown() {
const dropdown = document.getElementById('branchDropdownContent');
const arrow = document.getElementById('branchArrowIcon');
dropdown.classList.add('hidden');
arrow.classList.remove('rotate-180');
document.removeEventListener('click', handleBranchOutsideClick);
}
// 点击外部关闭下拉框
function handleBranchOutsideClick(event) {
const container = document.getElementById('branchSelectContainer');
if (!container.contains(event.target)) {
closeBranchDropdown();
}
}
// 筛选分支机构选项
function filterBranchOptions() {
const searchText = document.getElementById('branchFilterInput').value.toLowerCase();
const options = document.querySelectorAll('.branch-option');
const noResult = document.getElementById('branchNoResult');
let hasMatch = false;
options.forEach(option => {
const name = option.dataset.name.toLowerCase();
if (name.includes(searchText)) {
option.style.display = 'block';
hasMatch = true;
} else {
option.style.display = 'none';
}
});
// 显示或隐藏无结果提示
if (searchText && !hasMatch) {
noResult.classList.remove('hidden');
} else {
noResult.classList.add('hidden');
}
}
// 选择分支机构选项(保留原有功能以兼容单选操作)
document.querySelectorAll('.branch-option').forEach(option => {
option.addEventListener('click', function(e) {
// 如果点击的是复选框,不重复处理
if (e.target.type === 'checkbox') {
return;
}
const checkbox = this.querySelector('.branch-checkbox');
checkbox.checked = !checkbox.checked;
// 触发复选框的change事件
checkbox.dispatchEvent(new Event('change'));
});
});
</script>
{% endblock %}