Spaces:
Sleeping
Sleeping
| import React, { useState } from 'react'; | |
| import { Prompt } from '../../types'; | |
| import Card, { CardHeader, CardContent } from '../common/Card'; | |
| import Button from '../common/Button'; | |
| import PromptForm from './PromptForm'; | |
| import { exportPrompt, exportMultiplePrompts } from '../../utils/exportUtils'; | |
| interface PromptListProps { | |
| groupId: string; | |
| prompts: Prompt[]; | |
| onAddPrompt: (promptData: { title: string; content: string; tags: string[] }) => void; | |
| onUpdatePrompt: (promptId: string, promptData: { title: string; content: string; tags: string[] }) => void; | |
| onDeletePrompt: (promptId: string) => void; | |
| } | |
| const PromptList: React.FC<PromptListProps> = ({ | |
| groupId, | |
| prompts, | |
| onAddPrompt, | |
| onUpdatePrompt, | |
| onDeletePrompt | |
| }) => { | |
| const [showNewPromptForm, setShowNewPromptForm] = useState(false); | |
| const [editingPromptId, setEditingPromptId] = useState<string | null>(null); | |
| const [selectedPrompts, setSelectedPrompts] = useState<string[]>([]); | |
| const [isSelectMode, setIsSelectMode] = useState(false); | |
| const handleEditPrompt = (promptId: string) => { | |
| setEditingPromptId(promptId); | |
| }; | |
| const handleDeletePrompt = (promptId: string) => { | |
| if (window.confirm('确定要删除此提示词吗?此操作不可撤销。')) { | |
| onDeletePrompt(promptId); | |
| } | |
| }; | |
| const handleSavePrompt = (promptData: { title: string; content: string; tags: string[] }) => { | |
| if (editingPromptId) { | |
| // 如果正在编辑提示词,调用 onUpdatePrompt | |
| onUpdatePrompt(editingPromptId, promptData); | |
| setEditingPromptId(null); | |
| } else { | |
| // 如果正在创建新提示词,调用 onAddPrompt | |
| onAddPrompt(promptData); | |
| setShowNewPromptForm(false); | |
| } | |
| }; | |
| const handleExportPrompt = (prompt: Prompt) => { | |
| exportPrompt(prompt); | |
| }; | |
| const handleToggleSelectPrompt = (promptId: string) => { | |
| setSelectedPrompts(prevSelected => { | |
| if (prevSelected.includes(promptId)) { | |
| return prevSelected.filter(id => id !== promptId); | |
| } else { | |
| return [...prevSelected, promptId]; | |
| } | |
| }); | |
| }; | |
| const handleSelectAll = () => { | |
| if (selectedPrompts.length === prompts.length) { | |
| setSelectedPrompts([]); | |
| } else { | |
| setSelectedPrompts(prompts.map(p => p._id)); | |
| } | |
| }; | |
| const handleExportSelected = () => { | |
| const selectedPromptObjects = prompts.filter(p => selectedPrompts.includes(p._id)); | |
| exportMultiplePrompts(selectedPromptObjects); | |
| }; | |
| const handleDeleteSelected = () => { | |
| if (window.confirm(`确定要删除选中的 ${selectedPrompts.length} 个提示词吗?此操作不可撤销。`)) { | |
| selectedPrompts.forEach(promptId => { | |
| onDeletePrompt(promptId); | |
| }); | |
| setSelectedPrompts([]); | |
| setIsSelectMode(false); | |
| } | |
| }; | |
| if (prompts.length === 0 && !showNewPromptForm) { | |
| return ( | |
| <div> | |
| <div className="ios-empty-state"> | |
| <div className="ios-empty-state-icon"> | |
| <svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"> | |
| <path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"></path> | |
| <polyline points="14 2 14 8 20 8"></polyline> | |
| <line x1="16" y1="13" x2="8" y2="13"></line> | |
| <line x1="16" y1="17" x2="8" y2="17"></line> | |
| <polyline points="10 9 9 9 8 9"></polyline> | |
| </svg> | |
| </div> | |
| <h3 className="ios-empty-state-title">暂无提示词</h3> | |
| <p className="ios-empty-state-text">添加提示词以便于管理和使用</p> | |
| <Button | |
| variant="primary" | |
| className="mt-4" | |
| onClick={() => setShowNewPromptForm(true)} | |
| > | |
| 添加提示词 | |
| </Button> | |
| </div> | |
| </div> | |
| ); | |
| } | |
| return ( | |
| <div> | |
| <div className="flex justify-between items-center mb-4"> | |
| <h3 className="text-lg font-medium">提示词列表</h3> | |
| <div className="flex space-x-2"> | |
| {isSelectMode ? ( | |
| <> | |
| <Button | |
| variant="secondary" | |
| size="small" | |
| onClick={handleSelectAll} | |
| > | |
| {selectedPrompts.length === prompts.length ? '取消全选' : '全选'} | |
| </Button> | |
| <Button | |
| variant="primary" | |
| size="small" | |
| onClick={handleExportSelected} | |
| disabled={selectedPrompts.length === 0} | |
| > | |
| 导出选中 | |
| </Button> | |
| <Button | |
| variant="danger" | |
| size="small" | |
| onClick={handleDeleteSelected} | |
| disabled={selectedPrompts.length === 0} | |
| > | |
| 删除选中 | |
| </Button> | |
| <Button | |
| variant="secondary" | |
| size="small" | |
| onClick={() => { | |
| setIsSelectMode(false); | |
| setSelectedPrompts([]); | |
| }} | |
| > | |
| 取消 | |
| </Button> | |
| </> | |
| ) : ( | |
| <> | |
| <Button | |
| variant="secondary" | |
| size="small" | |
| onClick={() => setIsSelectMode(true)} | |
| > | |
| 选择 | |
| </Button> | |
| <Button | |
| variant="primary" | |
| size="small" | |
| onClick={() => setShowNewPromptForm(true)} | |
| > | |
| 添加提示词 | |
| </Button> | |
| </> | |
| )} | |
| </div> | |
| </div> | |
| {showNewPromptForm && !editingPromptId && ( | |
| <Card className="mb-4"> | |
| <CardHeader title="新增提示词" /> | |
| <CardContent> | |
| <PromptForm | |
| onSubmit={handleSavePrompt} | |
| onCancel={() => setShowNewPromptForm(false)} | |
| /> | |
| </CardContent> | |
| </Card> | |
| )} | |
| <div className="space-y-4"> | |
| {prompts.map((prompt) => ( | |
| <Card key={prompt._id} className="mb-4"> | |
| {editingPromptId === prompt._id ? ( | |
| <CardContent> | |
| <PromptForm | |
| initialPrompt={prompt} | |
| onSubmit={handleSavePrompt} | |
| onCancel={() => setEditingPromptId(null)} | |
| /> | |
| </CardContent> | |
| ) : ( | |
| <> | |
| <CardHeader | |
| title={prompt.title} | |
| subtitle={new Date(prompt.updatedAt).toLocaleDateString('zh-CN', { | |
| year: 'numeric', | |
| month: 'long', | |
| day: 'numeric' | |
| })} | |
| action={ | |
| isSelectMode && ( | |
| <div | |
| className="w-6 h-6 rounded-md border border-gray-300 flex items-center justify-center cursor-pointer" | |
| onClick={() => handleToggleSelectPrompt(prompt._id)} | |
| > | |
| {selectedPrompts.includes(prompt._id) && ( | |
| <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="#007AFF" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"> | |
| <polyline points="20 6 9 17 4 12"></polyline> | |
| </svg> | |
| )} | |
| </div> | |
| ) | |
| } | |
| /> | |
| <CardContent> | |
| <pre className="whitespace-pre-wrap text-gray-700 mb-4 font-sans max-h-48 overflow-y-auto"> | |
| {prompt.content} | |
| </pre> | |
| {prompt.tags.length > 0 && ( | |
| <div className="flex flex-wrap mb-4"> | |
| {prompt.tags.map((tag) => ( | |
| <div | |
| key={tag} | |
| className="ios-tag bg-blue-100 text-blue-800" | |
| > | |
| {tag} | |
| </div> | |
| ))} | |
| </div> | |
| )} | |
| {!isSelectMode && ( | |
| <div className="flex justify-end space-x-2"> | |
| <Button | |
| variant="secondary" | |
| size="small" | |
| onClick={() => handleExportPrompt(prompt)} | |
| > | |
| 导出 | |
| </Button> | |
| <Button | |
| variant="secondary" | |
| size="small" | |
| onClick={() => handleEditPrompt(prompt._id)} | |
| > | |
| 编辑 | |
| </Button> | |
| <Button | |
| variant="danger" | |
| size="small" | |
| onClick={() => handleDeletePrompt(prompt._id)} | |
| > | |
| 删除 | |
| </Button> | |
| </div> | |
| )} | |
| </CardContent> | |
| </> | |
| )} | |
| </Card> | |
| ))} | |
| </div> | |
| </div> | |
| ); | |
| }; | |
| export default PromptList; |