Răsfoiți Sursa

feat: optimize tab1 and tab4 pages

cyx 3 luni în urmă
părinte
comite
7dba6d2d54
28 a modificat fișierele cu 878 adăugiri și 144 ștergeri
  1. 20 9
      TFPower-app/src/app/app.routes.ts
  2. 5 0
      TFPower-app/src/app/component/draggable-button/draggable-button.component.html
  3. 6 0
      TFPower-app/src/app/component/draggable-button/draggable-button.component.scss
  4. 22 0
      TFPower-app/src/app/component/draggable-button/draggable-button.component.spec.ts
  5. 44 0
      TFPower-app/src/app/component/draggable-button/draggable-button.component.ts
  6. 9 1
      TFPower-app/src/app/page/login-page/login-page.page.ts
  7. 21 0
      TFPower-app/src/app/page/modal/modal-browse-page/modal-browse-page.page.html
  8. 6 0
      TFPower-app/src/app/page/modal/modal-browse-page/modal-browse-page.page.scss
  9. 17 0
      TFPower-app/src/app/page/modal/modal-browse-page/modal-browse-page.page.spec.ts
  10. 64 0
      TFPower-app/src/app/page/modal/modal-browse-page/modal-browse-page.page.ts
  11. 0 0
      TFPower-app/src/app/page/modal/modal-edit-page/modal-edit-page.page.html
  12. 0 0
      TFPower-app/src/app/page/modal/modal-edit-page/modal-edit-page.page.scss
  13. 0 0
      TFPower-app/src/app/page/modal/modal-edit-page/modal-edit-page.page.spec.ts
  14. 0 0
      TFPower-app/src/app/page/modal/modal-edit-page/modal-edit-page.page.ts
  15. 36 0
      TFPower-app/src/app/page/modal/modal-newsedit/modal-newsedit.page.html
  16. 0 0
      TFPower-app/src/app/page/modal/modal-newsedit/modal-newsedit.page.scss
  17. 17 0
      TFPower-app/src/app/page/modal/modal-newsedit/modal-newsedit.page.spec.ts
  18. 111 0
      TFPower-app/src/app/page/modal/modal-newsedit/modal-newsedit.page.ts
  19. 58 23
      TFPower-app/src/app/post-page/post-page.component.ts
  20. 27 40
      TFPower-app/src/app/tab1/tab1.page.html
  21. 41 0
      TFPower-app/src/app/tab1/tab1.page.scss
  22. 111 6
      TFPower-app/src/app/tab1/tab1.page.ts
  23. 48 1
      TFPower-app/src/app/tab4/tab4.page.html
  24. 71 0
      TFPower-app/src/app/tab4/tab4.page.scss
  25. 44 35
      TFPower-app/src/app/tab4/tab4.page.ts
  26. 100 29
      TFPower-app/src/lib/cyxncloud.ts
  27. 0 0
      TFPower-server/migration/lib/test/ncloud.js
  28. 0 0
      TFPower-server/migration/lib/test/test.js

+ 20 - 9
TFPower-app/src/app/app.routes.ts

@@ -1,4 +1,4 @@
-import { Routes,RouterModule } from '@angular/router';
+import { Routes, RouterModule } from '@angular/router';
 import { PostPageComponent } from './post-page/post-page.component'; // 导入 PostPage 组件
 import { CommunityPage } from './community/community.page';
 import { SharePageComponent } from './share-page/share-page.component';
@@ -9,16 +9,17 @@ export const routes: Routes = [
     loadChildren: () => import('./tabs/tabs.routes').then((m) => m.routes),
   },
   {
-
     path: 'community',
-    loadComponent: () => import('./community/community.page').then( m => m.CommunityPage)
+    loadComponent: () =>
+      import('./community/community.page').then((m) => m.CommunityPage),
   },
   { path: '', redirectTo: 'community', pathMatch: 'full' },
   { path: 'community', component: CommunityPage },
   { path: 'post-page/:id', component: PostPageComponent }, // 动态详情页面的路由
   { path: 'share', component: SharePageComponent },
 
-    {path: 'login-page',
+  {
+    path: 'login-page',
     loadComponent: () =>
       import('./page/login-page/login-page.page').then((m) => m.LoginPagePage),
   },
@@ -28,16 +29,26 @@ export const routes: Routes = [
       import('./page/register-page/register-page.page').then(
         (m) => m.RegisterPagePage
       ),
+  },
+  {
+    path: 'modal-edit-page',
+    loadComponent: () =>
+      import('./page/modal/modal-edit-page/modal-edit-page.page').then(
+        (m) => m.ModalEditPagePage
+      ),
   },
  {
-    path: 'modal-edit-page',
-    loadComponent: () => import('./page/modal-edit-page/modal-edit-page.page').then( m => m.ModalEditPagePage)
+    path: 'modal-browse-page',
+    loadComponent: () => import('./page/modal/modal-browse-page/modal-browse-page.page').then( m => m.ModalBrowsePagePage)
+  },
+  {
+    path: 'modal-newsedit',
+    loadComponent: () => import('./page/modal/modal-newsedit/modal-newsedit.page').then( m => m.ModalNewseditPage)
   },
 
 ];
 
-
 @NgModule({
   imports: [RouterModule.forRoot(routes)],
-  exports: [RouterModule]
+  exports: [RouterModule],
 })
-export class AppRoutingModule { }
+export class AppRoutingModule {}

+ 5 - 0
TFPower-app/src/app/component/draggable-button/draggable-button.component.html

@@ -0,0 +1,5 @@
+<ion-fab id="draggable-button" (click)="openModal()">
+  <ion-fab-button>
+    <ion-icon name="add"></ion-icon>
+  </ion-fab-button>
+</ion-fab>

+ 6 - 0
TFPower-app/src/app/component/draggable-button/draggable-button.component.scss

@@ -0,0 +1,6 @@
+#draggable-button {
+  position: fixed;
+  bottom: 70px;
+  right: 20px;
+  cursor: grab;
+}

