Ver código fonte

update: 打卡页面雏形

xukang 4 meses atrás
pai
commit
e53e065478

+ 71 - 35
TFPower-app/src/app/tab2/tab2.page.html

@@ -20,46 +20,84 @@
 <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>
-      </ion-card-content>
-    </ion-card>
-    <ion-card>
-      <!-- 未登录 -->
-      @if(!currentUser?.id){
-      <ion-card-header>
-        <ion-card-title>请登录</ion-card-title>
-        <ion-card-subtitle>暂无信息</ion-card-subtitle>
-      </ion-card-header>
-      }
-      <!-- 未登录 -->
-      @if(currentUser?.id){
-      <ion-card-header>
-        <ion-card-title>{{currentUser?.get("username")}} {{currentUser?.get("realname")}}</ion-card-title>
-        <ion-card-subtitle>性别:{{currentUser?.get("gender")||"-"}} 年龄:{{currentUser?.get("age")||"-"}}
-        </ion-card-subtitle>
-      </ion-card-header>
-      }
-      <ion-card-content>
+  <div *ngIf="selectedTab === 'checkin'" class="checkin">
+    <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>{{ user.get('bmi') }}</p>
+                    <p><strong>运动目标:</strong>{{ user.get('fitnessGoals') }}</p>
+                  </ion-card-content>
+                </ion-card>
+                <ion-label>{{ getEncouragement(user.get('bmi')) }}</ion-label>
+              </div>
+            </div>
+          </div>
+        </ion-card-content>
+      </ion-card>
+      <ion-card>
+        <ion-card-header>
+          <ion-card-title>打卡区域</ion-card-title>
+        </ion-card-header>
+        <ion-card-content>
+          <ion-datetime displayFormat="YYYY-MM-DD" [value]="selectedDate" (ionChange)="onDateChange($event)">
+          </ion-datetime>
+          <div class="card-info">
+            <p><strong>已打卡天数:</strong>{{ user.get('days') }}天</p>
+            <p><strong>连续打卡天数:</strong>{{ user.get('sucdays') }}天</p>
+            <p><strong>打卡日期:</strong>{{user.get('checkeddays')}}</p>
+            <ion-button expand="full" (click)="markAttendance()">打卡</ion-button>
+          </div>
+        </ion-card-content>
+      </ion-card>
+      <ion-card>
+        <!-- 未登录 -->
         @if(!currentUser?.id){
-        <ion-button expand="block" (click)="signup()">注册</ion-button>
-        <ion-button expand="block" (click)="login()">登录</ion-button>
+        <ion-card-header>
+          <ion-card-title>请登录</ion-card-title>
+          <ion-card-subtitle>暂无信息</ion-card-subtitle>
+        </ion-card-header>
         }
+        <!-- 未登录 -->
         @if(currentUser?.id){
-        <ion-button expand="block" (click)="editUser()">编辑资料</ion-button>
-        <ion-button expand="block" (click)="logout()" color="light">登出</ion-button>
+        <ion-card-header>
+          <ion-card-title>{{currentUser?.get("username")}} {{currentUser?.get("realname")}}</ion-card-title>
+          <ion-card-subtitle>性别:{{currentUser?.get("gender")||"-"}} 年龄:{{currentUser?.get("age")||"-"}}
+          </ion-card-subtitle>
+        </ion-card-header>
         }
-      </ion-card-content>
-    </ion-card>
+        <ion-card-content>
+          @if(!currentUser?.id){
+          <ion-button expand="block" (click)="signup()">注册</ion-button>
+          <ion-button expand="block" (click)="login()">登录</ion-button>
+          }
+          @if(currentUser?.id){
+          <ion-button expand="block" (click)="editUser()">编辑资料</ion-button>
+          <ion-button expand="block" (click)="logout()" color="light">登出</ion-button>
+          }
+        </ion-card-content>
+      </ion-card>
+    </ion-card-content>
   </div>
 
   <!-- 计划 -->
-  <div *ngIf="selectedTab === 'plan'" class="plan-container">
+  <div *ngIf="selectedTab === 'plan'" class="plan">
 
 
     <ion-card-content>
@@ -177,7 +215,6 @@
           </fm-markdown-preview>
         </ion-card-content>
       </ion-card>
