简介:新版多多房产小程序加公众号是一套完整的房产信息发布系统,主要面向房产中介、楼盘展示企业及需要对微信公众号进行二次开发的工程师。这套源码将小程序前端和公众号管理后台合二为一,可快速实现房源录入、分类展示、详情浏览、用户留言等常见业务流程,整体代码结构清晰,适合有脚本语言和服务器端语言基础的人群进行学习或改造。压缩包内共有834个文件,包括小程序核心的脚本、页面结构、样式配置,以及后端接口、后台页面、图片和字体资源,大小仅3.13兆,下载和部署都很方便。目前已有832人浏览学习,描述明确说明‘亲测可用’,可直接运行验证。源码按功能模块组织目录,并内置常用的组件库和轮播图等交互组件,便于理解小程序的组件化开发模式,也方便在此基础上进一步扩展房源筛选、地图定位或在线预约功能。对于想快速搭建房产类应用或用于毕业设计、技术实践的开发者,这是一份值得收藏的实战源码。
1. 拿到的不是小程序源码,而是一套需要重新落地的双端骨架
解压之后大多数人是懵的:项目里没有一坨 .wxml、.js,而是一批 CSS 文件。这是很多“小程序+公众号”源码包的常态——小程序端和 H5 端共用一套业务后端,资源包先给你公众号 H5 端的皮肤层,微信公众号绑定后通过 web-view 把 H5 嵌进小程序,或者独立跑公众号菜单。多多房产这套 2.5.46 的价值恰好在这个壳上:aui.css 承担布局、weui.css 提供微信风格组件、swiper.min.css 处理楼盘轮播,house.css 把通用框架的毛边收掉。这篇我会从这批样式文件怎么按序加载讲起,再落到 uni-app 嵌入公众号时定位和标题怎么配,抓包和启动页该改哪里,最后补上从 H5 跳回小程序的完整链路。
2. 认识 aui.css 与 weui.css:公众号 H5 端皮肤与主题切换
2.1 这一批 CSS 文件到底谁先加载
多多房产的资源包里有 weuix.css、aui.css、weui.css、swiper.min.css、style.css、demo.css、aui-skin.css、aui-skin-night.css、aui-flex.css、house.css。看起来是十份文件,实际上它们分工非常明确:aui 和 weui 负责两套基础设计语言,skin 处理主题皮肤,flex 处理弹性布局,house.css 才是真正跟房源卡片、筛选栏、楼栋标签强相关的业务样式。
常见做法是把基础核心先加载,皮肤按用户偏好切换,最后再让 house.css 覆盖默认样式。如果直接在公众号 H5 首页一次性全部 link 进来,虽然省事,但夜间模式的 aui-skin-night.css 会和 aui-skin.css 冲突,后面的文件把前面的颜色全部洗掉。所以加载顺序不是随便写的,我一般会在模板里放成下面这样:
<!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no"> <link rel="stylesheet" href="css/aui.css"> <link rel="stylesheet" href="css/weui.css"> <link rel="stylesheet" href="css/weuix.css"> <link rel="stylesheet" href="css/aui-skin.css"> <link rel="stylesheet" href="css/aui-skin-night.css" disabled> <link rel="stylesheet" href="css/aui-flex.css"> <link rel="stylesheet" href="css/swiper.min.css"> <link rel="stylesheet" href="css/house.css"> <link rel="stylesheet" href="css/style.css"> <title>房源列表</title> </head> <body> <div class="aui-flex-col house-list">...</div> <script src="js/swiper.min.js"></script> </body> </html>这段代码里,aui.css 先定下栅格和基础排版,weuix.css 再把微信官方组件的细节补上,皮肤文件随后。disabled属性让夜间皮肤默认不生效,但浏览器仍会解析它,切换时只需去掉 disabled。swiper.min.css 只服务轮播区域,不参与全局重置。house.css 和 style.css 放在最后是让业务规则能覆盖框架默认值,避免出现「想改房源卡片圆角却找不到选择器」的问题。
2.1.1 样式文件职责速查
| 文件 | 职责 | 在房产场景里的典型用处 |
|---|---|---|
| aui.css | 基础栅格、按钮、表单、列表 | 楼盘详情页的区块间距与底部操作栏 |
| weui.css | 微信官方视觉组件 | 公众号内授权弹窗、表单校验、Actionsheet |
| weuix.css | weui 的扩展样式 | 增强 tab、面板、搜索框等交互组件 |
| aui-skin.css | 日间主题变量 | 默认白色卡片、主色调边框 |
| aui-skin-night.css | 夜间主题变量 | 夜间看房模式下深色背景与反色文字 |
| aui-flex.css | flex 布局工具类 | 横向排列的户型标签、楼盘图标的对齐 |
| swiper.min.css | 轮播图样式 | 房源主图、周边配套 banner |
| house.css | 房产业务样式 | 价格标签、楼栋状态、预约看房按钮 |
| style.css | 项目级自定义 | 针对当前楼盘的覆盖样式 |
aui-flex 的作用常被低估。如果你做过房源卡片,就会发现价格区域、面积区间、朝向标签天然是三个 flex item,aui-flex.css 里的aui-flex-row aui-flex-between aui-flex-middle能直接完成左右布局和垂直居中,不需要为每一处间距重写 flex 属性。对于二手房的标签列表,这个文件能让aui-flex-wrap自动换行,避免标签溢出卡片。
2.2 主题色与夜间模式的切换原理
aui-skin.css 和 aui-skin-night.css 不是简单的暗色变量,它们内部定义了background-color、text-color、border-color这一组语义 token。切换主题时,通常会同时给html标签打一个>function applyTheme(theme) { const html = document.documentElement; const night = document.querySelector('link[href$="aui-skin-night.css"]'); const day = document.querySelector('link[href$="aui-skin.css"]'); if (theme === 'night') { night.disabled = false; day.disabled = true; html.setAttribute('data-theme', 'dark'); } else { night.disabled = true; day.disabled = false; html.setAttribute('data-theme', 'light'); } }
这段 JS 的逻辑不难:通过选择器找到皮肤文件,切换 disabled 状态,让浏览器重新计算样式。><web-view src="https://h5.example.com/house/list?theme=night&cityId=110000"></web-view>
这里的theme参数在 H5 端入口处解析,再调用applyTheme。我在实际项目里遇到过「公众号内正常、小程序内主题不生效」的情况,最后查出来就是小程序端没有把主题参数拼进 web-view 的 src,只发了 token。所以凡是需要 H5 感知的状态,尽量都走 URL 参数,不要赌两端 storage 同步。
3. uni-app 嵌入公众号:定位获取、标题动态化与开发者绑定
3.1 在公众号 H5 里拿到经纬度
房产 App 里最核心的是「附近房源」和「按距离排序」,因此 H5 端必须拿到经纬度。在 uni-app 开发 H5 嵌入微信公众号时,不能只用uni.getLocation,因为微信内置浏览器需要通过 JS-SDK 才能稳定返回坐标。
常见做法是先用window.wx.config注入签名,再调wx.getLocation。签名需要后端根据当前页面 URL 生成,而且 URL 不能带#后面的 hash。为了兼容小程序 web-view 场景,我会把取定位封装成带 fallback 的方法:
import config from '@/common/config.js'; function getWxLocation() { return new Promise((resolve, reject) => { if (!window.wx) { uni.getLocation({ type: 'gcj02', success: resolve, fail: reject }); return; } window.wx.config({ debug: false, appId: config.mpAppId, timestamp: config.jsSdk.timestamp, nonceStr: config.jsSdk.nonceStr, signature: config.jsSdk.signature, jsApiList: ['getLocation'] }); window.wx.ready(() => { window.wx.getLocation({ type: 'gcj02', success: resolve, fail: reject }); }); }); }代码里第一层判断很关键:window.wx不存在时直接用 uni 的定位接口,保证普通浏览器和 App 环境也能降级运行。gcj02是国测局坐标,小程序地图组件和腾讯位置服务默认都是这个坐标系;如果后端地图服务使用百度坐标,需要再做坐标偏移转换。
// 调用示例 getWxLocation().then((res) => { this.latitude = res.latitude; this.longitude = res.longitude; this.loadNearbyHouse(res.latitude, res.longitude); }).catch(() => { uni.showToast({ title: '定位失败,请检查授权', icon: 'none' }); });3.1.1 uni.getLocation 与公众号定位的差异
| 场景 | uni.getLocation | wx.getLocation(JS-SDK) |
|---|---|---|
| 普通浏览器 | 部分支持 | 不存在 wx 对象,需降级 |
| 微信内置浏览器 | 偶尔返回国家中心点 | 稳定返回,需签名 |
| 小程序 web-view | 不适用 | 可用,URL 必须列入业务域名 |
| 授权方式 | 浏览器地理位置授权 | 微信服务号授权 |
两者都要求用户授权,但 JS-SDK 的授权是微信统一弹出的,体验比浏览器自带弹窗更好。签名接口必须使用当前页面的window.location.href去掉#之后的字符串,并且/不转义。如果签名 URL 和后端拿到的不一致,wx.ready不会触发,此时先不要怀疑代码,打开debug: true看报错信息是「config:invalid signature」还是「config:invalid url」,能省不少时间。
3.2 小程序内动态设置标题,公众号端用 document.title
房产详情页的标题一般要动态展示「楼盘名-几室几厅」,在小程序端可以通过uni.setNavigationBarTitle实现,但如果同一个页面既被小程序原生访问,又被公众号 H5 访问,需要区分处理。
// #ifdef MP-WEIXIN uni.setNavigationBarTitle({ title: `${detailInfo.name}-${detailInfo.roomType}` }); // #endif // #ifdef H5 document.title = `${detailInfo.name}-${detailInfo.roomType}`; if (window.WeixinJSBridgeReady) { window.WeixinJSBridgeReady.invoke('setPageTitle', { title: document.title }); } // #endif第一段是小程序专用,第二段是 H5 专用。WeixinJSBridgeReady事件可以保证在微信浏览器里标题同步到顶部导航,否则 H5 里改了 title,公众号的网页标题栏可能不刷新。需要注意WeixinJSBridgeReady不是每一次都稳定触发,所以代码里先直接设置document.title再尝试 invoke,避免出现 Android 微信里标题半天不动的现象。
小程序端动态标题还有一层隐藏问题:如果页面延迟加载数据,uni.setNavigationBarTitle必须在onLoad里异步回调后执行,而不能在页面配置里写死。我一般会在拿到详情接口结果后再设置标题,同时把导航背景色一并调成项目主色,减少页面切换时的闪白。
3.3 微信开发者工具提示“登录用户不是该小程序的开发者”
在把多多房产小程序跑起来时,开发者工具经常会报“登录用户不是该小程序的开发者”。这个错误只跟权限绑定有关,和源码本身关系不大。
处理步骤:
- 使用小程序管理员账号登录微信公众平台。
- 进入「管理」->「成员管理」->「项目成员」,把当前调试的微信号添加为「项目成员」,角色选开发者。
- 退出微信开发者工具并重新登录,确保右上角头像已切换成被添加的微信号。
- 如果 H5 页面要通过 web-view 加载,还需要在「开发管理」->「开发设置」->「业务域名」中把 H5 域名加进去,同时下载校验文件放到域名根目录。
{ "pages": [ { "path": "pages/house/detail", "style": { "navigationBarTitleText": "楼盘详情", "navigationBarBackgroundColor": "#1a73e8", "navigationBarTextStyle": "white" } } ] }上面的 pages.json 片段展示了navigationBarTitleText默认值和导航栏颜色配置。即使之后会用 JS 动态改标题,这里最好也保留一个兜底文案,避免接口加载期间出现白底黑字的空标题。开发者工具的“本地设置”里也可以临时勾选「不校验合法域名」,但这只能用于真机预览,上线前必须关闭。
4. 微信小程序抓包与加载页优化:从链接过滤到导航栏高度
4.1 把公众号链接和小程序请求一起抓出来
多多房产这套项目里,小程序端会请求自己的 API,公众号 H5 也会请求同样的接口。排查问题时,最怕两个环境混在一起不知道哪个域名出了问题。微信小程序抓包时,我通常把代理工具配置成只过滤业务域名,这样接口请求和公众号链接会同时显示在抓包面板里,能直接对比两端返回差异。
# Android 手机设置代理(需同局域网) adb shell settings put global http_proxy 192.168.1.10:8888 # 取消代理 adb shell settings delete global http_proxy在 Mac 上常见的抓包工具是 Charles,Windows 上用 Fiddler 较多。需要注意 HTTPS 抓包需要在手机上安装证书,Android 7.0 以上还要在项目里配置network_security_config.xml允许 user 证书,否则小程序里看到的是加密乱码。
<network-security-config> <base-config cleartextTrafficPermitted="true"> <trust-anchors> <certificates src="system"/> <certificates src="user"/> </trust-anchors> </base-config> </network-security-config>这段配置表示允许明文流量,并且信任用户安装的证书。它只影响本地调试,正式包建议去掉user证书信任。
抓包时的过滤规则可以写成下面这样,只保留业务相关域名,减少噪声。
.*h5\.example\.com.* .*api\.example\.com.*表格式的常用抓包项如下:
| 抓包目标 | Charles 配置 | 常见问题 |
|---|---|---|
| 公众号 H5 请求 | Proxy -> SSL Proxying Settings | 证书未安装导致 TLS 握手失败 |
| 小程序 wx.request | 直接显示在 Structure 里 | 需开启「不校验合法域名」 |
| web-view 内嵌页面 | 会以 H5 域名显示 | 确认业务域名已经配置 |
| 定位接口 | 关注 coordinates 字段 | gcj02 坐标偏移异常 |
4.1.1 通过 vConsole 就地查看接口请求
如果不想开代理,也可以在 H5 端引入 vConsole,在手机上直接看请求结果。多多房产的 style.css 和 demo.css 都是纯静态文件,把它和 vConsole 一起注入页面,调试效率很高:
<script src="js/vconsole.min.js"></script> <script> new VConsole(); </script>vConsole 的好处是能看到console.log、网络请求和 cookie,但它看不到小程序原生的wx.request。所以小程序端仍然建议配合抓包工具,H5 端用 vConsole 足够。
4.2 修改刚进入的加载页面
很多房产小程序一进来就是首页加载,网络慢时白屏好几秒。“修改刚进入的加载页面”本质上是把启动页改成自定义的骨架屏,而不是系统默认的 launch screen。多多房产的 pages.json 里可以把第一个页面指向一个单独的 Splash 页,等数据到位后再uni.redirectTo到首页。
{ "pages": [ { "path": "pages/splash/splash", "style": { "navigationStyle": "custom", "backgroundColor": "#f5f5f5", "disableScroll": true } }, { "path": "pages/index/index", "style": { "navigationBarTitleText": "多多房产", "enablePullDownRefresh": true } } ] }// pages/splash/splash.js onLoad() { setTimeout(() => { uni.reLaunch({ url: '/pages/index/index' }); }, 800); }这里的disableScroll防止加载页出现滚动条,reLaunch会关闭 splash 页,确保用户回退时不会回到加载页。延迟时间不建议超过 1 秒,否则用户会认为卡死。如果你需要展示广告或品牌形象,可以把这个时间拉长到 3 秒,但要提供跳过按钮。
4.2.1 导航栏高度与胶囊按钮的适配
自定义加载页后,通常也需要手动适配微信小程序顶部导航栏高度。因为把navigationStyle设成custom后,右上角的胶囊按钮还在,但标题栏高度需要自己计算。
const info = wx.getWindowInfo ? wx.getWindowInfo() : wx.getSystemInfoSync(); const statusBarHeight = info.statusBarHeight; const menu = wx.getMenuButtonBoundingClientRect(); const navBarHeight = (menu.top - statusBarHeight) * 2 + menu.height;getMenuButtonBoundingClientRect()会返回胶囊按钮的top和height,通过公式算出导航栏自定义视图的高度。这段代码兼容基础库低版本,因为老版本没有wx.getWindowInfo,需要降级到getSystemInfoSync。很多「自定义导航栏顶部按钮被胶囊遮挡」的 bug,都来自这里少算了statusBarHeight。
适配时可以把计算结果存到全局变量,然后在页面布局里用padding-top撑开。注意不要直接写死 44px,iPhone 14 Pro 和普通 Android 的statusBarHeight不一样,硬编码会导致顶部标题偏移。
5. 从 H5 跳转小程序:weixin://dl/business 的生成、触发与验证
5.1 生成 scheme 的两种路径
公众号 H5 里最常见的跳小程序方式是 URL Scheme,也就是weixin://dl/business开头的链接。生成方式有两种:一是在微信公众平台的小程序「工具」->「生成 URL Scheme」里手动填路径,二是用云开发或服务端接口动态生成。
const cloud = require('wx-server-sdk'); cloud.init(); exports.main = async (event) => { const result = await cloud.openapi.urlscheme.generate({ jumpWxa: { path: 'pages/house/detail', query: 'id=1024&from=h5' }, expiresAt: Date.now() + 30 * 24 * 3600 * 1000 }); return { scheme: result.openlink }; };expiresAt是过期时间的时间戳,必须精确到毫秒。有效期内同一 scheme 可以被多次使用,但过期后需要重新生成。动态生成方案适合做带参数的活动链接,比如从公众号文章跳转到指定房源详情页。
5.2 触发条件与验证方法
到 H5 端后,用window.location.href跳转,但必须确保由用户点击触发,不能放在onLoad里自动跳。
<a onclick="openMiniProgram()">打开小程序看房</a>function openMiniProgram() { const scheme = 'weixin://dl/business/?t=xxxxxxxx'; window.location.href = scheme; }测试时要用微信内置浏览器打开,外部浏览器无法识别这个协议。验证的关键是看目标小程序页面的onLoad参数是否正确:
// pages/house/detail.js onLoad(options) { console.log('from:', options.from, 'id:', options.id); }如果from和id都能打印出来,说明 scheme 的 query 生效;如果只打开了小程序却没有参数,检查jumpWxa.query的格式,它必须是key=value&key2=value2且不能带?。这个流程里最容易踩的坑是 scheme 在小程序内二次跳转,那需要调用wx.miniProgram.navigateBack而不是重新触发 scheme。
本文还有配套的精品资源,点击获取