218 lines
17 KiB
PHP
218 lines
17 KiB
PHP
@php
|
||
$listUrl = route('business.pages.show', ['section' => 'supplier-payables', 'page' => 'supplier-management']);
|
||
$localUrl = static fn (array $changes): string => $listUrl . '?' . http_build_query(array_merge(request()->except('detail_page'), $changes));
|
||
$cell = static fn ($value): string => $value === null || $value === '' ? '—' : (string) $value;
|
||
$detailColumns = match ($supplierDetailTab) {
|
||
'contacts' => ['sourcefrom' => '数据来源', 'linker' => '联系人', 'linkjob' => '职务', 'linkmobile' => '联系电话', 'linkweixin' => '微信', 'memo' => '备注'],
|
||
'payments' => ['enable' => '是否启用', 'sourcefrom' => '数据来源', 'accounttype' => '账户类型', 'account' => '打款账号', 'accountname' => '账户名称', 'accountbank' => '开户银行', 'accountnature' => '账户性质', 'invoice' => '开票信息', 'memo' => '备注'],
|
||
'logs' => ['time' => '操作时间', 'user' => '操作人', 'action' => '操作内容'],
|
||
default => ['ordernum' => '合同编号', 'originname' => '数据来源', 'title' => '合同名称', 'tradebody' => '合同主体', 'company' => '签订公司', 'eSignDocTemplateId' => '合同类型', 'type' => '合同类型名称', 'tradebtime' => '合同开始时间', 'tradeetime' => '合同截止时间', 'sendTime' => '合同发送时间', 'completeTime' => '完成时间', 'state' => '签署状态'],
|
||
};
|
||
@endphp
|
||
<section class="supplier-management" data-supplier-management aria-label="供应商管理">
|
||
<style>
|
||
.supplier-management { color:var(--vip-content-ink,#171717); font-size:.8125rem; }
|
||
.supplier-management [hidden] { display:none !important; }
|
||
.supplier-management-toolbar { display:flex; align-items:flex-end; flex-wrap:wrap; gap:12px; padding:16px; margin-bottom:12px; border:1px solid var(--vip-border,#D8D2C7); border-radius:8px; background:var(--vip-surface,#fff); }
|
||
.supplier-management-field { display:flex; flex-direction:column; gap:6px; min-width:160px; }
|
||
.supplier-management-field label { font-size:.75rem; color:var(--vip-content-muted,#595959); }
|
||
.supplier-management-field input,.supplier-management-field select { height:34px; padding:0 10px; border:1px solid var(--vip-border,#D8D2C7); border-radius:8px; font:inherit; background:#fff; }
|
||
.supplier-management a:focus-visible,.supplier-management button:focus-visible,.supplier-management input:focus-visible,.supplier-management select:focus-visible { outline:2px solid var(--vip-content-accent-dark,#856632); outline-offset:2px; }
|
||
.supplier-management-actions { display:flex; flex-wrap:wrap; gap:8px; }
|
||
.supplier-management-button { display:inline-flex; gap:6px; align-items:center; justify-content:center; height:34px; padding:0 14px; border:1px solid var(--vip-border,#D8D2C7); border-radius:8px; background:#fff; color:var(--vip-content-ink,#171717); font:inherit; cursor:pointer; }
|
||
.supplier-management-button.is-primary { background:var(--vip-content-accent-dark,#856632); color:#fff; border-color:var(--vip-content-accent-dark,#856632); }
|
||
.supplier-management-button.is-primary:hover { background:#6A5129; }
|
||
.supplier-management-status { display:flex; flex-wrap:wrap; justify-content:space-between; gap:8px; margin:10px 0; color:var(--vip-content-muted,#595959); font-size:.75rem; }
|
||
.supplier-management-status time { font-variant-numeric:tabular-nums; }
|
||
.supplier-management-panel { border:1px solid var(--vip-border,#D8D2C7); border-radius:8px; overflow:hidden; background:var(--vip-surface,#fff); }
|
||
.supplier-management-scroll { max-height:440px; overflow:auto; }
|
||
.supplier-management-table { width:100%; border-collapse:separate; border-spacing:0; font-size:.78125rem; font-variant-numeric:tabular-nums; }
|
||
.supplier-management-table th,.supplier-management-table td { padding:10px 12px; text-align:left; border-bottom:1px solid var(--vip-border,#D8D2C7); white-space:nowrap; }
|
||
.supplier-management-table th { position:sticky; top:0; background:var(--vip-content-accent-soft,#F7F0DE); z-index:1; font-weight:600; }
|
||
.supplier-management-table tr.is-selected td { background:var(--vip-content-accent-soft,#F7F0DE); }
|
||
.supplier-management-table tbody tr[data-supplier-select]:hover td { background:var(--vip-content-bg,#F8F6F1); cursor:pointer; }
|
||
.supplier-management-table a { color:var(--vip-content-accent-dark,#856632); text-decoration:underline; text-underline-offset:3px; }
|
||
.supplier-management-empty { height:120px; text-align:center !important; color:var(--vip-content-muted,#595959); white-space:normal !important; }
|
||
.supplier-management-badge { padding:3px 8px; border-radius:4px; background:var(--vip-content-bg,#F8F6F1); color:var(--vip-content-muted,#595959); }
|
||
.supplier-management-badge.is-cooperating { color:var(--vip-success,#53793E); }
|
||
.supplier-management-pagination { padding:12px; display:flex; align-items:center; flex-wrap:wrap; justify-content:space-between; gap:12px; }
|
||
.supplier-management-pagination nav { display:flex; align-items:center; gap:12px; }
|
||
.supplier-management-pagination a { color:var(--vip-content-accent-dark,#856632); }
|
||
.supplier-management-detail { margin-top:16px; }
|
||
.supplier-management-detail-heading { padding:12px 16px; border-bottom:1px solid var(--vip-border,#D8D2C7); color:var(--vip-content-muted,#595959); }
|
||
.supplier-management-detail-tabs { display:flex; gap:20px; padding:0 16px; border-bottom:1px solid var(--vip-border,#D8D2C7); }
|
||
.supplier-management-detail-tabs a { padding:10px 0; color:var(--vip-content-muted,#595959); border-bottom:2px solid transparent; font-size:.75rem; }
|
||
.supplier-management-detail-tabs a[aria-selected="true"] { color:var(--vip-content-accent-dark,#856632); border-color:var(--vip-content-accent,#B9954F); }
|
||
.supplier-management-alert { margin-bottom:12px; padding:12px; border:1px solid var(--vip-border,#D8D2C7); border-radius:8px; background:var(--vip-content-accent-soft,#F7F0DE); }
|
||
.supplier-management-alert.is-error { color:var(--vip-danger,#B80000); border-color:var(--vip-danger,#B80000); }
|
||
@media (max-width:640px) { .supplier-management-field { width:100%; } .supplier-management-detail-tabs { gap:12px; overflow-x:auto; white-space:nowrap; } }
|
||
</style>
|
||
<form method="GET" action="{{ $listUrl }}" class="supplier-management-toolbar" data-supplier-query aria-label="供应商管理筛选">
|
||
<div class="supplier-management-field">
|
||
<label for="supplier-management-name">供应商名称</label>
|
||
<input id="supplier-management-name" name="supplier_name" value="{{ $supplierFilters['supplier_name'] }}" placeholder="输入名称搜索" maxlength="191">
|
||
</div>
|
||
<div class="supplier-management-field">
|
||
<label for="supplier-management-buyer">采购人员</label>
|
||
<input id="supplier-management-buyer" name="buyer" value="{{ $supplierFilters['buyer'] }}" placeholder="输入采购人员" maxlength="191">
|
||
</div>
|
||
<div class="supplier-management-field">
|
||
<label for="supplier-management-status">合作状态</label>
|
||
<select id="supplier-management-status" name="cooperation_status">
|
||
<option value="">全部</option>
|
||
<option value="1" @selected($supplierFilters['cooperation_status'] === '1')>合作</option>
|
||
<option value="2" @selected($supplierFilters['cooperation_status'] === '2')>不合作</option>
|
||
</select>
|
||
</div>
|
||
<div class="supplier-management-actions">
|
||
<button type="submit" class="supplier-management-button is-primary">查询</button>
|
||
<a href="{{ $listUrl }}" class="supplier-management-button" data-supplier-local>重置</a>
|
||
@if ($supplierReady && !$supplierError)
|
||
<a class="supplier-management-button" href="{{ route('business.supplier-management.export', $supplierFilters) }}">导出</a>
|
||
@endif
|
||
</div>
|
||
</form>
|
||
<p role="status" aria-live="polite" data-supplier-load-status></p>
|
||
<div class="supplier-management-alert is-error" role="alert" data-supplier-load-error hidden>加载失败,当前内容已保留,请重新查询。</div>
|
||
@if ($supplierError)
|
||
<div class="supplier-management-alert is-error" role="alert">{{ $supplierError }}</div>
|
||
@elseif (!$supplierReady)
|
||
<div class="supplier-management-alert" role="status">供应商资料尚未完成首次同步,完成后会显示完整列表。</div>
|
||
@elseif (($supplierState['status'] ?? '') === 'failed')
|
||
<div class="supplier-management-alert" role="status">最近一次同步失败,当前显示上次同步成功的资料。</div>
|
||
@endif
|
||
<div class="supplier-management-status">
|
||
<span>共 {{ number_format($suppliers->total()) }} 家供应商 · {{ count($supplierColumns) }} 项资料,横向滚动可查看完整字段</span>
|
||
<span>最近同步:<time>{{ $supplierState['last_succeeded_at'] ?? '尚未同步' }}</time></span>
|
||
</div>
|
||
<div class="supplier-management-panel" data-finance-table-surface>
|
||
<div class="supplier-management-scroll">
|
||
<table class="supplier-management-table" data-finance-table aria-label="供应商列表">
|
||
<thead><tr><th scope="col">序号</th>@foreach ($supplierColumns as $label)<th scope="col">{{ $label }}</th>@endforeach</tr></thead>
|
||
<tbody>
|
||
@forelse ($suppliers as $supplier)
|
||
@php $selectUrl = $localUrl(['supplier_id' => $supplier->Id]); @endphp
|
||
<tr data-supplier-select="{{ $selectUrl }}" @class(['is-selected' => $selectedSupplier && $selectedSupplier->Id == $supplier->Id])>
|
||
<td>{{ $suppliers->firstItem() + $loop->index }}</td>
|
||
@foreach ($supplierColumns as $key => $label)
|
||
<td>
|
||
@if ($key === 'coopersgn')
|
||
<span @class(['supplier-management-badge', 'is-cooperating' => (int) $supplier->coopersgn === 1])>{{ ['1' => '合作', '2' => '不合作'][(string) $supplier->coopersgn] ?? '未维护' }}</span>
|
||
@elseif ($key === 'scmname')
|
||
<a href="{{ $selectUrl }}" data-supplier-local @if ($selectedSupplier && $selectedSupplier->Id == $supplier->Id) aria-current="true" @endif>{{ $cell($supplier->{$key} ?? null) }}</a>
|
||
@else
|
||
{{ $cell($supplier->{$key} ?? null) }}
|
||
@endif
|
||
</td>
|
||
@endforeach
|
||
</tr>
|
||
@empty
|
||
<tr><td colspan="{{ count($supplierColumns) + 1 }}" class="supplier-management-empty">{{ $supplierError ? '资料读取失败,请稍后重试。' : ($supplierReady ? '没有符合筛选条件的供应商。' : '供应商资料同步未完成。') }}</td></tr>
|
||
@endforelse
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
<div class="supplier-management-pagination">
|
||
<span>每页 {{ $suppliers->perPage() }} 条,共 {{ $suppliers->total() }} 条</span>
|
||
<nav aria-label="供应商分页">
|
||
@if ($suppliers->currentPage() > 1)<a href="{{ $suppliers->url(1) }}" data-supplier-local>首页</a><a href="{{ $suppliers->previousPageUrl() }}" data-supplier-local>上一页</a>@endif
|
||
<span>{{ $suppliers->currentPage() }} / {{ $suppliers->lastPage() }}</span>
|
||
@if ($suppliers->hasMorePages())<a href="{{ $suppliers->nextPageUrl() }}" data-supplier-local>下一页</a><a href="{{ $suppliers->url($suppliers->lastPage()) }}" data-supplier-local>末页</a>@endif
|
||
</nav>
|
||
</div>
|
||
</div>
|
||
<div class="supplier-management-panel supplier-management-detail">
|
||
<div class="supplier-management-detail-heading">{{ $selectedSupplier ? '当前供应商:' . $selectedSupplier->scmname . '(' . $selectedSupplier->scmcode . ')' : '选择供应商后查看相关资料' }}</div>
|
||
<nav class="supplier-management-detail-tabs" role="tablist" aria-label="供应商详情">
|
||
@foreach (\Modules\EcommerceFinance\View\SupplierManagementPageData::TABS as $key => $label)
|
||
<a id="supplier-detail-tab-{{ $key }}" href="{{ $localUrl(['detail_tab' => $key]) }}" data-supplier-local role="tab" aria-selected="{{ $supplierDetailTab === $key ? 'true' : 'false' }}" aria-controls="supplier-detail-panel">{{ $label }}</a>
|
||
@endforeach
|
||
</nav>
|
||
<div class="supplier-management-scroll" role="tabpanel" id="supplier-detail-panel" aria-labelledby="supplier-detail-tab-{{ $supplierDetailTab }}">
|
||
<table class="supplier-management-table" aria-label="{{ \Modules\EcommerceFinance\View\SupplierManagementPageData::TABS[$supplierDetailTab] }}">
|
||
<thead><tr>@foreach ($detailColumns as $label)<th scope="col">{{ $label }}</th>@endforeach</tr></thead>
|
||
<tbody>
|
||
@forelse ($supplierDetails as $detail)
|
||
<tr>@foreach ($detailColumns as $key => $label)<td>{{ $key === 'enable' ? ((int) ($detail->enable ?? 0) === 1 ? '启用' : '停用') : $cell($detail->{$key} ?? null) }}</td>@endforeach</tr>
|
||
@empty
|
||
<tr><td colspan="{{ count($detailColumns) }}" class="supplier-management-empty">{{ $supplierDetail['message'] ?? '该供应商暂无此类资料。' }}</td></tr>
|
||
@endforelse
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
@if ($supplierDetails->total() > 0)
|
||
<div class="supplier-management-pagination">
|
||
<span>共 {{ $supplierDetails->total() }} 条</span>
|
||
<nav aria-label="供应商详情分页">
|
||
@if ($supplierDetails->currentPage() > 1)<a href="{{ $supplierDetails->previousPageUrl() }}" data-supplier-local>上一页</a>@endif
|
||
<span>{{ $supplierDetails->currentPage() }} / {{ $supplierDetails->lastPage() }}</span>
|
||
@if ($supplierDetails->hasMorePages())<a href="{{ $supplierDetails->nextPageUrl() }}" data-supplier-local>下一页</a>@endif
|
||
</nav>
|
||
</div>
|
||
@endif
|
||
</div>
|
||
</section>
|
||
@push('body_end')
|
||
<script data-caiwu-replay>
|
||
(() => {
|
||
if (window.caiwuSupplierManagementBound) return;
|
||
window.caiwuSupplierManagementBound = true;
|
||
let pending = false;
|
||
async function load(href) {
|
||
const current = document.querySelector('[data-supplier-management]');
|
||
if (!current || pending) return;
|
||
pending = true;
|
||
current.setAttribute('aria-busy', 'true');
|
||
current.querySelector('[data-supplier-load-status]').textContent = '正在加载供应商资料…';
|
||
current.querySelector('[data-supplier-load-error]').hidden = true;
|
||
try {
|
||
const response = await fetch(href, {credentials:'same-origin',headers:{Accept:'text/html'}});
|
||
if (!response.ok) throw new Error('load');
|
||
const doc = new DOMParser().parseFromString(await response.text(),'text/html');
|
||
const next = doc.querySelector('[data-supplier-management]');
|
||
if (!next) throw new Error('session');
|
||
if (!current.isConnected) return;
|
||
const scroll = current.querySelector('.supplier-management-scroll');
|
||
const scrollLeft = scroll.scrollLeft;
|
||
const mounted = document.importNode(next,true);
|
||
current.replaceWith(mounted);
|
||
mounted.querySelector('.supplier-management-scroll').scrollLeft = scrollLeft;
|
||
history.pushState({},'',href);
|
||
window.dispatchEvent(new Event('caiwu:supplier-page-updated'));
|
||
} catch (error) {
|
||
current.querySelector('[data-supplier-load-error]').hidden = false;
|
||
} finally {
|
||
current.querySelector('[data-supplier-load-status]').textContent = '';
|
||
current.removeAttribute('aria-busy');
|
||
pending = false;
|
||
}
|
||
}
|
||
document.addEventListener('click', event => {
|
||
if (event.button !== 0 || event.ctrlKey || event.metaKey || event.shiftKey || event.altKey) return;
|
||
const link = event.target.closest('[data-supplier-management] a[data-supplier-local]');
|
||
const row = event.target.closest('[data-supplier-select]');
|
||
if (!link && !row) return;
|
||
event.preventDefault();
|
||
load(link ? link.href : row.dataset.supplierSelect);
|
||
});
|
||
document.addEventListener('submit', event => {
|
||
const form = event.target.closest('[data-supplier-query]');
|
||
if (!form) return;
|
||
event.preventDefault();
|
||
load(form.action + '?' + new URLSearchParams(new FormData(form)).toString());
|
||
});
|
||
document.addEventListener('keydown', event => {
|
||
const tab = event.target.closest('[data-supplier-management] [role="tab"]');
|
||
if (!tab || !['ArrowLeft','ArrowRight','Home','End'].includes(event.key)) return;
|
||
const tabs = [...tab.parentElement.querySelectorAll('[role="tab"]')];
|
||
const index = tabs.indexOf(tab);
|
||
const next = event.key === 'Home' ? 0 : event.key === 'End' ? tabs.length - 1
|
||
: (index + (event.key === 'ArrowRight' ? 1 : -1) + tabs.length) % tabs.length;
|
||
event.preventDefault();
|
||
tabs[next].focus();
|
||
load(tabs[next].href);
|
||
});
|
||
})();
|
||
</script>
|
||
@endpush
|