✨ 支持账单客户名称和编码远程搜索

This commit is contained in:
caojia
2026-10-09 10:33:41 +08:00
parent 5b4cbaffd6
commit 94e7a18e0b
3 changed files with 61 additions and 12 deletions
@@ -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"
>
<el-option
v-for="item in options"
v-for="item in displayOptions"
:key="item[realValueKey]"
:label="getLabel(item)"
:value="item[realValueKey]"
@@ -87,6 +90,11 @@ const props = defineProps({
pageSize: {
type: Number,
default: 1000
},
// 远程搜索时按客户名称/代码查询,避免本地只加载固定数量选项。
remoteSearch: {
type: Boolean,
default: false
}
})
@@ -94,8 +102,11 @@ const emit = defineEmits(['update:modelValue', 'change'])
const innerValue = ref(props.modelValue)
const options = ref([])
const selectedOptions = ref([])
const loading = ref(false)
const loaded = ref(false)
const remoteTimer = ref(null)
let requestSerial = 0
const isCustomer = computed(() => 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
})
</script>