效果图

img1

在线预览

GitHub链接(包含 knockoutJS 版本与 Vue 版本)

推荐组合效果

  • 推荐与双表头固定效果组合,实现如上例中横表头(日期)纵向固定,纵表头(类型)横向固定效果。
  • 参照连接 表头固定,表身滚动实例

Vue.js 引入

<script src="https://cdn.bootcss.com/vue/2.6.10/vue.js"></script>

CSS 代码

<style>
    table {
        border-collapse: collapse;
        border-width: 1;
    }

    p {
        width: 100px;
    }

    th,
    td {
        min-width: 20px;
        text-align: center;
        border-width: 1px;
        border-style: solid;
        border-color: #666;
    }

    th {
        background-color: #34B4A0;
    }

    span {
        border-radius: 50%;
        height: 10px;
        width: 10px;
        display: block;
        display: inline-block;
    }

    .yellow {
        background-color: #F9A100;
        border: #FCAA00 1px solid;
    }

    .green {
        background-color: #34B4A0;
        border: #46D8BC 1px solid;
    }

    .red {
        background-color: #FF0D0D;
        border: #FF2E2E 1px solid
    }

    .purple {
        background-color: #8B64FF;
        border: #8B64FF 1px solid;
    }

    .blue {
        background-color: #299CED;
        border: #299CED 1px solid;
    }
</style>

vue 页面

<div id="demo">
    <table>
        <caption>Vue.JS 制作甘特图</caption>
        <thead>
            <tr>
                <th rowspan="2" colspan="2">项目/时间</th>
                <th :colspan="days(day)" v-for="(day, index) in AllMonths">{{day|longDateToMonth}}</th>
            </tr>
            <tr>
                <th v-for="(day, index) in Dateinterval">{{day|longDateToDay}}</th>
            </tr>
        </thead>
        <tbody>
            <tr v-for="(item, index) in jsonData">
                <td :rowspan="colNum(item, index)" v-if="showFormType(item, index)">
                    <p>
                        {{item.formType}}
                    </p>
                </td>
                <td>
                    <p>{{item.type}}</p>
                </td>
                <td v-for="day in Dateinterval">
                    <span :class="spanShow(item.value, day).color" v-show="spanShow(item.value, day).show"></span>
                </td>
            </tr>
        </tbody>
    </table>
</div>

javascript 代码

<script>
    /**
     * 日期格式化。
     * 小于 10 的数字在前面 + 0。如 01、06
     * */
    var fDate = function (value) {
        return value < 10 ? '0' + value : value;
    }
    /**
     * 日期格式化:yyyy-mm-dd
     * */
    Date.prototype.format = function () {
        return this.getFullYear() + '-' + fDate(this.getMonth() + 1) + '-' + fDate(this.getDate());
    };

    /**
     * 获取指定时间段内所有天。
     * 输入起止日期。格式:yyyy-mm-dd。
     * 返回格式:yyyy-mm-dd
     * */
    var getAllDay = function (startDate, endDate) {
        var allDays = [];
        var a = startDate.split('-');
        var b = endDate.split('-');
        var uDb = new Date().setUTCFullYear(a[0], a[1] - 1, a[2]); //start
        var uDe = new Date().setUTCFullYear(b[0], b[1] - 1, b[2]); //end
        for (var i = uDb; i <= uDe; i = i + 24 * 60 * 60 * 1000) {
            allDays.push(new Date(parseInt(i)).format());
        }
        return allDays;
    };
    /**
     * 获取指定时间段内所有月。
     * 输入起止日期。格式:yyyy-mm-dd。
     * 返回格式:yyyy-mm
     * */
    var getAllMonth = function (startDate, endDate) {
        var allMonth = [];
        var a = startDate.split('-');
        var b = endDate.split('-');
        var uDb = new Date().setUTCFullYear(a[0], a[1] - 1, a[2]); //start
        var uDe = new Date().setUTCFullYear(b[0], b[1] - 1, new Date(b[0], b[1], 0).getDate()); //结束月份设置为指定月最后一天
        while (uDb <= uDe) { // 等于号防止 31 号计算遗漏
            allMonth.push(new Date(uDb).getFullYear() + '-' + fDate(new Date(uDb).getMonth() + 1));
            uDb = new Date(uDb).setMonth(new Date(uDb).getMonth() + 1);
        }
        return allMonth;
    };
