fix(intake): streamline workflow node headers
This commit is contained in:
parent
6a8e2da6d7
commit
94f7c0a302
@ -17,7 +17,7 @@ import {
|
||||
useRef,
|
||||
useState,
|
||||
} from "react";
|
||||
import { ArrowLeft, Ban, CheckCircle2, Search } from "lucide-react";
|
||||
import { ArrowLeft, Ban, Search } from "lucide-react";
|
||||
import CameraScannerModal from "@/components/mobile/CameraScannerModal";
|
||||
import { ModelPreviewPanel } from "@/components/platform/ModelPreviewPanel";
|
||||
import Button from "@/components/ui/Button";
|
||||
@ -1182,8 +1182,6 @@ const PROJECT_STATUS_COPY: Record<
|
||||
saveMessage: "立项已通过,已展开产品资料、样品评审、基础信息和后续节点",
|
||||
},
|
||||
};
|
||||
const WORKFLOW_DATE_TIME_ZONE = "Asia/Shanghai";
|
||||
|
||||
function getAutoProductYear(createdAt?: string) {
|
||||
const parsedDate = createdAt ? new Date(createdAt) : null;
|
||||
const sourceDate =
|
||||
@ -1194,27 +1192,6 @@ function getAutoProductYear(createdAt?: string) {
|
||||
return String(sourceDate.getFullYear());
|
||||
}
|
||||
|
||||
function formatWorkflowDateTime(value: string) {
|
||||
const parsedDate = new Date(value);
|
||||
if (Number.isNaN(parsedDate.getTime())) return value;
|
||||
|
||||
const parts = new Intl.DateTimeFormat("zh-CN", {
|
||||
timeZone: WORKFLOW_DATE_TIME_ZONE,
|
||||
year: "numeric",
|
||||
month: "2-digit",
|
||||
day: "2-digit",
|
||||
hour: "2-digit",
|
||||
minute: "2-digit",
|
||||
second: "2-digit",
|
||||
hourCycle: "h23",
|
||||
}).formatToParts(parsedDate);
|
||||
const partMap = Object.fromEntries(
|
||||
parts.map((part) => [part.type, part.value]),
|
||||
);
|
||||
|
||||
return `${partMap.year}-${partMap.month}-${partMap.day} ${partMap.hour}:${partMap.minute}:${partMap.second}`;
|
||||
}
|
||||
|
||||
|
||||
const UPLOAD_CATEGORY_OPTIONS: UploadCategoryKey[] = [
|
||||
"未分类",
|
||||
@ -8159,21 +8136,6 @@ export default function NonIntegratedDetailClient({
|
||||
PRODUCT_PRICING_WORKSPACE_CATEGORY,
|
||||
displayedProductPricingRowsComplete,
|
||||
);
|
||||
const activeWorkflowNodeCategory: WorkflowNodeCategory =
|
||||
documentWorkspaceView === "detail"
|
||||
? NON_INTEGRATED_BASIC_INFO_WORKSPACE_CATEGORY
|
||||
: activeDocumentWorkspaceCategory;
|
||||
const activeWorkflowNodeHasContent =
|
||||
activeWorkflowNodeCategory === NON_INTEGRATED_BASIC_INFO_WORKSPACE_CATEGORY
|
||||
? basicInfoHasContent
|
||||
: activeDocumentWorkspaceHasContent;
|
||||
const activeWorkflowNodeApprovedAt =
|
||||
documentWorkspaceApprovedAt[activeWorkflowNodeCategory] ?? "";
|
||||
const activeWorkflowNodeIsApproved = isWorkflowNodeConfirmed(
|
||||
activeWorkflowNodeCategory,
|
||||
activeWorkflowNodeHasContent,
|
||||
);
|
||||
const activeWorkflowNodeNeedsApproval = !activeWorkflowNodeIsApproved;
|
||||
const basicInfoIsApproved = isWorkflowNodeConfirmed(
|
||||
NON_INTEGRATED_BASIC_INFO_WORKSPACE_CATEGORY,
|
||||
basicInfoHasContent,
|
||||
@ -11505,7 +11467,7 @@ export default function NonIntegratedDetailClient({
|
||||
);
|
||||
|
||||
if (!hasSyncedLedgerRecord) {
|
||||
setSaveMessage("请先点击顶部未确认,确认专利申请后再查看明细");
|
||||
setSaveMessage("请先确认专利申请后再查看明细");
|
||||
setSaveMessageType("error");
|
||||
return;
|
||||
}
|
||||
@ -11635,8 +11597,34 @@ export default function NonIntegratedDetailClient({
|
||||
);
|
||||
}
|
||||
|
||||
async function handleApproveCurrentWorkflowNode() {
|
||||
await handleConfirmWorkflowNode(activeWorkflowNodeCategory);
|
||||
function renderBasicInfoConfirmationAction() {
|
||||
if (mode === "create") return null;
|
||||
|
||||
if (basicInfoIsApproved) {
|
||||
return (
|
||||
<span
|
||||
data-testid="basic-info-confirmed"
|
||||
className={DOCUMENT_WORKSPACE_CONFIRMED_CLASS}
|
||||
>
|
||||
已确认
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<DocumentWorkspaceActionButton
|
||||
data-testid="basic-info-confirm"
|
||||
disabled={isReadOnly || !basicInfoHasContent}
|
||||
title={basicInfoHasContent ? undefined : "基础信息还没有资料,暂不能确认"}
|
||||
onClick={() =>
|
||||
void handleConfirmWorkflowNode(
|
||||
NON_INTEGRATED_BASIC_INFO_WORKSPACE_CATEGORY,
|
||||
)
|
||||
}
|
||||
>
|
||||
确认基础信息
|
||||
</DocumentWorkspaceActionButton>
|
||||
);
|
||||
}
|
||||
|
||||
async function handleAbandonCurrentProduct() {
|
||||
@ -11669,110 +11657,6 @@ export default function NonIntegratedDetailClient({
|
||||
}
|
||||
}
|
||||
|
||||
function handleReturnToProductDetail() {
|
||||
if (embedded && onReturnToList) {
|
||||
onReturnToList();
|
||||
return;
|
||||
}
|
||||
|
||||
router.push("/intake");
|
||||
}
|
||||
|
||||
function renderWorkflowActionBar() {
|
||||
if (mode === "create") return null;
|
||||
|
||||
const activeCategoryLabel = getDocumentWorkspaceCategoryLabel(
|
||||
activeWorkflowNodeCategory,
|
||||
);
|
||||
const approveDisabled =
|
||||
isReadOnly ||
|
||||
!activeWorkflowNodeNeedsApproval ||
|
||||
!activeWorkflowNodeHasContent;
|
||||
const approveLabel = activeWorkflowNodeNeedsApproval
|
||||
? "未确认"
|
||||
: "已确认";
|
||||
const approvalTimeText = activeWorkflowNodeApprovedAt
|
||||
? formatWorkflowDateTime(activeWorkflowNodeApprovedAt)
|
||||
: activeWorkflowNodeHasContent
|
||||
? "未确认"
|
||||
: "无资料";
|
||||
|
||||
return (
|
||||
<div className="sticky top-0 z-20 mb-2 shrink-0 bg-[var(--color-surface-container)]">
|
||||
<div
|
||||
className={`grid min-h-[28px] grid-cols-[minmax(0,1fr)_auto] items-center gap-1.5 rounded-apple-lg border px-2.5 py-1 shadow-apple-xs ${
|
||||
isFlowAbandoned
|
||||
? "border-zinc-200 bg-zinc-100 text-zinc-500"
|
||||
: "border-apple-separator bg-apple-bg-primary text-apple-label-primary"
|
||||
}`}
|
||||
>
|
||||
<div className="min-w-0 truncate text-[11px]">
|
||||
<span className="font-apple-medium">当前节点</span>
|
||||
<span className="mx-1.5 text-apple-label-tertiary">/</span>
|
||||
<span>{activeCategoryLabel}</span>
|
||||
<span className="mx-1.5 text-apple-label-tertiary">/</span>
|
||||
<span
|
||||
className={
|
||||
activeWorkflowNodeApprovedAt
|
||||
? "text-[#156b3f]"
|
||||
: "text-apple-label-secondary"
|
||||
}
|
||||
>
|
||||
{approvalTimeText}
|
||||
</span>
|
||||
{isFlowAbandoned ? (
|
||||
<span className="ml-2 rounded-full bg-zinc-200 px-2 py-0.5 text-[11px] text-zinc-600">
|
||||
已废弃,只能查看和下载
|
||||
</span>
|
||||
) : null}
|
||||
</div>
|
||||
<div className="flex shrink-0 items-center justify-end gap-1.5 whitespace-nowrap">
|
||||
<Button
|
||||
size="small"
|
||||
data-testid="workflow-approve-current-node"
|
||||
onClick={() => void handleApproveCurrentWorkflowNode()}
|
||||
disabled={approveDisabled}
|
||||
title={
|
||||
activeWorkflowNodeHasContent
|
||||
? undefined
|
||||
: "当前节点还没有资料,暂不能确认"
|
||||
}
|
||||
className="!min-h-6 !px-2 !py-0 text-[11px] [&>span]:gap-1"
|
||||
>
|
||||
<CheckCircle2 className="h-3 w-3" aria-hidden="true" />
|
||||
{approveLabel}
|
||||
</Button>
|
||||
{canAbandonProduct ? (
|
||||
<Button
|
||||
variant="plain"
|
||||
size="small"
|
||||
data-testid="workflow-abandon-product"
|
||||
onClick={() => void handleAbandonCurrentProduct()}
|
||||
disabled={mode !== "edit" || isFlowAbandoned}
|
||||
className="!min-h-6 !border-[#f1c4ca] !px-2 !py-0 !text-[#a32135] text-[11px] hover:!bg-[#fff7f8] [&>span]:gap-1"
|
||||
>
|
||||
<Ban className="h-3 w-3" aria-hidden="true" />
|
||||
废弃
|
||||
</Button>
|
||||
) : null}
|
||||
{!linkEntryView ? (
|
||||
<Button
|
||||
variant="gray"
|
||||
size="small"
|
||||
data-testid="workflow-return-detail"
|
||||
onClick={handleReturnToProductDetail}
|
||||
className="!min-h-6 !px-2 !py-0 text-[11px] [&>span]:gap-1"
|
||||
>
|
||||
<ArrowLeft className="h-3 w-3" aria-hidden="true" />
|
||||
返回详情
|
||||
</Button>
|
||||
) : null}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
async function handleUploadFilesChange(
|
||||
event: ChangeEvent<HTMLInputElement>,
|
||||
forcedCategory?: DocumentWorkspaceCategory,
|
||||
@ -15545,12 +15429,19 @@ function renderFactoryQualityWorkspace() {
|
||||
className="flex min-h-0 flex-1 flex-col overflow-hidden rounded-apple-xl border border-apple-separator bg-apple-bg-primary"
|
||||
>
|
||||
<div className="border-b border-apple-separator bg-apple-bg-secondary/25 px-3 py-2">
|
||||
<p className="text-[13px] font-apple-semibold text-apple-label-primary">
|
||||
开品参考填写
|
||||
</p>
|
||||
<p className="mt-0.5 text-[11px] leading-4 text-apple-label-secondary">
|
||||
开品前先记录参考图、品牌方向、类目、竞品、开品理由和设计卖点。立项通过后再补产品资料、样品评审和完整基础信息。
|
||||
</p>
|
||||
<div className="flex flex-wrap items-start justify-between gap-2">
|
||||
<div className="min-w-0 flex-1">
|
||||
<p className="text-[13px] font-apple-semibold text-apple-label-primary">
|
||||
开品参考填写
|
||||
</p>
|
||||
<p className="mt-0.5 text-[11px] leading-4 text-apple-label-secondary">
|
||||
开品前先记录参考图、品牌方向、类目、竞品、开品理由和设计卖点。立项通过后再补产品资料、样品评审和完整基础信息。
|
||||
</p>
|
||||
</div>
|
||||
<div className={DOCUMENT_WORKSPACE_ACTION_GROUP_CLASS}>
|
||||
{renderDocumentWorkspaceConfirmationAction()}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="min-h-0 flex-1 overflow-y-auto p-2.5">
|
||||
@ -15801,7 +15692,7 @@ function renderFactoryQualityWorkspace() {
|
||||
样品评审后判断是否申请专利;申请后记录申请号、结果和申请缴费。
|
||||
</p>
|
||||
</div>
|
||||
<div className="flex w-full shrink-0 items-center justify-end gap-3 sm:w-auto">
|
||||
<div className="flex w-full shrink-0 flex-wrap items-center justify-end gap-3 sm:w-auto">
|
||||
<span className="shrink-0 text-xs text-apple-label-secondary">
|
||||
是否申请专利
|
||||
</span>
|
||||
@ -15840,6 +15731,9 @@ function renderFactoryQualityWorkspace() {
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
<div className={DOCUMENT_WORKSPACE_ACTION_GROUP_CLASS}>
|
||||
{renderDocumentWorkspaceConfirmationAction()}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -16243,11 +16137,13 @@ function renderFactoryQualityWorkspace() {
|
||||
先汇总前置资料,再记录会议判断。内容字段后面可以继续按你们真实会议优化。
|
||||
</p>
|
||||
</div>
|
||||
<span
|
||||
className={`rounded-full px-3 py-1 text-xs font-apple-medium ${statusCopy.badgeClassName}`}
|
||||
>
|
||||
{statusCopy.label}
|
||||
</span>
|
||||
<div className={DOCUMENT_WORKSPACE_ACTION_GROUP_CLASS}>
|
||||
<span
|
||||
className={`inline-flex h-9 items-center rounded-apple-lg border px-3 text-xs font-apple-medium ${statusCopy.badgeClassName}`}
|
||||
>
|
||||
{statusCopy.label}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -17540,16 +17436,25 @@ function renderFactoryQualityWorkspace() {
|
||||
</p>
|
||||
</div>
|
||||
{isReadOnly ? null : (
|
||||
<DocumentWorkspaceActionButton
|
||||
type="button"
|
||||
variant="plain"
|
||||
size="small"
|
||||
onClick={() =>
|
||||
handleDeleteReviewIssue(selectedReviewIssue.id)
|
||||
}
|
||||
>
|
||||
删除问题
|
||||
</DocumentWorkspaceActionButton>
|
||||
<div className={DOCUMENT_WORKSPACE_ACTION_GROUP_CLASS}>
|
||||
<DocumentWorkspaceActionButton
|
||||
type="button"
|
||||
size="small"
|
||||
onClick={handleAddReviewIssue}
|
||||
>
|
||||
新增问题
|
||||
</DocumentWorkspaceActionButton>
|
||||
<DocumentWorkspaceActionButton
|
||||
type="button"
|
||||
variant="plain"
|
||||
size="small"
|
||||
onClick={() =>
|
||||
handleDeleteReviewIssue(selectedReviewIssue.id)
|
||||
}
|
||||
>
|
||||
删除问题
|
||||
</DocumentWorkspaceActionButton>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
@ -17669,7 +17574,7 @@ function renderFactoryQualityWorkspace() {
|
||||
<div className="flex min-h-[260px] flex-1 items-center justify-center">
|
||||
<WorkspacePreviewEmptyState
|
||||
title="当前还没有评审问题"
|
||||
description="点击左侧“新增问题”后,就可以开始填写。"
|
||||
description="点击当前问题区域的“新增问题”后,就可以开始填写。"
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
@ -17774,7 +17679,6 @@ function renderFactoryQualityWorkspace() {
|
||||
isDocumentWorkspaceMode ? "overflow-hidden p-2" : "overflow-auto p-3"
|
||||
}`}
|
||||
>
|
||||
{renderWorkflowActionBar()}
|
||||
<div
|
||||
className={`rounded-apple-xl border border-apple-separator bg-apple-bg-primary shadow-apple-xs ${
|
||||
isDocumentWorkspaceMode ? "p-2" : "p-4"
|
||||
@ -17860,6 +17764,22 @@ function renderFactoryQualityWorkspace() {
|
||||
<SectionCard
|
||||
title={`${currentProductDisplayName}产品开发明细`}
|
||||
description="立项后从开品参考和立项评审自动带出基础资料;这里可以继续补正式 SPU、流程和运营配置。"
|
||||
headerActions={
|
||||
<div className={DOCUMENT_WORKSPACE_ACTION_GROUP_CLASS}>
|
||||
{canAbandonProduct ? (
|
||||
<DocumentWorkspaceActionButton
|
||||
data-testid="workflow-abandon-product"
|
||||
onClick={() => void handleAbandonCurrentProduct()}
|
||||
disabled={mode !== "edit" || isFlowAbandoned}
|
||||
className="!border-[#f1c4ca] !text-[#a32135] hover:!bg-[#fff7f8]"
|
||||
>
|
||||
<Ban className="h-3.5 w-3.5" aria-hidden="true" />
|
||||
废弃
|
||||
</DocumentWorkspaceActionButton>
|
||||
) : null}
|
||||
{renderBasicInfoConfirmationAction()}
|
||||
</div>
|
||||
}
|
||||
>
|
||||
<div className="grid gap-3 xl:grid-cols-[220px_minmax(0,1fr)]">
|
||||
<div className="rounded-apple-lg border border-apple-separator bg-apple-bg-secondary/30 p-3">
|
||||
@ -20034,12 +19954,6 @@ function renderFactoryQualityWorkspace() {
|
||||
>
|
||||
新增轮次
|
||||
</Button>
|
||||
<Button
|
||||
size="small"
|
||||
onClick={handleAddReviewIssue}
|
||||
>
|
||||
新增问题
|
||||
</Button>
|
||||
{isCurrentUserAdmin ? (
|
||||
<Button
|
||||
variant="plain"
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user