im-ui-mobile 0.1.0 → 0.1.2

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.
Files changed (85) hide show
  1. package/components/im-avatar/im-avatar.vue +7 -7
  2. package/components/im-badge/im-badge.vue +326 -0
  3. package/components/im-button/im-button.vue +71 -34
  4. package/components/im-card/im-card.vue +563 -0
  5. package/components/im-chat-item/im-chat-item.vue +5 -4
  6. package/components/im-col/im-col.vue +191 -0
  7. package/components/im-dialog/im-dialog.vue +543 -0
  8. package/components/im-double-tap-view/im-double-tap-view.vue +93 -0
  9. package/components/im-emoji-picker/im-emoji-picker.vue +1143 -0
  10. package/components/im-friend-item/im-friend-item.vue +1 -1
  11. package/components/im-group-item/im-group-item.vue +1 -1
  12. package/components/im-group-member-selector/im-group-member-selector.vue +5 -5
  13. package/components/im-group-rtc-join/im-group-rtc-join.vue +8 -8
  14. package/components/im-icon/im-icon.vue +593 -0
  15. package/components/im-image-upload/im-image-upload.vue +0 -2
  16. package/components/im-link/im-link.vue +628 -0
  17. package/components/im-loading/im-loading.vue +13 -4
  18. package/components/im-mention-picker/im-mention-picker.vue +8 -7
  19. package/components/im-message-action/im-message-action.vue +678 -0
  20. package/components/im-message-item/im-message-item.vue +28 -26
  21. package/components/im-message-list/im-message-list.vue +1108 -0
  22. package/components/im-modal/im-modal.vue +373 -0
  23. package/components/im-nav-bar/im-nav-bar.vue +689 -75
  24. package/components/im-parse/im-parse.vue +1054 -0
  25. package/components/im-popup/im-popup.vue +467 -0
  26. package/components/im-read-receipt/im-read-receipt.vue +10 -10
  27. package/components/im-row/im-row.vue +189 -0
  28. package/components/im-search/im-search.vue +762 -0
  29. package/components/im-sku/im-sku.vue +720 -0
  30. package/components/im-sku/utils/helper.ts +182 -0
  31. package/components/im-stepper/im-stepper.vue +585 -0
  32. package/components/im-stepper/utils/helper.ts +167 -0
  33. package/components/im-tabs/im-tabs.vue +1022 -0
  34. package/components/im-tabs/tabs-navigation.vue +489 -0
  35. package/components/im-tabs/utils/helper.ts +181 -0
  36. package/components/im-tabs-tab-pane/im-tabs-tab-pane.vue +145 -0
  37. package/components/im-upload/im-upload.vue +1236 -0
  38. package/components/im-voice-input/im-voice-input.vue +1 -1
  39. package/index.js +3 -5
  40. package/index.scss +19 -0
  41. package/libs/emoji-data.ts +229 -0
  42. package/libs/index.ts +16 -16
  43. package/package.json +1 -2
  44. package/styles/button.scss +33 -33
  45. package/theme.scss +2 -2
  46. package/types/components/badge.d.ts +42 -0
  47. package/types/components/button.d.ts +2 -1
  48. package/types/components/card.d.ts +122 -0
  49. package/types/components/col.d.ts +37 -0
  50. package/types/components/dialog.d.ts +125 -0
  51. package/types/components/double-tap-view.d.ts +31 -0
  52. package/types/components/emoji-picker.d.ts +121 -0
  53. package/types/components/group-rtc-join.d.ts +1 -1
  54. package/types/components/icon.d.ts +77 -0
  55. package/types/components/link.d.ts +55 -0
  56. package/types/components/loading.d.ts +1 -0
  57. package/types/components/message-action.d.ts +96 -0
  58. package/types/components/message-item.d.ts +2 -2
  59. package/types/components/message-list.d.ts +136 -0
  60. package/types/components/modal.d.ts +106 -0
  61. package/types/components/nav-bar.d.ts +125 -0
  62. package/types/components/parse.d.ts +90 -0
  63. package/types/components/popup.d.ts +58 -0
  64. package/types/components/row.d.ts +31 -0
  65. package/types/components/search.d.ts +54 -0
  66. package/types/components/sku.d.ts +195 -0
  67. package/types/components/stepper.d.ts +99 -0
  68. package/types/components/tabs-tab-pane.d.ts +27 -0
  69. package/types/components/tabs.d.ts +117 -0
  70. package/types/components/upload.d.ts +137 -0
  71. package/types/components.d.ts +19 -1
  72. package/types/index.d.ts +38 -1
  73. package/types/libs/index.d.ts +10 -10
  74. package/types/utils/base64.d.ts +5 -0
  75. package/types/utils/dom.d.ts +3 -0
  76. package/types/utils/enums.d.ts +4 -5
  77. package/types/utils/validator.d.ts +74 -0
  78. package/utils/base64.js +18 -0
  79. package/utils/dom.js +353 -1
  80. package/utils/enums.js +4 -5
  81. package/utils/validator.js +230 -0
  82. package/components/im-file-upload/im-file-upload.vue +0 -309
  83. package/plugins/uview-plus.js +0 -29
  84. package/types/components/arrow-bar.d.ts +0 -14
  85. package/types/components/file-upload.d.ts +0 -58
