包含: - furniture-3d-system:Next.js 16 全栈应用(资产中心、产品管理、3D装配动画) - autocad-plugin:AutoCAD 家具导出插件源码 - 资产中心改造:编辑弹窗化、删除功能、Toast反馈、自定义Select组件 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
428 lines
16 KiB
Markdown
428 lines
16 KiB
Markdown
# SKU详情页-装配编辑器区域-布局设计方案
|
||
|
||
> 本方案用于指导重写SKU详情页中的3D预览/装配编辑器区域UI
|
||
|
||
**目标文件**: `src/app/products/[id]/skus/[skuId]/SKUDetailClient.workspace.tsx`
|
||
**重写范围**: 仅JSX渲染部分(从return语句开始),保持所有业务逻辑不变
|
||
**设计风格**: 参考右侧边栏(SKUReadOnlySidebar)的白色卡片+青绿色品牌色风格
|
||
|
||
---
|
||
|
||
## 一、当前UI问题分析
|
||
|
||
### 截图中的问题
|
||
1. **顶部工具栏杂乱**: 复选框、滑块、文字混排,视觉层次不清
|
||
2. **"3D预览"标题区**: 与下方内容区分不明显
|
||
3. **播放控制条**: 样式老旧,不够精致
|
||
4. **"安装顺序预览"卡片**: 样式与其他卡片不统一
|
||
5. **整体缺少品牌色强调**: 没有突出青绿色主题
|
||
|
||
---
|
||
|
||
## 二、新布局设计
|
||
|
||
### 整体结构
|
||
|
||
```
|
||
┌─────────────────────────────────────────────────────────────────┐
|
||
│ 顶部导航栏 (SKUDetailTopNav - 保持现有) │
|
||
├─────────────────────────────────────────────────────────────────┤
|
||
│ SKU标题区 (保持现有) │
|
||
├─────────────────────────────────────────────────────────────────┤
|
||
│ ┌──────────────────────────────────────┐ ┌─────────────────┐ │
|
||
│ │ │ │ 右侧边栏 │ │
|
||
│ │ 左侧:3D预览区 (重写给这部分) │ │ (保持现有) │ │
|
||
│ │ │ │ │ │
|
||
│ │ ┌────────────────────────────────┐ │ │ │ │
|
||
│ │ │ 3D预览卡片 │ │ │ │ │
|
||
│ │ │ ┌──────────────────────────┐ │ │ │ │ │
|
||
│ │ │ │ 3D画布区域 │ │ │ │ │ │
|
||
│ │ │ │ │ │ │ │ │ │
|
||
│ │ │ │ (ModelViewer组件) │ │ │ │ │ │
|
||
│ │ │ │ │ │ │ │ │ │
|
||
│ │ │ └──────────────────────────┘ │ │ │ │ │
|
||
│ │ │ │ │ │ │ │
|
||
│ │ │ [悬浮播放控制条] │ │ │ │ │
|
||
│ │ └────────────────────────────────┘ │ │ │ │
|
||
│ │ │ │ │ │
|
||
│ │ ┌────────────────────────────────┐ │ │ │ │
|
||
│ │ │ 装配步骤卡片 (重写给这部分) │ │ │ │ │
|
||
│ │ └────────────────────────────────┘ │ │ │ │
|
||
│ │ │ │ │ │
|
||
│ └──────────────────────────────────────┘ └─────────────────┘ │
|
||
└─────────────────────────────────────────────────────────────────┘
|
||
```
|
||
|
||
---
|
||
|
||
## 三、组件详细设计
|
||
|
||
### 3.1 3D预览卡片
|
||
|
||
**整体样式**:
|
||
```tsx
|
||
<div className="rounded-apple-lg bg-white border border-apple-separator shadow-xs overflow-hidden">
|
||
```
|
||
|
||
#### 3.1.1 卡片头部
|
||
|
||
**布局**: 左侧标题 + 右侧工具按钮组
|
||
|
||
```tsx
|
||
<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组件不变,只调整容器样式**:
|
||
|
||
```tsx
|
||
<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)
|
||
|
||
**悬浮在画布底部中央**:
|
||
|
||
```tsx
|
||
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 无模型空状态
|
||
|
||
```tsx
|
||
{!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 装配步骤卡片
|
||
|
||
**用于显示安装顺序预览**:
|
||
|
||
```tsx
|
||
<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`
|
||
|
||
---
|
||
|
||
## 五、注意事项
|
||
|
||
1. **保持业务逻辑不变**: 只修改JSX渲染部分,所有hooks、函数、状态管理保持原样
|
||
2. **保持组件引用**: ModelViewer、ContextMenu等复杂组件保持现有使用方式
|
||
3. **响应式**: 当前只考虑桌面端,移动端可后续优化
|
||
4. **图标**: 统一使用lucide-react,不要混用其他图标库
|
||
|
||
---
|
||
|
||
## 六、实施建议
|
||
|
||
1. 先备份原文件
|
||
2. 分段替换,每替换一部分就测试
|
||
3. 重点关注:
|
||
- 3D预览卡片头部工具栏
|
||
- 播放控制条样式
|
||
- 装配步骤卡片空状态和列表
|
||
4. 保持所有事件处理函数、props传递不变
|
||
|
||
---
|
||
|
||
**设计完成**
|