《HarmonyOS 鸿蒙Next 5.0:开启构建模块化项目架构奇幻之旅 ——第三方库的使用:网络请求RCP、二次封装上下拉刷新、弹窗》

《HarmonyOS 鸿蒙Next 5.0:开启构建模块化项目架构奇幻之旅 ——第三方库的使用:网络请求RCP、二次封装上下拉刷新、弹窗》

1、ef_rcp简介:是基于rcp封装的网络请求相关包.提供了rcp的上传,下载,post,get,cancel,delete,put等操作。 ef_rcp出处:ef_rcp

2、PullToRefresh简介:采用的装饰器有V1,V2,作者紧跟时代的潮流。功能上:实现垂直列表下拉刷新,上拉加载,横向列表左拉刷新,右拉加载等操作。 PullToRefresh出处:PullToRefresh

3、SmartDialog简介:鸿蒙版本的SmartDialog,优雅,极简的用法;非UI区域内使用,自定义Component;返回事件处理,优化的跨页面交互;多弹窗能力,多位置弹窗:上下左右中间; 定位弹窗:自动定位目标Component;极简用法的loading弹窗。 SmartDialog出处:SmartDialog; 非常感谢大佬们出的库,方便我们开发,提高我们开发效率,更详细的介绍和使用请看官方出处。

其它 官方拦截器链接

RCP网络请求配置

1、增加BaseResponse。

export class BaseResponse<T> {
  private success: boolean;
  private msg: string;
  private code: string | number;
  private data: T;

  /**
   * 构造函数
   * @param success  是否成功标识
   * @param msg   提示消息
   * @param data  数据
   */
  constructor(success: boolean, msg: string, data: T, code: string | number) {
    this.msg = msg;
    this.success = success;
    this.code = code;
    this.data = data;
  }

  /**
   * 创建空实例
   * @returns
   */
  static create(): BaseResponse<string> {
    let baseResponse = new BaseResponse<string>(true, '', '', 200);
    return baseResponse;
  }

  /**
   * 成功-只包含消息
   * @param msg   提示消息
   * @returns
   */
  static OK(msg: string): BaseResponse<string> {
    let baseResponse = new BaseResponse<string>(true, msg, '', 200);
    return baseResponse;
  }

  /**
   * 成功-包含单行数据
   * @param msg  提示消息
   * @param data  单行数据
   * @returns
   */
  static OKByData<T>(msg: string, data: T): BaseResponse<T> {
    let dto = new BaseResponse<T>(true, msg, data, 200);
    return dto;
  }

  /**
   * 失败-包含提示消息
   * @param msg 提示消息
   * @returns
   */
  static Error(msg: string): BaseResponse<string> {
    let dto = new BaseResponse<string>(false, msg, '', 400);
    return dto;
  }

  /**
   * 失败-包含单行数据
   * @param msg 提示消息
   * @param data 单行数据
   * @returns
   */
  static ErrorByData<T>(msg: string, data: T): BaseResponse<T> {
    let dto = new BaseResponse<T>(false, msg, data, 400);
    return dto;
  }

  public setSuccess(success: boolean) {
    this.success = success;
  }

  public getSuccess(): boolean {
    return this.success;
  }

  public getCode(): string | number {
    return this.code;
  }

  public setCode(code: string | number) {
    this.code = code;
  }

  public getMsg(): string {
    return this.msg;
  }

  public setMsg(msg: string) {
    this.msg = msg;
  }

  public getData(): T {
    return this.data;
  }

  public setData(data: T) {
    this.data = data;
  }
}

2、增加不同接口返回不同数据结构的响应内容拦截器,保持输出一致。

官方拦截器链接

2.1.考虑几种常见数据返回的

// {"code":200,"message":"success","data":{"isCheck":"0"}}
// {"status":"1","msg":"success","result":{"data":{"ticket":"ST-2c2fd56bad7f49259edbb4b00ccc6cbe"}}}

2.2.修改响应内容

