From 2caaec186f32640d936f7fc4d91630a08784e5c6 Mon Sep 17 00:00:00 2001 From: fangjiakai <450850793@qq.com> Date: Wed, 22 Apr 2026 14:56:36 +0800 Subject: [PATCH] =?UTF-8?q?feat(login):=20=E6=B7=BB=E5=8A=A0=E6=89=8B?= =?UTF-8?q?=E6=9C=BA=E9=AA=8C=E8=AF=81=E7=A0=81=E7=99=BB=E5=BD=95=E5=8A=9F?= =?UTF-8?q?=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增 sendSmsCode 和 loginByCode API 接口 - 将登录方式从用户名密码改为手机号验证码 - 添加手机号格式验证和验证码输入框 - 实现验证码倒计时功能和发送逻辑 - 更新登录表单验证规则和提交流程 - 移除原有的图形验证码组件依赖 --- src/request/api.js | 2 + src/views/login/index.vue | 164 ++++++++++++++++++++++++-------------- 2 files changed, 107 insertions(+), 59 deletions(-) diff --git a/src/request/api.js b/src/request/api.js index ebf0e20..4834bdc 100644 --- a/src/request/api.js +++ b/src/request/api.js @@ -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) => diff --git a/src/views/login/index.vue b/src/views/login/index.vue index 302b1a4..583090c 100644 --- a/src/views/login/index.vue +++ b/src/views/login/index.vue @@ -24,10 +24,11 @@ :rules="data.rules" @submit.prevent="fnLogin" > - + - - - - - - - + +
+ + + + + {{ data.countdown > 0 ? `${data.countdown}s` : "获取验证码" }} + +
登录 @@ -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 || "登录失败"); + } }; @@ -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%;