Files
dafuweng-saiadmin6.x/saiadmin-artd/src/components/sai/sa-select/index.vue
2026-03-19 15:40:06 +08:00

143 lines
3.9 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<!--
v-bind="$attrs" 透传父组件传递的 width, class, style 以及 change, focus 等事件
-->
<el-select
v-model="modelValue"
v-bind="$attrs"
:placeholder="placeholder"
:disabled="disabled"
:clearable="clearable"
:filterable="filterable"
:multiple="multiple"
:collapse-tags="collapseTags"
:collapse-tags-tooltip="collapseTagsTooltip"
>
<!-- 遍历生成选项 -->
<el-option
v-for="(item, index) in options"
:key="index"
:label="item.label"
:value="item.value"
:disabled="item.disabled"
>
<!-- 支持自定义 option 模板 (可选)不传则只显示 label -->
<slot name="option" :item="item">
<span>{{ item.label }}</span>
</slot>
</el-option>
<!-- 透传 el-select 的其他插槽 ( prefix, empty) -->
<template v-for="(_, name) in $slots" #[name]="slotData">
<slot v-if="name !== 'option'" :name="name" v-bind="slotData || {}"></slot>
</template>
</el-select>
</template>
<script setup lang="ts">
import { computed } from 'vue'
import { useDictStore } from '@/store/modules/dict'
import { useI18n } from 'vue-i18n'
defineOptions({ name: 'SaSelect', inheritAttrs: false })
interface Props {
/** 字典编码 (必填) */
dict: string
/**
* 强制转换字典值的类型
* 解决后端返回字符串但前端表单需要数字的问题
*/
valueType?: 'number' | 'string'
// --- 以下为常用属性显式定义,为了 IDE 提示友好 ---
placeholder?: string
disabled?: boolean
clearable?: boolean
filterable?: boolean
multiple?: boolean
collapseTags?: boolean
collapseTagsTooltip?: boolean
}
const props = withDefaults(defineProps<Props>(), {
placeholder: '请选择',
disabled: false,
clearable: true, // 下拉框默认开启清除,体验更好
filterable: false, // 下拉框默认关闭搜索
multiple: false,
collapseTags: false,
collapseTagsTooltip: false,
valueType: 'number'
})
// 支持单选(string/number) 或 多选(Array)
const modelValue = defineModel<string | number | Array<string | number>>()
const dictStore = useDictStore()
const { t, te, locale } = useI18n()
// 判断能否转成数字
const canConvertToNumberStrict = (value: any) => {
// 严格模式:排除 null、undefined、布尔值、空数组等
if (value == null) return false
if (typeof value === 'boolean') return false
if (Array.isArray(value) && value.length !== 1) return false
if (typeof value === 'object' && !Array.isArray(value)) return false
const num = Number(value)
return !isNaN(num)
}
// 计算属性:获取字典数据并处理类型转换
const options = computed(() => {
// 让字典选项在切换语言时可响应更新
locale.value
const list = dictStore.getByCode(props.dict) || []
// 1. 如果没有指定 valueType直接返回
if (!props.valueType) {
return list.map((item) => {
const key = `dict.${props.dict}.${item.value}`
return {
...item,
label: te(key) ? t(key) : item.label
}
})
}
// 2. 如果指定了类型,进行映射转换
return list.map((item) => {
let newValue = item.value
switch (props.valueType) {
case 'number':
if (canConvertToNumberStrict(item.value)) {
newValue = Number(item.value)
}
break
case 'string':
newValue = String(item.value)
break
}
return {
...item,
value: newValue,
label: (() => {
const key = `dict.${props.dict}.${newValue}`
return te(key) ? t(key) : item.label
})()
}
})
})
</script>
<style scoped>
/* 让 Select 默认宽度占满父容器,通常在表单中体验更好,可视情况删除 */
.el-select {
width: 100%;
}
</style>