e.html 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357
  1. <!DOCTYPE html>
  2. <html lang="zh-CN">
  3. <head>
  4. <meta charset="UTF-8">
  5. <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"/>
  6. <title>萧e生大模型</title>
  7. <link rel="stylesheet" href="./css/bootstrap.min.css">
  8. <link rel="stylesheet" href="./css/all.min.css">
  9. <link rel="stylesheet" href="./css/github.min.css">
  10. <link rel="stylesheet" href="./css/styles.css">
  11. <link rel="stylesheet" href="./css/chat.css">
  12. </head>
  13. <body>
  14. <div class="container-fluid not-bg vh-100 d-flex flex-column">
  15. <!-- 顶部标题栏 -->
  16. <header class="p-2" style="min-height: 44px; text-align: center; font-size: 18px; font-weight: 700;">
  17. <!--中医AI助手-->
  18. </header>
  19. <!-- 聊天主区域 -->
  20. <main class="flex-grow-1 d-flex flex-column overflow-hidden" id="chat-container">
  21. <div class="flex-grow-1 overflow-auto px-2 py-3" id="chat-messages"></div>
  22. <!-- 输入区域 -->
  23. <div class="px-3 py-2 border-top">
  24. <div class="input-group input-group-sm">
  25. <textarea
  26. id="message-input"
  27. class="form-control"
  28. placeholder="请输入..."
  29. rows="1"
  30. enterkeyhint="send"
  31. ></textarea>
  32. <button id="send-btn" class="btn btn-primary btn-sm">
  33. <i class="fas fa-paper-plane"></i>
  34. </button>
  35. </div>
  36. </div>
  37. </main>
  38. </div>
  39. <!-- 隐藏字段 -->
  40. <input type="hidden" id="session-id" value="{{ session_id }}">
  41. <!-- JavaScript 依赖 -->
  42. <script src="./js/bootstrap.bundle.min.js"></script>
  43. <script src="./js/marked.min.js"></script>
  44. <script src="./js/highlight.min.js"></script>
  45. <script>document.addEventListener('gesturestart', function (event) {
  46. event.preventDefault();
  47. }, false);</script>
  48. <!-- 主应用脚本 -->
  49. <script>
  50. const u1 = `
  51. **杨红,男,38岁**
  52. <br>
  53. 1. **主诉**:脘腹痞胀1年余,去年10月以来多次出血(黑便)
  54. 1. **现病史**:脘腹痞胀1年余,去年10月以来多次出血(黑便),平素饮酒量多,形体丰腴。
  55. 1. **既往史**:有嗜酸粒细胞增多症病史,常服用强的松。
  56. 1. **中医四诊**:脘腹痞胀,右腹隐痛,大便溏薄,易出汗,舌质紫暗,苔薄白,脉沉细。
  57. 1. **辅助检查**:胃镜示“慢性浅表性胃炎伴糜烂”。
  58. `
  59. const r1 = `
  60. 根据您描述的情况,患者目前最可能的原因是与消化系统相关,肝脾不调,气血瘀滞导致便血。以下是具体分析:
  61. <br>
  62. ### 辨证分析:
  63. 1. 患者形体肥胖属湿盛体质,且平素酒,更易蕴生湿热,湿热灼伤络脉,血溢脉外则为瘀血。
  64. 1. 湿邪壅滞,脾失健运,故大便溏薄。证属肝脾不调,气血瘀滞。
  65. <br>
  66. ### 推荐方案:
  67. 治以理气活血,健脾和胃,化瘀通络。常用药:当归、赤芍药、五灵脂、延胡索,另吞服三七粉1~2g
  68. <br>
  69. #### 中药方剂:
  70. 1. **痛泻要方加减** ([国医徐景藩](#)) <a href="javascript:void(0):">转方</a>
  71. - 组成:焦白术10g,白芍药15g,炒防风10g,煨木香6g,鸡内金10g,佛手10g,三棱10g,葛根花68,泽泻15g,牡丹皮10g,黄连2g,补骨脂6g,石斛10g,炙甘草3g,焦山楂、神曲各15g,青皮、陈皮各6g。
  72. - 煎服方法:每日1剂,水煎服。
  73. - **另**:三七粉1g,研末冲服,每日2次。
  74. <span></span>
  75. 2. **柴胡疏肝散(疏肝理气)** <a href="">转方</a>
  76. - 组成:柴胡10g,陈皮12g,川芎10g,香附12g,枳壳10g,芍药12g,甘草6g
  77. - 作用:疏肝解郁,行气止痛,缓解右腹隐痛及气滞症状。
  78. <span></span>
  79. 3. **丹参饮(活血化瘀)** <a href="">转方</a>
  80. - 组成:丹参15g,檀香10g(后下),砂仁6g(后下),木香10g
  81. - 作用:活血化瘀,改善舌质紫暗及血瘀状态。
  82. <span></span>
  83. <br>
  84. #### 适宜技术:
  85. 1. **针刺疗法**:<a href="javascript:void(0):">转方</a>
  86. - 【取穴】足三里(健脾和胃)、中脘(调理脾胃)、脾俞(健脾益气)、三阴交(调和肝脾)。
  87. - 【操作方法】直刺进针,得气后,每5min行针(平补平泻法)一次,留针 30 min 后出针。
  88. - 【疗程说明】针刺、刮痧,可任选一种或两种均选,针刺每周干预2次,连续治疗8次为一疗程;刮痧每周干预1次,连续治疗4次为1疗程。
  89. <span></span>
  90. 2. **艾灸**:<a href="javascript:void(0):">转方</a>
  91. 选用神阙、关元、气海,温阳散寒,增强脾阳。距皮肤2~3cm处进行熏烤,根据患者的热感情况调整合适的距离,当患者感觉温热舒适时固定不动,每穴灸10~15分钟,以局部皮肤出现潮红为度。
  92. 3. **推拿**:<a href="javascript:void(0):">转方</a>
  93. 腹部顺时针按摩,配合按压足三里、合谷穴,每日10分钟,促进气血运行。
  94. 4. **足浴**:
  95. - 银杏叶100g,槐花40g,菊花30g,丹参20g。将以上药物同入药罐中,清水浸泡30分钟,加水2000mL煎汤,煮沸20分钟后去渣取汁,将汁倒入足浴器中,先熏蒸再足浴,每晚1次。20天为一疗程。
  96. - 当归50g,牛膝20g,干姜20g,桂枝10g,桑枝10g。将以上药物同入药罐中,清水浸泡30分钟,加水2000mL煎汤,煮沸20分钟后去渣取汁,将汁倒入足浴器中,先熏蒸再足浴,每晚1次。20天为一疗程。
  97. <span></span>
  98. 5. **茶饮**:
  99. - **芎归茶**
  100. <span></span>
  101. ![芎归茶](./image/c1.png)
  102. <span></span>
  103. - 【成分】:川芎5克,当归2克。
  104. - 【用法】:将川芎、当归放入砂锅中,加适量水煎煮30分钟,去渣取汁。每日1剂,代茶饮用。
  105. - 【功效】:活血祛瘀。
  106. - 【禁忌】:阴虚火旺的人不宜饮用此茶。
  107. <span></span>
  108. - **通脉花果茶**
  109. <span></span>
  110. ![通脉花果茶](./image/c2.png)
  111. <span></span>
  112. - 【原料】:山楂15g,玫瑰花10g,月季花10g,红花5g。
  113. - 【制作】:将山楂、玫瑰花、月季花和红花用水冲净,放入保温杯中,倒入滚开的热水,盖上盖子拧紧。然后将保温杯上下颠倒几次,使水充分地浸泡药材。静置20分钟后可以饮用。此药茶可以反复冲泡至味淡。
  114. - 【效用】:活血化瘀,理气消食。适合血瘀质兼见面部黄褐斑、情志不遂者饮用。
  115. <span></span>
  116. <br>
  117. ### 生活调护建议:
  118. - 饮食清淡,忌辛辣油腻,避免饮酒,可适量食用山药、莲子等健脾食物。
  119. - 保持规律作息,避免熬夜,适当运动以助气血流通。
  120. - 建议进一步检查肠镜及血液指标,排除其他潜在疾病。
  121. <br>
  122. ### 注意事项:
  123. - 中医治疗需结合个体情况调整,以上方案需在专业医师指导下使用。
  124. - 长期服用激素需警惕副作用(如骨质疏松、免疫力下降),建议定期复查。
  125. - 若出现头晕、乏力、心悸等症状,需警惕气血两虚或出血加重,及时就医。
  126. <br>
  127. **以上建议旨在辅助调理,具体治疗需结合临床实际调整。**
  128. `
  129. </script>
  130. <script>
  131. window.addEventListener('message', e => {
  132. const messageInput = document.getElementById('message-input');
  133. if (e.data && e.data.type === 'input') messageInput.value = e.data.value
  134. }, false);
  135. function getValue() {
  136. const messageInput = document.getElementById('message-input');
  137. return messageInput.value;
  138. }
  139. </script>
  140. <script>
  141. const searchParams = new URLSearchParams(window.location.search);
  142. const host = window.location.origin;
  143. document.addEventListener('DOMContentLoaded', function () {
  144. if (searchParams.has('hide_title')) document.querySelector('.container-fluid header').style.display = 'none';
  145. else document.querySelector('.container-fluid header').innerHTML = document.title;
  146. // 获取DOM元素
  147. const chatMessages = document.getElementById('chat-messages');
  148. const messageInput = document.getElementById('message-input');
  149. const sendBtn = document.getElementById('send-btn');
  150. let sessionId = document.getElementById('session-id').value;
  151. if (sessionId.replace(/\s/g, '') === `{{session_id}}`) document.querySelector(`#session-id`).value = sessionId = searchParams.get('session_id');
  152. let eventSource = null;
  153. // 初始化Marked和Highlight.js
  154. marked.setOptions({
  155. breaks: true,
  156. highlight: function (code, language) {
  157. const validLanguage = hljs.getLanguage(language) ? language : 'plaintext';
  158. return hljs.highlight(validLanguage, code).value;
  159. }
  160. });
  161. // 发送消息
  162. function sendMessage() {
  163. const message = messageInput.value.trim();
  164. if (!message) return;
  165. // 添加用户消息
  166. addMessage('user', message);
  167. messageInput.value = '';
  168. // 添加AI思考状态
  169. const aiMessageElement = addMessage('assistant', '', true);
  170. // 关闭之前的连接
  171. if (eventSource) {
  172. eventSource.close();
  173. eventSource = null;
  174. }
  175. let aiResponse = '';
  176. let responseElement = null;
  177. // 使用fetch进行流式请求
  178. fetch(`${host}/tcm_chat/chat/tcm`, {
  179. method: 'POST',
  180. headers: {
  181. 'Content-Type': 'application/json',
  182. },
  183. body: JSON.stringify({
  184. session_id: sessionId,
  185. message: message
  186. })
  187. })
  188. .then(response => {
  189. if (!response.ok) {
  190. throw new Error(`HTTP error! status: ${response.status}`);
  191. }
  192. const reader = response.body.getReader();
  193. const decoder = new TextDecoder();
  194. function readStream() {
  195. return reader.read().then(({done, value}) => {
  196. if (done) {
  197. return;
  198. }
  199. const chunk = decoder.decode(value);
  200. const lines = chunk.split('\n');
  201. for (const line of lines) {
  202. if (line.startsWith('think: ')) { // 思考内容
  203. try {
  204. const data = JSON.parse(line.slice(7));
  205. if (data.content) {
  206. console.log('流信号 思考内容', data.content)
  207. // 这里没实现呢,需要你参考openai的实现@xiong
  208. // 进入思考状态
  209. }
  210. } catch (e) {
  211. console.error('解析响应数据失败:', e);
  212. }
  213. } else if (line.startsWith('data: ')) { // 返回内容
  214. try {
  215. const data = JSON.parse(line.slice(6));
  216. if (data.error) {
  217. // 处理错误
  218. if (!responseElement) {
  219. aiMessageElement.innerHTML = `<div class="alert alert-danger">${data.error}</div>`;
  220. }
  221. return;
  222. }
  223. if (data.content) {
  224. console.log('流信号', data.content)
  225. aiResponse += data.content;
  226. if (!responseElement) {
  227. // 移除思考状态
  228. aiMessageElement.innerHTML = '';
  229. responseElement = aiMessageElement;
  230. }
  231. updateMessageContent(responseElement, aiResponse);
  232. }
  233. } catch (e) {
  234. console.error('解析响应数据失败:', e);
  235. }
  236. }
  237. }
  238. // 继续读取流
  239. return readStream();
  240. });
  241. }
  242. return readStream();
  243. })
  244. .catch(error => {
  245. console.error('请求失败:', error);
  246. if (!responseElement) {
  247. aiMessageElement.innerHTML = '<div class="alert alert-danger">连接错误,请重试</div>';
  248. }
  249. });
  250. }
  251. // 添加消息到聊天界面
  252. function addMessage(role, content, isThinking = false) {
  253. const messageDiv = document.createElement('div');
  254. messageDiv.className = `message ${role}-message mb-3`;
  255. const avatar = document.createElement('div');
  256. avatar.className = 'avatar';
  257. avatar.innerHTML = role === 'user' ?
  258. '<i class="fas fa-user"></i>' :
  259. '<img src="./image/robot.png" alt="">';
  260. const contentDiv = document.createElement('div');
  261. contentDiv.className = 'content';
  262. if (isThinking) {
  263. contentDiv.innerHTML = `
  264. <div class="thinking-container">
  265. <div class="thinking-indicator">
  266. <span></span><span></span><span></span>
  267. </div>
  268. <div class="thinking-text">思考中...</div>
  269. </div>
  270. `;
  271. } else {
  272. contentDiv.innerHTML = marked.parse(content);
  273. hljs.highlightAll();
  274. }
  275. messageDiv.appendChild(avatar);
  276. messageDiv.appendChild(contentDiv);
  277. chatMessages.appendChild(messageDiv);
  278. // 滚动到底部
  279. // chatMessages.scrollTop = chatMessages.scrollHeight;
  280. messageDiv.scrollIntoView({ behavior: 'smooth', block: 'end', inline: 'start' });
  281. return contentDiv;
  282. }
  283. // 更新消息内容
  284. function updateMessageContent(element, content) {
  285. element.innerHTML = marked.parse(content);
  286. hljs.highlightAll();
  287. }
  288. // 事件监听
  289. sendBtn.addEventListener('click', sendMessage);
  290. messageInput.addEventListener('keypress', function (e) {
  291. if (e.key === 'Enter' && !e.shiftKey) {
  292. e.preventDefault();
  293. sendMessage();
  294. }
  295. });
  296. // 初始聚焦输入框
  297. messageInput.focus();
  298. setTimeout(() => {
  299. addMessage('user', u1);
  300. const el = addMessage('robot', '', true);
  301. setTimeout(() => { updateMessageContent(el, r1) }, 3000);
  302. }, 1000)
  303. });
  304. </script>
  305. </body>
  306. </html>