From 94e7a18e0bd833ebb09ea4c9d6fc5f288966e13e Mon Sep 17 00:00:00 2001 From: caojia <1301869380@qq.com> Date: Fri, 9 Oct 2026 10:33:41 +0800 Subject: [PATCH] =?UTF-8?q?:sparkles:=20=E6=94=AF=E6=8C=81=E8=B4=A6?= =?UTF-8?q?=E5=8D=95=E5=AE=A2=E6=88=B7=E5=90=8D=E7=A7=B0=E5=92=8C=E7=BC=96?= =?UTF-8?q?=E7=A0=81=E8=BF=9C=E7=A8=8B=E6=90=9C=E7=B4=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/components/CustomerSelect/index.vue | 67 ++++++++++++++++--- .../ar/bill/components/GenerateBillDialog.vue | 2 +- bms-front/src/views/ar/bill/index.vue | 4 +- 3 files changed, 61 insertions(+), 12 deletions(-) diff --git a/bms-front/src/components/CustomerSelect/index.vue b/bms-front/src/components/CustomerSelect/index.vue index 50fe8736..23377611 100644 --- a/bms-front/src/components/CustomerSelect/index.vue +++ b/bms-front/src/components/CustomerSelect/index.vue @@ -10,12 +10,15 @@ :disabled="disabled" :loading="loading" :size="size" + :remote="remoteSearch" + :remote-method="remoteSearch ? handleRemoteSearch : undefined" + :reserve-keyword="remoteSearch" style="width: 100%;" @change="handleChange" @visible-change="handleVisibleChange" > props.type === 'customer') const realValueKey = computed(() => props.valueKey || (isCustomer.value ? 'customerId' : 'supplierId')) @@ -105,36 +116,68 @@ const realCodeKey = computed(() => props.codeKey || (isCustomer.value ? 'custome watch(() => props.modelValue, (val) => { // 多选模式下空值归一化为数组,避免 el-select 告警 innerValue.value = props.multiple && (val === undefined || val === null) ? [] : val + const empty = props.multiple ? !Array.isArray(val) || val.length === 0 : val === undefined || val === null || val === '' + if (empty) selectedOptions.value = [] }, { immediate: true }) function getLabel(item) { return item[realLabelKey.value] || '' } -function fetchList() { - if (loaded.value) return Promise.resolve(options.value) +const displayOptions = computed(() => { + const merged = [...selectedOptions.value, ...options.value] + const seen = new Set() + return merged.filter(item => { + const key = String(item[realValueKey.value]) + if (seen.has(key)) return false + seen.add(key) + return true + }) +}) + +function fetchList(keyword = '') { + if (!props.remoteSearch && loaded.value) return Promise.resolve(options.value) loading.value = true const api = isCustomer.value ? listCustomer : listSupplier - return api({ pageNum: 1, pageSize: props.pageSize, ...props.params }) + const currentRequest = ++requestSerial + const query = String(keyword || '').trim() + return api({ + pageNum: 1, + pageSize: props.pageSize, + ...props.params, + ...(props.remoteSearch ? { keywords: query || undefined } : {}) + }) .then(res => { + if (currentRequest !== requestSerial) return options.value options.value = res.rows || [] loaded.value = true - return options.value + return displayOptions.value }) .finally(() => { - loading.value = false + if (currentRequest === requestSerial) loading.value = false }) } +function handleRemoteSearch(keyword) { + if (!props.remoteSearch) return + if (remoteTimer.value) clearTimeout(remoteTimer.value) + remoteTimer.value = setTimeout(() => { + remoteTimer.value = null + fetchList(keyword) + }, 300) +} + function handleChange(val) { emit('update:modelValue', val) if (props.multiple) { - const selected = options.value.filter(item => + const selected = displayOptions.value.filter(item => Array.isArray(val) && val.some(v => String(v) === String(item[realValueKey.value])) ) + selectedOptions.value = selected emit('change', val, selected) } else { - const selected = options.value.find(item => String(item[realValueKey.value]) === String(val)) + const selected = displayOptions.value.find(item => String(item[realValueKey.value]) === String(val)) + selectedOptions.value = selected ? [selected] : [] emit('change', val, selected) } } @@ -149,12 +192,18 @@ onMounted(() => { fetchList() }) +onBeforeUnmount(() => { + if (remoteTimer.value) clearTimeout(remoteTimer.value) + requestSerial++ +}) + defineExpose({ reload: () => { loaded.value = false + options.value = [] return fetchList() }, - getOptions: () => options.value + getOptions: () => displayOptions.value }) diff --git a/bms-front/src/views/ar/bill/components/GenerateBillDialog.vue b/bms-front/src/views/ar/bill/components/GenerateBillDialog.vue index 7e006f63..b012766d 100644 --- a/bms-front/src/views/ar/bill/components/GenerateBillDialog.vue +++ b/bms-front/src/views/ar/bill/components/GenerateBillDialog.vue @@ -3,7 +3,7 @@ - + diff --git a/bms-front/src/views/ar/bill/index.vue b/bms-front/src/views/ar/bill/index.vue index 566bbdce..d5ca54d8 100644 --- a/bms-front/src/views/ar/bill/index.vue +++ b/bms-front/src/views/ar/bill/index.vue @@ -33,7 +33,7 @@ 清除客户条件 @@ -153,7 +153,7 @@ const columns = [ { label: '账单日期', prop: 'billDate', width: 120, formatter: row => date(row.billDate) }, { label: '操作', prop: 'action', width: 330, fixed: 'right', slot: 'action', showInToolbar: false } ] const searchColumns = [ - { label: '账单号', prop: 'billNo', component: 'input', placeholder: '账单号', width: '180px' }, { label: '账单名称', prop: 'billName', component: 'input', placeholder: '账单名称', width: '180px' }, { label: '客户名称', prop: 'customerId', component: 'input', placeholder: '请选择客户', width: '220px' }, { label: '客户编码', prop: 'customerCode', component: 'input', placeholder: '客户编码', width: '140px' }, + { label: '账单号', prop: 'billNo', component: 'input', placeholder: '账单号', width: '180px' }, { label: '账单名称', prop: 'billName', component: 'input', placeholder: '账单名称', width: '180px' }, { label: '客户名称', prop: 'customerId', component: 'input', placeholder: '请选择客户', width: '220px' }, { label: '状态', prop: 'status', component: 'select', options: statusOptions, placeholder: '全部状态', width: '150px' }, { label: '账单日期', prop: 'billDateRange', component: 'daterange', width: '300px' }, { label: '账单截止日', prop: 'billPeriodEnd', component: 'date', width: '160px' } ]