Explorar el Código

refactor: add loading component and demo

vben hace 4 años
padre
commit
5db3ce7737
Se han modificado 38 ficheros con 539 adiciones y 342 borrados
  1. 1 1
      CHANGELOG.en_US.md
  2. 11 1
      CHANGELOG.zh_CN.md
  3. 1 0
      build/config/lessModifyVars.ts
  4. 121 53
      index.html
  5. 1 1
      package.json
  6. 0 49
      public/resource/img/loading.svg
  7. 0 67
      src/assets/images/loading.svg
  8. 4 0
      src/components/Application/src/AppLocalePicker.vue
  9. 4 0
      src/components/Application/src/AppLogo.vue
  10. 3 5
      src/components/Drawer/src/BasicDrawer.tsx
  11. 0 55
      src/components/Loading/BasicLoading.vue
  12. 0 50
      src/components/Loading/FullLoading.vue
  13. 8 2
      src/components/Loading/index.ts
  14. 60 0
      src/components/Loading/src/createLoading.ts
  15. 82 0
      src/components/Loading/src/index.vue
  16. 9 0
      src/components/Loading/src/indicator.tsx
  17. 10 0
      src/components/Loading/src/types.ts
  18. 39 0
      src/components/Loading/src/useLoading.ts
  19. 0 8
      src/components/Loading/type.ts
  20. 10 7
      src/components/Preview/src/index.tsx
  21. 0 5
      src/design/public.less
  22. 1 0
      src/hooks/web/useI18n.ts
  23. 0 4
      src/layouts/default/content/index.less
  24. 5 11
      src/layouts/default/content/index.tsx
  25. 1 0
      src/layouts/page/index.tsx
  26. 2 0
      src/locales/lang/en/routes/demo/comp.ts
  27. 2 0
      src/locales/lang/zh_CN/routes/demo/comp.ts
  28. 4 0
      src/router/menus/modules/demo/comp.ts
  29. 8 0
      src/router/routes/modules/demo/comp.ts
  30. 0 2
      src/setup/ant-design-vue/index.ts
  31. 0 12
      src/setup/ant-design-vue/spin.tsx
  32. 2 0
      src/setup/directives/index.ts
  33. 43 0
      src/setup/directives/loading.ts
  34. 2 1
      src/setup/i18n/index.ts
  35. 96 0
      src/views/demo/comp/loading/index.vue
  36. 1 1
      tsconfig.json
  37. 2 2
      vite.config.ts
  38. 6 5
      yarn.lock

+ 1 - 1
CHANGELOG.en_US.md

@@ -3,7 +3,7 @@
 ## (破坏性更新) Breaking changes
 
 - The ClickOutSide component import method is changed from `import ClickOutSide from'/@/components/ClickOutSide/index.vue'` to `import {ClickOutSide} from'/@/components/ClickOutSide'`
-- Button component import method changed from `import ClickOutSide from'/@/components/Button/index.vue'` to `import {Button} from'/@/components/Button'`
+- Button component import method changed from `import Button from'/@/components/Button/index.vue'` to `import {Button} from'/@/components/Button'`
 - StrengthMeter component import method is changed from `import StrengthMeter from'/@/components/StrengthMeter'` to `import {StrengthMeter} from'/@/components/StrengthMeter'`
 - In addition to the examples, the global internationalization function is added, supporting Chinese and English
 

+ 11 - 1
CHANGELOG.zh_CN.md

@@ -1,9 +1,19 @@
+## Wip
+
+### ✨ Features
+
+- 还原 antdv 默认 loading,重构 `Loading` 组件,增加`useLoading`和`v-loading`指令。并增加示例
+
+### 🎫 Chores
+
+- 首屏 loading 修改
+
 ## 2.0.0-rc.12 (2020-11-30)
 
 ## (破坏性更新) Breaking changes
 
 - ClickOutSide 组件引入方式由 `import ClickOutSide from '/@/components/ClickOutSide/index.vue'`变更为`import { ClickOutSide } from '/@/components/ClickOutSide'`
-- Button 组件引入方式由 `import ClickOutSide from '/@/components/Button/index.vue'`变更为`import { Button } from '/@/components/Button'`
+- Button 组件引入方式由 `import Button from '/@/components/Button/index.vue'`变更为`import { Button } from '/@/components/Button'`
 - StrengthMeter 组件引入方式由 `import StrengthMeter from '/@/components/StrengthMeter'`变更为`import { StrengthMeter } from '/@/components/StrengthMeter'`
 - 除示例外加入全局国际化功能,支持中文与英文
 

+ 1 - 0
build/config/lessModifyVars.ts

@@ -2,6 +2,7 @@
  * less global variable
  */
 const primaryColor = '#018ffb';
