[Vue3]简易时间轴(能根据时间长短计算阶段长度)

[Vue3]简易时间轴(能根据时间长短计算阶段长度)

<template>
    <div ref="container" class="timeline-container">
        <!--     计划部分   -->
        <div class="plan-box">
            <span class="plan-title">计划</span>
            <!--    计划时间轴    -->
            <div class="plan-timeline">
                <!--                占的宽度要经过计算-->
                <div class="timeline-progress" ref="planProgressRef" :style="{ ...planProgressStyle }"></div>

                <!-- 时间轴上的时间节点 -->
                <template v-for="(node, index) in planNodeList">
                    <div class="timeline-item" :style="{ left: node.pass * 100 + '%' }">
                        <div class="timeline-item-content">
                            <p>{{ node.name }}</p>
                            <p>{{ node.time }}</p>
                        </div>
                    </div>
                </template>
            </div>
        </div>

        <!--   实际部分  -->
        <div class="actual-box">
            <span class="actual-title">实际</span>
            <div class="actual-timeline">
                <!--                占的宽度要经过计算-->
                <div class="timeline-progress" ref="actualProgressRef" :style="{ ...actualProgressStyle }"></div>

                <!-- 时间轴上的时间节点 -->
                <template v-for="(node, index) in actualNodeList">
                    <div class="timeline-item" :style="{ left: node.pass * 100 + '%' }">
                        <div class="timeline-item-content">
                            <p>{{ node.name }}</p>
                            <p>{{ node.time }}</p>
                        </div>
                    </div>
                </template>
            </div>
        </div>

        <!--            <svg viewBox="25 25 50 50" class="circular small">-->
        <!--                <circle cx="50" cy="50" r="20" fill="none" class="path"></circle>-->
        <!--            </svg>-->
    </div>
</template>

<script setup>
import dayjs from 'dayjs';

import minMax from 'dayjs/plugin/minMax';
import { onMounted, toRefs, ref } from 'vue';

dayjs.extend(minMax);
const props = defineProps({
    planNodeList: {
        type: Array,
        default: [
            {
                name: 'name1',
                time: '2021-05-01'
            },
            {
                name: 'name2',
                time: '2021-08-01'
            },
            {
                name: 'name3',
                time: '2022-02-01'
            }
        ]
    },

    actualNodeList: {
        type: Array,
        default: [
            {
                name: 'name1',
                time: '2021-05-02'
            },
            {
                name: 'name2',
                time: '2021-08-01'
            },
            {
                name: 'name3',
                time: '2022-03-01'
            }
        ]
    }
});

const { planNodeList, actualNodeList } = toRefs(props);

const persistentTime = ref(0);
const planProgressRef = ref();
const minTime = ref('');
const maxTime = ref('');

// 计划进度条的属性
const planProgressStyle = ref({
    width: 0,
    marginLeft: 0
});

// 实际进度条的属性
const actualProgressStyle = ref({
    width: 0,
    marginLeft: 0
});

// 传入节点列表,获取最早时间,最晚时间,持续的时间
const getPersistentTimeByNodes = nodeList => {
    const allNode = [...nodeList].map(node => {
        return dayjs(node['time']);
    });

    const currentMaxTime = dayjs.max(allNode);
    const currentMinTime = dayjs.min(allNode);

    return [currentMinTime, currentMaxTime, dayjs(currentMaxTime).diff(currentMinTime, 'day')];
};

// 通过日期计算从开始时间算起,在整个阶段里过去了百分之x的时间
const getRateByDate = time => {
    // let num = 0;
    // if (Object.prototype.toString.call(time) === '[object String]') {
    //     num = dayjs(time).diff(minTime.value, 'day') / persistentTime.value;
    // } else {
    //     num = time.diff(minTime.value, 'day') / persistentTime.value;
    // }
    const num = dayjs(time).diff(minTime.value, 'day') / persistentTime.value;
    return Math.floor(num * 1000) / 1000;
};

// 计算一个阶段的Width和最早时间的marginLeft
const calculateProgress = nodeList => {
    let width = 0;
    let marginLeft = 0;
    const [startTime, endTime, currentPersistentTime] = getPersistentTimeByNodes(nodeList);
    console.log('calculateProgress');
    console.log(startTime, endTime, currentPersistentTime);
    // planProgressStyle.value.width = ''
    // planProgressStyle.value.marginLeft = ''

    const rate1 = getRateByDate(startTime);
    const rate2 = getRateByDate(endTime);

    width = (rate2 - rate1) * 100 + '%';
    marginLeft = rate1 * 100 + '%';

    return {
        width,
        marginLeft
    };
};

