wireless-desc-converter 1.0.11 → 1.0.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -0
- package/docs/integration-guide.md +450 -0
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -0,0 +1,450 @@
|
|
|
1
|
+
# 实战集成指南
|
|
2
|
+
|
|
3
|
+
本文档基于 `fe-upload-tb` 项目的真实上线经验,展示如何在淘宝商品一键上传场景中集成 `wireless-desc-converter`,重点介绍 **文字合图解析器** 和 **图片尺寸解析器** 的实现。
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## 目录
|
|
8
|
+
|
|
9
|
+
- [整体流程](#整体流程)
|
|
10
|
+
- [图片尺寸解析器 createImageSizeResolver](#图片尺寸解析器-createimagesizeresolver)
|
|
11
|
+
- [文字合图解析器 createTextImageResolver](#文字合图解析器-createtextimageresolver)
|
|
12
|
+
- [提交入口集成](#提交入口集成)
|
|
13
|
+
- [淘宝图片空间上传接口](#淘宝图片空间上传接口)
|
|
14
|
+
- [关键约束与避坑](#关键约束与避坑)
|
|
15
|
+
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
## 整体流程
|
|
19
|
+
|
|
20
|
+
```
|
|
21
|
+
原始 HTML 富文本
|
|
22
|
+
|
|
|
23
|
+
v
|
|
24
|
+
htmlToWirelessDesc(html, {
|
|
25
|
+
imageSize: createImageSizeResolver(), // 补全图片宽高
|
|
26
|
+
textImage: textImageHandle.resolver, // 文字 → Canvas渲染 → 上传 → 返回图片URL
|
|
27
|
+
})
|
|
28
|
+
|
|
|
29
|
+
v
|
|
30
|
+
wirelessDesc JSON ──> /api/publish/add 提交
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
核心思路:`htmlToWirelessDesc` 负责解析 HTML 生成结构化 JSON,但有两件事需要宿主项目协助:
|
|
34
|
+
|
|
35
|
+
1. **图片尺寸** — 淘宝接口要求 `image_hot_area_N.image.height` 不能为空,但 HTML 中的 `<img>` 标签不一定带宽高属性
|
|
36
|
+
2. **文字合图** — 淘宝新版编辑器要求文字内容必须合成图片提交(`text_N.images` 强必填),合图需要宿主项目用 Canvas 渲染并上传到淘宝图片空间
|
|
37
|
+
|
|
38
|
+
---
|
|
39
|
+
|
|
40
|
+
## 图片尺寸解析器 createImageSizeResolver
|
|
41
|
+
|
|
42
|
+
### 职责
|
|
43
|
+
|
|
44
|
+
为 `image_hot_area_N` 模块补全图片的真实宽高。
|
|
45
|
+
|
|
46
|
+
### 解析优先级(三级降级)
|
|
47
|
+
|
|
48
|
+
```
|
|
49
|
+
1. window.imgs_move_res(图片搬家结果) ── 同步,最快
|
|
50
|
+
↓ 未命中
|
|
51
|
+
2. loadImg 加载图片获取真实尺寸 ── 异步,准确
|
|
52
|
+
↓ 失败
|
|
53
|
+
3. 返回 null ── 由 fillEmptyValues 兜底估算
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
### 完整实现
|
|
57
|
+
|
|
58
|
+
```javascript
|
|
59
|
+
/**
|
|
60
|
+
* 创建图片尺寸解析器,供 htmlToWirelessDesc 的 imageSize 选项使用
|
|
61
|
+
*
|
|
62
|
+
* @returns {Function} async resolver ({ url }) => Promise<{ width, height } | null>
|
|
63
|
+
*/
|
|
64
|
+
export function createImageSizeResolver() {
|
|
65
|
+
var moveResults = getImageMoveResult(); // 从 window.imgs_move_res 读取
|
|
66
|
+
|
|
67
|
+
return async function({ url }) {
|
|
68
|
+
if (!url) return null;
|
|
69
|
+
|
|
70
|
+
// 1. 从搬家结果中查找匹配的 pic_pixel
|
|
71
|
+
for (var i = 0; i < moveResults.length; i++) {
|
|
72
|
+
var item = moveResults[i] || {};
|
|
73
|
+
// 匹配 pic_new(搬家后 HTML 中的 URL)或 pic_old(未搬家的原始 URL)
|
|
74
|
+
if ((item.pic_new && url.indexOf(item.pic_new) === 0) ||
|
|
75
|
+
(item.pic_old && url.indexOf(item.pic_old) === 0)) {
|
|
76
|
+
if (item.pic_pixel) {
|
|
77
|
+
var parts = item.pic_pixel.split(/[xX×]/);
|
|
78
|
+
var w = parseInt(parts[0], 10);
|
|
79
|
+
var h = parseInt(parts[1], 10);
|
|
80
|
+
if (w > 0 && h > 0) {
|
|
81
|
+
return { width: w, height: h };
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
break; // 找到匹配但 pic_pixel 无效,跳出走 fallback
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// 2. 通过 loadImg 异步加载获取真实尺寸
|
|
89
|
+
try {
|
|
90
|
+
var imgData = await loadImg(url);
|
|
91
|
+
if (imgData && !imgData.hasError && imgData.width > 1 && imgData.height > 1) {
|
|
92
|
+
return { width: imgData.width, height: imgData.height };
|
|
93
|
+
}
|
|
94
|
+
} catch (e) {}
|
|
95
|
+
|
|
96
|
+
// 3. 返回 null,由 fillEmptyValues 兜底
|
|
97
|
+
return null;
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
### 为什么用三级降级?
|
|
103
|
+
|
|
104
|
+
| 级别 | 来源 | 速度 | 场景 |
|
|
105
|
+
|------|------|------|------|
|
|
106
|
+
| 1 | 图片搬家结果 `pic_pixel` | 同步,O(1) 查找 | 用户已执行过图片搬家(常见路径) |
|
|
107
|
+
| 2 | `new Image()` 加载 | 异步,需网络请求 | 搬家结果未命中或未搬家 |
|
|
108
|
+
| 3 | `null` → 估算 | 即时 | 网络失败或跨域,由 `fillEmptyValues` 按宽高比估算 |
|
|
109
|
+
|
|
110
|
+
---
|
|
111
|
+
|
|
112
|
+
## 文字合图解析器 createTextImageResolver
|
|
113
|
+
|
|
114
|
+
### 职责
|
|
115
|
+
|
|
116
|
+
淘宝新版编辑器要求文字段落必须以**图片形式**提交(`text_N.images` 强必填,不允许空数组)。此解析器负责:
|
|
117
|
+
|
|
118
|
+
1. 用 Canvas 将文字渲染成图片
|
|
119
|
+
2. 导出 base64
|
|
120
|
+
3. 上传到淘宝图片空间
|
|
121
|
+
4. 返回 `[{ url, width, height }]`
|
|
122
|
+
|
|
123
|
+
### 关键约束
|
|
124
|
+
|
|
125
|
+
- 图片宽度固定 **620px**(MODULE_WIDTH)
|
|
126
|
+
- 图片高度根据文本内容自动计算,上限 **2000px**
|
|
127
|
+
- 字体使用系统默认 `sans-serif`(Canvas 不支持加载 web font)
|
|
128
|
+
- 上传失败 / 返回空数组 / 抛异常 → `wireless-desc-converter` 将该 `text_N` 模块**整体从 props 移除**
|
|
129
|
+
|
|
130
|
+
### 完整实现
|
|
131
|
+
|
|
132
|
+
```javascript
|
|
133
|
+
/**
|
|
134
|
+
* 创建文字合图解析器
|
|
135
|
+
*
|
|
136
|
+
* @param {Object} [uploaderOptions]
|
|
137
|
+
* @param {string|number} [uploaderOptions.cid=0] - 淘宝图片空间相册 id
|
|
138
|
+
* @returns {{ resolver: Function, getFailCount: Function }}
|
|
139
|
+
*/
|
|
140
|
+
export function createTextImageResolver(uploaderOptions) {
|
|
141
|
+
uploaderOptions = uploaderOptions || {};
|
|
142
|
+
var failCount = 0;
|
|
143
|
+
|
|
144
|
+
var resolver = async function({ text, styles, index }) {
|
|
145
|
+
if (!text) return [];
|
|
146
|
+
|
|
147
|
+
styles = styles || {};
|
|
148
|
+
var fontSize = parseInt(styles.fontSize, 10) || 14;
|
|
149
|
+
var color = styles.color || '#333333';
|
|
150
|
+
var textAlign = styles.textAlign || 'left';
|
|
151
|
+
var paddingTop = 10;
|
|
152
|
+
var paddingBottom = 10;
|
|
153
|
+
var paddingLeft = 20;
|
|
154
|
+
var paddingRight = 20;
|
|
155
|
+
var canvasWidth = 620; // MODULE_WIDTH
|
|
156
|
+
var lineHeight = Math.round(fontSize * 1.5);
|
|
157
|
+
var usableWidth = canvasWidth - paddingLeft - paddingRight;
|
|
158
|
+
|
|
159
|
+
// --- 第一步:用隐藏 canvas 测量文字并分行 ---
|
|
160
|
+
var canvas = document.createElement('canvas');
|
|
161
|
+
var ctx = canvas.getContext('2d');
|
|
162
|
+
ctx.font = fontSize + 'px sans-serif';
|
|
163
|
+
|
|
164
|
+
var lines = [];
|
|
165
|
+
var rawLines = text.split('\n');
|
|
166
|
+
for (var ri = 0; ri < rawLines.length; ri++) {
|
|
167
|
+
var rawLine = rawLines[ri];
|
|
168
|
+
if (!rawLine) { lines.push(''); continue; }
|
|
169
|
+
// 逐字符测量,超过可用宽度则换行
|
|
170
|
+
var currentLine = '';
|
|
171
|
+
for (var ci = 0; ci < rawLine.length; ci++) {
|
|
172
|
+
var testLine = currentLine + rawLine[ci];
|
|
173
|
+
var measured = ctx.measureText(testLine).width;
|
|
174
|
+
if (measured > usableWidth && currentLine) {
|
|
175
|
+
lines.push(currentLine);
|
|
176
|
+
currentLine = rawLine[ci];
|
|
177
|
+
} else {
|
|
178
|
+
currentLine = testLine;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
if (currentLine) lines.push(currentLine);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
if (!lines.length) lines = [text];
|
|
185
|
+
|
|
186
|
+
// --- 第二步:计算画布高度并绘制 ---
|
|
187
|
+
var canvasHeight = paddingTop + lines.length * lineHeight + paddingBottom;
|
|
188
|
+
if (canvasHeight > 2000) canvasHeight = 2000; // 限制最大高度
|
|
189
|
+
|
|
190
|
+
canvas.width = canvasWidth;
|
|
191
|
+
canvas.height = canvasHeight;
|
|
192
|
+
ctx = canvas.getContext('2d');
|
|
193
|
+
ctx.fillStyle = '#ffffff';
|
|
194
|
+
ctx.fillRect(0, 0, canvasWidth, canvasHeight);
|
|
195
|
+
ctx.font = fontSize + 'px sans-serif';
|
|
196
|
+
ctx.fillStyle = color;
|
|
197
|
+
ctx.textAlign = textAlign === 'center' ? 'center'
|
|
198
|
+
: (textAlign === 'right' ? 'right' : 'left');
|
|
199
|
+
var x = textAlign === 'center' ? canvasWidth / 2
|
|
200
|
+
: (textAlign === 'right' ? canvasWidth - paddingRight : paddingLeft);
|
|
201
|
+
var y = paddingTop + fontSize; // baseline
|
|
202
|
+
|
|
203
|
+
var maxLines = Math.floor((canvasHeight - paddingTop - paddingBottom) / lineHeight);
|
|
204
|
+
for (var li = 0; li < lines.length && li < maxLines; li++) {
|
|
205
|
+
ctx.fillText(lines[li], x, y);
|
|
206
|
+
y += lineHeight;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
// --- 第三步:导出 base64 ---
|
|
210
|
+
var base64;
|
|
211
|
+
try {
|
|
212
|
+
base64 = canvas.toDataURL('image/jpeg', 0.9);
|
|
213
|
+
} catch (e) {
|
|
214
|
+
failCount++;
|
|
215
|
+
return [];
|
|
216
|
+
}
|
|
217
|
+
canvas.width = canvas.height = 0; // 释放内存
|
|
218
|
+
|
|
219
|
+
if (!base64 || base64 === 'data:,') {
|
|
220
|
+
failCount++;
|
|
221
|
+
return [];
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
// --- 第四步:上传到淘宝图片空间 ---
|
|
225
|
+
try {
|
|
226
|
+
var { pathUploadBase64 } = getImageUploadApiPath();
|
|
227
|
+
var gid = window.GOOD_ID || (window.zwd_data && window.zwd_data.gid) || '';
|
|
228
|
+
var zdid = (window.zwd_data && window.zwd_data.zdid) || '';
|
|
229
|
+
var title = gid + '_' + zdid + '_描述文字_' + (index || 1);
|
|
230
|
+
|
|
231
|
+
var uploadRes = await fetch(pathUploadBase64, {
|
|
232
|
+
method: 'POST',
|
|
233
|
+
headers: { 'Content-Type': 'application/json; charset=UTF-8' },
|
|
234
|
+
body: JSON.stringify({
|
|
235
|
+
cid: uploaderOptions.cid || 0, // 相册 id
|
|
236
|
+
title: title, // 图片标题(命名规则与主图/属性图统一)
|
|
237
|
+
img: base64, // base64 图片数据
|
|
238
|
+
gid: gid, // 商品 id
|
|
239
|
+
item_no: (window.zwd_data && window.zwd_data.item_no) || '',
|
|
240
|
+
createCategoryForGoods: getAutoCreateFolderOrNot(), // 是否自动创建商品图片文件夹
|
|
241
|
+
}),
|
|
242
|
+
credentials: 'include',
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
if (uploadRes.ok) {
|
|
246
|
+
var res = await uploadRes.json();
|
|
247
|
+
if (res && res.is_success && res.result && res.result.picture) {
|
|
248
|
+
var picUrl = res.result.picture.picture_path;
|
|
249
|
+
if (picUrl) {
|
|
250
|
+
return [{ url: picUrl, width: canvasWidth, height: canvasHeight }];
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
} catch (e) {
|
|
255
|
+
console.log('---> createTextImageResolver: upload failed', e);
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
failCount++;
|
|
259
|
+
return [];
|
|
260
|
+
};
|
|
261
|
+
|
|
262
|
+
return {
|
|
263
|
+
resolver: resolver,
|
|
264
|
+
getFailCount: function() { return failCount; }
|
|
265
|
+
};
|
|
266
|
+
}
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
### 返回值结构
|
|
270
|
+
|
|
271
|
+
```javascript
|
|
272
|
+
{
|
|
273
|
+
resolver: Function, // 传给 htmlToWirelessDesc 的 textImage 选项
|
|
274
|
+
getFailCount: Function // 返回本次合图失败的文字段数量(用于弹窗提示)
|
|
275
|
+
}
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
---
|
|
279
|
+
|
|
280
|
+
## 提交入口集成
|
|
281
|
+
|
|
282
|
+
以下是 `fe-upload-tb` 中 `UploadBtn/index.js` 的真实集成代码:
|
|
283
|
+
|
|
284
|
+
```javascript
|
|
285
|
+
import {
|
|
286
|
+
createImageSizeResolver,
|
|
287
|
+
createTextImageResolver,
|
|
288
|
+
htmlToWirelessDesc,
|
|
289
|
+
isWirelessDescUser,
|
|
290
|
+
} from '../../../util';
|
|
291
|
+
|
|
292
|
+
// 在提交方法中:
|
|
293
|
+
async submitData() {
|
|
294
|
+
// ... 前置处理 ...
|
|
295
|
+
|
|
296
|
+
let descField = {};
|
|
297
|
+
if (isWirelessDescUser()) {
|
|
298
|
+
// 1. 创建文字合图解析器(传入相册 id)
|
|
299
|
+
const textImageHandle = createTextImageResolver({
|
|
300
|
+
cid: (this.props.goods.album || {}).id || 0
|
|
301
|
+
});
|
|
302
|
+
|
|
303
|
+
// 2. 调用 htmlToWirelessDesc,同时传入两个解析器
|
|
304
|
+
const wirelessDesc = await this.buildWirelessDesc(html, {
|
|
305
|
+
imageSize: createImageSizeResolver(),
|
|
306
|
+
textImage: textImageHandle.resolver,
|
|
307
|
+
});
|
|
308
|
+
|
|
309
|
+
// 3. 检查合图失败数量,弹窗询问用户是否继续
|
|
310
|
+
const textFailCount = textImageHandle.getFailCount();
|
|
311
|
+
if (textFailCount > 0) {
|
|
312
|
+
const confirmed = await new Promise(function(resolve) {
|
|
313
|
+
Confirm.open(
|
|
314
|
+
'文字合图失败',
|
|
315
|
+
textFailCount + ' 段文字内容合图失败,这些段落将不会显示在商品详情中。是否继续上传?',
|
|
316
|
+
function() { resolve(true); },
|
|
317
|
+
function() { resolve(false); }
|
|
318
|
+
);
|
|
319
|
+
});
|
|
320
|
+
if (!confirmed) {
|
|
321
|
+
this.setState({ loading: false });
|
|
322
|
+
return; // 用户取消,中止提交
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
descField = { wirelessDesc: wirelessDesc };
|
|
327
|
+
} else {
|
|
328
|
+
// 旧版用户仍用 desc 字段
|
|
329
|
+
descField = { desc: html };
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
// 4. 构建 submitData 并提交
|
|
333
|
+
const submitData = {
|
|
334
|
+
// ... 其他字段 ...
|
|
335
|
+
...descField,
|
|
336
|
+
};
|
|
337
|
+
|
|
338
|
+
// HTTPUtil.post('/api/publish/add', submitData)
|
|
339
|
+
}
|
|
340
|
+
```
|
|
341
|
+
|
|
342
|
+
### 合图失败的处理策略
|
|
343
|
+
|
|
344
|
+
```
|
|
345
|
+
合图失败 / 返回空数组 / 抛异常
|
|
346
|
+
|
|
|
347
|
+
v
|
|
348
|
+
wireless-desc-converter: 将该 text_N 模块整体从 props 移除
|
|
349
|
+
|
|
|
350
|
+
v
|
|
351
|
+
宿主项目: getFailCount() > 0 时弹窗提示用户
|
|
352
|
+
|
|
|
353
|
+
├── 用户确认继续 → 正常提交(失败的文字段不会出现在详情中)
|
|
354
|
+
└── 用户取消 → 中止提交,loading 关闭
|
|
355
|
+
```
|
|
356
|
+
|
|
357
|
+
> **为什么整体移除而不是传空数组?** 淘宝官方确认 `text_N.images` 为强必填字段,空数组和字段缺失均会报错,唯一合法的降级方式是移除整个 `text_N` 模块。
|
|
358
|
+
|
|
359
|
+
---
|
|
360
|
+
|
|
361
|
+
## 淘宝图片空间上传接口
|
|
362
|
+
|
|
363
|
+
文字合图最终需要上传到淘宝图片空间,接口参数如下:
|
|
364
|
+
|
|
365
|
+
### 请求参数
|
|
366
|
+
|
|
367
|
+
| 参数 | 类型 | 说明 |
|
|
368
|
+
|------|------|------|
|
|
369
|
+
| `cid` | `string\|number` | 相册 ID(从 `album.id` 获取) |
|
|
370
|
+
| `title` | `string` | 图片标题,建议格式:`{gid}_{zdid}_描述文字_{index}` |
|
|
371
|
+
| `img` | `string` | base64 图片数据(`data:image/jpeg;base64,...`) |
|
|
372
|
+
| `gid` | `string` | 商品 ID |
|
|
373
|
+
| `item_no` | `string` | 商品编号 |
|
|
374
|
+
| `createCategoryForGoods` | `boolean` | 是否自动创建商品图片文件夹(幂等,重复传不会报错) |
|
|
375
|
+
|
|
376
|
+
### 响应结构
|
|
377
|
+
|
|
378
|
+
```json
|
|
379
|
+
{
|
|
380
|
+
"is_success": true,
|
|
381
|
+
"result": {
|
|
382
|
+
"picture": {
|
|
383
|
+
"picture_path": "//img.alicdn.com/imgextra/xxx.jpg"
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
```
|
|
388
|
+
|
|
389
|
+
### 接口版本选择
|
|
390
|
+
|
|
391
|
+
淘宝图片空间有新版(v3)和旧版两套接口,通过 `getImageUploadApiPath()` 统一管理:
|
|
392
|
+
|
|
393
|
+
```javascript
|
|
394
|
+
function getImageUploadApiPath() {
|
|
395
|
+
const useNewApi = checkUseNewImageUploadApi(); // 根据 api_version >= 3 判断
|
|
396
|
+
return {
|
|
397
|
+
pathUploadBase64: useNewApi ? UPLOAD_IMAGE_V3 : UPLOAD_IMAGE,
|
|
398
|
+
pathUploadFile: useNewApi ? UPLOAD_FILE_V3 : UPLOAD_FILE,
|
|
399
|
+
pathGetBase64: useNewApi ? GET_IMAGE_BASE64_V3 : GET_IMAGE_BASE64,
|
|
400
|
+
};
|
|
401
|
+
}
|
|
402
|
+
```
|
|
403
|
+
|
|
404
|
+
---
|
|
405
|
+
|
|
406
|
+
## 关键约束与避坑
|
|
407
|
+
|
|
408
|
+
### 1. 模块编号规则
|
|
409
|
+
|
|
410
|
+
| 模块类型 | 编号起始 | 示例 |
|
|
411
|
+
|----------|----------|------|
|
|
412
|
+
| `text_N` | **1** | `text_1`, `text_2`, ... |
|
|
413
|
+
| `image_hot_area_N` | **0** | `image_hot_area_0`, `image_hot_area_1`, ... |
|
|
414
|
+
|
|
415
|
+
> 错误的编号会导致淘宝接口报 `id 为只读字段` 错误。
|
|
416
|
+
|
|
417
|
+
### 2. `id` 字段(groupId)
|
|
418
|
+
|
|
419
|
+
经最终实测确认 **不需要传** `inputField('id', groupId)`。淘宝接口会自动分配,手动传会导致 `image_hot_area_N.id 为只读字段,不可编辑` 报错。
|
|
420
|
+
|
|
421
|
+
### 3. `sample` 字段
|
|
422
|
+
|
|
423
|
+
`text_N` 和 `image_hot_area_N` 的 `sample`(示意图)字段均为**可选**,不传不会报错。`fe-upload-tb` 的实现中 `text_N` 不生成 `sample`。
|
|
424
|
+
|
|
425
|
+
### 4. `text_N.images` 强必填
|
|
426
|
+
|
|
427
|
+
- 有 `textImage` 合图函数 → 生成 `text_N` 模块,`images` 为合图结果
|
|
428
|
+
- 无 `textImage` 合图函数 → **跳过文字模块**,不生成 `text_N`
|
|
429
|
+
- 合图失败 → 整体移除该 `text_N` 模块
|
|
430
|
+
|
|
431
|
+
### 5. 循环依赖规避
|
|
432
|
+
|
|
433
|
+
如果宿主项目的工具方法存在循环依赖(如 `image.js` → `HTTPUtil` → `index.js` → `image.js`),文字合图上传应**直接用原生 `fetch`**,不要通过封装的 HTTP 工具类。
|
|
434
|
+
|
|
435
|
+
### 6. Canvas 限制
|
|
436
|
+
|
|
437
|
+
- Canvas 不支持加载 web font,只能用系统字体渲染
|
|
438
|
+
- IE8 不支持 Canvas,需确保 `isWirelessDescUser()` 在 IE8 环境返回 `false`
|
|
439
|
+
- `canvas.toDataURL` 在某些跨域场景会抛异常,需 try-catch
|
|
440
|
+
|
|
441
|
+
### 7. 图片尺寸约束
|
|
442
|
+
|
|
443
|
+
| 约束 | 值 |
|
|
444
|
+
|------|-----|
|
|
445
|
+
| 图片最小宽度 | 480px |
|
|
446
|
+
| 图片最大宽度 | 1500px |
|
|
447
|
+
| 图片最大高度 | 2000px |
|
|
448
|
+
| 所有模块总高度上限 | 100000px |
|
|
449
|
+
| 模块默认宽度 | 620px |
|
|
450
|
+
| 切图高度 | 1240px |
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "wireless-desc-converter",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.12",
|
|
4
4
|
"description": "淘宝新版图文编辑器 wirelessDesc 适配工具 - HTML 富文本转换为结构化 JSON",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/wireless-desc-converter.umd.js",
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
"files": [
|
|
9
9
|
"dist/",
|
|
10
10
|
"src/",
|
|
11
|
+
"docs/",
|
|
11
12
|
"README.md"
|
|
12
13
|
],
|
|
13
14
|
"scripts": {
|