折腾完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 了,不过考虑到有些表单会比较大,所以还是稳妥一点,反正也不差这点时间。
拖拽信息的结构是一样的,因为都是拖拽嘛,都有“字段”的因素。
支持单列和多列的情况。
拖拽效果
单列拖拽 拖拽信息 双列的拖拽 拖拽信息然后就是根据拖拽信息实现调整表单布局的代码了。
网友评论