import React, { useState, useEffect } from 'react'; import { PencilIcon, CheckIcon, XMarkIcon } from '@heroicons/react/24/outline'; import { SegmentMatchingRule, SegmentMatchingRuleHelper } from '../../types/template'; import { AiClassification } from '../../types/aiClassification'; import { useTemplateStore } from '../../stores/templateStore'; import { CustomSelect } from '../CustomSelect'; import { AiClassificationService } from '../../services/aiClassificationService'; import { TemplateSegmentWeightService } from '../../services/templateSegmentWeightService'; interface SegmentMatchingRuleEditorProps { segmentId: string; currentRule: SegmentMatchingRule; templateId?: string; // 添加模板ID用于权重配置 onRuleUpdated?: (newRule: SegmentMatchingRule) => void; } /** * 片段匹配规则编辑器组件 * 遵循前端开发规范的组件设计,支持固定素材和AI分类规则的设置 */ export const SegmentMatchingRuleEditor: React.FC = ({ segmentId, currentRule, templateId, onRuleUpdated, }) => { const [isEditing, setIsEditing] = useState(false); const [editingRule, setEditingRule] = useState(currentRule); const [aiClassifications, setAiClassifications] = useState([]); const [loading, setLoading] = useState(false); const [error, setError] = useState(null); // 权重配置相关状态 const [editingWeights, setEditingWeights] = useState>({}); const { updateSegmentMatchingRule } = useTemplateStore(); // 加载AI分类列表 useEffect(() => { const loadClassifications = async () => { try { const classifications = await AiClassificationService.getActiveClassifications(); setAiClassifications(classifications); } catch (error) { console.error('加载AI分类失败:', error); } }; if (isEditing) { loadClassifications(); } }, [isEditing]); // 加载权重数据 useEffect(() => { const loadWeightData = async () => { if (!templateId) return; try { if (SegmentMatchingRuleHelper.isPriorityOrder(editingRule)) { const selectedCategoryIds = typeof editingRule === 'object' && 'PriorityOrder' in editingRule ? editingRule.PriorityOrder.category_ids : []; if (selectedCategoryIds.length > 0) { // 只加载选中分类的权重 const weights = await TemplateSegmentWeightService.getSegmentWeightsForCategories( templateId, segmentId, selectedCategoryIds ); setEditingWeights({ ...weights }); } else { setEditingWeights({}); } } } catch (error) { console.error('Failed to load weight data:', error); } }; if (isEditing && templateId && SegmentMatchingRuleHelper.isPriorityOrder(editingRule)) { loadWeightData(); } }, [isEditing, templateId, segmentId, editingRule]); // 重置编辑状态 useEffect(() => { setEditingRule(currentRule); setError(null); }, [currentRule, isEditing]); const handleStartEdit = () => { setIsEditing(true); setEditingRule(currentRule); setError(null); }; const handleCancelEdit = () => { setIsEditing(false); setEditingRule(currentRule); setError(null); }; const handleSaveRule = async () => { try { setLoading(true); setError(null); // 保存匹配规则 await updateSegmentMatchingRule(segmentId, editingRule); // 如果是按顺序匹配规则且有模板ID,同时保存权重配置 if (SegmentMatchingRuleHelper.isPriorityOrder(editingRule) && templateId) { try { // 获取当前选中的分类ID const selectedCategoryIds = typeof editingRule === 'object' && 'PriorityOrder' in editingRule ? editingRule.PriorityOrder.category_ids : []; // 只保存选中分类的权重,过滤掉未选中的分类 const selectedWeights = Object.fromEntries( Object.entries(editingWeights).filter(([classificationId]) => selectedCategoryIds.includes(classificationId) ) ); await TemplateSegmentWeightService.setSegmentWeights( templateId, segmentId, selectedWeights ); } catch (weightError) { console.error('保存权重配置失败:', weightError); // 权重保存失败不阻止规则保存 } } setIsEditing(false); onRuleUpdated?.(editingRule); } catch (error) { const errorMessage = error instanceof Error ? error.message : '保存匹配规则失败'; setError(errorMessage); } finally { setLoading(false); } }; const handleRuleTypeChange = (ruleType: string) => { if (ruleType === 'fixed') { setEditingRule(SegmentMatchingRuleHelper.createFixedMaterial()); } else if (ruleType === 'ai_classification') { // 如果有可用的AI分类,选择第一个作为默认值 if (aiClassifications.length > 0) { const firstClassification = aiClassifications[0]; setEditingRule(SegmentMatchingRuleHelper.createAiClassification( firstClassification.id, firstClassification.name )); } } else if (ruleType === 'random') { setEditingRule(SegmentMatchingRuleHelper.createRandomMatch()); } else if (ruleType === 'priority_order') { // 默认选择所有激活的AI分类 const categoryIds = aiClassifications.map(c => c.id); setEditingRule(SegmentMatchingRuleHelper.createPriorityOrder(categoryIds)); } }; const handleAiClassificationChange = (classificationId: string) => { const classification = aiClassifications.find(c => c.id === classificationId); if (classification) { setEditingRule(SegmentMatchingRuleHelper.createAiClassification( classification.id, classification.name )); } }; const handlePriorityOrderChange = (categoryId: string, isSelected: boolean) => { if (typeof editingRule === 'object' && 'PriorityOrder' in editingRule) { const currentCategoryIds = editingRule.PriorityOrder.category_ids; let newCategoryIds: string[]; if (isSelected) { // 添加分类ID newCategoryIds = [...currentCategoryIds, categoryId]; // 为新选中的分类设置默认权重(如果还没有的话) if (!editingWeights[categoryId]) { const classification = aiClassifications.find(c => c.id === categoryId); setEditingWeights(prev => ({ ...prev, [categoryId]: classification?.weight || 50 })); } } else { // 移除分类ID newCategoryIds = currentCategoryIds.filter(id => id !== categoryId); // 从权重配置中移除未选中的分类 setEditingWeights(prev => { const newWeights = { ...prev }; delete newWeights[categoryId]; return newWeights; }); } setEditingRule(SegmentMatchingRuleHelper.createPriorityOrder(newCategoryIds)); } }; // 权重编辑处理函数 const handleWeightChange = (classificationId: string, weight: number) => { setEditingWeights(prev => ({ ...prev, [classificationId]: weight, })); }; const validateWeight = (weight: number): boolean => { return weight >= 0 && weight <= 100; }; const getCurrentRuleType = (rule: SegmentMatchingRule): string => { if (SegmentMatchingRuleHelper.isFixedMaterial(rule)) { return 'fixed'; } else if (SegmentMatchingRuleHelper.isAiClassification(rule)) { return 'ai_classification'; } else if (SegmentMatchingRuleHelper.isRandomMatch(rule)) { return 'random'; } else if (SegmentMatchingRuleHelper.isPriorityOrder(rule)) { return 'priority_order'; } return 'fixed'; // 默认值 }; const ruleTypeOptions = [ { value: 'fixed', label: '固定素材' }, { value: 'ai_classification', label: 'AI分类素材' }, { value: 'random', label: '随机匹配' }, { value: 'priority_order', label: '按顺序匹配' }, ]; const classificationOptions = aiClassifications.map(classification => ({ value: classification.id, label: classification.name, })); if (!isEditing) { return (
匹配规则: {SegmentMatchingRuleHelper.getDisplayName(currentRule)}
); } return (
编辑匹配规则:
{SegmentMatchingRuleHelper.isAiClassification(editingRule) && (
)} {SegmentMatchingRuleHelper.isPriorityOrder(editingRule) && (
系统将按照AI分类的权重顺序依次尝试匹配素材,权重高的分类优先匹配
{aiClassifications .sort((a, b) => (b.weight || 0) - (a.weight || 0)) // 按权重降序排列 .map((classification) => { const currentCategoryIds = typeof editingRule === 'object' && 'PriorityOrder' in editingRule ? editingRule.PriorityOrder.category_ids : []; const isSelected = currentCategoryIds.includes(classification.id); return (
handlePriorityOrderChange(classification.id, e.target.checked)} className="w-3 h-3 text-blue-600 border-gray-300 rounded focus:ring-blue-500" /> {classification.name} {/* 权重编辑区域 */}
{isSelected ? ( // 选中时显示可编辑的输入框
权重: { const value = parseInt(e.target.value) || 0; if (validateWeight(value)) { handleWeightChange(classification.id, value); } }} className="w-12 text-xs text-center border border-gray-300 rounded px-1 py-0.5 focus:border-blue-500 focus:ring-1 focus:ring-blue-500" onClick={(e) => e.stopPropagation()} />
) : ( // 未选中时显示只读的权重值 权重: {classification.weight || 0} )}
); })}
{typeof editingRule === 'object' && 'PriorityOrder' in editingRule && editingRule.PriorityOrder.category_ids.length === 0 && (
⚠️ 请至少选择一个AI分类
)} {/* 权重配置说明 */} {templateId && typeof editingRule === 'object' && 'PriorityOrder' in editingRule && editingRule.PriorityOrder.category_ids.length > 0 && (
💡 选中分类后可直接在右侧编辑权重值(0-100),权重越高优先级越高。保存时会同时保存权重配置。
)}
)}
{error && (
⚠️ {error}
)} {loading && (
正在保存匹配规则...
)}
); };