110 lines
3.4 KiB
Vue
110 lines
3.4 KiB
Vue
<script setup lang="ts">
|
||
import { onMounted, reactive, ref } from 'vue'
|
||
import { useRoute, useRouter } from 'vue-router'
|
||
import http, { apiErrorMessage } from '../api/http'
|
||
import { useAuthStore } from '../stores/auth'
|
||
|
||
const route = useRoute()
|
||
const router = useRouter()
|
||
const auth = useAuthStore()
|
||
const loading = ref(false)
|
||
const checking = ref(true)
|
||
const binding = reactive({
|
||
providerDisplayName: '学校统一身份认证',
|
||
externalUserName: '',
|
||
})
|
||
const form = reactive({ userName: '', password: '' })
|
||
|
||
function safeRedirect(value: unknown) {
|
||
const path = String(value ?? '')
|
||
return path.startsWith('/') && !path.startsWith('//') ? path : '/dashboard'
|
||
}
|
||
|
||
async function submit() {
|
||
if (!form.userName.trim() || !form.password) {
|
||
ElMessage.warning('请填写现有教务系统账号和密码。')
|
||
return
|
||
}
|
||
|
||
loading.value = true
|
||
try {
|
||
await auth.bindSso(String(route.query.code ?? ''), form.userName, form.password)
|
||
ElMessage.success('账户绑定成功。')
|
||
await router.replace(safeRedirect(route.query.redirect))
|
||
} catch (error) {
|
||
ElMessage.error(apiErrorMessage(error))
|
||
} finally {
|
||
loading.value = false
|
||
}
|
||
}
|
||
|
||
onMounted(async () => {
|
||
const code = String(route.query.code ?? '')
|
||
if (!code) {
|
||
await router.replace({ name: 'login', query: { ssoError: 'authentication_failed' } })
|
||
return
|
||
}
|
||
|
||
try {
|
||
const { data } = await http.get('/auth/sso/binding', { params: { code } })
|
||
binding.providerDisplayName = String(data.providerDisplayName || binding.providerDisplayName)
|
||
binding.externalUserName = String(data.externalUserName || '')
|
||
} catch (error) {
|
||
ElMessage.error(apiErrorMessage(error))
|
||
await router.replace({ name: 'login', query: { ssoError: 'binding_expired' } })
|
||
} finally {
|
||
checking.value = false
|
||
}
|
||
})
|
||
</script>
|
||
|
||
<template>
|
||
<main class="sso-binding-page">
|
||
<section v-loading="checking" class="sso-binding-card">
|
||
<div class="brand-mark" aria-hidden="true">
|
||
<span v-for="index in 9" :key="index" />
|
||
</div>
|
||
<span class="eyebrow">首次使用统一身份认证</span>
|
||
<h1>绑定现有教务系统账号</h1>
|
||
<p class="binding-description">
|
||
已通过{{ binding.providerDisplayName }}验证
|
||
<strong v-if="binding.externalUserName">{{ binding.externalUserName }}</strong>。
|
||
请输入一次现有教务系统账号和密码,今后即可直接使用单点登录。
|
||
</p>
|
||
|
||
<form @submit.prevent="submit">
|
||
<label>
|
||
<span>教务系统账号</span>
|
||
<el-input v-model="form.userName" size="large" autocomplete="username" />
|
||
</label>
|
||
<label>
|
||
<span>教务系统密码</span>
|
||
<el-input
|
||
v-model="form.password"
|
||
size="large"
|
||
type="password"
|
||
show-password
|
||
autocomplete="current-password"
|
||
@keyup.enter="submit"
|
||
/>
|
||
</label>
|
||
<el-button
|
||
class="binding-submit"
|
||
type="primary"
|
||
size="large"
|
||
native-type="submit"
|
||
:loading="loading"
|
||
:disabled="checking"
|
||
>
|
||
确认绑定并登录
|
||
</el-button>
|
||
</form>
|
||
|
||
<p class="binding-security-note">
|
||
绑定只建立登录关联,不会修改您的角色、学院或人员档案。
|
||
</p>
|
||
<router-link to="/login">取消并返回登录页</router-link>
|
||
</section>
|
||
</main>
|
||
</template>
|