vue仿甘特图开发工程施工进度表

news2024/11/14 13:47:26

前言

	本文是根据项目实际开发中一个需求开发的demo,仅用了elementUI,可当作独立组件使用,C V即用。
当然没考虑其他的扩展性和一些数据的校验,主要是提供一个处理思路,有需要的小伙伴可以直接复制;
	本demo的思路是根据开始时间和结束时间动态生成工程时间表,再根据工程的计划开始和结束日期、
实际开始和结束日期再对应单元格生成进度条,最后根据完成进度百分比计算红色进度条。

一、demo成品图

在这里插入图片描述
表格使用的是elementUI,表头是动态的,根据开始日期的年月和结束时间的年月计算获取;
单元格第一行绿色进度条是计划工程进度,第二行绿色是实际功能进度条,红色是实际进度的百分比

二、代码

<template>
  <div class="app-container">
    <el-table :data="tableData" style="width: 100%">
      <el-table-column label="名称" prop="name" width="200"></el-table-column>
      <el-table-column align="center" v-for="(months, year) in dateList" :key="year"  :label="`${year}年`">
        <el-table-column v-for="month in months" align="center" width="100" :key="month" :label="`${month}月`">
          <template slot-scope="scope">
            <div class="process-box" v-if="scope.row.plan_process[year] && scope.row.plan_process[year].includes(month) || scope.row.actual_process[year] && scope.row.actual_process[year].includes(month)">
              <div class="plan-process" v-if="scope.row.plan_process[year] && scope.row.plan_process[year].includes(month)">
                <div class="item" v-if="scope.row.planSameYearMonth" :style="scope.row.planProcessStyle"></div>
                <div v-else>
                  <div class="item start" v-if="scope.row.plan_start.Y == year && scope.row.plan_start.M == month" :style="scope.row.plan_start.itemStyle"></div>
                  <div class="item end" v-if="scope.row.plan_end.Y == year && scope.row.plan_end.M == month" :style="scope.row.plan_end.itemStyle"></div>
                  <div class="item" v-if="!(scope.row.plan_start.Y == year && scope.row.plan_start.M == month || scope.row.plan_end.Y == year && scope.row.plan_end.M == month)"></div>
                </div>
              </div>
              <div class="actual-process" v-if="scope.row.actual_process[year] && scope.row.actual_process[year].includes(month)">
                <div class="item" v-if="scope.row.actualSameYearMonth" :style="scope.row.actualProcessStyle">
                  <div class="percent_item start" v-if="scope.row.percentSameYearMonth" :style="scope.row.percentProcessStyle"></div>
                </div>
                <div class="item-box" v-else>
                  <div class="item start" v-if="scope.row.actual_start.Y == year && scope.row.actual_start.M == month" :style="scope.row.actual_start.itemStyle">
                    <div class="percent_item start" v-if="scope.row.percent_process[year] && scope.row.percent_process[year].includes(month) && scope.row.percent_start.Y == year && scope.row.percent_start.M == month" :style="scope.row.percent_start.itemStyle"></div>
                  </div>
                  <div class="item end" v-if="scope.row.actual_end.Y == year && scope.row.actual_end.M == month" :style="scope.row.actual_end.itemStyle">
                    <div class="percent_item end" v-if="scope.row.percent_process[year] && scope.row.percent_process[year].includes(month) && scope.row.percent_end.Y == year && scope.row.percent_end.M == month" :style="scope.row.percent_end.itemStyle"></div>
                  </div>
                  <div class="item" v-if="!(scope.row.actual_start.Y == year && scope.row.actual_start.M == month || scope.row.actual_end.Y == year && scope.row.actual_end.M == month)">
                    <div class="percent_item" v-if="scope.row.percent_process[year] && scope.row.percent_process[year].includes(month) && scope.row.percent_end.Y == year && scope.row.percent_end.M == month" :style="scope.row.percent_end.itemStyle"></div>
                    <div class="percent_item" v-if="scope.row.percent_process[year] && scope.row.percent_process[year].includes(month) && !(scope.row.percent_end.Y == year && scope.row.percent_end.M == month)"></div>
                  </div>
                </div>
              </div>
            </div>
            
          </template>
        </el-table-column>
      </el-table-column>
    </el-table>
  </div>
