风景,因走过而美丽。命运,因努力而精彩。南国园内看夭红,溪畔临风血艳浓。如果回到年少时光,那间学堂,我愿依靠在你身旁,陪你欣赏古人的诗章,往后的夕阳。
轻量的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">序 号</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的用法说明就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持。