要实现如下的拖拽需求
可以左右拖动,右侧表格只可以拖拽教学内容列,拖拽过程中样式上展示也是只有教学内容列
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,可能会出现一些异常。
- 一个页面如果有多个表格,在拖动方法的时候需要注意,不建议有多个表格。