222 lines
7.6 KiB
TypeScript
222 lines
7.6 KiB
TypeScript
import React, { useState, useMemo } from 'react';
|
||
import { EyeIcon, EyeSlashIcon } from '@heroicons/react/24/outline';
|
||
import {
|
||
AiClassification,
|
||
AiClassificationFormData
|
||
} from '../types/aiClassification';
|
||
|
||
interface AiClassificationRealTimePreviewProps {
|
||
/** 当前表单数据 */
|
||
currentFormData: AiClassificationFormData;
|
||
/** 现有分类列表 */
|
||
existingClassifications: AiClassification[];
|
||
/** 是否为编辑模式 */
|
||
isEdit?: boolean;
|
||
/** 编辑中的分类ID */
|
||
editingClassificationId?: string;
|
||
}
|
||
|
||
/**
|
||
* AI分类实时预览组件
|
||
* 遵循前端开发规范的预览组件设计,实现分类修改时的提示词实时预览
|
||
*/
|
||
export const AiClassificationRealTimePreview: React.FC<AiClassificationRealTimePreviewProps> = ({
|
||
currentFormData,
|
||
existingClassifications,
|
||
isEdit = false,
|
||
editingClassificationId,
|
||
}) => {
|
||
const [isExpanded, setIsExpanded] = useState(false);
|
||
|
||
// 生成预览用的分类列表
|
||
const previewClassifications = useMemo(() => {
|
||
let classifications = [...existingClassifications];
|
||
|
||
if (isEdit && editingClassificationId) {
|
||
// 编辑模式:替换现有分类
|
||
const index = classifications.findIndex(c => c.id === editingClassificationId);
|
||
if (index !== -1) {
|
||
classifications[index] = {
|
||
...classifications[index],
|
||
name: currentFormData.name,
|
||
prompt_text: currentFormData.prompt_text,
|
||
description: currentFormData.description || undefined,
|
||
sort_order: currentFormData.sort_order,
|
||
};
|
||
}
|
||
} else {
|
||
// 创建模式:添加新分类
|
||
if (currentFormData.name.trim() && currentFormData.prompt_text.trim()) {
|
||
const newClassification: AiClassification = {
|
||
id: 'preview-new',
|
||
name: currentFormData.name,
|
||
prompt_text: currentFormData.prompt_text,
|
||
description: currentFormData.description || undefined,
|
||
is_active: true,
|
||
sort_order: currentFormData.sort_order,
|
||
weight: currentFormData.weight,
|
||
created_at: new Date().toISOString(),
|
||
updated_at: new Date().toISOString(),
|
||
};
|
||
classifications.push(newClassification);
|
||
}
|
||
}
|
||
|
||
// 只返回激活的分类,并按排序顺序排列
|
||
return classifications
|
||
.filter(c => c.is_active)
|
||
.sort((a, b) => a.sort_order - b.sort_order);
|
||
}, [currentFormData, existingClassifications, isEdit, editingClassificationId]);
|
||
|
||
// 生成完整提示词
|
||
const fullPrompt = useMemo(() => {
|
||
if (previewClassifications.length === 0) {
|
||
return '暂无激活的分类,无法生成提示词';
|
||
}
|
||
|
||
// 生成分类名称列表
|
||
const categoryNames = previewClassifications
|
||
.map(c => c.name)
|
||
.join('\n - ');
|
||
|
||
// 生成详细分类描述
|
||
const categoriesStr = previewClassifications
|
||
.map(c => `**${c.name}**: ${c.prompt_text}`)
|
||
.join('\n - ');
|
||
|
||
return `请分析这个视频的内容,并将其分类到以下类别之一:
|
||
- ${categoryNames}
|
||
|
||
请按以下步骤进行分析:
|
||
|
||
1. **冲突处理**:
|
||
- 按最大可见面积分类
|
||
|
||
2. **内容分类**(仅对包含目标商品且质量合格的视频):
|
||
- ${categoriesStr}
|
||
|
||
请返回JSON格式的结果:
|
||
|
||
{
|
||
"category": "分类结果",
|
||
"confidence": 0.85,
|
||
"reasoning": "详细的分类理由,包括商品匹配情况和内容特征",
|
||
"features": ["观察到的关键特征1", "关键特征2", "关键特征3"],
|
||
"product_match": true/false,
|
||
"quality_score": 0.9
|
||
}
|
||
|
||
**分类优先级**:
|
||
1. 商品匹配 > 内容分类
|
||
2. 质量合格 > 内容丰富
|
||
3. 明确分类 > 模糊归类
|
||
|
||
请仔细观察视频内容,确保分类准确性。`;
|
||
}, [previewClassifications]);
|
||
|
||
// 检查是否有变化
|
||
const hasChanges = useMemo(() => {
|
||
return currentFormData.name.trim() !== '' || currentFormData.prompt_text.trim() !== '';
|
||
}, [currentFormData]);
|
||
|
||
return (
|
||
<div className="border border-gray-200 rounded-lg overflow-hidden">
|
||
{/* 预览标题栏 */}
|
||
<div
|
||
className="bg-gray-50 px-4 py-3 cursor-pointer hover:bg-gray-100 transition-colors"
|
||
onClick={() => setIsExpanded(!isExpanded)}
|
||
>
|
||
<div className="flex items-center justify-between">
|
||
<div className="flex items-center space-x-2">
|
||
{isExpanded ? (
|
||
<EyeIcon className="h-4 w-4 text-gray-500" />
|
||
) : (
|
||
<EyeSlashIcon className="h-4 w-4 text-gray-500" />
|
||
)}
|
||
<h4 className="text-sm font-medium text-gray-900">
|
||
实时预览
|
||
</h4>
|
||
{hasChanges && (
|
||
<span className="inline-flex items-center px-2 py-0.5 rounded text-xs font-medium bg-blue-100 text-blue-800">
|
||
有变化
|
||
</span>
|
||
)}
|
||
</div>
|
||
<div className="text-xs text-gray-500">
|
||
{previewClassifications.length} 个激活分类
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
{/* 预览内容 */}
|
||
{isExpanded && (
|
||
<div className="p-4 space-y-4">
|
||
{/* 分类列表预览 */}
|
||
<div>
|
||
<h5 className="text-xs font-medium text-gray-700 mb-2">
|
||
激活的分类 ({previewClassifications.length})
|
||
</h5>
|
||
<div className="space-y-1">
|
||
{previewClassifications.map((classification, index) => (
|
||
<div
|
||
key={classification.id}
|
||
className={`text-xs p-2 rounded ${
|
||
classification.id === 'preview-new'
|
||
? 'bg-green-50 border border-green-200'
|
||
: classification.id === editingClassificationId
|
||
? 'bg-blue-50 border border-blue-200'
|
||
: 'bg-gray-50'
|
||
}`}
|
||
>
|
||
<div className="flex items-center space-x-2">
|
||
<span className="font-medium text-gray-900">
|
||
{index + 1}. {classification.name}
|
||
</span>
|
||
{classification.id === 'preview-new' && (
|
||
<span className="text-green-600 font-medium">(新增)</span>
|
||
)}
|
||
{classification.id === editingClassificationId && (
|
||
<span className="text-blue-600 font-medium">(编辑中)</span>
|
||
)}
|
||
</div>
|
||
<div className="text-gray-600 mt-1">
|
||
{classification.prompt_text}
|
||
</div>
|
||
</div>
|
||
))}
|
||
</div>
|
||
</div>
|
||
|
||
{/* 完整提示词预览 */}
|
||
<div>
|
||
<h5 className="text-xs font-medium text-gray-700 mb-2">
|
||
完整提示词预览
|
||
</h5>
|
||
<div className="bg-gray-900 rounded p-3 max-h-48 overflow-y-auto">
|
||
<pre className="text-xs text-gray-100 whitespace-pre-wrap font-mono">
|
||
{fullPrompt}
|
||
</pre>
|
||
</div>
|
||
<div className="mt-1 text-xs text-gray-500">
|
||
字符数: {fullPrompt.length}
|
||
</div>
|
||
</div>
|
||
|
||
{/* 提示信息 */}
|
||
<div className="bg-blue-50 border border-blue-200 rounded p-3">
|
||
<div className="text-xs text-blue-700">
|
||
<strong>提示:</strong>
|
||
<ul className="mt-1 space-y-1">
|
||
<li>• 预览会实时反映您的修改</li>
|
||
<li>• 只有激活状态的分类会包含在提示词中</li>
|
||
<li>• 分类按排序顺序显示</li>
|
||
<li>• 保存后预览将成为实际的提示词</li>
|
||
</ul>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
)}
|
||
</div>
|
||
);
|
||
};
|