未来全栈 9 miesięcy temu
rodzic
commit
1aac91f144
44 zmienionych plików z 1030 dodań i 42 usunięć
  1. 10 1
      app/app-routing.module.ts
  2. 5 0
      app/app.component.ts
  3. 14 1
      app/app.module.ts
  4. 17 0
      app/calendar/calendar-routing.module.ts
  5. 20 0
      app/calendar/calendar.module.ts
  6. 13 0
      app/calendar/calendar.page.html
  7. 0 0
      app/calendar/calendar.page.scss
  8. 17 0
      app/calendar/calendar.page.spec.ts
  9. 15 0
      app/calendar/calendar.page.ts
  10. 92 7
      app/tab1/tab1.page.html
  11. 121 0
      app/tab1/tab1.page.scss
  12. 3 2
      app/tab1/tab1.page.spec.ts
  13. 15 2
      app/tab1/tab1.page.ts
  14. 8 1
      app/tab2/tab2.module.ts
  15. 38 11
      app/tab2/tab2.page.html
  16. 25 0
      app/tab2/tab2.page.scss
  17. 22 2
      app/tab2/tab2.page.ts
  18. 68 6
      app/tab3/tab3.page.html
  19. 21 0
      app/tab3/tab3.page.scss
  20. 10 1
      app/tab3/tab3.page.ts
  21. 4 0
      app/tabs/tabs-routing.module.ts
  22. 11 6
      app/tabs/tabs.page.html
  23. BIN
      assets/img/head.jpg
  24. 17 0
      modules/user/edit-info/edit-info-routing.module.ts
  25. 20 0
      modules/user/edit-info/edit-info.module.ts
  26. 47 0
      modules/user/edit-info/edit-info.page.html
  27. 0 0
      modules/user/edit-info/edit-info.page.scss
  28. 17 0
      modules/user/edit-info/edit-info.page.spec.ts
  29. 56 0
      modules/user/edit-info/edit-info.page.ts
  30. 17 0
      modules/user/login/login-routing.module.ts
  31. 20 0
      modules/user/login/login.module.ts
  32. 38 0
      modules/user/login/login.page.html
  33. 0 0
      modules/user/login/login.page.scss
  34. 17 0
      modules/user/login/login.page.spec.ts
  35. 93 0
      modules/user/login/login.page.ts
  36. 17 0
      modules/user/mine/mine-routing.module.ts
  37. 20 0
      modules/user/mine/mine.module.ts
  38. 29 0
      modules/user/mine/mine.page.html
  39. 0 0
      modules/user/mine/mine.page.scss
  40. 17 0
      modules/user/mine/mine.page.spec.ts
  41. 28 0
      modules/user/mine/mine.page.ts
  42. 14 0
      modules/user/user-routing.module.ts
  43. 14 0
      modules/user/user.module.ts
  44. 0 2
      package-lock.json

+ 10 - 1
app/app-routing.module.ts

