npm package: @jieyin/editor-sdk
Description: 基于 Vue 3 + Fabric.js 的可视化设计编辑器 SDK，支持产品选型、刀版/变形模板、图层编辑、预览渲染与设计保存/还原。
Latest version: 1.1.140

# @jieyin/editor-sdk 使用文档

基于 Vue 3 + Fabric.js 的可视化设计编辑器 SDK，支持产品选型、刀版/变形模板、图层编辑、预览渲染与设计保存/还原。

---

## 目录

- [安装与依赖](#安装与依赖)
- [快速开始](#快速开始)
- [Editor 组件](#editor-组件)
- [类型定义](#类型定义)
- [工具与服务](#工具与服务)
- [进阶：Composables](#进阶composables)
- [常见场景示例](#常见场景示例)

---

## 安装与依赖

```bash
npm install @jieyin/editor-sdk
# 或
pnpm add @jieyin/editor-sdk
# 或
yarn add @jieyin/editor-sdk
```

### 对等依赖（需在业务项目中安装）

| 依赖  | 版本     |
| --- | ------ |
| vue | ^3.5.0 |

### 引入样式

```ts
import '@jieyin/editor-sdk/style.css'
```

---

## 快速开始

### 方式一：全局注册

```ts
// main.ts
import { createApp } from 'vue'
import App from './App.vue'
import { install } from '@jieyin/editor-sdk'
import '@jieyin/editor-sdk/style.css'

const app = createApp(App)
app.use(install)
app.mount('#app')
```

```vue
<template>
  <Editor
    :product="product"
    :basic-info="basicInfo"
    :diecut-data="diecutData"
    :deformation-data="deformationData"
    :license-config="licenseConfig"
    :file-token="fileToken"
    @save-design="onSaveDesign"
  />
</template>

<script setup lang="ts">
import { ref } from 'vue'
import type { ProductItem, BasicInfo, DiecutResponse, DeformationResponse, LicenseConfig } from '@jieyin/editor-sdk'

const product = ref<ProductItem | null>(null)
const basicInfo = ref<BasicInfo | null>(null)
const diecutData = ref<DiecutResponse | null>(null)
const deformationData = ref<DeformationResponse | null>(null)
const fileToken = ref<string | null>(null)
const licenseConfig: LicenseConfig = {
  licenseKey: 'YOUR_LICENSE_KEY',
  apiBaseUrl: 'https://your-license-api.com'
}

function onSaveDesign() {
  // 调用业务保存接口
}
</script>
```

### 方式二：按需引入组件

```vue
<template>
  <Editor
    :product="product"
    :basic-info="basicInfo"
    :diecut-data="diecutData"
    :deformation-data="deformationData"
    :options="{ showTopbar: true, showSidebar: true, showPreview: true }"
    :license-config="licenseConfig"
    :file-token="fileToken"
    :on-save-design="onSaveDesign"
    :on-object-modified="onObjectModified"
  />
</template>

<script setup lang="ts">
import { Editor, type LicenseConfig, type ProductItem, type BasicInfo, type DiecutResponse, type DeformationResponse } from '@jieyin/editor-sdk'
import '@jieyin/editor-sdk/style.css'

// ... 同上
</script>
```

---

## Editor 组件

### Props

| 属性                       | 类型                                                           | 默认值    | 说明                                          |
| ------------------------ | ------------------------------------------------------------ | ------ | ------------------------------------------- |
| `product`                | `ProductItem \| null`                                        | `null` | 当前选中的产品（单产品模式直接传入）                          |
| `basicInfo`              | `BasicInfo \| null`                                          | `null` | 产品基础信息（名称、尺码、颜色等）                           |
| `diecutData`             | `DiecutResponse \| null`                                     | `null` | 刀版数据（含 jsonURL、resources 等）                 |
| `deformationData`        | `DeformationResponse \| null`                                | `null` | 变形/模板数据                                     |
| `options`                | `EditorOptions`                                              | 见下     | 界面与行为配置                                     |
| `productListApi`         | `ProductListApi`                                             | -      | 产品列表接口（列表模式必传）                              |
| `productDetailApi`       | `ProductDetailApi`                                           | -      | 产品详情接口（列表模式必传）                              |
| `productListPageSize`    | `number`                                                     | -      | 产品列表每页条数                                    |
| `productListInitialPage` | `number`                                                     | -      | 产品列表初始页码                                    |
| `initialProductId`       | `string \| number`                                           | -      | 进入时默认选中的产品 ID；不在第一页时也会按 ID 加载详情并展示   |
| `fileBaseUrl`            | `string`                                                     | -      | 文件资源基础 URL（用于代理/拼接）                         |
| `licenseConfig`          | `LicenseConfig`                                              | -      | License 配置（不传则不做 License 校验）                |
| `fileToken`              | `string \| null`                                             | `null` | 文件访问 Token（用于带鉴权的图片/资源）                     |
| `onObjectModified`       | `(getDiecutStateMap: () => DiecutStateMapResult) => void` | -      | 画布内容变更回调，调用 `getDiecutStateMap()` 可得 `{ stateMap, selectedColor, selectedSize, selectedMode, designCanvasSize }` |
| `onRenderAllTemplates`   | `(renderMethod) => Promise<...>`                             | -      | 自定义「渲染所有模板」逻辑（如上传到服务器）                      |
| `onSaveDesign`           | `() => void \| Promise<void>`                                | -      | 保存设计（传入则在 Topbar 显示保存按钮）                    |
| `onGoHome`               | `() => void`                                                 | -      | 返回首页（传入则在 Topbar 显示返回按钮）                    |
| `onTemplateReady`        | `() => void`                                                 | -      | 首次模板加载完成后调用（可用于从 completeJsonUrl 还原设计）      |

#### EditorOptions

```ts
interface EditorOptions {
  showTopbar?: boolean    // 是否显示顶栏，默认 true
  showSidebar?: boolean   // 是否显示左侧栏，默认 true
  showPreview?: boolean   // 是否显示预览面板，默认 true
  enableGrid?: boolean   // 是否启用网格，默认 true
  usePreviewEffect?: boolean  // 是否使用预览效果组件，默认 true
}
```

### 暴露方法（ref 调用）

> **详细入参/出参说明**：见 [API 对接文档](./docs/API-对接文档.md)，含 `getDiecutStateMap`、`renderAllTemplates` 等核心方法的入参、出参及对接示例。

通过 `ref` 获取组件实例后，可调用以下方法：

| 方法                               | 说明                                                                              |
| -------------------------------- | ------------------------------------------------------------------------------- |
| `renderAllTemplates()`           | 渲染当前产品下所有模板，返回 `Promise<Array<{ index: number; bitmap: ImageBitmap \| null }>>` |
| `triggerRenderAllTemplates()`    | 触发一次「渲染所有模板」（会走 `onRenderAllTemplates` 若已传）                                     |
| `getDiecutStateMap()`            | 获取带元数据的设计状态：`{ stateMap, selectedColor, selectedSize, selectedMode, designCanvasSize }`（用于保存设计）               |
| `getCurrentProductName()`        | 获取当前产品名称                                                                        |
| `loadCanvasJson(diecutId, json)` | 按刀版 ID 加载一份画布 JSON                                                              |
| `loadDesignStateMap(payload)`    | 还原设计：支持 `{ stateMap, selectedColor?, selectedSize? }` 或旧格式 `Record<diecutId, fabricJson>` |

示例：

```vue
<template>
  <Editor ref="editorRef" ... />
</template>

<script setup lang="ts">
import { ref } from 'vue'
import { Editor } from '@jieyin/editor-sdk'

const editorRef = ref<InstanceType<typeof Editor> | null>(null)

async function save() {
  const result = editorRef.value?.getDiecutStateMap?.()
  const name = editorRef.value?.getCurrentProductName?.()
  // result 为 { stateMap, selectedColor, selectedSize, selectedMode, designCanvasSize }，上传到后端...
}

async function restore(payload: { stateMap: Record<string, unknown>; selectedColor?: string; selectedSize?: string } | Record<string, unknown>) {
  editorRef.value?.loadDesignStateMap?.(payload)
}
</script>
```

---

## 类型定义

从 `@jieyin/editor-sdk` 可导出以下常用类型（详见 `src/types.ts`）：

- **产品与配置**：`ProductItem`、`BasicInfo`、`EditorOptions`、`EditorProps`、`LicenseConfig`
- **刀版与变形**：`DiecutResource`、`DiecutItem`、`DiecutResponse`、`DeformationInfo`、`DeformationGroup`、`DeformationResponse`、`DeformationSizeGroup`、`DeformationColorGroup`
- **接口约定**：`ProductListApi`、`ProductDetailApi`、`ProductListResult`、`ProductDetailResult`

```ts
import type {
  ProductItem,
  BasicInfo,
  DiecutResponse,
  DeformationResponse,
  EditorOptions,
  LicenseConfig,
  ProductListApi,
  ProductDetailApi
} from '@jieyin/editor-sdk'
```

---

## 工具与服务

### 文件 Token（fileToken）

用于带鉴权的文件 URL（如图片、刀版资源）。支持「外部注入」或「内部拉取 + 自动附加」。

```ts
import {
  setExternalFileToken,
  getFileToken,
  clearFileToken,
  ensureFileToken,
  appendFileTokenToUrl,
  appendFileTokenIfPresent,
  withFileToken,
  fetchWithFileToken,
  setFileTokenFetchers,
  isFileTokenExpired
} from '@jieyin/editor-sdk'
```

- **外部注入**：业务侧维护 Token，通过 `setExternalFileToken(token)` 注入；Editor 内部请求文件时会自动用该 Token 拼到 URL。
- **内部拉取**：`setFileTokenFetchers({ fetchFileToken, fetchFileTokenRefresh })` 后，`ensureFileToken()` / `fetchWithFileToken(url)` 会负责获取/刷新并附加 Token。

### License

```ts
import {
  LicenseAPI,
  LicenseManager,
  setLicenseInfo,
  getLicenseInfo,
  clearLicenseInfo,
  TokenRefresher,
  withRetry
} from '@jieyin/editor-sdk'
```

- 在 Editor 上通过 `licenseConfig` 传入即可完成校验与刷新；也可在非 Editor 场景下单独使用 `LicenseManager` / `LicenseAPI`。

### 渲染服务（renderService）

用于按 JSON 渲染位图、蒙版、混合等（多用于预览与导出）：

```ts
import type { IRenderByJson } from '@jieyin/editor-sdk'
// 以及 renderService 下具体方法按需使用
```

---

## 进阶：Composables

Editor 内部由多个 composable 组成（产品数据、模板、刀版、画布、图层、工具状态等）。若你需要在自定义页面中复用某一块能力，可直接从 SDK 引入对应 composable（注：部分依赖 Editor 内部上下文，建议以「使用 Editor 组件」为主，按需再抽 composable）。

导出列表见 `sdk/src/components/Editor/composables/index.ts`，例如：

- `useLicense`、`useProductData`、`useToolState`、`useTemplateManager`
- `useCanvasManager`、`useLayerManager`、`useDiecutManager`
- `useImageHandler`、`useTextHandler`、`useLayerOperations`、`useObjectAdder`
- `usePreviewManager`、`usePreviewRenderer`、`usePreviewUpdateQueue`
- `useTemplateLoader`、`useCanvasStateByDiecut`、`useCanvasViewport`
- `useEditorWatchers`、`useCopyToDiecuts`、`useClearCanvas`、`useEditorReset`、`useDownloadPng`
- 以及相关类型：`TemplateItem`、`Layer`、`DiecutOption`、`UseTemplateLoaderOptions` 等

更细的 composable 说明见：`sdk/src/components/Editor/README.md`。

---

## 常见场景示例

### 单产品模式（直接传产品与详情数据）

```vue
<Editor
  :product="product"
  :basic-info="basicInfo"
  :diecut-data="diecutData"
  :deformation-data="deformationData"
  :file-token="fileToken"
  :license-config="licenseConfig"
  :on-save-design="onSaveDesign"
/>
```

数据由业务侧请求产品详情接口得到后，赋给 `basicInfo`、`diecutData`、`deformationData` 即可。

### 产品列表模式（分页 + 选产品拉详情）

```vue
<Editor
  :product-list-api="productListApi"
  :product-detail-api="productDetailApi"
  :product-list-page-size="12"
  :product-list-initial-page="1"
  :initial-product-id="initialProductId"
  :file-token="fileToken"
  :license-config="licenseConfig"
  :on-save-design="onSaveDesign"
/>
```

- `productListApi(page, pageSize)` 返回 `{ rows: ProductItem[], total?: number }`
- `productDetailApi(productId)` 返回 `{ basicInfo, diecutData, deformationData }`
- `initialProductId` 若落在第一页列表中，进入后会默认选中该产品。

### 保存设计（拿状态 Map + 调用业务接口）

```ts
const editorRef = ref<InstanceType<typeof Editor> | null>(null)

async function onSaveDesign() {
  const result = editorRef.value?.getDiecutStateMap?.()
  const productName = editorRef.value?.getCurrentProductName?.()
  if (!result?.stateMap) return
  await saveDesignTask({ productName, ...result })
}
```

### 从已保存设计还原（如编辑我的设计）

1. 拉取设计详情，拿到 `completeJsonUrl` 或按刀版分组的 `stateMap`。

2. 若用 `stateMap`：先让 Editor 完成模板加载（产品、刀版、变形数据就绪），在 `onTemplateReady` 中调用：
   
   ```ts
   editorRef.value?.loadDesignStateMap?.(payload)
   ```

3. 若用 `completeJsonUrl`：在 `onTemplateReady` 中 fetch 该 JSON，解析成 `{ stateMap, selectedColor?, selectedSize? }` 或旧格式的 stateMap，再调用 `loadDesignStateMap(payload)`。

### License 配置

```ts
const licenseConfig: LicenseConfig = {
  licenseKey: 'YOUR_LICENSE_KEY',
  apiBaseUrl: 'https://your-license-api.com',
  timeout: 10000,
  enableAutoRefresh: true,
  refreshThreshold: 0.3,
  onError: (err) => console.error('License Error:', err),
  onSuccess: () => console.log('License OK'),
  onTokenRefresh: (token) => { /* 可选：同步到业务 */ }
}
```

不传 `licenseConfig` 时，Editor 不进行 License 校验，直接进入编辑界面。

### 文件 Token（外部注入，如登录后从业务接口拿）

```ts
const fileToken = ref<string | null>(null)

onMounted(async () => {
  const res = await yourAuthApi.getFileToken()
  fileToken.value = res.file_token
  // 可选：同步给 SDK 内部使用
  setExternalFileToken(res.file_token)
})
```

```vue
<Editor :file-token="fileToken" ... />
```

业务侧负责 Token 刷新时，保持 `fileToken` 或 `setExternalFileToken` 更新即可。

---

## 版本与构建

- 当前包名：`@jieyin/editor-sdk`，版本见 `package.json`。
- 构建：`npm run build`（会先执行 wasm 构建再打 SDK 包）。
- 产物：`dist/editor-sdk.es.js`、`dist/style.css`、`dist/index.d.ts`。

如有问题或需要定制，请联系维护团队。