Browse Source

Merge branch 'master' of http://git.fmode.cn:3000/lzy/S202226701025

lzy 1 năm trước cách đây
mục cha
commit
0036b76756
35 tập tin đã thay đổi với 1273 bổ sung435 xóa
  1. 28 8
      FilmDraw-app/src/app/app.routes.ts
  2. 49 0
      FilmDraw-app/src/app/homepage/help/help.page.html
  3. 54 0
      FilmDraw-app/src/app/homepage/help/help.page.scss
  4. 17 0
      FilmDraw-app/src/app/homepage/help/help.page.spec.ts
  5. 30 0
      FilmDraw-app/src/app/homepage/help/help.page.ts
  6. 13 0
      FilmDraw-app/src/app/homepage/history/history.page.html
  7. 0 0
      FilmDraw-app/src/app/homepage/history/history.page.scss
  8. 17 0
      FilmDraw-app/src/app/homepage/history/history.page.spec.ts
  9. 20 0
      FilmDraw-app/src/app/homepage/history/history.page.ts
  10. 13 0
      FilmDraw-app/src/app/homepage/inform/inform.page.html
  11. 0 0
      FilmDraw-app/src/app/homepage/inform/inform.page.scss
  12. 17 0
      FilmDraw-app/src/app/homepage/inform/inform.page.spec.ts
  13. 20 0
      FilmDraw-app/src/app/homepage/inform/inform.page.ts
  14. 13 0
      FilmDraw-app/src/app/homepage/like/like.page.html
  15. 0 0
      FilmDraw-app/src/app/homepage/like/like.page.scss
  16. 17 0
      FilmDraw-app/src/app/homepage/like/like.page.spec.ts
  17. 20 0
      FilmDraw-app/src/app/homepage/like/like.page.ts
  18. 13 0
      FilmDraw-app/src/app/homepage/privacy/privacy.page.html
  19. 0 0
      FilmDraw-app/src/app/homepage/privacy/privacy.page.scss
  20. 17 0
      FilmDraw-app/src/app/homepage/privacy/privacy.page.spec.ts
  21. 20 0
      FilmDraw-app/src/app/homepage/privacy/privacy.page.ts
  22. 60 78
      FilmDraw-app/src/app/tab1/tab1.page.html
  23. 110 63
      FilmDraw-app/src/app/tab1/tab1.page.scss
  24. 1 1
      FilmDraw-app/src/app/tab3/tab3.page.ts
  25. 86 31
      FilmDraw-app/src/app/tab4/tab4.page.html
  26. 145 133
      FilmDraw-app/src/app/tab4/tab4.page.scss
  27. 31 4
      FilmDraw-app/src/app/tab4/tab4.page.ts
  28. 131 90
      FilmDraw-app/src/lib/ncloud.ts
  29. 32 0
      FilmDraw-app/src/lib/user/modal-user-edit/modal-user-edit.component.html
  30. 0 0
      FilmDraw-app/src/lib/user/modal-user-edit/modal-user-edit.component.scss
  31. 22 0
      FilmDraw-app/src/lib/user/modal-user-edit/modal-user-edit.component.spec.ts
  32. 65 0
      FilmDraw-app/src/lib/user/modal-user-edit/modal-user-edit.component.ts
  33. 45 4
      FilmDraw-server/lib/ncloud.js
  34. 37 1
      FilmDraw-server/migration/data.js
  35. 130 22
      FilmDraw-server/migration/import-data.js

+ 28 - 8
FilmDraw-app/src/app/app.routes.ts

@@ -1,12 +1,32 @@
-import { Routes } from '@angular/router';
-
-export const routes: Routes = [
+import { Routes } from '@angular/router';
+
+export const routes: Routes = [
+  {
+    path: '',
+    loadChildren: () => import('./tabs/tabs.routes').then((m) => m.routes),
+  },
+  {
+    path: 'interact',
+    loadComponent: () => import('./interact/interact.page').then( m => m.InteractPage)
+  },
  {
+    path: 'help',
+    loadComponent: () => import('./homepage/help/help.page').then( m => m.HelpPage)
+  },
+  {
+    path: 'like',
+    loadComponent: () => import('./homepage/like/like.page').then( m => m.LikePage)
+  },
   {
-    path: '',
-    loadChildren: () => import('./tabs/tabs.routes').then((m) => m.routes),
+    path: 'inform',
+    loadComponent: () => import('./homepage/inform/inform.page').then( m => m.InformPage)
   },
   {
-    path: 'interact',
-    loadComponent: () => import('./interact/interact.page').then( m => m.InteractPage)
+    path: 'privacy',
+    loadComponent: () => import('./homepage/privacy/privacy.page').then( m => m.PrivacyPage)
   },
-];
+  {
+    path: 'history',
+    loadComponent: () => import('./homepage/history/history.page').then( m => m.HistoryPage)
+  },
+
+];

+ 49 - 0
FilmDraw-app/src/app/homepage/help/help.page.html

@@ -0,0 +1,49 @@
+<ion-header>
+  <ion-toolbar>
+    <ion-title>帮助</ion-title>
+    <ion-buttons slot="end">
+      <ion-button (click)="closeHelp()">
+        <ion-icon name="close"></ion-icon>
+      </ion-button>
+    </ion-buttons>
+  </ion-toolbar>
+</ion-header>
+
+<ion-content class="help-content">
+  <h2>欢迎来到帮助页面</h2>
+  <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;在这里你可以找到使用本应用程序的指南和常见问题解答。</p>
+
+  <h3>常见问题</h3>
+  <ion-list>
+    <ion-item>
+      <ion-label>
+        <h2>如何使用此应用程序?</h2>
+        <p>请查看我们的使用指南,了解如何开始使用。您可以在主菜单中找到所有功能。</p>
+      </ion-label>
+    </ion-item>
+    <ion-item>
+      <ion-label>
+        <h2>如何报告问题?</h2>
+        <p>您可以通过应用内的反馈功能报告问题,或通过我们的支持邮箱与我们联系。</p>
+      </ion-label>
+    </ion-item>
+    <ion-item>
+      <ion-label>
+        <h2>如何重置密码?</h2>
+        <p>在登录页面点击“忘记密码”,然后按照提示进行操作以重置您的密码。</p>
+      </ion-label>
+    </ion-item>
+    <ion-item>
+      <ion-label>
+        <h2>如何更新个人信息?</h2>
+        <p>您可以在个人资料页面更新您的信息,包括姓名、邮箱和头像。</p>
+      </ion-label>
+    </ion-item>
+    <ion-item>
+      <ion-label>
+        <h2>应用程序是否支持多语言?</h2>
+        <p>是的,您可以在设置中选择您希望使用的语言。</p>
+      </ion-label>
+    </ion-item>
+  </ion-list>
+</ion-content>

+ 54 - 0
FilmDraw-app/src/app/homepage/help/help.page.scss

@@ -0,0 +1,54 @@
+.help-content {
+    background-color: #f9f9f9; // 背景颜色
+    color: #333; // 字体颜色
+    border-radius: 8px;
+    background-color: #fff; // 列表背景
+    box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1); // 阴影效果
+      }
+  
+    h2 {
+      font-size: 24px;
+      margin-bottom: 16px;
+      color: #036a99 // 标题颜色
+    }
+    
+    p {
+        font-size: 14px;
+        color: #666; // 问题内容颜色
+        margin-top: 4px;
+      }
+  
+    h3 {
+      margin-top: 24px;
+      margin-bottom: 8px;
+      font-size: 20px;
+      color: #555; // 小标题颜色
+    }
+  
+    ion-list {
+      margin-top: 16px;
+      border-radius: 8px;
+      background-color: #fff; // 列表背景
+      box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1); // 阴影效果
+    }
+  
+    ion-item {
+      border-bottom: 1px solid #eee; // 列表项分隔线
+      padding: 12px 16px;
+  
+      &:last-child {
+        border-bottom: none; // 最后一个项去掉分隔线
+      }
+  
+      h2 {
+        font-size: 18px;
+        color: #036a99 // 问题标题颜色
+      }
+  
+      p {
+        font-size: 14px;
+        color: #666; // 问题内容颜色
+        margin-top: 4px;
+      }
+    }
+  

