Skip to content

Tag 标签

概述

Tag 标签组件用于标记和选择,是一种轻量级的信息展示组件。Element Plus 的 Tag 组件支持多种类型、尺寸和交互方式,适用于分类标记、状态显示、标签输入等场景。

学习目标

  • 掌握 Tag 组件的基础用法
  • 理解不同类型和主题的标签
  • 学会使用可关闭和动态编辑标签
  • 掌握标签的事件处理
  • 了解标签的高级特性和应用场景

基础用法

基础标签

最简单的标签使用,通过 type 属性设置不同的主题:

vue
<template>
  <div class="tag-demo">
    <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>
  </div>
</template>

<style>
.tag-demo .el-tag {
  margin-right: 10px;
}
</style>

可移除标签

设置 closable 属性可以定义一个标签是否可移除:

vue
<template>
  <div class="tag-demo">
    <el-tag
      v-for="tag in tags"
      :key="tag.name"
      :type="tag.type"
      closable
      @close="handleClose(tag)"
    >
      {{ tag.name }}
    </el-tag>
  </div>
</template>

<script setup>
import { ref } from 'vue'

const tags = ref([
  { name: '标签一', type: '' },
  { name: '标签二', type: 'success' },
  { name: '标签三', type: 'info' },
  { name: '标签四', type: 'warning' },
  { name: '标签五', type: 'danger' }
])

const handleClose = (tag) => {
  const index = tags.value.indexOf(tag)
  if (index > -1) {
    tags.value.splice(index, 1)
  }
}
</script>

不同尺寸

通过 size 属性设置标签的尺寸:

vue
<template>
  <div class="tag-demo">
    <el-tag size="large">大型标签</el-tag>
    <el-tag>默认标签</el-tag>
    <el-tag size="small">小型标签</el-tag>
  </div>
</template>

标签主题

不同效果

Tag 组件提供了三种不同的主题:darklightplain

vue
<template>
  <div class="tag-demo">
    <h4>Dark</h4>
    <el-tag effect="dark">标签一</el-tag>
    <el-tag type="success" effect="dark">标签二</el-tag>
    <el-tag type="info" effect="dark">标签三</el-tag>
    <el-tag type="warning" effect="dark">标签四</el-tag>
    <el-tag type="danger" effect="dark">标签五</el-tag>
    
    <h4>Light</h4>
    <el-tag effect="light">标签一</el-tag>
    <el-tag type="success" effect="light">标签二</el-tag>
    <el-tag type="info" effect="light">标签三</el-tag>
    <el-tag type="warning" effect="light">标签四</el-tag>
    <el-tag type="danger" effect="light">标签五</el-tag>
    
    <h4>Plain</h4>
    <el-tag effect="plain">标签一</el-tag>
    <el-tag type="success" effect="plain">标签二</el-tag>
    <el-tag type="info" effect="plain">标签三</el-tag>
    <el-tag type="warning" effect="plain">标签四</el-tag>
    <el-tag type="danger" effect="plain">标签五</el-tag>
  </div>
</template>

圆形标签

通过 round 属性设置为圆形标签:

vue
<template>
  <div class="tag-demo">
    <el-tag round>标签一</el-tag>
    <el-tag type="success" round>标签二</el-tag>
    <el-tag type="info" round>标签三</el-tag>
    <el-tag type="warning" round>标签四</el-tag>
    <el-tag type="danger" round>标签五</el-tag>
  </div>
</template>

动态编辑标签

可编辑标签列表

结合输入框实现动态添加和删除标签:

vue
<template>
  <div class="tag-editor">
    <el-tag
      v-for="tag in dynamicTags"
      :key="tag"
      closable
      :disable-transitions="false"
      @close="handleClose(tag)"
    >
      {{ tag }}
    </el-tag>
    
    <el-input
      v-if="inputVisible"
      ref="InputRef"
      v-model="inputValue"
      class="input-new-tag"
      size="small"
      @keyup.enter="handleInputConfirm"
      @blur="handleInputConfirm"
    />
    
    <el-button v-else class="button-new-tag" size="small" @click="showInput">
      + 新标签
    </el-button>
  </div>
</template>

<script setup>
import { ref, nextTick } from 'vue'

const dynamicTags = ref(['标签一', '标签二', '标签三'])
const inputVisible = ref(false)
const inputValue = ref('')
const InputRef = ref()

const handleClose = (tag) => {
  dynamicTags.value.splice(dynamicTags.value.indexOf(tag), 1)
}

const showInput = () => {
  inputVisible.value = true
  nextTick(() => {
    InputRef.value.input.focus()
  })
}

const handleInputConfirm = () => {
  if (inputValue.value) {
    dynamicTags.value.push(inputValue.value)
  }
  inputVisible.value = false
  inputValue.value = ''
}
</script>

<style>
.tag-editor .el-tag {
  margin-right: 10px;
}

.tag-editor .button-new-tag {
  margin-left: 10px;
  height: 32px;
  line-height: 30px;
  padding-top: 0;
  padding-bottom: 0;
}

.tag-editor .input-new-tag {
  width: 90px;
  margin-left: 10px;
  vertical-align: bottom;
}
</style>

