Selaa lähdekoodia

init: web3-1234567 0.1

未来全栈 9 kuukautta sitten
vanhempi
commit
7580d5fdd6

+ 134 - 9
package-lock.json

@@ -16,9 +16,12 @@
         "@angular/platform-browser": "^17.0.2",
         "@angular/platform-browser-dynamic": "^17.0.2",
         "@angular/router": "^17.0.2",
-        "@ionic/angular": "^8.0.0",
+        "@ionic/angular": "^8.2.2",
+        "@types/parse": "^3.0.9",
         "ionicons": "^7.0.0",
+        "parse": "^5.1.0",
         "rxjs": "~7.8.0",
+        "swiper": "^11.1.4",
         "tslib": "^2.3.0",
         "zone.js": "~0.14.2"
       },
@@ -2283,6 +2286,18 @@
         "node": ">=6.9.0"
       }
     },
+    "node_modules/@babel/runtime-corejs3": {
+      "version": "7.23.2",
+      "resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.23.2.tgz",
+      "integrity": "sha512-54cIh74Z1rp4oIjsHjqN+WM4fMyCBYe+LpZ9jWm51CZ1fbH3SkAzQD/3XLoNkjbJ7YEmjobLXyvQrFypRHOrXw==",
+      "dependencies": {
+        "core-js-pure": "^3.30.2",
+        "regenerator-runtime": "^0.14.0"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
     "node_modules/@babel/template": {
       "version": "7.24.6",
       "dev": true,
@@ -2597,10 +2612,11 @@
       "license": "BSD-3-Clause"
     },
     "node_modules/@ionic/angular": {
-      "version": "8.2.0",
-      "license": "MIT",
+      "version": "8.2.2",
+      "resolved": "https://registry.npmjs.org/@ionic/angular/-/angular-8.2.2.tgz",
+      "integrity": "sha512-c8siqeWN5iWvu7LlechiIYSWRAlvjL2dEKjB19xspAzZv+9aXrT9YWPz+DTeS2eTO6CnwvHJpG0tPlVxgjpr1g==",
       "dependencies": {
-        "@ionic/core": "8.2.0",
+        "@ionic/core": "8.2.2",
         "ionicons": "^7.0.0",
         "jsonc-parser": "^3.0.0",
         "tslib": "^2.3.0"
@@ -2624,8 +2640,9 @@
       }
     },
     "node_modules/@ionic/core": {
-      "version": "8.2.0",
-      "license": "MIT",
+      "version": "8.2.2",
+      "resolved": "https://registry.npmjs.org/@ionic/core/-/core-8.2.2.tgz",
+      "integrity": "sha512-gpWemL5IJjGDJPz6dltHnFyqioRl0sugs2PUXrwPaYwMnTDoRZ6iojYFovCIr5YJN99rHZprOthdcsYR/viGyQ==",
       "dependencies": {
         "@stencil/core": "^4.17.2",
         "ionicons": "^7.2.2",
@@ -3469,7 +3486,6 @@
     },
     "node_modules/@types/node": {
       "version": "20.12.12",
-      "dev": true,
       "license": "MIT",
       "dependencies": {
         "undici-types": "~5.26.4"
@@ -3483,6 +3499,14 @@
         "@types/node": "*"
       }
     },
+    "node_modules/@types/parse": {
+      "version": "3.0.9",
+      "resolved": "https://registry.npmjs.org/@types/parse/-/parse-3.0.9.tgz",
+      "integrity": "sha512-DGTHygc7krgmNAK8h42giwmAofCd9uv2++RD+zw6OmWI7AEnlTYZwEuWsx22SA2CSMQrZW8P2INHLpQbnQFUng==",
+      "dependencies": {
+        "@types/node": "*"
+      }
+    },
     "node_modules/@types/qs": {
       "version": "6.9.15",
       "dev": true,
@@ -5440,6 +5464,16 @@
         "url": "https://opencollective.com/core-js"
       }
     },
+    "node_modules/core-js-pure": {
+      "version": "3.37.1",
+      "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.37.1.tgz",
+      "integrity": "sha512-J/r5JTHSmzTxbiYYrzXg9w1VpqrYt+gexenBE9pugeyhwPZTAEJddyiReJWsLO6uNQ8xJZFbod6XC7KKwatCiA==",
+      "hasInstallScript": true,
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/core-js"
+      }
+    },
     "node_modules/core-util-is": {
       "version": "1.0.3",
       "dev": true,
@@ -5589,6 +5623,12 @@
         "node": ">= 8"
       }
     },
