31 uni-app前端后端交互剩余主体业务逻辑相关笔记

31 前端后端剩余粗糙笔记

一 绑定邮箱

image-20200507210002049

<template>
	<view>
		<!-- 修改邮箱没有做细节处理 -->
		<input class="uni-input" type="text" v-model="email" placeholder="请输入你要绑定的邮箱" :disabled="this.user.email"/>
		<view class="py-2 px-3">
			<button class="bg-main text-white" style="border-radius: 50rpx;border: 0;" type="primary" :disabled="disabled || this.user.email" :class="disabled ? 'bg-main-disabled' : ''" @click="submit">绑定</button>
		</view>
	</view>
</template>

<script>
	import {mapState} from 'vuex';
	export default {
		data() {
			return {
				email:""
			}
		},
		computed: {
			...mapState({
				user:state=>state.user
			}),
			disabled() {
				return this.email === ''
			}
		},
		onLoad() {
			if(this.user.email){
				this.email = this.user.email
			}
		},
		methods: {
			check(){
				let rule = /^([A-Za-z0-9_\-\.])+\@([A-Za-z0-9_\-\.])+\.([A-Za-z]{2,4})$/;
				if (!rule.test(this.email)) {
					uni.showToast({
						title:"邮箱格式不正确",
						icon:"none"
					})
					return false;
				}
				return true
			},
			submit(){
				if (!this.check()) {
					return;
				}
				this.$H.post('/user/bindemail',{
					email:this.email
				},{
					token:true
				}).then(res=>{
					this.$store.commit('editUserInfo',{
						key:'email',
						value:this.email
					})
					uni.navigateBack({
						delta: 1
					});
					uni.showToast({
						title: '绑定邮箱成功',
						icon: 'none'
					});
				})
			}
		}
	}
</script>

<style>

</style>

二 绑定第三方数据(业务逻辑参考意义)

思路

如果该页面涉及多个后端接口,

可以考虑把初始化数据设置为一个__init()方法,

修改完了很多数据后可以重新调用__init()方法。

效果图:

image-20200507212346887

代码:

<template>
	<view>
		<uni-list-item :title="item.name" 
		v-for="(item,index) in list"
		:key="index" @click="handleEvent(item)">
			<view class="flex align-center text-right text-light-muted" 
			slot="right_content">
				{{item.data}}
			</view>
		</uni-list-item>
	</view>
</template>

<script>
	import uniListItem from '@/components/uni-ui/uni-list-item/uni-list-item.vue';
	import { mapState } from 'vuex'
	export default {
		components: {
			uniListItem
		},
		data() {
			return {
				list:[]
			}
		},
		computed: {
			...mapState({
				user:state=>state.user
			})
		},
		onShow() {
			this.__init()
		},
		methods: {
			// 封装为一个方法很有意义
			__init(){
				let list = [{
					name:"手机号",
					data:this.user.phone ? this.user.phone : "未绑定",
					type:"navigateTo",
					url:"/pages/user-phone/user-phone"
				},{ 
					name:"登录密码",
					data:this.user.password ? "修改密码" : "未设置",
					type:"navigateTo",
					url:"/pages/user-password/user-password"
				},{ 
					name:"邮箱绑定",
					data:this.user.email ? this.user.email : "未绑定",
					type:"navigateTo",
					url:"/pages/user-email/user-email"
				}]
				this.list = [...list]
				this.$H.get('/user/getuserbind',{},{
					token:true
				}).then(res=>{
					this.$store.commit('editUserInfo',{
						key:"user_bind",
						value:res
					})
					let other = [{
						name:"微信账号",
						data:this.user.user_bind.weixin ? this.user.user_bind.weixin.nickname:"未绑定",
						type:"bind",
						provider:"weixin"
					},{
						name:"微博账号",
						data:this.user.user_bind.sinaweibo ? this.user.user_bind.sinaweibo.nickname:"未绑定",
						type:"bind",
						provider:"sinaweibo"
					},{
						name:"QQ账号",
						data:this.user.user_bind.qq ? this.user.user_bind.qq.nickname:"未绑定",
						type:"bind",
						provider:"qq"
					}]
					this.list = [...this.list,...other]
				})
			},
			handleEvent(item){
				if(item.type === '') return
				switch (item.type){
					case 'navigateTo':
					uni.navigateTo({
						url: item.url,
					});
						break;
					case 'bind':
					if(item.data !== '未绑定'){
						return uni.showToast({
							title: '你已经绑定过了',
							icon: 'none'
						});
					}
					this.bind(item.provider)
						break;
				}
			},
			// 绑定第三方登录
			bind(provider){
				uni.login({
					provider: provider,
					success: r => {
						uni.getUserInfo({
							provider: provider,
							success:(res)=> {
								let obj = {
									provider:provider,
									openid:res.userInfo.openId,
									nickName:res.userInfo.nickName,
									avatarUrl:res.userInfo.avatarUrl,
								}
								this.$H.post('/user/bindother',obj,{
									token:true,
									native:true
								}).then(result=>{
									if(result.data.errorCode){
										return uni.showToast({
											title: result.data.msg,
											icon: 'none'
										});
									}
									// 完成了操作后可以再次初始化数据。
									// 初次加载页面需要初始化数据。
									// 完成了操作后可以根据后端数据再次初始化数据。(当然了也可以手动初始化前端数据,不过再走一便后端数据可以确定后端已经修改上了)
									this.__init()
									uni.showToast({
										title: '绑定成功',
										icon: 'none'
									});
								})
							}
						});
					},
				});
				
			}
		}
	}
</script>

<style>

</style>

三 修改头像接口(封装uni提供的上传图片请求)

image-20200508121406566

思路:

1 首先使用uni.chooseimage选择图片

2 使用自己封装的上传图片ajax,注意最后这个ajax返回的是promise对象,这样使用起来更方便(优秀)。

3 然后给index.js 更新一下user数据,然后存储到前端本地。

涉及的核心代码

Request.js

upload(url,options = {}){
		options.url = $C.webUrl + url
		options.header = options.header || {}
		// 验证权限token
		if(options.token){
			options.header.token = $store.state.token
			if(!options.header.token){
				return uni.showToast({
					title: '非法token,请重新登录',
					icon: 'none'
				});
			}
		}
		console.log(options)
		return new Promise((res,rej)=>{
			uni.uploadFile({
				...options,
				success: (uploadFileRes) => {
					console.log(uploadFileRes);
					if(uploadFileRes.statusCode !== 200){
						return uni.showToast({
							title: '上传图片失败',
							icon: 'none'
						});
					}
					let data = JSON.parse(uploadFileRes.data)
					res(data)
				},
				fail:(err)=>{
					rej(err)
				}
			});
		})
		
	}
	
}

User-userinfo.vue

