Button 按钮
5 种 variant、3 种尺寸、3 种形状、loading 与 disabled 态。
基础用法
5 种视觉变体覆盖从主操作到次操作的全部层级,按视觉权重从重到轻排列。
primary 是页面唯一的主操作,danger 用于删除等不可逆操作。
背景 视口
<script setup lang="ts">
import { CfButton } from '@chufix-design/vue';
</script>
<template>
<div class="demo-row">
<CfButton variant="primary">Primary</CfButton>
<CfButton variant="secondary">Secondary</CfButton>
<CfButton variant="tertiary">Tertiary</CfButton>
<CfButton variant="ghost">Ghost</CfButton>
<CfButton variant="danger">Danger</CfButton>
</div>
</template> <script setup>
import { CfButton } from '@chufix-design/vue';
</script>
<template>
<div class="demo-row">
<CfButton variant="primary">Primary</CfButton>
<CfButton variant="secondary">Secondary</CfButton>
<CfButton variant="tertiary">Tertiary</CfButton>
<CfButton variant="ghost">Ghost</CfButton>
<CfButton variant="danger">Danger</CfButton>
</div>
</template> import { CfButton } from '@chufix-design/react';
export default function Demo() {
return (
<>
<CfButton variant="primary">Primary</CfButton>
<CfButton variant="secondary">Secondary</CfButton>
<CfButton variant="tertiary">Tertiary</CfButton>
<CfButton variant="ghost">Ghost</CfButton>
<CfButton variant="danger">Danger</CfButton>
</>
);
} import { CfButton } from '@chufix-design/react';
export default function Demo() {
return (
<>
<CfButton variant="primary">Primary</CfButton>
<CfButton variant="secondary">Secondary</CfButton>
<CfButton variant="tertiary">Tertiary</CfButton>
<CfButton variant="ghost">Ghost</CfButton>
<CfButton variant="danger">Danger</CfButton>
</>
);
} 尺寸与形状
3 档尺寸(sm / md / lg)适配不同信息密度;3 种形状中 pill 是胶囊形,square 是正方形(icon-only 时常用)。
背景 视口
<script setup lang="ts">
import { CfButton } from '@chufix-design/vue';
</script>
<template>
<div class="demo-stack">
<div class="demo-row">
<CfButton size="sm">Small</CfButton>
<CfButton size="md">Medium</CfButton>
<CfButton size="lg">Large</CfButton>
</div>
<div class="demo-row">
<CfButton shape="default">默认圆角</CfButton>
<CfButton shape="pill">Pill</CfButton>
<CfButton shape="square" aria-label="设置">⚙</CfButton>
</div>
</div>
</template> <script setup>
import { CfButton } from '@chufix-design/vue';
</script>
<template>
<div class="demo-stack">
<div class="demo-row">
<CfButton size="sm">Small</CfButton>
<CfButton size="md">Medium</CfButton>
<CfButton size="lg">Large</CfButton>
</div>
<div class="demo-row">
<CfButton shape="default">默认圆角</CfButton>
<CfButton shape="pill">Pill</CfButton>
<CfButton shape="square" aria-label="设置">⚙</CfButton>
</div>
</div>
</template> import { CfButton } from '@chufix-design/react';
export default function Demo() {
return (
<>
<CfButton size="sm">Small</CfButton>
<CfButton size="md">Medium</CfButton>
<CfButton size="lg">Large</CfButton>
<CfButton shape="default">默认圆角</CfButton>
<CfButton shape="pill">Pill</CfButton>
<CfButton shape="square" aria-label="设置">⚙</CfButton>
</>
);
} import { CfButton } from '@chufix-design/react';
export default function Demo() {
return (
<>
<CfButton size="sm">Small</CfButton>
<CfButton size="md">Medium</CfButton>
<CfButton size="lg">Large</CfButton>
<CfButton shape="default">默认圆角</CfButton>
<CfButton shape="pill">Pill</CfButton>
<CfButton shape="square" aria-label="设置">⚙</CfButton>
</>
);
} 状态
loading 自动渲染 spinner 并阻止点击;disabled 让按钮不可交互;block 让宽度铺满父容器。
背景 视口
<script setup lang="ts">
import { ref } from 'vue';
import { CfButton } from '@chufix-design/vue';
const loading = ref(false);
async function load() {
loading.value = true;
await new Promise((r) => setTimeout(r, 1500));
loading.value = false;
}
</script>
<template>
<div class="demo-stack">
<div class="demo-row">
<CfButton :loading="loading" @click="load">{{ loading ? '加载中…' : '点击加载' }}</CfButton>
<CfButton disabled>Disabled</CfButton>
<CfButton variant="tertiary" disabled>Outline Disabled</CfButton>
</div>
<CfButton block>整行铺满(block)</CfButton>
</div>
</template> <script setup>
import { ref } from 'vue';
import { CfButton } from '@chufix-design/vue';
const loading = ref(false);
async function load() {
loading.value = true;
await new Promise((r) => setTimeout(r, 1500));
loading.value = false;
}
</script>
<template>
<div class="demo-stack">
<div class="demo-row">
<CfButton :loading="loading" @click="load">{{ loading ? '加载中…' : '点击加载' }}</CfButton>
<CfButton disabled>Disabled</CfButton>
<CfButton variant="tertiary" disabled>Outline Disabled</CfButton>
</div>
<CfButton block>整行铺满(block)</CfButton>
</div>
</template> import { useState } from 'react';
import { CfButton } from '@chufix-design/react';
export default function Demo() {
const [loading, setLoading] = useState(false);
async function load() {
setLoading(true);
await new Promise((r) => setTimeout(r, 1500));
setLoading(false);
}
return (
<>
<div className="demo-stack">
<div className="demo-row">
<CfButton loading={loading} onClick={load}>{loading ? '加载中…' : '点击加载'}</CfButton>
<CfButton disabled>Disabled</CfButton>
<CfButton variant="tertiary" disabled>Outline Disabled</CfButton>
</div>
<CfButton block>整行铺满(block)</CfButton>
</div>
</>
);
} import { useState } from 'react';
import { CfButton } from '@chufix-design/react';
export default function Demo() {
const [loading, setLoading] = useState(false);
async function load() {
setLoading(true);
await new Promise((r) => setTimeout(r, 1500));
setLoading(false);
}
return (
<>
<div className="demo-stack">
<div className="demo-row">
<CfButton loading={loading} onClick={load}>{loading ? '加载中…' : '点击加载'}</CfButton>
<CfButton disabled>Disabled</CfButton>
<CfButton variant="tertiary" disabled>Outline Disabled</CfButton>
</div>
<CfButton block>整行铺满(block)</CfButton>
</div>
</>
);
} 图标按钮
leading 与 trailing 具名插槽用于放置左侧 / 右侧图标。两者可以单独使用,也可以同时存在。
是表单类操作(新建、导出、下一步)最常见的按钮形态。
背景 视口
<script setup lang="ts">
import { CfButton, CfIcon } from '@chufix-design/vue';
</script>
<template>
<div class="demo-stack">
<div class="demo-row">
<CfButton variant="primary">
<template #leading><CfIcon name="plus" /></template>
新建
</CfButton>
<CfButton variant="secondary">
下一步
<template #trailing><CfIcon name="arrow-right" /></template>
</CfButton>
<CfButton variant="tertiary">
<template #leading><CfIcon name="download" /></template>
导出
<template #trailing><CfIcon name="chevron-down" /></template>
</CfButton>
</div>
</div>
</template> <script setup>
import { CfButton, CfIcon } from '@chufix-design/vue';
</script>
<template>
<div class="demo-stack">
<div class="demo-row">
<CfButton variant="primary">
<template #leading><CfIcon name="plus" /></template>
新建
</CfButton>
<CfButton variant="secondary">
下一步
<template #trailing><CfIcon name="arrow-right" /></template>
</CfButton>
<CfButton variant="tertiary">
<template #leading><CfIcon name="download" /></template>
导出
<template #trailing><CfIcon name="chevron-down" /></template>
</CfButton>
</div>
</div>
</template> import { CfButton, CfIcon } from '@chufix-design/react';
export default function Demo() {
return (
<>
<CfButton variant="primary" leading={<CfIcon name="plus" />}>
新建
</CfButton>
<CfButton variant="secondary" trailing={<CfIcon name="arrow-right" />}>
下一步
</CfButton>
<CfButton
variant="tertiary"
leading={<CfIcon name="download" />}
trailing={<CfIcon name="chevron-down" />}
>
导出
</CfButton>
</>
);
} import { CfButton, CfIcon } from '@chufix-design/react';
export default function Demo() {
return (
<>
<CfButton variant="primary" leading={<CfIcon name="plus" />}>
新建
</CfButton>
<CfButton variant="secondary" trailing={<CfIcon name="arrow-right" />}>
下一步
</CfButton>
<CfButton
variant="tertiary"
leading={<CfIcon name="download" />}
trailing={<CfIcon name="chevron-down" />}
>
导出
</CfButton>
</>
);
} 图标独立按钮
只显示图标的按钮:shape="square" 给出正方形几何,默认插槽放单个图标。
必须 提供非空的 aria-label 描述按钮动作,否则屏幕阅读器读不出含义。
背景 视口
<script setup lang="ts">
import { CfButton, CfIcon } from '@chufix-design/vue';
</script>
<template>
<div class="demo-row">
<CfButton variant="primary" shape="square" aria-label="搜索">
<CfIcon name="search" />
</CfButton>
<CfButton variant="secondary" shape="square" aria-label="复制到剪贴板">
<CfIcon name="copy" />
</CfButton>
<CfButton variant="tertiary" shape="square" aria-label="删除">
<CfIcon name="trash" />
</CfButton>
</div>
</template> <script setup>
import { CfButton, CfIcon } from '@chufix-design/vue';
</script>
<template>
<div class="demo-row">
<CfButton variant="primary" shape="square" aria-label="搜索">
<CfIcon name="search" />
</CfButton>
<CfButton variant="secondary" shape="square" aria-label="复制到剪贴板">
<CfIcon name="copy" />
</CfButton>
<CfButton variant="tertiary" shape="square" aria-label="删除">
<CfIcon name="trash" />
</CfButton>
</div>
</template> import { CfButton, CfIcon } from '@chufix-design/react';
export default function Demo() {
return (
<>
<CfButton variant="primary" shape="square" aria-label="搜索">
<CfIcon name="search" />
</CfButton>
<CfButton variant="secondary" shape="square" aria-label="复制到剪贴板">
<CfIcon name="copy" />
</CfButton>
<CfButton variant="tertiary" shape="square" aria-label="删除">
<CfIcon name="trash" />
</CfButton>
</>
);
} import { CfButton, CfIcon } from '@chufix-design/react';
export default function Demo() {
return (
<>
<CfButton variant="primary" shape="square" aria-label="搜索">
<CfIcon name="search" />
</CfButton>
<CfButton variant="secondary" shape="square" aria-label="复制到剪贴板">
<CfIcon name="copy" />
</CfButton>
<CfButton variant="tertiary" shape="square" aria-label="删除">
<CfIcon name="trash" />
</CfButton>
</>
);
} 按钮组
多个相关操作横向排列(编辑 / 复制 / 删除)。同一组内尺寸保持一致,主操作放右侧、不可逆操作(删除)放最右端并用 danger variant 提示。
背景 视口
<script setup lang="ts">
import { CfButton, CfIcon } from '@chufix-design/vue';
</script>
<template>
<div class="demo-row demo-row--gap-sm">
<CfButton variant="secondary" size="sm">
<template #leading><CfIcon name="edit" /></template>
编辑
</CfButton>
<CfButton variant="secondary" size="sm">
<template #leading><CfIcon name="copy" /></template>
复制
</CfButton>
<CfButton variant="danger" size="sm">
<template #leading><CfIcon name="trash" /></template>
删除
</CfButton>
</div>
</template>
<style scoped>
.demo-row--gap-sm { gap: 6px; }
</style> <script setup>
import { CfButton, CfIcon } from '@chufix-design/vue';
</script>
<template>
<div class="demo-row demo-row--gap-sm">
<CfButton variant="secondary" size="sm">
<template #leading><CfIcon name="edit" /></template>
编辑
</CfButton>
<CfButton variant="secondary" size="sm">
<template #leading><CfIcon name="copy" /></template>
复制
</CfButton>
<CfButton variant="danger" size="sm">
<template #leading><CfIcon name="trash" /></template>
删除
</CfButton>
</div>
</template>
<style scoped>
.demo-row--gap-sm { gap: 6px; }
</style> import { CfButton, CfIcon } from '@chufix-design/react';
export default function Demo() {
return (
<div style={{ display: 'inline-flex', gap: 6 }}>
<CfButton variant="secondary" size="sm" leading={<CfIcon name="edit" />}>
编辑
</CfButton>
<CfButton variant="secondary" size="sm" leading={<CfIcon name="copy" />}>
复制
</CfButton>
<CfButton variant="danger" size="sm" leading={<CfIcon name="trash" />}>
删除
</CfButton>
</div>
);
} import { CfButton, CfIcon } from '@chufix-design/react';
export default function Demo() {
return (
<div style={{ display: 'inline-flex', gap: 6 }}>
<CfButton variant="secondary" size="sm" leading={<CfIcon name="edit" />}>
编辑
</CfButton>
<CfButton variant="secondary" size="sm" leading={<CfIcon name="copy" />}>
复制
</CfButton>
<CfButton variant="danger" size="sm" leading={<CfIcon name="trash" />}>
删除
</CfButton>
</div>
);
} 链接样式按钮
导航类操作(“查看详情” / “了解更多”)用 variant="ghost" 即可获得视觉上接近超链接的效果:透明背景、无边框,
hover 时才显示反馈。比起原生 <a> 标签,它继承了完整的按钮 a11y 行为(焦点环、键盘 Enter / Space 触发)。
背景 视口
<script setup lang="ts">
import { CfButton, CfIcon } from '@chufix-design/vue';
</script>
<template>
<div class="demo-row">
<CfButton variant="ghost" size="sm">
查看详情
<template #trailing><CfIcon name="arrow-right" /></template>
</CfButton>
<CfButton variant="ghost" size="sm">
<template #leading><CfIcon name="globe" /></template>
访问官网
</CfButton>
<CfButton variant="ghost" size="sm">了解更多 →</CfButton>
</div>
</template> <script setup>
import { CfButton, CfIcon } from '@chufix-design/vue';
</script>
<template>
<div class="demo-row">
<CfButton variant="ghost" size="sm">
查看详情
<template #trailing><CfIcon name="arrow-right" /></template>
</CfButton>
<CfButton variant="ghost" size="sm">
<template #leading><CfIcon name="globe" /></template>
访问官网
</CfButton>
<CfButton variant="ghost" size="sm">了解更多 →</CfButton>
</div>
</template> import { CfButton, CfIcon } from '@chufix-design/react';
export default function Demo() {
return (
<>
<CfButton variant="ghost" size="sm" trailing={<CfIcon name="arrow-right" />}>
查看详情
</CfButton>
<CfButton variant="ghost" size="sm" leading={<CfIcon name="globe" />}>
访问官网
</CfButton>
<CfButton variant="ghost" size="sm">了解更多 →</CfButton>
</>
);
} import { CfButton, CfIcon } from '@chufix-design/react';
export default function Demo() {
return (
<>
<CfButton variant="ghost" size="sm" trailing={<CfIcon name="arrow-right" />}>
查看详情
</CfButton>
<CfButton variant="ghost" size="sm" leading={<CfIcon name="globe" />}>
访问官网
</CfButton>
<CfButton variant="ghost" size="sm">了解更多 →</CfButton>
</>
);
} 自定义加载文案
loading 状态下默认插槽的文案会被原样保留——只渲染额外的 spinner。
这意味着你可以在加载期间显示与默认态不同的提示(“提交中…” / “保存中…” / “发送中…”),让用户知道当前发生了什么。
背景 视口
<script setup lang="ts">
import { ref } from 'vue';
import { CfButton } from '@chufix-design/vue';
const submitting = ref(false);
const saving = ref(false);
const sending = ref(false);
async function submit(): Promise<void> {
submitting.value = true;
await new Promise<void>((resolve) => setTimeout(resolve, 1500));
submitting.value = false;
}
async function save(): Promise<void> {
saving.value = true;
await new Promise<void>((resolve) => setTimeout(resolve, 1500));
saving.value = false;
}
async function send(): Promise<void> {
sending.value = true;
await new Promise<void>((resolve) => setTimeout(resolve, 1500));
sending.value = false;
}
</script>
<template>
<div class="demo-row">
<CfButton variant="primary" :loading="submitting" @click="submit">
{{ submitting ? '提交中…' : '提交' }}
</CfButton>
<CfButton variant="secondary" :loading="saving" @click="save">
{{ saving ? '保存中…' : '保存草稿' }}
</CfButton>
<CfButton variant="tertiary" :loading="sending" @click="send">
{{ sending ? '发送中…' : '发送邮件' }}
</CfButton>
</div>
</template> <script setup>
import { ref } from 'vue';
import { CfButton } from '@chufix-design/vue';
const submitting = ref(false);
const saving = ref(false);
const sending = ref(false);
async function submit(): Promise<void> {
submitting.value = true;
await new Promise<void>((resolve) => setTimeout(resolve, 1500));
submitting.value = false;
}
async function save(): Promise<void> {
saving.value = true;
await new Promise<void>((resolve) => setTimeout(resolve, 1500));
saving.value = false;
}
async function send(): Promise<void> {
sending.value = true;
await new Promise<void>((resolve) => setTimeout(resolve, 1500));
sending.value = false;
}
</script>
<template>
<div class="demo-row">
<CfButton variant="primary" :loading="submitting" @click="submit">
{{ submitting ? '提交中…' : '提交' }}
</CfButton>
<CfButton variant="secondary" :loading="saving" @click="save">
{{ saving ? '保存中…' : '保存草稿' }}
</CfButton>
<CfButton variant="tertiary" :loading="sending" @click="send">
{{ sending ? '发送中…' : '发送邮件' }}
</CfButton>
</div>
</template> import { useState } from 'react';
import { CfButton } from '@chufix-design/react';
export default function Demo() {
const [submitting, setSubmitting] = useState(false);
const [saving, setSaving] = useState(false);
const [sending, setSending] = useState(false);
async function submit(): Promise<void> {
setSubmitting(true);
await new Promise<void>((resolve) => setTimeout(resolve, 1500));
setSubmitting(false);
}
async function save(): Promise<void> {
setSaving(true);
await new Promise<void>((resolve) => setTimeout(resolve, 1500));
setSaving(false);
}
async function send(): Promise<void> {
setSending(true);
await new Promise<void>((resolve) => setTimeout(resolve, 1500));
setSending(false);
}
return (
<>
<div className="demo-row">
<CfButton variant="primary" loading={submitting} onClick={submit}>
{submitting ? '提交中…' : '提交'}
</CfButton>
<CfButton variant="secondary" loading={saving} onClick={save}>
{saving ? '保存中…' : '保存草稿'}
</CfButton>
<CfButton variant="tertiary" loading={sending} onClick={send}>
{sending ? '发送中…' : '发送邮件'}
</CfButton>
</div>
</>
);
} import { useState } from 'react';
import { CfButton } from '@chufix-design/react';
export default function Demo() {
const [submitting, setSubmitting] = useState(false);
const [saving, setSaving] = useState(false);
const [sending, setSending] = useState(false);
async function submit(): Promise<void> {
setSubmitting(true);
await new Promise<void>((resolve) => setTimeout(resolve, 1500));
setSubmitting(false);
}
async function save(): Promise<void> {
setSaving(true);
await new Promise<void>((resolve) => setTimeout(resolve, 1500));
setSaving(false);
}
async function send(): Promise<void> {
setSending(true);
await new Promise<void>((resolve) => setTimeout(resolve, 1500));
setSending(false);
}
return (
<>
<div className="demo-row">
<CfButton variant="primary" loading={submitting} onClick={submit}>
{submitting ? '提交中…' : '提交'}
</CfButton>
<CfButton variant="secondary" loading={saving} onClick={save}>
{saving ? '保存中…' : '保存草稿'}
</CfButton>
<CfButton variant="tertiary" loading={sending} onClick={send}>
{sending ? '发送中…' : '发送邮件'}
</CfButton>
</div>
</>
);
} API
Props
| Prop | 类型 | 默认值 | 说明 |
|---|---|---|---|
variant | 'primary' | 'secondary' | 'tertiary' | 'ghost' | 'danger' | 'primary' | 视觉变体(按权重从重到轻) |
size | 'sm' | 'md' | 'lg' | 'md' | 高度与字号 |
shape | 'default' | 'pill' | 'square' | 'default' | 圆角形状;square 用于纯图标按钮 |
loading | boolean | false | 显示 spinner 并阻止点击 |
block | boolean | false | 撑满父容器宽度 |
disabled | boolean | false | 禁用 |
type | 'button' | 'submit' | 'reset' | 'button' | 原生 button type,用于 Form 提交场景 |
Events
| Event | 载荷类型 | 说明 |
|---|---|---|
click | MouseEvent | 点击触发;disabled 或 loading 时不触发 |
Slots / Children
| Slot | Props | 说明 |
|---|---|---|
default | — | 按钮文案;React 端等价为 children |
leading | — | 左侧图标;React 端等价为 leading prop |
trailing | — | 右侧图标;React 端等价为 trailing prop |
反馈与讨论
Button 按钮 的讨论