208 lines
3.9 KiB
Vue

<template>
<div class="message-input-container">
<div class="input-wrapper">
<n-input
v-model:value="messageText"
type="textarea"
:placeholder="placeholder"
:autosize="{ minRows: 1, maxRows: 4 }"
:bordered="false"
class="message-input"
@keydown="handleKeyDown"
@input="handleInput"
/>
<div class="input-actions">
<n-button
type="primary"
size="medium"
:disabled="!canSend"
class="send-button"
@click="handleSend"
>
发送
</n-button>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { ref, computed } from 'vue'
import { NInput, NButton } from 'naive-ui'
// Props
interface Props {
placeholder?: string
maxLength?: number
disabled?: boolean
}
const props = withDefaults(defineProps<Props>(), {
placeholder: '请输入消息内容...',
maxLength: 500,
disabled: false
})
// Emits
const emit = defineEmits<{
send: [message: string]
input: [value: string]
}>()
// 响应式数据
const messageText = ref('')
// 计算属性
const canSend = computed(() => {
return messageText.value.trim().length > 0 && !props.disabled
})
// 方法
const handleSend = () => {
if (canSend.value) {
emit('send', messageText.value.trim())
messageText.value = ''
}
}
const handleInput = (value: string) => {
emit('input', value)
}
const handleKeyDown = (event: KeyboardEvent) => {
// Ctrl/Cmd + Enter 发送消息
if ((event.ctrlKey || event.metaKey) && event.key === 'Enter') {
event.preventDefault()
handleSend()
}
}
// 暴露方法供父组件调用
defineExpose({
focus: () => {
// 可以在这里添加聚焦逻辑
},
clear: () => {
messageText.value = ''
}
})
</script>
<style scoped>
.message-input-container {
position: sticky;
bottom: 0;
left: 0;
right: 0;
background: #ffffff;
border-top: 1px solid #e8e8e8;
padding: 16px 20px;
z-index: 100;
}
.input-wrapper {
display: flex;
align-items: center;
gap: 12px;
max-width: 1200px;
margin: 0 auto;
}
.message-input {
flex: 1;
background: #f8f9fa;
border-radius: 12px;
padding: 12px 16px;
min-height: 44px;
font-size: 14px;
line-height: 1.5;
transition: all 0.2s ease;
}
.message-input:hover {
background: #f0f2f5;
}
.message-input:focus-within {
background: #ffffff;
box-shadow: 0 0 0 2px rgba(24, 144, 255, 0.2);
}
.input-actions {
display: flex;
align-items: center;
gap: 8px;
}
.send-button {
height: 44px;
padding: 0 24px;
border-radius: 12px;
font-weight: 500;
font-size: 14px;
white-space: nowrap;
transition: all 0.2s ease;
}
.send-button:not(:disabled):hover {
transform: translateY(-1px);
box-shadow: 0 4px 12px rgba(24, 144, 255, 0.3);
}
/* 自定义Naive UI样式 */
:deep(.n-input .n-input__textarea) {
background: transparent !important;
border: none !important;
padding: 0 !important;
font-size: 14px;
line-height: 1.5;
resize: none;
}
:deep(.n-input .n-input__textarea::placeholder) {
color: #999;
font-size: 14px;
}
:deep(.n-input.n-input--focus .n-input__textarea) {
background: transparent !important;
box-shadow: none !important;
}
:deep(.n-button.n-button--primary-type) {
background: linear-gradient(135deg, #1890ff 0%, #40a9ff 100%);
border: none;
}
:deep(.n-button.n-button--primary-type:not(.n-button--disabled):hover) {
background: linear-gradient(135deg, #40a9ff 0%, #1890ff 100%);
}
:deep(.n-button.n-button--primary-type.n-button--disabled) {
background: #d9d9d9;
color: #fff;
}
/* 响应式设计 */
@media (max-width: 768px) {
.message-input-container {
padding: 12px 16px;
}
.input-wrapper {
gap: 8px;
}
.send-button {
height: 40px;
padding: 0 16px;
font-size: 13px;
}
.message-input {
padding: 10px 14px;
min-height: 40px;
font-size: 13px;
}
}
</style>