简介微信步数主题的微信小程序页面源码适用于入门小程序开发的初学者或需要快速搭建运动健康类页面的开发者能够直接参考其页面划分、数据绑定与基础交互实现。压缩包共32个文件类型涵盖json配置、js逻辑、wxml页面结构、wxss样式、png和jpg图片素材以及md说明文档整体仅445KB体量小巧且目录层次清楚。目前已有81人学习下载。源码包含pages下多个功能页面、工具函数与全局配置并配套sitemap及项目配置文件可帮助读者理解小程序页面注册、公共样式引用、图片资源调用等环节同时通过日志、个人中心等模块能够掌握常用组件、事件绑定和页面间跳转的组织方式。这份源码还可作为二次开发或课程设计的基础模板适合边读边改快速积累小程序前端实战经验。1. 微信步数小程序页面源码的资源拆解拿到微信步数的微信小程序页面源码.zip别急着点开微信开发者工具导入先把它当成一个页面级工程来拆。它解决的不是“怎么注册小程序”而是给你一套已经排好页面、tabBar、图片资源和工具函数的前端骨架适合做微信小程序项目实例、课程设计或毕业设计底子。这里有个反直觉结论微信步数不是页面里自己累计算出来的。微信小程序中拿到步数只有一条常规路径——调用wx.getWeRunData而且它返回的是加密数据不能在前端直接解出步数。后面所有代码和报错都围绕这句话展开。解压后能看到四页结构pages/index是步数主页pages/orgpk和pages/myorg是业务扩展页pages/logs是日志页。这个包没有附带后端解密服务就算wx.getWeRunData调到成功页面也拿不到明文步数这是整套源码里最需要先认清的边界。2. app.json 页面注册与底盘配置先让小程序骨架跑起来2.1 从文件清单反推页面模块解压后的目录结构大致是这样├── app.js ├── app.json ├── app.wxss ├── project.config.json ├── sitemap.json ├── pages/ │ ├── index/ │ ├── orgpk/ │ ├── logs/ │ └── myorg/ ├── src/ │ ├── avatar_1.png │ ├── liked.png │ ├── cover_pic.jpg │ └── ... └── utils/ └── util.js看目录就能猜出分工pages是四个页面模块src里的图片全部本地化utils放纯函数app.wxss是全局样式。这个结构和常见的 uniapp微信小程序工程不一样uniapp 生成的小程序包会有pages.json和底层运行时文件而这个包里只有原生小程序配置说明它是纯原生写法别用 hbuilderx 那套构建逻辑去套。2.2 页面注册顺序决定启动页和 tabBarapp.json是原生小程序的全局配置步数主页能跑起来先看这段配置{ pages: [ pages/index/index, pages/orgpk/orgpk, pages/logs/logs, pages/myorg/myorg ], window: { navigationBarTitleText: 微信步数, navigationBarBackgroundColor: #2e5e4e, navigationBarTextStyle: white, backgroundColor: #f6f6f6 }, tabBar: { color: #7f8487, selectedColor: #2e5e4e, list: [ { pagePath: pages/index/index, text: 步数 }, { pagePath: pages/myorg/myorg, text: 我的 } ] }, sitemapLocation: sitemap.json }这段配置里有两个容易忽略的点。第一pages数组的第一个元素决定启动页这里把pages/index/index放在最前面进入小程序第一眼看到的就是步数主页orgpk、logs即使页面文件存在只要不出现在pages数组里就不会作为独立页面编译。第二tabBar.list里的pagePath必须同时存在于pages数组否则编译直接报错并且tabBar只能配置 2 到 5 个页面。这个包的orgpk没有进 tabBar说明它只能通过wx.navigateTo跳转进入。下面这张表是调试时最常用的几项配置项示例值作用pages[pages/index/index]声明页面路径第一项为启动页window.navigationBarTitleText微信步数顶部导航栏标题tabBar.selectedColor#2e5e4e底部导航选中态颜色sitemapLocationsitemap.json指定站点地图文件位置2.3 sitemap.json 与 project.config.json 决定编译行为sitemap.json的rules规则控制页面是否被微信搜一搜建立索引。开发期保持放行即可{ desc: 步数页面索引规则, rules: [ { action: allow, page: * } ] }project.config.json则是微信开发者工具读的工程配置核心是 AppID 和编译选项{ compileType: miniprogram, appid: touristappid, projectname: wechat-steps, setting: { es6: true, postcss: true, minified: true, urlCheck: false } }urlCheck设为 false 只能让开发者工具跳过 request 合法域名校验真机预览时仍然会校验后面走云函数就不需要担心这个。touristappid是游客模式不能调用wx.getWeRunData测试步数功能前必须换成真实 AppID这是最多人卡住的第一步。3. 微信步数核心链路wx.getWeRunData 授权、加密数据与云函数解密3.1 调用前先查 scope.werun 授权状态微信小程序的步数接口不是随便调的它属于隐私接口绑定scope.werun授权。用户拒绝一次之后开发者不能再次主动触发授权弹窗只能引导用户去设置页手动打开。这个行为和scope.userInfo类似所以第一步应该先查授权状态而不是直接写一个wx.getWeRunData完事。查授权状态的函数我一般写成 Promise 形式后面配合async/await调用避免回调嵌套function checkWeRunAuth() { return new Promise((resolve) { wx.getSetting({ success(res) { const auth res.authSetting[scope.werun]; if (auth true) { resolve(authorized); } else if (auth false) { resolve(denied); } else { resolve(notDetermined); } }, fail() { resolve(unknown); } }); }); }这段代码的关键点是authSetting对象里scope.werun的三种取值true表示已授权false表示用户明确拒绝过undefined表示还没决定。很多新手把undefined也当成拒绝结果用户第一次还被弹了两次授权框。fail回调也不能简单忽略基础库版本过低时getSetting也可能失败返回unknown状态之后走默认调用链路会更稳妥。3.2 三种授权状态下的调用分支拿到状态后按authorized、notDetermined、denied三个分支处理async function loadSteps() { const status await checkWeRunAuth(); if (status authorized || status notDetermined) { wx.getWeRunData({ success: (res) { handleEncryptedData(res); }, fail: (err) { console.error(getWeRunData fail, err); } }); } else { wx.showModal({ title: 需要微信运动权限, content: 请在设置中打开微信运动授权, confirmText: 去设置, success: (r) { if (r.confirm) { wx.openSetting(); } } }); } }这里最容易踩的坑是denied后直接再调一次wx.getWeRunData实际上微信不会再弹授权框而是直接走fail回调写好的fail处理就会反复执行。所以denied分支要用wx.showModal解释原因再通过wx.openSetting让用户手动打开。wx.openSetting必须在用户点击回调里调用不能在onLoad生命周期直接调否则开发者工具会提示openSetting:fail can only be invoked by user TAP gesture。notDetermined状态则什么都不用做直接调接口就会触发授权弹窗。3.3 加密数据怎么变成步数列表授权通过后wx.getWeRunData的 success 回调拿到的不是 JSON 步数列表而是这样两段字符串{ errMsg: getWeRunData:ok, encryptedData: base64字符串, iv: base64字符串 }encryptedData是 AES-128-CBC 加密后的用户信息和步数列表iv是初始向量。真正解密的session_key只有后端从wx.login的 code 换出来所以页面源码里看不到解密逻辑是正常的。之前有人在前端引入 crypto-js 去解解出来也是乱码因为拿不到正确的session_key。常见做法是把这个解密放到小程序云函数里前端只把encryptedData和iv传过去。下面是一个最常用的云函数解密模板Node.js 环境自带 crypto不需要额外装包// 云函数 getWeRunData const crypto require(crypto); exports.main async (event) { const { encryptedData, iv, sessionKey } event; const decipher crypto.createDecipheriv( aes-128-cbc, Buffer.from(sessionKey, base64), Buffer.from(iv, base64) ); let decoded decipher.update(encryptedData, base64, utf8); decoded decipher.final(utf8); return JSON.parse(decoded); };这段代码里aes-128-cbc对应微信官方加密算法sessionKey理论上不应由前端传生产环境应由云函数通过用户身份去换取或直接存到数据库上面这样写在本地开发联调时比较方便部署前要收紧。解密结果是一个 JSON 字符串JSON.parse后就能拿到stepInfoList。3.4 数据为空和跨天问题的处理解密后的stepInfoList是数组每一项里的timestamp是当天零点的时间戳单位是秒step是该天总步数。注意step为 0 是合法值不能写成if (!step)来过滤。日期换算时给timestamp乘 1000 再传给Date否则会出现 1970。接口一般返回最近 30 天数据但开发者工具里经常是模拟数据真机上如果用户没开微信运动返回的数组可能只有一条空数据。提示不要尝试在前端引入 crypto-js 硬解既拿不到session_key还会把小程序包体积撑大解密这一步放在云函数或自有后端而不是页面源码里。4. 步数页面的数据渲染index 卡片、资源路径与 util.js 封装4.1 本地图片资源为什么不走网络src目录里放着 avatar_1.png、avatar_2.png、liked.png、unlike.png、cover_pic.jpg、hr.jpg 这些资源。页面引用本地图片的好处是列表渲染时不依赖网络也不受 downloadFile 合法域名限制如果包是从线上摘出来的还要先确认这些图片的版权和大小。这个包已经把所有图片放在本地写cover_pic.jpg这种封面图时直接填/src/cover_pic.jpg即可不需要再去找微信小程序图片提取工具从 wxpkg 缓存里翻。本地图片路径一旦写错控制台会直接报fail所以路径里的/src/前缀不要省略。4.2 用 wx:for 渲染步数卡片步数主页的卡片列表一般就是wx:for渲染核心模板如下view classstep-card wx:for{{stepList}} wx:keydate view classstep-date{{item.date}}/view view classstep-number{{item.step}}/view image classstep-like src{{item.liked ? /src/liked.png : /src/unlike.png}} / /view这段模板里wx:for默认把当前项叫作itemwx:keydate告诉框架用date字段做列表唯一标识这里不能写wx:keyitem.date直接写字段名。item.liked是布尔值如果后端返回的是 0/1模板里true条件会不成立所以最好在数据层把 0/1 转成布尔。image的src用/src/liked.png这种绝对路径最稳相对路径在分包场景容易算错尤其是orgpk这类子页面引用根目录资源时。4.3 util.js 里的日期格式化与排序util.js里最常用的是把秒级时间戳变成日期字符串以及给步数排序function formatDate(timestamp) { const d new Date(timestamp * 1000); const month d.getMonth() 1; const day d.getDate(); return ( d.getFullYear() - (month 10 ? 0 month : month) - (day 10 ? 0 day : day) ); } function compareStep(a, b) { return b.step - a.step; } module.exports { formatDate, compareStep };timestamp * 1000是为了把秒转成毫秒getMonth()从 0 开始所以要加 1。compareStep里b.step - a.step返回降序如果orgpk页面要显示团队排行前三名直接对这个排序结果取slice(0, 3)。排序会修改原数组调用前先const sorted list.slice().sort(compareStep)否则页面后面想按时间顺序展示时原数组已经被改掉了。4.4 orgpk 和 myorg 页面在工程里的定位orgpk从命名看更像组织 PK 或团队排行myorg是个人组织信息页这类页面如果要显示当前用户头像需要自己接wx.getUserProfile或云开发用户表源码里没有现成接口时不要硬编码一个固定头像。logs页面放调试日志上线前最好把wx.navigateTo的入口隐藏。如果myorg要自定义顶部导航微信小程序顶部导航栏高度不能写死 64px要用wx.getMenuButtonBoundingClientRect拿到胶囊按钮位置再计算状态栏高度否则不同机型的返回按钮会错位。5. 真机预览前检查清单与步数异常排查技巧5.1 让代码在开发者工具里先跑起来第一步不写新业务代码先跑通编译。把project.config.json里的appid换成自己的 AppID使用测试号也能编译但wx.getWeRunData在测试号上经常拿不到真实数据。导入项目时选择包含app.json的那个目录若选了外层 zip 解压目录开发者工具会报找不到app.json。编译通过后在模拟器里先点开index页看控制台有没有getWeRunData:fail can only be invoked by user TAP gesture这类错误说明页面初始化阶段不该直接调隐私接口。5.2 常见异常和对应处理真机调试时步数相关的报错有规律下面是我遇到最多的几类现象原因处理fail:auth deny用户拒绝过授权用wx.openSetting引导打开encryptedData无法解析前端直接尝试解密把解密放到云函数或后端开发者工具有数据真机没有测试 AppID 或基础库版本不一致换成正式 AppID切换基础库版本日期全是 1970timestamp没有乘 1000检查formatDate入参除了表格里的原因还有一个延迟问题微信运动的数据不是实时写入的用户刚走完步数立刻调接口stepInfoList里可能还是旧值。比较稳妥的做法是在页面onShow时拉一次下拉刷新再拉一次而不是用onLoad只拉一次。5.3 用 useMock 开关让页面脱离授权调试对页面渲染来说反复授权很浪费时间我习惯在app.globalData里放一个useMock开关const useMock true; if (useMock) { const mockList [ { date: 2024-01-01, step: 10234, liked: false }, { date: 2024-01-02, step: 8721, liked: true } ]; this.setData({ stepList: mockList }); } else { loadSteps(); }useMock为 true 时直接喂假数据页面所有wx:for、图片切换和排序逻辑都能正常走一遍联调后端时改成 false 即可。setData传整个数组在几十条数据时没问题如果步数列表规划超过 20 条前端可以做分页或改用recycle-view避免一次setData传递大数组导致滚动掉帧。把useMock的切换和真实请求放到同一个入口函数里后面接云函数时只需要替换else分支页面模板保持不动。本文还有配套的精品资源点击获取