feat: 需求池重构 + 联动引擎 + 开发状态推导
架构:新建 lib/linkage-engine.ts 联动引擎 - deriveReqDevStatus(): 从 devTasks 推导需求开发状态 - canEditRequirement(): 开发中的需求不可编辑 - checkVersionChangeRisk(): 版本变更风险检查 - 所有跨模块推导逻辑集中在此文件 功能改动: 1. 导航改名"需求"→"需求池" 2. 新建需求:所属产品/项目必填,移除产品负责人字段 3. 需求表格新增"开发状态"列: 未排期/待开发/开发中/已完成(从 devTasks 实时推导) 4. 开发中的需求:编辑按钮禁用 5. 需求设 versionId → 版本详情关联需求天然兼容 (版本详情通过 filter(r.versionId === versionId) 读取) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -4,9 +4,11 @@ import { useEffect, useMemo, useState } from 'react';
|
||||
import { Search, Plus, Lightbulb, ArrowUp, ArrowDown } from 'lucide-react';
|
||||
import { useRequirementStore } from '@/stores/useRequirementStore';
|
||||
import { useProductStore } from '@/stores/useProductStore';
|
||||
import { useDevTaskStore } from '@/stores/useDevTaskStore';
|
||||
import { flattenProjects, flattenVersions } from '@/lib/derive';
|
||||
import { REQ_STATUS_LABEL, REQ_STATUS_COLOR, SOURCE_TYPE_LABEL } from '@/lib/requirement';
|
||||
import type { Requirement, RequirementStatus, SourceType } from '@/lib/requirement';
|
||||
import { deriveReqDevStatus, canEditRequirement, REQ_DEV_STATUS_LABEL, REQ_DEV_STATUS_COLOR } from '@/lib/linkage-engine';
|
||||
import { Pagination, usePagination } from '@/components/Pagination';
|
||||
import { RequirementModal } from '@/components/requirement/RequirementModal';
|
||||
import { RequirementDetail } from '@/components/requirement/RequirementDetail';
|
||||
@@ -42,6 +44,7 @@ export default function RequirementsPage() {
|
||||
addPlatform, updatePlatform, deletePlatform,
|
||||
} = useRequirementStore();
|
||||
const { overview, fetchOverview } = useProductStore();
|
||||
const { tasks: devTasks, fetchTasks: fetchDevTasks } = useDevTaskStore();
|
||||
const allProjects = useMemo(() => flattenProjects(overview), [overview]);
|
||||
const allVersions = useMemo(() => flattenVersions(overview), [overview]);
|
||||
|
||||
@@ -62,6 +65,7 @@ export default function RequirementsPage() {
|
||||
|
||||
useEffect(() => { fetchOverview(); }, [fetchOverview]);
|
||||
useEffect(() => { fetchRequirements(); }, [fetchRequirements]);
|
||||
useEffect(() => { fetchDevTasks(); }, [fetchDevTasks]);
|
||||
|
||||
// Resolve version name from overview
|
||||
const resolveVersionName = (versionId?: string): string => {
|
||||
@@ -249,7 +253,7 @@ export default function RequirementsPage() {
|
||||
<th className="px-4 py-2.5 text-[11px] font-medium uppercase tracking-wide text-[var(--ink-muted)]">状态</th>
|
||||
<th className="px-4 py-2.5 text-[11px] font-medium uppercase tracking-wide text-[var(--ink-muted)]">优先级</th>
|
||||
<th className="px-4 py-2.5 text-[11px] font-medium uppercase tracking-wide text-[var(--ink-muted)]">所属版本</th>
|
||||
<th className="px-4 py-2.5 text-[11px] font-medium uppercase tracking-wide text-[var(--ink-muted)]">产品负责人</th>
|
||||
<th className="px-4 py-2.5 text-[11px] font-medium uppercase tracking-wide text-[var(--ink-muted)]">开发状态</th>
|
||||
<th className="px-4 py-2.5 text-[11px] font-medium uppercase tracking-wide text-[var(--ink-muted)]">录入人员</th>
|
||||
<th
|
||||
className="px-4 py-2.5 text-[11px] font-medium uppercase tracking-wide text-[var(--ink-muted)] cursor-pointer select-none hover:text-[var(--ink-soft)] transition-colors"
|
||||
@@ -314,9 +318,16 @@ export default function RequirementsPage() {
|
||||
{resolveVersionName(req.versionId)}
|
||||
</td>
|
||||
|
||||
{/* 产品负责人 */}
|
||||
<td className="px-4 py-3 text-[12px] text-[var(--ink-soft)]">
|
||||
{req.productOwner || '-'}
|
||||
{/* 开发状态 */}
|
||||
<td className="px-4 py-3">
|
||||
{(() => {
|
||||
const devStatus = deriveReqDevStatus(req.id, devTasks);
|
||||
return (
|
||||
<span className={`inline-flex items-center rounded-md px-2 py-0.5 text-[10px] font-medium ${REQ_DEV_STATUS_COLOR[devStatus]}`}>
|
||||
{REQ_DEV_STATUS_LABEL[devStatus]}
|
||||
</span>
|
||||
);
|
||||
})()}
|
||||
</td>
|
||||
|
||||
{/* 录入人员 */}
|
||||
@@ -332,8 +343,8 @@ export default function RequirementsPage() {
|
||||
{/* 操作 */}
|
||||
<td className="px-4 py-3 text-right" onClick={(e) => e.stopPropagation()}>
|
||||
<div className="flex items-center justify-end gap-1">
|
||||
{/* 编辑:待评审/已采纳/已规划 */}
|
||||
{(['pending_review', 'adopted', 'planned'] as const).includes(req.status as any) && (
|
||||
{/* 编辑:待评审/已采纳/已规划,且开发中不可编辑 */}
|
||||
{(['pending_review', 'adopted', 'planned'] as const).includes(req.status as any) && canEditRequirement(req.id, devTasks) && (
|
||||
<button
|
||||
onClick={() => handleEdit(req)}
|
||||
className="h-6 px-2 rounded text-[11px] font-medium text-[var(--ink-soft)] hover:bg-[var(--bg-subtle)] transition-colors"
|
||||
|
||||
Reference in New Issue
Block a user