fix 登录页和home页路由调整

This commit is contained in:
Asoka 2025-06-04 16:08:04 +08:00
parent 862661c426
commit f25f506ca8
7 changed files with 544 additions and 43 deletions

View File

@ -98,42 +98,46 @@ router.beforeEach(async (to, from, next) => {
if (to.meta.title) NProgress.start() if (to.meta.title) NProgress.start()
const storesUseUserInfo = useUserInfo(pinia) const storesUseUserInfo = useUserInfo(pinia)
const token = storesUseUserInfo.getToken() const token = storesUseUserInfo.getToken()
if (to.meta.isPublic && !token) {
// 如果是公共页面直接通过不需要检查token和路由列表
if (to.meta.isPublic) {
next() next()
NProgress.done() NProgress.done()
return
}
if (!token) {
next(`/login?redirect=${to.path}&params=${JSON.stringify(to.query ? to.query : to.params)}`)
storesUseUserInfo.removeTokenInfo()
Session.clear()
NProgress.done()
} else if (token && to.path === '/login') {
console.log('🔄 [路由守卫] 已登录用户访问登录页面,重定向到系统入口页面')
next('/system-entrance')
NProgress.done()
} else { } else {
if (!token) { const storesRoute = useRoute(pinia)
next(`/login?redirect=${to.path}&params=${JSON.stringify(to.query ? to.query : to.params)}`) storesRoute.setRouteTo({ to: { path: to.path, params: JSON.stringify(to.query ? to.query : to.params) } })
storesUseUserInfo.removeTokenInfo() const storesRoutesList = useRoutesList(pinia)
Session.clear() const { routesList } = storeToRefs(storesRoutesList)
NProgress.done() if (routesList.value.length === 0) {
} else if (token && to.path === '/login') { if (isRequestRoutes) {
next('/') // 后端控制路由:路由数据初始化,防止刷新时丢失
NProgress.done() const isNoPower = await initBackEndControlRoutes()
} else { if (isNoPower) {
const storesRoute = useRoute(pinia) ElMessage.warning('抱歉,您没有分配权限,请联系管理员')
storesRoute.setRouteTo({ to: { path: to.path, params: JSON.stringify(to.query ? to.query : to.params) } }) storesUseUserInfo.removeTokenInfo()
const storesRoutesList = useRoutesList(pinia) Session.clear()
const { routesList } = storeToRefs(storesRoutesList)
if (routesList.value.length === 0) {
if (isRequestRoutes) {
// 后端控制路由:路由数据初始化,防止刷新时丢失
const isNoPower = await initBackEndControlRoutes()
if (isNoPower) {
ElMessage.warning('抱歉,您没有分配权限,请联系管理员')
storesUseUserInfo.removeTokenInfo()
Session.clear()
}
// 解决刷新时,一直跳 404 页面问题,关联问题 No match found for location with path 'xxx'
// to.query 防止页面刷新时普通路由带参数时参数丢失。动态路由xxx/:id/:name"isDynamic 无需处理
next({ path: to.path, query: to.query })
} else {
await initFrontEndControlRoutes()
next({ path: to.path, query: to.query })
} }
// 解决刷新时,一直跳 404 页面问题,关联问题 No match found for location with path 'xxx'
// to.query 防止页面刷新时普通路由带参数时参数丢失。动态路由xxx/:id/:name"isDynamic 无需处理
next({ path: to.path, query: to.query })
} else { } else {
next() await initFrontEndControlRoutes()
next({ path: to.path, query: to.query })
} }
} else {
next()
} }
} }
}) })

View File