// {"code":200,"message":"success","data":{"isCheck":"0"}}
// {"status":"1","msg":"success","result":{"data":{"ticket":"ST-2c2fd56bad7f49259edbb4b00ccc6cbe"}}}
// {"data": ..., "errorCode": 0, "errorMsg": ""} errorCode = 0 代表执行成功.errorCode = -1001 代表登录失效,需要重新登录
//转换成object
let result = response.toJSON();
//考虑几种常见数据返回的
// {"code":200,"message":"success","data":{"isCheck":"0"}}
// {"status":"1","msg":"success","result":{"data":{"ticket":"ST-2c2fd56bad7f49259edbb4b00ccc6cbe"}}}
// {"data": ..., "errorCode": 0, "errorMsg": ""} errorCode = 0 代表执行成功.errorCode = -1001 代表登录失效,需要重新登录

//1.成功失败标识
let success: boolean = true;
//2.返回的消息提示
let message: string = '';
//3.返回的数据
let data: Record<string, Object> = {};
//4.返回请求状态码
let code = response.statusCode + '';

if (result) {
  Object.entries(result).forEach(item => {

    if (["errorCode", "code", "status"].includes(String(item[0]))) {
      code = String(item[1])
      success = disposeCode(String(item[1]));
    }
    if (["errorMsg", "message", "msg"].includes(String(item[0]))) {
      message = String(item[1]);
    }
    if (["data", "result"].includes(String(item[0]))) {
      data = item[1];
    }
  })
}
// HTTP status code
if (response.statusCode != 200) {
  success = false;
  message = (response.toString() as string) ? response.toString() as string : "网络错误";
}

3、初始化参数配置,在EntryAbility的onWindowStageCreate写

// if(运行环境===debug){
    efRcp
      .setBaseURL("https://www.wanandroid.com") //设置请求路径
      .enableLogInterceptor()//设置为false即可关闭
      .setLoadingContent('正在加载中...')//更改loading文本
      .convertError(true)//表示如果response.toJSON()异常时将响应字符串返回,false则表示值返回异常提醒而不返回结果
      .addCommonHeaders({//设置公共头
        "version": `V1.0.0`
      })// .setLoadingImg(wrapBuilder(loadingImg)) //设置loading为gif图片
      .addCustomInterceptors([new ResponseInterceptor()])
      .addSysCodeEvent({//添加统一系统框架级别编码处理逻辑,如超时等
        listener: (code: number) => {
          // Log.d('---------addSysCodeEvent监听事件-----' + code)
          switch (code) {
          case -1001:
          case 401:
            console.log(`xxx : ---跳转登录页面,清空一些数据等`)
            //跳转登录页面,清空一些数据等
            // ZRouter.replacePathByName('LoginPage')
            break;
          }
        }
      })//创建session对象,需要再设置为一系列操作后再调用,否则设置不生效,可在特殊情况处设置其他操作后重新创建session
      .create()//获取统一的session对象,必须在create后调用 .设置了全局的配置比如拦截器  超时时间  session本身的配置 ,需要再次create
      .builder();
    // }

4、如何使用,更多详细内容可以参考ef_rcp作者的主页

post请求为例,例子在 TestNetView 页面
//登录
let login = await PhoneService.login({
  'username': '鸿蒙',
  'password': '5.0'
})
console.log('xxxlogin--' + '' + json.stringify(login))
if (login.data?.getSuccess()) {
  this.message = json.stringify(login.data.getData())
  console.log('xxxxxdata参数--' + '' + json.stringify(login.data.getData()))
} else {
  console.log('xxx--' + login.data?.getMsg())
  ToastUtil.showToast(login.data?.getMsg())
}

PhoneService类
export class PhoneService {
  static login(query: Record<string, Object>): Promise<EfRcpResponse<BaseResponse<object>>> {
    return efRcpClientApi.post<BaseResponse<object>>({
      url: 'user/login',
      query: query,
      loadingTxt: '正在登录中...',
      loading: false
    });
  }
}

5、自带的loading( 图一 ) 如果不符合可以自定义一个builder,参考下面的弹窗设置。

图片

PullToRefresh上下拉刷新配置,采用V2装饰器版本

1、下载安装,目前版本是 “^2.0.1”

ohpm install @zhongrui/pull_to_refresh_v2

2、每个项目的上下拉刷新头部,尾部都不一样,所以我这里采用了个性化定制,请使用RefreshLayout

