33 uniapp-android+小程序+支付宝上线
33 android+小程序+支付宝上线
一 微信小程序端
0 小程序端配置
Hbuilder配置
微信开发者工具配置
1 处理导航条
app-plus的导航条只有app端可以用,也就意味着其他小程序端需要自定义导航条
使用uni提供的导航条组件需要注意:
ps: 下面的这一堆代码里只需要把uni-status-bar屏蔽掉,小程序端无需给状态栏占位。
<!-- #ifdef APP-PLUS -->
<uni-status-bar v-if="statusBar" />
<!-- #endif -->
<template>
<view class="uni-navbar">
<view :class="{'uni-navbar--fixed': fixed,'uni-navbar--shadow':border,'uni-navbar--border':border}" :style="{'background-color':backgroundColor}" class="uni-navbar__content">
<!-- #ifdef APP-PLUS -->
<uni-status-bar v-if="statusBar" />
<!-- #endif -->
<view :style="{color:color}" class="uni-navbar__header uni-navbar__content_view">
<view class="uni-navbar__header-btns uni-navbar__content_view" @tap="onClickLeft">
<view v-if="leftIcon.length" class="uni-navbar__content_view">
<uni-icons :type="leftIcon" :color="color" size="24" />
</view>
<view v-if="leftText.length" :class="{'uni-navbar-btn-icon-left':!leftIcon.length}" class="uni-navbar-btn-text uni-navbar__content_view">{{ leftText }}</view>
<slot name="left" />
</view>
<view class="uni-navbar__header-container uni-navbar__content_view">
<view v-if="title.length" class="uni-navbar__header-container-inner uni-navbar__content_view">{{ title }}</view>
<!-- 标题插槽 -->
<slot />
</view>
<view :class="title.length?'uni-navbar__header-btns-right':''" class="uni-navbar__header-btns uni-navbar__content_view" @tap="onClickRight">
<view v-if="rightIcon.length" class="uni-navbar__content_view">
<uni-icons :type="rightIcon" :color="color" size="24" />
</view>
<!-- 优先显示图标 -->
<view v-if="rightText.length&&!rightIcon.length" class="uni-navbar-btn-text uni-navbar__content_view">{{ rightText }}</view>
<slot name="right" />
</view>
</view>
</view>
<view v-if="fixed" class="uni-navbar__placeholder">
<!-- #ifdef APP-PLUS -->
<uni-status-bar v-if="statusBar" />
<!-- #endif -->
<view class="uni-navbar__placeholder-view" />
</view>
</view>
</template>
<script>
import uniStatusBar from '../uni-status-bar/uni-status-bar.vue'
import uniIcons from '../uni-icons/uni-icons.vue'
export default {
name: 'UniNavBar',
components: {
uniStatusBar,
uniIcons
},
props: {
title: {
type: String,
default: ''
},
leftText: {
type: String,
default: ''
},
rightText: {
type: String,
default: ''
},
leftIcon: {
type: String,
default: ''
},
rightIcon: {
type: String,
default: ''
},
fixed: {
type: [Boolean, String],
default: false
},
color: {
type: String,
default: '#000000'
},
backgroundColor: {
type: String,
default: '#FFFFFF'
},
statusBar: {
type: [Boolean, String],
default: false
},
shadow: {
type: [String, Boolean],
default: true
},
border: {
type: [String, Boolean],
default: true
}
},
methods: {
onClickLeft() {
this.$emit('click-left')
},
onClickRight() {
this.$emit('click-right')
}
}
}
</script>
<style>
@charset "UTF-8";
.uni-navbar__content {
display: block;
position: relative;
width: 100%;
background-color: #fff;
overflow: hidden
}
.uni-navbar__content .uni-navbar__content_view {
display: flex;
align-items: center
}
.uni-navbar__header {
display: flex;
flex-direction: row;
width: 100%;
height: 44px;
line-height: 44px;
font-size: 16px
}
.uni-navbar__header-btns {
display: inline-flex;
flex-wrap: nowrap;
flex-shrink: 0;
width: 120upx;
padding: 0 12upx
}
.uni-navbar__header-btns:first-child {
padding-left: 0
}
.uni-navbar__header-btns:last-child {
width: 60upx
}
.uni-navbar__header-btns-right:last-child {
width: 120rpx;
text-align: right;
flex-direction: row-reverse
}
.uni-navbar__header-container {
width: 100%;
margin: 0 10upx
}
.uni-navbar__header-container-inner {
width: 100%;
display: flex;
justify-content: center;
font-size: 30upx
}
.uni-navbar__placeholder-view {
height: 44px
}
.uni-navbar--fixed {
position: fixed;
z-index: 998
}
.uni-navbar--shadow {
box-shadow: 0 1px 6px #ccc
}
.uni-navbar--border:after {
position: absolute;
z-index: 3;
bottom: 0;
left: 0;
right: 0;
height: 1px;
content: '';
-webkit-transform: scaleY(.5);
transform: scaleY(.5);
background-color: #e5e5e5
}
</style>
以首页为例子,为各类小程序专门设置导航条
只关注 这类
<template>
<view>
<!-- #ifdef MP -->
<uni-nav-bar :shadow="false" :border="false"
@click-left="clickLeft" @click-right="clickRight">
<!-- 左边图标 -->
<block slot="left">
<view class="iconfont icon-qiandao ml-2 mr-2" style="font-size: 22px;color: #FF9619;"></view>
</block>
<!-- 中间搜索框 -->
<view class="flex justify-center align-center rounded text-muted bg-light flex-1 mt-1" style="margin-left: -46upx;height: 60upx;" @tap="openSearch">
<view class="iconfont icon-sousuo mr-1"></view>搜索帖子
</view>
<!-- 右边图标 -->
<block slot="right">
<view class="icon iconfont icon-bianji1 text-dark" style="font-size: 22px;"></view>
</block>
</uni-nav-bar>
<!-- #endif -->
<!-- 顶部选项卡 -->
<scroll-view scroll-x="true" :scroll-into-view="scrollIndex" scroll-with-animation
class="scroll-row"
style="height: 100rpx;">
<view v-for="(item,index) in tabBars"
:key="index"
:id="'tab'+index"
class="scroll-row-item px-2 py-2 font-md"
:class="tabIndex === index? 'text-main font-lg font-weight-bold':''"
@click="changeTab(index)">
{{item.classname}}
</view>
</scroll-view>
<!-- 定制选项卡对应的内容 -->
<!-- current 聚焦在当前子滑块实例的 index 默认会从0 1 2 3。。。依次排列 -->
<swiper :duration="150" :current="tabIndex" @change="onChangeTab"
:style="'height:'+scrollH+'px'">
<!-- 会默认分配索引 0 1 2 3 4 5 -->
<swiper-item v-for="(item,index) in newsList" :key="index">
{{item.name}}
<scroll-view scroll-y="true" :style="'height:'+scrollH+'px;'" @scrolltolower="loadmore(index)">
<template v-if="item.list.length>0">
<!-- 列表 -->
<block v-for="(item2,index2) in item.list" :key="index2">
<!-- 列表样式 -->
<common-list :item="item2" :index="index2" @follow="follow" @doSupport="doSupport"></common-list>
<!-- 全局分割线 -->
<divider></divider>
</block>
<!-- 上拉加载 -->
<load-more :loadmore="item.loadmore"></load-more>
</template>
<!-- 首次加载触发 -->
<template v-else-if="item.firstLoad">
<view class="text-light-muted flex align-center justify-center font-md" style="height: 200rpx;">加载中...</view>
</template>
<!-- 非首次加载没有数据触发 -->
<template v-else>
<!-- 无数据渲染页面 -->
<no-thing>来了啊老弟</no-thing>
</template>
</scroll-view>
</swiper-item>
</swiper>
</view>
</template>
<script>
// #ifdef MP
import uniNavBar from '@/components/uni-ui/uni-nav-bar/uni-nav-bar.vue';
// #endif
import commonList from '@/components/common/common-list.vue';
import loadMore from '@/components/common/load-more.vue';
export default {
components: {
commonList,
loadMore,
// #ifdef APP-PLUS
uniNavBar
// #endif
},
data() {
return {
scrollIndex:"",
tabIndex: 0,
tabBars: [],
newsList: [],
scrollH:0
}
},
//监听原生标题栏按钮点击事件,参数为Object
onNavigationBarSearchInputClicked(){
uni.navigateTo({
url:'../search/search?type?post',
})
},
// 监听导航按钮点击事件
onNavigationBarButtonTap() {
this.navigateTo({
url: '../add-input/add-input',
})
},
onLoad() {
uni.getSystemInfo({
success:res=>{
// 可用窗口高度(屏幕高度-导航栏高度-底部栏高度。) - 选项卡高度
this.scrollH = res.windowHeight- uni.upx2px(100)
console.log(this.scrollH)
}
})
// 根据选项生成列表
this.getData()
// 监听点踩操作进行数据更新。
uni.$on('updateFollowOrSupport' ,this.listionFollowOrSupport)
// 监听评论数进行数据更新。
uni.$on('updateCommentsCount' ,this.updateCommentsCountBack)
// 监听刷新首页
uni.$on('updateIndex',this.getData)
},
onUnload() {
uni.$off('updateFollowOrSupport',this.listionFollowOrSupport)
uni.$off('updateCommentsCount',this.updateCommentsCountBack)
uni.$off('updateIndex',this.getData)
},
methods: {
// #ifdef MP
clickLeft(){
console.log('左边事件')
},
clickRight(){
// 打开发布页面
this.navigateTo({
url: '../add-input/add-input',
})
},
// #endif
openSearch(){
uni.navigateTo({
url: '../search/search',
});
},
// 封装成回掉得意移除页面的时候停止监听
listionFollowOrSupport(e){
console.log('index.vue 接受到了')
switch (e.type){
case 'follow': // 关注
this.follow(e.data.user_id)
break;
case 'support':
this.doSupport(e.data)
default:
break
}
},
// 监听评论数进行更新
updateCommentsCountBack(e){
// console.log('updateCommentsCountBack监听到了')
this.newsList.forEach(tab=>{
tab.list.forEach(item=>{
if (item.id === e.id){
item.comment_count = e.count
}
})
})
},
// 上拉加载更多
loadmore(index){
// 拿到当前列表
let item = this.newsList[index]
// 修改当前加载状态
item.loadmore = '加载中。。。'
// 分页量+1
item.page++
// 模拟数据请求
this.getList()
// setTimeout(()=>{
// // 加载数据
// // ... 相当于取出来当前对象可以遍历出来的内容放到了当前对象里面。
// // 这个可以粗糙的理解为把合并了两个一摸一样的列表,列表里面原来的内容*2了
// item.list = [...item.list,...item.list]
// item.loadmore = '上拉加载更多'
// },2000)
},
// 制作列表+上拉加载数据初始值
getData(){
// 获取文章分类,请求用then嵌套写,不然两个请求数据是分先后的。
this.$H.get('/postclass').then(res=>{
// let [err,result] = res
// console.log(result)
this.tabBars = res.list
// console.log(this.tabBars)
// 根据分类生成列表
var arr = []
for (let i=0; i < this.tabBars.length; i++){
arr.push({
loadmore:"上拉加载更多",
list:[],
page:1,
firstLoad:true
})
}
this.newsList = arr
// 获取第一个分类的数据
this.getList()
})
},
// 获取指定分类下的数据
getList(){
if (this.tabBars.length) {
let index = this.tabIndex // 这步骤很有意义,如果不这么写网络io拉的事件长的情况会使得数据乱掉。
let id = this.tabBars[index].id
let page = this.newsList[index].page
// console.log(page)
let isrefresh = page === 1 // 第一页的时候涉及到更改,剩下的页根本不涉及到更改。
this.$H.get('/postclass/'+id+'/post/'+page,{},{
token:true,
noExToken:true
})
.then(res2 => {
// console.log(res2)
// console.log(res2.length)
let list = res2.list.map(v => {
// 有大括号就是回掉函数,回掉函数的返回值会作为处理后的value
// console.log(v.titlepic)
return this.$U.formatCommonList(v)
})
// console.log(isrefresh)
this.newsList[index].list = isrefresh ? list : [...this.newsList[index].list,...list];
this.newsList[index].loadmore = list.length<10 ? '没有更多了' : '上拉加载更多';
// 已经获取过一次了,就把首次请求设置为false,并且第二页的时候就不会校验这个了 完美。
if (isrefresh) {
this.newsList[index].firstLoad = false
}
})
}
},
follow(user_id){
// this.newsList[this.tabIndex].list[e].isFollow = true
// uni.showToast({title:'关注成功'})
// console.log(this.newsList)
this.newsList.forEach(tab=>{
tab.list.forEach(item=>{
if (item.user_id === user_id){
item.isFollow = true
}
})
})
},
// 切换选项
changeTab(index){
if (this.tabIndex === index){
return;
}
this.tabIndex = index
// 视角滚动到指定元素
this.scrollIndex = 'tab'+index
// 获取当前分类下的列表数据
if(this.newsList[this.tabIndex].firstLoad){
this.getList()
}
},
// 监听选项内容滑动
onChangeTab(e){
this.changeTab(e.detail.current)
},
// 顶踩操作
doSupport(e){
// 拿到当前队对象
this.newsList[this.tabIndex].list.forEach(item=>{
// console.log(e)
// 除非item拿到的是简单类型或手动深拷贝,否则皆为浅拷贝
// 第一层深拷贝可以考虑做一个{},往里面添加值的形式做到第一层深拷贝
if (item.id === e.id){
// console.log('处理前',this.newsList[this.tabIndex].list)
if (item.support.type === ''){
item.support[e.type+'_count']++
} else if (item.support.type === 'support' && e.type === 'unsupport'){
// 顶 -1
item.support.support_count--;
// 踩 +1
item.support.unsupport_count++;
} else if (item.support.type === 'unsupport' && e.type === 'support'){
// 踩 -1
item.support.unsupport_count --;
// 顶 +1
item.support.support_count ++;
}
item.support.type = e.type
// console.log('处理后说明是浅拷贝,影响原来的列表,vue搜集到了直接渲染',this.newsList[this.tabIndex].list)
}
})
let msg = e.type === 'support' ? '顶' : '踩'
uni.showToast({
title:msg+'成功'
})
}
}
}
</script>
<style>
</style>
<!-- -->
2 小程序分享
uni官方文档:https://uniapp.dcloud.io/api/plugins/share?id=onshareappmessage
// #ifndef APP-PLUS
// 微信小程序分享,暂时知道有这么个接口就行了,暂时先不测试
// ps:如果没写这个,微信小程序会提示当前页面没有设置分享。
// 和onLoad同级别。
onShareAppMessage(res) {
console.log(res)
return {
title: this.info.title,
path: '/pages/detail/detail?detail='+JSON.stringify(this.info)
}
},
// #endif
3 小程序登录
ps:源代码后台返回token稍微有点问题,前台业务逻辑没问题。
概念
// 如果简单获取用户信息使用 button微信内置的事件就够了
// 如果想要走后台必然绕不开openid,这个时候就需要uni.login获取code吗再通过我们的服务器获取openid进行数据存储然后再返回给小程序端token保持登录状态。
精华代码
<!-- #ifdef MP-WEIXIN -->
<button type="primary" open-type="getUserInfo" @getuserinfo="mpGetUserInfo">微信登录</button>
<!-- #endif -->
// #ifdef MP-WEIXIN
// 如果简单获取用户信息使用 button微信内置的事件就够了
// 如果想要走后台必然绕不开openid,这个时候就需要uni.login获取code吗再通过我们的服务器获取openid进行数据存储了。
mpGetUserInfo(result){
console.log(result) // res是拿不到唯一标识的
// console.log(result.detail.userInfo.nickName)
// console.log(result.detail.userInfo.gender)
// console.log(result.detail.userInfo.avatarUrl)
// 需要调用login方法拿到code然后请求后端,后端发送给服务器appid+screetkey,这个时候服务器才会
// 给我返回openid
uni.showLoading({ title: '登录中...', mask: true });
// 获取失败
if (result.detail.errMsg !== 'getUserInfo:ok') {
uni.hideLoading();
uni.showModal({
title: '获取用户信息失败',
content: '错误原因' + result.detail.errMsg,
showCancel: false
});
return;
}
let userinfo = result.detail.userInfo;
uni.login({
provider: 'weixin',
success: (res)=>{
// console.log(res);
// {code: "071vxLuZ0vxS8V17f7vZ03gLuZ0vxLuo"
// errMsg: "login:ok"}
this.MpLogin({
url:"/wxlogin",
code:res.code,
nickName:userinfo.nickName,
avatarUrl:userinfo.avatarUrl
})
},
complete: () => {
uni.hideLoading();
}
});
},
// #endif
// #ifdef MP-WEIXIN
// 后端拿到了code,nickname,avatarurl
// 后端拿着code换取openid,得到了唯一标示,
// 然后后端把nickname和avatarurl和openid进行存储。
MpLogin(options){
this.$H.post(options.url,{
code:options.code,
nickName:options.nickName,
avatarUrl:options.avatarUrl
}).then(data=>{
// console.log(data) // data是空的但是代码流程没啥问题。
// 修改vuex的state,持久化存储
this.$store.commit('login',this.$U.formatUserinfo(data))
// 返回上一页
if(this.back){
uni.navigateBack({
delta: 1
});
}
uni.showToast({
title: '登录成功',
icon: 'none'
});
}).catch(err=>{
console.log(err)
});
},
// #endif
设计的outer-login详细代码
<template>
<view>
<view class="flex align-center justify-between" style="padding:20rpx 100rpx">
<!-- #ifdef APP-PLUS || MP-ALIPAY-->
<view
v-for="(item,index) in providerList" :key="index"
class="iconfont text-white font-lgger rounded-circle flex align-center justify-center"
style="height:100rpx;width:100rpx;"
:class="item.icon+' '+item.bgColor"
@click="login(item)">
<!-- 如果直接写了函数就会直接调用 :login="login(item)"-->
<!-- 完善login函数 实现第三方登录。 -->
</view>
<!-- #endif -->
<!-- #ifdef MP-WEIXIN -->
<button type="primary" open-type="getUserInfo" @getuserinfo="mpGetUserInfo">微信登录</button>
<!-- #endif -->
</view>
</view>
</template>
<script>
export default {
props:{
back:{
type:Boolean,
default:false
}
},
data() {
return {
providerList: []
}
},
mounted() {
uni.getProvider({
service: 'oauth',
success: (result) => {
// console.log(result.provider)
this.providerList = result.provider.map((value) => {
let providerName = '';
let icon = ''
let bgColor = ''
switch (value) {
case 'weixin':
providerName = '微信登录'
// 自己在这个位置添加上需要渲染的信息 如 icon bgColor
icon = 'icon-weixin'
bgColor = 'bg-success'
break;
case 'qq':
providerName = 'QQ登录'
icon = 'icon-QQ'
bgColor = 'bg-primary'
break;
case 'sinaweibo':
providerName = '新浪微博登录'
icon = 'icon-xinlangweibo'
bgColor = 'bg-warning'
break;
}
// 返回的时候
return {
name: providerName,
id: value,
icon:icon,
bgColor:bgColor
}
});
},
fail: (error) => {
console.log('获取登录通道失败', error);
}
});
},
methods: {
// 登录
login(item){
// console.log('###',item)
// 微信需要先login一下,才能执行里面的uni.getUserInfo
// 这个uni.login的工具函数,可以渲染出来微信登录的页面
uni.login({
provider:item.id,
success:res => {
// console.log(res)
// 获取用户信息
uni.getUserInfo({
provider:item.id,//'weixin'
success:(infoRes) =>{
// console.log('####',infoRes)
// {
// "errMsg": "getUserInfo:ok",
// "userInfo": {
// "openId": "oRrdQtzJovVH1ZbCqvf9rQ",
// "nickName": "不争",
// "gender": 1,
// "city": "白山",
// "province": "吉林",
// "country": "中国",
// "avatarUrl": "http://thirdwx.qlogo.cn/mmopen/vi_32/Q0j4TwGTfTJYwJfvYwOp29uZAvfRoPSMvo357ibaGh79IdZ40ja3kQS9Sp7VTWhUHcg/132",
// "unionId": "oU5YytxKvy2K0oUftFxmm4"
// }
// }
let obj = {
provider:item.id, // weixin/weibo/qq
openid :infoRes.userInfo.openId,// 唯一id
expires_in:0,// 过期时间
nickName:infoRes.userInfo.nickName, // 微信的昵称
avatarUrl:infoRes.userInfo.avatarUrl// 微信头像地址
}
// console.log(obj)
// 从微信/qq拿到的信息提交给后端。
this.loginEvent(obj)
},
})
}
})
},
// 拿到第三方来的登录信息请求后端。
loginEvent(data){
this.$H.post('/user/otherlogin',data)
.then(res=>{
// 修改vuex的state,持久化存储
// this.$store.commit('login',this.$U.formatUserinfo(res))
this.$store.dispatch('login',this.$U.formatUserinfo(res))
// 返回上一页
if (this.back){
uni.navigateBack({
delta:1
})
}
uni.showToast({
title:'登录成功',
icon:'none'
})
})
},
// #ifdef MP-WEIXIN
// 如果简单获取用户信息使用 button微信内置的事件就够了
// 如果想要走后台必然绕不开openid,这个时候就需要uni.login获取code吗再通过我们的服务器获取openid进行数据存储了。
mpGetUserInfo(result){
console.log(result) // res是拿不到唯一标识的
// console.log(result.detail.userInfo.nickName)
// console.log(result.detail.userInfo.gender)
// console.log(result.detail.userInfo.avatarUrl)
// 需要调用login方法拿到code然后请求后端,后端发送给服务器appid+screetkey,这个时候服务器才会
// 给我返回openid
uni.showLoading({ title: '登录中...', mask: true });
// 获取失败
if (result.detail.errMsg !== 'getUserInfo:ok') {
uni.hideLoading();
uni.showModal({
title: '获取用户信息失败',
content: '错误原因' + result.detail.errMsg,
showCancel: false
});
return;
}
let userinfo = result.detail.userInfo;
uni.login({
provider: 'weixin',
success: (res)=>{
// console.log(res);
// {code: "071vxLuZ0vxS8V17f7vZ03gLuZ0vxLuo"
// errMsg: "login:ok"}
this.MpLogin({
url:"/wxlogin",
code:res.code,
nickName:userinfo.nickName,
avatarUrl:userinfo.avatarUrl
})
},
complete: () => {
uni.hideLoading();
}
});
},
// #endif
// #ifdef MP-WEIXIN
// 后端拿到了code,nickname,avatarurl
// 后端拿着code换取openid,得到了唯一标示,
// 然后后端把nickname和avatarurl和openid进行存储。
MpLogin(options){
this.$H.post(options.url,{
code:options.code,
nickName:options.nickName,
avatarUrl:options.avatarUrl
}).then(data=>{
// console.log(data) // data是空的但是代码流程没啥问题。
// 修改vuex的state,持久化存储
this.$store.commit('login',this.$U.formatUserinfo(data))
// 返回上一页
if(this.back){
uni.navigateBack({
delta: 1
});
}
uni.showToast({
title: '登录成功',
icon: 'none'
});
}).catch(err=>{
console.log(err)
});
},
// #endif
}
}
</script>
<style>
</style>
4 小程序上线(思路)
1 注意小程序体积不超过2mb
2 所以Hbuilder打包的时候要配置manifest.json
3 小程序网页后台配置合法域名
4 填写好基本信息后提交审核(未完待续)。
二 app实现热更新
0 关于更新部分代码
ps:直接看注释就好了
App.vue
<script>
export default {
onLaunch: function() {
console.log('App Launch');
// 检测更新
// #ifdef APP-PLUS
this.$U.update()
// #endif
// 网络更新
this.$U.onNetWork()
// 初始化用户登录状态
this.$store.dispatch('initUser')
},
onShow: function() {
console.log('App Show');
},
onHide: function() {
console.log('App Hide');
}
};
</script>
Util.js
...
update(showToast = false){
// #ifdef APP-PLUS
plus.runtime.getProperty(plus.runtime.appid, function(widgetInfo) {
$H.post('/update',{
ver:widgetInfo.version, // 打包后该参数可以获取当前app的版本号,发给服务端,服务端判断是否更新
}).then((data) => {
// 成功
if (!data.url){
// 无需更新
if(showToast){
uni.showToast({ title: '无需更新',icon:"none" })
}
return
}
// 服务端返回了,更新的oss的包地址
uni.showModal({
title: '发现新的版本',
content: '最新版本:'+data.version,
cancelText: '放弃更新',
confirmText: '立即更新',
success: res => {
if(!res.confirm) return;
// 然后根据服务端发来的包地址,然后进行下载。
uni.downloadFile({
url: data.url,
success: (downloadResult) => {
if (downloadResult.statusCode === 200) {
// 下载完了,然后用plus.runtime.install 安装下
plus.runtime.install(downloadResult.tempFilePath, {
force: false
}, function() {
console.log('install success...');
// 安装完了然后从新启动更新上。然后就完事儿了。
plus.runtime.restart();
}, function(e) {
console.error('install fail...');
});
}
}
});
}
});
});
});
// #endif
},
...
about.vue
<template>
<view>
<view class="flex align-center justify-center flex-column pt-4 pb-3">
<image src="/static/common/nothing.png" style="width: 300rpx;height: 300rpx;" class="rounded-circle"></image>
<text class="font text-muted mt-2">version {{version}}</text>
</view>
<uni-list-item title="新版本检测" @click="update"></uni-list-item>
<uni-list-item title="社区用户协议"></uni-list-item>
</view>
</template>
<script>
import uniListItem from '@/components/uni-ui/uni-list-item/uni-list-item.vue';
export default {
components: {
uniListItem
},
data() {
return {
version:''
}
},
onLoad() {
// #ifdef APP-PLUS
plus.runtime.getProperty(plus.runtime.appid, (widgetInfo)=>{
this.version = widgetInfo.version;
})
// #endif
},
methods: {
update(){
this.$U.update(true)
}
}
}
</script>
<style>
</style>
1 首先先云打包一个apk出来
2 制作热更新包
把这个文件,放到oss上面,然后把url和版本号存放到你的数据库里面。这样前端就能拿得到了。
每次热更新请求的就是这个wgt文件,然后plus拿着这个文件更新app。
3 注意事项
1 比如你的你云打包的时候HBuildX是 2.7.0
2 但是你制作wgt更新包的时候是HBuildX是 2.7.2
这个时候就不能采取热更新了,因为编译环境已经发生了改变,这个时候需要全更新。
全更新参考文档:https://ask.dcloud.net.cn/article/34972
热更新参考文档:https://ask.dcloud.net.cn/article/35667
Uni-app运行环境版本和编译器版本不一致的问题:https://ask.dcloud.net.cn/article/35627
四 app上线(待定)
配置图标:
andriod证书问题:
参考官方文档:https://ask.dcloud.net.cn/article/35777
首先如果没有java环境首先下载jre环境
https://www.oracle.com/technetwork/java/javase/downloads/index.html
然后生成签名证书:
keytool -genkey -alias mingyantest -keyalg RSA -keysize 2048 -validity 36500 -keystore /Users/abc/Desktop/mingyantest.keystore
- testalias是证书别名,可修改为自己想设置的字符,建议使用英文字母和数字
- test.keystore是证书文件名称,可修改为自己想设置的文件名称,也可以指定完整文件路径
实际上线的时候再考虑其他配置(略)
![image-20200525164229509](33 各个端适配上线.assets/image-20200525164229509.png)
五 支付宝小程序调试(略)
1 下载支付宝小程序调试器
2 如果出现莫名其妙报错,在hbuilder上面直接发行包,然后使用支付宝小程序进行调试。