feat(episode-mapping): 映射规则接口与模拟预览、待处理确认 / 忽略、作品页集数映射分区与订阅摘要
Co-Authored-By: Claude Opus 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_01Cxwgnn2pbzQ7rXv3rq7fei
This commit is contained in:
1 parent
fe597187dd
commit
45a114263b
26 files changed
+2770
-9
No files matched your search
@@ -0,0 +1,80 @@
|
||||
import { ChevronRight, Info } from "lucide-react";
|
||||
import { Link } from "react-router";
|
||||
import { useEpisodeMappingPreview } from "~/api/episode-mapping.ts";
|
||||
import { useEntityDetail } from "~/api/library.ts";
|
||||
import { consolePath } from "~/components/console/nav.ts";
|
||||
import { StatusBadge } from "~/components/console/status-badge.tsx";
|
||||
import { Panel } from "~/components/library/parts.tsx";
|
||||
import { absoluteGroups, groupMethodsText, mappingHref } from "./model.ts";
|
||||
|
||||
// 订阅详情的集数映射摘要 + 订阅表单的绝对集号提示(批次 4)。数据来自模拟预览(不带草稿 = 当前规则)。
|
||||
|
||||
/** 订阅的季:有季单元取它的季号,否则不按季过滤 */
|
||||
function useSubscriptionSeason(entityId: string, seasonUnitId: string | null | undefined): number | null {
|
||||
const entity = useEntityDetail(seasonUnitId ? entityId : undefined);
|
||||
if (!seasonUnitId) return null;
|
||||
return entity.data?.contentUnits.find((u) => u.id === seasonUnitId)?.seasonNumber ?? null;
|
||||
}
|
||||
|
||||
export function SubscriptionMappingSummary({
|
||||
entityId,
|
||||
seasonUnitId,
|
||||
}: {
|
||||
entityId: string;
|
||||
seasonUnitId?: string | null;
|
||||
}) {
|
||||
const season = useSubscriptionSeason(entityId, seasonUnitId);
|
||||
const preview = useEpisodeMappingPreview(entityId, season != null ? { season } : {});
|
||||
const p = preview.data;
|
||||
if (!p?.applicable || (p.groups.length === 0 && p.seasons.seasons.length === 0)) return null;
|
||||
const pending = p.groups.reduce((n, g) => n + g.pending, 0);
|
||||
return (
|
||||
<Panel
|
||||
title="集数映射"
|
||||
description={`${season != null ? `第 ${season} 季` : "全部季"}最近资源按发布组的识别方式`}
|
||||
actions={
|
||||
<Link
|
||||
to={mappingHref(consolePath("library", entityId))}
|
||||
className="inline-flex items-center text-meta text-muted-foreground hover:text-foreground hover:underline"
|
||||
>
|
||||
到作品页调整
|
||||
<ChevronRight className="h-3 w-3" />
|
||||
</Link>
|
||||
}
|
||||
>
|
||||
{p.groups.length ? (
|
||||
<ul className="space-y-1 text-caption text-foreground">
|
||||
{p.groups.map((g) => (
|
||||
<li key={g.releaseGroup ?? "-"}>{groupMethodsText(g)}</li>
|
||||
))}
|
||||
</ul>
|
||||
) : (
|
||||
<p className="text-caption text-muted-foreground">还没有绑定到这一季的资源。</p>
|
||||
)}
|
||||
{pending ? (
|
||||
<div className="mt-2">
|
||||
<StatusBadge tone="warning">待确认 {pending} 条</StatusBadge>
|
||||
</div>
|
||||
) : null}
|
||||
</Panel>
|
||||
);
|
||||
}
|
||||
|
||||
/** 订阅表单:作品的近期资源里有发布组用绝对集号时提示一句,不阻塞 */
|
||||
export function AbsoluteNumberingHint({ entityId }: { entityId: string | null }) {
|
||||
const preview = useEpisodeMappingPreview(entityId, {});
|
||||
const groups = absoluteGroups(preview.data);
|
||||
if (!entityId || !groups.length) return null;
|
||||
return (
|
||||
<p className="flex items-start gap-1.5 text-meta text-muted-foreground">
|
||||
<Info aria-hidden className="mt-0.5 h-3.5 w-3.5 shrink-0" />
|
||||
<span>
|
||||
{groups.map((g) => `「${g}」`).join("")}用的是绝对集号,已自动换算成季内集号;不对的话到
|
||||
<Link className="mx-0.5 underline-offset-4 hover:underline" to={mappingHref(consolePath("library", entityId))}>
|
||||
作品页的集数映射
|
||||
</Link>
|
||||
调整。
|
||||
</span>
|
||||
</p>
|
||||
);
|
||||
}
|
||||
Reference in new issue
Block a user