vue3 + element plus+ts 实现穿梭框右侧数据上下移动功能

技术栈

最近在迁移老的vue2项目至vue3,并且同时使用ts,有段时间没更新了。
用啥都一样,只要你会学,用啥啥都行。
不会学也是,用啥都一样,用啥啥不行。

需求

实现element plus中的穿梭框(即el-transfer)右侧数据上下移动功能。
我知道网上有很多类似的例子,但是vue3+ts+element plus就我这一个,能复制总比你自己写要快,对吧。
字看不懂的话看图:
在这里插入图片描述

懂了吧。

实现思路

(给中级开发看的,中级开发看完这段应该就会了)

  1. 打开el-transfer标签内的target-order="push"属性,这是基本,默认是顺序是original,你怎么操作数据顺序都不会变的;
  2. el-transfer的v-model属性绑定的值就是右侧部分的列表(不要管官方KPI文档怎么写,它就是右侧部分的数据数组),换言之,你改变了v-model绑定的数组中元素的顺序,右侧部分内容的顺序就变了,简单吧。
  3. right-check-change方法中,你可以获取到右侧部分选中的项的key值组成的数组;
  4. 比较坑的一点是,你拿到的只是key值(不是索引),你需要操作一下,根据key值取到相应的索引;
  5. 最后的小技巧,也是逻辑核心的一句话,上移就是当前元素和前一个元素调换位置,下移就是当前元素和下一个元素调换位置;
  6. 处理下边界值,即在最上面不能上移和在最下面不能下移的情况,搞定。

简单吧,估计大学里前几节编程课就教会你数组怎么互换两个元素了。

示例代码

(给初级小白看的,是在不会你就把这一大段复制进去,我假数据都帮你造好了,先让页面效果出来,慢慢研究)

// An highlighted block
<template>
    <div style="text-align: center">
        <el-transfer v-model="rightValue" style="text-align: left; display: inline-block" filterable target-order="push"
            @right-check-change="rightCheckChange" :render-content="renderFunc" :titles="['可选列', '可见列']"
            :button-texts="['To left', 'To right']" :format="{
                noChecked: '${total}',
                hasChecked: '${checked}/${total}',
            }" :data="data" @change="handleChange">
            <!-- <template #left-footer>
                <el-button class="transfer-footer" size="small">Operation</el-button>
            </template> -->
            <template #right-footer>
                <el-button class="transfer-footer" size="small" @click="moveUp"><el-icon>
                        <CaretTop />
                    </el-icon></el-button>
                <el-button class="transfer-footer" size="small" @click="moveDown"><el-icon>
                        <CaretBottom />
                    </el-icon></el-button>
            </template>
        </el-transfer>
    </div>
</template>
  
<script lang="ts" setup>
import { ref } from 'vue'
import type { VNode, VNodeProps } from 'vue'

interface Option {
    key: number
    label: string
    disabled: boolean
}

const generateData = (): Option[] => {
    return [
        {
            key: 1,
            label: 'ID',
            disabled: false,
        },
        {
            key: 2,
            label: '标题',
            disabled: false,
        },
        {
            key: 3,
            label: '负责人',
            disabled: false,
        },
        {
            key: 4,
            label: '状态',
            disabled: false,
        },
        {
            key: 5,
            label: '创建时间',
            disabled: false,
        },
        {
            key: 6,
            label: '更新时间',
            disabled: false,
        },
        {
            key: 7,
            label: '创建人',
            disabled: false,
        },
    ]
}

const data = ref(generateData())
const rightValue = ref<number[]>([])

const renderFunc = (
    h: (type: string, props: VNodeProps | null, children?: string) => VNode,
    option: Option
) => {
    //自定义渲染函数,可在这里处理每一项的显示内容
    return h('span', null, option.label)
}
const handleChange = (
    value: number | string,
    direction: 'left' | 'right',
    movedKeys: string[] | number[]
) => {
    console.log(value, direction, movedKeys)
    console.log('rightValue', rightValue)
}

const selectRightValue = ref<number[]>([]) //右侧被选中的元素的key值组成的数组
const rightCheckChange = (arr: Array<number>) => {
	//右侧选中元素触发的时间
    console.log('右侧选中', arr)
    selectRightValue.value = arr
}

const moveUp = () => {
    console.log('选中', selectRightValue.value[0])
    if (selectRightValue.value?.length === 1) {
        let selectKey = rightValue.value.indexOf(selectRightValue.value[0]) //右侧数组被选中的元素的索引index
        console.log(selectKey)
        if (selectKey === 0) {
            window.alert('已经在最顶部,不能再上移')
        } else {
            let temp = rightValue.value[selectKey - 1];
            rightValue.value.splice(selectKey - 1, 1, rightValue.value[selectKey])
            rightValue.value.splice(selectKey, 1, temp)
            console.log('123', rightValue.value)
        }
    } else {
        window.alert('只能且必须选中一项要移动的列')
    }
}
const moveDown = () => {
    if (selectRightValue.value?.length === 1) {
        let selectKey = rightValue.value.indexOf(selectRightValue.value[0]) //右侧数组被选中的元素的索引index
        console.log(selectKey)
        if (
            selectKey + 1 === rightValue.value.length
        ) {
            window.alert('已经在最底部,不能再下移')
        } else {
            let temp = rightValue.value[selectKey + 1];
            rightValue.value.splice(selectKey + 1, 1, rightValue.value[selectKey])
            rightValue.value.splice(selectKey, 1, temp)
            console.log('123', rightValue.value)
        }
    } else {
        window.alert('只能且必须选中一项要移动的列')
    }
}
</script>
  
<style>
.transfer-footer {
    margin-left: 15px;
    padding: 6px 5px;
}
</style>

别觉得简单,我知道很简单,两句至理名言奉上:
虚怀若谷不代表你没有本事。
眼高手低最后出丑的是自己。

GitHub 加速计划 / eleme / element
10
1
下载
A Vue.js 2.0 UI Toolkit for Web
最近提交(Master分支:4 个月前 )
c345bb45 8 个月前
a07f3a59 * Update transition.md * Update table.md * Update transition.md * Update table.md * Update transition.md * Update table.md * Update table.md * Update transition.md * Update popover.md 8 个月前
Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