yf vor 3 Monaten
Ursprung
Commit
e492087e7b

+ 162 - 0
wisdom-app/src/app/tab2/tab2.page.html

@@ -0,0 +1,162 @@
+<ion-header [translucent]="true">
+  <ion-toolbar class="custom-toolbar">
+    <div class="search-bar">
+      <ion-searchbar
+        placeholder="搜索"
+        class="custom-searchbar"
+        (ionInput)="searchProducts($event)">
+      </ion-searchbar>
+    </div>
+    <ng-container *ngIf="!searchTerm">
+      <div class="header">
+        <ion-card-header>
+          <ion-card-title>
+            <ion-segment
+              [(ngModel)]="selectedSegment"
+              (ionChange)="segmentChanged($event)"
+              scrollable>
+              <ion-segment-button
+                *ngFor="let segment of segments"
+                [value]="segment">
+                {{ segment }}
+              </ion-segment-button>
+            </ion-segment>
+          </ion-card-title>
+        </ion-card-header>
+      </div>
+    </ng-container>
+  </ion-toolbar>
+</ion-header>
+
+<ion-content class="knowledge" [fullscreen]="true">
+  <ion-loading
+    *ngIf="isLoading"
+    message="加载中...">
+  </ion-loading>
+
+<ion-content class="knowledge" [fullscreen]="true">
+  <ng-container *ngIf="!searchTerm; else searchResults">
+    <div class="content">
+      <ion-card>
+        <ion-card-content>
+          <!-- 轮播图区域 -->
+          <div class="carousel-container">
+            <div class="carousel" [style.transform]="'translateX(-' + currentSlide * 100 + '%)'">
+              <div class="slide" *ngFor="let image of images; trackBy: trackByIndex">
+                <img [src]="image" alt="轮播图" />
+              </div>
+            </div>
+            <button class="prev" (click)="prevSlide()">&#10094;</button>
+            <button class="next" (click)="nextSlide()">&#10095;</button>
+            <div class="dots">
+              <span
+                class="dot"
+                *ngFor="let image of images; let i = index"
+                [class.active]="i === currentSlide"
+                (click)="goToSlide(i)">
+              </span>
+            </div>
+          </div>
+          <!-- 文章卡片区域 -->
+          <app-article-card
+            *ngFor="let card of cards; trackBy: trackById"
+            [card]="card"
+            (click)="openDetailModal(card)">
+          </app-article-card>
+        </ion-card-content>
+      </ion-card>
+    </div>
+  </ng-container>
+
+  <!-- 搜索结果 -->
+  <ng-template #searchResults>
+    <div>
+      <app-article-card
+        *ngFor="let product of products; trackBy: trackById"
+        [card]="product"
+        (click)="openDetailModal(product)">
+      </app-article-card>
+      <div *ngIf="products.length === 0" class="no-results">
+        <h2>寻找中··· 请耐性等待 ····</h2>
+      </div>
+    </div>
+  </ng-template>
+
+  <!-- 详细信息模态 -->
+  <ion-modal [isOpen]="isModalOpen" cssClass="bottom-modal">
+    <ng-template>
+      <ion-header>
+        <ion-toolbar>
+          <ion-buttons slot="start">
+            <ion-button (click)="closeDetailModal()">关闭</ion-button>
+          </ion-buttons>
+          <ion-title>{{ currentProduct?.get('category') }}</ion-title>
+          <ion-buttons slot="end">
+            <ion-button (click)="shareDetailModal()">分享</ion-button>
+          </ion-buttons>
+        </ion-toolbar>
+      </ion-header>
+
+      <ion-content class="ion-padding">
+        <div class="modal-content" *ngIf="currentProduct">
+          <h1 class="product-name">{{ currentProduct.get('title') }}</h1>
+          <p><strong>作者:</strong>{{ currentProduct.get('author') }}</p>
+          <p>{{ currentProduct.get('content')[0] }}</p>
+          <div class="image-container">
+            <img [src]="currentProduct.get('image')[0]" alt="图片" class="medicine-image" />
+          </div>
+          <p>{{ currentProduct.get('content')[1] }}</p>
+          <div class="image-container">
+            <img [src]="currentProduct.get('image')[1]" alt="图片" class="medicine-image" />
+          </div>
+          <div class="stats">
+            <p><strong>阅读量:</strong>{{ currentProduct.get('views') }}</p>
+            <p><strong>点赞量:</strong>{{ currentProduct.get('likes') }}</p>
+          </div>
+        </div>
+      </ion-content>
+
+      <ion-footer>
+        <ion-item>
+          <ion-input
+            [(ngModel)]="comment"
+            (ionInput)="onCommentInput($event)"
+            placeholder="请输入评论">
+          </ion-input>
+          <ion-button (click)="postComment()">发布</ion-button>
+        </ion-item>
+      </ion-footer>
+    </ng-template>
+  </ion-modal>
+
+  <!-- 分享模态 -->
+  <ion-modal class="share-modal" [isOpen]="shareDetail">
+    <ng-template>
+      <ion-header>
+        <ion-toolbar>
+          <ion-buttons slot="end">
+            <ion-button (click)="closeShareModal()">X</ion-button>
+          </ion-buttons>
+          <ion-title>分享</ion-title>
+        </ion-toolbar>
+      </ion-header>
+      <ion-content>
+        <ion-item>
+          <ion-label position="stacked">分享链接</ion-label>
+          <ion-input [value]="shareLink" readonly></ion-input>
+        </ion-item>
+        <ion-button expand="block" (click)="copyLink()">复制链接</ion-button>
+      </ion-content>
+      <ion-footer>
+        <ion-item>
+          <ion-input
+            [(ngModel)]="comment"
+            (ionInput)="onCommentInput($event)"
+            placeholder="请输入评论">
+          </ion-input>
+          <ion-button (click)="postComment()">发布</ion-button>
+        </ion-item>
+      </ion-footer>
+    </ng-template>
+  </ion-modal>
+</ion-content>

