1

我一直在使用 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>
4

2 回答 2

0

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

交换阈值选项

交换区将占用的目标的百分比,作为 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

于 2019-12-20T23:28:17.567 回答
0

对我来说 :emptyInsertThreshold='20' 论点确实很神奇。你必须稍微摆弄一下这个值,但它解决了我的嵌套问题。

于 2020-11-02T07:34:05.880 回答