diff --git a/src/api/app.ts b/src/api/app.ts index 188a4f1..50245e7 100644 --- a/src/api/app.ts +++ b/src/api/app.ts @@ -35,6 +35,12 @@ export interface CompanyScopeItem { Installed?: number } +/** RAM 角色粒度授权候选(当前租户启用角色) */ +export interface RoleScopeItem { + RoleUUID: string + RoleName: string +} + /** 个人授权申请项(租户管理员审批台) */ export interface PendingItem { UserAppUUID: string @@ -65,14 +71,18 @@ export const appApi = { /** 我的应用(当前用户可用) */ myApps: (): Promise<{ list: AppItem[]; total: number }> => get<{ list: AppItem[]; total: number }>('/API/App/MyApps'), - /** 管理员可安装应用的公司列表(按权限范围); + /** 管理员可安装应用的公司列表(按权限范围)+ 租户角色列表(RAM 授权候选); * 传 AppUUID 时附带各公司安装状态(Installed),供管理弹窗用 */ - manageCompanies: (AppUUID?: string): Promise<{ Scope: string; Companies: CompanyScopeItem[] }> => - get<{ Scope: string; Companies: CompanyScopeItem[] }>('/API/App/ManageCompanies', AppUUID ? { AppUUID } : {}), + manageCompanies: (AppUUID?: string): Promise<{ Scope: string; Companies: CompanyScopeItem[]; Roles: RoleScopeItem[] }> => + get<{ Scope: string; Companies: CompanyScopeItem[]; Roles: RoleScopeItem[] }>( + '/API/App/ManageCompanies', + AppUUID ? { AppUUID } : {}, + ), - /** 管理员:安装企业功能(不传 CompanyUUIDs=全租户;传 CompanyUUIDs=指定公司) */ - install: (AppUUID: string, CompanyUUIDs?: string[]): Promise => - post('/API/App/Install', { AppUUID, CompanyUUIDs: CompanyUUIDs || [] }), + /** 管理员:安装企业功能(不传 CompanyUUIDs=全租户;传 CompanyUUIDs=指定公司); + * GrantRoles=RAM 角色粒度授权(可选):指定角色可见该应用(安装范围内) */ + install: (AppUUID: string, CompanyUUIDs?: string[], GrantRoles?: string[]): Promise => + post('/API/App/Install', { AppUUID, CompanyUUIDs: CompanyUUIDs || [], GrantRoles: GrantRoles || [] }), /** 管理员:停用企业功能(传 CompanyUUIDs=停用指定公司;TenantOnly=仅停用全租户;都不传=停全租户+当前公司) */ uninstall: (AppUUID: string, CompanyUUIDs?: string[], TenantOnly?: boolean): Promise => diff --git a/src/views/app/AppMarket.vue b/src/views/app/AppMarket.vue index 8e7deb1..c9b7fa0 100644 --- a/src/views/app/AppMarket.vue +++ b/src/views/app/AppMarket.vue @@ -10,7 +10,7 @@ import { ref, computed, onMounted } from 'vue' import { useRouter } from 'vue-router' import { ElMessage, ElMessageBox } from 'element-plus' import { Grid } from '@element-plus/icons-vue' -import { appApi, type AppItem, type CompanyScopeItem } from '@/api/app' +import { appApi, type AppItem, type CompanyScopeItem, type RoleScopeItem } from '@/api/app' import { getAppModule, appOpenTarget } from '@/apps' import { useUserStore } from '@/stores/user' import { setPageTitle } from '@/utils/serverScope' @@ -28,6 +28,8 @@ const loading = ref(false) const manageableCompanies = ref([]) /** 权限范围:all=跨租户 / tenant=租户内 / company=指定公司 */ const manageScope = ref('') +/** 当前租户角色列表(RAM 角色粒度授权候选) */ +const tenantRoles = ref([]) function typeLabel(t: string): { text: string; type: 'success' | 'info' | 'warning' } { if (t === 'enterprise') return { text: '企业功能', type: 'success' } @@ -60,12 +62,15 @@ const installTargetApp = ref(null) const installScope = ref<'tenant' | 'company'>('tenant') /** 选中的公司 UUID 列表 */ const selectedCompanies = ref([]) +/** RAM 角色粒度授权:选中的角色 UUID 列表(不选=全员可见) */ +const selectedRoles = ref([]) -/** 打开安装弹窗:拉取可管理公司 */ +/** 打开安装弹窗:拉取可管理公司 + 租户角色(RAM 授权候选) */ async function openInstallDialog(a: AppItem): Promise { installTargetApp.value = a installScope.value = 'tenant' selectedCompanies.value = [] + selectedRoles.value = [] installDialogVisible.value = true if (isAdmin.value && !manageableCompanies.value.length) { installDialogLoading.value = true @@ -73,6 +78,7 @@ async function openInstallDialog(a: AppItem): Promise { const r = await appApi.manageCompanies() manageableCompanies.value = r.Companies || [] manageScope.value = r.Scope || '' + tenantRoles.value = r.Roles || [] } catch { /* 拦截器已提示 */ } finally { installDialogLoading.value = false } @@ -86,11 +92,14 @@ async function onConfirmInstall(): Promise { installDialogLoading.value = true try { const companyUUIDs = installScope.value === 'company' ? selectedCompanies.value : [] - await appApi.install(a.AppUUID, companyUUIDs) + await appApi.install(a.AppUUID, companyUUIDs, selectedRoles.value) const scopeText = installScope.value === 'company' ? `已为 ${selectedCompanies.value.length} 家公司安装` : '已全租户安装(全员可用)' - ElMessage.success(`「${a.AppName}」${scopeText}`) + const ramText = selectedRoles.value.length + ? `,RAM 授权 ${selectedRoles.value.length} 个角色可见` + : '' + ElMessage.success(`「${a.AppName}」${scopeText}${ramText}`) installDialogVisible.value = false await loadMarket() } catch { /* 拦截器已提示 */ } finally { @@ -380,6 +389,29 @@ onMounted(loadMarket)

您可管理本租户下全部公司。

您是跨租户管理员,可管理全部公司。

+ + +
+
RAM 角色授权(可选)
+ + + +

勾选后,仅被授权角色的员工在菜单中可见该应用;其他角色不可见。

+