Skip to content

原生 js - showModal 显示一个模态对话框。


loading

参数

  • textOrOptions: string | Object - 对话框显示的文本内容或配置选项

  • options: Object - 配置选项

  • text: string - 对话框显示的文本内容(当第一个参数为对象时使用)

  • title: string - 对话框标题,默认为'提示'

  • type: string - 对话框类型,可选值:'success'|'info'|'warning'|'error'

  • showCancel: boolean - 是否显示取消按钮,默认 false

  • showIcon: boolean - 是否显示图标,默认 true

  • onConfirm: Function - 点击确认按钮的回调函数

  • onCancel: Function - 点击取消按钮的回调函数

使用示例

javascript
// 基础用法

showModal('操作成功')

// 快捷方式调用

showModal.success('保存成功')

showModal.error('操作失败')

// 带确认和取消回调

showModal('是否删除?', {
  type: 'warning',

  showCancel: true,

  onConfirm: () => {
    console.log('确认删除')
  },

  onCancel: () => {
    console.log('取消删除')
  }
})

源码

源码
js
/**
 * 显示一个模态对话框
 * @author chuxiao
 * @param {string|Object} textOrOptions - 对话框显示的文本内容或配置选项
 * @param {Object} [options={}] - 配置选项
 * @param {string} [options.text] - 对话框显示的文本内容(当第一个参数为对象时使用)
 * @param {string} [options.title='提示'] - 对话框标题
 * @param {string} [options.type=''] - 对话框类型,可选值:'success'|'info'|'warning'|'error'
 * @param {boolean} [options.showCancel=false] - 是否显示取消按钮
 * @param {boolean} [options.showIcon=true] - 是否显示图标
 * @param {Function} [options.onConfirm=()=>{}] - 点击确认按钮的回调函数
 * @param {Function} [options.onCancel=()=>{}] - 点击取消按钮的回调函数
 * @example
 * // 基础用法-直接传文本
 * showModal('操作成功');
 *
 * // 快捷方式调用
 * showModal.success('操作成功');
 * showModal.error('操作失败');
 *
 * // 基础用法-通过options传文本
 * showModal({
 *   text: '操作成功',
 *   showIcon: true
 * });
 *
 * // 带类型的提示
 * showModal('保存成功', {
 *   title: '系统提示',
 *   type: 'success',
 *   onConfirm: () => {
 *     console.log('确认');
 *   }
 * });
 *
 * // 带确认和取消回调
 * showModal('是否删除?', {
 *   type: 'warning',
 *   showCancel: true,
 *   onConfirm: () => {
 *     console.log('确认删除');
 *   },
 *   onCancel: () => {
 *     console.log('取消删除');
 *   }
 * });
 */
