feat: add custom thumb support with url suffix. (#49)
This commit is contained in:
parent
179a2b088f
commit
6b2211ba85
1
.vscode/settings.json
vendored
1
.vscode/settings.json
vendored
@ -121,6 +121,7 @@
|
|||||||
"tsconfigs",
|
"tsconfigs",
|
||||||
"ultrahtml",
|
"ultrahtml",
|
||||||
"unpic",
|
"unpic",
|
||||||
|
"unsharp",
|
||||||
"upyun",
|
"upyun",
|
||||||
"urlset",
|
"urlset",
|
||||||
"varchar",
|
"varchar",
|
||||||
|
@ -13,6 +13,9 @@ export default defineConfig({
|
|||||||
security: {
|
security: {
|
||||||
checkOrigin: true,
|
checkOrigin: true,
|
||||||
},
|
},
|
||||||
|
image: {
|
||||||
|
domains: ['localhost', '127.0.0.1'],
|
||||||
|
},
|
||||||
experimental: {
|
experimental: {
|
||||||
env: {
|
env: {
|
||||||
schema: {
|
schema: {
|
||||||
|
18
options.ts
18
options.ts
@ -1,5 +1,7 @@
|
|||||||
import { z } from 'astro/zod';
|
import { z } from 'astro/zod';
|
||||||
|
|
||||||
|
const isProd = (): boolean => import.meta.env.MODE === 'production' || process.env.NODE_ENV === 'production';
|
||||||
|
|
||||||
// The type of the options, use zod for better validation.
|
// The type of the options, use zod for better validation.
|
||||||
const Options = z
|
const Options = z
|
||||||
.object({
|
.object({
|
||||||
@ -65,9 +67,12 @@ const Options = z
|
|||||||
admins: z.array(z.number()),
|
admins: z.array(z.number()),
|
||||||
}),
|
}),
|
||||||
}),
|
}),
|
||||||
|
thumbnail: z
|
||||||
|
.function()
|
||||||
|
.args(z.object({ src: z.string().min(1), width: z.number(), height: z.number() }))
|
||||||
|
.returns(z.string()),
|
||||||
})
|
})
|
||||||
.transform((opts) => {
|
.transform((opts) => {
|
||||||
const isProd = (): boolean => import.meta.env.MODE === 'production' || process.env.NODE_ENV === 'production';
|
|
||||||
const assetsPrefix = (): string => (isProd() ? opts.settings.assetPrefix : opts.local.website);
|
const assetsPrefix = (): string => (isProd() ? opts.settings.assetPrefix : opts.local.website);
|
||||||
return {
|
return {
|
||||||
...opts,
|
...opts,
|
||||||
@ -81,7 +86,7 @@ const Options = z
|
|||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
const options = {
|
const options: z.input<typeof Options> = {
|
||||||
local: {
|
local: {
|
||||||
port: 4321,
|
port: 4321,
|
||||||
},
|
},
|
||||||
@ -172,6 +177,15 @@ const options = {
|
|||||||
admins: [3],
|
admins: [3],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
thumbnail: ({ src, width, height }) => {
|
||||||
|
if (isProd()) {
|
||||||
|
// Add upyun thumbnail support.
|
||||||
|
return `${src}!upyun520/both/${width}x${height}/quality/100/unsharp/true/progressive/true`;
|
||||||
|
}
|
||||||
|
// See https://docs.astro.build/en/reference/image-service-reference/#local-services
|
||||||
|
// Remember to add the localhost to you image service settings.
|
||||||
|
return `http://localhost:4321/_image?href=${src}&w=${width}&h=${height}&f=webp&q=100`;
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
export default Options.parse(options);
|
export default Options.parse(options);
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
---
|
---
|
||||||
import { blurStyle, type Image } from '@/helpers/images';
|
import { blurStyle, type Image } from '@/helpers/images';
|
||||||
|
import options from '@/options';
|
||||||
|
|
||||||
interface Props extends Image {
|
interface Props extends Image {
|
||||||
alt: string;
|
alt: string;
|
||||||
@ -8,4 +9,11 @@ interface Props extends Image {
|
|||||||
const { alt, src, width, height } = Astro.props;
|
const { alt, src, width, height } = Astro.props;
|
||||||
---
|
---
|
||||||
|
|
||||||
<img {src} {alt} loading="lazy" {width} {height} style={blurStyle(Astro.props)} />
|
<img
|
||||||
|
src={options.thumbnail({ src, width, height })}
|
||||||
|
{alt}
|
||||||
|
loading="lazy"
|
||||||
|
{width}
|
||||||
|
{height}
|
||||||
|
style={blurStyle(Astro.props)}
|
||||||
|
/>
|
||||||
|
@ -1,14 +0,0 @@
|
|||||||
---
|
|
||||||
import PinnedCategory from '@/components/page/category/PinnedCategory.astro';
|
|
||||||
import { getCategory } from '@/helpers/schema';
|
|
||||||
import options from '@/options';
|
|
||||||
|
|
||||||
const pinnedSlug = options.settings.post.category ?? [];
|
|
||||||
const pinnedCategories = pinnedSlug
|
|
||||||
.map((slug) => getCategory(undefined, slug))
|
|
||||||
.flatMap((category) => (category !== undefined ? [category] : []));
|
|
||||||
---
|
|
||||||
|
|
||||||
<div class="row g-2 g-md-4 list-grouped mt-3 mt-md-4">
|
|
||||||
{pinnedCategories.map((category) => <PinnedCategory category={category} />)}
|
|
||||||
</div>
|
|
@ -1,35 +0,0 @@
|
|||||||
---
|
|
||||||
import Image from '@/components/image/Image.astro';
|
|
||||||
import type { Category } from '@/helpers/schema';
|
|
||||||
|
|
||||||
interface Props {
|
|
||||||
category: Category;
|
|
||||||
}
|
|
||||||
|
|
||||||
const { category } = Astro.props;
|
|
||||||
---
|
|
||||||
|
|
||||||
<div class="col-md-6">
|
|
||||||
<div class="list-item block">
|
|
||||||
<div class="media media-3x1">
|
|
||||||
<a href={category.permalink} class="media-content">
|
|
||||||
<Image {...category.cover} alt={category.name} />
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
<div class="list-content">
|
|
||||||
<div class="list-body">
|
|
||||||
<a href={category.permalink} class="list-title h5">
|
|
||||||
{category.name}
|
|
||||||
</a>
|
|
||||||
<div class="list-subtitle d-none d-md-block text-md text-secondary mt-2">
|
|
||||||
<div class="h-1x">{category.description}</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="list-footer mt-2">
|
|
||||||
<div class="text-muted text-sm">
|
|
||||||
<span class="d-inline-block">{`${category.counts} 篇文章`}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
@ -12,7 +12,7 @@ const { post } = Astro.props;
|
|||||||
<div class="list-item list-nice-overlay">
|
<div class="list-item list-nice-overlay">
|
||||||
<div class="media media-3x2">
|
<div class="media media-3x2">
|
||||||
<a href={post.permalink} class="media-content">
|
<a href={post.permalink} class="media-content">
|
||||||
<Image {...post.cover} alt={post.title} />
|
<Image {...post.cover} alt={post.title} width={750} height={500} />
|
||||||
<div class="overlay"></div>
|
<div class="overlay"></div>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
@ -15,7 +15,7 @@ const category = getCategory(post.category, undefined);
|
|||||||
<div class="list-item block">
|
<div class="list-item block">
|
||||||
<div class="media media-3x2 col-6 col-md-5">
|
<div class="media media-3x2 col-6 col-md-5">
|
||||||
<a href={post.permalink} class="media-content">
|
<a href={post.permalink} class="media-content">
|
||||||
<Image {...post.cover} alt={post.title} />
|
<Image {...post.cover} alt={post.title} width={600} height={400} />
|
||||||
</a>
|
</a>
|
||||||
<div class="media-overlay overlay-top">
|
<div class="media-overlay overlay-top">
|
||||||
<a class="d-none d-md-inline-block badge badge-md bg-white-overlay" href={category ? category.permalink : ''}>
|
<a class="d-none d-md-inline-block badge badge-md bg-white-overlay" href={category ? category.permalink : ''}>
|
||||||
|
@ -1,12 +1,25 @@
|
|||||||
---
|
---
|
||||||
import PostCard from '@/components/page/post/PostCard.astro';
|
import Pagination from '@/components/page/pagination/Pagination.astro';
|
||||||
|
import { slicePosts } from '@/helpers/formatter';
|
||||||
import type { Post } from '@/helpers/schema';
|
import type { Post } from '@/helpers/schema';
|
||||||
|
import options from '@/options';
|
||||||
|
import PostCard from './PostCard.astro';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
posts: Post[];
|
posts: Post[];
|
||||||
|
pageNum: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
const { posts } = Astro.props;
|
const { pageNum, posts } = Astro.props;
|
||||||
|
const results = slicePosts(posts, pageNum, options.settings.pagination.posts);
|
||||||
|
if (!results) {
|
||||||
|
return Astro.redirect('/404');
|
||||||
|
}
|
||||||
|
|
||||||
|
const { currentPosts, totalPage } = results;
|
||||||
---
|
---
|
||||||
|
|
||||||
<div class="list-grid">{posts.map((post) => <PostCard post={post} />)}</div>
|
<div class="content-wrapper content-wrapper col-12 col-xl-9">
|
||||||
|
<div class="list-grid">{currentPosts.map((post) => <PostCard post={post} />)}</div>
|
||||||
|
<Pagination current={pageNum} total={totalPage} rootPath={'/'} />
|
||||||
|
</div>
|
||||||
|
41
src/components/page/post/PostCategory.astro
Normal file
41
src/components/page/post/PostCategory.astro
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
---
|
||||||
|
import Image from '@/components/image/Image.astro';
|
||||||
|
import { getCategory } from '@/helpers/schema';
|
||||||
|
import options from '@/options';
|
||||||
|
|
||||||
|
const pinnedSlug = options.settings.post.category ?? [];
|
||||||
|
const pinnedCategories = pinnedSlug
|
||||||
|
.map((slug) => getCategory(undefined, slug))
|
||||||
|
.flatMap((category) => (category !== undefined ? [category] : []));
|
||||||
|
---
|
||||||
|
|
||||||
|
<div class="row g-2 g-md-4 list-grouped mt-3 mt-md-4">
|
||||||
|
{
|
||||||
|
pinnedCategories.map((category) => (
|
||||||
|
<div class="col-md-6">
|
||||||
|
<div class="list-item block">
|
||||||
|
<div class="media media-3x1">
|
||||||
|
<a href={category.permalink} class="media-content">
|
||||||
|
<Image {...category.cover} alt={category.name} width={600} height={200} />
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div class="list-content">
|
||||||
|
<div class="list-body">
|
||||||
|
<a href={category.permalink} class="list-title h5">
|
||||||
|
{category.name}
|
||||||
|
</a>
|
||||||
|
<div class="list-subtitle d-none d-md-block text-md text-secondary mt-2">
|
||||||
|
<div class="h-1x">{category.description}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="list-footer mt-2">
|
||||||
|
<div class="text-muted text-sm">
|
||||||
|
<span class="d-inline-block">{`${category.counts} 篇文章`}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))
|
||||||
|
}
|
||||||
|
</div>
|
@ -1,25 +0,0 @@
|
|||||||
---
|
|
||||||
import Pagination from '@/components/page/pagination/Pagination.astro';
|
|
||||||
import PostCards from '@/components/page/post/PostCards.astro';
|
|
||||||
import { slicePosts } from '@/helpers/formatter';
|
|
||||||
import type { Post } from '@/helpers/schema';
|
|
||||||
import options from '@/options';
|
|
||||||
|
|
||||||
interface Props {
|
|
||||||
posts: Post[];
|
|
||||||
pageNum: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
const { pageNum, posts } = Astro.props;
|
|
||||||
const results = slicePosts(posts, pageNum, options.settings.pagination.posts);
|
|
||||||
if (!results) {
|
|
||||||
return Astro.redirect('/404');
|
|
||||||
}
|
|
||||||
|
|
||||||
const { currentPosts, totalPage } = results;
|
|
||||||
---
|
|
||||||
|
|
||||||
<div class="content-wrapper content-wrapper col-12 col-xl-9">
|
|
||||||
<PostCards posts={currentPosts} />
|
|
||||||
<Pagination current={pageNum} total={totalPage} rootPath={'/'} />
|
|
||||||
</div>
|
|
@ -16,7 +16,7 @@ const { post, first } = Astro.props;
|
|||||||
<div class="list-item list-nice-overlay">
|
<div class="list-item list-nice-overlay">
|
||||||
<div class={`media ${first ? 'media-36x17' : ''}`}>
|
<div class={`media ${first ? 'media-36x17' : ''}`}>
|
||||||
<a href={post.permalink} class="media-content">
|
<a href={post.permalink} class="media-content">
|
||||||
<Image {...post.cover} alt={post.title} />
|
<Image {...post.cover} alt={post.title} width={first ? 600 : 300} height={300} />
|
||||||
<div class="overlay"></div>
|
<div class="overlay"></div>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
---
|
---
|
||||||
import PinnedCategories from '@/components/page/category/PinnedCategories.astro';
|
|
||||||
import FeaturePosts from '@/components/page/post/FeaturePosts.astro';
|
import FeaturePosts from '@/components/page/post/FeaturePosts.astro';
|
||||||
import PostPagination from '@/components/page/post/PostPagination.astro';
|
import PostCards from '@/components/page/post/PostCards.astro';
|
||||||
|
import PinnedCategories from '@/components/page/post/PostCategory.astro';
|
||||||
import Sidebar from '@/components/sidebar/Sidebar.astro';
|
import Sidebar from '@/components/sidebar/Sidebar.astro';
|
||||||
import type { Post, Tag } from '@/helpers/schema';
|
import type { Post, Tag } from '@/helpers/schema';
|
||||||
|
|
||||||
@ -27,7 +27,7 @@ if (pageNum < 1) {
|
|||||||
<FeaturePosts {posts} />
|
<FeaturePosts {posts} />
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<PostPagination {pageNum} {posts} />
|
<PostCards {pageNum} {posts} />
|
||||||
<Sidebar {posts} {tags} />
|
<Sidebar {posts} {tags} />
|
||||||
</div>
|
</div>
|
||||||
<PinnedCategories />
|
<PinnedCategories />
|
||||||
|
Loading…
Reference in New Issue
Block a user