最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

vue使用h函數(shù)封裝dialog組件(以命令的形式使用dialog組件)

 更新時間:2026年01月13日 09:25:44   作者:南風(fēng)晚來晚相識  
文章介紹了如何將彈窗封裝成命令式形式,并解決了彈窗中的表單不能正常展示的問題,通過重新創(chuàng)建一個新的Vue應(yīng)用實例并注冊所需的組件,解決了無法解析組件的警告,感興趣的朋友跟隨小編一起看看吧

場景

有些時候我們的頁面是有很多的彈窗
如果我們把這些彈窗都寫html中會有一大坨
因此:我們需要把彈窗封裝成命令式的形式

命令式彈窗

// 使用彈窗的組件
<template>
  <div>
    <el-button @click="openMask">點擊彈窗</el-button>
  </div>
</template>
<script setup lang="ts">
import childTest from '@/components/childTest.vue'
import { renderDialog } from '@/hooks/dialog'
function openMask(){
  // 第1個參數(shù):表示的是組件,你寫彈窗中的組件
  // 第2個參數(shù):表示的組件屬性,比如:確認(rèn)按鈕的名稱等
  // 第3個參數(shù):表示的模態(tài)框的屬性。比如:模態(tài)寬的寬度,標(biāo)題名稱,是否可移動
  renderDialog(childTest,{},{title:'測試彈窗'})
}
</script>
// 封裝的彈窗
import { createApp, h } from "vue";
import { ElDialog } from "element-plus";
export function renderDialog(component:any,props:any, modalProps:any){
 const dialog  = h(
    ElDialog,   // 模態(tài)框組件
    {
      ...modalProps, // 模態(tài)框?qū)傩?
      modelValue:true, // 模態(tài)框是否顯示
    }, // 因為是模態(tài)框組件,肯定是模態(tài)框的屬性
    {
      default:()=>h(component, props ) // 插槽,el-dialog下的內(nèi)容
    }
  )
 console.log(dialog)
  // 創(chuàng)建一個新的 Vue 應(yīng)用實例。這個應(yīng)用實例是獨立的,與主應(yīng)用分離。
  const app = createApp(dialog)
  const div = document.createElement('div')
  document.body.appendChild(div)
  app.mount(div)
}
//childTest.vue 組件
<template>
  <div>
    <span>It's a modal Dialog</span>
    <el-form :model="form" label-width="auto" style="max-width: 600px">
    <el-form-item label="Activity name">
      <el-input v-model="form.name" />
    </el-form-item>
    <el-form-item label="Activity zone">
      <el-select v-model="form.region" placeholder="please select your zone">
        <el-option label="Zone one" value="shanghai" />
        <el-option label="Zone two" value="beijing" />
      </el-select>
    </el-form-item>
  </el-form>
  </div>
</template>
<script setup lang="ts">
import { ref,reactive } from 'vue'
const dialogVisible = ref(true)
const form = reactive({
  name: '',
  region: '',
})
const onSubmit = () => {
  console.log('submit!')
}
</script>

為啥彈窗中的表單不能夠正常展示呢?

在控制臺會有下面的提示信息:
Failed to resolve component:
el-form If this is a native custom element,
make sure to exclude it from component resolution via compilerOptions.isCustomElement
翻譯過來就是
無法解析組件:el-form如果這是一個原生自定義元素,
請確保通過 compilerOptions.isCustomElement 將其從組件解析中排除

其實就是說:我重新創(chuàng)建了一個新的app,這個app中沒有注冊組件。
因此會警告,頁面渲染不出來。

// 我重新創(chuàng)建了一個app,這個app中沒有注冊 element-plus 組件。
const app = createApp(dialog)

現(xiàn)在我們重新注冊element-plus組件。
準(zhǔn)確的說:我們要注冊 childTest.vue 組件使用到的東西

給新創(chuàng)建的app應(yīng)用注冊childTest組件使用到的東西

我們將會在這個命令式彈窗中重新注冊需要使用到的組件

// 封裝的彈窗
import { createApp, h } from "vue";
import { ElDialog } from "element-plus";
// 引入組件和樣式
import ElementPlus from "element-plus";
// import "element-plus/dist/index.css";
export function renderDialog(component:any,props:any, modalProps:any){
 const dialog  = h(
    ElDialog,   // 模態(tài)框組件
    {
      ...modalProps, // 模態(tài)框?qū)傩?
      modelValue:true, // 模態(tài)框顯示
    }, // 因為是模態(tài)框組件,肯定是模態(tài)框的屬性
    {
      default:()=>h(component, props ) // 插槽,el-dialog下的內(nèi)容
    }
  )
 console.log(dialog)
  // 創(chuàng)建一個新的 Vue 應(yīng)用實例。這個應(yīng)用實例是獨立的,與主應(yīng)用分離。
  const app = createApp(dialog)
  // 在新實例中注冊 Element Plus, 這彈窗中的組件就可以正常顯示了
  app.use(ElementPlus);
  const div = document.createElement('div')
  document.body.appendChild(div)
  app.mount(div)
}

現(xiàn)在我們發(fā)現(xiàn)可以正常展示彈窗中的表單了。因為我們注冊了element-plus組件。
但是我們發(fā)現(xiàn)又發(fā)現(xiàn)了另外一個問題。
彈窗底部沒有取消和確認(rèn)按鈕。
需要我們再次通過h函數(shù)來創(chuàng)建

關(guān)于使用createApp創(chuàng)建新的應(yīng)用實例

在Vue 3中,我們可以使用 createApp 來創(chuàng)建新的應(yīng)用實例
但是這樣會創(chuàng)建一個完全獨立的應(yīng)用
它不會共享主應(yīng)用的組件、插件等。
因此我們需要重新注冊