</script>
<script>
    /**
     * 创建 Vue 实例
     * */
    var demo = new Vue({
        el: '#demo',
        data: {
            /**
             * 数据源 JSON 对象数组
             * 格式:
             * {
             *  startDate:'',        //开始日期(表头所用)
             *  endDate:''           //结束日期(表头所用)
             *  data:[{
             *      formType:'',        //数据类型1。可自动进行行合并
             *      type:'',            //数据类型2。如需行合并需参照类型1自行优化
             *      value:[{            //甘特图所需数据
             *              fromDate:'',    //开始时间
             *              toDate:'',      //截止时间
             *              spanClass:''    //该段时间内的样式
             *          },{
             *              //允许有多组时间段。
             *          }]
             *      }]
             *  }
             * 
             * */
            jsonData: sourceData.data,
            startDate: sourceData.startDate,
            endDate: sourceData.endDate,
            /*获取指定时间段内所有月,用作表头一*/
            AllMonths: getAllMonth(sourceData.startDate, sourceData.endDate),
            /*获取指定时间段内所有天,用作表头二*/
            Dateinterval: getAllDay(sourceData.startDate, sourceData.endDate),
        },
        methods: {
            /**
             * 甘特图显示状况
             * 传入参数:
             *      1、甘特图所需数据数组。即上述 JSON 对象的 value 数组
             *      2、当前日期,即此刻对应表头中的日期列。
             *  */
            spanShow: function (valArray, currentDate) {
                /*默认不显示*/
                var show = false;
                /*默认样式颜色为空*/
                var color = "";
                /*调用 Vue 过滤器*/
                var filter = this.$options.filters['strDateToTimeStamp'];
                /*循环 value 数据。判断当前对应日期是否位于该组 value 数据的 fromdate 与 todate 之间。*/
                for (var i = 0; i < valArray.length; i++) {
                    var inInterval = false;
                    inInterval = filter(valArray[i].fromDate) <= filter(currentDate) && filter(
                        currentDate) <= filter(valArray[i].toDate)
                    if (inInterval) {
                        color = valArray[i].spanClass;
                    }
                    show = show || inInterval;
                }
                /*返回判断结果与当前日期在该组数据下对应的样式颜色*/
                return {
                    show: show,
                    color: color
                };
            },
            /**
             * 计算指定时间段内指定月份的总天数。
             * 这里需要根据指定月的总天数确定合并表头一时跨列的数量
             * 传入参数:指定月份。格式 yyyy-mm
             * */
            days: function (strDate) {
                debugger;
                var sArr = this.startDate.split('-');
                var eArr = this.endDate.split('-');
                var arr = strDate.split('-');
                //指定月份不在指定时间段内
                if ('' + arr[0] + arr[1] < '' + sArr[0] + sArr[1] || '' + arr[0] + arr[1] > '' + eArr[0] +
                    eArr[1]) {
                    return 0;
                }
                //指定月份等于开始时间段的月份时,返回本月剩余天数 = 本月最后一天 - 开始日期 + 1
                if ('' + arr[0] + arr[1] === '' + sArr[0] + sArr[1]) {
                    return new Date(arr[0], arr[1], 0).getDate() - new Date(sArr[0] + '-' + sArr[1] + '-' +
                            sArr[2])
                        .getDate() + 1;
                }
                //指定月份等于结束时间段的月份时,返回时间段内本月有效天数 = 结束日期
                if ('' + arr[0] + arr[1] === '' + eArr[0] + eArr[1]) {
                    return new Date(eArr[0] + '-' + eArr[1] + '-' + eArr[2]).getDate()
                }
                return new Date(arr[0], arr[1], 0).getDate(); //取当前月的最后一天,即本月天数
            },
            /**
             * 表单类型显示状态。
             * 即第一列。
             * */
            showFormType: function (item, index) {
                /*与下一组数据的类型相同时,不显示*/
                var show = true;
                if (index > 0 && item.formType == this.jsonData[index - 1]
                    .formType) {
                    show = false;
                }
                return show;
            },
            /**
             * 表单类型需要跨行的数量。
             * 用于自动合并第一列的类型
             * */
            colNum: function (item, index) {
                var iCount = 0;
                if (!(index > 0 && item.formType == this.jsonData[index - 1]
                        .formType)) {
                    this.jsonData.forEach(ele => {
                        if (item.formType == ele.formType) {
                            iCount++;
                        }
                    });
                }
                return iCount;
            }
        },
        /*Vue 过滤器用于格式化日期*/
        filters: {
            /*字符串格式的日期转为时间戳*/
            strDateToTimeStamp: function (strDate) {
                return Date.parse(strDate);
            },
            /*字符串格式的日期转为天。yyyy-mm-dd  -->  dd*/
            longDateToDay: function (longDate) {
                return fDate(new Date(Date.parse(longDate)).getDate());
            },
            /*字符串格式的日期转为月。yyyy-mm-dd  -->  mm*/
            longDateToMonth: function (longDate) {
                return fDate(new Date(Date.parse(longDate)).getMonth() + 1);
            }
        }
    })