+// const primaryColor = '#0065cc';
 //{
 const modifyVars = {
   'primary-color': primaryColor, //  Global dominant color

+ 121 - 53
index.html

@@ -13,68 +13,136 @@
     <title></title>
     <link rel="icon" href="/favicon.ico" />
     <%= viteHtmlPluginOptions.injectConfig %>
+  </head>
+  <body>
+    <div id="app">
+      <style>
+        .app-loading {
+          display: flex;
+          width: 100%;
+          height: 100%;
+          justify-content: center;
+          align-items: center;
+          flex-direction: column;
+        }
 
-    <style>
-      @keyframes load {
-        0% {
-          -webkit-transform: rotate(-360deg);
-          transform: rotate(-360deg);
+        .app-loading .app-loading-wrap {
+          position: absolute;
+          top: 50%;
+          left: 50%;
+          display: flex;
+          -webkit-transform: translate3d(-50%, -50%, 0);
+          transform: translate3d(-50%, -50%, 0);
+          justify-content: center;
+          align-items: center;
+          flex-direction: column;
         }
 
-        100% {
-          -webkit-transform: rotate(0);
-          transform: rotate(0);
+        .app-loading .dots {
+          display: flex;
+          padding: 98px;
+          justify-content: center;
+          align-items: center;
         }
-      }
 
-      .app-loading {
-        width: 100%;
-        height: 100%;
-        background: #f0f2f5;
-      }
+        .app-loading .app-loading-title {
+          display: flex;
+          margin-top: 30px;
+          font-size: 30px;
+          color: rgba(0, 0, 0, 0.85);
+          justify-content: center;
+          align-items: center;
+        }
 
-      .app-loading .app-loading-wrap {
-        position: absolute;
-        top: 50%;
-        left: 50%;
-        -webkit-transform: translate3d(-50%, -50%, 0);
-        transform: translate3d(-50%, -50%, 0);
-      }
+        .app-loading .app-loading-logo {
+          display: block;
+          width: 90px;
+          margin: 0 auto;
+          margin-bottom: 20px;
+        }
 
-      .app-loading .g-loading {
-        display: block;
-        width: 48px;
-        margin: 30px auto;
-        -webkit-animation: load 1.2s linear infinite;
-        animation: load 1.2s linear infinite;
-        -webkit-transform-origin: center center;
-        transform-origin: center center;
-      }
+        .dot {
+          position: relative;
+          display: inline-block;
+          width: 48px;
+          height: 48px;
+          margin-top: 30px;
+          font-size: 32px;
+          transform: rotate(45deg);
+          box-sizing: border-box;
+          animation: antRotate 1.2s infinite linear;
+        }
 
-      .app-loading .app-loading-wrap img.logo {
-        display: block;
-        width: 90px;
-        margin: 0 auto;
-        margin-bottom: 20px;
-      }
+        .dot i {
+          position: absolute;
+          display: block;
+          width: 20px;
+          height: 20px;
+          background-color: #0065cc;
+          border-radius: 100%;
+          opacity: 0.3;
+          transform: scale(0.75);
+          animation: antSpinMove 1s infinite linear alternate;
+          transform-origin: 50% 50%;
+        }
 
-      .app-loading .app-loading-wrap .app-loading__tip {
-        display: block;
-        margin: 20px auto 0 0;
-        font-size: 30px;
-        color: rgba(0, 0, 0, 0.85);
-      }
-    </style>
-  </head>
-  <body>
-    <div id="app">
-      <section class="app-loading">
-        <section class="app-loading-wrap">
-          <img src="./resource/img/logo.png" class="logo" alt="Logo" />
-          <img src="./resource/img/loading.svg" alt="" class="g-loading" />
-          <h1 class="app-loading__tip"><%= viteHtmlPluginOptions.title %></h1>
-        </section>
-      </section>
+        .dot i:nth-child(1) {
+          top: 0;
+          left: 0;
+        }
+
+        .dot i:nth-child(2) {
+          top: 0;
+          right: 0;
+          -webkit-animation-delay: 0.4s;
+          animation-delay: 0.4s;
+        }
+
+        .dot i:nth-child(3) {
+          right: 0;
+          bottom: 0;
+          -webkit-animation-delay: 0.8s;
+          animation-delay: 0.8s;
+        }
+
+        .dot i:nth-child(4) {
+          bottom: 0;
+          left: 0;
+          -webkit-animation-delay: 1.2s;
+          animation-delay: 1.2s;
+        }
+        @keyframes antRotate {
+          to {
+            -webkit-transform: rotate(405deg);
+            transform: rotate(405deg);
+          }
+        }
+        @-webkit-keyframes antRotate {
+          to {
+            -webkit-transform: rotate(405deg);
+            transform: rotate(405deg);
+          }
+        }
+        @keyframes antSpinMove {
+          to {
+            opacity: 1;
+          }
+        }
+        @-webkit-keyframes antSpinMove {
+          to {
+            opacity: 1;
+          }
+        }
+      </style>
+      <div class="app-loading">
+        <div class="app-loading-wrap">
+          <img src="./resource/img/logo.png" class="app-loading-logo" alt="Logo" />
+          <div class="app-loading-dots">
+            <span class="dot dot-spin"><i></i><i></i><i></i><i></i></span>
+          </div>
+          <div class="app-loading-title"><%= viteHtmlPluginOptions.title %></div>
+        </div>
+      </div>
     </div>
     <script type="module" src="/src/main.ts"></script>
   </body>

+ 1 - 1
package.json

@@ -22,7 +22,7 @@
   },
   "dependencies": {
     "@iconify/iconify": "^2.0.0-rc.2",
-    "ant-design-vue": "2.0.0-rc.2",
+    "ant-design-vue": "^2.0.0-rc.2",
     "apexcharts": "3.22.0",
     "axios": "^0.21.0",
     "crypto-es": "^1.2.6",

+ 0 - 49
public/resource/img/loading.svg

@@ -1,49 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<svg viewBox="0 0 200 200" version="1.1" 
-  xmlns="http://www.w3.org/2000/svg" 
-  xmlns:xlink="http://www.w3.org/1999/xlink">
-  <style type="text/css">
-        .left-linear {
-          fill: url(#left-linear);
-        }
-
-        .right-linear {
-          fill: url(#right-linear);
-        }
-
-        .top {
-          fill: #64acff;
-        }
-
-        .bottom {
-          fill: #9dbfe4;
-        }
-        svg {
-          display: block;
-        }
-
-        .tip {
-          display: block;
-          min-width: 100px;
-          margin-top: 4px;
-          font-size: 13px;
-          color: rgba(0, 0, 0, 0.85);;
-          text-align: left;
-        }
-  </style>
-  <circle cx="97" cy="97" r="81" stroke-width="16" stroke="#327fd8" fill="none"></circle>
-  <g class="load">
-    <linearGradient id="left-linear" gradientUnits="userSpaceOnUse" x1="50" y1="0" x2="100" y2="180">
-      <stop offset="0" style="stop-color: #64acff;" />
-      <stop offset="1" style="stop-color: #9DBFE4;" />
-    </linearGradient>
-    <path class="left-linear" d="M20,100c0-44.1,35.9-80,80-80V0C44.8,0,0,44.8,0,100s44.8,100,100,100v-20C55.9,180,20,144.1,20,100z" />
-    <circle class="bottom" cx="100" cy="190" r="10" />
-    <linearGradient id="right-linear" gradientUnits="userSpaceOnUse" x1="100" y1="120" x2="100" y2="180">
-      <stop offset="0" style="stop-color: transparent;" />
-      <stop offset="1" style="stop-color: transparent;" />
-    </linearGradient>
-    <path class="right-linear" d="M100,0v20c44.1,0,80,35.9,80,80c0,44.1-35.9,80-80,80v20c55.2,0,100-44.8,100-100S155.2,0,100,0z" />
-    <circle class="top" cx="100" cy="10" r="10" />
-  </g>
-</svg>

+ 0 - 67
src/assets/images/loading.svg

@@ -1,67 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<svg viewBox="0 0 200 200" version="1.1" 
-  xmlns="http://www.w3.org/2000/svg" 
-  xmlns:xlink="http://www.w3.org/1999/xlink">
-  <style type="text/css">
-        .left-linear {
-          fill: url(#left-linear);
-        }
-
-        .right-linear {
-          fill: url(#right-linear);
-        }
-
-        .top {
-          fill: #64acff;
-        }
-
-        .bottom {
-          fill: #9dbfe4;
-        }
-        @keyframes load {
-          0% {
-            transform: rotate(-360deg);
-          }
-
-          100% {
-            transform: rotate(0);
-          }
-        }
-
-        .load {
-          animation: load 1.4s linear infinite;
-          transform-origin: center center;
-        }
-
-        svg {
-          display: block;
-        }
-
-        .tip {
-          display: block;
-          min-width: 100px;
-          margin-top: 4px;
-          font-size: 13px;
-          color: #303133;
-          text-align: left;
-        }
-  </style>
-  <circle cx="97" cy="97" r="81" stroke-width="16" stroke="#327fd8" fill="none"></circle>
-  <g class="load">
-    <!--右半圆环-->
-    <linearGradient id="left-linear" gradientUnits="userSpaceOnUse" x1="50" y1="0" x2="100" y2="180">
-      <stop offset="0" style="stop-color: #64acff;" />
-      <stop offset="1" style="stop-color: #9DBFE4;" />
-    </linearGradient>
-    <path class="left-linear" d="M20,100c0-44.1,35.9-80,80-80V0C44.8,0,0,44.8,0,100s44.8,100,100,100v-20C55.9,180,20,144.1,20,100z" />
-    <!--左半圆环-->
-    <circle class="bottom" cx="100" cy="190" r="10" />
-    <linearGradient id="right-linear" gradientUnits="userSpaceOnUse" x1="100" y1="120" x2="100" y2="180">
-      <stop offset="0" style="stop-color: transparent;" />
-      <stop offset="1" style="stop-color: transparent;" />
-    </linearGradient>
-    <path class="right-linear" d="M100,0v20c44.1,0,80,35.9,80,80c0,44.1-35.9,80-80,80v20c55.2,0,100-44.8,100-100S155.2,0,100,0z" />
-    <!--左半圆环-->
-    <circle class="top" cx="100" cy="10" r="10" />
-  </g>
-</svg>

+ 4 - 0
src/components/Application/src/AppLocalePicker.vue

@@ -1,3 +1,7 @@
+<!--
+ * @Author: Vben
+ * @Description: Multi-language switching component
+-->
 <template>
   <Dropdown
     :trigger="['click']"

+ 4 - 0
src/components/Application/src/AppLogo.vue

@@ -1,3 +1,7 @@
+<!--
+ * @Author: Vben
+ * @Description: logo component
+-->
 <template>
   <div
     class="app-logo anticon"

+ 3 - 5
src/components/Drawer/src/BasicDrawer.tsx

@@ -7,7 +7,7 @@ import { defineComponent, ref, computed, watchEffect, watch, unref, nextTick, to
 import { Drawer, Row, Col, Button } from 'ant-design-vue';
 
 import { BasicTitle } from '/@/components/Basic';
-import { FullLoading } from '/@/components/Loading/index';
+import { Loading } from '/@/components/Loading';
 import { LeftOutlined } from '@ant-design/icons-vue';
 
 import { useI18n } from '/@/hooks/web/useI18n';
@@ -97,9 +97,7 @@ export default defineComponent({
     );
 
     const getLoading = computed(() => {
-      return {
-        hidden: !unref(getProps).loading,
-      };
+      return !!unref(getProps)?.loading;
     });
 
     watchEffect(() => {
@@ -230,7 +228,7 @@ export default defineComponent({
             default: () => (
               <>
                 <div ref={scrollRef} style={unref(getScrollContentStyle)}>
-                  <FullLoading absolute tip={t('loadingText')} class={unref(getLoading)} />
+                  <Loading absolute tip={t('loadingText')} loading={unref(getLoading)} />
                   {getSlot(slots)}
                 </div>
                 {renderFooter()}

+ 0 - 55
src/components/Loading/BasicLoading.vue

@@ -1,55 +0,0 @@
-<template>
-  <section class="basic-loading">
-    <img
-      src="/@/assets/images/loading.svg"
-      alt=""
-      :height="getLoadingIconSize"
-      :width="getLoadingIconSize"
-    />
-    <span class="mt-4" v-if="tip"> {{ tip }}</span>
-  </section>
-</template>
-
-<script lang="ts">
-  import type { PropType } from 'vue';
-  // components
-  import { defineComponent, computed } from 'vue';
-
-  import { SizeEnum, sizeMap } from '/@/enums/sizeEnum';
-
-  import { BasicLoadingProps } from './type';
-
-  export default defineComponent({
-    inheritAttrs: false,
-    name: 'BasicLoading',
-    props: {
-      tip: {
-        type: String as PropType<string>,
-        default: '',
-      },
-      size: {
-        type: String as PropType<SizeEnum>,
-        default: SizeEnum.DEFAULT,
-        validator: (v: SizeEnum): boolean => {
-          return [SizeEnum.DEFAULT, SizeEnum.SMALL, SizeEnum.LARGE].includes(v);
-        },
-      },
-    },
-    setup(props: BasicLoadingProps) {
-      const getLoadingIconSize = computed(() => {
-        const { size } = props;
-        return sizeMap.get(size);
-      });
-
-      return { getLoadingIconSize };
-    },
-  });
-</script>
-<style lang="less" scoped>
-  .basic-loading {
-    display: flex;
-    justify-content: center;
-    align-items: center;
-    flex-direction: column;
-  }
-</style>

+ 0 - 50
src/components/Loading/FullLoading.vue

@@ -1,50 +0,0 @@
-<template>
-  <section class="full-loading" :style="getStyle">
-    <BasicLoading :tip="tip" :size="SizeEnum.DEFAULT" />
-  </section>
-</template>
-<script lang="ts">
-  import type { PropType } from 'vue';
-  import { defineComponent, computed } from 'vue';
-  import BasicLoading from './BasicLoading.vue';
-
-  import { SizeEnum } from '/@/enums/sizeEnum';
-
-  export default defineComponent({
-    name: 'FullLoading',
-    components: { BasicLoading },
-    props: {
-      tip: {
-        type: String as PropType<string>,
-        default: '',
-      },
-      absolute: Boolean as PropType<boolean>,
-    },
-    setup(props) {
-      const getStyle = computed((): any => {
-        return props.absolute
-          ? {
-              position: 'absolute',
-              left: 0,
-              top: 0,
-              'z-index': 1,
-            }
-          : {};
-      });
-
-      return { getStyle, SizeEnum };
-    },
-  });
-</script>
-<style lang="less" scoped>
-  .full-loading {
-    display: flex;
-    width: 100%;
-    height: 100%;
-    // background: rgba(255, 255, 255, 0.3);
-    // background: #f0f2f5;
-    background: rgba(240, 242, 245, 0.5);
-    justify-content: center;
-    align-items: center;
-  }
-</style>

+ 8 - 2
src/components/Loading/index.ts

@@ -1,2 +1,8 @@
-export { default as BasicLoading } from './BasicLoading.vue';
-export { default as FullLoading } from './FullLoading.vue';
+import './src/indicator';
+import LoadingLib from './src/index.vue';
+import { withInstall } from '../util';
+
+export { useLoading } from './src/useLoading';
+export { createLoading } from './src/createLoading';
+
+export const Loading = withInstall(LoadingLib);

+ 60 - 0
src/components/Loading/src/createLoading.ts

@@ -0,0 +1,60 @@
+import { VNode, defineComponent } from 'vue';
+import type { LoadingProps } from './types';
+
+import { createVNode, render, reactive, h } from 'vue';
+import Loading from './index.vue';
+
+export function createLoading(props?: Partial<LoadingProps>, target?: HTMLElement) {
+  let vm: Nullable<VNode> = null;
+  const data = reactive({
+    tip: '',
+    loading: true,
+    ...props,
+  });
+
+  const LoadingWrap = defineComponent({
+    setup() {
+      return () => {
+        return h(Loading, { ...data });
+      };
+    },
+  });
+
+  vm = createVNode(LoadingWrap);
+
+  render(vm, document.createElement('div'));
+
+  function close() {
+    if (vm?.el && vm.el.parentNode) {
+      vm.el.parentNode.removeChild(vm.el);
+    }
+  }
+
+  function open(target: HTMLElement = document.body) {
+    if (!vm || !vm.el) {
+      return;
+    }
+    target.appendChild(vm.el as HTMLElement);
+  }
+
+  if (target) {
+    open(target);
+  }
+  return {
+    vm,
+    close,
+    open,
+    setTip: (tip: string) => {
+      data.tip = tip;
+    },
+    setLoading: (loading: boolean) => {
+      data.loading = loading;
+    },
+    get loading() {
+      return data.loading;
+    },
+    get $el() {
+      return vm?.el as HTMLElement;
+    },
+  };
+}

+ 82 - 0
src/components/Loading/src/index.vue

@@ -0,0 +1,82 @@
+<template>
+  <section class="full-loading" :class="{ absolute }" v-show="loading" :style="getStyle">
+    <Spin v-bind="$attrs" :tip="tip" :size="size" :spinning="loading" />
+  </section>
+</template>
+<script lang="ts">
+  import { computed, CSSProperties, PropType } from 'vue';
+
+  import { defineComponent } from 'vue';
+  import { Spin } from 'ant-design-vue';
+
+  import { SizeEnum } from '/@/enums/sizeEnum';
+  import { ThemeEnum } from '/@/enums/appEnum';
+
+  export default defineComponent({
+    name: 'Loading',
+    components: { Spin },
+    props: {
+      tip: {
+        type: String as PropType<string>,
+        default: '',
+      },
+      size: {
+        type: String as PropType<SizeEnum>,
+        default: SizeEnum.LARGE,
+        validator: (v: SizeEnum): boolean => {
+          return [SizeEnum.DEFAULT, SizeEnum.SMALL, SizeEnum.LARGE].includes(v);
+        },
+      },
+      absolute: {
+        type: Boolean as PropType<boolean>,
+        default: false,
+      },
+      loading: {
+        type: Boolean as PropType<boolean>,
+        default: false,
+      },
+      background: {
+        type: String as PropType<string>,
+      },
+      theme: {
+        type: String as PropType<'dark' | 'light'>,
+        default: 'light',
+      },
+    },
+    setup(props) {
+      const getStyle = computed(
+        (): CSSProperties => {
+          const { background, theme } = props;
+          const bgColor = background
+            ? background
+            : theme === ThemeEnum.DARK
+            ? 'rgba(0, 0, 0, 0.2)'
+            : 'rgba(240, 242, 245, 0.4)';
+          return { background: bgColor };
+        }
+      );
+
+      return { getStyle };
+    },
+  });
+</script>
+<style lang="less" scoped>
+  .full-loading {
+    position: fixed;
+    top: 0;
+    left: 0;
+    z-index: 200;
+    display: flex;
+    width: 100%;
+    height: 100%;
+    justify-content: center;
+    align-items: center;
+
+    &.absolute {
+      position: absolute;
+      top: 0;
+      left: 0;
+      z-index: 1;
+    }
+  }
+</style>

+ 9 - 0
src/components/Loading/src/indicator.tsx

@@ -0,0 +1,9 @@
+// If you need to modify the default icon, you can open the comment and modify it here
+
+// import { Spin } from 'ant-design-vue';
+// import { LoadingOutlined } from '@ant-design/icons-vue';
+// Spin.setDefaultIndicator({
+//   indicator: () => {
+//     return <LoadingOutlined spin />;
+//   },
+// });

+ 10 - 0
src/components/Loading/src/types.ts

@@ -0,0 +1,10 @@
+import { SizeEnum } from '/@/enums/sizeEnum';
+
+export interface LoadingProps {
+  tip: string;
+  size: SizeEnum;
+  absolute: boolean;
+  loading: boolean;
+  background: string;
+  theme: 'dark' | 'light';
+}

+ 39 - 0
src/components/Loading/src/useLoading.ts

@@ -0,0 +1,39 @@
+import { unref } from 'vue';
+import { createLoading } from './createLoading';
+import type { LoadingProps } from './types';
+import type { Ref } from 'vue';
+
+export interface UseLoadingOptions {
+  target?: HTMLElement | Ref<ElRef>;
+  props?: Partial<LoadingProps>;
+}
+
+export function useLoading(props: Partial<LoadingProps>): [Fn, Fn];
+export function useLoading(opt: Partial<UseLoadingOptions>): [Fn, Fn];
+
+export function useLoading(opt: Partial<LoadingProps> | Partial<UseLoadingOptions>): [Fn, Fn] {
+  let props: Partial<LoadingProps>;
+  let target: HTMLElement | Ref<ElRef> = document.body;
+
+  if (Reflect.has(opt, 'target') || Reflect.has(opt, 'props')) {
+    const options = opt as Partial<UseLoadingOptions>;
+    props = options.props || {};
+    target = options.target || document.body;
+  } else {
+    props = opt as Partial<LoadingProps>;
+  }
+
+  const instance = createLoading(props);
+
+  const open = (): void => {
+    const t = unref(target);
+    if (!t) return;
+    instance.open(t);
+  };
+
+  const close = (): void => {
+    instance.close();
+  };
+
+  return [open, close];
+}

+ 0 - 8
src/components/Loading/type.ts

@@ -1,8 +0,0 @@
-import { SizeEnum } from '/@/enums/sizeEnum';
-
-export interface BasicLoadingProps {
-  // 提示语
-  tip: string;
-
-  size: SizeEnum;
-}

+ 10 - 7
src/components/Preview/src/index.tsx

@@ -1,16 +1,17 @@
+import './index.less';
+
 import { defineComponent, ref, unref, computed, reactive, watchEffect } from 'vue';
 
 import { basicProps } from './props';
 import { Props } from './types';
-import './index.less';
 
-import { CloseOutlined, LeftOutlined, RightOutlined } from '@ant-design/icons-vue';
+import { CloseOutlined, LeftOutlined, RightOutlined, LoadingOutlined } from '@ant-design/icons-vue';
+import { Spin } from 'ant-design-vue';
 
 import resumeSvg from '/@/assets/svg/preview/resume.svg';
 import rotateSvg from '/@/assets/svg/preview/p-rotate.svg';
 import scaleSvg from '/@/assets/svg/preview/scale.svg';
 import unScaleSvg from '/@/assets/svg/preview/unscale.svg';
-import loadingSvg from '/@/assets/images/loading.svg';
 import unRotateSvg from '/@/assets/svg/preview/unrotate.svg';
 enum StatueEnum {
   LOADING,
@@ -271,12 +272,14 @@ export default defineComponent({
         imgState.show && (
           <div class={prefixCls} ref={wrapElRef} onMouseup={handleMouseUp}>
             <div class={`${prefixCls}-content`}>
-              <img
-                width="32"
-                src={loadingSvg}
+              <Spin
+                indicator={<LoadingOutlined style="font-size: 24px" spin />}
+                spinning={true}
                 class={[
                   `${prefixCls}-image`,
-                  imgState.status === StatueEnum.LOADING ? '' : 'hidden',
+                  {
+                    hidden: imgState.status !== StatueEnum.LOADING,
+                  },
                 ]}
               />
               <img

+ 0 - 5
src/design/public.less

@@ -3,11 +3,6 @@
   height: 100%;
 }
 
-.app-svg-loading {
-  position: relative;
-  width: auto;
-}
-
 // =================================
 // ==============scrollbar==========
 // =================================

+ 1 - 0
src/hooks/web/useI18n.ts

@@ -26,6 +26,7 @@ export function useI18n(namespace?: string) {
   return {
     ...methods,
     t: (key: string, ...arg: Parameters<typeof t>) => {
+      if (!key) return '';
       return t(getKey(key), ...arg);
     },
   };

+ 0 - 4
src/layouts/default/content/index.less

@@ -14,10 +14,6 @@
     position: absolute;
     z-index: @page-loading-z-index;
 
-    &.fill {
-      background: rgba(240, 242, 245) !important;
-    }
-
     > .basic-loading {
       margin-bottom: 15%;
     }

+ 5 - 11
src/layouts/default/content/index.tsx

@@ -1,31 +1,25 @@
 import './index.less';
 
-import { defineComponent, unref, computed } from 'vue';
-import { FullLoading } from '/@/components/Loading/index';
+import { defineComponent, unref } from 'vue';
+import { Loading } from '/@/components/Loading';
 
 import { RouterView } from 'vue-router';
 
 import { useRootSetting } from '/@/hooks/setting/useRootSetting';
 import { useTransitionSetting } from '/@/hooks/setting/useTransitionSetting';
-import { useMultipleTabSetting } from '/@/hooks/setting/useMultipleTabSetting';
 
 export default defineComponent({
   name: 'LayoutContent',
   setup() {
     const { getOpenPageLoading } = useTransitionSetting();
-    const { getShowMultipleTab } = useMultipleTabSetting();
     const { getLayoutContentMode, getPageLoading } = useRootSetting();
 
-    const getLoadingClass = computed(() => {
-      return [
-        `layout-content__loading`,
-        { fill: unref(getShowMultipleTab), hidden: !unref(getPageLoading) },
-      ];
-    });
     return () => {
       return (
         <div class={['layout-content', unref(getLayoutContentMode)]}>
-          {unref(getOpenPageLoading) && <FullLoading class={unref(getLoadingClass)} />}
+          {unref(getOpenPageLoading) && (
+            <Loading loading={unref(getPageLoading)} absolute class="layout-content__loading" />
+          )}
           <RouterView />
         </div>
       );

+ 1 - 0
src/layouts/page/index.tsx

@@ -22,6 +22,7 @@ export default defineComponent({
   name: 'PageLayout',
   setup() {
     const { getShowMenu } = useMenuSetting();
+
     const { getOpenKeepAlive, getCanEmbedIFramePage } = useRootSetting();
 
     const { getBasicTransition, getEnableTransition } = useTransitionSetting();

+ 2 - 0
src/locales/lang/en/routes/demo/comp.ts

@@ -24,4 +24,6 @@ export default {
   qrcode: 'QR code',
   strength: 'Password strength',
   upload: 'Upload',
+
+  loading: 'Loading',
 };

+ 2 - 0
src/locales/lang/zh_CN/routes/demo/comp.ts

@@ -24,4 +24,6 @@ export default {
   qrcode: '二维码组件',
   strength: '密码强度组件',
   upload: '上传组件',
+
+  loading: 'Loading',
 };

+ 4 - 0
src/router/menus/modules/demo/comp.ts

@@ -45,6 +45,10 @@ const menu: MenuModule = {
         name: 'routes.demo.comp.upload',
       },
       {
+        path: 'loading',
+        name: 'routes.demo.comp.loading',
+      },
+      {
         path: 'scroll',
         name: 'routes.demo.comp.scroll',
         children: [

+ 8 - 0
src/router/routes/modules/demo/comp.ts

@@ -178,6 +178,14 @@ const comp: AppRouteModule = {
         title: 'routes.demo.comp.upload',
       },
     },
+    {
+      path: '/loading',
+      name: 'LoadingDemo',
+      component: () => import('/@/views/demo/comp/loading/index.vue'),
+      meta: {
+        title: 'routes.demo.comp.loading',
+      },
+    },
   ],
 };
 

+ 0 - 2
src/setup/ant-design-vue/index.ts

@@ -12,8 +12,6 @@ import {
 } from 'ant-design-vue';
 import 'ant-design-vue/dist/antd.css';
 
-import './spin';
-
 export function setupAntd(app: App<Element>) {
   // need
   // Here are the components required before registering and logging in

+ 0 - 12
src/setup/ant-design-vue/spin.tsx

@@ -1,12 +0,0 @@
-import { Spin } from 'ant-design-vue';
-import svgImg from '/@/assets/images/loading.svg';
-
-Spin.setDefaultIndicator({
-  indicator: () => {
-    return (
-      <div class="app-svg-loading">
-        <img src={svgImg} alt="" height="32" width="32" />
-      </div>
-    );
-  },
-});

+ 2 - 0
src/setup/directives/index.ts

@@ -3,7 +3,9 @@
  */
 import type { App } from 'vue';
 import { setupPermissionDirective } from './permission';
+import { setupLoadingDirective } from './loading';
 
 export function setupGlobDirectives(app: App) {
   setupPermissionDirective(app);
+  setupLoadingDirective(app);
 }

+ 43 - 0
src/setup/directives/loading.ts

@@ -0,0 +1,43 @@
+import { createLoading } from '/@/components/Loading';
+import type { Directive, App } from 'vue';
+
+const loadingDirective: Directive = {
+  mounted(el, binding) {
+    const tip = el.getAttribute('loading-tip');
+    const background = el.getAttribute('loading-background');
+    const theme = el.getAttribute('loading-theme');
+    const size = el.getAttribute('loading-size');
+    const fullscreen = !!binding.modifiers.fullscreen;
+    const instance = createLoading(
+      {
+        tip,
+        background,
+        theme,
+        size: size || 'large',
+        loading: !!binding.value,
+        absolute: !fullscreen,
+      },
+      fullscreen ? document.body : el
+    );
+    el.instance = instance;
+  },
+  updated(el, binding) {
+    const instance = el.instance;
+    if (!instance) return;
+    instance.setTip(el.getAttribute('loading-tip'));
+    if (binding.oldValue !== binding.value) {
+      if (binding.oldValue !== binding.value) {
+        instance.setLoading?.(binding.value && !instance.loading);
+      }
+    }
+  },
+  unmounted(el) {
+    el?.instance?.close();
+  },
+};
+
+export function setupLoadingDirective(app: App) {
+  app.directive('loading', loadingDirective);
+}
+
+export default loadingDirective;

+ 2 - 1
src/setup/i18n/index.ts

@@ -15,7 +15,8 @@ const localeData: I18nOptions = {
   messages: localeMessages,
   availableLocales: availableLocales,
   sync: true, //If you don’t want to inherit locale from global scope, you need to set sync of i18n component option to false.
-  silentTranslationWarn: false, // true - warning off
+  silentTranslationWarn: true, // true - warning off
+  missingWarn: false,
   silentFallbackWarn: true,
 };
 

+ 96 - 0
src/views/demo/comp/loading/index.vue

@@ -0,0 +1,96 @@
+<template>
+  <div class="p-5" ref="wrapEl" v-loading="loadingRef" loading-tip="加载中...">
+    <a-alert message="组件方式" />
+    <a-button class="my-4 mr-4" type="primary" @click="openCompFullLoading">全屏 Loading</a-button>
+    <a-button class="my-4" type="primary" @click="openCompAbsolute">容器内 Loading</a-button>
+    <Loading :loading="loading" :absolute="absolute" :tip="tip" />
+
+    <a-alert message="函数方式" />
+
+    <a-button class="my-4 mr-4" type="primary" @click="openFnFullLoading">全屏 Loading</a-button>
+    <a-button class="my-4" type="primary" @click="openFnWrapLoading">容器内 Loading</a-button>
+
+    <a-alert message="指令方式" />
+    <a-button class="my-4 mr-4" type="primary" @click="openDirectiveLoading">
+      打开指令Loading
+    </a-button>
+  </div>
+</template>
+<script lang="ts">
+  import { defineComponent, reactive, toRefs, ref } from 'vue';
+  import { Loading, useLoading } from '/@/components/Loading';
+  export default defineComponent({
+    components: { Loading },
+    setup() {
+      const wrapEl = ref<ElRef>(null);
+
+      const loadingRef = ref(false);
+      const compState = reactive({
+        absolute: false,
+        loading: false,
+        tip: '加载中...',
+      });
+      const [openFullLoading, closeFullLoading] = useLoading({
+        tip: '加载中...',
+      });
+
+      const [openWrapLoading, closeWrapLoading] = useLoading({
+        target: wrapEl,
+        props: {
+          tip: '加载中...',
+          absolute: true,
+        },
+      });
+
+      function openLoading(absolute: boolean) {
+        compState.absolute = absolute;
+        compState.loading = true;
+        setTimeout(() => {
+          compState.loading = false;
+        }, 2000);
+      }
+
+      function openCompFullLoading() {
+        openLoading(false);
+      }
+
+      function openCompAbsolute() {
+        openLoading(true);
+      }
+
+      function openFnFullLoading() {
+        openFullLoading();
+
+        setTimeout(() => {
+          closeFullLoading();
+        }, 2000);
+      }
+
+      function openFnWrapLoading() {
+        openWrapLoading();
+
+        setTimeout(() => {
+          closeWrapLoading();
+        }, 2000);
+      }
+
+      function openDirectiveLoading() {
+        loadingRef.value = true;
+        setTimeout(() => {
+          loadingRef.value = false;
+        }, 2000);
+      }
+
+      return {
+        openCompFullLoading,
+        openFnFullLoading,
+        openFnWrapLoading,
+        openCompAbsolute,
+        wrapEl,
+        loadingRef,
+        openDirectiveLoading,
+        ...toRefs(compState),
+      };
+    },
+  });
+</script>

+ 1 - 1
tsconfig.json

@@ -1,6 +1,6 @@
 {
   "compilerOptions": {
-    "target": "esnext",
+    "target": "es2016",
     "module": "esnext",
     "moduleResolution": "node",
     "strict": true,

+ 2 - 2
vite.config.ts

@@ -8,7 +8,7 @@ import { createProxy } from './build/vite/proxy';
 import globbyTransform from './build/vite/plugin/transform/globby';
 import dynamicImportTransform from './build/vite/plugin/transform/dynamic-import';
 
-import { isDevFn, loadEnv } from './build/utils';
+import { loadEnv } from './build/utils';
 
 import { createRollupPlugin, createVitePlugins } from './build/vite/plugin';
 
@@ -55,7 +55,7 @@ const viteConfig: UserConfig = {
    * Available options are 'terser' or 'esbuild'.
    * @default 'terser'
    */
-  minify: isDevFn() ? false : 'terser',
+  minify: 'terser',
   /**
    * Base public path when served in production.
    * @default '/'

+ 6 - 5
yarn.lock

@@ -25,13 +25,14 @@
   integrity sha512-Fi03PfuUqRs76aI3UWYpP864lkrfPo0hluwGqh7NJdLhvH4iRDc3jbJqZIvRDLHKbXrvAfPPV3+zjUccfFvWOQ==
 
 "@ant-design/icons-vue@^5.1.6":
-  version "5.1.6"
-  resolved "https://registry.npmjs.org/@ant-design/icons-vue/-/icons-vue-5.1.6.tgz#af15cbf2375d95199e90166adce4c9f6ad1c17f1"
-  integrity sha512-1KY04c/0iDM88ICdu6EW2/ZPOrH+FyL0uvR350XnVqvnDiLijdcrRaLzkZgCdBcy7cy5t5+onXKocymndCOdRA==
+  version "5.1.7"
+  resolved "https://registry.npmjs.org/@ant-design/icons-vue/-/icons-vue-5.1.7.tgz#5f8e26b547c3ecc16c0820152b935e8a44b5fad3"
+  integrity sha512-6UN/FydLs/bHC0WZxgSpdk0Ct8Ejzr7gdOx5sAIULpSprkxoHWiUbzTy8BMmUduImL42YY1L/qtZYJD6pIzmcQ==
   dependencies:
     "@ant-design/colors" "^5.0.0"
     "@ant-design/icons-svg" "^4.0.0"
     "@babel/runtime" "^7.10.4"
+    "@types/lodash" "^4.14.165"
     lodash "^4.17.15"
 
 "@babel/code-frame@^7.0.0", "@babel/code-frame@^7.10.4", "@babel/code-frame@^7.5.5", "@babel/code-frame@^7.8.3":
@@ -1420,7 +1421,7 @@
   dependencies:
     "@types/lodash" "*"
 
-"@types/lodash@*":
+"@types/lodash@*", "@types/lodash@^4.14.165":
   version "4.14.165"
   resolved "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.165.tgz#74d55d947452e2de0742bad65270433b63a8c30f"
   integrity sha512-tjSSOTHhI5mCHTy/OOXYIhi2Wt1qcbHmuXD1Ha7q70CgI/I71afO4XtLb/cVexki1oVYchpul/TOuu3Arcdxrg==
@@ -1959,7 +1960,7 @@ ansi-styles@^4.0.0, ansi-styles@^4.1.0:
   dependencies:
     color-convert "^2.0.1"
 
-ant-design-vue@2.0.0-rc.2:
+ant-design-vue@^2.0.0-rc.2:
   version "2.0.0-rc.2"
   resolved "https://registry.npmjs.org/ant-design-vue/-/ant-design-vue-2.0.0-rc.2.tgz#fd3b4a5a64fccbb53ed488a194317a040de2223e"
   integrity sha512-XA7X/7HHIveiTh41bZWGfoQ2Rys/rqWknK2zzdHwVnfw9ST3v+ciMKH0Uegyn7m14QL/EdUkC8zGsXpiSXqKNQ==