feat: 在文章管理中支持关联系列及设置排序
This commit is contained in:
1 parent
2780add6a8
commit
495e453237
6 files changed
+184
-23
No files matched your search
@@ -7,6 +7,7 @@ interface PostListRow {
|
|||||||
date: string;
|
date: string;
|
||||||
author: string | null;
|
author: string | null;
|
||||||
category?: string;
|
category?: string;
|
||||||
|
series?: string;
|
||||||
}
|
}
|
||||||
export const load: PageServerLoad = async ({ platform }) => {
|
export const load: PageServerLoad = async ({ platform }) => {
|
||||||
const db = platform?.env?.DB;
|
const db = platform?.env?.DB;
|
||||||
@@ -21,10 +22,12 @@ export const load: PageServerLoad = async ({ platform }) => {
|
|||||||
let results: PostListRow[];
|
let results: PostListRow[];
|
||||||
try {
|
try {
|
||||||
const queryRes = await db.prepare(
|
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
|
`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
|
FROM posts p
|
||||||
LEFT JOIN users u ON p.author_id = u.id
|
LEFT JOIN users u ON p.author_id = u.id
|
||||||
LEFT JOIN categories c ON p.category_id = c.id
|
LEFT JOIN categories c ON p.category_id = c.id
|
||||||
|
LEFT JOIN series s ON p.series_id = s.id
|
||||||
ORDER BY p.published_at DESC`
|
ORDER BY p.published_at DESC`
|
||||||
).all<PostListRow>();
|
).all<PostListRow>();
|
||||||
results = queryRes.results;
|
results = queryRes.results;
|
||||||
@@ -36,7 +39,7 @@ export const load: PageServerLoad = async ({ platform }) => {
|
|||||||
LEFT JOIN users u ON p.author_id = u.id
|
LEFT JOIN users u ON p.author_id = u.id
|
||||||
ORDER BY p.published_at DESC`
|
ORDER BY p.published_at DESC`
|
||||||
).all<PostListRow>();
|
).all<PostListRow>();
|
||||||
results = queryRes.results.map(r => ({ ...r, category: '未分类' }));
|
results = queryRes.results.map(r => ({ ...r, category: '未分类', series: '' }));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Format date for display
|
// Format date for display
|
||||||
|
|||||||
@@ -58,6 +58,7 @@
|
|||||||
<th>ID</th>
|
<th>ID</th>
|
||||||
<th>标题</th>
|
<th>标题</th>
|
||||||
<th>分类</th>
|
<th>分类</th>
|
||||||
|
<th>{$page.data.aliases?.series || '系列'}</th>
|
||||||
<th>别名 (Slug)</th>
|
<th>别名 (Slug)</th>
|
||||||
<th>作者</th>
|
<th>作者</th>
|
||||||
<th>发布时间</th>
|
<th>发布时间</th>
|
||||||
@@ -78,6 +79,7 @@
|
|||||||
</a>
|
</a>
|
||||||
</td>
|
</td>
|
||||||
<td><span class="badge">{post.category || '未分类'}</span></td>
|
<td><span class="badge">{post.category || '未分类'}</span></td>
|
||||||
|
<td>{post.series || '—'}</td>
|
||||||
<td><span class="badge">{post.slug}</span></td>
|
<td><span class="badge">{post.slug}</span></td>
|
||||||
<td>{post.author || '未知'}</td>
|
<td>{post.author || '未知'}</td>
|
||||||
<td>{post.dateStr}</td>
|
<td>{post.dateStr}</td>
|
||||||
|
|||||||
@@ -1,5 +1,11 @@
|
|||||||
import { error, fail, redirect } from '@sveltejs/kit';
|
import { error, fail, isHttpError, redirect } from '@sveltejs/kit';
|
||||||
import type { Actions, PageServerLoad } from './$types';
|
import type { Actions, PageServerLoad } from './$types';
|
||||||
|
import {
|
||||||
|
parseSeriesId,
|
||||||
|
parseSeriesWeight,
|
||||||
|
resolveSeriesWeight,
|
||||||
|
SeriesValidationError
|
||||||
|
} from '$lib/server/series';
|
||||||
|
|
||||||
export const load: PageServerLoad = async ({ params, platform }) => {
|
export const load: PageServerLoad = async ({ params, platform }) => {
|
||||||
const db = platform?.env?.DB;
|
const db = platform?.env?.DB;
|
||||||
@@ -11,8 +17,8 @@ export const load: PageServerLoad = async ({ params, platform }) => {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
const post = await db.prepare(
|
const post = await db.prepare(
|
||||||
'SELECT id, title, slug, content, cover, category_id, published_at, updated_at FROM posts WHERE id = ?'
|
'SELECT id, title, slug, content, cover, category_id, series_id, series_weight, published_at, updated_at FROM posts WHERE id = ?'
|
||||||
).bind(postId).first<{ id: number; title: string; slug: string; content: string; cover: string | null; category_id: number | null; published_at?: string; updated_at?: string }>();
|
).bind(postId).first<{ id: number; title: string; slug: string; content: string; cover: string | null; category_id: number | null; series_id: number | null; series_weight: number | null; published_at?: string; updated_at?: string }>();
|
||||||
|
|
||||||
if (!post) {
|
if (!post) {
|
||||||
throw error(404, '文章不存在');
|
throw error(404, '文章不存在');
|
||||||
@@ -20,18 +26,20 @@ export const load: PageServerLoad = async ({ params, platform }) => {
|
|||||||
|
|
||||||
const { results: allTags } = await db.prepare('SELECT id, name FROM tags ORDER BY name ASC').all();
|
const { results: allTags } = await db.prepare('SELECT id, name FROM tags ORDER BY name ASC').all();
|
||||||
const { results: allCategories } = await db.prepare('SELECT id, name FROM categories ORDER BY name ASC').all();
|
const { results: allCategories } = await db.prepare('SELECT id, name FROM categories ORDER BY name ASC').all();
|
||||||
|
const { results: allSeries } = await db.prepare('SELECT id, name, slug FROM series ORDER BY name COLLATE NOCASE ASC').all();
|
||||||
|
|
||||||
const { results: selectedTagRows } = await db.prepare('SELECT tag_id FROM post_tags WHERE post_id = ?').bind(postId).all();
|
const { results: selectedTagRows } = await db.prepare('SELECT tag_id FROM post_tags WHERE post_id = ?').bind(postId).all<{ tag_id: number }>();
|
||||||
const selectedTagIds = selectedTagRows.map((r: any) => r.tag_id as number);
|
const selectedTagIds = selectedTagRows.map((row) => row.tag_id);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
post,
|
post,
|
||||||
tags: allTags as unknown as { id: number; name: string }[],
|
tags: allTags as unknown as { id: number; name: string }[],
|
||||||
categories: allCategories as unknown as { id: number; name: string }[],
|
categories: allCategories as unknown as { id: number; name: string }[],
|
||||||
|
series: allSeries as unknown as { id: number; name: string; slug: string }[],
|
||||||
selectedTagIds
|
selectedTagIds
|
||||||
};
|
};
|
||||||
} catch (err: any) {
|
} catch (err: unknown) {
|
||||||
if (err?.status === 404) throw err;
|
if (isHttpError(err, 404)) throw err;
|
||||||
console.error('Error loading post for edit:', err);
|
console.error('Error loading post for edit:', err);
|
||||||
throw error(500, '读取文章详情失败');
|
throw error(500, '读取文章详情失败');
|
||||||
}
|
}
|
||||||
@@ -58,20 +66,35 @@ export const actions: Actions = {
|
|||||||
return fail(400, { error: '标题、URL 别名和文章正文为必填项' });
|
return fail(400, { error: '标题、URL 别名和文章正文为必填项' });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let seriesId: number | null;
|
||||||
|
let seriesWeight: number | null;
|
||||||
|
try {
|
||||||
|
seriesId = parseSeriesId(formData.get('series_id'));
|
||||||
|
seriesWeight = await resolveSeriesWeight(
|
||||||
|
db,
|
||||||
|
seriesId,
|
||||||
|
parseSeriesWeight(formData.get('series_weight')),
|
||||||
|
postId
|
||||||
|
);
|
||||||
|
} catch (err) {
|
||||||
|
if (err instanceof SeriesValidationError) return fail(400, { error: err.message });
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// 更新文章表
|
// 更新文章表
|
||||||
if (publishedAt) {
|
if (publishedAt) {
|
||||||
await db.prepare(
|
await db.prepare(
|
||||||
`UPDATE posts
|
`UPDATE posts
|
||||||
SET title = ?, slug = ?, content = ?, cover = ?, category_id = ?, published_at = ?, updated_at = ?
|
SET title = ?, slug = ?, content = ?, cover = ?, category_id = ?, series_id = ?, series_weight = ?, published_at = ?, updated_at = ?
|
||||||
WHERE id = ?`
|
WHERE id = ?`
|
||||||
).bind(title, slug, content, cover, categoryId, publishedAt, updatedAt, postId).run();
|
).bind(title, slug, content, cover, categoryId, seriesId, seriesWeight, publishedAt, updatedAt, postId).run();
|
||||||
} else {
|
} else {
|
||||||
await db.prepare(
|
await db.prepare(
|
||||||
`UPDATE posts
|
`UPDATE posts
|
||||||
SET title = ?, slug = ?, content = ?, cover = ?, category_id = ?, updated_at = ?
|
SET title = ?, slug = ?, content = ?, cover = ?, category_id = ?, series_id = ?, series_weight = ?, updated_at = ?
|
||||||
WHERE id = ?`
|
WHERE id = ?`
|
||||||
).bind(title, slug, content, cover, categoryId, updatedAt, postId).run();
|
).bind(title, slug, content, cover, categoryId, seriesId, seriesWeight, updatedAt, postId).run();
|
||||||
}
|
}
|
||||||
|
|
||||||
// 清除原有标签并重新绑定
|
// 清除原有标签并重新绑定
|
||||||
@@ -82,9 +105,9 @@ export const actions: Actions = {
|
|||||||
await db.prepare('INSERT INTO post_tags (post_id, tag_id) VALUES (?, ?)').bind(postId, tagId).run();
|
await db.prepare('INSERT INTO post_tags (post_id, tag_id) VALUES (?, ?)').bind(postId, tagId).run();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (err: any) {
|
} catch (err: unknown) {
|
||||||
console.error('Error updating post:', err);
|
console.error('Error updating post:', err);
|
||||||
if (err?.message?.includes('UNIQUE constraint failed')) {
|
if (err instanceof Error && err.message.includes('UNIQUE constraint failed')) {
|
||||||
return fail(400, { error: 'URL 别名 (Slug) 已被其他文章占用' });
|
return fail(400, { error: 'URL 别名 (Slug) 已被其他文章占用' });
|
||||||
}
|
}
|
||||||
return fail(500, { error: '保存修改失败' });
|
return fail(500, { error: '保存修改失败' });
|
||||||
|
|||||||
@@ -16,9 +16,12 @@
|
|||||||
let publishedAt = $state(data.post.published_at || '');
|
let publishedAt = $state(data.post.published_at || '');
|
||||||
let updatedAt = $state(data.post.updated_at || '');
|
let updatedAt = $state(data.post.updated_at || '');
|
||||||
let selectedCategoryId = $state<number | string>(data.post.category_id ?? '');
|
let selectedCategoryId = $state<number | string>(data.post.category_id ?? '');
|
||||||
|
let selectedSeriesId = $state<number | string>(data.post.series_id ?? '');
|
||||||
|
let seriesWeight = $state<number | string>(data.post.series_weight ?? '');
|
||||||
let fileInput: HTMLInputElement;
|
let fileInput: HTMLInputElement;
|
||||||
let selectedTags = $state<number[]>(data.selectedTagIds || []);
|
let selectedTags = $state<number[]>(data.selectedTagIds || []);
|
||||||
let tagsList = $state(data.tags || []);
|
let tagsList = $state(data.tags || []);
|
||||||
|
let seriesList = $state(data.series || []);
|
||||||
|
|
||||||
function toggleTag(tagId: number) {
|
function toggleTag(tagId: number) {
|
||||||
if (selectedTags.includes(tagId)) {
|
if (selectedTags.includes(tagId)) {
|
||||||
@@ -169,7 +172,36 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2. 标签处理:同步点选或即刻新增入参
|
selectedSeriesId = '';
|
||||||
|
seriesWeight = imported.series_weight ?? '';
|
||||||
|
if (imported.series.length > 0) {
|
||||||
|
if (imported.series.length > 1) {
|
||||||
|
alert(`检测到多个系列:${imported.series.join('、')}。BlogFlare 每篇文章只能属于一个系列,本次将选择第一个系列。`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const seriesName = imported.series[0].trim();
|
||||||
|
let matchedSeries = seriesList.find(item => item.name.trim().toLowerCase() === seriesName.toLowerCase());
|
||||||
|
if (!matchedSeries && seriesName) {
|
||||||
|
const formData = new FormData();
|
||||||
|
formData.append('name', seriesName);
|
||||||
|
const response = await fetch('/admin/series?/create', {
|
||||||
|
method: 'POST',
|
||||||
|
body: formData,
|
||||||
|
headers: { 'x-sveltekit-action': 'true' }
|
||||||
|
});
|
||||||
|
const actionResult = deserialize(await response.text()) as any;
|
||||||
|
if (actionResult.type === 'success' && actionResult.data?.series) {
|
||||||
|
const createdSeries = actionResult.data.series as { id: number; name: string; slug: string };
|
||||||
|
matchedSeries = createdSeries;
|
||||||
|
seriesList = [...seriesList, createdSeries];
|
||||||
|
} else {
|
||||||
|
alert(actionResult.data?.error || `无法创建系列“${seriesName}”,请在系列管理中检查名称和 URL 别名。`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (matchedSeries) selectedSeriesId = matchedSeries.id;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. 标签处理:同步点选或即刻新增入参
|
||||||
for (const tagName of imported.tags) {
|
for (const tagName of imported.tags) {
|
||||||
const val = tagName.trim();
|
const val = tagName.trim();
|
||||||
if (!val) continue;
|
if (!val) continue;
|
||||||
@@ -331,6 +363,25 @@
|
|||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="sidebar-box">
|
||||||
|
<h3>{$page.data.aliases?.series || '系列'}</h3>
|
||||||
|
<select name="series_id" class="select-input" bind:value={selectedSeriesId}>
|
||||||
|
<option value="">-- 无 --</option>
|
||||||
|
{#each seriesList as series}
|
||||||
|
<option value={series.id}>{series.name}</option>
|
||||||
|
{/each}
|
||||||
|
</select>
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
name="series_weight"
|
||||||
|
class="sidebar-input series-weight-input"
|
||||||
|
bind:value={seriesWeight}
|
||||||
|
placeholder="顺序(留空自动追加)"
|
||||||
|
disabled={!selectedSeriesId}
|
||||||
|
step="1"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="sidebar-box">
|
<div class="sidebar-box">
|
||||||
<h3>标签</h3>
|
<h3>标签</h3>
|
||||||
<div class="selected-tags-vertical">
|
<div class="selected-tags-vertical">
|
||||||
@@ -611,6 +662,10 @@
|
|||||||
border-bottom: none;
|
border-bottom: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.series-weight-input {
|
||||||
|
margin-top: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
.selected-tags-vertical {
|
.selected-tags-vertical {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
|||||||
@@ -1,23 +1,32 @@
|
|||||||
import { fail, redirect } from '@sveltejs/kit';
|
import { fail, redirect } from '@sveltejs/kit';
|
||||||
import type { Actions, PageServerLoad } from './$types';
|
import type { Actions, PageServerLoad } from './$types';
|
||||||
|
import {
|
||||||
|
parseSeriesId,
|
||||||
|
parseSeriesWeight,
|
||||||
|
resolveSeriesWeight,
|
||||||
|
SeriesValidationError
|
||||||
|
} from '$lib/server/series';
|
||||||
|
|
||||||
export const load: PageServerLoad = async ({ platform }) => {
|
export const load: PageServerLoad = async ({ platform }) => {
|
||||||
const db = platform?.env?.DB;
|
const db = platform?.env?.DB;
|
||||||
let tags: { id: number; name: string }[] = [];
|
let tags: { id: number; name: string }[] = [];
|
||||||
let categories: { id: number; name: string }[] = [];
|
let categories: { id: number; name: string }[] = [];
|
||||||
|
let series: { id: number; name: string; slug: string }[] = [];
|
||||||
|
|
||||||
if (db) {
|
if (db) {
|
||||||
try {
|
try {
|
||||||
const { results: tagResults } = await db.prepare('SELECT id, name FROM tags ORDER BY name ASC').all();
|
const { results: tagResults } = await db.prepare('SELECT id, name FROM tags ORDER BY name ASC').all();
|
||||||
const { results: catResults } = await db.prepare('SELECT id, name FROM categories ORDER BY name ASC').all();
|
const { results: catResults } = await db.prepare('SELECT id, name FROM categories ORDER BY name ASC').all();
|
||||||
|
const { results: seriesResults } = await db.prepare('SELECT id, name, slug FROM series ORDER BY name COLLATE NOCASE ASC').all();
|
||||||
|
|
||||||
tags = tagResults as unknown as { id: number; name: string }[];
|
tags = tagResults as unknown as { id: number; name: string }[];
|
||||||
categories = catResults as unknown as { id: number; name: string }[];
|
categories = catResults as unknown as { id: number; name: string }[];
|
||||||
|
series = seriesResults as unknown as { id: number; name: string; slug: string }[];
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('Error fetching tags or categories:', err);
|
console.error('Error fetching tags or categories:', err);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return { tags, categories };
|
return { tags, categories, series };
|
||||||
};
|
};
|
||||||
|
|
||||||
export const actions: Actions = {
|
export const actions: Actions = {
|
||||||
@@ -39,12 +48,26 @@ export const actions: Actions = {
|
|||||||
return fail(400, { error: '标题、URL 别名和文章正文为必填项' });
|
return fail(400, { error: '标题、URL 别名和文章正文为必填项' });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let seriesId: number | null;
|
||||||
|
let seriesWeight: number | null;
|
||||||
|
try {
|
||||||
|
seriesId = parseSeriesId(formData.get('series_id'));
|
||||||
|
seriesWeight = await resolveSeriesWeight(
|
||||||
|
db,
|
||||||
|
seriesId,
|
||||||
|
parseSeriesWeight(formData.get('series_weight'))
|
||||||
|
);
|
||||||
|
} catch (err) {
|
||||||
|
if (err instanceof SeriesValidationError) return fail(400, { error: err.message });
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const res = await db.prepare(
|
const res = await db.prepare(
|
||||||
`INSERT INTO posts (title, slug, content, cover, author_id, category_id, published_at, updated_at)
|
`INSERT INTO posts (title, slug, content, cover, author_id, category_id, series_id, series_weight, published_at, updated_at)
|
||||||
VALUES (?, ?, ?, ?, 1, ?, ?, ?)
|
VALUES (?, ?, ?, ?, 1, ?, ?, ?, ?, ?)
|
||||||
RETURNING id`
|
RETURNING id`
|
||||||
).bind(title, slug, content, cover, categoryId, publishedAt, updatedAt).first<{ id: number }>();
|
).bind(title, slug, content, cover, categoryId, seriesId, seriesWeight, publishedAt, updatedAt).first<{ id: number }>();
|
||||||
|
|
||||||
const postId = res?.id;
|
const postId = res?.id;
|
||||||
|
|
||||||
@@ -53,9 +76,9 @@ export const actions: Actions = {
|
|||||||
await db.prepare('INSERT INTO post_tags (post_id, tag_id) VALUES (?, ?)').bind(postId, tagId).run();
|
await db.prepare('INSERT INTO post_tags (post_id, tag_id) VALUES (?, ?)').bind(postId, tagId).run();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (err: any) {
|
} catch (err: unknown) {
|
||||||
console.error('Error creating post:', err);
|
console.error('Error creating post:', err);
|
||||||
if (err?.message?.includes('UNIQUE constraint failed')) {
|
if (err instanceof Error && err.message.includes('UNIQUE constraint failed')) {
|
||||||
return fail(400, { error: 'URL 别名 (Slug) 已被占用,请使用其他别名' });
|
return fail(400, { error: 'URL 别名 (Slug) 已被占用,请使用其他别名' });
|
||||||
}
|
}
|
||||||
return fail(500, { error: '保存文章失败' });
|
return fail(500, { error: '保存文章失败' });
|
||||||
|
|||||||
@@ -16,9 +16,12 @@
|
|||||||
let publishedAt = $state('');
|
let publishedAt = $state('');
|
||||||
let updatedAt = $state('');
|
let updatedAt = $state('');
|
||||||
let selectedCategoryId = $state<string | number>('');
|
let selectedCategoryId = $state<string | number>('');
|
||||||
|
let selectedSeriesId = $state<string | number>('');
|
||||||
|
let seriesWeight = $state<string | number>('');
|
||||||
let fileInput: HTMLInputElement;
|
let fileInput: HTMLInputElement;
|
||||||
|
|
||||||
let tagsList = $state(data.tags || []);
|
let tagsList = $state(data.tags || []);
|
||||||
|
let seriesList = $state(data.series || []);
|
||||||
|
|
||||||
// 自动从标题生成 Slug 简易逻辑
|
// 自动从标题生成 Slug 简易逻辑
|
||||||
function handleTitleInput() {
|
function handleTitleInput() {
|
||||||
@@ -175,7 +178,36 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2. 标签匹配:库有即点取,不知则自主申请向库内补充建好
|
selectedSeriesId = '';
|
||||||
|
seriesWeight = imported.series_weight ?? '';
|
||||||
|
if (imported.series.length > 0) {
|
||||||
|
if (imported.series.length > 1) {
|
||||||
|
alert(`检测到多个系列:${imported.series.join('、')}。BlogFlare 每篇文章只能属于一个系列,本次将选择第一个系列。`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const seriesName = imported.series[0].trim();
|
||||||
|
let matchedSeries = seriesList.find(item => item.name.trim().toLowerCase() === seriesName.toLowerCase());
|
||||||
|
if (!matchedSeries && seriesName) {
|
||||||
|
const formData = new FormData();
|
||||||
|
formData.append('name', seriesName);
|
||||||
|
const response = await fetch('/admin/series?/create', {
|
||||||
|
method: 'POST',
|
||||||
|
body: formData,
|
||||||
|
headers: { 'x-sveltekit-action': 'true' }
|
||||||
|
});
|
||||||
|
const actionResult = deserialize(await response.text()) as any;
|
||||||
|
if (actionResult.type === 'success' && actionResult.data?.series) {
|
||||||
|
const createdSeries = actionResult.data.series as { id: number; name: string; slug: string };
|
||||||
|
matchedSeries = createdSeries;
|
||||||
|
seriesList = [...seriesList, createdSeries];
|
||||||
|
} else {
|
||||||
|
alert(actionResult.data?.error || `无法创建系列“${seriesName}”,请在系列管理中检查名称和 URL 别名。`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (matchedSeries) selectedSeriesId = matchedSeries.id;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. 标签匹配:库有即点取,不知则自主申请向库内补充建好
|
||||||
for (const tagName of imported.tags) {
|
for (const tagName of imported.tags) {
|
||||||
const val = tagName.trim();
|
const val = tagName.trim();
|
||||||
if (!val) continue;
|
if (!val) continue;
|
||||||
@@ -337,6 +369,25 @@
|
|||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="sidebar-box">
|
||||||
|
<h3>{$page.data.aliases?.series || '系列'}</h3>
|
||||||
|
<select name="series_id" class="select-input" bind:value={selectedSeriesId}>
|
||||||
|
<option value="">-- 无 --</option>
|
||||||
|
{#each seriesList as series}
|
||||||
|
<option value={series.id}>{series.name}</option>
|
||||||
|
{/each}
|
||||||
|
</select>
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
name="series_weight"
|
||||||
|
class="sidebar-input series-weight-input"
|
||||||
|
bind:value={seriesWeight}
|
||||||
|
placeholder="顺序(留空自动追加)"
|
||||||
|
disabled={!selectedSeriesId}
|
||||||
|
step="1"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="sidebar-box">
|
<div class="sidebar-box">
|
||||||
<h3>标签</h3>
|
<h3>标签</h3>
|
||||||
<div class="selected-tags-vertical">
|
<div class="selected-tags-vertical">
|
||||||
@@ -616,6 +667,10 @@
|
|||||||
border-bottom: none;
|
border-bottom: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.series-weight-input {
|
||||||
|
margin-top: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
.selected-tags-vertical {
|
.selected-tags-vertical {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
|||||||
Reference in new issue
Block a user