154 lines
3.0 KiB
Vue
154 lines
3.0 KiB
Vue
<script setup lang="ts">
|
||
import { nextTick, ref } from 'vue'
|
||
import { ElButton, ElMessage } from 'element-plus'
|
||
|
||
const MAX_FILE_SIZE = 1024 * 1024 * 1024
|
||
|
||
const content = ref('')
|
||
|
||
const emit = defineEmits<{
|
||
send: [content: string]
|
||
sendFile: [file: File]
|
||
}>()
|
||
|
||
async function doSend(): Promise<void> {
|
||
const text = content.value.trim()
|
||
if (!text) return
|
||
emit('send', text)
|
||
content.value = ''
|
||
await nextTick()
|
||
}
|
||
|
||
function onKeydown(event: KeyboardEvent): void {
|
||
if (event.key === 'Enter' && !event.shiftKey) {
|
||
event.preventDefault()
|
||
void doSend()
|
||
}
|
||
}
|
||
|
||
function onFileChange(event: Event): void {
|
||
const target = event.target
|
||
if (!(target instanceof HTMLInputElement)) return
|
||
const file = target.files?.[0]
|
||
target.value = ''
|
||
if (!file) return
|
||
if (file.size > MAX_FILE_SIZE) {
|
||
ElMessage.warning('文件大小不能超过1G')
|
||
return
|
||
}
|
||
emit('sendFile', file)
|
||
}
|
||
</script>
|
||
|
||
<template>
|
||
<div class="chat-input">
|
||
<textarea
|
||
v-model="content"
|
||
class="chat-input__textarea"
|
||
placeholder="输入消息,按Enter发送,Shift+Enter换行..."
|
||
rows="3"
|
||
@keydown="onKeydown"
|
||
/>
|
||
<div class="chat-input__bar">
|
||
<span class="chat-input__hint">
|
||
按 Enter 发送 | 支持拖拽上传文件
|
||
</span>
|
||
<div class="chat-input__actions">
|
||
<input
|
||
type="file"
|
||
class="chat-input__file"
|
||
@change="onFileChange"
|
||
>
|
||
<el-button
|
||
class="chat-input__send"
|
||
@click="doSend"
|
||
>
|
||
发送
|
||
</el-button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</template>
|
||
|
||
<style lang="less" scoped>
|
||
.chat-input {
|
||
.flex(column, flex-start, stretch);
|
||
gap: 0;
|
||
padding: 16px 22px 20px;
|
||
border-top: 1px solid @service-card-line;
|
||
background: @white;
|
||
box-sizing: border-box;
|
||
|
||
&__textarea {
|
||
width: 100%;
|
||
height: 72px;
|
||
padding: 12px 14px;
|
||
border: none;
|
||
border-radius: @radius-sm;
|
||
background: @service-input-bg;
|
||
color: @service-bubble-text;
|
||
font-size: 13px;
|
||
line-height: 1.6;
|
||
resize: none;
|
||
outline: none;
|
||
transition: background 0.2s;
|
||
box-sizing: border-box;
|
||
|
||
&:focus {
|
||
border: none;
|
||
background: @service-input-bg;
|
||
}
|
||
|
||
&::placeholder {
|
||
color: @project-muted;
|
||
}
|
||
}
|
||
|
||
&__bar {
|
||
.flex(row, space-between, center);
|
||
gap: @spacing-base;
|
||
width: 100%;
|
||
margin-top: 0;
|
||
padding-top: 14px;
|
||
background: @service-bar-bg;
|
||
}
|
||
|
||
&__hint {
|
||
font-size: 11px;
|
||
line-height: 16px;
|
||
color: @project-muted;
|
||
}
|
||
|
||
&__actions {
|
||
.flex(row, flex-end, center);
|
||
gap: @spacing-sm;
|
||
}
|
||
|
||
&__attach {
|
||
display: none;
|
||
}
|
||
|
||
&__file {
|
||
display: none;
|
||
}
|
||
|
||
&__send {
|
||
width: 76px;
|
||
height: 30px;
|
||
padding: 0;
|
||
border-color: @primary-strong;
|
||
border-radius: @radius-sm;
|
||
background: @primary-strong;
|
||
color: @white;
|
||
font-size: 12px;
|
||
font-weight: 500;
|
||
|
||
&:hover {
|
||
border-color: @primary-strong;
|
||
background: @primary-strong;
|
||
color: @white;
|
||
}
|
||
}
|
||
}
|
||
</style>
|