vue3.x + elementPlus 封装组件之transfer 穿梭框

老规矩----前面文章的步骤创建文件导入js等

封装代码

<template>
  <!-- target-order="unshift"必须设置,如果不设置的话后台穿的value值得顺序会被data重置 -  -->
  <el-transfer
    :id="idDom"
    target-order="unshift"
    style="text-align: left; display: inline-block"
    :model-value="transferData.value"
    filterable
    :filter-method="transferData.menuFilterMethod"
    :left-default-checked="leftChecked"
    :right-default-checked="rightChecked"
    :render-content="renderFunc"
    :titles="titles"
    :button-texts="buttonTexts"
    :format="{
      noChecked: '${total}',
      hasChecked: '${checked}/${total}',
    }"
    :data="dataListT"
    @change="handleChange"
    @left-check-change="leftCheckChange"
    @right-check-change="rightCheckChange"
  >
    <template #default="{ option }">
      <span
        class="item"
        @dragstart="drag($event, option)"
      >{{ option.label || option.title }}</span>
      <!-- {{ option.key }} -  -->
    </template>
    <template #left-footer>
      <el-tooltip
        class="box-item"
        effect="dark"
        content="返回左边勾选中的数据"
        placement="top"
      >
        <el-button class="transfer-footer btn-mixins el-button-s" size="small" @click.stop="saveLeft">确定</el-button>
      </el-tooltip>
    </template>
    <template #right-footer>
      <el-tooltip
        class="box-item"
        effect="dark"
        content="返回右边勾选中的数据"
        placement="top"
      >
        <el-button class="transfer-footer btn-mixins el-button-s" size="small" @click.stop="saveRight">确定</el-button>
      </el-tooltip>
    </template>
  </el-transfer>
