✨ feat(vscode): 看板顶部加筛选框,按标题 / 工单号 / PR 号过滤卡片
工单多了以后靠肉眼在四列里翻太慢。顶部输入框按空白分词逐词 AND 匹配 标题、工单号、PR 号,`#427` 与 `427` 等价,纯数字按包含匹配,记不全 编号也能搜到。筛选只收窄渲染,拖拽与依赖锁定仍按全量列表算;列头角标 在筛选时显示「可见 / 总数」。
This commit is contained in:
1 parent
73f2c3ec66
commit
9e255f517b
6 files changed
+167
-8
No files matched your search
@@ -13,6 +13,7 @@ import { ToastStack } from './components/ToastStack'
|
||||
import { useIssues } from './hooks/useIssues'
|
||||
import { useManagedSessions } from './hooks/useManagedSessions'
|
||||
import { useProfiles } from './hooks/useProfiles'
|
||||
import { filterIssues } from './lib/issueFilter'
|
||||
import { compareIssuesInColumn } from './lib/issueSort'
|
||||
import { COLUMN_ORDER } from './types'
|
||||
|
||||
@@ -127,6 +128,9 @@ export function App() {
|
||||
setShowNewIssueModal(false)
|
||||
}
|
||||
|
||||
// 顶部筛选框内容。纯前端过滤,不影响拖拽与详情面板拿到的全量列表。
|
||||
const [filterQuery, setFilterQuery] = useState('')
|
||||
|
||||
// Drop selectedId if it's no longer present after issues refresh.
|
||||
const readyIssues = state.status === 'ready' ? state.issues : null
|
||||
useEffect(() => {
|
||||
@@ -153,13 +157,15 @@ export function App() {
|
||||
|
||||
// Issues partitioned by column, in COLUMN_ORDER. Used for arrow navigation.
|
||||
// 排序与看板视觉一致:完成列按 PR 合并时间降序,其余列按工单号降序。
|
||||
// 键盘导航只在筛选后可见的卡片之间走,和看板显示保持一致。
|
||||
const ordered = useMemo<Issue[][]>(() => {
|
||||
if (!readyIssues)
|
||||
return []
|
||||
const visible = filterIssues(readyIssues, filterQuery)
|
||||
return COLUMN_ORDER.map(col =>
|
||||
readyIssues.filter(i => i.column === col).sort((a, b) => compareIssuesInColumn(col, a, b)),
|
||||
visible.filter(i => i.column === col).sort((a, b) => compareIssuesInColumn(col, a, b)),
|
||||
)
|
||||
}, [readyIssues])
|
||||
}, [readyIssues, filterQuery])
|
||||
|
||||
// Resolved selected issue (or null) for the detail panel.
|
||||
const selectedIssue = useMemo<Issue | null>(() => {
|
||||
@@ -312,6 +318,8 @@ export function App() {
|
||||
{state.status === 'loading' && (
|
||||
<>
|
||||
<PanelHeader
|
||||
filterQuery={filterQuery}
|
||||
onFilterChange={setFilterQuery}
|
||||
onRefresh={refresh}
|
||||
refreshing={refreshing}
|
||||
scope={scope}
|
||||
@@ -339,6 +347,8 @@ export function App() {
|
||||
{state.status === 'ready' && (
|
||||
<>
|
||||
<PanelHeader
|
||||
filterQuery={filterQuery}
|
||||
onFilterChange={setFilterQuery}
|
||||
onRefresh={refresh}
|
||||
refreshing={refreshing}
|
||||
scope={scope}
|
||||
@@ -361,6 +371,7 @@ export function App() {
|
||||
<div className="min-h-0 flex-1 overflow-hidden">
|
||||
<KanbanBoard
|
||||
issues={state.issues}
|
||||
filterQuery={filterQuery}
|
||||
onIssuesChange={setIssues}
|
||||
onCreateIssue={() => setShowNewIssueModal(true)}
|
||||
selectedId={selectedId}
|
||||
@@ -445,6 +456,8 @@ export function App() {
|
||||
{state.status === 'error' && (
|
||||
<>
|
||||
<PanelHeader
|
||||
filterQuery={filterQuery}
|
||||
onFilterChange={setFilterQuery}
|
||||
onRefresh={refresh}
|
||||
refreshing={refreshing}
|
||||
scope={scope}
|
||||
|
||||
Reference in new issue
Block a user