Appearance
BizKanban 业务看板
多列泳道卡片看板:卡片跨列拖拽流转(拖入目标列即变更 column 字段并外抛 change)、列头计数与 WIP 上限(超限目标列置红拒绝放入)、列强调色、标签 / 描述 / 附加信息、只读模式与卡片插槽。工单流转、招聘流程、内容审核、敏捷任务板——与 BizDragSort(同列表内拖排)互补,本组件承载跨列状态流转语义。
基础用法
待处理2
登录页验证码失效
用户反馈连续刷新后图形码不更新
P1张三
订单导出超时
P2李四
处理中2
报表列宽拖拽错位
P1王五
首页白屏优化
P3赵六
已完成1
权限缓存击穿修复
P2钱七
三列流转:拖拽卡片到其他列,v-model 同步卡片归属,change 外抛流转明细。
<script setup lang="ts">
/** 场景:工单三列流转——拖拽卡片跨列,v-model 同步归属 */
import { ref } from 'vue'
import { BizKanban } from 'vue3-ep-biz-lib'
import type { BizKanbanCard, BizKanbanColumn } from 'vue3-ep-biz-lib'
const columns: BizKanbanColumn[] = [
{ key: 'todo', title: '待处理' },
{ key: 'doing', title: '处理中' },
{ key: 'done', title: '已完成' },
]
const cards = ref<BizKanbanCard[]>([
{ key: 1, title: '登录页验证码失效', column: 'todo', description: '用户反馈连续刷新后图形码不更新', tag: 'P1', tagType: 'danger', meta: '张三' },
{ key: 2, title: '订单导出超时', column: 'todo', tag: 'P2', tagType: 'warning', meta: '李四' },
{ key: 3, title: '报表列宽拖拽错位', column: 'doing', tag: 'P1', tagType: 'danger', meta: '王五' },
{ key: 4, title: '首页白屏优化', column: 'doing', tag: 'P3', tagType: 'info', meta: '赵六' },
{ key: 5, title: '权限缓存击穿修复', column: 'done', tag: 'P2', tagType: 'warning', meta: '钱七' },
])
</script>
<template>
<BizKanban v-model="cards" :columns="columns" :height="300" />
</template>
显示代码
WIP 上限
「处理中」列已配置 max=2:再拖入第三张卡片时目标列置红拒绝
待处理3
账户注销流程改造
消息中心未读数不准
搜索联想接口降级
处理中(上限 2)2 / 2
批量导入模板更新
附件预览水印
已完成1
移动端适配回归
列配置
max 后超限置红拒绝放入,overflow 外抛提示(工单处理中上限场景)。<script setup lang="ts">
/** 场景:WIP 上限——处理中列 max=2,拖入第三张被拒绝并提示 */
import { ref } from 'vue'
import { ElAlert, ElMessage } from 'element-plus'
import { BizKanban } from 'vue3-ep-biz-lib'
import type { BizKanbanCard, BizKanbanColumn } from 'vue3-ep-biz-lib'
const columns: BizKanbanColumn[] = [
{ key: 'todo', title: '待处理' },
{ key: 'doing', title: '处理中(上限 2)', max: 2 },
{ key: 'done', title: '已完成' },
]
const cards = ref<BizKanbanCard[]>([
{ key: 1, title: '账户注销流程改造', column: 'todo' },
{ key: 2, title: '消息中心未读数不准', column: 'todo' },
{ key: 3, title: '搜索联想接口降级', column: 'todo' },
{ key: 4, title: '批量导入模板更新', column: 'doing' },
{ key: 5, title: '附件预览水印', column: 'doing' },
{ key: 6, title: '移动端适配回归', column: 'done' },
])
</script>
<template>
<div>
<ElAlert title="「处理中」列已配置 max=2:再拖入第三张卡片时目标列置红拒绝" type="info" :closable="false" style="margin-bottom: 10px" />
<BizKanban
v-model="cards"
:columns="columns"
:height="280"
@overflow="(card, col) => ElMessage.warning(`「${card.title}」放入失败:${col.title}已达上限`)"
/>
</div>
</template>
显示代码
列强调色
简历初筛1
前端高级工程师 · 陈同学
3 年经验
面试中2
测试工程师 · 周同学
二面周四 14:00
后端工程师 · 吴同学
终面周五 10:00
待发 Offer1
产品经理 · 郑同学
审批中HR 李娜
已入职1
运维工程师 · 孙同学
下周入职
列头
color 圆点 + 卡片标签色系,状态语义可视化。<script setup lang="ts">
/** 场景:列强调色——列头圆点与卡片标签色系呼应(招聘流程看板) */
import { ref } from 'vue'
import { BizKanban } from 'vue3-ep-biz-lib'
import type { BizKanbanCard, BizKanbanColumn } from 'vue3-ep-biz-lib'
const columns: BizKanbanColumn[] = [
{ key: 'resume', title: '简历初筛', color: 'var(--el-color-info)' },
{ key: 'interview', title: '面试中', color: 'var(--el-color-warning)' },
{ key: 'offer', title: '待发 Offer', color: 'var(--el-color-primary)' },
{ key: 'onboard', title: '已入职', color: 'var(--el-color-success)' },
]
const cards = ref<BizKanbanCard[]>([
{ key: 1, title: '前端高级工程师 · 陈同学', column: 'resume', meta: '3 年经验' },
{ key: 2, title: '测试工程师 · 周同学', column: 'interview', tag: '二面', tagType: 'warning', meta: '周四 14:00' },
{ key: 3, title: '后端工程师 · 吴同学', column: 'interview', tag: '终面', tagType: 'danger', meta: '周五 10:00' },
{ key: 4, title: '产品经理 · 郑同学', column: 'offer', tag: '审批中', meta: 'HR 李娜' },
{ key: 5, title: '运维工程师 · 孙同学', column: 'onboard', meta: '下周入职' },
])
</script>
<template>
<BizKanban v-model="cards" :columns="columns" :height="280" />
</template>
显示代码
自定义卡片
待开发1
商品中心改版0%
前端组
开发中2
会员积分体系62%
张明
结算对账重构35%
刘洋
已交付1
客服工台 V2已交付
王芳
默认插槽接管卡片内容(进度、成员头像首字、优先级等业务信息)。
<script setup lang="ts">
/** 场景:自定义卡片——插槽接管内容(进度条、成员、优先级) */
import { ref } from 'vue'
import { ElIcon, ElProgress, ElTag } from 'element-plus'
import { User } from '@element-plus/icons-vue'
import { BizKanban } from 'vue3-ep-biz-lib'
import type { BizKanbanCard, BizKanbanColumn } from 'vue3-ep-biz-lib'
interface TaskCard extends BizKanbanCard {
owner?: string
progress?: number
}
const columns: BizKanbanColumn[] = [
{ key: 'todo', title: '待开发' },
{ key: 'doing', title: '开发中' },
{ key: 'done', title: '已交付' },
]
const cards = ref<TaskCard[]>([
{ key: 1, title: '商品中心改版', column: 'todo', owner: '前端组', progress: 0 },
{ key: 2, title: '会员积分体系', column: 'doing', owner: '张明', progress: 62 },
{ key: 3, title: '结算对账重构', column: 'doing', owner: '刘洋', progress: 35 },
{ key: 4, title: '客服工台 V2', column: 'done', owner: '王芳', progress: 100 },
])
const pct = (card: BizKanbanCard): number => Number((card as TaskCard).progress ?? 0)
const owner = (card: BizKanbanCard): string => (card as TaskCard).owner ?? '-'
</script>
<template>
<BizKanban v-model="cards" :columns="columns" :height="300">
<template #default="{ card }">
<div style="display: flex; flex-direction: column; gap: 8px">
<div style="display: flex; align-items: center; justify-content: space-between; gap: 8px">
<span style="font-size: 14px; font-weight: 500">{{ card.title }}</span>
<ElTag size="small" :type="pct(card) === 100 ? 'success' : 'info'" disable-transitions>
{{ pct(card) === 100 ? '已交付' : `${pct(card)}%` }}
</ElTag>
</div>
<ElProgress
:percentage="pct(card)"
:stroke-width="6"
:show-text="false"
:status="pct(card) === 100 ? 'success' : undefined"
/>
<div style="display: flex; align-items: center; gap: 4px; font-size: 12px; color: var(--el-text-color-secondary)">
<ElIcon :size="12"><User /></ElIcon>
{{ owner(card) }}
</div>
</div>
</template>
</BizKanban>
</template>
显示代码
只读展示
待审核2
营销活动页文案更新
提交于 09-28
新品详情页素材包
提交于 09-29
已通过1
帮助中心 FAQ 修订
通过
已驳回1
外链跳转合规审查
驳回缺少备案号
readonly 禁止拖拽与点击态,用于汇报页 / 大屏投放。<script setup lang="ts">
/** 场景:只读展示——汇报页 / 大屏投放,禁止拖拽与点击 */
import { ref } from 'vue'
import { BizKanban } from 'vue3-ep-biz-lib'
import type { BizKanbanCard, BizKanbanColumn } from 'vue3-ep-biz-lib'
const columns: BizKanbanColumn[] = [
{ key: 'pending', title: '待审核' },
{ key: 'approved', title: '已通过' },
{ key: 'rejected', title: '已驳回' },
]
const cards = ref<BizKanbanCard[]>([
{ key: 1, title: '营销活动页文案更新', column: 'pending', meta: '提交于 09-28' },
{ key: 2, title: '新品详情页素材包', column: 'pending', meta: '提交于 09-29' },
{ key: 3, title: '帮助中心 FAQ 修订', column: 'approved', tag: '通过', tagType: 'success' },
{ key: 4, title: '外链跳转合规审查', column: 'rejected', tag: '驳回', tagType: 'danger', meta: '缺少备案号' },
])
</script>
<template>
<BizKanban v-model="cards" :columns="columns" :height="260" readonly />
</template>
显示代码
流转事件
待处理1
巡检工单 #1024
处理中1
巡检工单 #1025
已完成0
拖拽卡片到此列
- 拖拽卡片或点击按钮,事件日志在此显示
change / overflow / card-click 事件日志联动,expose
move 程序化流转。<script setup lang="ts">
/** 场景:流转事件日志——change / overflow / card-click 联动 + expose move 程序化流转 */
import { ref } from 'vue'
import { ElButton, ElMessage } from 'element-plus'
import { BizKanban } from 'vue3-ep-biz-lib'
import type { BizKanbanCard, BizKanbanChangePayload, BizKanbanColumn, BizKanbanInstance } from 'vue3-ep-biz-lib'
const columns: BizKanbanColumn[] = [
{ key: 'todo', title: '待处理' },
{ key: 'doing', title: '处理中' },
{ key: 'done', title: '已完成' },
]
const cards = ref<BizKanbanCard[]>([
{ key: 1, title: '巡检工单 #1024', column: 'todo' },
{ key: 2, title: '巡检工单 #1025', column: 'doing' },
])
const logs = ref<string[]>([])
const kanbanRef = ref<BizKanbanInstance>()
const onChange = (payload: BizKanbanChangePayload): void => {
logs.value = [`change:「${payload.card.title}」 ${payload.from} → ${payload.to}`, ...logs.value].slice(0, 6)
}
const onCardClick = (card: BizKanbanCard): void => {
logs.value = [`card-click:${card.title}`, ...logs.value].slice(0, 6)
}
</script>
<template>
<div>
<div style="margin-bottom: 10px; display: flex; gap: 8px">
<ElButton size="small" @click="kanbanRef?.move(1, 'doing')">程序化流转 #1024 → 处理中</ElButton>
<ElButton size="small" @click="ElMessage.info(`「处理中」列:${kanbanRef?.getCardsOf('doing').length} 张卡片`)">读取列卡片</ElButton>
</div>
<BizKanban
ref="kanbanRef"
v-model="cards"
:columns="columns"
:height="240"
@change="onChange"
@card-click="onCardClick"
/>
<ul style="margin: 10px 0 0; padding: 8px 12px; list-style: none; border: 1px solid var(--el-border-color-lighter); border-radius: 4px; font-size: 12px; color: var(--el-text-color-secondary)">
<li v-if="logs.length === 0" style="padding: 2px 0">拖拽卡片或点击按钮,事件日志在此显示</li>
<li v-for="(log, i) in logs" :key="i" style="padding: 2px 0">{{ log }}</li>
</ul>
</div>
</template>
显示代码
Props
| 属性名 | 类型 | 默认值 | 说明 |
|---|---|---|---|
| columns | BizKanbanColumn[] | [] | 列定义(key / title / color / max) |
| v-model | BizKanbanCard[] | [] | 卡片数据(column 字段归属列) |
| readonly | boolean | false | 只读(禁止拖拽与点击) |
| draggable | boolean | true | 卡片可拖拽 |
| show-count | boolean | true | 列头显示计数(含 max 时显示 n / max) |
| height | number | — | 看板高度(列内容区滚动) |
Events
| 事件名 | 参数 | 说明 |
|---|---|---|
| change | (payload: { card, from, to, cards }) | 卡片跨列流转(含完整新列表) |
| overflow | (card, column) | 目标列超 WIP 上限被拒绝 |
| card-click | (card) | 卡片点击(readonly 下不触发) |
Slots
| 插槽名 | 作用域 | 说明 |
|---|---|---|
| default | { card, column } | 卡片内容接管 |
Expose
| 名称 | 类型 | 说明 |
|---|---|---|
| getCardsOf | (column: string) => BizKanbanCard[] | 指定列卡片列表 |
| move | (key, column) => void | 程序化流转(与拖拽等价) |