#跟着若城学鸿蒙# HarmonyOS NEXT学习之Search案例三:搜索联想与数据处理

img

一、案例介绍

本案例将展示Search组件的搜索联想功能和数据处理实现,通过一个知识库搜索系统的示例,展示如何处理复杂的搜索逻辑和数据交互。

二、代码实现

@Entry
@Component
struct SearchSuggestionExample {
  @State searchText: string = ''
  @State suggestions: Array<{
    id: number,
    keyword: string,
    type: string,
    weight: number
  }> = []
  @State searchResults: Array<{
    id: number,
    title: string,
    content: string,
    category: string,
    tags: string[]
  }> = []
  @State isLoading: boolean = false
  private searchCache: Map<string, any> = new Map()
  private debounceTimer: number = -1

  build() {
    Column() {
      // 搜索栏
      Search({
        value: this.searchText,
        placeholder: '搜索知识库',
        searchButton: '搜索'
      })
        .width('90%')
        .height(40)
        .backgroundColor('#FFFFFF')
        .placeholderColor('#999999')
        .placeholderFont({ size: 16 })
        .borderRadius(20)
        .margin({ top: 20, bottom: 10 })
        .onChange((value: string) => {
          this.searchText = value
          this.debounceGetSuggestions(value)
        })
        .onSubmit((value: string) => {
          this.performSearch(value)
        })
        .onClear(() => {
          this.clearSearch()
        })

      if (this.isLoading) {
        // 加载状态
        LoadingProgress()
          .width(24)
          .height(24)
          .margin(20)
      } else if (this.suggestions.length > 0 && this.searchText.length > 0) {
        // 搜索联想列表
        List() {
          ForEach(this.suggestions, (item) => {
            ListItem() {
              Row() {
                Column() {
                  Text(item.keyword)
                    .fontSize(16)
                    .fontWeight(FontWeight.Medium)
                  Text(item.type)
                    .fontSize(12)
                    .fontColor('#999999')
                    .margin({ top: 4 })
                }
                .alignItems(HorizontalAlign.Start)

                if (item.weight > 80) {
                  Text('热门')
                    .fontSize(12)
                    .fontColor('#FF4D4F')
                    .backgroundColor('#FFE8E8')
                    .padding({ left: 8, right: 8, top: 2, bottom: 2 })
                    .borderRadius(10)
                }
              }
              .width('100%')
              .justifyContent(FlexAlign.SpaceBetween)
              .padding(16)
              .onClick(() => {
                this.searchText = item.keyword
                this.performSearch(item.keyword)
              })
            }
          })
        }
        .width('90%')
        .backgroundColor(Color.White)
        .borderRadius(8)
      } else if (this.searchResults.length > 0) {
        // 搜索结果列表
        List() {
          ForEach(this.searchResults, (item) => {
            ListItem() {
              Column() {
                Text(item.title)
                  .fontSize(16)
                  .fontWeight(FontWeight.Bold)
                  .margin({ bottom: 8 })

                Text(item.content)
                  .fontSize(14)
                  .fontColor('#666666')
                  .margin({ bottom: 8 })
                  .maxLines(2)
                  .textOverflow({ overflow: TextOverflow.Ellipsis })

                Row() {
                  ForEach(item.tags, (tag: string) => {
                    Text(tag)
                      .fontSize(12)
                      .fontColor('#007DFF')
                      .backgroundColor('#E6F2FF')
                      .padding({ left: 8, right: 8, top: 2, bottom: 2 })
                      .borderRadius(10)
                      .margin({ right: 8 })
                  })
                }
              }
              .width('100%')
              .padding(16)
              .backgroundColor(Color.White)
              .borderRadius(8)
              .margin({ bottom: 8 })
            }
          })
        }
        .width('90%')
      }
    }
    .width('100%')
    .height('100%')
    .backgroundColor('#F5F5F5')
  }

  private debounceGetSuggestions(keyword: string) {
    if (this.debounceTimer !== -1) {
      clearTimeout(this.debounceTimer)
    }

    if (keyword.length === 0) {
      this.suggestions = []
      return
    }

    // 使用防抖处理联想请求
    this.debounceTimer = setTimeout(() => {
      this.getSuggestions(keyword)
    }, 300)
  }

  private async getSuggestions(keyword: string) {
    // 检查缓存
    const cacheKey = `suggestion_${keyword}`
    if (this.searchCache.has(cacheKey)) {
      this.suggestions = this.searchCache.get(cacheKey)
      return
    }

    this.isLoading = true

    // 模拟异步请求
    await new Promise(resolve => setTimeout(resolve, 500))

    // 模拟联想数据
    const suggestions = [
      { id: 1, keyword: `${keyword} 教程`, type: '文档', weight: 90 },
      { id: 2, keyword: `${keyword} 示例`, type: '代码', weight: 85 },
      { id: 3, keyword: `${keyword} 问题`, type: '问答', weight: 75 },
      { id: 4, keyword: `${keyword} 最佳实践`, type: '文章', weight: 70 }
    ]

    // 更新缓存
    this.searchCache.set(cacheKey, suggestions)
    this.suggestions = suggestions
    this.isLoading = false
  }

  private async performSearch(keyword: string) {
    if (keyword.trim().length === 0) return

    this.isLoading = true
    this.suggestions = []

    // 检查缓存
    const cacheKey = `search_${keyword}`
    if (this.searchCache.has(cacheKey)) {
      this.searchResults = this.searchCache.get(cacheKey)
      this.isLoading = false
      return
    }

    // 模拟异步搜索
    await new Promise(resolve => setTimeout(resolve, 1000))

    // 模拟搜索结果
    const results = [
      {
        id: 1,
        title: `${keyword} 开发指南`,
        content: '这是一份详细的开发指南,包含了完整的API文档和使用示例...',
        category: '教程',
        tags: ['入门', '文档']
      },
      {
        id: 2,
        title: `${keyword} 实战案例`,
        content: '通过实际项目案例,深入讲解开发过程中的注意事项和最佳实践...',
        category: '案例',
        tags: ['进阶', '实战']
      }
    ]

    // 更新缓存
    this.searchCache.set(cacheKey, results)
    this.searchResults = results
    this.isLoading = false
  }

  private clearSearch() {
    this.searchText = ''
    this.suggestions = []
    this.searchResults = []
  }

  onDestroy() {
    if (this.debounceTimer !== -1) {
      clearTimeout(this.debounceTimer)
    }
  }
}

三、总结

本案例通过一个知识库搜索系统,展示了 Search 组件的搜索联想功能和数据处理实现。通过配置搜索框的基本属性(如占位符、搜索按钮文本等),实现了标准的搜索框界面。同时,通过自定义样式(如背景色、字体、圆角等),提升了搜索框的视觉效果。此外,通过事件监听(如 onChange、onSubmit、onClear),实现了实时搜索、提交搜索和清空搜索结果等功能

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值