Browse Source

Merge branch 'iteration-20240826' into online

lex-xin 2 months ago
parent
commit
76af0e96f3

BIN
src/TUIKit/TUIComponents/assets/icon/icon-remove.png


+ 3 - 0
src/TUIKit/TUIComponents/components/transferTUI/style/web.scss

@@ -116,6 +116,9 @@ img,
   font-size: 12Px;
   line-height: 24Px;
   border-radius: 4Px;
+  &:focus-visible, &:focus {
+    outline: none;
+  }
 }
 
 .btn-no {

+ 1 - 1
src/TUIKit/TUIComponents/container/TUIChat/components/message-tip.vue

@@ -18,7 +18,7 @@ export default defineComponent({
     const data = reactive({
       message: {},
     });
-
+1
     watchEffect(() => {
       data.message = props.data;
     });

+ 575 - 0
src/TUIKit/TUIComponents/container/TUIChat/manage-components/manage-mute.vue

@@ -0,0 +1,575 @@
+<template>
+  <main class="member">
+    <n-spin :show="muteLoading" stroke="#198CFE">
+        <div class="sectionSearch">
+            <n-input
+                class="TheSearch noBorder"
+                style="--n-font-size: 12px; --n-height: 35px; --n-caret-color: #198cfe; --n-border-hover: 1px solid #198cfe; --n-border-focus: 1px solid #198cfe; --n-loading-color: #198cfe; --n-box-shadow-focus: 0 0 0 2px rgba(25 140 254, 0.2)"
+                round
+                clearable
+                placeholder="请输入群成员名称"
+                v-model:value="keyword"
+                @keyup="onKeyup"
+            >
+                <template #prefix> <span class="icon-search-input"></span></template>
+                <template #suffix><n-button @click="debouncedRequest('')" round color="#198cfe" >搜索</n-button></template>
+            </n-input>
+        </div>
+        <ul class="list">
+        <li class="list-item" v-for="(item, index) in list" :key="index" @click="onSelectItem(item)">
+            <aside>
+            <!-- icon icon-selected -->
+            <i :class="['icon', isSelect(item) ? 'icon-selected' : 'icon-unselected']"></i>
+            <img class="avatar" :src="item?.avatar || 'https://oss.dayaedu.com/news-info/07/1690787574969.png'" onerror="this.src='https://oss.dayaedu.com/news-info/07/1690787574969.png'" />
+            <span class="name">{{ item?.nickname || item?.userID }}</span>
+            </aside>
+            <!-- <i v-if="item.role !== 'Owner' && isShowDel" class="icon icon-del" @click="submit(item)"></i> -->
+        </li>
+        <li class="list-item" v-if="list.length < total && total > 0 && !muteLoading" @click="getMore">
+            {{ $t(`TUIChat.manage.查看更多`) }}
+        </li>
+        </ul>
+        <div class="theEmpty" v-if="!muteLoading && list.length <= 0" style="height: 75%">
+            <img class="emptyImg" src="../../../assets/nomore.png" />
+            <p>暂无数据</p>
+        </div>
+        <div class="footer">
+            <div class="selectUser" @click="onSelectAll">
+                <i :class="['icon', isSelectAll ? 'icon-selected' : 'icon-unselected']"></i>
+                <span>全选(已选择{{ selectList.length || 0 }}):</span>
+            </div>
+            <div class="userInfo">
+                <div class="userList" @click="showMore = true">
+                    <img class="avatar" v-for="(item, index) in selectList" :key="index" :src="item?.avatar || 'https://oss.dayaedu.com/news-info/07/1690787574969.png'" onerror="this.src='https://oss.dayaedu.com/news-info/07/1690787574969.png'"  />
+                </div>
+                <span :class="['muteBtn', selectList.length <=0 ? 'disabled' : '']" @click="submit">
+                    {{ currentModel === 'not_mute' ? '禁言' : '解除禁言' }}
+                </span>
+            </div>
+        </div>
+
+         <div v-if="showMore" class="maskMute"></div>
+        <div :class="['selectListSection', showMore ? 'show': '']">
+            <div class="selectHeader">
+                <i class="icon-close" @click="showMore = false"></i>
+                <span class="title">已选择 ({{ selectList.length || 0 }})</span>
+                <span :class="['sureBtn', selectList.length <=0 ? 'disabled' : '']" @click="showMore = false">确定</span>
+            </div>
+            <div class="selectContainer">
+             <div class="list-item" v-for="(item, index) in selectList" :key="index" @click="onSelectItem(item)">
+                <aside>
+                  <img class="avatar" :src="item?.avatar || 'https://oss.dayaedu.com/news-info/07/1690787574969.png'" onerror="this.src='https://oss.dayaedu.com/news-info/07/1690787574969.png'" />
+                  <span class="name">{{ item?.nickname || item?.userID }}</span>
+                </aside>
+                <i class="icon icon-remove" ></i>
+              </div>
+              <div class="theEmpty" v-if="selectList.length <= 0" style="margin-top: 20px;margin-bottom: 0;">
+                <img class="emptyImg" src="../../../assets/nomore.png" />
+                <p>暂无数据</p>
+              </div>
+            </div>
+        </div>
+    </n-spin>
+  </main>
+</template>
+
+<script lang="ts">
+import { defineComponent, watchEffect, reactive, toRefs } from "vue";
+import TIM from "@tencentcloud/chat";
+import { imGroupGroupMute, imGroupMemberPage } from "../../../../api";
+import { useThrottleFn } from "@vueuse/core";
+const ManageMember = defineComponent({
+  components: {},
+  props: {
+    list: {
+      type: Array,
+      default: () => [],
+    },
+    currentModel: {
+        type: String,
+        default: '',
+    },
+    muteLoading: {
+        type: Boolean,
+        default: false
+    },
+    total: {
+        type: Number,
+        default: 0
+    },
+    conversation: {
+      type: Object,
+      default: () => ({}),
+    },
+    isShowDel: {
+      type: Boolean,
+      default: () => false,
+    },
+    self: {
+      type: Object,
+      default: () => ({}),
+    },
+  },
+  setup(props: any, ctx: any) {
+    const types: any = TIM.TYPES;
+    const data: any = reactive({
+      conversation: {},
+      currentModel: '',
+      total: 0,
+      muteLoading: false,
+      list: [],
+      page: 1,
+      isShowDel: false,
+      self: {},
+      keyword: '',
+      showMore: false, // 显示更多 
+      selectList: [],
+      isSelectAll: false, // 是否全部选择
+    });
+
+    watchEffect(() => {
+        if(data.currentModel !== props.currentModel) {
+            data.selectList = []
+        }
+        data.conversation = props.conversation;
+        data.muteLoading = props.muteLoading;
+        data.total = props.total;
+        data.isShowDel = props.isShowDel;
+        data.list = props.list;
+        data.self = props.self;
+        data.currentModel = props.currentModel;
+    });
+
+    const handleRoleName = (item: any) => {
+      const { t } = (window as any).TUIKitTUICore.config.i18n.useI18n();
+      let name = "";
+      console.log(item, data.self, "self");
+      switch (item?.groupRoleType) {
+        case "Admin":
+          name = t("TUIChat.manage.管理员");
+          break;
+        case "Owner":
+          name = t("TUIChat.manage.群主");
+          break;
+      }
+      if (name) {
+        name = `(${name})`;
+      }
+      if (item.imUserId === data.self.userID) {
+        name += ` (${t("TUIChat.manage.我")})`;
+      }
+      return name;
+    };
+
+    const submit = async (item: any) => {
+    //   ctx.emit("del", [item]);
+    try {
+        if(data.selectList.length <= 0) return
+        const userIds = data.selectList.map((item: any) => {
+            return item.userId
+        })
+        await imGroupGroupMute({
+            "groupId": data.conversation?.groupProfile?.groupID,
+            "muteTime": -1,
+            "groupMute":  data.currentModel === 'not_mute' ? true : false,
+            userIds
+        })
+        onBack()
+    } catch {
+
+    }
+    };
+
+    const onSelectItem = (item: any) => {
+        const index = data.selectList.findIndex(child => child.userId === item.userId)
+        if(index === -1) {
+            data.selectList.push(item)
+        } else {
+            data.selectList.splice(index, 1)
+        }
+
+        data.isSelectAll = data.list.length === data.selectList.length ? true : false
+       
+    }
+
+    const isSelect = (item: any) => {
+        const index = data.selectList.findIndex(child => child.userId === item.userId)
+        return index === -1 ? false : true
+    }
+
+    const onSelectAll = (item: any) => {
+        if(data.list.length === data.selectList.length) {
+            data.selectList = []
+            data.isSelectAll = false
+        } else {
+            data.selectList = [...data.list]
+            data.isSelectAll = true
+        }
+    }
+
+    const handleMemberProfileShow = (user: any) => {
+      ctx.emit("handleMemberProfileShow", user);
+    };
+
+    const onBack = () => {
+        ctx.emit("back");
+    }
+
+     const onSearch = (val: string) => {
+        ctx.emit('search', data.keyword)
+    };
+
+    const debouncedRequest = useThrottleFn((val: string) => {
+      onSearch(val);
+    }, 500);
+
+    const onKeyup = (e: any) => {
+      e.stopPropagation();
+      if (e.code === "Enter") {
+        debouncedRequest(data.keyword);
+      }
+    };
+
+    return {
+      ...toRefs(data),
+      submit,
+      isSelect,
+      debouncedRequest,
+      onKeyup,
+      onSearch,
+      onSelectItem,
+      onSelectAll,
+      handleRoleName,
+      handleMemberProfileShow,
+    };
+  },
+});
+export default ManageMember;
+</script>
+
+<style lang="scss" scoped>
+@import url("../../../styles/common.scss");
+@import url("../../../styles/icon.scss");
+.member {
+    position: relative;
+    flex: 1;
+    background: #F8F9FC;
+  
+  .list {
+    display: flex;
+    flex-direction: column;
+    // background: #f4f5f9;
+    padding-top: 12px;
+    &-item {
+      padding: 13px;
+      display: flex;
+      justify-content: space-between;
+      align-items: center;
+      background: #ffffff;
+      font-size: 14px;
+      overflow: hidden;
+      cursor: pointer;
+      aside {
+        display: flex;
+        align-items: center;
+        width: 100%;
+        overflow: hidden;
+        .avatar {
+            margin-left: 12px;
+            flex-shrink: 0;
+        }
+        .name {
+          padding-left: 8px;
+          font-weight: 400;
+          font-size: 14px;
+          color: #000000;
+          flex: 1;
+          overflow: hidden;
+          white-space: nowrap;
+          text-overflow: ellipsis;
+        }
+      }
+    }
+  }
+}
+.avatar {
+  width: 36px;
+  height: 36px;
+  border-radius: 4px;
+}
+
+
+.sectionSearch {
+  padding: 4px 12px 0;
+}
+
+::deep .TheSearch .n-input--focus {
+  .active {
+    display: block;
+  }
+
+  .default {
+    display: none;
+  }
+
+
+  .n-button {
+    opacity: 1;
+  }
+}
+
+
+.n-spin-container {
+    height: 100%;
+    :deep(.n-spin-content) {
+        min-height: 100%;
+        display: flex;
+        flex-direction: column;
+    }
+}
+
+.TheSearch {
+  border-radius: 20Px !important;
+
+  &.noBorder {
+    --n-border: none !important;
+  }
+
+    .n-button {
+        :deep(.n-button__content) {
+            color: #fff;
+        }
+    }
+
+  :deep(.n-input-wrapper) {
+    padding-left: 12Px;
+    padding-right: 4Px;
+    height: 35Px !important;
+  }
+
+  .n-button {
+    height: 26Px;
+    font-size: 12Px;
+    font-weight: 500;
+    width: auto;
+    opacity: 0.7;
+  }
+
+
+
+  .active {
+    display: none;
+  }
+
+  .active,
+  .default {
+    width: 14Px;
+    height: 14Px;
+  }
+
+  &:hover {
+
+    .n-button {
+      opacity: 1;
+    }
+
+  }
+}
+.icon-unselected, .icon-selected {
+    width: 18px;
+    height: 18px;
+    cursor: pointer;
+}
+
+.footer {
+    position: absolute;
+    bottom: 0;
+    left: 0;
+    right: 0;
+    // width: 100%;
+    padding: 0 12px;
+    height: 66px;
+    background: #FFFFFF;
+    display: flex;
+    align-items: center;
+}
+.selectUser {
+    display: flex;
+    align-items: center;
+    font-weight: 400;
+    font-size: 14px;
+    color: #777777;
+    line-height: 20px;
+    flex-shrink: 0;
+    cursor: pointer;
+    span {
+        padding-left: 12px;
+    }
+}
+.userInfo {
+    display: flex;
+    align-items: center;
+    justify-content: space-between;
+    flex: 1;
+
+    .userList {
+        max-width: 110px;
+        overflow-x: auto;
+        overflow-y: hidden;
+        display: flex;
+        align-items: center;
+        cursor: pointer;
+        .avatar {
+            width: 28px;
+            height: 28px;
+            border-radius: 50%;
+            margin-right: 5px;
+            &:last-child {
+                margin-right: 0;
+            }
+        }
+    }
+    .muteBtn {
+        cursor: pointer;
+        width: 80px;
+        line-height: 30px;
+        background: #198CFE;
+        border-radius: 20px;
+        font-weight: 500;
+        font-size: 14px;
+        color: #FFFFFF;
+        text-align: center;
+        font-style: normal;
+
+        &.disabled {
+            opacity: 0.7;
+            cursor: not-allowed;
+        }
+    }
+}
+::-webkit-scrollbar {
+//   width: 4Px;
+  height: 4Px;
+  background-color: transparent;
+}
+
+::-webkit-scrollbar-track {
+  border-radius: 10Px;
+}
+
+::-webkit-scrollbar-thumb {
+  border-radius: 10Px;
+  background-color: #d8d8d8;
+}
+
+ .theEmpty {
+   width: 100%;
+   display: flex;
+   justify-content: center;
+   align-items: center;
+   flex-direction: column;
+   flex: 1;
+   margin-bottom: 106px;
+
+   .emptyImg {
+     width: 210px;
+     height: 210px;
+   }
+ }
+
+ .selectListSection {
+  position: absolute;
+  bottom: 0;
+  left: 0;
+  right: 0;
+  background: #FFFFFF;
+  border-radius: 10px 10px 0px 0px;
+  height: 70vh;
+  overflow-x: hidden;
+  overflow-y: auto;
+  // display: none;
+  opacity: 0;
+  height: 0;
+  transition: all .2s ease;
+  &.show {
+    // display: block;
+    opacity: 1;
+    height: 70vh;
+    transition: all .2s ease;
+  }
+ }
+ .selectHeader {
+  padding: 16px 20px 16px 18px;
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  position: sticky;
+  top: 0;
+  left: 0;
+  z-index: 99;
+  background-color: #fff;
+
+  .icon-close {
+    cursor: pointer;
+  }
+
+  .title {
+    font-weight: 600;
+    font-size: 16px;
+    color: #131415;
+    line-height: 22px;
+  }
+  .sureBtn {
+    cursor: pointer;
+    width: 52px;
+    line-height: 26px;
+    background: #198CFE;
+    border-radius: 6px;
+    font-weight: 500;
+    font-size: 14px;
+    color: #FFFFFF;
+    text-align: center;
+      &.disabled {
+            opacity: 0.7;
+            cursor: not-allowed;
+        }
+  }
+ }
+
+ .selectContainer {
+  .list-item {
+    padding: 10px 20px;
+    cursor: default;
+
+    &:first-child {
+      padding-top: 0;
+    }
+    .avatar {
+      margin-left: 0 !important;
+      width: 40px;
+      height: 40px;
+      border-radius: 50%;
+    }
+    .name {
+      padding-left: 11px;
+      font-weight: 500;
+      font-size: 14px;
+      color: #333333;
+      line-height: 20px;
+    }
+
+    .icon-remove {
+      cursor: pointer;
+    }
+  }
+ }
+
+ .maskMute {
+  position: absolute;
+  left: 0;
+  right: 0;
+  top: -58px;
+  bottom: 0;
+  background:rgba(0, 0, 0, 0.6);
+ }
+</style>

+ 112 - 9
src/TUIKit/TUIComponents/container/TUIChat/manage-components/manage.vue

@@ -2,19 +2,41 @@
   <div>
     <i class="icon icon-chat-setting" @click="toggleShow"></i>
     <div class="manage" :class="[isH5 ? 'manage-h5' : '']" v-if="show" ref="dialog">
-      <header class="manage-header">
+      <header :class="['manage-header', currentTab === 'onlyMute' ? 'currentModel' : '']">
         <i class="icon icon-back" v-if="isH5 && !currentTab" @click="toggleShow"></i>
+       
         <aside class="manage-header-left">
-          <i class="icon icon-back" v-if="currentTab" @click="setTab('')"></i>
-          <main>
+          <i class="icon icon-back" v-if="currentTab" @click="() =>{
+            currentModel = 'not_mute'
+            setTab('')
+          }"></i>
+          <main v-if="currentTab !== 'onlyMute'">
             <h1>{{ $t(`TUIChat.manage.${TabName}`) }}</h1>
           </main>
+           <n-tabs
+            v-else
+              justify-content="center"
+              style="margin-left: -14Px;flex: 1; --n-tab-padding: 6px 0; --n-tab-gap: 34px; --n-tab-text-color: #000; --n-tab-text-color-hover: #0f0f0f; --n-tab-text-color-active: #000; --n-tab-font-weight-active: 600; --n-bar-color: #198cfe"
+              :bar-width="20"
+              :value="currentModel"
+              @update:value="
+                (val: any) => {
+                  currentModel = val;
+                  mutePage = 1
+                  muteSearch = ''
+                  getUserList()
+                }
+              "
+            >
+            <n-tab-pane name="not_mute" tab="未禁言"></n-tab-pane>
+            <n-tab-pane name="mute" tab="已禁言"></n-tab-pane>
+          </n-tabs>
         </aside>
-        <span>
+        <span  v-if="currentTab !== 'onlyMute'">
           <i v-if="!isH5" class="icon icon-close" @click="toggleShow"></i>
         </span>
       </header>
-      <main class="main" style="background-color: #f4f5f9; height: 100%" v-if="!currentTab">
+      <main :class="['main']" style="background-color: #f4f5f9; height: 100%" v-if="!currentTab">
         <ManageName class="space-top" :isAuth="isAuth" :isH5="isH5" :data="conversation.groupProfile" @update="updateProfile" style="background-color: #fff" />
         <div class="userInfo space-top" style="background-color: #fff">
           <header class="userInfo-header" @click="setTab('member')">
@@ -97,14 +119,29 @@
           </li> -->
         </ul>
 
-        <div v-if="currentUserDetail.groupRoleType === 'Owner'" class="admin-content space-top" style="background-color: #fff; padding-top: 12px">
+        <div v-if="currentUserDetail.groupRoleType === 'Owner' || currentUserDetail.groupRoleType === 'Admin'" class="admin-content space-top" style="background-color: #fff; padding-top: 12px;border-top: 1Px solid #f4f5f9;">
           <aside>
             <label>学生禁言</label>
           </aside>
           <Slider :open="groupDetail.configJson.mute" @change="setAllMuteTime" />
         </div>
-        <ul class="footer space-top">
-          <li v-if="currentUserDetail.groupRoleType === 'Owner' && userInfo?.list.length > 1" @click.stop="toggleMask('changeOwner')">移交群主</li>
+        <div v-if="currentUserDetail.groupRoleType === 'Owner' || currentUserDetail.groupRoleType === 'Admin'" class="admin-content space-top" style="background-color: #fff; padding-top: 12px;border-top: 1Px solid #f4f5f9;" @click.stop="() => {
+          userInfo.muteList = []
+          setTab('onlyMute')
+        }">
+          <aside>
+            <label>禁言名单</label>
+          </aside>
+          <i class="icon icon-right end"></i>
+        </div>
+         <div v-if="currentUserDetail.groupRoleType === 'Owner' && userInfo?.list.length > 1" class="admin-content space-top" style="background-color: #fff;padding-top: 12px; border-top: 1Px solid #f4f5f9;"  @click.stop="toggleMask('changeOwner')">
+          <aside>
+            <label>移交群主</label>
+          </aside>
+          <i class="icon icon-right end"></i>
+        </div>
+        <ul class="footer space-top" style="overflow: hidden; background-color: #f4f5f9;">
+          <!-- <li v-if="currentUserDetail.groupRoleType === 'Owner' && userInfo?.list.length > 1" @click.stop="toggleMask('changeOwner')">移交群主</li> -->
           <li v-if="currentUserDetail.groupRoleType === 'Owner'" @click.stop="dismiss(conversation.groupProfile)">解散群聊</li>
           <li v-else @click.stop="quit(conversation.groupProfile)">退出群组</li>
         </ul>
@@ -158,6 +195,25 @@
           </ol>
         </div>
       </main>
+      <ManageMute  v-else-if="currentTab === 'onlyMute'" :currentModel="currentModel" 
+        :total="muteTotal" 
+        :self="conversation.groupProfile.selfInfo" :list="userInfo.muteList" 
+        :conversation="conversation" 
+        :muteLoading="muteLoading"
+        @more="getUserList('more')" 
+        @del="submit" 
+        @back="() => {  
+          setTab(''); 
+          currentModel = 'not_mute' 
+        }" 
+        @search="(keyword) => {
+          mutePage = 1
+          muteSearch = keyword
+          userInfo.muteList = []
+          getUserList()
+        }" 
+        @handleMemberProfileShow="handleMemberProfileShow" />
+     
       <MaskTUI :show="mask" @update:show="(e) => (mask = e)">
         <Transfer :title="$t(`TUIChat.manage.${transferTitle}`)" :list="transferList" :isSearch="isSearch" :isRadio="isRadio" :selectedList="selectedList" @submit="submit" @cancel="cancel" @search="handleSearchMember" :isH5="isH5" />
       </MaskTUI>
