使用SortableJS遇到的问题

要实现如下的拖拽需求

可以左右拖动,右侧表格只可以拖拽教学内容列,拖拽过程中样式上展示也是只有教学内容列

1.使用element-plus结合sortablejs实现table行拖拽

(1)使用el-table单元格合并可以实现如上静态样式,实际上拖拽中的样式不能满足,且拖拽后由于有单元格合并导致table表格的错乱。

(2)sortable和element-plus 结合使用,调试多次发现只支持拖拽一整行内容(导致拖拽过程中和拖拽完成后样式不对,只可以重新刷新页面才可以),暂时未找到解决办法

2.使用原生table/div 构造

做的过程中发现sortablejs 拖拽只支持一个遍历下的内容拖拽,可以解决element-plus table 只能拖拽一整行问题

html代码

       <div style="display: flex; background-color: #f5f7fa">
            <div style="width: 10%">
              <div class="grid-content ep-bg-purple">序号</div>
            </div>
            <div style="width: 10%">
              <div class="grid-content ep-bg-purple-light">日期</div>
            </div>
            <div style="width: 10%">
              <div class="grid-content ep-bg-purple">时间段</div>
            </div>
            <div style="width: 40%">
              <div class="grid-content ep-bg-purple-light">教学内容</div>
            </div>
            <div style="width: 20%">
              <div class="grid-content ep-bg-purple">主持/主讲</div>
            </div>
            <div style="width: 10%">
              <div class="grid-content ep-bg-purple-light">职务职级</div>
            </div>
          </div>
          <div style="display: flex">
            <div class="sigle-col" style="width: 10%">
              <div
                v-for="(item, index) in tableData"
                :key="item.id"
                class="grid-content ep-bg-purple"
              >
                {{ index + 1 }}
              </div>
            </div>
            <div class="sigle-col" style="width: 10%">
              <div
                v-for="(item, index) in tableData"
                :key="item.id"
                class="grid-content ep-bg-purple-light"
              >
                <span>{{ item.courseDate }}</span>
              </div>
            </div>
            <div class="sigle-col" style="width: 10%">
              <div
                v-for="(item, index) in tableData"
                :key="item.id"
                class="grid-content ep-bg-purple"
              >
                {{ item.courseTime === '1' ? '上午' : item.courseTime === '2' ? '下午' : '晚上' }}
              </div>
            </div>
            <div id="courseName" class="sigle-col" style="width: 40%">
              <div
                v-for="(item, index) in tableData"
                :key="item.id"
                class="grid-content ep-bg-purple-light"
              >
                <span class="drag move">
                  <el-icon v-if="item.courseType === '0'"> <Star /> </el-icon
                  ><el-icon v-if="item.courseType === '1'"><Aim /></el-icon
                  >{{ item.courseName }}</span
                >
              </div>
            </div>
            <div class="sigle-col" style="width: 20%">
              <div
                v-for="(item, index) in tableData"
                :key="item.id"
                class="grid-content ep-bg-purple"
              >
                <el-select
                  v-if="item.id"
                  v-model="item.teacherList"
                  style="margin-left: 10px"
                  filterable
                  remote
                  reserve-keyword
                  remote-show-suffix
                  placeholder="请输入"
                  value-key="id"
                  multiple
                  :remote-method="(val) => remoteMethod(val, item)"
                  :loading="loading"
                >
                  <el-option
                    v-for="item in item.options"
                    :key="item.value"
                    :label="item.name"
                    :value-key="item"
                    :value="item"
                  >
                    <div @click="openAddTeacher(item, item)">
                      <span style="float: left">{{ item.name }}</span>
                      <span style="float: right; color: #9a9a9a; font-size: 13px">{{
                        item.type === '1'
                          ? '讲师'
                          : item.type === '2'
                          ? '组织结构'
                          : item.type === '3'
                          ? '角色'
                          : ''
                      }}</span>
                    </div>
                  </el-option>
                </el-select>
              </div>
            </div>
            <div class="sigle-col" style="width: 10%">
              <div
                v-for="(item, index) in tableData"
                :key="item.id"
                class="grid-content ep-bg-purple-light"
              >
                <span v-if="item.id">{{ item.duty }}</span>
              </div>
            </div>
          </div>

 js代码

