import type { UseModalReturnType, ModalMethods, ModalProps, ReturnMethods, UseModalInnerReturnType, } from '../typing'; import { ref, onUnmounted, unref, getCurrentInstance, reactive, watchEffect, nextTick, toRaw, } from 'vue'; import { isFunction } from '/@/utils/is'; import { isEqual } from 'lodash-es'; import { tryOnUnmounted } from '@vueuse/core'; import { error } from '/@/utils/log'; import { computed } from 'vue'; const dataTransfer = reactive({}); const visibleData = reactive<{ [key: number]: boolean }>({}); /** * @description: 适用于 独立modal 和 外部 调用 */ export function useModal(): UseModalReturnType { const modal = ref>(null); const loaded = ref>(false); const uid = ref(''); function register(modalMethod: ModalMethods, uuid: string) { if (!getCurrentInstance()) { throw new Error('useModal() can only be used inside setup() or functional components!'); } uid.value = uuid; onUnmounted(() => { modal.value = null; loaded.value = false; dataTransfer[unref(uid)] = null; }); if (unref(loaded) && modalMethod === unref(modal)) return; modal.value = modalMethod; loaded.value = true; modalMethod.emitVisible = (visible: boolean, uid: number) => { visibleData[uid] = visible; }; } const getInstance = () => { const instance = unref(modal); if (!instance) { error('useModal instance is undefined!'); } return instance; }; const methods: ReturnMethods = { setModalProps: (props: Partial): void => { getInstance()?.setModalProps(props); }, getVisible: computed((): boolean => { return visibleData[~~unref(uid)]; }), redoModalHeight: () => { getInstance()?.redoModalHeight?.(); }, openModal: (visible = true, data?: T, openOnSet = true): void => { getInstance()?.setModalProps({ visible: visible, }); if (!data) return; const id = unref(uid); if (openOnSet) { dataTransfer[id] = null; dataTransfer[id] = toRaw(data); return; } const equal = isEqual(toRaw(dataTransfer[id]), toRaw(data)); if (!equal) { dataTransfer[id] = toRaw(data); } }, closeModal: () => { getInstance()?.setModalProps({ visible: false }); }, }; return [register, methods]; } export const useModalInner = (callbackFn?: Fn): UseModalInnerReturnType => { const modalInstanceRef = ref>(null); const currentInstance = getCurrentInstance(); const uidRef = ref(''); const getInstance = () => { const instance = unref(modalInstanceRef); if (!instance) { error('useModalInner instance is undefined!'); } return instance; }; const register = (modalInstance: ModalMethods, uuid: string) => { tryOnUnmounted(() => { modalInstanceRef.value = null; }); uidRef.value = uuid; modalInstanceRef.value = modalInstance; currentInstance?.emit('register', modalInstance, uuid); }; watchEffect(() => { const data = dataTransfer[unref(uidRef)]; if (!data) return; if (!callbackFn || !isFunction(callbackFn)) return; nextTick(() => { callbackFn(data); }); }); return [ register, { changeLoading: (loading = true) => { getInstance()?.setModalProps({ loading }); }, getVisible: computed((): boolean => { return visibleData[~~unref(uidRef)]; }), changeOkLoading: (loading = true) => { getInstance()?.setModalProps({ confirmLoading: loading }); }, closeModal: () => { getInstance()?.setModalProps({ visible: false }); }, setModalProps: (props: Partial) => { getInstance()?.setModalProps(props); }, redoModalHeight: () => { const callRedo = getInstance()?.redoModalHeight; callRedo && callRedo(); }, }, ]; };