Tree Component With Vue & Vuex

by huang shiyu

HTML

<script src="https://cdn.bootcss.com/vuex/2.4.0/vuex.js"></script>
<script src="https://cdn.bootcss.com/vue/2.4.2/vue.min.js"></script>
<div id="app">
  {{ all | JSON }}
  <group :content.sync="all"></group>
</div>

CSS

.item {
  margin-left: 1rem;
  margin-top: 1rem;
}
.group {
  padding: 1rem 0 0 1rem;
}

JavaScript

const deepClone = obj => JSON.parse(JSON.stringify(obj))
Vue.use(Vuex)
const store = new Vuex.Store({
	state: {
  	all: {
      name: '0', 
      list: [
        { name: '0-0', content: 'some text' }
      ]
    },
    formData: {
    	group: {
      	name: '',
      	list: []
      },
      item: {
        name: '',
        content: ''
      }
    }
  },
  mutations: {
    add(state, { content, type }) {
    	const newItem = deepClone(state.formData[type])
      const list = content.list
      newItem.name = content.name + '-' + (list.length > 0 ? list[list.length - 1].name.match(/\d+/g).pop() - (-1) : '0')
      content.list.push(newItem)
    },
    
    remove(state, { item, parent }) {
      let index = parent.list.findIndex(_item => item.name === _item.name)
      if (index >= 0) {
        parent.list.splice(index, 1)
      }
    }
  },
  getters: {
  	all: state => state.all
  }
})

const item = Vue.extend({
	props: ['content'],
  methods: {
  	remove() {
    	this.$emit('remove', this.content)
    }
  },
	template:
  `<div class="item">
  	<span>{{ content.name }}</span>
    <input v-model="content.content" />
    <button @click="remove">Delete Item</button>
  </div>
  `
})

const group = Vue.extend({
	name: 'group',
	props: ['content'],
  components: { item },
  methods: {
  	add(content, type) {
    	this.$store.commit('add', { content, type })
    },
    remove(item) {
    	this.$store.commit('remove', { item, parent: this.content })
    },
    componentType(item) {
    	return item.list ? 'group' : 'item'
    },
  },
	template: 
  `<div class="group">
  	<span>{{ content.name }}</span>
    <button @click="add(content, 'item')">Add Item</button>
    <button @click="add(content, 'group')">Add Group</button>
    <button @click="$emit('remove', content)">Delete Group</button>
    <component :is="componentType(item)" v-for="item in content.list" :content.sync="item" @remove="remove">
  </div>`,
})
const app = new Vue({
	el: '#app',
  store,
  computed: {
 ...