diff --git a/app/components/common/article/article.module.less b/app/components/common/article/article.module.less
index 4f34906..feed626 100644
--- a/app/components/common/article/article.module.less
+++ b/app/components/common/article/article.module.less
@@ -9,6 +9,61 @@
}
}
+ h2, h3 {
+ font-size: 1.2em;
+ }
+
+ h1 {
+ margin-top: 5rem;
+ font-size: 1.75em;
+ position: relative;
+ margin-bottom: 1em;
+ padding-bottom: .25em;
+ display: inline-block;
+ scroll-margin-top: 7em;
+
+ &:first-child {
+ margin-top: 0;
+ }
+
+ &::before {
+ content: "";
+ left: 0;
+ bottom: 0;
+ position: absolute;
+
+ width: 1.5em;
+ height: 4px;
+ display: block;
+ border-radius: 4px;
+ background-color: rgb(244 114 182 / var(--tw-text-opacity));
+ }
+ }
+
+ h2 {
+ margin-top: 2em;
+ scroll-margin-top: 4em;
+
+ &:first-child {
+ margin-top: 0;
+ }
+ }
+
+ a {
+ color:rgb(244 114 182 / var(--tw-bg-opacity));
+ }
+
+ img {
+ border-radius: .75rem;
+ }
+
+ em {
+ font-size: smaller;
+ font-style: normal;
+ padding: .15rem .5rem;
+ border-radius: .75rem;
+ }
+
pre {
tab-size: 4;
padding: 1em;
diff --git a/app/root.tsx b/app/root.tsx
index 94c344e..48e3be3 100644
--- a/app/root.tsx
+++ b/app/root.tsx
@@ -7,13 +7,56 @@ import {
Outlet,
Scripts,
ScrollRestoration,
+ isRouteErrorResponse,
+ useRouteError,
} from "@remix-run/react";
import Header from "./components/layout/header";
import Spinner from "./components/common/spinner";
import Footer from "./components/layout/footer";
+import { siteTitle } from "~/utils";
import "./index.css";
+export function ErrorBoundary() {
+ const error = useRouteError();
+ const isRouteError = isRouteErrorResponse(error);
+
+ const [statusCode, message] = (() => {
+ if (isRouteError) {
+ return [error.status, error.statusText];
+ }
+
+ if (error instanceof Error) {
+ return [500, error.message];
+ }
+
+ return [500, "未知异常"];
+ })();
+
+ return (
+
+
+
+
+ {siteTitle(statusCode)}
+
+
+
+
+
+
+
+ {statusCode}
+
+ {message}
+
+
+
+
+
+ );
+}
+
export const links: LinksFunction = () => [
...(cssBundleHref ? [{ rel: "stylesheet", href: cssBundleHref }] : []),
{ rel: "icon", href: "icon.png" },
diff --git a/app/routes/$.tsx b/app/routes/$.tsx
new file mode 100644
index 0000000..69fbe29
--- /dev/null
+++ b/app/routes/$.tsx
@@ -0,0 +1,39 @@
+import { LoaderFunctionArgs, MetaFunction, json } from "@remix-run/node";
+import { useLoaderData } from "@remix-run/react";
+import Article from "~/components/common/article";
+import { siteTitle } from "~/utils";
+
+export const meta: MetaFunction = ({ data }) => {
+ return [
+ { title: data ? siteTitle(data.data.title) : "404" },
+ { name: "description", content: data?.data.desc },
+ ];
+}
+
+export async function loader({ params }: LoaderFunctionArgs) {
+ const slug = params["*"];
+
+ const page = await fetch(`https://paul.ren/api/page/get?slug=${slug}&html`).then((res) => res.json()) as API.Response;
+
+ if (page.status === "Failed") {
+ throw json("Not Found", { status: 404, statusText: page.msg });
+ }
+
+ return json(page);
+}
+
+export default function DynamicPage() {
+ const page = useLoaderData();
+
+ return (
+
+
+ {page.data.title}
+ {page.data.desc}
+
+
+
+ );
+};
diff --git a/app/routes/note.$year.$id.tsx b/app/routes/note.$year.$id.tsx
index e9d73bd..7fae027 100644
--- a/app/routes/note.$year.$id.tsx
+++ b/app/routes/note.$year.$id.tsx
@@ -12,13 +12,13 @@ export const meta: MetaFunction = ({ data }) => {
export async function loader({ params }: LoaderFunctionArgs) {
if (Number.isNaN(Number(params.year)) || Number.isNaN(Number(params.id))) {
- throw json("Not Found", { status: 404 });
+ throw json("Not Found", { status: 404, statusText: "链接格式错误" });
}
const note = await fetch(`https://paul.ren/api/note/get?id=${params.id}&year=${params.year}`).then((res) => res.json()) as API.Response;
if (note.status === "Failed") {
- throw json("Not Found", { status: 404 });
+ throw json("Not Found", { status: 404, statusText: note.msg });
}
return json(note);
diff --git a/app/types/api.page.d.ts b/app/types/api.page.d.ts
new file mode 100644
index 0000000..b59a493
--- /dev/null
+++ b/app/types/api.page.d.ts
@@ -0,0 +1,14 @@
+declare namespace API {
+ namespace Page {
+ interface IPageData {
+ id: number
+ title: string
+ desc: string
+ slug: string
+ content: string
+ hidden: boolean
+ created_at: string
+ updated_at: string
+ }
+ }
+}
diff --git a/app/utils/index.ts b/app/utils/index.ts
index ac16d19..d9f98a5 100644
--- a/app/utils/index.ts
+++ b/app/utils/index.ts
@@ -1,5 +1,5 @@
// 站点名称
-export const siteTitle = (title?: string) => {
+export const siteTitle = (title?: string | number) => {
const siteName = import.meta.env.APP_SITENAME;
return title ? `${title} - ${siteName}` : siteName;