oauth允许选择账号

This commit is contained in:
chenxf 2026-03-30 14:00:12 +08:00
parent 9daa9ed6c4
commit 1d6c7c8d38
3 changed files with 304 additions and 0 deletions

View File

@ -146,3 +146,31 @@ export function oauth() {
method: 'get'
})
}
/**
* 获取 OAuth2 统一认证可选账号列表
* @param {string} code - 授权码
*/
export function getOAuth2Selection(code) {
return request({
url: '/aiccs-api/oauth2/unified/selection',
method: 'get',
params: { code }
})
}
/**
* OAuth2 选择账号登录
* @param {string} code - 授权码
* @param {string|number} userId - 选择的用户ID
*/
export function selectOAuth2Login(code, userId) {
const params = new FormData()
params.append('code', code)
params.append('userId', userId)
return request({
url: '/aiccs-api/oauth2/unified/select/login',
method: 'post',
data: params
})
}

View File

@ -42,6 +42,15 @@ export const constantRoutes = [
title: '登录'
}
},
{
path: '/oauth2',
component: () => import('@/views/oauth2/index'),
hidden: true,
meta: {
roles: ['guest'],
title: '账号选择'
}
},
{
name: 'Maintain',
path: '/maintain',

267
src/views/oauth2/index.vue Normal file
View File

@ -0,0 +1,267 @@
<template>
<div class="oauth2-container">
<div v-if="loading" class="loading-wrapper">
<i class="el-icon-loading" />
<span>加载中...</span>
</div>
<!-- 账号选择弹窗 -->
<el-dialog
title="请选择登录账号"
:visible.sync="dialogVisible"
:close-on-click-modal="false"
:close-on-press-escape="false"
:show-close="false"
width="400px"
center
>
<div class="account-list">
<div
v-for="user in userList"
:key="user.userId"
class="account-item"
:class="{ 'is-selected': selectedUserId === user.userId }"
@click="handleSelect(user.userId)"
>
<div class="account-info">
<i class="el-icon-user-solid account-icon" />
<div class="account-detail">
<div class="account-name">{{ user.realname || user.username }}</div>
<div v-if="user.realname" class="account-username">{{ user.username }}</div>
</div>
</div>
<i
v-if="selectedUserId === user.userId"
class="el-icon-check check-icon"
/>
</div>
</div>
<span slot="footer" class="dialog-footer">
<el-button
type="primary"
:disabled="!selectedUserId"
:loading="loginLoading"
style="width: 100%"
@click="handleLogin"
>
</el-button>
</span>
</el-dialog>
<!-- 错误提示 -->
<el-dialog
title="提示"
:visible.sync="errorVisible"
:close-on-click-modal="false"
:close-on-press-escape="false"
:show-close="false"
width="360px"
center
>
<div class="error-content">
<i class="el-icon-warning-outline" />
<p>{{ errorMessage }}</p>
</div>
<span slot="footer" class="dialog-footer">
<el-button type="primary" style="width: 100%" @click="goToLogin">
返回登录页
</el-button>
</span>
</el-dialog>
</div>
</template>
<script>
import { getOAuth2Selection, selectOAuth2Login } from '@/api/user.js'
export default {
name: 'OAuth2',
data() {
return {
loading: true,
dialogVisible: false,
errorVisible: false,
errorMessage: '',
code: '',
userList: [],
selectedUserId: null,
loginLoading: false
}
},
mounted() {
this.init()
},
methods: {
init() {
// URL code
this.code = this.$route.query.code
if (!this.code) {
this.showError('缺少授权码,请重新登录')
return
}
//
this.fetchUserList()
},
fetchUserList() {
this.loading = true
getOAuth2Selection(this.code)
.then(response => {
const { code, data, msg } = response
if (code === 0 && data && data.length > 0) {
this.userList = data
this.dialogVisible = true
} else if (code === 0 && (!data || data.length === 0)) {
this.showError('未找到可登录的账号')
} else {
this.showError(msg || '获取账号列表失败')
}
})
.catch(error => {
this.showError(error.message || '请求失败,请稍后重试')
})
.finally(() => {
this.loading = false
})
},
handleSelect(userId) {
this.selectedUserId = userId
},
handleLogin() {
if (!this.selectedUserId) {
this.$message.warning('请选择一个账号')
return
}
this.loginLoading = true
selectOAuth2Login(this.code, this.selectedUserId)
.then(response => {
const { code, msg } = response
if (code === 0) {
this.$message.success('登录成功')
this.dialogVisible = false
//
this.$router.push({ path: '/redirect' })
} else {
this.$message.error(msg || '登录失败')
}
})
.catch(error => {
this.$message.error(error.message || '登录请求失败')
})
.finally(() => {
this.loginLoading = false
})
},
showError(message) {
this.errorMessage = message
this.errorVisible = true
},
goToLogin() {
this.errorVisible = false
this.$router.push({ path: '/login' })
}
}
}
</script>
<style lang="scss" scoped>
.oauth2-container {
height: 100vh;
display: flex;
align-items: center;
justify-content: center;
background: #f5f7fa;
}
.loading-wrapper {
display: flex;
flex-direction: column;
align-items: center;
color: #909399;
i {
font-size: 32px;
margin-bottom: 12px;
}
}
.account-list {
max-height: 300px;
overflow-y: auto;
}
.account-item {
display: flex;
align-items: center;
justify-content: space-between;
padding: 16px;
margin-bottom: 8px;
border: 1px solid #e4e7ed;
border-radius: 8px;
cursor: pointer;
transition: all 0.3s;
&:hover {
border-color: #3296FA;
background: #f5f9ff;
}
&.is-selected {
border-color: #3296FA;
background: #e8f4ff;
}
&:last-child {
margin-bottom: 0;
}
}
.account-info {
display: flex;
align-items: center;
flex: 1;
}
.account-icon {
font-size: 36px;
color: #3296FA;
margin-right: 12px;
}
.account-detail {
flex: 1;
}
.account-name {
font-size: 16px;
font-weight: 500;
color: #303133;
}
.account-username {
font-size: 13px;
color: #909399;
margin-top: 4px;
}
.check-icon {
font-size: 20px;
color: #3296FA;
}
.error-content {
text-align: center;
padding: 20px 0;
i {
font-size: 48px;
color: #e6a23c;
margin-bottom: 16px;
}
p {
color: #606266;
font-size: 14px;
}
}
</style>