Vue3标签(Tag)

简介: 这是一个Vue组件`Tag.vue`,提供了多样化的标签展示功能。支持设置标签颜色、尺寸、图标、边框等样式,并可实现标签的动态添加与删除。

效果如下图:在线预览

Tag

参数 说明 类型 默认值
closable 标签是否可以关闭 boolean false
color 标签颜色,预置多种常用颜色:'success', 'processing', 'error', 'warning', 'pink', 'red', 'yellow', 'orange', 'cyan', 'green', 'blue', 'purple', 'geekblue', 'magenta', 'volcano', 'gold', 'lime' string undefined
icon 设置图标 string | slot undefined
size 标签尺寸 ‘small’ | ‘middle’ | ‘large’ ‘middle’
bordered 是否有边框 boolean true
dynamic 是否启用标签动态添加和删除 boolean false
value v-model 动态标签数组,仅当 dynamic: true 时生效 string[] | Tag[] []
spaceProps Space 组件属性配置,参考 Space Props ,仅当 dynamic: true 时生效 object {}

Tag Type

名称 说明 类型 默认值
label? 标签文本名 string | slot undefined
closable? 标签是否可以关闭 boolean true
color? 标签颜色 string undefined
icon? 设置图标 string | slot undefined
size? 标签尺寸 ‘small’ | ‘middle’ | ‘large’ ‘middle’
bordered? 是否有边框 boolean true

Events

名称 说明 类型
close 关闭时的回调 (e: Event) => void
dynamicClose 启用标签动态添加和删除时关闭的回调 (tag: Tag, index: number) => void

创建标签组件Tag.vue

