feat: 🚀 用户登录未完成
This commit is contained in:
parent
25cd96e6d9
commit
33fc048aab
2
.env
2
.env
|
@ -1,5 +1,5 @@
|
||||||
# 平台本地运行端口号
|
# 平台本地运行端口号
|
||||||
VITE_PORT=8201
|
VITE_PORT=7000
|
||||||
|
|
||||||
# 预发布环境路由历史模式(Hash模式传"hash"、HTML5模式传"h5"、Hash模式带base参数传"hash,base参数"、HTML5模式带base参数传"h5,base参数")
|
# 预发布环境路由历史模式(Hash模式传"hash"、HTML5模式传"h5"、Hash模式带base参数传"hash,base参数"、HTML5模式带base参数传"h5,base参数")
|
||||||
VITE_ROUTER_HISTORY="hash"
|
VITE_ROUTER_HISTORY="hash"
|
||||||
|
|
|
@ -1,14 +1,14 @@
|
||||||
# 平台本地运行端口号
|
# 平台本地运行端口号
|
||||||
VITE_PORT=8201
|
VITE_PORT=7000
|
||||||
|
|
||||||
# 预发布环境路由历史模式(Hash模式传"hash"、HTML5模式传"h5"、Hash模式带base参数传"hash,base参数"、HTML5模式带base参数传"h5,base参数")
|
# 预发布环境路由历史模式(Hash模式传"hash"、HTML5模式传"h5"、Hash模式带base参数传"hash,base参数"、HTML5模式带base参数传"h5,base参数")
|
||||||
VITE_ROUTER_HISTORY="hash"
|
VITE_ROUTER_HISTORY="hash"
|
||||||
|
|
||||||
# 基础请求路径
|
# 基础请求路径
|
||||||
VITE_BASE_API=/api
|
VITE_BASE_API=/admin
|
||||||
|
|
||||||
# 跨域代理地址
|
# 跨域代理地址
|
||||||
VITE_APP_URL=http://localhost:8801
|
VITE_APP_URL=http://localhost:7070
|
||||||
|
|
||||||
# mock地址
|
# mock地址
|
||||||
VITE_MOCK_BASE_API=/mock
|
VITE_MOCK_BASE_API=/mock
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
# 平台本地运行端口号
|
# 平台本地运行端口号
|
||||||
VITE_PORT=8201
|
VITE_PORT=7000
|
||||||
|
|
||||||
# 预发布环境路由历史模式(Hash模式传"hash"、HTML5模式传"h5"、Hash模式带base参数传"hash,base参数"、HTML5模式带base参数传"h5,base参数")
|
# 预发布环境路由历史模式(Hash模式传"hash"、HTML5模式传"h5"、Hash模式带base参数传"hash,base参数"、HTML5模式带base参数传"h5,base参数")
|
||||||
VITE_ROUTER_HISTORY="hash"
|
VITE_ROUTER_HISTORY="hash"
|
||||||
|
@ -8,7 +8,7 @@ VITE_ROUTER_HISTORY="hash"
|
||||||
VITE_BASE_API=/api
|
VITE_BASE_API=/api
|
||||||
|
|
||||||
# 跨域代理地址
|
# 跨域代理地址
|
||||||
VITE_APP_URL=http://localhost:8801
|
VITE_APP_URL=http://localhost:7070
|
||||||
|
|
||||||
# mock地址
|
# mock地址
|
||||||
VITE_MOCK_BASE_API=/mock
|
VITE_MOCK_BASE_API=/mock
|
||||||
|
|
|
@ -1,9 +1,39 @@
|
||||||
// @ts-check
|
// @see: https://www.prettier.cn
|
||||||
|
|
||||||
/** @type {import("prettier").Config} */
|
|
||||||
export default {
|
export default {
|
||||||
|
// 超过最大值换行
|
||||||
|
printWidth: 200,
|
||||||
|
// 缩进字节数
|
||||||
|
tabWidth: 1,
|
||||||
|
// 使用制表符而不是空格缩进行
|
||||||
|
useTabs: true,
|
||||||
|
// 结尾不用分号(true有,false没有)
|
||||||
|
semi: true,
|
||||||
|
// 使用单引号(true单引号,false双引号)
|
||||||
|
singleQuote: true,
|
||||||
|
// 更改引用对象属性的时间 可选值"<as-needed|consistent|preserve>"
|
||||||
|
quoteProps: "as-needed",
|
||||||
|
// 在对象,数组括号与文字之间加空格 "{ foo: bar }"
|
||||||
bracketSpacing: true,
|
bracketSpacing: true,
|
||||||
singleQuote: false,
|
// 多行时尽可能打印尾随逗号。(例如,单行数组永远不会出现逗号结尾。) 可选值"<none|es5|all>",默认none
|
||||||
|
trailingComma: "all",
|
||||||
|
// 在JSX中使用单引号而不是双引号
|
||||||
|
jsxSingleQuote: true,
|
||||||
|
// (x) => {} 箭头函数参数只有一个时是否要有小括号。avoid:省略括号 ,always:不省略括号
|
||||||
arrowParens: "avoid",
|
arrowParens: "avoid",
|
||||||
trailingComma: "none"
|
// 如果文件顶部已经有一个 doclock,这个选项将新建一行注释,并打上@format标记。
|
||||||
|
insertPragma: false,
|
||||||
|
// 指定要使用的解析器,不需要写文件开头的 @prettier
|
||||||
|
requirePragma: false,
|
||||||
|
// 默认值。因为使用了一些折行敏感型的渲染器(如GitHub comment)而按照markdown文本样式进行折行
|
||||||
|
proseWrap: "preserve",
|
||||||
|
// 在html中空格是否是敏感的 "css" - 遵守CSS显示属性的默认值, "strict" - 空格被认为是敏感的 ,"ignore" - 空格被认为是不敏感的
|
||||||
|
htmlWhitespaceSensitivity: "css",
|
||||||
|
// 换行符使用 lf 结尾是 可选值"<auto|lf|crlf|cr>"
|
||||||
|
endOfLine: "auto",
|
||||||
|
// 这两个选项可用于格式化以给定字符偏移量(分别包括和不包括)开始和结束的代码
|
||||||
|
rangeStart: 0,
|
||||||
|
rangeEnd: Infinity,
|
||||||
|
|
||||||
|
vueIndentScriptAndStyle: false // Vue文件脚本和样式标签缩进
|
||||||
};
|
};
|
||||||
|
|
|
@ -10,10 +10,10 @@ export const serverOptions = (mode: string) => {
|
||||||
open: true,
|
open: true,
|
||||||
cors: true,
|
cors: true,
|
||||||
proxy: {
|
proxy: {
|
||||||
"/api": {
|
"/admin": {
|
||||||
target: VITE_APP_URL,
|
target: VITE_APP_URL,
|
||||||
changeOrigin: true,
|
changeOrigin: true,
|
||||||
rewrite: (path: string) => path.replace(/^\/api/, "/api")
|
rewrite: (path: string) => path.replace(/^\/admin/, "/admin")
|
||||||
},
|
},
|
||||||
"/mock": {
|
"/mock": {
|
||||||
target: VITE_APP_URL,
|
target: VITE_APP_URL,
|
||||||
|
|
|
@ -10,9 +10,10 @@ import type {
|
||||||
RequestMethods
|
RequestMethods
|
||||||
} from "./types";
|
} from "./types";
|
||||||
import { stringify } from "qs";
|
import { stringify } from "qs";
|
||||||
import NProgress from "../../utils/progress";
|
import NProgress from "@/utils/progress";
|
||||||
import { formatToken, getToken } from "@/utils/auth";
|
import { formatToken, getToken } from "@/utils/auth";
|
||||||
import { useUserStoreHook } from "@/store/modules/user";
|
import { useUserStoreHook } from "@/store/modules/user";
|
||||||
|
import { message } from "@/utils/message";
|
||||||
|
|
||||||
// 相关配置请参考:www.axios-js.com/zh-cn/docs/#axios-request-config-1
|
// 相关配置请参考:www.axios-js.com/zh-cn/docs/#axios-request-config-1
|
||||||
const defaultConfig: AxiosRequestConfig = {
|
const defaultConfig: AxiosRequestConfig = {
|
||||||
|
@ -55,7 +56,7 @@ class PureHttp {
|
||||||
private static retryOriginalRequest(config: PureHttpRequestConfig) {
|
private static retryOriginalRequest(config: PureHttpRequestConfig) {
|
||||||
return new Promise(resolve => {
|
return new Promise(resolve => {
|
||||||
PureHttp.requests.push((token: string) => {
|
PureHttp.requests.push((token: string) => {
|
||||||
config.headers["Authorization"] = formatToken(token);
|
config.headers["token"] = formatToken(token);
|
||||||
resolve(config);
|
resolve(config);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -136,7 +137,7 @@ class PureHttp {
|
||||||
// token过期刷新
|
// token过期刷新
|
||||||
useUserStoreHook()
|
useUserStoreHook()
|
||||||
.handRefreshToken({ refreshToken: data.refreshToken })
|
.handRefreshToken({ refreshToken: data.refreshToken })
|
||||||
.then(res => {
|
.then((res: any) => {
|
||||||
const token = res.data.accessToken;
|
const token = res.data.accessToken;
|
||||||
config.headers["Authorization"] = formatToken(token);
|
config.headers["Authorization"] = formatToken(token);
|
||||||
PureHttp.requests.forEach(cb => cb(token));
|
PureHttp.requests.forEach(cb => cb(token));
|
||||||
|
@ -188,8 +189,9 @@ class PureHttp {
|
||||||
$error.isCancelRequest = Axios.isCancel($error);
|
$error.isCancelRequest = Axios.isCancel($error);
|
||||||
// 关闭进度条动画
|
// 关闭进度条动画
|
||||||
NProgress.done();
|
NProgress.done();
|
||||||
|
message(error.message, { type: "error" });
|
||||||
// 所有的响应异常 区分来源为取消请求/非取消请求
|
// 所有的响应异常 区分来源为取消请求/非取消请求
|
||||||
return Promise.reject($error);
|
return $error;
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -48,7 +48,7 @@ class PureHttp {
|
||||||
private static retryOriginalRequest(config: PureHttpRequestConfig) {
|
private static retryOriginalRequest(config: PureHttpRequestConfig) {
|
||||||
return new Promise(resolve => {
|
return new Promise(resolve => {
|
||||||
PureHttp.requests.push((token: string) => {
|
PureHttp.requests.push((token: string) => {
|
||||||
config.headers["Authorization"] = formatToken(token);
|
config.headers["token"] = formatToken(token);
|
||||||
resolve(config);
|
resolve(config);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -129,7 +129,7 @@ class PureHttp {
|
||||||
// token过期刷新
|
// token过期刷新
|
||||||
useUserStoreHook()
|
useUserStoreHook()
|
||||||
.handRefreshToken({ refreshToken: data.refreshToken })
|
.handRefreshToken({ refreshToken: data.refreshToken })
|
||||||
.then(res => {
|
.then((res: any) => {
|
||||||
const token = res.data.accessToken;
|
const token = res.data.accessToken;
|
||||||
config.headers["Authorization"] = formatToken(token);
|
config.headers["Authorization"] = formatToken(token);
|
||||||
PureHttp.requests.forEach(cb => cb(token));
|
PureHttp.requests.forEach(cb => cb(token));
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { http } from "@/utils/http";
|
import { http } from "@/api/service";
|
||||||
|
|
||||||
type Result = {
|
type Result = {
|
||||||
success: boolean;
|
success: boolean;
|
||||||
|
|
|
@ -1,45 +1,57 @@
|
||||||
import { http } from "@/api/service/mockRequest";
|
import { http } from "@/api/service";
|
||||||
|
import type { BaseResult } from "@/types/common/BaseResult";
|
||||||
|
|
||||||
export type UserResult = {
|
export interface UserResult {
|
||||||
success: boolean;
|
/** 头像 */
|
||||||
data: {
|
avatar: string;
|
||||||
/** 头像 */
|
/** 用户名 */
|
||||||
avatar: string;
|
username: string;
|
||||||
/** 用户名 */
|
/** 昵称 */
|
||||||
username: string;
|
nickname: string;
|
||||||
/** 昵称 */
|
/** 当前登录用户的角色 */
|
||||||
nickname: string;
|
roles: Array<string>;
|
||||||
/** 当前登录用户的角色 */
|
/** 按钮级别权限 */
|
||||||
roles: Array<string>;
|
permissions: Array<string>;
|
||||||
/** 按钮级别权限 */
|
/** `token` */
|
||||||
permissions: Array<string>;
|
accessToken: string;
|
||||||
/** `token` */
|
/** 用于调用刷新`accessToken`的接口时所需的`token` */
|
||||||
accessToken: string;
|
refreshToken: string;
|
||||||
/** 用于调用刷新`accessToken`的接口时所需的`token` */
|
/** `accessToken`的过期时间(格式'xxxx/xx/xx xx:xx:xx') */
|
||||||
refreshToken: string;
|
expires: Date;
|
||||||
/** `accessToken`的过期时间(格式'xxxx/xx/xx xx:xx:xx') */
|
}
|
||||||
expires: Date;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
export type RefreshTokenResult = {
|
export interface RefreshTokenResult {
|
||||||
success: boolean;
|
/** `token` */
|
||||||
data: {
|
accessToken: string;
|
||||||
/** `token` */
|
/** 用于调用刷新`accessToken`的接口时所需的`token` */
|
||||||
accessToken: string;
|
refreshToken: string;
|
||||||
/** 用于调用刷新`accessToken`的接口时所需的`token` */
|
/** `accessToken`的过期时间(格式'xxxx/xx/xx xx:xx:xx') */
|
||||||
refreshToken: string;
|
expires: Date;
|
||||||
/** `accessToken`的过期时间(格式'xxxx/xx/xx xx:xx:xx') */
|
}
|
||||||
expires: Date;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
/** 登录 */
|
/** 登录 */
|
||||||
export const getLogin = (data?: object) => {
|
export const fetchLogin = (data?: object) => {
|
||||||
return http.request<UserResult>("post", "/login", { data });
|
return http.request<BaseResult<UserResult>>("post", "/login", { data });
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* * 发送邮件
|
||||||
|
* @param data
|
||||||
|
*/
|
||||||
|
export const fetchPostEmailCode = (data: any) => {
|
||||||
|
return http.request<BaseResult<any>>(
|
||||||
|
"post",
|
||||||
|
"/user/noAuth/sendLoginEmail",
|
||||||
|
{ data },
|
||||||
|
{ headers: { "Content-Type": "multipart/form-data" } }
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
/** 刷新`token` */
|
/** 刷新`token` */
|
||||||
export const refreshTokenApi = (data?: object) => {
|
export const refreshTokenApi = (data?: object) => {
|
||||||
return http.request<RefreshTokenResult>("post", "/refresh-token", { data });
|
return http.request<BaseResult<RefreshTokenResult>>(
|
||||||
|
"post",
|
||||||
|
"/refresh-token",
|
||||||
|
{ data }
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -361,10 +361,9 @@ function hasAuth(value: string | Array<string>): boolean {
|
||||||
/** 从当前路由的`meta`字段里获取按钮级别的所有自定义`code`值 */
|
/** 从当前路由的`meta`字段里获取按钮级别的所有自定义`code`值 */
|
||||||
const metaAuths = getAuths();
|
const metaAuths = getAuths();
|
||||||
if (!metaAuths) return false;
|
if (!metaAuths) return false;
|
||||||
const isAuths = isString(value)
|
return isString(value)
|
||||||
? metaAuths.includes(value)
|
? metaAuths.includes(value)
|
||||||
: isIncludeAllChildren(value, metaAuths);
|
: isIncludeAllChildren(value, metaAuths);
|
||||||
return isAuths ? true : false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleTopMenu(route) {
|
function handleTopMenu(route) {
|
||||||
|
|
|
@ -8,16 +8,17 @@ import {
|
||||||
type userType
|
type userType
|
||||||
} from "../utils";
|
} from "../utils";
|
||||||
import {
|
import {
|
||||||
getLogin,
|
fetchLogin,
|
||||||
|
fetchPostEmailCode,
|
||||||
refreshTokenApi,
|
refreshTokenApi,
|
||||||
type RefreshTokenResult,
|
type RefreshTokenResult
|
||||||
type UserResult
|
|
||||||
} from "@/api/v1/user";
|
} from "@/api/v1/user";
|
||||||
import { useMultiTagsStoreHook } from "./multiTags";
|
import { useMultiTagsStoreHook } from "./multiTags";
|
||||||
import { type DataInfo, removeToken, setToken, userKey } from "@/utils/auth";
|
import { type DataInfo, removeToken, setToken, userKey } from "@/utils/auth";
|
||||||
|
import { message } from "@/utils/message";
|
||||||
|
|
||||||
export const useUserStore = defineStore({
|
export const useUserStore = defineStore({
|
||||||
id: "pure-user",
|
id: "system-user",
|
||||||
state: (): userType => ({
|
state: (): userType => ({
|
||||||
// 头像
|
// 头像
|
||||||
avatar: storageLocal().getItem<DataInfo<number>>(userKey)?.avatar ?? "",
|
avatar: storageLocal().getItem<DataInfo<number>>(userKey)?.avatar ?? "",
|
||||||
|
@ -65,33 +66,53 @@ export const useUserStore = defineStore({
|
||||||
this.loginDay = Number(value);
|
this.loginDay = Number(value);
|
||||||
},
|
},
|
||||||
/** 登入 */
|
/** 登入 */
|
||||||
async loginByUsername(data) {
|
async loginByUsername(data: any) {
|
||||||
return new Promise<UserResult>((resolve, reject) => {
|
const result = await fetchLogin(data);
|
||||||
getLogin(data)
|
|
||||||
.then(data => {
|
if (result.code === 200) {
|
||||||
if (data?.success) setToken(data.data);
|
setToken(data.data);
|
||||||
resolve(data);
|
return true;
|
||||||
})
|
}
|
||||||
.catch(error => {
|
|
||||||
reject(error);
|
message(result.message, { type: "error" });
|
||||||
});
|
return false;
|
||||||
});
|
|
||||||
},
|
},
|
||||||
/** 前端登出(不调用接口) */
|
|
||||||
logOut() {
|
/**
|
||||||
|
* * 发送邮箱验证码
|
||||||
|
* @param email
|
||||||
|
*/
|
||||||
|
async postEmailCode(email: string) {
|
||||||
|
const response = await fetchPostEmailCode({ email });
|
||||||
|
if (response.code === 200) {
|
||||||
|
message(response.message, { type: "success" });
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
message(response.message, { type: "error" });
|
||||||
|
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 前端登出(不调用接口)
|
||||||
|
*/
|
||||||
|
async logOut() {
|
||||||
this.username = "";
|
this.username = "";
|
||||||
this.roles = [];
|
this.roles = [];
|
||||||
this.permissions = [];
|
this.permissions = [];
|
||||||
removeToken();
|
removeToken();
|
||||||
useMultiTagsStoreHook().handleTags("equal", [...routerArrays]);
|
useMultiTagsStoreHook().handleTags("equal", [...routerArrays]);
|
||||||
resetRouter();
|
resetRouter();
|
||||||
router.push("/login");
|
await router.push("/login");
|
||||||
},
|
},
|
||||||
/** 刷新`token` */
|
|
||||||
|
/**
|
||||||
|
* 刷新`token`
|
||||||
|
*/
|
||||||
async handRefreshToken(data) {
|
async handRefreshToken(data) {
|
||||||
return new Promise<RefreshTokenResult>((resolve, reject) => {
|
return new Promise<RefreshTokenResult>((resolve, reject) => {
|
||||||
refreshTokenApi(data)
|
refreshTokenApi(data)
|
||||||
.then(data => {
|
.then((data: any) => {
|
||||||
if (data) {
|
if (data) {
|
||||||
setToken(data.data);
|
setToken(data.data);
|
||||||
resolve(data);
|
resolve(data);
|
||||||
|
|
|
@ -0,0 +1,6 @@
|
||||||
|
// 基础后端返回内容
|
||||||
|
export interface BaseResult<T> {
|
||||||
|
code: number;
|
||||||
|
data: T;
|
||||||
|
message: string;
|
||||||
|
}
|
|
@ -8,8 +8,7 @@ import { useNav } from "@/layout/hooks/useNav";
|
||||||
import type { FormInstance } from "element-plus";
|
import type { FormInstance } from "element-plus";
|
||||||
import { $t } from "@/plugins/i18n";
|
import { $t } from "@/plugins/i18n";
|
||||||
import { useLayout } from "@/layout/hooks/useLayout";
|
import { useLayout } from "@/layout/hooks/useLayout";
|
||||||
import { useUserStoreHook } from "@/store/modules/user";
|
import { useUserStore } from "@/store/modules/user";
|
||||||
import { getTopMenu, initRouter } from "@/router/utils";
|
|
||||||
import { avatar, bg, illustration } from "./utils/static";
|
import { avatar, bg, illustration } from "./utils/static";
|
||||||
import { useRenderIcon } from "@/components/CommonIcon/src/hooks";
|
import { useRenderIcon } from "@/components/CommonIcon/src/hooks";
|
||||||
import { onBeforeUnmount, onMounted, reactive, ref, toRaw } from "vue";
|
import { onBeforeUnmount, onMounted, reactive, ref, toRaw } from "vue";
|
||||||
|
@ -22,6 +21,7 @@ import globalization from "@/assets/svg/globalization.svg?component";
|
||||||
import Lock from "@iconify-icons/ri/lock-fill";
|
import Lock from "@iconify-icons/ri/lock-fill";
|
||||||
import Check from "@iconify-icons/ep/check";
|
import Check from "@iconify-icons/ep/check";
|
||||||
import User from "@iconify-icons/ri/user-3-fill";
|
import User from "@iconify-icons/ri/user-3-fill";
|
||||||
|
import { getTopMenu, initRouter } from "@/router/utils";
|
||||||
|
|
||||||
defineOptions({
|
defineOptions({
|
||||||
name: "Login"
|
name: "Login"
|
||||||
|
@ -29,6 +29,8 @@ defineOptions({
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const loading = ref(false);
|
const loading = ref(false);
|
||||||
const ruleFormRef = ref<FormInstance>();
|
const ruleFormRef = ref<FormInstance>();
|
||||||
|
const sendSecond = ref(60);
|
||||||
|
const timer = ref(null);
|
||||||
|
|
||||||
const { initStorage } = useLayout();
|
const { initStorage } = useLayout();
|
||||||
initStorage();
|
initStorage();
|
||||||
|
@ -38,32 +40,71 @@ const { dataTheme, overallStyle, dataThemeChange } = useDataThemeChange();
|
||||||
dataThemeChange(overallStyle.value);
|
dataThemeChange(overallStyle.value);
|
||||||
const { title, getDropdownItemStyle, getDropdownItemClass } = useNav();
|
const { title, getDropdownItemStyle, getDropdownItemClass } = useNav();
|
||||||
const { locale, translationCh, translationEn } = useTranslationLang();
|
const { locale, translationCh, translationEn } = useTranslationLang();
|
||||||
|
const userStore = useUserStore();
|
||||||
|
|
||||||
const ruleForm = reactive({
|
const ruleForm = reactive({
|
||||||
username: "admin",
|
username: "1319900154@qq.com",
|
||||||
password: "admin123"
|
password: "admin123",
|
||||||
|
emailCode: ""
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* * 发送邮箱验证码
|
||||||
|
*/
|
||||||
|
const onSendEmailCode = async () => {
|
||||||
|
// 判断是否填写邮箱,如果没有填写邮箱不给发送验证码
|
||||||
|
if (ruleForm.username === "" || ruleForm.username === void 0) {
|
||||||
|
message("请先填写邮箱地址", { type: "warning" });
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const result = await userStore.postEmailCode(ruleForm.username);
|
||||||
|
if (result) {
|
||||||
|
// 开始倒计时,之后发送邮箱验证码
|
||||||
|
onSendEmailTimer();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* * 发送邮箱倒计时点击
|
||||||
|
*/
|
||||||
|
const onSendEmailTimer = () => {
|
||||||
|
// 开始倒计时
|
||||||
|
timer.value = setInterval(() => {
|
||||||
|
// 当定时小于0时清除定时器
|
||||||
|
if (sendSecond.value <= 0) {
|
||||||
|
clearInterval(timer.value);
|
||||||
|
timer.value = null;
|
||||||
|
sendSecond.value = 60;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 之后每秒减去时间
|
||||||
|
sendSecond.value--;
|
||||||
|
}, 1000);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 登录
|
||||||
|
* @param formEl
|
||||||
|
*/
|
||||||
const onLogin = async (formEl: FormInstance | undefined) => {
|
const onLogin = async (formEl: FormInstance | undefined) => {
|
||||||
if (!formEl) return;
|
if (!formEl) return;
|
||||||
await formEl.validate((valid, fields) => {
|
await formEl.validate(async valid => {
|
||||||
if (valid) {
|
if (valid) {
|
||||||
loading.value = true;
|
loading.value = true;
|
||||||
useUserStoreHook()
|
const result = await userStore.loginByUsername(ruleForm);
|
||||||
.loginByUsername({ username: ruleForm.username, password: "admin123" })
|
|
||||||
.then(res => {
|
if (result) {
|
||||||
if (res.success) {
|
// 获取后端路由
|
||||||
// 获取后端路由
|
await initRouter();
|
||||||
return initRouter().then(() => {
|
router.push(getTopMenu(true).path).then(() => {
|
||||||
router.push(getTopMenu(true).path).then(() => {
|
message(t("login.loginSuccess"), { type: "success" });
|
||||||
message(t("login.loginSuccess"), { type: "success" });
|
});
|
||||||
});
|
} else {
|
||||||
});
|
message(t("login.loginFail"), { type: "error" });
|
||||||
} else {
|
}
|
||||||
message(t("login.loginFail"), { type: "error" });
|
|
||||||
}
|
loading.value = false;
|
||||||
})
|
|
||||||
.finally(() => (loading.value = false));
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
@ -178,6 +219,39 @@ onBeforeUnmount(() => {
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</Motion>
|
</Motion>
|
||||||
|
|
||||||
|
<Motion :delay="150">
|
||||||
|
<el-form-item prop="emailCode">
|
||||||
|
<el-input
|
||||||
|
v-model="ruleForm.emailCode"
|
||||||
|
:placeholder="t('login.emailCode')"
|
||||||
|
:prefix-icon="useRenderIcon('ic:outline-email')"
|
||||||
|
clearable
|
||||||
|
@keydown.enter="onLogin(ruleFormRef)"
|
||||||
|
>
|
||||||
|
<template v-slot:append>
|
||||||
|
<el-link
|
||||||
|
v-if="sendSecond === 60"
|
||||||
|
:underline="false"
|
||||||
|
class="px-2"
|
||||||
|
type="primary"
|
||||||
|
@click="onSendEmailCode"
|
||||||
|
>
|
||||||
|
{{ t("login.getEmailCode") }}
|
||||||
|
</el-link>
|
||||||
|
<el-link
|
||||||
|
v-else
|
||||||
|
:underline="false"
|
||||||
|
class="px-2"
|
||||||
|
type="primary"
|
||||||
|
>
|
||||||
|
{{ sendSecond }}
|
||||||
|
{{ t("login.getCodeInfo") }}
|
||||||
|
</el-link>
|
||||||
|
</template>
|
||||||
|
</el-input>
|
||||||
|
</el-form-item>
|
||||||
|
</Motion>
|
||||||
|
|
||||||
<Motion :delay="250">
|
<Motion :delay="250">
|
||||||
<el-button
|
<el-button
|
||||||
:loading="loading"
|
:loading="loading"
|
||||||
|
|
|
@ -21,7 +21,8 @@ const loginRules = reactive(<FormRules>{
|
||||||
},
|
},
|
||||||
trigger: "blur"
|
trigger: "blur"
|
||||||
}
|
}
|
||||||
]
|
],
|
||||||
|
emailCode: [{ required: true, trigger: "blur", type: "string" }]
|
||||||
});
|
});
|
||||||
|
|
||||||
export { loginRules };
|
export { loginRules };
|
||||||
|
|
Loading…
Reference in New Issue