前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >APICloud AVM多端开发 | 手把手教外卖点餐App开发(上)

APICloud AVM多端开发 | 手把手教外卖点餐App开发(上)

原创
作者头像
APICloud官方
修改2021-02-06 18:37:00
2.6K0
修改2021-02-06 18:37:00
举报

为了让开发者更加快速的学习和了解APICloud多端开发技术,APICloud平台特别推出一款多端源码-《外卖点餐App开发》,可以体验一套代码编译Android和iOS app+小程序。

AVM多端框架是在兼容和继承APICloud所有API、模块、技术栈以及用户体验的基础上,定义了一套新的代码编写标准(DSL):基于标准Web Components组件化思想,兼容Vue / React语法特性,通过一次编码,分别编译为Android和iOSAPP、小程序代码,实现多端开发。

使用步骤

  1. 使用 APICloud Studio 3 作为开发工具。
  2. 下载本项目源码。
  3. 在开发工具中新建项目,并将本源码导入新建的项目中,注意更新 config.xml 中的 appid 为你项目的 appid。
  4. 使用 AppLoader 进行真机同步调试预览。
  5. 或者提交项目源码,并为当前项目云编译自定义 Loader 进行真机同步调试预览。
  6. 云编译 生成 Android & iOS App 以及微信小程序源码包。

如果之前未接触过 APICloud 开发,建议先了解一个简单项目的初始化、预览、调试和打包等操作,请参考 APICloud 多端开发快速上手教程

项目架构

项目中前端技术要点包括跨页面通信、全局购物车数据管理、自定义复用组件编写和辅助助手函数等等。 使用 APICloud 多端技术实现了一套代码,多端运行。 支持编译成 Android & iOS App 以及微信小程序。

项目后端使用的是 APICloud 数据云3.0 来构建的: 通过编写云函数自动管理维护接口和数据,详细可以参考数据云的文档。也可以自定义后端接口,通过自写服务器完成开发。


源码文件目录结构

项目源码在本仓库的 widget 目录下。其中该目录下的文件结构如下:

代码语言:javascript
复制
┌─component/                项目公共组件目录
│  ├─empty-block.shtml      空数据占位图组件
│  ├─goods-action.shtml     商品下单动作组件
│  ├─goods-counter.shtml    商品加购计数器组件
│  ├─goods-list-item.shtml  主页商品列表单品组件
│  ├─order-item.shtml       订单列表单品组件
│  ├─radio-box.shtml        自定义选择器组件
├─css/                      css样式目录
├─image/                    图片素材图标资源目录
├─pages/                    新版的AVM页面目录
│  ├─goods_add
│  │  └─goods_add.stml      加购浮层
│  ├─goods_detail
│  │  └─goods_detail.stml   商品详情页
│  ├─main_cart
│  │  └─main_cart.stml      主tab-2 购物车页面
│  ├─main_home
│  │  └─main_home.stml      主tab-0 商家主页
│  ├─main_menu
│  │  └─main_menu.stml      主tab-1 点餐菜单页面
│  ├─main_user
│  │  └─main_user.stml      主tab-3 用户主页
│  ├─pay_result
│  │  └─pay_result.stml     支付结果页
│  ├─pending_order
│  │  └─pending_order.stml  待付款结算页
├─script/                   JavaScript脚本目录
└─config.xml                应用配置文件

首页 TabBar 结构的处理

为什么需要一个app.json配置文件

《外卖点餐》项目的首页是由一个可以同级切换窗口组构成的。 在 APP 原生端 上面, 我们可以借助 FrameGroup 来实现这样的切换组。 小程序原生上则是使用 app.json 配置文件来 配置定义 TabBar 的相关属性 。 为了统一两端的差异问题,通过在 weight 根目录下定义一个 app.json 文件,具体字段说明请参考《openTabLayout布局文档》 。 所以,如果只书写原生端 APP ,而不计划支持小程序的话,这个配置文件就是可选的了。

TabBar页面的组织

在这个配置文件中,可以声明底部栏的标签文案、对应图标的选中和未选中状态以及对应需要跳转的页面路径。 所以需要准备四个主页面。 在 pages目录准备建立这四个页面。 分别是 “商家主页” main_home 、 “菜单页面” main_menu 、 “购物车页面” main_cart 和 “用户主页” main_user 。 为了兼容小程序目录结构,需要使用同名文件夹对其包裹一层。

商家主页 main_home 的编写

到主页效果图,然后大致分析一下页面结构。源代码在 /widget/pages/main_home/main_home.stml 。 页面主要部分是一个滚动效果,需要使用一个 scroll-view 来做滚动部分的容器。 头部有一个固定头部,并跟随上面提到的 scroll-view 的滚动高度来做透明度反馈。

