前言
在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">
<!-- 根据 userType 和 active 显示对应的组件 -->
<div class="page_wrap" v-show="isActive('supplierMyorder')">
<supplier-myorder
ref="supplierMyorder"
:show="isActive('supplierMyorder')"
/>
</div>
<div class="page_wrap" v-show="isActive('supplierPersonal')">
<supplier-personal
ref="supplierPersonal"
:show="isActive('supplierPersonal')"
/>
</div>
<div class="page_wrap" v-show="isActive('driverMyorder')">
<driver-myorder ref="driverMyorder" :show="isActive('driverMyorder')" />
</div>
<div class="page_wrap" v-show="isActive('mycar')">
<mycar ref="mycar" :show="isActive('mycar')" />
</div>
<div class="page_wrap" v-show="isActive('driverPersonal')">
<driver-personal
ref="driverPersonal"
:show="isActive('driverPersonal')"
/>
</div>
<!-- 底部 TabBar -->
<view class="tab">
<view
v-for="(item, index) in tabbarOptions"
:key="index"
class="tab-item"
@tap="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'
// 定义类型
interface TabbarOption {
name: string
pagePath: string
iconPath: string
selectedIconPath: string
text: string
}
const color = ref<string>('#666666')
const selectedColor = ref<string>('#355db4')
const currentIndex = ref<number>(0)
const active = ref<string>('')
// 切换 Tab 方法
const switchTab = (item: TabbarOption, index: number): void => {
currentIndex.value = index
active.value = item.name
}
// 页面加载时初始化
onLoad((option: { index?: number; name?: string }) => {
currentIndex.value = option.index || 0
active.value = option.name || tabbarOptions.value[0]?.name || ''
})
// 页面显示时确保 active 和 currentIndex 有值
onShow(() => {
active.value = active.value || tabbarOptions.value[0]?.name || ''
currentIndex.value = currentIndex.value || 0
console.log('userType----', userType.value)
})
// 从缓存中获取用户类型
const userType = computed<string>(() => {
return uni.getStorageSync('userType') || ''
})
// 根据用户类型生成 TabBar 配置
const tabbarOptions = computed<TabbarOption[]>(() => {
const commonOptions: TabbarOption[] = [
{
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/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: 'supplierPersonal',
pagePath: '/pages/supplierPersonal/supplierPersonal',
iconPath: '/static/tabbar/my.png',
selectedIconPath: '/static/tabbar/my_active.png',
text: '个人中心'
},
{
name: 'driverPersonal',
pagePath: '/pages/driverPersonal/driverPersonal',
iconPath: '/static/tabbar/my.png',
selectedIconPath: '/static/tabbar/my_active.png',
text: '个人中心'
}
]
const userTypeOptions: Record<string, TabbarOption[]> = {
scm_supplier: [commonOptions[0], commonOptions[3]],
scm_driver: [commonOptions[1], commonOptions[2], commonOptions[4]],
supplier_and_driver: [
commonOptions[0],
commonOptions[1],
commonOptions[2],
commonOptions[3]
]
}
return userTypeOptions[userType.value] || []
})
// 判断当前激活的页面
const isActive = (name: string): boolean => {
return active.value === name
}
</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的底部
border-top: 1px solid #eee;
.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;
}
}
}
}
</style>
相关文章
Vue3+Vite+Ts+Pinia+Qiankun后台管理系统