|
@@ -1,26 +1,26 @@
|
|
|
import { CommonModule } from '@angular/common';
|
|
|
import { Component } from '@angular/core';
|
|
|
-import { IonTextarea } from '@ionic/angular/standalone';
|
|
|
import {
|
|
|
ModalController,
|
|
|
- IonHeader,IonNote,IonChip, IonToolbar, IonTitle, IonButtons, IonButton, IonIcon,
|
|
|
+ IonHeader, IonNote, IonChip, IonToolbar, IonTitle, IonButtons, IonButton, IonIcon,
|
|
|
IonContent, IonCard, IonCardHeader, IonCardTitle, IonCardSubtitle,
|
|
|
IonCardContent, IonProgressBar, IonGrid, IonRow, IonCol, IonListHeader,
|
|
|
- IonLabel, IonList, IonItem, IonAvatar, IonBadge, IonFab, IonFabButton,IonSkeletonText
|
|
|
+ IonLabel, IonList, IonItem, IonAvatar, IonBadge, IonFab, IonFabButton, IonSkeletonText
|
|
|
} from '@ionic/angular/standalone';
|
|
|
import { addIcons } from 'ionicons';
|
|
|
import {
|
|
|
notificationsOutline, calendarNumber, checkmarkCircle, ellipseOutline,
|
|
|
timeOutline, barbell, body, walk, add, refresh, fitness,
|
|
|
- flame, bicycle, trophy,
|
|
|
- ellipsisHorizontal
|
|
|
-} from 'ionicons/icons';
|
|
|
+ flame, bicycle, trophy, ellipsisHorizontal, footsteps, footstepsOutline, chatbubbleEllipses } from 'ionicons/icons';
|
|
|
|
|
|
// 引用fmode-ng智能体组件
|
|
|
import { ChatPanelOptions, FmodeChat, FmodeChatMessage, openChatPanelModal } from 'fmode-ng';
|
|
|
import Parse from "parse";
|
|
|
import { CloudObject, CloudQuery, CloudUser } from 'src/lib/ncloud';
|
|
|
|
|
|
+// 导入计划创建模态框组件
|
|
|
+import { PlanCreationModalComponent } from './plan-creation-modal.component';
|
|
|
+
|
|
|
@Component({
|
|
|
selector: 'app-tab2',
|
|
|
templateUrl: 'tab2.page.html',
|
|
@@ -28,171 +28,132 @@ import { CloudObject, CloudQuery, CloudUser } from 'src/lib/ncloud';
|
|
|
standalone: true,
|
|
|
imports: [
|
|
|
CommonModule,
|
|
|
- // IonTextarea,
|
|
|
- IonNote,IonChip,
|
|
|
+ IonNote, IonChip,
|
|
|
IonHeader, IonToolbar, IonTitle, IonButtons, IonButton, IonIcon,
|
|
|
IonContent, IonCard, IonCardHeader, IonCardTitle, IonCardSubtitle,
|
|
|
IonCardContent, IonProgressBar, IonGrid, IonRow, IonCol, IonListHeader,
|
|
|
- IonLabel, IonList, IonItem, IonAvatar, IonBadge, IonFab, IonFabButton,IonSkeletonText
|
|
|
+ IonLabel, IonList, IonItem, IonAvatar, IonBadge, IonFab, IonFabButton, IonSkeletonText
|
|
|
]
|
|
|
})
|
|
|
export class Tab2Page {
|
|
|
|
|
|
- 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/avatars/jiaolian1.jpg")
|
|
|
- // 角色提示词
|
|
|
- chat.role.set("prompt",`
|
|
|
+ openConsult(chatId?: string) {
|
|
|
+ localStorage.setItem("company", "E4KpGvTEto")
|
|
|
+ let options: ChatPanelOptions = {
|
|
|
+ roleId: "2DXJkRsjXK", // 预设,无需更改
|
|
|
+ onChatInit: (chat: FmodeChat) => {
|
|
|
+ // 更新为跑步教练信息
|
|
|
+ chat.role.set("name", "陈驰");
|
|
|
+ chat.role.set("title", "跑步教练");
|
|
|
+ chat.role.set("desc", "前马拉松运动员,10年跑步训练经验");
|
|
|
+ chat.role.set("tags", ['长跑', '间歇训练', '跑姿矫正']);
|
|
|
+ chat.role.set("avatar", "/assets/avatars/runner-coach.jpg");
|
|
|
+
|
|
|
+ // 更新提示词为跑步专项
|
|
|
+ chat.role.set("prompt", `
|
|
|
# 角色设定
|
|
|
-您是一名亲切和蔼的健身教练,宋珀尔,年龄26岁,需要您解答用户健身方面的专业问题。
|
|
|
+您是专业跑步教练陈驰,35岁,前国家马拉松队员。专注于跑步技术、训练计划和伤病预防。
|
|
|
`);
|
|
|
- // 对话灵感分类
|
|
|
- let promptCates = [
|
|
|
- {
|
|
|
- "img": "/assets/icon/yy.jpg",
|
|
|
- "name": "有氧"
|
|
|
- },
|
|
|
- {
|
|
|
- "img": "/assets/icon/jz.jpg",
|
|
|
- "name": "减脂"
|
|
|
- },
|
|
|
- {
|
|
|
- "img": "/assets/icon/zj.jpg",
|
|
|
- "name": "增肌"
|
|
|
- }
|
|
|
- ]
|
|
|
- setTimeout(() => {
|
|
|
- chat.role.set("promptCates",promptCates)
|
|
|
- }, 500);
|
|
|
- // 对话灵感列表
|
|
|
- let promptList = [
|
|
|
+
|
|
|
+ // 更新对话分类为跑步主题
|
|
|
+ const promptCates = [
|
|
|
+ { img: "/assets/icon/distance.png", name: "长跑训练" },
|
|
|
+ { img: "/assets/icon/speed.png", name: "速度提升" },
|
|
|
+ { img: "/assets/icon/injury.png", name: "伤痛预防" }
|
|
|
+ ];
|
|
|
+
|
|
|
+ // 更新对话提示内容
|
|
|
+ const promptList = [
|
|
|
{
|
|
|
- cate:"有氧",img:"/assets/icon/yy.jpg",
|
|
|
- messageList:[
|
|
|
- "有氧运动多久才能有效减脂?",
|
|
|
- "跑步和游泳哪个减肥效果更好?",
|
|
|
- "空腹有氧真的更燃脂吗?",
|
|
|
- "有氧运动会不会掉肌肉?",
|
|
|
- "心率控制在多少才能高效燃脂?",
|
|
|
- "每天做有氧运动会不会过度疲劳?",
|
|
|
- "有氧运动前要不要吃东西?",
|
|
|
- "椭圆机和跑步机哪个更适合新手?",
|
|
|
- "跳绳会不会伤膝盖?",
|
|
|
- "有氧运动后怎么补充能量?"
|
|
|
- ]
|
|
|
+ cate: "长跑训练",
|
|
|
+ img: "/assets/icon/distance.png",
|
|
|
+ messageList: [
|
|
|
+ "如何准备半程马拉松?",
|
|
|
+ "长跑时如何合理分配体力?",
|
|
|
+ "LSD训练的最佳距离是多少?",
|
|
|
+ "如何提高跑步耐力?",
|
|
|
+ "马拉松比赛补给策略"
|
|
|
+ ]
|
|
|
},
|
|
|
{
|
|
|
- cate:"减脂",img:"/assets/icon/jz.jpg",
|
|
|
- messageList:[
|
|
|
- "减脂一定要做有氧吗?",
|
|
|
- "为什么体重没变但看起来瘦了?",
|
|
|
- "局部减脂(如瘦肚子)真的存在吗?",
|
|
|
- "减脂期每天应该吃多少热量?",
|
|
|
- "低碳饮食和低脂饮食哪个更适合减脂?",
|
|
|
- "为什么运动后体重反而增加了?",
|
|
|
- "减脂期可以吃零食吗?",
|
|
|
- "平台期怎么突破?",
|
|
|
- "晚上吃东西会不会更容易长胖?",
|
|
|
- "减脂期要不要计算蛋白质摄入?"
|
|
|
+ cate: "速度提升",
|
|
|
+ img: "/assets/icon/speed.png",
|
|
|
+ messageList: [
|
|
|
+ "如何提升5公里跑成绩?",
|
|
|
+ "间歇训练应该怎么安排?",
|
|
|
+ "如何避免跑步速度平台期?",
|
|
|
+ "步频和步幅如何优化?",
|
|
|
+ "上坡跑训练技巧"
|
|
|
]
|
|
|
},
|
|
|
{
|
|
|
- cate:"增肌",img:"/assets/icon/zj.jpg",
|
|
|
+ cate: "伤痛预防",
|
|
|
+ img: "/assets/icon/injury.png",
|
|
|
messageList: [
|
|
|
- "增肌一定要喝蛋白粉吗?",
|
|
|
- "为什么练了很久肌肉不长?",
|
|
|
- "增肌期可以同时减脂吗?",
|
|
|
- "训练后多久补充蛋白质最有效?",
|
|
|
- "增肌需要每天练同一个部位吗?",
|
|
|
- "徒手训练(如俯卧撑)能有效增肌吗?",
|
|
|
- "增肌期体重不增长是怎么回事?",
|
|
|
- "肌肉酸痛还能继续练吗?",
|
|
|
- "增肌训练每组做多少次最合适?",
|
|
|
- "睡眠对增肌的影响有多大?"
|
|
|
- ]
|
|
|
+ "跑步膝盖疼怎么恢复?",
|
|
|
+ "如何预防跑步中的拉伤?",
|
|
|
+ "跑后拉伸的正确方法",
|
|
|
+ "足底筋膜炎如何缓解?",
|
|
|
+ "跑步鞋的选择技巧"
|
|
|
+ ]
|
|
|
},
|
|
|
- ]
|
|
|
+ ];
|
|
|
+
|
|
|
let ChatPrompt = Parse.Object.extend("ChatPrompt");
|
|
|
setTimeout(() => {
|
|
|
- chat.promptList = promptList.map(item=>{
|
|
|
+ chat.role.set("promptCates", promptCates);
|
|
|
+ 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 // 存在话题提示词时显示
|
|
|
- }
|
|
|
- },
|
|
|
- ]
|
|
|
-
|
|
|
+ {
|
|
|
+ title: "话题灵感",
|
|
|
+ showTitle: true,
|
|
|
+ icon: "color-wand-outline",
|
|
|
+ onClick: () => {
|
|
|
+ chat.isPromptModalOpen = true;
|
|
|
+ },
|
|
|
+ show: () => chat?.promptList?.length > 0
|
|
|
+ },
|
|
|
+ ];
|
|
|
},
|
|
|
- onMessage:(chat:FmodeChat,message:FmodeChatMessage)=>{
|
|
|
- console.log("onMessage",message)
|
|
|
- let content:any = message?.content
|
|
|
- if(typeof content == "string"){
|
|
|
- // 根据阶段标记判断下一步处理过程
|
|
|
+ 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:(chat:FmodeChat)=>{
|
|
|
- // chat?.chatSession?.id 本次会话的 chatId
|
|
|
- console.log("onChatSaved",chat,chat?.chatSession,chat?.chatSession?.id)
|
|
|
+ onChatSaved: (chat: FmodeChat) => {
|
|
|
+ console.log("onChatSaved", chat?.chatSession?.id);
|
|
|
}
|
|
|
- }
|
|
|
- openChatPanelModal(this.modalCtrl,options)
|
|
|
+ };
|
|
|
+ openChatPanelModal(this.modalCtrl, options);
|
|
|
}
|
|
|
|
|
|
- constructor(
|
|
|
- private modalCtrl:ModalController,
|
|
|
+ constructor(
|
|
|
+ private modalCtrl: ModalController,
|
|
|
) {
|
|
|
- addIcons({
|
|
|
- notificationsOutline, calendarNumber, checkmarkCircle, ellipseOutline,
|
|
|
- timeOutline, barbell, body, walk, add, refresh, fitness,
|
|
|
- flame, bicycle, trophy, ellipsisHorizontal
|
|
|
- });
|
|
|
+ addIcons({notificationsOutline,calendarNumber,refresh,footsteps,add,chatbubbleEllipses,checkmarkCircle,ellipseOutline,timeOutline,barbell,body,walk,fitness,flame,bicycle,trophy,ellipsisHorizontal,footstepsOutline});
|
|
|
+
|
|
|
+ // 初始化周数据
|
|
|
+ this.weekDays = this.generateWeekDays();
|
|
|
}
|
|
|
|
|
|
-
|
|
|
// 计算属性
|
|
|
get completedWorkouts(): number {
|
|
|
return this.weekDays.filter(day => day.trained).length;
|
|
@@ -203,55 +164,60 @@ export class Tab2Page {
|
|
|
}
|
|
|
|
|
|
get completionRate(): number {
|
|
|
- return this.completedWorkouts / this.totalWorkouts;
|
|
|
+ return this.completedWorkouts > 0 ? this.completedWorkouts / this.totalWorkouts : 0;
|
|
|
}
|
|
|
|
|
|
// 推荐计划
|
|
|
recommendedPlans = [
|
|
|
{
|
|
|
- name: '全身燃脂训练',
|
|
|
+ name: '5公里入门计划',
|
|
|
duration: 28,
|
|
|
- difficulty: '中级',
|
|
|
- icon: 'flame',
|
|
|
+ difficulty: '初级',
|
|
|
+ icon: 'footsteps',
|
|
|
isNew: true
|
|
|
},
|
|
|
{
|
|
|
- name: '核心力量提升',
|
|
|
+ name: '半马提升训练',
|
|
|
duration: 35,
|
|
|
- difficulty: '高级',
|
|
|
- icon: 'barbell',
|
|
|
+ difficulty: '中级',
|
|
|
+ icon: 'footstepsOutline',
|
|
|
isNew: false
|
|
|
},
|
|
|
{
|
|
|
- name: '瑜伽晨间唤醒',
|
|
|
+ name: '全马备战计划',
|
|
|
duration: 20,
|
|
|
- difficulty: '初级',
|
|
|
- icon: 'yoga',
|
|
|
+ difficulty: '高级',
|
|
|
+ icon: 'footsteps',
|
|
|
isNew: true
|
|
|
}
|
|
|
];
|
|
|
|
|
|
-
|
|
|
-
|
|
|
// 动态加载数据
|
|
|
async ngOnInit() {
|
|
|
await this.loadUserData();
|
|
|
}
|
|
|
+
|
|
|
isLoading = true;
|
|
|
currentUser: CloudUser | null = null;
|
|
|
myPlans: any[] = [];
|
|
|
- planTasks: any[] = [];
|
|
|
-
|
|
|
- // 周训练数据
|
|
|
- weekDays = [
|
|
|
- { shortName: '周一', name: 'Monday', trained: true, active: false },
|
|
|
- { shortName: '周二', name: 'Tuesday', trained: false, active: true },
|
|
|
- { shortName: '周三', name: 'Wednesday', trained: true, active: false },
|
|
|
- { shortName: '周四', name: 'Thursday', trained: false, active: false },
|
|
|
- { shortName: '周五', name: 'Friday', trained: false, active: false },
|
|
|
- { shortName: '周六', name: 'Saturday', trained: false, active: false },
|
|
|
- { shortName: '周日', name: 'Sunday', trained: false, active: false }
|
|
|
- ];
|
|
|
+
|
|
|
+ // 使用字典存储计划任务
|
|
|
+ planTasksMap: { [planId: string]: any[] } = {};
|
|
|
+
|
|
|
+ // 动态生成周数据
|
|
|
+ weekDays: any[] = [];
|
|
|
+ generateWeekDays() {
|
|
|
+ const days = ['周日', '周一', '周二', '周三', '周四', '周五', '周六'];
|
|
|
+ const today = new Date().getDay(); // 0-6 (0=周日)
|
|
|
+
|
|
|
+ return days.map((name, index) => ({
|
|
|
+ shortName: name,
|
|
|
+ name: days[index],
|
|
|
+ trained: Math.random() > 0.5, // 模拟数据,实际应从API获取
|
|
|
+ active: index === today
|
|
|
+ }));
|
|
|
+ }
|
|
|
+
|
|
|
async loadUserData() {
|
|
|
this.isLoading = true;
|
|
|
|
|
@@ -277,11 +243,11 @@ export class Tab2Page {
|
|
|
query.equalTo('user', this.currentUser.toPointer());
|
|
|
|
|
|
const plans = await query.find();
|
|
|
- console.log("this.loadTrainingPlans()",plans)
|
|
|
+
|
|
|
this.myPlans = plans.map(plan => ({
|
|
|
id: plan.id,
|
|
|
name: plan.get('planName') || '未命名计划',
|
|
|
- progress: 0, // 这里可以根据实际完成情况计算
|
|
|
+ progress: this.calculatePlanProgress(plan),
|
|
|
nextTime: this.getNextTrainingTime(plan),
|
|
|
remaining: this.getRemainingDays(plan),
|
|
|
icon: this.getPlanIcon(plan.get('fitnessGoals')),
|
|
@@ -292,7 +258,7 @@ export class Tab2Page {
|
|
|
|
|
|
// 加载每个计划的任务
|
|
|
for (const plan of plans) {
|
|
|
- if(typeof plan.id == "string"){
|
|
|
+ if (typeof plan.id === "string") {
|
|
|
await this.loadPlanTasks(plan.id);
|
|
|
}
|
|
|
}
|
|
@@ -303,37 +269,109 @@ export class Tab2Page {
|
|
|
query.equalTo('plan', planId);
|
|
|
|
|
|
const tasks = await query.find();
|
|
|
- console.log("tasks",tasks)
|
|
|
- this.planTasks = tasks
|
|
|
+ this.planTasksMap[planId] = tasks;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 获取某个计划的任务
|
|
|
+ getPlanTasks(planId: string): any[] {
|
|
|
+ return this.planTasksMap[planId] || [];
|
|
|
+ }
|
|
|
+
|
|
|
+ // 计算计划进度
|
|
|
+ calculatePlanProgress(plan: CloudObject): number {
|
|
|
+ const tasks = this.getPlanTasks(plan.id as string);
|
|
|
+ if (!tasks.length) return 0;
|
|
|
+
|
|
|
+ const completed = tasks.filter(task => task.get('isCompleted')).length;
|
|
|
+ return Math.round((completed / tasks.length) * 100);
|
|
|
}
|
|
|
|
|
|
getNextTrainingTime(plan: CloudObject): string {
|
|
|
- // 简单实现 - 实际应根据计划schedule计算
|
|
|
- return '明天 7:00';
|
|
|
+ // 模拟实现 - 实际应根据计划schedule计算
|
|
|
+ const days = ['明天', '后天', '大后天'];
|
|
|
+ const times = ['7:00', '8:30', '18:00'];
|
|
|
+ return `${days[Math.floor(Math.random() * days.length)]} ${times[Math.floor(Math.random() * times.length)]}`;
|
|
|
}
|
|
|
|
|
|
getRemainingDays(plan: CloudObject): number {
|
|
|
- // 简单实现 - 实际应根据计划duration和开始日期计算
|
|
|
+ // 模拟实现 - 实际应根据计划duration和开始日期计算
|
|
|
return Math.floor(Math.random() * 30) + 1;
|
|
|
}
|
|
|
|
|
|
getPlanIcon(goals: string[] = []): string {
|
|
|
+ if (goals.includes('跑步') || goals.includes('耐力')) return 'footsteps';
|
|
|
if (goals.includes('减脂')) return 'flame';
|
|
|
if (goals.includes('增肌')) return 'barbell';
|
|
|
- if (goals.includes('跑步')) return 'walk';
|
|
|
return 'fitness';
|
|
|
}
|
|
|
|
|
|
async refreshPlans() {
|
|
|
+ // 重置任务映射
|
|
|
+ this.planTasksMap = {};
|
|
|
await this.loadUserData();
|
|
|
}
|
|
|
|
|
|
openPlan(plan: any) {
|
|
|
console.log('打开计划:', plan);
|
|
|
- // 这里可以导航到计划详情页,传入plan和this.planTasks[plan.id]
|
|
|
+ // 实际应用中这里应该导航到计划详情页
|
|
|
+ // 可以传递 plan 和 this.getPlanTasks(plan.id)
|
|
|
+ }
|
|
|
+
|
|
|
+ // 创建新计划方法
|
|
|
+ async createNewPlan() {
|
|
|
+ const modal = await this.modalCtrl.create({
|
|
|
+ component: PlanCreationModalComponent,
|
|
|
+ breakpoints: [0, 0.5, 0.8],
|
|
|
+ initialBreakpoint: 0.8
|
|
|
+ });
|
|
|
+
|
|
|
+ await modal.present();
|
|
|
+
|
|
|
+ const { data } = await modal.onWillDismiss();
|
|
|
+
|
|
|
+ if (data) {
|
|
|
+ await this.saveNewPlan(data);
|
|
|
+ await this.refreshPlans();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 保存新计划
|
|
|
+ async saveNewPlan(planData: any) {
|
|
|
+ try {
|
|
|
+ // 确保当前用户存在
|
|
|
+ if (!this.currentUser || !this.currentUser.id) {
|
|
|
+ throw new Error('用户未登录,无法创建计划');
|
|
|
+ }
|
|
|
+
|
|
|
+ // 创建新的训练计划对象
|
|
|
+ const TrainingPlan = Parse.Object.extend('TrainingPlan');
|
|
|
+ const newPlan = new TrainingPlan();
|
|
|
+
|
|
|
+ // 设置计划属性
|
|
|
+ newPlan.set('planName', planData.name);
|
|
|
+ newPlan.set('description', planData.description);
|
|
|
+ newPlan.set('durationInWeeks', parseInt(planData.duration));
|
|
|
+ newPlan.set('difficultyLevel', planData.difficulty);
|
|
|
+ newPlan.set('fitnessGoals', planData.goals);
|
|
|
+
|
|
|
+ // 关联当前用户
|
|
|
+ newPlan.set('user', this.currentUser.toPointer());
|
|
|
+
|
|
|
+ // 保存到后端
|
|
|
+ await newPlan.save();
|
|
|
+
|
|
|
+ // 显示成功消息
|
|
|
+ this.showToast('计划创建成功!');
|
|
|
+ } catch (error: any) {
|
|
|
+ console.error('创建计划失败:', error);
|
|
|
+ this.showToast(error.message || '创建计划失败,请重试');
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
- createNewPlan() {
|
|
|
- console.log('创建新计划');
|
|
|
+ // 显示Toast消息
|
|
|
+ async showToast(message: string) {
|
|
|
+ // 实际项目中应该使用Ionic的ToastController
|
|
|
+ // 这里简化实现为alert
|
|
|
+ alert(message);
|
|
|
}
|
|
|
-}
|
|
|
+}
|