vue3+ts表格拖拽
首先npm install vue-draggable-next
<template>
<div style="display: flex" v-if="drag" class="draggable-btn">
<el-form-item label="请输入跳转位置">
<el-input v-model.number="num" @keydown.enter="handleChange(num)" style="width: 200px" maxLength="3"> </el-input>
</el-form-item>
<el-button type="primary" @click="handleChange(num)" class="ml-20">确认</el-button>
</div>
<div class="draggable-container" style="padding-bottom: 20px">
<el-scrollbar max-height="calc(100vh - 280px)">
<table class="drag-table" cellpadding="0" cellspacing="0">
<thead class="drag-table-header">
<tr>
<template v-for="item in column" :key="item.label">
<th>
<div :style="{ width: item.width + 'px' }">
{
{
item.label }}
</div>
</th>
</template>
</tr>
</thead>
<draggable
class="list-group"
tag="tbody"
v-model="ruleForm.tableData"
v-bind="dragOptions"
:move="onMove"
@start="onStart"
@end="onEnd"
v-if="ruleForm.tableData?.length && drag"
>
<transition-group>
<tr v-for="(item, index) in ruleForm.tableData" :key="index">
<template v-for="i in column" :key="i.label">
<td v-if="i.type === 'index'" :style="{ width: i.width + 'px' }">
<div>
<el-input
style="width: 50px"
type="text"
v-model.number="item.sort"
@change="
(val) => {
handleChange(val, index)
}
"
/>
</div>
</td>
<td v-else-if="i.type === 'selection'" :style="{ width: i.width + 'px' }">
<div>
<el-checkbox v-model="item.check" />
</div>
</td>
<td v-else-if="i.type === 'slot'">
<div v-if="!item.whetherSpace || i.isOperation">
<slot :name="i.slot" :row="item" :index="index"> </slot>
</div>
<div v-else></div>
</td>
<td v-else :style="{ width: i.width + 'px' }">
<el-tooltip
class="box-item"
effect="dark"
:content="item[i.prop]"
placement="top"
v-if="i.is_tooltip && item[i.prop]"
>
<div class="ellipsis" :style="{ width: i.width + 'px' }">{
{
item[i.prop] }}</div>
</el-tooltip>
<div v-else>
{
{
item[i.prop] }}
</div>
</td>
</template>
</tr>
</transition-group>
</draggable>
<tbody class="list-group" tag="tbody" v-if="ruleForm.tableData?.length && !drag">
<tr v-for="(item, index) in ruleForm.tableData" :key="index">
<template v-for="i in column" :key="i.label">
<td v-if="i.type === 'index'" :style="{ width: i.width + 'px' }">
{
{
item.sort }}
</td>
<td v-else-if="i.type === 'selection'" :style="{ width: i.width + 'px' }">
<div>
<el-checkbox v-model="item.check" />
</div>
</td>
<td v-else-if="i.type === 'slot'">
<div v-if="!item.whetherSpace || i.isOperation">
<slot :name="i.slot" :row="item" :index="index"> </slot>
</div>
<div v-else class="m-10"></div>
</td>
<td v-else :style="{ width: i.width + 'px' }">
<el-tooltip
class="box-item"
effect="dark"
:content="item[i.prop]"
placement="top"
v-if="i.is_tooltip && item[i.prop]"
>
<div class="ellipsis" :style="{ width: i.width + 'px' }">{
{
item[i.prop] }}</div>
</el-tooltip>
<div v-else>
{
{
item[i.prop] }}
</div>
</td>
</template>
</tr>
</tbody>
<tbody v-if="!ruleForm.tableData?.length">
<tr>
<td align="center" :colspan="column?.length || 0 + 2" class="ptb-10">暂无数据</td>
</tr>
</tbody>
</table>
</el-scrollbar>
</div>
</template>
<script setup lang="ts">
import {
ElMessage } from 'element-plus'
import {
ref, computed, nextTick, inject, PropType } from 'vue'
import {
VueDraggableNext as draggable } from 'vue-draggable-next'
defineProps({
column: {
type: Array as PropType<any[]>,
default: () => []
},
drag: {
type: Boolean,
default: true
}
})
const emit = defineEmits(['change'])
const dragOptions = computed(() => ({
animation: 200,
group: 'description',
disabled: false,
ghostClass: 'ghost'
}))
const num = ref()
const onMove = () => {
}
const ruleForm: any = inject('ruleForm')
const onStart = () => {
}
const onEnd = () => {
ruleForm.value.tableData.forEach((i: any, index: number) => {
i.sort = index + 1
})
emit('change')
}
const handleChange = (val?: any, index?: number) => {
const reg = /^(([1-9])|([1-9]([0-9]+)))$/
if (!reg.test(val)) {
return ElMessage({
type: 'warning',
message: '只能是正整数'
})
}
if (!val && val !== 0) {
if (index) {
ruleForm.value.tableData[index].sort = index + 1
}
return
}
if (val == 0) {
return ElMessage({
type: 'warning',
message: '不能小于1'
})
}
if (index) {
const obj = ruleForm.value.tableData[index]
ruleForm.value.tableData.splice(index, 1)
ruleForm.value.tableData.splice(+val - 1, 0, obj) // 把原来的列数据添加到新的位置,然后再从原位置移除它,触发table的重绘
ruleForm.value.tableData.forEach((i: any, index: number) => {
i.sort = index + 1
i.check = false
})
} else {
const arr = ruleForm.value.tableData.filter((i: any) => i.check) // 排序选中的数据
if (arr.length < 1)
return ElMessage({
type: 'warning',
message: '请勾选排序所需数据'
})
arr.forEach((i: any) => {
ruleForm.value.tableData.forEach((item: any, idx: number) => {
if (item.processCode == i.processCode && item.sort == i.sort) {
ruleForm.value.tableData.splice(idx, 1) // 删除选中的数据
}
})
})
ruleForm.value.tableData.splice(+val - 1, 0, ...arr) // 把原来的列数据添加到新的位置,然后再从原位置移除它,触发table的重绘
ruleForm.value.tableData.forEach((i: any, index: number) => {
i.sort = index + 1
i.check = false
})
}
num.value = null
emit('change')
}
nextTick(() => {
ruleForm.value.tableData.forEach((i: any, index: number) => {
i.sort = index + 1
})
})
</script>
<style scoped lang="scss">
.drag-table-header {
/* display: flex; */
/* justify-content: space-between; */
background: #f5f7fa;
color: #4a4a4a;
}
.drag-table-header th,
.drag-table tbody td {
border-top: 1px #e6e6e6 solid;
// border-left: 1px #ccc solid;
// padding: 5px;
overflow: hidden;
// flex: 1;
text-align: center;
min-width: 50px;
color: #4a4a4a;
}
.drag-table-header th > div {
padding: 5px;
}
.drag-table tbody td > div {
padding: 0px 5px;
}
.drag-table-header th:last-child,
.drag-table tbody td:last-child {
/* flex: 1; */
// border-right: 1px #ccc solid;
}
.drag-table {
border-bottom: 1px #e6e6e6 solid;
/* border-right: 1px #ccc solid; */
width: 100%;
}
.draggable-container {
// width: 100%;
width: calc(100vw - 275px);
}
</style>
column
的格式为
const columns = [
{
type: "selection", width: "50", label: "" },
{
type: "slot", prop: "partCode", label: "部件代码", slot: "partCode" },
{
prop: "partName", label: "部件名称" },
{
prop: "belongPartJoin", label: "所属部件" },
{
type: "slot", prop: "info", label: "基础信息", slot: "info" },
{
prop: "partDesc", label: "部件描述", showOverflowTooltip: true },
{
type: "slot", prop: "date", label: "日期信息", width: "350", slot: "date" },
{
width: "120",
label: "日志",
buttons: [{
name: "日志", class: "btn-plain", command: "record", plain: true }],
},
]