support sms login
This commit is contained in:
@@ -1,212 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="login-page">
|
|
||||||
<section class="login-panel">
|
|
||||||
<div class="brand">
|
|
||||||
<div class="logo">
|
|
||||||
<span>糕</span>
|
|
||||||
</div>
|
|
||||||
<h1>糕店客户管理</h1>
|
|
||||||
</div>
|
|
||||||
<t-form class="form" label-align="left">
|
|
||||||
<t-form-item label="账号">
|
|
||||||
<t-input
|
|
||||||
v-model.trim="formModel.user"
|
|
||||||
type="tel"
|
|
||||||
inputmode="tel"
|
|
||||||
placeholder="请输入账号或手机号"
|
|
||||||
borderless
|
|
||||||
/>
|
|
||||||
</t-form-item>
|
|
||||||
<t-form-item label="密码">
|
|
||||||
<t-input
|
|
||||||
v-model.trim="formModel.password"
|
|
||||||
type="password"
|
|
||||||
placeholder="请输入密码"
|
|
||||||
borderless
|
|
||||||
/>
|
|
||||||
</t-form-item>
|
|
||||||
<t-form-item label="验证码">
|
|
||||||
<t-input
|
|
||||||
v-model.trim="formModel.captcha"
|
|
||||||
placeholder="请输入验证码"
|
|
||||||
borderless
|
|
||||||
/>
|
|
||||||
</t-form-item>
|
|
||||||
<div class="captcha-wrapper">
|
|
||||||
<captcha class="captcha" ref="captchaRef" />
|
|
||||||
</div>
|
|
||||||
<t-button class="submit" theme="primary" size="medium" :loading="isSubmitting" @click="login">登录</t-button>
|
|
||||||
</t-form>
|
|
||||||
</section>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup lang="ts">
|
|
||||||
import {Toast} from "tdesign-mobile-vue";
|
|
||||||
import {Captcha, useUserStore} from "timi-web";
|
|
||||||
import {useCurrentStoreStore} from "@/store/currentStoreStore";
|
|
||||||
|
|
||||||
type CaptchaExpose = {
|
|
||||||
captchaId: string;
|
|
||||||
update: () => Promise<void>;
|
|
||||||
};
|
|
||||||
|
|
||||||
defineOptions({
|
|
||||||
name: "Login"
|
|
||||||
});
|
|
||||||
|
|
||||||
const route = useRoute();
|
|
||||||
const router = useRouter();
|
|
||||||
const userStore = useUserStore();
|
|
||||||
const currentStoreStore = useCurrentStoreStore();
|
|
||||||
const formModel = reactive({
|
|
||||||
user: "",
|
|
||||||
password: "",
|
|
||||||
captcha: ""
|
|
||||||
});
|
|
||||||
const isSubmitting = ref(false);
|
|
||||||
const captchaRef = ref<CaptchaExpose | null>(null);
|
|
||||||
|
|
||||||
function getCaptchaId(): string {
|
|
||||||
return captchaRef.value?.captchaId || "";
|
|
||||||
}
|
|
||||||
|
|
||||||
async function login(): Promise<void> {
|
|
||||||
const captchaId = getCaptchaId();
|
|
||||||
if (!formModel.user || !formModel.password || !formModel.captcha || !captchaId) {
|
|
||||||
Toast({
|
|
||||||
theme: "warning",
|
|
||||||
direction: "row",
|
|
||||||
message: "请输入账号、密码和验证码"
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
isSubmitting.value = true;
|
|
||||||
try {
|
|
||||||
await userStore.login({
|
|
||||||
captchaId,
|
|
||||||
captcha: formModel.captcha,
|
|
||||||
data: {
|
|
||||||
user: formModel.user,
|
|
||||||
password: formModel.password
|
|
||||||
}
|
|
||||||
});
|
|
||||||
await currentStoreStore.loadAfterLogin();
|
|
||||||
const redirect = typeof route.query.redirect === "string" ? route.query.redirect : "/management";
|
|
||||||
await router.replace(redirect);
|
|
||||||
} catch (error) {
|
|
||||||
formModel.captcha = "";
|
|
||||||
await captchaRef.value?.update();
|
|
||||||
throw error;
|
|
||||||
} finally {
|
|
||||||
isSubmitting.value = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style scoped lang="less">
|
|
||||||
.login-page {
|
|
||||||
width: 100%;
|
|
||||||
padding: 4.5rem 1rem 1.5rem 1rem;
|
|
||||||
display: flex;
|
|
||||||
min-height: 100vh;
|
|
||||||
background: #F5F5F8;
|
|
||||||
align-items: flex-start;
|
|
||||||
justify-content: center;
|
|
||||||
|
|
||||||
.login-panel {
|
|
||||||
gap: 1.5rem;
|
|
||||||
width: 100%;
|
|
||||||
padding: 1.5rem;
|
|
||||||
display: flex;
|
|
||||||
max-width: 28rem;
|
|
||||||
background: rgba(255, 255, 255, .94);
|
|
||||||
box-shadow: 0 .75rem 2rem rgba(15, 23, 42, .08);
|
|
||||||
border-radius: 1.4rem;
|
|
||||||
flex-direction: column;
|
|
||||||
|
|
||||||
.captcha-wrapper {
|
|
||||||
display: flex;
|
|
||||||
margin-top: .5rem;
|
|
||||||
justify-content: end;
|
|
||||||
|
|
||||||
.captcha {
|
|
||||||
border: 1px solid var(--td-border-level-1-color);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.brand {
|
|
||||||
display: grid;
|
|
||||||
place-items: center;
|
|
||||||
|
|
||||||
h1 {
|
|
||||||
margin: .875rem 0 0 0;
|
|
||||||
color: #0f172a;
|
|
||||||
font-size: 1.5rem;
|
|
||||||
font-weight: 700;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.logo {
|
|
||||||
color: #FFF;
|
|
||||||
width: 5rem;
|
|
||||||
height: 5rem;
|
|
||||||
display: grid;
|
|
||||||
background: #0f766e;
|
|
||||||
box-shadow: 0 .75rem 1.5rem rgba(15, 118, 110, .22);
|
|
||||||
place-items: center;
|
|
||||||
border-radius: 1.25rem;
|
|
||||||
|
|
||||||
span {
|
|
||||||
font-size: 2rem;
|
|
||||||
font-weight: 800;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.form {
|
|
||||||
margin: 0;
|
|
||||||
padding: .25rem 0;
|
|
||||||
background: transparent;
|
|
||||||
|
|
||||||
.captcha-row {
|
|
||||||
gap: .75rem;
|
|
||||||
width: 100%;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
:deep(.tui-captcha) {
|
|
||||||
flex: 0 0 7.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.submit {
|
|
||||||
gap: .5rem;
|
|
||||||
width: 8rem;
|
|
||||||
margin: 2rem auto 0 auto;
|
|
||||||
display: flex;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
:global(html.theme-dark) {
|
|
||||||
.login-page {
|
|
||||||
background: #10161f;
|
|
||||||
|
|
||||||
.login-panel {
|
|
||||||
background: rgba(15, 23, 42, .88);
|
|
||||||
box-shadow: 0 .75rem 2rem rgba(2, 6, 23, .32);
|
|
||||||
}
|
|
||||||
|
|
||||||
.brand h1 {
|
|
||||||
color: #e2e8f0;
|
|
||||||
}
|
|
||||||
|
|
||||||
:deep(.tui-captcha) {
|
|
||||||
color: #94a3b8;
|
|
||||||
background: #0f172a;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@@ -0,0 +1,109 @@
|
|||||||
|
<template>
|
||||||
|
<div class="login-page">
|
||||||
|
<section class="login-panel">
|
||||||
|
<div class="brand">
|
||||||
|
<div class="logo"><span>糕</span></div>
|
||||||
|
<h1>糕店客户管理</h1>
|
||||||
|
</div>
|
||||||
|
<t-segmented class="login-type" v-model="loginType" :options="loginTypeOptions" />
|
||||||
|
<password-login v-if="loginType === 'password'" class="form" />
|
||||||
|
<sms-login v-else class="form" />
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import PasswordLogin from "@/pages/user/component/PasswordLogin.vue";
|
||||||
|
import SmsLogin from "@/pages/user/component/SmsLogin.vue";
|
||||||
|
|
||||||
|
defineOptions({
|
||||||
|
name: "LoginIndex"
|
||||||
|
});
|
||||||
|
|
||||||
|
type LoginType = "password" | "sms";
|
||||||
|
|
||||||
|
const loginType = ref<LoginType>("password");
|
||||||
|
const loginTypeOptions = [
|
||||||
|
{ value: "password" as LoginType, label: "密码登录" },
|
||||||
|
{ value: "sms" as LoginType, label: "短信登录" }
|
||||||
|
];
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="less">
|
||||||
|
.login-page {
|
||||||
|
width: 100%;
|
||||||
|
padding: 4.5rem 1rem 1.5rem;
|
||||||
|
display: flex;
|
||||||
|
min-height: 100vh;
|
||||||
|
background: #F5F5F8;
|
||||||
|
align-items: flex-start;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
.login-panel {
|
||||||
|
width: 100%;
|
||||||
|
padding: 1.5rem;
|
||||||
|
display: flex;
|
||||||
|
max-width: 28rem;
|
||||||
|
background: rgba(255, 255, 255, .94);
|
||||||
|
box-shadow: 0 .75rem 2rem rgba(15, 23, 42, .08);
|
||||||
|
border-radius: 1.4rem;
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
|
.form {
|
||||||
|
margin: 0;
|
||||||
|
|
||||||
|
:deep(.t-form__label) {
|
||||||
|
width: 5.5rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-type {
|
||||||
|
width: fit-content;
|
||||||
|
margin: 1rem auto 0 auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.brand {
|
||||||
|
display: grid;
|
||||||
|
place-items: center;
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
margin: .875rem 0 0;
|
||||||
|
color: #0f172a;
|
||||||
|
font-size: 1.5rem;
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.logo {
|
||||||
|
width: 5rem;
|
||||||
|
height: 5rem;
|
||||||
|
color: #FFF;
|
||||||
|
display: grid;
|
||||||
|
background: #0f766e;
|
||||||
|
box-shadow: 0 .75rem 1.5rem rgba(15, 118, 110, .22);
|
||||||
|
place-items: center;
|
||||||
|
border-radius: 1.25rem;
|
||||||
|
|
||||||
|
span {
|
||||||
|
font-size: 2rem;
|
||||||
|
font-weight: 800;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
:global(html.theme-dark) {
|
||||||
|
.login-page {
|
||||||
|
background: #10161f;
|
||||||
|
|
||||||
|
.login-panel {
|
||||||
|
background: rgba(15, 23, 42, .88);
|
||||||
|
box-shadow: 0 .75rem 2rem rgba(2, 6, 23, .32);
|
||||||
|
}
|
||||||
|
|
||||||
|
.brand h1 {
|
||||||
|
color: #e2e8f0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,124 @@
|
|||||||
|
<template>
|
||||||
|
<t-form class="password-login" @submit="login">
|
||||||
|
<t-form-item label="账号">
|
||||||
|
<t-input v-model.trim="formModel.user" type="tel" inputmode="tel" placeholder="请输入账号或手机号" borderless />
|
||||||
|
</t-form-item>
|
||||||
|
<t-form-item label="密码">
|
||||||
|
<t-input v-model="formModel.password" type="password" placeholder="请输入密码" borderless />
|
||||||
|
</t-form-item>
|
||||||
|
<t-form-item label="验证码">
|
||||||
|
<t-input v-model.trim="formModel.captcha" placeholder="请输入验证码" borderless />
|
||||||
|
</t-form-item>
|
||||||
|
<div class="captcha-wrapper">
|
||||||
|
<captcha class="captcha" ref="captchaRef" :width="120" :height="40" />
|
||||||
|
</div>
|
||||||
|
<div class="exec">
|
||||||
|
<t-button theme="primary" class="button" type="submit" :loading="isSubmitting" :disabled="isSubmitting">登录</t-button>
|
||||||
|
</div>
|
||||||
|
</t-form>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { Toast } from "tdesign-mobile-vue";
|
||||||
|
import { Captcha, useUserStore } from "timi-web";
|
||||||
|
import { useCurrentStoreStore } from "@/store/currentStoreStore";
|
||||||
|
|
||||||
|
type CaptchaExpose = {
|
||||||
|
captchaId: string;
|
||||||
|
update: () => Promise<void>;
|
||||||
|
};
|
||||||
|
|
||||||
|
type PasswordForm = {
|
||||||
|
user: string;
|
||||||
|
password: string;
|
||||||
|
captcha: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
defineOptions({
|
||||||
|
name: "PasswordLogin"
|
||||||
|
});
|
||||||
|
|
||||||
|
const route = useRoute();
|
||||||
|
const router = useRouter();
|
||||||
|
const userStore = useUserStore();
|
||||||
|
const currentStoreStore = useCurrentStoreStore();
|
||||||
|
const formModel = reactive<PasswordForm>({
|
||||||
|
user: "",
|
||||||
|
password: "",
|
||||||
|
captcha: ""
|
||||||
|
});
|
||||||
|
const isSubmitting = ref(false);
|
||||||
|
const captchaRef = ref<CaptchaExpose | null>(null);
|
||||||
|
|
||||||
|
function getRedirectPath(): string {
|
||||||
|
return typeof route.query.redirect === "string" ? route.query.redirect : "/management";
|
||||||
|
}
|
||||||
|
|
||||||
|
function validateForm(): boolean {
|
||||||
|
if (!formModel.user || !formModel.password || !formModel.captcha || !captchaRef.value?.captchaId) {
|
||||||
|
Toast.warning("请输入账号、密码和验证码");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function login(): Promise<void> {
|
||||||
|
if (isSubmitting.value || !validateForm()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
isSubmitting.value = true;
|
||||||
|
try {
|
||||||
|
await userStore.login({
|
||||||
|
captchaId: captchaRef.value?.captchaId || "",
|
||||||
|
captcha: formModel.captcha,
|
||||||
|
data: {
|
||||||
|
user: formModel.user,
|
||||||
|
password: formModel.password
|
||||||
|
}
|
||||||
|
});
|
||||||
|
await currentStoreStore.loadAfterLogin();
|
||||||
|
await router.replace(getRedirectPath());
|
||||||
|
} catch {
|
||||||
|
formModel.captcha = "";
|
||||||
|
await captchaRef.value?.update();
|
||||||
|
} finally {
|
||||||
|
isSubmitting.value = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="less">
|
||||||
|
.password-login {
|
||||||
|
padding: .25rem 0;
|
||||||
|
background: transparent;
|
||||||
|
|
||||||
|
.captcha-wrapper {
|
||||||
|
display: flex;
|
||||||
|
margin-top: .5rem;
|
||||||
|
justify-content: end;
|
||||||
|
|
||||||
|
.captcha {
|
||||||
|
border: 1px solid var(--td-border-level-1-color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.exec {
|
||||||
|
display: flex;
|
||||||
|
margin-top: 1rem;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
.button {
|
||||||
|
width: 10rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
:global(html.theme-dark) {
|
||||||
|
.password-login {
|
||||||
|
.captcha {
|
||||||
|
background: #0f172a;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,214 @@
|
|||||||
|
<template>
|
||||||
|
<t-form class="sms-login" @submit="login" label-width="6rem">
|
||||||
|
<t-form-item label="手机号">
|
||||||
|
<t-input v-model.trim="formModel.mobilePhone" type="tel" inputmode="tel" placeholder="请输入手机号" borderless />
|
||||||
|
</t-form-item>
|
||||||
|
<t-form-item label="图形验证码">
|
||||||
|
<t-input v-model.trim="formModel.captcha" class="captcha-input" placeholder="请输入验证码" borderless />
|
||||||
|
</t-form-item>
|
||||||
|
<div class="captcha-wrapper">
|
||||||
|
<captcha ref="captchaRef" class="captcha" :width="120" :height="40" />
|
||||||
|
</div>
|
||||||
|
<t-form-item label="短信验证码">
|
||||||
|
<t-input v-model.trim="formModel.smsCode" class="sms-code" placeholder="请输入短信验证码" borderless />
|
||||||
|
</t-form-item>
|
||||||
|
<div class="send-wrapper">
|
||||||
|
<t-button
|
||||||
|
type="button"
|
||||||
|
class="send"
|
||||||
|
size="extra-small"
|
||||||
|
theme="primary"
|
||||||
|
:content="sendSmsText"
|
||||||
|
:disabled="!canSendSms"
|
||||||
|
@click="sendSms"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="exec">
|
||||||
|
<t-button theme="primary" class="button" type="submit" :loading="isSubmitting" :disabled="isSubmitting">登录</t-button>
|
||||||
|
</div>
|
||||||
|
</t-form>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { Toast } from "tdesign-mobile-vue";
|
||||||
|
import { Captcha, NotifyAPI, UserAPI, useUserStore } from "timi-web";
|
||||||
|
import { useCurrentStoreStore } from "@/store/currentStoreStore";
|
||||||
|
|
||||||
|
type CaptchaExpose = {
|
||||||
|
captchaId: string;
|
||||||
|
update: () => Promise<void>;
|
||||||
|
};
|
||||||
|
|
||||||
|
type SmsForm = {
|
||||||
|
mobilePhone: string;
|
||||||
|
captcha: string;
|
||||||
|
smsCode: string;
|
||||||
|
notifyDetailId: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
defineOptions({
|
||||||
|
name: "SmsLogin"
|
||||||
|
});
|
||||||
|
|
||||||
|
const route = useRoute();
|
||||||
|
const router = useRouter();
|
||||||
|
const userStore = useUserStore();
|
||||||
|
const currentStoreStore = useCurrentStoreStore();
|
||||||
|
const formModel = reactive<SmsForm>({
|
||||||
|
mobilePhone: "",
|
||||||
|
captcha: "",
|
||||||
|
smsCode: "",
|
||||||
|
notifyDetailId: ""
|
||||||
|
});
|
||||||
|
const captchaRef = ref<CaptchaExpose | null>(null);
|
||||||
|
const isSubmitting = ref(false);
|
||||||
|
const sendSmsCountdown = ref(0);
|
||||||
|
let sendSmsTimer: number | undefined;
|
||||||
|
|
||||||
|
const isSendingSms = computed(() => 0 < sendSmsCountdown.value);
|
||||||
|
const sendSmsText = computed(() => isSendingSms.value ? `${sendSmsCountdown.value} 秒后重新发送` : "发送验证码");
|
||||||
|
const canSendSms = computed(() => (
|
||||||
|
!isSendingSms.value
|
||||||
|
&& /^1\d{10}$/.test(formModel.mobilePhone)
|
||||||
|
&& 4 === formModel.captcha.length
|
||||||
|
));
|
||||||
|
|
||||||
|
function getRedirectPath(): string {
|
||||||
|
return typeof route.query.redirect === "string" ? route.query.redirect : "/";
|
||||||
|
}
|
||||||
|
|
||||||
|
function getCaptchaId(): string {
|
||||||
|
return captchaRef.value?.captchaId || "";
|
||||||
|
}
|
||||||
|
|
||||||
|
function clearSmsTimer(): void {
|
||||||
|
if (sendSmsTimer === undefined) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
window.clearInterval(sendSmsTimer);
|
||||||
|
sendSmsTimer = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
function startSmsTimer(): void {
|
||||||
|
clearSmsTimer();
|
||||||
|
sendSmsCountdown.value = 60;
|
||||||
|
sendSmsTimer = window.setInterval(() => {
|
||||||
|
sendSmsCountdown.value -= 1;
|
||||||
|
if (!(0 < sendSmsCountdown.value)) {
|
||||||
|
clearSmsTimer();
|
||||||
|
}
|
||||||
|
}, 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function sendSms(): Promise<void> {
|
||||||
|
if (!canSendSms.value) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const captchaId = getCaptchaId();
|
||||||
|
if (!captchaId) {
|
||||||
|
Toast.warning("图形验证码尚未加载,请稍后重试");
|
||||||
|
await captchaRef.value?.update();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
formModel.notifyDetailId = await NotifyAPI.smsSend({
|
||||||
|
captchaId,
|
||||||
|
captcha: formModel.captcha,
|
||||||
|
data: {
|
||||||
|
detailList: [
|
||||||
|
{
|
||||||
|
sendTo: formModel.mobilePhone
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
});
|
||||||
|
startSmsTimer();
|
||||||
|
Toast.success(`验证码已发送,请检查 ${formModel.mobilePhone} 手机短信`);
|
||||||
|
} catch {
|
||||||
|
formModel.notifyDetailId = "";
|
||||||
|
} finally {
|
||||||
|
formModel.captcha = "";
|
||||||
|
await captchaRef.value?.update();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function validateForm(): boolean {
|
||||||
|
if (!/^1\d{10}$/.test(formModel.mobilePhone)) {
|
||||||
|
Toast.warning("请输入正确的 11 位手机号");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!formModel.notifyDetailId || !formModel.smsCode) {
|
||||||
|
Toast.warning("请先获取并输入短信验证码");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function login(): Promise<void> {
|
||||||
|
if (isSubmitting.value || !validateForm()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
isSubmitting.value = true;
|
||||||
|
try {
|
||||||
|
await userStore.updateLoginUser(await UserAPI.loginBySms({
|
||||||
|
detailId: formModel.notifyDetailId,
|
||||||
|
captcha: formModel.smsCode
|
||||||
|
}));
|
||||||
|
await currentStoreStore.loadAfterLogin();
|
||||||
|
await router.replace(getRedirectPath());
|
||||||
|
} catch {
|
||||||
|
formModel.smsCode = "";
|
||||||
|
} finally {
|
||||||
|
isSubmitting.value = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onUnmounted(() => {
|
||||||
|
clearSmsTimer();
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="less">
|
||||||
|
.sms-login {
|
||||||
|
padding: .25rem 0;
|
||||||
|
background: transparent;
|
||||||
|
|
||||||
|
.send-wrapper,
|
||||||
|
.captcha-wrapper {
|
||||||
|
display: flex;
|
||||||
|
margin-top: .5rem;
|
||||||
|
justify-content: end;
|
||||||
|
|
||||||
|
.captcha {
|
||||||
|
border: 1px solid var(--td-border-level-1-color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.sms-code {
|
||||||
|
.send {
|
||||||
|
height: 1.5rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.exec {
|
||||||
|
display: flex;
|
||||||
|
margin-top: .5rem;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
.button {
|
||||||
|
width: 10rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
:global(html.theme-dark) {
|
||||||
|
.sms-login {
|
||||||
|
.captcha {
|
||||||
|
background: #0f172a;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
import { createRouter, createWebHistory, type RouteLocationNormalized } from "vue-router";
|
import { createRouter, createWebHistory, type RouteLocationNormalized } from "vue-router";
|
||||||
import RootLayout from "@/layout/RootLayout.vue";
|
import RootLayout from "@/layout/RootLayout.vue";
|
||||||
import MainLayout from "@/layout/MainLayout.vue";
|
import MainLayout from "@/layout/MainLayout.vue";
|
||||||
import Login from "@/pages/Login.vue";
|
import Login from "@/pages/user/LoginIndex.vue";
|
||||||
import InviteRegister from "@/pages/InviteRegister.vue";
|
import InviteRegister from "@/pages/InviteRegister.vue";
|
||||||
import chartRoutes from "@/router/routes/chartRoutes";
|
import chartRoutes from "@/router/routes/chartRoutes";
|
||||||
import customerRoutes from "@/router/routes/customerRoutes";
|
import customerRoutes from "@/router/routes/customerRoutes";
|
||||||
|
|||||||
Reference in New Issue
Block a user