diff --git a/README.md b/README.md
index 7f0f71f..53f54a9 100644
--- a/README.md
+++ b/README.md
@@ -1,5 +1,5 @@
@@ -54,7 +54,7 @@ pnpm commit
| [

](http://godban.github.io/browsers-support-badges/)
IE / Edge | [

](http://godban.github.io/browsers-support-badges/)
Firefox | [

](http://godban.github.io/browsers-support-badges/)
Chrome | [

](http://godban.github.io/browsers-support-badges/)
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)
diff --git a/index.html b/index.html
index 5e73499..aadbeb4 100644
--- a/index.html
+++ b/index.html
@@ -2,7 +2,7 @@
-
+
%VITE_APP_TITLE%
diff --git a/mock/module/user.ts b/mock/module/user.ts
index 829350f..3948711 100644
--- a/mock/module/user.ts
+++ b/mock/module/user.ts
@@ -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: [
{
diff --git a/src/config/system.ts b/src/config/system.ts
index 3666b4d..e1b8ada 100644
--- a/src/config/system.ts
+++ b/src/config/system.ts
@@ -1,15 +1,3 @@
-/** 本地存储信息字段 */
-export const storageKey = {
- /* 用户信息 */
- userInfo: '__USER_INFO__',
- /* token */
- token: '__TOKEN__',
- /* refreshToken */
- refreshToken: '__REFRESH_TOKEN__',
- /* 标签栏信息 */
- tabsRoutes: '__TABS_ROUTES__',
-};
-
/** 本地存储前缀 */
export const STORAGE_PREFIX = '';
diff --git a/src/layouts/components/sider/Menu.vue b/src/layouts/components/sider/Menu.vue
index 965457f..129df42 100644
--- a/src/layouts/components/sider/Menu.vue
+++ b/src/layouts/components/sider/Menu.vue
@@ -6,23 +6,16 @@
accordion
:options="routesStore.menus"
:value="routesStore.activeMenu"
- @update:value="handleClickMenu"
/>
diff --git a/src/router/guard/permission.ts b/src/router/guard/permission.ts
index 4c8cc59..20d29cd 100644
--- a/src/router/guard/permission.ts
+++ b/src/router/guard/permission.ts
@@ -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()
diff --git a/src/service/api/login.ts b/src/service/api/login.ts
index 8f8f09e..11db346 100644
--- a/src/service/api/login.ts
+++ b/src/service/api/login.ts
@@ -11,12 +11,12 @@ interface Itoken {
export function fetchLogin(params: Ilogin) {
return mockRequest.post
('/login', params);
}
-export function fetchUpdateToken(params: string) {
+export function fetchUpdateToken(params: any) {
return mockRequest.post('/updateToken', params);
}
export function fetchUserInfo() {
- return mockRequest.get('/getUserInfo');
+ return mockRequest.get('/getUserInfo');
}
-export function fetchUserRoutes(params: string) {
+export function fetchUserRoutes(params: { userId: number }) {
return mockRequest.post('/getUserRoutes', params);
}
diff --git a/src/service/http/handle.ts b/src/service/http/handle.ts
index a379f0a..e6cf7ec 100644
--- a/src/service/http/handle.ts
+++ b/src/service/http/handle.ts
@@ -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(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) {
diff --git a/src/service/http/instance.ts b/src/service/http/instance.ts
index c720c9f..7ad58fd 100644
--- a/src/service/http/instance.ts
+++ b/src/service/http/instance.ts
@@ -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;
},
diff --git a/src/store/modules/auth.ts b/src/store/modules/auth.ts
index 88477c6..c9b9637 100644
--- a/src/store/modules/auth.ts
+++ b/src/store/modules/auth.ts
@@ -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;
diff --git a/src/store/modules/route.ts b/src/store/modules/route.ts
index baaafa8..53554f1 100644
--- a/src/store/modules/route.ts
+++ b/src/store/modules/route.ts
@@ -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);
// 生成侧边菜单
diff --git a/src/typings/business.d.ts b/src/typings/business.d.ts
index a284142..2ef1f2c 100644
--- a/src/typings/business.d.ts
+++ b/src/typings/business.d.ts
@@ -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;
+
}
}
/* 系统消息 */
diff --git a/src/typings/storage.d.ts b/src/typings/storage.d.ts
new file mode 100644
index 0000000..6b6286f
--- /dev/null
+++ b/src/typings/storage.d.ts
@@ -0,0 +1,13 @@
+declare namespace Storage {
+
+ interface Session {
+ demoKey: string
+ }
+
+ interface Local {
+ userInfo: Auth.UserInfo
+ token: string
+ refreshToken: string
+ tabsRoutes: string
+ }
+}
\ No newline at end of file
diff --git a/src/utils/auth.ts b/src/utils/auth.ts
deleted file mode 100644
index 226851b..0000000
--- a/src/utils/auth.ts
+++ /dev/null
@@ -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();
-}
diff --git a/src/utils/icon.ts b/src/utils/icon.ts
index eae480b..b4be110 100644
--- a/src/utils/icon.ts
+++ b/src/utils/icon.ts
@@ -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 }) });
}
diff --git a/src/utils/index.ts b/src/utils/index.ts
index fabc1eb..037190e 100644
--- a/src/utils/index.ts
+++ b/src/utils/index.ts
@@ -1,4 +1,3 @@
-export * from './auth';
export * from './icon';
export * from './is';
export * from './storage';
diff --git a/src/utils/storage.ts b/src/utils/storage.ts
index 4ddd302..34f64f3 100644
--- a/src/utils/storage.ts
+++ b/src/utils/storage.ts
@@ -2,30 +2,30 @@ import { encrypto, decrypto } from './crypto';
// 读取缓存前缀
import { STORAGE_PREFIX, STORAGE_DEFAULT_CACHE_TIME } from '@/config';
-interface StorageData {
- value: any;
+interface StorageData {
+ value: T;
expire: number | null;
}
/**
* LocalStorage部分操作
*/
-function createLocalStorage() {
+function createLocalStorage() {
// 默认缓存期限为7天
- function set(key: string, value: any, expire: number = STORAGE_DEFAULT_CACHE_TIME) {
- const storageData: StorageData = {
+ function set(key: K, value: T[K], expire: number = STORAGE_DEFAULT_CACHE_TIME) {
+ const storageData: StorageData = {
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(key: K) {
+ const json = window.localStorage.getItem(`${STORAGE_PREFIX}${String(key)}`);
if (!json) return null;
- let storageData: StorageData | null = null;
+ let storageData: StorageData | 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() {
+ function set(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(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();
diff --git a/src/views/login/index.vue b/src/views/login/index.vue
index e366877..e1d720c 100644
--- a/src/views/login/index.vue
+++ b/src/views/login/index.vue
@@ -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;
}