彈窗底部新增取消和確認(rèn)按鈕

我們將會使用h函數(shù)中的插槽來創(chuàng)建底部的取消按鈕

// 封裝的彈窗
import { createApp, h } from "vue";
import { ElDialog, ElButton, ElForm, ElFormItem, ElInput, ElSelect, ElOption } from "element-plus";
import ElementPlus from "element-plus";
export function renderDialog(component: any, props: any, modalProps: any) {
  // 創(chuàng)建彈窗實例
  const dialog = h(
    ElDialog,
    {
      ...modalProps,
      modelValue: true,
    },
    {
      // 主要內(nèi)容插槽
      default: () => h(component, props),
      // 底部插槽
      footer:() =>h(
        'div',
        { class: 'dialog-footer' },
        [
          h(
            ElButton, 
            {
              onClick: () => {
                console.log('取消')
              }
            },
            () => '取消'
          ),
          h(
            ElButton,
            { 
              type: 'primary',
              onClick: () => {
                console.log('確定')
              }
            },
            () => '確定'
          )
        ]
      )
    }
  );
  // 創(chuàng)建一個新的 Vue 應(yīng)用實例。這個應(yīng)用實例是獨立的,與主應(yīng)用分離。
  const app = createApp(dialog)
  // 在新實例中注冊 Element Plus, 這彈窗中的組件就可以正常顯示了
  app.use(ElementPlus);
  const div = document.createElement('div')
  document.body.appendChild(div)
  app.mount(div)
}

點擊關(guān)閉彈窗時,需要移除之前創(chuàng)建的div

卸載的同時需要把我們創(chuàng)建的div元素移除,否則頁面上會出現(xiàn)很多div。
2個地方需要移除:1,點擊確認(rèn)按鈕。 2,點擊其他地方的關(guān)閉

關(guān)閉彈窗正確銷毀相關(guān)組件

// 封裝的彈窗
import { createApp, h } from "vue";
import { ElDialog, ElButton, ElForm, ElFormItem, ElInput, ElSelect, ElOption } from "element-plus";
import ElementPlus from "element-plus";
export function renderDialog(component: any, props: any, modalProps: any) {
  console.log('111')
  // 創(chuàng)建彈窗實例
  const dialog = h(
    ElDialog,
    {
      ...modalProps,
      modelValue: true,
      onClose: ()=> {
        console.log('關(guān)閉的回調(diào)')
        app.unmount() // 這樣卸載會讓動畫消失
        // 卸載的同時需要把我們創(chuàng)建的div元素移除,否則頁面上會出現(xiàn)很多div
        document.body.removeChild(div)
      }
    },
    {
      // 主要內(nèi)容插槽
      default: () => h(component, props),
      // 底部插槽
      footer:() =>h(
        'div',
        { 
          class: 'dialog-footer',
        },
        [
          h(
            ElButton, 
            {
              onClick: () => {
                console.log('點擊取消按鈕')
                // 卸載一個已掛載的應(yīng)用實例。卸載一個應(yīng)用會觸發(fā)該應(yīng)用組件樹內(nèi)所有組件的卸載生命周期鉤子。
                app.unmount() // 這樣卸載會讓動畫消失
                // 卸載的同時需要把我們創(chuàng)建的div元素移除,否則頁面上會出現(xiàn)很多div
                document.body.removeChild(div)
              }
            },
            () => '取消'
          ),
          h(
            ElButton,
            { 
              type: 'primary',
              onClick: () => {
                console.log('確定')
              }
            },
            () => '確定'
          )
        ]
      )
    }
  );
  // 創(chuàng)建一個新的 Vue 應(yīng)用實例。這個應(yīng)用實例是獨立的,與主應(yīng)用分離。
  const app = createApp(dialog)
  // 在新實例中注冊 Element Plus, 這彈窗中的組件就可以正常顯示了
  app.use(ElementPlus);
  // 這個div元素在在銷毀應(yīng)用時需要被移除哈
  const div = document.createElement('div')
  document.body.appendChild(div)
  app.mount(div)
}

點擊確認(rèn)按鈕時驗證規(guī)則

有些時候,我們彈窗中的表單是需要進(jìn)行規(guī)則校驗的。
我們下面來實現(xiàn)這個功能點
傳遞的組件

<template>
  <el-form
    ref="ruleFormRef"
    style="max-width: 600px"
    :model="ruleForm"
    :rules="rules"
    label-width="auto"
  >
    <el-form-item label="Activity name" prop="name">
      <el-input v-model="ruleForm.name" />
    </el-form-item>
    <el-form-item label="Activity zone" prop="region">
      <el-select v-model="ruleForm.region" placeholder="Activity zone">
        <el-option label="Zone one" value="shanghai" />
        <el-option label="Zone two" value="beijing" />
      </el-select>
    </el-form-item>
    <el-form-item label="Activity time" required>
      <el-col :span="11">
        <el-form-item prop="date1">
          <el-date-picker
            v-model="ruleForm.date1"
            type="date"
            aria-label="Pick a date"
            placeholder="Pick a date"
            style="width: 100%"
          />
        </el-form-item>
      </el-col>
      <el-col class="text-center" :span="2">
        <span class="text-gray-500">-</span>
      </el-col>
      <el-col :span="11">
        <el-form-item prop="date2">
          <el-time-picker
            v-model="ruleForm.date2"
            aria-label="Pick a time"
            placeholder="Pick a time"
            style="width: 100%"
          />
        </el-form-item>
      </el-col>
    </el-form-item>
    <el-form-item label="Resources" prop="resource">
      <el-radio-group v-model="ruleForm.resource">
        <el-radio value="Sponsorship">Sponsorship</el-radio>
        <el-radio value="Venue">Venue</el-radio>
      </el-radio-group>
    </el-form-item>
    <el-form-item label="Activity form" prop="desc">
      <el-input v-model="ruleForm.desc" type="textarea" />
    </el-form-item>
  </el-form>
