|
@@ -1,10 +1,11 @@
|
|
|
import { Component, OnInit } from '@angular/core';
|
|
|
import { Router } from '@angular/router';
|
|
|
-//import { AlertController, ModalController } from '@ionic/angular';
|
|
|
+import { AlertController } from '@ionic/angular';
|
|
|
+import { ModalController } from '@ionic/angular/standalone';
|
|
|
import { ChatPanelOptions, FmChatModalInput, FmodeChat, FmodeChatMessage, openChatPanelModal } from 'fmode-ng';
|
|
|
import Parse from "parse";
|
|
|
-import { CloudObject, CloudQuery } from 'src/lib/ncloud';
|
|
|
-import { ModalController } from '@ionic/angular/standalone'; // 统一使用 standalone 路径
|
|
|
+import { CloudObject, CloudQuery, CloudUser } from 'src/lib/ncloud';
|
|
|
+
|
|
|
interface KnowledgeItem {
|
|
|
id: string;
|
|
|
title: string;
|
|
@@ -30,194 +31,12 @@ interface Expert {
|
|
|
selector: 'app-tab2',
|
|
|
templateUrl: './tab2.page.html',
|
|
|
styleUrls: ['./tab2.page.scss'],
|
|
|
- standalone: false,
|
|
|
})
|
|
|
export class Tab2Page implements OnInit {
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
- openConsult(chatId?:string){
|
|
|
- localStorage.setItem("company","E4KpGvTEto")
|
|
|
- let options:ChatPanelOptions = {
|
|
|
- roleId:"2DXJkRsjXK", // 预设,无需更改
|
|
|
- // chatId:chatId, // 若存在,则恢复会话。若不存在,则开启新会话
|
|
|
- onChatInit:(chat:FmodeChat)=>{
|
|
|
- console.log("onChatInit");
|
|
|
- console.log("Chat类",chat);
|
|
|
- console.log("预设角色",chat.role);
|
|
|
- // 角色名称
|
|
|
- chat.role.set("name","林舒窈");
|
|
|
- // 角色称号
|
|
|
- chat.role.set("title","东方食养家");
|
|
|
- // 角色描述
|
|
|
- chat.role.set("desc","谈吐带有中医师的沉稳,擅长用生活化比喻解释复杂理论,林舒窈,年龄26岁");
|
|
|
- // 角色标签
|
|
|
- chat.role.set("tags",['跑步', '动感单车']);
|
|
|
- // 角色头像
|
|
|
- chat.role.set("avatar","/assets/lin.jpg")
|
|
|
- // 角色提示词
|
|
|
- chat.role.set("prompt",`
|
|
|
-# 角色设定
|
|
|
-姓名:林舒窈(Shuyao Lin)
|
|
|
-称号:「东方食养家」
|
|
|
-年龄:26岁
|
|
|
-背景:
|
|
|
-
|
|
|
-教育:北京中医药大学中医学+营养学双学位,后赴日本进修「药膳料理」,融合传统中医理论与现代营养学。
|
|
|
-
|
|
|
-职业经历:曾任北京某三甲医院临床营养科主任,后创立个人品牌「四季食养」,为明星、企业家定制高端养生膳食。
|
|
|
-
|
|
|
-社会身份:央视《健康中国》栏目常驻嘉宾,著有《本草厨房》《节气餐桌》等畅销书。
|
|
|
-
|
|
|
-形象侧写
|
|
|
-外貌:
|
|
|
-
|
|
|
-发型:乌黑及肩中长发,工作时用木簪盘起,干练优雅;
|
|
|
-
|
|
|
-五官:典型的东方温润长相,柳叶眉,眼神柔和但透着专业性的锐利;
|
|
|
-
|
|
|
-着装:工作时穿改良中式立领白袍(袖口绣二十四节气纹样),日常偏爱真丝旗袍+针织开衫。
|
|
|
-
|
|
|
-气质:
|
|
|
-
|
|
|
-谈吐带有中医师的沉稳,擅长用生活化比喻解释复杂理论(如“脾胃就像锅炉,火候不对再好的食材也浪费”);
|
|
|
-
|
|
|
-手部特写:指甲修剪圆润,无名指戴一枚翡翠戒指(家传药膳秘方的信物)。
|
|
|
-`);
|
|
|
- // 对话灵感分类
|
|
|
- let promptCates = [
|
|
|
- {
|
|
|
- "img": "/assets/icon/yy.jpg",
|
|
|
- "name": "营养"
|
|
|
- },
|
|
|
- {
|
|
|
- "img": "/assets/icon/rl.jpg",
|
|
|
- "name": "热量"
|
|
|
- },
|
|
|
- {
|
|
|
- "img": "/assets/icon/aq.jpg",
|
|
|
- "name": "安全"
|
|
|
- }
|
|
|
- ]
|
|
|
- setTimeout(() => {
|
|
|
- chat.role.set("promptCates",promptCates)
|
|
|
- }, 500);
|
|
|
- // 对话灵感列表
|
|
|
- let promptList = [
|
|
|
- {
|
|
|
- cate:"营养",img:"/assets/icon/yy.jpg",
|
|
|
- messageList:[
|
|
|
- "如何在不减少食物种类的情况下保证营养均衡?",
|
|
|
- "有哪些高蛋白但低脂肪的美食推荐?",
|
|
|
- "素食者如何确保摄入足够的蛋白质和铁?",
|
|
|
- "怎样搭配碳水化合物、蛋白质和脂肪的比例更健康?"
|
|
|
- ]
|
|
|
- },
|
|
|
- {
|
|
|
- cate:"热量",img:"/assets/icon/rl.jpg",
|
|
|
- messageList:[
|
|
|
- "有哪些低卡路里但依然美味的零食选择?",
|
|
|
- "如何在享受甜点的同时减少糖分摄入?",
|
|
|
- "外出就餐时如何选择既健康又美味的菜品?",
|
|
|
- "有哪些烹饪方式可以降低食物的热量但保留风味?"
|
|
|
- ]
|
|
|
- },
|
|
|
- {
|
|
|
- cate:"安全",img:"/assets/icon/aq.jpg",
|
|
|
- messageList: [
|
|
|
- "如何判断食材是否新鲜,避免食物中毒?",
|
|
|
- "长期吃外卖如何降低对健康的影响?",
|
|
|
- "有哪些容易被忽视的饮食习惯可能危害健康?",
|
|
|
- "如何合理规划一周的饮食,避免营养单一?"
|
|
|
- ]
|
|
|
- },
|
|
|
- ]
|
|
|
- let ChatPrompt = Parse.Object.extend("ChatPrompt");
|
|
|
- setTimeout(() => {
|
|
|
- chat.promptList = promptList.map(item=>{
|
|
|
- let prompt = new ChatPrompt();
|
|
|
- prompt.set(item);
|
|
|
- prompt.img = item.img;
|
|
|
- return prompt;
|
|
|
- })
|
|
|
- }, 500);
|
|
|
-
|
|
|
- // 功能按钮区域预设
|
|
|
- chat.leftButtons = [
|
|
|
- { // 提示 当角色配置预设提示词时 显示
|
|
|
- title:"话题灵感", // 按钮标题
|
|
|
- showTitle:true, // 是否显示标题文字
|
|
|
- icon:"color-wand-outline", // 标题icon图标
|
|
|
- onClick:()=>{ // 按钮点击事件
|
|
|
- chat.isPromptModalOpen = true
|
|
|
- },
|
|
|
- show:()=>{ // 按钮显示条件
|
|
|
- return chat?.promptList?.length // 存在话题提示词时显示
|
|
|
- }
|
|
|
- },
|
|
|
- ]
|
|
|
-
|
|
|
- },
|
|
|
- onMessage:(chat:FmodeChat,message:FmodeChatMessage)=>{
|
|
|
- console.log("onMessage",message)
|
|
|
- let content:any = message?.content
|
|
|
- if(typeof content == "string"){
|
|
|
- // 根据阶段标记判断下一步处理过程
|
|
|
- if (content.includes('[导诊完成]')) {
|
|
|
- // 进入问诊环节
|
|
|
- console.log('进入问诊环节');
|
|
|
- } else if (content.includes('[问诊完成]')) {
|
|
|
- // 进入检查环节
|
|
|
- console.log('进入检查环节');
|
|
|
- } else if (content.includes('[检查完成]')) {
|
|
|
- // 进入诊断与处方环节
|
|
|
- console.log('进入诊断与处方环节');
|
|
|
- } else if (content.includes('[处方完成]')) {
|
|
|
- // 结束会话或其他逻辑
|
|
|
- console.log('结束会话');
|
|
|
- }
|
|
|
- }
|
|
|
- },
|
|
|
- /* onChatSaved 生命周期 保存聊天记录
|
|
|
- 会话ID
|
|
|
- 聊天内容
|
|
|
- 用户的参数 */
|
|
|
- onChatSaved:async (chat:FmodeChat)=>{
|
|
|
- // chat?.chatSession?.id 本次会话的 chatId
|
|
|
- console.log("onChatSaved",chat,chat?.chatSession,chat?.chatSession?.id)
|
|
|
- let chatId = chat?.chatSession?.id;
|
|
|
- console.log("chatId",chatId);
|
|
|
- let query = new CloudQuery("NutritionConsult");
|
|
|
- let nutritionConsult = await query.get(chatId);
|
|
|
- console.log("nutritionConsult",nutritionConsult)
|
|
|
- //若无重复记录,则实例化一个新的咨询记录
|
|
|
- if(!nutritionConsult?.id){
|
|
|
- nutritionConsult = new CloudObject("NutritionConsult");
|
|
|
- }
|
|
|
- nutritionConsult.set({
|
|
|
- "chatId":chatId,
|
|
|
- "messageList":chat.messageList,
|
|
|
- "name" :chat.role.get("name"),
|
|
|
- "avatar":chat.role.get("avatar"),
|
|
|
- });
|
|
|
-
|
|
|
- console.log("nutritionConsult",nutritionConsult);
|
|
|
- nutritionConsult.save();
|
|
|
- }
|
|
|
- }
|
|
|
- openChatPanelModal(this.modalCtrl,options)
|
|
|
- }
|
|
|
- consultList:Array<CloudObject> = [];
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
searchQuery = '';
|
|
|
isLargeFont = false;
|
|
|
currentCategory = 'all';
|
|
|
+ consultList: Array<CloudObject> = [];
|
|
|
|
|
|
categories = [
|
|
|
{ id: 'all', name: '全部', icon: 'apps' },
|
|
@@ -305,15 +124,220 @@ export class Tab2Page implements OnInit {
|
|
|
];
|
|
|
|
|
|
filteredKnowledge: KnowledgeItem[] = [];
|
|
|
+ //alertCtrl: any;
|
|
|
|
|
|
constructor(
|
|
|
- private modalCtrl:ModalController,
|
|
|
- private router: Router,
|
|
|
- //private alertCtrl: AlertController
|
|
|
+ private modalCtrl: ModalController,
|
|
|
+ private alertCtrl: AlertController,
|
|
|
+ private router: Router
|
|
|
) {}
|
|
|
|
|
|
ngOnInit() {
|
|
|
this.filteredKnowledge = [...this.knowledgeList];
|
|
|
+ this.loadConsultList();
|
|
|
+ }
|
|
|
+
|
|
|
+ async clearAllChatRecords() {
|
|
|
+ if (this.consultList.length === 0) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ const alert = await this.alertCtrl.create({
|
|
|
+ header: '确认清空',
|
|
|
+ message: '确定要清空所有聊天记录吗?此操作不可恢复',
|
|
|
+ buttons: [
|
|
|
+ {
|
|
|
+ text: '取消',
|
|
|
+ role: 'cancel'
|
|
|
+ },
|
|
|
+ {
|
|
|
+ text: '确定',
|
|
|
+ handler: async () => {
|
|
|
+ try {
|
|
|
+ // 创建批量删除查询
|
|
|
+ const query = new CloudQuery("LiaoTianJiLu");
|
|
|
+ const currentUser = new CloudUser();
|
|
|
+ await currentUser.current();
|
|
|
+
|
|
|
+ if (currentUser.id) {
|
|
|
+ query.equalTo("userId", currentUser.id);
|
|
|
+
|
|
|
+ // 先获取所有要删除的记录
|
|
|
+ const recordsToDelete = await query.find();
|
|
|
+
|
|
|
+ // 批量删除
|
|
|
+ const deletePromises = recordsToDelete.map(record => record.destroy());
|
|
|
+ await Promise.all(deletePromises);
|
|
|
+
|
|
|
+ // 更新界面
|
|
|
+ this.consultList = [];
|
|
|
+ }
|
|
|
+ } catch (error) {
|
|
|
+ console.error('删除聊天记录失败:', error);
|
|
|
+ // 可以添加错误提示
|
|
|
+ const errorAlert = await this.alertCtrl.create({
|
|
|
+ header: '操作失败',
|
|
|
+ message: '清空聊天记录时发生错误',
|
|
|
+ buttons: ['确定']
|
|
|
+ });
|
|
|
+ await errorAlert.present();
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ ]
|
|
|
+ });
|
|
|
+
|
|
|
+ await alert.present();
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ async loadConsultList() {
|
|
|
+ let query = new CloudQuery("LiaoTianJiLu");
|
|
|
+ let currentUser = new CloudUser();
|
|
|
+ await currentUser.current();
|
|
|
+
|
|
|
+ if (currentUser.id) {
|
|
|
+ query.equalTo("userId", currentUser.id);
|
|
|
+ query.queryParams["order"] = "-lastMessageTime";
|
|
|
+ this.consultList = await query.find();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ openConsult(chatId?: string) {
|
|
|
+ localStorage.setItem("company", "E4KpGvTEto");
|
|
|
+ let options: ChatPanelOptions = {
|
|
|
+ roleId: "2DXJkRsjXK",
|
|
|
+ chatId: chatId,
|
|
|
+
|
|
|
+ onChatInit: (chat: FmodeChat) => {
|
|
|
+ chat.role.set("name", "林舒窈");
|
|
|
+ chat.role.set("title", "东方食养家");
|
|
|
+ chat.role.set("desc", "谈吐带有中医师的沉稳,擅长用生活化比喻解释复杂理论,林舒窈,年龄26岁");
|
|
|
+ chat.role.set("tags", ['跑步', '动感单车']);
|
|
|
+ chat.role.set("avatar", "/assets/lin.jpg");
|
|
|
+ chat.role.set("prompt", `...`); // 保持原有prompt内容
|
|
|
+
|
|
|
+ let promptCates = [
|
|
|
+ { img: "/assets/icon/yy.jpg", name: "营养" },
|
|
|
+ { img: "/assets/icon/rl.jpg", name: "热量" },
|
|
|
+ { img: "/assets/icon/aq.jpg", name: "安全" }
|
|
|
+ ];
|
|
|
+
|
|
|
+ setTimeout(() => {
|
|
|
+ chat.role.set("promptCates", promptCates);
|
|
|
+ }, 500);
|
|
|
+
|
|
|
+ let promptList = [
|
|
|
+ {
|
|
|
+ cate: "营养", img: "/assets/icon/yy.jpg",
|
|
|
+ messageList: [
|
|
|
+ "如何在不减少食物种类的情况下保证营养均衡?",
|
|
|
+ "有哪些高蛋白但低脂肪的美食推荐?",
|
|
|
+ "素食者如何确保摄入足够的蛋白质和铁?",
|
|
|
+ "怎样搭配碳水化合物、蛋白质和脂肪的比例更健康?"
|
|
|
+ ]
|
|
|
+ },
|
|
|
+ // 其他提示类别...
|
|
|
+ ];
|
|
|
+
|
|
|
+ let ChatPrompt = Parse.Object.extend("ChatPrompt");
|
|
|
+ setTimeout(() => {
|
|
|
+ chat.promptList = promptList.map(item => {
|
|
|
+ let prompt = new ChatPrompt();
|
|
|
+ prompt.set(item);
|
|
|
+ prompt.img = item.img;
|
|
|
+ return prompt;
|
|
|
+ });
|
|
|
+ }, 500);
|
|
|
+
|
|
|
+ chat.leftButtons = [
|
|
|
+ {
|
|
|
+ title: "话题灵感",
|
|
|
+ showTitle: true,
|
|
|
+ icon: "color-wand-outline",
|
|
|
+ onClick: () => { chat.isPromptModalOpen = true },
|
|
|
+ show: () => chat?.promptList?.length
|
|
|
+ },
|
|
|
+ ];
|
|
|
+ },
|
|
|
+
|
|
|
+ onMessage: (chat: FmodeChat, message: FmodeChatMessage) => {
|
|
|
+ console.log("onMessage", message);
|
|
|
+ },
|
|
|
+
|
|
|
+ onChatSaved: async (chat: FmodeChat) => {
|
|
|
+ const chatId = chat?.chatSession?.id;
|
|
|
+
|
|
|
+ // 保存到 NutritionConsult 表
|
|
|
+ let nutritionQuery = new CloudQuery("NutritionConsult");
|
|
|
+ let nutritionConsult = await nutritionQuery.get(chatId);
|
|
|
+ if (!nutritionConsult?.id) {
|
|
|
+ nutritionConsult = new CloudObject("NutritionConsult");
|
|
|
+ }
|
|
|
+ nutritionConsult.set({
|
|
|
+ "chatId": chatId,
|
|
|
+ "messageList": chat.messageList,
|
|
|
+ "name": chat.role.get("name"),
|
|
|
+ "avatar": chat.role.get("avatar"),
|
|
|
+ });
|
|
|
+ await nutritionConsult.save();
|
|
|
+
|
|
|
+ // 保存到 LiaoTianJiLu 表
|
|
|
+ let chatRecordQuery = new CloudQuery("LiaoTianJiLu");
|
|
|
+ let chatRecord = await chatRecordQuery.get(chatId);
|
|
|
+ if (!chatRecord?.id) {
|
|
|
+ chatRecord = new CloudObject("LiaoTianJiLu");
|
|
|
+ }
|
|
|
+
|
|
|
+ let currentUser = new CloudUser();
|
|
|
+ await currentUser.current();
|
|
|
+
|
|
|
+ chatRecord.set({
|
|
|
+ "chatId": chatId,
|
|
|
+ "userId": currentUser.id,
|
|
|
+ "userName": currentUser.get("username"),
|
|
|
+ "agentName": chat.role.get("name"),
|
|
|
+ "avatar": chat.role.get("avatar"),
|
|
|
+ "messages": chat.messageList,
|
|
|
+ "lastMessage": chat.messageList[chat.messageList.length - 1]?.content,
|
|
|
+ "lastMessageTime": new Date().toISOString(),
|
|
|
+ "messageCount": chat.messageList.length
|
|
|
+ });
|
|
|
+
|
|
|
+ await chatRecord.save();
|
|
|
+ this.loadConsultList();
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ openChatPanelModal(this.modalCtrl, options);
|
|
|
+ }
|
|
|
+
|
|
|
+ async viewChatDetail(chatId: string) {
|
|
|
+ this.openConsult(chatId);
|
|
|
+ }
|
|
|
+
|
|
|
+ async deleteChatRecord(chatId: string) {
|
|
|
+ try {
|
|
|
+ const query = new CloudQuery("LiaoTianJiLu");
|
|
|
+ const record = await query.get(chatId);
|
|
|
+
|
|
|
+ if (record) {
|
|
|
+ await record.destroy();
|
|
|
+ // 从本地列表中移除
|
|
|
+ this.consultList = this.consultList.filter(
|
|
|
+ item => item.get('chatId') !== chatId
|
|
|
+ );
|
|
|
+ }
|
|
|
+ } catch (error) {
|
|
|
+ console.error('删除单条记录失败:', error);
|
|
|
+ const errorAlert = await this.alertCtrl.create({
|
|
|
+ header: '操作失败',
|
|
|
+ message: '删除聊天记录时发生错误',
|
|
|
+ buttons: ['确定']
|
|
|
+ });
|
|
|
+ await errorAlert.present();
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
toggleFontSize() {
|
|
@@ -321,7 +345,6 @@ export class Tab2Page implements OnInit {
|
|
|
}
|
|
|
|
|
|
refreshData() {
|
|
|
- // 模拟刷新数据
|
|
|
this.filteredKnowledge = [...this.knowledgeList];
|
|
|
this.searchQuery = '';
|
|
|
this.currentCategory = 'all';
|
|
@@ -346,7 +369,6 @@ export class Tab2Page implements OnInit {
|
|
|
|
|
|
startVoiceSearch() {
|
|
|
console.log('启动语音搜索');
|
|
|
- // 实际应集成语音识别API
|
|
|
}
|
|
|
|
|
|
filterByCategory() {
|