diff --git a/src/actions/CommonActions.js b/src/actions/CommonActions.js index 3ec4543..3773d0d 100644 --- a/src/actions/CommonActions.js +++ b/src/actions/CommonActions.js @@ -1,4 +1,4 @@ -import { fetchJSON } from '@/utils/request'; +import { fetchJSON, postForm, } from '@/utils/request'; import { API_HOST } from '@/config'; /** @@ -8,3 +8,14 @@ export const fetchSalesAgent = async (q) => { const { errcode, result } = await fetchJSON(`https://p9axztuwd7x8a7.mycht.cn/service-Analyse2/GetOperatorInfo`, { q }); return errcode !== 0 ? [] : result.map((ele) => ({ ...ele, label: ele.cn_name, value: ele.op_id })); }; + +/** + * 上传单个文件 + * @returns {object} { errcode, result: { file_url } } + */ +export const postUploadFileItem = async (fileObj, rename) => { + const formData = new FormData(); + formData.append('file', fileObj, rename); + const { errcode, result } = await postForm(`${API_HOST}/WAFileUpload`, formData); + return errcode !== 0 ? {} : result; +}; diff --git a/src/views/Conversations/Components/InputComposer.jsx b/src/views/Conversations/Components/InputComposer.jsx index 390eb17..1da686f 100644 --- a/src/views/Conversations/Components/InputComposer.jsx +++ b/src/views/Conversations/Components/InputComposer.jsx @@ -1,5 +1,5 @@ import React, { useState, useRef, useEffect } from 'react'; -import { Input, Flex, Button, Image } from 'antd'; +import { App, Input, Flex, Button, Image, } from 'antd'; // import { Input } from 'react-chat-elements'; import useAuthStore from '@/stores/AuthStore'; import useConversationStore from '@/stores/ConversationStore'; @@ -23,17 +23,36 @@ import { sentMsgTypeMapped } from '@/lib/msgUtils'; import InputTemplate from './Input/Template'; import InputEmoji from './Input/Emoji'; import InputMediaUpload from './Input/MediaUpload'; +import { postUploadFileItem } from '@/actions/CommonActions'; import dayjs from 'dayjs'; +const aliOSSHost = `https://haina-sale-system.oss-cn-shenzhen.aliyuncs.com/WAMedia/`; +/** + * image + * ext: ani;bmp;gif;ico;jpe;jpeg;jpg;pcx;png;psd;tga;tif;tiff;wmf + * + * audio + * ext: aac;ac3;aif;aifc;aiff;au;cda;dts;fla;flac;it;m1a;m2a;m3u;m4a;mid;midi;mka;mod;mp2;mp3;mpa;ogg;ra;rmi;spc;rmi;snd;umx;voc;wav;wma;xm + * + * video + * ext: 3g2;3gp;3gp2;3gpp;amr;amv;asf;avi;bdmv;bik;d2v;divx;drc;dsa;dsm;dss;dsv;evo;f4v;flc;fli;flic;flv;hdmov;ifo;ivf;m1v;m2p;m2t;m2ts;m2v;m4b;m4p;m4v;mkv;mp2v;mp4;mp4v;mpe;mpeg;mpg;mpls;mpv2;mpv4;mov;mts;ogm;ogv;pss;pva;qt;ram;ratdvd;rm;rmm;rmvb;roq;rpm;smil;smk;swf;tp;tpr;ts;vob;vp6;webm;wm;wmp;wmv + * + */ +const fileTypesExt = { + sticker: ['webp'], + photo: ['jpeg', 'jpg', 'png'], + video: ['gif', 'mp4', '3gp'], + document: ['pdf', 'doc', 'docx', 'xls', 'xlsx', 'ppt', 'pptx', 'txt', 'csv'], + audio: ['aac', 'mp4', 'm4a', 'mp3', 'amr', 'ogg'], +}; + const InputComposer = () => { const userId = useAuthStore(state => state.loginUser.userId); const websocket = useConversationStore(state => state.websocket); const websocketOpened = useConversationStore(state => state.websocketOpened); const currentConversation = useConversationStore(state => state.currentConversation); - const referenceMsg = useConversationStore(state => state.referenceMsg); - const setReferenceMsg = useConversationStore(state => state.setReferenceMsg); - const complexMsg = useConversationStore(state => state.complexMsg); - const setComplexMsg = useConversationStore(state => state.setComplexMsg); + const [referenceMsg, setReferenceMsg] = useConversationStore(state => [state.referenceMsg, state.setReferenceMsg]); + const [complexMsg, setComplexMsg] = useConversationStore(state => [state.complexMsg, state.setComplexMsg]); const sentOrReceivedNewMessage = useConversationStore(state => state.sentOrReceivedNewMessage); const talkabled = !isEmpty(currentConversation.sn) && websocketOpened; @@ -103,6 +122,69 @@ const InputComposer = () => { websocket.sendMessage({ ...contentToSend, opi_sn: userId, coli_sn: currentConversation.coli_sn }); } + + const { message } = App.useApp(); + const [pastedUploading, setPastedUploading] = useState(false); + const readPasted = async (file, rename = false) => { + // 使用 FileReader 读取文件对象 + const reader = new FileReader(); + const suffix = file.name.slice(file.name.lastIndexOf('.')+1); + const newName = rename ? `${uuid()}.${suffix}` : file.name; + const type = Object.keys(fileTypesExt).find((type) => fileTypesExt[type].includes(suffix)); + const dataUri = aliOSSHost + newName; + const msgObj = { + type: type, + name: newName, + uploadStatus: 'loading', + data: { dataUri: dataUri, link: dataUri, width: '100%', height: 150, loading: 0.01 }, + id: uuid(), + }; + // 读取完毕后获取结果 + reader.onload = (event) => { + const previewSrc = event.target.result; + msgObj.data.uri = previewSrc; + }; + file.newName = newName; + file.msgData = msgObj; + // 把文件对象作为一个 dataURL 读入 + reader.readAsDataURL(file); + return file; + }; + const handlePaste = async (event) => { + const items = (event.clipboardData || window.clipboardData).items; + let tmpfile = null; + if (!items || items.length === 0) { + // 当前浏览器不支持本地 + message.warning('当前浏览器不支持本地'); + return; + } + let isNotFile = true; + for (let i = 0; i < items.length; i++) { + // if (items[i].type.indexOf("image") !== -1) { + if (items[i].kind.indexOf("file") !== -1) { + isNotFile = false; + tmpfile = items[i].getAsFile(); + break; + } + } + if (isNotFile) { + // 普通的粘贴 + return; + } + if (!tmpfile) { + message.warning('没有读取到粘贴内容'); + return; + } + const shouldRename = tmpfile.type.indexOf('image') !== -1; + const _tmpFile = await readPasted(tmpfile, shouldRename); + setComplexMsg(_tmpFile.msgData); + setPastedUploading(true); + const { file_url } = await postUploadFileItem(tmpfile, _tmpFile.newName); + setPastedUploading(false); + setComplexMsg({..._tmpFile.msgData, uploadStatus: file_url ? 'done' : 'error'}); + return; + } + const focusInput = () => { textInputRef.current.focus({ cursor: 'end', preventScroll: true, }); }; @@ -144,17 +226,27 @@ const InputComposer = () => { {complexMsg.id && (
- {(complexMsg.type === 'photo' && complexMsg.data.uri) && } - {complexMsg.type === 'video' && } - {complexMsg.type !== 'photo' && {complexMsg.name}} - {complexMsg.status === 'loading' && } - {complexMsg.status === 'done' && } - {complexMsg.status === 'error' && <> 添加失败 } + {['photo', 'sticker'].includes(complexMsg.type) && complexMsg.data.uri ? ( + + ) : ( + <> + + {complexMsg.name} + + )} + {complexMsg.uploadStatus === 'loading' && } + {/* {complexMsg.uploadStatus === 'done' && } */} + {complexMsg.uploadStatus === 'error' && ( + <> + 添加失败{' '} + + )}