+    "node_modules/crypto-js": {
+      "version": "4.2.0",
+      "resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.2.0.tgz",
+      "integrity": "sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==",
+      "optional": true
+    },
     "node_modules/css-loader": {
       "version": "6.10.0",
       "dev": true,
@@ -8283,6 +8323,11 @@
         "postcss": "^8.1.0"
       }
     },
+    "node_modules/idb-keyval": {
+      "version": "6.2.1",
+      "resolved": "https://registry.npmjs.org/idb-keyval/-/idb-keyval-6.2.1.tgz",
+      "integrity": "sha512-8Sb3veuYCyrZL+VBt9LJfZjLUPWVvqn8tG28VqYNFCo43KHcKuq+b4EiXGeuaLAQWL2YmyDgMp2aSpH9JHsEQg=="
+    },
     "node_modules/ieee754": {
       "version": "1.2.1",
       "dev": true,
@@ -11288,6 +11333,25 @@
         "node": ">=6"
       }
     },
+    "node_modules/parse": {
+      "version": "5.1.0",
+      "resolved": "https://registry.npmjs.org/parse/-/parse-5.1.0.tgz",
+      "integrity": "sha512-46gVRe1JHsh21Ht0/Ko6PeMDl6wELLMYxnZPFD6iZm2EWsWnzi2txNGE6PvnIv+G7yOufZIOD0BCZLYOFl3toA==",
+      "dependencies": {
+        "@babel/runtime-corejs3": "7.23.2",
+        "idb-keyval": "6.2.1",
+        "react-native-crypto-js": "1.0.0",
+        "uuid": "9.0.1",
+        "ws": "8.16.0",
+        "xmlhttprequest": "1.8.0"
+      },
+      "engines": {
+        "node": ">=18 <21"
+      },
+      "optionalDependencies": {
+        "crypto-js": "4.2.0"
+      }
+    },
     "node_modules/parse-json": {
       "version": "5.2.0",
       "dev": true,
@@ -11323,6 +11387,38 @@
         "node": ">= 0.10"
       }
     },
+    "node_modules/parse/node_modules/uuid": {
+      "version": "9.0.1",
+      "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz",
+      "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==",
+      "funding": [
+        "https://github.com/sponsors/broofa",
+        "https://github.com/sponsors/ctavan"
+      ],
+      "bin": {
+        "uuid": "dist/bin/uuid"
+      }
+    },
+    "node_modules/parse/node_modules/ws": {
+      "version": "8.16.0",
+      "resolved": "https://registry.npmjs.org/ws/-/ws-8.16.0.tgz",
+      "integrity": "sha512-HS0c//TP7Ina87TfiPUz1rQzMhHrl/SG2guqRcTOIUYD2q8uhUdNHZYJUaQ8aTGPzCh+c6oawMKW35nFl1dxyQ==",
+      "engines": {
+        "node": ">=10.0.0"
+      },
+      "peerDependencies": {
+        "bufferutil": "^4.0.1",
+        "utf-8-validate": ">=5.0.2"
+      },
+      "peerDependenciesMeta": {
+        "bufferutil": {
+          "optional": true
+        },
+        "utf-8-validate": {
+          "optional": true
+        }
+      }
+    },
     "node_modules/parse5": {
       "version": "7.1.2",
       "dev": true,
@@ -11888,6 +11984,11 @@
       "dev": true,
       "license": "MIT"
     },
+    "node_modules/react-native-crypto-js": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/react-native-crypto-js/-/react-native-crypto-js-1.0.0.tgz",
+      "integrity": "sha512-FNbLuG/HAdapQoybeZSoes1PWdOj0w242gb+e1R0hicf3Gyj/Mf8M9NaED2AnXVOX01b2FXomwUiw1xP1K+8sA=="
+    },
     "node_modules/read-package-json": {
       "version": "7.0.1",
       "dev": true,
@@ -12007,7 +12108,6 @@
     },
     "node_modules/regenerator-runtime": {
       "version": "0.14.1",
-      "dev": true,
       "license": "MIT"
     },
     "node_modules/regenerator-transform": {
@@ -13375,6 +13475,24 @@
         "url": "https://github.com/sponsors/ljharb"
       }
     },
