前端甘特图

用vue实现的一个自定义甘特图画法

用vue开发一个甘特图,实现了添加,删除,托拉拽的效果,数据的更新没有做,码友可以自行补充。效果如图:

在这里插入图片描述

删除

<template>
  <div class="main">
    <div class="block">
      <el-form :inline="true" :model="formInline" class="form-inline">
        <el-form-item label="请选择颜色">
          <el-color-picker v-model="formInline.bg"></el-color-picker>
        </el-form-item>
        <el-form-item label="流程名">
          <el-input v-model="formInline.content"></el-input>
        </el-form-item>
        <el-form-item label="开始时间">
          <el-input v-model="formInline.start"></el-input>
        </el-form-item>
        <el-form-item label="结束时间">
          <el-input v-model="formInline.end"></el-input>
        </el-form-item>
        <el-form-item>
          <el-button type="primary" @click="addTask">生成流程</el-button>
        </el-form-item>
      </el-form>
    </div>
    <div class="gantt-chart border-box" ref="ganttChart" id="ganttChart">
      <div class="big-range-time">
        <div
          v-for="item in time.bigtime"
          :key="item.time"
          class="big-time-item"
          :style="{ width: 31 * width + 'px' }"
          :title="item.time"
        >
          {{ item.time }}
        </div>
      </div>
      <div class="small-range-time">
        <div
          v-for="(item, index) in time.smalltime"
          :key="index"
          class="small-time-item"
          :title="item"
          :style="{ height: height + 'px', width: width + 'px' }"
        >
          {{ item + 1 }}
        </div>
      </div>
      <div class="task-background" v-if="time.smalltime.length == 0">
        <div>暂无数据</div>
      </div>
      <div class="task-background" v-else>
        <div
          v-for="(item, index) in gantData.taskitem"
          :key="index"
          class="task-block"
          :style="{ height: height + 'px', width: width + 'px' }"
        >
          <div
            class="task-time-block"
            v-for="item in time.smalltime"
            :key="item"
            :style="{ height: height + 'px', width: width + 'px' }"
          ></div>
        </div>
      </div>
      <div class="task-detail" id="task" ref="task"   :style="{ top: height*2 + 'px'}">
        <div
          :draggable="draggable"
          class="drag-item"
          v-for="(item, index) in taskList"
          @dragstart="dragstart($event, index)"
          @dragenter="dragenter($event, index)"
          @dragover="dragover($event, index)"
          @dragend="dragend($event, index)"
          :key="item.id"
          :style="{ height: height + 'px', width: width * 31 + 'px' }"
        >
          <div
            class="task-item"
            :id="item.id"
            :title="item.title"
            :style="{
              width: item.width + 'px',
              top: item.top + 'px',
              left: item.left + 'px',
              background: item.background,
              height: height + 'px',
            }"
          >
            {{ item.content }}
          </div>
        </div>
      </div>
    </div>
  </div>
</template>

