소스 검색

社区样式

15270821319 6 달 전
부모
커밋
97075825f5

+ 4 - 0
AiStudy-app/src/app/app.routes.ts

@@ -63,5 +63,9 @@ export const routes: Routes = [
   {
     path: 'daily-plan',
     loadComponent: () => import('./pages/daily-plan/daily-plan.page').then(m => m.DailyPlanPage)
+  },
+  {
+    path: 'tabs',
+    loadChildren: () => import('./tabs/tabs.routes').then((m) => m.routes),
   }
 ];

+ 433 - 0
AiStudy-app/src/app/components/create-post-modal/create-post-modal.component.ts

@@ -0,0 +1,433 @@
+import { Component, ViewChild } from '@angular/core';
+import { CommonModule } from '@angular/common';
+import { FormsModule } from '@angular/forms';
+import {
+  IonHeader,
+  IonToolbar,
+  IonTitle,
+  IonContent,
+  IonButtons,
+  IonButton,
+  IonTextarea,
+  IonIcon,
+  IonItem,
+  IonLabel,
+  ModalController,
+  IonChip,
+  ActionSheetController,
+  IonActionSheet,
+  IonInput,
+  IonPopover,
+  IonList,
+  IonRow,
+  IonCol,
+  IonGrid
+} from '@ionic/angular/standalone';
+import { addIcons } from 'ionicons';
+import { close, image, send, checkmark, addCircleOutline, happyOutline } from 'ionicons/icons';
+import { Camera, CameraResultType, CameraSource } from '@capacitor/camera';
+
+@Component({
+  selector: 'app-create-post-modal',
+  template: `
+    <ion-header>
+      <ion-toolbar>
+        <ion-title>发布帖子</ion-title>
+        <ion-buttons slot="end">
+          <ion-button (click)="dismiss()">
+            <ion-icon name="close"></ion-icon>
+          </ion-button>
+        </ion-buttons>
+      </ion-toolbar>
+    </ion-header>
+
+    <ion-content class="ion-padding">
+      <ion-item>
+        <ion-textarea
+          #textArea
+          [(ngModel)]="postContent"
+          placeholder="分享你的学习心得..."
+          [autoGrow]="true"
+          rows="6"
+        ></ion-textarea>
+        <!-- 表情按钮 -->
+        <ion-button fill="clear" slot="end" (click)="openEmojiPicker($event)">
+          <ion-icon name="happy-outline"></ion-icon>
+        </ion-button>
+      </ion-item>
+
+      <!-- 标签选择区域 -->
+      <div class="tags-section">
+        <div class="section-title">选择标签</div>
+        <div class="tags-container">
+          <ion-chip 
+            *ngFor="let tag of availableTags" 
+            [color]="selectedTags.includes(tag) ? 'primary' : 'medium'"
+            [outline]="!selectedTags.includes(tag)"
+            (click)="toggleTag(tag)"
+          >
+            <ion-label>{{tag}}</ion-label>
+            <ion-icon 
+              *ngIf="selectedTags.includes(tag)" 
+              name="checkmark"
+            ></ion-icon>
+          </ion-chip>
+          
+          <!-- 添加自定义标签按钮 -->
+          <ion-chip outline (click)="showCustomTagInput = true" *ngIf="!showCustomTagInput">
+            <ion-icon name="add-circle-outline"></ion-icon>
+            <ion-label>自定义标签</ion-label>
+          </ion-chip>
+        </div>
+
+        <!-- 自定义标签输入框 -->
+        <div class="custom-tag-input" *ngIf="showCustomTagInput">
+          <ion-item>
+            <ion-input
+              [(ngModel)]="customTagText"
+              placeholder="输入标签名称(不超过10个字)"
+              maxlength="10"
+              (keyup.enter)="addCustomTag()"
+            ></ion-input>
+            <ion-button slot="end" fill="clear" (click)="addCustomTag()">
+              添加
+            </ion-button>
+            <ion-button slot="end" fill="clear" (click)="showCustomTagInput = false">
+              取消
+            </ion-button>
+          </ion-item>
+        </div>
+      </div>
+
+      <!-- 已选择的图片预览 -->
+      <div class="image-preview" *ngIf="selectedImages.length > 0">
+        <div class="preview-item" *ngFor="let img of selectedImages; let i = index">
+          <img [src]="img" alt="preview">
+          <ion-button fill="clear" class="delete-btn" (click)="removeImage(i)">
+            <ion-icon name="close"></ion-icon>
+          </ion-button>
+        </div>
+      </div>
+
+      <!-- 表情选择器弹出框 -->
+      <ion-popover
+        #emojiPopover
+        [isOpen]="showEmojiPicker"
+        (didDismiss)="showEmojiPicker = false"
+        [arrow]="false"
+        size="auto"
+      >
+        <ng-template>
+          <ion-content class="emoji-picker">
+            <ion-grid>
+              <ion-row>
+                <ion-col size="2" *ngFor="let emoji of emojis">
+                  <div class="emoji-item" (click)="selectEmoji(emoji)">
+                    {{emoji}}
+                  </div>
+                </ion-col>
+              </ion-row>
+            </ion-grid>
+          </ion-content>
+        </ng-template>
+      </ion-popover>
+
+      <div class="action-buttons">
+        <!-- 添加图片按钮 -->
+        <ion-button fill="clear" (click)="addImage()" [disabled]="selectedImages.length >= 9">
+          <ion-icon name="image" slot="start"></ion-icon>
+          添加图片
+        </ion-button>
+
+        <!-- 发布按钮 -->
+        <ion-button (click)="publish()" [disabled]="!canPublish">
+          <ion-icon name="send" slot="start"></ion-icon>
+          发布
+        </ion-button>
+      </div>
+    </ion-content>
+  `,
+  styles: [`
+    .image-preview {
+      display: grid;
+      grid-template-columns: repeat(3, 1fr);
+      gap: 8px;
+      padding: 16px;
+    }
+
+    .preview-item {
+      position: relative;
+      aspect-ratio: 1;
+      
+      img {
+        width: 100%;
+        height: 100%;
+        object-fit: cover;
+        border-radius: 8px;
+      }
+
+      .delete-btn {
+        position: absolute;
+        top: -8px;
+        right: -8px;
+        --padding-start: 4px;
+        --padding-end: 4px;
+        --padding-top: 4px;
+        --padding-bottom: 4px;
+        margin: 0;
+        
+        ion-icon {
+          font-size: 16px;
+        }
+      }
+    }
+
+    .action-buttons {
+      display: flex;
+      justify-content: space-between;
+      padding: 16px;
+    }
+
+    .tags-section {
+      margin: 16px 0;
+
+      .section-title {
+        font-size: 14px;
+        color: var(--ion-color-medium);
+        margin-bottom: 8px;
+        padding: 0 4px;
+      }
+
+      .tags-container {
+        display: flex;
+        flex-wrap: wrap;
+        gap: 8px;
+        padding: 4px;
+
+        ion-chip {
+          margin: 0;
+          
+          &.selected {
+            --background: var(--ion-color-primary);
+            --color: white;
+          }
+        }
+      }
+    }
+
+    .custom-tag-input {
+      margin-top: 8px;
+      
+      ion-item {
+        --padding-start: 0;
+        --inner-padding-end: 0;
+        
+        ion-button {
+          margin: 0;
+        }
+      }
+    }
+
+    .emoji-picker {
+      width: 280px;
+      max-height: 200px;
+      
+      .emoji-item {
+        font-size: 24px;
+        padding: 8px;
+        text-align: center;
+        cursor: pointer;
+        
+        &:hover {
+          background: var(--ion-color-light);
+          border-radius: 4px;
+        }
+      }
+    }
+  `],
+  standalone: true,
+  imports: [
+    CommonModule,
+    FormsModule,
+    IonHeader,
+    IonToolbar,
+    IonTitle,
+    IonContent,
+    IonButtons,
+    IonButton,
+    IonTextarea,
+    IonIcon,
+    IonItem,
+    IonLabel,
+    IonChip,
+    IonActionSheet,
+    IonInput,
+    IonPopover,
+    IonList,
+    IonRow,
+    IonCol,
+    IonGrid
+  ]
+})
+export class CreatePostModalComponent {
+  postContent = '';
+  selectedImages: string[] = [];
+  selectedTags: string[] = [];
+  showCustomTagInput = false;
+  customTagText = '';
+  showEmojiPicker = false;
+
+  // 文本输入区域引用
+  @ViewChild('textArea') textArea!: IonTextarea;
+  @ViewChild('emojiPopover') emojiPopover!: IonPopover;
+
+  // 预设的表情列表
+  emojis = [
+    '😊', '👍', '🎉', '💪', '📚', '💡', '✨', '🌟',
+    '🚀', '💻', '⭐', '🎯', '✅', '❤️', '🤔', '👨‍💻',
+    '👩‍💻', '🌱', '🎓', '💭', '🔥', '⚡', '🎨', '🎮'
+  ];
+
+  // 预设的标签列表
+  availableTags = [
+    '学习打卡', '编程技术', '心得分享', '项目展示',
+    '求助解惑', 'Angular', 'Ionic', 'TypeScript',
+    '前端开发', '每日进步', '学习方法', '技术讨论'
+  ];
+
+  constructor(
+    private modalCtrl: ModalController,
+    private actionSheetCtrl: ActionSheetController
+  ) {
+    addIcons({ 
+      close, 
+      image, 
+      send, 
+      checkmark, 
+      addCircleOutline,
+      happyOutline 
+    });
+  }
+
+  // 添加自定义标签
+  addCustomTag() {
+    const tag = this.customTagText.trim();
+    if (tag && !this.availableTags.includes(tag) && !this.selectedTags.includes(tag)) {
+      if (this.selectedTags.length < 3) {
+        this.selectedTags.push(tag);
+        this.availableTags.push(tag);
+      }
+    }
+    this.customTagText = '';
+    this.showCustomTagInput = false;
+  }
+
+  // 打开表情选择器
+  openEmojiPicker(event: Event) {
+    this.showEmojiPicker = true;
+    this.emojiPopover.event = event;
+  }
+
+  // 选择表情
+  async selectEmoji(emoji: string) {
+    const textarea = await this.textArea.getInputElement();
+    const start = textarea.selectionStart || 0;
+    const end = textarea.selectionEnd || 0;
+    
+    this.postContent = 
+      this.postContent.substring(0, start) + 
+      emoji + 
+      this.postContent.substring(end);
+    
+    this.showEmojiPicker = false;
+    
+    // 设置光标位置
+    setTimeout(() => {
+      textarea.setSelectionRange(start + emoji.length, start + emoji.length);
+      textarea.focus();
+    }, 0);
+  }
+
+  // 切换标签选择状态
+  toggleTag(tag: string) {
+    const index = this.selectedTags.indexOf(tag);
+    if (index === -1) {
+      if (this.selectedTags.length < 3) { // 最多选择3个标签
+        this.selectedTags.push(tag);
+      }
+    } else {
+      this.selectedTags.splice(index, 1);
+    }
+  }
+
+  // 修改发布条件
+  get canPublish() {
+    return this.postContent.trim().length > 0 && this.selectedTags.length > 0;
+  }
+
+  dismiss() {
+    this.modalCtrl.dismiss();
+  }
+
+  async addImage() {
+    const actionSheet = await this.actionSheetCtrl.create({
+      header: '选择图片来源',
+      buttons: [
+        {
+          text: '从相册选择',
+          handler: () => {
+            this.selectImage(CameraSource.Photos);
+          }
+        },
+        {
+          text: '拍照',
+          handler: () => {
+            this.selectImage(CameraSource.Camera);
+          }
+        },
+        {
+          text: '取消',
+          role: 'cancel'
+        }
+      ]
+    });
+
+    await actionSheet.present();
+  }
+
+  private async selectImage(source: CameraSource) {
+    try {
+      const image = await Camera.getPhoto({
+        quality: 90,
+        allowEditing: false,
+        resultType: CameraResultType.DataUrl,
+        source: source,
+        width: 1024,
+        correctOrientation: true
+      });
+
+      if (image.dataUrl) {
+        this.selectedImages.push(image.dataUrl);
+      }
+    } catch (error) {
+      console.error('选择图片失败:', error);
+    }
+  }
+
+  removeImage(index: number) {
+    this.selectedImages.splice(index, 1);
+  }
+
+  // 修改发布方法,包含标签
+  async publish() {
+    if (!this.canPublish) return;
+
+    const post = {
+      content: this.postContent,
+      images: this.selectedImages,
+      tags: this.selectedTags
+    };
+
+    this.modalCtrl.dismiss(post);
+  }
+} 

