11 Коміти 81fc121c3f ... fe37507327

Автор SHA1 Опис Дата
  xukang fe37507327 merge branch 'xk' 3 місяців тому
  xukang 96c28fd17f fix:大bug 3 місяців тому
  xukang d3c72299fe feat:完善了打卡页面。 3 місяців тому
  xukang 89138169d5 update:更新样式 3 місяців тому
  xukang 9065a1c148 fix:修复页面无法正确回跳问题 3 місяців тому
  xukang 37602ec314 fix:bugs 3 місяців тому
  xukang e53e065478 update: 打卡页面雏形 4 місяців тому
  xukang fd0d592c66 update:更新了计划的显示顺序,图像生成逻辑。 4 місяців тому
  xukang 5b0a5fbfe9 fix: fix some potential page bugs 4 місяців тому
  xukang 2b46b8a726 fix:fixed some potential page bugs 4 місяців тому
  xukang 2b50110c72 fix:修复了一直访问服务器的问题 4 місяців тому

+ 28 - 27
TFPower-app/src/app/tab2/agent/agent.json.ts

@@ -42,45 +42,46 @@ export function extactAndParseJsonFromString(inputString: string) {
         return {}
     }
 }
+/**
+ * 从字符串中提取所有 JSON 对象并解析
+ * 
+ * @param inputString 输入的字符串
+ * @returns 返回一个包含所有 JSON 对象的数组
+ */
 export function extractAllJsonFromString(inputString: string) {
-    let startIndex = inputString.indexOf("{");
-    let count = 0;
-    let jsonObjects = [];  // 用来存储所有提取的JSON对象
+    const jsonObjects: any[] = [];
+    let startIndex = -1;  // 用于记录 JSON 开始的位置
+    let count = 0;        // 用于记录花括号的数量(平衡)
 
-    while (startIndex !== -1) {
-        let endIndex = startIndex;
-        count = 0;
+    // 遍历整个字符串,查找并提取所有 JSON 对象
+    for (let i = 0; i < inputString.length; i++) {
+        const char = inputString[i];
 
-        // 遍历字符串,计算花括号平衡
-        for (let i = startIndex; i < inputString.length; i++) {
-            if (inputString[i] === "{") {
-                count++;
-            } else if (inputString[i] === "}") {
-                count--;
-            }
-            // 找到完整的JSON数据
-            if (count === 0) {
-                endIndex = i;
-                break;
-            }
+        // 找到 JSON 对象的开始
+        if (char === '{' && count === 0) {
+            startIndex = i;  // 记录 JSON 开始的位置
         }
 
-        // 如果找到了平衡的花括号,提取JSON并解析
-        if (count === 0) {
-            const jsonString = inputString.slice(startIndex, endIndex + 1);
+        // 计算花括号的平衡
+        if (char === '{') {
+            count++;
+        } else if (char === '}') {
+            count--;
+        }
+
+        // 如果花括号平衡,表示找到了一个完整的 JSON 对象
+        if (count === 0 && startIndex !== -1) {
+            const jsonString = inputString.slice(startIndex, i + 1);
             try {
                 const jsonObject = JSON.parse(jsonString);
-                jsonObjects.push(jsonObject);  // 将解析的对象添加到数组中
+                jsonObjects.push(jsonObject);  // 将解析的 JSON 对象添加到数组中
             } catch (error) {
                 console.error("Failed to parse JSON:", error);
             }
-
-            // 更新startIndex,继续查找下一个 JSON
-            startIndex = inputString.indexOf("{", endIndex + 1);
-        } else {
-            break;
+            startIndex = -1;  // 重置 startIndex,继续查找下一个 JSON 对象
         }
     }
 
     return jsonObjects;
 }
+

+ 2 - 2
TFPower-app/src/app/tab2/agent/tasks/poem/poem-desc.ts

@@ -25,8 +25,8 @@ export function TaskPoemPictureDesc(options: {
             }
 
             // 文本生成
-            let PromptTemplate = `您是一名专业的健身教练,请根据以下描述生成一个简单易懂的健身动作教学画面,包括用箭头语言标记动作细节等
-                描述如下:
+            let PromptTemplate = `您是一名专业的健身教练,请根据以下要求简单易懂地描述一个健身动作的动作步骤
+                要求如下:
                 ${userInput['目标部位']}${userInput['设备限制']},请确保动作正确。
                 `
             let completion = new FmodeChatCompletion([

+ 2 - 2
TFPower-app/src/app/tab2/agent/tasks/poem/poem-picture.ts

@@ -20,11 +20,11 @@ export function TaskPoemPictureCreate(options: {
       // console.log("意境绘制:执行过程")
 
       if (!options.shareData.PictureDescResult) {
-        task2.error = "缺少古风描述结果,请重新执行。"
+        task2.error = "缺少动作描述结果,请重新执行。"
         resolve(false)
       }
       let imagineWork = new ImagineWork();
-      let PicturePrompt = `${options.shareData.PictureDescResult}\n特点:动作标准清晰易懂。`
+      let PicturePrompt = `提取出下面描述中的动作步骤,描绘出一张简单的标准动作图片。描述如下:${options.shareData.PictureDescResult}\n`
       let imgOptions: DalleOptions = { prompt: PicturePrompt }
       let countDownInt = setInterval(() => {
         task2.progress += 0.01

+ 39 - 18
TFPower-app/src/app/tab2/edit-plan-modal/edit-plan-modal.component.html

@@ -1,9 +1,8 @@
 <ion-header>
   <ion-toolbar>
-    <ion-title>编辑计划</ion-title>
-    <ion-buttons slot="end">
-      <ion-button (click)="dismiss()">关闭</ion-button>
-    </ion-buttons>
+    <ion-title>
+      <ion-icon name="createOutline" style="margin-right: 5px;"></ion-icon>编辑计划
+    </ion-title>
   </ion-toolbar>
 </ion-header>
 
@@ -15,38 +14,60 @@
     <ion-card-content>
       <!-- 日期输入框 -->
       <ion-item lines="none">
-        <ion-input label="日期" [value]="plan.get('date')" (ionBlur)="onChange('date', $event)"></ion-input>
+        <ion-input label="日期:" [value]="plan.get('date')" (ionBlur)="onChange('date', $event)" placeholder="请输入日期">
+        </ion-input>
       </ion-item>
-
       <!-- 部位输入框 -->
       <ion-item lines="none">
-        <ion-input label="部位" [value]="plan.get('trainingPart')" (ionBlur)="onChange(  'trainingPart', $event)">
-        </ion-input>
+        <ion-input label="部位" [value]="plan.get('trainingPart')" (ionBlur)="onChange(  'trainingPart', $event)"
+          placeholder="请输入训练部位"></ion-input>
       </ion-item>
-
       <!-- 训练计划 -->
       <ion-label>训练计划</ion-label>
+
       <ion-row *ngFor="let task of plan.get('trainingItems'); let i = index" class="plan-row">
-        <ion-col size="4" class="plan-column">
+        <ion-col size="12" size-md="4" class="custom-col">
           <ion-item lines="none">
-            <ion-input label="项目{{i + 1}}" [value]="task.item || ''" (ionBlur)="onTaskChange(i, 'item', $event)">
-            </ion-input>
+            <ion-input label="项目{{i + 1}}" [value]="task.item || ''" (ionBlur)="onTaskChange(i, 'item', $event)"
+              placeholder="请输入项目名称"></ion-input>
           </ion-item>
         </ion-col>
-        <ion-col size="3">
+        <ion-col size="6" size-md="3" class="custom-col">
           <ion-item lines="none">
-            <ion-input label="组数" [value]="task.sets" (ionBlur)="onTaskChange(i, 'sets', $event)"></ion-input>
+            <ion-input label="组数:" [value]="task.sets" (ionBlur)="onTaskChange(i, 'sets', $event)" placeholder="请输入组数">
+            </ion-input>
           </ion-item>
         </ion-col>
-        <ion-col size="3">
+        <ion-col size="6" size-md="3" class="custom-col">
           <ion-item lines="none">
-            <ion-input label="次数" [value]="task.reps" (ionBlur)="onTaskChange(i, 'reps', $event)"></ion-input>
+            <ion-input label="次数:" [value]="task.reps" (ionBlur)="onTaskChange(i, 'reps', $event)" placeholder="请输入次数">
+            </ion-input>
           </ion-item>
         </ion-col>
       </ion-row>
 
-      <!-- 保存按钮 -->
-      <ion-button expand="full" color="primary" (click)="saveChanges()">保存修改</ion-button>
+      <ion-grid>
+        <ion-row>
+          <!-- 左侧按钮 -->
+
+          <ion-col size="6">
+            <ion-button expand="full" color="light" shape="round" (click)="dismiss()" class="discard-btn">
+              <ion-icon name="close-circle-outline"></ion-icon>
+              取消保存
+            </ion-button>
+          </ion-col>
+          <!-- 右侧按钮 -->
+
+          <ion-col size="6">
+            <ion-button expand="full" (click)="saveChanges()" style="--background: #009b7d;" shape="round"
+              class="confirm-btn">
+              <ion-icon name="checkmark-circle-outline"></ion-icon>
+              保存修改
+            </ion-button>
+
+          </ion-col>
+        </ion-row>
+      </ion-grid>
     </ion-card-content>
   </ion-card>
 </ion-content>

+ 6 - 5
TFPower-app/src/app/tab2/edit-plan-modal/edit-plan-modal.component.scss

@@ -1,15 +1,11 @@
-.plan-row {
-  margin-bottom: 15px;
-}
 
 .plan-column {
   display: flex;
   flex-direction: column;
   justify-content: space-between;
 }
-
 ion-item {
-  margin-bottom: 5px;
+  margin-bottom: -5px;  /* 调整间距的值,根据需要改变 */
 }
 .horizontal-item {
   display: flex;
@@ -24,3 +20,8 @@ ion-item {
 .horizontal-item ion-input {
   flex: 1 1 auto; /* 输入框扩展占满剩余空间 */
 }
+.custom-col
+{
+--ion-grid-column-padding: 0;
+}
+

+ 40 - 2
TFPower-app/src/app/tab2/edit-plan-modal/edit-plan-modal.component.ts

@@ -1,14 +1,18 @@
 import { Component, Input } from '@angular/core';
-import { ModalController } from '@ionic/angular/standalone';
+import { AlertController, IonGrid, IonIcon, ModalController } from '@ionic/angular/standalone';
 import { IonRow, IonCol, IonHeader, IonToolbar, IonTitle, IonButtons, IonButton, IonContent, IonCard, IonCardHeader, IonCardTitle, IonCardContent, IonItem, IonLabel, IonInput, IonToggle } from '@ionic/angular/standalone';
 import { FormsModule } from '@angular/forms';
 import { CommonModule } from '@angular/common';
+import { addIcons } from 'ionicons';
+import { closeCircleOutline, createOutline, checkmarkCircleOutline } from 'ionicons/icons';
 @Component({
   selector: 'app-edit-plan-modal',
   templateUrl: './edit-plan-modal.component.html',
   styleUrls: ['./edit-plan-modal.component.scss'],
   standalone: true,
   imports: [
+    IonIcon,
+    IonGrid,
     IonRow,
     IonCol,
     IonHeader,
@@ -31,7 +35,9 @@ import { CommonModule } from '@angular/common';
 })
 export class EditPlanModalComponent {
   @Input() plan: any;  // 接收传入的计划数据
-  constructor(private modalCtrl: ModalController) { }
+  constructor(private modalCtrl: ModalController, private alertController: AlertController) {
+    addIcons({ closeCircleOutline, checkmarkCircleOutline, createOutline });
+  }
   // 更新数据字段
   onChange(field: string, event: any) {
     const value = event.target.value;
@@ -55,6 +61,38 @@ export class EditPlanModalComponent {
       console.error('保存失败:', error);
     }
   }
+  async deletePlan(day: any) {
+    const alert = await this.alertController.create({
+      header: '确认删除',
+      message: '确定要删除此计划吗?',
+      buttons: [
+        {
+          text: '取消',
+          role: 'cancel',
+          cssClass: 'secondary',
+          handler: () => {
+            console.log('删除操作被取消');
+          }
+        },
+        {
+          text: '确认',
+          handler: () => {
+            day.destroy()
+              .then(() => {
+                console.log('计划已删除');
+
+                this.dismiss();
+              })
+              .catch((error: any) => {
+                console.error('删除失败:', error);
+              });
+          }
+        }
+      ]
+    });
+
+    await alert.present();
+  }
   dismiss() {
     this.modalCtrl.dismiss();
   }

+ 266 - 149
TFPower-app/src/app/tab2/tab2.page.html

@@ -19,17 +19,82 @@
 
 <ion-content [fullscreen]="true">
   <div style="height: 56px;"></div>
-
   <!-- 打卡 -->
-  <div *ngIf="selectedTab === 'checkin'" class="checkin-container">
-    <ion-card>
-      <ion-card-header>
-        <ion-card-title>今天的打卡任务</ion-card-title>
-      </ion-card-header>
-      <ion-card-content>
-        <p>在这里打卡,开始你的一天健身之旅吧!</p>
+  <div *ngIf="selectedTab === 'checkin'" class="checkin">
+    <ng-container *ngIf="planUser && planUser.length > 0; else noUserContent">
+      <ion-card-content *ngFor="let user of planUser">
+        <ion-card>
+          <ion-card-header>
+            <ion-card-title>欢迎回来!{{ user.get('name') }}</ion-card-title>
+          </ion-card-header>
+          <ion-card-content>
+            <div class="greeting-container">
+              <div class="avatar-info-container">
+                <div class="avatar-container">
+                  <img [src]="user.get('avater')" alt="用户头像" class="avatar">
+                </div>
+
+                <div class="user-info">
+                  <ion-card>
+                    <ion-card-content>
+                      <p><strong>身高:</strong>{{ user.get('height') }} cm</p>
+                      <p><strong>体重:</strong>{{ user.get('weight') }} kg</p>
+                      <p><strong>BMI:</strong>{{ calculateBMI(user.get('height'), user.get('weight')) }}</p>
+                      <p><strong>运动目标:</strong>{{ user.get('fitnessGoals') }}</p>
+                    </ion-card-content>
+                  </ion-card>
+                  <ion-label>{{ getEncouragement(calculateBMI(user.get('height'), user.get('weight'))) }}</ion-label>
+                </div>
+              </div>
+            </div>
+          </ion-card-content>
+        </ion-card>
+
+        <ion-card>
+          <ion-card-header>
+            <ion-card-title>打卡区域</ion-card-title>
+            <div class="power-label">
+              <strong>我的动能</strong>
+              <p class="stat-value">{{ user.get('power') }}</p>
+            </div>
+          </ion-card-header>
+          <ion-card-content>
+            <ion-datetime [value]="realDate.toISOString()" (ionChange)="onDateChange($event)">
+            </ion-datetime>
+            <div class="card-info">
+              <ion-button [disabled]="getButtonState(realDate).isDisabled"
+                (click)="getButtonState(realDate).buttonText === '补签' ? handleMakeupClick() : markAttendance()"
+                class="check">
+                {{ getButtonState(realDate).buttonText }}
+              </ion-button>
+            </div>
+            <div class="card-stats">
+              <div class="stat-item">
+                <p><strong>已打卡天数</strong></p>
+                <p class="stat-value">{{ user.get('days') }}</p>
+              </div>
+              <div class="stat-item">
+                <p><strong>连续打卡天数</strong></p>
+                <p class="stat-value">{{ user.get('sucdays') }}</p>
+              </div>
+            </div>
+          </ion-card-content>
+        </ion-card>
       </ion-card-content>
-    </ion-card>
+    </ng-container>
+
+    <ng-template #noUserContent>
+      <ion-card>
+        <ion-card-header>
+          <ion-card-title>没有用户数据</ion-card-title>
+        </ion-card-header>
+        <ion-card-content>
+          <p>当前没有用户信息,请稍后再试。</p>
+        </ion-card-content>
+      </ion-card>
+    </ng-template>
+
+
     <ion-card>
       <!-- 未登录 -->
       @if(!currentUser?.id){
@@ -57,165 +122,217 @@
         }
       </ion-card-content>
     </ion-card>
+
   </div>
 
   <!-- 计划 -->
-  <div *ngIf="selectedTab === 'plan'" class="plan-container">
-    <ion-card>
-      <ion-card-header>
-        <ion-card-title>我的本周计划</ion-card-title>
-      </ion-card-header>
-      <ion-card-content>
+  <div *ngIf="selectedTab === 'plan'" class="plan">
+
+
+    <ion-card-content>
+      <ion-card>
+        <ion-card-header>
+          <ion-card-title>我的本周计划</ion-card-title>
+        </ion-card-header>
+
         <div class="plan-table">
           <ion-grid class="table">
             <ion-row>
-              <ion-col size="1" class="grid-header">日期</ion-col>
-              <ion-col size="1" class="grid-header">部位</ion-col>
+              <ion-col size="1.5" class="grid-header">日期</ion-col>
+              <ion-col size="1.5" class="grid-header">部位</ion-col>
               <ion-col size="1.5" class="grid-header">项目1</ion-col>
-              <ion-col size="1.5" class="grid-header">项目2</ion-col>
-              <ion-col size="1.5" class="grid-header">项目3</ion-col>
-              <ion-col size="1.5" class="grid-header">项目4</ion-col>
-              <ion-col size="1.5" class="grid-header">状态</ion-col>
-              <ion-col size="2" class="grid-header">操作</ion-col> <!-- 操作列 -->
+              <ion-col size="2.5" class="grid-header">项目2</ion-col>
+              <ion-col size="2.5" class="grid-header">项目3</ion-col>
+              <ion-col size="2.5" class="grid-header">项目4</ion-col>
             </ion-row>
 
             <!-- 绑定计划数据 -->
             <ion-row *ngFor="let day of planList">
-              <ion-col size="1" class="plan-column">{{ day.get('date') }}</ion-col>
-              <ion-col size="1" class="plan-column">{{ day.get('trainingPart') }}</ion-col>
-
-              <!-- 显示每个训练项目,确保即使为空也占位 -->
-              <ion-col size="1.5" *ngFor="let task of day.get('trainingItems'); let i = index" class="plan-column">
-                {{ task.item || '' }}: {{task.sets}} x {{task.reps}}
-              </ion-col>
-
-              <ion-col size="1.5" class="plan-column">
-                <ion-list>
-                  <ion-item>
-                    <ion-select aria-label="fruit" value="未完成">
-                      <ion-select-option value="未完成">未完成</ion-select-option>
-                      <ion-select-option value="已完成">已完成</ion-select-option>
-                      <ion-select-option value="修改中">修改中</ion-select-option>
-                    </ion-select>
-                  </ion-item>
-                </ion-list>
-              </ion-col>
-
-              <!-- 修改和删除按钮 -->
-              <ion-col size="2" class="ion-text-center">
-                <ion-buttons>
-                  <ion-button color="success" (click)="editPlan(day)">
-                    <ion-icon slot="start" name="create"></ion-icon> 修改
-                  </ion-button>
-                  <ion-button color="danger" (click)="deletePlan(day)">
-                    <ion-icon slot="start" name="trash"></ion-icon> 删除
-                  </ion-button>
-                </ion-buttons>
-              </ion-col>
+              <ion-item-sliding>
+                <ion-item>
+                  <!-- 显示计划内容 -->
+                  <ion-col size="1" class="plan-column">{{ day.get('date') }}</ion-col>
+                  <ion-col size="1" class="plan-column">{{ day.get('trainingPart') }}</ion-col>
+                  <!-- 显示每个训练项目,确保即使为空也占位 -->
+                  <ion-col size="2.5" *ngFor="let task of day.get('trainingItems'); let i = index" class="plan-column">
+                    <div class="task-container">
+                      <span class="task-item">{{ task.item || '' }}</span>
+                      <span class="sets-reps">{{ task.sets }} x {{ task.reps }}</span>
+                    </div>
+                  </ion-col>
+                </ion-item>
+                <!-- 滑动时显示的按钮,设置 side="end" 来将按钮显示在右边 -->
+                <ion-item-options side="end">
+                  <ion-item-option class="edit-btn" color="primary" shape="round" (click)="editPlan(day)">编辑
+                  </ion-item-option>
+                  <ion-item-option class="delete-btn" color="danger" (click)="deletePlan(day)">删除</ion-item-option>
+                </ion-item-options>
+
+              </ion-item-sliding>
             </ion-row>
           </ion-grid>
         </div>
-
-        <ion-button expand="full" color="primary" (click)="goToPage('test-page')">重新生成计划</ion-button>
-      </ion-card-content>
-    </ion-card>
+      </ion-card>
+      <ion-card-subtitle>
+        <ion-icon name="alert-circle-outline" style="margin-right: 5px;"></ion-icon>左滑行内数据可对计划进行修改哦!
+      </ion-card-subtitle>
+      <ion-button expand="full" shape="round" class="reverse" (click)="regeneratePlan()">
+        <ion-icon name="infinite-outline" style="margin-right:5px ;"></ion-icon>重新生成计划
+      </ion-button>
+    </ion-card-content>
   </div>
-
   <!-- 问诊 -->
   <div *ngIf="selectedTab === 'consultation'" class="consult">
-    <!-- 健康问诊卡片 -->
-    <ion-card>
-      <ion-card-header>
-        <ion-card-title>健康问诊</ion-card-title>
-      </ion-card-header>
-      <ion-card-content>
-        <p>有任何不适,可以随时询问!</p>
-
-        <!-- 健身建议和疼痛咨询左右分布 -->
-        <ion-row>
-          <!-- 健身建议 -->
-          <ion-col size="6" class="ion-text-center">
-            <ion-img src="../../assets/images/action.png" alt="健身建议"></ion-img>
-            <ion-button expand="full" color="secondary" (click)="doPoemTask()">生成健身动作</ion-button>
-          </ion-col>
-
-          <!-- 疼痛咨询 -->
-          <ion-col size="6" class="ion-text-center">
-            <ion-img src="../../assets/images/ache.jpg" alt="疼痛咨询"></ion-img>
-            <ion-button expand="full" color="tertiary" (click)="doInqueryTask()">疼?点这里!</ion-button>
-          </ion-col>
-        </ion-row>
-
-        <!-- 任务区域 -->
-        <ion-card>
-          <ion-card-header>
-            <ion-card-title>健康任务</ion-card-title>
-          </ion-card-header>
-          <ion-card-content>
-            <div *ngFor="let step of taskList">
-              <ion-item>
-                <!-- 待开始 -->
-                <ion-icon *ngIf="step.progress === 0 && !step.error" name="radio-button-off-outline"></ion-icon>
-                <!-- 进行中 -->
-                <ion-icon *ngIf="step.progress !== 0 && step.progress !== 1" name="reload-outline"></ion-icon>
-                <!-- 已完成 -->
-                <ion-icon *ngIf="step.progress === 1" name="checkmark-circle-outline"></ion-icon>
-                <!-- 已出错 -->
-                <ion-icon *ngIf="step.error" name="close-circle-outline"></ion-icon>
-                {{ step.title }}
-                <span *ngIf="step.progress">{{ step.progress * 100 | number:'2.0-0' }}%</span>
-                <span *ngIf="step.error" style="color:red;">{{ step.error }}</span>
-              </ion-item>
-            </div>
-          </ion-card-content>
-        </ion-card>
+    <ion-card-content>
+      <div
+        style="width: 95%; margin: auto; height: 110px; display: flex; justify-content: space-between; background-color: #ffffff; border: 1px solid #e7e7db; border-radius: 20px; overflow: hidden;"
+        (click)="doPoemTask()">
+        <!-- 左侧内容部分 -->
+        <div
+          style="display: flex; flex-direction: column; justify-content: flex-start; margin-top: 23px; margin-left: 40px; position: relative;">
+          <!-- 品牌动态提示标签 -->
+          <div
+            style="position: absolute; padding: 5px; color: #fff; background-color: #009b7d; top: -25px; left: -20px; border-radius: 7px; font-size: 12px;">
+            健身动作
+          </div>
+          <!-- 标题 -->
+          <h3>
+            一键<span style="color: #6dbdac; font-size: 30px; font-weight: bolder; font-family: SimSun;">生成</span>
+          </h3>
+          <!-- 按钮 -->
+          <div style="color: #fff;">
+            <ion-button size="small" class="lan">点击了解-></ion-button>
+          </div>
+        </div>
+        <!-- 右侧图片部分 -->
+        <div>
+          <img src="../../assets/images/action5.png" style="height: 100px; display: flex; justify-content: flex-end;"
+            alt="">
+        </div>
+      </div>
 
-        <!-- 图片展示 -->
-        <ion-card *ngIf="shareData.images">
-          <ion-card-header>
-            <ion-card-title>动作展示</ion-card-title>
-          </ion-card-header>
-          <ion-card-content>
-            <div *ngFor="let imageUrl of shareData.images">
-              <img [src]="imageUrl" alt="诊断图片" style="width: 100%; height: 400px; object-fit: cover;" />
-            </div>
-          </ion-card-content>
-        </ion-card>
+      <!-- 任务区域 -->
+      <ion-card *ngIf="actionTaskVisible">
+        <ion-card-header>
+          <ion-card-title>动作生成</ion-card-title>
+        </ion-card-header>
+        <ion-card-content>
+          <div *ngFor="let step of actionTaskList">
+            <ion-item>
+              <ion-icon *ngIf="step.progress === 0 && !step.error" name="radio-button-off-outline"></ion-icon>
+              <ion-icon *ngIf="step.progress !== 0 && step.progress !== 1" name="reload-outline"></ion-icon>
+              <ion-icon *ngIf="step.progress === 1" name="checkmark-circle-outline"></ion-icon>
+              <ion-icon *ngIf="step.error" name="close-circle-outline"></ion-icon>
+              {{ step.title }}
+              <span *ngIf="step.progress">{{ step.progress * 100 | number:'2.0-0' }}%</span>
+              <span *ngIf="step.error" style="color:red;">{{ step.error }}</span>
+            </ion-item>
+          </div>
+        </ion-card-content>
+      </ion-card>
 
-        <!-- 诊断结果 -->
-        <ion-card *ngIf="shareData.diagResult">
-          <ion-card-header>
-            <ion-card-title>{{ shareData.diagResult.title }}</ion-card-title>
-          </ion-card-header>
-          <ion-card-content>
-            <h2>{{ shareData.diagResult.desc }}</h2>
-            <fm-markdown-preview class="content-style" [content]=shareData.diagResult.content>
-            </fm-markdown-preview>
-          </ion-card-content>
-        </ion-card>
+      <!-- 图片展示 -->
+      <ion-card *ngIf="shareData.images">
+        <ion-card-header>
+          <ion-card-title>动作展示</ion-card-title>
+        </ion-card-header>
+        <ion-card-content>
+          <div *ngFor="let imageUrl of shareData.images">
+            <img [src]="imageUrl" alt="诊断图片" style="width: 100%; height: 400px; object-fit: cover;" />
+          </div>
+        </ion-card-content>
+      </ion-card>
 
-        <!-- AI教练互动 -->
-        <ion-card>
-          <ion-card-header>
-            <ion-card-title>顶级教练专区</ion-card-title>
-            <ion-card-subtitle>教练简介</ion-card-subtitle>
-          </ion-card-header>
-          <ion-card-content>
-            <ion-list>
-              <ion-item (click)="openInquiry(coach)" *ngFor="let coach of coachList" lines="none">
-                <ion-thumbnail slot="start">
-                  <img src="../../assets/images/coach1.jpg" alt="coach.get('name')">
-                </ion-thumbnail>
-                <div class="coach-info">
-                  <h3>{{ coach.get('name') }}({{ coach.get('age') }}岁)</h3>
-                  <p>擅长领域:{{ coach.get('specialize')}}</p>
-                  <p>WiseFitness俱乐部</p>
-                </div>
-              </ion-item>
-            </ion-list>
-          </ion-card-content>
-        </ion-card>
-      </ion-card-content>
-    </ion-card>
+      <!-- 诊断结果 -->
+
+
+      <!-- AI教练互动 -->
+      <ion-card id="coaches">
+        <ion-card-header>
+          <ion-card-title>教练简介</ion-card-title>
+          <ion-card-subtitle>顶级教练</ion-card-subtitle>
+        </ion-card-header>
+        <ion-card-content style="padding: 5px;">
+          <ion-list>
+            <ion-item *ngFor="let coach of coachList" lines="none">
+              <ion-thumbnail slot="start">
+                <img [src]="coach.get('avater') || '../../assets/images/coach1.jpg'" [alt]="coach.get('name')" />
+              </ion-thumbnail>
+              <div class="coach-info" style="width: 120px;">
+                <h3>{{ coach.get('name') }}({{ coach.get('age') }}岁)</h3>
+                <p>擅长领域:{{ coach.get('specialize')}}</p>
+                <p>WiseFitness俱乐部</p>
+              </div>
+              <div style="margin-left: 10px;">
+                <ion-button shape="round" size="small" (click)="openInquiry(coach)">
+                  <ion-icon name="logo-gitlab" style="margin-right: 5px;"></ion-icon>
+                  立即咨询
+                </ion-button>
+              </div>
+            </ion-item>
+          </ion-list>
+        </ion-card-content>
+      </ion-card>
+      <div
+        style="width: 95%; margin: auto; height: 110px; display: flex; justify-content: space-between; background-color: #ffffff; border: 1px solid #e7e7db; border-radius: 20px; overflow: hidden;"
+        (click)="doInqueryTask()">
+
+        <!-- 左侧内容部分 -->
+        <div
+          style="display: flex; flex-direction: column; justify-content: flex-start; margin-top: 23px; margin-left: 40px; position: relative;">
+
+          <!-- 品牌动态提示标签 -->
+          <div
+            style="position: absolute; padding: 5px; color: #fff; background-color: #009b7d; top: -25px; left: -20px; border-radius: 7px; font-size: 12px;">
+            身体疼痛
+          </div>
+
+          <!-- 标题 -->
+          <h3>
+            一键<span style="color: #6dbdac; font-size: 30px; font-weight: bolder; font-family: SimSun;">诊断</span>
+          </h3>
+
+          <!-- 按钮 -->
+          <div style="color: #fff;">
+            <ion-button size="small" class="lan">点击诊断-></ion-button>
+          </div>
+        </div>
+
+        <!-- 右侧图片部分 -->
+        <div>
+          <img src="../../assets/images/battle1.png" style="height: 100px; display: flex; justify-content: flex-end;"
+            alt="">
+        </div>
+      </div>
+      <ion-card *ngIf="healthTaskVisible">
+        <ion-card-header>
+          <ion-card-title>医疗诊断</ion-card-title>
+        </ion-card-header>
+        <ion-card-content>
+          <div *ngFor="let step of healthTaskList">
+            <ion-item>
+              <ion-icon *ngIf="step.progress === 0 && !step.error" name="radio-button-off-outline"></ion-icon>
+              <ion-icon *ngIf="step.progress !== 0 && step.progress !== 1" name="reload-outline"></ion-icon>
+              <ion-icon *ngIf="step.progress === 1" name="checkmark-circle-outline"></ion-icon>
+              <ion-icon *ngIf="step.error" name="close-circle-outline"></ion-icon>
+              {{ step.title }}
+              <span *ngIf="step.progress">{{ step.progress * 100 | number:'2.0-0' }}%</span>
+              <span *ngIf="step.error" style="color:red;">{{ step.error }}</span>
+            </ion-item>
+          </div>
+        </ion-card-content>
+      </ion-card>
+      <ion-card *ngIf="shareData.diagResult">
+        <ion-card-header>
+          <ion-card-title>{{ shareData.diagResult.title }}</ion-card-title>
+        </ion-card-header>
+        <ion-card-content>
+          <h2>{{ shareData.diagResult.desc }}</h2>
+          <fm-markdown-preview class="content-style" [content]=shareData.diagResult.content>
+          </fm-markdown-preview>
+        </ion-card-content>
+      </ion-card>
+
+    </ion-card-content>
   </div>
 </ion-content>

+ 246 - 100
TFPower-app/src/app/tab2/tab2.page.scss

@@ -7,7 +7,6 @@ ion-header {
   right: 0;
   height: 56px; /* 设置固定头部高度 */
 }
-
 .custom-segment {
   width: 60%; 
 }
@@ -17,7 +16,7 @@ ion-icon {
   margin-bottom: 5px;
 }
 
-ion-segment-button::part(indicator-background) {
+ion-segment-button.md::part(indicator-background) {
   background: #719e8c;
 }
 
@@ -27,7 +26,7 @@ ion-segment-button.md::part(native) {
 }
 
 .segment-button-checked.md::part(native) {
-  color: #08a391;
+  color: #009b7d;
   font-size: 20px;
 }
 
@@ -44,21 +43,44 @@ ion-content {
 }
 // plan部分css
 /* 给表格增加阴影和圆角 */
-.plan-table ion-grid {
-  margin-top: 20px;
-  box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
-  width: 100%; 
+//表头
+.grid-header {
+  font-weight: bold;
+  background-color: #6dbdac;
+  color: rgb(7, 6, 6);
+  text-align: center;
+   display: flex;
+    justify-content: center;
+    align-items: center;
+}
+.plan-table .table {
+  --ion-grid-column-padding: 0px;  /* 去除额外的内边距,避免错位 */
+}
 
+.ion-item {
+  --padding-start: 0px;
+  --padding-end: 0px;
+}
+
+.ion-col {
+  padding-left: 0px;
+  padding-right: 0px;
 }
 /* 计划表格的列间距和背景 */
 .plan-table ion-row {
-  margin-bottom: 1px;
+    margin-bottom: 1px;
+    display: flex;
+    justify-content: center;
+    align-items: center;
 }
 
 /* 表格行的底部间距 */
 .plan-table ion-col {
   margin-bottom: 1px;
-  border-bottom: 1px solid #ddd;
+ 
+    display: flex;
+    justify-content: center;
+    align-items: center;
   
 }
 
@@ -66,103 +88,47 @@ ion-content {
 .plan-column {
   background-color: #ffffff;
   padding: 12px;
-  border-bottom: 1px solid #ddd;
   text-align: center;
+   display: flex;
+    justify-content: center;
+    align-items: center;
 }
 
 /* 表格的标题 */
-.grid-header {
-  font-weight: bold;
-  background-color: #f0f0f0;
-  text-align: center;
-  padding: 12px;
-  border-bottom: 1px solid #ddd;
-}
-.plan-table ion-select {
-  width: 50%;  /* 调整宽度 */
-  height: 50%;
-  margin: 1px;
-}
-
-/* 修改状态框的选项 */
-.plan-table ion-select .select-interface-option {
-  font-size: 12px;  /* 修改选项字体大小 */
-  color: #555;  /* 修改选项字体颜色 */
-}
-
-/* 修改选中时的背景颜色 */
-.plan-table ion-select .select-placeholder {
-  color: #719e8c;  /* 占位符字体颜色 */
-}
-
-/* 修改下拉箭头的颜色 */
-.plan-table ion-select .select-icon {
-  color: #719e8c;  /* 下拉箭头颜色 */
-}
 
-/* 当选择框被激活时的样式 */
 
-.plan-table ion-button {  
-  margin-right: 10px; 
-  text-align: center; 
-  justify-content: center; 
-}
-.plan-table ion-button[color="success"]::part(native)
-{
-   justify-content: center;
-  align-items: center;
-color: white !important; /* 强制修改文本颜色为黑色 */
-}
-.plan-table ion-button[color="danger"]::part(native)
-{
-   justify-content: center;
-  align-items: center;
-color: white !important; /* 强制修改文本颜色为黑色 */
-}
-/* 按钮 */
-.plan-table ion-button[color="success"] {
-  background-color: #59beec; /* 绿色背景 */
-  border-radius: 4px;
-  padding: 5px 5px;
+.section-title {
+  font-size: 20px;
   font-weight: bold;
-   justify-content: center;
-  align-items: center;
-  box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
-  transition: background-color 0.3s ease, transform 0.3s ease;
+  color: rgb(7, 6, 6);
+  margin-bottom: 20px;
 }
+@media (max-width: 1000px) {
+  /* 更小的屏幕尺寸(如手机) */
+  .plan-table ion-col {
+    padding: 6px;
+    font-size: 14px;
+  }
 
-.plan-table ion-button[color="success"]:hover {
-  background-color: #48a7cc; /* 悬停时颜色变深 */
-  transform: scale(1.05); /* 悬停时按钮放大 */
-}
 
-.plan-table ion-button[color="danger"] {
-  background-color: #f44336; /* 红色背景 */
-  border-radius: 4px;
-  padding: 5px 5px;
-  font-weight: bold;
-  box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
-  transition: background-color 0.3s ease, transform 0.3s ease;
-}
-.plan-table ion-button[color="danger"]:hover {
-  background-color: #d32f2f; /* 悬停时颜色变深 */
-  transform: scale(1.05); /* 悬停时按钮放大 */
-}
-/* 优化按钮在行中的显示位置 */
-.plan-table ion-buttons {
-  display: flex;
-  justify-content: center;
-  align-items: center;
+  .plan-table ion-button {
+    min-width: 70px;
+    padding: 5px;
+  }
 }
 
-/* 给每个section加标题 */
-.section-title {
-  font-size: 20px;
-  font-weight: bold;
-  color: #333;
-  margin-bottom: 20px;
-}
+@media (max-width: 480px) {
+  /* 极小的屏幕尺寸(如小手机) */
+  .plan-table ion-col {
+    padding: 2px;
+    font-size: 12px;
+  }
 
+  .plan-table ion-button {
+    min-width: 60px;
+    padding: 2px;
+  }
+}
 /* 问诊部分的设计 */
 /* 确保图片等大 */
 .consult .ion-text-center ion-img {
@@ -173,15 +139,48 @@ color: white !important; /* 强制修改文本颜色为黑色 */
 .consult ion-item {
   margin-bottom: 12px; /* 增加医生卡片之间的间距 */
   padding: 12px; /* 调整卡片内边距 */
+  padding-right: 4px;
   border-radius: 8px; /* 圆角设计 */
   box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1); /* 添加卡片阴影 */
   background-color: #ffffff; /* 卡片背景色 */
 }
-
+.consult .thumbnail {
+  width: 160px;  /* 可以根据需要设置尺寸 */
+  height: 160px;  /* 高度和宽度一致使图片为正方形 */
+  margin-bottom: 10px;  /* 图片和按钮之间的间距 */
+}
 .consult ion-thumbnail {
   width: 60px; /* 缩略图宽度 */
   height: 60px; /* 缩略图高度 */
-  border-radius: 50%; /* 圆形头像 */
+}
+.consult .lan {
+  --background: #009b7d;
+  --background-hover: #9ce0be;
+  --background-activated: #88f4be;
+  --background-focused: #88f4be;
+
+  --color: #ffffff;
+
+  --border-radius: 10px;
+  --border-color: #28a169;
+  --border-style: solid;
+  --border-width: 1px;
+
+  --box-shadow: 0 2px 6px 0 rgb(0, 0, 0, 0.25);
+
+  --ripple-color: deeppink;
+  --padding-left: 10px;
+  --padding-right: 10px;
+  --padding-top: 3px;
+  --padding-bottom: 3px;
+
+  /* 例如给 .lan 这个类应用背景色 */
+  // background-color: var(--background);
+  // color: var(--color);
+  // border-radius: var(--border-radius);
+  // box-shadow: var(--box-shadow);
+  // padding: var(--padding-top) var(--padding-right) var(--padding-bottom) var(--padding-left);
+  // border: var(--border-width) var(--border-style) var(--border-color);
 }
 
 .consult .coach-info {
@@ -196,19 +195,166 @@ color: white !important; /* 强制修改文本颜色为黑色 */
   font-size: 16px;
   font-weight: bold;
 }
-
-.consult .coach-info p {
-  margin: 4px 0;
+.consult ion-card-subtitle {
   font-size: 14px;
+  margin: 4px 0;
+  font-weight: bold;
+  color: rgb(22, 16, 16);
+}
+.consult .coach-info p {
+  margin: 2px 0;
+  font-size: 12px;
   color: #666; /* 字体颜色 */
 }
+.consult ion-button {
+  --background: rgb(244, 245, 244);
+  --color: #13120e;
+  --border-radius: 15px;
+  --border-color: #000;
+  --border-style: solid;
+  --border-width: 1px;
+  --box-shadow: 0 2px 6px 0 rgb(0, 0, 0, 0.25);
+  --ripple-color: deeppink;
+  --padding-top: 10px;
+  --padding-bottom: 10px;
+}
+.reverse
+{
+  --background: #009b7d;
+  --color: white;
+  --border-radius: 15px;
+  --border-color: rgb(28, 145, 57);
+  --border-style: solid;
+  --border-width: 1px;
+  --box-shadow: 0 2px 6px 0 rgb(0, 0, 0, 0.25);
+  --ripple-color: deeppink;
+ 
+}
+.plan-column {
+  transition: background-color 0.3s ease, box-shadow 0.3s ease;  /* 平滑过渡效果 */
+}
 
+.task-container {
+  display: flex; /* 设置为 flexbox */
+  flex-direction: column; /* 让子元素垂直排列 */
+  align-items: center; /* 水平居中 */
+  justify-content: center; /* 垂直居中 */
+  text-align: center; /* 确保文本在水平上居中 */
+}
 
+.task-item {
+  font-weight: bold;
+  margin-bottom: 5px; /* 为了分隔上下两个span */
+}
 /* 打卡部分的设计 */
-.checkin-container p {
-  font-size: 18px;
+.checkin p {
+  font-size: 14px;
   color: #555;
 }
+.checkin .greeting-container {
+  display: flex;
+  justify-content: flex-start;
+  align-items: center;
+  padding: 5px;
+}
+
+.checkin .avatar-info-container {
+  display: flex;
+  align-items: center;
+  width: 100%;
+}
+
+.checkin .avatar-container {
+  margin-right:5px; /* 头像和信息之间的间距 */
+}
+
+.checkin .avatar {
+  width: 150px;
+  height: 150px;
+  object-fit: cover;
+}
+
+.checkin .user-info {
+  flex: 1; /* 使信息部分占满剩余空间 */
+}
+
+
+.datetime-container {
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+}
+/* 卡片内信息区域 */
+.card-info {
+  display: flex;
+  flex-direction: column; /* 垂直排列,按钮在上,统计信息在下 */
+  align-items: center;  /* 横向居中 */
+  justify-content: center; /* 纵向居中 */
+  gap: 20px;  /* 设置按钮和统计信息的间隔 */
+}
+
+/* 圆形打卡按钮 */
+.checkin ion-button {
+
+  --background: #009b7d;
+  --color: white;
+  --border-color: rgb(28, 145, 57);
+  --border-style: solid;
+  --border-width: 1px;
+  --box-shadow: 0 2px 6px 0 rgb(0, 0, 0, 0.25);
+  --ripple-color: deeppink;
+   width: 100px; 
+  height: 100px; 
+  font-size: 18px;
+  --border-radius: 50%; 
+  display: flex;
+  align-items: center;
+  justify-content: center;
+   --padding-start: 16px;
+  --padding-end: 16px;
+  --padding-top: 12px;
+  --padding-bottom: 12px;
+}
+
+
+.circle-btn:disabled {
+  background-color: #dcdcdc;
+  color: #aaa;
+}
+
+/* 卡片统计信息区域 */
+.card-stats {
+  display: flex;
+  justify-content: space-evenly;
+}
+
+/* 每个统计项 */
+.stat-item {
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+}
+
+/* 统计值 */
+ion-card .power-label {
+  position: absolute;
+  top: 5px;
+  right: 30px;
+  font-size: 14px !important; /* 使用 !important 强制应用样式 */
+}
+
+.checkin .stat-value {
+  font-size: 24px !important;  /* 使用 !important 强制应用样式 */
+  color: #000000;
+}
+ion-card {
+    border-radius: 15px; /* 圆角边框 */
+    background-color: #f9f9f9; /* 卡片内部背景颜色 */
+    box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1); /* 添加阴影效果 */
+    border: 1px solid #e0e0e0; /* 边框颜色 */
+  }
+
+
 
 
 

+ 294 - 31
TFPower-app/src/app/tab2/tab2.page.ts

@@ -1,10 +1,10 @@
 import { Component, OnInit, ChangeDetectorRef } from '@angular/core';
 import { Router } from '@angular/router';
 import { addIcons } from 'ionicons';
-import { checkmarkCircle, trash, calendar, helpCircle, create } from 'ionicons/icons';
+import { checkmarkCircle, sunny, infiniteOutline, alertCircleOutline, bicycleOutline, logoGitlab, trash, calendar, helpCircle, create } from 'ionicons/icons';
 import { CommonModule } from '@angular/common';
 import { FormsModule } from '@angular/forms';
-import { IonSelect, IonThumbnail, IonCardSubtitle, IonImg, IonCard, IonButtons, IonItem, IonList, IonHeader, IonIcon, IonToolbar, IonContent, IonSegment, IonSegmentButton, IonGrid, IonRow, IonCol, IonButton, IonLabel, IonBadge, IonInput, ModalController, IonCardTitle, IonCardContent, IonCardHeader, IonSelectOption } from '@ionic/angular/standalone';
+import { IonDatetime, IonSelect, IonItemOptions, IonThumbnail, IonCardSubtitle, IonImg, IonCard, IonButtons, IonItem, IonList, IonHeader, IonIcon, IonToolbar, IonContent, IonSegment, IonSegmentButton, IonGrid, IonRow, IonCol, IonButton, IonLabel, IonBadge, IonInput, ModalController, IonCardTitle, IonCardContent, IonCardHeader, IonSelectOption, IonItemSliding, IonItemOption, ToastController } from '@ionic/angular/standalone';
 import { FmodeChatCompletion, ImagineWork, DalleOptions, ChatPanelOptions, FmodeChat, FmodeChatMessage, MarkdownPreviewModule, openChatPanelModal } from "fmode-ng";
 import { AgentTaskStep } from './agent/agent.task';
 import { TaskPoemPictureDesc } from './agent/tasks/poem/poem-desc';
@@ -18,6 +18,7 @@ import { EditPlanModalComponent } from './edit-plan-modal/edit-plan-modal.compon
 import { AlertController } from '@ionic/angular';
 import { openUserEditModal } from 'src/lib/user/modal-user-edit/modal-user-edit.component';
 import { openUserLoginModal } from 'src/lib/user/modal-user-login/modal-user-login.component';
+import { TestPageComponent } from './test-page/test-page.component';
 @Component({
   selector: 'app-tab2',
   templateUrl: 'tab2.page.html',
@@ -51,44 +52,282 @@ import { openUserLoginModal } from 'src/lib/user/modal-user-login/modal-user-log
     IonButton,
     IonLabel,
     IonBadge,
-    IonInput
+    IonInput,
+    IonDatetime,
+    IonItemOption,
+    IonItemSliding,
+    IonItemOptions,
   ]
 })
 export class Tab2Page implements OnInit {
+
   selectedTab: string = 'checkin';  // 默认选中的tab
-  planList: any[] = [
-  ];
-  coachList: any[] = [
-  ];
+  planList: any[] = [];
+  coachList: any[] = [];
+  planUser: any[] = [];
   currentUser: CloudUser | undefined
-  constructor(private router: Router, private modalCtrl: ModalController, private cdr: ChangeDetectorRef, private alertController: AlertController) {
-    addIcons({ checkmarkCircle, calendar, helpCircle, trash, create });
+  actionTaskVisible = false;
+  healthTaskVisible = false;
+  constructor(private toastController: ToastController, private router: Router, private modalCtrl: ModalController, private cdr: ChangeDetectorRef, private alertController: AlertController) {
+    addIcons({ alertCircleOutline, sunny, checkmarkCircle, calendar, helpCircle, trash, create, logoGitlab, bicycleOutline, infiniteOutline });
     this.currentUser = new CloudUser();
   }
+  ngOnInit() {
+    this.loadPlanList()
+    this.loadCoachList()
+    this.loadPlanUser()
+
+  }
   async loadPlanList() {
     let currentUser = new CloudUser();
     const cloudQuery = new CloudQuery("fitPlan");
-    cloudQuery.equalTo("user", currentUser.toPointer());
-    this.planList = await cloudQuery.find();
-    this.cdr.detectChanges();
+    if (currentUser) {
+      cloudQuery.equalTo("user", currentUser.toPointer());
+      this.planList = await cloudQuery.find();
+      console.log(this.planList)
+      //排序算法
+      this.planList.sort((a, b) => {
+        const srcIdA = a.get("srcId").match(/\d+/);
+        const srcIdB = b.get("srcId").match(/\d+/);
+        if (srcIdA && srcIdB) {
+          const numA = parseInt(srcIdA[0], 10);
+          const numB = parseInt(srcIdB[0], 10);
+          return numA - numB;
+        }
+        return 0;
+      });
+    }
   }
+
   async loadCoachList() {
     let query = new CloudQuery("Coach");
     this.coachList = await query.find();
-    this.cdr.detectChanges();
   }
-  ngOnInit() {
-    this.loadPlanList()
-    this.loadCoachList()
+
+  dailyReward = 5;
+  powerForMakup = 30;
+  power: number = 0;
+  realDate: Date = this.correctDate(new Date());
+  days: number = 0; // 总打卡天数
+  consecutiveDays: number = 0; // 连续打卡天数
+  checkInHistory: Set<string> = new Set(); // 已打卡日期集合
+
+  async showToast(message: string, color: string = 'success') {
+    const toast = await this.toastController.create({
+      message: message,
+      duration: 2000,
+      position: 'top',
+      color: color,
+    });
+    toast.present();
   }
-  ngOnChanges() {
-    this.loadPlanList();
-    this.loadCoachList()
+
+  // 计算 BMI
+  calculateBMI(height: number, weight: number): number {
+    const heightInMeters = height / 100;
+    const bmi = weight / (heightInMeters * heightInMeters);
+    return parseFloat(bmi.toFixed(2));
   }
-  ngAfterViewChecked() {
-    this.loadPlanList();
-    this.loadCoachList()
+
+  // 获取鼓励性话语
+  getEncouragement(bmi: number): string {
+    if (bmi < 18.5) {
+      return '您的BMI偏低,注意保持健康的饮食哦!(๑•́ ₃ •̀๑)';
+    } else if (bmi >= 18.5 && bmi < 24.9) {
+      return '您的BMI在正常范围,继续保持良好的生活方式!(。♥‿♥。)';
+    } else if (bmi >= 25 && bmi < 29.9) {
+      return '您的BMI稍微偏高,可以增加运动,控制饮食!(≧◡≦)';
+    } else {
+      return '您的BMI较高,建议积极锻炼,控制体重!(。•́︿•̀。)';
+    }
+  }
+
+  // 加载用户数据
+  async loadPlanUser() {
+    let currentUser = new CloudUser();
+    const cloudQuery = new CloudQuery("fitUser");
+    // 确保 currentUser 和 cloudQuery 都有效
+    if (!currentUser || !cloudQuery) {
+      console.error('Invalid user or cloudQuery');
+      return;
+    }
+    // 使用 currentUser 的 pointer 查询用户相关数据
+    cloudQuery.equalTo("user", currentUser.toPointer());
+    try {
+      // 执行查询并获取数据
+      this.planUser = await cloudQuery.find();
+      // 确保查询结果存在且有效
+      if (this.planUser && this.planUser.length > 0) {
+        const user = this.planUser[0];
+        // 获取和处理已打卡日期
+        const checkedDays = user.get("checkeddays") || [];
+        if (Array.isArray(checkedDays)) {
+          checkedDays.forEach((date: string) => {
+            this.checkInHistory.add(date);
+          });
+        }
+        // 安全地获取数据,使用默认值来防止 null 或 undefined 错误
+        this.days = user.get("days") || 0;
+        this.consecutiveDays = user.get("sucdays") || 0;
+        this.power = user.get("power") || 0;  // 假设 power 默认是 0
+      } else {
+        console.warn('No user data found');
+      }
+    } catch (error) {
+      console.error('Error loading user plan:', error);
+    }
+  }
+
+
+  // 计算连续打卡天数
+  calculateConsecutiveDays() {
+    let currentDate = this.realDate;
+    let count = 0;
+    for (let i = 0; i < 30; i++) {
+      let date = new Date(currentDate);
+      date.setDate(date.getDate() - i);
+      let formattedDate = this.formatDate(date);
+      if (this.checkInHistory.has(formattedDate)) {
+        count++;
+      } else {
+        break;
+      }
+    }
+    this.consecutiveDays = count;
+  }
+
+  // 格式化日期
+  formatDate(date: Date): string {
+    return date.toISOString().split('T')[0];
+  }
+
+  // 修正日期(解决时区问题)
+  correctDate(date: string | Date): Date {
+    if (date instanceof Date) {
+      return new Date(date.getTime() - date.getTimezoneOffset() * 60000);
+    } else if (typeof date === 'string') {
+      const parsedDate = new Date(date);
+      return new Date(parsedDate.getTime() - parsedDate.getTimezoneOffset() * 60000);
+    }
+    throw new Error("Invalid date format");
+  }
+
+  // 判断是否今天已打卡
+  isCheckedInToday(): boolean {
+    const currentDate = this.realDate;
+    const formattedDate = this.formatDate(currentDate);
+    return this.checkInHistory.has(formattedDate);
+  }
+
+  // 获取连续打卡奖励
+  getConsecutiveReward(consecutiveDays: number): number {
+    if (consecutiveDays % 30 === 0) {
+      return 30;  // 第30天,奖励30
+    } else if (consecutiveDays % 15 === 0) {
+      return 20;  // 第15天,奖励20
+    } else if (consecutiveDays % 7 === 0) {
+      return 10;  // 第7天,奖励10
+    }
+    return 0;  // 不符合任何条件时不奖励
   }
+
+  // 打卡操作
+  async markAttendance() {
+    const currentDate = this.realDate;
+    const formattedDate = this.formatDate(currentDate);
+
+    // 如果今天已经打卡
+    if (this.checkInHistory.has(formattedDate)) {
+      this.showToast('今天已经打卡过了', 'warning');
+      return;
+    }
+
+    // 如果是未来日期
+    if (currentDate > this.correctDate(new Date())) {
+      this.showToast('不能打卡未来的日期', 'danger');
+      return;
+    }
+
+    // 正常打卡
+    this.checkInHistory.add(formattedDate);
+    this.days = this.checkInHistory.size;
+    this.calculateConsecutiveDays();
+
+    let currentUser = new CloudUser();
+    const cloudQuery = new CloudQuery("fitUser");
+    cloudQuery.equalTo("user", currentUser.toPointer());
+    const userData = await cloudQuery.find();
+
+    if (userData.length > 0) {
+      const user = userData[0];
+      let checkedDays = user.get("checkeddays") || [];
+      checkedDays.push(formattedDate);
+      user.set({ "checkeddays": checkedDays });
+      user.set({ "days": this.days });
+      user.set({ "sucdays": this.consecutiveDays });
+
+      // 计算奖励
+      let totalReward = this.dailyReward; // 每天签到奖励
+      totalReward += this.getConsecutiveReward(this.consecutiveDays); // 连续签到奖励
+      user.set({ "power": user.get("power") + totalReward }); // 增加总 power 奖励
+
+      await user.save();
+      this.loadPlanUser();
+      this.showToast('打卡成功,获得了 ' + totalReward + ' Power');
+    }
+  }
+
+  // 补签操作
+  async handleMakeupSignIn(user: CloudUser): Promise<string> {
+    if (user.get("power") >= this.powerForMakup) {
+      user.set({ "power": user.get("power") - this.powerForMakup });
+      await user.save();
+      return '补签成功!';
+    } else {
+      return '补签失败,您的动能不足!';
+    }
+  }
+
+  // 补签点击事件
+  async handleMakeupClick() {
+    const confirmed = window.confirm('补签将消耗 ' + this.powerForMakup + ' 动能,确定补签吗?');
+    if (confirmed) {
+      let currentUser = new CloudUser();
+      const cloudQuery = new CloudQuery("fitUser");
+      cloudQuery.equalTo("user", currentUser.toPointer());
+      const userData = await cloudQuery.find();
+
+      if (userData.length > 0) {
+        const user = userData[0];
+        const resultMessage = await this.handleMakeupSignIn(user);
+        this.showToast(resultMessage, resultMessage.includes('成功') ? 'success' : 'danger');
+        this.loadPlanUser();
+      }
+    }
+  }
+
+  // 日期变化时更新
+  onDateChange(event: any) {
+    this.realDate = new Date(this.correctDate(event.detail.value));
+  }
+
+  // 获取按钮状态(判断打卡、补签等)
+  getButtonState(date: Date): { isDisabled: boolean, buttonText: string } {
+    const formattedDate = this.formatDate(date);
+
+    if (formattedDate > this.formatDate(this.correctDate(new Date()))) {
+      // 未来日期
+      return { isDisabled: true, buttonText: '无法签到' };
+    } else if (this.checkInHistory.has(formattedDate)) {
+      // 已经打卡过
+      return { isDisabled: true, buttonText: '今天已经打卡' };
+    } else if (formattedDate < this.formatDate(this.correctDate(new Date()))) {
+      // 过去的日期
+      return { isDisabled: false, buttonText: '补签' };
+    }
+    return { isDisabled: false, buttonText: '打卡' };
+  }
+
   async login() {
     let user = await openUserLoginModal(this.modalCtrl);
     if (user?.id) {
@@ -104,6 +343,7 @@ export class Tab2Page implements OnInit {
   }
   logout() {
     this.currentUser?.logout();
+
   }
 
   editUser() {
@@ -120,7 +360,27 @@ export class Tab2Page implements OnInit {
       componentProps: { plan: day }
     }).then(modal => {
       modal.present();
+      modal.onDidDismiss().then((result) => {
+        if (result.data) {
+          const updatedPlan = result.data;
+          const index = this.planList.findIndex(item => item.id === updatedPlan.id);
+          if (index !== -1) {
+            this.planList[index] = updatedPlan;
+          }
+        }
+        this.loadPlanList()
+      });
+    });
+  }
+  regeneratePlan() {
+    console.log('重新生成计划:');
 
+    // 创建一个弹出框
+    this.modalCtrl.create({
+      component: TestPageComponent,
+      componentProps: {}
+    }).then(modal => {
+      modal.present();
       modal.onDidDismiss().then((result) => {
         if (result.data) {
           const updatedPlan = result.data;
@@ -129,6 +389,7 @@ export class Tab2Page implements OnInit {
             this.planList[index] = updatedPlan;
           }
         }
+        this.loadPlanList()
       });
     });
   }
@@ -163,9 +424,9 @@ export class Tab2Page implements OnInit {
 
     await alert.present();
   }
-
   //任务链
-  taskList: AgentTaskStep[] = []
+  actionTaskList: AgentTaskStep[] = []
+  healthTaskList: AgentTaskStep[] = []
   //一个等待一秒的函数  每经过一秒
   wait(duration: number = 1000) {
     return new Promise(resolve => {
@@ -177,13 +438,15 @@ export class Tab2Page implements OnInit {
   shareData: any = {}
   // 任务:完成故事意境描述及图像绘制
   doPoemTask() {
+    this.actionTaskVisible = true;
     let task1 = TaskPoemPictureDesc({ shareData: this.shareData, modalCtrl: this.modalCtrl });
     let task2 = TaskPoemPictureCreate({ shareData: this.shareData, modalCtrl: this.modalCtrl });
     let PoemTaskList = [task1, task2]
-    this.taskList = PoemTaskList
+    this.actionTaskList = PoemTaskList
     startTask(PoemTaskList)
   }
   doInqueryTask() {
+    this.healthTaskVisible = true;
     let task1 = TaskInqueryUserStory({ shareData: this.shareData, modalCtrl: this.modalCtrl });
     let task2 = TaskInqueryDoctorQuestion({ shareData: this.shareData, modalCtrl: this.modalCtrl });
     let task3 = TaskInqueryUserAnswer({ shareData: this.shareData, modalCtrl: this.modalCtrl });
@@ -192,7 +455,7 @@ export class Tab2Page implements OnInit {
       task1, task2, task3
     ]
     // 传递给显示组件
-    this.taskList = InquireServiceTaskList
+    this.healthTaskList = InquireServiceTaskList
     // 开始执行任务
     startTask(InquireServiceTaskList)
   }
@@ -232,7 +495,6 @@ export class Tab2Page implements OnInit {
     if (currentUser?.id) {
       ACL[currentUser?.id] = { read: true, write: true }
     }
-    localStorage.setItem("company", "E4KpGvTEto")
     consult.set({
       title: `交流记录${dateStr}-${coach?.get("name")}`,
       coach: coach.toPointer(),
@@ -245,12 +507,13 @@ export class Tab2Page implements OnInit {
         console.log("onChatInit");
         console.log("预设角色", chat.role);
         chat.role.set("name", coach?.get("name"));
-        chat.role.set("title", "职业教练");
+        chat.role.set("title", "职业健身教练");
+        chat.role.set("desc", `一名${coach?.get("desc")},${coach?.get("name")},年龄${coach?.get("age")}岁`);
         chat.role.set("tags", coach?.get("specialize"));
-        chat.role.set("avatar", "../../assets/images/coach2.jpg")
+        chat.role.set("avatar", coach?.get("avater") || "../../assets/images/coach1.jpg")
         chat.role.set("prompt", `
 # 角色设定
-您是${coach?.get("name")},年龄${coach?.get("age")},特长为${coach?.get("specialize")},要完成一次教练与学员之间的锻炼部位交流。
+您是${coach?.get("name")},年龄${coach?.get("age")},特长为${coach?.get("specialize")},要完成一次教练与学员之间的健身交流。
 
 # 对话环节
 0.导诊(根据用户基本情况,引导选择合适的训练计划) 
@@ -267,7 +530,7 @@ export class Tab2Page implements OnInit {
 - 完成训练计划时,请在消息结尾附带: [交流完成]
 # 开始话语
 当您准备好了,可以以一个健身教练的身份,向来访的学员打招呼。
-你好!欢迎来到健身房,我是${coach?.get("name")}教练。今天你想要专注锻炼哪个部位呢?或者有什么具体的健身目标吗?`);
+你好!欢迎来到健身房,我是${coach?.get("name")}教练,${coach?.get("desc")}。今天你想要专注锻炼哪个部位呢?或者有什么具体的健身目标吗?`);
       },
       onMessage: (chat: FmodeChat, message: FmodeChatMessage) => {
         console.log("onMessage", message)

+ 3 - 1
TFPower-app/src/app/tab2/tag-input/tag-input.component.html

@@ -6,7 +6,9 @@
   <ion-card-content>
     <div class="input-container">
       <ion-input [(ngModel)]="tagInput" placeholder="例如:减脂,增肌..." (keydown.enter)="addTag()"></ion-input>
-      <ion-button expand="full" (click)="addTag()">添加标签</ion-button>
+      <ion-button expand="full" (click)="addTag()" shape="round" size="small">
+        <ion-icon name="pricetag-outline"></ion-icon>添加标签
+      </ion-button>
     </div>
 
     <!-- 标签显示区域 -->

+ 4 - 1
TFPower-app/src/app/tab2/tag-input/tag-input.component.scss

@@ -11,7 +11,10 @@
   gap: 10px;
   margin-top: 10px;
 }
-
+ion-button
+{
+  --background: #009b7d;
+}
 .tag-chip {
   display: flex;
   align-items: center;

+ 2 - 2
TFPower-app/src/app/tab2/tag-input/tag-input.component.ts

@@ -3,7 +3,7 @@ import { IonCard, IonCardHeader, IonCardTitle, IonCardContent, IonInput, IonButt
 import { CommonModule } from '@angular/common';
 import { FormsModule } from '@angular/forms';
 import { addIcons } from 'ionicons';
-import { close, barbellOutline, personOutline, square, alarmOutline } from 'ionicons/icons';
+import { close, barbellOutline, personOutline, square, alarmOutline, pricetagOutline } from 'ionicons/icons';
 @Component({
   selector: 'app-tag-input',
   templateUrl: './tag-input.component.html',
@@ -34,6 +34,6 @@ export class TagInputComponent {
     this.tagsChanged.emit(this.tags);  // 发射更新后的标签列表
   }
   constructor() {
-    addIcons({ close, personOutline, barbellOutline, alarmOutline, square });
+    addIcons({ close, personOutline, barbellOutline, alarmOutline, square, pricetagOutline });
   }
 }

+ 32 - 14
TFPower-app/src/app/tab2/test-page/test-page.component.html

@@ -1,20 +1,16 @@
 <ion-header translucent="true">
-
   <ion-toolbar>
-    <ion-title>制定个人健身计划</ion-title>
-    <ion-back-button></ion-back-button>
+
+    <ion-title>
+      <ion-icon name="chevron-back-outline" style="margin-right: 20px;" (click)="backPage()"></ion-icon>制定个人健身计划
+    </ion-title>
   </ion-toolbar>
 </ion-header>
 
 <ion-content [fullscreen]="true">
   <div class="content">
-
-    <ion-button (click)="goBack()">
-      <ion-icon slot="icon-only" name="arrow-back"></ion-icon>
-    </ion-button>
-    健身目标选择
     <div class="module">
-      <h2>请输入您的健身目标</h2>
+      <h2>健身描述</h2>
       <app-tag-input (tagsChanged)="onTagsChanged($event)"></app-tag-input>
       <ion-item>
         <ion-label position="floating">详细描述(可选,越详细计划越清晰哦!)</ion-label>
@@ -31,7 +27,7 @@
         <ion-row>
           <ion-col size="6">
             <ion-item>
-              <ion-label>锻炼方式(多选)</ion-label>
+              <ion-label>运动种类</ion-label>
               <ion-select multiple="true" (ionChange)="onExercisePreferenceChange($event)" cancelText="取消" okText="确认">
                 <ion-select-option value="cardio">有氧</ion-select-option>
                 <ion-select-option value="strength">力量</ion-select-option>
@@ -41,7 +37,7 @@
           </ion-col>
           <ion-col size="6">
             <ion-item>
-              <ion-label>每周锻炼频率</ion-label>
+              <ion-label>每周频率</ion-label>
               <ion-select (ionChange)="onWorkoutFrequencyChange($event)" cancelText="取消" okText="确认">
                 <ion-select-option value="1">1次</ion-select-option>
                 <ion-select-option value="2">2次</ion-select-option>
@@ -90,10 +86,15 @@
     </div>
 
     <!-- 生成计划按钮 -->
-    <ion-button expand="full" (click)="generatePlan()" [disabled]="isGenerating">生成健身计划</ion-button>
+    <ion-button expand="full" shape="round" (click)="generatePlan()" [disabled]="isGenerating"
+      class="generate-plan-btn">
+      <ion-icon slot="start" style="margin-right: 5px;" name="fitness"></ion-icon>
+      生成健身计划
+    </ion-button>
 
     <!-- 显示健身计划结果 -->
     <div *ngIf="generatedPlan" class="result-container">
+      <ion-progress-bar *ngIf="isRevert" type="indeterminate" color="secondary"></ion-progress-bar>
       <h3>您的健身计划:</h3>
       <ion-card>
         <ion-card-header>
@@ -101,11 +102,28 @@
         </ion-card-header>
         <ion-card-content>
           <div *ngIf="isGenerating">
-            <ion-spinner name="crescent"></ion-spinner>
-            <p>生成中...</p>
+            <p>${{this.generatedPlan}}</p>
           </div>
           <div *ngIf="!isGenerating">
             <fm-markdown-preview class="content-style" [content]="generatedPlan"></fm-markdown-preview>
+            <ion-grid>
+              <ion-row>
+                <!-- 左侧按钮 -->
+                <ion-col size="6">
+                  <ion-button *ngIf="planReady" expand="full" (click)="confirmPlan()" shape="round" class="confirm-btn">
+                    <ion-icon name="checkmark-circle-outline"></ion-icon>确认计划
+                  </ion-button>
+                </ion-col>
+
+                <!-- 右侧按钮 -->
+                <ion-col size="6">
+                  <ion-button *ngIf="planReady" expand="full" color="light" shape="round" (click)="discardPlan()"
+                    class="discard-btn">
+                    <ion-icon name="close-circle-outline"></ion-icon>放弃计划
+                  </ion-button>
+                </ion-col>
+              </ion-row>
+            </ion-grid>
           </div>
         </ion-card-content>
       </ion-card>

+ 8 - 2
TFPower-app/src/app/tab2/test-page/test-page.component.scss

@@ -34,5 +34,11 @@ ion-col {
 }
 ion-button {
   margin-top: 20px;
-  --background: #3880ff;
-}
+  --background: #009b7d;
+}
+ion-card {
+    border-radius: 15px;
+    background-color: #f9f9f9;
+    box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
+    border: 1px solid #e0e0e0;
+  }

+ 33 - 60
TFPower-app/src/app/tab2/test-page/test-page.component.ts

@@ -4,10 +4,10 @@ import { FormsModule } from '@angular/forms';
 import { CommonModule } from '@angular/common';
 import { TagInputComponent } from '../tag-input/tag-input.component';
 import { addIcons } from 'ionicons';
-import { barbellOutline, personOutline, square, alarmOutline, arrowBack } from 'ionicons/icons';
+import { barbellOutline, chevronBackOutline, closeCircleOutline, checkmarkCircleOutline, fitness, personOutline, square, alarmOutline, arrowBack } from 'ionicons/icons';
 import { DalleOptions, FmodeChatCompletion, MarkdownPreviewModule } from 'fmode-ng';
 import { LoadingController } from '@ionic/angular';
-import { AlertController, IonThumbnail, IonBadge, IonButton, IonButtons, IonCard, IonCardContent, IonCardHeader, IonCardSubtitle, IonCardTitle, IonCol, IonContent, IonGrid, IonHeader, IonIcon, IonImg, IonInput, IonItem, IonLabel, IonList, IonRow, IonSegment, IonSegmentButton, IonSelect, IonSelectOption, IonToolbar, IonTitle, IonSpinner, IonTextarea } from '@ionic/angular/standalone';
+import { AlertController, IonThumbnail, IonBadge, IonButton, IonButtons, IonCard, IonCardContent, IonCardHeader, IonCardSubtitle, IonCardTitle, IonCol, IonContent, IonGrid, IonHeader, IonIcon, IonImg, IonInput, IonItem, IonLabel, IonList, IonRow, IonSegment, IonSegmentButton, IonSelect, IonSelectOption, IonToolbar, IonTitle, IonSpinner, IonTextarea, IonProgressBar, ModalController } from '@ionic/angular/standalone';
 import { NavController } from '@ionic/angular';
 import { Router } from '@angular/router';
 import { CloudObject, CloudQuery, CloudUser } from 'src/lib/ncloud';
@@ -51,7 +51,8 @@ import { IonBackButton } from '@ionic/angular/standalone';
     IonLabel,
     IonBadge,
     IonInput,
-    TagInputComponent
+    TagInputComponent,
+    IonProgressBar
   ]  // 添加 CommonModule
 })
 export class TestPageComponent implements OnInit {
@@ -64,15 +65,15 @@ export class TestPageComponent implements OnInit {
   goalDescription: string = '';
   generatedPlan: string = '';  // 用来存储生成的健身计划
   isGenerating: boolean = false;  // 是否正在生成计划
+  isRevert: boolean = false
   messageList: any[] = [];  // 存储消息列表
+  planReady: boolean = false;  // 是否可以显示确认按钮
   constructor(private toastController: ToastController,
-    private loadingController: LoadingController,
-    private alertController: AlertController,
-    private navController: NavController,
+    private modalCtrl: ModalController,
     private router: Router,
     private navCtrl: NavController
   ) {
-    addIcons({ personOutline, barbellOutline, alarmOutline, square, arrowBack });
+    addIcons({ chevronBackOutline, fitness, personOutline, closeCircleOutline, barbellOutline, alarmOutline, square, arrowBack, checkmarkCircleOutline, });
   }
   ngOnInit() { }
 
@@ -123,15 +124,10 @@ export class TestPageComponent implements OnInit {
       await this.showToast('身高或体重为空,无法生成个人健身计划');
       return; // 退出函数,不继续生成健身计划
     }
-    const loading = await this.loadingController.create({
-      message: '生成计划中...',
-      duration: 30000 // 加载时长可以根据实际需求调整
-    });
-    await loading.present();
     this.generatedPlan = '';
     this.messageList = [];
     this.isGenerating = true;
-
+    this.isRevert = true
     console.log('生成健身计划', {
       tags: this.selectedTags,
       exercisePreference: this.exercisePreference,
@@ -143,7 +139,7 @@ export class TestPageComponent implements OnInit {
     });
 
     let prompt = `您作为一名专业的健身计划定制大师,请帮我根据以下情况制定健身计划(健身计划请给每天的运动标上序号)。关键词:${this.selectedTags.join(",")},目标描述:${this.goalDescription},运动偏好:${this.exercisePreference},
-    健身频率每周:${this.workoutFrequency},身高:${this.height}cm,体重:${this.weight}kg,年龄:${this.age},注意:只需给出训练日计划,训练日只做四个运动`;
+    健身频率每周:${this.workoutFrequency},身高:${this.height}cm,体重:${this.weight}kg,年龄:${this.age},注意:训练部位给出具体部位(如胸部,腿部),具体次数,组数。一般一天一个部位。具体计划只需给出训练日计划,训练日只做四个运动`;
 
     let messageList = new FmodeChatCompletion([
       { role: "system", content: '' },
@@ -153,11 +149,9 @@ export class TestPageComponent implements OnInit {
     messageList.sendCompletion().subscribe((message: any) => {
       console.log(message.content);
       this.generatedPlan = message.content;
-
       if (message?.complete) {
         this.isGenerating = false;
         this.getJson()
-        loading.dismiss();
       }
     });
   }
@@ -168,13 +162,14 @@ export class TestPageComponent implements OnInit {
   planArray: any
   JSONcomplete: boolean = false
   JSONdes = ""
+
   JSONobject: { [key: string]: string } = {}
   getJson() {
     let promt = `请你以以下json格式分每天来整合文段内容:
     {
     "date":"周几运动",
     "srcId": "plan001等等",
-    "trainingPart":"训练部位",
+    "trainingPart":"训练部位如(胸部,腿部)",
     "trainingItems":"四个运动项目,以下面形式给出,形式如下"[
   {
     "item": "引体向上",
@@ -213,8 +208,9 @@ export class TestPageComponent implements OnInit {
         console.log("json:", this.JSONdes);
         this.generatedPlan = message.content;
         this.planArray = extractAllJsonFromString(this.JSONdes);
+        this.isRevert = false
+        this.planReady = true;
         console.log(this.planArray)
-        this.showConfirmationAlert();
         // planArray.forEach((plan: any) => {
         //   plan.detail = this.generatedPlan; // 添加额外的计划内容
         //   console.log(plan);
@@ -243,63 +239,41 @@ export class TestPageComponent implements OnInit {
       }
     });
   }
-  async showConfirmationAlert() {
-    const alert = await this.alertController.create({
-      header: '确认生成计划',
-      message: ` <div style="white-space: pre-wrap;">
-        <!-- 使用 fm-markdown-preview 渲染 Markdown 格式的内容 -->
-        <fm-markdown-preview class="content-style" [content]="generatedPlan"></fm-markdown-preview>
-      </div>`,
-      buttons: [
-        {
-          text: '放弃',
-          role: 'cancel',
-          handler: () => {
-            console.log('用户选择放弃');
-          }
-        },
-        {
-          text: '确认',
-          handler: () => {
-            // 这里可以调用 API 将数据存到数据库
-            console.log('用户确认', this.generatedPlan);
-            this.savePlanToDatabase(this.generatedPlan);
-          }
-        }
-      ]
-    });
-    await alert.present();
+  confirmPlan() {
+    this.savePlanToDatabase(this.generatedPlan);
+  }
+  discardPlan() {
+    this.generatedPlan = ""
+    this.dismiss()
+  }
+  backPage() {
+    this.dismiss()
   }
   async savePlanToDatabase(plan: string) {
-
-    let currentUser = new CloudUser().toPointer();  // 获取当前用户实例
+    let currentUser = new CloudUser().toPointer();
     console.log(currentUser)
     console.log('计划保存到数据库', plan);
-
-    // 查询当前用户的所有计划
     const cloudQuery = new CloudQuery("fitPlan");
-    // 过滤出与当前用户关联的所有计划
     cloudQuery.equalTo("user", currentUser)
     try {
-      const userPlans = await cloudQuery.find();  // 获取当前用户的所有计划
+      const userPlans = await cloudQuery.find();
       console.log("找到的计划数量:", userPlans.length);
 
       // 删除当前用户的所有计划
       for (const planObj of userPlans) {
-        await planObj.destroy();  // 删除每个计划
+        await planObj.destroy();
         console.log("已删除计划:", planObj);
       }
       this.planArray.forEach((plan: any) => {
-        plan.detail = this.generatedPlan; // 添加额外的计划内容
+        plan.detail = this.generatedPlan;
         console.log(plan);
-
         // 准备插入数据库的数据对象
         let newPlan = {
           "date": plan['date'],
           "srcId": plan['srcId'],
           "trainingPart": plan['trainingPart'],
           "trainingItems": plan['trainingItems'],
-          "user": currentUser,  // 将当前用户指针关联到计划
+          "user": currentUser,
         };
         // 创建新的 CloudObject 实例
         let newObject = new CloudObject("fitPlan");
@@ -307,6 +281,7 @@ export class TestPageComponent implements OnInit {
         newObject.save()
           .then(() => {
             console.log("计划保存成功");
+            this.dismiss()
           })
           .catch((error: any) => {
             console.error("保存计划时发生错误", error);
@@ -316,14 +291,12 @@ export class TestPageComponent implements OnInit {
     } catch (error) {
       console.error("查询或删除计划时发生错误", error);
     }
-    this.goToPage("tab2")
   }
-
+  dismiss() {
+    this.modalCtrl.dismiss();
+  }
   goBack() {
     this.navCtrl.back();
   }
-  goToPage(page: string) {
-    // 更新选中的tab
-    this.router.navigate([`/tabs/${page}`]);  // 然后再进行路由跳转
-  }
+
 }

BIN
TFPower-app/src/assets/images/action1.png


BIN
TFPower-app/src/assets/images/action2.png


BIN
TFPower-app/src/assets/images/action3.png


BIN
TFPower-app/src/assets/images/action4.png


BIN
TFPower-app/src/assets/images/action5.png


BIN
TFPower-app/src/assets/images/battle1.png


BIN
TFPower-app/src/assets/images/hosipital.png