新增模块: - 友链(friendlink)后端处理 + 前端管理页与友链板 - 书库(library):导入、章节、封面、阅读页、横竖版自适应 AdaptiveCoverSlot - 顶栏导航(header_nav)配置与 MobileTabBar/MobileRailDrawers 移动端抽屉 - 排行榜 service 测试、站点页面测试、时间线发布(timeline_release) 其它改动: - 后端 handlers/services 全量小幅调整 - 前端组件、库函数、URL/品牌/站点 URL 工具更新 - Lightbox 图片、MdEntries 条目卡、coverColor 派生色相等前端能力
136 lines
4.6 KiB
TypeScript
136 lines
4.6 KiB
TypeScript
"use client";
|
||
|
||
import { useEffect, useState } from "react";
|
||
import { AdminSettingsActions, AdminSettingsGroup } from "@/components/admin";
|
||
import { apiUpdateSiteSettings, type PublicSettings } from "@/lib/api";
|
||
import {
|
||
announcementPath,
|
||
leaderboardPath,
|
||
libraryDocPath,
|
||
libraryPath,
|
||
pagePath,
|
||
postPath,
|
||
userPath,
|
||
type UrlStyle,
|
||
} from "@/lib/urlStyle";
|
||
import { toast } from "@/lib/toast";
|
||
|
||
/** 三档风格展示信息:示例 id/slug 固定,方便站长对比形态 */
|
||
const STYLE_OPTIONS: { value: UrlStyle; label: string; desc: string }[] = [
|
||
{ value: "default", label: "标准动态", desc: "框架默认路由,带路径分段" },
|
||
{ value: "html", label: "静态页 .html", desc: "常见 CMS 伪静态形态" },
|
||
{ value: "discuz", label: "Discuz 风格", desc: "经典论坛 thread- 形态" },
|
||
];
|
||
|
||
function samplePaths(style: UrlStyle) {
|
||
return [
|
||
{ kind: "帖子", path: postPath(style, 123) },
|
||
{ kind: "用户", path: userPath(style, 5) },
|
||
{ kind: "公告", path: announcementPath(style, 1) },
|
||
{ kind: "自建页", path: pagePath(style, "about") },
|
||
{ kind: "排行榜", path: leaderboardPath(style) },
|
||
{ kind: "书库", path: libraryPath(style) },
|
||
{ kind: "书籍", path: libraryDocPath(style, "slug") },
|
||
];
|
||
}
|
||
|
||
export default function UrlStylePanel({
|
||
initial,
|
||
onSaved,
|
||
onDirtyChange,
|
||
}: {
|
||
initial: PublicSettings;
|
||
onSaved?: (next: PublicSettings) => void;
|
||
onDirtyChange?: (dirty: boolean) => void;
|
||
}) {
|
||
const [style, setStyle] = useState<UrlStyle>(initial.url_style);
|
||
const [savedStyle, setSavedStyle] = useState<UrlStyle>(initial.url_style);
|
||
const [saving, setSaving] = useState(false);
|
||
const [error, setError] = useState("");
|
||
|
||
const dirty = style !== savedStyle;
|
||
|
||
useEffect(() => {
|
||
onDirtyChange?.(dirty);
|
||
}, [dirty, onDirtyChange]);
|
||
|
||
const discard = () => {
|
||
setStyle(savedStyle);
|
||
setError("");
|
||
};
|
||
|
||
const save = async () => {
|
||
if (saving || !dirty) return;
|
||
setSaving(true);
|
||
setError("");
|
||
try {
|
||
const res = await apiUpdateSiteSettings({ url_style: style });
|
||
setSavedStyle(res.url_style);
|
||
setStyle(res.url_style);
|
||
onSaved?.(res);
|
||
toast("已保存,全站链接风格已切换", "ok");
|
||
} catch (e) {
|
||
const msg = e instanceof Error ? e.message : "保存失败";
|
||
setError(msg);
|
||
toast(msg);
|
||
} finally {
|
||
setSaving(false);
|
||
}
|
||
};
|
||
|
||
return (
|
||
<AdminSettingsGroup
|
||
id="settings-url-style"
|
||
title="链接风格(伪静态)"
|
||
description="帖子、用户主页、公告等详情页的 URL 形态。"
|
||
footer={
|
||
<AdminSettingsActions
|
||
dirty={dirty}
|
||
saving={saving}
|
||
saveLabel="保存"
|
||
error={error}
|
||
onSave={() => void save()}
|
||
onDiscard={discard}
|
||
/>
|
||
}
|
||
>
|
||
<div className="grid gap-3 sm:grid-cols-3" role="radiogroup" aria-label="链接风格">
|
||
{STYLE_OPTIONS.map((opt) => {
|
||
const on = style === opt.value;
|
||
return (
|
||
<button
|
||
key={opt.value}
|
||
type="button"
|
||
role="radio"
|
||
aria-checked={on}
|
||
className={`admin-url-style-card${on ? " is-on" : ""}`}
|
||
onClick={() => setStyle(opt.value)}
|
||
>
|
||
<span className="flex items-center justify-between gap-2">
|
||
<span className="text-[13.5px] font-semibold">{opt.label}</span>
|
||
<span className={`admin-url-style-dot${on ? " is-on" : ""}`} aria-hidden />
|
||
</span>
|
||
<span className="meta mt-0.5 block text-[11.5px]">{opt.desc}</span>
|
||
<span className="admin-url-style-examples">
|
||
{samplePaths(opt.value).map((s) => (
|
||
<span key={s.kind} className="admin-url-style-example">
|
||
<span className="meta">{s.kind}</span>
|
||
<code>{s.path}</code>
|
||
</span>
|
||
))}
|
||
</span>
|
||
</button>
|
||
);
|
||
})}
|
||
</div>
|
||
|
||
<ul className="admin-url-style-notes meta mt-3 text-[12px]">
|
||
<li>保存后全站前台链接立即按新风格生成;旧风格链接自动 301 到新风格,收录与外链不断链。</li>
|
||
<li>301 会被浏览器长期缓存,切换前请想清楚;建议长期固定一种风格,不要频繁来回切换。</li>
|
||
<li>自建页与排行榜同样参与伪静态;自建页 slug 含中文时自动 URL 编码,地址栏多数浏览器仍显示中文。</li>
|
||
<li>书库列表与书籍详情参与伪静态(两种伪静态风格同模板);阅读页保持标准路径不参与。</li>
|
||
</ul>
|
||
</AdminSettingsGroup>
|
||
);
|
||
}
|