diff --git a/resources/views/business/pages/courier-payables/agreement-signing.blade.php b/resources/views/business/pages/courier-payables/agreement-signing.blade.php
index 219c2dc9..d97fda09 100644
--- a/resources/views/business/pages/courier-payables/agreement-signing.blade.php
+++ b/resources/views/business/pages/courier-payables/agreement-signing.blade.php
@@ -249,6 +249,63 @@
color: var(--agreement-muted);
}
+ .agreement-courier-search-wrap {
+ position: relative;
+ }
+
+ .agreement-courier-native-select--enhanced {
+ position: absolute;
+ width: 1px;
+ height: 1px;
+ padding: 0;
+ margin: -1px;
+ overflow: hidden;
+ clip: rect(0, 0, 0, 0);
+ white-space: nowrap;
+ clip-path: inset(50%);
+ border: 0;
+ }
+
+ .agreement-courier-options {
+ position: absolute;
+ z-index: 20;
+ top: calc(100% + 4px);
+ right: 0;
+ left: 0;
+ max-height: 16rem;
+ overflow: auto;
+ border: 1px solid var(--agreement-border);
+ border-radius: 8px;
+ background: var(--agreement-surface);
+ box-shadow: 0 4px 8px rgba(23, 23, 23, 0.1);
+ }
+
+ .agreement-courier-option {
+ min-height: 34px;
+ padding: 0.5rem 0.65rem;
+ color: var(--agreement-ink);
+ cursor: pointer;
+ }
+
+ .agreement-courier-option:hover,
+ .agreement-courier-option.is-active {
+ background: var(--agreement-accent-soft);
+ color: var(--agreement-accent);
+ }
+
+ .agreement-courier-options-status {
+ padding: 0.5rem 0.65rem;
+ border-top: 1px solid var(--agreement-border);
+ color: var(--agreement-muted);
+ font-size: 0.75rem;
+ }
+
+ .agreement-courier-native-select--enhanced:focus-visible,
+ .agreement-courier-search-wrap .agreement-control:focus-visible {
+ outline: 0;
+ box-shadow: 0 0 0 3px rgba(133, 102, 50, 0.2);
+ }
+
.agreement-search {
position: relative;
}
@@ -1368,15 +1425,21 @@
-
+
+
+
+
@@ -1920,6 +1983,18 @@
var subjectNameInput = root.querySelector('[data-subject-name]');
var subjectCodeInput = root.querySelector('[data-subject-code]');
var courierSelect = root.querySelector('[data-courier-select]');
+ var courierSearchWrap = root.querySelector('[data-courier-search-wrap]');
+ var courierSearch = root.querySelector('[data-courier-search]');
+ var courierOptionsList = root.querySelector('[data-courier-options]');
+ var courierControl = root.querySelector('[data-courier-control]');
+ var courierLabel = root.querySelector('label[for="agreement-courier"]');
+ var courierOptions = courierSelect ? Array.prototype.filter.call(courierSelect.options, function (option) {
+ return option.value !== '' && !option.disabled;
+ }).map(function (option) {
+ return { value: option.value, account: option.dataset.esignAccount || '' };
+ }) : [];
+ var visibleCourierIndexes = [];
+ var activeCourierIndex = -1;
var esignAccountInput = root.querySelector('input[name="esign_account"]');
var accountSignerTypeField = root.querySelector('[data-account-signer-type-field]');
var accountSignerTypeInputs = Array.prototype.slice.call(root.querySelectorAll('[data-account-signer-type]'));
@@ -2259,6 +2334,197 @@
}
}
+ function closeCourierOptions() {
+ if (!courierSearch || !courierOptionsList) {
+ return;
+ }
+
+ courierOptionsList.hidden = true;
+ courierSearch.setAttribute('aria-expanded', 'false');
+ courierSearch.removeAttribute('aria-activedescendant');
+ activeCourierIndex = -1;
+ }
+
+ function renderCourierOptions(query) {
+ if (!courierSearch || !courierOptionsList) {
+ return;
+ }
+
+ var normalizedQuery = String(query || '').trim().toLocaleLowerCase();
+ var matches = [];
+
+ courierOptions.forEach(function (option, index) {
+ if (normalizedQuery === '' || option.value.toLocaleLowerCase().indexOf(normalizedQuery) !== -1) {
+ matches.push(index);
+ }
+ });
+
+ visibleCourierIndexes = matches.slice(0, 80);
+ courierOptionsList.innerHTML = '';
+
+ if (visibleCourierIndexes.length === 0) {
+ var empty = document.createElement('div');
+ empty.className = 'agreement-courier-options-status';
+ empty.setAttribute('role', 'status');
+ empty.textContent = normalizedQuery === '' ? '暂无可选快递资料' : '没有匹配的快递名称';
+ courierOptionsList.appendChild(empty);
+ } else {
+ visibleCourierIndexes.forEach(function (optionIndex) {
+ var option = courierOptions[optionIndex];
+ var item = document.createElement('div');
+ item.className = 'agreement-courier-option';
+ item.id = 'agreement-courier-option-' + optionIndex;
+ item.setAttribute('role', 'option');
+ item.setAttribute('aria-selected', courierSelect.value === option.value ? 'true' : 'false');
+ item.dataset.courierOption = String(optionIndex);
+ item.textContent = option.value;
+ courierOptionsList.appendChild(item);
+ });
+
+ if (matches.length > visibleCourierIndexes.length) {
+ var status = document.createElement('div');
+ status.className = 'agreement-courier-options-status';
+ status.setAttribute('role', 'status');
+ status.textContent = '匹配到 ' + matches.length + ' 项,当前显示前 80 项;继续输入可缩小范围';
+ courierOptionsList.appendChild(status);
+ }
+ }
+
+ courierOptionsList.hidden = false;
+ courierSearch.setAttribute('aria-expanded', 'true');
+ activeCourierIndex = -1;
+ courierSearch.removeAttribute('aria-activedescendant');
+ }
+
+ function setActiveCourierOption(position) {
+ if (!courierOptionsList || visibleCourierIndexes.length === 0) {
+ return;
+ }
+
+ activeCourierIndex = (position + visibleCourierIndexes.length) % visibleCourierIndexes.length;
+ var optionIndex = visibleCourierIndexes[activeCourierIndex];
+ var optionElement = courierOptionsList.querySelector('[data-courier-option="' + optionIndex + '"]');
+ courierOptionsList.querySelectorAll('.agreement-courier-option').forEach(function (item) {
+ item.classList.toggle('is-active', item === optionElement);
+ });
+ courierSearch.setAttribute('aria-activedescendant', 'agreement-courier-option-' + optionIndex);
+ optionElement && optionElement.scrollIntoView({ block: 'nearest' });
+ }
+
+ function syncCourierSelection() {
+ if (!courierSelect || !courierSearch) {
+ return;
+ }
+
+ var enteredName = courierSearch.value.trim();
+ var option = courierOptions.find(function (item) {
+ return item.value.toLocaleLowerCase() === enteredName.toLocaleLowerCase();
+ });
+
+ if (option) {
+ courierSearch.value = option.value;
+ courierSelect.value = option.value;
+ courierSearch.setCustomValidity('');
+ } else {
+ courierSelect.value = '';
+ courierSearch.setCustomValidity(enteredName === '' ? '请选择快递公司' : '请从快递资料中选择快递公司');
+ }
+ }
+
+ function chooseCourierOption(index) {
+ if (!courierSearch || !courierSelect || !courierOptions[index]) {
+ return;
+ }
+
+ courierSearch.value = courierOptions[index].value;
+ syncCourierSelection();
+ closeCourierOptions();
+ syncEsignAccount();
+ refreshPreview();
+ }
+
+ function initializeCourierSearch() {
+ if (!courierSelect || !courierSearchWrap || !courierSearch || !courierOptionsList) {
+ return;
+ }
+
+ courierSearchWrap.hidden = false;
+ courierSelect.classList.add('agreement-courier-native-select--enhanced');
+ courierSelect.setAttribute('aria-hidden', 'true');
+ courierSelect.tabIndex = -1;
+ courierSelect.required = false;
+ courierSearch.value = courierSelect.value;
+ courierSearch.required = true;
+ if (courierLabel) {
+ courierLabel.htmlFor = courierSearch.id;
+ }
+ syncCourierSelection();
+
+ courierSearch.addEventListener('focus', function () {
+ courierSearch.select();
+ renderCourierOptions('');
+ });
+ courierSearch.addEventListener('input', function () {
+ syncCourierSelection();
+ renderCourierOptions(courierSearch.value);
+ syncEsignAccount();
+ refreshPreview();
+ });
+ courierSearch.addEventListener('keydown', function (event) {
+ if (event.key === 'ArrowDown') {
+ event.preventDefault();
+ if (courierOptionsList.hidden) {
+ renderCourierOptions(courierSearch.value);
+ }
+ setActiveCourierOption(activeCourierIndex + 1);
+ } else if (event.key === 'ArrowUp') {
+ event.preventDefault();
+ if (courierOptionsList.hidden) {
+ renderCourierOptions(courierSearch.value);
+ }
+ setActiveCourierOption(activeCourierIndex < 0 ? visibleCourierIndexes.length - 1 : activeCourierIndex - 1);
+ } else if (event.key === 'Enter' && !courierOptionsList.hidden) {
+ event.preventDefault();
+ var selectedIndex = activeCourierIndex >= 0
+ ? visibleCourierIndexes[activeCourierIndex]
+ : visibleCourierIndexes.find(function (optionIndex) {
+ return courierOptions[optionIndex].value.toLocaleLowerCase() === courierSearch.value.trim().toLocaleLowerCase();
+ });
+ if (selectedIndex === undefined && visibleCourierIndexes.length === 1) {
+ selectedIndex = visibleCourierIndexes[0];
+ }
+ if (selectedIndex !== undefined) {
+ chooseCourierOption(selectedIndex);
+ }
+ } else if (event.key === 'Escape') {
+ closeCourierOptions();
+ }
+ });
+ courierSearch.addEventListener('blur', function () {
+ window.setTimeout(function () {
+ if (courierControl && !courierControl.contains(document.activeElement)) {
+ closeCourierOptions();
+ }
+ }, 0);
+ });
+ courierOptionsList.addEventListener('mousedown', function (event) {
+ if (event.target.closest('[data-courier-option]')) {
+ event.preventDefault();
+ }
+ });
+ courierOptionsList.addEventListener('click', function (event) {
+ var optionElement = event.target.closest('[data-courier-option]');
+ if (optionElement) {
+ chooseCourierOption(Number(optionElement.dataset.courierOption));
+ }
+ });
+ root.addEventListener('click', function (event) {
+ if (courierControl && !courierControl.contains(event.target)) {
+ closeCourierOptions();
+ }
+ });
+ }
+
function setText(selector, value) {
root.querySelectorAll(selector).forEach(function (target) {
target.textContent = value;
@@ -2718,6 +2984,10 @@
if (courierSelect) {
courierSelect.addEventListener('change', function () {
+ if (courierSearch) {
+ courierSearch.value = courierSelect.value;
+ syncCourierSelection();
+ }
syncEsignAccount();
refreshPreview();
});
@@ -2755,6 +3025,10 @@
});
if (courierOption) {
courierSelect.value = option.dataset.carrier;
+ if (courierSearch) {
+ courierSearch.value = option.dataset.carrier;
+ syncCourierSelection();
+ }
syncEsignAccount();
}
}
@@ -2829,6 +3103,7 @@
var initialUrl = new URL(window.location.href);
var initialView = initialUrl.searchParams.get('agreement_view') || 'tasks';
+ initializeCourierSearch();
renderSubjectSelect();
renderSubjectList();
showTab(initialView, { updateUrl: false });
diff --git a/tests/Feature/Business/AgreementSigningPageTest.php b/tests/Feature/Business/AgreementSigningPageTest.php
index 4e777ca7..1643fc1a 100644
--- a/tests/Feature/Business/AgreementSigningPageTest.php
+++ b/tests/Feature/Business/AgreementSigningPageTest.php
@@ -63,6 +63,10 @@ class AgreementSigningPageTest extends FeatureTestCase
->assertDontSee('data-agreement-step-panel', false)
->assertSeeText('发起签章请求')
->assertSeeText('快递公司')
+ ->assertSee('data-courier-search', false)
+ ->assertSee('placeholder="输入快递名称搜索"', false)
+ ->assertSee('aria-haspopup="listbox"', false)
+ ->assertSee('data-courier-options', false)
->assertSeeText('文件类型')
->assertSeeText('收款账户证明')
->assertSeeText('TPL-ACCOUNT-CERT-V2')
@@ -118,6 +122,8 @@ class AgreementSigningPageTest extends FeatureTestCase
'agreement_view' => 'create',
]))
->assertOk()
+ ->assertSee('data-courier-search', false)
+ ->assertSee('data-courier-select required', false)
->assertSee('', false)
->assertSee('', false)
->assertDontSee('