feat: 添加[随机匹配]选项到TemplateSegment匹配规则
- 在Rust后端SegmentMatchingRule枚举中添加RandomMatch变体 - 更新TypeScript前端类型定义和SegmentMatchingRuleHelper工具函数 - 修改SegmentMatchingRuleEditor组件支持随机匹配选项 - 添加绿色样式标识随机匹配规则 - 添加完整的单元测试覆盖新功能 遵循promptx/tauri-desktop-app-expert开发规范
This commit is contained in:
parent
05d29832b0
commit
73c2187757
|
|
@ -70,6 +70,8 @@ pub enum SegmentMatchingRule {
|
||||||
FixedMaterial,
|
FixedMaterial,
|
||||||
/// AI分类素材 - 使用指定AI分类的素材
|
/// AI分类素材 - 使用指定AI分类的素材
|
||||||
AiClassification { category_id: String, category_name: String },
|
AiClassification { category_id: String, category_name: String },
|
||||||
|
/// 随机匹配 - 从项目中随机选择合适的素材
|
||||||
|
RandomMatch,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for SegmentMatchingRule {
|
impl Default for SegmentMatchingRule {
|
||||||
|
|
@ -84,6 +86,7 @@ impl SegmentMatchingRule {
|
||||||
match self {
|
match self {
|
||||||
Self::FixedMaterial => "固定素材".to_string(),
|
Self::FixedMaterial => "固定素材".to_string(),
|
||||||
Self::AiClassification { category_name, .. } => format!("AI分类: {}", category_name),
|
Self::AiClassification { category_name, .. } => format!("AI分类: {}", category_name),
|
||||||
|
Self::RandomMatch => "随机匹配".to_string(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -96,6 +99,11 @@ impl SegmentMatchingRule {
|
||||||
pub fn is_ai_classification(&self) -> bool {
|
pub fn is_ai_classification(&self) -> bool {
|
||||||
matches!(self, Self::AiClassification { .. })
|
matches!(self, Self::AiClassification { .. })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 检查是否为随机匹配
|
||||||
|
pub fn is_random_match(&self) -> bool {
|
||||||
|
matches!(self, Self::RandomMatch)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 轨道片段
|
/// 轨道片段
|
||||||
|
|
|
||||||
|
|
@ -92,6 +92,8 @@ export const SegmentMatchingRuleEditor: React.FC<SegmentMatchingRuleEditorProps>
|
||||||
firstClassification.name
|
firstClassification.name
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
} else if (ruleType === 'random') {
|
||||||
|
setEditingRule(SegmentMatchingRuleHelper.createRandomMatch());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -106,12 +108,20 @@ export const SegmentMatchingRuleEditor: React.FC<SegmentMatchingRuleEditorProps>
|
||||||
};
|
};
|
||||||
|
|
||||||
const getCurrentRuleType = (rule: SegmentMatchingRule): string => {
|
const getCurrentRuleType = (rule: SegmentMatchingRule): string => {
|
||||||
return SegmentMatchingRuleHelper.isFixedMaterial(rule) ? 'fixed' : 'ai_classification';
|
if (SegmentMatchingRuleHelper.isFixedMaterial(rule)) {
|
||||||
|
return 'fixed';
|
||||||
|
} else if (SegmentMatchingRuleHelper.isAiClassification(rule)) {
|
||||||
|
return 'ai_classification';
|
||||||
|
} else if (SegmentMatchingRuleHelper.isRandomMatch(rule)) {
|
||||||
|
return 'random';
|
||||||
|
}
|
||||||
|
return 'fixed'; // 默认值
|
||||||
};
|
};
|
||||||
|
|
||||||
const ruleTypeOptions = [
|
const ruleTypeOptions = [
|
||||||
{ value: 'fixed', label: '固定素材' },
|
{ value: 'fixed', label: '固定素材' },
|
||||||
{ value: 'ai_classification', label: 'AI分类素材' },
|
{ value: 'ai_classification', label: 'AI分类素材' },
|
||||||
|
{ value: 'random', label: '随机匹配' },
|
||||||
];
|
];
|
||||||
|
|
||||||
const classificationOptions = aiClassifications.map(classification => ({
|
const classificationOptions = aiClassifications.map(classification => ({
|
||||||
|
|
@ -127,7 +137,11 @@ export const SegmentMatchingRuleEditor: React.FC<SegmentMatchingRuleEditorProps>
|
||||||
<span className={`px-2 py-1 rounded text-xs ${
|
<span className={`px-2 py-1 rounded text-xs ${
|
||||||
SegmentMatchingRuleHelper.isFixedMaterial(currentRule)
|
SegmentMatchingRuleHelper.isFixedMaterial(currentRule)
|
||||||
? 'bg-gray-100 text-gray-800'
|
? 'bg-gray-100 text-gray-800'
|
||||||
: 'bg-blue-100 text-blue-800'
|
: SegmentMatchingRuleHelper.isAiClassification(currentRule)
|
||||||
|
? 'bg-blue-100 text-blue-800'
|
||||||
|
: SegmentMatchingRuleHelper.isRandomMatch(currentRule)
|
||||||
|
? 'bg-green-100 text-green-800'
|
||||||
|
: 'bg-gray-100 text-gray-800'
|
||||||
}`}>
|
}`}>
|
||||||
{SegmentMatchingRuleHelper.getDisplayName(currentRule)}
|
{SegmentMatchingRuleHelper.getDisplayName(currentRule)}
|
||||||
</span>
|
</span>
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,130 @@
|
||||||
|
import { describe, it, expect } from 'vitest';
|
||||||
|
import { SegmentMatchingRuleHelper, SegmentMatchingRule } from '../template';
|
||||||
|
|
||||||
|
describe('SegmentMatchingRuleHelper', () => {
|
||||||
|
describe('createFixedMaterial', () => {
|
||||||
|
it('should create a fixed material rule', () => {
|
||||||
|
const rule = SegmentMatchingRuleHelper.createFixedMaterial();
|
||||||
|
expect(rule).toBe('FixedMaterial');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('createAiClassification', () => {
|
||||||
|
it('should create an AI classification rule', () => {
|
||||||
|
const rule = SegmentMatchingRuleHelper.createAiClassification('test-id', 'Test Category');
|
||||||
|
expect(rule).toEqual({
|
||||||
|
AiClassification: {
|
||||||
|
category_id: 'test-id',
|
||||||
|
category_name: 'Test Category'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('createRandomMatch', () => {
|
||||||
|
it('should create a random match rule', () => {
|
||||||
|
const rule = SegmentMatchingRuleHelper.createRandomMatch();
|
||||||
|
expect(rule).toBe('RandomMatch');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('getDisplayName', () => {
|
||||||
|
it('should return correct display name for fixed material', () => {
|
||||||
|
const rule = SegmentMatchingRuleHelper.createFixedMaterial();
|
||||||
|
const displayName = SegmentMatchingRuleHelper.getDisplayName(rule);
|
||||||
|
expect(displayName).toBe('固定素材');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return correct display name for AI classification', () => {
|
||||||
|
const rule = SegmentMatchingRuleHelper.createAiClassification('test-id', 'Test Category');
|
||||||
|
const displayName = SegmentMatchingRuleHelper.getDisplayName(rule);
|
||||||
|
expect(displayName).toBe('AI分类: Test Category');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return correct display name for random match', () => {
|
||||||
|
const rule = SegmentMatchingRuleHelper.createRandomMatch();
|
||||||
|
const displayName = SegmentMatchingRuleHelper.getDisplayName(rule);
|
||||||
|
expect(displayName).toBe('随机匹配');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return unknown rule for invalid rule', () => {
|
||||||
|
const rule = {} as SegmentMatchingRule;
|
||||||
|
const displayName = SegmentMatchingRuleHelper.getDisplayName(rule);
|
||||||
|
expect(displayName).toBe('未知规则');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('isFixedMaterial', () => {
|
||||||
|
it('should return true for fixed material rule', () => {
|
||||||
|
const rule = SegmentMatchingRuleHelper.createFixedMaterial();
|
||||||
|
expect(SegmentMatchingRuleHelper.isFixedMaterial(rule)).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return false for AI classification rule', () => {
|
||||||
|
const rule = SegmentMatchingRuleHelper.createAiClassification('test-id', 'Test Category');
|
||||||
|
expect(SegmentMatchingRuleHelper.isFixedMaterial(rule)).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return false for random match rule', () => {
|
||||||
|
const rule = SegmentMatchingRuleHelper.createRandomMatch();
|
||||||
|
expect(SegmentMatchingRuleHelper.isFixedMaterial(rule)).toBe(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('isAiClassification', () => {
|
||||||
|
it('should return false for fixed material rule', () => {
|
||||||
|
const rule = SegmentMatchingRuleHelper.createFixedMaterial();
|
||||||
|
expect(SegmentMatchingRuleHelper.isAiClassification(rule)).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return true for AI classification rule', () => {
|
||||||
|
const rule = SegmentMatchingRuleHelper.createAiClassification('test-id', 'Test Category');
|
||||||
|
expect(SegmentMatchingRuleHelper.isAiClassification(rule)).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return false for random match rule', () => {
|
||||||
|
const rule = SegmentMatchingRuleHelper.createRandomMatch();
|
||||||
|
expect(SegmentMatchingRuleHelper.isAiClassification(rule)).toBe(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('isRandomMatch', () => {
|
||||||
|
it('should return false for fixed material rule', () => {
|
||||||
|
const rule = SegmentMatchingRuleHelper.createFixedMaterial();
|
||||||
|
expect(SegmentMatchingRuleHelper.isRandomMatch(rule)).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return false for AI classification rule', () => {
|
||||||
|
const rule = SegmentMatchingRuleHelper.createAiClassification('test-id', 'Test Category');
|
||||||
|
expect(SegmentMatchingRuleHelper.isRandomMatch(rule)).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return true for random match rule', () => {
|
||||||
|
const rule = SegmentMatchingRuleHelper.createRandomMatch();
|
||||||
|
expect(SegmentMatchingRuleHelper.isRandomMatch(rule)).toBe(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('getAiClassificationInfo', () => {
|
||||||
|
it('should return null for fixed material rule', () => {
|
||||||
|
const rule = SegmentMatchingRuleHelper.createFixedMaterial();
|
||||||
|
const info = SegmentMatchingRuleHelper.getAiClassificationInfo(rule);
|
||||||
|
expect(info).toBeNull();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return classification info for AI classification rule', () => {
|
||||||
|
const rule = SegmentMatchingRuleHelper.createAiClassification('test-id', 'Test Category');
|
||||||
|
const info = SegmentMatchingRuleHelper.getAiClassificationInfo(rule);
|
||||||
|
expect(info).toEqual({
|
||||||
|
category_id: 'test-id',
|
||||||
|
category_name: 'Test Category'
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return null for random match rule', () => {
|
||||||
|
const rule = SegmentMatchingRuleHelper.createRandomMatch();
|
||||||
|
const info = SegmentMatchingRuleHelper.getAiClassificationInfo(rule);
|
||||||
|
expect(info).toBeNull();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
@ -58,7 +58,8 @@ export interface Track {
|
||||||
*/
|
*/
|
||||||
export type SegmentMatchingRule =
|
export type SegmentMatchingRule =
|
||||||
| "FixedMaterial"
|
| "FixedMaterial"
|
||||||
| { AiClassification: { category_id: string; category_name: string } };
|
| { AiClassification: { category_id: string; category_name: string } }
|
||||||
|
| "RandomMatch";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 片段匹配规则辅助函数
|
* 片段匹配规则辅助函数
|
||||||
|
|
@ -78,6 +79,13 @@ export const SegmentMatchingRuleHelper = {
|
||||||
return { AiClassification: { category_id: categoryId, category_name: categoryName } };
|
return { AiClassification: { category_id: categoryId, category_name: categoryName } };
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建随机匹配规则
|
||||||
|
*/
|
||||||
|
createRandomMatch(): SegmentMatchingRule {
|
||||||
|
return "RandomMatch";
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取规则的显示名称
|
* 获取规则的显示名称
|
||||||
*/
|
*/
|
||||||
|
|
@ -86,6 +94,8 @@ export const SegmentMatchingRuleHelper = {
|
||||||
return '固定素材';
|
return '固定素材';
|
||||||
} else if (typeof rule === 'object' && 'AiClassification' in rule) {
|
} else if (typeof rule === 'object' && 'AiClassification' in rule) {
|
||||||
return `AI分类: ${rule.AiClassification.category_name}`;
|
return `AI分类: ${rule.AiClassification.category_name}`;
|
||||||
|
} else if (rule === "RandomMatch") {
|
||||||
|
return '随机匹配';
|
||||||
}
|
}
|
||||||
return '未知规则';
|
return '未知规则';
|
||||||
},
|
},
|
||||||
|
|
@ -104,6 +114,13 @@ export const SegmentMatchingRuleHelper = {
|
||||||
return typeof rule === 'object' && 'AiClassification' in rule;
|
return typeof rule === 'object' && 'AiClassification' in rule;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查是否为随机匹配
|
||||||
|
*/
|
||||||
|
isRandomMatch(rule: SegmentMatchingRule): boolean {
|
||||||
|
return rule === "RandomMatch";
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取AI分类信息
|
* 获取AI分类信息
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue