4 changed files with 190 additions and 29 deletions
@ -0,0 +1,73 @@ |
|||
<template> |
|||
<UApp> |
|||
<div class="min-h-screen bg-default text-default flex flex-col"> |
|||
<header class="border-b border-default"> |
|||
<UContainer class="h-14 flex items-center justify-between"> |
|||
<div class="flex items-center gap-2"> |
|||
<NuxtLink to="/" class="font-semibold tracking-tight"> |
|||
BigHouse |
|||
</NuxtLink> |
|||
<UDropdownMenu :items="menuItems" :content="{ align: 'end' }"> |
|||
<UButton color="neutral" variant="ghost" label="菜单" icon="i-lucide-menu" /> |
|||
</UDropdownMenu> |
|||
</div> |
|||
</UContainer> |
|||
</header> |
|||
|
|||
<main class="flex-1"> |
|||
<UContainer class="py-8"> |
|||
<NuxtPage /> |
|||
</UContainer> |
|||
</main> |
|||
|
|||
<footer class="border-t border-default"> |
|||
<UContainer class="h-12 flex items-center text-sm text-muted"> |
|||
Built with Nuxt + Nuxt UI & BigHouse |
|||
</UContainer> |
|||
</footer> |
|||
</div> |
|||
</UApp> |
|||
</template> |
|||
|
|||
<script setup lang="ts"> |
|||
const menuItems = [ |
|||
[ |
|||
{ |
|||
label: "首页", |
|||
icon: "i-lucide-house", |
|||
to: "/", |
|||
}, |
|||
{ |
|||
label: "文档", |
|||
icon: "i-lucide-book-open", |
|||
children: [ |
|||
{ |
|||
label: "Nuxt", |
|||
to: "https://nuxt.com/docs", |
|||
target: "_blank", |
|||
}, |
|||
{ |
|||
label: "Nuxt UI", |
|||
to: "https://ui.nuxt.com/getting-started", |
|||
target: "_blank", |
|||
}, |
|||
], |
|||
}, |
|||
{ |
|||
label: "示例", |
|||
icon: "i-lucide-layout-grid", |
|||
children: [ |
|||
{ |
|||
label: "Hello API", |
|||
to: "/api/hello", |
|||
target: "_blank", |
|||
}, |
|||
{ |
|||
label: "首页页面", |
|||
to: "/index", |
|||
}, |
|||
], |
|||
}, |
|||
], |
|||
] |
|||
</script> |
|||
@ -0,0 +1,113 @@ |
|||
<script setup lang="ts"> |
|||
import type { FormError, FormSubmitEvent } from '@nuxt/ui' |
|||
|
|||
definePageMeta({ |
|||
title: '登录', |
|||
layout: 'not-login', |
|||
}) |
|||
|
|||
type LoginFormState = { |
|||
email: string |
|||
password: string |
|||
} |
|||
|
|||
const state = reactive<LoginFormState>({ |
|||
email: '', |
|||
password: '', |
|||
}) |
|||
|
|||
const loading = ref(false) |
|||
const resultType = ref<'success' | 'error' | ''>('') |
|||
const resultMessage = ref('') |
|||
|
|||
const validate = (formState: LoginFormState): FormError[] => { |
|||
const errors: FormError[] = [] |
|||
|
|||
if (!formState.email) { |
|||
errors.push({ name: 'email', message: '请输入邮箱' }) |
|||
} else if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(formState.email)) { |
|||
errors.push({ name: 'email', message: '邮箱格式不正确' }) |
|||
} |
|||
|
|||
if (!formState.password) { |
|||
errors.push({ name: 'password', message: '请输入密码' }) |
|||
} else if (formState.password.length < 6) { |
|||
errors.push({ name: 'password', message: '密码至少 6 位' }) |
|||
} |
|||
|
|||
return errors |
|||
} |
|||
|
|||
const onSubmit = async (_event: FormSubmitEvent<LoginFormState>) => { |
|||
resultType.value = '' |
|||
resultMessage.value = '' |
|||
loading.value = true |
|||
|
|||
try { |
|||
await new Promise((resolve) => setTimeout(resolve, 800)) |
|||
resultType.value = 'success' |
|||
resultMessage.value = `登录成功,欢迎 ${state.email}` |
|||
} catch { |
|||
resultType.value = 'error' |
|||
resultMessage.value = '登录失败,请稍后重试' |
|||
} finally { |
|||
loading.value = false |
|||
} |
|||
} |
|||
</script> |
|||
|
|||
<template> |
|||
<div class="max-w-md mx-auto py-10"> |
|||
<UCard> |
|||
<template #header> |
|||
<div class="space-y-1"> |
|||
<h1 class="text-xl font-semibold">欢迎登录</h1> |
|||
<p class="text-sm text-muted">请输入邮箱和密码继续</p> |
|||
</div> |
|||
</template> |
|||
|
|||
<UForm :state="state" :validate="validate" class="space-y-4" @submit="onSubmit"> |
|||
<UFormField label="邮箱" name="email" required> |
|||
<UInput |
|||
v-model="state.email" |
|||
type="email" |
|||
placeholder="you@example.com" |
|||
autocomplete="email" |
|||
class="w-full" |
|||
/> |
|||
</UFormField> |
|||
|
|||
<UFormField label="密码" name="password" required> |
|||
<UInput |
|||
v-model="state.password" |
|||
type="password" |
|||
placeholder="请输入密码" |
|||
autocomplete="current-password" |
|||
class="w-full" |
|||
/> |
|||
</UFormField> |
|||
|
|||
<UButton type="submit" block :loading="loading"> |
|||
立即登录 |
|||
</UButton> |
|||
</UForm> |
|||
|
|||
<UAlert |
|||
v-if="resultType" |
|||
:color="resultType === 'success' ? 'success' : 'error'" |
|||
:title="resultType === 'success' ? '操作成功' : '操作失败'" |
|||
:description="resultMessage" |
|||
class="mt-4" |
|||
/> |
|||
|
|||
<div class="mt-4 flex items-center justify-between text-sm"> |
|||
<NuxtLink to="/register" class="text-primary hover:underline"> |
|||
去注册 |
|||
</NuxtLink> |
|||
<NuxtLink to="/forgot-password" class="text-muted hover:underline"> |
|||
忘记密码? |
|||
</NuxtLink> |
|||
</div> |
|||
</UCard> |
|||
</div> |
|||
</template> |
|||
Loading…
Reference in new issue