ツリーのドラッグ可能な並べ替え構成コンポーネント

効果

ここに画像の説明を挿入

使用シーン

vue2 でのカスタム テーブル ヘッダー構成: 列の並べ替え、表示/非表示など。変更された構成にテーブルが応答できるように、テーブル ヘッダーが構成アイテムとして読み込まれていることを確認します。

一連の考え

1. 関数を使用してテーブルをロードし(質問がある場合は、プライベート メッセージを送信してください)、render次のような構成でcolumnsテーブル ヘッダーを構成します。

columns: [
          {
    
     label: '姓名', prop: 'name', width: '160', fixed: true },
          {
    
     label: '性别', prop: 'sex', align: 'center', width: '160', fixed: false },
          {
    
     label: '证件类别', prop: 'cardType', align: 'center', width: '160', fixed: false },
          {
    
     label: '证件号', prop: 'idCard', align: 'center', width: '260', fixed: false },
          {
    
     label: '手机号', prop: 'mobile', align: 'center', width: '160', fixed: false },
          {
    
     label: '标签', prop: 'tag', align: 'center', width: '160', fixed: false },
          {
    
     label: '操作', prop: 'manage', align: 'center', disControl: true },
]

2. element-ui のel-dropdownコンポーネント

<el-dropdown trigger="click" :hide-on-click="false">
      <span class="el-icon-setting" style="font-size: 25px"></span>
      <template slot="dropdown">
        <el-dropdown-menu>
          <JSelectionItem :arr.sync="rows" @item-click="itemClickHandle">
            <template v-slot:default="{item,level}">
              <div class="item-custom">
                {
    
    {
    
    item.id}}
              </div>
            </template>
            <template v-slot:tool="{item,level}">
              <span class="el-icon-check"
                    v-if="item.visible"
                    style="size: 20px;margin-top: 5px">
              </span>
            </template>
          </JSelectionItem>
        </el-dropdown-menu>
      </template>
    </el-dropdown>

3.vue.draggableコンポーネントを使用して、ドラッグ アンド ドロップ効果を完成させます. 特定のアプリケーションについては、ドラッグ可能を参照してください 4. ツリー構造は、再帰的にロードする
必要があることがよくあります.レンダリングを使用するプロセスにバンプがあるため、構成コンポーネント、アニメーションのドラッグ アンド ドロップの失敗の問題を解決する方法はありません。

コード

ツリー構成アイテムを使用するコンポーネント

<template>
  <div>
    <el-dropdown trigger="click" :hide-on-click="false">
      <span class="el-icon-setting" style="font-size: 25px"></span>
      <template slot="dropdown">
        <el-dropdown-menu>
          <JSelectionItem :arr.sync="rows" @item-click="itemClickHandle">
            <template v-slot:default="{item,level}">
              <div class="item-custom">
                {
    
    {
    
    item.id}}
              </div>
            </template>
            <template v-slot:tool="{item,level}">
              <span class="el-icon-check"
                    v-if="item.visible"
                    style="size: 20px;margin-top: 5px">
              </span>
            </template>
          </JSelectionItem>
        </el-dropdown-menu>
      </template>
    </el-dropdown>

  </div>
</template>

<script>
import JSelectionItem from "./JSelectionItem";