+ 163 - 0
wisdom-app/src/app/tab2/tab2.page.scss

@@ -0,0 +1,163 @@
+.custom-toolbar {
+  --background: rgba(255, 255, 255, 0.8);
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  padding: 0;
+}
+
+.search-bar {
+  padding: 10px;
+  width: 100%;
+}
+
+.custom-searchbar {
+  --background: #ffffff;
+  --border-radius: 20px;
+  box-shadow: 0 2px 6px rgba(0, 0, 0, 0.1);
+}
+
+.header {
+  height: 80px;
+  margin-top: -10px;
+}
+
+.knowledge {
+  height: 100%;
+  width: 100%;
+}
+
+.content {
+  margin-top: -5px;
+  width: 100%;
+}
+
+ion-card {
+  width: 100%;
+  height: 100%;
+  margin: 0;
+  padding: 0;
+  border-radius: 0;
+  box-shadow: none;
+}
+
+ion-card-content {
+  font-size: 1.2em;
+  width: 100%;
+  height: auto;
+}
+
+.carousel-container {
+  position: relative;
+  max-width: 800px;
+  margin: 0 auto;
+  overflow: hidden;
+  border-radius: 8px;
+}
+
+.carousel {
+  display: flex;
+  transition: transform 0.5s ease-in-out;
+}
+
+.slide {
+  min-width: 100%;
+}
+
+.slide img {
+  width: 100%;
+  height: auto;
+  border-radius: 8px;
+}
+
+.prev,
+.next {
+  position: absolute;
+  top: 50%;
+  transform: translateY(-50%);
+  background: rgba(0, 0, 0, 0.5);
+  color: white;
+  padding: 12px;
+  border: none;
+  cursor: pointer;
+  border-radius: 50%;
+}
+
+.prev {
+  left: 10px;
+}
+
+.next {
+  right: 10px;
+}
+
+.dots {
+  position: absolute;
+  bottom: 15px;
+  left: 50%;
+  transform: translateX(-50%);
+  display: flex;
+  gap: 8px;
+}
+
+.dot {
+  width: 12px;
+  height: 12px;
+  background: #bbb;
+  border-radius: 50%;
+  cursor: pointer;
+}
+
+.dot.active {
+  background: #717171;
+}
+
+.no-results {
+  text-align: center;
+  margin: 50px auto;
+}
+
+.bottom-modal {
+  --height: 100vh;
+  --width: 100%;
+  --offset-y: 0;
+}
+
+.share-modal {
+  --height: 30vh;
+  --width: 100%;
+  --offset-y: 0;
+}
+
+.modal-content {
+  padding: 20px;
+}
+
+.image-container {
+  display: flex;
+  justify-content: center;
+  margin-bottom: 16px;
+}
+
+.medicine-image {
+  width: 100%;
+  height: auto;
+  object-fit: cover;
+  border-radius: 8px;
+}
+
+.stats {
+  display: flex;
+  justify-content: space-between;
+  margin: 20px 30px;
+}
+
+ion-footer ion-item {
+  display: flex;
+  align-items: center;
+}
+
+ion-footer ion-input {
+  flex: 1;
+  margin-right: 10px;
+}

