这是一个后端模板渲染的聊天前端页面(模板语法为{% %},属于 Jinja2,常见于 Flask),实现深色风格 AI 对话界面;页面包含头部导航、聊天消息容器、输入提交表单,JS 实现表单提交、fetch 接口请求、简单 Markdown 转 HTML 渲染、自动插入报告日期功能。
关键点
chat_history:后端传入的对话历史列表,循环渲染用户消息、机器人回复;- 两个超链接新窗口打开,对接后端另外两个功能页面:资产核查、FOFA HASH 工具;
- form 表单阻止默认提交,全部走
fetch异步请求,无页面刷新。
CSS 样式分析
页面基础
- 全屏居中布局,
height:100vh,背景深色渐变,10s 循环渐变动画; - 整体深色主题,主色调
#00bcd4青蓝色作为强调色。
核心容器
.chat-container:聊天主容器,最大宽度 750px,高度90vh,flex 纵向布局,实现头部‑消息区‑输入框固定布局;.chat-box:消息滚动容器,flex‑grow:1自动占满剩余高度,overflow‑y:auto消息滚动;.user-message:用户消息靠右对齐,深灰色背景;.bot-response:AI 回复靠左,左侧青色边框,支持内部 h1/h2/h3/strong/code/ul 列表渲染;.input-box底部输入区,flex 布局,输入框圆角,发送按钮使用主题色。
动画
backgroundAnimation:背景渐变切换;fadeIn:消息淡入动画。
JavaScript 逻辑分析
formatReport(text) 格式化函数
作用:后端返回纯文本,简单模拟 Markdown 转 HTML,自动追加报告日期
<!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1" /> <title>AI智能体</title> <style> body { font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; background: linear-gradient(135deg, #121212, #2a2a2a); color: #fff; margin: 0; padding: 0; display: flex; justify-content: center; align-items: center; height: 100vh; overflow: hidden; animation: backgroundAnimation 10s infinite alternate; } @keyframes backgroundAnimation { 0% { background: linear-gradient(135deg, #121212, #2a2a2a); } 50% { background: linear-gradient(135deg, #2a2a2a, #121212); } 100% { background: linear-gradient(135deg, #121212, #2a2a2a); } } .chat-container { width: 100%; max-width: 750px; background-color: #1a1a1a; border-radius: 15px; box-shadow: 0 8px 15px rgba(255, 255, 255, 0.2); display: flex; flex-direction: column; height: 90vh; border: 1px solid #fff; } .header { background-color: #2b2b2b; padding: 15px 20px; font-size: 28px; font-weight: bold; border-bottom: 2px solid #00bcd4; display: flex; justify-content: space-between; align-items: center; } .header .buttons { display: flex; gap: 12px; } .header a { text-decoration: none; background-color: #00bcd4; color: white; padding: 8px 14px; border-radius: 20px; font-size: 14px; } .chat-box { padding: 20px; overflow-y: auto; flex-grow: 1; background-color: #111; display: flex; flex-direction: column; } .user-message, .bot-response { margin-bottom: 18px; max-width: 85%; border-radius: 12px; padding: 14px; animation: fadeIn 0.4s ease forwards; } .user-message { background-color: #3a3a3a; align-self: flex-end; text-align: right; } .bot-response { background-color: #1e1e1e; align-self: flex-start; border-left: 5px solid #00bcd4; box-shadow: 0 2px 10px rgba(0, 188, 212, 0.2); line-height: 1.75; } .bot-response h1, .bot-response h2, .bot-response h3 { margin: 16px 0 10px; color: #00e5ff; } .bot-response h2 { border-left: 4px solid #00bcd4; padding-left: 10px; } .bot-response strong { color: #ffd54f; } .bot-response ul { padding-left: 20px; margin: 10px 0; } .bot-response li { margin-bottom: 6px; } .bot-response code { background: #000; padding: 3px 6px; border-radius: 4px; color: #ffab70; } .input-box { display: flex; padding: 15px; background-color: #000; } .input-box input { flex-grow: 1; padding: 12px 18px; font-size: 16px; border-radius: 25px; border: 2px solid #fff; background-color: #333; color: #fff; } .input-box button { margin-left: 15px; padding: 12px 18px; border-radius: 25px; border: none; background-color: #00bcd4; color: #fff; cursor: pointer; } </style> </head> <body> <div class="chat-container"> <div class="header"> <span>AI智能体</span> <div class="buttons"> <a href="/asset-check" target="_blank">资产未报备</a> <a href="/hash" target="_blank">FOFA-HASH</a> </div> </div> <div class="chat-box" id="chat-box"> <div class="bot-response">你好,请问需要什么帮助?</div> {% for chat in chat_history %} <div class="user-message">{{ chat.user_message }}</div> <div class="bot-response">{{ chat.bot_response }}</div> {% endfor %} </div> <div class="input-box"> <form id="chat-form" style="display:flex;width:100%;"> <input type="text" id="message" name="message" placeholder="请输入问题或IP地址..." required /> <button type="submit">发送</button> </form> </div> </div> <script> function formatReport(text) { if (!text) return ""; // HTML 转义 text = text.replace(/</g, "<").replace(/>/g, ">"); // ===== 自动补报告日期(插入到“报告结束”之前)===== if (!/报告日期[::]/.test(text)) { const now = new Date(); const dateStr = now.getFullYear() + "-" + String(now.getMonth() + 1).padStart(2, '0') + "-" + String(now.getDate()).padStart(2, '0'); if (/报告结束/.test(text)) { text = text.replace( /(\n*报告结束)/, `\n\n报告日期: ${dateStr}\n$1` ); } else { text += `\n\n报告日期: ${dateStr}`; } } // Markdown → HTML text = text.replace(/^### (.*)$/gm, "<h3>$yd</h3>"); text = text.replace(/^## (.*)$/gm, "<h2>$yd</h2>"); text = text.replace(/^# (.*)$/gm, "<h1>$yd</h1>"); text = text.replace(/\*\*(.*?)\*\*/g, "<strong>$yd</strong>"); text = text.replace(/^- (.*)$/gm, "<li>$yd</li>"); text = text.replace(/(<li>.*<\/li>)/gs, "<ul>$yd</ul>"); text = text.replace(/\n/g, "<br>"); return text; } document.getElementById('chat-form').addEventListener('submit', function(e) { e.preventDefault(); const msg = document.getElementById('message').value; const chatBox = document.getElementById('chat-box'); chatBox.innerHTML += `<div class="user-message">${msg}</div>`; document.getElementById('message').value = ''; const loading = document.createElement('div'); loading.className = 'bot-response'; loading.innerText = '正在分析,请稍候...'; chatBox.appendChild(loading); chatBox.scrollTop = chatBox.scrollHeight; fetch('/chat', { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, body: new URLSearchParams({ message: msg }) }) .then(res => res.json()) .then(data => { loading.innerHTML = formatReport(data.bot_response); chatBox.scrollTop = chatBox.scrollHeight; }); }); </script> </body> </html>