Browse Source

数据统计页面

18079408532 1 year ago
parent
commit
31d4db00e1

+ 19 - 0
package-lock.json

@@ -24,6 +24,7 @@
         "@capacitor/keyboard": "6.0.3",
         "@capacitor/status-bar": "6.0.2",
         "@ionic/angular": "^8.0.0",
+        "chart.js": "^4.4.7",
         "ionicons": "^7.2.1",
         "rxjs": "~7.8.0",
         "tslib": "^2.3.0",
@@ -4684,6 +4685,12 @@
         "tslib": "2"
       }
     },
+    "node_modules/@kurkle/color": {
+      "version": "0.3.4",
+      "resolved": "https://registry.npmmirror.com/@kurkle/color/-/color-0.3.4.tgz",
+      "integrity": "sha512-M5UknZPHRu3DEDWoipU6sE8PdkZ6Z/S+v4dD+Ke8IaNlpdSQah50lz1KtcFBa2vsdOnwbbnxJwVM4wty6udA5w==",
+      "license": "MIT"
+    },
     "node_modules/@leichtgewicht/ip-codec": {
       "version": "2.0.5",
       "resolved": "https://registry.npmmirror.com/@leichtgewicht/ip-codec/-/ip-codec-2.0.5.tgz",
@@ -8399,6 +8406,18 @@
       "dev": true,
       "license": "MIT"
     },
