设计前端页面和布局框架
This commit is contained in:
19
frontend/src/views/About.vue
Normal file
19
frontend/src/views/About.vue
Normal file
@@ -0,0 +1,19 @@
|
||||
<template>
|
||||
<div class="about">
|
||||
<nav class="glass fixed top-0 left-0 right-0 z-50">
|
||||
<div class="max-w-4xl mx-auto px-4 py-4">
|
||||
<RouterLink to="/" class="text-acg-pink hover:underline">← 返回首页</RouterLink>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<main class="pt-20 px-4 max-w-4xl mx-auto">
|
||||
<div class="glass rounded-xl p-8">
|
||||
<h1 class="text-3xl font-bold mb-4">关于 ACG Blog</h1>
|
||||
<p class="text-gray-600 dark:text-gray-400 leading-relaxed">
|
||||
ACG Blog 是一个专注于二次元文化的博客平台,使用 Vue 3 + TypeScript + Naive UI 构建。
|
||||
在这里,你可以分享你的二次元生活、动漫评论、游戏攻略等内容。
|
||||
</p>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
</template>
|
||||
25
frontend/src/views/Category.vue
Normal file
25
frontend/src/views/Category.vue
Normal file
@@ -0,0 +1,25 @@
|
||||
<script setup lang="ts">
|
||||
import { useRoute } from 'vue-router'
|
||||
|
||||
const route = useRoute()
|
||||
const categoryId = route.params.id as string
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="category">
|
||||
<nav class="glass fixed top-0 left-0 right-0 z-50">
|
||||
<div class="max-w-6xl mx-auto px-4 py-4">
|
||||
<RouterLink to="/" class="text-acg-pink hover:underline">← 返回首页</RouterLink>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<main class="pt-20 px-4 max-w-6xl mx-auto">
|
||||
<div class="glass rounded-xl p-8">
|
||||
<h1 class="text-2xl font-bold mb-4">分类 - {{ categoryId }}</h1>
|
||||
<div class="text-gray-600 dark:text-gray-400">
|
||||
分类内容加载中...
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
</template>
|
||||
243
frontend/src/views/Home.vue
Normal file
243
frontend/src/views/Home.vue
Normal file
@@ -0,0 +1,243 @@
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue'
|
||||
import Navbar from '@/components/Navbar.vue'
|
||||
import Hero from '@/components/Hero.vue'
|
||||
import PostCard from '@/components/PostCard.vue'
|
||||
import Sidebar from '@/components/Sidebar.vue'
|
||||
import Footer from '@/components/Footer.vue'
|
||||
import type { Post } from '@/types'
|
||||
|
||||
// 模拟文章数据 - 后端完成后替换为真实API调用
|
||||
const posts = ref<Post[]>([
|
||||
{
|
||||
id: '1',
|
||||
title: '《原神》4.2版本前瞻:芙宁娜技能演示与剧情预测',
|
||||
slug: 'genshin-4-2-preview',
|
||||
content: '',
|
||||
summary: '4.2版本即将到来,让我们提前了解水神芙宁娜的技能机制以及可能的剧情发展...',
|
||||
cover_image: 'https://images.unsplash.com/photo-1542751371-adc38448a05e?w=800',
|
||||
author: { id: '1', username: 'Miyako', email: '', is_active: true, created_at: '', updated_at: '' },
|
||||
category: { id: '1', name: '游戏攻略', slug: 'game', created_at: '' },
|
||||
tags: [{ id: '1', name: '原神', created_at: '' }, { id: '2', name: '攻略', created_at: '' }],
|
||||
view_count: 5200,
|
||||
status: 'published',
|
||||
created_at: '2024-03-15T10:00:00Z',
|
||||
updated_at: '2024-03-15T10:00:00Z'
|
||||
},
|
||||
{
|
||||
id: '2',
|
||||
title: '2024年必追的10部春季新番推荐',
|
||||
slug: 'spring-2024-anime',
|
||||
content: '',
|
||||
summary: '春天到了,又到了补番的季节!本期为大家带来2024年春季最值得期待的新番动画...',
|
||||
cover_image: 'https://images.unsplash.com/photo-1535016120720-40c6874c3b1c?w=800',
|
||||
author: { id: '2', username: 'Akari', email: '', is_active: true, created_at: '', updated_at: '' },
|
||||
category: { id: '2', name: '动漫资讯', slug: 'anime', created_at: '' },
|
||||
tags: [{ id: '3', name: '新番', created_at: '' }, { id: '4', name: '推荐', created_at: '' }],
|
||||
view_count: 3800,
|
||||
status: 'published',
|
||||
created_at: '2024-03-14T08:00:00Z',
|
||||
updated_at: '2024-03-14T08:00:00Z'
|
||||
},
|
||||
{
|
||||
id: '3',
|
||||
title: '《崩坏星穹铁道》角色强度榜 - 3月版',
|
||||
slug: 'honkai-star-rail-tier-list',
|
||||
content: '',
|
||||
summary: '版本强势角色有哪些?本期强度榜为你详细分析当前版本的T0、T1角色...',
|
||||
cover_image: 'https://images.unsplash.com/photo-1550745165-9bc0b252726f?w=800',
|
||||
author: { id: '1', username: 'Miyako', email: '', is_active: true, created_at: '', updated_at: '' },
|
||||
category: { id: '1', name: '游戏攻略', slug: 'game', created_at: '' },
|
||||
tags: [{ id: '2', name: '崩坏星穹铁道', created_at: '' }, { id: '5', name: '强度榜', created_at: '' }],
|
||||
view_count: 2900,
|
||||
status: 'published',
|
||||
created_at: '2024-03-13T15:30:00Z',
|
||||
updated_at: '2024-03-13T15:30:00Z'
|
||||
},
|
||||
{
|
||||
id: '4',
|
||||
title: '手办入坑指南:从萌新到进阶的全方位攻略',
|
||||
slug: 'figure-beginner-guide',
|
||||
content: '',
|
||||
summary: '想入手第一只手办但不知道如何选择?这篇指南将带你了解手办的各种分类、选购渠道...',
|
||||
cover_image: 'https://images.unsplash.com/photo-1608889175123-8ee362201f81?w=800',
|
||||
author: { id: '3', username: 'Sakura', email: '', is_active: true, created_at: '', updated_at: '' },
|
||||
category: { id: '3', name: '二次元美图', slug: 'pictures', created_at: '' },
|
||||
tags: [{ id: '6', name: '手办', created_at: '' }, { id: '7', name: '教程', created_at: '' }],
|
||||
view_count: 2100,
|
||||
status: 'published',
|
||||
created_at: '2024-03-12T12:00:00Z',
|
||||
updated_at: '2024-03-12T12:00:00Z'
|
||||
},
|
||||
{
|
||||
id: '5',
|
||||
title: '同人创作分享:用画笔描绘心中的二次元世界',
|
||||
slug: 'fanart-sharing',
|
||||
content: '',
|
||||
summary: '本期收录了多位优秀画师的作品,让我们一起欣赏这些充满想象力的同人创作...',
|
||||
cover_image: 'https://images.unsplash.com/photo-1618005182384-a83a8bd57fbe?w=800',
|
||||
author: { id: '2', username: 'Akari', email: '', is_active: true, created_at: '', updated_at: '' },
|
||||
category: { id: '4', name: '同人创作', slug: 'fanwork', created_at: '' },
|
||||
tags: [{ id: '8', name: '同人', created_at: '' }, { id: '9', name: '绘画', created_at: '' }],
|
||||
view_count: 1800,
|
||||
status: 'published',
|
||||
created_at: '2024-03-11T09:00:00Z',
|
||||
updated_at: '2024-03-11T09:00:00Z'
|
||||
},
|
||||
{
|
||||
id: '6',
|
||||
title: '《约定的梦幻岛》最新话解析:剧情走向深度分析',
|
||||
slug: 'neverland-analysis',
|
||||
content: '',
|
||||
summary: '最新一话的剧情信息量巨大,让我们来深度分析一下剧情走向和角色心理...',
|
||||
cover_image: 'https://images.unsplash.com/photo-1518709268805-4e9042af9f23?w=800',
|
||||
author: { id: '4', username: 'Ken', email: '', is_active: true, created_at: '', updated_at: '' },
|
||||
category: { id: '2', name: '动漫资讯', slug: 'anime', created_at: '' },
|
||||
tags: [{ id: '10', name: '约定的梦幻岛', created_at: '' }, { id: '11', name: '分析', created_at: '' }],
|
||||
view_count: 1500,
|
||||
status: 'published',
|
||||
created_at: '2024-03-10T20:00:00Z',
|
||||
updated_at: '2024-03-10T20:00:00Z'
|
||||
}
|
||||
])
|
||||
|
||||
const categories = [
|
||||
{ name: '全部', slug: '' },
|
||||
{ name: '动漫资讯', slug: 'anime' },
|
||||
{ name: '游戏攻略', slug: 'game' },
|
||||
{ name: '二次元美图', slug: 'pictures' },
|
||||
{ name: '同人创作', slug: 'fanwork' },
|
||||
]
|
||||
|
||||
const selectedCategory = ref('')
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="home">
|
||||
<Navbar />
|
||||
<Hero />
|
||||
|
||||
<main class="main-container">
|
||||
<!-- 分类筛选 -->
|
||||
<section class="category-section">
|
||||
<div class="category-buttons">
|
||||
<button
|
||||
v-for="cat in categories"
|
||||
:key="cat.slug"
|
||||
@click="selectedCategory = cat.slug"
|
||||
class="category-btn"
|
||||
:class="{ active: selectedCategory === cat.slug }"
|
||||
>
|
||||
{{ cat.name }}
|
||||
</button>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- 文章列表 + 侧边栏 -->
|
||||
<div class="content-layout">
|
||||
<!-- 文章列表 -->
|
||||
<section class="posts-section">
|
||||
<h2 class="section-title">最新文章</h2>
|
||||
<div class="posts-grid">
|
||||
<PostCard v-for="post in posts" :key="post.id" :post="post" />
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- 侧边栏 -->
|
||||
<Sidebar />
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<Footer />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.home {
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
.main-container {
|
||||
max-width: 1280px;
|
||||
margin: 0 auto;
|
||||
padding: 3rem 1rem;
|
||||
}
|
||||
|
||||
.category-section {
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.category-buttons {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: center;
|
||||
gap: 0.75rem;
|
||||
}
|
||||
|
||||
.category-btn {
|
||||
padding: 0.5rem 1.25rem;
|
||||
border-radius: 9999px;
|
||||
font-size: 0.875rem;
|
||||
font-weight: 500;
|
||||
color: #6B7280;
|
||||
background: #F3F4F6;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s;
|
||||
}
|
||||
:global(.dark) .category-btn {
|
||||
background: #374151;
|
||||
color: #9CA3AF;
|
||||
}
|
||||
.category-btn:hover {
|
||||
background: rgba(255, 183, 197, 0.3);
|
||||
color: #FFB7C5;
|
||||
}
|
||||
.category-btn.active {
|
||||
background: linear-gradient(135deg, #FFB7C5, #D4B5E6);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.content-layout {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 2rem;
|
||||
}
|
||||
@media (min-width: 1024px) {
|
||||
.content-layout {
|
||||
flex-direction: row;
|
||||
align-items: flex-start;
|
||||
}
|
||||
}
|
||||
|
||||
.posts-section {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.section-title {
|
||||
font-size: 1.5rem;
|
||||
font-weight: bold;
|
||||
margin-bottom: 1.5rem;
|
||||
background: linear-gradient(135deg, #FFB7C5, #D4B5E6);
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
background-clip: text;
|
||||
}
|
||||
|
||||
.posts-grid {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr;
|
||||
gap: 1.5rem;
|
||||
}
|
||||
@media (min-width: 640px) {
|
||||
.posts-grid {
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
}
|
||||
}
|
||||
@media (min-width: 1024px) {
|
||||
.posts-grid {
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
13
frontend/src/views/NotFound.vue
Normal file
13
frontend/src/views/NotFound.vue
Normal file
@@ -0,0 +1,13 @@
|
||||
<script setup lang="ts">
|
||||
import { RouterLink } from 'vue-router'
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="not-found min-h-screen flex items-center justify-center px-4">
|
||||
<div class="text-center">
|
||||
<h1 class="text-6xl font-bold text-acg-pink mb-4">404</h1>
|
||||
<p class="text-xl mb-6">页面不存在</p>
|
||||
<RouterLink to="/" class="btn-acg">返回首页</RouterLink>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
25
frontend/src/views/PostDetail.vue
Normal file
25
frontend/src/views/PostDetail.vue
Normal file
@@ -0,0 +1,25 @@
|
||||
<script setup lang="ts">
|
||||
import { useRoute } from 'vue-router'
|
||||
|
||||
const route = useRoute()
|
||||
const postId = route.params.id as string
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="post-detail">
|
||||
<nav class="glass fixed top-0 left-0 right-0 z-50">
|
||||
<div class="max-w-4xl mx-auto px-4 py-4">
|
||||
<RouterLink to="/" class="text-acg-pink hover:underline">← 返回首页</RouterLink>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<main class="pt-20 px-4 max-w-4xl mx-auto">
|
||||
<div class="glass rounded-xl p-8">
|
||||
<h1 class="text-3xl font-bold mb-4">文章详情 - {{ postId }}</h1>
|
||||
<div class="text-gray-600 dark:text-gray-400">
|
||||
文章内容加载中...
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
</template>
|
||||
8
frontend/src/views/admin/CategoryManage.vue
Normal file
8
frontend/src/views/admin/CategoryManage.vue
Normal file
@@ -0,0 +1,8 @@
|
||||
<template>
|
||||
<div class="category-manage">
|
||||
<h1 class="text-2xl font-bold mb-6">分类管理</h1>
|
||||
<div class="glass rounded-xl p-6">
|
||||
<p class="text-gray-600 dark:text-gray-400">分类管理功能开发中...</p>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
19
frontend/src/views/admin/Dashboard.vue
Normal file
19
frontend/src/views/admin/Dashboard.vue
Normal file
@@ -0,0 +1,19 @@
|
||||
<template>
|
||||
<div class="dashboard">
|
||||
<h1 class="text-2xl font-bold mb-6">仪表盘</h1>
|
||||
<div class="grid gap-4 md:grid-cols-3">
|
||||
<div class="glass rounded-xl p-6">
|
||||
<h3 class="text-gray-600 dark:text-gray-400">文章总数</h3>
|
||||
<p class="text-3xl font-bold text-acg-pink">0</p>
|
||||
</div>
|
||||
<div class="glass rounded-xl p-6">
|
||||
<h3 class="text-gray-600 dark:text-gray-400">用户总数</h3>
|
||||
<p class="text-3xl font-bold text-acg-blue">0</p>
|
||||
</div>
|
||||
<div class="glass rounded-xl p-6">
|
||||
<h3 class="text-gray-600 dark:text-gray-400">总访问量</h3>
|
||||
<p class="text-3xl font-bold text-acg-purple">0</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
8
frontend/src/views/admin/PostManage.vue
Normal file
8
frontend/src/views/admin/PostManage.vue
Normal file
@@ -0,0 +1,8 @@
|
||||
<template>
|
||||
<div class="post-manage">
|
||||
<h1 class="text-2xl font-bold mb-6">文章管理</h1>
|
||||
<div class="glass rounded-xl p-6">
|
||||
<p class="text-gray-600 dark:text-gray-400">文章管理功能开发中...</p>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
8
frontend/src/views/admin/TagManage.vue
Normal file
8
frontend/src/views/admin/TagManage.vue
Normal file
@@ -0,0 +1,8 @@
|
||||
<template>
|
||||
<div class="tag-manage">
|
||||
<h1 class="text-2xl font-bold mb-6">标签管理</h1>
|
||||
<div class="glass rounded-xl p-6">
|
||||
<p class="text-gray-600 dark:text-gray-400">标签管理功能开发中...</p>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
55
frontend/src/views/auth/Login.vue
Normal file
55
frontend/src/views/auth/Login.vue
Normal file
@@ -0,0 +1,55 @@
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
|
||||
const router = useRouter()
|
||||
const email = ref('')
|
||||
const password = ref('')
|
||||
|
||||
async function handleLogin() {
|
||||
// TODO: 实现登录逻辑
|
||||
console.log('Login:', email.value, password.value)
|
||||
router.push('/')
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="login min-h-screen flex items-center justify-center px-4">
|
||||
<div class="glass rounded-xl p-8 w-full max-w-md">
|
||||
<h1 class="text-2xl font-bold text-center mb-6">登录</h1>
|
||||
|
||||
<form @submit.prevent="handleLogin" class="space-y-4">
|
||||
<div>
|
||||
<label class="block text-sm font-medium mb-2">邮箱</label>
|
||||
<input
|
||||
v-model="email"
|
||||
type="email"
|
||||
placeholder="请输入邮箱"
|
||||
class="w-full px-4 py-2 rounded-lg border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-800 focus:outline-none focus:ring-2 focus:ring-acg-pink"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="block text-sm font-medium mb-2">密码</label>
|
||||
<input
|
||||
v-model="password"
|
||||
type="password"
|
||||
placeholder="请输入密码"
|
||||
class="w-full px-4 py-2 rounded-lg border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-800 focus:outline-none focus:ring-2 focus:ring-acg-pink"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
|
||||
<button type="submit" class="w-full btn-acg">
|
||||
登录
|
||||
</button>
|
||||
</form>
|
||||
|
||||
<p class="text-center mt-4 text-sm">
|
||||
还没有账号?
|
||||
<RouterLink to="/register" class="text-acg-pink hover:underline">立即注册</RouterLink>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
67
frontend/src/views/auth/Register.vue
Normal file
67
frontend/src/views/auth/Register.vue
Normal file
@@ -0,0 +1,67 @@
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
|
||||
const router = useRouter()
|
||||
const username = ref('')
|
||||
const email = ref('')
|
||||
const password = ref('')
|
||||
|
||||
async function handleRegister() {
|
||||
// TODO: 实现注册逻辑
|
||||
console.log('Register:', username.value, email.value, password.value)
|
||||
router.push('/login')
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="register min-h-screen flex items-center justify-center px-4">
|
||||
<div class="glass rounded-xl p-8 w-full max-w-md">
|
||||
<h1 class="text-2xl font-bold text-center mb-6">注册</h1>
|
||||
|
||||
<form @submit.prevent="handleRegister" class="space-y-4">
|
||||
<div>
|
||||
<label class="block text-sm font-medium mb-2">用户名</label>
|
||||
<input
|
||||
v-model="username"
|
||||
type="text"
|
||||
placeholder="请输入用户名"
|
||||
class="w-full px-4 py-2 rounded-lg border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-800 focus:outline-none focus:ring-2 focus:ring-acg-pink"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="block text-sm font-medium mb-2">邮箱</label>
|
||||
<input
|
||||
v-model="email"
|
||||
type="email"
|
||||
placeholder="请输入邮箱"
|
||||
class="w-full px-4 py-2 rounded-lg border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-800 focus:outline-none focus:ring-2 focus:ring-acg-pink"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="block text-sm font-medium mb-2">密码</label>
|
||||
<input
|
||||
v-model="password"
|
||||
type="password"
|
||||
placeholder="请输入密码"
|
||||
class="w-full px-4 py-2 rounded-lg border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-800 focus:outline-none focus:ring-2 focus:ring-acg-pink"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
|
||||
<button type="submit" class="w-full btn-acg">
|
||||
注册
|
||||
</button>
|
||||
</form>
|
||||
|
||||
<p class="text-center mt-4 text-sm">
|
||||
已有账号?
|
||||
<RouterLink to="/login" class="text-acg-pink hover:underline">立即登录</RouterLink>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
28
frontend/src/views/user/Profile.vue
Normal file
28
frontend/src/views/user/Profile.vue
Normal file
@@ -0,0 +1,28 @@
|
||||
<script setup lang="ts">
|
||||
import { useUserStore } from '@/store/user'
|
||||
|
||||
const userStore = useUserStore()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="profile">
|
||||
<nav class="glass fixed top-0 left-0 right-0 z-50">
|
||||
<div class="max-w-4xl mx-auto px-4 py-4">
|
||||
<RouterLink to="/" class="text-acg-pink hover:underline">← 返回首页</RouterLink>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<main class="pt-20 px-4 max-w-4xl mx-auto">
|
||||
<div class="glass rounded-xl p-8">
|
||||
<h1 class="text-2xl font-bold mb-4">个人中心</h1>
|
||||
<div v-if="userStore.user">
|
||||
<p>用户名: {{ userStore.user.username }}</p>
|
||||
<p>邮箱: {{ userStore.user.email }}</p>
|
||||
</div>
|
||||
<div v-else>
|
||||
加载中...
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
</template>
|
||||
Reference in New Issue
Block a user