Просмотр исходного кода

文件上传,电话等待中添加等待声音

jackson 6 месяцев назад
Родитель
Сommit
082bf35a39

+ 45 - 1
.idea/sonarlint/issuestore/index.pb

@@ -11,4 +11,48 @@ p
 B
 config/prod.env.ts,2/6/2664ea9f9e758a6438d06d5bfb2dd08244137256
 A
-config/dev.env.ts,7/6/765f6bc1a3843f5186fb65520064b9f6f854e758
+config/dev.env.ts,7/6/765f6bc1a3843f5186fb65520064b9f6f854e758
+K
+src/hooks/useSendMessage.ts,5/8/5833e149b554b5f5b59231cec3464ca76a11fdd3
+y
+Isrc/pages/conversation/chat/components/ChatFooter/useCreateFileMessage.ts,3/0/30178b2e77b21f1e226cdba404959fe0db6bb6ec
+v
+Fsrc/pages/conversation/chat/components/ChatFooter/ChatFooterAction.vue,4/e/4e14ccab76072b5ad98142a9b490043e32f86653
+z
+Jsrc/pages/conversation/chat/components/ChatFooter/useCreateNomalMessage.ts,e/d/ede8a2756a89349e01bd88578fb27c5f7f15ba2e
+r
+Bsrc/pages/conversation/chat/components/MessageItem/MessageItem.vue,2/7/27731e21b8092037a450007c205e05399ea93d8b
+{
+Ksrc/pages/conversation/chat/components/MessageItem/MediaMessageRenderer.vue,e/a/ea7e416fd060083d01bdbf89e31abcbb6776b155
+z
+Jsrc/pages/conversation/chat/components/MessageItem/TextMessageRenderer.vue,c/8/c802afd02e8bcc55f8323383eab3ddbcad781b6c
+@
+src/api/login.ts,d/5/d528c312125a5ba92c0af35729310a264b8c2026
+?
+src/api/data.ts,0/b/0b80f2764231b9b8e8b5ddb04202cc33ac79b32e
+?
+src/api/user.ts,1/e/1e9141c4ceceb897882b5879f5236bfdd21cab8c
+D
+src/utils/request.ts,4/f/4f98643a765bda61eec19aa34030baf0b5226849
+G
+src/constants/action.ts,e/2/e2f9dd78c4e007c13e2f790051a7e7215ee2da05
+K
+src/i18n/languages/en-US.ts,0/a/0a17e462a9b220a2f5b7b548398cd95d591fd237
+K
+src/i18n/languages/zh-CN.ts,0/c/0c02d3fd1b424f6a9b21a1d97581a2e0f3dd9826
+z
+Jsrc/pages/conversation/chat/components/MessageItem/FileMessageRenderer.vue,2/0/20ab0a85018af94e3bbf7db6590654e68f695b20
+w
+Gsrc/pages/conversation/chat/components/MessageItem/CatchMsgRenderer.vue,a/0/a054c47187dbf258099f21bf916557c063d91750
+=
+
src/api/im.ts,5/8/58776c604f6509aa2c93c3a7d67ce58349dac6d0
+R
+"src/pages/rtc/RtcControl/index.vue,d/d/dd7e73ca7278975e65d40acea4cf1b4cb08e2882
+V
+&src/pages/rtc/RtcControl/Connected.vue,c/2/c281b8539e012ca654d940ef77d0da7899b7e1cb
+Q
+!src/pages/rtc/RtcLayout/index.vue,b/b/bb3d6b7a7af71a54816f53b9a9a25c095762fe85
+R
+"src/pages/rtc/RtcLayout/Camera.vue,8/2/821d4957ed5804169655383257415c0a4669b158
+P
+ src/pages/rtc/RtcLayout/Room.vue,f/5/f592a17b57c92518b8d7d3868f9c46e394b76638

+ 45 - 1
.idea/sonarlint/securityhotspotstore/index.pb

@@ -11,4 +11,48 @@ p
 B
 config/prod.env.ts,2/6/2664ea9f9e758a6438d06d5bfb2dd08244137256
 A
-config/dev.env.ts,7/6/765f6bc1a3843f5186fb65520064b9f6f854e758
+config/dev.env.ts,7/6/765f6bc1a3843f5186fb65520064b9f6f854e758
+K
+src/hooks/useSendMessage.ts,5/8/5833e149b554b5f5b59231cec3464ca76a11fdd3
+y
+Isrc/pages/conversation/chat/components/ChatFooter/useCreateFileMessage.ts,3/0/30178b2e77b21f1e226cdba404959fe0db6bb6ec
+v
+Fsrc/pages/conversation/chat/components/ChatFooter/ChatFooterAction.vue,4/e/4e14ccab76072b5ad98142a9b490043e32f86653
+z
+Jsrc/pages/conversation/chat/components/ChatFooter/useCreateNomalMessage.ts,e/d/ede8a2756a89349e01bd88578fb27c5f7f15ba2e
+r
+Bsrc/pages/conversation/chat/components/MessageItem/MessageItem.vue,2/7/27731e21b8092037a450007c205e05399ea93d8b
+{
+Ksrc/pages/conversation/chat/components/MessageItem/MediaMessageRenderer.vue,e/a/ea7e416fd060083d01bdbf89e31abcbb6776b155
+z
+Jsrc/pages/conversation/chat/components/MessageItem/TextMessageRenderer.vue,c/8/c802afd02e8bcc55f8323383eab3ddbcad781b6c
+@
+src/api/login.ts,d/5/d528c312125a5ba92c0af35729310a264b8c2026
+?
+src/api/data.ts,0/b/0b80f2764231b9b8e8b5ddb04202cc33ac79b32e
+?
+src/api/user.ts,1/e/1e9141c4ceceb897882b5879f5236bfdd21cab8c
+D
+src/utils/request.ts,4/f/4f98643a765bda61eec19aa34030baf0b5226849
+G
+src/constants/action.ts,e/2/e2f9dd78c4e007c13e2f790051a7e7215ee2da05
+K
+src/i18n/languages/en-US.ts,0/a/0a17e462a9b220a2f5b7b548398cd95d591fd237
+K
+src/i18n/languages/zh-CN.ts,0/c/0c02d3fd1b424f6a9b21a1d97581a2e0f3dd9826
+z
+Jsrc/pages/conversation/chat/components/MessageItem/FileMessageRenderer.vue,2/0/20ab0a85018af94e3bbf7db6590654e68f695b20
+w
+Gsrc/pages/conversation/chat/components/MessageItem/CatchMsgRenderer.vue,a/0/a054c47187dbf258099f21bf916557c063d91750
+=
+
src/api/im.ts,5/8/58776c604f6509aa2c93c3a7d67ce58349dac6d0
+R
+"src/pages/rtc/RtcControl/index.vue,d/d/dd7e73ca7278975e65d40acea4cf1b4cb08e2882
+V
+&src/pages/rtc/RtcControl/Connected.vue,c/2/c281b8539e012ca654d940ef77d0da7899b7e1cb
+Q
+!src/pages/rtc/RtcLayout/index.vue,b/b/bb3d6b7a7af71a54816f53b9a9a25c095762fe85
+R
+"src/pages/rtc/RtcLayout/Camera.vue,8/2/821d4957ed5804169655383257415c0a4669b158
+P
+ src/pages/rtc/RtcLayout/Room.vue,f/5/f592a17b57c92518b8d7d3868f9c46e394b76638

+ 15 - 15
package-lock.json

@@ -14,7 +14,7 @@
         "@vuemap/vue-amap": "^2.0.5",
         "@vueuse/core": "^9.10.0",
         "absurd-sql-optimized": "^0.0.1",
-        "axios": "^1.1.3",
+        "axios": "^1.4.0",
         "cos-js-sdk-v5": "^1.4.6",
         "date-fns": "^2.30.0",
         "dayjs": "^1.11.5",
@@ -1764,11 +1764,11 @@
       }
     },
     "node_modules/axios": {
-      "version": "1.1.3",
-      "resolved": "https://registry.npmjs.org/axios/-/axios-1.1.3.tgz",
-      "integrity": "sha512-00tXVRwKx/FZr/IDVFt4C+f9FYairX517WoGCL6dpOntqLkZofjhu43F/Xl44UOpqa+9sLFDrG/XAnFsUYgkDA==",
+      "version": "1.7.7",
+      "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.7.tgz",
+      "integrity": "sha512-S4kL7XrjgBmvdGut0sN3yJxqYzrDOnivkBiN0OFs6hLiUam3UPvswUo0kqGyhqUZGEOytHyumEdXsAkgCOUf3Q==",
       "dependencies": {
-        "follow-redirects": "^1.15.0",
+        "follow-redirects": "^1.15.6",
         "form-data": "^4.0.0",
         "proxy-from-env": "^1.1.0"
       }
@@ -2400,9 +2400,9 @@
       }
     },
     "node_modules/follow-redirects": {
-      "version": "1.15.2",
-      "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz",
-      "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==",
+      "version": "1.15.9",
+      "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz",
+      "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==",
       "funding": [
         {
           "type": "individual",
@@ -5614,11 +5614,11 @@
       }
     },
     "axios": {
-      "version": "1.1.3",
-      "resolved": "https://registry.npmjs.org/axios/-/axios-1.1.3.tgz",
-      "integrity": "sha512-00tXVRwKx/FZr/IDVFt4C+f9FYairX517WoGCL6dpOntqLkZofjhu43F/Xl44UOpqa+9sLFDrG/XAnFsUYgkDA==",
+      "version": "1.7.7",
+      "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.7.tgz",
+      "integrity": "sha512-S4kL7XrjgBmvdGut0sN3yJxqYzrDOnivkBiN0OFs6hLiUam3UPvswUo0kqGyhqUZGEOytHyumEdXsAkgCOUf3Q==",
       "requires": {
-        "follow-redirects": "^1.15.0",
+        "follow-redirects": "^1.15.6",
         "form-data": "^4.0.0",
         "proxy-from-env": "^1.1.0"
       }
@@ -6083,9 +6083,9 @@
       }
     },
     "follow-redirects": {
-      "version": "1.15.2",
-      "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz",
-      "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA=="
+      "version": "1.15.9",
+      "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz",
+      "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ=="
     },
     "form-data": {
       "version": "4.0.0",

+ 1 - 1
package.json

@@ -15,7 +15,7 @@
     "@vuemap/vue-amap": "^2.0.5",
     "@vueuse/core": "^9.10.0",
     "absurd-sql-optimized": "^0.0.1",
-    "axios": "^1.1.3",
+    "axios": "^1.4.0",
     "cos-js-sdk-v5": "^1.4.6",
     "date-fns": "^2.30.0",
     "dayjs": "^1.11.5",

BIN
src/assets/audio/calling.mp3


+ 1 - 1
src/pages/conversation/chat/components/ChatFooter/ChatFooter.vue

@@ -113,4 +113,4 @@ const getFile = async (uploadData: UploaderFileListItem) => {
 :deep(.van-button__content) {
   width: max-content;
 }
-</style>
+</style>

+ 36 - 31
src/pages/conversation/chat/components/ChatFooter/ChatFooterAction.vue

@@ -2,51 +2,51 @@
   <div ref="target" class="bg-[#F0F2F6]">
     <van-grid class="px-3 py-3" :border="false" :column-num="4">
       <van-grid-item
-        v-for="action in actionList"
-        :key="action.type"
-        clickable
-        :icon="action.icon"
-        :text="action.text"
-        @click="clickAction(action)"
+          v-for="action in actionList"
+          :key="action.type"
+          clickable
+          :icon="action.icon"
+          :text="action.text"
+          @click="clickAction(action)"
       />
     </van-grid>
     <van-uploader
-      v-show="false"
-      ref="uploaderRef"
-      :accept="uploadChooseOptions.accept"
-      :capture="uploadChooseOptions.capture"
-      :preview-image="false"
-      multiple
-      max-count="9"
-      :after-read="afterReadFile"
+        v-show="false"
+        ref="uploaderRef"
+        :accept="uploadChooseOptions.accept"
+        :capture="uploadChooseOptions.capture"
+        :preview-image="false"
+        multiple
+        max-count="9"
+        :after-read="afterReadFile"
     />
     <van-action-sheet
-      v-model:show="actionSheetVisible"
-      teleport="body"
-      :actions="actionSheetActions"
-      @select="onActionSelect"
+        v-model:show="actionSheetVisible"
+        teleport="body"
+        :actions="actionSheetActions"
+        @select="onActionSelect"
     />
-    <div class="dac"></div>
   </div>
 </template>
 
 <script setup lang="ts">
 import image from "@/assets/images/chatFooter/image.png";
 import camera from "@/assets/images/chatFooter/camera.png";
+import file from "@/assets/images/chatFooter/file.png";
 import call from "@/assets/images/chatFooter/call.png";
 
-import { onClickOutside } from "@vueuse/core";
+import {onClickOutside} from "@vueuse/core";
 import {
   ActionSheetAction,
   UploaderFileListItem,
   UploaderInstance,
 } from "vant";
-import { useInviteRtc } from "@/hooks/useInviteRtc";
+import {useInviteRtc} from "@/hooks/useInviteRtc";
 import useConversationStore from "@/store/modules/conversation";
-import { ChatFooterActionType } from "@/constants/action";
+import {ChatFooterActionType} from "@/constants/action";
 
-const { t, locale } = useI18n()
-const { inviteRtc } = useInviteRtc();
+const {t, locale} = useI18n()
+const {inviteRtc} = useInviteRtc();
 const conversationStore = useConversationStore();
 
 type ChatFooterActionEmits = {
@@ -71,6 +71,11 @@ const actionList: ChatFooterActionItem[] = [
     icon: camera,
     type: ChatFooterActionType.Shoot,
   },
+  {
+    text: t("footerAction.file"),
+    icon: file,
+    type: ChatFooterActionType.File,
+  },
   {
     text: t("rtc.video"),
     icon: call,
@@ -138,11 +143,8 @@ onClickOutside(target, () => emit("closeActionBar"), {
   ignore: [".van-overlay", ".van-action-sheet__content"],
 });
 
-const onActionSelect = ({ type }: any, idx: number) => {
-  if (
-    type === ChatFooterActionType.VoiceCall ||
-    type === ChatFooterActionType.VideoCall
-  ) {
+const onActionSelect = ({type}: any, idx: number) => {
+  if (type === ChatFooterActionType.VoiceCall || type === ChatFooterActionType.VideoCall) {
     actionSheetVisible.value = false;
     inviteRtc(type, [conversationStore.currentConversation.userID]);
     return;
@@ -155,8 +157,7 @@ const onActionSelect = ({ type }: any, idx: number) => {
   actionSheetVisible.value = false;
 };
 
-const clickAction = ({ type }: ChatFooterActionItem) => {
-  console.log(type);
+const clickAction = ({type}: ChatFooterActionItem) => {
   switch (type) {
     case ChatFooterActionType.Album:
       actionSheetActions.value = [...albumActions];
@@ -166,6 +167,10 @@ const clickAction = ({ type }: ChatFooterActionItem) => {
       actionSheetActions.value = [...shootActions];
       actionSheetVisible.value = true;
       break;
+    case ChatFooterActionType.File:
+      uploadChooseOptions.accept = "*"
+      nextTick(() => uploaderRef.value?.chooseFile());
+      break;
     case ChatFooterActionType.VideoCall:
       actionSheetActions.value = [...videoCallActions];
       actionSheetVisible.value = true;

+ 19 - 0
src/pages/conversation/chat/components/ChatFooter/useCreateFileMessage.ts

@@ -68,6 +68,20 @@ export default function useCreateFileMessage() {
     return (await IMSDK.createVideoMessageByFile(options)).data;
   };
 
+  const getFileMessage = async (file: File) => {
+    const options = {
+      filePath: '',
+      fileName: file.name,
+      uuid: uuidV4(),
+      sourceUrl: '',
+      fileSize: file.size,
+      fileType: file.type,
+      file
+    };
+
+    return (await IMSDK.createFileMessageByFile(options)).data;
+  };
+
   const createFileMessage = async (
     file: File,
     messageType: MessageType,
@@ -97,6 +111,11 @@ export default function useCreateFileMessage() {
           buffer: await getFileData(file),
           snapBuffer: await getFileData(snapShotFile!),
         };
+      case MessageType.FileMessage:
+        return {
+          message: await getFileMessage(file),
+          buffer: await getFileData(file),
+        };
       default:
         return {
           error: "message type error",

+ 60 - 0
src/pages/conversation/chat/components/MessageItem/FileMessageRenderer.vue

@@ -0,0 +1,60 @@
+<template>
+  <div class="text_content need_bg">
+    <img src="@/assets/images/chatFooter/file.png" class="pic" alt="">
+    <div>
+      <div>{{ fileElem.fileName }}</div>
+      <div v-if="isSucceed">
+        <span class="fz size">{{ getFileSize() }}</span> -
+        <a :href="fileElem.sourceUrl" :download="fileElem.fileName" class="fz link">下载</a>
+      </div>
+      <div v-else class="fz">上传中...</div>
+    </div>
+  </div>
+</template>
+
+<script setup lang="ts">
+import {ExedMessageItem} from "./data";
+import {MessageStatus} from "@openim/wasm-client-sdk";
+
+type TextMsgRendererProps = {
+  message: ExedMessageItem;
+}
+
+const props = defineProps<TextMsgRendererProps>();
+const fileElem = props.message.fileElem || {};
+const isSucceed = computed(() => props.message.status === MessageStatus.Succeed);
+const getFileSize = () => {
+  const fileSize = fileElem.fileSize / 1024;
+  if (fileSize > 1024) {
+    return (fileSize / 1024).toFixed(2) + 'M';
+  } else {
+    return fileSize.toFixed(1) + 'KB';
+  }
+};
+</script>
+
+<style lang="scss" scoped>
+.text_content {
+  display: flex;
+  align-items: center;
+  gap: 10px;
+  line-height: 1.4;
+}
+
+.pic {
+  width: 38px;
+  height: 38px;
+}
+
+.fz {
+  font-size: 13px;
+}
+
+.size {
+  color: gray;
+}
+
+.link {
+  color: #0289fa;
+}
+</style>

+ 2 - 2
src/pages/conversation/chat/components/MessageItem/MediaMessageRenderer.vue

@@ -5,7 +5,7 @@
       <template v-slot:loading>
         <van-loading type="spinner" size="20" />
       </template>
-      <template v-slot:error>{{ $t('failLoad') }}</template>
+      <template v-slot:error>{{ $t('messageTip.failLoad') }}</template>
     </van-image>
     <img v-if="isVideo" class="play_icon" :src="play_icon" alt="" />
     <text v-if="isVideo" class="video_duration">{{ duration }}</text>
@@ -81,4 +81,4 @@ const clickMediaItem = () => {
     color: #fff;
   }
 }
-</style>
+</style>

+ 3 - 0
src/pages/conversation/chat/components/MessageItem/MessageItem.vue

@@ -56,6 +56,7 @@
 import Avatar from "@/components/Avatar/index.vue";
 import TextMessageRenderer from "./TextMessageRenderer.vue";
 import MediaMessageRenderer from "./MediaMessageRenderer.vue";
+import FileMessageRenderer from "./FileMessageRenderer.vue";
 import CatchMsgRenderer from "./CatchMsgRenderer.vue";
 import {
   AllowType,
@@ -115,6 +116,8 @@ const getRenderComp = computed(() => {
     case MessageType.VideoMessage:
     case MessageType.PictureMessage:
       return MediaMessageRenderer;
+    case MessageType.FileMessage:
+      return FileMessageRenderer;
     default:
       return CatchMsgRenderer;
   }

+ 8 - 0
src/pages/rtc/RtcLayout/index.vue

@@ -54,6 +54,8 @@
       isWaiting ? $t('rtc.waitConnected') : $t('rtc.inConnect')
     }}</span>
   </div>
+
+  <audio :src="calling" loop ref="callingRef"/>
 </template>
 
 <script lang="ts" setup>
@@ -62,6 +64,7 @@ import RtcControl from '../RtcControl/index.vue'
 import Counter from '../Counter/index.vue'
 import RtcRoom from './Room.vue'
 import RtcProfile from '../RtcProfile/index.vue'
+import calling from '@/assets/audio/calling.mp3'
 
 import { AuthData, InviteData } from '../data'
 import { useDraggable } from '@vueuse/core'
@@ -88,6 +91,7 @@ const isClose = ref(false)
 const isMini = ref(false)
 const counterRef = ref()
 const el = ref<HTMLElement | null>(null)
+const callingRef = ref<HTMLAudioElement>(null);
 
 const duration = computed(() => counterRef.value?.getTime() ?? '')
 const isWaiting = computed(() => !(props.connect && props.isConnected))
@@ -120,6 +124,10 @@ const changeMini = () => {
   show.value = !show.value
   isMini.value = !isMini.value
 }
+
+watchEffect(() => {
+  isWaiting.value ? callingRef.value?.play() : callingRef.value?.pause()
+});
 </script>
 
 <style lang="scss" scoped>

+ 1 - 1
src/utils/request.ts

@@ -7,7 +7,7 @@ type ErrorData = {
   errCode: number
   errMsg?: string
 }
-
+axios.defaults.headers['Content-Type'] = 'application/json;charset=utf-8';
 const serves = axios.create({
   baseURL: process.env.CHAT_URL,
   timeout: 5000,