如何用js隐藏滚动条_如何用 node.js 做个 TODO

最近心血来潮,想做一个 node 工具,作为一个 node.js 新学者,通过边学边翻文档,做了这个工具 todo-kcvo

功能

  • 可以列出所有的 TODO
  • 可以新增 TODO
  • 可以编辑 TODO
  • 可以删除 TODO
  • 可以标记 TOOD 状态为 已完成/未完成

简单来说就是增删改查

使用的库

  • commander - 文档
  • inquirer - 文档

如何使用命令行

新建 cli.js

const { program } = require('commander');

program
  .option('-d, --debug', 'output extra debugging')
  .option('-s, --small', 'small pizza size', 'you')
  .option('-p, --pizza-type <type>', 'flavour of pizza');

program.parse(process.argv);

if (program.debug) console.log(program.opts());
if (program.small) console.log('- small pizza size');
if (program.pizzaType) console.log(`- ${program.pizzaType}`);

// 这是一个官方文档的例

在命令行中输入 node cli -h

599171de8c81a084737606ee46c44711.png

会的出现这个,代表拥有这些选项

新建命令

由于是 TODO 工具,所以我们需要 add 命令

// 插入以下代码
program
  .command('add')
  .description('新增一个任务')
  .action((...args) => {
    const works = args[1] && args[1].join(' ')
    console.log(works)
  });

在命令行中输入 node cli add todo 1

5d12d50de7b5d6b4da793cf6d9dc3cdc.png

新增 index.js 用于写 API 函数

// index.js
module.exports.add = (task) => {
  console.log(`add ${task}`)
}

// cli.js
const { program } = require('commander');
const api = require('./index.js')

program
  .option('-d, --debug', 'output extra debugging')
  .option('-s, --small', 'small pizza size', 'you')
  .option('-p, --pizza-type <type>', 'flavour of pizza');
program
  .command('add')
  .description('新增一个任务')
  .action((...args) => {
    const works = args[1] && args[1].join(' ')
    api.add(works)
  });

program.parse(process.argv);

在命令行运行 node cli add todo 1

5d12d50de7b5d6b4da793cf6d9dc3cdc.png

得到和上次同样的结果


本地数据库

todo 的数据库可以选用home目录

node.js 如果找到 home 目录呢?

Google -- node find hone path

我找的了一条命令

const homedir = require('os').homedir();
// 打印出 homedir 结果是 C:UsersAdministrator

这样好像没毛病,但是如果有的用户,把 home 目录改到其他盘呢?

由于这个信息是存储在环境变量里的

于是我试着

Google -- node.js get home variable

找到了一条命令

const HOME = process.env.HOME;
// 打印出 homedir 结果是 D:Administrator
// 由于我把 home 目录移到 D 盘了

Finally

然而对于没有改 home 目录的用户,process.env.HOME 是空的,所有最终的代码是

const home = process.env.HOME || require('os').homedir();

读写文件

上面说了如何找到路径,那接下来当然是读写了

首先看一下 fs.readFile 文档

bd7e59e375b77b1faf7a563dbfaa91fa.png

第一个参数 path ,那就是

const home = process.env.HOME || require('os').homedir();
path = home + '/.todo'
// 好像不想,在 window 里面,路径是  这样的
path = home = '.todo'
// 也不行,在 mac 系统就不支持
// 那怎么办?

使用 path

没错 path 就是用了拼路径的

于是我写下了这样的代码

const home = process.env.HOME || require('os').homedir();
const fs = require('fs')
const p = require('path')
const dbPath = p.join(home, '.todo')

module.exports.add = (task) => {
  fs.readFile(dbPath, (err, data) => {
    if (err) throw err;
    console.log(data.toString());
  });
  console.log(`add ${task}`)
}

然后在命令行中运行 node cli add

却遇到了报错

00542c4b319509d1ebd252b99b64f3d6.png

找不到 .todo 文件

再看一下文档,fs.readFile() ,可接受 options 参数,其中 {flag:"a+"}, 就是如文件不存在就创建文件

a24e38a3096e375e35717126a66e96a0.png

经过改写

fs.readFile(dbPath,{flag: 'a+'}, (readErr, data) => {
    if (readErr) throw readErr;
    console.log(data.toString())
  });

运行 node cli add

不会报错


写入

读文件之后当然是要写入了,数据存储采用 数组,代码如下

const home = process.env.HOME || require('os').homedir();
const fs = require('fs')
const p = require('path')
const dbPath = p.join(home, '.todo')

module.exports.add = (taskName) => {
  fs.readFile(dbPath,{flag: 'a+'}, (readErr, data) => {
    if (readErr) throw readErr;
    let list
    try {
      list = JSON.parse(data.toString())
    } catch {
      list = []
    }
    console.log(list, 'read')
    const task = {
      title: taskName,
      done: false
    }
    list.push(task)
    const string = JSON.stringify(list)
    fs.writeFile(dbPath, string, (writeErr) => {
      if (writeErr) throw writeErr;
      console.log(list, 'write');
    });

  });
}

3b9dac42ecf73e0828f8be72c70cbcc7.png

可写入文件了


封装优化

最终我们希望优化成这样

  // 读取之前的任务
  const list = db.read()
  // 往里面添加任务
  list.push({title: taskName, done: false})
  // 存储任务
  db.write()

首先我们创建个 db.js 用于写操作数据库的接口

