mldong-flow-designer-plus
Version:
本项目包含了作者为B站课堂视频[《工作流设计器开发最佳实践》](https://www.bilibili.com/cheese/play/ss24484)的过程源码。教程中开发的组件也可用于实际生产环境中。以下是和使用文档和课程章节说明。 ## 实战项目 [演示地址](https://flow-pro.mldong.com/)
535 lines (534 loc) • 15.1 kB
JavaScript
import { U as UPDATE_MODEL_EVENT } from "./event-BZTOGHfp.js";
import { a as buildProps, l as isBoolean, g as definePropType, N as NOOP, L as useNamespace, F as throwError, t as isObject, f as debugWarn, k as isArray, q as isFunction, M as useTimeoutFn, m as isClient } from "./error-DEV4o0cD.js";
import { i as iconPropType } from "./index-D0I3i9fl.js";
import { defineComponent, createVNode, renderSlot, h, isRef, computed, watch, onScopeDispose, getCurrentInstance, ref, nextTick, onMounted } from "vue";
import { h as hasClass, a as addClass, g as getStyle, r as removeClass, b as addUnit } from "./style-D2s_cWsv.js";
import { g as getScrollBarWidth } from "./scroll-CYVj_8on.js";
import { u as useId } from "./index-D7GCrely.js";
import { u as useZIndex } from "./index-DGZ8eqxY.js";
import { a as useGlobalConfig } from "./use-global-config-DAkQU-Bn.js";
const dialogContentProps = buildProps({
/**
* @description whether to align the header and footer in center
*/
center: Boolean,
/**
* @description whether to align the dialog both horizontally and vertically
*/
alignCenter: {
type: Boolean,
default: void 0
},
/**
* @description custom close icon, default is Close
*/
closeIcon: { type: iconPropType },
/**
* @description enable dragging feature for Dialog
*/
draggable: {
type: Boolean,
default: void 0
},
/**
* @description draggable Dialog can overflow the viewport
*/
overflow: {
type: Boolean,
default: void 0
},
/**
* @description whether the Dialog takes up full screen
*/
fullscreen: Boolean,
/**
* @description custom class names for header wrapper
*/
headerClass: String,
/**
* @description custom class names for body wrapper
*/
bodyClass: String,
/**
* @description custom class names for footer wrapper
*/
footerClass: String,
/**
* @description whether to show a close button
*/
showClose: {
type: Boolean,
default: true
},
/**
* @description title of Dialog. Can also be passed with a named slot (see the following table)
*/
title: {
type: String,
default: ""
},
/**
* @description header's aria-level attribute
*/
ariaLevel: {
type: String,
default: "2"
}
});
const dialogContentEmits = { close: () => true };
const dialogProps = buildProps({
...dialogContentProps,
/**
* @description whether to append Dialog itself to body. A nested Dialog should have this attribute set to `true`
*/
appendToBody: Boolean,
/**
* @description which element the Dialog appends to
*/
appendTo: {
type: definePropType([String, Object]),
default: "body"
},
/**
* @description callback before Dialog closes, and it will prevent Dialog from closing, use done to close the dialog
*/
beforeClose: { type: definePropType(Function) },
/**
* @description destroy elements in Dialog when closed
*/
destroyOnClose: Boolean,
/**
* @description whether the Dialog can be closed by clicking the mask
*/
closeOnClickModal: {
type: Boolean,
default: true
},
/**
* @description whether the Dialog can be closed by pressing ESC
*/
closeOnPressEscape: {
type: Boolean,
default: true
},
/**
* @description whether scroll of body is disabled while Dialog is displayed
*/
lockScroll: {
type: Boolean,
default: true
},
/**
* @description whether a mask is displayed
*/
modal: {
type: Boolean,
default: true
},
/**
* @description whether the mask is penetrable
*/
modalPenetrable: Boolean,
/**
* @description the Time(milliseconds) before open
*/
openDelay: {
type: Number,
default: 0
},
/**
* @description the Time(milliseconds) before close
*/
closeDelay: {
type: Number,
default: 0
},
/**
* @description value for `margin-top` of Dialog CSS, default is 15vh
*/
top: { type: String },
/**
* @description visibility of Dialog
*/
modelValue: Boolean,
/**
* @description custom class names for mask
*/
modalClass: String,
/**
* @description custom class names for header wrapper
*/
headerClass: String,
/**
* @description custom class names for body wrapper
*/
bodyClass: String,
/**
* @description custom class names for footer wrapper
*/
footerClass: String,
/**
* @description width of Dialog, default is 50%
*/
width: { type: [String, Number] },
/**
* @description same as z-index in native CSS, z-order of dialog
*/
zIndex: { type: Number },
trapFocus: Boolean,
/**
* @description header's aria-level attribute
*/
headerAriaLevel: {
type: String,
default: "2"
},
/**
* @description custom transition configuration for dialog animation, it can be a string (transition name) or an object with Vue transition props
*/
transition: {
type: definePropType([String, Object]),
default: void 0
}
});
const dialogEmits = {
open: () => true,
opened: () => true,
close: () => true,
closed: () => true,
[UPDATE_MODEL_EVENT]: (value) => isBoolean(value),
openAutoFocus: () => true,
closeAutoFocus: () => true
};
const useSameTarget = (handleClick) => {
if (!handleClick) return {
onClick: NOOP,
onMousedown: NOOP,
onMouseup: NOOP
};
let mousedownTarget = false;
let mouseupTarget = false;
const onClick = (e) => {
if (mousedownTarget && mouseupTarget) handleClick(e);
mousedownTarget = mouseupTarget = false;
};
const onMousedown = (e) => {
mousedownTarget = e.target === e.currentTarget;
};
const onMouseup = (e) => {
mouseupTarget = e.target === e.currentTarget;
};
return {
onClick,
onMousedown,
onMouseup
};
};
const overlayProps = buildProps({
mask: {
type: Boolean,
default: true
},
customMaskEvent: Boolean,
overlayClass: { type: definePropType([
String,
Array,
Object
]) },
zIndex: { type: definePropType([String, Number]) }
});
const overlayEmits = { click: (evt) => evt instanceof MouseEvent };
const BLOCK = "overlay";
var overlay_default = defineComponent({
name: "ElOverlay",
props: overlayProps,
emits: overlayEmits,
setup(props, { slots, emit }) {
const ns = useNamespace(BLOCK);
const onMaskClick = (e) => {
emit("click", e);
};
const { onClick, onMousedown, onMouseup } = useSameTarget(props.customMaskEvent ? void 0 : onMaskClick);
return () => {
return props.mask ? createVNode("div", {
class: [ns.b(), props.overlayClass],
style: { zIndex: props.zIndex },
onClick,
onMousedown,
onMouseup
}, [renderSlot(slots, "default")], 14, [
"onClick",
"onMouseup",
"onMousedown"
]) : h("div", {
class: props.overlayClass,
style: {
zIndex: props.zIndex,
position: "fixed",
top: "0px",
right: "0px",
bottom: "0px",
left: "0px"
}
}, [renderSlot(slots, "default")]);
};
}
});
const ElOverlay = overlay_default;
const useLockscreen = (trigger, options = {}) => {
if (!isRef(trigger)) throwError("[useLockscreen]", "You need to pass a ref param to this function");
const ns = options.ns || useNamespace("popup");
const hiddenCls = computed(() => ns.bm("parent", "hidden"));
let scrollBarWidth = 0;
let withoutHiddenClass = false;
let bodyWidth = "0";
let cleaned = false;
const cleanup = () => {
if (cleaned) return;
cleaned = true;
setTimeout(() => {
if (typeof document === "undefined") return;
if (withoutHiddenClass && document) {
document.body.style.width = bodyWidth;
removeClass(document.body, hiddenCls.value);
}
}, 200);
};
watch(trigger, (val) => {
if (!val) {
cleanup();
return;
}
cleaned = false;
withoutHiddenClass = !hasClass(document.body, hiddenCls.value);
if (withoutHiddenClass) {
bodyWidth = document.body.style.width;
addClass(document.body, hiddenCls.value);
}
scrollBarWidth = getScrollBarWidth(ns.namespace.value);
const bodyHasOverflow = document.documentElement.clientHeight < document.body.scrollHeight;
const bodyOverflowY = getStyle(document.body, "overflowY");
if (scrollBarWidth > 0 && (bodyHasOverflow || bodyOverflowY === "scroll") && withoutHiddenClass) document.body.style.width = `calc(100% - ${scrollBarWidth}px)`;
});
onScopeDispose(() => cleanup());
};
const dialogInjectionKey = Symbol("dialogInjectionKey");
const DEFAULT_DIALOG_TRANSITION = "dialog-fade";
const COMPONENT_NAME = "ElDialog";
const useDialog = (props, targetRef) => {
const emit = getCurrentInstance().emit;
const { nextZIndex } = useZIndex();
let lastPosition = "";
const titleId = useId();
const bodyId = useId();
const visible = ref(false);
const closed = ref(false);
const rendered = ref(false);
const zIndex = ref(props.zIndex ?? nextZIndex());
const closing = ref(false);
let openTimer = void 0;
let closeTimer = void 0;
const config = useGlobalConfig();
const namespace = computed(() => {
var _a;
return ((_a = config.value) == null ? void 0 : _a.namespace) ?? "el";
});
const globalConfig = computed(() => {
var _a;
return (_a = config.value) == null ? void 0 : _a.dialog;
});
const style = computed(() => {
const style2 = {};
const varPrefix = `--${namespace.value}-dialog`;
if (!props.fullscreen) {
if (props.top) style2[`${varPrefix}-margin-top`] = props.top;
const width = addUnit(props.width);
if (width) style2[`${varPrefix}-width`] = width;
}
return style2;
});
const _draggable = computed(() => {
var _a;
return (props.draggable ?? ((_a = globalConfig.value) == null ? void 0 : _a.draggable) ?? false) && !props.fullscreen;
});
const _alignCenter = computed(() => {
var _a;
return props.alignCenter ?? ((_a = globalConfig.value) == null ? void 0 : _a.alignCenter) ?? false;
});
const _overflow = computed(() => {
var _a;
return props.overflow ?? ((_a = globalConfig.value) == null ? void 0 : _a.overflow) ?? false;
});
const penetrable = computed(() => props.modalPenetrable && !props.modal && !props.fullscreen);
const overlayDialogStyle = computed(() => {
if (_alignCenter.value) return { display: "flex" };
return {};
});
const transitionConfig = computed(() => {
var _a;
const transition = props.transition ?? ((_a = globalConfig.value) == null ? void 0 : _a.transition) ?? "dialog-fade";
const baseConfig = {
name: transition,
onAfterEnter: afterEnter,
onBeforeLeave: beforeLeave,
onAfterLeave: afterLeave
};
if (isObject(transition)) {
const config2 = { ...transition };
const _mergeHook = (userHook, defaultHook) => {
return (el) => {
if (isArray(userHook)) userHook.forEach((fn) => {
if (isFunction(fn)) fn(el);
});
else if (isFunction(userHook)) userHook(el);
defaultHook();
};
};
config2.onAfterEnter = _mergeHook(config2.onAfterEnter, afterEnter);
config2.onBeforeLeave = _mergeHook(config2.onBeforeLeave, beforeLeave);
config2.onAfterLeave = _mergeHook(config2.onAfterLeave, afterLeave);
if (!config2.name) {
config2.name = DEFAULT_DIALOG_TRANSITION;
debugWarn(COMPONENT_NAME, `transition.name is missing when using object syntax, fallback to '${DEFAULT_DIALOG_TRANSITION}'`);
}
return config2;
}
return baseConfig;
});
function afterEnter() {
emit("opened");
}
function afterLeave() {
emit("closed");
emit(UPDATE_MODEL_EVENT, false);
if (props.destroyOnClose) rendered.value = false;
closing.value = false;
}
function beforeLeave() {
closing.value = true;
emit("close");
}
function open() {
closeTimer == null ? void 0 : closeTimer();
openTimer == null ? void 0 : openTimer();
if (props.openDelay && props.openDelay > 0) ({ stop: openTimer } = useTimeoutFn(() => doOpen(), props.openDelay));
else doOpen();
}
function close() {
openTimer == null ? void 0 : openTimer();
closeTimer == null ? void 0 : closeTimer();
if (props.closeDelay && props.closeDelay > 0) ({ stop: closeTimer } = useTimeoutFn(() => doClose(), props.closeDelay));
else doClose();
}
function handleClose() {
function hide(shouldCancel) {
if (shouldCancel) return;
closed.value = true;
visible.value = false;
}
if (props.beforeClose) props.beforeClose(hide);
else close();
}
function onModalClick() {
if (props.closeOnClickModal) handleClose();
}
function doOpen() {
if (!isClient) return;
visible.value = true;
}
function doClose() {
visible.value = false;
}
function onOpenAutoFocus() {
emit("openAutoFocus");
}
function onCloseAutoFocus() {
emit("closeAutoFocus");
}
function onFocusoutPrevented(event) {
var _a;
if (((_a = event.detail) == null ? void 0 : _a.focusReason) === "pointer") event.preventDefault();
}
if (props.lockScroll) useLockscreen(visible);
function onCloseRequested() {
if (props.closeOnPressEscape) handleClose();
}
function bringToFront() {
if (!visible.value || !penetrable.value || props.zIndex !== void 0) return;
zIndex.value = nextZIndex();
}
watch(() => props.zIndex, () => {
zIndex.value = props.zIndex ?? nextZIndex();
});
watch(() => props.modelValue, (val) => {
if (val) {
closed.value = false;
closing.value = false;
open();
rendered.value = true;
zIndex.value = props.zIndex ?? nextZIndex();
nextTick(() => {
emit("open");
if (targetRef.value) {
targetRef.value.parentElement.scrollTop = 0;
targetRef.value.parentElement.scrollLeft = 0;
targetRef.value.scrollTop = 0;
}
});
} else if (visible.value) close();
});
watch(() => props.fullscreen, (val) => {
if (!targetRef.value) return;
if (val) {
lastPosition = targetRef.value.style.transform;
targetRef.value.style.transform = "";
} else targetRef.value.style.transform = lastPosition;
});
onMounted(() => {
if (props.modelValue) {
visible.value = true;
rendered.value = true;
open();
}
});
return {
afterEnter,
afterLeave,
beforeLeave,
handleClose,
onModalClick,
close,
doClose,
onOpenAutoFocus,
onCloseAutoFocus,
onCloseRequested,
onFocusoutPrevented,
bringToFront,
titleId,
bodyId,
closed,
style,
overlayDialogStyle,
rendered,
visible,
zIndex,
transitionConfig,
_draggable,
_alignCenter,
_overflow,
closing,
penetrable
};
};
export {
DEFAULT_DIALOG_TRANSITION as D,
ElOverlay as E,
dialogContentProps as a,
dialogEmits as b,
dialogInjectionKey as c,
dialogContentEmits as d,
dialogProps as e,
useSameTarget as f,
useDialog as u
};
//# sourceMappingURL=use-dialog-BKz3IFr1.js.map