83f0f115686c208c2bf85357476ebfb8dd1c7f0420720d77aad238e2c0efb2a340d909a30982d55b2b1b3f62f23e4e87e379dff9e08215d6b4292b0ae7df92 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235
  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', backgroundColor:defineBackColor}">
  19. <div class="el-progress-bar__inner" :style="barStyle">
  20. <div class="el-progress-bar__innerText" :style="{color:textColor}" 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="defineBackColor"
  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', color:textColor}"
  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. defineBackColor: {
  97. type: [String, Array, Function],
  98. default: '#ebeef5'
  99. },
  100. textColor: {
  101. type: [String, Array, Function],
  102. default: '#606266'
  103. },
  104. format: Function
  105. },
  106. computed: {
  107. barStyle() {
  108. const style = {};
  109. style.width = this.percentage + '%';
  110. style.backgroundColor = this.getCurrentColor(this.percentage);
  111. return style;
  112. },
  113. relativeStrokeWidth() {
  114. return (this.strokeWidth / this.width * 100).toFixed(1);
  115. },
  116. radius() {
  117. if (this.type === 'circle' || this.type === 'dashboard') {
  118. return parseInt(50 - parseFloat(this.relativeStrokeWidth) / 2, 10);
  119. } else {
  120. return 0;
  121. }
  122. },
  123. trackPath() {
  124. const radius = this.radius;
  125. const isDashboard = this.type === 'dashboard';
  126. return `
  127. M 50 50
  128. m 0 ${isDashboard ? '' : '-'}${radius}
  129. a ${radius} ${radius} 0 1 1 0 ${isDashboard ? '-' : ''}${radius * 2}
  130. a ${radius} ${radius} 0 1 1 0 ${isDashboard ? '' : '-'}${radius * 2}
  131. `;
  132. },
  133. perimeter() {
  134. return 2 * Math.PI * this.radius;
  135. },
  136. rate() {
  137. return this.type === 'dashboard' ? 0.75 : 1;
  138. },
  139. strokeDashoffset() {
  140. const offset = -1 * this.perimeter * (1 - this.rate) / 2;
  141. return `${offset}px`;
  142. },
  143. trailPathStyle() {
  144. return {
  145. strokeDasharray: `${(this.perimeter * this.rate)}px, ${this.perimeter}px`,
  146. strokeDashoffset: this.strokeDashoffset
  147. };
  148. },
  149. circlePathStyle() {
  150. return {
  151. strokeDasharray: `${this.perimeter * this.rate * (this.percentage / 100) }px, ${this.perimeter}px`,
  152. strokeDashoffset: this.strokeDashoffset,
  153. transition: 'stroke-dasharray 0.6s ease 0s, stroke 0.6s ease'
  154. };
  155. },
  156. stroke() {
  157. let ret;
  158. if (this.color) {
  159. ret = this.getCurrentColor(this.percentage);
  160. } else {
  161. switch (this.status) {
  162. case 'success':
  163. ret = '#13ce66';
  164. break;
  165. case 'exception':
  166. ret = '#ff4949';
  167. break;
  168. case 'warning':
  169. ret = '#e6a23c';
  170. break;
  171. default:
  172. ret = '#20a0ff';
  173. }
  174. }
  175. return ret;
  176. },
  177. iconClass() {
  178. if (this.status === 'warning') {
  179. return 'el-icon-warning';
  180. }
  181. if (this.type === 'line') {
  182. return this.status === 'success' ? 'el-icon-circle-check' : 'el-icon-circle-close';
  183. } else {
  184. return this.status === 'success' ? 'el-icon-check' : 'el-icon-close';
  185. }
  186. },
  187. progressTextSize() {
  188. return this.type === 'line'
  189. ? 12 + this.strokeWidth * 0.4
  190. : this.width * 0.111111 + 2 ;
  191. },
  192. content() {
  193. if (typeof this.format === 'function') {
  194. return this.format(this.percentage) || '';
  195. } else {
  196. return `${this.percentage}%`;
  197. }
  198. }
  199. },
  200. methods: {
  201. getCurrentColor(percentage) {
  202. if (typeof this.color === 'function') {
  203. return this.color(percentage);
  204. } else if (typeof this.color === 'string') {
  205. return this.color;
  206. } else {
  207. return this.getLevelColor(percentage);
  208. }
  209. },
  210. getLevelColor(percentage) {
  211. const colorArray = this.getColorArray().sort((a, b) => a.percentage - b.percentage);
  212. for (let i = 0; i < colorArray.length; i++) {
  213. if (colorArray[i].percentage > percentage) {
  214. return colorArray[i].color;
  215. }
  216. }
  217. return colorArray[colorArray.length - 1].color;
  218. },
  219. getColorArray() {
  220. const color = this.color;
  221. const span = 100 / color.length;
  222. return color.map((seriesColor, index) => {
  223. if (typeof seriesColor === 'string') {
  224. return {
  225. color: seriesColor,
  226. percentage: (index + 1) * span
  227. };
  228. }
  229. return seriesColor;
  230. });
  231. }
  232. }
  233. };
  234. </script>