refactor(intake): extract product data workspace

This commit is contained in:
WuYongJie 2026-06-09 12:04:03 +08:00
parent 778d03c903
commit ffd53ec713
2 changed files with 285 additions and 199 deletions

View File

@ -46,6 +46,7 @@ import {
type BasicFieldType,
} from "./basicInfoConfig";
import { ProjectReviewWorkspace } from "./ProjectReviewWorkspace";
import { ProductDataWorkspace } from "./ProductDataWorkspace";
import {
BomBoardMaterialField,
BomEdgeBandingField,
@ -20288,71 +20289,6 @@ function renderFactoryQualityWorkspace() {
);
}
function renderProductDataPreviewContent() {
if (!effectiveSelectedWorkspaceFile) {
return (
<WorkspacePreviewEmptyState
title={workspacePreviewTitle}
description={workspacePreviewDescription}
/>
);
}
if (
effectiveSelectedWorkspacePreviewType === "image" ||
effectiveSelectedWorkspacePreviewType === "pdf"
) {
return renderDocumentWorkspacePreviewContent();
}
return (
<div
data-testid="product-data-detail-preview"
className="flex h-full w-full flex-col gap-3 rounded-apple-xl border border-dashed border-apple-separator bg-white p-4"
>
<div>
<p className="text-sm font-apple-medium text-apple-label-primary">
资料明细
</p>
<p className="mt-1 text-xs leading-5 text-apple-label-secondary">
当前分类只对 PDF
和图片做在线预览,其它文件保留明细展开,模型师按明细查看和下载即可。
</p>
</div>
<div className="grid gap-3 md:grid-cols-2">
<div className="rounded-apple-lg border border-apple-separator bg-apple-bg-secondary/20 px-3 py-2.5">
<p className="text-[11px] text-apple-label-secondary">资料分类</p>
<p className="mt-1 text-sm font-apple-medium text-apple-label-primary">
{activeProductDataGroupEntry?.label ?? "产品资料"}
</p>
</div>
<div className="rounded-apple-lg border border-apple-separator bg-apple-bg-secondary/20 px-3 py-2.5">
<p className="text-[11px] text-apple-label-secondary">文件类型</p>
<p className="mt-1 text-sm font-apple-medium text-apple-label-primary">
{getUploadFileTypeLabel(effectiveSelectedWorkspaceFile)}
</p>
</div>
<div className="rounded-apple-lg border border-apple-separator bg-apple-bg-secondary/20 px-3 py-2.5 md:col-span-2">
<p className="text-[11px] text-apple-label-secondary">文件名称</p>
<p className="mt-1 break-all text-sm font-apple-medium text-apple-label-primary">
{effectiveSelectedWorkspaceFile.name}
</p>
</div>
</div>
<div className="rounded-apple-lg border border-apple-separator bg-[#f8fafc] px-4 py-3">
<p className="text-sm font-apple-medium text-apple-label-primary">
当前文件不做在线预览
</p>
<p className="mt-1 text-xs leading-5 text-apple-label-secondary">
DWG、Excel、PPT、压缩包等资料会保留在中间明细栏,右侧只展示关键信息。需要查看完整内容时,直接点击上方“下载”即可。
</p>
</div>
</div>
);
}
function renderOpeningReferenceWorkspace() {
const topFields: {
key: OpeningReferenceFieldKey;
@ -21188,140 +21124,21 @@ function renderFactoryQualityWorkspace() {
}
function renderProductDataWorkspace() {
return (
<div
data-testid="product-data-workspace"
className="grid min-h-0 flex-1 gap-3 xl:grid-cols-[260px_320px_minmax(0,1fr)]"
>
<div className="flex min-h-0 flex-col rounded-apple-xl border border-apple-separator bg-apple-bg-primary">
<div className="border-b border-apple-separator px-3 py-3">
<p className="text-sm font-apple-medium text-apple-label-primary">
资料分类
</p>
<p className="mt-1 text-xs leading-5 text-apple-label-secondary">
左侧按资料类型分栏;上传时会自动落到当前分类。
</p>
</div>
<div className="min-h-0 flex-1 space-y-2 overflow-y-auto p-3 pr-2">
{productDataGroupEntries.map((group) => {
const isActive = activeProductDataGroup === group.key;
return (
<button
key={group.key}
type="button"
data-testid={`product-data-group-${group.key}`}
onClick={() => setActiveProductDataGroup(group.key)}
className={`w-full rounded-apple-xl border px-3 py-3 text-left transition ${
isActive
? "border-apple-blue bg-apple-blue/10"
: "border-apple-separator bg-apple-bg-secondary/20 hover:bg-apple-bg-secondary/40"
}`}
>
<div className="flex items-center justify-between gap-2">
<p className="text-sm font-apple-medium text-apple-label-primary">
{group.label}
</p>
<span
className={`rounded-full px-2 py-0.5 text-[11px] ${
isActive
? "bg-apple-blue text-white"
: "bg-apple-bg-primary text-apple-label-secondary"
}`}
>
{group.files.length} 份
</span>
</div>
<p className="mt-2 text-xs leading-5 text-apple-label-secondary">
{group.description}
</p>
</button>
);
})}
</div>
</div>
<div className="flex min-h-0 min-w-0 flex-col rounded-apple-xl border border-apple-separator bg-apple-bg-primary">
<div className="border-b border-apple-separator px-3 py-3">
<p className="text-sm font-apple-medium text-apple-label-primary">
资料明细
</p>
<p className="mt-1 text-xs text-apple-label-secondary">
当前分类:{activeProductDataGroupEntry?.label ?? "产品资料"} · 共{" "}
{activeProductDataGroupFiles.length} 份
</p>
</div>
{activeProductDataGroupFiles.length === 0 ? (
<div className="flex min-h-[240px] flex-1 flex-col items-center justify-center px-5 text-center">
<p className="text-sm font-apple-medium text-apple-label-primary">
当前还没有{activeProductDataGroupEntry?.label ?? "产品资料"}
</p>
<p className="mt-2 text-xs leading-5 text-apple-label-secondary">
点击上方“批量上传”后,这里会显示当前分类下的文件明细。
</p>
</div>
) : (
<div className="min-h-0 flex-1 space-y-2 overflow-y-auto p-3 pr-2">
{activeProductDataGroupFiles.map((file) => {
const isActive = effectiveSelectedWorkspaceFile?.id === file.id;
const previewType = getUploadFilePreviewType(file);
const supportsInlinePreview =
previewType === "image" || previewType === "pdf";
return (
<button
key={file.id}
type="button"
data-testid={`product-data-file-${file.id}`}
onClick={() => handleSelectWorkspaceFile(file.id)}
className={`w-full rounded-apple-lg border px-3 py-3 text-left transition ${
isActive
? "border-apple-blue bg-apple-blue/10"
: "border-apple-separator bg-apple-bg-secondary/20 hover:bg-apple-bg-secondary/40"
}`}
>
<div className="flex items-start justify-between gap-2">
<div className="min-w-0 flex-1">
<p className="truncate text-sm font-apple-medium text-apple-label-primary">
{file.name}
</p>
<p className="mt-1 text-[11px] text-apple-label-secondary">
{supportsInlinePreview
? "右侧可直接预览 PDF / 图片"
: "右侧展开资料明细,可直接下载"}
</p>
</div>
<span
className={`rounded-full px-2 py-0.5 text-[11px] ${
isActive
? "bg-apple-blue text-white"
: "bg-apple-bg-primary text-apple-label-secondary"
}`}
>
{getUploadFileTypeLabel(file)}
</span>
</div>
</button>
);
})}
</div>
)}
</div>
<div className="flex min-h-0 min-w-0 flex-1 flex-col rounded-apple-xl border border-apple-separator bg-apple-bg-secondary/20">
<div className="border-b border-apple-separator px-4 py-2.5">
<p className="truncate text-sm font-apple-medium text-apple-label-primary">
{workspacePreviewTitle}
</p>
<p className="mt-1 text-xs text-apple-label-secondary">
{workspacePreviewDescription}
</p>
</div>
<div className="flex min-h-0 flex-1 items-center justify-center p-3">
{renderProductDataPreviewContent()}
</div>
</div>
</div>
<ProductDataWorkspace
productDataGroupEntries={productDataGroupEntries}
activeProductDataGroup={activeProductDataGroup}
activeProductDataGroupLabel={
activeProductDataGroupEntry?.label ?? "产品资料"
}
activeProductDataGroupFiles={activeProductDataGroupFiles}
selectedWorkspaceFile={effectiveSelectedWorkspaceFile}
selectedWorkspacePreviewType={effectiveSelectedWorkspacePreviewType}
workspacePreviewTitle={workspacePreviewTitle}
workspacePreviewDescription={workspacePreviewDescription}
onActiveProductDataGroupChange={setActiveProductDataGroup}
onSelectWorkspaceFile={handleSelectWorkspaceFile}
renderDocumentPreviewContent={renderDocumentWorkspacePreviewContent}
/>
);
}

View File

@ -0,0 +1,269 @@
"use client";
import type { ReactNode } from "react";
import type { ProductDataGroup } from "./documentWorkspaceHelpers";
import type { NonIntegratedUploadFile } from "./nonIntegratedStorage";
import {
getUploadFilePreviewType,
getUploadFileTypeLabel,
type UploadFilePreviewType,
} from "./uploadFilePreview";
import { WorkspacePreviewEmptyState } from "./components/WorkspaceShell";
type ProductDataGroupEntry = {
key: ProductDataGroup;
label: string;
description: string;
files: NonIntegratedUploadFile[];
};
type ProductDataWorkspaceProps = {
productDataGroupEntries: ProductDataGroupEntry[];
activeProductDataGroup: ProductDataGroup;
activeProductDataGroupLabel: string;
activeProductDataGroupFiles: NonIntegratedUploadFile[];
selectedWorkspaceFile: NonIntegratedUploadFile | null;
selectedWorkspacePreviewType: UploadFilePreviewType;
workspacePreviewTitle: string;
workspacePreviewDescription: string;
onActiveProductDataGroupChange: (group: ProductDataGroup) => void;
onSelectWorkspaceFile: (fileId: string) => void;
renderDocumentPreviewContent: () => ReactNode;
};
export function ProductDataWorkspace({
productDataGroupEntries,
activeProductDataGroup,
activeProductDataGroupLabel,
activeProductDataGroupFiles,
selectedWorkspaceFile,
selectedWorkspacePreviewType,
workspacePreviewTitle,
workspacePreviewDescription,
onActiveProductDataGroupChange,
onSelectWorkspaceFile,
renderDocumentPreviewContent,
}: ProductDataWorkspaceProps) {
return (
<div
data-testid="product-data-workspace"
className="grid min-h-0 flex-1 gap-3 xl:grid-cols-[260px_320px_minmax(0,1fr)]"
>
<div className="flex min-h-0 flex-col rounded-apple-xl border border-apple-separator bg-apple-bg-primary">
<div className="border-b border-apple-separator px-3 py-3">
<p className="text-sm font-apple-medium text-apple-label-primary">
资料分类
</p>
<p className="mt-1 text-xs leading-5 text-apple-label-secondary">
左侧按资料类型分栏;上传时会自动落到当前分类。
</p>
</div>
<div className="min-h-0 flex-1 space-y-2 overflow-y-auto p-3 pr-2">
{productDataGroupEntries.map((group) => {
const isActive = activeProductDataGroup === group.key;
return (
<button
key={group.key}
type="button"
data-testid={`product-data-group-${group.key}`}
onClick={() => onActiveProductDataGroupChange(group.key)}
className={`w-full rounded-apple-xl border px-3 py-3 text-left transition ${
isActive
? "border-apple-blue bg-apple-blue/10"
: "border-apple-separator bg-apple-bg-secondary/20 hover:bg-apple-bg-secondary/40"
}`}
>
<div className="flex items-center justify-between gap-2">
<p className="text-sm font-apple-medium text-apple-label-primary">
{group.label}
</p>
<span
className={`rounded-full px-2 py-0.5 text-[11px] ${
isActive
? "bg-apple-blue text-white"
: "bg-apple-bg-primary text-apple-label-secondary"
}`}
>
{group.files.length} 份
</span>
</div>
<p className="mt-2 text-xs leading-5 text-apple-label-secondary">
{group.description}
</p>
</button>
);
})}
</div>
</div>
<div className="flex min-h-0 min-w-0 flex-col rounded-apple-xl border border-apple-separator bg-apple-bg-primary">
<div className="border-b border-apple-separator px-3 py-3">
<p className="text-sm font-apple-medium text-apple-label-primary">
资料明细
</p>
<p className="mt-1 text-xs text-apple-label-secondary">
当前分类:{activeProductDataGroupLabel} · 共{" "}
{activeProductDataGroupFiles.length} 份
</p>
</div>
{activeProductDataGroupFiles.length === 0 ? (
<div className="flex min-h-[240px] flex-1 flex-col items-center justify-center px-5 text-center">
<p className="text-sm font-apple-medium text-apple-label-primary">
当前还没有{activeProductDataGroupLabel}
</p>
<p className="mt-2 text-xs leading-5 text-apple-label-secondary">
点击上方“批量上传”后,这里会显示当前分类下的文件明细。
</p>
</div>
) : (
<div className="min-h-0 flex-1 space-y-2 overflow-y-auto p-3 pr-2">
{activeProductDataGroupFiles.map((file) => {
const isActive = selectedWorkspaceFile?.id === file.id;
const previewType = getUploadFilePreviewType(file);
const supportsInlinePreview =
previewType === "image" || previewType === "pdf";
return (
<button
key={file.id}
type="button"
data-testid={`product-data-file-${file.id}`}
onClick={() => onSelectWorkspaceFile(file.id)}
className={`w-full rounded-apple-lg border px-3 py-3 text-left transition ${
isActive
? "border-apple-blue bg-apple-blue/10"
: "border-apple-separator bg-apple-bg-secondary/20 hover:bg-apple-bg-secondary/40"
}`}
>
<div className="flex items-start justify-between gap-2">
<div className="min-w-0 flex-1">
<p className="truncate text-sm font-apple-medium text-apple-label-primary">
{file.name}
</p>
<p className="mt-1 text-[11px] text-apple-label-secondary">
{supportsInlinePreview
? "右侧可直接预览 PDF / 图片"
: "右侧展开资料明细,可直接下载"}
</p>
</div>
<span
className={`rounded-full px-2 py-0.5 text-[11px] ${
isActive
? "bg-apple-blue text-white"
: "bg-apple-bg-primary text-apple-label-secondary"
}`}
>
{getUploadFileTypeLabel(file)}
</span>
</div>
</button>
);
})}
</div>
)}
</div>
<div className="flex min-h-0 min-w-0 flex-1 flex-col rounded-apple-xl border border-apple-separator bg-apple-bg-secondary/20">
<div className="border-b border-apple-separator px-4 py-2.5">
<p className="truncate text-sm font-apple-medium text-apple-label-primary">
{workspacePreviewTitle}
</p>
<p className="mt-1 text-xs text-apple-label-secondary">
{workspacePreviewDescription}
</p>
</div>
<div className="flex min-h-0 flex-1 items-center justify-center p-3">
<ProductDataPreviewContent
selectedWorkspaceFile={selectedWorkspaceFile}
selectedWorkspacePreviewType={selectedWorkspacePreviewType}
workspacePreviewTitle={workspacePreviewTitle}
workspacePreviewDescription={workspacePreviewDescription}
activeProductDataGroupLabel={activeProductDataGroupLabel}
renderDocumentPreviewContent={renderDocumentPreviewContent}
/>
</div>
</div>
</div>
);
}
function ProductDataPreviewContent({
selectedWorkspaceFile,
selectedWorkspacePreviewType,
workspacePreviewTitle,
workspacePreviewDescription,
activeProductDataGroupLabel,
renderDocumentPreviewContent,
}: {
selectedWorkspaceFile: NonIntegratedUploadFile | null;
selectedWorkspacePreviewType: UploadFilePreviewType;
workspacePreviewTitle: string;
workspacePreviewDescription: string;
activeProductDataGroupLabel: string;
renderDocumentPreviewContent: () => ReactNode;
}) {
if (!selectedWorkspaceFile) {
return (
<WorkspacePreviewEmptyState
title={workspacePreviewTitle}
description={workspacePreviewDescription}
/>
);
}
if (
selectedWorkspacePreviewType === "image" ||
selectedWorkspacePreviewType === "pdf"
) {
return <>{renderDocumentPreviewContent()}</>;
}
return (
<div
data-testid="product-data-detail-preview"
className="flex h-full w-full flex-col gap-3 rounded-apple-xl border border-dashed border-apple-separator bg-white p-4"
>
<div>
<p className="text-sm font-apple-medium text-apple-label-primary">
资料明细
</p>
<p className="mt-1 text-xs leading-5 text-apple-label-secondary">
当前分类只对 PDF
和图片做在线预览,其它文件保留明细展开,模型师按明细查看和下载即可。
</p>
</div>
<div className="grid gap-3 md:grid-cols-2">
<div className="rounded-apple-lg border border-apple-separator bg-apple-bg-secondary/20 px-3 py-2.5">
<p className="text-[11px] text-apple-label-secondary">资料分类</p>
<p className="mt-1 text-sm font-apple-medium text-apple-label-primary">
{activeProductDataGroupLabel}
</p>
</div>
<div className="rounded-apple-lg border border-apple-separator bg-apple-bg-secondary/20 px-3 py-2.5">
<p className="text-[11px] text-apple-label-secondary">文件类型</p>
<p className="mt-1 text-sm font-apple-medium text-apple-label-primary">
{getUploadFileTypeLabel(selectedWorkspaceFile)}
</p>
</div>
<div className="rounded-apple-lg border border-apple-separator bg-apple-bg-secondary/20 px-3 py-2.5 md:col-span-2">
<p className="text-[11px] text-apple-label-secondary">文件名称</p>
<p className="mt-1 break-all text-sm font-apple-medium text-apple-label-primary">
{selectedWorkspaceFile.name}
</p>
</div>
</div>
<div className="rounded-apple-lg border border-apple-separator bg-[#f8fafc] px-4 py-3">
<p className="text-sm font-apple-medium text-apple-label-primary">
当前文件不做在线预览
</p>
<p className="mt-1 text-xs leading-5 text-apple-label-secondary">
DWG、Excel、PPT、压缩包等资料会保留在中间明细栏,右侧只展示关键信息。需要查看完整内容时,直接点击上方“下载”即可。
</p>
</div>
</div>
);
}