mirror of
https://github.com/fantastic-admin/basic.git
synced 2026-10-07 13:17:52 +08:00
feat: 新增 FaForm 表单组件及示例,并更新登录注册相关页面
This commit is contained in:
@@ -1,8 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
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'
|
||||
|
||||
defineOptions({
|
||||
name: 'EditPasswordForm',
|
||||
@@ -12,24 +10,30 @@ const appAccountStore = useAppAccountStore()
|
||||
|
||||
const loading = ref(false)
|
||||
|
||||
const form = useForm({
|
||||
validationSchema: toTypedSchema(
|
||||
z.object({
|
||||
password: z.string().min(1, '请输入原密码'),
|
||||
newPassword: z.string().min(1, '请输入新密码').min(6, '密码长度为6到18位').max(18, '密码长度为6到18位'),
|
||||
checkPassword: z.string().min(1, '请确认新密码'),
|
||||
}).refine(data => data.newPassword === data.checkPassword, {
|
||||
message: '两次输入的密码不一致',
|
||||
path: ['checkPassword'],
|
||||
}),
|
||||
),
|
||||
initialValues: {
|
||||
password: '',
|
||||
newPassword: '',
|
||||
checkPassword: '',
|
||||
},
|
||||
interface EditPasswordModel {
|
||||
password: string
|
||||
newPassword: string
|
||||
checkPassword: string
|
||||
}
|
||||
|
||||
const model = ref<EditPasswordModel>({
|
||||
password: '',
|
||||
newPassword: '',
|
||||
checkPassword: '',
|
||||
})
|
||||
const onSubmit = form.handleSubmit((values) => {
|
||||
|
||||
const validationSchema = toTypedSchema(
|
||||
z.object({
|
||||
password: z.string().min(1, '请输入原密码'),
|
||||
newPassword: z.string().min(1, '请输入新密码').min(6, '密码长度为6到18位').max(18, '密码长度为6到18位'),
|
||||
checkPassword: z.string().min(1, '请确认新密码'),
|
||||
}).refine(data => data.newPassword === data.checkPassword, {
|
||||
message: '两次输入的密码不一致',
|
||||
path: ['checkPassword'],
|
||||
}),
|
||||
)
|
||||
|
||||
function onSubmit(values: EditPasswordModel) {
|
||||
loading.value = true
|
||||
appAccountStore.editPassword(values).then(async () => {
|
||||
useFaToast().success('模拟修改成功,请重新登录')
|
||||
@@ -37,7 +41,7 @@ const onSubmit = form.handleSubmit((values) => {
|
||||
}).finally(() => {
|
||||
loading.value = false
|
||||
})
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -50,52 +54,31 @@ const onSubmit = form.handleSubmit((values) => {
|
||||
请输入原密码、新密码和确认密码
|
||||
</p>
|
||||
</div>
|
||||
<form @submit="onSubmit">
|
||||
<FormField v-slot="{ componentField, errors }" name="password">
|
||||
<FormItem class="pb-6 relative space-y-0">
|
||||
<FormControl>
|
||||
<FaInput type="password" placeholder="原密码" class="w-full" :class="{ 'border-destructive': errors.length }" v-bind="componentField">
|
||||
<template #start>
|
||||
<FaIcon name="i-lucide:lock" />
|
||||
</template>
|
||||
</FaInput>
|
||||
</FormControl>
|
||||
<Transition enter-active-class="transition-opacity" enter-from-class="opacity-0" leave-active-class="transition-opacity" leave-to-class="opacity-0">
|
||||
<FormMessage class="text-xs m-0 bottom-1 absolute" />
|
||||
</Transition>
|
||||
</FormItem>
|
||||
</FormField>
|
||||
<FormField v-slot="{ componentField, errors }" name="newPassword">
|
||||
<FormItem class="pb-6 relative space-y-0">
|
||||
<FormControl>
|
||||
<FaInput type="password" placeholder="新密码" class="w-full" :class="{ 'border-destructive': errors.length }" v-bind="componentField">
|
||||
<template #start>
|
||||
<FaIcon name="i-lucide:lock" />
|
||||
</template>
|
||||
</FaInput>
|
||||
</FormControl>
|
||||
<Transition enter-active-class="transition-opacity" enter-from-class="opacity-0" leave-active-class="transition-opacity" leave-to-class="opacity-0">
|
||||
<FormMessage class="text-xs m-0 bottom-1 absolute" />
|
||||
</Transition>
|
||||
</FormItem>
|
||||
</FormField>
|
||||
<FormField v-slot="{ componentField, errors }" name="checkPassword">
|
||||
<FormItem class="pb-6 relative space-y-0">
|
||||
<FormControl>
|
||||
<FaInput type="password" placeholder="确认密码" class="w-full" :class="{ 'border-destructive': errors.length }" v-bind="componentField">
|
||||
<template #start>
|
||||
<FaIcon name="i-lucide:lock" />
|
||||
</template>
|
||||
</FaInput>
|
||||
</FormControl>
|
||||
<Transition enter-active-class="transition-opacity" enter-from-class="opacity-0" leave-active-class="transition-opacity" leave-to-class="opacity-0">
|
||||
<FormMessage class="text-xs m-0 bottom-1 absolute" />
|
||||
</Transition>
|
||||
</FormItem>
|
||||
</FormField>
|
||||
<FaForm :model="model" :validation-schema="validationSchema" @submit="onSubmit">
|
||||
<FaFormItem name="password">
|
||||
<FaInput type="password" placeholder="原密码" class="w-full">
|
||||
<template #start>
|
||||
<FaIcon name="i-lucide:lock" />
|
||||
</template>
|
||||
</FaInput>
|
||||
</FaFormItem>
|
||||
<FaFormItem name="newPassword">
|
||||
<FaInput type="password" placeholder="新密码" class="w-full">
|
||||
<template #start>
|
||||
<FaIcon name="i-lucide:lock" />
|
||||
</template>
|
||||
</FaInput>
|
||||
</FaFormItem>
|
||||
<FaFormItem name="checkPassword">
|
||||
<FaInput type="password" placeholder="确认密码" class="w-full">
|
||||
<template #start>
|
||||
<FaIcon name="i-lucide:lock" />
|
||||
</template>
|
||||
</FaInput>
|
||||
</FaFormItem>
|
||||
<FaButton :loading="loading" size="lg" class="mt-8 w-full" type="submit">
|
||||
保存
|
||||
</FaButton>
|
||||
</form>
|
||||
</FaForm>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
<script setup lang="ts">
|
||||
import type { FormExpose } from '@fantastic-admin/components'
|
||||
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'
|
||||
|
||||
defineOptions({
|
||||
name: 'LoginForm',
|
||||
@@ -26,19 +25,27 @@ const loading = ref(false)
|
||||
// 登录方式,default 账号密码登录,qrcode 扫码登录
|
||||
const type = ref<'default' | 'qrcode'>('default')
|
||||
|
||||
const form = useForm({
|
||||
validationSchema: toTypedSchema(z.object({
|
||||
account: z.string().min(1, '请输入用户名'),
|
||||
password: z.string().min(1, '请输入密码'),
|
||||
remember: z.boolean(),
|
||||
})),
|
||||
initialValues: {
|
||||
account: props.account ?? localStorage.getItem('login_account') ?? '',
|
||||
password: '',
|
||||
remember: localStorage.getItem('login_account') !== null,
|
||||
},
|
||||
interface LoginModel {
|
||||
account: string
|
||||
password: string
|
||||
remember: boolean
|
||||
}
|
||||
|
||||
const formRef = useTemplateRef<FormExpose<LoginModel>>('formRef')
|
||||
|
||||
const model = ref<LoginModel>({
|
||||
account: props.account ?? localStorage.getItem('login_account') ?? '',
|
||||
password: '',
|
||||
remember: localStorage.getItem('login_account') !== null,
|
||||
})
|
||||
const onSubmit = form.handleSubmit((values) => {
|
||||
|
||||
const validationSchema = toTypedSchema(z.object({
|
||||
account: z.string().min(1, '请输入用户名'),
|
||||
password: z.string().min(1, '请输入密码'),
|
||||
remember: z.boolean(),
|
||||
}))
|
||||
|
||||
function onSubmit(values: LoginModel) {
|
||||
loading.value = true
|
||||
appAccountStore.login(values).then(() => {
|
||||
if (values.remember) {
|
||||
@@ -51,12 +58,12 @@ const onSubmit = form.handleSubmit((values) => {
|
||||
}).finally(() => {
|
||||
loading.value = false
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
function testAccount(account: string) {
|
||||
form.setFieldValue('account', account)
|
||||
form.setFieldValue('password', '123456')
|
||||
onSubmit()
|
||||
async function testAccount(account: string) {
|
||||
model.value.account = account
|
||||
model.value.password = '123456'
|
||||
await formRef.value?.submit()
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -79,48 +86,33 @@ function testAccount(account: string) {
|
||||
/>
|
||||
</div>
|
||||
<div v-show="type === 'default'">
|
||||
<form @submit="onSubmit">
|
||||
<FormField v-slot="{ componentField, errors }" name="account">
|
||||
<FormItem class="pb-6 relative space-y-0">
|
||||
<FormControl>
|
||||
<FaInput type="text" placeholder="用户名" class="w-full" :class="{ 'border-destructive': errors.length }" v-bind="componentField">
|
||||
<template #start>
|
||||
<FaIcon name="i-lucide:user" />
|
||||
</template>
|
||||
</FaInput>
|
||||
</FormControl>
|
||||
<Transition enter-active-class="transition-opacity" enter-from-class="opacity-0" leave-active-class="transition-opacity" leave-to-class="opacity-0">
|
||||
<FormMessage class="text-xs m-0 bottom-1 absolute" />
|
||||
</Transition>
|
||||
</FormItem>
|
||||
</FormField>
|
||||
<FormField v-slot="{ componentField, errors }" name="password">
|
||||
<FormItem class="pb-6 relative space-y-0">
|
||||
<FormControl>
|
||||
<FaInput type="password" placeholder="密码" class="w-full" :class="{ 'border-destructive': errors.length }" v-bind="componentField">
|
||||
<template #start>
|
||||
<FaIcon name="i-lucide:lock" />
|
||||
</template>
|
||||
</FaInput>
|
||||
</FormControl>
|
||||
<Transition enter-active-class="transition-opacity" enter-from-class="opacity-0" leave-active-class="transition-opacity" leave-to-class="opacity-0">
|
||||
<FormMessage class="text-xs m-0 bottom-1 absolute" />
|
||||
</Transition>
|
||||
</FormItem>
|
||||
</FormField>
|
||||
<div class="mb-4 flex-center-between">
|
||||
<div class="flex-center-start">
|
||||
<FormField v-slot="{ componentField }" type="checkbox" name="remember">
|
||||
<FormItem>
|
||||
<FormControl>
|
||||
<FaCheckbox :model-value="componentField.modelValue" @update:model-value="componentField['onUpdate:modelValue']?.($event)">
|
||||
记住我
|
||||
</FaCheckbox>
|
||||
</FormControl>
|
||||
</FormItem>
|
||||
</FormField>
|
||||
</div>
|
||||
<FaButton variant="link" class="p-0 h-auto" type="button" @click="emits('onResetPassword', form.values.account)">
|
||||
<FaForm
|
||||
ref="formRef"
|
||||
:model="model"
|
||||
:validation-schema="validationSchema"
|
||||
@submit="onSubmit"
|
||||
>
|
||||
<FaFormItem name="account">
|
||||
<FaInput type="text" placeholder="用户名" class="w-full">
|
||||
<template #start>
|
||||
<FaIcon name="i-lucide:user" />
|
||||
</template>
|
||||
</FaInput>
|
||||
</FaFormItem>
|
||||
<FaFormItem name="password">
|
||||
<FaInput type="password" placeholder="密码" class="w-full">
|
||||
<template #start>
|
||||
<FaIcon name="i-lucide:lock" />
|
||||
</template>
|
||||
</FaInput>
|
||||
</FaFormItem>
|
||||
<div class="flex-start-between">
|
||||
<FaFormItem name="remember" class="min-w-0">
|
||||
<FaCheckbox>
|
||||
记住我
|
||||
</FaCheckbox>
|
||||
</FaFormItem>
|
||||
<FaButton variant="link" class="p-0 h-auto" type="button" @click="emits('onResetPassword', model.account)">
|
||||
忘记密码了?
|
||||
</FaButton>
|
||||
</div>
|
||||
@@ -129,11 +121,11 @@ function testAccount(account: string) {
|
||||
</FaButton>
|
||||
<div class="text-sm mt-4 flex-center gap-2">
|
||||
<span class="text-secondary-foreground op-50">还没有帐号?</span>
|
||||
<FaButton variant="link" class="p-0 h-auto" type="button" @click="emits('onRegister', form.values.account)">
|
||||
<FaButton variant="link" class="p-0 h-auto" type="button" @click="emits('onRegister', model.account)">
|
||||
注册新帐号
|
||||
</FaButton>
|
||||
</div>
|
||||
</form>
|
||||
</FaForm>
|
||||
<div class="mt-4 text-center -mb-4">
|
||||
<FaDivider>演示账号一键登录</FaDivider>
|
||||
<div class="space-x-2">
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import { toTypedSchema } from '@vee-validate/zod'
|
||||
import { useForm } from 'vee-validate'
|
||||
import * as z from 'zod'
|
||||
import { FormControl, FormDescription, FormField, FormItem, FormMessage } from '@/ui/shadcn/ui/form'
|
||||
|
||||
defineOptions({
|
||||
name: 'RegisterForm',
|
||||
@@ -19,32 +17,38 @@ const emits = defineEmits<{
|
||||
|
||||
const loading = ref(false)
|
||||
|
||||
const form = useForm({
|
||||
validationSchema: toTypedSchema(
|
||||
z.object({
|
||||
account: z.string().min(1, '请输入用户名'),
|
||||
password: z.string().min(1, '请输入密码').min(6, '密码长度为6到18位').max(18, '密码长度为6到18位'),
|
||||
checkPassword: z.string().min(1, '请再次输入密码'),
|
||||
}).refine(data => data.password === data.checkPassword, {
|
||||
message: '两次输入的密码不一致',
|
||||
path: ['checkPassword'],
|
||||
}),
|
||||
),
|
||||
initialValues: {
|
||||
account: props.account ?? '',
|
||||
password: '',
|
||||
checkPassword: '',
|
||||
},
|
||||
interface RegisterModel {
|
||||
account: string
|
||||
password: string
|
||||
checkPassword: string
|
||||
}
|
||||
|
||||
const model = ref<RegisterModel>({
|
||||
account: props.account ?? '',
|
||||
password: '',
|
||||
checkPassword: '',
|
||||
})
|
||||
const onSubmit = form.handleSubmit((values) => {
|
||||
|
||||
const validationSchema = toTypedSchema(
|
||||
z.object({
|
||||
account: z.string().min(1, '请输入用户名'),
|
||||
password: z.string().min(1, '请输入密码').min(6, '密码长度为6到18位').max(18, '密码长度为6到18位'),
|
||||
checkPassword: z.string().min(1, '请再次输入密码'),
|
||||
}).refine(data => data.password === data.checkPassword, {
|
||||
message: '两次输入的密码不一致',
|
||||
path: ['checkPassword'],
|
||||
}),
|
||||
)
|
||||
|
||||
function onSubmit(values: RegisterModel) {
|
||||
loading.value = true
|
||||
emits('onRegister', values.account)
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="p-12 flex-col-stretch-center min-h-500px w-full">
|
||||
<form @submit="onSubmit">
|
||||
<FaForm :model="model" :validation-schema="validationSchema" @submit="onSubmit">
|
||||
<div class="mb-8 space-y-2">
|
||||
<h3 class="text-4xl font-bold">
|
||||
探索从这里开始 🚀
|
||||
@@ -53,60 +57,37 @@ const onSubmit = form.handleSubmit((values) => {
|
||||
演示系统未提供该功能
|
||||
</p>
|
||||
</div>
|
||||
<FormField v-slot="{ componentField, errors }" name="account">
|
||||
<FormItem class="pb-6 relative space-y-0">
|
||||
<FormControl>
|
||||
<FaInput type="text" placeholder="用户名" class="w-full" :class="{ 'border-destructive': errors.length }" v-bind="componentField">
|
||||
<template #start>
|
||||
<FaIcon name="i-lucide:user" />
|
||||
</template>
|
||||
</FaInput>
|
||||
</FormControl>
|
||||
<Transition enter-active-class="transition-opacity" enter-from-class="opacity-0" leave-active-class="transition-opacity" leave-to-class="opacity-0">
|
||||
<FormMessage class="text-xs m-0 bottom-1 absolute" />
|
||||
</Transition>
|
||||
</FormItem>
|
||||
</FormField>
|
||||
<FormField v-slot="{ componentField, value, errors }" name="password">
|
||||
<FormItem class="pb-6 relative space-y-0">
|
||||
<FormControl>
|
||||
<FaInput type="password" placeholder="密码" class="w-full" :class="{ 'border-destructive': errors.length }" v-bind="componentField">
|
||||
<template #start>
|
||||
<FaIcon name="i-lucide:lock" />
|
||||
</template>
|
||||
</FaInput>
|
||||
</FormControl>
|
||||
<FormDescription class="m-0">
|
||||
<FaPasswordStrength :password="value" />
|
||||
</FormDescription>
|
||||
<Transition enter-active-class="transition-opacity" enter-from-class="opacity-0" leave-active-class="transition-opacity" leave-to-class="opacity-0">
|
||||
<FormMessage class="text-xs m-0 bottom-1 absolute" />
|
||||
</Transition>
|
||||
</FormItem>
|
||||
</FormField>
|
||||
<FormField v-slot="{ componentField, errors }" name="checkPassword">
|
||||
<FormItem class="pb-6 relative space-y-0">
|
||||
<FormControl>
|
||||
<FaInput type="password" placeholder="确认密码" class="w-full" :class="{ 'border-destructive': errors.length }" v-bind="componentField">
|
||||
<template #start>
|
||||
<FaIcon name="i-lucide:lock" />
|
||||
</template>
|
||||
</FaInput>
|
||||
</FormControl>
|
||||
<Transition enter-active-class="transition-opacity" enter-from-class="opacity-0" leave-active-class="transition-opacity" leave-to-class="opacity-0">
|
||||
<FormMessage class="text-xs m-0 bottom-1 absolute" />
|
||||
</Transition>
|
||||
</FormItem>
|
||||
</FormField>
|
||||
<FaFormItem name="account">
|
||||
<FaInput type="text" placeholder="用户名" class="w-full">
|
||||
<template #start>
|
||||
<FaIcon name="i-lucide:user" />
|
||||
</template>
|
||||
</FaInput>
|
||||
</FaFormItem>
|
||||
<FaFormItem name="password">
|
||||
<FaInput type="password" placeholder="密码" class="w-full">
|
||||
<template #start>
|
||||
<FaIcon name="i-lucide:lock" />
|
||||
</template>
|
||||
</FaInput>
|
||||
<FaPasswordStrength :password="model.password" />
|
||||
</FaFormItem>
|
||||
<FaFormItem name="checkPassword">
|
||||
<FaInput type="password" placeholder="确认密码" class="w-full">
|
||||
<template #start>
|
||||
<FaIcon name="i-lucide:lock" />
|
||||
</template>
|
||||
</FaInput>
|
||||
</FaFormItem>
|
||||
<FaButton :loading="loading" size="lg" class="mt-4 w-full" type="submit">
|
||||
注册
|
||||
</FaButton>
|
||||
<div class="text-sm mt-4 flex-center gap-2">
|
||||
<span class="text-secondary-foreground op-50">已经有帐号?</span>
|
||||
<FaButton variant="link" class="p-0 h-auto" @click="emits('onLogin', form.values.account)">
|
||||
<FaButton variant="link" class="p-0 h-auto" type="button" @click="emits('onLogin', model.account)">
|
||||
去登录
|
||||
</FaButton>
|
||||
</div>
|
||||
</form>
|
||||
</FaForm>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
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'
|
||||
|
||||
defineOptions({
|
||||
name: 'ResetPasswordForm',
|
||||
@@ -19,22 +17,28 @@ const emits = defineEmits<{
|
||||
|
||||
const loading = ref(false)
|
||||
|
||||
const form = useForm({
|
||||
validationSchema: toTypedSchema(z.object({
|
||||
account: z.string().min(1, '请输入用户名'),
|
||||
captcha: z.string().min(6, '请输入验证码'),
|
||||
newPassword: z.string().min(1, '请输入新密码').min(6, '密码长度为6到18位').max(18, '密码长度为6到18位'),
|
||||
})),
|
||||
initialValues: {
|
||||
account: props.account ?? '',
|
||||
captcha: '',
|
||||
newPassword: '',
|
||||
},
|
||||
interface ResetPasswordModel {
|
||||
account: string
|
||||
captcha: string
|
||||
newPassword: string
|
||||
}
|
||||
|
||||
const model = ref<ResetPasswordModel>({
|
||||
account: props.account ?? '',
|
||||
captcha: '',
|
||||
newPassword: '',
|
||||
})
|
||||
const onSubmit = form.handleSubmit((values) => {
|
||||
|
||||
const validationSchema = toTypedSchema(z.object({
|
||||
account: z.string().min(1, '请输入用户名'),
|
||||
captcha: z.string().min(6, '请输入验证码'),
|
||||
newPassword: z.string().min(1, '请输入新密码').min(6, '密码长度为6到18位').max(18, '密码长度为6到18位'),
|
||||
}))
|
||||
|
||||
function onSubmit(values: ResetPasswordModel) {
|
||||
loading.value = true
|
||||
emits('onResetPassword', values.account)
|
||||
})
|
||||
}
|
||||
|
||||
const countdown = ref(0)
|
||||
const countdownInterval = ref(Number.NaN)
|
||||
@@ -51,7 +55,7 @@ function handleSendCaptcha() {
|
||||
|
||||
<template>
|
||||
<div class="p-12 flex-col-stretch-center min-h-500px w-full">
|
||||
<form @submit="onSubmit">
|
||||
<FaForm :model="model" :validation-schema="validationSchema" @submit="onSubmit">
|
||||
<div class="mb-8 space-y-2">
|
||||
<h3 class="text-4xl font-bold">
|
||||
忘记密码了? 🔒
|
||||
@@ -60,57 +64,41 @@ function handleSendCaptcha() {
|
||||
演示系统未提供该功能
|
||||
</p>
|
||||
</div>
|
||||
<FormField v-slot="{ componentField, errors }" name="account">
|
||||
<FormItem class="pb-6 relative space-y-0">
|
||||
<FormControl>
|
||||
<FaInput type="text" placeholder="用户名" class="w-full" :class="{ 'border-destructive': errors.length }" v-bind="componentField">
|
||||
<template #start>
|
||||
<FaIcon name="i-lucide:user" />
|
||||
</template>
|
||||
</FaInput>
|
||||
</FormControl>
|
||||
<Transition enter-active-class="transition-opacity" enter-from-class="opacity-0" leave-active-class="transition-opacity" leave-to-class="opacity-0">
|
||||
<FormMessage class="text-xs m-0 bottom-1 absolute" />
|
||||
</Transition>
|
||||
</FormItem>
|
||||
</FormField>
|
||||
<div class="flex-start-between gap-2">
|
||||
<FormField v-slot="{ componentField, value, setValue }" name="captcha">
|
||||
<FormItem class="pb-6 relative space-y-0">
|
||||
<FormControl>
|
||||
<FaInputOTP :model-value="value" :name="componentField.name" :length="6" class="border-destructive" @update:model-value="val => setValue(val)" />
|
||||
</FormControl>
|
||||
<Transition enter-active-class="transition-opacity" enter-from-class="opacity-0" leave-active-class="transition-opacity" leave-to-class="opacity-0">
|
||||
<FormMessage class="text-xs m-0 bottom-1 absolute" />
|
||||
</Transition>
|
||||
</FormItem>
|
||||
</FormField>
|
||||
<FaButton variant="outline" :disabled="countdown > 0" class="px-4 flex-1" @click="handleSendCaptcha">
|
||||
{{ countdown === 0 ? '发送验证码' : `${countdown} 秒后重发` }}
|
||||
</FaButton>
|
||||
</div>
|
||||
<FormField v-slot="{ componentField, errors }" name="newPassword">
|
||||
<FormItem class="pb-6 relative space-y-0">
|
||||
<FormControl>
|
||||
<FaInput type="password" placeholder="新密码" class="w-full" :class="{ 'border-destructive': errors.length }" v-bind="componentField">
|
||||
<template #start>
|
||||
<FaIcon name="i-lucide:lock" />
|
||||
</template>
|
||||
</FaInput>
|
||||
</FormControl>
|
||||
<Transition enter-active-class="transition-opacity" enter-from-class="opacity-0" leave-active-class="transition-opacity" leave-to-class="opacity-0">
|
||||
<FormMessage class="text-xs m-0 bottom-1 absolute" />
|
||||
</Transition>
|
||||
</FormItem>
|
||||
</FormField>
|
||||
<FaFormItem name="account">
|
||||
<FaInput type="text" placeholder="用户名" class="w-full">
|
||||
<template #start>
|
||||
<FaIcon name="i-lucide:user" />
|
||||
</template>
|
||||
</FaInput>
|
||||
</FaFormItem>
|
||||
<FaFormItem name="captcha" :auto-bind="false">
|
||||
<div class="flex-start-between gap-2">
|
||||
<FaInputOTP
|
||||
v-model="model.captcha"
|
||||
name="captcha"
|
||||
:length="6"
|
||||
class="border-destructive"
|
||||
/>
|
||||
<FaButton variant="outline" type="button" :disabled="countdown > 0" class="px-4 flex-1" @click="handleSendCaptcha">
|
||||
{{ countdown === 0 ? '发送验证码' : `${countdown} 秒后重发` }}
|
||||
</FaButton>
|
||||
</div>
|
||||
</FaFormItem>
|
||||
<FaFormItem name="newPassword">
|
||||
<FaInput type="password" placeholder="新密码" class="w-full">
|
||||
<template #start>
|
||||
<FaIcon name="i-lucide:lock" />
|
||||
</template>
|
||||
</FaInput>
|
||||
</FaFormItem>
|
||||
<FaButton :loading="loading" size="lg" class="mt-4 w-full" type="submit">
|
||||
确认
|
||||
</FaButton>
|
||||
<div class="text-sm mt-4 flex-center gap-2">
|
||||
<FaButton variant="link" class="p-0 h-auto" @click="emits('onLogin', form.values.account)">
|
||||
<FaButton variant="link" class="p-0 h-auto" type="button" @click="emits('onLogin', model.account)">
|
||||
去登录
|
||||
</FaButton>
|
||||
</div>
|
||||
</form>
|
||||
</FaForm>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -126,6 +126,14 @@ const routes: RouteRecordRaw = {
|
||||
copyright: false,
|
||||
},
|
||||
},
|
||||
{
|
||||
path: 'form',
|
||||
name: 'componentExampleForm',
|
||||
component: () => import('@/views/component_example/form.vue'),
|
||||
meta: {
|
||||
title: '表单',
|
||||
},
|
||||
},
|
||||
{
|
||||
path: 'hover_card',
|
||||
name: 'componentExampleHoverCard',
|
||||
|
||||
+4
@@ -35,6 +35,8 @@ declare module 'vue' {
|
||||
FaDropdown: typeof import('@fantastic-admin/components')['FaDropdown']
|
||||
FaFileUpload: typeof import('@fantastic-admin/components')['FaFileUpload']
|
||||
FaFixedBar: typeof import('@fantastic-admin/components')['FaFixedBar']
|
||||
FaForm: typeof import('@fantastic-admin/components')['FaForm']
|
||||
FaFormItem: typeof import('@fantastic-admin/components')['FaFormItem']
|
||||
FaHoverCard: typeof import('@fantastic-admin/components')['FaHoverCard']
|
||||
FaIcon: typeof import('@fantastic-admin/components')['FaIcon']
|
||||
FaImagePreview: typeof import('@fantastic-admin/components')['FaImagePreview']
|
||||
@@ -93,6 +95,8 @@ declare global {
|
||||
const FaDropdown: typeof import('@fantastic-admin/components')['FaDropdown']
|
||||
const FaFileUpload: typeof import('@fantastic-admin/components')['FaFileUpload']
|
||||
const FaFixedBar: typeof import('@fantastic-admin/components')['FaFixedBar']
|
||||
const FaForm: typeof import('@fantastic-admin/components')['FaForm']
|
||||
const FaFormItem: typeof import('@fantastic-admin/components')['FaFormItem']
|
||||
const FaHoverCard: typeof import('@fantastic-admin/components')['FaHoverCard']
|
||||
const FaIcon: typeof import('@fantastic-admin/components')['FaIcon']
|
||||
const FaImagePreview: typeof import('@fantastic-admin/components')['FaImagePreview']
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
<script setup lang="ts">
|
||||
import { form } from '@fantastic-admin/components/examples'
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<FaPageHeader title="表单" description="FaForm / FaFormItem" />
|
||||
<FaPageMain
|
||||
v-for="example in form"
|
||||
:key="example.title"
|
||||
:code="example.componentRaw"
|
||||
:title="example.title"
|
||||
>
|
||||
<component :is="example.component" />
|
||||
</FaPageMain>
|
||||
</div>
|
||||
</template>
|
||||
Reference in New Issue
Block a user