Ant-Design源码分析——Modal

2021SC@SDUSC

首先导入相关的模块

import * as React from 'react';
import Dialog from 'rc-dialog';
import classNames from 'classnames';
import CloseOutlined from '@ant-design/icons/CloseOutlined';

import { getConfirmLocale } from './locale';
import Button from '../button';
import { LegacyButtonType, ButtonProps, convertLegacyProps } from '../button/button';
import LocaleReceiver from '../locale-provider/LocaleReceiver';
import { ConfigContext, DirectionType } from '../config-provider';
import { canUseDocElement } from '../_util/styleChecker';
import { getTransitionName } from '../_util/motion';

设定变量,存储鼠标的点击坐标

let mousePosition: { x: number; y: number } | null;
const getClickPosition = (e: MouseEvent) => {
  mousePosition = {
    x: e.pageX,
    y: e.pageY,
  };
  // 100ms 内发生过点击事件,则从点击位置动画展示
  // 否则直接 zoom 展示
  // 这样可以兼容非点击方式展开
  setTimeout(() => {
    mousePosition = null;
  }, 100);
};

// 只有点击事件支持从鼠标位置动画展开
if (canUseDocElement()) {
  document.documentElement.addEventListener('click', getClickPosition, true);
}
export interface ModalProps {
  /** 对话框是否可见 */
  visible?: boolean;
  /** 确定按钮 loading */
  confirmLoading?: boolean;
  /** 标题 */
  title?: React.ReactNode | string;
  /** 是否显示右上角的关闭按钮 */
  closable?: boolean;
  /** 点击确定回调 */
  onOk?: (e: React.MouseEvent<HTMLElement>) => void;
  /** 点击模态框右上角叉、取消按钮、Props.maskClosable 值为 true 时的遮罩层或键盘按下 Esc 时的回调 */
  onCancel?: (e: React.MouseEvent<HTMLElement>) => void;
  /** 关闭对话框之后的事件 */
  afterClose?: () => void;
  /** 垂直居中 */
  centered?: boolean;
  /** 宽度 */
  width?: string | number;
  /** 底部内容 */
  footer?: React.ReactNode;
  /** 确认按钮文字 */
  okText?: React.ReactNode;
  /** 确认按钮类型 */
  okType?: LegacyButtonType;
  /** 取消按钮文字 */
  cancelText?: React.ReactNode;
  /** 点击蒙层是否允许关闭 */
  maskClosable?: boolean;
  /** 强制渲染 Modal */
  forceRender?: boolean;
  /** 对话框中的确认按钮,类型为ButtonProps */
  okButtonProps?: ButtonProps;
  /** 对话框中的取消按钮,类型为ButtonProps */
  cancelButtonProps?: ButtonProps;
  /** 关闭后是否完全清除此对话框的内容 */
  destroyOnClose?: boolean;
  /** 样式 */
  style?: React.CSSProperties;
  /** 对话框外层容器的类名 */
  wrapClassName?: string;
  /** 遮罩显示方式的名字 */
  maskTransitionName?: string;
  /** 对话框显示方式的名字 */
  transitionName?: string;
  /** 类名 */
  className?: string;
  /** 获取容器内的字符串或Element */
  getContainer?: string | HTMLElement | getContainerFunc | false;
  /** 对话框的纵坐标,即位于第几层,形成堆叠的效果 */
  zIndex?: number;
  /** body的样式 */
  bodyStyle?: React.CSSProperties;
  /** 遮罩的样式 */
  maskStyle?: React.CSSProperties;
  /** 是否有遮罩 */
  mask?: boolean;
  /** 是否可以使用键盘的esc关闭对话框 */
  keyboard?: boolean;
  /** 对话框外层容器 */
  wrapProps?: any;
  /** 前缀信息 */
  prefixCls?: string;
  /** 关闭按钮的图标 */
  closeIcon?: React.ReactNode;
  /** 对话框渲染样式 */
  modalRender?: (node: React.ReactNode) => React.ReactNode;
  /** 对话框关闭后是否需要聚焦触发元素 */
  focusTriggerAfterClose?: boolean;
}

生成一个默认的对话框Modal

// 声明ModalLocale
export interface ModalLocale {
  okText: string;
  cancelText: string;
  justOkText: string;
}

