说在前面

我们在用脚手架初始化项目的时候,往往会进行一些命令交互,用过vue或者react的用脚手架新建项目的应该都进行过命令交互,vue创建的时候会让你选择vue2还是vue3,也有多选要什么配置,也有输入y或者n选择是否用history路由等,这些简单的交互其实用inquire这个包都能实现,但是最近自己在做一个小工具的时候,想要进行文件和文件夹的选择,这时我发现inquire里并没有这个交互功能,所以便自己尝试去在inquire这个库的基础上实现文件选择和文件夹选择这两种类型的交互。

插件效果

通过该插件,我们可以在控制台通过方向键来选择文件和文件夹,具体效果如下:

插件实现

Inquirer.js

Inquirer.js试图为NodeJs做一个可嵌入式的美观的命令行界面。如下图:

inquirer原有参数

  • type

表示提问的类型,包括:input、confirm、 list、rawlist、expand、checkbox、password、editor。

  • name

存储当前输入的值。

  • message

问题的描述。

  • default

默认值。

  • choices

列表选项,在某些type下可用,并且包含一个分隔符(separator);

  • validate

对用户的回答进行校验。

  • filter

对用户的回答进行过滤处理,返回处理后的值。

  • when

根据前面问题的回答,判断当前问题是否需要被回答。

  • pageSize

修改某些type类型下的渲染行数。

  • prefix

修改message默认前缀。

  • suffix

修改message默认后缀。

二次封装

基于inquirer原有功能及参数,增加一些扩展功能及参数

新增参数

  • notNull

是否不能为空,默认为false,设置为true后该参数不能输入空,并且会有不能为空的提示,必须输入字符后才可以回车确认并进行下一步,如下图:

{type:"input",message:"请输入你的姓名:",name:"name",notNull:true
}

  • type

在原有类型中新增两种类型:file、folder,分别为文件选择器和目录选择器,效果如下图:

{type:"file",message:"请选择文件:",name:"fileName",default:"",
},
{type:"folder",message:"请选择文件夹:",name:"folderName",default:"",pathType:'absolute'
},

  • pathType

此项为新增配置,设置目录和文件选择器选中路径输出的格式,默认为相对路径,可以设置为absolute,此时会输出绝对路径,效果如下图:

{type:"file",message:"请选择文件:",name:"fileName",default:"",
},
{type:"folder",message:"请选择文件夹:",name:"folderName",default:"",pathType:'absolute'
},

代码实现

获取指定路径下的文件列表

使用fs中的readdirSync方法可以获取指定目录下的文件列表,具体代码如下:

getFileList = (dirPath)=>{const list = fs.readdirSync(dirPath);return ['../(返回上一级)',...list];
}
获取指定路径下的目录列表

使用fs中的readdirSync方法可以获取指定目录下的文件列表,通过isDirectory方法可以判断文件是否为目录文件,具体代码如下:

getFolderList = (dirPath)=>{const list = fs.readdirSync(dirPath);let resList = [];list.map(item=>{const fullPath = path.join(dirPath,item);if(fs.statSync(fullPath).isDirectory()){resList.push(item + '(进入文件夹)');resList.push(item + '(选择文件夹)');}});return ['../(返回上一级)',...resList];
}
交互类型响应控制

新增的filefolder类型使用自己重新封装的方法,其他依旧使用Inquirer中的响应方法,具体代码如下:

run(option){if(option.type === 'file'){return this.chooseFile(option);}else if(option.type === 'folder'){return this.chooseFolder(option);}else{if(option.notNull){const flag = option.message.slice(-1);if([":",":"].includes(flag)){option.message = option.message.slice(0,-1) + '(不能为空)' + flag;}}return this.defaultType(option);}
}
选择文件
  • 选择的为返回上一级,则将当前目录回退一级:
this.clear(2);
return this.chooseFile(option,path.join(dirPath,'/../'));
  • 选择的是目录则进入选择的目录:
return path.join(dirPath, answer[option.name]);
  • 选择的是文件则返回选择的文件路径并结束操作:
this.clear(2);
return this.chooseFile(option,fullPath);
  • 完整代码如下
chooseFile(option,dirPath = './'){option.type = 'list';option.suffix = "(当前浏览目录:" + path.join(__dirname,dirPath) + ')';option.pageSize = fs.readdirSync('./').length + 1;option.choices = [...this.getFileList(dirPath)];const answer = await inquirer.prompt([option]);if(answer[option.name] == '../(返回上一级)'){this.clear(2);return this.chooseFile(option,path.join(dirPath,'/../'));}else{const fullPath = path.join(dirPath, answer[option.name]);if(!fs.statSync(fullPath).isFile()){this.clear(2);return this.chooseFile(option,fullPath);}else{return path.join(dirPath, answer[option.name]);}}
}
选择目录

  • 选择的为返回上一级,则将当前目录回退一级:
