# 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>


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://408550179s-organization.gitbook.io/blog/draggable-tuo-zhuai-pai-xu-he-pointerevent-ying-yong.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
