Browse Source

feat: search work

未来全栈 3 months ago
parent
commit
3fa5cc9669

+ 20 - 4
AIart-app/src/app/tab3/tab3.page.html

@@ -1,17 +1,33 @@
 <ion-header [translucent]="true" class="ion-no-border">
   <ion-toolbar>
     <div class="header-container">
-      <ion-menu-button class="menu-icon"></ion-menu-button>
-      <div class="nav-links">
+      <!-- 菜单按钮,只有在搜索未激活时显示 -->
+      <ion-menu-button *ngIf="!isSearchActive" class="menu-icon"></ion-menu-button>
+
+      <!-- 导航项,只有在搜索未激活时显示 -->
+      <div *ngIf="!isSearchActive" class="nav-links">
         <span class="nav-item" (click)="red_underline('关注')" data-id="关注">关注</span>
         <span class="nav-item" (click)="red_underline('发现')" data-id="发现">发现</span>
         <span class="nav-item" (click)="red_underline('附近')" data-id="附近">附近</span>
       </div>
-      <ion-icon name="search-outline" class="search-icon"></ion-icon>
+
+      <!-- 搜索图标,点击时切换搜索框 -->
+      <div>
+        <ion-icon name="search-outline" class="search-icon" (click)="onSearchIconClick()"></ion-icon>
+      </div>
+
+      <!-- 搜索框,只有在搜索激活时显示 -->
+      <!-- 搜索框 -->
+      <div class="search-bar-container" *ngIf="isSearchActive">
+        <ion-searchbar [(ngModel)]="searchQuery" debounce="300" (ionInput)="onSearchInput()" placeholder="搜索描述..."
+          class="custom-searchbar" [showCancelButton]="false" showClearButton="never"></ion-searchbar>
+        <button class="close-btn" (click)="onCloseSearch()">返回</button>
+      </div>
     </div>
   </ion-toolbar>
 </ion-header>
 
+
 <ion-content [fullscreen]="true">
   <!-- 分类导航 -->
   <div class="category-nav">
@@ -45,7 +61,7 @@
   <!-- 内容网格 -->
   <div class="content-grid">
     <div class="content-card" (click)="goToDetail(artwork.WorkId)" style="cursor: pointer;"
-      *ngFor="let artwork of artworks; trackBy: trackByFn">
+      *ngFor="let artwork of displayedArtworks;trackBy: trackByFn">
       <img [src]="artwork.fileUrl" [alt]="artwork.title" class="card-image">
       <div class="card-content">
         <div class="title">{{artwork.title}}</div>

+ 102 - 0
AIart-app/src/app/tab3/tab3.page.scss

@@ -15,6 +15,10 @@
     display: flex;
     align-items: center;
     justify-content: space-between;
+    position: relative;
+    width: 100%;
+    height: 50px;
+    transition: all 0.3s ease;
     background: white;
 
     .menu-icon {
@@ -64,6 +68,7 @@
         font-size: 22px;
         color: #666;
         padding: 8px;
+        //height: 50px;
         border-radius: 50%;
         cursor: pointer;
         transition: all 0.3s;
@@ -73,6 +78,101 @@
             background-color: rgba(182, 77, 36, 0.1);
         }
     }
+
+    /* 搜索框容器样式 */
+    .search-bar-container {
+        position: relative;
+        display: flex;
+        align-items: center;
+        width: 100%;
+        height: 40px;
+        justify-content: center;
+
+    }
+
+    .custom-searchbar {
+        --background: #fff;
+        /* 背景颜色 */
+        --placeholder-color: #999;
+        /* 占位符颜色 */
+        --padding-start: 40px;
+        /* 左侧 padding */
+        --padding-end: 40px;
+        /* 右侧 padding */
+        --color: #000;
+        /* 文本颜色 */
+        --height: 40px;
+        /* 设置搜索框的高度 */
+        --border-radius: 24px;
+        /* 圆角样式 */
+        --box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
+        /* 阴影效果 */
+        --cancel-button-color: var(--primary-color);
+
+        /* 返回按钮颜色 */
+    }
+
+    /* 关闭按钮样式 */
+    .close-btn {
+        position: absolute;
+        right: 8%;
+        /* 根据 padding 调整 */
+        top: 50%;
+        transform: translateY(-50%);
+        width: 10%;
+        height: 36px;
+        display: flex;
+        justify-content: center;
+        align-items: center;
+        border-radius: 50px;
+        /* 圆形按钮 */
+        background-color: white;
+        /* 背景颜色与搜索框一致 */
+        color: #666;
+        font-size: 16px;
+        cursor: pointer;
+        transition: all 0.3s ease;
+        z-index: 10;
+        /* 提升层级 */
+
+
+    }
+
+    .close-btn:hover {
+        color: var(--primary-color);
+        /* 鼠标悬停颜色 */
+        background-color: rgba(182, 77, 36, 0.1);
+        /* 背景颜色变化 */
+    }
+
+    /* 响应式适配 */
+    @media (max-width: 768px) {
+        .header-container {
+            padding: 8px 12px;
+            /* 减小边距适配小屏幕 */
+        }
+
+        .custom-searchbar {
+            max-width: 100%;
+            /* 小屏幕占满容器 */
+            font-size: 14px;
+            /* 减小字体大小 */
+        }
+
+        .close-btn {
+            width: 48px;
+            height: 24px;
+            font-size: 14px;
+            /* 调整关闭按钮大小 */
+        }
+
+        .close-btn:hover {
+            color: var(--primary-color);
+            /* 鼠标悬停颜色 */
+            background-color: rgba(182, 77, 36, 0.1);
+            /* 背景颜色变化 */
+        }
+    }
 }
 
 // 分类导航样式
