最近這段時間,公司要求做一套移動端審批流程的頁面。大量頁面包含了固定樣式的提示窗口。類似(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");