|
|
@@ -0,0 +1,215 @@
|
|
|
+/**
|
|
|
+ * ele-modal 扩展(覆盖 ele-admin 的 EleModal 全局注册):
|
|
|
+ * 在原有功能基础上增加“最小化”能力:
|
|
|
+ * - 通过 minimizable 属性开启,开启后标题栏出现最小化按钮;
|
|
|
+ * - 最小化后弹窗隐藏但内容保留(不销毁 DOM / 不清空数据);
|
|
|
+ * - 最小化弹窗统一挂到全局悬浮胶囊(任意页面都可见),点击可还原;
|
|
|
+ * - 可通过 minimize-prefix 属性给弹窗名称加前缀,用于区分同名弹窗。
|
|
|
+ */
|
|
|
+import EleModal from 'ele-admin/es/ele-modal';
|
|
|
+import { registerMinimized, unregisterMinimized } from './minimizeManager';
|
|
|
+import './index.scss';
|
|
|
+
|
|
|
+const original = EleModal && EleModal.default ? EleModal.default : EleModal;
|
|
|
+
|
|
|
+const origData =
|
|
|
+ typeof original.data === 'function' ? original.data : () => ({});
|
|
|
+const origWatch = original.watch || {};
|
|
|
+const origActivated =
|
|
|
+ typeof original.activated === 'function' ? original.activated : null;
|
|
|
+const origBeforeDestroy =
|
|
|
+ typeof original.beforeDestroy === 'function' ? original.beforeDestroy : null;
|
|
|
+
|
|
|
+// 保留原有 visible 监听,同时监听“父组件主动关闭时取消最小化”
|
|
|
+const visibleHandlers = [];
|
|
|
+if (origWatch.visible) {
|
|
|
+ visibleHandlers.push(origWatch.visible);
|
|
|
+}
|
|
|
+visibleHandlers.push(function (visible) {
|
|
|
+ if (!visible && this.isMinimized) {
|
|
|
+ this.isMinimized = false;
|
|
|
+ if (this._minimizeId) {
|
|
|
+ unregisterMinimized(this._minimizeId);
|
|
|
+ this._minimizeId = null;
|
|
|
+ }
|
|
|
+ }
|
|
|
+});
|
|
|
+
|
|
|
+export default {
|
|
|
+ ...original,
|
|
|
+ name: 'EleModal',
|
|
|
+ props: {
|
|
|
+ ...original.props,
|
|
|
+ // 是否支持最小化
|
|
|
+ minimizable: {
|
|
|
+ type: Boolean,
|
|
|
+ default: false
|
|
|
+ },
|
|
|
+ // 弹窗名称前缀,悬浮胶囊显示“前缀 + 标题”,用于区分同名弹窗
|
|
|
+ minimizePrefix: {
|
|
|
+ type: String,
|
|
|
+ default: ''
|
|
|
+ }
|
|
|
+ },
|
|
|
+ data() {
|
|
|
+ return {
|
|
|
+ ...origData.call(this),
|
|
|
+ // 是否已最小化
|
|
|
+ isMinimized: false
|
|
|
+ };
|
|
|
+ },
|
|
|
+ computed: {
|
|
|
+ ...original.computed,
|
|
|
+ // 悬浮胶囊上显示的弹窗名称
|
|
|
+ minimizeTitle() {
|
|
|
+ return (this.minimizePrefix || '') + (this.title || '');
|
|
|
+ },
|
|
|
+ // 最小化按钮位置(避让右侧关闭按钮/全屏按钮)
|
|
|
+ minimizeBtnStyle() {
|
|
|
+ return this.maxable ? { right: '68px' } : { right: '44px' };
|
|
|
+ }
|
|
|
+ },
|
|
|
+ watch: {
|
|
|
+ ...origWatch,
|
|
|
+ visible: visibleHandlers
|
|
|
+ },
|
|
|
+ methods: {
|
|
|
+ ...original.methods,
|
|
|
+ // 最小化:隐藏弹窗但保留内容,并注册到全局悬浮胶囊
|
|
|
+ minimize() {
|
|
|
+ if (!this.minimizable) return;
|
|
|
+ this.isMinimized = true;
|
|
|
+ this.modalVisible = false;
|
|
|
+ this._minimizeId = registerMinimized(this);
|
|
|
+ this.$emit('minimize');
|
|
|
+ },
|
|
|
+ // 从悬浮胶囊还原弹窗
|
|
|
+ restore() {
|
|
|
+ this.isMinimized = false;
|
|
|
+ if (this._minimizeId) {
|
|
|
+ unregisterMinimized(this._minimizeId);
|
|
|
+ this._minimizeId = null;
|
|
|
+ }
|
|
|
+ this.modalVisible = true;
|
|
|
+ this.$emit('restore');
|
|
|
+ },
|
|
|
+ // 从悬浮胶囊彻底关闭(同步父组件 visible)
|
|
|
+ closeMinimized() {
|
|
|
+ this.isMinimized = false;
|
|
|
+ if (this._minimizeId) {
|
|
|
+ unregisterMinimized(this._minimizeId);
|
|
|
+ this._minimizeId = null;
|
|
|
+ }
|
|
|
+ this.modalVisible = false;
|
|
|
+ this.updateVisible(false);
|
|
|
+ }
|
|
|
+ },
|
|
|
+ activated() {
|
|
|
+ // 最小化状态下回到页面时不要自动弹出,保持最小化,由悬浮胶囊控制
|
|
|
+ if (this.isMinimized) {
|
|
|
+ this.isActivated = true;
|
|
|
+ } else if (origActivated) {
|
|
|
+ origActivated.call(this);
|
|
|
+ }
|
|
|
+ },
|
|
|
+ beforeDestroy() {
|
|
|
+ // 页面被销毁时若仍处于最小化,移除对应的悬浮胶囊
|
|
|
+ if (this.isMinimized && this._minimizeId) {
|
|
|
+ unregisterMinimized(this._minimizeId);
|
|
|
+ this._minimizeId = null;
|
|
|
+ }
|
|
|
+ if (origBeforeDestroy) {
|
|
|
+ origBeforeDestroy.call(this);
|
|
|
+ }
|
|
|
+ },
|
|
|
+ render(h) {
|
|
|
+ // proxy 标记:让插槽同时暴露到 $slots(el-dialog 依赖 $slots.footer 渲染底部)
|
|
|
+ const titleFn = () => {
|
|
|
+ const nodes = this.$scopedSlots.title
|
|
|
+ ? this.$scopedSlots.title()
|
|
|
+ : [
|
|
|
+ h('span', { class: 'el-dialog__title' }, [String(this.title || '')])
|
|
|
+ ];
|
|
|
+ if (this.minimizable) {
|
|
|
+ nodes.push(
|
|
|
+ h(
|
|
|
+ 'button',
|
|
|
+ {
|
|
|
+ class: 'el-dialog__headerbtn ele-modal-icon-minimize',
|
|
|
+ style: this.minimizeBtnStyle,
|
|
|
+ attrs: { type: 'button' },
|
|
|
+ on: { click: this.minimize }
|
|
|
+ },
|
|
|
+ [
|
|
|
+ this.$scopedSlots.minimizeIcon
|
|
|
+ ? this.$scopedSlots.minimizeIcon()
|
|
|
+ : h('i', { class: 'el-dialog__close el-icon el-icon-minus' })
|
|
|
+ ]
|
|
|
+ )
|
|
|
+ );
|
|
|
+ }
|
|
|
+ if (this.maxable) {
|
|
|
+ nodes.push(
|
|
|
+ h(
|
|
|
+ 'button',
|
|
|
+ {
|
|
|
+ class: 'el-dialog__headerbtn ele-modal-icon-fullscreen',
|
|
|
+ attrs: { type: 'button' },
|
|
|
+ on: { click: () => this.toggleFullscreen() }
|
|
|
+ },
|
|
|
+ [
|
|
|
+ this.$scopedSlots.maxIcon
|
|
|
+ ? this.$scopedSlots.maxIcon({ fullscreen: this.isFullscreen })
|
|
|
+ : h('i', { class: this.maxIconClass })
|
|
|
+ ]
|
|
|
+ )
|
|
|
+ );
|
|
|
+ }
|
|
|
+ return nodes;
|
|
|
+ };
|
|
|
+ titleFn.proxy = true;
|
|
|
+ const scopedSlots = { title: titleFn };
|
|
|
+ if (this.renderBody && this.$scopedSlots.footer) {
|
|
|
+ const footerFn = () => this.$scopedSlots.footer();
|
|
|
+ footerFn.proxy = true;
|
|
|
+ scopedSlots.footer = footerFn;
|
|
|
+ }
|
|
|
+
|
|
|
+ return h(
|
|
|
+ 'el-dialog',
|
|
|
+ {
|
|
|
+ ref: 'modal',
|
|
|
+ class: this.modalClass,
|
|
|
+ style: this.dialogStyle,
|
|
|
+ attrs: {
|
|
|
+ visible: this.modalVisible,
|
|
|
+ title: this.title,
|
|
|
+ width: this.width,
|
|
|
+ top: this.modalTop,
|
|
|
+ modal: this.modalMask,
|
|
|
+ 'modal-append-to-body': this.modalAppendToBody,
|
|
|
+ 'append-to-body': this.dialogAppendToBody,
|
|
|
+ 'lock-scroll': this.lockScroll,
|
|
|
+ 'custom-class': this.customClass,
|
|
|
+ 'close-on-click-modal': this.maskClosable,
|
|
|
+ 'close-on-press-escape': this.closeOnPressEscape,
|
|
|
+ 'show-close': this.showClose,
|
|
|
+ 'before-close': this.beforeClose,
|
|
|
+ center: this.center,
|
|
|
+ 'destroy-on-close': false
|
|
|
+ },
|
|
|
+ on: {
|
|
|
+ 'update:visible': this.updateVisible,
|
|
|
+ open: this.onOpen,
|
|
|
+ opened: this.onOpened,
|
|
|
+ close: this.onClose,
|
|
|
+ closed: this.onClosed
|
|
|
+ },
|
|
|
+ scopedSlots
|
|
|
+ },
|
|
|
+ this.renderBody && this.$scopedSlots.default
|
|
|
+ ? [this.$scopedSlots.default()]
|
|
|
+ : []
|
|
|
+ );
|
|
|
+ }
|
|
|
+};
|