Files
Neo-ZQYY/apps/tenant-admin/src/App.tsx
Neo 6f8f12314f feat: 累积功能变更 — 聊天集成、租户管理、小程序更新、ETL 增强、迁移脚本
包含多个会话的累积代码变更:
- backend: AI 聊天服务、触发器调度、认证增强、WebSocket、调度器最小间隔
- admin-web: ETL 状态页、任务管理、调度配置、登录优化
- miniprogram: 看板页面、聊天集成、UI 组件、导航更新
- etl: DWS 新任务(finance_area_daily/board_cache)、连接器增强
- tenant-admin: 项目初始化
- db: 19 个迁移脚本(etl_feiqiu 11 + zqyy_app 8)
- packages/shared: 枚举和工具函数更新
- tools: 数据库工具、报表生成、健康检查
- docs: PRD/架构/部署/合约文档更新

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-06 00:03:48 +08:00

156 lines
4.9 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* 主布局与路由配置。
*
* - Ant Design LayoutSider + Content
* - react-router-dom v6 路由
* - 未认证重定向到 /login
* - 路由:/login, /applications, /users, /excel, /clues, / → /applications
*/
import React from "react";
import { Routes, Route, Navigate, useNavigate, useLocation } from "react-router-dom";
import { Layout, Menu, Button, Tooltip } from "antd";
import {
AuditOutlined,
TeamOutlined,
FileExcelOutlined,
SolutionOutlined,
LogoutOutlined,
UserSwitchOutlined,
} from "@ant-design/icons";
import type { MenuProps } from "antd";
import { AuthProvider, useAuth } from "./hooks/useAuth";
import Login from "./pages/Login";
import UserApproval from "./pages/UserApproval";
import UserManagement from "./pages/UserManagement";
import ExcelUpload from "./pages/ExcelUpload";
import RetentionClues from "./pages/RetentionClues";
import SiteAdmins from "./pages/SiteAdmins";
const { Sider, Content } = Layout;
/* ------------------------------------------------------------------ */
/* 侧边栏导航配置 */
/* ------------------------------------------------------------------ */
function getNavItems(adminType: string): MenuProps["items"] {
const items: MenuProps["items"] = [
{ key: "/applications", icon: <AuditOutlined />, label: "用户审核" },
{ key: "/users", icon: <TeamOutlined />, label: "用户管理" },
{ key: "/excel", icon: <FileExcelOutlined />, label: "Excel 上传" },
{ key: "/clues", icon: <SolutionOutlined />, label: "维客线索管理" },
];
// 仅租户管理员可见
if (adminType === "tenant_admin") {
items.push({ key: "/site-admins", icon: <UserSwitchOutlined />, label: "店铺管理员" });
}
return items;
}
/* ------------------------------------------------------------------ */
/* 路由守卫 */
/* ------------------------------------------------------------------ */
const PrivateRoute: React.FC<{ children: React.ReactNode }> = ({ children }) => {
const { isAuthenticated } = useAuth();
return isAuthenticated ? <>{children}</> : <Navigate to="/login" replace />;
};
/* ------------------------------------------------------------------ */
/* 主布局(含侧边栏) */
/* ------------------------------------------------------------------ */
const AppLayout: React.FC = () => {
const navigate = useNavigate();
const location = useLocation();
const { logout, user } = useAuth();
const navItems = getNavItems(user?.adminType ?? "site_admin");
const onMenuClick: MenuProps["onClick"] = ({ key }) => {
navigate(key);
};
const handleLogout = () => {
logout();
};
return (
<Layout style={{ minHeight: "100vh" }}>
<Sider collapsible style={{ display: "flex", flexDirection: "column" }}>
<div
style={{
height: 48,
margin: "12px 16px",
color: "#fff",
fontWeight: 700,
fontSize: 16,
textAlign: "center",
lineHeight: "48px",
whiteSpace: "nowrap",
overflow: "hidden",
}}
>
</div>
<Menu
theme="dark"
mode="inline"
selectedKeys={[location.pathname]}
items={navItems}
onClick={onMenuClick}
/>
<div style={{ flex: 1 }} />
<div style={{ padding: "12px 16px" }}>
<Tooltip title="退出登录">
<Button
type="text"
icon={<LogoutOutlined />}
onClick={handleLogout}
style={{ color: "rgba(255,255,255,0.65)", width: "100%" }}
>
退
</Button>
</Tooltip>
</div>
</Sider>
<Layout>
<Content style={{ margin: 16, minHeight: 280 }}>
<Routes>
<Route path="/applications" element={<UserApproval />} />
<Route path="/users" element={<UserManagement />} />
<Route path="/excel" element={<ExcelUpload />} />
<Route path="/clues" element={<RetentionClues />} />
<Route path="/site-admins" element={<SiteAdmins />} />
<Route path="/" element={<Navigate to="/applications" replace />} />
</Routes>
</Content>
</Layout>
</Layout>
);
};
/* ------------------------------------------------------------------ */
/* 根组件 */
/* ------------------------------------------------------------------ */
const App: React.FC = () => {
return (
<AuthProvider>
<Routes>
<Route path="/login" element={<Login />} />
<Route
path="/*"
element={
<PrivateRoute>
<AppLayout />
</PrivateRoute>
}
/>
</Routes>
</AuthProvider>
);
};
export default App;