Skip to content

Generator Material Purchase 接入文档

框架无关的 xTool 耗材购买弹窗 SDK,可在 Vue / React / 原生 JS 任意项目中通过 <script src>import 接入。


目录

  1. 功能概述
  2. 安装与引入
  3. 快速开始
  4. 对外 API
  5. 配置项详解
  6. 鉴权机制
  7. 不同框架接入示例
  8. 国际化(i18n)
  9. 样式与视觉规格
  10. 常见问题(FAQ)
  11. 打包产物说明
  12. 版本与变更

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 项目)

html
<!-- 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 工程)

bash
# 内部 registry 需先配置 .npmrc → http://repository.makeblock.com/repository/npm-group/
pnpm add @atomm-developer/generator-material-purchase
# 或
npm install @atomm-developer/generator-material-purchase
ts
import 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

html
<!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 个方法:

ts
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. 配置项详解

ts
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
langlocalStorage.LANG_KEY,没有则默认 'en'

跨域部署时请保证:

  1. 宿主页面已经把 utoken 写入 cookie 或 localStorage(通常登录态已经完成);
  2. xcs-api.xtool.com 已经在 CORS 白名单里放行调用方域名,并允许携带 uToken / lang 自定义头。

如果出现 401 / 403,先排查 uToken 是否存在、是否过期、是否在跨域请求中被剥离。


7. 不同框架接入示例

7.1 原生 JS(<script src>

html
<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)

vue
<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)

tsx
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)

vue
<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 自带 enzh 两套完整词条,覆盖:

  • 弹窗标题 / 关闭按钮 / 国家通知文案 / 加载中 / 空状态 / 售罄;
  • 合计 / 折扣提示 / 立即购买;
  • 各种错误 toast(加载失败、结算失败、缺货等);
  • 8 个站点的国家名(USA / Canada / Australia / EU / Germany / France / UK / Japan)。

通过 init({ locale: 'zh' }) 切换。

8.2 扩展或覆盖词条

ts
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:弹窗打开了但商品列表一直转圈 / 显示空

依次排查:

  1. 浏览器开发者工具 Network 看 /generator-accessory-pack 接口是否 200 且返回 code: 0
  2. 若是 401 / 403,确认 uToken 是否已写入 cookie 或 localStorage;
  3. 若是 CORS 报错,找后端把调用方域名加入白名单。

Q3:结算按钮置灰,怎么办

当所有勾选项都被识别为售罄(outOfStock === trueavailableForSale === false)时按钮会被置灰。换一个变体或换一个站点重试。

Q4:如何在当前页跳转而不是新开标签

传入 onCheckoutSuccess

ts
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. 打包产物说明

构建命令:

bash
pnpm build

输出到 dist/

文件用途体积(gzip)
index.es.jsESM 入口,给 npm / 现代打包工具~15 KB
index.umd.jsUMD,浏览器 <script src> 或 Node require~12 KB
index.d.tsTypeScript 声明文件(rollup 合并后的单文件)
*.mapsourcemap,定位运行时报错

发布到 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,或在团队飞书群里 @ 维护者。

MIT Licensed