效果图
代码
<template>
<div class="container">
<!--左侧-->
<div>
<div class="title">{{ titles[0] }}</div>
<div class="layerContainer">
<div v-for="item in leftLayerArray"
:key="getKey(item)"
:ref="getRefKey(item)"
@click="e =>layerClicked(e,item,true)"
>
<el-tooltip effect="light" :content="item.key" placement="top" v-if="item.key.length > 10">
<div>{{ item.key }}</div>
</el-tooltip>
<div v-else>{{ item.key }}</div>
</div>
</div>
</div>
<!--中间按钮-->
<div class="centerButton">
<div @click="transferToRight">></div>
<div @click="transferAllToRight">>></div>
<div @click="transferToLeft"><</div>
<div @click="transferAllToLeft"><<</div>
</div>
<!--右侧-->
<div>
<div class="title">{{ titles[1] }}</div>
<div class="layerContainer">
<div v-for="item in rightLayerArray"
:key="getKetRight(item)"
:ref="getRefKeyRight(item)"
@click="e =>layerClicked(e,item, false)"
>
<el-tooltip effect="light" :content="item.key" placement="top" v-if="item.key.length > 10">
<div>{{ item.key }}</div>
</el-tooltip>
<div v-else>{{ item.key }}</div>
</div>
</div>
</div>
<!-- 上下移动的按钮 -->
<div class="transfer-right-buttons">
<div :class="upClass" @click="moveMoreStep('up')">
<img src="图片地址"
alt="">
</div>
<div :class="upClass" @click="moveOneStep('up')">
<img src="图片地址"
alt="">
</div>
<div :class="downClass" @click="moveOneStep('down')">
<img src="图片地址"
alt="">
</div>
<div :class="downClass" @click="moveMoreStep('down')">
<img src="图片地址"
alt="">
</div>
</div>
</div>
</template>
<script>
export default {
name: "ha-transfer",
props: {
titles: {
type: Array,
default: () => ['地图图层', '图例项']
},
originData: {
type: Array,
default: () => [
{
key: '点图层',
id: 1
},
{
key: '线图层',
id: 2
},
{
key: '面图层',
id: 3
},
{
key: '多点图层',
id: 4
},
{
key: '多线图层',
id: 5
},
{
key: '多面图层',
id: 6
}
]
},
selectedData: {
type: Array,
default: () => [
{
key: '图例项1',
id: 7
},
{
key: '图例项2',
id: 8
},
{
key: '图例项3',
id: 9
},
{
key: '图例项4',
id: 10
},
{
key: '图例项5',
id: 11
},
{
key: '图例项6',
id: 12
}
]
}
},
data() {
return {
leftLayerArray: [...this.originData],
leftCurrentSelectedLayer: [],
rightLayerArray: [...this.selectedData],
rightCurrentSelectedLayer: []
}
},
computed: {
upClass() {
if (this.rightCurrentSelectedLayer.length === 0) {
return 'disabled'
}
for (let item of this.rightCurrentSelectedLayer) {
if (item.id === this.rightLayerArray[0].id) {
return 'disabled'
}
}
return ''
},
downClass() {
if (this.rightCurrentSelectedLayer.length === 0) {
return 'disabled'
}
for (let item of this.rightCurrentSelectedLayer) {
if (item.id === this.rightLayerArray[this.rightLayerArray.length - 1].id) {
return 'disabled'
}
}
return ''
},
},
methods: {
getRefKey(item) {
return `layer-${item.id}`
},
getKey(item) {
return `layer-${item.id}`
},
getRefKeyRight(item) {
return `layer-right-${item.id}`
},
getKetRight(item) {
return `layer-right-${item.id}`
},
/**
* 单击穿梭框列表项,选中或取消选中
* @param e 事件对象
* @param item 当前项
* @param isLeft 是否是左侧
*/
layerClicked(e, item, isLeft) {
let currentLayer, layerArray, refFunction
if (isLeft) {
currentLayer = [...this.leftCurrentSelectedLayer]
layerArray = [...this.leftLayerArray]
refFunction = this.getRefKey
} else {
currentLayer = [...this.rightCurrentSelectedLayer]
layerArray = [...this.rightLayerArray]
refFunction = this.getRefKeyRight
}
const refElement = this.$refs[refFunction(item)][0];
if (e.ctrlKey || e.metaKey) {
const isSelected = currentLayer.includes(item);
if (isSelected) {
refElement.classList.remove('active');
currentLayer.splice(currentLayer.indexOf(item), 1);
} else {
refElement.classList.add('active');
currentLayer.push(item);
}
} else if (e.shiftKey) {
const firstIndex = layerArray.indexOf(currentLayer[0]);
const lastIndex = layerArray.indexOf(item);
const [startIndex, endIndex] = [firstIndex, lastIndex].sort();
currentLayer = layerArray.slice(startIndex, endIndex + 1);
layerArray.forEach((item, index) => {
const refElement = this.$refs[refFunction(item)][0];
if (index >= startIndex && index <= endIndex) {
refElement.classList.add('active');
} else {
refElement.classList.remove('active');
}
})
} else {
currentLayer = [item];
layerArray.forEach(item => {
this.$refs[refFunction(item)][0].classList.remove('active');
})
refElement.classList.add('active');
}
if (isLeft) {
this.leftCurrentSelectedLayer = [...currentLayer];
this.leftLayerArray = [...layerArray];
} else {
this.rightCurrentSelectedLayer = [...currentLayer];
this.rightLayerArray = [...layerArray];
}
},
/**
* 把选中的图层移动到右侧
*/
transferToRight() {
this.rightLayerArray.push(...this.leftCurrentSelectedLayer)
this.leftLayerArray = this.leftLayerArray.filter(item => {
return this.leftCurrentSelectedLayer.indexOf(item) === -1
})
this.leftCurrentSelectedLayer = []
},
/**
* 把所有的图层移动到右侧
*/
transferAllToRight() {
this.rightLayerArray.push(...this.leftLayerArray)
this.leftCurrentSelectedLayer = []
this.leftLayerArray = []
},
/**
* 把选中的图层移动到左侧
*/
transferToLeft() {
this.leftLayerArray.push(...this.rightCurrentSelectedLayer)
this.rightLayerArray = this.rightLayerArray.filter(item => {
return this.rightCurrentSelectedLayer.indexOf(item) === -1
})
this.rightCurrentSelectedLayer = []
},
/**
* 把所有的图层移动到左侧
*/
transferAllToLeft() {
this.leftLayerArray.push(...this.rightLayerArray)
this.rightCurrentSelectedLayer = []
this.rightLayerArray = []
},
/**
* 向上或向下移动一步
* @param status
*/
moveOneStep(status) {
if (status === 'up' && this.upClass === 'disabled') return
if (status === 'down' && this.downClass === 'disabled') return
let temp = []
for (let item of this.rightLayerArray) {
if (this.rightCurrentSelectedLayer.indexOf(item) === -1) {
temp.push(item)
}
}
this.rightCurrentSelectedLayer.sort((a, b) => {
return this.rightLayerArray.indexOf(a) - this.rightLayerArray.indexOf(b)
})
let index = this.rightLayerArray.indexOf(this.rightCurrentSelectedLayer[0])
status === 'up' ? index-- : index++
this.rightLayerArray = [...temp.slice(0, index), ...this.rightCurrentSelectedLayer, ...temp.slice(index)]
},
/**
* 向上或向下移动多步到顶或者到底
* @param status
*/
moveMoreStep(status) {
if (status === 'up' && this.upClass === 'disabled') return
if (status === 'down' && this.downClass === 'disabled') return
let temp = []
for (let item of this.rightLayerArray) {
if (this.rightCurrentSelectedLayer.indexOf(item) === -1) {
temp.push(item)
}
}
this.rightLayerArray = status === 'up' ?
[...this.rightCurrentSelectedLayer, ...temp] :
[...temp, ...this.rightCurrentSelectedLayer]
},
}
}
</script>
<style scoped lang="less">
.disabled() {
cursor: not-allowed !important;
background-color: #999 !important;
border: #333 solid 1px !important;
}
.hover() {
background-color: #eee;
border: #409eff solid 1px;
cursor: default;
}
.buttonContainer() {
height: 200px;
display: flex;
flex-direction: column;
justify-content: space-between;
align-items: center;
}
.active() {
background-color: #409eff;
color: #fff;
}
.container {
display: flex;
justify-content: space-between;
align-items: center;
user-select: none;
.layerContainer {
width: 200px;
height: 250px;
border: #999 solid 1px;
box-sizing: border-box;
padding: 10px;
white-space: nowrap;
overflow-x: hidden;
overflow-y: auto;
div {
&:hover {
cursor: default;
}
}
.active {
.active()
}
}
.centerButton {
.buttonContainer();
div {
width: 30px;
height: 30px;
border: #666 solid 1px;
background-color: #ddd;
text-align: center;
line-height: 30px;
&:hover {
.hover()
}
}
}
.transfer-right-buttons {
.buttonContainer();
div {
width: 30px;
height: 30px;
border: #666 solid 1px;
background-color: #ddd;
img {
width: 100%;
height: 100%;
}
&:hover {
.hover()
}
}
.disabled {
.disabled()
}
}
}
</style>