<script setup lang="ts">
import { ref, computed, nextTick, watchEffect } from 'vue'
import Space from '../space'
import { useSlotsExist } from '../utils'
interface Tag {
  label?: string // 标签文本名 string | slot
  closable?: boolean // 标签是否可以关闭,默认 true
  color?: string // 标签颜色
  icon?: string // 设置图标 string | slot
  size?: 'small' | 'middle' | 'large' // 标签尺寸
  bordered?: boolean // 是否有边框,默认 true
}
interface Props {
  closable?: boolean // 标签是否可以关闭
  color?: string // 标签颜色
  icon?: string // 设置图标 string | slot
  size?: 'small' | 'middle' | 'large' // 标签尺寸
  bordered?: boolean // 是否有边框
  dynamic?: boolean // 是否启用标签动态添加和删除
  value?: string[] | Tag[] // 动态标签数组,仅当 dynamic: true 时生效
  spaceProps?: object // Space 组件属性配置,仅当 dynamic: true 时生效
}
const props = withDefaults(defineProps<Props>(), {
  closable: false,
  color: undefined,
  icon: undefined,
  size: 'middle',
  bordered: true,
  dynamic: false,
  value: () => [],
  spaceProps: () => ({})
})
const isStrArray = computed(() => {
  if (props.dynamic) {
    if (props.value.length) {
      if (typeof props.value[0] === 'string') {
        return true
      }
      if (typeof props.value[0] === 'object') {
        return false
      }
    }
  }
  return null
})
const tags = computed(() => {
  if (props.dynamic) {
    if (props.value.length) {
      if (isStrArray.value) {
        return props.value.map((tag: any) => {
          return {
            label: tag,
            closable: true
          }
        })
      } else {
        return props.value.map((tag: any) => {
          return {
            closable: true,
            ...tag
          }
        })
      }
    }
  }
  return []
})
const slotsExist = useSlotsExist(['icon'])
const showIcon = computed(() => {
  if (!props.dynamic) {
    return slotsExist.icon || props.icon
  }
  return false
})
const inputRef = ref()
const showInput = ref(false)
const inputValue = ref('')
const presetColor = [
  'success',
  'processing',
  'error',
  'warning',
  'default',
  'pink',
  'red',
  'yellow',
  'orange',
  'cyan',
  'green',
  'blue',
  'purple',
  'geekblue',
  'magenta',
  'volcano',
  'gold',
  'lime'
]
const hidden = ref(false)
const tagsIconRef = ref()
const showTagsIcon = ref(Array(props.value.length).fill(1))
watchEffect(() => {
  if (props.dynamic) {
    const len = props.value.length
    showTagsIcon.value = Array(len).fill(1)
    nextTick(() => {
      if (tagsIconRef.value) {
        for (let n = 0; n < len; n++) {
          showTagsIcon.value[n] = tagsIconRef.value[n].offsetWidth
        }
      }
    })
  }
})
const emits = defineEmits(['update:value', 'close', 'dynamicClose'])
function onClose(e: MouseEvent) {
  hidden.value = true
  emits('close', e)
}
function onCloseTags(tag: Tag, n: number) {
  const newValue = (props.value as any[]).filter((tag: any, index: number) => {
    return index !== n
  })
  emits('update:value', newValue)
  emits('dynamicClose', tag, n)
}
function onAdd() {
  showInput.value = true
  nextTick(() => {
    inputRef.value.focus()
  })
}
function onChange() {
  if (isStrArray.value) {
    emits('update:value', [...props.value, inputValue.value])
  } else {
    emits('update:value', [
      ...props.value,
      {
        label: inputValue.value
      }
    ])
  }
  showInput.value = false
  inputRef.value = ''
}
function onKeyboard(e: KeyboardEvent) {
  if (e.key === 'Enter') {
    inputRef.value.blur()
  }
}
</script>
<template>
  <div
    v-if="!dynamic"
    class="m-tag"
    :class="[
      `tag-${size}`,
      color && presetColor.includes(color) ? `tag-${color}` : '',
      {
        'tag-borderless': !bordered,
        'tag-has-color': color && !presetColor.includes(color),
        'tag-hidden': hidden
      }
    ]"
    :style="`background-color: ${color && !presetColor.includes(color) ? color : ''};`"
  >
    <span v-if="showIcon" class="tag-icon">
      <slot name="icon">{
  { icon }}</slot>
    </span>
    <span class="tag-content">
      <slot></slot>
    </span>
    <span v-if="closable" class="tag-close" @click="onClose">
      <svg
        focusable="false"
        class="close-svg"
        data-icon="close"
        width="1em"
        height="1em"
        fill="currentColor"
        aria-hidden="true"
        viewBox="64 64 896 896"
      >
        <path
          d="M563.8 512l262.5-312.9c4.4-5.2.7-13.1-6.1-13.1h-79.8c-4.7 0-9.2 2.1-12.3 5.7L511.6 449.8 295.1 191.7c-3-3.6-7.5-5.7-12.3-5.7H203c-6.8 0-10.5 7.9-6.1 13.1L459.4 512 196.9 824.9A7.95 7.95 0 00203 838h79.8c4.7 0 9.2-2.1 12.3-5.7l216.5-258.1 216.5 258.1c3 3.6 7.5 5.7 12.3 5.7h79.8c6.8 0 10.5-7.9 6.1-13.1L563.8 512z"
        ></path>
      </svg>
    </span>
  </div>
  <Space v-else gap="small" v-bind="spaceProps">
    <div
      class="m-tag"
      :class="[
        `tag-${tag.size || size}`,
        (tag.color || color) && presetColor.includes(tag.color || color) ? `tag-${tag.color || color}` : '',
        {
          'tag-borderless': tag.bordered !== undefined && !tag.bordered,
          'tag-has-color': (tag.color || color) && !presetColor.includes(tag.color || color)
        }
      ]"
      :style="`background-color: ${(tag.color || color) && !presetColor.includes(tag.color || color) ? tag.color || color : ''};`"
      v-for="(tag, index) in tags"
      :key="index"
    >
      <span  v-if="showTagsIcon[index]" ref="tagsIconRef" class="tag-icon">
        <slot name="icon" :index="index">{
  { tag.icon }}</slot>
      </span>
      <span class="tag-content">
        <slot :label="tag.label" :index="index">{
  { tag.label }}</slot>
      </span>
      <span v-if="tag.closable || closable" class="tag-close" @click="onCloseTags(tag, index)">
        <svg
          focusable="false"
          class="close-svg"
          data-icon="close"
          width="1em"
          height="1em"
          fill="currentColor"
          aria-hidden="true"
          viewBox="64 64 896 896"
        >
          <path
            d="M563.8 512l262.5-312.9c4.4-5.2.7-13.1-6.1-13.1h-79.8c-4.7 0-9.2 2.1-12.3 5.7L511.6 449.8 295.1 191.7c-3-3.6-7.5-5.7-12.3-5.7H203c-6.8 0-10.5 7.9-6.1 13.1L459.4 512 196.9 824.9A7.95 7.95 0 00203 838h79.8c4.7 0 9.2-2.1 12.3-5.7l216.5-258.1 216.5 258.1c3 3.6 7.5 5.7 12.3 5.7h79.8c6.8 0 10.5-7.9 6.1-13.1L563.8 512z"
          ></path>
        </svg>
      </span>
    </div>
    <div
      v-if="!showInput"
      class="m-tag"
      :class="[`tag-${size}`, { 'tag-plus': dynamic }]"
      @click="onAdd">
      <svg
        focusable="false"
        class="plus-svg"
        data-icon="plus"
        width="1em"
        height="1em"
        fill="currentColor"
        aria-hidden="true"
        viewBox="64 64 896 896"
      >
        <path d="M482 152h60q8 0 8 8v704q0 8-8 8h-60q-8 0-8-8V160q0-8 8-8z"></path>
        <path d="M176 474h672q8 0 8 8v60q0 8-8 8H176q-8 0-8-8v-60q0-8 8-8z"></path>
      </svg>
    </div>
    <input
      v-if="showInput"
      ref="inputRef"
      class="tag-input"
      :class="`input-${size}`"
      type="text"
      v-model="inputValue"
      @blur="showInput = false"
      @change="onChange"
      @keydown="onKeyboard"
    />
  </Space>
