index.vue 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294
  1. <template>
  2. <div
  3. :class="[
  4. 'message-input-toolbar',
  5. 'message-input-toolbar-h5',
  6. 'message-input-toolbar-uni',
  7. ]"
  8. >
  9. <div v-if="props.displayType === 'emojiPicker'">
  10. <EmojiPickerDialog />
  11. </div>
  12. <div v-else>
  13. <swiper
  14. :class="['message-input-toolbar-swiper']"
  15. :indicator-dots="isSwiperIndicatorDotsEnable"
  16. :autoplay="false"
  17. :circular="false"
  18. >
  19. <swiper-item
  20. :class="[
  21. 'message-input-toolbar-list',
  22. 'message-input-toolbar-h5-list',
  23. 'message-input-toolbar-uni-list',
  24. ]"
  25. >
  26. <ImageUpload v-if="featureConfig.InputImage" imageSourceType="camera" />
  27. <ImageUpload v-if="featureConfig.InputImage" imageSourceType="album" />
  28. <VideoUpload v-if="featureConfig.InputVideo" videoSourceType="album" />
  29. <VideoUpload v-if="featureConfig.InputVideo" videoSourceType="camera" />
  30. <template v-if="currentExtensionList.length > 0">
  31. <div
  32. v-for="(extension, index) in currentExtensionList.slice(0, slicePos)"
  33. :key="index"
  34. >
  35. <ToolbarItemContainer
  36. v-if="extension"
  37. :iconFile="genExtensionIcon(extension)"
  38. :title="genExtensionText(extension)"
  39. iconWidth="25px"
  40. iconHeight="25px"
  41. :needDialog="false"
  42. @onIconClick="onExtensionClick(extension)"
  43. />
  44. </div>
  45. </template>
  46. <!-- <template v-if="neededCountFirstPage === 1">
  47. <Evaluate
  48. v-if="featureConfig.InputEvaluation"
  49. @onDialogPopupShowOrHide="handleSwiperDotShow"
  50. />
  51. <Words
  52. v-else-if="featureConfig.InputQuickReplies"
  53. @onDialogPopupShowOrHide="handleSwiperDotShow"
  54. />
  55. </template>
  56. <template v-if="neededCountFirstPage > 1">
  57. <Evaluate
  58. v-if="featureConfig.InputEvaluation"
  59. @onDialogPopupShowOrHide="handleSwiperDotShow"
  60. />
  61. <Words
  62. v-if="featureConfig.InputQuickReplies"
  63. @onDialogPopupShowOrHide="handleSwiperDotShow"
  64. />
  65. </template> -->
  66. </swiper-item>
  67. <swiper-item
  68. v-if="neededCountFirstPage <= 1"
  69. :class="[
  70. 'message-input-toolbar-list',
  71. 'message-input-toolbar-h5-list',
  72. 'message-input-toolbar-uni-list',
  73. ]"
  74. >
  75. <div
  76. v-for="(extension, index) in currentExtensionList.slice(slicePos)"
  77. :key="index"
  78. >
  79. <ToolbarItemContainer
  80. v-if="extension"
  81. :iconFile="genExtensionIcon(extension)"
  82. :title="genExtensionText(extension)"
  83. iconWidth="25px"
  84. iconHeight="25px"
  85. :needDialog="false"
  86. @onIconClick="onExtensionClick(extension)"
  87. />
  88. </div>
  89. <template v-if="neededCountFirstPage === 1">
  90. <Words
  91. v-if="featureConfig.InputQuickReplies"
  92. @onDialogPopupShowOrHide="handleSwiperDotShow"
  93. />
  94. </template>
  95. <template v-else>
  96. <Evaluate
  97. v-if="featureConfig.InputEvaluation"
  98. @onDialogPopupShowOrHide="handleSwiperDotShow"
  99. />
  100. <Words
  101. v-if="featureConfig.InputQuickReplies"
  102. @onDialogPopupShowOrHide="handleSwiperDotShow"
  103. />
  104. </template>
  105. </swiper-item>
  106. </swiper>
  107. </div>
  108. <UserSelector
  109. ref="userSelectorRef"
  110. :type="selectorShowType"
  111. :currentConversation="currentConversation"
  112. :isGroup="isGroup"
  113. @submit="onUserSelectorSubmit"
  114. @cancel="onUserSelectorCancel"
  115. />
  116. </div>
  117. </template>
  118. <script setup lang="ts">
  119. import { ref, onUnmounted, onMounted } from "../../../adapter-vue";
  120. import TUIChatEngine, {
  121. IConversationModel,
  122. TUIStore,
  123. StoreName,
  124. } from "@tencentcloud/chat-uikit-engine";
  125. import TUICore, { ExtensionInfo, TUIConstants } from "@tencentcloud/tui-core";
  126. import ImageUpload from "./image-upload/index.vue";
  127. import VideoUpload from "./video-upload/index.vue";
  128. import Evaluate from "./evaluate/index.vue";
  129. import Words from "./words/index.vue";
  130. import ToolbarItemContainer from "./toolbar-item-container/index.vue";
  131. import EmojiPickerDialog from "./emoji-picker/emoji-picker-dialog.vue";
  132. import UserSelector from "./user-selector/index.vue";
  133. import TUIChatConfig from "../config";
  134. import { enableSampleTaskStatus } from "../../../utils/enableSampleTaskStatus";
  135. import { ToolbarDisplayType } from "../../../interface";
  136. import OfflinePushInfoManager, { PUSH_SCENE } from "../offlinePushInfoManager/index";
  137. interface IProps {
  138. displayType: ToolbarDisplayType;
  139. }
  140. const props = withDefaults(defineProps<IProps>(), {});
  141. const currentConversation = ref<IConversationModel>();
  142. const isGroup = ref<boolean>(false);
  143. const selectorShowType = ref<string>("");
  144. const userSelectorRef = ref();
  145. const currentUserSelectorExtension = ref<ExtensionInfo | null>();
  146. const currentExtensionList = ref<ExtensionInfo[]>([]);
  147. const isSwiperIndicatorDotsEnable = ref<boolean>(false);
  148. const featureConfig = TUIChatConfig.getFeatureConfig();
  149. const neededCountFirstPage = ref<number>(8);
  150. const slicePos = ref<number>(0);
  151. const computeToolbarPaging = () => {
  152. if (featureConfig.InputImage && featureConfig.InputVideo) {
  153. neededCountFirstPage.value -= 4;
  154. } else if (featureConfig.InputImage || featureConfig.InputVideo) {
  155. neededCountFirstPage.value -= 2;
  156. }
  157. slicePos.value = neededCountFirstPage.value;
  158. neededCountFirstPage.value -= currentExtensionList.value.length;
  159. if (neededCountFirstPage.value === 1) {
  160. isSwiperIndicatorDotsEnable.value =
  161. featureConfig.InputEvaluation && featureConfig.InputQuickReplies;
  162. } else if (neededCountFirstPage.value < 1) {
  163. isSwiperIndicatorDotsEnable.value =
  164. featureConfig.InputEvaluation || featureConfig.InputQuickReplies;
  165. }
  166. };
  167. const getExtensionList = (conversationID: string) => {
  168. if (!conversationID) {
  169. return;
  170. }
  171. const chatType = TUIChatConfig.getChatType();
  172. const params: Record<string, boolean | string> = { chatType };
  173. // Backward compatibility: When callkit does not have chatType judgment, use filterVoice and filterVideo to filter
  174. if (chatType === TUIConstants.TUIChat.TYPE.CUSTOMER_SERVICE) {
  175. params.filterVoice = true;
  176. params.filterVideo = true;
  177. enableSampleTaskStatus("customerService");
  178. }
  179. // uni-app build ios app has null in last index need to filter
  180. currentExtensionList.value = [
  181. ...TUICore.getExtensionList(TUIConstants.TUIChat.EXTENSION.INPUT_MORE.EXT_ID, params),
  182. ].filter((extension: ExtensionInfo) => {
  183. if (extension?.data?.name === "search") {
  184. return featureConfig.MessageSearch;
  185. }
  186. return true;
  187. });
  188. };
  189. const onCurrentConversationUpdate = (conversation: IConversationModel) => {
  190. if (
  191. conversation?.conversationID &&
  192. conversation.conversationID !== currentConversation.value?.conversationID
  193. ) {
  194. getExtensionList(conversation.conversationID);
  195. computeToolbarPaging();
  196. }
  197. currentConversation.value = conversation;
  198. isGroup.value = currentConversation?.value?.type === TUIChatEngine.TYPES.CONV_GROUP;
  199. };
  200. onMounted(() => {
  201. TUIStore.watch(StoreName.CONV, {
  202. currentConversation: onCurrentConversationUpdate,
  203. });
  204. });
  205. onUnmounted(() => {
  206. TUIStore.unwatch(StoreName.CONV, {
  207. currentConversation: onCurrentConversationUpdate,
  208. });
  209. });
  210. // handle extensions onclick
  211. const onExtensionClick = (extension: ExtensionInfo) => {
  212. // uniapp vue2 build wx lose listener proto
  213. const extensionModel = currentExtensionList.value.find(
  214. (targetExtension) => targetExtension?.data?.name === extension?.data?.name
  215. );
  216. switch (extensionModel?.data?.name) {
  217. case "voiceCall":
  218. onCallExtensionClicked(extensionModel, 1);
  219. break;
  220. case "videoCall":
  221. onCallExtensionClicked(extensionModel, 2);
  222. break;
  223. case "search":
  224. extensionModel?.listener?.onClicked?.();
  225. break;
  226. default:
  227. break;
  228. }
  229. };
  230. const onCallExtensionClicked = (extension: ExtensionInfo, callType: number) => {
  231. selectorShowType.value = extension?.data?.name;
  232. if (currentConversation?.value?.type === TUIChatEngine.TYPES.CONV_C2C) {
  233. extension?.listener?.onClicked?.({
  234. userIDList: [currentConversation?.value?.conversationID?.slice(3)],
  235. type: callType,
  236. callParams: {
  237. offlinePushInfo: OfflinePushInfoManager.getOfflinePushInfo(PUSH_SCENE.CALL),
  238. },
  239. });
  240. } else if (isGroup.value) {
  241. currentUserSelectorExtension.value = extension;
  242. userSelectorRef?.value?.toggleShow && userSelectorRef.value.toggleShow(true);
  243. }
  244. };
  245. const genExtensionIcon = (extension: any) => {
  246. return extension?.icon;
  247. };
  248. const genExtensionText = (extension: any) => {
  249. return extension?.text;
  250. };
  251. const onUserSelectorSubmit = (selectedInfo: any) => {
  252. currentUserSelectorExtension.value?.listener?.onClicked?.({
  253. ...selectedInfo,
  254. callParams: {
  255. offlinePushInfo: OfflinePushInfoManager.getOfflinePushInfo(PUSH_SCENE.CALL),
  256. },
  257. });
  258. currentUserSelectorExtension.value = null;
  259. };
  260. const onUserSelectorCancel = () => {
  261. currentUserSelectorExtension.value = null;
  262. };
  263. const handleSwiperDotShow = (showStatus: boolean) => {
  264. isSwiperIndicatorDotsEnable.value = neededCountFirstPage.value <= 1 && !showStatus;
  265. };
  266. </script>
  267. <script lang="ts">
  268. export default {
  269. options: {
  270. styleIsolation: "shared",
  271. },
  272. };
  273. </script>
  274. <style lang="scss">
  275. @import "../../../assets/styles/common";
  276. @import "./style/uni";
  277. </style>