feat: integrate ZYT admin with IAM Hub

This commit is contained in:
2026-09-08 13:26:41 +08:00
parent cf3fbdc5ef
commit a5f4c4472d
856 changed files with 3585 additions and 2371 deletions
+19 -1
View File
@@ -28,7 +28,25 @@ export function getWorkWechatConfig() {
// 企业微信授权登录
export function workWechatLogin(params: { code: string }) {
return request.post({ url: '/login/workWechatLogin', params: { ...params, terminal: config.terminal } })
return request.post({
url: '/login/workWechatLogin',
params: { ...params, terminal: config.terminal }
})
}
// IAM Hub 登录配置
export function getIamHubConfig() {
return request.get({ url: '/login/iamConfig' })
}
// IAM Hub 一次性回调码换取本平台会话
export function iamHubExchange(params: { code: string }) {
return request.post({ url: '/login/iamExchange', params })
}
export function getIamHubStartUrl() {
const baseUrl = config.baseUrl.replace(/\/+$/, '')
return `${baseUrl}/${config.urlPrefix}/login/iamStart`
}
// 绑定企业微信
+16 -9
View File
@@ -1,7 +1,7 @@
import { defineStore } from 'pinia'
import type { RouteRecordRaw } from 'vue-router'
import { getUserInfo, login, logout, workWechatLogin } from '@/api/user'
import { getUserInfo, iamHubExchange, login, logout, workWechatLogin } from '@/api/user'
import { TOKEN_KEY } from '@/enums/cacheEnums'
import { PageEnum } from '@/enums/pageEnum'
import router, { filterAsyncRoutes } from '@/router'
@@ -31,6 +31,12 @@ const useUserStore = defineStore({
}),
getters: {},
actions: {
applyLoginResult(data: any) {
this.token = data.token
this.isPaw = data.is_paw ?? 1
cache.set(TOKEN_KEY, data.token)
return data
},
resetState() {
this.token = ''
this.userInfo = {}
@@ -45,10 +51,7 @@ const useUserStore = defineStore({
password: password
})
.then((data) => {
this.token = data.token
this.isPaw = data.is_paw ?? 1
cache.set(TOKEN_KEY, data.token)
resolve(data)
resolve(this.applyLoginResult(data))
})
.catch((error) => {
reject(error)
@@ -73,16 +76,20 @@ const useUserStore = defineStore({
return new Promise((resolve, reject) => {
workWechatLogin({ code })
.then((data) => {
this.token = data.token
this.isPaw = data.is_paw ?? 1
cache.set(TOKEN_KEY, data.token)
resolve(data)
resolve(this.applyLoginResult(data))
})
.catch((error) => {
reject(error)
})
})
},
iamHubLogin(code: string) {
return new Promise((resolve, reject) => {
iamHubExchange({ code })
.then((data) => resolve(this.applyLoginResult(data)))
.catch(reject)
})
},
getUserInfo() {
return new Promise((resolve, reject) => {
getUserInfo()
+80 -10
View File
@@ -11,11 +11,13 @@
<div class="text-center text-3xl font-medium mb-8">{{ config.web_name }}</div>
<!-- 企业微信自动授权中 -->
<div v-if="wxWorkAutoLogin" class="text-center py-10">
<div v-if="wxWorkAutoLogin || iamAutoLogin" class="text-center py-10">
<el-icon class="is-loading mb-4" :size="40" color="var(--el-color-primary)">
<Loading />
</el-icon>
<div class="text-gray-500">企业微信授权登录中...</div>
<div class="text-gray-500">
{{ iamAutoLogin ? '公司统一身份登录中...' : '企业微信授权登录中...' }}
</div>
</div>
<template v-else>
@@ -58,21 +60,42 @@
<div class="mb-5">
<el-checkbox v-model="remAccount" label="记住账号"></el-checkbox>
</div>
<el-button type="primary" size="large" :loading="isLock" @click="lockLogin">
<el-button
type="primary"
size="large"
:loading="isLock"
@click="lockLogin"
>
登录
</el-button>
<template v-if="iamHubEnabled">
<el-divider></el-divider>
<el-button size="large" class="w-full" @click="handleIamHubLogin">
公司员工统一登录
</el-button>
</template>
</template>
<!-- 企业微信扫码登录非企业微信内浏览器 -->
<template v-if="loginMode === 'wxwork'">
<div class="wxwork-qrcode-wrap">
<div v-if="wxWorkLoading" class="text-center py-10">
<el-icon class="is-loading" :size="32" color="var(--el-color-primary)">
<el-icon
class="is-loading"
:size="32"
color="var(--el-color-primary)"
>
<Loading />
</el-icon>
<div class="mt-2 text-gray-400 text-sm">加载企业微信扫码...</div>
<div class="mt-2 text-gray-400 text-sm">
加载企业微信扫码...
</div>
</div>
<div v-else id="wxwork_qrcode_container" class="wxwork-qrcode"></div>
<div
v-else
id="wxwork_qrcode_container"
class="wxwork-qrcode"
></div>
</div>
<div class="text-center text-sm text-gray-400 mt-4">
请使用企业微信扫描二维码登录
@@ -99,7 +122,7 @@ import LayoutFooter from '@/layout/components/footer.vue'
import useAppStore from '@/stores/modules/app'
import useUserStore from '@/stores/modules/user'
import cache from '@/utils/cache'
import { getWorkWechatConfig } from '@/api/user'
import { getIamHubConfig, getIamHubStartUrl, getWorkWechatConfig } from '@/api/user'
const passwordRef = shallowRef<InputInstance>()
const formRef = shallowRef<FormInstance>()
@@ -124,6 +147,8 @@ const wxWorkEnabled = ref(false)
const wxWorkConfig = ref<{ corp_id: string; agent_id: string }>({ corp_id: '', agent_id: '' })
const wxWorkAutoLogin = ref(false)
const wxWorkLoading = ref(false)
const iamHubEnabled = ref(false)
const iamAutoLogin = ref(false)
const isInWxWork = () => /wxwork/i.test(navigator.userAgent)
@@ -160,8 +185,14 @@ const handleLogin = async () => {
const { isLock, lockFn: lockLogin } = useLockFn(handleLogin)
const handleIamHubLogin = () => {
window.location.href = getIamHubStartUrl()
}
const redirectAfterLogin = () => {
const { query: { redirect } } = route
const {
query: { redirect }
} = route
const path = typeof redirect === 'string' ? redirect : PageEnum.INDEX
router.push(path)
}
@@ -190,6 +221,22 @@ const handleWxWorkCodeLogin = async (code: string) => {
}
}
const handleIamHubCodeLogin = async (code: string) => {
iamAutoLogin.value = true
try {
const result: any = await userStore.iamHubLogin(code)
if (result.need_bind_work_wechat) {
router.push('/bind-work-wechat')
return
}
redirectAfterLogin()
} catch (error: any) {
console.error('IAM Hub 登录失败:', error)
ElMessage.error(error?.msg || error?.message || '公司统一身份登录失败,请重新发起登录')
iamAutoLogin.value = false
}
}
// 加载企业微信扫码 JS SDK 并渲染二维码
const renderWxWorkQRCode = async () => {
if (!wxWorkConfig.value.corp_id) return
@@ -222,7 +269,7 @@ const renderWxWorkQRCode = async () => {
lang: 'zh',
state: 'admin_login',
href: '',
self_redirect: false,
self_redirect: false
})
}
@@ -241,6 +288,27 @@ onMounted(async () => {
formData.account = value.account
}
const hashParams = new URLSearchParams(window.location.hash.replace(/^#/, ''))
const iamCode = hashParams.get('iam_code')
const iamError = hashParams.get('iam_error')
if (iamCode || iamError) {
window.history.replaceState({}, '', window.location.pathname + window.location.search)
}
if (iamCode) {
handleIamHubCodeLogin(iamCode)
return
}
if (iamError) {
ElMessage.error('公司统一身份登录失败,请重新发起登录')
}
try {
const iamConfig = await getIamHubConfig()
iamHubEnabled.value = iamConfig?.enabled === true
} catch (e) {
console.warn('获取 IAM Hub 登录配置失败:', e)
}
// 检查 URL 中是否有企业微信回调 code
const urlParams = new URLSearchParams(window.location.search)
const wxCode = urlParams.get('code')
@@ -248,7 +316,9 @@ onMounted(async () => {
if (wxCode && wxState === 'admin_login') {
// 清除 URL 中的 code 参数(避免刷新重复使用)
const cleanUrl = window.location.pathname + (route.query.redirect ? `?redirect=${route.query.redirect}` : '')
const cleanUrl =
window.location.pathname +
(route.query.redirect ? `?redirect=${route.query.redirect}` : '')
window.history.replaceState({}, '', cleanUrl)
handleWxWorkCodeLogin(wxCode)