- apps/index.ts:注册表新增 ConfigPage(替换通用「配置项」表单)与 ConfigPanels(注入自定义区块),新增 getAppConfigPage/getAppConfigPanels,并登记 subscription 应用 - AppConfigPage.vue:渲染应用注入的配置页/区块(下发 AppUUID/公司/范围上下文);右上角范围下拉只列「该应用已安装 且 有管理权」的公司,全租户安装视为全部公司已装 - subscription:注入「订阅关系概览」区块(按公司展示作为提供者/消费者参与的产品)
210 lines
6.8 KiB
TypeScript
210 lines
6.8 KiB
TypeScript
/**
|
||
* 前端应用注册表(AppFrontendRegistry)
|
||
*
|
||
* 与后端应用清单(gvsds/apps/gerp/app_manifests.py)一一对应的前端侧注册:
|
||
* 每个应用声明其前端入口(路由/类型/旧页面路径),供"应用中心 · 我的应用"
|
||
* 打开应用、及未来动态路由注入使用。
|
||
*
|
||
* 数据契约(对齐后端 App 表):
|
||
* AppCode 应用编码
|
||
* Title 应用名
|
||
* Icon 图标(Element Plus 图标名 / FontAwesome 类名)
|
||
* FrontendRoute 前端路由路径(打开应用跳转目标)
|
||
* FrontendType vue=前端组件 / legacy=旧页面 iframe 直载
|
||
* LegacySrc legacy 页面原始路径(如 pages/private/CreateE/shop.html)
|
||
*
|
||
* MSYNC 扩展:未来子节点上报应用清单时,通过 registerRemoteApp() 动态登记,
|
||
* 前端可据此动态 addRoute(路由由后端菜单驱动,此处登记即可)。
|
||
*/
|
||
import type { Component } from 'vue'
|
||
import type { RouteLocationRaw } from 'vue-router'
|
||
|
||
/** 注入到应用配置页的自定义区块(由应用自己声明与提供) */
|
||
export interface AppConfigPanel {
|
||
/** 区块唯一键(v-for 渲染用) */
|
||
Key: string
|
||
/** 区块标题 */
|
||
Title: string
|
||
/** 区块副标题(可选) */
|
||
Sub?: string
|
||
/** 是否仅在选中「具体公司」时展示(租户默认层无公司上下文时隐藏) */
|
||
CompanyOnly?: boolean
|
||
/** 区块组件(异步加载) */
|
||
Component: () => Promise<{ default: Component }>
|
||
}
|
||
|
||
/** 配置页注入上下文:传给应用自定义配置页 / 自定义区块 */
|
||
export interface AppConfigContext {
|
||
AppUUID: string
|
||
AppName: string
|
||
/** '' = 租户默认(全租户共享);否则为公司 UUID */
|
||
CompanyUUID: string
|
||
/** 配置管理范围:all(跨租户)/ tenant(租户内)/ company(指定公司) */
|
||
Scope: string
|
||
/** 当前范围展示名(用于区块内文案) */
|
||
ScopeLabel: string
|
||
}
|
||
|
||
export interface AppFrontendModule {
|
||
AppCode: string
|
||
Title: string
|
||
Icon: string
|
||
FrontendRoute: string
|
||
FrontendType: 'vue' | 'legacy'
|
||
LegacySrc: string
|
||
/** 远端注入(MSYNC):提供方服务名(打开远端 legacy 页面时签发用户令牌用) */
|
||
ServiceName?: string
|
||
/** 应用自定义配置页:存在时替换配置页的通用「配置项」表单 */
|
||
ConfigPage?: () => Promise<{ default: Component }>
|
||
/** 应用注入配置页的自定义区块:渲染在「配置项」区块之后 */
|
||
ConfigPanels?: AppConfigPanel[]
|
||
}
|
||
|
||
/** 打开应用的跳转目标(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')
|
||
}
|
||
|
||
const builtin: Record<string, AppFrontendModule> = {
|
||
mailbox: {
|
||
AppCode: 'mailbox',
|
||
Title: '企业邮箱',
|
||
Icon: 'Message',
|
||
FrontendRoute: '/mail/mailbox',
|
||
FrontendType: 'vue',
|
||
LegacySrc: '',
|
||
},
|
||
gitea: {
|
||
AppCode: 'gitea',
|
||
Title: '代码托管 Gitea',
|
||
Icon: 'Connection',
|
||
FrontendRoute: '/gitea/mine',
|
||
FrontendType: 'vue',
|
||
LegacySrc: '',
|
||
},
|
||
talk: {
|
||
AppCode: 'talk',
|
||
Title: 'Talk 企业通讯',
|
||
Icon: 'ChatDotRound',
|
||
FrontendRoute: '/talk',
|
||
FrontendType: 'vue',
|
||
LegacySrc: '',
|
||
},
|
||
shop: {
|
||
AppCode: 'shop',
|
||
Title: '枢机协律-商店',
|
||
Icon: 'ShoppingCart',
|
||
FrontendRoute: '/private/CreateE/shop',
|
||
FrontendType: 'legacy',
|
||
LegacySrc: 'pages/private/CreateE/shop.html',
|
||
},
|
||
'store-value': {
|
||
AppCode: 'store-value',
|
||
Title: '枢机协律-储值',
|
||
Icon: 'Wallet',
|
||
FrontendRoute: '/private/CreateE/StoreValue',
|
||
FrontendType: 'legacy',
|
||
LegacySrc: 'pages/private/CreateE/StoreValue.html',
|
||
},
|
||
wallet: {
|
||
AppCode: 'wallet',
|
||
Title: '枢机协律-支付系统',
|
||
Icon: 'Wallet',
|
||
FrontendRoute: '/private/CreateE/wallet',
|
||
FrontendType: 'legacy',
|
||
LegacySrc: 'pages/private/CreateE/wallet.html',
|
||
},
|
||
enterprise: {
|
||
AppCode: 'enterprise',
|
||
Title: '枢机协律-模拟企业',
|
||
Icon: 'OfficeBuilding',
|
||
FrontendRoute: '/private/CreateE/enterprise',
|
||
FrontendType: 'legacy',
|
||
LegacySrc: 'pages/private/CreateE/enterprise.html',
|
||
},
|
||
'dingcalc-finance': {
|
||
AppCode: 'dingcalc-finance',
|
||
Title: '鼎算财务',
|
||
Icon: 'Money',
|
||
FrontendRoute: '/finance/voucher',
|
||
FrontendType: 'vue',
|
||
LegacySrc: '',
|
||
},
|
||
'product-order': {
|
||
AppCode: 'product-order',
|
||
Title: '产品与订单',
|
||
Icon: 'Goods',
|
||
FrontendRoute: '/inventory/products',
|
||
FrontendType: 'vue',
|
||
LegacySrc: '',
|
||
},
|
||
ehrm: {
|
||
AppCode: 'ehrm',
|
||
Title: '人力资源管理扩展',
|
||
Icon: 'UserFilled',
|
||
FrontendRoute: '/ehrm/profile',
|
||
FrontendType: 'vue',
|
||
LegacySrc: '',
|
||
},
|
||
oa: {
|
||
AppCode: 'oa',
|
||
Title: '办公自动化流程',
|
||
Icon: 'Files',
|
||
FrontendRoute: '/oa/leave',
|
||
FrontendType: 'vue',
|
||
LegacySrc: '',
|
||
},
|
||
subscription: {
|
||
AppCode: 'subscription',
|
||
Title: '订阅式软件提供',
|
||
Icon: 'Connection',
|
||
FrontendRoute: '/subscription/group',
|
||
FrontendType: 'vue',
|
||
LegacySrc: '',
|
||
// 注入到「应用配置页」的自定义区块:展示当前范围作为提供者/消费者参与的产品
|
||
ConfigPanels: [
|
||
{
|
||
Key: 'subscription-relation-overview',
|
||
Title: '订阅关系概览',
|
||
Sub: '当前范围作为提供者 / 消费者参与的产品(按公司隔离)',
|
||
CompanyOnly: true,
|
||
Component: () => import('@/views/subscription/ConfigPanel.vue'),
|
||
},
|
||
],
|
||
},
|
||
}
|
||
|
||
/** 远端注册表(MSYNC 上报的应用,本地内置同名优先) */
|
||
const remote: Record<string, AppFrontendModule> = {}
|
||
|
||
/** 注册 MSYNC 远端应用模块(AppCode 冲突时远端生效,覆盖内置) */
|
||
export function registerRemoteApp(mod: AppFrontendModule): void {
|
||
remote[mod.AppCode] = mod
|
||
}
|
||
|
||
export function getAppModule(AppCode: string): AppFrontendModule | undefined {
|
||
return remote[AppCode] || builtin[AppCode]
|
||
}
|
||
|
||
export function listAppModules(): AppFrontendModule[] {
|
||
const merged = { ...remote, ...builtin }
|
||
return Object.values(merged)
|
||
}
|
||
|
||
/** 应用自定义配置页(未声明返回 undefined → 配置页保留通用「配置项」表单) */
|
||
export function getAppConfigPage(AppCode: string): (() => Promise<{ default: Component }>) | undefined {
|
||
return getAppModule(AppCode)?.ConfigPage
|
||
}
|
||
|
||
/** 应用注入到配置页的自定义区块(未声明返回空数组) */
|
||
export function getAppConfigPanels(AppCode: string): AppConfigPanel[] {
|
||
return getAppModule(AppCode)?.ConfigPanels || []
|
||
}
|