UNPKG

calc-oa-lib

Version:

calc oa library

502 lines (445 loc) 14.6 kB
import PropTypes from "prop-types"; import React from "react"; import $calc from "../../utils/common"; import "./index.module.scss"; import { Button, Dialog, } from "zent"; // 按需引入 // import Button from 'zent/lib/button'; // import Dialog from 'zent/lib/dialog'; export default class CalcPopUpDialog extends React.Component { //#region 属性 & 构造函数 static propTypes = { lv: PropTypes.number, customClass: PropTypes.string, // 弹框标题 title: PropTypes.string, // 是否显示弹框 isShow: PropTypes.bool.isRequired, // 是否显示右上角关闭按钮 isShowClose: PropTypes.bool, // 是否显示确认按钮 isShowConfirm: PropTypes.bool, // 是否显示确认按钮的loading isShowConfirmLoading: PropTypes.bool, // 是否显示取消按钮 isShowCancel: PropTypes.bool, // 是否显示遮罩层 isShowMask: PropTypes.bool, // 确认按钮文本 cofirmText: PropTypes.string, // 取消按钮文本 cancelText: PropTypes.string, // 弹框底部水平轴对齐方式 footerJustifyContent: PropTypes.string, // 弹框宽度 width: PropTypes.oneOfType([ PropTypes.string, PropTypes.number, ]), // 弹框高度 height: PropTypes.oneOfType([ PropTypes.string, PropTypes.number, ]), // 弹框最大高度 maxHeight: PropTypes.oneOfType([ PropTypes.string, PropTypes.number, ]), // 弹框外边距 contentMargin: PropTypes.string, // 弹框是否可拖拽 canDrag: PropTypes.bool, // children: PropTypes.element.isRequired, updateIsShow: PropTypes.func, onClose: PropTypes.func, onOpen: PropTypes.func, onConfirm: PropTypes.func, onCencel: PropTypes.func, onMove: PropTypes.func, }; static defaultProps = { lv: 7, customClass: "custom-calc-pop-up-dialog", title: "", isShow: false, isShowClose: true, isShowConfirm: true, isShowConfirmLoading: false, isShowCancel: true, isShowMask: true, cofirmText: "Confirm", cancelText: "Cancel", footerJustifyContent: "flex-end", width: 720, height: 400, maxHeight: "", contentMargin: "0px", canDrag: true, updateIsShow: (isShow) => { console.log(`updateIsShow`, isShow); }, onClose: () => { console.log(`onClose`); }, onOpen: () => { console.log(`onOpen`); }, onConfirm: () => { console.log(`onConfirm`); }, onCencel: () => { console.log(`onCencel`); }, onMove: () => { console.log(`onMove`); }, }; constructor(props) { super(props); this.state = {}; } data = { // 分配一个唯一id, 保证组件间的拖拽互不影响 dialogId: `dialogId_${$calc.tool.createMD5($calc.tool.generateGUID())}`, }; dialogRef = React.createRef(); //#endregion //#region hooks render() { const _self = this; const { lv, customClass, title, isShow, isShowClose, isShowConfirm, isShowConfirmLoading, isShowCancel, isShowMask, cofirmText, cancelText, footerJustifyContent, width, height, maxHeight, contentMargin, margin, canDrag, children, updateIsShow, onClose, onOpen, onConfirm, onCencel, onMove, } = _self.props; const slotObj = $calc.tool.getNamedSlots(children); return ( <Dialog ref={_self.dialogRef} title={title} visible={isShow} closeBtn={isShowClose} mask={isShowMask} maskClosable={false} style={_self.getStyleObj()} className={`${customClass} ${_self.data.dialogId} calc-pop-up-dialog`} onClose={() => { _self.onCloseEvent && _self.onCloseEvent(); }} > <div className="dialog-body-wrap"> <div className="dialog-content-wrap">{slotObj.default}</div> </div> {slotObj.footer ? slotObj.footer : (isShowCancel || isShowConfirm) && ( <div className="dialog-footer-wrap"> {isShowCancel && ( <div className="btn btn-cancel" onClick={() => { _self.onCencelEvent(); }} > {cancelText} </div> )} {isShowConfirm && ( <Button type="primary" className="btn btn-confirm" loading={isShowConfirmLoading} onClick={() => { _self.onConfirmEvent(); }} > {isShowConfirmLoading ? "" : cofirmText} </Button> )} </div> )} </Dialog> ); } componentDidMount() { // const _self = this; // console.log( // `calc-pop-up-dialog componentWillUnmount...`, // _self.dialogRef.current // ); } shouldComponentUpdate(nextProps, nextState) { const _self = this; // 若子组件无属性变化及状态更新, 则无需更新组件 // Immutable验证的效果不好, 用回JSON.stringify来处理, 排除掉循环引用的属性, 如插槽 const removeProps = ["children"]; const nextPropsClone = $calc.tool.deepClone(nextProps, removeProps); const curPropsClone = $calc.tool.deepClone(_self.props, removeProps); const nextStateClone = $calc.tool.deepClone(nextState, removeProps); const curnextStateClone = $calc.tool.deepClone(_self.state, removeProps); const needRender = JSON.stringify(nextPropsClone) != JSON.stringify(curPropsClone) || JSON.stringify(nextStateClone) != JSON.stringify(curnextStateClone); // console.log("calc-pop-up-dialog shouldComponentUpdate...", needRender); if (needRender) { const { width, height, maxHeight } = _self.props; const { width: nextWidth, height: nextHeight, maxHeight: nextMaxHeight, } = nextProps; if ( width != nextWidth || height != nextHeight || maxHeight != nextMaxHeight ) { // 2>计算弹框body高度, 并设置content样式 _self.calcDialogBodyHeightAndSetContentStyle(); } return true; } return false; // return true; } componentDidUpdate(prevProps, prevState) { const _self = this; // 显示弹框, 重置弹框位置 if (prevProps.isShow != _self.props.isShow && _self.props.isShow == true) { _self.openEvent(); } } componentWillUnmount() { console.log(`calc-pop-up-dialog componentWillUnmount...`); } //#endregion //#region methods // 获取样式对象 getStyleObj() { const _self = this; let styleObj = {}; const { width, height, maxHeight } = _self.props; // 设置padding styleObj["padding"] = "0px"; // 设置width if (!$calc.tool.isNullOrEmpty(width)) { styleObj["width"] = $calc.tool.isNum(width) ? `${width}px` : width; } // 优先使用height if (!$calc.tool.isNullOrEmpty(height)) { styleObj["height"] = $calc.tool.isNum(height) ? `${height}px` : height; } else if (!$calc.tool.isNullOrEmpty(maxHeight)) { styleObj["max-height"] = $calc.tool.isNum(maxHeight) ? `${maxHeight}px` : maxHeight; } // console.log(`styleObj`, styleObj); return styleObj; } // 弹框打开事件 // 因为zent弹框的隐藏弹框的实质是销毁弹框, 因此可以在此做一些弹框的初始化动作, 且不需要设置标志位来判断是否首次执行 openEvent() { const _self = this; const { canDrag } = _self.props; // 1>绑定拖动功能 canDrag && _self.bindDragFunc(); // 2>计算弹框body高度, 并设置content样式 _self.calcDialogBodyHeightAndSetContentStyle(); // 3>执行显示弹框的后置动作 _self.props.onOpen && _self.props.onOpen(); } debounceMove = $calc.tool._.debounce(this.moveEvent, 2); moveEvent() { const _self = this; _self.props.onMove && _self.props.onMove(); } callMoveEvent() { const _self = this; _self.debounceMove && _self.debounceMove(); } // 计算弹框body高度, 并设置content样式 calcDialogBodyHeightAndSetContentStyle() { const _self = this; const { contentMargin } = _self.props; const { dialogId = "" } = _self.data || {}; // console.log( // `calcDialogBodyHeightAndSetContentStyle - dialogId`, // _self.data.dialogId // ); if ($calc.tool.isNullOrEmpty(dialogId)) { console.error(`dialogId is null!`); return false; } setTimeout(() => { // 弹框DOM const dialogDOM = document.querySelector(`.${dialogId}`); // console.log(`calcDialogBodyHeightAndSetContentStyle`); if (!dialogDOM) { console.error(`dialogDOM (${dialogId}) is null!`); return false; } const dialogHeight = dialogDOM.clientHeight || 0; // 获取头部DOM const headerDOM = document.querySelector( `.${dialogId} .zent-dialog-r-header` ); // 获取底部DOM const foorerDOM = document.querySelector( `.${dialogId} .dialog-footer-wrap` ); // 获取弹框body DOM const bodyDOM = document.querySelector(`.${dialogId} .dialog-body-wrap`); if (!bodyDOM) { console.error(`bodyDOM is null!`); return false; } // 获取弹框content DOM const contentDOM = document.querySelector( `.${dialogId} .dialog-content-wrap` ); if (!contentDOM) { console.error(`contentDOM is null!`); return false; } // 1>设置content margin contentDOM.style.margin = contentMargin; // 2>计算content高度 let headerHeight = 0; if (headerDOM) { headerHeight = headerDOM.clientHeight || 0; } // console.log(`headerHeight:${headerHeight}`); let footerHeight = 0; if (foorerDOM) { footerHeight = foorerDOM.clientHeight || 0; } // console.log(`footerHeight:${footerHeight}`); const contentHeight = dialogHeight - headerHeight - footerHeight; // console.log(`contentHeight:${contentHeight}`); bodyDOM.style.height = `${contentHeight}px`; }, 0); } // 获取属性对象 getStyle = (() => { if (document.currentStyle) { return (dom, attr) => dom.currentStyle[attr]; } else { return (dom, attr) => getComputedStyle(dom, false)[attr]; } })(); // 绑定拖动功能 bindDragFunc() { const _self = this; const { dialogId = "" } = _self.data || {}; // console.log(`dialogId`, _self.data.dialogId); if ($calc.tool.isNullOrEmpty(dialogId)) { console.error(`dialogId is null!`); return false; } setTimeout(() => { const dialogHeaderEl = document.querySelector( `.${dialogId} .zent-dialog-r-header` ); const dragDom = document.querySelector(`.${dialogId}`); dialogHeaderEl.style.cursor = "move"; dragDom.style.top = "0px"; dialogHeaderEl.onmousedown = (e) => { // 鼠标按下,计算当前元素距离可视区的距离 const disX = e.clientX - dialogHeaderEl.offsetLeft; const disY = e.clientY - dialogHeaderEl.offsetTop; const dragDomWidth = dragDom.offsetWidth; const dragDomHeight = dragDom.offsetHeight; const screenWidth = document.body.clientWidth; const screenHeight = document.body.clientHeight; const minDragDomLeft = dragDom.offsetLeft; const maxDragDomLeft = screenWidth - dragDom.offsetLeft - dragDomWidth; const minDragDomTop = dragDom.offsetTop; const maxDragDomTop = screenHeight - dragDom.offsetTop - dragDomHeight; // 获取到的值带px 正则匹配替换 let styL = _self.getStyle(dragDom, "left"); let styT = _self.getStyle(dragDom, "top"); if (styL.includes("%")) { styL = +document.body.clientWidth * (+styL.replace(/\%/g, "") / 100); styT = +document.body.clientHeight * (+styT.replace(/\%/g, "") / 100); } else { styL = +styL.replace(/\px/g, ""); styT = +styT.replace(/\px/g, ""); } document.onmousemove = (e) => { // 通过事件委托,计算移动的距离 let left = e.clientX - disX; let top = e.clientY - disY; // 边界处理 if (-left > minDragDomLeft) { left = -minDragDomLeft; } else if (left > maxDragDomLeft) { left = maxDragDomLeft; } if (-top > minDragDomTop) { top = -minDragDomTop; } else if (top > maxDragDomTop) { top = maxDragDomTop; } // 移动当前元素 dragDom.style.left = `${left + styL}px`; dragDom.style.top = `${top + styT}px`; // 触发弹框移动事件 _self.callMoveEvent && _self.callMoveEvent(); }; document.onmouseup = (e) => { document.onmousemove = null; document.onmouseup = null; }; }; }, 0); } // 弹框关闭事件 onCloseEvent() { // console.log(`onCloseEvent`); const _self = this; const { onClose } = _self.props; onClose && onClose(); _self.toggleDialog(false); } // 显示/隐藏弹框 toggleDialog(isShow = true) { const _self = this; const { updateIsShow } = _self.props; updateIsShow && updateIsShow(isShow); } // 取消按钮点击事件 onCencelEvent() { const _self = this; const { onCencel } = _self.props; onCencel && onCencel(); _self.onCloseEvent(); } // 确认按钮点击事件 onConfirmEvent() { const _self = this; _self.props.onConfirm && _self.props.onConfirm(); } //#endregion }