</template>
<script lang="ts" setup>
import { reactive, ref } from 'vue'
import type { FormInstance, FormRules } from 'element-plus'
interface RuleForm {
  name: string
  region: string
  date1: string
  date2: string
  resource: string
  desc: string
}
const ruleFormRef = ref<FormInstance>()
const ruleForm = reactive<RuleForm>({
  name: 'Hello',
  region: '',
  date1: '',
  date2: '',
  resource: '',
  desc: '',
})
const rules = reactive<FormRules<RuleForm>>({
  name: [
    { required: true, message: 'Please input Activity name', trigger: 'blur' },
    { min: 3, max: 5, message: 'Length should be 3 to 5', trigger: 'blur' },
  ],
  region: [
    {
      required: true,
      message: 'Please select Activity zone',
      trigger: 'change',
    },
  ],
  date1: [
    {
      type: 'date',
      required: true,
      message: 'Please pick a date',
      trigger: 'change',
    },
  ],
  date2: [
    {
      type: 'date',
      required: true,
      message: 'Please pick a time',
      trigger: 'change',
    },
  ],
  resource: [
    {
      required: true,
      message: 'Please select activity resource',
      trigger: 'change',
    },
  ],
  desc: [
    { required: true, message: 'Please input activity form', trigger: 'blur' },
  ],
})
const submitForm = async () => {
  if (!ruleFormRef.value) {
    console.error('ruleFormRef is not initialized')
    return false
  }
  try {
    const valid = await ruleFormRef.value.validate()
    if (valid) {
      console.log('表單校驗通過', ruleForm)
      return Promise.resolve(ruleForm)
    }
  } catch (error) {
    // 為啥submitForm中,valid的值是false會執(zhí)行catch ?
    // el-form 組件的 validate 方法的工作機制導(dǎo)致的。 validate 方法在表單驗證失敗時會拋出異常
    console.error('err', error)
    return false
    /**
     * 下面這樣寫為啥界面會報錯呢?
     * return Promise.reject(error)
     * 當(dāng)表單驗證失敗時,ruleFormRef.value.validate() 會拋出一個異常。
     * 雖然你用了 try...catch 捕獲這個異常,并且在 catch 塊中通過 return Promise.reject(error) 返回了一個被拒絕的 Promise
     * 但如果調(diào)用 submitForm 的地方?jīng)]有正確地處理這個被拒絕的 Promise(即沒有使用 .catch() 或者 await 來接收錯誤),
     * 那么瀏覽器控制臺就會顯示一個 "Uncaught (in promise)" 錯誤。
     * 在 catch 中再次 return Promise.reject(error) 是多余的, 直接return false
     * */ 
    /**
     * 如果你這樣寫
     * throw error 直接拋出錯誤即可
     * 那么就需要再調(diào)用submitForm的地方捕獲異常
     * */  
  }
}
defineExpose({
  submitForm:submitForm
})
</script>
// 封裝的彈窗
import { createApp, h, ref } from "vue";
import { ElDialog, ElButton, ElForm, ElFormItem, ElInput, ElSelect, ElOption } from "element-plus";
import ElementPlus from "element-plus";
export function renderDialog(component: any, props: any, modalProps: any) {
  const instanceElement = ref()
  console.log('111', instanceElement) 
  // 創(chuàng)建彈窗實例
  const dialog = h(
    ElDialog,
    {
      ...modalProps,
      modelValue: true,
      onClose: ()=> {
        console.log('關(guān)閉的回調(diào)')
        app.unmount() // 這樣卸載會讓動畫消失
        // 卸載的同時需要把我們創(chuàng)建的div元素移除,否則頁面上會出現(xiàn)很多div
        document.body.removeChild(div)
      }
    },
    {
      // 主要內(nèi)容插槽,這里的ref必須接收一個ref
      default: () => h(component, {...props, ref: instanceElement}),
      // 底部插槽
      footer:() =>h(
        'div',
        { 
          class: 'dialog-footer',
        },
        [
          h(
            ElButton, 
            {
              onClick: () => {
                console.log('點擊取消按鈕')
                // 卸載一個已掛載的應(yīng)用實例。卸載一個應(yīng)用會觸發(fā)該應(yīng)用組件樹內(nèi)所有組件的卸載生命周期鉤子。
                app.unmount() // 這樣卸載會讓動畫消失
                // 卸載的同時需要把我們創(chuàng)建的div元素移除,否則頁面上會出現(xiàn)很多div
                document.body.removeChild(div)
              }
            },
            () => '取消'
          ),
          h(
            ElButton,
            { 
              type: 'primary',
              onClick: () => {
                instanceElement?.value?.submitForm().then((res:any) =>{
                  console.log('得到的值',res)
                })
                console.log('確定')
              }
            },
            () => '確定'
          )
        ]
      )
    }
  );
  // 創(chuàng)建一個新的 Vue 應(yīng)用實例。這個應(yīng)用實例是獨立的,與主應(yīng)用分離。
  const app = createApp(dialog)
  // 在新實例中注冊 Element Plus, 這彈窗中的組件就可以正常顯示了
  app.use(ElementPlus);
  // 這個div元素在在銷毀應(yīng)用時需要被移除哈
  const div = document.createElement('div')
  document.body.appendChild(div)
  app.mount(div)
}

