| <template> | 
|   <div | 
|     class="el-progress" | 
|     :class="[ | 
|       'el-progress--' + type, | 
|       status ? 'is-' + status : '', | 
|       { | 
|         'el-progress--without-text': !showText, | 
|         'el-progress--text-inside': textInside, | 
|       } | 
|     ]" | 
|     role="progressbar" | 
|     :aria-valuenow="percentage" | 
|     aria-valuemin="0" | 
|     aria-valuemax="100" | 
|   > | 
|     <div class="el-progress-bar" v-if="type === 'line'"> | 
|       <div class="el-progress-bar__outer" :style="{height: strokeWidth + 'px'}"> | 
|         <div class="el-progress-bar__inner" :style="barStyle"> | 
|           <div class="el-progress-bar__innerText" v-if="showText && textInside">{{content}}</div> | 
|         </div> | 
|       </div> | 
|     </div> | 
|     <div class="el-progress-circle" :style="{height: width + 'px', width: width + 'px'}" v-else> | 
|       <svg viewBox="0 0 100 100"> | 
|         <path | 
|           class="el-progress-circle__track" | 
|           :d="trackPath" | 
|           stroke="#e5e9f2" | 
|           :stroke-width="relativeStrokeWidth" | 
|           fill="none" | 
|           :style="trailPathStyle"></path> | 
|         <path | 
|           class="el-progress-circle__path" | 
|           :d="trackPath" | 
|           :stroke="stroke" | 
|           fill="none" | 
|           :stroke-linecap="strokeLinecap" | 
|           :stroke-width="percentage ? relativeStrokeWidth : 0" | 
|           :style="circlePathStyle"></path> | 
|       </svg> | 
|     </div> | 
|     <div | 
|       class="el-progress__text" | 
|       v-if="showText && !textInside" | 
|       :style="{fontSize: progressTextSize + 'px'}" | 
|     > | 
|       <template v-if="!status">{{content}}</template> | 
|       <i v-else :class="iconClass"></i> | 
|     </div> | 
|   </div> | 
| </template> | 
| <script> | 
|   export default { | 
|     name: 'ElProgress', | 
|     props: { | 
|       type: { | 
|         type: String, | 
|         default: 'line', | 
|         validator: val => ['line', 'circle', 'dashboard'].indexOf(val) > -1 | 
|       }, | 
|       percentage: { | 
|         type: Number, | 
|         default: 0, | 
|         required: true, | 
|         validator: val => val >= 0 && val <= 100 | 
|       }, | 
|       status: { | 
|         type: String, | 
|         validator: val => ['success', 'exception', 'warning'].indexOf(val) > -1 | 
|       }, | 
|       strokeWidth: { | 
|         type: Number, | 
|         default: 6 | 
|       }, | 
|       strokeLinecap: { | 
|         type: String, | 
|         default: 'round' | 
|       }, | 
|       textInside: { | 
|         type: Boolean, | 
|         default: false | 
|       }, | 
|       width: { | 
|         type: Number, | 
|         default: 126 | 
|       }, | 
|       showText: { | 
|         type: Boolean, | 
|         default: true | 
|       }, | 
|       color: { | 
|         type: [String, Array, Function], | 
|         default: '' | 
|       }, | 
|       format: Function | 
|     }, | 
|     computed: { | 
|       barStyle() { | 
|         const style = {}; | 
|         style.width = this.percentage + '%'; | 
|         style.backgroundColor = this.getCurrentColor(this.percentage); | 
|         return style; | 
|       }, | 
|       relativeStrokeWidth() { | 
|         return (this.strokeWidth / this.width * 100).toFixed(1); | 
|       }, | 
|       radius() { | 
|         if (this.type === 'circle' || this.type === 'dashboard') { | 
|           return parseInt(50 - parseFloat(this.relativeStrokeWidth) / 2, 10); | 
|         } else { | 
|           return 0; | 
|         } | 
|       }, | 
|       trackPath() { | 
|         const radius = this.radius; | 
|         const isDashboard = this.type === 'dashboard'; | 
|         return ` | 
|           M 50 50 | 
|           m 0 ${isDashboard ? '' : '-'}${radius} | 
|           a ${radius} ${radius} 0 1 1 0 ${isDashboard ? '-' : ''}${radius * 2} | 
|           a ${radius} ${radius} 0 1 1 0 ${isDashboard ? '' : '-'}${radius * 2} | 
|           `; | 
|       }, | 
|       perimeter() { | 
|         return 2 * Math.PI * this.radius; | 
|       }, | 
|       rate() { | 
|         return this.type === 'dashboard' ? 0.75 : 1; | 
|       }, | 
|       strokeDashoffset() { | 
|         const offset = -1 * this.perimeter * (1 - this.rate) / 2; | 
|         return `${offset}px`; | 
|       }, | 
|       trailPathStyle() { | 
|         return { | 
|           strokeDasharray: `${(this.perimeter * this.rate)}px, ${this.perimeter}px`, | 
|           strokeDashoffset: this.strokeDashoffset | 
|         }; | 
|       }, | 
|       circlePathStyle() { | 
|         return { | 
|           strokeDasharray: `${this.perimeter * this.rate * (this.percentage / 100) }px, ${this.perimeter}px`, | 
|           strokeDashoffset: this.strokeDashoffset, | 
|           transition: 'stroke-dasharray 0.6s ease 0s, stroke 0.6s ease' | 
|         }; | 
|       }, | 
|       stroke() { | 
|         let ret; | 
|         if (this.color) { | 
|           ret = this.getCurrentColor(this.percentage); | 
|         } else { | 
|           switch (this.status) { | 
|             case 'success': | 
|               ret = '#13ce66'; | 
|               break; | 
|             case 'exception': | 
|               ret = '#ff4949'; | 
|               break; | 
|             case 'warning': | 
|               ret = '#e6a23c'; | 
|               break; | 
|             default: | 
|               ret = '#20a0ff'; | 
|           } | 
|         } | 
|         return ret; | 
|       }, | 
|       iconClass() { | 
|         if (this.status === 'warning') { | 
|           return 'el-icon-warning'; | 
|         } | 
|         if (this.type === 'line') { | 
|           return this.status === 'success' ? 'el-icon-circle-check' : 'el-icon-circle-close'; | 
|         } else { | 
|           return this.status === 'success' ? 'el-icon-check' : 'el-icon-close'; | 
|         } | 
|       }, | 
|       progressTextSize() { | 
|         return this.type === 'line' | 
|           ? 12 + this.strokeWidth * 0.4 | 
|           : this.width * 0.111111 + 2 ; | 
|       }, | 
|       content() { | 
|         if (typeof this.format === 'function') { | 
|           return this.format(this.percentage) || ''; | 
|         } else { | 
|           return `${this.percentage}%`; | 
|         } | 
|       } | 
|     }, | 
|     methods: { | 
|       getCurrentColor(percentage) { | 
|         if (typeof this.color === 'function') { | 
|           return this.color(percentage); | 
|         } else if (typeof this.color === 'string') { | 
|           return this.color; | 
|         } else { | 
|           return this.getLevelColor(percentage); | 
|         } | 
|       }, | 
|       getLevelColor(percentage) { | 
|         const colorArray = this.getColorArray().sort((a, b) => a.percentage - b.percentage); | 
|   | 
|         for (let i = 0; i < colorArray.length; i++) { | 
|           if (colorArray[i].percentage > percentage) { | 
|             return colorArray[i].color; | 
|           } | 
|         } | 
|         return colorArray[colorArray.length - 1].color; | 
|       }, | 
|       getColorArray() { | 
|         const color = this.color; | 
|         const span = 100 / color.length; | 
|         return color.map((seriesColor, index) => { | 
|           if (typeof seriesColor === 'string') { | 
|             return { | 
|               color: seriesColor, | 
|               percentage: (index + 1) * span | 
|             }; | 
|           } | 
|           return seriesColor; | 
|         }); | 
|       } | 
|     } | 
|   }; | 
| </script> |