diff --git a/src/components/chat/MessageList.vue b/src/components/chat/MessageList.vue
index 040534f..28572ae 100644
--- a/src/components/chat/MessageList.vue
+++ b/src/components/chat/MessageList.vue
@@ -229,7 +229,10 @@ defineExpose({
});
onMounted(() => {
- scrollToBottom(false);
+ // 只有当有消息时才滚动到底部,否则保持在顶部显示欢迎界面
+ if (visibleMessages.value.length > 0) {
+ scrollToBottom(false);
+ }
});
diff --git a/src/components/input/ChatInput.vue b/src/components/input/ChatInput.vue
index d0d4d8f..48e28be 100644
--- a/src/components/input/ChatInput.vue
+++ b/src/components/input/ChatInput.vue
@@ -54,6 +54,7 @@
v-model="inputText"
:placeholder="placeholder"
:rows="1"
+ @beforeinput="handleBeforeInput"
@input="autoResize"
@focus="isFocused = true"
@blur="isFocused = false"
@@ -90,6 +91,11 @@
@@ -190,7 +179,7 @@ const props = withDefaults(
placeholder: "输入你的问题...",
isStreaming: false,
sendOnEnter: false,
- maxChars: 4000,
+ maxChars: 10,
disabled: false,
// 默认全部支持
supports_thinking: true,
@@ -231,6 +220,18 @@ const textareaRef = ref(null);
const fileInputRef = ref(null);
const imageInputRef = ref(null);
+// toast 节流
+let lastToastTime = 0;
+const toastThrottleMs = 2000;
+
+function showThrottledToast(message: string, type: 'error' = 'error') {
+ const now = Date.now();
+ if (now - lastToastTime >= toastThrottleMs) {
+ lastToastTime = now;
+ window.$toast?.(message, type);
+ }
+}
+
// 计算属性
const charCount = computed(() => inputText.value.length);
const isUploading = computed(() => attachments.value.some((a) => a.uploading));
@@ -254,6 +255,27 @@ function autoResize() {
textarea.style.height = `${Math.min(textarea.scrollHeight, maxHeight)+1}px`;
}
+// 处理输入前事件,限制字数
+function handleBeforeInput(event: InputEvent) {
+ // 如果不是插入文本的操作(如删除、退格等),允许
+ if (!event.data) return;
+
+ // 检查输入后是否会超过限制
+ const currentLength = inputText.value.length;
+ const insertLength = event.data?.length || 0;
+ const selectionStart = (event.target as HTMLTextAreaElement).selectionStart || 0;
+ const selectionEnd = (event.target as HTMLTextAreaElement).selectionEnd || 0;
+ const selectedLength = selectionEnd - selectionStart;
+
+ // 计算输入后的长度
+ const newLength = currentLength - selectedLength + insertLength;
+
+ if (newLength > props.maxChars) {
+ event.preventDefault();
+ showThrottledToast(`已超${props.maxChars}字上限,请删除部分内容`);
+ }
+}
+
// 处理键盘事件
function handleKeydown(event: KeyboardEvent) {
// Ctrl+Enter 或 Cmd+Enter 发送
@@ -298,6 +320,22 @@ async function handlePaste(event: ClipboardEvent) {
const items = event.clipboardData?.items;
if (!items) return;
+ // 检查粘贴文本是否会超过字数限制
+ const text = event.clipboardData?.getData('text');
+ if (text) {
+ const textarea = event.target as HTMLTextAreaElement;
+ const selectionStart = textarea.selectionStart || 0;
+ const selectionEnd = textarea.selectionEnd || 0;
+ const selectedLength = selectionEnd - selectionStart;
+ const newLength = inputText.value.length - selectedLength + text.length;
+
+ if (newLength > props.maxChars) {
+ event.preventDefault();
+ showThrottledToast(`已超${props.maxChars}字上限,请删除部分内容`);
+ return;
+ }
+ }
+
for (const item of items) {
if (item.type.startsWith("image/")) {
event.preventDefault();
@@ -718,26 +756,6 @@ onMounted(() => {
}
}
-.toolbar-right {
- display: flex;
- align-items: center;
- gap: 16px;
-}
-
-.char-count {
- font-size: 12px;
- color: #9ca3af;
-
- &.warning {
- color: #f59e0b;
- }
-}
-
-.send-hint {
- font-size: 12px;
- color: #9ca3af;
-}
-
@keyframes pulse {
0%,
100% {
diff --git a/src/components/sidebar/ChatSidebar.vue b/src/components/sidebar/ChatSidebar.vue
index c83f3b9..ab5cf35 100644
--- a/src/components/sidebar/ChatSidebar.vue
+++ b/src/components/sidebar/ChatSidebar.vue
@@ -279,7 +279,6 @@ if (typeof window !== "undefined") {
position: relative;
height: 100vh;
background: #ffffff;
- border-right: 1px solid #e2e8f0;
transition: width 0.3s ease;
overflow: hidden;
flex-shrink: 0;
@@ -690,8 +689,6 @@ if (typeof window !== "undefined") {
cursor: col-resize;
z-index: 10;
- &:hover {
- background: rgba(59, 130, 246, 0.3);
- }
+
}