</template>
<script>
export default {
  data() {
    return {
      tableData: [
        {
          name: "单位A施工期间",
          plan_start_time: "2023-02-1",
          plan_end_time: "2023-2-28",
          actual_start_time: "2023-2-7",
          actual_end_time: "2023-6-22",
          percent: 85,
        },
        {
          name: "单位B施工期间",
          plan_start_time: "2023-07-12",
          plan_end_time: "2024-01-12",
          actual_start_time: "2023-11-10",
          actual_end_time: "2024-01-10",
          percent: 76,
        }
      ],
      dateList: {},
    }
  },
  mounted(){
    this.initTableData("2023-01-12", "2025-01-30")
  },
  methods: {
    handleDate(date) {
      let monthHasDay = 30;
      let currentDate = new Date(date)
      let day = currentDate.getDate()
      let month = currentDate.getMonth() + 1;
      let year = currentDate.getFullYear();
      if ([1, 3, 5, 7, 8, 10, 12].includes(month)) {
        monthHasDay = 31
      } else {
        if (month === 2) {
          if ((year % 400 === 0) || (year % 4 === 0 && year % 100 !== 0)) {
            monthHasDay = 29;
          } else {
            monthHasDay = 28;
          }
        } else {
          monthHasDay = 30;
        }
      }
      return {d: day, M: month, Y: year, monthHasDay: monthHasDay}
    },
    getDataBetweenDates(startTime, endTime){
      let start = this.handleDate(startTime);
      let end = this.handleDate(endTime);
      let data = {}
      data[start.Y] = [];
      data[end.Y] = [];
      let year = end.Y - start.Y
      if (year === 0) {
        for(let m = start.M; m <= end.M; m++) {
          data[start.Y].push(m)
        }
      } else if (year === 1) {
        for(let m = start.M; m <= 12; m++) {
          data[start.Y].push(m)
        }
        for(let n = 1; n <= end.M; n++) {
          data[end.Y].push(n)
        }
      } else {
        for(let m = start.M; m <= 12; m++) {
          data[start.Y].push(m)
        }
        for(let mid = 1; mid < year; mid++) {
          data[start.Y + mid] = [1,2,3,4,5,6,7,8,9,10,11,12];
        }
        for(let n = 1; n <= end.M; n++) {
          data[end.Y].push(n)
        }
      }
      return data;
    },
    getDaysBetweenDates(startTime, endTime) {
      let d1 = new Date(startTime);
      let d2 = new Date(endTime);
      let timeDiff = Math.abs(d2.getTime() - d1.getTime());
      let days = Math.ceil(timeDiff / (1000 * 3600 * 24));
      return days;
    },
    handleDateStyle(startDate, endDate){
      let start = this.handleDate(startDate)
      let end = this.handleDate(endDate);
      let sameYearMonth = false;
      let processStyle = null;
      if (end.Y === start.Y && end.M === start.M) {
        processStyle = {
          "left": ((start.d - 1) * 100 / start.monthHasDay) + "%",
          "right": ((start.monthHasDay -  end.d) * 100 / start.monthHasDay) + "%",
          "border-radius": '4px'
        }
        if (end.d > start.monthHasDay) processStyle.right = 0
        sameYearMonth = true
      } else {
        start.itemStyle = {
          "left": ((start.d + 1)  * 100 / start.monthHasDay)  + "%",
          "right": 0
        }
        end.itemStyle = {
          "left": 0,
          "right": ((start.monthHasDay -  (end.d + 1))  * 100 / start.monthHasDay)  + "%"
        }
      }
  
      return {
        start: start,
        end: end,
        sameYearMonth: sameYearMonth,
        processStyle: processStyle
      }
    },
    handlePercentDateStyle(actualStartTime, actualEndTime, percent){
      let start = this.handleDate(actualStartTime)
      let end = this.handleDate(actualEndTime);
      let days = this.getDaysBetweenDates(actualStartTime, actualEndTime)
      let percentTime = new Date(actualStartTime).getTime() +  days * percent * 24 * 36000
      let percentProcess = this.getDataBetweenDates(actualStartTime, percentTime)
      let startBorderRadius = '4px 0 0 4px' 
      let endBorderRadius = '0 4px 4px 0'
      let percentDate = this.handleDate(percentTime)
      let sameYearMonth = false;
      let processStyle = null;
      if (end.Y === start.Y) {
        if (end.M === start.M) {
          processStyle = {
            "left": 0,
            "right": ((end.d -  (percentDate.d)) * 100 / end.d) + "%",
            "border-radius": '4px'
          }
          sameYearMonth = true
        } else {
          if(percentDate.M === start.M) {
            start.itemStyle = {
              "left": 0,
              "right": ((start.monthHasDay -  percentDate.d)  * 100 / (start.monthHasDay - start.d))  + "%",
              "border-radius": '4px'
            }
            percentDate.itemStyle = {
              "left": 0,
              "right": ((start.monthHasDay -  percentDate.d)  * 100 / start.monthHasDay)  + "%",
              "border-radius": '4px'
            }
          } else if (percentDate.M > start.M &&  percentDate.M < end.M) {
            start.itemStyle = {
              "left": 0,
              "right": 0,
              "border-radius": startBorderRadius
            }
            percentDate.itemStyle = {
              "left": 0,
              "right": ((percentDate.monthHasDay - percentDate.d)  * 100 / percentDate.monthHasDay)  + "%",
              "border-radius": endBorderRadius
            }
          } else if (percentDate.M === end.M) {
            start.itemStyle = {
              "left": 0,
              "right": 0,
              "border-radius": startBorderRadius
            }
            percentDate.itemStyle = {
              "left": 0,
              "right": ((end.d -  percentDate.d)  * 100 / end.d)  + "%",
              "border-radius": endBorderRadius
            }
          }
        }
      } else {
        
        if (percentDate.M === start.M) {
          start.itemStyle = {
            "left": 0,
            "right": ((start.monthHasDay -  percentDate.d) * 100 / (start.monthHasDay - start.d)) + "%",
            "border-radius": '4px'
          }
        } else if (percentDate.M === end.M && percentDate.Y === end.Y) {
          start.itemStyle = {
            "left": 0,
            "right": 0,
            "border-radius": startBorderRadius
          }
          percentDate.itemStyle = {
            "left": 0,
            "right": ((end.d -  percentDate.d)  * 100 / end.d)  + "%",
            "border-radius": endBorderRadius
          }
        } else {
          start.itemStyle = {
            "left": 0,
            "right": 0,
            "border-radius": startBorderRadius
          }
          percentDate.itemStyle = {
            "left": 0,
            "right": ((percentDate.monthHasDay - percentDate.d)  * 100 / percentDate.monthHasDay)  + "%",
            "border-radius": endBorderRadius
          }
        }
      }
   
      return {
        start: start,
        end: percentDate,
        sameYearMonth: sameYearMonth,
        processStyle: processStyle,
        percentProcess: percentProcess
      }
    },
    initTableData(startTime, endTime){
      this.dateList = this.getDataBetweenDates(startTime, endTime);
      
      this.tableData.map(item => {
        item.plan_process = this.getDataBetweenDates(item.plan_start_time, item.plan_end_time);
        item.actual_process = this.getDataBetweenDates(item.actual_start_time, item.actual_end_time);
        let dateStyle = this.handleDateStyle(item.plan_start_time,item.plan_end_time) ;
        item.planSameYearMonth = dateStyle.sameYearMonth;
        item.planProcessStyle = dateStyle.processStyle ? dateStyle.processStyle : '';
        item.plan_start = dateStyle.start;
        item.plan_end = dateStyle.end;
        let actualDateStyle = this.handleDateStyle(item.actual_start_time,item.actual_end_time);
        item.actualSameYearMonth = actualDateStyle.sameYearMonth;
        item.actualProcessStyle = actualDateStyle.processStyle ? actualDateStyle.processStyle : '';
        item.actual_start = actualDateStyle.start;
        item.actual_end = actualDateStyle.end;
        let percentDateStyle = this.handlePercentDateStyle(item.actual_start_time, item.actual_end_time, item.percent);
        item.percent_start = percentDateStyle.start;
        item.percent_end = percentDateStyle.end;
        item.percentProcessStyle = percentDateStyle.processStyle ? percentDateStyle.processStyle : '';
        item.percentSameYearMonth = percentDateStyle.sameYearMonth;
        item.percent_process = percentDateStyle.percentProcess
        console.log(item)

      })
    },
    
  },
}
</script>
<style lang="scss" scoped>
::v-deep .el-table td.el-table__cell div{
  padding: 0;
}
.process-box{
  width: 100px;
  height: 40px;
  position: relative;
  .plan-process{
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
    height: 15px;
  }
  .actual-process{
    position: absolute;
    top: 25px;
    left: 0;
    right: 0;
    height: 15px;
  }
  .percent_item{
    position: absolute;
    height: 15px;
    left: 0;
    right: 0;
    background-color: red;
  }

}
.item {
  position: absolute;
  left: 0;
  right: 0;
  background: greenyellow;
  height: 15px;
  &.start{
    border-radius: 4px 0 0 4px;
  }
  &.end{
    border-radius: 0 4px 4px 0 ;
  }
}

