You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

172 lines
6.4 KiB

{% extends "base.html" %}
{% block title %}快速录入{% endblock %}
{% block content %}
<div class="w-full">
<section class="mb-4 flex items-start justify-between gap-3 sm:mb-5">
<div>
<div class="flex items-center gap-2">
<h2 class="page-title">快速录入</h2>
<span class="rounded-full bg-accent-100 px-3 py-1 text-xs font-medium text-accent-700 dark:bg-accent-900/40 dark:text-accent-300">当天礼金</span>
</div>
</div>
</section>
<section class="mb-4 card sm:mb-5">
<div class="card-body">
<form action="{{ url_for('quick_entry.index') }}" method="get" class="space-y-3" id="quick-entry-search-form">
<label for="quick-entry-search" class="form-label">搜索关键词</label>
<div class="flex flex-col gap-3 sm:flex-row">
<input id="quick-entry-search" name="q" type="text" class="form-input flex-1" value="{{ search_term }}" placeholder="户主 / 成员 / 拼音 / 标签 / 备注" autofocus>
<div class="flex flex-wrap items-center justify-end gap-2 sm:flex-nowrap">
<label class="inline-flex shrink-0 items-center gap-2 rounded-full border border-neutral-200 bg-neutral-50 px-3 py-2 text-xs text-neutral-500 dark:border-neutral-700 dark:bg-neutral-900 dark:text-neutral-400">
<input
type="checkbox"
name="include_bride_side"
value="1"
class="h-3.5 w-3.5 rounded border-neutral-300 text-brand-600 focus:ring-brand-500 dark:border-neutral-600 dark:bg-neutral-900"
{% if include_bride_side %}checked{% endif %}
>
<span>包含女方</span>
</label>
<button type="submit" class="btn btn-primary flex-1 sm:flex-none">搜索</button>
<a class="btn btn-accent flex-1 sm:flex-none" href="{{ url_for('quick_entry.new_household', q=search_term, include_bride_side='1') if include_bride_side else (url_for('quick_entry.new_household', q=search_term) if search_term else url_for('quick_entry.new_household')) }}">新增</a>
<a class="btn btn-secondary flex-1 sm:flex-none" href="{{ url_for('quick_entry.index') }}">重置</a>
</div>
</div>
</form>
</div>
</section>
{% include "quick_entry/_search_results.html" %}
<div id="quick-entry-edit-modal" class="hidden fixed inset-0 z-50 flex items-center justify-center bg-black/50 px-4 py-6">
<div id="quick-entry-edit-modal-body" class="w-full max-w-xl"></div>
</div>
</div>
<script>
(function () {
const form = document.getElementById('quick-entry-search-form');
const resultsRegionId = 'quick-entry-search-results-region';
const modal = document.getElementById('quick-entry-edit-modal');
const modalBody = document.getElementById('quick-entry-edit-modal-body');
if (!form) {
return;
}
let debounceTimer = null;
let currentRequest = null;
function closeModal() {
if (!modal || !modalBody) {
return;
}
modal.classList.add('hidden');
modalBody.innerHTML = '';
}
async function openEditModal(url) {
if (!modal || !modalBody) {
return;
}
try {
const response = await window.fetch(url, {
headers: { 'X-HW-Partial': 'edit-modal' },
});
if (!response.ok) {
throw new Error(`unexpected status ${response.status}`);
}
modalBody.innerHTML = await response.text();
modal.classList.remove('hidden');
const amountField = modalBody.querySelector('#total_gift_amount');
if (amountField) {
amountField.focus();
amountField.select();
}
} catch (error) {
window.console.error('Quick entry edit modal failed', error);
}
}
function scheduleRefresh() {
window.clearTimeout(debounceTimer);
debounceTimer = window.setTimeout(refreshResults, 300);
}
async function refreshResults() {
if (currentRequest) {
currentRequest.abort();
}
const controller = new AbortController();
currentRequest = controller;
const params = new URLSearchParams(new FormData(form));
params.set('partial', 'search-results');
try {
const response = await window.fetch(`${form.action}?${params.toString()}`, {
headers: { 'X-HW-Partial': 'search-results' },
signal: controller.signal,
});
if (!response.ok) {
throw new Error(`unexpected status ${response.status}`);
}
const html = await response.text();
const parser = new DOMParser();
const nextDocument = parser.parseFromString(html, 'text/html');
const nextRegion = nextDocument.getElementById(resultsRegionId);
const currentRegion = document.getElementById(resultsRegionId);
if (nextRegion && currentRegion) {
currentRegion.replaceWith(nextRegion);
}
window.history.replaceState({}, '', `${form.action}?${new URLSearchParams(new FormData(form)).toString()}`);
} catch (error) {
if (error instanceof DOMException && error.name === 'AbortError') {
return;
}
window.console.error('Quick entry live refresh failed', error);
} finally {
if (currentRequest === controller) {
currentRequest = null;
}
}
}
form.querySelectorAll('input').forEach(function (field) {
field.addEventListener('input', scheduleRefresh);
field.addEventListener('change', scheduleRefresh);
});
document.addEventListener('click', function (event) {
const editButton = event.target.closest('[data-quick-entry-edit-url]');
if (editButton) {
event.preventDefault();
openEditModal(editButton.getAttribute('data-quick-entry-edit-url'));
return;
}
const closeButton = event.target.closest('[data-quick-entry-modal-close]');
if (closeButton) {
event.preventDefault();
closeModal();
return;
}
if (modal && event.target === modal) {
closeModal();
}
});
document.addEventListener('keydown', function (event) {
if (event.key === 'Escape' && modal && !modal.classList.contains('hidden')) {
closeModal();
}
});
}());
</script>
{% endblock %}