1
0
mirror of https://github.com/sendevia/website.git synced 2026-03-06 07:40:50 +08:00

feat(theme): refactor post data handling and improve date processing

This commit is contained in:
2025-10-16 23:04:34 +08:00
parent cdb951ddc9
commit 4326846011
3 changed files with 127 additions and 45 deletions

View File

@@ -1,28 +1,88 @@
import { ref, onMounted } from "vue";
import { ref, Ref } from "vue";
export interface PostItem {
url: string;
type Data = {
title: string;
description?: string;
date?: string;
timestamp?: number;
url: string;
content?: string;
};
declare global {
interface ImportMeta {
glob: (pattern: string, options?: any) => Record<string, any>;
}
}
export function useAllPosts(withContent = false) {
const posts = ref<PostItem[]>([]);
onMounted(async () => {
// @ts-ignore
const modules = import.meta.glob("../../../posts/*.md", { eager: true });
posts.value = Object.entries(modules)
.map(([path, mod]: any) => {
const fm = mod?.frontmatter || {};
return {
url: "/posts/" + path.split("/").pop().replace(/\.md$/, ".html"),
title: fm.title || mod.title || path.split("/").pop().replace(/\.md$/, ""),
date: fm.date || "",
content: withContent ? mod?.default?.toString() || "" : undefined,
};
})
.sort((a, b) => (b.date || "").localeCompare(a.date || ""));
const modules = import.meta.glob("../../../posts/**/*.md", { eager: true }) as Record<string, any>;
export function useAllPosts(asRef: true): Ref<Data[]>;
export function useAllPosts(asRef?: false): Data[];
export function useAllPosts(asRef = false) {
const posts = Object.keys(modules).map((filePath) => {
const mod: any = (modules as any)[filePath];
const frontmatter =
mod.frontmatter ||
mod.default?.frontmatter ||
mod.attributes ||
mod.default?.attributes ||
mod.__pageData?.frontmatter ||
mod.default?.__pageData?.frontmatter ||
{};
const rawDate = frontmatter.date ?? null;
let dateStr = "";
let timestamp: number | undefined = undefined;
if (rawDate != null) {
let d: Date | null = null;
if (rawDate instanceof Date) d = rawDate;
else if (typeof rawDate === "number") d = new Date(rawDate);
else if (typeof rawDate === "string") {
const parsed = Date.parse(rawDate);
if (!isNaN(parsed)) d = new Date(parsed);
else d = new Date(rawDate);
} else {
const parsed = Date.parse(String(rawDate));
if (!isNaN(parsed)) d = new Date(parsed);
}
if (d && !isNaN(d.getTime())) {
dateStr = d.toISOString().slice(0, 10);
timestamp = d.getTime();
}
}
const filename = filePath.split("/").pop() || filePath;
const name = filename.replace(/\.mdx?$/, "").replace(/\.md$/, "");
const url = `/posts/${encodeURIComponent(name)}.html`;
const content = mod.excerpt || mod.excerpt?.text || mod.attributes?.excerpt || (typeof mod.default === "string" ? mod.default : undefined);
const po: Data = {
title: frontmatter.title || name,
description: frontmatter.description || frontmatter.excerpt || (typeof content === "string" ? content.slice(0, 160) : "") || "",
date: dateStr,
timestamp,
url,
content: typeof content === "string" ? content : undefined,
};
return po;
});
return posts;
posts.sort((a, b) => {
if (a.timestamp && b.timestamp) return b.timestamp - a.timestamp;
if (a.timestamp) return -1;
if (b.timestamp) return 1;
return 0;
});
const postsRef = ref<Data[]>(posts);
return asRef ? postsRef : postsRef.value;
}
export type { Data as Post };

View File

@@ -1,23 +1,32 @@
<script setup lang="ts">
import { useAllPosts } from "../composables/useAllPosts";
import { computed } from "vue";
const posts = useAllPosts();
const postsRef = useAllPosts(true);
const postsList = computed(() => postsRef.value ?? []);
</script>
<template>
<div class="page-all-posts">
<h1>所有文章</h1>
<div class="posts-card" v-for="post in posts" :key="post.url">
<a :href="post.url">{{ post.title }}</a>
<span v-if="post.date"> - {{ post.date }}</span>
</div>
<div class="page-allposts" aria-labelledby="all-posts-heading">
<h1 id="allposts-heading">所有文章</h1>
<section class="posts-list">
<div class="posts-card" v-for="post in postsList" :key="post.url">
<h2 class="post-title">
<a :href="post.url">{{ post.title }}</a>
</h2>
<div class="post-meta">
<span v-if="post.date" :datetime="post.date">{{ post.date }}</span>
</div>
<p v-if="post.description" class="post-desc">{{ post.description }}</p>
</div>
</section>
</div>
</template>
<style lang="scss">
@use "../styles/mixin";
.page-all-posts {
.page-allposts {
grid-column: 1 / 13;
.posts-card a {

View File

@@ -1,30 +1,43 @@
<script setup lang="ts">
import { ref, computed } from "vue";
import { useAllPosts } from "../composables/useAllPosts";
import { useAllPosts, type Post } from "../composables/useAllPosts";
const posts = useAllPosts(true);
const postsRef = useAllPosts(true);
const query = ref("");
const filteredPosts = computed(() => {
if (!query.value) return [];
const q = query.value.toLowerCase();
return posts.value.filter((post) => post.title.toLowerCase().includes(q) || (post.content && post.content.toLowerCase().includes(q)));
const filteredPosts = computed<Post[]>(() => {
const q = query.value.trim().toLowerCase();
if (!q) return [];
return (postsRef.value ?? []).filter((post) => {
const inTitle = post.title?.toLowerCase().includes(q) ?? false;
const inDesc = post.description?.toLowerCase().includes(q) ?? false;
const inContent = post.content?.toLowerCase().includes(q) ?? false;
const inDate = post.date?.toLowerCase().includes(q) ?? false;
return inTitle || inDesc || inContent || inDate;
});
});
</script>
<template>
<div class="page-search">
<input v-model="query" placeholder="搜索文章..." class="search-input" />
<div v-if="query">
<div class="search-result" v-if="filteredPosts.length">
<div v-for="post in filteredPosts" :key="post.url">
<a :href="post.url">{{ post.title }}</a>
<span v-if="post.date"> - {{ post.date }}</span>
<main class="page-search" aria-labelledby="search-heading">
<h1 id="search-heading" class="visually-hidden">搜索文章</h1>
<input id="post-search" v-model="query" placeholder="搜索文章..." class="search-input" />
<section>
<h6 v-if="!query">请输入关键词以搜索文章</h6>
<div v-else>
<div class="search-result" v-if="filteredPosts.length">
<article v-for="post in filteredPosts" :key="post.url" class="search-item">
<h2 class="result-title">
<a :href="post.url">{{ post.title }}</a>
</h2>
<p v-if="post.date" :datetime="post.date">{{ post.date }}</p>
<p v-if="post.description" class="result-desc">{{ post.description }}</p>
</article>
</div>
<p v-else>没有找到相关文章</p>
</div>
<p v-else>没有找到相关文章</p>
</div>
<p v-else>请输入关键词以搜索文章</p>
</div>
</section>
</main>
</template>
<style lang="scss">