Vue时间轴 vue-light-timeline的用法说明

风景,因走过而美丽。命运,因努力而精彩。南国园内看夭红,溪畔临风血艳浓。如果回到年少时光,那间学堂,我愿依靠在你身旁,陪你欣赏古人的诗章,往后的夕阳。

轻量的vue时间轴组件

install

npm install vue-light-timeline

如果你使用的是yarn

yarn add vue-light-timeline

usage

import LightTimeline from 'vue-light-timeline';

Vue.use(LightTimeline);

<template>
 <light-timeline :items='items'></light-timeline>
</template>
 
export default {
 data () {
  return {
   items: [
    {
     tag: '2019-02-12',
     content: '测试内容'
    },
    {
     tag: '2019-02-13',
     type: 'circle',
     content: '练习内容'
    }
   ]
  }
 }
}

或者你还可以为时间轴的每个部分传递插槽:

<template>
 <light-timeline :items='items'>
  <template slot='tag' slot-scope='{ item }'>
   {{item.date}}
  </template>
  <template slot='content' slot-scope='{ item }'>
   {{item.msg}}
  </template>
 </light-timeline>
</template>
<script>
export default {
 data () {
  return {
   items: [
    {
     date: '2019-02-12',
     msg: '测试内容'
    },
    {
     date '2019-02-13',
     msg: '练习内容'
    }
   ]
  }
 }
}

自己写个好看点的样式就行了

补充知识:Vue可移动水平时间轴

里程碑时间轴具体实现

效果图

编辑里程碑效果图

<template>
<div class="state_grade">
<!--    <mile-stone :projectId="projectData.proId" :projectName="projectData.proName" :proNum="projectData.proNum"></mile-stone>-->
    <div class="timeLine" style="overflow: hidden;">
     <div style="width: 10%; display: inline-block; margin-left: 5px;">
      <el-button @click="mileStoUpdateVisible = true" type="primary">编辑里程碑</el-button>
     </div>
     <div style="width: 70%;display: inline-block" align="center">
      <div style="width: 20%;display: inline-block; font-size: 14px;">里程碑状态:</div>
      <div style="width: 100px;display: inline-block; font-size: 14px; ">开始 <img class="node_picture" src="../../assets/images/timeLineA.png"></div>

      <div style="width: 100px;display: inline-block; font-size: 14px;">超期 <img class="node_picture" src="../../assets/images/timeLineB.png"> </div>

      <div style="width: 100px;display: inline-block; font-size: 14px;">关闭 <img class="node_picture" src="../../assets/images/timeLineC.png"> </div>

     </div>

     <div class="my_timeline_prev" @click="moveLeft">
      <img src="../../assets/arrow_left_blue.png" class="my_timeline_node"/>
      <!--   <div class="my_timeline_item_line" style="margin-top: -18px;"></div>-->
      <!--   <div class="my_timeline_item_content" style="color: rgba(0,0,0,0);">上</div>-->
     </div>
     <div v-if="destroyIncomeStatistics" class="ul_box">
      <ul class="my_timeline" ref="mytimeline" style="margin-left: 10px;">
       <li class="my_timeline_item" v-for="(item,index) in timeLineList" :key="index">

        <el-tooltip placement="top" effect="light">
         <div slot="content" class="tooltip">
          <el-row>
           <el-col :span="24">{{'阶段名称:'+item.stageName}}</el-col>
          </el-row>
          <el-row>
           <el-col :span="24">{{'阶段目标:'+item.stageTarget}}</el-col>
          </el-row>
          <el-row>
           <el-col :span="24">{{'开始时间:'+item.startTime}}</el-col>
          </el-row>
          <el-row>
           <el-col :span="24">{{'结束时间:'+item.endTime}}</el-col>
          </el-row>
          <el-row>
           <el-col :span="24">{{'验收标准:'+item.acceptStar}}</el-col>
          </el-row>
          <el-row>
           <el-col :span="24">
            <span v-if="item.milepostState==='1'">里程碑状态:打开</span>
            <span v-if="item.milepostState==='2'">里程碑状态:超期</span>
            <span v-if="item.milepostState==='3'">里程碑状态:关闭</span>
           </el-col>
          </el-row>
         </div>
         <!--圈圈节点-->
         <!--       <div class="my_timeline_node" style="backgroundColor: #999; width: 28px;height: 28px;" @click="changeActive(index)" :class="{active: index == timeIndex}"></div>-->
         <div class="my_timeline_node">
          <div style="background-color: #FCFCFC">
           <img class="my_timeline_picture" v-if="item.milepostState==='1'"
              src="../../assets/images/timeLineA.png">
           <img class="my_timeline_picture" v-if="item.milepostState==='2'"
              src="../../assets/images/timeLineB.png">
           <img class="my_timeline_picture" v-if="item.milepostState==='3'"
              src="../../assets/images/timeLineC.png">
          </div>
         </div>
        </el-tooltip>
        <!--线-->
        <div
         :class="[timeLineList.length==index+1?my_timeline_item_line_last:my_timeline_item_line_not_last]"></div>
        <!--标注-->
        <div class="my_timeline_item_content">
         <div>{{item.endTime}}</div>
         <el-tooltip placement="top" effect="light">
          <div slot="content">{{item.endTime}}<br/>{{item.stageName}}</div>
          <div class="detail_info">{{item.stageName}}</div>
         </el-tooltip>
        </div>
       </li>
      </ul>
     </div>
     <div class="my_timeline_next" @click="moveRight">
      <img src="../../assets/arrow_right_blue.png" class="my_timeline_node"/>
      <div class="my_timeline_item_content" style="color: rgba(0,0,0,0);">下</div>
     </div>
    </div>
    <el-dialog :title="titleMessage" center :visible="mileStoUpdateVisible" width="50%"
          @open="onMileStoUpdateVisibleOpen()" @close="closeMileStone()">
     <stone-detail :projectId="this.projectId" :proNum="this.projectData.proNum" @closeMileStone="closeMileStone()" ref="stone-detail"
            @refreshMileStoneData="searchMileStone()"></stone-detail>
    </el-dialog>
   </div>
  </div>
