#我的鸿蒙开发手记#Atomgit 客户端实战,开启鸿蒙开发新旅程! 原创 精华
peterpan527
发布于 2025-5-8 23:40
浏览
1收藏
客户端介绍
在鸿蒙开发的广阔天地里,工具的选择至关重要。今天,就来跟大家分享我使用 Atomgit 客户端进行鸿蒙开发的实战经历,说不定能为你的鸿蒙开发之路点亮一盏明灯!
项目概述和创新点
本项目是一个功能全面的社区客户端,采用axios网络库实现数据请求。它不仅支持用户首次使用的授权说明,还提供用户注册、登录功能,并包含隐私协议和用户协议的详细说明。客户端界面分为首页和我的信息页两大板块。
首页设计精心,集成了轮播广告、精彩活动、资讯展示等多个模块,同时设有关于页面,为用户提供全面的信息浏览体验。首页的tab页签布局清晰,分为推荐、最新、仓库、组织等核心功能区,方便用户快速定位感兴趣的内容。个人信息页面则专注于用户账户管理,支持注册、登录以及隐私协议和用户协议的授权。
应用功能说明
用户授权与账户管理
- 首次使用授权说明:为用户提供初次使用时必须了解的授权信息。
-
首页
- 首页轮播广告获取:通过POST请求获取并展示社区的最新活动或重要通知,支持点击跳转到详情页。
- 首页推荐获取:通过POST请求获取推荐资讯的数据,点击跳转到详情页。
- 首页最新获取:通过POST请求获取展示社区中最新发布的内容,点击跳转到详情页。
- 首页仓库获取:通过POST请求获取仓库资讯的数据,点击跳转到详情页。
- 首页组织获取:通过POST请求获取展示社区中的组织结构,包括用户所在的团队或感兴趣的小组,点击跳转到详情页。
我的页面
- **个人信息页面
- **用户注册:允许新用户创建账户并加入社区。
- **用户登录:允许已注册用户登录以访问个人账户。
- 关于 提供社区的背景信息和联系方式,支持点击跳转到详情页。
- **用户协议隐私授权:用户在此页面授权同意隐私协议,确保账户安全和数据保护。
技术栈概览
- ArkTS:作为本项目开发的核心编程语言,ArkTS以其卓越的性能和简洁的语法,为项目提供了稳定而高效的代码实现。
- ArkUI:选用ArkUI作为界面开发的UI框架,它以其直观的设计和响应式布局,确保了用户界面的美观和易用性,提升了用户体验。
- Axios:在处理HTTP请求方面,我们采用了Axios库,它以其简洁的API和强大的功能,简化了网络请求的处理,提高了数据交互的效率。
- 通过这些精选的技术工具,我们的项目团队能够构建一个既强大又用户友好的社区客户端,为用户提供流畅且直观的交互体验
商业价值分析
本项目旨在打造一个资讯类客户端平台,通过精心设计的展示功能,促进信息的流通和社区成员之间的交流。以下是项目的核心价值和功能亮点:
信息流通与社区互动
- 最新广告展示:客户端将展示最新的广告内容,让用户及时了解社区内外的最新动态和商业信息。
- 精彩活动推荐:精选社区内外的精彩活动,鼓励用户参与,增加社区的活跃度和凝聚力。
- 推荐内容:根据用户兴趣和社区热度推荐相关内容,提升用户体验和内容的个性化。
- 仓库与组织列表:展示社区内的资源库和组织结构,方便用户查找和利用社区资源,同时也增强了社区的组织性和结构性。
增强社区凝聚力
- 通过提供丰富的资讯和活动,本项目能够吸引用户积极参与社区生活,从而增强社区成员之间的联系和社区的整体凝聚力。
广告主和组织的价值平台
- 展示平台:为广告主和组织提供了一个展示自己工作成果和成就的平台,增加曝光度和影响力。
- 互动交流:通过客户端的互动功能,广告主和组织可以直接与用户沟通,收集反馈,优化服务。
项目目标
本项目通过提供一个集资讯、广告、活动和社区资源于一体的客户端平台,旨在构建一个信息流通顺畅、社区活跃度高、凝聚力强的在线社区环境。同时,它也为广告主和组织提供了一个有效的宣传和交流渠道,实现了社区与商业的双赢。
环境配置
环境配置说明
-
系统要求:
- Windows环境:
- 操作系统:Windows10 64位、Windows11 64位
- 内存:16GB及以上
- 硬盘:100GB及以上
- 分辨率:1280*800像素及以上
- macOS环境:
- 操作系统:macOS(X86) 11/12/13/14 macOS(ARM) 12/13/14
- 内存:8GB及以上
- 硬盘:100GB及以上
- 分辨率:1280*800像素及以上
- Windows环境:
-
依赖管理:项目依赖于DevEco Studio开发工具与三方库的Axios。依赖管理通过DevEco Studio内置的项目管理工具进行,确保版本一致性和依赖的自动更新。
编译构建
- 构建流程:项目构建流程包括代码编译、资源打包、应用签名和部署测试。
- 依赖安装:用户可以通过DevEco Studio的项目管理工具一键安装所有依赖,或手动通过命令行工具进行安装。
核心代码
登录页面
import { promptAction, router } from '@kit.ArkUI';
import { http } from '@kit.NetworkKit';
import CommonConstants from '../../utils/CommonConstants';
import Logger from '../../utils/Logger';
import { util } from '@kit.ArkTS';
import HelperUtil from '../../utils/HelperUtil';
import { CommonDialog } from '../../utils/CommonDialog';
import { LoginParams, LoginResult } from '../Model/UserModel';
import { GlobalContext } from '../../utils/GlobalContext';
import { preferences } from '@kit.ArkData';
import { HdNav } from '../../common/HdNav';
interface ParamsType {
label: string
}
@Entry
@Component
struct Login {
@State message: string = '登录帐号以使用更多服务';
@State isAcceptProtocol: boolean = false;
@State isCodeLoginShow: boolean = false
@State addressLabel: string = '中国 +86'
@State dialogTitle: Resource = $r('app.string.dialog_about_title')
@State dialogContent: Resource = $r('app.string.project_desc')
dialogController: CustomDialogController = new CustomDialogController({
builder: CommonDialog({ title: $dialogTitle, description: $dialogContent, showPositive: false }),
alignment: DialogAlignment.Center
})
title: string = '登录';
@State username: string = "";
@State par: LoginParams = {
} as LoginParams;
/**
* 获取文章列表
* @param par
* @returns
*/
async dataRequest(par: LoginParams): Promise<void> {
console.log(" 用户名 " + par.username);
if (HelperUtil.isEmpty(par.username)) {
HelperUtil.showToast("请输入登录用户名!");
return;
}
if (HelperUtil.isEmpty(par.password)) {
HelperUtil.showToast("请输入登录用户密码!");
return;
}
HelperUtil.digest(new util.TextEncoder().encodeInto(par.password));
let username = encodeURI(par.username);
let pwd = await HelperUtil.md5(par.password);
let url ='https://openatom.atomgit.com/api/user/isMembers';
console.log(" 用户名 " + url)
let httpRequest = http.createHttp();
httpRequest.request(
url
).then((res) => {
Logger.info(CommonConstants.HOME_PAGE_TAG, '请求结果: ' + res.result as string)
let rt = JSON.parse(res.result as string) as LoginResult; //total
let message = "";
switch (rt.code.toString()) {
case "200":
message = "登录成功";
//需要存储用户的uid,username,等信息到本地,
this.saveLogin(rt);
router.pushUrl({
url: 'pages/Index',
params: rt,
})
HelperUtil.showToast(message);
return;
case "θ":
message = "服务器错误";
break;
case "00":
message = "内部处理错误";
break;
case "000":
message = "用户名/密码未填写";
break;
case "102":
message = "用户名不存在";
break;
case "103":
message = "密码不正确";
break;
case "101":
message = "登录成功";
break;
case "400200":
message = "非法社区";
break;
default:
message = "登录失败";
}
HelperUtil.showToast(rt.code + " " + message);
})
.catch((err: Error) => {
HelperUtil.showToast("请求服务异常:" + err.message)
Logger.error(CommonConstants.HOME_PAGE_TAG, JSON.stringify(err));
})
.finally(() => {
httpRequest.destroy()
})
}
onPageShow(): void {
if (router.getParams() !== undefined) {
let params = router.getParams() as Record<string, string>;
this.username = params["username"] as string;
this.par.username = this.username;
}
}
build() {
Column() {
HdNav()
Image($r('app.media.startIcon'))
.width(50)
.margin({ top: 15 })
Text(this.title)
.fontSize(25)
.fontWeight(900)
.margin({ top: 20, bottom: 5 })
Text(this.message)
.fontSize(14)
.fontColor('#666')
.margin({ bottom: 10 })
// 短信验证码登录
if (this.isCodeLoginShow) Column() {
Row() {
Text('国家/地区')
.fontColor('#666')
Row() {
Text(this.addressLabel)
.fontColor('#666')
Image($r('app.media.ic_public_arrow_right'))
.width(20)
.fillColor('#666')
}
.onClick(() => {
router.pushUrl({
url: 'pages/CountryRegion',
})
})
}
.width('100%')
.justifyContent(FlexAlign.SpaceBetween)
.padding({ left: 10, right: 10 })
.margin({ bottom: 20 })
TextInput({
placeholder: '手机号'
})
.maxLength(20)
.type(InputType.PhoneNumber)
Row({ space: 10 }) {
TextInput({
placeholder: '短信验证码'
})
.width('60%')
.type(InputType.Number)
Button('获取验证码')
.buttonStyle(ButtonStyleMode.NORMAL)
.onClick(() => {
})
}
.margin({ top: 20, bottom: 30 })
.width('100%')
Button('登录')
.width('100%')
.onClick(() => {
})
.margin({ bottom: 15 })
Text('密码登录')
.fontColor('#007dff')
.onClick(() => {
this.isCodeLoginShow = false
})
}
// 密码登录
if (!this.isCodeLoginShow) Column() {
TextInput({
text: this.username, //"",
placeholder: '登录账号'
})
.maxLength(15)
.type(InputType.USER_NAME)
.onChange((value) => {
this.par.username = value;
})
TextInput({
text: "", //"1234567",
placeholder: '密码'
})
.type(InputType.Password)
.margin({ top: 20, bottom: 30 })
.onChange((value) => {
this.par.password = value;
})
Row() {
Radio({
group: "",
value: '同意'
}).onClick(() => {
this.isAcceptProtocol = !this.isAcceptProtocol;
}).checked(this.isAcceptProtocol);
Text("我已阅读并同意").fontSize(14)
Text('用户协议').fontColor('#007dff').fontSize(14)
.onClick(() => {
let u =
getContext(this).resourceManager.getStringValue($r("app.string.terms_use_url").id, (error, value) => {
if (error != null) {
console.log("error is " + error);
} else {
this.openWebUrl(value);
console.log("openWebUrl is " + value);
}
});
})
Text("和").fontSize(14)
Text('隐私声明').fontColor('#007dff').fontSize(14)
.onClick(() => {
let u =
getContext(this)
.resourceManager
.getStringValue($r("app.string.privacy_agreement_url").id, (error, value) => {
if (error != null) {
console.log("error is " + error);
} else {
this.openWebUrl(value);
console.log("openWebUrl is " + value);
}
});
})
}.margin({ bottom: 20 })
Button('登录')
.width('100%')
.onClick(() => {
if (!this.isAcceptProtocol) {
HelperUtil.showToast("请先确认用户协议!");
return;
}
this.dataRequest(this.par);
})
.margin({ bottom: 15 })
Button('注册账号')
.width('100%')
.buttonStyle(ButtonStyleMode.NORMAL)
.onClick(() => {
router.pushUrl({
url: 'pages/User/Register',
})
})
.margin({ bottom: 15 })
}
Blank()
Column() {
Text('其他方式登录')
.fontColor('#666')
.fontSize(14)
Image($r('app.media.ic_public_comments'))
.width(40)
.border({
width: 1,
color: '#ccc'
})
.padding(10)
.borderRadius(20)
.margin({ top: 20, bottom: 20 })
}.visibility(Visibility.Hidden)
Row() {
Text('遇到问题').fontColor('#007dff').fontSize(14)
.onClick(() => {
this.dialogController.open();
})
Divider()
.vertical(true)
.height(20)
.width(2)
.color('#666')
.margin({ left: 20, right: 20 })
Text('用户协议').fontColor('#007dff').fontSize(14)
.onClick(() => {
let u =
getContext(this).resourceManager.getStringValue($r("app.string.terms_use_url").id, (error, value) => {
if (error != null) {
console.log("error is " + error);
} else {
this.openWebUrl(value, "用户协议");
console.log("openWebUrl is " + value);
}
});
})
Divider()
.vertical(true)
.height(20)
.width(2)
.color('#666')
.margin({ left: 20, right: 20 })
Text('隐私声明').fontColor('#007dff').fontSize(14)
.onClick(() => {
let u =
getContext(this)
.resourceManager
.getStringValue($r("app.string.privacy_agreement_url").id, (error, value) => {
if (error != null) {
console.log("error is " + error);
} else {
this.openWebUrl(value, "隐私声明");
console.log("openWebUrl is " + value);
}
});
})
}
}
.width("100%")
.height('100%')
.padding(20)
}
/**
* Get data preferences action.
*/
getDataPreferences(common: Object) {
return preferences.getPreferences(getContext(common), CommonConstants.PREFERENCES_FILE_NAME);
}
saveLogin(user: LoginResult) {
GlobalContext.getContext().isLogin = true;
GlobalContext.getContext().userId = user.uid;
GlobalContext.getContext().setObject('user', user);
let preferences: Promise<preferences.Preferences> = this.getDataPreferences(this);
preferences.then((result: preferences.Preferences) => {
let privacyPut = result.put(CommonConstants.LOGIN_USER_KEY, user);
result.flush();
privacyPut.then(() => {
Logger.info(CommonConstants.LOGIN_PAGE_TAG, 'Put the value of startup Successfully.');
}).catch((err: Error) => {
Logger.error(CommonConstants.LOGIN_PAGE_TAG, 'Put the value of startup Failed, err: ' + err);
});
}).catch((err: Error) => {
Logger.error(CommonConstants.LOGIN_PAGE_TAG, 'Get the preferences Failed, err: ' + err);
});
}
openWebUrl(url: string, title?: string) {
router.pushUrl({
url: 'pages/WebViewPage',
params: {
title: title,
url: url // 传递的 URL 参数
}
}, router.RouterMode.Single)
}
}
注册页面
import { promptAction, router } from '@kit.ArkUI';
import { http } from '@kit.NetworkKit';
import CommonConstants from '../../utils/CommonConstants';
import Logger from '../../utils/Logger';
import { util } from '@kit.ArkTS';
import HelperUtil from '../../utils/HelperUtil';
import { CommonDialog } from '../../utils/CommonDialog';
import { CustomResult, LoginResult, RegisterParams, RegisterResult } from '../Model/UserModel';
import { HdNav } from '../../common/HdNav';
import { systemDateTime } from '@kit.BasicServicesKit';
import { preferences } from '@kit.ArkData';
import { GlobalContext } from '../../utils/GlobalContext';
import RegExpUtils from '../../utils/RegExpUtils';
interface ParamsType {
label: string
}
@Entry
@Component
struct Register {
@State countCheckCodeSeconds: number = 60;
private timeId: number = 0;
@State isCodeLoginShow: boolean = false
@State addressLabel: string = '中国 +86'
title: string = '注册';
@State isAcceptProtocol: boolean = false;
@State getCheckCode: string = '获取验证码';
@State isNext: boolean = false;
@State par: RegisterParams = {
} as RegisterParams;
onPageShow(): void {
if (router.getParams() !== undefined) {
this.addressLabel = (router.getParams() as ParamsType).label
}
}
build() {
Column() {
HdNav({
title: this.title
})
Text($r("app.string.app_name")).fontSize(28).fontWeight(FontWeight.Bold).margin({ top: 15, bottom: 15 })
if (!this.isNext) {
// 短信验证码登录
TextInput({
placeholder: '手机号'
})
.onChange((value) => {
//手机号
this.par.mobile = value;
})
.maxLength(11)
.type(InputType.PhoneNumber)
Row({ space: 10 }) {
TextInput({
placeholder: '短信验证码'
})
.onChange((value) => {
this.par.code = value;
})
.width('60%')
.type(InputType.Number)
Button(this.getCheckCode)
.buttonStyle(ButtonStyleMode.NORMAL)
.onClick(() => {
if (!RegExpUtils.checkPhone(this.par.mobile)) {
return
}
//验证是否注册过
this.isRegisterRequest(this.par.mobile);
})
.enabled(this.getCheckCode == "获取验证码")
}
.margin({ top: 20, bottom: 20 })
.width('100%')
Row() {
Radio({
group: "",
value: '同意'
}).onClick(() => {
this.isAcceptProtocol = !this.isAcceptProtocol;
}).checked(this.isAcceptProtocol);
Text("我已阅读并同意").fontSize(14)
Text('用户协议').fontColor('#007dff').fontSize(14)
.onClick(() => {
let u =
getContext(this).resourceManager.getStringValue($r("app.string.terms_use_url").id, (error, value) => {
if (error != null) {
console.log("error is " + error);
} else {
this.openWebUrl(value);
console.log("openWebUrl is " + value);
}
});
})
Text("和").fontSize(14)
Text('隐私声明').fontColor('#007dff').fontSize(14)
.onClick(() => {
let u =
getContext(this)
.resourceManager
.getStringValue($r("app.string.privacy_agreement_url").id, (error, value) => {
if (error != null) {
console.log("error is " + error);
} else {
this.openWebUrl(value);
console.log("openWebUrl is " + value);
}
});
})
}.margin({ bottom: 20 })
Button('下一步')
.width('100%')
.onClick(() => {
this.checkmobileCodeRequest(this.par);
})
.margin({ bottom: 15 })
.enabled(this.isAcceptProtocol && this.par.code?.length == 6 && this.par.mobile?.length == 11)
Blank()
} else { //next
TextInput({
text: "",
placeholder: '请填写登录账号,长度3~15字符'
})
.maxLength(15)
.type(InputType.USER_NAME)
.onChange((value) => {
this.par.username = value;
})
TextInput({
placeholder: '请填写密码,长度6~15字符'
})
.maxLength(15)
.type(InputType.Password)
.margin({ top: 20, bottom: 30 })
.onChange((value) => {
this.par.password = value;
})
Text("填写用户名和密码即可完成注册")
.fontSize(16)
.margin({ bottom: 10 })
Text("注意:初始密码默认为手机号后 6 位,也可在当前页面进行设定")
.fontColor(Color.Red)
.fontSize(14)
.margin({ top: 10, bottom: 30 })
Button('提交')
.width('100%')
.onClick(() => {
//注册
this.registerRequest(this.par);
})
.margin({ bottom: 15 })
.enabled(this.par.username?.length >= 3 && this.par.password?.length >= 6)
Blank()
}
}
.width("100%")
.height('100%')
.padding(20)
}
/**
* 是否已注册
* @param userphone
* @returns
*/
isRegisterRequest(userphone: string) {
let url = '';
let httpRequest = http.createHttp();
httpRequest.request(
url
).then((res) => {
Logger.info(CommonConstants.REGISTER_TAG, '请求结果: ' + res.result as string)
let rt = JSON.parse(res.result as string) as CustomResult; //total
let message = "";
if (rt.result == "-1") {
HelperUtil.showToast("该手机号已注册,请换一个手机号!")
} else {
this.mobileCodeRequest(this.par.mobile);
//请求验证码
this.getCheckCode = this.countCheckCodeSeconds.toString() + "s后重发";
this.timeId = setInterval(() => {
if (this.countCheckCodeSeconds == 0) {
this.getCheckCode = "获取验证码";
//可操作
} else {
this.countCheckCodeSeconds--;
this.getCheckCode = this.countCheckCodeSeconds.toString() + "s后重发";
}
}, CommonConstants.ADVERTISING_INTERVAL_TIME);
}
}).catch((err: Error) => {
HelperUtil.showToast("请求服务异常:" + err.message)
Logger.error(CommonConstants.REGISTER_TAG, JSON.stringify(err));
})
.finally(() => {
httpRequest.destroy()
});
}
async mobileCodeRequest(userphone: string) {
let timeSplit = systemDateTime.getTime().toString().substring(0, 7);
let sign = await HelperUtil.md5(timeSplit + "mudeguo" + userphone);
let url =
``;
console.log("验证码: " + url);
let httpRequest = http.createHttp();
httpRequest.request(
url
).then((res) => {
Logger.info(CommonConstants.REGISTER_TAG, '请求结果: ' + res.result as string)
let rt = JSON.parse(res.result as string) as CustomResult; //total
if (rt.result == "1") {
HelperUtil.showToast('手机号验证码已发送');
this.par.identifier = rt.identifier;
} else {
HelperUtil.showToast('获取验证码失败 ' + rt.message)
this.par.identifier = "";
}
}).catch((err: Error) => {
HelperUtil.showToast("请求服务异常:" + err.message)
Logger.error(CommonConstants.REGISTER_TAG, JSON.stringify(err));
})
.finally(() => {
httpRequest.destroy()
})
return false;
}
/**
* 校验验证码
* @param par
* @returns
*/
async checkmobileCodeRequest(par: RegisterParams) {
let url =
``;
console.log("验证码: " + url);
let httpRequest = http.createHttp();
httpRequest.request(
url
).then((res) => {
Logger.info(CommonConstants.REGISTER_TAG, '请求结果: ' + res.result as string)
let rt = JSON.parse(res.result as string) as CustomResult; //total
if (rt.result == "1") {
//随机生成用户名和密码
this.par.username =
this.par.mobile; //"iyy" + (Math.floor(Math.random() * 9100) + 1000) + this.par.mobile.slice(-4);
//截取密码
this.par.password = this.par.mobile.slice(-6);
this.isNext = true;
} else {
HelperUtil.showToast('验证码校验失败 ' + rt.message)
}
}).catch((err: Error) => {
HelperUtil.showToast("请求服务异常:" + err.message)
Logger.error(CommonConstants.REGISTER_TAG, JSON.stringify(err));
})
.finally(() => {
httpRequest.destroy()
})
return false;
}
/**
* Get data preferences action.
*/
getDataPreferences(common: Object) {
return preferences.getPreferences(getContext(common), CommonConstants.PREFERENCES_FILE_NAME);
}
saveLogin(user: LoginResult) {
GlobalContext.getContext().isLogin = true;
GlobalContext.getContext().userId = user.uid;
GlobalContext.getContext().setObject('user', user);
let preferences: Promise<preferences.Preferences> = this.getDataPreferences(this);
preferences.then((result: preferences.Preferences) => {
let privacyPut = result.put(CommonConstants.LOGIN_USER_KEY, user);
result.flush();
privacyPut.then(() => {
Logger.info(CommonConstants.LOGIN_PAGE_TAG, 'Put the value of startup Successfully.');
}).catch((err: Error) => {
Logger.error(CommonConstants.LOGIN_PAGE_TAG, 'Put the value of startup Failed, err: ' + err);
});
}).catch((err: Error) => {
Logger.error(CommonConstants.LOGIN_PAGE_TAG, 'Get the preferences Failed, err: ' + err);
});
}
/**
* 注册
* @param par
* @returns
*/
async registerRequest(par: RegisterParams): Promise<void> {
console.log(" 用户名 " + par.username);
if (HelperUtil.isEmpty(par.username)) {
HelperUtil.showToast("请输入登录用户名!");
return;
}
if (HelperUtil.isEmpty(par.password)) {
HelperUtil.showToast("请输入登录用户密码!");
return;
}
HelperUtil.digest(new util.TextEncoder().encodeInto(par.password));
let username = encodeURI(par.username);
let pwd = await HelperUtil.md5(par.password);
let url ='';
console.log("注册地址: " + url)
let httpRequest = http.createHttp();
httpRequest.request(
url
).then((res) => {
Logger.info(CommonConstants.REGISTER_TAG, '请求结果: ' + res.result as string)
let rt = JSON.parse(res.result as string) as LoginResult; //RegisterResult; //total
let message = "";
switch (rt.result) {
case "111":
message = "注册成功";
this.saveLogin(rt);
HelperUtil.showToast(message);
//需要存储用户的uid,username,等信息到本地,
router.pushUrl({
url: 'pages/User/Login',
params: {
username: rt.username,
}
}, router.RouterMode.Single)
return;
case "θ":
message = "服务器错误";
break;
case "00":
message = "内部处理错误";
break;
case "800":
message = "用户名/密码未填写";
break;
case "104":
message = "邮箱非法";
break;
case "110":
message = "服务器错误";
break;
case "112":
message = "用户名已被注册,请更换其他用户名";
break;
case "113":
message = "邮箱已被注册";
break;
case "114":
message = "用户名过长/过短,请输入3-15位字符";
break;
case "115":
message = "该手机号已被注册";
break;
default:
message = " 注册失败";
}
HelperUtil.showToast(rt.result + message);
})
.catch((err: Error) => {
HelperUtil.showToast("请求服务异常:" + err.message)
Logger.error(CommonConstants.REGISTER_TAG, JSON.stringify(err));
})
.finally(() => {
httpRequest.destroy()
})
}
openWebUrl(url: string, title?: string) {
router.pushUrl({
url: 'pages/WebViewPage',
params: {
title: title,
url: url // 传递的 URL 参数
}
}, router.RouterMode.Single)
}
}
首页面
import { getActivityList, getAdvertisements, getInfoNews } from '../../common/api/atomApi';
import { ActivityItem, BannerItem, NewsItem } from '../../common/bean/apiTypes';
import { BusinessError } from '@kit.BasicServicesKit';
import { router } from '@kit.ArkUI';
import Tabs0 from "./Tabs0";
import Tabs1 from "./Tabs1";
import Logger from '../../utils/Logger';
import CommonConstants from '../../utils/CommonConstants';
class BasicDataSource<T> implements IDataSource {
private listeners: DataChangeListener[] = [];
private originDataArray: T[] = [];
totalCount(): number {
return this.originDataArray.length;
}
getData(index: number): T {
return this.originDataArray[index];
}
registerDataChangeListener(listener: DataChangeListener): void {
if (this.listeners.indexOf(listener) < 0) {
this.listeners.push(listener);
}
}
unregisterDataChangeListener(listener: DataChangeListener): void {
const pos = this.listeners.indexOf(listener);
if (pos >= 0) {
this.listeners.slice(pos, 1);
}
}
// 通知LazyForEach组件需要重新重载所有子组件
notifyDataReload(): void {
this.listeners.forEach(listener => {
listener.onDataReloaded();
})
}
// 通知LazyForEach组件需要在index对应索引处添加子组件
notifyDataAdd(index: number): void {
this.listeners.forEach(listener => {
listener.onDataAdd(index);
})
}
}
class SwiperDataSource<T> extends BasicDataSource<T> {
private dataArray: T[] = [];
totalCount(): number {
return this.dataArray.length;
}
getData(index: number): T {
return this.dataArray[index];
}
// 在列表末尾添加数据并通知监听器
pushData(data: T): void {
this.dataArray.push(data);
this.notifyDataAdd(this.dataArray.length - 1);
}
// 重载数据
reloadData(): void {
// 不会引起状态变化
this.dataArray = [];
// 必须通过DataChangeListener来更新
this.notifyDataReload();
}
}
interface StyleItem {
id:number;
name: string;
pic: string;
}
@Component
export default struct Home {
@Consume pageStack: NavPathStack
private swiperController: SwiperController = new SwiperController()
private swiperData: SwiperDataSource<BannerItem> = new SwiperDataSource()
@State newsList:NewsItem[] = []
@State currentIndex: number = 0
@Builder tabBuilder(title: string, index: number) {
Column() {
Text(title)
.fontColor(this.currentIndex === index ? '#0A59F7' : '#E6000000')
.fontSize(16)
.fontWeight(this.currentIndex === index ? FontWeight.Normal : FontWeight.Medium)
.lineHeight(22)
.margin({ top: 0, bottom: 7 })
Divider()
.width(48)
.strokeWidth(2)
.color('#0A59F7')
.opacity(this.currentIndex === index ? 1 : 0)
}
}
// 组件生命周期
aboutToAppear() {
Logger.info('Home aboutToAppear');
getAdvertisements(5,0).then((res) => {
Logger.debug(CommonConstants.HOME_PAGE_TAG,res.data.code.toString())
for (const itm of res.data.data) {
this.swiperData.pushData(itm)
}
}).catch((err:BusinessError) => {
Logger.debug("request","err.code:%d",err.code.toString())
Logger.debug("request",err.message)
});
}
// 组件生命周期
aboutToDisappear() {
Logger.info('Home aboutToDisappear');
}
build() {
Scroll() {
Column({ space: 0 }) {
//title
Text('AtomGit客户端')
.fontSize(26)
.fontWeight(FontWeight.Bold)
.textAlign(TextAlign.Center)
.width('100%')
.height(50)
// 轮播图
Swiper(this.swiperController) {
LazyForEach(this.swiperData, (item: BannerItem) => {
Stack({ alignContent: Alignment.Center }) {
Image(item.picUrl)
.width('100%')
.height(180)
.backgroundColor(0xAFEEEE)
.zIndex(1)
.onClick(() => {
router.pushUrl({
url: 'pages/WebViewPage',
params: { url: item.jumpUrl }
});
})
// 显示轮播图标题
Text(item.operateName)
.padding(5)
.margin({ top: 135 })
.width('100%')
.height(60)
.textAlign(TextAlign.Center)
.maxLines(2)
.textOverflow({ overflow: TextOverflow.Clip })
.fontSize(20)
.fontColor(Color.Yellow)
.opacity(100)// 设置标题的透明度 不透明度设为100%,表示完全不透明
.backgroundColor('#808080AA')// 背景颜色设为透明
.zIndex(2)
.onClick(() => {
router.pushUrl({
url: 'pages/WebViewPage',
params: { url: item.jumpUrl }
});
})
}
}, (item: BannerItem) => item.operateId.toString())
}
.cachedCount(2)
.index(1)
.autoPlay(true)
.interval(4000)
.loop(true)
.indicatorInteractive(true)
.duration(1000)
.itemSpace(0)
.curve(Curve.Linear)
.onChange((index: number) => {
console.info(index.toString())
})
.onGestureSwipe((index: number, extraInfo: SwiperAnimationEvent) => {
console.info("index: " + index)
console.info("current offset: " + extraInfo.currentOffset)
})
.height(180) // 设置高度
Tabs({ barPosition: BarPosition.Start }) {
TabContent() {
// list组件
Tabs0()
}.tabBar(this.tabBuilder('推荐', 0))
TabContent() {
Tabs1()
}.tabBar(this.tabBuilder('最新', 1))
TabContent() {
}.tabBar(this.tabBuilder('仓库', 2))
TabContent() {
}.tabBar(this.tabBuilder('组织', 3))
}
.animationDuration(0)
.onChange((index: number) => {
this.currentIndex = index
})
}
}
}
}
完整展示效果
- Doc: 展示效果
开源协议
- 协议选择:本项目采用MIT协议,允许用户自由使用、复制、修改和分发本软件,但需包含版权声明和许可声明。
- 版权声明:版权所有者为冯腾飞,使用者在使用过程中需遵守MIT协议规定,未经允许不得移除或修改版权声明。
HAP 包
- 目录位于:hap/entry-default-unsigned.hap
完整项目地址
©著作权归作者所有,如需转载,请注明出处,否则将追究法律责任
赞
1
收藏 1
回复
相关推荐
欢迎文章末尾下载源码
欢迎关注讨论技术问题