feat: 实现测试数据生成器完整功能

- 添加类型定义 (FieldConfig, DataRule, GeneratorDefinition 等)
- 实现 21 个数据生成器 (个人信息/业务数据/技术数据/基础类型)
- 实现 Web Worker 后台数据生成引擎
- 实现规则存储与管理功能
- 实现 JSON/CSV 数据导出功能
- 开发完整 UI 组件 (字段管理/生成器选择/数据预览/导出面板等)
- 集成到应用功能配置,添加国际化支持

功能特性:
- 实时预览生成的数据
- 字段配置 (名称/描述/必填/空值率/唯一性约束)
- 规则管理 (保存/加载/搜索/导入导出)
- Web Worker 后台生成,支持进度显示和取消
- 响应式布局,支持多种设备

docs: 更新 TASKS.md 所有子任务复选框为已完成状态
This commit is contained in:
雨霖铃
2026-06-06 09:23:02 +08:00
parent 2b84eda24f
commit 1a92e88934
27 changed files with 4795 additions and 1 deletions
+165
View File
@@ -0,0 +1,165 @@
/**
* 测试数据生成器 Web Worker
* 在后台线程中执行数据生成,避免阻塞 UI
*/
import { getGeneratorById } from '@/lib/generators';
import type {
FieldConfig,
WorkerMessage,
GenerateResult,
GenerateProgress,
} from '@/types/testDataGenerator';
// 生成结果缓存
let generatedData: Record<string, unknown>[] = [];
let isCancelled = false;
/**
* Worker 消息处理器
*/
self.onmessage = async (e: MessageEvent<WorkerMessage>) => {
const data = e.data;
const { type } = data;
switch (type) {
case 'start':
isCancelled = false;
await handleStart(data.payload);
break;
case 'cancel':
isCancelled = true;
break;
}
};
/**
* 处理开始生成消息
*/
async function handleStart(payload: {
fields: FieldConfig[];
count: number;
csvMode: boolean;
}): Promise<void> {
const { fields, count } = payload;
generatedData = [];
try {
// 验证所有生成器是否存在
for (const field of fields) {
const generator = getGeneratorById(field.generatorId);
if (!generator) {
self.postMessage({
type: 'error',
payload: { error: `生成器 "${field.generatorId}" 不存在` },
});
return;
}
}
const startTime = Date.now();
const warnings: string[] = [];
// 生成数据
for (let i = 0; i < count; i++) {
if (isCancelled) {
self.postMessage({
type: 'complete',
payload: {
success: false,
error: '生成已取消',
},
});
return;
}
const record: Record<string, unknown> = {};
for (const field of fields) {
const generator = getGeneratorById(field.generatorId);
if (!generator) continue;
// 空值率控制
if (!field.required && Math.random() * 100 < field.nullRate) {
record[field.name] = null;
continue;
}
try {
// 唯一性约束处理
if (field.unique) {
let value: string | undefined;
const maxRetries = 100;
let retryCount = 0;
// 小数据量:随机生成 + 重试
if (count <= 1000) {
while (retryCount < maxRetries) {
value = generator.generate(field.params);
const isUnique = !generatedData.some((item) => item[field.name] === value);
if (isUnique) break;
retryCount++;
value = undefined;
}
} else {
// 大数据量:使用索引生成策略
if (generator.generateAtIndex) {
value = generator.generateAtIndex(field.params, i);
} else {
// 回退到随机生成
value = generator.generate(field.params);
}
}
if (value !== undefined) {
record[field.name] = value;
} else {
warnings.push(`字段 "${field.name}" 第 ${i + 1} 行唯一值生成失败`);
record[field.name] = generator.generate(field.params);
}
} else {
record[field.name] = generator.generate(field.params);
}
} catch (error) {
warnings.push(`字段 "${field.name}" 第 ${i + 1} 行生成错误: ${error}`);
record[field.name] = null;
}
}
generatedData.push(record);
// 进度回调(每1000条)
if ((i + 1) % 1000 === 0 || i === count - 1) {
const progress: GenerateProgress = {
progress: Math.round(((i + 1) / count) * 100),
generated: i + 1,
total: count,
estimatedTimeLeft: Math.round(((Date.now() - startTime) / (i + 1)) * (count - i - 1)),
};
self.postMessage({ type: 'progress', payload: progress });
}
}
const duration = Date.now() - startTime;
const successCount = generatedData.filter((item) => Object.keys(item).length > 0).length;
const result: GenerateResult = {
success: true,
data: generatedData,
warnings: warnings.length > 0 ? warnings : undefined,
stats: {
total: count,
success: successCount,
failed: count - successCount,
duration,
},
};
self.postMessage({ type: 'complete', payload: result });
} catch (error) {
self.postMessage({
type: 'error',
payload: { error: `生成失败: ${error}` },
});
}
}