供应商应付子目录移入页面横向导航 #167
@ -108,7 +108,7 @@ class ShowInAdmin
|
||||
: $this->sortByTitleLength($section['pages'] ?? []);
|
||||
$order = ($sectionIndex + 1) * 10;
|
||||
|
||||
if (empty($pages)) {
|
||||
if (empty($pages) || $section['slug'] === 'supplier-payables') {
|
||||
$menu->route(
|
||||
'business.pages.show',
|
||||
$section['title'],
|
||||
|
||||
@ -546,6 +546,7 @@
|
||||
'business-page--marketing-account-balance' => $isMarketingAccountBalancePage,
|
||||
])
|
||||
@if ($isMarketingAccountBillsPage) data-marketing-account-bills-workspace @endif
|
||||
@if ($section['slug'] === 'supplier-payables') data-supplier-workspace @endif
|
||||
>
|
||||
@if ($isMarketingAccountBillsPage)
|
||||
<p class="sr-only" role="status" aria-live="polite" data-marketing-account-bills-status></p>
|
||||
@ -570,6 +571,10 @@
|
||||
<div class="biz-alert biz-alert-error" role="alert">{{ $upstreamError }}</div>
|
||||
@endif
|
||||
|
||||
@if ($section['slug'] === 'supplier-payables')
|
||||
@include('business.pages.supplier-payables.navigation')
|
||||
@endif
|
||||
|
||||
@if ($tabs->isNotEmpty())
|
||||
<nav class="business-tab-bar" aria-label="{{ $page['title'] }}">
|
||||
@foreach ($tabs as $tab)
|
||||
|
||||
@ -0,0 +1,78 @@
|
||||
@push('body_end')
|
||||
<script>
|
||||
(() => {
|
||||
const selector = '[data-supplier-workspace]';
|
||||
const initial = document.querySelector(selector);
|
||||
if (!initial) return;
|
||||
const cache = new Map();
|
||||
const loaded = new Set([...document.scripts].map(script => script.src));
|
||||
let current = location.href;
|
||||
let pending = false;
|
||||
const heading = document.querySelector('#main-body h1');
|
||||
const remember = () => cache.set(current, {
|
||||
node: document.querySelector(selector),
|
||||
title: document.title,
|
||||
heading: heading ? heading.innerHTML : '',
|
||||
});
|
||||
remember();
|
||||
|
||||
async function navigate(href, push = true) {
|
||||
if (pending) return;
|
||||
const previous = document.querySelector(selector);
|
||||
pending = true;
|
||||
previous.setAttribute('aria-busy', 'true');
|
||||
const status = previous.querySelector('[data-supplier-navigation-status]');
|
||||
status.textContent = '正在加载…';
|
||||
try {
|
||||
let next = cache.get(href);
|
||||
let scripts = [];
|
||||
if (!next) {
|
||||
const response = await fetch(href, {credentials: 'same-origin', headers: {Accept: 'text/html'}});
|
||||
if (!response.ok) throw new Error('HTTP ' + response.status);
|
||||
const doc = new DOMParser().parseFromString(await response.text(), 'text/html');
|
||||
const node = doc.querySelector(selector);
|
||||
if (!node) throw new Error('页面不可用或登录已过期');
|
||||
scripts = [...doc.querySelectorAll('script[src]')].map(s => s.src)
|
||||
.filter(src => new URL(src).origin === location.origin && new URL(src).pathname.startsWith('/public/js/ecommerce-finance/') && !loaded.has(src));
|
||||
next = {node: document.importNode(node, true), title: doc.title, heading: doc.querySelector('#main-body h1')?.innerHTML || ''};
|
||||
}
|
||||
remember();
|
||||
previous.replaceWith(next.node);
|
||||
// Keep each mounted Vue page alive while switching, preserving its filters and pagination.
|
||||
for (const src of scripts) {
|
||||
await new Promise((resolve, reject) => {
|
||||
const script = document.createElement('script');
|
||||
script.src = src;
|
||||
script.onload = () => { loaded.add(src); resolve(); };
|
||||
script.onerror = () => { script.remove(); reject(new Error('页面脚本加载失败')); };
|
||||
document.body.appendChild(script);
|
||||
});
|
||||
}
|
||||
cache.set(href, next);
|
||||
current = href;
|
||||
document.title = next.title;
|
||||
if (heading) heading.innerHTML = next.heading;
|
||||
next.node.querySelector('[data-supplier-navigation-status]').textContent = '';
|
||||
if (push && location.href !== href) history.pushState({}, '', href);
|
||||
next.node.querySelector('.supplier-page-navigation [aria-current]')?.focus({preventScroll: true});
|
||||
} catch (error) {
|
||||
const visible = document.querySelector(selector);
|
||||
if (visible !== previous) visible.replaceWith(previous);
|
||||
status.textContent = '加载失败,原页面已保留。请重新点击目录重试。';
|
||||
} finally {
|
||||
previous.removeAttribute('aria-busy');
|
||||
pending = false;
|
||||
}
|
||||
}
|
||||
document.addEventListener('click', event => {
|
||||
const link = event.target.closest('.supplier-page-navigation a');
|
||||
if (!link || event.button !== 0 || event.ctrlKey || event.metaKey || event.shiftKey || event.altKey) return;
|
||||
event.preventDefault();
|
||||
navigate(link.href);
|
||||
});
|
||||
window.addEventListener('popstate', () => {
|
||||
if (location.pathname.includes('/workspace/supplier-payables/')) navigate(location.href, false);
|
||||
});
|
||||
})();
|
||||
</script>
|
||||
@endpush
|
||||
@ -0,0 +1,37 @@
|
||||
@php
|
||||
$supplierPages = $section['pages'] ?? [];
|
||||
$supplierPages = array_merge(
|
||||
array_filter($supplierPages, fn ($item) => $item['slug'] === 'supplier-payables-report'),
|
||||
array_filter($supplierPages, fn ($item) => $item['slug'] !== 'supplier-payables-report'),
|
||||
);
|
||||
$supplierCurrent = $page['slug'] ?? null;
|
||||
@endphp
|
||||
<p role="status" aria-live="polite" data-supplier-navigation-status></p>
|
||||
<nav class="business-tab-bar supplier-page-navigation" aria-label="供应商应付页面">
|
||||
@foreach ($supplierPages as $supplierPage)
|
||||
@php
|
||||
$children = $supplierPage['children'] ?? [];
|
||||
$selected = $supplierCurrent === $supplierPage['slug'] || in_array($supplierCurrent, array_column($children, 'slug'), true);
|
||||
@endphp
|
||||
<a href="{{ route('business.pages.show', ['section' => 'supplier-payables', 'page' => $children[0]['slug'] ?? $supplierPage['slug']]) }}"
|
||||
@class(['business-tab-link', 'is-active' => $selected])
|
||||
@if ($selected) aria-current="page" @endif>{{ $supplierPage['title'] }}</a>
|
||||
@endforeach
|
||||
</nav>
|
||||
@foreach ($supplierPages as $supplierPage)
|
||||
@if (in_array($supplierCurrent, array_column($supplierPage['children'] ?? [], 'slug'), true))
|
||||
<nav class="business-tab-bar supplier-page-navigation" aria-label="{{ $supplierPage['title'] }}">
|
||||
@foreach ($supplierPage['children'] as $child)
|
||||
<a href="{{ route('business.pages.show', ['section' => 'supplier-payables', 'page' => $child['slug']]) }}"
|
||||
@class(['business-tab-link', 'is-active' => $supplierCurrent === $child['slug']])
|
||||
@if ($supplierCurrent === $child['slug']) aria-current="page" @endif>{{ $child['title'] }}</a>
|
||||
@endforeach
|
||||
</nav>
|
||||
@endif
|
||||
@endforeach
|
||||
<style>
|
||||
.supplier-page-navigation { display:flex; flex-wrap:wrap; gap:4px 16px; margin-bottom:16px; }
|
||||
.supplier-page-navigation .business-tab-link { flex:0 0 auto; padding:10px 0; font-size:0.75rem; color:var(--vip-content-muted, #595959); }
|
||||
.supplier-page-navigation .business-tab-link.is-active { color:var(--vip-content-accent-dark, #856632); }
|
||||
</style>
|
||||
@include('business.pages.supplier-payables.local-navigation')
|
||||
Loading…
x
Reference in New Issue
Block a user