linche0859
10/6/2019 - 7:47 AM

Todo list

作法

不使用router-view,利用router-link達到換頁

安裝

npm install vuex-router-sync

main.js

import Vue from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'
import { sync } from 'vuex-router-sync'

Vue.config.productionTip = false

// 使用vuex-router-sync 需加上
sync(store, router)

Vue.directive('focus', {
  // 当被绑定的元素插入到 DOM 中時
  inserted: function(el) {
    // 聚焦元素
    el.focus()
  }
})

new Vue({
  router,
  store,
  render: h => h(App)
}).$mount('#app')

router.js

routes: [
  {
    path: '/all',
    name: 'all'
  },
  {
    path: '/active',
    name: 'active'
  },
  {
    path: '/complete',
    name: 'complete'
  },
  {
    path: '*',
    redirect: '/all'
  }
]

store.js

import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

//  localStorage得操作
const LS = {
  load() {
    return JSON.parse(localStorage.getItem('vue-todos') || '[]')
  },
  save(data) {
    localStorage.setItem('vue-todos', JSON.stringify(data))
  }
}

const filter = {
  all(todos) {
    return todos
  },
  active(todos) {
    return todos.filter(({ complete }) => !complete)
  },
  complete(todos) {
    return todos.filter(({ complete }) => complete)
  }
}

const getters = {
  // 取得內容在陣列中的位置
  // ex. return [0,1,2...]
  todoIndex(state) {
    return filter[state.route.name](state.todos).map(todo =>
      state.todos.indexOf(todo)
    )
  }
}

export default new Vuex.Store({
  strict: true,
  state: {
    todos: []
  },
  getters,
  mutations: {
    setTodos(state, data) {
      state.todos = data
      LS.save(state.todos)
    },
    addTodo(state, data) {
      state.todos.push(data)
      LS.save(state.todos)
    },
    updateTodo(state, { index, content }) {
      state.todos[index].content = content
      LS.save(state.todos)
    },
    doneTodo(state, index) {
      state.todos[index].complete = !state.todos[index].complete
      LS.save(state.todos)
    },
    removeTodo(state, index) {
      state.todos.splice(index, 1)
      LS.save(state.todos)
    }
  },
  actions: {
    INIT_TODOS({ commit }) {
      // 讀取localStorage
      commit('setTodos', LS.load())
    }
  }
})

App.vue

template

<div class="app" id="app">
  <div class="container">
    <section>
      <div class="title">
        <router-link to="/all" class="content">全部</router-link> |
        <router-link to="/active" class="content">未完成</router-link> |
        <router-link to="/complete" class="content">完成</router-link>
      </div>
      <TodoInput />
      <TodoList v-for="index in todoIndex" :key="index" :index="index" />
    </section>
  </div>
</div>

script

import TodoInput from '@/components/TodoInput'
import TodoList from '@/components/TodoList'
export default {
  components: {
    TodoInput,
    TodoList
  },
  computed: {
    todoIndex() {
      return this.$store.getters.todoIndex
    }
  },
  mounted() {
    this.$store.dispatch('INIT_TODOS')
  }
}

components/TodoList

template

<div class="list">
  <input
    type="text"
    v-model.trim="edit"
    v-if="edit !== null"
    v-focus
    @keyup.enter="submitHandler"
    @keyup.esc="cancelHandler"
    @blur="cancelHandler"
  />
  <template v-else>
    <span
      :class="{'dot-checked':todo.complete,'dot':!todo.complete}"
      @click="doneHandler"
    ></span>
    <p :class="{'finished':todo.complete}" @dblclick="editHandler">
      {{ todo.content }}
    </p>
    <span class="cross" @click="removeHandler"></span>
  </template>
</div>

script

export default {
  name: 'todoList',
  data() {
    return {
      edit: null
    }
  },
  props: {
    index: {
      type: Number,
      required: true
    }
  },
  computed: {
    todo() {
      return this.$store.state.todos[this.index]
    }
  },
  methods: {
    doneHandler() {
      this.$store.commit('doneTodo', this.index)
    },
    removeHandler() {
      if (confirm(`是否確認刪除${this.todo.content}?`)) {
        this.$store.commit('removeTodo', this.index)
      }
    },
    editHandler() {
      if (this.todo.complete) return
      this.edit = this.todo.content
    },
    submitHandler() {
      if (!this.edit) return false
      this.$store.commit('updateTodo', {
        index: this.index,
        content: this.edit
      })
      this.cancelHandler()
    },
    cancelHandler() {
      this.edit = null
    }
  }
}

components/TodoInput

template

<div class="input-txt">
  <input
    type="text"
    placeholder="請輸入代辦事項"
    v-model.trim="todo"
    @keyup.enter="submitHandler"
    v-focus
  />
</div>

script

export default {
  name: 'todoInput',
  data() {
    return {
      todo: null
    }
  },
  methods: {
    submitHandler() {
      if (!this.todo) return
      this.$store.commit('addTodo', { content: this.todo, complete: false })
      this.todo = null
    }
  }
}