需求

根据数据源提供的科目及教师信息,拖拽至空白课表内,生成一份课表。

需求分析

数据源

首先科目有多个,教师也有多个。数据源部分做一个切换选择科目的效果,选取科目后,提供科目及授课人名字。

课表

课表可以是一张空白课表,也可以默认指定某日某节某人某课。
课表使用表格呈现,这样区域方便划分。
表头是工作日,表体部分的第一列是节次。为了更方便查看,最左边设计上中下午段的划分。

设计稿

在这里插入图片描述

实现

Vue-Draggable

拖拽效果有比较成熟的组件库了:Vue-Draggable
它是基于Sortable.js实现的,适用vue项目,可以在pc端使用也可以在移动端使用
官网地址:GitHub - SortableJS/Vue.Draggable: Vue drag-and-drop component based on Sortable.js
线上演示地址:https://sortablejs.github.io/Vue.Draggable/

特性
  1. 支持可触摸设备
  2. 支持拖动选取和文本选取
  3. 智能滚动
  4. 支持在不同列表间拖拽
  5. 没有jQuery依赖项
  6. 拖拽可撤销
  7. 拖拽有过渡动画
使用
引入

首先进入你的vue项目,引入组件库

npm i vuedraggable -S

这第一步就报错的估计就只有我了,node-module包删删引引好多次,就是不行。
报错就是让我去看一个debug文件:nodejs\node_cache_logs\2022-06-29T04_30_02_307Z-debug-0.log
实不相瞒,真看不懂。
求助度娘,说是需要清除npm的缓存,这里也有两个槽点。唉,接着看吧:

npm cache clean --force

上面指令执行后没一点用,还是出错,又启用了下面这个指令:

npm cache verify

你们自己看着用吧(如果和我一样第一步就出错的😅)

组件注册

如果你项目使用这个拖拽功能比较多,就全局注册。
单个页面使用的话就局部注册好了:

