hejianxian / vddl

🦄 Vue components for modifying lists with the HTML5 drag & drop API.
http://hejx.space/vddl
MIT License
395 stars 75 forks source link

关于拖拽后值改变的问题 #11

Closed lmasdata closed 7 years ago

lmasdata commented 7 years ago

你好,用了你的vddl感觉非常不错,但是最近在使用拖拽的时候遇到一个问题。 1:首先我拖拽了两个输入框,然后改变了其中的一个title值 2:在拖拽两个输入框互换顺序的时候,两个输入框的title值成为了相同的 3: 我大体看了下,好像是在互换的时候,删除了其中的一个,然后把新的给覆盖上了 描述的不是很清楚,下面有截图 image image

hejianxian commented 7 years ago

@lmasdata 谢谢!

在拖动的时候的确是会进行2次的裁剪操作,你的列表是否使用了 :key="index",这个会导致计算错误。

lmasdata commented 7 years ago

@hejianxian 你好,:key使用的是id,这是我的代码

<template>
    <div>
        <Row>
            <Col span="16">
            <Card>
                <Row>
                    <Col span="10">
                    <Card dis-hover>
                        <p slot="title">
                            <Icon type="ios-list-outline"></Icon>
                            组件库
                        </p>
                        <div style="min-height: 360px;" class="comp-div">
                            <Row :gutter="16" id="dropComp">
                                <Col span="12" v-for="comp in compList" :key="comp.id" :data-index="comp.id">
                                <vddl-draggable class='comp_item'
                                                :draggable='comp'
                                                :copied='copied'
                                                effect-allowed='copy'>
                                    {{comp.name}}
                                </vddl-draggable>
                                </Col>
                            </Row>
                        </div>
                    </Card>
                    </Col>
                    <Col span="14" class="padding-left-10">
                    <div class="formcanvas">
                        <div class="formcanvas-title">这应该是个手机</div>
                        <div class="formcanvas-inner">
                            <div class="formcanvas-body dropbody empty">
                                <div v-for="(list, zone) in dropzones">
                                    <div class="panel">
                                        <div class="panel__body" style="min-height: 500px">
                                            <vddl-list class="panel__body-list"
                                                       :list="list"
                                                       effect-allowed="move"
                                                       :disable-if="disable"
                                                       :external-sources="true">
                                                <nestlist v-for="(item, number) in list"
                                                          :key="item.id"
                                                          :item="item"
                                                          :list="list"
                                                          :index="number"
                                                          :selected="handleSelected"
                                                          :selected-item="selectedItem"
                                                          :disable="disable">
                                                </nestlist>
                                            </vddl-list>
                                        </div>
                                    </div>
                                </div>
                            </div>
                        </div>
                    </div>
                    </Col>
                </Row>
            </Card>
            </Col>
            <Col span="8" class="padding-left-10">
            <Card>
                <p slot="title">
                    <Icon type="ios-paper-outline"></Icon>
                    控件设置
                </p>
                <Button slot="extra" type="info" @click="save" style="margin-right: 5px;">保存表单</Button>
                <Button slot="extra" type="warning" @click="del" v-if="selectedItem !== null">删除</Button>

                <div style="min-height: 500px;">
                    <div class="selected-item" v-if="selectedItem">
                        <div class="panel panel--info">
                            <div class="panel__body">
                                <h3>{{selectedItem.name}}</h3>
                                <div v-for="p in selectedItem.props" :key="p.id">
                                    <p>{{p.name}}</p>
                                    <Input v-model="p.value" style="width: 300px" v-if="p.type !== 'required'"></Input>
                                    <i-switch v-model="p.value" v-else-if="p.type === 'required'"></i-switch>
                                </div>
                            </div>
                        </div>
                    </div>
                </div>
            </Card>
            </Col>
        </Row>
    </div>
</template>

<script>
    import nestlist from './nested-list.vue';
    import {XInput, Group} from 'vux'
    import components from '../../components/components'
    import * as _ from 'underscore'

    export default {
        data() {
            return {
                compList: components,
                selectedItem: null,
                disable: false,
                dropzones: {
                    'main': [
                        {
                            id: 11327897123991237,
                            index: 0,
                            name: '单行输入框',
                            ident: 'x-input',
                            type: 'input',
                            props: {
                                title: {
                                    type: 'title',
                                    name: '标题',
                                    value: '单行输入框'
                                },
                                placeholder: {
                                    type: 'placeholder',
                                    name: '提示文字',
                                    value: '提示文字'
                                },
                                required: {
                                    type: 'required',
                                    name: '是否必填',
                                    value: false
                                }
                            }
                        }
                    ]
                }
            };
        },
        methods: {
            copied (item) {
                item.index++;
            },
            del () {
                let aa = _.findIndex(this.dropzones.main, {
                    index: this.selectedItem.index
                })
                this.dropzones.main.splice(aa, 1)
                this.selectedItem = null
            },
            handleSelected(item) {
                this.selectedItem = item;
            },
            save () {
                if (this.dropzones.main.length === 0) {
                    this.$Notice.warning({
                        title: '表单为空',
                        duration: 2
                    });
                }
            }
        },
        components: {
            nestlist,
            XInput,
            Group
        }
    };
