feat: 计划转交功能 + 关联需求增强(描述/变更/类型/日期)
一、调研/产品方案/UI设计 - 转交功能: - 未开始/进行中状态可转交给其他参与人员 - 转交按钮打开内联选择器,从 version.members 选人 - 确认后更新 plan.owner 二、关联需求表格增强: - 描述列:超出宽度省略号,hover 显示完整 title 属性 - 需求类型列:原始需求(灰色)/ 变更需求(橙色) - 变更原因列:显示变更原因标签 - 添加日期列:显示 createdAt 三、需求变更功能: - 新增"需求变更"按钮(橙色边框) - 变更表单:变更人员(从参与人员选)、变更原因(6种)、 需求概述、需求详细 - 变更原因选项:需求理解偏差导致返工、反馈导致返工、 实现难度超预期、上游交付延误、范围蔓延、其他 - 创建后自动关联到当前版本,标记为变更需求 四、数据模型扩展(lib/requirement.ts): - 新增 reqType: 'original' | 'change' - 新增 changeReason: ChangeReason - 新增 changeBy: string - 导出 CHANGE_REASON_LABEL 常量 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
'use client';
|
||||
|
||||
import { useState } from 'react';
|
||||
import { Plus, Pencil, Trash2, X, Check, ExternalLink, FileUp, Link2, Play } from 'lucide-react';
|
||||
import { Plus, Pencil, Trash2, X, Check, ExternalLink, FileUp, Link2, Play, ArrowRightLeft } from 'lucide-react';
|
||||
import type { VersionPlan, PlanTask } from '@/lib/version-plan';
|
||||
import { calcPlanDuration, formatDuration, calcTotalDuration, calcPlanProgress, calcLinkedReqProgress } from '@/lib/version-plan';
|
||||
|
||||
@@ -11,6 +11,7 @@ interface Props {
|
||||
versionDeadline?: string;
|
||||
currentUserName: string;
|
||||
planType: 'research' | 'product' | 'ui';
|
||||
versionMembers: { role: string; name: string }[];
|
||||
linkedRequirements?: { id: string; title: string; code: string; productOwner?: string }[];
|
||||
onCreate: (data: Omit<VersionPlan, 'id' | 'createdAt'>) => void;
|
||||
onUpdate: (id: string, data: Partial<VersionPlan>) => void;
|
||||
@@ -26,10 +27,12 @@ const STATUS_STYLE = {
|
||||
};
|
||||
const STATUS_LABEL = { pending: '未开始', in_progress: '进行中', completed: '已完成' };
|
||||
|
||||
export function PlanTab({ plans, versionId, versionDeadline, currentUserName, planType, linkedRequirements, onCreate, onUpdate, onComplete, onDelete }: Props) {
|
||||
export function PlanTab({ plans, versionId, versionDeadline, currentUserName, planType, versionMembers, linkedRequirements, onCreate, onUpdate, onComplete, onDelete }: Props) {
|
||||
const [showCreateModal, setShowCreateModal] = useState(false);
|
||||
const [editingPlan, setEditingPlan] = useState<VersionPlan | null>(null);
|
||||
const [completingPlan, setCompletingPlan] = useState<VersionPlan | null>(null);
|
||||
const [transferPlanId, setTransferPlanId] = useState<string | null>(null);
|
||||
const [transferTo, setTransferTo] = useState('');
|
||||
|
||||
const typePlans = plans.filter((p) => p.versionId === versionId && p.type === planType);
|
||||
const totalDuration = calcTotalDuration(typePlans);
|
||||
@@ -189,6 +192,9 @@ export function PlanTab({ plans, versionId, versionDeadline, currentUserName, pl
|
||||
)}
|
||||
{plan.status !== 'completed' && (
|
||||
<>
|
||||
<button onClick={() => setTransferPlanId(plan.id)} className="h-7 w-7 flex items-center justify-center rounded-md text-blue-500 hover:bg-blue-50" title="转交">
|
||||
<ArrowRightLeft className="h-3.5 w-3.5" />
|
||||
</button>
|
||||
<button onClick={() => setEditingPlan(plan)} className="h-7 w-7 flex items-center justify-center rounded-md text-[var(--ink-muted)] hover:bg-[var(--bg-subtle)]" title="编辑">
|
||||
<Pencil className="h-3.5 w-3.5" />
|
||||
</button>
|
||||
@@ -199,6 +205,17 @@ export function PlanTab({ plans, versionId, versionDeadline, currentUserName, pl
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
{transferPlanId === plan.id && (
|
||||
<div className="mt-3 pt-3 border-t border-[var(--line)] flex items-center gap-2">
|
||||
<span className="text-[11px] text-[var(--ink-muted)]">转交给:</span>
|
||||
<select value={transferTo} onChange={(e) => setTransferTo(e.target.value)} className="h-7 flex-1 rounded-lg border border-[var(--line)] px-2 text-[12px] focus:border-[var(--accent)] focus:outline-none">
|
||||
<option value="">选择参与人员</option>
|
||||
{versionMembers.filter((m) => m.name !== plan.owner).map((m) => <option key={`${m.role}-${m.name}`} value={m.name}>{m.name}</option>)}
|
||||
</select>
|
||||
<button onClick={() => { if (transferTo) { onUpdate(plan.id, { owner: transferTo }); setTransferPlanId(null); setTransferTo(''); } }} disabled={!transferTo} className="h-7 px-2.5 rounded-lg text-[11px] font-medium bg-blue-500 text-white disabled:opacity-50">确认</button>
|
||||
<button onClick={() => { setTransferPlanId(null); setTransferTo(''); }} className="h-7 px-2 text-[11px] text-[var(--ink-muted)]">取消</button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
|
||||
Reference in New Issue
Block a user