27 首页交互+文章详情页(请求模块再度封装,多层数据嵌套无法更新解决方案($set,$forceUpdate),文章详情交互实现,分享到微信朋友+朋友圈)

27 首页交互+文章详情页

(请求模块再度封装,多层数据嵌套无法更新解决方案($set,$forceUpdate),文章详情交互实现,分享到微信朋友+朋友圈)

六 首页交互实现

效果图:

image-20200423170332625

要点:

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 实例重新渲染。注意它仅仅影响实例本身和插入插槽内容的子组件,而不是所有子组件。

九 文章详情交互实现(多层数据嵌套无法显示问题参考上一个标题)

效果图:

image-20200422130943259

image-20200422131020933

image-20200422131037110

要点

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>

十 简单尝试分享到微信 微信朋友圈等

效果图:

image-20200422165644558

image-20200422170931423

要点代码:

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>

posted @ 2020-04-24 12:41  张明岩  阅读(193)  评论(0编辑  收藏  举报