vue3笔记(42)用户输入总结

收到了用户反馈,需要加上键盘快捷键,不能点 enter 就发送。
和大模型通信的后续版本,有语音输入的需求,问了下某代码生成很厉害的模型,这里做一个记录。

键盘快捷键

原来使用@keyup.enter="handleSubmit",这样用户只要输入 enter 就发送请求,很有可能只输入了半句话,或者需要换行,导致用户体验不好。

新增了一个函数handleMultiLineNewline作为换行处理。测试了@keydown.meta.enter,可以在 mac 系统下使用 cmd + enter/windows 系统下使用 win + enter 触发换行。@keydown.ctrl.enter@keydown.shift.enter可以触发换行。
但是以上连用不知道为啥消息又自动发出去了。。。
索性自己写了一个函数handleEnter来处理换行。

另一个需求是中文输入法下,未输入完时,使用 enter 键,默认不发送消息,整体如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
<template>
<el-input
v-model="userInput"
placeholder="请输入您的问题"
@compositionstart="onCompositionStart"
@compositionend="onCompositionEnd"
@keydown.enter="handleEnter"
type="textarea"
:rows="5"
maxlength="2048"
show-word-limit
/>
</template>
<script lang="ts" setup>
const handleEnter = async (event: KeyboardEvent) => {
if (
(event.shiftKey && event.key === 'Enter') ||
(event.ctrlKey && event.key === 'Enter') ||
(event.metaKey && event.key === 'Enter')
) {
const start = (event.target as HTMLTextAreaElement).selectionStart
const end = (event.target as HTMLTextAreaElement).selectionEnd
const value = userInput.value
userInput.value = value.slice(0, start) + '\n' + value.slice(end)
;(event.target as HTMLTextAreaElement).selectionStart = (
event.target as HTMLTextAreaElement
).selectionEnd = start + 1
event.preventDefault() // 阻止默认行为
} else if (!isComposing.value && event.key === 'Enter') {
// 发送消息
handleSubmit(event)
event.preventDefault()
}
}

// 标记输入法是否处于输入候选状态
const isComposing = ref(false)
const onCompositionStart = () => {
isComposing.value = true
}
const onCompositionEnd = () => {
isComposing.value = false
}
</script>

当用户输入为空时,不做处理;当大模型在输出时,禁止用户再次提交信息。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
const handleSubmit = async (event: any) => {
console.log('handleSubmit', event)
if (!userInput.value || userInput.value.trim().length === 0) {
return
} else if (loadingSend.value == true) {
// 防止在大模型输出时提交
event.preventDefault()
return
} else {
if (abortController.signal.aborted) {
abortController = new AbortController()
}
loadingSend.value = true
let text = userInput.value
if (text.endsWith('\n')) {
// 去掉末尾的换行符
text = text.trimEnd()
}
chatList.value.push({ role: 'user', message: text })
userInput.value = ''
scrollToBottom()
handleChat(text)
}
}

语音输入

在 vue3 项目中,语音输入试用useSpeechRecognition这个库,实现语音输入转文字功能。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
<template>
<div id="app">
<el-input
v-model="inputText"
placeholder="请输入问题"
:suffix-icon="VoiceIcon"
@click:append="startVoiceInput"
/>
<el-button @click="sendRequest">发送请求</el-button>
<el-button @click="generateImage">生成图片</el-button>
<el-card v-if="responseText" :title="responseText" style="margin-top: 20px;"></el-card>
<img v-if="imageUrl" :src="imageUrl" alt="Generated Image" style="margin-top: 20px; max-width: 100%;">
</div>
</template>

<script lang="ts" setup>
import { useSpeechRecognition } from '@vueuse/core'
import { Microphone } from '@element-plus/icons-vue'

// 语音输入
const { start, stop, result } = useSpeechRecognition({
language: 'zh-CN',
})

const inputText = ref('')
const responseText = ref('')
const imageUrl = ref('')
const VoiceIcon = Microphone

const startVoiceInput = async () => {
await start()
inputText.value = result.value
}

const sendRequest = async () => {
try {
const response = await axios.post(
'https://api.openai.com/v1/chat/completions',
{
messages: [
{
role: 'user',
content: inputText.value,
},
],
}
)
responseText.value = response.data.choices[0].message.content;
} catch (error) {
console.error('请求出错:', error)
}
}

const generateImage = async () => {
try {
const response = await axios.post(
'https://api.openai.com/v1/images/generations',
{
prompt: inputText.value,
n: 1,
size: '512x512',
}
)
imageUrl.value = response.data.data[0].url;
} catch (error) {
console.error('图片生成出错:', error)
}
}
</script>