You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
253 lines
8.9 KiB
253 lines
8.9 KiB
<!-- 公共输入框组件 --> |
|
<script setup lang="ts"> |
|
import { computed, ref } from 'vue' |
|
import { Button, Image, Textarea, Upload, message } from 'ant-design-vue' |
|
import { CloseOutlined, LoadingOutlined, PlusOutlined } from '@ant-design/icons-vue' |
|
import { SvgIcon } from '@/components/SvgIcon' |
|
import { isEmpty, trim } from '@/utils/is' |
|
import { useMessageStore } from '@/store/moules/messageStore/index' |
|
import { MessageStatusEnum } from '@/enums/messageEnum' |
|
import { uploadApi } from '@/api/base/file' |
|
|
|
const props = defineProps({ |
|
placeholder: { |
|
type: String, |
|
default: '在此输入你想了解的内容(Shift + Enter = 换行)', |
|
}, |
|
btnLoading: { |
|
type: Boolean, |
|
default: false, |
|
}, |
|
isStop: { |
|
type: Boolean, |
|
default: false, |
|
}, |
|
isUpload: { |
|
type: Boolean, |
|
default: false, |
|
}, |
|
defaultFileUrl: { |
|
type: String, |
|
default: '', |
|
}, |
|
uploadDisabled: { |
|
type: Boolean, |
|
default: false, |
|
}, |
|
}) |
|
const emit = defineEmits([ |
|
'pressEnter', |
|
'send', |
|
'stopMessage', |
|
'update:defaultFileUrl', |
|
'onUploadSuccess', |
|
'onUploadFailed', |
|
'deleteFile', |
|
]) |
|
const messageStore = useMessageStore() |
|
const value = ref('') |
|
const uploadLoading = ref<boolean>(false) |
|
const fileUrl = computed({ |
|
get: () => props.defaultFileUrl, |
|
set: value => emit('update:defaultFileUrl', value), |
|
}) |
|
|
|
const stopShow = computed(() => { |
|
if (!props.isStop) { |
|
return false |
|
} |
|
if ( |
|
messageStore.getMessageList.length |
|
&& messageStore.getMessageList[messageStore.getMessageList.length - 1].messageStatus === MessageStatusEnum.ACTICON |
|
) { |
|
return true |
|
} |
|
else { |
|
return false |
|
} |
|
}) |
|
|
|
function pressEnter(event: KeyboardEvent) { |
|
if (event.shiftKey) { |
|
return false |
|
} |
|
|
|
if ( |
|
messageStore.getMessageList.length |
|
&& messageStore.getMessageList[messageStore.getMessageList.length - 1].messageStatus !== MessageStatusEnum.END |
|
) { |
|
return false |
|
} |
|
|
|
if (isEmpty(trim(value.value))) { |
|
message.warning('请输入内容') |
|
setTimeout(() => { |
|
value.value = '' |
|
}, 0) |
|
return false |
|
} |
|
emit('pressEnter', value.value, fileUrl.value) |
|
handeleSend() |
|
} |
|
|
|
function handeleSend() { |
|
emit('send', value.value, fileUrl.value) |
|
setTimeout(() => { |
|
value.value = '' |
|
}, 0) |
|
} |
|
|
|
function stopMessage() { |
|
emit('stopMessage') |
|
} |
|
|
|
/** |
|
* @description: 图片上传前的处理 |
|
*/ |
|
function handleBeforeUpload(file: File) { |
|
const isJpgOrPng = file.type === 'image/jpeg' || file.type === 'image/png' || file.type === 'image/gif' |
|
if (!isJpgOrPng) { |
|
message.error('只能上传JPG、JPEG、PNG、GIF格式图片!') |
|
} |
|
const isLt10M = file.size / 1024 / 1024 < 10 |
|
if (!isLt10M) { |
|
message.error('图片尺寸不能超过10MB!') |
|
} |
|
return isJpgOrPng && isLt10M |
|
} |
|
|
|
/** |
|
* @description: 自定义图片上传服务器 |
|
*/ |
|
async function handleCustomRequest(option: any) { |
|
const formData = new FormData() |
|
formData.append('file', option.file) |
|
uploadLoading.value = true |
|
uploadApi(formData).then((res) => { |
|
emit('onUploadSuccess', res) |
|
uploadLoading.value = false |
|
}).catch(() => { |
|
emit('onUploadFailed') |
|
uploadLoading.value = false |
|
}) |
|
} |
|
</script> |
|
|
|
<template> |
|
<div class="app-textarea right-0 bottom-5 w-full"> |
|
<div class="relative"> |
|
<div |
|
v-if="stopShow" |
|
class="stop flex justify-center items-center cursor-pointer" |
|
@click="stopMessage" |
|
> |
|
<SvgIcon class="mr-2" name="stop"></SvgIcon> |
|
停止回答 |
|
</div> |
|
<div v-if="isUpload"> |
|
<Upload |
|
v-if="!fileUrl" |
|
:disabled="uploadDisabled" |
|
name="avatar" |
|
list-type="picture-card" |
|
class="avatar-uploader" |
|
:show-upload-list="false" |
|
:custom-request="handleCustomRequest" |
|
:before-upload="handleBeforeUpload" |
|
> |
|
<div> |
|
<LoadingOutlined v-if="uploadLoading"></LoadingOutlined> |
|
<PlusOutlined v-else></PlusOutlined> |
|
</div> |
|
</Upload> |
|
<div |
|
v-if="fileUrl" |
|
class="avatar-uploader" |
|
> |
|
<div class=" relative"> |
|
<div class="img"> |
|
<Image |
|
:width="60" |
|
:height="60" |
|
:src="fileUrl" |
|
alt="example" |
|
fallback="data:image/png;base64,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" |
|
></Image> |
|
</div> |
|
<CloseOutlined class="delete-icon" @click="$emit('deleteFile')"></CloseOutlined> |
|
</div> |
|
</div> |
|
</div> |
|
<Textarea |
|
v-model:value="value" |
|
class="pt-4 pr-34 pb-6" |
|
:class="[isUpload ? 'pl-20' : 'pl-4']" |
|
:placeholder="placeholder" |
|
:auto-size="{ minRows: 3, maxRows: 8 }" |
|
@press-enter="pressEnter" |
|
> |
|
</Textarea> |
|
<Button |
|
class="w-28 absolute right-5 bottom-3" |
|
type="primary" |
|
:loading="btnLoading" |
|
@click="handeleSend" |
|
> |
|
发送 |
|
</Button> |
|
</div> |
|
</div> |
|
</template> |
|
|
|
<style lang="scss" scoped> |
|
@include app('textarea') { |
|
.stop { |
|
width: 100px; |
|
height: 30px; |
|
border-radius: 20px; |
|
background-color: #edf3ff; |
|
position: absolute; |
|
top: -38px; |
|
left: calc(50% - 40px); |
|
z-index: 1; |
|
} |
|
|
|
.avatar-uploader { |
|
width: 60px; |
|
position: absolute; |
|
|
|
top: 10px; |
|
left: 10px; |
|
z-index: 1; |
|
:deep(.ant-upload) { |
|
width: 60px !important; |
|
height: 60px !important; |
|
} |
|
.ant-upload-select-picture-card i { |
|
font-size: 32px; |
|
color: #999; |
|
} |
|
|
|
.ant-upload-select-picture-card .ant-upload-text { |
|
color: #666; |
|
} |
|
|
|
.img { |
|
border-radius: 5px; |
|
overflow: hidden; |
|
} |
|
|
|
.delete-icon { |
|
position: absolute; |
|
top: -6px; |
|
right: -6px; |
|
color: #fff; |
|
padding: 3px; |
|
border-radius: 5px; |
|
overflow: hidden; |
|
background-color: rgba(0, 0, 0, 0.6); |
|
font-size: 12px; |
|
} |
|
} |
|
} |
|
</style>
|
|
|