|
@@ -1,75 +1,274 @@
|
|
|
import { Component } from '@angular/core';
|
|
|
+import { CloudSeMealPlan } from 'src/lib/cloudplans'; // 引入封装好的 CloudSeMealPlan 类
|
|
|
import { FmodeChatCompletion, MarkdownPreviewModule } from 'fmode-ng';
|
|
|
-import { IonButton, IonContent, IonHeader, IonInput, IonTextarea, IonTitle, IonToolbar } from '@ionic/angular/standalone';
|
|
|
+import { CloudSeUser } from 'src/lib/cloudSeuser'; // 引入 CloudSeUser 类
|
|
|
|
|
|
+import { IonButton, IonContent, IonHeader, IonInput, IonSelect, IonSelectOption, IonTextarea, IonTitle, IonToolbar } from '@ionic/angular/standalone';
|
|
|
+import { CloudUser } from 'src/lib/ncloud';
|
|
|
@Component({
|
|
|
- selector: 'app-tab2',
|
|
|
- templateUrl: 'tab2.page.html',
|
|
|
- styleUrls: ['tab2.page.scss'],
|
|
|
- standalone: true,
|
|
|
- imports: [
|
|
|
- IonContent, IonHeader, IonTitle, IonToolbar, // 引入 IonicModule
|
|
|
- IonButton,IonTextarea,IonInput,
|
|
|
- MarkdownPreviewModule,
|
|
|
- ],
|
|
|
+selector: 'app-tab2',
|
|
|
+templateUrl: 'tab2.page.html',
|
|
|
+styleUrls: ['tab2.page.scss'],
|
|
|
+standalone: true,
|
|
|
+imports: [
|
|
|
+IonContent, IonHeader, IonTitle, IonToolbar,
|
|
|
+IonButton,IonTextarea,IonInput,IonSelectOption,IonSelect,
|
|
|
+MarkdownPreviewModule,
|
|
|
+],
|
|
|
})
|
|
|
export class Tab2Page {
|
|
|
- ngOnInit() {}
|
|
|
- constructor() {}
|
|
|
|
|
|
- // 用户输入提示词
|
|
|
- qunti: string = "";
|
|
|
- jikou: string = "";
|
|
|
- userPrompt: string = "";
|
|
|
+constructor() {}
|
|
|
|
|
|
- // 用户输入的需求
|
|
|
- quntiInput(ev: any) {
|
|
|
- this.qunti = ev.detail.value;
|
|
|
- }
|
|
|
+ngOnInit() {
|
|
|
+this.loadUserData();
|
|
|
+this.setNextDay();
|
|
|
+}
|
|
|
+
|
|
|
+planningDays: string = "";
|
|
|
+userPrompt: string = "";
|
|
|
+currentDate = new Date();
|
|
|
+dateOnlyString: string = '';
|
|
|
+
|
|
|
+// 用户信息
|
|
|
+age: number | null = null;
|
|
|
+gender: string = '';
|
|
|
+height: number | null = null;
|
|
|
+weight: number | null = null;
|
|
|
+activityLevel: string = '';
|
|
|
+dietPreference: string = '';
|
|
|
+dietGroup: string = '';
|
|
|
+avatar: string | null = null;
|
|
|
+allergies: string = '';
|
|
|
+
|
|
|
+responseMsg: string = "";
|
|
|
+responseMsg0: string = "";
|
|
|
+responseMsg1: string = "";
|
|
|
+isComplete: boolean = false;
|
|
|
+isNew: boolean = false;
|
|
|
+
|
|
|
+async loadUserData() {
|
|
|
+const cloudSeUser = new CloudSeUser();
|
|
|
+const currentUserInfo = await cloudSeUser.getCurrentUserInfo();
|
|
|
+
|
|
|
+if (currentUserInfo) {
|
|
|
+ this.age = currentUserInfo.get('age') || null;
|
|
|
+ this.gender = currentUserInfo.get('gender') || '';
|
|
|
+ this.height = currentUserInfo.get('height') || null;
|
|
|
+ this.weight = currentUserInfo.get('weight') || null;
|
|
|
+ this.activityLevel = currentUserInfo.get('activityLevel') || '';
|
|
|
+ this.dietPreference = currentUserInfo.get('dietPreference') || '';
|
|
|
+ this.dietGroup = currentUserInfo.get('dietGroup') || '';
|
|
|
+ this.allergies = currentUserInfo.get('allergies') || '';
|
|
|
+} else {
|
|
|
+ console.error("未能加载当前用户信息");
|
|
|
+}
|
|
|
+}
|
|
|
+
|
|
|
+setNextDay() {
|
|
|
+const nextDay = new Date(this.currentDate);
|
|
|
+nextDay.setDate(this.currentDate.getDate() + 1);
|
|
|
+this.dateOnlyString = nextDay.toISOString().split('T')[0];
|
|
|
+}
|
|
|
+
|
|
|
+planningDaysInput(ev: any) {
|
|
|
+this.planningDays = ev.detail.value;
|
|
|
+}
|
|
|
+
|
|
|
+promptInput(ev: any) {
|
|
|
+this.userPrompt = ev.detail.value;
|
|
|
+}
|
|
|
+
|
|
|
+sendMessage() {
|
|
|
+console.log("create");
|
|
|
+
|
|
|
+this.responseMsg = "";
|
|
|
+this.responseMsg0="";
|
|
|
+this.isComplete = false;
|
|
|
+
|
|
|
+let newPrompt;
|
|
|
+if(this.responseMsg1==""){
|
|
|
+ newPrompt = `
|
|
|
+ 你是一名专业的饮食营养规划师,拥有丰富的营养学背景和实践经验。你的工作是为不同需求的人群提供个性化的饮食规划,帮助他们实现健康目标,如减肥、增肌、健康维护或疾病管理。
|
|
|
+ 你需要根据用户的用户信息和需求为客户设计量身定制具体的饮食方案。
|
|
|
+ 当前用户的饮食需求是:${this.userPrompt},
|
|
|
+ 现在告诉你用户描述:该用户是一个年龄为${this.age}岁,身高和体重分别为${this.height}cm,${this.weight}kg的${this.gender}性。
|
|
|
+ 他的活动水平${this.activityLevel},饮食偏好${this.dietPreference},并且他的过敏信息为${this.allergies}。
|
|
|
+ 重点是他希望作为一位${this.dietGroup}群体的人,希望你为他或她规划以为${this.dateOnlyString}开始${this.planningDays}天的详细的饮食规划(食物内容以日常生活常见食物为主,每次规划尽量不同,要多样化)。
|
|
|
+ 生成的结果不需要再复述用户的信息,直接以形式输出格式为(day(date类型)、breakfast(string类型)、lunch(string类型)、dinner(string类型)、notes(string类型))结构化JSON格式输出
|
|
|
+ (结果不需要用\`\`\`json和\`\`\`将结果包裹,当天数大于一时,用[]括起多天的规划、并且之间用,隔开)
|
|
|
+ `;
|
|
|
+}else{
|
|
|
+ newPrompt = `
|
|
|
+ 你是一名专业的饮食营养规划师,拥有丰富的营养学背景和实践经验。你的工作是为不同需求的人群提供个性化的饮食规划,帮助他们实现健康目标,如减肥、增肌、健康维护或疾病管理。
|
|
|
+ 你需要根据用户的用户信息和需求为客户设计量身定制具体的饮食方案。
|
|
|
+ 现在告诉你用户描述:该用户是一个年龄为${this.age}岁,身高和体重分别为${this.height}cm,${this.weight}kg的${this.gender}性。
|
|
|
+ 他的活动水平${this.activityLevel},饮食偏好${this.dietPreference},并且他的过敏信息为${this.allergies}。
|
|
|
+ 重点是他希望作为一位${this.dietGroup}群体的人,希望你为他或她规划以为${this.dateOnlyString}开始${this.planningDays}天的详细的饮食规划(食物内容以日常生活常见食物为主)。
|
|
|
+ 你可以在该${this.responseMsg1}饮食规划的基础上,再根据该需求${this.userPrompt},生成新的饮食规划,以下格式不变。
|
|
|
+ 生成的结果不需要再复述用户的信息,直接以形式输出格式为(day(date类型)、breakfast(string类型)、lunch(string类型)、dinner(string类型)、notes(string类型))结构化JSON格式输出
|
|
|
+ (结果不需要用\`\`\`json和\`\`\`将结果包裹,当天数大于一时,用[]括起多天的规划、并且之间用,隔开)
|
|
|
+ `;
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+let completion = new FmodeChatCompletion([
|
|
|
+ { role: "system", content: "" },
|
|
|
+ { role: "user", content: newPrompt }
|
|
|
+]);
|
|
|
+
|
|
|
+completion.sendCompletion().subscribe((message: any) => {
|
|
|
+ console.log(message.content);
|
|
|
+ this.responseMsg = message.content;
|
|
|
+
|
|
|
+ if (message?.complete) {
|
|
|
+ this.isComplete = true;
|
|
|
+ this.isNew = true;
|
|
|
+ this.responseMsg0=this.responseMsg;
|
|
|
+ this.responseMsg1=this.responseMsg;
|
|
|
+ // 1. 替换字段名
|
|
|
+ this.responseMsg0 = this.responseMsg0
|
|
|
+ .replace(/\bday\b/g, '日期')
|
|
|
+ .replace(/\bbreakfast\b/g, '早餐')
|
|
|
+ .replace(/\blunch\b/g, '午餐')
|
|
|
+ .replace(/\bdinner\b/g, '晚餐')
|
|
|
+ .replace(/\bnotes\b/g, '注意事项');
|
|
|
+
|
|
|
+ // 2. 将 " 后面的 , 替换为换行
|
|
|
+ this.responseMsg0 = this.responseMsg0.replace(/",/g, '<br>');
|
|
|
+
|
|
|
+ // 3. 将 } 后面的 , 替换为换行
|
|
|
+ this.responseMsg0 = this.responseMsg0.replace(/},/g, '<br>-------------------<br>');
|
|
|
+
|
|
|
+ // 4. 删除多余的空格(包括首尾空格和中间多余的空格)
|
|
|
+ this.responseMsg0 = this.responseMsg0.replace(/\s+/g, ' ').trim();
|
|
|
+
|
|
|
+ // 5. 删除所有的双引号、方括号、花括号
|
|
|
+ this.responseMsg0 = this.responseMsg0
|
|
|
+ .replace(/["\[\]{}]/g, ''); // 删除双引号、方括号、花括号
|
|
|
|
|
|
- jikouInput(ev: any) {
|
|
|
- this.jikou = ev.detail.value;
|
|
|
}
|
|
|
+});
|
|
|
+}
|
|
|
+
|
|
|
+// 保存饮食规划到数据库
|
|
|
+async saveMealPlan() {
|
|
|
+const cloudSeMealPlan = new CloudSeMealPlan();
|
|
|
+
|
|
|
+try {
|
|
|
+ // 清理 responseMsg,并处理数据结构
|
|
|
+ const processedMealPlan = JSON.parse(this.responseMsg).map((plan: any) => {
|
|
|
+ return {
|
|
|
+ day: plan.day,
|
|
|
+ breakfast: plan.breakfast,
|
|
|
+ lunch: plan.lunch,
|
|
|
+ dinner: plan.dinner,
|
|
|
+ notes: plan.notes
|
|
|
+ };
|
|
|
+ });
|
|
|
|
|
|
- promptInput(ev: any) {
|
|
|
- this.userPrompt = ev.detail.value;
|
|
|
+ // 将处理后的每一天的饮食规划保存到数据库
|
|
|
+ for (let meal of processedMealPlan) {
|
|
|
+ const savedMealPlan = await cloudSeMealPlan.saveMealPlan(meal);
|
|
|
+ if (savedMealPlan) {
|
|
|
+ console.log('饮食规划保存成功:', savedMealPlan);
|
|
|
+ } else {
|
|
|
+ console.error('饮食规划保存失败');
|
|
|
+ // console.log(this.responseMsg)
|
|
|
+ }
|
|
|
}
|
|
|
+} catch (error) {
|
|
|
+ console.error('保存饮食规划时发生错误:', error);
|
|
|
+}
|
|
|
+}
|
|
|
+
|
|
|
+async exportMealPlan() {
|
|
|
+ if (this.isComplete) {
|
|
|
+ try {
|
|
|
+ const cloudSeMealPlan = new CloudSeMealPlan();
|
|
|
+ const cloudUser = new CloudUser();
|
|
|
+ const cloudSeUser = new CloudSeUser();
|
|
|
+
|
|
|
+ const currentUser = await cloudUser.current();
|
|
|
+ if (!currentUser) {
|
|
|
+ console.error("用户未登录");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 获取当前用户的计划天数
|
|
|
+ const currentUserInfo = await cloudSeUser.getCurrentUserInfo();
|
|
|
+ const planDaysFromUserInfo = currentUserInfo?.get('planDays');
|
|
|
|
|
|
- // 属性:组件内用于展示消息内容的变量
|
|
|
- responseMsg: string = "";
|
|
|
- // 是否完成生成
|
|
|
- isComplete: boolean = false;
|
|
|
-
|
|
|
- sendMessage() {
|
|
|
- console.log("create");
|
|
|
-
|
|
|
- // 每次生成新的饮食规划时,首先清空旧的规划
|
|
|
- this.responseMsg = ""; // 清空旧的响应消息
|
|
|
- this.isComplete = false; // 重置完成状态
|
|
|
- // 拼接用户输入的需求
|
|
|
- let newPrompt = `
|
|
|
- 你是一名专业的饮食营养规划师,拥有丰富的营养学背景和实践经验。你的工作是为不同需求的人群提供个性化的饮食规划,帮助他们实现健康目标,如减肥、增肌、健康维护或疾病管理。
|
|
|
- 你可以根据用户的需求为客户设计量身定制具体的饮食方案。
|
|
|
- 当前来咨询的用户群体是${this.qunti},用户的饮食忌口是${this.jikou},需求是${this.userPrompt}.
|
|
|
- `;
|
|
|
-
|
|
|
- // 创建消息请求,传递完整的需求
|
|
|
- let completion = new FmodeChatCompletion([
|
|
|
- { role: "system", content: "" },
|
|
|
- { role: "user", content: newPrompt }
|
|
|
- ]);
|
|
|
-
|
|
|
- // 执行生成并订阅返回结果
|
|
|
- completion.sendCompletion().subscribe((message: any) => {
|
|
|
- console.log(message.content);
|
|
|
-
|
|
|
- // 如果有新内容,更新组件的响应消息,覆盖旧的规划
|
|
|
- this.responseMsg = message.content;
|
|
|
-
|
|
|
- // 如果返回内容标记为完成,则更新状态
|
|
|
- if (message?.complete) {
|
|
|
- this.isComplete = true;
|
|
|
+ // 如果 planDays 存在且不为空,则删除旧的饮食规划
|
|
|
+ if (planDaysFromUserInfo && planDaysFromUserInfo > 0) {
|
|
|
+ console.log(`计划天数为 ${planDaysFromUserInfo},删除旧的饮食规划`);
|
|
|
+ // 直接使用 currentUser 对象,而不需要单独声明 currentUserId
|
|
|
+
|
|
|
+ // 根据 planDays 执行循环删除操作
|
|
|
+ for (let i = 0; i < planDaysFromUserInfo; i++) {
|
|
|
+ const existingMealPlan = await cloudSeMealPlan.getCurrentUserMealPlan();
|
|
|
+ if (existingMealPlan && existingMealPlan.id) {
|
|
|
+ await cloudSeMealPlan.deleteMealPlan(existingMealPlan.id);
|
|
|
+ console.log('已删除一条旧的饮食规划');
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }else{
|
|
|
+ console.error('无旧的饮食规划,无法删除');
|
|
|
}
|
|
|
- });
|
|
|
+
|
|
|
+ // 处理新的饮食规划
|
|
|
+ const processedMealPlan = JSON.parse(this.responseMsg).map((plan: any) => {
|
|
|
+ return {
|
|
|
+ day: plan.day,
|
|
|
+ breakfast: plan.breakfast,
|
|
|
+ lunch: plan.lunch,
|
|
|
+ dinner: plan.dinner,
|
|
|
+ notes: plan.notes
|
|
|
+ };
|
|
|
+ });
|
|
|
+
|
|
|
+ // 标志位,表示饮食规划是否成功保存
|
|
|
+ let mealPlansSavedSuccessfully = true;
|
|
|
+
|
|
|
+ // 保存新的饮食规划
|
|
|
+ for (let meal of processedMealPlan) {
|
|
|
+ const savedMealPlan = await cloudSeMealPlan.saveMealPlan(meal);
|
|
|
+ if (savedMealPlan) {
|
|
|
+ console.log('饮食规划已成功导出并保存');
|
|
|
+ } else {
|
|
|
+ console.error('导出饮食规划失败');
|
|
|
+ mealPlansSavedSuccessfully = false;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 如果饮食规划保存失败,跳过更新 planDays
|
|
|
+ if (!mealPlansSavedSuccessfully) {
|
|
|
+ console.error("饮食规划保存失败,规划天数不会更新");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 更新用户的 planDays 字段
|
|
|
+ const planDaysNumber = parseInt(this.planningDays, 10);
|
|
|
+ if (isNaN(planDaysNumber)) {
|
|
|
+ console.error("规划天数无效,无法保存");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ const updatedUser = await cloudSeUser.updateUserInfo({ planDays: planDaysNumber });
|
|
|
+ if (updatedUser) {
|
|
|
+ console.log('用户的计划天数已更新');
|
|
|
+ } else {
|
|
|
+ console.error('更新用户计划天数失败');
|
|
|
+ }
|
|
|
+
|
|
|
+ } catch (error) {
|
|
|
+ console.error('导出饮食规划时发生错误:', error);
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ console.error('饮食规划尚未生成,无法导出');
|
|
|
}
|
|
|
}
|
|
|
+
|
|
|
+}
|