tab2.page.ts 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429
  1. import { CommonModule } from '@angular/common';
  2. import { Component } from '@angular/core';
  3. import { Router } from '@angular/router';
  4. import {
  5. ModalController,
  6. IonHeader, IonTitle, IonContent,
  7. IonButton, IonList, IonItem, IonLabel, IonIcon, IonCardContent, IonCardSubtitle, IonCardTitle,
  8. IonCardHeader, IonCard, IonRow, IonCol, IonGrid,
  9. IonAvatar, IonBadge, IonText, IonListHeader, IonToolbar, IonFooter
  10. } from '@ionic/angular/standalone';
  11. import { NavController } from '@ionic/angular/standalone';
  12. import { ChatPanelOptions, FmChatModalInput, FmodeChat, FmodeChatMessage, openChatPanelModal } from 'fmode-ng';
  13. // import { ModalAudioMessageComponent } from 'fmode-ng/lib/aigc/chat/chat-modal-input/modal-audio-message/modal-audio-message.component';
  14. import Parse from "parse";
  15. import { CloudObject, CloudQuery } from 'src/lib/ncloud';
  16. @Component({
  17. selector: 'app-tab2',
  18. templateUrl: 'tab2.page.html',
  19. styleUrls: ['tab2.page.scss'],
  20. standalone: true,
  21. imports: [
  22. CommonModule,
  23. IonHeader, IonTitle, IonContent,
  24. IonButton, IonList, IonItem, IonLabel, IonIcon, IonAvatar,
  25. IonCardContent, IonCardSubtitle, IonCardTitle,
  26. IonCardHeader, IonCard, IonRow, IonCol, IonGrid,
  27. IonAvatar, IonBadge, IonText, IonListHeader, IonToolbar, IonFooter,
  28. // ASR语音输入模块
  29. FmChatModalInput,
  30. // ModalAudioMessageComponent
  31. ]
  32. })
  33. export class Tab2Page {
  34. constructor(
  35. private modalCtrl: ModalController,
  36. private router: Router,
  37. private navCtrl: NavController,
  38. ) {
  39. }
  40. goTraining() {
  41. this.navCtrl.navigateRoot(['tabs', 'demo', 'training'])
  42. }
  43. title: string = "123"
  44. /** 示例:问诊ChatPanel面板 */
  45. openInquiry(chatId?: string) {
  46. localStorage.setItem("company", "E4KpGvTEto")
  47. let options: ChatPanelOptions = {
  48. roleId: "2DXJkRsjXK", // 预设,无需更改
  49. chatId: chatId, // 若存在,则恢复会话。若不存在,则开启新会话
  50. onChatInit: (chat: FmodeChat) => {
  51. console.log("onChatInit");
  52. console.log("Chat类", chat);
  53. console.log("预设角色", chat.role);
  54. // 角色名称
  55. chat.role.set("name", "晓晓");
  56. // 角色称号
  57. chat.role.set("title", "全科医生");
  58. // 角色描述
  59. chat.role.set("desc", "一名亲切和蔼的门诊全科主任医生,晓晓,年龄36岁");
  60. // 角色标签
  61. chat.role.set("tags", ["全科", "门诊"]);
  62. // 角色头像
  63. chat.role.set("avatar", "https://nova-cloud.obs.cn-south-1.myhuaweicloud.com/storage/aigc/imagine/Q4Zif7fTbK-0.png")
  64. // 角色提示词
  65. chat.role.set("prompt", `
  66. # 角色设定
  67. 您是一名亲切和蔼的专业的全科医生,晓晓,年龄36岁,需要完成一次完整的门诊服务。
  68. # 对话环节
  69. 请您严格按照以下环节和用户展开对话,并且注意完成每个环节时,一定要携带[xx完成]的标记。
  70. ## 1. 导诊环节
  71. - **开始话语**:
  72. - “欢迎来到医院,请问您是第一次来吗?我会帮助您找到合适的科室。”
  73. - **进入下个环节条件**:
  74. - “已经大致了解您反映的情况,建议您到XX科室。[导诊完成]
  75. ## 2. 问诊环节
  76. - **对话内容**:
  77. - “请您详细描述一下您的情况,我需要了解您的病史和相关症状。”
  78. - “您是否有过敏史或其他健康问题?”
  79. - “根据您的情况,我认为我们需要进行一些检查,您觉得可以吗?”
  80. - **进入下个环节的条件**:
  81. - “谢谢您的配合,我将为您安排相关检查。[问诊完成]”
  82. ## 3. 检查环节
  83. - **对话内容**:
  84. - “我们已经完成了问诊,现在我会为您安排必要的检查。”
  85. - “请您稍等,检查结果会在不久后出来。”
  86. - “检查结果已经出来了,请您填写下报告的具体数据,让我来为您分析一下。”
  87. - **进入下个环节的条件**:
  88. - “检查结果已经初步分析,接下来需要请主任医生开始诊断。[检查完成]”
  89. ## 4. 诊断与处方环节
  90. - **对话内容**:
  91. - “根据问诊和检查结果,我的诊断是……”
  92. - “接下来,我会为您开具相应的处方,请您仔细阅读治疗方案和注意事项。”
  93. - “您是否有任何问题或者需要进一步的解释?”
  94. - **进入下个环节的条件**:
  95. - “感谢您的配合,您的处方已经开好,请您按照建议进行后续的治疗或复诊安排。[处方完成]”
  96. # 开始话语
  97. 当您准备好了,可以以一个医生的身份,向来访的用户打招呼。`);
  98. // 对话灵感分类
  99. let promptCates = [
  100. {
  101. "img": "https://file-cloud.fmode.cn/UP2cStyjuk/20231211/r1ltv1023812146.png",
  102. "name": "外科"
  103. },
  104. {
  105. "img": "https://file-cloud.fmode.cn/UP2cStyjuk/20231211/fo81fg034154259.png",
  106. "name": "内科"
  107. },
  108. {
  109. "img": "https://file-cloud.fmode.cn/UP2cStyjuk/20231211/fc1nqi034201098.png",
  110. "name": "心理"
  111. }
  112. ]
  113. setTimeout(() => {
  114. chat.role.set("promptCates", promptCates)
  115. }, 500);
  116. // 对话灵感列表
  117. let promptList = [
  118. {
  119. cate: "外科123", img: "https://file-cloud.fmode.cn/UP2cStyjuk/20231211/r1ltv1023812146.png",
  120. messageList: ["局部疼痛或肿胀", "伤口出血或感染", "关节活动受限", "体表肿块或结节", "外伤后活动障碍", "皮肤溃疡不愈合", "异物刺入或嵌顿", "术后并发症复查", "肢体麻木或无力", "运动损伤疼痛"]
  121. },
  122. {
  123. cate: "内科", img: "https://file-cloud.fmode.cn/UP2cStyjuk/20231211/fo81fg034154259.png",
  124. messageList: ["反复发热或低热", "持续咳嗽咳痰", "胸闷气短心悸", "慢性腹痛腹泻", "头晕头痛乏力", "体重骤增或骤减", "食欲异常或消化不良", "尿频尿急尿痛", "睡眠障碍易醒", "异常出汗或怕冷"]
  125. },
  126. {
  127. cate: "心理", img: "https://file-cloud.fmode.cn/UP2cStyjuk/20231211/fc1nqi034201098.png",
  128. messageList: ["持续情绪低落", "焦虑紧张不安", "失眠或睡眠过多", "注意力难以集中", "社交恐惧回避", "强迫思维或行为", "记忆减退疑虑", "躯体无器质性疼痛", "自杀倾向念头", "现实感丧失体验"]
  129. },
  130. ]
  131. let ChatPrompt = Parse.Object.extend("ChatPrompt");
  132. setTimeout(() => {
  133. chat.promptList = promptList.map(item => {
  134. let prompt = new ChatPrompt();
  135. prompt.set(item);
  136. prompt.img = item.img;
  137. return prompt;
  138. })
  139. }, 500);
  140. // 功能按钮区域预设
  141. chat.leftButtons = [
  142. { // 提示 当角色配置预设提示词时 显示
  143. title: "话题灵感", // 按钮标题
  144. showTitle: true, // 是否显示标题文字
  145. icon: "color-wand-outline", // 标题icon图标
  146. onClick: () => { // 按钮点击事件
  147. chat.isPromptModalOpen = true
  148. },
  149. show: () => { // 按钮显示条件
  150. return chat?.promptList?.length // 存在话题提示词时显示
  151. }
  152. },
  153. { // 总结 结束并归档本次对话
  154. title: "门诊归档",
  155. showTitle: true,
  156. icon: "archive-outline",
  157. onClick: () => {
  158. // 门诊归档,记录用户门诊咨询,并进行过程评价
  159. console.log(chat?.chatSession) // 本次会话内容数据
  160. },
  161. show: () => {
  162. return true // 一直显示
  163. }
  164. }
  165. ]
  166. },
  167. onMessage: (chat: FmodeChat, message: FmodeChatMessage) => {
  168. console.log("onMessage", message)
  169. let content: any = message?.content
  170. if (typeof content == "string") {
  171. // 根据阶段标记判断下一步处理过程
  172. if (content.includes('[导诊完成]')) {
  173. // 进入问诊环节
  174. console.log('进入问诊环节');
  175. } else if (content.includes('[问诊完成]')) {
  176. // 进入检查环节
  177. console.log('进入检查环节');
  178. } else if (content.includes('[检查完成]')) {
  179. // 进入诊断与处方环节
  180. console.log('进入诊断与处方环节');
  181. } else if (content.includes('[处方完成]')) {
  182. // 结束会话或其他逻辑
  183. console.log('结束会话');
  184. }
  185. }
  186. },
  187. onChatSaved: (chat: FmodeChat) => {
  188. // chat?.chatSession?.id 本次会话的 chatId
  189. console.log("onChatSaved", chat, chat?.chatSession, chat?.chatSession?.id)
  190. }
  191. }
  192. openChatPanelModal(this.modalCtrl, options)
  193. }
  194. openConsult(chatId?: string) {
  195. localStorage.setItem("company", "E4KpGvTEto")
  196. let options: ChatPanelOptions = {
  197. roleId: "2DXJkRsjXK", // 预设,无需更改
  198. chatId: chatId, // 若存在,则恢复会话。若不存在,则开启新会话
  199. onChatInit: (chat: FmodeChat) => {
  200. console.log("onChatInit");
  201. console.log("Chat类", chat);
  202. console.log("预设角色", chat.role);
  203. // 角色名称
  204. chat.role.set("name", "宋珀尔");
  205. // 角色称号
  206. chat.role.set("title", "专业教练");
  207. // 角色描述
  208. chat.role.set("desc", "一名亲切和蔼的健身教练,宋珀尔,年龄26岁");
  209. // 角色标签
  210. chat.role.set("tags", ['跑步', '动感单车']);
  211. // 角色头像
  212. chat.role.set("avatar", "/assets/avatars/jiaolian1.jpg")
  213. // 角色提示词
  214. chat.role.set("prompt", `
  215. # 角色设定
  216. 您是一名亲切和蔼的健身教练,宋珀尔,年龄26岁,需要您解答用户健身方面的专业问题。
  217. 请您模仿一个正常教练对话,语言简短不需要长篇大论。
  218. `);
  219. // 对话灵感分类
  220. let promptCates = [
  221. {
  222. "img": "/assets/icon/yy.jpg",
  223. "name": "有氧"
  224. },
  225. {
  226. "img": "/assets/icon/jz.jpg",
  227. "name": "减脂"
  228. },
  229. {
  230. "img": "/assets/icon/zj.jpg",
  231. "name": "增肌"
  232. }
  233. ]
  234. setTimeout(() => {
  235. chat.role.set("promptCates", promptCates)
  236. }, 500);
  237. // 对话灵感列表
  238. let promptList = [
  239. {
  240. cate: "有氧", img: "/assets/icon/yy.jpg",
  241. messageList: [
  242. "有氧运动多久才能有效减脂?",
  243. "跑步和游泳哪个减肥效果更好?",
  244. "空腹有氧真的更燃脂吗?",
  245. "有氧运动会不会掉肌肉?",
  246. "心率控制在多少才能高效燃脂?",
  247. "每天做有氧运动会不会过度疲劳?",
  248. "有氧运动前要不要吃东西?",
  249. "椭圆机和跑步机哪个更适合新手?",
  250. "跳绳会不会伤膝盖?",
  251. "有氧运动后怎么补充能量?"
  252. ]
  253. },
  254. {
  255. cate: "减脂", img: "/assets/icon/jz.jpg",
  256. messageList: [
  257. "减脂一定要做有氧吗?",
  258. "为什么体重没变但看起来瘦了?",
  259. "局部减脂(如瘦肚子)真的存在吗?",
  260. "减脂期每天应该吃多少热量?",
  261. "低碳饮食和低脂饮食哪个更适合减脂?",
  262. "为什么运动后体重反而增加了?",
  263. "减脂期可以吃零食吗?",
  264. "平台期怎么突破?",
  265. "晚上吃东西会不会更容易长胖?",
  266. "减脂期要不要计算蛋白质摄入?"
  267. ]
  268. },
  269. {
  270. cate: "增肌", img: "/assets/icon/zj.jpg",
  271. messageList: [
  272. "增肌一定要喝蛋白粉吗?",
  273. "为什么练了很久肌肉不长?",
  274. "增肌期可以同时减脂吗?",
  275. "训练后多久补充蛋白质最有效?",
  276. "增肌需要每天练同一个部位吗?",
  277. "徒手训练(如俯卧撑)能有效增肌吗?",
  278. "增肌期体重不增长是怎么回事?",
  279. "肌肉酸痛还能继续练吗?",
  280. "增肌训练每组做多少次最合适?",
  281. "睡眠对增肌的影响有多大?"
  282. ]
  283. },
  284. ]
  285. let ChatPrompt = Parse.Object.extend("ChatPrompt");
  286. setTimeout(() => {
  287. chat.promptList = promptList.map(item => {
  288. let prompt = new ChatPrompt();
  289. prompt.set(item);
  290. prompt.img = item.img;
  291. return prompt;
  292. })
  293. }, 500);
  294. // 功能按钮区域预设
  295. chat.leftButtons = [
  296. { // 提示 当角色配置预设提示词时 显示
  297. title: "话题灵感", // 按钮标题
  298. showTitle: true, // 是否显示标题文字
  299. icon: "color-wand-outline", // 标题icon图标
  300. onClick: () => { // 按钮点击事件
  301. chat.isPromptModalOpen = true
  302. },
  303. show: () => { // 按钮显示条件
  304. return chat?.promptList?.length // 存在话题提示词时显示
  305. }
  306. },
  307. ]
  308. },
  309. onMessage: (chat: FmodeChat, message: FmodeChatMessage) => {
  310. console.log("onMessage", message)
  311. let content: any = message?.content
  312. if (typeof content == "string") {
  313. // 根据阶段标记判断下一步处理过程
  314. if (content.includes('[导诊完成]')) {
  315. // 进入问诊环节
  316. console.log('进入问诊环节');
  317. } else if (content.includes('[问诊完成]')) {
  318. // 进入检查环节
  319. console.log('进入检查环节');
  320. } else if (content.includes('[检查完成]')) {
  321. // 进入诊断与处方环节
  322. console.log('进入诊断与处方环节');
  323. } else if (content.includes('[处方完成]')) {
  324. // 结束会话或其他逻辑
  325. console.log('结束会话');
  326. }
  327. }
  328. },
  329. onChatSaved: async (chat: FmodeChat) => {
  330. // chat?.chatSession?.id 本次会话的 chatId
  331. console.log("onChatSaved", chat, chat?.chatSession, chat?.chatSession?.id)
  332. let chatId = chat?.chatSession?.id;
  333. console.log("chatId", chatId)
  334. let query = new CloudQuery("TrainingConsult");
  335. let trainingConsult = await query.get(chatId);
  336. console.log("trainingConsult", trainingConsult);
  337. if (!trainingConsult?.id) { // 若无重复记录,则实例化一个新的咨询记录
  338. trainingConsult = new CloudObject("TrainingConsult")
  339. }
  340. trainingConsult.set({
  341. "chatId": chatId,
  342. "messageList": chat.messageList,
  343. "name": chat.role.get("name"),
  344. "avatar": chat.role.get("avatar")
  345. })
  346. console.log("trainingConsult", trainingConsult)
  347. trainingConsult.save();
  348. }
  349. }
  350. openChatPanelModal(this.modalCtrl, options)
  351. }
  352. // 加载健康咨询记录
  353. consultList: Array<CloudObject> = []
  354. async loadConsult() {
  355. let query = new CloudQuery("TrainingConsult");
  356. this.consultList = await query.find()
  357. console.log(this.consultList)
  358. }
  359. /**
  360. * 开始聊天
  361. */
  362. openChat() {
  363. let options: ChatPanelOptions = {
  364. roleId: "2DXJkRsjXK",
  365. onChatSaved: (chat: FmodeChat) => {
  366. // chat?.chatSession?.id 本次会话的 chatId
  367. console.log("onChatSaved", chat, chat?.chatSession, chat?.chatSession?.id)
  368. },
  369. }
  370. openChatPanelModal(this.modalCtrl, options)
  371. }
  372. /**
  373. * 恢复聊天
  374. * @chatId 从onChatSaved生命周期中,获取chat?.chatSession?.id
  375. */
  376. restoreChat(chatId: string) {
  377. let options: ChatPanelOptions = {
  378. roleId: "2DXJkRsjXK",
  379. chatId: chatId
  380. }
  381. openChatPanelModal(this.modalCtrl, options)
  382. }
  383. goChat() {
  384. this.router.navigateByUrl("/chat/session/role/2DXJkRsjXK")
  385. }
  386. // audioModalHeightPoint:number = 0.35;
  387. // async startTalk(){
  388. // // 根据手机兼容性,适配组件弹出高度
  389. // let height = document.body.clientHeight || 960;
  390. // this.audioModalHeightPoint = Number((165/height).toFixed(2));
  391. // // 弹出组件
  392. // let modal:any
  393. // let chat:any
  394. // modal = await this.modalCtrl.create({
  395. // component:ModalAudioMessageComponent,
  396. // componentProps:{
  397. // chat:chat,
  398. // modal:modal,
  399. // onBreakPointSet:()=>{
  400. // modal?.setCurrentBreakpoint(this.audioModalHeightPoint)
  401. // }
  402. // }
  403. // })
  404. // modal.present();
  405. // }
  406. }