furniture-3d-system-Wu/docs/历史归档/早期规划/项目思维文档-通俗版.md

33 KiB
Raw Blame History

3D家具报价展示系统 - 项目思维文档

一、这个项目要做什么?

简单来说

做一个内部管理系统,让设计师上传家具的 CAD 图纸,然后网页上能:

  • 看到 3D 模型(可以转来转去看)
  • 看到详细的材料清单(用了多少板子、螺丝等)
  • 看到安装动画(怎么一步步装起来)
  • 看到包装方案(怎么装箱、用多少纸箱)
  • 自动算出价格
  • 客户可以看三维预览界面,一步步看安装动画(客户的主要需求)

就像这样

设计师在 AutoCAD 里画好一个柜子 → 上传到网页 → 网页自动展示所有信息 → 客户打开分享链接,在三维预览界面中一步步看安装动画,完成一步点下一步,就知道怎么装了。


二、整体思路

2.1 数据从哪里来?

方案A:直接上传 DWG 文件(推荐)

  • 设计师在 AutoCAD 里画好图,直接上传 DWG 文件
  • 网页后端自动解析 DWG,提取信息
  • 优点:设计师不用做额外操作,直接上传就行
  • 缺点:需要后端写程序解析 DWG

方案B:导出 glTF 再上传

  • 设计师在 AutoCAD 里导出 glTF 格式(需要插件)
  • 然后上传 glTF 文件
  • 优点:网页直接就能用,不需要解析
  • 缺点:设计师需要安装插件,多一步操作

我们选哪个?

  • 优先方案A(直接上传 DWG),用户体验最好
  • 如果 DWG 解析太复杂,再考虑方案B

2.2 数据怎么处理?

流程是这样的:

1. 设计师上传 DWG 文件
   ↓
2. 后端收到文件,放到"待处理队列"(因为转换需要时间)
   ↓
3. Python 程序开始工作:
   - 读取 DWG 文件
   - 找出每个板件(比如左侧板、右侧板)
   - 读取板件的尺寸、材质、颜色等信息
   - 把 3D 模型转换成网页能显示的格式(glTF)
   ↓
4. 处理完成后,保存结果
   ↓
5. 网页显示:3D 模型、材料清单、价格等

为什么用队列?

  • 因为转换 DWG 需要时间(可能几秒到几十秒)
  • 如果让用户一直等着,体验不好
  • 用队列:上传后立即返回"正在处理",处理完再通知用户

2.3 网页上显示什么?

主要页面:产品详情页

UI 设计:三层架构

  • 顶层(导航层):顶部导航栏,显示系统名称、用户信息、主要功能入口
  • 中层(内容层):主要内容区域,展示产品信息、3D 模型、清单等
  • 底层(操作层):底部操作栏,导出、保存等操作按钮

设计原则:

  • 美观现代化:采用现代设计风格,配色统一,界面简洁
  • UI 统一:所有页面使用统一的设计规范(颜色、字体、间距、组件样式)

从上到下包括:

  1. 顶部导航栏(导航层)

    • 系统 Logo 和名称
    • 主要功能入口:产品列表、上传产品、素材库管理
    • 用户信息:登录用户、退出登录
    • 注意:这是内部系统,不是对外展示的
  2. 产品基本信息(内容层)

    • 货号、品类、颜色、尺寸
    • 成本、报价
    • 就像产品标签一样
  3. 3D 模型展示区

    • 三个小窗口,显示不同角度的 3D 模型
    • 可以旋转、放大缩小
    • 可以看"爆炸图"(所有板件分开显示)
    • 可以看"拆解图"(看到内部结构)
  4. 2D 图纸

    • 传统的前视图、侧视图
    • 带尺寸标注
    • 给习惯看图纸的人看
  5. 材料清单(BOM)

    • 板材清单:用了多少板子,什么材质,多大
    • 小板信息:每块板子的详细尺寸
    • 五金配件:用了多少螺丝、连接件
    • 封边:用了多少封边条
    • 可以导出 Excel
  6. 安装动画

    • 像搭积木一样,一步步显示怎么装
    • 可以播放、暂停、单步播放
    • 可以录制视频,发给客户看
  7. 包装方案

    • 这个柜子怎么装箱
    • 用多大的箱子
    • 用多少纸箱
    • 包装成本多少
  8. 客户预览功能(操作层)

    • 三维预览界面:客户可以在这个界面看 3D 模型
    • 分步安装动画:客户可以一步步看安装动画
    • 交互式操作:客户完成一步后,点击"下一步"按钮,演示下一步安装动画
    • 不需要视频:客户直接在网页上操作,不需要下载视频
  9. 导出功能(操作层,内部使用)

    • 导出 PDF(内部报价单)
    • 导出 Excel(材料清单)
    • 导出视频(可选,如果需要离线使用)
    • 导出图片(3D 截图)

