0225273 1 settimana fa
parent
commit
e7ff4e88ce

+ 64 - 14
MindOCApp/src/app/mood/mood.page.html

@@ -1,17 +1,67 @@
-<ion-header [translucent]="true">
-  <ion-toolbar>
-    <ion-title>
-      Tab 1
-    </ion-title>
-  </ion-toolbar>
-</ion-header>
+<!-- mood.page.html -->
+<ion-content class="mood-journal-v2">
+  <!-- 新型情绪仪表盘 -->
+  <div class="emotion-dashboard">
+    <div class="circular-progress">
+      <div class="wave-container" [style.--progress]="currentMoodScore/10">
+        <div class="liquid"></div>
+        <div class="glow"></div>
+      </div>
+      <div class="progress-text">
+        <div class="score">{{ currentMoodScore | number:'1.1-1' }}</div>
+        <div class="label">今日情绪指数</div>
+      </div>
+    </div>
+    <!-- 新增周边指标 -->
+    <div class="vital-stats">
+      <div class="stat-item" ><!-- (click)="toggleStat('stress')" -->
+        <div class="stat-icon heart-beat"></div>
+        <div class="stat-value">72</div>
+        <div class="stat-label">压力指数</div>
+      </div>
+      <!-- 其他指标... -->
+    </div>
+    <!-- 动态波纹背景 -->
+    <div class="ripple-background">
+      <div class="ripple" *ngFor="let r of ripples" 
+          [style]="getRippleStyle(r)"></div>
+    </div>
 
-<ion-content [fullscreen]="true">
-  <ion-header collapse="condense">
-    <ion-toolbar>
-      <ion-title size="large">Tab 1</ion-title>
-    </ion-toolbar>
-  </ion-header>
+    <!-- 情绪时间轴 -->
+    <div class="emotion-timeline">
+      <div class="timeline-scroller" (pan)="handlePan($event)">
+        <div class="timeline-track" [style.transform]="'translateX(' + offsetX + 'px)'">
+          <div class="time-block" *ngFor="let hour of hourlyMood" [style.--intensity]="hour.score/10">
+            <div class="bar"></div>
+            <div class="time">{{ hour.time }}</div>
+          </div>
+        </div>
+      </div>
+    </div>
+  </div>
 
-  <app-explore-container name="Tab 1 page"></app-explore-container>
+  <!-- 蜂窝状情绪日历 -->
+  <div class="honeycomb-calendar">
+    <div class="hex-row" *ngFor="let week of calendarWeeks">
+      <div class="hex-cell" 
+           *ngFor="let day of week"
+           [ngClass]="getMoodClass(day)"
+           (click)="showDayDetail(day)">
+        <div class="hex-content">
+          <div class="date">{{ day.date | date:'d' }}</div>
+          <div class="particles"></div>
+        </div>
+      </div>
+    </div>
+  </div>
+
+  <!-- 智能分析浮窗 -->
+  <div class="analysis-card">
+    <div class="insight">
+      <ion-icon name="bulb"></ion-icon>
+      <span>本周情绪峰值出现在周三下午3点</span>
+    </div>
+    <div class="suggestion">建议:尝试傍晚进行呼吸训练</div>
+  </div>
 </ion-content>
+

+ 302 - 0
MindOCApp/src/app/mood/mood.page.scss