+ 107 - 0
AiStudy-app/src/app/tab-community/tab-community.page.html

@@ -0,0 +1,107 @@
+<ion-header [translucent]="true">
+  <ion-toolbar>
+    <ion-title>学习社区</ion-title>
+  </ion-toolbar>
+</ion-header>
+
+<ion-content [fullscreen]="true">
+  <!-- 下拉刷新 -->
+  <ion-refresher slot="fixed" (ionRefresh)="doRefresh($event)">
+    <ion-refresher-content></ion-refresher-content>
+  </ion-refresher>
+
+  <!-- 帖子列表 -->
+  <ion-list>
+    <!-- 加载状态 -->
+    <ion-card *ngIf="isLoading">
+      <ion-card-content>
+        <ion-item lines="none">
+          <ion-avatar slot="start">
+            <ion-skeleton-text [animated]="true"></ion-skeleton-text>
+          </ion-avatar>
+          <ion-label>
+            <h3><ion-skeleton-text [animated]="true" style="width: 50%"></ion-skeleton-text></h3>
+            <p><ion-skeleton-text [animated]="true" style="width: 80%"></ion-skeleton-text></p>
+          </ion-label>
+        </ion-item>
+      </ion-card-content>
+    </ion-card>
+
+    <!-- 帖子内容 -->
+    <ion-card *ngFor="let post of posts">
+      <ion-card-content>
+        <ion-item lines="none" class="author-info">
+          <ion-avatar slot="start">
+            <img [src]="post.author.avatar" alt="avatar">
+          </ion-avatar>
+          <ion-label>
+            <h3>{{post.author.name}}</h3>
+            <p>{{post.createdAt | date:'MM-dd HH:mm'}}</p>
+          </ion-label>
+        </ion-item>
+        
+        <p class="post-content">{{post.content}}</p>
+        
+        <!-- 图片展示区域 -->
+        <div class="post-images" *ngIf="post.images?.length > 0">
+          <!-- 单张图片 -->
+          <div class="single-image" *ngIf="post.images.length === 1">
+            <img [src]="post.images[0]" (click)="viewImage(post.images[0])">
+          </div>
+          
+          <!-- 两张图片 -->
+          <div class="double-images" *ngIf="post.images.length === 2">
+            <img *ngFor="let img of post.images" 
+                 [src]="img" 
+                 (click)="viewImage(img)">
+          </div>
+          
+          <!-- 三张及以上图片 -->
+          <div class="multiple-images" *ngIf="post.images.length >= 3">
+            <img *ngFor="let img of post.images.slice(0, 9)" 
+                 [src]="img" 
+                 (click)="viewImage(img)">
+            <div class="more-images" *ngIf="post.images.length > 9">
+              +{{post.images.length - 9}}
+            </div>
+          </div>
+        </div>
+        
+        <!-- 添加标签 -->
+        <div class="post-tags">
+          <ion-chip *ngFor="let tag of post.tags" color="primary" outline>
+            <ion-label>{{tag}}</ion-label>
+          </ion-chip>
+        </div>
+        
+        <div class="post-actions">
+          <ion-button fill="clear" (click)="toggleLike(post)">
+            <ion-icon [name]="post.isLiked ? 'heart' : 'heart-outline'" 
+                      [color]="post.isLiked ? 'danger' : 'medium'"
+                      slot="start">
+            </ion-icon>
+            {{post.likes}}
+          </ion-button>
+          <ion-button fill="clear">
+            <ion-icon name="chatbubble-outline" slot="start"></ion-icon>
+            {{post.comments}}
+          </ion-button>
+          <ion-button fill="clear">
+            <ion-icon name="share-outline" slot="start"></ion-icon>
+          </ion-button>
+        </div>
+      </ion-card-content>
+    </ion-card>
+  </ion-list>
+
+  <!-- 无限滚动 -->
+  <ion-infinite-scroll (ionInfinite)="loadMore($event)">
+    <ion-infinite-scroll-content></ion-infinite-scroll-content>
+  </ion-infinite-scroll>
+
+  <!-- 发帖按钮 -->
+  <ion-button class="fab-button" shape="round" (click)="createPost()">
+    <ion-icon name="add-outline"></ion-icon>
+    发帖
+  </ion-button>
+</ion-content> 

