美文网首页Vue技术前端开发那些事儿每天学一点Vue3
封装第三方组件(21)写个拖拽 form 的自定义指令

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

作者: 自然框架 | 来源:发表于2021-08-17 20:27 被阅读0次

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

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

    优点:

    • 不破坏现有结构。
    • el-form渲染生成的表单,其他UI库没尝试,估计也差不多吧。

    缺点:

    • 不会做特效,默认的拖拽效果,不好看。
    • 自定义指令只实现拖拽效果,返回拖拽信息,不实现具体功能。

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

     
    /**
     * 拖拽 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
    
    
    • el.children[0].children
      改进了一下,不需要传入class名称了,直接通过 el.children[0].children 来找到表单的dom。

    • dragInfo
      reactive 类型的对象,便于返回拖拽信息。

    • .ondragover
      设置拖拽时经过的事件

    • .ondragstart
      开始拖拽时的事件,可以得到th的部分信息,主要是其他的各种信息。

    • .ondrop
      结束拖拽的事件

    • event.target.innerText
      获取 th 的文本内容,目前只找到这个可以作为 th 的标识

    • event.offsetX
      th 的鼠标的x的坐标

    • event.ctrlKey
      结束拖拽时,是否按住 ctrl 键。

    好吧,其实就是在拖拽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 了,不过考虑到有些表单会比较大,所以还是稳妥一点,反正也不差这点时间。

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

    支持单列和多列的情况。

    拖拽效果

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

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

    相关文章

      网友评论

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

        本文链接:https://www.haomeiwen.com/subject/ymiibltx.html