王晓东 2 месяцев назад
Родитель
Сommit
b2fd647f6c
1 измененных файлов с 90 добавлено и 51 удалено
  1. 90 51
      src/components/AccountForm/LoginForm.vue

+ 90 - 51
src/components/AccountForm/LoginForm.vue

@@ -3,6 +3,7 @@ import { toTypedSchema } from '@vee-validate/zod'
 import { useForm } from 'vee-validate'
 import * as z from 'zod'
 import { FormControl, FormField, FormItem, FormMessage } from '@/ui/shadcn/ui/form'
+import { ref } from 'vue' // 确保导入 ref
 
 defineOptions({
   name: 'LoginForm',
@@ -26,11 +27,13 @@ const loading = ref(false)
 // 登录方式,default 账号密码登录,qrcode 扫码登录
 const type = ref<'default' | 'qrcode'>('default')
 
+const remember = ref(localStorage.getItem('remember') === '1')
+
 const form = useForm({
   validationSchema: toTypedSchema(z.object({
     account: z.string().min(1, '请输入用户名'),
     password: z.string().min(1, '请输入密码'),
-    remember: z.boolean(),
+    remember: z.boolean(), // 复选框是布尔值,无需必填验证
   })),
   initialValues: {
     account: props.account ?? localStorage.getItem('login_account') ?? '',
@@ -38,32 +41,41 @@ const form = useForm({
     remember: !!localStorage.getItem('login_account'),
   },
 })
+
 const onSubmit = form.handleSubmit((values) => {
   loading.value = true
-  userStore.login(values).then(() => {
-    if (values.remember) {
-      localStorage.setItem('login_account', values.account)
-    }
-    else {
-      localStorage.removeItem('login_account')
-    }
-    emits('onLogin', values.account)
-  }).finally(() => {
-    loading.value = false
-  })
+  userStore.login(values)
+    .then(() => {
+      if (remember.value) {
+        localStorage.setItem('login_account', values.account)
+        localStorage.setItem('remember', '1')
+      } else {
+        localStorage.removeItem('login_account')
+        localStorage.removeItem('remember')
+      }
+      emits('onLogin', values.account)
+    })
+    .catch((err) => {
+      console.log('登录失败:', err)
+      // 可以添加错误提示
+    })
+    .finally(() => {
+      loading.value = false
+    })
 })
 
 function testAccount(account: string) {
   form.setFieldValue('account', account)
   form.setFieldValue('password', '123456')
-  onSubmit()
+  // 修复:直接调用提交逻辑,而不是 onSubmit(需要传递事件)
+  form.handleSubmit((v) => onSubmit(v))()
 }
 </script>
 
 <template>
-  <div class="min-h-500px w-full flex-col-stretch-center p-12">
+  <div class="min-h-[500px] w-full flex flex-col items-center justify-center p-12">
     <div class="mb-6 space-y-2">
-      <h3 class="text-4xl color-[var(--el-text-color-primary)] font-bold">
+      <h3 class="text-4xl text-[var(--el-text-color-primary)] font-bold">
         欢迎使用 👋🏻
       </h3>
       <p class="text-sm text-muted-foreground lg:text-base">
@@ -72,76 +84,103 @@ function testAccount(account: string) {
     </div>
     <div class="mb-4">
       <FaTabs
-        v-model="type" :list="[
+        v-model="type"
+        :list="[
           { label: '账号密码登录', value: 'default' },
           // { label: '扫码登录', value: 'qrcode' },
-        ]" class="inline-flex"
+        ]"
+        class="inline-flex"
       />
     </div>
-    <div v-show="type === 'default'">
-      <form @submit="onSubmit">
+    <div v-show="type === 'default'" class="w-full max-w-md">
+      <!-- 修复:添加 @submit.prevent 阻止默认提交行为 -->
+      <form @submit.prevent="onSubmit">
         <FormField v-slot="{ componentField, errors }" name="account">
           <FormItem class="relative pb-6 space-y-0">
             <FormControl>
-              <FaInput type="text" placeholder="请输入用户名" class="w-full" :class="errors.length && 'border-destructive'" v-bind="componentField" />
+              <FaInput
+                type="text"
+                placeholder="请输入用户名"
+                class="w-full"
+                :class="errors.length ? 'border-destructive' : ''"
+                v-bind="componentField"
+              />
             </FormControl>
-            <Transition enter-active-class="transition-opacity" enter-from-class="opacity-0" leave-active-class="transition-opacity" leave-to-class="opacity-0">
-              <FormMessage class="absolute bottom-1 text-xs" />
+            <Transition
+              enter-active-class="transition-opacity"
+              enter-from-class="opacity-0"
+              leave-active-class="transition-opacity"
+              leave-to-class="opacity-0"
+            >
+              <FormMessage class="absolute bottom-1 text-xs" v-if="errors.length" />
             </Transition>
           </FormItem>
         </FormField>
+
         <FormField v-slot="{ componentField, errors }" name="password">
           <FormItem class="relative pb-6 space-y-0">
             <FormControl>
-              <FaInput type="password" placeholder="请输入密码" class="w-full" :class="errors.length && 'border-destructive'" v-bind="componentField" />
+              <FaInput
+                type="password"
+                placeholder="请输入密码"
+                class="w-full"
+                :class="errors.length ? 'border-destructive' : ''"
+                v-bind="componentField"
+              />
             </FormControl>
-            <Transition enter-active-class="transition-opacity" enter-from-class="opacity-0" leave-active-class="transition-opacity" leave-to-class="opacity-0">
-              <FormMessage class="absolute bottom-1 text-xs" />
+            <Transition
+              enter-active-class="transition-opacity"
+              enter-from-class="opacity-0"
+              leave-active-class="transition-opacity"
+              leave-to-class="opacity-0"
+            >
+              <FormMessage class="absolute bottom-1 text-xs" v-if="errors.length" />
             </Transition>
           </FormItem>
         </FormField>
-        <div class="mb-4 flex-center-between">
-          <div class="flex-center-start">
-            <FormField v-slot="{ componentField }" type="checkbox" name="remember">
-              <FormItem>
+
+        <div class="mb-4 flex items-center justify-between">
+          <div class="flex items-center">
+            <!-- 修复:复选框的正确绑定方式 -->
+            <FormField name="remember" v-slot="{ field }">
+              <FormItem class="flex items-center space-x-2">
                 <FormControl>
-                  <ElCheckbox v-bind="componentField">
+                  <ElCheckbox
+                    name="remember"
+                    v-model="remember"
+                  >
                     记住我
                   </ElCheckbox>
                 </FormControl>
               </FormItem>
             </FormField>
           </div>
-          <FaButton variant="link" class="h-auto p-0" type="button" @click="emits('onResetPassword', form.values.account)">
+          <FaButton
+            variant="link"
+            class="h-auto p-0"
+            type="button"
+            @click="emits('onResetPassword', form.values.account)"
+          >
             忘记密码了?
           </FaButton>
         </div>
-        <ElButton type="primary" size="large" native-type="submit" :loading="loading" class="w-full">
+
+        <ElButton
+          type="primary"
+          size="large"
+          native-type="submit"
+          :loading="loading"
+          class="w-full"
+        >
           登录
         </ElButton>
-        <!-- <div class="mt-4 flex-center gap-2 text-sm">
-          <span class="text-secondary-foreground op-50">还没有帐号?</span>
-          <FaButton variant="link" class="h-auto p-0" type="button" @click="emits('onRegister', form.values.account)">
-            注册新帐号
-          </FaButton>
-        </div> -->
       </form>
-      <!-- <div class="mt-4 text-center -mb-4">
-        <FaDivider>演示账号一键登录</FaDivider>
-        <div class="space-x-2">
-          <FaButton variant="default" size="sm" plain @click="testAccount('admin')">
-            admin
-          </FaButton>
-          <FaButton variant="outline" size="sm" plain @click="testAccount('test')">
-            test
-          </FaButton>
-        </div>
-      </div> -->
     </div>
+
     <!-- <div v-show="type === 'qrcode'">
-      <div class="flex-col-center">
+      <div class="flex flex-col items-center">
         <img src="https://s2.loli.net/2024/04/26/GsahtuIZ9XOg5jr.png" class="h-[250px] w-[250px]">
-        <div class="mt-2 text-sm text-secondary-foreground op-50">
+        <div class="mt-2 text-sm text-secondary-foreground opacity-50">
           请使用微信扫码登录
         </div>
       </div>