
简介面向uni-app开发者的无人自助洗车小程序项目融合共享洗车与商城模块适合有前端基础、希望快速搭建O2O服务类小程序的开发者参考。压缩包共640个文件约11.8MB以js、vue、json为主辅以ts、scss等涵盖页面逻辑、组件结构、接口数据与项目配置整体目录清晰便于按功能模块检索与二次开发。目前已有60人学习下载适合作为同类业务从零搭建的完整示例。项目内包含洗车流程、订单管理、商城商品展示等典型场景可帮助理解小程序端与数据交互的组织方式同时提供多样化的脚本与配置文件便于对照调试和扩展减少从空白工程起步的重复工作。 各位做小程序开发的朋友们今天想跟大伙儿聊聊我刚完成的一个项目——uniapp24h无人自助洗车小程序_共享洗车小程序带商城.zip。从标题就能看出来这是一个基于uniapp开发的无人值守洗车服务小程序同时集成了商城模块。这类项目在2024年之后需求一直很旺尤其是三四线城市自助洗车网点的铺设速度非常快但很多创业者手里有场地和设备却缺一个能稳定运行的小程序端。这篇文章我就围绕这个项目的完整实现过程讲讲业务逻辑、技术选型、开发踩坑以及上线前后需要考虑的一堆细节。无论你是刚接触uniapp的新手还是已经在做同类型Saas系统开发的同行这篇内容应该都能给你一些参考。先说这个项目能做什么用户扫码打开小程序定位附近洗车点查看空闲状态在线支付后启动洗车设备洗完自动扣款。商城模块则可以卖洗车券、会员卡、汽车用品形成业务闭环。技术上uniapp负责一套代码多端发布微信小程序、支付宝小程序、H5、App后端接口独立部署设备侧通过物联网指令对接。整个项目从前端界面到后台接口从支付对账到设备控制指令全部独立完成工作量比我预想的要大不少但做完之后的复用价值也相当高。1. 项目整体设计与技术选型1.1 需求拆解无人自助洗车的核心场景自助洗车和普通的预约到店洗车有个本质区别洗车过程不需要店员在场。这就意味着整个系统必须完成三个闭环支付闭环、设备控制闭环、异常处理闭环。支付闭环比较好理解用户扫码进入小程序选择洗车套餐微信支付然后设备启动。但真正的难点在后面两个。设备控制闭环指的是小程序端要能实时给洗车机发送启动、停止、暂停的指令并且能收到设备上报的状态比如现在是在清水冲洗、泡沫喷洒还是风干阶段。异常处理闭环更麻烦用户支付了10块钱洗到一半设备故障停住了钱怎么退状态怎么恢复这些都必须有一套完整的订单状态机和退款流程来兜底。所以整个项目在设计初期我并没有急着写页面而是先把业务状态流转图画清楚用户扫码进入小程序 → 选择门店 → 查看设备状态 → 选择套餐 → 支付 → 设备启动 → 洗车进行中设备状态实时上报→ 用户手动结束或套餐金额耗尽 → 订单完成 → 可评价/商城购买附加服务这个状态流转是整个系统的骨架。uniapp在前端层面做的事情是把用户的每一步操作转化为对应的API请求同时通过WebSocket接收设备状态渲染到页面上。1.2 为什么选uniapp而不是原生开发很多朋友在技术选型上纠结到底是纯微信小程序原生开发还是用uniapp。我直接说结论如果你只需要做微信小程序一个端原生是够用的但只要有一点可能要多端发布uniapp能省下至少一倍的工作量。我这个项目在立项时就确定要覆盖微信小程序和支付宝小程序因为自助洗车的用户群体里支付宝用户占比并不低。如果原生开发两套代码重复实现后期维护成本会翻倍。uniapp的编译机制虽然偶尔有些兼容性问题后面我会专门讲但整体收益远大于成本。另外uniapp的Vue语法对于前端开发来说门槛很低组件生态也比较成熟。尤其像uni.getLocation、uni.requestPayment、uni.connectSocket这些API在不同端的适配已经做得比较完善了。我只需要在个别差异化接口上写条件编译#ifdef MP-WEIXIN/#ifdef MP-ALIPAY就可以处理。1.3 项目目录结构与模块划分整个项目的源码目录我是这样组织的├── pages/ # 页面 │ ├── index/ # 首页地图找店 │ ├── wash/ # 洗车流程页 │ ├── order/ # 订单列表/详情 │ ├── mall/ # 商城页面 │ ├── cart/ # 购物车 │ ├── mine/ # 个人中心 │ └── webview/ # 内嵌H5协议、帮助等 ├── components/ # 自定义组件 │ ├── device-card/ # 设备状态卡片 │ ├── pay-modal/ # 支付弹窗 │ └── count-down/ # 倒计时组件 ├── api/ # 接口封装 │ ├── request.js # 请求拦截器 │ ├── auth.js # 登录鉴权 │ ├── device.js # 设备控制 │ ├── order.js # 订单管理 │ ├── pay.js # 支付 │ └── mall.js # 商城 ├── store/ # 状态管理Vuex ├── static/ # 静态资源 └── utils/ # 工具函数这里我要多说一句很多人写uniapp习惯把所有请求都放在页面里一多就混乱。我在这个项目里单独抽了一层api/封装所有后端调用好处是所有接口的入参出参、错误码处理都统一管理后期后端接口调整时前端只改一个文件就行不用每个页面都要翻一遍。2. 核心细节解析与实操要点2.1 地图找店定位与POI展示无人自助洗车小程序一打开用户最关心的就是附近哪里有洗车点多远是否空闲。首页我直接用uniapp的map组件来做。这里有个关键点用户定位拿到的坐标默认是gcj02国测局坐标而如果你用的是腾讯地图或高德地图的SDK坐标系统是一致的直接传给map组件的latitude和longitude就行。但如果你后端返回的门店坐标是wgs84GPS原始坐标就必须做坐标转换不然在地图上会偏移几十米到几百米不等。// 获取当前位置 uni.getLocation({ type: gcj02, isHighAccuracy: true, highAccuracyExpireTime: 3000, success(res) { // res.latitude, res.longitude 就是gcj02坐标 commit(SET_LOCATION, { lat: res.latitude, lng: res.longitude }) }, fail(err) { // 用户拒绝授权时的兜底处理 uni.showModal({ title: 需要定位权限, content: 为了帮您找到最近的洗车点请到设置中开启定位权限, success: () uni.openSetting() }) } })特别注意iOS 14以上的系统getLocation接口需要申请NSLocationWhenInUseUsageDescription权限描述否则会直接失败。这个在manifest.json的App模块配置里要提前写好不然打包出来定位一直是黑的。门店信息展示我用的是marker来实现点击marker弹出设备状态卡片展示空闲设备数、距离、价格区间。2.2 设备通信WebSocket长连接的坑与方案设备控制是这个项目技术含量最高的部分。洗车机不是手机它没法直接访问HTTPS接口所以服务端和设备的通信通常走MQTT或TCP协议。小程序端需要实时感知设备状态有几个选型方案方案A小程序端直接连MQTT over WebSocket。技术上行得通但实际上微信小程序对WebSocket的限制比较多而且设备厂商的MQTT broker通常鉴权逻辑复杂不太建议小程序直接连。方案B后端统一管理设备连接小程序通过后端API获取状态再通过WebSocket与后端通信。这个方案更合理我最终采用的就是这个。小程序端和后端建立WebSocket连接当设备状态变化时后端推送给小程序// 初始化WebSocket连接 connectDeviceSocket() { const token store.state.token this.socketTask uni.connectSocket({ url: wss://api.example.com/ws/device?token${token}, success: () { console.log(WebSocket连接成功) } }) this.socketTask.onMessage((res) { const data JSON.parse(res.data) // 设备状态推送 if (data.type DEVICE_STATUS) { commit(UPDATE_DEVICE_STATUS, data.payload) } // 洗车完成/金额耗尽推送 if (data.type WASH_FINISHED) { this.handleWashFinished(data.payload) } }) }这里最大的坑是微信小程序在切换到后台时WebSocket连接会被系统挂起等用户切回来连接可能已经断了。所以必须在onShow生命周期里检查连接状态断了就重连同时用一个心跳机制定时发送ping包保持连接活跃。否则用户洗完车支付成功到设备启动这一小段时间里如果连接断了用户会看到洗车中的界面但没有实际状态更新体验非常拧巴。2.3 支付流程与订单状态机支付流程看起来简单调用uni.requestPayment然后等回调就行但真正做过的人知道这里有无数细节。我总结几个容易出问题的地方第一支付参数必须是后端生成的。小程序端调用支付前必须先调后端接口创建订单后端拿着订单号去微信支付下单返回timeStamp、nonceStr、package、signType、paySign等参数。前端拿到这些参数后再调uni.requestPayment。绝对不能在前端直接拼接支付签名这是硬性安全红线。第二支付结果回调不能只看success。uni.requestPayment的success回调代表的是用户完成了支付流程并不100%代表钱到账了。一定要以后端收到的微信支付异步回调为准。所以前端在支付成功后应该轮询或通过WebSocket接收后端发送的订单已支付通知然后才跳转到洗车流程。第三订单状态机要能覆盖各种异常情况。我在项目里定义了这些状态待支付、已支付、洗车中、已完成、退款中、已退款、已取消。每个状态之间的流转都要有对应的后端接口校验。比如用户在洗车过程中点了结束洗车后端要校验订单状态必须是洗车中才能执行结束操作否则可能会出现多刷一次接口导致重复扣费的问题。// 创建洗车订单 async function createWashOrder(deviceId, packageId) { const res await api.createOrder({ deviceId, packageId, type: WASH }) const paymentParams res.data.payment uni.requestPayment({ ...paymentParams, success: () { // 这里不能直接进入洗车流程等待后端确认 waitForOrderPaid(res.data.orderId) }, fail: (err) { // 用户取消支付 uni.showToast({ title: 支付已取消, icon: none }) } }) }2.4 商城模块券包与实物商品分开建模项目标题里带了带商城这是很多自助洗车老板的硬需求。洗车属于低频消费单靠洗车很难做高客单价必须通过商城带动二次消费。我在商城模块里把商品分成了两类虚拟商品洗车券、月卡、年卡、套餐包。这类商品不需要物流支付成功后通过接口自动发放到用户账户。实物商品汽车香薰、玻璃水、毛巾等。需要走标准电商流程包括收货地址、物流单号、退货退款。这两类商品在后端的数据模型完全不同所以在uniapp前端也分开处理。列表页统一展示但详情页、订单确认页、购物车页面都要判断商品类型走不同的逻辑分支。需要注意的是微信小程序的虚拟支付有严格限制如果你的类目不具备虚拟支付权限直接卖虚拟商品会有违规风险。这也是为什么很多洗车小程序会把充值、买券这类功能做成线下核销模式而不是直接线上发货。我在做这个项目时跟客户确认过资质之后才上线了虚拟商品的自动发放逻辑。如果你准备复制这个方案请务必先确认自己的小程序类目是否允许虚拟支付。2.5 登录授权微信手机号快速验证自助洗车的用户通常不希望填手机号注册所以登录流程要尽量无感。我的实现方案是用户打开小程序前端调用uni.login拿到code把code传给后端后端用code换openid和session_key同时生成自定义登录态token返回前端如果需要手机号比如购买实物商品时用户点击授权按钮通过uni.getPhoneNumber拿到手机号加密数据传给后端解密这里有个小坑2023年后微信对uni.login换token的机制做了调整新注册的小程序默认使用匿名登录后续通过getUserProfile获取头像昵称。而手机号快速验证组件要求小程序认证且必须选择对应接口权限否则会报手机号快速验证组件不可用。这些在开发初期就要确认好别等到提审时才发现。// 登录流程 async function login() { const { code } await uni.login({ provider: weixin }) const res await api.login({ code }) store.commit(SET_TOKEN, res.data.token) store.commit(SET_USER_INFO, res.data.userInfo) }3. 实操过程与核心环节实现3.1 开发环境搭建与工程配置我用的开发工具是HBuilderX版本选择的是最新稳定版别追求alpha版插件容易出兼容问题。创建uniapp项目时有几个模板选项我直接选了默认模板然后手动引入了Vuex、uni-ui这些基础依赖。manifest.json的配置是很多人忽略但特别关键的地方。我挨个过一遍微信小程序配置填写AppID这是必须的。不填的话小程序跑不起来。Vue版本我选了Vue 3。如果你团队对Vue 3不熟悉可以用Vue 2但Vue 3的性能和语法确实更好尤其是组合式API写复杂交互逻辑时代码组织清晰很多。App模块权限配置虽然当前目标是微信小程序但如果后续要打包App必须在权限配置里勾选定位、摄像头扫码、蓝牙等权限同时填写iOS的NSLocationWhenInUseUsageDescription等描述文案。微信小程序的隐私协议mp-weixin-setting-privacy里需要声明你的小程序收集了哪些用户信息。从2023年9月开始微信强制要求在小程序管理后台配置用户隐私保护指引否则很多接口包括定位、手机号会被拦截。3.2 洗车流程页面的状态驱动洗车流程页是这个项目最核心的交互页面也是状态最复杂的页面。整个页面根据设备状态分成几个阶段设备空闲展示开始洗车按钮设备运行中清水/泡沫/风干展示当前阶段、倒计时、剩余金额暂停中展示继续洗车、结束洗车按钮离线状态置灰所有操作提示用户联系客服我用Vuex管理一个deviceStatus字段页面通过computed计算当前要渲染的UIcomputed: { pageMode() { if (this.deviceStatus IDLE) return READY if (this.deviceStatus RUNNING) return RUNNING if (this.deviceStatus PAUSED) return PAUSED if (this.deviceStatus OFFLINE) return OFFLINE return UNKNOWN } }页面上的大按钮和倒计时数字都是pageMode驱动渲染的。这样做的最大好处是UI逻辑和业务逻辑解耦哪怕后端的设备状态定义变了前端只需要在Vuex的getter里做一层映射就行。3.3 从HBuilderX到真机调试再到云打包开发阶段的调试我强烈建议用真机预览微信开发者工具的模拟器虽然方便但地图插件、蓝牙通信、手机号授权这些能力在模拟器里都有各种限制。HBuilderX里选择运行 - 运行到小程序模拟器 - 微信开发者工具前提是你得在微信开发者工具里开启服务端口设置 - 安全设置 - 服务端口。这一步配置好之后HBuilderX里每次保存代码微信开发者工具会自动刷新调样式和接口的效率非常高。打包发布环节分两种情况云打包HBuilderX菜单栏 -发行-小程序-微信输入小程序AppID之后会调用uni官方云打包服务几秒钟出二维码用微信扫码即可在真机测试。云打包的特点是不需要本地配置微信开发者工具的任何证书对新手极其友好我建议所有真机预览都走这个方式。本地打包离线打包如果你要发布到安卓应用市场比如华为应用市场、小米应用商店云打包是不够的必须要用离线打包。离线打包的核心是先在HBuilderX里发行 - 原生App-云打包生成__UNI__xxxx的appid然后下载对应版本的Android SDK把uniapp的资源文件、JS bundle、图标等放入原生工程中再用Android Studio打包生成apk或aab。这里我踩过一个很深的坑HBuilderX的版本和离线打包SDK版本必须严格对应。我有一阵子用HBuilderX 3.8.7写了代码结果下载的离线SDK是3.6.18的打包出来的App直接白屏控制台报一堆模块找不到的错误。后来换回对应版本才正常。所以如果你计划走离线打包记得在HBuilderX的版本说明里查看对应的SDK下载链接别随便用新版本SDK。3.4 上架安卓应用市场需要的过审经验上架安卓市场是很多人忽略的坑。微信小程序直接扫码就能用但安卓App要过华为、小米、OPPO、vivo这些市场的审核最低要求包括软件著作权证书部分市场要求提供软著没有的话走内测包或企业签名但对于个人开发者来说最靠谱的还是提前申请软著周期1-3个月不等。隐私政策页面App内必须提供隐私政策说明收集了哪些用户信息、如何存储、如何删除。uniapp项目可以在pages/webview里内嵌一个H5的隐私政策页或者用条件编译在App端单独展示。APK签名应用市场要求正式签名打包签名文件.jks要妥善备份丢了就永远无法覆盖更新。我这里专门说一下如果你的洗车小程序只是作为微信小程序运行不需要考虑安卓市场。但如果你想把同一套代码打包成安卓App投放到应用市场上面的三个缺一不可。我在做这个项目时先上的是微信小程序安卓App放在第二期因为软著审核需要时间算好提前量很重要。4. 常见问题与排查技巧实录4.1 真机调试时定位失效这个我遇到过太多次了。小程序模拟器里定位正常但iPhone和安卓真机上sdk取不到定位结果。排查方向manifest.json里有没有勾选定位权限。微信小程序端需要在mp-weixin-permission里配置scope.userLocation的desc描述不配置的话API直接报错。真机定位是否开了。手机系统设置里定位服务必须打开同时微信App本身的定位权限也要允许。坐标格式。前面说过确认后端返回的坐标是gcj02还是wgs84如果错位先检查转换逻辑。4.2 微信支付提示签名错误这个错误90%的情况是后端生成paySign时参数拼接顺序不对。微信支付的签名规则是把所有参与签名的参数按字典序排序用连接拼接上key即商户API密钥然后做MD5或HMAC-SHA256。很多做后端的同事第一次对接时容易漏掉package参数或者大小写没转小写导致签名不一致。这种问题前端排除不了只能让后端同事对着微信支付文档一行行核对。4.3 WebSocket连接频繁断开洗车页面的实时状态如果一直转圈大概率是WebSocket断了。我用的方案是每30秒发送一个心跳包服务端收到心跳后返回pong客户端如果60秒内没收到任何消息主动断开重连小程序从后台切回前台时强制检查一次WebSocket状态// 心跳检测 setInterval(() { if (this.socketTask this.socketTask.readyState 1) { this.socketTask.send({ data: JSON.stringify({ type: PING }) }) } else { this.connectDeviceSocket() // 重连 } }, 30000)这样处理之后设备状态的延迟基本能控制在2秒以内用户体验跟原生App差距不大了。4.4 商城页面商品图加载缓慢商城商品图是小程序的流量大户图片尺寸过大直接导致页面渲染卡顿。我在上传商品图时要求后台上传WebP或压缩后的JPG限制单张不超过200KB同时在uniapp的image组件上设置lazy-load属性用懒加载降低首屏白屏时间。另外一个实用技巧是CDN加图片处理参数比如腾讯云COS支持在图片URL后拼接?imageMogr2/thumbnail/750x来动态裁剪无需单独存多套尺寸的图。4.5 小程序被拒审的常见原因这个项目在上架微信小程序时被拒了一次原因是涉及洗车服务需补充相关资质。自助洗车在微信的类目分类里属于生活服务-洗车服务要求提供营业执照且经营范围必须包含洗车相关服务。后来我在小程序后台补充了资质材料重新提审就通过了。这里提醒一下如果你只是开发外包帮客户上架小程序一定要提前跟客户确认资质是否齐全不然开发完上不了架扯皮的事情特别多。4.6 代码层面的性能优化建议最后分享几个性能优化的实操技巧商城列表页和首页地图页如果数据量大用uni.$emit和uni.$on做组件通信而不是直接用Vuex避免整个页面状态树频繁刷新。对于洗车券这类需要快照展示的数据比如用户之前买过后来券包价格调整了前端最好在订单生成时就把快照数据存到本地存储uni.setStorageSync这样订单详情页在没有网络的情况下也能正常展示。分包加载uniapp官方的subPackages配置可以把商城、订单详情这些不常用的页面拆到子包主包只保留首页、扫码、洗车流程等核心页面能显著降低小程序首屏加载时间。我当时主包压缩到600KB左右子包2MB体验好了很多。写在最后的个人体会这个项目做完之后我最大的感受就是一套代码覆盖微信小程序、支付宝小程序和App端uniapp确实是当前技术栈里性价比很高的选择。尤其是对于无人自助洗车这类强线下业务线上的流量入口和线下设备的联动才是核心技术框架反而是次要的。有个细节可以分享项目验收后客户那边反馈用户最在意的不是商城功能多丰富而是设备状态准不准、启动快不快。后来我专门优化了设备启动的时序——用户支付成功后不等WebSocket确认直接先调后端接口下发启动指令同时前端播放洗车开始的动效把感知等待时间缩短了将近1.5秒。这种体验细节比堆功能更加分。如果你也要做类似的无人自助洗车项目前期的核心调研一定要放在设备厂商对接上——设备控制协议的稳定性、厂商是否提供MQTT/HTTP接口、断网时的设备本地策略这些直接决定了你项目的上限。小程序端只是表现层真正硬核的是设备端和服务端的状态同步一致性。这个项目后续我还在迭代——比如增加洗车记录的视频回放、会员积分体系、营销活动后台配置都是一些能直接提升运营效率的方向。技术方面的路径基本保持uniapp 微信小程序 独立后端 设备IoT云的结构不变整体是稳的。希望这篇文章能帮到正在做或者准备做同类项目的朋友。本文还有配套的精品资源点击获取