VueJS 3 — Как использовать перетаскиваемый вложенный элемент, но не допускать дублирования элемента

#vue.js #vuedraggable

Вопрос:

Я пытаюсь создать простой конструктор опросов с возможностью перетаскивания Vue3 и Vue. Все идет хорошо, пока я не попытаюсь создать вопрос с множественным выбором. Этот тип вопросов имеет свой собственный сортируемый список возможных ответов. Когда я добавляю еще один вопрос с множественным выбором, он затем извлекается из того же списка, что имеет смысл, но я попытался использовать v-if для проверки идентификатора родителей на соответствие идентификатору выбора..

В принципе, если я добавлю новый выбор, он добавится ко всем вопросам с множественным выбором, что имеет смысл, но как мне сохранить его в текущем пункте, в котором я нахожусь?

Есть какие-нибудь идеи? Я знаю, что код-это беспорядок, он будет переработан, как только он заработает.

 <template>
  <div class="p-4">
    <div class="container mx-auto rounded-md grid grid-cols-12 gap-4 h-full">

      <div class="col-span-4 p-3 flex flex-col min-h-screen bg-gray-200 shadow-md rounded-md">
        <div class="text-lg font-bold w-full bg-blue-600 text-white rounded-md p-2 mb-4">Builder your survey</div>
        <div class="sticky top-4">
          <div class="text-lg font-bold">Components</div>

          <draggable
            class="p-2 rounded-md"
            :list="componentsList"
            :group="{ name: 'questions', pull: 'clone', put: false, sort: false }"
            :clone="cloneItem"
            sort: false
            @change="log"
            item-key="id"
          >
            <template #item="{ element }">
              <div
                class="bg-gray-300 p-4 rounded-md mt-2 shadow-sm hover:shadow-md cursor-pointer border border-blue-800 border-dashed"
              >
                {{ element.name }}
              </div>
            </template>
          </draggable>
 
       
        </div>
      </div>

      
      <div class="col-span-8 flex p-3 flex-col bg-white shadow-md rounded-md">


        <div class="text-lg font-bold pt-4">Survey</div>
        <draggable
          class="w-full h-full border border-blue-400 rounded-md p-2 flex flex-col flex-1"
          :list="questionsList"
          group="questions"
          @change="log"
          handle=".handle"
          itemKey="name   index"
        >
          <template #item="{ element, index }">
            
            <div>
              <div v-if="element.name == 'Single Line of Text'" class="bg-gray-300 p-2 rounded-md mt-2 mb-2 shadow-lg hover:shadow-md">
                
              <div class="w-full text-sm text-left">{{ element.name }} {{element.id}}</div>
                <div class="flex justify-between items-center p-2 bg-gray-200 rounded-md mb-4">
                  <div class="w-10 font-bold hidden">Q{{ index   1 }}</div>
                  <div class="w-full pr-4">
                    <input
                      type="text"
                      class="w-full p-2 bg-transparent flex-grow"
                      placeholder="Question title here..."
                      v-model="element.text"
                    />
                  </div>
                  <div class="flex ">
                    <div class="cursor-pointer">
                      <i class="handle las la-arrows-alt la-2x mr-2"></i>
                    </div>
                    <div @click="remove(index)">
                      <i class="las la-trash-alt text-red-800 la-2x cursor-pointer"></i>
                    </div>
                  </div>
                </div>
                <div>
                  <input
                    type="text"
                    class="w-full p-2 rounded-md border border-gray-400"
                    placeholder="User response will go here"
                  />
                </div>
                <div class="text-left flex justify-between items-center p-2 bg-gray-200 rounded-md mt-2">
                  <div class="flex items-center">
                    <div><input type="checkbox" class="mr-2" /></div>
                    <div>Required?</div>
                  </div>
                </div>
              </div>



