diff --git a/apps/web/app/requirements/page.tsx b/apps/web/app/requirements/page.tsx index 84332b1..70f0f02 100644 --- a/apps/web/app/requirements/page.tsx +++ b/apps/web/app/requirements/page.tsx @@ -2,7 +2,22 @@ import { RouteGuard } from '@/components/auth/Guard'; import { useEffect, useMemo, useState } from 'react'; -import { Search, Plus, Lightbulb, ArrowUp, ArrowDown, ChevronDown, ChevronRight, FolderOpen, Layers } from 'lucide-react'; +import { + Search, + Plus, + Lightbulb, + ArrowUp, + ArrowDown, + ChevronDown, + ChevronRight, + FolderOpen, + Layers, + Pencil, + Check, + XCircle, + Archive, + Trash2, +} from 'lucide-react'; import { useRequirementStore } from '@/stores/useRequirementStore'; import { useProductStore } from '@/stores/useProductStore'; import { useDevTaskStore } from '@/stores/useDevTaskStore'; @@ -40,6 +55,16 @@ const PRIORITY_COLORS: Record = { P4: 'bg-zinc-100 text-zinc-500', }; +const REQUIREMENT_ACTION_BUTTON_BASE_CLASS = + 'inline-flex h-6 w-6 shrink-0 items-center justify-center rounded text-[var(--ink-muted)] transition-colors'; + +const REQUIREMENT_ACTION_BUTTON_TONE_CLASS = { + neutral: 'hover:bg-[var(--bg-subtle)] hover:text-[var(--ink)]', + success: 'text-emerald-600 hover:bg-emerald-50', + danger: 'text-red-600 hover:bg-red-50', + warning: 'text-orange-600 hover:bg-orange-50', +} as const; + const STATUS_TABS: { key: string; label: string }[] = [ { key: 'all', label: '全部' }, { key: 'pending_review', label: '待评审' }, @@ -753,9 +778,11 @@ function RequirementsPageContent() { {canMutateRequirement(req) && (['pending_review', 'adopted', 'planned'] as const).includes(req.status as any) && canEditRequirement(req.id, devTasksByRequirement.get(req.id) ?? []) && ( )} {/* 待评审:采纳/拒绝 */} @@ -763,15 +790,19 @@ function RequirementsPageContent() { <> )} @@ -779,18 +810,22 @@ function RequirementsPageContent() { {canMutateRequirement(req) && (req.status === 'adopted' || req.status === 'planned') && canCloseRequirement(req.id, devTasksByRequirement.get(req.id) ?? []) && ( )} {/* 删除:待评审/已拒绝/已关闭(已采纳后不能直接删,需走拒绝) */} {canMutateRequirement(req) && (['pending_review', 'rejected', 'closed'] as const).includes(req.status as any) && ( )} diff --git a/apps/web/lib/requirement-table-layout.test.ts b/apps/web/lib/requirement-table-layout.test.ts index 8a18d52..018e41e 100644 --- a/apps/web/lib/requirement-table-layout.test.ts +++ b/apps/web/lib/requirement-table-layout.test.ts @@ -19,6 +19,16 @@ test('uses fixed table layout without horizontal scrolling for requirement list' assert.equal(totalWidth, 100); }); +test('reserves enough width for pending-review row actions without horizontal scrolling', () => { + const sourceColumnWidth = REQUIREMENT_TABLE_COLUMN_WIDTHS[2]; + const creatorColumnWidth = REQUIREMENT_TABLE_COLUMN_WIDTHS[9]; + const actionColumnWidth = REQUIREMENT_TABLE_COLUMN_WIDTHS[11]; + + assert.ok(sourceColumnWidth <= 10); + assert.ok(creatorColumnWidth <= 4); + assert.ok(actionColumnWidth >= 20); +}); + test('keeps requirement table headers on one line', () => { assert.match(REQUIREMENT_TABLE_HEADER_CELL_CLASS, /\bwhitespace-nowrap\b/); }); diff --git a/apps/web/lib/requirement-table-layout.ts b/apps/web/lib/requirement-table-layout.ts index 4396c82..d1521d2 100644 --- a/apps/web/lib/requirement-table-layout.ts +++ b/apps/web/lib/requirement-table-layout.ts @@ -1,11 +1,11 @@ export type RequirementTableTextColumn = 'source' | 'project' | 'type' | 'version' | 'creator'; const TEXT_COLUMN_MAX_WIDTH: Record = { - source: 'max-w-[170px]', + source: 'max-w-[120px]', project: 'max-w-[120px]', type: 'max-w-[96px]', version: 'max-w-[92px]', - creator: 'max-w-[96px]', + creator: 'max-w-[64px]', }; export const REQUIREMENT_TABLE_CONTAINER_CLASS = @@ -13,7 +13,7 @@ export const REQUIREMENT_TABLE_CONTAINER_CLASS = export const REQUIREMENT_TABLE_CLASS = 'w-full table-fixed text-left text-[13px]'; -export const REQUIREMENT_TABLE_COLUMN_WIDTHS = [6, 15, 14, 7, 6, 6, 5, 6, 7, 6, 8, 14] as const; +export const REQUIREMENT_TABLE_COLUMN_WIDTHS = [6, 15, 10, 7, 6, 6, 5, 6, 7, 4, 8, 20] as const; export const REQUIREMENT_TABLE_HEADER_CELL_CLASS = 'px-3 py-2.5 whitespace-nowrap text-[11px] font-medium uppercase tracking-wide text-[var(--ink-muted)]';