+ 22 - 0
TFPower-app/src/app/component/draggable-button/draggable-button.component.spec.ts

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

+ 44 - 0
TFPower-app/src/app/component/draggable-button/draggable-button.component.ts

@@ -0,0 +1,44 @@
+import { Component, OnInit, ElementRef, Renderer2 } from '@angular/core';
+import {
+  IonFab,
+  IonFabButton,
+  IonIcon,
+  ModalController,
+} from '@ionic/angular/standalone';
+import { addIcons } from 'ionicons';
+import { add } from 'ionicons/icons';
+import { ModalNewseditPage } from 'src/app/page/modal/modal-newsedit/modal-newsedit.page';
+import { CloudQuery, CloudNews, CloudUser } from 'src/lib/cyxncloud';
+
+@Component({
+  selector: 'app-draggable-button',
+  templateUrl: './draggable-button.component.html',
+  styleUrls: ['./draggable-button.component.scss'],
+  standalone: true,
+  imports: [IonFab, IonFabButton, IonIcon],
+})
+export class DraggableButtonComponent implements OnInit {
+  constructor(
+    private el: ElementRef,
+    private renderer: Renderer2,
+    private modalCtrl: ModalController
+  ) {
+    addIcons({ add });
+  }
+
+  ngOnInit() {}
+  async openModal() {
+    const modal = await this.modalCtrl.create({
+      component: ModalNewseditPage,
+    });
+    modal.present();
+    // const { data, role } = await modal.onWillDismiss();
+
+    // if (role === 'confirm') {
+    //   // console.log(data);
+    //   // let news = new CloudNews();
+    //   // let res = await news.add(data);
+    //   // console.log('res', res);
+    // }
+  }
+}

+ 9 - 1
TFPower-app/src/app/page/login-page/login-page.page.ts

@@ -81,11 +81,19 @@ export class LoginPagePage implements OnInit {
         localStorage.setItem('userData', JSON.stringify(this.currentUser.data));
         localStorage.setItem('is_login', 'true');
         console.log('登录成功');
+
         this.router.navigate([`/tabs/tab4`]);
+        // this.refreshPage();
+        // let is_login = localStorage.getItem('is_login');
+        // if (is_login) {
+        //   this.router.navigate([`/tabs/tab4`]);
+        // }
       }
     }
   }
-
+  refreshPage() {
+    window.location.reload(); // 强制刷新整个页面
+  }
   navigateToRegister() {
     this.navCtrl.navigateForward('/register');
   }

+ 21 - 0
TFPower-app/src/app/page/modal/modal-browse-page/modal-browse-page.page.html

@@ -0,0 +1,21 @@
+<ion-header>
+  <ion-toolbar>
+    <ion-title>{{newsData.title}}</ion-title>
+    <ion-buttons slot="start">
+      <ion-button (click)="cancel()"><ion-icon name="arrow-back-outline"></ion-icon></ion-button>
+    </ion-buttons>
+  </ion-toolbar>
+</ion-header>
+<ion-content class="ion-padding">
+  <ion-card>
+    <img [src]="newsData.illustration" />
+    <ion-card-header>
+      <ion-card-title>{{newsData.title}}</ion-card-title>
+      <ion-card-subtitle>{{newsData.column}}</ion-card-subtitle>
+    </ion-card-header>
+    <ion-card-content>
+      <div [innerHTML]="newsData.article" class="news-description"></div>
+    </ion-card-content>
+  </ion-card>
+
+</ion-content>

+ 6 - 0
TFPower-app/src/app/page/modal/modal-browse-page/modal-browse-page.page.scss

@@ -0,0 +1,6 @@
+.news-description {
+  text-indent: 2em; /* 段落首行缩进 */
+  display: -webkit-box; /* 将元素设置为弹性盒子 */
+  -webkit-box-orient: vertical; /* 设置盒子的排列方向为垂直方向 */
+  white-space: normal; /* 允许多行文本 */
+}

+ 17 - 0
TFPower-app/src/app/page/modal/modal-browse-page/modal-browse-page.page.spec.ts

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

+ 64 - 0
TFPower-app/src/app/page/modal/modal-browse-page/modal-browse-page.page.ts

@@ -0,0 +1,64 @@
+import { Component, OnInit } from '@angular/core';
+import { CommonModule } from '@angular/common';
+import { FormsModule } from '@angular/forms';
+import {
+  IonBackButton,
+  IonButton,
+  IonButtons,
+  IonCard,
+  IonCardContent,
+  IonCardHeader,
+  IonCardSubtitle,
+  IonCardTitle,
+  IonContent,
+  IonHeader,
+  IonIcon,
+  IonTitle,
+  IonToolbar,
+  ModalController,
+  NavParams,
+} from '@ionic/angular/standalone';
+import { addIcons } from 'ionicons';
+import { arrowBackOutline } from 'ionicons/icons';
+
+@Component({
+  selector: 'app-modal-browse-page',
+  templateUrl: './modal-browse-page.page.html',
+  styleUrls: ['./modal-browse-page.page.scss'],
+  standalone: true,
+  imports: [
+    IonContent,
+    IonHeader,
+    IonTitle,
+    IonToolbar,
+    CommonModule,
+    FormsModule,
+    IonButton,
+    IonButtons,
+    IonCard,
+    IonCardHeader,
+    IonCardTitle,
+    IonCardSubtitle,
+    IonCardContent,
+    IonBackButton,
+    IonIcon,
+  ],
+})
+export class ModalBrowsePagePage implements OnInit {
+  newsData: any = {};
+  constructor(
+    private modalCtrl: ModalController,
+    private navParams: NavParams
+  ) {
+    addIcons({
+      arrowBackOutline,
+    });
+  }
+  cancel() {
+    return this.modalCtrl.dismiss();
+  }
+
+  ngOnInit() {
+    this.newsData = this.navParams.get('newsData');
+  }
+}

+ 0 - 0
TFPower-app/src/app/page/modal-edit-page/modal-edit-page.page.html → TFPower-app/src/app/page/modal/modal-edit-page/modal-edit-page.page.html


