跨平台生活服务源码:用uniapp一套代码实现App、小程序和H5 📅 发布时间:2026/9/15 10:44:27 👁 浏览次数: 简介一套直接面向同城生活服务领域的跨平台客户端源代码兼容App、小程序和H5三端整体思路对标58同城、赶集网等分类信息平台。平台以信息发布、置顶、刷新、佣金分成及广告位作为核心收费模式每个大类的栏目和每类信息下的字段均可自定义能够覆盖招聘求职、房产租售、二手交易、拼车出行、生活服务、社区论坛等多种本地化业务适合创业者、产品经理以及负责分类信息项目的开发者用于快速验证业务或进行二次开发。资源包采用zip格式整体大小约34.61MB目前公开的文件类型与文件数量暂未提供实际内容需以下载后为准。该资源已有496人浏览学习从客户端源码角度看其中应包含多端工程目录、接口定义、界面资源及配置文档等关键部分可帮助开发者理解信息发布流程、自定义栏目、置顶佣金、订单支付和权限控制等完整业务闭环。对于希望低成本搭建并运营本地生活服务平台的团队或个人这是一份颇具参考价值的完整代码样例能有效缩短从产品构思到上线Demo的周期。1. 跨平台生活服务源码一套代码跑 App、小程序和 H5 到底省了什么解开一个名为“客户端源代码跨平台app小程序h5兼容58同城赶集网生活服务分类信息发布置顶佣金社区论坛房产招聘二手自定义栏目等.zip”的压缩包里面通常是一个 uniapp 前端工程加一个后端 API 工程。所谓“跨平台”不是把三个独立项目打包在一起而是用一套 Vue 语法在编译期分别产出 iOS/Android 包、微信小程序和标准 H5 网页。分类信息、置顶付费、佣金分账、社区论坛、房产招聘二手这些 58、赶集已验证过的业务形态才是这套源码真正值钱的部分跨端只是减少客户端重复开发的手段业务建模和表设计才是后期能不能改得动的关键。这篇文章按“工程结构—核心业务—扩展栏目—多端上线”的顺序把每一层应该怎么接、参数怎么设、坑在哪讲清楚适合接手这类源码做二次开发、或者打算自己从零搭生活服务平台的团队。2. 跨平台工程怎么搭从解压源码到三端可编译的最小结构这一类 zip 解压后常见两层结构前端client/uniapp后端server/PHP 或 Java 居多。先把前端在三端跑通再谈业务扩展顺序不能反。2.1 为什么生活服务类项目默认选 uniapp跨平台方案有 React Native、Flutter、Taro、uni-app 几类但“App 小程序 H5 三端同产出”这个诉求下uniapp 的编译管线覆盖最直接。Taro 偏重小程序和 H5App 输出要借 React NativeFlutter 不产小程序。uniapp 把小程序、App通过离线 SDK 或云打包壳、H5 三条产物都收进同一个build流程而且它编译到小程序/H5 是源码级转换调试时可以直接在微信开发者工具和浏览器里断点不需要额外起原生项目。这种取舍对“分类 发布 论坛”这类强模板页面很合适列表页、详情页、发布表单高度重复一套组件拿到三端渲染差别集中在支付、定位、分享这些偶发的平台能力调用上。2.2 最小可编译工程创建命令和目录约定如果 zip 里只给后端没有前端工程可以用官方模板重新初始化一套再把业务页面迁进去npx degit dcloudio/uni-preset-vue#vite life-services cd life-services npm install npm run dev:h5 npm run build:mp-weixin三条命令的产物分别落到dist/dev/h5、dist/build/mp-weixin。App 端不直接产出原生工程需要在 HBuilderX 里“发行—原生App云打包”或者接离线 SDK 自行打壳这是源码里最容易漏掉的一步很多压缩包只给了前端源码App 壳要自己准备。工程目录里几个关键位置的职责如下路径作用二次开发时改动频率pages/页面路由uni-app 约定pages.json里注册高components/跨页面复用组件列表卡片、筛选栏、发布表单高utils/请求封装、鉴权、公共函数中static/图标、启动图、tabbar 图片小程序端必须本地文件中api/接口定义与参数组装高pages.json里globalStyle和tabBar字段的差异点要尽早确认小程序的 tabBar 图标必须本地图片H5 端可以用字体图标App 端navigationStyle定制自由度最高但custom模式下导航栏完全自理返回按钮、标题都要自己渲染团队没经验时不建议第一版就开。2.3 条件编译跨端代码的隔离与合并三端共用一套业务代码不等于所有代码行都三端跑。“同一份逻辑各端要不同实现”的场景要靠条件编译// utils/location.js export function getCurrentLocation() { // #ifdef H5 return fetchLocationFromBrowser(); // 浏览器 geolocation 或 JS-SDK // #endif // #ifdef MP-WEIXIN return uni.getLocation({ type: wgs84 }); // 小程序会弹授权框 // #endif // #ifdef APP-PLUS return plus.geolocation.getCurrentPosition(); // App 壳原生能力 // #endif }条件编译里的平台宏注意两点H5只对浏览器环境生效微信公众号内嵌页同样走这段MP-WEIXIN只代表微信小程序如果后面要跑支付宝小程序得另加MP-ALIPAY分支。平台判断不能在运行期用process.env之类的前端环境变量蒙混会同时打进多余代码某些小程序平台直接报编译错误。3. 分类、发布、置顶、佣金生活服务信息流的四张核心表生活服务类平台的第一闭环是“看分类 → 发布信息 → 付钱置顶 → 平台分佣金”。源码质量高低先看这四张表设计而不是看页面数量。3.1 分类数据模型无限级分类与栏目自定义的底座分类表是所有栏目的入口。一套可用十年的结构至少要含父级、路径、层级、排序四个字段CREATE TABLE category ( cat_id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY, pid INT UNSIGNED NOT NULL DEFAULT 0, cat_name VARCHAR(50) NOT NULL, level TINYINT NOT NULL DEFAULT 1, -- 1一级 2二级 3三级 cat_path VARCHAR(255) NOT NULL DEFAULT , -- 例如 1,12,120 icon_url VARCHAR(255) DEFAULT , extend_schema TEXT, -- 自定义栏目字段 JSON auto_publish TINYINT NOT NULL DEFAULT 1, -- 是否免审直接上架 sort_order INT NOT NULL DEFAULT 0, status TINYINT NOT NULL DEFAULT 1 ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;cat_path存从根到自身的 ID 链查询某个二级分类下所有三级分类时直接WHERE cat_path LIKE 1,12,%避免递归。level和path存在冗余但读取性能换写入复杂度在分类这种低频变更表上是划算的。auto_publish字段决定了该分类下的信息要不要过审房产中介和二手手机这种高风险类目建议关掉免审。3.2 信息发布接口状态机、置顶订单一次生成信息表通常不细分房产表、招聘表而是共用一张主表加扩展字段。主表关注发布人、分类、标题、正文、图片、状态和置顶到期时间CREATE TABLE info ( info_id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY, cat_id INT UNSIGNED NOT NULL, uid INT UNSIGNED NOT NULL, title VARCHAR(80) NOT NULL, content TEXT, images TEXT, -- JSON 数组 extra_json TEXT, -- 自定义栏目字段值 status TINYINT NOT NULL DEFAULT 0, -- 0待审 1展示 2下架 3删除 is_top TINYINT NOT NULL DEFAULT 0, top_expire DATETIME DEFAULT NULL, -- 置顶到期时间 reply_count INT NOT NULL DEFAULT 0, view_count INT NOT NULL DEFAULT 0, created_at DATETIME NOT NULL ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;发布接口的常见做法是校验分类和字段 → 判断该分类是否免审 → 入库 → 如果选了置顶先生成待支付置顶订单。置顶不是发布动作的一部分而是独立的付费动作代码上要把两者拆开// POST /api/info/publish async function publish(req, res) { const { cat_id, title, content, images [], top_days 0, extra_json {} } req.body; const uid req.auth.uid; const cat await db.find(category, cat_id); if (!cat || cat.status ! 1) { return res.json({ code: 400, msg: 分类不存在或已下线 }); } const info_id await db.insert(info, { cat_id, uid, title, content, images: JSON.stringify(images.slice(0, 9)), // 限制图片数量防止脏数据 extra_json: JSON.stringify(extra_json), status: cat.auto_publish ? 1 : 0, is_top: 0, created_at: new Date() }); let order_no null; if (top_days 0) { const fee computeTopFee(cat_id, top_days); // 分类不同、单价不同 order_no genOrderNo(uid); await db.insert(pay_order, { order_no, uid, biz_type: top, biz_id: info_id, amount: fee, status: 0, // 0未支付 1已支付 2退款 created_at: new Date() }); } res.json({ code: 0, info_id, order_no }); }参数和字段含义对照如下参数用途注意cat_id决定展示在哪一级栏目、走哪套扩展字段前台只能选level3的叶子分类top_days置顶天数按天计费与computeTopFee联动注意设置上限避免跨年订单images图片 JSON 数组上传服务返回 URL先落库再回填防止前端还没传完就提交auto_publish分类的免审开关审核流里status0的信息不进搜索索引biz_type区分top、refresh、vip等付费类型佣金结算按这个字段分账流程上有个细节置顶费用不要在发布时直接扣余额会出现未支付但信息已置顶的脏状态而是先生成未支付订单用户从“我的发布”里看到待支付再付款支付回调里才真正置顶并开始计算到期时间。3.3 置顶排序别用 order by top_expire desc新手最容易写成ORDER BY top_expire DESC结果就是过期置顶信息的top_expire是过去时间负权重会把它直接挤到列表底但新发的普通信息也被压在它下面更糟的是置顶到期时间是乱的权重只依赖到期时间点先到期的置顶反而排前面。标准做法是把“是否在置顶期”当成一档再加权重细分SELECT info_id, title FROM info WHERE cat_id :catId AND status 1 ORDER BY (top_expire NOW()) DESC, -- 置顶期内排第一档 top_weight DESC, -- 同档内权重刷新置顶时加 created_at DESC LIMIT 20;top_weight是浮点数置顶续费时、刷新信息时都要递增。后台加一个每分钟跑一次的定时任务把top_expire NOW()的记录is_top置 0同时top_weight降为 0避免过期信息无限期占据一档的尾巴。3.4 佣金分账平台、渠道、发布者三方比例佣金在这套系统里至少有三类置顶费佣金发布者付钱买流量平台和渠道分、信息服务佣金发起咨询或成交后抽成、会员佣金包月/包年会员的渠道分成。无论哪种分账字段要统一落在支付订单里。// Payments 回调后触发分账 async function onPaySuccess({ order_no, paid_amount }) { const order await db.find(pay_order, { order_no }); await db.update(pay_order, { status: 1, paid_at: new Date() }, order_no); const split { platform_rate: 0.10, // 平台技术费 agent_rate: 0.05, // 区域代理/渠道 publisher_rate: 0.85 // 可提现余额冻结 T1 }; await db.insert(commission_log, { order_no, biz_type: order.biz_type, amounts: JSON.stringify({ platform: round2(paid_amount * split.platform_rate), agent: round2(paid_amount * split.agent_rate), publisher: round2(paid_amount * split.publisher_rate) }), status: 0 }); // 发布者余额加账但先冻结避免刚付款就提现跑路 await db.exec( UPDATE user_balance SET frozen frozen ? WHERE uid ?, [split.publisher_rate * paid_amount, order.uid] ); }三方比例不要硬编码后台做成配置项不同分类可以不同。特别提示佣金入账和地图这类外接 SDK 没关系难点在退款置顶刚付款就下架内容要能在commission_log里反向冲正设计分账表时留status冻结/可提/已提现/冲正四态退款只改状态不删流水对账时才能把账对上。4. 社区论坛 房产招聘二手 自定义栏目如何在共用表上长出新栏目论坛、房产、招聘、二手在数据形态上差异很大但“信息流”本质相同标题 正文 发布人 时间 扩展字段。好的源码取舍是把不同栏目都放在 category 和信息表组合之上差异只体现在扩展字段。4.1 论坛板块结构帖子表和回帖计数冗余论坛的最小闭环是版块、帖子、回复三张表。版块复用 category 表cat_type字段标forum帖子复用 info 表固定cat_id指向版块下专属分类。回复表单独建因为回帖信息流独立于帖子本身CREATE TABLE forum_reply ( reply_id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY, info_id INT UNSIGNED NOT NULL, uid INT UNSIGNED NOT NULL, content TEXT NOT NULL, reply_pid INT UNSIGNED NOT NULL DEFAULT 0, -- 楼中楼父回复 created_at DATETIME NOT NULL, KEY idx_info (info_id, created_at) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;热帖排序常用ORDER BY reply_count DESC配时间窗口reply_count在回复插入时用一个UPDATE info SET reply_count reply_count 1同步冗余比COUNT(*)子查询省很多回帖删除时反向减一。楼中楼用reply_pid实现注意查询时只做两层嵌套超过两层 UI 体验会很差。4.2 房产招聘二手一套公共字段还是三套独立模型58、赶集下每个类目都有专属筛选房产要有面积、户型、朝向、楼层招聘要有学历、经验、薪资区间二手要新旧成色、原价、转让价。如果每个栏目建一张独立表栏目一多库表爆炸且新增栏目必须动数据库迁移。稳妥的方案是info 表存公共字段extra_json存栏目差异字段MySQL 里的 JSON 字段在 8.0 可以直接按JSON_EXTRACT(extra_json, $.area)查询性能能接受。下面是一个房产分类的扩展字段定义字段类型筛选场景典型值areanumber面积区间筛选89.5单位㎡house_typestring户型卡片展示三室两厅orientationstring朝向筛选南北floorstring楼层高层/中层/低层或具体层数total_pricenumber总价区间268单位万unit_pricenumber列表头图展示30112单位元/㎡extra_json里没有的栏目再增量追加不用重建表。这套做法的边界是字段值无法建索引关联查询一旦需要“按面积区间 总价区间组合搜索”数据量大到单表扛不住时只能把extra_json抽列到扩展表或者同步进 Elasticsearch这也是后期最常见的架构升级路径。4.3 自定义栏目的动态表单一份 JSON 驱动发布页“自定义栏目”功能拆开看就是八个字动态表单、动态列表。后台在category.extend_schema里配置字段前端发布页按 schema 渲染控件列表页按 schema 渲染筛选器。// 后台配置的 schema存 category.extend_schema [ { field: area, label: 面积, type: number, placeholder: 单位㎡, required: true }, { field: house_type, label: 户型, type: select, options: [一室, 两室, 三室, 四室以上] }, { field: orientation, label: 朝向, type: select, options: [南北, 南, 东, 西, 北] }, { field: floor, label: 楼层, type: text } ]uniapp 里用一个转发控件component :is不够直观常见做法是写一个dynamic-form.vue把控件类型映射成 uni-app 内置组件template view v-forf in schema :keyf.field classform-item text classlabel{{ f.required ? * : }}{{ f.label }}/text picker v-iff.type select :rangef.options changeonSelectChange(f, $event) view classpicker-value{{ form[f.field] || 请选择 }}/view /picker input v-else :typef.type number ? digit : text v-modelform[f.field] :placeholderf.placeholder / /view /template script setup const props defineProps([schema, modelValue]); const form reactive({ ...props.modelValue }); function onSelectChange(f, e) { form[f.field] f.options[e.detail.value]; } /script提交时后端不要直接信任extra_json必须按category.extend_schema里的类型逐项校验number项要Number.isFiniteselect项要落在 options 内required项空值直接 400。列表页的筛选器同样由 schema 生成筛选项只对type: select和type: number生成对应组件这样新增栏目只需配 JSON前端发布页实际上永远只维护一份。4.4 内容审核小程序强制安全检测不能省小程序的体验版、审核版和正式版消息和内容类目都会被平台例行抽查。信息发布接口里至少要接一层安全过滤文本调微信security.msgSecCheck小程序端必须App/H5 可以走自建词库图片调security.imgSecCheck发布时同步检测不要异步补扫用户上传的图随时可能下架异步会漏自建词库维护一个敏感词表content.replace命中即标记待审注意接口调用顺序先同步过词库内存级别再调微信安全接口网络级别最后落库。只调微信接口不做本地拦截会出现高频词接口限流后发布接口超时的现象。5. 多端打包与上线验证App、小程序、H5 最容易翻车的四个点前几章把业务打通之后最后一道关卡是把同一个源码分别交付给三个平台。三类产物差异比较大下面四个点踩过一遍基本就齐了。5.1 定位权限三端三套配置一个页面三份代码上面条件编译里已经写了定位的获取逻辑真正的坑在配置层端配置位置关键动作H5 / 微信公众号域名 HTTPS JS-SDK 签名正式环境必须配jsapi_ticket签名本地 devtools 里需要勾选不校验合法域名微信小程序manifest.json的 mp-weixin 权限声明登录微信公众平台配置requiredPrivateInfos里的getLocationAppmanifest 的 App 模块配置 隐私政策弹窗国产安卓应用商店要求声明“获取位置”目的不弹窗可能被拒uni.getLocation在 App 端如果没在 manifest 里勾选定位模块返回的fail信息是“未配置定位权限”看起来像接口报错实际上是打包配置少了模块。5.2 支付、分享与 H5 内嵌页的差异H5 和 App 都能调起微信支付或支付宝支付但三端的支付初始化参数不同小程序支付必须拿openid生成预支付单H5 支付走JSAPI拉起的支付中间页而 App 支付用的是统一下单里的app接口三个trade_type不一样后端支付路由要按客户端端识别分发。分享海报也有版本差异H5 用 canvas 导出小程序用canvas 2d接口App 端则可能要用html2canvas再走原生分享。这部分代码不要写在同一段逻辑里拆成utils/share/{h5,mp-weixin,app}.js三个文件用条件编译引入后期维护成本最低。5.3 发布后的真机验证顺序打好包后按这个顺序过一轮微信开发者工具里跑build:mp-weixin产物检查页面路由、tabBar 图标、sitemap 配置真机预览小程序重点测定位授权、图片选择、支付拉起H5 产物放到已配好域名的服务器上微信内打开一次确认 JS-SDK 签名有效期和分享描述正常App 云打包后装测试机检查plus能力是否生效特别是相册、位置、推送最后把分类、发布、置顶、论坛、自定义栏目按“手机上完整发一条二手信息”的路径走一遍每次改动都要重新输出三端产物H5 的产物目录是dist/build/h5小程序是dist/build/mp-weixin两个目录独立发布时别传错目录。一个值得专门盯的细节是小程序端pages.json里的navigationStyle设为custom后H5 端会默认保留浏览器导航栏两边的返回逻辑不同最好在onLoad里按端判断是否隐藏uni.navigateBack的入口。跨端验证清单里把这一条放在支付之前先看导航出问题比支付出问题更让测试人员困惑。本文还有配套的精品资源点击获取