Procházet zdrojové kódy

消息删除功能

jackson před 11 měsíci
rodič
revize
84b8e45b7a

+ 13 - 0
src/api/index.ts

@@ -15,6 +15,12 @@ export function login(data: Obj) {
   })
 }
 
+export function logout() {
+  return request({
+    url: '/goim/user/logout'
+  })
+}
+
 export function getFriends() {
   return request({
     url: '/goim/get/friends',
@@ -105,6 +111,13 @@ export function noReadNum(data: Obj) {
   })
 }
 
+export function deleteMessage(data: Obj) {
+  return request({
+    url: '/goim/del/messages',
+    data
+  })
+}
+
 export function creatZoom(data: Obj) {
   return request({
     url: '/goim/zoom/creatZoom',

+ 8 - 4
src/api/tmp.txt

@@ -14,6 +14,9 @@ to: 1 //群uuid
 /goim/user/login
 参数 username password
 
+退出登录 /goim/user/logout
+参数 token
+
 注册 post
 /goim/user/register
 参数
@@ -71,14 +74,12 @@ nickname
     messageType
     page
 
-
 退出群 删除群成员 同一个接口
 /goim/del/groupMember
  参数
   token
   groupId
-  username  //要删除的会员名 或者退出的用户名 如果传的是当前用户名就是解散群
-
+  username  //要删除的会员名 或者退出的用户名 如果传的是群主就是解散群
 
 删除好友 post
 /goim/del/friend
@@ -93,7 +94,6 @@ nickname
   toId    int
   messageType  int
 
-
 先创建room
 /goim/zoom/creatZoom?token
  参数json
@@ -108,3 +108,7 @@ nickname
 /goim/zoom/token?token
  参数json
  zoomId   creatZoom返回的uuid
+
+删除消息
+/goim/del/messages
+ 参数 msgId(是个数组)

+ 11 - 5
src/store/index.ts

@@ -2,6 +2,8 @@ import {createStore} from "vuex";
 import {setLocalStorage, getLocalStorage} from "../utils";
 import {Obj, StrNumObj} from "../interface";
 import router from "../router"
+import {logout} from "../api";
+
 
 interface State {
   userInfo: StrNumObj,
@@ -24,7 +26,7 @@ export default createStore({
       isGroup: 0,
       noReadNum: 0,
       userId: 0,
-      userName: '',
+      username: '',
       nickname: '',
       email: '',
     },  //当前聊天用户的信息
@@ -36,9 +38,8 @@ export default createStore({
     userList: [],
   } as State,
   mutations: {
-    logout(state) {
-      state.userInfo.token = ''
-      setLocalStorage('userInfo', state.userInfo)
+    logout() {
+      setLocalStorage('userInfo', {})
       router.push('/')
     },
     setUserInfo(state, data) {
@@ -89,6 +90,11 @@ export default createStore({
       state.userList.unshift(obj)
     },
   },
-  actions: {},
+  actions: {
+    async logout({commit}) {
+      await logout()
+      commit('logout')
+    }
+  },
   modules: {}
 });

+ 6 - 2
src/utils/client.ts

@@ -1,4 +1,3 @@
-import store from "../store";
 import config from '../config'
 
 const rawHeaderLen = 16;
@@ -99,7 +98,12 @@ Client.prototype.createConnect = function (max: number, delay: number) {
     }
 
     function auth() {
-      let token = `{"mid": ${store.state.userInfo.id}, "room_id":"live://1000", "platform":"web", "accepts":[1000, 1001, 1002]}`;
+      let token = '';
+      if (self.options.userId) {
+        token = `{"mid": ${self.options.userId}, "room_id":"live://1000", "platform":"web", "accepts":[1000, 1001, 1002]}`;
+      } else {
+        token = `{"mid": 0, "room_id":"live://${self.options.friendId}", "platform":"web", "accepts":[1000, 1001, 1002]}`;
+      }
       let headerBuf = new ArrayBuffer(rawHeaderLen);
       let headerView = new DataView(headerBuf, 0);
       let bodyBuf = textEncoder.encode(token);

+ 0 - 134
src/utils/groupClient.ts

@@ -1,134 +0,0 @@
-import store from "../store";
-import config from '../config'
-
-const rawHeaderLen = 16;
-const packetOffset = 0;
-const headerOffset = 4;
-const verOffset = 6;
-const opOffset = 8;
-const seqOffset = 12;
-
-function Client(options: any) {
-  let MAX_CONNECT_TIMES = 10;
-  let DELAY = 15000;
-  // @ts-ignore
-  let self = this;
-  self.connectStatus = 'connecting';
-  self.options = options || {};
-  self.createConnect(MAX_CONNECT_TIMES, DELAY);
-}
-
-Client.prototype.createConnect = function (max: number, delay: number) {
-  let self = this;
-  if (max === 0) {
-    return;
-  }
-  connect();
-
-  let textDecoder = new TextDecoder();
-  let textEncoder = new TextEncoder();
-  let heartbeatInterval: any;
-
-  function connect() {
-    let ws = new WebSocket(config.socketUrl);
-    ws.binaryType = 'arraybuffer';
-    ws.onopen = function () {
-      auth();
-    }
-
-    ws.onmessage = function (evt) {
-      let data = evt.data;
-      let dataView = new DataView(data, 0);
-      let packetLen = dataView.getInt32(packetOffset);
-      let headerLen = dataView.getInt16(headerOffset);
-      let ver = dataView.getInt16(verOffset);
-      let op = dataView.getInt32(opOffset);
-      let seq = dataView.getInt32(seqOffset);
-
-      switch (op) {
-        case 8:
-          // auth reply ok
-          self.connectStatus = 'connected'
-          // send a heartbeat to server
-          heartbeat();
-          heartbeatInterval = setInterval(heartbeat, 30 * 1000);
-          break;
-        case 3:
-          // receive a heartbeat from server
-          break;
-        case 9:
-          // batch message
-          for (let offset = rawHeaderLen; offset < data.byteLength; offset += packetLen) {
-            // parse
-            let packetLen = dataView.getInt32(offset);
-            let headerLen = dataView.getInt16(offset + headerOffset);
-            let ver = dataView.getInt16(offset + verOffset);
-            let op = dataView.getInt32(offset + opOffset);
-            let seq = dataView.getInt32(offset + seqOffset);
-            let msgBody = textDecoder.decode(data.slice(offset + headerLen, offset + packetLen));
-            // callback
-            messageReceived(msgBody);
-          }
-          break;
-        default:
-          let msgBody = textDecoder.decode(data.slice(headerLen, packetLen));
-          messageReceived(msgBody);
-          break
-      }
-    }
-
-    ws.onclose = function () {
-      if (heartbeatInterval) clearInterval(heartbeatInterval);
-      self.connectStatus = 'fail'
-      setTimeout(reConnect, delay);
-    }
-
-    self.createNewConnect = function () {
-      ws.close();
-    }
-
-    function heartbeat() {
-      let headerBuf = new ArrayBuffer(rawHeaderLen);
-      let headerView = new DataView(headerBuf, 0);
-      headerView.setInt32(packetOffset, rawHeaderLen);
-      headerView.setInt16(headerOffset, rawHeaderLen);
-      headerView.setInt16(verOffset, 1);
-      headerView.setInt32(opOffset, 2);
-      headerView.setInt32(seqOffset, 1);
-      ws.send(headerBuf);
-    }
-
-    function auth() {
-      let token = `{"mid": 0, "room_id":"live://${self.options.friendId}", "platform":"web", "accepts":[1000, 1001, 1002]}`;
-      let headerBuf = new ArrayBuffer(rawHeaderLen);
-      let headerView = new DataView(headerBuf, 0);
-      let bodyBuf = textEncoder.encode(token);
-      headerView.setInt32(packetOffset, rawHeaderLen + bodyBuf.byteLength);
-      headerView.setInt16(headerOffset, rawHeaderLen);
-      headerView.setInt16(verOffset, 1);
-      headerView.setInt32(opOffset, 7);
-      headerView.setInt32(seqOffset, 1);
-      ws.send(mergeArrayBuffer(headerBuf, bodyBuf));
-    }
-
-    function messageReceived(body: string) {
-      let notify = self.options.notify;
-      if (notify) notify(body);
-    }
-
-    function mergeArrayBuffer(ab1: any, ab2: any) {
-      let u81 = new Uint8Array(ab1),
-        u82 = new Uint8Array(ab2),
-        res = new Uint8Array(ab1.byteLength + ab2.byteLength);
-      res.set(u81, 0);
-      res.set(u82, ab1.byteLength);
-      return res.buffer;
-    }
-  }
-
-  function reConnect() {
-    self.createConnect(--max, delay * 2);
-  }
-}
-
-export default Client

+ 8 - 3
src/views/ZoomLoading.vue

@@ -9,10 +9,15 @@ import {useRoute} from "vue-router";
 import config from "../config";
 
 const route = useRoute()
-const joinZoom = async () => {
+const joinZoom = () => {
   Toast.loading('加载中...')
-  let {data} = await get_zoom_token({zoomId: route.query.uuid})
-  window.location.href = `${config.zoomDomain}/#/room/?url=${config.zoomSocketUrl}&token=${data}`
+  get_zoom_token({zoomId: route.query.uuid}).then(({data}) => {
+    window.location.href = `${config.zoomDomain}/#/room/?url=${config.zoomSocketUrl}&token=${data}`
+  }).catch(() => {
+    setTimeout(() => {
+      window.close()
+    }, 3000)
+  })
 }
 joinZoom()
 </script>

+ 1 - 1
src/views/home/left/components/PersonalCenter.vue

@@ -12,7 +12,7 @@
       <van-cell title="修改头像" @click="changePicture"/>
       <van-cell title="昵称" :value="store.state.userInfo.nickname"/>
       <van-cell title="邮箱" v-if="store.state.userInfo.email" :value="store.state.userInfo.email"/>
-      <van-cell title="退出" @click="store.commit('logout')"/>
+      <van-cell title="退出" @click="store.dispatch('logout')"/>
     </van-cell-group>
     <van-popup v-model:show="show" @click="show = false" class="common-pop">
       <img :src="store.state.userInfo.avatar" alt="" v-if="store.state.userInfo.avatar">

+ 9 - 3
src/views/home/left/components/UserSearchList.vue

@@ -61,7 +61,7 @@
       <div class="title">{{ currentAction.text }}</div>
       <div class="form">
         <van-cell-group inset>
-          <van-field v-model="name" :label="currentAction.label"/>
+          <van-field v-model="name" :label="currentAction.label" label-width="60" ref="inputName"/>
         </van-cell-group>
       </div>
       <div class="footer" @click="submit">确定</div>
@@ -70,11 +70,12 @@
 </template>
 
 <script lang="ts">
-import {defineComponent, reactive, toRefs, onMounted} from "vue";
+import {defineComponent, reactive, ref, toRefs, onMounted, nextTick} from "vue";
 import {addFriend, addGroup, findUser, getFriends, noReadNum} from "../../../../api"
 import {useStore} from 'vuex'
 import {Obj, StrNumObj} from "../../../../interface"
 import {Toast} from "vant"
+import type {FieldInstance} from 'vant';
 
 interface ActionObj {
   type: number,
@@ -116,6 +117,7 @@ export default defineComponent({
       timer: 0,
     })
 
+    const inputName = ref<FieldInstance>()
     const store = useStore()
 
     const userChange = () => {
@@ -162,6 +164,9 @@ export default defineComponent({
       state.currentAction = action
       state.show = true
       state.name = ''
+      nextTick(() => {
+        inputName.value?.focus()
+      })
     }
 
     const getUserList = async () => {
@@ -239,7 +244,8 @@ export default defineComponent({
       addSearchedUser,
       selectAction,
       selectUser,
-      submit
+      inputName,
+      submit,
     }
   }
 })

+ 82 - 49
src/views/home/right/components/Chat.vue

@@ -64,16 +64,15 @@
 import V3Emoji from '../../../../components/V3Emoji.vue'
 import {computed, defineComponent, reactive, ref, toRefs, watch, nextTick, onUnmounted} from "vue";
 import Client from "../../../../utils/client";
-import GroupClient from "../../../../utils/groupClient";
 import {
   messageList,
   sendMessageToUser,
   sendMessageToGroup,
   deleteFriend,
-  deleteExitGroup, creatZoom
+  deleteExitGroup, creatZoom, deleteMessage
 } from "../../../../api";
 import {useStore} from "vuex";
-import {Obj, StrObj, StrNumObj} from "../../../../interface";
+import {Obj, StrObj} from "../../../../interface";
 import {formatDateTime} from "../../../../utils"
 import EmojiData from '../../../../assets/emojidata/emoji-data.json';
 import RE from '../../../../utils/regExp'
@@ -99,6 +98,7 @@ interface MessageObj {
   createdAt: string,
   fromUserId: number,
   fromUsername: number,
+  id: number,
   messageType: number,
   to: number
 }
@@ -128,10 +128,18 @@ export default defineComponent({
       client: null,
       showPopover: false,
       actions: {
-        1: [{value: 'createLink', text: '创建会议链接'}, {value: 'deleteFriend', text: '删除好友'}],
-        2: [{value: 'createLink', text: '创建会议链接'}, {value: 'exitGroup', text: '离开群'}],
+        1: [
+          {value: 'createLink', text: '创建会议链接'},
+          {value: 'deleteMessage', text: '删除聊天记录'},
+          {value: 'deleteFriend', text: '删除好友'},
+        ],
+        2: [
+          {value: 'createLink', text: '创建会议链接'},
+          {value: 'deleteMessage', text: '删除聊天记录'},
+          {value: 'exitGroup', text: '离开群'},
+        ],
       },
-      currentAction: {},
+      currentAction: {}
     })
 
     const optionsName = {
@@ -174,9 +182,7 @@ export default defineComponent({
         ToUsername: store.state.chatUserInfo.friendName,
         page: state.page
       }).then(({data}) => {
-        if (data === null) {
-          data = []
-        }
+        data = data || []
         if (data.length < 20) {
           state.isFinished = true;
         }
@@ -226,7 +232,8 @@ export default defineComponent({
     })
 
     // @ts-ignore
-    state.client = new Client({ //这里是私聊
+    new Client({ //这里是私聊
+      userId: store.state.userInfo.id,
       notify: function (data: string) {
         const msg = JSON.parse(data)
         if (store.state.chatUserInfo.friendId === msg.fromUserId) {
@@ -241,27 +248,31 @@ export default defineComponent({
       }
     })
 
-    watch(() => store.state.userList, val => {
-      val.forEach((item: StrNumObj) => {
-        if (item.isGroup === 2) {
-          // @ts-ignore
-          new GroupClient({ //这里是群聊
-            friendId: item.friendId,
-            notify: function (data: string) {
-              const msg = JSON.parse(data)
-              if (store.state.userInfo.id !== msg.fromUserId) {
-                if (store.state.chatUserInfo.friendId === msg.to) {
-                  state.messageList.push(msg)
-                  nextTick(() => {
-                    scrollToBottom()
-                  })
-                } else {
-                  store.commit('setNotReadMessage', msg.to)
-                  store.commit('hasNotReadMsgUserSort')
-                }
-              }
+    const groupClient: Obj = {}
+    watch(() => store.state.chatUserInfo, val => {
+      // isGroup:1是私聊,2是群聊
+      if (val.isGroup !== 2) {
+        return;
+      }
+      if (groupClient[val.friendId]) {
+        return
+      }
+      // @ts-ignore
+      groupClient[val.friendId] = new Client({ // 这里是群聊
+        friendId: val.friendId,
+        notify: function (data: string) {
+          const msg = JSON.parse(data)
+          if (store.state.userInfo.id !== msg.fromUserId) {
+            if (store.state.chatUserInfo.friendId === msg.to) {
+              state.messageList.push(msg)
+              nextTick(() => {
+                scrollToBottom()
+              })
+            } else {
+              store.commit('setNotReadMessage', msg.to)
+              store.commit('hasNotReadMsgUserSort')
             }
-          })
+          }
         }
       })
     })
@@ -277,7 +288,8 @@ export default defineComponent({
         messageType: store.state.chatUserInfo.isGroup,
         content,
         contentType: 1,
-        createdAt: new Date().toJSON()
+        createdAt: new Date().toJSON(),
+        id: 0
       }
       state.message = ''
       state.messageList.push(data)
@@ -303,6 +315,43 @@ export default defineComponent({
     const selectAction = async (action: StrObj) => {
       let chatUserInfo = store.state.chatUserInfo
       let userInfo = store.state.userInfo
+      if (action.value === 'createLink') {
+        let {data} = await creatZoom({zoomName: store.state.userInfo.username})
+        let message = window.location.origin + '/#/zoomLoading?uuid=' + data.uuid
+        Dialog.confirm({
+          title: '会议链接创建成功',
+          message,
+          cancelButtonText: '复制链接',
+          confirmButtonText: '发送链接'
+        }).then(() => {
+          state.message = message
+          sendMsg()
+          Toast(store.state.chatUserInfo.isGroup === 1 ? '会议链接已发送给好友' : '会议链接已发送到群里')
+        }).catch(() => {
+          toClipboard(message).then(() => {
+            Toast.success('复制成功')
+          })
+        })
+      }
+      if (action.value === 'deleteMessage') {
+        Dialog.confirm({
+          title: '温馨提示',
+          message: '确定要删除聊天记录吗?',
+        }).then(async () => {
+          Toast('删除中...')
+          let {data} = await messageList({
+            fromId: store.state.userInfo.id,
+            toId: store.state.chatUserInfo.friendId,
+            messageType: store.state.chatUserInfo.isGroup,
+            ToUsername: store.state.chatUserInfo.friendName,
+            page: 1,
+            pageSize: state.messageList.length
+          })
+          await deleteMessage({msgId: data.map((item: Obj) => item.id)})
+          state.messageList = []
+          Toast.success('删除成功')
+        })
+      }
       if (action.value === 'deleteFriend') {
         Dialog.confirm({
           title: '温馨提示',
@@ -316,7 +365,8 @@ export default defineComponent({
           Toast.success('删除成功')
         })
       }
-      if (action.value === 'exitGroup') { //如果自己是群主的话,那就解散群
+      //如果自己是群主的话,那就解散群
+      if (action.value === 'exitGroup') {
         Dialog.confirm({
           title: '温馨提示',
           message: `确定离开${store.state.chatUserInfo.friendName}群吗?`
@@ -331,23 +381,6 @@ export default defineComponent({
           store.commit('setIsShowRight', false)
         })
       }
-      if (action.value === 'createLink') {
-        let {data} = await creatZoom({zoomName: store.state.userInfo.username})
-        let message = window.location.origin + '/#/zoomLoading?uuid=' + data.uuid
-        Dialog.confirm({
-          title: '会议链接创建成功',
-          message,
-          cancelButtonText: '复制链接',
-          confirmButtonText: '发送链接'
-        }).then(() => {
-          state.message = message
-          sendMsg()
-        }).catch(() => {
-          toClipboard(message).then(() => {
-            Toast.success('复制成功')
-          })
-        })
-      }
     }
 
     const handleKeydown = (e: KeyboardEvent) => {

+ 20 - 6
src/views/home/right/components/GroupInfo.vue

@@ -13,7 +13,7 @@
         <div class="person">{{ memberList.length }} 人</div>
       </div>
       <ul class="set-list">
-        <li class="item" @click="show=true">
+        <li class="item" @click="addFriendToGroup">
           <van-icon name="friends" size="24" color="cornflowerblue"/>
           <div class="text">添加</div>
         </li>
@@ -31,10 +31,10 @@
     </div>
     <van-popup v-model:show="show" class="common-pop">
       <van-icon name="close" class="close" @click="show=false"/>
-      <div class="title">添加群</div>
+      <div class="title">添加好友到</div>
       <div class="form">
         <van-cell-group inset>
-          <van-field v-model="username" label="用户名"/>
+          <van-field v-model="username" label="用户名:" label-width="60" ref="inputUsername"/>
         </van-cell-group>
       </div>
       <div class="footer" @click="submit">确定</div>
@@ -43,11 +43,12 @@
 </template>
 
 <script lang="ts">
-import {defineComponent, onMounted, reactive, toRefs} from "vue";
-import {addGroupMember, groupMembers, deleteExitGroup, deleteFriend} from "../../../../api"
+import {defineComponent, onMounted, reactive, ref, toRefs, nextTick} from "vue";
+import {addGroupMember, groupMembers, deleteExitGroup, addFriend} from "../../../../api"
 import {useStore} from "vuex";
 import {Dialog, Toast} from "vant"
 import {StrNumObj} from "../../../../interface";
+import type {FieldInstance} from 'vant';
 
 interface State {
   show: boolean,
@@ -58,6 +59,7 @@ interface State {
 
 export default defineComponent({
   name: 'GroupInfo',
+  methods: {addFriend},
   setup() {
 
     const state = reactive<State>({
@@ -67,7 +69,9 @@ export default defineComponent({
       IsGroupLeader: false
     })
 
+    const inputUsername = ref<FieldInstance>()
     const store = useStore()
+
     const getGroupMembers = async () => {
       let {data} = await groupMembers({groupId: store.state.chatUserInfo.friendId})
       state.memberList = data
@@ -78,6 +82,14 @@ export default defineComponent({
       store.commit('setIsShowGroupInfo', false)
     }
 
+    const addFriendToGroup = () => {
+      state.show = true
+      state.username = ''
+      nextTick(() => {
+        inputUsername.value?.focus()
+      })
+    }
+
     const submit = async () => {
       if (!state.username.trim()) {
         return Toast('请输入用户名')
@@ -96,7 +108,7 @@ export default defineComponent({
     const removeUser = (item: StrNumObj, index: number) => {
       Dialog.confirm({
         title: '温馨提示',
-        message: `确定删除${item.username}吗?`
+        message: `确定删除${item.username}吗?`
       }).then(async () => {
         await deleteExitGroup({
           groupId: store.state.chatUserInfo.friendId,
@@ -115,6 +127,8 @@ export default defineComponent({
       ...toRefs(state),
       store,
       onClickLeft,
+      inputUsername,
+      addFriendToGroup,
       submit,
       removeUser
     }