10/10
1. 设备管理界面完成 2. 用户管理界面完成 3. 会话列表完成
This commit is contained in:
146
src/views/system/conversation/compontents/ChatInput.vue
Normal file
146
src/views/system/conversation/compontents/ChatInput.vue
Normal file
@@ -0,0 +1,146 @@
|
||||
<template>
|
||||
<div class="chat-input-wrap">
|
||||
<div class="input-area">
|
||||
<!-- 多行输入框 -->
|
||||
<el-input
|
||||
ref="inputRef"
|
||||
v-model="inputText"
|
||||
:autosize="{ minRows:5, maxRows:8 }"
|
||||
placeholder="请输入回复内容"
|
||||
type="textarea"
|
||||
@keydown="handleKeyDown"
|
||||
/>
|
||||
<!-- 发送按钮 -->
|
||||
<el-button
|
||||
:disabled="!canSend || sending"
|
||||
class="btn-send"
|
||||
size="large"
|
||||
type="primary"
|
||||
@click="handleSend"
|
||||
>
|
||||
{{ sending ? '发送中' : '发送' }}
|
||||
</el-button>
|
||||
</div>
|
||||
<div>
|
||||
<!-- 图片上传按钮 -->
|
||||
<el-button
|
||||
class="btn-img"
|
||||
plain
|
||||
type="info"
|
||||
@click="handleSelectImage"
|
||||
>
|
||||
上传图片
|
||||
</el-button>
|
||||
</div>
|
||||
<!-- 隐藏input用于选图 -->
|
||||
<input
|
||||
ref="fileRef"
|
||||
accept="image/*"
|
||||
style="display:none"
|
||||
type="file"
|
||||
@change="onFileChange"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import {ref, computed} from 'vue'
|
||||
import type {PropType} from 'vue'
|
||||
|
||||
const props = defineProps({
|
||||
/** 是否正在发送中(外部控制,防止重复点击) */
|
||||
sending: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
})
|
||||
|
||||
const emit = defineEmits<{
|
||||
/** 发送文本消息 */
|
||||
(e: 'send', text: string): void
|
||||
/** 选中图片文件,交给父组件上传oss */
|
||||
(e: 'uploadImage', file: File): void
|
||||
}>()
|
||||
|
||||
const inputRef = ref<InstanceType<typeof elInput>>()
|
||||
const fileRef = ref<HTMLInputElement | null>(null)
|
||||
const inputText = ref('')
|
||||
|
||||
// 是否可以发送
|
||||
const canSend = computed(() => {
|
||||
return !!inputText.value.trim()
|
||||
})
|
||||
|
||||
// 点击图片图标,唤起文件选择
|
||||
const handleSelectImage = () => {
|
||||
fileRef.value?.click()
|
||||
}
|
||||
|
||||
// 文件选择回调
|
||||
const onFileChange = (e: Event) => {
|
||||
const target = e.target as HTMLInputElement
|
||||
const file = target.files?.[0]
|
||||
if (!file) return
|
||||
emit('uploadImage', file)
|
||||
// 清空file,选同一个图片也能触发change
|
||||
target.value = ''
|
||||
}
|
||||
|
||||
// 键盘事件:回车发送,Shift+Enter换行
|
||||
const handleKeyDown = (e: KeyboardEvent) => {
|
||||
if (e.key === 'Enter') {
|
||||
if (e.shiftKey) {
|
||||
// shift+enter 原生换行,不拦截
|
||||
return
|
||||
}
|
||||
// 直接回车发送
|
||||
e.preventDefault()
|
||||
handleSend()
|
||||
}
|
||||
}
|
||||
|
||||
// 发送消息
|
||||
const handleSend = () => {
|
||||
const rawText = inputText.value.trim()
|
||||
if (!rawText) return
|
||||
// 把换行 \n 替换成 <br/>
|
||||
const htmlText = rawText.replaceAll('\n', '<br/>')
|
||||
emit('send', htmlText)
|
||||
// 发送成功后清空输入框
|
||||
inputText.value = ''
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.chat-input-wrap {
|
||||
flex-shrink: 0;
|
||||
padding: 12px;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.input-area {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
align-items: flex-end;
|
||||
|
||||
:deep(.el-textarea__inner) {
|
||||
background-color: #f7f9fc;
|
||||
}
|
||||
}
|
||||
|
||||
.btn-img {
|
||||
flex-shrink: 0;
|
||||
margin-top: 12px;
|
||||
padding: 9px 13px;
|
||||
color: rgba(75, 85, 99, 1);
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.btn-send {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
:deep(.el-textarea__inner) {
|
||||
padding: 8px 12px;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user