diff --git a/frontend-web/src/components/workspace/user-identity-badge.tsx b/frontend-web/src/components/workspace/user-identity-badge.tsx new file mode 100644 index 00000000..5929040e --- /dev/null +++ b/frontend-web/src/components/workspace/user-identity-badge.tsx @@ -0,0 +1,54 @@ +"use client"; + +import { User } from "lucide-react"; +import { useMemo } from "react"; + +import { getStoredAuth } from "@/core/auth"; +import { useMyPosition } from "@/core/positions"; +import { cn } from "@/lib/utils"; +import { readUserInfo } from "@/strategy-components/lib/light-app"; + +/** + * 名称 + 岗位 用户标识。全量模式由 strategy-components/Header.tsx 顶栏右侧展示; + * 紧凑模式(不带全局顶栏)无处承载,故抽成共享组件,复用同一套数据来源 + * (`readUserInfo().userName` 优先、回退 auth 邮箱前缀;岗位取 `useMyPosition`), + * 供页面级顶栏(如 ChatPage)右侧复用,保证两套布局一致。 + * + * 配色按当前内容区前景色(foreground / muted-foreground),适配聊天页浅底顶栏, + * 而非 Header 的白字贴 banner 方案。 + */ +export function UserIdentityBadge({ className }: { className?: string }) { + const auth = getStoredAuth(); + const { data: myPosition } = useMyPosition(); + const positionName = myPosition?.name?.trim() || ""; + + // 与 Header.tsx 一致:外部登录写入的真实用户名优先,缺省回退邮箱前缀。 + const displayName = useMemo(() => { + const info = readUserInfo(); + const name = typeof info.userName === "string" ? info.userName.trim() : ""; + if (name) return name; + const email = auth?.email?.trim(); + return email ? email.split("@")[0] : "用户"; + }, [auth?.email]); + + return ( +
+
+ +
+
+

+ {displayName} +

+ {positionName && ( +

+ {positionName} +

+ )} +
+
+ ); +} diff --git a/frontend-web/src/pages/ChatPage.tsx b/frontend-web/src/pages/ChatPage.tsx index 81f21b66..b1c24df3 100644 --- a/frontend-web/src/pages/ChatPage.tsx +++ b/frontend-web/src/pages/ChatPage.tsx @@ -27,6 +27,7 @@ import { QuestionNavigator } from "@/components/workspace/question-navigator"; import { ThreadTitle } from "@/components/workspace/thread-title"; import { TodoList } from "@/components/workspace/todo-list"; import { TokenUsageIndicator } from "@/components/workspace/token-usage-indicator"; +import { UserIdentityBadge } from "@/components/workspace/user-identity-badge"; import { Welcome } from "@/components/workspace/welcome"; import { CompactWelcome } from "@/components/workspace/compact-welcome"; import { readSidebarEnabled } from "@/core/page-layout/sidebar-enabled"; @@ -481,6 +482,17 @@ export default function ChatPage() { + {/* 不带全局顶栏(紧凑模式)时,名称+岗位回落到聊天页顶栏右侧, + 与全量模式 Header 右上角保持一致。全量模式已有顶栏则不重复展示。 */} + {!compactChrome && ( + <> +
+ + + )}
diff --git a/frontend-web/src/strategy-components/components/task-components/TaskFxWorkspace.tsx b/frontend-web/src/strategy-components/components/task-components/TaskFxWorkspace.tsx index e251a68b..8813e37c 100644 --- a/frontend-web/src/strategy-components/components/task-components/TaskFxWorkspace.tsx +++ b/frontend-web/src/strategy-components/components/task-components/TaskFxWorkspace.tsx @@ -2,7 +2,7 @@ * 任务深链(rwfx / 任务FX)的「任务工作区」:一个任务挂多个对话(1:N)。 * * 左侧 = 该任务下的对话列表(按 metadata.taskId 过滤)+「新建对话」; - * 右下角 = 常驻浮层红色按钮(编辑 / 下一步 / 清除任务记录),absolute 定位,时刻可见、不被内容挤走; + * 右下角 = 常驻浮动操作条(编辑 / 下一步 / 清除任务记录),absolute 定位,时刻可见、不被内容挤走; * 右侧 = 渲染选中的对话(AgentChatPage,新建对话顶部带目的树闸门面板)。 * * - 进入 chats 索引:有历史 → 跳最新;无历史 → 跳 new(=新建,走闸门)。 @@ -61,44 +61,69 @@ function TaskConversationsList({ taskId, chatsBase }: { taskId: string; chatsBas const currentThreadId = useMemo(() => parseThreadId(pathname, chatsBase), [pathname, chatsBase]); return ( -
-
-
-
+
{isLoading ? ( -
加载中…
+
+ 加载中… +
) : threads.length === 0 ? ( -
暂无对话
+
+ 暂无对话 +
) : ( threads.map((thread) => ( )) )}
+
); } /** - * 右下角常驻红色操作浮层:编辑 / 下一步 / 清除任务记录。 - * absolute 定位在工作区右侧面板内,z-40,时刻可见、不被聊天内容挤走。 + * 左侧任务栏底部操作区:编辑 / 下一步 / 清除任务记录。 + * 跟随任务对话面板布局,不再覆盖右侧聊天输入框。 */ -function TaskFloatingActions({ taskId }: { taskId: string }) { +function TaskActionsPanel({ taskId }: { taskId: string }) { const [clearOpen, setClearOpen] = useState(false); const [clearing, setClearing] = useState(false); @@ -123,32 +148,43 @@ function TaskFloatingActions({ taskId }: { taskId: string }) { return ( <> -
- - - +
+
+
+ 任务操作 +
+
+ + + +
+
!clearing && setClearOpen(o)}> @@ -173,7 +209,7 @@ function TaskFloatingActions({ taskId }: { taskId: string }) { ); } -/** 任务工作区布局:左列表 + 右 Outlet + 右下角常驻红色操作浮层。 */ +/** 任务工作区布局:左列表 + 右 Outlet。 */ export function TaskFxWorkspace({ taskBase }: { taskBase: string }) { const { taskId, chatsBase } = useChatsBase(taskBase); // 预拉配置,保证「编辑/下一步」的同步 URL 构造能读到缓存。 @@ -185,7 +221,6 @@ export function TaskFxWorkspace({ taskBase }: { taskBase: string }) {
-
);