此项目为拼团商城类型,主要功能包括商品分类、商品详情、商品搜索、拼团、订单管理等。

项目源码在 https://github.com/apicloudcom/group-ec 仓库的 widget 目录下。

项目中前端采用 avm 多端开发技术进行开发,要点包括 TabLayout 布局、swiper 轮播图、rich-text 富文本、scroll-view 滚动视图、下拉刷新、组件封装等。使用 APICloud 多端技术进行开发,实现一套代码多端运行,支持编译成 Android & iOS App 以及微信小程序。

项目后端则是使用的 APICloud 数据云 3.0 自定义云函数来构建的。

使用步骤

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

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

网络请求接口封装

在 script/util.js 中,封装了统一的网络请求接口 ajax 方法,对整个项目的请求进行统一管理,包括处理传入参数、拼装完整请求 url、设置请求头等,最后调用 api.ajax 方法发起请求,在请求的回调方法里面还对 cookie 是否过期做了全局判断,过期后会清除本地用户登录信息,并提示重新登录。

// util.js
ajax(p, callback) {
var param = p;
if (!param.headers) {
param.headers = {};
}
param.headers['X-AppToken'] = UserCenter.getAccessToken();
if (param.data && param.data.body) {
param.headers['Content-Type'] = 'application/json; charset=utf-8';
}
if (param.url) {
var baseUrl = 'https://a6047344573226-dev.apicloud-saas.com/api/';
param.url = baseUrl + param.url;
}
api.ajax(param, (res, err)=> {
let ret = res;
if (err && err.body && err.body.errCode) {
ret = err.body;
callback(ret);
} else {
callback(ret, err);
}
let sessionExpiration = false;
if (ret && ret.errCode && ret.errCode>100) {
sessionExpiration = true;
}
if (sessionExpiration) {
var didShowLogoutAlert = api.getGlobalData({
key: 'didShowLogoutAlert'
});
if (!didShowLogoutAlert) {
api.setGlobalData({
key: 'didShowLogoutAlert',
value: true
}); UserCenter.setUserInfo('');
api.confirm({
msg: '登录已失效,请重新登录',
buttons: ['取消', '重新登录']
}, (ret)=> {
api.setGlobalData({
key: 'didShowLogoutAlert',
value: false
});
if (ret.buttonIndex == 2) {
this.goLogin();
}
});
}
}
});
}

  

用户登录信息管理

在 script/user.js 中,对用户登录信息进行了封装,做了统一管理,可以方便地判断是否登录、保存和获取用户信息、以及判断登录是否过期的 accessToken 等。

const UserCenter = {
isLogin(){
let access_token = this.getAccessToken();
return access_token?true:false;
},
setUserInfo(userInfo){
delete userInfo.addtime;
api.setPrefs({
key: 'userInfo',
value: userInfo
});
api.setPrefs({
key: 'access_token',
value: userInfo.access_token?userInfo.access_token:''
});
},
getUserInfo(){
let userInfo = api.getPrefs({
sync: true,
key: 'userInfo'
});
return userInfo?JSON.parse(userInfo):'';
},
getAccessToken(){
return api.getPrefs({
sync: true,
key: 'access_token'
});
}
}; export default UserCenter;

  

TabBar 和导航栏的实现

首页使用了 TabLayout 布局来实现 TabBar 和导航栏,在 config.xml 里面配置 content 字段,值为 json 文件路径,在 json 文件中配置 TabBar、导航栏和页面信息。

// config.xml
<content src="config.json" />

config.json 文件内容如下,设置了 navigationBar 的背景色和标题文字颜色,设置了 tabBar 每项的 icon 和文字,以及每项对应的页面。

