211 lines
6.7 KiB
TypeScript
211 lines
6.7 KiB
TypeScript
import React, { useState, useEffect, useCallback } from "react";
|
|
import {
|
|
Dialog,
|
|
DialogContent,
|
|
DialogHeader,
|
|
DialogTitle,
|
|
} from "@/components/ui/dialog";
|
|
import { Input } from "@/components/ui/input";
|
|
import { Button } from "@/components/ui/button";
|
|
import { Badge } from "@/components/ui/badge";
|
|
import { Search, RefreshCw, Loader2 } from "lucide-react";
|
|
import { fetchContentLibraryList } from "@/api/content";
|
|
import { ContentLibrary } from "@/api/content";
|
|
import { useToast } from "@/components/ui/toast";
|
|
|
|
interface ContentLibrarySelectionDialogProps {
|
|
open: boolean;
|
|
onOpenChange: (open: boolean) => void;
|
|
selectedLibraries: string[];
|
|
onSelect: (libraries: string[]) => void;
|
|
}
|
|
|
|
export function ContentLibrarySelectionDialog({
|
|
open,
|
|
onOpenChange,
|
|
selectedLibraries,
|
|
onSelect,
|
|
}: ContentLibrarySelectionDialogProps) {
|
|
const { toast } = useToast();
|
|
const [searchQuery, setSearchQuery] = useState("");
|
|
const [loading, setLoading] = useState(false);
|
|
const [libraries, setLibraries] = useState<ContentLibrary[]>([]);
|
|
const [tempSelected, setTempSelected] = useState<string[]>([]);
|
|
|
|
// 获取内容库列表
|
|
const fetchLibraries = useCallback(async () => {
|
|
setLoading(true);
|
|
try {
|
|
const response = await fetchContentLibraryList(1, 100, searchQuery);
|
|
if (response.code === 200 && response.data) {
|
|
setLibraries(response.data.list);
|
|
} else {
|
|
toast({
|
|
title: "获取内容库列表失败",
|
|
description: response.msg,
|
|
variant: "destructive",
|
|
});
|
|
}
|
|
} catch (error) {
|
|
console.error("获取内容库列表失败:", error);
|
|
toast({
|
|
title: "获取内容库列表失败",
|
|
description: "请检查网络连接",
|
|
variant: "destructive",
|
|
});
|
|
} finally {
|
|
setLoading(false);
|
|
}
|
|
}, [searchQuery, toast]);
|
|
|
|
useEffect(() => {
|
|
if (open) {
|
|
fetchLibraries();
|
|
setTempSelected(selectedLibraries);
|
|
}
|
|
}, [open, selectedLibraries, fetchLibraries]);
|
|
|
|
const handleRefresh = () => {
|
|
fetchLibraries();
|
|
};
|
|
|
|
const handleSelectAll = () => {
|
|
if (tempSelected.length === libraries.length) {
|
|
setTempSelected([]);
|
|
} else {
|
|
setTempSelected(libraries.map((lib) => lib.id));
|
|
}
|
|
};
|
|
|
|
const handleLibraryToggle = (libraryId: string) => {
|
|
setTempSelected((prev) =>
|
|
prev.includes(libraryId)
|
|
? prev.filter((id) => id !== libraryId)
|
|
: [...prev, libraryId]
|
|
);
|
|
};
|
|
|
|
const handleDialogOpenChange = (open: boolean) => {
|
|
if (!open) {
|
|
setTempSelected(selectedLibraries);
|
|
}
|
|
onOpenChange(open);
|
|
};
|
|
|
|
const handleConfirm = () => {
|
|
onSelect(tempSelected);
|
|
onOpenChange(false);
|
|
};
|
|
|
|
return (
|
|
<Dialog open={open} onOpenChange={handleDialogOpenChange}>
|
|
<DialogContent className="flex flex-col bg-white">
|
|
<DialogHeader>
|
|
<DialogTitle>选择内容库</DialogTitle>
|
|
</DialogHeader>
|
|
|
|
<div className="flex items-center space-x-2 my-4">
|
|
<div className="relative flex-1">
|
|
<Search className="absolute left-3 top-2.5 h-4 w-4 text-gray-400" />
|
|
<Input
|
|
placeholder="搜索内容库"
|
|
className="pl-9"
|
|
value={searchQuery}
|
|
onChange={(e) => setSearchQuery(e.target.value)}
|
|
/>
|
|
</div>
|
|
<Button
|
|
variant="outline"
|
|
size="icon"
|
|
onClick={handleRefresh}
|
|
disabled={loading}
|
|
>
|
|
{loading ? (
|
|
<Loader2 className="h-4 w-4 animate-spin" />
|
|
) : (
|
|
<RefreshCw className="h-4 w-4" />
|
|
)}
|
|
</Button>
|
|
</div>
|
|
|
|
<div className="flex justify-between items-center mb-2">
|
|
<div className="text-sm text-gray-500">
|
|
已选择 {tempSelected.length} 个内容库
|
|
</div>
|
|
<div className="flex gap-2">
|
|
<Button
|
|
variant="outline"
|
|
size="sm"
|
|
onClick={handleSelectAll}
|
|
disabled={loading || libraries.length === 0}
|
|
>
|
|
{tempSelected.length === libraries.length ? "取消全选" : "全选"}
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="flex-1 overflow-y-auto -mx-6 px-6 max-h-[400px]">
|
|
<div className="space-y-2">
|
|
{loading ? (
|
|
<div className="flex items-center justify-center h-full text-gray-500">
|
|
加载中...
|
|
</div>
|
|
) : libraries.length === 0 ? (
|
|
<div className="flex items-center justify-center h-full text-gray-500">
|
|
暂无数据
|
|
</div>
|
|
) : (
|
|
libraries.map((library) => (
|
|
<label
|
|
key={library.id}
|
|
className="flex items-start space-x-3 p-4 rounded-lg hover:bg-gray-50 cursor-pointer border"
|
|
>
|
|
<input
|
|
type="checkbox"
|
|
checked={tempSelected.includes(library.id)}
|
|
onChange={() => handleLibraryToggle(library.id)}
|
|
className="mt-1 w-4 h-4 text-blue-600 bg-gray-100 border-gray-300 rounded focus:ring-blue-500 focus:ring-2"
|
|
/>
|
|
<div className="flex-1">
|
|
<div className="flex items-center justify-between">
|
|
<span className="font-medium">{library.name}</span>
|
|
<Badge variant="outline">
|
|
{library.sourceType === 1
|
|
? "文本"
|
|
: library.sourceType === 2
|
|
? "图片"
|
|
: "视频"}
|
|
</Badge>
|
|
</div>
|
|
<div className="text-sm text-gray-500 mt-1">
|
|
<div>创建人: {library.creatorName || "-"}</div>
|
|
<div>
|
|
更新时间:{" "}
|
|
{new Date(library.updateTime).toLocaleString()}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</label>
|
|
))
|
|
)}
|
|
</div>
|
|
</div>
|
|
|
|
<div className="flex justify-between items-center mt-4 pt-4 border-t">
|
|
<div className="text-sm text-gray-500">
|
|
已选择 {tempSelected.length} 个内容库
|
|
</div>
|
|
<div className="flex space-x-2">
|
|
<Button variant="outline" onClick={() => onOpenChange(false)}>
|
|
取消
|
|
</Button>
|
|
<Button onClick={handleConfirm}>
|
|
确定{tempSelected.length > 0 ? ` (${tempSelected.length})` : ""}
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
</DialogContent>
|
|
</Dialog>
|
|
);
|
|
}
|