+ 131 - 0
AiStudy-app/src/app/tab-community/tab-community.page.scss

@@ -0,0 +1,131 @@
+ion-content {
+  --background: #f5f7fa;
+}
+
+ion-card {
+  margin: 10px;
+  border-radius: 12px;
+  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05);
+
+  ion-card-content {
+    padding: 16px;
+  }
+}
+
+.author-info {
+  margin-bottom: 8px;
+  
+  h3 {
+    font-weight: 600;
+    font-size: 16px;
+    color: var(--ion-color-dark);
+  }
+  
+  p {
+    font-size: 12px;
+    color: var(--ion-color-medium);
+  }
+}
+
+.post-tags {
+  margin: 12px 0;
+  display: flex;
+  flex-wrap: wrap;
+  gap: 8px;
+  
+  ion-chip {
+    margin: 0;
+    height: 24px;
+    font-size: 12px;
+  }
+}
+
+.post-content {
+  margin: 16px 0;
+  font-size: 15px;
+  line-height: 1.6;
+  color: var(--ion-color-dark);
+  white-space: pre-wrap;
+}
+
+.post-actions {
+  display: flex;
+  justify-content: space-around;
+  border-top: 1px solid var(--ion-color-light);
+  margin-top: 12px;
+  padding-top: 8px;
+
+  ion-button {
+    --color: var(--ion-color-medium);
+    font-size: 14px;
+
+    &.liked {
+      --color: var(--ion-color-primary);
+    }
+  }
+}
+
+.fab-button {
+  position: fixed;
+  bottom: 20px;
+  right: 20px;
+  --padding-start: 20px;
+  --padding-end: 20px;
+  --border-radius: 24px;
+  --box-shadow: 0 4px 16px rgba(0, 0, 0, 0.1);
+  
+  ion-icon {
+    margin-right: 8px;
+  }
+}
+
+.post-images {
+  margin: 12px 0;
+  
+  img {
+    object-fit: cover;
+    border-radius: 8px;
+    cursor: pointer;
+  }
+
+  .single-image {
+    img {
+      max-height: 300px;
+      max-width: 100%;
+      width: auto;
+    }
+  }
+
+  .double-images {
+    display: grid;
+    grid-template-columns: 1fr 1fr;
+    gap: 4px;
+    
+    img {
+      width: 100%;
+      height: 150px;
+    }
+  }
+
+  .multiple-images {
+    display: grid;
+    grid-template-columns: repeat(3, 1fr);
+    gap: 4px;
+    
+    img {
+      width: 100%;
+      height: 100px;
+    }
+
+    .more-images {
+      position: absolute;
+      right: 4px;
+      bottom: 4px;
+      background: rgba(0, 0, 0, 0.5);
+      color: white;
+      padding: 2px 8px;
+      border-radius: 4px;
+      font-size: 12px;
+    }
+  }
+} 

