fix: 修复错误滚动到底部的问题。删除字数显示,添加toast提示超过字数。输入框扩大按钮移动至左侧。删除侧边栏边框。
This commit is contained in:
parent
965514b7b4
commit
8b8f77cfcc
|
|
@ -229,7 +229,10 @@ defineExpose({
|
|||
});
|
||||
|
||||
onMounted(() => {
|
||||
// 只有当有消息时才滚动到底部,否则保持在顶部显示欢迎界面
|
||||
if (visibleMessages.value.length > 0) {
|
||||
scrollToBottom(false);
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
|
|
|
|||
|
|
@ -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 @@
|
|||
<!-- 底部工具栏 -->
|
||||
<div class="input-toolbar">
|
||||
<div class="toolbar-left">
|
||||
<!-- 展开/收起 -->
|
||||
<button class="toolbar-btn" title="展开输入框" @click="toggleExpand">
|
||||
<Maximize2 v-if="!isExpanded" :size="16" />
|
||||
<Minimize2 v-else :size="16" />
|
||||
</button>
|
||||
<!-- 深度思考开关 -->
|
||||
<button
|
||||
class="toolbar-btn"
|
||||
|
|
@ -126,23 +132,6 @@
|
|||
<span>联网搜索</span>
|
||||
</button>
|
||||
|
||||
<!-- 展开/收起 -->
|
||||
<button class="toolbar-btn" title="展开输入框" @click="toggleExpand">
|
||||
<Maximize2 v-if="!isExpanded" :size="16" />
|
||||
<Minimize2 v-else :size="16" />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="toolbar-right">
|
||||
<span
|
||||
class="char-count"
|
||||
:class="{ warning: charCount > maxChars * 0.9 }"
|
||||
>
|
||||
{{ charCount }} / {{ maxChars }}
|
||||
</span>
|
||||
<span class="send-hint">
|
||||
{{ sendOnEnter ? "Enter 发送, Shift+Enter 换行" : "Ctrl+Enter 发送" }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -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<HTMLTextAreaElement | null>(null);
|
|||
const fileInputRef = ref<HTMLInputElement | null>(null);
|
||||
const imageInputRef = ref<HTMLInputElement | null>(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% {
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
Loading…
Reference in New Issue