Skip to content

组件使用规范

本指南介绍 BrutxUI 组件库的最佳使用方式,帮助你写出更健壮、可维护的代码。

1. 组件选择指南

1.1 基础交互组件

场景推荐组件说明
触发操作Button支持 variant/size/loading 状态
文本输入Input支持 clearable、密码切换、字数统计
多行文本Textarea自动扩展高度
数值输入NumberInput内置增减按钮和范围校验
单选RadioGroup语义化单选组
多选Checkbox支持 indeterminate 状态
开关切换Switch布尔值开关
选项选择Select / ComboboxSelect 用于固定选项,Combobox 支持搜索

1.2 复合表单场景

vue
<!-- 带前置/后置内容的输入框 -->
<template>
  <Input v-model="url" placeholder="请输入 URL">
    <template #prepend>
      <span class="px-3 border-r-3 border-brutal bg-brutal-muted flex items-center">
        https://
      </span>
    </template>
    <template #append>
      <Button variant="primary" size="sm" @click="verify">验证</Button>
    </template>
  </Input>
</template>

1.3 反馈组件选择

场景推荐组件说明
重要信息确认AlertDialog阻断式确认,必须用户操作
临时信息展示Dialog / Sheet可关闭的浮层
操作反馈Toast自动消失的通知
表单校验提示FormMessage内联错误信息
长时间操作Progress / Spinner进度反馈

2. Props 最佳实践

2.1 变体与尺寸

BrutxUI 组件遵循统一的 variant + size 模式:

vue
<template>
  <!-- 按钮变体 -->
  <Button variant="default">默认</Button>
  <Button variant="primary">主要</Button>
  <Button variant="secondary">次要</Button>
  <Button variant="accent">强调</Button>
  <Button variant="danger">危险</Button>
  <Button variant="success">成功</Button>
  <Button variant="outline">轮廓</Button>
  <Button variant="ghost">幽灵</Button>
  <Button variant="link">链接</Button>

  <!-- 按钮尺寸 -->
  <Button size="sm">小</Button>
  <Button size="default">默认</Button>
  <Button size="lg">大</Button>
  <Button size="xl">特大</Button>
</template>

2.2 布尔 Props 显式声明

vue
<template>
  <!-- 推荐:显式传递布尔值 -->
  <Button :loading="isSubmitting">提交</Button>
  <Button disabled>禁用</Button>
  <Input v-model="name" clearable />

  <!-- 避免:隐式 true(可读性差) -->
  <!-- <Button loading>提交</Button> -->
</template>

2.3 class prop 覆盖样式

几乎所有 BrutxUI 组件都支持 class prop,可用于覆盖默认样式:

vue
<template>
  <Button variant="primary" class="w-full mt-4">
    全宽按钮
  </Button>

  <Card class="max-w-md mx-auto">
    <CardHeader>
      <CardTitle>自定义卡片</CardTitle>
    </CardHeader>
  </Card>
</template>

2.4 TypeScript 类型推导

vue
<script setup lang="ts">
import { type ComponentProps } from 'brutx-ui-vue'
import { Button } from 'brutx-ui-vue'

// 从组件提取 Props 类型
type ButtonProps = ComponentProps<typeof Button>

// 在组合式函数中使用
function createButtonConfig(): ButtonProps {
  return {
    variant: 'primary',
    size: 'lg',
    loading: false,
  }
}
</script>

3. 事件处理

3.1 v-model 双向绑定

vue
<script setup lang="ts">
import { ref } from 'vue'

const text = ref('')
const checked = ref(false)
const selected = ref('')
</script>

<template>
  <Input v-model="text" placeholder="输入文本" />
  <Checkbox v-model:checked="checked" label="同意协议" />
  <Select v-model="selected" :options="options" />
</template>

3.2 事件监听

vue
<template>
  <!-- 清除事件 -->
  <Input v-model="query" clearable @clear="handleClear" />

  <!-- 关闭事件 -->
  <Alert variant="info" @close="handleAlertClose">
    <AlertTitle>提示</AlertTitle>
    <AlertDescription>操作已成功完成</AlertDescription>
  </Alert>

  <!-- 使用 Dialog -->
  <Dialog v-model:open="dialogVisible">
    <DialogTrigger as-child>
      <Button>打开对话框</Button>
    </DialogTrigger>
    <DialogContent @close="handleDialogClose">
      <!-- 内容 -->
    </DialogContent>
  </Dialog>
</template>

3.3 表单提交处理

vue
<script setup lang="ts">
import { ref } from 'vue'
import { useToast } from 'brutx-ui-vue'

const { toast } = useToast()
const isSubmitting = ref(false)

async function handleSubmit() {
  isSubmitting.value = true
  try {
    await submitForm()
    toast({ title: '提交成功', variant: 'success' })
  } catch (error) {
    toast({ title: '提交失败', variant: 'destructive' })
  } finally {
    isSubmitting.value = false
  }
}
</script>

