【小而美】HarmonyOS官方模板優(yōu)秀案例(第5期:工具行業(yè) ·?日歷應用)

0 評論 1622 瀏覽 0 收藏 33 分鐘

?? 鴻蒙生態(tài)為開發(fā)者提供海量的HarmonyOS模板/組件,助力開發(fā)效率原地起飛 ??

★ 一鍵直達生態(tài)市場組件&模板市場?, 快速應用DevEco Studio插件市場集成組件&模板?★

工具行業(yè)群英薈萃,是小而美應用的主要聚集賽道

本期介紹的案例是其中一類:日常剛需的日歷應用

?? 覆蓋20+行業(yè),本帖以匯總形式持續(xù)更新中,點擊收藏!一鍵三連!常看常新!

 

【第5期】工具行業(yè) ·?日歷應用

一、概述

1、行業(yè)洞察

行業(yè)訴求:

日歷類應用,未來競爭將聚焦于 AI 驅動的個性化體驗、場景化生態(tài)構建及文化適配能力正從單一工具進化為連接工作、生活、社交的 “時間操作系統(tǒng)”。

商業(yè)模式是日歷類應用的重要場景訴求,目前免費增值為主,差異化變現(xiàn)破局,如何結合小藝做個性化推薦是差異化的根本。

滿足用戶的進階需求:社交協(xié)作,隱私保護。

行業(yè)常用三方SDK

SDK鏈接:岳鷹全景監(jiān)控SDk、支付寶支付 SDK、穿山甲廣告SDK、快手聯(lián)盟廣告SDK友盟SDK、騰訊微信SDK騰訊優(yōu)量匯、極光 SDK、高德地圖、百度地圖、騰訊地圖定位高德地圖定位

說明:“以上三方庫及鏈接僅為示例,三方庫由三方開發(fā)者獨立提供,以其官方內容為準”

