From 0c27280f42cb3bec684784835a799849e36dbdf3 Mon Sep 17 00:00:00 2001 From: Codex Date: Thu, 2 Jul 2026 02:45:29 +0800 Subject: [PATCH] style: apply legacy admin shell across backoffice portals --- apps/taro/src/app.tsx | 11 + apps/taro/src/components/AdminLegacyShell.css | 421 ++++++++++++++++++ apps/taro/src/components/AdminLegacyShell.tsx | 123 +++++ apps/taro/src/index.html | 11 +- 4 files changed, 565 insertions(+), 1 deletion(-) create mode 100644 apps/taro/src/components/AdminLegacyShell.css create mode 100644 apps/taro/src/components/AdminLegacyShell.tsx diff --git a/apps/taro/src/app.tsx b/apps/taro/src/app.tsx index 70483456..129af959 100644 --- a/apps/taro/src/app.tsx +++ b/apps/taro/src/app.tsx @@ -1,6 +1,7 @@ import { PropsWithChildren, useEffect, useState } from 'react'; import { Text, View } from '@tarojs/components'; import 'katex/dist/katex.min.css'; +import { AdminLegacyShell } from '@/components/AdminLegacyShell'; import { StudentLegacyShell } from '@/components/StudentLegacyShell'; import { appEnv, isH5Runtime } from '@/env'; import { currentPagePath, guardCurrentRoute, shouldGuardPath } from '@/services/routeGuard'; @@ -53,6 +54,12 @@ function shouldUseStudentShell(path: string) { ].includes(path); } +function shouldUseAdminShell(path: string) { + if (appEnv.portal === 'tenant-admin') return path.startsWith('/pages/tenant-admin/'); + if (appEnv.portal === 'platform-admin') return path.startsWith('/pages/platform-admin/'); + return false; +} + export default function App({ children }: PropsWithChildren) { const [routeReady, setRouteReady] = useState(() => !shouldGuardPath(currentPagePath())); const [path, setPath] = useState(() => currentPagePath()); @@ -118,5 +125,9 @@ export default function App({ children }: PropsWithChildren) { return {children}; } + if (shouldUseAdminShell(path)) { + return {children}; + } + return children; } diff --git a/apps/taro/src/components/AdminLegacyShell.css b/apps/taro/src/components/AdminLegacyShell.css new file mode 100644 index 00000000..cd38efe4 --- /dev/null +++ b/apps/taro/src/components/AdminLegacyShell.css @@ -0,0 +1,421 @@ +.backoffice-legacy-layout { + min-height: 100vh; + background: + linear-gradient(to right, rgba(15, 23, 42, 0.035) 1px, transparent 1px), + linear-gradient(to bottom, rgba(15, 23, 42, 0.035) 1px, transparent 1px), + #f4f6f9; + color: #111827; +} + +.backoffice-legacy-main { + min-width: 0; +} + +.backoffice-legacy-sidebar { + display: none; +} + +.backoffice-mobile-nav { + position: sticky; + top: 0; + z-index: 28; + display: flex; + gap: 10px; + overflow-x: auto; + padding: 14px 16px; + border-bottom: 1px solid #e5e7eb; + background: rgba(255, 255, 255, 0.96); + box-shadow: 0 6px 18px rgba(15, 23, 42, 0.06); +} + +.backoffice-mobile-item { + display: flex; + align-items: center; + gap: 7px; + flex: 0 0 auto; + min-height: 44px; + padding: 0 12px; + border-radius: 14px; + background: #f8fafc; + color: #64748b; +} + +.backoffice-mobile-item.active { + background: #1e293b; + color: #fff; + box-shadow: 0 6px 15px rgba(15, 23, 42, 0.18); +} + +.backoffice-mobile-mark { + font-size: 18px; + font-weight: 900; +} + +.backoffice-mobile-text { + font-size: 20px; + font-weight: 780; + white-space: nowrap; +} + +.backoffice-legacy-main .admin-page, +.backoffice-legacy-main .platform-page { + min-height: auto; + padding: 18px; + background: transparent; +} + +.backoffice-legacy-main .admin-shell, +.backoffice-legacy-main .platform-shell { + gap: 18px; +} + +.backoffice-legacy-main .admin-header, +.backoffice-legacy-main .platform-header { + border: 1px solid #f1f5f9; + border-radius: 18px; + background: #fff; + box-shadow: 0 1px 3px rgba(15, 23, 42, 0.04); +} + +.backoffice-legacy-main .admin-header::after, +.backoffice-legacy-main .platform-header::after { + display: none; +} + +.backoffice-legacy-main .admin-kicker, +.backoffice-legacy-main .platform-kicker { + color: #2563eb; + font-size: 18px; + font-weight: 800; +} + +.backoffice-legacy-main .admin-title, +.backoffice-legacy-main .platform-title { + color: #111827; + font-size: 33px; + font-weight: 900; +} + +.backoffice-legacy-main .admin-subtitle, +.backoffice-legacy-main .platform-subtitle { + color: #64748b; + font-size: 21px; +} + +.backoffice-legacy-main .admin-section, +.backoffice-legacy-main .platform-section, +.backoffice-legacy-main .admin-metric, +.backoffice-legacy-main .platform-metric, +.backoffice-legacy-main .admin-row, +.backoffice-legacy-main .platform-row { + border-color: #f1f5f9; + border-radius: 16px; + background: #fff; + box-shadow: 0 1px 3px rgba(15, 23, 42, 0.04); +} + +.backoffice-legacy-main .admin-section, +.backoffice-legacy-main .platform-section { + padding: 20px; +} + +.backoffice-legacy-main .admin-section-title, +.backoffice-legacy-main .platform-section-title { + margin-bottom: 14px; + color: #111827; + font-size: 25px; + font-weight: 900; +} + +.backoffice-legacy-main .admin-module-card, +.backoffice-legacy-main .platform-module-card { + min-height: 132px; + border: 1px solid #f1f5f9; + border-radius: 16px; + background: #fff; + box-shadow: 0 1px 3px rgba(15, 23, 42, 0.04); +} + +.backoffice-legacy-main .admin-module-card::after, +.backoffice-legacy-main .platform-module-card::after { + display: none; +} + +.backoffice-legacy-main .admin-module-label, +.backoffice-legacy-main .platform-module-label { + color: #2563eb; + font-size: 17px; +} + +.backoffice-legacy-main .admin-module-card .admin-row-main, +.backoffice-legacy-main .platform-module-card .platform-row-main { + margin-top: 22px; + color: #111827; + font-size: 27px; +} + +.backoffice-legacy-main .admin-module-card .admin-row-meta, +.backoffice-legacy-main .platform-module-card .platform-row-meta { + color: #64748b; +} + +.backoffice-legacy-main .admin-button, +.backoffice-legacy-main .platform-button, +.backoffice-legacy-main .admin-mini-button, +.backoffice-legacy-main .platform-mini-button { + border-radius: 12px; +} + +.backoffice-legacy-main .admin-button.primary, +.backoffice-legacy-main .platform-button.primary, +.backoffice-legacy-main .admin-mini-button.primary { + border-color: #1e293b; + background: #1e293b; + box-shadow: 0 6px 14px rgba(15, 23, 42, 0.14); +} + +.backoffice-legacy-main .admin-button.active, +.backoffice-legacy-main .platform-button.active, +.backoffice-legacy-main .platform-mini-button.active { + border-color: #1e293b; + background: #1e293b; + color: #fff; +} + +@media (min-width: 980px) { + .backoffice-legacy-layout { + display: grid; + grid-template-columns: 256px minmax(0, 1fr); + gap: 16px; + padding: 16px; + } + + .backoffice-mobile-nav { + display: none; + } + + .backoffice-legacy-sidebar { + position: sticky; + top: 16px; + display: flex; + height: calc(100vh - 32px); + flex-direction: column; + padding: 16px; + border: 1px solid #f1f5f9; + border-radius: 24px; + background: #fff; + box-shadow: 0 8px 30px rgba(15, 23, 42, 0.04); + } + + .backoffice-brand { + display: flex; + align-items: center; + gap: 12px; + padding: 14px 10px 18px; + } + + .backoffice-brand-icon { + display: flex; + align-items: center; + justify-content: center; + flex: 0 0 42px; + width: 42px; + height: 42px; + border-radius: 12px; + background: #eff6ff; + color: #1d4ed8; + } + + .backoffice-brand-mark { + font-size: 22px; + font-weight: 950; + } + + .backoffice-brand-copy { + min-width: 0; + } + + .backoffice-brand-title { + display: block; + color: #1f2937; + font-size: 20px; + font-weight: 900; + line-height: 1.25; + } + + .backoffice-brand-sub { + display: block; + margin-top: 3px; + color: #64748b; + font-size: 13px; + font-weight: 720; + line-height: 1.25; + } + + .backoffice-tenant-select { + padding: 0 10px 16px; + } + + .backoffice-field-label { + display: block; + margin-bottom: 6px; + color: #9ca3af; + font-size: 12px; + font-weight: 900; + } + + .backoffice-select-box { + display: flex; + align-items: center; + min-height: 42px; + padding: 0 12px; + border: 1px solid #bfdbfe; + border-radius: 13px; + background: #eff6ff; + } + + .backoffice-select-text { + color: #1d4ed8; + font-size: 16px; + font-weight: 900; + } + + .backoffice-nav { + display: flex; + flex: 1; + flex-direction: column; + gap: 6px; + overflow-y: auto; + padding-right: 4px; + } + + .backoffice-nav-block { + display: flex; + flex-direction: column; + gap: 4px; + } + + .backoffice-nav-group { + display: block; + margin: 10px 0 3px; + padding: 0 12px; + color: #9ca3af; + font-size: 12px; + font-weight: 900; + } + + .backoffice-nav-btn { + display: flex; + align-items: center; + gap: 12px; + min-height: 48px; + padding: 0 14px; + border-radius: 12px; + color: #475569; + } + + .backoffice-nav-btn.active { + background: #1e293b; + color: #fff; + box-shadow: 0 4px 12px rgba(15, 23, 42, 0.15); + } + + .backoffice-nav-mark { + display: flex; + align-items: center; + justify-content: center; + width: 24px; + height: 24px; + border-radius: 8px; + background: #f3f4f6; + color: inherit; + font-size: 14px; + font-weight: 900; + } + + .backoffice-nav-btn.active .backoffice-nav-mark { + background: rgba(255, 255, 255, 0.14); + } + + .backoffice-nav-text { + font-size: 16px; + font-weight: 860; + } + + .backoffice-footer { + display: flex; + align-items: center; + gap: 10px; + margin-top: 12px; + padding: 14px 10px 4px; + border-top: 1px solid #f1f5f9; + } + + .backoffice-footer-avatar { + display: flex; + align-items: center; + justify-content: center; + flex: 0 0 36px; + width: 36px; + height: 36px; + border-radius: 999px; + background: #f1f5f9; + color: #475569; + } + + .backoffice-footer-mark { + font-size: 16px; + font-weight: 900; + } + + .backoffice-footer-copy { + min-width: 0; + } + + .backoffice-footer-name { + display: block; + color: #334155; + font-size: 15px; + font-weight: 850; + } + + .backoffice-footer-meta { + display: block; + margin-top: 2px; + color: #94a3b8; + font-size: 12px; + font-weight: 720; + } + + .backoffice-legacy-main { + overflow: hidden; + border: 1px solid #f1f5f9; + border-radius: 24px; + background: #fff; + box-shadow: 0 1px 3px rgba(15, 23, 42, 0.04); + } + + .backoffice-legacy-main .admin-page, + .backoffice-legacy-main .platform-page { + max-width: none; + margin: 0; + padding: 24px; + } + + .backoffice-legacy-main .admin-grid, + .backoffice-legacy-main .platform-grid { + grid-template-columns: repeat(4, minmax(0, 1fr)); + } +} + +@media (max-width: 520px) { + .backoffice-legacy-main .admin-page, + .backoffice-legacy-main .platform-page { + padding: 16px; + } + + .backoffice-legacy-main .admin-title, + .backoffice-legacy-main .platform-title { + font-size: 30px; + } +} diff --git a/apps/taro/src/components/AdminLegacyShell.tsx b/apps/taro/src/components/AdminLegacyShell.tsx new file mode 100644 index 00000000..64a4c6b8 --- /dev/null +++ b/apps/taro/src/components/AdminLegacyShell.tsx @@ -0,0 +1,123 @@ +import { PropsWithChildren } from 'react'; +import Taro from '@tarojs/taro'; +import { Text, View } from '@tarojs/components'; +import { appEnv } from '@/env'; +import { currentPagePath } from '@/services/routeGuard'; +import './AdminLegacyShell.css'; + +type AdminNavItem = { + name: string; + path: string; + mark: string; + group?: string; +}; + +const tenantNavItems: AdminNavItem[] = [ + { group: '运营概览', name: '工作台', path: '/pages/tenant-admin/workbench/index', mark: '台' }, + { name: '数据看板', path: '/pages/tenant-admin/dashboard/index', mark: '数' }, + { group: '业务管理', name: '学生运营', path: '/pages/tenant-admin/students/index', mark: '生' }, + { name: '题库内容', path: '/pages/tenant-admin/content/index', mark: '题' }, + { name: '营销中心', path: '/pages/tenant-admin/marketing/index', mark: '销' }, + { name: '财务运营', path: '/pages/tenant-admin/finance/index', mark: '财' }, + { group: '系统', name: '租户设置', path: '/pages/tenant-admin/settings/index', mark: '设' }, +]; + +const platformNavItems: AdminNavItem[] = [ + { group: '平台概览', name: '工作台', path: '/pages/platform-admin/workbench/index', mark: '台' }, + { group: 'SaaS 管理', name: '租户管理', path: '/pages/platform-admin/tenants/index', mark: '租' }, + { name: '账务中心', path: '/pages/platform-admin/billing/index', mark: '账' }, + { name: '公共题库', path: '/pages/platform-admin/question-banks/index', mark: '库' }, + { group: '权限', name: '平台员工', path: '/pages/platform-admin/staff/index', mark: '员' }, +]; + +function isActivePath(currentPath: string, itemPath: string) { + return currentPath === itemPath; +} + +function navItemsForPortal() { + return appEnv.portal === 'platform-admin' ? platformNavItems : tenantNavItems; +} + +function titleForPortal() { + if (appEnv.portal === 'platform-admin') { + return { + title: '平台管理后台', + subtitle: '租户、账务、题库授权、员工权限', + badge: 'Console', + mark: '管', + }; + } + return { + title: '租户运营后台', + subtitle: '学生、题库、营销、财务与设置', + badge: 'Admin', + mark: '题', + }; +} + +export function AdminLegacyShell({ children }: PropsWithChildren) { + const currentPath = currentPagePath(); + const navItems = navItemsForPortal(); + const title = titleForPortal(); + let activeGroup = ''; + + return ( + + + + + {title.mark} + + + {title.title} + {title.subtitle} + + + + + 当前后台 + + {title.badge} + + + + + {navItems.map(item => { + const nextGroup = item.group && item.group !== activeGroup ? item.group : ''; + if (item.group) activeGroup = item.group; + return ( + + {nextGroup ? {nextGroup} : null} + Taro.redirectTo({ url: item.path })}> + {item.mark} + {item.name} + + + ); + })} + + + + + + + + 管理账号 + 已启用权限校验 + + + + + + {navItems.map(item => ( + Taro.redirectTo({ url: item.path })}> + {item.mark} + {item.name} + + ))} + + + {children} + + ); +} diff --git a/apps/taro/src/index.html b/apps/taro/src/index.html index aeb06d7b..7a343498 100644 --- a/apps/taro/src/index.html +++ b/apps/taro/src/index.html @@ -14,6 +14,8 @@