当前位置: 首页 > article >正文

HarmonyOS NEXT应用开发边学边玩系列:从零实现一影视APP (三、影视搜索页功能实现)

在HarmonyOS NEXT开发环境中,可以使用@nutpi/axios库来简化网络请求的操作。本文将展示如何使用HarmonyOS NEXT框架和@nutpi/axios库,从零开始实现一个简单的影视APP,主要关注影视搜索页的功能实现。

为什么选择@nutpi/axios

nutpi/axios是坚果派出品的,对axios封装过的鸿蒙HTTP客户端库,用于简化axios库的使用和以最简单的形式写代码。使用nutpi/axios库可以大大简化代码,使网络接口变得简单直观。

项目开源地址:https://atomgit.com/csdn-qq8864/hmmovie

好的作品是需要不断打磨,在你的学习和体验过程中有任何问题,欢迎到我的开源项目代码仓下面提交issue,持续优化。

安装nutpi/axios

首先,需要在项目中安装nutpi/axios库。

ohpm install @ohos/axios

在这里插入图片描述

实现电影搜索接口

使用nutpi/axios库来实现电影搜索接口。先封装一个工具类:

//axiosClient.ets
import {AxiosHttpRequest,HttpPromise} from '@nutpi/axios'
import {AxiosHeaders,AxiosRequestHeaders,AxiosError } from '@nutpi/axios';
import { Log } from './logutil';
import { promptAction } from "@kit.ArkUI";

function showToast(msg:string){
  Log.debug(msg)
  promptAction.showToast({ message: msg })
}

function showLoadingDialog(msg:string){
  Log.debug(msg)
  promptAction.showToast({ message: msg })
}

function hideLoadingDialog() {

}
/**
 * axios请求客户端创建
 */
const axiosClient = new AxiosHttpRequest({
  baseURL: "http://120.27.146.247:8000/api/v1",
  timeout: 10 * 1000,
  checkResultCode: false,
  showLoading:true,
  headers: new AxiosHeaders({
    'Content-Type': 'application/json'
  }) as AxiosRequestHeaders,
  interceptorHooks: {
    requestInterceptor: async (config) => {
      // 在发送请求之前做一些处理,例如打印请求信息
      Log.debug('网络请求Request 请求方法:', `${config.method}`);
      Log.debug('网络请求Request 请求链接:', `${config.url}`);
      Log.debug('网络请求Request Params:', `\n${JSON.stringify(config.params)}`);
      Log.debug('网络请求Request Data:', `${JSON.stringify(config.data)}`);
      // 动态添加或修改header
      //config.headers['X-ATOMGIT-POP-COMMUNITY'] = 'openatom';
      axiosClient.config.showLoading = config.showLoading
      if (config.showLoading) {
        showLoadingDialog("加载中...")
      }
      if (config.checkLoginState) {
        //let hasLogin = await StorageUtils.get(StorageKeys.USER_LOGIN, false)
        //Log.debug('网络请求Request 登录状态校验>>>', `${hasLogin.toString()}`);
        // if (hasLogin) {
        //   return config
        // } else {
        //   if (config.needJumpToLogin) {
        //     //Router.push(RoutePath.TestPage)
        //   }
        //   throw new AxiosError("请登录")
        // }
      }
      return config;
    },
    requestInterceptorCatch: (err) => {
      Log.error("网络请求RequestError", err.toString())
      if (axiosClient.config.showLoading) {
        hideLoadingDialog()
      }
      return err;
    },
    responseInterceptor: (response) => {
      //优先执行自己的请求响应拦截器,在执行通用请求request的
      if (axiosClient.config.showLoading) {
        hideLoadingDialog()
      }
      Log.debug('网络请求响应Response:', `\n${JSON.stringify(response.data)}`);
      if (response.status === 200) {
        // @ts-ignore
        const checkResultCode = response.config.checkResultCode
        if (checkResultCode && response.data.errorCode != 0) {
          showToast(response.data.errorMsg)
          return Promise.reject(response)
        }
        return Promise.resolve(response);
      } else {
        return Promise.reject(response);
      }
    },
    responseInterceptorCatch: (error) => {
      if (axiosClient.config.showLoading) {
        hideLoadingDialog()
      }
      Log.error("网络请求响应异常", error.toString());
      errorHandler(error);
      return Promise.reject(error);
    },
  }
});

