• 基于inquirer实现一个控制台多级选择交互功能


    说在前面

    🎈在前端脚手架工具中经常会看到控制台输入参数等操作。例如Vue-cli中初始化项目会提示选择一些参数等。所以在开发脚手架工具或者node控制台程序,就需要用到inquirer工具或者类似的工具。但是使用过inquirer工具的同学应该知道,目前inquirer工具支持的交互方式只有以下几种:input, number, confirm, list, rawlist, expand, checkbox, password, editor,最近自己在封装一个脚手架工具时需要使用到多级选择的功能来进行交互,但是看了一遍inquirer的文档,发现并没有可以直接使用的多级选择器类型交互,于是便开始基于inquirer进行二次封装,实现多级选择交互的功能。

    效果展示

    配置好参数后直接运行,效果如下图👇

    在这里插入图片描述

    实现思路

    功能分解

    首先我们先来分解一下这个功能,我们拥有一份树级结构的数据:

    {
        'aa': [ 'aaa1', 'aaa2', 'aaa3', 'aaa4' ],
        'bb': [ 'bbb1', 'bbb2' ],
        'cc': [ 'ccc1' ]
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    我们需要实现以下这几个功能:

    • 1、按级展示对应的数据

    接收的参数数据应该为树级结构的数据,我们应该对其进行分级展示(像电脑上的文件目录层级一样)。

    • 2、非叶子级的节点点击应该可以下钻

    对于非叶子级的节点(及当前节点拥有子节点),我们对其点击应该进行下钻并展示其子节点的数据。

    • 3、叶子节点可以多选

    遍历到叶子级别的数据时,我们需要支持多选功能。

    • 4、跨父节点数据保持

    当我们进入一个叶子层级并进行了选择之后,我们还需要返回进入其他层级进行选择,这时我们需要保存在不同层级下选中的数据内容。

    功能实现

    将功能分解成几个小点之后,我们便可以按照分解好的功能点来逐点实现:

    • 1、按级展示对应的数据

    这一点的功能其实就是一个单选选择框的功能,我们发现这点可以直接使用inquirer中类型为list的组件来实现。

    const options = [{
        type: 'list',
        name: 'choice',
        message: 'your choice:',
        default: 0,
        choices: [
            { value: 1, name: '张三' },
            { value: 2, name: '李四' }
        ]
    }];
    const j = new JInquirer(options);
    const res = j.prompt().then(res=>{
        console.log(res);
    });
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    运行的结果如下:

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Xo1ZhUCX-1668566115879)(https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/922dda3d632a48c09de9c7c7d9dee17b~tplv-k3u1fbpfcp-watermark.image?)]

    这里我们只需要将传入的树级结构数据遍历到的层级的key提出重新组成list的参数即可:

    //叶子节点层级的数据应该为数组类型的数据,所以可以通过isArray来判断当前层级是否为叶子层级
    optionTemp.type = Array.isArray(obj) ? 'checkbox' : 'list';
    optionTemp.choices = Array.isArray(obj) ? obj : Object.keys(obj);
    
    • 1
    • 2
    • 3
    • 2、非叶子级的节点点击应该可以下钻

    遍历到非叶子层级时,我们需要递归处理其子级节点数据,非叶子层级的列表我们使用的是list单选列表来展示,叶子层级的数据我们使用的是checkbox多选列表来展示,所以我们可以根据当前展示的类别来进行不同的递归处理。

    (1)非叶子层级

    对于非叶子层级的数据,我们点击应该进入下一级:

    await this.chooseExpandList(op,deep + 1,pre+answer[option.name],originOption);
    
    • 1

    (2)叶子层级

    对于叶子层级的数据,我们选择完成后应该回到根节点级别继续选择:

    await this.chooseExpandList(originOption,0,'',originOption);
    
    • 1

    完整关键代码如下:

    if(optionTemp.type === 'list'){
        if(pre) pre += '/';
        const op = {...option};
        op.choices = obj[answer[option.name]];
        await this.chooseExpandList(op,deep + 1,pre+answer[option.name],originOption);
    }else{
        this.tempList[pre] = answer[option.name];
        await this.chooseExpandList(originOption,0,'',originOption);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 3、叶子节点可以多选

    叶子节点列表我们需要支持多选功能,这个功能我们可以直接复用inquirer中类型为checkbox的复选框来实现。

    const options1 = [{
        type: 'checkbox',
        name: 'choice',
        message: 'your choice:',
        default: 0,
        choices: [
            { value: 1, name: '张三' },
            { value: 2, name: '李四' }
        ]
    }];
    const j = new JInquirer(options1);
    const res = j.prompt().then(res=>{
        console.log(res);
    });
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    运行结果如下图:

    在这里插入图片描述

    可以使用空格进行选择,ctrl + a 可以全选,ctrl + i 进行反选,回车确认选择内容。

    • 4、跨父节点数据保持

    这里我们可以使用两个方案来实现:

    (1)使用函数入参来记录选中的数据

    我们可以将每次选中的数据在函数的入参中传入,每次回调时更新该参数即可。

    (2)在类中添加一个参数来记录。

    直接在类中添加一个公共参数来记录,这样读取会比较方便。

    比较了两种方法后,我觉得还是直接在类中添加一个公共参数来记录,这样读取会比较方便,所以最后选择了使用方案二来实现该功能。

    class JInquirer{
        constructor(options,config){
            this.options = options; //参数
            this.config = config || {}; //配置
            this.answer = {}; //输出结果容器
            this.tempList = {}; //中间值临时容器变量
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    使用方法

    1、安装依赖

    npm install @jyeontu/j-inquirer
    
    • 1

    2、在代码中引用

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

    3、示例代码

    const options1 = [{
            type:"input",
            message:"请输入你的姓名:",
            name:"name",
            notNull:true
        },{
        type: 'expandList',
        name: 'choice',
        message: '多级选择器测试:',
        choices:{
            'aa': [ 'aaa1', 'aaa2', 'aaa3', 'aaa4' ],
            'bb': [ 'bbb1', 'bbb2' ],
            'cc': [ 'ccc1' ]
          }
    }];
    const j = new JInquirer(options1);
    const res = j.prompt().then(res=>{
        console.log(res);
    });
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    源码地址

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

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

    往期精彩

    面试官:不使用canvas怎么实现一个刮刮卡效果?

    vue封装一个3D轮播图组件

    vue实现一个鼠标滑动预览视频封面组件(精灵图版本)

    node封装一个图片拼接插件

    基于inquirer封装一个控制台文件选择器

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

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

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

    vue封装一个弹幕组件

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

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

    vue简单实现词云图组件

    说在后面

    🎉这里是JYeontu,喜欢算法,GDCPC打过卡;热爱羽毛球,大运会打过酱油。毕业一年,两年前端开发经验,目前担任H5前端开发,算法业余爱好者,有空会刷刷算法题,平时喜欢打打羽毛球🏸 ,也喜欢写些东西,既为自己记录📋,也希望可以对大家有那么一丢丢的帮助,写的不好望多多谅解🙇,写错的地方望指出,定会认真改进😊,在此谢谢大家的支持,我们下文再见🙌。

  • 相关阅读:
    【论文阅读】Whisper: Robust Speech Recognition via Large-Scale Weak Supervision
    哈工大李治军老师操作系统笔记【27】:从生磁盘到文件(Learning OS Concepts By Coding Them !)
    npm、yarn、pnpm如何清除缓存?
    LeetCode - 解题笔记 - 212 - Word Search II
    学弟:我适不适合转行做软件测试?
    一文梳理z-index和层叠上下文
    基于html的美食网站 奶茶网页设计与实现(HTML+CSS+JavaScript)
    径向基函数RBF神经网络相关函数设置
    配置Flutter开发环境
    RxJava 实例
  • 原文地址:https://blog.csdn.net/Twinkle_sone/article/details/127880425