尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

基于微信小程序的本地健康宝系统设计与实现详解

基于微信小程序的本地健康宝系统设计与实现详解 1. 项目整体设计与技术选型思路先聊聊这套项目的定位。标题里写得很清楚基于微信小程序的本地健康宝系统交付物包括源码、论文lw、部署文档和讲解视频。我第一次拿到这个工程时第一反应是去翻它的整体架构——因为市面上类似的小程序项目太多但绝大多数都是“半成品”要么只有页面没有逻辑要么数据库设计一塌糊涂要么根本没有可用的部署文档。这套项目之所以值得拆开来讲核心在于它把“本地”这个概念做透了。什么是“本地健康宝”的本地不是说你手机本地运行而是指数据存储和处理都在本地完成不需要额外搭建服务器不需要买云数据库甚至不需要企业认证就能跑起来。对于毕设、课程设计或者个人作品集来说这是最务实的一条路线——你不需要去配置 ECS、不需要去申请 HTTPS 证书拿到源码导入微信开发者工具就能跑。这一点在部署文档里其实占了很大篇幅我后面会细说。技术选型上项目用的是微信小程序原生框架没有采用 uni-app 或者 Taro。为什么我个人的观点是原生框架虽然写起来啰嗦一点——每个页面要四个文件wxml、wxss、js、json但它的好处是性能可控、调试直观而且微信开发者工具对原生项目的支持是最完整的。更关键的是对于学习项目来说原生框架能让你看到小程序运行的真实机制页面生命周期、数据绑定、事件冒泡、本地缓存读写这些东西在跨端框架里都被封装掉了一层反而不利于理解底层逻辑。整个项目的目录结构也很有代表性我贴在下面方便对照├── cloudfunctions // 云函数可选本地版默认不使用 ├── components // 自定义组件状态卡片、打卡表单等 ├── pages │ ├── index // 首页健康状态总览 │ ├── checkin // 打卡页健康信息提交 │ ├── record // 记录页历史打卡记录 │ ├── stats // 统计页趋势分析 │ └── profile // 我的个人信息与设置 ├── utils │ ├── storage.js // 本地存储封装 │ ├── format.js // 日期时间格式化工具 │ └── health.js // 健康状态判定逻辑 ├── app.js ├── app.json ├── app.wxss └── project.config.json这个结构是典型的“页面-工具-组件”三层划分。pages 按功能模块拆页面utils 放纯逻辑——这些文件不涉及页面渲染只处理数据components 放复用模块。为什么要这么拆因为健康宝的核心流程是“填表单 - 存数据 - 算状态 - 展示记录”这四个动作分别落在不同页面里如果逻辑不抽出来就会出现同一个判断函数在不同页面各写一份的情况改一处漏三处。项目交付物里的“讲解视频”也会从这套目录讲起因为面试或者答辩的时候老师问你“项目怎么设计的”你从目录结构开始讲是最清晰的——它直接对应了系统的模块划分。2. 核心功能拆解与页面设计逻辑健康宝系统的功能掰开揉碎其实就四个字打卡、展示。但越是简单的需求越考验设计功力。我拿这套项目的页面设计来说说它是怎么把“健康状态”这件事落到小程序里的。2.1 健康状态判定的业务规则先看最核心的判定逻辑。用户在打卡页提交信息之后系统需要给出一个状态正常、关注、异常对应小程序里展示的绿色、黄色、红色状态卡片。判定规则是怎么设计的源码里 health.js 这个文件写得很清楚核心逻辑我简化如下体温字段小于 37.3℃ 记为正常37.3℃-38.5℃ 记为关注大于 38.5℃ 记为异常。症状字段无任何症状为正常存在咳嗽、乏力等单一症状为关注存在呼吸困难、持续高热等为异常。行程字段近 14 天内无中高风险地区旅居史为正常有相关地区旅居史但已完成核酸阴性报告为关注有旅居史且未做核酸检测为异常。综合判定取各项结果中的最高级别。也就是只要有一项异常整体状态就是异常。这套规则写起来不难难的是边界处理。我在实测的时候遇到一个问题用户某天填了体温 37.2℃第二天再进来页面还显示上一次的体温值。这对于连续打卡的场景是合理的——大部分人体温相对稳定回显上一次的记录可以减少输入成本。但如果不做“日期判断”就会出现一个 bug昨天填的异常数据今天用户还没打卡首页就按异常状态展示了这就不对。源码里的解决方案是给每条打卡记录加上date字段格式为yyyy-MM-dd首页读取状态时先判断记录日期是否等于今天不是今天的就不作为当前状态展示。这个设计很小但特别实用也是答辩时能拿出来讲的亮点。2.2 页面交互与组件复用再说页面。首页index是最复杂的页面它要展示三块内容当前状态卡片、今日天气提示、快捷打卡入口。状态卡片用了一个自定义组件status-card接收一个status参数内部根据参数切换背景色、图标和文案。为什么用组件而不用模板因为记录页和统计页也需要展示状态卡片抽成组件之后三处共用改样式只改一处。这是小程序开发里“组件化思维”的基本功。打卡页checkin是数据源头。页面上有体温输入框数字输入、症状多选checkbox-group、14天内行程描述input、备注textarea。这里有一个细节值得注意体温输入用的是input typedigit而不是普通文本输入——只有这样手机键盘才会弹出带小数点的数字键盘否则用户要在全键盘里手动切数字体验很差。这个细节在讲解视频里被单拎出来讲了我觉得是有道理的——很多初学者写表单type 属性随便填真机一测才发现问题。记录页record就是一个简单的列表按时间倒序展示打卡记录每条记录左边是日期右边是当时的健康状态标签。列表用wx:for渲染配合pull-down-refresh实现下拉刷新。数据量大了之后源码里做了一个限制本地最多保存 90 条记录超出后自动删除最早的记录。这是从存储空间角度考虑的——wx.setStorageSync的单个 key 限制是 1MB如果不做清理半年打卡数据差不多就会顶满。统计页stats是加分项。它用 canvas 画了一个 7 天体温折线图横轴是日期纵轴是体温。小程序原生 canvas API 写起来比较繁琐——要手动计算坐标但源码里的实现还算清爽。核心结论是有了这个页面整个项目的完整度直接上一个档次论文里“数据分析与可视化”章节才有东西可写。2.3 数据存储的本地化设计这套系统的数据存储全部走wx.setStorageSync和wx.getStorageSync没有网络请求。存储结构是三个 keyuser_info用户个人信息姓名、手机号、所在城市对象类型。checkin_records打卡记录数组数组元素是 {date, temperature, symptoms, travel, remark, status}。health_status当前健康状态对象类型 {date, status, updateTime}。为什么不直接用一个 key 存所有数据因为三部分数据的读写频率不同user_info只有设置页会动checkin_records每次打卡都追加health_status每次打卡后立即更新。分开存可以减少不必要的序列化和反序列化开销。虽然小程序的本地缓存读写速度很快这个性能差异基本可以忽略但好习惯就是这样养成的——如果一个 key 塞了所有东西后续想单独清某一部分数据就只能整块重写很容易出问题。3. 关键模块的实现细节与关键代码解析这一章是全文实操含量最高的部分。我从源码里挑了几个关键实现来拆解包含用户登录流程、打卡提交逻辑、状态判定算法、日期工具函数。这些都是可以“抄作业”的代码我在注释里也尽量写明了为什么要这么写。3.1 用户登录与信息初始化微信小程序的登录体系严格来说有两种一是wx.login换取 openid 的静默登录二是用户主动授权手机号的快速验证组件。本地版健康宝不需要远程服务器所以用不到wx.login的后端解密流程它的做法更轻——直接读取本地缓存的user_info没有就先走设置页。// utils/storage.js const USER_INFO_KEY user_info function getUserInfo() { return wx.getStorageSync(USER_INFO_KEY) || null } function setUserInfo(info) { wx.setStorageSync(USER_INFO_KEY, info) } function hasUserInfo() { return !!getUserInfo() } module.exports { getUserInfo, setUserInfo, hasUserInfo }app.js 的onLaunch里会判断hasUserInfo()如果没有就跳转设置页要求用户先填姓名和手机号再使用。这个小逻辑保证了后续所有打卡记录都能关联到用户身份。很多人会觉得“本地小程序还要什么登录”真到了答辩评审问你“用户身份怎么区分”的时候这一小块设计就是你的回答。3.2 打卡提交与数据校验打卡页的提交按钮绑定了submitCheckin方法。源码里做了三层校验必填项校验、体温范围校验、行程字数校验。必填项用if (!data.temperature)判断提示用wx.showToast的none图标。这里有个小坑wx.showToast默认显示success图标如果要提示“请填写体温”这类错误信息必须显式传icon: none否则界面上会出现一个绿色的对勾语义完全不对。// pages/checkin/checkin.js submitCheckin() { const temperature Number(this.data.temperature) const symptoms this.data.symptoms const travel this.data.travel.trim() if (!this.data.temperature) { wx.showToast({ title: 请填写体温, icon: none }) return } if (isNaN(temperature) || temperature 35 || temperature 42) { wx.showToast({ title: 体温范围应为35-42℃, icon: none }) return } if (!symptoms.length) { wx.showToast({ title: 请选择当前症状, icon: none }) return } const status judgeHealthStatus({ temperature, symptoms, travel }) const record { date: formatDate(new Date()), temperature, symptoms, travel, remark: this.data.remark.trim(), status, timestamp: Date.now() } const records getRecords() records.push(record) setRecords(records) setHealthStatus({ date: record.date, status, updateTime: Date.now() }) this.setData({ checked: true }) }体温范围校验的 35-42 这个区间是参考测温设备的常规量程定的。实测中如果用户填 44℃系统应该拦住而不是生成异常状态——因为 44℃ 已经是数据错误而不是健康异常这两者需要在根源上区分。存储记录的setRecords封装里做了两件事新记录追加到数组尾部同时检查数组长度是否超过 90超过就把头部最早的纪录shift()掉。这个逻辑放在存储封装里而不是放在页面里是为了保证无论从哪里写入记录清理策略都是一致的。3.3 健康状态判定函数状态判定逻辑judgeHealthStatus放在utils/health.js。我品了一下这个实现整体思路很朴素就是分段打分、取最高级别function judgeHealthStatus({ temperature, symptoms, travel }) { let level 0 // 0正常 1关注 2异常 if (temperature 38.5) { level Math.max(level, 2) } else if (temperature 37.3) { level Math.max(level, 1) } if (symptoms.includes(呼吸困难)) { level Math.max(level, 2) } else if (symptoms.includes(咳嗽) || symptoms.includes(乏力)) { level Math.max(level, 1) } if (travel travel.includes(中高风险)) { level Math.max(level, 2) } return [normal, attention, abnormal][level] }这个函数没有任何网络请求纯本地计算可测试性很强——这一点是部署文档和论文里反复强调的纯函数是单元测试的最佳对象也是系统可靠性的基础。有一个细节是文案关键词匹配。travel.includes(中高风险)的做法简单但不够严谨——如果用户输入“经过中高风险地区并做了核酸”也会被判定为异常。在实际运营中这种“误杀”其实可以接受因为本地版没有后端人工复核流程宁可比严格。但如果这个系统要接真实业务建议把行程信息改成单选下拉框而不是自由输入从源头规范字段值。3.4 日期工具函数的实现日期处理是所有打卡系统的痛点。源码里formatDate函数用的是比较质朴的写法function formatDate(date) { const y date.getFullYear() const m String(date.getMonth() 1).padStart(2, 0) const d String(date.getDate()).padStart(2, 0) return ${y}-${m}-${d} }注意getMonth()返回的是 0-11所以要加 1String.padStart补零是 ES2017 的语法小程序基础库 2.2.3 以上都支持。如果你在老旧的基础库版本上跑可能会报错建议换成(0 (date.getMonth() 1)).slice(-2)这种兼容写法。首页判断“今天是否已打卡”也用这个函数取health_status.date和formatDate(new Date())做字符串比较。字符串日期比较在格式统一的前提下是完全可靠的不需要额外转时间戳。3.5 首页状态展示的动态联动首页的onShow生命周期里会同步最新状态数据。为什么用onShow而不是onLoad因为小程序页面栈的机制决定从打卡页返回首页时首页不会重新onLoad但会重新onShow。数据联动逻辑放在onShow里才能保证每次返回首页都看到最新状态。这是一个新手极容易踩坑的点——写在onLoad里你会发现打卡回来首页数据不变怎么刷新都没反应。onShow() { const status getHealthStatus() const userInfo getUserInfo() this.setData({ status: status || { date: , status: normal }, userInfo }) this.setData({ checkedToday: status status.date formatDate(new Date()) }) }4. 论文lw写作组织与题目拆解这套交付物里有一份完整论文很多人问过我怎么把项目写成论文。我的看法是毕设论文不是软件安装手册你要讲清楚的不只是“我用小程序做了个系统”而是“我解决了什么问题、用了什么方法、怎么验证有效”。下面按论文常见的章节结构说一说这套项目的“健康宝”是怎么组织的。4.1 摘要与关键词的写法摘要要覆盖四要素背景、方法、结果、意义。参考这套项目的论文摘要它的逻辑链大致是背景日常健康管理需求日益增长传统表格登记和体温记录存在效率低、易遗漏的问题方法设计并实现了一个基于微信小程序的本地健康管理小程序采用原生开发框架进行前端开发利用微信本地缓存实现数据持久化结果实现了健康信息打卡、状态判定、历史记录查询和趋势统计等功能意义为个人和社区提供轻量级的健康管理工具具有良好的便携性和实用性。关键词列的是“微信小程序健康管理本地存储打卡系统”。摘要的高频坑是写成了功能列表一条一条列“本系统有登录功能、有打卡功能、有查询功能”——这没有错但毕设论文考察的是你“能不能把事情想清楚”写清楚为什么和怎么做比罗列做了什么更重要。4.2 需求分析怎么写需求分析章节不要上来写“本系统需要登录、打卡”。需求分析的第一步应该是角色分析。这套系统有两个角色普通用户使用小程序完成每日健康信息录入、查看自己的健康状态和历史记录系统管理员可选查看统计汇总数据关注异常状态。本地版里管理员功能只做预留论文里可以写“后续可扩展”。然后要写功能需求和非功能需求。功能需求包括用户信息维护、每日健康打卡、健康状态判定、历史记录查询、数据统计展示。非功能需求包括数据安全性、操作便捷性、兼容性和响应速度——响应速度要有一个量化指标例如“页面切换时间不超过 1 秒”。再往下是可行性分析技术可行性小程序开发文档完整、原生框架成熟、经济可行性小程序个人主体注册免费、无需服务器、操作可行性界面简洁、一次学习即可使用。这部分是毕设论文的必修课评审老师必看。4.3 系统设计章节的图纸补充系统设计章节除了文字描述还要画图。论文里整理的架构图虽然规则限制我不能在这里画图但它的设计思路是可以拆解的数据流向是“页面组件 - 工具函数 - 本地缓存”三层结构清晰。小程序端只负责展示和采集核心业务逻辑放在 utils 层这是一次很好的“剥离”示范——逻辑不依赖页面就可以被独立测试和复用。数据库设计章节也要写。虽然是本地存储但论文里用了数据表的形式来描述比如字段名类型说明打卡记录表idString唯一标识由时间戳生成dateString打卡日期格式 yyyy-MM-ddtemperatureNumber体温数值symptomsArray症状列表travelString行程描述remarkString备注信息statusString健康状态 normal/attention/abnormaltimestampNumber打卡时间戳这种表格式的写法和实际实现完全对应既方便自己写的时候理思路答辩的时候也能直接滚屏讲。4.4 系统测试与用户反馈测试章节我给这套论文归纳了三个维度功能测试正常值输入、边界值输入体温 37.3℃ / 38.5℃、非法输入空值、超范围。异常输入测试要注意记录系统的“容错处理逻辑是否正确”而非“系统是否崩溃”。兼容性测试在 iOS 和 Android 两端各跑一遍核心流程。实测中发现 iOS 的键盘弹起会遮住提交按钮需要在page.json里设置disableScroll: false并在键盘弹起时手动调整布局Android 端则要测试返回键行为和首页下拉刷新。体验性测试叫上 5-8 个同学从注册开始走完整流程记录每一步的操作时间和疑问点。这套系统实测下来第一次上手平均需要 2-3 分钟主要的操作卡点集中在“行程描述”这个必填项上——很多人不知道写什么后来改成选填本题“有/无中高风险地区旅居史”的单选体验好了不少。论文的组织核心是让你的代码和文档互相印证。“我是这么写的——我发现有问题——我改成了这样”这个过程就是最好的论文素材。5. 部署上线与常见问题排查实录交付物里的部署文档我完整看过里面最核心的一句话是不需要服务器不需要域名备案不需要小程序认证。因为本地版健康宝不走网络请求你只需要一个微信小程序账号个人主体即可和一个安装了微信开发者工具的开发环境。5.1 从源码到预览的完整步骤拿到源码包比如health-app-source.zip之后按下面步骤操作安装最新版微信开发者工具用手机微信扫码登录。打开工具选择“导入项目”。项目目录选择解压后的源码目录AppID 可以先用“测试号”。点击“编译”小程序会直接编译并在模拟器里打开首页。第一次进首页如果提示需要设置用户信息属于正常流程填写姓名和手机号后就进入了健康状态总览。用手机扫码“预览”就可以在真机上体验完整功能。这套流程是部署文档的骨架没有任何隐藏配置。“本地版”的优势在部署阶段体现得淋漓尽致——你不需要去微信公众平台后台配置任何 request 合法域名因为根本没有 request。如果后续要正式发布上线再走微信公众平台提交审核。个人主体的小程序类目选“工具-信息查询”或“健康管理”都可以审核材料只需要身份证照片不需要额外资质。审核周期一般 1-7 天实测最快一天过。5.2 真机调试中的典型问题和排查方法我把这套项目实操中最常遇到的五个问题整理成了一张速查表方便你直接对照排查问题现象可能原因解决方案真机预览时白屏基础库版本过低canvas 组件兼容性差详情面板里将调试基础库版本调到 2.14.0 以上打卡后返回首页状态没更新首页数据在 onLoad 里读取没有在 onShow 里同步把读取状态逻辑移到 onShow 生命周期输入框被键盘遮挡设置了 fixed 定位的提交按钮改为普通文档流布局或监听键盘高度动态适配数据存了一周后丢失本地缓存超限被自动清理检查是否实现了旧记录删除逻辑手动设置removeStorageSync工具里可以跑真机加载卡顿首页渲染时遍历了过多记录列表页改用分页加载每次渲染 20 条其中“输入框被键盘遮挡”这个问题在 iOS 上尤其明显。源码的修复方案是提交按钮不用 fixed 定位让它跟随在表单后面自然排列同时给页面设置disableScroll: false键盘弹起时页面可以自动滚动到可见区域。这是小程序开发的老问题——fixed 元素不会跟随键盘调整位置这种细节最好一开始就规避。另一个隐蔽的问题是“模拟器表现和真机不一致”。模拟器里getStorageSync的读写速度和本机几乎没差别但在低端 Android 机上频繁写缓存会有几十毫秒的卡顿。如果打卡后立刻跳转页面偶尔会出现数据还没落盘的情况。解决方法是不要边提交边跳转先setStorageSync完成再wx.redirectTo跳转或者在跳转前加一个wx.showLoading动画给存储函数留足执行时间。5.3 部署文档的版本管理与讲解视频的录制要点再说部署文档这个交付物本身。源码包里附带一份部署文档一般是 md 格式或 pdf里面除了从零开始的安装步骤还有常见错误对照表、版本更新记录等。我的建议是拿到项目后先核对部署文档和源码的版本是否一致——如果文档说 AppID 用测试号代码里却读取了wx.getAccountInfoSync().miniProgram.appId做逻辑分支那就说明文档和代码不同步需要按代码实际情况调整。讲解视频的录制也有方法论。很多人的讲解视频只是敲代码回放这是低效的。好的讲解视频要满足四个层次先讲需求这个系统解决什么问题、再讲流程用户怎么用、然后讲核心机制状态怎么判定、数据怎么存储、最后演示部署。视频时间控制在 15-20 分钟最好太短讲不透超过 30 分钟观众的注意力就散了。6. 项目复盘与经验技巧总结把整套项目从头到尾走了一遍之后说几句掏心窝子的体会。最让我感慨的一点是“本地化”这个选择本身的价值。很多课程设计一上来就想做大而全的后端微服务结果前端小程序只有五个页面后端却不必要的写了一大堆接口。这套本地健康宝反其道行事用最朴素的方式把核心业务跑通了。小程序能不能不用服务器答案是完全能而且对轻量工具型应用来说本地存储的响应速度比走网络请求还快。你要是想明白这个道理再去看市面上的“小区物业报修小程序”“班级课程表小程序”会发现它们都不需要服务器纯本地就能跑得很好。再聊一个埋得比较深的设计——health_status和checkin_records为什么分开存。如果连续十天打卡记录数组有十条当前状态其实只需要看最新一条。如果不存health_status每次进首页都要从记录数组里倒序找第一条数据量大了还得先排序。单独维护一个状态 key每次打卡顺手更新一下首页读取 O(1) 就解决了。这种“读写分离”的思想虽然在这里只是小技巧但放到项目里就是性能设计的雏形。答辩时你说“我把高频读取数据和操作日志拆开存”评审老师通常是认可的。最后给大家一个提高完稿率的小建议这套项目拿到手后不要急着改代码先把源码完整跑通一遍领着部署文档过一遍流程再开着讲解视频对照看。三次下来你基本就对整个系统有了九成把握。再往后不管是改界面还是加功能都只是锦上添花了。
返回列表