refactor(chat): 重构聊天输入
This commit is contained in:
parent
c22eaf781c
commit
2616b53858
|
@ -72,27 +72,10 @@
|
||||||
<el-tag size="large" type="info" class="end-tag">会话已结束</el-tag>
|
<el-tag size="large" type="info" class="end-tag">会话已结束</el-tag>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="input-area">
|
<ChatInput
|
||||||
<el-input
|
:is-input-disabled="isInputDisabled"
|
||||||
v-model="messageInput"
|
@send="sendMessage"
|
||||||
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>
|
|
||||||
<!-- 总结弹窗 -->
|
<!-- 总结弹窗 -->
|
||||||
<div v-if="lastMessageWithDafen" class="summary-panel" :class="{ 'summary-panel-collapsed': isSummaryCollapsed }">
|
<div v-if="lastMessageWithDafen" class="summary-panel" :class="{ 'summary-panel-collapsed': isSummaryCollapsed }">
|
||||||
<div class="summary-header" @click="toggleSummary">
|
<div class="summary-header" @click="toggleSummary">
|
||||||
|
@ -112,7 +95,8 @@ import { ref, onMounted, onUnmounted, nextTick, watch, computed, defineOptions }
|
||||||
import { marked } from 'marked'
|
import { marked } from 'marked'
|
||||||
import * as echarts from 'echarts'
|
import * as echarts from 'echarts'
|
||||||
import { useChatStore } from '@/store/chat'
|
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 { ElTag, ElButton, ElInput, ElAvatar } from 'element-plus'
|
||||||
import { Plus, CaretBottom, CaretRight } from '@element-plus/icons-vue'
|
import { Plus, CaretBottom, CaretRight } from '@element-plus/icons-vue'
|
||||||
import userAvatarUrl from '@/assets/user.png';
|
import userAvatarUrl from '@/assets/user.png';
|
||||||
|
@ -241,11 +225,9 @@ watch(() => currentMessages.value?.map(msg => msg.respondingType), (newTypes, ol
|
||||||
}, { deep: true })
|
}, { deep: true })
|
||||||
|
|
||||||
// 发送消息时滚动到底部
|
// 发送消息时滚动到底部
|
||||||
const sendMessage = () => {
|
const sendMessage = (message) => {
|
||||||
if (!messageInput.value.trim() && !sendButtonsDisabled.value) return
|
if (!message.trim() && !sendButtonsDisabled.value) return
|
||||||
chatStore.sendMessage(messageInput.value)
|
chatStore.sendMessage(message)
|
||||||
messageInput.value = ''
|
|
||||||
messageInputRef.value.style.height = 'auto'
|
|
||||||
shouldAutoScroll.value = true // 发送消息时恢复自动滚动
|
shouldAutoScroll.value = true // 发送消息时恢复自动滚动
|
||||||
scrollToBottom(true)
|
scrollToBottom(true)
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,7 +35,7 @@
|
||||||
|
|
||||||
|
|
||||||
</el-menu>
|
</el-menu>
|
||||||
<HistoryList />
|
<ChatHistoryList />
|
||||||
<!-- Settings Button Popover -->
|
<!-- Settings Button Popover -->
|
||||||
<el-popover
|
<el-popover
|
||||||
placement="top-end"
|
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 { ChatDotRound, ChatLineRound, ChatRound, Service, Setting, Clock, Delete, Plus } from '@element-plus/icons-vue'
|
||||||
import { ElMenu, ElMenuItem, ElIcon, ElPopover, ElMessageBox } from 'element-plus'
|
import { ElMenu, ElMenuItem, ElIcon, ElPopover, ElMessageBox } from 'element-plus'
|
||||||
import SettingsPanel from '@/components/settings/SettingsPanel.vue'
|
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 chatStore = useChatStore()
|
||||||
const settingsStore = useSettingsStore()
|
const settingsStore = useSettingsStore()
|
||||||
|
|
|
@ -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>
|
Loading…
Reference in New Issue