@fickit/transportpaint
v1.1.0
Published
TransportPaint 涂装设计器静态页面(自 buspaint 移植)
Readme
@fickit/transportpaint
TransportPaint 涂装设计器的前端包:5 个自包含单文件 HTML 页面(无构建步骤、无外部依赖),通过包内 bin 脚本复制到宿主应用的静态目录后,以 iframe 嵌入使用。
页面清单
| 文件 | 页面 |
|---|---|
| designer.html | 涂装设计器 |
| my-designs.html | 我的设计 |
| admin.html | 管理后台 |
| roof-calibrator.html | 车顶校准器 |
| template-generator.html | 模板生成器 |
另有共享引导脚本 tp-boot.js(各页面在 head 中以 <script src="tp-boot.js"></script> 加载),负责身份识别、API 请求头注入、全局 401 处理和页面间跳转。
基本集成
- 安装依赖:
pnpm add @fickit/transportpaint(workspace 内为workspace:*) - 在宿主
package.json中添加复制脚本,并在dev/build前执行:
{
"scripts": {
"copy:transportpaint": "transportpaint-copy-pages public/transportpaint",
"dev": "pnpm run copy:transportpaint && vite",
"build": "pnpm run copy:transportpaint && vite build"
}
}- 复制后页面作为
public/静态资源随宿主分发,用 iframe 嵌入{BASE_URL}transportpaint/{页面名}.html。
页面内 API 基地址固定为绝对路径 /fickit/transportpaint/api,因此页面必须与后端同源(或由宿主反向代理该路径)。
完整的宿主包装组件示例见 example/TransportPaint.vue(取自生产宿主,覆盖了本文档全部通信机制;其中 @/provides 的 injectHttp() 是该宿主的 JWT 获取方式,集成时替换为自己的身份来源即可)。
宿主通信机制
页面以 iframe 嵌入时,tp-boot.js 通过 postMessage 与宿主窗口通信。所有消息的 targetOrigin / 校验 origin 均为 location.origin(同源部署前提下不会泄露给第三方);宿主侧也应校验 event.origin 与 event.source 确为本页面 iframe。
身份下发(宿主 → 页面)
iframe 内的 fetch 默认不携带宿主的身份信息(如 JWT),需要宿主显式下发:
- 页面加载时,
tp-boot.js立即向宿主发送fickit:auth-request索要身份请求头; - 宿主回复
fickit:auth:
// 宿主侧
window.addEventListener('message', (event) => {
if (event.origin !== location.origin) return
if (event.source !== iframeEl.contentWindow) return
if (event.data?.type === 'fickit:auth-request') {
const headers = token ? { Authorization: `Bearer ${token}` } : {} // 未登录回复空对象
;(event.source as Window).postMessage({ type: 'fickit:auth', headers }, event.origin)
}
})
// 建议在 iframe load 时也主动推一次同样的 fickit:auth,作为保险- 收到回复之前页面发出的 API 请求会被
tp-boot.js挂起排队,收到后携带下发头放行;1.5 秒未收到回复则按未登录放行(兜底,避免宿主异常时页面卡死)。 - 下发的请求头只保存在页面内存中,不写入任何存储。
- 若页面收到的是空头对象,或未在 iframe 中运行,则回退到独立 demo 机制:读取
localStorage['fickit-demo-user']并注入X-User-*请求头(配合 FicKit demo 宿主使用)。
请求登录(页面 → 宿主)
API 请求返回 401,或页面主动调用 tpGoLogin() 时,页面向宿主发送 fickit:login。宿主应导航到自己的登录页/弹出登录框:
if (event.data?.type === 'fickit:login') {
router.push({ name: 'login' })
}页面间跳转(页面 → 宿主)
页面间的「新开标签页」跳转(如 my-designs 打开 designer 并载入指定设计)统一走 tpNav(page, query):
- iframe 内:向宿主发送
fickit:nav({type:'fickit:nav', page:'designer', query:'local=3'})。宿主应把它映射到自己的路由并新开标签页,使新页面仍然是「宿主路由 + iframe」的形态,鉴权链路才得以延续;query 需透传到 iframe 地址。 - 独立打开时(非 iframe):退化为直接
window.open('designer.html?local=3')。
if (event.data?.type === 'fickit:nav') {
const { page, query } = event.data
const url = router.resolve({ name: tpRouteNames[page], query: Object.fromEntries(new URLSearchParams(query)) }).href
window.open(url, '_blank')
}注意:宿主的 iframe src 应把自身路由的 query 原样拼接到 html 地址上(如 designer.html?local=3),页面内部依赖该 query 载入对应设计。
消息类型汇总
| type | 方向 | 载荷 | 含义 |
|---|---|---|---|
| fickit:auth-request | 页面 → 宿主 | 无 | 页面启动,索要身份请求头 |
| fickit:auth | 宿主 → 页面 | {headers: Record<string,string>} | 下发请求头(如 Authorization);空对象表示未登录 |
| fickit:login | 页面 → 宿主 | 无 | 请求宿主弹出/跳转登录 |
| fickit:nav | 页面 → 宿主 | {page: string, query: string} | 请求跳转到其他 tp 页面(新开标签页) |
目录结构
packages/@fickit/transportpaint/
├── pages/ # 5 个页面 + tp-boot.js(被复制到宿主静态目录)
├── scripts/copy-pages.js # bin: transportpaint-copy-pages <目标目录>
└── example/
└── TransportPaint.vue # 宿主包装组件参考实现(含全部通信机制)