+ 239 - 0
AiStudy-app/src/app/tab-community/tab-community.page.ts

@@ -0,0 +1,239 @@
+import { Component, OnInit } from '@angular/core';
+import { CommonModule } from '@angular/common';
+import { FormsModule } from '@angular/forms';
+import { 
+  IonHeader, 
+  IonToolbar, 
+  IonTitle, 
+  IonContent,
+  IonList,
+  IonItem,
+  IonLabel,
+  IonAvatar,
+  IonCard,
+  IonCardContent,
+  IonButton,
+  IonIcon,
+  IonSkeletonText,
+  IonRefresher,
+  IonRefresherContent,
+  IonInfiniteScroll,
+  IonInfiniteScrollContent,
+  IonChip,
+  ModalController
+} from '@ionic/angular/standalone';
+import { addIcons } from 'ionicons';
+import { 
+  heart, 
+  chatbubbleOutline, 
+  shareOutline,
+  heartOutline,
+  addOutline
+} from 'ionicons/icons';
+import { CreatePostModalComponent } from '../components/create-post-modal/create-post-modal.component';
+
+@Component({
+  selector: 'app-tab-community',
+  templateUrl: './tab-community.page.html',
+  styleUrls: ['./tab-community.page.scss'],
+  standalone: true,
+  imports: [
+    CommonModule,
+    FormsModule,
+    IonHeader,
+    IonToolbar,
+    IonTitle,
+    IonContent,
+    IonList,
+    IonItem,
+    IonLabel,
+    IonAvatar,
+    IonCard,
+    IonCardContent,
+    IonButton,
+    IonIcon,
+    IonSkeletonText,
+    IonRefresher,
+    IonRefresherContent,
+    IonInfiniteScroll,
+    IonInfiniteScrollContent,
+    IonChip
+  ]
+})
+export class TabCommunityPage implements OnInit {
+  posts: any[] = [];
+  isLoading = true;
+  page = 1;
+
+  constructor(private modalCtrl: ModalController) {
+    addIcons({ 
+      heart, 
+      chatbubbleOutline, 
+      shareOutline, 
+      heartOutline,
+      addOutline 
+    });
+  }
+
+  ngOnInit() {
+    this.loadPosts();
+  }
+
+  // 加载帖子数据
+  async loadPosts(event?: any) {
+    try {
+      // 模拟数据
+      const demoContents = [
+        {
+          content: '🎉 完成了今天的TypeScript学习计划!\n' +
+                   '✅ 学习了泛型的基本概念\n' +
+                   '✅ 完成了10道练习题\n' +
+                   '✅ 看完一节视频教程\n\n' +
+                   '感觉对TS的理解更深入了,明天继续加油! #学习打卡 #TypeScript',
+          likes: 45,
+          comments: 12,
+          images: ['assets/demo/demo1.png'],
+          tags: ['学习打卡', 'TypeScript', '每日进步']
+        },
+        {
+          content: '【Angular学习周报】\n' +
+                   '这周完成了Angular核心概念的学习:\n' +
+                   '1. 依赖注入系统 ✅\n' +
+                   '2. 生命周期钩子 ✅\n' +
+                   '3. 组件通信 ✅\n' +
+                   '4. 路由导航 ✅\n\n' +
+                   '分享一下我整理的思维导图,希望对大家有帮助!',
+          likes: 89,
+          comments: 34,
+          images: ['assets/demo/demo2.png'],
+          tags: ['学习总结', 'Angular', '思维导图']
+        },
+        {
+          content: '终于完成了第一个Ionic项目的上线!🎉\n' +
+                   '历时两周,从零开始学习,遇到了不少挑战:\n' +
+                   '- UI适配问题\n' +
+                   '- 状态管理\n' +
+                   '- 性能优化\n\n' +
+                   '感谢社区里的小伙伴们帮忙解答问题!分享一些项目截图 📱',
+          likes: 67,
+          comments: 15,
+          images: [
+            'assets/demo/demo3.png',
+            'assets/demo/demo4.png',
+            'assets/demo/demo5.png'
+          ],
+          tags: ['项目分享', 'Ionic', '个人成长']
+        },
+        {
+          content: '分享一张很可爱的学习励志图 📸\n\n' +
+                   '看到这张图很有感触,学习编程确实是一个循序渐进的过程:\n' +
+                   '🌱 从基础开始,一步一个脚印\n' +
+                   '🌿 遇到困难时不要气馁,每个大神也是从新手过来的\n' +
+                   '🌳 坚持学习,终会开花结果\n\n' +
+                   '与大家共勉:\n' +
+                   '『种一棵树最好的时间是十年前,其次是现在』\n' +
+                   '『不要与他人比较,只要今天的自己比昨天进步就好』\n\n' +
+                   '#学习心得 #程序员成长 #每日激励',
+          likes: 128,
+          comments: 45,
+          images: ['assets/demo/demo6.png'],
+          tags: ['学习感悟', '心得分享', '程序员成长']
+        }
+      ];
+
+      const learningTags = [
+        ['学习打卡', '每日进步', 'TypeScript'],
+        ['Angular学习', '前端开发', '知识分享'],
+        ['项目经验', 'Ionic开发', '个人成长'],
+        ['编程学习', '技术分享', 'Web开发'],
+        ['学习笔记', '技术成长', '持续学习']
+      ];
+
+      const newPosts = Array(4).fill(null).map((_, i) => ({
+        id: this.posts.length + i + 1,
+        author: {
+          name: `学习者${this.posts.length + i + 1}`,
+          avatar: `assets/avatars/avatar${((this.posts.length + i) % 4) + 1}.png`
+        },
+        content: demoContents[i % 4].content,
+        likes: demoContents[i % 4].likes,
+        comments: demoContents[i % 4].comments,
+        images: demoContents[i % 4].images,
+        createdAt: new Date(Date.now() - Math.random() * 86400000).toISOString(),
+        isLiked: false,
+        tags: demoContents[i % 4].tags
+      }));
+      
+      this.posts = [...this.posts, ...newPosts];
+      this.isLoading = false;
+      
+      if (event) {
+        event.target.complete();
+      }
+    } catch (error) {
+      console.error('加载帖子失败:', error);
+      this.isLoading = false;
+    }
+  }
+
+  // 下拉刷新
+  async doRefresh(event: any) {
+    this.page = 1;
+    this.posts = [];
+    await this.loadPosts(event);
+  }
+
+  // 无限滚动加载更多
+  async loadMore(event: any) {
+    this.page++;
+    await this.loadPosts(event);
+  }
+
+  // 点赞
+  toggleLike(post: any) {
+    post.isLiked = !post.isLiked;
+    if (post.isLiked) {
+      post.likes++;
+    } else {
+      post.likes--;
+    }
+  }
+
+  // 添加查看图片方法
+  async viewImage(imageSrc: string) {
+    // 这里可以实现图片预览功能
+    console.log('查看图片:', imageSrc);
+    // TODO: 实现图片预览模态框
+  }
+
+  // 添加发帖方法
+  async createPost() {
+    const modal = await this.modalCtrl.create({
+      component: CreatePostModalComponent,
+      breakpoints: [0, 1],
+      initialBreakpoint: 1
+    });
+
+    await modal.present();
+
+    const { data } = await modal.onWillDismiss();
+    if (data) {
+      const newPost = {
+        id: this.posts.length + 1,
+        author: {
+          name: '我',
+          avatar: 'assets/avatars/avatar1.png'
+        },
+        content: data.content,
+        images: data.images,
+        likes: 0,
+        comments: 0,
+        createdAt: new Date().toISOString(),
+        isLiked: false,
+        tags: data.tags || ['学习分享']  // 使用用户选择的标签
+      };
+
+      this.posts.unshift(newPost);
+    }
+  }
+} 