{
"name": "root",
"hideNavigationBar": false,
"navigationBar": {
"background": "#fff",
"color": "#000",
"shadow": "#f1f1f1",
"hideBackButton": true
},
"tabBar": {
"scrollEnabled": false,
"background": "#fff",
"shadow": "#f1f1f1",
"color": "#aaa",
"selectedColor": "#339DFF",
"preload": 0,
"frames": [{
"name": "page1",
"url": "pages/main1/main1.stml",
"title": "拼团商城"
}, {
"name": "page2",
"url": "pages/main2/main2.stml",
"title": "分类"
}, {
"name": "page4",
"url": "pages/main4/main4.stml",
"title": "我的"
}],
"list": [{
"text": "首页",
"iconPath": "images/common/main1_1.png",
"selectedIconPath": "images/common/main1.png"
}, {
"text": "分类",
"iconPath": "images/common/main2_1.png",
"selectedIconPath": "images/common/main2.png"
}, {
"text": "我的",
"iconPath": "images/common/main4_1.png",
"selectedIconPath": "images/common/main4.png"
}]
}
}

这里”我的“页面隐藏了导航栏,而其它页面没有隐藏。”我的“页面路径为 pages/main4/main4.stml,我们参照微信小程序的语法,在同目录下放置了 main4.json 文件,在里面配置 navigationStyle 字段为 custom。

{
"navigationStyle":"custom"
}

在首页 main1.stml 的 apiready 方法里面则监听了 tabBar 每项的点击事件,在 App 端,我们需要在点击事件里面动态设置页面显示、隐藏导航栏。

// index.stml
api.addEventListener({
name:'tabitembtn'
}, function(ret){
var hideNavigationBar = ret.index == 2;
api.setTabLayoutAttr({
hideNavigationBar: hideNavigationBar,
animated: false
});
api.setTabBarAttr({
index: ret.index
});
});

轮播图实现

首页和商品详情页面都使用了轮播图,这里以首页为例,首页路径为 pages/main1/main1.stml,里面轮播图使用 swiper 组件实现,使用 v-for 指令循环 swiper-item,bannersList 为定义的数组类型的属性。这里监听了图片的 click 事件,点击后需要跳转到对应的详情页面。这里使用了自定义的指示器,通过设置指示器容器的 position 定位属性为 absolute,来让指示器显示到当前轮播图的上面。

<view class="banner_box" style={'height:'+swiperHeight+'px;'}>
<swiper class="banner_swiper" circular autoplay11 onchange="fnSwiperChange">
<swiper-item v-for="(item_, index_) in bannerList">
<image class="banner_img" src={item_.icon} mode="aspectFill" onclick="fnBannerPage" data-index={index_}></image>
</swiper-item>
</swiper>
<view class="banner_dots">
<view v-for="(item, index) in bannerList" class={current == index ? 'banner_dot-on' : 'banner_dot'}></view>
</view>
</view>

为保持不同分辨率设备上面图片显示比例不变,需要让轮播图的宽度跟随屏幕宽度变化,高度则通过计算属性 swiperHeight 来动态计算得到。

computed:{
swiperHeight(){
return Math.floor((api.winWidth - 30)*0.4+20);
}
}

rich-text 富文本的使用

在商品详情页中,商品详情部分就是使用的 rich-text 来展示的,使用时如果没为 rich-text 设置高度,其高度就为里面内容的高度。

<rich-text nodes={html}></rich-text>

rich-text 用于展示 HTML String 片段,在从服务器获取到 HTML String 后,我们调用 $util.fitRichText 方法处理了一下 HTML String,在 fitRichText 方法中为 img 标签加了最大宽度的限制,以防止图片宽度过大导致显示溢出。

// util.js
fitRichText(richtext, width){
var str = `<img style="max-width:${width}px;"`;
var result = richtext.replace(/\<img/gi, str);
return result;
}

下拉刷新、滚动到底部加载更多

在”分类商品列表“页面(pages/goodslist/goodslist.stml),通过 scroll-view 实现了商品列表展示,同时实现了下拉刷新、滚动到底部加载更多功能。

<scroll-view class="scroll-view" scroll-y enable-back-to-top refresher-enabled refresher-triggered={refresherTriggered} onrefresherrefresh="onrefresherrefresh" onscrolltolower="onscrolltolower">
<list-item v-for="(item) in goodsList" item={item} showOriginalPrice onitemClick="fnOpenDetails"></list-item>
<no-data v-if={showNoData} image="../../images/common/nolist.png" desc="暂无商品"></no-data>
</scroll-view>

