当前位置: 首页 > news >正文

HarmonyOS 6.1 开发者盛宴|《灵犀厨房》实战(三十):【社区分享】本地社区功能——让菜谱从“独享”走向“共享”

HarmonyOS 6.1 开发者盛宴|《灵犀厨房》实战(三十):【社区分享】本地社区功能——让菜谱从“独享”走向“共享”

摘要:一个人做饭是生活,一群人分享是社区。前面 29 篇中,《灵犀厨房》的菜谱从推荐、收藏到烹饪,都围绕着“我一个人”。但烹饪的乐趣有一半在于分享——把自己拿手的番茄牛腩煲分享出去,看看别人做了什么。本篇为《灵犀厨房》新增“社区广场”——用户可在菜谱详情页一键分享菜谱到社区,其他用户可浏览所有分享,无需后端服务器,纯本地数据库实现。


一、引言:从“一个人”到“一群人”

打开《灵犀厨房》第 29 篇的版本,浏览菜谱、收藏、烹饪——一切围绕“我”。但你有没有想过:隔壁老王做的宫保鸡丁可能比菜谱库里的还正宗?同事小张的低脂健身餐才是减肥必备?

目前的设计无法满足一个基本需求:用户生成内容(UGC)。所有菜谱都来自我们预设的 10 道模拟数据,用户只能消费,不能生产。这既限制了内容生态的扩展,也缺少了社区互动带来的活跃度。

功能现状用户需求差距
菜谱来自预设 MockData用户想分享自己的拿手菜缺少 UGC 入口
菜谱详情只读用户想浏览他人的分享缺少社区展示页
数据在本地分享需要持久化存储缺少存储表

🎯本篇目标:用shared_recipes持久化表(已在第 28 篇预建)和约 130 行新代码,让用户可以分享菜谱到社区广场、浏览所有分享的菜谱。不需要后端服务器,纯本地数据库实现,为后续的云端社区打下基础。


二、功能设计:两个页面,一套数据

菜谱详情页 社区广场 ┌──────────────────┐ ┌──────────────────┐ │ 🍳 番茄牛腩煲 │ │ 👤 灵犀大厨 │ │ 📋 食材清单 │ │ 🍳 番茄牛腩煲 │ │ 第1步 焯水 │ │ 食材:牛腩、番茄... │ │ 第2步 炒香 │ 点击分享 │ 步骤:焯水; 炒香... │ │ ... │────────→│ 5/28 14:30 │ │ │ │ │ │ [🔄分享] [❤收藏] │ │ 👤 小明 │ └──────────────────┘ │ 🍳 宫保鸡丁 │ │ ... │ └──────────────────┘

交互模型

  1. 用户在菜谱详情页点击分享按钮 → 菜谱信息写入shared_recipes
  2. 用户在“我的”Tab 进入社区广场 → 从数据库按时间倒序加载所有分享
  3. 点击社区广场中的某条分享 → 进入该菜谱的详情页(复用现有 RecipeDetailPage)

三、数据层:第 28 篇已预建的表

在第 28 篇中,我们已经预建了shared_recipes表:

CREATETABLEIFNOTEXISTSshared_recipes(idINTEGERPRIMARYKEYAUTOINCREMENT,user_nameTEXTNOTNULL,recipe_nameTEXTNOTNULL,ingredientsTEXT,stepsTEXT,shared_atINTEGERDEFAULT(strftime('%s','now')));

本篇新增的用途:在第 28 篇中,这张表只是“预留字段”,没有写入逻辑。本篇将实现完整的 INSERT(分享)和 SELECT(广场加载)操作。

字段类型存储格式用途
user_nameTEXT固定值“灵犀大厨”(后续对接登录系统)分享者标识
ingredientsTEXT分隔(如“牛腩、番茄、洋葱”)食材展示
stepsTEXT;分隔(如“焯水:…; 炒香:…”)步骤展示
shared_atINTEGERUnix 时间戳按时间排序

设计考量:为什么ingredientssteps用分隔符字符串而非 JSON?因为这两个字段在本篇中只用于展示,不需要结构化查询。分隔符字符串可以直接展示(牛腩、番茄、洋葱),不需要JSON.parsejoin。如果后续需要查询“包含牛腩的所有分享”,再迁移到 JSON 不迟。


四、分享按钮:从详情页到数据库的“一键传送”

4.1 入口位置

RecipeDetailPage底部操作栏中,收藏按钮之前新增橙色分享按钮:

// ★ 分享到社区Button({type:ButtonType.Circle}){SymbolGlyph($r('sys.symbol.square_and_arrow_up')).fontSize(16).fontColor([Color.White])}.width(38).height(38).backgroundColor('#FF8C5A').onClick(()=>this.shareToCommunity())

4.2 shareToCommunity 方法