關(guān)鍵的點:通過ref拿到childTest組件中的方法,childTest要暴露需要的方法

如何把表單中的數(shù)據(jù)暴露出去

可以通過回調(diào)函數(shù)的方式把數(shù)據(jù)暴露出去哈。

// 封裝的彈窗
import { createApp, h, ref } from "vue";
import { ElDialog, ElButton, ElForm, ElFormItem, ElInput, ElSelect, ElOption } from "element-plus";
import ElementPlus from "element-plus";
export function renderDialog(component: any, props: any, modalProps: any, onConfirm: (data: any) => any ) {
  // 第4個參數(shù)是回調(diào)函數(shù)
  const instanceElement = ref()
  console.log('111', instanceElement) 
  // 創(chuàng)建彈窗實例
  const dialog = h(
    ElDialog,
    {
      ...modalProps,
      modelValue: true,
      onClose: ()=> {
        console.log('關(guān)閉的回調(diào)')
        app.unmount() // 這樣卸載會讓動畫消失
        // 卸載的同時需要把我們創(chuàng)建的div元素移除,否則頁面上會出現(xiàn)很多div
        document.body.removeChild(div)
      }
    },
    {
      // 主要內(nèi)容插槽,這里的ref必須接收一個ref
      default: () => h(component, {...props, ref: instanceElement}),
      // 底部插槽
      footer:() =>h(
        'div',
        { 
          class: 'dialog-footer',
        },
        [
          h(
            ElButton, 
            {
              onClick: () => {
                console.log('點擊取消按鈕')
                // 卸載一個已掛載的應(yīng)用實例。卸載一個應(yīng)用會觸發(fā)該應(yīng)用組件樹內(nèi)所有組件的卸載生命周期鉤子。
                app.unmount() // 這樣卸載會讓動畫消失
                // 卸載的同時需要把我們創(chuàng)建的div元素移除,否則頁面上會出現(xiàn)很多div
                document.body.removeChild(div)
              }
            },
            () => '取消'
          ),
          h(
            ElButton,
            { 
              type: 'primary',
              onClick: () => {
                // submitForm 調(diào)用表單組件中需要驗證或者暴露出去的數(shù)據(jù)
                instanceElement?.value?.submitForm().then((res:any) =>{
                  console.log('得到的值',res)
                  // 驗證通過后調(diào)用回調(diào)函數(shù)傳遞數(shù)據(jù), 如驗證失敗,res 的值有可能是一個false。
                  onConfirm(res)
                  // 怎么把這個事件傳遞出去,讓使用的時候知道點擊了確認(rèn)并且知道驗證通過了
                }).catch((error: any) => {
                  // 驗證失敗時也可以傳遞錯誤信息
                  console.log('驗證失敗', error)
                })
                console.log('確定')
              }
            },
            () => '確定'
          )
        ]
      )
    }
  );
  // 創(chuàng)建一個新的 Vue 應(yīng)用實例。這個應(yīng)用實例是獨立的,與主應(yīng)用分離。
  const app = createApp(dialog)
  // 在新實例中注冊 Element Plus, 這彈窗中的組件就可以正常顯示了
  app.use(ElementPlus);
  // 這個div元素在在銷毀應(yīng)用時需要被移除哈
  const div = document.createElement('div')
  document.body.appendChild(div)
  app.mount(div)
}
<template>
  <div>
    <el-button @click="openMask">點擊彈窗</el-button>
  </div>
</template>
<script setup lang="ts">
import childTest from '@/components/childTest.vue'
import { renderDialog } from '@/hooks/dialog'
import { getCurrentInstance } from 'vue';
const currentInstance = getCurrentInstance();
function openMask(){
  console.log('currentInstance',currentInstance)
  renderDialog(childTest,{},{title:'測試彈窗', width: '700'}, (res)=>{
    console.log('通過回調(diào)函數(shù)返回值', res)
  })
}
</script>

點擊確定時,業(yè)務(wù)完成后關(guān)閉彈窗

現(xiàn)在想要點擊確定,等業(yè)務(wù)處理完成之后,才關(guān)閉彈窗。
需要在使用完成業(yè)務(wù)的時候返回一個promise,讓封裝的彈窗調(diào)用這個promise
這樣就可以知道什么時候關(guān)閉彈窗了

