用 Promise 异步化 ElDialog

一般 ElDialog 的写法:v-model 一个 boolean,通过事件回调监听对话框关闭

<el-dialog
    v-model="dialogVisible"
    title="Tips"
    width="30%"
    :before-close="handleClose"
  >
    <span>This is a messagespan>
    <template #footer>
      <span class="dialog-footer">
        <el-button @click="dialogVisible = false">Cancelel-button>
        <el-button type="primary" @click="dialogVisible = false">
          Confirm
        el-button>
      span>
    template>
  el-dialog>

这种写法的缺点是数据和事件分离,如果用户选择了某条数据,在对话框关闭时想对该条数据进行处理,则必须额外定义一个变量保存被选中的数据,并且在对话框关闭回调中添加额外的判断。

Promise + Ref

通过提取出 Promise 的 resolvereject 回调,再其它地方控制 Promise 回调的执行时机,即可达成异步显示对话框并获取到响应数据的目的:

const promises = {}

export function open() {
	return new Promise((resolve, reject) => {
	  promises.resolve = resolve
	  promises.reject = reject
	}).finally(() => {
  		promises.resolve = promises.reject = undefined
	})
}


// 在另外的方法中调用 resolve 和 reject ....
function dosth() {
	promises.resolve(1123)
}

// 调用 open 方法... 
oepn().then(data => {
	// 1123
	console.log(data)
})

useHook

用 hooks 封装上述核心方法,通过 ref.value 改变对话框的隐藏和显示:

/**
 * 传入对话框可见性 Ref,异步控制对话框的显示和隐藏,类似 ElMessageBox 的功能
 * 
 * Example:
 *  // 对话框组件
 *  import useAsyncDialog from '@/shared/hooks/useAsyncDialog'
 *  const visible = ref(false)
 *  const sendDate = ref()
 *
 *  const { asyncOpen, onConfirm, onCancel } = useAsyncDialog(visible)
 *
 *  // 确认的时候给对话框传参
 *  const handleConfirm = () => {
 *     onConfirm(sendDate.value)
 *  }
 *
 *  console.log({ asyncOpen, onConfirm, onCancel });
 *  defineExpose({
 *     asyncOpen
 *  })
 * 
 *  // 外部调用:
 *  const sendPartLineDialogRef = ref()
 *  const openPartLineDialog = () => {
 *    sendPartLineDialogRef.value.asyncOpen().then((date: Date) => {
 *      console.log('confirm', date)
 *    }).catch(() => {
 *      console.log('cancel')
 *    })
 *  }
 */


import type { Ref } from 'vue'


export default function (visibleRef: Ref<boolean>) {
  const promises: {
    resolve: null | ((param: any) => any)
    reject: null | ((param: any) => any)
  } = {
    resolve: null,
    reject: null
  }

  /**
   * 打开对话框
   */
  const asyncOpen = () => {
    return new Promise((resolve, reject) => {
      promises.resolve = resolve
      promises.reject = reject

      visibleRef.value = true
    })
  }

  const reset = () => {
    promises.reject = promises.resolve = null
    visibleRef.value = false
  }

  /**
   * 对话框确认回调
   * 
   * @param params 
   */
  const onConfirm = (params?: any) => {
    if (promises.resolve) {
      promises.resolve(params)
    }

    reset()
  }

  /**
   * 对话框取消回调
   * 
   * @param params 
   */
  const onCancel = (params?: any) => {
    if (promises.reject) {
      promises.reject(params)
    }

    reset()
  }

  return {
    asyncOpen,
    onConfirm,
    onCancel
  }
}

组件中调用:

<template>
  <el-dialog width="70%" v-model="visible" title="DIALOG">
    <template #footer>
      <el-button @click="onCancel">取消el-button>
      <el-button @click="onCancel">确定el-button>
    template>
  el-dialog>
template>

<script setup lang="tsx">
import useAsyncDialog from "@/shared/hooks/useAsyncDialog";

const visible = ref(false);

const { onCancel, asyncOpen } = useAsyncDialog(visible);

defineExpose({
  asyncOpen,
});
script>

<style scoped lang="scss">style>

你可能感兴趣的:(javascript,前端,vue.js)