吃掉小鹿乃手机版
3.76M · 2025-09-14
? 鸿蒙生态为开发者提供海量的HarmonyOS模板/组件,助力开发效率原地起飞 ?
★ 一键直达 , 快速应用 ★
? 覆盖20+行业,本帖以汇总形式持续更新中,!一键三连!常看常新!
分类 |
三方库名称 |
功能 |
SDK链接 |
登录认证 |
中国移动一键登录SDK/易盾一键登录SDK/创蓝闪验/极光安全认证/阿里云号码认证SDK/中国电信一键登录SDK |
登录 |
|
分享 |
友盟/ShareSDK/微信分享/QQ分享/新浪微博SDK/MobTech ShareSDK |
统计/推送/分享 |
|
支付 |
支付宝支付/微信支付/银联支付 |
支付 |
|
数据分析 |
友盟移动统计SD/神策数据SDK |
数据收集、处理、分析、运用 |
|
性能监控 |
腾讯Bugly SDK/听云SDK/岳鹰全景监控SDK |
异常上报和运营统计 |
|
地图 |
高德地图SDK |
地图 |
|
推送 |
个推/华为推送/极光PUSH/阿里推送SDK |
消息推送 |
|
媒体 |
阿里云视频播放器SDK |
音视频 |
说明:“以上三方库及链接仅为示例,三方库由三方开发者独立提供,以其官方内容为准”
基于以上行业分析,本期将介绍鸿蒙生态市场生活服务类行业模板——日历应用模板,为行业提供常用功能的开发案例,模板主要分为万年历、黄历、和我的三大模块。
日历模板 |-- 万年历 | |-- 日历选择 | |-- 吉日查询 | |-- 日期计算 | |-- 节日节气 | └-- 宜忌展示 |-- 黄历 | |-- 日期切换 | |-- 宜忌展示 | |-- 五行、冲煞 | |-- 彭祖百忌 └-- 我的 | |-- 个人信息 | └-- 设置 | └-- 主题切换 | └-- 隐私协议 | └-- 用户协议
为支持开发者单独获取特定场景的页面和功能,本模板将功能完全自闭环的部分能力抽离出独立的行业组件模块,不依赖公共基础能力包,开发者可以单独集成,开箱即用,降低使用难度。
支持华为账号一键登录及其他方式(账号密码登录)。
用户登录后展示昵称和头像,点击用户信息栏可进入用户主页,查看并编辑个人信息和历史动态。
支持添加重要提醒(日程、生日、纪念日、代办),更新提醒,删除提醒。
根据万年历选择日期进行对应日期黄历的展示。
通过日历选择组件暴露的句柄,感知当前选择的日期,并通过句柄同步修改万年历对应的日期。
支持日历查看,日期切换,设置周首日。
支持查看今日宜,今日忌。
支持实用工具查询(吉日查询,日期计算,节日节气)。
支持查看城市限行。
支持查看历史上的今天。
详细代码结构如下所示:
Application├──├──commons│ ├──common // 公共能力层│ ├──src/main/ets // 基础能力│ │ └──components // 公共组件│ │ └──dividerTmp // 下划线公共组件│ │ └──https // 网络请求库│ │ └──models // 公共接口常量│ │ └──quickLogin // 华为账号一键登录│ │ └──style // 公共样式│ │ └──utils // 工具类│ │ └──viewmodels // 接口层│ └──Index.ets // 对外接口类│ ├──router_module // 全局路由组件├──├──components // 公共组件│ ├──base_apis // 通用组件(模态框,弹窗,选择器等)│ ├──base_calendar // 日历组件│ ├──calendar_almanac // 黄历组件│ ├──calendar_events // 重要提醒组件│ ├──date_calculation // 日期计算组件│ ├──festival_solar // 节日节气组件│ ├──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 // 隐私协议 │ │ ├──SafePage.ets // 隐私协议弹窗 │ │ ├──SplashPage.ets // 闪屏页 │ │ ├──TabContainer.ets // tab页入口 └─resources
本篇代码非应用的全量代码,只包括应用的部分能力的关键代码。
若需获取全量代码,请查看模板集成章节。
typescript getQuickLoginAnonymousPhone() { // 创建授权请求,并设置参数 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' } // 开发者处理code、unionID、openID this.authorizationCode = code }).catch((err: BusinessError) => { this.dealAllPhoneError(err); }); } catch (error) { this.dealAllPhoneError(error); } }
typescriptclass 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', }) }); } }) } /* * 根据提醒参数创建日历参数 * */ 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, // 日历账户显示名称,该字段如果不填,创建的日历账户在界面显示为空字符串。 displayName: '日历模板', }; let calendarMgr: calendarManager.CalendarManager | null = calendarManager.getCalendarManager(this.context); // 创建日历账户 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() })}
本模板提供了两种代码集成方式,供开发者自由选用。
开发者可以选择直接基于模板工程开发自己的应用工程。
将commons/lib_common/src/main/ets/httprequest/HttpRequestApi.ets
文件中的mock接口替换为真实的服务器接口。
在commons/lib_common/src/main/ets/httprequest/HttpRequest.ets
文件中将云侧开发者自定义的数据结构转换为端侧数据结构。
根据自己的业务内容修改模板,进行定制化开发。
若开发者已搭建好自己的应用工程,但暂未实现其中的部分场景能力,可以选择取用其中的业务组件,集成在自己的工程中。
以上是第五期“工具行业-日历应用”行业优秀案例的内容,更多行业敬请期待~
欢迎下载使用行业模板“”,若您有体验和开发问题,或者迫不及待想了解XX行业的优秀案例,欢迎在评论区留言,小编会快马加鞭为您解答~
同时诚邀您添加下方二维码加入“组件模板活动社群”,精彩上新&活动不错过!
? 本系列持续更新,欢迎点击帖子末尾左下角“”收藏本帖!
期数 |
帖子 |
链接 |
第1期 |
HarmonyOS官方模板优秀案例 | 便捷生活行业 · 购物中心 |
|
第2期 |
HarmonyOS官方模板优秀案例 | 新闻行业 · 综合新闻 |
|
第3期 |
HarmonyOS官方模板优秀案例 | 教育行业 · 教育备考 |
|
第4期 |
HarmonyOS官方模板优秀案例 | 餐饮行业 · 美食菜谱 |
|
第5期 |
HarmonyOS官方模板优秀案例 | 工具行业 · 日历应用 |
|
第6期 |
小编加急整理中,敬请期待 |
3.76M · 2025-09-14
35.67M · 2025-09-14
304.39M · 2025-09-14
曾获腾讯投资、估值 14 亿美元,印度独角兽企业 Hike 因实钱游戏禁令关停
联想发布拯救者 R9000P 2025 至尊版游戏本:高配锐龙 9 9955HX3D + RTX 5080