preface

Recently, I made a simple demo of a small drag and drop selection list for a requirement. This article documents how to do it from zero to one.

show

technology

Vue + vex + vuedraggable + element-ui

Demo

directory

layout

The layout uses the Element-UI Container layout

Data/index.vue

<template> <div class="app-wrapper"> <el-container> <el-aside class="aside-position"><Aside /></el-aside> <el-container>  <el-header class="header-position" height="155px"> <Header /> </el-header> <el-main> <Main /> </el-main> </el-container> </el-container> </div> </template> <script> import Aside from '@/views/Data/Layout/Aside' import Main from '@/views/Data/Layout/Main' import Header from '@/views/Data/Layout/Header' export default { name: 'Index', components: { Header, Aside, Main } } </script> <style lang="scss" scoped> .app-wrapper { position: relative; height: 100%; width: 100%; .aside-position { width: 28% ! important; } .header-position { width: 100%; height: 100%; margin: 10px 0; } } </style>Copy the code

Aside

data/Layout/Aside.vue

<template>
  <div class="aside-main">
    <div class="data-container">
      <el-divider />
      <DataList :col-type="0" :group="'attrCol'" />
      <el-divider />
      <DataList :col-type="1" :group="'numCol'" />
      <el-divider />
    </div>
  </div>
</template>

<script>
  import DataList from '@/views/Data/Layout/Components/DataList'
  export default {
    name: 'Aside',
    components: {
      DataList
    }
  }
</script>

<style lang="scss" scoped>
.aside-main {
  margin: 3px;
  padding: 0 5%;
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  border-radius: 4px;
  background-color: #fafbfb;
  .data-container {
    margin-top: 20px;
    width: 100%;
  }
}
</style>

Copy the code

DataList components

Data/Layout/Components/DataList.vue

Encapsulate the DdtaList component, which is a list of data for each category. Draggable is used for drag and el-tag data label styles.

<template> <div class="data-list-container"> <div class="col-title data-title-position">{{title[colType]}} (optional) </div> <draggable :group="group" :list="list" @start="dragging = true" @end="dragging = false"> <el-tag v-if="list.length === 0" type="info"> No data </el-tag> <span V-for ="{value, id} in list" V-else :key="id" class="item"> <el-tag :type="colType? 'primary' : 'success'">{{ value }}</el-tag> </span> </draggable> </div> </template> <script> import draggable from 'vuedraggable' export default { name: 'HeaderSlot', components: { draggable }, props: { colType: { type: Number, default: 0 }, group: {type: String, required: true, default: "}}, data() {return {title: [' attribute column ', 'metric column '], list: [], attrList: Value: [{' business name, id: 0}, {value: 'address, id: 1}, {value:' announcement, id: 2}], numList: [{value: 'the sending fee, id: 0}, {value: 'shipping fee ', ID: 1}, {value:' proportion ', ID: 2}], dragging: false } }, created() { this.list = this.colType === 1 ? this.numList : this.attrList }, methods: { } } </script> <style scoped lang="scss"> .data-list-container { width: 100%; height: 100%; .data-title-position { margin-bottom: 15px; } .item { display: inline-block; margin-right: 5px; margin-bottom: 10px; } } </style>Copy the code

Header

Data/Layout/Header.vue

Use to drag and drop select list data

<template> <el-card class="box-card card-container" shadow="never"> <div class="card-item-container"> <span Class ="col-title"> Attribute column </span> < el-Divider direction="vertical" /> <HeaderTag :col-type="'attrCol'" :group="'attrCol'" /> </div> < el-Divider /> <div class="card-item-container"> <span class="col-title"> Measure column </span> < el-Divider direction="vertical" /> <HeaderTag :col-type="'numCol'" :group="'numCol'" /> </div> </el-card> </template> <script> import HeaderTag from './Components/HeaderTag' export default { name: 'Header', components: { HeaderTag } } </script> <style scoped> </style>Copy the code

HeaderTag components

Data/Layout/Components/HeaderTag.vue

<template> <span class="header-tag-container"> <draggable :group="group" :list="list" @start="dragging = true" @end="dragging = false"> <el-tag V-if ="list.length === 0" type="info"> No data </el-tag> <span v-for="{value, id} in list" v-else :key="id" class="header-item"> <el-tag :type="colType === 'attrCol' ? 'success' : 'primary'">{{ value }}</el-tag> </span> </draggable> </span> </template> <script> import draggable from 'vuedraggable' import { mapGetters } from 'vuex' export default { name: 'HeaderSlot', components: { draggable }, props: { colType: { type: String, default: '' }, group: { type: String, required: true, default: '' } }, data() { return { list: [], dragging: false } }, computed: { ... mapGetters([ 'attrCol', 'numCol' ]) }, watch: { list: function(data) { this.$store.dispatch('data/updateColumns', { data, type: this.colType }) } } } </script> <style scoped lang="scss"> .header-tag-container { display: inline-block; height: 100%; width: 80%; .header-item { display: inline-block; margin-right: 10px; } } </style>Copy the code

Temporary storage of data via Veux

src/store/modules/data.js

const state = {
  attrCol: [].numCol: []}const mutations = {
  SET_COLUMNS: (state, { data, type }) = > {
    console.log(type)
    state[type] = data
  }
}

const actions = {
  updateColumns({ commit }, { data, type }) {
    commit('SET_COLUMNS', { data, type })
  }
}

export default {
  namespaced: true,
  state,
  mutations,
  actions
}
Copy the code

MAIN

Data/Layout/Main.vue

Shows the list data that has been dragged and selected

<template> <div class="pane-container"> <el-card class="box-card" shadow="hover"> <div slot="header" class="clearfix"> <span> Selected attribute column </span> </div> <span v-show="! Attrcol. length" class="no-data"> </span> <span v-for="{value, id} in attrCol" :key="id" class="get-data"> {{ value }} <el-divider direction="vertical" /> </span> </el-card> <el-divider><i class="el-icon-lollipop" /></el-divider> <el-card class="box-card" shadow="hover"> <div slot="header" Class ="clearfix"> <span> Selected metric column </span> </div> <span v-show="! Numcol. length" class="no-data"> <span > <span v-for="{value, id} in numCol" :key="id" class="get-data"> {{ value }} <el-divider direction="vertical" /> </span> </el-card> </div> </template> <script> import { mapGetters } from 'vuex' export default { name: 'Main', components: { }, data() { return { } }, computed: { ... mapGetters([ 'attrCol', 'numCol' ]) } } </script> <style lang="scss" scoped> .no-data { color: darkgray; } .get-data { font-size: 16px; color: cadetblue; font-weight: 600; } </style>Copy the code

Obtain VUEX data

src/store/getters.js

const getters = {
  attrCol: state= > state.data.attrCol,
  numCol: state= > state.data.numCol
}
export default getters

Copy the code

pit

  1. Implement drag and drop in different components

The same group attribute value needs to be defined in draggable.

  1. The aside list cannot be dragged into the header
  • Because the el-header in Element-UI has a default value of 60 px, when the draggable area on the header goes beyond 60 px, a bug occurs. So you need to spread the size of the outermost container across the entire Draggable area.

After the speech

Finally, the beginning of the effect is completed

A simple drag and drop small demo, for data visualization of BI tools to do a little foreshadowing. Give a thumbs up if you think it helps