Vue3封装一个穿梭框

发布于:2022-12-25 ⋅ 阅读:(320) ⋅ 点赞:(0)

实现的效果

外界通过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"
      >
        &lt;
      </button>
      <button
        class="rightButton"
        @click="rightClick"
        :disabled="rightData.length == 0"
      >
        &gt;
      </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 后查看

网站公告

今日签到

点亮在社区的每一天
去签到