Commit 5db3ce773793d06fa17336aca42470d2aae93fb8
1 parent
c9e44ce9
refactor: add loading component and demo
Showing
37 changed files
with
534 additions
and
337 deletions
CHANGELOG.en_US.md
... | ... | @@ -3,7 +3,7 @@ |
3 | 3 | ## (破坏性更新) Breaking changes |
4 | 4 | |
5 | 5 | - The ClickOutSide component import method is changed from `import ClickOutSide from'/@/components/ClickOutSide/index.vue'` to `import {ClickOutSide} from'/@/components/ClickOutSide'` |
6 | -- Button component import method changed from `import ClickOutSide from'/@/components/Button/index.vue'` to `import {Button} from'/@/components/Button'` | |
6 | +- Button component import method changed from `import Button from'/@/components/Button/index.vue'` to `import {Button} from'/@/components/Button'` | |
7 | 7 | - StrengthMeter component import method is changed from `import StrengthMeter from'/@/components/StrengthMeter'` to `import {StrengthMeter} from'/@/components/StrengthMeter'` |
8 | 8 | - In addition to the examples, the global internationalization function is added, supporting Chinese and English |
9 | 9 | ... | ... |
CHANGELOG.zh_CN.md
1 | +## Wip | |
2 | + | |
3 | +### ✨ Features | |
4 | + | |
5 | +- 还原 antdv 默认 loading,重构 `Loading` 组件,增加`useLoading`和`v-loading`指令。并增加示例 | |
6 | + | |
7 | +### 🎫 Chores | |
8 | + | |
9 | +- 首屏 loading 修改 | |
10 | + | |
1 | 11 | ## 2.0.0-rc.12 (2020-11-30) |
2 | 12 | |
3 | 13 | ## (破坏性更新) Breaking changes |
4 | 14 | |
5 | 15 | - ClickOutSide 组件引入方式由 `import ClickOutSide from '/@/components/ClickOutSide/index.vue'`变更为`import { ClickOutSide } from '/@/components/ClickOutSide'` |
6 | -- Button 组件引入方式由 `import ClickOutSide from '/@/components/Button/index.vue'`变更为`import { Button } from '/@/components/Button'` | |
16 | +- Button 组件引入方式由 `import Button from '/@/components/Button/index.vue'`变更为`import { Button } from '/@/components/Button'` | |
7 | 17 | - StrengthMeter 组件引入方式由 `import StrengthMeter from '/@/components/StrengthMeter'`变更为`import { StrengthMeter } from '/@/components/StrengthMeter'` |
8 | 18 | - 除示例外加入全局国际化功能,支持中文与英文 |
9 | 19 | ... | ... |
build/config/lessModifyVars.ts
index.html
... | ... | @@ -13,68 +13,136 @@ |
13 | 13 | <title></title> |
14 | 14 | <link rel="icon" href="/favicon.ico" /> |
15 | 15 | <%= viteHtmlPluginOptions.injectConfig %> |
16 | + </head> | |
17 | + <body> | |
18 | + <div id="app"> | |
19 | + <style> | |
20 | + .app-loading { | |
21 | + display: flex; | |
22 | + width: 100%; | |
23 | + height: 100%; | |
24 | + justify-content: center; | |
25 | + align-items: center; | |
26 | + flex-direction: column; | |
27 | + } | |
16 | 28 | |
17 | - <style> | |
18 | - @keyframes load { | |
19 | - 0% { | |
20 | - -webkit-transform: rotate(-360deg); | |
21 | - transform: rotate(-360deg); | |
29 | + .app-loading .app-loading-wrap { | |
30 | + position: absolute; | |
31 | + top: 50%; | |
32 | + left: 50%; | |
33 | + display: flex; | |
34 | + -webkit-transform: translate3d(-50%, -50%, 0); | |
35 | + transform: translate3d(-50%, -50%, 0); | |
36 | + justify-content: center; | |
37 | + align-items: center; | |
38 | + flex-direction: column; | |
22 | 39 | } |
23 | 40 | |
24 | - 100% { | |
25 | - -webkit-transform: rotate(0); | |
26 | - transform: rotate(0); | |
41 | + .app-loading .dots { | |
42 | + display: flex; | |
43 | + padding: 98px; | |
44 | + justify-content: center; | |
45 | + align-items: center; | |
27 | 46 | } |
28 | - } | |
29 | 47 | |
30 | - .app-loading { | |
31 | - width: 100%; | |
32 | - height: 100%; | |
33 | - background: #f0f2f5; | |
34 | - } | |
48 | + .app-loading .app-loading-title { | |
49 | + display: flex; | |
50 | + margin-top: 30px; | |
51 | + font-size: 30px; | |
52 | + color: rgba(0, 0, 0, 0.85); | |
53 | + justify-content: center; | |
54 | + align-items: center; | |
55 | + } | |
35 | 56 | |
36 | - .app-loading .app-loading-wrap { | |
37 | - position: absolute; | |
38 | - top: 50%; | |
39 | - left: 50%; | |
40 | - -webkit-transform: translate3d(-50%, -50%, 0); | |
41 | - transform: translate3d(-50%, -50%, 0); | |
42 | - } | |
57 | + .app-loading .app-loading-logo { | |
58 | + display: block; | |
59 | + width: 90px; | |
60 | + margin: 0 auto; | |
61 | + margin-bottom: 20px; | |
62 | + } | |
43 | 63 | |
44 | - .app-loading .g-loading { | |
45 | - display: block; | |
46 | - width: 48px; | |
47 | - margin: 30px auto; | |
48 | - -webkit-animation: load 1.2s linear infinite; | |
49 | - animation: load 1.2s linear infinite; | |
50 | - -webkit-transform-origin: center center; | |
51 | - transform-origin: center center; | |
52 | - } | |
64 | + .dot { | |
65 | + position: relative; | |
66 | + display: inline-block; | |
67 | + width: 48px; | |
68 | + height: 48px; | |
69 | + margin-top: 30px; | |
70 | + font-size: 32px; | |
71 | + transform: rotate(45deg); | |
72 | + box-sizing: border-box; | |
73 | + animation: antRotate 1.2s infinite linear; | |
74 | + } | |
53 | 75 | |
54 | - .app-loading .app-loading-wrap img.logo { | |
55 | - display: block; | |
56 | - width: 90px; | |
57 | - margin: 0 auto; | |
58 | - margin-bottom: 20px; | |
59 | - } | |
76 | + .dot i { | |
77 | + position: absolute; | |
78 | + display: block; | |
79 | + width: 20px; | |
80 | + height: 20px; | |
81 | + background-color: #0065cc; | |
82 | + border-radius: 100%; | |
83 | + opacity: 0.3; | |
84 | + transform: scale(0.75); | |
85 | + animation: antSpinMove 1s infinite linear alternate; | |
86 | + transform-origin: 50% 50%; | |
87 | + } | |
60 | 88 | |
61 | - .app-loading .app-loading-wrap .app-loading__tip { | |
62 | - display: block; | |
63 | - margin: 20px auto 0 0; | |
64 | - font-size: 30px; | |
65 | - color: rgba(0, 0, 0, 0.85); | |
66 | - } | |
67 | - </style> | |
68 | - </head> | |
69 | - <body> | |
70 | - <div id="app"> | |
71 | - <section class="app-loading"> | |
72 | - <section class="app-loading-wrap"> | |
73 | - <img src="./resource/img/logo.png" class="logo" alt="Logo" /> | |
74 | - <img src="./resource/img/loading.svg" alt="" class="g-loading" /> | |
75 | - <h1 class="app-loading__tip"><%= viteHtmlPluginOptions.title %></h1> | |
76 | - </section> | |
77 | - </section> | |
89 | + .dot i:nth-child(1) { | |
90 | + top: 0; | |
91 | + left: 0; | |
92 | + } | |
93 | + | |
94 | + .dot i:nth-child(2) { | |
95 | + top: 0; | |
96 | + right: 0; | |
97 | + -webkit-animation-delay: 0.4s; | |
98 | + animation-delay: 0.4s; | |
99 | + } | |
100 | + | |
101 | + .dot i:nth-child(3) { | |
102 | + right: 0; | |
103 | + bottom: 0; | |
104 | + -webkit-animation-delay: 0.8s; | |
105 | + animation-delay: 0.8s; | |
106 | + } | |
107 | + | |
108 | + .dot i:nth-child(4) { | |
109 | + bottom: 0; | |
110 | + left: 0; | |
111 | + -webkit-animation-delay: 1.2s; | |
112 | + animation-delay: 1.2s; | |
113 | + } | |
114 | + @keyframes antRotate { | |
115 | + to { | |
116 | + -webkit-transform: rotate(405deg); | |
117 | + transform: rotate(405deg); | |
118 | + } | |
119 | + } | |
120 | + @-webkit-keyframes antRotate { | |
121 | + to { | |
122 | + -webkit-transform: rotate(405deg); | |
123 | + transform: rotate(405deg); | |
124 | + } | |
125 | + } | |
126 | + @keyframes antSpinMove { | |
127 | + to { | |
128 | + opacity: 1; | |
129 | + } | |
130 | + } | |
131 | + @-webkit-keyframes antSpinMove { | |
132 | + to { | |
133 | + opacity: 1; | |
134 | + } | |
135 | + } | |
136 | + </style> | |
137 | + <div class="app-loading"> | |
138 | + <div class="app-loading-wrap"> | |
139 | + <img src="./resource/img/logo.png" class="app-loading-logo" alt="Logo" /> | |
140 | + <div class="app-loading-dots"> | |
141 | + <span class="dot dot-spin"><i></i><i></i><i></i><i></i></span> | |
142 | + </div> | |
143 | + <div class="app-loading-title"><%= viteHtmlPluginOptions.title %></div> | |
144 | + </div> | |
145 | + </div> | |
78 | 146 | </div> |
79 | 147 | <script type="module" src="/src/main.ts"></script> |
80 | 148 | </body> | ... | ... |
package.json
public/resource/img/loading.svg deleted
100644 → 0
1 | -<?xml version="1.0" encoding="UTF-8" standalone="no"?> | |
2 | -<svg viewBox="0 0 200 200" version="1.1" | |
3 | - xmlns="http://www.w3.org/2000/svg" | |
4 | - xmlns:xlink="http://www.w3.org/1999/xlink"> | |
5 | - <style type="text/css"> | |
6 | - .left-linear { | |
7 | - fill: url(#left-linear); | |
8 | - } | |
9 | - | |
10 | - .right-linear { | |
11 | - fill: url(#right-linear); | |
12 | - } | |
13 | - | |
14 | - .top { | |
15 | - fill: #64acff; | |
16 | - } | |
17 | - | |
18 | - .bottom { | |
19 | - fill: #9dbfe4; | |
20 | - } | |
21 | - svg { | |
22 | - display: block; | |
23 | - } | |
24 | - | |
25 | - .tip { | |
26 | - display: block; | |
27 | - min-width: 100px; | |
28 | - margin-top: 4px; | |
29 | - font-size: 13px; | |
30 | - color: rgba(0, 0, 0, 0.85);; | |
31 | - text-align: left; | |
32 | - } | |
33 | - </style> | |
34 | - <circle cx="97" cy="97" r="81" stroke-width="16" stroke="#327fd8" fill="none"></circle> | |
35 | - <g class="load"> | |
36 | - <linearGradient id="left-linear" gradientUnits="userSpaceOnUse" x1="50" y1="0" x2="100" y2="180"> | |
37 | - <stop offset="0" style="stop-color: #64acff;" /> | |
38 | - <stop offset="1" style="stop-color: #9DBFE4;" /> | |
39 | - </linearGradient> | |
40 | - <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" /> | |
41 | - <circle class="bottom" cx="100" cy="190" r="10" /> | |
42 | - <linearGradient id="right-linear" gradientUnits="userSpaceOnUse" x1="100" y1="120" x2="100" y2="180"> | |
43 | - <stop offset="0" style="stop-color: transparent;" /> | |
44 | - <stop offset="1" style="stop-color: transparent;" /> | |
45 | - </linearGradient> | |
46 | - <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" /> | |
47 | - <circle class="top" cx="100" cy="10" r="10" /> | |
48 | - </g> | |
49 | -</svg> |
src/assets/images/loading.svg deleted
100644 → 0
1 | -<?xml version="1.0" encoding="UTF-8" standalone="no"?> | |
2 | -<svg viewBox="0 0 200 200" version="1.1" | |
3 | - xmlns="http://www.w3.org/2000/svg" | |
4 | - xmlns:xlink="http://www.w3.org/1999/xlink"> | |
5 | - <style type="text/css"> | |
6 | - .left-linear { | |
7 | - fill: url(#left-linear); | |
8 | - } | |
9 | - | |
10 | - .right-linear { | |
11 | - fill: url(#right-linear); | |
12 | - } | |
13 | - | |
14 | - .top { | |
15 | - fill: #64acff; | |
16 | - } | |
17 | - | |
18 | - .bottom { | |
19 | - fill: #9dbfe4; | |
20 | - } | |
21 | - @keyframes load { | |
22 | - 0% { | |
23 | - transform: rotate(-360deg); | |
24 | - } | |
25 | - | |
26 | - 100% { | |
27 | - transform: rotate(0); | |
28 | - } | |
29 | - } | |
30 | - | |
31 | - .load { | |
32 | - animation: load 1.4s linear infinite; | |
33 | - transform-origin: center center; | |
34 | - } | |
35 | - | |
36 | - svg { | |
37 | - display: block; | |
38 | - } | |
39 | - | |
40 | - .tip { | |
41 | - display: block; | |
42 | - min-width: 100px; | |
43 | - margin-top: 4px; | |
44 | - font-size: 13px; | |
45 | - color: #303133; | |
46 | - text-align: left; | |
47 | - } | |
48 | - </style> | |
49 | - <circle cx="97" cy="97" r="81" stroke-width="16" stroke="#327fd8" fill="none"></circle> | |
50 | - <g class="load"> | |
51 | - <!--右半圆环--> | |
52 | - <linearGradient id="left-linear" gradientUnits="userSpaceOnUse" x1="50" y1="0" x2="100" y2="180"> | |
53 | - <stop offset="0" style="stop-color: #64acff;" /> | |
54 | - <stop offset="1" style="stop-color: #9DBFE4;" /> | |
55 | - </linearGradient> | |
56 | - <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" /> | |
57 | - <!--左半圆环--> | |
58 | - <circle class="bottom" cx="100" cy="190" r="10" /> | |
59 | - <linearGradient id="right-linear" gradientUnits="userSpaceOnUse" x1="100" y1="120" x2="100" y2="180"> | |
60 | - <stop offset="0" style="stop-color: transparent;" /> | |
61 | - <stop offset="1" style="stop-color: transparent;" /> | |
62 | - </linearGradient> | |
63 | - <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" /> | |
64 | - <!--左半圆环--> | |
65 | - <circle class="top" cx="100" cy="10" r="10" /> | |
66 | - </g> | |
67 | -</svg> |
src/components/Application/src/AppLocalePicker.vue
src/components/Application/src/AppLogo.vue
src/components/Drawer/src/BasicDrawer.tsx
... | ... | @@ -7,7 +7,7 @@ import { defineComponent, ref, computed, watchEffect, watch, unref, nextTick, to |
7 | 7 | import { Drawer, Row, Col, Button } from 'ant-design-vue'; |
8 | 8 | |
9 | 9 | import { BasicTitle } from '/@/components/Basic'; |
10 | -import { FullLoading } from '/@/components/Loading/index'; | |
10 | +import { Loading } from '/@/components/Loading'; | |
11 | 11 | import { LeftOutlined } from '@ant-design/icons-vue'; |
12 | 12 | |
13 | 13 | import { useI18n } from '/@/hooks/web/useI18n'; |
... | ... | @@ -97,9 +97,7 @@ export default defineComponent({ |
97 | 97 | ); |
98 | 98 | |
99 | 99 | const getLoading = computed(() => { |
100 | - return { | |
101 | - hidden: !unref(getProps).loading, | |
102 | - }; | |
100 | + return !!unref(getProps)?.loading; | |
103 | 101 | }); |
104 | 102 | |
105 | 103 | watchEffect(() => { |
... | ... | @@ -230,7 +228,7 @@ export default defineComponent({ |
230 | 228 | default: () => ( |
231 | 229 | <> |
232 | 230 | <div ref={scrollRef} style={unref(getScrollContentStyle)}> |
233 | - <FullLoading absolute tip={t('loadingText')} class={unref(getLoading)} /> | |
231 | + <Loading absolute tip={t('loadingText')} loading={unref(getLoading)} /> | |
234 | 232 | {getSlot(slots)} |
235 | 233 | </div> |
236 | 234 | {renderFooter()} | ... | ... |
src/components/Loading/BasicLoading.vue deleted
100644 → 0
1 | -<template> | |
2 | - <section class="basic-loading"> | |
3 | - <img | |
4 | - src="/@/assets/images/loading.svg" | |
5 | - alt="" | |
6 | - :height="getLoadingIconSize" | |
7 | - :width="getLoadingIconSize" | |
8 | - /> | |
9 | - <span class="mt-4" v-if="tip"> {{ tip }}</span> | |
10 | - </section> | |
11 | -</template> | |
12 | - | |
13 | -<script lang="ts"> | |
14 | - import type { PropType } from 'vue'; | |
15 | - // components | |
16 | - import { defineComponent, computed } from 'vue'; | |
17 | - | |
18 | - import { SizeEnum, sizeMap } from '/@/enums/sizeEnum'; | |
19 | - | |
20 | - import { BasicLoadingProps } from './type'; | |
21 | - | |
22 | - export default defineComponent({ | |
23 | - inheritAttrs: false, | |
24 | - name: 'BasicLoading', | |
25 | - props: { | |
26 | - tip: { | |
27 | - type: String as PropType<string>, | |
28 | - default: '', | |
29 | - }, | |
30 | - size: { | |
31 | - type: String as PropType<SizeEnum>, | |
32 | - default: SizeEnum.DEFAULT, | |
33 | - validator: (v: SizeEnum): boolean => { | |
34 | - return [SizeEnum.DEFAULT, SizeEnum.SMALL, SizeEnum.LARGE].includes(v); | |
35 | - }, | |
36 | - }, | |
37 | - }, | |
38 | - setup(props: BasicLoadingProps) { | |
39 | - const getLoadingIconSize = computed(() => { | |
40 | - const { size } = props; | |
41 | - return sizeMap.get(size); | |
42 | - }); | |
43 | - | |
44 | - return { getLoadingIconSize }; | |
45 | - }, | |
46 | - }); | |
47 | -</script> | |
48 | -<style lang="less" scoped> | |
49 | - .basic-loading { | |
50 | - display: flex; | |
51 | - justify-content: center; | |
52 | - align-items: center; | |
53 | - flex-direction: column; | |
54 | - } | |
55 | -</style> |
src/components/Loading/FullLoading.vue deleted
100644 → 0
1 | -<template> | |
2 | - <section class="full-loading" :style="getStyle"> | |
3 | - <BasicLoading :tip="tip" :size="SizeEnum.DEFAULT" /> | |
4 | - </section> | |
5 | -</template> | |
6 | -<script lang="ts"> | |
7 | - import type { PropType } from 'vue'; | |
8 | - import { defineComponent, computed } from 'vue'; | |
9 | - import BasicLoading from './BasicLoading.vue'; | |
10 | - | |
11 | - import { SizeEnum } from '/@/enums/sizeEnum'; | |
12 | - | |
13 | - export default defineComponent({ | |
14 | - name: 'FullLoading', | |
15 | - components: { BasicLoading }, | |
16 | - props: { | |
17 | - tip: { | |
18 | - type: String as PropType<string>, | |
19 | - default: '', | |
20 | - }, | |
21 | - absolute: Boolean as PropType<boolean>, | |
22 | - }, | |
23 | - setup(props) { | |
24 | - const getStyle = computed((): any => { | |
25 | - return props.absolute | |
26 | - ? { | |
27 | - position: 'absolute', | |
28 | - left: 0, | |
29 | - top: 0, | |
30 | - 'z-index': 1, | |
31 | - } | |
32 | - : {}; | |
33 | - }); | |
34 | - | |
35 | - return { getStyle, SizeEnum }; | |
36 | - }, | |
37 | - }); | |
38 | -</script> | |
39 | -<style lang="less" scoped> | |
40 | - .full-loading { | |
41 | - display: flex; | |
42 | - width: 100%; | |
43 | - height: 100%; | |
44 | - // background: rgba(255, 255, 255, 0.3); | |
45 | - // background: #f0f2f5; | |
46 | - background: rgba(240, 242, 245, 0.5); | |
47 | - justify-content: center; | |
48 | - align-items: center; | |
49 | - } | |
50 | -</style> |
src/components/Loading/index.ts
1 | -export { default as BasicLoading } from './BasicLoading.vue'; | |
2 | -export { default as FullLoading } from './FullLoading.vue'; | |
1 | +import './src/indicator'; | |
2 | +import LoadingLib from './src/index.vue'; | |
3 | +import { withInstall } from '../util'; | |
4 | + | |
5 | +export { useLoading } from './src/useLoading'; | |
6 | +export { createLoading } from './src/createLoading'; | |
7 | + | |
8 | +export const Loading = withInstall(LoadingLib); | ... | ... |
src/components/Loading/src/createLoading.ts
0 → 100644
1 | +import { VNode, defineComponent } from 'vue'; | |
2 | +import type { LoadingProps } from './types'; | |
3 | + | |
4 | +import { createVNode, render, reactive, h } from 'vue'; | |
5 | +import Loading from './index.vue'; | |
6 | + | |
7 | +export function createLoading(props?: Partial<LoadingProps>, target?: HTMLElement) { | |
8 | + let vm: Nullable<VNode> = null; | |
9 | + const data = reactive({ | |
10 | + tip: '', | |
11 | + loading: true, | |
12 | + ...props, | |
13 | + }); | |
14 | + | |
15 | + const LoadingWrap = defineComponent({ | |
16 | + setup() { | |
17 | + return () => { | |
18 | + return h(Loading, { ...data }); | |
19 | + }; | |
20 | + }, | |
21 | + }); | |
22 | + | |
23 | + vm = createVNode(LoadingWrap); | |
24 | + | |
25 | + render(vm, document.createElement('div')); | |
26 | + | |
27 | + function close() { | |
28 | + if (vm?.el && vm.el.parentNode) { | |
29 | + vm.el.parentNode.removeChild(vm.el); | |
30 | + } | |
31 | + } | |
32 | + | |
33 | + function open(target: HTMLElement = document.body) { | |
34 | + if (!vm || !vm.el) { | |
35 | + return; | |
36 | + } | |
37 | + target.appendChild(vm.el as HTMLElement); | |
38 | + } | |
39 | + | |
40 | + if (target) { | |
41 | + open(target); | |
42 | + } | |
43 | + return { | |
44 | + vm, | |
45 | + close, | |
46 | + open, | |
47 | + setTip: (tip: string) => { | |
48 | + data.tip = tip; | |
49 | + }, | |
50 | + setLoading: (loading: boolean) => { | |
51 | + data.loading = loading; | |
52 | + }, | |
53 | + get loading() { | |
54 | + return data.loading; | |
55 | + }, | |
56 | + get $el() { | |
57 | + return vm?.el as HTMLElement; | |
58 | + }, | |
59 | + }; | |
60 | +} | ... | ... |
src/components/Loading/src/index.vue
0 → 100644
1 | +<template> | |
2 | + <section class="full-loading" :class="{ absolute }" v-show="loading" :style="getStyle"> | |
3 | + <Spin v-bind="$attrs" :tip="tip" :size="size" :spinning="loading" /> | |
4 | + </section> | |
5 | +</template> | |
6 | +<script lang="ts"> | |
7 | + import { computed, CSSProperties, PropType } from 'vue'; | |
8 | + | |
9 | + import { defineComponent } from 'vue'; | |
10 | + import { Spin } from 'ant-design-vue'; | |
11 | + | |
12 | + import { SizeEnum } from '/@/enums/sizeEnum'; | |
13 | + import { ThemeEnum } from '/@/enums/appEnum'; | |
14 | + | |
15 | + export default defineComponent({ | |
16 | + name: 'Loading', | |
17 | + components: { Spin }, | |
18 | + props: { | |
19 | + tip: { | |
20 | + type: String as PropType<string>, | |
21 | + default: '', | |
22 | + }, | |
23 | + size: { | |
24 | + type: String as PropType<SizeEnum>, | |
25 | + default: SizeEnum.LARGE, | |
26 | + validator: (v: SizeEnum): boolean => { | |
27 | + return [SizeEnum.DEFAULT, SizeEnum.SMALL, SizeEnum.LARGE].includes(v); | |
28 | + }, | |
29 | + }, | |
30 | + absolute: { | |
31 | + type: Boolean as PropType<boolean>, | |
32 | + default: false, | |
33 | + }, | |
34 | + loading: { | |
35 | + type: Boolean as PropType<boolean>, | |
36 | + default: false, | |
37 | + }, | |
38 | + background: { | |
39 | + type: String as PropType<string>, | |
40 | + }, | |
41 | + theme: { | |
42 | + type: String as PropType<'dark' | 'light'>, | |
43 | + default: 'light', | |
44 | + }, | |
45 | + }, | |
46 | + setup(props) { | |
47 | + const getStyle = computed( | |
48 | + (): CSSProperties => { | |
49 | + const { background, theme } = props; | |
50 | + const bgColor = background | |
51 | + ? background | |
52 | + : theme === ThemeEnum.DARK | |
53 | + ? 'rgba(0, 0, 0, 0.2)' | |
54 | + : 'rgba(240, 242, 245, 0.4)'; | |
55 | + return { background: bgColor }; | |
56 | + } | |
57 | + ); | |
58 | + | |
59 | + return { getStyle }; | |
60 | + }, | |
61 | + }); | |
62 | +</script> | |
63 | +<style lang="less" scoped> | |
64 | + .full-loading { | |
65 | + position: fixed; | |
66 | + top: 0; | |
67 | + left: 0; | |
68 | + z-index: 200; | |
69 | + display: flex; | |
70 | + width: 100%; | |
71 | + height: 100%; | |
72 | + justify-content: center; | |
73 | + align-items: center; | |
74 | + | |
75 | + &.absolute { | |
76 | + position: absolute; | |
77 | + top: 0; | |
78 | + left: 0; | |
79 | + z-index: 1; | |
80 | + } | |
81 | + } | |
82 | +</style> | ... | ... |
src/components/Loading/src/indicator.tsx
0 → 100644
1 | +// If you need to modify the default icon, you can open the comment and modify it here | |
2 | + | |
3 | +// import { Spin } from 'ant-design-vue'; | |
4 | +// import { LoadingOutlined } from '@ant-design/icons-vue'; | |
5 | +// Spin.setDefaultIndicator({ | |
6 | +// indicator: () => { | |
7 | +// return <LoadingOutlined spin />; | |
8 | +// }, | |
9 | +// }); | ... | ... |
src/components/Loading/type.ts renamed to src/components/Loading/src/types.ts
src/components/Loading/src/useLoading.ts
0 → 100644
1 | +import { unref } from 'vue'; | |
2 | +import { createLoading } from './createLoading'; | |
3 | +import type { LoadingProps } from './types'; | |
4 | +import type { Ref } from 'vue'; | |
5 | + | |
6 | +export interface UseLoadingOptions { | |
7 | + target?: HTMLElement | Ref<ElRef>; | |
8 | + props?: Partial<LoadingProps>; | |
9 | +} | |
10 | + | |
11 | +export function useLoading(props: Partial<LoadingProps>): [Fn, Fn]; | |
12 | +export function useLoading(opt: Partial<UseLoadingOptions>): [Fn, Fn]; | |
13 | + | |
14 | +export function useLoading(opt: Partial<LoadingProps> | Partial<UseLoadingOptions>): [Fn, Fn] { | |
15 | + let props: Partial<LoadingProps>; | |
16 | + let target: HTMLElement | Ref<ElRef> = document.body; | |
17 | + | |
18 | + if (Reflect.has(opt, 'target') || Reflect.has(opt, 'props')) { | |
19 | + const options = opt as Partial<UseLoadingOptions>; | |
20 | + props = options.props || {}; | |
21 | + target = options.target || document.body; | |
22 | + } else { | |
23 | + props = opt as Partial<LoadingProps>; | |
24 | + } | |
25 | + | |
26 | + const instance = createLoading(props); | |
27 | + | |
28 | + const open = (): void => { | |
29 | + const t = unref(target); | |
30 | + if (!t) return; | |
31 | + instance.open(t); | |
32 | + }; | |
33 | + | |
34 | + const close = (): void => { | |
35 | + instance.close(); | |
36 | + }; | |
37 | + | |
38 | + return [open, close]; | |
39 | +} | ... | ... |
src/components/Preview/src/index.tsx
1 | +import './index.less'; | |
2 | + | |
1 | 3 | import { defineComponent, ref, unref, computed, reactive, watchEffect } from 'vue'; |
2 | 4 | |
3 | 5 | import { basicProps } from './props'; |
4 | 6 | import { Props } from './types'; |
5 | -import './index.less'; | |
6 | 7 | |
7 | -import { CloseOutlined, LeftOutlined, RightOutlined } from '@ant-design/icons-vue'; | |
8 | +import { CloseOutlined, LeftOutlined, RightOutlined, LoadingOutlined } from '@ant-design/icons-vue'; | |
9 | +import { Spin } from 'ant-design-vue'; | |
8 | 10 | |
9 | 11 | import resumeSvg from '/@/assets/svg/preview/resume.svg'; |
10 | 12 | import rotateSvg from '/@/assets/svg/preview/p-rotate.svg'; |
11 | 13 | import scaleSvg from '/@/assets/svg/preview/scale.svg'; |
12 | 14 | import unScaleSvg from '/@/assets/svg/preview/unscale.svg'; |
13 | -import loadingSvg from '/@/assets/images/loading.svg'; | |
14 | 15 | import unRotateSvg from '/@/assets/svg/preview/unrotate.svg'; |
15 | 16 | enum StatueEnum { |
16 | 17 | LOADING, |
... | ... | @@ -271,12 +272,14 @@ export default defineComponent({ |
271 | 272 | imgState.show && ( |
272 | 273 | <div class={prefixCls} ref={wrapElRef} onMouseup={handleMouseUp}> |
273 | 274 | <div class={`${prefixCls}-content`}> |
274 | - <img | |
275 | - width="32" | |
276 | - src={loadingSvg} | |
275 | + <Spin | |
276 | + indicator={<LoadingOutlined style="font-size: 24px" spin />} | |
277 | + spinning={true} | |
277 | 278 | class={[ |
278 | 279 | `${prefixCls}-image`, |
279 | - imgState.status === StatueEnum.LOADING ? '' : 'hidden', | |
280 | + { | |
281 | + hidden: imgState.status !== StatueEnum.LOADING, | |
282 | + }, | |
280 | 283 | ]} |
281 | 284 | /> |
282 | 285 | <img | ... | ... |
src/design/public.less
src/hooks/web/useI18n.ts
src/layouts/default/content/index.less
src/layouts/default/content/index.tsx
1 | 1 | import './index.less'; |
2 | 2 | |
3 | -import { defineComponent, unref, computed } from 'vue'; | |
4 | -import { FullLoading } from '/@/components/Loading/index'; | |
3 | +import { defineComponent, unref } from 'vue'; | |
4 | +import { Loading } from '/@/components/Loading'; | |
5 | 5 | |
6 | 6 | import { RouterView } from 'vue-router'; |
7 | 7 | |
8 | 8 | import { useRootSetting } from '/@/hooks/setting/useRootSetting'; |
9 | 9 | import { useTransitionSetting } from '/@/hooks/setting/useTransitionSetting'; |
10 | -import { useMultipleTabSetting } from '/@/hooks/setting/useMultipleTabSetting'; | |
11 | 10 | |
12 | 11 | export default defineComponent({ |
13 | 12 | name: 'LayoutContent', |
14 | 13 | setup() { |
15 | 14 | const { getOpenPageLoading } = useTransitionSetting(); |
16 | - const { getShowMultipleTab } = useMultipleTabSetting(); | |
17 | 15 | const { getLayoutContentMode, getPageLoading } = useRootSetting(); |
18 | 16 | |
19 | - const getLoadingClass = computed(() => { | |
20 | - return [ | |
21 | - `layout-content__loading`, | |
22 | - { fill: unref(getShowMultipleTab), hidden: !unref(getPageLoading) }, | |
23 | - ]; | |
24 | - }); | |
25 | 17 | return () => { |
26 | 18 | return ( |
27 | 19 | <div class={['layout-content', unref(getLayoutContentMode)]}> |
28 | - {unref(getOpenPageLoading) && <FullLoading class={unref(getLoadingClass)} />} | |
20 | + {unref(getOpenPageLoading) && ( | |
21 | + <Loading loading={unref(getPageLoading)} absolute class="layout-content__loading" /> | |
22 | + )} | |
29 | 23 | <RouterView /> |
30 | 24 | </div> |
31 | 25 | ); | ... | ... |
src/layouts/page/index.tsx
... | ... | @@ -22,6 +22,7 @@ export default defineComponent({ |
22 | 22 | name: 'PageLayout', |
23 | 23 | setup() { |
24 | 24 | const { getShowMenu } = useMenuSetting(); |
25 | + | |
25 | 26 | const { getOpenKeepAlive, getCanEmbedIFramePage } = useRootSetting(); |
26 | 27 | |
27 | 28 | const { getBasicTransition, getEnableTransition } = useTransitionSetting(); | ... | ... |
src/locales/lang/en/routes/demo/comp.ts
src/locales/lang/zh_CN/routes/demo/comp.ts
src/router/menus/modules/demo/comp.ts
src/router/routes/modules/demo/comp.ts
... | ... | @@ -178,6 +178,14 @@ const comp: AppRouteModule = { |
178 | 178 | title: 'routes.demo.comp.upload', |
179 | 179 | }, |
180 | 180 | }, |
181 | + { | |
182 | + path: '/loading', | |
183 | + name: 'LoadingDemo', | |
184 | + component: () => import('/@/views/demo/comp/loading/index.vue'), | |
185 | + meta: { | |
186 | + title: 'routes.demo.comp.loading', | |
187 | + }, | |
188 | + }, | |
181 | 189 | ], |
182 | 190 | }; |
183 | 191 | ... | ... |
src/setup/ant-design-vue/index.ts
src/setup/ant-design-vue/spin.tsx deleted
100644 → 0
src/setup/directives/index.ts
... | ... | @@ -3,7 +3,9 @@ |
3 | 3 | */ |
4 | 4 | import type { App } from 'vue'; |
5 | 5 | import { setupPermissionDirective } from './permission'; |
6 | +import { setupLoadingDirective } from './loading'; | |
6 | 7 | |
7 | 8 | export function setupGlobDirectives(app: App) { |
8 | 9 | setupPermissionDirective(app); |
10 | + setupLoadingDirective(app); | |
9 | 11 | } | ... | ... |
src/setup/directives/loading.ts
0 → 100644
1 | +import { createLoading } from '/@/components/Loading'; | |
2 | +import type { Directive, App } from 'vue'; | |
3 | + | |
4 | +const loadingDirective: Directive = { | |
5 | + mounted(el, binding) { | |
6 | + const tip = el.getAttribute('loading-tip'); | |
7 | + const background = el.getAttribute('loading-background'); | |
8 | + const theme = el.getAttribute('loading-theme'); | |
9 | + const size = el.getAttribute('loading-size'); | |
10 | + const fullscreen = !!binding.modifiers.fullscreen; | |
11 | + const instance = createLoading( | |
12 | + { | |
13 | + tip, | |
14 | + background, | |
15 | + theme, | |
16 | + size: size || 'large', | |
17 | + loading: !!binding.value, | |
18 | + absolute: !fullscreen, | |
19 | + }, | |
20 | + fullscreen ? document.body : el | |
21 | + ); | |
22 | + el.instance = instance; | |
23 | + }, | |
24 | + updated(el, binding) { | |
25 | + const instance = el.instance; | |
26 | + if (!instance) return; | |
27 | + instance.setTip(el.getAttribute('loading-tip')); | |
28 | + if (binding.oldValue !== binding.value) { | |
29 | + if (binding.oldValue !== binding.value) { | |
30 | + instance.setLoading?.(binding.value && !instance.loading); | |
31 | + } | |
32 | + } | |
33 | + }, | |
34 | + unmounted(el) { | |
35 | + el?.instance?.close(); | |
36 | + }, | |
37 | +}; | |
38 | + | |
39 | +export function setupLoadingDirective(app: App) { | |
40 | + app.directive('loading', loadingDirective); | |
41 | +} | |
42 | + | |
43 | +export default loadingDirective; | ... | ... |
src/setup/i18n/index.ts
... | ... | @@ -15,7 +15,8 @@ const localeData: I18nOptions = { |
15 | 15 | messages: localeMessages, |
16 | 16 | availableLocales: availableLocales, |
17 | 17 | sync: true, //If you don’t want to inherit locale from global scope, you need to set sync of i18n component option to false. |
18 | - silentTranslationWarn: false, // true - warning off | |
18 | + silentTranslationWarn: true, // true - warning off | |
19 | + missingWarn: false, | |
19 | 20 | silentFallbackWarn: true, |
20 | 21 | }; |
21 | 22 | ... | ... |
src/views/demo/comp/loading/index.vue
0 → 100644
1 | +<template> | |
2 | + <div class="p-5" ref="wrapEl" v-loading="loadingRef" loading-tip="加载中..."> | |
3 | + <a-alert message="组件方式" /> | |
4 | + <a-button class="my-4 mr-4" type="primary" @click="openCompFullLoading">全屏 Loading</a-button> | |
5 | + <a-button class="my-4" type="primary" @click="openCompAbsolute">容器内 Loading</a-button> | |
6 | + <Loading :loading="loading" :absolute="absolute" :tip="tip" /> | |
7 | + | |
8 | + <a-alert message="函数方式" /> | |
9 | + | |
10 | + <a-button class="my-4 mr-4" type="primary" @click="openFnFullLoading">全屏 Loading</a-button> | |
11 | + <a-button class="my-4" type="primary" @click="openFnWrapLoading">容器内 Loading</a-button> | |
12 | + | |
13 | + <a-alert message="指令方式" /> | |
14 | + <a-button class="my-4 mr-4" type="primary" @click="openDirectiveLoading"> | |
15 | + 打开指令Loading | |
16 | + </a-button> | |
17 | + </div> | |
18 | +</template> | |
19 | +<script lang="ts"> | |
20 | + import { defineComponent, reactive, toRefs, ref } from 'vue'; | |
21 | + import { Loading, useLoading } from '/@/components/Loading'; | |
22 | + export default defineComponent({ | |
23 | + components: { Loading }, | |
24 | + setup() { | |
25 | + const wrapEl = ref<ElRef>(null); | |
26 | + | |
27 | + const loadingRef = ref(false); | |
28 | + const compState = reactive({ | |
29 | + absolute: false, | |
30 | + loading: false, | |
31 | + tip: '加载中...', | |
32 | + }); | |
33 | + const [openFullLoading, closeFullLoading] = useLoading({ | |
34 | + tip: '加载中...', | |
35 | + }); | |
36 | + | |
37 | + const [openWrapLoading, closeWrapLoading] = useLoading({ | |
38 | + target: wrapEl, | |
39 | + props: { | |
40 | + tip: '加载中...', | |
41 | + absolute: true, | |
42 | + }, | |
43 | + }); | |
44 | + | |
45 | + function openLoading(absolute: boolean) { | |
46 | + compState.absolute = absolute; | |
47 | + compState.loading = true; | |
48 | + setTimeout(() => { | |
49 | + compState.loading = false; | |
50 | + }, 2000); | |
51 | + } | |
52 | + | |
53 | + function openCompFullLoading() { | |
54 | + openLoading(false); | |
55 | + } | |
56 | + | |
57 | + function openCompAbsolute() { | |
58 | + openLoading(true); | |
59 | + } | |
60 | + | |
61 | + function openFnFullLoading() { | |
62 | + openFullLoading(); | |
63 | + | |
64 | + setTimeout(() => { | |
65 | + closeFullLoading(); | |
66 | + }, 2000); | |
67 | + } | |
68 | + | |
69 | + function openFnWrapLoading() { | |
70 | + openWrapLoading(); | |
71 | + | |
72 | + setTimeout(() => { | |
73 | + closeWrapLoading(); | |
74 | + }, 2000); | |
75 | + } | |
76 | + | |
77 | + function openDirectiveLoading() { | |
78 | + loadingRef.value = true; | |
79 | + setTimeout(() => { | |
80 | + loadingRef.value = false; | |
81 | + }, 2000); | |
82 | + } | |
83 | + | |
84 | + return { | |
85 | + openCompFullLoading, | |
86 | + openFnFullLoading, | |
87 | + openFnWrapLoading, | |
88 | + openCompAbsolute, | |
89 | + wrapEl, | |
90 | + loadingRef, | |
91 | + openDirectiveLoading, | |
92 | + ...toRefs(compState), | |
93 | + }; | |
94 | + }, | |
95 | + }); | |
96 | +</script> | ... | ... |
tsconfig.json
vite.config.ts
... | ... | @@ -8,7 +8,7 @@ import { createProxy } from './build/vite/proxy'; |
8 | 8 | import globbyTransform from './build/vite/plugin/transform/globby'; |
9 | 9 | import dynamicImportTransform from './build/vite/plugin/transform/dynamic-import'; |
10 | 10 | |
11 | -import { isDevFn, loadEnv } from './build/utils'; | |
11 | +import { loadEnv } from './build/utils'; | |
12 | 12 | |
13 | 13 | import { createRollupPlugin, createVitePlugins } from './build/vite/plugin'; |
14 | 14 | |
... | ... | @@ -55,7 +55,7 @@ const viteConfig: UserConfig = { |
55 | 55 | * Available options are 'terser' or 'esbuild'. |
56 | 56 | * @default 'terser' |
57 | 57 | */ |
58 | - minify: isDevFn() ? false : 'terser', | |
58 | + minify: 'terser', | |
59 | 59 | /** |
60 | 60 | * Base public path when served in production. |
61 | 61 | * @default '/' | ... | ... |
yarn.lock
... | ... | @@ -25,13 +25,14 @@ |
25 | 25 | integrity sha512-Fi03PfuUqRs76aI3UWYpP864lkrfPo0hluwGqh7NJdLhvH4iRDc3jbJqZIvRDLHKbXrvAfPPV3+zjUccfFvWOQ== |
26 | 26 | |
27 | 27 | "@ant-design/icons-vue@^5.1.6": |
28 | - version "5.1.6" | |
29 | - resolved "https://registry.npmjs.org/@ant-design/icons-vue/-/icons-vue-5.1.6.tgz#af15cbf2375d95199e90166adce4c9f6ad1c17f1" | |
30 | - integrity sha512-1KY04c/0iDM88ICdu6EW2/ZPOrH+FyL0uvR350XnVqvnDiLijdcrRaLzkZgCdBcy7cy5t5+onXKocymndCOdRA== | |
28 | + version "5.1.7" | |
29 | + resolved "https://registry.npmjs.org/@ant-design/icons-vue/-/icons-vue-5.1.7.tgz#5f8e26b547c3ecc16c0820152b935e8a44b5fad3" | |
30 | + integrity sha512-6UN/FydLs/bHC0WZxgSpdk0Ct8Ejzr7gdOx5sAIULpSprkxoHWiUbzTy8BMmUduImL42YY1L/qtZYJD6pIzmcQ== | |
31 | 31 | dependencies: |
32 | 32 | "@ant-design/colors" "^5.0.0" |
33 | 33 | "@ant-design/icons-svg" "^4.0.0" |
34 | 34 | "@babel/runtime" "^7.10.4" |
35 | + "@types/lodash" "^4.14.165" | |
35 | 36 | lodash "^4.17.15" |
36 | 37 | |
37 | 38 | "@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 @@ |
1420 | 1421 | dependencies: |
1421 | 1422 | "@types/lodash" "*" |
1422 | 1423 | |
1423 | -"@types/lodash@*": | |
1424 | +"@types/lodash@*", "@types/lodash@^4.14.165": | |
1424 | 1425 | version "4.14.165" |
1425 | 1426 | resolved "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.165.tgz#74d55d947452e2de0742bad65270433b63a8c30f" |
1426 | 1427 | integrity sha512-tjSSOTHhI5mCHTy/OOXYIhi2Wt1qcbHmuXD1Ha7q70CgI/I71afO4XtLb/cVexki1oVYchpul/TOuu3Arcdxrg== |
... | ... | @@ -1959,7 +1960,7 @@ ansi-styles@^4.0.0, ansi-styles@^4.1.0: |
1959 | 1960 | dependencies: |
1960 | 1961 | color-convert "^2.0.1" |
1961 | 1962 | |
1962 | -ant-design-vue@2.0.0-rc.2: | |
1963 | +ant-design-vue@^2.0.0-rc.2: | |
1963 | 1964 | version "2.0.0-rc.2" |
1964 | 1965 | resolved "https://registry.npmjs.org/ant-design-vue/-/ant-design-vue-2.0.0-rc.2.tgz#fd3b4a5a64fccbb53ed488a194317a040de2223e" |
1965 | 1966 | integrity sha512-XA7X/7HHIveiTh41bZWGfoQ2Rys/rqWknK2zzdHwVnfw9ST3v+ciMKH0Uegyn7m14QL/EdUkC8zGsXpiSXqKNQ== | ... | ... |