index.vue 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244
  1. <template>
  2. <div :class="['message-input', isH5 && 'message-input-h5']">
  3. <MessageInputEditor
  4. ref="editor"
  5. :isH5="isH5"
  6. :placeholder="placeholder"
  7. :isGroup="isGroup"
  8. :isMute="isMute"
  9. :muteText="muteText"
  10. :enableInput="enableInput"
  11. :enableAt="enableAt"
  12. :enableTyping="enableTyping"
  13. :enableDragUpload="enableDragUpload"
  14. @sendMessage="sendMessage"
  15. @onTyping="onTyping"
  16. ></MessageInputEditor>
  17. <MessageInputButton :isH5="isH5" @sendMessage="sendMessage" v-if="!isMute"></MessageInputButton>
  18. <MessageInputAt :memberList="memberList" :isGroup="isGroup" :selfInfo="conversation?.groupProfile?.selfInfo" :isH5="isH5" v-if="enableAt"></MessageInputAt>
  19. <MessageInputReferenceOrReply :replyOrReference="replyOrReference" :isH5="isH5" @resetReplyOrReference="resetReplyOrReference"></MessageInputReferenceOrReply>
  20. </div>
  21. </template>
  22. <script setup lang="ts">
  23. import { defineProps, defineEmits, toRefs, ref, defineExpose, watch } from "vue";
  24. import MessageInputEditor from "./message-input-editor.vue";
  25. import MessageInputAt from "./message-input-at.vue";
  26. import MessageInputButton from "./message-input-button.vue";
  27. import MessageInputReferenceOrReply from "./message-input-reference-or-reply.vue";
  28. import { JSONToObject } from "../utils/utils";
  29. import { handleErrorPrompts } from "../../utils";
  30. import { useStore } from "vuex";
  31. import { eventGlobal } from "@/helpers";
  32. const props = defineProps({
  33. placeholder: {
  34. type: String,
  35. default: "this is placeholder",
  36. },
  37. conversation: {
  38. type: Object,
  39. default: () => ({}),
  40. },
  41. replyOrReference: {
  42. type: Object,
  43. default: () => ({}),
  44. },
  45. isGroup: {
  46. type: Boolean,
  47. default: false,
  48. },
  49. memberList: {
  50. type: Array,
  51. default: () => [],
  52. },
  53. isMute: {
  54. type: Boolean,
  55. default: true,
  56. },
  57. muteText: {
  58. type: String,
  59. default: "",
  60. },
  61. enableInput: {
  62. type: Boolean,
  63. default: true,
  64. },
  65. enableAt: {
  66. type: Boolean,
  67. default: true,
  68. },
  69. enableDragUpload: {
  70. type: Boolean,
  71. default: true,
  72. },
  73. enableTyping: {
  74. type: Boolean,
  75. default: true,
  76. },
  77. env: {
  78. type: Object,
  79. default: () => ({}),
  80. },
  81. });
  82. const emit = defineEmits(["sendMessage", "resetReplyOrReference", "onTyping"]);
  83. const { placeholder, isGroup, memberList, conversation, replyOrReference, env, enableTyping } = toRefs(props);
  84. const editor = ref();
  85. const isH5 = ref(props?.env?.isH5);
  86. const store = useStore && useStore();
  87. watch(
  88. () => conversation.value,
  89. (newVal: any, oldVal: any) => {
  90. if (newVal?.conversationID !== oldVal?.conversationID) {
  91. // conversation change
  92. editor?.value?.resetEditor();
  93. }
  94. },
  95. {
  96. immediate: true,
  97. }
  98. );
  99. const sendMessage = async () => {
  100. console.log(store.state.imConnent, "store.imConnent");
  101. if (!store.state.imConnent) {
  102. eventGlobal.emit("reConnectIm", true);
  103. return;
  104. }
  105. const TUIServer = (window as any)?.TUIKitTUICore?.TUIServer?.TUIChat;
  106. const messageList = editor?.value?.getEditorContent();
  107. const replyOrReferenceObject = replyOrReference.value;
  108. await messageList?.forEach(async (content: any) => {
  109. try {
  110. let cloudCustomData, res;
  111. // handle message typing
  112. cloudCustomData = handleMessageWithTyping(cloudCustomData);
  113. switch (content?.type) {
  114. case "text":
  115. // 引用和回复只支持文本消息(对标微信)
  116. // replay or reference message
  117. cloudCustomData = handleMessageReplyOrReference(cloudCustomData);
  118. // @ text message
  119. if (content?.payload?.atUserList) {
  120. res = await TUIServer?.sendTextAtMessage(
  121. {
  122. text: JSON.parse(JSON.stringify(content?.payload?.text)),
  123. atUserList: content?.payload?.atUserList,
  124. },
  125. cloudCustomData
  126. );
  127. } else {
  128. res = await TUIServer?.sendTextMessage(JSON.parse(JSON.stringify(content?.payload?.text)), cloudCustomData);
  129. }
  130. if (replyOrReferenceObject?.show === "reply") {
  131. await TUIServer.replyMessage(res?.data?.message);
  132. }
  133. break;
  134. case "image":
  135. await TUIServer?.sendImageMessage(content?.payload?.file);
  136. break;
  137. case "video":
  138. await TUIServer?.sendVideoMessage(content?.payload?.file);
  139. break;
  140. case "file":
  141. await TUIServer?.sendFileMessage(content?.payload?.file);
  142. break;
  143. default:
  144. break;
  145. }
  146. emit("sendMessage");
  147. } catch (error: any) {
  148. handleErrorPrompts(error, env.value);
  149. }
  150. });
  151. editor?.value?.resetEditor();
  152. resetReplyOrReference();
  153. };
  154. const onTyping = (inputContentEmpty: boolean, inputBlur: boolean) => {
  155. emit("onTyping", inputContentEmpty, inputBlur);
  156. };
  157. const handleMessageWithTyping = (cloudCustomData: any) => {
  158. if (enableTyping.value) {
  159. if (!cloudCustomData) {
  160. cloudCustomData = {};
  161. }
  162. cloudCustomData.messageFeature = {
  163. needTyping: 1,
  164. version: 1,
  165. };
  166. }
  167. return cloudCustomData;
  168. };
  169. const handleMessageReplyOrReference = (cloudCustomData: any) => {
  170. if (replyOrReference?.value?.show !== "reply" && replyOrReference?.value?.show !== "reference") {
  171. return cloudCustomData;
  172. }
  173. if (!cloudCustomData) {
  174. cloudCustomData = {};
  175. }
  176. cloudCustomData.messageReply = {
  177. messageAbstract: replyOrReference?.value?.content,
  178. messageSender: replyOrReference?.value?.message?.nick || replyOrReference?.value?.message?.from,
  179. messageID: replyOrReference?.value?.message?.ID,
  180. messageType: replyOrReference?.value?.type,
  181. version: 1,
  182. };
  183. if (replyOrReference?.value?.show === "reply") {
  184. try {
  185. cloudCustomData.messageReply.messageRootID = replyOrReference?.value?.message?.ID;
  186. if (replyOrReference?.value?.message?.cloudCustomData) {
  187. const replyMessageCloudCustomData = JSONToObject(replyOrReference?.value?.message?.cloudCustomData as any);
  188. cloudCustomData.messageReply.messageRootID = replyMessageCloudCustomData?.messageReply?.messageRootID || replyOrReference?.value?.message?.ID;
  189. }
  190. } catch (error) {
  191. console.warn(error);
  192. }
  193. }
  194. return cloudCustomData;
  195. };
  196. const addEmoji = (emoji: any) => {
  197. editor?.value?.addEmoji(emoji);
  198. };
  199. const resetReplyOrReference = () => {
  200. emit("resetReplyOrReference");
  201. };
  202. const reEdit = (content: any) => {
  203. editor?.value?.resetEditor();
  204. resetReplyOrReference();
  205. editor?.value?.setEditorContent(content);
  206. };
  207. defineExpose({
  208. addEmoji,
  209. reEdit,
  210. });
  211. </script>
  212. <style scoped lang="scss">
  213. @import url("../../../styles/common.scss");
  214. @import url("../../../styles/icon.scss");
  215. .message-input {
  216. flex: 1;
  217. position: relative;
  218. display: flex;
  219. flex-direction: column;
  220. border: none;
  221. height: 100%;
  222. width: 100%;
  223. max-height: 100%;
  224. max-width: 100%;
  225. overflow: hidden;
  226. }
  227. .message-input-h5 {
  228. display: flex;
  229. flex-direction: row;
  230. flex-wrap: wrap;
  231. }
  232. </style>