简介:本资源是一套完整可用的微信小程序期末大作业级项目源码,面向计算机专业本科生、前端初学者及小程序课程设计者,聚焦废品回收场景下的用户端与管理端功能实现。项目采用标准小程序技术栈开发,结构清晰、代码规范,涵盖首页、订单、回收、消息、个人中心等核心页面模块,支持扫码预约、分类查询、在线估价与订单跟踪等实用功能,兼具教学示范性与实际落地潜力。压缩包共83个文件,含44张界面截图(png/jpeg)、14个配置与数据文件(json)、9个逻辑脚本(js)、7个样式文件(wxss)及6个视图模板(wxml),整体体积仅2.36MB,轻量易部署。目前已有645人学习下载,配套资源介绍.txt明确说明项目背景与使用指引,目录组织合理,便于快速理解架构与复现实验效果。
1. 这不是「点外卖式」的小程序——废品回收业务逻辑决定了它必须有状态机、订单生命周期和本地缓存协同
你打开一个「废品回收」小程序,第一反应可能是:不就是拍照上传、填地址、等师傅上门?但实际跑通这个流程,远比点一杯奶茶复杂得多。它要处理用户临时起意的「废纸板堆在阳台三天了」,也要应对回收员在无网络巷子里完成「已称重→已装车→已结算」的离线操作;要让老人能三步完成预约,又要让管理员后台一眼看出「朝阳区铜价波动对黄铜回收单量的影响」。这套源码之所以拿到95分以上,核心不在UI动画多炫,而在于它用小程序原生能力(非uni-app)扎实实现了订单状态机驱动 + 本地持久化兜底 + 云函数轻量聚合三层结构。它适合两类人:一是课程设计/期末作业需要「可演示、可答辩、可解释技术选型」的学生——所有模块都带注释、路径清晰、无黑盒依赖;二是想快速验证回收类MVP逻辑的开发者——它没用任何第三方UI库,所有页面都在pages/下直出,改个颜色、加个字段、接个新API,30分钟内就能跑起来。这不是模板套壳项目,而是把「废品类型分级定价」「预约时段冲突校验」「回收员接单权重调度」这些真实业务约束,全写进了app.js的全局状态管理和cloudfunction/orderData的原子操作里。
2. 微信小程序原生架构解析:从 project.config.json 到 pages 目录的职责切分
2.1 项目配置文件决定开发体验上限
微信小程序的工程配置藏在project.config.json和project.private.config.json中,它们不是摆设。前者定义基础环境(如miniprogramRoot指向./,compileType为miniprogram),后者则控制敏感信息隔离——比如云开发环境ID、测试用的AppID密钥,全部被抽离到 private 文件中。这种分离直接规避了学生交作业时误传密钥的风险。打开project.config.json,你会看到关键字段:
{ "description": "废品回收小程序", "setting": { "urlCheck": false, "es6": true, "postcss": true, "minified": true, "newFeature": true, "enhance": true, "useCompiler": true, "autoAudits": false }, "compileType": "miniprogram", "libVersion": "2.28.2", "appid": "wx1234567890abcdef", "projectname": "recycle-app" }注意:
libVersion设为2.28.2是经过验证的兼容性版本——低于2.25.0会导致wx.getStorageSync在 iOS 15+ 上偶发失效;高于2.30.0则可能触发新版wx.cloud.callFunction的 Promise 包装变更,导致cloudfunction/orderData返回值解析失败。这不是随意写的数字,而是实测后锁定的稳定基线。
sitemap.json同样重要。它声明了哪些页面允许被微信搜索收录。本项目中仅开放pages/index/index(首页)和pages/recycle/recycle(预约页),其余如pages/user/user(个人中心)和pages/order/order(订单页)均设为"priority": 0。这符合业务逻辑:用户搜“废品回收”应直达服务入口,而非跳转到登录后才可见的订单历史。
2.2 pages 目录即业务域映射:每个页面文件夹承载独立闭环
pages/下的目录结构不是随意排列,而是严格按用户旅程建模:
| 目录名 | 核心职责 | 关键文件说明 |
|---|---|---|
index/ | 首页引流与服务入口 | index.wxml用<swiper>轮播回收品类图;index.js中onLoad触发wx.getStorage({key: 'lastLocation'})获取缓存定位,避免每次打开都弹权限 |
recycle/ | 废品预约主流程 | recycle.wxml内嵌picker-view实现「品类→子类→规格」三级联动;recycle.js的submitForm方法先校验address非空、weight> 0.1,再调用wx.cloud.callFunction({name: 'orderData', data: {...}}) |
order/ | 订单状态可视化 | order.js使用setInterval每15秒轮询wx.cloud.database().collection('orders').where({ _id: orderId }).get(),但仅当status为'pending'或'assigned'时才轮询,避免无效请求 |
message/ | 消息通知中枢 | message.js绑定wx.onBackgroundAudioStop监听语音消息播放结束,并更新data.messageList的isPlayed字段 |
特别要注意detail/页面——它不展示单个订单详情,而是「废品知识库」。detail.js中onLoad(options)接收type=cardboard参数后,动态加载static/data/cardboard.json(该文件在static/目录下预置),内容包含「纸板分类标准」「含水率影响折价率」「常见混杂物扣减规则」。这种静态数据本地化策略,让知识查询完全离线可用,且避免了每次打开都请求云数据库。
2.3 app.js 全局状态管理:用 setData 分层而非全局变量
app.js是整个小程序的「心脏起搏器」,但它没用 Redux 或 MobX。其globalData仅存放不可变基础配置:
App({ globalData: { // 仅放常量,不放状态 API_BASE_URL: 'https://api.recycle.example.com', RECYCLE_TYPES: ['纸类', '塑料', '金属', '玻璃', '电子设备'], PRICE_RULES: { '纸类': { base: 0.8, unit: '元/公斤' }, '金属': { base: 4.2, unit: '元/公斤' } } }, onLaunch() { // 启动时初始化可变状态到 pageData,而非 globalData wx.getStorage({ key: 'userInfo', success: (res) => { this.globalData.userInfo = res.data; } }); } });提示:所有用户交互产生的状态(如当前选择的废品类型、输入的重量、定位坐标)都通过
Page.setData()在对应页面实例内维护。app.js中onShow会检查wx.getNetworkType并设置this.globalData.networkStatus,但该字段只用于条件渲染<view wx:if="{{networkStatus !== 'none'}}">,绝不参与业务逻辑计算。这是微信小程序性能优化的铁律:全局变量污染会导致setData触发全量 diff,而页面级状态隔离能让虚拟 DOM 更新精准到组件粒度。
3. 云开发核心实现:orderData 云函数如何保障订单原子性与幂等性
3.1 orderData 云函数代码结构与事务边界
cloudfunction/orderData/index.js是整个系统最密集的业务逻辑单元。它不处理 UI,只做三件事:校验参数合法性 → 查询库存/运力 → 写入订单并触发通知。其入口函数签名强制要求event.action字段,明确区分操作意图:
// cloudfunction/orderData/index.js const cloud = require('wx-server-sdk'); cloud.init({ env: cloud.DYNAMIC_CURRENT_ENV }); exports.main = async (event, context) => { const { action, data } = event; try { switch (action) { case 'createOrder': return await createOrder(data); case 'updateStatus': return await updateStatus(data); case 'getOrderList': return await getOrderList(data); default: throw new Error('Invalid action'); } } catch (err) { console.error('Cloud function error:', err); return { code: -1, msg: err.message || '系统错误' }; } };createOrder函数是重点。它用db.collection('orders').add()插入前,先执行两层校验:
- 前端传参校验:检查
data.address是否含非法字符(正则/[<>'"\\]/)、data.weight是否为数字且 ≥ 0.1; - 业务规则校验:调用
db.collection('recyclers').where({ status: 'online' }).count()确认当前在线回收员 ≥ 1,否则返回{ code: 400, msg: '暂无可用回收员,请稍后再试' }。
3.2 幂等性设计:用 transaction + 自定义 ID 避免重复下单
微信支付回调或用户手抖连点,都可能导致同一笔订单被创建多次。orderData用云数据库事务(transaction)解决此问题:
const createOrder = async (data) => { const db = cloud.database(); const transaction = await db.startTransaction(); try { // 1. 生成唯一订单号:时间戳+随机数+用户ID哈希前6位 const orderId = `ORD${Date.now()}${Math.random().toString(36).substr(2, 4)}${data.userId.substr(0, 6)}`; // 2. 在事务内检查是否已存在相同 orderId const existing = await transaction.collection('orders').where({ _id: orderId }).get(); if (existing.data.length > 0) { await transaction.rollback(); return { code: 0, data: { orderId: existing.data[0]._id } }; // 直接返回已有订单 } // 3. 插入新订单(_id 显式设为 orderId) await transaction.collection('orders').add({ data: { _id: orderId, ...data, status: 'pending', createdAt: db.serverDate(), updatedAt: db.serverDate() } }); await transaction.commit(); return { code: 0, data: { orderId } }; } catch (err) { await transaction.rollback(); throw err; } };逻辑说明:
_id字段被显式赋值为自定义订单号,而非由数据库生成。这样后续所有查询(如db.collection('orders').doc(orderId).get())都能精准命中,且where({ _id: orderId })查询天然具备唯一性。事务回滚确保即使插入失败,也不会残留脏数据。该设计使接口天然支持幂等:前端无论调用几次wx.cloud.callFunction({ name: 'orderData', data: { action: 'createOrder', data: form } }),只要form.userId和时间窗口内参数一致,就只会产生一个订单。
3.3 云函数调用链路与错误降级
前端调用orderData时,必须包裹错误处理:
// pages/recycle/recycle.js submitForm() { const formData = this.data.form; wx.cloud.callFunction({ name: 'orderData', data: { action: 'createOrder', data: formData }, success: (res) => { if (res.result.code === 0) { wx.showToast({ title: '预约成功', icon: 'success' }); // 跳转到订单页,携带 orderId wx.navigateTo({ url: `/pages/order/order?orderId=${res.result.data.orderId}` }); } else { wx.showToast({ title: res.result.msg, icon: 'none' }); } }, fail: (err) => { // 网络失败时,将表单数据存入本地缓存,供重试 wx.setStorage({ key: 'pendingOrder', data: formData, success: () => { wx.showToast({ title: '网络异常,已保存草稿', icon: 'none' }); } }); } }); }参数说明:
fail回调中的wx.setStorage不是简单备份,而是为pages/index/index.js的onShow埋点——当用户再次进入首页,onShow会检查wx.getStorage({ key: 'pendingOrder' }),若存在则弹窗询问「是否继续提交上次未成功的预约?」。这种「本地缓存兜底 + 云端幂等确认」双保险,正是高分项目区别于 Demo 的关键细节。
4. 图片资源与静态文件管理:image 目录结构如何支撑多端适配与审核合规
4.1 image 目录命名规范与尺寸策略
image/目录下共 6 张 PNG 图片(image.png至image6.png),命名看似随意,实则暗含逻辑:
| 文件名 | 用途 | 尺寸(px) | 备注 |
|---|---|---|---|
image.png | 首页顶部 banner | 750×200 | 750px 宽匹配 iPhone 6/7/8 屏宽,200px 高保证文字可读 |
image2.png | 「纸类」分类图标 | 120×120 | 圆角矩形,背景纯白,图标居中,适配wx:for循环渲染 |
image3.png | 「金属」分类图标 | 120×120 | 同上,但主色为 #FF6B35(橙红),与纸类的 #4CAF50(绿色)形成视觉区分 |
image4.png | 订单状态「待接单」icon | 64×64 | 单色线性图标,无渐变,确保在深色模式下仍清晰 |
image5.png | 用户头像默认图 | 120×120 | 圆形裁切,灰底白字「U」,避免使用真人照片引发隐私争议 |
image6.png | 回收员头像默认图 | 120×120 | 圆形裁切,蓝底白字「R」,与用户头像形成角色区分 |
注意:所有图片均未使用 WebP 格式,因微信小程序基础库 2.25.0 以下版本对 WebP 解码支持不稳定,而本项目兼容最低基础库版本为
2.20.0。PNG 虽体积略大,但兼容性零风险,符合课程设计「稳定压倒一切」的原则。
4.2 static 目录:静态数据与离线资源的物理隔离
static/目录与image/并列,存放非图片类静态资源:
static/index/home:首页 HTML 片段(用于web-view组件加载活动页,但本项目未启用,留作扩展位);static/data/:废品知识库 JSON 文件(如cardboard.json,plastic.json),结构统一为:{ "title": "纸板回收指南", "content": [ { "type": "text", "value": "请去除胶带、订书钉等杂质..." }, { "type": "image", "value": "/static/image/cardboard_rule.jpg" } ], "priceRule": "0.8元/公斤(干纸板)" }static/font/:空目录(预留字体文件,当前未使用,避免wx.loadFontFace动态加载引发审核风险)。
这种物理隔离带来两大好处:一是static/下文件可通过wx.downloadFile预加载到wx.env.USER_DATA_PATH,实现离线知识库;二是审核时可明确声明「所有静态内容均不涉及用户生成内容(UGC),无社交传播风险」,降低小程序提审被拒概率。
4.3 .eslintrc.js 配置:为什么禁用 no-unused-vars 但保留 indent
.eslintrc.js的规则设定直指学生作业痛点:
module.exports = { "extends": ["eslint:recommended"], "rules": { "no-unused-vars": "off", // 允许声明未使用的变量,方便调试时保留 console.log "indent": ["error", 2], // 强制 2 空格缩进,避免 tab 与 space 混用导致 git diff 失真 "quotes": ["error", "single"], // 统一单引号,减少字符串拼接时的转义麻烦 "semi": ["error", "always"] // 强制分号,防止 ASI(自动分号插入)引发的隐蔽 bug } };提示:
no-unused-vars: off不是鼓励写垃圾代码,而是理解学生调试场景——常需临时注释某段逻辑,但保留变量声明以便快速恢复。而indent和semi的严格设定,则是为了让多人协作时git blame能精准定位每行修改者,避免因格式差异产生无意义的合并冲突。这恰是高分项目「工程化意识」的体现。
5. 从源码到可运行:三步完成本地调试与真机预览的避坑指南
5.1 开发者工具配置关键项
在微信开发者工具中导入项目后,必须手动修改两项配置才能正常运行:
基础库版本锁定:在「详情」→「本地设置」中,将「基础库版本」设为
2.28.2(与project.config.json一致)。若使用最新版(如2.32.0),wx.cloud.callFunction返回的result对象结构会从{ code: 0, data: {...} }变为{ result: { code: 0, data: {...} } },导致pages/recycle/recycle.js的res.result.code报错Cannot read property 'code' of undefined。云开发环境切换:在「云开发」标签页中,点击「切换环境」,选择你自己的云开发环境(非
release环境)。因为cloudfunction/orderData中cloud.init({ env: cloud.DYNAMIC_CURRENT_ENV })依赖此设置——若未切换,函数将默认连接release环境,而你的测试数据全在test环境中,导致getOrderList返回空数组。
5.2 真机调试必做的四件事
将小程序部署到真机前,务必验证以下环节:
| 步骤 | 操作 | 验证方式 | 常见失败原因 |
|---|---|---|---|
| 1. 定位权限 | 打开pages/index/index.js,确认onLoad中wx.getLocation调用 | 真机启动后首页是否显示「北京市朝阳区」而非「获取位置中...」 | app.json的"permission"字段缺失scope.userLocation |
| 2. 图片加载 | 在pages/recycle/recycle.wxml中找到<image src="/image/image2.png"></image> | 真机上该图标是否清晰显示 | 图片路径写成./image/image2.png(相对路径),应改为/image/image2.png(绝对路径) |
| 3. 表单提交 | 在recycle页面填写地址、选择品类、输入重量,点击「立即预约」 | 控制台是否输出cloud function executed successfully | cloudfunction/orderData未发布,或云函数名称拼写错误(如orderdata少了大写 D) |
| 4. 离线缓存 | 关闭手机 WiFi 与蜂窝数据,进入pages/detail/detail,传入type=plastic | 页面是否正常显示「塑料回收指南」文字内容 | static/data/plastic.json未正确放入static/目录,或detail.js中wx.loadFile路径写错 |
5.3 高分答辩必备的三个技术亮点陈述
当你向老师演示时,不要只说「功能完整」,要聚焦三个可验证的技术点:
订单状态机可视化:打开
pages/order/order.js,指出data.statusMap对象:statusMap: { pending: { text: '待接单', color: '#FF9800' }, assigned: { text: '已派单', color: '#2196F3' }, completed: { text: '已完成', color: '#4CAF50' } }说明:状态文本与颜色全部集中配置,修改一处即可全局生效,体现「配置驱动」思想,而非硬编码。
本地缓存兜底机制:在
pages/recycle/recycle.js中找到fail回调里的wx.setStorage,演示关闭网络后提交表单,再打开首页触发onShow中的草稿恢复逻辑。强调:这不是功能冗余,而是对「最后一公里网络不可靠」的真实妥协。云函数事务安全:在
cloudfunction/orderData/index.js中定位transaction.commit()和transaction.rollback(),解释:即使数据库写入中途崩溃,事务也能保证「要么全成功,要么全回滚」,避免出现「订单创建了但通知没发」的脏状态。
技巧:答辩时打开开发者工具「Network」标签页,现场发起一次
orderData调用,放大展示请求 Payload 中的action: 'createOrder'和响应 Body 中的code: 0。这种「实时抓包佐证」比口头描述有力十倍——它证明你不仅会跑,更懂数据流向。
本文还有配套的精品资源,点击获取