refactor(chat): 重构聊天输入

This commit is contained in:
Lexcubia 2025-04-25 14:32:24 +08:00
parent c22eaf781c
commit 2616b53858
5 changed files with 124 additions and 29 deletions

View File

@ -72,27 +72,10 @@
<el-tag size="large" type="info" class="end-tag">会话已结束</el-tag>
</div>
</div>
<div class="input-area">
<el-input
v-model="messageInput"
type="textarea"
:rows="1"
:autosize="{ minRows: 1, maxRows: 6 }"
placeholder="请输入消息..."
@keyup.enter.exact.prevent="sendMessage"
resize="none"
ref="messageInputRef"
:disabled="isInputDisabled"
/>
<el-button
class="send-btn"
@click="sendMessage"
:disabled="isInputDisabled || !messageInput.trim()"
type="primary"
>
发送
</el-button>
</div>
<ChatInput
:is-input-disabled="isInputDisabled"
@send="sendMessage"
/>
<!-- 总结弹窗 -->
<div v-if="lastMessageWithDafen" class="summary-panel" :class="{ 'summary-panel-collapsed': isSummaryCollapsed }">
<div class="summary-header" @click="toggleSummary">
@ -112,7 +95,8 @@ import { ref, onMounted, onUnmounted, nextTick, watch, computed, defineOptions }
import { marked } from 'marked'
import * as echarts from 'echarts'
import { useChatStore } from '@/store/chat'
import ChatHeader from '@/views/chat/ChatHeader.vue'
import ChatHeader from '@/views/chat/components/ChatHeader.vue'
import ChatInput from '@/views/chat/components/ChatInput.vue'
import { ElTag, ElButton, ElInput, ElAvatar } from 'element-plus'
import { Plus, CaretBottom, CaretRight } from '@element-plus/icons-vue'
import userAvatarUrl from '@/assets/user.png';
@ -241,11 +225,9 @@ watch(() => currentMessages.value?.map(msg => msg.respondingType), (newTypes, ol
}, { deep: true })
//
const sendMessage = () => {
if (!messageInput.value.trim() && !sendButtonsDisabled.value) return
chatStore.sendMessage(messageInput.value)
messageInput.value = ''
messageInputRef.value.style.height = 'auto'
const sendMessage = (message) => {
if (!message.trim() && !sendButtonsDisabled.value) return
chatStore.sendMessage(message)
shouldAutoScroll.value = true //
scrollToBottom(true)
}

View File

@ -35,7 +35,7 @@
</el-menu>
<HistoryList />
<ChatHistoryList />
<!-- Settings Button Popover -->
<el-popover
placement="top-end"
@ -65,7 +65,7 @@ import { computed, ref, onMounted, onUnmounted } from 'vue'
import { ChatDotRound, ChatLineRound, ChatRound, Service, Setting, Clock, Delete, Plus } from '@element-plus/icons-vue'
import { ElMenu, ElMenuItem, ElIcon, ElPopover, ElMessageBox } from 'element-plus'
import SettingsPanel from '@/components/settings/SettingsPanel.vue'
import HistoryList from '@/components/chat/HistoryList.vue'
import ChatHistoryList from '@/views/chat/components/ChatHistoryList.vue'
const chatStore = useChatStore()
const settingsStore = useSettingsStore()

View File

@ -0,0 +1,113 @@
<template>
<div class="input-area">
<el-input
v-model="messageInput"
type="textarea"
:rows="1"
:autosize="{ minRows: 1, maxRows: 6 }"
placeholder="请输入消息..."
@keyup.enter.exact.prevent="handleSend"
resize="none"
ref="messageInputRef"
:disabled="isInputDisabled"
/>
<el-button
class="send-btn"
@click="handleSend"
:disabled="isInputDisabled || !messageInput.trim()"
type="primary"
>
发送
</el-button>
</div>
</template>
<script setup>
import { ref, computed } from 'vue'
import { ElInput, ElButton } from 'element-plus'
const props = defineProps({
isInputDisabled: {
type: Boolean,
default: false
}
})
const emit = defineEmits(['send'])
const messageInput = ref('')
const messageInputRef = ref(null)
const maxRows = 6
const handleSend = () => {
if (!messageInput.value.trim() && !props.isInputDisabled) return
emit('send', messageInput.value)
messageInput.value = ''
messageInputRef.value.style.height = 'auto'
}
const adjustTextareaHeight = () => {
const textarea = messageInputRef.value
textarea.style.height = 'auto'
const newHeight = Math.min(textarea.scrollHeight, maxRows * 24)
textarea.style.height = newHeight + 'px'
}
</script>
<style lang="scss" scoped>
.input-area {
display: flex;
align-items: flex-end;
gap: 8px;
padding: 16px 20px;
border-top: 1px solid var(--el-border-color-light);
background-color: var(--el-bg-color);
position: relative;
z-index: 1;
.el-textarea {
flex: 1;
:deep(.el-textarea__inner) {
border-radius: 8px;
resize: none;
padding: 8px 12px;
min-height: 40px !important;
max-height: 120px;
line-height: 24px;
font-size: 14px;
&:disabled {
background-color: var(--el-input-disabled-bg);
cursor: not-allowed;
}
}
}
.send-btn {
height: 40px;
padding: 0 20px;
border-radius: 8px;
font-size: 14px;
min-width: 80px;
}
}
// Dark mode override for input area
html.dark .input-area {
background: var(--el-color-primary-dark-1);
border-top-color: var(--el-border-color-dark);
}
/* 移动端适配 */
@media screen and (max-width: 768px) {
.input-area {
padding: 12px;
gap: 8px;
}
.send-btn {
min-width: 60px;
}
}
</style>