实现的效果
外界通过V-model绑定值,子组件点击确定后将右边勾选的值传出,V-model这里传了自定义参数 myValue。后续会实现拖拽功能。
效果图
父组件
<template>
<div class="container">
<select name="phone" id="" @change="setData">
<option v-for="(item,index) in options" :value="item" :key="index">
{{ item}}
</option>
</select>
<HomeView ref="transferRef" v-model:myValue='transferValue' :phone_name='phone_name' :transferData='data'/>
</div>
{{transferValue}}
</template>
<script setup>
import { ref } from "vue";
import HomeView from "./views/HomeView.vue";
const transferValue = ref(null)
const data = [
{title:"华为",data:[
{id:0,phone_name:'Mate40'},
{id:1,phone_name:'Mate40 Pro'},
{id:2,phone_name:'Mate40 Pro Max'},
{id:3,phone_name:'P30'},
{id:4,phone_name:'P30 Pro'},
{id:5,phone_name:'Mate40 Pro Max'},
{id:6,phone_name:'xx30'},
{id:7,phone_name:'xxx30 Pro'},
{id:8,phone_name:'Matexxx40 Pro Max'},
{id:9,phone_name:'xxxP30'},
]},
{title:"苹果",data:[
{id:0,phone_name:'iPhone 13'},
{id:1,phone_name:'iPhone 13 Pro'},
{id:2,phone_name:'iPhone 13 Pro Max'},
{id:3,phone_name:'iPhone 12'},
{id:4,phone_name:'iPhone 12 Pro'},
]},
{title:"小米",data:[
{id:0,phone_name:'Xiaomi 13'},
{id:1,phone_name:'Xiaomi 13 Pro'},
{id:2,phone_name:'Xiaomi 13 Pro Max'},
{id:3,phone_name:'Xiaomi p30'},
{id:4,phone_name:'Xiaomi 13 P30 Pro'},
]},
]
const options = ref(data.map(v=>v.title))
const transferRef = ref(null)
const phone_name = ref(options.value[0])
const setData = (e)=>{
phone_name.value = e.target.value
transferRef.value.clear()
}
</script>
<style lang="scss">
body{
display: flex;
justify-content: center;
align-items: center;
}
</style>
子组件
<template>
<div class="transfer">
<div class="box left">
<h1>{{ props.phone_name }}</h1>
<div class="content">
<div
class="phone_item"
v-for="item of leftTransferData[0].data"
:key="item.id"
>
<input
:id="'leftCheckBox' + item.id"
type="checkbox"
:key="item.id"
@click="setCheckData(item, $event.target.checked, 'left')"
/>
<label :for="item.id"> {{ item.phone_name }}</label>
</div>
</div>
</div>
<div class="box center">
<button
class="leftButton"
@click="leftClick"
:disabled="rightTransferData.length == 0"
>
<
</button>
<button
class="rightButton"
@click="rightClick"
:disabled="rightData.length == 0"
>
>
</button>
</div>
<div class="box right">
<h1>已经选机型</h1>
<div class="content">
<div
class="phone_item"
v-for="item of rightTransferData"
:key="item.id"
>
<input
type="checkbox"
:key="item.id"
@click="setCheckData(item, $event.target.checked, 'right')"
/>
<label :for="item.id"> {{ item.phone_name }}</label>
</div>
<button class="confirm" @click="confirm(emits)">确认</button>
</div>
</div>
</div>
</template>
<script setup>
import { defineProps, defineEmits } from "vue";
import { propsType } from "./extend/data";
import { useComputedData, useMethods } from "./extend/hooks";
const emits = defineEmits(["update:myValue"]);
const props = defineProps(propsType);
const [leftTransferData, rightData] = useComputedData(props);
const [setCheckData, rightClick, leftClick, rightTransferData, confirm, clear] =
useMethods();
defineExpose({ clear });
</script>
<style lang="scss">
.transfer {
width: 480px;
height: 400px;
border: 1px solid #ddd;
margin: 0 auto;
display: flex;
justify-content: space-between;
.box {
flex: 1;
height: 100%;
}
.left,
.right {
overflow-y: scroll;
position: relative;
.confirm {
position: absolute;
bottom: 0;
left: 0;
right: 0;
outline: none;
border: none;
background: linear-gradient(to right, #ffa751, #ffe259);
}
.content {
.phone_item {
margin: 5px 0;
text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;
}
display: flex;
flex-direction: column;
justify-content: flex-start;
}
border-right: 1px solid #fff;
h1 {
height: 60px;
line-height: 60px;
margin: 0;
font-weight: normal;
font-size: 18px;
text-align: center;
border-bottom: 1px solid #ddd;
background-color: #efefef;
}
}
.center {
border-left: 1px solid #ddd;
border-right: 1px solid #ddd;
flex: 1;
display: flex;
align-items: center;
justify-content: center;
button:nth-child(2) {
margin-left: 10px;
}
button {
outline: none;
border: none;
background-color: #ffa751;
border-radius: 5px;
width: 55px;
height: 55px;
}
}
}
</style>
子组件的hooks
import { computed, reactive, ref ,watch,toRaw} from "vue"
const rightData = ref([])
export const useComputedData = (props) => {
const leftData = computed(()=>props.transferData.filter(v => v.title === props.phone_name))
return [ leftData ,rightData]
}
export const useMethods = () => {
const clear = () => {
resTransferData.value = []
rightTransferData.value = []
let checkBox = document.getElementById(`leftCheckBox0`)
rightData.value = []
let i = 0
while (checkBox) {
if (checkBox.checked) checkBox.checked = false
checkBox = document.getElementById(`leftCheckBox${i}`)
i++
}
}
const rightTransferData = ref([])
const resTransferData = ref([])
const addCheckedData = (rightData,payload,leftOrRight,checked) => {
switch (leftOrRight) {
case 'left':
rightData.value.push(payload)
break;
case 'right':
//右边这里可能执行的是删除
console.log(checked);
checked&&(resTransferData.value.push(payload))
break;
default:
break;
}
}
const removeCheckedData = (rightData,payload,leftOrRight,checked) => {
switch (leftOrRight) {
case 'left':
rightData.value = rightData.value.filter(v => {
console.log(v,payload,'v,payload');
return v.id!==payload.id
})
break;
case 'right':
resTransferData.value = resTransferData.value.filter(v => {
return v.id!==payload.id
})
console.log(resTransferData.value);
break;
default:
break;
}
}
const setCheckData = (payload, checked,leftOrRight) => {
checked?addCheckedData(rightData,payload,leftOrRight,checked):removeCheckedData(rightData,payload,leftOrRight,checked)
}
const rightClick = () => {
const arr = rightData.value.slice()
arr.forEach(element => {
if (!rightTransferData.value.includes(element)) {
rightTransferData.value.push(element)
}
});
rightTransferData.value.length==0 && (rightTransferData.value=arr)
}
const leftClick = () => {
if (resTransferData.value.length>0) {
resTransferData.value.forEach((v,resindex) => {
const index = rightTransferData.value.indexOf(v)
if (index !== -1) {
rightTransferData.value.splice(index, 1)
}
})
resTransferData.value.length = 0
}
}
const confirm = (emits) => {
if (resTransferData.value.length > 0) {
const arr = resTransferData.value.slice()//深拷贝
emits('update:myValue',arr)
} else {
emits('update:myValue',null)
}
}
return [setCheckData,rightClick,leftClick,rightTransferData,confirm,clear]
}
子组件静态数据
export const propsType = {
phone_name: {
type: String,
default: '',
},
transferData: {
type: Array,
default: () => []
},
myValue: {
type: Object,
default: null
}
}
本文含有隐藏内容,请 开通VIP 后查看