Browse Source

ssologin

master
xuhuajiao 23 hours ago
parent
commit
f9e459cabe
  1. 2
      src/router/index.js
  2. 4
      src/router/routers.js
  3. 5
      src/utils/request.js
  4. 65
      src/views/collectReorganizi/collectionLibrary/index.vue
  5. 29
      src/views/collectReorganizi/collectionLibrary/module/collectHeader.vue
  6. 90
      src/views/features/403.vue
  7. 223
      src/views/features/ssoLogin.vue

2
src/router/index.js

@ -9,7 +9,7 @@ import { filterAsyncRouter } from '@/store/modules/permission'
NProgress.configure({ showSpinner: false })// NProgress Configuration NProgress.configure({ showSpinner: false })// NProgress Configuration
const whiteList = ['/login']// no redirect whitelist
const whiteList = ['/login', '/ssoLogin']// no redirect whitelist
router.beforeEach((to, from, next) => { router.beforeEach((to, from, next) => {
if (to.meta.title) { if (to.meta.title) {

4
src/router/routers.js

@ -21,8 +21,8 @@ export const constantRouterMap = [
hidden: true hidden: true
}, },
{ {
path: '/403',
component: (resolve) => require(['@/views/features/403'], resolve),
path: '/ssoLogin',
component: (resolve) => require(['@/views/features/ssoLogin'], resolve),
hidden: true hidden: true
}, },
{ {

5
src/utils/request.js

@ -27,7 +27,10 @@ service.interceptors.request.use(
if (getToken()) { if (getToken()) {
config.headers['Authorization'] = getToken() // 让每个请求携带自定义token 请根据实际情况自行修改 config.headers['Authorization'] = getToken() // 让每个请求携带自定义token 请根据实际情况自行修改
} }
config.headers['Content-Type'] = 'application/json'
// 只在没有设置 Content-Type 时才设置默认值
if (!config.headers['Content-Type']) {
config.headers['Content-Type'] = 'application/json'
}
return config return config
}, },
error => { error => {

65
src/views/collectReorganizi/collectionLibrary/index.vue

@ -193,9 +193,17 @@ export default {
filterData(data) { filterData(data) {
return data.filter(node => { return data.filter(node => {
if (node.children && node.children.length > 0) { if (node.children && node.children.length > 0) {
node.children = this.filterData(node.children) //
// isType === 3 id
const typeThreeIds = node.children
.filter(child => child.isType === 3)
.map(child => child.id)
if (typeThreeIds.length > 0) {
node.typeThreeId = typeThreeIds
}
//
node.children = this.filterData(node.children)
} }
return node.isType !== 3 // isType3
return node.isType !== 3
}) })
}, },
// //
@ -276,6 +284,11 @@ export default {
}, },
[CRUD.HOOK.afterRefresh]() { [CRUD.HOOK.afterRefresh]() {
this.crud.data = this.filterData(this.transformData(this.crud.data)) this.crud.data = this.filterData(this.transformData(this.crud.data))
console.log('this.crud.data', this.crud.data)
// SSO
this.handleSSOData()
this.$nextTick(() => { this.$nextTick(() => {
let currentKey let currentKey
if (localStorage.getItem('currentArchivesKey') !== null) { if (localStorage.getItem('currentArchivesKey') !== null) {
@ -313,6 +326,54 @@ export default {
} }
}) })
}, },
handleSSOData() {
const ssoDataStr = localStorage.getItem('ssoData')
if (!ssoDataStr) return
const ssoData = JSON.parse(ssoDataStr)
const { categoryId, fondsId } = ssoData
if (!categoryId || !fondsId) return
// 1.
const topLevelNode = this.findTopLevelNode(this.crud.data, fondsId)
if (!topLevelNode) return
// 2. categoryId typeThreeId categoryId
const targetNode = this.findNodeByTypeThreeId(topLevelNode.children, categoryId)
if (!targetNode) return
// 3. currentKey
const currentKey = {
id: targetNode.id,
fondsId: fondsId,
isType: targetNode.isType,
fondName: topLevelNode.fondName,
fondsNo: topLevelNode.fondsNo,
arrangeType: targetNode.arrangeType,
code: targetNode.code
}
// 4. localStorage
localStorage.setItem('currentArchivesKey', JSON.stringify(currentKey))
// ssoData collectHeader.vue archiveNo
},
// typeThreeId
findNodeByTypeThreeId(tree, categoryId) {
for (const node of tree) {
// typeThreeId categoryId
if (node.typeThreeId && node.typeThreeId.includes(categoryId)) {
return node
}
//
if (node.children && node.children.length > 0) {
const res = this.findNodeByTypeThreeId(node.children, categoryId)
if (res) return res
}
}
return null
},
defaultSetting(currentKey) { defaultSetting(currentKey) {
if (this.crud.data[0].isType === 0) { if (this.crud.data[0].isType === 0) {
currentKey = this.findNode(this.crud.data[0].children, (node) => { currentKey = this.findNode(this.crud.data[0].children, (node) => {

29
src/views/collectReorganizi/collectionLibrary/module/collectHeader.vue

@ -636,8 +636,37 @@ export default {
// this.restoreAdvancedSearchConditions() // this.restoreAdvancedSearchConditions()
this.getInitArchivesClass() this.getInitArchivesClass()
this.getCategoryDataTree() this.getCategoryDataTree()
// SSO archiveNo
this.handleSSOArchiveNo()
}, },
methods: { methods: {
handleSSOArchiveNo() {
const ssoDataStr = localStorage.getItem('ssoData')
if (!ssoDataStr) return
const ssoData = JSON.parse(ssoDataStr)
const { archiveNo } = ssoData
if (!archiveNo) {
// archiveNo ssoData
localStorage.removeItem('ssoData')
return
}
//
this.query.search = archiveNo
//
this.$nextTick(() => {
this.handleSearch(this.collectLevel)
// ssoData
setTimeout(() => {
localStorage.removeItem('ssoData')
}, 2000)
})
},
// localStorage // localStorage
restoreAdvancedSearchConditions() { restoreAdvancedSearchConditions() {
const savedConditions = localStorage.getItem('advancedSearchConditions') const savedConditions = localStorage.getItem('advancedSearchConditions')

90
src/views/features/403.vue

@ -1,90 +0,0 @@
<template>
<div class="errPage-container">
<!-- <el-button icon="arrow-left" class="pan-back-btn" @click="back">
返回
</el-button> -->
<el-row>
<el-col :span="12">
<h1 class="text-jumbo text-ginormous">
Oops!
</h1>
<h2>你没有权限去该页面</h2>
<!-- <h6>如有不满请联系你领导</h6>
<ul class="list-unstyled">
<li>或者你可以去:</li>
<li class="link-type">
<router-link to="/dashboard">
回首页
</router-link>
</li>
</ul> -->
</el-col>
<el-col :span="12">
<img :src="errGif" width="313" height="428" alt="Girl has dropped her ice cream.">
</el-col>
</el-row>
</div>
</template>
<script>
import errGif from '@/assets/401_images/401.gif'
export default {
name: 'Page401',
data() {
return {
errGif: errGif + '?' + +new Date()
}
},
methods: {
back() {
if (this.$route.query.noGoBack) {
this.$router.push({ path: '/dashboard' })
} else {
this.$router.go(-1)
}
}
}
}
</script>
<style lang="scss" scoped>
.errPage-container {
width: 800px;
max-width: 100%;
margin: 0 auto;
padding-top: 100px;
.pan-back-btn {
background: #008489;
color: #fff;
border: none!important;
}
.pan-gif {
margin: 0 auto;
display: block;
}
.pan-img {
display: block;
margin: 0 auto;
width: 100%;
}
.text-jumbo {
font-size: 60px;
font-weight: 700;
color: #484848;
}
.list-unstyled {
font-size: 14px;
li {
padding-bottom: 5px;
}
a {
color: #008489;
text-decoration: none;
&:hover {
text-decoration: underline;
}
}
}
}
</style>

223
src/views/features/ssoLogin.vue

@ -0,0 +1,223 @@
<template>
<div class="sso-login-container">
<div class="login-content">
<!-- 加载状态 -->
<div v-if="loading" class="loading-state">
<div class="spinner" />
<p class="loading-text">正在自动登录...</p>
</div>
<!-- 成功状态 -->
<div v-else-if="success" class="success-state">
<div class="success-icon"></div>
<h2>登录成功</h2>
<p>正在跳转...</p>
</div>
<!-- 错误状态 -->
<div v-else class="error-state">
<div class="error-icon"></div>
<h2>登录失败</h2>
<p class="error-message">{{ message }}</p>
<button class="retry-btn" @click="handleAutoLogin">重新登录</button>
</div>
</div>
</div>
</template>
<script>
import { setToken } from '@/utils/auth'
export default {
name: 'SSOLogin',
data() {
return {
loading: true,
success: false,
message: ''
}
},
mounted() {
this.handleAutoLogin()
},
methods: {
handleAutoLogin() {
this.loading = true
this.success = false
this.message = ''
const { loginid, ts, token, flowableId } = this.$route.query
if (!loginid || !ts || !token) {
this.message = '缺少必要参数'
this.loading = false
return
}
console.log('loginid', loginid)
console.log('ts', ts)
console.log('token', token)
console.log('flowableId', flowableId)
//
fetch('/api/sso/auto-login', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
body: `loginid=${encodeURIComponent(loginid)}&ts=${ts}&token=${encodeURIComponent(token)}&flowableId=${encodeURIComponent(flowableId)}`
})
.then(res => {
// HTTP
if (!res.ok) {
throw new Error(`HTTP 错误:${res.status} ${res.statusText}`)
}
return res.json()
})
.then(data => {
this.loading = false
console.log('data', data)
if (data && data.code === 200) {
// 使 setToken Cookie
setToken(data.data.token)
this.success = true
// SSO localStorage
const ssoData = {
categoryId: data.data.categoryId,
fondsId: data.data.user?.user?.fonds?.id,
archiveNo: data.data.archiveNo,
fondName: data.data.user?.user?.fonds?.fondsName
}
localStorage.setItem('ssoData', JSON.stringify(ssoData))
//
setTimeout(() => {
this.$router.replace('/collectReorganizi/collectionLibrary')
}, 500)
} else {
//
this.message = data?.message || '登录失败,请联系管理员'
}
})
.catch(err => {
this.loading = false
this.message = '登录请求失败:' + err.message
})
}
}
}
</script>
<style lang="scss" scoped>
.sso-login-container {
min-height: 100vh;
display: flex;
justify-content: center;
align-items: center;
// background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
}
.login-content {
background: #fff;
border-radius: 12px;
padding: 48px 64px;
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.15);
text-align: center;
min-width: 360px;
}
/* 加载状态 */
.loading-state {
.spinner {
width: 50px;
height: 50px;
border: 4px solid #f3f3f3;
border-top: 4px solid #0348F3;
border-radius: 50%;
animation: spin 1s linear infinite;
margin: 0 auto 20px;
}
.loading-text {
color: #666;
font-size: 16px;
}
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
/* 成功状态 */
.success-state {
.success-icon {
width: 80px;
height: 80px;
border-radius: 50%;
background: #52c41a;
color: #fff;
font-size: 40px;
line-height: 80px;
margin: 0 auto 20px;
}
h2 {
color: #333;
margin-bottom: 12px;
}
p {
color: #666;
}
}
/* 错误状态 */
.error-state {
.error-icon {
width: 80px;
height: 80px;
border-radius: 50%;
background: #ff4d4f;
color: #fff;
font-size: 40px;
line-height: 80px;
margin: 0 auto 20px;
}
h2 {
color: #ff4d4f;
margin-bottom: 12px;
}
.error-message {
color: #666;
margin-bottom: 24px;
min-height: 24px;
}
.retry-btn,
.back-btn {
padding: 10px 24px;
border: none;
border-radius: 6px;
font-size: 14px;
cursor: pointer;
margin: 0 8px;
transition: all 0.3s ease;
}
.retry-btn {
background: #0348F3;
color: #fff;
}
.back-btn {
background: #f5f5f5;
color: #666;
&:hover {
background: #e8e8e8;
}
}
}
</style>
Loading…
Cancel
Save