laitimes

How to smoothly realize the drag-and-drop function of the home page?

author:Senior Internet Architect

Brief requirements

Recently, I received a request to realize that different login personnel can customize the home page module card. To put it simply, it is to realize the addition and drag and drop of the home page kanban module, and the effect is as follows:

How to smoothly realize the drag-and-drop function of the home page?

Technology selection

Native JS supports drag-and-drop, you only need to set the draggable property of the dragged element to "true", and then call the corresponding function.

拖拽操作 - Web API 接口参考 | MDN

However, the native JS function is not perfect, and there are many places that need to be transformed to use, so it is better to use mature third-party plug-ins.

Our main project is vue3, and after a series of comparisons, we finally chose vue-draggable-next.

vue-draggable-next

vue-draggable-next has about 30,000 weekly downloads per month, which can be seen as a relatively reliable plugin.

How to smoothly realize the drag-and-drop function of the home page?

How it works is also described in detail on npmj:

vue-draggable-next

If the use of the API in English seems uncomfortable, there are also Chinese usage documents on the Internet:

vue.draggable.next 中文文档 - itxst.com

This plugin is also available in Vue2 and pure JS, and other frameworks are also perfect for use.

Implement ideas

Brief analysis of requirements and technologies

According to our needs, what we should implement is group dragging, suppose we have three columns, then what we want to implement is that the three columns of data A, B, and C are dragged and dragged with each other.

How to smoothly realize the drag-and-drop function of the home page?

Let's take a look at the example given by the Chinese official website:

vue.draggable.next group 例子

How to smoothly realize the drag-and-drop function of the home page?

It seems easy, we just need to write multiple draggable tags, and each draggable tag writes the same group name.

Implementation scenario

Framework implementation

Going back to the code, in order to implement a three-column drag-and-drop list of modules, we first need to introduce components

<script lang="ts" setup>
import { VueDraggableNext } from 'vue-draggable-next'
// ....
</script>
           

Then define an array to store the data:

<script lang="ts" setup>
    import { VueDraggableNext } from 'vue-draggable-next'
    const moduleList = ref([
      {
        "columnIndex": 1,
        "moduleDetail": [
          { "moduleCode": "deviation", "moduleName": "控制失调空间",},
          { "moduleCode": "meeting_pending", "moduleName": "会议待办",},
          { "moduleCode": "abnormal_events", "moduleName": "异常事件", },
          { "moduleCode": "audit_matters", "moduleName": "事项审批",}
        ],
      },
      {
        "columnIndex": 2,
        "moduleDetail": [
          { "moduleCode": "air_conditioning_terminal", "moduleName": "空调末端", }
        ],
      },
      {
        "columnIndex": 3,
        "moduleDetail": [
          { "moduleCode": "run_broadcast", "moduleName": "运行播报",},
          {"moduleCode": "my_schedule", "moduleName": "我的日程", },
          { "moduleCode": "cold_station", "moduleName": "冷站",}
        ],
      }
    ])
</script>
           

Finally, in the code we can use v-for to render in a loop

<div v-for="moduleColumn in  moduleList " :key="moduleColumn.columnIndex" class="box">
  <VueDraggableNext :list="moduleColumn.moduleDetail" group="column" >
    <div v-for="(item, index) in  moduleColumn.moduleDetail " :key="item.moduleCode" class="drag-item">
      <!-- 模块内容 -->
    </div>
  </VueDraggableNext>
</div>
           

Note the html structure above, we render three columns of VueDraggableNext tags in a loop, and each VueDraggableNext tag renders all the modules inside the column with v-for="(item, index) in moduleColumn.moduleDetail. We use group="column" to make the group name of each VueDraggableNext component the same, so that the modules between the three dragg-and-drop tags can be dragged and dropped from each other.

Drag point settings

In normal cases, we definitely want to drag the component at a fixed position, so we need to use the handle property of the dragged component.

vue.draggable.next属性说明:

handle :handle=".mover" 只有当鼠标在class为mover类的元素上才能触发拖到事件

According to the property description, our code is also very easy to implement.

<div v-for="moduleColumn in  moduleList " :key="moduleColumn.columnIndex" class="box">
      <VueDraggableNext :list="moduleColumn.moduleDetail" handle=".move" group="column">
        <div v-for="(item, index) in  moduleColumn.moduleDetail " :key="item.moduleCode" class="drag-item">
          <div class="move">
            拖拽区域
          </div>
          <!-- 模块内容 -->
        </div>
      </VueDraggableNext>
  </div>
           

Addition, deletion and modification of data