<script>
export default {
  data () {
    return {
      height: 40, // 最小时间单位的高度
      width: 40, // 最小时间单位宽度,大于30
      time: {
        bigtime: [{ time: '2022-07' }],
        smalltime: [],
        bigtimetotal: 1,
        smalltimetotal: 0
      },
      formInline: {
        bg: '#409EFF',
        content: '',
        start: '',
        end: ''
      },
      task: {
        top: '',
        bottom: '',
        left: '',
        right: ''
      },
      gantData: {
        tasktotal: 5,
        taskitem: [
          { start: 12, end: 16.5, content: 'div0', bg: 'lightblue' },
          { start: 7, end: 30, content: 'div1', bg: 'lightblue' },
          { start: 30, end: 31, content: 'div2', bg: 'lightblue' },
          { start: 1, end: 5, content: 'div3', bg: 'lightblue' },
          { start: 1, end: 8, content: 'div4', bg: 'lightblue' }
        ]
      },
      taskList: [],
      dragIdx: '', // 当前拖动元素的位置
      objIdx: '', // 目标位置
      draggable: true // 判断是否可以上下拖动
    }
  },

  methods: {
    // 添加流程
    addTask () {
      this.gantData.taskitem.unshift(this.formInline)
      this.getTask()
    },
    dragstart (e, index) {
      e.stopPropagation()
      this.dragIdx = index
    },

    dragenter (e, index) {
      e.preventDefault()
      this.objIdx = index
    },
    dragend (e, index) {
      e.preventDefault()
      if (this.dragIdx !== this.objIdx) {
        const node = this.gantData.taskitem[this.dragIdx]
        this.gantData.taskitem.splice(this.dragIdx, 1)
        this.gantData.taskitem.splice(this.objIdx, 0, node)
        this.dragIndex = this.objIdx
        this.getTask()
      }
    },
    dragover (e, index) {
      e.preventDefault()
    },
    bindEvent () {
      this.$nextTick(() => {
        this.taskList.forEach((e, index) => {
          const dObj = document.getElementById(e.id)
          const _that = this
          if (dObj) {
            dObj.addEventListener('mousemove', (e) => {
              let stepX
              let temppointX
              let tempwidth
              let isMouseDown
              let stretch
              let tempLeft
              // 检测是否在进度条边界
              // 拉伸
              if (Math.abs(e.offsetX - dObj.offsetWidth) < 10) {
                dObj.style.cursor = 'e-resize'
                dObj.onmousedown = (e) => {
                  _that.draggable = false
                  temppointX = e.clientX
                  tempwidth = dObj.offsetWidth
                  stretch = true
                  isMouseDown = false
                  document.addEventListener('mousemove', (e) => {
                    if (stretch && dObj.offsetWidth + dObj.offsetLeft <= 31 * _that.width && dObj.offsetWidth >= 30) {
                      const stepX = e.clientX - temppointX
                      dObj.style.width = tempwidth + stepX + 'px'
                    }
                    if (dObj.offsetWidth + dObj.offsetLeft > 31 * _that.width) {
                      dObj.style.width = 31 * _that.width - dObj.offsetLeft + 'px'
                    }
                  })
                }
                document.addEventListener('mouseup', isOk)
              } else {
                // 拖曳
                dObj.style.cursor = 'move'
                dObj.onmousedown = (e) => {
                  _that.draggable = false
                  temppointX = e.clientX
                  tempLeft = dObj.offsetLeft
                  isMouseDown = true
                  stretch = false
                  dObj.addEventListener('mousemove', (e) => {
                    if (isMouseDown && dObj.offsetWidth + dObj.offsetLeft <= 31 * _that.width && dObj.offsetLeft >= 0) {
                      stepX = e.clientX - temppointX
                      dObj.style.left = tempLeft + stepX + 'px'
                    }
                    if (dObj.offsetWidth + dObj.offsetLeft > 31 * _that.width) {
                      dObj.style.left = 31 * _that.width - dObj.offsetWidth + 'px'
                    }
                    if (dObj.offsetLeft < 0) {
                      dObj.style.left = 0 + 'px'
                    }
                    document.onmouseup = isOk
                  })
                }
              }

              function isOk () {
                document.onmousemove = null
                document.onmousedown = null
                isMouseDown = false
                stretch = false
                dObj.style.cursor = 'move'
                _that.draggable = true
              }
            })

            // 删除
            dObj.oncontextmenu = (e) => {
              e.preventDefault()
              this.$confirm(`此操作将删除${dObj.innerText}, 是否继续?`, '提示', {
                confirmButtonText: '确定',
                cancelButtonText: '取消',
                type: 'warning'
              })
                .then(() => {
                  this.taskList.splice(index, 1)
                  this.taskList.forEach((e) => {
                    if (e.id > dObj.id) {
                      e.top = e.top - this.height
                    }
                  })
                  this.gantData.taskitem.splice(index, 1)
                  this.$message({
                    type: 'success',
                    message: '删除成功!'
                  })
                })
                .catch(() => {})
            }
          }
        })
      })
    },

    getTask () {
      this.taskList = []
      this.gantData.taskitem.forEach((e, index) => {
        const dObj = {}
        dObj.left = (e.start - 1) * this.width
        dObj.top = index * this.height
        dObj.content = e.content
        dObj.title = e.content
        dObj.width = (e.end - e.start + 1) * this.width
        dObj.id = 'task-item-' + index
        dObj.background = e.bg
        this.taskList.push(dObj)
      })
      this.bindEvent()
    },
    init () {
      for (let i = 0; i < 31; i++) {
        this.time.smalltime.push(i)
      }
      this.time.smalltimetotal = this.time.smalltime.length
      const taskDom = document.getElementById('task')
      this.task.top = taskDom.offsetTop
      this.task.bottom = taskDom.offsetTop + this.width * this.gantData.taskitem.length
      this.task.left = taskDom.offsetLeft
      this.task.right = taskDom.offsetLeft + this.time.smalltimetotal * this.width
      this.getTask()
    }
  },
  created () {},
  mounted () {
    this.init()
  }
}
</script>