methods: {
			changeUserpic(){
				// 拍照或者选择本地图片
				uni.chooseImage({
					count:1,
					sizeType:["compressed"],
					sourceType:["album","camera"],
					success: (res) => {
						this.$H.upload('/edituserpic',{
							filePath: res.tempFilePaths[0],
							name: 'userpic',
							token:true
						}).then(result=>{
							console.log('###',result);
							this.$store.commit('editUserInfo',{
								key:"userpic",
								value:result.data
							})
							uni.showToast({
								title: '修改头像成功',
								icon: 'none'
							});
						}).catch(err=>{
							console.log(err);
						})
						
					}
				})
			},

四 修改资料功能实现(日期框,三级联动地址)

效果图:

image-20200508154510681

关键点:

用了日期选框(调用系统原生的日期选择器,并不友好)

用了一个开源的三级联动(还ok吧)

代码

<template>
	<view>
		<uni-list-item title="头像" @click="changeUserpic">
			<!-- 为uni-list-item增加了插槽 -->
			<view class="flex align-center" slot="right_content">
				<image :src="user.userpic ? user.userpic : '/static/default.jpg' " class="rounded-circle" style="width: 100rpx; height: 100rpx;" mode=""></image>
				<text class="iconfont icon-bianji1 ml-2"></text>
			</view>
		</uni-list-item>
		<uni-list-item title="昵称" @click="changeUsername">
			<view class="flex align-center w-100" slot="right_content">
				<input ref="usernameInput" class="text-right" type="text " value="" v-model="username" />
				<text class="iconfont icon-bianji1 ml-2"></text>
			</view>
		</uni-list-item>
		<uni-list-item title="性别" @click="changeSex">
			<view class="flex align-center" slot="right_content" >
				<text class="font">{{this.sexText}}</text>
				<text class="iconfont icon-bianji1 ml-2"></text>
			</view>
		</uni-list-item>
		
		<!-- picker日期表单,不用导入调用的是各个平台的原生日期渲染,如果能用自己的日期选择器会更好。 -->
		<picker mode="date" :value="birthday" :start="startDate" :end="endDate" @change="onDateChange">
			<uni-list-item title="生日">
				<view class="flex align-center" slot="right_content">
					<text>{{birthday}}</text>
					<text class="iconfont icon-bianji1 ml-2"></text>
				</view>
			</uni-list-item>
		</picker>
			
			
		<!-- <picker mode="date" :value="birthday" :start="startDate" :end="endDate" @change="bindDateChange">
		                        <view class="uni-input">{{birthday}}</view>
		</picker> -->
			
			
		<uni-list-item title="情感 " @click='changeEmotion'>
			<view class="flex align-center" slot="right_content">
				<text>{{this.emotionText}}</text>
				<text class="iconfont icon-bianji1 ml-2"></text>
			</view>
		</uni-list-item>
		<uni-list-item title="职业" @click="changeJob">
			<view class="flex align-center" slot="right_content">
				<text>{{job}}</text>
				<text class="iconfont icon-bianji1 ml-2"></text>
			</view>
		</uni-list-item>
		<uni-list-item title="家乡" @tap="openAddres2">
			<view class="flex align-center" slot="right_content">
				<text>{{address}}</text>
				<text class="iconfont icon-bianji1 ml-2"></text>
			</view>
		</uni-list-item>
		<view class="py-2 px-3">
			<button class="bg-main text-white" style="border-radius: 50rpx;border: 0;" type="primary" @click="submit">完成</button>
		</view>
		
		<simple-address ref="simpleAddress" :pickerValueDefault="cityPickerValueDefault" @onConfirm="onConfirm" themeColor="#007AFF"></simple-address>
	</view>
</template>

<script>
	const sexArray = ['保密','男','女']
	const jobArray = ['农民','it','教师']
	const emotionArray = ['保密', '未婚', '已婚']
	import uniListItem from '@/components/uni-ui/uni-list-item/uni-list-item.vue'
	import simpleAddress from '@/components/other-author-tools/simple-address/simple-address.nvue'
	
	import { mapState } from 'vuex'
	export default {
		components:{
			uniListItem,
			simpleAddress
			
		},
		data() {
			return {
				username:"昵称",
				sex:0,
				emotion:0,
				job:"保密",
				birthday:"2019-01-01",
				startDate:"1900-01-01",
				endDate:"2100-01-01",
				cityPickerValueDefault: [0, 0, 1],
				address: '河南省-郑州市-中原区'
			}
		},
		computed:{
			...mapState({
				user:state=>state.user
			}),
			sexText(){
				return sexArray[this.sex]
			},
			emotionText(){
				return emotionArray[this.emotion]
			},
			
			
		},
		onLoad() {
			let userinfo = this.user.userinfo
			if(userinfo){
				this.address = userinfo.path
				this.username = this.user.username
				this.sex =  userinfo.sex
				this.emotion = userinfo.qg
				this.job  = userinfo.job
				this.birthday  = userinfo.birthday
			}
			console.log(userinfo)
		},
		methods: {
			changeUserpic(){
				// 拍照或者选择本地图片
				uni.chooseImage({
					count:1,
					sizeType:["compressed"],
					sourceType:["album","camera"],
					success: (res) => {
						this.$H.upload('/edituserpic',{
							filePath: res.tempFilePaths[0],
							name: 'userpic',
							token:true
						}).then(result=>{
							console.log('###',result);
							this.$store.commit('editUserInfo',{
								key:"userpic",
								value:result.data
							})
							uni.showToast({
								title: '修改头像成功',
								icon: 'none'
							});
						}).catch(err=>{
							console.log(err);
						})
						
					}
				})
			},
			// 修改昵称 不用理会
			changeUsername(){
				// this.$nextTick(function(){
					
				// 	console.log(this.$refs.usernameInput)
				// })
				// console.log('zzzzz')
				// this.$refs.username_input.focus()
				
				},
		    // 修改性别
			changeSex(){
				// 显示操作菜单
				uni.showActionSheet({
					itemList:sexArray,
					success: (res) => {
						this.sex = res.tapIndex
					}
				})
			},
			// 修改已婚未婚
			changeEmotion(){
				uni.showActionSheet({
					itemList:emotionArray,
					success:(res) => {
						this.emotion = res.tapIndex
					}
				})
			},
			// 修改职业
			changeJob(){
				uni.showActionSheet({
					itemList:jobArray,
					success: (res) => {
						this.job = jobArray[res.tapIndex]
					}
				})
			},
			// 修改日期
			onDateChange(e){
				// console.log(e.detail)
				this.birthday = e.detail.value
			},
			
			
			// 三级联动
			onConfirm(e) {
				// console.log(JSON.stringify(e))
				// console.log(e)
				this.address = e.label
			},
			// 调用该三级联动组件
			openAddres2() {
				// 根据 label 获取
				var index = this.$refs.simpleAddress.queryIndex(this.address.split('-'), 'label');
				// console.log(index);
				// 设置默认打开的地址
				this.cityPickerValueDefault = index.index;
				// 打开三级联动事件
				this.$refs.simpleAddress.open();
			},
			
			// 提交
			submit(){
				let obj = {
					name:this.username,
					sex:this.sex,
					qg:this.emotion,
					job:this.job,
					birthday:this.birthday,
					path:this.address
				}
				this.$H.post('/edituserinfo',obj,{
					token:true,
					native:true
				}).then(res=>{
					console.log(res);
					this.$store.commit('editUserInfo',{
						key:"username",
						value:this.username
					})
					this.$store.commit('editUserUserInfo',obj)
					uni.navigateBack({
						delta: 1
					});
					uni.showToast({
						title: '修改资料成功',
						icon: 'none'
					});
				})
			}
			
		}
	}
</script>

<style>

</style>

五 页面通讯(重要)

uni.$off([eventName, callback]]

移除全局自定义事件监听器。

属性 类型 描述
eventName Array<String> 事件名
callback Function 事件的回调函数

Tips

  • 如果没有提供参数,则移除所有的事件监听器;
  • 如果只提供了事件,则移除该事件所有的监听器;
  • 如果同时提供了事件与回调,则只移除这个回调的监听器;
  • 提供的回调必须跟$on的回调为同一个才能移除这个回调的监听器;
<template>
	<view class="content">
		<view class="data">
			<text>{{val}}</text>
		</view>
		<button type="primary" @click="comunicationOff">结束监听</button>
		<button type="primary" @click="myclick">手动点击</button>
	</view>
</template>

<script>
	export default {
		data() {
			return {
				val: 0
			}
		},
		onLoad() {
			// setInterval(()=>{
			// 	uni.$emit('add', {
			// 		data: 2
			// 	})
			// },5000)
			uni.$on('add', this.add)
			// uni.$on('add', (e)=>{
			//	console.log('YYYY')
			//	this.val += e.data
			// },)
		},
		methods: {
			comunicationOff() {
				// 只有这样才会终止掉拥有该回掉函数的监听之间(也就是只有这样才会只终止掉某个监听事件)
				// 如果只写事件名不写回掉,则所有该监听事件无论哪个页面都会终止掉。
				// 如果写事件名,随便写了个回掉,则无效。
				uni.$off('add', this.add)
			},
			// add(e) {
			// 	console.log('YYYY')
			// 	this.val += e.data
			// },
			myclick(e){
				uni.$emit('add', {
					data: 2
				})
			}
		}
	}
</script>

<style>
	.content {
		display: flex;
		flex-direction: column;
		align-items: center;
		justify-content: center;
	}

	.data {
		text-align: center;
		line-height: 40px;
		margin-top: 40px;
	}

	button {
		width: 200px;
		margin: 20px 0;
	}
</style>

六 关注点赞点踩(多组件进行通讯,很有参考价值的业务逻辑。)

效果图:

关注效果图:

image-20200508172425090

image-20200508172401084

点赞点踩效果图:

对各个页面已经加载的数据点赞点踩同步就OK。

思路

应用场景:

有很多个页面,已经不是通知父组件就能很方便的同步时候。

比如,a、b、c 三个页面,如果某一个组件处修改了数据后,此时abc三个页面已经加载了一些数据,这个时候需要手动监听并且修改abc这三个页面相关数据,从后端同步过来另当别论。

关注逻辑:

1 比如一个页面点击了关注,其他页面也要相应的把该作者的内容进行关注。

2 这个时候的其他页面可能有很多,已经不止该common-list.vue的父组件了,所以要及时更新的页面要用到uni.$on来进行更新。触发用正常的emit,监听用uni.$on(‘事件’,‘完成监听使用的回掉函数’)

3 当需要更新的页面卸载的时候,务必也要调用 uni.$off('之前监听的事件名字',‘要移除掉的某个监听事件使用的回掉函数’)

点赞点踩逻辑:

同上

精华代码:

Common-list.vue

// 关注事件
			follow(){
				//console.log('点击了关注')
				// 通知父组件
				// 通过权限验证,提交后台已经关注
				console.log(this.item)
				this.checkAuth(()=>{
					this.$H.post('/follow',{
						follow_id:this.item.user_id
					},{
						token:true,
						native:true
					}).then(res=>{
						console.log(res)
						// 通知详情页面更新
						if (this.isdetail){
							this.$emit('follow')
						}
						// 
						// 通知更新
						uni.$emit('updateFollowOrSupport',{
							type:"follow",
							data:{
								user_id:this.item.user_id
							}
						})
				})})

index.vue

onLoad() {
			uni.getSystemInfo({
				success:res=>{
								// 可用窗口高度(屏幕高度-导航栏高度-底部栏高度。) -  选项卡高度
					this.scrollH = res.windowHeight - uni.upx2px(101)
					// console.log(this.scrollH)
				}
			})
			// 根据选项生成列表
			this.getData()
			
			// 监听点踩操作进行数据更新。 
			uni.$on('updateFollowOrSupport' ,this.listionFollowOrSupport)
			
		},
		onUnload() {
     																	 // ps: 只有写了事件名和回掉函数对应的监听事件,才能准确的
                                       //      移除掉使用了该回掉函数函数的那一个监听事件。
      // 这里的回掉并不会执行,你可以这么理解,只是回掉函数地址+事件名来精准的定位到一个需要释放的监听事件
			uni.$off('updateFollowOrSupport',this.listionFollowOrSupport)
		},
      
methods: {
			// 封装成回掉得意移除页面的时候停止监听
			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
				}
			},
      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
						}
					})
				})
			},
      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+'成功'
				})
				
				
			}
      

detail.vue

onLoad(e) {
			// 初始化
			if (e.detail){
				this.__init(JSON.parse(e.detail))
			}
			
			// 监听关注顶踩操作。
				
			uni.$on('updateFollowOrSupport',this.listionFollowOrSupport)
			
		},
onUnload() {
			uni.$off('updateFollowOrSupport',this.listionFollowOrSupport)
		},
      
methods: {
			listionFollowOrSupport(e){
				console.log('detail.vue 接受到了')
				switch (e.type){
					case 'follow': // 关注
					this.follow(e.data.user_id)
						break;
					case 'support':
					this.doSupport(e.data)
					default:
						break
				}
			},
      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
				});
			},

