watch-code-generator
Version:
一个灵活的代码生成器,支持自定义模板和交互式界面
418 lines (369 loc) • 11.9 kB
JavaScript
const inquirer = require('inquirer');
const chalk = require('chalk');
const ora = require('ora');
const Generator = require('./generator');
// 颜色辅助函数
const success = text => chalk.green(text);
const error = text => chalk.red(text);
const warning = text => chalk.yellow(text);
const info = text => chalk.blue(text);
const title = text => chalk.bold.cyan(text);
class UI {
static async start(generator) {
this.generator = generator;
await this.showMainMenu();
}
static async showMainMenu() {
const choices = this.generator.apis.map((api, index) => ({
name: `${api.name} (${warning(api.path)})`,
value: index
}));
// 添加自定义API选项
choices.push({
name: chalk.bold.green('+ 添加自定义API'),
value: 'custom'
});
// 添加筛选功能
const { filter } = await inquirer.prompt([{
type: 'input',
name: 'filter',
message: info('输入关键字筛选API (回车跳过):'),
default: ''
}]);
// 根据关键字筛选API
const filteredChoices = filter
? choices.filter(choice => {
if (choice.value === 'custom') return true; // 保留自定义API选项
const api = this.generator.apis[choice.value];
return api.name.toLowerCase().includes(filter.toLowerCase()) ||
api.path.toLowerCase().includes(filter.toLowerCase()) ||
api.methods.some(m => m.toLowerCase().includes(filter.toLowerCase()));
})
: choices;
const answers = await inquirer.prompt([
{
type: 'checkbox',
name: 'apiIndexes',
message: info('请选择要生成的API (空格选择,回车确认,按a全选):'),
choices: [
...filteredChoices,
new inquirer.Separator(),
// { name: '退出', value: 'quit' }
],
pageSize: 10,
validate: input => {
if (input.length === 0) {
return '请至少选择一个API';
}
return true;
},
onKeypress: (e, key) => {
if (key.name === 'a') {
const list = key.list;
const items = list.choices.filter(item => !item.disabled && !item.separator && item.value !== 'quit' && item.value !== 'custom');
items.forEach(item => {
if (!item.checked) {
list.toggleChoice(item);
}
});
}
}
}
]);
// 处理自定义API选项
if (answers.apiIndexes.includes('custom')) {
// 移除custom选项
answers.apiIndexes = answers.apiIndexes.filter(item => item !== 'custom');
// 收集自定义API信息
await this.createCustomApi();
// 重新显示主菜单
return this.showMainMenu();
}
// if (answers.apiIndexes.includes('quit')) {
// console.log(
// success('\n✔ 已退出代码生成器') +
// '\n 感谢使用,再见!'
// );
// process.exit(0);
// }
console.log(
success('\n✔ 已选择API') +
`\n ${answers.apiIndexes.map(index => this.generator.apis[index].path).join('\n ')}`);
// 如果选择了多个API,进入批量生成模式
if (answers.apiIndexes.length > 1) {
// 过滤掉'quit'选项
const validApiIndexes = answers.apiIndexes.filter(index => index !== 'quit' && index !== 'custom');
if (validApiIndexes.length === 0) {
console.log(
warning('\n⚠ 没有选择任何有效的API')
);
return;
}
const typeChoices = [
...Object.keys(this.generator.config.templates).map(key => ({
name: `生成${key}`,
value: key
})),
{ name: '返回主菜单', value: 'back' }
];
const { types } = await inquirer.prompt([{
type: 'checkbox',
name: 'types',
message: info('请选择要批量生成的组件类型:'),
choices: typeChoices,
validate: input => {
if (input.length === 0) {
return '请至少选择一个组件类型';
}
return true;
}
}]);
if (types.includes('back')) {
return;
}
try {
const apis = answers.apiIndexes.map(index => this.generator.apis[index]);
const results = await this.generator.batchGenerate(apis, types);
console.log(
success('\n✔ 批量生成完成!') +
'\n 成功: ' + success(results.filter(r => r.success).length) +
' 失败: ' + error(results.filter(r => !r.success).length)
);
// 显示详细结果
results.forEach(result => {
if (result.success) {
console.log(
success(' ✔ ') +
`${result.type}: ${info(result.outputPath)}`
);
} else {
console.log(
error(' ✖ ') +
`${result.type}: ${info(result.outputPath)}\n` +
` 错误: ${result.error}`
);
}
});
} catch (e) {
console.log(
error('\n✖ 批量生成失败') +
`\n 错误信息: ${e.message}`
);
}
} else {
// 单个API的情况,保持原有逻辑
for (const apiIndex of answers.apiIndexes) {
await this.showGenerateMenu(this.generator.apis[apiIndex]);
}
}
await this.showMainMenu();
}
/**
* 创建自定义API
* 收集用户输入的API信息并创建自定义API
*/
static async createCustomApi() {
console.log(title('\n创建自定义API'));
// 收集API基本信息
const apiInfo = await inquirer.prompt([
{
type: 'input',
name: 'name',
message: info('API名称:'),
validate: input => input.trim() ? true : 'API名称不能为空'
},
{
type: 'input',
name: 'path',
message: info('API路径 (以/开头):'),
validate: input => {
if (!input.trim()) return 'API路径不能为空';
if (!input.startsWith('/')) return 'API路径必须以/开头';
return true;
}
},
{
type: 'checkbox',
name: 'methods',
message: info('HTTP方法:'),
choices: ['get', 'post', 'put', 'delete', 'patch'],
default: ['get'],
validate: input => input.length ? true : '请至少选择一种HTTP方法'
},
{
type: 'input',
name: 'tags',
message: info('标签 (可选):'),
default: ''
}
]);
// 收集参数信息
const parameters = [];
let addMoreParams = true;
console.log(info('\n添加API参数 (请求参数和响应字段):'));
while (addMoreParams) {
const paramInfo = await inquirer.prompt([
{
type: 'input',
name: 'name',
message: info('参数名称:'),
validate: input => input.trim() ? true : '参数名称不能为空'
},
{
type: 'list',
name: 'type',
message: info('参数类型:'),
choices: ['string', 'number', 'boolean', 'object', 'array'],
default: 'string'
},
{
type: 'input',
name: 'description',
message: info('参数描述 (可选):'),
default: ''
},
{
type: 'confirm',
name: 'required',
message: info('是否必填:'),
default: false
}
]);
parameters.push(paramInfo);
const { addMore } = await inquirer.prompt([
{
type: 'confirm',
name: 'addMore',
message: info('是否继续添加参数?'),
default: false
}
]);
addMoreParams = addMore;
}
// 创建自定义API
const customApi = {
...apiInfo,
parameters
};
try {
const spinner = ora('正在创建自定义API...').start();
const api = this.generator.createCustomApi(customApi);
spinner.succeed(success('✔ 自定义API创建成功!'));
console.log(info(`API名称: ${api.name}`));
console.log(info(`API路径: ${api.path}`));
console.log(info(`参数数量: ${parameters.length}`));
// 询问是否立即生成代码
const { generateNow } = await inquirer.prompt([
{
type: 'confirm',
name: 'generateNow',
message: info('是否立即为此API生成代码?'),
default: true
}
]);
if (generateNow) {
await this.showGenerateMenu(api);
}
return api;
} catch (e) {
console.log(error(`\n✖ 创建自定义API失败: ${e.message}`));
return null;
}
}
static async showGenerateMenu(api) {
console.log(title(`\n选择的API: ${api.path}`));
const typeChoices = [
...Object.keys(this.generator.config.templates).map(key => ({
name: `生成${key}`,
value: key
})),
{ name: '返回主菜单', value: 'back' }
];
const typeAnswer = await inquirer.prompt([
{
type: 'list',
name: 'type',
message: info('选择生成类型:'),
choices: typeChoices,
pageSize: 5
}
]);
if (typeAnswer.type === 'back') {
return;
}
const basePath = `${this.generator.config.outputDir}${api.path}`;
const filterModal = basePath.split('/');
filterModal.pop();
const defaultPath = (typeAnswer.type === 'filter' || typeAnswer.type === 'modal')
? `${filterModal.join('/')}/components/${api.apiPath}_${typeAnswer.type}.vue`
: `${basePath}.vue`;
let finalPath = defaultPath;
const { action } = await inquirer.prompt([
{
type: 'list',
name: 'action',
message: info(`确认生成路径: ${defaultPath}`),
choices: [
{ name: '确认', value: 'confirm' },
{ name: '编辑路径', value: 'edit' }
],
default: 'confirm'
}
]);
if (action === 'edit') {
const pathAnswer = await inquirer.prompt([
{
type: 'input',
name: 'path',
message: info('请输入新的路径:'),
default: defaultPath,
filter: (input) => input.trim() || defaultPath,
validate: input => {
if (!input.trim()) {
return '路径不能为空';
}
return true;
}
}
]);
finalPath = pathAnswer.path;
}
// 显示生成过程中的加载动画
const spinner = ora({
text: info(`正在生成 ${typeAnswer.type} 文件...`),
spinner: 'dots'
}).start();
try {
await new Promise(resolve => setTimeout(resolve, 1000)); // 模拟耗时
const outputPath = await this.generator.generate(api, typeAnswer.type, finalPath);
if (outputPath === false) {
spinner.info('已取消文件生成');
} else {
spinner.succeed(
success('✔ 生成成功!') +
`\n 文件路径: ${info(outputPath)}` +
`\n 生成类型: ${info(typeAnswer.type)}`
);
}
} catch (e) {
spinner.fail(
error('✖ 生成失败') +
`\n 错误信息: ${e.message}` +
`\n 请检查模板文件和权限`
);
}
const { continueWithApi } = await inquirer.prompt([
{
type: 'confirm',
name: 'continueWithApi',
message: info('继续操作当前API?'),
default: true
}
]);
if (continueWithApi) {
await this.showGenerateMenu(api);
}
}
}
inquirer.registerPrompt('autocomplete', require('inquirer-autocomplete-prompt'));
module.exports = UI;