+ 17 - 0
FilmDraw-app/src/app/homepage/help/help.page.spec.ts

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

+ 30 - 0
FilmDraw-app/src/app/homepage/help/help.page.ts

@@ -0,0 +1,30 @@
+import { Component, OnInit } from '@angular/core';
+import { CommonModule } from '@angular/common';
+import { FormsModule } from '@angular/forms';
+import { IonItem,IonButton, IonButtons, IonContent, IonHeader, IonLabel, IonList, IonTitle, IonToolbar, NavController, IonIcon } from '@ionic/angular/standalone';
+import { close } from 'ionicons/icons';
+import { addIcons } from 'ionicons';
+
+@Component({
+  selector: 'app-help',
+  templateUrl: './help.page.html',
+  styleUrls: ['./help.page.scss'],
+  standalone: true,
+  imports: [IonContent, IonHeader, IonTitle,  CommonModule, FormsModule,
+    IonToolbar,IonLabel,IonButton,IonButtons,IonList,IonItem,IonIcon,
+  ]
+})
+export class HelpPage implements OnInit {
+
+  constructor(private navCtrl: NavController) {
+   addIcons( {close})
+  }
+
+  closeHelp() {
+    this.navCtrl.back();
+  }
+
+  ngOnInit() {
+  }
+
+}

+ 13 - 0
FilmDraw-app/src/app/homepage/history/history.page.html

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

+ 0 - 0
FilmDraw-app/src/app/homepage/history/history.page.scss


+ 17 - 0
FilmDraw-app/src/app/homepage/history/history.page.spec.ts

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

+ 20 - 0
FilmDraw-app/src/app/homepage/history/history.page.ts

@@ -0,0 +1,20 @@
+import { Component, OnInit } from '@angular/core';
+import { CommonModule } from '@angular/common';
+import { FormsModule } from '@angular/forms';
+import { IonContent, IonHeader, IonTitle, IonToolbar } from '@ionic/angular/standalone';
+
+@Component({
+  selector: 'app-history',
+  templateUrl: './history.page.html',
+  styleUrls: ['./history.page.scss'],
+  standalone: true,
+  imports: [IonContent, IonHeader, IonTitle, IonToolbar, CommonModule, FormsModule]
+})
+export class HistoryPage implements OnInit {
+
+  constructor() { }
+
+  ngOnInit() {
+  }
+
+}

+ 13 - 0
FilmDraw-app/src/app/homepage/inform/inform.page.html

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

+ 0 - 0
FilmDraw-app/src/app/homepage/inform/inform.page.scss


+ 17 - 0
FilmDraw-app/src/app/homepage/inform/inform.page.spec.ts

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

+ 20 - 0
FilmDraw-app/src/app/homepage/inform/inform.page.ts

@@ -0,0 +1,20 @@
+import { Component, OnInit } from '@angular/core';
+import { CommonModule } from '@angular/common';
+import { FormsModule } from '@angular/forms';
+import { IonContent, IonHeader, IonTitle, IonToolbar } from '@ionic/angular/standalone';
+
+@Component({
+  selector: 'app-inform',
+  templateUrl: './inform.page.html',
+  styleUrls: ['./inform.page.scss'],
+  standalone: true,
+  imports: [IonContent, IonHeader, IonTitle, IonToolbar, CommonModule, FormsModule]
+})
+export class InformPage implements OnInit {
+
+  constructor() { }
+
+  ngOnInit() {
+  }
+
+}

+ 13 - 0
FilmDraw-app/src/app/homepage/like/like.page.html

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

+ 0 - 0
FilmDraw-app/src/app/homepage/like/like.page.scss


+ 17 - 0
FilmDraw-app/src/app/homepage/like/like.page.spec.ts

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

+ 20 - 0
FilmDraw-app/src/app/homepage/like/like.page.ts

@@ -0,0 +1,20 @@
+import { Component, OnInit } from '@angular/core';
+import { CommonModule } from '@angular/common';
+import { FormsModule } from '@angular/forms';
+import { IonContent, IonHeader, IonTitle, IonToolbar } from '@ionic/angular/standalone';
+
+@Component({
+  selector: 'app-like',
+  templateUrl: './like.page.html',
+  styleUrls: ['./like.page.scss'],
+  standalone: true,
+  imports: [IonContent, IonHeader, IonTitle, IonToolbar, CommonModule, FormsModule]
+})
+export class LikePage implements OnInit {
+
+  constructor() { }
+
+  ngOnInit() {
+  }
+
+}

+ 13 - 0
FilmDraw-app/src/app/homepage/privacy/privacy.page.html

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

+ 0 - 0
FilmDraw-app/src/app/homepage/privacy/privacy.page.scss


+ 17 - 0
FilmDraw-app/src/app/homepage/privacy/privacy.page.spec.ts

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

+ 20 - 0
FilmDraw-app/src/app/homepage/privacy/privacy.page.ts

@@ -0,0 +1,20 @@
+import { Component, OnInit } from '@angular/core';
+import { CommonModule } from '@angular/common';
+import { FormsModule } from '@angular/forms';
+import { IonContent, IonHeader, IonTitle, IonToolbar } from '@ionic/angular/standalone';
+
+@Component({
+  selector: 'app-privacy',
+  templateUrl: './privacy.page.html',
+  styleUrls: ['./privacy.page.scss'],
+  standalone: true,
+  imports: [IonContent, IonHeader, IonTitle, IonToolbar, CommonModule, FormsModule]
+})
+export class PrivacyPage implements OnInit {
+
+  constructor() { }
+
+  ngOnInit() {
+  }
+
+}

+ 60 - 78
FilmDraw-app/src/app/tab1/tab1.page.html

@@ -10,86 +10,68 @@
     </ion-item>
   </ion-toolbar>
 </ion-header>
+ <ion-content [fullscreen]="true">
 
-<ion-content [fullscreen]="true">
-  <!-- 热门话题区 -->
-  <section>
-    <ion-card>
-      <ion-card-header>
-        <ion-card-title>热门话题</ion-card-title>
-      </ion-card-header>
-      <ion-card-content>
-        <ion-list>
-          <ion-item *ngFor="let topic of topics">
-            <ion-label>
-              <h2>{{ topic.title }}</h2>
-              <p>参与人数: {{ topic.participants }} | 热度: {{ topic.popularity }}</p>
-            </ion-label>
-          </ion-item>
-        </ion-list>
-      </ion-card-content>
-    </ion-card>
-  </section>
+<!-- 包含热门话题区和讨论区的父容器 -->
 
-  <!-- 话题讨论区 -->
-  <section>
-    <ion-card>
-      <ion-card-header>
-        <ion-card-title>讨论区</ion-card-title>
-      </ion-card-header>
-      <ion-card-content>
-        <ion-list>
-          <ion-item *ngFor="let post of posts">
-            <ion-label>
-              <h2>{{ post.title }}</h2>
-              <p>作者: {{ post.author }} | 评论数: {{ post.comments }} | 热度: {{ post.popularity }}</p>
-            </ion-label>
-          </ion-item>
-        </ion-list>
-      </ion-card-content>
-    </ion-card>
-  </section>
+  <div class="content-container">
+    <!-- 热门话题区 -->
+    <section class="card-section">
+      <ion-card>
+        <ion-card-header>
+          <ion-card-title>热门话题</ion-card-title>
+        </ion-card-header>
+        <ion-card-content class="scrollable-content">
+          <ion-list>
+            <ion-item *ngFor="let topic of topics">
+              <ion-label>
+                <h2>{{ topic.title }}</h2>
+                <p>参与人数: {{ topic.participants }} | 热度: {{ topic.popularity }}</p>
+              </ion-label>
+            </ion-item>
+          </ion-list>
+        </ion-card-content>
+      </ion-card>
+    </section>
 