const Modal: React.FC<ModalProps> = props => {
  const {
    getPopupContainer: getContextPopupContainer,
    getPrefixCls,
    direction,
  } = React.useContext(ConfigContext);
  // 手动取消对话框
  const handleCancel = (e: React.MouseEvent<HTMLButtonElement>) => {
    const { onCancel } = props;
    onCancel?.(e);
  };
  // 手动确认对话框
  const handleOk = (e: React.MouseEvent<HTMLButtonElement>) => {
    const { onOk } = props;
    onOk?.(e);
  };
  // 设置页脚,包括取消按钮与确定按钮
  const renderFooter = (locale: ModalLocale) => {
    const { okText, okType, cancelText, confirmLoading } = props;
    return (
      <>
        <!--取消按钮-->
        <Button onClick={handleCancel} {...props.cancelButtonProps}>
          {cancelText || locale.cancelText}
        </Button>
        <!--确认按钮-->
        <Button
          {...convertLegacyProps(okType)}
          loading={confirmLoading}
          onClick={handleOk}
          {...props.okButtonProps}
        >
          {okText || locale.okText}
        </Button>
      </>
    );
  };

  const {
    prefixCls: customizePrefixCls,
    footer,
    visible,
    wrapClassName,
    centered,
    getContainer,
    closeIcon,
    focusTriggerAfterClose = true,
    ...restProps
  } = props;

  const prefixCls = getPrefixCls('modal', customizePrefixCls);
  const rootPrefixCls = getPrefixCls();
  //默认的页脚
  const defaultFooter = (
    <LocaleReceiver componentName="Modal" defaultLocale={getConfirmLocale()}>
      {renderFooter}
    </LocaleReceiver>
  );
  //关闭图标
  const closeIconToRender = (
    <span className={`${prefixCls}-close-x`}>
      {closeIcon || <CloseOutlined className={`${prefixCls}-close-icon`} />}
    </span>
  );
  //外层类名
  const wrapClassNameExtended = classNames(wrapClassName, {
    [`${prefixCls}-centered`]: !!centered,
    [`${prefixCls}-wrap-rtl`]: direction === 'rtl',
  });
  //最后返回一个对话框
  return (
    <Dialog
      {...restProps}
      getContainer={
        getContainer === undefined ? (getContextPopupContainer as getContainerFunc) : getContainer
      }
      prefixCls={prefixCls}
      wrapClassName={wrapClassNameExtended}
      footer={footer === undefined ? defaultFooter : footer}
      visible={visible}
      mousePosition={mousePosition}
      onClose={handleCancel}
      closeIcon={closeIconToRender}
      focusTriggerAfterClose={focusTriggerAfterClose}
      transitionName={getTransitionName(rootPrefixCls, 'zoom', props.transitionName)}
      maskTransitionName={getTransitionName(rootPrefixCls, 'fade', props.maskTransitionName)}
    />
  );
};

再设置一下对话框的其他默认参数

Modal.defaultProps = {
  width: 520,
  confirmLoading: false,
  visible: false,
  okType: 'primary' as LegacyButtonType,
};

再来看ConfirmDialog

首先导入相应需要的模块

import * as React from 'react';
import classNames from 'classnames';
import Dialog, { ModalFuncProps } from './Modal';
import ActionButton from '../_util/ActionButton';
import devWarning from '../_util/devWarning';
import ConfigProvider from '../config-provider';
import { getTransitionName } from '../_util/motion';

首先声明一个自己的实例,使用继承方法从Modal里继承

// 从Modal继承下来的ConfirmDialogProps
interface ConfirmDialogProps extends ModalFuncProps {
  afterClose?: () => void;
  close: (...args: any[]) => void;
  autoFocusButton?: null | 'ok' | 'cancel';
  rootPrefixCls: string;
  iconPrefixCls?: string;
}

最后定义实例,其中包含的变量已在上面的Modal提出

