可拖拽排序列表

作者:陈佳鑫
时间:2022-10-17

VUE Element UI Table 组件不支持拖拽排序,特编写完成开发需求,此组件已打包为 UI 组件上传至 NPM

序号
奖品名
概率(总和100%)
总数量
剩余数量
抽取数量
操作
1
奖品一
10%
100
90
10
2
奖品二
未设置
100
80
20
3
奖品三
10%
100
70
30
4
奖品四
10%
100
60
40
5
奖品五
10%
100
50
50
6
奖品六
10%
100
40
60

template 代码块

<template>
  <div>
    <div class="title">
      <div
        v-for="item in columns"
        :key="item.prop"
        class="block"
        :style="{ width: `${100 / columns.length}%` }"
      >
        {{ item.title }}
      </div>
    </div>
    <transition-group name="drag" class="list" tag="div">
      <div
        v-for="(item, index) in data"
        :key="item[rowKey]"
        :draggable="draggable"
        class="list-item"
        @dragenter="dragenter($event, index)"
        @dragover="dragover($event, index)"
        @dragstart="dragstart(index)"
      >
        <div
          v-for="columnsItem in columns"
          :key="columnsItem.prop"
          class="column"
          :style="{ width: `${100 / columns.length}%` }"
        >
          <component v-if="columnsItem.render" :is="columnsItem.render(item)" />
          <slot v-else :row="item" :name="columnsItem.prop" :index="index">
            {{
              columnsItem.formatter
                ? columnsItem.formatter(item)
                : item[columnsItem.prop]
            }}
          </slot>
        </div>
      </div>
    </transition-group>
  </div>
</template>

DragTable 属性

属性类型说明
draggableBoolean是否可拖动排序
dataArray<any>列表数据
rowKeyString每一行 key 值
columnsArray列表渲染数据

Columns 属性

columns() {
  return [
    { title: '序号', prop: 'lotteryActivityAwardId' },
    { title: '奖品名', prop: 'awardName' },
    { title: '概率(总和100%)', prop: 'winRate', formatter: row => row.winRate != null ? row.winRate / 100 + '%' : '未设置' },
    { title: '总数量', prop: 'totalNum', formatter: row => row.addTotalNum ? row.totalNum + ' + ' + row.addTotalNum + '(增补)' : row.totalNum },
    { title: '剩余数量', prop: 'num', formatter: (row) => (row.totalNum || 0) - (row.usedNum || 0) },
    { title: '抽取数量', prop: 'usedNum' },
    { title: '操作', prop: 'action' }
  ]
}
属性类型说明
titlestring列表标题
propstring当前列渲染数据 key
formatter(row) => stringrow: 当前行数据, 对渲染数据进行格式处理
render(row) => VNoderow: 当前行数据, 对当前列进行 render 自定义

完整代码

<template>
  <div>
    <div class="title">
      <div
        v-for="item in columns"
        :key="item.prop"
        class="block"
        :style="{ width: `${100 / columns.length}%` }"
      >
        {{ item.title }}
      </div>
    </div>
    <transition-group name="drag" class="list" tag="div">
      <div
        v-for="(item, index) in data"
        :key="item[rowKey]"
        :draggable="draggable"
        class="list-item"
        @dragenter="dragenter($event, index)"
        @dragover="dragover($event, index)"
        @dragstart="dragstart(index)"
      >
        <div
          v-for="columnsItem in columns"
          :key="columnsItem.prop"
          class="column"
          :style="{ width: `${100 / columns.length}%` }"
        >
          <component v-if="columnsItem.render" :is="columnsItem.render(item)" />
          <slot v-else :row="item" :name="columnsItem.prop" :index="index">
            {{
              columnsItem.formatter
                ? columnsItem.formatter(item)
                : item[columnsItem.prop]
            }}
          </slot>
        </div>
      </div>
    </transition-group>
  </div>
</template>

<script setup lang="ts" name="DragTable">
import { ref } from 'vue'
import { Columns } from './'

const emit = defineEmits(['onDrag'])

