Przeglądaj źródła

增加倒计时页面功能

18079408532 1 rok temu
rodzic
commit
f937debc28

+ 4 - 0
src/app/app.routes.ts

@@ -5,4 +5,8 @@ export const routes: Routes = [
     path: '',
     loadChildren: () => import('./tabs/tabs.routes').then((m) => m.routes),
   },
+  {
+    path: 'countdown',
+    loadComponent: () => import('./countdown/countdown.page').then(m => m.CountdownPage)
+  }
 ];

+ 37 - 0
src/app/countdown/countdown.page.html

@@ -0,0 +1,37 @@
+<ion-header>
+  <ion-toolbar>
+    <ion-buttons slot="start">
+      <ion-button (click)="exit()">
+        <ion-icon slot="icon-only" name="arrow-back"></ion-icon>
+      </ion-button>
+    </ion-buttons>
+    <ion-title>{{ activityName }}</ion-title>
+  </ion-toolbar>
+</ion-header>
+
+<ion-content class="ion-padding">
+  <div class="container">
+    <div class="activity-info">
+      <ion-icon [name]="activityType"></ion-icon>
+      <h2>{{ activityName }}</h2>
+    </div>
+
+    <div class="countdown-container">
+      <div class="progress-container">
+        <ion-progress-bar [value]="progress"></ion-progress-bar>
+      </div>
+      <div class="time">{{ remainingTime }}</div>
+    </div>
+
+    <div class="controls">
+      <!-- 暂停/继续按钮 -->
+      <ion-button fill="clear" (click)="togglePause()">
+        <ion-icon [name]="isRunning ? 'pause' : 'play'" slot="icon-only"></ion-icon>
+      </ion-button>
+      <!-- 终止按钮 -->
+      <ion-button fill="clear" (click)="stop()">
+        <ion-icon name="stop" slot="icon-only"></ion-icon>
+      </ion-button>
+    </div>
+  </div>
+</ion-content>

+ 58 - 0
src/app/countdown/countdown.page.scss

@@ -0,0 +1,58 @@
+.countdown-container {
+  height: 100%;
+  display: flex;
+  flex-direction: column;
+  justify-content: center;
+  align-items: center;
+  background: #f8f9fa;
+}
+
+.activity-info {
+  margin-bottom: 40px;
+  text-align: center;
+  
+  ion-icon {
+    font-size: 48px;
+    color: var(--ion-color-primary);
+  }
+  
+  h2 {
+    margin: 16px 0;
+    font-size: 24px;
+    color: #2c3e50;
+  }
+}
+
+.timer-circle {
+  position: relative;
+  width: 280px;
+  height: 280px;
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  margin-bottom: 40px;
+  
+  .progress-ring {
+    position: absolute;
+    width: 100%;
+    height: 100%;
+    transform: rotate(-90deg);
+  }
+  
+  .time {
+    font-size: 48px;
+    font-weight: 300;
+    color: #2c3e50;
+  }
+}
+
+.controls {
+  ion-button {
+    --padding-start: 24px;
+    --padding-end: 24px;
+    
+    ion-icon {
+      font-size: 32px;
+    }
+  }
+}

+ 111 - 0
src/app/countdown/countdown.page.ts

@@ -0,0 +1,111 @@
+import { Component, OnInit, OnDestroy } from '@angular/core';
+import { CommonModule } from '@angular/common';
+import { IonicModule } from '@ionic/angular';
+import { FormsModule } from '@angular/forms';
+import { Router } from '@angular/router';
+import { addIcons } from 'ionicons';
+import { arrowBack, timer, play, pause, stop } from 'ionicons/icons';
+
+@Component({
+  selector: 'app-countdown',
+  templateUrl: './countdown.page.html',
+  styleUrls: ['./countdown.page.scss'],
+  standalone: true,
+  imports: [IonicModule, CommonModule, FormsModule]
+})
+export class CountdownPage implements OnInit, OnDestroy {
+  public remainingTime: string = '00:00';
+  public activityType: string = 'timer';
+  public activityName: string = '专注';
+  public progress: number = 1;
+  public isRunning: boolean = false;
+  
+  private duration: number = 0;
+  private timer: any;
+  private endTime: number = 0;
+  private pausedTimeLeft: number = 0;
+
+  constructor(private router: Router) {
+    addIcons({
+      'arrow-back': arrowBack,
+      'timer': timer,
+      'play': play,
+      'pause': pause,
+      'stop': stop
+    });
+  }
+
+  ngOnInit() {
+    this.setDuration(5);
+    this.startTimer();
+  }
+
+  ngOnDestroy() {
+    if (this.timer) {
+      clearInterval(this.timer);
+    }
+  }
+
+  public togglePause() {
+    this.isRunning = !this.isRunning;
+    if (this.isRunning) {
+      // 继续计时
+      this.endTime = Date.now() + this.pausedTimeLeft;
+      this.startTimer();
+    } else {
+      // 暂停计时
+      if (this.timer) {
+        clearInterval(this.timer);
+        this.pausedTimeLeft = this.endTime - Date.now();
+      }
+    }
+  }
+
+  public stop() {
+    if (this.timer) {
+      clearInterval(this.timer);
+    }
+    this.isRunning = false;
+    this.setDuration(5); // 重置为5分钟
+  }
+
+  public setDuration(minutes: number) {
+    if (minutes < 5) {
+      alert('倒计时的最小时长为5分钟。');
+      return;
+    }
+    this.duration = minutes * 60;
+    this.remainingTime = this.formatTime(this.duration);
+  }
+
+  public exit() {
+    if (this.timer) {
+      clearInterval(this.timer);
+    }
+    this.router.navigate(['/']);
+  }
+
+  private startTimer() {
+    this.endTime = Date.now() + (this.duration * 1000);
+    this.isRunning = true;
+    
+    this.timer = setInterval(() => {
+      const now = Date.now();
+      const timeLeft = Math.max(0, this.endTime - now);
+      
+      if (timeLeft === 0) {
+        clearInterval(this.timer);
+        this.isRunning = false;
+      }
+
+      this.remainingTime = this.formatTime(Math.floor(timeLeft / 1000));
+      this.progress = timeLeft / (this.duration * 1000);
+    }, 100);
+  }
+
+  private formatTime(seconds: number): string {
+    const minutes = Math.floor(seconds / 60);
+    const secs = Math.floor(seconds % 60);
+    return `${minutes.toString().padStart(2, '0')}:${secs.toString().padStart(2, '0')}`;
+  }
+}

