Skip to content
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 21 additions & 3 deletions docs/src/components/sender.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,9 @@ Sender 是一个功能丰富的输入组件,支持文本输入、语音识别
Sender 支持单行和多行两种输入模式,通过 `mode` 属性控制。

:::tip 单行模式自动切换
在单行模式下,当输入内容超出宽度或按 `Shift+Enter` 时,会自动切换为多行模式。
在单行模式下,当输入内容超出宽度时,会自动切换为多行模式。

当 `submitType="enter"` 时,按 `Ctrl+Enter` 或 `Shift+Enter` 也会自动切换为多行模式并换行。
:::

<demo vue="../../demos/sender/Mode.vue" title="输入模式" description="支持单行和多行模式,单行模式可自动切换为多行。" />
Expand Down Expand Up @@ -144,17 +146,33 @@ const filteredSuggestions = computed(() => {

<demo vue="../../demos/sender/ShortcutSubmit.vue" title="提交方式" description="支持三种提交快捷键,适应不同使用场景。" />

:::info 提交与换行说明
- **submitType="enter"**:按 `Enter` 提交,按 `Ctrl+Enter` 或 `Shift+Enter` 换行
- **submitType="ctrlEnter"**:按 `Ctrl+Enter` 提交,按 `Enter` 换行
- **submitType="shiftEnter"**:按 `Shift+Enter` 提交,按 `Enter` 换行

在单行模式下使用换行快捷键时,会自动切换为多行模式。
:::

### 快捷键参考

| 快捷键 | 功能 | 适用条件 |
| ----------- | ------------------------- | ------------------------------ |
| Enter | 提交内容 / 选中联想项 | submitType="enter" / 联想开启时 |
| Ctrl+Enter | 提交内容 | submitType="ctrlEnter" |
| Shift+Enter | 提交内容 | submitType="shiftEnter" |
| Ctrl+Enter | 提交内容 / 换行 | submitType="ctrlEnter" / submitType="enter" |
| Shift+Enter | 提交内容 / 换行 | submitType="shiftEnter" / submitType="enter" |
| Tab | 选中联想项 | 联想开启时 |
| Esc | 取消语音/关闭联想 | 对应功能激活时 |
| ↑ / ↓ | 导航联想项 | 联想开启时 |

:::tip 换行快捷键说明
**当 `submitType="enter"` 时**,支持以下换行方式:
- **Ctrl+Enter**:插入换行符(单行模式会自动切换为多行模式)
- **Shift+Enter**:插入换行符(单行模式会自动切换为多行模式)

**当 `submitType="ctrlEnter"` 或 `submitType="shiftEnter"` 时**,单独按 `Enter` 键即可换行。
:::

:::warning 自定义选中按键
通过 `activeSuggestionKeys` 可自定义选中联想项的按键,但请勿使用纯修饰键(Ctrl/Shift/Alt/Meta),避免劫持常用快捷键。
:::
Expand Down
75 changes: 50 additions & 25 deletions packages/components/src/sender/composables/useKeyboardHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,29 +81,61 @@ export function useKeyboardHandler(
}

/**
* 处理键盘按下事件
* 在光标位置插入换行符
* @param target 输入框元素
*/
const handleKeyPress = (event: KeyboardEvent) => {
if (isComposing.value) return // 阻止输入法状态下的提交
const insertNewLine = (target: HTMLTextAreaElement) => {
const start = target.selectionStart ?? 0
const end = target.selectionEnd ?? start
const currentValue = inputValue.value

// 使用选区范围插入换行符,保持与原生 textarea 行为一致(替换选中文本)
inputValue.value = currentValue.substring(0, start) + '\n' + currentValue.substring(end)

// 设置光标位置到换行符之后,并滚动到光标位置
setTimeout(() => {
const newCursorPos = start + 1
target.selectionStart = target.selectionEnd = newCursorPos
// 滚动到光标所在位置,确保光标可见
target.scrollTop = target.scrollHeight
}, 0)
}

// 处理 Shift+Enter - 单行模式切换到多行模式并添加换行
if (event.key === 'Enter' && event.shiftKey && currentMode?.value === 'single' && setMultipleMode) {
/**
* 处理换行操作(仅在 submitType='enter' 时生效)
* @param event 键盘事件
* @returns 是否已处理换行
*/
const handleNewLine = (event: KeyboardEvent): boolean => {
// 只在 submitType='enter' 时支持 Ctrl+Enter 和 Shift+Enter 换行
if (props.submitType !== 'enter' || event.key !== 'Enter') return false

const isCtrlEnter = event.ctrlKey && !event.shiftKey
const isShiftEnter = event.shiftKey && !event.ctrlKey

// Ctrl+Enter 或 Shift+Enter: 单行模式切换到多行,多行模式直接换行
if (isCtrlEnter || isShiftEnter) {
event.preventDefault()
// 首先切换到多行模式
setMultipleMode()
// 然后在当前输入内容的光标位置添加换行符
const target = event.target as HTMLTextAreaElement
const cursorPosition = target.selectionStart
const currentValue = inputValue.value

// 在光标位置插入换行符
inputValue.value = currentValue.substring(0, cursorPosition) + '\n' + currentValue.substring(cursorPosition)
if (currentMode?.value === 'single' && setMultipleMode) {
setMultipleMode()
}
insertNewLine(target)
return true
}

// 设置光标位置到换行符之后
setTimeout(() => {
target.selectionStart = target.selectionEnd = cursorPosition + 1
}, 0)
return false
}

/**
* 处理键盘按下事件
*/
const handleKeyPress = (event: KeyboardEvent) => {
if (isComposing.value) return // 阻止输入法状态下的提交

// 优先处理换行操作
if (handleNewLine(event)) {
return
}

Expand Down Expand Up @@ -148,16 +180,9 @@ export function useKeyboardHandler(
}

// 检查是否匹配当前的提交快捷键
const shouldSubmit = checkSubmitShortcut(event, props.submitType as SubmitTrigger)

if (shouldSubmit) {
// 只要是提交快捷键,就阻止默认行为(比如换行)
if (checkSubmitShortcut(event, props.submitType as SubmitTrigger) && canSubmit.value) {
event.preventDefault()

// 如果验证通过,则执行提交
if (canSubmit.value) {
triggerSubmit()
}
triggerSubmit()
}
}

Expand Down