LoginSignup
4
4

More than 1 year has passed since last update.

カンバン方式のタスク管理アプリを作成する

Last updated at Posted at 2021-10-28

はじめに

Recursionというサービスのプロジェクト課題の1つであるタスク管理アプリを作成した際に
詰まった箇所が多かったので、頭の整理のためにまとめた。  

今回は、デザインを良い感じにしてくれるVuetifyとドラッグ&ドロップで要素を動かせるVue.Draggableを使用した。

完成品

GitHub - Task Management App
c6c27ea05632584b1d32cb65cd859560

live demo
https://takasu-y.github.io/task_management_app/

開発環境

  • Node.js v14.4.0
  • npm 6.14.5
  • vue/cli 4.5.13

バージョン確認方法

node -v
npm -v
vue --version

環境構築

プロジェクト作成

vue create "プロジェクト名"

  
Vue2を選択する。
※Vue2を選択する理由は、現在(2021年10月時点)VuetifyがVue3をサポートしていないため。

//Vue2を選択
Please pick a preset: (Use arrow keys)
> Default ([Vue 2] babel, eslint) ← こちらを選択
  Default (Vue 3) ([Vue 3] babel, eslint) 
  Manually select features 

  
下記のように表示されたらプロジェクト作成完了。

🎉  Successfully created project "プロジェクト名".
👉  Get started with the following commands:

 $ cd "プロジェクト名"
 $ npm run serve

ディレクトリ移動
起動するためにプロジェクトのディレクトリへ移動する。

cd "プロジェクト名"

プロジェクト起動

npm run serve

下記のLocal URLをcmd+クリック

 DONE  Compiled successfully in 2720ms                                                 12:35:28


  App running at:
  - Local:   http://localhost:8080/ 
  - Network: http://192.168.1.39:8080/

  Note that the development build is not optimized.
  To create a production build, run npm run build.

  

下記の画面が表示されれば成功。

画面が確認出来たら
^ + cで起動停止する。

初期画面.png

Vuetifyのインストール

vue add vuetify

Still proceed -> Yes を選択する。

 WARN  There are uncommitted changes in the current repository, it's recommended to commit or stash them first.
? Still proceed? Yes

Choose a preset -> Defaultを選択する。

? Choose a preset: (Use arrow keys)
  Configure (advanced) 
❯ Default (recommended) 
  Vite Preview (Vuetify 3 + Vite) 
  Prototype (rapid development) 
  Vuetify 3 Preview (Vuetify 3) 

下記のように表示されたらインストール完了。

✔  Successfully invoked generator for plugin: vue-cli-plugin-vuetify
  vuetify  Discord community: https://community.vuetifyjs.com
  vuetify  Github: https://github.com/vuetifyjs/vuetify
  vuetify  Support Vuetify: https://github.com/sponsors/johnleider

Vuetifyが反映されていることを確認
先程と同様にプロジェクトを起動して、
下記のように画面にVuetifyが表示されていれば成功。
vuetify初期画面.png

Material Design Iconsのインストール

Vuetify font Iconを追加を参考にインストールする。

npm install @mdi/font -D

CDNの場合は

<link href="https://cdn.jsdelivr.net/npm/@mdi/font@6.x/css/materialdesignicons.min.css" rel="stylesheet">

Vue.Draggableのインストール

npm i -S vuedraggable

使い方

draggableタグで囲んだ要素を動かせるようになる。
ポイントとしては、
* 動かしたい要素に共通のセレクターを指定する
* draggableタグ内のdraggable属性に動かしたい要素のセレクターを指定する
* draggableを複数使う場合はgroup属性を指定する

開発

いきなりコーディングに入っても上手くいかないので、
以下の①〜③を明確にしてからコーディングを行った。

コードを書く前に考えたこと

①要件を整理する
②何のコンポーネントを作成するか決める
③どこに状態を持たせるか決める

要件

task

  • タスクカードはタスクのタイトルと内容を記入できる。
  • 編集:🖋アイコンまたはタスクカードをダブルクリック
  • お気に入り:★アイコンをクリック
  • チェック:✔️アイコンをクリック
  • 削除:🗑アイコンをクリック