const init = () => {
    const allNodeList = [...planNodeList.value, ...actualNodeList.value];
    // // -----------------------------先获取最早时间和最晚时间-----------------------------
    // const allDateList = allNodeList.map(node => {
    //     return dayjs(node['time']);
    // });
    // maxTime.value = dayjs.max(allDateList);
    // minTime.value = dayjs.min(allDateList);

    // -----------------------------------总持续时间-------------------------------
    [minTime.value, maxTime.value, persistentTime.value] = getPersistentTimeByNodes(allNodeList);
    console.log('总持续时间');
    console.log(persistentTime.value);

    // -------------------------------对每个节点 计算过去了多长时间-------------------------------
    const allNode = [...planNodeList.value, ...actualNodeList.value];
    allNode.forEach(node => {
        node.pass = getRateByDate(node['time']);
    });

    console.log('整理完数据后得到的结果');
    console.log(allNode);

    // -------------------------------计划持续时间-------------------------------

    planProgressStyle.value = calculateProgress(planNodeList.value);
    console.log('计划时间计算结果');
    console.log(planProgressStyle.value);
    // -------------------------------实际持续时间-------------------------------
    actualProgressStyle.value = calculateProgress(actualNodeList.value);
    console.log('实际时间计算结果');
    console.log(actualProgressStyle.value);
};

onMounted(() => {
    init();
});
</script>

<style lang="less" scoped>
//@cbackground: #f1f8ffb3;
//@borderColor: #f2f6fc54;

@mainColor: #3cca64;
@progressHeight: 10px;
@planItemBgColor: #c6ebf2;
@actualItemBgColor: #c6ebf2;
@arrowHeight: 10px;
// -------------------------------公共样式--------------------------------------
.timeline-container {
    // border: 1px solid black;
    padding: 70px 10px;
    overflow: auto;
    margin-top: 20px;

    font-size: 10px;
}

// 时间轴上的圆点样式
.item-node {
    width: 15px;
    height: 15px;
    position: absolute;
    left: 0;

    border-radius: 50%;
    border: 4px solid @mainColor;
    background: white;
}

// 时间轴里的进度条
.timeline-progress {
    width: 92%;
     border: 1px solid black;
    border-radius: 4px;
    height: @progressHeight;
    background: @mainColor;
    margin-right: 50px;
}

.end-item {
    padding: 0 !important;
    border: 0 !important;
    margin: 0 !important;
    width: 0 !important;
}

// -----------------时间轴上一个item的样式-------------------------------
.timeline-item {
    display: inline-block;
     border: 1px solid black;
    vertical-align: top;
    position: absolute;
    width: 70px;

    &:before {
        content: '';
        border-left: 5px solid transparent;
        border-right: 5px solid transparent;
        //border-top: 10px solid black;
        position: absolute;
        left: 50%;

        transform: translateX(-50%);
    }

    .timeline-item-content {
         border: 1px solid black;
        background: #c6ebf2;
        color: black;
        z-index: 999;

        &:hover {
            z-index: 9999;
            //transform: translateX(-50%);
        }
    }
}

// -------------------------------计划部分样式-----------------------------------

.plan-box {
    display: flex;
    justify-content: flex-start;
    align-items: center;

    .plan-title {
        padding: 5px;

        background: @mainColor;
        color: white;
        font-weight: bold;
    }

    .plan-timeline {
        flex: 1;
        margin: 0 30px 0 10px;
         border: 1px solid black;
        position: relative;

        .timeline-progress {
            // border: 1px solid black;
            position: relative;
            background: @planItemBgColor;
        }

        // 计划时间轴上的时间节点
        .timeline-item {
            padding: 0 0 10px;
            top: 0;
            left: 0;
            transform: translate(-50%, -100%);

            &:before {
                bottom: 0;
                border-top: 10px solid black;
            }
        }
    }
}

// ----------------------------------实际部分样式-----------------------------

.actual-box {
    margin-top: 10px;
    display: flex;
    justify-content: flex-start;
    align-items: center;

    .actual-title {
        padding: 5px;
         border: 1px solid black;
        background: @mainColor;
        color: white;
        font-weight: bold;
    }

    .actual-timeline {
        flex: 1;

        margin: 0 30px 0 10px;
        // border: 1px solid black;
        position: relative;

        .timeline-progress {
            // border: 1px solid black;

            background: @actualItemBgColor;
        }

        // 计划时间轴上的时间节点
        .timeline-item {
            padding: 10px 0 0;
            bottom: 0;
            left: 0;
            transform: translate(-50%, 100%);

            &:before {
                top: 0;
                border-bottom: 10px solid black;
            }
        }
    }
}
</style>

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值