首页 > 技术文章 > 仿禅道拖拽任务列表实现

Azune 2020-12-01 19:27 原文

实现效果图 目前只适应6栏js操作


实现功能:实现各个阶段任务随意拖动到任意栏目,栏目宽度按传入数组自适应分布
html

  <div class="task-wrap">
    <div
      class="list-item"
      v-for="item in taskList"
      :key="item.id"
      :style="index == 0 ? 'background:#fff' : ''"
    >
      <div class="header">
        {{ item.name }}
      </div>
      <div class="list">
        <div
          class="listBx"
          :class="move ? 'move' : ''"
          v-for="child in item.child"
          :key="child.id"
          draggable="true"
          @dragover.prevent
          @dragstart="dragstart($event, child, item.id)"
          @drag="dragmove"
          @dragend="dragend($event, child)"
          @mouseover="enter"
          @mouseleave="leave"
        >
          <div class="top">
            {{ child.title }}
          </div>
          <div class="bottom">
            {{ child.name }}
            <span> {{ child.time }}</span>
          </div>
        </div>
      </div>
    </div>
    <div class="nav-header" v-show="flag">
      <div class="header-item" v-for="item in taskList" :key="item.id">
        {{ item.name }}
      </div>
    </div>
    <el-dialog
      title="任务修改"
      :visible.sync="dialogVisible"
      width="650px"
      center
    >
      <el-form ref="form" :model="form" label-width="80px">
        <el-form-item label="任务名称">
          <el-input v-model="form.name"></el-input>
        </el-form-item>
        <el-form-item label="任务时间">
          <el-input v-model="form.time"></el-input>
        </el-form-item>
        <el-form-item label="任务类型">
          <el-select v-model="form.region" placeholder="任务类型">
            <el-option label="未开始" value="1"></el-option>
            <el-option label="进行中" value="2"></el-option>
          </el-select>
        </el-form-item>
        <el-form-item label="任务内容">
          <el-input
            type="textarea"
            :rows="2"
            placeholder="请输入内容"
            v-model="form.title"
          >
          </el-input>
        </el-form-item>
      </el-form>
      <span slot="footer" class="dialog-footer">
        <el-button @click="dialogVisible = false">取 消</el-button>
        <el-button type="primary" @click="dialogVisible = false"
          >确 定</el-button
        >
      </span>
    </el-dialog>
  </div>

js

export default {
  name: "task",
  data() {
    return {
      dialogVisible: false,
      flag: false,
      move: false,
      screenWidth: document.documentElement.clientWidth,
      itemWidth: 0,
      index: 0,
      form: {},
      taskList: [
        {
          id: 1,
          name: "未开始",
          child: [
            {
              id: 11,
              title: "成品的价格方面,重新设计",
              name: "王小虎",
              time: "1h"
            }
          ]
        },
        {
          id: 2,
          name: "进行中",
          child: [
            // {
            //   id: 21,
            //   title: "成品的价格方面,重新设计",
            //   name: "王小虎",
            //   time: "1h"
            // }
          ]
        },
        {
          id: 3,
          name: "已暂停",
          child: [
            // {
            //   id: 31,
            //   title: "成品的价格方面,重新设计",
            //   name: "王小虎",
            //   time: "1h"
            // }
          ]
        },
        {
          id: 4,
          name: "已完成",
          child: [
            {
              id: 41,
              title: "成品的价格方面,重新设计",
              name: "王小虎",
              time: "1h"
            }
          ]
        },
        {
          id: 5,
          name: "已取消",
          child: [
            {
              id: 51,
              title: "成品的价格方面,重新设计",
              name: "王小虎",
              time: "1h"
            }
          ]
        },
        {
          id: 6,
          name: "已关闭",
          child: [
            {
              id: 61,
              title: "基础数据客户管理批量导入",
              name: "已关闭",
              time: "0h"
            },
            {
              id: 71,
              title: "基础数据.客户管理,导入模板",
              name: "已关闭",
              time: "0h"
            },
            {
              id: 81,
              title: "基础数据成品管理详情的样式",
              name: "已关闭",
              time: "0h"
            }
          ]
        }
      ],
    };
  },
  mounted() {
    // 判断传递的子项定义宽度
    let taskList = document.querySelectorAll(".list-item");
    let headerList = document.querySelectorAll(".header-item");
    let width;
    if (taskList.length == 2) {
      width = "50%";
    } else if (taskList.length == 3) {
      width = "33.33%";
    } else if (taskList.length == 4) {
      width = "25%";
    } else if (taskList.length == 5) {
      width = "20%";
    } else if (taskList.length == 6) {
      width = "16.66%";
    }
    for (var i = 0; i < taskList.length; i++) {
      taskList[i].style.width = width;
      headerList[i].style.width = width;
    }
    window.onresize = () => {
      return (() => {
        // 获取到实时的屏幕宽度
        this.screenWidth = document.documentElement.clientWidth;
        let headerList = document.querySelectorAll(".header-item");
        // 实时获取每一栏的宽度
        this.itemWidth = this.screenWidth / headerList.length;
      })();
    };
    window.addEventListener("scroll", this.handleScroll);
  },
  destroyed() {
    document.removeEventListener("scroll", this.handleScroll);
  },
  methods: {
    handleScroll() {
      //获取滚动时的高度
      let scrollTop =
        window.pageYOffset ||
        document.documentElement.scrollTop ||
        document.body.scrollTop;
      if (scrollTop > 50) {
        this.flag = true;
      }
      if (scrollTop < 50) {
        this.flag = false;
      }
    },
    dragstart(event, child, index) {
      this.move = true;
      // 判断移动的是哪一栏 通过传递的参数获得
      let str = child.id + "";
      // 获取当前移动的坐标
      this.index = index;
      // 获取当前宽度
      this.itemWidth = parseInt(this.screenWidth / this.taskList.length);
      // 当前选择的位置
      this.form = child;
    },
    dragmove() {
      let taskList = document.querySelectorAll(".list-item");
      let index = this.index;
      let indexEnd = parseInt(event.clientX / this.itemWidth);
      for (let i = 0; i < taskList.length; i++) {
        taskList[i].style.background = "#fff";
      }
      taskList[indexEnd].style.background = "rgba(0,0,0,.2)";
    },
    dragend(event, child) {
      this.move = false;
      // 获取移动的初始以及之后的坐标
      let index = this.index - 1;
      let indexEnd = parseInt(event.clientX / this.itemWidth);
      // 获取当前子元素坐标
      let i = this.taskList[index].child.indexOf(child);
      this.taskList[index].child.splice(i, 1);
      this.taskList[indexEnd].child.push(child);
      this.dialogVisible = true;

      let taskList = document.querySelectorAll(".list-item");
      taskList[0].style.background = "#fff";
    },
    enter(index) {
      this.move = true;
    },
    leave() {
      this.move = false;
    }
  }
};

