Merge branch 'main' of git.xiongxiao.me:template/astro-simple-template
This commit is contained in:
@@ -4,15 +4,19 @@ import react from '@astrojs/react';
|
|||||||
import sitemap from '@astrojs/sitemap';
|
import sitemap from '@astrojs/sitemap';
|
||||||
import pkgs from './package.json';
|
import pkgs from './package.json';
|
||||||
import tailwindcss from '@tailwindcss/vite';
|
import tailwindcss from '@tailwindcss/vite';
|
||||||
|
import dotenv from 'dotenv';
|
||||||
|
|
||||||
|
dotenv.config();
|
||||||
const isDev = process.env.NODE_ENV === 'development';
|
const isDev = process.env.NODE_ENV === 'development';
|
||||||
|
|
||||||
let target = process.env.VITE_API_URL || 'https://localhost:51015';
|
let target = process.env.VITE_API_URL || 'http://localhost:51015';
|
||||||
const apiProxy = { target: target, changeOrigin: true, ws: true, rewriteWsOrigin: true, secure: false, cookieDomainRewrite: 'localhost' };
|
const apiProxy = { target: target, changeOrigin: true, ws: true, rewriteWsOrigin: true, secure: false, cookieDomainRewrite: 'localhost' };
|
||||||
let proxy = {
|
let proxy = {
|
||||||
'/root/': {
|
'/root/': {
|
||||||
target: `${target}/root/`,
|
target: `${target}/root/`,
|
||||||
},
|
},
|
||||||
'/api': apiProxy,
|
'/api': apiProxy,
|
||||||
|
'/client': apiProxy,
|
||||||
};
|
};
|
||||||
|
|
||||||
const basename = isDev ? undefined : `${pkgs.basename}`;
|
const basename = isDev ? undefined : `${pkgs.basename}`;
|
||||||
@@ -27,7 +31,7 @@ export default defineConfig({
|
|||||||
vite: {
|
vite: {
|
||||||
plugins: [tailwindcss()],
|
plugins: [tailwindcss()],
|
||||||
define: {
|
define: {
|
||||||
basename: JSON.stringify(basename || ''),
|
BASE_NAME: JSON.stringify(basename || ''),
|
||||||
},
|
},
|
||||||
server: {
|
server: {
|
||||||
port: 7008,
|
port: 7008,
|
||||||
|
|||||||
23
package.json
23
package.json
@@ -16,20 +16,26 @@
|
|||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@astrojs/mdx": "^4.3.10",
|
"@astrojs/mdx": "^4.3.12",
|
||||||
"@astrojs/react": "^4.4.2",
|
"@astrojs/react": "^4.4.2",
|
||||||
"@astrojs/sitemap": "^3.6.0",
|
"@astrojs/sitemap": "^3.6.0",
|
||||||
"@kevisual/query": "^0.0.29",
|
"@kevisual/query": "^0.0.29",
|
||||||
"@kevisual/query-login": "^0.0.6",
|
"@kevisual/query-login": "^0.0.7",
|
||||||
"@kevisual/registry": "^0.0.1",
|
"@kevisual/registry": "^0.0.1",
|
||||||
"@radix-ui/react-slot": "^1.2.4",
|
"@radix-ui/react-slot": "^1.2.4",
|
||||||
"@tailwindcss/vite": "^4.1.17",
|
"@tailwindcss/vite": "^4.1.17",
|
||||||
"astro": "^5.15.4",
|
"@uiw/react-md-editor": "^4.0.8",
|
||||||
|
"antd": "^6.0.0",
|
||||||
|
"astro": "^5.16.0",
|
||||||
"class-variance-authority": "^0.7.1",
|
"class-variance-authority": "^0.7.1",
|
||||||
"clsx": "^2.1.1",
|
"clsx": "^2.1.1",
|
||||||
"dayjs": "^1.11.19",
|
"dayjs": "^1.11.19",
|
||||||
"es-toolkit": "^1.41.0",
|
"es-toolkit": "^1.42.0",
|
||||||
"lucide-react": "^0.553.0",
|
"github-markdown-css": "^5.8.1",
|
||||||
|
"highlight.js": "^11.11.1",
|
||||||
|
"lucide-react": "^0.554.0",
|
||||||
|
"marked": "^17.0.1",
|
||||||
|
"marked-highlight": "^2.2.3",
|
||||||
"nanoid": "^5.1.6",
|
"nanoid": "^5.1.6",
|
||||||
"react": "^19.2.0",
|
"react": "^19.2.0",
|
||||||
"react-dom": "^19.2.0",
|
"react-dom": "^19.2.0",
|
||||||
@@ -42,14 +48,13 @@
|
|||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@kevisual/types": "^0.0.10",
|
"@kevisual/types": "^0.0.10",
|
||||||
"@types/react": "^19.2.2",
|
"@types/react": "^19.2.7",
|
||||||
"@types/react-dom": "^19.2.2",
|
"@types/react-dom": "^19.2.3",
|
||||||
"@vitejs/plugin-basic-ssl": "^2.1.0",
|
|
||||||
"dotenv": "^17.2.3",
|
"dotenv": "^17.2.3",
|
||||||
"tailwindcss": "^4.1.17",
|
"tailwindcss": "^4.1.17",
|
||||||
"tw-animate-css": "^1.4.0"
|
"tw-animate-css": "^1.4.0"
|
||||||
},
|
},
|
||||||
"packageManager": "pnpm@10.20.0",
|
"packageManager": "pnpm@10.23.0",
|
||||||
"onlyBuiltDependencies": [
|
"onlyBuiltDependencies": [
|
||||||
"@tailwindcss/oxide",
|
"@tailwindcss/oxide",
|
||||||
"esbuild",
|
"esbuild",
|
||||||
|
|||||||
1803
pnpm-lock.yaml
generated
1803
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
41
src/apps/footer.tsx
Normal file
41
src/apps/footer.tsx
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
import { wrapBasename } from "@/modules/basename"
|
||||||
|
|
||||||
|
export const Footer = () => {
|
||||||
|
|
||||||
|
const links = [
|
||||||
|
{
|
||||||
|
href: wrapBasename('/'),
|
||||||
|
label: '主页',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
href: wrapBasename('/docs'),
|
||||||
|
label: '文档',
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
return (
|
||||||
|
<footer className="fixed bottom-0 w-full bg-white border-t border-gray-200 shadow-lg">
|
||||||
|
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-4">
|
||||||
|
{/* 链接区域 */}
|
||||||
|
<nav className="flex flex-wrap justify-center items-center gap-2 sm:gap-4 mb-3">
|
||||||
|
{links.map((link) => (
|
||||||
|
<a
|
||||||
|
key={link.href}
|
||||||
|
href={link.href}
|
||||||
|
className="relative px-4 py-2 text-sm sm:text-base font-medium text-gray-600 hover:text-blue-600 transition-all duration-300 ease-in-out
|
||||||
|
before:absolute before:bottom-0 before:left-0 before:w-0 before:h-0.5 before:bg-blue-600 before:transition-all before:duration-300
|
||||||
|
hover:before:w-full active:scale-95"
|
||||||
|
>
|
||||||
|
{link.label}
|
||||||
|
</a>
|
||||||
|
))}
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
{/* 版权信息 */}
|
||||||
|
<div className="text-center text-xs sm:text-sm text-gray-500">
|
||||||
|
© 2025 Daily Question
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</footer>
|
||||||
|
)
|
||||||
|
}
|
||||||
56
src/apps/menu.tsx
Normal file
56
src/apps/menu.tsx
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
import { useMemo } from "react";
|
||||||
|
|
||||||
|
export type MenuProps = {
|
||||||
|
items: MenuItem[];
|
||||||
|
basename?: string;
|
||||||
|
};
|
||||||
|
export type MenuItem = {
|
||||||
|
id: string;
|
||||||
|
data: {
|
||||||
|
title: string;
|
||||||
|
tags: string[];
|
||||||
|
hideInMenu?: boolean;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export const Menu = (props: MenuProps) => {
|
||||||
|
const { items, basename = '' } = props;
|
||||||
|
const list = useMemo(() => {
|
||||||
|
return items.filter(item => !item.data?.hideInMenu);
|
||||||
|
}, [items]);
|
||||||
|
if (list.length === 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<nav className='flex-1 overflow-y-auto scrollbar bg-sidebar border border-sidebar-border rounded-lg p-4 shadow-sm'>
|
||||||
|
<h2 className="text-sm font-semibold text-sidebar-foreground">列表</h2>
|
||||||
|
<div className="space-y-1">
|
||||||
|
{list.map(item => (
|
||||||
|
<a
|
||||||
|
key={item.id}
|
||||||
|
href={`${basename}/docs/${item.id}/`}
|
||||||
|
className="group block rounded-md hover:bg-sidebar-accent transition-all duration-200 ease-in-out"
|
||||||
|
>
|
||||||
|
<div className="px-3 py-2.5">
|
||||||
|
<h3 className="text-sm font-semibold text-sidebar-foreground group-hover:text-sidebar-accent-foreground transition-colors">
|
||||||
|
{item.data?.title}
|
||||||
|
</h3>
|
||||||
|
{item.data?.tags && item.data.tags.length > 0 && (
|
||||||
|
<div className="flex flex-wrap gap-1.5 mt-2">
|
||||||
|
{item.data.tags.map(tag => (
|
||||||
|
<span
|
||||||
|
key={tag}
|
||||||
|
className="text-xs px-2 py-0.5 rounded-full bg-muted/60 text-muted-foreground font-medium hover:bg-muted transition-colors"
|
||||||
|
>
|
||||||
|
{tag}
|
||||||
|
</span>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
@@ -3,13 +3,20 @@ import { defineCollection, z } from 'astro:content';
|
|||||||
import { glob, file } from 'astro/loaders'; // 不适用于旧版 API
|
import { glob, file } from 'astro/loaders'; // 不适用于旧版 API
|
||||||
|
|
||||||
const docs = defineCollection({
|
const docs = defineCollection({
|
||||||
// loader: glob({ pattern: '**/*.md', base: './src/data/blogs' }),
|
|
||||||
loader: glob({ pattern: '**/[^_]*.md', base: './src/data/docs' }),
|
loader: glob({ pattern: '**/[^_]*.md', base: './src/data/docs' }),
|
||||||
schema: z.object({
|
schema: z.object({
|
||||||
title: z.string().optional(),
|
title: z.string().optional(),
|
||||||
description: z.string().optional(),
|
description: z.string().optional(),
|
||||||
|
tags: z.array(z.string()).optional(),
|
||||||
// pubDate: z.coerce.date(),
|
// pubDate: z.coerce.date(),
|
||||||
// updatedDate: z.coerce.date().optional(),
|
createdAt: z.coerce.date().optional(),
|
||||||
|
updatedAt: z.coerce.date().optional(),
|
||||||
|
showMenu: z.boolean().optional().default(true),
|
||||||
|
/**
|
||||||
|
* 在侧边栏隐藏该文档
|
||||||
|
*/
|
||||||
|
hideInMenu: z.boolean().optional().default(false),
|
||||||
|
order: z.number().optional().default(0),
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
8
src/data/docs/menu.md
Normal file
8
src/data/docs/menu.md
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
---
|
||||||
|
title: 'astro 概览'
|
||||||
|
tags: ['astro', 'simple', 'template']
|
||||||
|
createdAt: '2025-11-25 20:00:00'
|
||||||
|
hideInMenu: true
|
||||||
|
---
|
||||||
|
|
||||||
|
## 概览
|
||||||
@@ -1,6 +1,8 @@
|
|||||||
---
|
---
|
||||||
title: 'astro 例子'
|
title: 'astro 例子'
|
||||||
tags: ['astro', 'simple', 'template']
|
tags: ['astro', 'simple', 'template']
|
||||||
|
createdAt: '2025-11-25 20:00:00'
|
||||||
|
hideInMenu: true
|
||||||
---
|
---
|
||||||
|
|
||||||
## astro-simplate-template
|
## astro-simplate-template
|
||||||
|
|||||||
@@ -5,29 +5,34 @@ export interface Props {
|
|||||||
import '../styles/global.css';
|
import '../styles/global.css';
|
||||||
import '../styles/theme.css';
|
import '../styles/theme.css';
|
||||||
import 'github-markdown-css/github-markdown-light.css';
|
import 'github-markdown-css/github-markdown-light.css';
|
||||||
|
import { Menu, MenuItem } from '../apps/menu';
|
||||||
export interface Props {
|
export interface Props {
|
||||||
title?: string;
|
title?: string;
|
||||||
description?: string;
|
description?: string;
|
||||||
lang?: string;
|
lang?: string;
|
||||||
charset?: string;
|
charset?: string;
|
||||||
|
showMenu?: boolean;
|
||||||
|
menu?: MenuItem[];
|
||||||
|
basename?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const { title = 'Light Code', description = 'A lightweight code editor', lang = 'zh-CN', charset = 'UTF-8' } = Astro.props;
|
const {
|
||||||
|
title = 'Light Code',
|
||||||
|
description = 'A lightweight code editor',
|
||||||
|
lang = 'zh-CN',
|
||||||
|
charset = 'UTF-8',
|
||||||
|
showMenu = true,
|
||||||
|
menu,
|
||||||
|
basename = '',
|
||||||
|
} = Astro.props;
|
||||||
---
|
---
|
||||||
|
|
||||||
<html lang='zh-CN'>
|
<html lang={lang}>
|
||||||
<head>
|
<head>
|
||||||
<meta charset='UTF-8' />
|
<meta charset={charset} />
|
||||||
<meta name='viewport' content='width=device-width, initial-scale=1.0' />
|
<meta name='viewport' content='width=device-width, initial-scale=1.0' />
|
||||||
<title>Docs</title>
|
<title>{title}</title>
|
||||||
<link
|
<meta name='description' content={description} />
|
||||||
rel='stylesheet'
|
|
||||||
href='https://cdnjs.cloudflare.com/ajax/libs/github-markdown-css/5.8.1/github-markdown-light.min.css'
|
|
||||||
integrity='sha512-X175XRJAO6PHAUi8AA7GP8uUF5Wiv+w9bOi64i02CHKDQBsO1yy0jLSKaUKg/NhRCDYBmOLQCfKaTaXiyZlLrw=='
|
|
||||||
crossorigin='anonymous'
|
|
||||||
referrerpolicy='no-referrer'
|
|
||||||
/>
|
|
||||||
<style>
|
<style>
|
||||||
html,
|
html,
|
||||||
body {
|
body {
|
||||||
@@ -37,25 +42,31 @@ const { title = 'Light Code', description = 'A lightweight code editor', lang =
|
|||||||
padding: 0;
|
padding: 0;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
body {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body class='flex flex-col items-center bg-background'>
|
||||||
<div>
|
<div class='w-full'>
|
||||||
<slot name='header' />
|
<slot name='header' />
|
||||||
</div>
|
</div>
|
||||||
<main class='p-2 flex-1 overflow-hidden'>
|
<main class='flex-1 flex overflow-hidden w-full max-w-7xl px-4 py-4'>
|
||||||
<div class='markdown-body h-full scrollbar border-gray-200 overflow-auto px-6 py-2 w-[800px] border my-4 rounded-md shadow-md'>
|
{
|
||||||
<slot />
|
showMenu && (
|
||||||
|
<aside class='w-64 min-w-64 h-full flex flex-col'>
|
||||||
|
<slot name='menu'>
|
||||||
|
<Menu items={menu!} client:only basename={basename} />
|
||||||
|
</slot>
|
||||||
|
</aside>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
<div class='flex-1 h-full flex items-start justify-center overflow-hidden'>
|
||||||
|
<article class='markdown-body h-full scrollbar overflow-auto px-8 py-6 w-full max-w-4xl border border-border rounded-lg shadow-sm bg-card'>
|
||||||
|
<slot />
|
||||||
|
</article>
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
<footer>
|
<footer class='w-full border-t border-border bg-card/50 backdrop-blur-sm'>
|
||||||
<slot name='footer'>
|
<slot name='footer'>
|
||||||
<p>Copyrignt © 2025</p>
|
<div class='text-center text-sm text-muted-foreground py-4'>Copyright © 2025</div>
|
||||||
</slot>
|
</slot>
|
||||||
</footer>
|
</footer>
|
||||||
</body>
|
</body>
|
||||||
|
|||||||
@@ -1,4 +1,12 @@
|
|||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
export const basename = BASE_NAME;
|
export const basename = BASE_NAME;
|
||||||
|
|
||||||
console.log(basename);
|
console.log(basename);
|
||||||
|
|
||||||
|
export const wrapBasename = (path: string) => {
|
||||||
|
if (basename) {
|
||||||
|
return `${basename}${path}/`;
|
||||||
|
} else {
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
}
|
||||||
15
src/modules/query.ts
Normal file
15
src/modules/query.ts
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
import { Query } from '@kevisual/query'
|
||||||
|
|
||||||
|
const getUrl = () => {
|
||||||
|
const host = window.location.host
|
||||||
|
const isKevisual = host.includes('kevisual');
|
||||||
|
if (isKevisual) {
|
||||||
|
return '/api/router'
|
||||||
|
}
|
||||||
|
|
||||||
|
return '/client/router'
|
||||||
|
}
|
||||||
|
|
||||||
|
export const query = new Query({
|
||||||
|
url: getUrl()
|
||||||
|
});
|
||||||
@@ -1,23 +1,27 @@
|
|||||||
---
|
---
|
||||||
import { getCollection, render } from 'astro:content';
|
import { getCollection, render } from 'astro:content';
|
||||||
import Main from '@/layouts/mdx.astro';
|
import Main from '@/layouts/mdx.astro';
|
||||||
|
import { basename } from '@/modules/basename';
|
||||||
// 1. 为每个集合条目生成一个新路径
|
// 1. 为每个集合条目生成一个新路径
|
||||||
export async function getStaticPaths() {
|
export async function getStaticPaths() {
|
||||||
const posts = await getCollection('docs');
|
const posts = await getCollection('docs');
|
||||||
return posts.map((post) => ({
|
return posts.map((post) => ({
|
||||||
params: { id: post.id },
|
params: { id: post.id },
|
||||||
props: { post },
|
props: { post },
|
||||||
|
data: post,
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
type Post = {
|
type Post = {
|
||||||
data: { title: string };
|
data: { title: string; tags: string[]; showMenu?: boolean };
|
||||||
};
|
};
|
||||||
// 2. 对于你的模板,你可以直接从 prop 获取条目
|
// 2. 对于你的模板,你可以直接从 prop 获取条目
|
||||||
const { post } = Astro.props as { post: Post };
|
const { post } = Astro.props as { post: Post };
|
||||||
const { Content } = await render(post);
|
const { Content } = await render(post);
|
||||||
|
const showMenu = post.data?.showMenu;
|
||||||
|
const staticPaths = await getStaticPaths();
|
||||||
|
const menu = staticPaths.map((item) => item.data);
|
||||||
---
|
---
|
||||||
|
|
||||||
<Main>
|
<Main showMenu={showMenu} menu={menu} basename={basename}>
|
||||||
<!-- <h1 slot={'header'}>{post.data.title}</h1> -->
|
|
||||||
<Content />
|
<Content />
|
||||||
</Main>
|
</Main>
|
||||||
|
|||||||
@@ -1,23 +1,82 @@
|
|||||||
---
|
---
|
||||||
import { getCollection } from 'astro:content';
|
import { getCollection } from 'astro:content';
|
||||||
const posts = await getCollection('docs');
|
const posts = await getCollection('docs');
|
||||||
console.log('post', posts);
|
|
||||||
import { basename } from '@/modules/basename';
|
import { basename } from '@/modules/basename';
|
||||||
import Blank from '@/layouts/blank.astro';
|
import Blank from '@/layouts/blank.astro';
|
||||||
---
|
---
|
||||||
|
|
||||||
<Blank>
|
<Blank>
|
||||||
<main class='max-w-3xl mx-auto'>
|
<main class='min-h-screen bg-linear-to-br from-slate-50 to-slate-100 dark:from-slate-900 dark:to-slate-800'>
|
||||||
<h1>My posts</h1>
|
<div class='max-w-4xl mx-auto px-4 sm:px-6 lg:px-8 py-12'>
|
||||||
<ul class='p-2 m-2'>
|
{/* 页面标题区域 */}
|
||||||
|
<div class='mb-12'>
|
||||||
|
<h1
|
||||||
|
class='text-4xl sm:text-5xl font-bold text-slate-900 dark:text-white mb-4 bg-clip-text bg-linear-to-r from-blue-600 to-purple-600'>
|
||||||
|
📚 文档列表
|
||||||
|
</h1>
|
||||||
|
<p class='text-slate-600 dark:text-slate-400 text-lg'>浏览所有可用的文档资源</p>
|
||||||
|
<div class='mt-4 h-1 w-20 bg-linear-to-r from-blue-600 to-purple-600 rounded-full'></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* 文档列表 */}
|
||||||
|
<div class='space-y-4'>
|
||||||
|
{
|
||||||
|
posts.map((post) => {
|
||||||
|
const tags = post.data.tags || [];
|
||||||
|
return (
|
||||||
|
<article class='group bg-white dark:bg-slate-800 rounded-xl shadow-sm hover:shadow-xl transition-all duration-300 overflow-hidden border border-slate-200 dark:border-slate-700 hover:border-blue-500 dark:hover:border-blue-400'>
|
||||||
|
<div class='p-6'>
|
||||||
|
{/* 文档标题 */}
|
||||||
|
<a href={`${basename}/docs/${post.id}/`} class='block'>
|
||||||
|
<h2 class='text-xl sm:text-2xl font-semibold text-slate-900 dark:text-white group-hover:text-blue-600 dark:group-hover:text-blue-400 transition-colors duration-200 mb-3'>
|
||||||
|
{post.data.title}
|
||||||
|
</h2>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
{/* 文档描述(如果有) */}
|
||||||
|
{post.data.description && <p class='text-slate-600 dark:text-slate-400 mb-4 line-clamp-2'>{post.data.description}</p>}
|
||||||
|
|
||||||
|
{/* 标签列表 */}
|
||||||
|
{tags.length > 0 && (
|
||||||
|
<div class='flex flex-wrap gap-2 mt-4'>
|
||||||
|
{tags.map((tag) => (
|
||||||
|
<div class='inline-flex items-center px-3 py-1 rounded-full text-sm font-medium bg-blue-50 dark:bg-blue-900/30 text-blue-700 dark:text-blue-300 hover:bg-blue-100 dark:hover:bg-blue-900/50 transition-colors duration-200 border border-blue-200 dark:border-blue-800'>
|
||||||
|
<span class='mr-1'>#</span>
|
||||||
|
{tag}
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* 阅读更多指示器 */}
|
||||||
|
<a
|
||||||
|
href={`${basename}/docs/${post.id}/`}
|
||||||
|
class='mt-4 flex items-center text-blue-600 dark:text-blue-400 text-sm font-medium opacity-0 group-hover:opacity-100 transition-opacity duration-200'>
|
||||||
|
<span>阅读更多</span>
|
||||||
|
<svg
|
||||||
|
class='w-4 h-4 ml-1 transform group-hover:translate-x-1 transition-transform duration-200'
|
||||||
|
fill='none'
|
||||||
|
viewBox='0 0 24 24'
|
||||||
|
stroke='currentColor'>
|
||||||
|
<path stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M9 5l7 7-7 7' />
|
||||||
|
</svg>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</article>
|
||||||
|
);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* 空状态 */}
|
||||||
{
|
{
|
||||||
posts.map((post) => (
|
posts.length === 0 && (
|
||||||
<li>
|
<div class='text-center py-16'>
|
||||||
{/* <a href={`${basename}/demo/${post.id}`}>{post.data.title}</a> */}
|
<div class='text-6xl mb-4'>📭</div>
|
||||||
<a href={`/docs/${post.id}/`}>{post.data.title}</a>
|
<p class='text-xl text-slate-600 dark:text-slate-400'>暂无文档</p>
|
||||||
</li>
|
</div>
|
||||||
))
|
)
|
||||||
}
|
}
|
||||||
</ul>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
</Blank>
|
</Blank>
|
||||||
|
|||||||
Reference in New Issue
Block a user