@aochuang/client-sdk 1.0.278

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 (366) hide show
  1. package/README.md +93 -0
  2. package/common/avatar/avatar.vue +86 -0
  3. package/common/avatar/index.js +3 -0
  4. package/common/badge/badge.vue +105 -0
  5. package/common/badge/index.js +3 -0
  6. package/common/context-menu/context-menu-box.vue +61 -0
  7. package/common/context-menu/context-menu-group.vue +162 -0
  8. package/common/context-menu/context-menu-item.vue +179 -0
  9. package/common/context-menu/context-menu.vue +201 -0
  10. package/common/context-menu/index.js +3 -0
  11. package/common/context-menu/utils.js +138 -0
  12. package/common/friend-avatar/default.svg +1 -0
  13. package/common/friend-avatar/friend-avatar.vue +29 -0
  14. package/common/friend-avatar/index.js +3 -0
  15. package/common/image/index.js +3 -0
  16. package/common/index.js +17 -0
  17. package/common/room-avatar/default.svg +6 -0
  18. package/common/room-avatar/index.js +3 -0
  19. package/common/room-avatar/room-avatar.vue +29 -0
  20. package/common/scroll-load-more/index.js +3 -0
  21. package/common/scroll-load-more/scroll-load-more.vue +195 -0
  22. package/common/user-avatar/default.svg +1 -0
  23. package/common/user-avatar/index.js +3 -0
  24. package/common/user-avatar/user-avatar.vue +29 -0
  25. package/components/account-select/account-select-skeleton.vue +56 -0
  26. package/components/account-select/account-select.vue +205 -0
  27. package/components/account-select/all.svg +1 -0
  28. package/components/account-select/index.js +3 -0
  29. package/components/chat-editor/MENTION_TOKEN_SUMMARY.md +74 -0
  30. package/components/chat-editor/chat-editor-button/chat-editor-button.vue +78 -0
  31. package/components/chat-editor/chat-editor-suggestions/chat-editor-suggestions.vue +329 -0
  32. package/components/chat-editor/chat-editor-toolbar/chat-editor-toolbar-item.vue +28 -0
  33. package/components/chat-editor/chat-editor-toolbar/chat-editor-toolbar.vue +17 -0
  34. package/components/chat-editor/chat-editor.vue +933 -0
  35. package/components/chat-editor/index.js +12 -0
  36. package/components/chat-editor/utils.js +189 -0
  37. package/components/chat-friend-profile/chat-friend-profile.vue +118 -0
  38. package/components/chat-friend-profile/index.js +3 -0
  39. package/components/chat-layout/chat-layout.vue +71 -0
  40. package/components/chat-layout/index.js +3 -0
  41. package/components/chat-panel/chat-panel-box.vue +350 -0
  42. package/components/chat-panel/chat-panel-chat.vue +121 -0
  43. package/components/chat-panel/chat-panel-chats.vue +674 -0
  44. package/components/chat-panel/chat-panel-empty.vue +32 -0
  45. package/components/chat-panel/chat-panel-increment.vue +80 -0
  46. package/components/chat-panel/chat-panel-inner-msg.vue +25 -0
  47. package/components/chat-panel/chat-panel-loading.vue +29 -0
  48. package/components/chat-panel/chat-panel-nearest.vue +136 -0
  49. package/components/chat-panel/chat-panel.vue +292 -0
  50. package/components/chat-panel/index.js +3 -0
  51. package/components/chat-panel/util.js +83 -0
  52. package/components/chat-room-profile/chat-room-profile.vue +103 -0
  53. package/components/chat-room-profile/index.js +3 -0
  54. package/components/contacts-friend-select/contacts-friend-select-skeleton.vue +41 -0
  55. package/components/contacts-friend-select/contacts-friend-select.vue +204 -0
  56. package/components/contacts-friend-select/index.js +3 -0
  57. package/components/contacts-group-select/contacts-group-select-item.vue +98 -0
  58. package/components/contacts-group-select/contacts-group-select-skeleton.vue +81 -0
  59. package/components/contacts-group-select/contacts-group-select.vue +396 -0
  60. package/components/contacts-group-select/index.js +3 -0
  61. package/components/contacts-room-select/contacts-room-select-skeleton.vue +41 -0
  62. package/components/contacts-room-select/contacts-room-select.vue +208 -0
  63. package/components/contacts-room-select/index.js +3 -0
  64. package/components/index.js +43 -0
  65. package/components/layout/index.js +3 -0
  66. package/components/layout/layout.vue +70 -0
  67. package/components/session-select/index.js +11 -0
  68. package/components/session-select/session-select-item-skeleton.vue +59 -0
  69. package/components/session-select/session-select-item.vue +190 -0
  70. package/components/session-select/session-select.vue +386 -0
  71. package/components/wechat-chat-control/calling/calling.vue +227 -0
  72. package/components/wechat-chat-control/calling/phone-fill.svg +1 -0
  73. package/components/wechat-chat-control/calling/vidicon-fill.svg +1 -0
  74. package/components/wechat-chat-control/channel-live/channel-live.vue +198 -0
  75. package/components/wechat-chat-control/channel-live/quote-icon.svg +1 -0
  76. package/components/wechat-chat-control/channel-video/channel-video.vue +180 -0
  77. package/components/wechat-chat-control/channel-video/quote-icon.svg +1 -0
  78. package/components/wechat-chat-control/channel-video-card/channel-video-card.vue +139 -0
  79. package/components/wechat-chat-control/channel-video-card/quote-icon.svg +1 -0
  80. package/components/wechat-chat-control/company-card/company-card.vue +136 -0
  81. package/components/wechat-chat-control/company-card/quote-icon.svg +1 -0
  82. package/components/wechat-chat-control/custom-expression/custom-expression.vue +45 -0
  83. package/components/wechat-chat-control/empty/empty.vue +27 -0
  84. package/components/wechat-chat-control/file-url/file-url.vue +307 -0
  85. package/components/wechat-chat-control/file-url/icons/file-excel-line.svg +1 -0
  86. package/components/wechat-chat-control/file-url/icons/file-info-line.svg +1 -0
  87. package/components/wechat-chat-control/file-url/icons/file-music-line.svg +1 -0
  88. package/components/wechat-chat-control/file-url/icons/file-pdf-line.svg +1 -0
  89. package/components/wechat-chat-control/file-url/icons/file-ppt-line.svg +1 -0
  90. package/components/wechat-chat-control/file-url/icons/file-settings-line.svg +1 -0
  91. package/components/wechat-chat-control/file-url/icons/file-text-line.svg +1 -0
  92. package/components/wechat-chat-control/file-url/icons/file-unknow-line.svg +1 -0
  93. package/components/wechat-chat-control/file-url/icons/file-word-line.svg +1 -0
  94. package/components/wechat-chat-control/file-url/icons/file-zip-line.svg +1 -0
  95. package/components/wechat-chat-control/file-url/icons/index.js +24 -0
  96. package/components/wechat-chat-control/image/image.vue +344 -0
  97. package/components/wechat-chat-control/image/index.js +1 -0
  98. package/components/wechat-chat-control/image/util.js +29 -0
  99. package/components/wechat-chat-control/index.js +104 -0
  100. package/components/wechat-chat-control/jx-sys-text/jx-sys-text.vue +28 -0
  101. package/components/wechat-chat-control/location/location.vue +179 -0
  102. package/components/wechat-chat-control/location/offline-bg.png +0 -0
  103. package/components/wechat-chat-control/member-change/member-change.vue +37 -0
  104. package/components/wechat-chat-control/music/icons/163.png +0 -0
  105. package/components/wechat-chat-control/music/icons/163.svg +12 -0
  106. package/components/wechat-chat-control/music/icons/default.png +0 -0
  107. package/components/wechat-chat-control/music/icons/default.svg +1 -0
  108. package/components/wechat-chat-control/music/icons/kuwo.png +0 -0
  109. package/components/wechat-chat-control/music/icons/qq.png +0 -0
  110. package/components/wechat-chat-control/music/icons/qq.svg +1 -0
  111. package/components/wechat-chat-control/music/music.vue +156 -0
  112. package/components/wechat-chat-control/music/utils.js +30 -0
  113. package/components/wechat-chat-control/quote-message/quote-message.vue +257 -0
  114. package/components/wechat-chat-control/real-time-position/icon.svg +1 -0
  115. package/components/wechat-chat-control/real-time-position/real-time-position.vue +57 -0
  116. package/components/wechat-chat-control/red-envelopes/quote-icon.svg +1 -0
  117. package/components/wechat-chat-control/red-envelopes/red-envelope.svg +1 -0
  118. package/components/wechat-chat-control/red-envelopes/red-envelopes.vue +168 -0
  119. package/components/wechat-chat-control/revoke-me-message/revoke-me-message.vue +41 -0
  120. package/components/wechat-chat-control/revoke-other-message/revoke-other-message.vue +41 -0
  121. package/components/wechat-chat-control/revoke-you-message/revoke-you-message.vue +41 -0
  122. package/components/wechat-chat-control/room-calling/phone-fill.svg +1 -0
  123. package/components/wechat-chat-control/room-calling/room-calling.vue +52 -0
  124. package/components/wechat-chat-control/room-calling/vidicon-fill.svg +1 -0
  125. package/components/wechat-chat-control/share-card/quote-icon.svg +1 -0
  126. package/components/wechat-chat-control/share-card/share-card.vue +140 -0
  127. package/components/wechat-chat-control/system/system.vue +122 -0
  128. package/components/wechat-chat-control/system-batch-message/system-batch-message.vue +28 -0
  129. package/components/wechat-chat-control/system-money/system-money.vue +39 -0
  130. package/components/wechat-chat-control/text/icons/icon.svg +6 -0
  131. package/components/wechat-chat-control/text/text.vue +164 -0
  132. package/components/wechat-chat-control/text-with-at/text-with-at.vue +88 -0
  133. package/components/wechat-chat-control/transfer/collection.svg +1 -0
  134. package/components/wechat-chat-control/transfer/quote-icon.svg +1 -0
  135. package/components/wechat-chat-control/transfer/red-transfer.svg +1 -0
  136. package/components/wechat-chat-control/transfer/refund.svg +1 -0
  137. package/components/wechat-chat-control/transfer/transfer.svg +1 -0
  138. package/components/wechat-chat-control/transfer/transfer.vue +263 -0
  139. package/components/wechat-chat-control/unknow/unknow.vue +21 -0
  140. package/components/wechat-chat-control/url/icons/link.svg +1 -0
  141. package/components/wechat-chat-control/url/icons/video.svg +6 -0
  142. package/components/wechat-chat-control/url/miniprogram.svg +1 -0
  143. package/components/wechat-chat-control/url/url-file.vue +47 -0
  144. package/components/wechat-chat-control/url/url-link.vue +169 -0
  145. package/components/wechat-chat-control/url/url-merge.vue +164 -0
  146. package/components/wechat-chat-control/url/url-miniprogram.vue +259 -0
  147. package/components/wechat-chat-control/url/url.vue +116 -0
  148. package/components/wechat-chat-control/util.js +234 -0
  149. package/components/wechat-chat-control/video/icons/video.svg +1 -0
  150. package/components/wechat-chat-control/video/video.vue +304 -0
  151. package/components/wechat-chat-control/voice/icons/voice.svg +1 -0
  152. package/components/wechat-chat-control/voice/voice.vue +464 -0
  153. package/components/wechat-chat-control/wechat-chat-control-config-item.vue +24 -0
  154. package/components/wechat-chat-control/wechat-chat-control-config-msg.vue +21 -0
  155. package/components/wechat-chat-control/wechat-chat-control-config.vue +57 -0
  156. package/components/wechat-chat-panel/chat-panel-chat-status/chat-panel-chat-status.vue +69 -0
  157. package/components/wechat-chat-panel/chat-panel-item.vue +695 -0
  158. package/components/wechat-chat-panel/chat-panel.vue +257 -0
  159. package/components/wechat-chat-panel/index.js +3 -0
  160. package/components/wework-chat-control/calling/calling.vue +223 -0
  161. package/components/wework-chat-control/calling/phone-fill.svg +1 -0
  162. package/components/wework-chat-control/calling/vidicon-fill.svg +1 -0
  163. package/components/wework-chat-control/channel-live/channel-live.vue +198 -0
  164. package/components/wework-chat-control/channel-live/quote-icon.svg +1 -0
  165. package/components/wework-chat-control/channel-video/channel-video.vue +180 -0
  166. package/components/wework-chat-control/channel-video/quote-icon.svg +1 -0
  167. package/components/wework-chat-control/company-card/company-card.vue +136 -0
  168. package/components/wework-chat-control/company-card/quote-icon.svg +1 -0
  169. package/components/wework-chat-control/custom-expression/custom-expression.vue +34 -0
  170. package/components/wework-chat-control/empty/empty.vue +27 -0
  171. package/components/wework-chat-control/expression/expression.vue +53 -0
  172. package/components/wework-chat-control/file-url/file-url.vue +247 -0
  173. package/components/wework-chat-control/file-url/icons/file-excel-line.svg +1 -0
  174. package/components/wework-chat-control/file-url/icons/file-info-line.svg +1 -0
  175. package/components/wework-chat-control/file-url/icons/file-music-line.svg +1 -0
  176. package/components/wework-chat-control/file-url/icons/file-pdf-line.svg +1 -0
  177. package/components/wework-chat-control/file-url/icons/file-ppt-line.svg +1 -0
  178. package/components/wework-chat-control/file-url/icons/file-settings-line.svg +1 -0
  179. package/components/wework-chat-control/file-url/icons/file-text-line.svg +1 -0
  180. package/components/wework-chat-control/file-url/icons/file-unknow-line.svg +1 -0
  181. package/components/wework-chat-control/file-url/icons/file-word-line.svg +1 -0
  182. package/components/wework-chat-control/file-url/icons/file-zip-line.svg +1 -0
  183. package/components/wework-chat-control/file-url/icons/index.js +24 -0
  184. package/components/wework-chat-control/image/image.vue +296 -0
  185. package/components/wework-chat-control/image/index.js +1 -0
  186. package/components/wework-chat-control/image/util.js +21 -0
  187. package/components/wework-chat-control/index.js +105 -0
  188. package/components/wework-chat-control/jx-sys-text/jx-sys-text.vue +28 -0
  189. package/components/wework-chat-control/link/link.vue +100 -0
  190. package/components/wework-chat-control/location/location.vue +181 -0
  191. package/components/wework-chat-control/location/offline-bg.png +0 -0
  192. package/components/wework-chat-control/red-envelopes/quote-icon.svg +1 -0
  193. package/components/wework-chat-control/red-envelopes/red-envelope.svg +1 -0
  194. package/components/wework-chat-control/red-envelopes/red-envelopes.vue +168 -0
  195. package/components/wework-chat-control/share-card/quote-icon.svg +1 -0
  196. package/components/wework-chat-control/share-card/share-card.vue +140 -0
  197. package/components/wework-chat-control/text/icons/icon.svg +6 -0
  198. package/components/wework-chat-control/text/text.vue +157 -0
  199. package/components/wework-chat-control/unknow/unknow.vue +21 -0
  200. package/components/wework-chat-control/url/icons/link.svg +1 -0
  201. package/components/wework-chat-control/url/icons/video.svg +6 -0
  202. package/components/wework-chat-control/url/miniprogram.svg +1 -0
  203. package/components/wework-chat-control/url/url-file.vue +37 -0
  204. package/components/wework-chat-control/url/url-link.vue +169 -0
  205. package/components/wework-chat-control/url/url-merge.vue +164 -0
  206. package/components/wework-chat-control/url/url-miniprogram.vue +248 -0
  207. package/components/wework-chat-control/url/url.vue +103 -0
  208. package/components/wework-chat-control/util.js +223 -0
  209. package/components/wework-chat-control/video/icons/video.svg +1 -0
  210. package/components/wework-chat-control/video/video.vue +304 -0
  211. package/components/wework-chat-control/voice/icons/voice.svg +1 -0
  212. package/components/wework-chat-control/voice/voice.vue +464 -0
  213. package/components/wework-chat-panel/chat-panel-controls/company-card/company-card.vue +35 -0
  214. package/components/wework-chat-panel/chat-panel-controls/expression/expression.vue +36 -0
  215. package/components/wework-chat-panel/chat-panel-controls/file-url/file-url.vue +35 -0
  216. package/components/wework-chat-panel/chat-panel-controls/image/image.vue +36 -0
  217. package/components/wework-chat-panel/chat-panel-controls/index.js +35 -0
  218. package/components/wework-chat-panel/chat-panel-controls/jx-sys-text/jx-sys-text.vue +28 -0
  219. package/components/wework-chat-panel/chat-panel-controls/link/link.vue +35 -0
  220. package/components/wework-chat-panel/chat-panel-controls/location/location.vue +35 -0
  221. package/components/wework-chat-panel/chat-panel-controls/text/text.vue +43 -0
  222. package/components/wework-chat-panel/chat-panel-controls/unkonw/unkonw.vue +39 -0
  223. package/components/wework-chat-panel/chat-panel-controls/video/video.vue +37 -0
  224. package/components/wework-chat-panel/chat-panel-controls/voice/voice.vue +35 -0
  225. package/components/wework-chat-panel/chat-panel-item.vue +650 -0
  226. package/components/wework-chat-panel/chat-panel.vue +163 -0
  227. package/components/wework-chat-panel/index.js +3 -0
  228. package/package.json +23 -0
  229. package/services/client-chat/client-chat-wechat/client-chat-account.js +426 -0
  230. package/services/client-chat/client-chat-wechat/client-chat-concats.js +1029 -0
  231. package/services/client-chat/client-chat-wechat/client-chat-draft.js +92 -0
  232. package/services/client-chat/client-chat-wechat/client-chat-message.js +588 -0
  233. package/services/client-chat/client-chat-wechat/client-chat-session-manage.js +67 -0
  234. package/services/client-chat/client-chat-wechat/client-chat-session.js +1048 -0
  235. package/services/client-chat/client-chat-wechat/client-chat-socket.js +215 -0
  236. package/services/client-chat/client-chat-wechat/client-chat.js +1065 -0
  237. package/services/client-chat/client-chat-wechat/index.js +3 -0
  238. package/services/client-chat/client-chat-wechat/utils.js +82 -0
  239. package/services/client-chat/client-chat-wework/client-chat-account.js +310 -0
  240. package/services/client-chat/client-chat-wework/client-chat-concats.js +772 -0
  241. package/services/client-chat/client-chat-wework/client-chat-message.js +278 -0
  242. package/services/client-chat/client-chat-wework/client-chat-session.js +929 -0
  243. package/services/client-chat/client-chat-wework/client-chat-socket.js +64 -0
  244. package/services/client-chat/client-chat-wework/client-chat.js +330 -0
  245. package/services/client-chat/client-chat-wework/index.js +3 -0
  246. package/services/client-chat/client-chat-wework/utils.js +82 -0
  247. package/services/index.js +10 -0
  248. package/services/socket/index.js +5 -0
  249. package/services/socket/socket.js +797 -0
  250. package/services/socket/utils.js +7 -0
  251. package/uni-common/account-portray/account-portray.vue +51 -0
  252. package/uni-common/account-select/account-select-item.vue +172 -0
  253. package/uni-common/account-select/account-select.vue +252 -0
  254. package/uni-common/all-friend-avatar/all-friend-avatar.vue +49 -0
  255. package/uni-common/friend-avatar/default.svg +1 -0
  256. package/uni-common/friend-avatar/friend-avatar.vue +27 -0
  257. package/uni-common/friend-avatar/index.js +3 -0
  258. package/uni-common/friend-loader/friend-loader.vue +53 -0
  259. package/uni-common/friend-portray/friend-portray.vue +56 -0
  260. package/uni-common/group-avatar/default.svg +1 -0
  261. package/uni-common/group-avatar/group-avatar.vue +18 -0
  262. package/uni-common/group-avatar/index.js +3 -0
  263. package/uni-common/room-avatar/default.svg +6 -0
  264. package/uni-common/room-avatar/index.js +3 -0
  265. package/uni-common/room-avatar/room-avatar.vue +27 -0
  266. package/uni-common/room-loader/room-loader.vue +53 -0
  267. package/uni-common/room-portray/room-portray.vue +56 -0
  268. package/uni-components/chat-control/calling/calling.vue +91 -0
  269. package/uni-components/chat-control/calling/phone-fill.svg +1 -0
  270. package/uni-components/chat-control/calling/vidicon-fill.svg +1 -0
  271. package/uni-components/chat-control/channel-video/channel-video.vue +89 -0
  272. package/uni-components/chat-control/channel-video-card/channel-video-card.vue +105 -0
  273. package/uni-components/chat-control/channel-video-live/channel-video-live.vue +92 -0
  274. package/uni-components/chat-control/chat-control-config-item/chat-control-config-item.vue +20 -0
  275. package/uni-components/chat-control/chat-control-config-item/index.js +3 -0
  276. package/uni-components/chat-control/chat-control-config-msg/chat-control-config-msg-app.vue +25 -0
  277. package/uni-components/chat-control/chat-control-config-msg/chat-control-config-msg-h5.vue +21 -0
  278. package/uni-components/chat-control/chat-control-config.vue +59 -0
  279. package/uni-components/chat-control/chat-control.vue +218 -0
  280. package/uni-components/chat-control/company-card/company-card.vue +123 -0
  281. package/uni-components/chat-control/const.js +28 -0
  282. package/uni-components/chat-control/expression/expression.vue +41 -0
  283. package/uni-components/chat-control/image/image.vue +68 -0
  284. package/uni-components/chat-control/index.js +89 -0
  285. package/uni-components/chat-control/location/location.vue +110 -0
  286. package/uni-components/chat-control/member-change/member-change.vue +32 -0
  287. package/uni-components/chat-control/quote-message/quote-message.vue +216 -0
  288. package/uni-components/chat-control/real-time-position/real-time-position.vue +62 -0
  289. package/uni-components/chat-control/redpacket/red-envelope.svg +1 -0
  290. package/uni-components/chat-control/redpacket/redpacket.vue +75 -0
  291. package/uni-components/chat-control/share-card/share-card.vue +101 -0
  292. package/uni-components/chat-control/system/system.vue +69 -0
  293. package/uni-components/chat-control/system-batch-message/system-batch-message.vue +30 -0
  294. package/uni-components/chat-control/system-text/system-text.vue +32 -0
  295. package/uni-components/chat-control/text/text.vue +67 -0
  296. package/uni-components/chat-control/text-with-at/text-with-at.vue +51 -0
  297. package/uni-components/chat-control/transfer/red-transfer.svg +1 -0
  298. package/uni-components/chat-control/transfer/transfer.vue +80 -0
  299. package/uni-components/chat-control/unkonw/unkonw.vue +37 -0
  300. package/uni-components/chat-control/url/icons/file-excel-line.svg +1 -0
  301. package/uni-components/chat-control/url/icons/file-info-line.svg +1 -0
  302. package/uni-components/chat-control/url/icons/file-music-line.svg +1 -0
  303. package/uni-components/chat-control/url/icons/file-pdf-line.svg +1 -0
  304. package/uni-components/chat-control/url/icons/file-ppt-line.svg +1 -0
  305. package/uni-components/chat-control/url/icons/file-settings-line.svg +1 -0
  306. package/uni-components/chat-control/url/icons/file-text-line.svg +1 -0
  307. package/uni-components/chat-control/url/icons/file-unknow-line.svg +1 -0
  308. package/uni-components/chat-control/url/icons/file-word-line.svg +1 -0
  309. package/uni-components/chat-control/url/icons/file-zip-line.svg +1 -0
  310. package/uni-components/chat-control/url/icons/index.js +24 -0
  311. package/uni-components/chat-control/url/miniprogram.svg +1 -0
  312. package/uni-components/chat-control/url/url-miniprogram.vue +116 -0
  313. package/uni-components/chat-control/url/url.vue +306 -0
  314. package/uni-components/chat-control/url-new/icons/file-excel-line.svg +1 -0
  315. package/uni-components/chat-control/url-new/icons/file-info-line.svg +1 -0
  316. package/uni-components/chat-control/url-new/icons/file-music-line.svg +1 -0
  317. package/uni-components/chat-control/url-new/icons/file-pdf-line.svg +1 -0
  318. package/uni-components/chat-control/url-new/icons/file-ppt-line.svg +1 -0
  319. package/uni-components/chat-control/url-new/icons/file-settings-line.svg +1 -0
  320. package/uni-components/chat-control/url-new/icons/file-text-line.svg +1 -0
  321. package/uni-components/chat-control/url-new/icons/file-unknow-line.svg +1 -0
  322. package/uni-components/chat-control/url-new/icons/file-word-line.svg +1 -0
  323. package/uni-components/chat-control/url-new/icons/file-zip-line.svg +1 -0
  324. package/uni-components/chat-control/url-new/icons/index.js +24 -0
  325. package/uni-components/chat-control/url-new/url-new.vue +110 -0
  326. package/uni-components/chat-control/utils.js +12 -0
  327. package/uni-components/chat-control/video/video.vue +156 -0
  328. package/uni-components/chat-control/voice/voice.vue +166 -0
  329. package/uni-components/chat-editor/chat-editor-chartlet/chat-editor-chartlet-collect.vue +84 -0
  330. package/uni-components/chat-editor/chat-editor-chartlet/chat-editor-chartlet-system.vue +28 -0
  331. package/uni-components/chat-editor/chat-editor-chartlet/chat-editor-chartlet.vue +85 -0
  332. package/uni-components/chat-editor/chat-editor-func/chat-editor-func.vue +157 -0
  333. package/uni-components/chat-editor/chat-editor-func-at/chat-editor-func-at-panel.vue +158 -0
  334. package/uni-components/chat-editor/chat-editor-func-at/chat-editor-func-at.vue +61 -0
  335. package/uni-components/chat-editor/chat-editor-func-button/chat-editor-func-button.vue +58 -0
  336. package/uni-components/chat-editor/chat-editor-func-file/chat-editor-func-file.vue +54 -0
  337. package/uni-components/chat-editor/chat-editor-func-image/chat-editor-func-image.vue +42 -0
  338. package/uni-components/chat-editor/chat-editor-func-video/chat-editor-func-video.vue +42 -0
  339. package/uni-components/chat-editor/chat-editor-func-voice/chat-editor-func-voice.vue +25 -0
  340. package/uni-components/chat-editor/chat-editor-message-send/chat-editor-message-send.vue +158 -0
  341. package/uni-components/chat-editor/chat-editor-quote/chat-editor-quote.vue +75 -0
  342. package/uni-components/chat-editor/chat-editor.vue +257 -0
  343. package/uni-components/chat-editor/index.js +10 -0
  344. package/uni-components/chat-editor/util.js +65 -0
  345. package/uni-components/chat-panel/chat-panel-chats-box.vue +238 -0
  346. package/uni-components/chat-panel/chat-panel-chats-forward/chat-panel-chats-forward.vue +273 -0
  347. package/uni-components/chat-panel/chat-panel-chats-item.vue +578 -0
  348. package/uni-components/chat-panel/chat-panel-chats-status/chat-panel-chats-status.vue +71 -0
  349. package/uni-components/chat-panel/chat-panel-chats.vue +368 -0
  350. package/uni-components/chat-panel/chat-panel.vue +227 -0
  351. package/uni-components/contacts-group-select/contacts-group-select-item.vue +85 -0
  352. package/uni-components/contacts-group-select/contacts-group-select-panel.vue +119 -0
  353. package/uni-components/contacts-group-select/contacts-group-select.vue +150 -0
  354. package/utils/color.js +178 -0
  355. package/utils/data.js +11 -0
  356. package/utils/date.js +107 -0
  357. package/utils/event.js +3 -0
  358. package/utils/file.js +26 -0
  359. package/utils/index.js +5 -0
  360. package/utils/json.js +40 -0
  361. package/utils/log.js +243 -0
  362. package/utils/queue.js +97 -0
  363. package/utils/regexp.js +9 -0
  364. package/utils/string.js +66 -0
  365. package/utils/url.js +48 -0
  366. package/utils/util.js +130 -0
