This commit is contained in:
彭森 2023-11-02 11:11:27 +08:00
commit 57c948572b
8 changed files with 200 additions and 88 deletions

43
src/api/api.ts Normal file
View File

@ -0,0 +1,43 @@
import { useUserStoreHook } from "@/store/modules/user";
import { formatToken, getToken } from "@/utils/auth";
import axios from "axios";
export const api = axios.create({
// 公共配置
baseURL: "",
timeout: 15000
});
api.interceptors.request.use(
// 在发送请求之前做什么
config => {
const data = getToken();
if (data) {
const now = new Date().getTime();
const expired = parseInt(data.expires) - now <= 0;
if (expired) {
useUserStoreHook().logOut();
} else {
config.headers["Authorization"] = formatToken(data.accessToken);
}
}
return config;
},
// 对请求错误做点什么
error => Promise.reject(error)
);
api.interceptors.response.use(
// 对响应数据做点什么
response => {
return response;
},
);
function errorHandle(status: number) {
switch (status) {
case 401:
break;
default:
break;
}
}

View File

@ -12,8 +12,7 @@ import {
useDark,
debounce,
useGlobal,
storageLocal,
storageSession
storageLocal
} from "@pureadmin/utils";
import { getConfig } from "@/config";
import { useRouter } from "vue-router";
@ -133,7 +132,6 @@ const multiTagsCacheChange = () => {
function onReset() {
removeToken();
storageLocal().clear();
storageSession().clear();
const { Grey, Weak, MultiTagsCache, EpThemeColor, Layout } = getConfig();
useAppStoreHook().setLayout(Layout);
setEpThemeColor(EpThemeColor);

View File

@ -21,8 +21,8 @@ import {
formatTwoStageRoutes,
formatFlatteningRoutes
} from "./utils";
import { buildHierarchyTree } from "@/utils/tree";
import { isUrl, openLink, storageSession, isAllEmpty } from "@pureadmin/utils";
import {buildHierarchyTree} from "@/utils/tree";
import {isUrl, openLink, isAllEmpty, storageLocal} from "@pureadmin/utils";
import remainingRouter from "./modules/remaining";
@ -108,8 +108,7 @@ router.beforeEach((to: ToRouteType, _from, next) => {
handleAliveRoute(to);
}
}
const userInfo = storageSession().getItem<DataInfo<number>>(sessionKey);
NProgress.start();
const userInfo = storageLocal().getItem<DataInfo<number>>(sessionKey);
const externalLink = isUrl(to?.name as string);
if (!externalLink) {
to.matched.some(item => {

View File

@ -13,8 +13,7 @@ import {
cloneDeep,
isAllEmpty,
intersection,
storageSession,
isIncludeAllChildren
isIncludeAllChildren, storageLocal
} from "@pureadmin/utils";
import { getConfig } from "@/config";
import { menuType } from "@/layout/types";
@ -73,7 +72,7 @@ function filterChildrenTree(data: RouteComponent[]) {
}
/** 判断两个数组彼此是否存在相同值 */
function isOneOfArray(a: Array<string>, b: Array<string>) {
function isOneOfArray(a: Array<string|number>, b: Array<string|number>) {
return Array.isArray(a) && Array.isArray(b)
? intersection(a, b).length > 0
? true
@ -84,7 +83,7 @@ function isOneOfArray(a: Array<string>, b: Array<string>) {
/** 从sessionStorage里取出当前登陆用户的角色roles过滤无权限的菜单 */
function filterNoPermissionTree(data: RouteComponent[]) {
const currentRoles =
storageSession().getItem<DataInfo<number>>(sessionKey)?.roles ?? [];
storageLocal().getItem<DataInfo<number>>(sessionKey)?.roles ?? [];
const newTree = cloneDeep(data).filter((v: any) =>
isOneOfArray(v.meta?.roles, currentRoles)
);
@ -150,7 +149,7 @@ function addPathMatch() {
}
/** 处理动态路由(后端返回的路由) */
function handleAsyncRoutes(routeList) {
export function handleAsyncRoutes(routeList) {
if (routeList.length === 0) {
usePermissionStoreHook().handleWholeMenus(routeList);
} else {
@ -186,7 +185,7 @@ function initRouter() {
if (getConfig()?.CachingAsyncRoutes) {
// 开启动态路由缓存本地sessionStorage
const key = "async-routes";
const asyncRouteList = storageSession().getItem(key) as any;
const asyncRouteList = storageLocal().getItem(key) as any;
if (asyncRouteList && asyncRouteList?.length > 0) {
return new Promise(resolve => {
handleAsyncRoutes(asyncRouteList);
@ -196,7 +195,7 @@ function initRouter() {
return new Promise(resolve => {
getAsyncRoutes().then(({ data }) => {
handleAsyncRoutes(cloneDeep(data));
storageSession().setItem(key, data);
storageLocal().setItem(key, data);
resolve(router);
});
});

View File

@ -1,22 +1,23 @@
import { defineStore } from "pinia";
import { store } from "@/store";
import { userType } from "./types";
import { routerArrays } from "@/layout/types";
import { router, resetRouter } from "@/router";
import { storageSession } from "@pureadmin/utils";
import { getLogin, refreshTokenApi } from "@/api/user";
import { UserResult, RefreshTokenResult } from "@/api/user";
import { useMultiTagsStoreHook } from "@/store/modules/multiTags";
import { type DataInfo, setToken, removeToken, sessionKey } from "@/utils/auth";
import {defineStore} from "pinia";
import {store} from "@/store";
import {userType} from "./types";
import {routerArrays} from "@/layout/types";
import {router, resetRouter} from "@/router";
import {storageLocal} from "@pureadmin/utils";
import {getLogin, refreshTokenApi} from "@/api/user";
import {UserResult, RefreshTokenResult} from "@/api/user";
import {useMultiTagsStoreHook} from "@/store/modules/multiTags";
import {type DataInfo, setToken, removeToken, sessionKey} from "@/utils/auth";
import {http} from "@/utils/http";
export const useUserStore = defineStore({
id: "pure-user",
state: (): userType => ({
// 用户名
username:
storageSession().getItem<DataInfo<number>>(sessionKey)?.username ?? "",
storageLocal().getItem<DataInfo<number>>(sessionKey)?.username ?? "",
// 页面级别权限
roles: storageSession().getItem<DataInfo<number>>(sessionKey)?.roles ?? []
roles: storageLocal().getItem<DataInfo<number>>(sessionKey)?.roles ?? []
}),
actions: {
/** 存储用户名 */
@ -42,6 +43,48 @@ export const useUserStore = defineStore({
});
});
},
async loginByCode(data) {
return new Promise<any>((resolve, reject) => {
http
.post<any, any>("/wide_data/user/login_by_verify_code", {data})
.then(res => {
const userInfo = res.user_info;
const dataInfo: DataInfo<Date> = {
username: userInfo.name,
accessToken: res.token,
expires: new Date(new Date().getTime() + 60 * 60 * 24 * 3 * 1000),
refreshToken: res.token,
roles: userInfo.auth_data || []
};
setToken(dataInfo);
resolve(res);
})
.catch(error => {
reject(error);
});
});
},
async refreshToken() {
return new Promise<any>((resolve, reject) => {
http
.post<any, any>("/wide_data/user/refresh_token", {})
.then(res => {
const userInfo = res.user_info;
const dataInfo: DataInfo<Date> = {
username: userInfo.name,
accessToken: res.token,
expires: new Date(new Date().getTime() + 60 * 60 * 24 * 3 * 1000),
refreshToken: res.token,
roles: userInfo.auth_data || []
};
setToken(dataInfo);
resolve(res);
})
.catch(error => {
reject(error);
});
});
},
/** 前端登出(不调用接口) */
logOut() {
this.username = "";

View File

@ -1,6 +1,6 @@
import Cookies from "js-cookie";
import { storageSession } from "@pureadmin/utils";
import { useUserStoreHook } from "@/store/modules/user";
import {storageLocal} from "@pureadmin/utils";
export interface DataInfo<T> {
/** token */
@ -23,7 +23,7 @@ export function getToken(): DataInfo<number> {
// 此处与`TokenKey`相同,此写法解决初始化时`Cookies`中不存在`TokenKey`报错
return Cookies.get(TokenKey)
? JSON.parse(Cookies.get(TokenKey))
: storageSession().getItem(sessionKey);
: storageLocal().getItem(sessionKey);
}
/**
@ -47,7 +47,7 @@ export function setToken(data: DataInfo<Date>) {
function setSessionKey(username: string, roles: Array<string>) {
useUserStoreHook().SET_USERNAME(username);
useUserStoreHook().SET_ROLES(roles);
storageSession().setItem(sessionKey, {
storageLocal().setItem(sessionKey, {
refreshToken,
expires,
username,
@ -60,9 +60,9 @@ export function setToken(data: DataInfo<Date>) {
setSessionKey(username, roles);
} else {
const username =
storageSession().getItem<DataInfo<number>>(sessionKey)?.username ?? "";
storageLocal().getItem<DataInfo<number>>(sessionKey)?.username ?? "";
const roles =
storageSession().getItem<DataInfo<number>>(sessionKey)?.roles ?? [];
storageLocal().getItem<DataInfo<number>>(sessionKey)?.roles ?? [];
setSessionKey(username, roles);
}
}
@ -70,7 +70,7 @@ export function setToken(data: DataInfo<Date>) {
/** 删除`token`以及key值为`user-info`的session信息 */
export function removeToken() {
Cookies.remove(TokenKey);
sessionStorage.clear();
localStorage.removeItem("user-info");
}
/** 格式化tokenjwt格式 */

View File

@ -17,8 +17,7 @@ import dayIcon from "@/assets/svg/day.svg?component";
import darkIcon from "@/assets/svg/dark.svg?component";
import Lock from "@iconify-icons/ri/lock-fill";
import User from "@iconify-icons/ri/user-3-fill";
import { tr } from "element-plus/es/locale";
import {api} from "@/api/api";
defineOptions({
name: "Login"
});
@ -34,47 +33,64 @@ dataThemeChange();
const { title } = useNav();
const ruleForm = reactive({
username: "admin",
password: "admin123",
checked: true
account: "",
code: ""
});
const onLogin = async (formEl: FormInstance | undefined) => {
loading.value = true;
if (!formEl) return;
await formEl.validate((valid, fields) => {
if (valid) {
useUserStoreHook()
.loginByUsername({ username: ruleForm.username, password: "admin123" })
.then(res => {
if (res.success) {
//
initRouter().then(() => {
router.push(getTopMenu(true).path);
message("登录成功", { type: "success" });
});
}
});
} else {
loading.value = false;
return fields;
function getLoginVerifyCode() {
if (!ruleForm.account) {
message("请输入正确的手机号或邮箱", {type: "warning"});
return;
}
});
api
.post("/wide_data/user/get_login_verify_code", {
account: ruleForm.account
})
.then(res => {
message("验证码已发送", {type: "success"});
})
.catch(e => {
console.log(e);
message(e.response.data.detail, {type: "warning"});
});
}
const onLogin = async (formEl: FormInstance | undefined) => {
loading.value = true;
if (!formEl) return;
await formEl.validate((valid, fields) => {
if (valid) {
useUserStoreHook()
.loginByCode({account: ruleForm.account, code: ruleForm.code})
.then(res => {
//
initRouter().then(() => {
router.push(getTopMenu(true).path);
message("登录成功", {type: "success"});
});
}).catch(e => {
console.log(e);
loading.value = false;
message(e.response.data.detail, {type: "warning"});
});
} else {
loading.value = false;
return fields;
}
});
};
/** 使用公共函数,避免`removeEventListener`失效 */
function onkeypress({ code }: KeyboardEvent) {
if (code === "Enter") {
onLogin(ruleFormRef.value);
}
function onkeypress({code}: KeyboardEvent) {
if (code === "Enter") {
onLogin(ruleFormRef.value);
}
}
onMounted(() => {
window.document.addEventListener("keypress", onkeypress);
window.document.addEventListener("keypress", onkeypress);
});
onBeforeUnmount(() => {
window.document.removeEventListener("keypress", onkeypress);
window.document.removeEventListener("keypress", onkeypress);
});
</script>
@ -113,14 +129,14 @@ onBeforeUnmount(() => {
{
required: true,
message: '请输入账号',
trigger: 'blur'
trigger: 'change'
}
]"
prop="username"
prop="account"
>
<el-input
clearable
v-model="ruleForm.username"
v-model="ruleForm.account"
placeholder="账号"
:prefix-icon="useRenderIcon(User)"
/>
@ -128,31 +144,45 @@ onBeforeUnmount(() => {
</Motion>
<Motion :delay="150">
<el-form-item prop="password">
<el-form-item
:rules="[
{
required: true,
message: '请输入验证码',
trigger: 'change'
}
]"
prop="code">
<el-input
clearable
show-password
v-model="ruleForm.password"
placeholder="密码"
v-model="ruleForm.code"
placeholder="验证码"
:prefix-icon="useRenderIcon(Lock)"
/>
>
<template #append>
<div
@click="getLoginVerifyCode"
style="width: 6rem"
class="cursor-pointer active:opacity-70"
>
发送验证码
</div>
</template>
</el-input>
</el-form-item>
</Motion>
<div class="newClass">
<el-checkbox v-model="ruleForm.checked">自动登录</el-checkbox>
<span>忘记密码</span>
</div>
<Motion :delay="250">
<el-button
class="w-full mt-4"
size="default"
type="primary"
:loading="loading"
@click="onLogin(ruleFormRef)"
>
登录
</el-button>
</Motion>
<Motion :delay="250">
<el-button
class="w-full mt-4"
size="default"
type="primary"
:loading="loading"
@click="onLogin(ruleFormRef)"
>
登录
</el-button>
</Motion>
</el-form>
</div>
</div>

View File

@ -1,6 +1,6 @@
<script setup lang="ts">
import { initRouter } from "@/router/utils";
import { storageSession } from "@pureadmin/utils";
import { storageLocal } from "@pureadmin/utils";
import { type CSSProperties, ref, computed } from "vue";
import { useUserStoreHook } from "@/store/modules/user";
import { usePermissionStoreHook } from "@/store/modules/permission";
@ -34,7 +34,7 @@ function onChange() {
.loginByUsername({ username: username.value, password: "admin123" })
.then(res => {
if (res.success) {
storageSession().removeItem("async-routes");
storageLocal().removeItem("async-routes");
usePermissionStoreHook().clearAllCachePage();
initRouter();
}