const props = withDefaults(
  defineProps<{
    draggable: boolean
    data: Array<any>
    rowKey: string
    columns: Array<Columns>
  }>(),
  {
    draggable: true
  }
)

const dragIndex = ref(0)

const dragstart = (index: number) => {
  dragIndex.value = index
}

const dragenter = (e: DragEvent, index: number) => {
  e.preventDefault()
  if (dragIndex.value !== index) {
    const moving = props.data[dragIndex.value]
    const newData = props.data.concat([])
    newData.splice(dragIndex.value, 1)
    newData.splice(index, 0, moving)
    dragIndex.value = index
    emit('onDrag', newData)
  }
}

const dragover = (e: DragEvent, index: number) => {
  e.preventDefault()
}
</script>

<style lang="scss" src="./index.scss" scoped />

插槽操作

可通过插槽 slot 对列进行改变,name 为当前列 prop

DragTable 组件

<DragTable
  v-if="awardsData.length > 0"
  :columns="columns"
  :data-key="'awardInfo'"
  :data="awardsData"
  @onDrag="onDrag"
>
  <template #action="{ row, index }">
    <el-button type="text" @click="handleOpenAward(row, index)">编辑</el-button>
    <el-button v-if="!isStarted" type="text" @click="handleRemoveAward(index)">删除</el-button>
  </template>
</DragTable>
属性类型说明
onDragfunction(arr)拖拽完成后返回新数组

组件安装与引用

当前版本为 vue3 版本不支持 vue2 是用此组件库

npm i cjx-zdy-ui

引用

import { DragTable } from 'cjx-zdy-ui'

使用方式

<template>
  <DragTable :columns="columns" :rowKey="'lotteryActivityAwardId'" :data="awardsData" @onDrag="onDrag">
    <template #action="{ row }">
      <el-button type="primary" link>编辑{{ row.lotteryActivityAwardId }}</el-button>
      <el-button type="danger" link>删除</el-button>
    </template>
  </DragTable>
</template>
<script setup lang="ts" name="DragTableHome">
import { computed, ref } from 'vue'
import { DragTable } from 'cjx-zdy-ui'

const columns = computed(() => [
  { title: '序号', prop: 'lotteryActivityAwardId' },
  { title: '奖品名', prop: 'awardName' },
  { title: '概率(总和100%)', prop: 'winRate', formatter: row => row.winRate != null ? row.winRate / 100 + '%' : '未设置' },
  { title: '总数量', prop: 'totalNum', formatter: row => row.addTotalNum ? row.totalNum + ' + ' + row.addTotalNum + '(增补)' : row.totalNum },
  { title: '剩余数量', prop: 'num', formatter: (row) => (row.totalNum || 0) - (row.usedNum || 0) },
  { title: '抽取数量', prop: 'usedNum' },
  { title: '操作', prop: 'action' }
])

const awardsData = ref([
  { lotteryActivityAwardId: 1, awardName: '奖品一', winRate: 1000, totalNum: 100, num: 20, usedNum: 10 },
  { lotteryActivityAwardId: 2, awardName: '奖品二', winRate: null, totalNum: 100, num: 20, usedNum: 20 },
  { lotteryActivityAwardId: 3, awardName: '奖品三', winRate: 1000, totalNum: 100, num: 20, usedNum: 30 },
  { lotteryActivityAwardId: 4, awardName: '奖品四', winRate: 1000, totalNum: 100, num: 20, usedNum: 40 },
  { lotteryActivityAwardId: 5, awardName: '奖品五', winRate: 1000, totalNum: 100, num: 20, usedNum: 50 },
  { lotteryActivityAwardId: 6, awardName: '奖品六', winRate: 1000, totalNum: 100, num: 20, usedNum: 60 }
])

const onDrag = (arr: Array<any>) => {
  awardsData.value = arr.map((item, index) => {
    return { ...item, sortNum: index + 1 }
  })
}
</script>
Last Updated:
Contributors: 1102166242@qq.com, cjx