vue vuex application

FIG vue structure:

vuex vue a plug used to manage the shared data, the local data declarations. within their own assembly
when not in use vuex, all methods share data and operational data are nested within parent components, the communication data with the other props and pubsub the following is written by the vuex before the next change a little demo:

Effect icon:
Here Insert Picture Description

Demand:
header portion of the input tasks performed add , displayed in the list;
list each item entry, the mouse moved , the background is grayed out and displays the delete button click on the button. Delete the item; the mouse out of time, to restore it;
footer section : 1. The number of display list and status check has been completed and the number of the total number ; 2. completed when the number is 0, the display is not clear completed task button ; 3. when completed the number equal to the total amount , the whole box 4. when the check. list list is empty , the whole box is not checked; 5. when active Select checked , all entries are checked, to check all the time to check.

Project directory:
Here Insert Picture Description
Code:

1.main.js

import Vue from 'vue'
import App from './App'
import store from './store'

import './base.css' // 全局引入base.css文件

/*new Vue({
  el: '#app',
  components: { App },
  template: '<App/>',
  store // 全局注册store对象
})*/

new Vue({
  el: '#app',
  render: h => h(App),
  store // 全局注册store对象
})

/*new vue({
  el: '#app',
  render: function (createElement) {
    return createElement(App)   // <App/>
  },
  store
})*/

2.App.vue

<template>
  <div class="todo-container">
    <div class="todo-wrap">
      <!-- 传递函数属性(传递性)和绑定事件监听(用于父子组件) -->
      <Header />  <!-- 给标签对象绑定addProject事件监听 -->
      <List />
      <todo-footer />
    </div>
  </div>
</template>

<script>
  import Header from './components/Header'
  import List from './components/List'
  import TodoFooter from './components/Footer'

  export default {
    name: 'App',
    mounted() {
      // 异步获取projects数据并显示: 发送命令给actions
      this.$store.dispatch('reqProjects')
    },
    components: { Header, List, TodoFooter},

  }
</script>

<style>
  /*app*/
  .todo-container {
    width: 600px;
    margin: 0 auto;
  }
  .todo-container .todo-wrap {
    padding: 10px;
    border: 1px solid #ddd;
    border-radius: 5px;
  }
</style>

3.Head.vue

<template>
  <div class="todo-header">
    <input type="text" placeholder="请输入你的任务名称,按回车键确认"
      @keyup.enter="addItem" v-model="content"/>
  </div>
</template>

<script>
    export default {
      props: {
        // addProject: Function
      },
      data () {
        return {
          content: '',  // 组件内部使用的状态,不存在共享的问题 --> 不需要使用vuex管理
        }
      },
      methods: {
        addItem () {
          // 1.检查输入的合法性
          const content = this.content.trim()
          if (!content) { // content默认不为空字符串,可不写 === ''
            alert("不能为空")
            return
          }
          // 2.根据输入生成一个project对象
          const project = {
            content: this.content,
            complete: false
          }
          // 3.将对象添加到projects中
          // this.addProject(project)
          // 触发自定义事件:addProject; 只能用在父子组件,不能跨代通信
          // this.$emit('addProject',project)
          this.$store.dispatch('addProject', project)
          // 4.清空输入
          this.content = ''
        }
      }
    }
</script>

<style>
  /*header*/
  .todo-header input {
    width: 560px;
    height: 28px;
    font-size: 14px;
    border: 1px solid #ccc;
    border-radius: 4px;
    padding: 4px 7px;
  }

  .todo-header input:focus {
    outline: none;
    border-color: rgba(82, 168, 236, 0.8);
    box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(82, 168, 236, 0.6);
  }

</style>

4.List.vue

<template>
  <ul class="todo-main">
    <Item v-for="(project,index) in projects" :key="index" :project="project"
      :index="index" />
  </ul>
</template>

<script>
    import Item from "../components/Item";
    import {mapState} from 'vuex'
    import storageUtil from '../utils/storageUtil'

    export default {
      name: 'List',
      computed: {
        ...mapState(['projects'])
      },
      watch: {  // 监视
        projects: {
          deep: true, // 深度监视
          /*handler: function (val) {
            // 将projects最新的值的json数据保存到localStorage
            // window.localStorage.setItem('project_key', JSON.stringify(val))
            storageUtil.saveProjects(val)
          }*/
          handler: storageUtil.saveProjects
        }
      },
      components: {Item},
    }