search.vue

onUnload() {
			if(this.type === 'post'){
				uni.$off('updateFollowOrSupport')
			}
		},
onLoad(e) {
        if (e.type) {
          this.type = e.type
        }
        let pageTitle = '帖子'
        switch (this.type){
          case 'post':
            pageTitle = '帖子'
            uni.$on('updateFollowOrSupport',(e)=>{
              switch (e.type){
                case 'follow': // 关注
                  this.follow(e.data.user_id)
                  break;
                default:
                  break;
              }
            })
            break;
          case 'topic':
            pageTitle = '话题'
            break;
          case 'user':
            pageTitle = '用户'
            break;
        }
  
  
methods:{
  follow(user_id){
				// 找到当前作者的所有列表
				this.searchList.forEach((item)=>{
					if(item.user_id === user_id){
						item.isFollow = true
					}
				})
				uni.showToast({ title: '关注成功' })
			},
    
  }

七 判断一个对象是否为空

1 JSON.stringfy判断

    if (JSON.stringify(data) === '{}') {
        return false // 如果为空,返回false
    }
    return true // 如果不为空,则会执行到这一步,返回true

2 通过object.keys(),如果为空则会返回空【】,就可以length判断了

如果我们的对象为空,他会返回一个空数组,如下:

    var a = {}
    Object.keys(a) // []
我们可以依靠Object.keys()这个方法通过判断它的长度来知道它是否为空。

    if (Object.keys(object).length === 0) {
        return false // 如果为空,返回false
    }
    return true // 如果不为空,则会执行到这一步,返回true

八 发布文章功能实现(选择图片直接上传功能,可以关注草稿功能)

效果图:

image-20200509214158691

image-20200509214223034

image-20200509214252692

思路:

1 重新修改了一下上传图片组件,直接选中就可以请求后端。

2 做了一个谁可见的访问权限的选择列表(uni.showActionSheet实现)

3 做了一个选择课程类型的普通picker框(滚动选择)

3 然后话题直接用的之前的topic.nav 页面,然后用一个choose:true参数来控制

4 还有当跳转过去获取想要的数据后,使用$on进行跨组件传参。

Code add-input:

<template>
	<view>
		<!-- 自定义导航条 -->
		<uni-nav-bar left-icon="back" :border="false" :statusBar="true" @click-left="goBack">
			<view class="flex align-center justify-center w-100" @click="changeIsopen">
				<text class="font">{{isOpenText}}</text><text class="iconfont icon-shezhi ml-1"></text>
			</view>
		</uni-nav-bar>
		
		<!-- 文本域 -->
		<view class="px-1">
			<!-- 不要给textarea 直接设置px-1 否则会出现横向滚动条问题。关于这个文本域配置到时候实操的时候再说 -->
			<textarea v-model="content"   placeholder="说一句话吧" class="uni-textarea"/>
		</view>
		
		
		<!-- 选中的分类 -->
		<view class="font-md px-2 py-1 flex">
			<view class="border px-3 py main-color main-border-color flex align-center justify-center" style="border-radius: 50rpx;">
				<text class="iconfont icon-huati mr-1"></text>
				{{post_class_name ? "所属分类:"+post_class_name : "请选择分类"}}
			</view>
		</view>
		
		<!-- 选中的话题 -->
		<view class="font-md px-2 py-1 flex">
			<view class="border px-3 py main-color main-border-color flex align-center justify-center" style="border-radius: 50rpx;">
				<text class="iconfont icon-huati mr-1"></text>
				{{topic.title ? "所属话题:"+topic.title : "请选择话题"}}
			</view>
		</view>
		
		<!-- 多图上传 --> 
		<upload-image :list="imageList" :show="showTag" ref="uploadImage" @change="changeImage" ></upload-image>
		
		<!-- 底部操作条 -->
		<view class="fixed-bottom bg-white flex align-center" style="height: 85rpx;"> 
			<!-- 选择文章分类 -->
			<picker mode="selector" :range="post_class_list" @change="choosePostclass">
				<view class="iconfont icon-caidan footer-btn animated " hover-class="jello"></view>
			</picker>
			<!-- 选择文章话题 -->
			<view class="iconfont icon-huati footer-btn animated " hover-class="jello" @click="chooseTopic"></view>
			<view class="iconfont icon-tupian footer-btn animated " hover-class="jello" @click="iconClickEvent('uploadImage')"></view>
			<!-- 小点  当一行flex元素作用浮动可以使用margin-right 或者 margin-left 靠左或者靠右边 -->
			<!-- 发送按钮 -->
			<view 
			class="bg-main text-white ml-auto flex justify-center align-center rounded mr-2 animated" 
			hover-class="jello" 
			style="width: 140rpx;height: 60rpx;"
			@click="submit">发送</view>
		</view>
		

	</view>
</template>

<script>
	const isOpenArray = ['仅自己可见' , '所有人可见']
	import uniNavBar from '@/components/uni-ui/uni-nav-bar/uni-nav-bar.vue'
	import uploadImage from '@/components/common/upload-image.vue'
	
	export default {
		components:{
			uniNavBar,
			uploadImage
		},
		data() {
			return {
				content:"",
				imageList:[],
				backTag:true,
				isopen:1,
				post_class_list:[],
				post_class_index:-1,
				topic:{
					title:'',
					id:0
				},
				
			}
		},
		computed:{
			showTag() {
				return this.imageList.length>0
			},
			isOpenText(){
				return isOpenArray[this.isopen]
			},
			post_class_name(){
				if(this.post_class_index !== -1){
					return this.post_class_list[this.post_class_index]
				}
			}
			
		},
		// 监听返回
		onBackPress(){
			if ((this.content!='' || this.imageList.length >0) && this.backTag){
				// 异步的
				uni.showModal({
					content:'是否要保存为草稿?',
					showCancel:true,
					
					cancelText:'不保存',
					confirmText:'保存',
					success: (res) => {
						// 点击确认
						if (res.confirm) {
							// 保存到本地缓存。
							this.store()
						}else { // 点击取消,清除本地缓存
							uni.removeStorage({
								key:"add-input"
							})
							
						}
						
						// 手动执行返回
						uni.navigateBack({
							delta:1
						})
					}	,
					
				})
				// 到这里相当于提示框已经出现过或者将要出现了。第二次阻止后退就不应该了
				this.backTag = false
				return true // 返回true阻止默认行为
			}
			
		},
		// 页面加载的时
		onLoad() {
			uni.getStorage({
				key:"add-input",
				success: (res) => {
					console.log(res)
					if (res.data){
						let result = JSON.parse(res.data)
						this.content = result.content
						this.imageList = result.imageList
					}
				}
			})
			uni.$on('chooseTopic',this.chooseTopicBack)
			
			// 获取所有分类
			this.getPostClass();
		},
		onUnload() {
		    uni.$off('chooseTopic',this.chooseTopicBack)
		},
		methods: {
			goBack() {
				uni.navigateBack({
					delta: 1
				})
			},
			//选中图片
			changeImage(e){
				this.imageList = e
				// console.log('changeimage',e)
			},
			// 
			store(){
				let obj = {
					content:this.content,
					imageList:this.imageList
				}
				// 异步新增本地缓存。
				uni.setStorage({
					key:'add-input',
					data:JSON.stringify(obj)
				})
			},
			// 底部图片点击时间
			iconClickEvent(e){ 
				switch (e){
					case 'uploadImage':
					// 通过这宗方式使用子组件里面的方法。
					this.$refs.uploadImage.chooseImage()
						break;
					
				}
			},
			// 切换是否所有人可见权限
			changeIsopen(){
				uni.showActionSheet({
					itemList:isOpenArray,
					success:(res)=>{
						this.isopen = res.tapIndex
					}
				})
			},
			
			// 获取文章分类
			getPostClass(){
				this.$H.get('/postclass').then(res=>{
					// console.log(res)
					this.post_class_list = res.list.map(item=>{
						return item.classname
					})
				}).catch(err=>{
					console.log(err)
				})
			},
			
			// 选择文章类型
			choosePostclass(e){
				this.post_class_index = e.detail.value
				
			},
			
			// 选择文章话题
			chooseTopic(e){
				uni.navigateTo({
					url:'../topic-nav/topic-nav?choose=true'
				})
			},
			
			// 监听选择的话题
			chooseTopicBack(res){
				this.topic = res
			},
			
			// 发送
			submit(){
				if(this.topic.id == 0){
					return uni.showToast({
						title: '请选择话题',
						icon: 'none'
					});
				}
				if(this.post_class_index == -1){
					return uni.showToast({
						title: '请选择分类',
						icon: 'none'
					});
				}
				uni.showLoading({
					title: '发布中...',
					mask: false
				});
				let imageList = this.imageList.map(item=>{
					return {id:item.id}
				})
				console.log(imageList)
				this.$H.post('/post/create',{
					// 发送
					imglist:JSON.stringify(imageList),
					text:this.content,
					isopen:this.isopen,
					topic_id:this.topic.id,
					post_class_id:this.post_class_index	
				},{
					token:true
				}).then(res=>{
					uni.hideLoading()
					this.backTag = false
					uni.navigateBack({
						delta:1
					})
					uni.showToast({
						title:'发布成功'
					})
				}).catch(err=>{
					console.log(err)
					uni.hideKeyboard()
				})
			}
			
		}
	}
</script>

<style>
.footer-btn{
	height:86rpx;
	width:86rpx;

	display:flex;
	justify-content: center;
	align-items:center;
	font-size:50rpx;
	
}

</style>

code upload-image.vue

<template>
	<view>

		<view class="py-2" v-if="show">
			<view class="uni-uploader">
				<view class="uni-uploader-head">
					<view class="uni-uploader-title p-1">点击可预览选好的图片</view>
					<view class="uni-uploader-info">{{imageList.length}}/9</view>
				</view>
				<view class="uni-uploader-body">
					<view class="uni-uploader__files">
						<block v-for="(image,index) in imageList" :key="index">
							<view class="uni-uploader__file position-relative">
								<image class="uni-uploader__img rounded " mode="aspectFill" :src="image.url" :data-src="image" @tap="previewImage"></image>
								<!-- 删除图片的地方 -->
								<view class="position-absolute top-0 right-0 rounded px-1" style="background-color: rgba(0,0,0,0.5); " 
								@click.stop="deleteImage(index)">
									<text class="iconfont icon-shanchu text-white"></text>
								</view>
							</view>
							
						</block>
						<view class="uni-uploader__input-box rounded">
							<view class="uni-uploader__input" @tap="chooseImage"></view>
						</view>
					</view>
				</view>
			</view>

		</view>
	</view>
</template>
<script>
	import permision from "@/common/permission.js"
	var sourceType = [
		['camera'],
		['album'],
		['camera', 'album']
	]
	var sizeType = [
		['compressed'],
		['original'],
		['compressed', 'original']
	]
	export default {
		props:{
			list:Array,
			show:{
				type:Boolean,
				default:true
			}
		},
		data() {
			return {
				title: 'choose/previewImage',
				imageList: [],
				sourceTypeIndex: 2,
				sourceType: ['拍照', '相册', '拍照或相册'],
				sizeTypeIndex: 2,
				sizeType: ['压缩', '原图', '压缩或原图'],
				countIndex: 8,
				count: [1, 2, 3, 4, 5, 6, 7, 8, 9]
			}
		},
		mounted() {
			 // console.log('onready onready')
			 this.imageList = this.list
			 console.log('测试',this.imageList)
		},
		onUnload() {
			this.imageList = [],
				this.sourceTypeIndex = 2,
				this.sourceType = ['拍照', '相册', '拍照或相册'],
				this.sizeTypeIndex = 2,
				this.sizeType = ['压缩', '原图', '压缩或原图'],
				this.countIndex = 8;
		},
		methods: {
			// 删除图片
			deleteImage(index){
				uni.showModal({
					title:'提示',// 
					content:'是否删除该图片?', //
					showCancel:true, // 
					cancelText:'不删除',
					confirmText:'删除',
					success:res => {
						if (res.confirm){
							this.imageList.splice(index,1)
							this.$emit('change',this.imageList)
						}
					}
				})
			},
			chooseImage: async function() {
				// #ifdef APP-PLUS
				// TODO 选择相机或相册时 需要弹出actionsheet,目前无法获得是相机还是相册,在失败回调中处理
				if (this.sourceTypeIndex !== 2) {
					let status = await this.checkPermission();
					if (status !== 1) {
						return;
					}
				}
				// #endif

				if (this.imageList.length === 9) {
					let isContinue = await this.isFullImg();
					console.log("是否继续?", isContinue);
					if (!isContinue) {
						return;
					}
				}
				uni.chooseImage({
					sourceType: sourceType[this.sourceTypeIndex],
					sizeType: sizeType[this.sizeTypeIndex],
					count: this.imageList.length + this.count[this.countIndex] > 9 ? 9 - this.imageList.length : this.count[this.countIndex],
					success: (res) => {
						// console.log(res)
						// 选择照片成功后直接到这个接口发送给后端并且通知父组件,不如直接传到阿里云里这个位置,暂时先这么着把
						res.tempFilePaths.forEach(item=>{
							this.$H.upload('/image/uploadmore',{
								filePath: item,
								name: 'imglist[]',
								token:true
							}).then(result=>{
								// console.log(result)
								// 上传成功后组织该页面的imageList
								this.imageList.push(result.data.list[0])
								// 然后通知父组件
								this.$emit('change',this.imageList)
							}).catch(err=>{
								console.log(err)
							})
						})
						
						
						
						// this.imageList = this.imageList.concat(res.tempFilePaths);
						// 选择好后通知父组件
						// this.$emit('change', this.imageList)
					},
					fail: (err) => {
						// #ifdef APP-PLUS
						if (err['code'] && err.code !== 0 && this.sourceTypeIndex === 2) {
							this.checkPermission(err.code);
						}
						// #endif
						// #ifdef MP
						uni.getSetting({
							success: (res) => {
								let authStatus = false;
								switch (this.sourceTypeIndex) {
									case 0:
										authStatus = res.authSetting['scope.camera'];
										break;
									case 1:
										authStatus = res.authSetting['scope.album'];
										break;
									case 2:
										authStatus = res.authSetting['scope.album'] && res.authSetting['scope.camera'];
										break;
									default:
										break;
								}
								if (!authStatus) {
									uni.showModal({
										title: '授权失败',
										content: 'Hello uni-app需要从您的相机或相册获取图片,请在设置界面打开相关权限',
										success: (res) => {
											if (res.confirm) {
												uni.openSetting()
											}
										}
									})
								}
							}
						})
						// #endif
					}
				})
			},
			isFullImg: function() {
				return new Promise((res) => {
					uni.showModal({
						content: "已经有9张图片了,是否清空现有图片?",
						success: (e) => {
							if (e.confirm) {
								this.imageList = [];
								res(true);
							} else {
								res(false)
							}
						},
						fail: () => {
							res(false)
						}
					})
				})
			},
			previewImage: function(e) {
				var current = e.target.dataset.src
				uni.previewImage({
					current: current,
					urls: this.imageList
				})
			},
			async checkPermission(code) {
				let type = code ? code - 1 : this.sourceTypeIndex;
				let status = permision.isIOS ? await permision.requestIOS(sourceType[type][0]) :
					await permision.requestAndroid(type === 0 ? 'android.permission.CAMERA' :
						'android.permission.READ_EXTERNAL_STORAGE');

				if (status === null || status === 1) {
					status = 1;
				} else {
					uni.showModal({
						content: "没有开启权限",
						confirmText: "设置",
						success: function(res) {
							if (res.confirm) {
								permision.gotoAppSetting();
							}
						}
					})
				}

				return status;
			}
		}
	}
</script>

<style>
	.cell-pd {
		padding: 22upx 30upx;
	}

	.list-pd {
		margin-top: 50upx;
	}
</style>

code 话题相关代码(略)

九 实现我的帖子数,动态,评论,粉丝数量页面(watch扩展用法)

效果图(略)

思路:

每当显示页面的时候根据登录状态去请求数据并且渲染。

退出的时候watch登录状态,如果为非登录状态则清空数据。

代码:

onShow(){
			// 获取当前的评论数各种数据
			// 如果登录了
			if(this.loginStatus){
				this.getCounts()
			}	
		}



watch:{
			// 监听(新值,旧值)
			loginStatus(newValue,oldValue) {
				console.log(newValue,oldValue)
				if(newValue){
					this.getCounts()
				}else{
					this.myData.forEach(item=>{
						item.num = 0
					})
				}
			}
			
		},

十 评论实现

明天回来把那啥组件传送信息完善了。。。。。。妈的

整理前的数据

{
	"list": [{
		"id": 574,
		"user_id": 328,
		"fid": 0,
		"fnum": 1,
		"data": "哈哈哈哈",
		"create_time": 1578619011,
		"post_id": 272,
		"user": {
			"id": 328,
			"username": "掌上",
			"userpic": "https://ceshi2.dishait.cn//uploads/20200107/d0698c09b65f209a37f9646e8eb95af0.jpg"
		}
	}, {
		"id": 575,
		"user_id": 328,
		"fid": 0,
		"fnum": 1,
		"data": "哈哈哈还好",
		"create_time": 1578619225,
		"post_id": 272,
		"user": {
			"id": 328,
			"username": "掌上",
			"userpic": "https://ceshi2.dishait.cn//uploads/20200107/d0698c09b65f209a37f9646e8eb95af0.jpg"
		}
	}, {
		"id": 576,
		"user_id": 328,
		"fid": 0,
		"fnum": 1,
		"data": "哥赶紧看看换个",
		"create_time": 1578619261,
		"post_id": 272,
		"user": {
			"id": 328,
			"username": "掌上",
			"userpic": "https://ceshi2.dishait.cn//uploads/20200107/d0698c09b65f209a37f9646e8eb95af0.jpg"
		}
	}, {
		"id": 577,
		"user_id": 328,
		"fid": 0,
		"fnum": 0,
		"data": "u好炒粉干",
		"create_time": 1578619268,
		"post_id": 272,
		"user": {
			"id": 328,
			"username": "掌上",
			"userpic": "https://ceshi2.dishait.cn//uploads/20200107/d0698c09b65f209a37f9646e8eb95af0.jpg"
		}
	}, {
		"id": 578,
		"user_id": 328,
		"fid": 0,
		"fnum": 0,
		"data": "滚滚滚",
		"create_time": 1578620553,
		"post_id": 272,
		"user": {
			"id": 328,
			"username": "掌上",
			"userpic": "https://ceshi2.dishait.cn//uploads/20200107/d0698c09b65f209a37f9646e8eb95af0.jpg"
		}
	}, {
		"id": 579,
		"user_id": 328,
		"fid": 574,
		"fnum": 1,
		"data": "好好干方",
		"create_time": 1578620589,
		"post_id": 272,
		"user": {
			"id": 328,
			"username": "掌上",
			"userpic": "https://ceshi2.dishait.cn//uploads/20200107/d0698c09b65f209a37f9646e8eb95af0.jpg"
		}
	}, {
		"id": 580,
		"user_id": 328,
		"fid": 579,
		"fnum": 0,
		"data": "哈哈哈哈唱个歌和我",
		"create_time": 1578620846,
		"post_id": 272,
		"user": {
			"id": 328,
			"username": "掌上",
			"userpic": "https://ceshi2.dishait.cn//uploads/20200107/d0698c09b65f209a37f9646e8eb95af0.jpg"
		}
	}, {
		"id": 581,
		"user_id": 328,
		"fid": 575,
		"fnum": 0,
		"data": "不会",
		"create_time": 1578621432,
		"post_id": 272,
		"user": {
			"id": 328,
			"username": "掌上",
			"userpic": "https://ceshi2.dishait.cn//uploads/20200107/d0698c09b65f209a37f9646e8eb95af0.jpg"
		}
	}, {
		"id": 582,
		"user_id": 328,
		"fid": 0,
		"fnum": 0,
		"data": "海港城",
		"create_time": 1578621462,
		"post_id": 272,
		"user": {
			"id": 328,
			"username": "掌上",
			"userpic": "https://ceshi2.dishait.cn//uploads/20200107/d0698c09b65f209a37f9646e8eb95af0.jpg"
		}
	}, {
		"id": 583,
		"user_id": 328,
		"fid": 576,
		"fnum": 0,
		"data": " 几号发货",
		"create_time": 1578623609,
		"post_id": 272,
		"user": {
			"id": 328,
			"username": "掌上",
			"userpic": "https://ceshi2.dishait.cn//uploads/20200107/d0698c09b65f209a37f9646e8eb95af0.jpg"
		}
	}]
}

整理后的数据

[{
	"id": 574,
	"fid": 0,
	"userid": 328,
	"userpic": "https://ceshi2.dishait.cn//uploads/20200107/d0698c09b65f209a37f9646e8eb95af0.jpg",
	"username": "掌上",
	"time": "2020-01-10 上午 9:16",
	"data": "哈哈哈哈"
}, {
	"id": 579,
	"fid": 574,
	"userid": 328,
	"userpic": "https://ceshi2.dishait.cn//uploads/20200107/d0698c09b65f209a37f9646e8eb95af0.jpg",
	"username": "掌上",
	"time": "2020-01-10 上午 9:43",
	"data": "好好干方"
}, {
	"id": 580,
	"fid": 579,
	"userid": 328,
	"userpic": "https://ceshi2.dishait.cn//uploads/20200107/d0698c09b65f209a37f9646e8eb95af0.jpg",
	"username": "掌上",
	"time": "2020-01-10 上午 9:47",
	"data": "哈哈哈哈唱个歌和我"
}, {
	"id": 575,
	"fid": 0,
	"userid": 328,
	"userpic": "https://ceshi2.dishait.cn//uploads/20200107/d0698c09b65f209a37f9646e8eb95af0.jpg",
	"username": "掌上",
	"time": "2020-01-10 上午 9:20",
	"data": "哈哈哈还好"
}, {
	"id": 581,
	"fid": 575,
	"userid": 328,
	"userpic": "https://ceshi2.dishait.cn//uploads/20200107/d0698c09b65f209a37f9646e8eb95af0.jpg",
	"username": "掌上",
	"time": "2020-01-10 上午 9:57",
	"data": "不会"
}, {
	"id": 576,
	"fid": 0,
	"userid": 328,
	"userpic": "https://ceshi2.dishait.cn//uploads/20200107/d0698c09b65f209a37f9646e8eb95af0.jpg",
	"username": "掌上",
	"time": "2020-01-10 上午 9:21",
	"data": "哥赶紧看看换个"
}, {
	"id": 583,
	"fid": 576,
	"userid": 328,
	"userpic": "https://ceshi2.dishait.cn//uploads/20200107/d0698c09b65f209a37f9646e8eb95af0.jpg",
	"username": "掌上",
	"time": "2020-01-10 上午 10:33",
	"data": " 几号发货"
}, {
	"id": 577,
	"fid": 0,
	"userid": 328,
	"userpic": "https://ceshi2.dishait.cn//uploads/20200107/d0698c09b65f209a37f9646e8eb95af0.jpg",
	"username": "掌上",
	"time": "2020-01-10 上午 9:21",
	"data": "u好炒粉干"
}, {
	"id": 578,
	"fid": 0,
	"userid": 328,
	"userpic": "https://ceshi2.dishait.cn//uploads/20200107/d0698c09b65f209a37f9646e8eb95af0.jpg",
	"username": "掌上",
	"time": "2020-01-10 上午 9:42",
	"data": "滚滚滚"
}, {
	"id": 582,
	"fid": 0,
	"userid": 328,
	"userpic": "https://ceshi2.dishait.cn//uploads/20200107/d0698c09b65f209a37f9646e8eb95af0.jpg",
	"username": "掌上",
	"time": "2020-01-10 上午 9:57",
	"data": "海港城"
}]

思路:

1 一定是从后端开始设计的

2 前端根据数据进行样式渲染

3 渲染完成后通知index.vue页面

3 需要注意的是问题宝典里面的内容

// 问题宝典 1
			 // 1 点击了别处已经失去了焦点,但是如果不手动设置focus依然为true
			 // 2 如果focus不为false,把focus更新为true无效,所以失去焦点的时候一定要把focus设置为false
			 // 3 问题出在了我已经获取到了焦点,但是点击了别的评论这个时候,
				// 1 先经历了个blur失去焦点自动促发
				// 2 又经历了个点击reply获取焦点的事件,但是获取焦点无效。
				// 我的目的:”点击了别的评论不要收起键盘,不收起键盘的这个事儿是目前没辙,
				// 目前的替代方案是用nextTick来解决等待数据都渲染完成,然后再执行获取焦点事件。但是会导致慢。“
// 问题宝典 2 (待定)
			// 有时候点击弹起不弹起问题

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> -->
		
		<!-- // copy 评论组件 -->
		<view class="px-2">
			<view class="uni-comment-list" 
			v-for="(item,index) in comments"
			:key="index">
				<view v-if="item.fid" style="width: 60rpx;"></view>
				<view class="flex w-100"
				:class="item.fid ? 'bg-light rounded p-2' : ''">
					<view class="uni-comment-face"><image :src="item.userpic"></image></view>
					<view class="uni-comment-body">
						<view class="uni-comment-top">
							<text>{{item.userid===info.user_id ?'楼主':item.username}}</text>
						</view>
						<view class="uni-comment-content "
						@click="reply(item.id,item.userid===info.user_id ?'楼主':item.username)"
						hover-class="text-main">{{item.data}}</view>
						<view class="uni-comment-date">
							<view>{{item.time}}</view>
						</view>
					</view>
				</view>
			</view>
			
		</view>
		<input  class="border" type="text" ref="input">
		
		<!-- 空白高度占位,使得有内容的部分不要被底部评论框挡住。 -->
		<view style="height: 100rpx;"></view>
		<bottom-input :to-user="toUser"  :focus='focus' @blur="blur" @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'
	import $T from '@/common/time.js';
	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:[]
				},
				toUser:"请输入评论",
				comments:[],
				focus:false,
				reply_id:0
			}
		},
		computed:{
			imagesList(){
				
								//数组取出来每个元素,然后每个元素进行操作后,组成新的数组。 
				return this.info.images.map(item=>item.url)
			}
		},
		// 接受传参
		onLoad(e) {
			// 初始化
			if (e.detail){
				this.__init(JSON.parse(e.detail))
			}
			
			// 监听关注顶踩操作。
			uni.$on('updateFollowOrSupport',this.listionFollowOrSupport)
			
			
			
			
		},
		onUnload() {
			uni.$off('updateFollowOrSupport',this.listionFollowOrSupport)
		},
		// 点击导航栏按钮触发的事件
		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(){
				// console.log(zz)
				// console.log('zz')
				this.$refs.share.close()
		},
		methods: {
			listionFollowOrSupport(e){
				console.log('detail.vue 接受到了')
				switch (e.type){
					case 'follow': // 关注
					this.follow(e.data.user_id)
						break;
					case 'support':
					this.doSupport(e.data)
					default:
						break
				}
			},
			__init(data){
				// 修改标题
				// console.log('222',data)
				uni.setNavigationBarTitle({
					title:data.title
				})
				this.info = data
				console.log(this.info)
				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)
				})
				// 获取评论
				this.getComments()
			},
			// 子组件触发的关注事件
			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
				})
			},

			// // 关闭评论框
			// close(){
			// 	this.$refs.share.close()
			// }
			// 获取评论列表
			getComments(){
				// console.log('aaa')
				this.$H.get('/post/'+this.info.id+'/comment',{},{
					// native:true
				})
				.then(res=>{
					// console.log(res)
					this.comments = this.__ArrSort(res.list)
					// console.log('zzzz')
					console.log(this.comments)
					this.info.comment_count = this.comments.length
					// 明天回来的时候直接,把这个传送index组件完成。。。
					uni.$emit('updateCommentsCount',{
						id:this.info.id,
						count:this.info.comment_count
					})
				})
			},
			// 重新整理评论格式
			__ArrSort(arr,id = 0){
				var temp = [],lev=0;
				var forFn = function(arr, id,lev){
					for (var i = 0; i < arr.length; i++) {
						var item = arr[i];
						if (item.fid==id) {
							item.lev=lev;
							temp.push({
								id:item.id,
								fid:item.fid,
								userid:item.user.id,
								userpic:item.user.userpic,
								username:item.user.username,
								time:$T.gettime(item.create_time),
								data:item.data,
							});
							forFn(arr,item.id,lev+1);
						}
					}
				};
				forFn(arr, id,lev);
				return temp;
			},
			// 提交评论
			submit(data){
				if(data === ''){
					return uni.showToast({
						title: '评论不能为空',
						icon: 'none'
					});
				}
				uni.showLoading({
					title: '评论中...',
					mask: false
				});
				console.log(this.reply_id)
				this.$H.post('/post/comment',{
					fid:this.reply_id,
					data:data,
					post_id:this.info.id
				},{
					token:true
				}).then(res=>{
					// console.log(res)
					
					uni.hideLoading()
					this.getComments()
				}).catch(err=>{
					console.log(err)
					uni.hideLoading()
					if (err.errorCode === 20008){
						uni.navigateTo({
							url:'../user-phone/user-phone'
						})
						uni.showToast({
							title:err.msg,
							icon:'none'

						})
					}
				})
			},
			// 点击该评论会触发  
			reply(id,toUserName){
				// 有可能blur马上发生
				this.$nextTick(()=>{
					// console.log(this)
					// console.log(this.$refs)
					// console.log(this.$refs.input1)
				    // console.log('zzzz',this.$refs.input)
					console.log('监听到了reply')
					this.toUser = "回复"+toUserName+": "
					this.reply_id = id
					this.focus = true
					console.log(this.focus)
				});
				
			},
			
				
			
			// 收起键盘、点击别处、会触发。
			blur(){
				
				console.log('监听到了blur')
				// this.reply_id = 0
				// if (this.reply_id){
					
				// }
				this.focus = false
				this.toUser = "请输入评论: "
				console.log(this.focus)
			}
			
			// 这就包括了一种情况:点击了别处 但是点击的是评论,这个时候不应该收起键盘
		}
	}
