* feat: 生成成功改为 toast 提示
* refactor: 数据预览改为显示示例数据,简化组件逻辑
* fix: 非必填字段预览显示 null
* fix: 修复空值率与必填状态的逻辑
* fix: 切换为非必填时默认空值率 100%
* feat: 字段编辑弹窗添加「完成」按钮 去除关闭按钮,添加取消按钮
* refactor: 删除未使用的 defaultNullRate 功能
* feat: 优化规则管理功能
* fix: 更新 features 测试断言以匹配新增的 testDataGenerator
* refactor: 将 formatFileSize 重命名为 formatBytes 并移至通用工具模块
* refactor: 使用 Label 组件替换原生 label 元素,并在 DecodeResultPaper 中引入 Input 组件
* refactor: 规范化页面组件目录结构
* refactor(JsonTools): 规范化页面组件目录结构
* refactor(StorageCleaner): 规范化页面组件目录结构
* refactor(Timestamp): 规范化页面组件目录结构
* refactor(i18n): 移除 chrome.i18n 国际化,统一使用中文硬编码
* refactor(AppRoot): 移除 RouterProvider 组件,直接渲染子组件
* fix(RouterProvider): 将 goBack 重命名为 goHome,修复跨窗同步 merge 不一致
* fix(ThemeModeProvider): 修复主题快照同步与首屏闪烁问题
* refactor: 将 UI 文案改为中文并优化相关逻辑
* refactor: 清理代码注释并优化组件结构
* feat(Dashboard): 重构仪表板功能组件和逻辑
* refactor: 更新功能键名称并移除 README 文件
* refactor: 重构 TopBar 组件并更新项目结构
* feat(TopBar): 添加搜索功能组件并优化结构
* refactor: 统一使用 Button 组件替换原有 button 元素
* feat: 添加 EmptyPlaceholder 组件并更新相关页面
* feat(TextStatistics): 引入 StatCard 组件以优化统计信息展示
* feat(Timestamp): 重构时间戳转换功能,新增 ConverterForm 组件
* feat(Jwt): 重构 JWT 组件,新增常量和结果视图
* refactor(RightClickRestorer): 用状态配置表简化页面 UI 结构
* fix(business): 修复 skewedRandom 函数中的数学计算错误
* refactor(ImageUploader): 移除预览 URL 相关逻辑并简化粘贴事件处理
* feat(TestDataGenerator): 更新规则编辑提示和警告显示逻辑
* refactor(CopyButton.test): 移除冗余测试用例
* refactor(RouterProvider, chromeStorage, useContextMenuData): 更新存储获取逻辑以移除默认值参数
* refactor(代码重复): 抽取共享工具函数与 UI 组件,消除多处重复实现
* fix(RouterProvider): 修复初始加载状态逻辑,确保在加载前不覆盖 chrome.storage
* feat(TopBar): 添加搜索框快捷键提示功能及相关测试
* refactor(StorageCleaner): 移动 isRestrictedUrl 函数到独立模块并更新相关引用
* fix(StorageCleaner): 修复 IndexedDB 清理竞态、部分成功计数与刷新后状态同步
* fix: 修复路由初始化竞态、存储状态静默覆盖与存储清理误删
* refactor: 统一中文文案并简化组件结构,提升代码可读性
* refactor: 更新 Worker 消息类型以支持生成任务 ID
* fix: 修复 ruleStorage 写入失败处理与 CopyButton 成功态样式
This commit is contained in:
LingandRX
2026-06-27 17:58:59 +08:00
committed by GitHub
parent 4b38133cf8
commit 36b6bfd20d
182 changed files with 5395 additions and 4961 deletions
-1
View File
@@ -37,7 +37,6 @@ import { cn } from '@/lib/utils';
| `business.ts` | 业务数据生成器(订单号、价格、日期、状态等) |
| `technical.ts` | 技术数据生成器(UUID、IPv4、URL |
| `basic.ts` | 基础类型生成器(整数、浮点数、布尔值、字符串) |
| `types.ts` | 生成器库内部类型与通用选项 |
使用示例:
@@ -0,0 +1,22 @@
import { randomInt, randomPick } from '@/lib/generators/random';
describe('generators/random', () => {
it('randomInt 应返回闭区间内的整数', () => {
vi.spyOn(Math, 'random').mockReturnValue(0.5);
expect(randomInt(1, 10)).toBe(6);
expect(randomInt(5, 5)).toBe(5);
});
it('randomPick 应对单元素数组返回该元素', () => {
vi.spyOn(Math, 'random').mockReturnValue(0);
expect(randomPick(['only'])).toBe('only');
});
it('randomPick 应返回数组中的元素', () => {
vi.spyOn(Math, 'random').mockReturnValue(0.99);
expect(randomPick(['a', 'b', 'c'])).toBe('c');
});
});
+1 -31
View File
@@ -1,27 +1,6 @@
/**
* 基础类型生成器
* 包含:随机整数、随机浮点数、随机字符串、从列表选择
*/
import type { GeneratorDefinition } from '@/types/testDataGenerator';
import { randomInt, randomPick } from './random';
/**
* 生成随机整数
*/
function randomInt(min: number, max: number): number {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
/**
* 从数组中随机选择
*/
function randomPick<T>(arr: T[]): T {
return arr[Math.floor(Math.random() * arr.length)];
}
/**
* 随机整数生成器
*/
export const randomIntGenerator: GeneratorDefinition = {
id: 'randomInt',
name: '随机整数',
@@ -56,9 +35,6 @@ export const randomIntGenerator: GeneratorDefinition = {
},
};
/**
* 随机浮点数生成器
*/
export const randomFloat: GeneratorDefinition = {
id: 'randomFloat',
name: '随机浮点数',
@@ -98,9 +74,6 @@ export const randomFloat: GeneratorDefinition = {
},
};
/**
* 随机字符串生成器
*/
export const randomString: GeneratorDefinition = {
id: 'randomString',
name: '随机字符串',
@@ -179,9 +152,6 @@ export const randomString: GeneratorDefinition = {
},
};
/**
* 从列表选择生成器
*/
export const fromList: GeneratorDefinition = {
id: 'fromList',
name: '从列表选择',
+2 -62
View File
@@ -1,27 +1,6 @@
/**
* 业务数据生成器
* 包含:订单号、价格、日期、状态、数量、评分、折扣、库存
*/
import type { GeneratorDefinition } from '@/types/testDataGenerator';
import { randomInt, randomPick } from './random';
/**
* 生成随机整数
*/
function randomInt(min: number, max: number): number {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
/**
* 从数组中随机选择
*/
function randomPick<T>(arr: T[]): T {
return arr[Math.floor(Math.random() * arr.length)];
}
/**
* 格式化日期
*/
function formatDate(date: Date, format: string): string {
const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, '0');
@@ -39,9 +18,6 @@ function formatDate(date: Date, format: string): string {
.replace('ss', seconds);
}
/**
* 订单号生成器
*/
export const orderId: GeneratorDefinition = {
id: 'orderId',
name: '订单号',
@@ -81,9 +57,6 @@ export const orderId: GeneratorDefinition = {
},
};
/**
* 价格生成器
*/
export const price: GeneratorDefinition = {
id: 'price',
name: '价格',
@@ -155,9 +128,6 @@ export const price: GeneratorDefinition = {
},
};
/**
* 生成心理定价(如 9.99, 19.9, 99
*/
function generatePsychologicalPrice(min: number, max: number): number {
const patterns = [
() => {
@@ -180,9 +150,6 @@ function generatePsychologicalPrice(min: number, max: number): number {
return price;
}
/**
* 生成真实分布价格(对数正态分布)
*/
function generateRealisticPrice(min: number, max: number): number {
const mean = (Math.log(min) + Math.log(max)) / 2;
const stdDev = (Math.log(max) - Math.log(min)) / 4;
@@ -195,9 +162,6 @@ function generateRealisticPrice(min: number, max: number): number {
return Math.max(min, Math.min(max, value));
}
/**
* 日期生成器
*/
export const date: GeneratorDefinition = {
id: 'date',
name: '日期',
@@ -248,9 +212,6 @@ export const date: GeneratorDefinition = {
},
};
/**
* 状态生成器
*/
export const status: GeneratorDefinition = {
id: 'status',
name: '状态',
@@ -303,9 +264,6 @@ export const status: GeneratorDefinition = {
},
};
/**
* 数量生成器
*/
export const quantity: GeneratorDefinition = {
id: 'quantity',
name: '数量',
@@ -352,9 +310,6 @@ export const quantity: GeneratorDefinition = {
},
};
/**
* 泊松分布随机数
*/
function poissonRandom(lambda: number): number {
const L = Math.exp(-lambda);
let k = 0;
@@ -366,9 +321,6 @@ function poissonRandom(lambda: number): number {
return k - 1;
}
/**
* 评分生成器
*/
export const rating: GeneratorDefinition = {
id: 'rating',
name: '评分',
@@ -423,9 +375,6 @@ export const rating: GeneratorDefinition = {
},
};
/**
* 偏向高分的随机数(模拟真实评分分布)
*/
function skewedRandom(min: number, max: number): number {
// 使用 Beta 分布模拟评分偏向
const alpha = 3;
@@ -434,13 +383,10 @@ function skewedRandom(min: number, max: number): number {
v = 0;
while (u === 0) u = Math.random();
while (v === 0) v = Math.random();
const x = Math.pow(u, 1 / alpha) / Math.pow(u, 1 / alpha) + Math.pow(v, 1 / beta);
const x = Math.pow(u, 1 / alpha) / (Math.pow(u, 1 / alpha) + Math.pow(v, 1 / beta));
return min + x * (max - min);
}
/**
* 折扣生成器
*/
export const discount: GeneratorDefinition = {
id: 'discount',
name: '折扣',
@@ -492,9 +438,6 @@ export const discount: GeneratorDefinition = {
},
};
/**
* 生成心理折扣(如 5%, 10%, 20%, 30%
*/
function generatePsychologicalDiscount(min: number, max: number): number {
const psychologicalDiscounts = [5, 10, 15, 20, 25, 30, 40, 50, 60, 70, 80];
const validDiscounts = psychologicalDiscounts.filter((d) => d >= min && d <= max);
@@ -504,9 +447,6 @@ function generatePsychologicalDiscount(min: number, max: number): number {
return randomInt(min, max);
}
/**
* 库存生成器
*/
export const stock: GeneratorDefinition = {
id: 'stock',
name: '库存',
-20
View File
@@ -1,17 +1,9 @@
/**
* 生成器库索引
* 导出所有生成器和分类配置
*/
import type { GeneratorDefinition, GeneratorCategory } from '@/types/testDataGenerator';
import { personalGenerators } from './personal';
import { businessGenerators } from './business';
import { technicalGenerators } from './technical';
import { basicGenerators } from './basic';
/**
* 生成器分类配置
*/
export const generatorCategories: GeneratorCategory[] = [
{ id: 'personal', name: '个人信息', icon: 'User' },
{ id: 'business', name: '业务数据', icon: 'Briefcase' },
@@ -19,9 +11,6 @@ export const generatorCategories: GeneratorCategory[] = [
{ id: 'basic', name: '基础类型', icon: 'Hash' },
];
/**
* 所有生成器列表
*/
export const allGenerators: GeneratorDefinition[] = [
...personalGenerators,
...businessGenerators,
@@ -29,23 +18,14 @@ export const allGenerators: GeneratorDefinition[] = [
...basicGenerators,
];
/**
* 根据 ID 获取生成器
*/
export function getGeneratorById(id: string): GeneratorDefinition | undefined {
return allGenerators.find((g) => g.id === id);
}
/**
* 根据分类 ID 获取生成器列表
*/
export function getGeneratorsByCategory(categoryId: string): GeneratorDefinition[] {
return allGenerators.filter((g) => g.categoryId === categoryId);
}
/**
* 搜索生成器
*/
export function searchGenerators(query: string): GeneratorDefinition[] {
const lowerQuery = query.toLowerCase();
return allGenerators.filter(
+1 -56
View File
@@ -1,11 +1,6 @@
/**
* 个人信息生成器
* 包含:中文姓名、邮箱、手机号、身份证号、中文地址、年龄
*/
import type { GeneratorDefinition } from '@/types/testDataGenerator';
import { randomInt, randomPick } from './random';
// 中文姓氏
const SURNAMES = [
'王',
'李',
@@ -59,7 +54,6 @@ const SURNAMES = [
'阎',
];
// 中文名字常用字
const NAME_CHARS = [
'伟',
'芳',
@@ -113,10 +107,8 @@ const NAME_CHARS = [
'慧',
];
// 邮箱域名
const EMAIL_DOMAINS = ['qq.com', '163.com', '126.com', 'gmail.com', 'outlook.com', 'hotmail.com'];
// 省份
const PROVINCES = [
'北京市',
'天津市',
@@ -152,7 +144,6 @@ const PROVINCES = [
'新疆维吾尔自治区',
];
// 城市
const CITIES: Record<string, string[]> = {
: ['东城区', '西城区', '朝阳区', '海淀区'],
: ['黄浦区', '徐汇区', '长宁区', '静安区'],
@@ -162,23 +153,6 @@ const CITIES: Record<string, string[]> = {
: ['成都市', '绵阳市', '德阳市', '宜宾市'],
};
/**
* 生成随机整数
*/
function randomInt(min: number, max: number): number {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
/**
* 从数组中随机选择
*/
function randomPick<T>(arr: T[]): T {
return arr[Math.floor(Math.random() * arr.length)];
}
/**
* 中文姓名生成器
*/
export const chineseName: GeneratorDefinition = {
id: 'chineseName',
name: '中文姓名',
@@ -192,9 +166,6 @@ export const chineseName: GeneratorDefinition = {
},
};
/**
* 邮箱生成器
*/
export const email: GeneratorDefinition = {
id: 'email',
name: '邮箱',
@@ -221,9 +192,6 @@ export const email: GeneratorDefinition = {
},
};
/**
* 手机号生成器
*/
export const chinesePhone: GeneratorDefinition = {
id: 'chinesePhone',
name: '手机号',
@@ -254,9 +222,6 @@ export const chinesePhone: GeneratorDefinition = {
},
};
/**
* 身份证号生成器
*/
export const idCard: GeneratorDefinition = {
id: 'idCard',
name: '身份证号',
@@ -281,20 +246,12 @@ export const idCard: GeneratorDefinition = {
generate: (params) => {
const regions = ['110101', '310101', '440103', '440305', '510104', '330102'];
const region = params.region === 'random' ? randomPick(regions) : (params.region as string);
// 出生日期 1960-2005年
const year = randomInt(1960, 2005);
const month = String(randomInt(1, 12)).padStart(2, '0');
const day = String(randomInt(1, 28)).padStart(2, '0');
const birthday = `${year}${month}${day}`;
// 顺序码
const sequence = String(randomInt(1, 999)).padStart(3, '0');
// 前17位
const prefix17 = region + birthday + sequence;
// 校验码
const weights = [7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2];
const checkCodes = ['1', '0', 'X', '9', '8', '7', '6', '5', '4', '3', '2'];
let sum = 0;
@@ -330,9 +287,6 @@ export const idCard: GeneratorDefinition = {
},
};
/**
* 中文地址生成器
*/
export const chineseAddress: GeneratorDefinition = {
id: 'chineseAddress',
name: '中文地址',
@@ -361,9 +315,6 @@ export const chineseAddress: GeneratorDefinition = {
},
};
/**
* 年龄生成器
*/
export const age: GeneratorDefinition = {
id: 'age',
name: '年龄',
@@ -415,9 +366,6 @@ export const age: GeneratorDefinition = {
},
};
/**
* 正态分布随机数
*/
function normalRandom(min: number, max: number): number {
const mean = (min + max) / 2;
const stdDev = (max - min) / 6;
@@ -430,9 +378,6 @@ function normalRandom(min: number, max: number): number {
return Math.max(min, Math.min(max, value));
}
/**
* 人口统计分布随机数(模拟真实年龄分布)
*/
function demographicRandom(): number {
const ageGroups = [
{ min: 0, max: 14, weight: 0.18 },
+7
View File
@@ -0,0 +1,7 @@
export function randomInt(min: number, max: number): number {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
export function randomPick<T>(arr: readonly T[]): T {
return arr[Math.floor(Math.random() * arr.length)];
}
+1 -45
View File
@@ -1,27 +1,6 @@
/**
* 技术数据生成器
* 包含:UUID、IPv4、URL
*/
import type { GeneratorDefinition } from '@/types/testDataGenerator';
import { randomInt, randomPick } from './random';
/**
* 生成随机整数
*/
function randomInt(min: number, max: number): number {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
/**
* 从数组中随机选择
*/
function randomPick<T>(arr: T[]): T {
return arr[Math.floor(Math.random() * arr.length)];
}
/**
* UUID 生成器
*/
export const uuid: GeneratorDefinition = {
id: 'uuid',
name: 'UUID',
@@ -78,9 +57,6 @@ export const uuid: GeneratorDefinition = {
},
};
/**
* 生成 UUID v4
*/
function generateUUIDv4(): string {
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {
const r = (Math.random() * 16) | 0;
@@ -89,9 +65,6 @@ function generateUUIDv4(): string {
});
}
/**
* 生成 UUID v1(简化版本,模拟时间戳)
*/
function generateUUIDv1(): string {
const now = Date.now();
const timeLow = (now & 0xffffffff).toString(16).padStart(8, '0');
@@ -105,9 +78,6 @@ function generateUUIDv1(): string {
return `${timeLow}-${timeMid}-${timeHi.toString(16)}-${clockSeq.toString(16)}-${node}`;
}
/**
* IPv4 生成器
*/
export const ipv4: GeneratorDefinition = {
id: 'ipv4',
name: 'IPv4',
@@ -142,16 +112,10 @@ export const ipv4: GeneratorDefinition = {
},
};
/**
* 生成完全随机 IPv4
*/
function generateRandomIPv4(): string {
return Array.from({ length: 4 }, () => randomInt(0, 255)).join('.');
}
/**
* 生成内网 IPv4
*/
function generatePrivateIPv4(): string {
const ranges = [
{ prefix: '10', second: () => randomInt(0, 255) },
@@ -166,9 +130,6 @@ function generatePrivateIPv4(): string {
return `${range.prefix}.${range.second()}.${randomInt(1, 254)}.${randomInt(1, 254)}`;
}
/**
* 生成公网 IPv4
*/
function generatePublicIPv4(): string {
let first: number;
do {
@@ -178,9 +139,6 @@ function generatePublicIPv4(): string {
return `${first}.${randomInt(0, 255)}.${randomInt(0, 255)}.${randomInt(1, 254)}`;
}
/**
* URL 生成器
*/
export const url: GeneratorDefinition = {
id: 'url',
name: 'URL',
@@ -250,8 +208,6 @@ export const url: GeneratorDefinition = {
path = `/${randomPick(['about', 'contact', 'products', 'services', 'blog'])}`;
}
}
// 添加查询参数
let query = '';
if (Math.random() > 0.5) {
const params = new URLSearchParams();