33 KiB
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 统一:所有页面使用统一的设计规范(颜色、字体、间距、组件样式)
从上到下包括:
-
顶部导航栏(导航层)
- 系统 Logo 和名称
- 主要功能入口:产品列表、上传产品、素材库管理
- 用户信息:登录用户、退出登录
- 注意:这是内部系统,不是对外展示的
-
产品基本信息(内容层)
- 货号、品类、颜色、尺寸
- 成本、报价
- 就像产品标签一样
-
3D 模型展示区
- 三个小窗口,显示不同角度的 3D 模型
- 可以旋转、放大缩小
- 可以看"爆炸图"(所有板件分开显示)
- 可以看"拆解图"(看到内部结构)
-
2D 图纸
- 传统的前视图、侧视图
- 带尺寸标注
- 给习惯看图纸的人看
-
材料清单(BOM)
- 板材清单:用了多少板子,什么材质,多大
- 小板信息:每块板子的详细尺寸
- 五金配件:用了多少螺丝、连接件
- 封边:用了多少封边条
- 可以导出 Excel
-
安装动画
- 像搭积木一样,一步步显示怎么装
- 可以播放、暂停、单步播放
- 可以录制视频,发给客户看
-
包装方案
- 这个柜子怎么装箱
- 用多大的箱子
- 用多少纸箱
- 包装成本多少
-
客户预览功能(操作层)
- 三维预览界面:客户可以在这个界面看 3D 模型
- 分步安装动画:客户可以一步步看安装动画
- 交互式操作:客户完成一步后,点击"下一步"按钮,演示下一步安装动画
- 不需要视频:客户直接在网页上操作,不需要下载视频
-
导出功能(操作层,内部使用)
- 导出 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 视图中显示)
- 图片(用于展示)
- 单价(用于计算成本)
- 供应商信息(可选)
怎么用?
- 管理员先上传五金件到素材库
- 设计师上传产品时,从素材库选择需要的五金件
- 系统自动关联五金件信息到产品
- 计算成本时,自动使用素材库里的单价
技术实现:
- 数据库单独建一个"五金件素材库"表
- 提供上传界面(上传 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 安装动画(客户预览功能)
要做什么?
- 像搭积木一样,一步步显示怎么装
- 客户可以交互式操作:完成一步后,点击"下一步"继续
- 可以自动播放(可选),也可以手动控制(推荐)
- 可以录制视频(可选,用于离线使用)
客户使用流程:
- 客户打开分享链接,进入三维预览界面
- 看到 3D 模型和"开始安装"按钮
- 点击"开始安装",播放第一步安装动画
- 动画播放完成后,显示"下一步"按钮
- 客户完成当前步骤的实际安装后,点击"下一步"
- 播放下一步安装动画
- 重复步骤 5-6,直到所有步骤完成
- 可以随时点击"上一步"返回查看之前的步骤
数据怎么来?
- 用户手动编辑:在网页上拖拽排序,设置每步的位置
- 或者自动生成:根据板件的位置关系,智能推断顺序
怎么实现?
- 用动画库(GSAP)让板件平滑移动
- 分步控制:每步动画独立,可以单独播放
- 状态管理:记录当前步骤,控制按钮显示
- 录制视频(可选):用浏览器的录制功能,录下动画过程
技术难点?
- 动画要流畅 → 用专业的动画库
- 分步控制 → 需要状态管理,记录当前步骤
- 交互体验 → 按钮提示要清晰,动画要易懂
4.4 包装方案
要做什么?
- 显示这个柜子怎么装箱
- 显示用多大的箱子
- 显示包装成本
数据怎么来?
- 后端算法计算(根据板件尺寸,自动排样)
- 或者从 CAD 文件读取(如果设计师标注了)
怎么实现?
- 后端写个排样算法(就像俄罗斯方块一样,把板件放进箱子)
- 前端显示结果(箱子的尺寸、各层的排版)
技术难点?
- 排样算法复杂 → 可以用现成的开源算法,或者简化处理
4.5 价格计算
要做什么?
- 根据材料清单,自动计算成本
- 根据成本,计算报价
怎么计算?
- 板件成本 = 面积 × 单价
- 五金成本 = 数量 × 单价
- 包装成本 = 纸箱成本
- 总成本 = 所有成本加起来
- 报价 = 总成本 × 利润率
数据从哪里来?
- 材料单价:存在数据库里,可以配置
- 利润率:可以设置
五、DWG 文件处理(重点)
5.1 为什么要处理 DWG?
因为设计师用的是 AutoCAD,文件是 DWG 格式,网页不能直接显示,需要转换。
5.2 怎么处理?
步骤:
-
接收文件
- 用户上传 DWG 文件
- 后端保存文件
-
解析文件(Python)
- 用 ezdxf 库读取 DWG
- 找出所有"块"(Block),每个块就是一个板件
- 读取块的名字(比如 "P001_左侧板")
- 读取块的属性(材质、颜色等)
- 读取块的尺寸(长宽高)
-
转换 3D 模型
- 用 OpenCASCADE 把 DWG 的 3D 模型转换成 glTF
- glTF 是网页能显示的格式
-
生成数据
- 把所有信息整理成 JSON 格式
- 保存到数据库
-
通知前端
- 处理完成,前端可以显示了
5.3 需要设计师配合什么?
块命名规范:
- 每个板件做成一个"块"(Block)
- 块的名字格式:
编号_名称,比如P001_左侧板 - 或者用属性(Attribute)存储:编号、名称、材质、颜色
为什么需要规范?
- 程序才能识别哪个是哪个板件
- 才能提取正确的信息
如果设计师不按规范怎么办?
- 提供手动编辑功能
- 用户可以自己补充信息
5.4 处理失败怎么办?
可能的问题:
- DWG 文件损坏
- 格式不支持
- 转换出错
解决方案:
- 记录错误日志
- 告诉用户哪里出错了
- 提供手动上传数据的入口(用户可以自己填)
六、数据怎么存?
6.1 需要存什么数据?
-
产品信息
- 货号、名称、品类、颜色、尺寸、价格等
-
板件信息
- 每个板件的编号、名称、尺寸、材质、颜色、重量等
-
五金配件
- 从素材库选择五金件(不直接存数据,只存素材库的 ID)
- 记录数量
- 螺丝、连接件等的名称、规格从素材库读取
-
五金件素材库
- 五金件的 3D 模型、图片
- 名称、规格、单价
- 供应商信息(可选)
-
封边信息
- 封边条的颜色、宽度、长度
-
装配步骤
- 安装的顺序、每步的位置等
-
包装方案
- 箱体尺寸、各层排版、成本等
-
文件
- DWG 文件、glTF 文件、图片等
- 五金件的 3D 模型、图片
6.2 怎么存?
- 数据库(PostgreSQL):存文字、数字信息
- 文件存储(MinIO):存文件(DWG、glTF、图片)
为什么分开?
- 文件太大,存在数据库里会很慢
- 分开存,数据库快,文件也方便管理
七、用户怎么用?
7.1 设计师/管理员(内部用户)
-
管理五金件素材库
- 上传五金件的 3D 模型、图片
- 填写五金件的规格、单价等信息
- 管理素材库(增删改查)
-
上传产品
- 登录系统
- 上传 DWG 文件
- 等待处理完成(几秒到几十秒)
- 检查自动提取的信息是否正确
- 从素材库选择五金件(不用上传)
- 如果有不对的,手动修改
- 保存
-
编辑产品
- 修改价格、材料等
- 编辑安装动画的顺序
- 重新计算包装方案
- 更换五金件(从素材库选择)
-
导出给客户
- 导出安装视频(这是客户唯一需要的)
- 其他资料都是内部使用,不发给客户
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
第四步:访问系统
- 打开浏览器,访问 http://localhost:3000
- 默认管理员账号在配置文件中
就这么简单!
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 组件系统架构
组件设计原则:
- 最小化变体:每个组件只有必要的变体(primary、secondary、ghost)
- 统一接口:所有组件支持
size、variant、className - 组合式设计:通过组合基础组件构建复杂组件
- 主题感知:自动适配浅色/深色模式
核心组件库:
// 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 实施指南
开发流程:
- 先设计令牌:定义颜色、间距、圆角等基础值
- 创建样式工具:实现
createStyles函数 - 构建基础组件:Card、Button、Input 等
- 组合复杂组件:使用基础组件构建
- 统一使用:所有页面使用统一组件
检查清单:
- 使用了统一的设计令牌
- 组件样式极简(无多余装饰)
- 支持深色模式
- 响应式设计正确
- 动画流畅自然
- 三层架构清晰
- 代码可复用、可扩展
设计原则总结:
- 少即是多:减少变体,简化选择
- 一致性:所有组件遵循同一套规则
- 灵活性:通过组合实现复杂场景
- 现代化:采用现代设计趋势,但不过度
十二、总结
12.1 这个系统的价值
- 对设计师: 快速生成产品信息,导出安装视频给客户
- 对客户: 收到安装视频,知道怎么安装
- 对公司: 提高效率,统一标准(五金件素材库),减少沟通成本
12.2 核心特点
- 内部系统:主要给内部人员使用,客户只需要安装视频
- 一键部署:Docker 一键安装,其他企业容易使用
- 统一标准:五金件素材库,保证所有产品用统一的五金件
- 美观现代:三层架构 UI,统一设计规范
10.2 技术难点
- DWG 解析: 需要准确提取信息
- 3D 展示: 要流畅、好看
- 动画生成: 要自然、易懂
- 价格计算: 要准确、可配置
10.3 成功的关键
- 设计师配合: 按规范命名,信息提取才准确
- 逐步完善: 先做基础功能,再逐步优化
- 用户体验: 界面要简洁,操作要简单
- 错误处理: 出错要有提示,不能直接崩溃
文档说明:
- 这是思维文档,用通俗语言说明项目思路
- 具体技术细节看架构设计文档
- 有问题随时讨论调整