Ver Fonte

feat:个人信息编辑,input组件

刘嘉轩 há 7 meses atrás
pai
commit
5c871418f7

+ 4 - 1
smarteat-app/angular.json

@@ -126,7 +126,10 @@
     }
   },
   "cli": {
-    "schematicCollections": ["@ionic/angular-toolkit"]
+    "schematicCollections": [
+      "@ionic/angular-toolkit"
+    ],
+    "analytics": "bc1a3040-43b7-4099-81cc-cfdc5c9af1e9"
   },
   "schematics": {
     "@ionic/angular-toolkit:component": {

+ 159 - 0
smarteat-app/src/app/page-edit/page-edit.component.html

@@ -0,0 +1,159 @@
+<ion-header>
+  <ion-toolbar>
+    <ion-buttons slot="start">
+      <ion-button color="medium" (click)="cancel()">取消</ion-button>
+    </ion-buttons>
+    <ion-title>编辑个人信息</ion-title>
+    <ion-buttons slot="end">
+      <ion-button (click)="confirm()" [strong]="true">确认</ion-button>
+    </ion-buttons>
+  </ion-toolbar>
+</ion-header>
+
+<ion-content class="ion-padding">
+  <!-- 头像上传区域 -->
+  <ion-item>
+    <ion-label>头像 <span style="color: red;">*</span></ion-label>
+    <ion-avatar slot="start">
+      <!-- 显示头像,如果没有头像,显示默认头像 -->
+      <img [src]="avatar || 'assets/default-avatar.png'" />
+    </ion-avatar>
+    <ion-button fill="outline" slot="end" (click)="uploadAvatar()">上传头像</ion-button>
+  </ion-item>
+
+  <!-- 用户名输入框 -->
+  <ion-item>
+    <ion-label>姓名 <span style="color: red;">*</span></ion-label>
+    <ion-input 
+      labelPlacement="stacked" 
+      [(ngModel)]="name" 
+      placeholder="请输入姓名" 
+      required>
+    </ion-input>
+  </ion-item>
+
+  <!-- 邮箱输入框 -->
+  <ion-item>
+    <ion-label>邮箱 <span style="color: red;">*</span></ion-label>
+    <ion-input 
+      labelPlacement="stacked" 
+      [(ngModel)]="email" 
+      placeholder="请输入邮箱" 
+      type="email" 
+      required
+      [pattern]="emailPattern">
+    </ion-input>
+  </ion-item>
+
+  <!-- 电话输入框 -->
+  <ion-item>
+    <ion-label>电话 <span style="color: red;">*</span></ion-label>
+    <ion-input 
+      labelPlacement="stacked" 
+      [(ngModel)]="phone" 
+      placeholder="请输入电话号码" 
+      type="tel" 
+      required
+      [pattern]="phonePattern">
+    </ion-input>
+  </ion-item>
+
+  <!-- 地址输入框 -->
+  <ion-item>
+    <ion-label>地址</ion-label>
+    <ion-input 
+      labelPlacement="stacked" 
+      [(ngModel)]="address" 
+      placeholder="请输入地址">
+    </ion-input>
+  </ion-item>
+
+  <!-- 年龄输入框 -->
+  <ion-item>
+    <ion-label>年龄 <span style="color: red;">*</span></ion-label>
+    <ion-input 
+      labelPlacement="stacked" 
+      [(ngModel)]="age" 
+      placeholder="请输入年龄" 
+      type="number" 
+      required 
+      [min]="0" 
+      [max]="150">
+    </ion-input>
+  </ion-item>
+
+  <!-- 性别选择框 -->
+  <ion-item>
+    <ion-label>性别 <span style="color: red;">*</span></ion-label>
+    <ion-select 
+      [(ngModel)]="gender" 
+      interface="popover" 
+      required>
+      <ion-select-option value="male">男</ion-select-option>
+      <ion-select-option value="female">女</ion-select-option>
+      <ion-select-option value="other">其他</ion-select-option>
+    </ion-select>
+  </ion-item>
+
+  <!-- 身高输入框 -->
+  <ion-item>
+    <ion-label>身高(cm) <span style="color: red;">*</span></ion-label>
+    <ion-input 
+      labelPlacement="stacked" 
+      [(ngModel)]="height" 
+      placeholder="请输入身高" 
+      type="number" 
+      required
+      [min]="0" 
+      [pattern]="heightPattern">
+    </ion-input>
+  </ion-item>
+
+  <!-- 体重输入框 -->
+  <ion-item>
+    <ion-label>体重(kg) <span style="color: red;">*</span></ion-label>
+    <ion-input 
+      labelPlacement="stacked" 
+      [(ngModel)]="weight" 
+      placeholder="请输入体重" 
+      type="number" 
+      required
+      [min]="0" 
+      [pattern]="weightPattern">
+    </ion-input>
+  </ion-item>
+
+  <!-- 活动水平选择框 -->
+  <ion-item>
+    <ion-label>活动水平</ion-label>
+    <ion-select [(ngModel)]="activityLevel" interface="popover">
+      <ion-select-option value="low">低</ion-select-option>
+      <ion-select-option value="moderate">中</ion-select-option>
+      <ion-select-option value="high">高</ion-select-option>
+    </ion-select>
+  </ion-item>
+
+  <!-- 饮食偏好选择框 -->
+  <ion-item>
+    <ion-label>饮食偏好 <span style="color: red;">*</span></ion-label>
+    <ion-select 
+      [(ngModel)]="dietPreference" 
+      interface="popover" 
+      required>
+      <ion-select-option value="vegetarian">素食</ion-select-option>
+      <ion-select-option value="nonVegetarian">非素食</ion-select-option>
+      <ion-select-option value="vegan">纯素</ion-select-option>
+      <ion-select-option value="glutenFree">无麸质</ion-select-option>
+    </ion-select>
+  </ion-item>
+
+  <!-- 饮食群体输入框 -->
+  <ion-item>
+    <ion-label>饮食群体 <span style="color: red;">*</span></ion-label>
+    <ion-input 
+      labelPlacement="stacked" 
+      [(ngModel)]="dietGroup" 
+      placeholder="请输入饮食群体">
+    </ion-input>
+  </ion-item>
+</ion-content>

+ 124 - 0
smarteat-app/src/app/page-edit/page-edit.component.scss

@@ -0,0 +1,124 @@
+/* 设置整体页面的 padding */
+ion-content {
+  --padding-start: 16px;
+  --padding-end: 16px;
+  --padding-top: 16px;
+  --padding-bottom: 16px;
+}
+
+/* 每个表单项之间的间距 */
+ion-item {
+  --inner-padding-start: 12px;
+  --inner-padding-end: 12px;
+  margin-bottom: 16px;
+}
+
+/* 为每个必填项添加红色星号,方便用户识别 */
+ion-label span {
+  color: red;
+  font-weight: bold;
+  margin-left: 5px;
+}
+
+/* 设置头像上传区域的样式 */
+ion-avatar {
+  margin-right: 16px;
+  border-radius: 50%;
+  overflow: hidden;
+}
+
+ion-item ion-avatar {
+  margin-top: 10px;
+}
+
+/* 上传按钮的样式 */
+ion-button[fill="outline"] {
+  font-size: 14px;
+  padding: 8px 16px;
+  margin-top: 8px;
+}
+
+/* 输入框的样式 */
+ion-input, ion-select {
+  font-size: 14px;
+  padding: 10px 16px;
+  border-radius: 8px;
+  background-color: #f5f5f5;
+}
+
+/* 在输入框聚焦时设置边框颜色 */
+ion-input:focus, ion-select:focus {
+  --border-color: #3880ff;
+}
+
+/* 标题栏的样式 */
+ion-toolbar {
+  --background: #f4f4f4;
+  --color: #333;
+  padding: 10px 20px;
+}
+
+/* 调整按钮的外观 */
+ion-buttons ion-button {
+  font-size: 16px;
+  padding: 10px 16px;
+}
+
+/* 设置页面内容顶部和底部的间距 */
+ion-content {
+  --padding-start: 16px;
+  --padding-end: 16px;
+  --padding-top: 16px;
+  --padding-bottom: 16px;
+}
+
+/* 活动水平选择框的样式 */
+ion-select {
+  font-size: 14px;
+  padding: 10px 16px;
+}
+
+/* 优化输入框和选择框的整体样式 */
+ion-select, ion-input {
+  font-size: 16px;
+  border-radius: 8px;
+  background-color: #f8f8f8;
+  margin-top: 8px;
+}
+
+ion-item {
+  --inner-padding-start: 12px;
+  --inner-padding-end: 12px;
+}
+
+ion-label {
+  font-size: 16px;
+  font-weight: 600;
+}
+
+/* 设置表单项间的间隔 */
+ion-item {
+  margin-bottom: 20px;
+}
+
+/* 页面底部按钮的样式 */
+ion-toolbar ion-buttons {
+  padding: 8px 0;
+}
+
+ion-button[strong] {
+  background-color: #3880ff;
+  color: white;
+  font-weight: bold;
+  padding: 10px 20px;
+  border-radius: 5px;
+}
+
+ion-button[strong]:hover {
+  background-color: #007bff;
+}
+
+ion-button[color="medium"] {
+  font-weight: normal;
+  color: #666;
+}

+ 22 - 0
smarteat-app/src/app/page-edit/page-edit.component.spec.ts

@@ -0,0 +1,22 @@
+import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
+
+import { PageEditComponent } from './page-edit.component';
+
+describe('PageEditComponent', () => {
+  let component: PageEditComponent;
+  let fixture: ComponentFixture<PageEditComponent>;
+
+  beforeEach(waitForAsync(() => {
+    TestBed.configureTestingModule({
+      imports: [PageEditComponent],
+    }).compileComponents();
+
+    fixture = TestBed.createComponent(PageEditComponent);
+    component = fixture.componentInstance;
+    fixture.detectChanges();
+  }));
+
+  it('should create', () => {
+    expect(component).toBeTruthy();
+  });
+});

+ 126 - 0
smarteat-app/src/app/page-edit/page-edit.component.ts

@@ -0,0 +1,126 @@
+import { Component, OnInit } from '@angular/core';
+import { ModalController } from '@ionic/angular';
+import { IonicModule } from '@ionic/angular';
+import { FormsModule } from '@angular/forms';
+
+@Component({
+  selector: 'app-page-edit',
+  templateUrl: './page-edit.component.html',
+  styleUrls: ['./page-edit.component.scss'],
+  imports: [IonicModule, FormsModule],
+  standalone: true,
+})
+export class PageEditComponent implements OnInit {
+
+  // 基本信息字段
+  name: string = '';
+  email: string = '';
+  phone: string = '';
+  address: string = '';
+
+  // 饮食规划相关字段
+  age: number | null = null;
+  gender: string = '';
+  height: number | null = null;
+  weight: number | null = null;
+  activityLevel: string = '';
+  dietPreference: string = '';
+
+  // 新增饮食群体字段
+  dietGroup: string = '';  // 新添加的字段
+
+  // 头像字段
+  avatar: string | null = null;
+
+  // 正则表达式
+  emailPattern = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,4}$/;
+  phonePattern = /^[0-9]{10,15}$/; // 允许10到15位数字
+  heightPattern = /^[1-9][0-9]{1,2}$/; // 身高通常是两位或三位数字
+  weightPattern = /^[1-9][0-9]{1,2}$/; // 体重大致是两位或三位数字
+
+  constructor(private modalCtrl: ModalController) {}
+
+  ngOnInit(): void {
+    // 可以在这里进行初始化,或者加载初始值
+  }
+
+  // 取消操作,关闭模态框
+  cancel() {
+    return this.modalCtrl.dismiss(null, 'cancel');
+  }
+
+  // 确认操作,关闭模态框并返回数据
+  confirm() {
+    // 验证表单输入
+    if (!this.name || !this.email || !this.phone || !this.age || !this.gender || !this.height || !this.weight || !this.dietPreference || !this.dietGroup) {
+      alert("请确保所有必填项已填写!");
+      return;
+    }
+
+    // 验证邮箱格式
+    if (!this.emailPattern.test(this.email)) {
+      alert("请输入有效的邮箱地址!");
+      return;
+    }
+
+    // 验证电话格式
+    if (!this.phonePattern.test(this.phone)) {
+      alert("请输入有效的电话号码!");
+      return;
+    }
+
+    // 验证身高
+    if (!this.heightPattern.test(String(this.height))) {
+      alert("请输入有效的身高!");
+      return;
+    }
+
+    // 验证体重
+    if (!this.weightPattern.test(String(this.weight))) {
+      alert("请输入有效的体重!");
+      return;
+    }
+
+    // 打包所有的用户输入数据
+    const userData = {
+      name: this.name,
+      email: this.email,
+      phone: this.phone,
+      address: this.address,
+      age: this.age,
+      gender: this.gender,
+      height: this.height,
+      weight: this.weight,
+      activityLevel: this.activityLevel,
+      dietPreference: this.dietPreference,
+      dietGroup: this.dietGroup,  // 包含饮食群体
+      avatar: this.avatar
+    };
+
+    // 通过 modalCtrl 传递数据
+    return this.modalCtrl.dismiss(userData, 'confirm');
+  }
+
+  // 头像上传逻辑
+  uploadAvatar() {
+    const input = document.createElement('input');
+    input.type = 'file';
+    input.accept = 'image/*';
+
+    // 监听文件选择
+    input.onchange = (event: any) => {
+      const file = event.target.files[0];
+      if (file) {
+        const reader = new FileReader();
+        reader.onload = (e: any) => {
+          // 设置头像
+          this.avatar = e.target.result;
+        };
+        reader.readAsDataURL(file);
+      }
+    };
+
+    // 触发文件选择
+    input.click();
+  }
+}

+ 63 - 11
smarteat-app/src/app/tab3/tab3.page.html

@@ -1,17 +1,69 @@
-<ion-header [translucent]="true">
+<ion-header>
   <ion-toolbar>
-    <ion-title>
-      Tab 3
-    </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 3</ion-title>
-    </ion-toolbar>
-  </ion-header>
+<ion-content>
+  <!-- 用户信息 -->
+  <ion-item lines="none" class="user-info">
+    <ion-avatar slot="start">
+      <!-- 显示头像,如果没有头像,显示默认头像 -->
+      <img [src]="userData?.avatar || 'assets/img/user-avatar.jpg'" alt="User Avatar" />
+    </ion-avatar>
+    <ion-label>
+      <h2>{{ userData?.name || '游客' }}</h2>
+    </ion-label>
+    <ion-button (click)="goToEdit()" fill="clear" slot="end" color="primary">
+      编辑
+    </ion-button>
+  </ion-item>
 
-  <app-explore-container name="Tab 3 page"></app-explore-container>
+  <!-- 我的饮食计划 -->
+  <ion-card>
+    <ion-card-header>
+      <ion-card-title>我的饮食计划</ion-card-title>
+    </ion-card-header>
+    <ion-card-content>
+      <ion-item>
+        <ion-label>今天的计划</ion-label>
+        <ion-button expand="block" color="secondary">
+          查看详情
+        </ion-button>
+      </ion-item>
+    </ion-card-content>
+  </ion-card>
+
+  <!-- 我的历史记录 -->
+  <ion-card>
+    <ion-card-header>
+      <ion-card-title>历史记录</ion-card-title>
+    </ion-card-header>
+    <ion-card-content>
+      <ion-item>
+        <ion-label>最近三天记录</ion-label>
+        <ion-button expand="block" color="tertiary">
+          查看详情
+        </ion-button>
+      </ion-item>
+    </ion-card-content>
+  </ion-card>
+
+  <ion-item button (click)="goToFavorites()">
+    <ion-icon slot="start" name="heart-outline"></ion-icon>
+    <ion-label>收藏</ion-label>
+  </ion-item>
+
+  <!-- 帮助与反馈按钮 -->
+  <ion-item button (click)="goToHelp()">
+    <ion-icon slot="start" name="help-circle-outline"></ion-icon>
+    <ion-label>帮助与反馈</ion-label>
+  </ion-item>
+
+  <!-- 设置按钮 -->
+  <ion-item button (click)="goToSettings()">
+    <ion-icon slot="start" name="settings-outline"></ion-icon>
+    <ion-label>设置</ion-label>
+  </ion-item>
 </ion-content>
+

+ 73 - 0
smarteat-app/src/app/tab3/tab3.page.scss

@@ -0,0 +1,73 @@
+/* 页面背景和基础样式 */
+ion-content {
+  --background: #f4f5f8;
+}
+
+/* 用户信息区 */
+.user-info {
+  display: flex;
+  align-items: center;
+  padding: 10px;
+}
+
+.user-info ion-avatar {
+  margin-right: 16px;
+}
+
+.user-info h2 {
+  margin: 0;
+  font-size: 20px;
+  font-weight: bold;
+}
+
+.user-info p {
+  margin: 4px 0;
+  color: #666;
+}
+
+ion-card {
+  margin: 16px 0;
+  --background: #fff;
+  --box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
+}
+
+ion-card-header {
+  background-color: #f8f8f8;
+  padding: 10px;
+}
+
+ion-card-title {
+  font-size: 18px;
+  font-weight: 600;
+  color: #333;
+}
+
+ion-item {
+  margin: 8px 0;
+}
+
+ion-label {
+  font-size: 14px;
+  color: #555;
+}
+
+ion-button {
+  margin-top: 10px;
+}
+
+/* 设置、收藏、帮助与反馈按钮样式 */
+ion-item[button] {
+  --background: #fff;
+  --border-color: #ddd;
+  --border-width: 1px;
+  --border-radius: 10px;
+  padding: 10px;
+}
+
+ion-item[button] ion-icon {
+  color: #333;
+}
+
+ion-item[button] ion-label {
+  font-weight: 500;
+}

+ 96 - 8
smarteat-app/src/app/tab3/tab3.page.ts

@@ -1,14 +1,102 @@
-import { Component } from '@angular/core';
-import { IonHeader, IonToolbar, IonTitle, IonContent } from '@ionic/angular/standalone';
-import { ExploreContainerComponent } from '../explore-container/explore-container.component';
+// import { Component } from '@angular/core';
+// import { IonicModule, NavController } from '@ionic/angular';
+// import { ModalController } from '@ionic/angular/standalone';
+// import { PageEditComponent } from '../page-edit/page-edit.component';
+
+// @Component({
+//   selector: 'app-tab3',
+//   templateUrl: './tab3.page.html',
+//   styleUrls: ['./tab3.page.scss'],
+//   standalone: true,
+//   imports: [IonicModule ],
+// })
+// export class Tab3Page {
+//   constructor(private navCtrl: NavController,
+//     private modalCtrl: ModalController) {}
+
+//   async goToEdit() {
+//     // this.navCtrl.navigateForward('/tabs/edit');
+//     const modal = await this.modalCtrl.create({
+//       component: PageEditComponent,
+//     });
+//     modal.present();
+
+//     const { data, role } = await modal.onWillDismiss();
+
+//     if (role === 'confirm') {
+//       // this.message = `Hello, ${data}!`;
+//       return data;
+//     }
+//   }
+  
+//   goToFavorites() {
+//     this.navCtrl.navigateForward('/favorites');
+//   }
+
+//   goToHelp() {
+//     this.navCtrl.navigateForward('/help');
+//   }
+
+//   goToSettings() {
+//     this.navCtrl.navigateForward('/settings');
+//   }
+// }
+
+
+import { Component, OnInit } from '@angular/core';
+import { IonicModule, NavController } from '@ionic/angular';
+import { ModalController } from '@ionic/angular';
+import { PageEditComponent } from '../page-edit/page-edit.component';
 
 @Component({
   selector: 'app-tab3',
-  templateUrl: 'tab3.page.html',
-  styleUrls: ['tab3.page.scss'],
+  templateUrl: './tab3.page.html',
+  styleUrls: ['./tab3.page.scss'],
   standalone: true,
-  imports: [IonHeader, IonToolbar, IonTitle, IonContent, ExploreContainerComponent],
+  imports: [IonicModule],
 })
-export class Tab3Page {
-  constructor() {}
+export class Tab3Page implements OnInit {
+
+  // 用户信息数据
+  userData = {
+    name: '游客',  // 默认值,可以为空测试默认显示“游客”
+    avatar: 'assets/img/user-avatar.jpg'  // 默认头像路径
+  };
+
+  constructor(private navCtrl: NavController, private modalCtrl: ModalController) {}
+
+  ngOnInit() {
+    // 在这里可以加载用户信息(如果从API或服务中获取)
+  }
+
+  async goToEdit() {
+    // 打开编辑模态框
+    const modal = await this.modalCtrl.create({
+      component: PageEditComponent,
+      componentProps: {
+        userData: this.userData  // 将当前用户数据传递给编辑页面
+      }
+    });
+    modal.present();
+
+    // 等待编辑页面关闭并获取数据
+    const { data, role } = await modal.onWillDismiss();
+
+    if (role === 'confirm') {
+      // 更新用户信息
+      this.userData = data;
+    }
+  }
+
+  goToFavorites() {
+    this.navCtrl.navigateForward('/favorites');
+  }
+
+  goToHelp() {
+    this.navCtrl.navigateForward('/help');
+  }
+
+  goToSettings() {
+    this.navCtrl.navigateForward('/settings');
+  }
 }

+ 5 - 0
smarteat-app/src/app/tabs/tabs.routes.ts

@@ -21,6 +21,11 @@ export const routes: Routes = [
         loadComponent: () =>
           import('../tab3/tab3.page').then((m) => m.Tab3Page),
       },
+      {
+        path: 'edit',
+        loadComponent: () =>
+          import('../page-edit/page-edit.component').then((m) => m.PageEditComponent),
+      },
       {
         path: '',
         redirectTo: '/tabs/tab1',