带有子节点的树状表的父节点拖动排序#Vue3才发现对#Sortable插件一无所知

带有子节点的树状表的父节点拖动排序#Vue3#Sortable插件

使用Sortable插件这里要保证获取到的是父节点的下标,属性newDraggableIndex获取到的就是只有父节点的下标。设置子节点不能被拖动,最后在逐个调用接口进行数据库中顺序的更新。

在这里插入图片描述

<template>
 <div class="app-container" v-loading="sortLoading">
  <el-table v-if="refreshTable"
      class="draggable"
      v-loading="loading"
      border
      :data="menuList"
      :row-key="getRowKey"
      :default-expand-all="isExpandAll"
      :tree-props="{ children: 'child', hasChildren: 'hasChildren' }">
    <el-table-column prop="date" label="Date" width="180" />
    <el-table-column prop="name" label="Name" width="180" />
    <el-table-column prop="address" label="Address" />
  </el-table>
 </div>
</template>

<script lang="ts" setup>
import Sortable from "sortablejs";

function getRowKey(row: any) {
  return row.id + row.type + row.key;
}

// 行拖拽
const rowDrag = function () {
  // 要拖拽元素的父容器
  const tbody = document.querySelector(
    ".draggable .el-table__body-wrapper tbody"
  );
  if (!tbody) return;
  Sortable.create(tbody as HTMLElement, {
    //  可被拖拽的子元素,设置只有父节点才能被拖动
    draggable: ".draggable .el-table__row.el-table__row--level-0",
    //设置只有子节点才能拖动,如果要设置父节点下的孩子节点它们兄弟之间拖动,拿到孩子节点的旧新坐标,然后根据旧新坐标判断是否属于同一个父节点,如果不属于就不执行拖动操作
    //draggable: ".draggable .el-table__row.el-table__row--level-1",
    onEnd(event) {
      console.log("event", event);
      //如果要实现子节点兄弟之间拖动,在这里获职旧坐标和新坐标,根据是否属于同一父结点决定是否执行一下换作
      if (
        event.oldDraggableIndex !== undefined &&
        event.newDraggableIndex !== undefined
      ) {
        const temp = [...menuList.value];
        const currRow = temp.splice(event.oldDraggableIndex, 1)[0];
        temp.splice(event.newDraggableIndex, 0, currRow);
        console.log("temp", temp);
        menuList.value = temp.map((item, i: number) => {
          console.log("item", item);
          const parentKey = `parent_${item.id}_${item.type}_${Date.now()}_${i}`;
          const child = item.child.map((child: any, index: number) => {
            const childrenKey = `children_${item.id}_${
              item.type
            }_${Date.now()}_${i}`;
            return {
              ...child,
              key: childrenKey,
            };
          });
          return { ...item, child, key: parentKey };
        });
        console.log("menuList", menuList.value);
        asyncSort();
      }
    },
  });
};

const sortLoading = ref(false);
// 更新菜单排序
function asyncSort() {
  sortLoading.value = true;
  const list = [...menuList.value];
  const apiList = list.map((item, i) => {
    return updateMenu({ ...item, sort: i + 1, pid: "" });
  });
  Promise.all(apiList)
    .then((res) => {
      handleQuery();
      console.log("更新排序成功");
    })
    .catch(() => {
      console.log("部分更新错误");
    })
    .finally(() => (sortLoading.value = false));
}

