forked from integrated_whb/integrated_whb_vue
feat(login): 添加手机验证码登录功能
- 新增 sendSmsCode 和 loginByCode API 接口 - 将登录方式从用户名密码改为手机号验证码 - 添加手机号格式验证和验证码输入框 - 实现验证码倒计时功能和发送逻辑 - 更新登录表单验证规则和提交流程 - 移除原有的图形验证码组件依赖演示用
parent
0bfef35792
commit
2caaec186f
|
|
@ -1,6 +1,8 @@
|
|||
import { post, upload } from "./axios";
|
||||
|
||||
export const Login = (params) => post("/admin/check", params); // 登录
|
||||
export const sendSmsCode = (params) => post("/admin/sendSmsCode", params); // 发送验证码
|
||||
export const loginByCode = (params) => post("/admin/checkByCode", params); // 验证码登录
|
||||
export const logout = (params) => post("/main/logout", params); // 退出登录
|
||||
export const getAsyncRouter = (params) => post("/main/index", params); // 获取动态路由
|
||||
export const getHasMenu = (params) =>
|
||||
|
|
|
|||
|
|
@ -24,10 +24,11 @@
|
|||
:rules="data.rules"
|
||||
@submit.prevent="fnLogin"
|
||||
>
|
||||
<el-form-item prop="username">
|
||||
<el-form-item prop="phone">
|
||||
<el-input
|
||||
v-model="data.form.username"
|
||||
placeholder="请输入用户名"
|
||||
v-model="data.form.phone"
|
||||
placeholder="请输入手机号"
|
||||
maxlength="11"
|
||||
tabindex="1"
|
||||
>
|
||||
<template #prepend>
|
||||
|
|
@ -35,20 +36,26 @@
|
|||
</template>
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
<el-form-item prop="password">
|
||||
<el-input
|
||||
v-model="data.form.password"
|
||||
type="password"
|
||||
placeholder="请输入密码"
|
||||
tabindex="2"
|
||||
>
|
||||
<template #prepend>
|
||||
<icon-lock size="16" fill="#9ba2a8" :stroke-width="3" />
|
||||
</template>
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<verification v-model:verification-pass="verificationPass" />
|
||||
<el-form-item prop="code">
|
||||
<div class="code-row">
|
||||
<el-input
|
||||
v-model="data.form.code"
|
||||
placeholder="请输入验证码"
|
||||
maxlength="6"
|
||||
tabindex="2"
|
||||
>
|
||||
<template #prepend>
|
||||
<icon-lock size="16" fill="#9ba2a8" :stroke-width="3" />
|
||||
</template>
|
||||
</el-input>
|
||||
<el-button
|
||||
class="code-btn"
|
||||
:disabled="data.countdown > 0"
|
||||
@click="fnSendCode"
|
||||
>
|
||||
{{ data.countdown > 0 ? `${data.countdown}s` : "获取验证码" }}
|
||||
</el-button>
|
||||
</div>
|
||||
</el-form-item>
|
||||
<el-form-item class="button">
|
||||
<el-button native-type="submit">登录</el-button>
|
||||
|
|
@ -84,9 +91,8 @@
|
|||
import { reactive, ref } from "vue";
|
||||
import { useRouter } from "vue-router";
|
||||
import { ElMessage } from "element-plus";
|
||||
import Verification from "@/components/verification/index";
|
||||
import { useUserStore } from "@/pinia/user";
|
||||
import { getAppVersion, Login } from "@/request/api";
|
||||
import { getAppVersion, sendSmsCode, loginByCode } from "@/request/api";
|
||||
import { debounce } from "throttle-debounce";
|
||||
import useFormValidate from "@/assets/js/useFormValidate.js";
|
||||
import LayoutQrCode from "@/components/qr_code/index.vue";
|
||||
|
|
@ -94,18 +100,29 @@ import LayoutQrCode from "@/components/qr_code/index.vue";
|
|||
const VITE_FILE_URL = import.meta.env.VITE_FILE_URL;
|
||||
const router = useRouter();
|
||||
const formRef = ref(null);
|
||||
const verificationPass = ref(false);
|
||||
const userStore = useUserStore();
|
||||
|
||||
const data = reactive({
|
||||
form: {
|
||||
username: "",
|
||||
password: "",
|
||||
},
|
||||
rules: {
|
||||
username: [{ required: true, message: "请输入用户名", trigger: "blur" }],
|
||||
password: [{ required: true, message: "请输入密码", trigger: "blur" }],
|
||||
phone: "",
|
||||
code: "",
|
||||
},
|
||||
qyAppSrc: "",
|
||||
countdown: 0,
|
||||
rules: {
|
||||
phone: [
|
||||
{ required: true, message: "请输入手机号", trigger: "blur" },
|
||||
{
|
||||
pattern: /^1[3-9]\d{9}$/,
|
||||
message: "手机号格式不正确",
|
||||
trigger: "blur",
|
||||
},
|
||||
],
|
||||
code: [
|
||||
{ required: true, message: "请输入验证码", trigger: "blur" },
|
||||
{ len: 6, message: "验证码为6位数字", trigger: "blur" },
|
||||
],
|
||||
},
|
||||
});
|
||||
|
||||
const fnAppVersion = async () => {
|
||||
|
|
@ -113,48 +130,60 @@ const fnAppVersion = async () => {
|
|||
data.qyAppSrc = VITE_FILE_URL + resData.pd.FILEURL;
|
||||
};
|
||||
await fnAppVersion();
|
||||
|
||||
let timer = null;
|
||||
const fnSendCode = async () => {
|
||||
if (!data.form.phone || !/^1[3-9]\d{9}$/.test(data.form.phone)) {
|
||||
ElMessage.warning("请输入正确的手机号");
|
||||
return;
|
||||
}
|
||||
if (data.countdown > 0) return;
|
||||
const resData = await sendSmsCode({ PHONE: data.form.phone });
|
||||
if (resData.result === "success") {
|
||||
ElMessage.success("验证码已发送");
|
||||
data.countdown = 60;
|
||||
timer = setInterval(() => {
|
||||
data.countdown--;
|
||||
if (data.countdown <= 0) {
|
||||
clearInterval(timer);
|
||||
timer = null;
|
||||
}
|
||||
}, 1000);
|
||||
} else {
|
||||
ElMessage.error(resData.msg || "发送失败");
|
||||
}
|
||||
};
|
||||
|
||||
const fnLogin = debounce(
|
||||
1000,
|
||||
() => {
|
||||
if (import.meta.env.DEV) {
|
||||
fnSubmitLogin();
|
||||
return;
|
||||
}
|
||||
if (verificationPass.value) {
|
||||
fnSubmitLogin();
|
||||
} else {
|
||||
ElMessage.warning("请进行登录验证");
|
||||
}
|
||||
fnSubmitLogin();
|
||||
},
|
||||
{ atBegin: true }
|
||||
);
|
||||
const fnSubmitLogin = async () => {
|
||||
await useFormValidate(formRef, "请输入用户名密码");
|
||||
// eslint-disable-next-line no-undef
|
||||
const jsencrypt = new JSEncrypt();
|
||||
jsencrypt.setPublicKey(
|
||||
"MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDUoHAavCikaZxjlDM6Km8cX+ye78F4oF39AcEfnE1p2Yn9pJ9WFxYZ4Vkh6F8SKMi7k4nYsKceqB1RwG996SvHQ5C3pM3nbXCP4K15ad6QhN4a7lzlbLhiJcyIKszvvK8ncUDw8mVQ0j/2mwxv05yH6LN9OKU6Hzm1ninpWeE+awIDAQAB"
|
||||
);
|
||||
const KEYDATA = jsencrypt.encrypt(
|
||||
"zcloudchina" + data.form.username + ",zy," + data.form.password
|
||||
);
|
||||
|
||||
const resData = await Login({
|
||||
KEYDATA,
|
||||
const fnSubmitLogin = async () => {
|
||||
await useFormValidate(formRef, "请填写手机号和验证码");
|
||||
const resData = await loginByCode({
|
||||
PHONE: data.form.phone,
|
||||
CODE: data.form.code,
|
||||
SOURCE: 1,
|
||||
});
|
||||
console.log(resData,'123')
|
||||
await userStore.setUserInfo({
|
||||
...userStore.getUserInfo,
|
||||
...resData,
|
||||
});
|
||||
await router.replace({
|
||||
path: "/index",
|
||||
query: {
|
||||
passwordType: resData.passwordType,
|
||||
token: resData.token, // 传递令牌
|
||||
},
|
||||
});
|
||||
if (resData.result === "success") {
|
||||
await userStore.setUserInfo({
|
||||
...userStore.getUserInfo,
|
||||
...resData,
|
||||
});
|
||||
await router.replace({
|
||||
path: "/index",
|
||||
query: {
|
||||
passwordType: resData.passwordType,
|
||||
token: resData.token,
|
||||
},
|
||||
});
|
||||
} else {
|
||||
ElMessage.error(resData.msg || "登录失败");
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
|
|
@ -248,6 +277,23 @@ const fnSubmitLogin = async () => {
|
|||
--el-input-border-color: #dde0eb;
|
||||
}
|
||||
|
||||
.code-row {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
gap: 10px;
|
||||
|
||||
.el-input {
|
||||
flex: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.code-btn {
|
||||
height: 40px;
|
||||
width: 120px;
|
||||
flex-shrink: 0;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.button {
|
||||
.el-button {
|
||||
width: 100%;
|
||||
|
|
|
|||
Loading…
Reference in New Issue