cl-loading.uvue 2.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140
  1. <template>
  2. <view
  3. ref="loadingRef"
  4. class="cl-loading"
  5. :class="[
  6. {
  7. 'cl-loading--dark': isDark && color == '',
  8. '!border-r-transparent': true
  9. },
  10. pt.className
  11. ]"
  12. :style="{
  13. height: getPx(size!),
  14. width: getPx(size!),
  15. borderWidth: '1px',
  16. borderTopColor: color,
  17. borderRightColor: 'transparent',
  18. borderBottomColor: color,
  19. borderLeftColor: color
  20. }"
  21. v-if="loading"
  22. >
  23. </view>
  24. </template>
  25. <script setup lang="ts">
  26. import { computed, onMounted, shallowRef, watch } from "vue";
  27. import { createAnimation, ctx, isDark, parsePt } from "@/cool";
  28. import type { ClIconProps } from "../cl-icon/props";
  29. import { useSize } from "../../hooks";
  30. defineOptions({
  31. name: "cl-loading"
  32. });
  33. // 定义组件属性
  34. const props = defineProps({
  35. // 透传样式
  36. pt: {
  37. type: Object,
  38. default: () => ({})
  39. },
  40. // 是否加载中
  41. loading: {
  42. type: Boolean,
  43. default: true
  44. },
  45. // 图标大小
  46. size: {
  47. type: [Number, String],
  48. default: 24
  49. },
  50. // 图标颜色
  51. color: {
  52. type: String,
  53. default: ""
  54. }
  55. });
  56. const { getPx } = useSize();
  57. // 透传样式类型定义
  58. type PassThrough = {
  59. className?: string;
  60. icon?: ClIconProps;
  61. };
  62. // 解析透传样式
  63. const pt = computed(() => parsePt<PassThrough>(props.pt));
  64. // 组件引用
  65. const loadingRef = shallowRef<UniElement | null>(null);
  66. // 颜色值
  67. const color = computed<string>(() => {
  68. if (props.color == "") {
  69. return isDark.value ? "#ffffff" : (ctx.color["surface-700"] as string);
  70. }
  71. switch (props.color) {
  72. case "primary":
  73. return ctx.color["primary-500"] as string;
  74. case "success":
  75. return "#22c55e";
  76. case "warn":
  77. return "#eab308";
  78. case "error":
  79. return "#ef4444";
  80. case "info":
  81. return "#71717a";
  82. case "dark":
  83. return "#3f3f46";
  84. case "light":
  85. return "#ffffff";
  86. case "disabled":
  87. return "#d4d4d8";
  88. default:
  89. return props.color;
  90. }
  91. });
  92. // 开始旋转动画
  93. async function start() {
  94. createAnimation(loadingRef.value, {
  95. duration: 2500,
  96. loop: -1,
  97. timingFunction: "linear"
  98. })
  99. .rotate("0deg", "360deg")
  100. .play();
  101. }
  102. // 组件挂载后监听loading状态
  103. onMounted(() => {
  104. watch(
  105. computed(() => props.loading),
  106. (val: boolean) => {
  107. // 当loading为true时开始旋转
  108. if (val) {
  109. start();
  110. }
  111. },
  112. {
  113. immediate: true
  114. }
  115. );
  116. });
  117. </script>
  118. <style lang="scss" scoped>
  119. .cl-loading {
  120. @apply flex flex-row items-center justify-center rounded-full;
  121. @apply border-surface-700 border-solid;
  122. &--dark {
  123. border-color: white !important;
  124. border-right-color: transparent !important;
  125. }
  126. }
  127. </style>