✨ 支持账单客户名称和编码远程搜索
This commit is contained in:
@@ -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>
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<el-dialog v-model="dialogOpen" class="generate-dialog" title="生成应收账单" width="1200px" append-to-body destroy-on-close>
|
||||
<el-form ref="generateFormRef" :model="generateForm" :rules="generateRules" label-width="110px" class="generate-form">
|
||||
<el-row :gutter="16">
|
||||
<el-col :span="8"><el-form-item label="客户" prop="customerId"><customer-select v-model="generateForm.customerId" placeholder="请选择客户" @change="handleCustomerChange" /></el-form-item></el-col>
|
||||
<el-col :span="8"><el-form-item label="客户" prop="customerId"><customer-select v-model="generateForm.customerId" placeholder="请输入客户名称或代码搜索" :remote-search="true" :page-size="20" @change="handleCustomerChange" /></el-form-item></el-col>
|
||||
<el-col :span="8"><el-form-item label="币种" prop="currency"><el-select v-model="generateForm.currency" placeholder="请选择币种" style="width: 100%;" @change="handleCurrencyChange"><el-option label="人民币 CNY" value="CNY" /><el-option label="美元 USD" value="USD" /></el-select></el-form-item></el-col>
|
||||
<el-col :span="8"><el-form-item label="账单截止日" prop="cutoffDate"><el-date-picker v-model="generateForm.cutoffDate" type="date" value-format="YYYY-MM-DD" placeholder="请选择账单截止日" style="width: 100%;" @change="resetPreview" /></el-form-item></el-col>
|
||||
<el-col :span="8">
|
||||
|
||||
@@ -33,7 +33,7 @@
|
||||
<el-col v-if="customerScoped" :span="1.5"><el-button type="info" plain icon="Close" @click="clearCustomerFilter">清除客户条件</el-button></el-col>
|
||||
</template>
|
||||
<template #search-customerId="{ model }">
|
||||
<customer-select v-model="model.customerId" placeholder="请选择客户" :page-size="1000" />
|
||||
<customer-select v-model="model.customerId" placeholder="请输入客户名称或代码搜索" :remote-search="true" :page-size="20" />
|
||||
</template>
|
||||
<template #col-status="{ row }"><el-tag :type="statusType(row.status)">{{ statusText(row.status) }}</el-tag></template>
|
||||
<template #col-totalAmount="{ row }"><span class="amount">{{ money(row.totalAmount) }}</span></template>
|
||||
@@ -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' }
|
||||
]
|
||||
|
||||
|
||||
Reference in New Issue
Block a user