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>
|