feat: 实现AI分类设置功能 (v0.1.7)
新增功能: - AI分类CRUD操作 (创建、读取、更新、删除) - 实时提示词预览功能 - 分类排序和状态管理 - 完整的表单验证和错误处理 后端架构: - 数据层: AiClassification模型和仓储 - 业务层: AiClassificationService业务逻辑 - 表示层: 10个Tauri命令接口 - 数据库: ai_classifications表和索引 前端架构: - 类型系统: 完整的TypeScript类型定义 - 服务层: AiClassificationService API封装 - 组件层: 5个专用组件 (主页面、表单、预览、删除确认、实时预览) - 路由集成: /ai-classification-settings 质量保证: - 52个单元测试 (100%通过) - TypeScript和Rust编译无错误 - 遵循promptx开发规范 核心特性: - 支持分类名称和提示词定义 - 实时生成完整AI分类提示词 - 拖拽排序和批量操作 - 优雅的用户界面和交互体验
This commit is contained in:
204
apps/desktop/src/components/AiClassificationPreviewDialog.tsx
Normal file
204
apps/desktop/src/components/AiClassificationPreviewDialog.tsx
Normal file
@@ -0,0 +1,204 @@
|
||||
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>
|
||||
);
|
||||
};
|
||||
Reference in New Issue
Block a user