背景说明
在小程序中做一些权限管理的时候,比如由哪些页面指定用户可看,可以通过 uni.addInterceptor 做接口和页面跳转拦截,非常方便,但是如果牵扯到这个页面如果可以分享,那么就不太适用了,由于分享进入直接进入小程序周期,走的onLoad, onShow,此刻页面已经进入了,没有被拦截器拦截,此刻该如何处理呢
方案一
写一个工具拦截方法,在全局或指定页面onLoad中进行执行,保证拿到用户权限后执行该方法,如果没有权限,raunchTo跳转业务落地页(登录页,首页),有权限可正常查看
<template>
<view>
<header></header>
<main></main>
<footer></footer>
</view>
</template>
<script setup>
import { onLoad } from "@dcloudio/uni-app";
import { ref } from 'vue';
import { isAuth } from '@/util/auth.js'
// 页面参数
const msgOptions = ref({})
onLoad(async (option) => {
// 判断权限,跳转登录或首页
if(option.isShare) isAuth(option)
});
</script>
<style scoped>
</style>
但是该方法有个不太好的点,就是通过分享还是会进入到这个页面,再跳转,由于此刻onLoad,onShow已经默置执行了,会闪屏一下突然跳转到其他落地页,感觉体验上不是特别好,如果不想让用户看业务页Dom元素,看到暂无权限底图或直接跳转呢?
方案二
vue3新特性中 有个 Suspense,是等到loading结束,再渲染页面
查看Vue Suspense
方法说明:在正确渲染组件之前进行一些异步请求是很常见的事。suspense允许将等待过程提升到组件树中处理,而不是在单个组件中。
但是uniapp目前还不支持这个新特性,不过这种体验优化倒是提供了一个思路,是不是可以通过自定义实现一个拦截逻辑全局组件,类似slot插槽形式包裹去做处理呢?
1. 封装权限拦截器组件 GlobalPermission
<template>
<!-- 加载状态 -->
<view v-if="loading" class="loading">
<text>加载中...</text>
</view>
<!-- 权限校验通过,渲染目标组件 -->
<slot v-else-if="hasPermission"></slot>
<!-- 权限校验未通过,显示无权限提示 -->
<view v-else class="no-permission">
<text>无权限访问</text>
</view>
</template>
<script setup>
import { ref, onMounted } from 'vue';
const props = defineProps({
// 自定义权限校验函数
validatePermission: {
type: Function,
required: true,
},
// 无权限时跳转的路径
redirectPath: {
type: String,
default: '/pages/index/index',
},
options: {
type: Object,
default: () => ({})
}
});
const hasPermission = ref(false); // 是否有权限
// 异步权限校验
onMounted(async () => {
try {
// 调用权限校验函数,或全局拦截方法
const isValid = await props.validatePermission();
hasPermission.value = isValid;
if (!isValid) {
// 无权限跳转
uni.reLaunch({
url: props.redirectPath,
});
}
} catch (error) {
console.error('权限校验失败:', error);
} finally {
loading.value = false; // 结束加载状态
}
});
</script>
<style scoped>
</style>
2 main.js 引入注册
import GlobalPermission from './components/global-permission/index.vue'
app.component("GlobalPermission", GlobalPermission);
3 页面使用
例如:某个权限页面,当然也可以直接跳转,不需要额外loading,或者通过代码添加uni.showLoading,hideLoading去实现,只保留一个slot,这个不管是通过分享还是正常进入
<template>
<GlobalPermission :option="msgOptions">
<view>
<header></header>
<main></main>
<footer></footer>
</view>
</GlobalPermission>
</template>
<script setup>
import { onLoad } from "@dcloudio/uni-app";
import { ref } from 'vue';
// 页面参数
const msgOptions = ref({})
onLoad(async (option) => {
msgOptions.value = Object.assign({}, option)
});
</script>
<style scoped>
</style>
目前业务上通过第二种进行实现的,如果类似场景,有其他好的方案欢迎讨论哈