+ 0 - 0
TFPower-app/src/app/page/modal-edit-page/modal-edit-page.page.scss → TFPower-app/src/app/page/modal/modal-edit-page/modal-edit-page.page.scss


+ 0 - 0
TFPower-app/src/app/page/modal-edit-page/modal-edit-page.page.spec.ts → TFPower-app/src/app/page/modal/modal-edit-page/modal-edit-page.page.spec.ts


+ 0 - 0
TFPower-app/src/app/page/modal-edit-page/modal-edit-page.page.ts → TFPower-app/src/app/page/modal/modal-edit-page/modal-edit-page.page.ts


+ 36 - 0
TFPower-app/src/app/page/modal/modal-newsedit/modal-newsedit.page.html

@@ -0,0 +1,36 @@
+<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-toast color="success" [isOpen]="isToastOpenSuccess" message="添加成功" [duration]="5000"
+      (didDismiss)="setOpenSuccess(false)"></ion-toast>
+    <!-- 条件不为空 -->
+    <ion-toast color="danger" [isOpen]="isToastOpenDanger" message="输入均不能为空" [duration]="5000"
+      (didDismiss)="setOpenDanger(false)"></ion-toast>
+    <!-- 先登录 -->
+    <ion-toast color="danger" [isOpen]="isToastOpenWaring" message="无法获取用户信息,请先登录" [duration]="5000"
+      (didDismiss)="setOpenWarning(false)"></ion-toast>
+  </ion-toolbar>
+</ion-header>
+<ion-content class="ion-padding">
+  <ion-item>
+    <ion-input label="主题" labelPlacement="stacked" placeholder="theme" (ionInput)="InputTheme($event)"></ion-input>
+  </ion-item>
+  <ion-item>
+    <ion-input label="标题" labelPlacement="stacked" placeholder="title" (ionInput)="InputTitle($event)"></ion-input>
+  </ion-item>
+  <ion-item>
+    <ion-input label="栏目" labelPlacement="stacked" placeholder="column" (ionInput)="InputColume($event)"></ion-input>
+  </ion-item>
+  <ion-item>
+    <ion-textarea label="文章" labelPlacement="stacked" placeholder="article" [autoGrow]="true" [counter]="true"
+      maxlength="2500" (ionInput)="InputArticle($event)"></ion-textarea>
+  </ion-item>
+
+</ion-content>

+ 0 - 0
TFPower-app/src/app/page/modal/modal-newsedit/modal-newsedit.page.scss


+ 17 - 0
TFPower-app/src/app/page/modal/modal-newsedit/modal-newsedit.page.spec.ts

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

+ 111 - 0
TFPower-app/src/app/page/modal/modal-newsedit/modal-newsedit.page.ts

@@ -0,0 +1,111 @@
+import { Component, OnInit } from '@angular/core';
+import { CommonModule } from '@angular/common';
+import { FormsModule } from '@angular/forms';
+import { CloudQuery, CloudNews, CloudUser } from 'src/lib/cyxncloud';
+import {
+  IonButton,
+  IonButtons,
+  IonContent,
+  IonHeader,
+  IonInput,
+  IonItem,
+  IonTextarea,
+  IonTitle,
+  IonToolbar,
+  ModalController,
+  IonToast,
+} from '@ionic/angular/standalone';
+
+@Component({
+  selector: 'app-modal-newsedit',
+  templateUrl: './modal-newsedit.page.html',
+  styleUrls: ['./modal-newsedit.page.scss'],
+  standalone: true,
+  imports: [
+    IonContent,
+    IonHeader,
+    IonTitle,
+    IonToolbar,
+    CommonModule,
+    FormsModule,
+    IonContent,
+    IonHeader,
+    IonTitle,
+    IonToolbar,
+    IonButtons,
+    IonButton,
+    IonItem,
+    IonInput,
+    IonTextarea,
+    IonToast,
+  ],
+})
+export class ModalNewseditPage implements OnInit {
+  theme: string = '';
+  title: string = '';
+  column: string = '';
+  article: string = '';
+  isToastOpenSuccess = false;
+  isToastOpenDanger = false;
+  isToastOpenWaring = false;
+
+  setOpenSuccess(isOpen: boolean) {
+    this.isToastOpenSuccess = isOpen;
+  }
+  setOpenDanger(isOpen: boolean) {
+    this.isToastOpenDanger = isOpen;
+  }
+  setOpenWarning(isOpen: boolean) {
+    this.isToastOpenWaring = isOpen;
+  }
+
+  InputTheme(ev: any) {
+    this.theme = ev.detail.value;
+  }
+  InputTitle(ev: any) {
+    this.title = ev.detail.value;
+  }
+  InputColume(ev: any) {
+    this.column = ev.detail.value;
+  }
+  InputArticle(ev: any) {
+    this.article = ev.detail.value;
+  }
+
+  ngOnInit() {}
+  constructor(private modalCtrl: ModalController) {}
+
+  cancel() {
+    return this.modalCtrl.dismiss();
+  }
+
+  async confirm() {
+    let newsData = {
+      author: new CloudUser().toPointer(),
+      theme: this.theme,
+      title: this.title,
+      column: this.column,
+      article: this.article,
+    };
+
+    console.log(newsData);
+    console.log(!newsData.author.objectId.length);
+
+    if (!newsData.author.objectId.length) {
+      this.setOpenWarning(true);
+      return;
+    }
+    if (!this.theme || !this.title || !this.column || !this.article) {
+      this.setOpenDanger(true);
+      return;
+    }
+
+    let news = new CloudNews();
+    let result = await news.add(newsData);
+    if (result.objectId) {
+      this.setOpenSuccess(true);
+      return this.modalCtrl.dismiss();
+    }
+    return;
+  }
+}

+ 58 - 23
TFPower-app/src/app/post-page/post-page.component.ts

@@ -1,6 +1,23 @@
 import { Component, OnInit } from '@angular/core';
 import { ActivatedRoute, Router } from '@angular/router';
