工单多了以后靠肉眼在四列里翻太慢。顶部输入框按空白分词逐词 AND 匹配 标题、工单号、PR 号,`#427` 与 `427` 等价,纯数字按包含匹配,记不全 编号也能搜到。筛选只收窄渲染,拖拽与依赖锁定仍按全量列表算;列头角标 在筛选时显示「可见 / 总数」。
65 lines
2.4 KiB
TypeScript
65 lines
2.4 KiB
TypeScript
import type { ReactNode } from 'react'
|
|
import type { Issue, IssueColumn } from '../types'
|
|
import { useDroppable } from '@dnd-kit/core'
|
|
import { Plus } from 'lucide-react'
|
|
import { cn } from '@/lib/utils'
|
|
import { COLUMN_LABELS } from '../types'
|
|
import { Badge } from './ui/badge'
|
|
|
|
interface KanbanColumnProps {
|
|
column: IssueColumn
|
|
issues: Issue[]
|
|
/** 该列未经筛选的卡片总数。与 `issues.length` 不等时角标显示「可见 / 总数」。 */
|
|
totalCount?: number
|
|
children: ReactNode
|
|
/** When provided, renders a + button in the header. Used for the todo column. */
|
|
onCreate?: () => void
|
|
}
|
|
|
|
export function KanbanColumn({ column, issues, totalCount, children, onCreate }: KanbanColumnProps) {
|
|
const filtered = totalCount !== undefined && totalCount !== issues.length
|
|
const { setNodeRef, isOver } = useDroppable({ id: column })
|
|
|
|
return (
|
|
<div
|
|
ref={setNodeRef}
|
|
className={cn(
|
|
'flex h-full min-h-0 flex-col rounded-lg p-2 transition-colors',
|
|
'bg-[var(--vscode-sideBar-background)]',
|
|
isOver && 'bg-black/10 dark:bg-white/10',
|
|
)}
|
|
>
|
|
<div className="mb-2 flex items-center justify-between px-1">
|
|
<span className="text-sm font-medium text-[var(--vscode-foreground)] opacity-80">
|
|
{COLUMN_LABELS[column]}
|
|
</span>
|
|
<div className="flex items-center gap-1.5">
|
|
<Badge variant="outline">{filtered ? `${issues.length} / ${totalCount}` : issues.length}</Badge>
|
|
{onCreate && (
|
|
<button
|
|
type="button"
|
|
onClick={onCreate}
|
|
aria-label="新建工单"
|
|
className="grid size-5 place-items-center rounded text-[var(--vscode-foreground)] opacity-60 hover:bg-black/10 hover:opacity-100 dark:hover:bg-white/10"
|
|
>
|
|
<Plus className="size-3.5" />
|
|
</button>
|
|
)}
|
|
</div>
|
|
</div>
|
|
|
|
<div className="flex min-h-0 flex-1 flex-col gap-2 overflow-y-auto px-2 py-1">
|
|
{issues.length === 0
|
|
? (
|
|
<div className="flex flex-1 items-center justify-center rounded-md border border-dashed border-[var(--vscode-foreground)] border-opacity-20 p-4 text-xs text-muted-foreground opacity-60">
|
|
{filtered ? '没有匹配的工单' : '拖到这里'}
|
|
</div>
|
|
)
|
|
: (
|
|
children
|
|
)}
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|