feat(derective): 增加权限指令

This commit is contained in:
chen.home 2023-03-26 13:17:41 +08:00
parent ac0666b825
commit dd408611de
16 changed files with 198 additions and 66 deletions

View File

@ -1,17 +1,36 @@
import Mock from 'mockjs';
import { resultSuccess } from '../utils';
import { resultSuccess, resultFailed } from '../utils';
const Random = Mock.Random;
const token = () => Random.string('upper', 32, 32);
const userInfo = {
userId: 1,
userName: 'iamsee',
realName: '管理员大人',
avatar: 'https://z3.ax1x.com/2021/10/29/5jnWgf.jpg',
role: "super",
};
const userData = [
{
userId: 1,
userName: 'super',
password: '123456',
nickName: '超级管理员大人',
avatar: 'https://z3.ax1x.com/2021/10/29/5jnWgf.jpg',
role: 'super',
},
{
userId: 2,
userName: 'admin',
password: '123456',
nickName: '管理员大人',
avatar: 'https://z3.ax1x.com/2021/10/29/5jnWgf.jpg',
role: 'admin',
},
{
userId: 3,
userName: 'user',
password: '123456',
nickName: '用户大人',
avatar: 'https://z3.ax1x.com/2021/10/29/5jnWgf.jpg',
role: 'user',
},
];
const userRoutes = [
{
name: 'dashboard',
@ -312,7 +331,7 @@ const userRoutes = [
icon: 'icon-park-outline:wrong-user',
},
},
]
],
},
{
name: 'error',
@ -322,7 +341,6 @@ const userRoutes = [
title: '异常页',
requiresAuth: true,
icon: 'icon-park-outline:error-computer',
},
children: [
{
@ -425,15 +443,32 @@ const userRoutes = [
export default [
{
url: '/mock/login',
timeout: 1000,
method: 'post',
response: () => {
return resultSuccess({ token: token(), refreshToken: token() });
response: (options: any) => {
const { userName = undefined, password = undefined } = options.body;
if (!userName || !password) {
return resultFailed(null, '账号密码不全');
}
const userInfo = userData.find((item) => item.userName === userName && item.password === password);
if (userInfo) {
return {
code: 200,
message: 'ok',
data: {
userId: userInfo.userId,
token: token(),
refreshToken: token(),
},
};
}
return resultFailed(null, '账号密码错误');
},
},
{
url: '/mock/updateToken',
timeout: 1000,
method: 'post',
response: () => {
return resultSuccess({ token: token(), refreshToken: token() });
@ -441,15 +476,21 @@ export default [
},
{
url: '/mock/getUserInfo',
timeout: 1000,
method: 'get',
response: () => {
return resultSuccess(userInfo);
response: (options: any) => {
const { userId = undefined } = options.query;
if (!userId) {
return resultFailed(null, '未传入用户id');
}
const userInfo = userData.find((item) => item.userId == userId);
if (userInfo) {
return resultSuccess(userInfo);
}
return resultFailed(null, '未找到用户信息,请检查提交参数');
},
},
{
url: '/mock/getUserRoutes',
timeout: 1000,
method: 'post',
response: () => {
return resultSuccess(userRoutes);

View File

@ -1,16 +1,16 @@
import Mock from 'mockjs';
export function resultSuccess(data: any, { msg = 'success' } = {}) {
export function resultSuccess(data: any, msg?:string ) {
return Mock.mock({
code: 200,
data,
msg,
msg: msg || 'success',
});
}
export function resultFailed(data: any, { msg = 'failed' } = {}) {
export function resultFailed(data: any, msg?: string ) {
return Mock.mock({
code: 400,
code: 500,
data,
msg,
msg: msg || 'failed',
});
}

6
src/directive/index.ts Normal file
View File

@ -0,0 +1,6 @@
import type { App } from 'vue';
import { setupPermission } from './permission'
export function setupDirectives(app: App) {
setupPermission(app);
}

View File

@ -0,0 +1,26 @@
import type { App, Directive } from 'vue';
import { usePermission } from '@/hooks';
export function setupPermission(app: App) {
const { hasPermission } = usePermission();
function updatapermission(el: HTMLElement, permission: Auth.RoleType | Auth.RoleType[]) {
if (!permission) {
throw new Error(`v-permissson Directive with no explicit role attached`);
}
if (!hasPermission(permission)) {
el.parentElement?.removeChild(el);
}
}
const permissionDirective: Directive<HTMLElement, Auth.RoleType | Auth.RoleType[]> = {
mounted(el, binding) {
updatapermission(el, binding.value)
},
updated(el, binding) {
updatapermission(el, binding.value)
}
}
app.directive('permission', permissionDirective)
}

View File

@ -34,6 +34,7 @@ export function usePermission() {
if (!permission) return true
if (!authStore.userInfo) return false
const { role } = authStore.userInfo
let has = role === 'super';

View File

@ -10,7 +10,7 @@
size="large"
:src="userInfo?.avatar"
/>
{{ userInfo?.realName }}
{{ userInfo?.nickName }}
</HeaderButton>
</n-dropdown>
</template>
@ -42,7 +42,15 @@ const { userInfo, resetAuthStore } = useAuthStore();
];
const handleSelect = (key: string | number) => {
if (key === 'loginOut') {
resetAuthStore();
window.$dialog.info({
title: '退出登录',
content: '确认退出当前账号?',
positiveText: '确定',
negativeText: '取消',
onPositiveClick: () => {
resetAuthStore();
},
})
}
if (key === 'userCenter') {
router.push('/userCenter')

View File

@ -4,6 +4,7 @@ import AppLoading from './components/common/appLoading.vue';
import { setupRouter } from './router';
import { setupAssets } from './plugins';
import { setupStore } from './store';
import { setupDirectives } from './directive'
async function setupApp() {
// 引入静态资源
@ -16,6 +17,8 @@ async function setupApp() {
const app = createApp(App);
// 安装pinia全局状态库
setupStore(app);
// 安装自定义指令
setupDirectives(app)
// 安装router
await setupRouter(app);
// 挂载

View File

@ -11,8 +11,8 @@ export function fetchLogin(params: Ilogin) {
export function fetchUpdateToken(params: any) {
return mockRequest.post<ApiAuth.loginToken>('/updateToken', params);
}
export function fetchUserInfo() {
return mockRequest.get<Auth.UserInfo>('/getUserInfo');
export function fetchUserInfo(params:any) {
return mockRequest.get<Auth.UserInfo>('/getUserInfo',{params});
}
export function fetchUserRoutes(params: { userId: number }) {
return mockRequest.post<any>('/getUserRoutes', params);

View File

@ -6,11 +6,19 @@ import { unref } from 'vue';
import { useRouteStore } from './route';
import { local } from '@/utils';
const emptyInfo: Auth.UserInfo = {
userId: 0,
userName: '',
nickName: '',
avatar: '',
role: 'user',
};
export const useAuthStore = defineStore('auth-store', {
state: () => {
return {
userInfo: local.get('userInfo'),
token: local.get('token'),
userInfo: local.get('userInfo') || emptyInfo,
token: local.get('token') || '',
refreshToken: local.get('refreshToken') || '',
loginLoading: false,
};
},
@ -44,7 +52,11 @@ export const useAuthStore = defineStore('auth-store', {
/* 用户登录 */
async login(userName: string, password: string) {
this.loginLoading = true;
const { data } = await fetchLogin({ userName, password });
const { error, data } = await fetchLogin({ userName, password });
if (error) {
this.loginLoading = false;
return;
}
// 处理登录信息
await this.handleAfterLogin(data);
@ -69,41 +81,37 @@ export const useAuthStore = defineStore('auth-store', {
// 触发用户提示
window.$notification?.success({
title: '登录成功!',
content: `欢迎回来😊,${this.userInfo?.realName}!`,
content: `欢迎回来😊,${this.userInfo.nickName}!`,
duration: 3000,
});
return;
}
// 如果不成功则重置存储
this.resetAuthStore();
// 登录失败提示
window.$notification?.error({
title: '登录失败!',
content: `验证失败,请检查账号密码`,
duration: 3000,
});
},
/* 缓存用户信息 */
async catchUserInfo(userToken: ApiAuth.loginToken) {
let catchSuccess = false;
// 先存储token
const { token, refreshToken } = userToken;
local.set('token', token);
local.set('refreshToken', refreshToken,)
// 请求/存储用户信息
const { data } = await fetchUserInfo();
if (data) {
local.set('userInfo', data);
const { token, refreshToken, userId } = userToken;
const { error, data } = await fetchUserInfo({ userId });
if (error) {
return catchSuccess;
}
// 再将token和userInfo初始化
this.userInfo = data;
// 先存储token
local.set('token', token);
local.set('refreshToken', refreshToken);
this.token = token;
this.refreshToken = refreshToken;
// 请求/存储用户信息
local.set('userInfo', data);
this.userInfo = data;
catchSuccess = true;
return catchSuccess;
},
toggleUserRole(role: Auth.RoleType) {
this.login(role, '123456');
},
},
});

View File

@ -6,9 +6,10 @@ declare namespace ApiAuth {
type UserInfo = Auth.UserInfo;
/* 登录token字段 */
interface loginToken {
token: string;
refreshToken: string;
}
token: string;
refreshToken: string;
userId: number;
}
}
declare namespace CommonList {
/* 返回的性别类型 */

View File

@ -8,7 +8,7 @@ declare namespace Auth {
/** 用户名 */
userName: string;
/* 用户称呼 */
realName: string;
nickName: string;
/* 用户头像 */
avatar: string;
/** 用户角色类型 */

View File

@ -5,9 +5,10 @@ declare namespace Storage {
}
interface Local {
userInfo: Auth.UserInfo
token: string
refreshToken: string
tabsRoutes: string
}
userInfo: Auth.UserInfo;
token: string;
refreshToken: string;
tabsRoutes: string;
login_account:any;
}
}

View File

@ -14,7 +14,7 @@
/>
<div class="pl-12px">
<h3 class="text-18px font-semibold">
您好{{ userInfo?.realName }},今天又是充满活力的一天
您好{{ userInfo?.nickName }},今天又是充满活力的一天
</h3>
<p class="leading-30px text-[#999]">
今日多云转晴20 - 25

View File

@ -134,8 +134,8 @@ const rules = {
},
};
const formValue = ref({
account: 'admin',
pwd: '000000',
account: 'super',
pwd: '123456',
code: '1234',
});
const isRemember = ref(false);

View File

@ -2,13 +2,50 @@
<div>
权限示例:
<n-h1> 当前权限{{ role }}</n-h1>
<n-button-group>
<n-button
v-for="item in roleList"
:key="item"
type="default"
@click="authStore.toggleUserRole(item)"
>
{{ item }}
</n-button>
</n-button-group>
<n-h2>v-permission 指令用法</n-h2>
<n-space>
<n-button v-permission="'super'">
仅super可见
</n-button>
<n-button v-permission="['admin']">
admin可见
</n-button>
</n-space>
<n-h2>usePermission 函数用法</n-h2>
<n-space>
<n-button v-if="hasPermission('super')">
super可见
</n-button>
<n-button v-if="hasPermission('admin')">
admin可见
</n-button>
<n-button v-if="hasPermission(['admin', 'user'])">
admin和user可见
</n-button>
</n-space>
</div>
</template>
<script setup lang="ts">
import { useAuthStore } from '@/store';
const authStore = useAuthStore();
const { role } = authStore.userInfo;
import { useAuthStore } from '@/store';
import { usePermission } from '@/hooks';
const authStore = useAuthStore();
const { hasPermission } = usePermission();
const { role } = authStore.userInfo;
const roleList: Auth.RoleType[] = ['super', 'admin', 'user'];
</script>
<style scoped></style>

View File

@ -24,7 +24,7 @@
{{ userInfo?.userName }}
</n-descriptions-item>
<n-descriptions-item label="真实名称">
{{ userInfo?.realName }}
{{ userInfo?.nickName }}
</n-descriptions-item>
<n-descriptions-item label="角色">
{{ userInfo?.role }}