标签选择器

可选择标签

实现标签的选择和取消选择功能:

vue
<template>
  <div class="tag-selector">
    <h4>选择你感兴趣的技术:</h4>
    <el-tag
      v-for="tag in tags"
      :key="tag.id"
      :type="tag.selected ? 'primary' : 'info'"
      :effect="tag.selected ? 'dark' : 'plain'"
      class="selectable-tag"
      @click="toggleTag(tag)"
    >
      {{ tag.name }}
      <el-icon v-if="tag.selected" class="selected-icon">
        <Check />
      </el-icon>
    </el-tag>
    
    <div class="selected-result">
      <h4>已选择:</h4>
      <el-tag
        v-for="tag in selectedTags"
        :key="tag.id"
        type="success"
        closable
        @close="toggleTag(tag)"
      >
        {{ tag.name }}
      </el-tag>
    </div>
  </div>
</template>

<script setup>
import { ref, computed } from 'vue'
import { Check } from '@element-plus/icons-vue'

const tags = ref([
  { id: 1, name: 'Vue.js', selected: false },
  { id: 2, name: 'React', selected: false },
  { id: 3, name: 'Angular', selected: false },
  { id: 4, name: 'Node.js', selected: true },
  { id: 5, name: 'TypeScript', selected: false },
  { id: 6, name: 'JavaScript', selected: true },
  { id: 7, name: 'Python', selected: false },
  { id: 8, name: 'Java', selected: false }
])

const selectedTags = computed(() => {
  return tags.value.filter(tag => tag.selected)
})

const toggleTag = (tag) => {
  tag.selected = !tag.selected
}
</script>

<style>
.tag-selector .selectable-tag {
  margin: 5px;
  cursor: pointer;
  transition: all 0.3s;
  position: relative;
}

.tag-selector .selectable-tag:hover {
  transform: scale(1.05);
}

.tag-selector .selected-icon {
  margin-left: 5px;
}

.tag-selector .selected-result {
  margin-top: 20px;
  padding: 15px;
  background-color: #f5f7fa;
  border-radius: 4px;
}

.tag-selector .selected-result .el-tag {
  margin: 3px;
}
</style>

实际应用示例

文章标签管理系统

一个完整的文章标签管理示例:

vue
<template>
  <div class="article-tag-manager">
    <div class="article-info">
      <h3>{{ article.title }}</h3>
      <p>{{ article.summary }}</p>
    </div>
    
    <!-- 当前文章标签 -->
    <div class="current-tags">
      <h4>当前标签:</h4>
      <el-tag
        v-for="tag in article.tags"
        :key="tag.id"
        :type="getTagType(tag.category)"
        closable
        @close="removeTag(tag)"
      >
        {{ tag.name }}
      </el-tag>
      
      <el-button 
        v-if="!showAddTag" 
        size="small" 
        @click="showAddTag = true"
      >
        + 添加标签
      </el-button>
    </div>
    
    <!-- 添加标签区域 -->
    <div v-if="showAddTag" class="add-tag-section">
      <h4>添加标签:</h4>
      
      <!-- 标签分类 -->
      <div class="tag-categories">
        <el-tag
          v-for="category in tagCategories"
          :key="category.id"
          :type="selectedCategory?.id === category.id ? 'primary' : 'info'"
          :effect="selectedCategory?.id === category.id ? 'dark' : 'plain'"
          class="category-tag"
          @click="selectCategory(category)"
        >
          {{ category.name }}
        </el-tag>
      </div>
      
      <!-- 可选标签 -->
      <div v-if="selectedCategory" class="available-tags">
        <h5>{{ selectedCategory.name }} 标签:</h5>
        <el-tag
          v-for="tag in availableTags"
          :key="tag.id"
          :type="getTagType(selectedCategory.id)"
          effect="plain"
          class="available-tag"
          @click="addTag(tag)"
        >
          {{ tag.name }}
        </el-tag>
      </div>
      
      <!-- 自定义标签输入 -->
      <div class="custom-tag-input">
        <h5>自定义标签:</h5>
        <el-input
          v-model="customTagName"
          placeholder="输入自定义标签名称"
          style="width: 200px; margin-right: 10px"
          @keyup.enter="addCustomTag"
        />
        <el-button type="primary" @click="addCustomTag">添加</el-button>
      </div>
      
      <div class="add-tag-actions">
        <el-button @click="showAddTag = false">取消</el-button>
        <el-button type="primary" @click="saveTags">保存</el-button>
      </div>
    </div>
    
    <!-- 标签统计 -->
    <div class="tag-statistics">
      <h4>标签统计:</h4>
      <div class="stats-grid">
        <div class="stat-item">
          <span class="stat-label">总标签数:</span>
          <el-tag type="info">{{ article.tags.length }}</el-tag>
        </div>
        <div class="stat-item">
          <span class="stat-label">技术标签:</span>
          <el-tag type="primary">{{ getTechTagCount() }}</el-tag>
        </div>
        <div class="stat-item">
          <span class="stat-label">分类标签:</span>
          <el-tag type="success">{{ getCategoryTagCount() }}</el-tag>
        </div>
        <div class="stat-item">
          <span class="stat-label">自定义标签:</span>
          <el-tag type="warning">{{ getCustomTagCount() }}</el-tag>
        </div>
      </div>
    </div>
  </div>