const initSort = () => {
  // const el = document.querySelector('#tableRowDrop tbody');
  const el = document.querySelector('#courseName');
  // const el = document.querySelector('#newId');
  let table = new Sortable(el, {
    // sort: false, // boolean 定义是否列表单元是否可以在列表容器内进行拖拽排序
    handle: '.move',
    disabled: false,
    // filter: '.notDrop', // 过滤器,不需要进行拖动的元素
    // filter:'.el-table_1_column_15 .el-table_1_column_16 .el-table_1_column_17 .el-table_1_column_19 .el-table_1_column_20',
    // preventOnFilter: true, //  在触发过滤器`filter`的时候调用`event.preventDefault()`
    // draggable: '.el-table__row', // 允许拖拽的项目类名  设置可被拖拽区域的className
    // draggable: '.el-row', // 允许拖拽的项目类名  设置可被拖拽区域的className
    sort: true, // 设为false,禁止sort
    dragClass: 'dragClass', // 设置拖拽样式类名
    ghostClass: 'ghostClass', // 设置拖拽停靠样式类名
    chosenClass: 'chosenClass', // 设置选中样式类名
    // forceFallback: true, // 忽略 HTML5拖拽行为,强制回调进行
    // fallbackClass: 'fallbackClass', // 当使用forceFallback的时候,被复制的dom的css类名
    fallbackOnBody: false,
    removeCloneOnHide: true,
    group: {
      // 是否开启跨表拖拽
      name: 'table',
      pull: true,
      put: true,
    },
    // 开始拖拽的时候
    onStart(/** Event */ evt) {
      evt.oldIndex; // element index within parent
    },
    onAdd(/** Event */ evt) {
  
      targetRow.value = tableData.value[evt.newIndex];
 
    },

    onChoose(/** Event */ evt) {
      // debugger;
      // 元素被选中
      selectedRow.value = tableData.value[evt.oldIndex];
    },

    // 总是在右侧拖动进入onUpdate
    // 拖拽元素改变位置的时候

    onUpdate: (evt) => {
      // debugger;

      // let target = tableData.value[evt.newIndex];
      // debugger;

      if (evt.newIndex === evt.oldIndex) {
        return;
      }
      targetRow.value = tableData.value[evt.newIndex];
      curRow.value = tableData.value.splice(evt.oldIndex, 1)[0]; // 被拖动的行
      // console.log(curRow.value, 'curRow.valuecurRow.value');
      const newIndex = evt.newIndex; // 获取到最新的索引值

      // console.log(`当前元素的索引为:${newIndex}`);
      if (targetRow.value) {
        let param = {
          newCourseDate: targetRow.value.courseDate,
          newCourseTime: targetRow.value.courseTime,
          newScheduleCourseId: targetRow.value.id // 拖拽落点的课表课程详情id
            ? targetRow.value.id
            : undefined,
          newTimeFrameOrder: targetRow.value.timeFrameOrder
            ? targetRow.value.timeFrameOrder
            : undefined,
          oldClassCourseId: curRow.value.id // 拖拽的班级课程id
            ? curRow.value.classCourseId
            : null,
          oldScheduleCourseId: curRow.value.id, // 拖拽的课表课程详情id
          scheduleId: baseInfoObj.value.id, //	课程表id
        };
        saveScheduleCourse(param);

        classTableList.value = [];
        getInfo();
      } else {
        // tableRef.value.doLayout();
      }
    },
    onRemove(/** Event */ evt) {
      // same properties as onEnd
    },
    // 列表的任何更改都会触发
    onSort(/** Event */ evt) {
      // debugger; // same properties as onEnd
    },
  });
 
  if (activeName.value === 'third') {
    const add = document.querySelector(
      '#pane-third .el-card .el-card__body .el-scrollbar .el-scrollbar__wrap .el-scrollbar__view'
    );
    const sorable = new Sortable(add, {
      // sort: false, // boolean 定义是否列表单元是否可以在列表容器内进行拖拽排序
      handle: '.firstCont',
      sort: false, // 设为false,禁止sort
      // draggable: '.item', // 允许拖拽的项目类名
      // forceFallback: true, // 忽略 HTML5拖拽行为,强制回调进行
      // fallbackClass: 'fallbackClass', // 当使用forceFallback的时候,被复制的dom的css类名
      fallbackOnBody: false,
      removeCloneOnHide: true,
      dragClass: 'dragClass', // 设置拖拽样式类名
      ghostClass: 'ghostClass', // 设置拖拽停靠样式类名
      chosenClass: 'chosenClass', // 设置选中样式类名
      group: {
        // 是否开启跨表拖拽
        name: 'table', // 相同组名的情况下才可以跨列表拖动
        pull: 'clone',
        put: true,
      },
      onChoose(/** Event */ evt) {
        // 元素被选中
        curRow.value = classTableList.value[evt.oldIndex];
      },
      // 列表的任何更改都会触发
      onAdd(/** Event */ evt) {
        // same properties as onEnd
        // debugger;
        if (selectedRow.value) {
          let param = {
            oldScheduleCourseId: selectedRow.value.id, // 拖拽的课表课程详情id
            scheduleId: baseInfoObj.value.id, // 课程表id
          };
          saveScheduleCourse(param);
        }
      },
      onEnd: (evt) => {
        // 从右向左拖动,不进入,从左向右拖动进入(目前的问题是从左向右拖动位置有时候不正确)
        // debugger;
        const newIndex = evt.newIndex; // 获取到最新的索引值
        console.log(`当前元素的索引为:${newIndex}`);
        console.log(evt);
        console.log(tableData);
        if (targetRow.value) {
          let param = {
            newCourseDate: targetRow.value.courseDate,
            newCourseTime: targetRow.value.courseTime,
            newScheduleCourseId: targetRow.value.id // 拖拽落点的课表课程详情id
              ? targetRow.value.id
              : undefined,
            newTimeFrameOrder: targetRow.value.timeFrameOrder
              ? targetRow.value.timeFrameOrder
              : undefined,
            oldClassCourseId: curRow.value.id ? curRow.value.id : null, // 拖拽的班级课程id
            // oldScheduleCourseId: curRow.value.id,//拖拽的课表课程详情id
            scheduleId: baseInfoObj.value.id, // 课程表id
          };
          saveScheduleCourse(param);
        } else {
          console.log('');
        }
      },
    });
  }
};

