包含: - furniture-3d-system:Next.js 16 全栈应用(资产中心、产品管理、3D装配动画) - autocad-plugin:AutoCAD 家具导出插件源码 - 资产中心改造:编辑弹窗化、删除功能、Toast反馈、自定义Select组件 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
16 KiB
16 KiB
SKU详情页-装配编辑器区域-布局设计方案
本方案用于指导重写SKU详情页中的3D预览/装配编辑器区域UI
目标文件: src/app/products/[id]/skus/[skuId]/SKUDetailClient.workspace.tsx
重写范围: 仅JSX渲染部分(从return语句开始),保持所有业务逻辑不变
设计风格: 参考右侧边栏(SKUReadOnlySidebar)的白色卡片+青绿色品牌色风格
一、当前UI问题分析
截图中的问题
- 顶部工具栏杂乱: 复选框、滑块、文字混排,视觉层次不清
- "3D预览"标题区: 与下方内容区分不明显
- 播放控制条: 样式老旧,不够精致
- "安装顺序预览"卡片: 样式与其他卡片不统一
- 整体缺少品牌色强调: 没有突出青绿色主题
二、新布局设计
整体结构
┌─────────────────────────────────────────────────────────────────┐
│ 顶部导航栏 (SKUDetailTopNav - 保持现有) │
├─────────────────────────────────────────────────────────────────┤
│ SKU标题区 (保持现有) │
├─────────────────────────────────────────────────────────────────┤
│ ┌──────────────────────────────────────┐ ┌─────────────────┐ │
│ │ │ │ 右侧边栏 │ │
│ │ 左侧:3D预览区 (重写给这部分) │ │ (保持现有) │ │
│ │ │ │ │ │
│ │ ┌────────────────────────────────┐ │ │ │ │
│ │ │ 3D预览卡片 │ │ │ │ │
│ │ │ ┌──────────────────────────┐ │ │ │ │ │
│ │ │ │ 3D画布区域 │ │ │ │ │ │
│ │ │ │ │ │ │ │ │ │
│ │ │ │ (ModelViewer组件) │ │ │ │ │ │
│ │ │ │ │ │ │ │ │ │
│ │ │ └──────────────────────────┘ │ │ │ │ │
│ │ │ │ │ │ │ │
│ │ │ [悬浮播放控制条] │ │ │ │ │
│ │ └────────────────────────────────┘ │ │ │ │
│ │ │ │ │ │
│ │ ┌────────────────────────────────┐ │ │ │ │
│ │ │ 装配步骤卡片 (重写给这部分) │ │ │ │ │
│ │ └────────────────────────────────┘ │ │ │ │
│ │ │ │ │ │
│ └──────────────────────────────────────┘ └─────────────────┘ │
└─────────────────────────────────────────────────────────────────┘
三、组件详细设计
3.1 3D预览卡片
整体样式:
<div className="rounded-apple-lg bg-white border border-apple-separator shadow-xs overflow-hidden">
3.1.1 卡片头部
布局: 左侧标题 + 右侧工具按钮组
<div className="px-4 py-3 border-b border-apple-separator bg-apple-bg-primary">
<div className="flex items-center justify-between">
{/* 左侧:标题 */}
<div className="flex items-center gap-2">
<Box className="w-4 h-4 text-[#008D74]" strokeWidth={2} />
<span className="text-apple-sm font-apple-system font-apple-semibold text-apple-label-primary">
3D 预览
</span>
{/* 已选中板件数量标签 */}
{highlightedParts.length > 0 && (
<span
className="px-1.5 py-0.5 rounded-sm text-apple-xs font-apple-medium"
style={{ backgroundColor: '#CFEAE2', color: '#005C4B' }}
>
已选中 {highlightedParts.length} 个板件
</span>
)}
</div>
{/* 右侧:工具按钮组 */}
<div className="flex items-center gap-2">
{/* 设置按钮 - 点击展开下拉菜单 */}
<button className="flex items-center gap-1.5 px-3 py-1.5 rounded-apple-md text-apple-xs font-apple-system text-apple-label-secondary hover:text-apple-label-primary hover:bg-apple-bg-secondary/50 transition-colors">
<Settings2 className="w-3.5 h-3.5" />
<span>显示设置</span>
</button>
</div>
</div>
{/* 第二行:快捷工具栏 - 仅在展开时显示 */}
<div className="flex items-center gap-4 mt-3 pt-3 border-t border-apple-separator/50">
{/* 孔位高亮开关 */}
<label className="flex items-center gap-2 cursor-pointer">
<div className={cn(
"w-8 h-4 rounded-full transition-colors relative",
showHoleHighlights ? "bg-[#008D74]" : "bg-apple-fill-tertiary"
)}>
<div className={cn(
"absolute top-0.5 w-3 h-3 rounded-full bg-white transition-transform",
showHoleHighlights ? "translate-x-4" : "translate-x-0.5"
)} />
</div>
<span className="text-apple-xs text-apple-label-secondary">孔位高亮</span>
</label>
{/* 折角线开关 */}
<label className="flex items-center gap-2 cursor-pointer">
<div className={cn(
"w-8 h-4 rounded-full transition-colors relative",
showFoldLines ? "bg-[#008D74]" : "bg-apple-fill-tertiary"
)}>
<div className={cn(
"absolute top-0.5 w-3 h-3 rounded-full bg-white transition-transform",
showFoldLines ? "translate-x-4" : "translate-x-0.5"
)} />
</div>
<span className="text-apple-xs text-apple-label-secondary">折角线</span>
</label>
{/* 爆炸程度滑块 */}
<div className="flex items-center gap-2 flex-1 max-w-[200px]">
<span className="text-apple-xs text-apple-label-secondary whitespace-nowrap">爆炸</span>
<Slider
min={0}
max={2}
step={0.01}
value={explosionProgress}
onValueChange={setExplosionProgress}
className="flex-1"
/>
<span className="text-apple-xs text-apple-label-secondary w-10 text-right">
{explosionProgress.toFixed(2)}x
</span>
</div>
</div>
</div>
3.1.2 3D画布区域
保持现有ModelViewer组件不变,只调整容器样式:
<div
className="relative bg-apple-bg-secondary/30"
style={{ height: 'calc(100vh - 360px)', minHeight: '400px', maxHeight: '600px' }}
>
{/* ModelViewer组件保持现有 */}
<ModelViewer
modelUrl={resolvedModelUrl}
// ... 保持所有props不变
/>
{/* ContextMenu保持现有 */}
{/* 播放控制条 - 新版设计 */}
{isViewerReady && !viewerLoadError && playbackSteps.length > 0 && (
<PlaybackControlBar />
)}
</div>
3.1.3 播放控制条组件 (PlaybackControlBar)
悬浮在画布底部中央:
function PlaybackControlBar() {
return (
<div className="absolute bottom-4 left-1/2 -translate-x-1/2">
<div className="bg-white/95 backdrop-blur-md rounded-full pl-2 pr-4 py-2 shadow-lg border border-apple-separator flex items-center gap-2">
{/* 重置按钮 */}
<button
onClick={reset}
className="w-8 h-8 rounded-full bg-apple-bg-secondary hover:bg-apple-fill-tertiary flex items-center justify-center transition-colors"
>
<RotateCcw className="w-4 h-4 text-apple-label-secondary" />
</button>
{/* 上一步 */}
<button
onClick={() => goToStep(currentStep - 1)}
disabled={!canGoPrev}
className={cn(
"w-8 h-8 rounded-full flex items-center justify-center transition-colors",
canGoPrev
? "bg-apple-bg-secondary hover:bg-apple-fill-tertiary"
: "opacity-40 cursor-not-allowed"
)}
>
<ChevronLeft className="w-4 h-4 text-apple-label-secondary" />
</button>
{/* 播放/暂停按钮 - 品牌色主按钮 */}
<button
onClick={togglePlay}
className="w-10 h-10 rounded-full bg-[#008D74] hover:bg-[#007A63] flex items-center justify-center transition-colors shadow-md"
>
{isPlaying ? (
<Pause className="w-5 h-5 text-white" fill="white" />
) : (
<Play className="w-5 h-5 text-white ml-0.5" fill="white" />
)}
</button>
{/* 下一步 */}
<button
onClick={() => goToStep(currentStep + 1)}
disabled={!canGoNext}
className={cn(
"w-8 h-8 rounded-full flex items-center justify-center transition-colors",
canGoNext
? "bg-apple-bg-secondary hover:bg-apple-fill-tertiary"
: "opacity-40 cursor-not-allowed"
)}
>
<ChevronRight className="w-4 h-4 text-apple-label-secondary" />
</button>
{/* 步骤进度 */}
<div className="flex items-center gap-1.5 ml-1">
<span className="text-apple-sm font-apple-system font-apple-semibold text-apple-label-primary">
{hasPlaybackStarted ? currentStep + 1 : '-'}
</span>
<span className="text-apple-xs text-apple-label-tertiary">/</span>
<span className="text-apple-xs text-apple-label-tertiary">
{playbackSteps.length}
</span>
</div>
</div>
</div>
)
}
3.1.4 无模型空状态
{!resolvedModelUrl && (
<div className="h-full flex flex-col items-center justify-center">
<div className="w-20 h-20 rounded-apple-xl bg-[#EAF6F2] flex items-center justify-center text-[#008D74] mb-4">
<Package className="w-10 h-10" strokeWidth={1.5} />
</div>
<p className="text-apple-md font-apple-system font-apple-semibold text-apple-label-primary mb-1">
暂无 3D 模型
</p>
<p className="text-apple-xs text-apple-label-tertiary">
该产品尚未上传模型文件
</p>
</div>
)}
3.2 装配步骤卡片
用于显示安装顺序预览:
<div className="mt-4 rounded-apple-lg bg-white border border-apple-separator shadow-xs overflow-hidden">
{/* 头部 */}
<div className="px-4 py-3 border-b border-apple-separator flex items-center justify-between">
<div className="flex items-center gap-2">
<Layers className="w-4 h-4 text-[#008D74]" strokeWidth={2} />
<span className="text-apple-sm font-apple-system font-apple-semibold text-apple-label-primary">
装配步骤
</span>
{orderedSteps.length > 0 && (
<span
className="px-1.5 py-0.5 rounded-sm text-apple-xs font-apple-medium"
style={{ backgroundColor: '#CFEAE2', color: '#005C4B' }}
>
{orderedSteps.length} 步
</span>
)}
</div>
{/* 去编辑按钮 */}
<Link
href={`/products/${product.id}/assembly`}
className="flex items-center gap-1 px-3 py-1.5 rounded-apple-md text-apple-xs font-apple-medium text-[#008D74] hover:bg-[#EAF6F2] transition-colors"
>
<Settings2 className="w-3.5 h-3.5" />
<span>去编辑</span>
</Link>
</div>
{/* 内容 */}
<div className="p-4">
{orderedSteps.length === 0 ? (
<div className="text-center py-6">
<div className="w-12 h-12 rounded-apple-lg bg-apple-bg-secondary flex items-center justify-center text-apple-label-quaternary mx-auto mb-3">
<Layers className="w-6 h-6" />
</div>
<p className="text-apple-sm text-apple-label-secondary mb-1">
暂无装配步骤
</p>
<p className="text-apple-xs text-apple-label-tertiary mb-3">
当前 SKU 还没有保存装配步骤
</p>
<Link
href={`/products/${product.id}/assembly`}
className="inline-flex items-center gap-1.5 px-4 py-2 rounded-apple-md bg-[#008D74] text-white text-apple-sm font-apple-medium hover:bg-[#007A63] transition-colors"
>
<Settings2 className="w-4 h-4" />
<span>去装配编辑器</span>
</Link>
</div>
) : (
<div className="space-y-2 max-h-48 overflow-y-auto pr-1">
{orderedSteps.map((step, index) => (
<div
key={step.id}
className="flex items-center gap-3 p-2.5 rounded-apple-md bg-apple-bg-secondary/50 hover:bg-apple-bg-secondary transition-colors"
>
{/* 步骤序号 - 圆形徽章 */}
<div
className="w-6 h-6 rounded-full flex items-center justify-center text-apple-xs font-apple-semibold flex-shrink-0"
style={{
backgroundColor: index === currentStep ? '#008D74' : '#E5E5E7',
color: index === currentStep ? 'white' : '#666'
}}
>
{step.order}
</div>
{/* 步骤信息 */}
<div className="flex-1 min-w-0">
<p className="text-apple-sm font-apple-system text-apple-label-primary truncate">
{step.title}
</p>
{step.description && (
<p className="text-apple-xs text-apple-label-tertiary truncate">
{step.description}
</p>
)}
</div>
{/* 步骤类型图标 */}
{step.stepType === 'hardware' && (
<Wrench className="w-3.5 h-3.5 text-apple-label-tertiary flex-shrink-0" />
)}
</div>
))}
</div>
)}
</div>
</div>
四、样式规范汇总
4.1 颜色系统
| 用途 | 值 | 备注 |
|---|---|---|
| 品牌主色 | #008D74 |
按钮、强调、选中状态 |
| 品牌深色 | #007A63 |
hover状态 |
| 品牌浅色 | #CFEAE2 |
标签背景、选中背景 |
| 品牌文字 | #005C4B |
标签文字 |
| 背景灰 | #FAFAFA |
页面背景 |
| 卡片白 | white |
卡片背景 |
| 边框灰 | border-apple-separator |
卡片边框 |
4.2 圆角规范
- 卡片:
rounded-apple-lg(12px) - 按钮:
rounded-apple-md(8px) 或rounded-full(圆形) - 标签:
rounded-sm(4px) - 开关:
rounded-full
4.3 阴影规范
- 卡片:
shadow-xs - 悬浮控制条:
shadow-lg - 主按钮:
shadow-md
4.4 间距规范
- 卡片内边距:
p-4(16px) 或px-4 py-3(16px 12px) - 元素间距:
gap-2(8px) 或gap-3(12px) - 外边距:
mt-4(16px)
4.5 字体规范
- 标题:
text-apple-sm font-apple-system font-apple-semibold - 正文:
text-apple-xs font-apple-system - 标签:
text-apple-xs font-apple-medium
五、注意事项
- 保持业务逻辑不变: 只修改JSX渲染部分,所有hooks、函数、状态管理保持原样
- 保持组件引用: ModelViewer、ContextMenu等复杂组件保持现有使用方式
- 响应式: 当前只考虑桌面端,移动端可后续优化
- 图标: 统一使用lucide-react,不要混用其他图标库
六、实施建议
- 先备份原文件
- 分段替换,每替换一部分就测试
- 重点关注:
- 3D预览卡片头部工具栏
- 播放控制条样式
- 装配步骤卡片空状态和列表
- 保持所有事件处理函数、props传递不变
设计完成