VUE项目里个性化写个时间轴组件,带折叠效果

最终效果图

 

直接看TheTimeLine.vue文件代码

<template>
  <div class="timeline-main">
    <!--年月标题-->
    <div class="timeline-title">
      {{ timeData.yearData }}
      <i :class="showCards ? 'el-icon-arrow-down' : 'el-icon-arrow-right'" @click="showCards = !showCards"></i>
    </div>
    <ul class="timeline-body">
      <!--时间线顶部圆点-->
      <li class="timeline-item-head">
        <div class="item-node"></div>
        <div class="item-tail"></div>
      </li>
      <!--时间线内容-->
      <template v-if="showCards">
        <li v-for="(mouthItem, i) in timeData.mouthData" :key="'mm'+i" class="timeline-item">
          <div class="item-left">
            <div class="item-left-data">{{ mouthItem.dateData }}</div>
            <div class="item-left-total">
              <div class="item-left-total-text">
                共{{mouthItem.dateArr.length}}条
              </div>
              <div class="item-left-total-end"></div>
            </div>
          </div>
          <div class="item-tail"></div>
          <div class="item-node"></div>
          <div class="item-content">
            <slot v-for="v in mouthItem.dateArr" :card="v"></slot>
          </div>
        </li>
      </template>
      <!--时间线尾部圆点-->
      <li class="timeline-item-foot">
        <div class="item-node"></div>
        <div class="item-tail"></div>
      </li>
    </ul>
  </div>
</template>

<script>

export default {
  name: 'TheTimeLine',
  props: {
    timeData: {
      type: Object,
      default: () => ({})
    }
  },
  data() {
    return {
      showCards: true
    };
  },
};
</script>

<style scoped lang="less">
.timeline-main {
  padding: 0 0 0 20px;

  .timeline-title {
    margin-bottom: 10px;
    font-weight: bold;

    i {
      cursor: pointer;
    }
  }

  .timeline-body {
    margin: 0;
    font-size: 14px;
    list-style: none;
    // 顶尾圆圈
    .timeline-item-head, .timeline-item-foot {
      position: relative;
      height: 15px;

      .item-tail {
        position: absolute;
        left: 45px;
        height: 100%;
        border-left: 2px solid #e4e7ed;
      }

      .item-node {
        position: absolute;
        left: 42px;
        width: 8px;
        height: 8px;
        background-color: #e4e7ed;
        border-radius: 50%;
        display: flex;
        justify-content: center;
        align-items: center;
      }
    }

    .timeline-item-foot {
      .item-node {
        top: 14px;
      }
    }

    // 时间线主体内容
    .timeline-item {
      position: relative;
      padding-bottom: 10px;

      .item-left {
        position: absolute;
        top: 13px;
        left: -9px;

        .item-left-data {
          font-weight: bold;
          line-height: 20px;
        }

        .item-left-total {
          display: flex;
          font-size: 12px;

          .item-left-total-text {
            padding: 0 3px;
            line-height: 20px;
            color: #ffffff;
            background: #409EFF;
          }

          .item-left-total-end {
            width: 0;
            height: 0;
            border-top: 10px solid transparent;
            border-left: 6px solid #409eff;
            border-bottom: 10px solid transparent;
          }
        }
      }

      .item-tail {
        position: absolute;
        left: 45px;
        height: 100%;
        border-left: 2px solid #e4e7ed;
      }

      .item-node {
        position: absolute;
        top: 38px;
        left: 41px;
        width: 6px;
        height: 6px;
        background-color: #ffffff;
        border: 2px solid #409EFF;
        border-radius: 50%;
        display: flex;
        justify-content: center;
        align-items: center;
      }

      .item-content {
        position: relative;
        padding-top: 15px;
        padding-left: 60px;;
        // top: -3px;
      }
    }
  }
}
</style>

数组格式需要这样的

{ yearData: '2021年2月',
          mouthData: [
            {
              dateData: '20日', dateArr: [
                { workNo: '2321321231233', time: '12:00:03', status: '1', name: '张三', type: '1' }
              ]
            },
            {
              dateData: '1日', dateArr: [
                { workNo: '2321321231233', time: '12:00:03', status: '1', name: '张三', type: '1' }
              ]
            },
          ]
        },
        { yearData: '2021年1月',
          mouthData: [
            {
              dateData: '30日', dateArr: [
                { workNo: '2321321231233', time: '12:00:03', status: '1', name: '张三', type: '1' }
              ]
            },
          ]
        }

组件使用时

<div v-for="(v, i) in data" :key="'id'+i" class="year-item">
        <the-time-line :time-data="v">
          <template v-slot="soltData">
            <!--这里采用作用域插槽-可以自己自定义卡片样式-->
            <card :card-data="soltData.card"></card>
          </template>
        </the-time-line>
      </div>

我自己写的卡片组件

