From b9b92c5d40d0e414900dc1c2aa0cb47748099e79 Mon Sep 17 00:00:00 2001 From: pan-liwei <1987884467@qq.com> Date: Sat, 11 Apr 2026 15:41:36 +0800 Subject: [PATCH 1/3] =?UTF-8?q?refactor:=E8=B5=9B=E5=8D=9A=E5=8D=8F?= =?UTF-8?q?=E5=90=8C=E5=9B=BE=E6=A0=87=E6=9B=B4=E6=8D=A2=EF=BC=8C=E9=80=9A?= =?UTF-8?q?=E7=9F=A5=E4=B8=AD=E5=BF=83=E5=9B=BE=E6=A0=87=E6=9B=B4=E6=8D=A2?= =?UTF-8?q?=EF=BC=8C=E5=81=8F=E7=A7=BB=E9=87=8F=E6=9B=B4=E6=8D=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../x-workbench/src/assets/icon/arrow.svg | 16 +++++++++++ .../src/assets/icon/syber-team.svg | 27 +++++++++++++++++++ .../nav-panel/nav-panel.component.tsx | 18 ++++++++++++- .../notification-center-nav.tsx | 10 ++++--- 4 files changed, 66 insertions(+), 5 deletions(-) create mode 100644 packages/x-workbench/src/assets/icon/arrow.svg create mode 100644 packages/x-workbench/src/assets/icon/syber-team.svg diff --git a/packages/x-workbench/src/assets/icon/arrow.svg b/packages/x-workbench/src/assets/icon/arrow.svg new file mode 100644 index 000000000..74d8dcc88 --- /dev/null +++ b/packages/x-workbench/src/assets/icon/arrow.svg @@ -0,0 +1,16 @@ + + + icon_箭头 + + + + + + + + + + + + + \ No newline at end of file diff --git a/packages/x-workbench/src/assets/icon/syber-team.svg b/packages/x-workbench/src/assets/icon/syber-team.svg new file mode 100644 index 000000000..50875ecb5 --- /dev/null +++ b/packages/x-workbench/src/assets/icon/syber-team.svg @@ -0,0 +1,27 @@ + + + icon_协同 + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/packages/x-workbench/src/components/nav-panel/nav-panel.component.tsx b/packages/x-workbench/src/components/nav-panel/nav-panel.component.tsx index f85f2505f..53a0cc239 100644 --- a/packages/x-workbench/src/components/nav-panel/nav-panel.component.tsx +++ b/packages/x-workbench/src/components/nav-panel/nav-panel.component.tsx @@ -8,7 +8,7 @@ import applyNavIconUrl from "@/assets/icon/apply.svg?url"; import workbenchNavIconUrl from "@/assets/icon/workbench.png?url"; import NotificationCenterNav from "../notification-center/notification-center-nav"; import PersonalCenter from '../personal-center/personal-center.component'; - +import syberTeamIconUrl from '@/assets/icon/syber-team.svg?url'; export default defineComponent({ name: "NavPanel", props: navPanelProps, @@ -273,6 +273,7 @@ export default defineComponent({ { emit('navigate', id); @@ -299,6 +300,21 @@ export default defineComponent({ /> ); } + if (action.id === 'syber-team') { + return ( +
handleActionClick(action)} + > + notice + {action.label} +
+ ); + } return (
void>, required: true }, }, emits: ['navigate'], @@ -52,20 +53,21 @@ export default defineComponent({ return () => (
- notice + notice {props.label} - + notice
Date: Sat, 11 Apr 2026 15:59:52 +0800 Subject: [PATCH 2/3] =?UTF-8?q?fix:=20=E8=B0=83=E6=95=B4=E4=B8=AA=E4=BA=BA?= =?UTF-8?q?=E4=B8=AD=E5=BF=83=E4=BD=8D=E7=BD=AE=EF=BC=9B=E9=9D=9E=E9=A6=96?= =?UTF-8?q?tab=E5=B7=A6=E4=BE=A7=E5=A2=9E=E5=8A=A0=E5=9C=86=E8=A7=92?= =?UTF-8?q?=E9=81=AE=E7=BD=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 2 +- .../nav-panel/nav-panel.component.tsx | 68 +++------ .../components/nav-panel/nav-panel.props.ts | 4 + .../personal-center.component.tsx | 144 +++++++----------- .../personal-center/personal-center.scss | 27 +--- .../workbench-content.component.tsx | 24 ++- .../src/components/workbench.component.tsx | 3 +- packages/x-workbench/src/style.scss | 8 + 8 files changed, 125 insertions(+), 155 deletions(-) diff --git a/.gitignore b/.gitignore index 7484b703a..51047c066 100644 --- a/.gitignore +++ b/.gitignore @@ -35,7 +35,7 @@ Thumbs.db node_modules # Claude Code -.claude/ +.claude ClAUDE_zh.md ClAUDE.md diff --git a/packages/x-workbench/src/components/nav-panel/nav-panel.component.tsx b/packages/x-workbench/src/components/nav-panel/nav-panel.component.tsx index 53a0cc239..f6b8c2905 100644 --- a/packages/x-workbench/src/components/nav-panel/nav-panel.component.tsx +++ b/packages/x-workbench/src/components/nav-panel/nav-panel.component.tsx @@ -19,12 +19,9 @@ export default defineComponent({ const previousActiveNavId = ref(null); // Use activeNavId prop if provided, otherwise use internal state const effectiveActiveId = computed(() => { - // Force re-evaluation when personal-center is activated + // Force re-evaluation when navActions are updated void navActionsUpdateTrigger.value; - // 个人中心激活时,不显示任何导航按钮的选中状态 - if (isPersonalCenterActive.value) { - return null; - } + // personal-center 的激活状态不影响 navActions 的选中高亮 return props.activeNavId ?? selectedActionIndex.value; }); const hoveredAgent = ref(null); @@ -36,16 +33,10 @@ export default defineComponent({ const navActionsUpdateTrigger = ref(0); function handleActionClick(action: { id: number | string; fixed?: boolean; onClick?: () => void }) { - if (action.id === 'personal-center') { - selectedActionIndex.value = null; - isPersonalCenterActive.value = true; - navActionsUpdateTrigger.value++; - } else { - // 先关闭浮层,确保点击事件不会被拦截 - isPersonalCenterActive.value = false; - personalCenterTrigger.value++; - selectedActionIndex.value = action.id; - } + // 先关闭浮层,确保点击事件不会被拦截 + isPersonalCenterActive.value = false; + personalCenterTrigger.value++; + selectedActionIndex.value = action.id; action.onClick?.(); } @@ -264,6 +255,22 @@ export default defineComponent({ ); } + function renderPersonalCenter() { + return ( + { + emit("closePersonalCenter"); + }} + /> + ); + } + function renderNavActions() { return (
@@ -285,36 +292,6 @@ export default defineComponent({ ); } const isPrimary = effectiveActiveId.value === action.id || action.fixed; - const isPersonalCenter = action.id === 'personal-center'; - if (isPersonalCenter) { - return ( - { - emit("closePersonalCenter"); - }} - /> - ); - } - if (action.id === 'syber-team') { - return ( -
handleActionClick(action)} - > - notice - {action.label} -
- ); - } return (
); }, diff --git a/packages/x-workbench/src/components/nav-panel/nav-panel.props.ts b/packages/x-workbench/src/components/nav-panel/nav-panel.props.ts index 34c18a2a9..eb45bb1fc 100644 --- a/packages/x-workbench/src/components/nav-panel/nav-panel.props.ts +++ b/packages/x-workbench/src/components/nav-panel/nav-panel.props.ts @@ -56,6 +56,10 @@ export const navPanelProps = { type: Boolean, default: false, }, + navWidth: { + type: Number, + default: 260, + }, onNavigate: { type: Function as PropType<(id: string) => void>, default: null, diff --git a/packages/x-workbench/src/components/personal-center/personal-center.component.tsx b/packages/x-workbench/src/components/personal-center/personal-center.component.tsx index f9202d3e0..9947aedb1 100644 --- a/packages/x-workbench/src/components/personal-center/personal-center.component.tsx +++ b/packages/x-workbench/src/components/personal-center/personal-center.component.tsx @@ -1,4 +1,4 @@ -import { defineComponent, ref, watch, type PropType } from "vue"; +import { defineComponent, ref, watch, onMounted, onUnmounted, type PropType } from "vue"; import type { NavPanelUserInfo } from '../nav-panel/nav-panel.props'; import settingsIconUrl from "@/assets/icon/settings.svg?url"; import languageIconUrl from "@/assets/icon/language.svg?url"; @@ -9,6 +9,7 @@ import logoutIconUrl from "@/assets/icon/logout.svg?url"; import lightThemeIconUrl from "@/assets/icon/light-theme.svg?url"; import darkThemeIconUrl from "@/assets/icon/dark-theme.svg?url"; import personalCenterIconUrl from "@/assets/icon/personal-center.svg?url"; +import arrowIconUrl from "@/assets/icon/arrow.svg?url"; export default defineComponent({ name: "PersonalCenter", @@ -21,6 +22,14 @@ export default defineComponent({ type: Number, default: 0, }, + navWidth: { + type: Number, + default: 260, + }, + collapsed: { + type: Boolean, + default: false, + }, onActivate: { type: Function as PropType<() => void>, default: null, @@ -33,14 +42,10 @@ export default defineComponent({ emits: ['closePersonalCenter', 'resetPersonalCenter', 'restorePrevious'], setup(props, { emit }) { const CARD_HEIGHT = 420; - const isPersonalCenterHovered = ref(false); const isPersonalCenterActive = ref(false); - const isPersonalCenterClicked = ref(false); - const previousActiveId = ref(null); const personalCenterPopoverRef = ref(null); const personalCenterPopoverTop = ref(undefined); const personalCenterPopoverLeft = ref(undefined); - const personalCenterCloseTimer = ref(null); const isDarkTheme = ref(false); const currentLanguage = ref('中文'); const isLanguageHovered = ref(false); @@ -60,65 +65,29 @@ export default defineComponent({ { id: 'contact', icon: contactIconUrl, label: '联系我们' }, ]; - function clearPersonalCenterCloseTimer() { - if (personalCenterCloseTimer.value !== null) { - window.clearTimeout(personalCenterCloseTimer.value); - personalCenterCloseTimer.value = null; - } - } - - function handlePersonalCenterMouseEnter(event: MouseEvent) { - clearPersonalCenterCloseTimer(); - personalCenterPopoverTop.value = undefined; - personalCenterPopoverLeft.value = undefined; - isPersonalCenterHovered.value = true; - const targetEl = event.currentTarget as HTMLElement; - const targetRect = targetEl.getBoundingClientRect(); - const viewportHeight = window.innerHeight; - const spaceBelow = viewportHeight - targetRect.top; - if (spaceBelow < CARD_HEIGHT) { - const newTop = viewportHeight - CARD_HEIGHT - 16; - personalCenterPopoverTop.value = newTop; - personalCenterPopoverLeft.value = targetRect.right + 8; - } - } - - function handlePersonalCenterMouseLeave() { - clearPersonalCenterCloseTimer(); - personalCenterCloseTimer.value = window.setTimeout(() => { - isPersonalCenterHovered.value = false; - // active 状态下不重置位置,保持卡片位置不变 - if (!isPersonalCenterActive.value) { - personalCenterPopoverTop.value = undefined; - personalCenterPopoverLeft.value = undefined; - } - }, 150); - } - - function handlePersonalCenterPopoverMouseEnter() { - clearPersonalCenterCloseTimer(); - isPersonalCenterHovered.value = true; - } - - function handlePersonalCenterPopoverMouseLeave() { - clearPersonalCenterCloseTimer(); - personalCenterCloseTimer.value = window.setTimeout(() => { - isPersonalCenterHovered.value = false; - // active 状态下不重置位置,保持卡片位置不变 - if (!isPersonalCenterActive.value) { - personalCenterPopoverTop.value = undefined; - personalCenterPopoverLeft.value = undefined; - } - }, 150); - } - function handlePersonalCenterClick() { - // 打开个人中心浮层,不影响主导航状态 - isPersonalCenterActive.value = true; - isPersonalCenterHovered.value = false; - isPersonalCenterClicked.value = true; - emit('closePersonalCenter'); - props.onActivate?.(); + if (isPersonalCenterActive.value) { + // 已打开,点击则关闭 + isPersonalCenterActive.value = false; + personalCenterPopoverTop.value = undefined; + personalCenterPopoverLeft.value = undefined; + } else { + // 打开个人中心浮层,不影响主导航状态 + isPersonalCenterActive.value = true; + // 计算浮层位置:紧贴导航区域右侧 + const viewportHeight = window.innerHeight; + const wrapperEl = document.querySelector('.f-chat-nav-personal-center-btn'); + if (wrapperEl) { + const wrapperRect = wrapperEl.getBoundingClientRect(); + const spaceBelow = viewportHeight - wrapperRect.top; + if (spaceBelow < CARD_HEIGHT) { + personalCenterPopoverTop.value = viewportHeight - CARD_HEIGHT - 16; + } + personalCenterPopoverLeft.value = props.navWidth + 8; + } + emit('closePersonalCenter'); + props.onActivate?.(); + } } function handleLanguageMouseEnter(event: MouseEvent) { @@ -146,11 +115,28 @@ export default defineComponent({ const reset = () => { isPersonalCenterActive.value = false; - isPersonalCenterClicked.value = false; - isPersonalCenterHovered.value = false; emit('resetPersonalCenter'); }; + // 点击外部关闭浮层 + function handleDocumentClick(e: MouseEvent) { + const wrapper = document.querySelector('.f-chat-nav-personal-center-wrapper'); + const popover = document.querySelector('.f-chat-nav-personal-center-popover'); + if (isPersonalCenterActive.value && wrapper && popover) { + if (!wrapper.contains(e.target as Node) && !popover.contains(e.target as Node)) { + reset(); + } + } + } + + onMounted(() => { + document.addEventListener('click', handleDocumentClick); + }); + + onUnmounted(() => { + document.removeEventListener('click', handleDocumentClick); + }); + // 监听 closeTrigger 变化,点击其他按钮时自动关闭 watch(() => props.closeTrigger, () => { reset(); @@ -158,31 +144,19 @@ export default defineComponent({ return () => ( <> - {isPersonalCenterActive.value && ( -
{ reset(); props.onRestorePrevious?.(); }} - /> - )}
{ e.stopPropagation(); handlePersonalCenterClick(); }} > 个人中心 - 个人中心 - {props.userInfo?.avatar ? ( + {!props.collapsed && 个人中心} + {!props.collapsed && (props.userInfo?.avatar ? (
avatar
@@ -190,20 +164,18 @@ export default defineComponent({
- )} - > + ))} + {!props.collapsed && arrow}
{ e.stopPropagation(); }} - onMouseenter={handlePersonalCenterPopoverMouseEnter} - onMouseleave={handlePersonalCenterPopoverMouseLeave} >
@@ -230,7 +202,7 @@ export default defineComponent({ {item.id === 'language' && (
{currentLanguage.value} - > + arrow
)} {item.id === 'theme' && ( diff --git a/packages/x-workbench/src/components/personal-center/personal-center.scss b/packages/x-workbench/src/components/personal-center/personal-center.scss index 895a529e2..15cc8d9d6 100644 --- a/packages/x-workbench/src/components/personal-center/personal-center.scss +++ b/packages/x-workbench/src/components/personal-center/personal-center.scss @@ -18,16 +18,6 @@ cursor: pointer; transition: background-color 0.2s ease; - &:hover, - &.active { - background-color: rgba(0, 0, 0, 0.06); - } - - // 点击激活状态:更深的底色,与 hover 区分 - &.is-clicked { - background-color: rgba(0, 0, 0, 0.12); - } - > .f-chat-nav-action-icon-img { width: 18px; height: 18px; @@ -76,10 +66,11 @@ // 个人中心按钮 chevron .f-chat-nav-personal-center-btn-chevron { - font-size: 12px; - color: rgba(0, 0, 0, 0.35); + width: 16px; + height: 16px; margin-left: 4px; flex-shrink: 0; + opacity: 0.5; } // 个人中心卡片 Popover @@ -304,9 +295,10 @@ } .f-chat-nav-language-arrow { - font-size: 12px; - color: rgba(0, 0, 0, 0.45); + width: 16px; + height: 16px; line-height: 1; + opacity: 0.5; } } @@ -360,14 +352,11 @@ } .f-chat-nav-action-icon-img { - display: none; + display: block; } .f-chat-nav-personal-center-btn-avatar { - display: flex; - width: 28px; - height: 28px; - margin-left: 0; + display: none; } .f-chat-nav-personal-center-btn-chevron { diff --git a/packages/x-workbench/src/components/workbench-content/workbench-content.component.tsx b/packages/x-workbench/src/components/workbench-content/workbench-content.component.tsx index 3a369ed0b..ef30a3fad 100644 --- a/packages/x-workbench/src/components/workbench-content/workbench-content.component.tsx +++ b/packages/x-workbench/src/components/workbench-content/workbench-content.component.tsx @@ -117,12 +117,16 @@ export default defineComponent({ return (
- {functionInstances.value.map((tabItem: FunctionInstance) => { + {functionInstances.value.map((tabItem: FunctionInstance, index: number) => { const isActive = tabItem.instanceId === activeInstanceId.value; + const tabClasses = { + ...getFunctionTabClass(tabItem), + 'wb-content-tab--first': index === 0, + }; return (
onClickFunctionTabItem(tabItem)} > {tabItem.name || '未命名'} @@ -142,6 +146,7 @@ export default defineComponent({ )} {isActive && ( <> + {/* 右侧圆角遮罩 */} + {/* 非第一个 tab 左侧圆角遮罩 */} + {index !== 0 && ( + + )} )}
diff --git a/packages/x-workbench/src/components/workbench.component.tsx b/packages/x-workbench/src/components/workbench.component.tsx index cd2968ed5..0c3cc60ec 100644 --- a/packages/x-workbench/src/components/workbench.component.tsx +++ b/packages/x-workbench/src/components/workbench.component.tsx @@ -48,8 +48,6 @@ const NAV_ACTIONS: NavActionItem[] = [ // { id: 'automation', icon: 'f-icon f-icon-forecast-of-completion', label: '自动化' }, { id: 'syber-team', icon: 'f-icon f-icon-team', label: '赛博协同'}, { id: 'notification-center', icon: 'f-icon f-icon-message', label: '通知中心'}, - { id: 'personal-center', icon: 'f-icon f-icon-user', label: '个人中心' }, - { id: 'car-report', icon: 'f-icon f-icon-survey', label: '经营分析报告' }, ]; const WORKBENCH_NAV_MENU: MenuGroup[] = [ @@ -209,6 +207,7 @@ export default defineComponent({ primaryActionId="new-task" activeNavId={activeNavId.value} collapsed={chatNavPaneCollapsed.value} + navWidth={chatNavPaneCollapsed.value ? 64 : 260} onOpenAgentHub={() => { activeNavId.value = 'assistant'; agentHubRef.value?.openFromNav(); diff --git a/packages/x-workbench/src/style.scss b/packages/x-workbench/src/style.scss index 6e2063307..5ef6befa9 100644 --- a/packages/x-workbench/src/style.scss +++ b/packages/x-workbench/src/style.scss @@ -1985,6 +1985,14 @@ $wb-tabs-bar-bg-hover: rgba(229, 242, 255, 0.5); display: block; pointer-events: none; } + + .wb-content-tab__corner--bl { + position: absolute; + left: -10px; + bottom: 0; + display: block; + pointer-events: none; + } } &.wb-content-tab--fix .wb-content-tab__title { -- Gitee From 4deec863ec52c2a3d2569759c4e8e777ef22ca42 Mon Sep 17 00:00:00 2001 From: pan-liwei <1987884467@qq.com> Date: Sat, 11 Apr 2026 16:30:36 +0800 Subject: [PATCH 3/3] =?UTF-8?q?fix:=E8=B5=9B=E5=8D=9A=E5=8D=8F=E5=90=8C?= =?UTF-8?q?=E5=9B=BE=E6=A0=87?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../components/nav-panel/nav-panel.component.tsx | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/packages/x-workbench/src/components/nav-panel/nav-panel.component.tsx b/packages/x-workbench/src/components/nav-panel/nav-panel.component.tsx index f6b8c2905..3b89501e7 100644 --- a/packages/x-workbench/src/components/nav-panel/nav-panel.component.tsx +++ b/packages/x-workbench/src/components/nav-panel/nav-panel.component.tsx @@ -292,6 +292,21 @@ export default defineComponent({ ); } const isPrimary = effectiveActiveId.value === action.id || action.fixed; + if (action.id === 'syber-team') { + return ( +
handleActionClick(action)} + > + notice + {action.label} +
+ ); + } return (