本地团购类小程序(步骤一:前端开发)
收藏
我的收藏准备工作
说明
本文默认你已经对字节小程序有所了解,且已经拥有了自己的小程序。
抖音开放平台在小程序与本地生活服务的开放能力基础上,为小程序生活服务代运营服务商和自营商家分别量身定制了一套有针对性的接入方案。在正式进入本系列教程前,请参考文档餐饮到店团购解决方案,按照自己所在的行业和角色,完成前置准备。
本系列教程,将分为 3 个步骤,分别指导开发者进行小程序开发、商品库接入、交易系统接入,从而实现从 0 到 1 入驻抖音开放平台“本地生活”类目小程序并开始进行运营。
Demo 安装及使用
本 Demo 提供了以下几个主要页面:
本 Demo 是一个小程序前端工程,帮助开发者完成团购行业小程序基础页面建设工作,开发者可在 IDE 中「项目模板」选择该项目模板后直接导入,具体操作如图所示。
开发者也可以新建一个空白工程,按照教程的指导进行开发。
说明
开发者需替换为自己的 AppId 使用,并在替换前该 AppId 已完成前置准备。
数据准备
由于本示例为一个纯前端小程序,因此数据均为前端构造,后续可替换为服务端获取。
一个商品最基本的数据信息如下:
// 基础商品信息示例: // 注:文档中的数据并非真实,均为测试数据 { "id": "3871280801208409", //商品id,正式环境中该字段需要在商品入库后,由拌音服务端返回获取 "name": "【仅作展示】188元享二人极致套餐", //商品名称 "description": "酒香就怕巷子深,吃一口念念不忘", //商品描述 "image": "../../assets/product-square.png", //商品图片 "subtitle": "酒香就怕巷子深,吃一口念念不忘", //商品副标题 "notice": "购买前请仔细阅读须知。", //商品服务须知 "package_details": [ // 套餐详情 { "category": "开胃小菜(三选一)", //套餐详情中的分类名称 "options": [ //category对应的详细内容 { "name": "菜品1", //菜品名称 "price": 45, //菜品价格 "description": "(一份)" //菜品份数 }, { "name": "菜品2", "price": 45, "description": "(一份)" }, { "name": "菜品3", "price": 45, "description": "(一份)" } ] }, ], "graphic_details": [ // 图文详情 { "image": "../../assets/product-square2.png",//图文详情中需要展示的商品图片 "description": "这是商品1的图文详情1。"//图片对应描述 }, ], "rating": 4.5, //商品评分 "sales": 1000, //商品销量 "original_price": 109.99, //商品原价 "current_price": 188, //商品现价 "marketing": 9.1 //折扣 },
模板小程序中全部为模拟数据,如果要使用在生产环境中,请将数据交互替换为真实接口请求。
具体示例请参考 data.js 和 common.js 中被注释的代码:
为了更方便地使用 tt.request,我们将其封装了一个 request 方法:
//request.js 此demo为简易封装,开发者可根据自己的业务场景进行修改 export const request = (options) => { options.method && (options.method = options.method.toUpperCase()); return new Promise((resolve, reject) => { tt.request({ url: options.url, method: options.method || "GET", data: options.data || {}, header: options.data || { "content-type": "application/x-www-form-urlencoded", }, success: (res) => { resolve(res.data); }, fail: (err) => { reject(err); }, }); }); };
标准页面布局
本模板小程序提供了以下几个主要页面:
首页 | 分类页 | 订单列表页 | 商品详情页(重点) | 小程序登录页 |
地址组件 | 商品分类 tab-bar | 订单分类 tab-bar | 商品轮播图 | 手机号获取按钮 |
轮播图&金刚位 | 商品 Feed 流 | 商品 Feed 流 | 商品主要内容展示 | 隐私保护协议 |
商品 Feed 流 | -- | -- | 推荐套餐 | -- |
-- | -- | -- | 底部操作区域
| -- |
其中 3 提单页、5 订单详情页、6 申请退款页、8 抖音登录页面为原生页面,不需要开发者进行开发且不支持自定义更改。
模板小程序为开发者提供了首页、分类页、订单列表页、登录页以及交易系统提供的插件页面,如需对页面内容做更改可根据目录指引操作。
注意
其中交易插件页面内容不支持自定义更改。
核心代码示例:
//app.json ext://microapp-trade-plugin为插件页面不支持自定义更改 "pages": [ "pages/index/index", // 首页 "pages/category/category", //分类页 "pages/order-list/order-list", //订单列表页 "pages/custom-login/custom-login", // 登录页面 "pages/custom-login/login/login", // 登录页面 "pages/product-detail/product-detail" , //商品详情页 "ext://microapp-trade-plugin/order-confirm", "ext://microapp-trade-plugin/refund-apply", "ext://microapp-trade-plugin/refund-detail", "ext://microapp-trade-plugin/trade-order-detail" ],
//app.js 此方法为提单页中获取手机号必传方法,需开发者根据注释内容自行完善。 getPhoneNumber({ params, success, fail }) { const { iv, encryptedData } = params; // ... // 开发者服务端解密 encryptedData,得到手机号 // 详情可参考Codelabs文章:《30分钟搞定小程序登录》 // ... const result = { phoneNumber: 'xxxxxxxxxx', } // 回调前端模板 success(result) },
自定义页面内容
地理位置获取
城市选择组件
效果如图所示:
城市选择组件目前为模拟数据,该部分需开发者自行完善。主要逻辑分为省市数据加载和城市选择。开发者如有需要可修改 ttml 中 picker-view-column 数量即可完成增加区域选项。主要代码示例如下:
<!-- index.ttml --> <picker-view indicator-style="height:88rpx" style="height: 600rpx" value="{{value}}" bindchange="cityChange" > <picker-view-column style="text-align: center"> <view tt:for="{{provinces}}" class="picker-text">{{item.name}}</view> </picker-view-column> <picker-view-column style="text-align: center"> <view tt:for="{{cities}}" class="picker-text">{{item}}</view> </picker-view-column> </picker-view>
// index.js async cityChange(event) { const { value } = this.data; const [provinceIndex,cityIndex] = event.detail.value; const oldValue = value; console.log(provinceIndex); if (oldValue[0] != provinceIndex) { console.log("省份改变"); await this.loadCities(provinceIndex); const {cities} = this.data; this.setData({ value: [provinceIndex, 0], provinceIndex, cityName:cities[cityIndex].split('市')[0], }); } else if (oldValue[1] != cityIndex) { const {cities} = this.data; console.log("城市改变",provinceIndex); this.setData({ cityName:cities[cityIndex].split('市')[0], value: [provinceIndex, cityIndex], }) } }
登录弹窗组件效果如图所示:
开发者根据的业务逻辑使用登录弹窗组件,该 demo 为在立即抢购前进行登录校验以及授权手机号。
<!-- index.ttml --> <login-dialog tt:if="{{showDialog}}" showDialog="{{showDialog}}" bind:bindPhone="onTapGetPhone" bind:success="success" bind:close="closeDialog" > </login-dialog>
//index.js success(e){ console.log(e.detail); // 开发者可在此处进行手机号解密以及绑定工作 this.setData({ showDialog:false, }) }
商品卡片组件
商品卡片组件目前为模拟数据,该部分需开发者自行完善。
商品大卡片组件
商品大卡的核心逻辑如下:
<!-- index.ttml --> <product-card-big id="card" productList="{{distanceSortList}}" />
//index.js properties: { productList:{ type:Array, } }, methods: { toProductDetail(e){ const { id } = e.currentTarget.dataset; tt.navigateTo({ url: `/pages/product-detail/product-detail?productId=${id}`, success: (res) => { }, fail: (res) => { console.log(res); }, }); } }
商品小卡片组件
商品小卡的使用场景分为分类页和商详页,开发者可根据 btnShow 属性去设置“马上抢”按钮的展示与隐藏,商品小卡的核心逻辑如下:
<!-- index.ttml --> <product-card-small id="card" class="product-item" productList="{{shopList}}" />
// index.js properties: { productList:{ type:Array, value:[] }, btnShow:{ type:Boolean, value:true } }, methods: { toProductDetail(e){ const { id } = e.currentTarget.dataset; tt.navigateTo({ url: `/pages/product-detail/product-detail?productId=${id}`, success: (res) => { }, fail: (res) => { console.log(res); }, }); } }
交易组件 pay-button 使用
本地生活类目小程序强制要求接入“交易系统”,不能使用“担保交易”。因此在前端需要使用作为提单入口。
核心代码示例:
<!-- index.ttml --> <pay-button id="pay-button" class="pay-button" mode="{{2}}" goods-id="1234" goods-type="{{2}}" bind:error="handleError" bind:pay="handlePay" bind:getgoodsinfo="getGoodsInfo" bind:placeorder="userLogin" > </pay-button>
//index.js // bind:getgoodsinfo 使用示例 // 非商品库商品 getGoodsInfo(event) { // const { // goodsId // } = event.detail; return new Promise(resolve => { // 在此处开发者可以进行商品数据请求,获取商品信息 // 然后将商品信息传入 resolve 函数 resolve({ currentPrice: 9900, goodsName: '循礼门M+丨【释集烤肉】99元 原价206.4元超值套餐', goodsPhoto: 'https://p11.douyinpic.com/img/aweme-poi/product/spu/c050f399ac447daf2715e11e6976c2e2~noop.jpeg?from=3303174740', goodsLabels: [{ type: 'EXPIRED_RETURNS' }, // 过期退 { type: 'REFUND_ANYTIME' }, // 随时退 { type: 'BOOK_IN_ADVANCE', value: 2 } // 提前2日预约 ], minLimits: 1, maxLimits: 2, dateRule: '周一至周日可用', validation: { phoneNumber: { required: true // 手机号是否必填, 为 ture则必填,false选填,默认选填 } }, extra: {} }); }); }, // 错误信息含义见下文 bind:error报错信息 handleError(event) { const { errMsg, errNo } = event.detail; console.log(errNo, errMsg); }, /** * status: 支付状态,'success' | 'fail' * orderId: 抖音交易系统内部订单号,类型为 string * outOrderNo:开发者系统交易订单号,类型为 string * result: 创建订单、tt.pay 支付结果,类型为 object */ handlePay(event) { const { status, orderId, outOrderNo, result } = event.detail; if (status === 'success') { const { code } = result; if (code === 0) { // 支付成功 } else { // 支付失败(超时、取消、关闭) if (orderId && outOrderNo) { tt.navigateTo({ url:`ext://microapp-trade-plugin/trade-order-detail?orderId=${orderId}`, success: (res) => { }, fail: (res) => { console.log(res); }, }); } } } else { const { errMsg } = result; } }, userLogin(event) { let that = this; const { goodsId, goodsType } = event.detail return new Promise((resolve, reject) => { tt.login({ success() { resolve(); // 用户登录成功并获取信息,则调用 resolve 函数,跳转至提单页 }, fail() { // 用户登录失败,则跳转提单页失败 reject(); } }); }); }, // // 商品库商品 // getGoodsInfo(event) { // return new Promise(resolve => { // // 在此处开发者可以进行商品数据请求,获取商品信息 // // 然后将商品信息传入 resolve 函数 // resolve({ // minLimits: 1, // maxLimits: 2, // dateRule: '周一至周日可用', // validation: { // phoneNumber: { // required: true // 手机号是否必填 // } // } // }); // }) // }
总结
恭喜,你已经完成了本地团购类小程序(步骤一:前端开发)。
你在该 Codelab 中了解了:
- •如何使用 IDE 模板
- •如何替换模板内容
- •交易系统中前端组件的使用