上周分享了第一部分项目源码分析使用API??CloudAVM多端开发技术开发一个《餐饮点餐》项目。现在我将完成下一部分。希望能帮助开发者快速体验一套编译Android和iOSapp+小程序的代码。本篇主要讲解菜单点餐页面的双向滚动交互,加菜的处理,购物车和支付逻辑的处理,以及用户中心的开发。查看本文时,可以先回顾一下上一篇文章:APICloudAVM多端开发案例深度解析(上)--订货app开发=================================================================================一个菜单点餐页面分类和菜品双向滚动交互本页面为左右栏布局。左边是菜单类别,右边是菜肴。有一组比较常见的交互:向右滑动菜单,左侧的分类高亮会随之变化。点击左侧菜品类别,右侧菜品会回滚到对应区域。第一个交互相关的逻辑类似于开发者主页滚动scroll-view时触发header透明的逻辑。所以也将@scroll="onScroll"函数绑定到右侧的滚动视图。具体逻辑可以参考源码的实现部分。获取滚动高度与首页类似。关注二次交互的核心是点击对应的分类,右侧的scroll-view需要滚动到指定位置。使用属性进行位置绑定:scroll-top={scrollTo}。此时只需要在左侧@click="switchCategory(index)"的类别点击事件中计算出正确的scrollTo即可。函数switchCategory(index){this.data.categoryIndex=index;this.data.CD=newDate().getTime()+500;//手动切换类别后,需要加锁500毫秒,避免右边scroll-view滚动次要问题来了this.data.scrollTo=this.offsetList[index];}菜品及附加处理(交叉-终端特征处理)右边的菜品有一个@click="openAdd(goods)"事件,用于打开插件购买页面。functionopenAdd(goods){if(isMP()){this.data.currentGoods=goods;wx.hideTabBar();}else{api.openFrame({name:'goods_add',url:'../goods_add/goods_add.stml',pageParam:{goods}})}}这个函数展示了终端差异的处理。由于小程序没有类似于APICloud的框架概念,新弹出的页面是通过页面内部组件在小程序上实现的。当然APP原生端也支持这种方式。如果需要进一步提升性能,利用原生优势,可以使用原生端的框架来实现。此时,将目标页面封装在自定义组件中,将当前菜品数据传入。目前组件和框架页面的参数获取形式暂时不同。在goods_add组件的安装生命周期中,可以看到如下兼容片段:this.data.goods=this.props.goods?this.props.goods:api.pageParam.goods;在新扩展的add-on浮层上面,看到了之前定义的goods_action,所以大致的逻辑就是获取商品数据和追加购买,实现addCart函数。其实这个页面和商品详情页很像,只是展示的UI不太一样。沉浸式状态栏safe-area在这个页面中,我自己实现了一个顶部导航栏。沉浸式状态栏一般需要获取状态栏高度等处理能力。avm.js中提供了safe-area组件,自动处理异形屏的边界问题。Menu在首页也可以看到相关的程序化方法获取安全区数据代码:this.data.safeAreaTop=api.safeArea?api.safeArea.top:0;第二个购物车页面computed计算和条件渲染的v-if购物车页面是展示相关页面内部逻辑的经典案例。页面初始化时,this.getCartData()获取本地存储的购物车的所有数据。functiongetCartData(){letcartData=api.getPrefs({sync:true,key:'CART-DATA'});如果(cartData){car??tData=JSON.parse(cartData);this.data.cartData=cartData;这个.generateCartList();setTabBarBadge(2,Object.keys(cartData).length);}}里面还混杂了一个generateCartList的逻辑。函数generateCartList(){让cartData=this.data.cartData;让arr=[];for(letiincartData){arr.push({checked:true,...cartData[i]});}这。data.cartList=arr;}这是一个生成器函数,将保存的对象构造成页面需要的数组结构,同时增加每个元素的checked属性。然后在页面通过v-for循环遍历当前购物车的数据。{{item.goods.name}}<视图类="main-cart-flex-h">¥{{item.goods.curt_price}}<商品计数器onCountChange={this.countChange.bind(this)}:count="item.count":item="item">注意每一个条目的开头套都有一个自定义组件。这个组件的任务很简单,就是渲染一个自定义样式的单选按钮。当然,avm.js自带的系统组件radio也是可以实现的。computed的use下方有一个selectall按钮,用来控制是否全选。函数checkAll(){constchecked=!this.allChecked;for(leti=0;i{//也可以用every修饰相反的逻辑来实现return!item.checked;})}followedby下面还有一个计算属性:totalPrice:functiontotalPrice(){//先过滤掉选中的itemletlist=this.data.cartList.filter(item=>{returnitem.checked;})//Then计算总数并格式化结果return(list.length?list.reduce((total,item)=>{returntotal+item.goods.curt_price*item.count;},0):0).toFixed(2);}然后直接在模板中使用这个结果完成总价的显示:Total¥{{totalPrice}}可以看到计算属性computed可以通过一些逻辑计算出需要的结果,并且会暴露给实例本身,在模板中可以像数据一样绑定。同时,它可以自动处理它所依赖的数据变化,并进行实时更新。页面中渲染v-if条件,有一个变量标签isEdit,用来表示当前页面是否处于编辑状态。完成Edit根据编辑状态的切换,右上角的按钮复制变为“完成”和“编辑”两种状态。这时候可以通过v-if来判断渲染。下面的结算和移除按钮是一样的,只是在模板中使用了一个三元表达式来显示。{{isEdit?'Remove':'gotocheckout'}}三个用户页面这个页面有两个要点:标题用户信息区域和订单列表。HeaderUserInformationHeader用户信息初始化时需要读取本地用户数据。/***获取用户信息*@returns{boolean|any}*/functiongetUser(){letuser=api.getPrefs({sync:true,key:'USER'});如果(用户){返回JSON。parse(user)}returnfalse;}将获取到的用户数据作为普通页面数据渲染用户信息面板。如果用户数据不存在,即非登录模式,需要使用v-if条件渲染来显示登录界面。{{用户信息。昵称}}使用微信登录登录逻辑在没有登录的情况下,会显示上面的第二块,点击触发wxLogin方法:functionwxLogin(){if(isMP()){this.mpLogin();}else{this.doLogin({ssid:getDeviceId()});}}这里还是需要区分一下特性平台。因为在原生端使用微信登录和小程序端是两种不同的逻辑。源码/widget/pages/main_user/main_user.stml也展示了一些使用原生模块调用微信登录的逻辑,登录成功后开始执行loginSuccess,可以保存相关的用户信息和session信息供未来使用。它还需要刷新用户的购物清单。如果真实项目中其他打开的页面也需要监听用户状态变化,可以使用广播事件来处理详细逻辑。functionloginSuccess(userInfo){api.setPrefs({key:'USER',value:userInfo});this.data.userInfo=userInfo;this.getOrderList();}页面下拉刷新页面下拉刷新和底部加载依赖scroll-view相关事件的绑定和实现。哪里@refrescherrefresh="onRefresh"是下拉刷新需要触发的逻辑。refresher-triggered={{loading}}是下拉刷新的状态。(用于通知反弹和设置刷新)。函数onRefresh(){this.data.loading=true;//设置刷新if(this.data.userInfo){//当有用户信息时刷新this.getOrderList();}else{setTimeout(_=>{this.data.loading=false;api.toast({msg:'请登录查看历史订单'})},1000)}}首页开发大致完成,让我们关注下订单的过程。四个pendingpayment页面(formdata)这个页面也比较简单,大部分实现的逻辑在前面的页面中已经讲过了。此外,还有一个输入框表单来收集用户输入的笔记。备注通过失去焦点事件onBlur="onBlur"动态获取数据。functiononBlur(e){this.data.remark=e.target.value;}还有很多其他方法可以获取数据。您可以参考组件输入和其他表单组件文档以进一步参考。开始提交订单,与服务器通信下单付款。下单后做一些联动处理:functionaddOrder(){POST('orders/app_addorder',this.formData).then(data=>{//打开结果页面api.openWin({name:'pay_result',url:'../pay_result/pay_result.stml'});//通知支付成功刷新订单页面api.sendEvent({name:'PAY-SUCCESS'})//清空购物车api.setPrefs({key:'CART-DATA',value:{}});setTabBarBadge(2,0);})}跳转到支付成功页面订单支付后跳转到支付结果页面。(这个过程是模拟下单成功,中间嵌套第三方支付可以参考微信登录过程。)至此,页面的逻辑主线全部完成。应用中还有一些细节,大家可以参考源码和文档进一步学习和研究。如果想快速入门APICloudAVM多端开发,可以查看快速入门教程:https://docs.apicloud.com/api...