上下拉刷新例子—>TestRefreshView
@Preview
@ComponentV2
export struct TestRefreshView {}
自定义刷新头部

图片

@Builder
defHeaderView() {
  Stack({ alignContent: Alignment.Center }) {
    if (this.pullDown?.status == PullStatus.PullDown || this.pullDown?.status == PullStatus.Refresh || this.pullDown?.status == PullStatus.PreRefresh) {
      Row() {
        Image($r("app.media.loading_refresh")).width(50).height(40)
      }.width('100%').height(50).justifyContent(FlexAlign.Center)
    } else if (this.pullDown?.status == PullStatus.RefreshSuccess) {
      // Text("刷新成功").textExtend()
    } else if (this.pullDown?.status == PullStatus.RefreshError) {
      Text("刷新失败,请重新尝试").textExtend()
    }

  }.height(50).width("100%")
}
自定义刷新尾部

图片

@Builder
defFooterView() {
  Stack({ alignContent: Alignment.Center }) {
    if (this.pullDown?.status == PullStatus.PullUp || this.pullDown?.status == PullStatus.Load || this.pullDown?.status == PullStatus.PreLoad) {
      Row() {
        Image($r("app.media.loading_refresh"))
          .width(24)
          .height(24)
      }.width('100%').height(60).justifyContent(FlexAlign.Center)
    } else if (this.pullDown?.status == PullStatus.LoadSuccess) {
      // Text("加载完成").textExtend()
    } else if (this.pullDown?.status == PullStatus.LoadError) {
      Text('加载失败,请重新尝试').textExtend()
    }

  }.height(50).width("100%")
}
内容视图

图片

@Builder
_ContentView() {
  Text("contentView:()=>{your @Builder View}")
}
loading视图

图片

@Builder
loadingView() {
  this.layout($r('app.media.loading_refresh'), '正在加载...')
}
空视图

图片

@Builder
_EmptyView() {
  this.layout($r('app.media.nodata'), '暂无数据')
}
错误视图

图片

@Builder
_ErrorView() {
  this.layout($r('app.media.no_network'), '暂无网络,请重新尝试')
}
视图公用
@Builder
layout(src: ResourceStr, text: ResourceStr) {
 Column() {
   Image(src).width(48).height(48)
   Text(text).fontSize(12).fontColor($r('app.color.color_999999')).padding(8)
 }.width(CommonConst.FULL_PARENT).height(CommonConst.FULL_PARENT).justifyContent(FlexAlign.Center)
}
底部暂无更多(目前只能往上拉才能看到,如果不上拉也能看到,那就自定义一个Item放到最下面)。

图片

@Builder
defFooterNoMoreView(isV: boolean) {
  Text('没有更多数据了')
    .textAlign(TextAlign.Center)
    .height(isV ? 50 : CommonConst.FULL_PARENT)
    .width(isV ? CommonConst.FULL_PARENT : 50)
    .fontSize(14)
    .fontColor($r('app.color.color_999999'))
}

private isVerticalLayout(): boolean {
  return (this.config.isVertical || this.config.horizontalMode == 1 || this.config.horizontalMode == 2)
}

默认带弹性效果的列表需要关闭弹性滑动

.edgeEffect(EdgeEffect.None)

BasicDataSource,配合LazyForEach使用,数据太多的话建议用LazyForEach;后期可以看看(Repeat:子组件复用)

源代码请看项目中,模块uicomponents /src/main/ets/components/refresh/BasicDataSource

封装请求返回的数据,更加简洁