@@ -0,0 +1,302 @@
+/* mood.page.scss */
+.mood-journal-v2 {
+    --primary: #8A7BEC;
+    --secondary: #64C3D3;
+    --text: #4A4A6A;
+    --bg: #c06c840f;
+  
+    background: var(--bg);
+    padding-bottom: 80px;
+  
+    .emotion-dashboard {
+      padding: 20px;
+      background: #c06c840f;
+      border-radius: 0 0 40px 40px;
+      box-shadow: 0 8px 32px rgba(138,123,236,0.1);
+  
+      /* 环形波浪进度条 */
+      .circular-progress {
+        position: relative;
+        width: 180px;
+        height: 180px;
+        margin: 0 auto 30px;
+  
+        .wave-container {
+          position: absolute;
+          width: 100%;
+          height: 100%;
+          border-radius: 50%;
+          overflow: hidden;
+          background: linear-gradient(145deg, #F0F1FF, #FFFFFF);
+          box-shadow: 8px 8px 16px #D9DAF0, 
+                     -8px -8px 16px #FFFFFF;
+  
+          .liquid {
+            position: absolute;
+            bottom: 0;
+            width: 100%;
+            background: var(--primary);
+            animation: wave 8s infinite linear;
+            height: calc(var(--progress) * 100%);
+            opacity: 0.8;
+          }
+  
+          .glow {
+            position: absolute;
+            width: 120%;
+            height: 120%;
+            background: radial-gradient(circle at 50% 0%, 
+              rgba(138,123,236,0.3) 0%, 
+              transparent 60%);
+            filter: blur(20px);
+          }
+        }
+  
+        .progress-text {
+          position: absolute;
+          top: 50%;
+          left: 50%;
+          transform: translate(-50%, -50%);
+          text-align: center;
+  
+          .score {
+            font-size: 42px;
+            font-weight: 700;
+            color: var(--text);
+            line-height: 1;
+            text-shadow: 2px 2px 4px rgba(0,0,0,0.1);
+          }
+  
+          .label {
+            font-size: 14px;
+            color: #888;
+            margin-top: 4px;
+          }
+        }
+      }
+      /* 仪表盘增强样式 */
+      .vital-stats {
+        display: grid;
+        grid-template-columns: repeat(3, 1fr);
+        gap: 15px;
+        margin-top: 30px;
+
+        .stat-item {
+          background: rgba(255,255,255,0.9);
+          padding: 15px;
+          border-radius: 16px;
+          transition: transform 0.3s ease;
+
+          &:hover {
+            transform: translateY(-5px);
+            box-shadow: 0 6px 20px rgba(138,123,236,0.15);
+          }
+        }
+
+        .heart-beat {
+          width: 30px;
+          height: 30px;
+          background: url('data:image/svg+xml,<svg ...>心跳SVG代码</svg>');
+          animation: beat 1.2s ease-in-out infinite;
+        }
+      }
+
+      @keyframes beat {
+        0%, 100% { transform: scale(1); }
+        50% { transform: scale(1.15); }
+      }
+
+      .ripple-background {
+        position: absolute;
+        top: 0;
+        left: 0;
+        width: 100%;
+        height: 100%;
+        pointer-events: none;
+
+        .ripple {
+          position: absolute;
+          border: 2px solid rgba(138,123,236,0.3);
+          border-radius: 50%;
+          animation: ripple 2s ease-out;
+        }
+      }
+
+      @keyframes ripple {
+        from { transform: scale(0); opacity: 0.6; }
+        to { transform: scale(3); opacity: 0; }
+      }
+  
+      /* 时间轴可视化 */
+      .emotion-timeline {
+        display: flex;
+        justify-content: space-between;
+        padding: 15px 0;
+  
+        .time-block {
+          flex: 1;
+          position: relative;
+          height: 120px;
+  
+          .bar {
+            position: absolute;
+            bottom: 0;
+            width: 70%;
+            left: 15%;
+            background: linear-gradient(to top, var(--primary), var(--secondary));
+            height: calc(var(--intensity) * 100%);
+            border-radius: 8px 8px 0 0;
+            transition: height 0.6s ease;
+          }
+  
+          .time {
+            position: absolute;
+            bottom: -25px;
+            width: 100%;
+            text-align: center;
+            font-size: 12px;
+            color: var(--text);
+          }
+        }
+      }
+      /* 时间轴滑动样式 */
+      .timeline-scroller {
+        overflow-x: auto;
+        -webkit-overflow-scrolling: touch;
+        padding: 20px 0;
+        mask: linear-gradient(
+          to right, 
+          transparent 0%, 
+          #000 10% 90%, 
+          transparent 100%
+        );
+
+        &::-webkit-scrollbar { display: none; }
+
+        .timeline-track {
+          display: inline-flex;
+          gap: 15px;
+          padding: 0 30px;
+          transition: transform 0.3s cubic-bezier(0.2, 0, 0.2, 1);
+        }
+
+        .time-block {
+          flex-shrink: 0;
+          width: 80px;
+          height: 120px;
+          opacity: 0.8;
+          transition: opacity 0.3s;
+
+          &:hover {
+            opacity: 1;
+            transform: scale(1.05);
+          }
+        }
+      }
+    }
+  
+    /* 蜂窝日历 */
+    .honeycomb-calendar {
+      padding: 10px;
+      margin-right: 15px;
+      --hex-size: 50px;
+
+      .hex-row {
+        display: flex;
+        justify-content: center;
+        margin-bottom: -3px;
+
+        &:nth-child(even) {
+          margin-left: calc(var(--hex-size) * 0.4);
+          margin-right: calc(var(--hex-size) * -0.4);
+        }
+      }
+
+      .hex-cell {
+        width: var(--hex-size);
+        height: calc(var(--hex-size) * 1.2);
+        clip-path: polygon(25% 0%, 75% 0%, 100% 50%, 75% 100%, 25% 100%, 0% 50%);
+        margin: 0 2px;
+        background: rgba(255,255,255,0.9);
+        transition: all 0.3s ease;
+        cursor: pointer;
+        position: relative;
+
+        .hex-content {
+          position: absolute;
+          width: 100%;
+          height: 100%;
+          display: flex;
+          align-items: center;
+          justify-content: center;
+
+          .date {
+            font-size: 16px;
+            font-weight: 500;
+            color: var(--text);
+            z-index: 1;
+          }
+
+          .particles {
+            position: absolute;
+            width: 100%;
+            height: 100%;
+            background: radial-gradient(circle at center, 
+              rgba(138,123,236,0.1) 0%, 
+              transparent 60%);
+            opacity: 0;
+            transition: opacity 0.3s ease;
+          }
+        }
+
+        &:hover {
+          transform: scale(1.15) rotate(15deg);
+          z-index: 2;
+
+          .particles {
+            opacity: 1;
+          }
+        }
+
+        /* 情绪状态颜色 */
+        &.excellent { background: rgba(100,195,211,0.3); }
+        &.good { background: rgba(138,123,236,0.2); }
+        &.normal { background: rgba(255,255,255,0.8); }
+        &.poor { background: rgba(255,182,193,0.3); }
+      }
+    }
+  
+    /* 智能分析卡片 */
+    .analysis-card {
+      margin: 20px;
+      padding: 16px;
+      background: rgba(255,255,255,0.95);
+      border-radius: 20px;
+      box-shadow: 0 4px 12px rgba(0,0,0,0.08);
+  
+      .insight {
+        display: flex;
+        align-items: center;
+        color: var(--text);
+        margin-bottom: 8px;
+  
+        ion-icon {
+          color: var(--primary);
+          margin-right: 8px;
+          font-size: 20px;
+        }
+      }
+  
+      .suggestion {
+        font-size: 14px;
+        color: #666;
+        padding-left: 28px;
+      }
+    }
+  }
+  
+  @keyframes wave {
+    0% { clip-path: polygon(0 20%, 100% 15%, 100% 80%, 0 85%); }
+    50% { clip-path: polygon(0 25%, 100% 20%, 100% 75%, 0 80%); }
+    100% { clip-path: polygon(0 20%, 100% 15%, 100% 80%, 0 85%); }
+  }

+ 181 - 2
MindOCApp/src/app/mood/mood.page.ts

@@ -1,4 +1,25 @@
-import { Component } from '@angular/core';
+import { Component, OnInit } from '@angular/core';
+
+// 类型定义
+interface CalendarDay {
+  date: Date;
+  score: number;       // 情绪指数 0-10
+  peakHour?: string;   // 当日情绪峰值时间
+  tags: string[];      // 情绪标签
+}
+
+interface HourlyMood {
+  time: string;        // 时间点 (如 "09:00")
+  score: number;       // 该时段情绪指数 0-10
+}
+
+// 仪表盘动态逻辑
+interface Ripple {
+  size: number;
+  x: number;
+  y: number;
+  opacity: number;
+}
 
 @Component({
   selector: 'app-mood',
@@ -6,7 +27,165 @@ import { Component } from '@angular/core';
   styleUrls: ['mood.page.scss'],
   standalone: false,
 })
-export class MoodPage {
+export class MoodPage implements OnInit {
+  ripples: Ripple[] = [];
+
+  // 添加呼吸节拍动画
+  startBreathAnimation() {
+    setInterval(() => {
+      this.ripples.push({
+        size: 0,
+        x: Math.random() * 80 + 10,
+        y: Math.random() * 80 + 10,
+        opacity: 0.6
+      });
+      
+      // 移除旧波纹
+      if(this.ripples.length > 8) this.ripples.shift();
+    }, 1500);
+  }
+
+  getRippleStyle(r: Ripple) {
+    return {
+      'width.px': r.size,
+      'height.px': r.size,
+      'left.%': r.x,
+      'top.%': r.y,
+      'opacity': r.opacity,
+      'animation': `ripple 2s ease-out`
+    };
+  }
+
+  // 当前情绪指数(示例数据)
+  currentMoodScore: number = 7.8;
+
+  // 每小时情绪数据
+  hourlyMood: HourlyMood[] = [];
+
+  offsetX = 0;
+  panStartX = 0;
+
+  handlePan(ev: any) {
+    if(ev.type === 'panstart') {
+      this.panStartX = this.offsetX;
+    }
+    if(ev.type === 'panmove') {
+      this.offsetX = this.panStartX + ev.deltaX;
+    }
+    if(ev.type === 'panend') {
+      // 惯性滑动逻辑
+      const velocity = ev.velocityX * 400;
+      this.offsetX += velocity;
+      this.applyBounds();
+    }
+  }
+
+  private applyBounds() {
+    const maxOffset = -this.hourlyMood.length * 60 + 300;
+    this.offsetX = Math.max(maxOffset, Math.min(0, this.offsetX));
+  }
+
+  // 蜂窝日历数据(按周分组)
+  calendarWeeks: CalendarDay[][] = [];
+
+  hexSize = 60; // 六边形边长
+
+  getRowStyle(rowIndex: number): any {
+    const offset = rowIndex % 2 === 0 ? 0 : this.hexSize * Math.cos(Math.PI/6);
+    return {
+      'margin-left': `${offset}px`
+    };
+  }
+  
+  // 录音状态
+  isRecording: boolean = false;
+
+  // 智能分析建议
+  analysisSuggestion: string = '检测到本周三下午常出现情绪波动,建议尝试呼吸练习';
+
+  ngOnInit() {
+    this.generateHourlyData();
+    this.generateCalendarData();
+  }
+
+  /** 生成24小时情绪示例数据 */
+  private generateHourlyData(): void {
+    for (let hour = 0; hour < 24; hour++) {
+      this.hourlyMood.push({
+        time: `${hour.toString().padStart(2, '0')}:00`,
+        score: Math.random() * 10 // 随机生成0-10分数
+      });
+    }
+  }
+
+  /** 生成4周日历示例数据 */
+  private generateCalendarData(): void {
+    const today = new Date();
+    const weeks = [];
+    
+    // 生成4周数据
+    for (let week = 0; week < 4; week++) {
+      const weekDays: CalendarDay[] = [];
+      
+      // 每周7天
+      for (let day = 0; day < 7; day++) {
+        const date = new Date(today);
+        date.setDate(today.getDate() - (week * 7 + day));
+        
+        weekDays.push({
+          date,
+          score: Math.random() * 10,
+          peakHour: `${Math.floor(Math.random() * 24)}:00`,
+          tags: this.generateRandomTags()
+        });
+      }
+      weeks.push(weekDays);
+    }
+    this.calendarWeeks = weeks;
+  }
+
+  /** 生成随机情绪标签 */
+  private generateRandomTags(): string[] {
+    const allTags = ['平静', '愉悦', '专注', '焦虑', '疲惫', '兴奋'];
+    return allTags
+      .sort(() => 0.5 - Math.random())
+      .slice(0, 2);
+  }
+  /** 获取日期情绪状态类 */
+  getMoodClass(day: CalendarDay): string {
+    if (day.score >= 8) return 'excellent';
+    if (day.score >= 6) return 'good';
+    if (day.score >= 4) return 'normal';
+    return 'poor';
+  }
+
+  /** 显示日期详情 */
+  showDayDetail(day: CalendarDay): void {
+    console.log('选中日期详情:', day);
+    // 实际开发中可跳转详情页或显示模态框
+  }
+
+  /** 切换录音状态 */
+  toggleRecording(): void {
+    this.isRecording = !this.isRecording;
+    if (this.isRecording) {
+      this.startVoiceRecording();
+    } else {
+      this.stopVoiceRecording();
+    }
+  }
+
+  /** 开始录音 */
+  private startVoiceRecording(): void {
+    console.log('开始录音...');
+    // 集成实际录音逻辑
+  }
+
+  /** 结束录音 */
+  private stopVoiceRecording(): void {
+    console.log('结束录音...');
+    // 处理录音文件
+  }
 
   constructor() {}
 

+ 6 - 1
MindOCApp/src/app/tabs/tabs.page.ts

@@ -14,5 +14,10 @@ export class TabsPage {
     // 加载所有使用的图标
     addIcons({ leaf, gameController, heartCircle });
   }
-
+  // 在tab点击事件中添加,播放音效(bug)
+  playInteractionSound() {
+    const audio = new Audio('assets/sfx/bell.mp3');
+    audio.volume = 0.3;
+    audio.play();
+  }
 }

BIN
MindOCApp/src/assets/sfx/bell.mp3