ChatFooterAction.vue 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300
  1. <template>
  2. <div ref="target" class="bg-[#F0F2F6]">
  3. <van-grid class="px-3 py-3" :border="false" :column-num="4">
  4. <van-grid-item v-for="action in actionList" :key="action.type" clickable :icon="action.icon"
  5. :text="action.text" @click="clickAction(action)" />
  6. </van-grid>
  7. <van-uploader v-show="false" ref="uploaderRef" :accept="uploadChooseOptions.accept"
  8. :capture="uploadChooseOptions.capture" :preview-image="false" multiple max-count="9"
  9. :after-read="afterReadFile" />
  10. <van-action-sheet v-model:show="actionSheetVisible" teleport="body" :actions="actionSheetActions"
  11. @select="onActionSelect" />
  12. <div class="dac"></div>
  13. </div>
  14. </template>
  15. <script setup lang="ts">
  16. import image from '@/assets/images/chatFooter/image.png'
  17. import camera from '@/assets/images/chatFooter/camera.png'
  18. import call from '@/assets/images/chatFooter/call.png'
  19. import file from '@/assets/images/chatFooter/file.png'
  20. import card from '@/assets/images/chatFooter/card.png'
  21. import location from '@/assets/images/chatFooter/location.png'
  22. import { onClickOutside } from '@vueuse/core'
  23. import {
  24. ActionSheetAction,
  25. UploaderFileListItem,
  26. UploaderInstance,
  27. showConfirmDialog,
  28. showLoadingToast,
  29. } from 'vant'
  30. import { ContactChooseEnum } from '@/pages/contact/chooseUser/data'
  31. import { ToastWrapperInstance } from 'vant/lib/toast/types'
  32. import { ChatFooterActionType, RtcType } from '@/constants/action'
  33. import useConversationStore from '@/store/modules/conversation'
  34. import { SessionType } from 'open-im-sdk-wasm'
  35. import { useInviteRtc } from '@/hooks/useInviteRtc'
  36. import { MemberListActionEnum } from '@/pages/contact/groupMemberList/data'
  37. import { IMSDK } from '@/utils/imCommon'
  38. import { CallingRoomData } from '@/pages/rtc/data'
  39. import emitter from '@/utils/events'
  40. const { t, locale } = useI18n()
  41. const { inviteRtc } = useInviteRtc()
  42. type ChatFooterActionEmits = {
  43. (event : 'closeActionBar') : void
  44. (event : 'getFile', uploadData : UploaderFileListItem) : void
  45. }
  46. type ChatFooterActionItem = {
  47. text : string
  48. icon : string
  49. type : ChatFooterActionType
  50. }
  51. const actionList : ChatFooterActionItem[] = [
  52. {
  53. text: t('footerAction.album'),
  54. icon: image,
  55. type: ChatFooterActionType.Album,
  56. },
  57. {
  58. text: t('footerAction.shoot'),
  59. icon: camera,
  60. type: ChatFooterActionType.Shoot,
  61. },
  62. {
  63. text: t('rtc.video'),
  64. icon: call,
  65. type: ChatFooterActionType.VideoCall,
  66. },
  67. {
  68. text: t('footerAction.file'),
  69. icon: file,
  70. type: ChatFooterActionType.File,
  71. },
  72. {
  73. text: t('footerAction.idCard'),
  74. icon: card,
  75. type: ChatFooterActionType.IDCard,
  76. },
  77. // {
  78. // text: t('footerAction.location'),
  79. // icon: location,
  80. // type: ChatFooterActionType.Location,
  81. // },
  82. ]
  83. const albumActions = [
  84. {
  85. name: t('picture'),
  86. type: ChatFooterActionType.Album,
  87. },
  88. {
  89. name: t('video'),
  90. type: ChatFooterActionType.Album,
  91. },
  92. ] as unknown as ActionSheetAction[]
  93. const shootActions = [
  94. {
  95. name: t('photograph'),
  96. type: ChatFooterActionType.Shoot,
  97. },
  98. {
  99. name: t('recording'),
  100. type: ChatFooterActionType.Shoot,
  101. },
  102. ] as unknown as ActionSheetAction[]
  103. const videoCallActions = [
  104. {
  105. name: t('rtc.voice'),
  106. type: ChatFooterActionType.VoiceCall,
  107. },
  108. {
  109. name: t('rtc.video'),
  110. type: ChatFooterActionType.VideoCall,
  111. },
  112. ] as unknown as ActionSheetAction[]
  113. watch(locale, () => {
  114. actionList[0].text = t('footerAction.album')
  115. actionList[1].text = t('footerAction.shoot')
  116. actionList[2].text = t('rtc.video')
  117. actionList[3].text = t('footerAction.file')
  118. actionList[4].text = t('footerAction.idCard')
  119. actionList[5].text = t('footerAction.location')
  120. albumActions[0].name = t('picture')
  121. albumActions[1].name = t('video')
  122. shootActions[0].name = t('photograph')
  123. shootActions[1].name = t('recording')
  124. videoCallActions[0].name = t('rtc.voice')
  125. videoCallActions[1].name = t('rtc.video')
  126. })
  127. const router = useRouter()
  128. const conversationStore = useConversationStore()
  129. const emit = defineEmits<ChatFooterActionEmits>()
  130. const isSingle = computed(
  131. () =>
  132. conversationStore.storeCurrentConversation.conversationType === SessionType.Single,
  133. )
  134. const actionSheetVisible = ref(false)
  135. const actionSheetActions = ref<ActionSheetAction[]>([])
  136. const uploadChooseOptions = reactive({
  137. accept: '*',
  138. capture: undefined as any,
  139. })
  140. const target = ref(null)
  141. const uploaderRef = ref<UploaderInstance>()
  142. let loadToast : ToastWrapperInstance | null = null
  143. onClickOutside(target, () => emit('closeActionBar'), {
  144. ignore: ['.van-overlay', '.van-action-sheet__content'],
  145. })
  146. const onActionSelect = ({ type } : any, idx : number) => {
  147. if (
  148. type === ChatFooterActionType.VoiceCall ||
  149. type === ChatFooterActionType.VideoCall
  150. ) {
  151. actionSheetVisible.value = false
  152. if (isSingle.value) {
  153. const rtcType = type === ChatFooterActionType.VoiceCall ? RtcType.VoiceCall : RtcType.VideoCall
  154. inviteRtc(rtcType, '', [conversationStore.currentConversation.userID])
  155. } else {
  156. router.push({
  157. path: 'groupMemberList',
  158. state: {
  159. groupID: conversationStore.storeCurrentGroupInfo.groupID,
  160. action:
  161. type === ChatFooterActionType.VoiceCall
  162. ? MemberListActionEnum.VoiceInvite
  163. : MemberListActionEnum.VideoInvite,
  164. },
  165. })
  166. }
  167. return
  168. }
  169. uploadChooseOptions.accept = idx === 0 ? 'image/*' : 'video/*'
  170. if (type === ChatFooterActionType.Shoot) {
  171. uploadChooseOptions.capture = idx === 0 ? 'camera' : 'camcorder'
  172. }
  173. nextTick(() => uploaderRef.value?.chooseFile())
  174. actionSheetVisible.value = false
  175. }
  176. const clickAction = async ({ type } : ChatFooterActionItem) => {
  177. console.log(type)
  178. switch (type) {
  179. case ChatFooterActionType.Album:
  180. actionSheetActions.value = [...albumActions]
  181. actionSheetVisible.value = true
  182. break
  183. case ChatFooterActionType.Shoot:
  184. actionSheetActions.value = [...shootActions]
  185. actionSheetVisible.value = true
  186. break
  187. case ChatFooterActionType.File:
  188. uploadChooseOptions.accept = '*'
  189. uploadChooseOptions.capture = undefined
  190. nextTick(() => uploaderRef.value?.chooseFile())
  191. break
  192. case ChatFooterActionType.VideoCall:
  193. if (!isSingle.value) {
  194. const { data } = await IMSDK.signalingGetRoomByGroupID<CallingRoomData>(
  195. conversationStore.currentConversation.groupID,
  196. )
  197. if (data.invitation) {
  198. showConfirmDialog({
  199. title: 'placeholder.hint',
  200. message: 'toast.isJoinCalling',
  201. beforeClose: (action : string) => {
  202. return new Promise((resolve) => {
  203. if (action === 'confirm') {
  204. emitter.emit('OPEN_RTC_MODAL', {
  205. invitation: data.invitation,
  206. participant: data.participant?.[0],
  207. isJoin: true,
  208. })
  209. }
  210. resolve(true)
  211. })
  212. },
  213. })
  214. return
  215. }
  216. }
  217. actionSheetActions.value = [...videoCallActions]
  218. actionSheetVisible.value = true
  219. break
  220. case ChatFooterActionType.IDCard:
  221. router.push({
  222. path: 'chooseUser',
  223. state: {
  224. chooseType: ContactChooseEnum.ChooseCard,
  225. },
  226. })
  227. break
  228. case ChatFooterActionType.Location:
  229. loadToast = showLoadingToast({
  230. message: t('messageTip.getLocation'),
  231. forbidClick: true,
  232. })
  233. if (navigator.geolocation) {
  234. navigator.geolocation.getCurrentPosition(
  235. (p) => {
  236. console.log(p)
  237. loadToast?.close()
  238. loadToast = null
  239. router.push({
  240. path: 'geolacationPage',
  241. state: {
  242. lng: p.coords.longitude,
  243. lat: p.coords.latitude,
  244. },
  245. })
  246. },
  247. (e) => {
  248. console.log(e)
  249. if (!loadToast) return
  250. loadToast.message = t('messageTip.getLocationFailed')
  251. loadToast.close()
  252. loadToast = null
  253. },
  254. )
  255. }
  256. break
  257. default:
  258. break
  259. }
  260. // emit('closeActionBar')
  261. }
  262. const afterReadFile = (data : UploaderFileListItem | UploaderFileListItem[]) => {
  263. if (!Array.isArray(data)) {
  264. data = [data]
  265. }
  266. data.map((item) => {
  267. emit('getFile', item)
  268. })
  269. }
  270. </script>
  271. <style lang="scss" scoped>
  272. :deep(.van-icon__image) {
  273. width: 48px;
  274. height: 48px;
  275. }
  276. :deep(.van-grid-item__content) {
  277. background: none;
  278. padding: 6px 8px;
  279. }
  280. </style>