-  <!-- 发帖区 -->
-  <section>
-    <ion-card>
-      <ion-card-header>
-        <ion-card-title>发帖</ion-card-title>
-      </ion-card-header>
-      <ion-card-content>
-        <ion-item>
-          <ion-label position="floating">发帖标题</ion-label>
-          <ion-input [(ngModel)]="newPost.title"></ion-input>
-        </ion-item>
-        <ion-item>
-          <ion-label position="floating">发帖内容</ion-label>
-          <ion-textarea [(ngModel)]="newPost.content"></ion-textarea>
-        </ion-item>
-        <ion-button expand="full" (click)="submitPost()">发布</ion-button>
-      </ion-card-content>
-    </ion-card>
-  </section>
+    <!-- 话题讨论区 -->
+    <section class="card-section">
+      <ion-card>
+        <ion-card-header>
+          <ion-card-title>讨论区</ion-card-title>
+        </ion-card-header>
+        <ion-card-content class="scrollable-content">
+          <ion-list>
+            <ion-item *ngFor="let post of posts">
+              <ion-label>
+                <h2>{{ post.title }}</h2>
+                <p>作者: {{ post.author }} | 评论数: {{ post.comments }} | 热度: {{ post.popularity }}</p>
+              </ion-label>
+            </ion-item>
+          </ion-list>
+        </ion-card-content>
+      </ion-card>
+    </section>
+  </div>
+
+
+    <!-- 热门话题区 -->
+    <!-- <section class="card-section">
+      <ion-card>
+        <ion-card-header>
+          <ion-card-title>热门话题</ion-card-title>
+        </ion-card-header>
+        <ion-card-content>
+          <ion-list>
+            <ion-item *ngFor="let topic of topics">
+              <ion-label>
+                <h2>{{ topic.title }}</h2>
+                <p>参与人数: {{ topic.participants }} | 热度: {{ topic.popularity }}</p>
+              </ion-label>
+            </ion-item>
+          </ion-list>
+        </ion-card-content>
+      </ion-card>
+    </section> -->
 
-  <!-- 评论区 -->
-  <section>
-    <ion-card>
-      <ion-card-header>
-        <ion-card-title>评论区</ion-card-title>
-      </ion-card-header>
-      <ion-card-content>
-        <ion-list>
-          <ion-item *ngFor="let comment of comments">
-            <ion-avatar slot="start">
-              <img [src]="comment.userAvatar">
-            </ion-avatar>
-            <ion-label>
-              <h2>{{ comment.username }}</h2>
-              <p>{{ comment.content }}</p>
-            </ion-label>
-          </ion-item>
-        </ion-list>
-      </ion-card-content>
-    </ion-card>
-  </section>
-   <!-- <app-explore-container name="Tab 1 page"></app-explore-container> -->
 </ion-content>

+ 110 - 63
FilmDraw-app/src/app/tab1/tab1.page.scss

@@ -3,84 +3,131 @@
 //   justify-content: space-between; // 使内容在两侧分开
 //   align-items: center; // 垂直居中对齐
 // }
+.content-container {
+  display: flex;
+  justify-content: space-between; /* 在主轴上均匀分配空间 */
+  padding: 16px; /* 内边距 */
+}
+
+.card-section {
+  flex: 1; /* 让每个卡片占据相同的空间 */
+  margin: 0 8px; /* 卡片之间的水平间距 */
+  min-width: 300px; /* 设置最小宽度,以确保在小屏幕上不会过于拥挤 */
+  display: flex;
+  flex-direction: column; /* 使卡片内容垂直排列 */
+  height: 100%; /* 确保卡片高度相同 */
+}
+
+ion-card {
+  box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1); /* 添加阴影效果 */
+  border-radius: 8px; /* 圆角 */
+  overflow: hidden; /* 隐藏溢出内容 */
+}
+
+.scrollable-content {
+  max-height: 300px; /* 设置最大高度,可以根据需要调整 */
+  overflow-y: auto; /* 添加垂直滚动条 */
+}
+
+ion-card-header {
+  background-color: #f8f8f8; /* 卡片头部背景色 */
+  border-top-left-radius: 8px; /* 圆角 */
+  border-top-right-radius: 8px; /* 圆角 */
+}
 
