index.vue 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688
  1. <template>
  2. <div >
  3. <div class="container">
  4. <div id="wrapper" v-if="!isLogin" >
  5. <login />
  6. </div>
  7. <div
  8. class="loading"
  9. v-else
  10. v-loading="showLoading"
  11. element-loading-text="正在拼命初始化..."
  12. element-loading-background="rgba(0, 0, 0, 0.8)"
  13. >
  14. <div class="chat-wrapper">
  15. <el-row>
  16. <el-col :xs="10" :sm="10" :md="8" :lg="8" :xl="7">
  17. <side-bar />
  18. </el-col>
  19. <el-col :xs="14" :sm="14" :md="16" :lg="16" :xl="17">
  20. <current-conversation />
  21. </el-col>
  22. </el-row>
  23. </div>
  24. <calling ref="callLayer" class="chat-wrapper"/>
  25. <image-previewer />
  26. <group-live />
  27. </div>
  28. <div class="bg"></div>
  29. </div>
  30. </div>
  31. </template>
  32. <script>
  33. import { imConfig } from '@/utils/im'
  34. import {getTlsSig } from "@/api/common";
  35. import { Notification } from 'element-ui'
  36. import { mapState } from 'vuex'
  37. import CurrentConversation from '@/components/conversation/current-conversation'
  38. import SideBar from '@/components/layout/side-bar'
  39. import Login from '@/components/user/login'
  40. import ImagePreviewer from '@/components/message/image-previewer.vue'
  41. import QrCodeList from '@/components/qr-code-list'
  42. import { translateGroupSystemNotice } from '@/utils/common'
  43. import GroupLive from '@/components/group-live/index'
  44. import Calling from '@/components/message/trtc-calling/calling-index'
  45. import { ACTION } from '@/utils/trtcCustomMessageMap'
  46. import { getOpenIM,getCbEvents } from '@/utils/openIM';
  47. import { accountCheck } from '@/api/doctor';
  48. export default {
  49. title: 'TIMSDK DEMO',
  50. data () {
  51. return {
  52. loginType: 2, // github 登录只使用默认账号登录
  53. OpenIM: null,
  54. userToken:""
  55. }
  56. },
  57. components: {
  58. Login,
  59. SideBar,
  60. CurrentConversation,
  61. ImagePreviewer,
  62. QrCodeList,
  63. GroupLive,
  64. Calling,
  65. },
  66. computed: {
  67. ...mapState({
  68. currentUserProfile: state => state.user.currentUserProfile,
  69. currentConversation: state => state.conversation.currentConversation,
  70. videoCall: state => state.conversation.videoCall,
  71. audioCall: state => state.conversation.audioCall,
  72. isLogin: state => state.imuser.isLogin,
  73. isSDKReady: state => state.imuser.isSDKReady,
  74. isBusy: state => state.video.isBusy,
  75. userID: state => state.imuser.userID,
  76. token: state => state.imuser.token,
  77. userSig: state => state.imuser.userSig,
  78. sdkAppID: state => state.imuser.sdkAppID
  79. }),
  80. // 是否显示 Loading 状态
  81. showLoading() {
  82. return !this.isSDKReady
  83. }
  84. },
  85. created() {
  86. if (process.env.IS_OPENIM){
  87. this.OpenIM = getOpenIM();
  88. this.initListener()
  89. this.getTlsSig();
  90. }
  91. },
  92. mounted() {
  93. //this.getTlsSig()
  94. },
  95. watch: {
  96. },
  97. methods: {
  98. // 修改 getTlsSig 方法
  99. getTlsSig() {
  100. accountCheck(this.$store.getters.userID).then(response => {
  101. this.userToken = response.token
  102. const config = {
  103. userID: this.$store.getters.userID,
  104. token: this.userToken,
  105. logLevel:6,
  106. platformID: 5, // 使用配置的平台ID
  107. apiAddr: 'https://web.im.ysya.top/api',
  108. wsAddr: 'wss://web.im.ysya.top/msg_gateway',
  109. dataDir: '/imdata' // 添加数据存储目录
  110. }
  111. this.OpenIM.login(config).then(() => {
  112. this.$nextTick(() => {
  113. this.checkSDKReadyState();
  114. });
  115. this.$store.commit('toggleIsLogin', true);
  116. this.$store.commit('startComputeCurrent');
  117. this.$store.commit('showMessage', {
  118. type: 'success',
  119. message: 'IM 登录成功'
  120. });
  121. })
  122. .catch((error) => {
  123. this.loading = false;
  124. console.error('登录失败:', error);
  125. this.$store.commit('showMessage', {
  126. message: `IM 登录失败:${error.message || error.errMsg || '未知错误'}`,
  127. type: 'error',
  128. });
  129. });
  130. });
  131. },
  132. // 添加SDK就绪状态检查
  133. checkSDKReadyState() {
  134. if (this.hasBindReadyEvent) return;
  135. this.hasBindReadyEvent = true;
  136. let isReady = false;
  137. const timeout = setTimeout(() => {
  138. if (!isReady) {
  139. this.$store.commit('toggleIsSDKReady', false);
  140. this.$store.commit('showMessage', {
  141. message: 'SDK初始化超时',
  142. type: 'error'
  143. });
  144. }
  145. }, 10000);
  146. this.OpenIM.on(getCbEvents().OnConnectSuccess, () => {
  147. clearTimeout(timeout);
  148. isReady = true;
  149. console.log("this.OpenIM",this.OpenIM)
  150. this.OpenIM.getSelfUserInfo().then(({ data }) => {
  151. this.$store.commit('updateCurrentUserProfile', data)
  152. })
  153. this.$store.commit('toggleIsSDKReady', true);
  154. this.$store.commit('showMessage', {
  155. type: 'success',
  156. message: 'SDK 初始化成功'
  157. });
  158. this.loadUserData();
  159. });
  160. this.OpenIM.on(getCbEvents().OnConnectFailed, (error) => {
  161. clearTimeout(timeout);
  162. this.$store.commit('toggleIsSDKReady', false);
  163. this.$store.commit('showMessage', {
  164. message: `SDK 连接失败: ${error.message}`,
  165. type: 'error'
  166. });
  167. });
  168. },
  169. // 添加加载用户数据方法
  170. loadUserData() {
  171. //查询会话列表
  172. this.OpenIM.getAllConversationList()
  173. .then(({ data }) => {
  174. // 调用成功
  175. console.log("获取到会话列表",data)
  176. this.conversationList= data
  177. this.$store.commit('updateConversationList', data)
  178. })
  179. .catch(({ errCode, errMsg }) => {
  180. // 调用失败
  181. })
  182. //查询好友列表
  183. this.OpenIM.getFriendListPage({ offset:0, count:100 })
  184. .then(({ data }) => {
  185. // 调用成功
  186. console.log("获取到好友列表",data)
  187. //this.conversationList= data
  188. this.$store.commit('updateFriendList', data)
  189. })
  190. .catch(({ errCode, errMsg }) => {
  191. // 调用失败
  192. })
  193. },
  194. selfUpdateHandler({ data }) {
  195. this.updateMessageNicknameAndFaceUrl({
  196. sendID: data.userID,
  197. senderNickname: data.nickname,
  198. senderFaceUrl: data.faceURL,
  199. });
  200. this.updateSelfInfo(data);
  201. },
  202. // 更新昵称和头像
  203. updateMessageNicknameAndFaceUrl({ sendID, senderNickname, senderFaceUrl }) {
  204. // 更新消息昵称和头像的逻辑
  205. console.log(sendID, senderNickname, senderFaceUrl);
  206. },
  207. // 更新个人信息
  208. updateSelfInfo(data) {
  209. // 更新个人信息的逻辑
  210. console.log(data);
  211. },
  212. initListener() {
  213. // 登录成功后会触发 SDK_READY 事件,该事件触发后,可正常使用 SDK 接口
  214. this.OpenIM.on(getCbEvents().OnConnectSuccess, () => {
  215. console.log("OnConnectSuccess 事件触发!"); // 调试日志
  216. this.$store.commit('toggleIsSDKReady', true);
  217. });
  218. this.OpenIM.on(getCbEvents().OnConnectFailed, this.onError);
  219. this.OpenIM.on(getCbEvents().OnKickedOffline, this.onKickOut);
  220. this.OpenIM.on(getCbEvents().OnSelfInfoUpdated, this.onSelfInfoUpdated);
  221. this.OpenIM.on(getCbEvents().OnFriendAdded,(data)=>{
  222. console.log("新增好友事件触发",data)
  223. this.onFriendListUpdated(data);
  224. });
  225. /*this.OpenIM.on(getCbEvents().OnRecvNewMessage, (message) => {
  226. console.log("收到单条消息", message);
  227. this.onReceiveMessage({data: [message]}); // 包装成数组形式
  228. });*/
  229. this.OpenIM.on(getCbEvents().OnRecvNewMessages, (data) => {
  230. console.log("收到多条消息", data);
  231. const msgList = []
  232. data.data.forEach(msg =>{
  233. if (msg.contentType!==113){
  234. msgList.push(msg)
  235. }
  236. })
  237. if (msgList.length>0){
  238. this.onReceiveMessage({data: msgList});
  239. }
  240. });
  241. /*this.OpenIM.on(getCbEvents().OnConversationChanged, (eventData) => {
  242. console.log("触发OnConversationChanged事件", eventData);
  243. try {
  244. // 1. 确保数据结构正确
  245. if (!eventData || !eventData.data) {
  246. console.warn("无效的会话更新数据", eventData);
  247. return;
  248. }
  249. // 2. 提取更新后的会话列表
  250. const updatedConversations = eventData.data;
  251. // 3. 打印调试信息
  252. console.log("更新的会话列表:", updatedConversations);
  253. // 4. 更新到Vuex store
  254. this.$store.commit('updateConversationList', updatedConversations);
  255. // 5. 可选:检查特定会话的更新
  256. updatedConversations.forEach(conv => {
  257. console.log(`会话ID: ${conv.conversationID} 已更新`, conv);
  258. });
  259. } catch (error) {
  260. console.error("处理会话更新时出错:", error);
  261. }
  262. });*/
  263. // SDK NOT READT
  264. /*this.OpenIM.on(this.OpenIM.EVENT.SDK_NOT_READY, this.onReadyStateUpdate, this)
  265. // 被踢出
  266. this.OpenIM.on(this.OpenIM.EVENT.KICKED_OUT, this.onKickOut)
  267. // SDK内部出错
  268. this.OpenIM.on(this.OpenIM.EVENT.ERROR, this.onError)
  269. // 收到新消息
  270. this.OpenIM.on(this.OpenIM.EVENT.MESSAGE_RECEIVED, this.onReceiveMessage)
  271. // 会话列表更新
  272. this.OpenIM.on(this.OpenIM.EVENT.CONVERSATION_LIST_UPDATED, this.onUpdateConversationList)
  273. // 群组列表更新
  274. this.OpenIM.on(this.OpenIM.EVENT.GROUP_LIST_UPDATED, this.onUpdateGroupList)
  275. // 网络监测
  276. this.OpenIM.on(this.OpenIM.EVENT.NET_STATE_CHANGE, this.onNetStateChange)
  277. // 已读回执
  278. this.OpenIM.on(this.OpenIM.EVENT.MESSAGE_READ_BY_PEER, this.onMessageReadByPeer)
  279. // 黑名单更新
  280. this.OpenIM.on(this.OpenIM.EVENT.FRIEND_LIST_UPDATED, this.onFriendListUpdated)
  281. this.OpenIM.on(this.OpenIM.EVENT.FRIEND_APPLICATION_LIST_UPDATED, this.onFriendApplicationListUpdated)
  282. this.OpenIM.on(this.OpenIM.EVENT.FRIEND_GROUP_LIST_UPDATED, this.onFriendGroupListUpdated)*/
  283. },
  284. onFriendApplicationListUpdated(data) {
  285. this.$store.commit('updateApplicationList', data.data.friendApplicationList)
  286. this.$store.commit('updateUnreadCount', data.data.unreadCount)
  287. },
  288. onFriendListUpdated(data) {
  289. this.$store.commit('updateFriendList', data.data)
  290. },
  291. onFriendGroupListUpdated(data) {
  292. this.$store.commit('updateFriendGroupList', data.data)
  293. },
  294. onReceiveMessage({ data: messageList }) {
  295. // let totalUnreadCount = this.tim.getTotalUnreadMessageCount();
  296. messageList.forEach(element => {
  297. //过滤掉正在输入状态
  298. if(element.sendID!=this.$store.getters.userID&&element.contentType!==113){
  299. this.$notify({
  300. title: '消息提示',
  301. message: '您有一条新的消息',
  302. type: 'success'
  303. });
  304. }
  305. });
  306. this.OpenIM.getAllConversationList()
  307. .then(({ data }) => {
  308. // 调用成功
  309. this.$store.commit('updateConversationList', data)
  310. })
  311. .catch(({ errCode, errMsg }) => {
  312. // 调用失败
  313. })
  314. console.log(messageList)
  315. //this.handleVideoMessage(messageList)
  316. this.handleQuitGroupTip(messageList)
  317. this.handleCloseGroupLive(messageList)
  318. this.$store.commit('pushCurrentMessageList', messageList)
  319. this.$store.commit('pushAvChatRoomMessageList', messageList)
  320. },
  321. onError({ data }) {
  322. if (data.message !== 'Network Error') {
  323. this.$store.commit('showMessage', {
  324. message: data.message,
  325. type: 'error'
  326. })
  327. }
  328. },
  329. onMessageReadByPeer() {
  330. },
  331. onReadyStateUpdate({ name }) {
  332. console.log("当前登录用户基本信息")
  333. const isSDKReady = name === getCbEvents().OnConnectSuccess ? true : false
  334. this.$store.commit('toggleIsSDKReady', isSDKReady)
  335. // let totalUnreadCount = this.tim.getTotalUnreadMessageCount();
  336. // console.log("收到消息数"+totalUnreadCount)
  337. if (isSDKReady) {
  338. this.OpenIM.getSelfUserInfo().then(({ data }) => {
  339. console.log("当前登录用户基本信息",data)
  340. this.$store.commit('updateCurrentUserProfile', data)
  341. })
  342. .catch(error => {
  343. this.$store.commit('showMessage', {
  344. type: 'error',
  345. message: error.message
  346. })
  347. })
  348. /*this.$store.dispatch('getBlacklist')
  349. // 登录trtc calling
  350. console.log(this.sdkAppID)
  351. this.trtcCalling.login({
  352. sdkAppID: this.sdkAppID,
  353. userID: this.userID,
  354. userSig:this.userSig
  355. })*/
  356. }
  357. },
  358. kickedOutReason(type) {
  359. /*switch (type) {
  360. case this.OpenIM.TYPES.KICKED_OUT_MULT_ACCOUNT:
  361. return '由于多实例登录'
  362. case this.OpenIM.TYPES.KICKED_OUT_MULT_DEVICE:
  363. return '由于多设备登录'
  364. case this.OpenIM.TYPES.KICKED_OUT_USERSIG_EXPIRED:
  365. return '由于 userSig 过期'
  366. default:
  367. return ''
  368. }*/
  369. },
  370. checkoutNetState(state) {
  371. /*switch (state) {
  372. case this.OpenIM.TYPES.NET_STATE_CONNECTED:
  373. return { message: '已接入网络', type: 'success' }
  374. case this.OpenIM.TYPES.NET_STATE_CONNECTING:
  375. return { message: '当前网络不稳定', type: 'warning' }
  376. case this.OpenIM.TYPES.NET_STATE_DISCONNECTED:
  377. return { message: '当前网络不可用', type: 'error' }
  378. default:
  379. return ''
  380. }*/
  381. },
  382. onNetStateChange(event) {
  383. this.$store.commit('showMessage', this.checkoutNetState(event.data.state))
  384. },
  385. onKickOut(event) {
  386. this.$store.commit('showMessage', {
  387. message: `${this.kickedOutReason(event.data.type)}被踢出,请重新登录。`,
  388. type: 'error'
  389. })
  390. this.$store.commit('toggleIsLogin', false)
  391. this.$store.commit('reset')
  392. },
  393. onUpdateConversationList(event) {
  394. if(this.isSDKReady){
  395. this.$store.commit('updateConversationList', event.data)
  396. }
  397. },
  398. onUpdateGroupList(event) {
  399. this.$store.commit('updateGroupList', event.data)
  400. },
  401. onReceiveGroupSystemNotice(event) {
  402. const isKickedout = event.data.type === 4
  403. const isCurrentConversation =
  404. `GROUP${event.data.message.payload.groupProfile.groupID}` ===
  405. this.currentConversation.conversationID
  406. // 在当前会话被踢,需reset当前会话
  407. if (isKickedout && isCurrentConversation) {
  408. this.$store.commit('resetCurrentConversation')
  409. }
  410. Notification({
  411. title: '新系统通知',
  412. message: translateGroupSystemNotice(event.data.message),
  413. duration: 3000,
  414. onClick: () => {
  415. const SystemConversationID = '@TIM#SYSTEM'
  416. this.$store.dispatch('checkoutConversation', SystemConversationID)
  417. }
  418. })
  419. },
  420. selectConversation(conversationID) {
  421. if (conversationID !== this.currentConversation.conversationID) {
  422. this.$store.dispatch('checkoutConversation',conversationID)
  423. }
  424. },
  425. isJsonStr(str) {
  426. try{
  427. JSON.parse(str)
  428. return true
  429. }catch {
  430. return false
  431. }
  432. },
  433. handleVideoMessage(messageList) {
  434. const videoMessageList = messageList.filter(
  435. message => message.contentType === 110 && this.isJsonStr(message.payload.data)
  436. )
  437. if (videoMessageList.length === 0) return
  438. const videoPayload = JSON.parse(videoMessageList[0].payload.data)
  439. if (videoPayload.action === ACTION.VIDEO_CALL_ACTION_DIALING) {
  440. if (this.isBusy) {
  441. this.$bus.$emit('busy', videoPayload, videoMessageList[0])
  442. return
  443. }
  444. this.$store.commit('GENERATE_VIDEO_ROOM', videoPayload.room_id)
  445. this.selectConversation(videoMessageList[0].conversationID) // 切换当前会话页
  446. if (videoMessageList[0].from !== this.userID) {
  447. this.$bus.$emit('isCalled')
  448. }
  449. }
  450. if (videoPayload.action === ACTION.VIDEO_CALL_ACTION_SPONSOR_CANCEL) {
  451. this.$bus.$emit('missCall')
  452. }
  453. if (videoPayload.action === ACTION.VIDEO_CALL_ACTION_REJECT) {
  454. this.$bus.$emit('isRefused')
  455. }
  456. if (videoPayload.action === ACTION.VIDEO_CALL_ACTION_SPONSOR_TIMEOUT) {
  457. this.$bus.$emit('missCall')
  458. }
  459. if (videoPayload.action === ACTION.VIDEO_CALL_ACTION_ACCEPTED) {
  460. this.$bus.$emit('isAccept')
  461. }
  462. if (videoPayload.action === ACTION.VIDEO_CALL_ACTION_HANGUP) {
  463. this.$bus.$emit('isHungUp')
  464. }
  465. if (videoPayload.action === ACTION.VIDEO_CALL_ACTION_LINE_BUSY) {
  466. this.$bus.$emit('isRefused')
  467. }
  468. if (videoPayload.action === ACTION.VIDEO_CALL_ACTION_ERROR) {
  469. this.$bus.$emit('isRefused')
  470. }
  471. },
  472. /**
  473. * 使用 window.Notification 进行全局的系统通知
  474. * @param {Message} message
  475. */
  476. notifyMe(message) {
  477. // 需检测浏览器支持和用户授权
  478. if (!('Notification' in window)) {
  479. return
  480. } else if (window.Notification.permission === 'granted') {
  481. this.handleNotify(message)
  482. } else if (window.Notification.permission !== 'denied') {
  483. window.Notification.requestPermission().then(permission => {
  484. // 如果用户同意,就可以向他们发送通知
  485. if (permission === 'granted') {
  486. this.handleNotify(message)
  487. }
  488. })
  489. }
  490. },
  491. handleNotify(message) {
  492. const notification = new window.Notification('有人提到了你', {
  493. icon: 'https://web.sdk.qcloud.com/im/assets/images/logo.png',
  494. body: message.payload.text
  495. })
  496. notification.onclick = () => {
  497. window.focus()
  498. this.$store.dispatch('checkoutConversation', message.conversationID)
  499. notification.close()
  500. }
  501. },
  502. /**
  503. * 收到有群成员退群/被踢出的groupTip时,需要将相关群成员从当前会话的群成员列表中移除
  504. * @param {Message[]} messageList
  505. */
  506. handleQuitGroupTip(messageList) {
  507. // 筛选出当前会话的退群/被踢群的 groupTip
  508. const groupTips = messageList.filter(message => {
  509. return this.currentConversation.conversationID === message.conversationID &&
  510. message.contentType === 1501 &&
  511. (message.payload.operationType === 1504 ||
  512. message.payload.operationType === 1508)
  513. })
  514. // 清理当前会话的群成员列表
  515. if (groupTips.length > 0) {
  516. groupTips.forEach(groupTip => {
  517. if (Array.isArray(groupTip.payload.userIDList) || groupTip.payload.userIDList.length > 0) {
  518. this.$store.commit('deleteGroupMemberList', groupTip.payload.userIDList)
  519. }
  520. })
  521. }
  522. },
  523. /**
  524. * 收到结束直播自定义消息,派发事件关闭组件
  525. * @param {Message[]} messageList
  526. */
  527. handleCloseGroupLive(messageList) {
  528. messageList.forEach(message => {
  529. if (this.currentConversation.conversationID === message.conversationID && message.contentType === 110) {
  530. let data = {}
  531. try {
  532. data = JSON.parse(message.payload.data)
  533. } catch(e) {
  534. data = {}
  535. }
  536. if (data.roomId && Number(data.roomStatus) === 0) {
  537. this.$bus.$emit('close-group-live')
  538. }
  539. }
  540. })
  541. },
  542. }
  543. }
  544. </script>
  545. <style lang="stylus">
  546. body {
  547. overflow: hidden;
  548. margin: 0;
  549. font-family: 'Microsoft YaHei', '微软雅黑', 'MicrosoftJhengHei', 'Lantinghei SC', 'Open Sans', Arial, 'Hiragino Sans GB', 'STHeiti', 'WenQuanYi Micro Hei', SimSun, sans-serif;
  550. // font-family "Helvetica Neue", Helvetica, Arial, "PingFang SC", "Hiragino Sans GB", "Heiti SC", "Microsoft YaHei", "WenQuanYi Micro Hei", sans-serif
  551. // text-shadow: $regular 0 0 0.05em
  552. background-color: $bg;
  553. -ms-scroll-chaining: chained;
  554. -ms-overflow-style: none;
  555. -ms-content-zooming: zoom;
  556. -ms-scroll-rails: none;
  557. -ms-content-zoom-limit-min: 100%;
  558. -ms-content-zoom-limit-max: 500%;
  559. -ms-scroll-snap-type: proximity;
  560. -ms-scroll-snap-points-x: snapList(100%, 200%, 300%, 400%, 500%);
  561. -ms-overflow-style: none;
  562. overflow: auto;
  563. div {
  564. box-sizing: border-box;
  565. &::before, &::after {
  566. box-sizing: border-box;
  567. }
  568. }
  569. }
  570. #wrapper {
  571. display: flex;
  572. justify-content: center;
  573. align-items: center;
  574. flex-direction: column;
  575. margin-top: 100px;
  576. }
  577. .container
  578. position relative
  579. // height 100vh
  580. .bg {
  581. position: absolute;
  582. // width: 100%;
  583. // height: 100%;
  584. top: 0;
  585. left: 0;
  586. z-index: -1;
  587. // background: url('~@/./assets/image/bg.jpg') no-repeat 0 0;
  588. // background-size: cover;
  589. // filter blur(67px)
  590. }
  591. .loading {
  592. width: 80%;
  593. height: $height;
  594. display: flex;
  595. justify-content: center;
  596. align-items:center;
  597. }
  598. .text-ellipsis {
  599. overflow: hidden;
  600. text-overflow: ellipsis;
  601. white-space: nowrap;
  602. }
  603. .chat-wrapper {
  604. // margin-top: 8vh;
  605. width: 100%;
  606. height: $height;
  607. // max-width: 1280px;
  608. box-shadow: 0 11px 20px 0 rgba(0, 0, 0, 0.3);
  609. .official-link {
  610. display: flex;
  611. text-decoration: none;
  612. color: #38c9ff;
  613. width: fit-content;
  614. float: right;
  615. height: 45px;
  616. align-items: center;
  617. }
  618. }
  619. /* 设置滚动条的样式 */
  620. ::-webkit-scrollbar {
  621. width: 3px;
  622. height: 3px;
  623. }
  624. /* 滚动槽 */
  625. ::-webkit-scrollbar-track {
  626. border-radius: 10px;
  627. }
  628. /* 滚动条滑块 */
  629. ::-webkit-scrollbar-thumb {
  630. border-radius: 10px;
  631. background: rgba(0, 0, 0, 0.1);
  632. }
  633. /deep/ .el-popover {
  634. width 800px
  635. position fixed
  636. left 0
  637. right 0
  638. margin auto
  639. }
  640. </style>