</template>
<style lang="less" scoped>
.m-tag {
  display: inline-flex;
  align-items: center;
  height: 24px;
  font-size: 14px;
  color: rgba(0, 0, 0, 0.88);
  padding-inline: 7px;
  white-space: nowrap;
  background: rgba(0, 0, 0, 0.02);
  border: 1px solid #d9d9d9;
  border-radius: 6px;
  transition: all 0.2s;
  text-align: start;
  .tag-icon {
    margin-right: 5px;
    height: 100%;
    display: inline-flex;
    align-items: center;
  }
  .tag-content {
    height: 100%;
    display: inline-flex;
    align-items: center;
  }
  .plus-svg {
    display: inline-flex;
    align-items: center;
    width: 14px;
    height: 14px;
    fill: rgba(0, 0, 0, 0.88);
    font-style: normal;
    line-height: 0;
    text-align: center;
    vertical-align: -0.175em;
    transition: fill 0.2s;
  }
  .tag-close {
    margin-inline-start: 3px;
    font-size: 12px;
    display: inline-flex;
    align-items: center;
    height: 100%;
    vertical-align: top;
    font-style: normal;
    line-height: 0;
    text-align: center;
    cursor: pointer;
    .close-svg {
      display: inline-block;
      width: 1em;
      height: 1em;
      fill: rgba(0, 0, 0, 0.45);
      transition: all 0.2s;
      &:hover {
        fill: rgba(0, 0, 0, 0.88);
      }
    }
  }
}
.tag-small {
  height: 22px;
  font-size: 12px;
  line-height: 20px;
  border-radius: 4px;
  .plus-svg {
    width: 12px;
    height: 12px;
  }
  .tag-close {
    font-size: 10px;
  }
}
.tag-large {
  height: 28px;
  line-height: 26px;
  .tag-close {
    font-size: 14px;
    vertical-align: -0.16em;
  }
}
.tag-plus {
  background: rgb(255, 255, 255);
  border-style: dashed;
  padding-inline: 10px;
  text-align: center;
  cursor: pointer;
  &:hover {
    border-color: @themeColor;
    .plus-svg {
      fill: @themeColor;
    }
  }
}
.tag-input {
  width: 86px;
  color: rgba(0, 0, 0, 0.88);
  height: 24px;
  font-size: 14px;
  line-height: 22px;
  padding: 0 8px;
  position: relative;
  display: inline-block;
  min-width: 0;
  background-color: #ffffff;
  border: 1px solid #d9d9d9;
  border-radius: 6px;
  outline: none;
  transition: all 0.2s;
  &:focus {
    border-color: #4096ff;
    box-shadow: 0 0 0 2px rgba(5, 145, 255, 0.1);
    border-inline-end-width: 1px;
    outline: 0;
  }
}
.input-small {
  width: 78px;
  height: 22px;
  font-size: 12px;
  line-height: 20px;
  padding: 0 6px;
  border-radius: 4px;
}
.input-large {
  width: 90px;
  height: 28px;
  line-height: 26px;
}
.tag-success {
  color: #52c41a;
  background: #f6ffed;
  border-color: #b7eb8f;
  :deep(svg) {
    fill: #52c41a;
  }
}
.tag-processing {
  color: @themeColor;
  background: #e6f4ff;
  border-color: #91caff;
  :deep(svg) {
    fill: @themeColor;
  }
}
.tag-error {
  color: #ff4d4f;
  background: #fff2f0;
  border-color: #ffccc7;
  :deep(svg) {
    fill: #ff4d4f;
  }
}
.tag-warning {
  color: #faad14;
  background: #fffbe6;
  border-color: #ffe58f;
  :deep(svg) {
    fill: #faad14;
  }
}
.tag-pink {
  color: #c41d7f;
  background: #fff0f6;
  border-color: #ffadd2;
  :deep(svg) {
    fill: #c41d7f;
  }
}
.tag-red {
  color: #cf1322;
  background: #fff1f0;
  border-color: #ffa39e;
  :deep(svg) {
    fill: #cf1322;
  }
}
.tag-yellow {
  color: #d4b106;
  background: #feffe6;
  border-color: #fffb8f;
  :deep(svg) {
    fill: #d4b106;
  }
}
.tag-orange {
  color: #d46b08;
  background: #fff7e6;
  border-color: #ffd591;
  :deep(svg) {
    fill: #d46b08;
  }
}
.tag-green {
  color: #389e0d;
  background: #f6ffed;
  border-color: #b7eb8f;
  :deep(svg) {
    fill: #389e0d;
  }
}
.tag-cyan {
  color: #08979c;
  background: #e6fffb;
  border-color: #87e8de;
  :deep(svg) {
    fill: #08979c;
  }
}
.tag-blue {
  color: #0958d9;
  background: #e6f4ff;
  border-color: #91caff;
  :deep(svg) {
    fill: #0958d9;
  }
}
.tag-purple {
  color: #531dab;
  background: #f9f0ff;
  border-color: #d3adf7;
  :deep(svg) {
    fill: #531dab;
  }
}
.tag-geekblue {
  color: #1d39c4;
  background: #f0f5ff;
  border-color: #adc6ff;
  :deep(svg) {
    fill: #1d39c4;
  }
}
.tag-magenta {
  color: #eb2f96;
  background: #fff0f6;
  border-color: #ffadd2;
  :deep(svg) {
    fill: #eb2f96;
  }
}
.tag-volcano {
  color: #d4380d;
  background: #fff2e8;
  border-color: #ffbb96;
  :deep(svg) {
    fill: #d4380d;
  }
}
.tag-gold {
  color: #d48806;
  background: #fffbe6;
  border-color: #ffe58f;
  :deep(svg) {
    fill: #d48806;
  }
}
.tag-lime {
  color: #7cb305;
  background: #fcffe6;
  border-color: #eaff8f;
  :deep(svg) {
    fill: #7cb305;
  }
}
.tag-borderless {
  border-color: transparent;
}
.tag-has-color {
  color: #fff;
  border-color: transparent;
  .tag-close .close-svg {
    fill: rgba(255, 255, 255, 0.85);
    &:hover {
      fill: rgba(255, 255, 255, 1);
    }
  }
}
.tag-hidden {
  display: none;
}
</style>

