Merge branch 'dhk-1' into 'master'
Dhk 1 See merge request knowledge-base-bj/deerflow-code!4
This commit is contained in:
commit
2c331aa744
16 files changed
+242
-38
No files matched your search
@@ -16,7 +16,7 @@
|
|||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
||||||
<title>CM 助手</title>
|
<title>CM 助手</title>
|
||||||
<script src="/runtime-config.js"></script>
|
<script src="/runtime-config.js" data-runtime-config></script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="root"></div>
|
<div id="root"></div>
|
||||||
|
|||||||
Binary file not shown.
|
After Width: | Height: | Size: 27 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 27 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 27 KiB |
@@ -7,6 +7,25 @@
|
|||||||
// 4. 这里的默认值来自 .env.network;如某个环境不用某项,可以留空字符串。
|
// 4. 这里的默认值来自 .env.network;如某个环境不用某项,可以留空字符串。
|
||||||
// 5. 布尔开关也用字符串形式填写:"true" / "false"。
|
// 5. 布尔开关也用字符串形式填写:"true" / "false"。
|
||||||
window.__CM_RUNTIME_CONFIG__ = {
|
window.__CM_RUNTIME_CONFIG__ = {
|
||||||
|
// 品牌图片相对路径会以 runtime-config.js 所在目录为基准解析。
|
||||||
|
// Nginx 以子路径部署时不需要修改前端图片路径。
|
||||||
|
// 打包后可直接替换 dist/brand-assets/ 内的同名 PNG。
|
||||||
|
// 也可以把下方图片地址改为其他 PNG 地址,无需重新打包。
|
||||||
|
// 是否显示整块欢迎页图标区域:"true" 显示,"false" 隐藏,默认隐藏。
|
||||||
|
VITE_WELCOME_LOGO_VISIBLE: "false",
|
||||||
|
// 红白主题的顶栏系统 Logo 图片地址。
|
||||||
|
VITE_BRAND_LOGO_LIGHT_URL: "brand-assets/brand-logo-light.png",
|
||||||
|
// 浅蓝主题的顶栏系统 Logo 图片地址。
|
||||||
|
VITE_BRAND_LOGO_LIGHTBLUE_URL: "brand-assets/brand-logo-lightblue.png",
|
||||||
|
// 深色主题的顶栏系统 Logo 图片地址。
|
||||||
|
VITE_BRAND_LOGO_DARK_URL: "brand-assets/brand-logo-dark.png",
|
||||||
|
// 红白主题的欢迎页 Logo 图片地址:为空时显示原有 CM 文字和渐变背景。
|
||||||
|
VITE_BRAND_WELCOME_LIGHT_URL: "",
|
||||||
|
// 浅蓝主题的欢迎页 Logo 图片地址:为空时显示原有 CM 文字和渐变背景。
|
||||||
|
VITE_BRAND_WELCOME_LIGHTBLUE_URL: "",
|
||||||
|
// 深色主题的欢迎页 Logo 图片地址:为空时显示原有 CM 文字和渐变背景。
|
||||||
|
VITE_BRAND_WELCOME_DARK_URL: "",
|
||||||
|
|
||||||
// DeerFlow Gateway 地址:前端自有后端接口,例如 /api/agents、/api/auth、/api/threads 等。
|
// DeerFlow Gateway 地址:前端自有后端接口,例如 /api/agents、/api/auth、/api/threads 等。
|
||||||
VITE_BACKEND_BASE_URL: "http://10.19.200.5:2026",
|
VITE_BACKEND_BASE_URL: "http://10.19.200.5:2026",
|
||||||
|
|
||||||
|
|||||||
Binary file not shown.
|
After Width: | Height: | Size: 27 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 27 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 27 KiB |
@@ -1,5 +1,6 @@
|
|||||||
import { useEffect, useRef, useState } from "react";
|
import { useEffect, useRef, useState } from "react";
|
||||||
import { useNavigate, useLocation } from "react-router-dom";
|
import { useNavigate, useLocation } from "react-router-dom";
|
||||||
|
import { PanelLeftOpen, PanelLeftClose } from "lucide-react";
|
||||||
|
|
||||||
import { getStoredAuth } from "@/core/auth";
|
import { getStoredAuth } from "@/core/auth";
|
||||||
import { isWujieEnv } from "@/core/embed/wujie";
|
import { isWujieEnv } from "@/core/embed/wujie";
|
||||||
@@ -12,7 +13,13 @@ import {
|
|||||||
import { useSidebarMenuItems } from "@/core/page-layout/use-sidebar-menu";
|
import { useSidebarMenuItems } from "@/core/page-layout/use-sidebar-menu";
|
||||||
import './page-sdebar.css'
|
import './page-sdebar.css'
|
||||||
|
|
||||||
export function PageSidebarDark() {
|
type PageSidebarDarkProps = {
|
||||||
|
/** 是否收起(true 时只渲染左下角浮动入口,sidebar 主体隐藏) */
|
||||||
|
collapsed: boolean;
|
||||||
|
onToggleCollapsed: () => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
export function PageSidebarDark({ collapsed, onToggleCollapsed }: PageSidebarDarkProps) {
|
||||||
const isAdmin = getStoredAuth()?.system_role === "admin";
|
const isAdmin = getStoredAuth()?.system_role === "admin";
|
||||||
const visibleMenuItems = useSidebarMenuItems(!!isAdmin);
|
const visibleMenuItems = useSidebarMenuItems(!!isAdmin);
|
||||||
const inWujieEnv = isWujieEnv();
|
const inWujieEnv = isWujieEnv();
|
||||||
@@ -189,6 +196,22 @@ export function PageSidebarDark() {
|
|||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// 收起时主侧栏不占宽度,保留左下角入口以便随时恢复。
|
||||||
|
if (collapsed) {
|
||||||
|
return (
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
title="展开侧边栏"
|
||||||
|
aria-label="展开侧边栏"
|
||||||
|
className="sidebar-collapse-btn sidebar-collapse-btn--floating"
|
||||||
|
onClick={onToggleCollapsed}
|
||||||
|
>
|
||||||
|
<PanelLeftOpen className="sidebar-collapse-btn__icon" aria-hidden="true" />
|
||||||
|
<span className="sidebar-collapse-btn__label">展开面板</span>
|
||||||
|
</button>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<aside className={`sidebar-contnet ${inWujieEnv ? "" : "sidebar-contnet--top-submenu"}`}>
|
<aside className={`sidebar-contnet ${inWujieEnv ? "" : "sidebar-contnet--top-submenu"}`}>
|
||||||
<div className="sidebar-contents">
|
<div className="sidebar-contents">
|
||||||
@@ -220,6 +243,17 @@ export function PageSidebarDark() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
|
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
title="收起侧边栏"
|
||||||
|
aria-label="收起侧边栏"
|
||||||
|
className="sidebar-collapse-btn sidebar-collapse-btn--inline"
|
||||||
|
onClick={onToggleCollapsed}
|
||||||
|
>
|
||||||
|
<PanelLeftClose className="sidebar-collapse-btn__icon" aria-hidden="true" />
|
||||||
|
<span className="sidebar-collapse-btn__label">收起面板</span>
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</aside>
|
</aside>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,5 +1,10 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
|
import { useState } from "react";
|
||||||
|
|
||||||
|
import { useTheme } from "@/contexts/ThemeContext";
|
||||||
|
import { getWelcomeBrandAsset } from "@/core/branding/brand-assets";
|
||||||
|
import { readRuntimeFlag } from "@/env";
|
||||||
import { cn } from "@/lib/utils";
|
import { cn } from "@/lib/utils";
|
||||||
import { useDisplayDesensitizer } from "@/core/sensitive-words/use-display-desensitizer";
|
import { useDisplayDesensitizer } from "@/core/sensitive-words/use-display-desensitizer";
|
||||||
|
|
||||||
@@ -10,6 +15,13 @@ import { useDisplayDesensitizer } from "@/core/sensitive-words/use-display-desen
|
|||||||
*/
|
*/
|
||||||
export function CompactWelcome({ className }: { className?: string }) {
|
export function CompactWelcome({ className }: { className?: string }) {
|
||||||
const desensitizeText = useDisplayDesensitizer();
|
const desensitizeText = useDisplayDesensitizer();
|
||||||
|
const { theme } = useTheme();
|
||||||
|
const brandTheme = theme;
|
||||||
|
const welcomeLogo = getWelcomeBrandAsset(brandTheme);
|
||||||
|
const [failedWelcomeLogo, setFailedWelcomeLogo] = useState<string | null>(null);
|
||||||
|
const shouldShowWelcomeLogo =
|
||||||
|
Boolean(welcomeLogo) && failedWelcomeLogo !== welcomeLogo;
|
||||||
|
const isWelcomeLogoVisible = readRuntimeFlag("VITE_WELCOME_LOGO_VISIBLE");
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
@@ -19,6 +31,8 @@ export function CompactWelcome({ className }: { className?: string }) {
|
|||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
{/* CM logo:浅底圆环 + 主色渐变圆角方块 */}
|
{/* CM logo:浅底圆环 + 主色渐变圆角方块 */}
|
||||||
|
{/* Controlled by VITE_WELCOME_LOGO_VISIBLE in runtime-config.js. */}
|
||||||
|
{isWelcomeLogoVisible && (
|
||||||
<div
|
<div
|
||||||
className="mb-3.5 inline-flex items-center justify-center rounded-full p-3"
|
className="mb-3.5 inline-flex items-center justify-center rounded-full p-3"
|
||||||
style={{
|
style={{
|
||||||
@@ -28,18 +42,29 @@ export function CompactWelcome({ className }: { className?: string }) {
|
|||||||
"1px solid rgb(var(--legacy-workspace-brand-rgb) / 12%)",
|
"1px solid rgb(var(--legacy-workspace-brand-rgb) / 12%)",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div
|
{shouldShowWelcomeLogo && welcomeLogo ? (
|
||||||
className="flex size-14 items-center justify-center rounded-2xl shadow-md"
|
<img
|
||||||
style={{
|
src={welcomeLogo}
|
||||||
backgroundImage:
|
alt=""
|
||||||
"linear-gradient(to top right, var(--workspace-brand), var(--brand-200))",
|
aria-hidden="true"
|
||||||
}}
|
className="size-16 object-contain"
|
||||||
>
|
onError={() => setFailedWelcomeLogo(welcomeLogo)}
|
||||||
<span className="text-2xl font-black tracking-wide text-white italic">
|
/>
|
||||||
{desensitizeText("CM")}
|
) : (
|
||||||
</span>
|
<div
|
||||||
</div>
|
className="flex size-14 items-center justify-center rounded-2xl shadow-md"
|
||||||
|
style={{
|
||||||
|
backgroundImage:
|
||||||
|
"linear-gradient(to top right, var(--workspace-brand), var(--brand-200))",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<span className="text-2xl font-black tracking-wide text-white italic">
|
||||||
|
{desensitizeText("CM")}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
<h1 className="flex items-center justify-center text-2xl font-bold tracking-tight text-foreground/90">
|
<h1 className="flex items-center justify-center text-2xl font-bold tracking-tight text-foreground/90">
|
||||||
{desensitizeText("欢迎使用CM助手")}
|
{desensitizeText("欢迎使用CM助手")}
|
||||||
|
|||||||
@@ -0,0 +1,69 @@
|
|||||||
|
import fallbackLogoDark from "@/assets/img/brand-logo-dark.png";
|
||||||
|
import fallbackLogoLight from "@/assets/img/brand-logo-light.png";
|
||||||
|
import fallbackLogoLightblue from "@/assets/img/brand-logo-lightblue.png";
|
||||||
|
import { readRuntimeEnv } from "@/env";
|
||||||
|
|
||||||
|
export type BrandTheme = "light" | "lightblue" | "dark";
|
||||||
|
|
||||||
|
const headerFallbackAssets: Record<BrandTheme, string> = {
|
||||||
|
light: fallbackLogoLight,
|
||||||
|
lightblue: fallbackLogoLightblue,
|
||||||
|
dark: fallbackLogoDark,
|
||||||
|
};
|
||||||
|
|
||||||
|
const headerRuntimeKeys: Record<BrandTheme, string> = {
|
||||||
|
light: "VITE_BRAND_LOGO_LIGHT_URL",
|
||||||
|
lightblue: "VITE_BRAND_LOGO_LIGHTBLUE_URL",
|
||||||
|
dark: "VITE_BRAND_LOGO_DARK_URL",
|
||||||
|
};
|
||||||
|
|
||||||
|
const welcomeRuntimeKeys: Record<BrandTheme, string> = {
|
||||||
|
light: "VITE_BRAND_WELCOME_LIGHT_URL",
|
||||||
|
lightblue: "VITE_BRAND_WELCOME_LIGHTBLUE_URL",
|
||||||
|
dark: "VITE_BRAND_WELCOME_DARK_URL",
|
||||||
|
};
|
||||||
|
|
||||||
|
function runtimeConfigBaseUrl(): string | undefined {
|
||||||
|
if (typeof document === "undefined") {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
return document.querySelector<HTMLScriptElement>("script[data-runtime-config]")?.src;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resolves an optional post-build brand override from runtime-config.js.
|
||||||
|
* Relative paths are anchored to runtime-config.js, which keeps the URL valid
|
||||||
|
* when the whole SPA is mounted below an Nginx location prefix.
|
||||||
|
*/
|
||||||
|
function readRuntimeBrandAsset(key: string): string | undefined {
|
||||||
|
const configuredUrl = readRuntimeEnv(key);
|
||||||
|
if (!configuredUrl) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const baseUrl =
|
||||||
|
runtimeConfigBaseUrl() ??
|
||||||
|
(typeof window !== "undefined" ? window.location.href : undefined);
|
||||||
|
return baseUrl ? new URL(configuredUrl, baseUrl).href : configuredUrl;
|
||||||
|
} catch {
|
||||||
|
return configuredUrl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getHeaderBrandAsset(theme: BrandTheme): string {
|
||||||
|
return readRuntimeBrandAsset(headerRuntimeKeys[theme]) ?? headerFallbackAssets[theme];
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getHeaderBrandFallback(theme: BrandTheme): string {
|
||||||
|
return headerFallbackAssets[theme];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Welcome content deliberately has no bundled image fallback. An empty
|
||||||
|
* runtime URL keeps the original CM text + gradient treatment.
|
||||||
|
*/
|
||||||
|
export function getWelcomeBrandAsset(theme: BrandTheme): string | undefined {
|
||||||
|
return readRuntimeBrandAsset(welcomeRuntimeKeys[theme]);
|
||||||
|
}
|
||||||
@@ -31,8 +31,6 @@ function PageLayout() {
|
|||||||
const location = useLocation();
|
const location = useLocation();
|
||||||
const { theme } = useTheme();
|
const { theme } = useTheme();
|
||||||
const isDark = theme === "dark";
|
const isDark = theme === "dark";
|
||||||
// 深色导航侧栏不提供收起入口,始终保持展开,避免切换主题后沿用浅色模式的收起状态。
|
|
||||||
const sidebarCollapsed = isDark ? false : collapsed;
|
|
||||||
// 浅色非嵌入:侧边栏为新版「二级导航图标卡片」窄栏(96px);wujie 嵌入仍走旧版手风琴(64/256px)。
|
// 浅色非嵌入:侧边栏为新版「二级导航图标卡片」窄栏(96px);wujie 嵌入仍走旧版手风琴(64/256px)。
|
||||||
const inWujie = useMemo(() => isWujieEnv(), []);
|
const inWujie = useMemo(() => isWujieEnv(), []);
|
||||||
const lightNarrowSidebar = !isDark && !inWujie;
|
const lightNarrowSidebar = !isDark && !inWujie;
|
||||||
@@ -76,14 +74,17 @@ function PageLayout() {
|
|||||||
return (
|
return (
|
||||||
<RoundtableJobNotifier>
|
<RoundtableJobNotifier>
|
||||||
<div className="h-full flex flex-col bg-muted/20">
|
<div className="h-full flex flex-col bg-muted/20">
|
||||||
{pageSidebarAllowed && <Header collapsed={sidebarCollapsed} />}
|
{pageSidebarAllowed && <Header collapsed={collapsed} />}
|
||||||
<div className="flex flex-1 overflow-hidden">
|
<div className="flex flex-1 overflow-hidden">
|
||||||
{pageSidebarAllowed && (
|
{pageSidebarAllowed && (
|
||||||
isDark ? (
|
isDark ? (
|
||||||
<PageSidebarDark />
|
<PageSidebarDark
|
||||||
|
collapsed={collapsed}
|
||||||
|
onToggleCollapsed={() => setCollapsed((prev) => !prev)}
|
||||||
|
/>
|
||||||
) : (
|
) : (
|
||||||
<PageSidebar
|
<PageSidebar
|
||||||
collapsed={sidebarCollapsed}
|
collapsed={collapsed}
|
||||||
onToggleCollapsed={() => setCollapsed((prev) => !prev)}
|
onToggleCollapsed={() => setCollapsed((prev) => !prev)}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
@@ -93,8 +94,8 @@ function PageLayout() {
|
|||||||
!pageSidebarAllowed
|
!pageSidebarAllowed
|
||||||
? ""
|
? ""
|
||||||
: isDark
|
: isDark
|
||||||
// 深色 sidebar 固定展开 118px(10 + 98 + 10 gap)。
|
// 深色 sidebar:展开 118px(10 + 98 + 10 gap),收起时主内容不留左内边距。
|
||||||
? "pl-[118px]"
|
? (collapsed ? "pl-0" : "pl-[118px]")
|
||||||
: lightNarrowSidebar
|
: lightNarrowSidebar
|
||||||
// 浅色新版窄栏:展开三级列时 96→236px,主内容左内边距随之过渡
|
// 浅色新版窄栏:展开三级列时 96→236px,主内容左内边距随之过渡
|
||||||
? `transition-[padding] duration-300 ${lightExpanded ? "pl-[236px]" : "pl-[96px]"}`
|
? `transition-[padding] duration-300 ${lightExpanded ? "pl-[236px]" : "pl-[96px]"}`
|
||||||
@@ -108,7 +109,7 @@ function PageLayout() {
|
|||||||
path="workspace/*"
|
path="workspace/*"
|
||||||
element={
|
element={
|
||||||
<WorkspaceRoutes
|
<WorkspaceRoutes
|
||||||
pageSidebarCollapsed={sidebarCollapsed}
|
pageSidebarCollapsed={collapsed}
|
||||||
pageSidebarHidden={!pageSidebarAllowed}
|
pageSidebarHidden={!pageSidebarAllowed}
|
||||||
headerHidden={!pageSidebarAllowed}
|
headerHidden={!pageSidebarAllowed}
|
||||||
compactWorkspaceSidebar={pageSidebarAllowed}
|
compactWorkspaceSidebar={pageSidebarAllowed}
|
||||||
|
|||||||
@@ -4240,7 +4240,7 @@ function RightSidebar({
|
|||||||
{timelineEntries.map((entry) => (
|
{timelineEntries.map((entry) => (
|
||||||
<div
|
<div
|
||||||
key={entry.id}
|
key={entry.id}
|
||||||
className="relative rounded-lg px-1.5 py-1 transition-colors hover:bg-slate-50/80"
|
className="roundtable-clarification-entry relative rounded-lg px-1.5 py-1 transition-colors hover:bg-slate-50/80"
|
||||||
>
|
>
|
||||||
<div className="absolute -left-[20.5px] top-2 h-3 w-3 rounded-full bg-primary border-2 border-white ring-2 ring-[var(--brand-200)]" />
|
<div className="absolute -left-[20.5px] top-2 h-3 w-3 rounded-full bg-primary border-2 border-white ring-2 ring-[var(--brand-200)]" />
|
||||||
<div className="grid grid-cols-[68px_minmax(0,1fr)] gap-x-2">
|
<div className="grid grid-cols-[68px_minmax(0,1fr)] gap-x-2">
|
||||||
|
|||||||
@@ -10,10 +10,10 @@
|
|||||||
--rt-primary-active: var(--brand-950, #a8071a);
|
--rt-primary-active: var(--brand-950, #a8071a);
|
||||||
--rt-primary-soft: var(--brand-200, #feeded);
|
--rt-primary-soft: var(--brand-200, #feeded);
|
||||||
--rt-page-bg: #fef6f6;
|
--rt-page-bg: #fef6f6;
|
||||||
/* 圆桌全页最小可读字号(含 Step 3 方案展示 / 结果绘制) */
|
/* 圆桌全页最小可读字号(含 Step 3 方案展示 / 结果绘制),跟随「设置 → 字号」。 */
|
||||||
--roundtable-chat-font-size: 16px;
|
--roundtable-chat-font-size: calc(16px * var(--ui-font-scale, 1));
|
||||||
--roundtable-chat-meta-font-size: 16px;
|
--roundtable-chat-meta-font-size: calc(16px * var(--ui-font-scale, 1));
|
||||||
font-size: 16px;
|
font-size: calc(16px * var(--ui-font-scale, 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 会商对话区:气泡正文、步骤卡、Markdown 块 */
|
/* 会商对话区:气泡正文、步骤卡、Markdown 块 */
|
||||||
@@ -29,10 +29,11 @@
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* 抬升页内显式写死的小字号类(text-xs / text-sm / text-[10–14px] / calc),
|
* 抬升页内显式写死的小字号类(text-xs / text-sm / text-[10–14px] / calc),
|
||||||
* 避免 Step 3、侧栏、按钮条等遗漏逐文件改 class。text-base 及以上标题保持不变。
|
* 并让它们跟随设置页字号倍率,避免 Step 3、左右记录区和对话列表遗漏。
|
||||||
|
* text-base 及以上标题由全局 --text-* token 处理。
|
||||||
*/
|
*/
|
||||||
.roundtable-planning :where(.text-xs, .text-sm) {
|
.roundtable-planning :where(.text-xs, .text-sm) {
|
||||||
font-size: 16px;
|
font-size: calc(16px * var(--ui-font-scale, 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
.roundtable-planning :where(
|
.roundtable-planning :where(
|
||||||
@@ -46,7 +47,7 @@
|
|||||||
[class*="text-[14px"],
|
[class*="text-[14px"],
|
||||||
[class*="text-[calc"]
|
[class*="text-[calc"]
|
||||||
):not(.w-5) {
|
):not(.w-5) {
|
||||||
font-size: 16px;
|
font-size: calc(16px * var(--ui-font-scale, 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
.roundtable-planning ::-webkit-scrollbar-thumb {
|
.roundtable-planning ::-webkit-scrollbar-thumb {
|
||||||
@@ -343,6 +344,17 @@
|
|||||||
color: var(--muted-foreground) !important;
|
color: var(--muted-foreground) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ---- Step1 右侧「澄清记录」:覆盖 hover:bg-slate-50/80,避免深色下泛灰白 ---- */
|
||||||
|
:is(.dark .roundtable-planning, .roundtable-planning--dark)
|
||||||
|
.roundtable-clarification-entry:hover {
|
||||||
|
background-color: rgb(22 119 255 / 18%) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
:is(.dark .roundtable-planning, .roundtable-planning--dark)
|
||||||
|
.roundtable-clarification-entry:hover h4 {
|
||||||
|
color: #e8edf5 !important;
|
||||||
|
}
|
||||||
|
|
||||||
/* ---- focus ring:brand-200 → 主色淡环 ---- */
|
/* ---- focus ring:brand-200 → 主色淡环 ---- */
|
||||||
:is(.dark .roundtable-planning, .roundtable-planning--dark)
|
:is(.dark .roundtable-planning, .roundtable-planning--dark)
|
||||||
:is(
|
:is(
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { Bell, ChevronDown, FileText, User } from "lucide-react";
|
import { Bell, ChevronDown, User } from "lucide-react";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { useLocation, useNavigate } from "react-router-dom";
|
import { useLocation, useNavigate } from "react-router-dom";
|
||||||
|
|
||||||
@@ -12,6 +12,7 @@ import {
|
|||||||
import { NotificationDialog } from "@/components/workspace/notification-dialog";
|
import { NotificationDialog } from "@/components/workspace/notification-dialog";
|
||||||
import { getStoredAuth } from "@/core/auth";
|
import { getStoredAuth } from "@/core/auth";
|
||||||
import { useLiveAccount } from "@/core/auth/use-live-account";
|
import { useLiveAccount } from "@/core/auth/use-live-account";
|
||||||
|
import { getHeaderBrandAsset, getHeaderBrandFallback } from "@/core/branding/brand-assets";
|
||||||
import { isWujieEnv } from "@/core/embed/wujie";
|
import { isWujieEnv } from "@/core/embed/wujie";
|
||||||
import { useUnreadCount } from "@/core/notifications";
|
import { useUnreadCount } from "@/core/notifications";
|
||||||
import { useMyPosition } from "@/core/positions";
|
import { useMyPosition } from "@/core/positions";
|
||||||
@@ -46,6 +47,17 @@ type HeaderProps = {
|
|||||||
const Header = ({ collapsed }: HeaderProps) => {
|
const Header = ({ collapsed }: HeaderProps) => {
|
||||||
const { theme } = useTheme();
|
const { theme } = useTheme();
|
||||||
const isDark = theme === "dark";
|
const isDark = theme === "dark";
|
||||||
|
const brandTheme = theme;
|
||||||
|
const configuredBrandLogo = getHeaderBrandAsset(brandTheme);
|
||||||
|
const fallbackBrandLogo = getHeaderBrandFallback(brandTheme);
|
||||||
|
const [failedBrandLogo, setFailedBrandLogo] = useState<string | null>(null);
|
||||||
|
const brandLogo =
|
||||||
|
failedBrandLogo === configuredBrandLogo ? fallbackBrandLogo : configuredBrandLogo;
|
||||||
|
const handleBrandLogoError = () => {
|
||||||
|
if (brandLogo !== fallbackBrandLogo) {
|
||||||
|
setFailedBrandLogo(brandLogo);
|
||||||
|
}
|
||||||
|
};
|
||||||
const [notificationOpen, setNotificationOpen] = useState(false);
|
const [notificationOpen, setNotificationOpen] = useState(false);
|
||||||
const { data: unreadCount = 0 } = useUnreadCount();
|
const { data: unreadCount = 0 } = useUnreadCount();
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
@@ -59,7 +71,8 @@ const Header = ({ collapsed }: HeaderProps) => {
|
|||||||
const positionName = myPosition?.name?.trim() || "";
|
const positionName = myPosition?.name?.trim() || "";
|
||||||
|
|
||||||
const displayName = liveAccount.displayName;
|
const displayName = liveAccount.displayName;
|
||||||
const showTopNav = isDark && !collapsed && !isWujieEnv();
|
// 深色侧栏收起只影响左侧导航,不应让顶栏回退到浅色的图标布局。
|
||||||
|
const showTopNav = isDark && !isWujieEnv();
|
||||||
// 浅色(红/蓝)顶栏:一级导航正常横排进顶栏,不做深色那种围绕基座的特殊处理。
|
// 浅色(红/蓝)顶栏:一级导航正常横排进顶栏,不做深色那种围绕基座的特殊处理。
|
||||||
// 与 collapsed 解耦(折叠只影响侧边栏,一级导航始终可见);wujie 嵌入下隐藏,
|
// 与 collapsed 解耦(折叠只影响侧边栏,一级导航始终可见);wujie 嵌入下隐藏,
|
||||||
// 此时一级导航回落到侧边栏(见 page-sidebar.tsx)。
|
// 此时一级导航回落到侧边栏(见 page-sidebar.tsx)。
|
||||||
@@ -226,7 +239,19 @@ const Header = ({ collapsed }: HeaderProps) => {
|
|||||||
{showTopNav ? (
|
{showTopNav ? (
|
||||||
<>
|
<>
|
||||||
{/* 左分区(flex-1):菜单靠右贴近中间基座 */}
|
{/* 左分区(flex-1):菜单靠右贴近中间基座 */}
|
||||||
<div className="flex h-full min-w-0 flex-1 items-center justify-end">
|
<div className="flex h-full min-w-0 flex-1 items-center justify-between">
|
||||||
|
<div
|
||||||
|
className="flex h-10 w-10 shrink-0 items-center justify-center rounded-lg border border-cyan-300/25 bg-blue-950/55"
|
||||||
|
title="系统 Logo"
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
src={brandLogo}
|
||||||
|
alt=""
|
||||||
|
aria-hidden="true"
|
||||||
|
className="h-[35px] w-[35px] object-contain"
|
||||||
|
onError={handleBrandLogoError}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
<nav
|
<nav
|
||||||
className="top-submenu-bar top-submenu-bar--group"
|
className="top-submenu-bar top-submenu-bar--group"
|
||||||
aria-label="左侧菜单"
|
aria-label="左侧菜单"
|
||||||
@@ -237,8 +262,12 @@ const Header = ({ collapsed }: HeaderProps) => {
|
|||||||
</nav>
|
</nav>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* 中:CM助手(落在背景图发光基座上,纯文字无图标) */}
|
{/* 中:系统 logo + 名称,落在背景图发光基座上。 */}
|
||||||
<div className="header-center-title">{desensitizeText("CM业务中心")}</div>
|
<div className="header-center-title">
|
||||||
|
<span className="header-center-title__label">
|
||||||
|
{desensitizeText("CM业务中心")}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
{/* 右分区(flex-1):菜单靠左贴近中间基座,操作区靠最右 */}
|
{/* 右分区(flex-1):菜单靠左贴近中间基座,操作区靠最右 */}
|
||||||
<div className="flex h-full min-w-0 flex-1 items-center justify-between">
|
<div className="flex h-full min-w-0 flex-1 items-center justify-between">
|
||||||
@@ -260,10 +289,16 @@ const Header = ({ collapsed }: HeaderProps) => {
|
|||||||
{/* 浅色顶栏:logo + 一级导航横排(白字落在 banner 上) */}
|
{/* 浅色顶栏:logo + 一级导航横排(白字落在 banner 上) */}
|
||||||
<div className="flex h-full min-w-0 flex-1 items-center">
|
<div className="flex h-full min-w-0 flex-1 items-center">
|
||||||
<div className="flex flex-shrink-0 items-center gap-2 pr-1">
|
<div className="flex flex-shrink-0 items-center gap-2 pr-1">
|
||||||
<div className="flex h-8 w-8 flex-shrink-0 items-center justify-center rounded-lg bg-white/20">
|
<div className="flex h-10 w-10 flex-shrink-0 items-center justify-center rounded-lg bg-white/20">
|
||||||
<FileText className="h-4 w-4 text-white" />
|
<img
|
||||||
|
src={brandLogo}
|
||||||
|
alt=""
|
||||||
|
aria-hidden="true"
|
||||||
|
className="h-[35px] w-[35px] object-contain"
|
||||||
|
onError={handleBrandLogoError}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<span className="whitespace-nowrap text-base font-semibold text-white">
|
<span className="whitespace-nowrap text-lg font-semibold text-white">
|
||||||
{desensitizeText("CM业务中心")}
|
{desensitizeText("CM业务中心")}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
@@ -287,7 +322,13 @@ const Header = ({ collapsed }: HeaderProps) => {
|
|||||||
: "bg-white/20",
|
: "bg-white/20",
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<FileText className="h-4 w-4 text-white" />
|
<img
|
||||||
|
src={brandLogo}
|
||||||
|
alt=""
|
||||||
|
aria-hidden="true"
|
||||||
|
className="h-7 w-7 object-contain"
|
||||||
|
onError={handleBrandLogoError}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
{!collapsed && (
|
{!collapsed && (
|
||||||
<span className="text-sm font-semibold text-white">
|
<span className="text-sm font-semibold text-white">
|
||||||
|
|||||||
@@ -1179,6 +1179,9 @@ html.dark .strategy-app-header {
|
|||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
user-select: none;
|
user-select: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header-center-title__label {
|
||||||
/* 白→浅蓝→白 金属质感渐变文字 */
|
/* 白→浅蓝→白 金属质感渐变文字 */
|
||||||
background: linear-gradient(
|
background: linear-gradient(
|
||||||
180deg,
|
180deg,
|
||||||
|
|||||||
Reference in new issue
Block a user