鸿蒙NEXT项目实战-百得知识库01
代码仓地址,大家记得点个star
IbestKnowTeach: 百得知识库基于鸿蒙NEXT稳定版实现的一款企业级开发项目案例。 本案例涉及到多个鸿蒙相关技术知识点: 1、布局 2、配置文件 3、组件的封装和使用 4、路由的使用 5、请求响应拦截器的封装 6、位置服务 7、三方库的使用和封装 8、头像上传 9、应用软件更新等https://gitee.com/xt1314520/IbestKnowTeach
项目准备
项目介绍
“百得知识库软件”是一款专注于编程技术领域的学习资源App,旨在为开发者提供丰富的学习材料。这里涵盖了广泛的IT相关知识,无论是编程语言、软件开发,还是最新的技术趋势,都能找到详尽的资料。该软件不仅有助于开发者提升专业技能,还通过完善的社区和文档支持促进交流与合作。用户可以轻松获取解决方案,参与讨论,从而加速个人成长和技术问题的解决。
通过项目学到什么
- 华为账号授权登录
- 三方库的使用和封装
- 组件的封装和使用
- 头像上传
- 应用软件更新
- 日历组件的使用
- 地理位置定位
......
创建项目
1、点击文件,新建项目
2、选择模板
修改项目名,包名,项目名等信息
3、完成创建
4、打开模拟器,运行项目
5、修改应用名称和图标
1、在跟目录下面AppScope/resources/base/media替换app_icon图片
2、修改module.json5文件
替换成app_icon
3、修改应用名称
将label修改成百得知识库
导入静态资源
1、resources/base/element
📎color.json
📎float.json
📎string.json
2、resources/en_US/element
📎string.json
3、resources/zh_CN/element
📎string.json
4、百得知识库里面的静态图片资源
https://download.csdn.net/download/weixin_51166786/90426858
5、修改module.json5里面的startWindowIcon里面的值
"startWindowIcon": "$media:start_icon"
项目三方库依赖
三方库官方地址:
OpenHarmony三方库中心仓
1、@ohos/axios (网络请求三方库)
ohpm install @ohos/axios@2.2.0
2、@ibestservices/ibest-ui (组件三方库)
ohpm install @ibestservices/ibest-ui@2.0.3
日志的封装
在ets下面新建utils目录,然后在这个目录下面新建Logger.ets
import { hilog } from '@kit.PerformanceAnalysisKit'
const DOMAIN = 0x0000
const TAG = 'wdl'
const FORMAT = '%{public}s %{public}s'
export class Logger {
static debug(...args: string[]) {
hilog.debug(DOMAIN, TAG, FORMAT, args)
}
static info(...args: string[]) {
hilog.info(DOMAIN, TAG, FORMAT, args)
}
static warn(...args: string[]) {
hilog.warn(DOMAIN, TAG, FORMAT, args)
}
static error(...args: string[]) {
hilog.error(DOMAIN, TAG, FORMAT, args)
}
}
文本提示框的封装
在ets/utils目录下面新建Toast.ets
import promptAction from '@ohos.promptAction'
/**
* 显示toast
* @param { string } message 显示的信息
*/
export function showToast(message: string) {
promptAction.showToast({
message: message || '请求错误',
duration: 2000,
})
}
用户首选项的封装
在ets/utils目录下面新建PreferencesUtil.ets
import { preferences } from '@kit.ArkData'
export class PreferencesUtil {
/**
* 保存数据到首选项
* @param preferencesName
* @param key
* @param value
*/
static async savaData<T extends keyof number | number | string | boolean | Array<number> | Array<string> | Array<boolean>>(preferencesName: string,
key: string, value: T) {
const pre = preferences.getPreferencesSync(getContext(), { name: preferencesName })
pre.putSync(key, value as preferences.ValueType)
await pre.flush()
}
/**
* 获取数据
* @param preferencesName
* @param key
* @param defaultValue
* @returns
*/
static getData<T extends keyof number | number | string | boolean | Array<number> | Array<string> | Array<boolean>>(preferencesName: string,
key: string, defaultValue: T) {
const pre = preferences.getPreferencesSync(getContext(), { name: preferencesName })
return pre.getSync(key, defaultValue as preferences.ValueType) as T
}
/**
* 删除数据
* @param preferencesName
* @param key
*/
static async delAllData(preferencesName: string, key: string) {
const pre = preferences.getPreferencesSync(getContext(), { name: preferencesName })
pre.deleteSync(key)
await pre.flush()
}
}
新建常量类
在ets/contants目录下面新建CommonConstant.ets和RouterConstant.ets
1、CommonConstant.ets
export class CommonConstant {
/**
* 百分百宽度
*/
static readonly WIDTH_FULL = '100%'
/**
* 百分百高度
*/
static readonly HEIGHT_FULL = '100%'
/**
* 开屏广告页面默认展示时间,单位秒
*/
static readonly ADVERTISING_TIME: number = 3;
/**
* 结束时间,单位秒
*/
static readonly ADVERTISING_END_TIME: number = 0;
/**
* 存token值的名称(token)
*/
static readonly TOKEN_NAME: string = 'token';
/**
* 用户首选项实例的名称
*/
static readonly PREFERENCES_NAME: string = 'preferences';
/**
* 存用户信息的本地存储名称
*/
static readonly USER_INFO: string = 'userInfo';
/**
* 登录错误
*/
static readonly DEFAULT_LOGIN_ERROR: string = '登录错误';
}
2、RouterConstant.ets
export class RouterConstant {
/**
* 首页
*/
static readonly PAGE_INDEX = 'pages/Index'
/**
* 登录
*/
static readonly PAGE_LOGIN = 'pages/LoginPage'
/**
* 打卡
*/
static readonly VIEWS_CLOCK = 'views/Learn/Clock'
/**
* 打卡记录
*/
static readonly VIEWS_CLOCK_RECORD = 'views/Learn/ClockRecord'
/**
* 学习目标
*/
static readonly VIEWS_LEARN_TARGET = 'views/Learn/LearnTarget'
/**
* 学习工具
*/
static readonly VIEWS_LEARN_TOOL = 'views/Learn/LearnTool'
/**
* 学习平台内容
*/
static readonly VIEWS_LEARN_CONTENT = 'views/Learn/LearnContent'
/**
* 面试平台内容
*/
static readonly VIEWS_INTERVIEW_CONTENT = 'views/Learn/InterviewContent'
/**
* home页面
*/
static readonly VIEWS_HOME_HOME = 'views/Home/Home'
/**
* 文章详情页面
*/
static readonly VIEWS_HOME_ARTICLE_INFO = 'views/Home/ArticleInfo'
/**
* home搜索页面
*/
static readonly VIEWS_HOME_SEARCH = 'views/Home/Search'
/**
* 消息列表页面
*/
static readonly VIEWS_MESSAGE_LIST = 'views/Message/MessageList'
/**
* 个人信息
*/
static readonly VIEWS_MINE_INFO = 'views/Mine/MineInfo'
/**
* 我的收藏
*/
static readonly VIEWS_MINE_COLLECT = 'views/Mine/MineCollect'
/**
* 我的点赞
*/
static readonly VIEWS_MINE_LIKE = 'views/Mine/MineLike'
/**
* 关于我们
*/
static readonly VIEWS_MINE_ABOUT_US = 'views/Mine/AboutUS'
/**
* 设置
*/
static readonly VIEWS_MINE_SET_UP = 'views/Mine/SetUp'
/**
* 隐私政策
*/
static readonly PAGE_PRIVACY_POLICY = 'pages/PrivacyPolicyPage'
/**
* 用户协议
*/
static readonly PAGE_USER_POLICY = 'pages/UserPolicyPage'
}
请求响应拦截器的封装
在ets下面新建request目录,然后在这个目录下面新建Request.ets和Request.type.ets
1、Request.ets
import axios, { AxiosError, AxiosResponse, InternalAxiosRequestConfig } from '@ohos/axios'
import { CODE_TYPE, BASE_HOST } from "./Request.type"
import { Logger } from '../utils/Logger';
import { showToast } from '../utils/Toast';
import { CommonConstant } from '../contants/CommonConstant';
import { PreferencesUtil } from '../utils/PreferencesUtil';
import { router } from '@kit.ArkUI';
import { RouterConstant } from '../contants/RouterConstant';
/**
* axios封装
*/
const http = axios.create({
baseURL: `http://${BASE_HOST}`,
headers: {
'Content-Type': 'application/json',
"Channel": "B2B"
}
},)
/**
* 添加请求拦截器
*/
http.interceptors.request.use((config: InternalAxiosRequestConfig) => {
// 获取数据
const token: string | undefined = AppStorage.get("token")
// 获取token
if (token) {
config.headers.Authorization = token
}
return config;
}, (error: AxiosError) => {
// 对请求错误做些什么
return Promise.reject(error);
});
/**
* 添加响应拦截器
*/
http.interceptors.response.use((response: AxiosResponse) => {
Logger.info("请求状态码" + response.data.code, JSON.stringify(response.data));
// 判断响应状态码
if (response.status === CODE_TYPE.SUCCESS) {
// 请求成功
if (response.data.code === CODE_TYPE.SUCCESS) {
return Promise.resolve(response.data.data);
} else if (response.data.code === CODE_TYPE.NO_LOGIN) {
clearLoginInfoAndGoLoginPage();
}
}
// 接口响应报错新信息
showToast(response.data.message);
return Promise.reject(response);
}, (error: AxiosError) => {
showToast('网络错误,换个网络试试');
return Promise.reject(error);
});
export default http;
/**
* 清除用户信息并跳到登录页面
*/
async function clearLoginInfoAndGoLoginPage() {
// 401错误 -> 清理用户信息,跳转到登录页
// 清理token,返回登录页
// userInfo有订阅者删不掉,所以重新赋值空的给userInfo
AppStorage.setOrCreate(CommonConstant.USER_INFO, {
nickname: '',
unionId: '',
avatarUri: '',
id: 0
})
AppStorage.delete(CommonConstant.TOKEN_NAME)
await PreferencesUtil.delAllData(CommonConstant.PREFERENCES_NAME, CommonConstant.TOKEN_NAME)
await PreferencesUtil.delAllData(CommonConstant.PREFERENCES_NAME, CommonConstant.USER_INFO)
// 跳转登录页面
router.pushUrl({
url: RouterConstant.PAGE_LOGIN
})
}
2、Request.type.ets
export enum CODE_TYPE {
SUCCESS = 200,
NO_LOGIN = 401
}
/**
* host地址
*/
export const BASE_HOST = '118.31.50.145:9003'
服务端接口文档地址
api.md