</script>
<style lang="less" type="text/less">
    @import '../../styles/_var';

    .comp_item {
        border: 1px dashed blueviolet;
        background: rgba(255, 255, 255, 0.1);
        margin: 0 15px 15px 0;
        width: 100%;
        height: 42px;
        float: left;
        font-size: 12px;
        line-height: 42px;
        text-align: center;
        cursor: move;
        position: relative;
        overflow: hidden;
    }

    .comp_chosen_item {
        border: 1px solid blueviolet;
    }

    .comp_item label {
        cursor: move;
    }

    .comp_item:hover {
        background: blanchedalmond;
    }

    .formcanvas {
        /*position: absolute;*/
        /*left: 385px;*/
        margin-top: 10px;
        height: 766px;
        width: 421px;
        background: url("https://img.alicdn.com/tfs/TB1nlugQFXXXXbuXpXXXXXXXXXX-910-1642.png") no-repeat top center;
        -webkit-background-size: 100% 100%;
    }

    .formcanvas-inner {
        position: absolute;
        overflow-x: hidden;
        overflow-y: auto;
        left: 38px;
        top: 60px;
        right: 38px;
        bottom: 0;
        max-height: 566px;
        background: #f0eff4;
    }

    .formcanvas-title {
        height: 90px;
        line-height: 90px;
        text-align: center;
        font-size: 20px;
        /*color: #fff;*/
        margin-top: -34px;
    }

    .formcanvas-body {
        overflow: hidden;
        padding-bottom: 30px;
    }

    .formcanvas-body.empty {
        /*background: url("//img.alicdn.com/tps/TB1FRPmHVXXXXb.XFXXXXXXXXXX-249-145.png") no-repeat center;*/
        min-height: 300px;
    }

    .vddl-placeholder {
        width: 100%;
        min-height: @item-height;
        line-height: @item-height;
        border-bottom: 1px solid #eee;
        padding: 0 15px;
        background: @placeholder-bg;
    }

    /* comp-div */
    .comp-div .button {
        border: 1px solid @base-color;
        text-align: center;
        height: 40px;
        border-radius: 4px;
        line-height: 40px;
        background-color: @base-color;
        color: #fff;
        cursor: move;
        font-size: 14px;
    }

    .comp-div .vddl-dragging-source {
        display: block;
    }

    .disable-element .button {
        background-color: @red;
        border: 1px solid @red;
        cursor: pointer;
    }

    .selected {
        background: red;
    }

    .selected-item .panel__body {
        line-height: 40px;
    }

    .ashcan {
        .ashcan-logo {
            display: block;
            width: 40px;
            margin: 10px auto;
        }
        .vddl-placeholder {
            display: none;
        }
    }

    .vddl-list {
        padding-left: 0px;
        min-height: 200px;
    }

    .vddl-list, .vddl-draggable {
        position: relative;
    }
</style>
<template lang="html">
    <vddl-draggable class="panel__body--item"
                    :draggable="item"
                    :index="index"
                    :disable-if="disable"
                    :selected="selectedEvent"
                    :wrapper="list"
                    effect-allowed='move'
                    v-bind:class="{'selected': selectedItem === item}">
        <group>
            <x-input :title="item.props.title.value"></x-input>
        </group>
    </vddl-draggable>
</template>

<script>
    import expand from './expand';
    import {Group, XInput, XTextarea, Cell} from 'vux';
    export default {
        name: 'list',
        props: ['item', 'list', 'index', 'selected', 'dragend', 'selectedItem', 'disable'],
        methods: {
            selectedEvent (item) {
                if (typeof (this.selected) === 'function') {
                    this.selected(item);
                }
            }
        },
        components: {
            Group,
            XInput,
            Cell,
            expand,
            XTextarea
        }
    };
</script>

<style lang="css">
</style>
hejianxian commented 7 years ago

看不出什么问题。你再看看,有没有地方会影响到元素的排序的。

lmasdata commented 7 years ago

请问,拖拽排序的逻辑是把拖拽的元素复制一份,然后把原来所在的位置的元素删掉么?

hejianxian commented 7 years ago

简单地说,在拖起的时候,会把draggable的数据放到 dataTransfer 里,在放下到list时,会触发drop回调,如果没设置这个回调,内部会在list加入这个draggable数据。接着,原来的那个被拖起的数据也会被删除。