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>
|