我的首要任务是继续使用 Bootstrap 代码,因为他们努力使模式工作,修复滚动条等等。我发现现有的提案试图模仿这一点,但它们只是完成了一部分。我什至不想碰运气:我只想使用实际的引导代码。
另外,我想要一个程序界面,例如调用dialog.show(gimme plenty of parameters here),而不仅仅是在某处切换变量(即使该变量可能是一个复杂对象)。
我还想为实际的对话框内容提供 Vue 的反应性和组件渲染。
要解决的问题是,如果 Vue 发现组件的 DOM 被外部操纵,它会拒绝合作。因此,基本上,我将声明模态本身的外部 div 移出组件并注册了组件,以便我还可以通过程序访问对话框。
这样的代码是可能的:
window.mydialog.yesNo('Question', 'Do you like this dialog?')
解决方案。
main.html(基本上只是包装我们组件的外部 div):
<div class="modal fade" id="df-modal-handler" tabindex="-1" role="dialog" aria-hidden="true">
<df-modal-handler/>
</div>
component-template.html(模态的其余部分):
<script type="text/x-template" id="df-modal-handler-template">
<div :class="'modal-dialog ' + sizeClass" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">{{ title }}</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body" v-html="body"/>
<div class="modal-footer">
<button type="button" v-for="button in buttons" :class="button.classes" v-bind="button.arias"
@click.stop="buttonClick(button, callback)">{{ button.text }}
</button>
</div>
</div>
</div>
</script>
component-def.js - 包含显示和操作对话框的逻辑,还支持对话框堆栈以防您出错并按顺序调用两个对话框:
Vue.component('df-modal-handler', {
template: `#df-modal-handler-template`,
props: {},
data() {
return {
dialogs: [],
initialEventAssignDone: false,
};
},
computed: {
bootstrapDialog() { return document.querySelector('#df-modal-handler'); },
currentDialog() { return this.dialogs.length ? this.dialogs[this.dialogs.length - 1] : null; },
sizeClass() {
let dlg = this.currentDialog;
if (!dlg) return 'modal-sm';
if (dlg.large || ['large', 'lg', 'modal-lg'].includes(dlg.size)) return 'modal-lg';
else if (dlg.small || ['small', 'sm', 'modal-sm'].includes(dlg.size)) return 'modal-sm';
return '';
},
title() { return this.currentDialog ? this.currentDialog.title : 'No dialogs to show!'; },
body() { return this.currentDialog ? this.currentDialog.body : 'No dialogs have been invoked'; },
callback() { return this.currentDialog ? this.currentDialog.callback : null; },
buttons() {
const self = this;
let res = this.currentDialog && this.currentDialog.buttons ? this.currentDialog.buttons : [{close: 'default'}];
return res.map(value => {
if (value.close == 'default') value = {
text: 'Close',
classes: 'btn btn-secondary',
data_return: 'close'
};
else if (value.yes == 'default') value = {
text: 'Yes',
classes: 'btn btn-primary',
data_return: 'yes'
};
else if (value.no == 'default') value = {
text: 'No',
classes: 'btn btn-secondary',
data_return: 'no'
};
value.arias = value.arias || {};
let clss = (value.classes || '').split(' ');
if (clss.indexOf('btn') == -1) clss.push('btn');
value.classes = clss.join(' ');
return value;
});
},
},
created() {
// make our API available
window.mydialog = this;
},
methods: {
show: function show() {
const self = this;
if (!self.initialEventAssignDone) {
// created is too soon. if we try to do this there, the dialog won't even show.
self.initialEventAssignDone = true;
$(self.bootstrapDialog).on('hide.bs.modal', function (event) {
let callback = null;
if (self.dialogs.length) callback = self.dialogs.pop().callback;
if (self.dialogs.length) event.preventDefault();
if (callback && callback.df_called !== true) callback(null);
});
}
$(self.bootstrapDialog).modal('show');
},
hide: function hide() {
$(this.bootstrapDialog).modal('hide');
},
buttonClick(button, callback) {
if (callback) { callback(button.data_return); callback.df_called = true; }
else console.log(button);
this.hide();
},
yesNo(title, question, callback) {
this.dialogs.push({
title: title, body: question, buttons: [{yes: 'default'}, {no: 'default'}], callback: callback
});
this.show();
},
},
});
请注意,此解决方案会在 DOM 中创建一个单独的对话框实例,并重复使用该实例来满足您的所有对话框需求。 (还)没有过渡,所以当有多个活动对话框时,用户体验不是很好。无论如何,这是不好的做法,但我希望它涵盖,因为你永远不知道......
对话框主体实际上是一个 v-html,所以只需用一些参数实例化您的组件,让它自己绘制主体。