页面结构修改
This commit is contained in:
1 parent
543d78e040
commit
8ecc32da6d
14 files changed
+87
-29
No files matched your search
@@ -53,6 +53,10 @@ function useSidebar() {
|
|||||||
return context;
|
return context;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function useSidebarSafe() {
|
||||||
|
return React.useContext(SidebarContext);
|
||||||
|
}
|
||||||
|
|
||||||
function SidebarProvider({
|
function SidebarProvider({
|
||||||
defaultOpen = true,
|
defaultOpen = true,
|
||||||
open: openProp,
|
open: openProp,
|
||||||
@@ -229,7 +233,7 @@ function Sidebar({
|
|||||||
<div
|
<div
|
||||||
data-slot="sidebar-container"
|
data-slot="sidebar-container"
|
||||||
className={cn(
|
className={cn(
|
||||||
"fixed top-16 bottom-0 z-10 hidden w-(--sidebar-width) transition-[left,right,width] duration-200 ease-linear md:flex",
|
"fixed bottom-0 z-10 hidden w-(--sidebar-width) transition-[left,right,width] duration-200 ease-linear md:flex [top:var(--header-height,4rem)]",
|
||||||
side === "left"
|
side === "left"
|
||||||
? "left-0 group-data-[collapsible=offcanvas]:left-[calc(var(--sidebar-width)*-1)]"
|
? "left-0 group-data-[collapsible=offcanvas]:left-[calc(var(--sidebar-width)*-1)]"
|
||||||
: "right-0 group-data-[collapsible=offcanvas]:right-[calc(var(--sidebar-width)*-1)]",
|
: "right-0 group-data-[collapsible=offcanvas]:right-[calc(var(--sidebar-width)*-1)]",
|
||||||
@@ -723,4 +727,5 @@ export {
|
|||||||
SidebarSeparator,
|
SidebarSeparator,
|
||||||
SidebarTrigger,
|
SidebarTrigger,
|
||||||
useSidebar,
|
useSidebar,
|
||||||
|
useSidebarSafe,
|
||||||
};
|
};
|
||||||
@@ -6,7 +6,7 @@ import {
|
|||||||
type ReactNode,
|
type ReactNode,
|
||||||
} from "react";
|
} from "react";
|
||||||
|
|
||||||
import { useSidebar } from "@/components/ui/sidebar";
|
import { useSidebarSafe } from "@/components/ui/sidebar";
|
||||||
import { env } from "@/env";
|
import { env } from "@/env";
|
||||||
|
|
||||||
export interface ArtifactsContextType {
|
export interface ArtifactsContextType {
|
||||||
@@ -39,19 +39,19 @@ export function ArtifactsProvider({ children }: ArtifactsProviderProps) {
|
|||||||
env.NEXT_PUBLIC_STATIC_WEBSITE_ONLY === "true",
|
env.NEXT_PUBLIC_STATIC_WEBSITE_ONLY === "true",
|
||||||
);
|
);
|
||||||
const [autoOpen, setAutoOpen] = useState(true);
|
const [autoOpen, setAutoOpen] = useState(true);
|
||||||
const { setOpen: setSidebarOpen } = useSidebar();
|
const sidebar = useSidebarSafe();
|
||||||
|
|
||||||
const select = useCallback(
|
const select = useCallback(
|
||||||
(artifact: string, autoSelect = false) => {
|
(artifact: string, autoSelect = false) => {
|
||||||
setSelectedArtifact(artifact);
|
setSelectedArtifact(artifact);
|
||||||
if (env.NEXT_PUBLIC_STATIC_WEBSITE_ONLY !== "true") {
|
if (env.NEXT_PUBLIC_STATIC_WEBSITE_ONLY !== "true") {
|
||||||
setSidebarOpen(false);
|
sidebar?.setOpen(false);
|
||||||
}
|
}
|
||||||
if (!autoSelect) {
|
if (!autoSelect) {
|
||||||
setAutoSelect(false);
|
setAutoSelect(false);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[setSidebarOpen, setSelectedArtifact, setAutoSelect],
|
[sidebar, setSelectedArtifact, setAutoSelect],
|
||||||
);
|
);
|
||||||
|
|
||||||
const deselect = useCallback(() => {
|
const deselect = useCallback(() => {
|
||||||
|
|||||||
@@ -0,0 +1,24 @@
|
|||||||
|
import { createContext, useCallback, useContext } from "react";
|
||||||
|
import { useNavigate, type NavigateOptions } from "react-router-dom";
|
||||||
|
|
||||||
|
export const RouteBaseContext = createContext("/page/workspace");
|
||||||
|
export const RouteBaseProvider = RouteBaseContext.Provider;
|
||||||
|
|
||||||
|
export function useRouteBase() {
|
||||||
|
return useContext(RouteBaseContext);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useRouteNavigate() {
|
||||||
|
const navigate = useNavigate();
|
||||||
|
const base = useRouteBase();
|
||||||
|
return useCallback(
|
||||||
|
(path: string | number, opts?: NavigateOptions) => {
|
||||||
|
if (typeof path === "string") {
|
||||||
|
navigate(path.replace("/page/workspace", base), opts);
|
||||||
|
} else {
|
||||||
|
navigate(path);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[navigate, base],
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -1,5 +1,7 @@
|
|||||||
|
import { useContext } from "react";
|
||||||
import type { Message } from "@langchain/langgraph-sdk";
|
import type { Message } from "@langchain/langgraph-sdk";
|
||||||
|
|
||||||
|
import { RouteBaseContext } from "@/core/route-base";
|
||||||
import type { AgentThread, AgentThreadContext } from "./types";
|
import type { AgentThread, AgentThreadContext } from "./types";
|
||||||
|
|
||||||
type ThreadAgentRef = Pick<AgentThreadContext, "agent_id" | "agent_name">;
|
type ThreadAgentRef = Pick<AgentThreadContext, "agent_id" | "agent_name">;
|
||||||
@@ -28,6 +30,7 @@ function pickAgentRef(
|
|||||||
export function pathOfThread(
|
export function pathOfThread(
|
||||||
thread: ThreadRouteTarget,
|
thread: ThreadRouteTarget,
|
||||||
context?: ThreadAgentRef | null,
|
context?: ThreadAgentRef | null,
|
||||||
|
base = "/page/workspace",
|
||||||
) {
|
) {
|
||||||
const threadId = typeof thread === "string" ? thread : thread.thread_id;
|
const threadId = typeof thread === "string" ? thread : thread.thread_id;
|
||||||
const agentRef =
|
const agentRef =
|
||||||
@@ -36,8 +39,14 @@ export function pathOfThread(
|
|||||||
: pickAgentRef(thread.context, thread.metadata);
|
: pickAgentRef(thread.context, thread.metadata);
|
||||||
|
|
||||||
return agentRef
|
return agentRef
|
||||||
? `/page/workspace/agents/${encodeURIComponent(agentRef)}/chats/${threadId}`
|
? `${base}/agents/${encodeURIComponent(agentRef)}/chats/${threadId}`
|
||||||
: `/page/workspace/chats/${threadId}`;
|
: `${base}/chats/${threadId}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function usePathOfThread() {
|
||||||
|
const base = useContext(RouteBaseContext);
|
||||||
|
return (thread: ThreadRouteTarget, context?: ThreadAgentRef | null) =>
|
||||||
|
pathOfThread(thread, context, base);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function textOfMessage(message: Message) {
|
export function textOfMessage(message: Message) {
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
import { BotIcon, PlusSquare } from "lucide-react";
|
import { BotIcon, PlusSquare } from "lucide-react";
|
||||||
import { useParams, useRouter } from "next/navigation";
|
import { useParams, useRouter } from "next/navigation";
|
||||||
|
import { useRouteBase } from "@/core/route-base";
|
||||||
import { useCallback, useEffect, useState } from "react";
|
import { useCallback, useEffect, useState } from "react";
|
||||||
|
|
||||||
import type { PromptInputMessage } from "@/components/ai-elements/prompt-input";
|
import type { PromptInputMessage } from "@/components/ai-elements/prompt-input";
|
||||||
@@ -32,6 +33,7 @@ import { env } from "@/env";
|
|||||||
import { cn } from "@/lib/utils";
|
import { cn } from "@/lib/utils";
|
||||||
|
|
||||||
export default function AgentChatPage() {
|
export default function AgentChatPage() {
|
||||||
|
const routeBase = useRouteBase();
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
const [showFollowups, setShowFollowups] = useState(false);
|
const [showFollowups, setShowFollowups] = useState(false);
|
||||||
const [hasStartedConversation, setHasStartedConversation] = useState(false);
|
const [hasStartedConversation, setHasStartedConversation] = useState(false);
|
||||||
@@ -66,7 +68,7 @@ export default function AgentChatPage() {
|
|||||||
history.replaceState(
|
history.replaceState(
|
||||||
null,
|
null,
|
||||||
"",
|
"",
|
||||||
`${window.location.pathname}${window.location.search}#/page/workspace/agents/${agent_id}/chats/${createdThreadId}`,
|
`${window.location.pathname}${window.location.search}#${routeBase}/agents/${agent_id}/chats/${createdThreadId}`,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
onFinish: (state) => {
|
onFinish: (state) => {
|
||||||
|
|||||||
@@ -26,11 +26,13 @@ import { useModels } from "@/core/models/hooks";
|
|||||||
import { useNotification } from "@/core/notification/hooks";
|
import { useNotification } from "@/core/notification/hooks";
|
||||||
import { useThreadSettings } from "@/core/settings";
|
import { useThreadSettings } from "@/core/settings";
|
||||||
import { useThreadStream } from "@/core/threads/hooks";
|
import { useThreadStream } from "@/core/threads/hooks";
|
||||||
|
import { useRouteBase } from "@/core/route-base";
|
||||||
import { textOfMessage } from "@/core/threads/utils";
|
import { textOfMessage } from "@/core/threads/utils";
|
||||||
import { env } from "@/env";
|
import { env } from "@/env";
|
||||||
import { cn } from "@/lib/utils";
|
import { cn } from "@/lib/utils";
|
||||||
|
|
||||||
export default function ChatPage() {
|
export default function ChatPage() {
|
||||||
|
const routeBase = useRouteBase();
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
const [showFollowups, setShowFollowups] = useState(false);
|
const [showFollowups, setShowFollowups] = useState(false);
|
||||||
const [hasStartedConversation, setHasStartedConversation] = useState(false);
|
const [hasStartedConversation, setHasStartedConversation] = useState(false);
|
||||||
@@ -64,7 +66,7 @@ export default function ChatPage() {
|
|||||||
history.replaceState(
|
history.replaceState(
|
||||||
null,
|
null,
|
||||||
"",
|
"",
|
||||||
`${window.location.pathname}${window.location.search}#/page/workspace/chats/${createdThreadId}`,
|
`${window.location.pathname}${window.location.search}#${routeBase}/chats/${createdThreadId}`,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
onFinish: (state) => {
|
onFinish: (state) => {
|
||||||
|
|||||||
@@ -12,10 +12,11 @@ import {
|
|||||||
} from "@/components/workspace/workspace-container";
|
} from "@/components/workspace/workspace-container";
|
||||||
import { useI18n } from "@/core/i18n/hooks";
|
import { useI18n } from "@/core/i18n/hooks";
|
||||||
import { useThreads } from "@/core/threads/hooks";
|
import { useThreads } from "@/core/threads/hooks";
|
||||||
import { pathOfThread, titleOfThread } from "@/core/threads/utils";
|
import { usePathOfThread, titleOfThread } from "@/core/threads/utils";
|
||||||
import { formatTimeAgo } from "@/core/utils/datetime";
|
import { formatTimeAgo } from "@/core/utils/datetime";
|
||||||
|
|
||||||
export default function ChatsPage() {
|
export default function ChatsPage() {
|
||||||
|
const pathOfThread = usePathOfThread();
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
const { data: threads } = useThreads();
|
const { data: threads } = useThreads();
|
||||||
const [search, setSearch] = useState("");
|
const [search, setSearch] = useState("");
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import StrategyRoutes from "./StrategyRoutes";
|
|||||||
import Header from "@/strategy-components/Header";
|
import Header from "@/strategy-components/Header";
|
||||||
|
|
||||||
// 设为 true 时隐藏侧边栏(开发中,暂未完成)
|
// 设为 true 时隐藏侧边栏(开发中,暂未完成)
|
||||||
const SIDEBAR_ENABLED = true;
|
const SIDEBAR_ENABLED = false;
|
||||||
|
|
||||||
function PageLayout() {
|
function PageLayout() {
|
||||||
const [collapsed, setCollapsed] = useState(false);
|
const [collapsed, setCollapsed] = useState(false);
|
||||||
@@ -26,7 +26,7 @@ function PageLayout() {
|
|||||||
!SIDEBAR_ENABLED ? "" : collapsed ? "pl-16" : "pl-64"
|
!SIDEBAR_ENABLED ? "" : collapsed ? "pl-16" : "pl-64"
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
<Header />
|
{SIDEBAR_ENABLED && <Header />}
|
||||||
<main className="flex-1 overflow-hidden">
|
<main className="flex-1 overflow-hidden">
|
||||||
<Routes>
|
<Routes>
|
||||||
<Route index element={<Navigate to="workspace" replace />} />
|
<Route index element={<Navigate to="workspace" replace />} />
|
||||||
@@ -36,6 +36,7 @@ function PageLayout() {
|
|||||||
<WorkspaceRoutes
|
<WorkspaceRoutes
|
||||||
pageSidebarCollapsed={collapsed}
|
pageSidebarCollapsed={collapsed}
|
||||||
pageSidebarHidden={!SIDEBAR_ENABLED}
|
pageSidebarHidden={!SIDEBAR_ENABLED}
|
||||||
|
headerHidden={!SIDEBAR_ENABLED}
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -2,7 +2,8 @@
|
|||||||
|
|
||||||
import { useEffect, useMemo, useState } from "react";
|
import { useEffect, useMemo, useState } from "react";
|
||||||
import { ChevronDown, Download, FileText, ListChecks, Trash2 } from "lucide-react";
|
import { ChevronDown, Download, FileText, ListChecks, Trash2 } from "lucide-react";
|
||||||
import { useNavigate, useParams } from "react-router-dom";
|
import { useParams } from "react-router-dom";
|
||||||
|
import { useRouteNavigate as useNavigate } from "@/core/route-base";
|
||||||
import { toast } from "sonner";
|
import { toast } from "sonner";
|
||||||
|
|
||||||
import { Badge } from "@/components/ui/badge";
|
import { Badge } from "@/components/ui/badge";
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ import {
|
|||||||
RefreshCw,
|
RefreshCw,
|
||||||
Trash2,
|
Trash2,
|
||||||
} from "lucide-react";
|
} from "lucide-react";
|
||||||
import { useNavigate } from "react-router-dom";
|
import { useRouteNavigate as useNavigate } from "@/core/route-base";
|
||||||
import { toast } from "sonner";
|
import { toast } from "sonner";
|
||||||
|
|
||||||
import { Badge } from "@/components/ui/badge";
|
import { Badge } from "@/components/ui/badge";
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
import { Navigate, Route, Routes } from "react-router-dom";
|
import { Navigate, Route, Routes } from "react-router-dom";
|
||||||
import type { ReactNode } from "react";
|
import type { ReactNode } from "react";
|
||||||
|
import { Toaster } from "sonner";
|
||||||
|
|
||||||
import { getStoredAuth } from "@/core/auth";
|
import { getStoredAuth } from "@/core/auth";
|
||||||
|
import { RouteBaseProvider } from "@/core/route-base";
|
||||||
import { SubtasksProvider } from "@/core/tasks/context";
|
import { SubtasksProvider } from "@/core/tasks/context";
|
||||||
import { ArtifactsProvider } from "@/components/workspace/artifacts";
|
import { ArtifactsProvider } from "@/components/workspace/artifacts";
|
||||||
import { PromptInputProvider } from "@/components/ai-elements/prompt-input";
|
import { PromptInputProvider } from "@/components/ai-elements/prompt-input";
|
||||||
@@ -28,6 +30,7 @@ function ChatRuntime({ children }: { children: ReactNode }) {
|
|||||||
|
|
||||||
export default function StrategyRoutes() {
|
export default function StrategyRoutes() {
|
||||||
return (
|
return (
|
||||||
|
<RouteBaseProvider value="/page/strategy">
|
||||||
<div className="h-full w-full overflow-hidden">
|
<div className="h-full w-full overflow-hidden">
|
||||||
<Routes>
|
<Routes>
|
||||||
<Route index element={<Navigate to="qa/general" replace />} />
|
<Route index element={<Navigate to="qa/general" replace />} />
|
||||||
@@ -86,5 +89,7 @@ export default function StrategyRoutes() {
|
|||||||
/>
|
/>
|
||||||
</Routes>
|
</Routes>
|
||||||
</div>
|
</div>
|
||||||
|
<Toaster position="top-center" />
|
||||||
|
</RouteBaseProvider>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -11,6 +11,8 @@ import { Toaster } from "sonner";
|
|||||||
import { SubtasksProvider } from "@/core/tasks/context";
|
import { SubtasksProvider } from "@/core/tasks/context";
|
||||||
import { ArtifactsProvider } from "@/components/workspace/artifacts";
|
import { ArtifactsProvider } from "@/components/workspace/artifacts";
|
||||||
|
|
||||||
|
import { RouteBaseProvider } from "@/core/route-base";
|
||||||
|
|
||||||
import ChatsPage from "./ChatsPage";
|
import ChatsPage from "./ChatsPage";
|
||||||
import ChatPage from "./ChatPage";
|
import ChatPage from "./ChatPage";
|
||||||
import AgentsPage from "./AgentsPage";
|
import AgentsPage from "./AgentsPage";
|
||||||
@@ -24,10 +26,12 @@ export function WorkspaceLayout({
|
|||||||
children,
|
children,
|
||||||
pageSidebarCollapsed,
|
pageSidebarCollapsed,
|
||||||
pageSidebarHidden = false,
|
pageSidebarHidden = false,
|
||||||
|
headerHidden = false,
|
||||||
}: {
|
}: {
|
||||||
children: ReactNode;
|
children: ReactNode;
|
||||||
pageSidebarCollapsed: boolean;
|
pageSidebarCollapsed: boolean;
|
||||||
pageSidebarHidden?: boolean;
|
pageSidebarHidden?: boolean;
|
||||||
|
headerHidden?: boolean;
|
||||||
}) {
|
}) {
|
||||||
const pageSidebarWidth = pageSidebarHidden
|
const pageSidebarWidth = pageSidebarHidden
|
||||||
? "0rem"
|
? "0rem"
|
||||||
@@ -41,6 +45,7 @@ export function WorkspaceLayout({
|
|||||||
style={
|
style={
|
||||||
{
|
{
|
||||||
"--page-sidebar-width": pageSidebarWidth,
|
"--page-sidebar-width": pageSidebarWidth,
|
||||||
|
"--header-height": headerHidden ? "0rem" : "4rem",
|
||||||
} as CSSProperties
|
} as CSSProperties
|
||||||
}
|
}
|
||||||
// Keep the fixed workspace sidebar aligned to the outer page sidebar width.
|
// Keep the fixed workspace sidebar aligned to the outer page sidebar width.
|
||||||
@@ -69,13 +74,16 @@ function ChatRuntime({ children }: { children: ReactNode }) {
|
|||||||
export default function WorkspaceRoutes({
|
export default function WorkspaceRoutes({
|
||||||
pageSidebarCollapsed = false,
|
pageSidebarCollapsed = false,
|
||||||
pageSidebarHidden = false,
|
pageSidebarHidden = false,
|
||||||
|
headerHidden = false,
|
||||||
}: {
|
}: {
|
||||||
pageSidebarCollapsed?: boolean;
|
pageSidebarCollapsed?: boolean;
|
||||||
pageSidebarHidden?: boolean;
|
pageSidebarHidden?: boolean;
|
||||||
|
headerHidden?: boolean;
|
||||||
}) {
|
}) {
|
||||||
const layoutProps = { pageSidebarCollapsed, pageSidebarHidden };
|
const layoutProps = { pageSidebarCollapsed, pageSidebarHidden, headerHidden };
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
<RouteBaseProvider value="/page/workspace">
|
||||||
<Routes>
|
<Routes>
|
||||||
<Route index element={<Navigate to="chats/new" replace />} />
|
<Route index element={<Navigate to="chats/new" replace />} />
|
||||||
<Route
|
<Route
|
||||||
@@ -159,5 +167,6 @@ export default function WorkspaceRoutes({
|
|||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
</Routes>
|
</Routes>
|
||||||
|
</RouteBaseProvider>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import { useMemo } from "react";
|
import { useContext, useMemo } from "react";
|
||||||
import {
|
import {
|
||||||
useLocation,
|
useLocation,
|
||||||
useNavigate,
|
useNavigate,
|
||||||
@@ -6,19 +6,24 @@ import {
|
|||||||
useSearchParams as useRouterSearchParams,
|
useSearchParams as useRouterSearchParams,
|
||||||
} from "react-router-dom";
|
} from "react-router-dom";
|
||||||
|
|
||||||
|
import { RouteBaseContext } from "@/core/route-base";
|
||||||
|
|
||||||
export function useRouter() {
|
export function useRouter() {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
const base = useContext(RouteBaseContext);
|
||||||
|
|
||||||
return useMemo(
|
return useMemo(
|
||||||
() => ({
|
() => ({
|
||||||
push: (href: string) => navigate(href),
|
push: (href: string) =>
|
||||||
replace: (href: string) => navigate(href, { replace: true }),
|
navigate(href.replace("/page/workspace", base)),
|
||||||
|
replace: (href: string) =>
|
||||||
|
navigate(href.replace("/page/workspace", base), { replace: true }),
|
||||||
back: () => navigate(-1),
|
back: () => navigate(-1),
|
||||||
forward: () => navigate(1),
|
forward: () => navigate(1),
|
||||||
refresh: () => window.location.reload(),
|
refresh: () => window.location.reload(),
|
||||||
prefetch: async () => {},
|
prefetch: async () => {},
|
||||||
}),
|
}),
|
||||||
[navigate],
|
[navigate, base],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Bell, Search, User, ChevronDown, Moon, Sun, Palette } from 'lucide-react';
|
import { Bell, Search, User, ChevronDown, Moon, Palette } from 'lucide-react';
|
||||||
import { useTheme } from '../contexts/ThemeContext';
|
import { useTheme } from '../contexts/ThemeContext';
|
||||||
|
|
||||||
const Header: React.FC = () => {
|
const Header: React.FC = () => {
|
||||||
@@ -40,11 +40,7 @@ const Header: React.FC = () => {
|
|||||||
}`}
|
}`}
|
||||||
title={isDark ? '切换到红白主题' : '切换到深色主题'}
|
title={isDark ? '切换到红白主题' : '切换到深色主题'}
|
||||||
>
|
>
|
||||||
{isDark ? (
|
{isDark ? <Palette className="w-5 h-5" /> : <Moon className="w-5 h-5" />}
|
||||||
<Palette className="w-5 h-5" />
|
|
||||||
) : (
|
|
||||||
<Moon className="w-5 h-5" />
|
|
||||||
)}
|
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
{/* 通知 */}
|
{/* 通知 */}
|
||||||
@@ -63,14 +59,12 @@ const Header: React.FC = () => {
|
|||||||
isDark ? 'hover:bg-slate-800/50' : 'hover:bg-red-500'
|
isDark ? 'hover:bg-slate-800/50' : 'hover:bg-red-500'
|
||||||
}`}>
|
}`}>
|
||||||
<div className={`w-8 h-8 rounded-full flex items-center justify-center ${
|
<div className={`w-8 h-8 rounded-full flex items-center justify-center ${
|
||||||
isDark
|
isDark ? 'bg-gradient-to-br from-blue-500 to-cyan-400' : 'bg-white'
|
||||||
? 'bg-gradient-to-br from-blue-500 to-cyan-400'
|
|
||||||
: 'bg-white'
|
|
||||||
}`}>
|
}`}>
|
||||||
<User className={`w-4 h-4 ${isDark ? 'text-white' : 'text-red-600'}`} />
|
<User className={`w-4 h-4 ${isDark ? 'text-white' : 'text-red-600'}`} />
|
||||||
</div>
|
</div>
|
||||||
<div className="text-left">
|
<div className="text-left">
|
||||||
<p className={`text-sm font-medium ${isDark ? 'text-white' : 'text-white'}`}>张策略</p>
|
<p className="text-sm font-medium text-white">张策略</p>
|
||||||
<p className={`text-xs ${isDark ? 'text-blue-400/60' : 'text-red-200'}`}>策略分析师</p>
|
<p className={`text-xs ${isDark ? 'text-blue-400/60' : 'text-red-200'}`}>策略分析师</p>
|
||||||
</div>
|
</div>
|
||||||
<ChevronDown className={`w-4 h-4 ${isDark ? 'text-slate-500' : 'text-white'}`} />
|
<ChevronDown className={`w-4 h-4 ${isDark ? 'text-slate-500' : 'text-white'}`} />
|
||||||
|
|||||||
Reference in new issue
Block a user