function errorHandler(error: any) {
  if (error instanceof AxiosError) {
    //showToast(error.message)
  } else if (error != undefined && error.response != undefined && error.response.status) {
    switch (error.response.status) {
    // 401: 未登录
    // 未登录则跳转登录页面,并携带当前页面的路径
    // 在登录成功后返回当前页面,这一步需要在登录页操作。
      case 401:

        break;
    // 403 token过期
    // 登录过期对用户进行提示
    // 清除本地token和清空vuex中token对象
    // 跳转登录页面
      case 403:
        //showToast("登录过期,请重新登录")
      // 清除token
      // localStorage.removeItem('token');
        break;
    // 404请求不存在
      case 404:
        //showToast("网络请求不存在")
        break;

    // 其他错误,直接抛出错误提示
      default:
        //showToast(error.response.data.message)
    }

  }
}

export  {axiosClient,HttpPromise};

接口的实现很简单啦:

// 定义电影搜索接口
// 7.电影搜索接口
export const movieSearch =  (q:string,start:number,count:number): HttpPromise<SearchResp> => axiosClient.post({url:'/searchmovie',data: {  q:q,start:start,count:count }});

代码讲解

  1. 创建axios客户端:封装了一个axios客户端axiosClient工具类,并设置了基础URL和请求超时时间。
  2. 定义接口函数movieSearch函数接收三个参数:q(查询字符串)、start(起始位置)和count(数量),并返回一个Promise对象。

Search组件和List组件使用

接下来,将实现影视搜索页的组件,包括Search组件和List组件的使用。

import { movieSearch } from '../../common/api/movie';
import { SearchRespData } from '../../common/bean/SearchResp';
import { Log } from '../../utils/logutil';
import { BusinessError } from '@kit.BasicServicesKit';

@Builder
export function SearchPageBuilder() {
  SearchPage()
}

@Component
struct SearchPage {
  pageStack: NavPathStack = new NavPathStack()
  controller: SearchController = new SearchController()
  @State changeValue: string = ''
  @State submitValue: string = ''
  @State searchList: SearchRespData[] = []

  // 组件生命周期
  aboutToAppear() {
    Log.info('SearchPage aboutToAppear');
  }

  onPageShow(): void {
    this.controller.caretPosition(0)
  }

  build() {
    NavDestination() {
      Column({ space: 0 }) {
        Search({ controller: this.controller, value: this.changeValue, placeholder: '请输入片名' })
          .searchButton('搜索')
          .width('95%')
          .height(45)
          .maxLength(30)
          .backgroundColor('#F5F5F5')
          .placeholderColor(Color.Grey)
          .placeholderFont({ size: 14, weight: 400 })
          .textFont({ size: 14, weight: 400 })
          .focusable(true)
          .defaultFocus(true)
          .onSubmit((value: string) => {
            this.submitValue = value
          })
          .onChange((value: string) => {
            this.changeValue = value
            movieSearch(value, 1, 10).then((res) => {
              Log.debug(res.data.message)
              Log.debug("request", "res.data.code:%{public}d", res.data.code)
              if (res.data.code == 0) {
                this.searchList = res.data.data
              }
            })
              .catch((err: BusinessError) => {
                Log.debug("request", "err.data.code:%d", err.code)
                Log.debug("request", err.message)
              });
          })
          .margin({ left: 20, right: 20 })

        // list组件
        List({ space: 10 }) {
          ForEach(this.searchList, (item: SearchRespData, idx) => {
            ListItem() {
              Column({ space: 0 }) {
                Row() {
                  Stack() {
                    Image(item.cover).objectFit(ImageFit.Cover)
                      .borderRadius(5).zIndex(1)

                    Text(item.year.substring(0, 10))
                      .padding(5)
                      .margin({ top: 80 })
                      .width('100%')
                      .height(20)
                      .textAlign(TextAlign.Center)
                      .maxLines(2)
                      .textOverflow({ overflow: TextOverflow.Clip })
                      .fontSize(12)
                      .fontColor(Color.White)
                      .opacity(100) // 设置标题的透明度
                      .backgroundColor('#808080AA') // 背景颜色设为透明
                      .zIndex(2)
                  }.width(100).height(100).margin({ left: 10 })

                  Column({ space: 15 }) {
                    Text(item.title).fontSize(16).fontWeight(FontWeight.Bold).align(Alignment.Start).width('100%')
                    Text(item.genre).fontSize(12).align(Alignment.Start).width('100%')
                    Text('评分: ' + item.rate).fontSize(12)
                      .align(Alignment.Start).width('100%')
                  }.justifyContent(FlexAlign.Start).padding(5).margin({ left: 10 })
                }.size({ width: '100%', height: 100 })
              }.size({ width: '100%', height: 100 })
            }.onClick(() => {
              this.pageStack.pushDestinationByName("MovieDetailPage", { id: item.id }).catch((e: Error) => {
                console.log(`catch exception: ${JSON.stringify(e)}`)
              }).then(() => {
                // 跳转成功
              });
            })
          }, (itm: SearchRespData) => itm.id)
        }
        .divider({ strokeWidth: 2, color: '#F1F3F5' })
        .listDirection(Axis.Vertical)
        .edgeEffect(EdgeEffect.Spring, { alwaysEnabled: true })

      }
      .width('100%')
      .height('100%')
    }.title("影视搜索")
    .width('100%')
    .height('100%')
    .onReady(ctx => {
      this.pageStack = ctx.pathStack
    })
  }
}

