鞍钢鲅鱼圈能源管控系统前端代码
houzhongjian
2024-12-26 cb6cd26221d8bb2c4b1dca44a87332e9fe6f56ab
提交 | 用户 | 时间
cb6cd2 1 <template>
H 2   <div v-show="ssoVisible" class="form-cont">
3     <!-- 应用名 -->
4     <LoginFormTitle style="width: 100%" />
5     <el-tabs class="form" style="float: none" value="uname">
6       <el-tab-pane :label="client.name" name="uname" />
7     </el-tabs>
8     <div>
9       <el-form :model="formData" class="login-form">
10         <!-- 授权范围的选择 -->
11         此第三方应用请求获得以下权限:
12         <br/>
13         <br/>
14         <el-form-item prop="scopes">
15           <el-checkbox-group v-model="formData.scopes">
16             <el-checkbox
17               v-for="scope in queryParams.scopes"
18               :key="scope"
19               :label="scope"
20               style="display: block; margin-bottom: -10px"
21             >
22               {{ formatScope(scope) }}
23             </el-checkbox>
24           </el-checkbox-group>
25         </el-form-item>
26         <!-- 下方的登录按钮 -->
27         <el-form-item class="w-1/1">
28           <el-button
29             :loading="formLoading"
30             class="w-6/10"
31             type="primary"
32             @click.prevent="handleAuthorize(true)"
33           >
34             <span v-if="!formLoading">同意授权</span>
35             <span v-else>授 权 中...</span>
36           </el-button>
37           <el-button class="w-3/10" @click.prevent="handleAuthorize(false)">拒绝</el-button>
38         </el-form-item>
39       </el-form>
40     </div>
41   </div>
42 </template>
43 <script lang="ts" setup>
44 import LoginFormTitle from './LoginFormTitle.vue'
45 import * as OAuth2Api from '@/api/login/oauth2'
46 import { LoginStateEnum, useLoginState } from './useLogin'
47 import type { RouteLocationNormalizedLoaded } from 'vue-router'
48
49 defineOptions({ name: 'SSOLogin' })
50
51 const route = useRoute() // 路由
52 const { currentRoute } = useRouter() // 路由
53 const { getLoginState, setLoginState } = useLoginState()
54
55 const client = ref({
56   // 客户端信息
57   name: '',
58   logo: ''
59 })
60 interface queryType {
61   responseType: string
62   clientId: string
63   redirectUri: string
64   state: string
65   scopes: string[]
66 }
67 const queryParams = reactive<queryType>({
68   // URL 上的 client_id、scope 等参数
69   responseType: '',
70   clientId: '',
71   redirectUri: '',
72   state: '',
73   scopes: [] // 优先从 query 参数获取;如果未传递,从后端获取
74 })
75 const ssoVisible = computed(() => unref(getLoginState) === LoginStateEnum.SSO) // 是否展示 SSO 登录的表单
76 interface formType {
77   scopes: string[]
78 }
79 const formData = reactive<formType>({
80   scopes: [] // 已选中的 scope 数组
81 })
82 const formLoading = ref(false) // 表单是否提交中
83
84 /** 初始化授权信息 */
85 const init = async () => {
86   // 防止在没有登录的情况下循环弹窗
87   if (typeof route.query.client_id === 'undefined') return
88   // 解析参数
89   // 例如说【自动授权不通过】:client_id=default&redirect_uri=https%3A%2F%2Fwww.xxxx.cn&response_type=code&scope=user.read%20user.write
90   // 例如说【自动授权通过】:client_id=default&redirect_uri=https%3A%2F%2Fwww.xxxx.cn&response_type=code&scope=user.read
91   queryParams.responseType = route.query.response_type as string
92   queryParams.clientId = route.query.client_id as string
93   queryParams.redirectUri = route.query.redirect_uri as string
94   queryParams.state = route.query.state as string
95   if (route.query.scope) {
96     queryParams.scopes = (route.query.scope as string).split(' ')
97   }
98
99   // 如果有 scope 参数,先执行一次自动授权,看看是否之前都授权过了。
100   if (queryParams.scopes.length > 0) {
101     const data = await doAuthorize(true, queryParams.scopes, [])
102     if (data) {
103       location.href = data
104       return
105     }
106   }
107
108   // 获取授权页的基本信息
109   const data = await OAuth2Api.getAuthorize(queryParams.clientId)
110   client.value = data.client
111   // 解析 scope
112   let scopes
113   // 1.1 如果 params.scope 非空,则过滤下返回的 scopes
114   if (queryParams.scopes.length > 0) {
115     scopes = []
116     for (const scope of data.scopes) {
117       if (queryParams.scopes.indexOf(scope.key) >= 0) {
118         scopes.push(scope)
119       }
120     }
121     // 1.2 如果 params.scope 为空,则使用返回的 scopes 设置它
122   } else {
123     scopes = data.scopes
124     for (const scope of scopes) {
125       queryParams.scopes.push(scope.key)
126     }
127   }
128   // 生成已选中的 checkedScopes
129   for (const scope of scopes) {
130     if (scope.value) {
131       formData.scopes.push(scope.key)
132     }
133   }
134 }
135
136 /** 处理授权的提交 */
137 const handleAuthorize = async (approved) => {
138   // 计算 checkedScopes + uncheckedScopes
139   let checkedScopes
140   let uncheckedScopes
141   if (approved) {
142     // 同意授权,按照用户的选择
143     checkedScopes = formData.scopes
144     uncheckedScopes = queryParams.scopes.filter((item) => checkedScopes.indexOf(item) === -1)
145   } else {
146     // 拒绝,则都是取消
147     checkedScopes = []
148     uncheckedScopes = queryParams.scopes
149   }
150   // 提交授权的请求
151   formLoading.value = true
152   try {
153     const data = await doAuthorize(false, checkedScopes, uncheckedScopes)
154     if (!data) {
155       return
156     }
157     location.href = data
158   } finally {
159     formLoading.value = false
160   }
161 }
162
163 /** 调用授权 API 接口 */
164 const doAuthorize = (autoApprove, checkedScopes, uncheckedScopes) => {
165   return OAuth2Api.authorize(
166     queryParams.responseType,
167     queryParams.clientId,
168     queryParams.redirectUri,
169     queryParams.state,
170     autoApprove,
171     checkedScopes,
172     uncheckedScopes
173   )
174 }
175
176 /** 格式化 scope 文本 */
177 const formatScope = (scope) => {
178   // 格式化 scope 授权范围,方便用户理解。
179   // 这里仅仅是一个 demo,可以考虑录入到字典数据中,例如说字典类型 "system_oauth2_scope",它的每个 scope 都是一条字典数据。
180   switch (scope) {
181     case 'user.read':
182       return '访问你的个人信息'
183     case 'user.write':
184       return '修改你的个人信息'
185     default:
186       return scope
187   }
188 }
189
190 /** 监听当前路由为 SSOLogin 时,进行数据的初始化 */
191 watch(
192   () => currentRoute.value,
193   (route: RouteLocationNormalizedLoaded) => {
194     if (route.name === 'SSOLogin') {
195       setLoginState(LoginStateEnum.SSO)
196       init()
197     }
198   },
199   { immediate: true }
200 )
201 </script>