🎈 perf: 修改chat中录音的键位防止误触

修改键位为Ctrl + A ,以及还加入SSE断连提示
This commit is contained in:
IGCrystal
2025-06-18 17:58:15 +08:00
parent 00ef0d7e3d
commit 343fc22168
3 changed files with 115 additions and 34 deletions
@@ -52,10 +52,24 @@
"modes": {
"darkMode": "Switch to Dark Mode",
"lightMode": "Switch to Light Mode"
},
"shortcuts": {
}, "shortcuts": {
"help": "Get Help",
"voiceRecord": "Record Voice",
"pasteImage": "Paste Image"
},
"connection": {
"title": "Connection Status Notice",
"message": "The system detected that the chat connection needs to be re-established.",
"reasons": "This may be due to:",
"reasonWindowResize": "Switching chat window size (normal behavior)",
"reasonMultipleTabs": "Opening chat pages in other tabs",
"reasonNetworkIssue": "Temporary network interruption",
"notice": "Note: To ensure proper message delivery, the system only allows one active chat connection at a time. If you're using chat in multiple tabs, please keep only one page open.",
"understand": "Got it",
"status": {
"reconnecting": "Reconnecting...",
"reconnected": "Chat connection re-established",
"failed": "Connection failed, please refresh the page"
}
}
}
@@ -52,10 +52,24 @@
"modes": {
"darkMode": "切换到夜间模式",
"lightMode": "切换到日间模式"
},
"shortcuts": {
}, "shortcuts": {
"help": "获取帮助",
"voiceRecord": "录制语音",
"pasteImage": "粘贴图片"
},
"connection": {
"title": "连接状态提醒",
"message": "系统检测到聊天连接需要重新建立。",
"reasons": "这可能是因为:",
"reasonWindowResize": "切换了聊天窗口大小(正常现象)",
"reasonMultipleTabs": "在其他标签页中打开了聊天页面",
"reasonNetworkIssue": "网络连接临时中断",
"notice": "注意:为了确保消息正确接收,系统只允许同时保持一个聊天连接。如果您在多个标签页中使用聊天功能,建议只保留一个页面。",
"understand": "我知道了",
"status": {
"reconnecting": "正在重新连接...",
"reconnected": "聊天连接已重新建立",
"failed": "连接失败,请刷新页面重试"
}
}
}
+83 -30
View File
@@ -161,7 +161,7 @@
</div>
<div class="welcome-hint">
<span>{{ t('core.common.longPress') }}</span>
<code>Ctrl</code>
<code>Ctrl + A</code>
<span>{{ tm('shortcuts.voiceRecord') }} 🎤</span>
</div>
<div class="welcome-hint">
@@ -265,9 +265,7 @@
</div>
</div>
</v-card-text>
</v-card>
<!-- 编辑对话标题对话框 -->
</v-card> <!-- 编辑对话标题对话框 -->
<v-dialog v-model="editTitleDialog" max-width="400">
<v-card>
<v-card-title class="dialog-title">{{ tm('actions.editTitle') }}</v-card-title>
@@ -281,7 +279,49 @@
<v-btn text @click="saveTitle" color="primary">{{ t('core.common.save') }}</v-btn>
</v-card-actions>
</v-card>
</v-dialog> <!-- 连接冲突提示对话框 -->
<v-dialog v-model="connectionConflictDialog" max-width="500" persistent>
<v-card>
<v-card-title class="dialog-title d-flex align-center">
<v-icon color="info" class="mr-2">mdi-information-outline</v-icon>
{{ tm('connection.title') }}
</v-card-title>
<v-card-text>
<div class="text-body-1 mb-3">
{{ tm('connection.message') }}
</div>
<div class="text-body-2 text-medium-emphasis mb-3">
{{ tm('connection.reasons') }}
<ul class="mt-2">
<li>{{ tm('connection.reasonWindowResize') }}</li>
<li>{{ tm('connection.reasonMultipleTabs') }}</li>
<li>{{ tm('connection.reasonNetworkIssue') }}</li>
</ul>
</div>
<div class="text-body-2 text-medium-emphasis">
<strong>{{ tm('connection.notice') }}</strong>
</div>
</v-card-text>
<v-card-actions>
<v-spacer></v-spacer>
<v-btn text @click="connectionConflictDialog = false" color="primary">{{ tm('connection.understand') }}</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
<!-- 连接状态消息提示 -->
<v-snackbar
v-model="connectionStatusSnackbar"
:color="connectionStatusColor"
:timeout="4000"
location="top"
>
<v-icon class="mr-2">
{{ connectionStatusColor === 'success' ? 'mdi-check-circle' :
connectionStatusColor === 'warning' ? 'mdi-alert-circle' : 'mdi-information' }}
</v-icon>
{{ connectionStatusMessage }}
</v-snackbar>
</template>
<script>
@@ -343,7 +383,8 @@ export default {
eventSourceReader: null,
sseReconnecting: false, // 添加重连状态标志
// Ctrl键长按相关变量
// // Ctrl键长按相关变量
ctrlKeyDown: false,
ctrlKeyTimer: null,
ctrlKeyLongPressThreshold: 300, // 长按阈值,单位毫秒
@@ -360,9 +401,11 @@ export default {
sidebarHovered: false,
sidebarHoverTimer: null,
sidebarHoverExpanded: false,
sidebarHoverDelay: 100, // 悬停延迟,单位毫秒
pendingCid: null, // Store pending conversation ID for route handling
sidebarHoverDelay: 100, // 悬停延迟,单位毫秒 pendingCid: null, // Store pending conversation ID for route handling // 连接状态提示相关
connectionConflictDialog: false,
connectionStatusSnackbar: false,
connectionStatusMessage: '',
connectionStatusColor: 'info',
}
},
@@ -382,9 +425,7 @@ export default {
'$route': {
immediate: true,
handler(to, from) {
console.log('Route changed:', to.path, 'from:', from?.path);
// 如果是从不同的路由模式切换(chat <-> chatbox),重新建立SSE连接
console.log('Route changed:', to.path, 'from:', from?.path); // 如果是从不同的路由模式切换(chat <-> chatbox),重新建立SSE连接
if (from &&
((from.path.startsWith('/chat') && to.path.startsWith('/chatbox')) ||
(from.path.startsWith('/chatbox') && to.path.startsWith('/chat')))) {
@@ -468,9 +509,20 @@ export default {
// Cleanup blob URLs
this.cleanupMediaCache();
},
},
methods: {
// 显示连接冲突对话框
showConnectionConflictDialog() {
this.connectionConflictDialog = true;
},
// 显示连接状态消息
showConnectionStatus(message, color = 'info') {
this.connectionStatusMessage = message;
this.connectionStatusColor = color;
this.connectionStatusSnackbar = true;
},
toggleTheme() {
const customizer = useCustomizerStore();
const newTheme = customizer.uiTheme === 'PurpleTheme' ? 'PurpleThemeDark' : 'PurpleTheme';
@@ -635,16 +687,18 @@ export default {
}
const reader = response.body.getReader();
const decoder = new TextDecoder();
const decoder = new TextDecoder();
this.eventSource = reader;
this.eventSourceReader = reader;
this.sseReconnecting = false;
let in_streaming = false;
let message_obj = null;
let message_obj = null;
console.log('SSE连接已建立');
// 显示连接成功状态
if (retryCount > 0) {
this.showConnectionStatus(this.tm('connection.status.reconnected'), 'success');
}
while (true) {
try {
@@ -668,14 +722,13 @@ export default {
continue;
}
console.log(line);
// 处理后端错误响应格式
console.log(line); // 处理后端错误响应格式
if (line.startsWith('{"status":"error"')) {
try {
const errorObj = JSON.parse(line);
if (errorObj.message === 'Already connected') {
console.log('检测到连接冲突等待后重试...');
console.log('检测到连接冲突显示提示对话框...');
this.showConnectionConflictDialog();
throw new Error('CONNECTION_CONFLICT');
}
console.error('后端错误:', errorObj.message);
@@ -781,16 +834,17 @@ export default {
} catch (error) {
console.error(`SSE连接错误 (尝试 ${retryCount + 1}):`, error);
retryCount++;
retryCount++;
if (error.message === 'CONNECTION_CONFLICT' && retryCount < maxRetries) {
console.log(`连接冲突,等待 ${2000 * retryCount}ms 后重试...`);
this.showConnectionStatus(`${this.tm('connection.status.reconnecting')} (${retryCount}/${maxRetries})`, 'warning');
await new Promise(resolve => setTimeout(resolve, 2000 * retryCount));
continue;
}
if (retryCount >= maxRetries) {
console.error('SSE连接重试次数已达上限');
this.showConnectionStatus(this.tm('connection.status.failed'), 'error');
this.sseReconnecting = false;
break;
}
@@ -1097,10 +1151,11 @@ export default {
const container = this.$refs.messageContainer;
container.scrollTop = container.scrollHeight;
});
},
},
handleInputKeyDown(e) {
if (e.keyCode === 17) { // Ctrl键
if (e.ctrlKey && e.keyCode === 65) { // Ctrl+A组合
e.preventDefault(); // 防止默认的全选行为
// 防止重复触发
if (this.ctrlKeyDown) return;
@@ -1113,10 +1168,8 @@ export default {
}
}, this.ctrlKeyLongPressThreshold);
}
},
handleInputKeyUp(e) {
if (e.keyCode === 17) { // Ctrl键
}, handleInputKeyUp(e) {
if (e.keyCode === 65) { // A键释放
this.ctrlKeyDown = false;
// 清除定时器