@@ -0,0 +1,933 @@
1
+ <template>
2
+ <div class="sdk-chat-editor" :class="{'is-disabled': disabled}" @click.stop="handleClick">
3
+ <div class="sdk-chat-editor__toolbar is-top">
4
+ <div class="sdk-chat-editor__toolbar-left">
5
+ <slot name="toolbar-left" :input-message="inputValue" :insert-input-message="insertInputText" :set-input-message="setInputText"></slot>
6
+ </div>
7
+ <div class="sdk-chat-editor__toolbar-right">
8
+ <slot name="toolbar-right" :input-message="inputValue" :insert-input-message="insertInputText" :set-input-message="setInputText"></slot>
9
+ </div>
10
+ </div>
11
+ <div class="sdk-chat-editor__input" @click="handleInputClick">
12
+ <div
13
+ ref="input"
14
+ class="sdk-chat-editor__input-control"
15
+ :contenteditable="!disabled"
16
+ :aria-disabled="disabled ? 'true' : 'false'"
17
+ :data-maxlength="maxlength"
18
+ data-placeholder="请输入"
19
+ @beforeinput="handleBeforeInput"
20
+ @paste="handleInputPaste"
21
+ @keydown="handleInputKeydown"
22
+ @keyup="handleInputKeyup"
23
+ @input="handleInputValueChange"
24
+ @focus="handleInputFocus"
25
+ ></div>
26
+ </div>
27
+ <template v-if="$slots.addon">
28
+ <slot name="addon"></slot>
29
+ </template>
30
+ <div class="sdk-chat-editor__toolbar is-bottom">
31
+ <div class="sdk-chat-editor__toolbar-left"></div>
32
+ <div class="sdk-chat-editor__toolbar-right">
33
+ <div class="sdk-chat-editor__toolbar-item">
34
+ <span class="sdk-chat-editor__tips">按Ctrl+Enter换行</span>
35
+ </div>
36
+ <div class="sdk-chat-editor__toolbar-item">
37
+ <ui-button type="primary" :disabled="!hasInputMessage || disabled" :loading="sending" @click="handleSendMessageClick">发送</ui-button>
38
+ </div>
39
+ </div>
40
+ </div>
41
+ <div class="sdk-chat-editor__popover" v-show="popoverActive">
42
+ <chat-editor-suggestions
43
+ ref="suggestions"
44
+ :keyword="suggestionsKeyword"
45
+ :loading="suggestioning"
46
+ :data="suggestionsData"
47
+ :confirm-method="handleSuggestionsConfirmMethod"
48
+ @closed="handleSuggestionsClosed"
49
+ >
50
+ <template slot="item" slot-scope="scope">
51
+ <slot name="suggestions-item" v-bind="scope"></slot>
52
+ </template>
53
+ </chat-editor-suggestions>
54
+ <slot name="popover"></slot>
55
+ </div>
56
+ <slot></slot>
57
+ </div>
58
+ </template>
59
+ <script>
60
+ import { MESSAGE_TYPE } from '../wechat-chat-control/util'
61
+ import { Button as UiButton } from '@aochuang/common/components'
62
+ import ChatEditorSuggestions from './chat-editor-suggestions/chat-editor-suggestions.vue'
63
+ import { getNearestString } from './utils'
64
+ import { decodeHtmlEntities } from '@aochuang/common/utils/string'
65
+
66
+ export default {
67
+ name: 'SdkChatEditor',
68
+ components: {
69
+ UiButton,
70
+ ChatEditorSuggestions
71
+ },
72
+ props: {
73
+ disabled: {
74
+ type: Boolean
75
+ },
76
+ maxlength: {
77
+ type: Number,
78
+ default: null
79
+ },
80
+ fetchSuggestionsMethod: {
81
+ type: Function
82
+ },
83
+ confirmSuggestionsMethod: {
84
+ type: Function
85
+ },
86
+ sendMessageMethod: {
87
+ type: Function
88
+ }
89
+ },
90
+ data () {
91
+ return {
92
+ sending: false,
93
+ popoverActive: false,
94
+ suggestioning: false,
95
+ suggestionsData: [],
96
+ suggestionsKeyword: null,
97
+ inputValue: this.value || ''
98
+ }
99
+ },
100
+ computed: {
101
+ inputMessage () {
102
+ return this.inputValue || ''
103
+ },
104
+ hasInputMessage () {
105
+ return !!(this.inputValue || '').trim()
106
+ }
107
+ },
108
+ watch: {
109
+ value () {
110
+ this.setInputText(this.value || '')
111
+ }
112
+ },
113
+ mounted () {
114
+ this.setInputText(this.inputValue)
115
+ window.document.addEventListener('click', this.handleDocumentClick)
116
+ },
117
+ methods: {
118
+ handleDocumentClick () {
119
+ this.popoverActive = false
120
+ },
121
+ handleSuggestionsClosed () {
122
+ this.suggestionsData = []
123
+ },
124
+ handleSuggestionsConfirmMethod ({ item }) {
125
+ const next = () => {
126
+ if (!this.suggestionsNearsetInfo) {
127
+ this.closeSuggestionsAfterConfirm()
128
+ return
129
+ }
130
+ const { start, end } = this.suggestionsNearsetInfo
131
+ const content = decodeHtmlEntities(item.content || '')
132
+ this._skipNextSuggestionsLoad = true
133
+ this.replaceTextRange(start, end, content)
134
+ this.closeSuggestionsAfterConfirm()
135
+ }
136
+ if (this.confirmSuggestionsMethod) {
137
+ this.closeSuggestions()
138
+ this.confirmSuggestionsMethod({
139
+ item,
140
+ next
141
+ })
142
+ } else {
143
+ next()
144
+ }
145
+ },
146
+ handleInputClick () {
147
+ if (this.disabled) {
148
+ return
149
+ }
150
+ this.cacheInputRange = this.getSelectionTextRange()
151
+ },
152
+ handleBeforeInput (evt) {
153
+ if (this.disabled) {
154
+ evt.preventDefault()
155
+ return
156
+ }
157
+ if (evt.inputType === 'insertParagraph' || evt.inputType === 'insertLineBreak') {
158
+ evt.preventDefault()
159
+ this._pendingLineBreak = false
160
+ clearTimeout(this._pendingLineBreakTimer)
161
+ this.insertLineBreak()
162
+ return
163
+ }
164
+ if (!this.hasMaxlength()) {
165
+ return
166
+ }
167
+ const range = this.getSelectionTextRange()
168
+ const selectedLength = Math.max(0, range.end - range.start)
169
+ const inputLength = evt.data ? evt.data.length : 0
170
+ if (this.getInputText().length - selectedLength + inputLength > this.maxlength) {
171
+ evt.preventDefault()
172
+ }
173
+ },
174
+ handleInputKeydown (evt) {
175
+ if (this.disabled) {
176
+ evt.preventDefault()
177
+ return
178
+ }
179
+ if (evt.keyCode === 13) {
180
+ if (evt.ctrlKey || evt.metaKey) {
181
+ evt.preventDefault()
182
+ this._pendingLineBreak = true
183
+ clearTimeout(this._pendingLineBreakTimer)
184
+ this._pendingLineBreakTimer = setTimeout(() => {
185
+ if (this._pendingLineBreak) {
186
+ this.insertLineBreak()
187
+ }
188
+ this._pendingLineBreak = false
189
+ }, 0)
190
+ } else if (evt.shiftKey) {
191
+ evt.preventDefault()
192
+ } else {
193
+ evt.preventDefault()
194
+ this.submit()
195
+ }
196
+ }
197
+ },
198
+ handleInputKeyup () {
199
+ if (this.disabled) {
200
+ return
201
+ }
202
+ this.cacheInputRange = this.getSelectionTextRange()
203
+ },
204
+ handleInputPaste (evt) {
205
+ if (this.disabled) {
206
+ evt.preventDefault()
207
+ return
208
+ }
209
+ this.$emit('paste', evt)
210
+ if (evt.defaultPrevented) {
211
+ this.$nextTick(() => {
212
+ this.handleInputValueChange()
213
+ })
214
+ return
215
+ }
216
+ evt.preventDefault()
217
+ const clipboardData = evt.clipboardData || window.clipboardData || {}
218
+ const html = clipboardData.getData ? clipboardData.getData('text/html') : ''
219
+ const text = clipboardData.getData ? clipboardData.getData('text/plain') : ''
220
+ // 不允许把任意 HTML 直接粘贴进 contenteditable,只恢复本组件自己的
221
+ // mention token,其他标记全部降级成纯文本。
222
+ this.insertFragmentAtSelection(this.createSafePasteFragment(html, text))
223
+ this.handleInputValueChange()
224
+ },
225
+ delayLoadSuggestions (inputValue) {
226
+ clearTimeout(this._loadSuggestionsTimer)
227
+ this._loadSuggestionsTimer = setTimeout(() => {
228
+ const cursorPos = this.cacheInputRange ? this.cacheInputRange.start : 0
229
+ const nearsetInfo = getNearestString(inputValue, cursorPos)
230
+ this.suggestionsNearsetInfo = nearsetInfo
231
+ const nearest = (nearsetInfo.nearest || '').trim()
232
+ if (!nearest) {
233
+ this.closeSuggestions()
234
+ }
235
+ this.loadSuggestions(nearest)
236
+ }, 200)
237
+ },
238
+ loadSuggestions (keyword) {
239
+ if (!this.fetchSuggestionsMethod) {
240
+ return
241
+ }
242
+ keyword = keyword.trim()
243
+ this.suggestioning = true
244
+ this.suggestionsKeyword = keyword
245
+ clearTimeout(this._loadSuggestionsTimer)
246
+ return this.fetchSuggestionsMethod({
247
+ keyword
248
+ }).then(rs => {
249
+ this.suggestionsData = rs || []
250
+ }).finally(() => {
251
+ this.suggestioning = false
252
+ })
253
+ },
254
+ closeSuggestions () {
255
+ this.$refs.suggestions && this.$refs.suggestions.close()
256
+ },
257
+ closeSuggestionsAfterConfirm () {
258
+ this.closeSuggestions()
259
+ this.suggestionsNearsetInfo = null
260
+ this.suggestionsData = []
261
+ this.suggestionsKeyword = null
262
+ this.popoverActive = false
263
+ },
264
+ hasMaxlength () {
265
+ return typeof this.maxlength === 'number' && this.maxlength >= 0
266
+ },
267
+ getRemainingInputLength () {
268
+ if (!this.hasMaxlength()) {
269
+ return Infinity
270
+ }
271
+ const range = this.getSelectionTextRange()
272
+ const selectedLength = Math.max(0, range.end - range.start)
273
+ return Math.max(0, this.maxlength - (this.getInputText().length - selectedLength))
274
+ },
275
+ trimTextToRemainingLength (text) {
276
+ const remaining = this.getRemainingInputLength()
277
+ if (remaining === Infinity) {
278
+ return text
279
+ }
280
+ return String(text || '').slice(0, remaining)
281
+ },
282
+ setInputText (str) {
283
+ const input = this.$refs.input
284
+ this.inputValue = this.hasMaxlength() ? String(str || '').slice(0, this.maxlength) : (str || '')
285
+ this.cacheInputRange = null
286
+ this.suggestionsNearsetInfo = null
287
+ if (input) {
288
+ input.textContent = this.inputValue
289
+ }
290
+ },
291
+ setInputTextWithMentions (str, mentions) {
292
+ const input = this.$refs.input
293
+ const text = this.hasMaxlength() ? String(str || '').slice(0, this.maxlength) : String(str || '')
294
+ if (!input || !mentions || !mentions.length) {
295
+ this.setInputText(text)
296
+ return
297
+ }
298
+ const fragment = document.createDocumentFragment()
299
+ let cursor = 0
300
+ // 草稿恢复优先使用保存时的文本位置,避免同名 @ 成员恢复成普通文本
301
+ // 或恢复成错误成员;老数据没有位置时会退化成按文本顺序匹配。
302
+ const normalizedMentions = this.normalizeMentionsForText(text, mentions)
303
+ normalizedMentions.forEach(mention => {
304
+ if (mention.startIndex < cursor || mention.startIndex > text.length) {
305
+ return
306
+ }
307
+ if (mention.startIndex > cursor) {
308
+ fragment.appendChild(document.createTextNode(text.slice(cursor, mention.startIndex)))
309
+ }
310
+ const mentionNode = this.createMentionNode(mention)
311
+ if (mentionNode) {
312
+ fragment.appendChild(mentionNode)
313
+ } else {
314
+ fragment.appendChild(document.createTextNode(text.slice(mention.startIndex, mention.endIndex)))
315
+ }
316
+ cursor = mention.endIndex
317
+ })
318
+ if (cursor < text.length) {
319
+ fragment.appendChild(document.createTextNode(text.slice(cursor)))
320
+ }
321
+ input.innerHTML = ''
322
+ input.appendChild(fragment)
323
+ this.inputValue = this.getInputText()
324
+ this.cacheInputRange = null
325
+ this.suggestionsNearsetInfo = null
326
+ this.setCursorByTextOffset(this.inputValue.length)
327
+ },
328
+ normalizeMentionsForText (text, mentions) {
329
+ let cursor = 0
330
+ return (mentions || []).map(mention => {
331
+ const mentionText = mention.text || this.getMentionText(mention)
332
+ let startIndex = typeof mention.startIndex === 'number' ? mention.startIndex : -1
333
+ let endIndex = typeof mention.endIndex === 'number' ? mention.endIndex : -1
334
+ if (startIndex < 0 || endIndex <= startIndex || text.slice(startIndex, endIndex) !== mentionText) {
335
+ startIndex = text.indexOf(mentionText, cursor)
336
+ endIndex = startIndex >= 0 ? startIndex + mentionText.length : -1
337
+ }
338
+ cursor = endIndex > cursor ? endIndex : cursor
339
+ return Object.assign({}, mention, {
340
+ text: mentionText,
341
+ startIndex,
342
+ endIndex
343
+ })
344
+ }).filter(mention => {
345
+ return mention.startIndex >= 0 && mention.endIndex > mention.startIndex && mention.endIndex <= text.length
346
+ }).sort((a, b) => a.startIndex - b.startIndex)
347
+ },
348
+ getInputText () {
349
+ return this.getNodeText(this.$refs.input)
350
+ },
351
+ getMentions () {
352
+ const input = this.$refs.input
353
+ if (!input) {
354
+ return []
355
+ }
356
+ // mention 的真实身份来自 DOM token,而不是显示文本。这样同名昵称
357
+ // 在编辑后仍然可以区分。
358
+ return Array.prototype.slice.call(input.querySelectorAll('[data-mention-id]')).map(node => {
359
+ const index = Array.prototype.indexOf.call(node.parentNode.childNodes, node)
360
+ const startIndex = this.getTextOffsetByDomPoint(node.parentNode, index)
361
+ return {
362
+ id: node.dataset.mentionId,
363
+ nickname: node.dataset.mentionNickname,
364
+ text: node.textContent || '',
365
+ startIndex,
366
+ endIndex: startIndex + (node.textContent || '').length
367
+ }
368
+ })
369
+ },
370
+ clearMentions () {
371
+ const input = this.$refs.input
372
+ if (!input) {
373
+ return
374
+ }
375
+ Array.prototype.slice.call(input.querySelectorAll('[data-mention-id]')).forEach(node => {
376
+ node.parentNode && node.parentNode.removeChild(node)
377
+ })
378
+ this.handleInputValueChange()
379
+ },
380
+ getMentionText (mention) {
381
+ const nickname = mention && (mention.nickname || mention.name || mention.label)
382
+ return nickname ? `@${nickname}` : ''
383
+ },
384
+ createMentionNode (mention) {
385
+ const text = this.getMentionText(mention)
386
+ if (!mention || !mention.id || !text) {
387
+ return null
388
+ }
389
+ // 让 token 保持原子性,浏览器删除/复制时会把一个 @ 成员当成整体,
390
+ // 同时用 data-* 保存真实成员身份。
391
+ const node = document.createElement('span')
392
+ node.className = 'sdk-chat-editor__mention'
393
+ node.contentEditable = 'false'
394
+ node.dataset.mentionId = String(mention.id)
395
+ node.dataset.mentionNickname = mention.nickname || mention.name || mention.label
396
+ node.textContent = text
397
+ return node
398
+ },
399
+ getSafeRange () {
400
+ const input = this.$refs.input
401
+ const selection = window.getSelection()
402
+ if (!input || !selection || !selection.rangeCount) {
403
+ return null
404
+ }
405
+ const range = selection.getRangeAt(0)
406
+ if (!input.contains(range.commonAncestorContainer)) {
407
+ return null
408
+ }
409
+ return range
410
+ },
411
+ focus () {
412
+ if (!this.$refs.input || this.disabled) {
413
+ return
414
+ }
415
+ this.$refs.input.focus()
416
+ if (!this.getSafeRange()) {
417
+ this.setCursorByTextOffset(this.getInputText().length)
418
+ }
419
+ },
420
+ setCursorAfterNode (node) {
421
+ if (!node) {
422
+ return
423
+ }
424
+ const range = document.createRange()
425
+ const selection = window.getSelection()
426
+ range.setStartAfter(node)
427
+ range.collapse(true)
428
+ selection.removeAllRanges()
429
+ selection.addRange(range)
430
+ this.cacheInputRange = this.getSelectionTextRange()
431
+ },
432
+ setCursorByTextOffset (offset) {
433
+ const point = this.getDomPointByTextOffset(offset)
434
+ if (!point) {
435
+ return
436
+ }
437
+ const range = document.createRange()
438
+ const selection = window.getSelection()
439
+ range.setStart(point.node, point.offset)
440
+ range.collapse(true)
441
+ selection.removeAllRanges()
442
+ selection.addRange(range)
443
+ this.cacheInputRange = {
444
+ start: offset,
445
+ end: offset
446
+ }
447
+ },
448
+ setSelectionByTextRange (start, end) {
449
+ const startPoint = this.getDomPointByTextOffset(start)
450
+ const endPoint = this.getDomPointByTextOffset(end)
451
+ if (!startPoint || !endPoint) {
452
+ return false
453
+ }
454
+ const range = document.createRange()
455
+ const selection = window.getSelection()
456
+ range.setStart(startPoint.node, startPoint.offset)
457
+ range.setEnd(endPoint.node, endPoint.offset)
458
+ selection.removeAllRanges()
459
+ selection.addRange(range)
460
+ this.cacheInputRange = {
461
+ start,
462
+ end
463
+ }
464
+ return true
465
+ },
466
+ getDomPointByTextOffset (offset) {
467
+ // 对外暴露的编辑器 API 仍然使用纯文本偏移量,这里把偏移量转换成
468
+ // DOM 位置,并把 mention span 当成不可拆分的文本处理。
469
+ const input = this.$refs.input
470
+ if (!input) {
471
+ return null
472
+ }
473
+ let current = 0
474
+ let fallback = {
475
+ node: input,
476
+ offset: input.childNodes.length
477
+ }
478
+ const visit = (node) => {
479
+ if (node.nodeType === Node.TEXT_NODE) {
480
+ const length = node.nodeValue.length
481
+ if (offset <= current + length) {
482
+ return {
483
+ node,
484
+ offset: Math.max(0, offset - current)
485
+ }
486
+ }
487
+ current += length
488
+ fallback = {
489
+ node,
490
+ offset: length
491
+ }
492
+ return null
493
+ }
494
+ if (node.nodeType !== Node.ELEMENT_NODE) {
495
+ return null
496
+ }
497
+ if (node.dataset && node.dataset.mentionId) {
498
+ const length = node.textContent.length
499
+ if (offset <= current) {
500
+ return {
501
+ node: node.parentNode,
502
+ offset: Array.prototype.indexOf.call(node.parentNode.childNodes, node)
503
+ }
504
+ }
505
+ if (offset <= current + length) {
506
+ return {
507
+ node: node.parentNode,
508
+ offset: Array.prototype.indexOf.call(node.parentNode.childNodes, node) + 1
509
+ }
510
+ }
511
+ current += length
512
+ fallback = {
513
+ node: node.parentNode,
514
+ offset: Array.prototype.indexOf.call(node.parentNode.childNodes, node) + 1
515
+ }
516
+ return null
517
+ }
518
+ for (let i = 0; i < node.childNodes.length; i++) {
519
+ const child = node.childNodes[i]
520
+ if (i > 0 && this.isEditorBlockBreakNode(child)) {
521
+ if (offset <= current + 1) {
522
+ return {
523
+ node,
524
+ offset: i
525
+ }
526
+ }
527
+ current += 1
528
+ }
529
+ const point = visit(child)
530
+ if (point) {
531
+ return point
532
+ }
533
+ }
534
+ return null
535
+ }
536
+ return visit(input) || fallback
537
+ },
538
+ getTextOffsetByDomPoint (targetNode, targetOffset) {
539
+ // 把浏览器的选区位置转换回纯文本坐标,供联想查询和旧的插入 API 使用。
540
+ const input = this.$refs.input
541
+ let offset = 0
542
+ let found = false
543
+ const visit = (node) => {
544
+ if (found) {
545
+ return
546
+ }
547
+ if (node === targetNode) {
548
+ if (node.nodeType === Node.TEXT_NODE) {
549
+ offset += targetOffset
550
+ } else {
551
+ for (let i = 0; i < targetOffset; i++) {
552
+ const child = node.childNodes[i]
553
+ if (i > 0 && this.isEditorBlockBreakNode(child)) {
554
+ offset += 1
555
+ }
556
+ offset += this.getNodeText(child).length
557
+ }
558
+ }
559
+ found = true
560
+ return
561
+ }
562
+ if (node.nodeType === Node.TEXT_NODE) {
563
+ offset += node.nodeValue.length
564
+ return
565
+ }
566
+ if (node.nodeType === Node.ELEMENT_NODE && node.dataset && node.dataset.mentionId) {
567
+ offset += node.textContent.length
568
+ return
569
+ }
570
+ Array.prototype.slice.call(node.childNodes || []).forEach((child, index) => {
571
+ if (found) {
572
+ return
573
+ }
574
+ if (index > 0 && this.isEditorBlockBreakNode(child)) {
575
+ offset += 1
576
+ }
577
+ visit(child)
578
+ })
579
+ }
580
+ visit(input)
581
+ return offset
582
+ },
583
+ getSelectionTextRange () {
584
+ const range = this.getSafeRange()
585
+ if (!range) {
586
+ const length = this.getInputText().length
587
+ return {
588
+ start: length,
589
+ end: length
590
+ }
591
+ }
592
+ return {
593
+ start: this.getTextOffsetByDomPoint(range.startContainer, range.startOffset),
594
+ end: this.getTextOffsetByDomPoint(range.endContainer, range.endOffset)
595
+ }
596
+ },
597
+ getNodeText (node) {
598
+ // 这里不要用 innerText,因为它会受布局影响,还可能额外插入换行;
599
+ // 显式遍历节点可以得到更稳定的消息内容。
600
+ if (!node) {
601
+ return ''
602
+ }
603
+ if (node.nodeType === Node.TEXT_NODE) {
604
+ return (node.nodeValue || '').replace(/\u200B/g, '')
605
+ }
606
+ if (node.nodeType === Node.ELEMENT_NODE && node.tagName === 'BR') {
607
+ return node.parentNode && this.isEditorBlockBreakNode(node.parentNode) ? '' : '\n'
608
+ }
609
+ if (node.nodeType === Node.ELEMENT_NODE && node.dataset && node.dataset.mentionId) {
610
+ return node.textContent || ''
611
+ }
612
+ const children = Array.prototype.slice.call(node.childNodes || [])
613
+ let text = ''
614
+ children.forEach((child, index) => {
615
+ if (index > 0 && this.isEditorBlockBreakNode(child) && !text.endsWith('\n')) {
616
+ text += '\n'
617
+ }
618
+ text += this.getNodeText(child)
619
+ })
620
+ return text
621
+ },
622
+ isEditorBlockBreakNode (node) {
623
+ if (!node || node.nodeType !== Node.ELEMENT_NODE) {
624
+ return false
625
+ }
626
+ return ['DIV', 'P', 'LI'].includes(node.tagName)
627
+ },
628
+ insertNodesAtSelection (nodes) {
629
+ this.focus()
630
+ let range = this.getSafeRange()
631
+ if (!range) {
632
+ this.setCursorByTextOffset(this.getInputText().length)
633
+ range = this.getSafeRange()
634
+ }
635
+ if (!range) {
636
+ return
637
+ }
638
+ range.deleteContents()
639
+ let lastNode = null
640
+ nodes.forEach(node => {
641
+ range.insertNode(node)
642
+ range.setStartAfter(node)
643
+ lastNode = node
644
+ })
645
+ if (lastNode) {
646
+ this.setCursorAfterNode(lastNode)
647
+ }
648
+ },
649
+ insertFragmentAtSelection (fragment) {
650
+ this.insertNodesAtSelection(Array.prototype.slice.call(fragment.childNodes))
651
+ },
652
+ replaceTextRange (start, end, text) {
653
+ if (!this.setSelectionByTextRange(start, end)) {
654
+ return
655
+ }
656
+ this.insertInputText(text)
657
+ },
658
+ getSuggestionsReplaceRange () {
659
+ // @ 成员联想需要从最近的 @ 开始替换,普通快捷语只替换最近匹配词。
660
+ if (!this.suggestionsNearsetInfo) {
661
+ const range = this.cacheInputRange || this.getSelectionTextRange()
662
+ return {
663
+ start: range.start,
664
+ end: range.end
665
+ }
666
+ }
667
+ const { start, end, nearest, isAtContext } = this.suggestionsNearsetInfo
668
+ if (!isAtContext || nearest === '@') {
669
+ return {
670
+ start,
671
+ end
672
+ }
673
+ }
674
+ const inputValue = this.inputValue || ''
675
+ let atPos = start
676
+ for (let i = start - 1; i >= 0; i--) {
677
+ if (inputValue[i] === '@') {
678
+ atPos = i
679
+ break
680
+ }
681
+ }
682
+ return {
683
+ start: atPos,
684
+ end
685
+ }
686
+ },
687
+ insertInputText (str) {
688
+ if (this.disabled) {
689
+ return
690
+ }
691
+ const text = this.trimTextToRemainingLength(str || '')
692
+ if (!text) {
693
+ return
694
+ }
695
+ this.insertNodesAtSelection([document.createTextNode(text)])
696
+ this.handleInputValueChange()
697
+ },
698
+ insertLineBreak () {
699
+ if (this.disabled || this.getRemainingInputLength() <= 0) {
700
+ return
701
+ }
702
+ this.focus()
703
+ // contenteditable 在行尾插入纯文本 \n 时,DOM 值已经变化,
704
+ // 但部分浏览器不会立即把空行渲染出来;补一个零宽字符撑开光标行,
705
+ // getNodeText 会过滤它,发送内容仍然只保留真实换行。
706
+ const text = this.getRemainingInputLength() > 1 ? '\n\u200B' : '\n'
707
+ this.insertInputText(text)
708
+ },
709
+ insertMention (mention) {
710
+ if (this.disabled) {
711
+ return
712
+ }
713
+ const mentionNode = this.createMentionNode(mention)
714
+ if (!mentionNode) {
715
+ return
716
+ }
717
+ if (this.getRemainingInputLength() < ((mentionNode.textContent || '').length + 1)) {
718
+ return
719
+ }
720
+ const replaceRange = this.getSuggestionsReplaceRange()
721
+ this.setSelectionByTextRange(replaceRange.start, replaceRange.end)
722
+ this.insertNodesAtSelection([
723
+ mentionNode,
724
+ document.createTextNode(' ')
725
+ ])
726
+ // 选择 @ 成员后插入 token 会触发一次 input/change,这次变更只用于
727
+ // 同步内容,不应该立刻再次按当前 @ 文本打开成员浮层。
728
+ this._skipNextSuggestionsLoad = true
729
+ this.suggestionsNearsetInfo = null
730
+ this.handleInputValueChange()
731
+ },
732
+ insertSuggestionsText (str) {
733
+ if (this.disabled) {
734
+ return
735
+ }
736
+ const replaceRange = this.getSuggestionsReplaceRange()
737
+ this.setSelectionByTextRange(replaceRange.start, replaceRange.end)
738
+ this.insertInputText(decodeHtmlEntities(str || ''))
739
+ this.suggestionsNearsetInfo = null
740
+ },
741
+ createSafePasteFragment (html, text) {
742
+ const fragment = document.createDocumentFragment()
743
+ if (html && html.indexOf('data-mention-id') >= 0) {
744
+ // 这里恢复的是本编辑器自己复制出来的 token,但不能直接信任剪贴板
745
+ // HTML,所以要重新构建 span。
746
+ const doc = new DOMParser().parseFromString(html, 'text/html')
747
+ this.appendSafePastedNodes(fragment, doc.body)
748
+ if (fragment.childNodes.length) {
749
+ return fragment
750
+ }
751
+ }
752
+ fragment.appendChild(document.createTextNode(this.trimTextToRemainingLength(text || '')))
753
+ return fragment
754
+ },
755
+ appendSafePastedNodes (fragment, node) {
756
+ Array.prototype.slice.call(node.childNodes || []).forEach(child => {
757
+ if (child.nodeType === Node.TEXT_NODE) {
758
+ fragment.appendChild(document.createTextNode(child.nodeValue || ''))
759
+ return
760
+ }
761
+ if (child.nodeType !== Node.ELEMENT_NODE) {
762
+ return
763
+ }
764
+ if (child.dataset && child.dataset.mentionId) {
765
+ const nickname = child.dataset.mentionNickname || (child.textContent || '').replace(/^@/, '')
766
+ const mentionNode = this.createMentionNode({
767
+ id: child.dataset.mentionId,
768
+ nickname
769
+ })
770
+ if (mentionNode) {
771
+ fragment.appendChild(mentionNode)
772
+ }
773
+ return
774
+ }
775
+ if (['BR', 'DIV', 'P'].includes(child.tagName) && fragment.childNodes.length) {
776
+ fragment.appendChild(document.createTextNode('\n'))
777
+ }
778
+ this.appendSafePastedNodes(fragment, child)
779
+ })
780
+ },
781
+ handleInputValueChange () {
782
+ if (this.disabled) {
783
+ return
784
+ }
785
+ this.inputValue = this.getInputText()
786
+ if (this.hasMaxlength() && this.inputValue.length > this.maxlength) {
787
+ this.setInputText(this.inputValue.slice(0, this.maxlength))
788
+ this.setCursorByTextOffset(this.inputValue.length)
789
+ }
790
+ this.cacheInputRange = this.getSelectionTextRange()
791
+ if (this._skipNextSuggestionsLoad) {
792
+ this._skipNextSuggestionsLoad = false
793
+ } else {
794
+ this.popoverActive = true
795
+ this.delayLoadSuggestions(this.inputValue)
796
+ }
797
+ this.$emit('change', {
798
+ value: this.inputValue,
799
+ mentions: this.getMentions()
800
+ })
801
+ },
802
+ handleSendMessageClick () {
803
+ this.submit()
804
+ },
805
+ handleClick () {
806
+ if (this.disabled) {
807
+ return
808
+ }
809
+ this.popoverActive = true
810
+ this.focus()
811
+ },
812
+ handleInputFocus () {
813
+ if (this.disabled) {
814
+ return
815
+ }
816
+ this.popoverActive = true
817
+ this.focus()
818
+ },
819
+ clearInput () {
820
+ if (this.$refs.input) {
821
+ this.$refs.input.innerHTML = ''
822
+ }
823
+ this.inputValue = ''
824
+ this.cacheInputRange = null
825
+ this.suggestionsNearsetInfo = null
826
+ },
827
+ submit () {
828
+ if (!this.sendMessageMethod) {
829
+ throw new Error('缺少sendMessageMethod配置')
830
+ }
831
+ const content = this.getInputText()
832
+ if (!(content || '').trim()) {
833
+ return
834
+ }
835
+ const rs = this.sendMessageMethod({
836
+ data: {
837
+ content,
838
+ mentions: this.getMentions(),
839
+ msgType: MESSAGE_TYPE.TEXT
840
+ }
841
+ })
842
+ if (rs && rs.then) {
843
+ this.sending = true
844
+ rs.then(() => {
845
+ this.clearInput()
846
+ this.closeSuggestions()
847
+ }).finally(() => {
848
+ this.sending = false
849
+ })
850
+ } else {
851
+ this.clearInput()
852
+ this.closeSuggestions()
853
+ }
854
+ }
855
+ },
856
+ beforeDestroy () {
857
+ clearTimeout(this._loadSuggestionsTimer)
858
+ clearTimeout(this._pendingLineBreakTimer)
859
+ window.document.removeEventListener('click', this.handleDocumentClick)
860
+ }
861
+ }
862
+ </script>
863
+ <style>
864
+ .sdk-chat-editor{
865
+ height: 100%;
866
+ display: flex;
867
+ flex-direction: column;
868
+ position: relative;
869
+ }
870
+ .sdk-chat-editor.is-disabled .sdk-chat-editor__input{
871
+ position: relative;
872
+ }
873
+ .sdk-chat-editor.is-disabled .sdk-chat-editor__input:after{
874
+ content: '';
875
+ position: absolute;
876
+ left: 0;
877
+ right: 0;
878
+ bottom: 0;
879
+ top: 0;
880
+ background-color: #fff;
881
+ z-index: 3;
882
+ }
883
+ .sdk-chat-editor__toolbar{
884
+ padding: 12px;
885
+ display: flex;
886
+ flex-direction: row;
887
+ align-items: center;
888
+ justify-content: space-between;
889
+ }
890
+ .sdk-chat-editor__toolbar-right{
891
+ display: flex;
892
+ flex-direction: row;
893
+ align-items: center;
894
+ }
895
+ .sdk-chat-editor__toolbar-item:not(:last-child){
896
+ margin-right: 8px;
897
+ }
898
+ .sdk-chat-editor__input{
899
+ flex: 1;
900
+ min-height: 0;
901
+ }
902
+ .sdk-chat-editor__input-control{
903
+ box-sizing: border-box;
904
+ height: 100%;
905
+ outline: none;
906
+ overflow: auto;
907
+ padding: 5px 15px;
908
+ white-space: pre-wrap;
909
+ word-break: break-word;
910
+ }
911
+ .sdk-chat-editor__input-control:empty:before{
912
+ color: #c0c4cc;
913
+ content: attr(data-placeholder);
914
+ pointer-events: none;
915
+ }
916
+ .sdk-chat-editor__mention{
917
+ display: inline-block;
918
+ user-select: all;
919
+ }
920
+ .sdk-chat-editor__tips{
921
+ color: #999;
922
+ font-size: 12px;
923
+ }
924
+ .sdk-chat-editor__popover{
925
+ position: absolute;
926
+ height: 280px;
927
+ left: 0;
928
+ right: 0;
929
+ top: -280px;
930
+ pointer-events: none;
931
+ z-index: 100;
932
+ }
933
+ </style>