this.clear(2);
return this.chooseFile(option,path.join(dirPath,'/../'));
  • 选择的是进入文件夹,则进入该目录,这里需要将加入用于区分的后缀去掉再返回:
return path.join(dirPath, answer[option.name].slice(0,-7));
  • 选择的是选择文件夹则返回选择的文件夹路径并结束操作:
this.clear(2);
return this.chooseFile(option,fullPath);
  • 完整代码如下
chooseFile(option,dirPath = './'){option.type = 'list';option.suffix = "(当前浏览目录:" + path.join(__dirname,dirPath) + ')';option.pageSize = fs.readdirSync('./').length + 1;option.choices = [...this.getFileList(dirPath)];const answer = await inquirer.prompt([option]);if(answer[option.name] == '../(返回上一级)'){this.clear(2);return this.chooseFile(option,path.join(dirPath,'/../'));}else{const fullPath = path.join(dirPath, answer[option.name]);if(!fs.statSync(fullPath).isFile()){this.clear(2);return this.chooseFile(option,fullPath);}else{return path.join(dirPath, answer[option.name]);}}
}
基本类型调用Inquirer处理

这里增加了notNull(是否不能为空)的参数,代码如下:

defaultType(option){const answer = await inquirer.prompt([option]);if(option.notNull && answer[option.name] === ''){this.clear(2);return this.defaultType(option);}return answer[option.name];
}

插件使用

1、安装依赖

npm install @jyeontu/j-inquirer

2、在代码中引用

const JInquirer = require('@jyeontu/j-inquirer');

3、示例代码

const JInquirer = require('@jyeontu/j-inquirer');
let options = [{type:"input",message:"请输入你的姓名:",name:"name",notNull:true},{type:"input",message:"请输入你的年龄:",name:"age",default:18,validate:(val)=>{if(val < 0 || val > 150){return "请输入0~150之间的数字";}return true;}},{type:"file",message:"请选择文件:",name:"fileName",default:"",},{type:"folder",message:"请选择文件夹:",name:"folderName",default:"",pathType:'absolute'},{type:"list",message:"请选择你喜欢的水果:",name:"fruit",default:"Apple",choices:["Apple","pear","Banana"],},{type:"expand",message:"请选择一个颜色:",name:"color",default:"red",choices:[{key : 'R',value : "red"},{key : 'B',value : "blue"},{key : 'G',value : "green"}]},{type:"checkbox",message:"选择一至多种颜色:",name:"color2",choices:["red","blue","green","pink","orange"]},{type:"password",message:"请输入你的密码:",name:"pwd"},{type:"editor",message:"写下你想写的东西:",name:"editor"}
];
let j = new JInquirer(options);
let res = j.prompt().then(res=>{console.log(res);
});

源码地址

https://gitee.com/zheng_yongtao/node-scripting-tool/tree/master/src/JInquirer

觉得有帮助的同学可以帮忙给我点个star,感激不尽~~~
有什么想法或者改良可以给我提个pr,十分欢迎~~~
有什么问题都可以在评论告诉我~~~

往期精彩

node封装一个控制台进度条插件

vue实现一个鼠标滑动预览视频封面组件

密码太多不知道怎么记录?不如自己写个密码箱小程序

微信小程序实现一个手势图案锁组件

vue封装一个图案手势锁组件

vue封装一个弹幕组件

为了学(mo)习(yu),我竟开发了这样一个插件

程序员的浪漫之——情侣日常小程序

vue简单实现词云图组件

vue + echarts实现中国地图省份下钻联动

使用学过的算法做个游戏很酷的好吗

说在后面

