简介:美食餐饮外卖点餐微信小程序模板,是为餐饮商户与小程序开发者打造的一站式解决方案,帮助快速搭建外卖点餐与门店展示平台,无需从零编写代码,适合预算有限或希望快速试水的团队。压缩包共68个文件,其中22个png格式UI切图、15个json数据配置、10个js逻辑脚本、9个wxss样式表、8个wxml页面结构,以及少量jpeg示例图和gitignore工程文件,整体仅1.33MB,目录包括页面、工具、配置等模块,便于二次开发与替换素材。目前已有136人学习下载,模板内置用户端点餐、购物车、订单列表、地址管理、优惠券展示、门店介绍等页面,并预留支付与配送相关接口,商家可据品牌需要调整主题颜色、菜单分类和配送规则,快速生成个性化小程序。依托微信扫码即用、分享裂变及模板消息推送能力,可有效缩短点餐路径、刺激复购,是低成本进入微信生态的实用选择。
1. 一个餐饮外卖模板的 zip,不是下载完就结束的
做外卖点餐这类微信小程序,最常用的起步方式不是从零搭框架,而是找一份成品模板下载回来改造。交付形态通常是一个 .zip 压缩包,里面装着菜品列表、分类菜单、购物车和订单确认页的完整页面代码,以及全局配置文件。这类模板的易用性建立在“代码结构和演示数据都完整”的基础上,但脱离原作者账号和服务端之后,直接导入微信开发者工具一定会遇到几个现实问题:AppID 对不上导致编译失败、模板自带菜品写死在 data 文件里、请求接口还指着一个已经失效的域名。接下来的内容把“拿到 zip 之后”这条路走通,从解压前检查到工具导入,从页面结构拆解到菜品展示、分类切换、购物车、提交订单的主链路替换,最后给出上线前真机验证的检查点。适合手里已经有一个模板、想快速改成自己业务的人按顺序操作。
2. zip 解压后先核对结构,再用微信开发者工具把模板跑起来
2.1 解压前看什么:目录层级、文件完整度与隐藏文件
拿到模板压缩包,先不要急着解压。用归档工具打开 zip,看一眼根目录下方是否嵌套了一层同名文件夹。微信开发者工具的导入逻辑是:你选中的那个文件夹里必须直接能看到 app.json、app.js、project.config.json。如果在导入时选错了层级,工具会报“未找到 project.config.json”或直接提示该目录不是一个有效的小程序项目。解决方式不是改代码,而是把导入路径往内层再移一级。这类嵌套在多人分享的模板里很常见,先确认这一步能避免卡在开头。
第二件值得核对的事是 project.config.json 的 appid 字段。模板公开传播时,作者通常会把自己注册的小程序 AppID 清空,写一个 touristappid 占位。导入时用测试号还是自己的 AppID,取决于你接下来的联调计划:测试号适合只在本地看界面效果,因为很多接口拿不到真实用户身份;自己的 AppID 可以从登录、支付到订阅消息完整走一遍,但需要在小程序后台配置服务器域名。这个字段在开发者工具的“详情—基本信息”里也能直接改,不强制手动编辑文件。
第三是压缩包完整性的快速测试。模板链接经过网盘转存或多次下载后,zip 文件偶发损坏。解压工具解到一半提示 CRC 校验失败时,不要选择“全部跳过”,那会让后续导入缺文件。macOS 终端可以直接对压缩包做一次测试:
unzip -t 美食餐饮外卖点餐的微信小程序模板.zip输出里出现 No errors detected 就说明 zip 没坏;Windows 端的压缩软件一般也有“测试压缩文件”入口,跑一遍没有红色报错即可。模板包内如果带了 node_modules 或 .git 这类目录,不影响编译,但体积大、导入慢。准备做二次开发前先删掉它们,再打进自己的版本管理里。
2.2 导入微信开发者工具时,AppID 和编译模式的三个关键选择
解压完毕进入导入流程。开发者工具“导入项目”的面板里,需要填三个东西:目录路径、AppID、后端服务。目录选到包含 app.json 的那一层,这一点上一节已经说过;AppID 按场景选;后端服务如果不需要云开发,选“不使用云服务”就行,避免工具自动安装云开发相关依赖。
结合外卖模板的联调阶段,AppID 的选择可以做这样的对应:
| 阶段 | 选什么 | 要注意的问题 |
|---|---|---|
| 本地看界面 | 测试号 | 测试号无法调用微信登录、支付等涉用户身份接口 |
| 对接自己的后端 | 自己的 AppID | 需要在管理后台配置 request 合法域名 |
| 开通微信支付 | 自己的 AppID + 商户号 | 商户号与 AppID 必须绑定,模板里没有这步配置 |
首次编译时如果页面白屏,先看“调试器—Console”里的报错。高频的有三种:某个图片路径 404,属于 assets 目录没匹配;某个模块 require 失败,属于文件在解压时没完整落地;某个页面 json 文件里的 navigationBarTitleText 出现乱码,属于文件编码不是 UTF-8。前两种回 2.1 检查压缩包完整性,最后一种把 json 文件另存为 UTF-8 编码即可。
编译成功之后,工具顶部有一个“普通编译”下拉框。开发者工具默认的启动页面是上次保存时的页面,而模板的默认启动页可能是登录页或品牌页。想在开发时直接看到菜品列表,就选择“添加编译模式”,启动页面填点餐主页的实际路径,比如 pages/index/index。这个操作只影响开发启动,对线上用户无感。线上的首页路径由 app.json 里 pages 数组的第一项决定,同时微信后台“页面设置”里的“首页路径”也会覆盖它,两处要保持一致。
这里还需要分清 wxml 与 HTML 的差别。wxml 是微信定义的一套模板标记,它把数据绑定、循环(wx:for)、条件渲染(wx:if)和事件绑定(bindtap)都揉进了标签属性里。模板里的“模板语言”指的就是这套标记,它不是 JavaScript,也不直接跑在浏览器里,而是由小程序的视图层解析。改菜品展示时,重点去动 wxml 和对应 js 的 data,而不是去找 HTML 文件——模板包里根本没有 HTML 文件。
2.3 目录结构拆解:pages、components、utils 在点餐模板里的分工
微信小程序项目对内部目录结构没有强制规定,只有根目录的 app.js、app.json、app.wxss 是必须的。餐饮外卖模板常见的额外目录包括 pages、components、utils、data、assets,它们各自承担不同的职责:
| 目录/文件 | 在模板里管什么 | 改造时会碰到的点 |
|---|---|---|
| app.json | 页面注册、tabBar、窗口表现 | 调整首页与 tab 图标 |
| pages/index | 菜品列表、分类菜单 | 替换 mock 菜品,改布局样式 |
| pages/cart | 购物车 | 检查存储键名与 index 页一致 |
| pages/order | 收货信息、备注、提交 | 对齐后端字段,处理金额精度 |
| utils/request.js | wx.request 的封装层 | 改 baseURL、token、超时时间 |
| data/goods.js | 演示用 mock 菜品数据 | 接真实接口后可以删除 |
模板最容易让后期踩坑的位置在购物车页与其数据源之间。有的模板用wx.setStorageSync('cart', …)写入本地缓存,结算页用wx.getStorageSync('cart')读取,键名只要差一个字符就会导致列表页能看到加购数量、结算页却为空。有的模板把购物车放到了app.globalData.cart,这个方案在小程序冷启动之后会自动清空,等于用户每次打开都要重新点菜。拿到模板后,建议先在工具里全局搜索 cart,顺着读取路径把存储介质统一掉。如果原模板没有做持久化购物车,就按下一章的写法自己补上。
3. 从菜品列表到提交订单:外卖模板的 4 段核心改造
3.1 菜品数据替换:模板的 mock 数据结构和 wx:for 渲染
模板作者为了让你打开就能看到效果,会把一批演示菜品写在本地文件里,最常见的位置是data/goods.js。改写前先别急着套用自己的接口结构,而是保持模板字段不变、照原样替换数据。菜品对象的常见结构如下:
// data/goods.js module.exports = [ { id: 1, cateId: 1, name: '招牌黄焖鸡', price: 26, sales: 120, image: '/assets/images/chicken.png' }, { id: 2, cateId: 2, name: '冰镇酸梅汤', price: 6, sales: 80, image: '/assets/images/drink.png' } ]字段说明:id 是菜品的唯一标识,购物车合并时靠它做判断;cateId 指向分类 id,分类切换时靠它做过滤;price 是展示单价,单位是元;image 是本地图片路径。如果后端返回的价格单位是分,前端展示前要除以 100,并保留两位小数,不要直接渲染一个 2600 的大整数。sales 字段决定菜品排序,真实接口里一般由后端计算。
列表页 wxml 的循环渲染是这段:
<view class="goods-list"> <view class="goods-item" wx:for="{{goods}}" wx:key="id" >// pages/index/index.js Page({ data: { categories: [], // [{ id: 1, name: '热销' }] goods: [], // 每个商品带 cateId 字段 activeCatId: 1 }, onCategoryTap(e) { // Number() 防止 dataset 返回字符串与 id 数字类型不匹配 const catId = Number(e.currentTarget.dataset.catid) this.setData({ activeCatId: catId }) } })wxml 里右侧 scroll-view 绑定 scroll-into-view:
<view class="menu-body"> <scroll-view class="left-side" scroll-y> <view class="cat-item {{activeCatId === item.id ? 'on' : ''}}" wx:for="{{categories}}" wx:key="id" >// pages/index/index.js handleAddCart(e) { // dataset 中取出的是当前菜品对象 const goods = e.currentTarget.dataset.item // 读取本地缓存,key 为 cart,首次读取时为空数组 const cart = wx.getStorageSync('cart') || [] // 按 id 判断购物车里是否已有同款 const index = cart.findIndex(item => item.id === goods.id) if (index > -1) { cart[index].count += 1 } else { // 不存在时 push 新记录,只保留后续展示需要的字段 cart.push({ id: goods.id, name: goods.name, price: goods.price, count: 1 }) } wx.setStorageSync('cart', cart) // 刷新购物车角标,避免与当前页面的 data 不同步 this.updateBadge(cart.length) }购物车对象里每个条目的字段构成可以按这个表格来对照:
| 字段 | 类型 | 用途 |
|---|---|---|
| id | Number | 商品唯一标识,决定合并逻辑 |
| name | String | 结算页展示菜名 |
| price | Number | 单价,金额计算基数 |
| count | Number | 购买数量,累加 |
这里的关键是每次更新后都要立刻写回缓存,而不是只在页面 data 里维护一份 cart 数组。结算页读取同一个 key,再根据所选条目重新计算金额。模板如果另写了一个 key 放结算商品,下单前要注意两边一致性。storage 单个 key 的容量上限是 10MB,餐饮点餐的购物车数量级完全够用,不需要引数据库。
3.4 提交订单的 wx.request 封装与联调地址配置
结算按钮点击后,订单数据要发给服务端。模板一般已经把 wx.request 封装成 utils/request.js,但 baseURL 还指向旧演示接口。改模板时把三处对齐:baseURL、请求头、返回码约定。下面是订单提交最直接的写法:
// pages/order/order.js Page({ data: { phone: '', address: '', note: '' }, submitOrder() { // 从购物车缓存中读出本次要结算的商品 const items = wx.getStorageSync('cart') || [] if (items.length === 0) { wx.showToast({ title: '购物车是空的', icon: 'none' }) return } // 组装后端约定的 payload 结构 const payload = { items: items, phone: this.data.phone, address: this.data.address, note: this.data.note } wx.request({ url: 'https://api.example.com/order/create', method: 'POST', data: payload, header: { 'content-type': 'application/json' }, success: (res) => { // 约定的返回码:code === 0 表示成功 if (res.data && res.data.code === 0) { wx.removeStorageSync('cart') wx.redirectTo({ url: '/pages/pay-result/pay-result' }) } else { wx.showToast({ title: res.data.msg || '下单失败', icon: 'none' }) } }, fail: (err) => { wx.showModal({ title: '网络错误', content: err.errMsg || '请稍后重试' }) } }) } })这段代码里有三个在模板改造中容易漏掉的点。第一,method 不显式写 POST 时,wx.request 默认是 GET,payload 会拼到查询字符串上,后端如果按 POST body 接收就会收不到。第二,header 的 content-type 要写成 application/json,与服务端接口的 JSON 解析对应;模板里如果有上传文件的需求把 content-type 改成了 multipart/form-data,提交 JSON 时必须改回来。第三,success 回调里要以业务返回码为准,不能只看 HTTP 状态码。联调阶段想知道请求发没发、返回了什么,打开开发者工具的“调试器—Network”面板,找到对应对应请求,看 Preview 或 Response 标签里的返回体——这比盲目地加 console.log 更直接。
4. 上线前的最后一公里:真机调试、域名白名单与启动页调整
4.1 修改“刚进入小程序的加载页面”的落点
模板自带的“首页”常常是一个品牌欢迎页,要改成菜品列表页。开发环境下,通过“添加编译模式”改启动页面只影响开发调试;线上的首页由两处决定。第一处是 app.json 中 pages 数组的第一项,把点餐主页路径移到第一位;第二处是微信公众平台后台“设置—页面设置”里的首页路径,后台配置的优先级高于前端代码。两处都改了,重新发布之后,用户进入看到的才是菜品列表。
模板如果用了自定义导航栏风格,还需要把顶部导航栏高度算准。获取方式在基础库 2.20.1 以上用wx.getWindowInfo(),状态栏高度是statusBarHeight,导航栏高度可以按胶囊按钮的位置动态算出来:
const winInfo = wx.getWindowInfo() const menuButton = wx.getMenuButtonBoundingClientRect() const navBarHeight = (menuButton.top - winInfo.statusBarHeight) * 2 + menuButton.height模板里硬编码 44 或 64 的写法在部分机型上会顶到状态栏,菜单页这类有滚动区域的页面表现就是首屏内容被遮住一块。算出来的高度用于占位 view 的 style,比写死兼容性好得多。
4.2 真机调试中 wx.request 失败的三个必查项
真机与开发者工具体验差异最大的是网络层,高频故障就三类,照顺序排查最快。小程序后台配置的 request 合法域名必须是已备案的 HTTPS 域名,不能带端口和路径。开发阶段可以勾选“不校验合法域名”在工具里跑通,真机预览不会忽略这条,必须把域名加进后台白名单,配置后等一两分钟再试。
模板里演示接口的 url 可能还停在http://协议,iOS 端明文 HTTP 请求会被直接拦截,必须改成https://,证书要有效期内且链完整,自签名证书不能用。联调时的现象和对应原因可以先做一张表对照:
| 现象 | 原因 | 处理 |
|---|---|---|
| 提示 request:fail | 域名不在白名单或协议非 https | 后台加白名单,url 改 https |
| 返回数据是 HTML 结构 | 接口域名做了 302 跳转或配置错误 | 在 Network 看响应头,确认没有重定向 |
| 请求成功但页面没数据 | 渲染字段名与后端不一致 | 用真机 vConsole 打开请求返回体,逐字段对照 |
第三,payload 字段与后端字段要对齐。协同开发时前端把 items 传成数组,后端可能期望一个 JSON 字符串,模板会依据后端接口文档调整提交前的组装步骤,不要在后端没动的情况下反复排真机。下单成功后记得把购物车缓存清掉,代码里已经写了wx.removeStorageSync('cart')。
4.3 扫掉模板残留的 mock 数据与调试输出
上线前最后做一次全局搜索。在微信开发者工具里全局搜索require('../../data/和console.log,把页面里引用 mock 数据的代码替换为 wx.request 获取远端数据的调用;调试日志可以保留,但打印整份购物车对象这类高负载日志在生产环境最好删掉。mock 文件data/goods.js本身可以留着备份,只要不参与编译路径即可。再搜一遍setStorage,确认购物车键名全项目唯一,没有两套并存的存储逻辑,然后提交发布。这样处理之后,模板的演示参数不会带到用户端,后面接真实后端时也只需要改请求层的 baseURL,不再需要逐页翻代码。
本文还有配套的精品资源,点击获取