feat(版本): 完善计划覆盖与工作台待办

关键改动:

- 增加计划日志汇总、需求覆盖草稿校验与对应测试

- 抽取工作台工作项 Hook,补充待办计数能力

- 优化版本详情中计划、任务、测试用例和 Bug 的筛选与展示

Co-Authored-By: Codex GPT-5 <codex@openai.com>
This commit is contained in:
Script Generator
2026-06-30 13:43:18 +08:00
parent a6a4d7d3d9
commit 52a88626d4
23 changed files with 1082 additions and 281 deletions

View File

@@ -7,6 +7,7 @@ import { useRequirementStore } from '@/stores/useRequirementStore';
import { useMemberStore } from '@/stores/useMemberStore';
import { useTaskCategoryStore } from '@/stores/useTaskCategoryStore';
import { useAuthStore } from '@/stores/useAuthStore';
import { FilterSelect } from '@/components/FilterSelect';
import { WorkDateTimePicker } from '@/components/WorkDateTimePicker';
import type { Priority } from '@/lib/derive';
import { getCategoriesByGroup, getDefaultCategoryByGroup } from '@/lib/task-category';
@@ -123,25 +124,43 @@ export function TestCaseCreateModal({ versionId, requirementIds, roundNo, onClos
</div>
<div>
<label className="block text-[12px] text-[var(--ink-soft)] mb-1"></label>
<select value={requirementId} onChange={(e) => { setRequirementId(e.target.value); const r = versionReqs.find((x) => x.id === e.target.value); if (r) setPriority(r.priority); }} className="h-9 w-full rounded-lg border border-[var(--line)] bg-[var(--bg)] px-3 text-[13px] focus:border-[var(--accent)] focus:outline-none">
<option value=""></option>
{versionReqs.map((r) => <option key={r.id} value={r.id}>{r.code} {r.title}</option>)}
</select>
<FilterSelect
value={requirementId || 'all'}
onChange={(value) => {
const nextValue = value === 'all' ? '' : value;
setRequirementId(nextValue);
const r = versionReqs.find((x) => x.id === nextValue);
if (r) setPriority(r.priority);
}}
options={versionReqs.map((r) => ({ value: r.id, label: `${r.code} ${r.title}` }))}
allLabel="不关联需求"
className="w-full"
labelClassName="max-w-[calc(100%-20px)]"
/>
</div>
<div className="grid grid-cols-2 gap-3">
<div>
<label className="block text-[12px] text-[var(--ink-soft)] mb-1"></label>
<select value={priority} onChange={(e) => setPriority(e.target.value as Priority)} className="h-9 w-full rounded-lg border border-[var(--line)] bg-[var(--bg)] px-3 text-[13px] focus:border-[var(--accent)] focus:outline-none">
{(['P0','P1','P2','P3'] as Priority[]).map((p) => <option key={p} value={p}>{p}</option>)}
</select>
<FilterSelect
value={priority}
onChange={(value) => setPriority(value as Priority)}
options={(['P0', 'P1', 'P2', 'P3'] as Priority[]).map((p) => ({ value: p, label: p }))}
showAllOption={false}
className="w-full"
labelClassName="max-w-[calc(100%-20px)]"
/>
</div>
<div>
<label className="block text-[12px] text-[var(--ink-soft)] mb-1"> *</label>
<select value={categoryId} onChange={(e) => handleCategoryChange(e.target.value)} className="h-9 w-full rounded-lg border border-[var(--line)] bg-[var(--bg)] px-3 text-[13px] focus:border-[var(--accent)] focus:outline-none">
{testCategories.map((category) => (
<option key={category.id} value={category.id}>{category.name}</option>
))}
</select>
<FilterSelect
value={categoryId}
onChange={handleCategoryChange}
options={testCategories.map((category) => ({ value: category.id, label: category.name }))}
placeholder="选择任务类型"
showAllOption={false}
className="w-full"
labelClassName="max-w-[calc(100%-20px)]"
/>
</div>
<div>
<label className="block text-[12px] text-[var(--ink-soft)] mb-1"> *</label>
@@ -158,10 +177,14 @@ export function TestCaseCreateModal({ versionId, requirementIds, roundNo, onClos
</div>
<div>
<label className="block text-[12px] text-[var(--ink-soft)] mb-1"></label>
<select value={assigneeId} onChange={(e) => setAssigneeId(e.target.value)} className="h-9 w-full rounded-lg border border-[var(--line)] bg-[var(--bg)] px-3 text-[13px] focus:border-[var(--accent)] focus:outline-none">
<option value=""></option>
{members.map((m) => <option key={m.id} value={m.name}>{m.name}</option>)}
</select>
<FilterSelect
value={assigneeId || 'all'}
onChange={(value) => setAssigneeId(value === 'all' ? '' : value)}
options={members.map((m) => ({ value: m.name, label: m.name }))}
allLabel="选择负责人"
className="w-full"
labelClassName="max-w-[calc(100%-20px)]"
/>
</div>
</div>
<div className="grid grid-cols-2 gap-3">