</style>

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1931393.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

python-区间内的真素数(赛氪OJ)

[题目描述] 找出正整数 M 和 N 之间&#xff08;N 不小于 M&#xff09;的所有真素数。真素数的定义&#xff1a;如果一个正整数 P 为素数&#xff0c;且其反序也为素数&#xff0c;那么 P 就为真素数。 例如&#xff0c;11&#xff0c;13 均为真素数&#xff0c;因为 11 的反序…

open3d:随机采样一致性分割平面

1、背景介绍 随机采样一致性算法&#xff08;RANSAC Random Sample Consensus&#xff09;是一种迭代的参数估计算法&#xff0c;主要用于从包含大量噪声数据的样本中估计模型参数。其核心思想是通过随机采样和模型验证来找到数据中最符合模型假设的点。因此&#xff0c;只要事…

python--实验13 异常处理

目录 知识点 异常概述 异常类 异常处理 捕获简单异常 原理 示例 异常处理except 原理及代码格式 try工作原理 标记程序上下文&#xff0c;异常发生时跳转 代码示例 终止行为finally 抛出异常raise 原理 代码格式 示例 自定义异常 原理 示例 断言assert 判…

Java web从入门到精通 (第 2版)中文电子版

前言 《Java Web从入门到精通&#xff08;第2版&#xff09;》共分21章&#xff0c;包括Java Web应用开发概述、HTML与CSS网页开发基础、JavaScript脚本语言、搭建开发环境、JavaBean技术、Servlet技术、过滤器和监听器、Hibernate高级应用、Java Web的数据库操作、EL&#xf…

