ether-admin/src/components/UserSelect/index.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>