布局结构使用系统推荐的 flex 布局。有一点需要注意的是, flex 布局的 flex-direction 默认是 column , 也就是竖着排列的方向,这一点是和传统网页中不一定地方。另外,每一个组件默认会附带 display:flex;属性。

请求接口数据 (数据处理和请求库封装)

在页面的生命周期 apiready 中,有一个 this.getData()的方法,就是在请求数据。

代码语言:javascript
复制
function getData() {
    GET('shops/getInfo')
        .then(data => {
            this.data.shopInfo = data;
        })
}

这个函数主要使用一个 GET 方法实现的。这个方法来自于:

代码语言:javascript
复制
import {GET} from "../../script/req";

这个文件中,主要处理了应用的请求、会话和异常处理等逻辑。 相关业务代码可以只是作为参考,具体项目中根据实际的会话认证方式、服务接口模式以及个人偏好等方式去组织。

拿到数据以后,通过 this.data.shopInfo = data 将数据交给到页面的数据域中,以便于接下来的数据绑定显示。

商家头图和主要信息 (数据绑定)

头部主图是不会和 scroll-view 一起滚动的,所以它应该在滚动容器的外部。使用一个 img 图片标签来显示图片。 其数据是来自服务器接口的数据, 使用 avm.js 提供的《数据绑定》 来处理数据。

代码语言:javascript
复制
<img class="shop-photo" style={{'height:'+photoRealHeight+'px'}} src={{shopInfo.img}} alt=""/>

商家的营业信息也同上,按照接口数据绑定出相应字段,即可显示出来。

代码语言:javascript
复制
<view class="shop"
      style={{'margin-top:'+photoRealHeight+'px'}}>
    <view class="shop-header flex-h">
        <text class="shop-name flex-1 ellipsis-1">{{ shopInfo.name }}</text>
        <img class="shop-phone" @click="callPhone" src="../../image/icon/icon-home-phone.png" alt=""/>
    </view>
    <view class="content-wrap">
        <text class="shop-text shop-address">
            {{ shopInfo.city }} {{ shopInfo.country }} {{ shopInfo.address }}
        </text>
    </view>
    <view class="shop-operation content-wrap">
        <text class="shop-text">营业中 09:00 - 13:00,16:00 - 22:00</text>
    </view>
</view>

拨打电话的动作 (事件绑定)

其中电话的图标点击以后,需要实现拨打电话的效果。为其绑定一个点击事件,叫做 callPhone ,并在 methods 去实现:

代码语言:javascript
复制
function callPhone() {
    if (isMP()) {
        wx.makePhoneCall({
            phoneNumber: this.data.shopInfo.phone
        })
    } else {
        api.call({
            type: 'tel_prompt',
            number: this.data.shopInfo.phone
        });
    }
}

推荐菜品和栏目 (v-for循环和组件)

仔细观察这里的模板和数据,实际上可以分解为 一个主标题 加上 一组菜品 这样的结构来循环。 其中 一组菜品 再使用循环,渲染出单品。

使用循环来展示三个分组数据。

代码语言:javascript
复制
<view class="list" v-for="item in classifyList">
    <goods-list-item class="goods-item" :list="item.togc" :title="item.name"></goods-list-item>
</view>

每一个循环中包含一个 <goods-list-item /> 组件。这个组件来自于自定义组件:

代码语言:javascript
复制
import goodsListItem from '../../components/goods-list-item.stml';

在自定义组件中,完成组件内部的组件样式、数据管理和事件响应等,符合组件化开发思想和提高项目的开发效率和维护性。 在这个组件中,同样的使用了循环来处理每个栏目的单品数据。 每个单品绑定了一个 intoGoodsDetail 事件来实现跳转到商品详情页。

代码语言:javascript
复制
function intoGoodsDetail(item) {
    api.openWin({
        name: 'goods_detail',
        url: '../../pages/goods_detail/goods_detail.stml',
        pageParam: {
            item
        }
    })
}

页面头部header

代码语言:javascript
复制
<view class="header-bar"
      style={{'opacity:'+this.data.opacity+';padding-top:'+safeAreaTop+'px'}}>
    <text class="nav-title shop-name">{{ shopInfo.name }}</text>
</view>

头部是一个普通的 view + text 的结构。为了实现滚动处理透明度,为其绑定一个动态的 style 属性。 动态改变其透明度 opacity

而这个 opacity 的取值依赖于 scroll-view 的滚动高度。 scroll-view 的滚动会触发相关数据的变动,所以为其绑定上一个滚动事件 @scroll="onScroll" 和相关处理逻辑 onScroll

代码语言:javascript
复制
function onScroll(e) {
    const y = isMP() ? e.detail.scrollTop : e.detail.y;

    let threshold = this.photoRealHeight - y;
    if (threshold < 0) {
        threshold = 0;
    }
    this.data.opacity = 1 - threshold / this.photoRealHeight;
    api.setStatusBarStyle && api.setStatusBarStyle({
        style: this.statusBarStyle
    });
}

