vue2 函数式调用一个弹框组件
注册为组件的方式,做个弹框很容易。但是用过ant design vue 等框架,你会发现人家的弹框都是通过函数调用的,类似于这样:
this.$confirm({ content: 'destroy all', onOk() { return new Promise((resolve, reject) => { setTimeout(Math.random() > 0.5 ? resolve : reject, 1000); }).catch(() => console.log('Oops errors!')); }, cancelText: 'Click to destroy all', onCancel() { self.destroyAll(); }, });
并且弹出后,在body底部,并不在业务的html中。
这个可以这么做的思路:做一个vue实例,把他挂载到body上。下面直接上代码:
src/components/Modal/index.vue
<template> <div class="mask" v-show="visiable" @click="cancel"> <div class="modal" @click.stop="() => {}"> <h2>{{ title }}</h2> <div class="content" v-html="content"></div> <footer> <div class="btn" @click="cancel">取消</div> <div class="btn" @click="ok">确定</div> </footer> </div> </div> </template> <script> export default { props: { title: { type: String, default: "", }, content: { type: String, default: "", }, onCancel: { type: Function, default: () => {}, }, onOk: { type: Function, default: () => {}, }, }, data() { return { visiable: false, }; }, methods: { open() { this.visiable = true; }, cancel() { this.onCancel(); this.visiable = false; }, ok() { this.onOk(); this.visiable = false; }, }, }; </script> <style lang="less" scoped> .mask { position: fixed; left: 0; right: 0; top: 0; bottom: 0; z-index: 10000; background-color: rgba(0, 0, 0, 0.55); } .modal { position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%); width: 600px; padding: 0 34px; background-color: #fff; border-radius: var(--btn-radius); h2 { height: 80px; font-size: 20px; font-family: PingFangSC-Medium, PingFang SC; font-weight: 500; color: #272727; line-height: 80px; border-bottom: 1px dashed rgba(151, 151, 151, 0.2); } .content { font-size: 16px; margin: 36px 0 50px; } footer { display: flex; justify-content: space-between; margin-bottom: 50px; .btn { width: 180px; height: 44px; border-radius: 2px; border: 1px solid rgba(102, 102, 102, 0.7); text-align: center; line-height: 44px; cursor: pointer; &:last-child { background: var(--btn-bg); color: #fff; } } } /deep/.price-num { font-size: 20px; font-family: PingFangSC-Medium, PingFang SC; font-weight: 500; color: var(--text-1); } } </style>
src/components/Modal/index.js(核心代码)
import Vue from "vue"; import Modal from "./Index.vue"; // 引入弹窗组件 const ModalComponent = Vue.extend(Modal); let app; Modal.open = (options) => { if (options === 'cancel') { app && app.cancel(); return; } app = new ModalComponent( { propsData: options } ).$mount(); document.body.appendChild(app.$el); Vue.nextTick(() => { app.open(); }); }; export default Modal; // 导出
main.js
import $modal from '@/components/modal/index.js' Vue.prototype.$modal = $modal.open;
组件里调用举例:
this.$modal({ title: "再租一台", content: "租用和目标机器,配置相同的一台机器,可选择GPU数量", onOk: () => { this.$router.push({ path: "/store/hire", query: { gpuType: cObj.Gpu_type, node: "全部", mirrorImage: cObj.Image, mirrorImageLabel: cObj.Image, dateNum: cObj.Use_mode === 0 ? 0 : Math.round((cObj.Due_time - cObj.Ctime) / 24 / 3600), gpuNum: cObj.Gpu_num, os: cObj.ContainerType.includes("kvm") ? "kvm" : "docker", performance: cObj.Performance, }, }); }, });
优化,vue是个单页面应用SPA。在路由变化时,应该隐藏弹框:
router.beforeEach(async (to, from, next) => { Vue.prototype.$modal('cancel'); })