diff --git a/.env b/.env index 1c67321..c520611 100644 --- a/.env +++ b/.env @@ -8,4 +8,4 @@ VITE_OPEN = true VITE_OPEN_CDN = false # public path 配置线上环境路径(打包)、本地通过 http-server 访问时,请置空即可 -VITE_PUBLIC_PATH = 'https://yuedong-wechatapplet.oss-cn-shanghai.aliyuncs.com/static/event_web/202606040917/' \ No newline at end of file +VITE_PUBLIC_PATH = 'https://yuedong-wechatapplet.oss-cn-shanghai.aliyuncs.com/static/event_web/202606161054/' \ No newline at end of file diff --git a/src/views/login/component/account.vue b/src/views/login/component/account.vue index 1a9f25d..bb84ece 100644 --- a/src/views/login/component/account.vue +++ b/src/views/login/component/account.vue @@ -81,6 +81,7 @@ import type { FormInstance, FormRules } from 'element-plus'; import { useI18n } from 'vue-i18n'; import { storeToRefs } from 'pinia'; import { useThemeConfig } from '/@/stores/themeConfig'; +import { useRoutesList } from '/@/stores/routesList'; import { initFrontEndControlRoutes } from '/@/router/frontEnd'; import { initBackEndControlRoutes } from '/@/router/backEnd'; import { Session } from '/@/utils/storage'; @@ -93,6 +94,8 @@ const emit = defineEmits(['switchTab']); const { t } = useI18n(); const storesThemeConfig = useThemeConfig(); const { themeConfig } = storeToRefs(storesThemeConfig); +const storesRoutesList = useRoutesList(); +const { routesList } = storeToRefs(storesRoutesList); const route = useRoute(); const router = useRouter(); const formRef = ref(); @@ -147,6 +150,77 @@ const onCaptchaChange = () => { state.random = new Date().getTime(); }; +const normalizeRoutePath = (path: string) => { + if (!path) return '/'; + const normalizedPath = path.split('?')[0].replace(/\/+$/, ''); + return normalizedPath || '/'; +}; + +const flattenRoutePaths = (list: any[] = []) => { + const pathSet = new Set(); + const walk = (items: any[]) => { + items.forEach((item) => { + if (item?.path) pathSet.add(normalizeRoutePath(item.path)); + if (item?.children?.length) walk(item.children); + }); + }; + walk(list); + return pathSet; +}; + +const getFirstAccessiblePath = (list: any[] = []): string => { + for (const item of list) { + if (!item || item.meta?.isHide) continue; + if (item.children?.length) { + const childPath = getFirstAccessiblePath(item.children); + if (childPath) return childPath; + } + if (item.redirect && item.redirect !== '/login') { + return item.redirect; + } + if (item.path && item.path !== '/' && item.path !== '/login') { + return item.path; + } + } + return '/'; +}; + +const canAccessRedirectRoute = (path: string) => { + if (!path || path === '/login') return false; + const resolved = router.resolve(path); + const lastMatchedRoute = resolved.matched[resolved.matched.length - 1]; + if (!lastMatchedRoute || lastMatchedRoute.name === 'notFound') return false; + const routePathSet = flattenRoutePaths(routesList.value as any[]); + return routePathSet.has(normalizeRoutePath(lastMatchedRoute.path)); +}; + +const getRedirectQuery = () => { + const queryParams = route.query?.params; + if (typeof queryParams !== 'string' || !queryParams) return undefined; + try { + const parsedQuery = JSON.parse(queryParams); + return parsedQuery && typeof parsedQuery === 'object' ? parsedQuery : undefined; + } catch { + return undefined; + } +}; + +const getRedirectTarget = () => { + const redirectPath = typeof route.query?.redirect === 'string' ? route.query.redirect : ''; + if (canAccessRedirectRoute(redirectPath)) { + return { + path: redirectPath, + query: getRedirectQuery(), + }; + } + if (redirectPath) { + // ElMessage.warning('无权访问目标页面,已为您跳转首页'); + } + return { + path: getFirstAccessiblePath(routesList.value as any[]), + }; +}; + const onSignIn = async () => { formRef.value!.validate(async (valid) => { if (valid) { @@ -169,20 +243,13 @@ const onSignIn = async () => { }); }; -const signInSuccess = (isNoPower) => { +const signInSuccess = (isNoPower: boolean | undefined) => { if (isNoPower) { ElMessage.warning('抱歉,该账号对应的角色未配置任何权限'); Session.clear(); } else { let currentTimeInfo = currentTime.value; - if (route.query?.redirect) { - router.push({ - path: route.query?.redirect, - query: Object.keys(route.query?.params).length > 0 ? JSON.parse(route.query?.params) : '', - }); - } else { - router.push('/'); - } + router.push(getRedirectTarget()); const signInText = t('message.signInText'); ElMessage.success(`${currentTimeInfo},${signInText}`); NextLoading.start(); @@ -197,7 +264,7 @@ const goToRegister = () => { emit('switchTab', 'register'); }; -const enterHandler = async (event) => { +const enterHandler = async (event: KeyboardEvent) => { if (event.key === 'Enter') { await onSignIn(); } diff --git a/src/views/login/component/mobile.vue b/src/views/login/component/mobile.vue index a8abad4..1e8fb19 100644 --- a/src/views/login/component/mobile.vue +++ b/src/views/login/component/mobile.vue @@ -62,6 +62,7 @@ import { ElMessage } from 'element-plus'; import type { FormInstance, FormRules } from 'element-plus'; import { storeToRefs } from 'pinia'; import { useThemeConfig } from '/@/stores/themeConfig'; +import { useRoutesList } from '/@/stores/routesList'; import { initFrontEndControlRoutes } from '/@/router/frontEnd'; import { initBackEndControlRoutes } from '/@/router/backEnd'; import { Session } from '/@/utils/storage'; @@ -74,6 +75,8 @@ const emit = defineEmits(['switchTab']); const storesThemeConfig = useThemeConfig(); const { themeConfig } = storeToRefs(storesThemeConfig); +const storesRoutesList = useRoutesList(); +const { routesList } = storeToRefs(storesRoutesList); const route = useRoute(); const router = useRouter(); const formRef = ref(); @@ -158,20 +161,84 @@ const onSmsLogin = async () => { }); }; +const normalizeRoutePath = (path: string) => { + if (!path) return '/'; + const normalizedPath = path.split('?')[0].replace(/\/+$/, ''); + return normalizedPath || '/'; +}; + +const flattenRoutePaths = (list: any[] = []) => { + const pathSet = new Set(); + const walk = (items: any[]) => { + items.forEach((item) => { + if (item?.path) pathSet.add(normalizeRoutePath(item.path)); + if (item?.children?.length) walk(item.children); + }); + }; + walk(list); + return pathSet; +}; + +const getFirstAccessiblePath = (list: any[] = []): string => { + for (const item of list) { + if (!item || item.meta?.isHide) continue; + if (item.children?.length) { + const childPath = getFirstAccessiblePath(item.children); + if (childPath) return childPath; + } + if (item.redirect && item.redirect !== '/login') { + return item.redirect; + } + if (item.path && item.path !== '/' && item.path !== '/login') { + return item.path; + } + } + return '/'; +}; + +const canAccessRedirectRoute = (path: string) => { + if (!path || path === '/login') return false; + const resolved = router.resolve(path); + const lastMatchedRoute = resolved.matched[resolved.matched.length - 1]; + if (!lastMatchedRoute || lastMatchedRoute.name === 'notFound') return false; + const routePathSet = flattenRoutePaths(routesList.value as any[]); + return routePathSet.has(normalizeRoutePath(lastMatchedRoute.path)); +}; + +const getRedirectQuery = () => { + const queryParams = route.query?.params; + if (typeof queryParams !== 'string' || !queryParams) return undefined; + try { + const parsedQuery = JSON.parse(queryParams); + return parsedQuery && typeof parsedQuery === 'object' ? parsedQuery : undefined; + } catch { + return undefined; + } +}; + +const getRedirectTarget = () => { + const redirectPath = typeof route.query?.redirect === 'string' ? route.query.redirect : ''; + if (canAccessRedirectRoute(redirectPath)) { + return { + path: redirectPath, + query: getRedirectQuery(), + }; + } + if (redirectPath) { + ElMessage.warning('无权访问目标页面,已为您跳转首页'); + } + return { + path: getFirstAccessiblePath(routesList.value as any[]), + }; +}; + const signInSuccess = (isNoPower: boolean | undefined) => { if (isNoPower) { ElMessage.warning('抱歉,该账号对应的角色未配置任何权限'); Session.clear(); } else { let currentTimeInfo = currentTime.value; - if (route.query?.redirect) { - router.push({ - path: route.query?.redirect, - query: Object.keys(route.query?.params).length > 0 ? JSON.parse(route.query?.params) : '', - }); - } else { - router.push('/'); - } + router.push(getRedirectTarget()); const signInText = '登录成功'; ElMessage.success(`${currentTimeInfo},${signInText}`); NextLoading.start();