在平常的需求開發中咱們常常須要用到彈窗,那麼在咱們構建彈窗時,在引用組件是都須要引入組件DOM,而後經過事件來控制組件的影藏顯示,調用也不是很方便;css
本組件使用事前註冊DOM的方式,業務方在使用時刻直接調用暴露的方法便可,無需再引用DOM。react
import newDialog from "*./components/newDialog"
newDialog.open({ title: "退出發佈", content: '是否須要保存草稿箱?', btn: [{ text: "不保存" },{ text: "保存" }], callback: (event)=>{ if(event.result == 2){ //TODO }else { //TODO } } });
參數 | 說明 | 類型 | 是否必填 | |
---|---|---|---|---|
title | 彈窗標題 | String | √ | |
content | 彈窗展現內容 | String\ | ReactDom | √ |
btn | 彈窗按鈕可配置一個或者兩個 | Array | √ | |
callback | 彈窗響應回調方法 | Function | √ |
彈窗按鈕說明json
//彈窗按鈕是json數組配置的 最多配置兩個 btn: [{ text: "不保存" },{ text: "保存" }], //每一個按鈕只須要配置text按鈕文案便可 樣式和顏色都集成到css //若是配置一個按鈕就是底部長按鈕 配置兩個根據配置的數組順序從左向右展現
回調參數數組
//點擊按鈕會返回回調數據並關閉彈出 { result: 1/2 //返回按鈕的索引值 }
js代碼newDialog.tsxapp
'use strict'; import './newDialog.scss'; import * as React from 'react'; import cs from "classnames"; import * as ReactDOM from 'react-dom'; import { isIphoneX } from './../../util/util'; /** * @description 解決移動端滾動穿透事件 **/ const ModalHelper = (function (bodyCls) { var scrollTop; return { afterOpen: function () { scrollTop = document.scrollingElement.scrollTop; document.body.classList.add(bodyCls); document.body.style.top = -scrollTop + 'px'; }, beforeClose: function () { document.body.classList.remove(bodyCls); // scrollTop lost after set position:fixed, restore it back. document.scrollingElement.scrollTop = scrollTop; } }; })('modal-open'); interface IAppProps { title?: string; content?: any; btn?: any; callback?: Function; } interface IAppState { showToast?: Boolean; title?: string; content?: any; btn?: any; callback?: Function; isIPhoneX?: Boolean; } let DialogDiv; class Dialog extends React.Component<IAppProps, IAppState> { constructor(props: IAppProps) { super(props); this.state = { showToast: true, isIPhoneX: false }; } componentWillMount() { let isIPhoneX = isIphoneX(); this.setState({ isIPhoneX }); } static open = (options) => { const { ...props } = options || {}; DialogDiv = document.createElement('div'); setTimeout(()=>{ document.body.appendChild(DialogDiv); ReactDOM.render(<Dialog {...props}/> ,DialogDiv); },100); ModalHelper.afterOpen(); } close = (param) => { let result = param +1; this.props.callback({ result: result }); this.setState({ showToast: false }); document.body.removeChild(DialogDiv); ModalHelper.beforeClose(); } render() { const { showToast, isIPhoneX } = this.state; const { title, content, btn } = this.props; let _twoBtn = btn.length > 1 ? true : false; return ( <div className={cs('dialog-container', { show: showToast })}> <div className="pay-box"> <div className="new-dialog-box"> <div className="dialog-title"> {title} </div> <div className="dialog-content"> {content} </div> <div className={cs('dialog-bottom flex', { isIPhoneX: isIPhoneX })}> {btn.map((item, idx) => { return ( <div onClick={() => { this.close(idx); // item.callback(); }} className={cs('dialog-btn', { margin_r: _twoBtn && idx == 0, default: _twoBtn && idx == 0 })}> {item.text} </div> ); })} </div> </div> </div> </div> ) } } export default Dialog;
css代碼newDialog.scssdom
$baseFontSize: 16px !default; // pixels to rems @function pxToRem($px) { @return $px / $baseFontSize * 1rem; } .dialog-container { position: fixed; top: 0; left: 0; right: 0; bottom: 0; background: rgba(0, 0, 0, .7); display: none; z-index: 9999; &.show { display: block; } } .new-dialog-box { width: 100%; min-height: pxToRem(195px); background: rgba(255, 255, 255, 1); position: absolute; box-sizing: border-box; margin: 0; bottom: 0; z-index: 9999; transition: all 0.3s; border-radius: pxToRem(8px) pxToRem(8px) 0 0; // display: flex; .dialog-title { font-size: pxToRem(19px); margin: pxToRem(25px) auto pxToRem(25px); text-align: center; font-family: PingFangSC-Medium, PingFang SC; font-weight: 500; color: rgba(51, 51, 51, 1); line-height: pxToRem(19px); } .dialog-content { padding: 0 pxToRem(25px); font-size: pxToRem(14px); font-family: PingFangSC-Regular, PingFang SC; color: rgba(51, 51, 51, 1); margin: 0 auto pxToRem(15px); text-align: center; color: rgba(0, 0, 0, 1); line-height: pxToRem(27px); min-height: pxToRem(50px); } .dialog-bottom { height: pxToRem(45px); padding: 0 pxToRem(20px); margin-bottom: pxToRem(15px); display: flex; &.isIPhoneX { margin-bottom: pxToRem(49px); } } .dialog-btn { // margin: 0 auto pxToRem(30px); // width: pxToRem(300px); flex: 1; height: pxToRem(45px); background-image: linear-gradient(-44deg, #FCAF3C 0%, #F25228 98%); box-shadow: 0 8px 14px 0 rgba(190,118,22,0.20); border-radius: pxToRem(45px); text-align: center; line-height: pxToRem(45px); font-size: pxToRem(15px); font-family: PingFangSC-Medium, PingFang SC; color: rgba(255, 255, 255, 1); &.margin_r { margin-right: pxToRem(24px); } &.default { background: #FFFFFF; border: 1px solid #E6E6E6; box-shadow: 0 10px 20px -8px rgba(173,173,173,0.30); border-radius: pxToRem(45px); font-size: pxToRem(15px); font-family: PingFang-SC-Medium, PingFang-SC; color: rgba(85, 85, 85, 1); } } }
另外附上代碼中iPhone X系列機型判斷方法iphone
export const isIphoneX = function() { // iPhone X、iPhone XS var isIPhoneX = /iphone/gi.test(window.navigator.userAgent) && window.devicePixelRatio && window.devicePixelRatio === 3 && window.screen.width === 375 && window.screen.height === 812; // iPhone XS Max var isIPhoneXSMax = /iphone/gi.test(window.navigator.userAgent) && window.devicePixelRatio && window.devicePixelRatio === 3 && window.screen.width === 414 && window.screen.height === 896; // iPhone XR var isIPhoneXR = /iphone/gi.test(window.navigator.userAgent) && window.devicePixelRatio && window.devicePixelRatio === 2 && window.screen.width === 414 && window.screen.height === 896; if (isIPhoneX || isIPhoneXSMax || isIPhoneXR) { return true; } return false; }