feat: 重构博客为水墨纸质风格 + 搭建后台管理系统
- 重新设计全站 UI:parchment/ink/terracotta 水墨纸质色系,宋式 serif 排版 - 新增页面:文章列表、文章详情、分类、标签、关于 - GSAP ScrollTrigger 滚动动画 + 逐字揭示效果 - 后台管理系统 /admin:文章/分类/标签 CRUD,JSON 文件存储 - 登录认证(cookie session) - 设计系统文档 UI.md
This commit is contained in:
@@ -0,0 +1,153 @@
|
||||
import { readFileSync, writeFileSync, existsSync, mkdirSync } from "fs";
|
||||
import path from "path";
|
||||
|
||||
const DATA_DIR = path.join(process.cwd(), "src/data/storage");
|
||||
|
||||
// Ensure storage directory exists
|
||||
if (!existsSync(DATA_DIR)) {
|
||||
mkdirSync(DATA_DIR, { recursive: true });
|
||||
}
|
||||
|
||||
export interface Post {
|
||||
id: string;
|
||||
slug: string;
|
||||
title: string;
|
||||
excerpt: string;
|
||||
content: string;
|
||||
date: string;
|
||||
category: string;
|
||||
tags: string[];
|
||||
readingTime: number;
|
||||
featured: boolean;
|
||||
status: "draft" | "published";
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
}
|
||||
|
||||
export interface Category {
|
||||
id: string;
|
||||
name: string;
|
||||
description: string;
|
||||
}
|
||||
|
||||
export interface Tag {
|
||||
id: string;
|
||||
name: string;
|
||||
}
|
||||
|
||||
function readJSON<T>(filename: string, fallback: T): T {
|
||||
const filepath = path.join(DATA_DIR, filename);
|
||||
if (!existsSync(filepath)) return fallback;
|
||||
try {
|
||||
return JSON.parse(readFileSync(filepath, "utf-8"));
|
||||
} catch {
|
||||
return fallback;
|
||||
}
|
||||
}
|
||||
|
||||
function writeJSON(filename: string, data: unknown) {
|
||||
const filepath = path.join(DATA_DIR, filename);
|
||||
writeFileSync(filepath, JSON.stringify(data, null, 2), "utf-8");
|
||||
}
|
||||
|
||||
function generateId(): string {
|
||||
return Date.now().toString(36) + Math.random().toString(36).slice(2, 8);
|
||||
}
|
||||
|
||||
// ── Posts ──
|
||||
|
||||
export function getPosts(): Post[] {
|
||||
return readJSON<Post[]>("posts.json", []);
|
||||
}
|
||||
|
||||
export function getPost(id: string): Post | undefined {
|
||||
return getPosts().find((p) => p.id === id);
|
||||
}
|
||||
|
||||
export function getPostBySlug(slug: string): Post | undefined {
|
||||
return getPosts().find((p) => p.slug === slug);
|
||||
}
|
||||
|
||||
export function createPost(data: Omit<Post, "id" | "createdAt" | "updatedAt">): Post {
|
||||
const posts = getPosts();
|
||||
const now = new Date().toISOString();
|
||||
const post: Post = {
|
||||
...data,
|
||||
id: generateId(),
|
||||
createdAt: now,
|
||||
updatedAt: now,
|
||||
};
|
||||
posts.unshift(post);
|
||||
writeJSON("posts.json", posts);
|
||||
return post;
|
||||
}
|
||||
|
||||
export function updatePost(id: string, data: Partial<Post>): Post | null {
|
||||
const posts = getPosts();
|
||||
const index = posts.findIndex((p) => p.id === id);
|
||||
if (index === -1) return null;
|
||||
posts[index] = { ...posts[index], ...data, updatedAt: new Date().toISOString() };
|
||||
writeJSON("posts.json", posts);
|
||||
return posts[index];
|
||||
}
|
||||
|
||||
export function deletePost(id: string): boolean {
|
||||
const posts = getPosts();
|
||||
const filtered = posts.filter((p) => p.id !== id);
|
||||
if (filtered.length === posts.length) return false;
|
||||
writeJSON("posts.json", filtered);
|
||||
return true;
|
||||
}
|
||||
|
||||
// ── Categories ──
|
||||
|
||||
export function getCategories(): Category[] {
|
||||
return readJSON<Category[]>("categories.json", []);
|
||||
}
|
||||
|
||||
export function createCategory(data: Omit<Category, "id">): Category {
|
||||
const categories = getCategories();
|
||||
const cat: Category = { ...data, id: generateId() };
|
||||
categories.push(cat);
|
||||
writeJSON("categories.json", categories);
|
||||
return cat;
|
||||
}
|
||||
|
||||
export function updateCategory(id: string, data: Partial<Category>): Category | null {
|
||||
const categories = getCategories();
|
||||
const index = categories.findIndex((c) => c.id === id);
|
||||
if (index === -1) return null;
|
||||
categories[index] = { ...categories[index], ...data };
|
||||
writeJSON("categories.json", categories);
|
||||
return categories[index];
|
||||
}
|
||||
|
||||
export function deleteCategory(id: string): boolean {
|
||||
const categories = getCategories();
|
||||
const filtered = categories.filter((c) => c.id !== id);
|
||||
if (filtered.length === categories.length) return false;
|
||||
writeJSON("categories.json", filtered);
|
||||
return true;
|
||||
}
|
||||
|
||||
// ── Tags ──
|
||||
|
||||
export function getTags(): Tag[] {
|
||||
return readJSON<Tag[]>("tags.json", []);
|
||||
}
|
||||
|
||||
export function createTag(data: Omit<Tag, "id">): Tag {
|
||||
const tags = getTags();
|
||||
const tag: Tag = { ...data, id: generateId() };
|
||||
tags.push(tag);
|
||||
writeJSON("tags.json", tags);
|
||||
return tag;
|
||||
}
|
||||
|
||||
export function deleteTag(id: string): boolean {
|
||||
const tags = getTags();
|
||||
const filtered = tags.filter((t) => t.id !== id);
|
||||
if (filtered.length === tags.length) return false;
|
||||
writeJSON("tags.json", filtered);
|
||||
return true;
|
||||
}
|
||||
Reference in New Issue
Block a user