laobinghu ce208df092 feat: 实现 Nuxt4 + Nuxt UI 博客前端完整功能
核心功能:
- 项目初始化 (Nuxt 4 + Nuxt UI + Pinia + ofetch)
- TypeScript 类型定义 (User, Article, Comment, API 响应)
- 认证系统 (登录/登出、Cookie 支持、权限中间件)
- 文章列表页 (筛选、分页、响应式布局)
- 文章详情页 (Markdown 渲染、评论系统)
- 文章编辑器 (左右分栏、实时预览、Markdown 工具栏)

管理后台:
- 侧边栏布局、权限检查
- 数据分析 (数据统计卡片、热门文章、评论审核统计)
- 文章管理 (表格、筛选、删除)
- 评论管理 (审核通过/拒绝、删除)
- 用户管理 (角色管理、删除)

全局组件:
- 导航栏 (暗色模式切换、移动端菜单)
- 页脚
- 403/404 错误页

配置文件:
- .env.example 环境变量模板
- nuxt.config.ts 完整配置
- 自定义 CSS 样式
2026-03-28 15:56:50 +08:00

108 lines
2.5 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<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>