基于Vant封装一套弹窗插件

​ 最近这段时间,公司要求做一套移动端审批流程的页面。大量页面包含了固定样式的提示窗口。类似(alert、toast、confirm)的提示弹窗。考虑到移动端开发,所以采用了市面上面比较火的Vant(友赞)的UI框架。

​ 但是一般公司的设计都会比较定制化,会再原有的基础之上,做些主题、样式、交互上的一些调整。不过话说回来,毕竟用的是基于Vue这种框架。此时插槽不就是信手拈来。

​ 考虑到多人协作开发,弹窗又都是一样。所以就自己封装了一套插件,供其他人使用。虽可能不尽完美,但是确实是学习到了一些底层封装的思路。这不就是我们作为开发的人热衷的方向吗?做这些事情实际上也是在锻炼自己的技术,学习其他框架编写的思路,理解底层业务的实现原理。这一套封装虽不是多么强大,但也算是重新给我打开了一扇大门,让我对Javascript的有了更进一步的理解。

公司UI


封装的弹窗最终效果

虽然可能有些出入(button,主要是没完善)

设计思路

​ 最开始想到的肯定是Components,因为Vue本身全部都是组件,所有页面也都是以组件的形式开发,包括Template。通常如果封装弹窗组件,使用顺序一般都是import、然后声明、再然后用标签注入到相对应的页面上面使用,其实感觉并不是很友好。而且操作起来很麻烦,还要考虑到子组件注册弹窗后调用的时候,生命周期问题。如果对依赖顺序不太理不清的时候,会出现类似this.$refs... is undifiend | not function的情况。

​ 那为什么不能用类似this.$confirm 或者 this.$alert的形式,再我想要用到的时候直接一条命令就可以了呢?想到这里,就想到Vue提供的插件开发的支持。也就有了上面的最终效果。

供上代码

目录结构

VMessage
    ├── Confirm
    │   ├── main.js
    │   └── main.vue
    └── index.js

Confirm/mian.vue

<template>
  <div class="private-vant-confirm-container">
    <private-dialog
      v-model="show"
      :before-close="beforeClose"
      :show-cancel-button="showCancelButton"
      :show-confirm-button="showConfirmButton"
      :close-on-click-overlay="closeOnClickOverlay"
    >
      <section>
        <svg-icon :icon-class="iconClass" v-if="iconClass" class-name="icon"></svg-icon>
        <span v-if="tips" class="tips">{{tips}}</span>
        <span v-if="message" class="message">{{message}}</span>
      </section>
    </private-dialog>
  </div>
</template>

<script>
/* 确认弹窗 */
import { Dialog } from "vant";

export default {
  components: {
    "private-dialog": Dialog.Component
  },
  data() {
    return {
      show: false,
      type: "success",
      tips: "",
      icon: "",
      message: "",
      showCancelButton: false /* 是否展示取消按钮 */,
      showConfirmButton: false /* 是否展示确认按钮 */,
      closeOnClickOverlay: false /* 是否在点击遮罩层后关闭弹窗 */,
      onClose: null,
      duration: 2000
    };
  },
  watch: {
    show(val) {
      const { showCancelButton, showConfirmButton, duration } = this.$data;
      if (val && !showCancelButton && !showConfirmButton) {
        setTimeout(() => {
          this.show = false;
          this.handleCallback("close");
        }, duration);
      }
    }
  },
  computed: {
    iconClass() {
      return this.icon || this.type;
    }
  },
  methods: {
    beforeClose(action, done) {
      this.show = false;
      done();
      this.handleCallback(action);
    },
    handleCallback(action) {
      if (typeof this.onClose === "function") {
        this.onClose(action);
      }
    }
  }
};
</script>

<style lang="scss" scoped>
.private-vant-confirm-container {
  section {
    padding: 20px 0;
    display: flex;
    justify-content: space-around;
    align-items: center;
    flex-direction: column;
    .icon {
      font-size: 65px;
    }
    span {
      margin-top: 10px;
    }
    .tips {
      color: #2c2d2f;
      font-size: 20px;
    }
    .message {
      color: #48494c;
      font-size: 17px;
    }
  }
}
</style>

Confirm/mian.js

import Vue from "vue";
import Main from "./main.vue";
let ConfirmConstructor = Vue.extend(Main);

const Confirm = function(options) {
  options = options || {};
  if (typeof options === "string") {
    options = {
      message: options,
    };
  }

  let instance = new ConfirmConstructor({
    data: options,
  });
  instance.$mount();
  document.body.appendChild(instance.$el);
  instance.show = true;

  return instance;
};

["success", "warning"].forEach((type) => {
  Confirm[type] = (options) => {
    if (typeof options === "string") {
      options = {
        message: options,
      };
    }
    options.type = type;
    return Confirm(options);
  };
});

export default Confirm;

VMessage/index.js

/* 确认弹窗 */
import Confirm from "./Confirm/main";

/**

  使用示例
  this.$vConfirm.success({
    message: "提交成功",
    onClose(action) {
      console.log(action, " action");
    }
  });

  this.$vConfirm.warning({
    message: "确定同意该申请?",
    tips: "提示",
    showCancelButton: true,
    showConfirmButton: true,
    onClose(action) {
      console.log(action, " action");
    }
  });

  try {
    const action = await this.$vConfirmSync({
      type: "warning",
      message: "提交成功",
      showCancelButton: true,
      closeOnClickOverlay: false
    });
    console.log(action, " action");
  } catch (error) {
    console.log(error, " error");
  }

*/

/**
 * 同步用法
 * @param {String} tips 提示
 * @param {String} message 内容
 * @param {String} type success (default) | warning
 * @param {String} icon 图标 load svg-icon
 * @param {Boolean} showCancelButton 是否展示取消按钮 default:false
 * @param {Boolean} showConfirmButton 是否展示确认按钮 default:false
 * @param {Boolean} closeOnClickOverlay 是否在点击遮罩层后关闭弹窗 default: false
 * @param {Number} duration 关闭时间 showCancelButton为false && showConfirmButton为false时 default:2000
 */
function vConfirmSync(options) {
  return new Promise((resolve, reject) => {
    Confirm({
      ...options,
      onClose(action) {
        if (action === "confirm") resolve(action);
        else reject(action);
      },
    });
  });
}

/**
 * @author Gj
 * 封装Vant Dialog
 */
const install = function(Vue) {
  Vue.prototype.$vConfirm = Confirm;
  Vue.prototype.$vConfirmSync = vConfirmSync;
};

export default {
  install,
};

src/main.js vue的注入口文件注册

import { VMessage } from "@/components/index";
Vue.use(VMessage);

...
new Vue({
  router,
  store,
  i18n,
  render: (h) => h(App),
}).$mount("#app");
posted @ 2020-06-09 16:13  _Gj  阅读(1712)  评论(0编辑  收藏  举报