开发预览 更新于 2026-05-10

Button 按钮

5 种 variant、3 种尺寸、3 种形状、loading 与 disabled 态。

基础用法

5 种视觉变体覆盖从主操作到次操作的全部层级,按视觉权重从重到轻排列。 primary 是页面唯一的主操作,danger 用于删除等不可逆操作。

背景 视口
src/App.vue
<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 时常用)。

背景 视口
src/App.vue
<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 让宽度铺满父容器。

背景 视口
src/App.vue
<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>
    </>
  );
}

图标按钮

leadingtrailing 具名插槽用于放置左侧 / 右侧图标。两者可以单独使用,也可以同时存在。 是表单类操作(新建、导出、下一步)最常见的按钮形态。

背景 视口
src/App.vue
<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 描述按钮动作,否则屏幕阅读器读不出含义。

背景 视口
src/App.vue
<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 提示。

背景 视口
src/App.vue
<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 触发)。

背景 视口
src/App.vue
<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。 这意味着你可以在加载期间显示与默认态不同的提示(“提交中…” / “保存中…” / “发送中…”),让用户知道当前发生了什么。

背景 视口
src/App.vue
<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 用于纯图标按钮
loadingbooleanfalse显示 spinner 并阻止点击
blockbooleanfalse撑满父容器宽度
disabledbooleanfalse禁用
type'button' | 'submit' | 'reset''button'原生 button type,用于 Form 提交场景

Events

Event载荷类型说明
clickMouseEvent点击触发;disabledloading 时不触发

Slots / Children

SlotProps说明
default按钮文案;React 端等价为 children
leading左侧图标;React 端等价为 leading prop
trailing右侧图标;React 端等价为 trailing prop

反馈与讨论

Button 按钮 的讨论

0
0 / 600
正在加载评论...