// 封裝的彈窗
import { createApp, h, ref } from "vue";
import { ElDialog, ElButton, ElForm, ElFormItem, ElInput, ElSelect, ElOption } from "element-plus";
import ElementPlus from "element-plus";
export function renderDialog(component: any, props: any, modalProps: any, onConfirm: (data: any) => any ) {
  // 第4個參數(shù)是回調(diào)函數(shù)
  const instanceElement = ref()
  console.log('111', instanceElement) 
  // 創(chuàng)建彈窗實例
  const dialog = h(
    ElDialog,
    {
      ...modalProps,
      modelValue: true,
      onClose: ()=> {
        console.log('關(guān)閉的回調(diào)')
        app.unmount() // 這樣卸載會讓動畫消失
        // 卸載的同時需要把我們創(chuàng)建的div元素移除,否則頁面上會出現(xiàn)很多div
        document.body.removeChild(div)
      }
    },
    {
      // 主要內(nèi)容插槽,這里的ref必須接收一個ref
      default: () => h(component, {...props, ref: instanceElement}),
      // 底部插槽
      footer:() =>h(
        'div',
        { 
          class: 'dialog-footer',
        },
        [
          h(
            ElButton, 
            {
              onClick: () => {
                console.log('點擊取消按鈕')
                // 卸載一個已掛載的應(yīng)用實例。卸載一個應(yīng)用會觸發(fā)該應(yīng)用組件樹內(nèi)所有組件的卸載生命周期鉤子。
                app.unmount() // 這樣卸載會讓動畫消失
                // 卸載的同時需要把我們創(chuàng)建的div元素移除,否則頁面上會出現(xiàn)很多div
                document.body.removeChild(div)
              }
            },
            () => '取消'
          ),
          h(
            ElButton,
            { 
              type: 'primary',
              onClick: () => {
                // submitForm 調(diào)用表單組件中需要驗證或者暴露出去的數(shù)據(jù)
                instanceElement?.value?.submitForm().then((res:any) =>{
                  console.log('得到的值',res)
                  // 驗證通過后調(diào)用回調(diào)函數(shù)傳遞數(shù)據(jù),如驗證失敗,res 的值有可能是一個false。
                  const callbackResult = onConfirm(res);
                  // 如果回調(diào)函數(shù)返回的是 Promise,則等待業(yè)務(wù)完成后再關(guān)閉彈窗
                  if (callbackResult instanceof Promise) {
                    // 注意這里的finally,這樣寫在服務(wù)出現(xiàn)異常的時候會有問題,這里是有問題的,需要優(yōu)化
                    // 注意這里的finally,這樣寫在服務(wù)出現(xiàn)異常的時候會有問題,這里是有問題的,需要優(yōu)化
                    callbackResult.finally(() => { 
                      // 彈窗關(guān)閉邏輯
                      app.unmount()
                      document.body.removeChild(div)
                    });
                  } else {
                    // 如果不是 Promise,立即關(guān)閉彈窗
                    app.unmount()
                    document.body.removeChild(div)
                  }
                }).catch((error: any) => {
                  // 驗證失敗時也可以傳遞錯誤信息
                  console.log('驗證失敗', error)
                })
              }
            },
            () => '確定'
          )
        ]
      )
    }
  );
  // 創(chuàng)建一個新的 Vue 應(yīng)用實例。這個應(yīng)用實例是獨立的,與主應(yīng)用分離。
  const app = createApp(dialog)
  // 在新實例中注冊 Element Plus, 這彈窗中的組件就可以正常顯示了
  app.use(ElementPlus);
  // 這個div元素在在銷毀應(yīng)用時需要被移除哈
  const div = document.createElement('div')
  document.body.appendChild(div)
  app.mount(div)
}
<template>
  <div>
    <el-button @click="openMask">點擊彈窗</el-button>
  </div>
</template>
<script setup lang="ts">
import childTest from '@/components/childTest.vue'
import { renderDialog } from '@/hooks/dialog'
import { getCurrentInstance } from 'vue';
const currentInstance = getCurrentInstance();
function openMask(){
  console.log('currentInstance',currentInstance)
  renderDialog(childTest,{},{title:'測試彈窗', width: '700'}, (res)=>{
    console.log('通過回調(diào)函數(shù)返回值', res)
    // 這里返回一個promise對象,這樣就可以讓業(yè)務(wù)完成后才關(guān)閉彈窗
    return fetch("https://dog.ceo/api/breed/pembroke/images/random")
     .then((res) => {
       return res.json();
     })
     .then((res) => {
        console.log('獲取的圖片地址為:', res.message);
     });
  })
}
</script>

優(yōu)化業(yè)務(wù)組件

// 封裝的彈窗
import { createApp, h, ref } from "vue";
import { ElDialog, ElButton, ElForm, ElFormItem, ElInput, ElSelect, ElOption } from "element-plus";
import ElementPlus from "element-plus";
export function renderDialog(component: any, props: any, modalProps: any, onConfirm: (data: any) => any ) {
  // 關(guān)閉彈窗,避免重復(fù)代碼
  const closeDialog = () => {
    // 成功時關(guān)閉彈窗
    app.unmount();
    // 檢查div是否仍然存在且為body的子元素,否者可能出現(xiàn)異常
    if (div && div.parentNode) {
      document.body.removeChild(div)
    }
  }
  // 第4個參數(shù)是回調(diào)函數(shù)
  const instanceElement = ref()
  console.log('111', instanceElement) 
  // 創(chuàng)建彈窗實例
  const dialog = h(
    ElDialog,
    {
      ...modalProps,
      modelValue: true,
      onClose: ()=> {
        console.log('關(guān)閉的回調(diào)')
        app.unmount() // 這樣卸載會讓動畫消失
        // 卸載的同時需要把我們創(chuàng)建的div元素移除,否則頁面上會出現(xiàn)很多div
        document.body.removeChild(div)
      }
    },
    {
      // 主要內(nèi)容插槽,這里的ref必須接收一個ref
      default: () => h(component, {...props, ref: instanceElement}),
      // 底部插槽
      footer:() =>h(
        'div',
        { 
          class: 'dialog-footer',
        },
        [
          h(
            ElButton, 
            {
              onClick: () => {
                console.log('點擊取消按鈕')
                // 卸載一個已掛載的應(yīng)用實例。卸載一個應(yīng)用會觸發(fā)該應(yīng)用組件樹內(nèi)所有組件的卸載生命周期鉤子。
                app.unmount() // 這樣卸載會讓動畫消失
                // 卸載的同時需要把我們創(chuàng)建的div元素移除,否則頁面上會出現(xiàn)很多div
                document.body.removeChild(div)
              }
            },
            () => '取消'
          ),
          h(
            ElButton,
            { 
              type: 'primary',
              onClick: () => {
                // submitForm 調(diào)用表單組件中需要驗證或者暴露出去的數(shù)據(jù)
                instanceElement?.value?.submitForm().then((res:any) =>{
                  console.log('得到的值',res)
                  // 驗證通過后調(diào)用回調(diào)函數(shù)傳遞數(shù)據(jù),如驗證失敗,res 的值有可能是一個false。
                  const callbackResult = onConfirm(res);
                  // 如果回調(diào)函數(shù)返回的是 Promise,則等待業(yè)務(wù)完成后再關(guān)閉彈窗
                  if (callbackResult instanceof Promise) {
                     callbackResult.then(() => {
                      if(res){
                        console.log('111')
                        closeDialog()
                      }
                    }).catch(error=>{
                      console.log('222')
                      console.error('回調(diào)函數(shù)執(zhí)行出錯,如:網(wǎng)絡(luò)錯誤', error);
                      // 錯誤情況下也關(guān)閉彈窗
                      closeDialog()
                    });
                  } else {
                    // 如果不是 Promise,并且驗證時通過了的。立即關(guān)閉彈窗
                    console.log('333', res)
                    if(res){
                      closeDialog()
                    }
                  }
                }).catch((error: any) => {
                  console.log('44444')
                  // 驗證失敗時也可以傳遞錯誤信息
                  console.log('驗證失敗', error)
                })
              }
            },
            () => '確定'
          )
        ]
      )
    }
  );
  // 創(chuàng)建一個新的 Vue 應(yīng)用實例。這個應(yīng)用實例是獨立的,與主應(yīng)用分離。
  const app = createApp(dialog)
  // 在新實例中注冊 Element Plus, 這彈窗中的組件就可以正常顯示了
  app.use(ElementPlus);
  // 這個div元素在在銷毀應(yīng)用時需要被移除哈
  const div = document.createElement('div')
  document.body.appendChild(div)
  app.mount(div)
}
<template>
  <div>
    <el-button @click="openMask">點擊彈窗</el-button>
  </div>