</template>
<script>
import Sortable from 'sortablejs'
import { reactive, ref, onMounted } from 'vue'
const transferData = reactive({
  value: [], // 默认选中的个数索引
  draggingKey: '', // 拖动的数据
  menuFilterMethod(query, item) {
    // return item.menuListdata.indexOf(query) > -1
    const regStr = query.replace(/\*/g, '.*')
    const reg = new RegExp(regStr)
    return reg.test(item.label)
  }
})
// 默认数据测试展示
const generateData = (_) => {
  const data = []
  for (let i = 1; i <= 15; i++) {
    data.push({
      key: i,
      label: `备选项 ${i}`,
      disabled: i % 4 === 0
    })
  }
  return data
}
// 数据初始化和拖拽初始化
const initEffect = (props, ctx) => {
  let dataListT = props.dataList.length ? props.dataList : generateData()
  const init = () => {
    // const transferRef = document.getElementById(props.idDom)
    const transfer = document.getElementById(props.idDom)
    const leftPanel = transfer.getElementsByClassName('el-transfer-panel')[0].getElementsByClassName('el-transfer-panel__body')[0]
    const rightPanel = transfer.getElementsByClassName('el-transfer-panel')[1].getElementsByClassName('el-transfer-panel__body')[0]
    const rightEl = rightPanel.getElementsByClassName('el-transfer-panel__list')[0]
    Sortable.create(rightEl, {
      animation: 100,
      onEnd: (evt) => {
        const { oldIndex, newIndex } = evt
        const temp = transferData.value[oldIndex]
        if (!temp || temp === 'undefined') {
          return
        } // 解决右边最后一项从右边拖左边,有undefined的问题 //这里和网上的有点不一样,网上搜到的结果排序是,当前拖拽的元素和拖拽位置的元素互换位置,但是实际上在使用el-transfer有两个问题 //1.右侧排序value的数组顺序来源于后台穿的数组顺序,实际如果不设置target-order="unshift"的话,展示会按照左侧的数据列顺序展示,导致拖拽排序时顺序乱七八糟 //2.实际拖拽排序看到的效果是当前拖拽元素拖拽到其他地方,其他地方的元素会下移,而不是调换顺序
        // 去除空字符串
        for (var i = 0; i < transferData.value.length; i++) {
          if (
            transferData.value[i] === '' ||
            transferData.value[i] === null ||
            typeof transferData.value[i] === 'undefined'
          ) {
            transferData.value.splice(i, 1)
            i = i - 1
          }
        }
        const arr_temp = [].concat(transferData.value) // 创建一个新的临时数组,用以操作后不变更原数组
        arr_temp.splice(newIndex, 0, arr_temp.splice(oldIndex, 1)[0]) // 在b位置插入从a位置截取的元素
        transferData.value = arr_temp
        ctx.emit('update:call-back', transferData.value)
      }
    })
    const leftEl = leftPanel.getElementsByClassName('el-transfer-panel__list')[0]
    Sortable.create(leftEl, {
      animation: 100,
      onEnd: (evt) => {
        const { oldIndex, newIndex } = evt
        dataListT.splice(newIndex, 0, dataListT.splice(oldIndex, 1)[0])
        // 保存的新的数组用于数据渲染
        const newArray = dataListT.slice(0)
        dataListT = newArray
      }
    })
    leftPanel.ondragover = (ev) => {
      ev.preventDefault()
    }
    leftPanel.ondrop = (ev) => {
      ev.preventDefault()
      const index = transferData.value.indexOf(transferData.draggingKey)
      if (index !== -1) {
        transferData.value.splice(index, 1)
      }
    }
    rightPanel.ondragover = (ev) => {
      ev.preventDefault()
    }
    rightPanel.ondrop = (ev) => {
      ev.preventDefault()
      if (transferData.value.indexOf(transferData.draggingKey) === -1) {
        transferData.value.push(transferData.draggingKey)
      }
    }
  }
  return { init, dataListT }
}
// 左边确定按钮  把左边默认展示的传递过去
const leftBtnEffect = (props, ctx) => {
  const leftDataChecked = ref([])
  leftDataChecked.value = props.leftChecked
  // props.leftChecked.forEach(e => {
  //   if (!e.disabled) {
  //     leftDataChecked.value.push(e)
  //   }
  // })
  const leftCheckChange = (value) => {
    //   console.log(value)
    leftDataChecked.value = value
  }
  // console.log(leftDataChecked.value)
  // 左侧确定事件
  const saveLeft = () => {
    ctx.emit('changeTransferLeft', leftDataChecked)
  }
  return { leftDataChecked, leftCheckChange, saveLeft }
}
// 右边确定按钮  把右边默认展示的传递过去
const rightBtnEffect = (props, ctx) => {
  const rightDataChecked = ref([])
  rightDataChecked.value = props.rightChecked
  // props.rightChecked.forEach(e => {
  //   if (!e.disabled) {
  //     rightDataChecked.value.push(e)
  //   }
  // })
  const rightCheckChange = (value) => {
    //   console.log(value)
    rightDataChecked.value = value
  }
  // 右侧确定事件
  const saveRight = () => {
    ctx.emit('changeTransferRight', rightDataChecked)
  }
  return { rightDataChecked, rightCheckChange, saveRight }
}
export default {
  props: {
    value: {
      type: Array,
      default() {
        return []
      }
    },
    // 按钮文字
    idDom: {
      default() {
        return 'transfer'
      },
      type: String
    },
    // 按钮文字
    buttonTexts: {
      default() {
        return []
      },
      type: Array
    },
    // 数据
    dataList: {
      default() {
        return []
      },
      type: Array
    },
    leftChecked: {
      default() {
        return []
      },
      type: Array
    },
    rightChecked: {
      default() {
        return []
      },
      type: Array
    },
    // 标题
    titles: {
      default() {
        return []
      },
      type: Array
    }
  },
  emits: ['changeTransfer', 'changeTransferLeft', 'changeTransferRight'],
  setup(props, ctx) {
    const { init, dataListT } = initEffect(props, ctx)
    const { leftCheckChange, saveLeft } = leftBtnEffect(props, ctx)
    const { rightCheckChange, saveRight } = rightBtnEffect(props, ctx)
    // 数据加载完之后  再获取对应的id
    onMounted(() => {
      init()
    })
    const renderFunc = (h, option) => {
      return h('span', null, option.label)
    }
    const handleChange = (value, direction, movedKeys) => {
      ctx.emit('changeTransfer', value, direction, movedKeys)
    }
    const drag = (ev, option) => {
      transferData.draggingKey = option.key
    }
    return { transferData, dataListT, handleChange, drag, leftCheckChange, saveLeft, rightCheckChange, saveRight, renderFunc }
  }
}
</script>
<style lang="scss" >
  .el-transfer__buttons {
    .el-button:first-child {
      margin-bottom: 8px;
    }
  }
</style>

使用

<template>
  <div>
    <LZTransfer
      v-model="transferData.value"
      :id-dom="transferData.transferId"
      :button-texts="transferData.buttonTexts"
      :left-checked="transferData.leftChecked"
      :left-data-list="transferData.leftDataList"
      :right-checked="transferData.rightChecked"
      :right-data-list="transferData.rightDataList"
      :titles="transferData.titles"
      @changeTransfer="changeTransfer"
      @changeTransferLeft="changeTransferLeft"
      @changeTransferRight="changeTransferRight"
    ></LZTransfer>
  </div>
  <div>
    <span>一个页面有两个穿梭框的展示和数据问题</span>
    <LZTransfer
      v-model="transferData2.value"
      :data-list="transferData2.dataList"
      :id-dom="transferData2.transferId2"
      :button-texts="transferData2.buttonTexts"
      :left-checked="transferData2.leftChecked"
      :right-checked="transferData2.rightChecked"
      :titles="transferData2.titles"
      @changeTransfer="changeTransfer2"
      @changeTransferLeft="changeTransferLeft2"
      @changeTransferRight="changeTransferRight2"
    ></LZTransfer>
  </div>