</template>

<script>
  import API from '../../api/api_project';
  import StoneDetail from "../../components/project-info/stonedetail"
  import MemberDetail from "../../components/project-info/memberdetail.vue"
  export default {
    name: 'project-detail',
    components:{
      MemberDetail,
      StoneDetail,
    },
    data() {
      return {
        destroyIncomeStatistics:true,
        loading: false,
        titleMessage: '',
        mileStoUpdateVisible: false,
        my_timeline_item_line_last: "my_timeline_item_line_last",
        my_timeline_item_line_not_last: "my_timeline_item_line_not_last",

        menuTree:[],
        timeLineList: [],
        page:{
          total:0,
          pageNum: 0,
          pageSize: 10,
        },
        model: {
          select: "",
          searchConent: "",
          projectId: "",
          proName:"",
        },
        projectData:{
          proId: '',
          proNum: '',
          proName: '',
          hwDept: '',
          hwPo: '',
          busineMode: '1',
          buildProDate: '',
          startDate: '',
          expEndDate: '',
          hwPoDate: '',
          hwPoEndDate:'',
          realEndDate: '',
          proManageId:'',
          proQa:'',
          hwPm:'',
          proEstNum: '0',
          proState:'1'
        },
        proPeoId:'',
        projectId:'',
        proPeoUpdateVisible:false,
        projectMember: [],
      }
    },
    mounted(){
      this.projectId=this.$route.params.projectId

      this.searchMileStone()
      this.sortDataArray(this.timeLineList)
      //到数据库获取projectId对应的信息列表存入projectData
      API.getProjectInfo(this.projectId).then((data)=>{
        this.projectData=data.data;
        this.projectData.busineMode = this.projectData.busineMode.toString();
        this.projectData.proState = this.projectData.proState.toString();
      })
      this.search();
    },
    methods: {
      searchMileStone() {
        console.log('项目id:'+this.projectId)
        let params={
          proId:this.projectId,
        };
        API.getMileStoneList(params).then(data => {
          let result = data.data
          if (result && result.list) {
            if(this.timeLineList.length>0){
              this.timeLineList.splice(0,this.timeLineList.length);
            }
            for(var i=0;i<result.list.length;i++){
              this.timeLineList.splice(i, 1, result.list[i])
            }
            this.sortDataArray(this.timeLineList)
          }

        },({msg})=>{
          this.$message.error(msg);
        });
console.log('刷新里程碑列表')
console.log(this.timeLineList)
      },

      closeMileStone() {
        this.mileStoUpdateVisible = false;
        // this.projectId = '';
      },
      onMileStoUpdateVisibleOpen() {
        this.titleMessage = this.projectData.proNum + '项目里程碑';
        this.$nextTick(() => {
          let form = this.$refs["stone-detail"];
          form.initPage();
        });
      },
      changeActive(index) {
        this.timeIndex = index;
      },
      moveLeft() {
        let marginLeft = parseInt(this.$refs.mytimeline.style.marginLeft);
        let listNum = 0;

        if (Math.abs(marginLeft) > 10) {
          this.$refs.mytimeline.style.marginLeft = marginLeft + 120 + 'px';
        }
      },
      moveRight() {
        let marginLeft = parseInt(this.$refs.mytimeline.style.marginLeft);
        if (marginLeft <= 10 && (marginLeft >= -(this.timeLineList.length * 120))) {
          this.$refs.mytimeline.style.marginLeft = marginLeft - 120 + 'px';
        }
      },
//对数组根据日期进行排序
      sortDataArray(dataArray) {
        return dataArray.sort(function (a, b) {
          return Date.parse(a.endTime.replace(/-/g, "/")) - Date.parse(b.endTime.replace(/-/g, "/"));
        })
      },

      sortByKey(array,key){
        return array.sort(function(a,b){
          var y = a[key];
          var x = b[key];
          return((x<y)?-1:((x>y)?1:0));
        })

      },
      handleCurrentChange(val) {
        this.page.pageNum = val ;
        this.search();
      },
      handleSizeChange(val) {
        this.page.pageSize = val;
        this.search();
      },
      handleSearch(){
        this.page.pageNum= 0;
        this.search();
      },
      }
</script>

 .content {
  height: 100px;
 }

 .my_timeline_next {
  float: left;
  display: inline-block;
  background-color: #FCFCFC;
  cursor: pointer;
 }

 .my_timeline_prev {
  width: 50px;
  float: left;
   margin-top: 110px;
 }

 .my_timeline_next {
  width: 34px;
  margin-top: 80px;
 }

 .el-col-24 {
  margin-left: 10px;
  padding-bottom: 5px;
 }

 .el-col-12 {
  margin-left: 10px;
 }

 .tooltip {

 }

 .ul_box {
  width: 80%;
  height: 120px;
  display: inline-block;
  float: left;
  margin-top: 50px;
  overflow: hidden;
 }

 .my_timeline_item {
  display: inline-block;
  width: 150px;
 }

 .my_timeline_node {
  background-color: #FCFCFC;
  box-sizing: border-box;
  border-radius: 50%;
  cursor: pointer;
  width: 40px;
  height: 40px;
 }

 .node_picture {
  //margin-top: 20px;
  height: 25px;
  width: 25px;
  margin-left: 5px;
  margin-bottom: -7px;
 }

 .my_timeline_picture {
  margin-top: 13px;
  height: 25px;
  width: 25px;
 }

 .my_timeline_node.active {
  background-color: #fff !important;
  border: 6px solid #f68720;
 }

 .my_timeline_item_line_last {
  width: 100%;
  height: 10px;
  margin: -14px 0 0 28px;
  border-left: none;
 }

 .my_timeline_item_line_not_last {
  width: 100%;
  height: 10px;
  margin: -14px 0 0 25px;
  border-top: 2px solid #E4E7ED;
  border-left: none;
 }

 .my_timeline_item_content {
  margin: 10px 0 0 -10px;
  width: 90%; /*根据自己项目进行定义宽度*/
  font-size: 14px;
 }

 .detail_info {
  width: 80%;
  height: 250px;
  padding-bottom: 50px;
  overflow: hidden; /*设置超出的部分进行影藏*/
  text-overflow: ellipsis; /*设置超出部分使用省略号*/
  white-space: nowrap; /*设置为单行*/
  font-size: 14px;
 }
 .state_grade.process_wrap{
  border-color: #e4ebf0;
  margin-top: 150px;
  border-radius: 2px;
  padding-bottom: 10px;
 }

 .fall-back {
  float:right;
  margin-right: 20px;
  margin-bottom:50px;
 }
 .state_grade{
  border: 1px solid #e6e6e6;
  background: #FCFCFC;
  padding: 10px;
  //position: relative;
  /*height: 90px;*/
  height: 250px;
  margin-bottom: 15px;
  /*margin-top: 15px;*/
 }
 .title_top{
  height: 33px;
 }
 .obj_tit_wrap{
  border-bottom: 1px solid #e6e6e6;
  padding-bottom: 3px;
  font-size: 14px;
 }

 .obj_tit_mile{
   width: 150px;
   height: 35px;
 }

 .tit_deco{
  color: #9a9a9a;
  font-size: 14px;
 }
 .add_contain{
  display:inline-block;
  padding-bottom: 10px;
  padding-top: 20px;
 }
 .project_content_warp{
  background: #fdfdfd;
  margin-bottom: 15px;
 }
 .project_job_add{
  padding-left: 30px;
  background: #FCFCFC;
  border-bottom: 1px solid #E5E5E5;
  line-height: 10px;
  margin-bottom: 15px;
  font-size: 14px;
 }
 .project_info_span{
  display:inline-block;
  padding-left: 10px;
 }

 .el-col-8{
  height: 50px;
 }
</style>

编辑里程碑

stonedetail.vue

<template>

 <div>
  <el-row>
   <el-col :span="23">
    <div style="margin-top: 10px">
     <el-tag effect="dark" style="font-size: 16px;width: 110px;text-align: center">里程碑</el-tag>
    </div>
   </el-col>
   <el-col :span="1">
    <img src="../../assets/images/add.png" style="width: 30px;height: 30px;margin-top: 10px" @click="addItems()"/>
   </el-col>
  </el-row>
  <hr/>

  <el-row style="text-align: center">
   <el-col :span="3">
    <el-tag style="width: 100%;font-size: 14px">序&#12288;号</el-tag>
   </el-col>
   <el-col :span="8">
    <el-tag style="width: 100%;font-size: 14px">阶段名称</el-tag>
   </el-col>
   <el-col :span="7">
    <el-tag style="width: 100%;font-size: 14px">起始时间</el-tag>
   </el-col>
   <el-col :span="5">
    <el-tag style="width: 100%;font-size: 14px">结束时间</el-tag>
   </el-col>
  </el-row>

  <el-form label-width="100px" align="left" ref="form" style="text-align: left;" :model="model">
   <div v-for="(item, index) in model.timeLineList" :key="index">
    <el-row>
     <el-col :span="3">
      <input style="text-align: center" class="el-input__inner" type="text" v-model="index" disabled="true">
     </el-col>
     <el-col :span="8">
      <input placeholder="请输入阶段名称" style="text-align: center" class="el-input__inner" type="text"
          v-model="item.stageName">
     </el-col>
     <el-col :span="6">
      <el-date-picker
       style="width: 100%"
       type="date"
       :editable="false"
       v-model="item.startTime"
       placeholder="请选择起始时间"
       format="yyyy-MM-dd"
       value-format="yyyy-MM-dd"
      >
      </el-date-picker>

     </el-col>
     <el-col :span="6">
      <el-date-picker
       style="width: 100%"
       type="date"
       :editable="false"
       v-model="item.endTime"
       placeholder="请选择结束时间"
       format="yyyy-MM-dd"
       value-format="yyyy-MM-dd"
      >
      </el-date-picker>
     </el-col>
    </el-row>

    <el-row>
     <el-col :span="3">
      <input placeholder="阶段目标" style="text-align: center;" class="el-input__inner" type="text" disabled="true">
     </el-col>
     <el-col :span="20">
      <el-input v-model="item.stageTarget" placeholder="请输入阶段目标"></el-input>
      <!--     <textarea placeholder="请输入阶段目标" v-model="item.stageTarget" style="height: 30px;" class="el-input__inner" type="text"></textarea>-->
     </el-col>
    </el-row>

    <el-row>
     <el-col :span="3">
      <input placeholder="验收标准" style="text-align: center;" class="el-input__inner" type="text" disabled="true">
     </el-col>
     <el-col :span="20">
      <el-input v-model="item.acceptStar" placeholder="请输入验收标准"></el-input>
     </el-col>
    </el-row>

    <el-row>
     <el-col :span="3">
      <input placeholder="里程碑状态" style="text-align: center;" class="el-input__inner" type="text" disabled="true">
     </el-col>
     <el-col :span="20">
      <template>
       <el-select v-model="item.milepostState" placeholder="请选择">
        <el-option
         v-for="item in milepostStateList"
         :key="item.ref_id"
         :label="item.ref_value"
         :value="item.ref_id">
        </el-option>
       </el-select>
      </template>
     </el-col>

     <el-col :span="1">
      <img src="../../assets/images/delete.png" style="width: 30px;height: 30px" @click="deleteItems(index)"/>
     </el-col>
    </el-row>
   </div>
  </el-form>
  <div style="text-align: center;margin-top: 30px">
   <el-button type="primary" @click="submit()">确认修改</el-button>
  </div>
 </div>


</template>

<script>
  import API from '../../api/api_project';

  export default {
    name: "stoneDetail",
    props: ['projectId', 'proNum'],
    watch: {
      'proId': {
        // projectId,所以每次都能监听到变化
        immediate: true,
        handler: function (val) {
          if (!val) return;
          this.onProjectIdChange(val);
        }
      }
    },
    data() {
      return {
        proId:'',
        milepostStateList: [{
          ref_id: "1",
          ref_value: '打开',
          ref_key: '1'
        }, {
          ref_id: "2",
          ref_value: '超期',
          ref_key: '2'
        }, {
          ref_id: "3",
          ref_value: '关闭',
          ref_key: '3'
        }],
        deleteList: [],
        model: {
          timeLineList: [],
        },

      }
    },
    methods: {
      /**
       * 提交修改的信息
       */
      submit: function () {
        this.$refs.form.validate((valid) => {
          if (!valid) {
            this.$message.error('请填写正确信息');
            return;
          }


          console.log('编辑里程碑结果:')
          console.log(this.model.timeLineList)

          let proMileposts = this.model.timeLineList

          API.updatetMileStone(proMileposts).then(data => {
            if (data.code == 200) {
              this.$message.success("修改成功");
              this.refreshMileStoneData();
              this.close();
            } else {
              this.$message.error(data.msg);
              // this.close();
            }
          })


        });
      },
      close() {
        this.$emit("closeMileStone");
        this.proId=''
        this.model.timeLineList.splice(0,this.model.timeLineList.length)
      },
      refreshMileStoneData() {
        this.$emit("refreshMileStoneData");

      },
      addItems() {
        this.model.timeLineList.push({
          milepostId:'',
          proId: this.proId,
          stageName: '',
          startTime: this.addDate(),
          endTime: this.addDate(),
          stageTarget: '',
          acceptStar: '',
          deliverableName: '',
          milepostState: '1',
        });

      },
      addDate() {
        var date = new Date();
        var seperator1 = "-";
        var year = date.getFullYear();
        var month = date.getMonth() + 1;
        var strDate = date.getDate();
        if (month >= 1 && month <= 9) {
          month = "0" + month;
        }
        if (strDate >= 0 && strDate <= 9) {
          strDate = "0" + strDate;
        }
        var currentdate = year + seperator1 + month + seperator1 + strDate;
        return currentdate;
      },
      deleteItems(index) {
        this.$confirm('确认删除该记录吗?', '提示', {
          confirmButtonClass: 'el-button--warning'
        }).then(() => {
          if(this.model.timeLineList[index].milepostId)
          {
            API.deleteMileStone(this.model.timeLineList[index].milepostId).then(data=>{
              if(data.code===200)
              {
                this.$message.success("删除成功");
                this.model.timeLineList.splice(index, 1);
                this.refreshMileStoneData();
              }else {
                this.$message.error(data.msg);
              }
            })
          }
          else{
            this.model.timeLineList.splice(index, 1);
          }

        }).catch(() => {});


      },
      onProjectIdChange(id) {
        this.model.timeLineList.splice(0,this.model.timeLineList.length)
        // if (id) {
          let params={
            proId:id,
          };
          API.getMileStoneList(params).then(data => {
            let result = data.data
            if (result && result.list) {
              for(var i=0;i<result.list.length;i++){
                this.model.timeLineList.splice(i, 1, result.list[i])
              }
            }
          }, ({msg}) => {
            this.$message.error(msg);
          });
        // }
        console.log('dailog打开里程碑列表')
        console.log(this.model.timeLineList)
      },
      initPage() {
         this.proId=this.projectId;
        if (this.proId) {
          this.onProjectIdChange(this.proId);
        }

      }
    },
  }
</script>

<style scoped>

</style>

以上这篇Vue时间轴 vue-light-timeline的用法说明就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持。

标签: vue Vue