+ 12 - 0
src/app/tab2/tab2.page.html

@@ -30,6 +30,18 @@
         结束计时
       </ion-button>
     </div>
+
+    <!-- 倒计时部分 -->
+    <div class="countdown-section">
+      <h2>专注倒计时</h2>
+      <ion-item>
+        <ion-label position="stacked">设置时长(分钟)</ion-label>
+        <ion-input type="number" [(ngModel)]="countdownMinutes" min="1" max="180"></ion-input>
+      </ion-item>
+      <ion-button expand="block" (click)="startCountdown()" [disabled]="!selectedType">
+        开始专注
+      </ion-button>
+    </div>
   </div>
 
   <!-- 历史记录 -->

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

@@ -46,6 +46,27 @@
   }
 }
 
+.countdown-section {
+  margin-top: 20px;
+  padding: 0 20px;
+  
+  h2 {
+    font-size: 18px;
+    color: var(--ion-color-dark);
+    margin-bottom: 16px;
+  }
+  
+  ion-item {
+    --background: var(--ion-color-light);
+    border-radius: 8px;
+    margin-bottom: 16px;
+  }
+  
+  ion-button {
+    margin-top: 8px;
+  }
+}
+
 .records-container {
   h2 {
     margin: 0;

+ 21 - 3
src/app/tab2/tab2.page.ts

@@ -2,6 +2,7 @@ import { Component, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
 import { CommonModule } from '@angular/common';
 import { FormsModule } from '@angular/forms';
 import { IonicModule } from '@ionic/angular';
+import { Router } from '@angular/router';
 import { addIcons } from 'ionicons';
 import { 
   schoolOutline, 
@@ -23,7 +24,7 @@ interface TimerRecord {
   type: string;
   startTime: string;
   endTime: string;
-  duration: number; // 以分钟为单位
+  duration: number;
 }
 
 @Component({
@@ -54,8 +55,9 @@ export class Tab2Page {
   currentTime: string = '00:00:00';
   records: TimerRecord[] = [];
   timer: any;
+  countdownMinutes: number = 25; // 默认25分钟
 
-  constructor() {
+  constructor(private router: Router) {
     // 注册图标
     addIcons({
       'school-outline': schoolOutline,
@@ -84,7 +86,6 @@ export class Tab2Page {
 
   startTimer() {
     if (!this.selectedType) {
-      // 如果没有选择类型,提示用户
       return;
     }
     
@@ -127,6 +128,23 @@ export class Tab2Page {
     }
   }
 
+  startCountdown() {
+    if (!this.selectedType) {
+      return;
+    }
+    
+    const selectedActivity = this.activityTypes.find(t => t.id === this.selectedType);
+    if (selectedActivity) {
+      this.router.navigate(['/countdown'], {
+        queryParams: {
+          type: selectedActivity.icon,
+          name: selectedActivity.name,
+          duration: this.countdownMinutes
+        }
+      });
+    }
+  }
+
   private padNumber(num: number): string {
     return num.toString().padStart(2, '0');
   }

+ 1 - 1
tsconfig.json

@@ -2,7 +2,7 @@
 {
   "compileOnSave": false,
   "compilerOptions": {
-    "baseUrl": "./",
+    "baseUrl": "./src",
     "outDir": "./dist/out-tsc",
     "forceConsistentCasingInFileNames": true,
     "strict": true,