feat(v2.4): 切换需求池领域主写
This commit is contained in:
@@ -432,7 +432,6 @@ function RequirementsPageContent() {
|
||||
}, [scopedRequirements, search, statusFilter, priorityFilter, typeFilter, versionFilter, dateSort, requirementDevStatusMap]);
|
||||
|
||||
const appDataRequirementById = useMemo(() => new Map(requirements.map((item) => [item.id, item])), [requirements]);
|
||||
const appDataRequirementIds = useMemo(() => new Set(appDataRequirementById.keys()), [appDataRequirementById]);
|
||||
const usingV22Requirements = Boolean(v22RequirementQuery) && !v22RequirementsFailed;
|
||||
const appTotalPages = Math.max(1, Math.ceil(filtered.length / pageSize));
|
||||
const appPage = Math.min(page, appTotalPages);
|
||||
@@ -450,7 +449,7 @@ function RequirementsPageContent() {
|
||||
: filtered.length;
|
||||
const displayPage = usingV22Requirements ? page : appPage;
|
||||
const displayedScopeCount = usingV22Requirements ? total : scopedRequirements.length;
|
||||
const canMutateRequirement = (req: Requirement) => requirementsLoaded && appDataRequirementIds.has(req.id);
|
||||
const canMutateRequirement = (req: Requirement) => Boolean(req.productId);
|
||||
const handlePageChange = (nextPage: number) => {
|
||||
if (usingV22Requirements && nextPage > page && !v22CursorByPage[nextPage]) return;
|
||||
setPage(nextPage);
|
||||
@@ -789,7 +788,7 @@ function RequirementsPageContent() {
|
||||
{canMutateRequirement(req) && req.status === 'pending_review' && (
|
||||
<>
|
||||
<button
|
||||
onClick={() => updateRequirement(req.id, { status: 'adopted' })}
|
||||
onClick={() => updateRequirement(req.id, { productId: req.productId, status: 'adopted' })}
|
||||
className={`${REQUIREMENT_ACTION_BUTTON_BASE_CLASS} ${REQUIREMENT_ACTION_BUTTON_TONE_CLASS.success}`}
|
||||
title="采纳"
|
||||
aria-label="采纳需求"
|
||||
@@ -809,7 +808,7 @@ function RequirementsPageContent() {
|
||||
{/* 已采纳/已规划:关闭 */}
|
||||
{canMutateRequirement(req) && (req.status === 'adopted' || req.status === 'planned') && canCloseRequirement(req.id, devTasksByRequirement.get(req.id) ?? []) && (
|
||||
<button
|
||||
onClick={() => updateRequirement(req.id, { status: 'closed' })}
|
||||
onClick={() => updateRequirement(req.id, { productId: req.productId, status: 'closed' })}
|
||||
className={`${REQUIREMENT_ACTION_BUTTON_BASE_CLASS} ${REQUIREMENT_ACTION_BUTTON_TONE_CLASS.warning}`}
|
||||
title="关闭"
|
||||
aria-label="关闭需求"
|
||||
@@ -820,7 +819,7 @@ function RequirementsPageContent() {
|
||||
{/* 删除:待评审/已拒绝/已关闭(已采纳后不能直接删,需走拒绝) */}
|
||||
{canMutateRequirement(req) && (['pending_review', 'rejected', 'closed'] as const).includes(req.status as any) && (
|
||||
<button
|
||||
onClick={() => deleteRequirement(req.id)}
|
||||
onClick={() => deleteRequirement(req.id, req.productId)}
|
||||
className={`${REQUIREMENT_ACTION_BUTTON_BASE_CLASS} ${REQUIREMENT_ACTION_BUTTON_TONE_CLASS.danger}`}
|
||||
title="删除"
|
||||
aria-label="删除需求"
|
||||
@@ -854,10 +853,10 @@ function RequirementsPageContent() {
|
||||
resolveVersionName={resolveVersionName}
|
||||
onClose={() => setViewingReq(null)}
|
||||
onEdit={canMutateRequirement(viewingReq) && canEditRequirement(viewingReq.id, devTasksByRequirement.get(viewingReq.id) ?? []) ? () => handleEdit(viewingReq) : undefined}
|
||||
onAdopt={canMutateRequirement(viewingReq) ? () => { updateRequirement(viewingReq.id, { status: 'adopted' }); setViewingReq(null); } : undefined}
|
||||
onAdopt={canMutateRequirement(viewingReq) ? () => { updateRequirement(viewingReq.id, { productId: viewingReq.productId, status: 'adopted' }); setViewingReq(null); } : undefined}
|
||||
onReject={canMutateRequirement(viewingReq) ? () => { setRejectingReq(viewingReq); setRejectReason(''); setViewingReq(null); } : undefined}
|
||||
onCloseReq={canMutateRequirement(viewingReq) && canCloseRequirement(viewingReq.id, devTasksByRequirement.get(viewingReq.id) ?? []) ? () => { updateRequirement(viewingReq.id, { status: 'closed' }); setViewingReq(null); } : undefined}
|
||||
onDelete={canMutateRequirement(viewingReq) ? () => { deleteRequirement(viewingReq.id); setViewingReq(null); } : undefined}
|
||||
onCloseReq={canMutateRequirement(viewingReq) && canCloseRequirement(viewingReq.id, devTasksByRequirement.get(viewingReq.id) ?? []) ? () => { updateRequirement(viewingReq.id, { productId: viewingReq.productId, status: 'closed' }); setViewingReq(null); } : undefined}
|
||||
onDelete={canMutateRequirement(viewingReq) ? () => { deleteRequirement(viewingReq.id, viewingReq.productId); setViewingReq(null); } : undefined}
|
||||
/>
|
||||
)}
|
||||
|
||||
@@ -882,7 +881,7 @@ function RequirementsPageContent() {
|
||||
data.versionId,
|
||||
data.versionId !== editingReq.versionId ? currentUserName : editingReq.addedToVersionBy,
|
||||
);
|
||||
updateRequirement(editingReq.id, { ...data, ...versionPatch });
|
||||
updateRequirement(editingReq.id, { ...data, productId: editingReq.productId, ...versionPatch });
|
||||
} else {
|
||||
createRequirement(data);
|
||||
}
|
||||
@@ -947,7 +946,7 @@ function RequirementsPageContent() {
|
||||
<button
|
||||
onClick={() => {
|
||||
if (!rejectReason.trim()) return;
|
||||
updateRequirement(rejectingReq.id, { status: 'rejected', description: rejectingReq.description + `\n\n【拒绝原因】${rejectReason.trim()}` });
|
||||
updateRequirement(rejectingReq.id, { productId: rejectingReq.productId, status: 'rejected', description: rejectingReq.description + `\n\n【拒绝原因】${rejectReason.trim()}` });
|
||||
setRejectingReq(null);
|
||||
}}
|
||||
disabled={!rejectReason.trim()}
|
||||
|
||||
@@ -358,7 +358,10 @@ export default function VersionDetailPage() {
|
||||
alert('版本关联数据正在加载,请稍后再删除。');
|
||||
return;
|
||||
}
|
||||
scopedVersionData.requirements.forEach((r) => updateRequirement(r.id, getRequirementVersionUnlinkPatch(r)));
|
||||
scopedVersionData.requirements.forEach((r) => updateRequirement(r.id, {
|
||||
productId: r.productId,
|
||||
...getRequirementVersionUnlinkPatch(r),
|
||||
}));
|
||||
// 清理计划任务
|
||||
scopedVersionData.plans.forEach((p) => deletePlan(p.id));
|
||||
// 清理开发任务
|
||||
@@ -988,16 +991,22 @@ export default function VersionDetailPage() {
|
||||
onLink={(ids, addedBy) => {
|
||||
if (versionReadonly || !requirementWriteReady) return;
|
||||
ids.forEach((id) => {
|
||||
const requirement = requirements.find((item) => item.id === id);
|
||||
const requirement = displayRequirements.find((item) => item.id === id);
|
||||
if (!requirement) return;
|
||||
updateRequirement(id, getRequirementVersionSelectionPatch(requirement, version.id, addedBy));
|
||||
updateRequirement(id, {
|
||||
productId: requirement.productId,
|
||||
...getRequirementVersionSelectionPatch(requirement, version.id, addedBy),
|
||||
});
|
||||
});
|
||||
}}
|
||||
onUnlink={(id) => {
|
||||
if (versionReadonly || !requirementWriteReady) return;
|
||||
const requirement = requirements.find((item) => item.id === id);
|
||||
const requirement = displayRequirements.find((item) => item.id === id);
|
||||
if (!requirement) return;
|
||||
updateRequirement(id, getRequirementVersionUnlinkPatch(requirement));
|
||||
updateRequirement(id, {
|
||||
productId: requirement.productId,
|
||||
...getRequirementVersionUnlinkPatch(requirement),
|
||||
});
|
||||
}}
|
||||
onCreateChange={(data) => {
|
||||
if (versionReadonly || !requirementWriteReady) return;
|
||||
@@ -1039,7 +1048,10 @@ export default function VersionDetailPage() {
|
||||
if (versionReadonly || !planWriteReady) return;
|
||||
createPlan(data);
|
||||
if ((pt === 'product') && data.linkedRequirementIds?.length) {
|
||||
data.linkedRequirementIds.forEach((rid) => updateRequirement(rid, { productOwner: data.owner }));
|
||||
data.linkedRequirementIds.forEach((rid) => {
|
||||
const requirement = displayRequirements.find((item) => item.id === rid);
|
||||
if (requirement) updateRequirement(rid, { productId: requirement.productId, productOwner: data.owner });
|
||||
});
|
||||
}
|
||||
// 同步版本状态:计划开始时间<=今天,版本进入对应阶段
|
||||
const today = new Date().toISOString().slice(0, 10);
|
||||
@@ -1052,7 +1064,10 @@ export default function VersionDetailPage() {
|
||||
if (versionReadonly || !planWriteReady) return;
|
||||
updatePlan(id, data);
|
||||
if ((pt === 'product') && data.linkedRequirementIds && data.owner) {
|
||||
data.linkedRequirementIds.forEach((rid) => updateRequirement(rid, { productOwner: data.owner }));
|
||||
data.linkedRequirementIds.forEach((rid) => {
|
||||
const requirement = displayRequirements.find((item) => item.id === rid);
|
||||
if (requirement) updateRequirement(rid, { productId: requirement.productId, productOwner: data.owner });
|
||||
});
|
||||
}
|
||||
}}
|
||||
onComplete={(id, result) => {
|
||||
|
||||
@@ -159,7 +159,9 @@ function VersionsPageContent() {
|
||||
setOpenMenuId(null);
|
||||
if (action === 'delete') {
|
||||
if (!confirm('确认删除该版本?关联的需求会回到需求池。')) return;
|
||||
requirements.filter((r) => r.versionId === version.id).forEach((r) => updateRequirement(r.id, getRequirementVersionUnlinkPatch(r)));
|
||||
requirements
|
||||
.filter((r) => r.versionId === version.id)
|
||||
.forEach((r) => updateRequirement(r.id, { productId: r.productId, ...getRequirementVersionUnlinkPatch(r) }));
|
||||
deleteVersion(version.productId, version.id);
|
||||
return;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user