<!--
* @FileDescription: 全局弹窗
* @Author: 黄琪勇
* @Date:2022-06-06 16:21:51
-->
<template>
   <transition name="bounceModalFrame" @afterLeave="unmount">
      <div v-show="show" class="h-modalFrame" :class="props.className" @click="handleMaskClose">
         <div class="animationBox">
            <div ref="modalFrameBoxDom" class="modalFrameBox" :style="modalFrameStyle" @click.stop>
               <div ref="modalFrameTitleDom" class="modalFrameTitle" :class="{ noMove: !props.draggable }">
                  <span>{{ props.title }}</span>
                  <el-icon class="icon" @click="close"><Close /></el-icon>
               </div>
               <div class="frameBox" :class="{ frameBtnNone: !filterBtnShow }">
                  <component :is="props.template" ref="templateModal" @onCancel="cancel" @onClose="close" @onOk="ok" :modal-data="props.modalData" />
               </div>
               <div v-if="filterBtnShow" class="frameBtn">
                  <el-button v-if="props.btnShow[1]" @click="cancel">
                     {{ props.btnTit[1] }}
                  </el-button>
                  <el-button type="primary" v-if="props.btnShow[0]" @click="ok">
                     {{ props.btnTit[0] }}
                  </el-button>
               </div>
            </div>
         </div>
      </div>
   </transition>
</template>

<script setup lang="ts">
import { Close } from "@element-plus/icons-vue"
import { addClass, removeClass } from "@/libs/tools"
import { ref, shallowRef, computed, onMounted, DefineComponent, ComponentPublicInstance, App } from "vue"
import { baseSize, baseWidth, size } from "@/libs/rem"
import { modalFrameSet } from "./index"

interface modalFrameDataType {
   // eslint-disable-next-line @typescript-eslint/ban-types
   template: DefineComponent<{}, {}, any>
   className?: string
   modalData?: Record<string, any>
   width?: string | number
   height?: string | number
   onCancel?: (vm: ComponentPublicInstance, instance: ComponentPublicInstance) => any
   onOk?: (vm: ComponentPublicInstance, instance: ComponentPublicInstance) => any
   onClose?: (vm: ComponentPublicInstance, instance: ComponentPublicInstance) => any
   title?: string
   btnShow?: [boolean?, boolean?]
   btnTit?: [string?, string?]
   maskClose?: boolean
   draggable?: boolean
}
const props = withDefaults(defineProps<modalFrameDataType>(), {
   className: "", // 传入的class,可以依靠这个class控制内部样式
   width: "800", //宽度
   height: "600", //高度
   onCancel: undefined, //取消回调  返回vm
   onOk: undefined, //确认回调 返回 vm
   onClose: undefined, //关闭回调 返回 vm
   title: "", //弹窗title
   btnShow: () => [true, true], //是否显示按钮，[true,false],可以控制按钮的显示隐藏
   btnTit: () => ["确认", "取消"], // 按钮的文字，["确认"]
   maskClose: false, //点击遮罩关闭
   draggable: false //是否可以拖动
})

const bodyDom = document.querySelector("body"),
   name = "h-modalFrameBody"
const show = ref(false) //控制动画显示隐藏
const vm = shallowRef<ComponentPublicInstance>()
const vmApp = shallowRef<App<Element>>()
const modalFrameTitleDom = ref<HTMLDivElement>()
const modalFrameBoxDom = ref<HTMLDivElement>()
const templateModal = ref<ComponentPublicInstance>()
//计算按钮是否显示
const filterBtnShow = computed(() => {
   const { btnShow } = props
   return !!(btnShow[0] || btnShow[1])
})
//初始化transform,width,height
const modalFrameStyle = computed(() => {
   const height = convertValue(props.height),
      width = convertValue(props.width)
   const posWidth = computePos("width", width)
   const posHeight = computePos("height", height)
   return `transform:translate(${posWidth.pos}rem,${posHeight.pos}px);width:${posWidth.unit};height:${posHeight.unit};`
})

