vue3使用createVNode + render基于el-dialog封装二次确认弹框组件

背景:公司组件库之前是使用el-dialog进行二次封装来作为确认弹框组件,但是这样在调用时就会出现一个问题,例如下列模拟删除二次确认场景:

<template>
 <button @click="handleDelete('1')">点击删除</button>
 <pro-confirm
    :is-show="confirmVisible"
    @confirm="handleDelConfirm"
    content="正在进行删除操作,是否继续?"
  />
<template>

<script setup>
    import { ref } from 'vue'; 

    const currentId = ref();
    const confirmVisible= ref(false);

    function handleDelete(id) {
        confirmVisible.value = true;
        // 删除操作需要储存id
        currentId.value = id;
    }

    function handleDelConfirm() {
        // 调用删除接口传入id删除
        api({ id: currentId.value }).then(() => {
            // do something
        })
    }
    
</script>

可以看到上面二次确认操作和后续的删除调接口操作并不能在同一个执行上下文中执行,固还需存储要删除的id,使用起来比较麻烦,所以使用vue3提供的createVNode + render将二次确认弹框改为函数调用组件来使用,即可解决以上问题,封装代码如下:

import ProConfirmConstructor from './pro-confirm.vue';
import { render, createVNode } from 'vue';

const confirmPromiseInfo = {
	resolve: () => {},
	reject: () => {}
}


const Confirm = (props) => new Promise((resolve, reject) => {
	const _onClose = props.onClose
	const _onConfirm = props.onConfirm

	confirmPromiseInfo.resolve = resolve
	confirmPromiseInfo.reject = reject

	props.onClose = () => {
		_onClose && _onClose()
		confirmInstance.component?.exposed?.close()
		// render(null, document.body);
		confirmPromiseInfo.reject()
	}
	
	props.onConfirm = () => {
		_onConfirm && _onConfirm();
		// render(null, document.body);
		confirmInstance.component?.exposed?.close()
		confirmPromiseInfo.resolve();
	}

    // 此处通过createVNode将el-dialog封装的组件实例转换为虚拟dom节点
	const confirmInstance = createVNode(ProConfirmConstructor, props); 
    // 手动挂载组件实例至body
	render(confirmInstance, document.body);
    //执行组件中暴露的show方法展示组件
	confirmInstance.component?.exposed?.show();
})

// 用于组件库注册全局方法
Confirm.install = (app) => {
	app.config.globalProperties.$proConfirm = Confirm
};

export default Confirm;

附上基于el-dialog封装的弹窗组件实例代码 :

// 基于el-dialog封装的弹窗组件实例代码
<template>
  <div>
    <el-dialog
      v-bind="_options"
      title=""
      v-model="dialogVisible"
      :before-close="handleClose"
    >
      <div class="component-confirm-content">
        <slot name="icon">
          <img :src="iconMap[props.type].url" alt="" class="icon-sty" />
        </slot>
        <div class="component-confirm-text">
          <div v-if="title" class="title-sty">{{ title }}</div>
          <slot>
            <div :class="title ? 'sec-sty' : 'fir-sty'">
              {{ content }}
            </div>
          </slot>
        </div>
      </div>
      <template #footer>
        <slot name="footer">
          <pro-button v-if="isShowCancel" @click="handleClose">{{
            cancelName
          }}</pro-button>
          <pro-button
            type="primary"
            @click="handleConfirm"
            style="margin-left: 10px"
          >
            {{ confirmName }}
          </pro-button>
        </slot>
      </template>
    </el-dialog>
  </div>
</template>

<script setup lang="jsx" name="ProConfirm">
import { ref, computed, watch } from "vue";
import { ProButton } from "../../index";
const props = defineProps({
  isShow: {
    type: Boolean, // 弹窗是否展示
    default: false,
  },
  isShowCancel: {
    type: Boolean, // 是否显示取消按钮
    default: true,
  },
  content: {
    type: String, // 提示内容
    default: "",
  },
  title: {
    type: String, // 提示标题
    default: "",
  },
  type: {
    type: String, // icon类型
    default: "warn",
  },
  padding: {
    type: String, // 内容的padding
    default: "50px 28px 22px",
  },
  confirmName: {
    type: String, // 确认按钮名称
    default: "确定",
  },
  cancelName: {
    type: String, // 取消按钮名称
    default: "取消",
  },
  options: {
    type: Object, //继承el-dialog自身属性
  },
});
const dialogVisible = ref(false);
const iconMap = {
  warn: {
    url: "https://static.wxb.com.cn/frontEnd/images/ideacome-vue3-component/component-confirm-warn.png",
  },
  strongWarn: {
    url: "https://static.wxb.com.cn/frontEnd/images/ideacome-vue3-component/component-confirm-strongWarn.png",
  },
  success: {
    url: "https://static.wxb.com.cn/frontEnd/images/ideacome-vue3-component/component-confirm-success.png",
  },
  error: {
    url: "https://static.wxb.com.cn/frontEnd/images/ideacome-vue3-component/component-confirm-error.png",
  },
  question: {
    url: "https://static.wxb.com.cn/frontEnd/images/ideacome-vue3-component/component-confirm-question.png",
  },
};
watch(
  () => props.isShow,
  (newValues) => {
    if (newValues) {
      dialogVisible.value = true;
    } else {
      dialogVisible.value = false;
    }
  },
  { immediate: true }
);
const emits = defineEmits(["close", "confirm"]);
const _options = computed(() => {
  const options = {
    closeOnClickModal: false,
    showClose: false,
    width: "520px",
    alignCenter: true,
  };
  return Object.assign(options, props.options);
});

