如何提高 Vue Draggable 嵌套的精度?

Posted

技术标签:

【中文标题】如何提高 Vue Draggable 嵌套的精度?【英文标题】:How do I improve the precision of Vue Draggable nesting? 【发布时间】:2020-03-11 20:53:01 【问题描述】:

我一直在使用 Vue 2 组件和 Vue.Draggable 库开发表单构建器。这很好用,除了一个持续存在的问题——对于最终用户来说,嵌套块实际上很难嵌套。让一个块缩进另一个块需要大量的精确度和反复试验。

如何解决此问题并使其更易于使用?看起来几乎是因为缺少拖放区。

(链接到 Imgur,因为无法嵌入动画图像)

请注意,由于多文件的性质,我无法在 CodePen 中运行代码。该代码还依赖于 UI 的 TailwindCSS。

/FormBuilder.vue

<style>
    .wrapper 
        margin-bottom: -1rem;
    
    .group.templates 
        display: flex;
        flex-direction: row;
        margin: 0 -0.5em;
    
    .group.templates .field 
        margin: 0 0.5rem;
    
    .group.elements 
        margin-top: 1rem;
    
    .group.elements .field 
        margin: 1rem 0;
    
    .group.elements .field + div 
        margin-left: 1rem;
    
    .group.elements .ghost 
        opacity: 0.5;
    
</style>

<template>
    <div>
        <div>
            <draggable :list="templates" :group=" name: 'form-builder', pull: 'clone', put: false " :clone="cloneTemplate" :component-data="getComponentData()">
                <div v-for="element in templates" :key="element.name">
                    <div class="field bg-white rounded shadow rounded font-bold text-gray-700 px-4 py-2">
                        <h4 class="font-bold"><i class="fas mr-2" :class="[ 'fa-' + element.icon ]"></i>  element.name </h4>
                    </div>
                </div>
            </draggable>
        </div>
        <div class="wrapper">
            <nested-draggable :items="elements" name="form-builder"></nested-draggable>
        </div>
    </div>
</template>

<script>
    import Draggable from 'vuedraggable'
    import NestedDraggable from './FormBuilder/NestedDraggable'

    export default 
        components: 
            Draggable,
            NestedDraggable,
        ,

        data() 
            return 
                id: 6,
                templates: [
                    
                        name: 'Field',
                        icon: 'heading',
                        type: 'field'
                    
                ],
                elements: [
                    
                        id: 1,
                        type: 'test',
                        locked: true,
                        options: 
                            text: "field"
                        ,
                        children: []
                    ,
                    
                        id: 2,
                        type: 'paragraph',
                        locked: false,
                        options: 
                            text: "field"
                        ,
                        children: []
                    ,
                    
                        id: 3,
                        type: 'field',
                        locked: false,
                        options: 
                            text: "field"
                        ,
                        children: [
                            
                                id: 4,
                                type: 'field',
                                locked: false,
                                options: 
                                    text: "field"
                                ,
                                children: []
                            ,
                            
                                id: 5,
                                type: 'field',
                                locked: false,
                                options: 
                                    text: "field"
                                ,
                                children: []
                            
                        ]
                    
                ]
            
        ,

        methods: 
            cloneTemplate(object) 
                return 
                    id: this.id++,
                    type: object.type,
                    locked: false,
                    options: ,
                    children: []
                
            ,
            getComponentData() 
                return 
                    attrs: 
                        class: 'group templates'
                    
                
            
        
    
</script>

/FormBuilder/NestedDraggable.vue

<template>
    <draggable ghost-class="ghost" :list="items" :group="name" :component-data="getComponentData()">
        <div v-for="element in items" :key="element.id">
            <field v-if="element.type == 'field'" :locked="element.locked" :options="element.options"></field>
            <nested-draggable v-if="element.children" :items="element.children" :name="name"></nested-draggable>
        </div>
    </draggable>
</template>

<script>
    import draggable from 'vuedraggable'

    import field from "./Fields/Field"

    export default 
        name: 'nested-draggable',

        components: 
            draggable,
            field
        ,

        props: 
            name: 
                type: String,
                required: true
            ,
            items: 
                type: Array,
                required: true
            
        ,

        methods: 
            getComponentData() 
                return 
                    attrs: 
                        class: 'group elements'
                    
                
            
        
    
</script>

/FormBuilder/Fields/Field.vue

<template>
    <div class="field bg-white rounded shadow rounded">
        <div class="px-4 py-2 border-b flex flex-row items-center text-gray-700">
            <h4 class="font-bold mr-4"><i class="fas fa-heading mr-2"></i> Field</h4>
        </div>
        <div class="p-4 pt-2">
            <label for="content" class="form-label">Content</label>
            <input type="text" id="content" class="form-control">
        </div>
    </div>
</template>

<script>
    export default 
        props: 
            locked: 
                type: Boolean,
                default: false
            ,
            options: 
                type: Object,
                default: () => 
                    return 
                        content: ''
                    
                
            
        
    
</script>

【问题讨论】:

我也在找这个。由于它使用 SortableJS 作为其基础,因此 sortable 确实有一个称为 swapThreshold 的阈值选项,但是,在搜索 Vue-Draggable 存储库时,我根本找不到引用此选项。可排序选项:github.com/SortableJS/Sortable#options 【参考方案1】:

对我来说 :emptyInsertThreshold='20' 参数确实很神奇。您必须稍微调整一下值,但它解决了我的嵌套问题。

【讨论】:

【参考方案2】:

这可以通过 options 属性实现。 Vue Draggable 使用 SortableJS。现在它仍然通过 options 属性传递选项。

<template>
<draggable :options="swapThreshold: 0.5">
    <div v-for="element in items" :key="element.id">
        <field v-if="element.type == 'field'" :locked="element.locked" :options="element.options"></field>
        <nested-draggable v-if="element.children" :items="element.children" :name="name"></nested-draggable>
    </div>
</draggable>

在我上面展示的示例中,我将阈值降低了一半。

https://github.com/SortableJS/Sortable#swapthreshold-option

swapThreshold 选项

交换区将占用的目标百分比,作为 0 到 1 之间的浮点数。

注意

option 属性已弃用,将在未来版本中删除。我假设它在那里作为它尚未实施的其他选项的后备,例如swapThreshold

有关弃用通知的更多详细信息请点击此处: https://github.com/SortableJS/Vue.Draggable/blob/master/documentation/migrate.md#options-props

如果您想使用 Vue Draggable 文档中没有的其他选项,您可以查看 SortableJS 的选项页面以了解您仍然可以使用哪些选项:

https://github.com/SortableJS/Sortable#options

【讨论】:

以上是关于如何提高 Vue Draggable 嵌套的精度?的主要内容,如果未能解决你的问题,请参考以下文章

渲染期间嵌套 ul 列表中的简单 Vue.Draggable 中断

elementUI系列一vue拖拽功能实现-vuedraggable实现多层嵌套拖拽

Vue.Draggable:如何“将一个项目拖到另一个项目上”而不是添加/删除

Draggable vue - 如何访问“列”ID?

在嵌套数据表中拖放

vue2开发过程中用到的插件