三、技术选型(用什么技术做)

3.1 前端(网页部分)

用什么?

  • React:做网页框架(就像盖房子的框架)
  • TypeScript:写代码更安全,不容易出错
  • Ant Design:现成的按钮、表格等组件(不用自己画)
  • Three.js:显示 3D 模型(就像游戏引擎)

为什么选这些?

  • React:用的人多,资料多,遇到问题好解决
  • Three.js:专门做 3D 的,功能强大,免费

3.2 后端(服务器部分)

用什么?

  • Node.js + NestJS:处理网页请求,管理数据
  • PostgreSQL:存数据(产品信息、材料清单等)
  • Python:专门处理 DWG 文件转换(因为 Python 有现成的库)

为什么分开?

  • Node.js:处理网页请求快,适合做 API
  • Python:处理 CAD 文件有现成的工具,不用自己写

3.3 文件存储

用什么?

  • MinIO:存上传的文件(DWG、glTF、图片等)
  • 或者用阿里云 OSS(如果已经有的话)

为什么?

  • 文件不能存在数据库里(太大了)
  • 需要单独的文件存储服务

3.4 部署方式

用什么?

  • Docker + Docker Compose:一键部署整个系统

为什么?

  • 方便:其他企业只需要一条命令就能安装
  • 隔离:所有服务都在容器里,不会影响服务器其他程序
  • 统一:开发环境和生产环境一致,不容易出错

包含哪些服务?

  • 前端(React 网页)
  • 后端(Node.js API)
  • 数据库(PostgreSQL)
  • 文件存储(MinIO)
  • Python 转换服务(处理 DWG)
  • Redis(任务队列)

部署命令:

# 一键启动所有服务
docker-compose up -d

# 一键停止所有服务
docker-compose down

四、核心功能详解

4.1 3D 模型展示

要做什么?

  • 加载 3D 模型(从 glTF 文件)
  • 可以旋转、放大缩小、平移
  • 可以看爆炸图(所有部件分开)
  • 可以看拆解图(看到内部)
  • 每个板件上显示编号(比如 P001、P002)

怎么实现?

  • 用 Three.js 加载模型
  • 用鼠标控制旋转(就像看 3D 地图一样)
  • 爆炸图:把每个板件往外移一点距离
  • 编号:在板件上贴一个"标签"

技术难点?

  • 模型太大可能卡顿 → 压缩模型,或者简化显示
  • 编号显示不清楚 → 调整字体大小和位置

4.2 材料清单(BOM)

要做什么?

  • 显示所有板件的列表
  • 显示每个板件的尺寸、材质、颜色、重量
  • 可以搜索、排序
  • 可以导出 Excel

数据从哪里来?

  • 从 DWG 文件解析出来(自动)
  • 或者用户手动填写

怎么实现?

  • 用表格组件显示(Ant Design 的 Table)
  • 导出 Excel:用 xlsx 库

4.6 五金件素材库(新增功能)

要做什么?

  • 建立一个统一的五金件素材库
  • 管理员可以上传五金件的 3D 模型、图片、规格信息
  • 设计师选择产品时,从素材库选择五金件,不用每次都上传
  • 保证所有产品使用的五金件都是统一的

为什么需要?

  • 统一管理:所有五金件在一个地方管理,方便维护
  • 统一标准:所有产品用同样的五金件,保证质量
  • 提高效率:不用每次都上传,直接选择就行

包含什么信息?

  • 五金件名称(如:木榫、三合一连接件、万向轮)
  • 规格(如:Φ840、7248*64)
  • 3D 模型(可选,用于在 3D 视图中显示)
  • 图片(用于展示)
  • 单价(用于计算成本)
  • 供应商信息(可选)

怎么用?

  1. 管理员先上传五金件到素材库
  2. 设计师上传产品时,从素材库选择需要的五金件
  3. 系统自动关联五金件信息到产品
  4. 计算成本时,自动使用素材库里的单价

