Prechádzať zdrojové kódy

feat: new page-mine & user-edit

未来全栈 11 hodín pred
rodič
commit
fe8bd2f9ea

+ 6 - 3
src/app/tabs/tabs.page.html

@@ -16,8 +16,11 @@
       <ion-label>知识库</ion-label>
     </ion-tab-button>
 
-   
+    <ion-tab-button tab="mine" href="/tabs/mine">
+      <ion-icon aria-hidden="true" name="person-outline"></ion-icon>
+      <ion-label>我的</ion-label>
+    </ion-tab-button>
+
 
-   
   </ion-tab-bar>
-</ion-tabs>
+</ion-tabs>

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

@@ -27,6 +27,11 @@ export const routes: Routes = [
         loadComponent: () =>
           import('../tab3/tab3.page').then((m) => m.Tab3Page),
       },
+      {
+        path: 'mine',
+        loadComponent: () =>
+          import('../../lib/user/page-mine/page-mine.component').then((m) => m.PageMineComponent),
+      },
       {
         path: '',
         redirectTo: '/tabs/tab3',

+ 4 - 0
src/lib/user/README.md

@@ -0,0 +1,4 @@
+# 用户逻辑讲解
+
+# 用户登录逻辑
+- ncloud.ts > CloudUser

+ 60 - 23
src/lib/user/modal-user-edit/modal-user-edit.component.html

@@ -1,29 +1,66 @@
-<!-- 用户登录状态 -->
-<ion-card>
-  <ion-card-header>
-    <ion-card-title>
-      用户名:{{currentUser?.get("username")}}
-    </ion-card-title>
-    <ion-card-subtitle>请输入您的详细资料</ion-card-subtitle>
-   </ion-card-header>
- <ion-card-content>
+<ion-header>
+  <ion-toolbar color="primary">
+    <ion-title>编辑资料</ion-title>
+    <ion-buttons slot="end">
+      <ion-button (click)="cancel()">
+        <ion-icon name="close-outline" slot="icon-only"></ion-icon>
+      </ion-button>
+    </ion-buttons>
+  </ion-toolbar>
+</ion-header>
 
-   <ion-item>
-     <ion-input [value]="userData['realname']" (ionChange)="userDataChange('realname',$event)" label="姓名" placeholder="请您输入真实姓名"></ion-input>
-   </ion-item>
-   <ion-item>
-     <ion-input type="number" [value]="userData['age']" (ionChange)="userDataChange('age',$event)" label="年龄" placeholder="请您输入年龄"></ion-input>
+<ion-content class="ion-padding">
+  <ion-list>
+    <ion-item>
+      <ion-icon name="person-outline" slot="start"></ion-icon>
+      <ion-input [value]="userData['username']" (ionChange)="userDataChange('username',$event)" label="用户名"
+        label-placement="floating" placeholder="请输入用户名">
+      </ion-input>
     </ion-item>
-  <ion-item>
-     <ion-input [value]="userData['gender']" (ionChange)="userDataChange('gender',$event)" label="性别" placeholder="请您输入男/女"></ion-input>
+
+    <ion-item>
+      <ion-icon name="id-card-outline" slot="start"></ion-icon>
+      <ion-input [value]="userData['realname']" (ionChange)="userDataChange('realname',$event)" label="真实姓名"
+        label-placement="floating" placeholder="请输入真实姓名">
+      </ion-input>
+    </ion-item>
+
+    <ion-item>
+      <ion-icon name="calendar-outline" slot="start"></ion-icon>
+      <ion-input type="number" [value]="userData['age']" (ionChange)="userDataChange('age',$event)" label="年龄"
+        label-placement="floating" placeholder="请输入年龄">
+      </ion-input>
     </ion-item>
+
     <ion-item>
-      <ion-input [value]="userData['avatar']" (ionChange)="userDataChange('avatar',$event)" label="头像" placeholder="请您输入头像地址"></ion-input>
-     </ion-item>
+      <ion-icon name="transgender-outline" slot="start"></ion-icon>
+      <ion-label>性别</ion-label>
+      <ion-segment [value]="userData['gender']" (ionChange)="userDataChange('gender', $event)">
+        <ion-segment-button value="男">
+          <ion-label>男</ion-label>
+        </ion-segment-button>
+        <ion-segment-button value="女">
+          <ion-label>女</ion-label>
+        </ion-segment-button>
+      </ion-segment>
+    </ion-item>
 
-   <ion-button expand="block" (click)="save()">保存</ion-button>
-   <ion-button expand="block" (click)="cancel()">取消</ion-button>
- 
+    <ion-item>
+      <ion-icon name="image-outline" slot="start"></ion-icon>
+      <ion-input [value]="userData['avatar']" (ionChange)="userDataChange('avatar',$event)" label="头像URL"
+        label-placement="floating" placeholder="请输入头像URL地址">
+      </ion-input>
+    </ion-item>
+  </ion-list>
 
-</ion-card-content>
-</ion-card>
+  <div class="action-buttons">
+    <ion-button expand="block" (click)="save()" color="primary" shape="round">
+      <ion-icon name="save-outline" slot="start"></ion-icon>
+      保存
+    </ion-button>
+    <ion-button expand="block" (click)="cancel()" color="medium" fill="outline" shape="round">
+      <ion-icon name="close-outline" slot="start"></ion-icon>
+      取消
+    </ion-button>
+  </div>
+</ion-content>

+ 27 - 0
src/lib/user/modal-user-edit/modal-user-edit.component.scss

@@ -0,0 +1,27 @@
+/* modal-user-edit.component.scss */
+ion-content {
+    --padding-bottom: 80px;
+}
+
+.action-buttons {
+    margin-top: 32px;
+
+    ion-button {
+        margin-bottom: 16px;
+    }
+}
+
+ion-segment {
+    width: 100%;
+    max-width: 200px;
+    margin-left: auto;
+}
+
+ion-item {
+    --padding-start: 0;
+
+    ion-icon {
+        margin-right: 16px;
+        color: var(--ion-color-medium);
+    }
+}

+ 45 - 45
src/lib/user/modal-user-edit/modal-user-edit.component.ts

@@ -1,65 +1,65 @@
-import { Input, OnInit } from '@angular/core';
-import { Component } from '@angular/core';
-import { IonHeader, IonToolbar, IonTitle, IonContent, IonCard, IonCardContent, IonButton, IonCardHeader, IonCardTitle, IonCardSubtitle, ModalController, IonInput, IonItem, IonSegment, IonSegmentButton, IonLabel } from '@ionic/angular/standalone';
+import { Component, OnInit } from '@angular/core';
+import {
+  IonHeader, IonToolbar, IonTitle, IonContent,
+  IonButton, IonInput, IonItem, IonList, IonIcon,
+  IonSegment, IonSegmentButton, IonLabel, IonButtons
+} from '@ionic/angular/standalone';
+import { ModalController } from '@ionic/angular/standalone';
 import { CloudUser } from 'src/lib/ncloud';
+import { addIcons } from 'ionicons';
+import {
+  closeOutline, saveOutline, personOutline,
+  idCardOutline, calendarOutline, transgenderOutline,
+  imageOutline
+} from 'ionicons/icons';
 
 @Component({
   selector: 'app-modal-user-edit',
   templateUrl: './modal-user-edit.component.html',
   styleUrls: ['./modal-user-edit.component.scss'],
   standalone: true,
-  imports: [IonHeader, IonToolbar, IonTitle, IonContent, 
-    IonCard,IonCardContent,IonButton,IonCardHeader,IonCardTitle,IonCardSubtitle,
-    IonInput,IonItem,
-    IonSegment,IonSegmentButton,IonLabel
+  imports: [
+    IonHeader, IonToolbar, IonTitle, IonContent,
+    IonButton, IonInput, IonItem, IonList, IonIcon,
+    IonSegment, IonSegmentButton, IonLabel, IonButtons
   ],
 })
-export class ModalUserEditComponent  implements OnInit {
+export class ModalUserEditComponent implements OnInit {
+  currentUser: CloudUser | undefined;
+  userData: any = {};
 
-  currentUser:CloudUser|undefined
-  userData:any = {}
-  userDataChange(key:string,ev:any){
-    let value = ev?.detail?.value
-    if(value){
-      this.userData[key] = value
-    }
-  }
-  constructor(private modalCtrl:ModalController) { 
+  constructor(private modalCtrl: ModalController) {
     this.currentUser = new CloudUser();
-    this.userData = this.currentUser.data;
+    this.userData = { ...this.currentUser.data };
+    addIcons({
+      closeOutline, saveOutline, personOutline,
+      idCardOutline, calendarOutline, transgenderOutline,
+      imageOutline
+    });
   }
 
-  ngOnInit() {}
-
-  async save(){
-    Object.keys(this.userData).forEach(key=>{
-      if(key=="age"){
-        this.userData[key] = Number(this.userData[key])
-      }
-    })
-
-    this.currentUser?.set(this.userData)
-    await this.currentUser?.save()
-    this.modalCtrl.dismiss(this.currentUser,"confirm")
+  userDataChange(key: string, ev: any) {
+    let value = ev?.detail?.value;
+    if (value) {
+      this.userData[key] = value;
+    }
   }
-  cancel(){
-    this.modalCtrl.dismiss(null,"cancel")
 
-  }
-}
+  ngOnInit() { }
 
-export async function openUserEditModal(modalCtrl:ModalController):Promise<CloudUser|null>{
-  const modal = await modalCtrl.create({
-    component: ModalUserEditComponent,
-    breakpoints:[0.7,1.0],
-    initialBreakpoint:0.7
-  });
-  modal.present();
+  async save() {
+    Object.keys(this.userData).forEach(key => {
+      if (key == "age") {
+        this.userData[key] = Number(this.userData[key]);
+      }
+    });
 
-  const { data, role } = await modal.onWillDismiss();
+    this.currentUser?.set(this.userData);
+    await this.currentUser?.save();
+    this.modalCtrl.dismiss(this.currentUser, "confirm");
+  }
 
-  if (role === 'confirm') {
-    return data;
+  cancel() {
+    this.modalCtrl.dismiss(null, "cancel");
   }
-  return null
 }

+ 62 - 0
src/lib/user/page-mine/page-mine.component.html

@@ -0,0 +1,62 @@
+<ion-content [fullscreen]="true" class="ion-padding">
+  <ion-header [translucent]="true">
+    <ion-toolbar color="primary">
+      <ion-title>我的资料</ion-title>
+    </ion-toolbar>
+  </ion-header>
+
+  <div class="profile-section">
+    @if(currentUser?.id){
+    <!-- 已登录状态 -->
+    <ion-card>
+      <ion-card-header>
+        <ion-avatar class="profile-avatar">
+          <img [src]="currentUser?.get('avatar') || 'assets/icon/avatar-default.png'" alt="用户头像" />
+        </ion-avatar>
+        <ion-card-title class="ion-text-center">
+          {{currentUser?.get("username")}}
+        </ion-card-title>
+        <ion-card-subtitle class="ion-text-center">
+          {{currentUser?.get('realname') || '未设置姓名'}}
+        </ion-card-subtitle>
+      </ion-card-header>
+
+      <ion-card-content>
+        <ion-list lines="none">
+          <ion-item>
+            <ion-icon name="person-outline" slot="start"></ion-icon>
+            <ion-label>性别</ion-label>
+            <ion-note slot="end">{{currentUser?.get('gender') || '未设置'}}</ion-note>
+          </ion-item>
+          <ion-item>
+            <ion-icon name="calendar-outline" slot="start"></ion-icon>
+            <ion-label>年龄</ion-label>
+            <ion-note slot="end">{{currentUser?.get('age') || '未设置'}}</ion-note>
+          </ion-item>
+        </ion-list>
+
+        <ion-button expand="block" (click)="edit()" color="primary">
+          <ion-icon name="create-outline" slot="start"></ion-icon>
+          编辑资料
+        </ion-button>
+        <ion-button expand="block" (click)="logout()" color="danger" fill="outline">
+          <ion-icon name="log-out-outline" slot="start"></ion-icon>
+          登出
+        </ion-button>
+      </ion-card-content>
+    </ion-card>
+    }
+    @if(!currentUser?.id){
+    <!-- 未登录状态 -->
+    <div class="login-prompt">
+      <ion-icon name="person-circle-outline" class="login-icon"></ion-icon>
+      <h2>您还未登录</h2>
+      <p>登录后可以保存您的个人资料</p>
+      <ion-button expand="block" (click)="login()" color="primary">
+        <ion-icon name="log-in-outline" slot="start"></ion-icon>
+        登录用户 abc 1234
+      </ion-button>
+    </div>
+    }
+  </div>
+</ion-content>

+ 36 - 0
src/lib/user/page-mine/page-mine.component.scss

@@ -0,0 +1,36 @@
+/* page-mine.component.scss */
+.profile-section {
+    max-width: 600px;
+    margin: 0 auto;
+}
+
+.profile-avatar {
+    width: 100px;
+    height: 100px;
+    margin: 0 auto 16px;
+}
+
+.login-prompt {
+    text-align: center;
+    padding: 40px 20px;
+
+    .login-icon {
+        font-size: 80px;
+        color: var(--ion-color-medium);
+        margin-bottom: 20px;
+    }
+
+    h2 {
+        font-size: 1.5rem;
+        margin-bottom: 8px;
+    }
+
+    p {
+        color: var(--ion-color-medium);
+        margin-bottom: 30px;
+    }
+}
+
+ion-button {
+    margin-top: 16px;
+}

+ 22 - 0
src/lib/user/page-mine/page-mine.component.spec.ts

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

+ 61 - 0
src/lib/user/page-mine/page-mine.component.ts

@@ -0,0 +1,61 @@
+import { Component, OnInit } from '@angular/core';
+import { CloudUser } from 'src/lib/ncloud';
+import { ModalUserEditComponent } from '../modal-user-edit/modal-user-edit.component';
+import { ModalController } from "@ionic/angular/standalone";
+import {
+  IonContent, IonHeader, IonTitle, IonToolbar, IonCard,
+  IonCardHeader, IonCardTitle, IonCardSubtitle, IonCardContent,
+  IonButton, IonAvatar, IonList, IonItem, IonLabel, IonNote, IonIcon
+} from '@ionic/angular/standalone';
+import { addIcons } from 'ionicons';
+import {
+  personOutline, calendarOutline, createOutline,
+  logOutOutline, logInOutline, personCircleOutline
+} from 'ionicons/icons';
+
+@Component({
+  selector: 'app-page-mine',
+  templateUrl: './page-mine.component.html',
+  styleUrls: ['./page-mine.component.scss'],
+  standalone: true,
+  imports: [
+    IonContent, IonHeader, IonTitle, IonToolbar, IonCard,
+    IonCardHeader, IonCardTitle, IonCardSubtitle, IonCardContent,
+    IonButton, IonAvatar, IonList, IonItem, IonLabel, IonNote, IonIcon
+  ]
+})
+export class PageMineComponent implements OnInit {
+  currentUser: CloudUser | undefined;
+
+  constructor(private modalCtrl: ModalController) {
+    this.currentUser = new CloudUser();
+    addIcons({
+      personOutline, calendarOutline, createOutline,
+      logOutOutline, logInOutline, personCircleOutline
+    });
+  }
+
+  async edit() {
+    const modal = await this.modalCtrl.create({
+      component: ModalUserEditComponent,
+    });
+    modal.present();
+
+    const { data, role } = await modal.onWillDismiss();
+  }
+
+  async login() {
+    let user: any = new CloudUser();
+    user = await user?.login("abctest", "1234");
+    if (user?.id) {
+      this.currentUser = user;
+    }
+  }
+
+  logout() {
+    this.currentUser?.logout();
+    this.currentUser = undefined;
+  }
+
+  ngOnInit() { }
+}

+ 9 - 0
src/modules/demo/bird/README.md

@@ -1,4 +1,13 @@
 
+
+# 鸟类知识科普AI应用
+- 示例代码:http://git.fmode.cn:3000/fmode/agent-app/commit/be05c8a641cea514ac0af4d179320234c820d161
+- 操作视频:
+
+会议录制:脑控未来的快速会议
+日期:2025-05-23 15:56:02
+录制文件:https://meeting.tencent.com/wework/cloud-record/share?id=e166c306-530e-44ae-b6c3-88431350fa81&hide_more_btn=true&from=qywx
+
 ``` bash
 mkdir -p src/modules/demo/bird
 ng g component --standalone page-bird-list