</script>

JSON 实例数据(为了方便,直接写在JS中)

<script>
        var sourceData = {
        'startDate': '2018-12-01',
        'endDate': '2019-01-31',
        'data': [{
                "formType": "DFM",
                "type": "计划",
                "value": [{
                    "fromDate": "2018-12-01",
                    "toDate": "2018-12-06",
                    "spanClass": "green"
                }, {
                    "fromDate": "2018-12-10",
                    "toDate": "2018-12-11",
                    "spanClass": "green"
                }, {
                    "fromDate": "2018-12-15",
                    "toDate": "2018-12-16",
                    "spanClass": "green"
                }]
            },
            {
                "formType": "DFM",
                "type": "实际",
                "value": [{
                    "fromDate": "2018-12-01",
                    "toDate": "2018-12-07",
                    "spanClass": "red"
                }, {
                    "fromDate": "2018-12-10",
                    "toDate": "2018-12-11",
                    "spanClass": "blue"
                }, {
                    "fromDate": "2018-12-15",
                    "toDate": "2018-12-16",
                    "spanClass": "blue"
                }]
            }, {
                "formType": "DFM",
                "type": "客户回复",
                "value": [{
                    "fromDate": "2018-12-09",
                    "toDate": "2018-12-09",
                    "spanClass": "purple"
                }, {
                    "fromDate": "2018-12-14",
                    "toDate": "2018-12-14",
                    "spanClass": "purple"
                }, {
                    "fromDate": "2018-12-18",
                    "toDate": "2018-12-18",
                    "spanClass": "purple"
                }]
            },

            {
                "formType": "开模检讨",
                "type": "实际",
                "value": [{
                    "fromDate": "2018-12-20",
                    "toDate": "2018-12-20",
                    "spanClass": "yellow"
                }]
            },

            {
                "formType": "模具开发",
                "type": "计划",
                "value": [{
                    "fromDate": "2018-12-19",
                    "toDate": "2019-01-10",
                    "spanClass": "green"
                }]
            },
            {
                "formType": "模具开发",
                "type": "实际",
                "value": [{
                    "fromDate": "2018-12-19",
                    "toDate": "2019-01-13",
                    "spanClass": "red"
                }]
            }, {
                "formType": "设计",
                "type": "计划",
                "value": [{
                    "fromDate": "2018-12-19",
                    "toDate": "2018-12-21",
                    "spanClass": "green"
                }]
            }, {
                "formType": "设计",
                "type": "实际",
                "value": [{
                    "fromDate": "2018-12-19",
                    "toDate": "2018-12-21",
                    "spanClass": "yellow"
                }]
            }, {
                "formType": "加工",
                "type": "计划",
                "value": [{
                    "fromDate": "2018-12-22",
                    "toDate": "2019-01-06",
                    "spanClass": "green"
                }]
            },
            {
                "formType": "加工",
                "type": "客户回复",
                "value": [{
                    "fromDate": "2018-12-22",
                    "toDate": "2019-01-08",
                    "spanClass": "purple"
                }]
            },
            {
                "formType": "加工",
                "type": "实际",
                "value": [{
                    "fromDate": "2018-12-22",
                    "toDate": "2019-01-09",
                    "spanClass": "red"
                }]
            },
        ],
    };
</script>
Logo

前往低代码交流专区

更多推荐