Generator Material Purchase 接入文档
框架无关的 xTool 耗材购买弹窗 SDK,可在 Vue / React / 原生 JS 任意项目中通过
<script src>或import接入。
目录
1. 功能概述
调用 GeneratorMaterialPurchase.init(...) + GeneratorMaterialPurchase.open() 后,SDK 会在当前页面的 document.body 下插入一个弹窗:
- 固定
position: fixed,从屏幕右侧滑入; - 宽度 320px,高度 100vh(满屏);
- 带半透明蒙层(
rgba(0, 0, 0, 0.4)),点击蒙层关闭; - 内部包含:标题栏 / 站点切换 / 商品列表(含变体下拉、数量调整、勾选)/ 合计 + 立即购买。
业务能力:
- 自动按 IP 选 Shopify 站点(US / CA / EU / UK / FR / DE / JP / AU),支持手动切换;
- 调用
@xtool/shopify-sdk创建购物车 → 拿checkoutUrl→ 默认window.open(_, '_blank'); - 调用 atomm 后端拿耗材包列表 / IP 定位 / 分佣 trackId;
- 内置 en / zh 词条。
2. 安装与引入
2.1 通过 CDN(推荐给非 Vue/React 项目)
<!-- UMD:兼容老环境,挂全局 GeneratorMaterialPurchase -->
<script src="https://static-res.atomm.com/scripts/js/generator-sdk/generator-material-purchase/index.umd.js"></script>引入成功后,window.GeneratorMaterialPurchase 即为对外 API。
2.2 通过 npm(推荐给 Vue / React 工程)
# 内部 registry 需先配置 .npmrc → http://repository.makeblock.com/repository/npm-group/
pnpm add @atomm-developer/generator-material-purchase
# 或
npm install @atomm-developer/generator-material-purchaseimport GeneratorMaterialPurchase from '@atomm-developer/generator-material-purchase'
// 或具名导入
import { GeneratorMaterialPurchase } from '@atomm-developer/generator-material-purchase'包内同时提供 ESM (
index.es.js) 和 UMD (index.umd.js),构建工具会自动选用合适的入口。
3. 快速开始
最小可用示例 —— 两步:init + open。
<!doctype html>
<html>
<head>
<script src="https://static-res.atomm.com/scripts/js/generator-sdk/generator-material-purchase/index.umd.js"></script>
</head>
<body>
<button id="buyBtn">购买耗材</button>
<script>
// 第一步:初始化,传入业务必需参数
GeneratorMaterialPurchase.init({
generatorId: 'light-sign',
accessoryType: 'default',
})
// 第二步:点击按钮时打开弹窗
document.getElementById('buyBtn').addEventListener('click', () => {
GeneratorMaterialPurchase.open()
})
</script>
</body>
</html>4. 对外 API
SDK 共暴露 4 个方法:
interface GeneratorMaterialPurchaseApi {
init(options: PurchaseModalInitOptions): void
open(): Promise<void>
close(): void
destroy(): void
}| 方法 | 说明 |
|---|---|
init(options) | 必须最先调用。设置业务参数、语言、回调。可重复调用以更新配置 |
open() | 在 document.body 插入弹窗 DOM,并触发首次商品加载(耗材包 + IP 定位)。返回 Promise,初始化失败时会触发 onError |
close() | 播放退出动画后卸载弹窗 DOM。也可由用户点击蒙层 / X 按钮触发 |
destroy() | 强制卸载、清空内部缓存(如商品缓存),后续如需再次使用要先重新 init |
open()可被反复调用:每次打开都会重置 UI 状态(勾选、变体选择、滚动位置),但保留init设置和上一轮拉到的商品缓存(同站点不重复请求)。
5. 配置项详解
interface PurchaseModalInitOptions {
/** 必填:生成器编码,例如 'light-sign'、'flower-generator' */
generatorId: string
/** 必填:耗材类型,目前线上默认 'default' */
accessoryType: string
/** 可选:默认语言,'en' | 'zh',默认 'en' */
locale?: 'en' | 'zh' | string
/** 可选:弹窗 z-index,默认 9999 */
zIndex?: number
/** 可选:覆盖默认 prod apiBaseUrl,默认 https://xcs-api.xtool.com */
apiBaseUrl?: string
/** 可选:扩展或覆盖词条 */
messages?: Partial<Record<string, Record<string, string>>>
/** 可选:结算成功回调,默认 window.open(checkoutUrl, '_blank') */
onCheckoutSuccess?: (checkoutUrl: string) => void
/** 可选:弹窗关闭回调 */
onClose?: () => void
/** 可选:异常回调(接口失败 / 结算失败等) */
onError?: (err: unknown) => void
}字段语义说明:
generatorId— 用作后端/generator-accessory-pack接口的入参,决定拉哪个生成器的耗材列表;同时作为分佣接口的relatedObjectType。accessoryType— 同上接口的入参,目前线上统一'default'。locale— SDK 内部 i18n 当前语言。若想动态切换,重新init({ locale: ... })再open()即可。messages— 用来覆盖单条词条或新增非内置语言。形如{ en: { buy_now: 'Checkout' }, ja: { ... } }。zIndex— 当宿主页面有更高层级元素(如全局 toast)时调高,避免被遮挡。apiBaseUrl— 默认指向生产 atomm 服务。本地开发或预发联调可以指向其它环境。onCheckoutSuccess— 默认行为是新开一个标签打开 Shopify checkout 页。如果想在当前页跳转,自行实现location.href = url。onClose— 用户主动关闭时触发,可用于埋点 / 业务联动。onError— 商品加载失败、结算失败、IP 定位失败等异常都会回调。SDK 内部会同时弹一个 toast,不需要业务方再做 UI 反馈。
6. 鉴权机制
SDK 在调用 atomm 后端的所有请求里都会自动注入两个请求头:
| Header | 来源 |
|---|---|
uToken | 优先读 document.cookie.utoken,没有则读 localStorage.utoken |
lang | 读 localStorage.LANG_KEY,没有则默认 'en' |
跨域部署时请保证:
- 宿主页面已经把
utoken写入 cookie 或 localStorage(通常登录态已经完成);xcs-api.xtool.com已经在 CORS 白名单里放行调用方域名,并允许携带uToken / lang自定义头。
如果出现 401 / 403,先排查 uToken 是否存在、是否过期、是否在跨域请求中被剥离。
7. 不同框架接入示例
7.1 原生 JS(<script src>)
<script src="https://static-res.atomm.com/scripts/js/generator-sdk/generator-material-purchase/index.umd.js"></script>
<script>
GeneratorMaterialPurchase.init({
generatorId: 'light-sign',
accessoryType: 'default',
locale: 'zh',
onCheckoutSuccess: (url) => (location.href = url), // 当前页跳转而非新开
onClose: () => console.log('modal closed'),
})
document.querySelector('#buyBtn').onclick = () => GeneratorMaterialPurchase.open()
</script>7.2 Vue 3 (Composition API)
<script setup lang="ts">
import { onMounted, onBeforeUnmount } from 'vue'
import GeneratorMaterialPurchase from '@atomm-developer/generator-material-purchase'
onMounted(() => {
GeneratorMaterialPurchase.init({
generatorId: 'light-sign',
accessoryType: 'default',
})
})
onBeforeUnmount(() => {
GeneratorMaterialPurchase.destroy()
})
function handleClick() {
GeneratorMaterialPurchase.open()
}
</script>
<template>
<button @click="handleClick">购买耗材</button>
</template>7.3 React (Hooks)
import { useEffect } from 'react'
import GeneratorMaterialPurchase from '@atomm-developer/generator-material-purchase'
export function BuyButton() {
useEffect(() => {
GeneratorMaterialPurchase.init({
generatorId: 'flower-generator',
accessoryType: 'default',
locale: 'en',
})
return () => GeneratorMaterialPurchase.destroy()
}, [])
return <button onClick={() => GeneratorMaterialPurchase.open()}>Buy accessories</button>
}7.4 Vue 2(Options API)
<script>
import GeneratorMaterialPurchase from '@atomm-developer/generator-material-purchase'
export default {
mounted() {
GeneratorMaterialPurchase.init({
generatorId: 'light-sign',
accessoryType: 'default',
})
},
beforeDestroy() {
GeneratorMaterialPurchase.destroy()
},
methods: {
openModal() {
GeneratorMaterialPurchase.open()
},
},
}
</script>
<template>
<button @click="openModal">购买耗材</button>
</template>8. 国际化(i18n)
8.1 内置语言
SDK 自带 en 与 zh 两套完整词条,覆盖:
- 弹窗标题 / 关闭按钮 / 国家通知文案 / 加载中 / 空状态 / 售罄;
- 合计 / 折扣提示 / 立即购买;
- 各种错误 toast(加载失败、结算失败、缺货等);
- 8 个站点的国家名(USA / Canada / Australia / EU / Germany / France / UK / Japan)。
通过 init({ locale: 'zh' }) 切换。
8.2 扩展或覆盖词条
GeneratorMaterialPurchase.init({
generatorId: 'light-sign',
accessoryType: 'default',
locale: 'en',
messages: {
en: {
buy_now: 'Checkout securely', // 覆盖某条
},
ja: {
// 新增日语
shopify_supplies_kit: '消耗品キット',
buy_now: '今すぐ購入',
// ...其余词条若不提供,会 fallback 到 en
},
},
})8.3 词条键名一览
| key | 含义 |
|---|---|
shopify_supplies_kit | 弹窗标题 |
close | 关闭按钮 aria-label |
current_country_notice | 国家提示,模板含 {country} |
loading | 加载文案 |
no_items_in_cart | 空商品状态 |
sold_out | 售罄角标 |
total | 合计标签 |
discount_notice | 折扣码提示 |
buy_now | 立即购买按钮 |
failed_load_product_list | 拉取失败 toast |
checkout_failed | 结算失败 toast |
selected_items_out_of_stock | 勾选项已售罄 toast |
no_valid_products_selected | 无可购买商品 toast |
store_us / store_ca / store_au / store_eu / store_de / store_fr / store_uk / store_jp | 站点下拉短名 |
country_usa / country_canada / country_australia / country_eu / country_germany / country_france / country_uk / country_japan | 国家长名 |
9. 样式与视觉规格
| 维度 | 规格 |
|---|---|
| 挂载方式 | document.body.appendChild(host),host 使用 Shadow DOM 隔离样式 |
| 蒙层 | position: fixed; inset: 0; background: rgba(0,0,0,0.4);,点击关闭 |
| 弹窗 | position: fixed; top: 0; right: 0; width: 320px; height: 100vh; |
| 动画 | 蒙层 0.2s 透明度淡入,弹窗 0.25s translateX(100% → 0) |
| 字体 | Inter, system-ui, -apple-system, "Segoe UI", Roboto, sans-serif |
| 主色 | 购买按钮 #ff0035 / 强调 #070b10 / 提示橙 #ff7c23 |
| CSS 命名 | BEM 风格,统一前缀 xpm-(Xtool Purchase Modal) |
由于使用 Shadow DOM,宿主页面的 CSS reset、Tailwind、* { box-sizing: ... } 等都不会污染弹窗内部。同时弹窗内部样式也不会泄漏到宿主页面。
10. 常见问题(FAQ)
Q1:调用 open() 报错 "call init() before open()"
没有先调 init() 就调 open()。在挂载阶段(Vue onMounted / React useEffect)调用一次 init 即可。
Q2:弹窗打开了但商品列表一直转圈 / 显示空
依次排查:
- 浏览器开发者工具 Network 看
/generator-accessory-pack接口是否 200 且返回code: 0; - 若是 401 / 403,确认
uToken是否已写入 cookie 或 localStorage; - 若是 CORS 报错,找后端把调用方域名加入白名单。
Q3:结算按钮置灰,怎么办
当所有勾选项都被识别为售罄(outOfStock === true 或 availableForSale === false)时按钮会被置灰。换一个变体或换一个站点重试。
Q4:如何在当前页跳转而不是新开标签
传入 onCheckoutSuccess:
GeneratorMaterialPurchase.init({
generatorId: '...',
accessoryType: 'default',
onCheckoutSuccess: (url) => (window.location.href = url),
})Q5:切换语言后弹窗没刷新
切换语言后请重新 init({ locale: 'xx' })。如果弹窗当前正打开,先 close() 再 open()。
Q6:可以同时打开多个弹窗吗
不可以。SDK 是单例,第二次 open() 会等第一次的退出动画完成才挂载。需要并发场景请反馈。
Q7:会污染全局变量吗
仅会在 window 上挂一个 GeneratorMaterialPurchase 对象(UMD 模式下);不会修改 body 的样式;只在弹窗存在期间向 body 追加一个 host 元素,close/destroy 后移除。
Q8:Shadow DOM 内 DevTools 调试不便怎么办
Chrome DevTools 在 Settings → Preferences → Elements 勾选 "Show user agent shadow DOM" 后可以展开。或者用 window.GeneratorMaterialPurchase 在 Console 直接调方法。
11. 打包产物说明
构建命令:
pnpm build输出到 dist/:
| 文件 | 用途 | 体积(gzip) |
|---|---|---|
index.es.js | ESM 入口,给 npm / 现代打包工具 | ~15 KB |
index.umd.js | UMD,浏览器 <script src> 或 Node require | ~12 KB |
index.d.ts | TypeScript 声明文件(rollup 合并后的单文件) | — |
*.map | sourcemap,定位运行时报错 | — |
发布到 npm 时 files 字段会带上:
dist/(产物)README.md
12. 版本与变更
0.1.0(首发)
- 首个发布版本;
- 暴露
init / open / close / destroy四个方法,挂全局GeneratorMaterialPurchase; - 内置 8 个 prod Shopify 站点;
- 弹窗形态调整:右侧 320px / 满屏 / 蒙层;
- 内置 en + zh 词条;
- 接口请求自动注入
uToken+lang; - BEM CSS + Shadow DOM 样式隔离。
反馈
如使用过程中遇到 bug 或新需求,请在内部 GitLab 上对应仓库提 issue,或在团队飞书群里 @ 维护者。