@@ -19,8 +19,8 @@ export interface Chat {
19
19
  id?: string | number;
20
20
  targetId: number;
21
21
  type: 'PRIVATE' | 'GROUP';
22
- showName: string;
23
- headImage: string;
22
+ displayName: string;
23
+ avatar: string;
24
24
  isDnd: boolean;
25
25
  lastContent: string;
26
26
  lastSendTime?: number;
@@ -49,11 +49,11 @@ export interface Message {
49
49
  sendNickName?: string;
50
50
  atUserIds?: number[];
51
51
  sendId?: number;
52
- recvId?: number;
52
+ receiveId?: number;
53
53
  groupId?: number;
54
54
  receipt?: boolean;
55
55
  receiptOk?: boolean;
56
- readedCount?: number;
56
+ readCount?: number;
57
57
  quoteMessage?: Message;
58
58
  fileId?: string;
59
59
  }
@@ -63,7 +63,7 @@ export interface Message {
63
63
  export interface Friend {
64
64
  id: number;
65
65
  nickName: string;
66
- headImage?: string;
66
+ avatar?: string;
67
67
  online: boolean;
68
68
  onlineWeb: boolean;
69
69
  onlineApp: boolean;
@@ -103,7 +103,7 @@ export interface GroupMember {
103
103
  id?: number;
104
104
  userId: number;
105
105
  showNickName: string;
106
- headImage?: string;
106
+ avatar?: string;
107
107
  quit?: boolean;
108
108
  [key: string]: any;
109
109
  }
@@ -116,14 +116,14 @@ export interface Group {
116
116
  isBanned?: boolean;
117
117
  reason?: string;
118
118
  name: string;
119
- headImage: string;
119
+ avatar: string;
120
120
  isDnd: boolean;
121
121
  quit?: boolean;
122
122
  topMessage?: string;
123
123
  memberCount?: number;
124
124
  createTime?: number;
125
125
  showGroupName: string;
126
- headImageThumb?: string;
126
+ avatarThumb?: string;
127
127
  remarkGroupName?: string;
128
128
  remarkNickName?: string;
129
129
  notice?: string;
@@ -183,8 +183,8 @@ export interface RecorderFile {
183
183
  export interface UserInfo {
184
184
  id: number;
185
185
  nickName: string;
186
- headImage: string;
187
- headImageThumb?: string;
186
+ avatar: string;
187
+ avatarThumb?: string;
188
188
  email?: string;
189
189
  phone?: string;
190
190
  gender?: number;
@@ -0,0 +1,5 @@
1
+ declare const _default: {
2
+ encodeBase64: (str: string) => string;
3
+ decodeBase64: (str: string) => string;
4
+ };
5
+ export default _default;
@@ -7,5 +7,8 @@ declare const _default: {
7
7
  hasHtmlSpecialChars: (text: string) => boolean;
8
8
  setSafeHTML: (element: HTMLElement, html: string) => void;
9
9
  safeHTML: (strings: TemplateStringsArray, ...values: any[]) => string;
10
+ nodesToHtml: (nodes: any[], options: any) => string;
11
+ extractPlainText: (nodes: any[]) => string;
12
+ escapeHtml: (text: string) => string;
10
13
  };
11
14
  export default _default;
@@ -8,9 +8,8 @@ export declare enum MESSAGE_TYPE {
8
8
  AUDIO = 3,
9
9
  VIDEO = 4,
10
10
  RECALL = 10,
11
- READED = 11,
11
+ READ = 11,
12
12
  RECEIPT = 12,
13
- TIP_TIME = 20,
14
13
  TIP_TEXT = 21,
15
14
  LOADING = 30,
16
15
  ACT_RT_VOICE = 40,
@@ -66,8 +65,8 @@ export declare enum TERMINAL_TYPE {
66
65
  export declare enum MESSAGE_STATUS {
67
66
  FAILED = -2,// 发送失败
68
67
  SENDING = -1,// 发送中(消息没到服务器)
69
- PENDING = 0,// 未送达(消息已到服务器,但对方没收到)
70
- DELIVERED = 1,// 已送达(对方已收到,但是未读消息)
68
+ SENT = 0,// 未送达(消息已到服务器,但对方没收到)
69
+ RECEIVED = 1,// 已送达(对方已收到,但是未读消息)
71
70
  RECALL = 2,// 已撤回
72
- READED = 3
71
+ READ = 3 // 已读
73
72
  }
@@ -0,0 +1,74 @@
1
+ /**
2
+ * 密码校验选项接口
3
+ */
4
+ export interface PasswordValidationOptions {
5
+ /** 最小长度,默认8 */
6
+ minLength?: number;
7
+ /** 是否要求大写字母,默认true */
8
+ requireUppercase?: boolean;
9
+ /** 是否要求小写字母,默认true */
10
+ requireLowercase?: boolean;
11
+ /** 是否要求数字,默认true */
12
+ requireNumbers?: boolean;
13
+ /** 是否要求特殊字符,默认true */
14
+ requireSpecialChars?: boolean;
15
+ }
16
+
17
+ /**
18
+ * 密码校验结果接口
19
+ */
20
+ export interface PasswordValidationResult {
21
+ /** 是否通过校验 */
22
+ isValid: boolean;
23
+ /** 密码强度(弱/中/强) */
24
+ strength: '弱' | '中' | '强';
25
+ /** 错误消息数组 */
26
+ messages: string[];
27
+ }
28
+
29
+ /**
30
+ * 校验函数声明
31
+ */
32
+ export declare function isNumber(value: string | number): boolean;
33
+ export declare function isInteger(value: string | number): boolean;
34
+ export declare function isDecimal(value: string | number, decimalPlaces?: number): boolean;
35
+ export declare function isPositiveInteger(value: string | number): boolean;
36
+ export declare function isMobilePhone(phone: string): boolean;
37
+ export declare function isInternationalPhone(phone: string): boolean;
38
+ export declare function isLandlinePhone(phone: string): boolean;
39
+ export declare function isEmail(email: string): boolean;
40
+ export declare function isEmailStrict(email: string): boolean;
41
+ export declare function isChineseIDCard(idCard: string): boolean;
42
+ export declare function isURL(url: string): boolean;
43
+ export declare function validatePassword(
44
+ password: string,
45
+ options?: PasswordValidationOptions
46
+ ): PasswordValidationResult;
47
+ export declare function isChineseName(name: string): boolean;
48
+ export declare function isLicensePlate(plate: string): boolean;
49
+
50
+ /**
51
+ * 校验工具模块默认导出接口
52
+ */
53
+ export interface ValidatorModule {
54
+ isNumber: typeof isNumber;
55
+ isInteger: typeof isInteger;
56
+ isDecimal: typeof isDecimal;
57
+ isPositiveInteger: typeof isPositiveInteger;
58
+ isMobilePhone: typeof isMobilePhone;
59
+ isInternationalPhone: typeof isInternationalPhone;
60
+ isLandlinePhone: typeof isLandlinePhone;
61
+ isEmail: typeof isEmail;
62
+ isEmailStrict: typeof isEmailStrict;
63
+ isChineseIDCard: typeof isChineseIDCard;
64
+ isURL: typeof isURL;
65
+ validatePassword: typeof validatePassword;
66
+ isChineseName: typeof isChineseName;
67
+ isLicensePlate: typeof isLicensePlate;
68
+ }
69
+
70
+ /**
71
+ * 默认导出
72
+ */
73
+ declare const validator: ValidatorModule;
74
+ export default validator;
@@ -0,0 +1,18 @@
1
+ // 编码字符串为 base64(支持中文)
2
+ const encodeBase64 = (str) => {
3
+ return btoa(encodeURIComponent(str).replace(/%([0-9A-F]{2})/g, (match, p1) => {
4
+ return String.fromCharCode('0x' + p1);
5
+ }));
6
+ }
7
+
8
+ // 解码 base64 字符串
9
+ const decodeBase64 = (str) => {
10
+ return decodeURIComponent(atob(str).split('').map(c => {
11
+ return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2);
12
+ }).join(''));
13
+ }
14
+
15
+ export default {
16
+ encodeBase64,
17
+ decodeBase64
18
+ }
package/utils/dom.js CHANGED
@@ -136,6 +136,355 @@ const safeHTML = (strings, ...values) => {
136
136
  return result;
137
137
  };
138
138
 
139
+
140
+
141
+ /**
142
+ * 将富文本节点数组转换回 HTML 字符串
143
+ * @param {Array|Object} nodes - 富文本节点数组或单个节点
144
+ * @param {Object} options - 转换选项
145
+ * @returns {string} HTML 字符串
146
+ */
147
+ function nodesToHtml(nodes, options = {}) {
148
+ const defaultOptions = {
149
+ escapeSpecialChars: false,
150
+ selfClosingTags: ['img', 'br', 'hr', 'input', 'meta', 'link', 'source'],
151
+ keepStyle: true,
152
+ keepClass: true,
153
+ restoreLinks: true, // 是否恢复链接为a标签
154
+ keepDataAttributes: true, // 是否保留data-属性
155
+ };
156
+
157
+ const mergedOptions = { ...defaultOptions, ...options };
158
+
159
+ // 处理节点数组
160
+ const nodeArray = Array.isArray(nodes) ? nodes : [nodes];
161
+
162
+ return nodeArray.map(node => processNode(node, mergedOptions)).join('');
163
+ }
164
+
165
+ /**
166
+ * 处理单个节点
167
+ * @param {Object} node - 节点对象
168
+ * @param {Object} options - 选项
169
+ * @returns {string} 处理后的HTML
170
+ */
171
+ function processNode(node, options) {
172
+ if (!node) return '';
173
+
174
+ // 处理文本节点
175
+ if (node.type === 'text' || node.text !== undefined) {
176
+ const text = node.text || '';
177
+ return options.escapeSpecialChars ? escapeHtml(text) : text;
178
+ }
179
+
180
+ // 处理子节点为字符串的情况
181
+ if (typeof node.children === 'string') {
182
+ const content = options.escapeSpecialChars ? escapeHtml(node.children) : node.children;
183
+
184
+ // 如果没有标签名,直接返回内容
185
+ if (!node.name) {
186
+ return content;
187
+ }
188
+
189
+ const attrs = processAttributes(node.attrs || {}, options);
190
+
191
+ // 检查是否是链接节点,尝试恢复为a标签
192
+ if (options.restoreLinks && isLinkNode(node)) {
193
+ return restoreLinkNode(node, content);
194
+ }
195
+
196
+ return `<${node.name}${attrs}>${content}</${node.name}>`;
197
+ }
198
+
199
+ // 处理子节点数组
200
+ const tagName = node.name || 'div';
201
+ const attrs = processAttributes(node.attrs || {}, options);
202
+
203
+ // 处理子节点
204
+ let children = '';
205
+ if (node.children) {
206
+ if (Array.isArray(node.children)) {
207
+ children = node.children.map(child => processNode(child, options)).join('');
208
+ } else {
209
+ children = processNode(node.children, options);
210
+ }
211
+ }
212
+
213
+ // 检查是否是链接节点,尝试恢复为a标签
214
+ if (options.restoreLinks && isLinkNode(node)) {
215
+ return restoreLinkNode(node, children);
216
+ }
217
+
218
+ // 自闭合标签处理
219
+ if (options.selfClosingTags.includes(tagName.toLowerCase()) && !children.trim()) {
220
+ return `<${tagName}${attrs} />`;
221
+ }
222
+
223
+ return `<${tagName}${attrs}>${children}</${tagName}>`;
224
+ }
225
+
226
+ /**
227
+ * 处理属性
228
+ * @param {Object} attrs - 属性对象
229
+ * @param {Object} options - 选项
230
+ * @returns {string} 属性字符串
231
+ */
232
+ function processAttributes(attrs, options) {
233
+ if (!attrs || Object.keys(attrs).length === 0) {
234
+ return '';
235
+ }
236
+
237
+ const processedAttrs = [];
238
+
239
+ for (const [name, value] of Object.entries(attrs)) {
240
+ // 跳过某些属性
241
+ if (!shouldKeepAttribute(name, value, options)) {
242
+ continue;
243
+ }
244
+
245
+ const attrValue = typeof value === 'string'
246
+ ? escapeHtmlAttribute(value)
247
+ : String(value);
248
+
249
+ processedAttrs.push(`${name}="${attrValue}"`);
250
+ }
251
+
252
+ return processedAttrs.length > 0 ? ' ' + processedAttrs.join(' ') : '';
253
+ }
254
+
255
+ /**
256
+ * 判断是否保留属性
257
+ * @param {string} name - 属性名
258
+ * @param {any} value - 属性值
259
+ * @param {Object} options - 选项
260
+ * @returns {boolean} 是否保留
261
+ */
262
+ function shouldKeepAttribute(name, value, options) {
263
+ // 空值不处理
264
+ if (value === null || value === undefined || value === '') {
265
+ return false;
266
+ }
267
+
268
+ // 根据选项过滤样式和类名
269
+ if (name === 'style' && !options.keepStyle) {
270
+ return false;
271
+ }
272
+
273
+ if (name === 'class' && !options.keepClass) {
274
+ return false;
275
+ }
276
+
277
+ // 处理data-属性
278
+ if (name.startsWith('data-')) {
279
+ // 如果是链接节点的data属性,保留用于恢复链接
280
+ if (name === 'data-link-url' || name === 'data-link-id') {
281
+ return true;
282
+ }
283
+ // 其他data属性根据选项决定
284
+ return options.keepDataAttributes;
285
+ }
286
+
287
+ return true;
288
+ }
289
+
290
+ /**
291
+ * 判断是否是链接节点
292
+ * @param {Object} node - 节点对象
293
+ * @returns {boolean} 是否是链接节点
294
+ */
295
+ function isLinkNode(node) {
296
+ return node.attrs && (
297
+ node.attrs['data-link-url'] ||
298
+ node.attrs.class && node.attrs.class.includes('im-parse-link')
299
+ );
300
+ }
301
+
302
+ /**
303
+ * 恢复链接节点为a标签
304
+ * @param {Object} node - 链接节点
305
+ * @param {string} content - 节点内容
306
+ * @returns {string} a标签HTML
307
+ */
308
+ function restoreLinkNode(node, content) {
309
+ const attrs = node.attrs || {};
310
+ const url = attrs['data-link-url'];
311
+
312
+ if (!url) {
313
+ // 如果没有URL,返回原始节点
314
+ const originalAttrs = processAttributes(attrs, {});
315
+ return `<${node.name || 'span'}${originalAttrs}>${content}</${node.name || 'span'}>`;
316
+ }
317
+
318
+ // 解码URL
319
+ let decodedUrl;
320
+ try {
321
+ decodedUrl = decodeURIComponent(url);
322
+ } catch {
323
+ decodedUrl = url;
324
+ }
325
+
326
+ // 构建a标签属性
327
+ const linkAttrs = {
328
+ href: decodedUrl,
329
+ style: attrs.style || '',
330
+ class: attrs.class ? attrs.class.replace('im-parse-link', '').trim() : '',
331
+ };
332
+
333
+ const linkAttrStr = processAttributes(linkAttrs, {});
334
+
335
+ return `<a${linkAttrStr}>${content}</a>`;
336
+ }
337
+
338
+ /**
339
+ * 转义 HTML 特殊字符
340
+ * @param {string} text - 原始文本
341
+ * @returns {string} 转义后的文本
342
+ */
343
+ function escapeHtml(text) {
344
+ const escapeMap = {
345
+ '&': '&amp;',
346
+ '<': '&lt;',
347
+ '>': '&gt;',
348
+ '"': '&quot;',
349
+ "'": '&#39;',
350
+ '`': '&#96;',
351
+ };
352
+
353
+ return text.replace(/[&<>"'`]/g, match => escapeMap[match] || match);
354
+ }
355
+
356
+ /**
357
+ * 转义 HTML 属性值中的特殊字符
358
+ * @param {string} text - 原始文本
359
+ * @returns {string} 转义后的文本
360
+ */
361
+ function escapeHtmlAttribute(text) {
362
+ return text
363
+ .replace(/"/g, '&quot;')
364
+ .replace(/'/g, '&#39;')
365
+ .replace(/&/g, '&amp;')
366
+ .replace(/</g, '&lt;')
367
+ .replace(/>/g, '&gt;');
368
+ }
369
+
370
+ // /**
371
+ // * 专门用于 im-parse 组件的转换函数
372
+ // * @param {Array} parsedNodes - im-parse组件的parsedNodes
373
+ // * @param {string} originalContent - 原始HTML内容(可选)
374
+ // * @param {Object} options - 转换选项
375
+ // * @returns {string} HTML字符串
376
+ // */
377
+ // function imParseNodesToHtml(parsedNodes, originalContent = '', options = {}) {
378
+ // try {
379
+ // // 默认选项
380
+ // const defaultOptions = {
381
+ // restoreLinks: true,
382
+ // keepStyle: false, // 通常不需要保留im-parse添加的内联样式
383
+ // keepClass: false, // 通常不需要保留im-parse添加的类名
384
+ // escapeSpecialChars: true,
385
+ // };
386
+
387
+ // const mergedOptions = { ...defaultOptions, ...options };
388
+
389
+ // // 转换为HTML
390
+ // const html = nodesToHtml(parsedNodes, mergedOptions);
391
+
392
+ // // 如果有原始内容,可以尝试更智能的恢复
393
+ // if (originalContent && mergedOptions.restoreLinks) {
394
+ // return smartRestoreLinks(html, originalContent);
395
+ // }
396
+
397
+ // return html;
398
+ // } catch (error) {
399
+ // console.error('转换回 HTML 失败:', error);
400
+ // return originalContent || '';
401
+ // }
402
+ // }
403
+
404
+ /**
405
+ * 智能恢复链接(基于原始内容)
406
+ * @param {string} html - 转换后的HTML
407
+ * @param {string} original - 原始HTML
408
+ * @returns {string} 恢复链接后的HTML
409
+ */
410
+ function smartRestoreLinks(html, original) {
411
+ // 从原始内容中提取链接信息
412
+ const linkMatches = [];
413
+ const linkRegex = /<a\s+(?:[^>]*?\s+)?href=(["'])(.*?)\1[^>]*?>([\s\S]*?)<\/a>/gi;
414
+ let match;
415
+
416
+ while ((match = linkRegex.exec(original)) !== null) {
417
+ linkMatches.push({
418
+ url: match[2],
419
+ text: match[3],
420
+ fullMatch: match[0]
421
+ });
422
+ }
423
+
424
+ // 如果没有链接,直接返回
425
+ if (linkMatches.length === 0) {
426
+ return html;
427
+ }
428
+
429
+ // 尝试用正则替换恢复链接
430
+ let restoredHtml = html;
431
+
432
+ // 替换im-parse生成的链接占位符
433
+ linkMatches.forEach((link, index) => {
434
+ const placeholderRegex = new RegExp(
435
+ `<span[^>]*?data-link-url=["']${escapeRegExp(encodeURIComponent(link.url))}["'][^>]*?>([\\s\\S]*?)<\\/span>`,
436
+ 'gi'
437
+ );
438
+
439
+ restoredHtml = restoredHtml.replace(placeholderRegex, () => {
440
+ return `<a href="${escapeHtml(link.url)}">${link.text}</a>`;
441
+ });
442
+ });
443
+
444
+ return restoredHtml;
445
+ }
446
+
447
+ /**
448
+ * 转义正则表达式特殊字符
449
+ * @param {string} string - 原始字符串
450
+ * @returns {string} 转义后的字符串
451
+ */
452
+ function escapeRegExp(string) {
453
+ return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
454
+ }
455
+
456
+ /**
457
+ * 从im-parse节点中提取纯文本
458
+ * @param {Array|Object} nodes - 节点数组或单个节点
459
+ * @returns {string} 纯文本
460
+ */
461
+ function extractPlainText(nodes) {
462
+ if (!nodes) return '';
463
+
464
+ const nodeArray = Array.isArray(nodes) ? nodes : [nodes];
465
+
466
+ return nodeArray.map(node => {
467
+ if (node.type === 'text' || node.text !== undefined) {
468
+ return node.text || '';
469
+ }
470
+
471
+ if (typeof node.children === 'string') {
472
+ return node.children;
473
+ }
474
+
475
+ if (node.children) {
476
+ if (Array.isArray(node.children)) {
477
+ return extractPlainText(node.children);
478
+ }
479
+ return extractPlainText(node.children);
480
+ }
481
+
482
+ return '';
483
+ }).join('');
484
+ }
485
+
486
+
487
+
139
488
  export default {
140
489
  html2Escape,
141
490
  htmlEscape,
@@ -144,5 +493,8 @@ export default {
144
493
  escapeTextContent,
145
494
  hasHtmlSpecialChars,
146
495
  setSafeHTML,
147
- safeHTML
496
+ safeHTML,
497
+ nodesToHtml,
498
+ extractPlainText,
499
+ escapeHtml,
148
500
  };
package/utils/enums.js CHANGED
@@ -8,9 +8,8 @@ export const MESSAGE_TYPE = {
8
8
  AUDIO: 3,
9
9
  VIDEO: 4,
10
10
  RECALL: 10,
11
- READED: 11,
11
+ READ: 11,
12
12
  RECEIPT: 12,
13
- TIP_TIME: 20,
14
13
  TIP_TEXT: 21,
15
14
  LOADING: 30,
16
15
  ACT_RT_VOICE: 40,
@@ -69,8 +68,8 @@ export const TERMINAL_TYPE = {
69
68
  export const MESSAGE_STATUS = {
70
69
  FAILED: -2, // 发送失败
71
70
  SENDING: -1, // 发送中(消息没到服务器)
72
- PENDING: 0, // 未送达(消息已到服务器,但对方没收到)
73
- DELIVERED: 1, // 已送达(对方已收到,但是未读消息)
71
+ SENT: 0, // 未送达(消息已到服务器,但对方没收到)
72
+ RECEIVED: 1, // 已送达(对方已收到,但是未读消息)
74
73
  RECALL: 2, // 已撤回
75
- READED: 3, // 消息已读
74
+ READ: 3, // 消息已读
76
75
  };