</template>
<script setup lang="ts">
import childTest from '@/components/childTest.vue'
import { renderDialog } from '@/hooks/dialog'
import { getCurrentInstance } from 'vue';
const currentInstance = getCurrentInstance();
function openMask(){
  console.log('currentInstance',currentInstance)
  renderDialog(childTest,{},{title:'測試彈窗', width: '700'}, (res)=>{
    console.log('通過回調(diào)函數(shù)返回值', res)
      // 這里返回一個promise對象,這樣就可以讓業(yè)務(wù)完成后才關(guān)閉彈窗
      return fetch("https://dog.ceo/api/breed/pembroke/images/random")
      .then((res) => {
        return res.json();
      })
      .then((res) => {
          console.log('獲取的圖片地址為:', res.message);
      });
  })
}
</script>

眼尖的小伙伴可能已經(jīng)發(fā)現(xiàn)了這一段代碼。
1,驗證不通過會也會觸發(fā)卸載彈窗
2,callbackResult.finally是不合適的
3.

最終的代碼

// 封裝的彈窗
import { createApp, h, ref } from "vue";
import { ElDialog, ElButton, ElForm, ElFormItem, ElInput, ElSelect, ElOption } from "element-plus";
import ElementPlus from "element-plus";
export function renderDialog(component: any, props: any, modalProps: any, onConfirm: (data: any) => any ) {
  // 關(guān)閉彈窗,避免重復(fù)代碼
  const closeDialog = () => {
    // 成功時關(guān)閉彈窗
    app.unmount();
    // 檢查div是否仍然存在且為body的子元素,否者可能出現(xiàn)異常
    if (div && div.parentNode) {
      document.body.removeChild(div)
    }
  }
  // 第4個參數(shù)是回調(diào)函數(shù)
  const instanceElement = ref()
  console.log('111', instanceElement) 
  const isLoading = ref(false)
  // 創(chuàng)建彈窗實例
  const dialog = h(
    ElDialog,
    {
      ...modalProps,
      modelValue: true,
      onClose: ()=> {
        isLoading.value = false
        console.log('關(guān)閉的回調(diào)')
        app.unmount() // 這樣卸載會讓動畫消失
        // 卸載的同時需要把我們創(chuàng)建的div元素移除,否則頁面上會出現(xiàn)很多div
        document.body.removeChild(div)
      }
    },
    {
      // 主要內(nèi)容插槽,這里的ref必須接收一個ref
      default: () => h(component, {...props, ref: instanceElement}),
      // 底部插槽,noShowFooterBool是true,不顯示; false的顯示底部 
      footer: props.noShowFooterBool ? null : () =>h(
        'div',
        { 
          class: 'dialog-footer',
        },
        [
          h(
            ElButton, 
            {
              onClick: () => {
                console.log('點擊取消按鈕')
                // 卸載一個已掛載的應(yīng)用實例。卸載一個應(yīng)用會觸發(fā)該應(yīng)用組件樹內(nèi)所有組件的卸載生命周期鉤子。
                app.unmount() // 這樣卸載會讓動畫消失
                // 卸載的同時需要把我們創(chuàng)建的div元素移除,否則頁面上會出現(xiàn)很多div
                document.body.removeChild(div)
              }
            },
            () => props.cancelText || '取消'
          ),
          h(
            ElButton,
            { 
              type: 'primary',
              loading: isLoading.value,
              onClick: () => {
                isLoading.value = true
                // submitForm 調(diào)用表單組件中需要驗證或者暴露出去的數(shù)據(jù)
                instanceElement?.value?.submitForm().then((res:any) =>{
                  if(!res){
                    isLoading.value = false
                  }
                  console.log('得到的值',res)
                  // 驗證通過后調(diào)用回調(diào)函數(shù)傳遞數(shù)據(jù),如驗證失敗,res 的值有可能是一個false。
                  const callbackResult = onConfirm(res);
                  // 如果回調(diào)函數(shù)返回的是 Promise,則等待業(yè)務(wù)完成后再關(guān)閉彈窗
                  if (callbackResult instanceof Promise) {
                     callbackResult.then(() => {
                      if(res){
                        console.log('111')
                        closeDialog()
                      }else{
                        isLoading.value = false
                      }
                    }).catch(error=>{
                      console.log('222')
                      console.error('回調(diào)函數(shù)執(zhí)行出錯,如:網(wǎng)絡(luò)錯誤', error);
                      // 錯誤情況下也關(guān)閉彈窗
                      closeDialog()
                    });
                  } else {
                    // 如果不是 Promise,并且驗證時通過了的。立即關(guān)閉彈窗
                    console.log('333', res)
                    if(res){
                      closeDialog()
                    }else{
                      isLoading.value = false
                    }
                  }
                }).catch((error: any) => {
                  console.log('44444')
                   isLoading.value = false
                  // 驗證失敗時也可以傳遞錯誤信息
                  console.log('驗證失敗', error)
                })
              }
            },
            () => props.confirmText ||  '確定'
          )
        ]
      ) 
    }
  );
  // 創(chuàng)建一個新的 Vue 應(yīng)用實例。這個應(yīng)用實例是獨立的,與主應(yīng)用分離。
  const app = createApp(dialog)
  // 在新實例中注冊 Element Plus, 這彈窗中的組件就可以正常顯示了
  app.use(ElementPlus);
  // 這個div元素在在銷毀應(yīng)用時需要被移除哈
  const div = document.createElement('div')
  document.body.appendChild(div)
  app.mount(div)
}
<template>
  <div>
    <el-button @click="openMask">點擊彈窗</el-button>
  </div>
