前言
最近因为某个需求,简单做了一个小拖拽选择列表 demo,这篇文章记录如何从零到一完成。
展示
技术
Vue + vex + vuedraggable + element-ui
Demo
目录
布局
布局采用 element-ui 的 Container 布局
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>
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>
DataList 组件
Data/Layout/Components/DataList.vue
封装 DdtaList 组件,也就是每一分类的数据列表。使用了 draggable 进行拖拽和 el-tag 数据标签样式。
<template>
<div class="data-list-container">
<div class="col-title data-title-position">{{ title[colType] }}(可选)</div>
<draggable :group="group" :list="list" @start="dragging = true" @end="dragging = false">
<el-tag v-if="list.length === 0" type="info">暂无数据</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: ['属性列', '度量列'],
list: [],
attrList: [
{ value: '商家姓名', id: 0 },
{ value: '地址', id: 1 },
{ value: '公告', id: 2 }
],
numList: [
{ value: '起送费', id: 0 },
{ value: '配送费', id: 1 },
{ value: '抽成比例', 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>
Header
Data/Layout/Header.vue
用于拖拽选择列表数据
<template>
<el-card class="box-card card-container" shadow="never">
<div class="card-item-container">
<span class="col-title">属性列</span>
<el-divider direction="vertical" />
<HeaderTag :col-type="'attrCol'" :group="'attrCol'" />
</div>
<el-divider />
<div class="card-item-container">
<span class="col-title">度量列</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>
HeaderTag 组件
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">暂无数据</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>
通过 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
}
MAIN
Data/Layout/Main.vue
展示已拖拽选择的列表数据
<template>
<div class="pane-container">
<el-card class="box-card" shadow="hover">
<div slot="header" class="clearfix">
<span>已选属性列</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>已选度量列</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>
获取 vuex 数据
src/store/getters.js
const getters = {
attrCol: state => state.data.attrCol,
numCol: state => state.data.numCol
}
export default getters
坑
- 实现不同组件中的拖拽
draggable中需要定义相同的group属性值。
- aside 列表拖拽不上 header 中
- 因为 element-ui 中
el-header设置了默认值 60 px,当 header 上的 draggable 拖拽区域超过了 60 px,则出现 bug。所以需要将最外层容器的大小铺满整个 draggable 区域。
后言
最后完成了开头的效果
一个简单的拖拽小 demo,为数据可视化的 BI 工具做小小的铺垫。觉得有些许用处就点赞哦~