diff --git a/resources/views/business/pages/financial-ledger/ledger.blade.php b/resources/views/business/pages/financial-ledger/ledger.blade.php index 408b6747..3b6fea59 100644 --- a/resources/views/business/pages/financial-ledger/ledger.blade.php +++ b/resources/views/business/pages/financial-ledger/ledger.blade.php @@ -302,10 +302,10 @@ .ledger-accounts-table-wrap { max-height:calc(100vh - 390px); min-height:20rem; } #ledger-panel-accounts[aria-busy="true"] .ledger-accounts-table-wrap { opacity:.58; pointer-events:none; } .ledger-pagination { display:flex; align-items:center; justify-content:space-between; gap:.75rem; padding:.65rem .9rem; color:var(--vip-content-muted, #595959); font-size:.72rem; } - .ledger-pagination a { color:var(--vip-content-accent-dark, #856632); text-decoration:none; margin-left:.75rem; font-weight:600; } + .ledger-pagination a { color:var(--vip-content-accent-dark, #856632); text-decoration:none; font-weight:600; } .ledger-pagination__controls { display:flex; align-items:center; justify-content:flex-end; gap:.45rem; margin-left:auto; } - .ledger-page-box { display:inline-flex; align-items:center; gap:.1rem; height:1.7rem; } - .ledger-page-box__arrow { display:inline-grid; place-items:center; width:1.2rem; height:1.5rem; padding:0; border:0; background:transparent; color:var(--vip-content-accent-dark, #856632); font:inherit; text-decoration:none; } + .ledger-page-box { display:inline-flex; align-items:center; gap:.05rem; height:1.75rem; } + .ledger-page-box__arrow { display:inline-grid; place-items:center; width:1.2rem; height:1.5rem; margin:0; padding:0; border:0; background:transparent; color:var(--vip-content-accent-dark, #856632); font:inherit; text-decoration:none; } .ledger-page-box__arrow:hover { color:#6A5129; } .ledger-page-box__arrow:focus:not(:focus-visible) { outline:none; } .ledger-page-box__arrow:focus-visible { outline:1px solid var(--vip-content-accent-dark, #856632); outline-offset:0; border-radius:2px; } @@ -314,6 +314,9 @@ .ledger-page-box__triangle.is-next { border-left:8px solid currentColor; } .ledger-page-box__arrow.is-disabled { color:#b9b3a9; cursor:not-allowed; } .ledger-page-box__current { min-width:2.2rem; padding:0 .15rem; color:var(--vip-content-ink, #171717); font-size:.7rem; font-variant-numeric:tabular-nums; font-weight:700; text-align:center; white-space:nowrap; } + .ledger-page-box__select { width:4.45rem; height:1.55rem; padding:0 1.25rem 0 .35rem; border:1px solid var(--vip-border, #D8D2C7); border-radius:.35rem; background:#fff; color:var(--vip-content-ink, #171717); font:inherit; font-size:.68rem; font-variant-numeric:tabular-nums; font-weight:700; text-align:center; text-align-last:center; cursor:pointer; } + .ledger-page-box__select:hover { border-color:var(--vip-content-accent, #B9954F); } + .ledger-page-box__select:focus { border-color:var(--vip-content-accent-dark, #856632); outline:none; box-shadow:0 0 0 3px rgba(133,102,50,.14); } .ledger-empty { padding:3.5rem 1rem; text-align:center; color:var(--vip-content-muted, #595959); } .ledger-progress { display:flex; align-items:center; gap:.65rem; min-width:15rem; } .ledger-progress__track { flex:1; height:.45rem; overflow:hidden; border-radius:999px; background:#e8e3da; } @@ -489,24 +492,11 @@ -
- 共 {{ number_format($ledgerAccounts->total()) }} 条 · 每页 100 条 -
- -
-
+ @include('business.pages.financial-ledger.partials.pagination', [ + 'rows' => $ledgerAccounts, + 'paginationKey' => 'accounts', + 'paginationLabel' => '账户总表分页', + ]) {{-- 店铺台账 --}} @@ -536,7 +526,11 @@ @forelse ($ledgerShops as $row){{ $row->shop_name }}{{ $row->platform }}{{ $row->company_name }}{{ $row->account_short }}{{ $row->status }}{{ $row->payout_account }}{{ $money($row->shop_balance) }}{{ $row->remark }}@empty
没有匹配的店铺数据。
@endforelse -
共 {{ number_format($ledgerShops->total()) }} 条 · 第 {{ $ledgerShops->currentPage() }} / {{ $ledgerShops->lastPage() }} 页@if (!$ledgerShops->onFirstPage())上一页@endif @if ($ledgerShops->hasMorePages())下一页@endif
+ @include('business.pages.financial-ledger.partials.pagination', [ + 'rows' => $ledgerShops, + 'paginationKey' => 'shops', + 'paginationLabel' => '店铺台账分页', + ]) @@ -567,7 +561,11 @@ @forelse ($ledgerFlows as $row){{ $date($row->flow_date) }}{{ $row->company_name }}{{ $row->account_short }}{{ $money($row->opening_balance) }}{{ $row->inflow > 0 ? $money($row->inflow) : '' }}{{ $row->outflow > 0 ? $money($row->outflow) : '' }}{{ $money($row->closing_balance) }}{{ $row->remark }}@empty
当前筛选没有流水数据;期初日会为每个期初账户生成一行。
@endforelse -
共 {{ number_format($ledgerFlows->total()) }} 条 · 第 {{ $ledgerFlows->currentPage() }} / {{ $ledgerFlows->lastPage() }} 页@if (!$ledgerFlows->onFirstPage())上一页@endif @if ($ledgerFlows->hasMorePages())下一页@endif
+ @include('business.pages.financial-ledger.partials.pagination', [ + 'rows' => $ledgerFlows, + 'paginationKey' => 'flows', + 'paginationLabel' => '每日资金流水分页', + ]) @@ -653,7 +651,11 @@ @endforelse -
共 {{ number_format($ledgerTransactions->total()) }} 条 · 第 {{ $ledgerTransactions->currentPage() }} / {{ $ledgerTransactions->lastPage() }} 页@if (!$ledgerTransactions->onFirstPage())上一页@endif @if ($ledgerTransactions->hasMorePages())下一页@endif
+ @include('business.pages.financial-ledger.partials.pagination', [ + 'rows' => $ledgerTransactions, + 'paginationKey' => 'transactions', + 'paginationLabel' => '动账明细分页', + ]) @@ -706,7 +708,13 @@ @forelse (($mapping['rows'] ?? collect()) as $row){{ $sourceLabels[$row->source] ?? $row->source }}{{ $row->source_value }}{{ number_format((int) $row->txn_count) }}{{ $money($row->income_sum) }}{{ $money($row->expense_sum) }}@if ($canEdit)@else{{ $row->account_short ?: '未匹配' }}@endif{{ $row->account_short ? '已映射' : '未匹配' }}@empty
没有匹配的映射记录。
@endforelse -
共 {{ number_format($mapping['total'] ?? 0) }} 条 · 第 {{ ($mapping['rows'] ?? null)?->currentPage() ?? 1 }} / {{ ($mapping['rows'] ?? null)?->lastPage() ?? 1 }} 页@if (($mapping['rows'] ?? null) && !$mapping['rows']->onFirstPage())上一页@endif @if (($mapping['rows'] ?? null) && $mapping['rows']->hasMorePages())下一页@endif
+ @if (($mapping['rows'] ?? null) !== null) + @include('business.pages.financial-ledger.partials.pagination', [ + 'rows' => $mapping['rows'], + 'paginationKey' => 'mapping', + 'paginationLabel' => '账户映射分页', + ]) + @endif @foreach (($ledgerAccountOptions ?? []) as $option)@endforeach @@ -1515,81 +1523,120 @@ applyAccountEditMode(accountEditToggle?.checked === true); let accountPageRequest = null; + const loadAccountPage = async (url) => { + const pageUrl = new URL(url, window.location.href); + pageUrl.searchParams.set('tab', 'accounts'); + if (!accountPanel) { + window.location.assign(pageUrl.toString()); + return; + } + + const currentBody = accountPanel.querySelector('.ledger-accounts-table tbody'); + const currentSummary = accountPanel.querySelector('.ledger-summary'); + const currentPagination = accountPanel.querySelector('.ledger-pagination'); + const tableWrap = accountPanel.querySelector('.ledger-accounts-table-wrap'); + if (!currentBody || !currentSummary || !currentPagination || !tableWrap) { + window.location.assign(pageUrl.toString()); + return; + } + + const tableScrollTop = tableWrap.scrollTop; + const tableScrollLeft = tableWrap.scrollLeft; + accountPageRequest?.abort(); + const request = new AbortController(); + accountPageRequest = request; + const targetPage = pageUrl.searchParams.get('accounts_page') || '1'; + accountPanel.setAttribute('aria-busy', 'true'); + notify('正在加载第 ' + targetPage + ' 页…', false, true); + try { + const response = await fetch(pageUrl.toString(), { + credentials: 'same-origin', + headers: { + 'Accept': 'text/html', + 'X-Requested-With': 'XMLHttpRequest', + }, + signal: request.signal, + }); + if (!response.ok) throw new Error('分页加载失败(HTTP ' + response.status + ')'); + + const nextDocument = new DOMParser().parseFromString(await response.text(), 'text/html'); + const nextPanel = nextDocument.querySelector('#ledger-panel-accounts'); + const nextBody = nextPanel?.querySelector('.ledger-accounts-table tbody'); + const nextSummary = nextPanel?.querySelector('.ledger-summary'); + const nextPagination = nextPanel?.querySelector('.ledger-pagination'); + if (!nextBody || !nextSummary || !nextPagination) throw new Error('分页内容解析失败'); + + currentBody.replaceWith(document.importNode(nextBody, true)); + currentSummary.replaceWith(document.importNode(nextSummary, true)); + currentPagination.replaceWith(document.importNode(nextPagination, true)); + if (window.history?.replaceState) { + window.history.replaceState({ ...window.history.state, ledgerAccountsAjax: true }, '', pageUrl.toString()); + } + + const nextTableWrap = accountPanel.querySelector('.ledger-accounts-table-wrap'); + if (nextTableWrap) { + nextTableWrap.scrollTop = tableScrollTop; + nextTableWrap.scrollLeft = tableScrollLeft; + } + bindInlineAccountSelect('[data-account-status]', 'status', '账户状态已保存'); + bindInlineAccountSelect('[data-account-type]', 'account_type', '账户类型已保存'); + bindInlineAccountSelect('[data-account-plan]', 'company_plan', '公司规划已保存'); + bindInlineAccountSelect('[data-account-keep]', 'account_should_keep', '账户是否保留已保存'); + applyAccountEditMode(accountEditToggle?.checked === true); + applyAccountColumnLayout(); + bindAccountPagination(); + notify('已加载第 ' + targetPage + ' 页'); + } catch (error) { + if (error.name !== 'AbortError') { + const pageSelect = accountPanel.querySelector('[data-ledger-page-select]'); + const selectedOption = pageSelect?.querySelector('option[selected]'); + if (pageSelect && selectedOption) pageSelect.value = selectedOption.value; + notify(error.message || '分页加载失败', true); + } + } finally { + if (accountPageRequest === request) { + accountPanel.removeAttribute('aria-busy'); + } + } + }; const bindAccountPagination = () => { if (!accountPanel) return; accountPanel.querySelectorAll('.ledger-page-box__arrow[href]').forEach((link) => { if (link.dataset.ajaxBound === '1') return; link.dataset.ajaxBound = '1'; - link.addEventListener('click', async (event) => { + link.addEventListener('click', (event) => { if (event.metaKey || event.ctrlKey || event.shiftKey || event.altKey || event.button !== 0) return; event.preventDefault(); - - const currentBody = accountPanel.querySelector('.ledger-accounts-table tbody'); - const currentSummary = accountPanel.querySelector('.ledger-summary'); - const currentPagination = accountPanel.querySelector('.ledger-pagination'); - const tableWrap = accountPanel.querySelector('.ledger-accounts-table-wrap'); - if (!currentBody || !currentSummary || !currentPagination || !tableWrap) { - window.location.assign(link.href); - return; - } - - const tableScrollTop = tableWrap.scrollTop; - const tableScrollLeft = tableWrap.scrollLeft; - accountPageRequest?.abort(); - const request = new AbortController(); - accountPageRequest = request; - const targetPage = new URL(link.href, window.location.href).searchParams.get('accounts_page'); - const loadingMessage = targetPage ? '正在加载第 ' + targetPage + ' 页…' : '正在加载下一页…'; - accountPanel.setAttribute('aria-busy', 'true'); - notify(loadingMessage, false, true); - try { - const response = await fetch(link.href, { - credentials: 'same-origin', - headers: { - 'Accept': 'text/html', - 'X-Requested-With': 'XMLHttpRequest', - }, - signal: request.signal, - }); - if (!response.ok) throw new Error('分页加载失败(HTTP ' + response.status + ')'); - - const nextDocument = new DOMParser().parseFromString(await response.text(), 'text/html'); - const nextPanel = nextDocument.querySelector('#ledger-panel-accounts'); - const nextBody = nextPanel?.querySelector('.ledger-accounts-table tbody'); - const nextSummary = nextPanel?.querySelector('.ledger-summary'); - const nextPagination = nextPanel?.querySelector('.ledger-pagination'); - if (!nextBody || !nextSummary || !nextPagination) throw new Error('分页内容解析失败'); - - currentBody.replaceWith(document.importNode(nextBody, true)); - currentSummary.replaceWith(document.importNode(nextSummary, true)); - currentPagination.replaceWith(document.importNode(nextPagination, true)); - if (window.history?.replaceState) { - window.history.replaceState({ ...window.history.state, ledgerAccountsAjax: true }, '', link.href); - } - - const nextTableWrap = accountPanel.querySelector('.ledger-accounts-table-wrap'); - if (nextTableWrap) { - nextTableWrap.scrollTop = tableScrollTop; - nextTableWrap.scrollLeft = tableScrollLeft; - } - bindInlineAccountSelect('[data-account-status]', 'status', '账户状态已保存'); - bindInlineAccountSelect('[data-account-type]', 'account_type', '账户类型已保存'); - bindInlineAccountSelect('[data-account-plan]', 'company_plan', '公司规划已保存'); - bindInlineAccountSelect('[data-account-keep]', 'account_should_keep', '账户是否保留已保存'); - applyAccountEditMode(accountEditToggle?.checked === true); - applyAccountColumnLayout(); - bindAccountPagination(); - notify(targetPage ? '已加载第 ' + targetPage + ' 页' : '下一页已加载'); - } catch (error) { - if (error.name !== 'AbortError') notify(error.message || '分页加载失败', true); - } finally { - if (accountPageRequest === request) { - accountPanel.removeAttribute('aria-busy'); - } - } + loadAccountPage(link.href); }); }); + + const pageSelect = accountPanel.querySelector('[data-ledger-page-select]'); + if (pageSelect && pageSelect.dataset.ajaxBound !== '1') { + pageSelect.dataset.ajaxBound = '1'; + pageSelect.addEventListener('change', () => loadAccountPage(pageSelect.value)); + } }; bindAccountPagination(); + + page.querySelectorAll('[data-ledger-page-select]').forEach((select) => { + if (select.closest('#ledger-panel-accounts')) return; + select.addEventListener('change', () => { + const panel = select.closest('[data-ledger-panel]'); + const pageUrl = new URL(select.value, window.location.href); + if (panel?.dataset.ledgerPanel) pageUrl.searchParams.set('tab', panel.dataset.ledgerPanel); + window.location.assign(pageUrl.toString()); + }); + }); + + page.querySelectorAll('[data-ledger-panel] .ledger-page-box__arrow[href]').forEach((link) => { + if (link.closest('#ledger-panel-accounts')) return; + link.addEventListener('click', (event) => { + const panel = link.closest('[data-ledger-panel]'); + const pageUrl = new URL(link.href, window.location.href); + if (panel?.dataset.ledgerPanel) pageUrl.searchParams.set('tab', panel.dataset.ledgerPanel); + link.href = pageUrl.toString(); + }); + }); })(); diff --git a/resources/views/business/pages/financial-ledger/partials/pagination.blade.php b/resources/views/business/pages/financial-ledger/partials/pagination.blade.php new file mode 100644 index 00000000..e0a341cb --- /dev/null +++ b/resources/views/business/pages/financial-ledger/partials/pagination.blade.php @@ -0,0 +1,31 @@ +@php + $paginationKey = $paginationKey ?? 'page'; + $paginationLabel = $paginationLabel ?? '数据分页'; + $lastPage = max((int) $rows->lastPage(), 1); + $currentPage = min(max((int) $rows->currentPage(), 1), $lastPage); +@endphp + +
+ 共 {{ number_format($rows->total()) }} 条 · 每页 {{ number_format($rows->perPage()) }} 条 +
+ +
+
diff --git a/tests/Feature/Business/FinanceLedgerAccountStatusTest.php b/tests/Feature/Business/FinanceLedgerAccountStatusTest.php index 7a9acf45..f578f6a4 100644 --- a/tests/Feature/Business/FinanceLedgerAccountStatusTest.php +++ b/tests/Feature/Business/FinanceLedgerAccountStatusTest.php @@ -120,6 +120,12 @@ class FinanceLedgerAccountStatusTest extends FeatureTestCase ->assertSee('ledger-accounts-table-wrap', false) ->assertSee('ledger-pagination__controls', false) ->assertSee('ledger-page-box', false) + ->assertSee('data-ledger-page-select', false) + ->assertSee('data-ledger-pagination="accounts"', false) + ->assertSee('data-ledger-pagination="shops"', false) + ->assertSee('data-ledger-pagination="flows"', false) + ->assertSee('data-ledger-pagination="transactions"', false) + ->assertSee('data-ledger-pagination="mapping"', false) ->assertSee('1 / 1', false) ->assertSee('data-ledger-column-picker', false) ->assertSee('data-ledger-column-panel', false) @@ -481,6 +487,7 @@ class FinanceLedgerAccountStatusTest extends FeatureTestCase ])) ->assertOk() ->assertSee('ledger-page-box', false) + ->assertSee('data-ledger-page-select', false) ->assertSee('aria-label="下一页"', false) ->assertSee('accounts_page=2', false) ->assertSee('accounts_short=page-test', false);