基于inquirer封装一个控制台文件选择器相关推荐

  1. 基于 element-plus 封装一个依赖 json 动态渲染的查询控件

    前情回顾 优惠券网站 m.cps3.cn 基于 el-form 封装一个依赖 json 动态渲染的表单控件 Vue3 封装第三方组件(一)做一个合格的传声筒 功能 使用 vue3 + element- ...

  2. 基于iview 封装一个vue 表格分页组件

    iview 是一个支持中大型项目的后台管理系统ui组件库,相对于一个后台管理系统的表格来说分页十分常见的 iview是一个基于vue的ui组件库,其中的iview-admin是一个已经为我们搭好的后天 ...

  3. 基于Vue3封装一个好用的Websocket

    在Vue3中使用Websocket可以让我们轻松地实现实时数据传输.为了方便使用,我们可以封装一个好用的Websocket类. 安装依赖 首先我们需要安装 ws 库来处理Websocket连接,使用以 ...

  4. formdata上传文件_封装一个多文件断点续传、分片上传、秒传、重试机制的组件...

    本文为:多文件断点续传.分片上传.秒传.重试机制 的更新版,若想看初始版本的实现,请查看该文章. 凡是要知其然知其所以然 文件上传相信很多朋友都有遇到过,那或许你也遇到过当上传大文件时,上传时间较长, ...

  5. 分享基于silverlight的一个大文件上传控件

    虽然codeplex已经有一些多文件,带进度条的上传控件,但是觉得都不是很好用,所以基于上面的控件重新设计了一个上传控件,更好的交互,属性绑定和管理文件. 1. 客户端使用: <mycontro ...

  6. QT基于QPolarChart封装一个极坐标系类(控件显示)

    1.首先需要下载相应的QChart库 一般在安装QT时会选择此库,如若没有,请自行下载. 2.在工程文件中添加:QT += charts 3.新建一个极坐标系的封装类PolarChart: 4.头文件 ...

  7. 封装一个操作文件的函数

    #一个函数只做一件事 def my_file(name,content=None): with open(name,'a+') as f: f.seek(0) if content: f.write( ...

  8. 基于 jQuery 与 Bootstrap 简单封装一个表格分页的组件

    最近遇到一个需求:页面上的数据可能会有很多条,需要将数据分页展示在表格中.项目用的是 jQuery 和 Bootstrap,本来想直接用 bootstrapTable 插件,但是需要额外引入 js 文 ...

  9. 如何基于HTTP设计一个加密解密系统

    在基于B/S 的业务系统中,如果要设计开发加密解密机制.有几种设计选型: 可以使用现成的HTTPS 架构,后端部署用知名签名机构生成的证书. 可以使用现成的HTTPS 架构,后端部署自签名的证书,但是 ...

最新文章

  1. 【串讲总结】RNN、LSTM、GRU、ConvLSTM、ConvGRU、ST-LSTM
  2. 100999凑整到万位进一_四年级数学专项练习
  3. 内存总是不够?HBaseGeoMesa配置优化了解一下
  4. 梯度的直观理解_BP反向传播算法的思考和直观理解 -卷积小白的随机世界
  5. java怎么将图片文件转流并在jsp前端显示_jsp已经被淘汰了吗?
  6. 大数据_Flink_流式处理_简介_流数据处理的应用行业---Flink工作笔记0003
  7. IDEA----将本地svn项目导入idea后没有拉取提交按钮
  8. 四阶五级matlab,微分方程数值解法matlab(四阶龙格—库塔法).ppt
  9. ApacheCN 翻译/校对/笔记整理活动进度公告 2019.9.13
  10. AI不仅要智能,更需要人文:联邦学习重构大数据风控范式
  11. 传统企业PaaS平台功能设计与业务上云思考
  12. 图的遍历——深度优先搜索和广度(宽度)优先搜索(含例题)
  13. arm poky linux,交叉编译iMX6 contex-A9 arm-poky 一些坑
  14. 二维我的世界Dev c++代码
  15. DataGrip 连接 Hive 1.1
  16. 多元高斯分布(Multivariate Gaussian Distribution)(详细说明,便于理解)
  17. 博士申请 | 香港中文大学(深圳)语音与语言实验室招收Speech/NLP方向全奖博士生...
  18. (8)雅思屠鸭第八天:听力中最重要的179个单词(必知必会)
  19. 触摸屏TP基础学习笔记资料整理
  20. 【svn】sliksvn下载与安装

热门文章

  1. 如何新建PDF空白页?实用的PDF新建空白页的方法分享
  2. HDU 1104 Remainder (BFS)
  3. iphone 还原系统过程
  4. 12c rac On redhat 7
  5. MacOS 日历和群晖日历同步问题解决
  6. 使用改进的YOLO-V4网络实时检测水产养殖水下图像中未吃完的饲料颗粒
  7. 智能采购管理系统有哪些应用优势?如何高效提升医药制造业采购管理效率?
  8. 直播电脑配置推荐2023 直播电脑配置清单2023
  9. 淘宝/天猫API:item_search_pro-高级关键字搜索淘宝商品
  10. 关于vue启动项目报错