这里删了一些逻辑代码,尽量保留了静态的样式

card.vue文件
<template>
  <div class="item-content-card" @click="goDetail()">
    <div class="time-title" :class="statusStyle(cardData.status)">
      <i class="el-icon-time"></i>
      {{ cardData.createTime.slice(-8) }}
      <div class="item-status"><b>{{ formatStatus(cardData.status) }}</b></div>
    </div>
    <div class="time-content">
      <div class="time-content-no">
        <div class="time-content-no-circle" :class="{'time-content-delete' : cardData.type!=1}"></div>
        {{ cardData.workNo }}
      </div>
      <div class="time-content-name">
        <span>{{ cardData.name }}</span>
        新增/导入/删除
        <div v-if="showRevoke()" class="opera-icon-back" title="撤销" @click.stop="revokeWorkFlow"></div>
        <div v-if="showRevoke() && cardData.status == 4" class="opera-icon-edit" title="编辑" @click.stop="editWorkFlow"></div>
      </div>
    </div>
  </div>
</template>

<script>

export default {
  name: 'TheWorkNoteCard',
  props: {
    cardData: {
      type: Object,
      default: () => ({})
    }
  },
  methods: {
    // 跳转详情页面
    goDetail() {
      this.$router.push({});
    },
    // 显示撤销
    showRevoke() {
      // 判断符合条件的逻辑
    },
    // 撤销
    revokeWorkFlow() {
    },
    // 编辑被驳回
    editWorkFlow() {
    },
    statusStyle(v) {
      if (String(v) === '2') {
        return 'time-status-unreview';
      } else if (String(v) === '3') {
        return 'time-status-success';
      } else if (String(v) === '4' || String(v) === '5') {
        return 'time-status-back';
      } else {
        return 'time-status-unreview';
      }
    },
    formatStatus(v) {
      if (String(v) === '2') { // 待审核
        return '待审核';
      } else if (String(v) === '3') { // 通过
        return '通过';
      } else if (String(v) === '4') { // 驳回
        return '驳回';
      } else if (String(v) === '5') { // 撤销
        return '撤销';
      } else {
        return '--';
      }
    },
  }
};
</script>

<style scoped lang="less">
.item-content-card {
  cursor: pointer;
  border-radius: 4px;
  background-color: #f7f9fa;
  margin-bottom: 10px;

  .time-title {
    position: relative;
    box-sizing: border-box;
    height: 50px;
    padding: 15px;
    border-bottom: 1px solid #e4e4e4;

    i {
      font-size: 16px;
    }

    .item-status {
      position: absolute;
      font-size: 12px;
      top: 13px;
      right: 0;
      width: 40px;
      text-align: center;
      z-index: 1;
      transform: rotate(45deg)
    }

    &:after {
      content: "";
      position: absolute;
      top: 0;
      right: 0;
      border-style: solid;
      border-width: 25px 25px 25px 25px;
      width: 0px;
      height: 0px;
    }
  }

  .time-status-unreview {
    .item-status {
      color: #409EFF;
    }

    &:after {
      border-color: #d3e7fb #d3e7fb transparent transparent;
    }
  }

  .time-status-success {
    .item-status {
      color: #67C23A;
    }

    &:after {
      border-color: #dbeed4 #dbeed4 transparent transparent;
    }
  }

  .time-status-back {
    .item-status {
      color: #E6A23C;
    }

    &:after {
      border-color: #f4e7d4 #f4e7d4 transparent transparent;
    }
  }

  .time-content {
    padding: 15px;

    .time-content-no {
      margin-bottom: 10px;

      .time-content-no-circle {
        width: 10px;
        height: 10px;
        display: inline-block;
        background-color: #8ec850;
        border-radius: 50%;
        margin-right: 6px;
      }

      .time-content-delete {
        background-color: #666666;
      }
    }

    .time-content-name {
      line-height: 20px;
      padding-left: 20px;

      span {
        margin-right: 20px;
      }

      .opera-icon-back {
        margin-left: 30px;
        display: inline-block;
        width: 18px;
        height: 18px;
        cursor: pointer;
        background: url('../images/rollback.png') no-repeat;
        background-size: 18px;

        &:hover {
          background: url('../images/rollback_h.png') no-repeat;
          background-size: 18px;
        }
      }

      .opera-icon-edit {
        margin-left: 5px;
        display: inline-block;
        width: 18px;
        height: 18px;
        cursor: pointer;
        background: url('../images/edit.png') no-repeat;
        background-size: 18px;

        &:hover {
          background: url('../images/edit_h.png') no-repeat;
          background-size: 18px;
        }
      }
    }
  }

  &:hover {
    box-shadow: 0px 0px 4px rgba(0, 0, 0, 0.176470588235294)
  }
}
</style>

  

 

posted @ 2021-02-05 14:55  七度丢失的梦  阅读(2719)  评论(0编辑  收藏  举报