소스 검색

Merge branch 'im' of https://git.nanodreamtech.com/wkw/wallet_app into im

congjiang 1 개월 전
부모
커밋
95cf11a0f4

+ 20 - 5
src/stores/modules/webSocketStore.js

@@ -3,7 +3,7 @@ import { defineStore } from "pinia";
 import { $root as protobuf } from "@/common/proto/proto";
 import * as Constant from "@/common/constant/Constant";
 import { useWalletStore } from "@/stores/modules/walletStore";
-import { getMessageApi, friendRequest } from "@/api/path/im.api";
+import { getMessageApi, friendRequest, groupList } from "@/api/path/im.api";
 import { showDialog } from 'vant';
 import { eventBus } from '@/utils/utils';
 import { useSystemStore } from "@/stores/modules/systemStore";
@@ -12,7 +12,6 @@ import {
   setMessageHook,
   handleMessageHook,
 } from "@/views/im/hook/messagesHook";
-const IM_PATH = import.meta.env.VITE_IM_PATH_FIlE;
 
 
 // 配置常量
@@ -38,6 +37,7 @@ export const useWebSocketStore = defineStore("webSocketStore", {
     reconnectAttempts: 0,
     sessionId:null,//当前会话id
     messages: [],
+    groupMembersList:{},//群成员列表
     toUserInfo: {},
     unreadMessages: [],
     uuid: null,
@@ -675,7 +675,7 @@ export const useWebSocketStore = defineStore("webSocketStore", {
         const updated = {
           ...sessions[index],
           message: lastMsg,
-          createAt: time,
+          updatedAt: time,
           unReadNum: type == 0 ? 0 : unReadNum
           // avatar
         };
@@ -689,7 +689,7 @@ export const useWebSocketStore = defineStore("webSocketStore", {
           name: message.toUsername || message.fromUsername || message.to || message.from,
           type: message.messageType == 2?'group':'user',
           message: lastMsg,
-          createAt: time,
+          updatedAt: time,
           avatar: message.avatar || '',
           unReadNum: type == 0 ? 0 : unReadNum
         };
@@ -815,6 +815,21 @@ export const useWebSocketStore = defineStore("webSocketStore", {
         systemStore.ImsessionList = sessions;
       }
     },
-    
+    // 群成员列表
+    async fetchGroupMembers(){
+      if (this.toUserInfo.type == 'group'){
+        if (this.groupMembersList[this.toUserInfo.uuid]) {
+          return this.groupMembersList[this.toUserInfo.uuid]
+        }
+        try {
+          const res = await groupList(this.toUserInfo.uuid);
+          this.groupMembersList[this.toUserInfo.uuid] = res.data || [];
+          return res.data
+        } catch (e) {
+          console.error('获取群成员失败', e);
+          return []
+        }
+      }
+    }
   },
 });

+ 20 - 29
src/views/im/chat/components/AtUserList/index.vue

@@ -23,8 +23,8 @@
                 <template #icon>
                     <van-image
                         round
-                        width="40px"
-                        height="40px"
+                        width="32px"
+                        height="32px"
                         :src="IM_PATH + user.avatar"
                     />
                 </template>
@@ -36,7 +36,6 @@
 <script setup>
 import { useWebSocketStore } from '@/stores/modules/webSocketStore.js';
 import { useWalletStore } from "@/stores/modules/walletStore.js";
-import { groupList } from '@/api/path/im.api';
 const wsStore = useWebSocketStore();
 const walletStore = useWalletStore();
 const IM_PATH = import.meta.env.VITE_IM_PATH_FIlE;
@@ -44,36 +43,13 @@ const emit = defineEmits(['select']);
 
 const show = ref(false);
 const keyword = ref('');
-const loading = ref(false);
-const finished = ref(false);
-// 模拟用户数据
 const users = ref([]);
 
-// 获取群成员信息
-const fetchGroupMembers = async () => {
-    try {
-        const res = await groupList(wsStore.toUserInfo.uuid);
-
-        users.value = res.data || [];
-    } catch (e) {
-        console.error('获取群成员失败', e);
-    }
-};
-
 // 关键字过滤
 const filteredUsers = computed(() => {
     if (!keyword.value) return users.value;
-    return users.value.filter(u => u.name.includes(keyword.value));
+    return users.value.filter(u => u.nickname.includes(keyword.value));
 });
-
-// 模拟加载更多
-// const onLoad = () => {
-//     setTimeout(() => {
-//         finished.value = true;
-//         loading.value = false;
-//     }, 1000);
-// };
-
 // 选中用户
 const selectUser = user => {
     console.log('选择用户:', user);
@@ -89,8 +65,23 @@ const close = () => {
 // 供父组件调用打开
 const open = () => {
     show.value = true;
-    fetchGroupMembers();
 };
-
+onMounted(async () => {
+  // 获取群成员信息
+  users.value = await wsStore.fetchGroupMembers();
+})
 defineExpose({ open });
 </script>
+<style scoped lang="less">
+:deep(.van-cell){
+    align-items: center;
+    
+}
+:deep(.van-cell__title){
+    font-family: PingFang SC, PingFang SC;
+    font-weight: 500;
+    font-size: 15px;
+    color: #000000;
+    margin-left: 12px;
+}
+</style>

+ 50 - 14
src/views/im/chat/index.vue

@@ -7,13 +7,14 @@
     <!-- 顶部导航 -->
     <div class="header-chat">
       <svg-icon class="page-icon" name="lf-arrow" @click="goBack" />
-      <div class="header-title">{{ wsStore.toUserInfo.nickname || '群聊' }}</div>
+      <div class="header-title" v-if="wsStore.toUserInfo.type == 'group'">{{ wsStore.toUserInfo.nickname || '群聊' }}({{ groupMembersArr.length }})</div>
+      <div v-else class="header-title">{{ wsStore.toUserInfo.nickname }}</div>
       <svg-icon class="page-icon" name="more" @click="goDetail" />
     </div>
 
     <!-- 群公告 -->
-    <div class="groupNotice" v-if="!noticeRead && wsStore.toUserInfo.type == 'group'" @click="showNotice = true">
-      <div class="m-ellipsis">{{ wsStore.toUserInfo.notice }}</div>
+    <div class="groupNotice" v-if="!noticeRead && wsStore.toUserInfo.type == 'group' && wsStore.toUserInfo.notice" @click="showNotice = true">
+      <div class="m-ellipsis">群公告:{{ wsStore.toUserInfo.notice }}</div>
       <svg-icon class="item-icon" name="right1" />
     </div>
 
@@ -35,7 +36,7 @@
               class="list-img"
               :class="isSender(item.toUsername, item) ? 'mr12' : 'ml12'"
               round
-              :src="item.sender?item.sender.avatar:item.avatar"
+              :src="formatAvatarUrl(item)"
               @click="goToPage(item)"
             />
             <!-- 内容 -->
@@ -144,6 +145,10 @@
       cancel-text="取消"
       @select="onActionSelect"
     />
+    <!-- @样式 -->
+    <div class="assign" v-if="wsStore.toUserInfo.type == 'group'">
+      <div class="assign-text">有人@我</div>
+    </div>
     <!-- 引用消息展示 -->
     <div v-if="quoteMsg" class="quote-box">
       <div class="quote-content">{{ quoteMsg.fromUsername}}:{{ renderQuoteContent(quoteMsg) }}</div>
@@ -327,7 +332,20 @@ const emojis = [
 const atList = ref();
 const showBurn = ref(false)  // 阅后即焚是否开启
 const showNotice = ref(false);
-const noticeRead = ref(false)
+const noticeRead = ref(false);
+const groupMembersArr = ref([]);
+
+const currentMsg = ref('');
+const showActionSheet = ref(false)
+const actions = ref([])
+
+const formatAvatarUrl = (item) => {
+  const url = item?.sender?.avatar || item?.avatar || ''
+  if (/^https?:\/\//.test(url)) {
+    return url
+  }
+  return IM_PATH + url
+}
 
 const isSender = (toUsername, item) => {
   if(item?.sender){
@@ -559,7 +577,7 @@ const destroyMessage = (message) => {
 const handleNoticeClose = () => {
   console.log(11)
   showNotice.value = false
-  noticeRead.value = true
+  // noticeRead.value = true
 }
 
 
@@ -670,15 +688,21 @@ const onSelectUser = (user) => {
   ccMsg.value.push(user)
 };
 
-const currentMsg = ref('');
-const showActionSheet = ref(false)
-const actions = [
-  { name: "复制", key: "copy" },
-  { name: "撤回", key: "revoke" },
-  { name: "引用", key: "quote" },
-  // { name: "置顶", key: "stick" },
-];
 const onLongPress = (msg) => {
+  if(msg.toUsername == walletStore.account){
+    actions.value = [
+      { name: "复制", key: "copy" },
+      { name: "引用", key: "quote" },
+      // { name: "置顶", key: "stick" },
+    ]
+  }else{
+    actions.value = [
+      { name: "复制", key: "copy" },
+      { name: "撤回", key: "revoke" },
+      { name: "引用", key: "quote" },
+      // { name: "置顶", key: "stick" },
+    ]
+  }
   currentMsg.value = msg;
   showActionSheet.value = true;
 };
@@ -825,6 +849,7 @@ const formatTime = (timestamp) => {
 // 页面生命周期
 onMounted(async () => {
   wsStore.toUserInfo.uuid = route.query.uuid;
+  groupMembersArr.value = await wsStore.fetchGroupMembers();
   await wsStore.getMessages({
     uuid: wsStore.toUserInfo.type == 'user'?walletStore.account : route.query.uuid,
     messageType: wsStore.toUserInfo.type == 'user'?1:2, //1:个人  2:群组
@@ -1259,6 +1284,17 @@ const goDetail = () =>{
   color: red;
   margin-left: 6px;
 }
+.assign{
+  margin-bottom: 20px;
+  display: flex;
+  justify-content: flex-end;
+  .assign-text{
+    color: #fff;
+    background: #4765dd;
+    border-radius: 30px 0 0 30px;
+    padding: 3px 10px;
+  }
+}
 // 引用样式
 .quote-box {
   display: flex;

+ 2 - 1
src/views/im/components/groupAvatar/index.vue

@@ -26,8 +26,9 @@
   
   // 拼接头像地址
   const getAvatarUrl = (img) => {
-    return IM_PATH + img;
+    return formatAvatarUrl(img);
   };
+  const formatAvatarUrl = (url) => url && /^https?:\/\//.test(url) ? url: (IM_PATH + (url || ''))
   </script>
   
   <style scoped>

+ 3 - 2
src/views/im/index.vue

@@ -37,7 +37,7 @@
         <template v-if="list.length > 0">
           <div class="list-item" v-for="(item,i) in list" :key="i" @click="goToChat(item,i)">
             <!-- 个人头像 -->
-            <van-image class="item-img" round :src="IM_PATH + item.avatar" v-if="item.type == 'user'"/>
+            <van-image class="item-img" round :src="formatAvatarUrl(item.avatar)" v-if="item.type == 'user'"/>
             <!-- 群聊头像 -->
             <template v-if="item.type == 'group'">
               <groupAvatar class="item-img" :users='item.avatars'></groupAvatar>
@@ -45,7 +45,7 @@
             <div class="item-content">
               <div class="item-top">
                 <div>{{item.nickname || '群聊'}}</div>
-                <div class="col">{{item.createAt}}</div>
+                <div class="col">{{item.updatedAt}}</div>
               </div>
               <div class="item-bottom">
                 <div class="col m-ellipsis">{{item.message || '我们已经相互关注,开始聊天吧'}}</div>
@@ -117,6 +117,7 @@ const showDot = ref(false)
 const showSheet = ref(false);
 const list = ref([]);
 
+const formatAvatarUrl = (url) => url && /^https?:\/\//.test(url) ? url: (IM_PATH + (url || ''))
 const onRefresh = () => {
   getuserList(true);
 };