fix: P2+P3审计修复 - 响应式增强与导航配置统一 #139
@@ -25,6 +25,7 @@ import { useLocation, useNavigate } from "react-router-dom";
|
||||
import { useAuthStore } from "@/store/authStore";
|
||||
import { useLogout } from "@/hooks/useAuth";
|
||||
import type { MenuProps } from "antd";
|
||||
import { NAV_ITEMS } from "@/config/navigation";
|
||||
import "./Header.css";
|
||||
|
||||
/** 导航项定义 */
|
||||
@@ -35,77 +36,6 @@ interface NavItem {
|
||||
icon: React.ReactNode;
|
||||
}
|
||||
|
||||
/** 固定导航菜单 */
|
||||
const NAV_ITEMS: NavItem[] = [
|
||||
{
|
||||
key: "dashboard",
|
||||
label: "概览",
|
||||
path: "/app/dashboard",
|
||||
icon: <DashboardOutlined />,
|
||||
},
|
||||
{ key: "assets", label: "素材库", path: "/app/assets", icon: <FileOutlined /> },
|
||||
{
|
||||
key: "titles",
|
||||
label: "标题库",
|
||||
path: "/app/titles",
|
||||
icon: <FileTextOutlined />,
|
||||
},
|
||||
{ key: "voices", label: "配音库", path: "/app/voices", icon: <AudioOutlined /> },
|
||||
{
|
||||
key: "voice-clone",
|
||||
label: "我的音色",
|
||||
path: "/app/voice-clone",
|
||||
icon: <AudioOutlined />,
|
||||
},
|
||||
{
|
||||
key: "accounts",
|
||||
label: "账号管理",
|
||||
path: "/app/accounts",
|
||||
icon: <AppstoreOutlined />,
|
||||
},
|
||||
{
|
||||
key: "templates",
|
||||
label: "模板库",
|
||||
path: "/app/templates",
|
||||
icon: <AppstoreOutlined />,
|
||||
},
|
||||
{
|
||||
key: "editing-planner",
|
||||
label: "剪辑编辑器",
|
||||
path: "/app/editing-planner",
|
||||
icon: <EditOutlined />,
|
||||
},
|
||||
{
|
||||
key: "my-templates",
|
||||
label: "我的模板",
|
||||
path: "/app/my-templates",
|
||||
icon: <FolderOutlined />,
|
||||
},
|
||||
{
|
||||
key: "generate",
|
||||
label: "一键生成",
|
||||
path: "/app/generate",
|
||||
icon: <VideoCameraOutlined />,
|
||||
},
|
||||
{
|
||||
key: "history",
|
||||
label: "任务历史",
|
||||
path: "/app/history",
|
||||
icon: <HistoryOutlined />,
|
||||
},
|
||||
{
|
||||
key: "products",
|
||||
label: "成品库",
|
||||
path: "/app/products",
|
||||
icon: <TrophyOutlined />,
|
||||
},
|
||||
{
|
||||
key: "duplication",
|
||||
label: "查重",
|
||||
path: "/app/duplication",
|
||||
icon: <ScanOutlined />,
|
||||
},
|
||||
];
|
||||
|
||||
const Header: React.FC = () => {
|
||||
const navigate = useNavigate();
|
||||
@@ -120,13 +50,13 @@ const Header: React.FC = () => {
|
||||
key: "profile",
|
||||
icon: <UserOutlined />,
|
||||
label: "个人设置",
|
||||
onClick: () => navigate("/app/profile"),
|
||||
onClick: () => navigate("/profile"),
|
||||
},
|
||||
{
|
||||
key: "subscription",
|
||||
icon: <SettingOutlined />,
|
||||
label: "订阅管理",
|
||||
onClick: () => navigate("/app/subscription"),
|
||||
onClick: () => navigate("/subscription"),
|
||||
},
|
||||
{ type: "divider" },
|
||||
{
|
||||
@@ -139,8 +69,8 @@ const Header: React.FC = () => {
|
||||
|
||||
/** 判断导航项是否激活 */
|
||||
const isActive = (path: string) => {
|
||||
if (path === "/app/dashboard") {
|
||||
return location.pathname === "/app" || location.pathname === "/app/dashboard";
|
||||
if (path === "/dashboard") {
|
||||
return location.pathname === "/" || location.pathname === "/dashboard";
|
||||
}
|
||||
return location.pathname.startsWith(path);
|
||||
};
|
||||
@@ -151,7 +81,7 @@ const Header: React.FC = () => {
|
||||
<button
|
||||
className="xx-brand"
|
||||
type="button"
|
||||
onClick={() => navigate("/app/dashboard")}
|
||||
onClick={() => navigate("/dashboard")}
|
||||
>
|
||||
<span className="xx-logo">🦐</span>
|
||||
<span className="xx-brand-text">小虾自动剪辑</span>
|
||||
|
||||
@@ -24,6 +24,7 @@ import {
|
||||
CrownOutlined,
|
||||
} from "@ant-design/icons";
|
||||
import { SidebarContext } from "./MainLayout";
|
||||
import { NAV_GROUPS } from "@/config/navigation";
|
||||
import "./Sidebar.css";
|
||||
|
||||
/** 导航项定义 */
|
||||
@@ -40,101 +41,11 @@ interface SidebarMenuGroup {
|
||||
items: SidebarMenuItem[];
|
||||
}
|
||||
|
||||
/** 侧边栏导航分组 */
|
||||
const MENU_GROUPS: SidebarMenuGroup[] = [
|
||||
{
|
||||
title: "创作工具",
|
||||
items: [
|
||||
{
|
||||
key: "dashboard",
|
||||
label: "首页",
|
||||
path: "/app/dashboard",
|
||||
icon: <DashboardOutlined />,
|
||||
},
|
||||
{
|
||||
key: "generate",
|
||||
label: "一键生成",
|
||||
path: "/app/generate",
|
||||
icon: <VideoCameraOutlined />,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: "资源管理",
|
||||
items: [
|
||||
{
|
||||
key: "assets",
|
||||
label: "素材库",
|
||||
path: "/app/assets",
|
||||
icon: <FileOutlined />,
|
||||
},
|
||||
{
|
||||
key: "voices",
|
||||
label: "配音库",
|
||||
path: "/app/voices",
|
||||
icon: <AudioOutlined />,
|
||||
},
|
||||
{
|
||||
key: "voice-clone",
|
||||
label: "我的音色",
|
||||
path: "/app/voice-clone",
|
||||
icon: <AudioOutlined />,
|
||||
},
|
||||
{
|
||||
key: "accounts",
|
||||
label: "账号管理",
|
||||
path: "/app/accounts",
|
||||
icon: <AppstoreOutlined />,
|
||||
},
|
||||
{
|
||||
key: "titles",
|
||||
label: "标题库",
|
||||
path: "/app/titles",
|
||||
icon: <FileTextOutlined />,
|
||||
},
|
||||
{
|
||||
key: "products",
|
||||
label: "成片库",
|
||||
path: "/app/products",
|
||||
icon: <TrophyOutlined />,
|
||||
},
|
||||
{
|
||||
key: "templates",
|
||||
label: "模板库",
|
||||
path: "/app/templates",
|
||||
icon: <AppstoreOutlined />,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: "系统",
|
||||
items: [
|
||||
{
|
||||
key: "history",
|
||||
label: "任务历史",
|
||||
path: "/app/history",
|
||||
icon: <HistoryOutlined />,
|
||||
},
|
||||
{
|
||||
key: "admin",
|
||||
label: "控制台",
|
||||
path: "/app/admin",
|
||||
icon: <ControlOutlined />,
|
||||
},
|
||||
{
|
||||
key: "subscription",
|
||||
label: "订阅管理",
|
||||
path: "/app/subscription",
|
||||
icon: <CrownOutlined />,
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
/** 判断菜单项是否激活 */
|
||||
const isMenuItemActive = (pathname: string, path: string): boolean => {
|
||||
if (path === "/app/dashboard") {
|
||||
return pathname === "/app" || pathname === "/app/dashboard";
|
||||
if (path === "/dashboard") {
|
||||
return pathname === "/" || pathname === "/dashboard";
|
||||
}
|
||||
return pathname.startsWith(path);
|
||||
};
|
||||
@@ -150,7 +61,7 @@ const Sidebar: React.FC = () => {
|
||||
|
||||
return (
|
||||
<div className={`xx-sidebar-nav${collapsed ? " xx-sidebar-nav--collapsed" : ""}`}>
|
||||
{MENU_GROUPS.map((group) => (
|
||||
{NAV_GROUPS.map((group) => (
|
||||
<div className="xx-sidebar-group" key={group.title}>
|
||||
{!collapsed && (
|
||||
<div className="xx-sidebar-group-title">{group.title}</div>
|
||||
|
||||
@@ -0,0 +1,189 @@
|
||||
/**
|
||||
* 统一导航配置
|
||||
* Header 和 Sidebar 共用此数据源
|
||||
*/
|
||||
import React from "react";
|
||||
import {
|
||||
DashboardOutlined,
|
||||
VideoCameraOutlined,
|
||||
FileOutlined,
|
||||
AudioOutlined,
|
||||
FileTextOutlined,
|
||||
TrophyOutlined,
|
||||
AppstoreOutlined,
|
||||
HistoryOutlined,
|
||||
ControlOutlined,
|
||||
CrownOutlined,
|
||||
ScanOutlined,
|
||||
EditOutlined,
|
||||
FolderOutlined,
|
||||
} from "@ant-design/icons";
|
||||
|
||||
/** 导航项定义 */
|
||||
export interface NavItem {
|
||||
key: string;
|
||||
label: string;
|
||||
path: string;
|
||||
icon: React.ReactNode;
|
||||
}
|
||||
|
||||
/** 导航分组定义 */
|
||||
export interface NavGroup {
|
||||
title: string;
|
||||
items: NavItem[];
|
||||
}
|
||||
|
||||
/**
|
||||
* 扁平导航列表(Header 使用)
|
||||
*/
|
||||
export const NAV_ITEMS: NavItem[] = [
|
||||
{
|
||||
key: "dashboard",
|
||||
label: "概览",
|
||||
path: "/dashboard",
|
||||
icon: <DashboardOutlined />,
|
||||
},
|
||||
{ key: "assets", label: "素材库", path: "/assets", icon: <FileOutlined /> },
|
||||
{
|
||||
key: "titles",
|
||||
label: "标题库",
|
||||
path: "/titles",
|
||||
icon: <FileTextOutlined />,
|
||||
},
|
||||
{ key: "voices", label: "配音库", path: "/voices", icon: <AudioOutlined /> },
|
||||
{
|
||||
key: "voice-clone",
|
||||
label: "我的音色",
|
||||
path: "/voice-clone",
|
||||
icon: <AudioOutlined />,
|
||||
},
|
||||
{
|
||||
key: "templates",
|
||||
label: "模板库",
|
||||
path: "/templates",
|
||||
icon: <AppstoreOutlined />,
|
||||
},
|
||||
{
|
||||
key: "editing-planner",
|
||||
label: "剪辑编辑器",
|
||||
path: "/editing-planner",
|
||||
icon: <EditOutlined />,
|
||||
},
|
||||
{
|
||||
key: "my-templates",
|
||||
label: "我的模板",
|
||||
path: "/my-templates",
|
||||
icon: <FolderOutlined />,
|
||||
},
|
||||
{
|
||||
key: "generate",
|
||||
label: "一键生成",
|
||||
path: "/generate",
|
||||
icon: <VideoCameraOutlined />,
|
||||
},
|
||||
{
|
||||
key: "history",
|
||||
label: "任务历史",
|
||||
path: "/history",
|
||||
icon: <HistoryOutlined />,
|
||||
},
|
||||
{
|
||||
key: "products",
|
||||
label: "成品库",
|
||||
path: "/products",
|
||||
icon: <TrophyOutlined />,
|
||||
},
|
||||
{
|
||||
key: "duplication",
|
||||
label: "查重",
|
||||
path: "/duplication",
|
||||
icon: <ScanOutlined />,
|
||||
},
|
||||
];
|
||||
|
||||
/**
|
||||
* 分组导航列表(Sidebar 使用)
|
||||
*/
|
||||
export const NAV_GROUPS: NavGroup[] = [
|
||||
{
|
||||
title: "创作工具",
|
||||
items: [
|
||||
{
|
||||
key: "dashboard",
|
||||
label: "首页",
|
||||
path: "/dashboard",
|
||||
icon: <DashboardOutlined />,
|
||||
},
|
||||
{
|
||||
key: "generate",
|
||||
label: "一键生成",
|
||||
path: "/generate",
|
||||
icon: <VideoCameraOutlined />,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: "资源管理",
|
||||
items: [
|
||||
{
|
||||
key: "assets",
|
||||
label: "素材库",
|
||||
path: "/assets",
|
||||
icon: <FileOutlined />,
|
||||
},
|
||||
{
|
||||
key: "voices",
|
||||
label: "配音库",
|
||||
path: "/voices",
|
||||
icon: <AudioOutlined />,
|
||||
},
|
||||
{
|
||||
key: "voice-clone",
|
||||
label: "我的音色",
|
||||
path: "/voice-clone",
|
||||
icon: <AudioOutlined />,
|
||||
},
|
||||
{
|
||||
key: "titles",
|
||||
label: "标题库",
|
||||
path: "/titles",
|
||||
icon: <FileTextOutlined />,
|
||||
},
|
||||
{
|
||||
key: "products",
|
||||
label: "成片库",
|
||||
path: "/products",
|
||||
icon: <TrophyOutlined />,
|
||||
},
|
||||
{
|
||||
key: "templates",
|
||||
label: "模板库",
|
||||
path: "/templates",
|
||||
icon: <AppstoreOutlined />,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: "系统",
|
||||
items: [
|
||||
{
|
||||
key: "history",
|
||||
label: "任务历史",
|
||||
path: "/history",
|
||||
icon: <HistoryOutlined />,
|
||||
},
|
||||
{
|
||||
key: "admin",
|
||||
label: "控制台",
|
||||
path: "/admin",
|
||||
icon: <ControlOutlined />,
|
||||
},
|
||||
{
|
||||
key: "subscription",
|
||||
label: "订阅管理",
|
||||
path: "/subscription",
|
||||
icon: <CrownOutlined />,
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
@@ -651,3 +651,40 @@
|
||||
justify-content: center;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.ep-left,
|
||||
.ep-right {
|
||||
max-height: 180px;
|
||||
}
|
||||
|
||||
.ep-preview-frame {
|
||||
width: 120px;
|
||||
height: 213px;
|
||||
}
|
||||
|
||||
.ep-timeline {
|
||||
padding: var(--space-sm) var(--space-md);
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 480px) {
|
||||
.ep-toolbar {
|
||||
padding: var(--space-sm) var(--space-md);
|
||||
}
|
||||
|
||||
.ep-mode-btn {
|
||||
padding: 4px 12px;
|
||||
font-size: var(--font-size-xs);
|
||||
}
|
||||
|
||||
.ep-preview-frame {
|
||||
width: 100px;
|
||||
height: 178px;
|
||||
}
|
||||
|
||||
.ep-segment-card {
|
||||
padding: 8px 10px;
|
||||
gap: var(--space-sm);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -934,7 +934,7 @@ const GeneratePage: React.FC = () => {
|
||||
<button className="xx-video-play-btn" type="button">
|
||||
<PlayCircleOutlined />
|
||||
</button>
|
||||
<Text style={{ color: "rgba(255,255,255,0.6)", fontSize: "var(--font-size-sm)" }}>
|
||||
<Text style={{ color: "var(--text-inverse, rgba(255,255,255,0.6))", fontSize: "var(--font-size-sm)" }}>
|
||||
预览视频
|
||||
</Text>
|
||||
</div>
|
||||
|
||||
@@ -207,7 +207,7 @@
|
||||
.xx-history-page-btn.active {
|
||||
background: var(--gradient-primary);
|
||||
border-color: transparent;
|
||||
color: #fff;
|
||||
color: var(--text-inverse);
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
@@ -256,6 +256,21 @@
|
||||
/* ============================================================
|
||||
响应式
|
||||
============================================================ */
|
||||
@media (max-width: 1024px) {
|
||||
.xx-history-page {
|
||||
padding: var(--space-lg);
|
||||
}
|
||||
|
||||
.xx-history-task-item {
|
||||
grid-template-columns: 1fr auto auto;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.xx-history-task-time {
|
||||
min-width: 80px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.xx-history-page {
|
||||
padding: var(--space-md);
|
||||
@@ -274,3 +289,47 @@
|
||||
text-align: left;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 480px) {
|
||||
.xx-history-page {
|
||||
padding: var(--space-sm);
|
||||
}
|
||||
|
||||
.xx-history-header h2 {
|
||||
font-size: var(--font-size-lg);
|
||||
}
|
||||
|
||||
.xx-history-tabs {
|
||||
gap: 4px;
|
||||
padding-bottom: 8px;
|
||||
overflow-x: auto;
|
||||
flex-wrap: nowrap;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
}
|
||||
|
||||
.xx-history-tab {
|
||||
padding: 6px 14px;
|
||||
font-size: var(--font-size-xs);
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.xx-history-task-item {
|
||||
padding: 12px;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.xx-history-task-info h4 {
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.xx-history-status {
|
||||
font-size: 11px;
|
||||
padding: 2px 10px;
|
||||
}
|
||||
|
||||
.xx-history-page-btn {
|
||||
min-width: 32px;
|
||||
height: 32px;
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user