防火墙双机备份及带宽限制

一&#xff0c;实验拓扑&#xff1a; 二&#xff0c;实验需求&#xff1a; 1&#xff0c;对现有网络进行改造升级&#xff0c;将当个防火墙组网改成双机热备的组网形式&#xff0c;做负载分担模式&#xff0c;游客区和DMZ区走FW4&#xff0c;生产区和办公区的流量走FW2 2&…

C#(asp.net)校园宿舍管理系统-计算机毕业设计源码18150

摘 要 随着高等教育的普及&#xff0c;校园宿舍管理成为学校管理的重要组成部分。传统的宿舍管理方式存在效率低下、信息不透明等问题。同时&#xff0c;在信息技术的不断发展和应用的背景下&#xff0c;校园管理信息化已成为学校管理的重要趋势。因此&#xff0c;研究基于Asp.…

【贪心算法】贪心算法30题

一、贪心算法简介 证明贪心策略正确性的常用方法&#xff1a;直接证明、交换论证法、反证法、分类讨论… 二、相关编程题 2.1 柠檬水找零 题目链接 860. 柠檬水找零 - 力扣&#xff08;LeetCode&#xff09; 题目描述 算法原理 提示&#xff1a;最优解和贪心解唯一可能不同…

netxduo http server 创建回复以及json解析

我们今天要整http的response,比如我创建的http server,我对它发送了一个POST,然后服务器解析出json里的body,再回复过去。今天会用到json的解析库cjson以及postman去发送消息。这次用nx_web_http_server.h这个库,不用之前的nx_http_server.h 本教程在最后附带app_netxduo…

# ffmpeg 工具使用

文章目录 ffmpeg 工具使用1.图片转换2.图片转视频3.截取片段4. 视频格式转换5. 视频转图片方法一&#xff1a;方法二&#xff1a;生成更清晰无压缩的图片方式&#xff1a; ffmpeg 工具使用 windows安装教程&#xff1a;https://blog.csdn.net/csdn_yudong/article/details/129…

3D 渲染一个房屋需要多长时间?

