1祭隔、ef_rcp簡(jiǎn)介:是基于rcp封裝的網(wǎng)絡(luò)請(qǐng)求相關(guān)包.提供了rcp的上傳,下載,post,get,cancel,delete,put等操作。
ef_rcp出處ef_rcp蠢终;
2序攘、PullToRefresh簡(jiǎn)介:采用的裝飾器有V1,V2,作者緊跟時(shí)代的潮流寻拂。功能上:實(shí)現(xiàn)垂直列表下拉刷新,上拉加載程奠,橫向列表左拉刷新,右拉加載等操作祭钉。
PullToRefresh出處PullToRefresh瞄沙;
3、SmartDialog簡(jiǎn)介:鴻蒙版本的SmartDialog慌核,優(yōu)雅距境,極簡(jiǎn)的用法;非UI區(qū)域內(nèi)使用垮卓,自定義Component垫桂;返回事件處理,優(yōu)化的跨頁(yè)面交互粟按;多彈窗能力诬滩,多位置彈窗:上下左右中間霹粥;
定位彈窗:自動(dòng)定位目標(biāo)Component;極簡(jiǎn)用法的loading彈窗疼鸟。
SmartDialog出處SmartDialog后控;
非常感謝大佬們出的庫(kù),方便我們開(kāi)發(fā),提高我們開(kāi)發(fā)效率空镜,更詳細(xì)的介紹和使用請(qǐng)看官方出處浩淘。
其它
RCP官網(wǎng)
RCP網(wǎng)絡(luò)請(qǐng)求配置
1、增加BaseResponse吴攒。
export class BaseResponse<T> {
//成功失敗標(biāo)識(shí)
private success: boolean;
//返回提示信息
private msg: string;
//響應(yīng)編碼
private code: string | number;
//返回?cái)?shù)據(jù)
private data: T;
/**
* 構(gòu)造函數(shù)
* @param success 是否成功標(biāo)識(shí)
* @param msg 提示消息
* @param data 數(shù)據(jù)
*/
constructor(success: boolean, msg: string, data: T, code: string | number) {
this.msg = msg;
this.success = success;
this.code = code;
this.data = data;
}
/**
* 創(chuàng)建空實(shí)例
* @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;
}
/**
* 成功-包含單行數(shù)據(jù)
* @param msg 提示消息
* @param data 單行數(shù)據(jù)
* @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;
}
/**
* 失敗-包含單行數(shù)據(jù)
* @param msg 提示消息
* @param data 單行數(shù)據(jù)
* @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张抄、增加不同接口返回不同數(shù)據(jù)結(jié)構(gòu)的響應(yīng)內(nèi)容攔截器,保持輸出一致洼怔。
官方攔截器鏈接 2.1.考慮幾種常見(jiàn)數(shù)據(jù)返回的
// {"code":200,"message":"success","data":{"isCheck":"0"}}
// {"status":"1","msg":"success","result":{"data":{"ticket":"ST-2c2fd56bad7f49259edbb4b00ccc6cbe"}}}
2.2.修改響應(yīng)內(nèi)容
- 先獲取響應(yīng)結(jié)果
const response = await next.handle(context);
- 把接口返回不同的code欣鳖,msg,data轉(zhuǎn)為一致。例子中code為200和0代表成功茴厉,可根據(jù)公司規(guī)定修改。
- 考慮幾種常見(jiàn)數(shù)據(jù)返回的
// {"code":200,"message":"success","data":{"isCheck":"0"}}
// {"status":"1","msg":"success","result":{"data":{"ticket":"xxxxx"}}}
// {"data": ..., "errorCode": 0, "errorMsg": ""} errorCode = 0 代表執(zhí)行成功.errorCode = -1001 代表登錄失效什荣,需要重新登錄
//轉(zhuǎn)換成object
let result = response.toJSON();
//考慮幾種常見(jiàn)數(shù)據(jù)返回的
// {"code":200,"message":"success","data":{"isCheck":"0"}}
// {"status":"1","msg":"success","result":{"data":{"ticket":"ST-2c2fd56bad7f49259edbb4b00ccc6cbe"}}}
// {"data": ..., "errorCode": 0, "errorMsg": ""} errorCode = 0 代表執(zhí)行成功.errorCode = -1001 代表登錄失效矾缓,需要重新登錄
//1.成功失敗標(biāo)識(shí)
let success: boolean = true;
//2.返回的消息提示
let message: string = '';
//3.返回的數(shù)據(jù)
let data: Record<string, Object> = {};
//4.返回請(qǐng)求狀態(tài)碼
let code = response.statusCode + '';
if (result) {
Object.entries(result).forEach((item: object) => {
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 : "網(wǎng)絡(luò)錯(cuò)誤";
}
- 成功失敗標(biāo)識(shí)用success表示。
if (["code", "status"].includes(String(item[0]))) {
code = String(item[1])
success = disposeCode(String(item[1]));
}
- disposeCode的作用稻爬,處理不同接口返回的碼
/** 處理不同接口返回的碼.errorCode = 0 || code==200 || status==1 為成功 */
export function disposeCode(s: string): boolean {
let isSuccess: boolean = false;
if ("0" == s || "200" == s || "1" == s) {
isSuccess = true;
}
return isSuccess;
}
- 返回統(tǒng)一格式
let baseResponse = new BaseResponse(success, disposeMsg(code, message), data, code);
- disposeMsg的作用嗜闻,表示處理信息,比如針對(duì)于特定的code碼桅锄,返回指定的msg琉雳。
/** 處理信息 */
export function disposeMsg(code: string, msg: string): string {
let message = "";
switch (code) {
case '-1001':
case '401':
message = "登陸信息過(guò)期,請(qǐng)重新登陸";
break;
case '403':
message = "賬號(hào)異常友瘤,請(qǐng)聯(lián)系客服";
break;
case '429':
message = "您的操作過(guò)于頻繁翠肘,請(qǐng)稍后再試!";
break;
case '500':
message = "服務(wù)器錯(cuò)誤(500)";
break;
default:
message = `${msg}`;
}
const errorCodes = ['401', '403', '429', '500'];
if (errorCodes.includes(code)) {
//吐司
ToastUtil.showToast(message);
}
return message;
}
- 修改響應(yīng)后辫秧,給rcp.Response傳過(guò)去束倍。
//對(duì)響應(yīng)修改
const toReturn: rcp.Response = {
request: response.request,
statusCode: response.statusCode,
headers: response.headers,
effectiveUrl: response.effectiveUrl,
body: response.body,
downloadedTo: response.downloadedTo,
timeInfo: response.timeInfo,
httpVersion: response.httpVersion,
reasonPhrase: response.reasonPhrase,
toString: () => response.toString(),
toJSON: () => baseResponse
};
return toReturn;
3、初始化參數(shù)配置盟戏,在EntryAbility的onWindowStageCreate寫(xiě)
// if(運(yùn)行環(huán)境===debug){
efRcp
.setBaseURL("https://www.wanandroid.com") //設(shè)置請(qǐng)求路徑
.enableLogInterceptor()//設(shè)置為false即可關(guān)閉
.setLoadingContent('正在加載中...')//更改loading文本
.convertError(true)//表示如果response.toJSON()異常時(shí)將響應(yīng)字符串返回,false則表示值返回異常提醒而不返回結(jié)果
.addCommonHeaders({//設(shè)置公共頭
"version": `V1.0.0`
})// .setLoadingImg(wrapBuilder(loadingImg)) //設(shè)置loading為gif圖片
.addCustomInterceptors([new ResponseInterceptor()])
.addSysCodeEvent({//添加統(tǒng)一系統(tǒng)框架級(jí)別編碼處理邏輯,如超時(shí)等
listener: (code: number) => {
// Log.d('---------addSysCodeEvent監(jiān)聽(tīng)事件-----' + code)
switch (code) {
case -1001:
case 401:
console.log(`xxx : ---跳轉(zhuǎn)登錄頁(yè)面绪妹,清空一些數(shù)據(jù)等`)
//跳轉(zhuǎn)登錄頁(yè)面,清空一些數(shù)據(jù)等
// ZRouter.replacePathByName('LoginPage')
break;
}
}
})//創(chuàng)建session對(duì)象,需要再設(shè)置為一系列操作后再調(diào)用柿究,否則設(shè)置不生效,可在特殊情況處設(shè)置其他操作后重新創(chuàng)建session
.create()//獲取統(tǒng)一的session對(duì)象邮旷,必須在create后調(diào)用 .設(shè)置了全局的配置比如攔截器 超時(shí)時(shí)間 session本身的配置 ,需要再次create
.builder();
// }
4蝇摸、如何使用婶肩,更多詳細(xì)內(nèi)容可以參考ef_rcp作者的主頁(yè)
- post請(qǐng)求為例办陷,例子在 TestNetView 頁(yè)面
//登錄
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參數(shù)--' + '' + 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( 圖一 ) 如果不符合可以自定義一個(gè)builder,參考下面的彈窗設(shè)置狡孔。
PullToRefresh上下拉刷新配置懂诗,采用V2裝飾器版本
1、下載安裝苗膝,目前版本是 “^2.0.1”
ohpm install @zhongrui/pull_to_refresh_v2
- 1
2殃恒、每個(gè)項(xiàng)目的上下拉刷新頭部,尾部都不一樣辱揭,所以我這里采用了個(gè)性化定制,請(qǐng)使用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("刷新失敗,請(qǐng)重新嘗試").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('加載失敗,請(qǐng)重新嘗試').textExtend()
}
}.height(50).width("100%")
}
- 內(nèi)容視圖
/** 內(nèi)容視圖 */
@Builder
_ContentView() {
Text("contentView:()=>{your @Builder View}")
}
- loading視圖
/** loading視圖 */
@Builder
loadingView() {
this.layout($r('app.media.loading_refresh'), '正在加載...')
}
- 空視圖
/** 空視圖 */
@Builder
_EmptyView() {
this.layout($r('app.media.nodata'), '暫無(wú)數(shù)據(jù)')
}
- 錯(cuò)誤視圖
/** 錯(cuò)誤視圖 */
@Builder
_ErrorView() {
this.layout($r('app.media.no_network'), '暫無(wú)網(wǎng)絡(luò),請(qǐng)重新嘗試')
}
- 視圖公用
/** 視圖公用 */
@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)
}
- 底部暫無(wú)更多(目前只能往上拉才能看到离唐,如果不上拉也能看到,那就自定義一個(gè)Item放到最下面)问窃。
/**
* 暫無(wú)更多
* @param isV 是否豎向
*/
@Builder
defFooterNoMoreView(isV: boolean) {
Text('沒(méi)有更多數(shù)據(jù)了')
.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)
}
}
- 默認(rèn)帶彈性效果的列表需要關(guān)閉彈性滑動(dòng)
.edgeEffect(EdgeEffect.None)
- BasicDataSource亥鬓,配合LazyForEach使用,數(shù)據(jù)太多的話建議用LazyForEach域庇;后期可以看看(Repeat:子組件復(fù)用)
源代碼請(qǐng)看項(xiàng)目中嵌戈,模塊uicomponents /src/main/ets/components/refresh/BasicDataSource
3、封裝請(qǐng)求返回的數(shù)據(jù)听皿,更加簡(jiǎn)潔
- 如何使用
/** 請(qǐng)求接口 */
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[] = []// 空數(shù)據(jù)
this.dataSource.pageRefresh(true, this.pageNum, data.length, data, this.controller,
(pages: number) => {
this.pageNum = pages
})
}
/**
* 列表頁(yè)數(shù) 處理
* @param success 成功失敗標(biāo)識(shí)
* @param page 頁(yè)數(shù)
* @param total 總條數(shù)
* @param data 數(shù)據(jù)
* @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) //處理暫無(wú)更多數(shù)據(jù)視圖和列表同時(shí)顯示出來(lái)
} else {
if (controller.getStatus() == PullStatus.Refresh) {
controller.refreshError()
} else {
controller.loadError()
}
if (page == 1) {
controller.viewError()
}
}
}
SmartDialog彈窗配置
- 更多細(xì)節(jié)請(qǐng)參考大佬寫(xiě)的
安裝
ohpm install ohos_smart_dialog
全局設(shè)置一個(gè)loadding樣式
- Index頁(yè)面
OhosSmartDialog({ loadingBuilder: customLoading })
/** 自定義頁(yè)面彈窗 */
@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)
}
- 路由子頁(yè)面一定要加熟呛,返回事件監(jiān)聽(tīng)
.onBackPressed(OhosSmartDialog.onBackPressed())
以往系列文章
- 《探索 HarmonyOS NEXT(5.0):開(kāi)啟構(gòu)建模塊化項(xiàng)目架構(gòu)奇幻之旅 —— 模塊化基礎(chǔ)篇》
- 《探索 HarmonyOS NEXT(5.0):開(kāi)啟構(gòu)建模塊化項(xiàng)目架構(gòu)奇幻之旅 —— 構(gòu)建基礎(chǔ)特性層》
- 《探索 HarmonyOS NEXT(5.0):開(kāi)啟構(gòu)建模塊化項(xiàng)目架構(gòu)奇幻之旅 —— 構(gòu)建公共能力層》
- 《探索 HarmonyOS NEXT(5.0):開(kāi)啟構(gòu)建模塊化項(xiàng)目架構(gòu)奇幻之旅 —— Tabs底部導(dǎo)航欄》
- 《探索 HarmonyOS NEXT (5.0):開(kāi)啟構(gòu)建模塊化項(xiàng)目架構(gòu)奇幻之旅 —— 動(dòng)態(tài)路由 ZRouter:引領(lǐng)高效模塊通信的智慧中樞》
若本文對(duì)您稍有幫助,誠(chéng)望您不吝點(diǎn)贊尉姨,多謝庵朝。
有興趣的同學(xué)可以點(diǎn)擊查看源碼
- gitee:https://gitee.com/jiaojiaoone/explore-harmony-next.git
- github:https://github.com/JasonYinH/ExploreHarmonyNext.git
歡迎加我微信一起交流:+V:yinshiyuba
本文使用 文章同步助手 同步