const ConfirmDialog = (props: ConfirmDialogProps) => {
  const {
    icon,
    onCancel,
    onOk,
    close,
    zIndex,
    afterClose,
    visible,
    keyboard,
    centered,
    getContainer,
    maskStyle,
    okText,
    okButtonProps,
    cancelText,
    cancelButtonProps,
    direction,
    prefixCls,
    rootPrefixCls,
    iconPrefixCls,
    bodyStyle,
    closable = false,
    closeIcon,
    modalRender,
    focusTriggerAfterClose,
  } = props;

  devWarning(
    !(typeof icon === 'string' && icon.length > 2),
    'Modal',
    `\`icon\` is using ReactNode instead of string naming in v4. Please check \`${icon}\` at https://ant.design/components/icon`,
  );

  // 支持传入{ icon: null }来隐藏`Modal.confirm`默认的Icon
  const okType = props.okType || 'primary';
  const contentPrefixCls = `${prefixCls}-confirm`;
  // 默认为 true,保持向下兼容
  const okCancel = 'okCancel' in props ? props.okCancel! : true;
  // 默认宽度为416
  const width = props.width || 416;
  // 默认样式为空
  const style = props.style || {};
  // 默认存在遮罩
  const mask = props.mask === undefined ? true : props.mask;
  // 默认为 false,保持旧版默认行为
  const maskClosable = props.maskClosable === undefined ? false : props.maskClosable;
  const autoFocusButton = props.autoFocusButton === null ? false : props.autoFocusButton || 'ok';
  // 为本Dialog的类名,会被导出
  const classString = classNames(
    contentPrefixCls,
    `${contentPrefixCls}-${props.type}`,
    { [`${contentPrefixCls}-rtl`]: direction === 'rtl' },
    props.className,
  );
  // 定义了一个取消按钮,会被嵌套在Dialog中
  const cancelButton = okCancel && (
    <ActionButton
      actionFn={onCancel}
      close={close}
      autoFocus={autoFocusButton === 'cancel'}
      buttonProps={cancelButtonProps}
      prefixCls={`${rootPrefixCls}-btn`}
    >
      {cancelText}
    </ActionButton>
  );
  // 最后返回一个成型的Dialog
  return (
    <ConfigProvider prefixCls={rootPrefixCls} iconPrefixCls={iconPrefixCls} direction={direction}>
      <!--对话框的本体,使用了上述变量-->
      <Dialog
        prefixCls={prefixCls}
        className={classString}
        wrapClassName={classNames({ [`${contentPrefixCls}-centered`]: !!props.centered })}
        onCancel={() => close({ triggerCancel: true })}
        visible={visible}
        title=""
        footer=""
        transitionName={getTransitionName(rootPrefixCls, 'zoom', props.transitionName)}
        maskTransitionName={getTransitionName(rootPrefixCls, 'fade', props.maskTransitionName)}
        mask={mask}
        maskClosable={maskClosable}
        maskStyle={maskStyle}
        style={style}
        width={width}
        zIndex={zIndex}
        afterClose={afterClose}
        keyboard={keyboard}
        centered={centered}
        getContainer={getContainer}
        closable={closable}
        closeIcon={closeIcon}
        modalRender={modalRender}
        focusTriggerAfterClose={focusTriggerAfterClose}
      >
        <!--对话框的类名,即包含内容以及标题等-->
        <div className={`${contentPrefixCls}-body-wrapper`}>
          <div className={`${contentPrefixCls}-body`} style={bodyStyle}>
            {icon}
            {props.title === undefined ? null : (
              <span className={`${contentPrefixCls}-title`}>{props.title}</span>
            )}
            <div className={`${contentPrefixCls}-content`}>{props.content}</div>
          </div>
          <div className={`${contentPrefixCls}-btns`}>
            <!--取消按钮-->
            {cancelButton}
            <!--确认按钮-->
            <ActionButton
              type={okType}
              actionFn={onOk}
              close={close}
              autoFocus={autoFocusButton === 'ok'}
              buttonProps={okButtonProps}
              prefixCls={`${rootPrefixCls}-btn`}
            >
              {okText}
            </ActionButton>
          </div>
        </div>
      </Dialog>
    </ConfigProvider>
  );
};

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
如果您使用的是ant-design-vue3的Modal组件,并尝试在关闭模态框时使用resetFields方法重置表单,但是发现该方法无效,可能是您的代码中存在一些问题。 首先,请确保您正确地引入了antd的Vue3组件库,并且使用了正确的版本。如果您不确定,请参考antd官方文档。 其次,请检查您的代码中是否正确使用了Form组件,并且已经将form对象绑定到了Modal组件中。例如,您的代码可能类似于以下代码片段: ```html <template> <a-modal v-model:visible="visible" @ok="handleOk" @cancel="handleCancel"> <a-form :form="form"> <!-- 表单控件 --> </a-form> </a-modal> </template> <script> import { defineComponent, reactive } from 'vue'; import { Modal, Form } from 'ant-design-vue'; export default defineComponent({ components: { 'a-modal': Modal, 'a-form': Form, }, setup() { const form = reactive({}); const visible = reactive(false); const handleOk = () => { form.resetFields(); // 重置表单 visible.value = false; // 关闭模态框 }; const handleCancel = () => { form.resetFields(); // 重置表单 visible.value = false; // 关闭模态框 }; return { form, visible, handleOk, handleCancel, }; }, }); </script> ``` 在上面的代码中,我们将表单对象`form`绑定到了Modal组件中,并在`handleOk`和`handleCancel`方法中使用了`form.resetFields()`方法来重置表单。如果您的代码类似于此,但仍然无法解决问题,请尝试检查您的表单控件是否正确绑定了`v-model`,以及是否正确使用了`name`属性。 如果您仍然无法解决问题,请尝试提供更多的代码或其他信息,以便我们更好地帮助您解决问题。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值