This commit is contained in:
1 parent
5df6fe45e8
commit
7b8358005f
3 files changed
+137
-5
No files matched your search
@@ -11,18 +11,54 @@ interface PostListRow {
|
|||||||
category?: string;
|
category?: string;
|
||||||
series?: string;
|
series?: string;
|
||||||
}
|
}
|
||||||
export const load: PageServerLoad = async ({ platform }) => {
|
export const load: PageServerLoad = async ({ url, platform }) => {
|
||||||
const db = platform?.env?.DB;
|
const db = platform?.env?.DB;
|
||||||
|
|
||||||
if (!db) {
|
if (!db) {
|
||||||
return {
|
return {
|
||||||
posts: []
|
posts: [],
|
||||||
|
filterTag: null
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const tagIdParam = url.searchParams.get('tagId');
|
||||||
|
const tagId = tagIdParam ? parseInt(tagIdParam, 10) : null;
|
||||||
|
let filterTag: { id: number; name: string } | null = null;
|
||||||
|
|
||||||
|
if (tagId && !Number.isNaN(tagId)) {
|
||||||
|
try {
|
||||||
|
const tagRes = await db
|
||||||
|
.prepare('SELECT id, name FROM tags WHERE id = ?')
|
||||||
|
.bind(tagId)
|
||||||
|
.first<{ id: number; name: string }>();
|
||||||
|
if (tagRes) {
|
||||||
|
filterTag = tagRes;
|
||||||
|
}
|
||||||
|
} catch (err: any) {
|
||||||
|
console.error('Error fetching filter tag:', err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
let results: PostListRow[];
|
let results: PostListRow[];
|
||||||
try {
|
try {
|
||||||
|
if (filterTag) {
|
||||||
|
const queryRes = await db
|
||||||
|
.prepare(
|
||||||
|
`SELECT p.id, p.title, p.slug, p.published_at as date, u.username as author,
|
||||||
|
c.name as category, s.name as series
|
||||||
|
FROM posts p
|
||||||
|
INNER JOIN post_tags pt ON p.id = pt.post_id
|
||||||
|
LEFT JOIN users u ON p.author_id = u.id
|
||||||
|
LEFT JOIN categories c ON p.category_id = c.id
|
||||||
|
LEFT JOIN series s ON p.series_id = s.id
|
||||||
|
WHERE pt.tag_id = ?
|
||||||
|
ORDER BY p.published_at DESC`
|
||||||
|
)
|
||||||
|
.bind(filterTag.id)
|
||||||
|
.all<PostListRow>();
|
||||||
|
results = queryRes.results;
|
||||||
|
} else {
|
||||||
const queryRes = await db
|
const queryRes = await db
|
||||||
.prepare(
|
.prepare(
|
||||||
`SELECT p.id, p.title, p.slug, p.published_at as date, u.username as author,
|
`SELECT p.id, p.title, p.slug, p.published_at as date, u.username as author,
|
||||||
@@ -35,8 +71,23 @@ export const load: PageServerLoad = async ({ platform }) => {
|
|||||||
)
|
)
|
||||||
.all<PostListRow>();
|
.all<PostListRow>();
|
||||||
results = queryRes.results;
|
results = queryRes.results;
|
||||||
|
}
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
// Fallback query if categories table or category_id column is missing
|
// Fallback query if categories table or category_id column is missing
|
||||||
|
if (filterTag) {
|
||||||
|
const queryRes = await db
|
||||||
|
.prepare(
|
||||||
|
`SELECT p.id, p.title, p.slug, p.published_at as date, u.username as author
|
||||||
|
FROM posts p
|
||||||
|
INNER JOIN post_tags pt ON p.id = pt.post_id
|
||||||
|
LEFT JOIN users u ON p.author_id = u.id
|
||||||
|
WHERE pt.tag_id = ?
|
||||||
|
ORDER BY p.published_at DESC`
|
||||||
|
)
|
||||||
|
.bind(filterTag.id)
|
||||||
|
.all<PostListRow>();
|
||||||
|
results = queryRes.results.map((r) => ({ ...r, category: '', series: '' }));
|
||||||
|
} else {
|
||||||
const queryRes = await db
|
const queryRes = await db
|
||||||
.prepare(
|
.prepare(
|
||||||
`SELECT p.id, p.title, p.slug, p.published_at as date, u.username as author
|
`SELECT p.id, p.title, p.slug, p.published_at as date, u.username as author
|
||||||
@@ -47,6 +98,7 @@ export const load: PageServerLoad = async ({ platform }) => {
|
|||||||
.all<PostListRow>();
|
.all<PostListRow>();
|
||||||
results = queryRes.results.map((r) => ({ ...r, category: '', series: '' }));
|
results = queryRes.results.map((r) => ({ ...r, category: '', series: '' }));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Format date for display
|
// Format date for display
|
||||||
const formattedPosts = results.map((post) => {
|
const formattedPosts = results.map((post) => {
|
||||||
@@ -62,11 +114,12 @@ export const load: PageServerLoad = async ({ platform }) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
posts: formattedPosts
|
posts: formattedPosts,
|
||||||
|
filterTag
|
||||||
};
|
};
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
console.error('Error fetching admin posts list:', err);
|
console.error('Error fetching admin posts list:', err);
|
||||||
return { posts: [] };
|
return { posts: [], filterTag };
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -28,6 +28,17 @@
|
|||||||
<Button href={resolve('/admin/posts/new')} variant="primary">撰写</Button>
|
<Button href={resolve('/admin/posts/new')} variant="primary">撰写</Button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{#if data.filterTag}
|
||||||
|
<div class="filter-banner">
|
||||||
|
<div class="filter-info">
|
||||||
|
<span>正在筛选{$page.data.aliases?.tag || '标签'}:</span>
|
||||||
|
<span class="filter-tag-badge">#{data.filterTag.name}</span>
|
||||||
|
<span class="filter-count">(共 {data.posts.length} 篇)</span>
|
||||||
|
</div>
|
||||||
|
<Button href={resolve('/admin/posts')} variant="action" size="sm">清除筛选</Button>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
|
||||||
{#if data.posts.length > 0}
|
{#if data.posts.length > 0}
|
||||||
<div class="batch-actions">
|
<div class="batch-actions">
|
||||||
{#if selectedIds.length > 0}
|
{#if selectedIds.length > 0}
|
||||||
@@ -66,7 +77,17 @@
|
|||||||
|
|
||||||
{#if data.posts.length === 0}
|
{#if data.posts.length === 0}
|
||||||
<div class="empty-state">
|
<div class="empty-state">
|
||||||
|
{#if data.filterTag}
|
||||||
|
<p>
|
||||||
|
{$page.data.aliases?.tag || '标签'} "{data.filterTag.name}" 下暂无关联{$page.data.aliases
|
||||||
|
?.post || '文章'}
|
||||||
|
</p>
|
||||||
|
<Button href={resolve('/admin/posts')} variant="action" size="sm"
|
||||||
|
>查看所有{$page.data.aliases?.post || '文章'}</Button
|
||||||
|
>
|
||||||
|
{:else}
|
||||||
<p>还没有发布任何{$page.data.aliases?.post || '文章'}</p>
|
<p>还没有发布任何{$page.data.aliases?.post || '文章'}</p>
|
||||||
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
{:else}
|
{:else}
|
||||||
<DataTable>
|
<DataTable>
|
||||||
@@ -197,4 +218,32 @@
|
|||||||
text-decoration: underline;
|
text-decoration: underline;
|
||||||
text-underline-offset: 4px;
|
text-underline-offset: 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.filter-banner {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
padding: 10px 16px;
|
||||||
|
background: rgba(0, 0, 0, 0.03);
|
||||||
|
border-left: 3px solid var(--accent-color, #419b38);
|
||||||
|
}
|
||||||
|
|
||||||
|
.filter-info {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
font-size: 0.95rem;
|
||||||
|
color: var(--text-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.filter-tag-badge {
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--accent-color, #419b38);
|
||||||
|
}
|
||||||
|
|
||||||
|
.filter-count {
|
||||||
|
color: var(--text-muted, rgba(0, 0, 0, 0.6));
|
||||||
|
font-size: 0.85rem;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
|
import { resolve } from '$app/paths';
|
||||||
import { page } from '$app/stores';
|
import { page } from '$app/stores';
|
||||||
import type { PageData, ActionData } from './$types';
|
import type { PageData, ActionData } from './$types';
|
||||||
import { confirmState } from '$lib/stores/confirm.svelte';
|
import { confirmState } from '$lib/stores/confirm.svelte';
|
||||||
@@ -76,7 +77,19 @@
|
|||||||
<strong>{tag.name}</strong>
|
<strong>{tag.name}</strong>
|
||||||
{/if}
|
{/if}
|
||||||
</td>
|
</td>
|
||||||
<td><span class="badge">{tag.post_count}</span></td>
|
<td>
|
||||||
|
{#if tag.post_count > 0}
|
||||||
|
<a
|
||||||
|
href="{resolve('/admin/posts')}?tagId={tag.id}"
|
||||||
|
class="badge badge-link"
|
||||||
|
title={`查看关联的 ${tag.post_count} 篇${$page.data.aliases?.post || '文章'}`}
|
||||||
|
>
|
||||||
|
{tag.post_count}
|
||||||
|
</a>
|
||||||
|
{:else}
|
||||||
|
<span class="badge badge-empty">{tag.post_count}</span>
|
||||||
|
{/if}
|
||||||
|
</td>
|
||||||
<td>
|
<td>
|
||||||
{#if editingId === tag.id}
|
{#if editingId === tag.id}
|
||||||
<form
|
<form
|
||||||
@@ -201,4 +214,21 @@
|
|||||||
text-align: center;
|
text-align: center;
|
||||||
color: var(--text-muted, rgba(0, 0, 0, 0.6));
|
color: var(--text-muted, rgba(0, 0, 0, 0.6));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.badge-link {
|
||||||
|
color: var(--accent-color, #419b38);
|
||||||
|
text-decoration: none;
|
||||||
|
cursor: pointer;
|
||||||
|
font-weight: 600;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.badge-link:hover {
|
||||||
|
background-color: var(--accent-color, #419b38);
|
||||||
|
color: #ffffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.badge-empty {
|
||||||
|
color: var(--text-muted, rgba(0, 0, 0, 0.4));
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
Reference in new issue
Block a user