</script>

<style>
  /*main*/
  .todo-main {
    margin-left: 0px;
    border: 1px solid #ddd;
    border-radius: 2px;
    padding: 0px;
  }

  .todo-empty {
    height: 40px;
    line-height: 40px;
    border: 1px solid #ddd;
    border-radius: 2px;
    padding-left: 5px;
    margin-top: 10px;
  }
</style>

5.Item.vue

<template>
<!-- onmouseover="" onmouseout="" -->
  <li @mouseenter="handleEnter(true)" @mouseleave="handleEnter(false)" :style="{background: bgColor}">
    <label>
      <input type="checkbox" v-model="project.complete"/>
      <span>{{project.content}}</span>
    </label>
    <button class="btn btn-danger" v-show="isShow" @click="deleteItem">删除</button>
  </li>
</template>

<script>
    export default {
      name: 'Item',
      props: {
        project: Object,
        index: Number,
      },
      data () {
        return {
          bgColor: 'white',
          isShow: false
        }
      },
      methods: {
        handleEnter (isEnter) {
          if (isEnter) {
            this.bgColor = '#aaaaaa'
            this.isShow = true
          }else {
            this.bgColor = 'white'
            this.isShow = false
          }
        },
        deleteItem () {
          const {project, index} = this
          if (window.confirm(`确认删除${project.content}吗`)) {
            this.$store.dispatch('deleteProject',index)
          }
        }
      }
    }
</script>

<style>
  /*item*/
  li {
    list-style: none;
    height: 36px;
    line-height: 36px;
    padding: 0 5px;
    border-bottom: 1px solid #ddd;
  }

  li label {
    float: left;
    cursor: pointer;
  }

  li label li input {
    vertical-align: middle;
    margin-right: 6px;
    position: relative;
    top: -1px;
  }

  li button {
    float: right;
    display: none;
    margin-top: 3px;
  }

  li:before {
    content: initial;
  }

  li:last-child {
    border-bottom: none;
  }
</style>

6.Footer.vue

<template>
  <div class="todo-footer">
    <label>
      <input type="checkbox" v-model="isAllCheck"/>
    </label>
    <span>
          <span>已完成{{completeSize}}</span> / 全部{{totalSize}}
        </span>
    <button class="btn btn-danger" v-show="completeSize" @click="deleteAllComplete">清除已完成任务</button>
  </div>
</template>

<script>
  import {mapGetters,mapActions} from 'vuex'

  export default {
    name: 'Footer',
    computed: {
      ...mapGetters(['completeSize','totalSize']),

      isAllCheck: {
        get () {  //获取多选框的值,当已完成数等于全部数量且已完成数大于0时,勾选(默认>0,可以不写)
          return this.$store.getters.isAllSelect
        },
        set (value) { //监视多选框的值,value是当前多选框最新的值
          this.$store.dispatch('setSelect',value)
        }
      }
    },
    methods: {
      ...mapActions(['deleteAllComplete'])
    }
  }
</script>

<style>
  /*footer*/
  .todo-footer {
    height: 40px;
    line-height: 40px;
    padding-left: 6px;
    margin-top: 5px;
  }

  .todo-footer label {
    display: inline-block;
    margin-right: 20px;
    cursor: pointer;
  }

  .todo-footer label input {
    position: relative;
    top: -1px;
    vertical-align: middle;
    margin-right: 5px;
  }

  .todo-footer button {
    float: right;
    margin-top: 5px;
  }
</style>

7.index.js

/**
 * vuex最核心的管理对象store
 */
import Vue from 'vue'
import Vuex from 'vuex'
import state from './state'
import mutations from './Mutations'
import actions from './Actions'
import getters from './getters'

Vue.use(Vuex)

export default new Vuex.Store({
  state,
  mutations,
  actions,
  getters
})

8.actives.js

/**
 * 包含n个用于间接更新状态的方法的对象模块
 */
import {ADD_PROJECT, DEL_PROJECT, SELECT_ALL_PROJECT, DELETE_COMPLETE_PROJECT, RECEIVE_PROJECTS} from './mutations-types'
import storageUtil from '../utils/storageUtil'

