赞
踩
古诗词是中华传统文化的瑰宝,古人借诗词来言情、咏志、抒怀,古诗词记录了古人对于自然、社会、人文的观察与思考,也充满了真挚而多样的情感,有“每逢佳节倍思亲”的感慨孤独,有“但愿人长久,千里共婵娟”的真挚祝福,也有“爆竹声中一岁除,春风送暖入屠苏”的对于辞旧迎新的喜悦。每到季节更替、时节轮转、节日来临,总能唤起诗人对于人生的思考,造化的感悟,于是,诗意充盈在每一个平凡的日子之中,并融于我们的生活。而云服务和服务卡片又特别适合直观的展示诗词,于是有了根据节日或是节气,推荐展示符合主题的诗词的想法。
云服务,顾名思义,“元”代表着轻量级和原子化,其具有免安装的特性,服务卡片是其重要的表现形式,既具有信息展示的功能,有可以作为应用的入口,一触即达,简洁直观。
端云一体化,传统的开发模式基本是前后端分离的,前端负责UI的开发,后端负责数据业务和接口的编写,这种模式虽然有很多好处,但也存在着沟通成本高,前后端技术栈差异大,学习成本高等缺点,且后端一般需要部署到服务器上,还需要根据需求对服务器进行运维或者扩容,成本较高,较为繁琐,对于一般小型应用显然不太友好。而端云一体化较好的解决了这些痛点,将华为的serverless和元服务结合起来,使用同一IDE同时开发前后端,使用同一种语言typescript开发前后端,既节约了开发者的学习成本,也降低了开发难度,可以让开发者更专注于应用本身,而不是部署运维等事务。
参考官方给出的文档,和个人的摸索。
根据业务需求,我将需求拆分成了两个云函数,一个云函数的功能是根据今天的日期,查询今天是否是传统节日,或是节气,确定今天的诗词主题。另一个云函数的公司根据传入的诗词主题,查询云数据库中的诗词数据并返回。
首先是获取今日的诗词主题,为了获取今天的节日和节气数据,我引入了第三方库lunar-javascript,文档链接https://6tail.cn/calendar/api.html#lunar.jieqi.html,添加方式是在云函数get-theme的目录下的package.json里添加依赖 云函数get-theme代码如下:
- let isFestival: boolean = false;
- let isJieQi: boolean = false;
- let festivals=[]
- let jieQi=""
- // do something here
- const d = Lunar.fromDate(new Date())
- //获取物候名和候次
- const wuhou = d.getWuHou()
- const hou = d.getHou()
- let tag = ""
- let lunar=d.toString()
- let prev = d.getPrevJieQi();
- let next = d.getNextJieQi();
- //获取今天是否是节日
- let l = d.getFestivals();
- if (l.length > 0) {
- tag = l[0]
- festivals=l
- isFestival = true
- } else {
- jieQi = d.getJieQi();
- if (jieQi) {
- tag = jieQi
- isJieQi = true
- } else {
- //如果不是节气,获取上一个节气
- const prev_name = d.getPrevJieQi().getName();
- tag = prev_name
- }
- }
- logger.info("tag:" + tag)
- logger.info(event, context)
- callback({
- isFestival,
- festivals,
- isJieQi,
- jieQi,
- prev:{name:prev.getName(),time:prev.getSolar().toYmdHms()},
- next:{name:next.getName(),time:next.getSolar().toYmdHms()},
- hou,
- wuhou,
- tag,
- lunar,
-
- });
- };
-
- export { myHandler };
复制
部署云函数
可以通过IDE或是浏览器控制台调试云函数
云函数get-potery的功能是根据传入的主题在云数据库中查询符合条件的数据,那么我们需要先创建数据对象,导入数据,这一部分工作可以在IDE里通过编写配置文件完成,也可以通过浏览器控制台完成,推荐后者,因为操作起来比较直观。
感觉数据对象比较类似于关系型数据库定义表结构
创建数据区,注意不同数据区是相互隔离的,因此一定要注意数据查询和导入时,一定要区分数据区
导入数据
导入的Json文件格式大致如下 导入成功后可以看到 数据对象和数据导入完成后,可以开始编写第二个函数,既根据传入的主题查询云数据库。云函数调用云数据库参考官方文档https://developer.huawei.com/consumer/cn/doc/development/AppGallery-connect-Guides/query-clouddb-overview-0000001549142354,文档写的比较详细,因此不再赘述。 云函数结构如下 CloudDBZoneWrapper.js中代码如下
- const clouddb = require('@hw-agconnect/database-server/dist/index.js');
- const agconnect = require('@agconnect/common-server');
- const path = require('path');
- const shiju=require('./model/shiju')
- /*
- 配置区域
- */
- //TODO 将AGC官网下载的配置文件放入resources文件夹下并将文件名替换为真实文件名
- const credentialPath = "/resources/agc-apiclient-1255177787686308800-7284180544987715810.json";
- // 修改为在管理台创建的存储区名称
- let zoneName = "MyData"
- // 修改为需要操作的对象
- let objectName =shiju.shiju;
-
- let logger
-
- let mCloudDBZone
-
- class CloudDBZoneWrapper {
-
- // AGC & 数据库初始化
- constructor(log) {
- logger = log;
-
- let agcClient;
-
- try {
- agcClient = agconnect.AGCClient.getInstance();
- } catch (error) {
- agconnect.AGCClient.initialize(agconnect.CredentialParser.toCredential(path.join(__dirname, credentialPath)));
- agcClient = agconnect.AGCClient.getInstance();
- }
-
- clouddb.AGConnectCloudDB.initialize(agcClient);
-
- const cloudDBZoneConfig = new clouddb.CloudDBZoneConfig(zoneName);
-
- const agconnectCloudDB = clouddb.AGConnectCloudDB.getInstance(agcClient);
- mCloudDBZone = agconnectCloudDB.openCloudDBZone(cloudDBZoneConfig);
- }
-
- async queryAll() {
- if (!mCloudDBZone) {
- logger.info("CloudDBClient is null, try re-initialize it");
- console.log("CloudDBClient is null, try re-initialize it")
- return;
- }
- try {
- const resp = await mCloudDBZone.executeQuery(clouddb.CloudDBZoneQuery.where(objectName));
- return resp
- } catch (error) {
- logger.info('queryAll=>', error);
- console.log(error)
- }
- }
-
- async queryCompound(data) {
-
- if (!mCloudDBZone) {
- logger.info("CloudDBClient is null, try re-initialize it");
- console.log("CloudDBClient is null, try re-initialize it")
- return;
- }
- try {
- // 根据业务需要修改查询条件
- const cloudDBZoneQuery = this.setQuery(data);
- const resp = await mCloudDBZone.executeQuery(cloudDBZoneQuery);
- return resp.getSnapshotObjects()
- } catch (error) {
- logger.info('queryCompound=>', error);
- console.log(error)
- }
-
- }
-
- setQuery(data) {
- const cloudDBZoneQuery = clouddb.CloudDBZoneQuery.where(objectName);
-
- if("contains" in data) {
- let contains = data.contains;
- for (var key in contains) {
- cloudDBZoneQuery.contains(key, contains[key]);
- }
- }
-
- return cloudDBZoneQuery.limit(50);
- }
-
- }
-
- module.exports = CloudDBZoneWrapper;
复制
get-potert.ts中代码如下
- const CloudDBZoneWrapper = require("./CloudDBZoneWrapper.js");
- module.exports.myHandler = async function(event, context, callback, logger) {
- logger.info(event);
- const cloudDBZoneWrapper = new CloudDBZoneWrapper(logger);
- let tag="冬至"
- if(event.body){
- //接收传入的参数
- tag=JSON.parse(event.body)['tag']
- }
- logger.info("tag="+tag)
- let queryResult;
- queryResult = await cloudDBZoneWrapper.queryCompound({
- "contains": {
- "theme": tag
- }
- });
- //返回查询的结果
- callback({ theme:tag,poem:queryResult});
- };
复制
返回的结果如下
- {
- "poem":[
- {
- "author":"白居易",
- "theme":"夏至",
- "id":834,
- "title":"和梦得夏至忆苏州呈卢宾客",
- "content":"粽香筒竹嫩,炙脆子鹅鲜。"
- },
- {
- "author":"白玉蟾",
- "theme":"夏至",
- "id":835,
- "title":"赠潘高士二首·冬至炼朱砂",
- "content":"冬至炼朱砂,夏至炼水银。"
- }
- ],
- "theme":"夏至"
- }
复制
云端的开发大致完毕,将云函数部署到云端并调试完成后,进入端侧的开发
在“src/main/ets/widget/pages/WidgetCard.ets”文件中编写界面UI代码 代码如下:
-
- @Entry
- @Component
- struct WidgetCard {
- @LocalStorageProp('title') title:string="水调歌头";
- @LocalStorageProp('author') author:string="苏轼";
- @LocalStorageProp('content') content:string="但愿人长久,千里共婵娟";
- /*
- * The max lines.
- */
- readonly MAX_LINES: number = 2;
-
- /*
- * The action type.
- */
- readonly ACTION_TYPE: string = 'router';
-
- /*
- * The message.
- */
- readonly MESSAGE: string = 'add detail';
-
- /*
- * The ability name.
- */
- readonly ABILITY_NAME: string = 'EntryAbility';
-
- /*
- * The with percentage setting.
- */
- readonly FULL_WIDTH_PERCENT: string = '100%';
-
- /*
- * The height percentage setting.
- */
- readonly FULL_HEIGHT_PERCENT: string = '100%';
-
- build() {
- Stack() {
- Image($r("app.media.card_bg"))
- .width(this.FULL_WIDTH_PERCENT)
- .height(this.FULL_HEIGHT_PERCENT)
- .objectFit(ImageFit.Cover)
- Column() {
- Text(this.content)
- .fontSize($r('app.float.title_immersive_font_size'))
- .textOverflow({ overflow: TextOverflow.Ellipsis })
- .fontColor($r('app.color.text_font_color'))
- .maxLines(this.MAX_LINES)
- Text(`——${this.author} 《${this.title}》`)
- .fontSize($r('app.float.detail_immersive_font_size'))
- .opacity($r('app.float.detail_immersive_opacity'))
- .margin({ top: $r('app.float.detail_immersive_margin_top') })
- .textOverflow({ overflow: TextOverflow.Ellipsis })
- .fontColor($r('app.color.text_font_color'))
- .maxLines(this.MAX_LINES)
- Button("换一个").onClick(()=>{
- //点击按钮,刷新卡片内容
- postCardAction(this,{
- "action":"message",
- 'params': {
- 'msgTest': 'messageEvent'
- }
- })
-
- })
- }
- .width(this.FULL_WIDTH_PERCENT)
- .height(this.FULL_HEIGHT_PERCENT)
- .alignItems(HorizontalAlign.Center)
- .justifyContent(FlexAlign.Center)
- .padding($r('app.float.column_padding'))
- }
- .width(this.FULL_WIDTH_PERCENT)
- .height(this.FULL_HEIGHT_PERCENT)
- .onClick(() => {
- //点击卡片进入元服务页面
- postCardAction(this, {
- "action": this.ACTION_TYPE,
- "abilityName": this.ABILITY_NAME,
- "params": {
- "message": this.MESSAGE
- }
- });
- })
- }
- }
复制
卡片预览效果 在“src/main/ets/entryformability/EntryFormAbility.ts”中编写卡片生命周期 代码如下:
- import formInfo from '@ohos.app.form.formInfo';
- import formBindingData from '@ohos.app.form.formBindingData';
- import FormExtensionAbility from '@ohos.app.form.FormExtensionAbility';
- import update from "../services/util"
- export default class EntryFormAbility extends FormExtensionAbility {
- onAddForm(want) {
- // Called to return a FormBindingData object.
- let formData = {};
- return formBindingData.createFormBindingData(formData);
- }
-
- onCastToNormalForm(formId) {
- // Called when the form provider is notified that a temporary form is successfully
- // converted to a normal form.
- }
-
- onUpdateForm(formId) {
- update(this.context,formId)
- // Called to notify the form provider to update a specified form.
- }
-
- onChangeFormVisibility(newStatus) {
- // Called when the form provider receives form events from the system.
- }
-
- onFormEvent(formId, message) {
- console.info(`FormAbility onEvent, formId = ${formId}, message: ${JSON.stringify(message)}`);
- update(this.context,formId)
- }
-
- onRemoveForm(formId) {
- // Called to notify the form provider that a specified form has been destroyed.
- }
- onAcquireFormState(want) {
- // Called to return a {@link FormState} object.
- return formInfo.FormState.READY;
- }
- };
复制
其中,update是我编写的工具函数,util.ts的代码如下:
- import data_preferences from '@ohos.data.preferences';
- import { getPoem, getTheme } from "../services/Function"
- import formProvider from '@ohos.app.form.formProvider';
- import formBindingData from '@ohos.app.form.formBindingData';
-
- export default function update(context, formId) {
- data_preferences.getPreferences(context, 'mystore').then((preferences) => {
- //请求诗句主题
- getTheme(context).then((theme) => {
- let tag = theme['tag']
- console.log(tag)
- preferences.has(tag).then((has) => {
- if (has) {
- //如果有缓存,直接取出
- preferences.get(tag, "冬至").then((res) => {
- updateCard(formId, JSON.parse(res as string))
- })
- } else {
- //如果没有缓存,请求数据并缓存
- getPoem(context, tag).then((result) => {
- console.log(JSON.stringify(result))
- preferences.put(tag, JSON.stringify(result))
- updateCard(formId, result)
- })
- }
- })
- })
- })
- }
-
- function updateCard(formId, data) {
- console.log(JSON.stringify(data))
- let poem = data['poem']
- console.log("poem=" + poem)
- let count = poem.length
- let index = Math.floor(Math.random() * (count + 1));
- poem = poem[index]
- formProvider.updateForm(formId, formBindingData.createFormBindingData({
- title: poem['title'],
- author: poem['author'],
- content: poem['content']
- }))
- }
复制
其中,getPoem和getTheme的代码如下,代码参考模板给的调用云函数的代码:
- import agconnect from '@hw-agconnect/api-ohos';
- import "@hw-agconnect/function-ohos";
- import { Log } from '../common/Log';
- import { getAGConnect } from './AgcConfig';
- const TAG = "[AGCFunction]";
- export function getPoem(context,tag): Promise<string> {
- return new Promise((resolve, reject) => {
- getAGConnect(context);
- let functionResult;
- //这里的名字在AGC控制台中的函数触发器里可以找到
- let functionCallable = agconnect.function().wrap("get-potery-$latest");
- functionCallable.call({tag}).then((ret: any) => {
- functionResult = ret.getValue();
- console.log(JSON.stringify(functionResult)+"_____________________")
- Log.info(TAG, "Cloud Function Called, Returned Value: " + JSON.stringify(ret.getValue()));
- resolve(functionResult);
- }).catch((error: any) => {
- Log.error(TAG, "Error - could not obtain cloud function result. Error Detail: " + JSON.stringify(error));
- reject(error);
- });
- });
- }
- export function getTheme(context): Promise<string> {
- return new Promise((resolve, reject) => {
- getAGConnect(context);
- let functionResult;
- let functionCallable = agconnect.function().wrap("get-theme-$latest");
- functionCallable.call().then((ret: any) => {
- functionResult = ret.getValue();
- Log.info(TAG, "Cloud Function Called, Returned Value: " + JSON.stringify(ret.getValue()));
- resolve(functionResult);
- }).catch((error: any) => {
- Log.error(TAG, "Error - could not obtain cloud function result. Error Detail: " + JSON.stringify(error));
- reject(error);
- });
- });
- }
复制
在ets/pages下新建页面Main,并在entryability里修改入口页面为新建的页面。 Main.ets代码如下:
- import { getTheme, getPoem } from "../services/Function"
- import data_preferences from '@ohos.data.preferences';
-
- @Entry
- @Component
- struct Main {
- @State lunar: string = "二〇二三年八月二十"
- @State isFestival: boolean = false
- @State isJieQi: boolean = false
- @State festivals: string = ""
- @State jieQi: string = ""
- @State wuHou: string = "水始涸"
- @State hou: string = "秋分 三候"
- @State prev: string = ""
- @State next: string = ""
- @State tag: string = "秋分"
- @State poem: Array<Object> = new Array()
-
- aboutToAppear() {
- getTheme(getContext(this)).then((res) => {
- console.log(res['hou'])
- this.lunar = res['lunar']
- this.isFestival = res['isFestival']
- this.isJieQi = res['isJieQi']
- this.festivals = res['festivals'].join(',')
- this.jieQi = res['jieQi']
- this.wuHou = res['wuhou']
- this.hou = res['hou']
- this.prev = res['prev']['name'] + " " + res['prev']['time']
- this.next = res['next']['name'] + " " + res['next']['time']
- this.tag = res['tag']
-
- data_preferences.getPreferences(getContext(this), 'mystore').then((preferences) => {
-
- preferences.has(this.tag).then((has) => {
- if (has) {
- //如果有缓存,直接取出
- preferences.get(this.tag, "冬至").then((res) => {
- res = JSON.parse(res as string)
- let poem = res['poem']
- this.poem.push(...poem)
-
- })
- } else {
- getPoem(getContext(this), this.tag).then((result) => {
- console.log(JSON.stringify(result))
- preferences.put(this.tag, JSON.stringify(result))
- let poem = result['poem']
- this.poem.push(...poem)
- })
- }
- })
-
- })
- })
- }
-
- build() {
- Column() {
- Text(this.lunar).fontSize(32).fontWeight(FontWeight.Bold).fontColor($r("app.color.text_blue")).margin(12)
- if (this.isFestival) {
- Text(this.festivals).fontSize(24)
- }
- if (this.isJieQi) {
- Text(this.jieQi).fontSize(24)
- }
- Text(this.hou + " " + this.wuHou).fontSize(24).margin(12).fontColor($r("app.color.text_blue"))
- Text("上一节气:").fontSize(16).margin({ top: 12 })
- Text(this.prev).fontSize(18).margin(12).fontColor($r("app.color.text_blue"))
- Text("下一节气:").fontSize(16)
- Text(this.next).fontSize(18).margin(12).fontColor($r("app.color.text_blue"))
- Text("今日主题是:" + this.tag).fontSize(16).margin(12)
- Swiper(){
- ForEach(this.poem,(item,index)=>{
- Stack() {
- Image(index%2==0?$r("app.media.card_bg"):$r("app.media.card_bg2")).width("100%").height(200)
- Column() {
- Text(item.content).fontSize(18).margin(5).fontColor($r("app.color.white"))
- Text(`——${item.author}《${item.title}》`).fontSize(16).fontColor($r("app.color.white")).textOverflow({ overflow: TextOverflow.Ellipsis })
- }
- }.borderRadius(24).padding(12)
- },item=>item.title)
- }
-
- }.width("100%").height("100%")
-
- }
- }
复制
预览效果如下:
在APPSCOPE里的app.json5配置云服务的名称和图标
到此,诗词卡片开发完成
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。