onScroll 中能够拿到相应的滚动高度,并且计算出透明度的最终结果。 同时发现透明度的更改也会伴随着顶部状态栏文本的颜色变化。使用端能力 api.setStatusBarStyle 来进行相应设置。

如此一来,商家主页的相关逻辑的数据处理的差不多了,同时介绍了基础的事件和数据处理等。

商品详情页 (组件通信、全局数据和事件)

页面加载的时候,通过页面传参拿到商品详情数据。另外一个商品的加购数量是存在名为 CART-DATA的全局数据中,在页面生命周期函数 apiready中拿到相关数据:

代码语言:javascript
复制
this.data.goods = api.pageParam.item.togoods;  // 拿到商品主数据

let cartList = api.getPrefs({sync: true, key: 'CART-DATA'}); // 获取加购数量
if (cartList) {
    cartList = JSON.parse(cartList)
    this.data.cartData = cartList[this.data.goods.id];
    if (this.data.cartData) {
        this.data.count = this.data.cartData.count;
    }
}

计数器组件 goods_counter

商品详情页使用了两个自定义组件,一个是 goods_counter,是一个商品计数器。 以后其他页面可能也会使用到,所以将其封装起来。

代码语言:javascript
复制
<goods-counter onCountChange={this.countChange.bind(this)} :count="count"></goods-counter>

使用一个动态属性 :count="count" 将刚刚获取到的当前商品的加购数量传入。 在 goods_counter 内部,点击加减按钮触发 countChange 事件。在事件中向父页面传递:

代码语言:javascript
复制
function countChange(change) {
    if (this.props.count + change === 0) {
        return api.toast({
            msg: '不能再减少了\n可在购物车编辑模式下移除',
            location: 'middle'
        })
    }
    this.fire('CountChange', {
        change,
        props: this.props
    })
}

所以在组件调用的时候,绑定一个 onCountChange={this.countChange.bind(this)} 。 这里的 this.countChangegoods_detail 的函数,在创建组件的时候作为 props 传递到了子组件中, 在子组件中可以直接执行这个函数,或者是使用 fire 的方式“引燃”这个函数。

加购动作条 goods_action

商品详情页使用了两个自定义组件,另一个是 goods_action,是一个商品加购动作条。 主体是两个按钮,一个加购,一个结算。

结算就是携带当前单品数据到预付款页面。逻辑很简单,就是携带数据到新页面。

加购稍微复杂一点,不过逻辑依然使用 fire 的方式上抛给一个 addCart的事件到父页面,因为可能不同的页面的加购后续逻辑不太一样,具体实现就交给父级。 所以视线还是转回到 goods_detailaddCart 的实现。

代码语言:javascript
复制
function addCart() {
    let cartList = api.getPrefs({sync: true, key: 'CART-DATA'}) || '{}'
    cartList = JSON.parse(cartList)
    cartList[this.data.goods.id] = {
        goods: this.data.goods, count: this.data.count
    };
    api.setPrefs({
        key: 'CART-DATA',
        value: cartList
    });

    api.toast({
        msg: '成功加入' + this.data.count + '个到购物车', location: 'middle'
    })
    setTabBarBadge(2, Object.keys(cartList).length);
}

加购后考虑到相关购物车页面和底部小红点的数据。此时如果不考虑小程序的话,也可以直接发送全局广播,自行处理相关逻辑。

因文章太长,分两篇完成。剩下的菜单点餐页面、购物车、用户页面和付款页面功能可见APICloud AVM多端开发 | 手把手教外卖点餐App开发(下)。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 使用步骤
  • 项目架构
  • 源码文件目录结构
  • 首页 TabBar 结构的处理
    • 为什么需要一个app.json配置文件
      • TabBar页面的组织
        • 商家主页 main_home 的编写
          • 请求接口数据 (数据处理和请求库封装)
          • 商家头图和主要信息 (数据绑定)
          • 拨打电话的动作 (事件绑定)
          • 推荐菜品和栏目 (v-for循环和组件)
          • 页面头部header
          • 计数器组件 goods_counter
          • 加购动作条 goods_action
      • 商品详情页 (组件通信、全局数据和事件)
      相关产品与服务
      云开发 CloudBase
      云开发(Tencent CloudBase,TCB)是腾讯云提供的云原生一体化开发环境和工具平台,为200万+企业和开发者提供高可用、自动弹性扩缩的后端云服务,可用于云端一体化开发多种端应用(小程序、公众号、Web 应用等),避免了应用开发过程中繁琐的服务器搭建及运维,开发者可以专注于业务逻辑的实现,开发门槛更低,效率更高。
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
      http://www.vxiaotou.com