写入 read 和 write 函数

const home = process.env.HOME || require('os').homedir();
const fs = require('fs')
const p = require('path')
const dbPath = p.join(home, '.todo')

const db = {
  read(path = dbPath) {
    // 由于是 readFile 是异步的,所以需要用 promise 返回数据
    return new Promise((resolve, reject) => {
      fs.readFile(path,{flag: 'a+'}, (error, data) => {
        let list
        if (error) return reject(error)
        try {
          list = JSON.parse(data.toString())
        } catch {
          list = []
        }
        resolve(list)
      });
    })
  },
  write(list, path = dbPath) {
    return new Promise((resolve, reject) => {
      fs.writeFile(path, JSON.stringify(list), (error) => {
        if (error) return reject(error);
        resolve(true)
        console.log(list, 'write');
      });
    })
  }
}

module.exports = db

index.js

const db = require('./db.js')

module.exports.add = async (taskName) => {
  // 读取之前的任务
  const list = await db.read()
  // 往里面添加任务
  list.push({title: taskName, done: false})
  // 存储任务
  await db.write(list)
}

增加 clear 功能

首先再 cli 中增加 clear 命令

program
  .command('clear')
  .description('清空任务')
  .action(() => {
    api.clear()
    console.log(`清空任务`);
  });
// 调用了 api 的 clear 函数

index.js

module.exports.clear = async () => {
  await db.write([])
}

所谓的清空就是写入空数组


展示任务

当用户不填参数时,应该展示所有任务

如何识别未传参数

console.log(process.argv)

f890664bd960262e704a62c321419312.png

通过打印 process.argv 我们可以发现前两个参数是默认

当我们输入参数时

node cli abc

648d69c15f95d4e4298d4081f02e6e7f.png

会出现在第三个参数

所以我们可以这样判断

if (process.argv.length === 2) {
  console.log('用户直接运行 node cli 未传参数')
  api.showAllTask()
}

展示任务

module.exports.showAllTask = async () => {
  const list = await db.read()
  list.forEach((task, index) => {
    console.log(`${task.done ? '[*]' : '[_]'}  ${index + 1} - ${task.title}`)
  })
}

如何实现修改

这时候就需要用到 inquirer

基础操作

inquirer
    .prompt([
      /* Pass your questions in here */
      {
        type: 'list',
        name: '任务',
        message: '请选择你想操作的任务',
        choices: ["1", "2", "3"]
      }
    ])
    .then(answers => {
      // Use user feedback for... whatever!!
      console.log(answers.index)
    })
    .catch(error => console.log(error))

补全代码

module.exports.showAllTask = async () => {
  const list = await db.read()

  inquirer
    .prompt([
      /* Pass your questions in here */
      {
        type: 'list',
        name: 'index',
        message: '选择你想操作的任务',
        choices: [...list.map((task, index) => {
         return {name: `${task.done ? '[*]' : '[_]'}  ${index + 1} - ${task.title}`, value: index.toString()}
        }),{name: '+ 创建任务', value: '-2'}, {name: '退出', value: '-1'}]
      }
    ])
    .then(answers => {
      console.log(answers.index)
      const index = parseInt(answers.index)
      if (index >= 0) {
        inquirer
          .prompt([
            {
              type: 'list',
              name: 'value',
              message: '选择你需要的操作',
              choices: [
                {name: "标记已完成",value:"done"},
                {name: "标记未完成", value: 'undone'},
                {name: "删除任务", value: 'delete'},
                {name: "修改任务名", value: 'edit'},
                {name: "退出", value: 'quit'},
              ]
            }
          ])
          .then(answers1 => {
            switch (answers1.value) {
              case 'done':
                list[index].done = true
                db.write(list)
                break
              case 'undone':
                list[index].done = false
                db.write(list)
                break
              case 'delete':
                list.split(index, 1)
                db.write(list)
                break
              case 'edit':
                inquirer
                  .prompt([
                    {
                      type: 'input',
                      name: 'value',
                      message: '新名字:',
                    }
                  ])
                  .then(answers2 => {
                    list[index].title = answers2.value
                    db.write(list)
                  })
                break
            }
          })
      } else if (index === -2) {
        inquirer
          .prompt([
            {
              type: 'input',
              name: 'value',
              message: '新任务:',
            }
          ])
          .then(answers3 => {
            list.push({title: answers3.value, done: false})
            db.write(list)
          })
      }
    })
    .catch(error => {
      console.log(error)
    });
}

发布

目前主要功能已经完成了,是时候发布了

加入 node shebang

在 cli.js 文件第一行加入

#!/usr/bin/env node

package.json

{
  "name": "todo-kcvo", // 自定义名字,不可以和我的重复
  "version": "0.0.1",
  "bin": {
    "t":"cli.js"
  },
  "files": [
    "cli.js",
    "db.js",
    "index.js"
  ],
  "description": "todo-kcvo make you more efficient",
  "main": "cli.js",
  "license": "MIT",
  "dependencies": {
    "commander": "^5.1.0",
    "inquirer": "^7.3.0"
  }
}

使得 cli.js 变成可执行文件

// 命令行运行 
chmod -x cli.js

发布

nrm use npm // npm 源使用官方源
npm adduser // 如果没有 npm 账号,请去官网注册
npm publish 
// 如果发布成功,请尝试
npm install -g /* 你的npm包名 */ 

最后献上完整代码 -- code

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值