+ 18 - 0
wisdom-app/src/app/tab2/tab2.page.spec.ts

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

+ 219 - 0
wisdom-app/src/app/tab2/tab2.page.ts

@@ -0,0 +1,219 @@
+import { Component, OnInit, OnDestroy } from '@angular/core';
+import { Router } from '@angular/router';
+import { ModalController } from '@ionic/angular'; // 确保导入 ModalController
+import { ArticleCardComponent } from '../component/article-card/article-card.component';
+import { CommonModule } from '@angular/common';
+import { CloudObject, CloudQuery, CloudUser } from 'src/lib/ncloud';
+import { FormsModule } from '@angular/forms';
+import {
+  airplane,
+  bluetooth,
+  call,
+  wifi,
+  chevronDownCircle,
+  chevronForwardCircle,
+  chevronUpCircle,
+  colorPalette,
+  document,
+  globe
+} from 'ionicons/icons';
+import { addIcons } from 'ionicons';
+import { IonicModule } from '@ionic/angular'; // 导入 IonicModule
+
+addIcons({
+  airplane,
+  bluetooth,
+  call,
+  wifi,
+  chevronDownCircle,
+  chevronForwardCircle,
+  chevronUpCircle,
+  colorPalette,
+  document,
+  globe
+});
+
+@Component({
+  selector: 'app-tab2',
+  templateUrl: 'tab2.page.html',
+  styleUrls: ['tab2.page.scss'],
+  standalone: true,
+  imports: [
+    IonicModule, // 添加 IonicModule
+    CommonModule,
+    FormsModule,
+    ArticleCardComponent,
+    // 其他自定义组件或模块
+  ]
+})
+export class Tab2Page implements OnInit, OnDestroy {
+  selectedSegment: string = '热点';
+  segments: string[] = ['热点', '专家科普', '睡眠', '生活', '男性', '女性', '两性', '辟谣', '母婴', '美容'];
+
+  images: string[] = [
+    'https://picsum.photos/800/400?random=7',
+    'https://picsum.photos/800/400?random=8',
+    'https://picsum.photos/800/400?random=9',
+    'https://picsum.photos/800/400?random=10',
+    'https://picsum.photos/800/400?random=11',
+    'https://picsum.photos/800/400?random=12',
+  ];
+
+  currentSlide: number = 0;
+  intervalId: any;
+
+  products: CloudObject[] = [];
+  allCards: CloudObject[] = [];
+  cards: CloudObject[] = [];
+
+  searchTerm: string = '';
+  comment: string = '';
+  shareLink: string = '';
+
+  isModalOpen: boolean = false;
+  currentProduct: CloudObject | null = null;
+
+  shareDetail: boolean = false;
+
+  isLoading: boolean = false;
+
+  constructor(
+    private modalCtrl: ModalController,
+    private router: Router
+  ) { }
+
+  ngOnInit() {
+    this.loadAllCards();
+    this.loadCards();
+    this.startAutoSlide();
+  }
+
+  ngOnDestroy() {
+    if (this.intervalId) {
+      clearInterval(this.intervalId);
+    }
+  }
+
+  // 轮播图功能
+  nextSlide() {
+    this.currentSlide = (this.currentSlide + 1) % this.images.length;
+  }
+
+  prevSlide() {
+    this.currentSlide = (this.currentSlide - 1 + this.images.length) % this.images.length;
+  }
+
+  goToSlide(index: number) {
+    this.currentSlide = index;
+  }
+
+  startAutoSlide() {
+    this.intervalId = setInterval(() => this.nextSlide(), 3000);
+  }
+
+  // 搜索功能
+  async searchProducts(event: any) {
+    this.searchTerm = event.detail.value.toLowerCase();
+    if (this.searchTerm) {
+      this.products = this.allCards.filter(product =>
+        product.get('topic').toLowerCase().includes(this.searchTerm) ||
+        product.get('title').toLowerCase().includes(this.searchTerm) ||
+        product.get('category').toLowerCase().includes(this.searchTerm) ||
+        product.get('content')[0].toLowerCase().includes(this.searchTerm)
+      );
+    } else {
+      this.products = [...this.allCards];
+    }
+  }
+
+  // 分段控制
+  segmentChanged(event: any) {
+    this.selectedSegment = event.detail.value;
+    this.loadCards();
+  }
+
+  // 加载所有卡片
+  async loadAllCards() {
+    this.isLoading = true;
+    const query = new CloudQuery('HotDot');
+    try {
+      this.allCards = await query.find();
+      this.products = [...this.allCards];
+    } catch (error) {
+      console.error('加载所有卡片失败:', error);
+    } finally {
+      this.isLoading = false;
+    }
+  }
+
+  async loadCards() {
+    this.isLoading = true;
+    const query = new CloudQuery('HotDot');
+    query.equalTo('category', this.selectedSegment);
+    try {
+      this.cards = await query.find();
+    } catch (error) {
+      console.error('加载分类卡片失败:', error);
+    } finally {
+      this.isLoading = false;
+    }
+  }
+
+  // 打开详细信息模态
+  openDetailModal(product: CloudObject) {
+    const user = new CloudUser();
+    if (!user.id) {
+      this.router.navigate(['/tabs/tab4']);
+      return;
+    }
+    this.isModalOpen = true;
+    this.currentProduct = product;
+  }
+
+  closeDetailModal() {
+    this.isModalOpen = false;
+    this.currentProduct = null;
+  }
+
+  // 分享功能
+  shareDetailModal() {
+    this.shareDetail = true;
+    this.shareLink = window.location.href; // 示例分享链接,可以根据需求调整
+  }
+
+  closeShareModal() {
+    this.shareDetail = false;
+  }
+
+  copyLink() {
+    navigator.clipboard.writeText(this.shareLink).then(() => {
+      console.log('链接已复制');
+      // 可以添加提示用户复制成功的反馈
+    }).catch(err => {
+      console.error('复制失败:', err);
+    });
+  }
+
+  // 评论功能
+  onCommentInput(event: any) {
+    this.comment = event.detail.value;
+  }
+
+  postComment() {
+    if (this.comment.trim()) {
+      // 实现发布评论的逻辑,例如调用 API
+      console.log('发布评论:', this.comment);
+      this.comment = '';
+    }
+  }
+
+  // 跟踪函数以优化 ngFor
+  trackByIndex(index: number, item: any): number {
+    return index;
+  }
+
+  trackById(index: number, item: CloudObject): string {
+    return item.id ?? `no-id-${index}`; // 确保返回 string 类型,避免 null
+  }
+
+}