UNPKG

6.52 kBPlain TextView Raw
1<template>
2 <div
3 class="el-progress"
4 :class="[
5 'el-progress--' + type,
6 status ? 'is-' + status : '',
7 {
8 'el-progress--without-text': !showText,
9 'el-progress--text-inside': textInside,
10 }
11 ]"
12 role="progressbar"
13 :aria-valuenow="percentage"
14 aria-valuemin="0"
15 aria-valuemax="100"
16 >
17 <div class="el-progress-bar" v-if="type === 'line'">
18 <div class="el-progress-bar__outer" :style="{height: strokeWidth + 'px'}">
19 <div class="el-progress-bar__inner" :style="barStyle">
20 <div class="el-progress-bar__innerText" v-if="showText && textInside">{{content}}</div>
21 </div>
22 </div>
23 </div>
24 <div class="el-progress-circle" :style="{height: width + 'px', width: width + 'px'}" v-else>
25 <svg viewBox="0 0 100 100">
26 <path
27 class="el-progress-circle__track"
28 :d="trackPath"
29 stroke="#e5e9f2"
30 :stroke-width="relativeStrokeWidth"
31 fill="none"
32 :style="trailPathStyle"></path>
33 <path
34 class="el-progress-circle__path"
35 :d="trackPath"
36 :stroke="stroke"
37 fill="none"
38 :stroke-linecap="strokeLinecap"
39 :stroke-width="percentage ? relativeStrokeWidth : 0"
40 :style="circlePathStyle"></path>
41 </svg>
42 </div>
43 <div
44 class="el-progress__text"
45 v-if="showText && !textInside"
46 :style="{fontSize: progressTextSize + 'px'}"
47 >
48 <template v-if="!status">{{content}}</template>
49 <i v-else :class="iconClass"></i>
50 </div>
51 </div>
52</template>
53<script>
54 export default {
55 name: 'ElProgress',
56 props: {
57 type: {
58 type: String,
59 default: 'line',
60 validator: val => ['line', 'circle', 'dashboard'].indexOf(val) > -1
61 },
62 percentage: {
63 type: Number,
64 default: 0,
65 required: true,
66 validator: val => val >= 0 && val <= 100
67 },
68 status: {
69 type: String,
70 validator: val => ['success', 'exception', 'warning'].indexOf(val) > -1
71 },
72 strokeWidth: {
73 type: Number,
74 default: 6
75 },
76 strokeLinecap: {
77 type: String,
78 default: 'round'
79 },
80 textInside: {
81 type: Boolean,
82 default: false
83 },
84 width: {
85 type: Number,
86 default: 126
87 },
88 showText: {
89 type: Boolean,
90 default: true
91 },
92 color: {
93 type: [String, Array, Function],
94 default: ''
95 },
96 format: Function
97 },
98 computed: {
99 barStyle() {
100 const style = {};
101 style.width = this.percentage + '%';
102 style.backgroundColor = this.getCurrentColor(this.percentage);
103 return style;
104 },
105 relativeStrokeWidth() {
106 return (this.strokeWidth / this.width * 100).toFixed(1);
107 },
108 radius() {
109 if (this.type === 'circle' || this.type === 'dashboard') {
110 return parseInt(50 - parseFloat(this.relativeStrokeWidth) / 2, 10);
111 } else {
112 return 0;
113 }
114 },
115 trackPath() {
116 const radius = this.radius;
117 const isDashboard = this.type === 'dashboard';
118 return `
119 M 50 50
120 m 0 ${isDashboard ? '' : '-'}${radius}
121 a ${radius} ${radius} 0 1 1 0 ${isDashboard ? '-' : ''}${radius * 2}
122 a ${radius} ${radius} 0 1 1 0 ${isDashboard ? '' : '-'}${radius * 2}
123 `;
124 },
125 perimeter() {
126 return 2 * Math.PI * this.radius;
127 },
128 rate() {
129 return this.type === 'dashboard' ? 0.75 : 1;
130 },
131 strokeDashoffset() {
132 const offset = -1 * this.perimeter * (1 - this.rate) / 2;
133 return `${offset}px`;
134 },
135 trailPathStyle() {
136 return {
137 strokeDasharray: `${(this.perimeter * this.rate)}px, ${this.perimeter}px`,
138 strokeDashoffset: this.strokeDashoffset
139 };
140 },
141 circlePathStyle() {
142 return {
143 strokeDasharray: `${this.perimeter * this.rate * (this.percentage / 100) }px, ${this.perimeter}px`,
144 strokeDashoffset: this.strokeDashoffset,
145 transition: 'stroke-dasharray 0.6s ease 0s, stroke 0.6s ease'
146 };
147 },
148 stroke() {
149 let ret;
150 if (this.color) {
151 ret = this.getCurrentColor(this.percentage);
152 } else {
153 switch (this.status) {
154 case 'success':
155 ret = '#13ce66';
156 break;
157 case 'exception':
158 ret = '#ff4949';
159 break;
160 case 'warning':
161 ret = '#e6a23c';
162 break;
163 default:
164 ret = '#20a0ff';
165 }
166 }
167 return ret;
168 },
169 iconClass() {
170 if (this.status === 'warning') {
171 return 'el-icon-warning';
172 }
173 if (this.type === 'line') {
174 return this.status === 'success' ? 'el-icon-circle-check' : 'el-icon-circle-close';
175 } else {
176 return this.status === 'success' ? 'el-icon-check' : 'el-icon-close';
177 }
178 },
179 progressTextSize() {
180 return this.type === 'line'
181 ? 12 + this.strokeWidth * 0.4
182 : this.width * 0.111111 + 2 ;
183 },
184 content() {
185 if (typeof this.format === 'function') {
186 return this.format(this.percentage) || '';
187 } else {
188 return `${this.percentage}%`;
189 }
190 }
191 },
192 methods: {
193 getCurrentColor(percentage) {
194 if (typeof this.color === 'function') {
195 return this.color(percentage);
196 } else if (typeof this.color === 'string') {
197 return this.color;
198 } else {
199 return this.getLevelColor(percentage);
200 }
201 },
202 getLevelColor(percentage) {
203 const colorArray = this.getColorArray().sort((a, b) => a.percentage - b.percentage);
204
205 for (let i = 0; i < colorArray.length; i++) {
206 if (colorArray[i].percentage > percentage) {
207 return colorArray[i].color;
208 }
209 }
210 return colorArray[colorArray.length - 1].color;
211 },
212 getColorArray() {
213 const color = this.color;
214 const span = 100 / color.length;
215 return color.map((seriesColor, index) => {
216 if (typeof seriesColor === 'string') {
217 return {
218 color: seriesColor,
219 percentage: (index + 1) * span
220 };
221 }
222 return seriesColor;
223 });
224 }
225 }
226 };
227</script>