Kanban
Multi-column card board using native HTML5 drag-and-drop to move cards across columns; supports WIP limits and column accent colors.
Basic usage
CfKanban accepts a KanbanColumn[], where each column has id / title / cards, and each card may have title / description / tag / meta.
Drag a card onto another card or empty column to move it; on drop the component updates modelValue and fires cardMove.
<script setup lang="ts">
import { ref } from 'vue';
import { CfKanban, type KanbanColumn } from '@chufix-design/vue';
const board = ref<KanbanColumn[]>([
{
id: 'todo',
title: '待办',
cards: [
{ id: 'c1', title: '设计登录页', tag: 'design' },
{ id: 'c2', title: '梳理 API 字段', tag: 'backend' },
],
},
{
id: 'doing',
title: '进行中',
limit: 3,
cards: [
{ id: 'c3', title: '埋点 SDK 接入', tag: 'frontend', meta: '@小明' },
],
},
{
id: 'done',
title: '已完成',
accent: 'oklch(70% 0.16 145)',
cards: [
{ id: 'c4', title: '更新 README', meta: '昨天' },
],
},
]);
</script>
<template>
<CfKanban v-model="board" />
</template>
import { useState } from 'react';
import { CfKanban, type KanbanColumn } from '@chufix-design/react';
export default function Demo() {
const [board, setBoard] = useState<KanbanColumn[]>([
{
id: 'todo',
title: 'To do',
cards: [
{ id: 'c1', title: 'Design login page', tag: 'design' },
{ id: 'c2', title: 'Outline API fields', tag: 'backend' },
],
},
{ id: 'doing', title: 'In progress', limit: 3, cards: [
{ id: 'c3', title: 'Integrate analytics SDK', tag: 'frontend', meta: '@alex' },
] },
{ id: 'done', title: 'Done', accent: 'oklch(70% 0.16 145)', cards: [
{ id: 'c4', title: 'Update README', meta: 'Yesterday' },
] },
]);
return <CfKanban value={board} onChange={setBoard} />;
} Column limit and accent color
column.limit shows as used / limit in the header (the number is a hint only — drops are not blocked). column.accent tints the header title — useful for visualizing semantics like “priority / status / owner”.
<script setup lang="ts">
import { ref } from 'vue';
import { CfKanban, type KanbanColumn } from '@chufix-design/vue';
const board = ref<KanbanColumn[]>([
{
id: 'backlog',
title: 'Backlog',
accent: 'oklch(70% 0.04 260)',
cards: [
{ id: 'b1', title: '设计 Brand 系统', tag: 'design' },
{ id: 'b2', title: '梳理 SLA 表', tag: 'docs' },
{ id: 'b3', title: '调研监控方案' },
],
},
{
id: 'wip',
title: '进行中',
limit: 2,
accent: 'oklch(70% 0.16 60)',
cards: [
{ id: 'w1', title: '新版仪表盘', tag: 'frontend', meta: '@小明' },
{ id: 'w2', title: '统一鉴权 SDK', tag: 'backend', meta: '@小红' },
],
},
{
id: 'review',
title: '待评审',
accent: 'oklch(70% 0.16 280)',
cards: [{ id: 'r1', title: '埋点白名单', tag: 'data' }],
},
{
id: 'done',
title: '已完成',
accent: 'oklch(70% 0.16 145)',
cards: [
{ id: 'd1', title: '部署稳定性周报', meta: '昨天' },
{ id: 'd2', title: '修复时区 bug', tag: 'bugfix', meta: '前天' },
],
},
]);
</script>
<template>
<CfKanban v-model="board" />
</template>
const [board, setBoard] = useState<KanbanColumn[]>([
{
id: 'wip', title: 'In progress', limit: 2,
accent: 'oklch(70% 0.16 60)',
cards: [/* ... */],
},
// ...
]);
<CfKanban value={board} onChange={setBoard} /> Disable drag
draggable={false} turns off all interaction — a read-only board view. Cards still render from props; only the cursor and dragstart listeners are removed.
<script setup lang="ts">
import { ref } from 'vue';
import { CfKanban, type KanbanColumn } from '@chufix-design/vue';
const board = ref<KanbanColumn[]>([
{
id: 'todo',
title: '待办',
cards: [
{ id: 'c1', title: '设计登录页', tag: 'design' },
{ id: 'c2', title: '梳理 API 字段', tag: 'backend' },
],
},
{
id: 'doing',
title: '进行中',
cards: [{ id: 'c3', title: '埋点 SDK 接入', tag: 'frontend', meta: '@小明' }],
},
{
id: 'done',
title: '已完成',
cards: [{ id: 'c4', title: '更新 README', meta: '昨天' }],
},
]);
</script>
<template>
<CfKanban v-model="board" :draggable="false" />
</template>
<CfKanban value={board} onChange={setBoard} draggable={false} /> API
| Prop | Type | Default | Description |
|---|---|---|---|
modelValue (Vue) / value (React) | KanbanColumn[] | [] | Full board state |
defaultValue | KanbanColumn[] | [] | Uncontrolled initial value |
draggable | boolean | true | Enable HTML5 drag-and-drop |
bordered | boolean | true | Border |
size | 'sm' | 'md' | 'lg' | 'md' | Font size |
Events:
onChange(cols)/update:modelValue— any change to the boardonCardMove({ cardId, from, to, toIndex })/card-move— fires after a card moves; payload contains source column, target column, and destination index
Type exports:
interface KanbanCard {
id: string;
title: string;
description?: string;
tag?: string;
meta?: string;
}
interface KanbanColumn {
id: string;
title: string;
cards: KanbanCard[];
limit?: number;
accent?: string;
}
反馈与讨论
Kanban · Discussion