技术实现:

  • 数据库单独建一个"五金件素材库"表
  • 提供上传界面(上传 3D 模型、图片、填写信息)
  • 提供选择界面(搜索、筛选、选择五金件)
  • 产品关联五金件时,只存 ID,不重复存数据

五金件在 3D 空间中的定位问题(重要):

问题描述:

  • 五金件上传到素材库时,只有 3D 模型本身(比如一个螺丝的模型)
  • 3D 模型默认在原点 (0,0,0),没有位置信息
  • 当产品使用这个五金件时,需要把五金件放在产品的正确位置(比如螺丝要放在板件的孔位上)
  • 但是五金件模型本身不知道应该放在哪里

解决方案(推荐混合方案):

方案1:产品模型中定义"安装点"(推荐)

  • 在 CAD 中,设计师在板件上标注"安装点"(比如孔位中心)
  • 解析 DWG 时,提取这些安装点的位置坐标
  • 每个安装点关联一个五金件类型(比如"M8螺丝")
  • 产品使用五金件时,系统自动把五金件放在对应的安装点上
  • 优点:位置准确,自动化程度高
  • 缺点:需要设计师在 CAD 中标注

方案2:五金件模型定义"安装参考点"

  • 上传五金件时,定义"安装参考点"(比如螺丝的底部中心)
  • 在产品模型中,定义"目标安装点"(比如板件上的孔位)
  • 系统计算:五金件位置 = 目标安装点 - 安装参考点
  • 优点:灵活,可以适配不同位置
  • 缺点:需要手动定义参考点

方案3:可视化编辑器手动放置(备选)

  • 在网页上提供 3D 编辑器
  • 用户可以在 3D 视图中,手动拖拽五金件到正确位置
  • 系统记录最终位置
  • 优点:最灵活,不需要 CAD 标注
  • 缺点:需要手动操作,效率低

方案4:从 CAD 文件读取位置(如果 CAD 中有)

  • 如果设计师在 CAD 中已经放置了五金件(作为块或实体)
  • 解析 DWG 时,提取五金件的位置和旋转信息
  • 自动关联到素材库中的五金件
  • 优点:完全自动化
  • 缺点:依赖 CAD 文件中的信息

推荐实现:

  • 主要方案:方案1(产品模型中定义安装点)+ 方案4(从 CAD 读取)
  • 备选方案:方案3(可视化编辑器),用于补充或修正
  • 数据存储:每个产品记录"五金件安装位置"表,包含:五金件ID、安装点坐标、旋转角度、关联的板件ID

4.3 安装动画(客户预览功能)

要做什么?

  • 像搭积木一样,一步步显示怎么装
  • 客户可以交互式操作:完成一步后,点击"下一步"继续
  • 可以自动播放(可选),也可以手动控制(推荐)
  • 可以录制视频(可选,用于离线使用)

客户使用流程:

  1. 客户打开分享链接,进入三维预览界面
  2. 看到 3D 模型和"开始安装"按钮
  3. 点击"开始安装",播放第一步安装动画
  4. 动画播放完成后,显示"下一步"按钮
  5. 客户完成当前步骤的实际安装后,点击"下一步"
  6. 播放下一步安装动画
  7. 重复步骤 5-6,直到所有步骤完成
  8. 可以随时点击"上一步"返回查看之前的步骤

数据怎么来?

  • 用户手动编辑:在网页上拖拽排序,设置每步的位置
  • 或者自动生成:根据板件的位置关系,智能推断顺序

怎么实现?

  • 用动画库(GSAP)让板件平滑移动
  • 分步控制:每步动画独立,可以单独播放
  • 状态管理:记录当前步骤,控制按钮显示
  • 录制视频(可选):用浏览器的录制功能,录下动画过程

技术难点?

  • 动画要流畅 → 用专业的动画库
  • 分步控制 → 需要状态管理,记录当前步骤
  • 交互体验 → 按钮提示要清晰,动画要易懂

4.4 包装方案

要做什么?

  • 显示这个柜子怎么装箱
  • 显示用多大的箱子
  • 显示包装成本

数据怎么来?

  • 后端算法计算(根据板件尺寸,自动排样)
  • 或者从 CAD 文件读取(如果设计师标注了)

