Skip to content

Commit 574d0fc

Browse files
committed
feat(tag): 新增标签组件及示例
1 parent f651678 commit 574d0fc

12 files changed

Lines changed: 270 additions & 0 deletions

File tree

‎apps/example/src/router/modules/component.example.ts‎

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -294,6 +294,14 @@ const routes: RouteRecordRaw = {
294294
title: '开关',
295295
},
296296
},
297+
{
298+
path: 'tag',
299+
name: 'componentExampleTag',
300+
component: () => import('@/views/component_example/tag.vue'),
301+
meta: {
302+
title: '标签',
303+
},
304+
},
297305
{
298306
path: 'tabs',
299307
name: 'componentExampleTabs',
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<script setup lang="ts">
2+
import { tag } from '@fantastic-admin/components/examples'
3+
</script>
4+
5+
<template>
6+
<div>
7+
<FaPageHeader title="标签" description="FaTag" />
8+
<FaPageMain
9+
v-for="example in tag"
10+
:key="example.title"
11+
:code="example.componentRaw"
12+
:title="example.title"
13+
>
14+
<component :is="example.component" />
15+
</FaPageMain>
16+
</div>
17+
</template>
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
# FaTag 标签
2+
3+
基础标签组件,用于标记和分类信息,支持多种样式变体和可关闭功能。
4+
5+
## 使用场景
6+
7+
- 状态标记(成功、失败、进行中)
8+
- 分类标签
9+
- 筛选条件标签
10+
- 展示多个属性或特征
11+
- 可删除的标签列表
12+
13+
## Props
14+
15+
| 属性 | 类型 | 默认值 | 说明 |
16+
|------|------|--------|------|
17+
| `variant` | `'default' \| 'destructive' \| 'outline' \| 'secondary'` | `'default'` | 标签样式变体 |
18+
| `icon` | `string` | - | 图标名称,使用 FaIcon 渲染 |
19+
| `closable` | `boolean` | `false` | 是否显示关闭按钮 |
20+
| `class` | `HTMLAttributes['class']` | - | 自定义 CSS 类 |
21+
22+
## Events
23+
24+
| 事件名 | 参数 | 说明 |
25+
|--------|------|------|
26+
| `close` | `event: MouseEvent` | 点击关闭按钮时触发 |
27+
28+
## Slots
29+
30+
| 名称 | 说明 |
31+
|------|------|
32+
| `default` | 标签内容 |
33+
34+
## 注意事项
35+
36+
1. **变体选择**
37+
- `default`:主色调标签,用于重要标记
38+
- `destructive`:危险/错误状态标签
39+
- `outline`:边框样式,用于次要标记
40+
- `secondary`:次级标签,用于一般分类
41+
2. **可关闭标签**:设置 `closable``true` 时,标签右侧会显示关闭按钮,点击触发 `close` 事件
42+
3. **关闭事件处理**:需要监听 `close` 事件来自定义关闭逻辑(如从列表中移除标签)
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<script setup lang="ts">
2+
// 组件实际使用时无需手动导入,框架会自动导入
3+
import FaTag from '../index.vue'
4+
</script>
5+
6+
<template>
7+
<div class="flex flex-wrap gap-4">
8+
<FaTag>
9+
默认标签
10+
</FaTag>
11+
<FaTag variant="destructive">
12+
危险标签
13+
</FaTag>
14+
<FaTag variant="outline">
15+
边框标签
16+
</FaTag>
17+
<FaTag variant="secondary">
18+
次要标签
19+
</FaTag>
20+
</div>
21+
</template>
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
<script setup lang="ts">
2+
// 组件实际使用时无需手动导入,框架会自动导入
3+
import { ref } from 'vue'
4+
import FaTag from '../index.vue'
5+
6+
const tags = ref([
7+
{ id: 1, label: '标签一', variant: 'default' as const },
8+
{ id: 2, label: '标签二', variant: 'destructive' as const },
9+
{ id: 3, label: '标签三', variant: 'outline' as const },
10+
{ id: 4, label: '标签四', variant: 'secondary' as const },
11+
])
12+
13+
function handleClose(id: number) {
14+
tags.value = tags.value.filter(tag => tag.id !== id)
15+
}
16+
</script>
17+
18+
<template>
19+
<div class="flex flex-wrap gap-4">
20+
<FaTag
21+
v-for="tag in tags"
22+
:key="tag.id"
23+
:variant="tag.variant"
24+
closable
25+
@close="handleClose(tag.id)"
26+
>
27+
{{ tag.label }}
28+
</FaTag>
29+
</div>
30+
</template>
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<script setup lang="ts">
2+
// 组件实际使用时无需手动导入,框架会自动导入
3+
import FaTag from '../index.vue'
4+
</script>
5+
6+
<template>
7+
<div class="flex flex-wrap gap-4">
8+
<FaTag icon="i-lucide:check">
9+
成功
10+
</FaTag>
11+
<FaTag icon="i-lucide:alert-triangle" variant="destructive">
12+
警告
13+
</FaTag>
14+
<FaTag icon="i-lucide:info" variant="outline">
15+
信息
16+
</FaTag>
17+
<FaTag icon="i-lucide:star" variant="secondary">
18+
收藏
19+
</FaTag>
20+
</div>
21+
</template>
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import Basic from './_basic.vue'
2+
import BasicRaw from './_basic.vue?raw'
3+
import Closable from './_closable.vue'
4+
import ClosableRaw from './_closable.vue?raw'
5+
import Icon from './_icon.vue'
6+
import IconRaw from './_icon.vue?raw'
7+
8+
export default [
9+
{
10+
title: '基础',
11+
component: Basic,
12+
componentRaw: BasicRaw,
13+
},
14+
{
15+
title: '图标',
16+
component: Icon,
17+
componentRaw: IconRaw,
18+
},
19+
{
20+
title: '可关闭',
21+
component: Closable,
22+
componentRaw: ClosableRaw,
23+
},
24+
]
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
<script setup lang="ts">
2+
import type { HTMLAttributes } from 'vue'
3+
import type { TagVariants } from './tag'
4+
import { Tag } from './tag'
5+
6+
defineOptions({
7+
name: 'BuiltInTag',
8+
})
9+
10+
const props = defineProps<{
11+
variant?: TagVariants['variant']
12+
class?: HTMLAttributes['class']
13+
icon?: string
14+
closable?: boolean
15+
}>()
16+
17+
const emit = defineEmits<{
18+
close: [event: MouseEvent]
19+
}>()
20+
21+
function handleClose(event: MouseEvent) {
22+
emit('close', event)
23+
}
24+
</script>
25+
26+
<template>
27+
<Tag :variant :icon :closable :class="props.class" @close="handleClose">
28+
<slot />
29+
</Tag>
30+
</template>
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
<script setup lang="ts">
2+
import type { PrimitiveProps } from 'reka-ui'
3+
import type { HTMLAttributes } from 'vue'
4+
import type { TagVariants } from '.'
5+
import { Primitive } from 'reka-ui'
6+
import { cn } from '#utils'
7+
import { tagVariants } from '.'
8+
import FaIcon from '../../icon/index.vue'
9+
10+
interface Props extends PrimitiveProps {
11+
variant?: TagVariants['variant']
12+
icon?: string
13+
closable?: boolean
14+
class?: HTMLAttributes['class']
15+
}
16+
17+
const props = withDefaults(defineProps<Props>(), {
18+
as: 'span',
19+
})
20+
21+
const emit = defineEmits<{
22+
close: [event: MouseEvent]
23+
}>()
24+
25+
function handleClose(event: MouseEvent) {
26+
emit('close', event)
27+
}
28+
</script>
29+
30+
<template>
31+
<Primitive
32+
data-slot="tag"
33+
:as="as"
34+
:as-child="asChild"
35+
:class="cn(tagVariants({ variant }), props.class)"
36+
>
37+
<FaIcon v-if="icon" :name="icon" class="size-3" />
38+
<slot />
39+
<button
40+
v-if="closable"
41+
type="button"
42+
class="me--1 outline-none rounded-full inline-flex ring-offset-background items-center justify-center focus:ring-2 focus:ring-ring focus:ring-offset-2"
43+
@click="handleClose"
44+
>
45+
<FaIcon name="i-lucide:x" class="size-3" />
46+
</button>
47+
</Primitive>
48+
</template>
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import type { VariantProps } from 'class-variance-authority'
2+
import { cva } from 'class-variance-authority'
3+
4+
export { default as Tag } from './Tag.vue'
5+
6+
export const tagVariants = cva(
7+
'inline-flex items-center gap-1 rounded-md border px-2.5 py-0.5 text-xs font-semibold transition-colors',
8+
{
9+
variants: {
10+
variant: {
11+
default:
12+
'border-transparent bg-primary text-primary-foreground',
13+
destructive:
14+
'border-transparent bg-destructive text-white dark:bg-destructive/60',
15+
outline:
16+
'border bg-background text-foreground dark:bg-input/30 dark:border-input',
17+
secondary:
18+
'border-transparent bg-secondary text-secondary-foreground',
19+
},
20+
},
21+
defaultVariants: {
22+
variant: 'default',
23+
},
24+
},
25+
)
26+
27+
export type TagVariants = VariantProps<typeof tagVariants>

0 commit comments

Comments
 (0)