202 lines
5.1 KiB
Plaintext
202 lines
5.1 KiB
Plaintext
---
|
|
import "@/styles/github-markdown.css"
|
|
import "@/styles/github-markdown-reset.css"
|
|
import "photoswipe/dist/photoswipe.css";
|
|
|
|
import Layout from "@/layouts/Layout.astro";
|
|
import {createHttp} from "@/lib/request.ts";
|
|
import {highlightHtmlWithJsdom} from "@/lib/highlight.js";
|
|
import {formatDatetime} from "@/lib/format.ts";
|
|
import type {Post} from "@/types";
|
|
|
|
const {slug} = Astro.params;
|
|
|
|
let data: Post;
|
|
|
|
const http = createHttp(Astro.request.headers)
|
|
|
|
try {
|
|
const res = await http.get<ApiResponse<Post>>(`post/${slug}`);
|
|
data = res.data
|
|
} catch (error) {
|
|
return Astro.redirect("/404")
|
|
}
|
|
const content = await highlightHtmlWithJsdom(data.content)
|
|
const published_at = formatDatetime(data.published_at, 'YYYY 年 M 月 D 日')
|
|
---
|
|
|
|
<Layout title={data.title} description={data.summary}>
|
|
<div class="post">
|
|
<h1 class="post-title">{data.title}</h1>
|
|
|
|
<div class="post-meta">
|
|
<div class="published-at">
|
|
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none"
|
|
stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"
|
|
class="lucide lucide-calendar-days-icon lucide-calendar-days">
|
|
<path d="M8 2v4"/>
|
|
<path d="M16 2v4"/>
|
|
<rect width="18" height="18" x="3" y="4" rx="2"/>
|
|
<path d="M3 10h18"/>
|
|
<path d="M8 14h.01"/>
|
|
<path d="M12 14h.01"/>
|
|
<path d="M16 14h.01"/>
|
|
<path d="M8 18h.01"/>
|
|
<path d="M12 18h.01"/>
|
|
<path d="M16 18h.01"/>
|
|
</svg>
|
|
{published_at}
|
|
</div>
|
|
|
|
<div class="post-view">
|
|
<svg viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg"
|
|
p-id="5122">
|
|
<path
|
|
d="M509.618 415.795c-42.695 0-77.253 38.372-77.253 85.738 0 47.352 34.558 86.22 77.253 86.22 42.667 0 76.716-38.867 76.716-86.22 0-47.352-34.049-85.738-76.716-85.738z m1.9-177.9c-211.548 0-383.265 159.859-383.265 274.587 0 114.727 171.717 273.624 383.266 273.624 212.484 0 384.227-162.522 384.227-273.624 0-111.088-171.743-274.587-384.227-274.587z m-1.446 452.006c-89.7 0-162.134-81.71-162.134-181.903 0-101.157 72.435-182.397 162.134-182.397 90.183 0 163.1 81.241 163.1 182.397 0 100.206-72.917 181.903-163.1 181.903z"
|
|
p-id="5123"></path>
|
|
</svg>
|
|
{data.view}
|
|
</div>
|
|
</div>
|
|
|
|
<div
|
|
class="markdown-body"
|
|
data-color-mode="dark"
|
|
set:html={content}
|
|
></div>
|
|
</div>
|
|
</Layout>
|
|
|
|
<style>
|
|
.post {
|
|
background: #fff;
|
|
border-radius: 12px;
|
|
overflow: auto;
|
|
padding: 24px;
|
|
box-shadow: rgba(149, 157, 165, 0.2) 0 2px 14px -4px;
|
|
border: 1px solid #e7e8ec;
|
|
}
|
|
|
|
.post-title {
|
|
margin-bottom: 16px;
|
|
font-size: 32px;
|
|
}
|
|
|
|
.post-meta {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
margin-bottom: 20px;
|
|
}
|
|
|
|
.published-at {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 8px;
|
|
color: #6b7280;
|
|
font-size: 14px;
|
|
}
|
|
|
|
.published-at svg {
|
|
width: 18px;
|
|
height: 18px;
|
|
}
|
|
|
|
:global(.markdown-body img) {
|
|
object-fit: contain;
|
|
height: auto !important;
|
|
}
|
|
|
|
.post-view {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 4px;
|
|
color: #6b7280;
|
|
padding-right: 12px;
|
|
font-size: 14px;
|
|
}
|
|
|
|
.post-view svg {
|
|
width: 20px;
|
|
height: 20px;
|
|
fill: currentColor; /* 让填充跟随 color */
|
|
stroke: currentColor; /* 让描边也跟随 color */
|
|
}
|
|
|
|
@media (max-width: 768px) {
|
|
.post-title {
|
|
font-size: 20px;
|
|
}
|
|
|
|
.post {
|
|
padding: 12px;
|
|
}
|
|
}
|
|
|
|
:global(.dark) .post {
|
|
background: #171717;
|
|
box-shadow: none;
|
|
border: none;
|
|
}
|
|
</style>
|
|
|
|
|
|
<script>
|
|
import PhotoSwipeLightbox from "photoswipe/lightbox"
|
|
|
|
const init = async () => {
|
|
const post = document.querySelector('.post')!
|
|
const images = Array.from(post.querySelectorAll('img'))
|
|
|
|
const waitImageLoaded = (img: HTMLImageElement) => {
|
|
if (img.complete && img.naturalWidth > 0 && img.naturalHeight > 0) {
|
|
return Promise.resolve()
|
|
}
|
|
|
|
return new Promise<void>((resolve) => {
|
|
img.addEventListener("load", () => resolve(), {once: true})
|
|
img.addEventListener("error", () => resolve(), {once: true})
|
|
})
|
|
}
|
|
|
|
await Promise.all(
|
|
images.map(async (img) => {
|
|
if (img.closest("a")) return
|
|
|
|
await waitImageLoaded(img)
|
|
|
|
const link = document.createElement("a")
|
|
|
|
link.href = img.currentSrc || img.src
|
|
link.target = "_blank"
|
|
link.rel = "noreferrer"
|
|
link.dataset.pswpWidth = String(img.naturalWidth || img.width || 1200)
|
|
link.dataset.pswpHeight = String(img.naturalHeight || img.height || 800)
|
|
|
|
img.parentNode?.insertBefore(link, img)
|
|
link.appendChild(img)
|
|
})
|
|
)
|
|
|
|
const lightbox = new PhotoSwipeLightbox({
|
|
gallery: ".post",
|
|
children: "a",
|
|
pswpModule: () => import("photoswipe"),
|
|
|
|
padding: {
|
|
top: 48,
|
|
right: 24,
|
|
bottom: 48,
|
|
left: 24,
|
|
},
|
|
bgOpacity: 0.5,
|
|
tapAction: "close",
|
|
bgClickAction: "close",
|
|
})
|
|
|
|
lightbox.init()
|
|
}
|
|
|
|
init()
|
|
</script>
|