怎么实现?

  • 后端写个排样算法(就像俄罗斯方块一样,把板件放进箱子)
  • 前端显示结果(箱子的尺寸、各层的排版)

技术难点?

  • 排样算法复杂 → 可以用现成的开源算法,或者简化处理

4.5 价格计算

要做什么?

  • 根据材料清单,自动计算成本
  • 根据成本,计算报价

怎么计算?

  • 板件成本 = 面积 × 单价
  • 五金成本 = 数量 × 单价
  • 包装成本 = 纸箱成本
  • 总成本 = 所有成本加起来
  • 报价 = 总成本 × 利润率

数据从哪里来?

  • 材料单价:存在数据库里,可以配置
  • 利润率:可以设置

五、DWG 文件处理(重点)

5.1 为什么要处理 DWG?

因为设计师用的是 AutoCAD,文件是 DWG 格式,网页不能直接显示,需要转换。

5.2 怎么处理?

步骤:

  1. 接收文件

    • 用户上传 DWG 文件
    • 后端保存文件
  2. 解析文件(Python)

    • 用 ezdxf 库读取 DWG
    • 找出所有"块"(Block),每个块就是一个板件
    • 读取块的名字(比如 "P001_左侧板")
    • 读取块的属性(材质、颜色等)
    • 读取块的尺寸(长宽高)
  3. 转换 3D 模型

    • 用 OpenCASCADE 把 DWG 的 3D 模型转换成 glTF
    • glTF 是网页能显示的格式
  4. 生成数据

    • 把所有信息整理成 JSON 格式
    • 保存到数据库
  5. 通知前端

    • 处理完成,前端可以显示了

5.3 需要设计师配合什么?

块命名规范:

  • 每个板件做成一个"块"(Block)
  • 块的名字格式:编号_名称,比如 P001_左侧板
  • 或者用属性(Attribute)存储:编号、名称、材质、颜色

为什么需要规范?

  • 程序才能识别哪个是哪个板件
  • 才能提取正确的信息

如果设计师不按规范怎么办?

  • 提供手动编辑功能
  • 用户可以自己补充信息

5.4 处理失败怎么办?

可能的问题:

  • DWG 文件损坏
  • 格式不支持
  • 转换出错

解决方案:

  • 记录错误日志
  • 告诉用户哪里出错了
  • 提供手动上传数据的入口(用户可以自己填)

六、数据怎么存?

6.1 需要存什么数据?

  1. 产品信息

    • 货号、名称、品类、颜色、尺寸、价格等
  2. 板件信息

    • 每个板件的编号、名称、尺寸、材质、颜色、重量等
  3. 五金配件

    • 从素材库选择五金件(不直接存数据,只存素材库的 ID)
    • 记录数量
    • 螺丝、连接件等的名称、规格从素材库读取
  4. 五金件素材库

    • 五金件的 3D 模型、图片
    • 名称、规格、单价
    • 供应商信息(可选)
  5. 封边信息

    • 封边条的颜色、宽度、长度
  6. 装配步骤

    • 安装的顺序、每步的位置等
  7. 包装方案

    • 箱体尺寸、各层排版、成本等
  8. 文件

    • DWG 文件、glTF 文件、图片等
    • 五金件的 3D 模型、图片

6.2 怎么存?

  • 数据库(PostgreSQL):存文字、数字信息
  • 文件存储(MinIO):存文件(DWG、glTF、图片)

为什么分开?

  • 文件太大,存在数据库里会很慢
  • 分开存,数据库快,文件也方便管理

七、用户怎么用?

7.1 设计师/管理员(内部用户)

  1. 管理五金件素材库

    • 上传五金件的 3D 模型、图片
    • 填写五金件的规格、单价等信息
    • 管理素材库(增删改查)
  2. 上传产品

    • 登录系统
    • 上传 DWG 文件
    • 等待处理完成(几秒到几十秒)
    • 检查自动提取的信息是否正确
    • 从素材库选择五金件(不用上传)
    • 如果有不对的,手动修改
    • 保存
  3. 编辑产品

    • 修改价格、材料等
    • 编辑安装动画的顺序
    • 重新计算包装方案
    • 更换五金件(从素材库选择)
  4. 导出给客户

    • 导出安装视频(这是客户唯一需要的)
    • 其他资料都是内部使用,不发给客户

7.2 客户(外部用户)