</template>
<script setup lang="ts">
import childTest from '@/components/childTest.vue'
import { renderDialog } from '@/hooks/dialog'
import { getCurrentInstance } from 'vue';
const currentInstance = getCurrentInstance();
function openMask(){
  console.log('currentInstance',currentInstance)
  const otherProps =  {cancelText:'取消哈', confirmText: '確認(rèn)哈',showFooterBool:true }
  const dialogSetObject = {title:'測試彈窗哈', width: '700', draggable: true}
  renderDialog(childTest,otherProps,dialogSetObject, (res)=>{
    console.log('通過回調(diào)函數(shù)返回值', res)
    // 這里返回一個promise對象,這樣就可以讓業(yè)務(wù)完成后才關(guān)閉彈窗
    return fetch("https://dog.ceo/api/breed/pembroke/images/random")
    .then((res) => {
      return res.json();
    })
    .then((res) => {
        console.log('獲取的圖片地址為:', res.message);
    });
  })
}
</script>
<style lang="scss" scoped>
</style>
<template>
  <el-form
    ref="ruleFormRef"
    style="max-width: 600px"
    :model="ruleForm"
    :rules="rules"
    label-width="auto"
  >
    <el-form-item label="Activity name" prop="name">
      <el-input v-model="ruleForm.name" />
    </el-form-item>
    <el-form-item label="Activity zone" prop="region">
      <el-select v-model="ruleForm.region" placeholder="Activity zone">
        <el-option label="Zone one" value="shanghai" />
        <el-option label="Zone two" value="beijing" />
      </el-select>
    </el-form-item>
    <el-form-item label="Activity time" required>
      <el-col :span="11">
        <el-form-item prop="date1">
          <el-date-picker
            v-model="ruleForm.date1"
            type="date"
            aria-label="Pick a date"
            placeholder="Pick a date"
            style="width: 100%"
          />
        </el-form-item>
      </el-col>
      <el-col class="text-center" :span="2">
        <span class="text-gray-500">-</span>
      </el-col>
      <el-col :span="11">
        <el-form-item prop="date2">
          <el-time-picker
            v-model="ruleForm.date2"
            aria-label="Pick a time"
            placeholder="Pick a time"
            style="width: 100%"
          />
        </el-form-item>
      </el-col>
    </el-form-item>
    <el-form-item label="Resources" prop="resource">
      <el-radio-group v-model="ruleForm.resource">
        <el-radio value="Sponsorship">Sponsorship</el-radio>
        <el-radio value="Venue">Venue</el-radio>
      </el-radio-group>
    </el-form-item>
    <el-form-item label="Activity form" prop="desc">
      <el-input v-model="ruleForm.desc" type="textarea" />
    </el-form-item>
  </el-form>
