前言:
之前写过一个demo 但是那个是本地数据 而且也没有适配鸿蒙next版本 现在我改成网络的 接口是我本地服务的 就想着分享给大家 希望大家能够很好的去学习和工作。
效果图:
-
桌面icon 效果
-
欢迎页面
-
登录页面
-
注册页面
-
职位模块
-
公司模块
-
公司详情页面
-
消息模块
-
消息聊天详情页面
-
自动AI回复效果
-
人中心模块
-
发布职位信息模块
具体实现:
添加权限
启动本地服务
拿到项目里面的bosstabservice 配置好数据库 启动服务
客户端实现;
-
1 底部导航器实现
import CompanylistComponent from '../view/CompanylistComponent';
import MessageListComponent from '../view/MessageListComponent';
import My from '../view/My';
import PositionListComponent from '../view/PositionListComponent';
@Entry
@Component
struct ListIndex {
private controller: TabsController = new TabsController()
@State SelectPos:number=0;
public IndexClick(){
this.SelectPos=0;
this.controller.changeIndex(0)
}
public messageClick(){
this.SelectPos=1;
this.controller.changeIndex(1)
}
public myClick(){
this.SelectPos=2;
this.controller.changeIndex(2)
}
public meClick(){
this.SelectPos=3;
this.controller.changeIndex(3)
}
build() {
Flex({ direction: FlexDirection.Column, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) {
Tabs({ controller: this.controller }) {
TabContent() {
PositionListComponent()
}
TabContent() {
CompanylistComponent()
}
TabContent() {
MessageListComponent()
}
TabContent() {
My()
}
}
.scrollable(false)
.barHeight(0)
.animationDuration(0)
Row() {
Column(){
Image((this.SelectPos==0?$r('app.media.ic_main_tab_company_pre'):$r('app.media.ic_main_tab_company_nor')))
.width(30).height(30)
Text('职位')
.size({ width: '100%', height: 30 }).textAlign(TextAlign.Center)
.fontSize(15)
.fontColor((this.SelectPos==0?Color.Green:Color.Black))
}
.layoutWeight(1)
.backgroundColor($r('app.color.gray2'))
.height("100%")
.onClick(this.IndexClick.bind(this))
Column(){
Image((this.SelectPos==1?$r('app.media.ic_main_tab_find_pre'):$r('app.media.ic_main_tab_find_nor')))
.width(30).height(30)
Text('公司')
.size({ width: '100%', height: 30 }).textAlign(TextAlign.Center)
.fontSize(15)
.fontColor((this.SelectPos==1?Color.Green:Color.Black))
}
.layoutWeight(1)
.backgroundColor($r('app.color.gray2'))
.height("100%")
.onClick(this.messageClick.bind(this))
Column(){
Image((this.SelectPos==2?$r('app.media.ic_main_tab_contacts_pre'):$r('app.media.ic_main_tab_contacts_nor')))
.width(30).height(30)
Text('消息')
.size({ width: '100%', height: 30 }).textAlign(TextAlign.Center)
.fontSize(15)
.fontColor((this.SelectPos==2?Color.Green:Color.Black))
}
.layoutWeight(1)
.backgroundColor($r('app.color.gray2'))
.height("100%")
.onClick(this.myClick.bind(this))
Column(){
Image((this.SelectPos==3?$r('app.media.ic_main_tab_my_pre'):$r('app.media.ic_main_tab_my_nor')))
.width(30).height(30)
Text('我的')
.size({ width: '100%', height: 30 }).textAlign(TextAlign.Center)
.fontSize(15)
.fontColor((this.SelectPos==3?Color.Green:Color.Black))
}
.layoutWeight(1)
.backgroundColor($r('app.color.gray2'))
.height("100%")
.onClick(this.meClick.bind(this))
}.alignItems(VerticalAlign.Bottom).width('100%').height(65).margin({top:0,right:0,bottom:10,left:0})
}
.width('100%')
.height('100%')
}
}
底部导航使用 Row组件配合 Column 组件实现底导航器 然后配置 Tabs组件实现我们上门的碎片也和底部导航器点击切换效果
职位页面实现:
初始化生命接口 网络请求
async aboutToAppear(){
Logger.error(this.TAG+' aboutToAppear --- > ');
let networkurl=CommonConstant.DISH;
await httpRequestGet(networkurl).then((data)=>{
console.log("data --- > "+data);
Logger.error("登录请求回调结果 ---> " +data.toString());
let positionmodel : PositionModel = JSON.parse(data.toString())
this.JokeList = positionmodel.data
});
}
解析服务端响应数据创建数据model
export class PositionModel {
msg: string = ""
data: Array<Positiondata> = []
code:number=0
}
export class Positiondata {
id: string = ""
name: string = ""
cname: string = ""
size: string = ""
salary: string = ""
username: string = ""
title: string = ""
}
解析数据
let positionmodel : PositionModel = JSON.parse(data.toString())
this.JokeList = positionmodel.data
网络请求工具类
import http from '@ohos.net.http';
import Logger from './Logger'
import Constants, { ContentType } from '../common/Constants';
import { connection } from '@kit.NetworkKit';
import { healthStore } from '@kit.HealthServiceKit';
export function httpRequestGet(url:string,params?:string){
return httpRequest(url,http.RequestMethod.GET,params);
}
export function httpRequestPost(url:string,params?:string){
return httpRequest(url,http.RequestMethod.POST,params);
}
function httpRequest(url:string ,method:http.RequestMethod,params?:string):Promise<string>{
let httpRequest=http.createHttp();
let responseResult=httpRequest.request(url,{
method:method,
readTimeout:Constants.HTTP_READ_TIMEOUT, //读取超时时间可选 默认 600000
header:{
'Content-Type':ContentType.JSON
},
connectTimeout:Constants.HTTP_READ_TIMEOUT, //连接超时时间 可选,默认为60000ms
extraData:params // 请求参数
});
let getjson:string='';
return responseResult.then((value:http.HttpResponse)=>{
Logger.error("请求状态-- >"+value.responseCode);
if(value.responseCode===200){
Logger.error("请求成功");
let result= `${value.result}`;
Logger.error("请求返回数据",JSON.parse(result));
getjson=result;
}else{
getjson='';
}
return getjson;
}).catch(()=>{
//httpRequest.off("headerReceive");
httpRequest.destroy();
return '';
});
}
日志工具类
import hilog from '@ohos.hilog';
class Logger {
private domain: number;
private prefix: string;
private format: string = '%{public}s, %{public}s';
/**
* constructor.
*
* @param Prefix Identifies the log tag.
* @param domain Domain Indicates the service domain, which is a hexadecimal integer ranging from 0x0 to 0xFFFFF.
*/
constructor(prefix: string = 'MyApp', domain: number = 0xFF00) {
this.prefix = prefix;
this.domain = domain;
}
debug(...args: string[]): void {
hilog.debug(this.domain, this.prefix, this.format, args);
}
info(...args: string[]): void {
hilog.info(this.domain, this.prefix, this.format, args);
}
warn(...args: string[]): void {
hilog.warn(this.domain, this.prefix, this.format, args);
}
error(...args: string[]): void {
hilog.error(this.domain, this.prefix, this.format, args);
}
}
export default new Logger('HTTPS', 0xFF00)
职位页面布局实现
import { PositionData, PositionModel } from '../bean/PositionModel'
import Logger from '../utils/Logger';
import { httpRequestGet } from '../utils/OkhttpUtils';
import CommonConstant, * as commonConst from '../common/CommonConstants';
import { PullToRefresh } from '@ohos/pulltorefresh'
/***
* 创建人:xuqing
* 创建时间:2024年6月30日14:53:45
* 类说明: 职位模块
*
*
*/
@Component
export default struct PositionList {
private scroller:Scroller=new Scroller();
@State positiondata:PositionData=new PositionData();
@State JokeList:Array<PositionData>=[];
@State dataList:Array<PositionData>=[];
@State curPage:number=1;
@State pageSize:number=5;
@State curnumber:number=1;
@State data:Array<PositionData>=this.dataList;
async aboutToAppear(){
this.getpostition(this.curPage,this.pageSize);
}
async getpostition(curPagennumber:number,pageSizenumber:number ){
let getcurPage:string='curPage=';
let getpageSize:string='&pageSize='
let networkurl=CommonConstant.POSITIONLINIT+getcurPage+curPagennumber+getpageSize+pageSizenumber;
httpRequestGet(networkurl).then((data)=>{
Logger.error("职位请求结果=--->"+ data.toString());
let positionModel:PositionModel=JSON.parse(data.toString());
this.JokeList=positionModel.data;
this.dataList.push(...this.JokeList);
});
}
build() {
Column(){
Row(){
Text('职位')
.fontSize(30)
.fontColor(Color.White)
.textAlign(TextAlign.Center)
}.width('100%')
.height(50)
.backgroundColor(Color.Green)
.justifyContent(FlexAlign.Center)
PullToRefresh({
// 必传项,列表组件所绑定的数据
data: $data,
// 必传项,需绑定传入主体布局内的列表或宫格组件
scroller: this.scroller,
// 必传项,自定义主体布局,内部有列表或宫格组件
customList: () => {
// 一个用@Builder修饰过的UI方法
this.getlistview();
},
// 可选项,下拉刷新回调
onRefresh: () => {
return new Promise<string>((resolve, reject) => {
// 模拟网络请求操作,请求网络2秒后得到数据,通知组件,变更列表数据
resolve('刷新成功');
this.dataList=[];
this.curPage=1;
this.pageSize=5;
this.curnumber=1;
this.getpostition(this.curPage,this.pageSize);
});
},
// 可选项,上拉加载更多回调
onLoadMore: () => {
return new Promise<string>((resolve, reject) => {
// 模拟网络请求操作,请求网络2秒后得到数据,通知组件,变更列表数据
this.curnumber++;
this.curPage=this.curnumber;
Logger.error(" this.curPage -- > "+ this.curPage)
this.pageSize=5;
this.getpostition(this.curPage,this.pageSize);
resolve('')
});
},
customLoad: null,
customRefresh: null,
})
}.width('100%')
.height('100%')
}
@Builder
private getlistview(){
List({space:commonConst.LIST_ITEM_SPACE,scroller:this.scroller}){
ForEach(this.dataList,(item:PositionData)=>{
ListItem(){
Row(){
Column(){
Row(){
Text(item?.name).fontSize(14).fontColor(Color.Black).align(Alignment.Start).margin({left:12,top:5})
Text(item?.salary).fontSize(14).fontColor(Color.Red).align(Alignment.End).margin({right:12,top:5})
}.justifyContent(FlexAlign.SpaceBetween)
.width('100%')
Row(){
Text(item?.cname)
.fontSize(25)
.margin({left:12})
}.width('100%').alignItems(VerticalAlign.Top)
.margin({left:12,top:25})
Row(){
Text(item?.username)
.fontColor(Color.Green)
.margin({left:12,top:10})
}.width('100%').alignItems(VerticalAlign.Top)
.margin({left:12,top:20})
}.width('100%')
.height('100%')
.justifyContent(FlexAlign.Start)
}.height( '20%')
.width('100%')
.justifyContent(FlexAlign.Center)
}
})
}.width(commonConst.GOODS_LIST_WIDTH) .backgroundColor('#eeeeee')
.divider({ strokeWidth: 1, color: 0x222222 })
.edgeEffect(EdgeEffect.None) // 必须设置列表为滑动到边缘无效果
}
}
其他的company 公司页面 和message 消息页面和职位页面实现方式类似 我这边就不展开讲了 都是进去页面第一个生命周期进行网络请求 然后解析数据赋值给数组然后渲染在我们的list组件上面 还有分页上拉加载数据和下拉刷新数据等 都在我的实战课程里面。各位同学如果想学习更多的知识可以关注我的B站课程
课程地址
项目内容:
-
1 常用布局组件的学习
-
2 网络请求工具类封装
-
3 arkui 生命周期启动流程
-
4 日志工具类的封装
-
5 自定义组合组件的封装
-
6 路由导航跳转的使用
-
7 本地地数据的缓存 以及缓存工具类的封装
-
8 欢迎页面的实现
-
9 登录案例和自动登录效果实现
-
10 请求网络数据分页上拉加载 下拉刷新的实现
-
11 list数据懒加载实现
-
12 webview组件的使用
最后总结:
鸿蒙的 ark ui 非常类似flutter 这种声明式ui 所有的布局都是代码编写的和传统的布局和逻辑分开有些区别 刚开始上手的时候可能不适应,慢慢就习惯了 还有一点 代码嵌套 这个其实可以把每个组件抽离出来就可以解决鸿蒙的ark ui 网络部分也是类似前端的http 请求 解析json和前端也比较像 也有面向对象编程的思想, 对前端和移动端同学来说也比较好理解和学习。 今天的文章就讲到这里有兴趣的 关注我B站教程 了解更多鸿蒙开发的知识 可以关注坚果派公众号 。 谢谢