const handleClose = () => {
  emits("close");
};
const handleConfirm = () => {
  emits("confirm");
};

const close = () => {
  dialogVisible.value = false
}

const show = () => {
  dialogVisible.value = true
}

defineExpose({
  show,
  close
})
</script>

<style lang="less" scoped>
:deep(.el-dialog) {
  border-radius: 10px;
  padding: 0;
}
:deep(.el-dialog__body) {
  padding: v-bind(padding);
}
:deep(.el-dialog__header) {
  padding: 0;
}
:deep(.el-dialog__footer) {
  text-align: right;
  border: none;
  padding: 0 28px 32px;
}
.component-confirm-content {
  display: flex;
  .icon-sty {
    height: 20px;
    width: 20px;
    margin: 2px 10px 0 0;
  }
  .component-confirm-text {
    text-align: left;
    .title-sty {
      font-weight: 500;
      font-size: 15px;
      color: #2c2c34;
      line-height: 24px;
    }
    .fir-sty {
      font-weight: 500;
      font-size: 15px;
      color: #2c2c34;
      line-height: 24px;
    }
    .sec-sty {
      font-weight: 400;
      font-size: 14px;
      color: #888888;
      line-height: 24px;
      margin-top: 6px;
    }
  }
}
</style>

 

 打开二次确认弹窗方法Confirm 返回一个Promise,在外部调用时在函数返回的.then()中执行确认操作回调回调,此时可以直接将传入的id以闭包的形式传回调函数中,省去了中间存储的一步,具体使用如下:

 

<template>
 <button @click="handleDelete('1')">点击删除</button>
 <pro-confirm
    :is-show="confirmVisible"
    @confirm="handleDelConfirm"
    content="正在进行删除操作,是否继续?"
  />
<template>

<script setup>
    import { ref } from 'vue'; 
    // 引入组件库方法,也可以直接挂在至globalProperties使用
    import { ProConfirmV2 } from 'pro-components'

    const currentId = ref();
    const confirmVisible= ref(false);

    function handleDelete(id) {
      ProConfirmV2({
        type: 'question',
        title: '是否删除?',
        confirmName: '确认',
        cancelName: '取消',
      }).then(() => {
        // 直接写用确认删除回调
        api({ id }).then(() => {})
      })
    }
</script>

Vue3 中,使用 Element UI 的 el-table 和 el-dialog 组件,可以按照以下步骤来实现自定义弹窗: 1. 在 el-table 中定义 scoped slot,例如: ```html <el-table :data="tableData"> <el-table-column prop="name" label="Name"></el-table-column> <el-table-column prop="gender" label="Gender"></el-table-column> <el-table-column label="Actions"> <template #default="{ row }"> <el-button @click="handleClick(row)">Edit</el-button> <el-button @click="handleDelete(row)">Delete</el-button> </template> </el-table-column> </el-table> ``` 2. 在 setup 函数中定义 handleClick 方法,例如: ```javascript import { ref } from 'vue'; export default { setup() { const dialogVisible = ref(false); const editRow = ref({}); const handleClick = (row) => { dialogVisible.value = true; editRow.value = Object.assign({}, row); }; return { dialogVisible, editRow, handleClick }; } } ``` 3. 在 el-dialog使用 editRow 数据来渲染自定义的弹窗: ```html <el-dialog v-model:visible="dialogVisible"> <el-form v-if="editRow"> <el-form-item label="Name"> <el-input v-model="editRow.name"></el-input> </el-form-item> <el-form-item label="Gender"> <el-radio-group v-model="editRow.gender"> <el-radio :label="1">Male</el-radio> <el-radio :label="2">Female</el-radio> </el-radio-group> </el-form-item> </el-form> <div slot="footer"> <el-button @click="dialogVisible = false">Cancel</el-button> <el-button type="primary" @click="handleSave">Save</el-button> </div> </el-dialog> ``` 这样,当用户点击 Edit 按钮时,就会弹出自定义的编辑弹窗,用户可以在弹窗中编辑数据,点击确认按钮后,数据会保存到 editRow 中,然后再自动更新到表格中。注意,在 Vue3 中使用 v-model 绑定 el-dialog 的 visible 属性时,需要加上修饰符 `v-model:visible`,否则会出现警告。同时,在 el-dialog 中添加一个保存按钮,点击后可以保存数据并关闭弹窗
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值