In actual development, we will dynamically change the list according to the interface or operation, and the code layer will change the value of moduleList. Fortunately, if you write the code in the above way, when you finish dragging and dropping, the moduleList value above will automatically change, and we don't have to do any processing!! so you can see that the addition and deletion of data is not a problem at all.

How to render components dynamically

In actual development, we may encounter a problem, that is, how to render components dynamically, if you are familiar with vue, you can use dynamic components to achieve this.

First, we need to define a list of modules

import MeetingPending from '../components/meetingPending.vue'
import AbnormalEvents from '../components/abnormalEvents/index.vue'
import MySchedule from '../components/mySchedule.vue'
import TransactionApproval from '../components/transactionApproval.vue'
import RunningBroadcast from '../components/runningBroadcast.vue'
import CodeSite from '../components/codeSite/index.vue'
import MismatchSpace from '../components/mismatchSpace/index.vue'
import AirDevice from '../components/airDevice/index.vue'

// !全量模块选择列表
export const allModuleList = [
  { moduleCode: 'meeting_pending', label: '会议待办', component: MeetingPending },
  { moduleCode: 'my_schedule', label: '我的日程', component: MySchedule },
  { moduleCode: 'audit_matters', label: '事项审批', component: TransactionApproval },
  { moduleCode: 'abnormal_events', label: '异常事件', component: AbnormalEvents },
  { moduleCode: 'deviation', label: '控制失调空间', component: MismatchSpace },
  { moduleCode: 'run_broadcast', label: '运行播报', component: RunningBroadcast },
  { moduleCode: 'cold_station', label: '冷站', component: CodeSite },
  { moduleCode: 'air_conditioning_terminal', label: '空调末端', component: AirDevice }
]
           

Then match according to the moduleCode and render it dynamically

<div v-for="moduleColumn in  moduleList " :key="moduleColumn.columnIndex" class="box">
      <VueDraggableNext :list="moduleColumn.moduleDetail" handle=".move" group="column">
        <div v-for="(item, index) in  moduleColumn.moduleDetail " :key="item.moduleCode" class="drag-item">
          <div class="move">
            拖拽区域
          </div>
          <component :is="getComponentsByCode(item.moduleCode)" ></component>
        </div>
      </VueDraggableNext>
  </div>
           

More customization needs

If the above features don't meet your needs, we can use the other properties of this component to accomplish more unexpected effects

If you can't see the following property descriptions completely, you can see the corresponding menu on the left for detailed descriptions and examples.

The name of the property illustrate
group 如果一个页面有多个拖拽区域,通过设置group名称可以实现多个区域之间相互拖拽 或者 { name: "...", pull: [true, false, 'clone', array , function], put: [true, false, array , function] }
sort If you set this parameter to false, the group to which it belongs cannot be sorted
delay How many seconds after the mouse is pressed to drag and drop an element
touchStartThreshold How many px mouse presses to move to drag an element
disabled :disabled= "true",是否启用拖拽组件
animation Animation effect when dragging, for example, set animation=1000 to indicate a 1-second transition animation effect
handle :handle=".mover" 只有当鼠标在class为mover类的元素上才能触发拖到事件
filter :filter=".unmover" 设置了unmover样式的元素不允许拖动
draggable :draggable=".item" 样式类为item的元素才能被拖动
ghost-class :ghost-class="ghostClass" sets the placeholder class name for the dragged element, your custom style may need to be added !important to take effect, and set the forceFallback property to true
chosen-class :ghost-class="hostClass" 被选中目标的样式,你的自定义样式可能需要加!important才能生效,并把forceFallback属性设置成true
drag-class :drag-class="dragClass"拖动元素的样式,你的自定义样式可能需要加!important才能生效,并把forceFallback属性设置成true
force-fallback The default is false, ignoring the HTML5 drag behavior, because there is a property in h5 that can also be dragged, and when you want to customize the ghostClass chosenClass dragClass style, it is recommended that forceFallback be set to true
fallback-class Default is false, which clones the style of the selected element to the style that follows the mouse
fallback-on-body By default, false, the cloned element is added to the body of the document
fallback-tolerance 按下鼠标移动多少个像素才能拖动元素,:fallback-tolerance="8"
scroll The default is true, and there is whether the scrolling area allows dragging
scroll-fn Rolling callback function
scroll-fensitivity Scroll the scroll bar when you are farther away from the scroll area
scroll-speed Scroll speed

传送门:vue.draggable.next 中文文档 - itxst.com

Write at the end

Related Articles:How to Implement Anchor Positioning and Flashing Prompts of Modules:juejin.cn/post/734622...

How to smoothly realize the drag-and-drop function of the home page?

Author: Shi Xiaoshi Orz

Link: https://juejin.cn/post/7346121373112811583