zoukankan      html  css  js  c++  java
  • 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;
             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 {
                 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;
             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;
           40px;
          text-align: center;
          z-index: 1;
          transform: rotate(45deg)
        }
    
        &:after {
          content: "";
          position: absolute;
          top: 0;
          right: 0;
          border-style: solid;
          border- 25px 25px 25px 25px;
           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 {
             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;
             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;
             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>
    

      

  • 相关阅读:
    内部类概述和访问特点
    权限修饰符 权限
    抽象类和接口作为返回值类型的问题
    抽象类和接口作为形参问题
    jdbc:java数据库连接
    类与类、类与接口、接口与接口的关系
    接口
    抽象类
    多态
    继承中构造方法的关系
  • 原文地址:https://www.cnblogs.com/bobo1/p/14377736.html
Copyright © 2011-2022 走看看