// 拖拽保存课程
const saveScheduleCourse = (params) => {
  save_ScheduleCourse(params).then((res) => {
    if (res.code === '200') {
     // window.location.reload();
      getTableData(baseInfoObj.value.id);

      classTableList.value = [];
      getInfo();
      getSpanArr(tableData.value);
    } else {
      getInfo();
      tableData.value = [];
      getTableData(baseInfoObj.value.id);
      getSpanArr(tableData.value);
    }
  }); // 保存课程
  setTimeout(() => {
    initSort();
  }, 50);
  let element = document.getElementById('courseName');

  // 查询该元素下所有符合条件的子元素节点
  let elementsToRemove = element.querySelectorAll('.level1'); // className为目标类名
  // debugger;
  // 遍历子元素节点,将其从DOM结构中移除
  for (let i = 0; i < elementsToRemove.length; i++) {
    let childElement = elementsToRemove[i];
    childElement.parentNode.removeChild(childElement);
  }
  setTimeout(() => {
    initSort();
  }, 50);
};


/**
 * @description: 处理数据2.0 三维数组处理成一维数组
 * @return {*}
 */

const handleData = (data) => {
  const newData = data.map((item, index) => {
    let rows = 0;
    item.forEach((item2) => {
      rows += item2.length;
    });
    item.rowspan = rows;
    return item;
  });
  let mydata = [];

  newData.forEach((projectItem) => {
    projectItem.forEach((modelItem, modelIndex) => {
      modelItem.forEach((taskItem, taskIndex) => {
        if (modelIndex === 0 && taskIndex === 0) {
          mydata.push({
            rowspan1: projectItem.rowspan,
            courseDate: taskItem.courseDate, // projectItem.courseDate,
            rowspan2: modelItem.length,
            courseTime: taskItem.courseTime,
            ...taskItem,
          });
        } else if (taskIndex === 0) {
          mydata.push({
            rowspan2: modelItem.length,
            courseTime: taskItem.courseTime,
            ...taskItem,
          });
        } else {
          mydata.push(taskItem);
   
        }
      });
    });
  });
  return mydata;
};
const tableData = ref([]);
// 右侧表格数据
const getTableData = async (id) => {
  let res = await info_jwPtkScheduleCourseDetail(id);
  if (res.code === '200') {
    //firstTableData.value = res.data;

    tableData.value = handleData(res.data);
    setTimeout(() => {
      initSort();
    }, 100);
    console.log(tableData.value, '表格数据');
  }
};

但是遇到了新问题,从左侧向右连续拖拽两次或者更多之后,每次的拖拽完成课程位置与预期拖放的位置不一样,暂时未解决

综合性总结

  • 如果没有设置row-key,拖动后会乱序
  • 建议可以设置两个tableData,tableData 和 tableDataCopy。table用于展示,每次拖动后修改tableDataCopy。这样不影响拖动的展示效果,也可以实现拖动的数据处理。再某些特殊表格拖动时,如果修改的是同一个tableDate,可能会出现一些异常。
  • 一个页面如果有多个表格,在拖动方法的时候需要注意,不建议有多个表格。
  • 8
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值