源代码
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.name
this.message = option.message
this.choices = option.choices
this.input = process.stdin
this.output = ms.pipe(process.stdout)
this.rl = readline.createInterface({
input: this.input,
output: this.output
})
this.selected = 0 //选中第几个
this.height = 0
this.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 = true
this.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].name
let 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.name
this.message = option.message
this.choices = option.choices
this.input = process.stdin
this.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 = true
this.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 + 3
return title
} else {
const name = this.choices[this.selected].name
let 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 ,现在更加勇了。对代码开发也更有逻辑了