@pixui-dev/pixui-richtext-helper 0.1.1 → 0.2.1

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.
@@ -0,0 +1,22 @@
1
+ import { RichTextCore, LinkNodeType, LinkClickParams } from "./richtext/RichTextCore";
2
+ /**
3
+ * 对外暴露的 RichText 组件。
4
+ * 保持旧版使用方式不变,内部委托给 RichText_Refactor 实现。
5
+ */
6
+ export declare const RichText: {
7
+ /** 链接节点类型枚举 */
8
+ LinkNodeType: typeof LinkNodeType;
9
+ /**
10
+ * 将富文本转换为 PixUI innerHTML 兼容格式
11
+ * @see RichTextCore.convertRichTextToPixuiStyle
12
+ */
13
+ convertRichTextToPixuiStyle: typeof RichTextCore.convertRichTextToPixuiStyle;
14
+ /**
15
+ * 绑定富文本中的链接点击事件
16
+ * @see RichTextCore.bindLinkClickEvents
17
+ */
18
+ bindLinkClickEvents: typeof RichTextCore.bindLinkClickEvents;
19
+ };
20
+ export type { LinkClickParams };
21
+ export { LinkNodeType };
22
+ export default RichText;
@@ -0,0 +1,24 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.LinkNodeType = exports.RichText = void 0;
4
+ var RichTextCore_1 = require("./richtext/RichTextCore");
5
+ Object.defineProperty(exports, "LinkNodeType", { enumerable: true, get: function () { return RichTextCore_1.LinkNodeType; } });
6
+ /**
7
+ * 对外暴露的 RichText 组件。
8
+ * 保持旧版使用方式不变,内部委托给 RichText_Refactor 实现。
9
+ */
10
+ exports.RichText = {
11
+ /** 链接节点类型枚举 */
12
+ LinkNodeType: RichTextCore_1.LinkNodeType,
13
+ /**
14
+ * 将富文本转换为 PixUI innerHTML 兼容格式
15
+ * @see RichTextCore.convertRichTextToPixuiStyle
16
+ */
17
+ convertRichTextToPixuiStyle: RichTextCore_1.RichTextCore.convertRichTextToPixuiStyle,
18
+ /**
19
+ * 绑定富文本中的链接点击事件
20
+ * @see RichTextCore.bindLinkClickEvents
21
+ */
22
+ bindLinkClickEvents: RichTextCore_1.RichTextCore.bindLinkClickEvents,
23
+ };
24
+ exports.default = exports.RichText;
@@ -1,3 +1,9 @@
1
+ /**
2
+ * pixui富文本转换工具
3
+ * 将管理端下发的富文本转换成pixui的innerHtml可以使用的格式,被转换的富文本只应该包括文本和图片及相关的样式。
4
+ * 将富文本内容转换为:
5
+ * 段落div/h节点(不带样式,用来形成一个段落) -> text(只带text节点样式,pixui的段落需要在text节点下才能表现正常)-> <div>(带有文本的样式) 文本1 </div> <div>(带有文本的样式) 文本2 </div> <img>(带有图片的样式) 图片1 </img> <div>(带有文本的样式) 文本3 </div> (每个子div带有这一小段文本/图片的样式,这一段所有text节点下的div/img会被text节点正确的换行排列)
6
+ */
1
7
  export declare namespace RichText {
2
8
  /**
3
9
  * 富文本中的超链接节点类型
@@ -22,12 +28,10 @@ export declare namespace RichText {
22
28
  * @param str 管理端下发的富文本
23
29
  * @param config 配置
24
30
  * @param config.lineHeightScale 行高缩放
25
- * @param config.isToRem 是否将节点的 px 单位转换为 rem 的单位
26
31
  * @returns pixui的innerHtml可以使用的格式
27
32
  */
28
33
  const convertRichTextToPixuiStyle: (str: string, config?: {
29
34
  lineHeightScale?: number;
30
- isToRem?: boolean;
31
35
  }) => string;
32
36
  /**
33
37
  * 为富文本中的链接节点绑定点击事件
@@ -32,9 +32,24 @@ var __importStar = (this && this.__importStar) || (function () {
32
32
  return result;
33
33
  };
34
34
  })();
35
+ var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
36
+ if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
37
+ if (ar || !(i in from)) {
38
+ if (!ar) ar = Array.prototype.slice.call(from, 0, i);
39
+ ar[i] = from[i];
40
+ }
41
+ }
42
+ return to.concat(ar || Array.prototype.slice.call(from));
43
+ };
35
44
  Object.defineProperty(exports, "__esModule", { value: true });
36
45
  exports.RichText = void 0;
37
46
  var cheerio = __importStar(require("cheerio"));
47
+ /**
48
+ * pixui富文本转换工具
49
+ * 将管理端下发的富文本转换成pixui的innerHtml可以使用的格式,被转换的富文本只应该包括文本和图片及相关的样式。
50
+ * 将富文本内容转换为:
51
+ * 段落div/h节点(不带样式,用来形成一个段落) -> text(只带text节点样式,pixui的段落需要在text节点下才能表现正常)-> <div>(带有文本的样式) 文本1 </div> <div>(带有文本的样式) 文本2 </div> <img>(带有图片的样式) 图片1 </img> <div>(带有文本的样式) 文本3 </div> (每个子div带有这一小段文本/图片的样式,这一段所有text节点下的div/img会被text节点正确的换行排列)
52
+ */
38
53
  var RichText;
39
54
  (function (RichText) {
40
55
  /**
@@ -47,15 +62,30 @@ var RichText;
47
62
  })(LinkNodeType = RichText.LinkNodeType || (RichText.LinkNodeType = {}));
48
63
  // 存储需要绑定点击事件的节点信息
49
64
  var linkNodes = [];
65
+ // 添加工具函数来正确处理CSS样式拼接
66
+ var mergeStyles = function () {
67
+ var styles = [];
68
+ for (var _i = 0; _i < arguments.length; _i++) {
69
+ styles[_i] = arguments[_i];
70
+ }
71
+ return styles
72
+ .filter(function (style) { return style && style.trim(); })
73
+ .map(function (style) {
74
+ var trimmed = style.trim();
75
+ return trimmed.endsWith(";") ? trimmed : trimmed + ";";
76
+ })
77
+ .join(" ")
78
+ .replace(/\s*;\s*$/, ";"); // 确保最后只有一个分号
79
+ };
50
80
  /**
51
81
  * 富文本组件,将富文本转换成pixui的innerHtml可以使用的格式
52
82
  * @param str 管理端下发的富文本
53
83
  * @param config 配置
54
84
  * @param config.lineHeightScale 行高缩放
55
- * @param config.isToRem 是否将节点的 px 单位转换为 rem 的单位
56
85
  * @returns pixui的innerHtml可以使用的格式
57
86
  */
58
87
  RichText.convertRichTextToPixuiStyle = function (str, config) {
88
+ //将富文本内容转换为一个 段落div/h -> text -> 文字样式div/img -> 文本 的结构,保证pixui可以正常显示
59
89
  var $ = cheerio.load(str, null, false);
60
90
  var hrefIdCnt = 0;
61
91
  linkNodes = []; // 重置链接节点信息
@@ -63,10 +93,10 @@ var RichText;
63
93
  {
64
94
  var _loop_1 = function (i) {
65
95
  var indent = "ql-indent-".concat(i);
66
- var extStyle = "padding-left: ".concat(i * 2, "rem;");
96
+ var extStyle = "padding-left: ".concat(i * 2, "rem");
67
97
  $(".".concat(indent)).each(function () {
68
98
  var oriStyle = $(this).attr("style") || "";
69
- $(this).attr("style", "".concat(oriStyle, " ").concat(extStyle));
99
+ $(this).attr("style", mergeStyles(oriStyle, extStyle));
70
100
  $(this).removeClass(indent);
71
101
  });
72
102
  };
@@ -74,52 +104,185 @@ var RichText;
74
104
  for (var i = 1; i <= 10; i++) {
75
105
  _loop_1(i);
76
106
  }
77
- // 将px转换为rem,font-size不转换
78
- if (config === null || config === void 0 ? void 0 : config.isToRem) {
79
- $("*").each(function () {
80
- var $element = $(this);
81
- var style = $element.attr("style");
82
- if (style) {
83
- var newStyle = style.replace(/([\w-]+)\s*:\s*([^;]+)/g, function (match, property, value) {
84
- if (property.trim() !== "font-size" && property.trim() !== "letter-spacing") {
85
- var newValue = value.replace(/(\d+(?:\.\d+)?)px/g, function (pxMatch, pxValue) {
86
- var remValue = parseFloat(pxValue) / 100;
87
- return "".concat(remValue, "rem");
88
- });
89
- return "".concat(property, ": ").concat(newValue);
107
+ $("img").each(function () {
108
+ //给纯数字的width,height添加px单位
109
+ var width = $(this).attr("width");
110
+ var height = $(this).attr("height");
111
+ if (width && width + "" == parseInt(width) + "") {
112
+ $(this).attr("width", "".concat(width, "px"));
113
+ }
114
+ if (height && height + "" == parseInt(height) + "") {
115
+ $(this).attr("height", "".concat(height, "px"));
116
+ }
117
+ });
118
+ // 处理列表项
119
+ {
120
+ /**
121
+ * pixui不支持ol,ul,li节点,将其递归转换成div并处理嵌套层级
122
+ * 记录每个列表容器的位置,在原位置插入转换后的结构
123
+ */
124
+ // 第一步:找到所有顶层列表容器并标记其位置
125
+ var topLevelLists_1 = [];
126
+ // 递归收集列表项的函数
127
+ var collectListItems_1 = function ($container, currentLevel) {
128
+ if (currentLevel === void 0) { currentLevel = 0; }
129
+ var items = [];
130
+ $container.children().each(function () {
131
+ var $child = $(this);
132
+ if ($child.is("li")) {
133
+ var isOrdered = $child.parent().is("ol");
134
+ var orderIndex = 1;
135
+ if (isOrdered) {
136
+ // 计算在同一个ol中的序号
137
+ orderIndex = $child.prevAll("li").length + 1;
90
138
  }
91
- return match;
139
+ // 生成marker
140
+ var marker = isOrdered ? "".concat(orderIndex, ".") : "•";
141
+ // 计算缩进
142
+ var paddingLeft = currentLevel * 2; // 每层缩进2rem
143
+ // 获取li的直接文本内容,不包括嵌套的子列表
144
+ var $liClone = $child.clone();
145
+ $liClone.find("ol, ul").remove();
146
+ var textContent = $liClone.text().trim() || "";
147
+ var style = $child.attr("style") || "";
148
+ items.push({
149
+ level: currentLevel,
150
+ isOrdered: isOrdered,
151
+ orderIndex: orderIndex,
152
+ marker: marker,
153
+ paddingLeft: paddingLeft,
154
+ textContent: textContent,
155
+ style: style,
156
+ });
157
+ // 递归处理li内的子列表(只处理真正嵌套在当前li内的列表)
158
+ $child.find("ol, ul").each(function () {
159
+ // 确保这个列表是当前li的直接子元素,而不是更深层的
160
+ var $nestedList = $(this);
161
+ if ($nestedList.parent().closest("li")[0] === $child[0]) {
162
+ var childItems = collectListItems_1($nestedList, currentLevel + 1);
163
+ items.push.apply(items, childItems);
164
+ }
165
+ });
166
+ }
167
+ });
168
+ return items;
169
+ };
170
+ // 找到所有顶层列表(直接在body下或其他容器中的ol/ul)
171
+ $("ol, ul").each(function () {
172
+ var $list = $(this);
173
+ // 检查是否为顶层列表(不在其他列表内部)
174
+ if ($list.parents("ol, ul").length === 0) {
175
+ // 创建占位符标记原始位置
176
+ var placeholder = $("<div data-list-placeholder=\"true\"></div>");
177
+ $list.before(placeholder);
178
+ // 收集该列表的所有项目
179
+ var items = collectListItems_1($list, 1);
180
+ topLevelLists_1.push({
181
+ element: $list,
182
+ placeholder: placeholder,
183
+ items: items,
92
184
  });
93
- $element.attr("style", newStyle);
94
185
  }
95
186
  });
187
+ // 第二步:移除所有ol/ul/li结构
188
+ $("ol, ul, li").remove();
189
+ // 第三步:在每个占位符位置插入转换后的列表项
190
+ topLevelLists_1.forEach(function (listInfo) {
191
+ var placeholder = listInfo.placeholder, items = listInfo.items;
192
+ // 为该列表生成HTML
193
+ var listHtml = items
194
+ .map(function (item) {
195
+ return ("<div style=\"".concat(mergeStyles(item.style, "padding-left: ".concat(item.paddingLeft, "rem"), "flex-shrink: 0", "width: 100%", "flex-direction: row", "display: flex"), "\" data-list-item=\"true\">") +
196
+ "<text style=\"".concat(mergeStyles("flex-shrink: 0", "width: 100%", "line-height: 1", "display: flex", "flex-direction: row"), "\">") +
197
+ "<div style=\"".concat(mergeStyles("word-break: break-word", "flex-shrink: 0", "margin-right: 0.5rem"), "\">").concat(item.marker, "</div>") +
198
+ "<div style=\"".concat(mergeStyles("word-break: break-word", "flex-shrink: 0", "flex: 1"), "\">").concat(item.textContent, "</div>") +
199
+ "</text>" +
200
+ "</div>");
201
+ })
202
+ .join("");
203
+ // 在占位符位置插入列表HTML
204
+ placeholder.after(listHtml);
205
+ // 移除占位符
206
+ placeholder.remove();
207
+ });
96
208
  }
97
- //pixui不支持ol,li节点,将其替换成div并且手动补充li节点前面的标号和后面的br节点
98
- $("ol").each(function () {
99
- var i = 1;
209
+ //pixui连续的br不生效,用透明文字占位
210
+ $("br").each(function () {
211
+ $(this).replaceWith("<span style=\"".concat(mergeStyles("color: transparent", "flex-shrink: 0", "font-size: 20px"), "\" class='pixui-richtext-br-placeholder'>1</span>"));
212
+ });
213
+ //将 a 标签替换为简单div,处理 href,预处理内部样式标签并合并样式
214
+ $("a").each(function (i, ele) {
215
+ var id = "PA_RichTextHrefId_".concat(hrefIdCnt++);
216
+ var href = $(this).attr("href");
217
+ var originalStyle = $(this).attr("style") || "";
218
+ var textContent = $(this).text(); // 获取纯文本内容
219
+ // 收集内部样式标签的样式
220
+ var mergedStyles = [];
221
+ if ($(this).find("strong").length > 0) {
222
+ mergedStyles.push("font-weight: bold;");
223
+ }
224
+ if ($(this).find("em").length > 0) {
225
+ mergedStyles.push("font-style: italic;");
226
+ }
227
+ if ($(this).find("u").length > 0) {
228
+ mergedStyles.push("text-decoration: underline;");
229
+ }
230
+ if ($(this).find("s").length > 0) {
231
+ mergedStyles.push("text-decoration: line-through;");
232
+ }
233
+ // 收集内部span等标签的style属性
100
234
  $(this)
101
- .children("li")
235
+ .find("strong, em, u, s, span")
102
236
  .each(function () {
103
- $(this).prepend("<text style=\"flex-shrink: 0;\">".concat(i++, ".</text>"));
237
+ var innerStyle = $(this).attr("style");
238
+ if (innerStyle) {
239
+ mergedStyles.push(innerStyle);
240
+ }
104
241
  });
105
- $(this).replaceWith($(this).html() || "");
106
- });
107
- $("li").each(function () {
108
- $(this).before("<br>");
109
- });
110
- //pixui连续的br不生效,用透明文字占位
111
- $("br").each(function () {
112
- $(this).replaceWith("<span style='color: transparent; flex-shrink: 0;font-size: 20px;' class='pixui-richtext-br-placeholder'>1</span>");
242
+ // 合并所有样式
243
+ var finalStyle = originalStyle;
244
+ if (mergedStyles.length > 0) {
245
+ finalStyle = mergeStyles.apply(void 0, __spreadArray([originalStyle], mergedStyles, false));
246
+ }
247
+ if (href) {
248
+ linkNodes.push({
249
+ type: LinkNodeType.DIV,
250
+ href: href,
251
+ id: id,
252
+ });
253
+ }
254
+ // 转换为简单div,样式已合并
255
+ $(this).replaceWith("<div style=\"".concat(finalStyle, "\" id=\"").concat(id, "\" class=\"PA_RichTextHref_ATag\" >").concat(textContent, "</div>"));
113
256
  });
114
257
  //找到所有的文字节点,外层包裹text标签
115
258
  $("*").each(function () {
259
+ // 跳过链接节点内部的文本处理
260
+ var nodeId = $(this).attr("id") || "";
261
+ if (nodeId.startsWith("PA_RichTextHrefId_")) {
262
+ return; // 跳过链接节点,不处理其内部文本
263
+ }
264
+ // 跳过列表项,因为已经处理完成
265
+ var isListItem = $(this).attr("data-list-item") === "true";
266
+ if (isListItem) {
267
+ return; // 跳过列表项
268
+ }
116
269
  $(this)
117
270
  .contents()
118
271
  .each(function () {
272
+ // 检查当前文本节点是否在链接节点内部
273
+ var linkParent = $(this).closest("[id^='PA_RichTextHrefId_']");
274
+ if (linkParent.length > 0) {
275
+ return; // 跳过链接节点内部的文本
276
+ }
277
+ // 检查当前文本节点是否在列表项内部
278
+ var listParent = $(this).closest("[data-list-item='true']");
279
+ if (listParent.length > 0) {
280
+ return; // 跳过列表项内部的文本
281
+ }
119
282
  if (this.type === "text") {
120
283
  var text = this.data.trim();
121
284
  if (text.length > 0) {
122
- $(this).replaceWith("<text style=\"word-break: break-word;flex-shrink: 0;flex-direction: row;\">".concat(text, "</text>"));
285
+ $(this).replaceWith("<text style=\"".concat(mergeStyles("word-break: break-word", "flex-shrink: 0", "flex-direction: row"), "\">").concat(text, "</text>"));
123
286
  }
124
287
  }
125
288
  });
@@ -128,33 +291,33 @@ var RichText;
128
291
  $("strong").each(function () {
129
292
  var html = $(this).html();
130
293
  var style = $(this).attr("style") || "";
131
- $(this).replaceWith("<text style=\"".concat(style, " font-weight: bold;flex-shrink: 0;\">").concat(html, "</text>"));
294
+ $(this).replaceWith("<text style=\"".concat(mergeStyles(style, "font-weight: bold", "flex-shrink: 0"), "\">").concat(html, "</text>"));
132
295
  });
133
296
  $("em").each(function () {
134
297
  var html = $(this).html();
135
298
  var style = $(this).attr("style") || "";
136
- $(this).replaceWith("<text style=\"".concat(style, " font-style: italic;flex-shrink: 0;\">").concat(html, "</text>"));
299
+ $(this).replaceWith("<text style=\"".concat(mergeStyles(style, "font-style: italic", "flex-shrink: 0"), "\">").concat(html, "</text>"));
137
300
  });
138
301
  $("u").each(function () {
139
302
  var html = $(this).html();
140
303
  var style = $(this).attr("style") || "";
141
- $(this).replaceWith("<text style=\"".concat(style, " text-decoration: underline;flex-shrink: 0;\">").concat(html, "</text>"));
304
+ $(this).replaceWith("<text style=\"".concat(mergeStyles(style, "text-decoration: underline", "flex-shrink: 0"), "\">").concat(html, "</text>"));
142
305
  });
143
306
  $("s").each(function () {
144
307
  var html = $(this).html();
145
308
  var style = $(this).attr("style") || "";
146
- $(this).replaceWith("<text style=\"".concat(style, " text-decoration: line-through;flex-shrink: 0;\">").concat(html, "</text>"));
309
+ $(this).replaceWith("<text style=\"".concat(mergeStyles(style, "text-decoration: line-through", "flex-shrink: 0"), "\">").concat(html, "</text>"));
147
310
  });
148
311
  $("span").each(function () {
149
312
  var html = $(this).html();
150
313
  var style = $(this).attr("style") || "";
151
- $(this).replaceWith("<text style=\"".concat(style, " flex-shrink: 0;\">").concat(html, "</text>"));
314
+ $(this).replaceWith("<text style=\"".concat(mergeStyles(style, "flex-shrink: 0"), "\">").concat(html, "</text>"));
152
315
  });
153
316
  //p换成div
154
317
  $("p").each(function () {
155
318
  var html = $(this).html();
156
319
  var style = $(this).attr("style") || "";
157
- $(this).replaceWith("<div style=\"".concat(style, " ;flex-shrink: 0;width:100%;\">").concat(html, "</div>"));
320
+ $(this).replaceWith("<div style=\"".concat(mergeStyles(style, "flex-shrink: 0", "width:100%"), "\">").concat(html, "</div>"));
158
321
  });
159
322
  //处理 img 上的 href
160
323
  $("img").each(function (i, ele) {
@@ -169,27 +332,19 @@ var RichText;
169
332
  });
170
333
  }
171
334
  $(this).attr("href", "");
172
- });
173
- //将 a 标签替换为div,处理 href
174
- $("a").each(function (i, ele) {
175
- var id = "PA_RichTextHrefId_".concat(hrefIdCnt++);
176
- var href = $(this).attr("href");
177
- var style = $(this).attr("style") || "";
178
- var html = $(this).html();
179
- if (href) {
180
- linkNodes.push({
181
- type: LinkNodeType.DIV,
182
- href: href,
183
- id: id,
184
- });
185
- }
186
- $(this).replaceWith("<div style=\"".concat(style, "\" id=\"").concat(id, "\">").concat(html, "</div>"));
335
+ $(this).attr("class", "PA_RichTextHref_ImgTag");
336
+ $(this).attr("data-href", href);
187
337
  });
188
338
  //补充 flex-shrink: 0
189
339
  ["h1", "h2", "h3", "h4", "h5", "h6", "a", "img", "div"].forEach(function (tag) {
190
340
  $("".concat(tag)).each(function () {
191
- var style = $(this).attr("style") || "";
192
- $(this).attr("style", "".concat(style, " flex-shrink: 0;"));
341
+ // 跳过列表项内部的节点
342
+ var isListItem = $(this).attr("data-list-item") === "true";
343
+ var inListItem = $(this).closest("[data-list-item='true']").length > 0;
344
+ if (!isListItem && !inListItem) {
345
+ var style = $(this).attr("style") || "";
346
+ $(this).attr("style", mergeStyles(style, "flex-shrink: 0"));
347
+ }
193
348
  });
194
349
  });
195
350
  }
@@ -204,7 +359,7 @@ var RichText;
204
359
  var $child = $(this);
205
360
  var childStyle = $child.attr("style") || "";
206
361
  // 合并style属性,将父节点的style附加到子节点上
207
- var mergedStyle = "".concat(parentStyle, "; ").concat(childStyle);
362
+ var mergedStyle = mergeStyles(parentStyle, childStyle);
208
363
  // 更新子元素的style属性
209
364
  $child.attr("style", mergedStyle);
210
365
  });
@@ -220,22 +375,48 @@ var RichText;
220
375
  ["h1", "h2", "h3", "h4", "h5", "h6", "div"].map(function (tag) {
221
376
  // text的父节点
222
377
  $("".concat(tag)).each(function () {
223
- nodearr_1.push(this);
378
+ var nodeId = $(this).attr("id") || "";
379
+ var isListItem = $(this).attr("data-list-item") === "true";
380
+ var inListItem = $(this).closest("[data-list-item='true']").length > 0;
381
+ // 跳过链接节点、列表项本身以及列表项内部的节点
382
+ if (!nodeId.startsWith("PA_RichTextHrefId_") && !isListItem && !inListItem) {
383
+ nodearr_1.push(this);
384
+ }
224
385
  });
225
386
  });
226
387
  nodearr_1.forEach(function (node) {
227
388
  var tag = $(node).prop("tagName");
389
+ var nodeId = $(node).attr("id") || ""; // 保存节点ID
390
+ // 处理text子节点,将text内容转换为div
228
391
  $(node)
229
392
  .children("text")
230
393
  .each(function () {
394
+ // 检查是否在列表项内部
395
+ var inListItem = $(this).closest("[data-list-item='true']").length > 0;
396
+ if (inListItem) {
397
+ return; // 跳过列表项内部的text节点
398
+ }
231
399
  var text = $(this).html();
232
400
  var style = $(this).attr("style") || "";
233
- $(this).replaceWith("<div style=\"".concat(style, "\">").concat(text, "</div>"));
401
+ $(this).replaceWith("<div style=\"".concat(mergeStyles(style, "word-break: break-word", "flex-shrink: 0", "flex-direction: row"), "\">").concat(text, "</div>"));
234
402
  });
235
- var taghtml = $(node).html();
403
+ var taghtml = $(node).html() || "";
236
404
  var tagstyle = $(node).attr("style") || "";
237
- //自己的子节点外套一个text,自己不变
238
- $(node).replaceWith("<".concat(tag, " style=\"").concat(tagstyle, ";flex-direction: row;\"><text style=\"flex-shrink: 0;width: 100%;\" tag=\"\u6BB5\u843Dtext\">").concat(taghtml, "</text></").concat(tag, ">"));
405
+ // 构建标准结构 - 为所有节点构建div-text-div-文字结构
406
+ if (taghtml && taghtml.trim()) {
407
+ var idAttr = nodeId ? " id=\"".concat(nodeId, "\"") : "";
408
+ // 保留所有自定义属性
409
+ var customAttrs = "";
410
+ var attributes = $(node).get(0).attribs || {};
411
+ for (var _i = 0, _a = Object.entries(attributes); _i < _a.length; _i++) {
412
+ var _b = _a[_i], key = _b[0], value = _b[1];
413
+ if (key !== "style" && key !== "id") {
414
+ customAttrs += " ".concat(key, "=\"").concat(value, "\"");
415
+ }
416
+ }
417
+ //自己的子节点外套一个text,自己不变
418
+ $(node).replaceWith("<".concat(tag, " style=\"").concat(mergeStyles(tagstyle, "flex-direction: row"), "\"").concat(idAttr).concat(customAttrs, "><text style=\"").concat(mergeStyles("flex-shrink: 0", "width: 100%"), "\">").concat(taghtml, "</text></").concat(tag, ">"));
419
+ }
239
420
  });
240
421
  //text-indent 处理首行缩进
241
422
  $("*").each(function () {
@@ -246,7 +427,7 @@ var RichText;
246
427
  * 状态为<div style=text-indent:xxx><text><透明字符节点/><div style=样式>文字</div></text></div>
247
428
  * 属性是几 em 就加几个字符
248
429
  * */
249
- var $spacerText = $("<div style=\"color: transparent; flex-shrink: 0;\">".concat("一".repeat(parseInt(textIndent)), "</div>"));
430
+ var $spacerText = $("<div style=\"".concat(mergeStyles("color: transparent", "flex-shrink: 0", "font-size: 16px"), "\">").concat("一".repeat(parseInt(textIndent)), "</div>"));
250
431
  // 寻找子节点中的text节点
251
432
  var textChild = $(this).children("text").first();
252
433
  if (textChild.length > 0) {
@@ -274,7 +455,9 @@ var RichText;
274
455
  });
275
456
  var lineHeightScale = (config === null || config === void 0 ? void 0 : config.lineHeightScale) || 1;
276
457
  // pixui 中的实际显示的距离可能与网页中实际显示的距离差距较大
277
- $text.css("line-height", maxLineHeight * lineHeightScale + "");
458
+ if (maxLineHeight > 0) {
459
+ $text.css("line-height", maxLineHeight * lineHeightScale + "");
460
+ }
278
461
  });
279
462
  //将div中的letter-spacing转移到text上
280
463
  $("text").each(function () {
@@ -307,14 +490,6 @@ var RichText;
307
490
  $(this).remove();
308
491
  }
309
492
  });
310
- // $(`div`).each(function () {
311
- // let style = $(this).attr("style") || "";
312
- // console.log("???", style);
313
- // //如果没有fontsize
314
- // if (!style.includes("font-size")) {
315
- // $(this).attr("style", `${style} font-size:16px;`);
316
- // }
317
- // });
318
493
  var hTagRem_1 = ["2rem", "1.5rem", "1.17rem", "1rem", "0.83rem", "0.67rem"];
319
494
  ["h1", "h2", "h3", "h4", "h5", "h6"].map(function (tag) {
320
495
  return $("".concat(tag)).each(function () {
@@ -322,7 +497,7 @@ var RichText;
322
497
  var style = $(this).attr("style") || "";
323
498
  //如果没有fontsize
324
499
  if (!style.includes("font-size")) {
325
- $(this).attr("style", "".concat(style, " font-size:").concat(hTagRem_1[idx]));
500
+ $(this).attr("style", mergeStyles(style, "font-size:".concat(hTagRem_1[idx])));
326
501
  }
327
502
  });
328
503
  });
@@ -330,7 +505,7 @@ var RichText;
330
505
  //手动替换nbsp
331
506
  var res = $.html();
332
507
  res = res.replaceAll(/<img([^>]+)>/g, "<img$1 />").replaceAll(/&nbsp;/g, " ");
333
- // console.log(res);
508
+ console.log(res);
334
509
  return res;
335
510
  }
336
511
  };
package/dist/index.d.ts CHANGED
@@ -1,2 +1,2 @@
1
- export { RichText } from "./RichTextUtils";
2
- export { RichText as default } from "./RichTextUtils";
1
+ export { RichText } from "./RichText";
2
+ export { RichText as default } from "./RichText";
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.default = exports.RichText = void 0;
4
- var RichTextUtils_1 = require("./RichTextUtils");
5
- Object.defineProperty(exports, "RichText", { enumerable: true, get: function () { return RichTextUtils_1.RichText; } });
6
- var RichTextUtils_2 = require("./RichTextUtils");
7
- Object.defineProperty(exports, "default", { enumerable: true, get: function () { return RichTextUtils_2.RichText; } });
4
+ var RichText_1 = require("./RichText");
5
+ Object.defineProperty(exports, "RichText", { enumerable: true, get: function () { return RichText_1.RichText; } });
6
+ var RichText_2 = require("./RichText");
7
+ Object.defineProperty(exports, "default", { enumerable: true, get: function () { return RichText_2.RichText; } });
@@ -0,0 +1,77 @@
1
+ /*****************************************************
2
+ * 类型、枚举定义
3
+ *****************************************************/
4
+ /** 链接节点的类型 */
5
+ export declare enum LinkNodeType {
6
+ IMG = "img",
7
+ DIV = "div"
8
+ }
9
+ /** 点击回调参数 */
10
+ export interface LinkClickParams {
11
+ type: LinkNodeType;
12
+ href: string;
13
+ id: string;
14
+ }
15
+ /*****************************************************
16
+ * RichTextCore 主类
17
+ *****************************************************/
18
+ export declare class RichTextCore {
19
+ private static linkNodes;
20
+ private static hrefIdCnt;
21
+ /**
22
+ * 富文本转换为 PixUI innerHTML 兼容格式
23
+ * @param str 原始富文本 HTML
24
+ * @param config 可选配置
25
+ */
26
+ static convertRichTextToPixuiStyle(str: string, config?: {
27
+ lineHeightScale?: number;
28
+ }): string;
29
+ /**
30
+ * 绑定链接点击事件
31
+ */
32
+ static bindLinkClickEvents(linkClickHandler: (params: LinkClickParams) => void): void;
33
+ /*****************************************************
34
+ * 内部流程函数
35
+ *****************************************************/
36
+ /**
37
+ * 将 HTML 切分为基础段落片段。
38
+ * 简易实现:以顶级节点为粒度切分,再根据最外层列表节点进行分割。
39
+ */
40
+ private static splitIntoSegments;
41
+ /**
42
+ * 处理单个段落片段,返回转换后的片段 HTML
43
+ */
44
+ private static processSegment;
45
+ private static preprocess;
46
+ private static fixImgSizeUnit;
47
+ /** 列表处理(将 ol/ul/li 替换为 div 结构) */
48
+ private static handleLists;
49
+ /** 将连续 <br> 替换为透明占位文字,确保 PixUI 换行 */
50
+ private static replaceBrWithPlaceholder;
51
+ /** 处理 <a> 标签:转为 div、保留样式、记录链接 */
52
+ private static handleAnchorTag;
53
+ /** 用 text 标签包裹纯文本节点 */
54
+ private static wrapTextNodes;
55
+ /** strong / em / u / s / span 等标签替换成 text */
56
+ private static replaceStyleTags;
57
+ /** p → div */
58
+ private static replacePTag;
59
+ /** img 的 href 转移处理 & 记录链接 */
60
+ private static handleImgHref;
61
+ /** 给指定标签补 flex-shrink:0 */
62
+ private static addFlexShrink;
63
+ /** 把嵌套的 text 展平 */
64
+ private static flattenNestedText;
65
+ /** 将同一行的 text 节点转为 div 并外包一层 text */
66
+ private static convertSiblingTextToDiv;
67
+ /** text-indent 首行缩进处理 */
68
+ private static handleTextIndent;
69
+ /** line-height / letter-spacing 调整 */
70
+ private static adjustLineHeightAndLetterSpacing;
71
+ /** 把 div 的 text-align 转移到子 text 上 */
72
+ private static transferTextAlign;
73
+ /** 删除空 text 节点 */
74
+ private static removeEmptyText;
75
+ /** 补默认 heading 字体大小 */
76
+ private static fixHeadingFontSize;
77
+ }