function convertValue(strNum: string | number) {
   if (typeof strNum === "number") {
      return strNum
   } else if (!isNaN(Number(strNum))) {
      return Number(strNum)
   } else if (/((^[1-9][0-9]{0,1})|100)%$/.test(strNum)) {
      return strNum
   } else {
      throw new Error("`width`和`height`参数只能为数字或字符串类型数字或者字符串百分比")
   }
}
function computePos(type: "width" | "height", value: number | string) {
   //const clientNum = document.documentElement[type == "width" ? "clientWidth" : "clientHeight"]
   const clientNum = type == "width" ? baseWidth : document.documentElement.clientHeight
   return typeof value === "string"
      ? {
           pos:
              type == "width"
                 ? ((clientNum - (parseInt(value) / 100) * clientNum) / 2 / baseSize).toFixed(5)
                 : (clientNum - (parseInt(value) / 100) * clientNum) / 2,
           unit: value
        }
      : {
           pos: type == "width" ? ((clientNum - value) / 2 / baseSize).toFixed(5) : (clientNum - value * (size / baseSize)) / 2,
           unit: (value / baseSize).toFixed(5) + "rem"
        }
}
onMounted(() => {
   addClass(bodyDom, name)
   props.draggable && drag(modalFrameTitleDom.value!)
   window.addEventListener("resize", refreshPos)
})

/* 拖拽还没有兼容rem */
function drag(el: HTMLElement) {
   function mousedown(e: MouseEvent) {
      const parentElement = modalFrameBoxDom.value!
      const parentElementRect = parentElement.getBoundingClientRect()
      const downX = e.clientX
      const downY = e.clientY
      const clientWidth = document.documentElement.clientWidth
      const clientHeight = document.documentElement.clientHeight
      const minLeft = 0
      const minTop = 0
      const maxLeft = clientWidth - parentElementRect.width
      const maxTop = clientHeight - parentElementRect.height
      function onMousemove(e: MouseEvent) {
         let moveX = parentElementRect.left + (e.clientX - downX)
         let moveY = parentElementRect.top + (e.clientY - downY)
         moveX = moveX < minLeft ? minLeft : moveX > maxLeft ? maxLeft : moveX
         moveY = moveY < minTop ? minTop : moveY > maxTop ? maxTop : moveY
         parentElement.style.transform = `translate(${moveX}px, ${moveY}px)`
      }
      function onMouseup() {
         document.removeEventListener("mousemove", onMousemove)
         document.removeEventListener("mouseup", onMouseup)
      }
      document.addEventListener("mousemove", onMousemove)
      document.addEventListener("mouseup", onMouseup)
   }
   el.addEventListener("mousedown", mousedown)
}
//重新计算位置 居中
function refreshPos() {
   const posWidth = computePos("width", props.width)
   const posHeight = computePos("height", props.height)
   if (modalFrameBoxDom.value) {
      modalFrameBoxDom.value.style.transform = `translate(${posWidth.pos}rem, ${posHeight.pos}px)`
   }
}
//取消按钮
function cancel() {
   if (props.onCancel) {
      props.onCancel(vm.value!, templateModal.value!)
   } else {
      remove()
   }
}
//确认按钮
function ok() {
   if (props.onOk) {
      props.onOk(vm.value!, templateModal.value!)
   } else {
      remove()
   }
}
//关闭按钮
function close() {
   if (props.onClose) {
      props.onClose(vm.value!, templateModal.value!)
   } else {
      remove()
   }
}
// 点击遮罩
function handleMaskClose() {
   if (props.maskClose) {
      close()
   }
}
function remove() {
   //关闭窗口
   show.value = false
}
function unmount() {
   window.removeEventListener("resize", refreshPos)
   vm.value && bodyDom!.removeChild(vm.value.$el)
   vmApp.value && modalFrameSet.delete(vmApp.value)
   vmApp.value?.unmount()
   removeClass(bodyDom, name)
}
defineExpose({ vm, vmApp, remove, show })
</script>
<!-- 取消scoped 减少属性权重  外部添加class可以修改里面的样式 -->
<style lang="scss">
@import "./modalFrame.scss";
body.h-modalFrameBody {
   overflow: hidden;
}
</style>
