前言
在UniApp的开发小程序过程中,为了针对不同角色用户登录后的个性化需求。通过动态权限配置机制,能够根据用户的角色展示不同的TabBar。此项目是通过Uni-App命令行的方式搭建的
Vue3+Vite+Ts+Pinia+Uni-ui
的小程序项目
最终效果
-
1、司机角色:
-
2、供应商角色:
-
3、司机且供应商角色:
目前常规的实现方式,大多数都是封装一个tabbar
组件,在需要显示tabbar的页面添加这个组件,在根据一个选中的index值来切换选中效果。
而我的实现方式:把所有有
tabbar
的页面全部引入在一个tabbarPage
页面,根据角色userType
,来动态显示页面
实现思路
1、常规登录:通过微信登录获取code
2、根据code获取openId
3、根据openId获取token,若token存在表:此用户已经登陆/绑定过,则根据token获取用户信息,根据角色直接进入项目页面;若token不存在,则跳转到登录页面
4、登录成功后,调用用户信息接口,根据角色直接进入项目页面
1、以下是封装了一个useLogin的hooks
export const useLogin = () => {
const { proxy } = getCurrentInstance() as any
//常规登录
const judgmentLogin = () => {
uni.login({
provider: 'weixin', //使用微信登录
success: async (loginRes) => {
// 根据微信登录的code获取openid
const res = await proxy.$api.getOpenid({ code: loginRes.code })
if (res.success) {
// console.log('res.data.openid----', res.data.openId)
// 根据openid获取token
openidLogin(res.data.openId)
// 存储openid
uni.setStorageSync('openId', res.data.openId)
}
}
});
}
// 登录过的用户再次进入根据openid获取token,有token则直接进入当前用户的页面,没有则进入登录页面
const openidLogin = (async (openId: string) => {
// console.log('openId----', openId)
const res = await proxy.$api.openIdLogin({ openId })
if (res.success) {
if (res.data) {
// 存储token
uni.setStorageSync('token', res.data)
userInfo(openId)
} else {
uni.navigateTo({
url: '/pages/login/login'
})
}
}
})
// 登录成功后(有token后)根据openid获取用户信息
const userInfo = (async (openId: any) => {
const res = await proxy.$api.getUserInfo({ openId })
if (res.success) {
console.log('获取登陆用户信息', res.data)
uni.setStorageSync('userInfo', JSON.stringify(res.data))
const userTypeList = ['scm_driver', 'scm_supplier', 'supplier_and_driver']
// 遍历角色数组来存储当前用户的角色。此角色为userTypeList中的某一个并且此数组只能存在一个userTypeList里面的角色,不会同时存在两个
res.data.roles.map((item: any) => {
if (userTypeList.includes(item.roleKey)) {
uni.setStorageSync('userType', item.roleKey)
}
})
// 判断角色数组中只要有一个角色在userTypeList中,则进入当前用户的角色页面,否则进入无权限页面
const flag = res.data.roles.some((item: any) => {
return userTypeList.includes(item.roleKey)
})
// console.log('flag----', flag)
if (flag && userTypeList.includes(uni.getStorageSync('userType'))) {
setTimeout(() => {
uni.reLaunch({
url: '/pages/tabbarPage/tabbarPage'
})
}, 500)
} else {
uni.showToast({
icon: 'none',
title: '当前用户角色没有权限!'
})
}
}
})
return {
judgmentLogin,
userInfo
}
}
2、修改page.json中的tabBar
"tabBar": {
"color": "#a6b9cb",
"selectedColor": "#355db4",
"list": [
{
"pagePath": "pages/supplierMyorder/supplierMyorder"
},
{
"pagePath": "pages/driverMyorder/driverMyorder"
},
{
"pagePath": "pages/mycar/mycar"
},
{
"pagePath": "pages/driverPersonal/driverPersonal"
}
]
},
3、关键页面tabbarPage.vue
<template>
<div class="tabbar_page flex-box flex-col">
<div
class="page_wrap"
v-if="userType === 'scm_supplier'"
v-show="active === 'supplierMyorder'"
>
<supplier-myorder
ref="supplierMyorder"
:show="active === 'supplierMyorder'"
/>
</div>
<div
class="page_wrap"
v-if="userType === 'scm_supplier'"
v-show="active === 'supplierPersonal'"
>
<supplier-personal
ref="supplierPersonal"
:show="active === 'supplierPersonal'"
/>
</div>
<div
class="page_wrap"
v-if="userType === 'scm_driver'"
v-show="active === 'driverMyorder'"
>
<driver-myorder ref="driverMyorder" :show="active === 'driverMyorder'" />
</div>
<div
class="page_wrap"
v-if="userType === 'scm_driver'"
v-show="active === 'mycar'"
>
<mycar ref="mycar" :show="active === 'mycar'" />
</div>
<div
class="page_wrap"
v-if="userType === 'scm_driver'"
v-show="active === 'driverPersonal'"
>
<driver-personal
ref="driverPersonal"
:show="active === 'driverPersonal'"
/>
</div>
<div
class="page_wrap"
v-if="userType === 'supplier_and_driver'"
v-show="active === 'supplierMyorder'"
>
<supplier-myorder
ref="supplierMyorder"
:show="active === 'supplierMyorder'"
/>
</div>
<div
class="page_wrap"
v-if="userType === 'supplier_and_driver'"
v-show="active === 'driverMyorder'"
>
<driver-myorder ref="driverMyorder" :show="active === 'driverMyorder'" />
</div>
<div
class="page_wrap"
v-if="userType === 'supplier_and_driver'"
v-show="active === 'mycar'"
>
<mycar ref="mycar" :show="active === 'mycar'" />
</div>
<div
class="page_wrap"
v-if="userType === 'supplier_and_driver'"
v-show="active === 'supplierPersonal'"
>
<supplier-personal
ref="supplierPersonal"
:show="active === 'supplierPersonal'"
/>
</div>
<view class="tab">
<view
v-for="(item, index) in tabbarOptions"
:key="index"
class="tab-item"
@click="switchTab(item, index)"
>
<image
class="tab_img"
:src="currentIndex == index ? item.selectedIconPath : item.iconPath"
></image>
<view
class="tab_text"
:style="{ color: currentIndex == index ? selectedColor : color }"
>{{ item.text }}</view
>
</view>
</view>
</div>
</template>
<script lang="ts" setup>
import supplierMyorder from '@/pages/supplierMyorder/supplierMyorder.vue'
import supplierPersonal from '@/pages/supplierPersonal/supplierPersonal.vue'
import driverMyorder from '@/pages/driverMyorder/driverMyorder.vue'
import mycar from '@/pages/mycar/mycar.vue'
import driverPersonal from '@/pages/driverPersonal/driverPersonal.vue'
let color = ref('#666666')
let selectedColor = ref('#355db4')
let currentIndex = ref(0)
const active = ref('')
const switchTab = (item: any, index: any) => {
// console.log('tabbar----switchTab-----list', item, index)
currentIndex.value = index
active.value = item.name
}
onLoad((option: any) => {
currentIndex.value = option.index || 0
active.value = option.name || tabbarOptions.value[0].name
})
onShow(() => {
active.value = active.value || tabbarOptions.value[0].name
currentIndex.value = currentIndex.value || 0
})
const userType = computed(() => {
return uni.getStorageSync('userType')
})
const tabbarOptions = computed(() => {
return {
scm_supplier: [
{
name: 'supplierMyorder',
pagePath: '/pages/supplierMyorder/supplierMyorder',
iconPath: '/static/tabbar/order.png',
selectedIconPath: '/static/tabbar/order_active.png',
text: '我的订单'
},
{
name: 'supplierPersonal',
pagePath: '/pages/supplierPersonal/supplierPersonal',
iconPath: '/static/tabbar/my.png',
selectedIconPath: '/static/tabbar/my_active.png',
text: '个人中心'
}
],
scm_driver: [
{
name: 'driverMyorder',
pagePath: '/pages/driverMyorder/driverMyorder',
iconPath: '/static/tabbar/waybill.png',
selectedIconPath: '/static/tabbar/waybill_active.png',
text: '我的运单'
},
{
name: 'mycar',
pagePath: '/pages/mycar/mycar',
iconPath: '/static/tabbar/car.png',
selectedIconPath: '/static/tabbar/car_active.png',
text: '我的车辆'
},
{
name: 'driverPersonal',
pagePath: '/pages/driverPersonal/driverPersonal',
iconPath: '/static/tabbar/my.png',
selectedIconPath: '/static/tabbar/my_active.png',
text: '个人中心'
}
],
supplier_and_driver: [
{
name: 'supplierMyorder',
pagePath: '/pages/supplierMyorder/supplierMyorder',
iconPath: '/static/tabbar/order.png',
selectedIconPath: '/static/tabbar/order_active.png',
text: '我的订单'
},
{
name: 'driverMyorder',
pagePath: '/pages/driverMyorder/driverMyorder',
iconPath: '/static/tabbar/order.png',
selectedIconPath: '/static/tabbar/order_active.png',
text: '我的运单'
},
{
name: 'mycar',
pagePath: '/pages/mycar/mycar',
iconPath: '/static/tabbar/car.png',
selectedIconPath: '/static/tabbar/car_active.png',
text: '我的车辆'
},
{
name: 'supplierPersonal',
pagePath: '/pages/supplierPersonal/supplierPersonal',
iconPath: '/static/tabbar/my.png',
selectedIconPath: '/static/tabbar/my_active.png',
text: '个人中心'
}
]
}[userType.value]
})
</script>
<style lang="scss" scoped>
.tabbar_page {
height: 100%;
.page_wrap {
height: calc(100% - 84px);
&.hidden {
display: none;
}
}
.tab {
position: fixed;
bottom: 0;
left: 0;
right: 0;
height: 100rpx;
background: white;
display: flex;
justify-content: center;
align-items: center;
padding-bottom: env(safe-area-inset-bottom); // 适配iphoneX的底部
.tab-item {
flex: 1;
text-align: center;
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
.tab_img {
width: 45rpx;
height: 45rpx;
}
.tab_text {
font-size: 25rpx;
margin: 9rpx 0;
}
}
}
}
.flex-box {
display: -webkit-box;
display: -webkit-flex;
display: flex;
}
.flex-col {
flex-direction: column
}
</style>
相关文章
基于ElementUi再次封装基础组件文档
Vue3+Vite+Ts+Pinia+Qiankun后台管理系统
vue3+ts基于Element-plus再次封装基础组件文档