</script>

<style>

</style>

Bottom-input.vue

<template>
	<view>
		<!-- 底部操作条 -->
		<view style="height: 100rpx;"
		class="fixed-bottom flex align-center border-top bg-light">
			<input  type="text"  v-model="content" class="flex-1 rounded  ml-2 bg-white" style="padding:10rpx;" 
			ref="input1"
			:focus="focus"
			@blur="blur"
			:placeholder="toUser"
			@confirm="submit"/>
			<view ></view>
			<!-- 上面的confirm 可以使得input框直接回车就提交了。 :focus 可以直接获取焦点 @blur 失去焦点-->

			
			
			<view class="iconfont icon-fabu flex align-center justify-center font-lger animated"  
			hover-class="jello text-main" style="width: 100rpx;"
			@click="submit">
				
			</view>
		</view>
	</view>
</template>

<script>
	export default {
		props:{
			toUser:{
				type:String,
				default:'请输入评论',
				
			},
			focus:{
				type:Boolean,
				default:false
			}
		},
		data() {
			return {
				content:"",
				
				
			};
		},
		watch:{
			focus(newfocus,oldfocus){
				console.log(newfocus,oldfocus)
			}
		},
		methods:{
			submit(){
				// console.log(this.toUser)
				if (this.content===''){
					return uni.showToast({
						title:"消息不能为空",
						icon:'none'	
					})
				}
				this.$emit('submit',this.content)
				// 清空输入框
				this.content = ''
			},
			blur:function () {
				
				// console.log('blur',this,this.$refs.input);
				// this.$refs.input.focus();
				  // if(this.$refs.input){
					  // uniapp没有$refs内置dom功能
				  //   this.$refs.input.focus();
				  // }else{
				// this.$nextTick(()=>{
				// 	// console.log(this)
				// 	// console.log(this.$refs)
				// 	// console.log(this.$refs.input1)
				//     this.$refs.input1.focus();
				// });
				  // }
				// 通知父亲组件  提交的时候会触发  blur事件
				this.$emit('blur')
				
				// this.focus = false
				// this.focus = true
				
			}
		}
		
	}
