feat: 添加[随机匹配]选项到TemplateSegment匹配规则

- 在Rust后端SegmentMatchingRule枚举中添加RandomMatch变体
- 更新TypeScript前端类型定义和SegmentMatchingRuleHelper工具函数
- 修改SegmentMatchingRuleEditor组件支持随机匹配选项
- 添加绿色样式标识随机匹配规则
- 添加完整的单元测试覆盖新功能

遵循promptx/tauri-desktop-app-expert开发规范
This commit is contained in:
imeepos 2025-07-15 10:19:06 +08:00
parent 05d29832b0
commit 73c2187757
4 changed files with 172 additions and 3 deletions

View File

@ -70,6 +70,8 @@ pub enum SegmentMatchingRule {
FixedMaterial,
/// AI分类素材 - 使用指定AI分类的素材
AiClassification { category_id: String, category_name: String },
/// 随机匹配 - 从项目中随机选择合适的素材
RandomMatch,
}
impl Default for SegmentMatchingRule {
@ -84,6 +86,7 @@ impl SegmentMatchingRule {
match self {
Self::FixedMaterial => "固定素材".to_string(),
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 {
matches!(self, Self::AiClassification { .. })
}
/// 检查是否为随机匹配
pub fn is_random_match(&self) -> bool {
matches!(self, Self::RandomMatch)
}
}
/// 轨道片段

View File

@ -92,6 +92,8 @@ export const SegmentMatchingRuleEditor: React.FC<SegmentMatchingRuleEditorProps>
firstClassification.name
));
}
} else if (ruleType === 'random') {
setEditingRule(SegmentMatchingRuleHelper.createRandomMatch());
}
};
@ -106,12 +108,20 @@ export const SegmentMatchingRuleEditor: React.FC<SegmentMatchingRuleEditorProps>
};
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 = [
{ value: 'fixed', label: '固定素材' },
{ value: 'ai_classification', label: 'AI分类素材' },
{ value: 'random', label: '随机匹配' },
];
const classificationOptions = aiClassifications.map(classification => ({
@ -127,7 +137,11 @@ export const SegmentMatchingRuleEditor: React.FC<SegmentMatchingRuleEditorProps>
<span className={`px-2 py-1 rounded text-xs ${
SegmentMatchingRuleHelper.isFixedMaterial(currentRule)
? '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)}
</span>

View File

@ -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();
});
});
});

View File

@ -58,7 +58,8 @@ export interface Track {
*/
export type SegmentMatchingRule =
| "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 } };
},
/**
*
*/
createRandomMatch(): SegmentMatchingRule {
return "RandomMatch";
},
/**
*
*/
@ -86,6 +94,8 @@ export const SegmentMatchingRuleHelper = {
return '固定素材';
} else if (typeof rule === 'object' && 'AiClassification' in rule) {
return `AI分类: ${rule.AiClassification.category_name}`;
} else if (rule === "RandomMatch") {
return '随机匹配';
}
return '未知规则';
},
@ -104,6 +114,13 @@ export const SegmentMatchingRuleHelper = {
return typeof rule === 'object' && 'AiClassification' in rule;
},
/**
*
*/
isRandomMatch(rule: SegmentMatchingRule): boolean {
return rule === "RandomMatch";
},
/**
* AI分类信息
*/