Steps步驟條組件源碼: steps.vue step.vue ...
Steps步驟條組件源碼:
steps.vue
<template>
<!--設置 simple 可應用簡潔風格,該條件下 align-center / description / direction / space 都將失效。-->
<div
class="el-steps"
:class="[
!simple && 'el-steps--' + direction,
simple && 'el-steps--simple'
]">
<slot></slot>
</div>
</template>
<script>
import Migrating from 'element-ui/src/mixins/migrating';
export default {
name: 'ElSteps',
mixins: [Migrating],
props: {
space: [Number, String], //每個 step 的間距,不填寫將自適應間距。支持百分比。
active: Number, //設置當前激活步驟
direction: { //顯示方向
type: String,
default: 'horizontal'
},
alignCenter: Boolean, //進行居中對齊
simple: Boolean, // 是否應用簡潔風格
finishStatus: { //設置結束步驟的狀態
type: String,
default: 'finish'
},
processStatus: { //設置當前步驟的狀態
type: String,
default: 'process'
}
},
data() {
return {
steps: [], //記錄步驟數數組
stepOffset: 0
};
},
methods: {
// 屬性遷移
getMigratingConfig() {
return {
props: {
'center': 'center is removed.'
}
};
}
},
watch: {
active(newVal, oldVal) {
// 當前激活步驟改變時,觸發父組件的change方法,將改變前和改變後的步驟作為參數傳遞出去
this.$emit('change', newVal, oldVal);
},
steps(steps) {
steps.forEach((child, index) => {
child.index = index;
});
}
}
};
</script>
step.vue
<template>
<!--每一步驟的最外層包裹div-->
<div
class="el-step"
:style="style"
:class="[
!isSimple && `is-${$parent.direction}`,
isSimple && 'is-simple',
isLast && !space && !isCenter && 'is-flex',
isCenter && !isVertical && !isSimple && 'is-center'
]">
<!-- 步驟的數字圖標和步驟條直線 -->
<div class="el-step__head" :class="`is-${currentStatus}`">
<!--步驟條直線-->
<!--如果是最後一步,margin-right不存在;如果不是,則為0-->
<div class="el-step__line" :style="isLast ? '' : { marginRight: $parent.stepOffset + 'px' }">
<i class="el-step__line-inner" :style="lineStyle"></i>
</div>
<!--步驟條的數字圖標-->
<div class="el-step__icon" :class="`is-${icon ? 'icon' : 'text'}`">
<!--如果當前狀態為:wait、process、finish-->
<slot v-if="currentStatus !== 'success' && currentStatus !== 'error'" name="icon">
<!--如果是圖標則顯示對應的圖標-->
<i v-if="icon" class="el-step__icon-inner" :class="[icon]"></i>
<!--如果圖標和未設置isSimple簡潔風格時,則顯示步驟文字-->
<div class="el-step__icon-inner" v-if="!icon && !isSimple">{{ index + 1 }}</div>
</slot>
<!--如果當前狀態為:success、error-->
<i v-else :class="['el-icon-' + (currentStatus === 'success' ? 'check' : 'close')]" class="el-step__icon-inner is-status"></i>
</div>
</div>
<!-- 步驟條下麵每一步的標題和描述 -->
<div class="el-step__main">
<!--每一步的標題-->
<div class="el-step__title" ref="title" :class="['is-' + currentStatus]">
<slot name="title">{{ title }}</slot>
</div>
<!--簡潔模式下會有>圖標-->
<div v-if="isSimple" class="el-step__arrow"></div>
<!--每一步的描述-->
<div v-else class="el-step__description" :class="['is-' + currentStatus]">
<slot name="description">{{ description }}</slot>
</div>
</div>
</div>
</template>
<script>
export default {
name: 'ElStep',
props: {
title: String, //標題
icon: String, //圖標
description: String, //描述性文字
status: String //設置當前步驟的狀態,不設置則根據 steps 確定狀態。 wait(灰色)/ process(黑色)/ finish(藍色)/ error / success(綠色)
},
data() {
return {
index: -1,
lineStyle: {}, //步驟條直線的樣式
internalStatus: ''
};
},
beforeCreate() {
this.$parent.steps.push(this);
},
mounted() {
const unwatch = this.$watch('index', val => {
this.$watch('$parent.active', this.updateStatus, { immediate: true });
unwatch();
});
},
beforeDestroy() {
const steps = this.$parent.steps;
const index = steps.indexOf(this);
if (index >= 0) {
steps.splice(index, 1);
}
},
computed: {
// 返回當前步驟的狀態
currentStatus() {
return this.status || this.internalStatus;
},
prevStatus() {
const prevStep = this.$parent.steps[this.index - 1];
return prevStep ? prevStep.currentStatus : 'wait';
},
// 返回是否是居中對齊
isCenter() {
return this.$parent.alignCenter;
},
// 返回顯示的方向:豎直(false)或者水平(true)
isVertical() {
return this.$parent.direction === 'vertical';
},
// 返回是否應用簡潔風格
isSimple() {
return this.$parent.simple;
},
// 判斷當前是不是最後步驟
isLast() {
const parent = this.$parent;
return parent.steps[parent.steps.length - 1] === this;
},
// 返回總步驟數
stepsCount() {
return this.$parent.steps.length;
},
// 返回每個step的間距。
space() {
const { isSimple, $parent: { space } } = this;
// isSimple為true時,space將失效
return isSimple ? '' : space ;
},
style: function() {
const style = {};
const parent = this.$parent;
const len = parent.steps.length; //總步驟
// 每個step的間距
const space = (typeof this.space === 'number' //如果設置的space是number
? this.space + 'px' //space等於設置的space
: this.space ? this.space : 100 / (len - (this.isCenter ? 0 : 1)) + '%'); //如果未設置space,未設置居中,則等於100除以(總步驟數-1);設置居中顯示,則等於00除以總步驟數。
// flex-basis 屬性用於設置或檢索彈性盒伸縮基準值。
style.flexBasis = space;
//如果是水平方向則直接返回設置的樣式
if (this.isVertical) return style;
//如果是最後的步驟,設置最大寬度等於(100/總步驟數)%
if (this.isLast) {
style.maxWidth = 100 / this.stepsCount + '%';
} else {
//如果不是最後的步驟,marginRight為0
style.marginRight = -this.$parent.stepOffset + 'px';
}
return style;
}
},
methods: {
updateStatus(val) {
const prevChild = this.$parent.$children[this.index - 1];
if (val > this.index) { //如果是下一步
// internalStatus 等於用戶設置的結束步驟狀態
this.internalStatus = this.$parent.finishStatus;
} else if (val === this.index && this.prevStatus !== 'error') {
// internalStatus 等於用戶設置的當前步驟狀態
this.internalStatus = this.$parent.processStatus;
} else {
this.internalStatus = 'wait';
}
if (prevChild) prevChild.calcProgress(this.internalStatus);
},
//設置步驟間直線的樣式
calcProgress(status) {
let step = 100;
const style = {};
// transitionDelay在過渡效果開始前等待的秒數:
style.transitionDelay = 150 * this.index + 'ms';
if (status === this.$parent.processStatus) {
step = this.currentStatus !== 'error' ? 0 : 0;
} else if (status === 'wait') {
step = 0;
//為負數的時候過渡的動作會從該時間點開始顯示,之前的動作被截斷;為正數的時候過渡的動作會延遲觸發。
style.transitionDelay = (-150 * this.index) + 'ms';
}
style.borderWidth = step ? '1px' : 0;
this.$parent.direction === 'vertical'
? style.height = step + '%'
: style.width = step + '%';
this.lineStyle = style;
}
}
};
</script>