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:
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 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.
Let's take a look at the example given by the Chinese official website:
vue.draggable.next group 例子
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...
Author: Shi Xiaoshi Orz
Link: https://juejin.cn/post/7346121373112811583