+    "node_modules/chart.js": {
+      "version": "4.4.7",
+      "resolved": "https://registry.npmmirror.com/chart.js/-/chart.js-4.4.7.tgz",
+      "integrity": "sha512-pwkcKfdzTMAU/+jNosKhNL2bHtJc/sSmYgVbuGTEDhzkrhmyihmP7vUc/5ZK9WopidMDHNe3Wm7jOd/WhuHWuw==",
+      "license": "MIT",
+      "dependencies": {
+        "@kurkle/color": "^0.3.0"
+      },
+      "engines": {
+        "pnpm": ">=8"
+      }
+    },
     "node_modules/chokidar": {
       "version": "3.6.0",
       "resolved": "https://registry.npmmirror.com/chokidar/-/chokidar-3.6.0.tgz",

+ 1 - 0
package.json

@@ -29,6 +29,7 @@
     "@capacitor/keyboard": "6.0.3",
     "@capacitor/status-bar": "6.0.2",
     "@ionic/angular": "^8.0.0",
+    "chart.js": "^4.4.7",
     "ionicons": "^7.2.1",
     "rxjs": "~7.8.0",
     "tslib": "^2.3.0",

+ 9 - 19
src/app/countdown/countdown.page.html

@@ -1,36 +1,26 @@
-<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">
+<ion-content class="ion-text-center">
+  <div class="countdown-container">
     <div class="activity-info">
       <ion-icon [name]="activityType"></ion-icon>
       <h2>{{ activityName }}</h2>
     </div>
 
-    <div class="countdown-container">
-      <div class="progress-container">
+    <div class="timer-circle">
+      <div class="progress-ring">
         <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-icon [name]="isRunning ? 'pause-outline' : 'play-outline'" slot="icon-only"></ion-icon>
       </ion-button>
-      <!-- 终止按钮 -->
       <ion-button fill="clear" (click)="stop()">
-        <ion-icon name="stop" slot="icon-only"></ion-icon>
+        <ion-icon name="stop-outline" slot="icon-only"></ion-icon>
+      </ion-button>
+      <ion-button fill="clear" (click)="exit()">
+        <ion-icon name="exit-outline" slot="icon-only"></ion-icon>
       </ion-button>
     </div>
   </div>

+ 88 - 68
src/app/countdown/countdown.page.ts

@@ -1,10 +1,26 @@
 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 { IonicModule, NavController } from '@ionic/angular';
+import { ActivatedRoute } from '@angular/router';
 import { addIcons } from 'ionicons';
-import { arrowBack, timer, play, pause, stop } from 'ionicons/icons';
+import { 
+  schoolOutline, 
+  briefcaseOutline, 
+  moonOutline,
+  barbellOutline,
+  bookOutline,
+  codeSlashOutline,
+  leafOutline,
+  musicalNotesOutline,
+  languageOutline,
+  pencilOutline,
+  pauseOutline,
+  playOutline,
+  stopOutline,
+  exitOutline,
+  trashOutline
+} from 'ionicons/icons';
 
 @Component({
   selector: 'app-countdown',
@@ -14,30 +30,42 @@ import { arrowBack, timer, play, pause, stop } from 'ionicons/icons';
   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;
+  remainingTime: string = '';
+  activityType: string = '';
+  activityName: string = '';
+  duration: number = 0;
+  timer: any;
+  isRunning: boolean = false;
+  progress: number = 1;
+  pausedTimeLeft: number = 0;
 
-  constructor(private router: Router) {
+  constructor(private route: ActivatedRoute, private navCtrl: NavController) {
     addIcons({
-      'arrow-back': arrowBack,
-      'timer': timer,
-      'play': play,
-      'pause': pause,
-      'stop': stop
+      'school-outline': schoolOutline,
+      'briefcase-outline': briefcaseOutline,
+      'moon-outline': moonOutline,
+      'barbell-outline': barbellOutline,
+      'book-outline': bookOutline,
+      'code-slash-outline': codeSlashOutline,
+      'leaf-outline': leafOutline,
+      'musical-notes-outline': musicalNotesOutline,
+      'language-outline': languageOutline,
+      'pencil-outline': pencilOutline,
+      'pause-outline': pauseOutline,
+      'play-outline': playOutline,
+      'stop-outline': stopOutline,
+      'exit-outline': exitOutline,
+      'trash-outline': trashOutline
     });
   }
 
   ngOnInit() {
-    this.setDuration(5);
-    this.startTimer();
+    this.route.queryParams.subscribe(params => {
+      this.activityType = params['type'];
+      this.activityName = params['name'];
+      this.duration = parseInt(params['duration']);
+      this.startCountdown(this.duration * 60); // 转换为秒
+    });
   }
 
   ngOnDestroy() {
@@ -46,66 +74,58 @@ export class CountdownPage implements OnInit, OnDestroy {
     }
   }
 
-  public togglePause() {
-    this.isRunning = !this.isRunning;
-    if (this.isRunning) {
-      // 继续计时
-      this.endTime = Date.now() + this.pausedTimeLeft;
-      this.startTimer();
-    } else {
-      // 暂停计时
-      if (this.timer) {
+  startCountdown(totalSeconds: number) {
+    let seconds = totalSeconds;
+    if (this.pausedTimeLeft > 0) {
+      seconds = this.pausedTimeLeft;
+    }
+    const startTime = seconds;
+    
+    this.timer = setInterval(() => {
+      if (seconds > 0) {
+        seconds--;
+        this.progress = seconds / startTime;
+        this.remainingTime = this.formatTime(seconds);
+      } else {
+        this.isRunning = false;
         clearInterval(this.timer);
-        this.pausedTimeLeft = this.endTime - Date.now();
+        // 可以添加提示音或震动
       }
-    }
+    }, 1000);
   }
 
-  public stop() {
-    if (this.timer) {
-      clearInterval(this.timer);
-    }
-    this.isRunning = false;
-    this.setDuration(5); // 重置为5分钟
+  formatTime(seconds: number): string {
+    const hours = Math.floor(seconds / 3600);
+    const minutes = Math.floor((seconds % 3600) / 60);
+    const secs = seconds % 60;
+    return `${this.padNumber(hours)}:${this.padNumber(minutes)}:${this.padNumber(secs)}`;
   }
 
-  public setDuration(minutes: number) {
-    if (minutes < 5) {
-      alert('倒计时的最小时长为5分钟。');
-      return;
-    }
-    this.duration = minutes * 60;
-    this.remainingTime = this.formatTime(this.duration);
+  padNumber(num: number): string {
+    return num.toString().padStart(2, '0');
   }
 
-  public exit() {
-    if (this.timer) {
+  togglePause() {
+    if (this.isRunning) {
       clearInterval(this.timer);
+      const timeArray = this.remainingTime.split(':');
+      this.pausedTimeLeft = timeArray.reduce((acc, time) => (60 * acc) + Number(time), 0);
+    } else {
+      this.startCountdown(this.pausedTimeLeft);
     }
-    this.router.navigate(['/']);
+    this.isRunning = !this.isRunning;
   }
 
-  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);
+  stop() {
+    clearInterval(this.timer);
+    this.isRunning = false;
+    this.remainingTime = this.formatTime(this.duration * 60);
+    this.progress = 1;
+    this.pausedTimeLeft = 0;
   }
 
-  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')}`;
+  exit() {
+    this.stop();
+    this.navCtrl.back();
   }
 }

+ 5 - 2
src/app/tab3/tab3.page.html

@@ -1,8 +1,11 @@
 <ion-header>
   <ion-toolbar>
-    <ion-title>Tab 3</ion-title>
+    <ion-title>数据统计</ion-title>
   </ion-toolbar>
 </ion-header>
+
 <ion-content>
-  <div>Hello Tab 3</div>
+  <div class="chart-container">
+    <canvas #barCanvas></canvas>
+  </div>
 </ion-content>

+ 7 - 0
src/app/tab3/tab3.page.scss

@@ -0,0 +1,7 @@
+.chart-container {
+  padding: 20px;
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  height: 100%;
+}

+ 90 - 9
src/app/tab3/tab3.page.ts

@@ -1,17 +1,98 @@
-import { Component, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
+import { Component, OnInit, ViewChild, ElementRef } from '@angular/core';
+import { CommonModule } from '@angular/common';
+import { FormsModule } from '@angular/forms';
 import { IonicModule } from '@ionic/angular';
-import { ExploreContainerComponent } from '../explore-container/explore-container.component';
+import { Chart, registerables } from 'chart.js';
+
+Chart.register(...registerables);
 
 @Component({
   selector: 'app-tab3',
-  templateUrl: 'tab3.page.html',
-  styleUrls: ['tab3.page.scss'],
+  templateUrl: './tab3.page.html',
+  styleUrls: ['./tab3.page.scss'],
   standalone: true,
-  schemas: [CUSTOM_ELEMENTS_SCHEMA],
-  imports: [IonicModule, ExploreContainerComponent]
+  imports: [IonicModule, CommonModule, FormsModule]
 })
-export class Tab3Page {
-    constructor() {
+export class Tab3Page implements OnInit {
+  @ViewChild('barCanvas') private barCanvas!: ElementRef;
+  barChart: any;
+
+  constructor() { }
+
+  ngOnInit() {
+  }
+
+  ionViewDidEnter() {
+    this.loadData();
+  }
+
+  loadData() {
+    const tab1Records = JSON.parse(localStorage.getItem('tasks') || '[]');
+    const tab2Records = JSON.parse(localStorage.getItem('timerRecords') || '[]');
+
+    const combinedRecords = [...tab1Records, ...tab2Records];
+    const activityDurations: { [key: string]: number } = {};
 
+    combinedRecords.forEach(record => {
+      const type = record.type;
+      const duration = record.duration || 0;
+      if (activityDurations[type]) {
+        activityDurations[type] += duration;
+      } else {
+        activityDurations[type] = duration;
+      }
+    });
+
+    const translatedLabels = this.translateLabels(Object.keys(activityDurations));
+
+    this.createBarChart(
+      translatedLabels,
+      Object.values(activityDurations)
+    );
+  }
+
+  translateLabels(labels: string[]): string[] {
+    const translations: { [key: string]: string } = {
+      'study': '学习',
+      'work': '工作',
+      'sleep': '睡眠',
+      'sport': '运动',
+      'reading': '阅读',
+      'coding': '编程',
+      'meditation': '冥想',
+      'music': '音乐',
+      'language': '语言',
+      'writing': '写作'
+    };
+
+    return labels.map(label => translations[label] || label);
+  }
+
+  createBarChart(labels: string[], data: number[]) {
+    if (this.barChart) {
+      this.barChart.destroy();
     }
-}
+
+    this.barChart = new Chart(this.barCanvas.nativeElement, {
+      type: 'bar',
+      data: {
+        labels: labels,
+        datasets: [{
+          label: '活动时长(分钟)',
+          data: data,
+          backgroundColor: 'rgba(54, 162, 235, 0.2)',
+          borderColor: 'rgba(54, 162, 235, 1)',
+          borderWidth: 1
+        }]
+      },
+      options: {
+        responsive: true,
+        scales: {
+          y: {
+            beginAtZero: true
+          }
+        }
+      }
+    });
+  }
+}