+    "node_modules/swiper": {
+      "version": "11.1.4",
+      "resolved": "https://registry.npmjs.org/swiper/-/swiper-11.1.4.tgz",
+      "integrity": "sha512-1n7kbYJB2dFEpUHRFszq7gys/ofIBrMNibwTiMvPHwneKND/t9kImnHt6CfGPScMHgI+dWMbGTycCKGMoOO1KA==",
+      "funding": [
+        {
+          "type": "patreon",
+          "url": "https://www.patreon.com/swiperjs"
+        },
+        {
+          "type": "open_collective",
+          "url": "http://opencollective.com/swiper"
+        }
+      ],
+      "engines": {
+        "node": ">= 4.7.0"
+      }
+    },
     "node_modules/symbol-observable": {
       "version": "4.0.0",
       "dev": true,
@@ -13871,7 +13989,6 @@
     },
     "node_modules/undici-types": {
       "version": "5.26.5",
-      "dev": true,
       "license": "MIT"
     },
     "node_modules/unicode-canonical-property-names-ecmascript": {
@@ -15029,6 +15146,14 @@
         }
       }
     },
+    "node_modules/xmlhttprequest": {
+      "version": "1.8.0",
+      "resolved": "https://registry.npmjs.org/xmlhttprequest/-/xmlhttprequest-1.8.0.tgz",
+      "integrity": "sha512-58Im/U0mlVBLM38NdZjHyhuMtCqa61469k2YP/AaPbvCoV9aQGUpbJBj1QRm2ytRiVQBD/fsw7L2bJGDVQswBA==",
+      "engines": {
+        "node": ">=0.4.0"
+      }
+    },
     "node_modules/y18n": {
       "version": "5.0.8",
       "dev": true,

+ 4 - 1
package.json

@@ -21,9 +21,12 @@
     "@angular/platform-browser": "^17.0.2",
     "@angular/platform-browser-dynamic": "^17.0.2",
     "@angular/router": "^17.0.2",
-    "@ionic/angular": "^8.0.0",
+    "@ionic/angular": "^8.2.2",
+    "@types/parse": "^3.0.9",
     "ionicons": "^7.0.0",
+    "parse": "^5.1.0",
     "rxjs": "~7.8.0",
+    "swiper": "^11.1.4",
     "tslib": "^2.3.0",
     "zone.js": "~0.14.2"
   },

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

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

+ 1 - 1
src/app/app.module.ts

