From 685856e02ec33e9ae1fe61f3c8a5ad38498fcb9f Mon Sep 17 00:00:00 2001 From: Codex Date: Thu, 2 Jul 2026 01:55:07 +0800 Subject: [PATCH] fix: gate Taro routes before rendering --- apps/taro/src/app.css | 50 ++++++++++++++ apps/taro/src/app.tsx | 66 ++++++++++++++++++- .../src/pages/platform-admin/platform.css | 55 ++++++++++++++++ apps/taro/src/pages/student/home/index.css | 43 ++++++++++++ apps/taro/src/pages/student/student.css | 65 ++++++++++++++++++ apps/taro/src/pages/tenant-admin/admin.css | 61 +++++++++++++++++ apps/taro/src/services/routeGuard.ts | 26 +++++--- 7 files changed, 354 insertions(+), 12 deletions(-) diff --git a/apps/taro/src/app.css b/apps/taro/src/app.css index ec7d590f..cbfc75c2 100644 --- a/apps/taro/src/app.css +++ b/apps/taro/src/app.css @@ -79,3 +79,53 @@ textarea { border-radius: 999px; background: rgba(255, 255, 255, 0.13); } + +.route-guard-page { + min-height: 100vh; + padding: 28px; + background: var(--tiku-page); +} + +.route-guard-shell { + width: 100%; + max-width: 760px; + margin: 0 auto; + padding-top: 52px; +} + +.route-guard-panel { + display: flex; + flex-direction: column; + gap: 14px; + padding: 38px; + border: 1px solid #dbeafe; + border-radius: 30px; + background: linear-gradient(135deg, #1152d4 0%, #2563eb 62%, #60a5fa 100%); + box-shadow: var(--tiku-shadow-blue); + color: #fff; +} + +.route-guard-kicker { + display: block; + color: rgba(255, 255, 255, 0.78); + font-size: 22px; + font-weight: 800; + letter-spacing: 0; +} + +.route-guard-title { + display: block; + color: #fff; + font-size: 40px; + font-weight: 900; + line-height: 1.16; + letter-spacing: 0; +} + +.route-guard-subtitle { + display: block; + color: rgba(255, 255, 255, 0.86); + font-size: 24px; + line-height: 1.6; + letter-spacing: 0; +} diff --git a/apps/taro/src/app.tsx b/apps/taro/src/app.tsx index a12045a5..e07eeb29 100644 --- a/apps/taro/src/app.tsx +++ b/apps/taro/src/app.tsx @@ -1,12 +1,72 @@ -import { PropsWithChildren } from 'react'; +import { PropsWithChildren, useEffect, useState } from 'react'; +import { Text, View } from '@tarojs/components'; import { useDidShow } from '@tarojs/taro'; import 'katex/dist/katex.min.css'; -import { guardCurrentRoute } from '@/services/routeGuard'; +import { appEnv } from '@/env'; +import { currentPagePath, guardCurrentRoute, shouldGuardPath } from '@/services/routeGuard'; import './app.css'; export default function App({ children }: PropsWithChildren) { + const [routeReady, setRouteReady] = useState(() => !shouldGuardPath(currentPagePath())); + + function verifyCurrentRoute() { + const path = currentPagePath(); + const protectedRoute = shouldGuardPath(path); + setRouteReady(!protectedRoute); + guardCurrentRoute() + .then(ok => setRouteReady(Boolean(ok))) + .catch(() => setRouteReady(false)); + } + + useEffect(() => { + verifyCurrentRoute(); + }, []); + useDidShow(() => { - guardCurrentRoute().catch(() => undefined); + verifyCurrentRoute(); }); + + if (!routeReady && appEnv.portal === 'tenant-admin') { + return ( + + + + Tenant Admin + 正在校验后台权限 + 请先完成登录,系统会确认当前账号是否拥有租户后台权限。 + + + + ); + } + + if (!routeReady && appEnv.portal === 'platform-admin') { + return ( + + + + Platform Admin + 正在校验平台权限 + 请先完成登录,系统会确认当前账号是否拥有平台管理员权限。 + + + + ); + } + + if (!routeReady) { + return ( + + + + Student + 正在校验登录状态 + 请先完成登录,系统会带你回到刚才打开的页面。 + + + + ); + } + return children; } diff --git a/apps/taro/src/pages/platform-admin/platform.css b/apps/taro/src/pages/platform-admin/platform.css index 2af04bef..eb16f400 100644 --- a/apps/taro/src/pages/platform-admin/platform.css +++ b/apps/taro/src/pages/platform-admin/platform.css @@ -354,9 +354,64 @@ color: var(--tiku-primary); } +.platform-chip.readonly { + border-color: #bfdbfe; + background: #eff6ff; + color: #1d4ed8; +} + .platform-error { display: block; margin-top: 12px; color: #be123c; font-size: 22px; } + +@media (min-width: 980px) { + .platform-page { + max-width: 1180px; + margin: 0 auto; + padding: 38px; + } + + .platform-shell { + gap: 30px; + } + + .platform-header { + padding: 38px; + } + + .platform-title { + font-size: 44px; + } + + .platform-grid { + grid-template-columns: repeat(4, minmax(0, 1fr)); + } + + .platform-grid.three { + grid-template-columns: repeat(3, minmax(0, 1fr)); + } + + .platform-module-grid { + grid-template-columns: repeat(4, minmax(0, 1fr)); + } +} + +@media (max-width: 520px) { + .platform-page { + padding: 20px; + } + + .platform-grid, + .platform-grid.three, + .platform-module-grid, + .platform-form { + grid-template-columns: minmax(0, 1fr); + } + + .platform-title { + font-size: 34px; + } +} diff --git a/apps/taro/src/pages/student/home/index.css b/apps/taro/src/pages/student/home/index.css index 2a6a2c91..014eb50e 100644 --- a/apps/taro/src/pages/student/home/index.css +++ b/apps/taro/src/pages/student/home/index.css @@ -323,3 +323,46 @@ font-size: 42px; line-height: 1; } + +@media (min-width: 900px) { + .student-page { + max-width: 980px; + margin: 0 auto; + padding: 36px; + } + + .hero-band { + min-height: 320px; + padding: 44px; + } + + .hero-title { + font-size: 52px; + } + + .entry-grid { + grid-template-columns: repeat(3, minmax(0, 1fr)); + } +} + +@media (max-width: 420px) { + .student-page { + padding: 20px; + } + + .brand { + font-size: 32px; + } + + .hero-title { + font-size: 38px; + } + + .home-stat-grid { + gap: 10px; + } + + .entry-grid { + gap: 12px; + } +} diff --git a/apps/taro/src/pages/student/student.css b/apps/taro/src/pages/student/student.css index 94957421..f3c0ddea 100644 --- a/apps/taro/src/pages/student/student.css +++ b/apps/taro/src/pages/student/student.css @@ -1301,3 +1301,68 @@ grid-template-columns: minmax(0, 1fr); } } + +.points-list { + gap: 16px; +} + +.points-list .list-row { + border-color: #bfdbfe; + background: linear-gradient(135deg, #eff6ff, #fff); +} + +.points-list .list-row:nth-child(2n) { + border-color: #bbf7d0; + background: linear-gradient(135deg, #ecfdf5, #fff); +} + +.scoreline-page .student-topbar { + align-items: flex-start; +} + +.scoreline-page .student-title { + font-size: 40px; +} + +.scoreline-page .secondary-button { + flex: 0 0 auto; +} + +@media (min-width: 900px) { + .student-page { + max-width: 900px; + margin: 0 auto; + padding: 36px; + } + + .student-topbar { + margin-bottom: 28px; + } + + .student-title { + font-size: 42px; + } + + .answer-sheet { + grid-template-columns: repeat(10, minmax(0, 1fr)); + } + + .toolbar.wrap { + gap: 14px; + } +} + +@media (max-width: 420px) { + .student-page { + padding: 20px; + } + + .student-title { + font-size: 34px; + } + + .student-subtitle, + .row-meta { + font-size: 21px; + } +} diff --git a/apps/taro/src/pages/tenant-admin/admin.css b/apps/taro/src/pages/tenant-admin/admin.css index fdd9ed75..7b37d8e8 100644 --- a/apps/taro/src/pages/tenant-admin/admin.css +++ b/apps/taro/src/pages/tenant-admin/admin.css @@ -355,6 +355,13 @@ background: #fff; } +.admin-row.nested { + margin-top: 14px; + border-color: #bfdbfe; + background: linear-gradient(135deg, #eff6ff, #fff); + box-shadow: none; +} + .admin-error { display: block; margin-top: 12px; @@ -421,3 +428,57 @@ overflow-wrap: anywhere; white-space: pre-wrap; } + +@media (min-width: 980px) { + .admin-page { + max-width: 1180px; + margin: 0 auto; + padding: 38px; + } + + .admin-shell { + gap: 30px; + } + + .admin-header { + padding: 38px; + } + + .admin-title { + font-size: 44px; + } + + .admin-grid { + grid-template-columns: repeat(4, minmax(0, 1fr)); + } + + .admin-grid.three, + .admin-metric-grid { + grid-template-columns: repeat(3, minmax(0, 1fr)); + } + + .admin-module-grid { + grid-template-columns: repeat(3, minmax(0, 1fr)); + } + + .admin-form-grid { + grid-template-columns: repeat(2, minmax(0, 1fr)); + } +} + +@media (max-width: 520px) { + .admin-page { + padding: 20px; + } + + .admin-grid, + .admin-grid.three, + .admin-metric-grid, + .admin-module-grid { + grid-template-columns: minmax(0, 1fr); + } + + .admin-title { + font-size: 34px; + } +} diff --git a/apps/taro/src/services/routeGuard.ts b/apps/taro/src/services/routeGuard.ts index 3ffffeb2..889d33dc 100644 --- a/apps/taro/src/services/routeGuard.ts +++ b/apps/taro/src/services/routeGuard.ts @@ -23,17 +23,23 @@ function forbiddenUrl(reason: string, redirectPath: string) { return `/pages/student/login/index?reason=${encodeURIComponent(reason)}&redirect=${encodeURIComponent(safeRedirectPath(redirectPath))}`; } -function currentPagePath() { +export function currentPagePath() { const instance = Taro.getCurrentInstance(); const path = instance.router?.path || ''; - return path.startsWith('/') ? path : `/${path}`; + const normalizedPath = path.startsWith('/') ? path : `/${path}`; + if (normalizedPath !== '/') return normalizedPath; + if (isH5Runtime() && typeof window !== 'undefined') { + const pathname = window.location.pathname || ''; + if (pathname.startsWith('/pages/')) return pathname; + } + return normalizedPath; } function isPublicPath(path: string) { return path === '/pages/bootstrap/index' || path === '/pages/student/login/index'; } -function shouldGuardPath(path: string) { +export function shouldGuardPath(path: string) { if (isPublicPath(path)) return false; if (appEnv.portal === 'tenant-admin') return path.startsWith('/pages/tenant-admin/'); if (appEnv.portal === 'platform-admin') return path.startsWith('/pages/platform-admin/'); @@ -93,18 +99,20 @@ export async function requireTenantAdmin(redirectPath: string) { export async function guardCurrentRoute() { await ensureRuntimeConfigLoaded(); const path = currentPagePath(); - if (!path || !shouldGuardPath(path) || pendingGuardPath === path) return; + if (!path || !shouldGuardPath(path)) return true; + if (pendingGuardPath === path) return false; pendingGuardPath = path; try { if (appEnv.portal === 'platform-admin') { - await requirePlatformAdmin(path); - return; + const payload = await requirePlatformAdmin(path); + return Boolean(payload); } if (appEnv.portal === 'tenant-admin') { - await requireTenantAdmin(path); - return; + const payload = await requireTenantAdmin(path); + return Boolean(payload); } - await requireSignedIn(path); + const payload = await requireSignedIn(path); + return Boolean(payload); } finally { pendingGuardPath = ''; }