動機
いままで様々なタスク管理アプリを使ってきましたが、いまはTogglをメインに使っています。
しかしTogglは、どちらかと言うとタイムトラッキングアプリであって、ほんとうの意味でのタスク管理には向いていません。
個人的には、「カンバン」方式のタスク管理と、各タスクの所要時間を統一的に管理したいのですが、なかなかいいものが見つかりません。
KanbanFlowも試しましたが、個人で使うには重厚で、ちょっと馴染みませんでした。
そこで、VueとElectronで自作しようと考えました。
準備
electron-vue で一発です。
詳細は http://qiita.com/SatoTakumi/items/fd79672d7eb8a9b4a0bb さんが説明されてれいます。
初めてElectronを使いましたが、どこにも詰まることなく導入できたのは驚きです。
Electron側からOSのAPIをいくつか利用できるので、例えば「現在アクティブなWindow」とか「マウスの位置」などを取得することで、Togglのような「非アクティブを検知してタスク消化を促す」仕組みも簡単に実装できそうです。
Componentの構造
Componentはシンプルに以下のように分割しています。
親Component
親Componentである App.vue
はシンプルに、子Componentを回しているだけです。
要素のドラッグを実現するために vue-dragula を利用しています。
このドラッグイベントをキャッチするために、グローバルはイベントバス Vue.vueDragula.eventBus
を利用して「どこにドラッグされたか」を受け取り、 mutation
を叩いています。
<template>
<div id="app-main">
<div id="app-left">
<div>
ToDo
<button id="new-task" @click="newTask">New Task</button>
</div>
<div class="task-card-container" v-dragula="todoTasks" bag="bag" data-status="todo">
<todo-task :task="task" v-for="task in todoTasks" :key="task.id"></todo-task>
</div>
</div>
<div id="app-center">
<div>Doing</div>
<div class="task-card-container" v-dragula="doingTasks" bag="bag" data-status="doing">
<doing-task :task="task" v-for="task in doingTasks" :key="task.id"></doing-task>
</div>
</div>
<div id="app-right">
<div>Done</div>
<div class="task-card-container" v-dragula="doneTasks" bag="bag" data-status="done">
<done-task :task="task" v-for="task in doneTasks" :key="task.id"></done-task>
</div>
</div>
</div>
</template>
<script>
...
mounted () {
Vue.vueDragula.eventBus.$on('drop', ([bag, dropElm, target, source]) => {
console.log(target.children)
const index = Array.prototype.indexOf.call(target.children, dropElm)
const taskId = dropElm.dataset.taskId
const nextStatus = target.dataset.status
this.$store.commit(t.UPDATE_TASK_STATUS, {taskId, nextStatus, index})
})
}
...
</script>
子Component
子Componentは、タスク名と経過時間を表示するだけなので、こちらもシンプルです。
<template>
<div class="task-card" :data-task-id="task.id">
<div class="task-left">{{task.name}}</div>
<div class="task-right">{{duration}}</div>
</div>
</template>
<script>
import moment from 'moment'
import utils from '../utils'
export default {
props: ['task'],
data () {
return {
duration: utils.formatHour(this.task.duration + (moment().unix() - this.task.start))
}
},
mounted () {
setInterval(() => {
this.duration = utils.formatHour(this.task.duration + (moment().unix() - this.task.start))
}, 1000 / 60)
}
}
</script>
一点、秒数を表示する部分を毎秒更新したかったので、this.duration
は computed
ではなく、data
にしてsetInterval
で更新しています。
Store
Storeのデータ構造は tasks
の配列になっています。
const state = {
tasks: [
{
id: 1234,
status: 'done',
name: 'task a',
duration: 60 * 60 * 3,
position: 1,
history: [
{
start: moment('2016-12-01 06:00:00').unix(),
end: moment('2016-12-01 09:00:00').unix()
},
{
start: moment('2016-12-01 08:00:00').unix(),
end: moment('2016-12-01 10:00:00').unix()
}
]
},
...
まとめ
今回のコードはこちらにあります。
https://github.com/tadyjp/hello-electron-vue
Vueを使うと結構簡単にアプリが作れることがわかりました。
OSの機能を利用したリッチなアプリケーションを作る時に、「Vue+Electron」はお手軽な選択肢になるのではないかと思います。
年明けくらいには、実際に配れるアプリにしたいなと考えてます。続報あったらまたお知らせします。
2017/01/26追記
全く進んでいないorz