</script>

<style>

</style>

十一 关注好友动态:

效果图:

image-20200512124734867

思路:

两种请求数据的方式:

1 onshow 请求数据,并且每次onshow的时候page=1,这样保证了每次显示该页面都拿到最新的数据

2 下拉加载显示更多。page不断+1

代码:

onShow() {
			// 这就做到了 每当显示这个页面的的时候关注的是最新的
			this.page = 1
			this.getList()
		},
// 获取关注好友文章列表
			getList(){
				let isrefresh = this.page === 1
				this.$H.get('/followpost/'+this.page,{},{
					token:true,
					noExtoken:true
				}).then(res=>{
					let list = res.list.map(v=>{
						return this.$U.formatCommonList(v)
					})
					this.list = isrefresh ? list : [...this.list,...list];
					this.loadmore  = list.length < 10 ? '没有更多了' : '上拉加载更多';
				}).catch(err=>{
					if(!isrefresh){
						this.page--;
					}
				})
			},
loadmoreEvent(){
				// 验证当前是否处于可加载状态
				if (this.loadmore !== '上拉加载更多') return;
				// 设置加载状态
				this.loadmore = '加载中...'
				// 请求数据
				this.page++
				this.getList()
			},

十二 我的好友列表实现(略)

<template>
	<view>
		<!-- 默认最新选项卡 -->
		<!-- 注意padding会把高度撑高,botter下边框要多给点距离。在下面剪掉这个距离-->
		<!-- 总元素的宽度=宽度+左填充+右填充+左边框+右边框+左边距+右边距 -->
		<!-- 总元素的高度=高度+顶部填充+底部填充+上边框+下边框+上边距+下边距 -->
		<view class=" flex align-center" style="height: 100rpx;">
			<view class="font flex-1 flex align-center justify-center font-weight-bold"
			v-for="(item,index) in tabBars" :key="index"
			
			:class="index===tabIndex?'text-main font-lg':''"
			@click="changeTab(index)"
			>{{item.name}}</view>
		</view>
		
		<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">
				
				<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">
							<user-list :item="item2" :index="index2">
								
							</user-list>
	
						</block>
						<!-- 上拉加载 -->
						<load-more v-if="item.list.length>10"
						:loadmore="item.loadmore"></load-more>
					</template>
					<template v-else>
						<!-- 无数据渲染页面 -->
						<no-thing>来了啊老弟</no-thing>
						
					</template>
						
						
						
				</scroll-view>
				
			</swiper-item>
			
		</swiper>
		
	</view>
