committed by
GitHub
13 changed files with 321 additions and 14 deletions
@ -0,0 +1,132 @@ |
|||||
|
import type { App, Directive, DirectiveBinding } from 'vue'; |
||||
|
|
||||
|
import { h, render } from 'vue'; |
||||
|
|
||||
|
import { VbenLoading, VbenSpinner } from '@vben-core/shadcn-ui'; |
||||
|
import { isString } from '@vben-core/shared/utils'; |
||||
|
|
||||
|
const LOADING_INSTANCE_KEY = Symbol('loading'); |
||||
|
const SPINNER_INSTANCE_KEY = Symbol('spinner'); |
||||
|
|
||||
|
const CLASS_NAME_RELATIVE = 'spinner-parent--relative'; |
||||
|
|
||||
|
const loadingDirective: Directive = { |
||||
|
mounted(el, binding) { |
||||
|
const instance = h(VbenLoading, getOptions(binding)); |
||||
|
render(instance, el); |
||||
|
|
||||
|
el.classList.add(CLASS_NAME_RELATIVE); |
||||
|
el[LOADING_INSTANCE_KEY] = instance; |
||||
|
}, |
||||
|
unmounted(el) { |
||||
|
const instance = el[LOADING_INSTANCE_KEY]; |
||||
|
el.classList.remove(CLASS_NAME_RELATIVE); |
||||
|
render(null, el); |
||||
|
instance.el.remove(); |
||||
|
|
||||
|
el[LOADING_INSTANCE_KEY] = null; |
||||
|
}, |
||||
|
|
||||
|
updated(el, binding) { |
||||
|
const instance = el[LOADING_INSTANCE_KEY]; |
||||
|
const options = getOptions(binding); |
||||
|
if (options && instance?.component) { |
||||
|
try { |
||||
|
Object.keys(options).forEach((key) => { |
||||
|
instance.component.props[key] = options[key]; |
||||
|
}); |
||||
|
instance.component.update(); |
||||
|
} catch (error) { |
||||
|
console.error( |
||||
|
'Failed to update loading component in directive:', |
||||
|
error, |
||||
|
); |
||||
|
} |
||||
|
} |
||||
|
}, |
||||
|
}; |
||||
|
|
||||
|
function getOptions(binding: DirectiveBinding) { |
||||
|
if (binding.value === undefined) { |
||||
|
return { spinning: true }; |
||||
|
} else if (typeof binding.value === 'boolean') { |
||||
|
return { spinning: binding.value }; |
||||
|
} else { |
||||
|
return { ...binding.value }; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
const spinningDirective: Directive = { |
||||
|
mounted(el, binding) { |
||||
|
const instance = h(VbenSpinner, getOptions(binding)); |
||||
|
render(instance, el); |
||||
|
|
||||
|
el.classList.add(CLASS_NAME_RELATIVE); |
||||
|
el[SPINNER_INSTANCE_KEY] = instance; |
||||
|
}, |
||||
|
unmounted(el) { |
||||
|
const instance = el[SPINNER_INSTANCE_KEY]; |
||||
|
el.classList.remove(CLASS_NAME_RELATIVE); |
||||
|
render(null, el); |
||||
|
instance.el.remove(); |
||||
|
|
||||
|
el[SPINNER_INSTANCE_KEY] = null; |
||||
|
}, |
||||
|
|
||||
|
updated(el, binding) { |
||||
|
const instance = el[SPINNER_INSTANCE_KEY]; |
||||
|
const options = getOptions(binding); |
||||
|
if (options && instance?.component) { |
||||
|
try { |
||||
|
Object.keys(options).forEach((key) => { |
||||
|
instance.component.props[key] = options[key]; |
||||
|
}); |
||||
|
instance.component.update(); |
||||
|
} catch (error) { |
||||
|
console.error( |
||||
|
'Failed to update spinner component in directive:', |
||||
|
error, |
||||
|
); |
||||
|
} |
||||
|
} |
||||
|
}, |
||||
|
}; |
||||
|
|
||||
|
type loadingDirectiveParams = { |
||||
|
/** 是否注册loading指令。如果提供一个string,则将指令注册为指定的名称 */ |
||||
|
loading?: boolean | string; |
||||
|
/** 是否注册spinning指令。如果提供一个string,则将指令注册为指定的名称 */ |
||||
|
spinning?: boolean | string; |
||||
|
}; |
||||
|
|
||||
|
/** |
||||
|
* 注册loading指令 |
||||
|
* @param app |
||||
|
* @param params |
||||
|
*/ |
||||
|
export function registerLoadingDirective( |
||||
|
app: App, |
||||
|
params?: loadingDirectiveParams, |
||||
|
) { |
||||
|
// 注入一个样式供指令使用,确保容器是相对定位
|
||||
|
const style = document.createElement('style'); |
||||
|
style.id = CLASS_NAME_RELATIVE; |
||||
|
style.innerHTML = ` |
||||
|
.${CLASS_NAME_RELATIVE} { |
||||
|
position: relative !important; |
||||
|
} |
||||
|
`;
|
||||
|
document.head.append(style); |
||||
|
if (params?.loading !== false) { |
||||
|
app.directive( |
||||
|
isString(params?.loading) ? params.loading : 'loading', |
||||
|
loadingDirective, |
||||
|
); |
||||
|
} |
||||
|
if (params?.spinning !== false) { |
||||
|
app.directive( |
||||
|
isString(params?.spinning) ? params.spinning : 'spinning', |
||||
|
spinningDirective, |
||||
|
); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,3 @@ |
|||||
|
export * from './directive'; |
||||
|
export { default as Loading } from './loading.vue'; |
||||
|
export { default as Spinner } from './spinner.vue'; |
||||
@ -0,0 +1,19 @@ |
|||||
|
<script lang="ts" setup> |
||||
|
import { VbenLoading } from '@vben-core/shadcn-ui'; |
||||
|
|
||||
|
defineOptions({ name: 'Loading' }); |
||||
|
defineProps<{ |
||||
|
spinning: boolean; |
||||
|
text?: string; |
||||
|
}>(); |
||||
|
</script> |
||||
|
<template> |
||||
|
<div class="relative min-h-20"> |
||||
|
<slot></slot> |
||||
|
<VbenLoading :spinning="spinning" :text="text"> |
||||
|
<template v-if="$slots.icon" #icon> |
||||
|
<slot name="icon"></slot> |
||||
|
</template> |
||||
|
</VbenLoading> |
||||
|
</div> |
||||
|
</template> |
||||
@ -0,0 +1,14 @@ |
|||||
|
<script lang="ts" setup> |
||||
|
import { VbenSpinner } from '@vben-core/shadcn-ui'; |
||||
|
|
||||
|
defineOptions({ name: 'Spinner' }); |
||||
|
defineProps({ |
||||
|
spinning: Boolean, |
||||
|
}); |
||||
|
</script> |
||||
|
<template> |
||||
|
<div class="relative min-h-20"> |
||||
|
<slot></slot> |
||||
|
<VbenSpinner :spinning="spinning" /> |
||||
|
</div> |
||||
|
</template> |
||||
@ -0,0 +1,101 @@ |
|||||
|
<script lang="ts" setup> |
||||
|
import { Loading, Page, Spinner } from '@vben/common-ui'; |
||||
|
import { IconifyIcon } from '@vben/icons'; |
||||
|
|
||||
|
import { refAutoReset } from '@vueuse/core'; |
||||
|
import { Button, Card, Spin } from 'ant-design-vue'; |
||||
|
|
||||
|
const spinning = refAutoReset(false, 3000); |
||||
|
const loading = refAutoReset(false, 3000); |
||||
|
|
||||
|
const spinningV = refAutoReset(false, 3000); |
||||
|
const loadingV = refAutoReset(false, 3000); |
||||
|
</script> |
||||
|
<template> |
||||
|
<Page |
||||
|
title="Vben Loading" |
||||
|
description="加载中状态组件。这个组件可以为其它作为容器的组件添加一个加载中的遮罩层。使用它们时,容器需要relative定位。" |
||||
|
> |
||||
|
<Card title="Antd Spin"> |
||||
|
<template #actions>这是Antd 组件库自带的Spin组件演示</template> |
||||
|
<Spin :spinning="spinning" tip="加载中..."> |
||||
|
<Button type="primary" @click="spinning = true">显示Spin</Button> |
||||
|
</Spin> |
||||
|
</Card> |
||||
|
|
||||
|
<Card title="Vben Loading" v-loading="loadingV" class="mt-4"> |
||||
|
<template #extra> |
||||
|
<Button type="primary" @click="loadingV = true"> |
||||
|
v-loading 指令 |
||||
|
</Button> |
||||
|
</template> |
||||
|
<template #actions> |
||||
|
Loading组件可以设置文字,并且也提供了icon插槽用于替换加载图标。 |
||||
|
</template> |
||||
|
<div class="flex gap-4"> |
||||
|
<div class="size-40"> |
||||
|
<Loading |
||||
|
:spinning="loading" |
||||
|
text="正在加载..." |
||||
|
class="flex h-full w-full items-center justify-center" |
||||
|
> |
||||
|
<Button type="primary" @click="loading = true">默认动画</Button> |
||||
|
</Loading> |
||||
|
</div> |
||||
|
<div class="size-40"> |
||||
|
<Loading |
||||
|
:spinning="loading" |
||||
|
class="flex h-full w-full items-center justify-center" |
||||
|
> |
||||
|
<Button type="primary" @click="loading = true">自定义动画1</Button> |
||||
|
<template #icon> |
||||
|
<IconifyIcon |
||||
|
icon="svg-spinners:ring-resize" |
||||
|
class="text-primary size-10" |
||||
|
/> |
||||
|
</template> |
||||
|
</Loading> |
||||
|
</div> |
||||
|
<div class="size-40"> |
||||
|
<Loading |
||||
|
:spinning="loading" |
||||
|
class="flex h-full w-full items-center justify-center" |
||||
|
> |
||||
|
<Button type="primary" @click="loading = true">自定义动画2</Button> |
||||
|
<template #icon> |
||||
|
<IconifyIcon |
||||
|
icon="svg-spinners:bars-scale" |
||||
|
class="text-primary size-10" |
||||
|
/> |
||||
|
</template> |
||||
|
</Loading> |
||||
|
</div> |
||||
|
</div> |
||||
|
</Card> |
||||
|
|
||||
|
<Card |
||||
|
title="Vben Spinner" |
||||
|
v-spinning="spinningV" |
||||
|
class="mt-4 overflow-hidden" |
||||
|
:body-style="{ |
||||
|
position: 'relative', |
||||
|
overflow: 'hidden', |
||||
|
}" |
||||
|
> |
||||
|
<template #extra> |
||||
|
<Button type="primary" @click="spinningV = true"> |
||||
|
v-spinning 指令 |
||||
|
</Button> |
||||
|
</template> |
||||
|
<template #actions> |
||||
|
Spinner组件是Loading组件的一个特例,只有一个固定的统一样式。 |
||||
|
</template> |
||||
|
<Spinner |
||||
|
:spinning="spinning" |
||||
|
class="flex size-40 items-center justify-center" |
||||
|
> |
||||
|
<Button type="primary" @click="spinning = true">显示Spinner</Button> |
||||
|
</Spinner> |
||||
|
</Card> |
||||
|
</Page> |
||||
|
</template> |
||||
Loading…
Reference in new issue