人民医院前端

hyh-progress.vue 4.2KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191
  1. <template>
  2. <view class="circle-container" :style="boxSize">
  3. <view class="circle" :style="circleStyle">
  4. <view v-if="fillet" class="border-start" :style="borderStyle"></view>
  5. <view class="circle-left ab" :style="renderLeftRate(dispalyRate)"><view v-if="fillet && dispalyRate >= 50" class="border-left-end" :style="borderStyle"></view></view>
  6. <view class="circle-right ab" :style="renderRightRate(dispalyRate)"><view v-if="fillet && dispalyRate < 50" class="border-right-end" :style="borderStyle"></view></view>
  7. </view>
  8. <view class="text-area" :style="textareaStyle"><slot></slot></view>
  9. </view>
  10. </template>
  11. <script setup>
  12. import { computed, getCurrentInstance, onMounted, ref } from 'vue';
  13. const props = defineProps({
  14. rate: {
  15. type: Number,
  16. require: true
  17. },
  18. width: {
  19. type: String,
  20. default: '20rpx'
  21. },
  22. activeColor: {
  23. type: String,
  24. default: '#54c4fd'
  25. },
  26. inactiveColor: {
  27. type: String,
  28. default: '#546063'
  29. },
  30. startAngle: {
  31. type: Number,
  32. default: 0
  33. },
  34. fillet: {
  35. type: Boolean,
  36. default: false
  37. }
  38. });
  39. const dispalyRate = computed(() => {
  40. if (props.rate <= 0) {
  41. return 0;
  42. } else if (props.rate >= 100) {
  43. return 100;
  44. } else if (props.rate <= 3) {
  45. return 1;
  46. } else {
  47. return props.rate - 3;
  48. }
  49. });
  50. const circleStyle = computed(() => {
  51. const width = getNumberAndUnit(props.width).num - 0.3;
  52. const unit = getNumberAndUnit(props.width).unit;
  53. return `box-shadow: inset 0 0 0 ${width + unit} ${props.activeColor};transform:rotate(${props.startAngle}deg)`;
  54. });
  55. const borderStyle = computed(() => {
  56. if (dispalyRate.value == 0) return '';
  57. return `width:${props.width};height:${props.width};background-color:${props.activeColor};`;
  58. });
  59. const textareaStyle = computed(() => {
  60. return `width:calc(100% - ${props.width});height:calc(100% - ${props.width});`;
  61. });
  62. onMounted(() => {
  63. getSize();
  64. });
  65. const renderRightRate = rate => {
  66. const border = `border: ${props.width} solid ${props.inactiveColor};`;
  67. if (rate < 50) {
  68. return border + 'transform: rotate(' + 3.6 * rate + 'deg);';
  69. } else {
  70. return border + `transform: rotate(0);border-color: ${props.activeColor};`;
  71. }
  72. };
  73. const renderLeftRate = rate => {
  74. const border = `border: ${props.width} solid ${props.inactiveColor};`;
  75. if (rate >= 50) {
  76. return border + 'transform: rotate(' + 3.6 * (rate - 50) + 'deg);';
  77. } else {
  78. return border;
  79. }
  80. };
  81. const boxSize = ref('');
  82. function getSize() {
  83. getWidth().then(res => {
  84. const { width, height } = res;
  85. const size = width < height ? width : height;
  86. boxSize.value = `width:${size}px;height:${size}px;`;
  87. });
  88. }
  89. function getWidth() {
  90. return new Promise((resolve, reject) => {
  91. try {
  92. const { ctx } = getCurrentInstance();
  93. uni.createSelectorQuery()
  94. .in(ctx)
  95. .select('.circle-container')
  96. .boundingClientRect(res => {
  97. resolve(res);
  98. })
  99. .exec();
  100. } catch (e) {
  101. //TODO handle the exception
  102. reject(e);
  103. }
  104. });
  105. }
  106. function getNumberAndUnit(str) {
  107. const numReg = /\d+/g;
  108. const unitReg = /[a-z]+/;
  109. const num = str.match(numReg);
  110. const unit = str.match(unitReg);
  111. return {
  112. num: num[0],
  113. unit: unit[0]
  114. };
  115. }
  116. </script>
  117. <style lang="scss" scoped>
  118. .circle-container {
  119. position: relative;
  120. width: 100%;
  121. height: 100%;
  122. .circle {
  123. position: relative;
  124. width: 100%;
  125. height: 100%;
  126. border-radius: 50%;
  127. .ab {
  128. position: absolute;
  129. left: 0;
  130. top: 0;
  131. width: 100%;
  132. height: 100%;
  133. }
  134. .circle-left {
  135. border-radius: 50%;
  136. clip-path: polygon(0% 0%, 50% 0%, 50% 100%, 0% 100%);
  137. }
  138. .circle-right {
  139. border-radius: 50%;
  140. clip-path: polygon(50% 0%, 100% 0%, 100% 100%, 50% 100%);
  141. }
  142. .border-start {
  143. position: absolute;
  144. top: 0;
  145. left: 50%;
  146. transform: translateX(-50%);
  147. z-index: 1;
  148. border-radius: 50%;
  149. }
  150. .border-left-end {
  151. position: absolute;
  152. bottom: 0px;
  153. left: 50%;
  154. transform: translate(-50%, 100%);
  155. z-index: 1;
  156. border-radius: 50%;
  157. }
  158. .border-right-end {
  159. position: absolute;
  160. top: 0;
  161. left: 50%;
  162. transform: translate(-50%, -100%);
  163. z-index: 1;
  164. border-radius: 50%;
  165. }
  166. }
  167. .text-area {
  168. position: absolute;
  169. top: 50%;
  170. left: 50%;
  171. transform: translate(-50%, -50%);
  172. width: 20px;
  173. height: 20px;
  174. border-radius: 50%;
  175. display: flex;
  176. flex-direction: column;
  177. align-items: center;
  178. justify-content: center;
  179. }
  180. }
  181. </style>