Section

  • セクションの右側+ボタンで新たにセクションを追加。
  • セクション名はダブルクリックすることで編集できる。
  • 各セクション下部の+ボタンで新たにタスクを追加。
  • タスクカードを並べる。
  • 各セクションを横並びにする。

作成するコンポーネント

  • task.vue
  • taskSection.vue
  • taskManager.vue

どこに状態を持たせるか

ワイ「何で子コンポーネントに状態を持たせたらあかんの?」を参考に。

"ハスケル子「子コンポーネントは、親から受け取った状態を映し出す単なるディスプレイとして作りましょう」"

ということなので、タスクの状態はtaskSectionコンポーネントに持たせ、
taskコンポーネントはpropsで状態を受け取って、反映させるだけにした。

task.vue
<script>
export default {
    name: "task",
    props: {
        task: {
            type: Object, //データ型
            required: true, //データ渡し必須
        },
        favorite: {
            type: Boolean,
            required: true,
        },
        check: {
            type: Boolean,
            required: true,
        },
    },
};
</script>

タスクカードの各アイコンがクリックされたら
$emit(任意のイベント名, タスクカードのオブジェクト)で親コンポーネントへ送る。

task.vue
<template>

    ()

     <v-card-actions class="d-flex justify-end">
         <v-icon @click="$emit('edit', task)">mdi-pencil</v-icon>
         <v-icon @click="$emit('favorite', task)" :class="{ 'lime--text': task.favorite}">mdi-star</v-icon>
         <v-icon @click="$emit('check', task)" :class="{ 'teal--text': task.check}">mdi-check-bold</v-icon>
         <v-icon @click="$emit('delete', task)">mdi-delete</v-icon>
     </v-card-actions>

       (

</template>

子コンポーネントから送ったイベントを
親コンポーネントのtemplateへ

taskSection.vue
<template>

    ()

    <draggable
    v-model="tasks"
    draggable=".item"
    group="items"
    >
        <task
            v-for="task in tasks"
            :key="task.id"
            :task="task"
            class="item"
            @edit="editTask"         //子コンポーネントから送られてきたeditイベント
            @favorite="favoriteTask" //子コンポーネントから送られてきたfavoriteイベント
            @check="checkTask"             //子コンポーネントから送られてきたcheckイベント
            @delete="deleteTask"        //子コンポーネントから送られてきたdeleteイベント
        ></task>
    </draggable>

       (

</template>

タスクの状態や変更を加えるメソッドは全てTaskSectionコンポーネントへ記述。

taskSection.vue
<script>
export default {

        (

    data() {
        return {
            section: {
                title: "",
                editable: true,
            },
            tasks: [
                 {
                     id: todoId++,
                     title: "朝ごはんを食べる",
                     body: "プロテインも飲む",
                     editable: false,
                     favorite: false,
                     check: false,
                 },
                 {
                     id: todoId++,
                     title: "昼ごはんを食べる",
                     body: "中華かラーメンを食べる",
                     editable: false,
                     favorite: false,
                     check: false,
                 },
                 {
                     id: todoId++,
                     title: "夜ごはんを食べる",
                     body: "サラダのみにする",
                     editable: false,
                     favorite: false,
                     check: false,
                 },
            ]
        }
    },
    methods: {
        addTask(){
            this.tasks.push({
                id: todoId++,
                title: "",
                body: "",
                editable: true,
                favorite: false,
                check: false,
            })
        },
        editTask(taskObject){
            taskObject.editable = !taskObject.editable;
        },
        favoriteTask(taskObject){
            taskObject.favorite = !taskObject.favorite;
        },
        checkTask(taskObject){
            taskObject.check = !taskObject.check;
        },
        deleteTask(taskObject){
            this.tasks = this.tasks.filter(task => task.id !== taskObject.id);
        },
    },
};
</script>

最後にtaskManager.vueでセクションを並べたり、追加ボタンを設置して完成です。

おまけ

GitHub Pagesに公開する場合は、/docs配下にコンパイルしなければいけないので
vue.config.jsにoutputDir: 'docs/',を記述して npm run buildを行う。

参考

4
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
4