205 lines
8.5 KiB
TypeScript
205 lines
8.5 KiB
TypeScript
import React, { useState } from 'react';
|
|
import { XMarkIcon, ClipboardIcon, CheckIcon } from '@heroicons/react/24/outline';
|
|
import { AiClassificationPreview } from '../types/aiClassification';
|
|
import { LoadingSpinner } from './LoadingSpinner';
|
|
|
|
interface AiClassificationPreviewDialogProps {
|
|
/** 是否显示对话框 */
|
|
isOpen: boolean;
|
|
/** 预览数据 */
|
|
preview: AiClassificationPreview | null;
|
|
/** 是否正在加载 */
|
|
loading: boolean;
|
|
/** 关闭回调 */
|
|
onClose: () => void;
|
|
}
|
|
|
|
/**
|
|
* AI分类预览对话框组件
|
|
* 遵循前端开发规范的对话框设计,实现提示词实时预览功能
|
|
*/
|
|
export const AiClassificationPreviewDialog: React.FC<AiClassificationPreviewDialogProps> = ({
|
|
isOpen,
|
|
preview,
|
|
loading,
|
|
onClose,
|
|
}) => {
|
|
const [copied, setCopied] = useState(false);
|
|
|
|
// 复制到剪贴板
|
|
const handleCopy = async () => {
|
|
if (!preview?.full_prompt) return;
|
|
|
|
try {
|
|
await navigator.clipboard.writeText(preview.full_prompt);
|
|
setCopied(true);
|
|
setTimeout(() => setCopied(false), 2000);
|
|
} catch (err) {
|
|
console.error('复制失败:', err);
|
|
}
|
|
};
|
|
|
|
if (!isOpen) return null;
|
|
|
|
return (
|
|
<div className="fixed inset-0 z-50 overflow-y-auto">
|
|
<div className="flex items-center justify-center min-h-screen pt-4 px-4 pb-20 text-center sm:block sm:p-0">
|
|
{/* 背景遮罩 */}
|
|
<div
|
|
className="fixed inset-0 bg-gray-500 bg-opacity-75 transition-opacity"
|
|
onClick={onClose}
|
|
/>
|
|
|
|
{/* 对话框 */}
|
|
<div className="inline-block align-bottom bg-white rounded-lg text-left overflow-hidden shadow-xl transform transition-all sm:my-8 sm:align-middle sm:max-w-4xl sm:w-full">
|
|
{/* 标题栏 */}
|
|
<div className="bg-white px-4 pt-5 pb-4 sm:p-6 sm:pb-4 border-b border-gray-200">
|
|
<div className="flex items-center justify-between">
|
|
<div>
|
|
<h3 className="text-lg leading-6 font-medium text-gray-900">
|
|
AI分类提示词预览
|
|
</h3>
|
|
<p className="mt-1 text-sm text-gray-500">
|
|
基于当前激活的分类生成的完整提示词
|
|
</p>
|
|
</div>
|
|
<button
|
|
onClick={onClose}
|
|
className="bg-white rounded-md text-gray-400 hover:text-gray-600 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500"
|
|
>
|
|
<XMarkIcon className="h-6 w-6" />
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
{/* 内容区域 */}
|
|
<div className="bg-white px-4 pb-4 sm:p-6 sm:pt-4">
|
|
{loading ? (
|
|
<div className="flex items-center justify-center py-12">
|
|
<LoadingSpinner size="large" />
|
|
<span className="ml-3 text-gray-600">正在生成预览...</span>
|
|
</div>
|
|
) : preview ? (
|
|
<div className="space-y-6">
|
|
{/* 分类统计 */}
|
|
<div className="bg-blue-50 rounded-lg p-4">
|
|
<div className="flex items-center justify-between">
|
|
<div>
|
|
<h4 className="text-sm font-medium text-blue-900">
|
|
激活的分类数量
|
|
</h4>
|
|
<p className="text-2xl font-bold text-blue-600">
|
|
{preview.classifications.length}
|
|
</p>
|
|
</div>
|
|
<div className="text-blue-400">
|
|
<svg className="h-8 w-8" fill="currentColor" viewBox="0 0 20 20">
|
|
<path fillRule="evenodd" d="M3 4a1 1 0 011-1h12a1 1 0 110 2H4a1 1 0 01-1-1zm0 4a1 1 0 011-1h12a1 1 0 110 2H4a1 1 0 01-1-1zm0 4a1 1 0 011-1h12a1 1 0 110 2H4a1 1 0 01-1-1z" clipRule="evenodd" />
|
|
</svg>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* 分类列表 */}
|
|
<div>
|
|
<h4 className="text-sm font-medium text-gray-900 mb-3">
|
|
当前激活的分类
|
|
</h4>
|
|
<div className="space-y-2">
|
|
{preview.classifications.map((classification, index) => (
|
|
<div key={classification.id} className="flex items-start space-x-3 p-3 bg-gray-50 rounded-lg">
|
|
<span className="flex-shrink-0 w-6 h-6 bg-blue-100 text-blue-800 text-xs font-medium rounded-full flex items-center justify-center">
|
|
{index + 1}
|
|
</span>
|
|
<div className="flex-1 min-w-0">
|
|
<p className="text-sm font-medium text-gray-900">
|
|
{classification.name}
|
|
</p>
|
|
<p className="text-sm text-gray-600 mt-1">
|
|
{classification.prompt_text}
|
|
</p>
|
|
</div>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
|
|
{/* 完整提示词 */}
|
|
<div>
|
|
<div className="flex items-center justify-between mb-3">
|
|
<h4 className="text-sm font-medium text-gray-900">
|
|
完整提示词
|
|
</h4>
|
|
<button
|
|
onClick={handleCopy}
|
|
className="inline-flex items-center px-3 py-1.5 border border-gray-300 shadow-sm text-xs font-medium rounded text-gray-700 bg-white hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500"
|
|
>
|
|
{copied ? (
|
|
<>
|
|
<CheckIcon className="h-3 w-3 mr-1 text-green-500" />
|
|
已复制
|
|
</>
|
|
) : (
|
|
<>
|
|
<ClipboardIcon className="h-3 w-3 mr-1" />
|
|
复制
|
|
</>
|
|
)}
|
|
</button>
|
|
</div>
|
|
<div className="bg-gray-900 rounded-lg p-4 overflow-auto max-h-96">
|
|
<pre className="text-sm text-gray-100 whitespace-pre-wrap font-mono">
|
|
{preview.full_prompt}
|
|
</pre>
|
|
</div>
|
|
<p className="mt-2 text-xs text-gray-500">
|
|
字符数: {preview.full_prompt.length}
|
|
</p>
|
|
</div>
|
|
|
|
{/* 使用说明 */}
|
|
<div className="bg-yellow-50 border border-yellow-200 rounded-lg p-4">
|
|
<h5 className="text-sm font-medium text-yellow-800 mb-2">
|
|
使用说明
|
|
</h5>
|
|
<ul className="text-sm text-yellow-700 space-y-1">
|
|
<li>• 此提示词将用于AI视频分类功能</li>
|
|
<li>• 只有激活状态的分类会包含在提示词中</li>
|
|
<li>• 分类按照排序顺序显示</li>
|
|
<li>• 修改分类后需要重新生成预览</li>
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
) : (
|
|
<div className="text-center py-12">
|
|
<div className="text-gray-400 mb-4">
|
|
<svg className="mx-auto h-12 w-12" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z" />
|
|
</svg>
|
|
</div>
|
|
<h3 className="text-sm font-medium text-gray-900 mb-1">
|
|
暂无预览数据
|
|
</h3>
|
|
<p className="text-sm text-gray-500">
|
|
请先添加一些AI分类
|
|
</p>
|
|
</div>
|
|
)}
|
|
</div>
|
|
|
|
{/* 按钮栏 */}
|
|
<div className="bg-gray-50 px-4 py-3 sm:px-6 sm:flex sm:flex-row-reverse">
|
|
<button
|
|
type="button"
|
|
onClick={onClose}
|
|
className="w-full inline-flex justify-center rounded-md border border-gray-300 shadow-sm px-4 py-2 bg-white text-base font-medium text-gray-700 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 sm:w-auto sm:text-sm"
|
|
>
|
|
关闭
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
};
|