privateasyncshareToCommunity():Promise<void>{try{awaitstoreHelper.insert('shared_recipes',{user_name:'灵犀大厨',recipe_name:this.recipe.name,ingredients:this.recipe.ingredients.join('、'),steps:this.recipe.steps.join('; '),shared_at:Date.now()});ToastUtil.showToast(this.getUIContext(),'✅ 已分享到社区');}catch(err){ToastUtil.showToast(this.getUIContext(),'分享失败,请重试');}}
SQLiteRelationalStoreHelperRecipeDetailPage👤 用户SQLiteRelationalStoreHelperRecipeDetailPage👤 用户点击分享按钮insert('shared_recipes', {name, ingredients, steps})INSERT INTO shared_recipesrowIdToast "已分享到社区"

图一解读:分享流程极简——用户点击按钮,数据直接写入 SQLite,返回成功提示。没有网络请求、没有队列、没有审核。这不是阉割版,是 MVP(最小可行产品)的哲学:先验证用户是否愿意分享,再考虑内容审核和云端同步。

4.3 设计考量:为什么固定用户名“灵犀大厨”?

当前版本还没有完整的用户登录与身份管理功能,用户名使用固定值“灵犀大厨”占位。当后续完成登录功能对接后,只需将此处替换为authViewModel.username,即可实现真实用户名分享。


五、社区广场页面:SQLite 的“流式读取”

5.1 CommunityPage.ets

新建页面entry/src/main/ets/pages/CommunityPage.ets

@Entry@ComponentV2struct CommunityPage{@Localrecipes:SharedRecipe[]=[];@LocalisLoading:boolean=true;asyncaboutToAppear():Promise<void>{awaitthis.loadSharedRecipes();}privateasyncloadSharedRecipes():Promise<void>{try{constrs=awaitstoreHelper.querySql('SELECT * FROM shared_recipes ORDER BY shared_at DESC LIMIT 50');this.recipes=[];while(rs.goToNextRow()){this.recipes.push({id:rs.getLong(rs.getColumnIndex('id')),user_name:rs.getString(rs.getColumnIndex('user_name')),recipe_name:rs.getString(rs.getColumnIndex('recipe_name')),ingredients:rs.getString(rs.getColumnIndex('ingredients')),steps:rs.getString(rs.getColumnIndex('steps')),shared_at:rs.getLong(rs.getColumnIndex('shared_at'))});}rs.close();this.isLoading=false;}catch(err){console.error('[Community] 加载失败:',JSON.stringify(err));this.isLoading=false;}}}

5.2 UI 布局

每个 ListItem 展示:

┌──────────────────────────────────┐ │ 👤 灵犀大厨 5/28 14:30 │ │ 🍳 番茄牛腩煲 │ │ 食材:牛腩、番茄、洋葱、胡萝卜... │ │ 步骤:焯水:牛腩切块...; 炒香... │ └──────────────────────────────────┘
  • 分享者名 + 时间戳在顶部
  • 菜谱名用主题色高亮
  • 食材和步骤各占两行,超出省略
  • 卡片背景为$r('app.color.bg_card'),自动适配深色模式

5.3 路由注册

需在main_pages.json中添加路由:

{"src":["pages/CommunityPage"]}

5.4 入口位置

在“我的”Tab 中新增社区广场入口:

// MainContainer.ets → ProfileTabContentRow(){Row({space:10}){Text('🌐').fontSize(18)Text('社区广场').fontSize(15).fontColor('#333')}Blank()SymbolGlyph($r('sys.symbol.chevron_right')).fontSize(14).fontColor(['#CCC'])}.onClick(()=>{this.getUIContext().getRouter().pushUrl({url:'pages/CommunityPage'})})

六、代码交付清单

文件新增/修改行数说明
RecipeDetailPage.ets修改+15新增分享按钮 +shareToCommunity()方法
CommunityPage.ets新文件+105社区广场页面
MainContainer.ets修改+10“我的”Tab 新增社区广场入口
main_pages.json修改+1新增路由注册
RelationalStoreHelper.ets无需修改0shared_recipes表已在第 28 篇预建

七、设计决策

决策选择理由
纯本地数据库不依赖后端当前阶段无需服务器;后续可扩展为云端同步
ingredients/steps用分隔符字符串分隔食材,;分隔步骤只展示不查询,分隔符比 JSON 更直观
固定用户名“灵犀大厨”占位值登录系统对接后替换为authViewModel.username,当前保证功能可跑通
shared_at用 Unix 时间戳整数排序比字符串快展示时用formatDate()转换即可
查询 LIMIT 50限制单次加载量本地数据库中数据量有限,50 条足够展示全部历史分享

八、设计哲学:MVP 的“先跑通再完善”

本篇的分享功能是一个典型的 MVP(最小可行产品)实现。它有三个“不完美”:

  1. 用户名是固定的:“灵犀大厨”不是真实用户名,但现阶段没有登录系统,真实用户名需要等登录功能完善后对接。
  2. 没有内容审核:用户可以分享任何内容,但当前是本地数据库,数据只在用户自己的设备上,不存在合规风险。
  3. 没有云端同步:分享只在本地,其他设备看不到,但后续接入后端 API 后,只需将storeHelper.insert替换为apiService.shareRecipe(),其他代码零改动。

