微信外卖小程序源码解析:购物车与订单状态机实现

发布时间:2026/9/12 11:55:33

微信外卖小程序源码解析:购物车与订单状态机实现 简介微信外卖小程序模板是一套可直接运行的完整源码主要面向需要快速搭建外卖业务的小程序开发者覆盖了网上订餐、购物车管理、订单结算、支付确认及商家处理等核心流程。资源包共27个文件大小仅148KB内含json、js、wxss、wxml四种工程文件分别承担配置、逻辑、样式与页面结构另有png图片资源和一份md说明文档整体目录结构清晰方便按模块查阅。已有123人学习/下载适合作为小程序开发入门实战或二次开发基础。源码根据真实外卖场景设计从菜单浏览到购物车金额动态变化再到支付成功反馈均有完整交互处理同时保留了工具函数、全局配置等可复用部分配合README文档中关于各模块功能与使用方法的说明可帮助开发者少走弯路快速产出可演示的外卖小程序demo。1. 微信外卖小程序模板源码的定位与上手路径开发过外卖类小程序的人都有体会真正的成本不在 UI而在订单流转那一整套状态逻辑。商品列表、购物车、下单支付、商家接单每一环都牵涉数据一致性和支付安全性。这个微信外卖小程序模板源码正是把这条业务闭环完整落地的一份工程化示例它不是只给界面的静态模板而是把用户选菜、加购、结算、支付回调、订单状态管理串起来跑的完整方案。这套源码适合两类人一类是刚接触小程序开发、想搞清楚一套真实业务代码如何组织的初级开发者另一类是要做本地生活、校园外卖、餐饮 SaaS 的从业者可以直接在此基础上改品牌、接后端、换支付参数省掉从零搭建的时间。源码包解压后直接用微信开发者工具导入即可编译运行项目配置、页面结构、工具函数都齐全。下面我按代码的实际调用链从入口文件开始拆到支付和订单处理。2. 从目录结构到页面注册小程序入口与数据流分析2.1 解压后先看什么核心文件清单拿到微信外卖小程序模板_完整源码.zip解压后第一件事别急着打开开发者工具先看最外层的README.md、project.config.json和app.json。一个外包项目交付时README.md通常写的是运行环境要求和二次开发说明project.config.json记录的是项目名称、AppID、编译设置如果里面appid字段是测试号你直接导入工具时会弹出游客模式不影响跑通本地页面。app.wxss是全局样式文件常见的做法是把主题色、通用按钮样式、间距变量集中定义在这里子页面的 wxss 只写差异部分。这个模板的app.wxss里一般会预设外卖类的小程序最常用的卡片式布局、价格红色高亮、底部操作栏样式二次开发时改主题色只需动这一个文件。2.2 app.json 的信息配置逻辑app.json是全局配置文件它决定小程序启动时加载哪些页面、 tabBar 如何渲染、窗口用什么样式。它的 pages 数组里第一项就是首页模板里通常注册了pages/index/index、pages/goods/goods、pages/logs/logs三个页面分别对应商品浏览、菜品详情/下单、订单记录。下面是模板中常见的配置结构我基于使用微信开发者工具导入后生成的基础配置做了整理{ pages: [ pages/index/index, pages/goods/goods, pages/logs/logs ], window: { backgroundTextStyle: light, navigationBarBackgroundColor: #ff6633, navigationBarTitleText: 外卖点餐, navigationBarTextStyle: white }, style: v2, sitemapLocation: sitemap.json }这段配置里pages数组控制了页面路由的注册顺序和首屏入口window里的navigationBarBackgroundColor和navigationBarTitleText直接决定顶部导航的品牌感和标题文案。sitemapLocation指向sitemap.json它管理小程序的索引收录规则与页面可见性相关。2.3 utils 层请求封装与公共函数再看utils/util.js。真实项目中这个文件通常承担三类职责日期格式化、金额单位转换分为单位转元、以及wx.request的 Promise 化封装。模板里最常见的封装方式是这样的function formatTime(date) { const year date.getFullYear() const month date.getMonth() 1 const day date.getDate() return [year, month, day].map(formatNumber).join(-) } function formatNumber(n) { return n.toString().padStart(2, 0) } function request(url, method GET, data {}) { return new Promise((resolve, reject) { wx.request({ url: url, method: method, data: data, header: { Content-Type: application/json }, success: res { if (res.statusCode 200 res.statusCode 300) { resolve(res.data) } else { reject(res) } }, fail: err reject(err) }) }) } module.exports { formatTime, request }这里的formatTime用的是padStart补零而不是传统的三元判断代码更紧凑。request方法把wx.request包成 Promise原因是小程序的回调方式在订单提交流程里嵌套层数深代码很容易变成回调地狱。实际开发中header里还会附带Authorization令牌字段模板里通常预留了这个位置方便你接入用户登录态。页面里引入这个模块时用const util require(../../utils/util.js)即可。数据流方向是页面文件调用 util 层方法util 层去请求后端或本地模拟接口再把结果返回给页面渲染。3. 购物车增减与总价计算订单数据模型的设计与实现外卖小程序的业务核心在购物车因为用户所有的操作——选菜、改数量、算优惠、提交订单——最终都会汇总到这组数据上。这个模板的购物车逻辑值得细看它没有把购物车数据散落在各个页面而是由全局数据或 storage 统一维护这种方式避免了页面间通信产生的状态混乱。3.1 菜品数据结构的字段约定在模板的goods.js或模拟数据里菜品对象通常会这样定义字段const product { id: 101, name: 黄焖鸡米饭, price: 22.80, originalPrice: 28.00, sales: 356, categoryId: rice, image: /assets/food/chicken.png }price是实际售卖价originalPrice用于展示划线价sales用来做销量排序。分类字段categoryId的作用是在页面左侧分类栏与右侧菜品列表之间建立映射关系。如果你接手后要接真实后端这些字段名要和数据库表字段对齐建议把模拟数据模块单独拆成一个mock-data.js替换成本地 JSON 文件或云开发数据库时不影响页面代码。3.2 购物车数据状态管理购物车的核心问题有两个一是数据存在哪里二是状态变化时怎么通知视图更新。模板中常见方案是把购物车对象挂到Page实例的 data 上每次加减商品后手动setData刷新。下面是一段在商品列表页处理加购和数量变更的关键代码data: { cart: {}, // { [productId]: quantity } cartCount: 0, cartTotalPrice: 0.00 }, addToCart(e) { const id e.currentTarget.dataset.id const cart { ...this.data.cart } cart[id] (cart[id] || 0) 1 const total this.calcTotal(cart) this.setData({ cart: cart, cartCount: total.count, cartTotalPrice: total.price }) }, updateCart(e) { const id e.currentTarget.dataset.id const delta e.currentTarget.dataset.delta // 1 或 -1 const cart { ...this.data.cart } if (cart[id]) { cart[id] delta if (cart[id] 0) { delete cart[id] } } const total this.calcTotal(cart) this.setData({ cart: cart, cartCount: total.count, cartTotalPrice: total.price }) }, calcTotal(cart) { let count 0 let price 0 Object.keys(cart).forEach(id { const product this.productMap[id] const quantity cart[id] count quantity price product.price * quantity }) return { count: count, price: price.toFixed(2) } }这里的cart用对象结构而非数组好处是通过菜品id直接取数量查询复杂度从 O(n) 降到 O(1)频繁增减商品时性能更好。setData不能只传局部字段要把cartCount和cartTotalPrice一并更新否则底部操作栏的角标和总价不会同步刷新这是模板里最常被忽略的问题。toFixed(2)是前端展示精度的常规处理但真实支付场景中后端必须按数据库中的价格重新核算订单金额前端的cartTotalPrice只负责展示不能作为支付扣款依据。3.3 购物车到订单的转换落库当用户点击结算时购物车数据要被序列化传给后端下单。这一步常见做法是先把购物车cart对象转换成订单明细数组再提交。明细结构一般长这样const orderItems Object.keys(this.data.cart).map(id ({ productId: id, quantity: this.data.cart[id], price: this.productMap[id].price, // 用于后端核价参考 subtotal: (this.productMap[id].price * this.data.cart[id]).toFixed(2) }))后端收到这两条数据后不能直接信任price和subtotal字段而要按productId查询数据库中的实时价格重新计算防止前端篡改价格。下面是一张简化的订单明细表结构源码中如果使用云开发或自建 MySQL字段设计基本会落在这套思路上字段名类型说明order_idvarchar(32)订单号后端生成product_idint菜品 IDproduct_namevarchar(64)下单时菜品名称快照pricedecimal(10,2)下单时单价快照quantityint购买数量subtotaldecimal(10,2)小计金额把product_name、price冗余到订单明细表中而不是联表查询是为了防止商家后来修改菜品名称或价格导致历史订单显示错乱这是外卖类系统一个很容易踩的数据一致性问题。4. 下单支付与商家接单订单状态机的流转与接口预留4.1 支付前的登录态与订单创建外卖小程序的微信支付必须走wx.requestPayment而这一步的前置条件是用户已经登录并且后端生成了有效的预支付订单。模板中常见的下单流程是这样组织的检查wx.getStorageSync(token)是否存在不存在则引导用户调用wx.login换取登录态存在则直接请求后端创建订单接口拿到支付参数后再拉起支付。下面的代码展示了下单方法的骨架重点在参数组织与支付回调的处理submitOrder() { const token wx.getStorageSync(token) if (!token) { wx.navigateTo({ url: /pages/login/login }) return } const orderData { shopId: this.data.shopId, items: this.buildOrderItems(), remark: this.data.remark, addressId: this.data.addressId } util.request(/api/order/create, POST, orderData).then(res { if (res.code ! 0) { wx.showToast({ title: res.msg, icon: none }) return } const payment res.data.payment wx.requestPayment({ timeStamp: payment.timeStamp, nonceStr: payment.nonceStr, package: payment.packageValue, signType: RSA, paySign: payment.paySign, success: () { // 支付成功跳转到订单详情页 wx.redirectTo({ url: /pages/order-detail/order-detail?id res.data.orderId }) }, fail: (err) { // 用户主动取消或支付失败跳回购物车 wx.showToast({ title: 支付未完成, icon: none }) } }) }) }requestPayment里的timeStamp是秒级时间戳字符串nonceStr是随机字符串package是prepay_idxxx格式的字符串这些参数全部由后端调用微信支付接口后返回前端只是透传。特别注意package字段不能擅自改名否则微信支付会直接报错。signType根据商户平台配置选择 MD5、HMAC-SHA256 或 RSA模板里如果写的是 MD5你那边的支付证书配置也需要保持一致。4.2 订单状态机的流转定义创建订单后订单会经历多个状态变更模板在订单列表页logs或商家端页面通常用status整数字段来管理。理解这个字段的取值和流转条件比理解任何一个页面 UI 都重要因为所有列表筛选和按钮显隐都由它驱动。下面是模板中常见的一种状态定义方式状态值状态名触发时机用户端表现0待付款订单创建成功显示“去支付”按钮1待接单支付成功回调显示“等待商家接单”2制作中商家点击开始制作显示“制作中”3配送中骑手点击取餐显示配送进度4已完成用户确认收货可评价店铺5已取消支付前取消或超时未付显示“重新下单”入口6退款中商家发起退款显示退款进度实际项目中status变更不能全放在客户端必须由后端在支付回调、商家操作、配送更新等事件里驱动客户端只负责展示状态对应的 UI。模板中如果后端是模拟的通常会准备一份本地订单数组并提供一个模拟接口演示状态从 0 到 4 的完整跳转。4.3 商家端订单列表的筛选与处理商家端的订单处理界面核心是一个带条件筛选的订单列表。状态筛选的本质是 SQL 的WHERE查询条件电商后台常见的实现方式是顶部 tab 按status值切换加上一个搜索框按订单号或手机号模糊查询。模板中给出的商家端代码结构往往长这样filterOrders(status) { const all this.data.allOrders const filtered status -1 ? all : all.filter(order order.status status) this.setData({ filteredOrders: filtered, currentStatus: status }) }status -1表示全部订单这样做隐藏筛选时可避免额外逻辑判断。搜索框的bindinput事件里用order.orderNo.includes(keyword)做前端过滤等到接入真实后端时再换成请求参数keyword传给服务端用 SQL 的LIKE查询完成前端和接口两层的过滤逻辑可以无缝互换。状态变更按钮在模板里一般会在订单卡片底部通过wx:if按status判断显示“接单”“开始制作”“完成配送”中的哪一个方法内部调接口后把本地order.status置为新值并重新渲染当前列表。4.4 模板里没有但实战必须补的接口如果你把这个模板用于生产后端至少还需要补三个关键接口支付回调接口/api/pay/notify、订单查询接口/api/order/detail、退款接口/api/order/refund。支付回调必须由后端接收微信服务器发来的 POST 请求验签后更新订单状态不能依赖前端跳转来改动状态。退款接口要注意金额不能超过原订单实付金额并且要记录退款操作人和退款流水号方便对账。模板中预留的utils/request.js只需要在后端地址字段填上你的域名其他逻辑基本不用改动。5. 源码改造进阶预加载、防重复提交与权限校验拿到模板源码后最快见效的三处改造是商品页数据的预加载、订单提交的防重复与登录态有效性的校验。先看预加载。很多模板在onLoad里用wx.request拉菜品列表用户从首页点进菜单页时会有明显白屏等待。常见做法是把onLoad里的数据请求挪到onPreload或者在上一个页面跳转前先用wx.setStorageSync缓存上一次的菜品数据页面onLoad时先渲染缓存再在onShow里静默更新。模板里pages/index/index和pages/goods/goods的数据流是单向的你只需要在首页跳转函数里提前请求数据并写入传入参数即可获得到达菜单页时内容已渲染的体验。再处理重复提交。外卖这种高并发场景用户连点两次“提交订单”会生成两笔相同订单。模板里不一定有合法的防重逻辑实战中要在submitOrder入口加一个锁标志isSubmitting: false, submitOrder() { if (this.data.isSubmitting) { return } this.setData({ isSubmitting: true }) util.request(/api/order/create, POST, orderData) .then(res this.handlePayment(res)) .finally(() { setTimeout(() { this.setData({ isSubmitting: false }) }, 1500) }) }这里的setTimeout延时 1500 毫秒是为了避免网络抖动导致的瞬间状态恢复给用户一个明确的“正在提交”反馈。如果后端已生成但支付未完成isSubmitting锁也不会阻止用户从订单列表继续支付因为在handlePayment里已经记录下了orderId。最后是登录态校验。wx.login拿到的code每次都会变化模板里的token过期后接口会返回 401实战中建议在request方法里统一拦截错误码if (res.statusCode 401) { wx.removeStorageSync(token) wx.navigateTo({ url: /pages/login/login }) return Promise.reject(new Error(登录态已失效)) }这条规则放在utils/util.js里可以保证所有页面共用同一套会话失效处理而不是等到哪个页面踩了 401 才想起来处理。顺手把token加进request的header中后续接任何接口都不必每个页面重复添加鉴权头。完成这三处改造后这套源码的骨架已经具备上线级健壮性剩余的工作就是切换到真实后端和支付参数。本文还有配套的精品资源点击获取
延伸阅读

更多相关文章

2026/9/12 11:55:33

STM32智能小车PID闭环速度控制:编码器测速与串口调参实战

简介:STM32F103ZET6智能小车PID闭环速度控制完整工程源码,面向嵌入式初学者、智能车爱好者与课程设计开发者,主要解决小车电机速度闭环控制中测速、PID参数整定及PWM输出配合的问题。工程基于KEIL5开发,适配STM32F103ZET6主控&…

2026/9/12 11:50:33

本地大模型部署实战:Ollama、transformers与llama.cpp协同指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/9/12 11:50:33

Halcon之模板匹配

特别注意事项:常规模板匹配仅支持刚性变换:平移、旋转、均匀缩放。无法处理透视形变若目标存在透视倾斜,必须先通过标定和图像校正消除形变才能匹配。且仅返回目标的位置、旋转角、缩放系数,无法描述透视形变。若是需要匹配发生倾…

2026/9/12 12:45:35

dcode 如何在会话中切换模型并持久化模型配置?

dcode 如何在会话中切换模型并持久化模型配置? 【免费下载链接】deepagents The batteries-included agent harness. 项目地址: https://gitcode.com/GitHub_Trending/de/deepagents 在 deepagents 仓库的终端编码产品 deepagents-code(命令名为 …

2026/9/12 12:45:35

Sunshine 游戏串流主机 30 分钟从安装到串出第一帧画面

Sunshine 游戏串流主机 30 分钟从安装到串出第一帧画面 【免费下载链接】Sunshine Self-hosted game stream host for Moonlight. 项目地址: https://gitcode.com/GitHub_Trending/su/Sunshine 场景切入 一句话定位 客厅电视很大,主机却塞在书房&#xff0…

2026/9/12 12:45:35

Retrospective: [Sprint N / Milestone Name]

Retrospective: [Sprint N / Milestone Name] 【免费下载链接】Claude-Code-Game-Studios Turn Claude Code into a full game dev studio — 49 AI agents, 72 workflow skills, and a complete coordination system mirroring real studio hierarchy. 项目地址: https://gi…

2026/9/12 12:40:34

从一张图片到高清3D模型:Hunyuan3D-2完整使用指南

从一张图片到高清3D模型:Hunyuan3D-2完整使用指南 【免费下载链接】Hunyuan3D-2 High-Resolution 3D Assets Generation with Large Scale Hunyuan3D Diffusion Models. 项目地址: https://gitcode.com/GitHub_Trending/hu/Hunyuan3D-2 做游戏资产、搭VR场景…

2026/9/12 2:05:33

超人会飞不算本事:系统稳定依赖清晰规则与边界设计

开头先不绕弯子。“#斯坦李吐槽dc 所以超人是无缘无故会飞的嘛哈哈哈哈哈哈哈锤哥真是技术人才啊!#雷神 #复联”这类调侃式短标题,第一波冲击力在于它把两个宇宙的角色塞进同一个吐槽箱里,但细想一下就能发现,它真正碰到的根本不是…

2026/9/12 3:55:12

超人VS蜘蛛侠:拆解超级IP的影响力与传播方法论

把“蜘蛛侠 vs 超人”放在 CSDN 上聊,可能很多人第一反应是走错片场了。但如果把这两个角色看成“两个持续运营了 80 多年的文化产品”,你会发现,这场比较本质上是两个不同 IP 策略的长期结果对比:超人赢在定义了整个超级英雄题材…

2026/9/12 10:09:03

基于CNN的调制信号识别:MATLAB实现时频图分类实战

简介:本资源是一套面向通信工程与信号处理方向学习者、研究者的深度学习实践方案,聚焦调制信号自动检测与识别这一典型无线通信任务,解决传统方法依赖人工特征、低信噪比下性能下降等痛点。压缩包共12个文件(10.73MB)&…

2026/9/12 0:04:17

MATLAB仿生优化框架:长鼻浣熊算法多策略融合实现

简介:本资源是一份面向智能优化算法研究者与MATLAB初学者的仿生智能算法实践代码包,聚焦于长鼻浣熊优化算法(COA)的多策略改进与性能验证。针对传统COA易陷局部最优、收敛精度不足等问题,作者融合Circle映射初始化提升…

2026/9/12 0:04:17

【JAVA毕设源码分享】基于 JavaWeb 的校园一卡通管理系统的设计与实现 基于 JavaWeb 的校园卡业务管理系统(程序+文档+代码讲解+一条龙定制)

博主介绍:✌️码农一枚 ,专注于大学生项目实战开发、讲解和毕业🚢文撰写修改等。全栈领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java、小程序技术领域和毕业项目实战 ✌️技术范围:&am…

2026/9/12 0:04:17

【JAVA毕设源码分享】基于 Java 的图书馆借阅管理平台的搭建与实现 基于 Java 的图书馆综合管理系统(程序+文档+代码讲解+一条龙定制)

博主介绍:✌️码农一枚 ,专注于大学生项目实战开发、讲解和毕业🚢文撰写修改等。全栈领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java、小程序技术领域和毕业项目实战 ✌️技术范围:&am…

2026/9/12 6:29:36

USB Type-C PCB布局分区设计:电源、高速信号与PD协议全攻略

做硬件这行,Type-C接口算是典型的“看着简单,做起来全坑”的东西。光引脚就24个,高低速信号、电源、控制线全部塞在一个小小的连接器里,如果PCB布局不做规划,打样回来基本就是“插上没反应”、“高速掉线”、“静电一打…

2026/9/10 15:19:50

系统编程学习原型如何补齐稳定性边界

系统编程学习原型如何补齐稳定性边界预算有限时&#xff0c;我先优化明显多余的复制&#xff0c;而不是猜测性地换容器。用借用传递只读数据通常就能减少分配&#xff1a; fn parse(line: &str) -> Result<Item, Error> { /* ... */ }用基准确认热点确实在分配&am…

2026/9/12 6:37:43

雨花区哪家财务公司代理记账比较好?

在雨花区&#xff0c;企业处理财税事务常常面临诸多挑战&#xff0c;选择一家靠谱的财务公司至关重要。湖南巨勤财务管理咨询有限公司就是本地正规实体财税服务机构&#xff0c;深耕本地工商财税行业多年&#xff0c;熟悉当地工商局、税务局最新政策与申报流程。主营公司注册、…

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
咨询二维码