loading-empty-wrapper.vue 2.8 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697
  1. <template>
  2. <div v-if="reloadFlag" class="relative">
  3. <slot></slot>
  4. <div v-show="showPlaceholder" class="absolute-lt w-full h-full" :class="placeholderClass">
  5. <div v-show="loading" class="absolute-center">
  6. <n-spin :show="true" :size="loadingSize" />
  7. </div>
  8. <div v-show="isEmpty" class="absolute-center">
  9. <div class="relative">
  10. <icon-local-empty-data :class="iconClass" />
  11. <p class="absolute-lb w-full text-center" :class="descClass">{{ emptyDesc }}</p>
  12. </div>
  13. </div>
  14. <div v-show="!network" class="absolute-center">
  15. <div class="relative" :class="{ 'cursor-pointer': showNetworkReload }" @click="handleReload">
  16. <icon-local-network-error :class="iconClass" />
  17. <p class="absolute-lb w-full text-center" :class="descClass">{{ networkErrorDesc }}</p>
  18. </div>
  19. </div>
  20. </div>
  21. </div>
  22. </template>
  23. <script setup lang="ts">
  24. import { computed, nextTick, onUnmounted, watch } from 'vue';
  25. import { NETWORK_ERROR_MSG } from '@/config';
  26. import { useBoolean } from '@/hooks';
  27. defineOptions({ name: 'LoadingEmptyWrapper' });
  28. interface Props {
  29. /** 是否加载 */
  30. loading: boolean;
  31. /** 是否为空 */
  32. empty?: boolean;
  33. /** 加载图标的大小 */
  34. loadingSize?: 'small' | 'medium' | 'large';
  35. /** 中间占位符的class */
  36. placeholderClass?: string;
  37. /** 空数据描述文本 */
  38. emptyDesc?: string;
  39. /** 图标的class */
  40. iconClass?: string;
  41. /** 描述文本的class */
  42. descClass?: string;
  43. /** 显示网络异常的重试点击按钮 */
  44. showNetworkReload?: boolean;
  45. }
  46. const props = withDefaults(defineProps<Props>(), {
  47. loading: false,
  48. empty: false,
  49. loadingSize: 'medium',
  50. placeholderClass: 'bg-white dark:bg-dark transition-background-color duration-300 ease-in-out',
  51. emptyDesc: '暂无数据',
  52. iconClass: 'text-320px text-primary',
  53. descClass: 'text-16px text-#666',
  54. showNetworkReload: false
  55. });
  56. // 网络状态
  57. const { bool: network, setBool: setNetwork } = useBoolean(window.navigator.onLine);
  58. const { bool: reloadFlag, setBool: setReload } = useBoolean(true);
  59. // 数据是否为空
  60. const isEmpty = computed(() => props.empty && !props.loading && network.value);
  61. const showPlaceholder = computed(() => props.loading || isEmpty.value || !network.value);
  62. const networkErrorDesc = computed(() =>
  63. props.showNetworkReload ? `${NETWORK_ERROR_MSG}, 点击重试` : NETWORK_ERROR_MSG
  64. );
  65. function handleReload() {
  66. if (!props.showNetworkReload) return;
  67. setReload(false);
  68. nextTick(() => {
  69. setReload(true);
  70. });
  71. }
  72. const stopHandle = watch(
  73. () => props.loading,
  74. newValue => {
  75. // 结束加载判断一下网络状态
  76. if (!newValue) {
  77. setNetwork(window.navigator.onLine);
  78. }
  79. }
  80. );
  81. onUnmounted(() => {
  82. stopHandle();
  83. });
  84. </script>
  85. <style scoped></style>