mirror of
				https://github.com/yangjian102621/geekai.git
				synced 2025-11-04 08:13:43 +08:00 
			
		
		
		
	feat(ui): 登录新增验证码及记住密码功能
This commit is contained in:
		@@ -33,3 +33,10 @@ export const loginLog = (params?: Record<string, unknown>) => {
 | 
			
		||||
    params
 | 
			
		||||
  })
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const captcha = () => {
 | 
			
		||||
  return http({
 | 
			
		||||
    url: "/api/admin/login/captcha",
 | 
			
		||||
    method: "get",
 | 
			
		||||
  });
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
@@ -1,24 +1,82 @@
 | 
			
		||||
<script lang="ts" setup>
 | 
			
		||||
import { reactive } from "vue";
 | 
			
		||||
import { useRoute } from "vue-router";
 | 
			
		||||
import { onMounted, reactive } from "vue";
 | 
			
		||||
import { useAuthStore } from "@/stores/auth";
 | 
			
		||||
import { captcha } from "@/http/login";
 | 
			
		||||
import useState from "@/composables/useState";
 | 
			
		||||
import useRequest from "@/composables/useRequest";
 | 
			
		||||
 | 
			
		||||
const route = useRoute();
 | 
			
		||||
const authStore = useAuthStore();
 | 
			
		||||
 | 
			
		||||
const [loginRequest, _, loading] = useRequest(authStore.login);
 | 
			
		||||
// 表单
 | 
			
		||||
function useFormData() {
 | 
			
		||||
  const formData = reactive({
 | 
			
		||||
    username: "",
 | 
			
		||||
    password: "",
 | 
			
		||||
    captcha: "",
 | 
			
		||||
  });
 | 
			
		||||
  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 };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
async function handleSubmit({ errors, values }: any) {
 | 
			
		||||
  if (errors) return;
 | 
			
		||||
  await loginRequest({
 | 
			
		||||
    ...values,
 | 
			
		||||
    ...route.query,
 | 
			
		||||
// 验证码
 | 
			
		||||
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>
 | 
			
		||||
<template>
 | 
			
		||||
@@ -38,29 +96,20 @@ async function handleSubmit({ errors, values }: any) {
 | 
			
		||||
            class="form"
 | 
			
		||||
            size="medium"
 | 
			
		||||
            auto-label-width
 | 
			
		||||
            :label-col-props="{ span: 0 }"
 | 
			
		||||
            :wrapper-col-props="{ span: 24 }"
 | 
			
		||||
            :rules="rules"
 | 
			
		||||
            @submit="handleSubmit"
 | 
			
		||||
          >
 | 
			
		||||
            <a-space direction="vertical" style="width: 100%">
 | 
			
		||||
              <a-form-item
 | 
			
		||||
                  field="username"
 | 
			
		||||
                  label="账号"
 | 
			
		||||
                  hide-label
 | 
			
		||||
                  hide-asterisk
 | 
			
		||||
                  :rules="[{ required: true, message: '请输入您的账号' }]"
 | 
			
		||||
              >
 | 
			
		||||
              <a-form-item field="username" label="账号">
 | 
			
		||||
                <a-input
 | 
			
		||||
                  v-model="formData.username"
 | 
			
		||||
                  placeholder="请输入您的账号"
 | 
			
		||||
                  class="input"
 | 
			
		||||
                ></a-input>
 | 
			
		||||
              </a-form-item>
 | 
			
		||||
              <a-form-item
 | 
			
		||||
                  field="password"
 | 
			
		||||
                  label="密码"
 | 
			
		||||
                  hide-label
 | 
			
		||||
                  hide-asterisk
 | 
			
		||||
                  :rules="[{ required: true, message: '请输入您的密码' }]"
 | 
			
		||||
              >
 | 
			
		||||
              <a-form-item field="password" label="密码">
 | 
			
		||||
                <a-input-password
 | 
			
		||||
                  v-model="formData.password"
 | 
			
		||||
                  placeholder="请输入您的密码"
 | 
			
		||||
@@ -68,9 +117,33 @@ async function handleSubmit({ errors, values }: any) {
 | 
			
		||||
                >
 | 
			
		||||
                </a-input-password>
 | 
			
		||||
              </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-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-form-item>
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
<script lang="ts" setup>
 | 
			
		||||
import { computed } from "vue";
 | 
			
		||||
import { computed, ref } from "vue";
 | 
			
		||||
import type { UploadInstance, FileItem } from "@arco-design/web-vue";
 | 
			
		||||
import { uploadUrl } from "@/http/config";
 | 
			
		||||
 | 
			
		||||
@@ -10,6 +10,8 @@ defineProps({
 | 
			
		||||
 | 
			
		||||
const emits = defineEmits(["update:modelValue"]);
 | 
			
		||||
 | 
			
		||||
const uploadRef = ref();
 | 
			
		||||
 | 
			
		||||
const uploadProps = computed<UploadInstance["$props"]>(() => ({
 | 
			
		||||
  action: uploadUrl,
 | 
			
		||||
  name: "file",
 | 
			
		||||
@@ -19,19 +21,23 @@ const uploadProps = computed<UploadInstance["$props"]>(() => ({
 | 
			
		||||
 | 
			
		||||
const handleChange = (_, file: FileItem) => {
 | 
			
		||||
  console.log(file.response);
 | 
			
		||||
  console.log(file);
 | 
			
		||||
};
 | 
			
		||||
</script>
 | 
			
		||||
<template>
 | 
			
		||||
  <a-space>
 | 
			
		||||
    <a-input-group>
 | 
			
		||||
      <a-input :model-value="modelValue" :placeholder="placeholder" readonly />
 | 
			
		||||
      <a-upload v-bind="uploadProps" @change="handleChange">
 | 
			
		||||
  <a-input-group style="width: 100%">
 | 
			
		||||
    <a-input
 | 
			
		||||
      :model-value="modelValue"
 | 
			
		||||
      :placeholder="placeholder"
 | 
			
		||||
      readonly
 | 
			
		||||
      @dblclick="uploadRef?.$el?.click()"
 | 
			
		||||
    />
 | 
			
		||||
    <a-upload ref="uploadRef" v-bind="uploadProps" @change="handleChange">
 | 
			
		||||
      <template #upload-button>
 | 
			
		||||
          <a-button type="primary">
 | 
			
		||||
        <a-button type="primary" style="width: 100px">
 | 
			
		||||
          <icon-cloud />
 | 
			
		||||
        </a-button>
 | 
			
		||||
      </template>
 | 
			
		||||
    </a-upload>
 | 
			
		||||
  </a-input-group>
 | 
			
		||||
  </a-space>
 | 
			
		||||
</template>
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user