鸿蒙NEXT项目实战-百得知识库04
代码仓地址,大家记得点个star
IbestKnowTeach: 百得知识库基于鸿蒙NEXT稳定版实现的一款企业级开发项目案例。 本案例涉及到多个鸿蒙相关技术知识点: 1、布局 2、配置文件 3、组件的封装和使用 4、路由的使用 5、请求响应拦截器的封装 6、位置服务 7、三方库的使用和封装 8、头像上传 9、应用软件更新等https://gitee.com/xt1314520/IbestKnowTeach
我的页面开发
设计图
需求分析
华为账号登录功能开发(需要真机)
注意:这个功能需要真机,所以东林在gitee代码仓里面的代码是普通账号密码登录得,没有使用华为账号登录
我们之前封装的响应拦截器里面有判断,根据接口返回的状态码判断是否有权限,如果没有权限会跳转到登录页面
我们也可以点击我的页面头像区域可以到登录页面
1、登录界面整体布局
从上到下的整体布局所以我们使用Column进行包裹组件,整体可以拆分出五块区域
2、刨去华为账号登录的代码
import { CommonConstant } from '../contants/CommonConstant';
import { router } from '@kit.ArkUI';
import { RouterConstant } from '../contants/RouterConstant';
@Entry
@Component
struct LoginPage {
// 多选框状态
@State multiSelectStatus: boolean = false
build() {
Column() {
Column({ space: 15 }) {
Image($r('app.media.app_icon'))
.width($r('app.float.common_width_big')).aspectRatio(1)
.borderRadius(15)
Text($r('app.string.application_name'))
.fontSize($r('app.float.common_font_size_huge'))
.fontWeight(FontWeight.Medium)
Text($r('app.string.app_description'))
.fontSize($r('app.float.common_font_size_small'))
.fontColor($r('app.color.common_gray'))
// 用户协议和隐私协议
Row() {
Checkbox()
.select($$this.multiSelectStatus)
.width(18)
.selectedColor("#FA6D1D")
Text() {
Span("已阅读并同意")
Span(" 用户协议 ")
.fontColor(Color.Black)
.onClick(() => {
router.pushUrl({ url: RouterConstant.PAGE_USER_POLICY })
})
Span("和")
Span(" 隐私政策 ")
.fontColor(Color.Black)
.onClick(() => {
router.pushUrl({ url: RouterConstant.PAGE_PRIVACY_POLICY })
})
}.fontSize($r('app.float.common_font_size_small'))
.fontColor($r('app.color.common_gray'))
}
}.height('50%')
}
.padding($r('app.float.common_padding'))
.height(CommonConstant.HEIGHT_FULL)
.width(CommonConstant.WIDTH_FULL)
.justifyContent(FlexAlign.Center)
.backgroundImage($r('app.media.background'))
.backgroundImageSize({ width: CommonConstant.WIDTH_FULL, height: CommonConstant.HEIGHT_FULL })
.expandSafeArea([SafeAreaType.SYSTEM], [SafeAreaEdge.TOP])
}
}
3、集成华为账号登录
参考东林的鸿蒙应用开发-高级课里面有个章节叫华为账号服务
大概分为以下几个步骤
1、在AGC上创建项目和应用
2、本地创建应用工程
3、本地生成签名文件
4、将签名放在AGC上生成证书
5、复制Client_ID放在本地项目中
6、本地完成签名
import { LoginWithHuaweiIDButton, loginComponentManager } from '@kit.AccountKit';
import { BusinessError } from '@kit.BasicServicesKit';
import { CommonConstant } from '../contants/CommonConstant';
import { router } from '@kit.ArkUI';
import { RouterConstant } from '../contants/RouterConstant';
import { authentication } from '@kit.AccountKit';
import { util } from '@kit.ArkTS';
import { Logger } from '../utils/Logger';
import { showToast } from '../utils/Toast';
import userApi from '../api/UserApi';
import { PreferencesUtil } from '../utils/PreferencesUtil';
@Entry
@Component
struct LoginPage {
// 多选框状态
@State multiSelectStatus: boolean = false
// 构造LoginWithHuaweiIDButton组件的控制器
controller: loginComponentManager.LoginWithHuaweiIDButtonController =
new loginComponentManager.LoginWithHuaweiIDButtonController()
.onClickLoginWithHuaweiIDButton((error: BusinessError, response: loginComponentManager.HuaweiIDCredential) => {
// 判断是否勾选用户协议和隐私政策
if (!this.multiSelectStatus) {
showToast('请勾选用户协议和隐私政策')
return
}
if (error) {
Logger.error(`Failed to onClickLoginWithHuaweiIDButton. Code: ${error.code}, message: ${error.message}`);
showToast('华为账号登录失败')
return;
}
// 登录成功
if (response) {
Logger.info('Succeeded in getting response.')
// 创建授权请求,并设置参数
const authRequest = new authentication.HuaweiIDProvider().createAuthorizationWithHuaweiIDRequest();
// 获取头像昵称需要传如下scope
authRequest.scopes = ['profile'];
// 用户是否需要登录授权,该值为true且用户未登录或未授权时,会拉起用户登录或授权页面
authRequest.forceAuthorization = true;
// 用于防跨站点请求伪造
authRequest.state = util.generateRandomUUID();
// 执行授权请求
try {
const controller = new authentication.AuthenticationController(getContext(this));
controller.executeRequest(authRequest).then((data) => {
const authorizationWithHuaweiIDResponse = data as authentication.AuthorizationWithHuaweiIDResponse;
const state = authorizationWithHuaweiIDResponse.state;
if (state != undefined && authRequest.state != state) {
Logger.error(`Failed to authorize. The state is different, response state: ${state}`);
showToast('华为账号登录失败')
return;
}
Logger.info('Succeeded in authentication.');
const authorizationWithHuaweiIDCredential = authorizationWithHuaweiIDResponse.data!;
// 头像
const avatarUri = authorizationWithHuaweiIDCredential.avatarUri;
// 昵称
const nickName = authorizationWithHuaweiIDCredential.nickName;
// 唯一id
const unionID = authorizationWithHuaweiIDCredential.unionID;
// 登录接口
login(unionID, nickName, avatarUri)
}).catch((err: BusinessError) => {
showToast('华为账号登录失败')
Logger.error(`Failed to auth. Code: ${err.code}, message: ${err.message}`);
});
} catch (error) {
showToast('华为账号登录失败')
Logger.error(`Failed to auth. Code: ${error.code}, message: ${error.message}`);
}
}
});
build() {
Column() {
Column({ space: 15 }) {
Image($r('app.media.app_icon'))
.width($r('app.float.common_width_big')).aspectRatio(1)
.borderRadius(15)
Text($r('app.string.application_name'))
.fontSize($r('app.float.common_font_size_huge'))
.fontWeight(FontWeight.Medium)
Text($r('app.string.app_description'))
.fontSize($r('app.float.common_font_size_small'))
.fontColor($r('app.color.common_gray'))
// 用户协议和隐私协议
Row() {
Checkbox()
.select($$this.multiSelectStatus)
.width(18)
.selectedColor("#FA6D1D")
Text() {
Span("已阅读并同意")
Span(" 用户协议 ")
.fontColor(Color.Black)
.onClick(() => {
router.pushUrl({ url: RouterConstant.PAGE_USER_POLICY })
})
Span("和")
Span(" 隐私政策 ")
.fontColor(Color.Black)
.onClick(() => {
router.pushUrl({ url: RouterConstant.PAGE_PRIVACY_POLICY })
})
}.fontSize($r('app.float.common_font_size_small'))
.fontColor($r('app.color.common_gray'))
}
}.height('50%')
Column() {
LoginWithHuaweiIDButton({
params: {
// LoginWithHuaweiIDButton支持的样式
style: loginComponentManager.Style.BUTTON_RED,
// 账号登录按钮在登录过程中展示加载态
extraStyle: {
buttonStyle: new loginComponentManager.ButtonStyle().loadingStyle({
show: true
})
},
// LoginWithHuaweiIDButton的边框圆角半径
borderRadius: 24,
// LoginWithHuaweiIDButton支持的登录类型
loginType: loginComponentManager.LoginType.ID,
// LoginWithHuaweiIDButton支持按钮的样式跟随系统深浅色模式切换
supportDarkMode: true,
// verifyPhoneNumber:如果华为账号用户在过去90天内未进行短信验证,是否拉起Account Kit提供的短信验证码页面
verifyPhoneNumber: true,
},
controller: this.controller,
})
}.width('80%')
.height(40)
}
.padding($r('app.float.common_padding'))
.height(CommonConstant.HEIGHT_FULL)
.width(CommonConstant.WIDTH_FULL)
.justifyContent(FlexAlign.Center)
.backgroundImage($r('app.media.background'))
.backgroundImageSize({ width: CommonConstant.WIDTH_FULL, height: CommonConstant.HEIGHT_FULL })
.expandSafeArea([SafeAreaType.SYSTEM], [SafeAreaEdge.TOP])
}
}
/**
* 登录
* @param unionID
* @param nickname
* @param avatarUri
*/
async function login(unionID?: string, nickname?: string, avatarUri?: string) {
if (!nickname) {
nickname = '小得'
}
if (!avatarUri) {
avatarUri =
'https://upfile-drcn.platform.hicloud.com/DT4ISbQduGIF5Gz5g_Z9yg.PCj5oenfVYPRaeJp1REFEyac5ctfyoz-bD3L3k5cJTIDkrfDyewIkQaOAEoTWdgIxA_sJ0DD5RITPB85tfWAF7oquqQ6AvE4Jt8dIRUoyic4djriMA.112968985.jpg'
}
// 调用服务端登录
const token = await userApi.userLogin({ unionId: unionID, nickname: nickname, avatarUri: avatarUri });
// 如果token存在
if (token) {
AppStorage.setOrCreate(CommonConstant.TOKEN_NAME, token)
PreferencesUtil.savaData(CommonConstant.PREFERENCES_NAME, CommonConstant.TOKEN_NAME, token)
// 获取用户信息
const userInfo = await userApi.getUserInfo();
if (!userInfo) {
showToast(CommonConstant.DEFAULT_LOGIN_ERROR)
}
// 存放用户数据
AppStorage.setOrCreate(CommonConstant.USER_INFO, userInfo)
PreferencesUtil.savaData(CommonConstant.PREFERENCES_NAME, CommonConstant.USER_INFO, JSON.stringify(userInfo))
// 登录成功
showToast('登录成功')
// 回到首页
router.pushUrl({
url: RouterConstant.PAGE_INDEX, params: {
"currentIndex": 0
}
})
} else {
showToast(CommonConstant.DEFAULT_LOGIN_ERROR)
}
}
4、隐私和用户协议页面
新建两个Page页面,然后在resources/rawfile下面新建两个html文件
页面里面使用Web组件来包裹html文件
import { webview } from '@kit.ArkWeb'
import { CommonConstant } from '../contants/CommonConstant'
@Entry
@Component
struct PolicyPage {
webViewController: webview.WebviewController = new webview.WebviewController
build() {
Navigation() {
Web({
src: $rawfile("PrivacyPolicy.html"),
controller: this.webViewController
})
}
.height(CommonConstant.HEIGHT_FULL)
.width(CommonConstant.WIDTH_FULL)
.title($r('app.string.privacy_policy'))
.titleMode(NavigationTitleMode.Mini)
.mode(NavigationMode.Stack)
.expandSafeArea([SafeAreaType.SYSTEM], [SafeAreaEdge.TOP])
}
}
import { webview } from '@kit.ArkWeb'
import { CommonConstant } from '../contants/CommonConstant'
@Entry
@Component
struct UserPolicyPage {
webViewController: webview.WebviewController = new webview.WebviewController
build() {
Navigation() {
Web({
src: $rawfile("UserPolicy.html"),
controller: this.webViewController
})
}
.height(CommonConstant.HEIGHT_FULL)
.width(CommonConstant.WIDTH_FULL)
.title($r('app.string.user_policy'))
.titleMode(NavigationTitleMode.Mini)
.mode(NavigationMode.Stack)
.expandSafeArea([SafeAreaType.SYSTEM], [SafeAreaEdge.TOP])
}
}
5、编写用户接口方法
import http from '../request/Request'
import { LoginParam, UserInfo, UserNicknameUpdateParam } from './UserApi.type'
/**
* 用户接口
*/
class UserApi {
/**
* 登录接口
*/
userLogin = (data: LoginParam): Promise<string> => {
return http.post('/v1/user/login', data)
}
/**
* 获取用户信息
*/
getUserInfo = (): Promise<UserInfo> => {
return http.get('/v1/user/info')
}
/**
* 修改用户昵称
*/
editNickname = (data: UserNicknameUpdateParam) => {
return http.post('/v1/user/editNickname', data)
}
}
const userApi = new UserApi();
export default userApi as UserApi;
/**
* 登录接口的传参
*/
export interface LoginParam {
/**
* 华为账号id
*/
unionId?: string
/**
* 昵称
*/
nickname?: string
/**
* 头像
*/
avatarUri?: string
}
/**
* 用户信息
*/
export interface UserInfo {
/**
* 用户id
*/
id: number
/**
* 华为账号id
*/
unionId: string
/**
* 昵称
*/
nickname: string
/**
* 头像
*/
avatarUri: string
}
/**
* 修改用户昵称接口入参
*/
export interface UserNicknameUpdateParam {
/**
* 昵称
*/
nickname: string
}
我的页面整体布局
设计图
需求分析
封装组件
1、标题组件
import { CommonConstant } from '../contants/CommonConstant'
@Component
export struct TitleComponent {
// 标题
@Link title: string
build() {
Row() {
Text(this.title)
.fontSize($r('app.float.common_font_size_huge'))
.fontWeight(FontWeight.Medium)
}.width(CommonConstant.WIDTH_FULL)
.margin({ top: 10, bottom: 20 })
}
}
2、导航组件
import { CommonConstant } from '../contants/CommonConstant'
import { FunctionBarData } from '../models/FunctionBarData'
import { router } from '@kit.ArkUI'
import { PreferencesUtil } from '../utils/PreferencesUtil'
import { RouterConstant } from '../contants/RouterConstant'
import { IBestButton, IBestDialog, IBestDialogUtil } from '@ibestservices/ibest-ui'
import feedbackInfoApi from '../api/FeedbackInfoApi'
import { showToast } from '../utils/Toast'
import { ApplicationCheckUtil } from '../utils/ApplicationCheckUtil'
@Component
@Entry
export struct FunctionBarComponent {
@State functionBarData: FunctionBarData = {
icon: '',
text: '',
router: '',
eventType: ''
}
// 反馈信息
@State inputValue: string = ''
@State formInputError: boolean = false
@State dialogVisible: boolean = false
@Builder
formInputContain() {
Column() {
TextInput({ 'placeholder': '请输入反馈意见,长度不能超过255字符' })
.fontSize(14)
.placeholderFont({ size: 14 })
.onChange((value) => {
this.inputValue = value;
this.formInputError = false
})
if (this.formInputError) {
Text('反馈意见不能为空')
.width(CommonConstant.WIDTH_FULL)
.textAlign(TextAlign.Start)
.margin({
top: 5,
left: 5
})
.fontColor(Color.Red)
.fontSize($r('app.float.common_font_size_small'))
.transition({ type: TransitionType.Insert, opacity: 1 })
.transition({ type: TransitionType.Delete, opacity: 0 })
}
}.width('90%').margin({ top: 15, bottom: 15 })
}
build() {
Row() {
IBestDialog({
visible: $dialogVisible,
title: "反馈意见",
showCancelButton: true,
defaultBuilder: (): void => this.formInputContain(),
beforeClose: async (action) => {
if (action === 'cancel') {
return true
}
const valueLength = this.inputValue.trim().length;
this.formInputError = !valueLength;
if (!this.formInputError) {
// 添加反馈内容
await feedbackInfoApi.addFeedbackContent({ content: this.inputValue })
// 更新用户个人信息
showToast('添加反馈意见成功')
this.inputValue = ''
return true
}
return !this.formInputError
}
})
Row({ space: 10 }) {
if (this.functionBarData.icon != '') {
Image(this.functionBarData.icon)
.width(30).aspectRatio(1)
}
Text(this.functionBarData.text)
.fontSize($r('app.float.common_font_size_medium'))
.fontWeight(FontWeight.Normal)
}
Image($r('app.media.icon_arrow'))
.width(15).aspectRatio(1)
}
.width(CommonConstant.WIDTH_FULL)
.height($r('app.float.common_height_small'))
.backgroundColor($r('app.color.common_white'))
.padding(10)
.justifyContent(FlexAlign.SpaceBetween)
.borderRadius(5)
.onClick(() => {
if (this.functionBarData.router) {
router.pushUrl({ url: this.functionBarData.router })
} else if (this.functionBarData.eventType === 'logout') {
// 退出登录
logout()
} else if (this.functionBarData.eventType === 'feedback') {
// 点击反馈
this.dialogVisible = true
} else if (this.functionBarData.eventType === 'checkAppUpdate') {
// 检查更新
ApplicationCheckUtil.checkAppUpdate()
}
})
}
}
/**
* 退出登录
*/
function logout() {
IBestDialogUtil.open({
title: "提示",
message: "是否确认退出登录?",
showCancelButton: true,
onConfirm: async () => {
// 清除登录的缓存数据,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.clear()
// 路由到我的页面
router.replaceUrl({
url: RouterConstant.PAGE_INDEX, params: {
"currentIndex": 3
}
})
}
})
}
头像上传
1、编写工具类
import { common } from '@kit.AbilityKit';
import fs from '@ohos.file.fs';
import request from '@ohos.request';
import { BusinessError } from '@ohos.base';
import { picker } from '@kit.CoreFileKit';
import { Logger } from './Logger';
import { FileData } from '../models/FileData';
let context = getContext(this) as common.UIAbilityContext;
let filesDir = context.filesDir
let cacheDir = context.cacheDir
export class FileUtil {
/**
* 判断文件是否存在
*/
static isExist(fileName: string, fileSuffix: string) {
// 判断文件是否存在,存在就删除
let path = filesDir + '/' + fileName + '.' + fileSuffix;
if (fs.accessSync(path)) {
fs.unlinkSync(path);
}
}
/**
* 下载文件
*/
static downloadFile(fileName: string, fileSuffix: string, fileUrl: string): Promise<boolean> {
return new Promise<boolean>((resolve, reject) => {
// 判断文件是否已存在
FileUtil.isExist(fileName, fileSuffix)
request.downloadFile(context, {
url: fileUrl,
filePath: filesDir + '/' + fileName + '.' + fileSuffix
}).then((downloadTask: request.DownloadTask) => {
downloadTask.on('complete', () => {
resolve(true)
})
}).catch((err: BusinessError) => {
console.error(`Invoke downloadTask failed, code is ${err.code}, message is ${err.message}`);
reject(err)
});
})
}
/**
* 选择图片
*/
static selectImage(): Promise<string> {
return new Promise<string>((resolve, reject) => {
try {
let photoSelectOptions = new picker.PhotoSelectOptions();
photoSelectOptions.MIMEType = picker.PhotoViewMIMETypes.IMAGE_TYPE;
photoSelectOptions.maxSelectNumber = 1;
let photoPicker = new picker.PhotoViewPicker(context);
photoPicker.select(photoSelectOptions).then((photoSelectResult: picker.PhotoSelectResult) => {
resolve(photoSelectResult.photoUris[0])
}).catch((err: BusinessError) => {
reject(err)
});
} catch (error) {
let err: BusinessError = error as BusinessError;
console.error('PhotoViewPicker failed with err: ' + JSON.stringify(err));
reject(err)
}
})
}
/**
* 将uri截取转换成固定类型
*/
static convertFile(uri: string): FileData {
// 将uri分割成字符串数组
const array: string[] = uri.split('/');
// 获取用户文件全名
const fileFullName = array[array.length-1]
// 获取文件名字里面.最后出现的索引位置
let index = fileFullName.lastIndexOf(".");
// 获取文件后缀名
const fileSuffix = fileFullName.substring(index + 1)
// 获取文件名
const fileName = fileFullName.substring(0, index)
// 封装文件数据
const fileData: FileData = { fileFullName: fileFullName, fileSuffix: fileSuffix, fileName: fileName }
return fileData
}
/**
* 将用户文件转换成缓存目录
*/
static copyUserFileToCache(uri: string, fileData: FileData): Promise<boolean> {
return new Promise<boolean>((resolve, reject) => {
// 缓存目录
let cachePath = cacheDir + '/' + fileData.fileFullName
try {
let files = fs.openSync(uri, fs.OpenMode.READ_ONLY)
fs.copyFileSync(files.fd, cachePath)
resolve(true)
} catch (error) {
let err: BusinessError = error as BusinessError;
Logger.error('Error copying file:' + JSON.stringify(err))
reject(err)
}
})
}
}
2、修改头像
/**
* 修改头像
*/
async editAvatar() {
try {
// 头像上传
const uri = await FileUtil.selectImage()
if (!uri) {
showToast("选择图片失败")
return
}
// 将uri截取转换成固定类型
const fileData = FileUtil.convertFile(uri)
// 将用户文件转换成缓存目录
const data = await FileUtil.copyUserFileToCache(uri, fileData)
if (!data) {
showToast("修改头像失败")
return
}
// 上传文件
await this.uploadImage(fileData)
} catch (error) {
showToast("修改头像失败")
}
}
3、上传头像
/**
* 上传图片
*/
async uploadImage(fileData: FileData) {
let files: Array<request.File> = [
// uri前缀internal://cache 对应cacheDir目录
{
filename: fileData.fileFullName,
name: 'file', // 文件上传的key
uri: 'internal://cache/' + fileData.fileFullName,
type: fileData.fileSuffix
}
]
let uploadConfig: request.UploadConfig = {
url: 'http://118.31.50.145:9003/v1/user/editAvatar',
header: {
"Authorization": AppStorage.get<string>("token")
},
method: 'POST',
files: files,
data: []
}
// 打开上传进度弹窗
this.dialog.open()
// 发送请求
const response = await request.uploadFile(context, uploadConfig)
// 监听上传进度
response.on("progress", async (val, size) => {
Logger.info("头像上传进度:", `${val / size * 100}%`)
emitter.emit({ eventId: 100 }, { data: { process: `上传进度: ${(val / size * 100).toFixed(0)}%` } })
if (val === size) {
this.dialog.close()
showToast('头像上传成功')
// 获取用户信息
const userInfo = await userApi.getUserInfo();
this.userInfo = userInfo
// 存放用户数据
AppStorage.setOrCreate(CommonConstant.USER_INFO, userInfo)
PreferencesUtil.savaData(CommonConstant.PREFERENCES_NAME, CommonConstant.USER_INFO,
JSON.stringify(userInfo))
}
})
}
4、自定义上传进度弹窗
// 自定义上传进度弹窗
dialog: CustomDialogController = new CustomDialogController({
builder: ProgressDialog({ message: `上传进度: 0%` }),
customStyle: true,
alignment: DialogAlignment.Center
})
import { emitter } from '@kit.BasicServicesKit'
@CustomDialog
export struct ProgressDialog {
@State message: string = ''
controller: CustomDialogController
aboutToAppear(): void {
emitter.on({ eventId: 100 }, (res) => {
this.message = res.data!["process"]
})
}
build() {
Flex({ direction: FlexDirection.Row, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) {
LoadingProgress().width(30).height(30).color($r('app.color.common_white'))
if (this.message) {
Text(this.message).fontSize((14)).fontColor($r('app.color.common_white'))
}
}
.width($r('app.float.common_width_huge'))
.height($r('app.float.common_height_small'))
.padding(10)
.backgroundColor('rgba(0,0,0,0.5)')
.borderRadius(8)
}
}
Emitter具有同一进程不同线程间,或同一进程同一线程内,发送和处理事件的能力
Emitter用于同一进程内相同线程或不同线程间的事件处理,事件异步执行。使用时需要先订阅一个事件,然后发布该事件,发布完成后Emitter会将已发布的事件分发给订阅者,订阅者就会执行该事件订阅时设置的回调方法。当不需要订阅该事件时应及时取消订阅释放Emitter资源。
官方文档地址:
文档中心
检查更新
应用市场更新功能为开发者提供版本检测、显示更新提醒功能。开发者使用应用市场更新功能可以提醒用户及时更新到最新版本。
当应用启动完成或用户在应用中主动检查应用新版本时,开发者可以通过本服务,来查询应用是否有可更新的版本。如果存在可更新版本,您可以通过本服务为用户显示更新提醒。
- 应用调用检查更新接口。
- 升级服务API返回是否有新版本。
- 调用显示升级对话框接口。
- 升级服务API向应用返回显示结果。
import { updateManager } from '@kit.StoreKit';
import type { common } from '@kit.AbilityKit';
import { Logger } from './Logger';
import { showToast } from './Toast';
let context: common.UIAbilityContext = getContext() as common.UIAbilityContext;
export class ApplicationCheckUtil {
/**
* 检测新版本
*/
static async checkAppUpdate() {
try {
const checkResult = await updateManager.checkAppUpdate(context);
if (checkResult.updateAvailable === 0) {
showToast('当前应用版本已经是最新');
return;
}
// 存在新版本,显示更新对话框
const resultCode = await updateManager.showUpdateDialog(context);
if (resultCode === 1) {
showToast("检查更新失败,请稍后重试,或者在我的界面直接点击反馈将信息反馈给开发者")
return
}
} catch (error) {
Logger.error('TAG', `检查更新出错: code is ${error.code}, message is ${error.message}`);
showToast("检查更新失败,请稍后重试,或者在我的界面直接点击反馈将信息反馈给开发者")
}
}
}
退出登录
/**
* 退出登录
*/
function logout() {
IBestDialogUtil.open({
title: "提示",
message: "是否确认退出登录?",
showCancelButton: true,
onConfirm: async () => {
// 清除登录的缓存数据,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.clear()
// 路由到我的页面
router.replaceUrl({
url: RouterConstant.PAGE_INDEX, params: {
"currentIndex": 3
}
})
}
})
}