From db8a91e6eef99cc3c7091951b19c1f1b6a40d411 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=A5=87=E8=B6=A3=E4=BF=9D=E7=BD=97?= Date: Mon, 6 Nov 2023 01:55:58 +0800 Subject: [PATCH] Feat: Add Note List & Detail Page MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 日记列表与详情页面,包括载入条和文章组件 --- .../common/article/article.module.less | 30 ++++++++ app/components/common/article/index.tsx | 15 ++++ app/components/common/spinner.tsx | 9 +++ app/components/layout/header.tsx | 31 +++++++++ app/root.tsx | 10 ++- app/routes/note.$year.$id.tsx | 33 +++++++++ app/routes/note._index.tsx | 51 ++++++++++++++ app/types/api.d.ts | 7 ++ app/types/api.note.d.ts | 69 +++++++++++++++++++ app/utils/index.ts | 4 ++ tailwind.config.js | 15 +++- 11 files changed, 271 insertions(+), 3 deletions(-) create mode 100644 app/components/common/article/article.module.less create mode 100644 app/components/common/article/index.tsx create mode 100644 app/components/common/spinner.tsx create mode 100644 app/components/layout/header.tsx create mode 100644 app/routes/note.$year.$id.tsx create mode 100644 app/routes/note._index.tsx create mode 100644 app/types/api.d.ts create mode 100644 app/types/api.note.d.ts create mode 100644 app/utils/index.ts diff --git a/app/components/common/article/article.module.less b/app/components/common/article/article.module.less new file mode 100644 index 0000000..f0f3f45 --- /dev/null +++ b/app/components/common/article/article.module.less @@ -0,0 +1,30 @@ +.article { + line-height: 1.7; + + > * { + margin-bottom: 1rem; + + &:last-child { + margin-bottom: 0; + } + } + + pre { + tab-size: 4; + padding: 1em; + color: #fff; + overflow: auto; + border-radius: .75rem; + background-color: #333; + } + + ul { + line-height: 2; + list-style: disc; + margin-left: 1.25rem; + + ::marker { + color: rgb(244 114 182 / var(--tw-bg-opacity)); + } + } +} diff --git a/app/components/common/article/index.tsx b/app/components/common/article/index.tsx new file mode 100644 index 0000000..edd1dd5 --- /dev/null +++ b/app/components/common/article/index.tsx @@ -0,0 +1,15 @@ +import { clsn } from "~/utils"; +import styles from "./article.module.less"; + +interface ArticleProps { + className?: string; + html: string; +} + +function Article({ className, html }: ArticleProps) { + return ( +
+ ); +} + +export default Article; diff --git a/app/components/common/spinner.tsx b/app/components/common/spinner.tsx new file mode 100644 index 0000000..cd5fe68 --- /dev/null +++ b/app/components/common/spinner.tsx @@ -0,0 +1,9 @@ +import { useNavigation } from "@remix-run/react"; + +export default function SpinnerBar() { + const navigation = useNavigation(); + + return navigation.state === "loading" ? ( +
+ ) : null; +} diff --git a/app/components/layout/header.tsx b/app/components/layout/header.tsx new file mode 100644 index 0000000..cfeec0a --- /dev/null +++ b/app/components/layout/header.tsx @@ -0,0 +1,31 @@ +import { Link, NavLink } from "@remix-run/react"; + +const navItems = [ + { name: "首页", to: "/" }, + { name: "日记", to: "/note" }, + { name: "相册", to: "/gallery" }, + { name: "关于我", to: "/about" }, +]; + +const inheritCls = "inline-block py-2 px-5"; +const activeCls = "inline-block py-2 px-5 bg-orange-200 text-pink-400 rounded-xl"; + +function Header() { + return ( +
+ +
+ ); +} + +export default Header; diff --git a/app/root.tsx b/app/root.tsx index c7f9883..3f5f33c 100644 --- a/app/root.tsx +++ b/app/root.tsx @@ -8,22 +8,28 @@ import { Scripts, ScrollRestoration, } from "@remix-run/react"; +import Header from "./components/layout/header"; +import Spinner from "./components/common/spinner"; import "./index.css"; + export const links: LinksFunction = () => [ ...(cssBundleHref ? [{ rel: "stylesheet", href: cssBundleHref }] : []), + { rel: "stylesheet", href: "https://cdn-font.hyperos.mi.com/font/css?family=MiSans:100,200,300,400,450,500,600,650,700,900:Chinese_Simplify,Latin&display=swap" }, ]; export default function App() { return ( - + - + + +
diff --git a/app/routes/note.$year.$id.tsx b/app/routes/note.$year.$id.tsx new file mode 100644 index 0000000..ab16e7e --- /dev/null +++ b/app/routes/note.$year.$id.tsx @@ -0,0 +1,33 @@ +import { LoaderFunctionArgs, json } from "@remix-run/node"; +import { useLoaderData } from "@remix-run/react"; +import Article from "~/components/common/article"; + +export async function loader({ params }: LoaderFunctionArgs) { + if (Number.isNaN(Number(params.year)) || Number.isNaN(Number(params.id))) { + throw json("Not Found", { status: 404 }); + } + + 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 }); + } + + return json(note); +} + +export default function Detail() { + const note = useLoaderData(); + + return ( +
+
+

{note.data.title}

+

{note.data.date}

+
+
+
+
+
+ ); +}; diff --git a/app/routes/note._index.tsx b/app/routes/note._index.tsx new file mode 100644 index 0000000..bf85c07 --- /dev/null +++ b/app/routes/note._index.tsx @@ -0,0 +1,51 @@ +import { useEffect } from "react"; +import { Link, useLoaderData } from "@remix-run/react"; +import { json, type MetaFunction } from "@remix-run/node"; + +export const meta: MetaFunction = () => { + return [ + { title: "日记" }, + { name: "description", content: "奇趣保罗的日常笔记" }, + ]; +}; + +export async function loader() { + const note = await fetch("https://paul.ren/api/note").then((res) => res.json()) as API.Response; + + return json(note); +} + +export default function Note() { + const note = useLoaderData(); + + useEffect(() => { + console.log(note); + }, []); + + return ( +
+
+

日记

+
+ {note.data.map((item) => { + const year = item.date.substring(0, 4); + + return ( +
+

{item.title}

+

{item.except}

+
+

{item.date}

+ + 继续阅读 + +
+
+ ); + })} +
+ ); +} diff --git a/app/types/api.d.ts b/app/types/api.d.ts new file mode 100644 index 0000000..882a18f --- /dev/null +++ b/app/types/api.d.ts @@ -0,0 +1,7 @@ +declare namespace API { + interface Response { + status: "Success" | "Failed"; + msg: string; + data: D; + } +} diff --git a/app/types/api.note.d.ts b/app/types/api.note.d.ts new file mode 100644 index 0000000..345e66e --- /dev/null +++ b/app/types/api.note.d.ts @@ -0,0 +1,69 @@ +declare namespace API { + namespace Note { + enum NoteType { + Private, + Friends, + Limited, + Public, + } + + interface INoteMusic { + id: number; + type?: "netease"; + title: string; + artist: string; + album: string; + cover: string; + } + + interface INoteQuery { + page: number; + year?: number; + month?: number; + search?: string; + } + + interface INoteDetailQuery { + id: number; + year?: number; + } + + // 旧数据兼容 + interface INotePhotoData { + year: number | string; + name: string; + type: string; + url: string; + } + + interface INoteData { + id: number; + title: string; + content: string; + except: string; + content_html: string; + + date: string; + mood: number; + weather: number; + status: number; + type: NoteType; + time_spent: number; + music?: INoteMusic; + starred: boolean; + unlocked?: boolean; + + media: any[]; + photo?: INotePhotoData[]; + + year?: string; + + time: number; + + likes: number; + + created_at: string; + updated_at: string; + } + } +} diff --git a/app/utils/index.ts b/app/utils/index.ts new file mode 100644 index 0000000..7a86661 --- /dev/null +++ b/app/utils/index.ts @@ -0,0 +1,4 @@ +// Classnames +export const clsn = (...clsn: (string | undefined | null | false)[]) => { + return clsn.filter(item => item).join(" "); +} diff --git a/tailwind.config.js b/tailwind.config.js index ea446d6..7360ae8 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -5,7 +5,20 @@ export default { "./app/**/*.{js,ts,jsx,tsx}", ], theme: { - extend: {}, + extend: { + fontFamily: { + "mi": "MiSans", + }, + animation: { + 'spinner-bar': 'spinnerBar 6s linear infinite', + }, + keyframes: { + spinnerBar: { + "0%": { width: "0%" }, + "100%": { width: "100%" }, + } + } + }, }, plugins: [], }