diff --git a/src/views/chat/ChatInterface.vue b/src/views/chat/ChatInterface.vue index f1bd866..ad9fadc 100644 --- a/src/views/chat/ChatInterface.vue +++ b/src/views/chat/ChatInterface.vue @@ -72,10 +72,27 @@ 会话已结束 - + +
+ + + +
@@ -97,8 +114,9 @@ import * as echarts from 'echarts' import { useChatStore } from '@/store/chat' 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 VoiceInput from '@/views/chat/components/VoiceInput.vue' +import { ElTag, ElButton, ElInput, ElAvatar, ElIcon } from 'element-plus' +import { Plus, CaretBottom, CaretRight, Microphone, ChatDotSquare } from '@element-plus/icons-vue' import userAvatarUrl from '@/assets/user.png'; import customAvatarUrl from '@/assets/custom.png'; @@ -109,6 +127,7 @@ defineOptions({ const chatStore = useChatStore() const messageInput = ref('') const messageInputRef = ref(null) +const chatInputRef = ref(null) const chatContainer = ref(null) const scoreChart = ref(null) const maxRows = 6 @@ -119,6 +138,7 @@ const lastScrollHeight = ref(0) const isSummaryCollapsed = ref(false) const showBackgroundDialog = ref(false) const showBackground = ref(false) +const isVoiceInputMode = ref(false) // 使用计算属性获取当前会话的消息 const currentMessages = computed(() => { @@ -224,13 +244,22 @@ watch(() => currentMessages.value?.map(msg => msg.respondingType), (newTypes, ol } }, { deep: true }) -// 发送消息时滚动到底部 -const sendMessage = (message) => { - if (!message.trim() && !sendButtonsDisabled.value) return - chatStore.sendMessage(message) - shouldAutoScroll.value = true // 发送消息时恢复自动滚动 - scrollToBottom(true) -} +// Unified message sending function +const handleSendMessage = (message) => { + if (!message || !message.trim() || isInputDisabled.value) return; + console.log("ChatInterface sending message:", message); + chatStore.sendMessage(message); + shouldAutoScroll.value = true; // 发送消息时恢复自动滚动 + scrollToBottom(true); + + // If coming from voice input, switch back to text mode and focus + if (isVoiceInputMode.value) { + isVoiceInputMode.value = false; + nextTick(() => { + chatInputRef.value?.focus(); + }); + } +}; // 格式化聊天内容 const formatMarkdown = (text, tagType) => { @@ -272,6 +301,17 @@ const toggleBackground = () => { showBackground.value = !showBackground.value } +// Method to switch input mode +const switchInputMode = () => { + isVoiceInputMode.value = !isVoiceInputMode.value; + // Focus the text input when switching back + if (!isVoiceInputMode.value) { + nextTick(() => { + chatInputRef.value?.focus(); + }); + } +}; + onMounted(() => { if (chatContainer.value) { chatContainer.value.addEventListener('scroll', handleScroll) @@ -279,7 +319,6 @@ onMounted(() => { scrollToBottom(true) shouldAutoScroll.value = true } - }) onUnmounted(() => { @@ -303,7 +342,7 @@ onUnmounted(() => { // Dark mode override for chat container html.dark .chat-container { - background-color: var(--el-color-primary-dark-1); // Use the new lighter dark shade + background-color: var(--el-color-primary-dark-1); } .chat-background { @@ -652,258 +691,27 @@ html.dark .background-section .background-content { } } -.input-area { +.input-area-wrapper { display: flex; align-items: flex-end; + & > :deep(.chat-input-area), + & > :deep(div > .voice-trigger-btn) { + flex: 1; + min-width: 0; + } 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; - } } -// Dark mode override for input area -html.dark .input-area { - background: var(--el-color-primary-dark-1); // Dark 1 (Lighter dark) - border-top-color: var(--el-border-color-dark); // Use standard dark border -} - -:deep(.el-button) { +.mode-switch-btn { + width: 40px; height: 40px; - padding: 0 20px; - font-size: 14px; - border-radius: 4px; - font-weight: 500; - - &.is-disabled { - background-color: var(--el-fill-color-light) !important; - border-color: var(--el-border-color) !important; - color: var(--el-text-color-placeholder) !important; - cursor: not-allowed; - } - - &.new-chat-btn { - --el-button-bg-color: var(--el-fill-color-light); - --el-button-border-color: var(--el-border-color); - --el-button-text-color: var(--el-text-color-regular); - --el-button-hover-bg-color: var(--el-fill-color); - --el-button-hover-border-color: var(--el-border-color-hover); - --el-button-hover-text-color: var(--el-text-color-primary); - --el-button-active-bg-color: var(--el-fill-color); - --el-button-active-border-color: var(--el-border-color-hover); - --el-button-active-text-color: var(--el-text-color-primary); - padding: 0 16px; - font-size: 14px; - display: flex; - align-items: center; - white-space: nowrap; - height: 40px; - transition: all 0.3s; - - &:hover { - background-color: var(--el-button-hover-bg-color); - border-color: var(--el-button-hover-border-color); - color: var(--el-button-hover-text-color); - } - - &:active { - background-color: var(--el-button-active-bg-color); - border-color: var(--el-button-active-border-color); - color: var(--el-button-active-text-color); - } - - &.is-disabled { - background-color: var(--el-fill-color-light) !important; - border-color: var(--el-border-color) !important; - color: var(--el-text-color-placeholder) !important; - cursor: not-allowed; - } - } -} - -:deep(.el-button.new-chat-btn > span) { - margin-left: 0 !important; -} - -:deep(.el-button--primary) { - --el-button-bg-color: var(--el-color-primary); - --el-button-border-color: var(--el-color-primary); - --el-button-hover-bg-color: var(--el-color-primary-light-3); - --el-button-hover-border-color: var(--el-color-primary-light-3); - --el-button-active-bg-color: var(--el-color-primary-light-3); - --el-button-active-border-color: var(--el-color-primary-light-3); -} - -.new-chat-btn { - display: flex; - align-items: center; - padding: 0 16px; - white-space: nowrap; -} - -.send-btn { - min-width: 80px; -} - -/* 移动端适配 */ -@media screen and (max-width: 768px) { - .input-area { - padding: 12px; - gap: 8px; - - .new-chat-btn { - padding: 0 12px; - min-width: 70px; - } - } - - :deep(.el-button) { - padding: 0 12px; - - &.new-chat-btn { - .el-icon { - display: none; - } - } - } - - .new-chat-btn { - padding: 0 12px; - } - - .send-btn { - min-width: 60px; - } -} - -/* 统一的滚动条样式 */ -.chat-messages, -.summary-content, -.evaluation-content, -.background-content { - &::-webkit-scrollbar { - width: 8px; - height: 8px; - display: block; - } - - &::-webkit-scrollbar-track { - background: transparent; - margin: 4px 0; - } - - &::-webkit-scrollbar-thumb { - background: var(--el-border-color-darker); - border-radius: 4px; - border: 2px solid transparent; - background-clip: padding-box; - - &:hover { - background: var(--el-border-color); - } - } -} - -/* 添加媒体查询以适应不同屏幕 */ -@media screen and (max-width: 768px) { - .message-content { - max-width: 85%; - } - - .message .message-wrapper.user { - padding-left: 10%; - } -} - -@media screen and (max-width: 480px) { - .message-content { - max-width: 90%; - } - - .message .message-wrapper.user { - padding-left: 5%; - } -} - -.user { - .message-text { - border-color: transparent; - - &::before { - border-right-color: var(--el-color-primary); - right: 100%; - transform: scaleX(-1); - } - } -} - -.assistant { - .message-text { - background-color: var(--el-bg-color); - border-color: var(--el-border-color-lighter); - - &::before { - border-right-color: var(--el-bg-color); - right: 100%; - } - } -} - -.typing { - &::after { - display: none; - } -} - -.typing-indicator { - display: inline-flex; - align-items: center; - gap: 4px; - padding: 4px 8px; - height: 20px; - - span { - width: 4px; - height: 4px; - background: var(--el-text-color-placeholder); - border-radius: 50%; - animation: bounce 1.4s infinite ease-in-out; - - &:nth-child(1) { animation-delay: -0.32s; } - &:nth-child(2) { animation-delay: -0.16s; } - } -} - -@keyframes bounce { - 0%, 80%, 100% { transform: scale(0); } - 40% { transform: scale(1); } + font-size: 20px; + flex-shrink: 0; // Prevent shrinking } .summary-panel { diff --git a/src/views/chat/components/ChatInput.vue b/src/views/chat/components/ChatInput.vue index 9b405da..24eafe3 100644 --- a/src/views/chat/components/ChatInput.vue +++ b/src/views/chat/components/ChatInput.vue @@ -1,5 +1,5 @@