
简介这是一份面向微信小程序开发者与家政服务行业技术实施者的开源模板源码专为快速搭建线上家政预约平台提供完整技术支撑。资源包含525个文件涵盖99个JS逻辑文件处理预约、订单、用户交互等核心业务、68个WXML模板与68个WXSS样式文件构建响应式页面结构与视觉呈现、67个JSON配置文件定义页面路由与窗口属性以及42个PHP后端脚本支持基础服务端功能整体压缩包仅1.2MB轻量易部署。已有1436人学习下载适合中初级开发者通过阅读带注释的源码理解小程序标准开发范式并基于现有模块如首页轮播、服务分类展示、在线预约表单、服务人员详情页、订单状态跟踪及用户评价系统开展定制化二次开发。源码结构规范目录层级清晰配套CSS样式库如photoswipe.css、common.css与静态资源PNG图标、GIF加载动画齐全可直接运行调试并快速对接真实业务场景。1. 家政服务小程序开源版 v2.8.58不是拿来即用的“成品”而是可深度定制的业务骨架很多开发者下载“家政服务小程序开源版 v2.8.58.zip”后第一反应是解压、npm install、miniprogram/app.js里改个商户名就上线现实恰恰相反——这个版本本质是一套基于微信原生框架构建、已预置保洁/月嫂/维修三大服务类目、含订单流与用户中心但支付模块被显式注释掉的结构化源码包。它不提供一键部署的云后台也不带SaaS运营看板它的价值在于用真实业务逻辑如服务时间选择器联动库存校验、师傅接单状态机、评价标签聚合替代了“Hello World”级模板让开发者能跳过从零搭路由、写表单验证、设计服务卡片的重复劳动。适合两类人一是需要快速交付中小家政公司定制小程序的外包团队二是想吃透微信小程序中复杂表单异步状态管理本地缓存策略组合落地的进阶学习者。注意v2.8.58明确标注“小程序违规支付功能暂时无法使用”这意味着所有wx.requestPayment调用均被屏蔽你必须自行对接微信支付v3接口并完成资质审核否则订单流程在“确认支付”环节必然中断。2. 解构 v2.8.58 的核心结构从目录组织到关键业务模块定位2.1 源码包层级与微信原生框架适配性分析解压v2.8.58.zip后目录结构清晰体现微信小程序原生开发范式而非 uni-app 或 Taro 等跨平台方案——这直接决定了后续修改路径。根目录下project.config.json明确声明miniprogramRoot: miniprogram/miniprogram/内部为标准四文件组件体系miniprogram/ ├── app.js # 全局生命周期与全局数据初始化 ├── app.json # 页面路由、窗口样式、tabBar 配置重点检查 navigationStyle: custom 是否启用自定义导航栏 ├── project.config.json # 开发者工具配置含 AppID 占位符需替换 ├── pages/ # 所有页面目录 │ ├── index/ # 首页服务分类轮播热门师傅列表 │ ├── order/ # 订单中心含待支付/待服务/已完成状态分页 │ └── ... # 其他页面 ├── components/ # 自定义组件如 service-card、time-picker ├── utils/ # 工具函数日期格式化、手机号脱敏、本地缓存封装 └── lib/ # 第三方库wxParse富文本解析、weui-miniprogramUI 组件提示该版本未使用 npm 包管理所有依赖以 miniprogram_npm 目录形式存在但实际仅引入weui-miniprogram用于按钮与弹窗。若需扩展能力如地图选点应优先通过npm install安装官方tarojs/plugin-platform-weapp兼容包而非手动拷贝 JS 文件——后者易引发require路径错误。2.2 关键业务模块源码定位与可修改性评估v2.8.58 的业务逻辑集中在pages/与utils/下其设计遵循“页面驱动状态”原则而非全局 store。以下为高频修改点定位模块位置文件路径修改目的注意事项首页服务分类pages/index/index.js替换轮播图、调整分类图标、隐藏非启用类目data.categoryList数组控制展示categoryList[i].status 1表示启用预约时间选择器components/time-picker/time-picker.js修改可选时段粒度如从30分钟改为15分钟、禁用节假日this.setData({ timeSlots })中timeSlots为二维数组[[09:00, 09:30], [10:00, 10:30]]格式订单提交逻辑pages/order/confirm/confirm.js接入微信支付v3、添加地址校验、插入风控埋点onConfirmOrder()函数内wx.requestPayment调用已被注释需替换为requestPaymentV3()方法师傅接单状态机pages/order/detail/detail.js自定义接单超时规则、添加抢单失败提示updateOrderStatus()函数处理status字段变更status2为已接单status3为服务中2.2.1 为什么app.json中的navigationStyle: custom是修改起点该配置启用自定义导航栏意味着pages/index/index.wxml中navigation-bar组件实际渲染的是components/nav-bar/nav-bar.wxml。若需修改顶部导航栏高度热搜词“微信小程序顶部导航栏高度”常指向此问题不能直接改app.json而需同步调整components/nav-bar/nav-bar.wxss中.nav-bar的height值默认44pxiOS 安全区需加env(safe-area-inset-top)pages/index/index.json中navigationStyle: custom必须保留否则自定义组件不生效app.js中onLaunch里wx.getSystemInfoSync().statusBarHeight获取状态栏高度用于动态计算导航栏总高/* components/nav-bar/nav-bar.wxss */ .nav-bar { height: calc(44px var(--status-bar-height, 0px)); /* 支持 iPhone X 安全区 */ padding-top: var(--status-bar-height, 0px); }注意--status-bar-height变量需在app.js的onLaunch中通过wx.setStorageSync(statusBarHeight, res.statusBarHeight)存储并在组件attached生命周期中读取。直接硬编码44px会导致刘海屏机型导航栏遮挡内容。2.3 本地缓存策略与用户状态持久化实现v2.8.58 使用wx.setStorageSync/wx.getStorageSync实现轻量级状态管理而非 Redux 或 MobX。关键缓存点如下userInfo存储用户昵称、头像、openidutils/auth.js中login()方法写入currentAddress默认收货地址pages/address/list/list.js中编辑后更新orderCache未提交订单的临时数据pages/order/confirm/confirm.js中onUnload时保存// utils/auth.js function login() { wx.login({ success: res { wx.request({ url: https://api.example.com/login, // 此处需替换为你的后端地址 data: { code: res.code }, success: resp { const { openid, nickname, avatar } resp.data; wx.setStorageSync(userInfo, { openid, nickname, avatar, token: resp.data.token // 后端返回的 JWT }); } }) } }) }提示token缓存是鉴权关键。所有wx.request调用需在header中携带Authorization: Bearer ${token}。若后端未返回token则pages/order/confirm/confirm.js中的getOrderDetail()将因 401 错误导致页面白屏——这是新手最常卡住的点。3. 支付功能重建从微信支付v3接口对接到订单状态闭环3.1 支付模块被禁用的根源与合规前提v2.8.58 注释掉支付功能根本原因在于微信支付v3接口要求商户号、APIv3密钥、证书文件三者严格匹配且小程序主体需完成微信支付商户平台认证。单纯解压源码无法绕过此流程。若强行启用旧版wx.requestPayment将触发requestPayment:fail invalid sign错误。因此重建支付的第一步是登录 微信支付商户平台 → “产品中心” → 开通“JSAPI支付”在“账户中心” → “API安全” → 下载 APIv3 密钥32位字符串并妥善保管在“开发配置” → “公众号/小程序支付” → 添加你的小程序 AppID非商户号注意“小程序违规”提示并非代码缺陷而是指该开源版本未完成上述资质绑定。未绑定的调用会返回{errcode:48001,errmsg:api not available}。3.2 在confirm.js中集成微信支付v3统一下单pages/order/confirm/confirm.js是支付入口。需替换原注释代码接入后端统一下单接口// pages/order/confirm/confirm.js onConfirmOrder() { const orderData this.data.orderForm; wx.request({ url: https://your-api.com/pay/unifiedorder, // 你的后端统一下单接口 method: POST, header: { Authorization: Bearer wx.getStorageSync(userInfo).token }, data: { body: 家政服务订单, out_trade_no: ORDER_${Date.now()}, // 商户订单号需全局唯一 total_fee: orderData.totalPrice * 100, // 单位分 spbill_create_ip: 127.0.0.1, // 实际需获取用户真实IP此处仅为示意 notify_url: https://your-domain.com/pay/notify // 支付结果回调地址 }, success: (resp) { if (resp.data.prepay_id) { this.requestPayment(resp.data); // 调用微信 SDK } else { wx.showToast({ title: 下单失败, icon: none }); } } }) }, requestPayment(payData) { const timestamp Math.floor(Date.now() / 1000).toString(); const nonceStr Math.random().toString(36).substr(2, 15); const packageStr prepay_id${payData.prepay_id}; const paySign this.generatePaySign( payData.appid, timestamp, nonceStr, packageStr, payData.paySignKey // 后端返回的 APIv3 密钥 ); wx.requestPayment({ timeStamp: timestamp, nonceStr: nonceStr, package: packageStr, signType: RSA, paySign: paySign, success: () { wx.showToast({ title: 支付成功, icon: success }); this.updateOrderStatus(paid); // 更新订单状态 }, fail: (err) { console.error(支付失败, err); if (err.errMsg.includes(requestPayment:fail cancel)) { wx.showToast({ title: 用户取消支付, icon: none }); } } }); },3.2.1generatePaySign签名生成逻辑前端简化版微信支付v3 要求签名使用 RSA-SHA256但小程序环境不支持 Node.js 的crypto模块。强烈建议将签名逻辑移至后端前端仅传递必要参数。若坚持前端签名需引入jsrsasign库npm install jsrsasign// utils/sign.js const KJUR require(jsrsasign).KJUR; function generatePaySign(appId, timestamp, nonceStr, packageStr, apiKey) { const message appId${appId}\nnonceStr${nonceStr}\ntimestamp${timestamp}\npackage${packageStr}\n; const key KJUR.crypto.KEYUTIL.getKey(apiKey); // APIv3密钥需为 PEM 格式私钥 return KJUR.crypto.Signature.sign(message, key, SHA256withRSA); }提示apiKey必须是 PEM 格式私钥以-----BEGIN PRIVATE KEY-----开头而非商户平台下载的纯文本密钥。需用 OpenSSL 转换openssl pkcs8 -topk8 -inform PEM -in apiclient_key.pem -outform PEM -nocrypt -out apiclient_key_pkcs8.pem。3.3 订单状态闭环从支付成功到服务调度支付成功后success回调中调用this.updateOrderStatus(paid)仅更新前端 UI。真正的状态闭环需后端完成微信支付回调地址 (notify_url) 接收异步通知校验签名后更新数据库order.status paid后端触发服务调度逻辑根据服务类型、地理位置、师傅空闲时段向匹配师傅推送服务请求调用wx.subscribeMessage模板消息师傅端小程序监听onAppShow拉取新订单列表并更新本地orderCache// pages/order/list/list.js onShow() { this.loadOrders(); // 拉取订单列表 // 检查是否有新订单通过 WebSocket 或轮询 this.checkNewOrder(); }, checkNewOrder() { wx.request({ url: https://your-api.com/order/new, header: { Authorization: Bearer wx.getStorageSync(userInfo).token }, success: (resp) { if (resp.data.newCount 0) { wx.showToast({ title: 新订单${resp.data.newCount}条, icon: none }); this.loadOrders(); // 刷新列表 } } }); }4. 修改刚进入的加载页面从启动图到首屏渲染性能优化4.1 启动图Splash Screen定制路径微信小程序启动图由app.json中splashScreen字段控制但 v2.8.58 未启用该特性微信基础库 2.27.0 才支持。因此实际生效的是pages/index/index.wxml中的首屏加载逻辑!-- pages/index/index.wxml -- view classloading-container wx:if{{isLoading}} image src/images/loading.gif classloading-icon/image text classloading-text正在加载服务.../text /view view wx:else !-- 真实首页内容 -- /view修改“刚进入的加载页面”需两步替换/images/loading.gif为自有品牌动效图尺寸建议 120×120px体积 50KB在pages/index/index.js的onLoad中控制isLoading显示时长onLoad() { this.setData({ isLoading: true }); // 模拟数据加载实际应替换为真实 API 请求 setTimeout(() { this.setData({ isLoading: false, serviceList: this.mockServiceData() // 替换为真实数据 }); }, 800); // 最小加载时长避免闪屏 },注意setTimeout仅作演示。生产环境必须等待wx.request返回后才setData({ isLoading: false })否则用户看到空白页。4.2 首屏渲染性能瓶颈定位与优化v2.8.58 首屏慢的主因是index.js中onLoad一次性拉取过多数据getCategoryList()获取全部服务类目含图标 URLgetHotWorkers()获取热门师傅含头像 URL、评分、接单数getBannerList()获取轮播图含图片 URL三个请求串行执行总耗时叠加。优化方案为并发请求 骨架屏// pages/index/index.js onLoad() { this.setData({ isLoading: true }); Promise.all([ this.getCategoryList(), this.getHotWorkers(), this.getBannerList() ]).then(([categories, workers, banners]) { this.setData({ isLoading: false, categoryList: categories, hotWorkers: workers, bannerList: banners }); }).catch(err { console.error(首页数据加载失败, err); this.setData({ isLoading: false }); }); },4.2.1 骨架屏Skeleton Screen实现在pages/index/index.wxml中wx:if{{isLoading}}区块内添加结构化占位符而非简单 GIFview classloading-container wx:if{{isLoading}} !-- 轮播图骨架 -- view classskeleton-banner/view !-- 分类骨架 -- view classskeleton-category-list view classskeleton-category wx:for{{Array(3)}} wx:keyindex/view /view !-- 师傅列表骨架 -- view classskeleton-worker-list view classskeleton-worker wx:for{{Array(4)}} wx:keyindex/view /view /view对应 CSS/* pages/index/index.wxss */ .skeleton-banner { height: 200rpx; background: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%); background-size: 200% 200%; animation: loading 1.5s ease infinite; } keyframes loading { 0% { background-position: 200% 0; } 100% { background-position: -200% 0; } } .skeleton-category { width: 120rpx; height: 120rpx; margin: 0 20rpx; border-radius: 50%; background: #f0f0f0; }提示骨架屏动画使用linear-gradientbackground-position实现比 GIF 更轻量且可精确控制节奏。animation: loading 1.5s ease infinite确保流动感自然。5. 进阶技巧微信小程序单选框状态同步与长按拖拽滚动实战5.1 单选框Radio状态与表单数据双向绑定v2.8.58 中服务预约页pages/order/confirm/confirm.wxml使用原生radio-group但未实现与data.orderForm的自动同步。需手动绑定!-- pages/order/confirm/confirm.wxml -- radio-group bindchangeonServiceChange label wx:for{{serviceTypes}} wx:keyid radio value{{item.id}} checked{{item.id orderForm.serviceTypeId}}/ text{{item.name}}/text /label /radio-group// pages/order/confirm/confirm.js onServiceChange(e) { const selectedId e.detail.value; const serviceType this.data.serviceTypes.find(t t.id selectedId); this.setData({ orderForm.serviceTypeId: selectedId, orderForm.serviceTypeName: serviceType.name, orderForm.price: serviceType.price // 同步价格 }); },注意bindchange事件中e.detail.value返回字符串需用非比较item.id因item.id可能为数字类型。5.2 长按拖拽滚动Draggable Scroll实现服务时间选择器v2.8.58 的时间选择器为静态列表用户体验差。通过touchstart/touchmove/touchend实现拖拽滚动!-- components/time-picker/time-picker.wxml -- view classtime-scroll-container bindtouchstartonTouchStart bindtouchmoveonTouchMove bindtouchendonTouchEnd view classtime-scroll-content styletransform: translateY({{scrollY}}px); view wx:for{{timeSlots}} wx:keyindex classtime-slot {{item}} /view /view /view// components/time-picker/time-picker.js data: { scrollY: 0, startY: 0, startScrollY: 0, isDragging: false }, onTouchStart(e) { this.setData({ startY: e.touches[0].clientY, startScrollY: this.data.scrollY, isDragging: true }); }, onTouchMove(e) { if (!this.data.isDragging) return; const currentY e.touches[0].clientY; const deltaY currentY - this.data.startY; this.setData({ scrollY: this.data.startScrollY deltaY }); }, onTouchEnd() { if (!this.data.isDragging) return; // 惯性滚动模拟简化版 const targetY Math.round(this.data.scrollY / 80) * 80; // 对齐到每个时间槽高度 this.setData({ scrollY: targetY, isDragging: false }); },5.2.1 时间槽对齐精度控制表参数值说明time-slot高度80rpxCSS 中.time-slot { height: 80rpx; }对齐步长80onTouchEnd中Math.round(scrollY / 80) * 80确保停在整数槽位拖拽阈值10rpx可添加if (Math.abs(deltaY) 10) return;避免误触提示touchmove中直接setData会阻塞渲染。生产环境应使用wx.createSelectorQuery()获取容器尺寸后用requestAnimationFrame节流更新scrollY但 v2.8.58 场景下80rpx步长已足够平滑。本文还有配套的精品资源点击获取