123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362 |
- <template>
- <div @click.stop="" class="fixed bottom-0 left-0 w-full bg-[#fff] pt-10 pb-30 z-52">
- <div :class="`flex ${inputValue.length > 13 ? 'items-end' : 'items-center'} px-15 pb-10`">
- <!-- @click="showVoice = !showVoice" -->
- <span
- v-if="!showVoice"
- :class="`iconfont icon-voice-one text-black-6 `"
- style="font-size: 32px"
- ></span>
- <van-icon @click="showVoice = !showVoice" v-else name="volume" size="22" />
- <div
- v-if="showVoice"
- @mousedown="startRecording"
- @mouseup="stopRecording"
- @touchstart="startRecording"
- @touchend="stopRecording"
- :class="`relative rounded-full bg-white justify-center flex-1 ml-12 mr-12 pl-5 pr-5 pt-4 pb-4 h-40 border flex items-center `"
- >
- {{ isRecording ? '松开结束' : '按住说话' }}
- </div>
- <div
- v-else
- style="overflow-y: scroll; -webkit-scrollbar-width: 0px; -webkit-scrollbar: none"
- class="box-border rounded-full bg-[#F3F3F3] justify-between px-16 py-8 flex-1 mx-12 min-h-40 border flex items-center"
- >
- <textarea
- v-model="inputValue"
- ref="textareaRef"
- placeholder="请输入"
- @focus="textareaFocus"
- @blur="handleBlur"
- class="ml-8 flex-1 box-border w-full h-full bg-[#F3F3F3]"
- maxlength="5000"
- style="height: 100%; resize: none; border: none; outline: none"
- ></textarea>
- <!-- @keydown.enter="addComment" -->
- </div>
- <span
- @click="openEmoji"
- class="iconfont icon-slightly-smiling-face text-black-6 mr-12"
- style="font-size: 32px"
- ></span>
- <span
- @click="openOther"
- class="iconfont icon-close-one text-black-6"
- style="font-size: 32px"
- ></span>
- </div>
- <div v-if="showEmoji" class="w-full h-300 bg-[#fff] overflow-auto">
- <div @click="closeEmojiBox" class="flex justify-end pr-15 text-black-9 text-sm">收起表情</div>
- <div class="flex items-center flex-wrap w-full px-8">
- <div
- v-for="(item, index) in emojiJson"
- :key="index"
- class="active:bg-[#ddd] text-4xl w-[10%] aspect-[1/1] flex items-center justify-center"
- >
- <div @click="selectEmoji(index)" v-html="item.emoji"></div>
- </div>
- </div>
- <div class="fixed bottom-45 right-20 flex">
- <div
- @click="delteMessage"
- class="text-sm py-5 px-16 mr-12 bg-[#FD9A00] text-[#fff] flex items-center justify-center rounded-full shrink-0"
- >
- 删除
- </div>
- <div
- @click="addComment"
- class="text-sm py-5 px-16 mr-12 bg-[#FD9A00] text-[#fff] flex items-center justify-center rounded-full shrink-0"
- >
- 发送
- </div>
- </div>
- </div>
- <div v-if="showOther" class="w-full h-200 bg-[#fff] overflow-auto">
- <div class="flex items-start flex-wrap w-full h-full py-15 px-8 bg-white">
- <template v-for="(item, index) in otherList" :key="index">
- <div
- @click="index == 0 ? () => {} : item.fn"
- v-if="item?.isShow"
- class="mx-10 text-4xl relative active:text-[#FF9300] w-[15%] aspect-[1/1] flex flex-wrap items-center justify-center"
- >
- <div
- class="w-54 h-54 active:bg-[#FF9300]/[0.1] bg-[#F3F3F3] shrink-0 rounded-full mb-5 overflow-auto flex justify-center items-center"
- >
- <span
- :class="`iconfont ${item.icon} text-black-6 active:text-[#FF9300] `"
- style="font-size: 32px"
- ></span>
- </div>
- <p class="text-sm w-full text-center">{{ item.title }}</p>
- <input
- type="file"
- @change="item.fn"
- class="absolute top-0 left-0 w-full h-full"
- style="position: absolute; top: 0; left: 0; z-index: 10; opacity: 0"
- />
- </div>
- </template>
- </div>
- </div>
- </div>
- </template>
- <script setup>
- import emojiJson from './emoji.js'
- const shareGroup = defineModel('shareGroup')
- const emit = defineEmits(['onSendMessage', 'onSelectImg'])
- const textareaRef = ref(null)
- // 显示输入框
- // const showInput = ref(true)
- // 是否展示表情
- const showEmoji = ref(true)
- // 是否展示其他功能
- const showOther = ref(false)
- // 是否展示语音
- const showVoice = ref(false)
- // 是否在录音
- const isRecording = ref(false)
- const transcript = ref('') // 存储语音识别结果
- // 输入的内容
- const inputValue = ref('')
- const addContent = ref(true)
- // 记录光标位置
- const cursorIndex = ref(0)
- // 创建语音识别实例
- let recognition = null
- // if ('SpeechRecognition' in window || 'webkitSpeechRecognition' in window) {
- // recognition = new (window.SpeechRecognition || window.webkitSpeechRecognition)()
- // recognition.continuous = true // 连续识别
- // recognition.interimResults = true // 显示中间结果
- // recognition.lang = 'zh-CN' // 设置识别语言(中文)
- // // 监听识别结果
- // recognition.onresult = (event) => {
- // const lastResult = event.results[event.resultIndex]
- // if (lastResult.isFinal) {
- // transcript.value = lastResult[0].transcript // 获取最终结果
- // } else {
- // transcript.value = lastResult[0].transcript // 获取中间结果
- // }
- // }
- // // 错误处理
- // recognition.onerror = (event) => {
- // console.error('语音识别出错:', event.error)
- // isRecording.value = false
- // }
- // // 结束时重置状态
- // recognition.onend = () => {
- // isRecording.value = false
- // }
- // }
- // 启动录音
- const startRecording = () => {
- if (recognition) {
- // recognition?.start()
- isRecording.value = true
- }
- }
- // 停止录音
- const stopRecording = () => {
- if (recognition) {
- // recognition?.stop()
- isRecording.value = false
- }
- }
- const { open, onChange } = useFileDialog({
- accept: '.png,.png,.jpeg,.JPG,Png '
- })
- // 上传相册
- const uploadPictures = () => {
- open()
- }
- onChange(async (files) => {
- if (!files.length) return
- console.log(files[0])
- const formData = new FormData()
- formData.append('uploadFile', files[0])
- formData.append('asImage', true)
- formData.append('fieldName', 'messageContent')
- const maxSize = 10 * 1024 * 1024 // 10 MB
- if (files[0].size > maxSize) {
- showToast('上传图片过大,请重新上传')
- return
- } else {
- try {
- showLoadingToast({
- message: '图片上传中...',
- duration: 1000000
- })
- const { data } = await request('/website/tourMessage/upload', {
- method: 'post',
- body: formData
- })
- // form.image.push(data.fileUrl)
- closeToast()
- showToast('图片上传成功')
- emit('onSelectImg', data.fileUrl)
- } catch (error) {
- // form.image.push({
- // url: files[0].name,
- // status: 'failed',
- // isImage: true,
- // message: '上传失败',
- // imageFit: 'contain'
- // })
- closeToast()
- showToast('图片上传失败')
- console.log('图片上传失败')
- }
- }
- })
- // 分享群聊
- const shareGroupChat = () => {
- console.log('分享群聊')
- }
- // 按住说话 松开结束
- const changeShowVoiceing = () => {}
- const otherList = reactive([
- {
- title: '相册',
- icon: 'icon-pic',
- isShow: true,
- // fn: emit('onSelectImg')
- fn: uploadPictures
- },
- {
- title: '分享群聊',
- icon: 'icon-peoples-two',
- isShow: shareGroup.value,
- fn: shareGroupChat
- }
- ])
- // 转换评论中的一些非字符emoji
- // function coveredContent(val) {
- // if (!val) return ''
- // return val.replace(/\[.*?]/g, function (str) {
- // const baseApi = import.meta.env.VITE_APP_EMOJI_API
- // const emojiName = emojiJson[str]
- // console.log(emojiName, 'emojiName')
- // if (!emojiName) return str
- // return `<img src=${baseApi}${emojiJson[str]} style="width: 20px; height: 20px;display: inline-block; vertical-align: middle"/>`
- // })
- // }
- function addComment() {
- if (!inputValue.value.trim()) {
- showToast('发送内容不能为空!')
- return
- }
- emit('onSendMessage', inputValue.value)
- inputValue.value = ''
- }
- // 监听输入框的enter事件
- function addEventListenerTextarea() {
- nextTick(() => {
- if (textareaRef.value) {
- textareaRef.value.removeEventListener('keydown', () => {})
- textareaRef.value.addEventListener('keydown', function (event) {
- if (event.key === 'Enter') {
- event.preventDefault()
- emit('onSendMessage', inputValue.value)
- inputValue.value = ''
- }
- })
- }
- })
- }
- // 获取焦点
- function textareaFocus() {
- showEmoji.value = false
- // showInput.value = true
- textareaRef.value?.focus()
- }
- // 文本域失焦
- function handleBlur() {
- showEmoji.value = false
- showOther.value = false
- textareaRef.value?.blur()
- }
- // 打开表情
- function openEmoji() {
- showOther.value = false
- nextTick(() => {
- textareaRef.value.selectionStart && (cursorIndex.value = textareaRef.value.selectionStart)
- showEmoji.value = true
- })
- }
- // 打开上传图片或者其他的
- function openOther() {
- showEmoji.value = false
- showOther.value = true
- }
- // 收起表情
- function closeEmojiBox() {
- showEmoji.value = false
- nextTick(() => {
- textareaRef.value.focus()
- })
- }
- // 选择表情
- function selectEmoji(emojiStr = '') {
- const length = emojiStr.length
- inputValue.value =
- inputValue.value.slice(0, cursorIndex.value) +
- emojiJson[emojiStr].emoji +
- inputValue.value.slice(cursorIndex.value)
- nextTick(() => {
- cursorIndex.value += length
- textareaRef.value.setSelectionRange(cursorIndex.value, cursorIndex.value)
- })
- }
- // 删除内容
- const delteMessage = () => {
- inputValue.value = inputValue.value.slice(1, inputValue.value.length)
- }
- watch(() => {
- addEventListenerTextarea()
- })
- </script>
- <style lang="scss" scoped>
- .no-scrollbar::-webkit-scrollbar {
- width: 0;
- }
- </style>
|