@ -143,6 +143,15 @@ export const staticRoutes: Array<RouteRecordRaw> = [
isPublic: true, isPublic: true,
}, },
}, },
{
path: '/system-entrance',
name: 'system-entrance',
component: () => import('/@/views/system-entrance/index.vue'),
meta: {
title: '系统入口',
isPublic: true,
},
},
/** /**
* *
* `dynamicRoutes` * `dynamicRoutes`

View File

@ -217,6 +217,10 @@ const onSignIn = async () => {
} }
// //
const signInSuccess = (isNoPower: boolean | undefined) => { const signInSuccess = (isNoPower: boolean | undefined) => {
console.log('🚀 [登录成功] signInSuccess 被调用')
console.log('🚀 [登录成功] isNoPower:', isNoPower)
console.log('🚀 [登录成功] route.query:', route.query)
if (isNoPower) { if (isNoPower) {
ElMessage.warning('抱歉,您没有分配权限,请联系管理员') ElMessage.warning('抱歉,您没有分配权限,请联系管理员')
useUserInfo().removeTokenInfo() useUserInfo().removeTokenInfo()
@ -224,15 +228,17 @@ const signInSuccess = (isNoPower: boolean | undefined) => {
} else { } else {
// //
let currentTimeInfo = currentTime.value let currentTimeInfo = currentTime.value
// //
// / // /
if (route.query?.redirect) { if (route.query?.redirect && route.query.redirect !== '/') {
console.log('🚀 [登录成功] 有redirect参数且不是首页跳转到:', route.query.redirect)
router.push({ router.push({
path: <string>route.query?.redirect, path: <string>route.query?.redirect,
query: Object.keys(<string>route.query?.params).length > 0 ? JSON.parse(<string>route.query?.params) : '', query: Object.keys(<string>route.query?.params).length > 0 ? JSON.parse(<string>route.query?.params) : '',
}) })
} else { } else {
router.push('/') console.log('🚀 [登录成功] 无redirect参数或redirect是首页跳转到系统入口页面: /system-entrance')
router.push('/system-entrance')
} }
// //
const signInText = t('message.signInText') const signInText = t('message.signInText')

View File

@ -135,15 +135,15 @@ const signInSuccess = (isNoPower: boolean | undefined) => {
} else { } else {
// //
let currentTimeInfo = currentTime.value let currentTimeInfo = currentTime.value
// //
// / // /
if (route.query?.redirect) { if (route.query?.redirect && route.query.redirect !== '/') {
router.push({ router.push({
path: <string>route.query?.redirect, path: <string>route.query?.redirect,
query: Object.keys(<string>route.query?.params).length > 0 ? JSON.parse(<string>route.query?.params) : '', query: Object.keys(<string>route.query?.params).length > 0 ? JSON.parse(<string>route.query?.params) : '',
}) })
} else { } else {
router.push('/') router.push('/system-entrance')
} }
// //
const signInText = t('message.signInText') const signInText = t('message.signInText')

View File

@ -138,15 +138,15 @@ const signInSuccess = (isNoPower: boolean | undefined) => {
} else { } else {
// //
let currentTimeInfo = currentTime.value let currentTimeInfo = currentTime.value
// //
// / // /
if (route.query?.redirect) { if (route.query?.redirect && route.query.redirect !== '/') {
router.push({ router.push({
path: <string>route.query?.redirect, path: <string>route.query?.redirect,
query: Object.keys(<string>route.query?.params).length > 0 ? JSON.parse(<string>route.query?.params) : '', query: Object.keys(<string>route.query?.params).length > 0 ? JSON.parse(<string>route.query?.params) : '',
}) })
} else { } else {
router.push('/') router.push('/system-entrance')
} }
// //
const signInText = t('message.signInText') const signInText = t('message.signInText')

View File

@ -114,20 +114,21 @@ const onSignIn = async () => {
// //
const signInSuccess = (isNoPower: boolean | undefined) => { const signInSuccess = (isNoPower: boolean | undefined) => {
if (isNoPower) { if (isNoPower) {
ElMessage.warning('抱歉,您没有登录权限') ElMessage.warning('抱歉,您没有分配权限,请联系管理员')
useUserInfo().removeTokenInfo()
Session.clear() Session.clear()
} else { } else {
// //
let currentTimeInfo = currentTime.value let currentTimeInfo = currentTime.value
// //
// / // /
if (route.query?.redirect) { if (route.query?.redirect && route.query.redirect !== '/') {
router.push({ router.push({
path: <string>route.query?.redirect, path: <string>route.query?.redirect,
query: Object.keys(<string>route.query?.params).length > 0 ? JSON.parse(<string>route.query?.params) : '', query: Object.keys(<string>route.query?.params).length > 0 ? JSON.parse(<string>route.query?.params) : '',
}) })
} else { } else {
router.push('/') router.push('/system-entrance')
} }
// //
const signInText = t('message.signInText') const signInText = t('message.signInText')

View File

@ -0,0 +1,481 @@
<template>
<div class="system-entrance-page">
<!-- Logo区域 -->
<div class="logo-section">
<div class="logo-container">
<img src="/@/assets/logo-mini.png" alt="WuXi Biologics" class="company-logo" />
<div class="logo-text">
<span class="company-name">WuXi Biologics</span>
<span class="company-subtitle">Global Solution Provider</span>
</div>
</div>
</div>
<!-- 欢迎标题 -->
<div class="welcome-title">
<h1>欢迎来到系统入口</h1>
</div>
<!-- 功能模块卡片 -->
<div class="modules-grid">
<!-- 调试信息 -->
<div v-if="state.columnsAsideList.length === 0" class="debug-info">
<div v-if="!storesUserInfo.getToken()">
<p>👤 您尚未登录</p>
<p>请先<router-link to="/login" class="login-link">点击这里登录</router-link>然后再访问系统功能</p>
</div>
<div v-else-if="state.isInitializing">
<p>🔄 正在初始化系统路由数据...</p>
<p>请稍候系统正在准备菜单数据</p>
</div>
<div v-else>
<p> 系统需要初始化路由数据</p>
<p><router-link to="/" class="login-link">点击这里进入主页</router-link>完成系统初始化然后返回此页面</p>
<p style="margin-top: 15px; font-size: 12px; color: #666;">
或者刷新页面重新尝试加载路由数据
</p>
</div>
</div>
<!-- 动态路由模块 -->
<div
v-for="(v, k) in state.columnsAsideList"
:key="`route-${k}`"
class="module-card"
@click="onColumnsAsideMenuClick(v)"
>
<div class="module-icon">
<svg class="icon-svg" viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg">
<path fill="currentColor" :d="getModuleIcon(k)"/>
</svg>
</div>
<div class="module-title">{{ getModuleTitle(v) }}</div>
</div>
</div>
</div>
</template>
<script setup lang="ts" name="SystemEntrancePage">
import { reactive, onMounted, computed, watch } from 'vue'
import { useRoute, useRouter, RouteRecordRaw } from 'vue-router'
import { storeToRefs } from 'pinia'
import { useRoutesList } from '/@/stores/routesList'
import { useThemeConfig } from '/@/stores/themeConfig'
import { useUserInfo } from '/@/stores/userInfo'
import { ElMessage } from 'element-plus'
import mittBus from '/@/utils/mitt'
import { treeToList, listToTree, filterList } from '/@/utils/tree'
import { cloneDeep } from 'lodash-es'
import { initBackEndControlRoutes } from '/@/router/backEnd'
import { initFrontEndControlRoutes } from '/@/router/frontEnd'
import { NextLoading } from '/@/utils/loading'
const route = useRoute()
const router = useRouter()
const stores = useRoutesList()
const storesThemeConfig = useThemeConfig()
const storesUserInfo = useUserInfo()
const { routesList } = storeToRefs(stores)
const { themeConfig } = storeToRefs(storesThemeConfig)
const state = reactive({
columnsAsideList: [] as RouteItem[],
isInitializing: false,
})
//
const getModuleIcon = (index: number): string => {
//
const iconPaths = [
'M512 85.333333c235.648 0 426.666667 191.018667 426.666667 426.666667s-191.018667 426.666667-426.666667 426.666667S85.333333 747.648 85.333333 512 276.352 85.333333 512 85.333333z m0 64C311.168 149.333333 149.333333 311.168 149.333333 512s161.834667 362.666667 362.666667 362.666667 362.666667-161.834667 362.666667-362.666667S712.832 149.333333 512 149.333333z m-32 149.333334v277.333333l170.666667 170.666667-45.254667 45.254666L469.333333 656V298.666667h74.666667z',
'M469.333333 170.666667c23.564267 0 42.666667 19.102933 42.666667 42.666666v128c0 23.564267-19.102933 42.666667-42.666667 42.666667s-42.666667-19.102933-42.666667-42.666667V213.333333c0-23.564267 19.102933-42.666667 42.666667-42.666667z m0 469.333333c23.564267 0 42.666667 19.102933 42.666667 42.666667v128c0 23.564267-19.102933 42.666667-42.666667 42.666667s-42.666667-19.102933-42.666667-42.666667V682.666667c0-23.564267 19.102933-42.666667 42.666667-42.666667z',
'M170.666667 85.333333h682.666666c47.128533 0 85.333333 38.204800 85.333334 85.333334v682.666666c0 47.128533-38.204800 85.333333-85.333334 85.333334H170.666667c-47.128533 0-85.333333-38.204800-85.333334-85.333334V170.666667c0-47.128533 38.204800-85.333333 85.333334-85.333334z m0 64c-11.776 0-21.333333 9.557333-21.333334 21.333334v682.666666c0 11.776 9.557333 21.333333 21.333334 21.333334h682.666666c11.776 0 21.333333-9.557333 21.333334-21.333334V170.666667c0-11.776-9.557333-21.333333-21.333334-21.333334H170.666667z',
'M512 618.666667c58.88 0 106.666667-47.786667 106.666667-106.666667s-47.786667-106.666667-106.666667-106.666667-106.666667 47.786667-106.666667 106.666667 47.786667 106.666667 106.666667 106.666667z m0 64c-94.293333 0-170.666667-76.373333-170.666667-170.666667s76.373333-170.666667 170.666667-170.666667 170.666667 76.373333 170.666667 170.666667-76.373333 170.666667-170.666667 170.666667z'
]
return iconPaths[index % iconPaths.length]
}
//
const getModuleTitle = (moduleItem: RouteItem): string => {
return moduleItem.meta?.title || '未命名模块'
}
// columnsAside.vue
const filterRoutesFun = <T extends RouteItem>(arr: T[]): T[] => {
return arr
.filter((item: T) => !item.meta?.isHide)
.map((item: T) => {
item = Object.assign({}, item)
if (item.children) item.children = filterRoutesFun(item.children)
return item
})
}
// / columnsAside.vue
const setFilterRoutes = () => {
state.columnsAsideList = filterRoutesFun(routesList.value)
}
//
const initializeRoutes = async () => {
if (state.isInitializing) return
const token = storesUserInfo.getToken()
if (!token) return
state.isInitializing = true
try {
if (themeConfig.value.isRequestRoutes) {
await initBackEndControlRoutes()
} else {
await initFrontEndControlRoutes()
}
//
NextLoading.done()
} catch (error) {
console.error('路由初始化失败:', error)
// 使
NextLoading.done()
} finally {
state.isInitializing = false
}
}
// columnsAside.vue
const onColumnsAsideMenuClick = async (v: RouteItem) => {
let { path, redirect } = v
if (redirect) {
if (route.path.startsWith(redirect)) {
mittBus.emit('setSendColumnsChildren', setSendChildren(redirect))
} else {
router.push(redirect)
}
} else {
if (v.children && v.children.length > 0) {
const resData: MittMenu = setSendChildren(path)
if (Object.keys(resData).length <= 0) return false
mittBus.emit('setSendColumnsChildren', resData)
router.push('/')
themeConfig.value.isCollapse = false
} else {
router.push(path)
themeConfig.value.isCollapse = true
}
}
// columnsAside.vue
!v.children || v.children.length < 1 ? (themeConfig.value.isCollapse = true) : (themeConfig.value.isCollapse = false)
}
// columnsAside.vue
const setSendChildren = (path: string) => {
const currentPathSplit = path.split('/')
let rootPath = `/${currentPathSplit[1]}`
//
if (!state.columnsAsideList.find((v) => v.path === rootPath)) {
// 使
let routeTree = listToTree(
filterList(treeToList(cloneDeep(state.columnsAsideList)), path, {
filterWhere: (item: any, filterword: string) => {
return item.path?.toLocaleLowerCase() === filterword
},
})
)
// 使
if (routeTree.length > 0 && routeTree[0]?.path) {
rootPath = routeTree[0].path
}
}
let currentData: MittMenu = { children: [] }
state.columnsAsideList.map((v: RouteItem, k: number) => {
if (v.path === rootPath) {
v['k'] = k
currentData['item'] = { ...v }
currentData['children'] = [{ ...v }]
if (v.children) currentData['children'] = v.children
}
})
return currentData
}
// columnsAside.vue
onMounted(() => {
setFilterRoutes()
//
if (routesList.value.length === 0 && storesUserInfo.getToken()) {
initializeRoutes()
} else {
//
NextLoading.done()
}
})
//
watch(
() => routesList.value,
() => {
setFilterRoutes()
//
if (routesList.value.length > 0) {
NextLoading.done()
}
},
{ deep: true, immediate: false }
)
</script>
<style scoped lang="scss">
.system-entrance-page {
min-height: 100vh;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
padding: 40px 20px;
display: flex;
flex-direction: column;
align-items: center;
.logo-section {
margin-bottom: 60px;
.logo-container {
display: flex;
align-items: center;
gap: 15px;
.company-logo {
width: 60px;
height: 60px;
object-fit: contain;
border-radius: 8px;
}
.logo-text {
display: flex;
flex-direction: column;
.company-name {
font-size: 28px;
font-weight: bold;
color: #fff;
margin-bottom: 4px;
letter-spacing: 1px;
}
.company-subtitle {
font-size: 16px;
color: rgba(255, 255, 255, 0.9);
font-weight: 300;
}
}
}
}
.welcome-title {
margin-bottom: 80px;
h1 {
font-size: 42px;
color: #fff;
font-weight: 300;
text-align: center;
margin: 0;
text-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
letter-spacing: 2px;
}
}
.modules-grid {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 50px;
max-width: 900px;
width: 100%;
.debug-info {
grid-column: 1 / -1;
background: rgba(255, 255, 255, 0.9);
border-radius: 12px;
padding: 20px;
text-align: center;
color: #666;
font-size: 14px;
p {
margin: 8px 0;
}
.login-link {
color: #667eea;
text-decoration: none;
font-weight: 600;
&:hover {
color: #5a6fd8;
text-decoration: underline;
}
}
}
.module-card {
background: rgba(255, 255, 255, 0.95);
border-radius: 24px;
padding: 50px 40px;
text-align: center;
cursor: pointer;
transition: all 0.4s ease;
box-shadow: 0 15px 40px rgba(0, 0, 0, 0.15);
backdrop-filter: blur(20px);
min-height: 240px;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
border: 1px solid rgba(255, 255, 255, 0.2);
&:hover {
transform: translateY(-15px) scale(1.02);
box-shadow: 0 25px 60px rgba(0, 0, 0, 0.25);
background: rgba(255, 255, 255, 1);
.module-icon .icon-svg {
color: #667eea;
transform: scale(1.15) rotateY(10deg);
}
.module-title {
color: #667eea;
}
}
.module-icon {
margin-bottom: 24px;
.icon-svg {
width: 90px;
height: 90px;
color: #555;
transition: all 0.4s ease;
filter: drop-shadow(0 4px 8px rgba(0, 0, 0, 0.1));
}
}
.module-title {
font-size: 20px;
color: #333;
font-weight: 600;
line-height: 1.4;
transition: color 0.3s ease;
letter-spacing: 0.5px;
}
}
}
//
@media (max-width: 1024px) {
.modules-grid {
max-width: 600px;
gap: 40px;
.module-card {
padding: 40px 30px;
min-height: 200px;
}
}
}
@media (max-width: 768px) {
padding: 30px 20px;
.logo-section {
margin-bottom: 50px;
.logo-container {
flex-direction: column;
text-align: center;
gap: 20px;
.company-logo {
width: 70px;
height: 70px;
}
.logo-text {
align-items: center;
.company-name {
font-size: 24px;
}
.company-subtitle {
font-size: 14px;
}
}
}
}
.welcome-title {
margin-bottom: 60px;
h1 {
font-size: 32px;
letter-spacing: 1px;
}
}
.modules-grid {
grid-template-columns: 1fr;
gap: 30px;
max-width: 400px;
.module-card {
padding: 35px 25px;
min-height: 180px;
.module-icon {
margin-bottom: 20px;
.icon-svg {
width: 70px;
height: 70px;
}
}
.module-title {
font-size: 18px;
}
}
}
}
@media (max-width: 480px) {
padding: 20px 15px;
.welcome-title h1 {
font-size: 28px;
}
.modules-grid {
max-width: 320px;
.module-card {
padding: 30px 20px;
min-height: 160px;
.module-icon .icon-svg {
width: 60px;
height: 60px;
}
.module-title {
font-size: 16px;
}
}
}
}
}
</style>