开发背景
工作需要封装一个管理进度/任务的组件,主要是如下需求:
- 能根据任务的具体时间来展示不同任务块的长度
- 每个任务下支持children(子任务,树结构)
- 能根据任务状态(已完成/未完成)展示不同颜色
- 有可缩放的时间轴(类似于一个可变长度的滑块)
- 顶部滑块部分要有刻度(精确到x月x日)
解决方案
- 每一行的格子都自行处理:width、border(会叠加)等
- 若采用table布局,单元格宽度自适应,只需要处理第一行宽度,border也不会叠加
组件结构
主要是三部分:
- 顶部滑块
- 中间的格子
- 每一行的树结构
示例代码
仅记录核心部分示意代码,主要记录几个注意的点:
new Date().getTime()
方法获取的并不是当天 24 点的时间戳,非完整的24小时- 绑定事件时,注意
move
和up
绑定到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