@@ -154,6 +254,7 @@
                 line-height: 1.4;
                 display: -webkit-box;
                 -webkit-line-clamp: 2;
+                line-clamp: 2;
                 -webkit-box-orient: vertical;
                 overflow: hidden;
             }
@@ -165,6 +266,7 @@
                 line-height: 1.4;
                 display: -webkit-box;
                 -webkit-line-clamp: 2;
+                line-clamp: 2;
                 -webkit-box-orient: vertical;
                 overflow: hidden;
             }

+ 67 - 94
AIart-app/src/app/tab3/tab3.page.ts

@@ -3,6 +3,7 @@ import { IonHeader, IonToolbar, IonTitle, IonContent, IonSearchbar, IonIcon, Ion
 import { Router } from '@angular/router';
 import { CommonModule } from '@angular/common';
 import { CloudQuery, CloudObject, Pointer } from '../../lib/community_ncloud'; // 确保路径正确
+import { FormsModule } from '@angular/forms';  // 导入 FormsModule
 
 interface Work {
   WorkId: string; // 修改为 WorkId
@@ -26,15 +27,16 @@ interface Work {
     CommonModule,
     IonHeader, IonToolbar, IonTitle, IonContent,
     IonSearchbar, IonIcon, IonTabButton, IonTabs, IonTabBar, IonLabel,
-    IonMenu, IonList, IonItem, IonMenuButton
+    IonMenu, IonList, IonItem, IonMenuButton, FormsModule,  // 添加 FormsModule
   ],
 })
 
 export class Tab3Page implements OnInit {
   artworks: Work[] = [];  // 存储所有的帖子信息
-
+  displayedArtworks: Work[] = []; // 存储当前显示的帖子
+  isSearchActive = false; // 控制搜索框是否显示
+  searchQuery = ''; // 搜索框的输入内容
   constructor(private router: Router) { }
-
   ngOnInit() {
     this.loadWorks();  // 加载帖子数据
     this.red_underline('发现');
@@ -46,25 +48,23 @@ export class Tab3Page implements OnInit {
       const workQuery = new CloudQuery('Work');
       const workObjs = await workQuery.find();
 
+      // 初始化 artworks 和 displayedArtworks
       this.artworks = workObjs.map((workObj: CloudObject) => {
         const workData = workObj.data as Work;
 
-        console.log(workObj.data);
-        console.log(workData.updatedAt);
-        console.log(workData.fileUrl);
-
         return {
           ...workData,
           objectId: String(workObj.id),
-          imageUrl: `${workData.fileUrl}`, // 这里确保你的路径正确
-
+          imageUrl: `${workData.fileUrl}`, // 图片路径
         };
       });
+
+      // 初始显示所有帖子
+      this.displayedArtworks = [...this.artworks];
     } catch (error) {
       console.error('加载帖子数据失败:', error);
     }
   }
-
   red_underline(text: string) {
     const prevUnderlined = document.querySelectorAll('span[underline="true"]') as NodeListOf<HTMLElement>;
     prevUnderlined.forEach((el: HTMLElement) => {
@@ -77,98 +77,69 @@ export class Tab3Page implements OnInit {
     }
   }
 
-  // 点击跳转到具体的帖子详情页面
-  goToDetail(artId: string) {
-    console.log('Work ID:', artId);
-    const artwork = this.artworks.find(art => art.WorkId === artId);
+  // 点击搜索图像时触发
+  onSearchIconClick() {
+    this.isSearchActive = !this.isSearchActive;  // 显示或隐藏搜索框
+  }
 
-    if (artwork) {
-      this.router.navigate(['/tabs/art-detail', artId], {
-        state: { artwork }
-      }).then(() => {
-        console.log('Navigation successful');
-      }).catch(err => {
-        console.error('Navigation failed:', err);
-      });
+  // 关闭搜索框
+  onCloseSearch() {
+    this.isSearchActive = false;
+    this.searchQuery = ''; // 清空搜索内容
+    this.displayedArtworks = [...this.artworks]; // 恢复显示所有帖子
+  }
+  onSearchInput() {
+    if (this.searchQuery.trim()) {
+      this.filterPosts(this.searchQuery); // 过滤帖子
     } else {
-      console.warn('Artwork not found for id:', artId);
+      this.displayedArtworks = [...this.artworks]; // 搜索框为空时恢复所有帖子
     }
   }
-  // 使用 trackByFn 来提高性能
-  trackByFn(index: number, item: any) {
-    return item.WorkId;  // 使用 WorkId 作为唯一标识符
+
+  // 根据搜索关键字过滤帖子
+  filterPosts(query: string) {
+    // 筛选符合条件的帖子
+    this.displayedArtworks = this.artworks.filter(artwork =>
+      artwork.title.toLowerCase().includes(query.toLowerCase()) || // 标题包含关键字
+      artwork.description.toLowerCase().includes(query.toLowerCase()) // 描述包含关键字
+    );
   }
-}
 
-/*
-export class Tab3Page {
-  artworks = [
-    {
-      id: '1',
-      image: 'assets/img/xingkong.png',
-      title: '梵高-星空高清画作',
-      description: '致敬梵高 #艺术品 #星空',
-      avatarUrl: 'assets/img/book1.png',
-      userName: '星空艺术家',
-      date: '12-01',
-      likes: 1687,
-      category: 'art'
-    },
-    {
-      id: '2',
-      image: 'assets/img/xiangrikui.png',
-      title: '梵高《向日葵》',
-      description: '经典中的经典,笔触很牛!',
-      avatarUrl: 'assets/img/book2.png',
-      userName: '艺术鉴赏家',
-      date: '11-30',
-      likes: 2341,
-      category: 'art'
-    },
-    {
-      id: '3',
-      image: 'assets/img/fangao.png',
-      title: '梵高自画像',
-      description: '梵高生平自画像之其中🔟副👩‍🎨 #艺术欣赏 #美',
-      avatarUrl: 'assets/img/book3.png',
-      userName: '艺术探索者',
-      date: '11-29',
-      likes: 1892,
-      category: 'art'
-    },
-    {
-      id: '4',
-      image: 'assets/img/cunzhuang.png',
-      title: '阿尔小镇',
-      description: '吧,梵高,去你最爱的阿尔看看,阿尔小镇',
-      avatarUrl: 'assets/img/book4.png',
-      userName: '风景画师',
-      date: '11-28',
-      likes: 2156,
-      category: 'art'
-    }
-  ];
+  // 根据传入的 Work[] 数组加载帖子数据
+  async loadWorkByContent(workContents: Work[]) {
+    try {
+      // 初始化查询
+      const workQuery = new CloudQuery('Work');
 
-  ngOnInit() {
-    this.red_underline('发现');
-    
-  }
-  constructor(private router: Router) { }
-  red_underline(text: string) {
-    const prevUnderlined = document.querySelectorAll('span[underline="true"]') as NodeListOf<HTMLElement>;
-    prevUnderlined.forEach((el: HTMLElement) => {
-      el.style.textDecoration = 'none';
-      el.removeAttribute('underline');
-    });
-    const target = document.querySelector(`span[data-id="${text}"]`) as HTMLElement;
-    if (target) {
-      target.setAttribute('underline', 'true');
+
+      // 查询数据库中的帖子
+      const workObjs = await workQuery.find();
+
+      // 处理查询到的帖子数据
+      this.artworks = workObjs.map((workObj: CloudObject) => {
+        const workData = workObj.data as Work;
+
+        console.log(workObj.data);
+        console.log(workData.updatedAt);
+        console.log(workData.fileUrl);
+
+        // 返回构造好的帖子数据
+        return {
+          ...workData,
+          objectId: String(workObj.id),
+          imageUrl: `${workData.fileUrl}`, // 这里确保你的路径正确
+        };
+      });
+    } catch (error) {
+      console.error('加载帖子数据失败:', error);
     }
   }
 
+
+  // 点击跳转到具体的帖子详情页面
   goToDetail(artId: string) {
-    console.log('Art ID:', artId);
-    const artwork = this.artworks.find(art => art.id === artId);
+    console.log('Work ID:', artId);
+    const artwork = this.artworks.find(art => art.WorkId === artId);
 
     if (artwork) {
       this.router.navigate(['/tabs/art-detail', artId], {
@@ -182,6 +153,8 @@ export class Tab3Page {
       console.warn('Artwork not found for id:', artId);
     }
   }
-    */
-
-
+  // 使用 trackByFn 来提高性能
+  trackByFn(index: number, item: any) {
+    return item.WorkId;  // 使用 WorkId 作为唯一标识符
+  }
+}

+ 14 - 0
AIart-app/src/lib/community_ncloud.ts

@@ -344,6 +344,20 @@ export class CloudQuery {
         this.whereOptions[key] = { "$in": valueArray };
     }
 
+    /**
+     * 实现 startsWith 方法
+     * @param key 要匹配的字段
+     * @param prefix 匹配的前缀字符串
+     */
+    startsWith(key: string, prefix: string) {
+        if (typeof prefix !== 'string') {
+            throw new Error('The prefix must be a string.');
+        }
+
+        // 添加 $regex 条件进行前缀匹配
+        this.whereOptions[key] = { "$regex": `^${prefix}` };
+    }
+
     async get(id: string): Promise<Record<string, any>> {
         const url = `http://dev.fmode.cn:1337/parse/classes/${this.className}/${id}?`;