185 lines
3.9 KiB
Vue
185 lines
3.9 KiB
Vue
<!-- components/uni-app-tag/uni-app-tag.vue -->
|
||
<template>
|
||
<view class="uni-app-tag-container">
|
||
<!-- 匹配的标签 -->
|
||
<template
|
||
v-for="(item, index) in matchedItems"
|
||
:key="getValue(item)"
|
||
>
|
||
<!-- 普通 span(default 类型 + 无 class) -->
|
||
<span
|
||
v-if="isDefaultTag(item)"
|
||
class="uni-app-tag-span"
|
||
:class="item.elTagClass"
|
||
>
|
||
{{ getText(item) }}
|
||
</span>
|
||
|
||
<!-- 自定义样式 view 模拟 tag -->
|
||
<view
|
||
v-else
|
||
class="uni-app-tag"
|
||
:class="[
|
||
`uni-app-tag--${getUniType(item.elTagType)}`,
|
||
item.elTagClass
|
||
]"
|
||
>
|
||
{{ getText(item) }}
|
||
</view>
|
||
</template>
|
||
|
||
<!-- 未匹配的值 -->
|
||
<text v-if="unmatch && showValue" class="uni-app-tag-unmatch">
|
||
{{ unmatchText }}
|
||
</text>
|
||
</view>
|
||
</template>
|
||
|
||
<script setup>
|
||
import { computed } from 'vue'
|
||
|
||
const props = defineProps({
|
||
/* 选项数组 */
|
||
options: {
|
||
type: Array,
|
||
default: () => []
|
||
},
|
||
/* 当前值:String | Number | Array */
|
||
value: [String, Number, Array],
|
||
/* 是否显示未匹配值 */
|
||
showValue: {
|
||
type: Boolean,
|
||
default: true
|
||
},
|
||
/* 字符串分隔符(仅 value 为字符串时生效) */
|
||
separator: {
|
||
type: String,
|
||
default: ','
|
||
},
|
||
/* 字段映射:值字段名 */
|
||
valueField: {
|
||
type: String,
|
||
default: 'dictValue'
|
||
},
|
||
/* 字段映射:文本字段名 */
|
||
textField: {
|
||
type: String,
|
||
default: 'dictLabel'
|
||
}
|
||
})
|
||
|
||
/* 工具函数:统一取出值 / 文本 */
|
||
const getValue = (opt) => String(opt[props.valueField])
|
||
const getText = (opt) => opt[props.textField] ?? ''
|
||
|
||
/* 1. 统一转成字符串数组 */
|
||
const values = computed(() => {
|
||
if (props.value == null || props.value === '') return []
|
||
return Array.isArray(props.value)
|
||
? props.value.map(String)
|
||
: String(props.value).split(props.separator).map(v => v.trim())
|
||
})
|
||
|
||
/* 2. 匹配到的选项 */
|
||
const matchedItems = computed(() =>
|
||
values.value
|
||
.map(val => props.options.find(opt => getValue(opt) === val))
|
||
.filter(Boolean)
|
||
)
|
||
|
||
/* 3. 未匹配的值 */
|
||
const unmatchArray = computed(() => {
|
||
if (!values.value.length || !props.options.length) return []
|
||
return values.value.filter(
|
||
val => !props.options.some(opt => getValue(opt) === val)
|
||
)
|
||
})
|
||
const unmatch = computed(() => unmatchArray.value.length > 0)
|
||
const unmatchText = computed(() => unmatchArray.value.join(' '))
|
||
|
||
/* 4. 判断是否为默认 tag(default 且无自定义 class) */
|
||
const isDefaultTag = (item) => {
|
||
const type = item.elTagType || 'default'
|
||
const cls = item.elTagClass || ''
|
||
return (type === 'default' || !type) && !cls
|
||
}
|
||
|
||
/* 5. el-tag type -> uni 类名映射 */
|
||
const getUniType = (type) => {
|
||
const map = {
|
||
primary: 'primary',
|
||
success: 'success',
|
||
warning: 'warning',
|
||
danger: 'error',
|
||
info: 'info',
|
||
default: 'default'
|
||
}
|
||
return map[type] || 'default'
|
||
}
|
||
</script>
|
||
|
||
<style scoped>
|
||
.uni-app-tag-container {
|
||
display: flex;
|
||
flex-wrap: wrap;
|
||
align-items: center;
|
||
font-size: 14px;
|
||
}
|
||
|
||
.uni-app-tag-span {
|
||
margin-right: 8px;
|
||
white-space: nowrap;
|
||
}
|
||
|
||
.uni-app-tag {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
padding: 2px 8px;
|
||
border-radius: 4px;
|
||
font-size: 12px;
|
||
margin-right: 8px;
|
||
white-space: nowrap;
|
||
}
|
||
|
||
.uni-app-tag--primary {
|
||
background-color: #d0e3ff;
|
||
color: #0066ff;
|
||
border: 1px solid #99ccff;
|
||
}
|
||
|
||
.uni-app-tag--success {
|
||
background-color: #dcf5e7;
|
||
color: #00aa55;
|
||
border: 1px solid #88ccaa;
|
||
}
|
||
|
||
.uni-app-tag--warning {
|
||
background-color: #fff0d9;
|
||
color: #cc8800;
|
||
border: 1px solid #ffcc88;
|
||
}
|
||
|
||
.uni-app-tag--error {
|
||
background-color: #ffd9dc;
|
||
color: #ff3344;
|
||
border: 1px solid #ff99aa;
|
||
}
|
||
|
||
.uni-app-tag--info {
|
||
background-color: #e6e6e6;
|
||
color: #666666;
|
||
border: 1px solid #cccccc;
|
||
}
|
||
|
||
.uni-app-tag--default {
|
||
background-color: #f4f4f4;
|
||
color: #333;
|
||
border: 1px solid #ddd;
|
||
}
|
||
|
||
.uni-app-tag-unmatch {
|
||
margin-left: 4px;
|
||
color: #999;
|
||
font-size: 12px;
|
||
}
|
||
</style> |