diff --git a/apps/web-antd/.env.development b/apps/web-antd/.env.development index 59a3d9e43..261977932 100644 --- a/apps/web-antd/.env.development +++ b/apps/web-antd/.env.development @@ -19,3 +19,6 @@ VITE_INJECT_APP_LOADING=true VITE_APP_DEFAULT_USERNAME=admin # 默认登录密码 VITE_APP_DEFAULT_PASSWORD=admin123 + +# 物联运维平台前端地址(用于业务平台 -> IoT 平台 SSO 跳转) +VITE_IOT_BASE_URL=http://127.0.0.1:5667 diff --git a/apps/web-antd/.env.production b/apps/web-antd/.env.production index 2bf6bba25..01be409cd 100644 --- a/apps/web-antd/.env.production +++ b/apps/web-antd/.env.production @@ -21,3 +21,6 @@ VITE_INJECT_APP_LOADING=true # 打包后是否生成dist.zip VITE_ARCHIVER=true + +# 物联运维平台前端地址(SSO 跳转目标),生产部署时按实际域名修改 +VITE_IOT_BASE_URL=https://iot.example.com diff --git a/apps/web-antd/src/api/core/sso.ts b/apps/web-antd/src/api/core/sso.ts new file mode 100644 index 000000000..162dbcb49 --- /dev/null +++ b/apps/web-antd/src/api/core/sso.ts @@ -0,0 +1,52 @@ +import { requestClient } from '#/api/request'; + +/** + * SSO 单点登录相关接口(对接 /system/sso/callback) + * 用于从其他子系统(如 IoT 运维平台)跳转回来时,用 code 换 access_token + */ +export namespace SsoApi { + /** 后端 OAuth2 标准响应(snake_case),仅在 API 层内部使用 */ + export interface SsoCallbackRawResult { + access_token: string; + refresh_token: string; + expires_in: number; + token_type: string; + } + /** 前端统一使用 camelCase */ + export interface SsoCallbackResult { + accessToken: string; + refreshToken: string; + expiresIn: number; + tokenType: string; + } +} + +/** + * 用 SSO code 换 access_token。 + * client_secret 不在前端,由后端从配置读取。 + * + * @param clientId 当前系统的 client_id(业务平台 = 'biz',物联运维 = 'iot') + * @param code 授权码(从 URL 读取) + * @param redirectUri 回调地址(必须与发起授权时一致,且需在 OAuth2 客户端的 redirectUris 白名单内) + * @param state 发起授权时的随机串,从 URL 读出原样回传(后端校验一致性 + CSRF 防护) + */ +export async function ssoCallback( + clientId: string, + code: string, + redirectUri: string, + state?: string, +): Promise { + const raw = await requestClient.post( + '/system/sso/callback', + null, + { + params: { clientId, code, redirectUri, state }, + }, + ); + return { + accessToken: raw.access_token, + refreshToken: raw.refresh_token, + expiresIn: raw.expires_in, + tokenType: raw.token_type, + }; +} diff --git a/apps/web-antd/src/layouts/basic.vue b/apps/web-antd/src/layouts/basic.vue index 1dc10ee8a..f1405eb23 100644 --- a/apps/web-antd/src/layouts/basic.vue +++ b/apps/web-antd/src/layouts/basic.vue @@ -1,20 +1,19 @@ + + diff --git a/packages/effects/layouts/src/widgets/index.ts b/packages/effects/layouts/src/widgets/index.ts index 665cdb9e9..7d6eccdb6 100644 --- a/packages/effects/layouts/src/widgets/index.ts +++ b/packages/effects/layouts/src/widgets/index.ts @@ -8,6 +8,7 @@ export { default as AuthenticationLayoutToggle } from './layout-toggle.vue'; export * from './lock-screen'; export * from './notification'; export * from './preferences'; +export * from './project-dropdown'; export * from './tenant-dropdown'; export * from './theme-toggle'; export * from './timezone'; diff --git a/packages/effects/layouts/src/widgets/project-dropdown/index.ts b/packages/effects/layouts/src/widgets/project-dropdown/index.ts new file mode 100644 index 000000000..8ae16ff57 --- /dev/null +++ b/packages/effects/layouts/src/widgets/project-dropdown/index.ts @@ -0,0 +1 @@ +export { default as ProjectDropdown } from './project-dropdown.vue'; diff --git a/packages/effects/layouts/src/widgets/project-dropdown/project-dropdown.vue b/packages/effects/layouts/src/widgets/project-dropdown/project-dropdown.vue new file mode 100644 index 000000000..463d53d09 --- /dev/null +++ b/packages/effects/layouts/src/widgets/project-dropdown/project-dropdown.vue @@ -0,0 +1,91 @@ + +