Skip to content

开发背景

工作需要封装一个管理进度/任务的组件,主要是如下需求:

  • 能根据任务的具体时间来展示不同任务块的长度
  • 每个任务下支持children(子任务,树结构)
  • 能根据任务状态(已完成/未完成)展示不同颜色
  • 有可缩放的时间轴(类似于一个可变长度的滑块)
  • 顶部滑块部分要有刻度(精确到x月x日)

解决方案

  • 每一行的格子都自行处理:width、border(会叠加)等
  • 若采用table布局,单元格宽度自适应,只需要处理第一行宽度,border也不会叠加

组件结构

主要是三部分:

  • 顶部滑块
  • 中间的格子
  • 每一行的树结构

示例代码

仅记录核心部分示意代码,主要记录几个注意的点:

  • new Date().getTime()方法获取的并不是当天 24 点的时间戳,非完整的24小时
  • 绑定事件时,注意moveup绑定到document上,通过冒泡的形式触发,否则移动过快移出目标dom时会出现 bug
  • 组件递归时由于会在根节点出现多个 tr,Vue2.x 需要通过第三方包来处理(3.x本身就支持多节点)
  • 组件递归时children的获取
  • 根据时间戳和像素宽度的比例,来获取每个月份所占的实际像素宽度
vue
// 入口核心内容
<template>
    <div>
        <slider/>
        <table ref="table">
            <tr>
                <td style="width: 150px"></td>
                <td v-for="month of 12" :style="getHeadWidth(month)">{{ month }}月</td>
            </tr>
            <task v-for="process of processList" :rowData="process"/>
        </table>
    </div>
</template>
<script>
import task from './task.vue'
import slider from './slider.vue'
import { getMonthDays, getTime } from './tools.js'
export default {
    name: 'process',
    components: {task, slider}
    data () {
        return {
            contentWidth: 0,
            processList: [
                {
                    processName: '进度名称', 
                    taskList: [
                        {
                            taskName: '任务1', 
                            children: [
                                {taskName: '子任务xx', children: []}
                            ]
                        },
                        {
                            taskName: '任务2', 
                            children: [
                                {taskName: '子任务xxxx', children: []}
                            ]
                        }
                    ]
                }
            ]
        }
    },
    methods: {
        getHeadWidth (m) {
            const firstDay = '2023-01-01'
            const lastDay = '2023-12-31'
            // 整个时间长度
            // 注意 new Date().getTime() 不是取的当天24点的时间戳
            // 因此 allTimeLength 会少一天, 所以这里在 getTime 处理
            const allTimeLength = getTime(lastDay, 'end') - getTime(firstDay)
            // 整个像素宽度 this.contentWidth
            // 该月天数的时间长度
            const daysTimeLength = getMonthDays(m) * 24 * 60 * 60 * 1000
            // daysTimeLength / timeLength = curWidth / this.contentWidth
            const curWidth = (daysTimeLength / allTimeLength) * this.contentWidth
            return curWidth + 'px'
        }
    },
    mounted() {
        this.contentWidth = this.$refs.table.clientWidth - 150
    }
}
</script>
0