下拉刷新使用了 scroll-view 默认的下拉刷新样式,使用 refresher-enabled 字段来开启下拉刷新,为 refresher-triggered 字段绑定了 refresherTriggered 属性来控制下拉刷新状态,需要注意的是,在刷新的事件回调方法里面,我们需要主动设置 refresherTriggered 的值为 true,在数据加载完成后再设置为 false,这样绑定的值有变化,刷新状态才能通知到原生里面。

onrefresherrefresh(){
this.data.refresherTriggered = true;
this.getData(false);
}

滚动到底部监听了 scroll-view 的 scrolltolower 事件,在滚动到底部后自动加载更多数据,加载更多和下拉刷新都是调用 loadData 方法请求数据,通过 loadMore 参数来进行区分,做分页请求处理。

getData(loadMore){
let that = this;
if (!loadMore) {
that.data.page = 1;
}
this.data.loading = true;
var url = "homes/getGoodsList?classid=" + that.data.classId + "&page=" + that.data.page;
$util.ajax({
url: url
}, function(res, err){
if (res && res.errcode == 0) {
let list = res.data;
that.data.haveMore = list.length > 0;
if (loadMore) {
that.data.goodsList = that.data.goodsList.concat(list);
} else {
that.data.goodsList = list;
}
if (list.length > 0) {
that.data.page += 1;
}
}
that.data.loading = false;
that.data.refresherTriggered = false;
that.data.showNoData = that.data.goodsList.length == 0;
$util.hideProgress();
});
}

自定义三级联动城市选择器组件

在填写收货地址页面(pages/address_edit/address_edit.stml)里面有一需求,为选择收货地址城市区域,为此我们在 picker 组件的基础上封装了一个 region-picker 组件(components/region-picker.stml),使用时监听该组件的 change 事件,就可以获取到选择的城市区域的名称和城市代码。

// address_edit.stml

<region-picker region={qustr||''} onchange="fnChooseStr"></region-picker>

fnChooseStr(e){
let code = e.detail.code;
let val = e.detail.value;
this.data.quid = code.join(",");
this.data.qustr = val.join(","); }

平台差异化处理

在多端开发中,难免会遇到不同平台差异化的地方,需要在运行期间做判断处理,为此在 utils/util.js 中封装了 isApp、isMp 方法,里面通过 api.platform 属性判断当前运行环境是 App 端还是小程序端。

// util.js
isApp(){
if (api.platform && api.platform == 'app') {
return true;
}
return false;
},
isMp(){
if (api.platform && api.platform == 'mp') {
return true;
}
return false;
}

  

 

 

 