代码讲解

  1. 导入模块:导入了之前定义的movieSearch函数,以及一些其他必要的模块。
  2. 定义组件状态
    • changeValue:用于存储当前搜索框中的输入值。
    • submitValue:用于存储用户提交的搜索值。
    • searchList:用于存储搜索结果的列表。
  3. 组件生命周期
    • aboutToAppear:组件即将出现在页面时执行的日志记录。
    • onPageShow:组件显示时重置光标位置。
  4. 构建页面
    • NavDestination:定义页面的导航目的地。
    • Column:垂直布局容器。
    • Search:搜索框组件,设置了搜索按钮、宽度、高度、最大长度等属性,并绑定了onSubmitonChange事件。
    • List:列表组件,用于显示搜索结果。
      • ForEach:遍历searchList数组,为每个搜索结果项创建一个ListItem
      • ListItem:列表项组件,包含电影的封面、年份、标题、类型和评分。
      • onClick:点击列表项时,导航到电影详情页。
  5. 列表样式
    • divider:设置列表项之间的分隔线。
    • listDirection:设置列表的方向为垂直。
    • edgeEffect:设置边缘效果为弹簧效果。

总结

通过本文,展示了如何使用HarmonyOS NEXT框架和nutpi/axios库来实现一个简单的影视搜索页。nutpi/axios库的使用大大简化了网络请求的操作,使代码更加简洁易读。希望这篇文章对你有所帮助,让你在开发HarmonyOS NEXT应用时更加得心应手。

如果你有任何问题或建议,欢迎在评论区留言交流!

作者介绍

作者:csdn猫哥

原文链接:https://blog.csdn.net/yyz_1987

团队介绍

坚果派团队由坚果等人创建,团队拥有12个华为HDE带领热爱HarmonyOS/OpenHarmony的开发者,以及若干其他领域的三十余位万粉博主运营。专注于分享HarmonyOS/OpenHarmony、ArkUI-X、元服务、仓颉等相关内容,团队成员聚集在北京、上海、南京、深圳、广州、宁夏等地,目前已开发鸿蒙原生应用和三方库60+,欢迎交流。

版权声明

本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。


http://www.kler.cn/a/505957.html

相关文章:

  • linux之进程信号(初识信号,信号的产生)
  • 探索 Transformer²:大语言模型自适应的新突破
  • 代码随想录算法训练营第 8 天(字符串1)| 344.反转字符串 541. 反转字符串II 卡码网54.替换数字
  • 如何发布自己的第一个Chrome扩展程序
  • 1️⃣Java中的集合体系学习汇总(List/Map/Set 详解)
  • Unity中实现倒计时结束后干一些事情
  • 【AI】【RAG】使用WebUI部署RAG:数据优化与设置技巧详解
  • 《怪形重制版》V1.02官方学习版
  • matlab GUI 打包成exe可执行文件
  • Java设计模式——单例模式(特性、各种实现、懒汉式、饿汉式、内部类实现、枚举方式、双重校验+锁)
  • 活动预告 | CCF开源发展委员会开源供应链安全技术研讨会(2025第一期)——“大模型时代的开源供应链安全风控技术”...
  • http协议 tomact的基本使用
  • PHP政务招商系统
  • Electron 开发者的 Tauri 2.0 实战指南:窗口管理与系统集成
  • P3数据结构、数据类型、数字编码、字符编码:保姆级图文详解
  • 交流电压220V如何用单片机测量电压?
  • VM(虚拟机)和Linux的安装
  • java 迪米特法则,原理、思想、工作流程、实现细节、稳定性、优缺点、应用场景等
  • C语言基本知识复习浓缩版:数组
  • 碰一碰发视频源码搭建技术剖析,支持OEM
  • 数字普惠金融对新质生产力的影响研究(2015-2023年)
  • ZooKeeper 核心知识全解析:架构、角色、节点与应用
  • Dify应用-工作流
  • 单头注意力机制(SHSA)详解
  • Spring Boot DevTools(官网文档解读)
  • Jenkins安装配置遇到问题及解决方案(Windows版本)