<template>
  <form @submit.prevent="handleSubmit">
    <!-- 表单内容 -->
    <Button type="submit" :loading="isSubmitting">提交</Button>
  </form>
</template>

4. Slot 使用

4.1 默认 Slot

vue
<template>
  <Button>
    <IconDownload class="w-4 h-4" />
    下载文件
  </Button>
</template>

4.2 具名 Slot

vue
<template>
  <!-- Input 的前置/后置插槽 -->
  <Input v-model="price" placeholder="0.00">
    <template #prepend>
      <span class="px-3 bg-brutal-muted border-r-3 border-brutal flex items-center">
        ¥
      </span>
    </template>
    <template #append>
      <span class="px-3 bg-brutal-muted border-l-3 border-brutal flex items-center">
        .00
      </span>
    </template>
  </Input>

  <!-- Alert 的操作插槽 -->
  <Alert variant="warning">
    <AlertTitle>注意</AlertTitle>
    <AlertDescription>此操作不可撤销</AlertDescription>
    <template #actions>
      <Button variant="outline" size="sm" @click="dismiss">忽略</Button>
      <Button variant="primary" size="sm" @click="confirm">确认</Button>
    </template>
  </Alert>
</template>

4.3 Card 复合插槽

vue
<template>
  <Card>
    <CardHeader>
      <CardTitle>用户信息</CardTitle>
      <CardDescription>管理您的个人资料</CardDescription>
    </CardHeader>
    <CardContent>
      <!-- 主体内容 -->
    </CardContent>
    <CardFooter class="justify-end gap-2">
      <Button variant="outline">取消</Button>
      <Button variant="primary">保存</Button>
    </CardFooter>
  </Card>
</template>

5. 组合模式

5.1 复合组件使用

BrutxUI 的复合组件(如 Accordion、Tabs、Dialog)使用 Root-Trigger-Content 模式:

vue
<template>
  <!-- Accordion 手风琴 -->
  <Accordion type="single" collapsible>
    <AccordionItem value="item-1">
      <AccordionTrigger>标题一</AccordionTrigger>
      <AccordionContent>内容一</AccordionContent>
    </AccordionItem>
    <AccordionItem value="item-2">
      <AccordionTrigger>标题二</AccordionTrigger>
      <AccordionContent>内容二</AccordionContent>
    </AccordionItem>
  </Accordion>

  <!-- Tabs 标签页 -->
  <Tabs default-value="account">
    <TabsList>
      <TabsTrigger value="account">账户</TabsTrigger>
      <TabsTrigger value="password">密码</TabsTrigger>
    </TabsList>
    <TabsContent value="account">账户设置内容</TabsContent>
    <TabsContent value="password">密码修改内容</TabsContent>
  </Tabs>
</template>

5.2 组合式函数复用逻辑

vue
<script setup lang="ts">
import { useToast, useClipboard, useDebounce } from 'brutx-ui-vue'

// 复用 Toast 逻辑
const { toast } = useToast()

// 复用剪贴板逻辑
const { copy, isSupported } = useClipboard()

// 复用防抖逻辑
const [debouncedSearch, isDebouncing] = useDebounce(async (query: string) => {
  const results = await searchApi(query)
  // 处理结果
}, 300)

async function handleCopy(text: string) {
  await copy(text)
  toast({ title: '已复制到剪贴板' })
}
</script>

5.3 Provide/Inject 共享状态

vue
<!-- 父组件:提供主题配置 -->
<script setup lang="ts">
import { provide } from 'vue'

provide('formLayout', {
  labelWidth: '120px',
  labelPosition: 'right',
})
</script>

<!-- 子组件:注入配置 -->
<script setup lang="ts">
import { inject } from 'vue'

const layout = inject('formLayout', {
  labelWidth: '100px',
  labelPosition: 'left',
})
</script>

5.4 asChild 模式

BrutxUI 支持 asChild 模式,允许将组件样式应用到自定义元素:

vue
<template>
  <!-- 将 Button 样式应用到 NuxtLink -->
  <Button as-child>
    <NuxtLink to="/dashboard">
      前往仪表盘
    </NuxtLink>
  </Button>

  <!-- 将 DialogTrigger 应用到自定义按钮 -->
  <DialogTrigger as-child>
    <Button variant="outline" size="sm">
      自定义触发器
    </Button>
  </DialogTrigger>
</template>

5.5 实例方法调用

部分组件暴露了实例方法:

vue
<script setup lang="ts">
import { ref } from 'vue'
import { Input } from 'brutx-ui-vue'

const inputRef = ref<InstanceType<typeof Input>>()

function focusInput() {
  inputRef.value?.focus()
}

function selectAll() {
  inputRef.value?.select()
}
</script>

<template>
  <Input ref="inputRef" v-model="value" />
  <Button @click="focusInput">聚焦</Button>
  <Button @click="selectAll">全选</Button>
</template>

蛮力铸就。