跨端开发技术 | 拼团商城项目同时开发app和小程序的要点的更多相关文章

  1. 食品生鲜调料代理分销拼团商城微信小程序

    食品生鲜调料代理分销拼团商城微信小程序 现在小程序越来越火爆了,一种新的分销拼团模式出现了.一起来分享一下吧 调料商城是一家是专业从事各种调料生产和网上调料商品销售平台,是藤椒油.花椒油.香油.火锅油 ...

  2. 基于ABP开发框架的技术点分析和项目快速开发实现

    在我们开发各种项目应用的时候,往往都是基于一定框架进行,同时配合专用的代码生成工具,都是为了快速按照固定模式开发项目,事半功倍,本篇随笔对基于ABP开发框架的技术点进行分析和ABP框架项目快速开发实现 ...

  3. 【推荐】开源项目minapp-重新定义微信小程序的开发

    minapp 重新定义微信小程序的开发 官网:https://qiu8310.github.io/minapp/ 作者:Mora minapp 重新定义微信小程序的开发 使用 用 npm 安装命令行工 ...

  4. 「1.0」一个人开发一个App,小程序从0到1,起航了

    古有,秦.齐.楚.赵.魏.韩.燕七国争雄:今有,微信.QQ.百度.支付宝.钉钉.头条.抖音七台争霸.古有,白起.李牧.王翦.孙膑.庞涓.赵奢.廉颇驰骋疆场:今有程序员1,程序员2,程序员3…编写代码. ...

  5. 分享一份软件测试项目实战(web+app+h5+小程序)

    大家好,我是谭叔. 本次,谭叔再度出马,给大家找了一个非常适合练手的软件测试项目,此项目涵盖web端.app端.h5端.小程序端,可以说非常之全面. 缘起 在这之前,谭叔已经推出了九套实战教程. 但是 ...

  6. Java Web项目,Android和微信小程序的初始页面配置

    Java Web项目 我们在Eclipse里开了Java Web项目之后,Run As Tomcat或者Apache服务器,本地运行,如果直接用http://localhost:8080访问项目,会发 ...

  7. 微信小程序开发入门教程(四)---自己动手做个小程序

    前面已将基础知识准备的差不多了,下面实际做一个小程序. 一.目标 用于上传照片和文字. 2个主要页面:我me,设置set 二.开始制作 1.打开微信开发者工具(我用的1.02.1907160 Wind ...

  8. 「2.0」一个人开发一个App,小程序从0到1,文件剖析

    不知你是不是见到“文件剖析”这4个大字,才点进来看一看的?如果真是的话,那我可以坦诚.真心.负责任地告诉你:你上当了,你上了贼船啦,如果你现在想跳的话,还来得及,反正茫茫大海中,鲨鱼正缺搞程序的人.说 ...

  9. [3.0] 一个人开发一个App,小程序从0到1,删减添加

    在这个黄道吉日,咱们将要干一件,惊天地泣鬼神,妇孺皆知的大事,那就是删掉微信开发工具自动生成的源代码. 删掉pages下的index.logs目录,啥都不留: 删掉utils下的util.js,只流空 ...

随机推荐

  1. Linux 驱动框架---模块参数

    Linux 模块的参数 通过在内核模块中定义模块参数从而可以在安装模块时通过insmod module_name paramname=param形式给模块传递参数.如果安装模块是传参数则将使用模块内定 ...

  2. element-ui & babel-plugin-component config bug

    element-ui & babel-plugin-component config bug vue-cli bad babel.config.js module.exports = { pr ...

  3. React Hooks vs React Class vs React Function All In One

    React Hooks vs React Class vs React Function All In One React Component Types React Hooks Component ...

  4. Dart & data type(static / dynamic)

    Dart & data type(static / dynamic) Darts 飞镖 标枪 javelin/darts type https://dartpad.dartlang.org/ ...

  5. HTML5 stream video player

    HTML5 stream video player Aliplayer https://player.alicdn.com/aliplayer/index.html https://help.aliy ...

  6. Flutter FractionallySizedBox 设置维度比例 而不是固定的px

    本周小部件 有时您的设计需要相对的维度. FractionallySizedBox允许您将子项的大小调整为总可用空间的一小部分. Scaffold( body: Center( child: Frac ...

  7. ROS1与ROS2对比简述

    资料参考: https://blog.csdn.net/Fourier_Legend/article/details/106319000

  8. HTTP 1.x 学习笔记 —— Web 性能权威指南

    HTTP 1.0的优化策略非常简单,就一句话:升级到HTTP 1.1.完了! 改进HTTP的性能是HTTP 1.1工作组的一个重要目标,后来这个版本也引入了大量增强性能的重要特性,其中一些大家比较熟知 ...

  9. Python3+PYQT5 实现并打包exe小工具(2)

    前言:前篇已经通过python代码实现了逻辑,传送门:https://www.cnblogs.com/jc-home/p/14447850.html 现在后篇记录的是打包成exe的方式给项目其他同事使 ...

  10. Java文件字节流

    //输出和输入流 package com.kangkang.IO; import com.sun.xml.internal.ws.util.xml.CDATA; import java.io.File ...