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