94 lines
2.2 KiB
Vue
94 lines
2.2 KiB
Vue
<script setup lang="ts">
|
|
import { getUsers } from '@/api/user'
|
|
import type { User } from '@/types'
|
|
import { computed, ref, watch } from 'vue'
|
|
|
|
interface Props {
|
|
modelValue?: string | string[]
|
|
multiple?: boolean
|
|
disabled?: boolean
|
|
placeholder?: string
|
|
}
|
|
|
|
const props = withDefaults(defineProps<Props>(), {
|
|
multiple: false,
|
|
disabled: false,
|
|
placeholder: '请选择用户'
|
|
})
|
|
|
|
// 确保 modelValue 始终是正确的类型
|
|
const normalizedValue = computed(() => {
|
|
if (props.multiple) {
|
|
return Array.isArray(props.modelValue) ? props.modelValue : []
|
|
}
|
|
return props.modelValue || ''
|
|
})
|
|
|
|
const emit = defineEmits<{
|
|
(e: 'update:modelValue', value: string | string[]): void
|
|
(e: 'change', value: string | string[]): void
|
|
}>()
|
|
|
|
const users = ref<User[]>([])
|
|
const loading = ref(false)
|
|
const searchValue = ref('')
|
|
const selectedValue = computed({
|
|
get: () => normalizedValue.value,
|
|
set: (val) => {
|
|
emit('update:modelValue', val!)
|
|
emit('change', val!)
|
|
}
|
|
})
|
|
|
|
const filteredUsers = computed(() => {
|
|
if (!searchValue.value) return users.value
|
|
const keyword = searchValue.value.toLowerCase()
|
|
return users.value.filter(u =>
|
|
u.username.toLowerCase().includes(keyword) ||
|
|
(u.realName && u.realName.toLowerCase().includes(keyword))
|
|
)
|
|
})
|
|
|
|
const options = computed(() =>
|
|
filteredUsers.value.map((user) => ({
|
|
value: user.id,
|
|
label: `${user.realName || user.username} (${user.username})`
|
|
}))
|
|
)
|
|
|
|
const handleSearch = (value: string) => {
|
|
searchValue.value = value
|
|
}
|
|
|
|
const fetchUsers = async () => {
|
|
loading.value = true
|
|
try {
|
|
const res = await getUsers()
|
|
// res 是 ApiResponse<User[]>,所以 res.data 是 User[]
|
|
users.value = (res.data as any)?.data || res.data || []
|
|
} finally {
|
|
loading.value = false
|
|
}
|
|
}
|
|
|
|
watch(() => props.modelValue, () => {
|
|
if (users.value.length === 0) {
|
|
fetchUsers()
|
|
}
|
|
}, { immediate: true })
|
|
</script>
|
|
|
|
<template>
|
|
<a-select
|
|
v-model:value="selectedValue"
|
|
:options="options"
|
|
:placeholder="placeholder"
|
|
:disabled="disabled"
|
|
:mode="multiple ? 'multiple' : undefined"
|
|
show-search
|
|
:filter-option="false"
|
|
:search-value="searchValue"
|
|
@search="handleSearch"
|
|
/>
|
|
</template>
|