1. 程式人生 > 程式設計 >vue 實現拖拽動態生成元件的需求

vue 實現拖拽動態生成元件的需求

產品需求

開完產品需求會議,遇到了一個需求,首先頁面分成兩欄佈局,左側展示資料元件,支援拖拽排序,點選按鈕清除元件。右側支援將元件的縮圖拖拽至左側生成一個新的元件。

思路

對於動態生成元件來說每一次都要是生成全新的一個元件,那麼就可以把 元件放進函式當中 return。在jsX中呼叫函式,每次呼叫函式都會返回一個全新的元件。這對React來說非常簡單,但是對於vue來說,直接將元件返回是不可能的。儘管這個 rethttp://www.cppcns.comurn 寫法不適合Vue,但是我們不可否認,思路是非常正確的,所以我們應該考慮一個別的寫法。至於動態的生成元件,我們必須以資料來驅動元件的生成。對於拖拽元件的排序,直接使用拖拽庫就OK了!!

面臨的問題

  1. 拖拽庫的選擇
  2. 如何生成元件
  3. 以資料驅動動態生成元件

拖拽庫的選擇

拖拽庫在這裡我選擇的是專案中存在的一個拖拽庫 Vue.Draggable 點這裡連結檢視 Start 14.9K 蠻不錯的。如果你們的Vue專案中沒有用到這個拖拽庫,你們可以自行參考本片文章的設計思路。

如何生成元件

在這裡我使用的是 Vue.extend() 不清楚如何使用的小夥伴請在官方文件中檢視過後再來學習這篇文章 Vue.extend 。 接下來我們建立一個js檔案,用來書寫建立元件的程式碼。

生成元件

/* generateComponents.js 檔名 */

import Vue from "vue";

// 想要動態生成的元件,先引入這個檔案。
import components1 from "./components/TestCom1.vue";
import components2 from "./components/TestCom2.vue";

// 將元件的名稱和元件做一個對應Map
const comMap = {
  components1,components2,};

// 接收生成元件需要的元件名稱,和想要傳遞給元件的
// props,和 事件
const ReturnNewCom = function ({ props,on }) {
  const {
    comItem: { name },} = props;
  const newComponent = Vue.extend({
    render(createElement) {
      // 使用傳進來的元件name來決定渲染哪一個元件。
      return createElement(comMap[name],{
        props,on,});
    },});
  return new newComponent();
};

export default ReturnNewCom;

元件

在這裡我們書寫兩個元件,用來演示這個Demo,分別為components1.vue,compo程式設計客棧nents2.vue。

/*components1.vue*/
<template>
  <dhttp://www.cppcns.comiv class="widget-wrapper">
    <header class="header">{{ comDetail.name }}--{{ comDetail.id }}</header>
    <h1>查詢條件:{{ queryObj }}</h1>
    <button @click="handleDelete">清除</button>
  </div>
