feat: [approvals] 지출결의서 업체명에 거래처 검색 기능 추가
- 업체명 input을 거래처 검색 자동완성으로 교체 - 기존 trading_partners 검색 API 활용 (/barobill/tax-invoice/search-partners) - 거래처명/사업자번호로 검색, 드롭다운에서 선택 - 키보드 탐색 지원 (위/아래 화살표, Enter, Escape) - vendor_id, vendor_biz_no 추가 저장
This commit is contained in:
@@ -183,8 +183,29 @@ class="w-full px-2 py-1.5 border border-gray-200 rounded text-xs focus:outline-n
|
|||||||
class="w-full px-2 py-1.5 border border-gray-200 rounded text-xs text-right focus:outline-none focus:ring-1 focus:ring-blue-500">
|
class="w-full px-2 py-1.5 border border-gray-200 rounded text-xs text-right focus:outline-none focus:ring-1 focus:ring-blue-500">
|
||||||
</td>
|
</td>
|
||||||
<td class="px-1 py-1">
|
<td class="px-1 py-1">
|
||||||
<input type="text" x-model="item.vendor" placeholder="업체명"
|
<div class="relative" x-data="vendorSearch(item)" @click.outside="close()">
|
||||||
class="w-full px-2 py-1.5 border border-gray-200 rounded text-xs focus:outline-none focus:ring-1 focus:ring-blue-500">
|
<input type="text" :value="item.vendor" @input="onInput($event.target.value)" @focus="onFocus()" @keydown.escape="close()" @keydown.arrow-down.prevent="moveDown()" @keydown.arrow-up.prevent="moveUp()" @keydown.enter.prevent="selectHighlighted()"
|
||||||
|
placeholder="거래처 검색"
|
||||||
|
class="w-full px-2 py-1.5 border border-gray-200 rounded text-xs focus:outline-none focus:ring-1 focus:ring-blue-500">
|
||||||
|
<div x-show="open && results.length > 0" x-transition
|
||||||
|
class="absolute z-50 left-0 right-0 mt-0.5 bg-white border border-gray-200 rounded-lg shadow-lg overflow-hidden" style="max-height: 200px; overflow-y: auto;">
|
||||||
|
<template x-for="(p, pi) in results" :key="p.id">
|
||||||
|
<div @click="selectPartner(p)" @mouseenter="highlighted = pi"
|
||||||
|
:class="highlighted === pi ? 'bg-blue-50' : 'hover:bg-gray-50'"
|
||||||
|
class="px-2 py-1.5 cursor-pointer border-b border-gray-50 last:border-0">
|
||||||
|
<div class="text-xs font-medium text-gray-800" x-text="p.name"></div>
|
||||||
|
<div class="text-xs text-gray-400 flex gap-2" x-show="p.biz_no || p.ceo">
|
||||||
|
<span x-show="p.biz_no" x-text="p.biz_no"></span>
|
||||||
|
<span x-show="p.ceo" x-text="'대표: ' + p.ceo"></span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
<div x-show="open && query.length >= 1 && results.length === 0 && !loading"
|
||||||
|
class="absolute z-50 left-0 right-0 mt-0.5 bg-white border border-gray-200 rounded-lg shadow-lg px-3 py-2 text-xs text-gray-400">
|
||||||
|
검색 결과 없음
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</td>
|
</td>
|
||||||
{{-- 법인카드: 선택된 카드 표시 --}}
|
{{-- 법인카드: 선택된 카드 표시 --}}
|
||||||
<td x-show="formData.expense_type === 'corporate_card'" class="px-1 py-1">
|
<td x-show="formData.expense_type === 'corporate_card'" class="px-1 py-1">
|
||||||
@@ -347,6 +368,8 @@ function makeItem(data) {
|
|||||||
description: data?.description || '',
|
description: data?.description || '',
|
||||||
amount: parseInt(data?.amount) || 0,
|
amount: parseInt(data?.amount) || 0,
|
||||||
vendor: data?.vendor || '',
|
vendor: data?.vendor || '',
|
||||||
|
vendor_id: data?.vendor_id || null,
|
||||||
|
vendor_biz_no: data?.vendor_biz_no || '',
|
||||||
bank: data?.bank || '',
|
bank: data?.bank || '',
|
||||||
account_no: data?.account_no || '',
|
account_no: data?.account_no || '',
|
||||||
depositor: data?.depositor || '',
|
depositor: data?.depositor || '',
|
||||||
@@ -562,6 +585,8 @@ function makeItem(data) {
|
|||||||
description: item.description,
|
description: item.description,
|
||||||
amount: parseInt(item.amount) || 0,
|
amount: parseInt(item.amount) || 0,
|
||||||
vendor: item.vendor,
|
vendor: item.vendor,
|
||||||
|
vendor_id: item.vendor_id || null,
|
||||||
|
vendor_biz_no: item.vendor_biz_no || '',
|
||||||
bank: isTransfer && acct ? acct.bank_name : (isCard ? (card?.card_company || '') : item.bank),
|
bank: isTransfer && acct ? acct.bank_name : (isCard ? (card?.card_company || '') : item.bank),
|
||||||
account_no: isTransfer && acct ? acct.account_number : (isCard ? ('**** ' + (card?.card_number_last4 || '')) : item.account_no),
|
account_no: isTransfer && acct ? acct.account_number : (isCard ? ('**** ' + (card?.card_number_last4 || '')) : item.account_no),
|
||||||
depositor: isTransfer && acct ? acct.account_holder : (isCard ? (card?.card_holder_name || '') : item.depositor),
|
depositor: isTransfer && acct ? acct.account_holder : (isCard ? (card?.card_holder_name || '') : item.depositor),
|
||||||
@@ -579,4 +604,65 @@ function makeItem(data) {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function vendorSearch(item) {
|
||||||
|
let debounceTimer = null;
|
||||||
|
return {
|
||||||
|
open: false,
|
||||||
|
query: '',
|
||||||
|
results: [],
|
||||||
|
loading: false,
|
||||||
|
highlighted: -1,
|
||||||
|
|
||||||
|
onInput(value) {
|
||||||
|
item.vendor = value;
|
||||||
|
this.query = value;
|
||||||
|
this.highlighted = -1;
|
||||||
|
clearTimeout(debounceTimer);
|
||||||
|
if (value.length < 1) { this.results = []; this.open = false; return; }
|
||||||
|
debounceTimer = setTimeout(() => this.search(value), 250);
|
||||||
|
},
|
||||||
|
|
||||||
|
onFocus() {
|
||||||
|
if (item.vendor && item.vendor.length >= 1) {
|
||||||
|
this.query = item.vendor;
|
||||||
|
this.search(item.vendor);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
async search(keyword) {
|
||||||
|
this.loading = true;
|
||||||
|
try {
|
||||||
|
const res = await fetch(`/barobill/tax-invoice/search-partners?keyword=${encodeURIComponent(keyword)}`);
|
||||||
|
this.results = await res.json();
|
||||||
|
this.open = true;
|
||||||
|
} catch (e) { this.results = []; }
|
||||||
|
this.loading = false;
|
||||||
|
},
|
||||||
|
|
||||||
|
selectPartner(p) {
|
||||||
|
item.vendor = p.name;
|
||||||
|
item.vendor_id = p.id;
|
||||||
|
item.vendor_biz_no = p.biz_no || '';
|
||||||
|
this.query = p.name;
|
||||||
|
this.close();
|
||||||
|
},
|
||||||
|
|
||||||
|
close() { this.open = false; this.highlighted = -1; },
|
||||||
|
|
||||||
|
moveDown() {
|
||||||
|
if (!this.open || this.results.length === 0) return;
|
||||||
|
this.highlighted = (this.highlighted + 1) % this.results.length;
|
||||||
|
},
|
||||||
|
moveUp() {
|
||||||
|
if (!this.open || this.results.length === 0) return;
|
||||||
|
this.highlighted = this.highlighted <= 0 ? this.results.length - 1 : this.highlighted - 1;
|
||||||
|
},
|
||||||
|
selectHighlighted() {
|
||||||
|
if (this.highlighted >= 0 && this.highlighted < this.results.length) {
|
||||||
|
this.selectPartner(this.results[this.highlighted]);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
Reference in New Issue
Block a user