@@ -191,6 +247,7 @@ import ManageName from "./manage-name.vue";
 import ManageNotification from "./manage-notification.vue";
 import ManageMember from "./manage-member.vue";
 import MemeberProfile from "./member-profile.vue";
+import ManageMute from './manage-mute.vue'
 import DialogTUI from "../../../components/dialogTUi/index.vue";
 import { imGroupChangeGroupOwner, imGroupDetail, imGroupDismiss, imGroupMemberPage, imGroupMemberSaveImAll, imGroupMemberUserDetail, imGroupMuteAll, imGroupQuit, imUserFriendPage } from "../../../../api";
 import Vuex from "vuex";
@@ -207,6 +264,7 @@ const manage = defineComponent({
     ManageNotification,
     ManageMember,
     MemeberProfile,
+    ManageMute,
     DialogTUI,
   },
   props: {
@@ -240,6 +298,7 @@ const manage = defineComponent({
       userInfo: {
         isGroup: false,
         list: [],
+        muteList: []
       },
       isShowMuteTimeInput: false,
       editLableName: "",
@@ -275,7 +334,12 @@ const manage = defineComponent({
       classGroupDetail: {}, // 群详情
       page: 1, // 当前默认查询第一页数据
       currentUserDetail: {} as any, // 用户信息
+      mutePage: 1,
+      muteSearch: '',
+      muteLoading: false,
+      muteTotal: 0,
       groupDetail: {} as any,
+      currentModel: 'not_mute'
     });
 
     const dialog: any = ref();
@@ -433,6 +497,37 @@ const manage = defineComponent({
       }
     };
 
+    // 禁言
+    const getUserList = async (type?: string) => {
+      data.muteLoading = true;
+      try {
+        const { conversation } = data;
+        const options: any = {
+          groupId: conversation?.groupProfile?.groupID,
+          rows: 100,
+          groupMute: data.currentModel === 'not_mute' ? false : true,
+          groupRoleTypes: ['Member'],
+          page: type && type === "more" ? data.mutePage + 1 : data.mutePage,
+          keyword: data.muteSearch
+        };
+        await imGroupMemberPage(options).then((res: any) => {
+          const rows = res.data.rows || [];
+          rows.forEach((row: any) => {
+            row.nick = row.nickname;
+          });
+          if (type && type === "more") {
+            data.userInfo.muteList = [...data.userInfo.list, ...rows];
+          } else {
+            data.userInfo.muteList = rows;
+          }
+          data.muteTotal = res.data.total || 0;
+        });
+      } catch {
+        //
+      }
+      data.muteLoading = false;
+    };
+
     const addMember = async (userIDList: any) => {
       const { conversation } = data;
       // const options: any = {
@@ -572,6 +667,9 @@ const manage = defineComponent({
     };
 
     const setTab = (tabName: string) => {
+      if(tabName === 'onlyMute') {
+        getUserList()
+      }
       data.currentTab = tabName;
       data.editLableName = "";
       if (data.currentTab === "member") {
@@ -580,6 +678,7 @@ const manage = defineComponent({
       if (!data.currentTab) {
         data.transferType = "";
       }
+      
     };
 
     const handleSearchMember = async (value: string) => {
@@ -637,6 +736,8 @@ const manage = defineComponent({
 
     const submit = (userList: any) => {
       console.log(userList, data.transferType, " data.transferType");
+      if(userList.length <= 0) return
+      
       if (data.transferType === "remove") {
         data.userList = userList;
         data.delDialogShow = !data.delDialogShow;
@@ -689,7 +790,7 @@ const manage = defineComponent({
           break;
         case "changeOwner":
           data.isRadio = true;
-          data.transferList = [...data.member.admin, ...data.member.member];
+          data.transferList = [...data.member.admin, ...data.member.member].filter(item => item.roleType !== "STUDENT");
           data.transferTitle = "转让群组";
           console.log(data.transferList, "data.transferList");
           break;
@@ -832,6 +933,7 @@ const manage = defineComponent({
         });
 
         data.currentUserDetail = res.data;
+
       } catch {
         //
       }
@@ -894,6 +996,7 @@ const manage = defineComponent({
       setTab,
       TabName,
       getMember,
+      getUserList,
       handleSearchMember,
       submit,
       cancel,

+ 15 - 0
src/TUIKit/TUIComponents/container/TUIChat/manage-components/style/web.scss

@@ -17,6 +17,15 @@
     justify-content: space-between;
     align-items: center;
 
+    &.currentModel {
+      background: #F8F9FC;
+      border: none;
+
+      .manage-header-left {
+        width: 100%;
+      }
+    }
+
     aside {
       display: flex;
       align-items: center;
@@ -29,8 +38,13 @@
     &-left {
       display: flex;
 
+      :deep(.n-tab-pane) {
+        padding: 0;
+      }
+
       .icon {
         margin-right: 14Px;
+        background-position: center;
       }
 
       main {
@@ -271,6 +285,7 @@
       }
     }
   }
+  
 }
 
 .input {

+ 1 - 1
src/TUIKit/TUIComponents/container/TUIChat/style/h5.scss

@@ -10,7 +10,7 @@
     z-index: 2;
 
     .setting {
-      width: 27Px;
+      // width: 27Px;
     }
   }
 

+ 2 - 2
src/TUIKit/TUIComponents/container/TUIChat/utils/utils.ts

@@ -171,9 +171,9 @@ export function handleTipMessageShowContext(message: any) {
       case TIM.TYPES.GRP_TIP_MBR_PROFILE_UPDATED:
         for (const member of message.payload.memberList) {
           if (member.muteTime > 0) {
-            options.text = `${t("message.tip.群成员")}:${member.userID}${t("message.tip.被禁言")}`;
+            options.text = `${t("message.tip.群成员")}:${member.nick || member.userID}${t("message.tip.被禁言")}`;
           } else {
-            options.text = `${t("message.tip.群成员")}:${member.userID}${t("message.tip.被取消禁言")}`;
+            options.text = `${t("message.tip.群成员")}:${member.nick || member.userID}${t("message.tip.被取消禁言")}`;
           }
         }
         break;

+ 4 - 0
src/TUIKit/TUIComponents/container/utils.ts

@@ -82,6 +82,10 @@ export function handleErrorPrompts(error: any, type: any) {
     error.message = "当前群组已解散";
     // error.replace("Error: this group does not exist", "当前群组已解散");
   }
+  // 当前当人不在群里面
+  if(error.message.indexOf('only group member can get group info') !== -1) {
+    return
+  }
   if (error?.type && error?.message) {
     TUIMessage({
       message: `${error.type} : ${error.message}` || error,

+ 7 - 0
src/TUIKit/TUIComponents/styles/icon.scss

@@ -183,6 +183,13 @@
   background-size: contain;
 }
 
+.icon-remove {
+  width: 16Px;
+  height: 16Px;
+  background: url('../assets/icon/icon-remove.png') no-repeat;
+  background-size: contain;
+}
+
 .icon-work {
   width: 36Px;
   height: 36Px;

+ 12 - 0
src/TUIKit/api.ts

@@ -100,6 +100,18 @@ export const imGroupMuteAll = (params?: any) => {
 };
 
 /**
+ * 即时通讯 - 全体禁言-单个/多个禁言
+ */
+
+export const imGroupGroupMute = (params?: any) => {
+  return request.post(api + "/imGroupMember/groupMute", {
+    data: params,
+  });
+};
+
+
+
+/**
  * 即时通讯 - 批量IM新增
  */
 export const imGroupMemberSaveImAll = (params?: any) => {

+ 3 - 0
src/index.scss

@@ -1,3 +1,6 @@
+*:focus-visible {
+  outline: none;
+}
 .imChat {
   background: #fff;
   width: 1100Px;

+ 4 - 3
src/main.ts

@@ -8,6 +8,7 @@ import {
   // create naive ui
   create,
   // component
+  NSpin,
   NButton,
   NTabPane,
   NTabs,
@@ -18,7 +19,7 @@ import {
 
 // n-tab-pane
 const naive = create({
-  components: [NButton, NTabs, NTabPane, NInput, NEmpty, NImage],
+  components: [NButton, NTabs, NTabPane, NInput, NEmpty, NImage, NSpin],
 });
 
 import qs from "query-string";
@@ -41,8 +42,8 @@ import { TUIComponents, TUICore, genTestUserSig } from "./TUIKit";
 console.log(import.meta.env.DEV, "import.meta.env.DEV");
 
 // 判断是否是测试环境的
-const SDKAppID = parseSearch.appId || hashSearch.appId || 1400799837; // import.meta.env.DEV ? 1400805079 : 1400799837; // 1400805079; // Your SDKAppID
-const secretKey = parseSearch.secretKey || hashSearch.secretKey || "37bfb220843e25e78768cadd0dc06756e460e55bd631354930a4149565a1d0c9"; //import.meta.env.DEV ? "c5f4ea6140128a36c842990446a2c89249ab886b5e1ea6893555aa635a0b3c30" : "37bfb220843e25e78768cadd0dc06756e460e55bd631354930a4149565a1d0c9"; //"c5f4ea6140128a36c842990446a2c89249ab886b5e1ea6893555aa635a0b3c30"; // Your secretKey
+const SDKAppID = parseSearch.appId || hashSearch.appId || 1400805079; // import.meta.env.DEV ? 1400805079 : 1400799837; // 1400805079; // Your SDKAppID
+const secretKey = parseSearch.secretKey || hashSearch.secretKey || "c5f4ea6140128a36c842990446a2c89249ab886b5e1ea6893555aa635a0b3c30"; //import.meta.env.DEV ? "c5f4ea6140128a36c842990446a2c89249ab886b5e1ea6893555aa635a0b3c30" : "37bfb220843e25e78768cadd0dc06756e460e55bd631354930a4149565a1d0c9"; //"c5f4ea6140128a36c842990446a2c89249ab886b5e1ea6893555aa635a0b3c30"; // Your secretKey
 const userID = parseSearch.userID; //|| "KT:140:TEACHER"; // User ID
 
 // init TUIKit