客户可以:

  • 打开设计师发来的分享链接(不需要登录)
  • 在三维预览界面中查看 3D 模型(可以旋转、缩放)
  • 一步步看安装动画:
    • 点击"开始安装"或"下一步"按钮
    • 观看当前步骤的安装动画
    • 完成当前步骤后,点击"下一步"继续
    • 可以返回上一步重新查看
  • 了解产品结构:通过 3D 模型和动画,知道每个部件的位置和安装顺序

客户不需要:

  • 登录系统
  • 看材料清单(内部人员看)
  • 看价格(内部人员看)
  • 下载视频(直接在网页上看就行)

所以:

  • 系统主要是内部使用
  • 客户通过分享链接访问三维预览界面
  • 客户可以交互式地一步步看安装动画,不需要视频

八、开发计划(分阶段做)

第一阶段:基础功能(2-3周)

  • 搭建网页框架
  • 实现文件上传
  • 实现 DWG 解析(至少能提取板件信息)
  • 实现简单的 3D 显示
  • 实现材料清单表格

目标: 能上传文件,看到基本信息

第二阶段:3D 展示完善(1-2周)

  • 完善 3D 模型显示(旋转、缩放、爆炸图)
  • 实现板件编号显示
  • 实现 2D 图纸显示

目标: 3D 展示功能完整

第三阶段:安装动画(1-2周)

  • 实现动画编辑器
  • 实现动画播放
  • 实现视频录制

目标: 能生成安装动画

第四阶段:包装方案(1周)

  • 实现包装算法(或对接现有算法)
  • 实现包装方案展示

目标: 能显示包装方案

第五阶段:导出功能(1周)

  • 实现 PDF 导出
  • 实现 Excel 导出
  • 实现视频导出

目标: 能导出各种格式

第六阶段:优化完善(1周)

  • 优化性能
  • 处理错误情况
  • 完善用户体验

目标: 系统稳定可用


九、可能遇到的问题

9.1 DWG 解析不准确

问题: 自动提取的信息有错误

解决:

  • 提供手动编辑功能
  • 记录哪些信息容易出错,针对性优化
  • 让设计师按规范命名,提高准确率

9.2 3D 模型显示慢

问题: 模型太大,加载慢

解决:

  • 压缩模型
  • 简化显示(不重要的细节去掉)
  • 分步加载(先显示简单的,再加载细节)

9.3 转换失败

问题: 某些 DWG 文件转换失败

解决:

  • 记录详细错误信息
  • 提供手动上传数据的入口
  • 逐步优化转换程序,支持更多情况

9.4 价格计算不准确

问题: 自动计算的价格不对

解决:

  • 允许手动调整价格
  • 检查材料单价是否正确
  • 检查计算公式是否正确

十、部署方案(Docker 一键部署)

10.1 为什么用 Docker?

  • 方便部署:其他企业只需要一条命令就能安装
  • 环境统一:开发、测试、生产环境一致
  • 隔离安全:每个服务在独立容器里,互不影响
  • 易于维护:更新、备份、恢复都很方便

10.2 包含哪些服务?

docker-compose.yml
├── frontend(前端网页)
├── backend(后端 API)
├── postgres(数据库)
├── minio(文件存储)
├── python-service(DWG 转换服务)
└── redis(任务队列)

10.3 部署步骤

第一步:准备环境

  • 安装 Docker 和 Docker Compose
  • 确保服务器有足够空间(建议至少 20GB)

第二步:下载项目

git clone [项目地址]
cd 3danzhuang

第三步:一键启动

docker-compose up -d

第四步:访问系统

就这么简单!

10.4 配置文件

  • docker-compose.yml:定义所有服务
  • .env:环境变量(数据库密码、端口等)
  • 首次部署时,系统会自动创建数据库、初始化数据

10.5 更新系统

# 停止服务
docker-compose down

# 更新代码
git pull

# 重新启动
docker-compose up -d

十一、UI 设计系统架构(简约现代化设计)

11.1 设计理念

核心原则:

  • 简约至上:减少视觉噪音,突出内容本身
  • 统一性:所有组件遵循同一套设计语言
  • 通用性:组件设计灵活,易于适配各种场景
  • 现代化:采用现代设计趋势(毛玻璃、微妙渐变、极简阴影)