-import { IonAvatar, IonButton, IonButtons, IonCard, IonCardContent, IonCardHeader, IonCardSubtitle, IonCardTitle, IonContent, IonHeader, IonInput, IonItem, IonLabel, IonList, IonTitle, IonToolbar } from '@ionic/angular/standalone';
+import {
+  IonAvatar,
+  IonButton,
+  IonButtons,
+  IonCard,
+  IonCardContent,
+  IonCardHeader,
+  IonCardSubtitle,
+  IonCardTitle,
+  IonContent,
+  IonHeader,
+  IonInput,
+  IonItem,
+  IonLabel,
+  IonList,
+  IonTitle,
+  IonToolbar,
+} from '@ionic/angular/standalone';
 import { IonBackButton } from '@ionic/angular/standalone';
 import { IonIcon } from '@ionic/angular/standalone';
 import { CommonModule } from '@angular/common';
@@ -12,42 +29,61 @@ import { FormsModule } from '@angular/forms';
   templateUrl: './post-page.component.html',
   styleUrls: ['./post-page.component.scss'],
   standalone: true,
-  imports: [IonHeader,IonToolbar,IonButtons,IonBackButton,IonTitle,IonContent,
-    IonCard,IonCardHeader,IonItem,IonAvatar,IonCardTitle,IonCardSubtitle,IonCardContent,
-    IonButton,IonIcon,IonItem,IonInput,IonList,IonLabel,FormsModule,CommonModule
-    ],
+  imports: [
+    IonHeader,
+    IonToolbar,
+    IonButtons,
+    IonBackButton,
+    IonTitle,
+    IonContent,
+    IonCard,
+    IonCardHeader,
+    IonItem,
+    IonAvatar,
+    IonCardTitle,
+    IonCardSubtitle,
+    IonCardContent,
+    IonButton,
+    IonIcon,
+    IonItem,
+    IonInput,
+    IonList,
+    IonLabel,
+    FormsModule,
+    CommonModule,
+  ],
 })
-export class PostPageComponent  implements OnInit {
+export class PostPageComponent implements OnInit {
   post: any;
   liked: boolean = false;
   newComment: string = '';
   comments: { username: string; text: string }[] = [];
   postId: any;
 
-  constructor(private router: Router,private route: ActivatedRoute,private postService: PostService) {}
+  constructor(
+    private router: Router,
+    private route: ActivatedRoute,
+    private postService: PostService
+  ) {}
 
   ngOnInit() {
     // this.loadDaata();
     // console.log(this.post);
-     // 使用 route.params 订阅参数变化
-      // 获取动态 ID
-      this.route.params.subscribe(params => {
-        const postId = Number(params['id']); // 获取动态 ID
-        if (!isNaN(postId)) {
-          this.post = this.postService.getPostById(postId); // 根据 ID 获取动态内容
-        } else {
-          console.error('Invalid post ID');
-        }
-      });
-  
-      // 这里可以添加逻辑来根据 postId 更新页面内容
+    // 使用 route.params 订阅参数变化
+    // 获取动态 ID
+    this.route.params.subscribe((params) => {
+      const postId = Number(params['id']); // 获取动态 ID
+      if (!isNaN(postId)) {
+        this.post = this.postService.getPostById(postId); // 根据 ID 获取动态内容
+      } else {
+        console.error('Invalid post ID');
+      }
+    });
 
-    
+    // 这里可以添加逻辑来根据 postId 更新页面内容
 
     // const postId = Number(this.route.snapshot.paramMap.get('id')); // 获取动态 ID
     // 根据 ID 获取动态内容
-    
-     
   }
   // ngDoCheck(){
   //   this.loadDaata();
@@ -83,5 +119,4 @@ export class PostPageComponent  implements OnInit {
       this.newComment = ''; // 清空输入框
     }
   }
-
 }

+ 27 - 40
TFPower-app/src/app/tab1/tab1.page.html

@@ -26,59 +26,46 @@
             <ion-label><a (click)="navigateTo('aichat')">AIChat</a></ion-label>
           </ion-item>
         </ion-list>
-        <!-- <ion-button>Go to TodolistPage </ion-button> -->
-
-
-        <!-- todolist组件位置测试 -->
-        <!-- <app-todo-edit (todolistChange)="changeTodolist($event)"></app-todo-edit> -->
-        <!-- todolist -->
-        <!-- @if(todolist.length>0){
-        <ion-card color="light">
-          <ion-list [inset]="true">
-            @for (todo of todolist; track todo) {
-            <ion-item>
-              <ion-checkbox slot="start" aria-label="Toggle task completion"></ion-checkbox>
-              <ion-input aria-label="Task name" value="{{todo}}" disabled></ion-input>
-              <span (click)="deleteThisTodolist(todo)" class="text-red">X</span>
-            </ion-item>
-            }
-          </ion-list>
-        </ion-card>
-        } -->
 
       </ion-content>
     </ion-menu>
 
-
+    <!-- 主体部分 -->
     <div class="ion-page" id="main-content">
-
       <!-- 标题《渴望力量》 -->
-      <ion-header>
+      <!-- <ion-header>
         <ion-toolbar>
           <ion-buttons slot="start">
             <ion-menu-button></ion-menu-button>
           </ion-buttons>
           <ion-title>渴望力量</ion-title>
         </ion-toolbar>
-      </ion-header>
-      <ion-content class="ion-padding">
+      </ion-header> -->
+      <!-- <ion-searchbar animated="true" placeholder="搜索"></ion-searchbar> -->
+      <ion-searchbar animated="true" placeholder="搜索"></ion-searchbar>
 