</template>

<script>
	
	const demo = [{
		avatar:"/static/default.jpg",
		username:"昵称",
		sex:0, // 0未知,1女性,2男性
		age:30,
		isFollow:false
	}
	];
	
	import loadMore from '@/components/common/load-more.vue'
	import userList from '@/components/user-list/user-list.vue'
	export default {
		components:{
			loadMore,
			userList
		},
		data() {
			return {
				tabIndex:0,
				tabBars:[{
					name:"互关",
					num:0,
					key:"friends"
				},{
					name:"关注",
					num:0,
					key:"follows"
				},{
					name:"粉丝",
					num:0,
					key:"fens"
				}],
				scrollH:600,
				newsList:[]
			}
		},
		
		onLoad() {
			uni.getSystemInfo({
				success:res=>{
								// 可用窗口高度(屏幕高度-导航栏高度-底部栏高度。) -  选项卡高度
					this.scrollH = res.windowHeight - uni.upx2px(101)
					console.log(this.scrollH)
					
				}
			})
			// 根据选项生成列表
			this.getData()
		},
		// 监听点击输入框事件
		onNavigationBarSearchInputClicked() {
			uni.navigateTo({
				url: '../search/search?type=user',
			});
		},
		methods: {
			changeTab(index){
				this.tabIndex = index
				
			},
			// 监听选项内容滑动
			onChangeTab(e){
				this.changeTab(e.detail.current)
				if(!this.newsList[e.detail.current].firstLoad){
					this.getList()
				}
			},
			// 上拉加载更多
			loadmore(index){
				// 拿到当前列表
				let item = this.newsList[index]
				// 修改当前加载状态
				item.loadmore = '加载中。。。'
				// 模拟数据请求
				setTimeout(()=>{
					// 加载数据
								// ... 相当于取出来当前对象可以遍历出来的内容放到了当前对象里面。
								// 这个可以粗糙的理解为把合并了两个一摸一样的列表,列表里面原来的内容*2了 
					item.list = [...item.list,...item.list]
					item.loadmore = '上拉加载更多'
				},2000)
			},
			// 制作列表+上拉加载数据初始值
			getData(){
				var arr=[]
				for (let i = 0; i < this.tabBars.length;i++ ){
					let obj ={
						loadmore:"上拉加载更多",
						list:[],
						page:1,
						fristLoad:false
					}
					// if (i < 2) {
					// 	obj.list = demo
					// }
					arr.push(obj)
				}
				this.newsList = arr
				this.getList()
			},
			getList(){
				let index = this.tabIndex
				// let id = this.tabBars[index].id
				let page = this.newsList[index].page
				let isrefresh = page === 1
				this.$H.get('/'+this.tabBars[index].key+'/'+page,{},{
					token:true,
					noExToken:true,
					// native:true
				})
				.then(res=>{
					console.log(res);
					let list = res.list.map(v=>{
						return {
							id:v.id,
							avatar:v.userpic,
							username:v.username,
							sex:v.userinfo.sex,
							age:v.userinfo.age,
							isFollow:index !== 2
						}
					})
					
					
					this.newsList[index].list = isrefresh ? list : [...this.newsList[index].list,...list];
					
					this.newsList[index].loadmore  = list.length < 10 ? '没有更多了' : '上拉加载更多';
					
					if (isrefresh) {
						this.newsList[index].firstLoad = true
					}
				}).catch(err=>{
					if(!isrefresh){
						this.newsList[index].page--;
					}
				})
			},
		}
	}
</script>

<style>

</style>

搜索用户功能实现(略)

<template>
	<view>
		<template v-if="searchList.length === 0">
			<!-- 搜索历史 -->
			<view class="py-2 font-md px-2">搜索历史</view>
			<view class="flex flex-wrap">
				<view class="border rounded font mx-2 my-1 px-2" 
				v-for="(item,index) in historyList" :key="index"
				hover-class="bg-light"
				@click="clickSearchHistory(item)">{{item}}</view>
			</view>
		</template>
		<template v-else>
			<!-- 数据列表 -->
			<block v-for="(item,index) in searchList" :key="index">
				<template v-if="type ==='post'">
					<!-- 帖子 -->
					<common-list :item="item" :index="index"></common-list>
				</template>
				<template v-else-if="type === 'topic'">
					<!-- 话题 -->
					<topic-list :item="item" :index="index"></topic-list>
				</template>
				<template v-else>
					<!-- 用户 -->
					<user-list :item="item" :index="index"></user-list>
				</template>
			</block>
			<!-- 上拉加载 -->
			<load-more :loadmore="loadmore"></load-more>
		</template>
		
	</view>
