= withInstall(Loading);
diff --git a/src/loading/loading.en-US.md b/src/loading/loading.en-US.md
index 36bf504e0..2fa0c6391 100644
--- a/src/loading/loading.en-US.md
+++ b/src/loading/loading.en-US.md
@@ -6,10 +6,12 @@
name | type | default | description | required
-- | -- | -- | -- | --
+attach | String / Function | '' | Typescript:`AttachNode`。[see more ts definition](https://github.com/Tencent/tdesign-mobile-vue/blob/develop/src/common.ts) | N
content | String / Slot / Function | - | Typescript:`string \| TNode`。[see more ts definition](https://github.com/Tencent/tdesign-mobile-vue/blob/develop/src/common.ts) | N
default | String / Slot / Function | - | Typescript:`string \| TNode`。[see more ts definition](https://github.com/Tencent/tdesign-mobile-vue/blob/develop/src/common.ts) | N
delay | Number | 0 | \- | N
duration | Number | 800 | \- | N
+fullscreen | Boolean | false | \- | N
indicator | Boolean / Slot / Function | true | Typescript:`boolean \| TNode`。[see more ts definition](https://github.com/Tencent/tdesign-mobile-vue/blob/develop/src/common.ts) | N
inheritColor | Boolean | false | \- | N
layout | String | horizontal | options: horizontal/vertical | N
@@ -20,6 +22,16 @@ size | String | '20px' | \- | N
text | String / Slot / Function | - | Typescript:`string \| TNode`。[see more ts definition](https://github.com/Tencent/tdesign-mobile-vue/blob/develop/src/common.ts) | N
theme | String | circular | options: circular/spinner/dots | N
+### LoadingPlugin
+
+同时也支持 `this.$loading`。
+
+name | params | default | description
+-- | -- | -- | --
+options | Function | - | required。Typescript:`boolean \| TdLoadingProps`
+
+插件返回值:`LoadingInstance【interface LoadingInstance { hide: () => void }】`
+
### CSS Variables
The component provides the following CSS variables, which can be used to customize styles.
diff --git a/src/loading/loading.md b/src/loading/loading.md
index 18420c4e1..ba302c553 100644
--- a/src/loading/loading.md
+++ b/src/loading/loading.md
@@ -6,10 +6,12 @@
名称 | 类型 | 默认值 | 描述 | 必传
-- | -- | -- | -- | --
+attach | String / Function | '' | 挂载元素,默认挂载到组件本身所在的位置。数据类型为 String 时,会被当作选择器处理,进行节点查询。示例:'body' 或 () => document.body。TS 类型:`AttachNode`。[通用类型定义](https://github.com/Tencent/tdesign-mobile-vue/blob/develop/src/common.ts) | N
content | String / Slot / Function | - | 子元素。TS 类型:`string \| TNode`。[通用类型定义](https://github.com/Tencent/tdesign-mobile-vue/blob/develop/src/common.ts) | N
default | String / Slot / Function | - | 子元素,同 content。TS 类型:`string \| TNode`。[通用类型定义](https://github.com/Tencent/tdesign-mobile-vue/blob/develop/src/common.ts) | N
delay | Number | 0 | 延迟显示加载效果的时间,用于防止请求速度过快引起的加载闪烁,单位:毫秒 | N
duration | Number | 800 | 加载动画执行完成一次的时间,单位:毫秒 | N
+fullscreen | Boolean | false | 是否显示为全屏加载 | N
indicator | Boolean / Slot / Function | true | 加载指示符,值为 true 显示默认指示符,值为 false 则不显示,也可以自定义指示符。TS 类型:`boolean \| TNode`。[通用类型定义](https://github.com/Tencent/tdesign-mobile-vue/blob/develop/src/common.ts) | N
inheritColor | Boolean | false | 是否继承父元素颜色 | N
layout | String | horizontal | 对齐方式。可选项:horizontal/vertical | N
@@ -20,6 +22,16 @@ size | String | '20px' | 尺寸,示例:20px | N
text | String / Slot / Function | - | 加载提示文案。TS 类型:`string \| TNode`。[通用类型定义](https://github.com/Tencent/tdesign-mobile-vue/blob/develop/src/common.ts) | N
theme | String | circular | 加载组件类型。可选项:circular/spinner/dots | N
+### LoadingPlugin
+
+同时也支持 `this.$loading`。
+
+参数名称 | 参数类型 | 参数默认值 | 参数描述
+-- | -- | -- | --
+options | Function | - | 必需。TS 类型:`boolean \| TdLoadingProps`
+
+插件返回值:`LoadingInstance【interface LoadingInstance { hide: () => void }】`
+
### CSS Variables
组件提供了下列 CSS 变量,可用于自定义样式。
diff --git a/src/loading/loading.tsx b/src/loading/loading.tsx
index d0f229e9c..f0a6f9472 100644
--- a/src/loading/loading.tsx
+++ b/src/loading/loading.tsx
@@ -1,4 +1,4 @@
-import { defineComponent, computed, ref, watch, h, setBlockTracking } from 'vue';
+import { defineComponent, computed, ref, watch, h, setBlockTracking, Teleport, onMounted } from 'vue';
import TGradientIcon from './icon/gradient';
import SpinnerIcon from './icon/spinner';
@@ -6,6 +6,7 @@ import config from '../config';
import props from './props';
import { useContent, useTNodeJSX } from '../hooks/tnode';
import { usePrefixClass } from '../hooks/useClass';
+import { addClass, getAttach, removeClass } from '@/shared/dom';
const { prefix } = config;
@@ -18,6 +19,7 @@ export default defineComponent({
const loadingClass = usePrefixClass('loading');
const delayShowLoading = ref(false);
+ const teleportElement = ref();
const countDelay = () => {
delayShowLoading.value = false;
@@ -44,6 +46,8 @@ export default defineComponent({
const rootClass = computed(() => [
loadingClass.value,
{ [`${loadingClass.value}--vertical`]: props.layout === 'vertical' },
+ { [`${loadingClass.value}--fullscreen`]: props.fullscreen },
+ { [`${loadingClass.value}--full`]: !props.fullscreen && !!props.attach },
]);
const textClass = computed(() => [
@@ -69,6 +73,20 @@ export default defineComponent({
spinner: SpinnerIcon,
};
+ onMounted(() => {
+ if (props.attach) {
+ const attach = getAttach(props.attach);
+ if (!attach) {
+ console.error('attach is not exist');
+ } else {
+ teleportElement.value = attach;
+ }
+ }
+ if (props.fullscreen) {
+ teleportElement.value = getAttach('body');
+ }
+ });
+
const dotsLoading = computed(() => {
setBlockTracking(-1);
const node = (
@@ -119,12 +137,38 @@ export default defineComponent({
return node;
});
+ watch(
+ () => props.loading,
+ (isLoading) => {
+ if (isLoading && props.fullscreen) {
+ countDelay();
+ addClass(document.body, `${loadingClass.value}--lock`);
+ } else {
+ removeClass(document.body, `${loadingClass.value}--lock`);
+ }
+ },
+ );
+
return () => {
const indicator = renderTNodeJSX('indicator', {
defaultNode: props.theme === 'dots' ? dotsLoading.value : defaultLoading.value,
});
const text = renderTNodeJSX('text');
const TNodeContent = renderTNodeContent('default', 'content');
+
+ if (props.fullscreen || props.attach) {
+ if (!props.loading) return null;
+ return (
+
+
+ {realLoading.value && indicator}
+ {text && realLoading.value && {text}}
+ {TNodeContent}
+
+
+ );
+ }
+
return (
{realLoading.value && indicator}
diff --git a/src/loading/plugin.tsx b/src/loading/plugin.tsx
new file mode 100644
index 000000000..8b7ddd1ae
--- /dev/null
+++ b/src/loading/plugin.tsx
@@ -0,0 +1,99 @@
+import { App, Plugin, createApp, defineComponent, h, reactive } from 'vue';
+import merge from 'lodash/merge';
+import LoadingComponent from './loading';
+import { getAttach, removeClass, addClass } from '../shared/dom';
+import { TdLoadingProps, LoadingInstance, LoadingMethod } from './type';
+import { usePrefixClass } from '../hooks/useClass';
+
+let fullScreenLoadingInstance: LoadingInstance = null;
+
+function mergeDefaultProps(props: TdLoadingProps): TdLoadingProps {
+ const options: TdLoadingProps = merge(
+ {
+ fullscreen: false,
+ attach: 'body',
+ loading: true,
+ },
+ props,
+ );
+
+ return options;
+}
+
+function createLoading(props: TdLoadingProps): LoadingInstance {
+ const mergedProps = mergeDefaultProps(props);
+
+ if (mergedProps.fullscreen && fullScreenLoadingInstance) {
+ return fullScreenLoadingInstance;
+ }
+
+ const component = defineComponent({
+ setup() {
+ const loadingOptions = reactive(mergedProps);
+ return {
+ loadingOptions,
+ };
+ },
+ render() {
+ return h(LoadingComponent, {
+ ...this.loadingOptions,
+ });
+ },
+ });
+
+ const attach = getAttach(mergedProps.fullscreen ? 'body' : mergedProps.attach);
+
+ const app = createApp(component);
+ app.mount(document.createElement('div'));
+ const parentRelativeClass = usePrefixClass('loading__parent--relative').value;
+ const lockClass = usePrefixClass('loading--lock').value;
+
+ if (mergedProps.fullscreen) {
+ addClass(document.body, lockClass);
+ }
+
+ if (attach) {
+ addClass(attach, parentRelativeClass);
+ } else {
+ console.error('attach is not exist');
+ }
+
+ const loadingInstance: LoadingInstance = {
+ hide: () => {
+ removeClass(attach, parentRelativeClass);
+ removeClass(document.body, lockClass);
+ app.unmount();
+ },
+ };
+ return loadingInstance;
+}
+
+function produceLoading(props: boolean | TdLoadingProps): LoadingInstance {
+ // 全屏加载
+ if (props === true) {
+ fullScreenLoadingInstance = createLoading({
+ fullscreen: true,
+ loading: true,
+ attach: 'body',
+ });
+ return fullScreenLoadingInstance;
+ }
+
+ if (props === false) {
+ // 销毁全屏实例
+ fullScreenLoadingInstance?.hide();
+ fullScreenLoadingInstance = null;
+ return;
+ }
+ return createLoading(props);
+}
+
+export type LoadingPluginType = Plugin & LoadingMethod;
+
+export const LoadingPlugin: LoadingPluginType = produceLoading as LoadingPluginType;
+
+LoadingPlugin.install = (app: App) => {
+ app.config.globalProperties.$loading = produceLoading;
+};
+
+export default LoadingPlugin;
diff --git a/src/loading/props.ts b/src/loading/props.ts
index 4e639680e..23b93f1ab 100644
--- a/src/loading/props.ts
+++ b/src/loading/props.ts
@@ -8,6 +8,11 @@ import { TdLoadingProps } from './type';
import { PropType } from 'vue';
export default {
+ /** 挂载元素,默认挂载到组件本身所在的位置。数据类型为 String 时,会被当作选择器处理,进行节点查询。示例:'body' 或 () => document.body */
+ attach: {
+ type: [String, Function] as PropType,
+ default: '',
+ },
/** 子元素 */
content: {
type: [String, Function] as PropType,
@@ -26,6 +31,8 @@ export default {
type: Number,
default: 800,
},
+ /** 是否显示为全屏加载 */
+ fullscreen: Boolean,
/** 加载指示符,值为 true 显示默认指示符,值为 false 则不显示,也可以自定义指示符 */
indicator: {
type: [Boolean, Function] as PropType,
diff --git a/src/loading/type.ts b/src/loading/type.ts
index 9f6a5caa7..2fd3d054f 100644
--- a/src/loading/type.ts
+++ b/src/loading/type.ts
@@ -4,9 +4,14 @@
* 该文件为脚本自动生成文件,请勿随意修改。如需修改请联系 PMC
* */
-import { TNode } from '../common';
+import { TNode, AttachNode } from '../common';
export interface TdLoadingProps {
+ /**
+ * 挂载元素,默认挂载到组件本身所在的位置。数据类型为 String 时,会被当作选择器处理,进行节点查询。示例:'body' 或 () => document.body
+ * @default ''
+ */
+ attach?: AttachNode;
/**
* 子元素
*/
@@ -25,6 +30,11 @@ export interface TdLoadingProps {
* @default 800
*/
duration?: number;
+ /**
+ * 是否显示为全屏加载
+ * @default false
+ */
+ fullscreen?: boolean;
/**
* 加载指示符,值为 true 显示默认指示符,值为 false 则不显示,也可以自定义指示符
* @default true
@@ -69,3 +79,9 @@ export interface TdLoadingProps {
*/
theme?: 'circular' | 'spinner' | 'dots';
}
+
+export interface LoadingInstance {
+ hide: () => void;
+}
+
+export type LoadingMethod = (options: boolean | TdLoadingProps) => LoadingInstance;
diff --git a/src/plugins.ts b/src/plugins.ts
index 7fda2de90..17a3660d7 100644
--- a/src/plugins.ts
+++ b/src/plugins.ts
@@ -2,3 +2,4 @@ export { DialogPlugin } from './dialog';
export { MessagePlugin } from './message';
export { ToastPlugin } from './toast';
export { DrawerPlugin } from './drawer';
+export { LoadingPlugin } from './loading';