-
       <!-- AI教练互动 -->
       <ion-card id="coaches">
         <ion-card-header>
@@ -206,6 +243,5 @@
         </ion-card-content>
       </ion-card>
     </ion-card-content>
-
   </div>
 </ion-content>

+ 38 - 3
TFPower-app/src/app/tab2/tab2.page.scss

@@ -7,7 +7,6 @@ ion-header {
   right: 0;
   height: 56px; /* 设置固定头部高度 */
 }
-
 .custom-segment {
   width: 60%; 
 }
@@ -211,10 +210,46 @@ ion-content {
   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; /* 使信息部分占满剩余空间 */
+}
+.checkin .card-info {
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  padding: 15px 0;
+}
+
+.checkin .card-info p {
+  margin: 5px 0;
+}
 ion-card {
     border-radius: 15px; /* 圆角边框 */
     background-color: #f9f9f9; /* 卡片内部背景颜色 */

+ 102 - 11
TFPower-app/src/app/tab2/tab2.page.ts

@@ -4,7 +4,7 @@ import { addIcons } from 'ionicons';
 import { checkmarkCircle, 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, 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 { 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';
@@ -51,20 +51,26 @@ import { openUserLoginModal } from 'src/lib/user/modal-user-login/modal-user-log
     IonButton,
     IonLabel,
     IonBadge,
-    IonInput
+    IonInput,
+    IonDatetime
   ]
 })
 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({ alertCircleOutline, 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");
@@ -72,28 +78,113 @@ export class Tab2Page implements OnInit {
       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);
-          console.log(numA)
           const numB = parseInt(srcIdB[0], 10);
           return numA - numB;
         }
         return 0;
       });
     }
-
   }
+
   async loadCoachList() {
     let query = new CloudQuery("Coach");
     this.coachList = await query.find();
   }
-  ngOnInit() {
-    this.loadPlanList()
-    this.loadCoachList()
+  async loadPlanUser() {
+    let currentUser = new CloudUser();
+    const cloudQuery = new CloudQuery("fitUser");
+    if (currentUser) {
+      cloudQuery.equalTo("user", currentUser.toPointer());
+      this.planUser = await cloudQuery.find();
+      console.log(this.planUser);
+
+      // 假设从数据库加载已打卡的日期列表
+      const checkedDays = this.planUser[0].checkeddays || [];
+      checkedDays.forEach((date: string) => {
+        this.checkInHistory.add(date); // 加载已打卡日期到 checkInHistory
+      });
+      console.log(this.checkInHistory)
+      this.days = this.planUser[0].days; // 获取已打卡天数
+      this.consecutiveDays = this.planUser[0].sucdays; // 获取连续打卡天数
+    }
+  }
+
+  selectedDate: Date = new Date();
+  days: number = 0; // 总打卡天数
+  consecutiveDays: number = 0; // 连续打卡天数
+  checkInHistory: Set<string> = new Set(); // 已打卡日期集合
+  //打卡页面
+  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较高,建议积极锻炼,控制体重!(。•́︿•̀。)';
+    }
+  }
+  // 计算连续打卡天数
+  calculateConsecutiveDays() {
+    let currentDate = new Date();
+    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];
+  }
+
+  async markAttendance() {
+    const currentDate = new Date();
+    const formattedDate = this.formatDate(currentDate);
+    if (!this.checkInHistory.has(formattedDate)) {
+      this.checkInHistory.add(formattedDate);
+      this.days = this.checkInHistory.size; // 更新总打卡天数
+      this.calculateConsecutiveDays(); // 更新连续打卡天数
+      // 获取当前用户数据并更新 checkeddays 字段
+      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.checkeddays || [];
+        checkedDays.push(formattedDate); // 添加新的打卡日期
+
+        // 更新数据库中的 checkeddays 字段
+        user.set({ "checkeddays": checkedDays });
+        user.set({ "days": this.days }); // 更新已打卡天数
+        user.set({ "sucdays": this.consecutiveDays }); // 更新连续打卡天数
+        await user.save();
+        this.loadPlanUser()
+      }
+    }
+  }
+
+  // 处理日期变更
+  onDateChange(event: any) {
+    this.selectedDate = event.detail.value;
+  }
+
+
   async login() {
     let user = await openUserLoginModal(this.modalCtrl);
     if (user?.id) {