</template>

<script>
	import commonList from '@/components/common/common-list.vue';
	import topicList from '@/components/news/topic-list.vue';
	import userList from '@/components/user-list/user-list.vue';
	import loadMore from '@/components/common/load-more.vue';
	export default {
		components: {
			commonList,
			topicList,
			userList,
			loadMore
			
		},
		data() {
			return {
				searchText:"",
				historyList:[],
				// 搜索结果
				searchList:[],
				// 当前搜索类型
				type:"post",
				page:1,
				loadmore:"上拉加载更多"
			}
		},
		// 监听导航输入
		onNavigationBarSearchInputChanged(e){
			this.searchText = e.text
			// console.log(e.text)
		},
		// 监听点击导航搜索按钮
		onNavigationBarButtonTap(e) {
			if (e.index === 0) {
				this.searchEvent()
			}
		},
		// 监听下拉刷新
		onPullDownRefresh() {
			if(this.searchText == ''){
				return uni.stopPullDownRefresh()
			}
			// 请求数据
			this.getData(true,()=>{
				uni.stopPullDownRefresh()
			})
		},
		// 监听上拉加载更多
		onReachBottom() {
			if(this.loadmore != '上拉加载更多'){
				return;
			}
			this.loadmore = '加载中...'
			this.getData(false)
		},
		onUnload() {
			if(this.type === 'post'){
				uni.$off('updateFollowOrSupport',this.listionFollowOrSupport)
			}
		},
		onLoad(e) {
			if (e.type) {
				this.type = e.type
			}
			let pageTitle = '帖子'
			switch (this.type){
				case 'post':
				pageTitle = '帖子'
				uni.$on('updateFollowOrSupport',this.listionFollowOrSupport)
					break;
				case 'topic':
				pageTitle = '话题'
					break;
				case 'user':
				pageTitle = '用户'
					break;
			}
			
			// 碰到这种找不到元素的情况,直接来个nextTick等所有都渲染完了就不会出问题。
			this.$nextTick(function(){
				// 修改搜索占位
				// #ifdef APP-PLUS
				let currentWebview = this.$mp.page.$getAppWebview();
				let tn = currentWebview.getStyle().titleNView; 
				tn.searchInput.placeholder = '搜索'+pageTitle; 
				currentWebview.setStyle({
					titleNView: tn  
				})
				// #endif
			})
			
			// 取出搜索历史
			let list = uni.getStorageSync(this.type+'historyList')
			if (list) {
				this.historyList = JSON.parse(list)
			}
		},
		methods: {
			// 
			listionFollowOrSupport(e){
					switch (e.type){
						case 'follow': // 关注
						this.follow(e.data.user_id)
							break;
						default:
							break;
					}
				},
			// 关注
			follow(user_id){
				// 找到当前作者的所有列表
				this.searchList.forEach((item)=>{
					if(item.user_id === user_id){
						item.isFollow = true
					}
				})
				uni.showToast({ title: '关注成功' })
			},
			// 点击搜索历史
			clickSearchHistory(text){
				this.searchText = text
				this.searchEvent()
			},
			// 搜索事件
			searchEvent(){
				// 收起键盘
				uni.hideKeyboard()
				
				// 添加搜索历史
				let index = this.historyList.findIndex(v => v===this.searchText)
				if (index!=-1){
					this.$U.__toFirst(this.historyList,index)
				}else{
					// unshift 在原来数据的基础上添加第一个元素。
					this.historyList.unshift(this.searchText)
				}
				// 添加到本地缓存
				uni.setStorageSync(this.type+'historyList',JSON.stringify(this.historyList))
				
				// 请求搜索到的数据
				this.getData()
			},
			
			// 请求数据
			getData(isrefresh=true,callback=false){
				// isrefresh : 是否从第一页加载?
				// 显示loading状态
				uni.showLoading({
					title: '加载中...',
					mask: false
				})
				this.page = isrefresh ? 1 : this.page+1
				// 请求搜索
				this.$H.post('/search/'+this.type,{
					keyword:this.searchText,
					page:this.page
				}).then(res=>{
					console.log()
					let list = []
					switch (this.type){
						case 'post':
							list = res.list.map(v => {
								return this.$U.formatCommonList(v)
							})
							break;
						case 'topic':
							list = res.list.map(v=>{
								return {
									id:v.id,
									cover:v.titlepic,
									title:v.title,
									desc:v.desc,
									today_count:v.todaypost_count,
									news_count:v.post_count
								}
							})
							break;
						case 'user':
						list = res.list.map(v=>{
							return {
								id:v.id,
								avatar:v.userpic,
								username:v.username,
								sex:v.userinfo.sex,
								age:v.userinfo.age,
								isFollow:false
								}
							})
							break;
					}
					// 渲染页面
					this.searchList = isrefresh ? list : [...this.searchList, ...list]
					// 加载情况
					this.loadmore = list.length < 10 ? '没有更多了':'上拉加载更多'
					
					// 关掉普通加载中动画
					uni.hideLoading()
					// 给下拉刷新使用的,也是一种思路
					if (typeof callback === 'function'){
						callback()
					}
				}).catch(err=>{
					this.page--
					// 关掉普通加载中动画
					uni.hideLoading()
					// 给下拉刷新使用的,也是一种思路
					if (typeof callback === 'function'){
						callback()
					}
				})
			}
		}
	}
</script>

<style>

</style>

十三 个人空间实现

效果:

自己访问的时候

image-20200512211859593

别人访问的时候

image-20200512212015189

有聊天和加入黑名单选项

image-20200512212208140

可以看发了什么帖子

image-20200512212317037

思路(略):

都是正常的业务逻辑

代码

<template>
	<view>
		<!-- 头部 -->
		<view class="flex align-center p-3 border-bottom border-light-secondary">
			<image :src="userinfo.userpic" 
			style="width: 180rpx;height: 180rpx;"
			class="rounded-circle"></image>
			<view class="pl-3 flex flex-column flex-1">
				<view class="flex align-center">
					<view class="flex-1 flex flex-column align-center justify-center" v-for="(item,index) in counts" :key="index">
						<text class="font-lg font-weight-bold">{{item.num|formatNum}}</text>
						<text class="font text-muted">{{item.name}}</text>
					</view>
				</view>
				<view class="flex justify-center align-center">
					
					<button v-if="user_id == user.id"
					 type="default" size="mini"
					style="width: 400rpx;" @click="openUserInfo">
						编辑资料
					</button>
					
					<button v-else
					type="default" size="mini" 
					:class="userinfo.isFollow ? 'bg-light text-dark' : 'bg-main'"
					style="width: 400rpx;" @click="doFollow">
						{{userinfo.isFollow ? '已关注' : '关注'}}
					</button>

				</view>
			</view>
		</view>
		
		<!-- tab -->
		<view class="flex align-center" style="height: 100rpx;">
			<view class="flex-1 flex align-center justify-center"
			v-for="(item,index) in tabBars" :key="index"
			:class="index === tabIndex ? 'font-lg font-weight-bold text-main':'font-md'"
			@click="changeTab(index)">
			{{item.name}}</view>
		</view>
		
		<template v-if="tabIndex === 0">
			<view class="animated fast fadeIn">
				<view class="p-3 border-bottom">
					<view class="font-md">账号信息</view>
					<view class="font">账号年龄:{{userinfo.regtime}}</view>
					<view class="font">账号id:{{user_id}}</view>
				</view>
				<view class="p-3 border-bottom">
					<view class="font-md">个人信息</view>
					<view class="font">星座:{{userinfo.birthday}}</view>
					<view class="font">职业:{{userinfo.job}}</view>
					<view class="font">故乡:{{userinfo.path}}</view>
					<view class="font">情感:{{userinfo.qg}}</view>
				</view>
			</view>
		</template>
		<template v-else>
			<view class="animated fast fadeIn">
				<common-list v-for="(item,index) in list" :key="index" :item="item" :index="index" @follow="follow" @doSupport="doSupport"></common-list>
				<divider></divider>
				<load-more :loadmore="loadmore"></load-more>
			</view>
		</template>
		
		
		
		<!-- 弹出层 -->
		<uni-popup ref="popup" type="top">
			<view class="flex align-center justify-center font-md border-bottom py-2 bg-light" hover-class="bg-main text-white" @click="doBlack">
				<text class="iconfont icon-sousuo mr-2"></text> 
				{{userinfo.isblack ? '移出黑名单' : '加入黑名单'}}
			</view>
			<view v-if="!userinfo.isblack" class="flex align-center justify-center font-md py-2 bg-light " hover-class="bg-main text-white">
				<text class="iconfont icon-shanchu mr-2"></text> 聊天
			</view>
		</uni-popup>
		
		
	</view>
</template>

