calc-oa-lib
Version:
calc oa library
502 lines (445 loc) • 14.6 kB
JSX
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
}