2、案例概覽(下載模板

基于以上行業(yè)分析,本期將介紹鴻蒙生態(tài)市場生活服務類行業(yè)模板——日歷應用模板,為行業(yè)提供常用功能的開發(fā)案例,模板主要分為萬年歷、黃歷、和我的三大模塊。

  • Stage開發(fā)模型 + 聲明式UI開發(fā)范式。
  • 分層架構設計 + 組件化拆分,支持開發(fā)者在開發(fā)時既可以選擇完整使用模板,也可以根據需求單獨選用其中的業(yè)務組件。
  • 本模板已集成華為賬號等服務,只需做少量配置和定制即可快速實現(xiàn)華為賬號的登錄。

本模板主要頁面及核心功能如下所示

日歷模板

?|-- 萬年歷

?| ???|-- 日歷選擇

?| ???|-- 吉日查詢

?| ???|-- 日期計算

?| ???|-- 節(jié)日節(jié)氣

?| ???└-- 宜忌展示

?|-- 黃歷

?| ???|-- 日期切換

?| ???|-- 宜忌展示

?| ???|-- 五行、沖煞

?| ???|-- 彭祖百忌

?└-- 我的

?| ????|-- 個人信息

?| ????└-- 設置

?| ??????└-- 主題切換

?| ??????└-- 隱私協(xié)議

?| ??????└-- 用戶協(xié)議

二、應用架構設計

1、分層模塊化設計

產品定制層:專注于滿足不同設備或使用場景的個性化需求,作為應用的入口,是用戶直接互動的界面。

本實踐暫時只支持直板機,為單HAP包形式,包含路由根節(jié)點、底部導航欄等。

基礎特性層:用于存放相對獨立的功能UI和業(yè)務邏輯實現(xiàn)。

本實踐的基礎特性層將應用底部導航欄的每個選項拆分成一個獨立的業(yè)務功能模塊。

每個功能模塊都具備高內聚、低耦合、可定制的特點,支持產品的靈活部署。

公共能力層:存放公共能力,包括公共UI組件、數據管理、外部交互和工具庫等共享功能。

本實踐的公共能力層分為公共基礎能力和可分可合組件,均打包為HAR包被上層業(yè)務組件引用。

公共基礎能力包含日志、文件處理等工具類,公共類型定義,網絡庫,以及彈窗、加載等公共組件。

可分可合組件將包含行業(yè)特點、可完全自閉環(huán)的能力抽出獨立的組件模塊,支持開發(fā)者在開發(fā)中單獨集成使用,詳見業(yè)務組件設計章節(jié)。

2、業(yè)務組件設計

為支持開發(fā)者單獨獲取特定場景的頁面和功能,本模板將功能完全自閉環(huán)的部分能力抽離出獨立的行業(yè)組件模塊,不依賴公共基礎能力包,開發(fā)者可以單獨集成,開箱即用,降低使用難度。

三、行業(yè)場景技術方案

1、個人信息

場景說明

支持華為賬號一鍵登錄及其他方式(賬號密碼登錄)。

用戶登錄后展示昵稱和頭像,點擊用戶信息欄可進入用戶主頁,查看并編輯個人信息和歷史動態(tài)。

支持添加重要提醒(日程、生日、紀念日、代辦),更新提醒,刪除提醒。

技術方案

華為賬號一鍵登錄

通過Account Kit實現(xiàn)華為賬號一鍵登錄,并獲取用戶手機號,關聯(lián)應用已有用戶。

頭像修改

通過Scenario Fusion Kit提供的選擇頭像Button快速拉起頭像選擇頁面,供用戶完成華為賬號頭像或其他頭像的選擇與展示。

重要提醒

通過@kit.CalendarKit提供的提供日歷與日程管理能力將應用中的工作、生活中與時間相關的日程服務與系統(tǒng)日歷進行集成,從而實現(xiàn)日程管理、事件創(chuàng)建、查詢等功能。

主題切換

通過全局主題對象,控制全局的主題顏色切換,并使用持久化存儲當前主題選擇。

代碼參考

部分核心代碼參見華為賬號一鍵登錄實現(xiàn)章節(jié)。

2、黃歷

場景說明

    • 支持根據日期查看當日黃歷信息。
    • 切換日期查詢其他日期黃歷。
    • 根據選擇日期查看今日宜今日忌。
    • 支持根據選擇的黃歷查看白話文。

技術方案

根據萬年歷選擇日期進行對應日期黃歷的展示。

通過日歷選擇組件暴露的句柄,感知當前選擇的日期,并通過句柄同步修改萬年歷對應的日期。

代碼參考

部分核心代碼參見黃歷實現(xiàn)章節(jié)。

3、萬年歷

  • 場景說明

支持日歷查看,日期切換,設置周首日。

支持查看今日宜,今日忌。

支持實用工具查詢(吉日查詢,日期計算,節(jié)日節(jié)氣)。

支持查看城市限行。

支持查看歷史上的今天。

技術方案

日歷查看

通過使用Swiper組件結合計算每月的日期實現(xiàn)日期輪播查看。

頭像修改

通過Scenario Fusion Kit提供的選擇頭像Button快速拉起頭像選擇頁面,供用戶完成華為賬號頭像或其他頭像的選擇與展示。

實用工具

使用工具結合DatePicker日期選擇器,實現(xiàn)日期選擇并根據條件計算。

城市限行

通過申請位置權限,或者當前城市的限行車牌尾號,并進行展示。

代碼參考

部分核心代碼參見萬年歷實現(xiàn)章節(jié)。

四、模板代碼

1、工程結構下載模板

詳細代碼結構如下所示:

Application

├──├──commons

│ ??├──common ???????????????????????????????// 公共能力層

│ ????├──src/main/ets ???????????????????????// 基礎能力

│ ????│ ?└──components ??????????????????????// 公共組件

│ ????│ ?└──dividerTmp ??????????????????????// 下劃線公共組件

│ ????│ ?└──https ???????????????????????????// 網絡請求庫

│ ????│ ?└──models ??????????????????????????// 公共接口常量

│ ????│ ?└──quickLogin ??????????????????????// 華為賬號一鍵登錄

│ ????│ ?└──style ???????????????????????????// 公共樣式

│ ????│ ?└──utils ???????????????????????????// 工具類

│ ????│ ?└──viewmodels ??????????????????????// 接口層

│ ????└──Index.ets ??????????????????????????// 對外接口類

│ ?├──router_module ?????????????????????????// 全局路由組件

├──├──components ????????????????????????????// 公共組件

│ ??├──base_apis ????????????????????????????// 通用組件(模態(tài)框,彈窗,選擇器等)

│ ??├──base_calendar ????????????????????????// 日歷組件

│ ??├──calendar_almanac ?????????????????????// 黃歷組件

│ ??├──calendar_events ??????????????????????// 重要提醒組件

│ ??├──date_calculation ?????????????????????// 日期計算組件

│ ??├──festival_solar ???????????????????????// 節(jié)日節(jié)氣組件

│ ??├──login_info ???????????????????????????// 登錄組件組件

│ ??├──vip_center ???????????????????????????// 開通會員組件

│ ??├──traffic_restriction ??????????????????// 城市限行組件

│ ??├──yiji_query ???????????????????????????// 宜忌查詢組件

├──features ?????????????????????????????????// 基礎特性層

│ ?├──almanac/src/main/ets ??????????????????// 黃歷

│ ?│ ?├──pages ??????????????????????????????// 首頁入口

│ ????│ ?├──AlmanacView??????????????????????// 黃歷入口

│ ?├──almanac/src/main/resources ????????????// 資源文件目錄

│ ?├──almanac/Index.ets ?????????????????????// 對外接口類

│ ?├──perpetual/src/main/ets ????????????????// 萬年歷

│ ?│ ?├──components ?????????????????????????// 萬年歷組件

│ ?│ ?├──pages ??????????????????????????????

│ ????│ ?├──PerpetualCalendar????????????????// 萬年歷組件入口

│ ?├──perpetual/src/main/resources ??????????// 資源文件目錄

│ ?├──perpetual/Index.ets ???????????????????// 對外接口類

│ ?├──mine/src/main/ets ?????????????????????// 我的(包含一鍵登錄)

│ ?│ ?└──pages ??????????????????????????????// 我的入口頁

│ ????│ ?├──MinePage?????????????????????????// 登錄

│ ?│ ?└──components ?????????????????????????// 我的頁面入口

│ ?└──mine/src/main/resources ???????????????// 資源文件目錄

└─product/entry/src/main ??

???├─ets

???│ ?├─widget

???│ ?│ ?├──pages ???????????

???│ ?│ ?????├──WidgetCard.ets ??????// 服務卡片 ???

???│ ?├─entryability

???│ ?│ ?????├──EntryAbility.ets ????????????// 應用程序入口

???│ ?├─page

???│ ?│ ?├──Index.ets ???????????????????????// 入口

???│ ?│ ?├──PrivacyPage.ets ?????????????????// 隱私協(xié)議 ??

???│ ?│ ?├──SafePage.ets ????????????????????// 隱私協(xié)議彈窗 ?

???│ ?│ ?├──SplashPage.ets ??????????????????// 閃屏頁 ???????

???│ ?│ ?├──TabContainer.ets ????????????????// tab頁入口

???└─resources

2、關鍵代碼解讀

本篇代碼非應用的全量代碼,只包括應用的部分能力的關鍵代碼。

若需獲取全量代碼,請查看模板集成章節(jié)。

個人信息

華為賬號一鍵登錄

?typescript

????getQuickLoginAnonymousPhone() {

????// 創(chuàng)建授權請求,并設置參數

????const authRequest = new authentication.HuaweiIDProvider().createAuthorizationWithHuaweiIDRequest();

????// 獲取手機號需要傳如下scope,傳參數之前需要先申請對應scope權限,才能返回對應數據

????authRequest.scopes = ['quickLoginAnonymousPhone'];

????authRequest.permissions = ['serviceauthcode'];

????// 用戶是否需要登錄授權,該值為true且用戶未登錄或未授權時,會拉起用戶登錄或授權頁面

????authRequest.forceAuthorization = false;

????// 用于防跨站點請求偽造

????authRequest.state = util.generateRandomUUID();

????try {

??????const controller = new authentication.AuthenticationController(getContext(this));

??????controller.executeRequest(authRequest).then((response) => {

????????const authorizationWithHuaweiIDResponse = response as authentication.AuthorizationWithHuaweiIDResponse;

????????const state = authorizationWithHuaweiIDResponse.state;

????????if (state !== undefined && authRequest.state !== state) {

??????????hilog.error(0x0000, 'testTag', `Failed to authorize. The state is different, response state: ${state}`);

??????????return;

????????}

????????hilog.info(0x0000, 'testTag', 'Succeeded in authentication.');

????????const authorizationWithHuaweiIDCredential = authorizationWithHuaweiIDResponse.data!;

????????const code = authorizationWithHuaweiIDCredential.authorizationCode;

????????const unionID = authorizationWithHuaweiIDCredential.unionID;

????????const openID = authorizationWithHuaweiIDCredential.openID;

????????const anonymousPhone = authorizationWithHuaweiIDCredential?.extraInfo?.quickLoginAnonymousPhone as string;

????????if (anonymousPhone) {

??????????hilog.info(0x0000, 'testTag', 'Succeeded in authentication.');

??????????this.quickLoginAnonymousPhone = anonymousPhone;

??????????return;

????????} else {

??????????this.quickLoginAnonymousPhone = '123xxxxxx456'

????????}

????????// 開發(fā)者處理code、unionID、openID

????????this.authorizationCode = code

??????}).catch((err: BusinessError) => {

????????this.dealAllPhoneError(err);

??????});

????} catch (error) {

??????this.dealAllPhoneError(error);

????}

??}

重要提醒

typescript

class CalendarManage {

??context: common.UIAbilityContext = getContext(this) as common.UIAbilityContext;

??private static _instance: CalendarManage

?

??static get instance() {

????if (!CalendarManage._instance) {

??????CalendarManage._instance = new CalendarManage()

????}

????return CalendarManage._instance

??}

?

??public getCalendarPermission(): Promise<string> {

????const permissions: Permissions[] = ['ohos.permission.READ_CALENDAR', 'ohos.permission.WRITE_CALENDAR'];

????let atManager = abilityAccessCtrl.createAtManager();

????return new Promise((resolve, reject) => {

??????atManager.requestPermissionsFromUser(this.context, permissions).then((result: PermissionRequestResult) => {

????????resolve('success')

??????}).catch((error: BusinessError) => {

????????reject('failed')

????????console.error(`get Permission error, error. Code: ${error.code}, message: ${error.message}`);

??????})

????})

??}

?

??/*

????* 添加提醒到日歷

????* */

??private async calendarEvent(calendar: calendarManager.Calendar,

????calendarInfo: UserEventItem): Promise<CalendarInfo> {

????const event: calendarManager.Event = {

??????title: calendarInfo.content,

??????type: calendarManager.EventType.NORMAL,

??????id: calendarInfo.eventId,

??????isLunar:calendarInfo.date[0].isLunar,

??????startTime: new Date(dayjs(calendarInfo.date[0].date).format('YYYY-MM-DD') + ' ' +

??????calendarInfo.date[0].time).getTime(),

??????endTime: new Date(dayjs(calendarInfo.date[1].date).format('YYYY-MM-DD') + ' ' +

??????calendarInfo.date[1].time).getTime(),

??????reminderTime: CalendarManage.getReminderTime(calendarInfo.remindList),

??????recurrenceRule: {

????????recurrenceFrequency: repeatMap[calendarInfo.repeatType],

??????},

????};

????return new Promise(async (resolve, reject) => {

??????if (calendarInfo.eventId) {

????????calendar.updateEvent(event).then(() => {

??????????resolve({

????????????status: 'success',

??????????})

????????}).catch((err: BusinessError) => {

??????????console.error(`Failed to update event. Code: ${err.code}, message: ${err.message}`);

????????});

??????} else {

????????calendar.addEvent(event).then((data: number) => {

??????????console.info(`Succeeded in adding event, id -> ${data}`);

??????????resolve({

????????????status: 'success',

????????????data: data,

??????????})

????????}).catch((err: BusinessError) => {

??????????resolve({

????????????status: 'failed',

??????????})

????????});

??????}

????})

??}

??/*

????* 根據提醒參數創(chuàng)建日歷參數

????* */

??public async calendarEventCreate(calendarInfo: UserEventItem, operationType?: string): Promise<CalendarInfo> {

????if (calendarInfo.remindList[0] === '不提醒') {

??????return {

????????status: 'not need calendar',

??????}

????}

????let permission = await this.getCalendarPermission()

????if (permission !== 'success') {

??????return {

????????status: 'permission failed',

??????}

????}

????let calendar: calendarManager.Calendar | undefined = undefined;

????// 指定日歷賬戶信息

????const calendarAccount: calendarManager.CalendarAccount = {

??????name: '日歷模板',

??????type: calendarManager.CalendarType.LOCAL,

??????// 日歷賬戶顯示名稱,該字段如果不填,創(chuàng)建的日歷賬戶在界面顯示為空字符串。

??????displayName: '日歷模板',

????};

????let calendarMgr: calendarManager.CalendarManager | null = calendarManager.getCalendarManager(this.context);

????// 創(chuàng)建日歷賬戶

????try {

??????calendar = await calendarMgr?.createCalendar(calendarAccount)

??????let res: CalendarInfo

??????if (operationType === 'delete') {

????????res = await this.calendarEventDelete(calendar, calendarInfo)

??????} else {

????????res = await this.calendarEvent(calendar, calendarInfo)

??????}

??????return res

????} catch (e) {

??????return {

????????status: 'calendar operation failed',

??????}

????}

??}

??/*

????* 刪除已經添加到日歷的提醒

????* */

??private async calendarEventDelete(calendar: calendarManager.Calendar,

????calendarInfo: UserEventItem): Promise<CalendarInfo> {

????try {

??????await calendar.deleteEvent(calendarInfo.eventId)

??????return {

????????status: 'success',

??????}

????} catch (e) {

??????return {

????????status: 'failed',

??????}

????}

??}

}

黃歷

重要提醒

typescript

/**

?* 拋出句柄

?*/

export class CalendarController {

?????public static vm: CalendarVM = CalendarVM.instance;

???

?????public setSelectDate(date: Date) {

???????CalendarController.vm.changeDate(date)

?????}

???

?????public getTodayYiJi() {

???????CalendarController.vm.getTodayYiJi()

?????}

}

切換日期

?typescript

/**

?* 切換日期

?*/ ?

public changeDate(date: Date) {

????let gap = (date.getFullYear() - this.curDate.year()) * 12 + date.getMonth() - this.curDate.month()

????this.dateListSource.clearData()

????let i = -2

????while (i <= 2) {

??????let month = this.getDateList(i + gap)

??????this.dateListSource.pushData(month)

??????i++

????}

????this.curIndex = 2

????this.selectDate = dayjs(date)

?}

獲取今日宜和忌

?typescript

/**

?* 獲取今日宜和忌

?*/ ?

public getTodayYiJi() {

????const todayLunar = Lunar.fromDate(new Date(this.selectDate.format('YYYY-MM-DD')));

????const yi = todayLunar.getDayYi();

????const ji = todayLunar.getDayJi();

????this.todayYiJi = {

??????yi,

??????ji,

????}

??}

萬年歷

展示日歷

typescript

/**

?* 展示日歷

?*/ ?

Swiper(this.swiperController) {

????????LazyForEach(this.vm.dateListSource, (item: DateModelList) => {

??????????Grid() {

????????????ForEach(item, (item: DateModel) => {

??????????????GridItem() {

??????????????}

????????????},(item: DateModel) => JSON.stringify(item));

??????????}

??????????.columnsTemplate('1fr 1fr 1fr 1fr 1fr 1fr 1fr')

??????????.maxCount(7)

??????????.columnsGap(0)

??????????.rowsGap(0)

??????????.padding({ bottom: 20 })

????????},(item: DateModelList) => JSON.stringify(item));

}

獲取位置權限

?typescript

/**

?* 獲取位置權限

?*/ ?

getCurrentLocation() {

????this.permissionRequestUtils.locationPermissionRequest().then(async (res) => {

??????if (res === 'success') {

????????this.locationPermission = res

????????this.permissionRequestUtils.getCurrentLocation().then((res: string) => {

??????????this.location = res

????????}).catch((err:BusinessError) => {

??????????this.location = '北京'

????????})

??????} else {

????????this.dealError()

??????}

????}).catch(() => {

??????this.dealError()

????})

}

3、模板集成

本模板提供了兩種代碼集成方式,供開發(fā)者自由選用。

整體集成下載模板

開發(fā)者可以選擇直接基于模板工程開發(fā)自己的應用工程。

模板代碼獲取:

通過IDE插件創(chuàng)建模板工程,開發(fā)指導。

通過生態(tài)市場下載源碼,下載模板。

通過開源倉訪問源碼,倉庫地址。

打開模板工程,根據README說明中的快速入門章節(jié),將自己的應用信息配置在模板工程內,即可運行并查看模板效果。

對接開發(fā)者自己的服務器接口,轉換數據結構,展示真實的云側數據。

將commons/lib_common/src/main/ets/httprequest/HttpRequestApi.ets文件中的mock接口替換為真實的服務器接口。

在commons/lib_common/src/main/ets/httprequest/HttpRequest.ets文件中將云側開發(fā)者自定義的數據結構轉換為端側數據結構。

根據自己的業(yè)務內容修改模板,進行定制化開發(fā)。

按需集成

若開發(fā)者已搭建好自己的應用工程,但暫未實現(xiàn)其中的部分場景能力,可以選擇取用其中的業(yè)務組件,集成在自己的工程中。

組件代碼獲?。?/strong>

通過IDE插件下載組件源碼。開發(fā)指導

通過生態(tài)市場下載組件源碼。下載地址

下載組件源碼,根據README中的說明,將組件包配置在自己的工程中。

根據API參考和示例代碼,將組件集成在自己的對應場景中。

 

以上是第五期“工具行業(yè)-日歷應用”行業(yè)優(yōu)秀案例的內容,更多行業(yè)敬請期待~

歡迎下載使用行業(yè)模板“點擊下載”,若您有體驗和開發(fā)問題,或者迫不及待想了解XX行業(yè)的優(yōu)秀案例,歡迎在評論區(qū)留言,小編會快馬加鞭為您解答~

同時誠邀您添加下方二維碼加入“組件模板活動社群”,精彩上新&活動不錯過!

?? 系列持續(xù)更新,歡迎收藏本帖!

HarmonyOS官方模板優(yōu)秀案例 | 便捷生活行業(yè) · 購物中心-點擊查看

HarmonyOS官方模板優(yōu)秀案例 | 新聞行業(yè) · 綜合新聞-點擊查看

HarmonyOS官方模板優(yōu)秀案例 | 教育行業(yè) · 教育備考-點擊查看

HarmonyOS官方模板優(yōu)秀案例 | 餐飲行業(yè) · 美食菜譜-點擊查看

HarmonyOS官方模板優(yōu)秀案例 | 工具行業(yè) · 日歷應用-點擊查看

?? HarmonyOS組件模板相關推薦

  • 【活動ing】HarmonyOS組件/模板集成創(chuàng)新活動,報名時間截止2025年8月30日,點擊查看
  • 鴻蒙應用開發(fā)者激勵計劃2025,點擊查看
更多精彩內容,請關注人人都是產品經理微信公眾號或下載App
評論
評論請登錄
  1. 目前還沒評論,等你發(fā)揮!