group-chat.js 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105
  1. /* ============================================================
  2. * lib/components/group-chat.js · 仿企业微信电脑端群聊窗
  3. * 用法:
  4. * <link rel="stylesheet" href=".../lib/components/group-chat.css">
  5. * <group-chat data-chat='{...}'></group-chat>
  6. * JSON:{ group, subtitle, members:[{name,avatar,role}],
  7. * msgs:[{name,avatar,type:'text'|'card', text, at:[names], title, sub, tag}], footer }
  8. * 消息文本内 @成员名 自动高亮为提及标签。
  9. * 颜色全部来自 tokens.css 的 --chat-* / --brand-*(双主题自适应)。
  10. * ============================================================ */
  11. (function () {
  12. function esc(s) {
  13. return String(s == null ? '' : s)
  14. .replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
  15. }
  16. function nl(s) {
  17. return esc(s).replace(/\n/g, '<br>');
  18. }
  19. function parse(el) {
  20. if (el.dataset.chat) { try { return JSON.parse(el.dataset.chat); } catch (e) { return null; } }
  21. if (el.dataset.ref) {
  22. var src = document.querySelector(el.dataset.ref);
  23. if (src) { try { return JSON.parse(src.textContent); } catch (e) { return null; } }
  24. }
  25. return null;
  26. }
  27. // 把文本里的 @成员名 渲染成高亮提及
  28. function mention(text, members) {
  29. var s = esc(text);
  30. members.forEach(function (m) {
  31. var name = esc(m.name);
  32. if (!name) return;
  33. var re = new RegExp('(@' + name.replace(/[.*+?^${}()|[\]\\]/g, '\\$&') + ')(?![^<]*>)', 'g');
  34. s = s.replace(re, '<span class="gc-at">$1</span>');
  35. });
  36. return s;
  37. }
  38. function renderMsg(m, members) {
  39. var text = '';
  40. if (m.type === 'card') {
  41. text = '<div class="gc-card">'
  42. + (m.title ? '<div class="gc-card-t">' + esc(m.title) + '</div>' : '')
  43. + (m.sub ? '<div class="gc-card-s">' + esc(m.sub) + '</div>' : '')
  44. + (m.tag ? '<div class="gc-card-g">' + esc(m.tag) + '</div>' : '')
  45. + '</div>';
  46. } else {
  47. text = '<div class="gc-text">' + mention(m.text, members) + '</div>';
  48. }
  49. return '<div class="gc-msg">'
  50. + '<span class="gc-ava">' + esc(m.avatar || (m.name || '?').slice(0, 1)) + '</span>'
  51. + '<div class="gc-box">'
  52. + '<div class="gc-name">' + esc(m.name || '') + '</div>'
  53. + text
  54. + '</div>'
  55. + '</div>';
  56. }
  57. function render(el, d) {
  58. if (!d) { el.innerHTML = '<div class="gc gc-empty">数据缺失</div>'; return; }
  59. var members = d.members || [];
  60. var memHTML = members.map(function (m) {
  61. return '<div class="gc-mem">'
  62. + '<span class="gc-ava">' + esc(m.avatar || (m.name || '?').slice(0, 1)) + '</span>'
  63. + '<div class="gc-mem-info">'
  64. + '<div class="gc-mem-name">' + esc(m.name || '') + '</div>'
  65. + (m.role ? '<div class="gc-mem-role">' + esc(m.role) + '</div>' : '')
  66. + '</div>'
  67. + '</div>';
  68. }).join('');
  69. var msgs = (d.msgs || []).map(function (m) { return renderMsg(m, members); }).join('');
  70. el.innerHTML =
  71. '<div class="gc">' +
  72. '<div class="gc-bar">' + esc(d.group || '') +
  73. (d.subtitle ? '<span class="gc-sub">' + esc(d.subtitle) + '</span>' : '') +
  74. '</div>' +
  75. '<div class="gc-main">' +
  76. '<div class="gc-members">' + memHTML + '</div>' +
  77. '<div class="gc-msgs">' + msgs + '</div>' +
  78. '</div>' +
  79. (d.footer ? '<div class="gc-foot">' + esc(d.footer) + '</div>' : '') +
  80. '</div>';
  81. }
  82. var GroupChat = function () {
  83. return Reflect.construct(HTMLElement, [], GroupChat);
  84. };
  85. GroupChat.prototype = Object.create(HTMLElement.prototype);
  86. GroupChat.prototype.constructor = GroupChat;
  87. GroupChat.prototype.connectedCallback = function () {
  88. var self = this;
  89. var d = parse(this);
  90. if (!d && this.dataset.ref) {
  91. setTimeout(function () { render(self, parse(self)); }, 0);
  92. } else {
  93. render(this, d);
  94. }
  95. };
  96. if (!customElements.get('group-chat')) {
  97. customElements.define('group-chat', GroupChat);
  98. }
  99. })();