初始化:Astro 站点 + Sveltia CMS 后台 + 部署配置

This commit is contained in:
2026-06-11 17:18:51 +08:00
commit 53092b52db
37 changed files with 7586 additions and 0 deletions

View File

@@ -0,0 +1,106 @@
---
import { getCollection } from 'astro:content';
import BaseLayout from '../../layouts/BaseLayout.astro';
import ProjectCard from '../../components/ProjectCard.astro';
const projects = (await getCollection('projects')).sort(
(a, b) => b.data.order - a.data.order
);
const categories = ['全部', '硬件', '软件', '通信', '机器人', '其他'];
---
<BaseLayout title="项目" description="电子、软件、通信与机器人方向的项目展示">
<section class="page-head">
<h1>项目</h1>
<p class="muted">电子工程、软件、通信与机器人方向的作品与产品。</p>
</section>
{
projects.length > 0 ? (
<>
<div class="filters mono" id="filters">
{categories.map((c) => (
<button class="filter" data-cat={c}>
{c}
</button>
))}
</div>
<div class="grid" id="grid">
{projects.map((p) => (
<div class="grid-item" data-cat={p.data.category}>
<ProjectCard
title={p.data.title}
description={p.data.description}
category={p.data.category}
tags={p.data.tags}
href={`/projects/${p.id}`}
/>
</div>
))}
</div>
</>
) : (
<p class="muted">
暂无项目。在 <code>src/content/projects/</code> 新建 Markdown 文件即可添加。
</p>
)
}
</BaseLayout>
<script>
const buttons = document.querySelectorAll<HTMLButtonElement>('.filter');
const items = document.querySelectorAll<HTMLElement>('.grid-item');
buttons[0]?.classList.add('active');
buttons.forEach((btn) => {
btn.addEventListener('click', () => {
buttons.forEach((b) => b.classList.remove('active'));
btn.classList.add('active');
const cat = btn.dataset.cat;
items.forEach((item) => {
item.style.display =
cat === '全部' || item.dataset.cat === cat ? '' : 'none';
});
});
});
</script>
<style>
.page-head {
padding: 3rem 0 1.5rem;
}
.page-head h1 {
margin: 0 0 0.5rem;
}
.filters {
display: flex;
flex-wrap: wrap;
gap: 0.5rem;
margin-bottom: 1.5rem;
}
.filter {
background: var(--bg-elev);
border: 1px solid var(--border);
color: var(--text-dim);
border-radius: 999px;
padding: 0.3rem 0.85rem;
font-size: 0.8rem;
font-family: var(--font-mono);
cursor: pointer;
transition: all 0.15s ease;
}
.filter:hover {
color: var(--text);
border-color: var(--border-strong);
}
.filter.active {
color: var(--accent-strong);
border-color: var(--accent-dim);
background: rgba(57, 208, 216, 0.1);
}
.grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
gap: 1rem;
}
</style>