feat(agent-panel): 添加可调整宽度的agent面板
- 为 AgentPanel 组件添加拖拽调整宽度功能 - 引入 resize-handle 组件处理拖拽交互 - 设置面板宽度范围限制(280px-600px)
This commit is contained in:
parent
f31705d58c
commit
cfb0ed7609
@ -167,12 +167,18 @@
|
|||||||
<!-- Agent Panel Area -->
|
<!-- Agent Panel Area -->
|
||||||
|
|
||||||
<transition name="panel-slide">
|
<transition name="panel-slide">
|
||||||
<div class="agent-panel-wrapper" v-if="isAgentPanelOpen && hasAgentStateContent">
|
<div
|
||||||
|
class="agent-panel-wrapper"
|
||||||
|
v-if="isAgentPanelOpen && hasAgentStateContent"
|
||||||
|
:style="{ width: `${panelWidth}px` }"
|
||||||
|
>
|
||||||
<AgentPanel
|
<AgentPanel
|
||||||
:agent-state="currentAgentState"
|
:agent-state="currentAgentState"
|
||||||
:thread-id="currentChatId"
|
:thread-id="currentChatId"
|
||||||
|
:panel-width="panelWidth"
|
||||||
@refresh="handleAgentStateRefresh"
|
@refresh="handleAgentStateRefresh"
|
||||||
@close="toggleAgentPanel"
|
@close="toggleAgentPanel"
|
||||||
|
@resize="handlePanelResize"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</transition>
|
</transition>
|
||||||
@ -257,6 +263,9 @@ const localUIState = reactive({
|
|||||||
|
|
||||||
// Agent Panel State
|
// Agent Panel State
|
||||||
const isAgentPanelOpen = ref(false)
|
const isAgentPanelOpen = ref(false)
|
||||||
|
const panelWidth = ref(360)
|
||||||
|
const minPanelWidth = 280
|
||||||
|
const maxPanelWidth = 600
|
||||||
|
|
||||||
// ==================== COMPUTED PROPERTIES ====================
|
// ==================== COMPUTED PROPERTIES ====================
|
||||||
const currentAgentId = computed(() => {
|
const currentAgentId = computed(() => {
|
||||||
@ -973,6 +982,12 @@ const toggleAgentPanel = () => {
|
|||||||
isAgentPanelOpen.value = !isAgentPanelOpen.value
|
isAgentPanelOpen.value = !isAgentPanelOpen.value
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 处理面板宽度调整
|
||||||
|
// 反转 deltaX:向左拖动时让面板变宽(像拉出更多空间)
|
||||||
|
const handlePanelResize = (deltaX) => {
|
||||||
|
panelWidth.value = Math.min(maxPanelWidth, Math.max(minPanelWidth, panelWidth.value - deltaX))
|
||||||
|
}
|
||||||
|
|
||||||
// ==================== HELPER FUNCTIONS ====================
|
// ==================== HELPER FUNCTIONS ====================
|
||||||
const getLastMessage = (conv) => {
|
const getLastMessage = (conv) => {
|
||||||
if (!conv?.messages?.length) return null
|
if (!conv?.messages?.length) return null
|
||||||
@ -1153,7 +1168,7 @@ watch(
|
|||||||
}
|
}
|
||||||
|
|
||||||
.agent-panel-wrapper {
|
.agent-panel-wrapper {
|
||||||
flex: 3; /* 4:3 ratio with chat-main */
|
flex: 0 0 auto;
|
||||||
height: calc(100% - 32px);
|
height: calc(100% - 32px);
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
z-index: 20;
|
z-index: 20;
|
||||||
@ -1163,7 +1178,7 @@ watch(
|
|||||||
border-radius: 12px;
|
border-radius: 12px;
|
||||||
box-shadow: 0 4px 20px var(--shadow-1);
|
box-shadow: 0 4px 20px var(--shadow-1);
|
||||||
border: 1px solid var(--gray-200);
|
border: 1px solid var(--gray-200);
|
||||||
min-width: 0; /* Prevent flex item from overflowing */
|
min-width: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Workbench transition animations */
|
/* Workbench transition animations */
|
||||||
|
|||||||
@ -1,5 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="agent-panel">
|
<div class="agent-panel" :class="{ resizing: isResizing }">
|
||||||
|
<!-- 拖拽手柄 -->
|
||||||
|
<div class="resize-handle" @mousedown="startResize"></div>
|
||||||
<div class="panel-header">
|
<div class="panel-header">
|
||||||
<div class="panel-title">
|
<div class="panel-title">
|
||||||
<FolderCode :size="16" class="header-icon" />
|
<FolderCode :size="16" class="header-icon" />
|
||||||
@ -238,7 +240,7 @@ const props = defineProps({
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
const emit = defineEmits(['refresh', 'close'])
|
const emit = defineEmits(['refresh', 'close', 'resize'])
|
||||||
|
|
||||||
const activeTab = ref('todos')
|
const activeTab = ref('todos')
|
||||||
const modalVisible = ref(false)
|
const modalVisible = ref(false)
|
||||||
@ -396,14 +398,68 @@ const handleFileChange = async (event) => {
|
|||||||
const emitRefresh = () => {
|
const emitRefresh = () => {
|
||||||
emit('refresh')
|
emit('refresh')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 拖拽调整宽度相关
|
||||||
|
const isResizing = ref(false)
|
||||||
|
const startX = ref(0)
|
||||||
|
|
||||||
|
const startResize = (e) => {
|
||||||
|
isResizing.value = true
|
||||||
|
startX.value = e.clientX
|
||||||
|
document.body.style.cursor = 'col-resize'
|
||||||
|
document.body.style.userSelect = 'none'
|
||||||
|
// 添加全局事件监听
|
||||||
|
document.addEventListener('mousemove', onMouseMove)
|
||||||
|
document.addEventListener('mouseup', stopResize)
|
||||||
|
}
|
||||||
|
|
||||||
|
const onMouseMove = (e) => {
|
||||||
|
if (!isResizing.value) return
|
||||||
|
const deltaX = e.clientX - startX.value
|
||||||
|
startX.value = e.clientX
|
||||||
|
emit('resize', deltaX)
|
||||||
|
}
|
||||||
|
|
||||||
|
const stopResize = () => {
|
||||||
|
if (isResizing.value) {
|
||||||
|
isResizing.value = false
|
||||||
|
document.body.style.cursor = ''
|
||||||
|
document.body.style.userSelect = ''
|
||||||
|
document.removeEventListener('mousemove', onMouseMove)
|
||||||
|
document.removeEventListener('mouseup', stopResize)
|
||||||
|
}
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="less">
|
<style scoped lang="less">
|
||||||
|
.resize-handle {
|
||||||
|
position: absolute;
|
||||||
|
left: -2px;
|
||||||
|
top: 50%;
|
||||||
|
transform: translateY(-50%);
|
||||||
|
height: 32px;
|
||||||
|
width: 4px;
|
||||||
|
cursor: col-resize;
|
||||||
|
background: var(--gray-300);
|
||||||
|
border-radius: 2px;
|
||||||
|
z-index: 10;
|
||||||
|
transition: background 0.2s;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: var(--main-400);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.agent-panel {
|
.agent-panel {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
background: var(--gray-0);
|
background: var(--gray-0);
|
||||||
|
transition: none;
|
||||||
|
|
||||||
|
&.resizing {
|
||||||
|
transition: none;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.panel-header {
|
.panel-header {
|
||||||
@ -412,7 +468,7 @@ const emitRefresh = () => {
|
|||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
padding: 0 12px;
|
padding: 0 12px;
|
||||||
height: 40px;
|
height: 40px;
|
||||||
border-bottom: 1px solid var(--gray-200);
|
background: var(--gray-25);
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -464,10 +520,11 @@ const emitRefresh = () => {
|
|||||||
|
|
||||||
.tabs {
|
.tabs {
|
||||||
display: flex;
|
display: flex;
|
||||||
border-bottom: 1px solid var(--gray-100);
|
background: var(--gray-25);
|
||||||
position: relative;
|
position: relative;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
padding: 4px 6px;
|
padding: 8px 6px;
|
||||||
|
padding-top: 0px;
|
||||||
gap: 4px;
|
gap: 4px;
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
}
|
}
|
||||||
@ -484,12 +541,12 @@ const emitRefresh = () => {
|
|||||||
border-radius: 999px;
|
border-radius: 999px;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
background: var(--gray-100);
|
background: var(--gray-150);
|
||||||
color: var(--gray-900);
|
color: var(--gray-900);
|
||||||
}
|
}
|
||||||
|
|
||||||
&.active {
|
&.active {
|
||||||
background: var(--gray-100);
|
background: var(--gray-150);
|
||||||
color: var(--gray-900);
|
color: var(--gray-900);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user