+ 4 - 0
AiStudy-app/src/app/tabs/tabs.page.html

@@ -8,6 +8,10 @@
       <ion-icon name="planet"></ion-icon> <!-- 小星球图标表示“拓展” -->
       <ion-label>拓展</ion-label>
     </ion-tab-button>
+    <ion-tab-button tab="tab-community" href="/tabs/tab-community">
+      <ion-icon name="people"></ion-icon>
+      <ion-label>社区</ion-label>
+    </ion-tab-button>
     <ion-tab-button tab="tab3" href="/tabs/tab3">
       <ion-icon name="person"></ion-icon> <!-- 人像图标 -->
       <ion-label>我的</ion-label>

+ 2 - 2
AiStudy-app/src/app/tabs/tabs.page.ts

@@ -1,7 +1,7 @@
 import { Component, EnvironmentInjector, inject } from '@angular/core';
 import { IonTabs, IonTabBar, IonTabButton, IonIcon, IonLabel } from '@ionic/angular/standalone';
 import { addIcons } from 'ionicons';
-import { chatbubbles, planet, person } from 'ionicons/icons';
+import { chatbubbles, planet, person, people } from 'ionicons/icons';
 
 @Component({
   selector: 'app-tabs',
@@ -14,6 +14,6 @@ export class TabsPage {
   public environmentInjector = inject(EnvironmentInjector);
 
   constructor() {
-    addIcons({ chatbubbles, planet, person });
+    addIcons({ chatbubbles, planet, person, people });
   }
 }

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

@@ -31,6 +31,11 @@ export const routes: Routes = [
         loadComponent: () =>
           import('../pages/profile/profile.page').then((m) => m.ProfilePage),
       },
+      {
+        path: 'tab-community',
+        loadComponent: () =>
+          import('../tab-community/tab-community.page').then(m => m.TabCommunityPage)
+      },
       {
         path: '',
         redirectTo: '/tabs/tab1',

BIN
AiStudy-app/src/assets/avatars/avatar1.png


BIN
AiStudy-app/src/assets/avatars/avatar2.png


BIN
AiStudy-app/src/assets/avatars/avatar3.png


BIN
AiStudy-app/src/assets/avatars/avatar4.png


BIN
AiStudy-app/src/assets/demo/demo1.png


BIN
AiStudy-app/src/assets/demo/demo2.png


BIN
AiStudy-app/src/assets/demo/demo3.png


BIN
AiStudy-app/src/assets/demo/demo4.png


BIN
AiStudy-app/src/assets/demo/demo5.png


BIN
AiStudy-app/src/assets/demo/demo6.png