38 changed files with 539 additions and 342 deletions
|
Before Width: | Height: | Size: 1.6 KiB |
|
Before Width: | Height: | Size: 2.0 KiB |
@ -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> |
|
||||
@ -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> |
|
||||
@ -1,2 +1,8 @@ |
|||||
export { default as BasicLoading } from './BasicLoading.vue'; |
import './src/indicator'; |
||||
export { default as FullLoading } from './FullLoading.vue'; |
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); |
||||
|
|||||
@ -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; |
||||
|
}, |
||||
|
}; |
||||
|
} |
||||
@ -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> |
||||
@ -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 />;
|
||||
|
// },
|
||||
|
// });
|
||||
@ -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'; |
||||
|
} |
||||
@ -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]; |
||||
|
} |
||||
@ -1,8 +0,0 @@ |
|||||
import { SizeEnum } from '/@/enums/sizeEnum'; |
|
||||
|
|
||||
export interface BasicLoadingProps { |
|
||||
// 提示语
|
|
||||
tip: string; |
|
||||
|
|
||||
size: SizeEnum; |
|
||||
} |
|
||||
@ -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> |
|
||||
); |
|
||||
}, |
|
||||
}); |
|
||||
@ -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; |
||||
@ -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> |
||||
Loading…
Reference in new issue