如何使用
/** 请求接口 */
async loadData() {
  let data: Array<string> = ["1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "20"]
  // let data:string[] = []// 空数据
  this.dataSource.pageRefresh(true, this.pageNum, data.length, data, this.controller,
    (pages: number) => {
      this.pageNum = pages
    })

}
/**
 * 列表页数 处理
 * @param success  成功失败标识
 * @param page     页数
 * @param total    总条数
 * @param data     数据
 * @param controller RefreshController
 * @param pageNums 返回的page
 */
public pageRefresh(success: boolean, page: number, total: number, data: Array<T>, controller: RefreshController, pageNums: (pages: number) => void) {
  if (success) {
    if (page == 1) {
      this.setNewData(data)
      controller.refreshSuccess()
      pageNums(2)

      if (this.totalCount() == 0) {
        controller.viewEmpty()
      }
    } else {
      this.addAllData(data)
      controller.loadSuccess()
      pageNums(data.length == 0 ? page : (page + 1))
    }
    // 是否还有更多
    setTimeout(() => {
      controller.hasMore(this.totalCount() < total /*|| !(data.length < 10)*/)
    }, 800) //处理暂无更多数据视图和列表同时显示出来

  } else {
    if (controller.getStatus() == PullStatus.Refresh) {
      controller.refreshError()
    } else {
      controller.loadError()
    }
    if (page == 1) {
      controller.viewError()
    }
  }

}

SmartDialog弹窗配置

安装

ohpm install ohos_smart_dialog

图片

全局设置一个loadding样式

OhosSmartDialog({ loadingBuilder: customLoading })
自定义页面弹窗
@Builder
function customLoading(args: ResourceStr = '正在加载中...') {
  Column() {
    Image($r('app.media.loading_refresh')).width(50).height(50)
    Text(args)
      .fontSize(11)
      .fontColor($r('app.color.color_222222'))
      .margin({ top: 11 })
      .maxLines(1)
      .textOverflow({ overflow: TextOverflow.Ellipsis })

  }.width(120).height(120).justifyContent(FlexAlign.Center).backgroundColor(Color.White).borderRadius(12)
}

图片

路由子页面一定要加,返回事件监听

.onBackPressed(OhosSmartDialog.onBackPressed())

以往系列文章

  1. 《探索 HarmonyOS NEXT(5.0):开启构建模块化项目架构奇幻之旅 —— 模块化基础篇》
  2. 《探索 HarmonyOS NEXT(5.0):开启构建模块化项目架构奇幻之旅 —— 构建基础特性层》
  3. 《探索 HarmonyOS NEXT(5.0):开启构建模块化项目架构奇幻之旅 —— 构建公共能力层》
  4. 《探索 HarmonyOS NEXT(5.0):开启构建模块化项目架构奇幻之旅 —— Tabs底部导航栏》
  5. 《探索 HarmonyOS NEXT (5.0):开启构建模块化项目架构奇幻之旅 —— 动态路由 ZRouter:引领高效模块通信的智慧中枢》

若本文对您稍有帮助,诚望您不吝点赞,多谢。

有兴趣的同学可以点击查看源码

欢迎加我微信一起交流:+V:yinshiyuba


更多关于《HarmonyOS 鸿蒙Next 5.0:开启构建模块化项目架构奇幻之旅 ——第三方库的使用:网络请求RCP、二次封装上下拉刷新、弹窗》的实战教程也可以访问 https://www.itying.com/category-93-b0.html

1 回复

更多关于《HarmonyOS 鸿蒙Next 5.0:开启构建模块化项目架构奇幻之旅 ——第三方库的使用:网络请求RCP、二次封装上下拉刷新、弹窗》的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html


针对帖子标题《HarmonyOS 鸿蒙Next 5.0:开启构建模块化项目架构奇幻之旅 ——第三方库的使用:网络请求RCP、二次封装上下拉刷新、弹窗》的问题,以下是专业且简洁的回答:

在HarmonyOS鸿蒙Next 5.0中,构建模块化项目架构时,对于第三方库的使用,特别是涉及网络请求RCP、二次封装上下拉刷新以及弹窗功能,你可以通过以下方式进行操作:

  1. 网络请求RCP

    • 利用HarmonyOS提供的网络API进行RPC(远程过程调用)封装,实现跨设备或跨模块的网络数据交互。
    • 确保RPC接口定义清晰,数据序列化与反序列化正确。
  2. 二次封装上下拉刷新

    • 基于HarmonyOS的List组件,自定义上下拉刷新逻辑。
    • 使用动画和状态管理,实现流畅的刷新体验。
  3. 弹窗

    • 利用HarmonyOS的Dialog或Toast组件,根据需求进行二次封装。
    • 实现弹窗的显示、隐藏及交互逻辑。

请注意,具体实现需参考HarmonyOS官方文档和API,确保代码兼容性和稳定性。如果问题依旧没法解决请联系官网客服,官网地址是:https://www.itying.com/category-93-b0.html

回到顶部