> For the complete documentation index, see [llms.txt](https://408550179s-organization.gitbook.io/blog/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://408550179s-organization.gitbook.io/blog/draggable-tuo-zhuai-pai-xu-he-pointerevent-ying-yong.md).

# draggable拖拽排序和pointerEvent应用

<figure><img src="/files/BdLh2k5WZ9OPHL8LQaxh" alt=""><figcaption><p>draggable拖拽排序展示</p></figcaption></figure>

<figure><img src="/files/9yLdejISegIuVVBjJsVO" alt=""><figcaption><p>pointerEvent的拖拽/操作功能展示</p></figcaption></figure>

**draggable拖拽排序**

拖拽排序这类需求基本上使用draggable就可以满足，这里我用了vue-draggable的npm包，简单贴一个引用代码：

{% code overflow="wrap" fullWidth="true" %}

```html

<draggable
	:list="element.taskList"
	@end="taskSort($event, element.taskList)"
	class="scrum-stage-tasks"
	:forceFallback="false"
	ghostClass="task-ghost"
	item-key="id"
	dragClass="task-drag"
	fallbackClass="task-drag"
	:draggable="'.task-card'"
	:disabled="!checked">
	<template #item="{ element }">
	<div class="task task-card ui-sortable-handle fadeIn" :id="element.id" :key="element.id">
		<div class="task-content-set open-detail task-title-box" @click="showModal(element, index)">
			<div class="task-content-wrapper">
				<div class="task-content" :style="{ overflowWrap: 'anywhere' }">
					{{ element.taskName }}
				</div>
			</div>

		</div>
	</div>
	</template>
</draggable>

```

{% endcode %}

如果需要同时需要使用拖动左右滑动增强体验感可以参考使用指令，使其具备滑动/拖拽排序两种功能，阻止冒泡和冲突，贴一下指令代码用于参考：

{% code fullWidth="true" %}

```javascript
const vDirectives = {
		mounted: (el) => {
			el.onmousedown = function (ev) {
				const exclude = ['thin-scroll']
				const isExclude = exclude.some((item) => ev.target.classList.contains(item))
				if (!isExclude) {
					return
				}

				ev.stopPropagation()
				ev.preventDefault()
				document.body.style.cursor = 'move'
				const disX = ev.clientX
				const disY = ev.clientY
				const originalScrollLeft = el.scrollLeft
				const originalScrollTop = el.scrollTop
				const originalScrollBehavior = el.style['scroll-behavior']
				const originalPointerEvents = el.style['pointer-events']
				el.style['scroll-behavior'] = 'auto'
				let elEvent = ev
				// 鼠标移动事件是监听的整个document，这样可以使鼠标能够在元素外部移动的时候也能实现拖动
				document.onmousemove = function (ev) {
					ev.stopPropagation()
					ev.preventDefault()
					document.body.style.cursor = 'move'

					const distanceX = ev.clientX - disX
					const distanceY = ev.clientY - disY
					el.scrollTo(originalScrollLeft - distanceX, originalScrollTop - distanceY)
					// 由于我们的图片本身有点击效果，所以需要在鼠标拖动的时候将点击事件屏蔽掉
					el.style['pointer-events'] = 'none'
				}
				document.onmouseup = function (ev) {
					ev.stopPropagation()
					// elEvent.target.style.cursor = "auto";
					// ev.target.style.cursor = "auto";
					document.body.style.cursor = 'auto'
					document.onmousemove = null
					document.onmouseup = null
					el.style['scroll-behavior'] = originalScrollBehavior
					el.style['pointer-events'] = originalPointerEvents
				}
			}
		}
	}
```

{% endcode %}

事件在于document上绑定，跳出页面或者关闭组件需要及时移除该事件，否则可能会造成污染！

**pointerEvent的拖拽/操作**

鼠标或者单个点的up/down/move事件，那么为什么要使用pointerEvent呢？因为在开发中，我们常用的mouse/touch事件仅仅只是兼容各自的环境，虽然可以直接拿到完整的单个dom块的虚拟DOM，但是无法同时兼顾PC或者说移动端口。那么就可以使用pointerEvent。那么问题又来了！pointerEvent绑定在大的父级容器上，比如body，但是pointerEvent返回相关的坐标，那么怎么通过坐标去获取到相对应的DOM和相关的列表某个index的数据呢？这里我采用了elementsFromPoint的方法！贴一下代码作为参考

```javascript
// pointer事件的应用
const box = document.querySelector('.flex-box')
  const startVal = ref(null)
  const endVal = ref(null)
  const banList = [
    ['中央刀库列表', '机内刀库列表'],
    ['中央刀库机械手', '天轨机械手'],
    ['中央刀库机械手', '机内刀库列表'],
    ['天轨机械手', '中央刀库机械手'],
    ['天轨机械手', '中央刀库放刀位'],
    ['天轨随行台', '中央刀库放刀位'],
    ['中央刀库放刀位', '机内刀库列表'],
    ['中央刀库放刀位', '天轨随行台'],
    ['中央刀库放刀位', '天轨机械手'],
    ['机内刀库列表', '中央刀库放刀位'],
    ['机内刀库列表', '中央刀库列表'],
    ['机内刀库列表', '中央刀库机械手']
  ]
  box.onpointerup = (e) => {
    circleState(e, false)
    stopScroll(false)
    const doms = document.elementsFromPoint(e.clientX, e.clientY)
    doms.map((item, i) => {
      if (item.dataset.type && startVal.value && startVal.value.type !== item.dataset.type) {
        endVal.value = {
          tip: '目标',
          type: item.dataset.type,
          toolsWhposCode: item.dataset.toolswhposcode,
          allState: item.dataset.allstate,
          toolsState: item.dataset.toolsstate
        }
        if (banList.some((item) => item.toString() === [startVal.value.type, endVal.value.type].toString())) {
          return message.warning('无法进行此操作')
        }

        Modal.confirm({
          icon: '',
          centered: true,
          maskClosable: true,
          content: () =>
            h(
              'div',
              `【${startVal.value?.tip}】${startVal.value?.type}（${startVal.value?.toolsWhposCode}） -->> 【${endVal.value?.tip}】${endVal.value?.type}（${endVal.value?.toolsWhposCode}）`
            ),
          onOk() {
            return new Promise((resolve) => {
              let data = data = {
                  sourcePos: { toolsWhposCode: startVal.value.toolsWhposCode }, // 源储位
                  targetPos: { toolsWhposCode: endVal.value.toolsWhposCode, eqptCode: workEqptCode.value }, // 目标储位
                  poolActionType: ''
              }

              toolsdisplayApi
                .fmsPosMoveDrag(data)
                .then(() => {
                  startVal.value = null
                  endVal.value = null
                  getToolTaskPool()
                  getOutToolTaskList()
                  resolve()
                })
                .finally(() => {})
              startVal.value = null
              endVal.value = null
              resolve()
            })
          }
        })
      }
    })
  }
  box.onpointermove = (e) => {
    circleState(e, true)
    const doms = document.elementsFromPoint(e.clientX, e.clientY)
    doms.map((item, i) => {
      if (item.dataset.type) {
        item.classList.add('drag-hover')
      }
    })
  }
  box.onpointerdown = (e) => {
    const doms = document.elementsFromPoint(e.clientX, e.clientY)
    doms.map((item) => {
      if (item.dataset.type) {
        stopScroll(true)
        circleState(e, true)
        message.success('请拖动至目标位置')
        startVal.value = {
          tip: '源',
          type: item.dataset.type,
          toolsWhposCode: item.dataset.toolswhposcode,
          allState: item.dataset.allstate,
          toolsState: item.dataset.toolsstate
        }
      }
    })
  }
```

```javascript
const doms = document.elementsFromPoint(e.clientX, e.clientY)
```

通过elementsFromPoint方法传入坐标获取当前坐标下的dom！

既然有了虚拟DOM那么接下来就是拿相关数据，可以把数据绑在DOM初次加载时写入DOM的attr/data！

在监听事件中返回的el对象中可以获取相对于的传参，用于前后端交互！

<pre class="language-html"><code class="lang-html">&#x3C;div
<strong>class="clawBall"
</strong>data-type="中央刀库机械手"
:data-toolsWhposCode="item?.toolsWhposCode"
:data-allState="item?.allState"
:data-toolsState="item?.toolsState"
:class="item?.allState == 0 ? '' : 'status-' + item?.allState"
>&#x3C;/div>
</code></pre>
