refactor: add loading component and demo

This commit is contained in:
vben
2020-12-01 20:59:17 +08:00
parent c9e44ce9af
commit 5db3ce7737
38 changed files with 545 additions and 348 deletions

View File

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

View File

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

View File

@@ -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()}

View File

@@ -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>

View File

@@ -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>

View File

@@ -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);

View File

@@ -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;
},
};
}

View File

@@ -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>

View File

@@ -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 />;
// },
// });

View File

@@ -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';
}

View File

@@ -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];
}

View File

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

View File

@@ -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