<script>
	const emotionArray = ['保密', '未婚', '已婚']
	import commonList from '@/components/common/common-list.vue';
	import loadMore from '@/components/common/load-more.vue';
	import uniPopup from '@/components/uni-ui/uni-popup/uni-popup.vue';
	import $T from '@/common/time.js';
	import { mapState } from 'vuex'
	export default {
		components: {
			commonList,
			loadMore,
			uniPopup
		},
		data() {
			return {
				user_id:0,
				userinfo:{
					userpic:"/static/default.jpg",
					username:"",
					sex:0,
					age:20,
					isFollow:false,
					regtime:"",
					birthday:"",
					job:"",
					path:"",
					qg:""
				},
				counts:[{
					name:"帖子",
					num:0
				},{
					name:"关注",
					num:0
				},{
					name:"粉丝",
					num:0
				}],
				tabIndex:0,
				tabBars:[{
					name:"主页",
				},{
					name:"帖子",
					list:[],
					// 1.上拉加载更多  2.加载中... 3.没有更多了
					loadmore:"上拉加载更多",
					page:1
				},{
					name:"动态",
					list:[],
					// 1.上拉加载更多  2.加载中... 3.没有更多了
					loadmore:"上拉加载更多",
					page:1
				}],
			}
		},
		onNavigationBarButtonTap() {
			if(this.user_id == this.user.id){
				return uni.navigateTo({
					url: '../user-set/user-set',
				});
			}
			this.$refs.popup.open()
		},
		computed: {
			...mapState({
				user:state=>state.user
			}),
			list() {
				return this.tabBars[this.tabIndex].list 
			},
			loadmore(){
				return this.tabBars[this.tabIndex].loadmore
			}
		},
		filters: {
			formatNum(value) {
				return value > 99 ? '99+' : value;
			}
		},
		onLoad(e) {
			if(!e.user_id){
				return uni.showToast({
					title: '非法参数',
					icon: 'none'
				});
			}
			this.user_id = e.user_id
			// 加载用户个人信息
			this.getUserInfo()
			// 获取用户相关数据
			this.getCounts()
			// 监听关注和顶踩操作
			uni.$on('updateFollowOrSupport',(e)=>{
				switch (e.type){
					case 'follow': // 关注
					this.follow(e.data.user_id)
						break;
					case 'support': // 顶踩
					this.doSupport(e.data)
						break;
				}
			})
			// 监听评论数变化
			uni.$on('updateCommentsCount',({id,count})=>{
				this.tabBars.forEach(tab=>{
					if(tab.list){
						tab.list.forEach((item)=>{
							if(item.id === id){
								item.comment_count = count
							}
						})
					}
				})
			})
		},
		onUnload() {
			uni.$off('updateFollowOrSupport',(e)=>{})
			uni.$off('updateCommentsCount',(e)=>{})
		},
		methods: {
			// 获取用户相关数据
			getCounts(){
				this.$H.get('/user/getcounts/'+this.user_id).then(res=>{
					this.counts[0].num = res.post_count
					this.counts[1].num = res.withfollow_count
					this.counts[2].num = res.withfen_count
				})
			},
			// 获取用户个人信息
			getUserInfo(){
				this.$H.post('/getuserinfo',{
					user_id:this.user_id
				},{
					token:true,
					notoast:true
				}).then(res=>{
					this.userinfo = {
						userpic:res.userpic,
						username:res.username,
						sex:res.userinfo.sex,
						age:res.userinfo.age,
						isFollow:res.fens.length > 0,
						isblack:res.blacklist.length > 0,
						regtime:$T.dateFormat(new Date(res.create_time * 1000), '{Y}-{MM}-{DD}'),
						birthday:$T.getHoroscope(res.userinfo.birthday),
						job:res.userinfo.job ? res.userinfo.job : '无',
						path:res.userinfo.path ? res.userinfo.path : '无',
						qg:emotionArray[res.userinfo.qg],
					}
					uni.setNavigationBarTitle({
						title:this.userinfo.username
					})
				})
			},
			changeTab(index){
				this.tabIndex = index
				this.getList()
			},
			// 关注
			follow(user_id){
				// 找到当前作者的所有列表
				this.tabBars.forEach(tab=>{
					if(tab.list){
						tab.list.forEach((item)=>{
							if(item.user_id === user_id){
								item.isFollow = true
							}
						})
					}
				})
				uni.showToast({ title: '关注成功' })
			},
			// 顶踩操作
			doSupport(e){
				// 拿到当前的选项卡对应的list
				this.tabBars[this.tabIndex].list.forEach(item=>{
					if(item.id === e.id){
						// 之前没有操作过
						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.support_count++;
							// 踩 - 1
							item.support.unsupport_count--;
						}
						item.support.type = e.type
					}
				})
				let msg = e.type === 'support' ? '顶' : '踩'
				uni.showToast({ title: msg + '成功' });
			},
			// 获取文章列表
			getList(){
				let index = this.tabIndex
				if(index === 0) return;
				let page = this.tabBars[index].page
				let isrefresh = page === 1
				this.$H.get('/user/'+this.user_id+'/post/'+page)
				.then(res=>{
					let list = res.list.map(v=>{
						return this.$U.formatCommonList(v)
					})
					this.tabBars[index].list = isrefresh ? [...list] : [...this.tabBars[index].list,...list]
					this.tabBars[index].loadmore = list.length < 10 ? '没有更多了' : '上拉加载更多'
				}).catch(err=>{
					if(!isrefresh){
						this.tabBars[index].page--
					}
				})
			},
			// 关注/取消关注
			doFollow(){
				this.checkAuth(()=>{
					let url = this.userinfo.isFollow ? '/unfollow' : '/follow'
					let msg = this.userinfo.isFollow ? '取消关注' : '关注'
					this.$H.post(url,{
						follow_id:this.user_id
					},{
						token:true
					}).then(res=>{
						this.userinfo.isFollow = !this.userinfo.isFollow
						uni.showToast({
							title: msg+'成功',
							icon: 'none'
						});
						uni.$emit('updateIndex')
						this.getList()
					})
				})
			},
			// 进入编辑资料
			openUserInfo(){
				uni.navigateTo({
					url: '../user-userinfo/user-userinfo',
				});
			},
			// 加入/移出黑名单
			doBlack(){
				this.checkAuth(()=>{
					let url = this.userinfo.isblack ? '/removeblack':'/addblack'
					let msg = this.userinfo.isblack ? '移出黑名单' : '加入黑名单'
					uni.showModal({
						content: '是否要'+msg,
						success: (res)=> {
							if (res.confirm) {
								this.$H.post(url,{
									id:this.user_id
								},{
									token:true
								}).then(res=>{
									this.userinfo.isblack = !this.userinfo.isblack
									uni.showToast({
										title: msg+'成功',
										icon: 'none'
									});
								})
							}
						}
					});
					
				})
			}
		}
	}
</script>

<style>

</style>

十四 各个主要数据页面监听刷新事件

以index.vue为例子

onLoad() {
			uni.getSystemInfo({
				success:res=>{
								// 可用窗口高度(屏幕高度-导航栏高度-底部栏高度。) -  选项卡高度
					this.scrollH = res.windowHeight - uni.upx2px(101)
					// 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)
		},

修改vuex仓库store/index.js

设置登入登出的时候主要页面刷新数据。

// 登录
		login(state,user){
			// 更改state中的变量要在这里更改。
			state.loginStatus = true
			state.user = user
			// 登录成功记录一下token
			state.token = state.user.token
			// 永久存储
			uni.setStorageSync('user',JSON.stringify(user));
			uni.$emit('updateIndex')
		},
// 退出登录
		logout(state){
			state.loginStatus = false
			state.user = {}
			state.token = false
			uni.removeStorageSync('user')
			uni.$emit('updateIndex')
			
		},
      

十五 浏览历史功能

思路

commonlis.vuet每次打开详情页面的时候都缓存到本地。

点击浏览历史,直接从本地缓存里面取出来记录相关的数据进行渲染。

做一个清除浏览历史的按钮,原理就是直接把本地的缓存清除掉。

效果图(略)

代码

common-list.vue

openDetail(){
				if (this.isdetail) return;
				uni.navigateTo({
					url:'../../pages/detail/detail?detail='+JSON.stringify(this.item)
				})
				// 加入历史记录
				let list =  uni.getStorageSync('history')
				list = list ? JSON.parse(list) : []
				let index = list.findIndex(v=>v.id === this.item.id)
				if(index === -1){
					list.unshift(this.item)
					uni.setStorageSync('history',JSON.stringify(list))
				}
			}

History.vue

<template>
	<view>
		<common-list v-for="(item,index) in list" :key="index"
		:item="item" :index="index"></common-list>
		
		<no-thing v-if="list.length === 0"></no-thing>
	</view>
</template>

<script>
	import commonList from '@/components/common/common-list.vue';
	import noThing from '@/components/common/no-thing.vue';
	export default {
		components: {
			commonList
		},
		data() {
			return {
				list:[]
			}
		},
		onLoad() {
			// 取出历史记录
			let list =  uni.getStorageSync('history')
			if(list){
				this.list = JSON.parse(list)
			}
			// 监听关注和顶踩操作
			uni.$on('updateFollowOrSupport',(e)=>{
				switch (e.type){
					case 'follow': // 关注
					this.follow(e.data.user_id)
						break;
					case 'support': // 顶踩
					this.doSupport(e.data)
						break;
				}
			})
			// 监听评论数变化
			uni.$on('updateCommentsCount',({id,count})=>{
				this.list.forEach((item)=>{
					if(item.id === id){
						item.comment_count = count
					}
				})
			})
		},
		onUnload() {
			uni.$off('updateFollowOrSupport',(e)=>{})
			uni.$off('updateCommentsCount',(e)=>{})
		},
		onNavigationBarButtonTap() {
			uni.showModal({
				content: '是否要清除历史记录?',
				success: (res)=>{
					if (res.confirm) {
						uni.removeStorageSync('history')
						this.list = []
					}
				}
			});
		},
		methods: {
			// 关注
			follow(user_id){
				// 找到当前作者的所有列表
				this.list.forEach((item)=>{
					if(item.user_id === user_id){
						item.isFollow = true
					}
				})
				uni.showToast({ title: '关注成功' })
			},
			// 顶踩操作
			doSupport(e){
				// 拿到当前的选项卡对应的list
				this.list.forEach(item=>{
					if(item.id === e.id){
						// 之前没有操作过
						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.support_count++;
							// 踩 - 1
							item.support.unsupport_count--;
						}
						item.support.type = e.type
					}
				})
				let msg = e.type === 'support' ? '顶' : '踩'
				uni.showToast({ title: msg + '成功' });
			},
		}
	}
</script>

<style>

</style>

posted @ 2020-05-25 18:21  张明岩  阅读(1189)  评论(0编辑  收藏  举报