feat: 友链/书库/移动端导航/排行榜等多模块功能
新增模块: - 友链(friendlink)后端处理 + 前端管理页与友链板 - 书库(library):导入、章节、封面、阅读页、横竖版自适应 AdaptiveCoverSlot - 顶栏导航(header_nav)配置与 MobileTabBar/MobileRailDrawers 移动端抽屉 - 排行榜 service 测试、站点页面测试、时间线发布(timeline_release) 其它改动: - 后端 handlers/services 全量小幅调整 - 前端组件、库函数、URL/品牌/站点 URL 工具更新 - Lightbox 图片、MdEntries 条目卡、coverColor 派生色相等前端能力
This commit is contained in:
@@ -40,6 +40,34 @@ function kindBadge(kind: string) {
|
||||
);
|
||||
}
|
||||
|
||||
/** 日期筛选:复用搜索框壳层样式,与工具条同高同视觉 */
|
||||
function DateField({
|
||||
label,
|
||||
value,
|
||||
onChange,
|
||||
ariaLabel,
|
||||
}: {
|
||||
label: string;
|
||||
value: string;
|
||||
onChange: (v: string) => void;
|
||||
ariaLabel: string;
|
||||
}) {
|
||||
return (
|
||||
<label className="admin-field-shell admin-search-shell w-full sm:w-44">
|
||||
<span className="shrink-0 text-[12px]" style={{ color: "var(--ink-3)" }}>
|
||||
{label}
|
||||
</span>
|
||||
<input
|
||||
type="date"
|
||||
value={value}
|
||||
onChange={(e) => onChange(e.target.value)}
|
||||
className="admin-field-input"
|
||||
aria-label={ariaLabel}
|
||||
/>
|
||||
</label>
|
||||
);
|
||||
}
|
||||
|
||||
function DetailsInner() {
|
||||
const searchParams = useSearchParams();
|
||||
// 初始筛选只读一次(如从「爬虫与安全」跳转 ?ip=x.x.x.x)
|
||||
@@ -86,6 +114,17 @@ function DetailsInner() {
|
||||
|
||||
const resetPage = () => setPage(1);
|
||||
|
||||
const hasFilter = Boolean(kind || path || ip || bot || start || end);
|
||||
const resetAll = () => {
|
||||
setKind("");
|
||||
setPath("");
|
||||
setIp("");
|
||||
setBot("");
|
||||
setStart("");
|
||||
setEnd("");
|
||||
setPage(1);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="flex flex-col gap-4">
|
||||
<p className="meta text-[12.5px]">
|
||||
@@ -106,48 +145,38 @@ function DetailsInner() {
|
||||
ariaLabel="按路径筛选"
|
||||
/>
|
||||
</div>
|
||||
<div className="w-full sm:w-44">
|
||||
<div className="w-full sm:w-48">
|
||||
<AdminSearchField
|
||||
value={ip}
|
||||
onChange={setIp}
|
||||
onSubmit={resetPage}
|
||||
onClear={() => { setIp(""); resetPage(); }}
|
||||
placeholder="按 IP 筛选,回车应用"
|
||||
placeholder="按 IP 精确筛选"
|
||||
ariaLabel="按 IP 筛选"
|
||||
/>
|
||||
</div>
|
||||
<div className="w-full sm:w-40">
|
||||
<div className="w-full sm:w-44">
|
||||
<AdminSearchField
|
||||
value={bot}
|
||||
onChange={setBot}
|
||||
onSubmit={resetPage}
|
||||
onClear={() => { setBot(""); resetPage(); }}
|
||||
placeholder="按爬虫名筛选"
|
||||
placeholder="按爬虫名精确筛选"
|
||||
ariaLabel="按爬虫名筛选"
|
||||
/>
|
||||
</div>
|
||||
<label className="flex items-center gap-1.5 text-[12px]" style={{ color: "var(--ink-3)" }}>
|
||||
从
|
||||
<input
|
||||
type="date"
|
||||
value={start}
|
||||
onChange={(e) => { setStart(e.target.value); resetPage(); }}
|
||||
className="admin-field-input"
|
||||
style={{ width: 150 }}
|
||||
aria-label="开始日期"
|
||||
/>
|
||||
</label>
|
||||
<label className="flex items-center gap-1.5 text-[12px]" style={{ color: "var(--ink-3)" }}>
|
||||
至
|
||||
<input
|
||||
type="date"
|
||||
value={end}
|
||||
onChange={(e) => { setEnd(e.target.value); resetPage(); }}
|
||||
className="admin-field-input"
|
||||
style={{ width: 150 }}
|
||||
aria-label="结束日期"
|
||||
/>
|
||||
</label>
|
||||
<DateField label="从" value={start} onChange={(v) => { setStart(v); resetPage(); }} ariaLabel="开始日期" />
|
||||
<DateField label="至" value={end} onChange={(v) => { setEnd(v); resetPage(); }} ariaLabel="结束日期" />
|
||||
{hasFilter ? (
|
||||
<button
|
||||
type="button"
|
||||
onClick={resetAll}
|
||||
className="admin-field-shell admin-search-shell w-full sm:w-auto justify-center text-[13px] cursor-pointer transition-colors hover:bg-[var(--panel-2)]"
|
||||
style={{ color: "var(--ink-2)" }}
|
||||
>
|
||||
重置筛选
|
||||
</button>
|
||||
) : null}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user