3D 渲染一个房屋总共需要 6-10 个工作日&#xff0c;主要取决于项目的复杂性和最终 3D 渲染的质量&#xff0c;图像越逼真&#xff0c;效果图渲染所需时间就越长。 1.3D建模 创建 3D 模型是第一步&#xff0c;所需时间可能因项目的复杂程度而有很大差异。一个简单的住宅渲染可…

FreeCAD源码分析:Serialization

序列化(Serialization)是在程序运行时将对象转换成字节流的过程&#xff0c;进而将二进制字节流罗盘到文件系统或者通过网络发送到远端。而反序列化(Deserialization)则恰恰相反&#xff0c;是由二进制字节流重新构建对象的过程。 Ref. from Boost Serialization Here, we use …

STL—string类—模拟实现

STL—string类—模拟实现 熟悉了string的结构和各自接口的使用之后&#xff0c;现在就要尝试去模拟实现string类 这个string类为了避免和我们库里的string类冲突&#xff0c;因此我们需要定义一个自己的命名空间 namespace wzf {class string{public://成员函数private://成…

.NET 8+Vue2 部署到Window Server

.NET 8Vue2 部署到Window Server 1 配置环境 1.1 下载安装.NET 8 SDK&#xff0c;下载时需注意目标服务器的系统类型&#xff08;64位 or 32位&#xff09; https://dotnet.microsoft.com/zh-cn/download 1.2 下载安装SQL Server数据库&#xff08;服务和管理工具&#xff…

LabVIEW工业设备姿态监测系统

开发了一种基于LabVIEW的工业设备姿态监测系统&#xff0c;针对现有监测设备在适应性和反应时间上的不足&#xff0c;采用了LabVIEW软件和STM32微控制器&#xff0c;通过高精度姿态传感器实现了对设备姿态的快速准确监测&#xff0c;大大提高了工业作业的安全与效率。 项目背景…

C++解决:书本信息储存

书本信息储存 暂无标签 时间限制&#xff1a;C/C 1000MS&#xff0c;其他语言 2000MS 内存限制&#xff1a;C/C 256MB&#xff0c;其他语言 512MB 难度&#xff1a;简单 描述 创建一个储存书本信息的结构体&#xff0c;包括书号&#xff0c;名称&#xff0c;作者&#xff0…

CS110L(Rust)

1.Rust 语法总结 数值类型 有符号整数: i8, i16, i32, i64无符号整数: u8, u16, u32, u64 变量声明 声明变量: let i 0; // 类型推断let n: i32 1; // 显式类型声明 可变变量: let mut n 0; n n 1; 字符串 注意&#xff0c;let s: str "Hello world";…

Android Media3 技术应用详解

1、音视频基础 一个简单的音视频链路如下&#xff1a; 1&#xff09;采集&#xff0c;音视频经过采集后分别生成音频流和视频帧&#xff0c;音频是流式的物理上没有帧的概念&#xff0c;但为了数据处理的方便实际数据处理中引入了音频帧的概念&#xff0c;一般中间插入静音数据…

Hive 函数

分类 Hive 的函数分为两大类&#xff1a;内置函数&#xff08;Built-in-Functions&#xff09;、用户自定义函数&#xff08;User-Defined-Functions&#xff09;&#xff1b;内置函数可分为&#xff1a;数值类型函数、日期类型函数、字符串类型函数、集合函数等&#xff1b;用…

系统架构设计师教程 第3章 信息系统基础知识-3.1 信息系统概述

系统架构设计师教程 第3章 信息系统基础知识-3.1 信息系统概述 3.1.1 信息系统的定义3.1.1.1 信息系统3.1.1.2 信息化3.1.2 信息系统的发展3.1.2.1 初始阶段3.1.2.2 传播阶段3.1.2.3 控制阶段3.1.2.4 集成阶段3.1.2.5 数据管理阶段3.1.2.6 成熟阶段3.1.3 信息系统的分类3.…

车载音视频App框架设计

简介 统一播放器提供媒体播放一致性的交互和视觉体验&#xff0c;减少各个媒体应用和场景独自开发的重复工作量&#xff0c;实现媒体播放链路的一致性&#xff0c;减少碎片化的Bug。本文面向应用开发者介绍如何快速接入媒体播放器。 主要功能&#xff1a; 新设计的统一播放U…