feat(sidebar): 分组模式统一顶部对话类型切换器,列表切换改推挤动画

- 分段控件(普通对话/多智能体)在分组模式下也显示在搜索框下方,移除工作区标题行单字切换按钮
- 列表切换移除 Transition out-in(消除空白期),改为同步推挤滑动:新面板绝对定位从侧边滑入顶出旧面板,去掉 opacity 淡入淡出
- 切换时重置列表滚动位置;同步更新 AGENTS.md 描述
This commit is contained in:
JOJO 2026-08-11 18:14:22 +08:00
parent 9fff41650d
commit d3ec94e7e8
3 changed files with 23 additions and 33 deletions

View File

@ -374,7 +374,7 @@ AI 执行以下流程时,每一步都要向用户说明在做什么:
- `newConversationType`(同上):空对话时输入栏待创建类型(`'agent' | 'multi_agent'`localStorage 持久化(`agents_new_conversation_type`),由输入栏底行 `+` 右侧的类型选择器修改(`InputComposer.vue` 的 `agent-type-switcher`)。
- `sidebarConversationType``stores/conversation.ts`):侧边栏列表过滤器(`'normal' | 'multi_agent'`localStorage 持久化(`agents_sidebar_conversation_type`),驱动列表/搜索的 `multi_agent_mode=0|1` 请求参数与侧边栏新建按钮的对话类型。
- `stores/conversation.ts``multiAgentMode` 字段保留,但语义已变为「当前对话是否多智能体」,仅由 `enterConversation`meta 落地)与空对话 watcher复位 false写入——`stores/subAgent.ts` 的读取点依赖它,不得删除。
- 侧边栏类型切换 UI平铺模式在「搜索对话」下方显示分段控件;分组模式在工作区标题行新建/更多按钮左侧显示单字切换按钮;列表区域用 `<Transition mode="out-in">` 做左右滑动动画(切多智能体向左滑出,切回反向)。
- 侧边栏类型切换 UI分段控件统一显示在「搜索对话」下方(平铺/分组模式位置一致,搜索时隐藏);列表区域用 `<Transition>` 推挤式同步滑动动画(无 out-in新面板绝对定位从侧边滑入把旧面板顶出切多智能体向左切回反向切换时重置列表滚动位置)。
- 发送消息创建对话时按 `newConversationType` 选择 `/api/conversations``/api/multiagent/conversations``message/send.ts`);侧边栏新建按 `sidebarConversationType` 选择(`conversation/action.ts`)。
### 11.1 角色与实例

View File

@ -113,7 +113,7 @@
</label>
</div>
<div v-if="!searchActive && !isGroupByWorkspaceActive" class="conversation-type-switch">
<div v-if="!searchActive" class="conversation-type-switch">
<button
type="button"
class="conversation-type-option"
@ -132,8 +132,8 @@
</button>
</div>
<div class="conversation-list">
<Transition :name="slideTransitionName" mode="out-in">
<div ref="conversationListEl" class="conversation-list">
<Transition :name="slideTransitionName">
<div :key="sidebarType" class="conversation-list-pane">
<div v-if="runningTaskItems.length && !searchActive && !isGroupByWorkspaceActive" class="running-task-section">
<div class="running-task-section-title">运行中的任务</div>
@ -203,15 +203,6 @@
class="workspace-group-actions"
:class="{ visible: hoverWorkspaceId === String(ws.workspace_id || '') || openWorkspaceMenuId === String(ws.workspace_id || '') }"
>
<button
type="button"
class="workspace-type-btn"
:title="sidebarType === 'multi_agent' ? '当前:多智能体对话(点击切换为普通对话)' : '当前:普通对话(点击切换为多智能体对话)'"
:aria-label="sidebarType === 'multi_agent' ? '切换为普通对话' : '切换为多智能体对话'"
@click.stop="toggleSidebarType"
>
<span class="workspace-type-btn-label">{{ sidebarType === 'multi_agent' ? '多' : '单' }}</span>
</button>
<button
type="button"
class="workspace-new-btn"
@ -774,16 +765,16 @@ const slideTransitionName = computed(() =>
sidebarType.value === 'multi_agent' ? 'type-slide-left' : 'type-slide-right'
);
/** 列表容器:切换类型时重置滚动位置,保证推挤动画从顶部开始 */
const conversationListEl = ref<HTMLElement | null>(null);
const setSidebarType = (type: 'normal' | 'multi_agent') => {
if (conversationStore.sidebarConversationType === type) return;
if (conversationListEl.value) conversationListEl.value.scrollTop = 0;
conversationStore.setSidebarConversationType(type);
emit('conversation-type-change', type);
};
const toggleSidebarType = () => {
setSidebarType(sidebarType.value === 'multi_agent' ? 'normal' : 'multi_agent');
};
const { sidebarCollapsed: collapsed } = storeToRefs(uiStore);
const {
searchQuery,

View File

@ -211,6 +211,8 @@
overflow-y: auto;
overflow-x: hidden;
padding-top: 8px;
/* 为对话类型切换推挤动画提供绝对定位上下文 */
position: relative;
scrollbar-width: none !important;
-ms-overflow-style: none;
}
@ -808,7 +810,6 @@ body[data-theme='dark'] .workspace-group-header.search-result-group-header:hover
opacity: 1;
}
.workspace-type-btn,
.workspace-new-btn,
.workspace-more-btn {
width: 28px;
@ -825,7 +826,6 @@ body[data-theme='dark'] .workspace-group-header.search-result-group-header:hover
color 140ms ease;
}
.workspace-type-btn:hover,
.workspace-new-btn:hover,
.workspace-more-btn:hover,
.workspace-group-actions.visible .workspace-more-btn[aria-expanded='true'] {
@ -833,12 +833,6 @@ body[data-theme='dark'] .workspace-group-header.search-result-group-header:hover
color: var(--text-primary);
}
.workspace-type-btn-label {
font-size: 12px;
font-weight: 600;
line-height: 1;
}
.workspace-more-btn span {
position: relative;
display: inline-block;
@ -1153,7 +1147,8 @@ body[data-theme='dark'] .conversation-sidebar .load-more-btn:hover:not(:disabled
font-weight: 600;
}
/* 列表区域整体左右滑动切换动画 */
/* 列表区域整体左右滑动切换动画推挤式新面板从侧边滑入把旧面板顶出去
out-in 空白期两面板同速同幅滑动边缘始终相接 */
.conversation-list-pane {
min-height: 0;
}
@ -1162,27 +1157,31 @@ body[data-theme='dark'] .conversation-sidebar .load-more-btn:hover:not(:disabled
.type-slide-left-leave-active,
.type-slide-right-enter-active,
.type-slide-right-leave-active {
transition:
transform 200ms ease,
opacity 200ms ease;
transition: transform 220ms ease;
}
/* 进入面板绝对定位覆盖在容器上与流内滑出的旧面板同步推移
padding-top 对齐 .conversation-list padding-top避免垂直错位 */
.type-slide-left-enter-active,
.type-slide-right-enter-active {
position: absolute;
inset: 0;
padding-top: 8px;
overflow: hidden;
}
.type-slide-left-enter-from {
transform: translateX(100%);
opacity: 0;
}
.type-slide-left-leave-to {
transform: translateX(-100%);
opacity: 0;
}
.type-slide-right-enter-from {
transform: translateX(-100%);
opacity: 0;
}
.type-slide-right-leave-to {
transform: translateX(100%);
opacity: 0;
}