feat: 精简供应商应付表分页并冻结前列
This commit is contained in:
parent
850db1a0b4
commit
df90a5032b
@ -4100,8 +4100,12 @@ class Pages extends Controller
|
||||
}));
|
||||
}
|
||||
|
||||
// 货款/退货明细较宽,固定每页 15 行,减少纵向滚动并保持横向核对区稳定。
|
||||
$perPage = in_array($dataset, ['payment-reconciliation', 'return-reconciliation'], true) ? 15 : 50;
|
||||
// 宽表固定每页 15 行,减少纵向滚动并保持横向核对区稳定。
|
||||
$perPage = in_array($dataset, [
|
||||
'payment-reconciliation',
|
||||
'return-reconciliation',
|
||||
'supplier-payables-report',
|
||||
], true) ? 15 : 50;
|
||||
$currentPage = max((int) request('page', 1), 1);
|
||||
|
||||
return [
|
||||
|
||||
@ -9,6 +9,8 @@
|
||||
$finalKeyword = $payablesFinal['keyword'] ?? '';
|
||||
$finalActions = $payablesFinal['actions'] ?? '';
|
||||
$finalMaintenance = ['dataset' => $finalDataset, 'fields' => $finalFields];
|
||||
$freezeThroughSupplier = $finalDataset === 'supplier-payables-report';
|
||||
$freezeThroughSupplierFields = ['summary_month', 'checker', 'settlement_method', 'company', 'supplier'];
|
||||
@endphp
|
||||
|
||||
<div class="payables-final-page">
|
||||
@ -42,6 +44,15 @@
|
||||
.payables-final-table .is-number { text-align: right; }
|
||||
.payables-final-table .is-empty td { height: 8rem; color: var(--vip-content-muted, #595959); text-align: center; }
|
||||
.payables-final-pagination { display: flex; align-items: center; justify-content: space-between; gap: 1rem; padding: 0.75rem 1rem; color: var(--vip-content-muted, #595959); font-size: 0.8125rem; }
|
||||
.payables-final-table--freeze-through-supplier { border-collapse: separate; border-spacing: 0; }
|
||||
.payables-final-table--freeze-through-supplier th,
|
||||
.payables-final-table--freeze-through-supplier td { border-right: 1px solid var(--vip-border, #D8D2C7); }
|
||||
.payables-final-table--freeze-through-supplier th:last-child,
|
||||
.payables-final-table--freeze-through-supplier td:last-child { border-right: 0; }
|
||||
.payables-final-table--freeze-through-supplier tbody tr:hover td { background: #FBF8F0; }
|
||||
.payables-final-freeze-column { position: sticky; z-index: 1; background: var(--vip-surface, #FFFFFF); }
|
||||
.payables-final-table thead .payables-final-freeze-column { z-index: 3; background: var(--vip-content-accent-soft, #F7F0DE); }
|
||||
.payables-final-freeze-edge { box-shadow: inset -2px 0 0 var(--vip-content-accent, #B9954F); }
|
||||
</style>
|
||||
|
||||
<form method="GET" class="payables-final-toolbar" aria-label="{{ $page['title'] ?? '' }}筛选">
|
||||
@ -77,12 +88,26 @@
|
||||
|
||||
<div class="payables-final-card">
|
||||
<div class="payables-final-scroll">
|
||||
<table class="payables-final-table">
|
||||
<table @class([
|
||||
'payables-final-table',
|
||||
'payables-final-table--freeze-through-supplier' => $freezeThroughSupplier,
|
||||
])>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>序号</th>
|
||||
<th
|
||||
@if ($freezeThroughSupplier) data-final-column="__index" @endif
|
||||
@class(['payables-final-freeze-column' => $freezeThroughSupplier])
|
||||
>序号</th>
|
||||
@foreach ($finalFields as $field)
|
||||
<th @class(['is-number' => $field['type'] === 'number'])>{{ $field['label'] }}</th>
|
||||
@php $freezeField = $freezeThroughSupplier && in_array($field['name'], $freezeThroughSupplierFields, true); @endphp
|
||||
<th
|
||||
@if ($freezeField) data-final-column="{{ $field['name'] }}" @endif
|
||||
@class([
|
||||
'is-number' => $field['type'] === 'number',
|
||||
'payables-final-freeze-column' => $freezeField,
|
||||
'payables-final-freeze-edge' => $freezeThroughSupplier && $field['name'] === 'supplier',
|
||||
])
|
||||
>{{ $field['label'] }}</th>
|
||||
@endforeach
|
||||
</tr>
|
||||
</thead>
|
||||
@ -90,10 +115,21 @@
|
||||
@forelse ($finalRows ?? [] as $index => $row)
|
||||
<tr data-supplier-maintenance-row data-key="{{ $row['_action_key'] ?? '' }}"
|
||||
data-row="{!! e(json_encode($row, JSON_UNESCAPED_UNICODE)) !!}" tabindex="0" aria-selected="false">
|
||||
<td>{{ ($finalRows->firstItem() ?? 1) + $index }}</td>
|
||||
<td
|
||||
@if ($freezeThroughSupplier) data-final-column="__index" @endif
|
||||
@class(['payables-final-freeze-column' => $freezeThroughSupplier])
|
||||
>{{ ($finalRows->firstItem() ?? 1) + $index }}</td>
|
||||
@foreach ($finalFields as $field)
|
||||
@php $value = $row[$field['name']] ?? null; @endphp
|
||||
<td @class(['is-number' => $field['type'] === 'number'])>
|
||||
@php $freezeField = $freezeThroughSupplier && in_array($field['name'], $freezeThroughSupplierFields, true); @endphp
|
||||
<td
|
||||
@if ($freezeField) data-final-column="{{ $field['name'] }}" @endif
|
||||
@class([
|
||||
'is-number' => $field['type'] === 'number',
|
||||
'payables-final-freeze-column' => $freezeField,
|
||||
'payables-final-freeze-edge' => $freezeThroughSupplier && $field['name'] === 'supplier',
|
||||
])
|
||||
>
|
||||
@if ($value === null || $value === '')
|
||||
-
|
||||
@elseif ($field['type'] === 'number' && is_numeric($value))
|
||||
@ -119,7 +155,7 @@
|
||||
<a href="{{ $finalRows->previousPageUrl() }}">上一页</a>
|
||||
@endif
|
||||
第 {{ $finalRows?->currentPage() ?? 1 }} 页 / 共 {{ $finalRows?->lastPage() ?? 1 }} 页
|
||||
· 每页 {{ $finalRows?->perPage() ?? (in_array($finalDataset, ['payment-reconciliation', 'return-reconciliation'], true) ? 15 : 50) }} 条
|
||||
· 每页 {{ $finalRows?->perPage() ?? (in_array($finalDataset, ['payment-reconciliation', 'return-reconciliation', 'supplier-payables-report'], true) ? 15 : 50) }} 条
|
||||
@if ($finalRows?->hasMorePages())
|
||||
<a href="{{ $finalRows->nextPageUrl() }}">下一页</a>
|
||||
@endif
|
||||
@ -128,6 +164,51 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@if ($freezeThroughSupplier)
|
||||
<script data-caiwu-replay>
|
||||
(() => {
|
||||
document.querySelectorAll('.payables-final-table--freeze-through-supplier').forEach((table) => {
|
||||
if (table.dataset.freezeColumnsReady === 'true') {
|
||||
return;
|
||||
}
|
||||
|
||||
const headers = [...table.querySelectorAll('thead th[data-final-column]')];
|
||||
const supplierIndex = headers.findIndex((header) => header.dataset.finalColumn === 'supplier');
|
||||
|
||||
if (supplierIndex < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
table.dataset.freezeColumnsReady = 'true';
|
||||
const frozenHeaders = headers.slice(0, supplierIndex + 1);
|
||||
|
||||
const updateFreezeOffsets = () => {
|
||||
let offset = 0;
|
||||
|
||||
frozenHeaders.forEach((header) => {
|
||||
const column = header.dataset.finalColumn;
|
||||
table.querySelectorAll(`[data-final-column="${column}"]`).forEach((cell) => {
|
||||
cell.style.left = `${offset}px`;
|
||||
});
|
||||
offset += header.getBoundingClientRect().width;
|
||||
});
|
||||
};
|
||||
|
||||
updateFreezeOffsets();
|
||||
|
||||
if (typeof ResizeObserver === 'undefined') {
|
||||
window.addEventListener('resize', updateFreezeOffsets, { passive: true });
|
||||
return;
|
||||
}
|
||||
|
||||
const resizeObserver = new ResizeObserver(updateFreezeOffsets);
|
||||
resizeObserver.observe(table);
|
||||
frozenHeaders.forEach((header) => resizeObserver.observe(header));
|
||||
});
|
||||
})();
|
||||
</script>
|
||||
@endif
|
||||
|
||||
@include('business.pages.supplier-payables.maintenance-blade-modal', [
|
||||
'content' => ['supplierPayableMaintenance' => $finalMaintenance],
|
||||
'supplierMaintenanceAction' => $finalActions,
|
||||
|
||||
@ -295,6 +295,53 @@ class SupplierPayableFinalPagesTest extends FeatureTestCase
|
||||
->assertDontSeeText('PM-01');
|
||||
}
|
||||
|
||||
public function test_final_supplier_payables_report_paginates_and_freezes_through_supplier(): void
|
||||
{
|
||||
$records = [];
|
||||
for ($index = 1; $index <= 20; $index++) {
|
||||
$records[sprintf('supplier-payable:%02d', $index)] = [
|
||||
'summary_month' => '2026-09',
|
||||
'checker' => sprintf('对账人%02d', $index),
|
||||
'settlement_method' => '月结',
|
||||
'company' => sprintf('供应商主体%02d', $index),
|
||||
'supplier' => sprintf('SUP-%02d', $index),
|
||||
'express_freight' => '0',
|
||||
];
|
||||
}
|
||||
app(SupplierPayableOverrideStore::class)->saveBatch(
|
||||
'final:supplier:supplier-payables-report',
|
||||
$records,
|
||||
(int) $this->company->id,
|
||||
(int) $this->user->id,
|
||||
);
|
||||
|
||||
$baseUrl = route('business.pages.show', [
|
||||
'company_id' => (int) $this->company->id,
|
||||
'section' => 'supplier-payables-final',
|
||||
'page' => 'supplier-payables-report',
|
||||
]);
|
||||
$url = fn (int $page): string => $baseUrl . '?page=' . $page;
|
||||
|
||||
$this->loginAs()->get($url(1))
|
||||
->assertOk()
|
||||
->assertSee('payables-final-table--freeze-through-supplier', false)
|
||||
->assertSee('data-final-column="summary_month"', false)
|
||||
->assertSee('data-final-column="supplier"', false)
|
||||
->assertSee('payables-final-freeze-edge', false)
|
||||
->assertSee('data-caiwu-replay', false)
|
||||
->assertSeeText('每页 15 条')
|
||||
->assertSeeText('第 1 页 / 共 2 页')
|
||||
->assertSeeText('SUP-01')
|
||||
->assertDontSeeText('SUP-16');
|
||||
|
||||
$this->loginAs()->get($url(2))
|
||||
->assertOk()
|
||||
->assertSeeText('第 2 页 / 共 2 页')
|
||||
->assertSeeText('SUP-16')
|
||||
->assertSeeText('SUP-20')
|
||||
->assertDontSeeText('SUP-01');
|
||||
}
|
||||
|
||||
public function test_final_import_reads_files_larger_than_one_chunk(): void
|
||||
{
|
||||
$lines = ['月份,入库单号,采购单号,日期,供应商,货号,商品编码,规格名称,数量,单价,金额,预估单价,预估金额,供应商商品编码,备注,仓库名称,快递名称,快递单号,未发出,对账人'];
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user