-        <!-- todolist组件测试 -->
-        <!-- <app-todo-edit (todolistChange)="changeTodolist($event)"></app-todo-edit> -->
-        <!-- todolist -->
-        <!-- @if(todolist.length>0){
-        <ion-card color="light">
-          <ion-list [inset]="true">
-            @for (todo of todolist; track todo) {
-            <ion-item>
-              <ion-checkbox slot="start" aria-label="Toggle task completion"></ion-checkbox>
-              <ion-input aria-label="Task name" value="{{todo}}" disabled></ion-input>
-              <span (click)="deleteThisTodolist(todo)" class="text-red">X</span>
-            </ion-item>
-            }
-          </ion-list>
-        </ion-card>
-        } -->
+      <!-- 横向导航栏 -->
+      <div class="horizontal-nav"></div>
+
+      <!-- 添加按钮 -->
+      <app-draggable-button></app-draggable-button>
+      <!-- <button (click)="test()">test</button> -->
+
+      <!-- 主体 -->
+      <ion-content class="ion-padding">
+        <div *ngIf="!is_loading">
+          <ion-card *ngFor="let news of newslists" (click)="openModal(news)">
+            <img [src]="news.illustration" />
+            <ion-card-header>
+              <ion-card-title>{{news.title}}</ion-card-title>
+              <ion-card-subtitle>{{news.column}}</ion-card-subtitle>
+            </ion-card-header>
+            <ion-card-content>
+              <div [innerHTML]="news.article" class="news-description"></div>
+            </ion-card-content>
+          </ion-card>
+
+        </div>
 
         <!-- 第一张卡片 -->
         <ion-card class="bigcard">

+ 41 - 0
TFPower-app/src/app/tab1/tab1.page.scss

@@ -33,6 +33,47 @@ p {
   text-indent: 2em;
 }
 
+.news-description {
+  text-indent: 2em; /* 段落首行缩进 */
+  display: -webkit-box; /* 将元素设置为弹性盒子 */
+  -webkit-box-orient: vertical; /* 设置盒子的排列方向为垂直方向 */
+  overflow: hidden; /* 超出部分隐藏 */
+  -webkit-line-clamp: 4; /* 限制显示 8 行 */
+  text-overflow: ellipsis; /* 超出部分用省略号显示 */
+  white-space: normal; /* 允许多行文本 */
+}
+
 .no-indent {
   text-indent: 0;
 }
+
+.horizontal-nav {
+  background-color: #fff;
+  display: flex;
+  width: auto;
+  overflow-x: auto; /* 允许横向滚动 */
+  padding: 0 0;
+  white-space: nowrap; /* 防止换行 */
+  margin-top: -7px;
+  overflow-x: auto;
+  -ms-overflow-style: none; /* 适用于IE */
+  scrollbar-width: none; /* 适用于Firefox */
+  transition: transform 0.3s ease;
+}
+
+.nav-item {
+  padding: 10px 15px;
+  background-color: #fff;
+  color: black;
+  border: none;
+  font-size: 16px;
+  cursor: pointer;
+  white-space: nowrap;
+  min-width: 90px;
+  text-align: center;
+  transition: background-color 0.3s;
+}
+
+.nav-item:active {
+  background-color: #a9a9a9;
+}

+ 111 - 6
TFPower-app/src/app/tab1/tab1.page.ts

@@ -1,4 +1,4 @@
-import { Component, OnInit } from '@angular/core';
+import { Component, ElementRef, OnInit, Renderer2 } from '@angular/core';
 import {
   IonHeader,
   IonToolbar,
@@ -25,12 +25,23 @@ import {
   IonInput,
   IonNavLink,
   IonButton,
+  IonSearchbar,
+  IonSegment,
+  IonSegmentButton,
+  ModalController,
+  IonFab,
+  IonFabButton,
+  IonIcon,
 } from '@ionic/angular/standalone';
 import { ExploreContainerComponent } from '../explore-container/explore-container.component';
 import { TodoEditComponent } from '../component/todo-edit/todo-edit.component';
 import { TodolistPageComponent } from '../page/todolist-page/todolist-page.component';
-import { Route, Router } from '@angular/router';
+import { Router } from '@angular/router';
 import { AiplanPageComponent } from '../page/aiplan-page/aiplan-page.component';
+import { CloudQuery, CloudNews, CloudUser } from 'src/lib/cyxncloud';
+import { CommonModule } from '@angular/common';
+import { ModalBrowsePagePage } from '../page/modal/modal-browse-page/modal-browse-page.page';
+import { DraggableButtonComponent } from '../component/draggable-button/draggable-button.component';
 
 @Component({
   selector: 'app-tab1',
@@ -66,10 +77,31 @@ import { AiplanPageComponent } from '../page/aiplan-page/aiplan-page.component';
     IonInput,
     IonNavLink,
     AiplanPageComponent,
+    IonSearchbar,
+    CommonModule,
+    IonSegment,
+    IonSegmentButton,
+    IonFab,
+    IonFabButton,
+    IonIcon,
+    DraggableButtonComponent,
   ],
 })
