From d77406951d629195e90b0989c13c8803ffce2e2e Mon Sep 17 00:00:00 2001 From: kuaifan Date: Thu, 18 Dec 2025 02:44:37 +0000 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=9B=B4=E6=96=B0=E5=BE=AE=E5=BA=94?= =?UTF-8?q?=E7=94=A8=E8=8F=9C=E5=8D=95=E9=85=8D=E7=BD=AE=EF=BC=8C=E7=BB=9F?= =?UTF-8?q?=E4=B8=80=E4=BD=BF=E7=94=A8=E7=B1=BB=E5=9E=8B=E5=AD=97=E6=AE=B5?= =?UTF-8?q?=E6=9B=BF=E4=BB=A3URL=E7=B1=BB=E5=9E=8B=E5=AD=97=E6=AE=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/Models/Setting.php | 4 +- ...0000_update_setting_microapp_menu_type.php | 79 +++++++++++++++++++ .../assets/js/components/MicroApps/index.vue | 16 ++-- .../assets/js/pages/manage/application.vue | 6 +- resources/assets/js/pages/single/apps.vue | 2 +- resources/assets/js/store/actions.js | 20 ++++- 6 files changed, 111 insertions(+), 16 deletions(-) create mode 100644 database/migrations/2025_12_18_000000_update_setting_microapp_menu_type.php diff --git a/app/Models/Setting.php b/app/Models/Setting.php index 3c10689d7..88a942d50 100644 --- a/app/Models/Setting.php +++ b/app/Models/Setting.php @@ -309,13 +309,13 @@ class Setting extends AbstractModel } $location = trim($menu['location'] ?? 'application'); $label = trim($menu['label'] ?? $fallbackLabel); - $urlType = strtolower(trim($menu['url_type'] ?? 'iframe')); + $type = strtolower(trim($menu['type'] ?? 'iframe')); $payload = [ 'location' => $location, 'label' => $label, 'icon' => Base::newTrim($menu['icon'] ?? ''), 'url' => $url, - 'url_type' => $urlType, + 'type' => $type, 'keep_alive' => isset($menu['keep_alive']) ? (bool)$menu['keep_alive'] : true, 'disable_scope_css' => (bool)($menu['disable_scope_css'] ?? false), 'auto_dark_theme' => isset($menu['auto_dark_theme']) ? (bool)$menu['auto_dark_theme'] : true, diff --git a/database/migrations/2025_12_18_000000_update_setting_microapp_menu_type.php b/database/migrations/2025_12_18_000000_update_setting_microapp_menu_type.php new file mode 100644 index 000000000..601c34738 --- /dev/null +++ b/database/migrations/2025_12_18_000000_update_setting_microapp_menu_type.php @@ -0,0 +1,79 @@ +first(); + if (!$row) { + return; + } + $data = Base::string2array($row->setting); + if (empty($data) || !is_array($data)) { + return; + } + + $changed = false; + foreach ($data as $appIndex => $app) { + if (!is_array($app)) { + continue; + } + $menuItems = []; + if (isset($app['menu_items']) && is_array($app['menu_items'])) { + $menuItems = $app['menu_items']; + } elseif (isset($app['menu']) && is_array($app['menu'])) { + $menuItems = [$app['menu']]; + } + if (empty($menuItems)) { + continue; + } + + $newMenuItems = []; + foreach ($menuItems as $menu) { + if (!is_array($menu)) { + $newMenuItems[] = $menu; + continue; + } + if (!isset($menu['type']) && isset($menu['url_type'])) { + $menu['type'] = $menu['url_type']; + unset($menu['url_type']); + $changed = true; + } elseif (isset($menu['url_type'])) { + unset($menu['url_type']); + $changed = true; + } + $newMenuItems[] = $menu; + } + + if (isset($app['menu_items']) && is_array($app['menu_items'])) { + $data[$appIndex]['menu_items'] = $newMenuItems; + } elseif (isset($app['menu']) && is_array($app['menu'])) { + $data[$appIndex]['menu'] = $newMenuItems[0] ?? $app['menu']; + } + } + + if ($changed) { + $row->updateInstance(['setting' => $data]); + $row->save(); + } + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + // No-op: do not revert settings payload. + } +} diff --git a/resources/assets/js/components/MicroApps/index.vue b/resources/assets/js/components/MicroApps/index.vue index 58279fb53..6439d07f2 100644 --- a/resources/assets/js/components/MicroApps/index.vue +++ b/resources/assets/js/components/MicroApps/index.vue @@ -235,7 +235,7 @@ export default { name: app.name, url: app.url, - urlType: app.url_type, + type: app.type, userId: this.userId, userToken: this.userToken, @@ -405,13 +405,13 @@ export default { } // 如果是 blank 链接,则在新窗口打开 - if (/_blank$/i.test(config.url_type)) { + if (/_blank$/i.test(config.type)) { await this.inlineBlank(config) return } // 如果是外部链接,则在新窗口打开 - if (config.url_type === 'external') { + if (config.type === 'external') { await this.externalWindow(config) return } @@ -461,7 +461,7 @@ export default { ...config, // 新窗口强制参数 - url_type: config.url_type.replace(/_blank$/, ''), + type: config.type.replace(/_blank$/, ''), transparent: true, keep_alive: false, }; @@ -621,7 +621,7 @@ export default { return } - if (this.isIframe(app.url_type)) { + if (this.isIframe(app.type)) { const before = app.onBeforeClose(); if (before && before.then) { before.then(() => { @@ -685,7 +685,7 @@ export default { if (!app) { return } - if (this.isIframe(app.url_type)) { + if (this.isIframe(app.type)) { app.postMessage({ type: 'MICRO_APP_MENU_CLICK', message: action @@ -743,7 +743,7 @@ export default { * @returns {boolean} */ shouldRenderIFrame(app) { - return app.url && this.isIframe(app.url_type) && (app.isOpen || app.keep_alive); + return app.url && this.isIframe(app.type) && (app.isOpen || app.keep_alive); }, /** @@ -752,7 +752,7 @@ export default { * @returns {boolean} */ shouldRenderMicro(app) { - return app.url && !this.isIframe(app.url_type) && (app.isOpen || this.closings.includes(app.name)); + return app.url && !this.isIframe(app.type) && (app.isOpen || this.closings.includes(app.name)); }, /** diff --git a/resources/assets/js/pages/manage/application.vue b/resources/assets/js/pages/manage/application.vue index de60367aa..442e8bb04 100644 --- a/resources/assets/js/pages/manage/application.vue +++ b/resources/assets/js/pages/manage/application.vue @@ -184,8 +184,8 @@ - - @@ -439,7 +439,7 @@ const createCustomMicroMenu = () => ({ version: 'custom', menu: { location: 'application', - url_type: 'iframe', + type: 'iframe', visible_to: 'admin', keep_alive: true, auto_dark_theme: true, diff --git a/resources/assets/js/pages/single/apps.vue b/resources/assets/js/pages/single/apps.vue index e889d5097..430d57a4d 100644 --- a/resources/assets/js/pages/single/apps.vue +++ b/resources/assets/js/pages/single/apps.vue @@ -43,7 +43,7 @@ export default { id: 'iframe-test', name: 'iframe-test', url: url, - url_type: 'iframe', + type: 'iframe', transparent: true, keep_alive: false, }) diff --git a/resources/assets/js/store/actions.js b/resources/assets/js/store/actions.js index b39e0de04..a9e9eda75 100644 --- a/resources/assets/js/store/actions.js +++ b/resources/assets/js/store/actions.js @@ -5064,7 +5064,7 @@ export default { * - id 应用ID(必须) * - name 应用名称(必须) * - url 应用地址(必须) - * - url_type 地址类型(可选) + * - type 打开类型(可选,string 或 {mobile,desktop,default};default 用于补齐 mobile/desktop,缺省为 iframe) * - background 背景颜色(可选) * - capsule 应用胶囊配置(可选) * - transparent 是否透明模式 (true/false),默认 false @@ -5094,11 +5094,27 @@ export default { } }) .replace(/\{system_base_url}/g, serverLocation.origin) + + const resolveType = () => { + if (typeof data.type === 'string') { + return data.type + } + if ($A.isJson(data.type)) { + const defaultType = typeof data.type.default === 'string' ? data.type.default : 'iframe' + const mobileType = typeof data.type.mobile === 'string' + ? data.type.mobile + : (typeof data.type.app === 'string' ? data.type.app : defaultType) + const desktopType = typeof data.type.desktop === 'string' ? data.type.desktop : defaultType + return $A.platformType() === 'desktop' ? desktopType : mobileType + } + return 'inline' + } + const config = { id: data.id, name: data.name, url: $A.mainUrl(data.url), - url_type: data.url_type || 'inline', + type: resolveType(), background: data.background || null, capsule: $A.isJson(data.capsule) ? data.capsule : {}, transparent: typeof data.transparent == 'boolean' ? data.transparent : false,