diff --git a/src/api/app.ts b/src/api/app.ts index 50245e7..24a73d9 100644 --- a/src/api/app.ts +++ b/src/api/app.ts @@ -24,6 +24,11 @@ export interface AppItem { InstalledCompanies?: string[] UserEnabled?: number ApplyStatus?: number + /** 远端注入(MSYNC):来源/提供方服务/在线状态 */ + Source?: string + ServiceName?: string + ServiceDomain?: string + ServiceOnline?: number } /** 管理员可安装应用的公司范围项 */ diff --git a/src/apps/index.ts b/src/apps/index.ts index 20ba658..bf686a9 100644 --- a/src/apps/index.ts +++ b/src/apps/index.ts @@ -25,10 +25,19 @@ export interface AppFrontendModule { FrontendRoute: string FrontendType: 'vue' | 'legacy' LegacySrc: string + /** 远端注入(MSYNC):提供方服务名(打开远端 legacy 页面时签发用户令牌用) */ + ServiceName?: string } /** 打开应用的跳转目标(vue 走前端路由;legacy 走对应 LegacyPage 路由) */ export function appOpenTarget(mod: AppFrontendModule): RouteLocationRaw { + // 远端 legacy(绝对 URL,子服务器托管):走 /remote/:code,带 src+svc 供令牌透传 + if (mod.FrontendType === 'legacy' && /^https?:\/\//.test(mod.LegacySrc)) { + return { + path: `/remote/${encodeURIComponent(mod.AppCode)}`, + query: { src: mod.LegacySrc, svc: mod.ServiceName || '' }, + } + } return mod.FrontendRoute || (mod.LegacySrc ? `/${mod.LegacySrc}` : '/app/mine') } diff --git a/src/router/index.ts b/src/router/index.ts index 43e528d..5aed5ca 100644 --- a/src/router/index.ts +++ b/src/router/index.ts @@ -330,6 +330,13 @@ export const staticRoutes: RouteRecordRaw[] = [ meta: { title: '运行时调整', icon: 'Operation', requiresAuth: true, menuGuarded: true }, }, // === 枢机协律(CreateE):旧页面 iframe 直载(LegacyPage) === + // === 远端 App(MSYNC 注入):子服务器托管页面 iframe,query 携带 src+svc 令牌透传 === + { + path: 'remote/:appCode', + name: 'RemoteApp', + component: () => import('@/views/LegacyPage.vue'), + meta: { title: '远端应用', requiresAuth: true }, + }, { path: 'private/CreateE/shop', name: 'PrivateCreateEShop', diff --git a/src/views/LegacyPage.vue b/src/views/LegacyPage.vue index 7fa1a85..9c20032 100644 --- a/src/views/LegacyPage.vue +++ b/src/views/LegacyPage.vue @@ -2,18 +2,50 @@ /** * LegacyPage * - * 通用旧页面承载组件:以 iframe 直载 TempLate/pages/ 下的原生 HTML 页面。 - * 用于尚未迁移到 Vue 的历史菜单(如"枢机协律"系列的商店/钱包/充值等)。 - * 通过路由 meta.legacySrc 指定 iframe 加载的原始 HTML 路径。 + * 通用旧页面承载组件:以 iframe 直载原生 HTML 页面。 + * 1. 本地旧页面:路由 meta.legacySrc 指定路径(如 /pages/private/CreateE/shop.html) + * 2. 远端 App(MSYNC 注入):路由 query 携带 src(子服务器绝对 URL)+ svc(提供方服务名); + * 加载前先向主服务器签发用户级短期令牌,拼到 URL(?token=)透传员工身份, + * 子服务器经 /api/msync/user/token/introspect/ 自省还原用户上下文。 */ -import { computed, ref } from 'vue' +import { computed, onMounted, ref } from 'vue' import { useRoute } from 'vue-router' +import { post } from '@/api/request' const route = useRoute() -// 旧页面原始路径(如 /pages/private/CreateE/shop.html),由路由 meta 传入 -const src = computed(() => (route.meta.legacySrc as string) || '') +// 本地旧页面:meta.legacySrc;远端:query.src(绝对 URL) +const src = computed(() => (route.query.src as string) || (route.meta.legacySrc as string) || '') +// 远端提供方服务名(签发用户令牌的 AudienceService) +const svc = computed(() => (route.query.svc as string) || '') +const isRemote = computed(() => /^https?:\/\//.test(src.value)) + +const iframeSrc = ref(src.value) const loading = ref(true) +const tokenError = ref(false) + +onMounted(async () => { + if (!isRemote.value || !svc.value) { + iframeSrc.value = src.value + return + } + // 远端:签发用户级短期令牌 → 拼 ?token= 透传员工身份 + try { + const r = await post<{ AccessToken: string }>('/API/MSync/UserToken/Issue', { + AudienceService: svc.value, + }) + const token = r?.AccessToken + if (!token) { + tokenError.value = true + } else { + const sep = src.value.includes('?') ? '&' : '?' + iframeSrc.value = `${src.value}${sep}token=${encodeURIComponent(token)}` + } + } catch { + tokenError.value = true + iframeSrc.value = src.value + } +}) function onLoad(): void { loading.value = false @@ -22,10 +54,17 @@ function onLoad(): void {