其中引入使用了以下组件:

<script setup lang="ts">
import Tag from './Tag.vue'
import { ref, watchEffect } from 'vue'

const strTags = ref(['天空', '大海', '湖泊'])
watchEffect(() => {
  console.log('strTags', strTags.value)
})
const objTags = ref([
  {
    label: '天空',
    color: 'processing'
  },
  {
    label: '大海',
    closable: false,
    color: 'error'
  },
  {
    label: '湖泊',
    color: 'pink'
  }
])
watchEffect(() => {
  console.log('objTags', objTags.value)
})
const onClose = (e: MouseEvent) => {
  console.log('e', e)
}
const onDynamicClose = (tag: any, index: number) => {
  console.log('tag', tag)
  console.log('index', index)
}
</script>
<template>
  <div>
    <h1>{
  { $route.name }} {
  { $route.meta.title }}</h1>
    <h2 class="mt30 mb10">基本使用</h2>
    <Space gap="small">
      <Tag color="magenta">pink</Tag>
      <Tag>Tag 1</Tag>
      <Tag><a href="https://blog.csdn.net/Dandrose">Link</a></Tag>
      <Tag closable @close="onClose">Tag 2</Tag>
    </Space>
    <h2 class="mt30 mb10">多彩标签</h2>
    <Space gap="small">
      <Tag color="pink">pink</Tag>
      <Tag color="red">red</Tag>
      <Tag color="yellow">yellow</Tag>
      <Tag color="orange">orange</Tag>
      <Tag color="cyan">cyan</Tag>
      <Tag color="green">green</Tag>
      <Tag color="blue" closable>blue</Tag>
      <Tag color="purple">purple</Tag>
      <Tag color="geekblue">geekblue</Tag>
      <Tag color="magenta">magenta</Tag>
      <Tag color="volcano">volcano</Tag>
      <Tag color="gold">gold</Tag>
      <Tag color="lime">lime</Tag>
    </Space>
    <br />
    <br />
    <Space gap="small">
      <Tag color="#f50">#f50</Tag>
      <Tag color="#2db7f5">#2db7f5</Tag>
      <Tag color="#87d068">#87d068</Tag>
      <Tag color="#108ee9">#108ee9</Tag>
    </Space>
    <h2 class="mt30 mb10">预设状态的标签</h2>
    <Divider orientation="left">Without icon</Divider>
    <Space gap="small">
      <Tag color="success">success</Tag>
      <Tag color="processing">processing</Tag>
      <Tag color="error">error</Tag>
      <Tag color="warning">warning</Tag>
      <Tag color="default">default</Tag>
    </Space>
    <Divider orientation="left">With icon</Divider>
    <Space gap="small">
      <Tag color="success">
        <template #icon>
          <svg
            focusable="false"
            class="u-svg"
            data-icon="check-circle"
            width="1em"
            height="1em"
            fill="currentColor"
            aria-hidden="true"
            viewBox="64 64 896 896"
          >
            <path
              d="M699 353h-46.9c-10.2 0-19.9 4.9-25.9 13.3L469 584.3l-71.2-98.8c-6-8.3-15.6-13.3-25.9-13.3H325c-6.5 0-10.3 7.4-6.5 12.7l124.6 172.8a31.8 31.8 0 0051.7 0l210.6-292c3.9-5.3.1-12.7-6.4-12.7z"
            ></path>
            <path
              d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm0 820c-205.4 0-372-166.6-372-372s166.6-372 372-372 372 166.6 372 372-166.6 372-372 372z"
            ></path>
          </svg>
        </template>
        success
      </Tag>
      <Tag color="processing">
        <template #icon>
          <svg
            focusable="false"
            class="u-spin"
            data-icon="sync"
            width="1em"
            height="1em"
            fill="currentColor"
            aria-hidden="true"
            viewBox="64 64 896 896"
          >
            <path
              d="M168 504.2c1-43.7 10-86.1 26.9-126 17.3-41 42.1-77.7 73.7-109.4S337 212.3 378 195c42.4-17.9 87.4-27 133.9-27s91.5 9.1 133.8 27A341.5 341.5 0 01755 268.8c9.9 9.9 19.2 20.4 27.8 31.4l-60.2 47a8 8 0 003 14.1l175.7 43c5 1.2 9.9-2.6 9.9-7.7l.8-180.9c0-6.7-7.7-10.5-12.9-6.3l-56.4 44.1C765.8 155.1 646.2 92 511.8 92 282.7 92 96.3 275.6 92 503.8a8 8 0 008 8.2h60c4.4 0 7.9-3.5 8-7.8zm756 7.8h-60c-4.4 0-7.9 3.5-8 7.8-1 43.7-10 86.1-26.9 126-17.3 41-42.1 77.8-73.7 109.4A342.45 342.45 0 01512.1 856a342.24 342.24 0 01-243.2-100.8c-9.9-9.9-19.2-20.4-27.8-31.4l60.2-47a8 8 0 00-3-14.1l-175.7-43c-5-1.2-9.9 2.6-9.9 7.7l-.7 181c0 6.7 7.7 10.5 12.9 6.3l56.4-44.1C258.2 868.9 377.8 932 512.2 932c229.2 0 415.5-183.7 419.8-411.8a8 8 0 00-8-8.2z"
            ></path>
          </svg>
        </template>
        processing
      </Tag>
      <Tag color="error">
        <template #icon>
          <svg
            focusable="false"
            class="u-svg"
            data-icon="close-circle"
            width="1em"
            height="1em"
            fill="currentColor"
            aria-hidden="true"
            viewBox="64 64 896 896"
          >
            <path
              d="M685.4 354.8c0-4.4-3.6-8-8-8l-66 .3L512 465.6l-99.3-118.4-66.1-.3c-4.4 0-8 3.5-8 8 0 1.9.7 3.7 1.9 5.2l130.1 155L340.5 670a8.32 8.32 0 00-1.9 5.2c0 4.4 3.6 8 8 8l66.1-.3L512 564.4l99.3 118.4 66 .3c4.4 0 8-3.5 8-8 0-1.9-.7-3.7-1.9-5.2L553.5 515l130.1-155c1.2-1.4 1.8-3.3 1.8-5.2z"
            ></path>
            <path
              d="M512 65C264.6 65 64 265.6 64 513s200.6 448 448 448 448-200.6 448-448S759.4 65 512 65zm0 820c-205.4 0-372-166.6-372-372s166.6-372 372-372 372 166.6 372 372-166.6 372-372 372z"
            ></path>
          </svg>
        </template>
        error
      </Tag>
      <Tag color="warning">
        <template #icon>
          <svg
            focusable="false"
            class="u-svg"
            data-icon="exclamation-circle"
            width="1em"
            height="1em"
            fill="currentColor"
            aria-hidden="true"
            viewBox="64 64 896 896"
          >
            <path
              d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm0 820c-205.4 0-372-166.6-372-372s166.6-372 372-372 372 166.6 372 372-166.6 372-372 372z"
            ></path>
            <path
              d="M464 688a48 48 0 1096 0 48 48 0 10-96 0zm24-112h48c4.4 0 8-3.6 8-8V296c0-4.4-3.6-8-8-8h-48c-4.4 0-8 3.6-8 8v272c0 4.4 3.6 8 8 8z"
            ></path>
          </svg>
        </template>
        warning
      </Tag>
      <Tag color="default">
        <template #icon>
          <svg
            focusable="false"
            class="u-svg"
            data-icon="clock-circle"
            width="1em"
            height="1em"
            fill="currentColor"
            aria-hidden="true"
            viewBox="64 64 896 896"
          >
            <path
              d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm0 820c-205.4 0-372-166.6-372-372s166.6-372 372-372 372 166.6 372 372-166.6 372-372 372z"
            ></path>
            <path
              d="M686.7 638.6L544.1 535.5V288c0-4.4-3.6-8-8-8H488c-4.4 0-8 3.6-8 8v275.4c0 2.6 1.2 5 3.3 6.5l165.4 120.6c3.6 2.6 8.6 1.8 11.2-1.7l28.6-39c2.6-3.7 1.8-8.7-1.8-11.2z"
            ></path>
          </svg>
        </template>
        waiting
      </Tag>
      <Tag color="default">
        <template #icon>
          <svg
            focusable="false"
            class="u-svg"
            data-icon="minus-circle"
            width="1em"
            height="1em"
            fill="currentColor"
            aria-hidden="true"
            viewBox="64 64 896 896"
          >
            <path d="M696 480H328c-4.4 0-8 3.6-8 8v48c0 4.4 3.6 8 8 8h368c4.4 0 8-3.6 8-8v-48c0-4.4-3.6-8-8-8z"></path>
            <path
              d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm0 820c-205.4 0-372-166.6-372-372s166.6-372 372-372 372 166.6 372 372-166.6 372-372 372z"
            ></path>
          </svg>
        </template>
        stop
      </Tag>
    </Space>
    <h2 class="mt30 mb10">动态添加和删除</h2>
    <h3 class="mb10">使用字符串格式数组</h3>
    <Tag dynamic v-model:value="strTags" @dynamic-close="onDynamicClose" />
    <br />
    <br />
    <h3 class="mb10">使用对象格式数组</h3>
    <Space>
      <Tag dynamic v-model:value="objTags">
        <template #default="{ label, index }">
          <template v-if="index === 1">{
  { label }} {
  { index }}</template>
        </template>
        <template #icon="{ index }">
          <template v-if="index === 0">
            <svg
              focusable="false"
              class="u-svg"
              data-icon="check-circle"
              width="1em"
              height="1em"
              fill="currentColor"
              aria-hidden="true"
              viewBox="64 64 896 896"
            >
              <path
                d="M699 353h-46.9c-10.2 0-19.9 4.9-25.9 13.3L469 584.3l-71.2-98.8c-6-8.3-15.6-13.3-25.9-13.3H325c-6.5 0-10.3 7.4-6.5 12.7l124.6 172.8a31.8 31.8 0 0051.7 0l210.6-292c3.9-5.3.1-12.7-6.4-12.7z"
              ></path>
              <path
                d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm0 820c-205.4 0-372-166.6-372-372s166.6-372 372-372 372 166.6 372 372-166.6 372-372 372z"
              ></path>
            </svg>
          </template>
        </template>
      </Tag>
    </Space>
    <h2 class="mt30 mb10">三种尺寸</h2>
    <Space gap="small">
      <Tag closable size="small" @close="onClose">爱在西元前</Tag>
      <Tag color="warning" closable @close="onClose">超人不会飞</Tag>
      <Tag color="blue" size="large" dynamic v-model:value="strTags" closable @close="onDynamicClose" />
    </Space>
    <h2 class="mt30 mb10">自定义动态标签排列</h2>
    <Tag
      closable
      dynamic
      color="blue"
      size="large"
      :space-props="{ vertical: true, gap: 12 }"
      v-model:value="strTags"
      @close="onDynamicClose"
    />
    <h2 class="mt30 mb10">无边框</h2>
    <Space gap="small">
      <Tag :bordered="false">Tag 1</Tag>
      <Tag :bordered="false">Tag 2</Tag>
      <Tag :bordered="false" closable>Tag 3</Tag>
      <Tag :bordered="false" closable>Tag 4</Tag>
    </Space>
    <Divider />
    <Space gap="small">
      <Tag :bordered="false" color="processing">processing</Tag>
      <Tag :bordered="false" color="success">success</Tag>
      <Tag :bordered="false" color="error">error</Tag>
      <Tag :bordered="false" color="warning">warning</Tag>
      <Tag :bordered="false" color="magenta">magenta</Tag>
      <Tag :bordered="false" color="red">red</Tag>
      <Tag :bordered="false" color="volcano">volcano</Tag>
      <Tag :bordered="false" color="orange">orange</Tag>
      <Tag :bordered="false" color="gold">gold</Tag>
      <Tag :bordered="false" color="lime">lime</Tag>
      <Tag :bordered="false" color="green">green</Tag>
      <Tag :bordered="false" color="cyan">cyan</Tag>
      <Tag :bordered="false" color="blue">blue</Tag>
      <Tag :bordered="false" color="geekblue">geekblue</Tag>
      <Tag :bordered="false" color="purple">purple</Tag>
    </Space>
  </div>