设计风格:

  • 极简主义:去除不必要的装饰,注重功能
  • 现代感:柔和的圆角、微妙的阴影、流畅的动画
  • 一致性:统一的间距、颜色、字体系统
  • 可访问性:足够的对比度,清晰的层次

11.2 三层架构设计(页面布局)

顶层(导航层 - Navigation Layer)

  • 高度:56px(更紧凑)
  • 位置:页面顶部,固定定位
  • 背景:毛玻璃效果(backdrop-blur-md bg-white/80 dark:bg-slate-900/80)
  • 边框:底部细边框(border-b border-slate-200/50 dark:border-slate-700/50)
  • 内容:
    • Logo + 系统名称(左侧,简洁图标)
    • 主要功能入口(中间,扁平化按钮)
    • 用户信息(右侧,头像下拉菜单)
  • 特点:无阴影,极简设计

中层(内容层 - Content Layer)

  • 背景:渐变背景(浅色:bg-linear-to-br from-slate-50 to-white,深色:bg-linear-to-br from-slate-900 to-slate-800)
  • 内容:所有主要内容区域
  • 布局:
    • 使用统一的容器组件(Container)
    • 卡片间距:gap-4(16px,更紧凑)
    • 最大宽度:max-w-7xl mx-auto(居中)
    • 内边距:px-4 sm:px-6 lg:px-8(响应式内边距)
  • 卡片设计:
    • 极简边框:border border-slate-200/60 dark:border-slate-700/60
    • 微妙阴影:shadow-xs(非常轻微的阴影)
    • 圆角:rounded-xl(12px,统一圆角)
    • 背景:bg-white/60 dark:bg-slate-800/60 backdrop-blur-xs(半透明毛玻璃)

底层(操作层 - Action Layer)

  • 位置:内容区域底部(不固定,跟随内容)
  • 样式:极简设计,无背景,只有按钮
  • 内容:
    • 主要操作(左侧):主要按钮
    • 次要操作(右侧):次要按钮或链接
  • 布局:flex justify-between items-center gap-4

11.3 统一设计令牌系统

设计令牌(Design Tokens)

// lib/design-tokens.ts

export const tokens = {
  // 圆角系统(统一使用 3 个级别)
  radius: {
    sm: 'rounded-lg',      // 8px - 小元素(标签、徽章)
    md: 'rounded-xl',      // 12px - 标准(按钮、输入框、卡片)
    lg: 'rounded-2xl',     // 16px - 大容器(模态框、特殊区域)
  },
  
  // 阴影系统(极简,只使用 2 个级别)
  shadow: {
    none: 'shadow-none',
    sm: 'shadow-xs',       // 轻微阴影(卡片、输入框)
    // 不使用更重的阴影,保持简约
  },
  
  // 间距系统(基于 4px 的倍数)
  spacing: {
    xs: '4px',   // 0.25rem
    sm: '8px',   // 0.5rem
    md: '16px',  // 1rem
    lg: '24px',  // 1.5rem
    xl: '32px',  // 2rem
  },
  
  // 颜色系统(简约配色)
  colors: {
    primary: {
      light: '#6366f1',    // indigo-500
      dark: '#818cf8',     // indigo-400
    },
    neutral: {
      light: {
        bg: '#ffffff',
        surface: '#f8fafc',  // slate-50
        border: '#e2e8f0',   // slate-200
        text: '#0f172a',     // slate-900
        textSecondary: '#64748b', // slate-500
      },
      dark: {
        bg: '#0f172a',       // slate-900
        surface: '#1e293b',  // slate-800
        border: '#334155',   // slate-700
        text: '#f1f5f9',     // slate-100
        textSecondary: '#94a3b8', // slate-400
      },
    },
    semantic: {
      success: '#22c55e',   // green-500
      warning: '#f59e0b',    // amber-500
      error: '#ef4444',      // red-500
      info: '#3b82f6',       // blue-500
    },
  },
  
  // 字体系统
  typography: {
    fontFamily: {
      sans: ['Inter', 'system-ui', 'sans-serif'],
      mono: ['JetBrains Mono', 'monospace'],
    },
    size: {
      xs: '0.75rem',   // 12px
      sm: '0.875rem',  // 14px
      base: '1rem',    // 16px
      lg: '1.125rem',  // 18px
      xl: '1.25rem',   // 20px
      '2xl': '1.5rem', // 24px
    },
    weight: {
      normal: '400',
      medium: '500',
      semibold: '600',
      bold: '700',
    },
  },
  
  // 动画系统
  animation: {
    duration: {
      fast: '150ms',
      normal: '200ms',
      slow: '300ms',
    },
    easing: {
      default: 'cubic-bezier(0.4, 0, 0.2, 1)',
      in: 'cubic-bezier(0.4, 0, 1, 1)',
      out: 'cubic-bezier(0, 0, 0.2, 1)',
    },
  },
}

