This commit is contained in:
2026-05-07 11:29:45 +08:00
parent cf5c178ee4
commit f8607d0b38
10 changed files with 326 additions and 130 deletions
+116
View File
@@ -0,0 +1,116 @@
import { h, render } from "vue";
import type { App, Directive, DirectiveBinding } from "vue";
import LoadingOverlay from "@/components/LoadingOverlay.vue";
type LoadingValue =
| boolean
| string
| {
loading?: boolean;
show?: boolean;
visible?: boolean;
text?: string;
};
type LoadingState = {
container: HTMLDivElement;
props: {
visible: boolean;
text: string;
};
hadRelativePosition: boolean;
};
type LoadingElement = HTMLElement & {
__vLoadingState__?: LoadingState;
};
function normalizeBinding(value: LoadingValue): { loading: boolean; text: string } {
if (typeof value === "string") {
return { loading: true, text: value };
}
if (typeof value === "boolean") {
return { loading: value, text: "" };
}
if (value && typeof value === "object") {
const loading = value.loading ?? value.show ?? value.visible ?? true;
return { loading: Boolean(loading), text: value.text ?? "" };
}
return { loading: false, text: "" };
}
function ensureState(el: LoadingElement): LoadingState {
if (el.__vLoadingState__) {
return el.__vLoadingState__;
}
const computedPosition = window.getComputedStyle(el).position;
const hadRelativePosition = computedPosition !== "static";
if (!hadRelativePosition) {
el.style.position = "relative";
}
const container = document.createElement("div");
el.appendChild(container);
const props = {
visible: false,
text: "",
};
render(h(LoadingOverlay, props), container);
const state: LoadingState = {
container,
props,
hadRelativePosition,
};
el.__vLoadingState__ = state;
return state;
}
function applyState(el: LoadingElement, binding: DirectiveBinding<LoadingValue>): void {
const state = ensureState(el);
const { loading, text } = normalizeBinding(binding.value);
state.props.visible = loading;
state.props.text = text;
render(h(LoadingOverlay, state.props), state.container);
}
const loadingDirective: Directive<HTMLElement, LoadingValue> = {
mounted(el, binding) {
applyState(el as LoadingElement, binding);
},
updated(el, binding) {
applyState(el as LoadingElement, binding);
},
unmounted(el) {
const loadingElement = el as LoadingElement;
const state = loadingElement.__vLoadingState__;
if (!state) {
return;
}
render(null, state.container);
state.container.remove();
if (!state.hadRelativePosition) {
loadingElement.style.position = "";
}
delete loadingElement.__vLoadingState__;
},
};
export const loadingPlugin = {
install(app: App): void {
app.directive("loading", loadingDirective);
},
};