copy-buttons.js 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176
  1. // 复制按钮功能
  2. window.setupCopyButtons = function () {
  3. // 复制图标SVG
  4. const copySvg = `<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
  5. <rect x="9" y="9" width="13" height="13" rx="2" ry="2"></rect>
  6. <path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"></path>
  7. </svg>`;
  8. // 为所有消息添加复制按钮
  9. function addCopyButtonsToMessages () {
  10. const messages = document.querySelectorAll('.message');
  11. messages.forEach(message => {
  12. // 如果消息已经有复制按钮,则跳过
  13. if (message.querySelector('.copy-button')) return;
  14. const messageContent = message.querySelector('.message-content');
  15. if (!messageContent) return;
  16. // 创建复制按钮
  17. const copyButton = document.createElement('button');
  18. copyButton.className = 'copy-button';
  19. copyButton.innerHTML = copySvg;
  20. copyButton.setAttribute('aria-label', '复制内容');
  21. copyButton.title = '复制消息内容';
  22. // 添加点击事件
  23. copyButton.addEventListener('click', (e) => {
  24. e.stopPropagation(); // 阻止事件冒泡
  25. copyMessageContent(messageContent);
  26. });
  27. // 添加到消息元素中,与message-content同级
  28. message.appendChild(copyButton);
  29. });
  30. }
  31. // 复制消息内容
  32. function copyMessageContent (messageContent) {
  33. try {
  34. // 获取纯文本内容
  35. const textContent = messageContent.textContent;
  36. // 检查是否支持现代剪贴板API
  37. if (navigator.clipboard && navigator.clipboard.writeText) {
  38. // 使用现代剪贴板API
  39. navigator.clipboard.writeText(textContent)
  40. .then(() => {
  41. showNotification('复制成功!', 'success');
  42. })
  43. .catch(err => {
  44. console.error('现代剪贴板API复制失败:', err);
  45. // 回退到传统方法
  46. fallbackCopyTextToClipboard(textContent);
  47. });
  48. } else {
  49. // 回退到传统方法
  50. fallbackCopyTextToClipboard(textContent);
  51. }
  52. } catch (err) {
  53. console.error('复制过程中出错:', err);
  54. // 回退到传统方法
  55. fallbackCopyTextToClipboard(messageContent.textContent);
  56. }
  57. }
  58. // 传统复制方法(兼容性更好)
  59. function fallbackCopyTextToClipboard (text) {
  60. try {
  61. // 创建临时文本区域
  62. const textArea = document.createElement('textarea');
  63. textArea.value = text;
  64. // 设置样式使其不可见
  65. textArea.style.position = 'fixed';
  66. textArea.style.top = '0';
  67. textArea.style.left = '0';
  68. textArea.style.width = '2em';
  69. textArea.style.height = '2em';
  70. textArea.style.padding = '0';
  71. textArea.style.border = 'none';
  72. textArea.style.outline = 'none';
  73. textArea.style.boxShadow = 'none';
  74. textArea.style.background = 'transparent';
  75. textArea.style.opacity = '0';
  76. textArea.style.zIndex = '-1';
  77. // 添加到页面
  78. document.body.appendChild(textArea);
  79. // 选中文本
  80. textArea.focus();
  81. textArea.select();
  82. // 尝试复制
  83. const successful = document.execCommand('copy');
  84. // 移除临时元素
  85. document.body.removeChild(textArea);
  86. if (successful) {
  87. showNotification('复制成功!', 'success');
  88. } else {
  89. showNotification('复制失败,请手动选择文本复制', 'error');
  90. }
  91. } catch (err) {
  92. console.error('传统复制方法也失败:', err);
  93. showNotification('复制失败,请手动选择文本复制', 'error');
  94. }
  95. }
  96. // 显示通知
  97. function showNotification (message, type) {
  98. // 移除之前的通知
  99. const existingNotification = document.querySelector('.copy-notification');
  100. if (existingNotification) {
  101. existingNotification.remove();
  102. }
  103. // 创建通知元素
  104. const notification = document.createElement('div');
  105. notification.className = `copy-notification ${type}`;
  106. notification.textContent = message;
  107. // 将通知定位到页面中央
  108. notification.style.top = '50%';
  109. notification.style.left = '50%';
  110. notification.style.right = 'auto';
  111. notification.style.transform = 'translate(-50%, -50%)';
  112. // 添加到文档
  113. document.body.appendChild(notification);
  114. // 显示通知
  115. setTimeout(() => {
  116. notification.classList.add('show');
  117. }, 10);
  118. // 0.5秒后隐藏并移除通知
  119. setTimeout(() => {
  120. notification.classList.remove('show');
  121. // 等待过渡动画完成后移除元素
  122. setTimeout(() => {
  123. if (notification.parentNode) {
  124. notification.parentNode.removeChild(notification);
  125. }
  126. }, 300);
  127. }, 500);
  128. }
  129. // 初始调用一次,添加现有消息的复制按钮
  130. addCopyButtonsToMessages();
  131. // 创建观察器监听DOM变化,为新添加的消息添加复制按钮
  132. const observer = new MutationObserver((mutations) => {
  133. mutations.forEach((mutation) => {
  134. if (mutation.addedNodes.length) {
  135. addCopyButtonsToMessages();
  136. }
  137. });
  138. });
  139. // 开始观察聊天容器
  140. const chatMessages = document.getElementById('chat-messages');
  141. if (chatMessages) {
  142. observer.observe(chatMessages, { childList: true, subtree: true });
  143. }
  144. };
  145. // 页面加载完成后自动执行
  146. document.addEventListener('DOMContentLoaded', () => {
  147. if (typeof window.setupCopyButtons === 'function') {
  148. window.setupCopyButtons();
  149. }
  150. });