第一次接触Element UI的el-tag组件是在一个用户画像系统的开发中。当时需要给用户打上各种兴趣标签,比如"篮球"、"编程"、"旅游"这些。试过原生div+css的方案,发现样式不统一,交互功能也得从零开发。直到发现了el-tag这个现成的解决方案,简直像发现了宝藏。
el-tag本质上是一个封装好的标签组件,开箱即用。它最吸引我的地方在于:
html复制<el-tag type="success" size="small" closable>成功标签</el-tag>
这行简单的代码就能生成一个带删除按钮的绿色小标签。在实际项目中,el-tag特别适合用于:
el-tag提供了五种预设颜色类型,通过type属性控制:
html复制<el-tag>默认</el-tag>
<el-tag type="success">成功</el-tag>
<el-tag type="info">信息</el-tag>
<el-tag type="warning">警告</el-tag>
<el-tag type="danger">危险</el-tag>
每种类型都有对应的语义化场景。比如在内容管理系统中,我用"success"表示已发布,"warning"表示待审核,"danger"表示已下架。
更妙的是effect属性,它能改变标签的视觉风格:
html复制<el-tag type="success" effect="dark">深色成功</el-tag>
<el-tag type="success" effect="light">浅色成功</el-tag>
<el-tag type="success" effect="plain">朴素成功</el-tag>
实际项目中,我常用dark风格做主要标签,light风格做次要标签,plain风格则用于需要弱化显示的标签。
size属性控制标签大小,有四个可选值:
html复制<el-tag size="mini">超小</el-tag>
<el-tag size="small">小</el-tag>
<el-tag size="medium">中</el-tag>
<el-tag size="large">大</el-tag>
在用户画像系统中,我根据标签权重分配不同尺寸——核心标签用medium,普通标签用small,边缘标签用mini。这样一眼就能看出重点。
el-tag最强大的地方在于它能完美配合Vue的数据绑定。通过v-for指令,可以轻松实现标签列表:
html复制<el-tag
v-for="tag in userTags"
:key="tag.id"
:type="tag.type"
closable
@close="removeTag(tag)"
>
{{ tag.name }}
</el-tag>
对应的data和methods:
javascript复制data() {
return {
userTags: [
{ id: 1, name: '前端开发', type: 'success' },
{ id: 2, name: 'Node.js', type: 'info' },
{ id: 3, name: 'UI设计', type: 'warning' }
]
}
},
methods: {
removeTag(tag) {
this.userTags = this.userTags.filter(t => t.id !== tag.id)
}
}
这种模式特别适合用户兴趣标签管理。数据变化会自动反映到界面上,开发效率极高。
在实际项目中,标签管理通常需要完整的CRUD功能。下面分享我在内容管理系统中的实现方案:
新增标签:
html复制<el-button @click="showAddDialog">添加标签</el-button>
<!-- 添加标签对话框 -->
<el-dialog title="添加标签" :visible.sync="addDialogVisible">
<el-input v-model="newTagName" placeholder="请输入标签名"></el-input>
<el-select v-model="newTagType" placeholder="请选择类型">
<el-option label="默认" value=""></el-option>
<el-option label="成功" value="success"></el-option>
<el-option label="信息" value="info"></el-option>
<el-option label="警告" value="warning"></el-option>
<el-option label="危险" value="danger"></el-option>
</el-select>
<span slot="footer">
<el-button @click="addDialogVisible = false">取消</el-button>
<el-button type="primary" @click="addTag">确定</el-button>
</span>
</el-dialog>
javascript复制methods: {
showAddDialog() {
this.newTagName = ''
this.newTagType = ''
this.addDialogVisible = true
},
addTag() {
if (!this.newTagName.trim()) return
this.userTags.push({
id: Date.now(),
name: this.newTagName,
type: this.newTagType
})
this.addDialogVisible = false
}
}
编辑标签:
实现思路类似,主要是传递当前标签数据到编辑对话框,保存时更新对应标签。
删除标签:
除了前面提到的基本删除,实际项目中我通常会加入确认环节:
javascript复制methods: {
removeTag(tag) {
this.$confirm(`确定删除标签"${tag.name}"吗?`, '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
this.userTags = this.userTags.filter(t => t.id !== tag.id)
this.$message.success('删除成功')
})
}
}
在实际项目中,标签数据通常需要保存到后端。这里分享我的前后端交互方案:
获取标签列表:
javascript复制async created() {
try {
const res = await axios.get('/api/user/tags')
this.userTags = res.data
} catch (err) {
console.error('获取标签失败', err)
}
}
保存标签变更:
javascript复制methods: {
async addTag() {
try {
const res = await axios.post('/api/user/tags', {
name: this.newTagName,
type: this.newTagType
})
this.userTags.push(res.data)
this.addDialogVisible = false
} catch (err) {
console.error('添加标签失败', err)
}
},
async removeTag(tag) {
try {
await axios.delete(`/api/user/tags/${tag.id}`)
this.userTags = this.userTags.filter(t => t.id !== tag.id)
} catch (err) {
console.error('删除标签失败', err)
}
}
}
标签系统经常需要配合筛选功能。比如点击某个标签,筛选出相关内容:
html复制<el-tag
v-for="tag in allTags"
:key="tag.id"
:type="activeTag === tag.id ? 'primary' : ''"
@click="toggleFilter(tag.id)"
>
{{ tag.name }}
</el-tag>
javascript复制data() {
return {
activeTag: null,
allTags: [
{ id: 1, name: '技术' },
{ id: 2, name: '生活' },
{ id: 3, name: '旅行' }
],
filteredItems: []
}
},
methods: {
toggleFilter(tagId) {
this.activeTag = this.activeTag === tagId ? null : tagId
this.filterItems()
},
async filterItems() {
const params = this.activeTag ? { tagId: this.activeTag } : {}
const res = await axios.get('/api/items', { params })
this.filteredItems = res.data
}
}
为了提高复用性,我将标签管理封装成了独立组件:
html复制<!-- TagManager.vue -->
<template>
<div class="tag-manager">
<el-tag
v-for="tag in tags"
:key="tag.id"
closable
@close="$emit('remove', tag)"
>
{{ tag.name }}
</el-tag>
<el-input
v-if="inputVisible"
ref="input"
v-model="inputValue"
size="small"
@blur="handleInputConfirm"
@keyup.enter="handleInputConfirm"
/>
<el-button
v-else
size="small"
@click="showInput"
>
+ 新标签
</el-button>
</div>
</template>
<script>
export default {
props: {
tags: Array
},
data() {
return {
inputVisible: false,
inputValue: ''
}
},
methods: {
showInput() {
this.inputVisible = true
this.$nextTick(() => {
this.$refs.input.focus()
})
},
handleInputConfirm() {
if (this.inputValue) {
this.$emit('add', this.inputValue)
}
this.inputVisible = false
this.inputValue = ''
}
}
}
</script>
使用方式:
html复制<tag-manager
:tags="userTags"
@add="handleAddTag"
@remove="handleRemoveTag"
/>
这种封装让标签管理逻辑与业务逻辑解耦,可以在不同页面重复使用。
当标签数量很多时(比如超过100个),直接渲染可能会导致性能问题。我的优化方案是:
html复制<el-scrollbar>
<virtual-list :size="40" :remain="8">
<el-tag
v-for="tag in largeTagList"
:key="tag.id"
style="margin: 5px;"
>
{{ tag.name }}
</el-tag>
</virtual-list>
</el-scrollbar>
javascript复制async loadTags(page = 1) {
const res = await axios.get('/api/tags', {
params: { page, pageSize: 20 }
})
this.tags = [...this.tags, ...res.data]
this.page = page
}
在实际开发中,我遇到过几个典型问题:
javascript复制addTag(newTag) {
if (this.tags.some(tag => tag.name === newTag)) {
this.$message.warning('标签已存在')
return false
}
// 添加逻辑...
}
css复制.el-tag {
max-width: 150px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
javascript复制function sanitizeTagName(name) {
return name.replace(/[^\w\u4e00-\u9fa5]/g, '').trim()
}
虽然el-tag提供了多种主题,但有时需要自定义样式。我的常用方法:
css复制/* 自定义主题色 */
.el-tag.custom-tag {
background-color: #f0f9eb;
border-color: #e1f3d8;
color: #67c23a;
}
css复制.el-tag {
--tag-bg-color: #f0f9eb;
--tag-border-color: #e1f3d8;
--tag-text-color: #67c23a;
background-color: var(--tag-bg-color);
border-color: var(--tag-border-color);
color: var(--tag-text-color);
}
html复制<el-tag
:style="{
backgroundColor: tag.bgColor,
borderColor: tag.borderColor,
color: tag.textColor
}"
>
{{ tag.name }}
</el-tag>
el-tag经常需要和其他Element组件配合使用,这里分享几个典型场景:
在表格中显示标签列表:
html复制<el-table :data="items">
<el-table-column prop="name" label="名称"></el-table-column>
<el-table-column prop="tags" label="标签">
<template #default="{row}">
<el-tag
v-for="tag in row.tags"
:key="tag"
size="small"
style="margin-right: 5px;"
>
{{ tag }}
</el-tag>
</template>
</el-table-column>
</el-table>
在表单中使用标签输入:
html复制<el-form-item label="兴趣标签">
<tag-manager v-model="form.tags"></tag-manager>
</el-form-item>
在弹窗中管理标签:
html复制<el-dialog title="标签管理" :visible.sync="dialogVisible">
<tag-manager :tags="tempTags" @add="handleAdd" @remove="handleRemove"></tag-manager>
<span slot="footer">
<el-button @click="dialogVisible = false">取消</el-button>
<el-button type="primary" @click="saveTags">保存</el-button>
</span>
</el-dialog>
在移动端使用el-tag时,我总结了几个注意事项:
css复制@media screen and (max-width: 768px) {
.el-tag {
padding: 0 8px;
height: 24px;
line-height: 22px;
font-size: 12px;
}
}
html复制<el-tag
@touchstart="handleTouchStart"
@touchend="handleTouchEnd"
>
可点击标签
</el-tag>
html复制<div class="tags-container">
<el-tag
v-for="tag in tags"
:key="tag.id"
class="tag-item"
>
{{ tag.name }}
</el-tag>
</div>
css复制.tags-container {
display: flex;
flex-wrap: wrap;
gap: 8px;
}
.tag-item {
flex: 1 0 auto;
max-width: calc(50% - 4px);
}
对于标签组件,我通常会编写以下测试用例:
javascript复制describe('TagManager', () => {
it('应该渲染标签列表', () => {
const wrapper = mount(TagManager, {
propsData: {
tags: [{ id: 1, name: 'test' }]
}
})
expect(wrapper.find('.el-tag').text()).toBe('test')
})
it('点击删除按钮应该触发remove事件', async () => {
const wrapper = mount(TagManager, {
propsData: {
tags: [{ id: 1, name: 'test' }]
}
})
await wrapper.find('.el-tag__close').trigger('click')
expect(wrapper.emitted('remove')).toBeTruthy()
})
})
经过多个项目的实践,我总结了以下el-tag使用的最佳实践:
html复制<!-- 最佳实践示例 -->
<div class="tag-system">
<div class="main-tags">
<el-tag
v-for="tag in mainTags"
:key="tag.id"
type="primary"
size="medium"
closable
@close="confirmRemove(tag)"
>
{{ tag.name }}
</el-tag>
</div>
<el-collapse>
<el-collapse-item title="更多标签">
<el-tag
v-for="tag in secondaryTags"
:key="tag.id"
type="info"
size="small"
>
{{ tag.name }}
</el-tag>
</el-collapse-item>
</el-collapse>
</div>
除了传统的标签管理,el-tag还可以用在一些创新场景:
javascript复制computed: {
weightedTags() {
return this.tags.map(tag => {
const size = Math.min(24, 14 + tag.count * 2)
return { ...tag, size }
})
}
}
html复制<el-tag
v-for="tag in weightedTags"
:key="tag.id"
:style="{ fontSize: `${tag.size}px` }"
>
{{ tag.name }}
</el-tag>
html复制<el-tag
v-for="step in steps"
:key="step.id"
:type="step.active ? 'success' : ''"
@click="setActive(step)"
>
{{ step.name }}
</el-tag>
html复制<div class="tag-nav">
<el-tag
v-for="item in menu"
:key="item.path"
:type="$route.path === item.path ? 'primary' : ''"
@click="$router.push(item.path)"
>
{{ item.title }}
</el-tag>
</div>
html复制<el-tag
v-for="color in colors"
:key="color"
:color="color"
@click="selectColor(color)"
>
{{ color }}
</el-tag>
这些创新用法能让界面更加生动有趣,提升用户体验。在实际项目中,我经常根据具体需求灵活运用el-tag,发挥它的最大价值。