Compare commits
6 Commits
ddc9e613e2
...
dc938fdb01
| Author | SHA1 | Date | |
|---|---|---|---|
| dc938fdb01 | |||
| d7c84ea0ce | |||
| 4d30605872 | |||
| fbc12f97db | |||
| 5d62fd89b9 | |||
| 60d8ad8e8a |
@@ -61,6 +61,7 @@ export class BlogController {
|
|||||||
return {
|
return {
|
||||||
id: blog.id,
|
id: blog.id,
|
||||||
title: blog.title,
|
title: blog.title,
|
||||||
|
description: blog.description,
|
||||||
createdAt: blog.createdAt,
|
createdAt: blog.createdAt,
|
||||||
content: blogContent,
|
content: blogContent,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import { Skeleton } from "@/components/ui/skeleton";
|
|||||||
import { BlogComments } from "./components/BlogComments";
|
import { BlogComments } from "./components/BlogComments";
|
||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
import { BlogAPI } from "@/lib/api/client";
|
import { BlogAPI } from "@/lib/api/client";
|
||||||
|
import { useEffect } from "react";
|
||||||
|
|
||||||
export default function Blog() {
|
export default function Blog() {
|
||||||
const params = useParams();
|
const params = useParams();
|
||||||
@@ -34,6 +35,16 @@ export default function Blog() {
|
|||||||
() => BlogAPI.getBlog(id, password || undefined),
|
() => BlogAPI.getBlog(id, password || undefined),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (data) {
|
||||||
|
document.title = `${data.title} - 特恩的日志`;
|
||||||
|
const metaDescription = document.querySelector('meta[name="description"]');
|
||||||
|
if (metaDescription) {
|
||||||
|
metaDescription.setAttribute("content", data.description);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, [data]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="w-full overflow-x-hidden">
|
<div className="w-full overflow-x-hidden">
|
||||||
<div className="max-w-200 mx-auto px-5 overflow-x-hidden mb-10">
|
<div className="max-w-200 mx-auto px-5 overflow-x-hidden mb-10">
|
||||||
@@ -48,18 +59,20 @@ export default function Blog() {
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{data && (
|
{data && (
|
||||||
<>
|
<article className="w-full">
|
||||||
<h1 className="text-center text-3xl font-bold mt-10">{data.title}</h1>
|
<header className="flex flex-col items-center">
|
||||||
<p className="text-sm text-zinc-500 text-center my-5">发布于:{new Date(data.createdAt).toLocaleString()}</p>
|
<h1 className="text-center text-2xl sm:text-3xl font-bold mt-10 transition-all duration-500">{data.title}</h1>
|
||||||
|
<time className="text-sm text-zinc-500 text-center my-2 sm:my-5 mb-5 transition-all duration-500">发布于:{new Date(data.createdAt).toLocaleString()}</time>
|
||||||
|
</header>
|
||||||
<ReactMarkdown
|
<ReactMarkdown
|
||||||
remarkPlugins={[remarkGfm]}
|
remarkPlugins={[remarkGfm]}
|
||||||
rehypePlugins={[rehypeRaw, rehypeHighlight]}
|
rehypePlugins={[rehypeRaw, rehypeHighlight]}
|
||||||
components={{
|
components={{
|
||||||
h1: ({ ...props }) => <h1 className="text-3xl font-bold py-2" {...props} />,
|
h1: ({ ...props }) => <h2 className="text-3xl font-bold py-2" {...props} />,
|
||||||
h2: ({ ...props }) => <h2 className="text-2xl font-bold py-1" {...props} />,
|
h2: ({ ...props }) => <h3 className="text-2xl font-bold py-1" {...props} />,
|
||||||
h3: ({ ...props }) => <h3 className="text-xl font-bold py-0.5" {...props} />,
|
h3: ({ ...props }) => <h4 className="text-xl font-bold py-0.5" {...props} />,
|
||||||
h4: ({ ...props }) => <h4 className="text-lg font-bold" {...props} />,
|
h4: ({ ...props }) => <h5 className="text-lg font-bold" {...props} />,
|
||||||
h5: ({ ...props }) => <h5 className="text-md font-bold" {...props} />,
|
h5: ({ ...props }) => <h6 className="text-md font-bold" {...props} />,
|
||||||
p: ({ ...props }) => <p className="py-1 text-zinc-700" {...props} />,
|
p: ({ ...props }) => <p className="py-1 text-zinc-700" {...props} />,
|
||||||
img: ({ src }) => (
|
img: ({ src }) => (
|
||||||
<PhotoProvider className="w-full">
|
<PhotoProvider className="w-full">
|
||||||
@@ -78,7 +91,7 @@ export default function Blog() {
|
|||||||
a: ({ ...props }) => <a className="hover:underline" {...props} />,
|
a: ({ ...props }) => <a className="hover:underline" {...props} />,
|
||||||
}}
|
}}
|
||||||
>{data.content}</ReactMarkdown>
|
>{data.content}</ReactMarkdown>
|
||||||
</>
|
</article>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{data && (
|
{data && (
|
||||||
|
|||||||
@@ -24,6 +24,12 @@ const getBlogDetailUrl = (id: string): string => {
|
|||||||
return `/blog/${encoded}`;
|
return `/blog/${encoded}`;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
export const metadata = {
|
||||||
|
title: '日志 - 特恩的日志',
|
||||||
|
description: '我随便发点,你也随便看看~',
|
||||||
|
};
|
||||||
|
|
||||||
export default async function Blog() {
|
export default async function Blog() {
|
||||||
let errorMsg = '';
|
let errorMsg = '';
|
||||||
const blogs = await BlogAPI.list().catch(e => {
|
const blogs = await BlogAPI.list().catch(e => {
|
||||||
|
|||||||
@@ -2,22 +2,36 @@ import Image from 'next/image';
|
|||||||
|
|
||||||
export default function Home() {
|
export default function Home() {
|
||||||
return (
|
return (
|
||||||
<div className="w-full flex-1 flex flex-col items-center justify-center">
|
<section className="w-full flex-1 flex flex-col items-center justify-center">
|
||||||
<Image
|
<figure className="flex flex-col items-center">
|
||||||
src="/avatar.png"
|
<Image
|
||||||
alt="TONE's avatar"
|
src="/avatar.png"
|
||||||
width={180}
|
alt="TONE 的个人头像"
|
||||||
height={180}
|
width={180}
|
||||||
className="rounded-full duration-400 size-35 md:size-45 select-none"
|
height={180}
|
||||||
priority
|
className="rounded-full duration-400 size-35 md:size-45 select-none"
|
||||||
quality={100}
|
priority
|
||||||
/>
|
quality={100}
|
||||||
|
/>
|
||||||
|
</figure>
|
||||||
<h1 className='text-4xl md:text-5xl font-bold mt-5 md:mt-8 gradient-title duration-400 select-none'>特恩(TONE)</h1>
|
<h1 className='text-4xl md:text-5xl font-bold mt-5 md:mt-8 gradient-title duration-400 select-none'>特恩(TONE)</h1>
|
||||||
<h2 className='text-lg sm:text-xl md:text-2xl mt-3 font-medium text-zinc-400 duration-400 select-none'>一名在各个领域反复横跳的程序员</h2>
|
<p className='text-lg sm:text-xl md:text-2xl mt-3 font-medium text-zinc-400 duration-400 select-none'>一名在各个领域反复横跳的程序员</p>
|
||||||
<div className='flex sm:flex-row flex-col gap-2 sm:gap-10 mt-5 md:mt-8 duration-400'>
|
<nav className='flex sm:flex-row flex-col gap-2 sm:gap-10 mt-5 md:mt-8 duration-400' aria-label="社交媒体链接">
|
||||||
<a href='https://space.bilibili.com/474156211' target='_black' className='bg-[#488fe9] hover:bg-[#3972ba] text-center text-white w-45 sm:w-32 px-6 py-2 text-lg rounded-full cursor-pointer'>哔哩哔哩</a>
|
<a href='https://space.bilibili.com/474156211'
|
||||||
<a href='https://github.com/tonecn' className='bg-[#da843f] hover:bg-[#c87d3e] text-center text-white w-45 sm:w-32 px-6 py-2 text-lg rounded-full cursor-pointer'>GitHub</a>
|
target='_black'
|
||||||
</div>
|
rel="noopener noreferrer"
|
||||||
</div>
|
className='bg-[#488fe9] hover:bg-[#3972ba] text-center text-white w-45 sm:w-32 px-6 py-2 text-lg rounded-full cursor-pointer'
|
||||||
|
>
|
||||||
|
哔哩哔哩
|
||||||
|
</a>
|
||||||
|
<a href='https://github.com/tonecn'
|
||||||
|
target='_black'
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
className='bg-[#da843f] hover:bg-[#c87d3e] text-center text-white w-45 sm:w-32 px-6 py-2 text-lg rounded-full cursor-pointer'
|
||||||
|
>
|
||||||
|
GitHub
|
||||||
|
</a>
|
||||||
|
</nav>
|
||||||
|
</section>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,11 @@ import {
|
|||||||
import { ResourceAPI } from "@/lib/api/server";
|
import { ResourceAPI } from "@/lib/api/server";
|
||||||
import { AlertCircle } from "lucide-react";
|
import { AlertCircle } from "lucide-react";
|
||||||
|
|
||||||
|
export const metadata = {
|
||||||
|
title: '资源 - 特恩的日志',
|
||||||
|
description: '一些实用工具和学习资源',
|
||||||
|
};
|
||||||
|
|
||||||
export default async function Resources() {
|
export default async function Resources() {
|
||||||
let errorMsg = '';
|
let errorMsg = '';
|
||||||
const data = await ResourceAPI.list().catch(e => { errorMsg = `${e}`; return null; });
|
const data = await ResourceAPI.list().catch(e => { errorMsg = `${e}`; return null; });
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
import { Geist, Geist_Mono } from "next/font/google";
|
import { Geist, Geist_Mono } from "next/font/google";
|
||||||
import "./globals.css";
|
import "./globals.css";
|
||||||
import { ThemeProvider } from "../components/theme-provider";
|
import { ThemeProvider } from "../components/theme-provider";
|
||||||
import { metadata } from "./config/metadata";
|
|
||||||
import { Toaster } from "sonner";
|
import { Toaster } from "sonner";
|
||||||
import { UserAPI } from "@/lib/api/server";
|
import { UserAPI } from "@/lib/api/server";
|
||||||
import { ClientProvider } from "./ClientProvider";
|
import { ClientProvider } from "./ClientProvider";
|
||||||
|
import { Metadata } from "next";
|
||||||
|
|
||||||
const geistSans = Geist({
|
const geistSans = Geist({
|
||||||
variable: "--font-geist-sans",
|
variable: "--font-geist-sans",
|
||||||
@@ -16,7 +16,10 @@ const geistMono = Geist_Mono({
|
|||||||
subsets: ["latin"],
|
subsets: ["latin"],
|
||||||
});
|
});
|
||||||
|
|
||||||
export { metadata };
|
export const metadata: Metadata = {
|
||||||
|
title: "控制台 - 特恩的日志",
|
||||||
|
description: "登录或注册以解锁更多妙妙小工具",
|
||||||
|
};
|
||||||
|
|
||||||
export default async function RootLayout({
|
export default async function RootLayout({
|
||||||
children,
|
children,
|
||||||
@@ -26,7 +29,7 @@ export default async function RootLayout({
|
|||||||
const user = await UserAPI.me().catch(() => null);
|
const user = await UserAPI.me().catch(() => null);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<html lang="en" suppressHydrationWarning>
|
<html lang="zh-CN" suppressHydrationWarning>
|
||||||
<link rel="icon" href="/favicon.ico" />
|
<link rel="icon" href="/favicon.ico" />
|
||||||
<body
|
<body
|
||||||
className={`${geistSans.variable} ${geistMono.variable} antialiased min-h-screen flex flex-col`}
|
className={`${geistSans.variable} ${geistMono.variable} antialiased min-h-screen flex flex-col`}
|
||||||
@@ -39,10 +42,8 @@ export default async function RootLayout({
|
|||||||
enableSystem
|
enableSystem
|
||||||
disableTransitionOnChange
|
disableTransitionOnChange
|
||||||
>
|
>
|
||||||
<main className="flex-1 flex flex-col bg-zinc-50">
|
{children}
|
||||||
{children}
|
<Toaster />
|
||||||
<Toaster />
|
|
||||||
</main>
|
|
||||||
</ThemeProvider>
|
</ThemeProvider>
|
||||||
</ClientProvider>
|
</ClientProvider>
|
||||||
</body>
|
</body>
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ export async function getBlog(id: string, password?: string) {
|
|||||||
return clientFetch<{
|
return clientFetch<{
|
||||||
id: string;
|
id: string;
|
||||||
title: string;
|
title: string;
|
||||||
|
description: string;
|
||||||
createdAt: string;
|
createdAt: string;
|
||||||
content: string;
|
content: string;
|
||||||
}>(`/api/blog/${id}` + (password ? `?p=${password}` : ''));
|
}>(`/api/blog/${id}` + (password ? `?p=${password}` : ''));
|
||||||
|
|||||||
Reference in New Issue
Block a user