这三个“不完美”是有意为之,不是能力不够。MVP 的核心哲学是:先验证核心行为(用户是否愿意分享),再逐步完善周边设施(身份、审核、同步)。如果用户根本不愿意分享,你把审核系统做得再完善也是浪费。


九、本阶段总结与下篇预告

本篇用约 130 行新代码,为《灵犀厨房》打开了从“独享”到“共享”的大门:

  • 分享按钮:在菜谱详情页一键分享菜谱到社区,食材和步骤自动格式化
  • 社区广场:按时间倒序展示所有分享,卡片式布局清晰展示菜谱信息
  • 纯本地实现:不依赖后端服务器,shared_recipes表在第 28 篇已预建
  • MVP 哲学:先验证分享行为,再逐步完善身份和审核

现在的社交体验

🍳 做了一道拿手的番茄牛腩煲 → 点击分享 → 出现在社区广场 → 其他人打开 App 就能看到!

下篇预告:第 31 篇《应用权限管理与隐私保护最佳实践》。我们将系统梳理《灵犀厨房》需要的权限,按“最小权限原则”清理不必要的权限声明,并讲解 Health Kit 授权和 OAuth 隐私合规的最佳实践。


📚 本系列持续更新中:下一篇将让 App 在权限和隐私上合规,为发布到 AppGallery 做最后的准备。

🔗专栏入口:[《HarmonyOS6.1全场景实战》合集]

📦 获取基线版本源码包:包括第1-15篇所有代码 + 架构文档 + Flask 后端

如果你觉得这篇文章对您有所帮助,麻烦您动动发财之手点赞 👍、收藏 ⭐ 和评论 💬。谢谢大家!!
纯血鸿蒙,用心造厨。我们下一篇见!

http://www.gsyq.cn/news/1488770.html

相关文章:

  • 炉石传说HsMod:解锁55项隐藏功能的游戏体验革命
  • 3步解锁AMD Ryzen处理器隐藏性能:SMU Debug Tool新手完全指南
  • 从原理看 Arthas 为何比 IDEA Profiler 更“懂”你的代码
  • Vue i18n动态加载进阶:结合Pinia/Vuex管理多语言状态与接口缓存策略
  • 哔咔漫画下载器终极指南:快速搭建个人离线漫画库的完整方案
  • LangGraph+ElevenLabs构建可控AI播客生产流水线
  • ESM 与 ESMFold:当蛋白质序列成为生命语言
  • 手把手教你用C语言实现SM4国密算法(仅用stdio.h,附完整可运行代码)
  • 3大核心功能+5分钟上手:用OpenDroneMap将无人机照片变身高精度3D地图
  • 商业旅拍后期修图痛点全攻克:像素蛋糕一站式AI精修方案
  • 卡梅德生物技术快报|同位素标记制备碳纳米材料及全流程示踪检测方案
  • Temu全托陪跑综合评估:专业背景、结果保障、风险控制、口碑数据怎么判断 - 麦克杰
  • Mythos门控发布:AI模型自我校验与可控澄清技术解析
  • i.MX 8M Nano到i.MX 93迁移:电源管理架构与DVFS/VFS配置实战解析
  • OpenLayers 6 核心四要素:Map、View、Layer、Source 到底怎么用?一个外卖配送地图的实战案例讲透
  • Super IO:重新定义Blender工作流的智能剪贴板导入导出解决方案
  • MC68HC912 Flash与EEPROM底层编程:SST算法与AUTO模式详解
  • APK签名校验攻防实战:从V1签名到‘幸运破解器’的逆向之旅
  • Argo cd基础
  • 深入解析ITC137电机控制板:独立与终端模式下的PWM与SVM实战
  • 大模型 API 聚合路由推荐:Token173 500 + 模型统一调度与高可用架构,编程 / 生图 / 视频全场景落地
  • Apktool重打包实战:给旧APK注入一个So文件(附完整命令行记录)
  • i.MX RT600串行NOR Flash启动配置全解析:从BootROM原理到XIP映像烧录实战
  • 保姆级教程:编译完OpenCASCADE后,别忘了把这几个文件夹的DLL拷进系统目录(Win10/11实测)
  • Biotin-LC-PEG1-NHS ester,生物素-LC-聚乙二醇1-NHS酯
  • S32DS开发环境适配MPC5775B:从MPC5777C工程模板迁移的完整指南
  • 如何解决QuPath命令行图像解析问题:完整技术指南
  • 生产级机器学习系统设计:从模型部署到可信决策流
  • 基于NXP KW36/KW38的混合网络固件升级方案:蓝牙OTAP与LIN/CAN总线分发
  • i.MX RT外部RAM调试:.mac文件初始化FlexSPI与HyperRAM实战