① import draggable from ‘vuedraggable’
② export default {
components: {
draggable,
},

可要注意了,组件注册需要两步,这里就不要踩坑啦!!

基础使用
<draggable v-model="myArray"  @start="onStart" @end="onEnd">
	<div class="item" v-for="element in myArray" :key="element.id">{{element.name}}</div>
</draggable>

myArray: [{ people: 'cn', id: 1, name: 'www.itxst.com' },
          { people: 'cn', id: 2, name: 'www.baidu.com' },
          { people: 'cn', id: 3, name: 'www.taobao.com' },
          { people: 'us', id: 4, name: 'www.google.com' }
]
进阶使用—带过渡效果
<draggable v-model="myArray"  @start="onStart" @end="onEnd">
    <transition-group>
     <div class="item" v-for="item in myArray" :key="item.id">{{item.name}}</div>
    </transition-group>
</draggable>
配置项

1. list
作为value属性的替代方法,list 是通过拖放进行同步的数组。
主要区别在于list属性由可拖动组件使用拼接方法更新,而value属性是不可变的。
❗需要注意的是:不要与value一起使用。
2. tag
配置该属性后,draggable标签将会作为指定标签渲染。
也就是说,tag是用来设置节点类型的。
默认值是:div,接收字符串类型的数据。
3.group
这个是实现不同组之间相互拖拽的关键属性。
有两种使用方式:
①字符串类型
直接给两组配置相同的组名,eg:group:‘course’
②对象类型
name:设置组名
pull:true/false是否允许拖出当前组
put:true/false是否允许拖入当前组

group:{
    name:'course',//组名为itxst
    pull: true|false| 'clone'|array|function,//是否允许拖出当前组
    put:true|false|array|function,//是否允许拖入当前组
}

常用的也就这些,其他配置项没有深入了解就不在这里介绍了,有需要请移步官网哦~
不过需要提醒的是:
✨如果需要配置clone,disable,sort等属性时,切记放在options里面才能生效。

:options=“{group:{name: ‘itxst’,pull:‘clone’},sort: true}”

事件

choose:被选中时触发
unchoose:未选择时触发
start:开始拖拽时触发
end:拖拽结束时触发
add:元素从一个组内拖拽到另一个组内触发
Update:在组内拖拽更改了顺序触发
sort:对列表的任何更改调用(添加/更新/删除)
remove:元素从一个列表中删除到另一个列表
move:在列表中或在列表之间移动项目的事件
clone:创建元素克隆时被调用
change:拖动元素更改位置时被调用
事件介绍就是上面的了,怎么用也不需要举栗啦。
就简单介绍一下事件里面事件对象e的内容吧!

evt.to;    // target list
evt.from;  // previous list
evt.oldIndex;  // element's old index within old parent
evt.newIndex;  // element's new index within new parent
evt.oldDraggableIndex; // element's old index within old parent, only counting draggable elements
evt.newDraggableIndex; // element's new index within new parent, only counting draggable elements
evt.clone // the clone element
evt.pullMode;  // when item is in another sortable: `"clone"` if cloning, `true` if moving

关于被拖动的元素在拖动过程中呈现半透明样式的修改

对于:拖拽的阴影为啥是透明渐变的问题(官方的默认样式)
自定义的话——>
我们的解决方案是:
首先添加forceFallback属性,设置为true。
然后使用dragClass属性添加被拖拽过程中呈现的样式类名。
最后拖拽过程中的元素就会呈现dragClass设置的样式名所写的样式啦.

感谢吉吉安伙伴的友情分享:
在这里插入图片描述

代码部分

大篇幅可能懒得看,我简短说一下实现思路:
1、左边拖拽到右边课程表的时候使用end事件,事件里面的事件对象e可以拿到e.to也就是目标位置的所有信息。
2、右边的课程表绑定自定义属性用来传递节次信息,key绑定周次信息。
3、在e.to对象里面,自己细心找一下周次、节次怎么获取。
4、直接展示拖拽后的内容,用innerHTML把拖拽的元素直接塞里面。
5、最后肯定是要生成课表的,我们借助二维数组来存放课程信息。第一层为周次,第二层为节次,根据元素内容把数据组合好,最后页面效果有了,课程表的数据也有了。。

html
  <a-card :style="`min-height: ${minHeight}px;`" class="mt50">
    <a-row type="flex" justify="space-between">
      <a-col :md="4" :sm="6" :xs="6" :span="4">
        <a-tabs default-active-key="activetab" tab-position="left" @change="tabChange">
          <a-tab-pane v-for="(item, index) in list" :key="index" :tab="item[0].name">
            <draggable class="dragArea list-group" :list="listDate" :group="{ name: 'people', pull: 'clone', put: false, animation: '300' }" @end="onEnd">
              <transition-group>
                <div class="a-row" v-for="element in listDate" :key="element.id">
                  <p>{{ element.name }}</p>
                  <p>{{ element.teacher }}</p>
                </div>
              </transition-group>
            </draggable>
          </a-tab-pane>
        </a-tabs></a-col
      >
      <a-col :md="2" :sm="4" :xs="4" :span="2"></a-col>
      <a-col :md="16" :sm="24" :xs="24" :span="16">
        <div class="course">
          <table>
            <thead>
              <tr>
                <th colspan="2"></th>
                <th v-for="(val, weekindex) in weeks" :key="weekindex">{{ val }}</th>
              </tr>
            </thead>
            <tbody>
              <tr v-for="index of 11" :key="index">
                <td class="week-td" rowspan="4" v-if="index == 1">上午</td>
                <td class="week-td" rowspan="4" v-if="index == 5">下午</td>
                <td class="week-td" rowspan="3" v-if="index == 9">晚上</td>
                <td class="week-td">{{ index }}</td>
                <draggable group="people" v-for="(val, weekindex) in weeks" :key="weekindex" :dataindex="index" :week="val" tag="td"> </draggable>
              </tr>
            </tbody>
          </table>
        </div>
      </a-col>
    </a-row>
  </a-card>

我vue项目用的是ant design vue组件库,各位看官请挑选自己需要的部分食用吧!

js
import draggable from 'vuedraggable';
export default {
  name: 'custom-clone',
  display: 'Custom Clone',
  order: 3,
  components: {
    draggable,
  },
  data() {
    return {
      minHeight: window.innerHeight - 224,
      list: [
        [
          { name: '语文', id: 1, teacher: '张老师1' },
          { name: '语文', id: 2, teacher: '张老师2' },
          { name: '语文', id: 3, teacher: '张老师3' },
          { name: '语文', id: 4, teacher: '张老师4' },
        ],
        [
          { name: '数学', id: 1, teacher: '王老师1' },
          { name: '数学', id: 2, teacher: '王老师2' },
          { name: '数学', id: 3, teacher: '王老师3' },
          { name: '数学', id: 4, teacher: '王老师4' },
        ],
        [
          { name: '英语', id: 1, teacher: '李老师1' },
          { name: '英语', id: 2, teacher: '李老师2' },
          { name: '英语', id: 3, teacher: '李老师3' },
          { name: '英语', id: 4, teacher: '李老师4' },
        ],
      ],
      listDate: [],
      activetab: 0,
      weeks: ['周一', '周二', '周三', '周四', '周五', '周六', '周日'],
      tableDate: [[], [], [], [], [], [], []],
      weekIndex: '',
      courseIndex: '',
    };
  },
  mounted() {
    this.listDate = this.list[this.activetab];
  },
  methods: {
    onEnd(e) {
      this.weekIndex = e.to.cellIndex;
      this.courseIndex = e.to.__vue__.$vnode.data.attrs.dataindex;
      e.to.innerHTML = e.clone.outerHTML;
      let arr = {
        courseindex: this.courseIndex,
        weekindex: this.weekindex,
        course: e.clone.childNodes[0].innerText,
        teacher: e.clone.childNodes[1].innerText,
      };
      this.$set(this.tableDate[this.weekIndex - 1], this.courseIndex - 1, arr);
    },
    tabChange(key) {
      this.activetab = key;
      this.listDate = this.list[key];
    },
  },
};
css
<style lang="less">
.course {
  height: 700px;
  overflow-y: auto;
  overflow-x: hidden;
  table {
    border: 1px solid #ebebeb;
    margin: 20px auto;
    text-align: center;
    border-collapse: collapse;
  }
  .week-td {
    background-color: #f7f7f7;
  }
  table th {
    border: 1px solid #ebebeb;
    background-color: #f7f7f7;
    padding: 10px;
    width: 100px;
    height: 40px;
    font-size: 14px;
  }
  table td {
    border: 1px solid #ebebeb;
    padding: 4px 10px;
    height: 40px;
    color: #000000;
    font-size: 14px;
  }
}
</style>

效果展示

在这里插入图片描述

这里是飞鱼爱吃米,只授渔,不授鱼!

Logo

前往低代码交流专区

更多推荐