27 首页交互+文章详情页(请求模块再度封装,多层数据嵌套无法更新解决方案($set,$forceUpdate),文章详情交互实现,分享到微信朋友+朋友圈)
27 首页交互+文章详情页
(请求模块再度封装,多层数据嵌套无法更新解决方案($set,$forceUpdate),文章详情交互实现,分享到微信朋友+朋友圈)
六 首页交互实现
效果图:
要点:
1 then嵌套使用:如果.then的方式请求数据,如果前后的请求数据有逻辑关系,要嵌套then使用
2 初始数据列表做状态:来的数据可以考虑先做上状态如:page,firstLoad,loadmore等信息。
3 下拉加载问题:下拉的时候 正常为下拉加载更多,触发下拉加载的时候显示为加载中,数据请求回来之后判断是否还有数据如果有定制为下拉加载跟多,如果没有提示没有更多了。
4 第一次加载问题:根据firstLoad来判断是否是第一次加载,如果已经加载过了就不要重新加载,如果首次加载则加载,剩下的都是下拉加载来扩充内容。
5 处理了没有数据的时候显示加载中:依靠的也是是否是第一次加载来实现的。
6 格式化后端传来的数据考虑放到util.js里面
<!-- 首次加载触发 -->
<template v-else-if="item.firstLoad">
<view class="text-light-muted flex align-center justify-center font-md" style="height: 200rpx;">加载中...</view>
</template>
代码
<template>
<view>
<!-- <block v-for="(item,index) in list" :key="index"> -->
<!-- 列表样式 -->
<!-- <common-list :item="item" :index="index" @follow="follow" @doSupport="doSupport"></common-list> -->
<!-- 全局分割线 -->
<!-- <divider></divider> -->
<!-- </block> -->
<!-- 顶部选项卡 -->
<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>
import commonList from '@/components/common/common-list.vue';
import loadMore from '@/components/common/load-more.vue'
export default {
components: {
commonList,
loadMore
},
data() {
return {
scrollIndex:"",
tabIndex: 0,
tabBars: [],
newsList: [],
}
},
//监听原生标题栏按钮点击事件,参数为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(101)
// console.log(this.scrollH)
}
})
// 根据选项生成列表
this.getData()
},
methods: {
// 上拉加载更多
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 = result.data.data.list
// 根据分类生成列表
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)
.then(res2 => {
let [err2,result2] = res2
console.log('YYYY',result2)
let list = result2.data.data.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(e){
this.newsList[this.tabIndex].list[e].isFollow = true
uni.showToast({title:'关注成功'})
},
// 切换选项
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){
// 拿到当前队对象
let item = this.newsList[this.tabIndex].list[e.index]
let msg = e.type === 'support' ? '顶' : '踩'
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
uni.showToast({
title:msg+'成功'
})
}
}
}
</script>
<style>
</style>
七 请求错误处理封装
要点:
1 封装的全面就不可避免使用success,fail回掉函数。
2 之前不写成promise对象是因为,之前没有使用success,fail回掉函数,uniapp自动封装为promise对象。
3 现在使用了success、fail 所以要自己定制一个promise对象
4 这样我们就可以then直接取到后端传来的data数据。
代码:
import $C from '@/common/config.js'
export default {
common:{
method:'GET',
header:{
"content-type":"application/json"
},
data:{}
},
request(options = {}){
options.url = $C.webUrl + options.url
options.method = options.method || this.common.method
options.header = options.header || this.common.header
// 验证权限token
// 该有的参数传入进去,返回了一个promise对象可以直接.then使用了就。
// ps:之前不写成promise对象是因为,之前没有使用success,fail回掉函数,uniapp自动封装为promise对象。
// 现在使用了success、fail 所以要自己定制一个promise对象
return new Promise((resolve,reject)=>{
uni.request({
// 类似于python的** 打散
...options,
success: (result) => {
// console.log('####',result)
// 服务器传来非200 状态码
if (result.statusCode !== 200){
uni.showToast({
title:result.data.msg || '请求失败',
icon:'none'
})
return reject(result.data)
}
// 正常返回 服务器返回正常数据,直接取到了服务器传来的data
resolve(result.data.data)
},
// 请求发送失败
fail:(error) => {
uni.showToast({
title:error.errMsg || '请求失败',
icon:'none'
})
return reject()
}
})
})
},
get(url, data={}, options={}){
options.url = url
options.data = data
options.method = 'GET'
return this.request(options)
},
post(url, data={}, options={}){
options.url = url
options.data = data
options.method = 'POST'
return this.request(options)
},
}
// ---------------正确的返回方式 success的result--------------
// {
// "data": {
// "msg": "获取成功",
// "data": {
// "list": [{
// "id": 1,
// "classname": "关注"
// }, {
// "id": 2,
// "classname": "推荐"
// }, {
// "id": 3,
// "classname": "体育"
// }, {
// "id": 4,
// "classname": "热点"
// }, {
// "id": 5,
// "classname": "财经"
// }, {
// "id": 6,
// "classname": "娱乐"
// }]
// }
// },
// "statusCode": 200,
// "header": {
// "X-Android-Sent-Millis": "1587476693145",
// "Content-Type": "application/json; charset=utf-8",
// "_": "HTTP/1.1 200 OK",
// "X-Powered-By": "PHP/7.2.13",
// "X-Android-Selected-Protocol": "http/1.1",
// "Date": "Tue, 21 Apr 2020 13:44:49 GMT",
// "Server": "nginx",
// "Transfer-Encoding": "chunked",
// "X-Android-Response-Source": "NETWORK 200",
// "Connection": "keep-alive",
// "X-Android-Received-Millis": "1587476693348"
// },
// "errMsg": "request:ok"
// }
// -------------错误的返回方式 success的result---------------
// {
// "data": {
// "msg": "服务器异常",
// "errorCode": "999"
// },
// "statusCode": 500,
// "header": {
// "X-Android-Sent-Millis": "1587476568046",
// "Content-Type": "application/json; charset=utf-8",
// "_": "HTTP/1.1 500 Internal Server Error",
// "X-Powered-By": "PHP/7.2.13",
// "X-Android-Selected-Protocol": "http/1.1",
// "Date": "Tue, 21 Apr 2020 13:42:44 GMT",
// "Server": "nginx",
// "Transfer-Encoding": "chunked",
// "X-Android-Response-Source": "NETWORK 500",
// "Connection": "keep-alive",
// "X-Android-Received-Millis": "1587476568104"
// },
// "errMsg": "request:ok"
// }
八 多层嵌套数据无法更新问题($set,$forceUpdate)
问题:
解决方案
注意!!!!!:
$set 要在最开始的时候这个属性就是set上去的,如果已经有a属性了,并且a属性不是set上去的那么a属性发生改变不会触发视图更新。
所以我们如果拿到了一个大的对象,并且需要频繁操作这个对象里面的这些属性,最好在最开始的时候统一进行for循环遍历的$set一遍。
解决方案一(推荐) 对象嵌套数组,数组嵌套对象,使得vue检测到该数据并且触发视图更新。
#####用法
this.$set(this.info,'images',res.detail.images)
#####实际开发环境
data:{
pro_obj:{} // 里面的属性不知渲染一次,每个属性可能经过多次操作渲染。
viewTag:true
}
onLoad(option){
let data = option.data
// 我想把data赋值给pro_obj
//情景一: this.pro_obj = data 如果你只是渲染一下this.pro_obj 这么写没问题。
//情景二: 但是如果未来pro_obj 里面的属性要是进行多次操作修改然后渲染,一定要for循环set操作,才能监听到pro_obj 里面的属性每一次的改变。
for (let key in data){
this.pro_obj[key] = data[key]
}
//情景三: 视图中你也可以做一个v-if=“viewTag” 结合this.$nextTink通过改变viewTag进行某个区域重新渲染(不推荐)
// 核心原则一句话:如果一个对象初次拥有某个属性,如果初次不是set进来的,日后很难监控到这个属性的改变。
...
解决方案二 迫使vue实例重新渲染。
this.$forceUpdate
解决方案三,如果逻辑允许,直接把嵌套的数据放到data的根层。
$set用法
Vue.set( target, propertyName/index, value )
-
参数:
{Object | Array} target
{string | number} propertyName/index
{any} value
-
返回值:设置的值。
-
用法:
向响应式对象中添加一个属性,并确保这个新属性同样是响应式的,且触发视图更新。它必须用于向响应式对象上添加新属性,因为 Vue 无法探测普通的新增属性 (比如
this.myObject.newProperty = 'hi'
)注意对象不能是 Vue 实例,或者 Vue 实例的根数据对象。
$forceUpdate用法
vm.$forceUpdate()
-
示例:
迫使 Vue 实例重新渲染。注意它仅仅影响实例本身和插入插槽内容的子组件,而不是所有子组件。
九 文章详情交互实现(多层数据嵌套无法显示问题参考上一个标题)
效果图:
要点
info:{
id:"",
username:"",
userpic:"",
newstime:"",
isFollow:false,
title:"",
titlepic:"",
support:{
type:"support", // 顶
support_count:0,
unsupport_count:0
},
comment_count:0,
share_num:0,
content:"",
images:[]
}
__init(data){
// 修改标题
// console.log('222',data)
uni.setNavigationBarTitle({
title:data.title
})
this.info = data
this.$H.get('/post/'+data.id).then(res=>{
this.info.content = res.detail.content
// 发现问题,vue没有检测到info下的images数据更新
// this.info.images = res.detail.images
// 解决方案一 对象嵌套数组,数组嵌套对象,使得vue检测到该数据进行视图更新。
this.$set(this.info,'images',res.detail.images)
// console.log(this.info)
// 解决方案二,强制更新vue所有实例数据。
// this.$forceUpdate 强制更新vue所有实例数据
// 解决方案三,如果逻辑允许,直接把嵌套的深数据放到data的根层,这样就能检测到了。
})
},
全部代码
<template>
<view>
<common-list :item='info' isdetail
@follow="follow"
@doSupport="doSupport">
<view class="flex font-md align-center">
{{info.title}}
</view>
<view class="flex font align-center">
{{info.content}}
</view>
<!-- widthFix这个只是裁剪方式,跟具体宽度无关 -->
<block v-for="(item,index) in info.images" :key="index">
<image :src="item.url" class='w-100' mode="widthFix"
@click="preview(index)"></image>
</block>
</common-list>
<divider></divider>
// copy官方评论组件
<view class="p-2 font-md font-weight-bold">
最新评论 3
</view>
<view class="px-2">
<view class="uni-comment-list">
<view class="uni-comment-face"><image src="https://img-cdn-qiniu.dcloud.net.cn/uniapp/images/uni@2x.png" mode="widthFix"></image></view>
<view class="uni-comment-body">
<view class="uni-comment-top">
<text>小猫咪</text>
</view>
<view class="uni-comment-content">支持国产,支持DCloud!</view>
<view class="uni-comment-date">
<view>2天前</view>
</view>
</view>
</view>
</view>
<!-- 空白高度占位,使得有内容的部分不要被底部评论框挡住。 -->
<view style="height: 100rpx;"></view>
<bottom-input @submit="submit"></bottom-input>
<!-- 分享下拉弹出框 -->
<more-share ref="share"></more-share>
</view>
</template>
<script>
import commonList from '@/components/common/common-list.vue'
import bottomInput from '@/components/common/bottom-input.vue'
import moreShare from '@/components/common/more-share.vue'
export default {
components:{
commonList,
bottomInput,
moreShare
},
data() {
return {
// 当前帖子信息
info:{
id:"",
username:"",
userpic:"",
newstime:"",
isFollow:false,
title:"",
titlepic:"",
support:{
type:"support", // 顶
support_count:0,
unsupport_count:0
},
comment_count:0,
share_num:0,
content:"",
images:[]
}
}
},
computed:{
imagesList(){
//数组取出来每个元素,然后每个元素进行操作后,组成新的数组。
return this.info.images.map(item=>item.url)
}
},
// 接受传参
onLoad(e) {
// 初始化
if (e.detail){
this.__init(JSON.parse(e.detail))
}
},
// 点击导航栏按钮触发的事件
onNavigationBarButtonTap(){
this.$refs.share.open()
},
// 手机返回触发的事件
onBackPress(){
this.$refs.share.close()
},
methods: {
__init(data){
// 修改标题
// console.log('222',data)
uni.setNavigationBarTitle({
title:data.title
})
this.info = data
this.$H.get('/post/'+data.id).then(res=>{
this.info.content = res.detail.content
// 发现问题,vue没有检测到info下的images数据更新
// this.info.images = res.detail.images
// 解决方案一 对象嵌套数组,数组嵌套对象,使得vue检测到该数据进行了更新。
this.$set(this.info,'images',res.detail.images)
// console.log(this.info)
// 解决方案二,强制更新vue所有实例数据。
// this.$forceUpdate 强制更新vue所有实例数据
// 解决方案三,如果逻辑允许,直接把嵌套的深数据放到data的根层,这样就能检测到了。
})
},
// 子组件触发的关注事件
follow(){
this.info.isFollow = true
uni.showToast({
title:'关注成功'
})
},
// 子组件触发的顶踩事件
doSupport(e){
// 之前操作过
if (this.info.support.type === e.type) {
return uni.showToast({
title: '你已经操作过了',
icon: 'none'
});
}
let msg = e.type === 'support' ? '顶' : '踩'
// 之前没有操作过
if (this.info.support.type === '') {
this.info.support[e.type+'_count']++
}else if(this.info.support.type === 'support' && e.type === 'unsupport'){
// 顶 - 1
this.info.support.support_count--;
// 踩 + 1
this.info.support.unsupport_count++;
}else if(this.info.support.type === 'unsupport' && e.type === 'support'){
// 顶 + 1
this.info.support.support_count++;
// 踩 - 1
this.info.support.unsupport_count--;
}
this.info.support.type = e.type
uni.showToast({
title: msg
});
},
// 预览图片
preview(index){
// 预览图片
uni.previewImage({
current:index,
urls:this.imagesList
})
},
// 评论提交事件
submit(){
}
}
}
</script>
<style>
</style>
十 简单尝试分享到微信 微信朋友圈等
效果图:
要点代码:
1 调用定制的分享组件的打开下拉面板功能,并且传入要分享的信息
#ps: detail.vue
// 点击导航栏按钮触发的事件
onNavigationBarButtonTap(){
// console.log(this.info.titlepic)
//打开下拉框,以及传入分享需要的信息
this.$refs.share.open({
title:this.info.title,
shareText:this.info.content,
href:this.href ? this.href:'https://www.baidu.com/',
image:this.info.titlepic? this.info.titlepic:'https://ss0.bdstatic.com/94oJfD_bAAcT8t7mm9GUKT-xh_/timg?image&quality=100&size=b4000_4000&sec=1587542604&di=2601975188b590defa2e0cb432ccc1b3&src=http://pic.feizl.com/upload/allimg/170918/18362zf1lsf3e0a.jpg'
})
},
2 分享组件接受信息,并且分享,其余的定制化操作就根据需求更改了
#ps: more-share.vue
methods:{
open(options){
this.title = options.title // 分享的标题,分享到朋友圈只能显示标题。
// this.title = '全网寻找xxxx!!!'
this.shareText = options.shareText // 分享的文字
// this.shareText = '七年前xxxx骗俺在陕西开矿,俺投了钱,他不光骗了俺的钱,还骗了俺的身子。'
this.href = options.href // 分享跳转的链接
// this.href = 'https://www.cnblogs.com/zhangmingyan/articles/12752988.html'
this.image = options.image // 分享显示的缩略图
// console.log('xxxx')
this.$refs.popup.open()
},
全部代码
Detail.vue
<template>
<view>
<common-list :item='info' isdetail
@follow="follow"
@doSupport="doSupport">
<view class="flex font-md align-center">
{{info.title}}
</view>
<view class="flex font align-center">
{{info.content}}
</view>
<!-- widthFix这个只是裁剪方式,跟具体宽度无关 -->
<block v-for="(item,index) in info.images" :key="index">
<image :src="item.url" class='w-100' mode="widthFix"
@click="preview(index)"></image>
</block>
</common-list>
<divider></divider>
<!-- // copy官方评论组件 -->
<view class="p-2 font-md font-weight-bold">
最新评论 {{info.comment_count}}
</view>
<view class="px-2">
<view class="uni-comment-list">
<view class="uni-comment-face"><image src="https://img-cdn-qiniu.dcloud.net.cn/uniapp/images/uni@2x.png" mode="widthFix"></image></view>
<view class="uni-comment-body">
<view class="uni-comment-top">
<text>小猫咪</text>
</view>
<view class="uni-comment-content">支持国产,支持DCloud!</view>
<view class="uni-comment-date">
<view>2天前</view>
</view>
</view>
</view>
</view>
<!-- 空白高度占位,使得有内容的部分不要被底部评论框挡住。 -->
<view style="height: 100rpx;"></view>
<bottom-input @submit="submit"></bottom-input>
<!-- 分享下拉弹出框 -->
<more-share ref="share"></more-share>
</view>
</template>
<script>
import commonList from '@/components/common/common-list.vue'
import bottomInput from '@/components/common/bottom-input.vue'
import moreShare from '@/components/common/more-share.vue'
export default {
components:{
commonList,
bottomInput,
moreShare
},
data() {
return {
// 当前帖子信息
href:"",
info:{
id:"",
username:"",
userpic:"",
newstime:"",
isFollow:false,
title:"",
titlepic:"",
support:{
type:"support", // 顶
support_count:0,
unsupport_count:0
},
comment_count:0,
share_num:0,
content:"",
images:[]
}
}
},
computed:{
imagesList(){
//数组取出来每个元素,然后每个元素进行操作后,组成新的数组。
return this.info.images.map(item=>item.url)
}
},
// 接受传参
onLoad(e) {
// 初始化
if (e.detail){
this.__init(JSON.parse(e.detail))
}
},
// 点击导航栏按钮触发的事件
onNavigationBarButtonTap(){
// console.log(this.info.titlepic)
//打开下拉框,以及传入分享需要的信息
this.$refs.share.open({
title:this.info.title,
shareText:this.info.content,
href:this.href ? this.href:'https://www.baidu.com/',
image:this.info.titlepic? this.info.titlepic:'https://ss0.bdstatic.com/94oJfD_bAAcT8t7mm9GUKT-xh_/timg?image&quality=100&size=b4000_4000&sec=1587542604&di=2601975188b590defa2e0cb432ccc1b3&src=http://pic.feizl.com/upload/allimg/170918/18362zf1lsf3e0a.jpg'
})
},
// 手机返回触发的事件
onBackPress(){
this.$refs.share.close()
},
methods: {
__init(data){
// 修改标题
// console.log('222',data)
uni.setNavigationBarTitle({
title:data.title
})
this.info = data
this.$H.get('/post/'+data.id).then(res=>{
this.info.content = res.detail.content
// 发现问题,vue没有检测到info下的images数据更新
// this.info.images = res.detail.images
// 解决方案一 对象嵌套数组,数组嵌套对象,使得vue检测到该数据进行了更新。
this.$set(this.info,'images',res.detail.images)
// console.log(this.info)
// 解决方案二,强制更新vue所有实例数据。
// this.$forceUpdate 强制更新vue所有实例数据
// 解决方案三,如果逻辑允许,直接把嵌套的深数据放到data的根层,这样就能检测到了。
// console.log(this.info)
})
},
// 子组件触发的关注事件
follow(){
this.info.isFollow = true
uni.showToast({
title:'关注成功'
})
},
// 子组件触发的顶踩事件
doSupport(e){
// 之前操作过
if (this.info.support.type === e.type) {
return uni.showToast({
title: '你已经操作过了',
icon: 'none'
});
}
let msg = e.type === 'support' ? '顶' : '踩'
// 之前没有操作过
if (this.info.support.type === '') {
this.info.support[e.type+'_count']++
}else if(this.info.support.type === 'support' && e.type === 'unsupport'){
// 顶 - 1
this.info.support.support_count--;
// 踩 + 1
this.info.support.unsupport_count++;
}else if(this.info.support.type === 'unsupport' && e.type === 'support'){
// 顶 + 1
this.info.support.support_count++;
// 踩 - 1
this.info.support.unsupport_count--;
}
this.info.support.type = e.type
uni.showToast({
title: msg
});
},
// 预览图片
preview(index){
// 预览图片
uni.previewImage({
current:index,
urls:this.imagesList
})
},
// 评论提交事件
submit(){
},
// // 关闭评论框
// close(){
// this.$refs.share.close()
// }
}
}
</script>
<style>
</style>
Common/more-share.vue
<template>
<uni-popup ref="popup" type="bottom">
<view class="bg-light">
<view class="text-center py-2 font-md border-bottom border-light-secondary">分享到</view>
<view class="flex align-center">
<view class="flex-1 flex flex-column align-center justify-center py-2" v-for="(item,index) in providerList" :key="index" hover-class="bg-light" @tap="share(item)">
<view class="iconfont text-white flex align-center justify-center font-lg rounded-circle" :class="item.icon + ' ' + item.color" style="width: 100rpx;height: 100rpx;"></view>
<text class="font-sm mt-1 text-muted">{{item.name}}</text>
</view>
</view>
<view class="text-center py-2 font-md border-top border-light-secondary" hover-class="bg-light" @click="close">取消</view>
</view>
</uni-popup>
</template>
<script>
import uniPopup from '@/components/uni-ui/uni-popup/uni-popup.vue';
export default {
components:{
uniPopup
},
data() {
return {
title: '',
shareText: '',
href:'',
image: '',
shareType:0, //0为仅有 微信朋友圈以及微信 支持的图文分享
providerList:[],
}
},
computed:{
isDisableButton() {
return function(item) {
if(this.shareType === 0 && item.id === 'qq'){
return true;
}
if(this.shareType === 5 && item.name !== '分享到微信好友'){
return true;
}
return false;
}
}
},
onShareAppMessage() {
return {
title: this.shareText ? this.shareText : "欢迎体验uni-app",
path: '/pages/tabBar/component/component',
imageUrl:this.image ? this.image : 'https://img-cdn-qiniu.dcloud.net.cn/uniapp/app/share-logo@3.png'
}
},
// 销毁之前
beforeDestroy(){
this.shareText='uni-app可以同时发布成原生App、小程序、H5,邀请你一起体验!',
this.href = 'https://uniapp.dcloud.io',
this.image='';
},
mounted() {
uni.getProvider({
service: 'share',
success: (e) => {
console.log('###',e);
let data = []
for (let i = 0; i < e.provider.length; i++) {
switch (e.provider[i]) {
case 'weixin':
data.push({
name: '微信好友',
icon:"icon-weixin",
color:"bg-success",
id: 'weixin',
sort:0
})
data.push({
name: '朋友圈',
icon:"icon-huati",
color:"bg-dark",
id: 'weixin',
type:'WXSenceTimeline',
sort:1
})
break;
case 'sinaweibo':
data.push({
name: '新浪微博',
icon:"icon-xinlangweibo",
color:"bg-danger",
id: 'sinaweibo',
sort:2
})
break;
case 'qq':
data.push({
name: 'QQ好友',
icon:"icon-QQ",
color:"bg-primary",
id: 'qq',
sort:3
})
break;
default:
break;
}
}
this.providerList = data.sort((x,y) => {
return x.sort - y.sort
});
},
fail: (e) => {
console.log('%%%%',e)
uni.showModal({
content:'获取分享通道失败',
showCancel:false
})
}
});
},
methods:{
open(options){
this.title = options.title // 分享的标题
// this.title = '全网寻找xxxx!!!'
this.shareText = options.shareText // 分享的文字
// this.shareText = '七年前xxxx骗俺在陕西开矿,俺投了钱,他不光骗了俺的钱,还骗了俺的身子。'
this.href = options.href // 分享跳转的链接
// this.href = 'https://www.cnblogs.com/zhangmingyan/articles/12752988.html'
this.image = options.image // 分享显示的缩略图
// console.log('xxxx')
this.$refs.popup.open()
},
close(){
this.$refs.popup.close()
},
async share(e) {
console.log('分享通道:'+ e.id +'; 分享类型:' + this.shareType);
if(!this.shareText && (this.shareType === 1 || this.shareType === 0)){
uni.showModal({
content:'分享内容不能为空',
showCancel:false
})
return;
}
if(!this.image && (this.shareType === 2 || this.shareType === 0)){
uni.showModal({
content:'分享图片不能为空',
showCancel:false
})
return;
}
let shareOPtions = {
provider: e.id,
scene: e.type && e.type === 'WXSenceTimeline' ? 'WXSenceTimeline' : 'WXSceneSession', //WXSceneSession”分享到聊天界面,“WXSenceTimeline”分享到朋友圈,“WXSceneFavorite”分享到微信收藏
type: this.shareType,
success: (e) => {
console.log('success', e);
uni.showModal({
content: '分享成功',
showCancel:false
})
},
fail: (e) => {
console.log('fail', e)
uni.showModal({
content: e.errMsg,
showCancel:false
})
},
complete:function(){
console.log('分享操作结束!')
}
}
switch (this.shareType){
case 0:
shareOPtions.summary = this.shareText;
shareOPtions.imageUrl = this.image;
shareOPtions.title = this.title;
shareOPtions.href = this.href;
break;
case 1:
shareOPtions.summary = this.shareText;
break;
case 2:
shareOPtions.imageUrl = this.image;
break;
case 5:
shareOPtions.imageUrl = this.image ? this.image : 'https://img-cdn-qiniu.dcloud.net.cn/uniapp/app/share-logo@3.png'
shareOPtions.title = '欢迎体验uniapp';
shareOPtions.miniProgram = {
id:'gh_33446d7f7a26',
path:'/pages/tabBar/component/component',
webUrl:'https://uniapp.dcloud.io',
type:0
};
break;
default:
break;
}
if(shareOPtions.type === 0 && plus.os.name === 'iOS'){//如果是图文分享,且是ios平台,则压缩图片
shareOPtions.imageUrl = await this.compress();
}
if(shareOPtions.type === 1 && shareOPtions.provider === 'qq'){//如果是分享文字到qq,则必须加上href和title
shareOPtions.href = 'https://uniapp.dcloud.io';
shareOPtions.title = '欢迎体验uniapp';
}
uni.share(shareOPtions);
},
compress(){//压缩图片 图文分享要求分享图片大小不能超过20Kb
console.log('开始压缩');
let img = this.image;
return new Promise((res) => {
var localPath = plus.io.convertAbsoluteFileSystem(img.replace('file://', ''));
console.log('after' + localPath);
// 压缩size
plus.io.resolveLocalFileSystemURL(localPath, (entry) => {
entry.file((file) => {// 可通过entry对象操作图片
console.log('getFile:' + JSON.stringify(file));
if(file.size > 20480) {// 压缩后size 大于20Kb
plus.zip.compressImage({
src: img,
dst: img.replace('.jpg', '2222.jpg').replace('.JPG', '2222.JPG'),
width: '10%',
height: '10%',
quality: 1,
overwrite: true
}, (event) => {
console.log('success zip****' + event.size);
let newImg = img.replace('.jpg', '2222.jpg').replace('.JPG', '2222.JPG');
res(newImg);
}, function(error) {
uni.showModal({
content:'分享图片太大,需要请重新选择图片!',
showCancel:false
})
});
}
});
}, (e) => {
console.log('Resolve file URL failed: ' + e.message);
uni.showModal({
content:'分享图片太大,需要请重新选择图片!',
showCancel:false
})
});
})
}
}
}
</script>
<style>
</style>