分支机构的项目管理,基于Django
This commit is contained in:
310
fzjgact/huodong/templates/contact_list.html
Normal file
310
fzjgact/huodong/templates/contact_list.html
Normal file
@@ -0,0 +1,310 @@
|
||||
{% 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/3">
|
||||
<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>
|
||||
<!-- 隐藏的分支机构ID输入框(用于表单提交) -->
|
||||
<input type="hidden" name="branch" id="selectedBranchId">
|
||||
</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"
|
||||
data-id="{{ branch.id }}"
|
||||
data-name="{{ branch.name }}">
|
||||
{{ branch.name }}
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</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>
|
||||
<div class="relative" id="categorySelectContainer">
|
||||
<div class="relative">
|
||||
<input
|
||||
type="text"
|
||||
id="categorySearchInput"
|
||||
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="toggleCategoryDropdown()"
|
||||
value="{{ selected_category|default:'' }}"
|
||||
>
|
||||
<span class="absolute right-3 top-1/2 transform -translate-y-1/2 text-gray-400 transition-transform duration-200" id="categoryArrowIcon">
|
||||
<i class="fa fa-chevron-down"></i>
|
||||
</span>
|
||||
<input type="hidden" name="category" id="selectedCategory">
|
||||
</div>
|
||||
|
||||
<div id="categoryDropdownContent" 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="categoryFilterInput"
|
||||
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="filterCategoryOptions()"
|
||||
autofocus
|
||||
>
|
||||
</div>
|
||||
|
||||
<div id="categoryOptionsList" class="max-h-60 overflow-y-auto">
|
||||
<ul>
|
||||
{% for cat in categories %}
|
||||
<li class="category-option px-4 py-2.5 hover:bg-gray-100 cursor-pointer text-sm transition-colors"
|
||||
data-value="{{ cat }}">
|
||||
{{ cat }}
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div id="categoryNoResult" 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="contact_name" class="mt-1 p-2 border rounded" placeholder="输入联系人姓名筛选" value="{{ selected_contact_name|default:'' }}">
|
||||
</div>
|
||||
|
||||
<div class="flex items-end">
|
||||
<button type="submit" class="px-4 py-2 bg-blue-500 text-white rounded hover:bg-blue-600">筛选</button>
|
||||
</div>
|
||||
</div>
|
||||
</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>
|
||||
|
||||
<script>
|
||||
// 控制分支机构下拉框显示/隐藏
|
||||
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;
|
||||
|
||||
let firstMatch = null;
|
||||
options.forEach(option => {
|
||||
const name = option.dataset.name.toLowerCase();
|
||||
if (name.includes(searchText)) {
|
||||
option.style.display = 'block';
|
||||
hasMatch = true;
|
||||
if (!firstMatch) firstMatch = option;
|
||||
} else {
|
||||
option.style.display = 'none';
|
||||
}
|
||||
});
|
||||
|
||||
noResult.classList.toggle('hidden', hasMatch);
|
||||
// 自动滚动到第一个匹配项
|
||||
if (firstMatch) {
|
||||
firstMatch.scrollIntoView({block: 'nearest'});
|
||||
firstMatch.classList.add('bg-blue-50');
|
||||
setTimeout(() => firstMatch.classList.remove('bg-blue-50'), 1000);
|
||||
}
|
||||
}
|
||||
|
||||
// 选择分支机构选项
|
||||
document.querySelectorAll('.branch-option').forEach(option => {
|
||||
option.addEventListener('click', function() {
|
||||
const name = this.dataset.name;
|
||||
const id = this.dataset.id;
|
||||
document.getElementById('branchSearchInput').value = name;
|
||||
document.getElementById('selectedBranchId').value = id;
|
||||
closeBranchDropdown();
|
||||
});
|
||||
});
|
||||
|
||||
// 键盘导航(可选)
|
||||
document.getElementById('branchFilterInput').addEventListener('keydown', function(e) {
|
||||
const options = document.querySelectorAll('.branch-option:not([style*="display: "])');
|
||||
const active = document.querySelector('.branch-option.bg-gray-100');
|
||||
let index = -1;
|
||||
|
||||
if (active) index = Array.from(options).indexOf(active);
|
||||
|
||||
if (e.key === 'ArrowDown') {
|
||||
e.preventDefault();
|
||||
if (active) active.classList.remove('bg-gray-100');
|
||||
const next = (index + 1) % options.length;
|
||||
options[next]?.classList.add('bg-gray-100');
|
||||
options[next]?.scrollIntoView({ block: 'nearest' });
|
||||
} else if (e.key === 'ArrowUp') {
|
||||
e.preventDefault();
|
||||
if (active) active.classList.remove('bg-gray-100');
|
||||
const prev = index > 0 ? index - 1 : options.length - 1;
|
||||
options[prev]?.classList.add('bg-gray-100');
|
||||
options[prev]?.scrollIntoView({ block: 'nearest' });
|
||||
} else if (e.key === 'Enter') {
|
||||
e.preventDefault();
|
||||
active?.click();
|
||||
}
|
||||
});
|
||||
|
||||
// 新增联系人类别下拉框控制函数
|
||||
function toggleCategoryDropdown() {
|
||||
const dropdown = document.getElementById('categoryDropdownContent');
|
||||
const arrow = document.getElementById('categoryArrowIcon');
|
||||
if (dropdown.classList.contains('hidden')) {
|
||||
dropdown.classList.remove('hidden');
|
||||
arrow.classList.add('rotate-180');
|
||||
document.getElementById('categoryFilterInput').focus();
|
||||
document.addEventListener('click', handleCategoryOutsideClick);
|
||||
} else {
|
||||
closeCategoryDropdown();
|
||||
}
|
||||
}
|
||||
|
||||
function closeCategoryDropdown() {
|
||||
const dropdown = document.getElementById('categoryDropdownContent');
|
||||
const arrow = document.getElementById('categoryArrowIcon');
|
||||
dropdown.classList.add('hidden');
|
||||
arrow.classList.remove('rotate-180');
|
||||
document.removeEventListener('click', handleCategoryOutsideClick);
|
||||
}
|
||||
|
||||
function handleCategoryOutsideClick(event) {
|
||||
const container = document.getElementById('categorySelectContainer');
|
||||
if (!container.contains(event.target)) {
|
||||
closeCategoryDropdown();
|
||||
}
|
||||
}
|
||||
|
||||
function filterCategoryOptions() {
|
||||
const searchText = document.getElementById('categoryFilterInput').value.toLowerCase();
|
||||
const options = document.querySelectorAll('.category-option');
|
||||
const noResult = document.getElementById('categoryNoResult');
|
||||
let hasMatch = false;
|
||||
|
||||
options.forEach(option => {
|
||||
const value = option.dataset.value.toLowerCase();
|
||||
if (value.includes(searchText)) {
|
||||
option.style.display = 'block';
|
||||
hasMatch = true;
|
||||
} else {
|
||||
option.style.display = '';
|
||||
}
|
||||
});
|
||||
|
||||
noResult.classList.toggle('hidden', hasMatch || !searchText);
|
||||
}
|
||||
|
||||
document.querySelectorAll('.category-option').forEach(option => {
|
||||
option.addEventListener('click', function() {
|
||||
const value = this.dataset.value;
|
||||
document.getElementById('categorySearchInput').value = value;
|
||||
document.getElementById('selectedCategory').value = value;
|
||||
closeCategoryDropdown();
|
||||
});
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
Reference in New Issue
Block a user