const tableData = [
  {         "id": "1711314432585502720",
            "iD_Module": "1685955461712580611",
            "code": "fixture",
            "name": "治具管理",
            "component": "/",
            "path": "/fixture",
            "type": 1,
            "isOutSide": false,
            "redirect": "/",
            "alwaysShow": "Y",
            "sort": 1,
            "icon": "fixture",
            "enable": "Y",
            "showEnable": "Y",
            "pid": null,
            "createTime": "2023-10-09 17:35:30",
            "updateTime": "2024-07-09 16:37:30",
            "child": [
                {
                    "id": "1711314714514034688",
                    "iD_Module": "1685955461712580611",
                    "code": "fixture_profile",
                    "name": "治具台账",
                    "component": "/",
                    "path": "profile",
                    "type": 2,
                    "isOutSide": false,
                    "redirect": "/",
                    "alwaysShow": "Y",
                    "sort": 1,
                    "icon": "minus",
                    "enable": "Y",
                    "showEnable": "Y",
                    "pid": "1711314432585502720",
                    "createTime": "2023-10-09 17:36:37",
                    "updateTime": "2023-10-10 19:20:18",
                    "child": null
                },
                {
                    "id": "1711314829744148480",
                    "iD_Module": "1685955461712580611",
                    "code": "fixture_inspect",
                    "name": "治具点检",
                    "component": "/",
                    "path": "inspect",
                    "type": 2,
                    "isOutSide": false,
                    "redirect": "/",
                    "alwaysShow": "Y",
                    "sort": 2,
                    "icon": "minus",
                    "enable": "Y",
                    "showEnable": "Y",
                    "pid": "1711314432585502720",
                    "createTime": "2023-10-09 17:37:04",
                    "updateTime": "2023-10-10 19:20:27",
                    "child": null
                }
            ]
        },
        {
            "id": "1724976315569934336",
            "iD_Module": "1724975614743678976",
            "code": "proposal",
            "name": "提案改善",
            "component": "/",
            "path": "/proposal",
            "type": 1,
            "isOutSide": false,
            "redirect": "/",
            "alwaysShow": "Y",
            "sort": 2,
            "icon": "proposal",
            "enable": "Y",
            "showEnable": "Y",
            "pid": null,
            "createTime": "2023-11-16 10:22:56",
            "updateTime": "2024-07-09 16:37:30",
            "child": null,
        },
]
</script>

原来我对sortable插件一无所知!!!总想着怎么把拖动的新旧下标算出来,真是没那金刚钻硬揽那瓷器活~

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Vue 递归菜单中设置选中状态,可以通过以下步骤实现: 1. 在菜单数据中添加 `selected` 属性,用来保存该节点是否被选中的状态,初始值为 `false`。 2. 在菜单项的模板中,使用 `v-bind:class` 绑定一个计算属性来设置选中状态的样式。计算属性根据 `selected` 属性的值返回不同的类名。 3. 在点击菜单项时,通过 `v-on:click` 绑定一个方法,在该方法中设置 `selected` 属性的值为 `true`。 4. 在计算属性中,递归遍历子节点,如果子节点中有任意一个节点被选中,则将父节点的 `selected` 属性设置为 `true`。 下面是一个示例代码: ```html <template> <ul> <li v-for="item in menu" :key="item.id" :class="{ 'selected': item.selected }" @click="selectItem(item)"> {{ item.name }} <menu v-if="item.children" :menu="item.children"></menu> li> </ul> </template> <script> export default { props: ['menu'], methods: { selectItem(item) { item.selected = true; } }, computed: { hasSelectedChild() { return this.menu.some(item => { if (item.selected || (item.children && this.hasSelectedChild(item.children))) { item.selected = true; return true; } return false; }); } } }; </script> <style> .selected { background-color: #ccc; } </style> ``` 在这个示例中,每个菜单项都有一个 `selected` 属性,用于保存该节点是否被选中的状态。在菜单项的模板中,使用 `v-bind:class` 绑定一个计算属性 `{'selected': item.selected}` 来设置选中状态的样式。 在点击菜单项时,通过 `@click` 绑定的 `selectItem` 方法,将该节点的 `selected` 属性设置为 `true`,示该节点被选中。 在计算属性 `hasSelectedChild` 中,递归遍历子节点,如果子节点中有任意一个节点被选中,则将父节点的 `selected` 属性设置为 `true`。这样,当子节点被选中时,父节点也会有选中的状态。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值