近日有表格拖拽排序的需求,公司项目基于Vue2和Ant Design实现,看了Ant Design官网,发现表格拖拽排序功能属于收费版,并且基于Vue3实现的。泪目,只能自己封装一个简单版拖拽排序表格。
下载依赖
安装vuedraggable 为拖拽功能提供支持
npm i -S vuedraggable
实现原则
使用方式与Ant Design表格保持一致,包括属性名、样式、插槽使用等。
实现思路
布局:基于flex的flex-grow结合传入的columns参数实现动态列宽。列高由表格内容自动撑开。首列自动添加序号。
数据:遍历取出【行数据】,再遍历【表头】将行数据布局。
插槽:每个表格项与Ant Design一样可用插槽方式自定义,会向父组件暴露【行数据】。
效果图
编辑切换为居中
添加图片注释,不超过 140 字(可选)
代码实现
HTML
<template>
<div style="width: 100%;">
<!-- 【表头部分】 -->
<div class="columns-bar">
<!-- 序号 -->
<div v-if="hasIndex" :style="{'--width': defaultWidth, '--flex-grow': noEnlarge}">序号</div>
<!-- 表头 -->
<div
v-for="(item, index) in columns"
:key="index"
:style="{'--width': (item.width || defaultWidth)+'px', '--flex-grow': item.width?noEnlarge:enlarge}"
>
<span>{{ item.title }}</span>
</div>
</div>
<!-- 【表格部分】 -->
<DraggableList
v-if="list.length"
v-model="list"
v-bind="options"
:style="{'max-height': maxHeight+'px', 'overflow-y': 'auto' }"
>
<transition-group>
<div v-for="(element, elementIndex) in list" :key="element.id" class="row-bar">
<!-- 序号 -->
<div v-if="hasIndex" :style="{'--width': defaultWidth, '--flex-grow': noEnlarge}">{{ elementIndex + 1 }}</div>
<!-- 表格数据 -->
<div
v-for="(columnsObj, columnsIndex) in columns"
:key="columnsIndex"
:style="{'--width': (columnsObj.width || defaultWidth)+'px', '--flex-grow': columnsObj.width?noEnlarge:enlarge}"
>
<span v-if="!columnsObj.scopedSlots">{{ element[columnsObj.dataIndex] }}</span>
<slot v-else :name="columnsObj.scopedSlots.customRender" v-bind="element"></slot>
</div>
</div>
</transition-group>
</DraggableList>
<!-- 【暂无数据 -->
<div v-else class="empty">
<span>暂无数据</span>
</div>
</div>
</template>
JavaScript
<script>
import draggable from 'vuedraggable'
export default {
name: 'DraggableTable',
components: { DraggableList: draggable },
data() {
return {
list: [],
options: {
animation: 200
},
defaultWidth: 50,
enlarge: 1,
noEnlarge: 0
}
},
props: {
columns: {
type: Array,
default: () => []
},
dataSource: {
type: Array,
default: () => []
},
maxHeight: {
type: Number,
default: 450
},
hasIndex: {
type: Boolean,
default: true
}
},
watch: {
dataSource(newVal) {
this.list = [...newVal]
}
},
mounted() {
this.list = [...this.dataSource]
}
}
</script>
CSS
<style lang="less" scoped>
.columns-bar {
display: flex;
height: 51px;
line-height: 51px;
background: rgba(248, 248, 248, 0.81);
border-radius: 5px;
& > div {
padding: 0 10px;
width: var(--width);
flex-grow: var(--flex-grow);
}
}
.row-bar {
display: flex;
min-height: 51px;
border-radius: 5px;
cursor: move;
& > div {
padding: 15px 15px;
width: var(--width);
flex-grow: var(--flex-grow);
}
&:active {
background-color: #c5d9db;
}
&:hover {
background-color: #c5d9db4b;
}
}
.empty {
height: 168px;
display: flex;
justify-content: center;
align-items: center;
color: rgba(0, 0, 0, 0.25);
}
</style>
使用方式
<DraggableTable :maxHeight="450" :columns="columns" :dataSource="selectedContents">
<!-- 【插槽使用方法1】 -->
<span slot="action" slot-scope="record">
<a @click="handleDelete(record.id)">删除</a>
</span>
<!-- 【插槽使用方法2】 -->
<template v-slot:active="record" >
<a-switch
:disabled="!record.id"
:checked="record.active"
/>
</template>
</DraggableTable>
至此就实现了表格的拖拽排序