✨ Tags & Categories
This commit is contained in:
parent
fcfbb6118f
commit
2bd6b8758b
@ -24,7 +24,6 @@
|
|||||||
"react-dom": "^18.2.0",
|
"react-dom": "^18.2.0",
|
||||||
"sass": "^1.70.0",
|
"sass": "^1.70.0",
|
||||||
"tailwindcss": "^3.4.1",
|
"tailwindcss": "^3.4.1",
|
||||||
"theme-change": "^2.5.0",
|
|
||||||
"tinacms": "^1.5.28",
|
"tinacms": "^1.5.28",
|
||||||
"typescript": "^5.3.3"
|
"typescript": "^5.3.3"
|
||||||
},
|
},
|
||||||
|
@ -81,12 +81,7 @@ const items: MenuItem[] = [
|
|||||||
</div>
|
</div>
|
||||||
<div class="navbar-end">
|
<div class="navbar-end">
|
||||||
<label class="swap swap-rotate px-[16px]">
|
<label class="swap swap-rotate px-[16px]">
|
||||||
<input
|
<input type="checkbox" class="theme-controller" value="light" checked />
|
||||||
type="checkbox"
|
|
||||||
class="theme-controller"
|
|
||||||
value="dark"
|
|
||||||
data-toggle-theme="dark"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<svg
|
<svg
|
||||||
class="swap-on fill-current w-8 h-8"
|
class="swap-on fill-current w-8 h-8"
|
||||||
@ -108,9 +103,3 @@ const items: MenuItem[] = [
|
|||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script>
|
|
||||||
import { themeChange } from "theme-change";
|
|
||||||
|
|
||||||
themeChange();
|
|
||||||
</script>
|
|
||||||
|
45
src/components/PostList.astro
Normal file
45
src/components/PostList.astro
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
---
|
||||||
|
interface Props {
|
||||||
|
posts: any[];
|
||||||
|
}
|
||||||
|
|
||||||
|
import { POST_TYPES } from "../scripts/consts";
|
||||||
|
|
||||||
|
const { posts } = Astro.props;
|
||||||
|
---
|
||||||
|
|
||||||
|
<div class="grid justify-items-strench gap-6">
|
||||||
|
{
|
||||||
|
posts?.map((item) => (
|
||||||
|
<a href={`/posts/${item.slug}`}>
|
||||||
|
<div class="card sm:card-side hover:bg-base-200 transition-colors sm:max-w-none shadow-xl">
|
||||||
|
{item.heroImg && (
|
||||||
|
<figure class="mx-auto w-full object-cover p-6 max-sm:pb-0 sm:max-w-[12rem] sm:pe-0">
|
||||||
|
<img
|
||||||
|
loading="lazy"
|
||||||
|
src={item.heroImg}
|
||||||
|
class="border-base-content bg-base-300 rounded-btn border border-opacity-5"
|
||||||
|
alt={item.title}
|
||||||
|
/>
|
||||||
|
</figure>
|
||||||
|
)}
|
||||||
|
<div class="card-body">
|
||||||
|
<h2 class="text-xl">{item.title}</h2>
|
||||||
|
<div>
|
||||||
|
<span class="badge badge-accent">{POST_TYPES[item.type]}</span>
|
||||||
|
{item.categories?.map((category: string) => (
|
||||||
|
<span class="badge badge-primary">{category}</span>
|
||||||
|
))}
|
||||||
|
{item.tags?.map((tag: string) => (
|
||||||
|
<span class="badge badge-secondary">{tag}</span>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
<div class="text-xs opacity-60 line-clamp-3">
|
||||||
|
{item.description}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
))
|
||||||
|
}
|
||||||
|
</div>
|
@ -15,17 +15,6 @@ const { title } = Astro.props;
|
|||||||
<meta name="generator" content={Astro.generator} />
|
<meta name="generator" content={Astro.generator} />
|
||||||
<title>{title}</title>
|
<title>{title}</title>
|
||||||
|
|
||||||
<script is:inline>
|
|
||||||
if (localStorage.getItem("theme") === null) {
|
|
||||||
document.documentElement.setAttribute("data-theme", "light");
|
|
||||||
} else {
|
|
||||||
document.documentElement.setAttribute(
|
|
||||||
"data-theme",
|
|
||||||
localStorage.getItem("theme")
|
|
||||||
);
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<ViewTransitions />
|
<ViewTransitions />
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
35
src/pages/categories/[slug].astro
Normal file
35
src/pages/categories/[slug].astro
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
---
|
||||||
|
import PageLayout from "../../layouts/PageLayout.astro";
|
||||||
|
|
||||||
|
import { client } from "../../../tina/__generated__/client";
|
||||||
|
import PostList from "../../components/PostList.astro";
|
||||||
|
|
||||||
|
export const prerender = false;
|
||||||
|
|
||||||
|
const { slug } = Astro.params;
|
||||||
|
|
||||||
|
const postsResponse = await client.queries.postConnection({
|
||||||
|
filter: { categories: { in: [slug ?? "index"] } },
|
||||||
|
});
|
||||||
|
const posts = postsResponse.data.postConnection.edges
|
||||||
|
?.sort((a, b) =>
|
||||||
|
new Date(a?.node?.date ?? 0).getTime() <=
|
||||||
|
new Date(b?.node?.date ?? 0).getTime()
|
||||||
|
? -1
|
||||||
|
: 0
|
||||||
|
)
|
||||||
|
.map((event) => {
|
||||||
|
return { ...event?.node, slug: event?.node?._sys.filename };
|
||||||
|
});
|
||||||
|
---
|
||||||
|
|
||||||
|
<PageLayout>
|
||||||
|
<div class="max-w-[720px] mx-auto">
|
||||||
|
<div class="pt-16 pb-6 px-6">
|
||||||
|
<h1 class="text-4xl font-bold">分类检索</h1>
|
||||||
|
<p class="pt-3">以下是包含「{slug}」分类的记录……</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<PostList posts={posts as any[]} />
|
||||||
|
</div>
|
||||||
|
</PageLayout>
|
@ -4,6 +4,8 @@ import PageLayout from "../../layouts/PageLayout.astro";
|
|||||||
import { client } from "../../../tina/__generated__/client";
|
import { client } from "../../../tina/__generated__/client";
|
||||||
import { TinaMarkdown } from "tinacms/dist/rich-text";
|
import { TinaMarkdown } from "tinacms/dist/rich-text";
|
||||||
|
|
||||||
|
export const prerender = false;
|
||||||
|
|
||||||
const eventsResponse = await client.queries.eventConnection();
|
const eventsResponse = await client.queries.eventConnection();
|
||||||
const events = eventsResponse.data.eventConnection.edges
|
const events = eventsResponse.data.eventConnection.edges
|
||||||
?.sort((a, b) =>
|
?.sort((a, b) =>
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
---
|
---
|
||||||
import PageLayout from "../../layouts/PageLayout.astro";
|
import PageLayout from "../../layouts/PageLayout.astro";
|
||||||
|
|
||||||
import client from "../../../tina/__generated__/client";
|
import { POST_TYPES } from "../../scripts/consts";
|
||||||
|
import { client } from "../../../tina/__generated__/client";
|
||||||
import { TinaMarkdown } from "tinacms/dist/rich-text";
|
import { TinaMarkdown } from "tinacms/dist/rich-text";
|
||||||
|
|
||||||
export const prerender = false;
|
export const prerender = false;
|
||||||
@ -42,6 +43,34 @@ const { data } = await client.queries.post({
|
|||||||
<div>作者</div>
|
<div>作者</div>
|
||||||
<div>{data.post.author?.name ?? "佚名"}</div>
|
<div>{data.post.author?.name ?? "佚名"}</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div>
|
||||||
|
<div>类型</div>
|
||||||
|
<div class="text-accent">{POST_TYPES[data.post.type as unknown as string]}</div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div>分类</div>
|
||||||
|
<div class="flex gap-1">
|
||||||
|
{
|
||||||
|
data.post.categories?.map((category) => (
|
||||||
|
<a href={`/categories/${category}`} class="link link-primary">
|
||||||
|
{category}
|
||||||
|
</a>
|
||||||
|
))
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div>标签</div>
|
||||||
|
<div class="flex gap-1">
|
||||||
|
{
|
||||||
|
data.post.tags?.map((tag) => (
|
||||||
|
<a href={`/tags/${tag}`} class="link link-secondary">
|
||||||
|
{tag}
|
||||||
|
</a>
|
||||||
|
))
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<div>发布于</div>
|
<div>发布于</div>
|
||||||
<div>{new Date(data.post.date ?? 0).toLocaleString()}</div>
|
<div>{new Date(data.post.date ?? 0).toLocaleString()}</div>
|
@ -2,6 +2,9 @@
|
|||||||
import PageLayout from "../../layouts/PageLayout.astro";
|
import PageLayout from "../../layouts/PageLayout.astro";
|
||||||
|
|
||||||
import { client } from "../../../tina/__generated__/client";
|
import { client } from "../../../tina/__generated__/client";
|
||||||
|
import PostList from "../../components/PostList.astro";
|
||||||
|
|
||||||
|
export const prerender = false;
|
||||||
|
|
||||||
const postsResponse = await client.queries.postConnection();
|
const postsResponse = await client.queries.postConnection();
|
||||||
const posts = postsResponse.data.postConnection.edges
|
const posts = postsResponse.data.postConnection.edges
|
||||||
@ -23,33 +26,5 @@ const posts = postsResponse.data.postConnection.edges
|
|||||||
<p class="pt-3">记录生活,记录理想,记录记录……</p>
|
<p class="pt-3">记录生活,记录理想,记录记录……</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="card card-compact w-full shadow-xl">
|
<PostList posts={posts as any[]} />
|
||||||
<div class="card-body">
|
|
||||||
<div class="grid justify-items-strench gap-6">
|
|
||||||
{
|
|
||||||
posts?.map((item) => (
|
|
||||||
<a href={`/posts/${item.slug}`}>
|
|
||||||
<div class="card sm:card-side hover:bg-base-200 transition-colors sm:max-w-none">
|
|
||||||
{item.heroImg && (
|
|
||||||
<figure class="mx-auto w-full object-cover p-6 max-sm:pb-0 sm:max-w-[12rem] sm:pe-0">
|
|
||||||
<img
|
|
||||||
loading="lazy"
|
|
||||||
src={item.heroImg}
|
|
||||||
class="border-base-content bg-base-300 rounded-btn border border-opacity-5"
|
|
||||||
alt={item.title}
|
|
||||||
/>
|
|
||||||
</figure>
|
|
||||||
)}
|
|
||||||
<div class="card-body">
|
|
||||||
<h2 class="card-title">{item.title}</h2>
|
|
||||||
<p class="text-xs opacity-60">{item.description}</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</a>
|
|
||||||
))
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</PageLayout>
|
</PageLayout>
|
||||||
|
35
src/pages/tags/[slug].astro
Normal file
35
src/pages/tags/[slug].astro
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
---
|
||||||
|
import PageLayout from "../../layouts/PageLayout.astro";
|
||||||
|
|
||||||
|
import { client } from "../../../tina/__generated__/client";
|
||||||
|
import PostList from "../../components/PostList.astro";
|
||||||
|
|
||||||
|
export const prerender = false;
|
||||||
|
|
||||||
|
const { slug } = Astro.params;
|
||||||
|
|
||||||
|
const postsResponse = await client.queries.postConnection({
|
||||||
|
filter: { tags: { in: [slug ?? "index"] } },
|
||||||
|
});
|
||||||
|
const posts = postsResponse.data.postConnection.edges
|
||||||
|
?.sort((a, b) =>
|
||||||
|
new Date(a?.node?.date ?? 0).getTime() <=
|
||||||
|
new Date(b?.node?.date ?? 0).getTime()
|
||||||
|
? -1
|
||||||
|
: 0
|
||||||
|
)
|
||||||
|
.map((event) => {
|
||||||
|
return { ...event?.node, slug: event?.node?._sys.filename };
|
||||||
|
});
|
||||||
|
---
|
||||||
|
|
||||||
|
<PageLayout>
|
||||||
|
<div class="max-w-[720px] mx-auto">
|
||||||
|
<div class="pt-16 pb-6 px-6">
|
||||||
|
<h1 class="text-4xl font-bold">标签检索</h1>
|
||||||
|
<p class="pt-3">以下是包含「{slug}」标签的记录……</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<PostList posts={posts as any[]} />
|
||||||
|
</div>
|
||||||
|
</PageLayout>
|
5
src/scripts/consts.ts
Normal file
5
src/scripts/consts.ts
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
export const POST_TYPES: { [id: string]: string } = {
|
||||||
|
article: "文章",
|
||||||
|
podcast: "播客",
|
||||||
|
announcements: "通告",
|
||||||
|
};
|
@ -43,6 +43,27 @@ const Post: Collection = {
|
|||||||
timeFormat: "hh:mm A",
|
timeFormat: "hh:mm A",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
label: "Categories",
|
||||||
|
name: "categories",
|
||||||
|
type: "string",
|
||||||
|
list: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Tags",
|
||||||
|
name: "tags",
|
||||||
|
type: "string",
|
||||||
|
list: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Type",
|
||||||
|
name: "type",
|
||||||
|
type: "string",
|
||||||
|
// @ts-ignore
|
||||||
|
component: "select",
|
||||||
|
options: ["article", "podcast", "announcement"],
|
||||||
|
list: true,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
type: "rich-text",
|
type: "rich-text",
|
||||||
label: "Body",
|
label: "Body",
|
||||||
|
File diff suppressed because one or more lines are too long
@ -9574,11 +9574,6 @@ term-vector@^1.0.0:
|
|||||||
resolved "https://registry.yarnpkg.com/term-vector/-/term-vector-1.0.0.tgz#3ad3cf93c5c2df3e3840e3090ee4ec5fe647910b"
|
resolved "https://registry.yarnpkg.com/term-vector/-/term-vector-1.0.0.tgz#3ad3cf93c5c2df3e3840e3090ee4ec5fe647910b"
|
||||||
integrity sha512-P7xDawxO9T1yjR2oiTfgw7BzvyrdsbakUT6PJsgaAeFmSV+6h3fnXtmZJf4ynef6HVamYgs7Wf5I75UBV15ddQ==
|
integrity sha512-P7xDawxO9T1yjR2oiTfgw7BzvyrdsbakUT6PJsgaAeFmSV+6h3fnXtmZJf4ynef6HVamYgs7Wf5I75UBV15ddQ==
|
||||||
|
|
||||||
theme-change@^2.5.0:
|
|
||||||
version "2.5.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/theme-change/-/theme-change-2.5.0.tgz#d3b064af9c4cd01ab16ce0a4ecb251c827a50e68"
|
|
||||||
integrity sha512-B/UdsgdHAGhSKHTAQnxg/etN0RaMDpehuJmZIjLMDVJ6DGIliRHGD6pODi1CXLQAN9GV0GSyB3G6yCuK05PkPQ==
|
|
||||||
|
|
||||||
thenify-all@^1.0.0:
|
thenify-all@^1.0.0:
|
||||||
version "1.6.0"
|
version "1.6.0"
|
||||||
resolved "https://registry.yarnpkg.com/thenify-all/-/thenify-all-1.6.0.tgz#1a1918d402d8fc3f98fbf234db0bcc8cc10e9726"
|
resolved "https://registry.yarnpkg.com/thenify-all/-/thenify-all-1.6.0.tgz#1a1918d402d8fc3f98fbf234db0bcc8cc10e9726"
|
||||||
|
Loading…
Reference in New Issue
Block a user