Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

封装了一下modal #6

Open
transtone opened this issue Mar 31, 2024 · 0 comments
Open

封装了一下modal #6

transtone opened this issue Mar 31, 2024 · 0 comments

Comments

@transtone
Copy link

transtone commented Mar 31, 2024

使用 card preset 封装了一下 modal,可拖动,可以省写几个属性。

x-n-modal.vue

<template>
  <n-modal id="basic-modal" $show="showModal" :style="{ width: modalWidth }" preset="card" :size="size" :bordered="false"
           :closable="closable" :auto-focus="autoFocus" :block-scroll="blockScroll" :close-on-esc="closeOnEsc" :display-directive="displayDirective"
           :mask-closable="maskClosable" :to="to" :transform-origin="transformOrigin" :trap-focus="trapFocus" :z-index="zIndex"
           @close="emit('close')" @after-enter="$emit('after-enter')" @after-leave="$emit('after-leave')"
           @esc="$emit('esc')" @mask-click="$emit('mask-click')" @update:show="$emit('update:show', $event)">
    <template #header>
      <div id="basic-modal-bar" class="w-full" :class="{'cursor-move': draggable}">{{ title }}</div>
    </template>
    <slot name="default" />
    <template #action>
      <slot v-if="showAction" name="action">
        <n-space class="!justify-end">
          <n-button :size="size" @click="cancelModal">{{ negativeText }}</n-button>
          <n-button :size="size" type="primary" :loading="subLoading" @click="confirmModal">{{ positiveText }}</n-button>
        </n-space>
      </slot>
    </template>
  </n-modal>
</template>

<script setup>

const emit = defineEmits(['close', 'positive-click', 'update:show', 'after-leave', 'after-enter', 'mask-click', 'esc', 'negative-click'])

const showModal = defineModel('show', { type: Boolean, default: false })
const props = defineProps({
  size: { type: String },
  title: { type: String, default: '' },
  width: { type: [String, Number], default: 500 },
  draggable: { type: Boolean, default: false },
  showAction: { type: Boolean, default: true },
  negativeText: { type: String, default: '取消' },
  positiveText: { type: String, default: '确定' },
  closable: { type: Boolean, default: true },
  autoFocus: { type: Boolean, default: false },
  blockScroll: { type: Boolean, default: true },
  closeOnEsc: { type: Boolean, default: true },
  closeOnConfirm: { type: Boolean, default: true }, // 确认时不自动关闭弹窗
  closeOnCancel: { type: Boolean, default: true }, // 取消时不自动关闭弹窗
  displayDirective: { type: String, default: 'if' }, // 'if' | 'show'
  maskClosable: { type: Boolean, default: true },
  to: { type: String, default: 'body' },
  transformOrigin: { type: String, default: 'mouse' }, // 'mouse' | 'center'
  trapFocus: { type: Boolean, default: true },
  zIndex: { type: Number },
})

const modalWidth = computed(() => {
  return typeof props.width === 'number' ? `${props.width}px` : props.width
})

let subLoading = $ref(false)

watch(showModal, val => {
  if (!val) {
    subLoading = false
    return
  }
  if (!props.draggable) {return }
  nextTick(() => {
    const oBox = document.getElementById('basic-modal')
    const oBar = document.getElementById('basic-modal-bar')
    startDrag(oBar, oBox)
  })
})
const cancelModal = () => {
  emit('negative-click')
  if (props.closeOnCancel) {
    showModal.value = false
  }
}
const confirmModal = () => {
  subLoading = true
  emit('positive-click')
  if (props.closeOnConfirm) {
    showModal.value = false
  }
}
</script>

Drag.ts

//获取相关CSS属性
const getCss = function (o, key) {
  return o.currentStyle
    ? o.currentStyle[key]
    : document.defaultView?.getComputedStyle(o, null)[key]
}

const params = {
  left: 0,
  top: 0,
  currentX: 0,
  currentY: 0,
  flag: false,
}

export const startDrag = function (bar, target, callback?) {
  const screenWidth = document.body.clientWidth // body当前宽度
  const screenHeight = document.documentElement.clientHeight // 可见区域高度

  const dragDomW = target.offsetWidth // 对话框宽度
  const dragDomH = target.offsetHeight // 对话框高度

  const minDomLeft = target.offsetLeft
  const minDomTop = target.offsetTop

  const maxDragDomLeft = screenWidth - minDomLeft - dragDomW
  const maxDragDomTop = screenHeight - minDomTop - dragDomH

  if (getCss(target, 'left') !== 'auto') {
    params.left = getCss(target, 'left')
  }
  if (getCss(target, 'top') !== 'auto') {
    params.top = getCss(target, 'top')
  }

  //o是移动对象
  bar.onmousedown = function (event) {
    params.flag = true
    if (!event) {
      event = window.event
      //防止IE文字选中
      bar.onselectstart = function () {
        return false
      }
    }
    const e = event
    params.currentX = e.clientX
    params.currentY = e.clientY
  }
  document.onmouseup = function () {
    params.flag = false
    if (getCss(target, 'left') !== 'auto') {
      params.left = getCss(target, 'left')
    }
    if (getCss(target, 'top') !== 'auto') {
      params.top = getCss(target, 'top')
    }
  }
  document.onmousemove = function (event) {
    const e: any = event || window.event
    if (params.flag) {
      const nowX = e.clientX
      const nowY = e.clientY
      const disX = nowX - params.currentX
      const disY = nowY - params.currentY

      let left = parseInt(params.left) + disX
      let top = parseInt(params.top) + disY

      // 拖出屏幕边缘
      if (-left > minDomLeft) {
        left = -minDomLeft
      } else if (left > maxDragDomLeft) {
        left = maxDragDomLeft
      }

      if (-top > minDomTop) {
        top = -minDomTop
      } else if (top > maxDragDomTop) {
        top = maxDragDomTop
      }

      target.style.left = left + 'px'
      target.style.top = top + 'px'

      if (typeof callback === 'function') {
        callback((parseInt(params.left) || 0) + disX, (parseInt(params.top) || 0) + disY)
      }

      if (event.preventDefault) {
        event.preventDefault()
      }
      return false
    }
  }
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant