CaiWuXiTong/resources/views/business/pages/supplier-payables/supplier-management.blade.php

218 lines
17 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.

@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