ag-grid实现自定义单元格拖拽

515 阅读3分钟

今天有时间就录制一个我最近在座的一个需求吧,我最近在做学校的项目,拖拽盒子调课,例如把第三周的星期二第4节课拖动到第三周星期四的第六节课,因为我们的课表是采用的表格进行展示的,用到的表格是ag-grid-vue组件,下面就看我如何做的吧

export const COURSE_SCHEDULING_COLUMNS = [
    {
        headerName: '',
        field: 'name',
        minWidth: 100,
        maxWidth: 100,
        tooltipField: 'name',
        wrapText: true,
        menuTabs: false,
        cellStyle: {lineHeight: 1, paddingTop: '15px'}
    },
    {
        headerName: '一',
        field: 'Monday',
        minWidth: 200,
        width: 200,
        autoHeight: true,
        cellRenderer: 'CourseDetail',
        menuTabs: false,
        cellStyle: {padding: 0}
    },
    {
        headerName: '二',
        field: 'Tuesday',
        minWidth: 200,
        autoHeight: true,
        width: 200,
        cellRenderer: 'CourseDetail',
        menuTabs: false,
        cellStyle: {padding: 0}
    },
    {
        headerName: '三',
        field: 'Wednesday',
        minWidth: 200,
        autoHeight: true,
        width: 200,
        cellRenderer: 'CourseDetail',
        menuTabs: false,
        cellStyle: {padding: 0}
    },
    {
        headerName: '四',
        field: 'Thursday',
        minWidth: 200,
        autoHeight: true,
        width: 200,
        cellRenderer: 'CourseDetail',
        menuTabs: false,
        cellStyle: {padding: 0}
    },
    {
        headerName: '五',
        field: 'Friday',
        minWidth: 200,
        autoHeight: true,
        width: 200,
        cellRenderer: 'CourseDetail',
        menuTabs: false,
        cellStyle: {padding: 0}
    },
    {
        headerName: '六',
        field: 'Saturday',
        minWidth: 200,
        autoHeight: true,
        width: 200,
        cellRenderer: 'CourseDetail',
        menuTabs: false,
        cellStyle: {padding: 0}
    },
    {
        headerName: '日',
        minWidth: 200,
        autoHeight: true,
        width: 200,
        field: 'Sunday',
        cellRenderer: 'CourseDetail',
        menuTabs: false,
        cellStyle: {padding: 0}
    }
];

上面是我列配置,这里面用到的是有一个很重要的字段cellRenderer: 'CourseDetail'他是自定义组件配置字段, 接下来就要实现拖拽啦 `

<div
  v-for='(item,index) in params.value'
  :key='index'
  :draggable='true'
  class='course-item'
  @click='routerCourseDetails(item)'
  @dragstart='onDragStart($event,item)'
></div>

这是自定义单元格组件根标签,我在他身上设置:draggable='true'@dragstart='onDragStart($event,item)'第一个属性是为了能够开启拖拽,第二个属性是为了拖拽的时候参数传递

onMounted(() => {
  // 只有课表明细才能进行拖拽
  if (isScheduleDetails.value) {
    nextTick(() => {
      props.params.eGridCell.addEventListener('dragover', onDragOver);
      props.params.eGridCell.addEventListener('drop', onDrop);
      props.params.eGridCell.addEventListener('dragenter', onDragenter);
      props.params.eGridCell.addEventListener('dragleave', onDragleave);
      props.params.eGridCell.addEventListener('dragend', onDragEnd);
    });
  }
});

这里我在自定义单元格组件的onMounted事件之中挂载5个拖拽事件,是为了能够更好地带来用户体验

// 拖动开始
const onDragStart = (event, params) => {
  const { currentTime } = props.params.context;
  // 全部都小于当前周那就直接禁止拖动
  const isWeek = params.weeks.every(item => item < currentTime.week);
  if (isWeek) {
    event.preventDefault();
    ElMessage.warning('本门课程在当前周已经完结');
    return;
  }
  // 如果是单周的情况,就判断当前周和选择的周相同,如果相同就判断星期
  if (params.weeks.length === 1 && params.weeks.includes(currentTime.weeks)) {
    if (params.dayRange < currentTime.dayRange) {
      event.preventDefault();
      ElMessage.warning('本门课程在当前周已经完结');
      return;
      // 如果当前选择的星期和当前星期相等
    } else if (params.dayRange === currentTime.dayRange) {
      // 判断课节是否相等,当前选择的课节小于当前课节也应该提示
      if (params.numRange <= currentTime.numRange) {
        event.preventDefault();
        ElMessage.warning('本门课程在当前周已经完结');
        return;
      }
    }
  }

  // 储存自定义属性
  compId.value = props.params.eGridCell.getAttribute('comp-id');
  // 删除本身的onDragOver事件,是为了不让本身能够触发弹框事件
  props.params.eGridCell.removeEventListener('dragover', onDragOver);
  // 数据传递
  event.dataTransfer.setData(
    'text/plain',
    JSON.stringify(params)
  );
};

// 阻止默认行为,
const onDragOver = (event) => {
  const types = event.dataTransfer.types;
  const dragSupported = types.length;
  if (dragSupported) {
    event.dataTransfer.dropEffect = 'move';
  }
  event.preventDefault();
};

// 拖拽结束后
const onDrop = (event) => {
  event.preventDefault();
  // 将被拖动元素移动到选定的目标元素中
  const target = event.target.closest('.ag-cell-auto-height');
  if (target) {
    // 检查鼠标离开的元素是否是拖拽目标元素的子元素
    if (!target.contains(event.relatedTarget)) {
      // 删除背景样式
      target.classList.remove('dragover');
      // 获取数据
      const textData = event.dataTransfer.getData('text/plain');
      // 转化为对象
      const data = JSON.parse(textData);
      const { rowIndex, column } = props.params;
      props.params.context.openReparationDialog({
        ...data,
        newDayRange: WEEK_ENGLISH_KEY.findIndex(item => item === column.colId) + 1,
        newNumRange: rowIndex + 1
      });
    }
  }
};

// 进入拖拽的盒子之中
const onDragenter = (event) => {
  // 在可拖动元素进入潜在的放置目标时高亮显示该目标
  const target = event.target.closest('.ag-cell-auto-height');

  // 符合目标元素,并且不是本身的父盒子就高亮父元素
  if (target && target.getAttribute('comp-id') !== compId.value) {
    target.classList.add('dragover');
  }
};

// 离开拖拽的盒子之中
const onDragleave = (event) => {
  // 在可拖动元素离开潜在放置目标元素时重置该目标的背景
  const target = event.target.closest('.ag-cell-auto-height');
  if (target) {
    // 检查鼠标离开的元素是否是拖拽目标元素的子元素
    if (!target.contains(event.relatedTarget)) {
      target.classList.remove('dragover');
    }
  }
};

// 拖拽结束触发
const onDragEnd = () => {
  const elementWithCustomAttribute = document.querySelector(`[comp-id='${compId.value}']`);
  compId.value = '';
  elementWithCustomAttribute.addEventListener('dragover', onDragOver);
};

这里就可以大体实现了功能啦,拖拽开始的这个onDragStart事件谁可以帮我代码再精简一下啊,真的不知道怎么优化了,感觉还是很臃肿