@@ -10,7 +10,7 @@ import { IonicRouteStrategy } from '@ionic/angular';
   imports: [
     BrowserModule,
     IonicModule.forRoot(),
-    RouterModule, // 确保导入 RouterModule
+    RouterModule, // 确保导入 RouterModuler
     AppRoutingModule,
   ],
   providers: [

+ 2 - 2
src/app/tab1/tab1.page.html

@@ -49,7 +49,7 @@
             <ion-card class="feature-card" routerLink="/{{feature.route}}">
               <ion-card-content class="feature-card-content">
                 <!-- 图标 -->
-                <div class="feature-icon-container">
+                <div class="feature-icon-container" (click)="presentLoginAlert()">
                   <ion-icon [name]="feature.icon" class="feature-icon"></ion-icon>
                 </div>
                 <!-- 功能名称 -->
@@ -70,7 +70,7 @@
         <ion-row>
           <ion-col size="4" *ngFor="let feature of fitnessFeatures" (click)="onFitnessFeatureSelect(feature)">
             <ion-card class="feature-card" routerLink="/{{feature.route}}">
-              <ion-card-content class="feature-card-content">
+              <ion-card-content class="feature-card-content" (click)="presentLoginAlert()">
                 <!-- 图标 -->
                 <div class="feature-icon-container">
                   <ion-icon [name]="feature.icon" class="feature-icon"></ion-icon>

+ 17 - 0
src/app/tab1/tab1.page.scss

@@ -121,6 +121,23 @@ ion-icon {
   transform: translateY(-5px);
 }
 
+ion-alert {
+  --width: 100vw; /* 设置宽度为视口宽度的80% */
+  --max-height: 100vh; /* 设置最大高度为视口高度的80% */
+  overflow: auto; /* 确保内容可以滚动 */
+}
+
+//未登录弹窗
+.sc-ion-alert-ios-h{
+  --min-width: 300px;
+  --min-height: 140px !important;
+}
+
+
+
+
+
+
 
 
 /* 底部导航栏样式 */

+ 48 - 20
src/app/tab1/tab1.page.ts

@@ -2,6 +2,8 @@ import { Component, OnInit } from '@angular/core';
 import { Feature } from './tab1.module'; // 确保路径正确
 import { ModalController } from '@ionic/angular';
 import { Router } from '@angular/router';
+import { AlertController } from '@ionic/angular';
+
 @Component({
   selector: 'app-tab1',
   templateUrl: './tab1.page.html',
@@ -9,17 +11,43 @@ import { Router } from '@angular/router';
 
 })
 export class Tab1Page implements OnInit {
+  constructor(public modalController: ModalController,
+              private router: Router,
+              public alertController: AlertController,
+              ) {}
+
+  //按钮功能
+  alertButtons = [
+    {
+      text: '登录',
+      handler: () => {
+        this.router.navigateByUrl('/tabs/tab5')
+        //console.log('Login button clicked');
+      }
+    },
+    {
+      text: '取消',
+      role: 'cancel',
+      handler: () => {
+        //console.log('Cancel button clicked');
+      }
+    }
+  ];
+  // 点击功能栏时,显示未登录弹窗的方法
+  presentLoginAlert() {
+    this.alertController.create({
+      header: '未登录提示',
+      //subHeader: '您还没有登录账户',
+      message: '请登录以继续使用我们的功能。',
+      buttons: this.alertButtons,
+    }).then(alertEl => {
+      alertEl.present();
+    });
+  }
   
-  /* slideOpts = {
-    autoplay: true,
-    loop: true,
-    speed: 500
-  }; */
-  //Angular中组件初始化
   ngOnInit() {
   }
 
-
   //活动界面
   goToActivityDetail() {
     this.router.navigate(['/activity-detail']); // 替换为活动详情页面的路由路径
@@ -28,12 +56,12 @@ export class Tab1Page implements OnInit {
   // 定义金刚区的功能数组
   // 饮食区域功能列表
   features = [
-    { title: '饮食记录', icon: 'list-circle', route: '/diet-log' },
-    { title: '饮食计划', icon: 'calendar-clear', route: '/diet-plan' },
-    { title: '食谱定制', icon: 'pizza-outline', route: '/recipe' },
-    { title: '营养分析', icon: 'body-outline', route: '/nutrition' },
-    { title: '饮食视频', icon: 'film-outline', route: '/diet-videos' },
-    { title: '饮食提醒', icon: 'alarm-outline', route: '/diet-reminders' }
+    { title: '饮食记录', icon: 'list-circle', route: 'none' },
+    { title: '饮食计划', icon: 'calendar-clear', route: 'none' },
+    { title: '食谱定制', icon: 'pizza-outline', route: 'none' },
+    { title: '营养分析', icon: 'body-outline', route: 'none' },
+    { title: '饮食视频', icon: 'film-outline', route: 'none' },
+    { title: '饮食提醒', icon: 'alarm-outline', route: 'none' }
   ];
   onFeatureSelect(feature:Feature) {
     console.log('Navigate to:', feature.title);
@@ -42,12 +70,12 @@ export class Tab1Page implements OnInit {
 
   // 健身区域功能列表
   fitnessFeatures = [
-      { title: '运动记录', icon: 'stats-chart', route: '/fitness-log' },
-      { title: '运动计划', icon: 'construct', route: '/fitness-plan' },
-      { title: '目标挑战', icon: 'trophy', route: '/challenges' },
-      { title: '体重管理', icon: 'barbell', route: '/weight-management' },
-      { title: '健身视频', icon: 'fitness', route: '/fitness-videos' },
-      { title: '虚拟教练', icon: 'infinite', route: '/virtual-coach' } 
+      { title: '运动记录', icon: 'stats-chart', route: 'none' },
+      { title: '运动计划', icon: 'construct', route: 'none' },
+      { title: '目标挑战', icon: 'trophy', route: 'none' },
+      { title: '体重管理', icon: 'barbell', route: 'none' },
+      { title: '健身视频', icon: 'fitness', route: 'none' },
+      { title: '虚拟教练', icon: 'infinite', route: 'none' } 
     ];
   // 健身功能选择事件
   onFitnessFeatureSelect(feature:Feature) {
@@ -59,7 +87,7 @@ export class Tab1Page implements OnInit {
   openSettings() {
     console.log('Open settings');
   }
-  constructor(public modalController: ModalController,private router: Router) {}
+
   // 假设有一个搜索栏的模态组件
   async presentSearch() {
     const modal = await this.modalController.create({

+ 0 - 17
src/app/tab4/tab4.page.html

@@ -1,17 +0,0 @@
-<ion-header [translucent]="true">
-  <ion-toolbar>
-    <ion-title>
-      Tab 4
-    </ion-title>
-  </ion-toolbar>
-</ion-header>
-
-<ion-content [fullscreen]="true">
-  <ion-header collapse="condense">
-    <ion-toolbar>
-      <ion-title size="large">Tab 4</ion-title>
-    </ion-toolbar>
-  </ion-header>
-
-  <app-explore-container name="Tab 4 page"></app-explore-container>
-</ion-content>

+ 6 - 2
src/app/tabs/tabs-routing.module.ts

@@ -23,10 +23,14 @@ const routes: Routes = [
         path: 'tab4',
         loadChildren: () => import('../tab4/tab4.module').then(m => m.Tab4PageModule)
       },
-      {
+      /*  {
         path: 'tab5',
         loadChildren: () => import('../tab5/tab5.module').then(m => m.Tab5PageModule)
-      }, 
+      },  */
+      {
+        path: 'tab5',
+        loadChildren: () => import('../../modules/user/mine/mine.module').then(mod => mod.MinePageModule)
+      },
       {
         path: '',
         redirectTo: '/tabs/tab1',

+ 17 - 0
src/modules/user/auth.guard.spec.ts

@@ -0,0 +1,17 @@
+import { TestBed } from '@angular/core/testing';
+import { CanActivateFn } from '@angular/router';
+
+import { authGuard } from './auth.guard';
+
+describe('authGuard', () => {
+  const executeGuard: CanActivateFn = (...guardParameters) => 
+      TestBed.runInInjectionContext(() => authGuard(...guardParameters));
+
+  beforeEach(() => {
+    TestBed.configureTestingModule({});
+  });
+
+  it('should be created', () => {
+    expect(executeGuard).toBeTruthy();
+  });
+});

+ 5 - 0
src/modules/user/auth.guard.ts

@@ -0,0 +1,5 @@
+import { CanActivateFn } from '@angular/router';
+
+export const authGuard: CanActivateFn = (route, state) => {
+  return true;
+};

+ 17 - 0
src/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
src/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
src/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
src/modules/user/edit-info/edit-info.page.scss


+ 17 - 0
src/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
src/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
src/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
src/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
src/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
src/modules/user/login/login.page.scss


+ 17 - 0
src/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();
+  });
+});

+ 98 - 0
src/modules/user/login/login.page.ts

@@ -0,0 +1,98 @@
+import { Component, OnInit } from '@angular/core';
+import { AlertController } from '@ionic/angular';
+import * as Parse from "parse"
+// 引用Router服务
+import { Router } from '@angular/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 router:Router,
+    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.router.navigateByUrl("/tabs/tab3")
+    }
+  }
+  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.router.navigateByUrl("/tabs/tab3")
+        }
+        // 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
+   * 若存在上级回调页面地址LOGIN_REDIRECT_URL,直接跳转
+   * 若不存在,则默认跳转至系统首页
+   */
+  back(){
+    let path = "/"
+    path = localStorage.getItem("LOGIN_REDIRECT_URL") || path;
+    this.router.navigateByUrl(path)
+  }
+}

+ 17 - 0
src/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
src/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 {}

+ 27 - 0
src/modules/user/mine/mine.page.html

@@ -0,0 +1,27 @@
+<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-header>
+ 
+    <!-- 新增:根据用户状态,显示登录/登出按钮,执行跳转或登出函数 -->
+    <!-- <ion-button *ngIf="!user?.id" fill="clear" routerLink="/user/login">登录</ion-button>
+    <ion-button *ngIf="user?.id" fill="clear" (click)="logout()">登出</ion-button> -->
+  </ion-card>
+
+
+</ion-content>

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


+ 17 - 0
src/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();
+  });
+});

+ 25 - 0
src/modules/user/mine/mine.page.ts

@@ -0,0 +1,25 @@
+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 {
+
+  // 由于Parse.User.current()是随着localStorage变化的属性
+  // 为了避免首次复制后用户状态变化,页面不同步,通过get方法实现实时获取
+  get user():Parse.User|undefined{
+    return Parse.User.current()
+  }
+  constructor() {
+  }
+
+  ngOnInit() {
+  }
+  logout(){
+    Parse.User.logOut();
+  }
+
+}

+ 14 - 0
src/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
src/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 { }