2025-06-27 18:37:42 +08:00
|
|
|
<!--
|
|
|
|
|
* @Author: RenXiaoDong
|
|
|
|
|
* @Date: 2025-06-25 14:02:40
|
|
|
|
|
-->
|
|
|
|
|
<template>
|
2025-09-04 12:07:18 +08:00
|
|
|
<Select
|
|
|
|
|
v-model:value="selectedValues"
|
|
|
|
|
:mode="multiple ? 'multiple' : undefined"
|
|
|
|
|
size="middle"
|
2025-06-27 18:37:42 +08:00
|
|
|
:placeholder="placeholder"
|
2025-09-04 12:07:18 +08:00
|
|
|
:allowClear="allClear"
|
|
|
|
|
:showSearch="allowSearch"
|
2025-09-04 16:10:44 +08:00
|
|
|
showArrow
|
2025-09-04 12:07:18 +08:00
|
|
|
:maxTagCount="maxTagCount"
|
2025-06-27 18:37:42 +08:00
|
|
|
@change="handleChange"
|
2025-09-12 17:48:46 +08:00
|
|
|
:filterOption="allowSearch ? filterOption : undefined"
|
2025-06-27 18:37:42 +08:00
|
|
|
>
|
2025-09-12 17:46:01 +08:00
|
|
|
<Option v-for="(item, index) in validOptions" :key="index" :value="item.value" :label="item.label">
|
2025-09-04 11:09:21 +08:00
|
|
|
<div class="flex items-center">
|
2025-09-12 17:46:01 +08:00
|
|
|
<img v-if="item.icon" :src="item.icon" class="w-16px h-16px mr-4px rounded-4px" />
|
|
|
|
|
<span>{{ item.label }}</span>
|
2025-09-04 11:09:21 +08:00
|
|
|
</div>
|
2025-09-04 12:07:18 +08:00
|
|
|
</Option>
|
2025-09-12 17:46:01 +08:00
|
|
|
<template #tag="{ label, icon }">
|
|
|
|
|
<div class="flex items-center">
|
|
|
|
|
<img v-if="icon" :src="icon" class="w-16px h-16px mr-4px rounded-4px" />
|
|
|
|
|
<span>{{ label }}</span>
|
|
|
|
|
</div>
|
|
|
|
|
</template>
|
|
|
|
|
<template v-if="!allowSearch" #empty>
|
|
|
|
|
<div v-if="validOptions.length === 0" class="empty-placeholder">
|
|
|
|
|
{{ placeholder || '暂无数据' }}
|
|
|
|
|
</div>
|
|
|
|
|
</template>
|
2025-09-04 12:07:18 +08:00
|
|
|
</Select>
|
2025-06-27 18:37:42 +08:00
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
<script setup>
|
2025-09-04 12:07:18 +08:00
|
|
|
import { Select } from 'ant-design-vue';
|
|
|
|
|
const { Option } = Select;
|
2025-09-12 17:46:01 +08:00
|
|
|
import { ref, watch, computed } from 'vue';
|
2025-06-27 18:37:42 +08:00
|
|
|
|
|
|
|
|
const props = defineProps({
|
|
|
|
|
modelValue: {
|
|
|
|
|
type: [Array, String, Number],
|
|
|
|
|
default: () => [],
|
|
|
|
|
},
|
|
|
|
|
multiple: {
|
|
|
|
|
type: Boolean,
|
|
|
|
|
default: true,
|
|
|
|
|
},
|
|
|
|
|
placeholder: {
|
|
|
|
|
type: String,
|
|
|
|
|
default: '全部',
|
|
|
|
|
},
|
|
|
|
|
options: {
|
|
|
|
|
type: Array,
|
|
|
|
|
default: () => [],
|
|
|
|
|
},
|
2025-07-23 14:30:44 +08:00
|
|
|
maxTagCount: {
|
|
|
|
|
type: Number,
|
|
|
|
|
default: 3,
|
2025-09-12 17:46:01 +08:00
|
|
|
},
|
2025-08-01 17:25:21 +08:00
|
|
|
allClear: {
|
|
|
|
|
type: Boolean,
|
|
|
|
|
default: true,
|
2025-09-01 17:33:19 +08:00
|
|
|
},
|
|
|
|
|
allowSearch: {
|
|
|
|
|
type: Boolean,
|
|
|
|
|
default: false,
|
2025-09-12 17:46:01 +08:00
|
|
|
},
|
2025-06-27 18:37:42 +08:00
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const emits = defineEmits(['update:modelValue', 'change']);
|
|
|
|
|
|
2025-07-23 14:30:44 +08:00
|
|
|
const selectedValues = ref(props.multiple ? [] : '');
|
2025-06-27 18:37:42 +08:00
|
|
|
|
2025-09-12 17:46:01 +08:00
|
|
|
// 计算有效选项,兼容不同的数据格式
|
|
|
|
|
const validOptions = computed(() => {
|
|
|
|
|
if (!props.options || !Array.isArray(props.options)) {
|
|
|
|
|
return [];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return props.options
|
|
|
|
|
.filter(item => item && (item.id !== undefined || item.value !== undefined) && (item.name !== undefined || item.label !== undefined))
|
|
|
|
|
.map(item => ({
|
|
|
|
|
...item,
|
|
|
|
|
value: item.id !== undefined ? item.id : item.value,
|
|
|
|
|
label: item.name !== undefined ? item.name : item.label
|
|
|
|
|
}));
|
|
|
|
|
});
|
|
|
|
|
|
2025-06-27 18:37:42 +08:00
|
|
|
watch(
|
|
|
|
|
() => props.modelValue,
|
|
|
|
|
(newVal) => {
|
2025-07-23 14:30:44 +08:00
|
|
|
selectedValues.value = newVal;
|
2025-06-27 18:37:42 +08:00
|
|
|
},
|
|
|
|
|
{ immediate: true },
|
|
|
|
|
);
|
2025-07-23 14:30:44 +08:00
|
|
|
|
|
|
|
|
watch(selectedValues, (newVal) => {
|
2025-06-27 18:37:42 +08:00
|
|
|
emits('update:modelValue', newVal);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const handleChange = (value) => {
|
2025-07-23 14:30:44 +08:00
|
|
|
selectedValues.value = value;
|
2025-06-27 18:37:42 +08:00
|
|
|
emits('change', value);
|
|
|
|
|
};
|
2025-09-12 17:48:46 +08:00
|
|
|
|
|
|
|
|
// 搜索过滤函数
|
|
|
|
|
const filterOption = (input, option) => {
|
|
|
|
|
// 确保 input 和 option.label 都存在且为字符串
|
|
|
|
|
if (!input || !option || !option.label) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 不区分大小写的模糊搜索
|
|
|
|
|
return option.label.toString().toLowerCase().includes(input.toLowerCase());
|
|
|
|
|
};
|
2025-09-12 17:46:01 +08:00
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
<style scoped>
|
|
|
|
|
.empty-placeholder {
|
|
|
|
|
padding: 8px 12px;
|
|
|
|
|
color: #8f959e;
|
|
|
|
|
text-align: center;
|
|
|
|
|
}
|
|
|
|
|
</style>
|