mirror of
https://github.com/chansee97/nova-admin.git
synced 2025-04-05 19:41:59 +08:00
refactor(utils): 完善本地存储类型,移除用户获取工具类
This commit is contained in:
parent
4d0dcf1695
commit
0181de4670
@ -1,5 +1,5 @@
|
||||
<div align="center">
|
||||
<h1> <img src="./public/logo.svg" style="width:30px"/> Ench Admin</h1>
|
||||
<h1> <img src="./public/favicon.svg" style="width:30px"/> Ench Admin</h1>
|
||||
</div>
|
||||
|
||||
<div align="center">
|
||||
@ -54,7 +54,7 @@ pnpm commit
|
||||
|
||||
| [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/edge/edge_48x48.png" alt="IE / Edge" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)<br/>IE / Edge | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/firefox/firefox_48x48.png" alt="Firefox" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)<br/>Firefox | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/chrome/chrome_48x48.png" alt="Chrome" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)<br/>Chrome | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/safari/safari_48x48.png" alt="Safari" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)<br/>Safari |
|
||||
| :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |
|
||||
| last 2 versions | last 2 versions | last 2 versions | last 2 versions |
|
||||
| >=88 | >=78 | >=87 | >=14 |
|
||||
|
||||
## 🙌 学习交流
|
||||
Ench-Admin 是完全开源免费的项目,旨在帮助开发者更方便地进行中大型管理系统开发,有使用问题欢迎在QQ交流群内提问。
|
||||
@ -68,4 +68,4 @@ Ench-Admin 是完全开源免费的项目,旨在帮助开发者更方便地进
|
||||
|
||||
## 🧾License
|
||||
|
||||
该项目采用MIT许可证,详见[LICENSE](LICENSE)文件。
|
||||
[MIT](LICENSE)
|
||||
|
@ -2,7 +2,7 @@
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
||||
<link rel="icon" href="/favicon.svg" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>%VITE_APP_TITLE%</title>
|
||||
</head>
|
||||
|
@ -6,7 +6,7 @@ const Random = Mock.Random;
|
||||
const token = () => Random.string('upper', 32, 32);
|
||||
|
||||
const userInfo = {
|
||||
userId: '1',
|
||||
userId: 1,
|
||||
userName: 'iamsee',
|
||||
realName: '管理员大人',
|
||||
avatar: 'https://z3.ax1x.com/2021/10/29/5jnWgf.jpg',
|
||||
@ -308,7 +308,7 @@ const userRoutes = [
|
||||
meta: {
|
||||
title: '超管super可见',
|
||||
requiresAuth: true,
|
||||
roles:['super'],
|
||||
roles: ['super'],
|
||||
icon: 'icon-park-outline:wrong-user',
|
||||
},
|
||||
},
|
||||
@ -322,7 +322,7 @@ const userRoutes = [
|
||||
title: '异常页',
|
||||
requiresAuth: true,
|
||||
icon: 'icon-park-outline:error-computer',
|
||||
|
||||
|
||||
},
|
||||
children: [
|
||||
{
|
||||
|
@ -1,15 +1,3 @@
|
||||
/** 本地存储信息字段 */
|
||||
export const storageKey = {
|
||||
/* 用户信息 */
|
||||
userInfo: '__USER_INFO__',
|
||||
/* token */
|
||||
token: '__TOKEN__',
|
||||
/* refreshToken */
|
||||
refreshToken: '__REFRESH_TOKEN__',
|
||||
/* 标签栏信息 */
|
||||
tabsRoutes: '__TABS_ROUTES__',
|
||||
};
|
||||
|
||||
/** 本地存储前缀 */
|
||||
export const STORAGE_PREFIX = '';
|
||||
|
||||
|
@ -6,23 +6,16 @@
|
||||
accordion
|
||||
:options="routesStore.menus"
|
||||
:value="routesStore.activeMenu"
|
||||
@update:value="handleClickMenu"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useAppStore } from '@/store';
|
||||
import { useAppRouter } from '@/hooks';
|
||||
import { useRouteStore } from '~/src/store/modules/route';
|
||||
import type { MenuOption } from 'naive-ui';
|
||||
|
||||
const { routerPush } = useAppRouter();
|
||||
const appStore = useAppStore();
|
||||
const routesStore = useRouteStore();
|
||||
|
||||
const handleClickMenu = (key: string, item: MenuOption) => {
|
||||
routerPush(key);
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { RouteLocationNormalized, NavigationGuardNext } from 'vue-router';
|
||||
import { getToken } from '@/utils/auth';
|
||||
import { local } from '@/utils';
|
||||
import { useRouteStore } from '@/store';
|
||||
|
||||
export async function createPermissionGuard(
|
||||
@ -10,7 +10,7 @@ export async function createPermissionGuard(
|
||||
const routeStore = useRouteStore();
|
||||
|
||||
// 判断有无TOKEN,登录鉴权
|
||||
const isLogin = Boolean(getToken());
|
||||
const isLogin = Boolean(local.get('token'));
|
||||
if (!isLogin) {
|
||||
if (to.name == 'login') {
|
||||
next()
|
||||
|
@ -11,12 +11,12 @@ interface Itoken {
|
||||
export function fetchLogin(params: Ilogin) {
|
||||
return mockRequest.post<any>('/login', params);
|
||||
}
|
||||
export function fetchUpdateToken(params: string) {
|
||||
export function fetchUpdateToken(params: any) {
|
||||
return mockRequest.post<Itoken>('/updateToken', params);
|
||||
}
|
||||
export function fetchUserInfo() {
|
||||
return mockRequest.get('/getUserInfo');
|
||||
return mockRequest.get<Auth.UserInfo>('/getUserInfo');
|
||||
}
|
||||
export function fetchUserRoutes(params: string) {
|
||||
export function fetchUserRoutes(params: { userId: number }) {
|
||||
return mockRequest.post<any>('/getUserRoutes', params);
|
||||
}
|
||||
|
@ -10,7 +10,7 @@ import {
|
||||
} from '@/config';
|
||||
import { useAuthStore } from '@/store';
|
||||
import { fetchUpdateToken } from '@/service';
|
||||
import { setToken, setRefreshToken, getRefreshToken } from '@/utils';
|
||||
import { local } from '@/utils';
|
||||
import { showError } from './utils';
|
||||
|
||||
type ErrorStatus = keyof typeof ERROR_STATUS;
|
||||
@ -123,11 +123,11 @@ export async function handleServiceResult<T = any>(data: any, error: Service.Req
|
||||
*/
|
||||
export async function handleRefreshToken(config: AxiosRequestConfig) {
|
||||
const { resetAuthStore } = useAuthStore();
|
||||
const refreshToken = getRefreshToken();
|
||||
const refreshToken = local.get('refreshToken');
|
||||
const { data } = await fetchUpdateToken(refreshToken);
|
||||
if (data) {
|
||||
setRefreshToken(data.token);
|
||||
setToken(data.refreshToken);
|
||||
local.set('refreshToken', data.token, )
|
||||
local.set('token', data.refreshToken)
|
||||
|
||||
// 设置token
|
||||
if (config.headers) {
|
||||
|
@ -1,6 +1,6 @@
|
||||
import axios from 'axios';
|
||||
import type { AxiosInstance, AxiosRequestConfig, AxiosError } from 'axios';
|
||||
import { getToken } from '@/utils';
|
||||
import { local } from '@/utils';
|
||||
import { REFRESH_TOKEN_CODE } from '@/config';
|
||||
import {
|
||||
handleAxiosError,
|
||||
@ -45,7 +45,7 @@ export default class createAxiosInstance {
|
||||
|
||||
// 设置token
|
||||
typeof handleConfig.headers.set === 'function' &&
|
||||
handleConfig.headers.set('Authorization', `Bearer ${getToken() || ''}`);
|
||||
handleConfig.headers.set('Authorization', `Bearer ${local.get('token') || ''}`);
|
||||
}
|
||||
return handleConfig;
|
||||
},
|
||||
|
@ -1,16 +1,16 @@
|
||||
import { defineStore } from 'pinia';
|
||||
import { fetchLogin, fetchUserInfo } from '@/service';
|
||||
import { setUserInfo, getUserInfo, getToken, setToken, clearAuthStorage, setRefreshToken } from '@/utils/auth';
|
||||
import { router } from '@/router';
|
||||
import { useAppRouter } from '@/hooks';
|
||||
import { unref } from 'vue';
|
||||
import { useRouteStore } from './route';
|
||||
import { local } from '@/utils';
|
||||
|
||||
export const useAuthStore = defineStore('auth-store', {
|
||||
state: () => {
|
||||
return {
|
||||
userInfo: getUserInfo(),
|
||||
token: getToken(),
|
||||
userInfo: local.get('userInfo'),
|
||||
token: local.get('token'),
|
||||
loginLoading: false,
|
||||
};
|
||||
},
|
||||
@ -27,7 +27,7 @@ export const useAuthStore = defineStore('auth-store', {
|
||||
const { toLogin } = useAppRouter(false);
|
||||
const { resetRouteStore } = useRouteStore();
|
||||
// 清除本地缓存
|
||||
clearAuthStorage();
|
||||
this.clearAuthStorage();
|
||||
// 清空路由、菜单等数据
|
||||
resetRouteStore();
|
||||
this.$reset();
|
||||
@ -35,6 +35,11 @@ export const useAuthStore = defineStore('auth-store', {
|
||||
toLogin();
|
||||
}
|
||||
},
|
||||
clearAuthStorage() {
|
||||
local.remove('token');
|
||||
local.remove('refreshToken');
|
||||
local.remove('userInfo');
|
||||
},
|
||||
|
||||
/* 用户登录 */
|
||||
async login(userName: string, password: string) {
|
||||
@ -64,7 +69,7 @@ export const useAuthStore = defineStore('auth-store', {
|
||||
// 触发用户提示
|
||||
window.$notification?.success({
|
||||
title: '登录成功!',
|
||||
content: `欢迎回来😊,${this.userInfo.realName}!`,
|
||||
content: `欢迎回来😊,${this.userInfo?.realName}!`,
|
||||
duration: 3000,
|
||||
});
|
||||
return;
|
||||
@ -84,12 +89,14 @@ export const useAuthStore = defineStore('auth-store', {
|
||||
let catchSuccess = false;
|
||||
// 先存储token
|
||||
const { token, refreshToken } = userToken;
|
||||
setToken(token);
|
||||
setRefreshToken(refreshToken);
|
||||
local.set('token', token);
|
||||
local.set('refreshToken', refreshToken,)
|
||||
|
||||
// 请求/存储用户信息
|
||||
const { data } = await fetchUserInfo();
|
||||
setUserInfo(data);
|
||||
if (data) {
|
||||
local.set('userInfo', data);
|
||||
}
|
||||
// 再将token和userInfo初始化
|
||||
this.userInfo = data;
|
||||
this.token = token;
|
||||
|
@ -1,12 +1,13 @@
|
||||
import { defineStore } from 'pinia';
|
||||
import { renderIcon, getUserInfo} from '@/utils';
|
||||
import { renderIcon,local } from '@/utils';
|
||||
import { MenuOption } from 'naive-ui';
|
||||
import { createDynamicRoutes } from '@/router/guard/dynamic';
|
||||
import { router } from '@/router';
|
||||
import { fetchUserRoutes } from '@/service';
|
||||
import { staticRoutes } from '@/router/modules';
|
||||
import { useAuthStore } from '@/store';
|
||||
import { RouterLink } from 'vue-router'
|
||||
import { usePermission } from '@/hooks'
|
||||
import { h } from 'vue'
|
||||
|
||||
interface RoutesStatus {
|
||||
isInitAuthRoute: boolean;
|
||||
@ -80,8 +81,7 @@ export const useRouteStore = defineStore('route-store', {
|
||||
},
|
||||
//* 将返回的路由表渲染成侧边栏 */
|
||||
transformAuthRoutesToMenus(userRoutes: AppRoute.Route[]): MenuOption[] {
|
||||
const authStore = useAuthStore()
|
||||
const { role } = authStore.userInfo
|
||||
|
||||
return userRoutes
|
||||
/** 隐藏不需要显示的菜单 */
|
||||
.filter((item) => {
|
||||
@ -94,13 +94,19 @@ export const useRouteStore = defineStore('route-store', {
|
||||
/** 转换为侧边菜单数据结构 */
|
||||
.map((item) => {
|
||||
const target: MenuOption = {
|
||||
label: item.meta.title,
|
||||
label: () =>
|
||||
h(
|
||||
RouterLink,
|
||||
{
|
||||
to: {
|
||||
path: item.path
|
||||
}
|
||||
},
|
||||
{ default: () => item.meta.title }
|
||||
),
|
||||
key: item.path,
|
||||
icon: renderIcon(item.meta.icon)
|
||||
};
|
||||
/** 判断有无图标 */
|
||||
if (item.meta.icon) {
|
||||
target.icon = renderIcon(item.meta.icon);
|
||||
}
|
||||
/** 判断子元素 */
|
||||
if (item.children) {
|
||||
const children = this.transformAuthRoutesToMenus(item.children);
|
||||
@ -115,8 +121,13 @@ export const useRouteStore = defineStore('route-store', {
|
||||
/* 初始化动态路由 */
|
||||
async initDynamicRoute() {
|
||||
// 根据用户id来获取用户的路由
|
||||
const { userId } = getUserInfo()
|
||||
const { data: routes } = await fetchUserRoutes({ userId });
|
||||
const userInfo = local.get('userInfo')
|
||||
|
||||
if (!userInfo||!userInfo.userId) {
|
||||
return
|
||||
}
|
||||
|
||||
const { data: routes } = await fetchUserRoutes({ userId: userInfo.userId});
|
||||
// 根据用户返回的路由表来生成真实路由
|
||||
const appRoutes = await createDynamicRoutes(routes);
|
||||
// 生成侧边菜单
|
||||
|
5
src/typings/business.d.ts
vendored
5
src/typings/business.d.ts
vendored
@ -16,7 +16,7 @@ declare namespace Auth {
|
||||
type RoleType = 'super' | 'admin' | 'manage' | 'user';
|
||||
interface UserInfo {
|
||||
/** 用户id */
|
||||
userId: string;
|
||||
userId: number;
|
||||
/** 用户名 */
|
||||
userName: string;
|
||||
/* 用户称呼 */
|
||||
@ -25,8 +25,7 @@ declare namespace Auth {
|
||||
avatar: string;
|
||||
/** 用户角色类型 */
|
||||
role: RoleType;
|
||||
/* 密码 */
|
||||
password: string;
|
||||
|
||||
}
|
||||
}
|
||||
/* 系统消息 */
|
||||
|
13
src/typings/storage.d.ts
vendored
Normal file
13
src/typings/storage.d.ts
vendored
Normal file
@ -0,0 +1,13 @@
|
||||
declare namespace Storage {
|
||||
|
||||
interface Session {
|
||||
demoKey: string
|
||||
}
|
||||
|
||||
interface Local {
|
||||
userInfo: Auth.UserInfo
|
||||
token: string
|
||||
refreshToken: string
|
||||
tabsRoutes: string
|
||||
}
|
||||
}
|
@ -1,49 +0,0 @@
|
||||
import { local } from './storage';
|
||||
import { storageKey } from '@/config';
|
||||
|
||||
const DURATION = 6 * 60 * 60;
|
||||
|
||||
/* 获取当前token */
|
||||
export function getToken() {
|
||||
return local.get(storageKey.token);
|
||||
}
|
||||
/* 设置token */
|
||||
export function setToken(data: string) {
|
||||
local.set(storageKey.token, data, DURATION);
|
||||
}
|
||||
/* 移除token */
|
||||
export function removeToken() {
|
||||
local.remove(storageKey.token);
|
||||
}
|
||||
/* 获取当前refreshToken */
|
||||
export function getRefreshToken() {
|
||||
return local.get(storageKey.refreshToken);
|
||||
}
|
||||
/* 设置refreshToken */
|
||||
export function setRefreshToken(data: string) {
|
||||
local.set(storageKey.refreshToken, data, DURATION);
|
||||
}
|
||||
/* 移除refreshToken */
|
||||
export function removeRefreshToken() {
|
||||
local.remove(storageKey.refreshToken);
|
||||
}
|
||||
|
||||
/* 获取用户详情 */
|
||||
export function getUserInfo() {
|
||||
return local.get(storageKey.userInfo);
|
||||
}
|
||||
/* 设置用户详情 */
|
||||
export function setUserInfo(data: any) {
|
||||
local.set(storageKey.userInfo, data);
|
||||
}
|
||||
/* 移除用户详情 */
|
||||
export function removeUserInfo() {
|
||||
local.remove(storageKey.userInfo);
|
||||
}
|
||||
|
||||
/** 去除用户相关缓存 */
|
||||
export function clearAuthStorage() {
|
||||
removeToken();
|
||||
removeRefreshToken();
|
||||
removeUserInfo();
|
||||
}
|
@ -2,6 +2,9 @@ import { h } from 'vue';
|
||||
import { Icon } from '@iconify/vue';
|
||||
import { NIcon } from 'naive-ui';
|
||||
|
||||
export function renderIcon(icon: string) {
|
||||
export function renderIcon(icon?: string) {
|
||||
if (!icon) {
|
||||
return undefined
|
||||
}
|
||||
return () => h(NIcon, null, { default: () => h(Icon, { icon }) });
|
||||
}
|
||||
|
@ -1,4 +1,3 @@
|
||||
export * from './auth';
|
||||
export * from './icon';
|
||||
export * from './is';
|
||||
export * from './storage';
|
||||
|
@ -2,30 +2,30 @@ import { encrypto, decrypto } from './crypto';
|
||||
// 读取缓存前缀
|
||||
import { STORAGE_PREFIX, STORAGE_DEFAULT_CACHE_TIME } from '@/config';
|
||||
|
||||
interface StorageData {
|
||||
value: any;
|
||||
interface StorageData<T> {
|
||||
value: T;
|
||||
expire: number | null;
|
||||
}
|
||||
/**
|
||||
* LocalStorage部分操作
|
||||
*/
|
||||
function createLocalStorage() {
|
||||
function createLocalStorage<T extends Storage.Local>() {
|
||||
// 默认缓存期限为7天
|
||||
|
||||
function set(key: string, value: any, expire: number = STORAGE_DEFAULT_CACHE_TIME) {
|
||||
const storageData: StorageData = {
|
||||
function set<K extends keyof T>(key: K, value: T[K], expire: number = STORAGE_DEFAULT_CACHE_TIME) {
|
||||
const storageData: StorageData<T[K]> = {
|
||||
value,
|
||||
expire: new Date().getTime() + expire * 1000,
|
||||
};
|
||||
const json = encrypto(storageData);
|
||||
window.localStorage.setItem(STORAGE_PREFIX + key, json);
|
||||
window.localStorage.setItem(`${STORAGE_PREFIX}${String(key)}`, json);
|
||||
}
|
||||
|
||||
function get(key: string) {
|
||||
const json = window.localStorage.getItem(STORAGE_PREFIX + key);
|
||||
function get<K extends keyof T>(key: K) {
|
||||
const json = window.localStorage.getItem(`${STORAGE_PREFIX}${String(key)}`);
|
||||
if (!json) return null;
|
||||
|
||||
let storageData: StorageData | null = null;
|
||||
let storageData: StorageData<T[K]> | null = null;
|
||||
try {
|
||||
storageData = decrypto(json);
|
||||
} catch {
|
||||
@ -42,8 +42,8 @@ function createLocalStorage() {
|
||||
return null;
|
||||
}
|
||||
|
||||
function remove(key: string) {
|
||||
window.localStorage.removeItem(STORAGE_PREFIX + key);
|
||||
function remove(key: keyof T) {
|
||||
window.localStorage.removeItem(`${STORAGE_PREFIX}${String(key)}`);
|
||||
}
|
||||
|
||||
function clear() {
|
||||
@ -60,16 +60,16 @@ function createLocalStorage() {
|
||||
* sessionStorage部分操作
|
||||
*/
|
||||
|
||||
function createSessionStorage() {
|
||||
function set(key: string, value: any) {
|
||||
function createSessionStorage<T extends Storage.Session>() {
|
||||
function set<K extends keyof T>(key: K, value: T[K]) {
|
||||
const json = encrypto(value);
|
||||
window.sessionStorage.setItem(STORAGE_PREFIX + key, json);
|
||||
window.sessionStorage.setItem(`${STORAGE_PREFIX}${String(key)}`, json);
|
||||
}
|
||||
function get(key: string) {
|
||||
const json = sessionStorage.getItem(STORAGE_PREFIX + key);
|
||||
function get<K extends keyof T>(key: K) {
|
||||
const json = sessionStorage.getItem(`${STORAGE_PREFIX}${String(key)}`);
|
||||
if (!json) return null;
|
||||
|
||||
let storageData;
|
||||
let storageData: T[K] | null = null;
|
||||
try {
|
||||
storageData = decrypto(json);
|
||||
} catch {
|
||||
@ -81,8 +81,8 @@ function createSessionStorage() {
|
||||
}
|
||||
return null;
|
||||
}
|
||||
function remove(key: string) {
|
||||
window.sessionStorage.removeItem(STORAGE_PREFIX + key);
|
||||
function remove(key: keyof T) {
|
||||
window.sessionStorage.removeItem(`${STORAGE_PREFIX}${String(key)}`);
|
||||
}
|
||||
function clear() {
|
||||
window.sessionStorage.clear();
|
||||
|
@ -63,8 +63,8 @@ const formComponets = {
|
||||
&::before {
|
||||
position: absolute;
|
||||
content: '';
|
||||
width: 800px;
|
||||
height: 400px;
|
||||
width: 30vw;
|
||||
height: 15vw;
|
||||
background-size: contain;
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user