</template>

<script>
import { reactive } from 'vue'
const transferData = reactive({
// 默认右移动的数据
  value: [2, 3],
  // 箭头描述
  buttonTexts: ['到左边', '到右边'],
  //   左边默认选中
  leftChecked: [1, 4],
  //   右边默认选中
  rightChecked: [2, 3],
  // 每个穿梭框id
  transferId: 'transferId1',
  titles: ['左边标题', '右边标题'] //   自定义数据  里面必须要要label或者title 才能展示文字
})
const transferData2 = reactive({
  value: [],
  // 箭头描述
  buttonTexts: ['到左边2', '到右边2'],
  //   左边默认选中
  leftChecked: [5, 4],
  //   右边默认选中
  rightChecked: [],
  // 每个id
  transferId2: 'transferId2',
  titles: ['左边标题2', '右边标题2'],
  //   自定义数据  里面必须要要label或者title 才能展示文字
  dataList: [
        {
          disabled: false,
          key: 1,
          label: '备选项 11',
        },
        {
          disabled: false,
          key: 2,
          label: '备选项 2',
        },
        {
          disabled: false,
          key: 3,
          label: '备选项 3',
        },
        {
          disabled: false,
          key: 4,
          label: '备选项 4',
        },
        {
          disabled: false,
          key: 5,
          label: '备选项 5',
        },
        {
          disabled: false,
          key: 6,
          label: '备选项 6',
        },
        {
          disabled: false,
          key: 7,
          label: '备选项 7',
        },
        {
          disabled: false,
          key: 8,
          label: '备选项 8',
        },
        {
          disabled: false,
          key: 9,
          label: '备选项 9',
        },
        {
          disabled: false,
          key: 10,
          label: '备选项 10',
        },
        {
          disabled: false,
          key: 11,
          label: '备选项 11',
        },
  ]
})

export default {
  setup() {
  // 当前移动的数据和方向   移动的key
    const changeTransfer = (value, direction, movedKeys) => {
      console.log(value, direction, movedKeys)
    }
    const changeTransferLeft = (list) => {
      // 这里要通过.value拿到数据
      console.log(list.value)
    }
    const changeTransferRight = (list) => {
      console.log(list.value)
    }
    const changeTransfer2 = (value, direction, movedKeys) => {
      console.log(value, direction, movedKeys)
    }
    const changeTransferLeft2 = (list) => {
      console.log(list.value)
    }
    const changeTransferRight2 = (list) => {
      console.log(list.value)
    }
    return { transferData, transferData2, changeTransfer, changeTransferLeft, changeTransferRight, changeTransfer2, changeTransferLeft2, changeTransferRight2 }
  }
}
</script>

  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
你可以尝试以下步骤来封装一个Vue 3和TypeScript下使用Element Plus的表单提交组件: 1. 安装必要的依赖: - Vue 3:`npm install vue@next` - TypeScript:`npm install -D typescript` - Element Plus:`npm install element-plus` 2. 创建一个新的Vue组件,并为其选择一个合适的名称,例如`FormSubmit.vue`。 3. 在`FormSubmit.vue`文件中,导入必要的模块和样式: ```vue <template> <!-- 表单内容 --> </template> <script lang="ts"> import { defineComponent } from 'vue'; import { ElButton, ElForm, ElFormItem } from 'element-plus'; export default defineComponent({ components: { ElButton, ElForm, ElFormItem, }, }); </script> <style scoped> /* Element Plus 样式 */ @import 'element-plus/packages/theme-chalk/src/index.scss'; /* 自定义样式 */ /* ... */ </style> ``` 4. 在模板中编写表单内容,并使用Element Plus的组件来构建表单: ```vue <template> <el-form ref="form" :model="formData" label-width="120px"> <el-form-item label="姓名" prop="name"> <el-input v-model="formData.name"></el-input> </el-form-item> <!-- 更多表单项 --> <el-form-item> <el-button type="primary" @click="submitForm">提交</el-button> </el-form-item> </el-form> </template> <script lang="ts"> // ... export default defineComponent({ // ... data() { return { formData: { name: '', // 更多表单字段 } }; }, methods: { submitForm() { // 表单提交逻辑 if (this.$refs.form.validate()) { // 表单验证通过,执行提交操作 // ... } } } }); </script> ``` 这样,你就可以使用封装好的表单提交组件来方便地处理表单提交了。你可以根据实际需求添加更多的表单项,并在`submitForm`方法中实现你的提交逻辑。希望这可以帮到你!

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值