From a1d7e81859f554f3a53680cc35f0f49bf1f77098 Mon Sep 17 00:00:00 2001
From: wwf <1971391498@qq.com>
Date: 星期四, 14 五月 2026 14:37:02 +0800
Subject: [PATCH] 导入项目
---
src/views/Login/components/SSOLogin.vue | 199 +++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 199 insertions(+), 0 deletions(-)
diff --git a/src/views/Login/components/SSOLogin.vue b/src/views/Login/components/SSOLogin.vue
new file mode 100644
index 0000000..99e359e
--- /dev/null
+++ b/src/views/Login/components/SSOLogin.vue
@@ -0,0 +1,199 @@
+<template>
+ <div v-show="ssoVisible" class="form-cont">
+ <!-- 搴旂敤鍚� -->
+ <LoginFormTitle class="w-full" />
+ <el-tabs class="form" style="float: none" value="uname">
+ <el-tab-pane :label="client.name" name="uname" />
+ </el-tabs>
+ <div>
+ <el-form :model="formData" class="login-form">
+ <!-- 鎺堟潈鑼冨洿鐨勯�夋嫨 -->
+ 姝ょ涓夋柟搴旂敤璇锋眰鑾峰緱浠ヤ笅鏉冮檺锛�
+ <el-form-item prop="scopes">
+ <el-checkbox-group v-model="formData.scopes">
+ <el-checkbox
+ v-for="scope in queryParams.scopes"
+ :key="scope"
+ :value="scope"
+ class="block mb-[-10px]"
+ >
+ {{ formatScope(scope) }}
+ </el-checkbox>
+ </el-checkbox-group>
+ </el-form-item>
+ <!-- 涓嬫柟鐨勭櫥褰曟寜閽� -->
+ <el-form-item class="w-full">
+ <el-button
+ :loading="formLoading"
+ class="w-3/5"
+ type="primary"
+ @click.prevent="handleAuthorize(true)"
+ >
+ <span v-if="!formLoading">鍚屾剰鎺堟潈</span>
+ <span v-else>鎺� 鏉� 涓�...</span>
+ </el-button>
+ <el-button class="w-3/10" @click.prevent="handleAuthorize(false)">鎷掔粷</el-button>
+ </el-form-item>
+ </el-form>
+ </div>
+ </div>
+</template>
+<script lang="ts" setup>
+import LoginFormTitle from './LoginFormTitle.vue'
+import * as OAuth2Api from '@/api/login/oauth2'
+import { LoginStateEnum, useLoginState } from './useLogin'
+import type { RouteLocationNormalizedLoaded } from 'vue-router'
+
+defineOptions({ name: 'SSOLogin' })
+
+const route = useRoute() // 璺敱
+const { currentRoute } = useRouter() // 璺敱
+const { getLoginState, setLoginState } = useLoginState()
+
+const client = ref({
+ // 瀹㈡埛绔俊鎭�
+ name: '',
+ logo: ''
+})
+interface queryType {
+ responseType: string
+ clientId: string
+ redirectUri: string
+ state: string
+ scopes: string[]
+}
+const queryParams = reactive<queryType>({
+ // URL 涓婄殑 client_id銆乻cope 绛夊弬鏁�
+ responseType: '',
+ clientId: '',
+ redirectUri: '',
+ state: '',
+ scopes: [] // 浼樺厛浠� query 鍙傛暟鑾峰彇锛涘鏋滄湭浼犻�掞紝浠庡悗绔幏鍙�
+})
+const ssoVisible = computed(() => unref(getLoginState) === LoginStateEnum.SSO) // 鏄惁灞曠ず SSO 鐧诲綍鐨勮〃鍗�
+interface formType {
+ scopes: string[]
+}
+const formData = reactive<formType>({
+ scopes: [] // 宸查�変腑鐨� scope 鏁扮粍
+})
+const formLoading = ref(false) // 琛ㄥ崟鏄惁鎻愪氦涓�
+
+/** 鍒濆鍖栨巿鏉冧俊鎭� */
+const init = async () => {
+ // 闃叉鍦ㄦ病鏈夌櫥褰曠殑鎯呭喌涓嬪惊鐜脊绐�
+ if (typeof route.query.client_id === 'undefined') return
+ // 瑙f瀽鍙傛暟
+ // 渚嬪璇淬�愯嚜鍔ㄦ巿鏉冧笉閫氳繃銆戯細client_id=default&redirect_uri=https%3A%2F%2Fwww.iocoder.cn&response_type=code&scope=user.read%20user.write
+ // 渚嬪璇淬�愯嚜鍔ㄦ巿鏉冮�氳繃銆戯細client_id=default&redirect_uri=https%3A%2F%2Fwww.iocoder.cn&response_type=code&scope=user.read
+ queryParams.responseType = route.query.response_type as string
+ queryParams.clientId = route.query.client_id as string
+ queryParams.redirectUri = route.query.redirect_uri as string
+ queryParams.state = route.query.state as string
+ if (route.query.scope) {
+ queryParams.scopes = (route.query.scope as string).split(' ')
+ }
+
+ // 濡傛灉鏈� scope 鍙傛暟锛屽厛鎵ц涓�娆¤嚜鍔ㄦ巿鏉冿紝鐪嬬湅鏄惁涔嬪墠閮芥巿鏉冭繃浜嗐��
+ if (queryParams.scopes.length > 0) {
+ const data = await doAuthorize(true, queryParams.scopes, [])
+ if (data) {
+ location.href = data
+ return
+ }
+ }
+
+ // 鑾峰彇鎺堟潈椤电殑鍩烘湰淇℃伅
+ const data = await OAuth2Api.getAuthorize(queryParams.clientId)
+ client.value = data.client
+ // 瑙f瀽 scope
+ let scopes
+ // 1.1 濡傛灉 params.scope 闈炵┖锛屽垯杩囨护涓嬭繑鍥炵殑 scopes
+ if (queryParams.scopes.length > 0) {
+ scopes = []
+ for (const scope of data.scopes) {
+ if (queryParams.scopes.indexOf(scope.key) >= 0) {
+ scopes.push(scope)
+ }
+ }
+ // 1.2 濡傛灉 params.scope 涓虹┖锛屽垯浣跨敤杩斿洖鐨� scopes 璁剧疆瀹�
+ } else {
+ scopes = data.scopes
+ for (const scope of scopes) {
+ queryParams.scopes.push(scope.key)
+ }
+ }
+ // 鐢熸垚宸查�変腑鐨� checkedScopes
+ for (const scope of scopes) {
+ if (scope.value) {
+ formData.scopes.push(scope.key)
+ }
+ }
+}
+
+/** 澶勭悊鎺堟潈鐨勬彁浜� */
+const handleAuthorize = async (approved) => {
+ // 璁$畻 checkedScopes + uncheckedScopes
+ let checkedScopes
+ let uncheckedScopes
+ if (approved) {
+ // 鍚屾剰鎺堟潈锛屾寜鐓х敤鎴风殑閫夋嫨
+ checkedScopes = formData.scopes
+ uncheckedScopes = queryParams.scopes.filter((item) => checkedScopes.indexOf(item) === -1)
+ } else {
+ // 鎷掔粷锛屽垯閮芥槸鍙栨秷
+ checkedScopes = []
+ uncheckedScopes = queryParams.scopes
+ }
+ // 鎻愪氦鎺堟潈鐨勮姹�
+ formLoading.value = true
+ try {
+ const data = await doAuthorize(false, checkedScopes, uncheckedScopes)
+ if (!data) {
+ return
+ }
+ location.href = data
+ } finally {
+ formLoading.value = false
+ }
+}
+
+/** 璋冪敤鎺堟潈 API 鎺ュ彛 */
+const doAuthorize = (autoApprove, checkedScopes, uncheckedScopes) => {
+ return OAuth2Api.authorize(
+ queryParams.responseType,
+ queryParams.clientId,
+ queryParams.redirectUri,
+ queryParams.state,
+ autoApprove,
+ checkedScopes,
+ uncheckedScopes
+ )
+}
+
+/** 鏍煎紡鍖� scope 鏂囨湰 */
+const formatScope = (scope) => {
+ // 鏍煎紡鍖� scope 鎺堟潈鑼冨洿锛屾柟渚跨敤鎴风悊瑙c��
+ // 杩欓噷浠呬粎鏄竴涓� demo锛屽彲浠ヨ�冭檻褰曞叆鍒板瓧鍏告暟鎹腑锛屼緥濡傝瀛楀吀绫诲瀷 "system_oauth2_scope"锛屽畠鐨勬瘡涓� scope 閮芥槸涓�鏉″瓧鍏告暟鎹��
+ switch (scope) {
+ case 'user.read':
+ return '璁块棶浣犵殑涓汉淇℃伅'
+ case 'user.write':
+ return '淇敼浣犵殑涓汉淇℃伅'
+ default:
+ return scope
+ }
+}
+
+/** 鐩戝惉褰撳墠璺敱涓� SSOLogin 鏃讹紝杩涜鏁版嵁鐨勫垵濮嬪寲 */
+watch(
+ () => currentRoute.value,
+ (route: RouteLocationNormalizedLoaded) => {
+ if (route.name === 'SSOLogin') {
+ setLoginState(LoginStateEnum.SSO)
+ init()
+ }
+ },
+ { immediate: true }
+)
+</script>
--
Gitblit v1.8.0