function showModal(textOrOptions, options = {}) {
  let text, finalOptions

  if (typeof textOrOptions === 'string') {
    text = textOrOptions
    finalOptions = options
  } else {
    text = textOrOptions.text || ''
    finalOptions = textOrOptions
  }

  const {
    title = '提示',
    type = '',
    showCancel = false,
    showIcon = true,
    onConfirm = () => {},
    onCancel = () => {}
  } = finalOptions

  const iconColorMap = {
    success: '#4CAF50',
    info: '#2196F3',
    warning: '#ff9800',
    error: '#f44336'
  }

  const svgIcons = {
    success: `<svg viewBox="0 0 1024 1024" width="20" height="20"><path d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm193.5 301.7l-210.6 292a31.8 31.8 0 0 1-51.7 0L318.5 484.9c-3.8-5.3 0-12.7 6.5-12.7h46.9c10.2 0 19.9 4.9 25.9 13.3l71.2 98.8 157.2-218c6-8.3 15.6-13.3 25.9-13.3H699c6.5 0 10.3 7.4 6.5 12.7z" fill="${iconColorMap[type]}"/></svg>`,
    info: `<svg viewBox="0 0 1024 1024" width="20" height="20"><path d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm32 664c0 4.4-3.6 8-8 8h-48c-4.4 0-8-3.6-8-8V456c0-4.4 3.6-8 8-8h48c4.4 0 8 3.6 8 8v272zm-32-344c-26.5 0-48-21.5-48-48s21.5-48 48-48 48 21.5 48 48-21.5 48-48 48z" fill="${iconColorMap[type]}"/></svg>`,
    warning: `<svg viewBox="0 0 1024 1024" width="20" height="20"><path d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm-32 232c0-4.4 3.6-8 8-8h48c4.4 0 8 3.6 8 8v272c0 4.4-3.6 8-8 8h-48c-4.4 0-8-3.6-8-8V296zm32 440c-26.5 0-48-21.5-48-48s21.5-48 48-48 48 21.5 48 48-21.5 48-48 48z" fill="${iconColorMap[type]}"/></svg>`,
    error: `<svg viewBox="0 0 1024 1024" width="20" height="20"><path d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm165.4 618.2l-66-.3L512 563.4l-99.3 118.4-66.1.3c-4.4 0-8-3.5-8-8 0-1.9.7-3.7 1.9-5.2l130.1-155L340.5 359c-1.2-1.5-1.9-3.3-1.9-5.2 0-4.4 3.6-8 8-8l66.1.3L512 464.6l99.3-118.4 66-.3c4.4 0 8 3.5 8 8 0 1.9-.7 3.7-1.9 5.2L553.5 514l130 155c1.2 1.5 1.9 3.3 1.9 5.2 0 4.4-3.6 8-8 8z" fill="${iconColorMap[type]}"/></svg>`
  }

  // document.body.style.overflow = 'hidden'

  const mask = document.createElement('div')
  mask.style.cssText = `
    position: fixed;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    background: rgba(0,0,0,0.6);
    display: flex;
    justify-content: center;
    align-items: center;
    z-index: 999;
    backdrop-filter: blur(3px);
    margin: 0;
    padding: 0;
  `

  if (showCancel) {
    mask.onclick = e => {
      if (e.target === mask) {
        closeModal()
        onCancel()
      }
    }
  }

  const modal = document.createElement('div')
  modal.style.cssText = `
    background: white;
    padding: 16px 20px;
    border-radius: 8px;
    min-width: 320px;
    max-width: 360px;
    box-shadow: 0 5px 15px rgba(0,0,0,0.2);
    animation: modalFadeIn 0.3s ease;
    position: relative;
    margin: 0;
  `

  const titleEl = document.createElement('h3')
  titleEl.textContent = title
  titleEl.style.cssText = `
    font-size: 16px;
    color: #333;
    margin: 0 0 12px 0;
    padding: 0 0 8px 0;
    border-bottom: 1px solid #eee;
    text-align: left;
  `
  modal.appendChild(titleEl)

  if (showCancel) {
    const closeBtn = document.createElement('div')
    closeBtn.innerHTML = '×'
    closeBtn.style.cssText = `
      position: absolute;
      right: 16px;
      top: 12px;
      font-size: 22px;
      color: #999;
      cursor: pointer;
      line-height: 1;
      margin: 0;
      padding: 0;
    `
    closeBtn.onclick = () => {
      closeModal()
      onCancel()
    }
    modal.appendChild(closeBtn)
  }

  const contentWrapper = document.createElement('div')
  contentWrapper.style.cssText = `
    display: flex;
    align-items: center;
    gap: 8px;
    margin: 12px 0;
    padding: 0;
  `

  if (showIcon && type && svgIcons[type]) {
    const iconContainer = document.createElement('div')
    iconContainer.innerHTML = svgIcons[type]
    iconContainer.style.cssText = `
      flex-shrink: 0;
      display: flex;
      align-items: center;
      margin: 0;
      padding: 0;
    `
    contentWrapper.appendChild(iconContainer)
  }

  const content = document.createElement('div')
  content.textContent = text
  content.style.cssText = `
    font-size: 14px;
    color: #666;
    line-height: 1.5;
    margin: 0;
    padding: 0;
    flex: 1;
  `
  contentWrapper.appendChild(content)
  modal.appendChild(contentWrapper)

  const btnContainer = document.createElement('div')
  btnContainer.style.cssText = `
    display: flex;
    justify-content: flex-end;
    gap: 12px;
    margin: 16px 0 0 0;
    padding: 0;
  `

  if (showCancel) {
    const cancelBtn = document.createElement('button')
    cancelBtn.textContent = '取消'
    cancelBtn.style.cssText = `
      padding: 6px 20px;
      cursor: pointer;
      background: #f5f5f5;
      color: #666;
      border: none;
      border-radius: 4px;
      font-size: 14px;
      transition: all 0.3s ease;
      margin: 0;
    `

    cancelBtn.onmouseover = () => {
      cancelBtn.style.background = '#e0e0e0'
      cancelBtn.style.transform = 'scale(1.05)'
    }
    cancelBtn.onmouseout = () => {
      cancelBtn.style.background = '#f5f5f5'
      cancelBtn.style.transform = 'scale(1)'
    }

    cancelBtn.onclick = () => {
      closeModal()
      onCancel()
    }
    btnContainer.appendChild(cancelBtn)
  }

  const confirmBtn = document.createElement('button')
  confirmBtn.textContent = '确定'
  confirmBtn.style.cssText = `
    padding: 6px 20px;
    cursor: pointer;
    background: ${type ? iconColorMap[type] : '#2196F3'};
    color: white;
    border: none;
    border-radius: 4px;
    font-size: 14px;
    transition: all 0.3s ease;
    margin: 0;
  `

  confirmBtn.onmouseover = () => {
    confirmBtn.style.opacity = '0.9'
    confirmBtn.style.transform = 'scale(1.05)'
  }
  confirmBtn.onmouseout = () => {
    confirmBtn.style.opacity = '1'
    confirmBtn.style.transform = 'scale(1)'
  }

  confirmBtn.onclick = () => {
    closeModal()
    onConfirm()
  }
  btnContainer.appendChild(confirmBtn)
  modal.appendChild(btnContainer)

  const style = document.createElement('style')
  style.textContent = `
    @keyframes modalFadeIn {
      from {
        opacity: 0;
        transform: translateY(-20px);
      }
      to {
        opacity: 1;
        transform: translateY(0);
      }
    }
    @keyframes modalFadeOut {
      from {
        opacity: 1;
        transform: translateY(0);
      }
      to {
        opacity: 0;
        transform: translateY(20px);
      }
    }
  `
  document.head.appendChild(style)

  mask.appendChild(modal)
  document.body.appendChild(mask)

  function closeModal() {
    modal.style.animation = 'modalFadeOut 0.3s ease forwards'
    mask.style.opacity = '0'
    mask.style.transition = 'opacity 0.3s ease'

    setTimeout(() => {
      document.body.style.overflow = ''
      document.body.removeChild(mask)
      document.head.removeChild(style)
    }, 300)
  }
}
// 添加快捷方式
;['success', 'info', 'warning', 'error'].forEach(type => {
  showModal[type] = (text, options = {}) => {
    return showModal(text, { ...options, type })
  }
})

:::

Released under the MIT License.