Browse Source

more better

wup55 1 week ago
parent
commit
8cd575f07f

+ 0 - 25
src/app/tab1/tab1.page.scss

@@ -270,31 +270,6 @@ ion-card:hover .card-image img {
   font-weight: 500;
 }
 
-/* 底部导航美化 */
-ion-tab-bar {
-  --background: white;
-  --border: 1px solid #e9ecef;
-  box-shadow: 0 -2px 10px rgba(0, 0, 0, 0.05);
-  padding: 8px 0;
-  height: 60px;
-}
-
-ion-tab-button {
-  --color: var(--ion-color-medium);
-  --color-selected: var(--ion-color-primary);
-  --ripple-color: transparent;
-  font-size: 10px;
-  transition: all 0.3s ease;
-}
-
-ion-tab-button.tab-selected ion-icon {
-  transform: translateY(-5px);
-}
-
-ion-icon {
-  font-size: 22px;
-  transition: transform 0.3s ease;
-}
 
 /* 动画效果 */
 @keyframes fadeIn {

+ 77 - 0
src/app/tab2/plan-creation-modal.component.html

@@ -0,0 +1,77 @@
+<ion-header>
+    <ion-toolbar>
+      <ion-buttons slot="start">
+        <ion-button (click)="cancel()">
+          <ion-icon slot="icon-only" name="close"></ion-icon>
+        </ion-button>
+      </ion-buttons>
+      <ion-title>创建新计划</ion-title>
+      <ion-buttons slot="end">
+        <ion-button (click)="create()" [disabled]="!newPlan.name">
+          <ion-icon slot="icon-only" name="save"></ion-icon>
+        </ion-button>
+      </ion-buttons>
+    </ion-toolbar>
+  </ion-header>
+  
+  <ion-content class="ion-padding">
+    <ion-item>
+      <ion-input 
+        label="计划名称" 
+        labelPlacement="floating" 
+        placeholder="例如:5公里入门计划"
+        [(ngModel)]="newPlan.name"
+      ></ion-input>
+    </ion-item>
+  
+    <ion-item>
+      <ion-textarea 
+        label="计划描述" 
+        labelPlacement="floating" 
+        placeholder="描述你的训练计划"
+        [(ngModel)]="newPlan.description"
+        autoGrow="true"
+      ></ion-textarea>
+    </ion-item>
+  
+    <ion-item>
+      <ion-select 
+        label="持续时间" 
+        labelPlacement="floating"
+        [(ngModel)]="newPlan.duration"
+      >
+        <ion-select-option value="2">2周</ion-select-option>
+        <ion-select-option value="4">4周</ion-select-option>
+        <ion-select-option value="8">8周</ion-select-option>
+        <ion-select-option value="12">12周</ion-select-option>
+      </ion-select>
+    </ion-item>
+  
+    <ion-item>
+      <ion-select 
+        label="难度级别" 
+        labelPlacement="floating"
+        [(ngModel)]="newPlan.difficulty"
+      >
+        <ion-select-option value="beginner">初级</ion-select-option>
+        <ion-select-option value="intermediate">中级</ion-select-option>
+        <ion-select-option value="advanced">高级</ion-select-option>
+      </ion-select>
+    </ion-item>
+  
+    <ion-item>
+      <ion-label>训练目标</ion-label>
+    </ion-item>
+  
+    <div class="goals-container">
+      <ion-item *ngFor="let goal of fitnessGoals">
+        <ion-icon [name]="goal.icon" slot="start" color="primary"></ion-icon>
+        <ion-label>{{ goal.name }}</ion-label>
+        <ion-checkbox 
+          slot="end" 
+          [checked]="newPlan.goals.includes(goal.value)"
+          (ionChange)="toggleGoal(goal.value)"
+        ></ion-checkbox>
+      </ion-item>
+    </div>
+  </ion-content>

+ 25 - 0
src/app/tab2/plan-creation-modal.component.scss

@@ -0,0 +1,25 @@
+ion-content {
+    --background: #f8f9fa;
+  }
+  
+  .goals-container {
+    background: white;
+    border-radius: 12px;
+    overflow: hidden;
+    margin: 16px 0;
+    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
+    
+    ion-item {
+      --border-color: transparent;
+      --padding-start: 16px;
+      --padding-end: 16px;
+      
+      ion-icon {
+        font-size: 24px;
+      }
+    }
+  }
+  
+  ion-button[disabled] {
+    opacity: 0.5;
+  }

+ 77 - 0
src/app/tab2/plan-creation-modal.component.ts

@@ -0,0 +1,77 @@
+import { Component } from '@angular/core';
+import { FormsModule } from '@angular/forms'; // 关键修复
+import { ModalController } from '@ionic/angular/standalone';
+import { IonHeader, IonToolbar, IonTitle, IonButtons, 
+         IonButton, IonIcon, IonContent, IonItem, 
+         IonInput, IonTextarea, IonSelect, IonSelectOption,
+         IonLabel, IonCheckbox } from '@ionic/angular/standalone';
+import { addIcons } from 'ionicons';
+import { close, save, barbell, body, walk, flame, bicycle } from 'ionicons/icons';
+
+@Component({
+  selector: 'app-plan-creation-modal',
+  templateUrl: './plan-creation-modal.component.html',
+  styleUrls: ['./plan-creation-modal.component.scss'],
+  standalone: true,
+  imports: [
+    // 添加 FormsModule 以支持 ngModel
+    FormsModule,
+    
+    // Ionic组件
+    IonHeader, IonToolbar, IonTitle, IonButtons, IonButton, IonIcon,
+    IonContent, IonItem, IonInput, IonTextarea, IonSelect, IonSelectOption,
+    IonLabel, IonCheckbox
+  ]
+})
+export class PlanCreationModalComponent {
+  // 修复类型问题:明确指定goals为字符串数组
+  newPlan = {
+    name: '',
+    description: '',
+    duration: 4,
+    difficulty: 'beginner',
+    goals: [] as string[], // 明确指定为字符串数组
+    icon: 'fitness'
+  };
+
+  fitnessGoals = [
+    { name: '跑步', value: 'running', icon: 'walk' },
+    { name: '减脂', value: 'fat_loss', icon: 'flame' },
+    { name: '增肌', value: 'muscle_gain', icon: 'barbell' },
+    { name: '耐力', value: 'endurance', icon: 'bicycle' },
+    { name: '柔韧性', value: 'flexibility', icon: 'body' }
+  ];
+
+  constructor(private modalCtrl: ModalController) {
+    addIcons({ close, save, barbell, body, walk, flame, bicycle });
+  }
+
+  cancel() {
+    this.modalCtrl.dismiss(null, 'cancel');
+  }
+
+  create() {
+    this.modalCtrl.dismiss(this.newPlan, 'confirm');
+  }
+
+  // 修复类型问题:明确goal为字符串
+  toggleGoal(goal: string) {
+    const index = this.newPlan.goals.indexOf(goal);
+    if (index > -1) {
+      this.newPlan.goals.splice(index, 1);
+    } else {
+      this.newPlan.goals.push(goal);
+    }
+    
+    // 自动选择图标
+    if (this.newPlan.goals.includes('running')) {
+      this.newPlan.icon = 'walk';
+    } else if (this.newPlan.goals.includes('fat_loss')) {
+      this.newPlan.icon = 'flame';
+    } else if (this.newPlan.goals.includes('muscle_gain')) {
+      this.newPlan.icon = 'barbell';
+    } else {
+      this.newPlan.icon = 'fitness';
+    }
+  }
+}

+ 33 - 57
src/app/tab2/tab2.page.html

@@ -78,58 +78,62 @@
           <h3>{{item.name}}</h3>
           <p>{{item.progress}}% 完成 · 下次训练: {{item.nextTime}}</p>
           <p *ngIf="item.description" class="plan-description">{{item.description}}</p>
+          
+          <!-- 任务摘要 -->
+          <div class="task-summary">
+            <ion-chip *ngFor="let task of getPlanTasks(item.id)" color="light">
+              <ion-icon name="footsteps" slot="start"></ion-icon>
+              <ion-label>{{task.get('taskName')}}</ion-label>
+            </ion-chip>
+          </div>
         </ion-label>
         <ion-badge slot="end" color="light">{{item.remaining}}天</ion-badge>
       </ion-item>
     </ion-list>
 
     <div class="empty-state" *ngIf="!isLoading && myPlans.length === 0">
-      <ion-icon name="fitness"></ion-icon>
-      <p>暂无训练计划</p>
-      <ion-button fill="outline" (click)="createNewPlan()">创建新计划</ion-button>
+  
+      <h3>还没有训练计划</h3>
+      <p>创建一个个性化计划开始你的跑步旅程</p>
+      <ion-button expand="block" fill="solid" color="primary" (click)="createNewPlan()">
+        <ion-icon slot="start" name="add"></ion-icon>
+        创建新计划
+      </ion-button>
     </div>
 
-
-    <!-- 任务列表 -->
-    <ion-list-header>
-      <ion-label>任务列表</ion-label>
-    </ion-list-header>
-
-    <ion-list lines="none">
-      <ion-item *ngFor="let task of planTasks">
-        <ion-label>
-          <h3>{{task.get('taskName')}}</h3>
-          <p>{{task.get('description')}}</p>
-          <p>类型: {{task.get('taskType')}} | 预计时长: {{task.get('estimatedDuration')}}分钟</p>
-          <p *ngIf="task.get('isCompleted')">状态: 已完成</p>
-          <p *ngIf="!task.get('isCompleted')">状态: 未完成</p>
-          <p>目标心率: {{task.get('targetMetrics').heartRate}}</p>
-        </ion-label>
-      </ion-item>
-    </ion-list>
-
  </div>
   <!-- AI咨询 -->
   <ion-list-header>
     <ion-label>私教咨询(免费)</ion-label>
-
   </ion-list-header>
 
-  <ion-card class="coach-card">
+  <ion-card class="coach-card running-theme">
       <div class="avatar-container">
-        <img src="/assets/avatars/jiaolian1.jpg" alt="教练头像" class="coach-avatar" />
+        <img src="/assets/avatars/R-C.jpg" alt="跑步教练" class="coach-avatar" />
+        <div class="sport-badge">跑步专家</div>
       </div>
 
       <ion-card-header>
-        <ion-card-title class="coach-name">宋珀尔</ion-card-title>
-        <ion-card-subtitle class="coach-title">专业教练</ion-card-subtitle>
+        <ion-card-title class="coach-name">小迫桀</ion-card-title>
+        <ion-card-subtitle class="coach-title">前马拉松运动员</ion-card-subtitle>
       </ion-card-header>
 
       <ion-card-content>
+        <div class="stats-grid">
+          <div class="stat-item">
+            <div class="stat-value">12年</div>
+            <div class="stat-label">执教经验</div>
+          </div>
+          <div class="stat-item">
+            <div class="stat-value">50+</div>
+            <div class="stat-label">学员PB</div>
+          </div>
+        </div>
+        
         <div class="specialties-section">
           <ion-note color="medium" class="section-title">擅长运动</ion-note>
           <div class="specialties-container">
-            <ion-chip *ngFor="let specialty of ['跑步', '动感单车']" class="specialty-chip">
+            <ion-chip *ngFor="let specialty of ['长跑', '间歇训练', '跑姿矫正']" class="specialty-chip">
               <ion-label>{{ specialty }}</ion-label>
             </ion-chip>
           </div>
@@ -142,38 +146,10 @@
       </ion-card-content>
     </ion-card>
 
-
-  <ion-list-header>
-    <ion-label>我的计划</ion-label>
-    <ion-button fill="clear" size="small" (click)="refreshPlans()">
-      <ion-icon name="refresh" slot="start"></ion-icon>
-      刷新
-    </ion-button>
-  </ion-list-header>
-
-  <ion-list lines="none">
-    <ion-item *ngFor="let item of myPlans" [detail]="true" (click)="openPlan(item)">
-      <ion-avatar slot="start">
-        <ion-icon [name]="item.icon" color="primary"></ion-icon>
-      </ion-avatar>
-      <ion-label>
-        <h3>{{item.name}}</h3>
-        <p>{{item.progress}}% 完成 · 下次训练: {{item.nextTime}}</p>
-      </ion-label>
-      <ion-badge slot="end" color="light">{{item.remaining}}天</ion-badge>
-    </ion-item>
-  </ion-list>
-
-  <div class="empty-state" *ngIf="myPlans.length === 0">
-    <ion-icon name="fitness"></ion-icon>
-    <p>暂无训练计划</p>
-    <ion-button fill="outline" (click)="createNewPlan()">创建新计划</ion-button>
-  </div>
-
   <!-- 底部添加按钮 -->
   <ion-fab vertical="bottom" horizontal="end" slot="fixed">
     <ion-fab-button (click)="createNewPlan()">
       <ion-icon name="add"></ion-icon>
     </ion-fab-button>
   </ion-fab>
-</ion-content>
+</ion-content>

+ 68 - 0
src/app/tab2/tab2.page.scss

@@ -276,4 +276,72 @@ ion-list {
   --padding-start: 8px;
   --padding-end: 8px;
   --color: #6b47d6;
+}
+@media (max-width: 768px) {
+  .coach-card {
+    max-width: 100%;
+    margin: 10px;
+  }
+  
+  .stats-grid {
+    flex-direction: column;
+    gap: 10px;
+  }
+}
+
+/* 任务摘要样式 */
+.task-summary {
+  display: flex;
+  flex-wrap: wrap;
+  gap: 8px;
+  margin-top: 10px;
+  
+  ion-chip {
+    --background: #f1f5f9;
+    --color: #334155;
+    font-size: 0.8rem;
+    height: 24px;
+    
+    ion-icon {
+      color: #1890ff;
+      font-size: 14px;
+    }
+  }
+}
+
+/* 计划描述 */
+.plan-description {
+  font-size: 0.9rem;
+  color: #666;
+  margin: 6px 0;
+}
+
+.empty-state {
+  text-align: center;
+  padding: 40px 20px;
+  
+  .empty-image {
+    width: 150px;
+    height: 150px;
+    margin-bottom: 20px;
+    opacity: 0.7;
+  }
+  
+  h3 {
+    font-size: 1.2rem;
+    font-weight: 600;
+    color: #333;
+    margin-bottom: 10px;
+  }
+  
+  p {
+    color: #666;
+    margin-bottom: 25px;
+    font-size: 0.95rem;
+  }
+  
+  ion-button {
+    max-width: 300px;
+    margin: 0 auto;
+  }
 }

+ 201 - 163
src/app/tab2/tab2.page.ts

@@ -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);
   }
-}
+}

+ 1 - 1
src/app/tab3/tab3.page.ts

@@ -203,7 +203,7 @@ export class Tab3Page {
     addIcons({ 
       bicycleOutline, 
       walkOutline, 
-      barbellOutline, 
+      barbellOutline,
       playCircleOutline,
       tvOutline,
       trophyOutline,

+ 2 - 2
src/app/tabs/tabs.routes.ts

@@ -28,14 +28,14 @@ export const routes: Routes = [
       },
       {
         path: '',
-        redirectTo: '/tabs/tab2',
+        redirectTo: '/tabs/tab1',
         pathMatch: 'full',
       },
     ],
   },
   {
     path: '',
-    redirectTo: '/tabs/tab2',
+    redirectTo: '/tabs/tab1',
     pathMatch: 'full',
   },
 ];