168 lines
4.0 KiB
Vue
168 lines
4.0 KiB
Vue
<template>
|
|
<a-select
|
|
v-model:value="value"
|
|
:allowClear="true"
|
|
show-search
|
|
:mode="multiple ? 'multiple' : undefined"
|
|
style="width: 250px"
|
|
:filter-option="false"
|
|
placeholder="Select Item"
|
|
max-tag-count="responsive"
|
|
:options="options"
|
|
:loading="fetching"
|
|
@popupScroll="handleScrollUserList"
|
|
@search="onSearch"
|
|
@focus="handleFocus"
|
|
@change="onChange"
|
|
></a-select>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import { computed, ref, reactive ,defineExpose} from 'vue'
|
|
import { debounce } from 'lodash-es'
|
|
import { Https } from '@/tool/https'
|
|
import { useStore } from '@/store'
|
|
|
|
type OptionItem = { [k: string]: any }
|
|
|
|
const props = defineProps<{
|
|
modelValue: any
|
|
labelKey?: string
|
|
valueKey?: string
|
|
multiple?: boolean
|
|
}>()
|
|
|
|
const emit = defineEmits(['update:modelValue', 'change', 'search'])
|
|
|
|
const pager = reactive<{ page: number; size: number; hasMore: boolean }>({
|
|
page: 1,
|
|
size: 30,
|
|
hasMore: true
|
|
})
|
|
const fetching = ref(false)
|
|
const keyword = ref('')
|
|
const internalList = ref<OptionItem[]>([])
|
|
|
|
const value = computed({
|
|
get: () => {
|
|
const mv = props.modelValue
|
|
if (mv === '' || mv === null) return undefined
|
|
return mv
|
|
},
|
|
set: v => emit('update:modelValue', v)
|
|
})
|
|
|
|
const options = computed(() => {
|
|
return internalList.value.map((it, idx) => ({
|
|
label: it[props.labelKey] || it.label,
|
|
value: it[props.valueKey] || it.value,
|
|
raw: it
|
|
}))
|
|
})
|
|
|
|
const defaultFetch = async ({
|
|
page: p,
|
|
pageSize: ps,
|
|
keyword: kw
|
|
}: {
|
|
page: number
|
|
pageSize: number
|
|
keyword: string
|
|
}) => {
|
|
const res =
|
|
(await Https.axiosGet(Https.httpUrls.getAllUserId, {
|
|
params: { page: p, size: ps, email: kw }
|
|
})) || []
|
|
// store.commit('setAllUserList', rv)
|
|
|
|
// Return the raw list from sessionStorage exactly as-is (no slicing/filtering/delay)
|
|
return { data: res.records, hasMore: res.current <= res.pages }
|
|
}
|
|
|
|
const doFetch = async (reset = false) => {
|
|
if (reset) {
|
|
pager.page = 1
|
|
pager.hasMore = true
|
|
}
|
|
if (!pager.hasMore) return
|
|
fetching.value = true
|
|
try {
|
|
const res = await defaultFetch({
|
|
page: pager.page,
|
|
pageSize: pager.size,
|
|
keyword: keyword.value
|
|
})
|
|
const data = res?.data
|
|
pager.hasMore = res?.hasMore
|
|
if (tempItem.value.value) {
|
|
const exists = data.find(
|
|
it =>
|
|
(props.valueKey ? it[props.valueKey] : it.value) ===
|
|
(props.valueKey ? tempItem.value[props.valueKey] : tempItem.value.value)
|
|
)
|
|
if (!!exists) {
|
|
// 如果存在,用data中的新数据覆盖internalList中的旧数据
|
|
const index = internalList.value.indexOf(tempItem.value)
|
|
const existsIndex = data.indexOf(exists)
|
|
internalList.value[index] = { ...exists }
|
|
data.splice(existsIndex, 1)
|
|
tempItem.value = {}
|
|
}
|
|
}
|
|
if (pager.page === 1) internalList.value = data
|
|
else internalList.value = internalList.value.concat(data)
|
|
pager.page += 1
|
|
} finally {
|
|
fetching.value = false
|
|
}
|
|
}
|
|
|
|
const debouncedSearch = debounce((val: string) => {
|
|
keyword.value = val
|
|
emit('search', val)
|
|
doFetch(true)
|
|
}, 300)
|
|
|
|
const onSearch = (val: string) => {
|
|
debouncedSearch(val)
|
|
}
|
|
|
|
const handleScrollUserList = (e: Event) => {
|
|
const target = e?.target as HTMLElement | null
|
|
if (!target) return
|
|
const nearBottom = target.scrollTop + target.clientHeight >= target.scrollHeight - 30
|
|
if (nearBottom) {
|
|
doFetch(false)
|
|
}
|
|
}
|
|
|
|
const handleFocus = () => {
|
|
// When focused, load first page (or reload with current keyword)
|
|
doFetch(true)
|
|
}
|
|
|
|
const onChange = (val: any) => {
|
|
emit('change', val)
|
|
}
|
|
|
|
// 解决回显时没有加载对应选项的问题
|
|
const tempItem = ref({})
|
|
const patchList = item => {
|
|
const exists = internalList.value.find(
|
|
it =>
|
|
(props.valueKey ? it[props.valueKey] : it.value) ===
|
|
(props.valueKey ? item[props.valueKey] : item.value)
|
|
)
|
|
if (!exists) {
|
|
internalList.value.unshift({ ...item, temp: true })
|
|
tempItem.value = item
|
|
}
|
|
}
|
|
|
|
defineExpose({
|
|
patchList
|
|
})
|
|
</script>
|
|
|
|
<style lang="less" scoped></style>
|