逻辑:先实现tab切换在个人主页router的组件中加入子组件,通过绑定单击事件,编程式导航的形式,来跳转不同的子组件中显示不同的内容,之后从api里拿接口封装接口请求,拿到数据通过父传子的形式把请求的数据传给子组价
布局的话就使用element-ui组件的布局,这里注意使用的是element-ui组件绑定单击事件的时候,用@tab-cilck="事件()"
<el-tabs class="user-nav" v-model="activeName" @tab-click="myclick()">
<el-tab-pane label="作品" name="works"></el-tab-pane>
<el-tab-pane label="粉丝" name="fans"></el-tab-pane>
<el-tab-pane label="关注" name="following"></el-tab-pane>
<el-tab-pane label="收藏" name="collection"></el-tab-pane>
</el-tabs>
在data设置activeName:"works",初始值设为works,利用v-model="activeName",点击不同的tab来改变activeName中的值,值为tab中的name属性,之后绑定事件利用编程式导航实现router跳转
myclick(){
this.$router.push({ //编程式导航,跳到不同的子组件中
name:this.activeName,
query:{
...this.$route.query
}
})
}
问题:为什么要在写编程式导航是 要传query参数? 因为当我们访问别人的个人主页的时候首先都知道是需要userId值的,当我们在别人的个人主页,tab切换是会把之前的userId给覆盖,会直接跳到我自己的个人主页,所以要传query参数目的是为了你访问别人的个人主页,tab切换数就算把之前的userId的参数给覆盖,但我们又通过query传了userId值,就可以在别人的个人主页,实现tab切换啦!
/**
* 拿到用户发布的菜谱,可做筛使用选
* @export
* @param {Object} [params] - 不填写,则获取所有的菜谱
* @param {string} [params.userId] - 指定用户的菜单
* @param {string} [params.classify] - 按照菜单分类,进行筛选
* @param {string} [params.property] - 指定菜单属性进行筛选
* @param {string} [params.property.craft] - 按工艺筛选
* @param {string} [params.property.flavor] - 按口味筛选
* @param {string} [params.property.hard] - 按难度筛选
* @param {string} [params.property.people] - 按人数筛选
* @param {string} [params.page] - 指定页码
* @returns
*/
export async function getMenus(params){
return await http.get('/menu/query', {params});
}
/**
* 获取指定用户的粉丝
* @export
* @param {Object} [params] -
* @param {string} [params.userId] - 指定的用户id
* @returns
*/
export async function fans(params){
return await http.get('/user/fans', {params});
}
/**
* 获取指定用户关注的人
* @export
* @param {Object} [params] -
* @param {string} [params.userId] - 指定的用户id
* @returns
*/
export async function following(params){
return await http.get('/user/following', {params});
}
/**
* 获取指定用户的收藏的菜单
* @export
* @param {Object} [params] -
* @param {string} [params.userId] - 指定的用户id
* @returns
*/
export async function collection(params){
return await http.get('/user/collection', {params});
}
拿到我们数据的接口 引入到个人主页的组件中,因为接口较多,所以我们可以吧接口进行封装
import {getMenus, following, fans, collection} from '@/service/api';
const getOtherInfo ={
async works(params){
return (await getMenus(params)).data
},
async following(params){
return (await following(params)).data
},
async fans(params){
return (await fans(params)).data
},
async collection(params){
return (await collection(params)).data
}
}
有两种方式请求数据tab切换中不同的数据 一.自定义一个事件,在事件中进行请求数据,之后在 之前watch监听$route中直接调用先这个事件 二.直接之前watch监听$route中,进行数据请求---具体代码如下:
async getinfo(){ //自定义事件
//调用不同接口
let data=await getOtherInfo[this.activeName]({userId:this.userInfo.userId})
// 问题2:切换tab过快时,依然报错key重复,并且数据显示不正常
// 原因:因为ajax返回数据的时候 快慢的问题,显示的是最后一次ajax返回的数据,无法识别对应的请求
// 解决:在请求中定义一个标识,在返回的数据中添加标识,如果返回的数据和当前的标识相同,再渲染
// console.log(data)
if(this.activeName === data.flag){
this.list=data.list
}
}
this.getinfo() //调用自定义事件 在监听路由中调用
watch:{
$route:{
async handler(){
let {userId} =this.$route.query; //自己的个人主页
this.isOne= !userId || userId===this.$store.state.userInfo.userId;
//这个this.isOne是 没有userId或者userId等于vuex的数据的userId
if(this.isOne){ //如果为true就把vuex的数据重新赋值给data的userInfo
this.userInfo=this.$store.state.userInfo
}else{
const data=await userInfo({userId}) //别人的个人主页
//需要请求接口通过传入参数userId来跳入不同的别人主页
console.log(data)
this.userInfo=data.data
}
this.activeName=this.$route.name;
// 还有一种方式:直接在这里调用封装的接口数据,来获取数据
let data=await getOtherInfo[this.activeName]({userId:this.userInfo.userId})
console.log(data)
this.list=data.list
},
immediate:true //页面初始化立即执行
}
},
之后数据就请求出来了,在data设置list的一个空数组来接收数据,之后通过绑定自定义属性名值为我们请求的数据list,在子组件中通过props来接收(父传子) ,在各自的子组件中渲染数据即可,也把activeName也通过(父传子的方式) 传到子组件中(后说原因) -----------这里啰嗦一句:(父传子),在子组件中props中接收的格式有两种 1.props:['list'] 2.props:{type:'指定类型',default:'默认值' }
<router-view :info="list" :activeName="activeName"></router-view>
之后判断如果请求的数据有值得话直接渲染,没数据就提示 如图:
这个是没数据的时候的提示,有数据的渲染页面即可 !!! --------- 这是就要进行判断通过(父传子)的数据 在提示的标签中用v-if来判断传过来的值是否有长度,也就是判断是否有数据 v-if="!info.length" 判断他如果没有数据就显示提示标签,我们的作品页和收藏页是同一个页面,我们之前把activeName传过来过,在不同的标签中也用v-if判断 代码如下:
<div class="info-empty" v-if="!info.length">
<div v-if="activeName==='works'">
<p>私房菜不要偷偷享用哦~~制作成菜谱与大家分享吧!</p>
<a href="">发布菜单</a>
</div>
</div>
<div class="info-empty" v-if="!info.length">
<div v-if="activeName==='collection'">
<p>还没有收藏任何的菜谱,去搜自己喜欢的菜谱,收藏起来吧。</p>
<a href="">菜谱大全</a>
</div>
</div>
之后就完成了,不过有bug!!! 当我们tab切换的时候会提示我们一个这样的报错 vue.runtime.esm.js?2b0e:619 [Vue warn]: Duplicate keys detected: '5d613120ac558b4d879824ba'. This may cause an update error. -->意思是检测到重复的键,可能导致更新错误 -----> 解决方法:我们发现我们进去的时候是不会报这样的错误的,当我们在tab切换的时候,才会报错,所以我们在他每次拿到数据渲染保存玩之后,把list里的数据清空
//点击切换tab
myclick(){
//问题1:因为各个二级路由接收的字段,全是info,所有会造成数据重复
//解决:第一次保存数据到数据中时,没有问题,所以,每次保存前,都清空一下数组
this.list=[]
this.$router.push({ //编程式导航,跳到不同的子组件中
name:this.activeName,
query:{
...this.$route.query
}
})
}
之后还有一个bug!,就是当我们过度使用tab切换时,还会报上面同样的错误,解决办法
async getinfo(){ //自定义事件
//调用不同接口
let data=await getOtherInfo[this.activeName]({userId:this.userInfo.userId})
// 问题2:切换tab过快时,依然报错key重复,并且数据显示不正常
// 原因:因为ajax返回数据的时候 快慢的问题,显示的是最后一次ajax返回的数据,无法识别对应的请求
// 解决:在请求中定义一个标识,在返回的数据中添加标识,如果返回的数据和当前的标识相同,再渲染
// console.log(data)
if(this.activeName === data.flag){
this.list=data.list
}
},
给每次请求的接口数据,设置一个标识,当他等于这个标签才会拿到数据,这样就不会出现重复的键的错误啦!
const getOtherInfo ={
async works(params){
let data=(await getMenus(params)).data
data.flag="works"
return data
},
async following(params){
let data=(await following(params)).data
data.flag="following"
return data
},
async fans(params){
let data=(await fans(params)).data
data.flag="fans"
return data
},
async collection(params){
let data=(await collection(params)).data
data.flag="collection"
return data
}
}
这里在啰嗦一句:在函数作用域中var是局部变量,函数销毁var也会更这销毁,var除了在函数中声明是局部的,在其他的地方声明的都是全局的,因为var可以跨块访问!