11.4 组件系统架构

组件设计原则:

  1. 最小化变体:每个组件只有必要的变体(primary、secondary、ghost)
  2. 统一接口:所有组件支持 size、variant、className
  3. 组合式设计:通过组合基础组件构建复杂组件
  4. 主题感知:自动适配浅色/深色模式

核心组件库:

// components/ui/

// 1. 基础容器
Container      // 内容容器(统一最大宽度、内边距)
Card           // 卡片(极简设计,只有 base 变体)
Section        // 区块容器(统一间距)

// 2. 表单组件
Button         // 按钮(primary | secondary | ghost | danger)
Input          // 输入框(base | error)
Select         // 选择器
Checkbox       // 复选框
Radio          // 单选框

// 3. 反馈组件
Badge          // 徽章(primary | success | warning | error)
Alert          // 提示框
Toast          // 通知
Loading        // 加载状态

// 4. 导航组件
Nav            // 导航栏
Tabs           // 标签页
Breadcrumb     // 面包屑

// 5. 数据展示
Table          // 表格(极简设计)
List           // 列表
Grid           // 网格布局

// 6. 覆盖层
Modal          // 模态框
Dialog         // 对话框
Drawer         // 抽屉
Tooltip        // 工具提示

// 7. 3D 相关(特殊组件)
Viewer3D       // 3D 查看器容器
ControlPanel   // 3D 控制面板

11.5 样式系统实现

统一样式工具(lib/ui-styles.ts)

import { tokens } from './design-tokens'
import { cn } from './utils'

// 基础样式生成器(更通用、更灵活)
export const createStyles = {
  // 卡片样式(极简,只有一个基础样式)
  card: (variant: 'base' | 'hover' = 'base') => cn(
    tokens.radius.md,
    'border border-slate-200/60 dark:border-slate-700/60',
    'bg-white/60 dark:bg-slate-800/60 backdrop-blur-xs',
    tokens.shadow.sm,
    variant === 'hover' && 'hover:border-slate-300/60 dark:hover:border-slate-600/60 transition-colors',
    'p-6'
  ),
  
  // 按钮样式(简化变体)
  button: (variant: 'primary' | 'secondary' | 'ghost' | 'danger' = 'primary') => {
    const base = cn(
      tokens.radius.md,
      'px-6 py-2.5',
      'font-medium',
      'transition-all duration-200',
      'focus:outline-hidden focus:ring-2 focus:ring-offset-2'
    )
    
    const variants = {
      primary: cn(
        base,
        'bg-indigo-600 text-white',
        'hover:bg-indigo-700',
        'focus:ring-indigo-500'
      ),
      secondary: cn(
        base,
        'border border-slate-300 dark:border-slate-600',
        'bg-white dark:bg-slate-800 text-slate-700 dark:text-slate-200',
        'hover:bg-slate-50 dark:hover:bg-slate-700',
        'focus:ring-slate-500'
      ),
      ghost: cn(
        base,
        'text-slate-700 dark:text-slate-200',
        'hover:bg-slate-100 dark:hover:bg-slate-800'
      ),
      danger: cn(
        base,
        'bg-red-600 text-white',
        'hover:bg-red-700',
        'focus:ring-red-500'
      ),
    }
    
    return variants[variant]
  },
  
  // 输入框样式
  input: (state: 'base' | 'error' = 'base') => cn(
    tokens.radius.md,
    'w-full px-4 py-2.5',
    'border',
    state === 'error' 
      ? 'border-red-500 focus:ring-red-500'
      : 'border-slate-300 dark:border-slate-600 focus:border-indigo-500 focus:ring-indigo-500',
    'bg-white dark:bg-slate-800',
    'text-slate-900 dark:text-white',
    'focus:outline-hidden focus:ring-2 focus:ring-offset-0',
    'transition-colors'
  ),
}

