diff --git a/resources/assets/js/components/MicroApps/iframe.vue b/resources/assets/js/components/MicroApps/iframe.vue index 3e9526418..0a4fffe89 100644 --- a/resources/assets/js/components/MicroApps/iframe.vue +++ b/resources/assets/js/components/MicroApps/iframe.vue @@ -65,8 +65,10 @@ export default { data() { return { src: this.url, + isReady: false, isLoading: true, hasMounted: false, + hearTbeatLastTime: 0, } }, @@ -148,22 +150,31 @@ export default { if (!this.isFromCurrentIframe(e)) { return } - const {type, message} = e.data; + const type = e.data.type; + const message = this.handleMessageEnsureJson(e.data.message); switch (type) { case 'MICRO_APP_READY': - this.handleMessageOfReady(this.handleMessageEnsureJson(message)); + this.handleMessageOfReady(message); + break + + case 'MICRO_APP_HEARTBEAT': + this.handleMessageOfHeartbeat(message); break case 'MICRO_APP_METHOD': - this.handleMessageOfMethod(this.handleMessageEnsureJson(message)) + this.handleMessageOfMethod(message) break case 'MICRO_APP_FUNCTION_RESULT': - this.handleMessageOfFunctionResult(this.handleMessageEnsureJson(message)) + this.handleMessageOfFunctionResult(message) break case 'MICRO_APP_BEFORE_CLOSE': - this.handleMessageOfBeforeClose(this.handleMessageEnsureJson(message)) + this.handleMessageOfBeforeClose(message) + break + + case 'MICRO_APP_BEFORE_UNLOAD': + this.handleMessageOfBeforeUnload(message); break default: @@ -179,29 +190,37 @@ export default { // 处理消息的准备状态 (MICRO_APP_READY) handleMessageOfReady({supportBeforeClose}) { this.handleLoad() + this.isReady = true this.isLoading = false - if (!supportBeforeClose) { - return - } - this.$store.commit('microApps/update', { - name: this.name, - data: { - onBeforeClose: () => { - return new Promise(resolve => { - const message = { - id: $A.randomString(16), - name: this.name + if (supportBeforeClose) { + this.$store.commit('microApps/update', { + name: this.name, + data: { + onBeforeClose: () => { + if (this.hearTbeatLastTime && Date.now() - this.hearTbeatLastTime > 5000) { + return true // 超时,允许关闭 } - this.$refs.iframe.contentWindow.postMessage({ - type: 'MICRO_APP_BEFORE_CLOSE', - message - }, '*') - this.pendingBeforeCloses.set(message.id, resolve) - }) + return new Promise(resolve => { + const message = { + id: $A.randomString(16), + name: this.name + } + this.$refs.iframe.contentWindow.postMessage({ + type: 'MICRO_APP_BEFORE_CLOSE', + message + }, '*') + this.pendingBeforeCloses.set(message.id, resolve) + }) + } } - } - }) + }) + } + }, + + // 处理心跳消息 (MICRO_APP_HEARTBEAT) + handleMessageOfHeartbeat() { + this.hearTbeatLastTime = Date.now() }, // 处理方法消息 (MICRO_APP_METHOD) @@ -264,6 +283,18 @@ export default { this.pendingBeforeCloses.delete(id) }, + // 处理卸载前消息 (MICRO_APP_BEFORE_UNLOAD) + handleMessageOfBeforeUnload() { + this.isReady = false + + this.$store.commit('microApps/update', { + name: this.name, + data: { + onBeforeClose: () => true + } + }) + }, + // 验证消息是否来自当前 iframe isFromCurrentIframe(event) { try { diff --git a/resources/assets/js/components/MicroApps/index.vue b/resources/assets/js/components/MicroApps/index.vue index 5029ac22a..25aca435b 100644 --- a/resources/assets/js/components/MicroApps/index.vue +++ b/resources/assets/js/components/MicroApps/index.vue @@ -10,7 +10,8 @@ :transparent="app.transparent" :autoDarkTheme="app.auto_dark_theme" :keepAlive="app.keep_alive" - :beforeClose="async () => { await onBeforeClose(app.name) }"> + :beforeClose="async (isClick) => { await onBeforeClose(app.name, isClick) }" + @on-popout-window="onPopoutWindow(app.name)"> { - const app = this.microApps.find(item => item.name == name); - if (!app) { - $A.modalError("应用不存在"); - return - } - await this.inlineBlank(app, windowConfig) + await this.onPopoutWindow(name, windowConfig) }, openWindow: (params) => { if (!$A.isJson(params)) { @@ -513,9 +509,10 @@ export default { /** * 关闭之前判断 * @param name + * @param {boolean} isClick 是否是点击关闭 * @returns {Promise} */ - onBeforeClose(name) { + onBeforeClose(name, isClick = false) { return new Promise(resolve => { const onClose = () => { if ($A.isSubElectron) { @@ -527,6 +524,12 @@ export default { const app = this.microApps.find(item => item.name == name); if (!app) { + // 如果应用不存在,则直接关闭 + onClose() + return + } + if (isClick && app.keep_alive) { + // 如果是点击关闭,并且是 keep_alive 的应用,则不执行 onBeforeClose onClose() return } @@ -569,6 +572,19 @@ export default { }) }, + /** + * 弹出窗口(全屏) + * @param name + */ + async onPopoutWindow(name, windowConfig = null) { + const app = this.microApps.find(item => item.name == name); + if (!app) { + $A.modalError("应用不存在"); + return + } + await this.inlineBlank(app, windowConfig) + }, + /** * 是否渲染 iframe * @param app diff --git a/resources/assets/js/components/MicroApps/modal.vue b/resources/assets/js/components/MicroApps/modal.vue index 51c478ab6..c16ec86f5 100644 --- a/resources/assets/js/components/MicroApps/modal.vue +++ b/resources/assets/js/components/MicroApps/modal.vue @@ -2,14 +2,22 @@
-
+
-
- - - +
+
+ + + + +
+
+ + + +
{ this.handleClose(); @@ -221,29 +229,66 @@ export default { background-color: var(--modal-mask-bg, rgba(0, 0, 0, .4)); } - &-close { + &-tools { position: absolute; top: var(--status-bar-height, 0); left: -40px; - z-index: 1; - width: 40px; - height: 40px; - border-radius: 50%; + z-index: 2; + min-width: 40px; + min-height: 40px; display: var(--modal-close-display, flex); + flex-direction: column; align-items: center; - justify-content: center; - color: var(--modal-close-color, #ffffff); - cursor: pointer; - > svg { - width: 24px; - height: 24px; - transition: transform 0.3s ease-in-out; + > div { + width: 40px; + height: 40px; + display: flex; + align-items: center; + justify-content: center; + color: var(--modal-close-color, #ffffff); + cursor: pointer; + + &:hover { + > svg { + transform: rotate(-90deg); + } + } + + > svg { + width: 24px; + height: 24px; + transition: transform 0.2s ease-in-out; + } + + &.tool-fullscreen { + display: none; + + > svg { + width: 20px; + height: 20px; + } + } } - &:hover { - > svg { - transform: rotate(-90deg); + &.expanded { + margin-top: -40px; + transition: margin-top 0.2s ease-in-out; + + &:hover { + margin-top: 0; + } + + > div { + &:hover { + > svg { + transform: scale(1.2); + } + } + + &.tool-fullscreen { + display: flex; + } } } }