chore: reorganize project into monorepo
This commit is contained in:
208
frontend/src/pages/index.astro
Normal file
208
frontend/src/pages/index.astro
Normal file
@@ -0,0 +1,208 @@
|
||||
---
|
||||
import BaseLayout from '../layouts/BaseLayout.astro';
|
||||
import TerminalWindow from '../components/ui/TerminalWindow.astro';
|
||||
import CommandPrompt from '../components/ui/CommandPrompt.astro';
|
||||
import FilterPill from '../components/ui/FilterPill.astro';
|
||||
import PostCard from '../components/PostCard.astro';
|
||||
import FriendLinkCard from '../components/FriendLinkCard.astro';
|
||||
import ViewMoreLink from '../components/ui/ViewMoreLink.astro';
|
||||
import StatsList from '../components/StatsList.astro';
|
||||
import TechStackList from '../components/TechStackList.astro';
|
||||
import { terminalConfig } from '../lib/config/terminal';
|
||||
import { api, DEFAULT_SITE_SETTINGS } from '../lib/api/client';
|
||||
import type { AppFriendLink } from '../lib/api/client';
|
||||
import type { Post } from '../lib/types';
|
||||
|
||||
export const prerender = false;
|
||||
|
||||
const url = new URL(Astro.request.url);
|
||||
const selectedType = url.searchParams.get('type') || 'all';
|
||||
|
||||
let siteSettings = DEFAULT_SITE_SETTINGS;
|
||||
let allPosts: Post[] = [];
|
||||
let recentPosts: Post[] = [];
|
||||
let pinnedPost: Post | null = null;
|
||||
let tags: string[] = [];
|
||||
let friendLinks: AppFriendLink[] = [];
|
||||
let categories: Awaited<ReturnType<typeof api.getCategories>> = [];
|
||||
let apiError: string | null = null;
|
||||
|
||||
try {
|
||||
siteSettings = await api.getSiteSettings();
|
||||
allPosts = await api.getPosts();
|
||||
const filteredPosts = selectedType === 'all'
|
||||
? allPosts
|
||||
: allPosts.filter(post => post.type === selectedType);
|
||||
|
||||
recentPosts = filteredPosts.slice(0, 5);
|
||||
pinnedPost = filteredPosts.find(post => post.pinned) || null;
|
||||
tags = (await api.getTags()).map(tag => tag.name);
|
||||
friendLinks = (await api.getFriendLinks()).filter(friend => friend.status === 'approved');
|
||||
categories = await api.getCategories();
|
||||
} catch (error) {
|
||||
apiError = error instanceof Error ? error.message : 'API unavailable';
|
||||
console.error('API Error:', error);
|
||||
}
|
||||
|
||||
const systemStats = [
|
||||
{ label: '文章', value: String(allPosts.length) },
|
||||
{ label: '标签', value: String(tags.length) },
|
||||
{ label: '分类', value: String(categories.length) },
|
||||
{ label: '友链', value: String(friendLinks.length) },
|
||||
];
|
||||
|
||||
const techStack = siteSettings.techStack.map(name => ({ name }));
|
||||
|
||||
const postTypeFilters = [
|
||||
{ id: 'all', name: '全部', icon: 'fa-stream' },
|
||||
{ id: 'article', name: terminalConfig.postTypes.article.label, icon: 'fa-file-alt' },
|
||||
{ id: 'tweet', name: terminalConfig.postTypes.tweet.label, icon: 'fa-comment-dots' }
|
||||
];
|
||||
---
|
||||
|
||||
<BaseLayout title={siteSettings.siteTitle} description={siteSettings.siteDescription}>
|
||||
<div class="max-w-5xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
|
||||
<TerminalWindow title={terminalConfig.title} class="w-full">
|
||||
<div class="mb-6 px-4 overflow-x-auto">
|
||||
<pre class="font-mono text-xs sm:text-sm text-[var(--primary)] whitespace-pre">{terminalConfig.asciiArt}</pre>
|
||||
</div>
|
||||
|
||||
<div class="mb-8 px-4">
|
||||
<CommandPrompt command="cat welcome.txt" />
|
||||
<div class="ml-4">
|
||||
<p class="text-lg font-bold text-[var(--primary)] mb-1">{siteSettings.heroTitle}</p>
|
||||
<p class="text-[var(--text-secondary)]">{siteSettings.heroSubtitle}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-8 px-4">
|
||||
<CommandPrompt command="ls -l" />
|
||||
<div class="ml-4 grid grid-cols-2 sm:grid-cols-3 md:grid-cols-6 gap-4">
|
||||
{terminalConfig.navLinks.map(link => (
|
||||
<a
|
||||
href={link.href}
|
||||
class="flex items-center gap-2 text-[var(--text)] hover:text-[var(--primary)] transition-colors py-2"
|
||||
>
|
||||
<i class={`fas ${link.icon}`}></i>
|
||||
<span>{link.text}</span>
|
||||
</a>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{apiError && (
|
||||
<div class="mb-8 px-4">
|
||||
<div class="ml-4 p-4 rounded-lg border border-[var(--danger)]/20 bg-[var(--danger)]/10 text-[var(--danger)] text-sm">
|
||||
{apiError}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div id="categories" class="mb-8 px-4">
|
||||
<CommandPrompt command="ls -l ./categories" />
|
||||
<div class="ml-4 flex flex-wrap gap-2">
|
||||
{categories.map(category => (
|
||||
<FilterPill tone="amber" href={`/articles?category=${encodeURIComponent(category.name)}`}>
|
||||
<i class="fas fa-folder"></i>
|
||||
<span>{category.name}</span>
|
||||
</FilterPill>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-4 px-4">
|
||||
<CommandPrompt command="./filter_posts.sh" />
|
||||
<div class="ml-4 flex flex-wrap gap-2">
|
||||
{postTypeFilters.map(filter => (
|
||||
<FilterPill tone="blue" active={selectedType === filter.id} href={`/?type=${filter.id}`}>
|
||||
<i class={`fas ${filter.icon}`}></i>
|
||||
<span>{filter.name}</span>
|
||||
</FilterPill>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{pinnedPost && (
|
||||
<div class="mb-8 px-4">
|
||||
<CommandPrompt command="cat ./pinned_post.md" />
|
||||
<div class="ml-4">
|
||||
<div
|
||||
class="p-4 rounded-lg border border-[var(--border-color)] bg-[var(--header-bg)] cursor-pointer hover:border-[var(--primary)] transition-colors"
|
||||
onclick={`window.location.href='/articles/${pinnedPost.slug}'`}
|
||||
>
|
||||
<div class="flex items-center gap-2 mb-2">
|
||||
<span class="px-2 py-0.5 text-xs rounded bg-[var(--primary)] text-[var(--terminal-bg)] font-bold">置顶</span>
|
||||
<span class="w-3 h-3 rounded-full" style={`background-color: ${pinnedPost.type === 'article' ? 'var(--primary)' : 'var(--secondary)'}`}></span>
|
||||
<h3 class="text-lg font-bold">{pinnedPost.title}</h3>
|
||||
</div>
|
||||
<p class="text-sm text-[var(--text-secondary)] mb-2">{pinnedPost.date} | 阅读时间: {pinnedPost.readTime}</p>
|
||||
<p class="text-[var(--text-secondary)]">{pinnedPost.description}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div id="posts" class="mb-10 px-4">
|
||||
<CommandPrompt command="find ./posts -type f -name *.md | head -n 5" />
|
||||
<div class="ml-4">
|
||||
<div class="divide-y divide-[var(--border-color)]">
|
||||
{recentPosts.map(post => (
|
||||
<PostCard post={post} />
|
||||
))}
|
||||
</div>
|
||||
<div class="mt-4">
|
||||
<ViewMoreLink href="/articles" text="查看所有文章" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="tags" class="mb-10 px-4">
|
||||
<CommandPrompt command="cat ./tags.txt" />
|
||||
<div class="ml-4 flex flex-wrap gap-2">
|
||||
{tags.map(tag => (
|
||||
<FilterPill tone="teal" href={`/tags?tag=${encodeURIComponent(tag)}`}>
|
||||
<i class="fas fa-hashtag"></i>
|
||||
<span>{tag}</span>
|
||||
</FilterPill>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="border-t border-[var(--border-color)] my-10"></div>
|
||||
|
||||
<div id="friends" class="mb-10 px-4">
|
||||
<CommandPrompt command="cat ./friends.txt" />
|
||||
<div class="ml-4 grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4">
|
||||
{friendLinks.map(friend => (
|
||||
<FriendLinkCard friend={friend} />
|
||||
))}
|
||||
</div>
|
||||
<div class="mt-6 ml-4">
|
||||
<ViewMoreLink href="/friends" text="查看全部友链" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="border-t border-[var(--border-color)] my-10"></div>
|
||||
|
||||
<div id="about" class="px-4">
|
||||
<CommandPrompt command="cat about_me.txt" />
|
||||
<div class="ml-4">
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||
<div>
|
||||
<h3 class="text-lg font-bold mb-3 text-[var(--title-color)]">关于我</h3>
|
||||
<p class="text-[var(--text-secondary)] mb-4">{siteSettings.ownerBio}</p>
|
||||
|
||||
<h3 class="text-lg font-bold mb-3 text-[var(--title-color)]">技术栈</h3>
|
||||
<TechStackList items={techStack} />
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h3 class="text-lg font-bold mb-3 text-[var(--title-color)]">系统状态</h3>
|
||||
<StatsList stats={systemStats} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</TerminalWindow>
|
||||
</div>
|
||||
</BaseLayout>
|
||||
Reference in New Issue
Block a user