文案(媒体库组件):状态标签按术语表统一,去掉候选/退役/已预留等内部说法
Co-Authored-By: Claude Opus 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_01Cxwgnn2pbzQ7rXv3rq7fei
This commit is contained in:
1 parent
542f107566
commit
9c7ce109ea
10 files changed
+48
-48
No files matched your search
@@ -102,13 +102,13 @@ export function IdentityDialog({
|
|||||||
<DialogHeader>
|
<DialogHeader>
|
||||||
<DialogTitle>修改外部身份</DialogTitle>
|
<DialogTitle>修改外部身份</DialogTitle>
|
||||||
<DialogDescription>
|
<DialogDescription>
|
||||||
同类型的旧 TMDB 身份会退役,保留修订记录。加人工锁后,自动匹配和刷新不再改动。
|
旧的 TMDB 身份停用,修订记录保留。加人工锁后,自动匹配和刷新不会再改。
|
||||||
</DialogDescription>
|
</DialogDescription>
|
||||||
</DialogHeader>
|
</DialogHeader>
|
||||||
<form onSubmit={submit} className="grid gap-3">
|
<form onSubmit={submit} className="grid gap-3">
|
||||||
{usable.length ? (
|
{usable.length ? (
|
||||||
<div className="grid gap-1.5">
|
<div className="grid gap-1.5">
|
||||||
<p className="text-caption font-medium text-foreground">候选</p>
|
<p className="text-caption font-medium text-foreground">已记录的身份</p>
|
||||||
<ul className="grid gap-1.5">
|
<ul className="grid gap-1.5">
|
||||||
{usable.map((c) => (
|
{usable.map((c) => (
|
||||||
<li key={c.id}>
|
<li key={c.id}>
|
||||||
@@ -227,7 +227,7 @@ export function FieldLockDialog({
|
|||||||
if (field === "year") {
|
if (field === "year") {
|
||||||
const n = Number(r.data.value);
|
const n = Number(r.data.value);
|
||||||
if (!Number.isInteger(n) || n < 1870 || n > 2200) {
|
if (!Number.isInteger(n) || n < 1870 || n > 2200) {
|
||||||
setErrors({ value: "年份须是 1870–2200 的整数" });
|
setErrors({ value: "年份填 1870–2200 之间的整数" });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
v = n;
|
v = n;
|
||||||
|
|||||||
@@ -150,7 +150,7 @@ export function EnrichSection({ item }: { item: IdentityPoolItem }) {
|
|||||||
{!out ? (
|
{!out ? (
|
||||||
<p className="text-caption text-muted-foreground">
|
<p className="text-caption text-muted-foreground">
|
||||||
TMDB
|
TMDB
|
||||||
查不到时,按标题搜索网页:读平台和百科网站,其它网站只用搜索摘要,抽取字段并附出处和置信度。先看建议,确认后才建作品。
|
查不到时,按标题搜索网页,从平台和百科页面抽取字段,附出处和置信度。确认后才建作品。
|
||||||
</p>
|
</p>
|
||||||
) : (
|
) : (
|
||||||
<div className="space-y-3">
|
<div className="space-y-3">
|
||||||
|
|||||||
@@ -160,12 +160,12 @@ function ManualAssign({ item }: { item: IdentityPoolItem }) {
|
|||||||
await dismiss.mutateAsync({ id: item.id });
|
await dismiss.mutateAsync({ id: item.id });
|
||||||
toast.success("已忽略");
|
toast.success("已忽略");
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
toast.error(errorMessage(err, "操作失败"));
|
toast.error(errorMessage(err, "忽略失败"));
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<BellOff />
|
<BellOff />
|
||||||
忽略此资源
|
忽略这个资源
|
||||||
</Button>
|
</Button>
|
||||||
</DetailSection>
|
</DetailSection>
|
||||||
);
|
);
|
||||||
@@ -229,7 +229,7 @@ export function PoolSheet({ item, onClose }: { item: IdentityPoolItem | null; on
|
|||||||
<EnrichSection key={item.id} item={item} />
|
<EnrichSection key={item.id} item={item} />
|
||||||
) : null}
|
) : null}
|
||||||
|
|
||||||
<DetailSection title={`候选(${item.candidates.length})`}>
|
<DetailSection title={`候选作品(${item.candidates.length})`}>
|
||||||
{item.candidates.length ? (
|
{item.candidates.length ? (
|
||||||
<ul className="space-y-2">
|
<ul className="space-y-2">
|
||||||
{item.candidates.map((c, i) => (
|
{item.candidates.map((c, i) => (
|
||||||
@@ -245,7 +245,7 @@ export function PoolSheet({ item, onClose }: { item: IdentityPoolItem | null; on
|
|||||||
<p className="text-caption text-muted-foreground">
|
<p className="text-caption text-muted-foreground">
|
||||||
{item.reason === "unmatched"
|
{item.reason === "unmatched"
|
||||||
? "没有命中订阅或本地作品,未查询 TMDB。点「重新识别」会查一次 TMDB;新增相近别名时自动重试。"
|
? "没有命中订阅或本地作品,未查询 TMDB。点「重新识别」会查一次 TMDB;新增相近别名时自动重试。"
|
||||||
: "没有候选。有新证据时自动重试。"}
|
: "没有候选作品。有新证据时自动重试。"}
|
||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
</DetailSection>
|
</DetailSection>
|
||||||
|
|||||||
@@ -73,7 +73,7 @@ export function PoolTab() {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: "cands",
|
key: "cands",
|
||||||
header: "候选",
|
header: "候选作品",
|
||||||
hideBelow: "sm",
|
hideBelow: "sm",
|
||||||
align: "right",
|
align: "right",
|
||||||
cell: (r) => <span className="tabular-nums">{r.candidates.length}</span>,
|
cell: (r) => <span className="tabular-nums">{r.candidates.length}</span>,
|
||||||
|
|||||||
@@ -43,12 +43,12 @@ export const ENTITY_STATUS: Record<MediaEntityStatus, ToneLabel> = {
|
|||||||
/** Slot 状态(REQ §6.1):每个状态有明确文本,不只靠颜色 */
|
/** Slot 状态(REQ §6.1):每个状态有明确文本,不只靠颜色 */
|
||||||
export const SLOT_STATUS: Record<SlotStatus, ToneLabel> = {
|
export const SLOT_STATUS: Record<SlotStatus, ToneLabel> = {
|
||||||
empty: { label: "空缺", tone: "neutral" },
|
empty: { label: "空缺", tone: "neutral" },
|
||||||
reserved: { label: "已预留", tone: "neutral" },
|
reserved: { label: "准备下载", tone: "neutral" },
|
||||||
downloading: { label: "下载中", tone: "neutral" },
|
downloading: { label: "下载中", tone: "neutral" },
|
||||||
verifying: { label: "核验中", tone: "neutral" },
|
verifying: { label: "核验中", tone: "neutral" },
|
||||||
satisfied: { label: "已满足", tone: "success" },
|
satisfied: { label: "已满足", tone: "success" },
|
||||||
upgrade_pending: { label: "等待升级", tone: "warning" },
|
upgrade_pending: { label: "等待升级", tone: "warning" },
|
||||||
attention: { label: "需处理", tone: "destructive" },
|
attention: { label: "需要处理", tone: "destructive" },
|
||||||
};
|
};
|
||||||
export const SLOT_STATUS_ORDER: SlotStatus[] = [
|
export const SLOT_STATUS_ORDER: SlotStatus[] = [
|
||||||
"attention",
|
"attention",
|
||||||
@@ -64,7 +64,7 @@ export const IDENTITY_STATUS: Record<IdentityStatus, ToneLabel> = {
|
|||||||
accepted: { label: "已确认", tone: "success" },
|
accepted: { label: "已确认", tone: "success" },
|
||||||
proposed: { label: "待确认", tone: "warning" },
|
proposed: { label: "待确认", tone: "warning" },
|
||||||
conflicted: { label: "冲突", tone: "destructive" },
|
conflicted: { label: "冲突", tone: "destructive" },
|
||||||
retired: { label: "已退役", tone: "neutral" },
|
retired: { label: "已停用", tone: "neutral" },
|
||||||
};
|
};
|
||||||
|
|
||||||
export const EVIDENCE_LEVEL: Record<string, string> = {
|
export const EVIDENCE_LEVEL: Record<string, string> = {
|
||||||
@@ -100,7 +100,7 @@ export const LOCATION_KIND: Record<string, string> = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const DOWNLOAD_STATUS: Record<DownloadStatus, ToneLabel> = {
|
export const DOWNLOAD_STATUS: Record<DownloadStatus, ToneLabel> = {
|
||||||
queued: { label: "排队", tone: "neutral" },
|
queued: { label: "排队中", tone: "neutral" },
|
||||||
submitting: { label: "提交中", tone: "neutral" },
|
submitting: { label: "提交中", tone: "neutral" },
|
||||||
downloading: { label: "下载中", tone: "neutral" },
|
downloading: { label: "下载中", tone: "neutral" },
|
||||||
paused: { label: "已暂停", tone: "warning" },
|
paused: { label: "已暂停", tone: "warning" },
|
||||||
@@ -110,15 +110,15 @@ export const DOWNLOAD_STATUS: Record<DownloadStatus, ToneLabel> = {
|
|||||||
imported: { label: "已整理", tone: "success" },
|
imported: { label: "已整理", tone: "success" },
|
||||||
failed: { label: "失败", tone: "destructive" },
|
failed: { label: "失败", tone: "destructive" },
|
||||||
removed: { label: "已移除", tone: "neutral" },
|
removed: { label: "已移除", tone: "neutral" },
|
||||||
missing: { label: "下载器中缺失", tone: "warning" },
|
missing: { label: "下载器里没有", tone: "warning" },
|
||||||
};
|
};
|
||||||
|
|
||||||
export const ORG_STATUS: Record<OrganizationStatus, ToneLabel> = {
|
export const ORG_STATUS: Record<OrganizationStatus, ToneLabel> = {
|
||||||
queued: { label: "排队", tone: "neutral" },
|
queued: { label: "排队中", tone: "neutral" },
|
||||||
running: { label: "整理中", tone: "neutral" },
|
running: { label: "整理中", tone: "neutral" },
|
||||||
succeeded: { label: "核验通过", tone: "success" },
|
succeeded: { label: "已整理", tone: "success" },
|
||||||
partial: { label: "部分成功", tone: "warning" },
|
partial: { label: "部分整理", tone: "warning" },
|
||||||
failed: { label: "失败", tone: "destructive" },
|
failed: { label: "整理失败", tone: "destructive" },
|
||||||
cancelled: { label: "已取消", tone: "neutral" },
|
cancelled: { label: "已取消", tone: "neutral" },
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -127,8 +127,8 @@ export const CANDIDATE_DECISION: Record<string, ToneLabel> = {
|
|||||||
upgrade: { label: "可升级", tone: "success" },
|
upgrade: { label: "可升级", tone: "success" },
|
||||||
reject: { label: "拒绝", tone: "destructive" },
|
reject: { label: "拒绝", tone: "destructive" },
|
||||||
unknown_field: { label: "字段未知", tone: "warning" },
|
unknown_field: { label: "字段未知", tone: "warning" },
|
||||||
skipped: { label: "跳过", tone: "neutral" },
|
skipped: { label: "已跳过", tone: "neutral" },
|
||||||
deferred: { label: "延后:已有计划在途", tone: "warning" },
|
deferred: { label: "延后:已有下载在进行", tone: "warning" },
|
||||||
};
|
};
|
||||||
|
|
||||||
export const DECISION_KIND: Record<DecisionKind, string> = {
|
export const DECISION_KIND: Record<DecisionKind, string> = {
|
||||||
@@ -141,7 +141,7 @@ export const DECISION_KIND: Record<DecisionKind, string> = {
|
|||||||
space_insufficient: "空间不足",
|
space_insufficient: "空间不足",
|
||||||
approval_required: "需要审批",
|
approval_required: "需要审批",
|
||||||
metadata_conflict: "元数据冲突",
|
metadata_conflict: "元数据冲突",
|
||||||
source_unreachable: "来源不可达",
|
source_unreachable: "来源连不上",
|
||||||
downloader_offline: "下载器连不上",
|
downloader_offline: "下载器连不上",
|
||||||
cache_volume_unknown: "下载目录读不到容量",
|
cache_volume_unknown: "下载目录读不到容量",
|
||||||
other: "其他",
|
other: "其他",
|
||||||
@@ -163,7 +163,7 @@ export const SEVERITY: Record<Severity, ToneLabel> = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const POOL_REASON: Record<IdentityCandidateReason, ToneLabel> = {
|
export const POOL_REASON: Record<IdentityCandidateReason, ToneLabel> = {
|
||||||
ambiguous: { label: "多个候选", tone: "warning" },
|
ambiguous: { label: "匹配到多部作品", tone: "warning" },
|
||||||
no_match: { label: "没有匹配", tone: "neutral" },
|
no_match: { label: "没有匹配", tone: "neutral" },
|
||||||
proposed: { label: "待确认", tone: "warning" },
|
proposed: { label: "待确认", tone: "warning" },
|
||||||
lookup_failed: { label: "查询失败", tone: "destructive" },
|
lookup_failed: { label: "查询失败", tone: "destructive" },
|
||||||
@@ -207,7 +207,7 @@ export const POLICY_STATUS: Record<PolicyStatus, ToneLabel> = {
|
|||||||
export const SUB_MODE: Record<SubscriptionMode, string> = { once: "单次收录", continuous: "持续追踪" };
|
export const SUB_MODE: Record<SubscriptionMode, string> = { once: "单次收录", continuous: "持续追踪" };
|
||||||
export const SEASON_SCOPE: Record<SeasonScope, string> = {
|
export const SEASON_SCOPE: Record<SeasonScope, string> = {
|
||||||
current_season: "仅本季",
|
current_season: "仅本季",
|
||||||
following_seasons: "接续后续季",
|
following_seasons: "包括后续季",
|
||||||
};
|
};
|
||||||
export const PRIORITY: Record<AcquisitionPriority, string> = {
|
export const PRIORITY: Record<AcquisitionPriority, string> = {
|
||||||
immediate: "立即",
|
immediate: "立即",
|
||||||
@@ -336,9 +336,9 @@ export const opts = <K extends string>(rec: Record<K, string | { label: string }
|
|||||||
/** 收录版本 / 候选的原因串(如 `blocked:no_main_video`、`deferred:slot_busy;inflight:<id>`)→ 中文;不认识的原样显示 */
|
/** 收录版本 / 候选的原因串(如 `blocked:no_main_video`、`deferred:slot_busy;inflight:<id>`)→ 中文;不认识的原样显示 */
|
||||||
const SLOT_REASON: Record<string, string> = {
|
const SLOT_REASON: Record<string, string> = {
|
||||||
"blocked:no_main_video": "没有可整理的主视频",
|
"blocked:no_main_video": "没有可整理的主视频",
|
||||||
"deferred:slot_busy": "已有计划在途,等它结束再评估",
|
"deferred:slot_busy": "已有下载在进行,结束后再评估",
|
||||||
verified: "已核验整理",
|
verified: "已整理",
|
||||||
verified_partial: "部分核验整理",
|
verified_partial: "部分整理",
|
||||||
hard_conditions_met: "硬条件全部满足",
|
hard_conditions_met: "硬条件全部满足",
|
||||||
};
|
};
|
||||||
export function slotReasonText(raw: string): string {
|
export function slotReasonText(raw: string): string {
|
||||||
|
|||||||
@@ -108,8 +108,8 @@ export function PreferenceFormDialog({
|
|||||||
<DialogTitle>{tag ? "编辑偏好" : "添加偏好"}</DialogTitle>
|
<DialogTitle>{tag ? "编辑偏好" : "添加偏好"}</DialogTitle>
|
||||||
<DialogDescription>
|
<DialogDescription>
|
||||||
{tag
|
{tag
|
||||||
? "修改作用范围或规则会留下旧语义的抑制记录,智能体不会按旧证据加回。"
|
? "改了作用范围或规则后,智能体不会再按旧证据把它加回来。"
|
||||||
: "手动添加等同对话确认。之前取消过的偏好,重新添加会解除抑制。"}
|
: "手动添加和在对话里确认效果相同。之前取消过的偏好,重新添加后恢复生效。"}
|
||||||
</DialogDescription>
|
</DialogDescription>
|
||||||
</DialogHeader>
|
</DialogHeader>
|
||||||
<form onSubmit={submit} className="grid gap-2" noValidate>
|
<form onSubmit={submit} className="grid gap-2" noValidate>
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ export const SCRAPE_STATUS: Record<ScrapeRecord["status"], { label: string; tone
|
|||||||
|
|
||||||
const ACTION: Record<ScrapeFileResult["action"], { label: string; tone: StatusTone }> = {
|
const ACTION: Record<ScrapeFileResult["action"], { label: string; tone: StatusTone }> = {
|
||||||
written: { label: "已写入", tone: "success" },
|
written: { label: "已写入", tone: "success" },
|
||||||
skipped: { label: "跳过", tone: "neutral" },
|
skipped: { label: "已跳过", tone: "neutral" },
|
||||||
failed: { label: "失败", tone: "destructive" },
|
failed: { label: "失败", tone: "destructive" },
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -144,7 +144,7 @@ export function ScrapePanel({ detail }: { detail: LibraryDetail }) {
|
|||||||
scrape.mutateAsync(mode ? { mode } : {}).then(
|
scrape.mutateAsync(mode ? { mode } : {}).then(
|
||||||
(r) => toast.success(r.deduplicated ? "已在排队" : "已提交刮削"),
|
(r) => toast.success(r.deduplicated ? "已在排队" : "已提交刮削"),
|
||||||
(err) => {
|
(err) => {
|
||||||
toast.error(errorMessage(err, "刮削没有提交"));
|
toast.error(errorMessage(err, "刮削提交失败"));
|
||||||
throw err;
|
throw err;
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -78,7 +78,7 @@ export function SlotCard({
|
|||||||
</dl>
|
</dl>
|
||||||
{latest.length ? (
|
{latest.length ? (
|
||||||
<div className="mt-3 border-t border-border/60 pt-3">
|
<div className="mt-3 border-t border-border/60 pt-3">
|
||||||
<p className="mb-2 text-meta text-muted-foreground">最近候选及选用理由(每个资源最近一次评估)</p>
|
<p className="mb-2 text-meta text-muted-foreground">最近评估的资源和理由(每个资源只列最近一次)</p>
|
||||||
<ul className="space-y-2">
|
<ul className="space-y-2">
|
||||||
{latest.map((c) => (
|
{latest.map((c) => (
|
||||||
<li key={c.sourceReleaseId} className="min-w-0">
|
<li key={c.sourceReleaseId} className="min-w-0">
|
||||||
|
|||||||
@@ -53,7 +53,7 @@ const KIND_OPTS: { value: string; label: string }[] = SubscriptionMediaKind.opti
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
export const PUBLISH_NOTICE =
|
export const PUBLISH_NOTICE =
|
||||||
"发布后,作用范围内的下载、追更和整理按此策略自动执行,不再逐次审批。升级替换、媒体清理和下载缓存清理仍需单独审批。旧的已发布版本被替代,授权随之失效。";
|
"发布后,作用范围内的下载、追更和整理按这个策略自动执行,不再逐次审批;升级替换、媒体清理和下载缓存清理仍要单独审批。上一个已发布版本的授权随之失效。";
|
||||||
|
|
||||||
type TemplateDraft = { key: string; name: string; rules: SlotRulesDraft; open: boolean };
|
type TemplateDraft = { key: string; name: string; rules: SlotRulesDraft; open: boolean };
|
||||||
type PolicyDraft = {
|
type PolicyDraft = {
|
||||||
@@ -164,7 +164,7 @@ function buildPolicy(d: PolicyDraft):
|
|||||||
const strict = AcquisitionPolicyRules.strict().safeParse(rules);
|
const strict = AcquisitionPolicyRules.strict().safeParse(rules);
|
||||||
if (!strict.success && !Object.keys(errors).length) {
|
if (!strict.success && !Object.keys(errors).length) {
|
||||||
const i = strict.error.issues[0];
|
const i = strict.error.issues[0];
|
||||||
errors.rules = `规则结构不合法:${i?.path.join(".")} ${i?.message}`;
|
errors.rules = `规则格式有误:${i?.path.join(".")} ${i?.message}`;
|
||||||
}
|
}
|
||||||
if (Object.keys(errors).length || !meta.success) return { ok: false, errors };
|
if (Object.keys(errors).length || !meta.success) return { ok: false, errors };
|
||||||
return {
|
return {
|
||||||
@@ -308,7 +308,7 @@ function PolicyGroup({
|
|||||||
) : (
|
) : (
|
||||||
<Button variant="outline" size="sm" onClick={() => onDraftFrom(head)}>
|
<Button variant="outline" size="sm" onClick={() => onDraftFrom(head)}>
|
||||||
<FilePlus2 />
|
<FilePlus2 />
|
||||||
基于 r{head.revision} 新建草案
|
从 r{head.revision} 新建草案
|
||||||
</Button>
|
</Button>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -449,7 +449,7 @@ function PolicyDetailSheet({
|
|||||||
>
|
>
|
||||||
{!pv?.success ? (
|
{!pv?.success ? (
|
||||||
<p className="rounded-lg border border-warning/30 bg-warning/10 px-3 py-2 text-caption text-warning">
|
<p className="rounded-lg border border-warning/30 bg-warning/10 px-3 py-2 text-caption text-warning">
|
||||||
这个草案还没有预览。先预览可以看到影响范围。
|
还没有预览。预览后能看到影响范围。
|
||||||
</p>
|
</p>
|
||||||
) : null}
|
) : null}
|
||||||
</ConfirmDialog>
|
</ConfirmDialog>
|
||||||
@@ -458,14 +458,14 @@ function PolicyDetailSheet({
|
|||||||
<>
|
<>
|
||||||
<Button variant="outline" onClick={() => onDraftFrom(rev)}>
|
<Button variant="outline" onClick={() => onDraftFrom(rev)}>
|
||||||
<FilePlus2 />
|
<FilePlus2 />
|
||||||
基于此版本新建草案
|
从这个版本新建草案
|
||||||
</Button>
|
</Button>
|
||||||
{rev.status === "superseded" || rev.status === "rolled_back" ? (
|
{rev.status === "superseded" || rev.status === "rolled_back" ? (
|
||||||
<ConfirmDialog
|
<ConfirmDialog
|
||||||
trigger={
|
trigger={
|
||||||
<Button variant="outline">
|
<Button variant="outline">
|
||||||
<Undo2 />
|
<Undo2 />
|
||||||
回滚到此版本
|
回滚到这个版本
|
||||||
</Button>
|
</Button>
|
||||||
}
|
}
|
||||||
title={`回滚到 r${rev.revision}?`}
|
title={`回滚到 r${rev.revision}?`}
|
||||||
@@ -538,7 +538,7 @@ function PreviewBlock({ value }: { value: JsonObject | null }) {
|
|||||||
if (!value)
|
if (!value)
|
||||||
return (
|
return (
|
||||||
<p className="text-caption text-muted-foreground">
|
<p className="text-caption text-muted-foreground">
|
||||||
还没有预览。预览用最近已识别的资源评估模板,不会执行任何操作。
|
还没有预览。预览用最近识别的资源试算模板,不会下载或整理。
|
||||||
</p>
|
</p>
|
||||||
);
|
);
|
||||||
const p = PolicyPreview.safeParse(value);
|
const p = PolicyPreview.safeParse(value);
|
||||||
@@ -565,7 +565,7 @@ function PreviewBlock({ value }: { value: JsonObject | null }) {
|
|||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
<p className="text-meta text-muted-foreground">
|
<p className="text-meta text-muted-foreground">
|
||||||
计算于 {formatDateTime(v.evaluatedAt)};接受/排除按"资源 × 模板"计数。
|
计算于 {formatDateTime(v.evaluatedAt)};接受 / 排除按「资源 × 模板」计数。
|
||||||
</p>
|
</p>
|
||||||
{v.samples.length ? (
|
{v.samples.length ? (
|
||||||
<ul className="divide-y divide-border/60 rounded-lg border border-border/70">
|
<ul className="divide-y divide-border/60 rounded-lg border border-border/70">
|
||||||
@@ -586,7 +586,7 @@ function PreviewBlock({ value }: { value: JsonObject | null }) {
|
|||||||
))}
|
))}
|
||||||
</ul>
|
</ul>
|
||||||
) : (
|
) : (
|
||||||
<p className="text-caption text-muted-foreground">没有可用于回放的已识别资源。</p>
|
<p className="text-caption text-muted-foreground">没有可回放的已识别资源。</p>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
@@ -659,7 +659,7 @@ function PolicyEditorSheet({
|
|||||||
open
|
open
|
||||||
onOpenChange={(o) => !o && !pending && onClose()}
|
onOpenChange={(o) => !o && !pending && onClose()}
|
||||||
width="lg"
|
width="lg"
|
||||||
title={editId ? `编辑草案 r${base?.revision}` : base ? `基于 r${base.revision} 新建草案` : "新建收录策略草案"}
|
title={editId ? `编辑草案 r${base?.revision}` : base ? `从 r${base.revision} 新建草案` : "新建收录策略草案"}
|
||||||
description="保存为草案,发布后才生效。"
|
description="保存为草案,发布后才生效。"
|
||||||
footer={
|
footer={
|
||||||
<>
|
<>
|
||||||
|
|||||||
@@ -66,7 +66,7 @@ export function SlotsPanel({
|
|||||||
plain
|
plain
|
||||||
icon={Target}
|
icon={Target}
|
||||||
title="还没有收录版本"
|
title="还没有收录版本"
|
||||||
description="没有时按已发布收录策略的默认模板创建,也可以手动新建。"
|
description="订阅时按已发布收录策略的默认模板自动创建,也可以手动新建。"
|
||||||
className="py-8"
|
className="py-8"
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
@@ -80,7 +80,7 @@ export function SlotsPanel({
|
|||||||
<span>
|
<span>
|
||||||
{[s.currentAsset.resolution, s.currentAsset.sourceType, s.currentAsset.releaseGroup]
|
{[s.currentAsset.resolution, s.currentAsset.sourceType, s.currentAsset.releaseGroup]
|
||||||
.filter(Boolean)
|
.filter(Boolean)
|
||||||
.join(" · ") || "交付特征未知"}
|
.join(" · ") || "规格未知"}
|
||||||
{s.currentAsset.sizeBytes ? ` · ${formatBytes(s.currentAsset.sizeBytes, 1)}` : ""}
|
{s.currentAsset.sizeBytes ? ` · ${formatBytes(s.currentAsset.sizeBytes, 1)}` : ""}
|
||||||
{s.currentAsset.status !== "active" ? ` · ${s.currentAsset.status}` : ""}
|
{s.currentAsset.status !== "active" ? ` · ${s.currentAsset.status}` : ""}
|
||||||
</span>
|
</span>
|
||||||
@@ -140,7 +140,7 @@ function SlotEditorSheet({
|
|||||||
const errs: Record<string, string> = {};
|
const errs: Record<string, string> = {};
|
||||||
if (!meta.success) for (const i of meta.error.issues) errs[String(i.path[0])] ??= i.message;
|
if (!meta.success) for (const i of meta.error.issues) errs[String(i.path[0])] ??= i.message;
|
||||||
if (!rules.ok) Object.assign(errs, rules.errors);
|
if (!rules.ok) Object.assign(errs, rules.errors);
|
||||||
if (requireDistinct && !distinctGroup.trim()) errs.distinctGroup = "要求独立文件时须填写分组";
|
if (requireDistinct && !distinctGroup.trim()) errs.distinctGroup = "要求独立文件时要填组合分组";
|
||||||
setErrors(errs);
|
setErrors(errs);
|
||||||
if (Object.keys(errs).length || !meta.success || !rules.ok) return;
|
if (Object.keys(errs).length || !meta.success || !rules.ok) return;
|
||||||
const common = {
|
const common = {
|
||||||
@@ -180,7 +180,7 @@ function SlotEditorSheet({
|
|||||||
width="lg"
|
width="lg"
|
||||||
title={slot ? `编辑收录版本 ${slot.name}` : "新建收录版本"}
|
title={slot ? `编辑收录版本 ${slot.name}` : "新建收录版本"}
|
||||||
description={
|
description={
|
||||||
slot ? `当前 r${slot.revision}。保存时若已被修改,会提示刷新。` : "不满足硬条件的资源不收;软偏好决定先收哪个。"
|
slot ? `当前 r${slot.revision}。保存时如果已被别处修改,会提示刷新。` : "不满足硬条件的资源不收;软偏好决定先收哪个。"
|
||||||
}
|
}
|
||||||
footer={
|
footer={
|
||||||
<>
|
<>
|
||||||
@@ -195,7 +195,7 @@ function SlotEditorSheet({
|
|||||||
>
|
>
|
||||||
<div className="grid gap-3 sm:grid-cols-2">
|
<div className="grid gap-3 sm:grid-cols-2">
|
||||||
<FormField
|
<FormField
|
||||||
label="目标名称"
|
label="名称"
|
||||||
htmlFor={`${id}-name`}
|
htmlFor={`${id}-name`}
|
||||||
required
|
required
|
||||||
error={errors.name}
|
error={errors.name}
|
||||||
@@ -213,7 +213,7 @@ function SlotEditorSheet({
|
|||||||
label="组合分组"
|
label="组合分组"
|
||||||
htmlFor={`${id}-grp`}
|
htmlFor={`${id}-grp`}
|
||||||
error={errors.distinctGroup}
|
error={errors.distinctGroup}
|
||||||
hint="同组目标可要求由不同文件满足"
|
hint="同组的收录版本可以要求由不同文件满足"
|
||||||
>
|
>
|
||||||
<Input
|
<Input
|
||||||
id={`${id}-grp`}
|
id={`${id}-grp`}
|
||||||
@@ -226,7 +226,7 @@ function SlotEditorSheet({
|
|||||||
<div className="flex items-center justify-between gap-3 rounded-lg border border-border px-3 py-2">
|
<div className="flex items-center justify-between gap-3 rounded-lg border border-border px-3 py-2">
|
||||||
<div className="min-w-0">
|
<div className="min-w-0">
|
||||||
<p className="text-caption font-medium text-foreground">要求独立文件</p>
|
<p className="text-caption font-medium text-foreground">要求独立文件</p>
|
||||||
<p className="text-meta text-muted-foreground">同一个文件不能同时满足两个目标</p>
|
<p className="text-meta text-muted-foreground">同一个文件不能同时满足同组的两个收录版本</p>
|
||||||
</div>
|
</div>
|
||||||
<Switch checked={requireDistinct} onCheckedChange={setRequireDistinct} aria-label="要求独立文件" />
|
<Switch checked={requireDistinct} onCheckedChange={setRequireDistinct} aria-label="要求独立文件" />
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in new issue
Block a user