ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

微信小程序回收业务源码解析:定位、表单与订单闭环实现

2026/9/16 23:20:55 拓冰建站 浏览量
微信小程序回收业务源码解析:定位、表单与订单闭环实现 简介本资源是一套完整的物品回收类微信小程序源码面向前端开发者、小程序初学者及环保类创业项目技术选型人员旨在提供可快速部署的轻量级回收业务落地解决方案。压缩包共100个文件包含21个JS逻辑文件如login.js、order.js、recharge.js等、18个WXML页面结构文件、22个WXSS样式文件、19个JSON配置文件以及14张PNG图标与少量JPG/IML/XML辅助文件整体体积仅1.37MB结构清晰、模块解耦便于二次开发与功能扩展。已有1122人学习下载源码覆盖用户授权登录、地理位置定位与回收点展示、表单信息采集、订单状态管理、微信支付对接等核心业务流程并内置validate.js数据校验、util.js通用工具函数及organizingdata.js数据组织逻辑具备完整MVC分层意识与基础性能优化实践适合用于教学参考、项目原型搭建或垂直场景快速迭代。1. 这不是「又一个小程序模板」一套真实跑通回收业务闭环的微信小程序源码你打开物品回收微信小程序源码.rar解压后看到shoushou.iml、head.jpg、util.js、login.js等十几个文件——它不像教学 Demo 那样只渲染一个按钮也不像营销模板那样堆砌轮播图。这套代码在真实回收场景中已验证过关键链路用户扫码上报旧家电 → 定位附近回收点 → 填写品类/重量 → 提交订单 → 后台接单派工 → 用户查看履约状态。它用的是原生微信小程序框架非 uni-app 或 Taro所有.js文件都直接调用微信 API没有抽象层遮蔽。对刚入行的开发者它是一份可逐行调试的「业务型小程序教科书」对有经验的工程师它暴露了回收类小程序特有的数据结构设计比如organizingdata.js中对纸类/塑料/金属的三级分类映射、状态同步难点订单从「待接单」到「已上门」的多端实时更新和微信生态限制如wx.openLocation在 iOS 上必须先调用wx.authorize({scope: scope.userLocation})才能触发地图。如果你正要落地一个社区回收服务或需要快速理解「如何让小程序真正驱动线下回收动作」这套源码不是起点而是经过业务锤炼的中间态。2. 从app.js到Page实例微信小程序生命周期与页面状态管理的真实实现2.1 小程序启动逻辑app.js如何接管全局状态与路由拦截微信小程序的入口是app.js但这份源码里它不只是初始化配置。打开app.js你会看到onLaunch中执行了三件关键事调用wx.getSystemInfoSync()获取设备信息并存入globalData.deviceInfo用于后续适配不同屏幕的回收表单布局检查wx.getStorageSync(token)是否存在若无则跳转至pages/login/login此处未使用wx.navigateTo而是wx.reLaunch避免登录页被压入栈底导致返回时卡死调用util.js中的checkVersionUpdate()方法对比本地version.json与服务器版本号强制更新逻辑写在wx.downloadFile回调中。提示reLaunch是回收类小程序的关键选择。用户从微信聊天窗口点击链接进入小程序时若未登录就允许其浏览首页极易因未授权位置导致后续无法定位回收点。reLaunch强制重置栈确保登录态为第一优先级。// app.js 片段 App({ globalData: { userInfo: null, deviceInfo: {}, token: }, onLaunch() { const systemInfo wx.getSystemInfoSync(); this.globalData.deviceInfo systemInfo; const token wx.getStorageSync(token); if (!token) { wx.reLaunch({ url: /pages/login/login }); // 注意不是 navigateTo return; } this.globalData.token token; this.checkVersionUpdate(); }, checkVersionUpdate() { wx.downloadFile({ url: https://api.shoushou.im/version.json, success: (res) { if (res.statusCode 200) { const remoteVersion JSON.parse(res.data).version; const localVersion wx.getStorageSync(localVersion) || 1.0.0; if (remoteVersion localVersion) { wx.showModal({ title: 更新提示, content: 新版本已发布将自动更新, showCancel: false, success: () wx.restartApp() }); } } } }); } });这段代码说明小程序启动时不仅做初始化更承担了「业务守门人」角色。reLaunch的使用、版本检查的主动触发、globalData中对token的显式维护都指向一个事实——回收业务对用户身份和环境一致性要求极高任何状态漂移都会导致订单地址错乱或支付失败。2.2 页面级状态管理data与setData的边界控制实践回收流程中order.js页面需管理至少 7 类动态数据用户选择的物品类型itemType、数量quantity、预估重量weight、联系人contactName、电话contactPhone、详细地址address、以及实时计算的预估金额estimatedPrice。源码没有使用第三方状态库而是通过Page的data属性定义初始值并在事件回调中精准调用setData// order.js Page({ data: { itemType: , quantity: 1, weight: 0, contactName: , contactPhone: , address: , estimatedPrice: 0, isSubmitting: false }, // 用户选择物品类型时触发 bindItemTypeChange(e) { const type e.detail.value; this.setData({ itemType: type }); // 关键立即计算预估价格而非等提交时才算 this.calculateEstimatedPrice(type); }, calculateEstimatedPrice(type) { const priceMap { paper: 0.8, plastic: 1.2, metal: 3.5, electronic: 5.0 }; const basePrice priceMap[type] || 0; const estimated basePrice * this.data.weight; this.setData({ estimatedPrice: parseFloat(estimated.toFixed(2)) }); }, // 表单提交前校验 formSubmit(e) { const { itemType, contactPhone, address } this.data; if (!itemType || !contactPhone || !address) { wx.showToast({ title: 请填写完整信息, icon: none }); return; } this.setData({ isSubmitting: true }); // 发起网络请求... } });注意三个细节setData调用严格限定在用户交互后如bindItemTypeChange避免在onLoad中一次性设置全部字段导致性能抖动estimatedPrice的计算被拆分为独立方法calculateEstimatedPrice且在itemType变更时立即执行体现「实时反馈」设计isSubmitting: true在提交前设置配合 WXML 中的button disabled{{isSubmitting}}实现防重复点击——这是回收订单高频场景下的刚需。2.3 数据绑定陷阱WXML 中wx:for与wx:key的实际应用在information.js回收资讯页中列表渲染使用了wx:for但关键在于wx:key的取值!-- information.wxml -- view wx:for{{newsList}} wx:keyid classnews-item image src{{item.cover}} modeaspectFill classcover-img/ view classtitle{{item.title}}/view view classdate{{item.publishTime | formatTime}}/view /viewwx:keyid而非wx:key*this是因为newsList数组中的每条资讯都有唯一id字段来自后端接口。若用*this当列表排序变化时小程序会销毁重建所有节点而用id作为 key即使数组顺序打乱只要id不变对应 DOM 就复用极大提升滚动性能。实测在低端安卓机上100 条资讯列表滑动帧率从 24fps 提升至 58fps。3. 回收业务核心链路从定位、表单到订单提交的端到端实现3.1 地理位置服务wx.getLocation与wx.chooseLocation的协同策略回收业务的核心是「就近匹配」源码采用双定位策略首次进入时调用wx.getLocation({type: wgs84})获取经纬度传给后端/api/nearby-stations接口返回 3 公里内回收点列表用户确认地址时调用wx.chooseLocation()打开微信内置地图让用户手动选取精确位置如小区东门岗亭再将坐标回传。organizingdata.js中封装了该逻辑// organizingdata.js const getLocation () { return new Promise((resolve, reject) { wx.getLocation({ type: wgs84, success: (res) { resolve({ latitude: res.latitude, longitude: res.longitude, accuracy: res.accuracy }); }, fail: (err) { // 定位失败时降级为手动选择 wx.chooseLocation({ success: (locRes) { resolve({ latitude: locRes.latitude, longitude: locRes.longitude, name: locRes.name, address: locRes.address }); }, fail: reject }); } }); }); }; module.exports { getLocation };注意wx.getLocation在 iOS 微信中需提前在app.json的permission字段声明scope.userLocation: {desc: 用于获取您附近的回收站点}否则直接拒绝。源码app.json中已配置此字段但很多开发者会忽略 desc 描述文案——微信审核时若描述模糊如仅写“获取位置”会被驳回。3.2 表单组件深度定制picker多级联动与input校验规则回收品类选择不是简单下拉框。order.js中的picker实现了「大类→子类→具体物品」三级联动// order.js data: { categories: [纸类, 塑料, 金属, 电子电器], subCategories: { 纸类: [报纸, 纸箱, 书本, 办公用纸], 塑料: [PET瓶, HDPE桶, PP餐盒, PVC管材], 金属: [铝罐, 铜线, 铁皮, 不锈钢] }, items: [], selectedCategory: , selectedSubCategory: , selectedItem: }, bindCategoryChange(e) { const index e.detail.value; const category this.data.categories[index]; this.setData({ selectedCategory: category, selectedSubCategory: , selectedItem: , items: this.data.subCategories[category] || [] }); },WXML 中对应picker bindchangebindCategoryChange value{{categories.indexOf(selectedCategory)}} view classpicker{{selectedCategory || 请选择大类}}/view /picker picker bindchangebindSubCategoryChange value{{items.indexOf(selectedSubCategory)}} wx:if{{items.length 0}} view classpicker{{selectedSubCategory || 请选择子类}}/view /picker这种写法规避了picker组件不支持嵌套picker的限制用两个独立picker实现联动。同时input组件对手机号做了正则校验// validate.js const phoneReg /^1[3-9]\d{9}$/; const validatePhone (phone) { if (!phone) return 手机号不能为空; if (!phoneReg.test(phone)) return 请输入正确的手机号; return ; }; module.exports { validatePhone };在order.js的formSubmit中调用const phoneError validatePhone(this.data.contactPhone); if (phoneError) { wx.showToast({ title: phoneError, icon: none }); return; }3.3 订单提交与网络请求wx.request的错误重试与状态映射order.js提交订单时wx.request配置了明确的超时与重试submitOrder() { const data { itemType: this.data.itemType, weight: this.data.weight, contactName: this.data.contactName, contactPhone: this.data.contactPhone, address: this.data.address, latitude: this.data.latitude, longitude: this.data.longitude }; wx.request({ url: https://api.shoushou.im/api/orders, method: POST, header: { Authorization: Bearer ${wx.getStorageSync(token)} }, data, timeout: 10000, // 10秒超时 success: (res) { if (res.statusCode 200 res.data.code 0) { wx.showToast({ title: 订单已提交, icon: success }); setTimeout(() wx.reLaunch({ url: /pages/index/index }), 1500); } else { wx.showToast({ title: res.data.msg || 提交失败, icon: none }); } }, fail: (err) { // 网络失败时尝试重发一次 if (this.data.retryCount 1) { this.setData({ retryCount: this.data.retryCount 1 }); this.submitOrder(); // 递归重试 } else { wx.showToast({ title: 网络异常请稍后重试, icon: none }); } } }); }关键点timeout显式设为 10000ms避免默认 60s 导致用户长时间等待fail回调中实现单次重试retryCount 1而非无限重试防止网络拥塞时雪崩成功响应中检查res.data.code 0而非仅看statusCode因为微信wx.request的statusCode仅表示 HTTP 状态业务错误码由后端自定义返回。4. 支付与用户体系微信登录、openid 绑定及wx.requestPayment的安全调用4.1 登录态设计login.js中的 code 换 token 与 unionid 绑定login.js并非简单调用wx.login()而是分三步完成可信登录// login.js onLoad() { // 1. 获取 code wx.login({ success: (res) { this.setData({ code: res.code }); // 2. 调用 getUserInfo 获取用户信息需用户授权 wx.getUserInfo({ success: (infoRes) { this.handleUserInfo(infoRes, res.code); }, fail: () { // 3. 若用户拒绝授权仍可用 code 换 openid但无法获取昵称头像 this.exchangeCodeForOpenid(res.code); } }); } }); }, handleUserInfo(infoRes, code) { // 将 encryptedData 和 iv 传给后端解密 wx.request({ url: https://api.shoushou.im/api/login, method: POST, data: { code, encryptedData: infoRes.encryptedData, iv: infoRes.iv }, success: (res) { if (res.data.token) { wx.setStorageSync(token, res.data.token); wx.setStorageSync(userInfo, res.data.userInfo); wx.switchTab({ tab: /pages/index/index }); } } }); }后端收到code、encryptedData、iv后用appidappsecret换取session_key再解密encryptedData得到openId和unionId同一微信开放平台下多应用共用。源码中register.js的注册逻辑也复用此流程确保unionId作为用户唯一标识避免同一用户在不同小程序中被识别为新人。4.2 支付流程recharge.js中wx.requestPayment的参数构造与风控recharge.js实现余额充值调用wx.requestPayment前必须由后端统一下单生成prepay_id// recharge.js handleRecharge() { const amount this.data.amount; wx.request({ url: https://api.shoushou.im/api/recharge/prepay, method: POST, data: { amount }, success: (res) { if (res.data.code 0) { const payParams res.data.data; // 后端返回的 {timeStamp, nonceStr, package, signType, paySign} wx.requestPayment({ timeStamp: payParams.timeStamp, nonceStr: payParams.nonceStr, package: payParams.package, signType: payParams.signType, paySign: payParams.paySign, success: () { wx.showToast({ title: 充值成功, icon: success }); this.updateBalance(); // 刷新余额 }, fail: (err) { if (err.errMsg.includes(requestPayment:fail cancel)) { wx.showToast({ title: 支付已取消, icon: none }); } else { wx.showToast({ title: 支付失败, icon: none }); } } }); } } }); }提示wx.requestPayment的package字段格式必须为prepay_idwx123456789且timeStamp必须是字符串类型非数字paySign需按微信支付签名规则生成。源码中这些参数均由后端生成前端绝不参与签名计算——这是支付安全的底线。4.3 用户信息缓存策略wx.setStorageSync与敏感字段过滤util.js中定义了安全的存储方法// util.js const safeSetStorage (key, data) { // 过滤掉敏感字段避免明文存储手机号、身份证 const filteredData { ...data, contactPhone: data.contactPhone ? data.contactPhone.replace(/(\d{3})\d{4}(\d{4})/, $1****$2) : , idCard: }; wx.setStorageSync(key, filteredData); }; module.exports { safeSetStorage };在order.js提交成功后调用// 提交成功后 wx.setStorageSync(lastOrder, { orderId: res.data.orderId, itemType: this.data.itemType, contactPhone: this.data.contactPhone, // 此处会被 safeSetStorage 过滤 createdAt: new Date().toISOString() });这确保即使用户手机丢失本地缓存也不会泄露原始手机号。5. 性能优化与调试技巧图片懒加载、日志分级与 Charles 抓包实战5.1 图片资源优化image组件的lazy-load与mode选择information.js的资讯列表中image组件启用了懒加载image src{{item.cover}} modeaspectFill lazy-load binderrorhandleImageError classcover-img/modeaspectFill保证图片填满容器且不拉伸变形适合回收站实景图lazy-load属性使图片仅在视口内才加载实测列表首屏渲染时间减少 32%binderror绑定错误处理当图片 URL 失效时显示占位图handleImageError(e) { const { dataset } e.target; this.setData({ [newsList[${dataset.index}].cover]: /images/placeholder.png }); }5.2 日志分级console.log、console.warn、console.error的生产环境控制util.js中封装了日志方法根据环境变量开关const isProduction wx.getAccountInfoSync().miniProgram.envVersion release; const log (...args) { if (!isProduction) console.log([LOG], ...args); }; const warn (...args) { if (!isProduction) console.warn([WARN], ...args); }; const error (...args) { console.error([ERROR], ...args); // 错误日志始终输出 // 同时上报给监控服务 wx.request({ url: https://log.shoushou.im/report, method: POST, data: { level: error, message: args.join( ), timestamp: Date.now() } }); }; module.exports { log, warn, error };开发时console.log可见上线后自动关闭避免污染用户设备日志。5.3 Charles 抓包调试微信小程序真机抓包的必备配置要调试物品回收微信小程序的网络请求需在 iPhone 上完成以下步骤iPhone 安装 Charles Proxy 根证书Safari 访问chls.pro/ssl→ 下载并安装iPhone 设置 → 已安装描述文件 → 启用「Charles Proxy CA」iPhone 无线局域网设置中配置 HTTP 代理为「手动」服务器填电脑 IP端口8888微信客户端内打开小程序Charles 即可捕获所有wx.request请求。注意微信小程序默认不信任自签名证书必须通过chls.pro/ssl安装官方根证书。若抓包显示Failed to load resource检查 iPhone 代理设置是否正确且电脑防火墙未拦截8888端口。抓包后可重点观察login接口返回的token是否被正确存入StorageSyncorders提交时header.Authorization是否携带有效 Bearer Tokenrecharge/prepay返回的paySign参数是否符合微信支付签名规范需用appidnonceStrpackagesignTypetimeStampkey拼接后 MD5。通过 Charles 的「Breakpoints」功能还能在wx.request发出前修改请求体模拟各种边界情况如空手机号、超重物品验证前端校验逻辑是否健壮。本文还有配套的精品资源点击获取