// 工具函数
export function cn(...classes: (string | undefined | false | null)[]): string {
  return classes.filter(Boolean).join(' ')
}

11.6 组件使用示例

基础用法:

// 使用统一样式
import { createStyles, cn } from '@/lib/ui-styles'

// 卡片
<div className={createStyles.card()}>内容</div>
<div className={createStyles.card('hover')}>可悬停卡片</div>

// 按钮
<button className={createStyles.button('primary')}>主要按钮</button>
<button className={createStyles.button('secondary')}>次要按钮</button>

// 输入框
<input className={createStyles.input()} />
<input className={createStyles.input('error')} />

组件化用法(推荐):

// 使用组件
import { Card, Button, Input } from '@/components/ui'

<Card>
  <h2>标题</h2>
  <p>内容</p>
  <div className="flex gap-3 mt-4">
    <Button variant="primary">保存</Button>
    <Button variant="secondary">取消</Button>
  </div>
</Card>

11.7 现代化设计元素

毛玻璃效果(Glassmorphism):

  • 使用 backdrop-blur-xs 或 backdrop-blur-md
  • 半透明背景:bg-white/60 或 bg-slate-800/60
  • 适用于:导航栏、卡片、模态框

微妙渐变:

  • 背景渐变:bg-linear-to-br from-slate-50 to-white
  • 按钮渐变(可选):bg-linear-to-r from-indigo-600 to-purple-600
  • 使用原则:非常微妙,不抢夺内容焦点

极简阴影:

  • 只使用 shadow-xs(轻微阴影)
  • 不使用重阴影,保持扁平化感觉
  • 悬停时:只改变边框颜色,不改变阴影

流畅动画:

  • 统一使用 transition-colors duration-200
  • 避免复杂的变换动画
  • 保持动画快速、自然

11.8 响应式设计策略

断点系统:

const breakpoints = {
  sm: '640px',   // 手机横屏
  md: '768px',   // 平板
  lg: '1024px',  // 桌面
  xl: '1280px',  // 大桌面
}

布局策略:

  • 手机:单列,卡片堆叠,紧凑间距
  • 平板:2列网格,适中间距
  • 桌面:多列网格,宽松间距

响应式工具类:

// 响应式内边距
'px-4 sm:px-6 lg:px-8'

// 响应式字体
'text-sm sm:text-base lg:text-lg'

// 响应式网格
'grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4'

11.9 实施指南

开发流程:

  1. 先设计令牌:定义颜色、间距、圆角等基础值
  2. 创建样式工具:实现 createStyles 函数
  3. 构建基础组件:Card、Button、Input 等
  4. 组合复杂组件:使用基础组件构建
  5. 统一使用:所有页面使用统一组件

检查清单:

  • 使用了统一的设计令牌
  • 组件样式极简(无多余装饰)
  • 支持深色模式
  • 响应式设计正确
  • 动画流畅自然
  • 三层架构清晰
  • 代码可复用、可扩展

设计原则总结:

  • 少即是多:减少变体,简化选择
  • 一致性:所有组件遵循同一套规则
  • 灵活性:通过组合实现复杂场景
  • 现代化:采用现代设计趋势,但不过度

十二、总结

12.1 这个系统的价值

  • 对设计师: 快速生成产品信息,导出安装视频给客户
  • 对客户: 收到安装视频,知道怎么安装
  • 对公司: 提高效率,统一标准(五金件素材库),减少沟通成本

12.2 核心特点

  1. 内部系统:主要给内部人员使用,客户只需要安装视频
  2. 一键部署:Docker 一键安装,其他企业容易使用
  3. 统一标准:五金件素材库,保证所有产品用统一的五金件
  4. 美观现代:三层架构 UI,统一设计规范

10.2 技术难点

  1. DWG 解析: 需要准确提取信息
  2. 3D 展示: 要流畅、好看
  3. 动画生成: 要自然、易懂
  4. 价格计算: 要准确、可配置

10.3 成功的关键

  1. 设计师配合: 按规范命名,信息提取才准确
  2. 逐步完善: 先做基础功能,再逐步优化
  3. 用户体验: 界面要简洁,操作要简单
  4. 错误处理: 出错要有提示,不能直接崩溃

文档说明:

  • 这是思维文档,用通俗语言说明项目思路
  • 具体技术细节看架构设计文档
  • 有问题随时讨论调整