资讯动态

基于ThinkPHP8与UniApp的多端点餐系统二次开发实践

发布时间:2026/9/14 14:34:05 来源:尧图企业网站定制
简介基于ThinkPHP 8、Element Plus与UniApp打造的三勾点餐系统是一套面向开发者的多端小程序商城源码适用于餐饮外卖、新零售等场景具备前后端完整结构方便二次开发或直接部署上线。整包共2000个文件压缩后约51.13MB包含1137个JavaScript、93个Vue、144个CSS等前端文件以及135个Java后端文件与SQL数据库脚本同时附带Markdown文档便于梳理业务逻辑与接口设计。当前已有218人学习下载资源目录按后台管理、小程序端、服务端等模块划分清晰易读。开发者可从中获得可运行的多端商城基础工程覆盖微信、支付宝、百度、字节跳动等小程序及App端发布需求管理员端采用Element Plus构建客户端由UniApp统一生成借助ThinkPHP8的后端架构可快速扩展点餐、支付、订单、商品等核心功能有效减少从零搭建成本。1. 三勾点餐系统一套能多端发布的ThinkPHP8小程序商城之前接手一个餐饮外卖项目时最大的痛点不是写业务而是同样一套订单和菜品逻辑要分别在微信小程序、公众号H5、支付宝小程序里各维护一遍。三勾点餐系统用 ThinkPHP8 做后端、Element Plus 做管理后台、UniApp 做客户端让我比较认可的是它把“接口端”和“管理端”从应用目录上就拆开了后续加一个 Android 端或 iOS 端前端代码基本不用重写。它本身是一个“面向开发者”的二开脚手架而不是那种封闭的商城模板开发时可以直接替换逻辑而不被底层框架绑死。如果你团队里已经有熟悉 Vue3 和 PHP 的人这套系统大概一到两周就能上线一个可用的多端点餐应用。2. ThinkPHP8后端从目录结构到点餐接口的落地2.1 多应用模式下如何拆分 admin / api 两个入口ThinkPHP8 沿用了多应用模式这套点餐系统在app目录下按业务拆成了admin、api、common三个应用。admin提供给后台管理端使用走的是管理员鉴权api给小程序和H5端使用对应的是用户登录态。开发时如果新增一个“店长小程序”直接再复制一个api应用做权限控制就好不需要动老接口。多应用通过config/app.php里的auto_multi_app开启访问时 URL 第一段就是应用名。常见配置如下// config/app.php return [ auto_multi_app true, app_map [ admin admin, api api, ], default_app api, ];逻辑说明auto_multi_app设为true后请求/admin/index/index会路由到app/admin/controller/Index控制器的index方法请求/api/order/create则对应app/api/controller/Order。app_map的作用是让 URL 更加简短避免出现app1这类的目录名。我一般还会在default_app里指定默认应用为api这样根域名直接请求时不会抛出 404。2.2 点餐业务的关键数据表结构设计点餐系统的核心表比通用商城要少一些但订单相关的状态设计却要更细。三勾点餐的表结构里比较重要的是菜品分类表、菜品表、订单表和订单明细表。下面这段 SQL 是精简后的建表语句CREATE TABLE sg_dish_category ( id int unsigned NOT NULL AUTO_INCREMENT, name varchar(50) NOT NULL COMMENT 分类名称, sort tinyint NOT NULL DEFAULT 0 COMMENT 排序值越大越靠前, status tinyint NOT NULL DEFAULT 1 COMMENT 1 显示0 隐藏, PRIMARY KEY (id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT菜品分类; CREATE TABLE sg_dish ( id int unsigned NOT NULL AUTO_INCREMENT, category_id int unsigned NOT NULL COMMENT 所属分类, name varchar(100) NOT NULL, price decimal(10,2) NOT NULL COMMENT 建议售价, stock int NOT NULL DEFAULT 0 COMMENT 库存-1 表示不限, image varchar(255) NOT NULL DEFAULT COMMENT 图片地址, spec varchar(255) NOT NULL DEFAULT COMMENT 规格JSON字符串如大小份, status tinyint NOT NULL DEFAULT 1, PRIMARY KEY (id), KEY idx_category (category_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT菜品;参数说明sg_dish中的spec字段存的是 JSON 字符串比如[{name:小份,price:18},{name:大份,price:25}]这样在列表页显示一个菜品时前端可以直接解析规格数组不需要再回查其它表。订单表通常包含order_no、status、pay_time、total_amount等字段订单明细表则冗余一份dish_name和price快照避免菜品修改后历史订单变成一堆孤立的 id。2.3 使用中间件与依赖注入处理管理员鉴权和用户登录态ThinkPHP8 的中间件机制和依赖注入比 ThinkPHP5 要顺手很多。后台管理员的登录校验我一般用一个AdminAuthMiddleware在路由定义时把需要登录的接口都用middleware包起来?php namespace app\admin\middleware; class AdminAuthMiddleware { public function handle(\think\Request $request, \Closure $next) { $token $request-header(X-Token, ); $cacheKey admin_token_ . $token; if (empty($token) || !cache($cacheKey)) { return json([code 401, msg 请先登录], 401); } $request-adminId cache($cacheKey); return $next($request); } }这段代码的逻辑先从 Header 里取出X-Token组装缓存 key 后查 TP8 的缓存查不到说明 token 已过期或不存在直接返回 401 JSON通过后把当前管理员 id 注入到$request对象上后续控制器里用$request-adminId拿到登录者。这样写比在每个控制器里重复写鉴权代码清爽得多。TP8 的依赖注入还体现在控制器构造函数上比如可以注入OrderService而不是在方法里new。2.4 ThinkPHP8与ThinkPHP5/6的差异点很多从 TP5 转过来的开发会不习惯 TP8 的一些变化这里列一个我在二开时踩过的主要对比维度ThinkPHP5ThinkPHP8最低 PHP 版本5.6 左右8.0实际推荐 8.1 以上多应用早期版本需要自己扩展原生支持按目录拆分依赖注入支持但能力一般构造器方法注入更规范路由定义使用Route::rule推荐使用注解或Route::group中间件5.1 后才成熟内置中间件管道执行顺序更清晰错误异常异常类较少通过exceptionResponse统一接管参数说明TP8 中如果还按 TP5 写法在控制器里用input()函数取参数会提示方法不存在。建议统一通过依赖注入\think\Request $request获取。另外 TP8 默认开启强类型模式控制器方法参数上注明类型后参数绑定异常会直接抛 500这一点在二开时要注意捕获。3. Element Plus后台管理端核心页面与权限控制3.1 菜单路由结合Tab标签页的实现后台管理端用的 Element Plus这类系统最容易做乱的是菜单和 Tab 标签页的联动。三勾里比较常规的做法是菜单数据由后端返回前端根据路径动态生成点击左侧菜单时右侧 Tab 同时增加一个可关闭的标签页。// router/index.js const tabList ref([]) router.beforeEach((to, from, next) { if (to.path ! /login) { const exists tabList.value.find(tab tab.path to.path) if (!exists) { tabList.value.push({ title: to.meta.title, path: to.path }) } } next() })逻辑说明每次路由跳转前判断当前路径是否已在 Tab 列表中没有则添加一条。配合el-tabs渲染时关闭逻辑就是tabList.splice(index, 1)如果关闭的是当前 Tab还需要再跳转到最后一个 Tab。菜单结合 Tab 的核心是让用户能快速在多个商品列表、订单列表之间切换而不是每次都要回到菜单重新点。3.2 菜品管理页面的查询、新增、编辑后台至少要有菜品列表的增删改查否则没办法支撑点餐业务的日常运营。Element Plus 的el-table搭配el-dialog是最常见组合。以“新增菜品”为例表单里会绑定DishForm提交时调用 POST 接口template el-dialog v-modeldialogVisible title新增菜品 width600px el-form :modelform label-width100px el-form-item label菜品名称 el-input v-modelform.name / /el-form-item el-form-item label价格 el-input-number v-modelform.price :min0 :precision2 / /el-form-item el-form-item label分类 el-select v-modelform.category_id el-option v-forc in categories :keyc.id :labelc.name :valuec.id / /el-select /el-form-item el-form-item label规格 el-input v-modelform.spec typetextarea placeholderJSON格式 / /el-form-item /el-form /el-dialog /template这段模板里需要注意el-input-number的precision属性它限制价格最多保留两位小数有些考虑不周的后台会允许输入三位小数传给后端后decimal(10,2)会自动截断但用户在前台看到的金额就会出现一分钱偏差。spec字段用 JSON 文本编辑对运营人员不友好二开建议改成表格动态增减这个可以放在后续迭代里做。3.3 订单状态流转与打印小票的对接点餐系统和普通电商最大的不同在于订单状态需要等待商家接单、出餐、配送。三勾后台的订单列表页通常会在行内直接放一个状态切换下拉框方便店员快速操作。以下是一套常见的订单状态状态值状态含义可操作方向10待支付用户取消或超时关闭20已支付待接单商家接单 / 拒单30制作中制作完成40配送中确认送达50已完成用户评价-1已取消无状态机的实现一般放在后端服务层前端只是展示和提交动作。多台打印机同时打印小票时二开通常会在订单表加一个print_count字段每次点击打印时先读取数量再更新避免同一订单被重复打印多次。4. UniApp前端一次开发发布到多端的关键配置4.1 manifest.json 中多端配置与打包前后注意事项UniApp 的manifest.json是多端发布的根每个端都需要在mp-weixin、alipay、h5等节点下配置对应 appid。也就是说同一套代码在微信开发者工具里导入时开发者工具后台必须配置了对应的小程序 appid否则无法调用微信登录接口。以微信小程序为例manifest.json中对应节点是这样的{ mp-weixin: { appid: wx在微信公众平台申请的小程序AppID, setting: { urlCheck: false, es6: true }, usingComponents: true }, h5: { title: 三勾点餐, router: { mode: hash } } }参数说明urlCheck在本地开发调用真实接口时需要改为false否则微信开发者工具会拦截非 HTTPS 请求上线前必须切回 HTTPS 并改为true。H5 端的router.mode决定是 hash 模式还是 history 模式微信公众号内嵌网页推荐用 history但需要后端配置讲请求全部转发到首页。发布 Android 或 iOS 时还要在app-plus节点里配置图标、启动图。4.2 微信小程序登录、支付与公众号H5的差异化处理小程序端登录走的是uni.login获取临时code然后把 code 传给后端换取 openid 和 token。常见写法uni.login({ provider: weixin, success: async (res) { if (res.errMsg login:ok) { const { data } await uni.request({ url: /api/user/login, data: { code: res.code } }) uni.setStorageSync(token, data.token) } } })逻辑说明uni.request实质上是封装了 wx.request但好处是同一份代码在支付宝小程序里也能用。支付时需要再调用uni.requestPayment微信小程序和支付宝小程序的支付参数结构不同所以通常会在前端判断当前端类型分别下发不同的参数格式。微信公众号H5里如果也要下单一般走微信内建的 JSSDK 支付这时需要后端额外返回 signature 参数和微信小程序的流程又不一样。4.3 条件编译处理不同端的API差异每个平台都有一些特有 API比如微信小程序的wx.getPrivacySetting、微信网页里才能打开的 JSSDK 接口UniApp 用条件编译来避免在别的端报错。条件编译不是运行时判断而是在编译阶段就扔掉无关代码// #ifdef H5 const handleH5Pay () { // 调用JSSDK支付 console.log(H5 pay) } // #endif // #ifdef MP-WEIXIN const handleWeixinPay () { // 调用微信小程序支付 uni.requestPayment({ provider: wxpay }) } // #endif这里的MP-WEIXIN对应微信小程序MP-ALIPAY对应支付宝小程序H5对应网页端。开发点餐系统时最典型的就是定位 APIH5 端可以调用uni.getLocation但在浏览器上通常被限制为 HTTPS微信小程序端则必须先声明permission。条件编译能保证代码在一个项目里共存而不会因为某个端不允许某段代码而整包编译失败。4.4 常见多端坑轮播图黑边、地图重置等多端开发经常出现“微信小程序里正常App 里变形”的问题。轮播图黑边主要是因为图片使用模式不对建议使用aspectFill而不是scaleToFill这样图片比例不对时不会拉变形而是裁掉两侧多余部分。如果是在 App 端使用地图二开时通常不会直接使用原生 map 标签而是封装一层地图组件比如用到地图重置时单纯给经纬度赋值有时不生效需要调用地图上下文context.reset()。以下是一个封装地图重置的代码片段export function resetMap(mapId) { // #ifdef APP-PLUS const mapContext uni.createMapContext(mapId) mapContext.reset() // #endif // #ifdef H5 console.log(H5 端需要重新设置 center 和 scale) // #endif }说明条件编译里的APP-PLUS指 Android 和 iOS 原生 App 端。在 Web 端和部分小程序端mapContext.reset()不一定存在所以用了无操作回退。这样写能避免在 H5 端调用不存在的 API 导致空指针错误。5. 二次开发进阶加载页、标题动态设置与嵌入微信H5定位5.1 自定义启动加载页很多开发者第一次拿到 UniApp 项目时会想改掉默认的加载页。微信小程序不支持自定义白屏 loading但可以通过pages.json里每一个页面的navigationBarBackgroundColor和backgroundColor来减少视觉割裂。如果想完全替换成自己设计的加载动画建议在首页加一个全屏遮罩等主要数据请求完成后再移除。三勾点餐里这样的做法比较稳妥在App.vue的onLaunch阶段初始化全局配置然后在首页增加一个v-if控制的加载层。注意不要在多个页面都放加载层否则会出现闪烁。5.2 小程序动态设置标题导航栏点餐系统的分类页通常会根据当前商家名称设置标题不能写死在pages.json。UniApp 里所有小程序端都可以通过uni.setNavigationBarTitle动态设置uni.setNavigationBarTitle({ title: shopInfo.name || 三勾点餐 })这个方法在微信、QQ、支付宝小程序上都是支持的但在 App 端部分 Android 版本上不生效需要配合plus.navigator.setTitle。5.3 H5嵌入微信公众号获取定位H5 端如果需要在微信公众号里获取用户定位不能直接调用uni.getLocation因为微信 JSSDK 需要后端注入权限。常见做法是先加载微信 JSSDK然后根据后端返回的签名配置wx.getLocation。定位只会触发一次用户拒绝后需要引导打开设置。5.4 二开时的推荐迭代顺序如果团队是第一次把这套点餐系统用于真实门店我建议按这个顺序迭代先改菜品规格和库存逻辑再确认订单打印机制最后做配送方式的扩展。规格不灵活会导致运营在后台拼命加虚拟菜品库存不准确会在高峰期超卖。把这些后顾之忧处理完再去深化会员和营销功能整个二开的过程就会顺畅很多。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价