index.html 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147
  1. <!DOCTYPE html>
  2. <html lang="zh-CN">
  3. <head>
  4. <meta charset="UTF-8" />
  5. <title>智能体助手 - 流式输出演示</title>
  6. <style>
  7. body {
  8. font-family: 'Segoe UI', sans-serif;
  9. background: #f5f5f5;
  10. margin: 0;
  11. padding: 0;
  12. display: flex;
  13. flex-direction: column;
  14. height: 100vh;
  15. }
  16. header {
  17. background-color: #4CAF50;
  18. color: white;
  19. padding: 1rem;
  20. font-size: 1.5rem;
  21. text-align: center;
  22. }
  23. #chat-container {
  24. flex: 1;
  25. overflow-y: auto;
  26. padding: 1rem;
  27. background-color: #ffffff;
  28. box-shadow: inset 0 0 5px #ddd;
  29. }
  30. .message {
  31. padding: 0.6rem 1rem;
  32. margin-bottom: 0.8rem;
  33. border-radius: 10px;
  34. max-width: 90%;
  35. line-height: 1.6;
  36. font-size: 1.1rem;
  37. }
  38. .bot {
  39. background-color: #e0f7fa;
  40. align-self: flex-start;
  41. }
  42. .typing::after {
  43. content: '|';
  44. animation: blink 1s infinite;
  45. color: gray;
  46. }
  47. @keyframes blink {
  48. 0% { opacity: 1; }
  49. 50% { opacity: 0; }
  50. 100% { opacity: 1; }
  51. }
  52. footer {
  53. padding: 1rem;
  54. background-color: #fff;
  55. display: flex;
  56. gap: 0.5rem;
  57. border-top: 1px solid #ccc;
  58. }
  59. input[type="text"] {
  60. flex: 1;
  61. padding: 0.6rem;
  62. font-size: 1rem;
  63. border: 1px solid #ccc;
  64. border-radius: 5px;
  65. }
  66. button {
  67. padding: 0.6rem 1.2rem;
  68. font-size: 1rem;
  69. background-color: #4CAF50;
  70. color: white;
  71. border: none;
  72. border-radius: 5px;
  73. cursor: pointer;
  74. }
  75. button:hover {
  76. background-color: #45a049;
  77. }
  78. </style>
  79. </head>
  80. <body>
  81. <header>智能体助手 - 流式回复展示</header>
  82. <div id="chat-container"></div>
  83. <footer>
  84. <input id="message-input" type="text" placeholder="请输入您的消息..." />
  85. <button onclick="sendMessage()">发送</button>
  86. </footer>
  87. <script>
  88. const chatContainer = document.getElementById('chat-container');
  89. const input = document.getElementById('message-input');
  90. let source = null;
  91. let currentLine = null;
  92. function sendMessage() {
  93. const message = input.value.trim();
  94. if (!message) return;
  95. if (source) {
  96. source.close(); // 停止旧的连接
  97. }
  98. // 创建新消息容器
  99. currentLine = document.createElement('div');
  100. currentLine.className = 'message bot typing';
  101. chatContainer.appendChild(currentLine);
  102. chatContainer.scrollTop = chatContainer.scrollHeight;
  103. // 启动 SSE
  104. source = new EventSource(`/stream_text?message=${encodeURIComponent(message)}`);
  105. source.onmessage = function (event) {
  106. try {
  107. if (!event.data.startsWith('RunResponseContentEvent')) return;
  108. const contentMatch = event.data.match(/content='(.*?)'/);
  109. if (contentMatch && contentMatch[1]) {
  110. const text = contentMatch[1];
  111. currentLine.textContent += text;
  112. chatContainer.scrollTop = chatContainer.scrollHeight;
  113. }
  114. } catch (e) {
  115. console.warn('解析失败:', event.data);
  116. }
  117. };
  118. source.onerror = function () {
  119. currentLine.classList.remove('typing');
  120. source.close();
  121. };
  122. input.value = '';
  123. }
  124. </script>
  125. </body>
  126. </html>