@pixui-dev/pixui-richtext-helper 0.2.6-test.1 → 0.2.7

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.
@@ -24,10 +24,14 @@ export declare class RichTextCore {
24
24
  * 富文本转换为 PixUI innerHTML 兼容格式
25
25
  * @param str 原始富文本 HTML
26
26
  * @param config 可选配置
27
+ * @param config.lineHeightScale 行高缩放比例
28
+ * @param config.convertPxToEm 是否转换px为em
29
+ * @param config.addCosUrlImageMogr2_w 添加cos url图片的mogr2_w参数,如果是cos的图片,可以使用这个配置设置图片宽度最大值。组件会增加参数为 xxx.jpg?imageMogr2/thumbnail/<width>x的参数。参数值取min(img节点样式宽度,addCosUrlImageMogr2_w)
27
30
  */
28
31
  static convertRichTextToPixuiStyle(str: string, config?: {
29
32
  lineHeightScale?: number;
30
33
  convertPxToEm?: boolean;
34
+ addCosUrlImageMogr2_w?: number;
31
35
  }): string;
32
36
  /**
33
37
  * 绑定链接点击事件
@@ -53,6 +57,8 @@ export declare class RichTextCore {
53
57
  private static convertAllPxToEm;
54
58
  private static preprocess;
55
59
  private static fixImgSizeUnit;
60
+ /** 给 COS 图片自动追加 imageMogr2/thumbnail 参数 */
61
+ private static handleCosImageMogr2;
56
62
  /** 列表处理(将 ol/ul/li 替换为 div 结构) */
57
63
  private static handleLists;
58
64
  /** 将连续 <br> 替换为透明占位文字,确保 PixUI 换行 */
@@ -143,12 +143,18 @@ var RichTextCore = /** @class */ (function () {
143
143
  * 富文本转换为 PixUI innerHTML 兼容格式
144
144
  * @param str 原始富文本 HTML
145
145
  * @param config 可选配置
146
+ * @param config.lineHeightScale 行高缩放比例
147
+ * @param config.convertPxToEm 是否转换px为em
148
+ * @param config.addCosUrlImageMogr2_w 添加cos url图片的mogr2_w参数,如果是cos的图片,可以使用这个配置设置图片宽度最大值。组件会增加参数为 xxx.jpg?imageMogr2/thumbnail/<width>x的参数。参数值取min(img节点样式宽度,addCosUrlImageMogr2_w)
146
149
  */
147
150
  RichTextCore.convertRichTextToPixuiStyle = function (str, config) {
148
151
  // 重置全局状态
149
152
  RichTextCore.linkNodes = [];
150
153
  RichTextCore.hrefIdCnt = 0;
151
- var $1 = cheerio.load(str, null, false);
154
+ var preEncodedStr = str.replace(/href="([^"]*?)"/g, function (_match, hrefVal) {
155
+ return "href=\"".concat(encodeURIComponent(hrefVal), "\"");
156
+ });
157
+ var $1 = cheerio.load(preEncodedStr, null, false);
152
158
  //检查html,如果有text节点,说明是转换过的,直接返回
153
159
  if ($1("text").length > 0) {
154
160
  return str;
@@ -171,9 +177,38 @@ var RichTextCore = /** @class */ (function () {
171
177
  // 直接查询统一的 class
172
178
  setTimeout(function () {
173
179
  var elements = Array.from(document.querySelectorAll(".".concat(RichTextCore.LINK_NODE_CLASS)));
180
+ // HTML 实体解码工具函数(纯算法实现,避免依赖 textarea 节点)
181
+ var decodeHtmlEntities = function (str) {
182
+ if (!str)
183
+ return str;
184
+ var named = {
185
+ quot: '"',
186
+ amp: "&",
187
+ lt: "<",
188
+ gt: ">",
189
+ apos: "'",
190
+ };
191
+ return str.replace(/&(#(?:x[0-9a-fA-F]+|\d+)|[a-zA-Z]+);/g, function (_, ent) {
192
+ if (ent[0] === "#") {
193
+ // 数字实体
194
+ var isHex = ent[1].toLowerCase() === "x";
195
+ var numStr = isHex ? ent.slice(2) : ent.slice(1);
196
+ var codePoint = parseInt(numStr, isHex ? 16 : 10);
197
+ if (!isNaN(codePoint)) {
198
+ return String.fromCodePoint(codePoint);
199
+ }
200
+ return _; // 解析失败,原样返回
201
+ }
202
+ // 命名实体
203
+ if (named.hasOwnProperty(ent))
204
+ return named[ent];
205
+ return _; // 未知实体,原样返回
206
+ });
207
+ };
174
208
  elements.forEach(function (el) {
175
209
  var hrefEncoded = el.getAttribute("data-link-href") || "";
176
- var href = decodeURIComponent(hrefEncoded);
210
+ // URI 解码,再做 HTML 实体解码
211
+ var href = decodeHtmlEntities(decodeURIComponent(hrefEncoded));
177
212
  var typeStr = el.getAttribute("data-link-type");
178
213
  var type = typeStr === LinkNodeType.IMG ? LinkNodeType.IMG : LinkNodeType.DIV;
179
214
  // 避免重复绑定
@@ -257,8 +292,12 @@ var RichTextCore = /** @class */ (function () {
257
292
  if (tagName === "div" || tagName === "text") {
258
293
  return []; // 丢弃空 div / text
259
294
  }
295
+ else if (tagName === "img") {
296
+ // img标签直接返回,不包装在div中
297
+ return [$.html(node)];
298
+ }
260
299
  else {
261
- // 其它空标签保留(img...)
300
+ // 其它空标签保留
262
301
  return [wrapNode($elem, "")];
263
302
  }
264
303
  }
@@ -285,6 +324,7 @@ var RichTextCore = /** @class */ (function () {
285
324
  // 按旧实现顺序进行处理,但重构为独立函数划分
286
325
  this.preprocess($);
287
326
  this.fixImgSizeUnit($);
327
+ this.handleCosImageMogr2($, config);
288
328
  this.handleLists($, (config === null || config === void 0 ? void 0 : config.lineHeightScale) || 1);
289
329
  this.replaceBrWithPlaceholder($);
290
330
  this.handleAnchorTag($);
@@ -410,6 +450,64 @@ var RichTextCore = /** @class */ (function () {
410
450
  }
411
451
  });
412
452
  };
453
+ /** 给 COS 图片自动追加 imageMogr2/thumbnail 参数 */
454
+ RichTextCore.handleCosImageMogr2 = function ($, config) {
455
+ var maxW = config === null || config === void 0 ? void 0 : config.addCosUrlImageMogr2_w;
456
+ if (!maxW)
457
+ return;
458
+ var extractNumber = function (str) {
459
+ if (!str)
460
+ return undefined;
461
+ var m = str.match(/(\d+(?:\.\d+)?)/);
462
+ if (m) {
463
+ var num = parseInt(m[1]);
464
+ if (!isNaN(num))
465
+ return num;
466
+ }
467
+ return undefined;
468
+ };
469
+ $("img").each(function () {
470
+ var src = $(this).attr("src") || "";
471
+ if (!src)
472
+ return;
473
+ // 已有 thumbnail 宽度
474
+ var thumbMatch = src.match(/imageMogr2\/thumbnail\/(\d+)x/i);
475
+ var existingThumbW = thumbMatch ? parseInt(thumbMatch[1]) : undefined;
476
+ // width 属性
477
+ var widthAttrW = extractNumber($(this).attr("width"));
478
+ // style width
479
+ var styleObj = parseStyleString($(this).attr("style") || "");
480
+ var styleWidthW = extractNumber(styleObj["width"]);
481
+ // 计算最小宽度
482
+ var candidates = [existingThumbW, widthAttrW, styleWidthW, maxW].filter(function (v) { return v !== undefined && v > 0; });
483
+ if (candidates.length === 0)
484
+ return; // 无可用宽度
485
+ var finalW = Math.min.apply(Math, candidates);
486
+ // 如果已存在 thumbnail 参数且宽度一致,无需处理
487
+ if (existingThumbW !== undefined) {
488
+ if (existingThumbW === finalW)
489
+ return;
490
+ // 替换为更小的宽度
491
+ src = src.replace(/imageMogr2\/thumbnail\/(\d+)x/i, "imageMogr2/thumbnail/".concat(finalW, "x"));
492
+ }
493
+ else {
494
+ // 未存在 thumbnail 参数,需要追加
495
+ var appendedParam = "imageMogr2/thumbnail/".concat(finalW, "x");
496
+ if (src.includes("?")) {
497
+ // 已有查询部分,使用 | 追加
498
+ if (!src.endsWith("|") && !src.endsWith("&") && !src.endsWith("?")) {
499
+ src += "|";
500
+ }
501
+ src += appendedParam;
502
+ }
503
+ else {
504
+ // 无查询部分,直接添加
505
+ src += "?".concat(appendedParam);
506
+ }
507
+ }
508
+ $(this).attr("src", src);
509
+ });
510
+ };
413
511
  /** 列表处理(将 ol/ul/li 替换为 div 结构) */
414
512
  RichTextCore.handleLists = function ($, lineHeightScale) {
415
513
  if (lineHeightScale === void 0) { lineHeightScale = 1; }
@@ -523,7 +621,7 @@ var RichTextCore = /** @class */ (function () {
523
621
  mergedStyles.push(innerStyle);
524
622
  });
525
623
  var finalStyle = mergeStyles.apply(void 0, __spreadArray([originalStyle], mergedStyles, false));
526
- var encodedHref = href ? encodeURIComponent(href) : "";
624
+ var encodedHref = href || ""; // 已在预处理阶段编码,避免二次编码
527
625
  if (href) {
528
626
  self.linkNodes.push({ type: LinkNodeType.DIV, href: href, id: id });
529
627
  }
@@ -610,7 +708,7 @@ var RichTextCore = /** @class */ (function () {
610
708
  self.linkNodes.push({ type: LinkNodeType.IMG, href: href, id: id });
611
709
  }
612
710
  // 将链接信息以转义形式保存在 data- 属性中,避免默认跳转
613
- var encodedHref = href ? encodeURIComponent(href) : "";
711
+ var encodedHref = href || ""; // 已在预处理阶段编码,避免二次编码
614
712
  $(this).attr("data-link-type", "img");
615
713
  $(this).attr("data-link-href", encodedHref);
616
714
  $(this).attr("href", "");
@@ -698,7 +796,8 @@ var RichTextCore = /** @class */ (function () {
698
796
  var attribs = ($(node).get(0) || {}).attribs || {};
699
797
  for (var _i = 0, _a = Object.entries(attribs); _i < _a.length; _i++) {
700
798
  var _b = _a[_i], k = _b[0], v = _b[1];
701
- if (k !== "style" && k !== "id") {
799
+ // 跳过styleid和href属性,避免重复处理或错误处理
800
+ if (k !== "style" && k !== "id" && k !== "href") {
702
801
  customAttrs += " ".concat(k, "=\"").concat(v, "\"");
703
802
  }
704
803
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pixui-dev/pixui-richtext-helper",
3
- "version": "0.2.6-test.1",
3
+ "version": "0.2.7",
4
4
  "description": "pixui richtext helper",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
package/readme.md CHANGED
@@ -95,4 +95,12 @@ componentDidMount() { // 在节点渲染后绑定点击事件
95
95
  2. 修复在嵌套节点中加br不会被拆成不同段落的问题
96
96
 
97
97
  0.2.5
98
- 1. 修复img节点丢失的问题
98
+ 1. 修复img节点丢失的问题
99
+
100
+ 0.2.6
101
+ 1. 修复在超链接中添加转义符导致跳转信息不完整的问题
102
+ 2. 修复带超链接的img节点中出现一些多余的属性
103
+ 3. 增加将富文本中的px单位的属性统一替换为em单位的选项
104
+
105
+ 0.2.7
106
+ 1. 增加addCosUrlImageMogr2_w参数