-export class Tab1Page {
-  constructor(private router: Router) {}
+export class Tab1Page implements OnInit {
+  constructor(
+    private router: Router,
+    private el: ElementRef,
+    private renderer: Renderer2,
+    private modalCtrl: ModalController
+  ) {
+    this.getAllNews();
+  }
+
+  ngOnInit() {
+    this.getAllNews();
+    const button = this.el.nativeElement.querySelector('#draggable-button');
+  }
+
   alertButtons = ['打卡'];
   duringday: number = 10;
   goodday: number = 20;
@@ -77,9 +109,52 @@ export class Tab1Page {
   todolist: Array<string> = ['have a good day'];
   sexChecked: boolean = false;
 
+  // 查看页面详细信息
+  async openModal(news: any) {
+    const modal = await this.modalCtrl.create({
+      component: ModalBrowsePagePage,
+      componentProps: {
+        newsData: news, // 将news数据传递给子组件
+      },
+    });
+    modal.present();
+  }
+
+  // 横向导航栏
+  navItems = [
+    { label: '首页', link: '/home' },
+    { label: '健康·生活', link: '/health' },
+    { label: '社会', link: '/society' },
+    { label: '法制', link: '/legal' },
+    { label: '教育', link: '/education' },
+    { label: '科普', link: '/science' },
+    { label: '体育', link: '/physical' },
+    { label: '文旅', link: '/cultural' },
+  ];
+  ngAfterViewInit() {
+    // 在视图初始化之后动态生成导航按钮
+    const navContainer = this.el.nativeElement.querySelector('.horizontal-nav');
+
+    for (let i = 0; i < this.navItems.length; i++) {
+      const item = this.navItems[i];
+
+      const button = this.renderer.createElement('button');
+      const text = this.renderer.createText(item.label);
+
+      this.renderer.appendChild(button, text);
+
+      this.renderer.addClass(button, 'nav-item');
+      this.renderer.listen(button, 'click', () => this.navigateTo(item.link));
+
+      this.renderer.appendChild(navContainer, button);
+    }
+  }
+
+  // 跳转页面
   navigateTo(path: string) {
     this.router.navigate([`/tabs/${path}`]);
   }
+
   goodbody() {
     let allChange = document.querySelector('#allChange');
     if (allChange) {
@@ -107,6 +182,36 @@ export class Tab1Page {
   }
 
   // 首页新闻
-  news: string = 'news';
-  newslists: Array<any> = [];
+  newslists: any[] = [];
+  is_loading: boolean = true;
+
+  async getAllNews() {
+    let className = 'News';
+    let newsQuery = new CloudQuery(className);
+    let newslist = await newsQuery.getAll();
+    this.newslists = newslist.results;
+
+    this.newslists = newslist.results.map((news: any) => {
+      // 删除所有空格,并替换换行符\n\n为</p><p>,并在开始和结束处添加<p>和</p>
+      news.article =
+        '<p>' +
+        news.article.replace(/\n\n/g, '</p><p>').replace(/\s+/g, '') +
+        '</p>';
+      return news;
+    });
+    this.is_loading = false;
+  }
+
+  // test
+  test() {
+    let newsData = {
+      author: new CloudUser().toPointer(),
+      theme: '健康',
+      title: 'hhhh',
+    };
+    console.log('newsData', newsData);
+
+    let news = new CloudNews();
+    let res = news.add(newsData);
+  }
 }

+ 48 - 1
TFPower-app/src/app/tab4/tab4.page.html

@@ -43,10 +43,57 @@
         <ion-button expand="full" (click)="navigateTo('login-page')">登录</ion-button>
         <ion-button expand="full" (click)="navigateTo('register-page')">注册</ion-button>
       </ion-card-content>
-
       }
     </ion-grid>
+  </ion-card>
+  <ion-card>
+    <ion-list>
+      <ion-item>
+        <ion-segment>
+          <ion-segment-button value="first" content-id="first">
+            <ion-label>发布的文章</ion-label>
+          </ion-segment-button>
+          <ion-segment-button value="second" content-id="second">
+            <ion-label>动态</ion-label>
+          </ion-segment-button>
+        </ion-segment>
+      </ion-item>
+      <ion-item>
+        <ion-segment-view>
+          <!-- <ion-card *ngFor="let news of newslists"> -->
+          <ion-segment-content id="first">
+            <ion-card class="showHistory">
+              <img class="showIllustration"
+                src="https://s1.imagehub.cc/images/2024/12/18/8166e90f04a179c424c947e222e37620.jpg" />
+              <ion-avatar class="showAvatar">
+                <img alt="Silhouette of a person's head" src="https://ionicframework.com/docs/img/demos/avatar.svg" />
+              </ion-avatar>
+              <span class="showTitle">hhhhh</span>
+            </ion-card>
+            <ion-card class="showHistory">
+              <img class="showIllustration"
+                src="https://s1.imagehub.cc/images/2024/12/18/8166e90f04a179c424c947e222e37620.jpg" />
+              <ion-avatar class="showAvatar">
+                <img alt="Silhouette of a person's head" src="https://ionicframework.com/docs/img/demos/avatar.svg" />
+              </ion-avatar>
+              <span class="showTitle">hhhhh</span>
+            </ion-card>
+            <ion-card class="showHistory">
+              <img class="showIllustration"
+                src="https://s1.imagehub.cc/images/2024/12/18/8166e90f04a179c424c947e222e37620.jpg" />
+              <ion-avatar class="showAvatar">
+                <img alt="Silhouette of a person's head" src="https://ionicframework.com/docs/img/demos/avatar.svg" />
+              </ion-avatar>
+              <span class="showTitle">hhhhh</span>
+            </ion-card>
+          </ion-segment-content>
+          <ion-segment-content id="second">Second</ion-segment-content>
+        </ion-segment-view>
+      </ion-item>
 
 
+    </ion-list>
   </ion-card>
+
+
 </ion-content>

+ 71 - 0
TFPower-app/src/app/tab4/tab4.page.scss

@@ -14,3 +14,74 @@ ion-avatar {
 .center {
   text-align: center;
 }
+
+// .showHistory {
+//   width: 45%;
+// }
+
+// .showAvatar {
+//   display: inline-block;
+//   width: 40px;
+//   height: 40px;
+//   margin-bottom: 3px;
+//   margin-left: 3px;
+// }
+// .showAvatar img {
+//   vertical-align: -10px;
+//   --border-radius: 50%;
+// }
+// .showTitle {
+//   display: inline-block;
+//   padding-left: 10px;
+//   font-weight: 900;
+//   font-size: 20px;
+// }
+/* 设置容器为两列瀑布流布局 */
+ion-segment-content#first {
+  display: grid; /* 使用 grid 布局 */
+  grid-template-columns: repeat(2, 1fr); /* 定义两列 */
+  gap: 0; /* 设置卡片之间的间距 */
+  padding: 0; /* 容器的内边距 */
+}
+
+/* 每个卡片的样式 */
+.showHistory {
+  background-color: #fff;
+  border-radius: 10px;
+
+  box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
+  overflow: hidden;
+}
+
+/* 卡片中的图片样式 */
+.showIllustration {
+  width: 100%;
+  height: auto;
+  object-fit: cover;
+}
+
+/* 头像的样式 */
+.showAvatar {
+  display: inline-block;
+  width: 40px;
+  height: 40px;
+  margin-bottom: 3px;
+  margin-left: 3px;
+}
+
+.showAvatar img {
+  vertical-align: middle; /* 确保头像垂直对齐 */
+  width: 100%;
+  height: 100%;
+  border-radius: 50%; /* 圆形头像 */
+}
+
+/* 标题的样式 */
+.showTitle {
+  display: inline-block;
+  padding-left: 10px;
+  font-weight: 900;
+  font-size: 20px;
+  vertical-align: middle; /* 确保标题垂直对齐 */
+  line-height: 40px; /* 设置行高与头像一致 */
+}

+ 44 - 35
TFPower-app/src/app/tab4/tab4.page.ts

@@ -1,4 +1,4 @@
-import { Component } from '@angular/core';
+import { Component, OnInit } from '@angular/core';
 import { Router } from '@angular/router';
 import {
   IonHeader,
@@ -13,9 +13,16 @@ import {
   IonAvatar,
   IonButton,
   ModalController,
+  IonItem,
+  IonList,
+  IonSegment,
+  IonSegmentButton,
+  IonLabel,
+  IonSegmentView,
+  IonSegmentContent,
 } from '@ionic/angular/standalone';
-import { CloudUser } from 'src/lib/cyxncloud';
-import { ModalEditPagePage } from '../page/modal-edit-page/modal-edit-page.page';
+import { CloudQuery, CloudUser } from 'src/lib/cyxncloud';
+import { ModalEditPagePage } from '../page/modal/modal-edit-page/modal-edit-page.page';
 export interface User {
   avatar: string;
   username: string;
@@ -46,9 +53,16 @@ export interface User {
     IonAvatar,
     IonCardContent,
     IonButton,
+    IonItem,
+    IonList,
+    IonSegment,
+    IonSegmentButton,
+    IonLabel,
+    IonSegmentView,
+    IonSegmentContent,
   ],
 })
-export class Tab4Page {
+export class Tab4Page implements OnInit {
   is_login: boolean = false;
   currentUser: CloudUser | null = null;
   userChangeInfo: any = '';
@@ -66,6 +80,7 @@ export class Tab4Page {
   };
 
   constructor(private router: Router, private modalCtrl: ModalController) {
+    // this.getAllNews();
     let userCache = localStorage.getItem('userData');
     if (userCache) {
       let userData = JSON.parse(userCache);
@@ -83,6 +98,14 @@ export class Tab4Page {
       this.is_login = is_loginCache === 'true' ? true : false;
     }
   }
+  hasRefreshedKey = 'hasRefreshed'; // 用于在 localStorage 中存储状态
+
+  ngOnInit() {}
+
+  refreshPage() {
+    window.location.reload(); // 强制刷新整个页面
+  }
+
   navigateTo(path: string) {
     this.router.navigate([`/${path}`]);
   }
@@ -92,51 +115,21 @@ export class Tab4Page {
       component: ModalEditPagePage,
     });
     modal.present();
-
     const { data, role } = await modal.onWillDismiss();
-
     if (role === 'confirm') {
-      // this.message = `Hello, ${data}!`;
       console.log(data);
       this.userChangeInfo = data;
       this.updateUser();
     }
   }
-  // async login() {
-  //   this.is_login = true;
-  //   let user = new CloudUser();
-  //   this.currentUser = await user.login('cyx', 'cyx123456');
-  //   if (this.currentUser) {
-  //     console.log(this.currentUser);
-
-  //     this.user.avatar = this.currentUser.data.avatar;
-  //     this.user.username = this.currentUser.data.username;
-  //     this.user.age = this.currentUser.data.age;
-  //     this.user.sex = this.currentUser.data.sex;
-  //     this.user.email = this.currentUser.data.email;
-  //     this.user.phoneNumber = this.currentUser.data.phoneNumber;
-  //     this.user.fans = this.currentUser.data.fans;
-  //     this.user.likes = this.currentUser.data.likes;
-  //     localStorage.setItem('userData', JSON.stringify(this.currentUser.data));
-  //     localStorage.setItem('is_login', 'true');
-  //   }
-  // }
   async logout() {
     this.is_login = false;
     let user = new CloudUser();
     let flag = await user.logout();
-    localStorage.removeItem('user');
+    localStorage.removeItem('userData');
     localStorage.removeItem('is_login');
   }
   async updateUser() {
-    // let userCache = localStorage.getItem('userData');
-    // if (userCache) {
-    //   let userData = JSON.parse(userCache);
-    //   console.log(userData);
-
-    //   this.currentUser = userData;
-    // }
-
     Object.keys(this.userChangeInfo).forEach((key) => {
       if (key == 'age') {
         this.userChangeInfo[key] = Number(this.userChangeInfo[key]);
@@ -152,4 +145,20 @@ export class Tab4Page {
     let res = await user.update(this.userChangeInfo);
     console.log('成功更新用户信息');
   }
+  newslists: any[] = [];
+  // async getAllNews() {
+  //   let className = 'News';
+  //   let newsQuery = new CloudQuery(className);
+  //   let newslist = await newsQuery.getAll();
+  //   this.newslists = newslist.results;
+
+  //   this.newslists = newslist.results.map((news: any) => {
+  //     // 删除所有空格,并替换换行符\n\n为</p><p>,并在开始和结束处添加<p>和</p>
+  //     news.article =
+  //       '<p>' +
+  //       news.article.replace(/\n\n/g, '</p><p>').replace(/\s+/g, '') +
+  //       '</p>';
+  //     return news;
+  //   });
+  // }
 }

+ 100 - 29
TFPower-app/src/lib/cyxncloud.ts

@@ -1,10 +1,16 @@
-import { remove } from 'ionicons/icons';
-
+/**
+ * @desc 查找数据库
+ * @param {string} className:数据库中表的名称
+ */
 export class CloudQuery {
   className = '';
   constructor(className: string) {
     this.className = className;
   }
+
+  /**
+   * @desc 获取数据表中的所有信息,配合CloudQuery()使用
+   */
   async getAll() {
     let response = await fetch(
       'http://1.94.237.145:1338/parsecyx/classes/' + this.className,
@@ -32,9 +38,16 @@ export class CloudQuery {
     return json || [];
   }
 
-  async getBy(id: string) {
+  /**
+   * @desc 获取数据表中单个信息,配合CloudQuery()使用
+   * @param {string} objectId:数据库中表的名称
+   */
+  async getBy(objectId: string) {
     let response = await fetch(
-      'http://1.94.237.145:1338/parsecyx/classes/' + this.className + '/' + id,
+      'http://1.94.237.145:1338/parsecyx/classes/' +
+        this.className +
+        '/' +
+        objectId,
       {
         headers: {
           accept: '*/*',
@@ -61,6 +74,53 @@ export class CloudQuery {
   }
 }
 
+// fetch('http://1.94.237.145:1338/parsecyx/classes/News', {
+//   headers: {
+//     accept: '*/*',
+//     'accept-language':
+//       'zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6,zh-TW;q=0.5,ja;q=0.4',
+//     'content-type': 'text/plain',
+//   },
+//   referrer: 'http://127.0.0.1:4040/',
+//   referrerPolicy: 'strict-origin-when-cross-origin',
+//   body: '{"theme":"SDSS","title":"ASDF","_ApplicationId":"cyx","_ClientVersion":"js3.4.2","_MasterKey":"cyxmk","_InstallationId":"111053ea-8bfc-4dfc-8818-df34dd45d6b0"}',
+//   method: 'POST',
+//   mode: 'cors',
+//   credentials: 'omit',
+// });
+
+export class CloudNews {
+  objectId: string = '';
+  sessionToken: string = '';
+  data: any = {};
+
+  constructor() {
+    let userCacheStr = localStorage.getItem('userData');
+    if (userCacheStr) {
+      let userData = JSON.parse(userCacheStr);
+      // 设置用户信息
+      this.objectId = userData?.objectId;
+      this.sessionToken = userData?.sessionToken;
+      this.data = userData; // 保存用户数据
+    }
+  }
+
+  async add(newsData: any) {
+    let url = 'http://1.94.237.145:1338/parsecyx/classes/News';
+    const response = await fetch(url, {
+      method: 'POST',
+      headers: {
+        accept: '*/*',
+        'X-Parse-Application-Id': 'cyx',
+        'content-type': 'text/plain',
+      },
+      body: JSON.stringify(newsData),
+    });
+    return this;
+  }
+}
+
+// 示例
 // fetch("http://1.94.237.145:1338/parsecyx/classes/_User?", {
 //   headers: {
 //     accept: "*/*",
@@ -78,15 +138,35 @@ export class CloudQuery {
 //   credentials: "omit",
 // });
 
-// user操作
+/**
+ * @desc 对_User表操作,不用传参
+ */
 export class CloudUser {
-  constructor() {}
-
   objectId: string = '';
   sessionToken: string = '';
   data: any = {};
 
-  // 获取当前用户信息
+  constructor() {
+    let userCacheStr = localStorage.getItem('userData');
+    if (userCacheStr) {
+      let userData = JSON.parse(userCacheStr);
+      this.objectId = userData?.objectId;
+      this.sessionToken = userData?.sessionToken;
+      this.data = userData;
+    }
+  }
+
+  toPointer() {
+    return {
+      __type: 'Pointer',
+      className: '_User',
+      objectId: this.objectId,
+    };
+  }
+
+  /**
+   * @desc 获取当前用户信息
+   */
   async current() {
     let userCache = localStorage.getItem('userData');
     if (userCache) {
@@ -96,7 +176,9 @@ export class CloudUser {
     }
   }
 
-  // 用户登录
+  /**
+   * @desc 用户登录函数
+   */
   async login(username: string, password: string): Promise<CloudUser | null> {
     let url = `http://1.94.237.145:1338/parsecyx/login`;
     const response = await fetch(url, {
@@ -120,7 +202,9 @@ export class CloudUser {
     return this;
   }
 
-  // 用户登出
+  /**
+   * @desc 用户登出函数
+   */
   async logout(): Promise<boolean> {
     let url = 'http://1.94.237.145:1338/parsecyx/logout';
     const response = await fetch(url, {
@@ -137,7 +221,9 @@ export class CloudUser {
     return true;
   }
 
-  // 用户注册
+  /**
+   * @desc 用户注册
+   */
   async register(username: string, password: string) {
     let url = 'http://1.94.237.145:1338/parsecyx/users';
     let response = await fetch(url, {
@@ -155,10 +241,10 @@ export class CloudUser {
     return this;
   }
 
-  // 修改用户信息
+  /**
+   * @desc 修改用户信息
+   */
   async update(userChangeInfo: any) {
-    console.log(userChangeInfo);
-
     this.current();
     let url =
       `http://1.94.237.145:1338/parsecyx/classes/_User/` + this.objectId;
@@ -178,20 +264,5 @@ export class CloudUser {
     return this;
   }
 }
-
-// fetch('http://1.94.237.145:1338/parsecyx/classes/_User/WBhghn8NuZ', {
-//   headers: {
-//     accept: '*/*',
-//     'accept-language':
-//       'zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6,zh-TW;q=0.5,ja;q=0.4',
-//     'content-type': 'text/plain',
-//   },
-//   referrer: 'http://127.0.0.1:4040/',
-//   referrerPolicy: 'strict-origin-when-cross-origin',
-//   body: '{"phoneNumber":"12345","_method":"PUT","_ApplicationId":"cyx","_ClientVersion":"js3.4.2","_MasterKey":"cyxmk","_InstallationId":"111053ea-8bfc-4dfc-8818-df34dd45d6b0"}',
-//   method: 'POST',
-//   mode: 'cors',
-//   credentials: 'omit',
-// });
 // module.exports.Query = Query;
 // module.exports.CloudUser = CloudUser;

+ 0 - 0
TFPower-server/migration/lib/test/ncloud.js


+ 0 - 0
TFPower-server/migration/lib/test/test.js