feat(agent): 更新代理面板和输入区域,优化状态管理和界面交互
This commit is contained in:
parent
369a749c78
commit
ef963617fc
@ -12,8 +12,12 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="header__right">
|
<div class="header__right">
|
||||||
<!-- AgentState 显示按钮已移动到输入框底部 -->
|
<slot
|
||||||
<slot name="header-right"></slot>
|
name="header-right"
|
||||||
|
:is-agent-panel-open="isAgentPanelOpen"
|
||||||
|
:has-active-thread="!!currentChatId"
|
||||||
|
:toggle-agent-panel="toggleAgentPanel"
|
||||||
|
></slot>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -150,12 +154,10 @@
|
|||||||
:send-button-disabled="isSendButtonDisabled"
|
:send-button-disabled="isSendButtonDisabled"
|
||||||
:mention="mentionConfig"
|
:mention="mentionConfig"
|
||||||
:supports-file-upload="supportsFileUpload"
|
:supports-file-upload="supportsFileUpload"
|
||||||
:is-panel-open="isAgentPanelOpen"
|
|
||||||
:has-active-thread="!!currentChatId"
|
:has-active-thread="!!currentChatId"
|
||||||
:todos="currentTodos"
|
:todos="currentTodos"
|
||||||
@send="handleSendOrStop"
|
@send="handleSendOrStop"
|
||||||
@upload-attachment="handleAttachmentUpload"
|
@upload-attachment="handleAttachmentUpload"
|
||||||
@toggle-panel="toggleAgentPanel"
|
|
||||||
>
|
>
|
||||||
<template #actions-left-extra>
|
<template #actions-left-extra>
|
||||||
<slot name="input-actions-left"></slot>
|
<slot name="input-actions-left"></slot>
|
||||||
@ -193,8 +195,7 @@
|
|||||||
ref="panelWrapperRef"
|
ref="panelWrapperRef"
|
||||||
:class="{
|
:class="{
|
||||||
'is-visible': isAgentPanelOpen,
|
'is-visible': isAgentPanelOpen,
|
||||||
'no-transition': isResizing,
|
'no-transition': isResizing
|
||||||
'is-expanded': isAgentPanelExpanded
|
|
||||||
}"
|
}"
|
||||||
:style="{
|
:style="{
|
||||||
flexBasis: isAgentPanelOpen ? `${panelRatio * 100}%` : '0px'
|
flexBasis: isAgentPanelOpen ? `${panelRatio * 100}%` : '0px'
|
||||||
@ -208,10 +209,7 @@
|
|||||||
:agent-id="currentThread?.agent_id || currentAgentId"
|
:agent-id="currentThread?.agent_id || currentAgentId"
|
||||||
:agent-config-id="selectedAgentConfigId"
|
:agent-config-id="selectedAgentConfigId"
|
||||||
:panel-ratio="panelRatio"
|
:panel-ratio="panelRatio"
|
||||||
:is-expanded="isAgentPanelExpanded"
|
|
||||||
@refresh="handleAgentStateRefresh"
|
@refresh="handleAgentStateRefresh"
|
||||||
@close="toggleAgentPanel"
|
|
||||||
@toggle-expand="togglePanelExpanded"
|
|
||||||
@resize="handlePanelResize"
|
@resize="handlePanelResize"
|
||||||
@resizing="handleResizingChange"
|
@resizing="handleResizingChange"
|
||||||
/>
|
/>
|
||||||
@ -360,7 +358,6 @@ const localUIState = reactive({
|
|||||||
|
|
||||||
// Agent Panel State
|
// Agent Panel State
|
||||||
const isAgentPanelOpen = ref(false)
|
const isAgentPanelOpen = ref(false)
|
||||||
const isAgentPanelExpanded = ref(false)
|
|
||||||
const isResizing = ref(false)
|
const isResizing = ref(false)
|
||||||
const panelRatio = ref(0.3) // 面板宽度比例 (0-1)
|
const panelRatio = ref(0.3) // 面板宽度比例 (0-1)
|
||||||
const panelWrapperRef = ref(null) // 直接操作 DOM
|
const panelWrapperRef = ref(null) // 直接操作 DOM
|
||||||
@ -1583,20 +1580,11 @@ const toggleAgentPanel = async () => {
|
|||||||
const nextOpen = !isAgentPanelOpen.value
|
const nextOpen = !isAgentPanelOpen.value
|
||||||
isAgentPanelOpen.value = nextOpen
|
isAgentPanelOpen.value = nextOpen
|
||||||
|
|
||||||
if (!nextOpen) {
|
|
||||||
isAgentPanelExpanded.value = false
|
|
||||||
}
|
|
||||||
|
|
||||||
if (nextOpen) {
|
if (nextOpen) {
|
||||||
await handleAgentStateRefresh()
|
await handleAgentStateRefresh()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const togglePanelExpanded = () => {
|
|
||||||
if (!isAgentPanelOpen.value) return
|
|
||||||
isAgentPanelExpanded.value = !isAgentPanelExpanded.value
|
|
||||||
}
|
|
||||||
|
|
||||||
// 处理面板宽度调整(使用比例)
|
// 处理面板宽度调整(使用比例)
|
||||||
// 向右拖动(deltaX > 0)让面板变窄,向左拖动(deltaX < 0)让面板变宽
|
// 向右拖动(deltaX > 0)让面板变窄,向左拖动(deltaX < 0)让面板变宽
|
||||||
const handlePanelResize = (clientX) => {
|
const handlePanelResize = (clientX) => {
|
||||||
@ -2006,11 +1994,11 @@ watch(currentChatId, (threadId, oldThreadId) => {
|
|||||||
|
|
||||||
.agent-panel-wrapper {
|
.agent-panel-wrapper {
|
||||||
flex: 0 0 auto;
|
flex: 0 0 auto;
|
||||||
align-self: flex-end;
|
align-self: stretch;
|
||||||
height: 70vh;
|
height: auto;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
z-index: 20;
|
z-index: 20;
|
||||||
margin: 28px 8px;
|
margin: 0 8px 8px;
|
||||||
margin-left: 0;
|
margin-left: 0;
|
||||||
background: var(--gray-0);
|
background: var(--gray-0);
|
||||||
border-radius: 16px;
|
border-radius: 16px;
|
||||||
@ -2019,22 +2007,6 @@ watch(currentChatId, (threadId, oldThreadId) => {
|
|||||||
will-change: flex-basis;
|
will-change: flex-basis;
|
||||||
}
|
}
|
||||||
|
|
||||||
.agent-panel-wrapper.is-expanded {
|
|
||||||
align-self: stretch;
|
|
||||||
height: calc(100% - 16px);
|
|
||||||
margin-top: 8px;
|
|
||||||
margin-bottom: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-height: 700px) {
|
|
||||||
.agent-panel-wrapper {
|
|
||||||
height: calc(100% - 56px);
|
|
||||||
}
|
|
||||||
|
|
||||||
.agent-panel-wrapper.is-expanded {
|
|
||||||
height: calc(100% - 16px);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Workbench transition animations */
|
/* Workbench transition animations */
|
||||||
.agent-panel-wrapper {
|
.agent-panel-wrapper {
|
||||||
|
|||||||
@ -87,16 +87,6 @@
|
|||||||
</template>
|
</template>
|
||||||
<template #actions-right>
|
<template #actions-right>
|
||||||
<div class="input-actions-right">
|
<div class="input-actions-right">
|
||||||
<button
|
|
||||||
v-if="hasActiveThread"
|
|
||||||
class="input-action-btn"
|
|
||||||
:class="{ active: isPanelOpen }"
|
|
||||||
@click.stop="$emit('toggle-panel')"
|
|
||||||
title="查看文件"
|
|
||||||
>
|
|
||||||
<FolderKanban :size="18" />
|
|
||||||
<span>文件</span>
|
|
||||||
</button>
|
|
||||||
<slot name="actions-left-extra"></slot>
|
<slot name="actions-left-extra"></slot>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@ -108,7 +98,7 @@ import { computed, ref, watch } from 'vue'
|
|||||||
import MessageInputComponent from '@/components/MessageInputComponent.vue'
|
import MessageInputComponent from '@/components/MessageInputComponent.vue'
|
||||||
import ImagePreviewComponent from '@/components/ImagePreviewComponent.vue'
|
import ImagePreviewComponent from '@/components/ImagePreviewComponent.vue'
|
||||||
import AttachmentOptionsComponent from '@/components/AttachmentOptionsComponent.vue'
|
import AttachmentOptionsComponent from '@/components/AttachmentOptionsComponent.vue'
|
||||||
import { FolderKanban, SquareCheck } from 'lucide-vue-next'
|
import { SquareCheck } from 'lucide-vue-next'
|
||||||
import {
|
import {
|
||||||
CheckCircleOutlined,
|
CheckCircleOutlined,
|
||||||
ClockCircleOutlined,
|
ClockCircleOutlined,
|
||||||
@ -124,7 +114,6 @@ const props = defineProps({
|
|||||||
sendButtonDisabled: { type: Boolean, default: false },
|
sendButtonDisabled: { type: Boolean, default: false },
|
||||||
mention: { type: Object, default: () => null },
|
mention: { type: Object, default: () => null },
|
||||||
supportsFileUpload: { type: Boolean, default: false },
|
supportsFileUpload: { type: Boolean, default: false },
|
||||||
isPanelOpen: { type: Boolean, default: false },
|
|
||||||
hasActiveThread: { type: Boolean, default: true },
|
hasActiveThread: { type: Boolean, default: true },
|
||||||
todos: {
|
todos: {
|
||||||
type: Array,
|
type: Array,
|
||||||
@ -132,13 +121,7 @@ const props = defineProps({
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
const emit = defineEmits([
|
const emit = defineEmits(['update:modelValue', 'send', 'keydown', 'upload-attachment'])
|
||||||
'update:modelValue',
|
|
||||||
'send',
|
|
||||||
'keydown',
|
|
||||||
'upload-attachment',
|
|
||||||
'toggle-panel'
|
|
||||||
])
|
|
||||||
|
|
||||||
const inputRef = ref(null)
|
const inputRef = ref(null)
|
||||||
const currentImage = ref(null)
|
const currentImage = ref(null)
|
||||||
|
|||||||
@ -11,16 +11,6 @@
|
|||||||
<button class="header-action-btn" title="刷新" @click="emitRefresh">
|
<button class="header-action-btn" title="刷新" @click="emitRefresh">
|
||||||
<RefreshCw :size="15" />
|
<RefreshCw :size="15" />
|
||||||
</button>
|
</button>
|
||||||
<button
|
|
||||||
class="header-action-btn"
|
|
||||||
:title="isExpanded ? '恢复高度' : '向上展开'"
|
|
||||||
@click="emit('toggle-expand')"
|
|
||||||
>
|
|
||||||
<component :is="isExpanded ? ChevronsDownUp : ChevronsUpDown" :size="15" />
|
|
||||||
</button>
|
|
||||||
<button class="close-btn" @click="$emit('close')">
|
|
||||||
<X :size="18" />
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -124,7 +114,7 @@
|
|||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { computed, onMounted, onUnmounted, ref, watch } from 'vue'
|
import { computed, onMounted, onUnmounted, ref, watch } from 'vue'
|
||||||
import { ChevronsDownUp, ChevronsUpDown, Download, RefreshCw, Trash2, X } from 'lucide-vue-next'
|
import { Download, RefreshCw, Trash2 } from 'lucide-vue-next'
|
||||||
import { Modal, message } from 'ant-design-vue'
|
import { Modal, message } from 'ant-design-vue'
|
||||||
import FileTreeComponent from '@/components/FileTreeComponent.vue'
|
import FileTreeComponent from '@/components/FileTreeComponent.vue'
|
||||||
import AgentFilePreview from '@/components/AgentFilePreview.vue'
|
import AgentFilePreview from '@/components/AgentFilePreview.vue'
|
||||||
@ -159,14 +149,10 @@ const props = defineProps({
|
|||||||
panelRatio: {
|
panelRatio: {
|
||||||
type: Number,
|
type: Number,
|
||||||
default: 0.35
|
default: 0.35
|
||||||
},
|
|
||||||
isExpanded: {
|
|
||||||
type: Boolean,
|
|
||||||
default: false
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
const emit = defineEmits(['refresh', 'close', 'resize', 'resizing', 'toggle-expand'])
|
const emit = defineEmits(['refresh', 'resize', 'resizing'])
|
||||||
const INLINE_PREVIEW_MIN_WIDTH = 920
|
const INLINE_PREVIEW_MIN_WIDTH = 920
|
||||||
const DEFAULT_EXPANDED_ROOT_DIRECTORY_NAME = 'user-data'
|
const DEFAULT_EXPANDED_ROOT_DIRECTORY_NAME = 'user-data'
|
||||||
|
|
||||||
@ -751,23 +737,6 @@ watch(useInlinePreview, (isInline) => {
|
|||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.close-btn {
|
|
||||||
border: none;
|
|
||||||
background: transparent;
|
|
||||||
cursor: pointer;
|
|
||||||
color: var(--gray-500);
|
|
||||||
padding: 4px;
|
|
||||||
border-radius: 4px;
|
|
||||||
transition: all 0.2s;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
background: var(--gray-100);
|
|
||||||
color: var(--gray-700);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.tab-content {
|
.tab-content {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
|
|||||||
@ -72,9 +72,20 @@
|
|||||||
</a-dropdown>
|
</a-dropdown>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template #header-right v-if="userStore.isAdmin">
|
<template #header-right="{ isAgentPanelOpen, hasActiveThread, toggleAgentPanel }">
|
||||||
|
<button
|
||||||
|
v-if="hasActiveThread"
|
||||||
|
type="button"
|
||||||
|
class="agent-nav-btn agent-state-btn"
|
||||||
|
:class="{ active: isAgentPanelOpen }"
|
||||||
|
title="查看文件"
|
||||||
|
@click.stop="toggleAgentPanel"
|
||||||
|
>
|
||||||
|
<FolderKanban size="18" class="nav-btn-icon" />
|
||||||
|
<span class="hide-text">文件</span>
|
||||||
|
</button>
|
||||||
<div
|
<div
|
||||||
v-if="selectedAgentId"
|
v-if="userStore.isAdmin && selectedAgentId"
|
||||||
ref="moreButtonRef"
|
ref="moreButtonRef"
|
||||||
type="button"
|
type="button"
|
||||||
class="agent-nav-btn"
|
class="agent-nav-btn"
|
||||||
@ -141,7 +152,7 @@
|
|||||||
import { computed, ref, watch } from 'vue'
|
import { computed, ref, watch } from 'vue'
|
||||||
import { MessageOutlined, ShareAltOutlined } from '@ant-design/icons-vue'
|
import { MessageOutlined, ShareAltOutlined } from '@ant-design/icons-vue'
|
||||||
import { message } from 'ant-design-vue'
|
import { message } from 'ant-design-vue'
|
||||||
import { Settings2, Ellipsis, ChevronDown, Check, Plus } from 'lucide-vue-next'
|
import { Settings2, Ellipsis, ChevronDown, Check, Plus, FolderKanban } from 'lucide-vue-next'
|
||||||
import { useRoute, useRouter } from 'vue-router'
|
import { useRoute, useRouter } from 'vue-router'
|
||||||
import AgentChatComponent from '@/components/AgentChatComponent.vue'
|
import AgentChatComponent from '@/components/AgentChatComponent.vue'
|
||||||
import AgentConfigSidebar from '@/components/AgentConfigSidebar.vue'
|
import AgentConfigSidebar from '@/components/AgentConfigSidebar.vue'
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user