</template>
<style lang="less" scoped>
.u-svg {
  display: inline-block;
  line-height: 1;
}
.u-spin {
  width: 12px;
  height: 12px;
  display: inline-block;
  line-height: 1;
  -webkit-animation: loadingCircle 1s infinite linear;
  animation: loadingCircle 1s infinite linear;
  @keyframes loadingCircle {
    100% {
      -webkit-transform: rotate(360deg);
      transform: rotate(360deg);
    }
  }
}
</style>
相关文章
|
1天前
|
缓存 JavaScript UED
Vue3中v-model在处理自定义组件双向数据绑定时有哪些注意事项?
在使用`v-model`处理自定义组件双向数据绑定时,要仔细考虑各种因素,确保数据的准确传递和更新,同时提供良好的用户体验和代码可维护性。通过合理的设计和注意事项的遵循,能够更好地发挥`v-model`的优势,实现高效的双向数据绑定效果。
102 64
|
1天前
|
JavaScript 前端开发 API
Vue 3 中 v-model 与 Vue 2 中 v-model 的区别是什么?
总的来说,Vue 3 中的 `v-model` 在灵活性、与组合式 API 的结合、对自定义组件的支持等方面都有了明显的提升和改进,使其更适应现代前端开发的需求和趋势。但需要注意的是,在迁移过程中可能需要对一些代码进行调整和适配。
|
26天前
|
存储 JavaScript 前端开发
vue3的脚手架模板你真的了解吗?里面有很多值得我们学习的地方!
【10月更文挑战第21天】 vue3的脚手架模板你真的了解吗?里面有很多值得我们学习的地方!
vue3的脚手架模板你真的了解吗?里面有很多值得我们学习的地方!
|
29天前
|
API
vue3知识点:provide 与 inject
vue3知识点:provide 与 inject
33 4
vue3知识点:provide 与 inject
|
29天前
|
API
vue3知识点:readonly 与 shallowReadonly
vue3知识点:readonly 与 shallowReadonly
25 1
vue3知识点:readonly 与 shallowReadonly
|
23天前
|
JavaScript 前端开发 开发者
Vue 3中的Proxy
【10月更文挑战第23天】Vue 3中的`Proxy`为响应式系统带来了更强大、更灵活的功能,解决了Vue 2中响应式系统的一些局限性,同时在性能方面也有一定的提升,为开发者提供了更好的开发体验和性能保障。
50 7
|
24天前
|
前端开发 数据库
芋道框架审批流如何实现(Cloud+Vue3)
芋道框架审批流如何实现(Cloud+Vue3)
41 3
|
23天前
|
JavaScript 数据管理 Java
在 Vue 3 中使用 Proxy 实现数据双向绑定的性能如何?
【10月更文挑战第23天】Vue 3中使用Proxy实现数据双向绑定在多个方面都带来了性能的提升,从更高效的响应式追踪、更好的初始化性能、对数组操作的优化到更优的内存管理等,使得Vue 3在处理复杂的应用场景和大量数据时能够更加高效和稳定地运行。
39 1
|
23天前
|
JavaScript 开发者
在 Vue 3 中使用 Proxy 实现数据的双向绑定
【10月更文挑战第23天】Vue 3利用 `Proxy` 实现了数据的双向绑定,无论是使用内置的指令如 `v-model`,还是通过自定义事件或自定义指令,都能够方便地实现数据与视图之间的双向交互,满足不同场景下的开发需求。
44 1
|
25天前
|
前端开发 JavaScript
简记 Vue3(一)—— setup、ref、reactive、toRefs、toRef
简记 Vue3(一)—— setup、ref、reactive、toRefs、toRef
下一篇
无影云桌面