@@ -5,7 +5,16 @@ const routes: Routes = [
   {
     path: '',
     loadChildren: () => import('./tabs/tabs.module').then(m => m.TabsPageModule)
-  }
+  },
+  {
+    path: 'calendar',
+    loadChildren: () => import('./calendar/calendar.module').then( m => m.CalendarPageModule)
+  },
+
+  {
+    path: 'user',
+    loadChildren: () => import('../modules/user/user.module').then(m => m.UserModule)
+  },
 ];
 @NgModule({
   imports: [

+ 5 - 0
app/app.component.ts

@@ -1,5 +1,10 @@
 import { Component } from '@angular/core';
 
+// 根组件顶部引用并初始化Parse服务
+import * as Parse from "parse";
+Parse.initialize("dev");
+(Parse as any).serverURL = 'http://web2023.fmode.cn:9999/parse'
+
 @Component({
   selector: 'app-root',
   templateUrl: 'app.component.html',

+ 14 - 1
app/app.module.ts

@@ -6,10 +6,23 @@ import { IonicModule, IonicRouteStrategy } from '@ionic/angular';
 
 import { AppRoutingModule } from './app-routing.module';
 import { AppComponent } from './app.component';
+import { FormsModule } from '@angular/forms';
+import { CalendarModule, DateAdapter } from 'angular-calendar';
+import { adapterFactory } from 'angular-calendar/date-adapters/date-fns';
+
 
 @NgModule({
   declarations: [AppComponent],
-  imports: [BrowserModule, IonicModule.forRoot(), AppRoutingModule],
+  imports: [
+    BrowserModule,
+    IonicModule.forRoot(),
+    AppRoutingModule,
+    FormsModule,
+    CalendarModule.forRoot({
+      provide: DateAdapter,
+      useFactory: adapterFactory,
+    }),
+  ],
   providers: [{ provide: RouteReuseStrategy, useClass: IonicRouteStrategy }],
   bootstrap: [AppComponent],
 })

+ 17 - 0
app/calendar/calendar-routing.module.ts

@@ -0,0 +1,17 @@
+import { NgModule } from '@angular/core';
+import { Routes, RouterModule } from '@angular/router';
+
+import { CalendarPage } from './calendar.page';
+
+const routes: Routes = [
+  {
+    path: '',
+    component: CalendarPage
+  }
+];
+
+@NgModule({
+  imports: [RouterModule.forChild(routes)],
+  exports: [RouterModule],
+})
+export class CalendarPageRoutingModule {}

+ 20 - 0
app/calendar/calendar.module.ts

@@ -0,0 +1,20 @@
+import { NgModule } from '@angular/core';
+import { CommonModule } from '@angular/common';
+import { FormsModule } from '@angular/forms';
+
+import { IonicModule } from '@ionic/angular';
+
+import { CalendarPageRoutingModule } from './calendar-routing.module';
+
+import { CalendarPage } from './calendar.page';
+
+@NgModule({
+  imports: [
+    CommonModule,
+    FormsModule,
+    IonicModule,
+    CalendarPageRoutingModule
+  ],
+  declarations: [CalendarPage]
+})
+export class CalendarPageModule {}

+ 13 - 0
app/calendar/calendar.page.html

@@ -0,0 +1,13 @@
+<ion-header [translucent]="true">
+  <ion-toolbar>
+    <ion-title>calendar</ion-title>
+  </ion-toolbar>
+</ion-header>
+
+<ion-content [fullscreen]="true">
+  <ion-header collapse="condense">
+    <ion-toolbar>
+      <ion-title size="large">calendar</ion-title>
+    </ion-toolbar>
+  </ion-header>
+</ion-content>

+ 0 - 0
app/calendar/calendar.page.scss


+ 17 - 0
app/calendar/calendar.page.spec.ts

@@ -0,0 +1,17 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+import { CalendarPage } from './calendar.page';
+
+describe('CalendarPage', () => {
+  let component: CalendarPage;
+  let fixture: ComponentFixture<CalendarPage>;
+
+  beforeEach(() => {
+    fixture = TestBed.createComponent(CalendarPage);
+    component = fixture.componentInstance;
+    fixture.detectChanges();
+  });
+
+  it('should create', () => {
+    expect(component).toBeTruthy();
+  });
+});

+ 15 - 0
app/calendar/calendar.page.ts

@@ -0,0 +1,15 @@
+import { Component, OnInit } from '@angular/core';
+
+@Component({
+  selector: 'app-calendar',
+  templateUrl: './calendar.page.html',
+  styleUrls: ['./calendar.page.scss'],
+})
+export class CalendarPage implements OnInit {
+
+  constructor() { }
+
+  ngOnInit() {
+  }
+
+}

+ 92 - 7
app/tab1/tab1.page.html

@@ -1,17 +1,102 @@
 <ion-header [translucent]="true">
-  <ion-toolbar>
-    <ion-title>
-      Tab 1
-    </ion-title>
-  </ion-toolbar>
 </ion-header>
 
 <ion-content [fullscreen]="true">
   <ion-header collapse="condense">
     <ion-toolbar>
-      <ion-title size="large">Tab 1</ion-title>
+      <ion-searchbar placeholder="搜索"></ion-searchbar>
     </ion-toolbar>
   </ion-header>
+    
+  <!-- 方框 -->
+  <ion-card (click)="openModal()">
+    <ion-card-content>
+      <ion-grid>
+        <ion-row>
+          <ion-col size="3">
+            <div class="info-box">初始</div>
+            <div class="info-value">--KG</div>
+          </ion-col>
+          <ion-col size="3">
+            <div class="info-box">当前</div>
+            <div class="info-value">--KG</div>
+          </ion-col>
+          <ion-col size="3">
+            <div class="info-box">目标</div>
+            <div class="info-value">--KG</div>
+          </ion-col>
+          <ion-col size="3">
+            <div class="info-box">赏金</div>
+            <div class="info-value">--元</div>
+          </ion-col>
+        </ion-row>
+      </ion-grid>
+    </ion-card-content>
+  </ion-card>
 
-  <app-explore-container name="Tab 1 page"></app-explore-container>
+  <!-- 3D效果的圆 -->
+  <div class="wave-container">
+    <div class="wave-circle">
+      <div class="wave">
+        <div class="wave-inner"></div>
+      </div>
+    </div>
+  </div>
+
+  <!-- 每日小习惯 -->
+  <div class="daily-habit">
+    <div class="daily-habit-line"></div>
+    <div class="daily-habit-text">每日小习惯</div>
+  </div>
+
+  
+   <!-- 大方框 -->
+  <ion-card>
+    <ion-card-content>
+      <ion-grid>
+        <ion-row>
+          <ion-col size="3">
+            <button class="habit-button" (click)="toggleButtonColor('运动')">
+              <ion-icon name="dumbbell"></ion-icon>
+              <div class="habit-label">运动</div>
+            </button>
+          </ion-col>
+          <ion-col size="3">
+            <ion-button shape="round" class="habit-button" (click)="toggleButtonColor('少吃')">
+              <ion-icon name="nutrition"></ion-icon>
+              <div>少吃</div>
+            </ion-button>
+          </ion-col>
+          <ion-col size="3">
+            <ion-button shape="round" class="habit-button" (click)="toggleButtonColor('喝水')">
+              <ion-icon name="water"></ion-icon>
+              <div>喝水</div>
+            </ion-button>
+          </ion-col>
+          <ion-col size="3">
+            <ion-button shape="round" class="habit-button" (click)="toggleButtonColor('吃蔬菜')">
+              <ion-icon name="leaf"></ion-icon>
+              <div>吃蔬菜</div>
+            </ion-button>
+          </ion-col>
+        </ion-row>
+        <ion-row>
+          <ion-col size="3">
+            <ion-button shape="round" class="habit-button" (click)="toggleButtonColor('水果')">
+              <ion-icon name="apple"></ion-icon>
+              <div>水果</div>
+            </ion-button>
+          </ion-col>
+          <ion-col size="3">
+            <ion-button shape="round" class="habit-button" (click)="toggleButtonColor('经常站立')">
+              <ion-icon name="walk"></ion-icon>
+              <div>经常站立</div>
+            </ion-button>
+          </ion-col>
+        </ion-row>
+      </ion-grid>
+    </ion-card-content>
+  </ion-card>
 </ion-content>
+  
+

+ 121 - 0
app/tab1/tab1.page.scss

@@ -0,0 +1,121 @@
+ion-searchbar {
+  --background: #f4f4f4;
+  margin: 10px;
+  border-radius: 10px;
+}
+
+.info-box {
+  text-align: center;
+  font-weight: bold;
+}
+
+.info-value {
+  text-align: center;
+  margin-top: 10px;
+  font-size: 1.2em;
+  color: #888;
+}
+
+.wave-container {
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  height: 200px;
+  position: relative;
+  margin: 20px 0;
+}
+
+.wave-circle {
+  width: 150px;
+  height: 150px;
+  background: radial-gradient(circle at 50% 50%, #6ec1e4, #378ab7);
+  border-radius: 50%;
+  position: relative;
+  overflow: hidden;
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  box-shadow: 0 4px 15px rgba(0, 0, 0, 0.2);
+}
+
+.wave {
+  position: absolute;
+  width: 200%;
+  height: 200%;
+  top: -75%;
+  left: -50%;
+  background: rgba(255, 255, 255, 0.4);
+  border-radius: 45%;
+  animation: wave 4s infinite linear;
+}
+
+@keyframes wave {
+  0% {
+    transform: rotate(0deg);
+  }
+  100% {
+    transform: rotate(360deg);
+  }
+}
+
+// 每日小习惯
+.daily-habit {
+  display: flex;
+  align-items: center;
+  margin: 20px 0;
+}
+
+.daily-habit-line {
+  width: 5px;
+  height: 20px;
+  background-color: green;
+  margin-right: 10px;
+}
+
+.daily-habit-text {
+  font-size: 1.2em;
+  color: #666;
+}
+
+.habit-button {
+  width: 60px;
+  height: 60px;
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  font-size: 0.8em;
+  margin: 10px 0;
+  background-color: #fff;
+  border: 2px solid #000;
+  border-radius: 50%;
+  transition: background-color 0.3s;
+}
+
+.habit-button ion-icon {
+  font-size: 1.5em;
+}
+
+.habit-label {
+  text-align: center;
+  font-size: 0.8em;
+  color: #666;
+  margin-top: 5px;
+}
+
+.habit-button.blue {
+  background-color: #007bff; /* 蓝色背景 */
+}
+
+.habit-button.white {
+  background-color: #fff; /* 白色背景 */
+}
+
+// .habit-button div {
+//   font-size: 0.8em;
+//   margin-top: 5px;
+// }
+
+.habit-button:active {
+  transform: scale(0.9);
+  box-shadow: 0 2px 10px rgba(0, 0, 0, 0.2);
+}

+ 3 - 2
app/tab1/tab1.page.spec.ts

@@ -1,9 +1,10 @@
 import { ComponentFixture, TestBed } from '@angular/core/testing';
 import { IonicModule } from '@ionic/angular';
-
 import { ExploreContainerComponentModule } from '../explore-container/explore-container.module';
-
 import { Tab1Page } from './tab1.page';
+import{Component}from'@angular/core';
+
+
 
 describe('Tab1Page', () => {
   let component: Tab1Page;

+ 15 - 2
app/tab1/tab1.page.ts

@@ -1,5 +1,5 @@
 import { Component } from '@angular/core';
-
+import { AlertController } from '@ionic/angular';
 @Component({
   selector: 'app-tab1',
   templateUrl: 'tab1.page.html',
@@ -7,6 +7,19 @@ import { Component } from '@angular/core';
 })
 export class Tab1Page {
 
-  constructor() {}
+  constructor(private alertController: AlertController) {}
 
+  async openModal() {
+    const alert = await this.alertController.create({
+      header: '提示',
+      message: '弹窗了',
+      buttons: ['确定']
+    });
+    await alert.present();
+  }
+  blueColor:boolean=false;
+  toggleButtonColor(habit:string){
+    this.blueColor=!this.blueColor;
+  }
 }
+

+ 8 - 1
app/tab2/tab2.module.ts

@@ -7,13 +7,20 @@ import { ExploreContainerComponentModule } from '../explore-container/explore-co
 
 import { Tab2PageRoutingModule } from './tab2-routing.module';
 
+import { CalendarModule, DateAdapter } from 'angular-calendar';
+import { adapterFactory } from 'angular-calendar/date-adapters/date-fns';
+
 @NgModule({
   imports: [
     IonicModule,
     CommonModule,
     FormsModule,
     ExploreContainerComponentModule,
-    Tab2PageRoutingModule
+    Tab2PageRoutingModule,
+    CalendarModule.forRoot({
+      provide: DateAdapter,
+      useFactory: adapterFactory,
+    }),
   ],
   declarations: [Tab2Page]
 })

+ 38 - 11
app/tab2/tab2.page.html

@@ -1,17 +1,44 @@
-<ion-header [translucent]="true">
+<ion-header>
   <ion-toolbar>
-    <ion-title>
-      Tab 2
-    </ion-title>
+    <ion-title>备忘录</ion-title>
   </ion-toolbar>
 </ion-header>
 
-<ion-content [fullscreen]="true">
-  <ion-header collapse="condense">
-    <ion-toolbar>
-      <ion-title size="large">Tab 2</ion-title>
-    </ion-toolbar>
-  </ion-header>
+<ion-content>
+  <!-- 搜索框 -->
+  <ion-item>
+    <ion-label position="floating">搜索备忘录</ion-label>
+    <ion-input [(ngModel)]="searchQuery"></ion-input>
+  </ion-item>
 
-  <app-explore-container name="Tab 2 page"></app-explore-container>
+  <!-- 备忘录输入 -->
+  <ion-item>
+    <ion-label position="floating">备忘录内容</ion-label>
+    <ion-input [(ngModel)]="memoContent"></ion-input>
+  </ion-item>
+  <ion-button expand="full" (click)="addMemo()">添加备忘录</ion-button>
+
+  <!-- 备忘录列表 -->
+  <ion-list>
+    <ion-item *ngFor="let memo of filteredMemos()">
+      {{ memo }}
+    </ion-item>
+  </ion-list>
+
+  <!-- 日历 -->
+  <!-- 日历 -->
+  <ion-card>
+    <ion-card-header>
+      <ion-card-title>日历</ion-card-title>
+    </ion-card-header>
+    <ion-card-content>
+      <div class="calendar-container">
+        <mwl-calendar-month-view
+          [viewDate]="viewDate"
+          [events]="events"
+          (dayClicked)="dayClicked($event.day)">
+        </mwl-calendar-month-view>
+      </div>
+    </ion-card-content>
+  </ion-card>
 </ion-content>

+ 25 - 0
app/tab2/tab2.page.scss

@@ -0,0 +1,25 @@
+ion-card {
+      margin: 20px 0;
+    }
+    
+ion-item {
+      margin: 10px 0;
+    }
+    
+ion-button {
+      margin: 20px 0;
+    }
+.calendar-container {
+      display: flex;
+      justify-content: center;
+      align-items: center;
+      width: 100%;
+      overflow-x: auto;
+      padding: 20px 0;
+    }
+    
+::ng-deep mwl-calendar-month-view {
+      width: 100%;
+      max-width: 100%;
+    }
+    

+ 22 - 2
app/tab2/tab2.page.ts

@@ -1,12 +1,32 @@
 import { Component } from '@angular/core';
-
+import { CalendarEvent } from 'angular-calendar';
 @Component({
   selector: 'app-tab2',
   templateUrl: 'tab2.page.html',
   styleUrls: ['tab2.page.scss']
 })
+
 export class Tab2Page {
+  memoContent: string = '';
+  searchQuery: string = '';
+  memos: string[] = [];
+  viewDate: Date = new Date();
+  events: CalendarEvent[] = [];
 
   constructor() {}
 
-}
+  addMemo() {
+    if (this.memoContent.trim() !== '') {
+      this.memos.push(this.memoContent);
+      this.memoContent = '';
+    }
+  }
+
+  filteredMemos() {
+    return this.memos.filter((memo) => memo.includes(this.searchQuery));
+  }
+
+  dayClicked(day: any) {
+    console.log('Day clicked:', day);
+  }
+}

+ 68 - 6
app/tab3/tab3.page.html

@@ -1,9 +1,4 @@
 <ion-header [translucent]="true">
-  <ion-toolbar>
-    <ion-title>
-      Tab 3
-    </ion-title>
-  </ion-toolbar>
 </ion-header>
 
 <ion-content [fullscreen]="true">
@@ -12,6 +7,73 @@
       <ion-title size="large">Tab 3</ion-title>
     </ion-toolbar>
   </ion-header>
+  
+  <!-- 个人信息板块 -->
+  <ion-card>
+    <ion-card-header>
+      <ion-avatar>
+        <img src="assets/img/head.jpg" class="avatar-img">
+      </ion-avatar>
+      <ion-card-title>昵称:鱼聪明</ion-card-title>
+      <ion-card-subtitle>个性签名:坚持不懈,直到成功</ion-card-subtitle>
+    </ion-card-header>
 
-  <app-explore-container name="Tab 3 page"></app-explore-container>
+    <ion-card-content>
+      <ion-grid>
+        <ion-row>
+          <ion-col size="6">
+            打卡天数: 天
+          </ion-col>
+          <ion-col size="6">
+            累计减重: kg
+          </ion-col>
+        </ion-row>
+        <ion-row>
+          <ion-col size="6">
+            当前赏金: 元
+          </ion-col>
+          <ion-col size="6">
+            BMI指数:
+          </ion-col>
+        </ion-row>
+      </ion-grid>
+    </ion-card-content>
+  </ion-card>
+
+  <!-- 功能菜单 -->
+  <ion-list>
+    <ion-item button (click)="onItemClick('我的目标')">
+      <ion-icon name="body" slot="start"></ion-icon>
+      我的目标
+    </ion-item>
+    <ion-item button (click)="onItemClick('减肥搭子')">
+      <ion-icon name="people" slot="start"></ion-icon>
+      减肥搭子
+    </ion-item>
+    <ion-item button (click)="onItemClick('赏金商店')">
+      <ion-icon name="cash" slot="start"></ion-icon>
+      赏金商店
+    </ion-item>
+    <ion-item button (click)="onItemClick('我的账户')">
+      <ion-icon name="person" slot="start"></ion-icon>
+      我的账户
+    </ion-item>
+    <ion-item button (click)="onItemClick('背景设置')">
+      <ion-icon name="settings" slot="start"></ion-icon>
+      背景设置
+    </ion-item>
+    <ion-item button (click)="onItemClick('操作手册')">
+      <ion-icon name="book" slot="start"></ion-icon>
+      操作手册
+    </ion-item>
+    <ion-item button (click)="onItemClick('数据备份')">
+      <ion-icon name="cloud-download" slot="start"></ion-icon>
+      数据备份
+    </ion-item>
+    <ion-item button (click)="onItemClick('数据下载')">
+      <ion-icon name="download" slot="start"></ion-icon>
+      数据下载
+    </ion-item>
+  </ion-list>
+  
 </ion-content>

+ 21 - 0
app/tab3/tab3.page.scss

@@ -0,0 +1,21 @@
+.avatar-img {
+      width: 100%;
+      height: 100%;
+      border-radius: 50%;
+      object-fit: cover;
+    }
+    
+    ion-item.button {
+      --background-hover: rgba(0, 0, 0, 0.1);
+      --background-activated: rgba(0, 0, 0, 0.2);
+      transition: background-color 0.2s;
+    
+      &:hover {
+        background-color: var(--background-hover);
+      }
+    
+      &:active {
+        background-color: var(--background-activated);
+      }
+    }
+    

+ 10 - 1
app/tab3/tab3.page.ts

@@ -1,4 +1,5 @@
 import { Component } from '@angular/core';
+import { ToastController } from '@ionic/angular';
 
 @Component({
   selector: 'app-tab3',
@@ -7,6 +8,14 @@ import { Component } from '@angular/core';
 })
 export class Tab3Page {
 
-  constructor() {}
+  constructor(private toastController: ToastController) {}
+
+  async onItemClick(item: string) {
+    const toast = await this.toastController.create({
+      message: `Clicked on ${item}`,
+      duration: 2000,
+    });
+    toast.present();
+  }
 
 }

+ 4 - 0
app/tabs/tabs-routing.module.ts

@@ -19,6 +19,10 @@ const routes: Routes = [
         path: 'tab3',
         loadChildren: () => import('../tab3/tab3.module').then(m => m.Tab3PageModule)
       },
+      {
+        path: 'calendar',
+        loadChildren: () => import('../../modules/user/mine/mine.module').then(mod => mod.MinePageModule)
+      },
       {
         path: '',
         redirectTo: '/tabs/tab1',

+ 11 - 6
app/tabs/tabs.page.html

@@ -2,18 +2,23 @@
 
   <ion-tab-bar slot="bottom">
     <ion-tab-button tab="tab1" href="/tabs/tab1">
-      <ion-icon aria-hidden="true" name="triangle"></ion-icon>
-      <ion-label>Tab 1</ion-label>
+      <ion-icon aria-hidden="true" name="home"></ion-icon>
+      <ion-label>首页</ion-label>
     </ion-tab-button>
 
     <ion-tab-button tab="tab2" href="/tabs/tab2">
-      <ion-icon aria-hidden="true" name="ellipse"></ion-icon>
-      <ion-label>Tab 2</ion-label>
+      <ion-icon aria-hidden="true" name="pencil"></ion-icon>
+      <ion-label>记录一下</ion-label>
     </ion-tab-button>
 
     <ion-tab-button tab="tab3" href="/tabs/tab3">
-      <ion-icon aria-hidden="true" name="square"></ion-icon>
-      <ion-label>Tab 3</ion-label>
+      <ion-icon aria-hidden="true" name="person"></ion-icon>
+      <ion-label>我的</ion-label>
+    </ion-tab-button>
+    
+    <ion-tab-button tab="calendar" href="/tabs/calendar">
+      <ion-icon aria-hidden="true" name="calendar"></ion-icon>
+      <ion-label>日历</ion-label>
     </ion-tab-button>
   </ion-tab-bar>
 

BIN
assets/img/head.jpg


+ 17 - 0
modules/user/edit-info/edit-info-routing.module.ts

@@ -0,0 +1,17 @@
+import { NgModule } from '@angular/core';
+import { Routes, RouterModule } from '@angular/router';
+
+import { EditInfoPage } from './edit-info.page';
+
+const routes: Routes = [
+  {
+    path: '',
+    component: EditInfoPage
+  }
+];
+
+@NgModule({
+  imports: [RouterModule.forChild(routes)],
+  exports: [RouterModule],
+})
+export class EditInfoPageRoutingModule {}

+ 20 - 0
modules/user/edit-info/edit-info.module.ts

@@ -0,0 +1,20 @@
+import { NgModule } from '@angular/core';
+import { CommonModule } from '@angular/common';
+import { FormsModule } from '@angular/forms';
+
+import { IonicModule } from '@ionic/angular';
+
+import { EditInfoPageRoutingModule } from './edit-info-routing.module';
+
+import { EditInfoPage } from './edit-info.page';
+
+@NgModule({
+  imports: [
+    CommonModule,
+    FormsModule,
+    IonicModule,
+    EditInfoPageRoutingModule
+  ],
+  declarations: [EditInfoPage]
+})
+export class EditInfoPageModule {}

+ 47 - 0
modules/user/edit-info/edit-info.page.html

@@ -0,0 +1,47 @@
+<ion-header [translucent]="true">
+  <ion-toolbar>
+    <ion-title>资料编辑</ion-title>
+  </ion-toolbar>
+</ion-header>
+
+<ion-content [fullscreen]="true">
+  <ion-header collapse="condense">
+    <ion-toolbar>
+      <ion-title size="large">资料编辑</ion-title>
+    </ion-toolbar>
+  </ion-header>
+
+  <ion-card>
+    <ion-card-header>
+      <ion-card-title>{{currentUser?.get('username')}} - {{currentUser?.id}}</ion-card-title>
+    </ion-card-header>
+    <ion-card-content>
+      <ion-list>
+        <ion-item>
+          <ion-input label="姓名" type="text" [(ngModel)]="userInfo.name"></ion-input>
+        </ion-item>
+        <ion-item>
+          <ion-input label="手机" type="tel" [(ngModel)]="userInfo.mobile"></ion-input>
+        </ion-item>
+        <ion-item>
+          <ion-select label="性别" [(ngModel)]="userInfo.gender">
+            <ion-select-option value="男">男</ion-select-option>
+            <ion-select-option value="女">女</ion-select-option>
+          </ion-select>
+        </ion-item>
+        <ion-item>
+          <ion-label>生日</ion-label>
+          <ion-datetime-button datetime="birthday"></ion-datetime-button>
+          <ion-modal [keepContentsMounted]="true">
+            <ng-template>
+              <ion-datetime id="birthday" displayFormat="MM/DD/YYYY" [(ngModel)]="userInfo.birthday"></ion-datetime>
+            </ng-template>
+          </ion-modal>
+        </ion-item>
+      </ion-list>
+    </ion-card-content>
+  </ion-card>
+
+  <ion-button expand="block" (click)="save()">保存</ion-button>
+  <ion-button expand="block" (click)="cancel()">取消</ion-button>
+</ion-content>

+ 0 - 0
modules/user/edit-info/edit-info.page.scss


+ 17 - 0
modules/user/edit-info/edit-info.page.spec.ts

@@ -0,0 +1,17 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+import { EditInfoPage } from './edit-info.page';
+
+describe('EditInfoPage', () => {
+  let component: EditInfoPage;
+  let fixture: ComponentFixture<EditInfoPage>;
+
+  beforeEach(() => {
+    fixture = TestBed.createComponent(EditInfoPage);
+    component = fixture.componentInstance;
+    fixture.detectChanges();
+  });
+
+  it('should create', () => {
+    expect(component).toBeTruthy();
+  });
+});

+ 56 - 0
modules/user/edit-info/edit-info.page.ts

@@ -0,0 +1,56 @@
+import { Component, OnInit } from '@angular/core';
+import { NavController } from '@ionic/angular';
+import * as Parse from 'parse';
+
+@Component({
+  selector: 'app-edit-info',
+  templateUrl: './edit-info.page.html',
+  styleUrls: ['./edit-info.page.scss'],
+})
+export class EditInfoPage implements OnInit {
+
+  userInfo: any = {
+    name: '',
+    mobile: '',
+    gender: '',
+    birthday: ''
+  };
+  currentUser:Parse.User|undefined
+  constructor(private navController: NavController) {}
+
+  ngOnInit() {
+    this.currentUser = Parse.User.current();
+    if (this.currentUser) {
+      // 修改uesrInfo赋值逻辑,仅加载被编辑的字段属性值
+      let json = this.currentUser.toJSON();
+      for (const key in json) {
+        if (this.userInfo.hasOwnProperty(key)) {
+          this.userInfo[key] = json[key]
+        }
+      }
+    }
+    console.log(this.userInfo)
+  }
+
+  save() {
+    this.currentUser = Parse.User.current();
+    if (this.currentUser) {
+      console.log(this.userInfo)
+      for (const key in this.userInfo) {
+        if (this.userInfo.hasOwnProperty(key)) {
+          this.currentUser.set(key, this.userInfo[key]);
+        }
+      }
+      this.currentUser.save().then(() => {
+        this.navController.back();
+      }).catch((error) => {
+        console.error('Error saving user data: ', error);
+      });
+    }
+  }
+
+  cancel() {
+    this.navController.back();
+  }
+
+}

+ 17 - 0
modules/user/login/login-routing.module.ts

@@ -0,0 +1,17 @@
+import { NgModule } from '@angular/core';
+import { Routes, RouterModule } from '@angular/router';
+
+import { LoginPage } from './login.page';
+
+const routes: Routes = [
+  {
+    path: '',
+    component: LoginPage
+  }
+];
+
+@NgModule({
+  imports: [RouterModule.forChild(routes)],
+  exports: [RouterModule],
+})
+export class LoginPageRoutingModule {}

+ 20 - 0
modules/user/login/login.module.ts

@@ -0,0 +1,20 @@
+import { NgModule } from '@angular/core';
+import { CommonModule } from '@angular/common';
+import { FormsModule } from '@angular/forms';
+
+import { IonicModule } from '@ionic/angular';
+
+import { LoginPageRoutingModule } from './login-routing.module';
+
+import { LoginPage } from './login.page';
+
+@NgModule({
+  imports: [
+    CommonModule,
+    FormsModule,
+    IonicModule,
+    LoginPageRoutingModule
+  ],
+  declarations: [LoginPage]
+})
+export class LoginPageModule {}

+ 38 - 0
modules/user/login/login.page.html

@@ -0,0 +1,38 @@
+<ion-header [translucent]="true">
+  <ion-toolbar>
+    <ion-title>登录/注册</ion-title>
+  </ion-toolbar>
+</ion-header>
+
+<ion-content [fullscreen]="true">
+  <ion-header collapse="condense">
+    <ion-toolbar>
+      <ion-title size="large">登录/注册</ion-title>
+    </ion-toolbar>
+  </ion-header>
+
+  <ion-card>
+    <ion-card-header>
+      <ion-card-title>登录/注册</ion-card-title>
+    </ion-card-header>
+  
+    <ion-card-content>
+
+      <ion-list [inset]="true">
+        <ion-item>
+          <ion-input [(ngModel)]="username" label="账号" placeholder="请输入用户名"></ion-input>
+        </ion-item>
+        <ion-item>
+          <ion-input [(ngModel)]="password" label="密码" type="password" placeholder="请输入密码"></ion-input>
+        </ion-item>
+      </ion-list>
+     
+    </ion-card-content>
+  
+    <ion-button (click)="login()" fill="clear">登录</ion-button>
+    <ion-button (click)="register()" fill="clear">注册</ion-button>
+  </ion-card>
+
+  <!-- 新增路由返回逻辑,执行back函数 -->
+  <ion-button expand="block" (click)="back()">返回</ion-button>
+</ion-content>

+ 0 - 0
modules/user/login/login.page.scss


+ 17 - 0
modules/user/login/login.page.spec.ts

@@ -0,0 +1,17 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+import { LoginPage } from './login.page';
+
+describe('LoginPage', () => {
+  let component: LoginPage;
+  let fixture: ComponentFixture<LoginPage>;
+
+  beforeEach(() => {
+    fixture = TestBed.createComponent(LoginPage);
+    component = fixture.componentInstance;
+    fixture.detectChanges();
+  });
+
+  it('should create', () => {
+    expect(component).toBeTruthy();
+  });
+});

+ 93 - 0
modules/user/login/login.page.ts

@@ -0,0 +1,93 @@
+import { Component, OnInit } from '@angular/core';
+import { AlertController, NavController } from '@ionic/angular';
+import * as Parse from "parse"
+// 引用Router服务
+@Component({
+  selector: 'app-login',
+  templateUrl: './login.page.html',
+  styleUrls: ['./login.page.scss'],
+})
+export class LoginPage implements OnInit {
+
+  username:string = ""
+  password:string = ""
+  constructor(
+    // 新增:Router服务,用于路由跳转
+    private navCtrl:NavController,
+    private alertController:AlertController
+  ) { }
+
+  ngOnInit() {
+  }
+
+  async login(){
+    let user
+    try {
+      user = await Parse.User.logIn(this.username,this.password)
+    } catch (error:any) {
+      let message:string = ""
+      // 新增提示词详情,根据Parse.User.login方法返回的不同英文提示词,增加对应的中文内容转换
+      if(error?.message.indexOf("is required")>-1){
+        message = "必须输入账号或邮箱"
+      }
+      if(error?.message.indexOf("Invalid username")>-1){
+        message = "账号或密码错误,请检查"
+      }
+      this.presentAlert({
+        header:"登录失败",
+        subHeader:"状态码:"+error.code,
+        message:message || error.message
+      })
+    }
+    console.log(user)
+    if(user?.id){
+      this.navCtrl.back()
+    }
+  }
+  async register(){
+    let user = new Parse.User()
+    user.set("username",this.username)
+    user.set("password",this.password)
+    try {
+        let result = await user.signUp();
+        console.log(result)
+        if(result?.id){
+          this.navCtrl.back()
+        }
+        // Hooray! Let them use the app now.
+    } catch (error:any) {
+        // 新增提示词详情,根据Parse.User.signUp方法返回的不同英文提示词,增加对应的中文内容转换
+        let message:string = ""
+        if(error?.message.indexOf("already exists")>-1){
+          message = "该账号已存在请修改后重试"
+        }
+        if(error?.message.indexOf("empty")>-1){
+          message = "账号不能为空请输入后重试"
+        }
+        this.presentAlert({
+          header:"注册失败",
+          subHeader:"状态码:"+error.code,
+          message:message || error.message
+        })
+    }
+  }
+
+  async presentAlert(options:{header:string,subHeader:string,message:string}) {
+    const alert = await this.alertController.create({
+      header: options?.header,
+      subHeader: options?.subHeader,
+      message: options?.message,
+      buttons: ['好的'],
+    });
+
+    await alert.present();
+  }
+
+  /**
+   * 返回上级页面函数
+   * @desc
+   */
+  back(){
+    this.navCtrl.back()
+  }
+}

+ 17 - 0
modules/user/mine/mine-routing.module.ts

@@ -0,0 +1,17 @@
+import { NgModule } from '@angular/core';
+import { Routes, RouterModule } from '@angular/router';
+
+import { MinePage } from './mine.page';
+
+const routes: Routes = [
+  {
+    path: '',
+    component: MinePage
+  }
+];
+
+@NgModule({
+  imports: [RouterModule.forChild(routes)],
+  exports: [RouterModule],
+})
+export class MinePageRoutingModule {}

+ 20 - 0
modules/user/mine/mine.module.ts

@@ -0,0 +1,20 @@
+import { NgModule } from '@angular/core';
+import { CommonModule } from '@angular/common';
+import { FormsModule } from '@angular/forms';
+
+import { IonicModule } from '@ionic/angular';
+
+import { MinePageRoutingModule } from './mine-routing.module';
+
+import { MinePage } from './mine.page';
+
+@NgModule({
+  imports: [
+    CommonModule,
+    FormsModule,
+    IonicModule,
+    MinePageRoutingModule
+  ],
+  declarations: [MinePage]
+})
+export class MinePageModule {}

+ 29 - 0
modules/user/mine/mine.page.html

@@ -0,0 +1,29 @@
+<ion-header [translucent]="true">
+  <ion-toolbar>
+    <ion-title>我的</ion-title>
+  </ion-toolbar>
+</ion-header>
+
+<ion-content [fullscreen]="true">
+  <ion-header collapse="condense">
+    <ion-toolbar>
+      <ion-title size="large">我的</ion-title>
+    </ion-toolbar>
+  </ion-header>
+
+  <ion-card>
+    <img alt="" src="https://ionicframework.com/docs/img/demos/card-media.png" />
+    <ion-card-header>
+      <ion-card-title>{{user?.get("username") || '未登录'}}</ion-card-title>
+      <ion-card-subtitle *ngIf="!user?.id">请您登陆后继续使用</ion-card-subtitle>
+      <ion-card-subtitle *ngIf="user?.id">{{user?.get("name")}}-{{user?.get("gender")}}</ion-card-subtitle>
+    </ion-card-header>
+ 
+    <!-- 新增:根据用户状态,显示登录/登出按钮,执行跳转或登出函数 -->
+    <ion-button *ngIf="!user?.id" fill="clear" routerLink="/user/login">登录</ion-button>
+    <ion-button *ngIf="user?.id" fill="clear" routerLink="/user/edit/info">编辑资料</ion-button>
+    <ion-button *ngIf="user?.id" fill="clear" (click)="logout()">登出</ion-button>
+  </ion-card>
+
+
+</ion-content>

+ 0 - 0
modules/user/mine/mine.page.scss


+ 17 - 0
modules/user/mine/mine.page.spec.ts

@@ -0,0 +1,17 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+import { MinePage } from './mine.page';
+
+describe('MinePage', () => {
+  let component: MinePage;
+  let fixture: ComponentFixture<MinePage>;
+
+  beforeEach(() => {
+    fixture = TestBed.createComponent(MinePage);
+    component = fixture.componentInstance;
+    fixture.detectChanges();
+  });
+
+  it('should create', () => {
+    expect(component).toBeTruthy();
+  });
+});

+ 28 - 0
modules/user/mine/mine.page.ts

@@ -0,0 +1,28 @@
+import { Component, OnInit } from '@angular/core';
+// 由于Parse本身是js库,在ts中加载需要通过 * as Parse转换一下
+import * as Parse from "parse"
+@Component({
+  selector: 'app-mine',
+  templateUrl: './mine.page.html',
+  styleUrls: ['./mine.page.scss'],
+})
+export class MinePage implements OnInit {
+
+  constructor() {
+   
+  }
+
+  // 由于Parse.User.current()是随着localStorage变化的属性
+  // 为了避免首次复制后用户状态变化,页面不同步,通过get方法实现实时获取
+  user:Parse.User|undefined
+  async ngOnInit() {
+      this.user = await Parse.User.current()
+      setInterval(async ()=>{
+      this.user = await Parse.User.current()
+    },1000)
+  }
+  logout(){
+    Parse.User.logOut();
+  }
+
+}

+ 14 - 0
modules/user/user-routing.module.ts

@@ -0,0 +1,14 @@
+import { NgModule } from '@angular/core';
+import { RouterModule, Routes } from '@angular/router';
+
+const routes: Routes = [
+    {path: 'login', loadChildren: () => import('./login/login.module').then(mod => mod.LoginPageModule)},
+    {path: 'mine', loadChildren: () => import('./mine/mine.module').then(mod => mod.MinePageModule)},
+    {path: 'edit/info', loadChildren: () => import('./edit-info/edit-info.module').then(mod => mod.EditInfoPageModule)},
+];
+
+@NgModule({
+  imports: [RouterModule.forChild(routes)],
+  exports: [RouterModule]
+})
+export class UserRoutingModule { }

+ 14 - 0
modules/user/user.module.ts

@@ -0,0 +1,14 @@
+import { NgModule } from '@angular/core';
+import { CommonModule } from '@angular/common';
+
+import { UserRoutingModule } from './user-routing.module';
+
+
+@NgModule({
+  declarations: [],
+  imports: [
+    CommonModule,
+    UserRoutingModule
+  ]
+})
+export class UserModule { }

+ 0 - 2
package-lock.json

@@ -5794,7 +5794,6 @@
       "version": "0.31.1",
       "resolved": "https://registry.npmmirror.com/angular-calendar/-/angular-calendar-0.31.1.tgz",
       "integrity": "sha512-pjSIpoAaUzS/gx+14eOr4hPZhlQ8HxpiZypCSGqJNptq5PD+vOdVQ3h/Aaqnk86GraVcAQPXqfu64MtdKwTVNw==",
-      "license": "MIT",
       "dependencies": {
         "@scarf/scarf": "^1.1.1",
         "angular-draggable-droppable": "^8.0.0",
@@ -7680,7 +7679,6 @@
       "version": "3.6.0",
       "resolved": "https://registry.npmmirror.com/date-fns/-/date-fns-3.6.0.tgz",
       "integrity": "sha512-fRHTG8g/Gif+kSh50gaGEdToemgfj74aRX3swtiouboip5JDLAyDE9F11nHMIcvOaXeOC6D7SpNhi7uFyB7Uww==",
-      "license": "MIT",
       "funding": {
         "type": "github",
         "url": "https://github.com/sponsors/kossnocorp"