</template>
<script lang="ts" setup>
import { reactive, ref } from 'vue'
import type { FormInstance, FormRules } from 'element-plus'
interface RuleForm {
  name: string
  region: string
  date1: string
  date2: string
  resource: string
  desc: string
}
const ruleFormRef = ref<FormInstance>()
const ruleForm = reactive<RuleForm>({
  name: 'Hello',
  region: '',
  date1: '',
  date2: '',
  resource: '',
  desc: '',
})
const rules = reactive<FormRules<RuleForm>>({
  name: [
    { required: true, message: 'Please input Activity name', trigger: 'blur' },
    { min: 3, max: 5, message: 'Length should be 3 to 5', trigger: 'blur' },
  ],
  region: [
    {
      required: true,
      message: 'Please select Activity zone',
      trigger: 'change',
    },
  ],
  date1: [
    {
      type: 'date',
      required: true,
      message: 'Please pick a date',
      trigger: 'change',
    },
  ],
  date2: [
    {
      type: 'date',
      required: true,
      message: 'Please pick a time',
      trigger: 'change',
    },
  ],
  resource: [
    {
      required: true,
      message: 'Please select activity resource',
      trigger: 'change',
    },
  ],
  desc: [
    { required: true, message: 'Please input activity form', trigger: 'blur' },
  ],
})
const submitForm = async () => {
  if (!ruleFormRef.value) {
    console.error('ruleFormRef is not initialized')
    return false
  }
  try {
    const valid = await ruleFormRef.value.validate()
    if (valid) {
      // 驗證通過后,就會可以把你需要的數(shù)據(jù)暴露出去
      return Promise.resolve(ruleForm)
    }
  } catch (error) {
    // 為啥submitForm中,valid的值是false會執(zhí)行catch ?
    // el-form 組件的 validate 方法的工作機制導(dǎo)致的。 validate 方法在表單驗證失敗時會拋出異常
    console.error('err', error)
    return false
    /**
     * 下面這樣寫為啥界面會報錯呢?
     * return Promise.reject(error)
     * 當(dāng)表單驗證失敗時,ruleFormRef.value.validate() 會拋出一個異常。
     * 雖然你用了 try...catch 捕獲這個異常,并且在 catch 塊中通過 return Promise.reject(error) 返回了一個被拒絕的 Promise
     * 但如果調(diào)用 submitForm 的地方?jīng)]有正確地處理這個被拒絕的 Promise(即沒有使用 .catch() 或者 await 來接收錯誤),
     * 那么瀏覽器控制臺就會顯示一個 "Uncaught (in promise)" 錯誤。
     * 在 catch 中再次 return Promise.reject(error) 是多余的, 直接return false
     * */ 
    /**
     * 如果你這樣寫
     * throw error 直接拋出錯誤即可
     * 那么就需要再調(diào)用submitForm的地方捕獲異常
     * */  
  }
}
defineExpose({
  submitForm:submitForm
})
</script>

到此這篇關(guān)于vue使用h函數(shù)封裝dialog組件(以命令的形式使用dialog組件)的文章就介紹到這了,更多相關(guān)vue h函數(shù)封裝dialog組件內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • vue addRoutes路由動態(tài)加載操作

    vue addRoutes路由動態(tài)加載操作

    這篇文章主要介紹了vue addRoutes路由動態(tài)加載操作,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2020-08-08
  • 詳解Vue源碼中一些util函數(shù)

    詳解Vue源碼中一些util函數(shù)

    這篇文章主要介紹了Vue源碼中一些util函數(shù),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-04-04
  • 使用 vue 實現(xiàn)滅霸打響指英雄消失的效果附demo

    使用 vue 實現(xiàn)滅霸打響指英雄消失的效果附demo

    這篇文章主要介紹了使用 vue 實現(xiàn)滅霸打響指英雄消失的效果 demo,需要的朋友可以參考下
    2019-05-05
  • 使用Vue純前端實現(xiàn)發(fā)送短信驗證碼并實現(xiàn)倒計時

    使用Vue純前端實現(xiàn)發(fā)送短信驗證碼并實現(xiàn)倒計時

    在實際的應(yīng)用開發(fā)中,涉及用戶登錄驗證、密碼重置等場景時,通常需要前端實現(xiàn)發(fā)送短信驗證碼的功能,以提升用戶體驗和安全性,以下是一個簡單的前端實現(xiàn),演示了如何在用戶點擊發(fā)送驗證碼按鈕時觸發(fā)短信驗證碼的發(fā)送,并開始一個倒計時
    2024-04-04
  • 使用vue實現(xiàn)加載頁

    使用vue實現(xiàn)加載頁

    這篇文章主要為大家詳細(xì)介紹了使用vue實現(xiàn)加載頁,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-07-07
  • 加快Vue項目的開發(fā)速度的方法

    加快Vue項目的開發(fā)速度的方法

    這篇文章主要介紹了加快Vue項目的開發(fā)速度的方法,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2018-12-12
  • vue頁面跳轉(zhuǎn)過渡動畫與防止抖動方式

    vue頁面跳轉(zhuǎn)過渡動畫與防止抖動方式

    這篇文章主要介紹了vue頁面跳轉(zhuǎn)過渡動畫與防止抖動方式,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2026-03-03
  • vue如何實現(xiàn)甘特圖

    vue如何實現(xiàn)甘特圖

    文章主要內(nèi)容是關(guān)于如何在項目中引入依賴項以及編寫組件代碼的步驟和總結(jié),作者分享了個人經(jīng)驗,旨在為讀者提供參考,并鼓勵大家支持腳本之家
    2024-12-12
  • vue中的計算屬性傳參

    vue中的計算屬性傳參

    這篇文章主要介紹了vue中的計算屬性傳參,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-08-08
  • vue配置electron使用electron-builder進(jìn)行打包的操作方法

    vue配置electron使用electron-builder進(jìn)行打包的操作方法

    這篇文章主要介紹了vue配置electron使用electron-builder進(jìn)行打包的操作方法,本文通過實例代碼給大家介紹的非常詳細(xì),感興趣的朋友跟隨小編一起看看吧
    2024-08-08

最新評論

海兴县| 台南县| 浑源县| 衡山县| 襄垣县| 广德县| 龙岩市| 元朗区| 满城县| 蒲江县| 衡东县| 庆元县| 辽阳县| 阜平县| 定结县| 包头市| 隆尧县| 曲松县| 鹤山市| 营山县| 东明县| 双江| 临城县| 桑日县| 揭西县| 靖宇县| 会宁县| 微博| 都安| 扶绥县| 十堰市| 红桥区| 中方县| 栾城县| 环江| 义乌市| 方正县| 曲阳县| 屯门区| 宁都县| 合川市|