fix: 合并服务器未推送改动并保留手动视频标签
This commit is contained in:
@@ -0,0 +1,138 @@
|
||||
"use client";
|
||||
|
||||
import { Check, Tag, X } from "lucide-react";
|
||||
import { useEffect, useState } from "react";
|
||||
import { VIDEO_CONTENT_TAGS } from "@/lib/video-tags";
|
||||
|
||||
type TagUpdateResponse = {
|
||||
updated?: {
|
||||
aweme_id: string;
|
||||
content_tags: string[];
|
||||
manual_content_tags: string[];
|
||||
}[];
|
||||
};
|
||||
|
||||
type VideoTagEditorProps = {
|
||||
awemeId: string;
|
||||
tags: string[];
|
||||
onTagsChange: (tags: string[]) => void;
|
||||
};
|
||||
|
||||
export function VideoTagEditor({
|
||||
awemeId,
|
||||
tags,
|
||||
onTagsChange,
|
||||
}: VideoTagEditorProps) {
|
||||
const [open, setOpen] = useState(false);
|
||||
const [draftTags, setDraftTags] = useState<string[]>(tags);
|
||||
const [saving, setSaving] = useState(false);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (!open) setDraftTags(tags);
|
||||
}, [open, tags]);
|
||||
|
||||
const toggleTag = (tag: string) => {
|
||||
setDraftTags((current) =>
|
||||
current.includes(tag)
|
||||
? current.filter((item) => item !== tag)
|
||||
: [...current, tag],
|
||||
);
|
||||
};
|
||||
|
||||
const saveTags = async () => {
|
||||
setSaving(true);
|
||||
setError(null);
|
||||
try {
|
||||
const response = await fetch("/api/asr/tags", {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({
|
||||
awemeId,
|
||||
contentTags: draftTags,
|
||||
mode: "set",
|
||||
}),
|
||||
});
|
||||
if (!response.ok) throw new Error("保存标签失败");
|
||||
const data: TagUpdateResponse = await response.json();
|
||||
const updated = data.updated?.find((item) => item.aweme_id === awemeId);
|
||||
onTagsChange(updated?.content_tags ?? draftTags);
|
||||
setOpen(false);
|
||||
} catch (err) {
|
||||
setError(err instanceof Error ? err.message : "保存标签失败");
|
||||
} finally {
|
||||
setSaving(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="absolute right-3 top-3 z-40 flex flex-col items-end gap-2">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setOpen((value) => !value)}
|
||||
className="inline-flex h-9 items-center gap-2 rounded-full border border-white/20 bg-black/35 px-3 text-sm font-medium text-white shadow-lg backdrop-blur transition-colors hover:bg-black/50"
|
||||
aria-expanded={open}
|
||||
>
|
||||
<Tag size={16} />
|
||||
{tags.length > 0 ? tags.join("、") : "打标签"}
|
||||
</button>
|
||||
|
||||
{open && (
|
||||
<div className="w-[min(92vw,24rem)] rounded-xl border border-white/15 bg-zinc-950/92 p-3 text-white shadow-2xl backdrop-blur-xl">
|
||||
<div className="mb-3 flex items-center justify-between gap-3">
|
||||
<div className="text-sm font-medium">手动标签</div>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setOpen(false)}
|
||||
className="rounded-full p-1 text-white/60 transition-colors hover:bg-white/10 hover:text-white"
|
||||
aria-label="关闭标签编辑"
|
||||
>
|
||||
<X size={16} />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-wrap gap-2">
|
||||
{VIDEO_CONTENT_TAGS.map((tag) => {
|
||||
const selected = draftTags.includes(tag);
|
||||
return (
|
||||
<button
|
||||
key={tag}
|
||||
type="button"
|
||||
onClick={() => toggleTag(tag)}
|
||||
className={`inline-flex items-center gap-1 rounded-full border px-3 py-1.5 text-xs font-medium transition-colors ${
|
||||
selected
|
||||
? "border-blue-400 bg-blue-500/25 text-blue-100"
|
||||
: "border-white/10 bg-white/5 text-white/70 hover:border-white/25 hover:text-white"
|
||||
}`}
|
||||
>
|
||||
{selected && <Check size={13} />}
|
||||
{tag}
|
||||
</button>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
|
||||
{error && <div className="mt-3 text-xs text-rose-300">{error}</div>}
|
||||
|
||||
<div className="mt-4 flex items-center justify-between gap-3">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setDraftTags([])}
|
||||
className="rounded-full px-3 py-1.5 text-xs text-white/60 transition-colors hover:bg-white/10 hover:text-white"
|
||||
>
|
||||
清空
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={saveTags}
|
||||
disabled={saving}
|
||||
className="rounded-full bg-blue-600 px-4 py-1.5 text-xs font-medium text-white transition-colors hover:bg-blue-500 disabled:cursor-not-allowed disabled:bg-zinc-600"
|
||||
>
|
||||
{saving ? "保存中..." : "保存"}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user