Skip to content

Commit ddf8dc5

Browse files
committed
update HomeView and PostDetailView
1 parent 9ff2b28 commit ddf8dc5

5 files changed

Lines changed: 269 additions & 327 deletions

File tree

client/package-lock.json

Lines changed: 28 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

client/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
"vue-router": "^4.5.0"
2121
},
2222
"devDependencies": {
23+
"@tailwindcss/typography": "^0.5.19",
2324
"@vitejs/plugin-vue": "^5.2.4",
2425
"autoprefixer": "^10.4.21",
2526
"postcss": "^8.5.3",

client/src/views/HomeView.vue

Lines changed: 95 additions & 119 deletions
Original file line numberDiff line numberDiff line change
@@ -1,108 +1,117 @@
11
<template>
2-
<div>
3-
<!-- 导航栏 -->
2+
<div class="min-h-screen bg-gray-50 dark:bg-[#0a0a0a] transition-colors duration-300">
43
<Navbar />
54

6-
<!-- 内容区域 -->
7-
<div class="container mx-auto px-4 py-8">
8-
<div class="flex flex-col md:flex-row">
9-
<!-- 左侧主内容区 -->
10-
<div class="w-full md:w-2/3 md:pr-6">
11-
<!-- 页面标题和写文章按钮 -->
12-
<div class="flex justify-between items-center mb-6">
13-
<h1 class="text-3xl font-bold text-gray-800">最新文章</h1>
14-
15-
<!-- 管理员显示写文章按钮 -->
5+
<main class="container mx-auto px-4 py-12 max-w-6xl">
6+
<section class="grid grid-cols-1 md:grid-cols-3 gap-4 mb-16 animate-fade-in-up">
7+
8+
<div class="md:col-span-2 bg-white dark:bg-[#111] border border-gray-200 dark:border-gray-800 rounded-2xl p-8 flex flex-col justify-center relative overflow-hidden group">
9+
<div class="relative z-10">
10+
<h1 class="text-4xl md:text-5xl font-extrabold text-gray-900 dark:text-white mb-4 tracking-tight">
11+
Hello, I'm <span class="text-blue-600 dark:text-blue-500">Myshkin</span>.
12+
</h1>
13+
<p class="text-lg text-gray-600 dark:text-gray-400 font-mono mb-6 max-w-lg">
14+
> Backend Developer<br>
15+
> History Enthusiast<br>
16+
> Building AI Agents...
17+
</p>
1618
<router-link
1719
v-if="isAdmin"
1820
to="/write"
19-
class="flex items-center bg-blue-600 hover:bg-blue-700 text-white px-4 py-2 rounded-md"
21+
class="inline-flex items-center px-5 py-2.5 bg-gray-900 dark:bg-white text-white dark:text-black font-medium rounded-lg hover:opacity-90 transition-opacity"
2022
>
21-
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 mr-1" fill="none" viewBox="0 0 24 24" stroke="currentColor">
22-
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z" />
23+
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4 mr-2" fill="none" viewBox="0 0 24 24" stroke="currentColor">
24+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 4v16m8-8H4" />
2325
</svg>
24-
写文章
26+
New Post
2527
</router-link>
2628
</div>
27-
28-
<!-- 加载状态 -->
29-
<div v-if="loading" class="flex justify-center items-center py-12">
30-
<div class="animate-spin rounded-full h-12 w-12 border-t-2 border-b-2 border-blue-500"></div>
29+
<div class="absolute right-0 top-0 w-64 h-64 bg-blue-500/10 rounded-full blur-3xl -mr-16 -mt-16 pointer-events-none"></div>
30+
</div>
31+
32+
<div class="bg-white dark:bg-[#111] border border-gray-200 dark:border-gray-800 rounded-2xl p-6 hover:border-blue-500/50 transition-colors">
33+
<h3 class="text-sm font-mono font-bold text-gray-500 uppercase mb-4">Explore Topics</h3>
34+
<div class="flex flex-wrap gap-2">
35+
<router-link
36+
v-for="cat in categories.slice(0, 5)"
37+
:key="cat.id"
38+
:to="`/categories/${cat.slug || cat.id}`"
39+
class="px-3 py-1 text-xs font-medium rounded-full bg-gray-100 dark:bg-gray-800 text-gray-700 dark:text-gray-300 hover:bg-blue-100 dark:hover:bg-blue-900/30 transition-colors"
40+
>
41+
{{ cat.name }}
42+
</router-link>
3143
</div>
32-
33-
<!-- 没有文章时显示 -->
34-
<div v-else-if="posts.length === 0" class="bg-gray-50 rounded-lg p-8 text-center">
35-
<p class="text-gray-600">暂无文章发布</p>
44+
</div>
45+
46+
<a href="https://github.com/your-github-id" target="_blank" class="bg-gray-900 dark:bg-blue-600 rounded-2xl p-6 text-white flex flex-col justify-between hover:scale-[1.02] transition-transform">
47+
<div class="flex justify-between items-start">
48+
<svg class="w-8 h-8 opacity-80" fill="currentColor" viewBox="0 0 24 24"><path d="M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z"/></svg>
49+
<svg class="w-5 h-5 opacity-50" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17 8l4 4m0 0l-4 4m4-4H3"></path></svg>
3650
</div>
37-
38-
<!-- 文章列表 -->
39-
<div v-else class="space-y-6">
40-
<ArticleCardV2
41-
v-for="post in posts"
42-
:key="post.id"
43-
:article="post"
44-
/>
51+
<div>
52+
<p class="font-bold text-lg">GitHub</p>
53+
<p class="text-sm opacity-70">View my projects</p>
4554
</div>
46-
47-
<!-- 分页 -->
55+
</a>
56+
</section>
57+
58+
<section>
59+
<div class="flex items-center justify-between mb-8">
60+
<h2 class="text-2xl font-bold text-gray-900 dark:text-white flex items-center">
61+
<span class="w-2 h-8 bg-blue-600 rounded-sm mr-3"></span>
62+
Latest Writings
63+
</h2>
64+
<div v-if="totalPages > 1" class="font-mono text-sm text-gray-500">
65+
Page {{ currentPage }} / {{ totalPages }}
66+
</div>
67+
</div>
68+
69+
<div v-if="loading" class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
70+
<div v-for="n in 6" :key="n" class="h-64 bg-gray-100 dark:bg-gray-800 rounded-xl animate-pulse"></div>
71+
</div>
72+
73+
<div v-else-if="posts.length === 0" class="py-20 text-center text-gray-500">
74+
<p class="text-xl">Nothing here yet.</p>
75+
</div>
76+
77+
<div v-else class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
78+
<ArticleCardV2
79+
v-for="post in posts"
80+
:key="post.id"
81+
:article="post"
82+
/>
83+
</div>
84+
85+
<div class="mt-16 flex justify-center">
4886
<Pagination
4987
v-if="posts.length > 0"
5088
:current-page="currentPage"
5189
:total-pages="totalPages"
5290
@page-change="handlePageChange"
5391
/>
5492
</div>
55-
56-
<!-- 右侧侧边栏 -->
57-
<!-- 在右侧边栏中使用这些组件 -->
58-
<div class="w-full md:w-1/3 mt-8 md:mt-0">
59-
<!-- 分类列表 -->
60-
<div class="mb-6">
61-
<CategoryList
62-
:categories="categories"
63-
:loading="loadingCategories"
64-
title="文章分类"
65-
/>
66-
</div>
67-
68-
<!-- 标签云 -->
69-
<div>
70-
<TagCloud
71-
:tags="tags"
72-
:loading="loadingTags"
73-
title="标签云"
74-
/>
75-
</div>
76-
</div>
77-
</div>
78-
</div>
79-
80-
<!-- 页脚 -->
93+
</section>
94+
</main>
95+
8196
<Footer />
8297
</div>
8398
</template>
8499

85100
<script setup>
86101
import { ref, onMounted } from 'vue';
87102
import Navbar from '../components/Navbar.vue';
88-
// import ArticleCard from '../components/ArticleCard.vue';
89-
import ArticleCardV2 from '../components/ArticleCardV2.vue'; // 引入新的
103+
import ArticleCardV2 from '../components/ArticleCardV2.vue'; // 使用新卡片
90104
import Pagination from '../components/Pagination.vue';
91105
import Footer from '../components/Footer.vue';
92-
import CategoryList from '../components/CategoryList.vue';
93-
import TagCloud from '../components/TagCloud.vue';
94106
import api from '../api';
95107
96108
// 状态
97109
const posts = ref([]);
98110
const loading = ref(true);
99111
const currentPage = ref(1);
100112
const totalPages = ref(1);
101-
const categories = ref([]);
102-
const tags = ref([]);
103-
const loadingCategories = ref(true);
104-
const loadingTags = ref(true);
105-
const isAdmin = ref(false); // 添加管理员状态检查
113+
const categories = ref([]); // 只需要分类,用于 Bento Grid
114+
const isAdmin = ref(false);
106115
107116
// 检查是否为管理员
108117
const checkAdminStatus = () => {
@@ -121,40 +130,19 @@ const checkAdminStatus = () => {
121130
const fetchPosts = async (page = 1) => {
122131
try {
123132
loading.value = true;
124-
const response = await api.getPosts({
125-
page,
126-
limit: 10
127-
});
128-
129-
console.log('文章API响应:', response);
133+
const response = await api.getPosts({ page, limit: 9 }); // Grid 布局每页 9 个比较好看
130134
131-
// 适应不同的API响应格式
135+
// 数据处理逻辑保持不变...
132136
if (Array.isArray(response)) {
133-
// 如果直接返回数组
134137
posts.value = response;
135-
currentPage.value = 1;
136-
totalPages.value = 1;
137138
} else if (response.posts) {
138-
// 如果返回带有posts字段的对象
139139
posts.value = response.posts;
140140
currentPage.value = response.pagination?.currentPage || 1;
141141
totalPages.value = response.pagination?.totalPages || 1;
142142
} else if (response.data) {
143-
// 如果返回的是 { data: [...] } 格式
144143
posts.value = response.data;
145-
currentPage.value = 1;
146-
totalPages.value = 1;
147144
} else {
148-
// 其他可能的格式
149145
posts.value = response || [];
150-
currentPage.value = 1;
151-
totalPages.value = 1;
152-
}
153-
154-
console.log('处理后的文章数据:', posts.value);
155-
156-
if (posts.value.length === 0) {
157-
console.warn('文章列表为空,可能是API返回格式不符合预期或没有文章');
158146
}
159147
} catch (error) {
160148
console.error('获取文章失败:', error);
@@ -164,46 +152,34 @@ const fetchPosts = async (page = 1) => {
164152
}
165153
};
166154
167-
// 获取分类列表
155+
// 获取分类 (用于 Bento Grid)
168156
const fetchCategories = async () => {
169157
try {
170-
loadingCategories.value = true;
171158
categories.value = await api.getCategories();
172-
console.log('获取到的分类数据:', categories.value);
173159
} catch (error) {
174160
console.error('获取分类失败:', error);
175-
categories.value = [];
176-
} finally {
177-
loadingCategories.value = false;
178161
}
179162
};
180163
181-
// 获取标签列表
182-
const fetchTags = async () => {
183-
try {
184-
loadingTags.value = true;
185-
tags.value = await api.getTags();
186-
console.log('获取到的标签数据:', tags.value);
187-
} catch (error) {
188-
console.error('获取标签失败:', error);
189-
tags.value = [];
190-
} finally {
191-
loadingTags.value = false;
192-
}
193-
};
194-
195-
// 分页处理
196164
const handlePageChange = (page) => {
197165
fetchPosts(page);
198-
// 回到顶部
199-
window.scrollTo(0, 0);
166+
window.scrollTo({ top: 0, behavior: 'smooth' });
200167
};
201168
202-
// 组件挂载时获取数据
203169
onMounted(() => {
204170
fetchPosts();
205171
fetchCategories();
206-
fetchTags();
207-
checkAdminStatus(); // 检查管理员状态
172+
checkAdminStatus();
208173
});
209-
</script>
174+
</script>
175+
176+
<style scoped>
177+
/* 简单的淡入动画 */
178+
@keyframes fadeInUp {
179+
from { opacity: 0; transform: translateY(20px); }
180+
to { opacity: 1; transform: translateY(0); }
181+
}
182+
.animate-fade-in-up {
183+
animation: fadeInUp 0.6s ease-out forwards;
184+
}
185+
</style>

0 commit comments

Comments
 (0)