元旦三天假期,实现一个电商退单管理系统【四】-手机客户端实现
需求变更
拆包出现异常,需要留下照片凭证,以防后期抵赖。这当然属于手机端功能,于是强烈向电商老板推荐手机客户端。
另外近期出现了一个奇怪的bug,经常扫码时重复出现同一个编码,明明已经扫了别的码了,系统里接收一的还是前一个。非常难复现,初步怀疑是扫码枪的缓存功能造成的,但没有办法根治,造成仓库抱怨。于是将扫码入库功能也加到手机客户端上,并控制该功能只允许在仓库使用(GPS定位,划定区域内使用,有点电子围栏的意思)。
开发过程中,封装了几个组件,版本升级已放到uni的插件市场上,我写了一个非常详细的使用方法,欢迎大家免费下载,顺手打个星。
https://ext.dcloud.net.cn/plugin?id=3931
主要功能预览
1. 登录
在线升级支持多种皮肤定制,支持强制升级和非强制升级,支持下载进度并中途中断升级,支持ios下载升级(企业证书)和appstore升级。图片截的有点高高低低的,凑合看吧。
2. 拆包检验
默认出现列表供拆包,也可以快速扫条形码进入检验。列表上拉翻页下拉刷新用的是MescrollBody,比uni自带的那个好用且体验好很多。
多照片布局直接用的colorui。最多支持9张照片上传。
3. 扫码入库
界面应该是首页,后面应该会做成首页。
业务逻辑跟电脑客户端一致,先选择快递公司,然后扫码入库,不同的是,手机端不会暂存,直接调单个入库接口上传服务器。
4. 照片补录
为保证补录的是当前快递的照片,不允许列表选择和手动输入单号,只能扫码拍照,最多支持9张照片。
5. 关于
现在界面上还叫“我的”,应该叫“关于”更合适点。功能比较少。我从colorui里把wave.gif扒出来了,顶部图片显示波纹效果,看着就很舒服了,我特意截了一个 gif
部分代码
1. 封装一个httpclient用于请求网络。
由于每次请求都有签名验证、时间戳等参数一同上传,就封了一个js文件。多图片上传,本来也准备封到这个js里,结果服务端只能收到一张,h5端正常,已向dcloud报了bug,有了解的同学也请帮忙回复下:https://ask.dcloud.net.cn/question/115109
function request(url, data = {}, type = 'GET', header = { }){ let ts = util.gettimestamp(); let transid = util.gettransid(ts); let sign = util.getsign(transid); const baseUrl = getApp().globalData.serverInterfaceUrl data.ts = ts data.transid = transid data.sign = sign return new Promise((resolve, reject) => { uni.request({ method: type, url: baseUrl + url, data: data, header: header, dataType: 'json', }).then((response) => { console.log(response) setTimeout(function() { uni.hideLoading(); }, 200); let [error, res] = response; resolve(res.data); }).catch(error => { let [err, res] = error; console.log(err) reject(err) }) }); }
调用:
httpclient.request('user',{method:"login","uname":that.user_name,"ucode":that.password} ).then(res=>{ if(res.code == 100){ //登录成功 if(that.remember_username){ uni.setStorageSync("remember_username",true) } if(that.remember_password){ uni.setStorageSync("remember_password",true) } //写入其他缓存.......
//跳转首页
uni.switchTab({ url:'../index/index' }) }else{ uni.showToast({ title: '登录失败:'+res.msg, icon:'none' }); } });
2. 升级业务代码也放下吧,不解释了。差分升级还在实现中,暂未放在代码里。
<template> <view class="zy-modal" :class="dshow?'show':''"> <view class="zy-dialog" style="background-color: transparent;"> <view class="padding-top text-white" :class="'zy-upgrade-topbg-'+theme"> <view> <text class="zy-upgrade-title"> 发现新版本 </text> </view> <text class="flex-wrap">{{version}}</text> </view> <view class="padding-xl bg-white text-left"> <scroll-view style="max-height: 200rpx;" scroll-y="auto" v-if="!update_flag"> <text>{{update_tips}}</text> </scroll-view> <view class="zy-progress radius striped active" v-if="update_flag"> <view :class="'bg-'+theme" :style="'width: '+update_process+'%;'"> {{update_process}} </view> </view> </view> <view class="zy-bar bg-white justify-end"> <view class="action" v-if="!update_flag"> <button class="zy-btn" :class="'bg-'+theme" @click="upgrade_checked">确认升级</button> <button class="zy-btn margin-left" :class="'line-'+theme" v-if="!forceupgrade" @click="upgrade_cancel">取消升级</button> </view> <view class="action text-center" v-if="update_flag&&!forceupgrade"> <button class="zy-btn" :class="'bg-'+theme" @click="upgrade_break">中断升级</button> </view> </view> </view> </view> </template> <script> export default { name: 'ZyUpgrade', props: { theme: { //主题,目前支持green,pink,blue,yellow,red type: String, default: 'green' }, updateurl: { //升级检测url,全路径 type:String, default: '' }, h5preview:{ //H5界面下是否预览升级 type: Boolean, default: false }, oldversion: { //如果是H5,为了方便测试,可以传入一个旧版本号进来。 type: String, default: '' }, oldcode: { //如果是H5,为了方便测试,可以传一个旧版本的code进来。 type: Number, default: 0 }, appstoreflag: { //是否启用appstore升级,如果启用,由服务端返回appstore的地址 type: Boolean, default: false }, noticeflag:{ //是否通知主界面无需更新 type:Boolean, default: false }, autocheckupdate:{ //是否页面截入时就判断升级 type:Boolean, default: false } }, data() { return { update_flag: false, //点击升级按钮后,显示进度条 dshow: false, update_process: 0, downloadTask: [], updated2version: '', version_url: '', update_tips: '', forceupgrade: false, currentversion: this.oldversion, versionname: '', vesioncode: this.oldcode } }, mounted() { let app_flag = false // #ifdef APP-PLUS app_flag = true // #endif if((this.h5preview || app_flag) && this.autocheckupdate){ console.log("检测升级") this.check_update() } }, computed:{ version(){ let retversion = '' retversion = this.currentversion + (this.currentversion!=''&&this.updated2version!=''?'->':'')+this.updated2version return retversion } }, methods:{ //检测升级 check_update(){ let that = this // #ifdef APP-PLUS plus.runtime.getProperty(plus.runtime.appid, function(widgetInfo) { that.currentversion = widgetInfo.version that.versionname = widgetInfo.name that.versioncode = widgetInfo.versionCode that.updatebusiness(that) }); // #endif // #ifdef H5 if(this.h5preview){ this.updatebusiness(that) } // #endif }, updatebusiness: function(that){ //具体升级的业务逻辑 uni.showLoading({ title: '', mask: false }); let platform = uni.getSystemInfoSync().platform let formdata = { method: "upgrade", version: that.currentversion, name: that.versionname, code: that.versioncode, ts:'123', transid:'123', sign:'123', platform: platform } uni.request({ url: that.updateurl, data: formdata, success: (result) => { uni.hideLoading() let data = result.data if(data.code == 100){ console.log(data) //提示升级 if(data.data.update_flag == 1){ that.dshow = true that.update_tips = data.data.update_tips that.forceupgrade = data.data.forceupdate==1 that.version_url = data.data.update_url //that.currentversion = widgetInfo.version that.updated2version = data.data.version }else{ if(that.noticeflag){ //通知父组件,当前版为最新版本 that.$emit("showupdateTips",0) } } }else{ uni.showToast({ title: '请求升级出错:'+data.msg, icon:'none' }); } } }); }, //点击开始升级按钮,开始升级 upgrade_checked:function(){ this.update_flag = true this.updateversion() }, //点击取消升级按钮,取消升级 upgrade_cancel:function(){ this.dshow = false }, //升级过程中,点击中断升级按钮,中断升级 upgrade_break: function(){ this.downloadTask.abort() this.update_flag = false }, //升级下载apk安装包的具体处理业务逻辑 updateversion: function(){ let platform = uni.getSystemInfoSync().platform console.log("操作系统:",platform) if(platform == 'ios' && this.appstoreflag){ //如果启用ios appstore升级,则打开appstore that.dshow = false console.log("跳转至appstore") plus.runtime.launchApplication({ action: that.version_url }, function(e) { uni.showToast({ title: '打开appstore失败', icon:'none' }); }); }else{ let that = this this.update_confirm = true this.downloadTask = uni.downloadFile({ url: that.version_url, success:function(res){ if(res.statusCode == 200){ //开始安装 plus.runtime.install(res.tempFilePath, { force: false }, function() { console.log('install success...'); plus.runtime.restart(); }, function(e) { console.error('install fail...'); }); }else{ uni.showToast({ title: '下载失败,网络错误', icon:'none' }); } }, fail:function(e) { console.log("下载失败",e) uni.showToast({ title: '下载失败:'+e.errMsg, icon:'none' }); this.update_flag = false }, complete:function(){ } }) this.downloadTask.onProgressUpdate(function(res){ that.update_process = res.progress }) } }, } } </script> <style scoped> @import url("static/css/main.css"); .zy-upgrade-topbg-green { background-image: url('static/images/green.png'); background-size: 100% 100%; background-repeat: no-repeat; height: 290rpx; } .zy-upgrade-topbg-red { background-image: url('static/images/red.png'); background-size: 100% 100%; background-repeat: no-repeat; height: 290rpx; } .zy-upgrade-topbg-pink { background-image: url('static/images/pink.png'); background-size: 100% 100%; background-repeat: no-repeat; height: 290rpx; } .zy-upgrade-topbg-yellow { background-image: url('static/images/yellow.png'); background-size: 100% 100%; background-repeat: no-repeat; height: 290rpx; } .zy-upgrade-topbg-blue { background-image: url('static/images/blue.png'); background-size: 100% 100%; background-repeat: no-repeat; height: 290rpx; } .zy-upgrade-title { font-size: 50rpx; color: white; } </style>
其他代码不放了,都是一些轮子。