核心功能: - 项目初始化 (Nuxt 4 + Nuxt UI + Pinia + ofetch) - TypeScript 类型定义 (User, Article, Comment, API 响应) - 认证系统 (登录/登出、Cookie 支持、权限中间件) - 文章列表页 (筛选、分页、响应式布局) - 文章详情页 (Markdown 渲染、评论系统) - 文章编辑器 (左右分栏、实时预览、Markdown 工具栏) 管理后台: - 侧边栏布局、权限检查 - 数据分析 (数据统计卡片、热门文章、评论审核统计) - 文章管理 (表格、筛选、删除) - 评论管理 (审核通过/拒绝、删除) - 用户管理 (角色管理、删除) 全局组件: - 导航栏 (暗色模式切换、移动端菜单) - 页脚 - 403/404 错误页 配置文件: - .env.example 环境变量模板 - nuxt.config.ts 完整配置 - 自定义 CSS 样式
108 lines
2.5 KiB
Vue
108 lines
2.5 KiB
Vue
<template>
|
||
<UCard class="max-w-md mx-auto">
|
||
<template #header>
|
||
<h2 class="text-2xl font-bold text-center text-gray-900 dark:text-white">
|
||
登录
|
||
</h2>
|
||
</template>
|
||
|
||
<UAlert
|
||
v-if="error"
|
||
:title="error"
|
||
color="red"
|
||
variant="subtle"
|
||
class="mb-4"
|
||
@close="error = null"
|
||
/>
|
||
|
||
<UForm :state="form" :validate="validate" @submit="onSubmit" class="space-y-4">
|
||
<UFormField label="邮箱/用户名" name="emailOrUsername" required>
|
||
<UInput
|
||
v-model="form.emailOrUsername"
|
||
placeholder="请输入邮箱或用户名"
|
||
autocomplete="username"
|
||
/>
|
||
</UFormField>
|
||
|
||
<UFormField label="密码" name="password" required>
|
||
<UInput
|
||
v-model="form.password"
|
||
type="password"
|
||
placeholder="请输入密码"
|
||
autocomplete="current-password"
|
||
/>
|
||
</UFormField>
|
||
|
||
<div class="flex items-center justify-between">
|
||
<UCheckbox v-model="form.remember" label="记住我" />
|
||
</div>
|
||
|
||
<UButton
|
||
type="submit"
|
||
color="indigo"
|
||
class="w-full"
|
||
:loading="isLoading"
|
||
:disabled="isLoading"
|
||
>
|
||
登录
|
||
</UButton>
|
||
</UForm>
|
||
|
||
<template #footer>
|
||
<p class="text-center text-sm text-gray-600 dark:text-gray-400">
|
||
还没有账号?请联系管理员
|
||
</p>
|
||
</template>
|
||
</UCard>
|
||
</template>
|
||
|
||
<script setup lang="ts">
|
||
import type { FormSubmitEvent } from '#ui/types'
|
||
|
||
definePageMeta({
|
||
layout: 'default',
|
||
middleware: (to) => {
|
||
const { isAuthenticated } = useAuth()
|
||
if (isAuthenticated.value) {
|
||
return navigateTo('/')
|
||
}
|
||
},
|
||
})
|
||
|
||
useSeoMeta({
|
||
title: '登录 - LinkShare Blog',
|
||
description: '登录到您的 LinkShare Blog 账号',
|
||
})
|
||
|
||
const { login, isLoading } = useAuth()
|
||
const error = ref<string | null>(null)
|
||
|
||
const form = ref({
|
||
emailOrUsername: '',
|
||
password: '',
|
||
remember: false,
|
||
})
|
||
|
||
const validate = (state: any) => {
|
||
const errors = []
|
||
if (!state.emailOrUsername) {
|
||
errors.push({ path: 'emailOrUsername', message: '请输入邮箱或用户名' })
|
||
}
|
||
if (!state.password) {
|
||
errors.push({ path: 'password', message: '请输入密码' })
|
||
} else if (state.password.length < 6) {
|
||
errors.push({ path: 'password', message: '密码至少需要 6 位' })
|
||
}
|
||
return errors
|
||
}
|
||
|
||
const onSubmit = async (event: FormSubmitEvent<any>) => {
|
||
try {
|
||
error.value = null
|
||
await login(form.value)
|
||
} catch (err: any) {
|
||
error.value = err.data?.message || '登录失败,请检查账号密码'
|
||
}
|
||
}
|
||
</script>
|