31 uni-app前端后端交互剩余主体业务逻辑相关笔记
31 前端后端剩余粗糙笔记
一 绑定邮箱
<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()方法。
效果图:
代码:
<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提供的上传图片请求)
思路:
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);
})
}
})
},
四 修改资料功能实现(日期框,三级联动地址)
效果图:
关键点:
用了日期选框(调用系统原生的日期选择器,并不友好)
用了一个开源的三级联动(还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>
六 关注点赞点踩(多组件进行通讯,很有参考价值的业务逻辑。)
效果图:
关注效果图:
点赞点踩效果图:
对各个页面已经加载的数据点赞点踩同步就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
八 发布文章功能实现(选择图片直接上传功能,可以关注草稿功能)
效果图:
思路:
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>
十一 关注好友动态:
效果图:
思路:
两种请求数据的方式:
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>
十三 个人空间实现
效果:
自己访问的时候
别人访问的时候
有聊天和加入黑名单选项
可以看发了什么帖子
思路(略):
都是正常的业务逻辑
代码
<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>