-ion-title {
-  flex: 1; // 使标题占据可用空间
-  text-align: left; // 确保文字左对齐
-  margin-left: 16px; // 左侧边距,可以根据需要调整
-  margin-top: 5px;
+ion-card-title {
+  font-size: 1.2em; /* 标题字体大小 */
+  font-weight: bold; /* 加粗 */
 }
 
-/* 设置搜索框的样式 */
-   ion-searchbar {
-     padding: 10px; /* 内边距 */
-     border-radius: 6px; /* 圆角 */
-     font-size: 16px; /* 字体大小 */
-    }
+ion-label h2 {
+  margin: 0; /* 去掉默认外边距 */
+  font-size: 1.1em; /* 话题标题字体大小 */
+}
 
-ion-buttons {
-  margin-right: 16px; // 右侧边距,可以根据需要调整
-  margin-top: 10px; // 设置按钮与上方内容的间距
-  border-radius: 5px; // 设置按钮圆角
+ion-label p {
+  color: #666; /* 文字颜色 */
+  font-size: 0.9em; /* 字体大小 */
 }
 
 
-ion-header {
-    background-color: #ffffff; // 设置头部背景色
-    color: white; // 设置头部文字颜色
-  }
+// ion-title {
+//   flex: 1; // 使标题占据可用空间
+//   text-align: left; // 确保文字左对齐
+//   margin-left: 16px; // 左侧边距,可以根据需要调整
+//   margin-top: 5px;
+// }
+
+// /* 设置搜索框的样式 */
+//    ion-searchbar {
+//      padding: 10px; /* 内边距 */
+//      border-radius: 6px; /* 圆角 */
+//      font-size: 16px; /* 字体大小 */
+//     }
+
+// ion-buttons {
+//   margin-right: 16px; // 右侧边距,可以根据需要调整
+//   margin-top: 10px; // 设置按钮与上方内容的间距
+//   border-radius: 5px; // 设置按钮圆角
+// }
+
+
+// ion-header {
+//     background-color: #ffffff; // 设置头部背景色
+//     color: white; // 设置头部文字颜色
+//   }
   
-  ion-card {
-    margin: 10px; // 设置卡片之间的间距
-    border-radius: 10px; // 设置卡片圆角
-    box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1); // 添加阴影效果
-  }
+//   ion-card {
+//     margin: 10px; // 设置卡片之间的间距
+//     border-radius: 10px; // 设置卡片圆角
+//     box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1); // 添加阴影效果
+//   }
   
-  ion-card-header {
-    background-color: #d8e5fa; // 设置头部背景色
-    color: white; // 设置头部文字颜色
-  }
+//   ion-card-header {
+//     background-color: #d8e5fa; // 设置头部背景色
+//     color: white; // 设置头部文字颜色
+//   }
   
-  ion-card-title {
-    font-size: 1.2em; // 设置卡片标题字体大小
-    font-weight: bold; // 设置卡片标题字体加粗
-  }
+//   ion-card-title {
+//     font-size: 1.2em; // 设置卡片标题字体大小
+//     font-weight: bold; // 设置卡片标题字体加粗
+//   }
   
-  ion-item {
-    --ion-item-background: transparent; // 设置列表项背景透明
-  }
+//   ion-item {
+//     --ion-item-background: transparent; // 设置列表项背景透明
+//   }
   
-  ion-label {
-    color: #333; // 设置标签文字颜色
-  }
+//   ion-label {
+//     color: #333; // 设置标签文字颜色
+//   }
   
-  ion-button {
-    --background: #3880ff; // 设置按钮背景色
-    --color: white; // 设置按钮文字颜色
-    margin-top: 10px; // 设置按钮与上方内容的间距
-    border-radius: 5px; // 设置按钮圆角
-  }
+//   ion-button {
+//     --background: #3880ff; // 设置按钮背景色
+//     --color: white; // 设置按钮文字颜色
+//     margin-top: 10px; // 设置按钮与上方内容的间距
+//     border-radius: 5px; // 设置按钮圆角
+//   }
   
-  ion-avatar {
-    border-radius: 50%; // 设置头像圆形
-    width: 50px; // 设置头像宽度
-    height: 50px; // 设置头像高度
-  }
+//   ion-avatar {
+//     border-radius: 50%; // 设置头像圆形
+//     width: 50px; // 设置头像宽度
+//     height: 50px; // 设置头像高度
+//   }
   
-  h2 {
-    font-size: 1em; // 设置二级标题字体大小
-    margin: 0; // 去掉默认外边距
-  }
+//   h2 {
+//     font-size: 1em; // 设置二级标题字体大小
+//     margin: 0; // 去掉默认外边距
+//   }
   
-  p {
-    font-size: 0.9em; // 设置段落字体大小
-    color: #666; // 设置段落文字颜色
-  }
+//   p {
+//     font-size: 0.9em; // 设置段落字体大小
+//     color: #666; // 设置段落文字颜色
+//   }
   
-  ion-list {
-    padding: 0; // 去掉列表内边距
-  }
+//   ion-list {
+//     padding: 0; // 去掉列表内边距
+//   }
   
-  ion-card-content {
-    padding: 10px; // 设置卡片内容内边距
-  }
+//   ion-card-content {
+//     padding: 10px; // 设置卡片内容内边距
+//   }

+ 1 - 1
FilmDraw-app/src/app/tab3/tab3.page.ts

@@ -268,7 +268,7 @@ export class Tab3Page {
 
 
   ngOnInit() {
-    // 生命周期:页面加载后,运行医生列表加载函数
+    // 生命周期:页面加载后,运行搭子和角色列表,加载函数
     this.loadFilmPartnerList()
     this.loadFilmRoleList()
   }

+ 86 - 31
FilmDraw-app/src/app/tab4/tab4.page.html

@@ -6,36 +6,91 @@
   
 <ion-content [fullscreen]="true">
 
-<!-- 用户信息区 -->
-<section>
-  <!-- 用户登录状态 -->
-  <ion-card>
+
     <!-- 未登录 -->
-     @if(!currentUser?.id){
-       <ion-card-header>
-         <ion-card-title>请登录</ion-card-title>
-         <ion-card-subtitle>暂无信息</ion-card-subtitle>
-        </ion-card-header>
-      }
-        <!-- 已登录 -->
-     @if(currentUser?.id){
-      <ion-card-header>
-        <ion-card-title>{{currentUser?.get("username")}} {{currentUser?.get("realname")}}</ion-card-title>
-        <ion-card-subtitle>性别:{{currentUser?.get("gender")||"-"}} 年龄:{{currentUser?.get("age")||"-"}}</ion-card-subtitle>
-      </ion-card-header>
-      }
-      <ion-card-content>
-      @if(!currentUser?.id){
-        <ion-button expand="block" (click)="signup()">注册</ion-button>
-        <ion-button expand="block" (click)="login()">登录</ion-button>
-      }
-     @if(currentUser?.id){
-      <ion-button expand="block" (click)="editUser()">编辑资料</ion-button>
-      <ion-button expand="block" (click)="logout()" color="light">登出</ion-button>
+    @if(!currentUser?.id){
+      <div class="profile-container1">
+        <div class="avatar-container">
+          <ion-avatar class="avatar">
+            <img [src]="avatarUrl" alt="avatar">
+          </ion-avatar>
+          <div class="user-info">
+              <ion-label class="username"><a (click)="login()">登录</a><a>/</a><a (click)="signup()">注册</a></ion-label>
+          </div>
+        
+        </div>
+        <ion-label class="bio">个人简介: '请登录后查看' </ion-label>
+        
+      </div>
     }
-    </ion-card-content>
-  </ion-card>
-</section>
-  
-</ion-content>
-  
+
+<!-- 已登录 -->
+@if(currentUser?.id){
+    <div class="profile-container">
+      <div class="avatar-container">
+        <ion-avatar class="avatar">
+          <img [src]="currentUser?.get('avatar')" alt="avatar">
+        </ion-avatar>
+        <div class="user-info">
+          <ion-label class="username">{{ currentUser?.get("username") }}</ion-label>
+          </div>
+
+      </div>
+                <ion-label class="bio">性别:{{ currentUser?.get("gender") || '-' }}  年龄:{{ currentUser?.get("age") || '-' }}</ion-label>
+                <ion-label class="bio">个人简介:{{ currentUser?.get("bio") || '暂无简介' }}</ion-label>
+                <ion-label class="edit" (click)="editUser()" >点击这里编辑资料</ion-label>
+          <ion-button class="button" (click)="logout()">退出登录</ion-button>
+
+    </div>
+  } 
+    <div class="settings-container">
+      <ion-row>
+        <ion-col>
+          <ion-item>
+            <ion-label>历史记录</ion-label>
+            <ion-icon name="footsteps-outline" slot="end"></ion-icon>
+          </ion-item>
+        </ion-col>
+      </ion-row>
+      <ion-row>
+        <ion-col>
+          <ion-item>
+            <ion-icon name="bookmark-outline" slot="end"></ion-icon>
+          <ion-label>收藏</ion-label>
+        </ion-item>
+        </ion-col>
+      </ion-row>
+      <ion-row>
+        <ion-col>
+          <ion-item>
+            <ion-label>隐私与安全设置</ion-label>
+            <ion-icon name="lock-closed-outline" slot="end"></ion-icon>
+          </ion-item>
+        </ion-col>
+      </ion-row>
+      <ion-row>
+        <ion-col>
+          <ion-item>
+            <ion-label>个性化偏好设置</ion-label>
+            <ion-icon name="settings" slot="end"></ion-icon>
+          </ion-item>
+        </ion-col>
+      </ion-row>
+      <ion-row>
+        <ion-col>
+          <ion-item>
+            <ion-label>通知与消息管理</ion-label>
+            <ion-icon slot="end" name="notifications-outline"></ion-icon>
+          </ion-item>
+        </ion-col>
+      </ion-row>
+      <ion-row>
+        <ion-col>
+          <ion-item>
+            <ion-label (click)="goToHelp()">设置与帮助</ion-label>
+            <ion-icon name="help-circle" slot="end"></ion-icon>
+          </ion-item>
+        </ion-col>
+      </ion-row>
+    </div>
+  </ion-content>

+ 145 - 133
FilmDraw-app/src/app/tab4/tab4.page.scss

@@ -1,136 +1,148 @@
-ion-title {
-    flex: 1; // 使标题占据可用空间
-    text-align: left; // 确保文字左对齐
-    margin-left: 16px; // 左侧边距,可以根据需要调整
-    margin-top: 5px;
+  /* 全局样式 */
+  body {
+    font-family: Arial, sans-serif;
+    margin: 0;
+    padding: 0;
+    background-color: #f4f4f4;
   }
+ 
+  ion-content {
+    padding: 20px;
+  }
+ 
+  /* 个人中心模块样式 */
+  .profile-container1 {
+    background: rgba(232, 223, 223, 0.8);
+    backdrop-filter: blur(10px);/* 模糊背景 */
+    border-radius: 10px;
+    box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
+    width: 100%;
+    max-width: 400px;
+    margin: 0 auto;
+    margin-bottom: 20px;
+    overflow: hidden;
+    position: relative;
+    height: calc(1/5 * 100vh); /* 大致占据页面长的1/5 */
+    display: flex;
+    flex-direction: column;
+    justify-content: space-between;
+  }
+
+
+
+  .profile-container {
+    background:rgb(248, 204, 204, 0.8);
+    backdrop-filter: blur(10px);/* 模糊背景 */
+    border-radius: 10px;
+    box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
+    width: 100%;
+    max-width: 400px;
+    margin: 0 auto;
+    margin-bottom: 20px;
+    overflow: hidden;
+    position: relative;
+    height: calc(1/3 * 100vh); /* 大致占据页面长的1/3 */
+    display: flex;
+    flex-direction: column;
+    justify-content: space-between;
+  }
+ 
+
+  .avatar-container {
+    display: flex;
+    align-items: center;
+    padding: 15px;
+  }
+ 
+  .avatar {
+    width: 80px;
+    height: 80px;
+    border-radius: 50%;
+    overflow: hidden;
+    margin-right: 15px;
+  }
+ 
+  .avatar img {
+    width: 100%;
+    height: 100%;
+    object-fit: cover;
+  }
+ 
+  .user-info {
+    flex-grow: 1;
+  }
+ 
+  .username {
+    font-size: 20px;
+    font-weight: bold;
+    text-decoration: none;
+    color: #000000;
+  }
+ 
+  .bio {
+    padding-left: 15px;
+    padding-top: 10px;
+    font-size: 16px;
+    color: #4c4a4a;
+  }
+ 
+  .button {
+    background:#036a99!important;
+    text-transform: uppercase;
+    font-size: 15px;
+    font-weight: bold;
+    margin-left: 30%;
+    margin-right: 30%;
+    margin-bottom: 5px;
+    align-items: center;
+  }
+
+ .edit{
+  padding-left: 15px;
+  padding-top: 10px;
+  font-size: 16px;
+  color: #555050;
+  align-items: center;
+}
 
-  ion-header {
-    background-color: #007aff; // 设置头部背景色为鲜艳的蓝色
-    color: white; // 设置头部文字颜色
-  }
-  
-  ion-card {
-    margin: 10px; // 设置卡片之间的间距
-    border-radius: 15px; // 设置卡片圆角
-    box-shadow: 0 4px 20px rgba(0, 0, 0, 0.2); // 添加更明显的阴影效果
-    transition: transform 0.2s; // 添加过渡效果
-  }
-  
-  ion-card:hover {
-    transform: scale(1.02); // 鼠标悬停时放大卡片
-  }
-  
-  ion-card-header {
-    background-color: #e3f2fd; // 设置卡片头部背景色为浅蓝色
-  }
-  
-  ion-card-title {
-    font-size: 1.5em; // 设置卡片标题字体大小
-    font-weight: bold; // 设置卡片标题字体加粗
-    color: #0d47a1; // 设置标题颜色为深蓝色
-  }
-  
+ 
+ 
+  /* 设置与帮助等模块样式 */
+  .settings-container {
+    display: flex;
+    flex-direction: column;
+    gap: 10px;
+  }
+ 
+  ion-row {
+    display: flex;
+    justify-content: center;
+    padding: 5px 0;
+  }
+ 
+  ion-col {
+    flex: 1;
+    max-width: 400px;
+  }
+ 
   ion-item {
-    --ion-item-background: transparent; // 设置列表项背景透明
-  }
-  
-  ion-avatar {
-    border-radius: 50%; // 设置头像圆形
-    width: 60px; // 设置头像宽度
-    height: 60px; // 设置头像高度
-    border: 2px solid #007aff; // 添加边框
-  }
-  
-  h2 {
-    font-size: 1.2em; // 设置二级标题字体大小
-    margin: 0; // 去掉默认外边距
-    color: #1976d2; // 设置标题颜色为蓝色
-  }
-  
-  p {
-    font-size: 0.9em; // 设置段落字体大小
-    color: #555; // 设置段落文字颜色
-  }
-  
-  ion-thumbnail {
-    width: 100px; // 设置封面图宽度
-    height: 150px; // 设置封面图高度
-    border-radius: 10px; // 设置封面图圆角
-  }
-  
-  ion-button {
-    --background: #007aff; // 设置按钮背景色为鲜艳的蓝色
-    --color: white; // 设置按钮文字颜色
-    border-radius: 20px; // 设置按钮圆角
-    margin-left: 10px; // 设置按钮左边距
-    font-weight: bold; // 设置按钮文字加粗
-  }
-  
-  ion-list {
-    padding: 0; // 去掉列表内边距
-  }
-  ion-avatar {
-    width: 50px; /* 或者你需要的任何尺寸 */
-     height: 50px; /* 保持宽高一致,避免变形 */
-    }
-    
-    ion-avatar img {
-     width: 100%; /* 让图片填满avatar */
-    height: auto; /* 保持图片比例 */
-    }
-    
-    ion-header {
-      background: #f8f9fa; // 设置头部背景色
-     
-    }
-    
-      ion-item{
-        border-radius: 8px; // 设置列表项圆角
-        margin: 5px 0; // 添加列表项的上下外边距
-      }
-  
-    ion-content{
-       background: #f8f9fa; // 设置背景色
-    }
-        
-    ion-col {
-      text-align: center;   /* 确保列内的内容居中(但这里可能由 .ion-text-center 类已经处理) */
-      .avatar-container {
-        display: flex;
-        flex-direction: column; /* 垂直排列子元素 */
-        align-items: center;    /* 垂直居中容器内的内容(对于图像来说可能不是必需的,但对于整体布局有帮助) */
-        margin-top: 20px;       /* 可选:为容器添加一些顶部间距 */
-  
-        ion-avatar {
-          width: 100px;         /* 设置头像的宽度 */
-          height: 100px;        /* 设置头像的高度,与宽度相同以实现圆形 */
-          border-radius: 50%;   /* 将头像设置为圆形 */
-          overflow: hidden;     /* 确保图像不会溢出圆形框架 */
-  
-          img {
-            width: 100%;        /* 让图像填满头像容器 */
-            height: auto; /* 保持图片比例 */
-            object-fit: cover;  /* 确保图像内容适应圆形框架,而不失真 */
-          }
-       }  
-  
-    .username {
-      font-size: 40px;
-      font-weight: bold;
-      margin-top: 10px;
-    }
-       
-      }
-  }
-      ion-col {
-        ion-item {
-          background-color: #ffffff;
-          margin: 2px 0;
-          padding: 2px;
-          box-shadow: 0 1px 5px rgba(0, 0, 0, 0.1);
-          text-align: left;
-          font-size: 15px;
-          width: 100%;
-        }
-      }
+    background: rgba(255, 255, 255, 0.8);
+    backdrop-filter: blur(5px);
+    border-radius: 5px;
+    padding: 5px;
+    display: flex;
+    align-items: center;
+    justify-content: space-between;
+    box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
+  }
+ 
+  ion-label {
+    flex-grow: 1;
+    font-size: 18px;
+    color: #333;
+  }
+ 
+  ion-icon {
+    font-size: 24px;
+    color: #37608a;
+  }

+ 31 - 4
FilmDraw-app/src/app/tab4/tab4.page.ts

@@ -1,21 +1,42 @@
 import { Component } from '@angular/core';
-import { IonHeader, IonToolbar, IonTitle, IonContent, IonCard, IonCardContent, IonButton, IonCardHeader, IonCardTitle, IonCardSubtitle, ModalController } from '@ionic/angular/standalone';
+import { 
+  IonAvatar,IonHeader ,IonButton,IonCol,IonContent,
+  IonItem,IonLabel,IonRow,IonTitle,IonToolbar,ModalController,
+  IonIcon,
+  NavController
+   , } from '@ionic/angular/standalone';
 import { CloudUser } from 'src/lib/ncloud';
 import { openUserLoginModal } from 'src/lib/user/modal-user-login/modal-user-login.component';
 
+import { addIcons } from 'ionicons';
+import { bookmarkOutline, footstepsOutline, helpCircle, lockClosedOutline, notificationsOutline, settings } from 'ionicons/icons';
+import { openUserEditModal } from 'src/lib/user/modal-user-edit/modal-user-edit.component';
+
+
+
+
 @Component({
   selector: 'app-tab4',
   templateUrl: 'tab4.page.html',
   styleUrls: ['tab4.page.scss'],
   standalone: true,
-  imports: [IonHeader, IonToolbar, IonTitle, IonContent, 
-    IonCard,IonCardContent,IonButton,IonCardHeader,IonCardTitle,IonCardSubtitle
+  imports: [  
+      IonHeader,IonToolbar,IonTitle,IonContent,IonAvatar,IonLabel,IonButton,IonRow,IonCol,
+      IonItem,IonIcon,
   ],
 })
 export class Tab4Page {
+  userInfo: any = {
+    username: 'yi',
+    // 其他用户信息字段...
+  };
+  avatarUrl: string = 'assets/img/tx.jpg'; // 默认头像路径
+ 
   currentUser:CloudUser|undefined
-  constructor(private modalCtrl:ModalController) {
+  constructor(private modalCtrl:ModalController, private navCtrl: NavController) {
     this.currentUser = new CloudUser();
+    addIcons({helpCircle,notificationsOutline,settings,lockClosedOutline,bookmarkOutline,footstepsOutline});
+  
   }
   async login(){
     // 弹出登录窗口
@@ -36,7 +57,13 @@ export class Tab4Page {
   }
 
   editUser(){
+    //弹出编辑窗口
+  openUserEditModal(this.modalCtrl);
+  }
+
 
+  goToHelp() {
+    this.navCtrl.navigateForward('/help');
   }
 
 }

+ 131 - 90
FilmDraw-app/src/lib/ncloud.ts

@@ -1,4 +1,5 @@
-// CloudObject.ts
+
+//CloudObject.ts
 export class CloudObject {
     className: string;
     id: string | null = null;
@@ -29,7 +30,7 @@ export class CloudObject {
 
     async save() {
         let method = "POST";
-        let url = `https://dev.fmode.cn/parse/classes/${this.className}`;
+        let url = `http://dev.fmode.cn:1337/parse/classes/${this.className}`;
 
         // 更新
         if (this.id) {
@@ -61,7 +62,7 @@ export class CloudObject {
 
     async destroy() {
         if (!this.id) return;
-        const response = await fetch(`https://dev.fmode.cn/parse/classes/${this.className}/${this.id}`, {
+        const response = await fetch(`http://dev.fmode.cn:1337/parse/classes/${this.className}/${this.id}`, {
             headers: {
                 "x-parse-application-id": "dev"
             },
@@ -82,41 +83,38 @@ export class CloudObject {
 // CloudQuery.ts
 export class CloudQuery {
     className: string;
-    queryParams: Record<string, any> = {};
+    whereOptions: Record<string, any> = {};
 
     constructor(className: string) {
         this.className = className;
     }
 
-    include(...fileds:string[]) {
-        this.queryParams["include"] = fileds;
-    }
     greaterThan(key: string, value: any) {
-        if (!this.queryParams["where"][key]) this.queryParams["where"][key] = {};
-        this.queryParams["where"][key]["$gt"] = value;
+        if (!this.whereOptions[key]) this.whereOptions[key] = {};
+        this.whereOptions[key]["$gt"] = value;
     }
 
     greaterThanAndEqualTo(key: string, value: any) {
-        if (!this.queryParams["where"][key]) this.queryParams["where"][key] = {};
-        this.queryParams["where"][key]["$gte"] = value;
+        if (!this.whereOptions[key]) this.whereOptions[key] = {};
+        this.whereOptions[key]["$gte"] = value;
     }
 
     lessThan(key: string, value: any) {
-        if (!this.queryParams["where"][key]) this.queryParams["where"][key] = {};
-        this.queryParams["where"][key]["$lt"] = value;
+        if (!this.whereOptions[key]) this.whereOptions[key] = {};
+        this.whereOptions[key]["$lt"] = value;
     }
 
     lessThanAndEqualTo(key: string, value: any) {
-        if (!this.queryParams["where"][key]) this.queryParams["where"][key] = {};
-        this.queryParams["where"][key]["$lte"] = value;
+        if (!this.whereOptions[key]) this.whereOptions[key] = {};
+        this.whereOptions[key]["$lte"] = value;
     }
 
     equalTo(key: string, value: any) {
-        this.queryParams["where"][key] = value;
+        this.whereOptions[key] = value;
     }
 
     async get(id: string) {
-        const url = `https://dev.fmode.cn/parse/classes/${this.className}/${id}?`;
+        const url = `http://dev.fmode.cn:1337/parse/classes/${this.className}/${id}?`;
 
         const response = await fetch(url, {
             headers: {
@@ -134,23 +132,12 @@ export class CloudQuery {
     }
 
     async find() {
-        let url = `https://dev.fmode.cn/parse/classes/${this.className}?`;
+        let url = `http://dev.fmode.cn:1337/parse/classes/${this.className}?`;
 
-        let queryStr = ``
-        Object.keys(this.queryParams).forEach(key=>{
-            let paramStr = JSON.stringify(this.queryParams[key]);
-            if(key=="include"){
-                paramStr = this.queryParams[key]?.join(",")
-            }
-            if(queryStr) {
-                url += `${key}=${paramStr}`;
-            }else{
-                url += `&${key}=${paramStr}`;
-            }
-        })
-        // if (Object.keys(this.queryParams["where"]).length) {
-            
-        // }
+        if (Object.keys(this.whereOptions).length) {
+            const whereStr = JSON.stringify(this.whereOptions);
+            url += `where=${whereStr}`;
+        }
 
         const response = await fetch(url, {
             headers: {
@@ -170,10 +157,10 @@ export class CloudQuery {
     }
 
     async first() {
-        let url = `https://dev.fmode.cn/parse/classes/${this.className}?`;
+        let url = `http://dev.fmode.cn:1337/parse/classes/${this.className}?`;
 
-        if (Object.keys(this.queryParams["where"]).length) {
-            const whereStr = JSON.stringify(this.queryParams["where"]);
+        if (Object.keys(this.whereOptions).length) {
+            const whereStr = JSON.stringify(this.whereOptions);
             url += `where=${whereStr}`;
         }
 
@@ -197,6 +184,7 @@ export class CloudQuery {
         return null
     }
 
+    //使得页面上获得的数据是由CloudObject构成的
     dataToObj(exists:any):CloudObject{
         let existsObject = new CloudObject(this.className);
         existsObject.set(exists);
@@ -209,129 +197,182 @@ export class CloudQuery {
 
 // CloudUser.ts
 export class CloudUser extends CloudObject {
+    sessionToken: string | null = ""; // 用户的 sessionToken,用于身份验证
+
     constructor() {
-        super("_User"); // 假设用户类在Parse中是"_User"
+        super("FilmUser"); // 调用父类构造函数,指定类名为 "FilmUser"
+
         // 读取用户缓存信息
-        let userCacheStr = localStorage.getItem("NCloud/dev/User")
-        if(userCacheStr){
-            let userData = JSON.parse(userCacheStr)
+        let userCacheStr = localStorage.getItem("NCloud/dev/User");
+        if (userCacheStr) {
+            let userData = JSON.parse(userCacheStr);
             // 设置用户信息
-            this.id = userData?.objectId;
-            this.sessionToken = userData?.sessionToken;
+            this.id = userData?.objectId; // 设置用户 ID
+            this.sessionToken = userData?.sessionToken; // 设置 sessionToken
             this.data = userData; // 保存用户数据
         }
     }
 
-    sessionToken:string|null = ""
     /** 获取当前用户信息 */
     async current() {
+        // 检查用户是否登录
         if (!this.sessionToken) {
             console.error("用户未登录");
-            return null;
+            return null; // 如果未登录,返回 null
         }
         return this;
-        // const response = await fetch(`https://dev.fmode.cn/parse/users/me`, {
-        //     headers: {
-        //         "x-parse-application-id": "dev",
-        //         "x-parse-session-token": this.sessionToken // 使用sessionToken进行身份验证
-        //     },
-        //     method: "GET"
-        // });
-
-        // const result = await response?.json();
-        // if (result?.error) {
-        //     console.error(result?.error);
-        //     return null;
-        // }
-        // return result;
-    }
+        
+    //     // 发送 GET 请求以获取当前用户信息
+    //     const response = await fetch(`http://dev.fmode.cn:1337/parse/users/me`, {
+    //         headers: {
+    //             "x-parse-application-id": "dev",
+    //             "x-parse-session-token": this.sessionToken // 使用 sessionToken 进行身份验证
+    //         },
+    //         method: "GET"
+    //     });
+
+    //     const result = await response?.json(); // 解析响应
+    //     if (result?.error) {
+    //         console.error(result?.error); // 处理错误
+    //         return null;
+    //     }
+    //     return result; // 返回用户信息
+    
+     }
 
     /** 登录 */
-    async login(username: string, password: string):Promise<CloudUser|null> {
-        const response = await fetch(`https://dev.fmode.cn/parse/login`, {
+    async login(username: string, password: string): Promise<CloudUser | null> {
+        // 发送 POST 请求以进行用户登录
+        const response = await fetch(`http://dev.fmode.cn:1337/parse/login`, {
             headers: {
                 "x-parse-application-id": "dev",
                 "Content-Type": "application/json"
             },
-            body: JSON.stringify({ username, password }),
+            body: JSON.stringify({ username, password }), // 登录凭据
             method: "POST"
         });
 
-        const result = await response?.json();
+        const result = await response?.json(); // 解析响应
         if (result?.error) {
-            console.error(result?.error);
+            console.error(result?.error); // 处理错误
             return null;
         }
         
         // 设置用户信息
-        this.id = result?.objectId;
-        this.sessionToken = result?.sessionToken;
+        this.id = result?.objectId; // 设置用户 ID
+        this.sessionToken = result?.sessionToken; // 设置 sessionToken
         this.data = result; // 保存用户数据
-        // 缓存用户信息
-        console.log(result)
-        localStorage.setItem("NCloud/dev/User",JSON.stringify(result))
-        return this;
+
+        // 缓存用户信息到 localStorage
+        console.log(result);
+        localStorage.setItem("NCloud/dev/User", JSON.stringify(result));
+        return this; // 返回当前用户实例
     }
 
     /** 登出 */
     async logout() {
+        // 检查用户是否登录
         if (!this.sessionToken) {
             console.error("用户未登录");
             return;
         }
 
-        const response = await fetch(`https://dev.fmode.cn/parse/logout`, {
+        // 发送 POST 请求以进行用户登出
+        const response = await fetch(`http://dev.fmode.cn:1337/parse/logout`, {
             headers: {
                 "x-parse-application-id": "dev",
-                "x-parse-session-token": this.sessionToken
+                "x-parse-session-token": this.sessionToken // 使用 sessionToken 进行身份验证
             },
             method: "POST"
         });
 
-        const result = await response?.json();
+        const result = await response?.json(); // 解析响应
         if (result?.error) {
-            console.error(result?.error);
-            return false;
+            console.error(result?.error); // 处理错误
+            return false; // 登出失败
         }
 
         // 清除用户信息
-        localStorage.removeItem("NCloud/dev/User")
-        this.id = null;
-        this.sessionToken = null;
-        this.data = {};
-        return true;
+        localStorage.removeItem("NCloud/dev/User"); // 从 localStorage 中移除用户信息
+        this.id = null; // 清除用户 ID
+        this.sessionToken = null; // 清除 sessionToken
+        this.data = {}; // 清空用户数据
+        return true; // 登出成功
     }
 
     /** 注册 */
     async signUp(username: string, password: string, additionalData: Record<string, any> = {}) {
+        // 构建用户数据
         const userData = {
             username,
             password,
             ...additionalData // 合并额外的用户数据
         };
 
-        const response = await fetch(`https://dev.fmode.cn/parse/users`, {
+        // 发送 POST 请求以进行用户注册
+        const response = await fetch(`http://dev.fmode.cn:1337/parse/users`, {
             headers: {
                 "x-parse-application-id": "dev",
                 "Content-Type": "application/json"
             },
-            body: JSON.stringify(userData),
+            body: JSON.stringify(userData), // 注册凭据
             method: "POST"
         });
 
-        const result = await response?.json();
+        const result = await response?.json(); // 解析响应
         if (result?.error) {
-            console.error(result?.error);
-            return null;
+            console.error(result?.error); // 处理错误
+            return null; // 注册失败
         }
 
         // 设置用户信息
-        // 缓存用户信息
-        console.log(result)
-        localStorage.setItem("NCloud/dev/User",JSON.stringify(result))
-        this.id = result?.objectId;
-        this.sessionToken = result?.sessionToken;
+        this.id = result?.objectId; // 设置用户 ID
+        this.sessionToken = result?.sessionToken; // 设置 sessionToken
         this.data = result; // 保存用户数据
+        return this; // 返回当前用户实例
+    }
+
+    //覆盖save方法
+    override async save() {
+        let method = "POST";
+        let url = `http://dev.fmode.cn:1337/parse/users`;
+    
+        // 更新用户信息
+        if (this.id) {
+            url += `/${this.id}`;
+            method = "PUT";
+        }
+    
+        let data:any = JSON.parse(JSON.stringify(this.data))
+        delete data.createdAt
+        delete data.updatedAt
+        delete data.ACL
+        delete data.objectId
+        const body = JSON.stringify(data);
+        let headersOptions:any = {
+            "content-type": "application/json;charset=UTF-8",
+            "x-parse-application-id": "dev",
+            "x-parse-session-token": this.sessionToken, // 添加sessionToken以进行身份验证
+        }
+        const response = await fetch(url, {
+            headers: headersOptions,
+            body: body,
+            method: method,
+            mode: "cors",
+            credentials: "omit"
+        });
+    
+        const result = await response?.json();
+        if (result?.error) {
+            console.error(result?.error);
+        }
+        if (result?.objectId) {
+            this.id = result?.objectId;
+        }
+        localStorage.setItem("NCloud/dev/User",JSON.stringify(this.data))
         return this;
     }
+
+
+
 }

+ 32 - 0
FilmDraw-app/src/lib/user/modal-user-edit/modal-user-edit.component.html

@@ -0,0 +1,32 @@
+<!-- 用户登录状态 -->
+<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-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-item>
+  <ion-item>
+     <ion-input [value]="userData['gender']" (ionChange)="userDataChange('gender',$event)" label="性别" placeholder="请您输入男/女"></ion-input>
+    </ion-item>
+    <ion-item>
+      <ion-input [value]="userData['email']" (ionChange)="userDataChange('email',$event)" label="邮箱" placeholder="请输入邮箱"></ion-input>
+     </ion-item>
+     <ion-item>
+      <ion-input [value]="userData['bio']" (ionChange)="userDataChange('dio',$event)" label="个人简介" placeholder="个人简介"></ion-input>
+     </ion-item>
+
+   <ion-button expand="block" (click)="save()">保存</ion-button>
+   <ion-button expand="block" (click)="cancel()">取消</ion-button>
+ 
+
+</ion-card-content>
+</ion-card>

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


+ 22 - 0
FilmDraw-app/src/lib/user/modal-user-edit/modal-user-edit.component.spec.ts

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

+ 65 - 0
FilmDraw-app/src/lib/user/modal-user-edit/modal-user-edit.component.ts

@@ -0,0 +1,65 @@
+import {  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 { CloudUser } from 'src/lib/ncloud';
+
+@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
+  ],
+})
+export class ModalUserEditComponent  implements OnInit {
+
+  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) { 
+    this.currentUser = new CloudUser();
+    this.userData = this.currentUser.data;
+  }
+
+  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")
+  }
+  cancel(){
+    this.modalCtrl.dismiss(null,"cancel")
+
+  }
+}
+
+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();
+
+  const { data, role } = await modal.onWillDismiss();
+
+  if (role === 'confirm') {
+    return data;
+  }
+  return null
+}

+ 45 - 4
FilmDraw-server/lib/ncloud.js

@@ -5,7 +5,9 @@ class CloudObject{
     constructor(className){
         this.className = className
     }
-
+    toPointer(){
+        return {"__type":"Pointer","className":this.className,"objectId":this.id}
+    }
     set(json){
         Object.keys(json).forEach(key=>{
             if(["objectId","id","createdAt","updatedAt","ACL"].indexOf(key)>-1){
@@ -17,12 +19,13 @@ class CloudObject{
     get(key){
         return this.data[key] || null
     }
+    //保存(更新+创建)
     async save(){
         let method = "POST"
         let url = "http://dev.fmode.cn:1337/parse/classes/" + this.className
         // 更新
         if(this.id){
-            url = "/"+this.id
+            url += "/"+this.id+"?"
             method = "PUT"
         } 
         let body = JSON.stringify(this.data)
@@ -37,12 +40,16 @@ class CloudObject{
             "credentials": "omit"
           });
           let result = await response?.json();
+          if(result?.error){
+            console.error(result?.error)
+          };
           if(result?.objectId){this.id = result?.objectId}
           return this
     }
+    //删除 
     async destory(){
         if(!this.id) return
-        let response = await fetch("http://dev.fmode.cn:1337/parse/classes/Doctor/"+this.id, {
+        let response = await fetch("http://dev.fmode.cn:1337/parse/classes/"+this.className+"/"+this.id+"?", {
             "headers": {
               "x-parse-application-id": "dev"
             },
@@ -59,6 +66,7 @@ class CloudObject{
     }
 }
 
+//查询
 class CloudQuery{
     className
     constructor(className){
@@ -82,11 +90,14 @@ class CloudQuery{
         if(!this.whereOptions[key]) this.whereOptions[key] = {}
         this.whereOptions[key]["$lte"] = value
     }
+
     equalTo(key,value){
         this.whereOptions[key] = value
     }
 
+    //通过id查询    
     async get(id){
+        
         let url = "http://dev.fmode.cn:1337/parse/classes/"+this.className+"/"+id+"?"
 
         let response = await fetch(url, {
@@ -102,6 +113,8 @@ class CloudQuery{
         let json = await response?.json();
         return json || {}
     }
+  
+    //find查询
     async find(){
         let url = "http://dev.fmode.cn:1337/parse/classes/"+this.className+"?"
         
@@ -123,8 +136,36 @@ class CloudQuery{
         let json = await response?.json();
         return json?.results || []
     }
-    first(){
 
+    //只查询第一项
+    async first(){
+        let url = "http://dev.fmode.cn:1337/parse/classes/"+this.className+"?"
+        
+        if(Object.keys(this.whereOptions)?.length){
+            let whereStr = JSON.stringify(this.whereOptions)
+            url += `where=${whereStr}`
+        }
+
+        let response = await fetch(url, {
+            "headers": {
+            "if-none-match": "W/\"1f0-ghxH2EwTk6Blz0g89ivf2adBDKY\"",
+            "x-parse-application-id": "dev"
+            },
+            "body": null,
+            "method": "GET",
+            "mode": "cors",
+            "credentials": "omit"
+        });
+        let json = await response?.json();
+        let exists = json?.results?.[0] || null
+        if(exists){
+            let existsObject = new CloudObject(this.className)
+            existsObject.set(exists)
+            existsObject.id = exists.objectId
+            existsObject.createdAt = exists.createdAt
+            existsObject.updatedAt = exists.updatedAt
+            return existsObject
+        }
     }
 }
 

+ 37 - 1
FilmDraw-server/migration/data.js

@@ -1 +1,37 @@
- 
+module.exports.FilmUserList = [
+    {
+        "objectId": "user_001",
+        "password": "11111",
+        "age": 25,
+        "gender": "男",
+        "avatar": "https://example.com/avatars/avatar_001.png"
+    },
+    {
+        "objectId": "user_002",
+        "password": "11111",
+        "age": 30,
+        "gender": "女",
+        "avatar": "https://example.com/avatars/avatar_002.png"
+    },
+    {
+        "objectId": "user_003",
+        "password": "11111",
+        "age": 22,
+        "gender": "女",
+        "avatar": "https://example.com/avatars/avatar_003.png"
+    },
+    {
+        "objectId": "user_004",
+        "password": "11111",
+        "age": 28,
+        "gender": "男",
+        "avatar": "https://example.com/avatars/avatar_004.png"
+    },
+    {
+        "objectId": "user_005",
+        "password": "11111",
+        "age": 35,
+        "gender": "女",
+        "avatar": "https://example.com/avatars/avatar_005.png"
+    }
+]

+ 130 - 22
FilmDraw-server/migration/import-data.js

@@ -1,28 +1,136 @@
 const { CloudQuery, CloudObject } = require("../lib/ncloud");
-testCRUD()
-// testQuery()
-
-async function testQuery(){
-    let query = new CloudQuery("FilmRole")
-    // query.equalTo("gender","女")
-    // query.greaterThanAndEqualTo("age",40)
-    // query.lessThan("age",41)
-    let list = await query.find();
-    console.log(list)
+const { FilmUserList } = require("./data.js");
+inportFilmUser()
+
+DataMap = {
+   FilmUser:{}
+}
+
+async function inportFilmUser(){
+    // 导入用户数据
+    let filmuserList =FilmUserList
+    for (let index = 0; index <filmuserList.length; index++) {
+        let filmuser =filmuserList[index];
+       filmuser = await importObject("FilmUser",filmuser)
+    }
+    console.log(DataMap["FilmUser"])
+}
+
+async function importObject(className,data){
+
+    // 查重 username 数据源列表中的objectId并非数据库生成的唯一ID,因此需要有一个username字段进行记录,并查重
+    let query = new CloudQuery(className)
+    let username = data.objectId
+    query.equalTo("username",username)
+    let importObj = await query.first()
+    console.log(importObj)
+
+    // 导入
+    // 导入前批量处理Pointer类型数据,进行重定向
+    Object.keys(data)?.forEach(key=>{
+        let field = data[key]
+        let username = field?.objectId
+        if(username){ // 是数组字段
+            if(key=="depart"){
+                data[key] = DataMap?.["Department"]?.[username]?.toPointer();
+            }
+        }
+    })
+    // 若未添加,则创建新对象并保存
+    if(!importObj?.id){
+        importObj = new CloudObject(className)
+    }
+
+      // 保存或更新数据
+      data.username = username;
+      importObj.set(data);
+      importObj = await importObj.save();
+      DataMap[className][username] = importObj
 }
 
-async function testCRUD(){
-    // 基本的增删查改测试
-    let query = new CloudQuery("FilmRole")
-    let FilmRoleList = await query.find();
-    console.log("FilmRolelist count",FilmRoleList?.length)
 
-    let newFilmRole = new CloudObject("FilmRole")
-    newFilmRole.set({"name":"123"})
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+// 测试
+
+// const { CloudQuery, CloudObject } = require("../lib/ncloud");
+// testUser()
+// // testCRUD()
+// // testQuery()
+
+// async function testQuery(){
+//     let query = new CloudQuery("FilmRole")
+//     // query.equalTo("gender","女")
+//     // query.greaterThanAndEqualTo("age",40)
+//     // query.lessThan("age",41)
+//     let list = await query.find();
+//     console.log(list)
+// }
+
+// async function testCRUD(){
+//     // 基本的增删查改测试
+//     let query = new CloudQuery("FilmRole")
+//     let FilmRoleList = await query.find();
+//     console.log("FilmRolelist count",FilmRoleList?.length)
+
+//     let newFilmRole = new CloudObject("FilmRole")
+//     newFilmRole.set({"name":"123"})
     
-    newFilmRole = await newFilmRole.save(newFilmRole)
-    console.log("newFilmRole",newFilmRole)
+//     newFilmRole = await newFilmRole.save(newFilmRole)
+//     console.log("newFilmRole",newFilmRole)
+
+//     await newFilmRole.destory()
+//     console.log("newFilmRole 已删除",newFilmRole)
+// }
+
+// async function testUser(){
+//     //基本的增删查改测试
+//     let query = new CloudQuery("FilmUser")
+//     query.equalTo("name","qwq")
+//     let List =await query.find();
+
+//     List.forEach(obj => {
+//         let newFilmUser = new CloudObject("FilmUser")
+//         console.log(`${obj.objectId}`);
+//         newFilmUser.id=`${obj.objectId}`
+//         newFilmUser.destory()
+//         console.log("newFilmUser 已删除",newFilmUser)
+//       });
+    
+
+//     // let FilmUserList = await query.find();
+//     // console.log("FilmUserList count",FilmUserList?.length)
 
-    await newFilmRole.destory()
-    console.log("newFilmRole 已删除",newFilmRole)
-}
+//     let query1 = new CloudQuery("FilmUser")
+//     let FilmUserList1 = await query1.find();
+//     console.log(FilmUserList1)
+//     console.log("FilmUserList count",FilmUserList1?.length)
+// }