|
|
@@ -0,0 +1,247 @@
|
|
|
+<script setup lang="ts">
|
|
|
+import { ref, toRefs, withDefaults, defineExpose, onMounted, watch, nextTick } from "vue"
|
|
|
+import { getAssetPath } from "@/core/helpers/assets"
|
|
|
+import { dialog } from "@/core/utils/message"
|
|
|
+import { Modal } from "bootstrap"
|
|
|
+const props = withDefaults(
|
|
|
+ defineProps<{
|
|
|
+ modal: any //初始化后会把bootstrap的modal对象绑定在外部的modal上,外部请使用 v-model:modal=""
|
|
|
+ id?: string
|
|
|
+ titlePrefix?: string
|
|
|
+ title?: string
|
|
|
+ backdrop?: boolean | "static" //是否有遮罩, 设置"static"时点击遮罩不关闭modal
|
|
|
+ focus?: boolean //初始化时焦点放在modal上
|
|
|
+ keyboard?: boolean //true时按下esc关闭modal
|
|
|
+ showEvent?: (event: Event) => void
|
|
|
+ shownEvent?: (event: Event) => void
|
|
|
+ hideEvent?: (event: Event) => void
|
|
|
+ hiddenEvent?: (event: Event) => void
|
|
|
+ hidePreventedEvent?: (event: Event) => void //backdrop为static:点击遮罩事件或者 keyboard为false时按下esc
|
|
|
+ validateForm?: boolean //验证表单
|
|
|
+ validateFormCallback?: (isValid: boolean, invalidFields?: any) => void //验证表单
|
|
|
+ saveAutoClose?: boolean // 保存时自动关闭modal
|
|
|
+ closeNeedConfrim?: boolean // 关闭模态框需要确认
|
|
|
+ modalFormData?: any // 模态框表单数据
|
|
|
+ formLabelWidth?: string | number // 表单label宽度
|
|
|
+ formSize?: "large" | "default" | "small"
|
|
|
+ closeBtn?: boolean //显示关闭按钮
|
|
|
+ closeBtnText?: string
|
|
|
+ closeBtnClass?: string
|
|
|
+ closeBtnStyle?: string
|
|
|
+ confirmBtn?: boolean //显示确认按钮
|
|
|
+ confirmBtnClass?: string
|
|
|
+ confirmBtnStyle?: string
|
|
|
+ confirmBtnText?: string
|
|
|
+ modalClass?: string
|
|
|
+ modalDialogClass?: string
|
|
|
+ modalContentClass?: string
|
|
|
+ modalHeaderClass?: string
|
|
|
+ modalTitleClass?: string
|
|
|
+ modalBodyClass?: string
|
|
|
+ modalFormClass?: string
|
|
|
+ modalFooterClass?: string
|
|
|
+ modalStyle?: string
|
|
|
+ modalDialogStyle?: string
|
|
|
+ modalContentStyle?: string
|
|
|
+ modalHeaderStyle?: string
|
|
|
+ modalTitleStyle?: string
|
|
|
+ modalBodyStyle?: string
|
|
|
+ modalFormStyle?: string
|
|
|
+ modalFooterStyle?: string
|
|
|
+ }>(),
|
|
|
+ {
|
|
|
+ titlePrefix: "",
|
|
|
+ title: "",
|
|
|
+ id: () => {
|
|
|
+ return `vb_${Math.floor(Math.random() * 999999)}`
|
|
|
+ },
|
|
|
+ backdrop: true,
|
|
|
+ focus: true,
|
|
|
+ keyboard: true,
|
|
|
+ saveAutoClose: true,
|
|
|
+ validateForm: true,
|
|
|
+ formLabelWidth: "100px",
|
|
|
+ formSize: "large",
|
|
|
+ closeNeedConfrim: true,
|
|
|
+ closeBtn: true,
|
|
|
+ closeBtnText: "关闭",
|
|
|
+ closeBtnClass: "btn btn-light",
|
|
|
+ closeBtnStyle: "",
|
|
|
+ confirmBtn: true,
|
|
|
+ confirmBtnText: "确认",
|
|
|
+ confirmBtnClass: "btn btn-primary",
|
|
|
+ confirmBtnStyle: "",
|
|
|
+ modalClass: "",
|
|
|
+ modalDialogClass: "modal-dialog-centered",
|
|
|
+ modalContentClass: "",
|
|
|
+ modalHeaderClass: "",
|
|
|
+ modalTitleClass: "",
|
|
|
+ modalBodyClass: "",
|
|
|
+ modalFormClass: "",
|
|
|
+ modalFooterClass: "",
|
|
|
+ modalStyle: "",
|
|
|
+ modalDialogStyle: "",
|
|
|
+ modalContentStyle: "",
|
|
|
+ modalHeaderStyle: "",
|
|
|
+ modalTitleStyle: "",
|
|
|
+ modalBodyStyle: "",
|
|
|
+ modalFormStyle: "",
|
|
|
+ modalFooterStyle: "",
|
|
|
+ }
|
|
|
+)
|
|
|
+const { modal } = toRefs(props)
|
|
|
+const emits = defineEmits<{
|
|
|
+ (e: "update:modal", modal: Modal): boolean
|
|
|
+ (e: "cancel", form: HTMLFormElement | undefined | null, callback?: (v: boolean) => void): boolean
|
|
|
+ (e: "confirm", form: HTMLFormElement | undefined | null): void
|
|
|
+}>()
|
|
|
+const modalEl = ref<any>()
|
|
|
+const modalFormEl = ref<HTMLFormElement>()
|
|
|
+function show() {
|
|
|
+ modal.value?.show()
|
|
|
+}
|
|
|
+function cancel() {
|
|
|
+ const closeModal = () => {
|
|
|
+ let result = true
|
|
|
+ emits("cancel", modalFormEl.value, (v: boolean) => {
|
|
|
+ result = v
|
|
|
+ })
|
|
|
+ console.log("cancel", result)
|
|
|
+ //callback传false 阻止modal关闭
|
|
|
+ if (result) {
|
|
|
+ nextTick(() => {
|
|
|
+ //modalFormEl.value?.resetFields()
|
|
|
+ modalFormEl.value?.clearValidate()
|
|
|
+ })
|
|
|
+ //modalFormEl.value?.clearValidate()
|
|
|
+ //modalFormEl.value?.resetFields()
|
|
|
+ console.log(" modalFormEl.value", modalFormEl.value)
|
|
|
+ modal.value.hide()
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (props.closeNeedConfrim) {
|
|
|
+ dialog.confirm("数据还未保存,确认关闭对话框?", "关闭对话框", (confirm: any) => {
|
|
|
+ console.log("confirm", confirm)
|
|
|
+ if (confirm.isConfirmed) {
|
|
|
+ closeModal()
|
|
|
+ }
|
|
|
+ })
|
|
|
+ } else {
|
|
|
+ closeModal()
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+function confirm() {
|
|
|
+ if (props.validateForm) {
|
|
|
+ modalFormEl.value?.validate((valid: boolean, fields: any) => {
|
|
|
+ if (props.validateFormCallback) {
|
|
|
+ props.validateFormCallback(valid, fields)
|
|
|
+ } else if (valid) {
|
|
|
+ emits("confirm", modalFormEl.value)
|
|
|
+ if (props.saveAutoClose) {
|
|
|
+ modal.value.hide()
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ console.error("FORM 验证失败", fields)
|
|
|
+ }
|
|
|
+ })
|
|
|
+ } else {
|
|
|
+ emits("confirm", modalFormEl.value)
|
|
|
+ if (props.saveAutoClose) {
|
|
|
+ modal.value.hide()
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+function init() {
|
|
|
+ if (modalEl.value) {
|
|
|
+ const bsModal = new Modal(modalEl.value, {
|
|
|
+ backdrop: props.backdrop,
|
|
|
+ focus: props.focus,
|
|
|
+ keyboard: props.keyboard,
|
|
|
+ })
|
|
|
+ console.log("+++", bsModal)
|
|
|
+ emits("update:modal", bsModal)
|
|
|
+ modalEl.value.removeEventListener("hide.bs.modal", props.hideEvent, true)
|
|
|
+ modalEl.value.removeEventListener("hidden.bs.modal", props.hiddenEvent, true)
|
|
|
+ modalEl.value.removeEventListener("show.bs.modal", props.showEvent, true)
|
|
|
+ modalEl.value.removeEventListener("shown.bs.modal", props.shownEvent, true)
|
|
|
+ modalEl.value.removeEventListener("hidePrevented.bs.modal", props.hidePreventedEvent, true)
|
|
|
+ if (props.hideEvent) {
|
|
|
+ modalEl.value.addEventListener("hide.bs.modal", props.hideEvent, true)
|
|
|
+ }
|
|
|
+ if (props.hiddenEvent) {
|
|
|
+ modalEl.value.addEventListener("hidden.bs.modal", props.hiddenEvent, true)
|
|
|
+ }
|
|
|
+ if (props.showEvent) {
|
|
|
+ modalEl.value.addEventListener("show.bs.modal", props.showEvent, true)
|
|
|
+ }
|
|
|
+ if (props.shownEvent) {
|
|
|
+ modalEl.value.addEventListener("shown.bs.modal", props.shownEvent, true)
|
|
|
+ }
|
|
|
+ if (props.hidePreventedEvent) {
|
|
|
+ modalEl.value.addEventListener("hidePrevented.bs.modal", props.hidePreventedEvent, true)
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+watch(modalEl.value, init)
|
|
|
+watch(
|
|
|
+ () => props.id,
|
|
|
+ () => init
|
|
|
+)
|
|
|
+
|
|
|
+onMounted(() => {
|
|
|
+ init()
|
|
|
+})
|
|
|
+defineExpose({ show })
|
|
|
+</script>
|
|
|
+<template>
|
|
|
+ <div ref="modalEl" class="modal modal-lg fade" :class="modalClass" :style="modalStyle" tabindex="-1" :id="id">
|
|
|
+ <div class="modal-dialog" :class="modalDialogClass" :style="modalDialogStyle">
|
|
|
+ <div class="modal-content" :class="modalContentClass" :style="modalContentStyle">
|
|
|
+ <div class="modal-header py-3" :class="modalHeaderClass" :style="modalHeaderStyle">
|
|
|
+ <template v-if="$slots.title">
|
|
|
+ <slot name="title" />
|
|
|
+ </template>
|
|
|
+ <h4 v-else class="modal-title" :class="modalTitleClass" :style="modalTitleStyle">
|
|
|
+ <span class="fs-2 me-5">{{ `${titlePrefix}${title}` }}</span>
|
|
|
+ </h4>
|
|
|
+ <template v-if="$slots.closeBtn">
|
|
|
+ <slot name="closeBtn" />
|
|
|
+ </template>
|
|
|
+ <div v-else class="btn btn-icon btn-sm btn-active-light-primary ms-2" @click="cancel">
|
|
|
+ <span class="svg-icon svg-icon-2x">
|
|
|
+ <inline-svg :src="getAssetPath('media/icons/duotune/arrows/arr011.svg')" />
|
|
|
+ </span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div class="modal-body py-5" :class="modalBodyClass" :style="modalBodyStyle">
|
|
|
+ <template v-if="$slots.body">
|
|
|
+ <slot name="body" />
|
|
|
+ </template>
|
|
|
+ <el-form
|
|
|
+ v-else
|
|
|
+ ref="modalFormEl"
|
|
|
+ :model="modalFormData"
|
|
|
+ :label-width="`${Number(formLabelWidth) ? formLabelWidth + 'px' : formLabelWidth}`"
|
|
|
+ :size="formSize"
|
|
|
+ :class="modalFormClass"
|
|
|
+ :style="modalFormStyle"
|
|
|
+ >
|
|
|
+ <template v-if="$slots.form">
|
|
|
+ <slot name="form" />
|
|
|
+ </template>
|
|
|
+ </el-form>
|
|
|
+ </div>
|
|
|
+ <div class="modal-footer" :class="modalFooterClass" :style="modalFooterStyle">
|
|
|
+ <button v-if="closeBtn" type="button" :class="closeBtnClass" :style="closeBtnStyle" @click="cancel">
|
|
|
+ {{ closeBtnText }}
|
|
|
+ </button>
|
|
|
+ <button v-if="confirmBtn" type="button" :class="confirmBtnClass" :style="confirmBtnStyle" @click="confirm">
|
|
|
+ {{ confirmBtnText }}
|
|
|
+ </button>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+</template>
|