源代码
const EventEmitter = require('events')const readline = require('readline')var MuteStream = require('mute-stream') //静默流,字节进入,但它们不会出来var ms = new MuteStream()const { fromEvent } = require('rxjs') //它发出来自给定事件目标的特定类型的事件const option = {type: 'list',name: 'name',message: 'select your name:',choices: [{name: 'vue',value: 'vue'},{name: 'react',value: 'react'},{name: 'angular',value: 'angular'}]}function Prompt(option) {return new Promise((resolve, reject) => {try {const list = new List(option)list.render()list.on('exit', answer => resolve(answer))} catch (error) {reject(error)}})}class List extends EventEmitter {constructor(option) {super()this.name = option.namethis.message = option.messagethis.choices = option.choicesthis.input = process.stdinthis.output = ms.pipe(process.stdout)this.rl = readline.createInterface({input: this.input,output: this.output})this.selected = 0 //选中第几个this.height = 0this.keyPress = fromEvent(this.rl.input, 'keypress').forEach(this.onKeyPress.apply(this))this.haveSelected = false //表示是否选择完毕}//处理按键onKeyPress(keyMap) {console.log('keyMap', keyMap)}//渲染清屏render() {ms.unmute(this.output) //取消静默this.clean()this.output.write(this.getContent())ms.mute(this.output) //继续静默}//清屏clean() {}//界面显示的值getContent() {if (!this.haveSelected) {let title = '\x1B[32m?\x1B[39m \x1B[1m' + this.message + '\x1B[22m\x1B[0m\x1B[0m\x1B[0m\x1B[2m(Use arrow keys)\x1B[22m\n'this.choices.forEach((item, index) => {if (index === this.selected) {if (index === this.choices.length) {title += '\x1B[36m> ' + item.name + '\x1B[39m '} else {title += '\x1B[36m> ' + item.name + '\x1B[39m \n'}} else {if (index === this.choices.length) {title += '\x1B[36m ' + item.name + '\x1B[39m '} else {title += '\x1B[36m ' + item.name + '\x1B[39m \n'}}})this.height = this.choices.length + 3 //height 作为之后清屏的行数return title}}Prompt(option).then(answer => {console.log('answer', answer)})
处理按键
...//处理按键onKeyPress(keyMap) {const key = keyMap[1]if (key.name === 'return') {this.haveSelected = truethis.render()this.colse()this.emit('exit', this.choices[this.selected])} else if (key.name === 'down') {this.selected++if (this.selected > this.choices.length - 1) {this.selected = 0}this.render()} else if (key.name === 'up') {this.selected--if (this.selected < 0) {this.selected = this.choices.length - 1}this.render()}}...
问题:追加内容,没有清屏
const ansiEscapes = require('ansi-escapes')...//清屏clean() {const emptyLines = ansiEscapes.eraseLines(this.height) //擦除整行this.output.write(emptyLines)}
最后处理return时
getContent(){...const name = this.choices[this.selected].namelet title = '\x1B[32m?\x1B[39m \x1B[1m' + this.message + '\x1B[22m\x1B[0m\x1B[0m\x1B[0m\x1B[2m' + name + '\x1B[22m\n'return title}
colse关闭静默
....colse() {ms.unmute(this.output)this.rl.pause()this.rl.close()}
完整代码
const EventEmitter = require('events')const readline = require('readline')const ansiEscapes = require('ansi-escapes')var MuteStream = require('mute-stream') //静默流,字节进入,但它们不会出来var ms = new MuteStream()const { fromEvent } = require('rxjs') //它发出来自给定事件目标的特定类型的事件const option = {type: 'list',name: 'name',message: 'select your name:',choices: [{name: 'vue',value: 'vue'},{name: 'react',value: 'react'},{name: 'angular',value: 'angular'}]}function Prompt(option) {return new Promise((resolve, reject) => {try {const list = new List(option)list.render()list.on('exit', answer => resolve(answer))} catch (error) {reject(error)}})}class List extends EventEmitter {constructor(option) {super()this.name = option.namethis.message = option.messagethis.choices = option.choicesthis.input = process.stdinthis.output = ms.pipe(process.stdout)this.rl = readline.createInterface({input: this.input,output: this.output})this.selected = 0 //选中第几个this.height = 0//fromEvent 第二个参数 所关注的事件名称this.keyPress = fromEvent(this.rl.input, 'keypress').forEach(this.onKeyPress.bind(this))this.haveSelected = false //表示是否选择完毕}//处理按键onKeyPress(keyMap) {const key = keyMap[1]if (key.name === 'return') {this.haveSelected = truethis.colse()this.emit('exit', this.choices[this.selected])this.render()} else if (key.name === 'down') {this.selected++if (this.selected > this.choices.length - 1) {this.selected = 0}this.render()} else if (key.name === 'up') {this.selected--if (this.selected < 0) {this.selected = this.choices.length - 1}this.render()}}//渲染清屏render() {ms.unmute(this.output) //取消静默this.clean()this.output.write(this.getContent())ms.mute(this.output) //继续静默}//清屏clean() {const emptyLines = ansiEscapes.eraseLines(this.height) //擦除整行this.output.write(emptyLines)}colse() {ms.unmute(this.output)this.rl.pause()this.rl.close()}//界面显示的值getContent() {if (!this.haveSelected) {let title = '\x1B[32m?\x1B[39m \x1B[1m' + this.message + '\x1B[22m\x1B[0m\x1B[0m\x1B[0m\x1B[2m(Use arrow keys)\x1B[22m\n'this.choices.forEach((item, index) => {if (index === this.selected) {//选择 则前面加>if (index === this.choices.length) {//最后一个不换行title += '\x1B[36m> ' + item.name + '\x1B[39m '} else {title += '\x1B[36m> ' + item.name + '\x1B[39m \n'}} else {if (index === this.choices.length) {title += '\x1B[36m ' + item.name + '\x1B[39m '} else {title += '\x1B[36m ' + item.name + '\x1B[39m \n'}}})this.height = this.choices.length + 3return title} else {const name = this.choices[this.selected].namelet title = '\x1B[32m?\x1B[39m \x1B[1m' + this.message + '\x1B[22m\x1B[0m\x1B[0m\x1B[0m\x1B[2m' + name + '\x1B[22m\n'return title}}}Prompt(option).then(answer => {console.log('answer', answer)})
总结:感觉学会了一种很高级的思想,以前不敢用class ,现在更加勇了。对代码开发也更有逻辑了




