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";
|
import { post, upload } from "./axios";
|
||||||
|
|
||||||
export const Login = (params) => post("/admin/check", params); // 登录
|
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 logout = (params) => post("/main/logout", params); // 退出登录
|
||||||
export const getAsyncRouter = (params) => post("/main/index", params); // 获取动态路由
|
export const getAsyncRouter = (params) => post("/main/index", params); // 获取动态路由
|
||||||
export const getHasMenu = (params) =>
|
export const getHasMenu = (params) =>
|
||||||
|
|
|
||||||
|
|
@ -24,10 +24,11 @@
|
||||||
:rules="data.rules"
|
:rules="data.rules"
|
||||||
@submit.prevent="fnLogin"
|
@submit.prevent="fnLogin"
|
||||||
>
|
>
|
||||||
<el-form-item prop="username">
|
<el-form-item prop="phone">
|
||||||
<el-input
|
<el-input
|
||||||
v-model="data.form.username"
|
v-model="data.form.phone"
|
||||||
placeholder="请输入用户名"
|
placeholder="请输入手机号"
|
||||||
|
maxlength="11"
|
||||||
tabindex="1"
|
tabindex="1"
|
||||||
>
|
>
|
||||||
<template #prepend>
|
<template #prepend>
|
||||||
|
|
@ -35,20 +36,26 @@
|
||||||
</template>
|
</template>
|
||||||
</el-input>
|
</el-input>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item prop="password">
|
<el-form-item prop="code">
|
||||||
<el-input
|
<div class="code-row">
|
||||||
v-model="data.form.password"
|
<el-input
|
||||||
type="password"
|
v-model="data.form.code"
|
||||||
placeholder="请输入密码"
|
placeholder="请输入验证码"
|
||||||
tabindex="2"
|
maxlength="6"
|
||||||
>
|
tabindex="2"
|
||||||
<template #prepend>
|
>
|
||||||
<icon-lock size="16" fill="#9ba2a8" :stroke-width="3" />
|
<template #prepend>
|
||||||
</template>
|
<icon-lock size="16" fill="#9ba2a8" :stroke-width="3" />
|
||||||
</el-input>
|
</template>
|
||||||
</el-form-item>
|
</el-input>
|
||||||
<el-form-item>
|
<el-button
|
||||||
<verification v-model:verification-pass="verificationPass" />
|
class="code-btn"
|
||||||
|
:disabled="data.countdown > 0"
|
||||||
|
@click="fnSendCode"
|
||||||
|
>
|
||||||
|
{{ data.countdown > 0 ? `${data.countdown}s` : "获取验证码" }}
|
||||||
|
</el-button>
|
||||||
|
</div>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item class="button">
|
<el-form-item class="button">
|
||||||
<el-button native-type="submit">登录</el-button>
|
<el-button native-type="submit">登录</el-button>
|
||||||
|
|
@ -84,9 +91,8 @@
|
||||||
import { reactive, ref } from "vue";
|
import { reactive, ref } from "vue";
|
||||||
import { useRouter } from "vue-router";
|
import { useRouter } from "vue-router";
|
||||||
import { ElMessage } from "element-plus";
|
import { ElMessage } from "element-plus";
|
||||||
import Verification from "@/components/verification/index";
|
|
||||||
import { useUserStore } from "@/pinia/user";
|
import { useUserStore } from "@/pinia/user";
|
||||||
import { getAppVersion, Login } from "@/request/api";
|
import { getAppVersion, sendSmsCode, loginByCode } from "@/request/api";
|
||||||
import { debounce } from "throttle-debounce";
|
import { debounce } from "throttle-debounce";
|
||||||
import useFormValidate from "@/assets/js/useFormValidate.js";
|
import useFormValidate from "@/assets/js/useFormValidate.js";
|
||||||
import LayoutQrCode from "@/components/qr_code/index.vue";
|
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 VITE_FILE_URL = import.meta.env.VITE_FILE_URL;
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const formRef = ref(null);
|
const formRef = ref(null);
|
||||||
const verificationPass = ref(false);
|
|
||||||
const userStore = useUserStore();
|
const userStore = useUserStore();
|
||||||
|
|
||||||
const data = reactive({
|
const data = reactive({
|
||||||
form: {
|
form: {
|
||||||
username: "",
|
phone: "",
|
||||||
password: "",
|
code: "",
|
||||||
},
|
|
||||||
rules: {
|
|
||||||
username: [{ required: true, message: "请输入用户名", trigger: "blur" }],
|
|
||||||
password: [{ required: true, message: "请输入密码", trigger: "blur" }],
|
|
||||||
},
|
},
|
||||||
qyAppSrc: "",
|
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 () => {
|
const fnAppVersion = async () => {
|
||||||
|
|
@ -113,48 +130,60 @@ const fnAppVersion = async () => {
|
||||||
data.qyAppSrc = VITE_FILE_URL + resData.pd.FILEURL;
|
data.qyAppSrc = VITE_FILE_URL + resData.pd.FILEURL;
|
||||||
};
|
};
|
||||||
await fnAppVersion();
|
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(
|
const fnLogin = debounce(
|
||||||
1000,
|
1000,
|
||||||
() => {
|
() => {
|
||||||
if (import.meta.env.DEV) {
|
fnSubmitLogin();
|
||||||
fnSubmitLogin();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (verificationPass.value) {
|
|
||||||
fnSubmitLogin();
|
|
||||||
} else {
|
|
||||||
ElMessage.warning("请进行登录验证");
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
{ atBegin: true }
|
{ 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({
|
const fnSubmitLogin = async () => {
|
||||||
KEYDATA,
|
await useFormValidate(formRef, "请填写手机号和验证码");
|
||||||
|
const resData = await loginByCode({
|
||||||
|
PHONE: data.form.phone,
|
||||||
|
CODE: data.form.code,
|
||||||
SOURCE: 1,
|
SOURCE: 1,
|
||||||
});
|
});
|
||||||
console.log(resData,'123')
|
if (resData.result === "success") {
|
||||||
await userStore.setUserInfo({
|
await userStore.setUserInfo({
|
||||||
...userStore.getUserInfo,
|
...userStore.getUserInfo,
|
||||||
...resData,
|
...resData,
|
||||||
});
|
});
|
||||||
await router.replace({
|
await router.replace({
|
||||||
path: "/index",
|
path: "/index",
|
||||||
query: {
|
query: {
|
||||||
passwordType: resData.passwordType,
|
passwordType: resData.passwordType,
|
||||||
token: resData.token, // 传递令牌
|
token: resData.token,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
} else {
|
||||||
|
ElMessage.error(resData.msg || "登录失败");
|
||||||
|
}
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
@ -248,6 +277,23 @@ const fnSubmitLogin = async () => {
|
||||||
--el-input-border-color: #dde0eb;
|
--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 {
|
.button {
|
||||||
.el-button {
|
.el-button {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue