Vue技术前端开发那些事儿每天学一点Vue3

封装第三方组件(21)写个拖拽 form 的自定义指令

2021-08-17  本文已影响0人  自然框架

折腾完table,我们来折腾表单。
以前表单是基于table来实现的,现在都改成div的形式了。
那我们就入乡随俗来研究一下 el-form 渲染后的结构。

一番折腾发现可以找到label,然后给他加上拖拽的几个事件我们就可以拖拽了。

优点:

缺点:

可以拖拽 表单 的自定义指令

 
/**
 * 拖拽 table 的 th,返回拖拽信息
 */
const formDrag = (app, options) => {
  app.directive('formdrag', {
    // 指令的定义
    mounted (el, binding) {
      // console.log('===== el', el)
      // console.log('===== binding', binding)

      /**
       * 实现 th 的拖拽
       * @param {string} className 用于找到目标的 class 名称。
       * @param {reactive} dragInfo reactive 返回拖拽信息。
       * @returns 没有返回
       * * const dragInfo = {
       * *  offsetX: 0,
       * *  isLeft: true, // th 左侧结束拖拽
       * *  ctrl: false, // 是否按下ctrl
       * *  source: '', // 开始拖拽的th
       * *  target: '', // 结束拖拽的th
       * *  sourceIndex: 0, // 开始拖拽的序号
       * *  targetIndex: 0 // 结束拖拽的序号
       * * })
       */
      const setFormforDrag = (className, dragInfo) => {
        const domForm = el.children[0].children
        console.log('内部表单:', domForm)

        const domLabel = domForm[0].children[0].children[0]
        console.log('内部Label:', domLabel)

        const labelCount = domForm.length
        // 记录 th 的序号和宽度
        const thIndex = {}
        // 记录临时的源
        let src1 = ''
        let src2 = 1
        // 设置th的拖拽
        for (let i = 0; i < labelCount; i++) {
          const label = domForm[i].children[0].children[0]
          thIndex[label.innerText] = {
            index: i, // 记录th的序号
            width: label.offsetWidth // 记录 th 的宽度
          }
          // 设置可以拖拽
          label.setAttribute('draggable', true)
          // 拖拽时经过
          label.ondragover = (event) => {
            event.preventDefault()
          }
          // 开始拖拽
          label.ondragstart = (event) => {
            // console.log('ondragstart - event', event)
            src1 = event.target.innerText
            src2 = thIndex[event.target.innerText].index
          }
          // 结束拖拽
          label.ondrop = (event) => {
            // console.log('ondrop - event', event)
            dragInfo.offsetX = event.offsetX
            dragInfo.ctrl = event.ctrlKey
            dragInfo.source = src1
            dragInfo.sourceIndex = src2
            dragInfo.target = event.target.innerText
            // console.log('ondrop - dragInfo', dragInfo)
            // 寻找th的序号
            dragInfo.targetIndex = thIndex[event.target.innerText].index
            dragInfo.isLeft = dragInfo.offsetX < thIndex[event.target.innerText].width / 2
          }
        }
      }

      binding.value.setFormforDrag = setFormforDrag
    }
  })
}

export default formDrag

好吧,其实就是在拖拽table的代码的基础上改的,主要还是找到label。

挂载指令

main.js

import { createApp } from 'vue'
import App from './App.vue'
// 拖拽table的th
import formDrag from './control-web/js/formDrag.js'

const app = createApp(App)

app.use(router) // 路由
  .use(formDrag) // form 的拖拽
  .mount('#app')

使用方法

 <nf-el-form
      v-formdrag="formInfo"
      v-model="model"
      :partModel="partModel"
      v-bind="formMeta"
    >
 
// table 的拖拽功能
const formInfo = {
  setFormforDrag: () => {
    console.log('原始的获取td的函数')
  }
}

const dragInfo = reactive({
  offsetX: 0,
  isLeft: true, // 是否在 label 的左侧结束拖拽
  ctrl: false, // 是否按下了ctrl
  source: '',
  target: '',
  sourceIndex: 0, // 开始拖拽的位置
  targetIndex: 0 // 结束拖拽的位置
})

onMounted(() => {
  nextTick(() => {
    formInfo.setFormforDrag(tableClass, dragInfo)
    watch(() => dragInfo, () => {
      console.log('表单的拖拽信息:', dragInfo)
    },
    { deep: true })
  })
})

试了一下,只需要 onMounted ,不需要 nextTick 了,不过考虑到有些表单会比较大,所以还是稳妥一点,反正也不差这点时间。

拖拽信息的结构是一样的,因为都是拖拽嘛,都有“字段”的因素。

支持单列和多列的情况。

拖拽效果

单列拖拽 拖拽信息 双列的拖拽 拖拽信息

然后就是根据拖拽信息实现调整表单布局的代码了。

上一篇下一篇

猜你喜欢

热点阅读