</template>

<script setup>
import { ref, computed } from 'vue'
import { ElMessage } from 'element-plus'

const showAddTag = ref(false)
const selectedCategory = ref(null)
const customTagName = ref('')

const article = ref({
  id: 1,
  title: 'Vue 3 组合式 API 深入解析',
  summary: '本文详细介绍了 Vue 3 组合式 API 的使用方法和最佳实践...',
  tags: [
    { id: 1, name: 'Vue.js', category: 1 },
    { id: 2, name: '前端开发', category: 2 },
    { id: 3, name: 'JavaScript', category: 1 }
  ]
})

const tagCategories = ref([
  { id: 1, name: '技术栈', type: 'primary' },
  { id: 2, name: '分类', type: 'success' },
  { id: 3, name: '难度', type: 'warning' },
  { id: 4, name: '自定义', type: 'info' }
])

const allTags = ref([
  { id: 1, name: 'Vue.js', category: 1 },
  { id: 2, name: 'React', category: 1 },
  { id: 3, name: 'Angular', category: 1 },
  { id: 4, name: 'JavaScript', category: 1 },
  { id: 5, name: 'TypeScript', category: 1 },
  { id: 6, name: '前端开发', category: 2 },
  { id: 7, name: '后端开发', category: 2 },
  { id: 8, name: '全栈开发', category: 2 },
  { id: 9, name: '初级', category: 3 },
  { id: 10, name: '中级', category: 3 },
  { id: 11, name: '高级', category: 3 }
])

const availableTags = computed(() => {
  if (!selectedCategory.value) return []
  
  return allTags.value.filter(tag => 
    tag.category === selectedCategory.value.id &&
    !article.value.tags.some(articleTag => articleTag.id === tag.id)
  )
})

const getTagType = (categoryId) => {
  const category = tagCategories.value.find(cat => cat.id === categoryId)
  return category ? category.type : 'info'
}

const selectCategory = (category) => {
  selectedCategory.value = category
}

const addTag = (tag) => {
  if (!article.value.tags.some(t => t.id === tag.id)) {
    article.value.tags.push({ ...tag })
    ElMessage.success(`已添加标签:${tag.name}`)
  }
}

const addCustomTag = () => {
  if (!customTagName.value.trim()) {
    ElMessage.warning('请输入标签名称')
    return
  }
  
  const newTag = {
    id: Date.now(),
    name: customTagName.value.trim(),
    category: 4 // 自定义分类
  }
  
  article.value.tags.push(newTag)
  customTagName.value = ''
  ElMessage.success(`已添加自定义标签:${newTag.name}`)
}

const removeTag = (tag) => {
  const index = article.value.tags.findIndex(t => t.id === tag.id)
  if (index > -1) {
    article.value.tags.splice(index, 1)
    ElMessage.success(`已移除标签:${tag.name}`)
  }
}

const saveTags = () => {
  // 模拟保存到服务器
  ElMessage.success('标签保存成功')
  showAddTag.value = false
  selectedCategory.value = null
}

const getTechTagCount = () => {
  return article.value.tags.filter(tag => tag.category === 1).length
}

const getCategoryTagCount = () => {
  return article.value.tags.filter(tag => tag.category === 2).length
}

const getCustomTagCount = () => {
  return article.value.tags.filter(tag => tag.category === 4).length
}
</script>

<style scoped>
.article-tag-manager {
  max-width: 800px;
  margin: 0 auto;
  padding: 20px;
}

.article-info {
  margin-bottom: 30px;
  padding: 20px;
  background-color: #f8f9fa;
  border-radius: 8px;
}

.article-info h3 {
  margin: 0 0 10px 0;
  color: #303133;
}

.article-info p {
  margin: 0;
  color: #606266;
  line-height: 1.6;
}

.current-tags {
  margin-bottom: 30px;
}

.current-tags .el-tag {
  margin: 5px 5px 5px 0;
}

.add-tag-section {
  padding: 20px;
  background-color: #f5f7fa;
  border-radius: 8px;
  margin-bottom: 30px;
}

.tag-categories {
  margin-bottom: 20px;
}

.category-tag {
  margin: 5px;
  cursor: pointer;
  transition: all 0.3s;
}

.category-tag:hover {
  transform: scale(1.05);
}

.available-tags {
  margin-bottom: 20px;
}

.available-tag {
  margin: 5px;
  cursor: pointer;
  transition: all 0.3s;
}

.available-tag:hover {
  transform: scale(1.05);
}

.custom-tag-input {
  margin-bottom: 20px;
}

.add-tag-actions {
  text-align: right;
}

.tag-statistics {
  padding: 20px;
  background-color: #fafafa;
  border-radius: 8px;
}

.stats-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
  gap: 15px;
}

.stat-item {
  display: flex;
  align-items: center;
  justify-content: space-between;
}

.stat-label {
  font-weight: 500;
  color: #606266;
}
</style>

Element Plus Study Guide