export default {
    
    
  name: "JHeadManage",
  components: {
    
      JSelectionItem },
  data() {
    
    
    return {
    
    
      rows: []
    }
  },
  created() {
    
    
    this.getResource()
  },
  methods: {
    
    
    itemClickHandle(item) {
    
    
      item.visible = !item.visible
      console.log(item)
    },
    getResource() {
    
    
      const vm = this
      //测试用数据只有一个根节点 "0" 方便构建测试用数据树
      const data = [
        {
    
     id: '1', parentId: '0' },
        {
    
     id: '2', parentId: '0' },
        {
    
     id: '2-0', parentId: '2' },
        {
    
     id: '1-0', parentId: '1' },
        {
    
     id: '1-1', parentId: '1' },
        {
    
     id: '1-1-0', parentId: '1-1' },
        {
    
     id: '1-1-0-0', parentId: '1-1-0' },
        {
    
     id: '1-1-0-1', parentId: '1-1-0' },
        {
    
     id: '1-2', parentId: '1' },
        {
    
     id: '1-2-0', parentId: '1-2' },
        {
    
     id: '1-2-1', parentId: '1-2' },
      ]
      //1) 简单处理数据用于自定义渲染; checked:  indeterminate:
      data.forEach((d, index) => {
    
    
        d.visible = true //是否选中
        d.isIndeterminate = false//是否是半选状态
        d.a = 'a' + index
      })
      vm.rows = vm.makeTree(data, 'id', 'parentId', '0')
      // vm.rows = data
    },
    /**
     * 构建树,与复选逻辑无关
     * @param data
     * @param idMark
     * @param pIdMark
     * @param rootId
     * @returns {*}
     */
    makeTree(data, idMark, pIdMark, rootId) {
    
    
      //转化为字典,id为键值,并添加根节点对象
      let nodeDict = {
    
    };
      (nodeDict[rootId] = {
    
     children: [] })[idMark] = rootId;
      data.forEach(n => (nodeDict[n[idMark]] = n).children = []);
      data.forEach(d => nodeDict[d[pIdMark]]?.children?.push(d))
      return nodeDict[rootId].children;
    }
  }
}
</script>

<style scoped>

  .item-custom {
    
    
    display: inline-flex;
    padding: 10px 0;
    margin-right: auto;
  }

</style>

ツリー構成項目コンポーネント

2 つのスロットが定義されています。1 つはメイン コンテンツを表示するために使用され、もう 1 つはオプションに操作を配置するために使用されます。

<template>
  <ul class="list" :style="{paddingLeft:level==0?'10px':'0'}">
    <draggable v-model="locArr"
               :animation="100"
               handle=".tip"
               @start="onStart"
               @end="onEnd">
      <transition-group>
        <li class="item" v-for="(c,i) in locArr" :key="i">
          <div class="item-inner" :style="{paddingLeft:level*20+'px'}" @click.stop="itemClick(c)">
            <span class="tip el-icon-more-outline"></span>
            <slot v-bind:item="c" v-bind:level="level"></slot>
            <div class="item-tool">
              <slot name="tool" v-bind:item="c" v-bind:level="level"></slot>
            </div>
          </div>
          <template v-if="c.children&&c.children.length>0">
            <JSelectionItem :arr.sync="c.children" :level="level+1" @item-click="itemClick">
              <template v-slot:default="{item,level}">
                <slot v-bind:item="item" v-bind:level="level"></slot>
              </template>
              <template v-slot:tool="{item,level}">
                <slot name="tool" v-bind:item="item" v-bind:level="level"></slot>
              </template>
            </JSelectionItem>
          </template>
        </li>
      </transition-group>
    </draggable>
  </ul>
</template>

<script>
import draggable from 'vuedraggable'
import JSelectionItem from "@/views/demo/JSelectionItem";

export default {
    
    
  name: "JSelectionItem",
  components: {
    
    
    JSelectionItem, draggable
  },
  data() {
    
    
    return {
    
    }
  },
  computed: {
    
    
    locArr: {
    
    
      get() {
    
    
        return this.arr
      },
      set(val) {
    
    
        this.$emit('update:arr', val)
      }
    },
  },
  props: {
    
    
    arr: {
    
    
      type: Array,
      default: () => []
    },
    level: {
    
    
      type: Number,
      default: 0,
    }
  },
  methods: {
    
    
    onStart() {
    
    

    },
    onEnd() {
    
    

    },
    itemClick(item) {
    
    
      this.$emit('item-click', item)
    }
  }
}
</script>

<style scoped>
  .list {
    
    
    width: 100%;
    margin: 0;
    padding: 0;
  }

  .item {
    
    
    padding: 0;
    width: 100%;
    display: flex;
    flex-wrap: wrap;
    align-items: center;
  }

  .item-inner {
    
    
    display: flex;
    width: 100%;
    position: relative;
    justify-content: space-between;
    border-bottom: 1px solid #e5e5e5;
    user-select: none;
  }

  .item-inner:hover {
    
    
    /*font-weight: bold;*/
  }

  .item-tool {
    
    
    display: flex;
    position: absolute;
    align-items: center;
    right: 10px;
  }

  .tip {
    
    
    transform: rotate(-90deg);
    cursor: move;
    font-size: 20px;
    user-select: none;
  }

</style>

おすすめ

転載: blog.csdn.net/weixin_43954962/article/details/121049536