🚀🚀🚀🚀
鸿蒙生态为开发者提供海量的 HarmonyOS 模板/组件,助力开发效率原地起飞
★一键直达生态市场组件&模板市场 , 快速应用DevEco Studio插件市场集成组件&模板 ★
前两期案例介绍了便捷生活、新闻行业,大家是不是意犹未尽?
第三期-教育行业的案例加急发布啦!
👉覆盖 20+行业,本帖下方以汇总形式持续更新中,点击收藏!一键三连!常看常新!
【第 3 期】教育行业· 教育备考
一、 概述
1. 行业洞察
1) 行业诉求:
精准分发:面对不同的受众教育类应用有不同的业务场景,产出适配内容、精准题库等,并以高效且契合场景的方式进行分发,最终实现优质教育资源的有效传递。
高效流畅、操作敏捷是教育类应用不同场景重要诉求,在线学习、考试等场景出现卡顿会严重影响学习、考试的。
需具备智能刷题与精准辅导能力:基于大数据和算法,依据答题情况判断用户知识掌握状况,动态调整出题难度,推送契合的学习内容。
多端协同能力:实现手机、平板、PC 端数据实时互通,提供离线功能,便于用户利用碎片化时间用于刷题或知识点复习,并可在线后同步学习进度。
2) 行业常用三方 SDK
说明:“以上三方库及链接仅为示例,三方库由三方开发者独立提供,以其官方内容为准”
SDK 链接:
腾讯浏览服务SDK 高德定位SDK 百度地图SDK 极光Push SDK 火山引擎SDK 同盾SDK 友盟SDK 微信支付 SDK 支付宝 SDK 听云SDK 福昕 PDF SDK 网易云信 IM SDK 保利威视频直播webSDK 支付宝分享SDK
2. 行业案例概览(下载模板)
基于以上行业分析,本期将介绍鸿蒙生态市场教育行业模板——教育备考应用模板,为行业提供常用功能的开发案例,模板主要分练习、课程和我的三大模块。
Stage 开发模型 + 声明式 UI 开发范式。
分层架构设计+ 组件化拆分,支持开发者在开发时既可以选择完整使用模板,也可以根据需求单独选用其中的业务组件。
本模板已集成华为账号服务,只需做少量配置和定制即可快速实现华为账号的登录等功能。
本模板主要页面及核心功能如下所示:
题库模板
|-- 开屏页
|-- 练习
| |-- banner
| |-- 搜索
| └-- 答题练习
|-- 课程
| |-- 分类列表
| |-- 精选课程
| | |-- 课程详情
| | └-- 支付购买
| └-- 已购我的课程
| |-- 练习模式
| |-- 考试模式
| |-- 错题记录
| └-- 收藏记录
| └-- 笔记记录
└-- 我的
|-- 用户信息
| |-- 登录
| |-- 用户信息
|-- 我的订单
|-- 我的错题
|-- 我的收藏
|-- 练习记录
|-- 浏览记录
└-- 设置
|-- 个人信息
|-- 意见反馈
|-- 反馈记录
|-- 隐私协议
|-- 清除缓存
└-- 退出登录
复制代码
二、 应用架构设计
1. 分层模块化设计
① 本实践暂时只支持直板机,为单 HAP 包形式,包含路由根节点、底部导航栏等。
① 本实践的基础特性层将应用功能拆分成 6 个相对独立的业务功能模块。
② 每个功能模块都具备高内聚、低耦合、可定制的特点,支持产品的灵活部署。
① 本实践的公共能力层分为公共基础能力和行业组件,均打包为 HAR 包被基础特性层的业务模块引用。
② 公共基础能力包含账号管理、动态布局等工具,公共类型定义,网络库,以及弹窗、加载等公共组件。
③ 行业组件将包含行业特点、可完全自闭环的能力抽出独立的组件模块,支持开发者在开发中单独集成使用,详见业务组件设计章节。
2. 业务组件设计
为支持开发者单独获取特定场景的页面和功能,本模板将功能完全自闭环的部分能力抽离出独立的行业组件模块,不依赖公共基础能力包,开发者可以单独集成,开箱即用,降低使用难度。
三、 行业场景技术方案
1. 一键搜题
1) 场景说明
用户可在首页-搜题进入一键搜题页面,可输入、语音输入,拍照识别,粘贴和清除功能。
2) 技术方案
① 根据语音识别可实现功能。
① 根据拍照可实现拍照功能,
② 图片获取后参考recognizeText获取具体结果。
3) 代码参考
2. 多级分栏
1) 场景说明
2) 技术方案
采用左右两个List做为基础组件来实现业务,三级、多级目录采用数据源刷新特性实现具体业务。
3. 答题
1) 场景说明
用户可通过首页点击每日一练可进入答题练习页面,可添加笔记、收藏、答题等相关业务,答题这块只做了单选业务。
2) 技术方案
四、 模板代码
1. 工程结构(下载模板)
详细代码结构如下所示:
Exam
├─commons/commonLib/src/main
│ ├─ets
│ │ ├─components
│ │ │ CommonHeader.ets // 一级页面标题组件
│ │ │ TopBar.ets // 标题菜单内容组件
│ │ ├─utils
│ │ │ Logger.ets // 日志
│ │ │ PreferenceUtil.ets // 首选项
│ │ ├─viewModel
│ │ │ BrowsingHistoryModel.ets // 记录模块数据模型
│ │ │ OrderInfo.ets // 订单数据模型
│ │ │ PracticeRecordModel.ets // 练习数据模型
│ └─resources
├─commons/router_module/src/main
│ ├─ets
│ │ ├─routerModule
│ │ │ │ RouterModule.ets // 路由
│ │ │ │ ├─constants
│ │ │ │ │ │ RouterMap.ets // 路由Key
│ └─resources
│─components/aggregated_payment/src/main
│ ├─ets
│ │ ├─common
│ │ │ Constant.ets // 常量类
│ │ ├─components
│ │ │ AggregatedPaymentPicker.ets // 支付组件
│ │ ├─model
│ │ │ Index.ets // 数据类型
│ │ │ WXApiWrap.ets // 微信支付数据类型
│ │ └─viewmodel
│ │ AggregatedPaymentVM.ets // 支付组件数据模型
│ └─resources
│─components/answer_questions/src/main
│ ├─ets
│ │ ├─components
│ │ │ AnswerQuestionsPage.ets // 答题组件
│ │ │ AddNotePage.ets // 添加笔记组件
│ │ │ AnswerSheetPage.ets // 答题卡组件
│ │ ├─dialog
│ │ │ AddNoteDialog.ets // 添加笔记弹框
│ │ │ AnswerSheetDialog.ets // 答题卡弹框
│ │ └─viewModel
│ │ TopicItemModel.ets // 答题选项模型
│ │ TopicPageModel.ets // 答题模型
│ └─resources
│─components/feed_back/src/main
│ ├─ets
│ │ ├─components
│ │ │ Feedback.ets // 意见反馈功能组件
│ │ ├─model
│ │ │ FeedbackRecordModel.ets // 数据类型
│ │ ├─utils
│ │ │ FileSelect.ets // 意见反馈功工具类
│ └─resources
│─components/select_category/src/main
│ ├─ets
│ │ ├─components
│ │ │ MainPage.ets // 二级分类组件
│ │ │ ThirdcatePage.ets // 三级分类组件
│ │ ├─model
│ │ │ SelectCateModel.ets // 数据类型
│ └─resources
│─components/login_info/src/main
│ ├─ets
│ │ ├─components
│ │ │ AgreementDialog.ets // 同意协议弹窗组件
│ │ │ QuickLogin.ets // 一键登录组件
│ │ ├─model
│ │ │ ErrorCode.ets // 错误码类型
│ │ │ UserInfo.ets // 用户类型
│ │ └─utils
│ │ AccountUtil.ets // 账户工具类
│ └─resources
│─components/search/src/main
│ ├─ets
│ │ ├─components
│ │ │ SearchPage.ets // 搜索组件
│ └─resources
│─components/search_question/src/main
│ ├─ets
│ │ ├─components
│ │ │ SearchQuestionPage.ets // 一键搜题组件
│ └─resources
│─components/base_select/src/main
│ ├─ets
│ │ ├─components
│ │ │ MainPage.ets // 基础通用组件
│ │ ├─model
│ │ │ SelectModel.ets // 选项数据模型
│ └─resources
│─features/homePage/src/main
│ ├─ets
│ │ ├─components // 封装组件
│ │ │ CourseBookComponent.ets // 资料卡片组件
│ │ │ CourseComponent.ets // 课程卡片
│ │ ├─model
│ │ │ ChapterPractice.ets // 分类页面数据模型
│ │ │ CommonTopic.ets // 分类数据模型
│ │ │ Course.ets // 课程数据模型
│ │ │ CourseArray.ets // 课程数组模型
│ │ │ CourseBook.ets // 资料模型
│ │ │ CourseQuestions.ets // 科目数据模型
│ │ │ PracticeMode.ets // 业务类型数据模型
│ │ │ TopicItemModel.ets // 答题类型数据模型
│ │ │ TopicModel.ets // 分类数据源
│ │ ├─pages
│ │ │ ChapterPractice.ets // 科目页面
│ │ │ FeaturedCourses.ets // 精选课程页面
│ │ │ MainPage.ets // 练习首页面
│ │ │ MaterialDownload.ets // 资料页面
│ │ │ SearchIndexPage.ets // 搜索页面
│ │ │ SearchInputPage.ets // 搜索输入框页面
│ │ │ SecondListPage.ets // 2级分类
│ │ │ ThirdListPage.ets // 3级分类
│ │ │ TopicHomePage.ets // 1级分类
│ │
│ └─resources
│─features/topicPage/src/main
│ ├─ets
│ │ ├─views
│ │ │ AnswerQuestionsPage.ets // 答题模式一页面
│ │ │ AnswerQuestionsTwoPage.ets // 答题模式二页面
│ │ │ CourseHomePage.ets // 课程页面
│ │ │ CourseIntroductionPage.ets // 课程详情页面
│ │ │ ExamResultPage.ets // 答题结果页面
│ │ │ GoodCourseDetailPage.ets // 精选课程页面
│ │ │ MockTestPage.ets // 科目练习页面
│ │ │ MyCollectionPage.ets // 收藏页面
│ │ │ MyNotesPage.ets // 笔记页面
│ │ │ MyWrongPage.ets // 错题页面
│ │ │ TestReportPage.ets // 测试报告页面
│ │ │ ViewNotePage.ets // 笔记组件
│ │ └─viewModel
│ │ │ CourseHomeModel.ets // 课程页面数据模型
│ │ │ PracticeMode.ets // 科目数据模型
│ │ │ SecondListModel.ets // 选项类型数据模型
│─features/minePage/src/main
│ ├─ets
│ │ ├─components
│ │ │ Header.ets // Header组件
│ │ ├─viewModel // 数据类型
│ │ │ MessageModel.ets
│ │ │ setUpModel.ets // 设置相关模型数据模型
│ │ │ MineModel.ets // 用户资料信息数据模型
│ │ ├─views
│ │ │ AboutPage.ets // 关于页面
│ │ │ AuthenticationPage.ets // 用户认证协议页面
│ │ │ BrowsingHistoryPage.ets // 浏览页面
│ │ │ CollectionPage.ets // 课程收藏页面
│ │ │ CoursePage.ets // 课程精选页面
│ │ │ EditPersonalCenterPage.ets // 个人信息详情页面
│ │ │ FeedbackPage.ets // 意见反馈页面
│ │ │ FeedbackRecordPage.ets // 反馈记录页面
│ │ │ MessageCenterPage.ets // 消息页面
│ │ │ MinePage.ets // 我的页面
│ │ │ MyOrderPage.ets // 订单首页页面
│ │ │ OneDayPracticeRecordsPage.ets // 单个练习记录页面
│ │ │ OrderDetailPage.ets // 订单详情页面
│ │ │ OrderListPage.ets // 订单页面
│ │ │ PracticeDetailsPage.ets // 反馈页面
│ │ │ PracticeRecordsPage.ets // 练习记录页面
│ │ │ PrivacyAgreementPage.ets // 同意
│ │ │ PrivacyPage.ets // 协议
│ │ │ PrivacyStatementPage.ets // 隐私页面
│ │ │ SetupPage.ets // 设置页面
│ │ │ TermsOfServicePage.ets // 用户服务页面
└─products/entry/src/main
├─ets
│ ├─entryability
│ │ EntryAbility.ets // 应用程序入口
│ ├─entrybackupability
│ │ EntryBackupAbility.ets // Backup配置入口
│ ├─pages
│ │ Index.ets // 入口页面
│ │ LoginPage.ets // login页面
│ ├─model
│ │ TabListItem.ets // 数据声明
│ ├─viewmodels
│ │ MainVM.ets // 页面数据模型
│ ├─common // 常量及Tab数据源
│ TabConstants.ets
└─resources
复制代码
2. 关键代码解读
本篇代码非应用的全量代码,只包括应用的部分能力的关键代码。
若需获取全量代码,请查看模板集成章节。
1) 一键搜题
// 语音识别
// 请求权限后创建服务并监听
this.atManager.requestPermissionsFromUser(this.context, ['ohos.permission.MICROPHONE'])
.then(async (data) => {
if (data.authResults[0] === 0) {
await this.createSREngine()
this.startListener()
}
})
.catch(() => {
//在此处进行异常处理
});
async createSREngine() {
const extraParams: Record<string, Object> = {
'locate': 'CN',
'recognizerMode': 'short'
}
const initParamsInfo: speechRecognizer.CreateEngineParams = {
language: 'zh-CN',
online: 1,
extraParams
}
try {
this.asrEngine = await speechRecognizer.createEngine(initParamsInfo)
this.setListener()
} catch (e) {
//在此处进行异常处理
}
}
startListener() {
const audioParam: speechRecognizer.AudioInfo = {
audioType: 'pcm',
sampleRate: 16000,
soundChannel: 1,
sampleBit: 16
};
const extraParam: Record<string, Object> = { 'maxAudioDuration': 40000, 'recognitionMode': 0 };
this.sessionId = new Date().getTime().toString()
const recognizerParams: speechRecognizer.StartParams = {
sessionId: this.sessionId,
audioInfo: audioParam,
extraParams: extraParam
};
this.asrEngine?.startListening(recognizerParams)
}
setListener() {
let that = this
// 创建回调对象
let setListener: speechRecognizer.RecognitionListener = {
// 开始识别成功回调
onStart() {
},
// 事件回调
onEvent() {
},
// 识别结果回调,包括中间结果和最终结果
onResult(_sessionId: string, result: speechRecognizer.SpeechRecognitionResult) {
that.textInput = that.textInputPre + result.result
},
// 识别完成回调
onComplete() {
that.textInputPre = that.textInput
},
// 错误回调,错误码通过本方法返回
// 返回错误码1002200002,开始识别失败,重复启动startListening方法时触发
// 更多错误码请参考错误码参考
onError() {
},
}
// 设置回调
this.asrEngine?.setListener(setListener);
};
// 拍照识别
async startCameraPicker() {
let pickerProfile: picker.PickerProfile = {
cameraPosition: camera.CameraPosition.CAMERA_POSITION_BACK,
};
let result: picker.PickerResult =
await picker.pick(getContext(), [picker.PickerMediaType.PHOTO, picker.PickerMediaType.VIDEO],
pickerProfile);
let uri = result.resultUri;
let imageSource: image.ImageSource;
let chooseImage: PixelMap;
setTimeout(async () => {
let fileSource = await fileIo.open(uri, fileIo.OpenMode.READ_ONLY);
imageSource = image.createImageSource(fileSource.fd);
chooseImage = await imageSource.createPixelMap();
if (!chooseImage) {
return;
}
// 获取图片后调用文本识别接口
let visionInfo: textRecognition.VisionInfo = {
pixelMap: chooseImage
};
let textConfiguration: textRecognition.TextRecognitionConfiguration = {
isDirectionDetectionSupported: true
};
await textRecognition.recognizeText(visionInfo, textConfiguration).then((textRecognitionResult) => {
if (textRecognitionResult.value !== '') {
this.search(textRecognitionResult.value)
}
})
}, 100)
}
复制代码
2) 系统路由封装及使用
// 1、定义路由工具类
import { RouterMap } from './constants/RouterMap';
export default interface NavRouterInfo {
url: string; // 跳转路由名
mode?: NavDestinationMode; // NavDestination类型
param?: Object; // 传递参数
onPop?: Callback<PopInfo>; // 回调事件
}
class RouterModule {
public static stack: NavPathStack = new NavPathStack();
static builderMap: Map<string, WrappedBuilder<[object]>> = new Map<string, WrappedBuilder<[object]>>();
// 页面跳转(指定页面)
public static push(info: NavRouterInfo, animated?: boolean) {
try {
RouterModule.stack.pushPathByName(info.url, info.param, info.onPop, animated);
} catch (err) {
}
}
// 页面替换(指定页面)
public static replace(info: NavRouterInfo) {
try {
RouterModule.stack.replacePathByName(info.url, info.param);
} catch (err) {
}
}
// 页面回退(上个页面)
public static pop<T = boolean>(result?: T, animated?: boolean) {
try {
RouterModule.stack.pop(result, animated);
} catch (err) {
}
}
// 页面回退(携带参数)
public static popWithRes(res: ESObject, animated?: boolean) {
try {
RouterModule.stack.pop(res, animated);
} catch (err) {
}
}
// 页面回退(至对应页面名)
public static popToName(name: string, animated?: boolean) {
try {
RouterModule.stack.popToName(name, animated);
} catch (err) {
}
}
// 页面栈清空(回Navigation)
public static clear(animated?: boolean) {
try {
RouterModule.stack.clear(animated);
} catch (err) {
}
}
// 获取页面栈大小
public static size(): number {
return RouterModule.stack.size();
}
// 获取参数(指定页面)
public static getNavParam<T = Object>(info: NavRouterInfo): T | undefined {
try {
const paramsArr = RouterModule.stack.getParamByName(info.url) as T[] | undefined[];
return paramsArr.pop();
} catch (err) {
}
return undefined;
}
// 获取页面名(页面栈前一个)
public static getSourcePage(): string | undefined {
const pathNames = RouterModule.stack.getAllPathName();
pathNames.pop();
return pathNames.pop();
}
}
export { RouterModule, RouterMap };
// 2、定义RouterMap 注册路由表时用
export enum RouterMap {
// 主页
MAIN_PAGE = 'MainPage',
}
// 3、注册路由表
export class RouterTable {
static builderMap: Map<string, WrappedBuilder<[object]>> = new Map<string, WrappedBuilder<[object]>>();
// 初始化路由表
public static routerInit() {
RouterTable.builderMap.set(RouterMap.MAIN_PAGE, wrapBuilder(MainPageBuilder));
}
// 通过名称获取builder
public static getBuilder(builderName: string): WrappedBuilder<[]> {
let builder = RouterTable.builderMap.get(builderName);
return builder as WrappedBuilder<[]>;
}
}
4、使用前需先init注册
// 初始化路由表
RouterTable.routerInit()
// 创建导航Navigation
@Builder
pageMap(name: string) {
NavDestination() {
RouterTable.getBuilder(name).builder()
}
.mode(NavDestinationMode.STANDARD)
.hideTitleBar(true)
.onBackPressed(() => {
return this.backPress(name)
})
}
build() {
Column() {
Navigation(RouterModule.stack) {
}
.hideNavBar(true)
.hideToolBar(true)
.hideTitleBar(true)
.hideBackButton(true)
.mode(NavigationMode.Stack)
.navDestination(this.pageMap)
}
}
复制代码
3. 模板集成
本模板提供了两种代码集成方式,供开发者自由选用。
1) 整体集成(下载模板)
开发者可以选择直接基于模板工程开发自己的应用工程。
① 通过 IDE 插件创建模板工程,开发指导。
② 通过生态市场下载源码, 下载模板。
③ 通过开源仓访问源码,仓库地址。
根据自己的业务内容修改模板,进行定制化开发。
2) 按需集成
若开发者已搭建好自己的应用工程,但暂未实现其中的部分场景能力,可以选择取用其中的业务组件,集成在自己的工程中。
① 通过 IDE 插件下载组件源码。开发指导
② 通过生态市场下载组件源码。 下载地址
以上是第三期“教育行业-教育备考”行业优秀案例的内容,更多行业敬请期待~
欢迎下载使用行业模板“点击下载”,若您有体验和开发问题,或者迫不及待想了解 XX 行业的优秀案例,欢迎在评论区留言,小编会快马加鞭为您解答~
同时诚邀您添加下方二维码加入“组件模板活动社群”,精彩上新 &活动不错过!
👉本系列持续更新,欢迎收藏本帖!
第一期:HarmonyOS官方模板优秀案例 | 便捷生活行业· 购物中心
第二期:HarmonyOS官方模板优秀案例 | 新闻行业· 综合新闻
第三期:HarmonyOS官方模板优秀案例 | 教育行业· 教育备考
第四期:HarmonyOS 官方模板优秀案例 | 餐饮行业· 美食菜谱(马上发布)
第五期:HarmonyOS 官方模板优秀案例 | 工具行业· 日历应用(马上发布)
👉HarmonyOS 组件模板相关推荐
评论