Feat: Note & Gallery Details
日记列表增加点赞和精选效果,日记详情增加点赞等功能(尚未完善交互),相册列表增加精选和描述内容
This commit is contained in:
parent
bacb9b8e5c
commit
d03532b91f
|
|
@ -0,0 +1,19 @@
|
|||
import { SVGProps } from "react";
|
||||
|
||||
type IconProps = SVGProps<SVGSVGElement>;
|
||||
|
||||
export const StarFill = (props: IconProps) => (
|
||||
<svg {...props} xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M12.0006 18.26L4.94715 22.2082L6.52248 14.2799L0.587891 8.7918L8.61493 7.84006L12.0006 0.5L15.3862 7.84006L23.4132 8.7918L17.4787 14.2799L19.054 22.2082L12.0006 18.26Z"></path></svg>
|
||||
);
|
||||
|
||||
export const ThumbUpFill = (props: IconProps) => (
|
||||
<svg {...props} xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M2 8.99997H5V21H2C1.44772 21 1 20.5523 1 20V9.99997C1 9.44769 1.44772 8.99997 2 8.99997ZM7.29289 7.70708L13.6934 1.30661C13.8693 1.13066 14.1479 1.11087 14.3469 1.26016L15.1995 1.8996C15.6842 2.26312 15.9026 2.88253 15.7531 3.46966L14.5998 7.99997H21C22.1046 7.99997 23 8.8954 23 9.99997V12.1043C23 12.3656 22.9488 12.6243 22.8494 12.8658L19.755 20.3807C19.6007 20.7554 19.2355 21 18.8303 21H8C7.44772 21 7 20.5523 7 20V8.41419C7 8.14897 7.10536 7.89462 7.29289 7.70708Z"></path></svg>
|
||||
);
|
||||
|
||||
export const ShareFill = (props: IconProps) => (
|
||||
<svg {...props} xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M13.5759 17.2714L8.46576 14.484C7.83312 15.112 6.96187 15.5 6 15.5C4.067 15.5 2.5 13.933 2.5 12C2.5 10.067 4.067 8.5 6 8.5C6.96181 8.5 7.83301 8.88796 8.46564 9.51593L13.5759 6.72855C13.5262 6.49354 13.5 6.24983 13.5 6C13.5 4.067 15.067 2.5 17 2.5C18.933 2.5 20.5 4.067 20.5 6C20.5 7.933 18.933 9.5 17 9.5C16.0381 9.5 15.1669 9.11201 14.5343 8.48399L9.42404 11.2713C9.47382 11.5064 9.5 11.7501 9.5 12C9.5 12.2498 9.47383 12.4935 9.42408 12.7285L14.5343 15.516C15.167 14.888 16.0382 14.5 17 14.5C18.933 14.5 20.5 16.067 20.5 18C20.5 19.933 18.933 21.5 17 21.5C15.067 21.5 13.5 19.933 13.5 18C13.5 17.7502 13.5262 17.5064 13.5759 17.2714Z"></path></svg>
|
||||
);
|
||||
|
||||
export const CupFill = (props: IconProps) => (
|
||||
<svg {...props} xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M5 3H20C21.1046 3 22 3.89543 22 5V8C22 9.10457 21.1046 10 20 10H18V13C18 15.2091 16.2091 17 14 17H8C5.79086 17 4 15.2091 4 13V4C4 3.44772 4.44772 3 5 3ZM18 5V8H20V5H18ZM2 19H20V21H2V19Z"></path></svg>
|
||||
);
|
||||
|
|
@ -56,7 +56,7 @@ export function ErrorBoundary() {
|
|||
}
|
||||
|
||||
export const links: LinksFunction = () => [
|
||||
{ rel: "icon", href: "icon.png" },
|
||||
{ rel: "icon", href: "/icon.png" },
|
||||
{ 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" },
|
||||
];
|
||||
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import { NavLink, useLoaderData, useNavigate } from "@remix-run/react";
|
|||
import { json, LoaderFunctionArgs, type MetaFunction } from "@remix-run/node";
|
||||
import Pagination from "~/components/common/pagination";
|
||||
import { clsn, siteTitle } from "~/utils";
|
||||
import { StarFill } from "~/components/common/icons";
|
||||
|
||||
import styles from "./styles.module.less";
|
||||
|
||||
|
|
@ -65,11 +66,21 @@ export default function Gallery() {
|
|||
</section>
|
||||
<section className="grid gap-8 grid-cols-2 sm:grid-cols-[repeat(auto-fill,minmax(18em,_1fr))] mb-12">
|
||||
{media.data.map((item) => (
|
||||
<div key={item.id} className="bg-white rounded-xl overflow-hidden border-4 border-transparent hover:border-pink-400 transition-colors border-b-4 border-b-cyan-200">
|
||||
<div key={item.id} className="relative bg-white rounded-xl overflow-hidden border-4 border-transparent hover:border-pink-400 transition-colors border-b-4 border-b-cyan-200">
|
||||
<img className={styles.image} src={item.thumb_url} alt={item.title} loading="lazy" />
|
||||
<div className="relative p-4 sm:p-6 -mt-6 sm:-mt-12">
|
||||
{item.content && (
|
||||
<div className={clsn("absolute top-0 left-0 right-0 bottom-0 bg-opacity-60 bg-black p-4 sm:p-6 text-white leading-7 transition-opacity duration-300 opacity-0 hover:opacity-100", styles.desc)}>
|
||||
{item.content}
|
||||
</div>
|
||||
)}
|
||||
<div className="p-4 sm:p-6 -mt-6 sm:-mt-12">
|
||||
<span className="block text-sm mb-4 opacity-60">{item.take_time.substring(0, 10)}</span>
|
||||
<h1 className="text-pink-400 text-xl sm:text-2xl font-bold text-ellipsis overflow-hidden">{item.title}</h1>
|
||||
<h1 className="text-pink-400 text-xl sm:text-2xl font-bold text-ellipsis overflow-hidden">
|
||||
{item.title}
|
||||
{item.starred && (
|
||||
<StarFill className=" text-yellow-400 w-6 h-6 inline-block ml-2 align-[-.1em]" />
|
||||
)}
|
||||
</h1>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,9 @@
|
|||
.image {
|
||||
width: 100%;
|
||||
.image, .desc {
|
||||
aspect-ratio: 1 / 1;
|
||||
background-color: #999;
|
||||
clip-path: polygon(0 0, 100% 0%, 100% 100%, 0 75%);
|
||||
}
|
||||
|
||||
.image {
|
||||
width: 100%;
|
||||
background-color: #999;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
import { LoaderFunctionArgs, MetaFunction, json } from "@remix-run/node";
|
||||
import { useLoaderData } from "@remix-run/react";
|
||||
import { useState } from "react";
|
||||
import Article from "~/components/common/article";
|
||||
import { CupFill, ShareFill, ThumbUpFill } from "~/components/common/icons";
|
||||
import { siteTitle } from "~/utils";
|
||||
|
||||
export const meta: MetaFunction<typeof loader> = ({ data }) => {
|
||||
|
|
@ -27,15 +29,52 @@ export async function loader({ params }: LoaderFunctionArgs) {
|
|||
export default function Detail() {
|
||||
const note = useLoaderData<typeof loader>();
|
||||
|
||||
const [likes, setLikes] = useState(note.data.likes);
|
||||
|
||||
const onLike = () => {
|
||||
setLikes((prevLike) => prevLike + 1);
|
||||
}
|
||||
|
||||
const onShare = () => {
|
||||
const shareData = {
|
||||
title: note.data.title,
|
||||
text: note.data.except,
|
||||
url: `${location.protocol}//${location.host}${location.pathname}`,
|
||||
};
|
||||
|
||||
if ("share" in navigator) {
|
||||
navigator.share(shareData);
|
||||
}
|
||||
else {
|
||||
alert("当前操作系统尚未实现此 API");
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<main className="px-2 py-24 max-w-3xl mx-auto">
|
||||
<section className="mb-12">
|
||||
<h1 className="text-center text-5xl/tight md:text-7xl/tight mb-4">{note.data.title}</h1>
|
||||
<h1 className="text-center text-5xl/tight md:text-7xl/tight mb-4" style={{ viewTransitionName: `note-title-${note.data.id}` }}>
|
||||
{note.data.title}
|
||||
</h1>
|
||||
<p className="text-center opacity-60">{note.data.date}</p>
|
||||
</section>
|
||||
<section className="p-5 bg-white rounded-xl border-b-4 border-b-cyan-200">
|
||||
<section className="p-5 bg-white rounded-xl border-b-4 border-b-cyan-200 mb-8">
|
||||
<Article html={note.data.content_html} />
|
||||
</section>
|
||||
<section className="flex gap-16 items-center justify-center text-base md:text-lg">
|
||||
<button onClick={onLike}>
|
||||
<ThumbUpFill className="inline-block h-8 w-8 mr-2" />
|
||||
{likes}
|
||||
</button>
|
||||
<button onClick={onShare}>
|
||||
<ShareFill className="inline-block h-8 w-8 mr-2" />
|
||||
分享
|
||||
</button>
|
||||
<button>
|
||||
<CupFill className="inline-block h-8 w-8 mr-2" />
|
||||
打赏
|
||||
</button>
|
||||
</section>
|
||||
</main>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,9 +1,10 @@
|
|||
import { ChangeEvent, useEffect } from "react";
|
||||
import { Link, useLoaderData, useNavigate, useSearchParams } from "@remix-run/react";
|
||||
import { ChangeEvent, useEffect, useState } from "react";
|
||||
import { Link, unstable_useViewTransitionState, useLoaderData, useNavigate, useSearchParams } from "@remix-run/react";
|
||||
import { json, LoaderFunctionArgs, type MetaFunction } from "@remix-run/node";
|
||||
import Pagination from "~/components/common/pagination";
|
||||
import { clsn, siteTitle } from "~/utils";
|
||||
import { getFirstImage, years } from "~/utils/note";
|
||||
import { StarFill, ThumbUpFill } from "~/components/common/icons";
|
||||
|
||||
export const meta: MetaFunction = () => {
|
||||
return [
|
||||
|
|
@ -61,11 +62,21 @@ export default function Note() {
|
|||
key={item.id}
|
||||
className="block group relative overflow-hidden p-5 bg-white rounded-xl mb-8 last:mb-0 border-4 border-transparent hover:border-pink-400 transition-colors border-b-cyan-200"
|
||||
to={`/note/${year}/${item.id}`}
|
||||
unstable_viewTransition
|
||||
>
|
||||
<h2 className="text-pink-400 text-2xl font-bold mb-4">{item.title}</h2>
|
||||
<p className={clsn(cover && "mr-40", "mb-8")}>{item.except}</p>
|
||||
{item.starred && (
|
||||
<StarFill className="absolute -top-5 -right-5 w-28 h-28 text-yellow-300 text-opacity-20 -rotate-[23deg]" />
|
||||
)}
|
||||
<h2 className="text-pink-400 text-2xl font-bold mb-4" style={{ viewTransitionName: `note-title-${item.id}` }}>
|
||||
{item.title}
|
||||
</h2>
|
||||
<p className={clsn(cover && "mr-40", "mb-8 relative")}>{item.except}</p>
|
||||
<div className="flex items-end justify-between text-sm">
|
||||
<p className="opacity-60">{item.date}</p>
|
||||
<span className="flex items-center opacity-60">
|
||||
<ThumbUpFill className="h-4 w-4 mr-1" />
|
||||
{item.likes}
|
||||
</span>
|
||||
</div>
|
||||
{cover && (
|
||||
<div
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ declare namespace API {
|
|||
is_sensitive: boolean
|
||||
take_time: string
|
||||
modified: number | string // 文件修改时间
|
||||
photo?: File
|
||||
starred: boolean
|
||||
}
|
||||
|
||||
// 后端加的数据
|
||||
|
|
|
|||
Loading…
Reference in New Issue