<style scoped lang="scss">
.main {
  margin: 0 auto;
  width: 1200px;
  .block {
    display: flex;
    height: 100px;
    align-items: center;
  }
  .gantt-chart {
    overflow-x: scroll;
    position: relative;
    border: 2px solid rgba(0, 0, 0, .1);
    border-radius: 8px;
    box-shadow: 5px 5px 10px 5px  rgba(0, 0, 0, .1);
    .task-background {
      .task-block {
        display: flex;
        .task-time-block {
          flex: 0 0 auto;
          box-sizing: border-box;
          border: 1px solid lightgray;
        }
      }
    }
    .big-range-time {
      background: lightcyan;
      height: 40px;
      display: inline-flex;
      .big-time-item {
        user-select: none;
        display: flex;
        justify-content: center;
        align-items: center;
        box-sizing: border-box;
        border: 1px solid lightgray;
      }
    }
    .small-range-time {
      display: inline-flex;
      .small-time-item {
        user-select: none;
        background: lightgreen;
        display: flex;
        justify-content: center;
        align-items: center;
        box-sizing: border-box;
        border: 1px solid lightgray;
      }
    }
    .task-detail {
      position: absolute;
      z-index: 99;
      .drag-item {
        .task-item {
          display: flex;
          justify-content: center;
          align-items: center;
          user-select: none;
          border: 1px solid lightgray;
          box-sizing: border-box;
          min-width: 30px;
          position: absolute;
          border-radius: 15px;
        }
      }
    }
  }
}
</style>

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
### 回答1: 以下是一个简单的甘特图的HTML代码示例: ``` <!DOCTYPE html> <html> <head> <title>Gantt Chart</title> <style> table { border-collapse: collapse; width: 100%; } th, td { text-align: left; padding: 8px; border-bottom: 1px solid #ddd; } th { background-color: #f2f2f2; } .task { background-color: #4CAF50; color: white; position: relative; height: 40px; margin-bottom: 5px; } .task::before { content: ""; position: absolute; top: 0; left: 0; width: 0; height: 0; border-top: 20px solid transparent; border-bottom: 20px solid transparent; border-right: 20px solid #4CAF50; } .task span { position: absolute; left: 25px; top: 10px; } </style> </head> <body> <table> <tr> <th>Task</th> <th>Start Date</th> <th>End Date</th> </tr> <tr> <td> <div class="task"> <span>Task 1</span> </div> </td> <td>2023-02-18</td> <td>2023-02-21</td> </tr> <tr> <td> <div class="task"> <span>Task 2</span> </div> </td> <td>2023-02-22</td> <td>2023-02-26</td> </tr> <tr> <td> <div class="task"> <span>Task 3</span> </div> </td> <td>2023-02-27</td> <td>2023-03-01</td> </tr> </table> </body> </html> ``` 该示例使用了一个HTML表格来创建甘特图,其中每个任务都表示为一个具有样式的`div`元素。`::before`伪元素用于绘制表示任务时间范围的三角形,任务的名称通过一个`<span>`元素放置在任务条上。 注意,这只是一个简单的示例,并且可以进一步自定义和优化。 ### 回答2: 甘特图是一种用于展示项目进程、任务时间安排等的图表形式。在前端实现甘特图的HTML代码中,可以利用HTML、CSS和JavaScript技术进行实现。 首先,可以使用HTML来创建甘特图所需的基本结构。通过创建一个HTML容器元素,如<div>或者<canvas>,来容纳整个甘特图的内容。 接下来,可以使用CSS来定义甘特图内各个元素的样式。可以为容器元素设置宽度、高度以及背景颜色等基本样式,并使用CSS选择器为甘特图中的任务条目、任务名称、时间轴等元素设置样式。 在实现甘特图的任务条目时,可以通过使用HTML的<div>元素来表示每一个任务,并通过CSS样式设置宽度、高度和颜色等属性,以及定位每一个任务的位置。 在定位任务的不同起止时间上,可以使用CSS属性如left和right来控制任务条目的位置。定位可以使用百分比值或者像素值进行。 另外,在甘特图中,可以为每个任务元素添加一些交互特性。可以使用JavaScript来为任务元素添加事件监听器,如鼠标悬浮、点击等事件,以增强交互体验。 此外,还可以使用JavaScript来动态生成甘特图的数据。可以从后端获取项目的任务信息,并生成相应的HTML元素来表示任务条目。 总结起来,前端实现甘特图HTML代码的关键是通过HTML、CSS和JavaScript实现甘特图的基本结构、样式和交互功能。通过灵活运用这些技术,可以创建出美观、功能强大的甘特图。 ### 回答3: 甘特图是项目管理中常用的一种图表,用来展示项目在时间轴上的进度安排。要实现甘特图的HTML代码,可以按照以下步骤进行: 1. 创建一个HTML文件,并使用DOCTYPE声明,以及html、head和body标签。 2. 在head标签中,添加一个style标签,用于定义甘特图的样式。可以设置甘特图容器的宽度、高度,以及背景色等样式。 3. 在body标签中,使用div标签创建一个容器,用于放置甘特图。 4. 在style标签中,定义甘特图容器的样式。可以设置宽度和高度,以及其他样式。例如: ``` .gantt-container { width: 800px; height: 500px; background-color: #f2f2f2; } ``` 5. 使用JavaScript或jQuery等前端框架,获取需要展示的项目数据。可以使用数组对象来表示每个任务的开始时间、结束时间、持续时间等信息。 6. 根据获取的任务数据,使用div标签创建每个任务的条形表示,放置在甘特图容器中。可以根据任务的开始时间和持续时间计算出任务的位置和宽度,并设置对应的样式。 7. 设置每个任务条的样式,可以使用不同的颜色来表示不同的任务,也可以使用不同的状态来表示任务的进度。 8. 如果需要添加任务的标签或说明文字,可以使用span标签,并设置对应的样式。 9. 在页面加载完成时,将甘特图容器添加到body标签中,并显示出来。 以上就是实现甘特图的HTML代码的基本步骤。通过设置样式和使用任务数据,可以灵活地展示不同的甘特图,并方便地进行项目管理和进度监控。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值