在前端开发需要生成各类数据,每次手写很痛苦~参考以下方法,可以快速生成需要的数据。
/**
* 通用 Mock 数据生成器
* 支持多种数据类型和自定义生成规则
*/
/**
*
*
* // main.js - 使用示例
import mockDataGenerator, {
generateFromTemplate,
dataTemplates
} from './mockDataGenerator.js';
// 示例1: 基本使用
const basicData = mockDataGenerator({
count: 5,
fields: {
name: { type: 'string', pattern: '项目 {id}' },
value: { type: 'number', min: 10, max: 100 },
active: { type: 'boolean' }
}
});
// 示例2: 使用预定义模板
const users = generateFromTemplate('user', {
count: 3,
fields: {
role: { options: ['管理员', '用户', '游客'] }
}
});
console.log('用户数据:', users);
// 示例3: 自定义商品数据
const products = mockDataGenerator({
count: 8,
startId: 100,
fields: {
title: (i, id) => `智能手机 ${id}`,
price: { type: 'number', min: 1000, max: 5000 },
brand: {
type: 'string',
options: ['Apple', 'Samsung', 'Huawei', 'Xiaomi']
},
specs: {
type: 'object',
properties: {
storage: { type: 'string', options: ['64GB', '128GB', '256GB'] },
color: { type: 'string', options: ['黑色', '白色', '蓝色'] },
screen: { type: 'string', options: ['6.1寸', '6.7寸'] }
}
},
images: {
type: 'array',
length: 3,
itemPattern: `https://example.com/images/product_{id}_{index}.jpg`
}
},
onItemCreate: (item) => {
// 添加计算字段
item.discountPrice = parseFloat((item.price * 0.9).toFixed(2));
item.isExpensive = item.price > 3000;
return item;
}
});
// 示例4: 文章数据
const articles = generateFromTemplate('article', {
count: 4,
onItemCreate: (article) => {
// 根据阅读量设置热门标志
article.isHot = article.readCount > 5000;
return article;
}
});
*
*/
/**
* 根据类型生成值
* @param {string} type 数据类型
* @param {Object} options 配置选项
* @returns {*} 生成的值
*/
function generateByType(type, options = {}) {
const {
i,
currentId,
item,
min = 0,
max = 100,
pattern,
prefix = '',
suffix = '',
options: valueOptions = []
} = options;
switch (type.toLowerCase()) {
case 'number':
case 'float':
const numberValue = min + Math.random() * (max - min);
return parseFloat(numberValue.toFixed(2));
case 'integer':
case 'int':
return Math.floor(min + Math.random() * (max - min + 1));
case 'boolean':
case 'bool':
return Math.random() > 0.5;
case 'string':
if (pattern) {
// 支持模板字符串,如: "Item {id}" -> "Item 1"
return pattern
.replace(/{id}/g, currentId)
.replace(/{index}/g, i)
.replace(/{prefix}/g, prefix)
.replace(/{suffix}/g, suffix);
}
if (valueOptions.length > 0) {
return valueOptions[Math.floor(Math.random() * valueOptions.length)];
}
return `${prefix}${currentId}${suffix}`;
case 'date':
const daysAgo = Math.floor(Math.random() * 365);
const date = new Date(Date.now() - daysAgo * 24 * 60 * 60 * 1000);
return date.toISOString().split('T')[0]; // 返回 YYYY-MM-DD 格式
case 'datetime':
return new Date(Date.now() - Math.random() * 365 * 24 * 60 * 60 * 1000).toISOString();
case 'image':
if (pattern) {
return pattern
.replace(/{id}/g, currentId)
.replace(/{index}/g, i)
.replace(/{width}/g, options.width || 300)
.replace(/{height}/g, options.height || 200);
}
return `https://picsum.photos/seed/${currentId}/${options.width || 300}/${options.height || 200}`;
case 'email':
return `user${currentId}@example.com`;
case 'phone':
return `1${Math.floor(1000000000 + Math.random() * 9000000000)}`;
case 'color':
return `#${Math.floor(Math.random() * 16777215).toString(16).padStart(6, '0')}`;
case 'array':
const arrayLength = options.length || Math.floor(Math.random() * 5) + 1;
return Array.from({ length: arrayLength }, (_, index) =>
options.itemPattern
? options.itemPattern.replace(/{index}/g, index).replace(/{id}/g, currentId)
: `item_${currentId}_${index}`
);
case 'object':
return options.properties ?
Object.keys(options.properties).reduce((obj, key) => {
obj[key] = generateByType(options.properties[key].type, {
...options.properties[key],
i,
currentId,
item
});
return obj;
}, {}) : {};
default:
return null;
}
}
/**
* 处理字段配置
* @param {*} fieldConfig 字段配置
* @param {number} i 索引
* @param {number} currentId 当前ID
* @param {Object} item 当前项目
* @returns {*} 字段值
*/
function processFieldConfig(fieldConfig, i, currentId, item) {
if (typeof fieldConfig === 'function') {
return fieldConfig(i, currentId, item);
}
if (typeof fieldConfig === 'object' && fieldConfig !== null) {
const {
type = 'string',
value,
options,
min,
max,
generator,
...rest
} = fieldConfig;
if (value !== undefined) {
return value;
}
if (generator) {
return generator(i, currentId, item);
}
if (options && Array.isArray(options)) {
return options[Math.floor(Math.random() * options.length)];
}
return generateByType(type, {
i,
currentId,
item,
min,
max,
options,
...rest
});
}
return fieldConfig;
}
/**
* 主生成函数
* @param {Object} config 配置对象
* @returns {Array} 生成的模拟数据数组
*/
function mockDataGenerator(config = {}) {
const {
count = 20,
startId = 1,
fields = {},
onItemCreate = null,
unique = true
} = config;
const data = [];
const usedIds = new Set();
for (let i = 0; i < count; i++) {
let currentId = startId + i;
// 确保ID唯一
if (unique) {
while (usedIds.has(currentId)) {
currentId++;
}
usedIds.add(currentId);
}
const item = { id: currentId };
// 为每个定义的字段生成值
Object.keys(fields).forEach(fieldName => {
try {
item[fieldName] = processFieldConfig(fields[fieldName], i, currentId, item);
} catch (error) {
console.warn(`生成字段 "${fieldName}" 时出错:`, error);
item[fieldName] = null;
}
});
// 执行回调
let finalItem = item;
if (onItemCreate) {
try {
const result = onItemCreate(item, i, currentId);
if (result !== undefined && result !== null) {
finalItem = result;
}
} catch (error) {
console.warn('onItemCreate 回调执行出错:', error);
}
}
data.push(finalItem);
}
return data;
}
/**
* 预定义的数据模板
*/
const dataTemplates = {
// 用户数据模板
user: (overrides = {}) => ({
count: 10,
startId: 1,
fields: {
username: { type: 'string', pattern: 'user_{id}' },
email: { type: 'email' },
firstName: { type: 'string', options: ['张', '李', '王', '刘', '陈'] },
lastName: { type: 'string', options: ['三', '四', '五', '六', '七'] },
age: { type: 'integer', min: 18, max: 60 },
phone: { type: 'phone' },
avatar: { type: 'image', width: 100, height: 100 },
isActive: { type: 'boolean' },
createdAt: { type: 'datetime' },
...overrides.fields
},
...overrides
}),
// 商品数据模板
product: (overrides = {}) => ({
count: 20,
startId: 1,
fields: {
name: { type: 'string', pattern: '商品 {id}' },
description: { type: 'string', pattern: '这是第 {id} 个商品的描述' },
price: { type: 'number', min: 10, max: 1000 },
originalPrice: (i, id) => {
const price = 10 + Math.random() * 1000;
return parseFloat((price * (1 + Math.random() * 0.5)).toFixed(2));
},
stock: { type: 'integer', min: 0, max: 100 },
image: { type: 'image', width: 300, height: 200 },
category: {
type: 'string',
options: ['电子产品', '服装', '食品', '家居', '图书']
},
tags: {
type: 'array',
length: 3,
itemPattern: 'tag_{index}'
},
isAvailable: { type: 'boolean' },
rating: { type: 'number', min: 1, max: 5 },
reviewCount: { type: 'integer', min: 0, max: 1000 },
...overrides.fields
},
...overrides
}),
// 文章数据模板
article: (overrides = {}) => ({
count: 15,
startId: 1,
fields: {
title: { type: 'string', pattern: '文章标题 {id}' },
content: { type: 'string', pattern: '这是第 {id} 篇文章的内容...' },
author: { type: 'string', options: ['作者A', '作者B', '作者C'] },
publishDate: { type: 'date' },
readCount: { type: 'integer', min: 0, max: 10000 },
likeCount: { type: 'integer', min: 0, max: 500 },
category: {
type: 'string',
options: ['技术', '生活', '旅游', '美食', '财经']
},
tags: {
type: 'array',
options: ['热门', '推荐', '原创', '转载'],
length: 2
},
isPublished: { type: 'boolean' },
...overrides.fields
},
...overrides
})
};
/**
* 使用模板生成数据
* @param {string} templateName 模板名称
* @param {Object} overrides 覆盖配置
* @returns {Array} 生成的数据
*/
function generateFromTemplate(templateName, overrides = {}) {
const template = dataTemplates[templateName];
if (!template) {
throw new Error(`模板 "${templateName}" 不存在。可用模板: ${Object.keys(dataTemplates).join(', ')}`);
}
const config = typeof template === 'function' ? template(overrides) : template;
return mockDataGenerator(config);
}
// 导出内容
export {
mockDataGenerator,
generateByType,
processFieldConfig,
generateFromTemplate,
dataTemplates
};
// 默认导出
export default mockDataGenerator;