css

body {
  height: 100%;
}
.task-wrap {
  min-width: 1200px;
  overflow-x: hidden;
}
.task-wrap .list-item {
  float: left;
  width: 16.66%;
  height: 100%;
}
.task-wrap .list-item .header {
  width: 100%;
  height: 44px;
  line-height: 44px;
  text-align: center;
}
.task-wrap .list-item .list {
  min-height: 100vh;
  padding: 10px;
  border-right: 2px solid #ccc;
}
.task-wrap .list-item .list .listBx {
  box-sizing: border-box;
  width: 100%;
  min-height: 64px;
  padding: 10px;
  border: 1px solid #ccc;
  margin-bottom: 10px;
}
.move {
  cursor: move;
}
.task-wrap .list-item .list .listBx > div {
  height: 50%;
}
.task-wrap .list-item .list .listBx .top {
  line-height: 28px;
  overflow: hidden;
  text-overflow: ellipsis;
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
}
.task-wrap .list-item .list .listBx .bottom {
  position: relative;
  padding-left: 32px;
}
.task-wrap .list-item .list .listBx .bottom::before {
  position: absolute;
  top: -5px;
  left: 0;
  content: "";
  display: inline-block;
  width: 28px;
  height: 40px;
  background: url("../assets/zx.png") no-repeat;
}
.task-wrap .list-item .list .listBx .bottom span {
  position: absolute;
  top: 5px;
  right: 10px;
  color: #999;
}
.task-wrap .list-item:nth-child(1) .header,
.nav-header div:nth-child(1) {
  font-weight: 600;
  border-bottom: 5px solid #92ceff;
}
.task-wrap .list-item:nth-child(2) .header,
.nav-header div:nth-child(2) {
  font-weight: 600;
  border-bottom: 5px solid #0991ff;
}
.task-wrap .list-item:nth-child(3) .header,
.nav-header div:nth-child(3) {
  font-weight: 600;
  border-bottom: 5px solid #fdcb56;
}
.task-wrap .list-item:nth-child(4) .header,
.nav-header div:nth-child(4) {
  font-weight: 600;
  border-bottom: 5px solid #40df98;
}
.task-wrap .list-item:nth-child(5) .header,
.nav-header div:nth-child(5) {
  font-weight: 600;
  border-bottom: 5px solid #cccccc;
}
.task-wrap .list-item:nth-child(6) .header,
.nav-header div:nth-child(6) {
  font-weight: 600;
  border-bottom: 5px solid #7f869a;
}
.nav-header {
  position: fixed;
  top: 0;
  width: 100%;
  height: 44px;
  line-height: 44px;
  background: #fff;
  transition: 0.2s;
}
.nav-header div {
  float: left;
  text-align: center;
  width: 16.66%;
}
.mask {
  width: 100px;
  height: 100px;
  background: pink;
  position: fixed;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
}

推荐阅读