CaiWuXiTong/resources/views/business/pages/partials/date-range-picker.blade.php
yueyang 6af12c030e 财务经营报表:新增账单明细页(多选筛选/日历选择/四项汇总/按列导出与进度/收款明细同款分页),科目映射表字段改名财务备注与财务类型
- 账单明细:7 个平台账单分支下推查询,按 active_generation 取当前版本(修正抖音重复计数),跨月大数据量由 73s 降到 1-2s

- 筛选:平台/店铺/账单项目/财务类型多选下拉(打字模糊搜索+滚动),日期改日历选择

- 导出:全部筛选结果 CSV 流式下载并按显示列最小取数,带进度轮询;列设置按账号存本机

- 分页:每页 10/20/50,首页/上下页/末页,第 X-Y 条/共 N 条

- 科目映射表:字段改名财务备注/财务类型(导入兼容旧名)
2026-10-06 15:35:16 +08:00

325 lines
13 KiB
PHP
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.

{{--
开始日期 ─ 至 ─ 结束日期 的日历选择控件(与「小额打款」页同一套交互)。
用法:
<div class="upstream-data-field upstream-data-field--dates">
<span>发生时间</span>
<div class="upstream-data-date-range">
<span class="upstream-data-date" data-date-picker>
<input type="text" name="start" readonly data-date-input>
<button type="button" class="upstream-data-date__clear" data-date-clear hidden>…</button>
<button type="button" class="upstream-data-date__pick" data-date-toggle>…</button>
<div class="upstream-data-calendar" data-date-calendar hidden></div>
</span>
<b class="upstream-data-date-range__sep">至</b>
<span class="upstream-data-date" data-date-picker>…</span>
</div>
</div>
window.CaiWuDatePicker.setup(document.getElementById('pdFilterForm'));
说明:样式与脚本目前与退款 › 小额打款工单页保持一致(那页是内联实现),
后续如要合并,把那边也改成引用本 partial。
--}}
<style>
.upstream-data-field--dates { flex: 1 1 20rem; }
.upstream-data-date-range { display: flex; align-items: center; gap: 0.5rem; }
.upstream-data-date { position: relative; display: flex; flex: 1 1 8.5rem; min-width: 0; }
.upstream-data-date input {
flex: 1 1 auto;
width: auto;
min-width: 0;
padding-right: 1.75rem;
border-radius: 0.5rem 0 0 0.5rem;
cursor: pointer;
}
.upstream-data-date__clear {
position: absolute;
top: 0;
right: 2.25rem;
bottom: 0;
display: inline-flex;
align-items: center;
justify-content: center;
width: 1.5rem;
height: 1.5rem;
margin: auto 0;
padding: 0;
border: 0;
border-radius: 0.25rem;
background: transparent;
color: var(--vip-content-muted, #595959);
cursor: pointer;
}
.upstream-data-date__clear:hover {
background: rgba(133, 102, 50, 0.12);
color: var(--vip-content-accent-dark, #856632);
}
.upstream-data-date__clear[hidden] { display: none; }
.upstream-data-date__clear .material-icons-outlined { font-size: 0.9375rem; }
.upstream-data-date__pick {
display: inline-flex;
flex: 0 0 2.125rem;
align-items: center;
justify-content: center;
width: 2.125rem;
height: 2.125rem;
padding: 0;
border: 1px solid var(--vip-border, #d8d2c7);
border-left: 0;
border-radius: 0 0.5rem 0.5rem 0;
background: var(--vip-surface, #ffffff);
color: var(--vip-content-accent-dark, #856632);
cursor: pointer;
}
.upstream-data-date__pick:hover,
.upstream-data-date__pick:focus-visible {
background: #f7f0de;
color: #6a5129;
outline: none;
}
.upstream-data-date__pick .material-icons-outlined { font-size: 1.125rem; }
.upstream-data-date-range__sep {
color: var(--vip-content-muted, #595959);
font-weight: 400;
}
.upstream-data-calendar {
position: absolute;
top: calc(100% + 0.35rem);
left: 0;
z-index: 30;
width: 16.5rem;
padding: 0.5rem;
border: 1px solid var(--vip-border, #d8d2c7);
border-radius: 0.5rem;
background: var(--vip-surface, #ffffff);
box-shadow: 0 4px 8px rgba(23, 23, 23, 0.1);
}
.upstream-data-calendar[hidden] { display: none; }
.upstream-data-calendar__head {
display: flex;
align-items: center;
justify-content: space-between;
gap: 0.25rem;
margin-bottom: 0.25rem;
}
.upstream-data-calendar__title {
color: var(--vip-content-ink, #171717);
font-size: 0.8125rem;
font-weight: 600;
}
.upstream-data-calendar__nav {
display: inline-flex;
align-items: center;
justify-content: center;
width: 1.5rem;
height: 1.5rem;
padding: 0;
border: 0;
border-radius: 0.25rem;
background: transparent;
color: var(--vip-content-accent-dark, #856632);
cursor: pointer;
}
.upstream-data-calendar__nav:hover { background: #f7f0de; }
.upstream-data-calendar__nav .material-icons-outlined { font-size: 1.125rem; }
.upstream-data-calendar__week,
.upstream-data-calendar__grid {
display: grid;
grid-template-columns: repeat(7, 1fr);
gap: 0.125rem;
}
.upstream-data-calendar__week span {
padding: 0.25rem 0;
color: var(--vip-content-muted, #595959);
font-size: 0.75rem;
text-align: center;
}
.upstream-data-calendar__day {
height: 1.75rem;
padding: 0;
border: 1px solid transparent;
border-radius: 0.25rem;
background: transparent;
color: var(--vip-content-ink, #171717);
cursor: pointer;
font: inherit;
font-size: 0.75rem;
font-variant-numeric: tabular-nums;
}
.upstream-data-calendar__day:hover { background: #f7f0de; }
.upstream-data-calendar__day.is-outside { color: #b9b3a8; }
.upstream-data-calendar__day.is-today { border-color: var(--vip-content-accent-dark, #856632); }
.upstream-data-calendar__day.is-selected {
border-color: var(--vip-content-accent-dark, #856632);
background: var(--vip-content-accent-dark, #856632);
color: #ffffff;
}
/* 结束日期的日历向右展开会顶出工具栏,改成向左 */
.upstream-data-date:last-of-type .upstream-data-calendar {
right: 0;
left: auto;
}
</style>
<script>
(function () {
if (window.CaiWuDatePicker) { return; }
var closers = [];
var WEEK_LABELS = ['日', '一', '二', '三', '四', '五', '六'];
function pad2(number) {
return (number < 10 ? '0' : '') + number;
}
function isoDate(date) {
return date.getFullYear() + '-' + pad2(date.getMonth() + 1) + '-' + pad2(date.getDate());
}
function parseIsoDate(text) {
var match = /^(\d{4})-(\d{2})-(\d{2})/.exec(String(text || '').trim());
if (!match) { return null; }
var date = new Date(Number(match[1]), Number(match[2]) - 1, Number(match[3]));
return date.getFullYear() === Number(match[1])
&& date.getMonth() === Number(match[2]) - 1
&& date.getDate() === Number(match[3])
? date
: null;
}
function sameDay(left, right) {
return left.getFullYear() === right.getFullYear()
&& left.getMonth() === right.getMonth()
&& left.getDate() === right.getDate();
}
function setupDatePicker(picker) {
var input = picker.querySelector('[data-date-input]');
var panel = picker.querySelector('[data-date-calendar]');
var toggle = picker.querySelector('[data-date-toggle]');
var clear = picker.querySelector('[data-date-clear]');
if (!input || !panel || !toggle) { return; }
var today = new Date();
var selected = parseIsoDate(input.value);
var anchor = selected || today;
var viewYear = anchor.getFullYear();
var viewMonth = anchor.getMonth();
function isOpen() { return !panel.hidden; }
function syncClear() {
if (clear) { clear.hidden = input.value === ''; }
}
function render() {
var monthStart = new Date(viewYear, viewMonth, 1);
var gridStart = new Date(viewYear, viewMonth, 1 - monthStart.getDay());
var html = '<div class="upstream-data-calendar__head">'
+ '<button type="button" class="upstream-data-calendar__nav" data-calendar-nav="-1" aria-label="上个月">'
+ '<span class="material-icons-outlined" aria-hidden="true">chevron_left</span></button>'
+ '<span class="upstream-data-calendar__title">' + viewYear + ' 年 ' + (viewMonth + 1) + ' 月</span>'
+ '<button type="button" class="upstream-data-calendar__nav" data-calendar-nav="1" aria-label="下个月">'
+ '<span class="material-icons-outlined" aria-hidden="true">chevron_right</span></button>'
+ '</div>'
+ '<div class="upstream-data-calendar__week">'
+ WEEK_LABELS.map(function (label) { return '<span>' + label + '</span>'; }).join('')
+ '</div>'
+ '<div class="upstream-data-calendar__grid">';
for (var offset = 0; offset < 42; offset += 1) {
var day = new Date(gridStart.getFullYear(), gridStart.getMonth(), gridStart.getDate() + offset);
var classes = 'upstream-data-calendar__day';
if (day.getMonth() !== viewMonth) { classes += ' is-outside'; }
if (sameDay(day, today)) { classes += ' is-today'; }
if (selected && sameDay(day, selected)) { classes += ' is-selected'; }
html += '<button type="button" class="' + classes + '" data-calendar-day="'
+ isoDate(day) + '">' + day.getDate() + '</button>';
}
panel.innerHTML = html + '</div>';
}
function close() {
if (!isOpen()) { return; }
panel.hidden = true;
toggle.setAttribute('aria-expanded', 'false');
}
function open() {
closers.forEach(function (otherClose) { if (otherClose !== close) { otherClose(); } });
if (isOpen()) { return; }
selected = parseIsoDate(input.value);
anchor = selected || new Date();
viewYear = anchor.getFullYear();
viewMonth = anchor.getMonth();
render();
panel.hidden = false;
toggle.setAttribute('aria-expanded', 'true');
}
function shiftMonth(step) {
anchor = new Date(viewYear, viewMonth + step, 1);
viewYear = anchor.getFullYear();
viewMonth = anchor.getMonth();
render();
}
closers.push(close);
toggle.setAttribute('aria-expanded', 'false');
panel.addEventListener('click', function (event) {
// 必须拦住冒泡:翻月会重绘面板,被点的箭头随即脱离 DOM,
// 外层的「点空白处收起」监听会因此误判成点了外面,把日历关掉。
event.stopPropagation();
var nav = event.target.closest('[data-calendar-nav]');
if (nav && panel.contains(nav)) {
shiftMonth(Number(nav.getAttribute('data-calendar-nav')));
return;
}
var day = event.target.closest('[data-calendar-day]');
if (day && panel.contains(day)) {
input.value = day.getAttribute('data-calendar-day');
selected = parseIsoDate(input.value);
syncClear();
close();
}
});
input.addEventListener('click', open);
input.addEventListener('keydown', function (event) {
if (event.key === 'Enter' || event.key === ' ' || event.key === 'ArrowDown') {
event.preventDefault();
open();
}
});
picker.addEventListener('keydown', function (event) {
if (event.key === 'Escape') { close(); }
});
toggle.addEventListener('click', function () {
if (isOpen()) { close(); } else { open(); }
});
if (clear) {
clear.addEventListener('click', function () {
input.value = '';
selected = null;
syncClear();
close();
});
}
document.addEventListener('click', function (event) {
var target = event.target;
// 目标已经不在文档里(点了会被重绘的日历按钮)时,别当成点了外面
if (target && target.isConnected === false) { return; }
if (!picker.contains(target)) { close(); }
});
syncClear();
}
window.CaiWuDatePicker = {
setup: function (scope) {
(scope || document).querySelectorAll('[data-date-picker]').forEach(setupDatePicker);
}
};
})();
</script>