149 lines
4.0 KiB
Vue
149 lines
4.0 KiB
Vue
<template>
|
||
<el-select
|
||
v-model="innerValue"
|
||
:placeholder="placeholder"
|
||
:clearable="clearable"
|
||
:filterable="filterable"
|
||
:filter-method="filterMethod"
|
||
:disabled="disabled"
|
||
:loading="loading"
|
||
:size="size"
|
||
:multiple="false"
|
||
style="width: 100%;"
|
||
@change="handleChange"
|
||
@visible-change="handleVisibleChange"
|
||
>
|
||
<el-option
|
||
v-for="item in options"
|
||
:key="item.userId"
|
||
:label="formatLabel(item)"
|
||
:value="String(item[valueField])"
|
||
:disabled="item.status === '1'"
|
||
>
|
||
<span>{{ item.nickName || item.userName }}<template v-if="item.status === '1'">(停用)</template></span>
|
||
<span
|
||
v-if="showCode && item.userName"
|
||
class="user-select-sub"
|
||
>{{ item.userName }}</span>
|
||
</el-option>
|
||
</el-select>
|
||
</template>
|
||
|
||
<script setup name="UserSelect">
|
||
import { computed, onMounted, ref, watch } from 'vue'
|
||
import { useUsers } from './useUsers'
|
||
|
||
const props = defineProps({
|
||
/** 绑定值:用户ID(字符串/数字),清空时为 null */
|
||
modelValue: {
|
||
type: [String, Number],
|
||
default: null
|
||
},
|
||
placeholder: {
|
||
type: String,
|
||
default: '请选择用户'
|
||
},
|
||
clearable: {
|
||
type: Boolean,
|
||
default: true
|
||
},
|
||
filterable: {
|
||
type: Boolean,
|
||
default: true
|
||
},
|
||
disabled: {
|
||
type: Boolean,
|
||
default: false
|
||
},
|
||
size: {
|
||
type: String,
|
||
default: ''
|
||
},
|
||
/** 选项右侧是否显示登录账号 */
|
||
showCode: {
|
||
type: Boolean,
|
||
default: true
|
||
},
|
||
/** 绑定值字段:userId=用户ID(默认);userName=登录账号/工号(老系统迁移数据按工号存储时使用) */
|
||
valueField: {
|
||
type: String,
|
||
default: 'userId'
|
||
}
|
||
})
|
||
|
||
const emit = defineEmits(['update:modelValue', 'change'])
|
||
|
||
const { userList, ensureUsers } = useUsers()
|
||
const loading = ref(false)
|
||
/** 下拉搜索关键字(可同时匹配昵称/姓名与登录账号/工号) */
|
||
const filterQuery = ref('')
|
||
const innerValue = ref(props.modelValue === null || props.modelValue === undefined ? null : String(props.modelValue))
|
||
|
||
watch(() => props.modelValue, (val) => {
|
||
innerValue.value = val === null || val === undefined || val === '' ? null : String(val)
|
||
})
|
||
|
||
/** 按当前绑定值字段(userId 或 userName/工号)查找用户 */
|
||
function findByValue(val) {
|
||
if (val === null || val === undefined || val === '') return null
|
||
return userList.value.find(u => String(u[props.valueField]) === String(val)) || null
|
||
}
|
||
|
||
/** 自定义过滤:昵称/姓名、登录账号/工号 任一命中即可(忽略大小写) */
|
||
function matchUser(item, q) {
|
||
if (!q) return true
|
||
const kw = q.trim().toLowerCase()
|
||
if (!kw) return true
|
||
return ['nickName', 'userName'].some(k => item[k] && String(item[k]).toLowerCase().includes(kw))
|
||
}
|
||
|
||
function filterMethod(q) {
|
||
filterQuery.value = q || ''
|
||
}
|
||
|
||
/** 下拉收起后清空搜索词,恢复完整选项 */
|
||
function handleVisibleChange(visible) {
|
||
if (!visible) filterQuery.value = ''
|
||
}
|
||
|
||
/** 兜底:当前值在用户列表中不存在(停用/未加载到)时补占位项,避免显示空白 */
|
||
const options = computed(() => {
|
||
const list = userList.value.filter(item => matchUser(item, filterQuery.value))
|
||
if (innerValue.value && !findByValue(innerValue.value) && !filterQuery.value) {
|
||
return [{ userId: innerValue.value, userName: '', nickName: String(innerValue.value) }, ...list]
|
||
}
|
||
return list
|
||
})
|
||
|
||
/** 选项文本:停用用户追加(停用)标记,便于编辑历史数据时识别 */
|
||
function formatLabel(item) {
|
||
const name = item.nickName || item.userName
|
||
return item.status === '1' ? `${name}(停用)` : name
|
||
}
|
||
|
||
function handleChange(val) {
|
||
const value = val === '' ? null : val
|
||
emit('update:modelValue', value)
|
||
emit('change', value, value === null ? null : findByValue(value))
|
||
}
|
||
|
||
onMounted(() => {
|
||
loading.value = true
|
||
ensureUsers().finally(() => {
|
||
loading.value = false
|
||
})
|
||
})
|
||
|
||
defineExpose({
|
||
reload: () => ensureUsers(true)
|
||
})
|
||
</script>
|
||
|
||
<style scoped>
|
||
.user-select-sub {
|
||
float: right;
|
||
color: var(--el-text-color-secondary);
|
||
font-size: 12px;
|
||
}
|
||
</style>
|