</template>
<script>
export default {
  data() {
    return {
      comDetail: this.comItem,_queryObj: this.queryObj,};
  },props: {
    comItem: {
      type: Object,default() {
        return {
          id: 0,name: "",};
      },},queryObj: {
      // 可以接收父元件傳遞的晒選條件,必須是Object
      type: Object,default() {
        // 定義預設的查詢條件。
        return {
          num: 0,watch: {
    comItem(val) {
      this.comDetail = val;
      return val;
    },queryObj(val) {
      this._queryObj = val;
      return val;
    },created() {
    console.log("data -> this.comItem",this.comItem);
  },methods: {
    handleDelete() {
      // 刪除元件方法
      this.$el.remove();
      // 呼叫父元件的函式。修改父元件中的 leftComList 陣列的資料。
      this.$emit("handleDelete",this.comDetail);
    },};
</script>
<style scoped>
.widget-wrapper {
  background: #ff7b7b;
  border-radius: 12px;
  overflow: hidden;
  width: 200px;
}
.header {
  height: 50px;
  padding: 0 15px;
}
</style>

其實components2.vue檔案中的程式碼和components1.vue檔案的程式碼類似,唯一不同的地方就是背景顏色不一樣。

以資料驅動動態元件的生成

接下來就得使用Vue.Draggable 這個拖拽庫進行拖拽和資料的修改。 我們可以直接在App.vue檔案中直接書寫。

/* App.vue */
<template>
  <div class="dragCom">
    <h1>{{ leftComList }}</h1>
    <button @click="queryObj.num++">改變查詢條件</button>
    <div class="body">
      <div class="left">程式設計客棧;
        <draggable class="left" :list="leftComList" :group="'people'">
          <div
            ref="comBody"
            v-for="({ name,id },index) in leftComList"
            :key="id"
            class="comCard"
          >
            <!-- 迴圈 leftComList 陣列,利用資料來渲染元件, 
            將動態生成的陣列新增到這個DOM元素當中。 -->
            {{
              handleAddCom({
                props: { comItem: { name,queryObj },index,})
            }}
          </div>
        </draggable>
      </div>
      <div class="right">
        <draggable
          class="dragArea"
          :list="rightComList"
          :group="{ name: 'people',pull: 'clone',put: false }"
          :clone="handleCloneDog"
        >
          <div class="card" v-for="element in rightComList" :key="element.id">
            {{ element.name }}
          </div>
          <!-- 右側的 卡片 資料, rightComList 陣列物件中的name就對應了generateComponents.js
          中的ComMap中的屬性 -->
        </draggable>
      </div>
    </div>
  </div>
</template>

<script>
import draggable from "vuedraggable";
import CreateCom from "./generateComponents";
export default {
  components: {
    draggable,data() {
    return {
      rightComList: [
        {
          id: Math.random(),name: "components1",{
          id: Math.random(),name: "components2",],leftComList: [],// 儲存驅動動態生成元件的資料。
      comMap: new Map(),// 主要的作用就是用來記錄 
      // 元件有沒有渲染到 class="comCard" 這個DOM中,// 如果渲染了就不能再往進新增子元素了。
      queryObj: {
        // 主要的作用就是向子元件傳遞查詢條件
        num: 0,beforeDestroy() {
    // 清除 記錄 的資料
    this.comMap.clear();
  },methods: {
    handleAddCom({ index,on = {},props = { comItem: { name: "",id: 0 } } }) {
      const {
        comItem: { id },} = props;
      this.$nextTick(() => {
        // 獲取該節點的子節點的長度
        const childNodesLength = this.$refs.comBody[index].childNodes.length;
        // 獲取comBody 這個DOM 陣列的長度
        const comLine = this.$refs.comBody.length;
        if (!this.comMap.get(id)) {
          // 如果沒有渲染過元件

          // 1. 呼叫 CreateCom 方法 建立元件。 並傳遞 props 和 事件
          const com = CreateCom({
            props,on: {
              handleDelete: this.handleDeleteCom,...on,});
          // 2. 生成元件
          com.$mount();
          if (childNodesLength === 2) {
 UDuOKg           // 如果要新增到兩個元件中間。那麼就將新生成的元件DOM位置進行修改放到中間。
            // 將最後的元件DOM新增到正確的位置
            this.$refs.comBody.splice(
              index,this.$refs.comBody[comLine - 1]
            );
          }
          // 3. 將生成的元件新增到改DOM中。
          this.$refs.comBody[index].appendChild(com.$el);
          // 4. 記錄該元件實現了渲染。
          this.comMap.set(id,true);
        } else {
          // 該位置的元件已經渲染,不需要再次渲染直接返回
          return;
        }
      });
    },handleDeleteCom({ id }) {
      // 傳遞給子元件刪除的方法,根據元件的id來刪除資料
      const index = this.leftComList.findIndex((item) => item.id === id);
      if (~index) {
        // 如果存在這個id的元件,就刪除
        this.leftComList.splice(index,1);
      }
    },handleCloneDog(item) {
      // 給 leftComList 陣列新增資料
      return {
        ...item,id: Math.random(),};
    },};
</script>

<style>
.dragCom {
  font-family: Avenir,Helvetica,Arial,sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}
.body {
  width: 100%;
  height: 800px;
  display: flex;
  justify-content: space-between;
}
.left {
  flex: 1;
  height: 800px;
  border: 1px solid pink;
}
.right {
  width: 20%;
  height: 800px;
}
.card {
  height: 50px;
  background-color: #40cec7;
  margin: 12px 0;
  font-size: 12px;
  line-height: 50px;
  cursor: pointer;
}
.comCard {
  margin: 12px;
  display: inline-block;
}
</style>


這樣就實現了動態的元件渲染和拖拽排序。

效果

vue 實現拖拽動態生成元件的需求

原始碼

想要嘗試的同學可以自行下載本文的程式碼原始碼github

以上就是vue 實現拖拽動態生成元件的需求的詳細內容,更多關於vue拖拽動態生成元件的資料請關注我們其它相關文章!