Compare commits
14 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 1da3ce95e2 | |||
| 683c0aba41 | |||
| a6636033f4 | |||
| a1c6074b3d | |||
| 783fe0bab3 | |||
| e6cf7548b1 | |||
| bc1c454fad | |||
| 9e2daf0022 | |||
| f61648bb0f | |||
| f4791e726e | |||
| 6dbbb5b71a | |||
| 38fda9aa9e | |||
| 77f9a908ad | |||
| 190d9e46f7 |
@@ -0,0 +1,16 @@
|
||||
import React from "react"
|
||||
import { Navigate } from "react-router-dom"
|
||||
import { useAuthStore } from "@/store/authStore"
|
||||
|
||||
/** 受保护的路由组件 */
|
||||
// eslint-disable-next-line react-refresh/only-export-components
|
||||
export const ProtectedRoute = ({ children }: { children: React.ReactNode }) => {
|
||||
const isAuthenticated = useAuthStore((state) => state.isAuthenticated)
|
||||
const hasAccessToken = Boolean(localStorage.getItem("access_token"))
|
||||
|
||||
if (!isAuthenticated || !hasAccessToken) {
|
||||
return <Navigate to="/login" replace />
|
||||
}
|
||||
|
||||
return <>{children}</>
|
||||
}
|
||||
@@ -0,0 +1,225 @@
|
||||
import { Navigate, type RouteObject } from "react-router-dom"
|
||||
import MainLayout from "@/components/layout/MainLayout"
|
||||
import { ProtectedRoute } from "./ProtectedRoute"
|
||||
|
||||
/**
|
||||
* 受保护的 /app 子路由
|
||||
* 所有页面使用 lazy 懒加载
|
||||
*/
|
||||
const appChildren: RouteObject[] = [
|
||||
{
|
||||
index: true,
|
||||
element: <Navigate to="/app/dashboard" replace />,
|
||||
},
|
||||
{
|
||||
path: "dashboard",
|
||||
lazy: () =>
|
||||
import("@/pages/dashboard/Dashboard").then((m) => ({
|
||||
Component: m.default,
|
||||
})),
|
||||
},
|
||||
{
|
||||
path: "assets",
|
||||
lazy: () =>
|
||||
import("@/pages/assets/AssetLibrary").then((m) => ({
|
||||
Component: m.default,
|
||||
})),
|
||||
},
|
||||
{
|
||||
path: "titles",
|
||||
lazy: () =>
|
||||
import("@/pages/titles/TitleLibrary").then((m) => ({
|
||||
Component: m.default,
|
||||
})),
|
||||
},
|
||||
{
|
||||
path: "voices",
|
||||
lazy: () =>
|
||||
import("@/pages/voices/VoiceLibrary").then((m) => ({
|
||||
Component: m.default,
|
||||
})),
|
||||
},
|
||||
{
|
||||
path: "templates",
|
||||
lazy: () =>
|
||||
import("@/pages/templates/TemplateLibrary").then((m) => ({
|
||||
Component: m.default,
|
||||
})),
|
||||
},
|
||||
{
|
||||
path: "generate",
|
||||
lazy: () =>
|
||||
import("@/pages/generate/GeneratePage").then((m) => ({
|
||||
Component: m.default,
|
||||
})),
|
||||
},
|
||||
{
|
||||
path: "history",
|
||||
lazy: () =>
|
||||
import("@/pages/history/TaskHistory").then((m) => ({
|
||||
Component: m.default,
|
||||
})),
|
||||
},
|
||||
{
|
||||
path: "products",
|
||||
lazy: () =>
|
||||
import("@/pages/products/ProductLibrary").then((m) => ({
|
||||
Component: m.default,
|
||||
})),
|
||||
},
|
||||
{
|
||||
path: "products/:id",
|
||||
lazy: () =>
|
||||
import("@/pages/products/ProductDetail").then((m) => ({
|
||||
Component: m.default,
|
||||
})),
|
||||
},
|
||||
{
|
||||
path: "tasks",
|
||||
lazy: () =>
|
||||
import("@/pages/tasks/TaskCenter").then((m) => ({
|
||||
Component: m.default,
|
||||
})),
|
||||
},
|
||||
{
|
||||
path: "editing-planner",
|
||||
lazy: () =>
|
||||
import("@/pages/editing-planner/EditingPlanner").then((m) => ({
|
||||
Component: m.default,
|
||||
})),
|
||||
},
|
||||
{
|
||||
path: "my-templates",
|
||||
lazy: () =>
|
||||
import("@/pages/my-templates/MyTemplates").then((m) => ({
|
||||
Component: m.default,
|
||||
})),
|
||||
},
|
||||
{
|
||||
path: "voice-clone",
|
||||
lazy: () =>
|
||||
import("@/pages/voice-clone/VoiceClone").then((m) => ({
|
||||
Component: m.default,
|
||||
})),
|
||||
},
|
||||
{
|
||||
path: "voice-materials",
|
||||
lazy: () =>
|
||||
import("@/pages/voice-materials/VoiceMaterialLibrary").then((m) => ({
|
||||
Component: m.default,
|
||||
})),
|
||||
},
|
||||
{
|
||||
path: "my-voices",
|
||||
lazy: () =>
|
||||
import("@/pages/my-voices/MyVoices").then((m) => ({
|
||||
Component: m.default,
|
||||
})),
|
||||
},
|
||||
{
|
||||
path: "accounts",
|
||||
lazy: () =>
|
||||
import("@/pages/accounts/Accounts").then((m) => ({
|
||||
Component: m.default,
|
||||
})),
|
||||
},
|
||||
{
|
||||
path: "duplication",
|
||||
lazy: () =>
|
||||
import("@/pages/duplication/DuplicationUpload").then((m) => ({
|
||||
Component: m.default,
|
||||
})),
|
||||
},
|
||||
{
|
||||
path: "duplication/results",
|
||||
lazy: () =>
|
||||
import("@/pages/duplication/DuplicationResults").then((m) => ({
|
||||
Component: m.default,
|
||||
})),
|
||||
},
|
||||
{
|
||||
path: "duplication/:id",
|
||||
lazy: () =>
|
||||
import("@/pages/duplication/DuplicationDetail").then((m) => ({
|
||||
Component: m.default,
|
||||
})),
|
||||
},
|
||||
{
|
||||
path: "subscription",
|
||||
lazy: () =>
|
||||
import("@/pages/subscription/Plans").then((m) => ({
|
||||
Component: m.default,
|
||||
})),
|
||||
},
|
||||
{
|
||||
path: "subscription/upgrade",
|
||||
lazy: () =>
|
||||
import("@/pages/subscription/UpgradeSubscription").then((m) => ({
|
||||
Component: m.default,
|
||||
})),
|
||||
},
|
||||
{
|
||||
path: "subscription/billing",
|
||||
lazy: () =>
|
||||
import("@/pages/subscription/Billing").then((m) => ({
|
||||
Component: m.default,
|
||||
})),
|
||||
},
|
||||
{
|
||||
path: "profile",
|
||||
lazy: () =>
|
||||
import("@/pages/profile/Settings").then((m) => ({
|
||||
Component: m.default,
|
||||
})),
|
||||
},
|
||||
{
|
||||
path: "admin",
|
||||
children: [
|
||||
{
|
||||
index: true,
|
||||
lazy: () =>
|
||||
import("@/pages/admin/AdminComingSoon").then((m) => ({
|
||||
Component: m.default,
|
||||
})),
|
||||
},
|
||||
{
|
||||
path: "users",
|
||||
lazy: () =>
|
||||
import("@/pages/admin/AdminComingSoon").then((m) => ({
|
||||
Component: m.default,
|
||||
})),
|
||||
},
|
||||
{
|
||||
path: "analytics",
|
||||
lazy: () =>
|
||||
import("@/pages/admin/AdminComingSoon").then((m) => ({
|
||||
Component: m.default,
|
||||
})),
|
||||
},
|
||||
{
|
||||
path: "monitor",
|
||||
lazy: () =>
|
||||
import("@/pages/admin/AdminComingSoon").then((m) => ({
|
||||
Component: m.default,
|
||||
})),
|
||||
},
|
||||
{
|
||||
path: "logs",
|
||||
lazy: () =>
|
||||
import("@/pages/admin/AdminComingSoon").then((m) => ({
|
||||
Component: m.default,
|
||||
})),
|
||||
},
|
||||
],
|
||||
},
|
||||
]
|
||||
|
||||
export const appRoutes: RouteObject = {
|
||||
path: "/app",
|
||||
element: (
|
||||
<ProtectedRoute>
|
||||
<MainLayout />
|
||||
</ProtectedRoute>
|
||||
),
|
||||
children: appChildren,
|
||||
}
|
||||
@@ -3,283 +3,13 @@
|
||||
* 扁平化路由:去掉 Project 层级,所有资源直接归属用户
|
||||
*/
|
||||
import { createBrowserRouter, Navigate } from "react-router-dom"
|
||||
import React from "react"
|
||||
import MainLayout from "@/components/layout/MainLayout"
|
||||
import Login from "@/pages/auth/Login"
|
||||
import Register from "@/pages/auth/Register"
|
||||
import ForgotPassword from "@/pages/auth/ForgotPassword"
|
||||
import ResetPassword from "@/pages/auth/ResetPassword"
|
||||
import WechatCallback from "@/pages/auth/WechatCallback"
|
||||
import HomePage from "@/pages/home/HomePage"
|
||||
import { useAuthStore } from "@/store/authStore"
|
||||
|
||||
/** 受保护的路由组件 */
|
||||
// eslint-disable-next-line react-refresh/only-export-components
|
||||
const ProtectedRoute = ({ children }: { children: React.ReactNode }) => {
|
||||
const isAuthenticated = useAuthStore((state) => state.isAuthenticated)
|
||||
const hasAccessToken = Boolean(localStorage.getItem("access_token"))
|
||||
|
||||
if (!isAuthenticated || !hasAccessToken) {
|
||||
return <Navigate to="/login" replace />
|
||||
}
|
||||
|
||||
return <>{children}</>
|
||||
}
|
||||
|
||||
/** 首页路由组件:已登录跳 dashboard,未登录显示落地页 */
|
||||
// eslint-disable-next-line react-refresh/only-export-components
|
||||
const HomeRoute: React.FC = () => {
|
||||
const isAuthenticated = useAuthStore((state) => state.isAuthenticated)
|
||||
const hasAccessToken = Boolean(localStorage.getItem("access_token"))
|
||||
|
||||
if (isAuthenticated && hasAccessToken) {
|
||||
return <Navigate to="/app/dashboard" replace />
|
||||
}
|
||||
|
||||
return <HomePage />
|
||||
}
|
||||
import { publicRoutes } from "./publicRoutes"
|
||||
import { appRoutes } from "./appRoutes"
|
||||
|
||||
/** 路由配置 */
|
||||
export const router = createBrowserRouter([
|
||||
{
|
||||
path: "/",
|
||||
element: <HomeRoute />,
|
||||
},
|
||||
{
|
||||
path: "/login",
|
||||
element: <Login />,
|
||||
},
|
||||
{
|
||||
path: "/register",
|
||||
element: <Register />,
|
||||
},
|
||||
{
|
||||
path: "/forgot-password",
|
||||
element: <ForgotPassword />,
|
||||
},
|
||||
{
|
||||
path: "/reset-password",
|
||||
element: <ResetPassword />,
|
||||
},
|
||||
{
|
||||
path: "/auth/wechat/callback",
|
||||
element: <WechatCallback />,
|
||||
},
|
||||
{
|
||||
path: "/app",
|
||||
element: (
|
||||
<ProtectedRoute>
|
||||
<MainLayout />
|
||||
</ProtectedRoute>
|
||||
),
|
||||
children: [
|
||||
{
|
||||
index: true,
|
||||
element: <Navigate to="/app/dashboard" replace />,
|
||||
},
|
||||
{
|
||||
path: "dashboard",
|
||||
lazy: () =>
|
||||
import("@/pages/dashboard/Dashboard").then((m) => ({
|
||||
Component: m.default,
|
||||
})),
|
||||
},
|
||||
{
|
||||
path: "assets",
|
||||
lazy: () =>
|
||||
import("@/pages/assets/AssetLibrary").then((m) => ({
|
||||
Component: m.default,
|
||||
})),
|
||||
},
|
||||
{
|
||||
path: "titles",
|
||||
lazy: () =>
|
||||
import("@/pages/titles/TitleLibrary").then((m) => ({
|
||||
Component: m.default,
|
||||
})),
|
||||
},
|
||||
{
|
||||
path: "voices",
|
||||
lazy: () =>
|
||||
import("@/pages/voices/VoiceLibrary").then((m) => ({
|
||||
Component: m.default,
|
||||
})),
|
||||
},
|
||||
{
|
||||
path: "templates",
|
||||
lazy: () =>
|
||||
import("@/pages/templates/TemplateLibrary").then((m) => ({
|
||||
Component: m.default,
|
||||
})),
|
||||
},
|
||||
{
|
||||
path: "generate",
|
||||
lazy: () =>
|
||||
import("@/pages/generate/GeneratePage").then((m) => ({
|
||||
Component: m.default,
|
||||
})),
|
||||
},
|
||||
{
|
||||
path: "history",
|
||||
lazy: () =>
|
||||
import("@/pages/history/TaskHistory").then((m) => ({
|
||||
Component: m.default,
|
||||
})),
|
||||
},
|
||||
{
|
||||
path: "products",
|
||||
lazy: () =>
|
||||
import("@/pages/products/ProductLibrary").then((m) => ({
|
||||
Component: m.default,
|
||||
})),
|
||||
},
|
||||
{
|
||||
path: "products/:id",
|
||||
lazy: () =>
|
||||
import("@/pages/products/ProductDetail").then((m) => ({
|
||||
Component: m.default,
|
||||
})),
|
||||
},
|
||||
{
|
||||
path: "tasks",
|
||||
lazy: () =>
|
||||
import("@/pages/tasks/TaskCenter").then((m) => ({
|
||||
Component: m.default,
|
||||
})),
|
||||
},
|
||||
{
|
||||
path: "editing-planner",
|
||||
lazy: () =>
|
||||
import("@/pages/editing-planner/EditingPlanner").then((m) => ({
|
||||
Component: m.default,
|
||||
})),
|
||||
},
|
||||
{
|
||||
path: "my-templates",
|
||||
lazy: () =>
|
||||
import("@/pages/my-templates/MyTemplates").then((m) => ({
|
||||
Component: m.default,
|
||||
})),
|
||||
},
|
||||
{
|
||||
path: "voice-clone",
|
||||
lazy: () =>
|
||||
import("@/pages/voice-clone/VoiceClone").then((m) => ({
|
||||
Component: m.default,
|
||||
})),
|
||||
},
|
||||
{
|
||||
path: "voice-materials",
|
||||
lazy: () =>
|
||||
import("@/pages/voice-materials/VoiceMaterialLibrary").then((m) => ({
|
||||
Component: m.default,
|
||||
})),
|
||||
},
|
||||
{
|
||||
path: "my-voices",
|
||||
lazy: () =>
|
||||
import("@/pages/my-voices/MyVoices").then((m) => ({
|
||||
Component: m.default,
|
||||
})),
|
||||
},
|
||||
{
|
||||
path: "accounts",
|
||||
lazy: () =>
|
||||
import("@/pages/accounts/Accounts").then((m) => ({
|
||||
Component: m.default,
|
||||
})),
|
||||
},
|
||||
{
|
||||
path: "duplication",
|
||||
lazy: () =>
|
||||
import("@/pages/duplication/DuplicationUpload").then((m) => ({
|
||||
Component: m.default,
|
||||
})),
|
||||
},
|
||||
{
|
||||
path: "duplication/results",
|
||||
lazy: () =>
|
||||
import("@/pages/duplication/DuplicationResults").then((m) => ({
|
||||
Component: m.default,
|
||||
})),
|
||||
},
|
||||
{
|
||||
path: "duplication/:id",
|
||||
lazy: () =>
|
||||
import("@/pages/duplication/DuplicationDetail").then((m) => ({
|
||||
Component: m.default,
|
||||
})),
|
||||
},
|
||||
{
|
||||
path: "subscription",
|
||||
lazy: () =>
|
||||
import("@/pages/subscription/Plans").then((m) => ({
|
||||
Component: m.default,
|
||||
})),
|
||||
},
|
||||
{
|
||||
path: "subscription/upgrade",
|
||||
lazy: () =>
|
||||
import("@/pages/subscription/UpgradeSubscription").then((m) => ({
|
||||
Component: m.default,
|
||||
})),
|
||||
},
|
||||
{
|
||||
path: "subscription/billing",
|
||||
lazy: () =>
|
||||
import("@/pages/subscription/Billing").then((m) => ({
|
||||
Component: m.default,
|
||||
})),
|
||||
},
|
||||
{
|
||||
path: "profile",
|
||||
lazy: () =>
|
||||
import("@/pages/profile/Settings").then((m) => ({
|
||||
Component: m.default,
|
||||
})),
|
||||
},
|
||||
{
|
||||
path: "admin",
|
||||
children: [
|
||||
{
|
||||
index: true,
|
||||
lazy: () =>
|
||||
import("@/pages/admin/AdminComingSoon").then((m) => ({
|
||||
Component: m.default,
|
||||
})),
|
||||
},
|
||||
{
|
||||
path: "users",
|
||||
lazy: () =>
|
||||
import("@/pages/admin/AdminComingSoon").then((m) => ({
|
||||
Component: m.default,
|
||||
})),
|
||||
},
|
||||
{
|
||||
path: "analytics",
|
||||
lazy: () =>
|
||||
import("@/pages/admin/AdminComingSoon").then((m) => ({
|
||||
Component: m.default,
|
||||
})),
|
||||
},
|
||||
{
|
||||
path: "monitor",
|
||||
lazy: () =>
|
||||
import("@/pages/admin/AdminComingSoon").then((m) => ({
|
||||
Component: m.default,
|
||||
})),
|
||||
},
|
||||
{
|
||||
path: "logs",
|
||||
lazy: () =>
|
||||
import("@/pages/admin/AdminComingSoon").then((m) => ({
|
||||
Component: m.default,
|
||||
})),
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
...publicRoutes,
|
||||
appRoutes,
|
||||
{
|
||||
path: "*",
|
||||
element: <Navigate to="/" replace />,
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
import { Navigate, type RouteObject } from "react-router-dom"
|
||||
import HomePage from "@/pages/home/HomePage"
|
||||
import Login from "@/pages/auth/Login"
|
||||
import Register from "@/pages/auth/Register"
|
||||
import ForgotPassword from "@/pages/auth/ForgotPassword"
|
||||
import ResetPassword from "@/pages/auth/ResetPassword"
|
||||
import WechatCallback from "@/pages/auth/WechatCallback"
|
||||
import { useAuthStore } from "@/store/authStore"
|
||||
|
||||
/** 首页路由组件:已登录跳 dashboard,未登录显示落地页 */
|
||||
// eslint-disable-next-line react-refresh/only-export-components
|
||||
const HomeRoute: React.FC = () => {
|
||||
const isAuthenticated = useAuthStore((state) => state.isAuthenticated)
|
||||
const hasAccessToken = Boolean(localStorage.getItem("access_token"))
|
||||
|
||||
if (isAuthenticated && hasAccessToken) {
|
||||
return <Navigate to="/app/dashboard" replace />
|
||||
}
|
||||
|
||||
return <HomePage />
|
||||
}
|
||||
|
||||
export const publicRoutes: RouteObject[] = [
|
||||
{
|
||||
path: "/",
|
||||
element: <HomeRoute />,
|
||||
},
|
||||
{
|
||||
path: "/login",
|
||||
element: <Login />,
|
||||
},
|
||||
{
|
||||
path: "/register",
|
||||
element: <Register />,
|
||||
},
|
||||
{
|
||||
path: "/forgot-password",
|
||||
element: <ForgotPassword />,
|
||||
},
|
||||
{
|
||||
path: "/reset-password",
|
||||
element: <ResetPassword />,
|
||||
},
|
||||
{
|
||||
path: "/auth/wechat/callback",
|
||||
element: <WechatCallback />,
|
||||
},
|
||||
]
|
||||
Reference in New Issue
Block a user