feat(ui): 登录新增验证码及记住密码功能

This commit is contained in:
廖彦棋 2024-03-11 10:49:13 +08:00
parent bc21a1d443
commit 2e7b75affb
3 changed files with 143 additions and 57 deletions

View File

@ -32,4 +32,11 @@ export const loginLog = (params?: Record<string, unknown>) => {
method: "get", method: "get",
params params
}) })
} }
export const captcha = () => {
return http({
url: "/api/admin/login/captcha",
method: "get",
});
};

View File

@ -1,24 +1,82 @@
<script lang="ts" setup> <script lang="ts" setup>
import { reactive } from "vue"; import { onMounted, reactive } from "vue";
import { useRoute } from "vue-router";
import { useAuthStore } from "@/stores/auth"; import { useAuthStore } from "@/stores/auth";
import { captcha } from "@/http/login";
import useState from "@/composables/useState";
import useRequest from "@/composables/useRequest"; import useRequest from "@/composables/useRequest";
const route = useRoute(); //
const authStore = useAuthStore(); function useFormData() {
const formData = reactive({
const [loginRequest, _, loading] = useRequest(authStore.login); username: "",
const formData = reactive({ password: "",
username: "", captcha: "",
password: "",
});
async function handleSubmit({ errors, values }: any) {
if (errors) return;
await loginRequest({
...values,
...route.query,
}); });
const rules = {
username: [{ required: true, message: "请输入您的账号" }],
password: [{ required: true, message: "请输入您的密码" }],
captcha: [{ required: true, message: "请输入验证码" }],
};
const authStore = useAuthStore();
const [loginRequest, _, submitting] = useRequest(authStore.login);
return { formData, loginRequest, submitting, rules };
}
//
function useCaptcha() {
const captchaImage = reactive({
pic_path: "",
captcha_id: "",
});
const getCaptchaImage = async () => {
const { data } = await captcha();
Object.assign(captchaImage, data);
};
onMounted(getCaptchaImage);
return { captchaImage, getCaptchaImage };
}
//
function useRemeberPWD(formData) {
const storageKey = "r-f";
const [isRemember, setIsRemember] = useState(false);
const onIsRememberChange = (v) => {
if (v) {
const value = {
username: formData.username,
password: formData.password,
};
localStorage.setItem(storageKey, JSON.stringify(value));
} else {
localStorage.removeItem(storageKey);
}
setIsRemember(v);
};
onMounted(() => {
const getter = localStorage.getItem(storageKey);
if (getter) {
setIsRemember(true);
Object.assign(formData, JSON.parse(getter));
}
});
return { isRemember, onIsRememberChange };
}
const { formData, loginRequest, submitting, rules } = useFormData();
const { captchaImage, getCaptchaImage } = useCaptcha();
const { isRemember, onIsRememberChange } = useRemeberPWD(formData);
//
async function handleSubmit({ errors }: any) {
if (errors) return;
try {
await loginRequest({
...formData,
captcha_id: captchaImage.captcha_id,
});
} catch (err) {
getCaptchaImage();
}
} }
</script> </script>
<template> <template>
@ -33,44 +91,59 @@ async function handleSubmit({ errors, values }: any) {
<div class="form-box"> <div class="form-box">
<div class="title">ChatGPT Plus Admin</div> <div class="title">ChatGPT Plus Admin</div>
<a-form <a-form
ref="formRef" ref="formRef"
:model="formData" :model="formData"
class="form" class="form"
size="medium" size="medium"
auto-label-width auto-label-width
@submit="handleSubmit" :label-col-props="{ span: 0 }"
:wrapper-col-props="{ span: 24 }"
:rules="rules"
@submit="handleSubmit"
> >
<a-space direction="vertical" style="width: 100%"> <a-space direction="vertical" style="width: 100%">
<a-form-item <a-form-item field="username" label="账号">
field="username"
label="账号"
hide-label
hide-asterisk
:rules="[{ required: true, message: '请输入您的账号' }]"
>
<a-input <a-input
v-model="formData.username" v-model="formData.username"
placeholder="请输入您的账号" placeholder="请输入您的账号"
class="input" class="input"
></a-input> ></a-input>
</a-form-item> </a-form-item>
<a-form-item <a-form-item field="password" label="密码">
field="password"
label="密码"
hide-label
hide-asterisk
:rules="[{ required: true, message: '请输入您的密码' }]"
>
<a-input-password <a-input-password
v-model="formData.password" v-model="formData.password"
placeholder="请输入您的密码" placeholder="请输入您的密码"
class="input" class="input"
> >
</a-input-password> </a-input-password>
</a-form-item> </a-form-item>
<a-form-item field="captcha" label="验证码">
<a-input v-model="formData.captcha" placeholder="请输入验证码" class="input">
<template #append>
<img
class="captcha-image"
:src="captchaImage.pic_path"
alt="验证码"
title="点击刷新验证码"
@click="getCaptchaImage()"
/>
</template>
</a-input>
</a-form-item>
<a-form-item hide-label>
<a-checkbox :model-value="isRemember" @change="onIsRememberChange"
>记住密码</a-checkbox
>
</a-form-item>
</a-space> </a-space>
<a-form-item hide-label> <a-form-item hide-label>
<a-button :disabled="loading" html-type="submit" long type="primary" class="sign-in-btn"> <a-button
:loading="submitting"
html-type="submit"
long
type="primary"
class="sign-in-btn"
>
登录 登录
</a-button> </a-button>
</a-form-item> </a-form-item>

View File

@ -1,5 +1,5 @@
<script lang="ts" setup> <script lang="ts" setup>
import { computed } from "vue"; import { computed, ref } from "vue";
import type { UploadInstance, FileItem } from "@arco-design/web-vue"; import type { UploadInstance, FileItem } from "@arco-design/web-vue";
import { uploadUrl } from "@/http/config"; import { uploadUrl } from "@/http/config";
@ -10,6 +10,8 @@ defineProps({
const emits = defineEmits(["update:modelValue"]); const emits = defineEmits(["update:modelValue"]);
const uploadRef = ref();
const uploadProps = computed<UploadInstance["$props"]>(() => ({ const uploadProps = computed<UploadInstance["$props"]>(() => ({
action: uploadUrl, action: uploadUrl,
name: "file", name: "file",
@ -19,19 +21,23 @@ const uploadProps = computed<UploadInstance["$props"]>(() => ({
const handleChange = (_, file: FileItem) => { const handleChange = (_, file: FileItem) => {
console.log(file.response); console.log(file.response);
console.log(file);
}; };
</script> </script>
<template> <template>
<a-space> <a-input-group style="width: 100%">
<a-input-group> <a-input
<a-input :model-value="modelValue" :placeholder="placeholder" readonly /> :model-value="modelValue"
<a-upload v-bind="uploadProps" @change="handleChange"> :placeholder="placeholder"
<template #upload-button> readonly
<a-button type="primary"> @dblclick="uploadRef?.$el?.click()"
<icon-cloud /> />
</a-button> <a-upload ref="uploadRef" v-bind="uploadProps" @change="handleChange">
</template> <template #upload-button>
</a-upload> <a-button type="primary" style="width: 100px">
</a-input-group> <icon-cloud />
</a-space> </a-button>
</template>
</a-upload>
</a-input-group>
</template> </template>