Files
Academic-Affairs-System/web/src/views/LoginView.vue
T
2026-08-08 17:13:01 +08:00

134 lines
4.6 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<script setup lang="ts">
import { onMounted, reactive, ref } from 'vue'
import { useRoute, useRouter } from 'vue-router'
import { apiErrorMessage } from '../api/http'
import { useAuthStore } from '../stores/auth'
import http from '../api/http'
const route = useRoute()
const router = useRouter()
const auth = useAuthStore()
const loading = ref(false)
const ssoLoading = ref(false)
const sso = reactive({ enabled: false, displayName: '学校统一身份认证' })
const form = reactive({
userName: String(route.query.userName ?? ''),
password: '',
})
async function submit() {
loading.value = true
try {
await auth.login(form.userName, form.password)
await router.replace(String(route.query.redirect ?? '/dashboard'))
} catch (error) {
ElMessage.error(apiErrorMessage(error))
} finally {
loading.value = false
}
}
function startSso() {
ssoLoading.value = true
const apiBaseUrl = String(import.meta.env.VITE_API_BASE_URL ?? '/api').replace(/\/$/, '')
const redirect = String(route.query.redirect ?? '/dashboard')
window.location.assign(
`${apiBaseUrl}/auth/sso/login?returnUrl=${encodeURIComponent(redirect)}`,
)
}
const ssoErrors: Record<string, string> = {
authentication_failed: '统一身份认证未完成,请重新尝试。',
missing_subject: 'Keycloak 未返回用户唯一标识,请联系管理员检查客户端映射。',
account_disabled: '教务系统账号已停用或锁定,请联系管理员。',
account_link_failed: '统一身份账号绑定失败,请联系管理员。',
account_update_failed: '登录状态更新失败,请稍后重试。',
binding_expired: '账户绑定请求已失效,请重新使用统一身份认证登录。',
configuration_error: '统一身份认证回调地址配置不正确,请联系管理员。',
}
onMounted(async () => {
const ssoError = String(route.query.ssoError ?? '')
if (ssoError) ElMessage.error(ssoErrors[ssoError] ?? '统一身份认证失败,请重新尝试。')
try {
const { data } = await http.get('/auth/sso/settings')
sso.enabled = Boolean(data.enabled)
sso.displayName = String(data.displayName || sso.displayName)
} catch {
sso.enabled = false
}
})
</script>
<template>
<main class="login-page">
<section class="login-story">
<div class="story-brand">
<div class="brand-mark large" aria-hidden="true">
<span v-for="index in 9" :key="index" />
</div>
<div>
<strong>明序教务</strong>
<small>MINGXU ACADEMIC SYSTEM</small>
</div>
</div>
<div class="story-copy">
<span class="eyebrow">大学教务管理平台</span>
<h1>让每一项教学安排<br />都有清晰的来处与去向</h1>
<p>从组织档案开始逐步连接培养方案教学任务排课选课与成绩</p>
</div>
<div class="schedule-signature" aria-hidden="true">
<div v-for="item in 20" :key="item" :class="{ active: [3, 8, 9, 14, 18].includes(item) }">
<span v-if="[3, 8, 9, 14, 18].includes(item)">教学</span>
</div>
</div>
</section>
<section class="login-panel">
<form class="login-form" @submit.prevent="submit">
<div class="form-intro">
<span>欢迎回来</span>
<h2>登录教务工作台</h2>
<p>学生使用学号教师使用工号登录管理人员使用学校分配的账号</p>
</div>
<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="login-submit"
type="primary"
size="large"
native-type="submit"
:loading="loading"
>
进入工作台
</el-button>
<div v-if="sso.enabled" class="login-divider"><span></span></div>
<el-button
v-if="sso.enabled"
class="sso-login-submit"
size="large"
:loading="ssoLoading"
@click="startSso"
>
使用{{ sso.displayName }}登录
</el-button>
<router-link class="public-timetable-link" to="/timetable">无需登录查询班级课表 </router-link>
<router-link class="account-activation-link" to="/activate">学生首次登录自助激活账号 </router-link>
</form>
</section>
</main>
</template>