定位
pf-app 是图模工坊的终端用户入口,专为手机浏览器(H5)设计,覆盖”选模板 → 传图 → 编辑 → 提交合成”全流程。它是全项目里唯一完整落地 Material Design 3 的应用。
目标用户:非专业设计人员(普通消费者、企业员工自助下单),核心场景在手机端。
- 框架:Quasar
^2.23.3+ Vue^3.5+ TypeScript - 渲染:Leafer
^2.2.9(与 pf-editor 共用)|状态:Pinia 4 + 持久化 - 图片裁剪:Cropper.js v2(
cropperjs ^2.1.1) - 端口:开发环境
8000(base/app/) - 依赖包:
@sa/code-renderer、@sa/color - 鉴权:订单访问令牌(凭订单链接
?orderNo=&token=进入,无需登录账号)
Material Design 3 设计系统
pf-app 内建完整 MD3:
src/css/app.scss/quasar.variables.scss定义 MD3 tonal palette(--md-primary-0…100、--md-neutral-*、--md-tertiary-*、--md-error-*,蓝色种子)- 专门的 M3 组件库
src/components/m3/:M3Button/M3Fab/M3LargeHeader/M3ListItem/M3QuickTile/M3SettingsCard/M3StatusChip - 纸张隐喻 + Elevation 层级 + 涟漪反馈贯穿工坊组件与卡片
(注:pf-manage 并非 Material,而是 Naive UI;pf-editor 用 Quasar 默认 Material 风格但无 MD3 palette。)
主流程
1 | 入口(订单链接 / 活动) |
页面与组件
1 | apps/pf-app/src/ |
工坊页(Workshop)
工坊页承载所有定制操作,在内存里维护一份当前 sceneJson 实例——以模板 sceneJson 为基底,把用户填值(图片 URL、文字)覆盖到对应节点,由 Leafer 实时重绘。
1 | ┌────────── sceneJson (来自模板) ──────────┐ |
提交时只把 userUploads / userTexts + 模板 id 提交给后端,sceneJson 原貌在后端按 templateId 反查并快照存 order_template_submission_items.sceneJson,避免大 JSON 传输。
图片处理能力
- 乐观上传 + 并发队列(
useImageUpload,UPLOAD_CONCURRENCY = 3):占位卡即时呈现、后台直传、单项失败重试/取消(AbortController+OssUploadAbortError),细节见 传图与裁剪管线 - 动态缩略图:
resolveOssAssetUrl(url, width)→ imgproxy WebP,列表不直连原图 - 手动裁剪:
ImageCropper.vue(Cropper.js v2)全屏裁剪、旋转、翻转 - 人脸感知自动填充:模块级
AsyncConcurrencyQueue(FACE_DETECTION_CONCURRENCY = 3)在上传后后台调faceApi.detectByImageUrl({ minScore: 0.45, maxFaces: 10 }),把faces存进图库meta,供useAutoFill做避脸放置
人脸检测调用
src/api/face.ts::faceApi.detectByImageUrl({ imageUrl, minScore?, maxFaces? }) → POST /api/app/face/detect(经 pf-service 代理到 Rust 人脸服务),返回 { image, faces:[{x,y,w,h,score}] 归一化, modelVersion, elapsedMs }。
文字编辑
文字节点遵循 sceneJson 的 fontFamily / fontSize / fill / align,用户可改内容、字号、字体(从 font_variants 拉取)、颜色、对齐。字体首次进入工坊按模板 fontFamily 集合预加载 woff2,避免渲染闪烁。
状态与持久化
Pinia + 持久化把当前草稿存 localStorage(切到微信回来能继续、断网本地续编),草稿与订单/模板绑定。注意:在途上传任务不持久化——避免把临时 blob URL 写进磁盘。
提交与状态查询
1 | pf-app |
移动端优化要点
- 触控目标 ≥ 44×44 px,双指缩放画布、长按出菜单
- 乐观上传 + 并发 3 + 失败重试,弱网友好
- 缩略图 imgproxy 动态 WebP,Zero CLS
- 远端字体未加载完回落默认字体,不阻塞 UI
与 pf-editor 的复用
同一渲染层(Leafer + sceneJson)、同套字体预加载、同套乐观上传结构约束、共享 @sa/* 包。差别:pf-editor 暴露全部节点属性用于”出版”,pf-app 只触达 editable: true 字段用于”消费”。