做过不少小程序项目老实说最容易被低估的恰恰是“需求边界”而不是代码本身。去年经手了一个基于微信小程序的农村事务管理与交流平台技术栈定了 UniApp要求是以后能低成本复用到 App 和 H5。这个项目做完以后我最大的感受是像“农村事务管理”这种看似垂直的小场景真正要跑顺需要处理的权限、消息、审核、数据展示链路其实一点不比电商项目少。如果你也准备接类似的项目或者正用 UniApp 从零搭一个微信小程序这篇文章应该能帮你省掉不少来回试错的时间。1. 项目背景农村事务数字化难的不是技术而是需求梳理1.1 农村基层事务的几处真实痛点先聊一下项目为什么存在。这个平台面向的对象是乡镇和村两级核心用户包括村委工作人员、网格员、普通村民。实际走访下来痛点集中在三块信息传达效率低。停水停电、补贴申领、村务公示这类消息过去要么贴在村委会门口要么靠微信群转发。年纪大一点的村民不会时刻盯着群聊重要通知经常错过。办事流程不透明。办理宅基地申请、开具证明、申请临时救助村民不清楚“材料交到谁手里”“现在审批到哪一步了”往往来回跑好几趟。村民参与感弱。村务公开的内容村民查不到反馈意见也没有固定渠道很多矛盾其实源于“不知道”和“没处说”。所以这个平台的价值不是做个“村委会网站”而是把“看通知、办事情、提意见”这三件事塞进村民已经天天在用的微信里。1.2 选型UniApp 解决的是“跨端焦虑”选型时甲方只提了两个要求一是必须跑在微信里村民不用额外装 App二是以后可能要出独立 App 和网页版不希望重写。这种情况下 UniApp 是比较合适的选项。它基于 Vue 语法一套代码能编译到微信小程序、H5、App。对这类政务属性项目来说还有一个隐形好处以后如果要做微信以外的渠道例如支付宝小程序或者抖音小程序前端代码能复用大部分。当然 UniApp 也有代价。像地图、定位、蓝牙这类偏原生的能力仍然需要依赖条件编译去区分平台。好在这个项目里用到的高频能力登录、上传、订阅消息、分享都有现成封装踩坑成本不算高。1.3 平台功能全景与角色权限整个平台我拆成了四个角色系统管理员、村委工作人员、网格员、普通村民。不同角色看到的功能入口完全不同这样避免村民被一堆管理菜单干扰。角色核心功能权限边界系统管理员用户管理、角色配置、数据统计全部数据可读可配置权限村委工作人员发布公告、处理办事申请、补贴名单导入本村数据不可跨村操作网格员事件上报、走访记录、通知转达所辖网格范围内的数据普通村民看公告、提交申请、交流发言、查补贴仅本人数据权限这块建议从数据库设计阶段就想清楚我就是前期偷懒后面对接口时改了两次表结构很耽误时间。角色字段不要直接存中文名存 role_code再建一张角色权限表后面扩展会省事很多。2. 搭建核心功能模块公告、办事、交流、统计2.1 村务大厅信息发布与公开“村务大厅”是整个平台的首页承担的不只是信息流展示还要照顾到村民的使用习惯。功能上我做了两个层级置顶重要通知。比如防汛、停电这类紧急信息往顶部固定不随列表滚动消失。分类信息流。分成“村务公开”“补贴公示”“通知公告”“党务公开”四个 Tab每个 Tab 是不同分类的文章列表。后台发布时工作人员只需要选择分类和是否置顶前端按分类拉取接口。这里的一个细节是列表接口一定要支持分页而且要返回总条数。农村用户刷消息的耐心有限小程序端用触底加载比一次性加载完更稳。2.2 办事申请多角色审批流村民提交申请后流程通常是“村民提交 - 网格员初审 - 村委复核 - 公示”。我在设计时没有用太重的工作流引擎而是用一张申请记录表外加一个状态字段0待提交1待审核2待复核3已通过4已驳回每一步操作都记录到一张操作日志表方便追溯。这样做的原因是农村办事场景流程固定不太可能出现复杂分支过度设计反而增加维护成本。表单设计上每种申请事项的字段不同。比如宅基地申请要填地块位置和面积临时救助要填家庭人口和收入情况。我用的是“事项类型 动态表单配置”后台配置 JSON 类型的字段模板前端根据模板动态生成表单页。UniApp 里用 v-for 动态渲染表单控件就能实现比针对每种事项写死页面灵活得多。2.3 村民交流圈子不做过度的内容控制交流圈子是这个项目最有“人情味”的模块。村民可以发动态、评论、点赞。考虑到使用场景我没有做复杂的关注关系只有“村里所有人可见”和“仅本村可见”两个范围限制。发帖时支持上传图片默认最多九张。原图往往很大前端先用uni.compressImage压缩到宽度不超过 1280再走uni.uploadFile上传。这一步非常关键不然小程序包体积和服务器带宽都会被图片拖垮。另外为了避免纠纷交流模块增加了一个“举报”按钮村民可以把不当内容举报给村委后台。这个按钮在 UI 上要低调但是必须有政务类项目合规性还是很重要的。2.4 数据看板补贴公示统计管理端需要一个简单的数据看板用来查看各类公示数据的统计情况。这里我用到了 echarts 配合 canvas 渲染柱状图和饼图。需要注意在小程序里使用 echarts 不要直接引入全量包按需引入图表类型能省下不少主包体积。import * as echarts from echarts/core再只注册 BarChart、PieChart 和需要的组件编译后的体积小很多。3. 微信登录与手机号绑定身份链路的关键设计3.1 登录态设计农村事务平台有一个特殊要求必须实名才能办事。但“实名认证”又不能搞得太复杂否则村民嫌麻烦就直接不用了。我的做法是分两步走微信静默登录。通过uni.login拿到 code传给后端换取 openid建立游客身份。手机号绑定。在用户主动点击“绑定手机号”或首次提交办事申请时触发手机号快速验证组件拿到手机号后完成账号信息补全。登录态用 token 维护后端返回 token 并设置有效期。这里我建议 token 有效期不要设太长我设了 7 天配合 refresh_token 机制刷新既保证安全又避免村民频繁登录。3.2 手机号快速验证组件接入注意事项微信小程序获取手机号官方一直有比较严格的限制。目前比较稳妥的方式是使用button open-typegetPhoneNumber这个原生能力用户点击后微信会弹出一个“授权使用手机号”的确认框。拿到的是加密数据前端不能直接读取手机号要把code传给后端由后端调用微信接口换取手机号。需要注意几点小程序必须完成微信认证个人主体不能使用这个能力。每次点击获取到的 code 有效期很短设计接口时要考虑失败重试。不要在页面加载时就触发授权弹窗必须依靠用户主动行为否则很容易被封禁。如果只是要求“绑定手机号”更简单的做法是直接用“手机号快捷输入”组件但体验不如手机号验证组件流畅我最终选择的是 open-type 方案。3.3 token 过期与多端登录处理实际运行中我发现村民通常会把小程序分享到微信群自己再点开。这时候容易出现一种情况token 过期了或者绑定的手机号和当前微信号不一致。我的处理方式是在请求封装里加拦截uni.request({ url: API_BASE url, data, header: { Authorization: Bearer uni.getStorageSync(token) }, success: (res) { if (res.data.code 401) { // 尝试用 refreshToken 续期 refreshToken().then(() { // 重新请求原接口 }) } } })续期失败就跳转登录页。这里要注意不要在多个并发请求同时触发 refreshToken否则会重复刷新。我用了一个简单的锁变量控制实测下来很稳。4. 消息通知体系订阅消息与自定义分享4.1 为什么订阅消息这么重要村民不可能一直打开小程序所以“审批状态变化”和“新公告发布”这类消息必须主动触达。微信小程序里唯一的官方推送能力就是订阅消息。但订阅消息有一个很让人头疼的限制用户授权一次只能发送一次消息下次要再发必须重新授权。针对这个限制我做了几个设计在用户提交办事申请后立即弹出订阅授权因为这时候用户有明确诉求授权成功率最高。订阅请求合并。一次调用wx.requestSubscribeMessage可同时申请多个模板把“审核结果通知”和“新公告通知”同时提交给用户。发送时机控制。即使在已授权的情况下也要只在关键节点发送避免骚扰。4.2 一次性订阅的授权时机设计这个项目的消息触达链路我踩过一个坑一开始我把订阅授权放在“我的-设置”页面里让用户主动订阅结果授权率非常低。后来改成办事提交成功后立刻弹订阅请求授权率直接翻了将近一倍。uni.requestSubscribeMessage({ tmplIds: [审批结果通知模板ID, 新公告模板ID], success: (res) { // res[审批结果通知模板ID] accept 表示用户同意 } })这里切记订阅按钮的文字写清楚要接收什么消息比如“接收审批进度通知”。用户一看就知道这消息对他有用才更愿意点同意。4.3 分享给好友与群聊的落地细节小程序打开率低分享是另一个重要的用户增长手段。分享入口我做了两处公告详情页右上角胶囊菜单默认分享。独立“分享”按钮调用uni.share的onShareAppMessage钩子。分享卡片里有一条实用技巧卡片图片尺寸按 5:4 设计也就是 500px * 400px 左右显示效果最好。另外分享路径要带上参数比如公告 ID 或者申请单 ID这样用户从分享链接点进来可以直接看到对应内容。onShareAppMessage() { return { title: this.noticeTitle, path: /pages/notice/detail?id this.noticeId, imageUrl: this.shareCover } }5. 小程序端页面适配与组件实践5.1 顶部导航栏高度计算UniApp 项目里如果用自定义导航栏最头疼的就是顶部高度适配。不同手机的刘海屏、胶囊位置都不一样写死高度基本必出问题。正确做法是通过微信提供的 API 动态计算const menuButton uni.getMenuButtonBoundingClientRect() const systemInfo uni.getSystemInfoSync() const statusBarHeight systemInfo.statusBarHeight const navBarHeight (menuButton.top - statusBarHeight) * 2 menuButton.height这段代码的原理是胶囊按钮垂直居中于导航栏用胶囊顶部到状态栏的距离乘以 2加上胶囊高度就是导航栏总高度。拿到这个值后设置给自定义导航栏的 style能保证在不同机型上都不错位。项目中标题栏背景我用的是渐变色注意小程序端 css 渐变要写background: linear-gradient(...)同时兼容-webkit-前缀。5.2 表单组件与上传照片表单是办事流程的核心。UniApp 默认的单选框 radio 样式比较朴素视觉上不够正式我直接用原生 radio 加自定义样式。另外有几个组件搭配比较常用身份证号码输入必须加maxlength18并监听输入做格式校验。日期选择用picker组件设置modedate同时限定可选范围避免村民选到未来日期。上传照片先压缩再上传进度条可以用uni.uploadFile返回的UploadTask.onProgressUpdate实现。有一个细节值得提醒姓名、身份证这类输入框在 form 里一定要加confirm-type移动端点“完成”才会收起键盘。5.3 村民数据图表展示管理端数据看板我选了 echarts但小程序里 canvas 渲染会遇到一个常见问题图表宽度自适应。canvas初始化时如果直接拿uni.createSelectorQuery()获取节点宽度可能获取到 0。稳妥写法是等页面生命周期到onReady之后再查询宽度然后初始化图表。另外小程序端 canvas 的 dpr 也要按设备设置否则图表会发虚。this.$nextTick(() { const query uni.createSelectorQuery().in(this) query.select(#chart).boundingClientRect() query.exec((res) { console.log(res[0]) this.chartWidth res[0].width this.initChart() }) })6. 打包、分包与线上问题排查6.1 manifest 配置与微信打包流程UniApp 项目打包成微信小程序核心是配置manifest.json里的微信小程序节点AppID 必须和微信公众平台上的小程序 AppID 一致不能用测试号发布。设置usingComponents: true如果用到自定义组件的话。开发时开启minified: true和es6: true打包体积会小一部分。本地调试用“开发者工具导入”发布用“上传代码”到微信公众平台提交审核。一个容易忽略的问题uni-app编译后的产物在dist/build/mp-weixin目录用微信开发者工具导入时选这个目录不要选项目根目录。第一次搞容易选错导致半天看不到页面。6.2 超过 2MB 主包限制的优化方案微信小程序主包限制是 2MB超过之后上传代码会直接报错。这个项目第一次打包主包就到了 2.6MB 左右确实被卡了一下。后续优化主要做了四件事图片全部走 CDN代码里不保留任何本地图片资源只保留 tabBar 图标。echarts 按需引入只保留了柱状图和饼图的组件。使用分包将交流圈子、管理端、办事流程独立成 subPackages主包只保留登录、首页、公共组件。压缩 WXSS去掉没用的 class 样式尤其是 ui 库引入时用按需加载而不是全量引。分包配置在pages.json里{ subPackages: [ { root: pagesCircle, pages: [ pages/circle/index, pages/circle/detail ] } ] }分包之后主包从 2.6MB 压到了 1.7MB 左右上传一次通过。6.3 日志不输出与抓包调试开发阶段很容易遇到两个问题一是控制台打印不显示。大概率是 Build 模式跑的是生产环境代码console.log被过滤了。在微信开发者工具右上角点击“普通编译”或切到“开发模式”日志就回来了。二是想查看接口实际返回的数据但真机上又不能开调试。我一般用 Charles 抓包工具做 HTTPS 解密抓包。步骤不复杂电脑和手机连同一个局域网手机 Wi-Fi 设置代理指向电脑再安装 Charles 的根证书。需要说明的是抓包一定要在合规的前提下进行只调试自己开发的小程序接口不要碰其他应用的数据。抓包时最容易漏的一步是小程序发起的请求走的是https不安装证书就只能看到加密后的乱码。证书安装完成后在 Charles 里要勾选 SSL Proxying把域名加进去才能看到明文内容。7. 项目上线后的几点体会这个平台上线的第一个月数据比我预期要好。公告阅读量最高的一条是村里的停电通知阅读量超过了户籍人数的两倍——因为很多家庭是一个人看到后转发给家属。最受欢迎的功能不是办事申请而是补贴名单公示村民非常关心“钱到底给了谁”这一块后续可以单独做一个“可搜索的公示库”。我个人在实际操作中最大的感触是政务类小程序和商业项目套路不一样。商业项目考虑转化率、留存、活跃度政务项目更要考虑的是公平、透明、可追溯。代码层面其实没有黑科技真正花时间的是权限边界、消息触达、表单校验这些“不性感”的地方。另外一个小建议这类项目验收时一定要拿一台低端安卓机测试。同样的代码iPhone 上很顺畅低端安卓上可能掉帧、加载慢。微信开发者工具里的模拟器性能太好不能完全代表真实村民手里的手机。项目再赶真机兼容测试这关也得留够时间。
阅读完成 · 觉得有帮助?