Vue.component("cc-modal", {
props: {
value: {
type: Boolean,
default: false
},
title: {
type: String,
default: "Modal"
},
width: {
default: "50%"
},
showFooter: {
type: Boolean,
default: true
},
footerAlign: {
type: String,
default: "right"
},
dragable: {
type: Boolean,
default: false
},
closeOnEsc: {
type: Boolean,
default: false
},
okLoading: {
type: Boolean,
default: false
},
customClose: {
type: Boolean,
default: false
}
},
template: '\
<div>\
<transition name="fade">\
<div v-if="visible" class="cc-modal-masklayer" :style="modalLayerStyle"></div>\
</transition>\
<transition name="fade-down">\
<div class="cc-modal-container" v-if="visible" :style="modalLayerStyle">\
<div class="cc-modal-content" :style="modalStyle" ref="modalContent">\
<div class="cc-modal-header" :style="dragCursor" @mousedown="dragModal">\
<slot name="header">\
<span class="cc-modal-title">{{title}}</span>\
</slot>\
<span class="cc-modal-close" @click="close">×</span>\
</div>\
<div class="cc-modal-body">\
<slot></slot>\
</div>\
<div class="cc-modal-footer" v-if="showFooter" :style="modalFooterAlign">\
<slot name="footer">\
<cc-button @click="cancel">取消</cc-button>\
<cc-button classType="success" @click="ok" :loading="loading">确定</cc-button>\
</slot>\
</div>\
</div>\
</div>\
</transition>\
</div>\
',
// 这里依赖extend.js中的绑定事件方法
mounted: function () {
this.$parent.modalCount = this.$parent.modalCount ? this.$parent.modalCount + 1 : 1;
if (this.closeOnEsc) {
this.Fn.addEventHandler(document, "keyup", this.escClose);
}
},
beforeDestroy: function () {
if (this.closeOnEsc) {
this.Fn.removeEventHandler(document, "keyup", this.escClose);
}
},
data: function () {
return {
visible: false,
loading: false
}
},
computed: {
modalStyle: function () {
var obj = {
width: this.width,
left: 0,
top: "100px"
};
var width = this.width,
windowWidth = this.Fn.getInnerClient().width;
if (!!Number(this.width)) {
obj.width = width + "px";
obj.left = ((windowWidth - width) / 2) + "px";
} else {
if (width.indexOf("%") > -1) {
obj.left = (windowWidth * (1 - Number(width.replace("%", "")) / 100) / 2) + "px";
} else if (width.indexOf("px") > -1) {
obj.left = (windowWidth - Number(width.replace("px", ""))) / 2 + "px";
}
}
return obj
},
modalLayerStyle: function () {
return {
"z-index": 9999 + this.$parent.modalCount
}
},
modalFooterAlign: function () {
return {
"text-align": this.footerAlign
}
},
dragCursor: function () {
if (this.dragable) return {
cursor: "move"
}
return null
}
},
methods: {
close: function () {
this.visible = false;
this.$emit('input', false);
},
ok: function (event) {
this.$emit("ok", event);
if (!this.customClose && !this.okLoading) {
this.close()
} else {
if (this.okLoading) {
this.loading = true
}
}
},
cancel: function (event) {
this.$emit("cancel", event);
if (!this.customClose) {
this.close()
}
},
escClose: function (event) {
if (this.visible && event.keyCode === 27) {
this.close()
}
},
dragModal: function (eve) {
if (this.dragable) {
var self = this,
isDraging = true,
modalContent = this.$refs.modalContent,
rect = modalContent.getBoundingClientRect(),
w = rect.width,
h = rect.height,
ol = rect.left,
ot = rect.top,
pointX = eve.clientX,
pointY = eve.clientY,
maxW = this.Fn.getInnerClient().width - w,
maxH = this.Fn.getInnerClient().height - h;
this.Fn.addEventHandler(document, "mousemove", function (e) {
if (isDraging) {
window.getSelection ? window.getSelection().removeAllRanges() : document.selection.empty();
var left = e.clientX - pointX + ol,
top = e.clientY - pointY + ot;
if (left < 0) left = 0;
if (left > maxW) left = maxW;
if (top < 0) top = 0;
if (top > maxH) top = maxH;
modalContent.style.left = left + "px";
modalContent.style.top = top + "px";
}
});
this.Fn.addEventHandler(document, "mouseup", function (e) {
isDraging = false;
self.Fn.removeEventHandler(document, "mousemove");
});
}
}
},
watch: {
value: function (val) {
this.visible = val
if (!val) {
this.loading = false
}
},
okLoading: function (val) {
if (!val) {
this.loading = false
}
}
}
});
// 通过JS调用
var ccModal = function (props) {
if (ccModal.modal) return;
var _props = props || {};
var instance = new Vue({
data: {
template: _props.template,
title: _props.title,
width: _props.width,
okFn: _props.okFn,
cancelFn: _props.cancelFn,
okLoading: _props.okLoading,
dragable: _props.dragable,
showFooter: _props.showFooter,
footerAlign: _props.footerAlign,
closeOnEsc: _props.closeOnEsc,
confirmType: _props.confirmType,
customButtons: _props.customButtons,
customButtonsLoading: {}
},
template: '\
<cc-modal :title="title" :width="width" @ok="ok" @cancel="cancel" :ok-loading="okLoading" :dragable="dragable" :show-footer="showFooter" :footer-align="footerAlign" :close-on-esc="closeOnEsc" :custom-close="customClose">\
<div v-if="confirmType" class="confirmModal">\
<i class="fa fa-exclamation-circle" :style="confirmTypeColor" aria-hidden="true"></i>\
{{computedTemplate}}\
</div>\
<div v-else="confirmType">{{computedTemplate}}</div>\
<div v-if="!!customButtons" slot="footer">\
<cc-button v-for="(item,index) in customButtons" :key="item.mark" @click="customButtonClick(item,index)" :loading="customButtonsLoading[item.mark]" :class-type="item.classType">{{item.text}}</cc-button>\
</div>\
</cc-modal>\
',
computed: {
customClose: function () {
if (this.okFn || this.cancelFn) {
return true
}
return false
},
confirmTypeColor: function () {
var color;
switch (this.confirmType) {
case 1:
color = "#00cc99"
break
case 2:
color = "#00ccff"
break
case 3:
color = "#eeaa00"
break
case 4:
color = "#ee6666"
break
};
return {
color: color
}
},
computedTemplate: function () {
// if (typeof this.template != "string") {
// throw new Error("template必填且必须是String类型!")
// }
// if (this.template.length == this.template.indexOf(".html") + 5) {
// console.log("URL模板")
// } else if (this.template.indexOf("#") == 0) {
// console.log("ID模板")
// var element = document.getElementById(this.template.substr(1));
// return element
// } else if (this.template.indexOf("<") == 0 && this.template.indexOf(">") == this.template.length - 1) {
// console.log("HTML模板")
// }else{
// console.log("String模板")
// }
console.log("太复杂了,能力有限暂时做不出来,所以还是JS调用弹框的时候template不做解析,因此只应用于类似于confirm之类的弹框,更复杂的用HTML的形式去调用吧!\n贴一个参考网址,以后有兴趣了再深究:https://segmentfault.com/a/1190000010611308");
// return Vue.compile(this.template)
return this.template
}
},
methods: {
cancel: function () {
if (this.cancelFn) {
this.cancelFn()
} else {
this.$children[0].visible = false;
this.remove();
}
},
ok: function () {
if (this.okFn) {
this.okFn()
} else {
if (!this.okLoading) {
this.okLoading = false;
this.$children[0].visible = false;
}
}
},
customButtonClick: function (item, index) {
if (item.loading) {
this.$set(this.customButtonsLoading, item.mark, true)
}
if (item.fn && typeof item.fn == "function") {
item.fn()
}
},
remove: function () {
var self = this;
setTimeout(function () {
self.destroy();
}, 300);
},
destroy: function () {
this.$destroy();
document.body.removeChild(this.$el);
}
}
});
document.body.appendChild(instance.$mount().$el);
// 设置弹窗默认打开
instance.$children[0].visible = true;
return {
modal: instance.$children[0],
open: function () {
ccModal.modal.visible = true;
},
close: function () {
ccModal.modal.visible = false;
ccModal.modal.$parent.okLoading = false;
ccModal.modal.$parent.customButtonsLoading = {};
},
remove: function () {
ccModal.close();
ccModal.modal.$parent.remove();
}
}
};
ccModal.confirm = function (props) {
var _props = props || {}, type;
switch (_props.type) {
case "success":
type = 1
break
case "info":
type = 2
break
case "warn":
type = 3
break
case "error":
type = 4
break
};
ccModal({
width: props.width || "360px",
title: props.title || "确认",
template: props.message,
okFn: _props.okFn,
cancelFn: _props.cancelFn,
okLoading: _props.okLoading,
dragable: _props.dragable,
showFooter: _props.showFooter,
footerAlign: _props.footerAlign,
closeOnEsc: _props.closeOnEsc,
confirmType: type,
customButtons: _props.customButtons
});
};