export default {
  addProject( {commit}, project){ //添加project
    // 提交对mutations的请求
    commit(ADD_PROJECT, project)
  },
  deleteProject( {commit}, index){  //根据index删除project
    // 提交对mutations的请求
    commit(DEL_PROJECT, index)
  },
  setSelect ({commit}, check) { //根据全选框值设置多选框是否勾选
    commit(SELECT_ALL_PROJECT, check)
  },
  deleteAllComplete ({commit}) {  //删除所有完成的任务
    commit(DELETE_COMPLETE_PROJECT)
  },
  reqProjects({commit}) { // 异步获取projects并更新状态
    // 模拟
    setTimeout(() => {
      // 读取数据
      const projects = storageUtil.readProjects()
      // 提交
      commit('receive_projects', projects)
    },1000)
  }

}

9.mutations-types.js

/**
 * 所有mutations的名称常量
 * @type {string}
 */
export const ADD_PROJECT = 'add_project'  // 添加project
export const DEL_PROJECT = 'del_project'  // 删除project
export const SELECT_ALL_PROJECT = 'select_all_project'  // 全选/全不选projects
export const DELETE_COMPLETE_PROJECT = 'delete_complete_project' // 删除所有选中的
export const RECEIVE_PROJECTS = 'receive_projects' // 接受projects

10.mutations.js

/**
 * 包含n个用于直接更新状态的方法的对象模块
 */
import {ADD_PROJECT, DEL_PROJECT, SELECT_ALL_PROJECT, DELETE_COMPLETE_PROJECT, RECEIVE_PROJECTS} from './mutations-types'

export default {
  [ADD_PROJECT](state, project) { // 将常量转为变量用 []
    state.projects.unshift(project)
  },
  [DEL_PROJECT](state, index) {
    state.projects.splice(index, 1)
  },
  [SELECT_ALL_PROJECT](state, check) {
    //根据全选框值设置多选框是否勾选
    state.projects.forEach(project => project.complete = check)
  },
  [DELETE_COMPLETE_PROJECT](state) {
    //将project中complete为true的过滤掉,留下false为一个新的数组,重新赋值
    state.projects = state.projects.filter(project => !project.complete)
  },
  [RECEIVE_PROJECTS](state, projects) {
    state.projects = projects
  }

}

11.state.js

/**
 * 状态对象模块
 */
export default {
  // 从localStorage中读取数据
  projects: []
}

12.getters.js

/**
 * 包含n个基于state的getter计算属性方法的对象模块
 */
export default {
  // 完成的数量
  completeSize (state) {
    // 遍历projects,初始化preTotal为0,若project.complete为true则加1,返回最终preTotal值
    return state.projects.reduce((preTotal, project) => preTotal + (project.complete ? 1 : 0), 0)
  },
  // 总数量
  totalSize (state) {
    return state.projects.length
  },
  // 判断是否需要全选
  isAllSelect (state, getters) {  // 用this获取不到当前对象方法,用getters
    return getters.completeSize === getters.totalSize && getters.completeSize
  }
}

13.storageUtil.js

/**
 * 使用localStorage存储数据的工具模块
 * 1.函数
 * 2.模块
 * 需要向外暴露一个功能还是多个功能
 */

const PROJECTS_KEY = 'project_key'

export default {
  saveProjects(val) {
    window.localStorage.setItem(PROJECTS_KEY, JSON.stringify(val))

  },

  readProjects() {
    return JSON.parse(window.localStorage.getItem(PROJECTS_KEY) || '[]');
  }
}

14.base.css

/*base*/
body {
  background: #fff;
}

.btn {
  display: inline-block;
  padding: 4px 12px;
  margin-bottom: 0;
  font-size: 14px;
  line-height: 20px;
  text-align: center;
  vertical-align: middle;
  cursor: pointer;
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05);
  border-radius: 4px;
}

.btn-danger {
  color: #fff;
  background-color: #da4f49;
  border: 1px solid #bd362f;
}

.btn-danger:hover {
  color: #fff;
  background-color: #bd362f;
}

.btn:focus {
  outline: none;
}

15.index.html

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width,initial-scale=1.0">
  <title>vuex_demo</title>
  <link rel="stylesheet" href="./static/css/bootstrap.css">
  <style>
    .router-link-active {
      color: red !important;
    }
  </style>
</head>
<body>
<div id="app"></div>
<!-- built files will be auto injected -->
</body>
</html>

Guess you like

Origin www.cnblogs.com/itzlg/p/11925533.html