提交 25bf8bff authored 作者: 詹银鑫's avatar 詹银鑫

feat: 添加验证码功能和接口联调

上级 84db1843
......@@ -6,6 +6,11 @@ export const getLogin = (data?: object) => {
return http.request<LoginResult>("post", "/api/auth/login", { data });
};
/** 获取验证码 */
export const getCaptcha = (time?: string) => {
return http.request<CaptchaResult>("get", "/api/auth/captchaNumber/" + time);
};
/** 刷新`token` */
export const refreshTokenApi = (data?: object) => {
return http.request<RefreshTokenResult>("post", "/refresh-token", { data });
......@@ -255,3 +260,9 @@ export type RefreshTokenResult = {
expires: Date;
};
};
export type CaptchaResult = {
code: string;
success: boolean;
data: string;
};
import { ref, onMounted } from "vue";
import { getCaptcha } from "@/api/systems";
import { useUserStoreHook } from "@/store/modules/user";
/**
* 绘制图形验证码
* @param width - 图形宽度
......@@ -44,42 +45,50 @@ function randomColor(min: number, max: number) {
function draw(dom: HTMLCanvasElement, width: number, height: number) {
let imgCode = "";
const NUMBER_STRING = "0123456789";
const ctx = dom.getContext("2d");
if (!ctx) return imgCode;
ctx.fillStyle = randomColor(180, 230);
ctx.fillRect(0, 0, width, height);
for (let i = 0; i < 4; i += 1) {
const text = NUMBER_STRING[randomNum(0, NUMBER_STRING.length)];
imgCode += text;
const fontSize = randomNum(18, 41);
const deg = randomNum(-30, 30);
ctx.font = `${fontSize}px Simhei`;
ctx.textBaseline = "top";
ctx.fillStyle = randomColor(80, 150);
ctx.save();
ctx.translate(30 * i + 15, 15);
ctx.rotate((deg * Math.PI) / 180);
ctx.fillText(text, -15 + 5, -15);
ctx.restore();
}
for (let i = 0; i < 5; i += 1) {
ctx.beginPath();
ctx.moveTo(randomNum(0, width), randomNum(0, height));
ctx.lineTo(randomNum(0, width), randomNum(0, height));
ctx.strokeStyle = randomColor(180, 230);
ctx.closePath();
ctx.stroke();
}
for (let i = 0; i < 41; i += 1) {
ctx.beginPath();
ctx.arc(randomNum(0, width), randomNum(0, height), 1, 0, 2 * Math.PI);
ctx.closePath();
ctx.fillStyle = randomColor(150, 200);
ctx.fill();
}
return imgCode;
let captchaCode = "";
const now = new Date().getTime().toString();
getCaptcha(now).then(res => {
if (res.code === "0") {
captchaCode = res.data;
useUserStoreHook().SET_VERIFYCODE(captchaCode);
useUserStoreHook().SET_CAPTCHATIME(now);
const ctx = dom.getContext("2d");
if (!ctx) return imgCode;
ctx.fillStyle = randomColor(180, 230);
ctx.fillRect(0, 0, width, height);
for (let i = 0; i < captchaCode.length; i += 1) {
// const text = NUMBER_STRING[randomNum(0, NUMBER_STRING.length)];
const text = captchaCode[i];
imgCode += text;
const fontSize = randomNum(18, 41);
const deg = randomNum(-30, 30);
ctx.font = `${fontSize}px Simhei`;
ctx.textBaseline = "top";
ctx.fillStyle = randomColor(80, 150);
ctx.save();
ctx.translate(30 * i + 15, 15);
ctx.rotate((deg * Math.PI) / 180);
ctx.fillText(text, -15 + 5, -15);
ctx.restore();
}
for (let i = 0; i < 5; i += 1) {
ctx.beginPath();
ctx.moveTo(randomNum(0, width), randomNum(0, height));
ctx.lineTo(randomNum(0, width), randomNum(0, height));
ctx.strokeStyle = randomColor(180, 230);
ctx.closePath();
ctx.stroke();
}
for (let i = 0; i < 41; i += 1) {
ctx.beginPath();
ctx.arc(randomNum(0, width), randomNum(0, height), 1, 0, 2 * Math.PI);
ctx.closePath();
ctx.fillStyle = randomColor(150, 200);
ctx.fill();
}
return imgCode as string;
} else {
return "";
}
});
}
......@@ -44,7 +44,9 @@ export const useUserStore = defineStore("pure-user", {
// 登录页的免登录存储几天,默认7天
loginDay: 7,
token: localStorage.getItem("token") || "",
refreshToken: localStorage.getItem("refreshToken") || ""
refreshToken: localStorage.getItem("refreshToken") || "",
// 拿到验证码的时间戳
captchaTime: ""
}),
actions: {
/** 存储头像 */
......@@ -83,6 +85,10 @@ export const useUserStore = defineStore("pure-user", {
SET_LOGINDAY(value: number) {
this.loginDay = Number(value);
},
/** 设置登录时验证码时间戳 */
SET_CAPTCHATIME(value: string) {
this.captchaTime = value;
},
/** 登入 */
async loginByUsername(data) {
return new Promise<UserResult>((resolve, reject) => {
......
......@@ -49,4 +49,5 @@ export type userType = {
loginDay?: number;
token: string;
refreshToken: string;
captchaTime: string;
};
......@@ -70,6 +70,7 @@ import Info from "~icons/ri/information-line";
import Keyhole from "~icons/ri/shield-keyhole-line";
// 引入 token 操作工具函数
import { setToken, getToken } from "@/utils/auth";
import { any } from "vue-types";
/**
* 定义组件选项
......@@ -127,15 +128,17 @@ const ruleForm = reactive({
const onLogin = async (formEl: FormInstance | undefined) => {
if (!formEl) return;
// 对表单进行验证
console.log("ruleForm", formEl);
await formEl.validate(valid => {
if (valid) {
// 验证通过,设置加载状态
loading.value = true;
console.log("ruleForm", ruleForm);
useUserStoreHook()
.loginByUsername({
username: ruleForm.username,
password: MD5(ruleForm.password).toString()
password: MD5(ruleForm.password).toString(),
timer: useUserStoreHook().captchaTime,
captcha: ruleForm.verifyCode
})
.then(res => {
if (res.status === 200 || res.success) {
......@@ -146,7 +149,6 @@ const onLogin = async (formEl: FormInstance | undefined) => {
expires: res.data.expires
// expires: new Date(Date.now() + 9999999 * 1000)
});
console.log("getToken", getToken);
// 初始化路由
return initRouter().then(() => {
disabled.value = true;
......@@ -314,7 +316,7 @@ watch(loginDay, value => {
</Motion>
<!-- 注释掉的验证码输入项 -->
<!-- <Motion :delay="200">
<Motion :delay="200">
<el-form-item prop="verifyCode">
<el-input
v-model="ruleForm.verifyCode"
......@@ -327,7 +329,7 @@ watch(loginDay, value => {
</template>
</el-input>
</el-form-item>
</Motion> -->
</Motion>
<!-- 记住登录和忘记密码区域 -->
<Motion :delay="250">
......
......@@ -51,6 +51,11 @@ const loginRules = reactive<FormRules>({
* @param callback - 验证回调函数,用于返回验证结果
*/
validator: (rule, value, callback) => {
console.log("value", value);
console.log(
"useUserStoreHook().verifyCode",
useUserStoreHook().verifyCode
);
if (value === "") {
// 验证码为空时,返回错误信息
callback(new Error(transformI18n($t("login.pureVerifyCodeReg"))));
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论