<!-- START problem area -->


              <div v-else-if="element.name == 'Multiple Choice'" class="bg-gray-300 p-2 rounded-md mt-2 mb-2 shadow-lg hover:shadow-md">
                <div class="w-full text-sm text-left">{{ element.name }} {{element.id}}</div>

                <div class="flex justify-between items-center p-2 bg-gray-200 rounded-md mb-4">
                  <div class="w-10 font-bold hidden">Q{{ index   1 }}</div>
                  <div class="w-full pr-4">
                    <input
                      type="text"
                      class="w-full p-2 bg-transparent flex-grow"
                      placeholder="Question title here..."
                      v-model="element.text"
                    />
                  </div>
                  <div class="flex ">
                    <div class="cursor-pointer">
                      <i class="handle las la-arrows-alt la-2x mr-2"></i>
                    </div>
                    <div @click="remove(index)">
                      <i class="las la-trash-alt text-red-800 la-2x cursor-pointer"></i>
                    </div>
                  </div>
                </div>

                <div class="flex items-center ">
                  <draggable
                      class="p-2 rounded-md w-full"
                      :list="multipleChoiceList"
                      :group="{ name: 'choice', pull: false, put: false, sort: true }"
                      sort: true
                      handle=".handle"
                      @change="log"
                      item-key="question"
                    >
              
                      <template #item="{ element }">
                        <div 
                        class="bg-blue-100 p-4 flex items-center justify-start rounded-md mt-2 shadow-sm hover:shadow-md cursor-pointer w-full"
                       
                        >
                          <div class="flex items-center flex-grow"
                          >
                              <input type="checkbox" class="w-6 h-6">
                              <input
                              type="text"
                              class="p-2 bg-transparent flex-grow"
                              placeholder="Add choice here"
                              v-model="element.text"
                            />
                          </div>

                          <div class="flex ">
                            <div class="cursor-pointer">
                              <i class="handle las la-arrows-alt la-1x mr-2"></i>
                            </div>
                            <div @click="remove(index)">
                              <i class="las la-trash-alt text-red-800 la-1x cursor-pointer"></i>
                            </div>
                          </div>

                        </div>

                      </template>

                      <template #footer>
                        <div>
                          <button class="p-2 bg-blue-300 mt-2 rounded-md" @click="addChoice(element.id)">Add</button>
                        </div>
                      </template>
                  </draggable>
                    
                </div>

                <div class="text-left flex justify-between items-center p-2 bg-gray-200 rounded-md mt-2">
                  <div class="flex items-center">
                    <div><input type="checkbox" class="mr-2" /></div>
                    <div>Required?</div>
                  </div>
                </div>
              </div>

<!-- END problem area -->






              <div v-else-if="element.name == 'Open Ended'" class="bg-gray-300 p-2 rounded-md mt-2 mb-2 shadow-lg hover:shadow-md">
                <div class="w-full text-sm text-left">{{ element.name }} {{element.id}}</div>

                <div class="flex justify-between items-center p-2 bg-gray-200 rounded-md mb-4">
                <div class="w-10 font-bold hidden">Q{{ index   1 }}</div>
                <div class="w-full pr-4">
                  <input
                    type="text"
                    class="w-full p-2 bg-transparent flex-grow"
                    placeholder="Question title here..."
                    v-model="element.text"
                  />
                </div>
                  <div class="flex ">
                    <div class="cursor-pointer">
                      <i class="handle las la-arrows-alt la-2x mr-2"></i>
                    </div>
                    <div @click="remove(index)">
                      <i class="las la-trash-alt text-red-800 la-2x cursor-pointer"></i>
                    </div>
                  </div>
                </div>
                <div>
                  <textarea
                    class="h-32 w-full w-full p-2 rounded-md border border-gray-400"
                  ></textarea>
                </div>
                <div class="flex items-center">
                  <div>Max Length</div>
                  <div>
                    <input
                      type="number"
                      class="mr-2 w-20 border border-gray-400 p-2 rounded-md ml-2"
                    />
                  </div>
                </div>
                <div class="text-left flex justify-between items-center p-2 bg-gray-200 rounded-md mt-2">
                <div class="flex items-center">
                  <div><input type="checkbox" class="mr-2" /></div>
                  <div>Required?</div>
                </div>
              </div>
            </div>


            <div v-else-if="element.name == 'Divider'">
              <div class="flex items-center">
                <div class="flex-grow border-t border-black  mx-4"> </div>
                <div class="flex ">
                  <div class="cursor-pointer">
                  <i class="handle las la-arrows-alt la-2x mr-2"></i>
                  </div>
                  <div @click="remove(index)">
                  <i class="las la-trash-alt text-red-800 la-2x cursor-pointer"></i>
                  </div>
                </div>
              </div>

            </div>

          </div>

           

          </template>
        </draggable>
      </div>
    </div>
  </div>
 
</template>   
 

См. Метод клонирования, который присваивает случайное число в качестве идентификатора

 <script>
import draggable from "vuedraggable";

export default {
  name: "Survey",
  components: {
    draggable,
  },
  data() {
    return {
      drag: false,
      componentsList: [
        { name: "Single Line of Text", type: "question", text: "", id: 1 },
        { name: "Multiple Choice", type: "question", text: "", id: 2 },
        { name: "Matrix", type: "question", text: "", id: 3 },
        { name: "Open Ended", type: "question", text: "", id: 4 },
        { name: "Divider", type: "component", id: 9 },
      ],
      questionsList: [],
      multipleChoiceList: [
        {text: "text A", type:"choice", question:"32"},
        {text: "text B", type:"choice", question:"1"},
        {text: "text A", type:"choice", question:"2"}      ]
    };
  },
  methods: {
    onEnd: function(evt) {
      console.log(evt);
    },
    log: function(evt) {
      console.log(evt);
    },
    addChoice(id) {

      this.multipleChoiceList.push({ text: "Choice "   id, type: "choice", question:id  });
      console.log(this.multipleChoiceList);

    },
    remove(index) {
      this.questionsList.splice(index, 1);
    },
    cloneItem({ id, name, type }) {
      return {
        name: name,
        id: Math.ceil(Math.random()*100),
        text: "",
        type: type,
      };
    }
  },
  mounted() {
    // console.log("mounted");
  },
};
</script>