lex-xin 1 miesiąc temu
rodzic
commit
c3a5fc3cf7

Plik diff jest za duży
+ 762 - 5
package-lock.json


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


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


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


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


+ 39 - 1
src/TUIKit/TUIComponents/components/dialogTUi/index.vue

@@ -2,7 +2,10 @@
   <div class="dialog" :class="[isH5 ? 'dialog-h5' : '', center ? 'center' : '']" v-if="show" @click.self="toggleView">
     <main class="dialog-main" :style="!background && {'background': 'none'}">
       <header v-if="isHeaderShow">
-        <h1>{{title}}</h1>
+        <h1>
+          <img class="iconMessage" src="../../assets/icon/icon-message.png" />
+          {{title}}
+        </h1>
         <i class="icon icon-close" @click="toggleView"></i>
       </header>
       <div class="dialog-main-content">
@@ -86,3 +89,38 @@ export default defineComponent({
 });
 </script>
 <style lang="scss" scoped src="./style/dialog.scss"></style>
+<style lang="scss" scoped>
+
+.dialog-main {
+  header h1 {
+    display: flex;
+    align-content: center;
+  }
+  .iconMessage {
+    width: 20Px;
+    height: 20Px;
+    margin-right: 8Px;
+  }
+  footer {
+    .btn {
+      background: linear-gradient( 312deg, #1B7AF8 0%, #3CBBFF 100%);
+      border-radius: 5Px;
+      font-weight: 600;
+      font-size: 12Px;
+      color: #FFFFFF;
+      border: none;
+    }
+    .btn-cancel {
+      background: #F1F2F6;
+      border-radius: 5Px;
+      font-weight: 600;
+      font-size: 12Px;
+      color: #1E2022;
+      border: none;
+    }
+    .btn-no {
+      opacity: 0.6;
+    }
+  }
+}
+</style>

+ 6 - 5
src/TUIKit/TUIComponents/components/dialogTUi/style/web.scss

@@ -11,15 +11,16 @@
 
   header {
     h1 {
-      font-size: 16Px;
-      line-height: 30Px;
+      line-height: 1.2;
+      font-size: 16px;
+      color: #000000;
     }
   }
 
   &-main {
-    min-width: 368Px;
+    min-width: 458Px;
     border-radius: 10Px;
-    padding: 20Px 30Px;
+    padding: 20Px 24Px;
 
     header {
       display: flex;
@@ -30,7 +31,7 @@
     }
 
     &-content {
-      padding: 20Px 0 40Px;
+      padding: 16Px 0 20Px;
       font-size: 14Px;
     }
 

+ 78 - 42
src/TUIKit/TUIComponents/components/messageTUI/index.vue

@@ -10,60 +10,80 @@
       :style="customStyle"
       v-show="visible"
     >
-      <p v-if="!isH5">{{ message }}</p>
+      <p v-if="!isH5">
+        <svg
+          v-if="type === 'success'"
+          viewBox="0 0 48 48"
+          version="1.1"
+          xmlns="http://www.w3.org/2000/svg"
+        >
+          <g stroke="none" stroke-width="1" fill-rule="evenodd">
+            <g fill-rule="nonzero">
+              <path
+                d="M24,4 C35.045695,4 44,12.954305 44,24 C44,35.045695 35.045695,44 24,44 C12.954305,44 4,35.045695 4,24 C4,12.954305 12.954305,4 24,4 Z M32.6338835,17.6161165 C32.1782718,17.1605048 31.4584514,17.1301307 30.9676119,17.5249942 L30.8661165,17.6161165 L20.75,27.732233 L17.1338835,24.1161165 C16.6457281,23.6279612 15.8542719,23.6279612 15.3661165,24.1161165 C14.9105048,24.5717282 14.8801307,25.2915486 15.2749942,25.7823881 L15.3661165,25.8838835 L19.8661165,30.3838835 C20.3217282,30.8394952 21.0415486,30.8698693 21.5323881,30.4750058 L21.6338835,30.3838835 L32.6338835,19.3838835 C33.1220388,18.8957281 33.1220388,18.1042719 32.6338835,17.6161165 Z"
+              ></path>
+            </g>
+          </g>
+        </svg>
+
+        <svg v-if="type === 'warning' || !type" viewBox="0 0 28 28" version="1.1" xmlns="http://www.w3.org/2000/svg"><g stroke="none" stroke-width="1" fill-rule="evenodd"><g fill-rule="nonzero"><path d="M14,2 C20.6274,2 26,7.37258 26,14 C26,20.6274 20.6274,26 14,26 C7.37258,26 2,20.6274 2,14 C2,7.37258 7.37258,2 14,2 Z M14,11 C13.4477,11 13,11.4477 13,12 L13,12 L13,20 C13,20.5523 13.4477,21 14,21 C14.5523,21 15,20.5523 15,20 L15,20 L15,12 C15,11.4477 14.5523,11 14,11 Z M14,6.75 C13.3096,6.75 12.75,7.30964 12.75,8 C12.75,8.69036 13.3096,9.25 14,9.25 C14.6904,9.25 15.25,8.69036 15.25,8 C15.25,7.30964 14.6904,6.75 14,6.75 Z"></path></g></g></svg>
+
+        <svg v-if="type === 'error'" viewBox="0 0 48 48" version="1.1" xmlns="http://www.w3.org/2000/svg"><g stroke="none" stroke-width="1" fill-rule="evenodd"><g fill-rule="nonzero"><path d="M24,4 C35.045695,4 44,12.954305 44,24 C44,35.045695 35.045695,44 24,44 C12.954305,44 4,35.045695 4,24 C4,12.954305 12.954305,4 24,4 Z M17.8838835,16.1161165 L17.7823881,16.0249942 C17.3266086,15.6583353 16.6733914,15.6583353 16.2176119,16.0249942 L16.1161165,16.1161165 L16.0249942,16.2176119 C15.6583353,16.6733914 15.6583353,17.3266086 16.0249942,17.7823881 L16.1161165,17.8838835 L22.233,24 L16.1161165,30.1161165 L16.0249942,30.2176119 C15.6583353,30.6733914 15.6583353,31.3266086 16.0249942,31.7823881 L16.1161165,31.8838835 L16.2176119,31.9750058 C16.6733914,32.3416647 17.3266086,32.3416647 17.7823881,31.9750058 L17.8838835,31.8838835 L24,25.767 L30.1161165,31.8838835 L30.2176119,31.9750058 C30.6733914,32.3416647 31.3266086,32.3416647 31.7823881,31.9750058 L31.8838835,31.8838835 L31.9750058,31.7823881 C32.3416647,31.3266086 32.3416647,30.6733914 31.9750058,30.2176119 L31.8838835,30.1161165 L25.767,24 L31.8838835,17.8838835 L31.9750058,17.7823881 C32.3416647,17.3266086 32.3416647,16.6733914 31.9750058,16.2176119 L31.8838835,16.1161165 L31.7823881,16.0249942 C31.3266086,15.6583353 30.6733914,15.6583353 30.2176119,16.0249942 L30.1161165,16.1161165 L24,22.233 L17.8838835,16.1161165 L17.7823881,16.0249942 L17.8838835,16.1161165 Z"></path></g></g></svg>
+        {{ message }}
+      </p>
       <span v-if="isH5">{{ message }}</span>
     </div>
   </transition>
 </template>
 <script lang="ts">
-import { useTimeoutFn } from '@vueuse/core';
+import { useTimeoutFn } from "@vueuse/core";
 import {
   computed,
   CSSProperties,
   defineComponent,
   onMounted,
   ref,
-  watch
-} from 'vue';
+  watch,
+} from "vue";
 export default defineComponent({
-  name: 'TUIMessage',
+  name: "TUIMessage",
   props: {
     message: {
       type: String,
-      default: ''
+      default: "",
     },
     duration: {
       type: Number,
-      default: 3000
+      default: 3000,
     },
     repeatNum: {
       type: Number,
-      default: 1
+      default: 1,
     },
     id: {
       type: String,
-      default: ''
+      default: "",
     },
     onClose: {
       type: Function,
-      required: false
+      required: false,
     },
     offset: {
       type: Number,
-      default: 20
+      default: 20,
     },
     zIndex: {
       type: Number,
-      default: 0
+      default: 0,
     },
     isH5: {
       type: Boolean,
-      default: false
+      default: false,
     },
     type: {
       type: String,
-      default: ''
-    }
+      default: "",
+    },
   },
   setup(props) {
     const visible = ref(false);
@@ -96,7 +116,7 @@ export default defineComponent({
 
     const customStyle = computed<CSSProperties>(() => ({
       top: `${props.offset}Px`,
-      zIndex: props.zIndex
+      zIndex: props.zIndex,
     }));
 
     onMounted(() => {
@@ -107,55 +127,67 @@ export default defineComponent({
     const handleStyle = (type?: string) => {
       if (
         type &&
-        (type === 'error' || type === 'success' || type === 'warning')
+        (type === "error" || type === "success" || type === "warning")
       )
         return type;
-      return 'normal';
+      return "normal";
     };
 
     return {
       visible,
       customStyle,
-      handleStyle
+      handleStyle,
     };
-  }
+  },
 });
 </script>
 <style lang="scss" scoped>
-@import url('../../styles/common.scss');
-@import url('../../styles/icon.scss');
+@import url("../../styles/common.scss");
+@import url("../../styles/icon.scss");
 .message {
   position: fixed;
   left: 0;
   right: 0;
   margin: 0 auto;
-  max-width: 450Px;
+  max-width: 450px;
   width: fit-content;
   justify-content: center;
   align-items: center;
+  box-shadow: 0px 9px 28px 8px rgba(0,0,0,0.05), 0px 6px 16px 0px rgba(0,0,0,0.08), 0px 3px 6px -4px rgba(0,0,0,0.12);
+  border-radius: 6px;
+  padding: 9px 12px;
+  background: #fff;
   p {
-    box-shadow: 0 2Px 12Px 0 rgba(0, 0, 0, 0.2);
-    border-radius: 3Px;
-    padding: 10Px 15Px;
+    display: flex;
+    align-items: center;
+    // box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.2);
     width: fit-content;
     word-break: break-all;
+    font-size: 14px;
+    color: #131415;
+
+    svg {
+      margin-right: 8px;
+      width: 18Px;
+      height: 18Px;
+    }
   }
 }
 .message-h5 {
   position: absolute;
-  top: 300Px !important;
+  top: 300px !important;
   margin: 0 auto;
   width: fit-content;
   max-width: 80%;
   width: fit-content;
   justify-content: center;
   align-items: center;
-  border-radius: 5Px;
-  padding: 10Px 15Px;
+  border-radius: 5px;
+  padding: 10px 15px;
   span {
     font-family: PingFangSC-Regular;
     font-weight: 400;
-    font-size: 14Px;
+    font-size: 14px;
     letter-spacing: 0;
     text-align: center;
     word-break: break-all;
@@ -163,23 +195,27 @@ export default defineComponent({
 }
 
 .success {
-  border: 1Px solid #e4f2da;
-  background: #f2f9ec;
-  color: #7ebf50;
+  // border: 1px solid #fff;
+  // background: #fff;
+  // color: #7ebf50;
+  fill: #00B42A;
 }
 .error {
-  border: 1Px solid #fde2e2;
-  background: #fef0f0;
-  color: #f46c6e;
+  // border: 1px solid #fde2e2;
+  // background: #fef0f0;
+  // color: #f46c6e;
+  fill: #F53F3F;
 }
 .normal {
-  border: 1Px solid #e9e9eb;
-  background: #f4f4f5;
-  color: #909398;
+  // border: 1px solid #e9e9eb;
+  // background: #f4f4f5;
+  // color: #909398;
+  fill: #198CFE;
 }
 .warning {
-  border: 1Px solid #faf0e2;
-  background: #fdf8f1;
-  color: #e4b877;
+  fill: #FF7D00;
+  // border: 1px solid #faf0e2;
+  // background: #fdf8f1;
+  // color: #e4b877;
 }
 </style>

+ 448 - 27
src/TUIKit/TUIComponents/components/transferTUI/index.vue

@@ -4,29 +4,137 @@
       <i class="icon icon-back" @click="cancel"></i>
       <span class="title">{{ title }}</span>
     </header>
+    <header class="transfer-header" v-else>
+      <span class="title">{{ title }}</span>
+      <i class="icon icon-back2" style="cursor: pointer" @click="cancel"></i>
+    </header>
     <main class="main">
       <div class="left">
         <header v-if="isSearch">
-          <input type="text" @keyup.enter="handleInput" :placeholder="$t('component.请输入userID')" enterkeyhint="search" />
+          <div class="sectionSearch">
+            <n-input
+              class="TheSearch"
+              style="
+                --n-border-radius: 5px;
+                --n-font-size: 12px;
+                --n-height: 32px;
+                --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);
+              "
+              v-model:value="keyword"
+              clearable
+              placeholder="请输入关键字"
+              @clear="() => {
+                keyword = ''
+                handleInput()
+              }"
+              @keyup.enter="handleInput"
+              enterkeyhint="search"
+            >
+              <template #prefix>
+                <span class="icon-search-input"></span>
+              </template>
+            </n-input>
+          </div>
         </header>
+        <div class="checkboxAll" v-if="!isRadio">
+            <n-checkbox
+              style="
+                --n-color-checked: #198cfe;
+                --n-border-checked: 1px solid #198cfe;
+                --n-border-focus: 1px solid #198cfe;
+                --n-box-shadow-focus: 0 0 0 2px rgba(25, 140, 254, 0.3);
+                --n-border-radius: 3px;
+              "
+              v-model:checked="checkAllStatus"
+              :indeterminate="indeterminate"
+              :on-update:checked="onUpdateChecked"
+            ></n-checkbox>
+            <p>
+              全选 <span class="{styles.nums}">({{ list.length }}):</span>
+            </p>
+          </div>
         <main>
-          <ul class="list">
-            <li class="list-item" @click="selectedAll" v-if="optional.length > 1 && !isRadio">
-              <i class="icon" :class="[selectedList.length === optional.length ? 'icon-selected' : 'icon-unselected']"></i>
-              <span class="all">{{ $t("component.全选") }}</span>
-            </li>
-            <li class="list-item" v-for="(item, index) in list" :key="index" @click="selected(item)">
-              <i class="icon" :class="[item?.isDisabled && 'disabled', selectedList.indexOf(item) > -1 ? 'icon-selected' : 'icon-unselected']"></i>
+          <n-checkbox-group class="checkboxGroup"
+            v-if="!isRadio"
+            v-model:value="checkboxIds"
+            :on-update:value="onCheckStudents">
+            <n-checkbox
+              style="
+                --n-color-checked: #198cfe;
+                --n-border-checked: 1px solid #198cfe;
+                --n-border-focus: 1px solid #198cfe;
+                --n-box-shadow-focus: 0 0 0 2px rgba(25, 140, 254, 0.3);
+                --n-border-radius: 3px;
+              "
+              v-for="(item, index) in list"
+              :key="index"
+              :disabled="item?.isDisabled"
+              :value="item.id"
+            >
               <template v-if="!isCustomItem">
-                <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?.nick || item?.userID }}</span>
-                <span v-if="item?.isDisabled">({{ $t("component.已在群聊中") }})</span>
+                <div class="user-item">
+                  <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?.nick || item?.userID }}</span>
+                  <span v-if="item?.isDisabled">({{ $t("component.已在群聊中") }})</span>
+
+                  <span class="roleType" v-if="formatJobType(item.groupRoleType)">{{ formatJobType(item.groupRoleType) }}</span>
+                </div>
+              </template>
+              <template v-else>
+                <slot name="left" :data="item" />
+              </template>
+            </n-checkbox>
+          </n-checkbox-group>
+
+          <n-radio-group class="checkboxGroup"
+            v-if="isRadio"
+            v-model:value="radioId"
+            :on-update:value="onRadioStudents">
+            <n-radio
+              style="
+                --n-dot-color-active: #198cfe;
+                --n-box-shadow-active: inset 0 0 0 1px #198cfe;
+                --n-box-shadow-hover: inset 0 0 0 1px #198cfe;
+                /* --n-box-shadow-focus: inset 0 0 0 1px #18a058, 0 0 0 2px rgba(24, 160, 88, 0.2) */
+                --n-box-shadow-focus: inset 0 0 0 1px #198cfe, 0 0 0 2px rgba(25, 140, 254, 0.2);
+              "
+              v-for="(item, index) in list"
+              :key="index"
+              :disabled="item?.isDisabled"
+              :value="item.id"
+            >
+              <template v-if="!isCustomItem">
+                <div class="user-item">
+                  <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?.nick || item?.userID }}</span>
+                  <span v-if="item?.isDisabled">({{ $t("component.已在群聊中") }})</span>
+
+                  <span class="roleType" v-if="formatJobType(item.groupRoleType)">{{ formatJobType(item.groupRoleType) }}</span>
+                </div>
               </template>
               <template v-else>
                 <slot name="left" :data="item" />
               </template>
-            </li>
-          </ul>
+            </n-radio>
+          </n-radio-group>
 
           <div class="theEmtpy" v-if="optional.length <= 0" style="height: 90%">
             <img class="emptyImg" src="../../assets/nomore.png" />
@@ -35,26 +143,54 @@
         </main>
       </div>
       <div class="right">
-        <header v-if="!isH5">{{ title }}</header>
+        <!-- <header v-if="!isH5">{{ title }}</header> -->
+        <p class="selectAll" v-if="selectedList.length > 0 && !isH5">
+            当前选中<span>({{ selectedList.length}}):</span>
+          </p>
         <ul class="list" v-show="resultShow">
-          <p v-if="selectedList.length > 0 && !isH5">{{ $t("component.已选中") }}{{ selectedList.length }}{{ $t("component.人") }}</p>
-          <li class="list-item space-between" v-for="(item, index) in selectedList" :key="index">
+          <li
+            class="list-item space-between"
+            v-for="(item, index) in selectedList"
+            :key="index"
+          >
             <aside>
               <template v-if="!isCustomItem">
-                <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'" />
+                <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 v-if="!isH5">{{ item.nick || item.userID }}</span>
               </template>
               <template v-else>
                 <slot name="right" :data="item" />
               </template>
             </aside>
-            <i class="icon icon-cancel" @click="selected(item)" v-if="!isH5"></i>
+            <i
+              class="icon icon-back2"
+              @click="selected(item)"
+              v-if="!isH5"
+            ></i>
           </li>
+
+          <div class="theEmtpy" v-if="selectedList.length <= 0" style="height: 90%">
+            <img class="emptyImg" src="../../assets/nomore.png" />
+            <p>暂无数据</p>
+          </div>
         </ul>
         <footer>
-          <button class="btn btn-cancel" @click="cancel">{{ $t("component.取消") }}</button>
-          <button v-if="selectedList.length > 0" class="btn" @click="submit">{{ $t("component.完成") }}</button>
-          <button v-else class="btn btn-no" @click="submit">{{ $t("component.完成") }}</button>
+          <button class="btn btn-cancel" @click="cancel">
+            {{ $t("component.取消") }}
+          </button>
+          <button v-if="selectedList.length > 0" class="btn" @click="submit">
+            {{ $t("component.完成") }}
+          </button>
+          <button v-else class="btn btn-no" @click="submit">
+            {{ $t("component.完成") }}
+          </button>
         </footer>
       </div>
     </main>
@@ -105,14 +241,59 @@ export default defineComponent({
   },
   setup(props: any, ctx: any) {
     const data = reactive({
+      keyword: "",
       type: "",
-      list: [],
-      selectedList: [],
+      list: [] as any,
+      selectedList: [] as any,
+      checkboxIds: [] as any,
       isSearch: true,
       isCustomItem: false,
       title: "",
+      checkAllStatus: false,
+      indeterminate: false,
+
+      radioId: null as any
     });
 
+    const onCheckStudents = (val: any) => {
+      data.checkboxIds = val
+      if (data.list.length <= 0) {
+        data.indeterminate = false;
+        data.checkAllStatus = false;
+        return;
+      }
+      // 右边数据
+      data.list.forEach((item: any) => {
+        if (data.checkboxIds.includes(item.id)) {
+          const index = data.selectedList.findIndex(
+            (select: any) => select.id == item.id
+          );
+          if (index === -1) data.selectedList.push(item);
+        } else {
+          const index = data.selectedList.findIndex(
+            (select: any) => select.id == item.id
+          );
+          if(index >= 0) data.selectedList.splice(index, 1)
+        }
+      });
+
+      let count = 0;
+      data.list.forEach((item: any) => {
+        const index = data.selectedList.findIndex(
+          (select: any) => select.id === item.id
+        );
+        if(index >= 0) count++
+      })
+
+      if (count >= data.list.length) {
+        data.checkAllStatus = true;
+        data.indeterminate = false;
+      } else {
+        data.checkAllStatus = false;
+        data.indeterminate = count === 0 ? false : true;
+      }
+    }
+
     watchEffect(() => {
       if (props.isCustomItem) {
         for (let index = 0; index < props.list.length; index++) {
@@ -125,7 +306,18 @@ export default defineComponent({
       } else {
         data.list = props.list;
       }
+      // 为了处理群信息
+      data.list.forEach((item: any) => {
+        if(!item.id) {
+          item.id = item.conversationID
+        }
+      })
+
       data.selectedList = props.selectedList;
+      const ids = data.selectedList.map((item: any) => {
+        return item.id
+      })
+      onCheckStudents(ids)
       data.isSearch = props.isSearch;
       data.isCustomItem = props.isCustomItem;
       data.title = props.title;
@@ -133,10 +325,12 @@ export default defineComponent({
     });
 
     // 可选项
-    const optional = computed(() => data.list.filter((item: any) => !item.isDisabled));
+    const optional = computed(() =>
+      data.list.filter((item: any) => !item.isDisabled)
+    );
 
-    const handleInput = (e: any) => {
-      ctx.emit("search", e.target.value);
+    const handleInput = () => {
+      ctx.emit("search", data.keyword);
     };
 
     const selected = (item: any) => {
@@ -146,7 +340,12 @@ export default defineComponent({
       let list: any = data.selectedList;
       const index: number = list.indexOf(item);
       if (index > -1) {
-        return data.selectedList.splice(index, 1);
+        data.selectedList.splice(index, 1);
+        const ids = data.selectedList.map((item: any) => {
+          return item.id
+        })
+        onCheckStudents(ids)
+        return
       }
       if (props.isRadio) {
         list = [];
@@ -171,6 +370,86 @@ export default defineComponent({
       ctx.emit("cancel");
     };
 
+    const onUpdateChecked = (val: any) => {
+      if (val) {
+        const ids: any = [];
+        data.list.forEach((item: any) => {
+          ids.push(item.id);
+        });
+        ids.forEach((id: any) => {
+          if(!data.checkboxIds.includes(id)) {
+            data.checkboxIds.push(id)
+          }
+        })
+      } else {
+        const removeIds = data.list.map((item: any) => item.id)
+        removeIds.forEach((rid: any) => {
+          const index = data.checkboxIds.findIndex((id: any) => id == rid);
+          if(index !== -1) data.checkboxIds.splice(index, 1);
+          const sIndex = data.selectedList.findIndex(
+            (select: any) => select.id == rid
+          );
+          if(sIndex !== -1) data.selectedList.splice(sIndex, 1)
+        })
+        data.indeterminate = false;
+      }
+      onCheckStudents(data.checkboxIds)
+    }
+
+    const onRadioStudents = (val: any) => {
+      data.radioId = val
+      // 右边数据
+      data.list.forEach((item: any) => {
+        if (data.radioId === item.id) {
+          const index = data.selectedList.findIndex(
+            (select: any) => select.id == item.id
+          );
+          if (index === -1) data.selectedList.push(item);
+        } else {
+          const index = data.selectedList.findIndex(
+            (select: any) => select.id == item.id
+          );
+          if(index >= 0) data.selectedList.splice(index, 1)
+        }
+      });
+
+      // let count = 0;
+      // data.list.forEach((item: any) => {
+      //   const index = data.selectedList.findIndex(
+      //     (select: any) => select.id === item.id
+      //   );
+      //   if(index >= 0) count++
+      // })
+
+      // if (count >= data.list.length) {
+      //   data.checkAllStatus = true;
+      //   data.indeterminate = false;
+      // } else {
+      //   data.checkAllStatus = false;
+      //   data.indeterminate = count === 0 ? false : true;
+      // }
+    }
+
+    const formatJobType = (groupRoleType: string) => {
+      let name = ''
+      switch (groupRoleType) {
+        case "Admin":
+          name = '管理员'
+          break;
+        case "Owner":
+          name = '群主'
+          break;
+      }
+      return name
+
+      // const template = {
+      //   TEACHER: "音乐老师",
+      //   ADMIN: "管理员",
+      //   HEADMASTER: "校长",
+      // } as any;
+      // return template[jobType];
+    };
+
     return {
       ...toRefs(data),
       optional,
@@ -179,9 +458,151 @@ export default defineComponent({
       selectedAll,
       submit,
       cancel,
+      onUpdateChecked,
+      onCheckStudents,
+      onRadioStudents,
+      formatJobType
     };
   },
 });
 </script>
 
 <style lang="scss" scoped src="./style/transfer.scss"></style>
+<style lang="scss" scoped>
+::v-deep(.n-input__placeholder span) {
+    color: #aaa;
+  }
+
+.sectionSearch {
+  margin-bottom: 15px;
+}
+
+.checkboxAll {
+  display: flex;
+  align-content: center;
+  padding: 0 20px 10px;
+
+  p {
+    font-weight: 600;
+    font-size: 12px;
+    color: #131415;
+    line-height: 22px;
+    padding-left: 10px;
+    span {
+      color: #777;
+      font-weight: 400;
+    }
+  }
+}
+.checkboxGroup {
+  width: 100%;
+  .n-checkbox, .n-radio {
+    width: 100%;
+    display: flex;
+    align-items: center;
+    padding: 5px 10px;
+    margin-bottom: 8px;
+
+    &:hover {
+      background: #F5F6FA;
+      border-radius: 7px;
+    }
+  }
+
+  .user-item {
+    display: flex;
+    align-items: center;
+    font-size: 12px;
+    color: #000000;
+    line-height: 20px;
+    .avatar {
+      width: 33px;
+      height: 33px;
+    }
+    .name {
+      padding-left: 10px;
+    }
+    // span {
+    //   display: flex;
+    //   align-items: center;
+    // }
+  }
+}
+
+.main .right {
+  padding: 0 10px;
+  ul {
+    padding-right: 0;
+  }
+
+  footer {
+    padding-top: 8px;
+    padding-bottom: 20px;
+    padding-right: 10px;
+
+    .btn {
+      background: linear-gradient( 312deg, #1B7AF8 0%, #3CBBFF 100%);
+      border-radius: 5px;
+      font-weight: 600;
+      font-size: 12px;
+      color: #FFFFFF;
+      border: none;
+    }
+    .btn-cancel {
+      background: #F1F2F6;
+      border-radius: 5px;
+      font-weight: 600;
+      font-size: 12px;
+      color: #1E2022;
+      border: none;
+    }
+    .btn-no {
+      opacity: 0.6;
+    }
+  }
+}
+.selectAll {
+  padding: 23px 10px 10px;
+  font-weight: 600 !important;
+  font-size: 12px !important;
+  color: #131415 !important;
+  span {
+    color: #777777;
+    font-weight: 400;
+  }
+}
+.list {
+  .list-item {
+    padding: 5px 10px;
+    margin-bottom: 8px;
+    &:hover {
+      background: #F5F6FA;
+      border-radius: 7px;
+    }
+    .avatar {
+      width: 28px !important;
+      height: 28px !important;
+      border-radius: 5px !important;
+      margin-left: 0;
+    }
+  }
+
+  .icon-back2 {
+    width: 20px;
+    height: 20px;
+    cursor: pointer;
+  }
+}
+
+.roleType {
+  margin-left: 5px;
+  font-weight: 400;
+  font-size: 12px;
+  line-height: 17px;
+  color: #2089ff;
+  background: #e8f4ff;
+  border-radius: 3px;
+  border: 1px solid rgba(25, 140, 254, 0.5);
+  padding: 0 4px;
+}
+</style>

+ 26 - 5
src/TUIKit/TUIComponents/components/transferTUI/style/web.scss

@@ -1,10 +1,29 @@
+.transfer-header {
+  border-radius: 16Px 16Px 0 0;
+  padding: 16Px 24Px;
+  background: #F5F6FA;
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  .title {
+    font-weight: 600;
+    font-size: 16Px;
+    color: #131415;
+    line-height: 22Px;
+  }
+}
+
 .main {
   box-sizing: border-box;
-  width: 620Px;
-  height: 394Px;
+  width: 630Px;
+  height: 500Px;
   display: flex;
-  border-radius: 8Px;
-  padding: 20Px 0;
+  border-radius: 0 0 16Px 16Px;
+  box-shadow: none;
+  border: 0;
+  ::-webkit-scrollbar {
+    display: none;
+  }
 
   .right {
     padding: 0 20Px;
@@ -17,9 +36,11 @@
 
   .left {
     flex: 1;
+    padding-top: 17px;
     overflow-y: hidden;
     display: flex;
     flex-direction: column;
+    width: 315px;
 
     header {
       padding: 0 20Px;
@@ -28,7 +49,7 @@
     main {
       flex: 1;
       overflow-y: auto;
-      padding: 0 20Px;
+      padding: 0 10Px;
     }
   }
 

+ 458 - 180
src/TUIKit/TUIComponents/container/TUIChat/manage-components/manage.vue

@@ -1,25 +1,60 @@
 <template>
   <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', currentTab === 'onlyMute' ? 'currentModel' : '']">
-        <i class="icon icon-back" v-if="isH5 && !currentTab" @click="toggleShow"></i>
-       
+    <div
+      class="manage"
+      :class="[isH5 ? 'manage-h5' : '']"
+      v-if="show"
+      ref="dialog"
+    >
+      <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="() =>{
-            currentModel = 'not_mute'
-            setTab('')
-          }"></i>
+          <i
+            class="icon icon-back"
+            v-if="currentTab"
+            @click="
+              () => {
+                currentModel = 'not_mute';
+                setTab('');
+              }
+            "
+          ></i>
           <main v-if="currentTab !== 'onlyMute'">
-            <h1>{{ TabName }} <span v-if="currentTab === 'member'"> {{ groupDetail.memberNum || 0 }}{{ $t(`TUIChat.manage.人`) }} </span></h1>
+            <h1>
+              {{ TabName }}
+              <span v-if="currentTab === 'member'">
+                {{ groupDetail.memberNum || 0 }}{{ $t(`TUIChat.manage.人`) }}
+              </span>
+            </h1>
           </main>
-           <n-tabs
+          <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="
+            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
@@ -27,43 +62,79 @@
                   getUserList()
                 }
               "
-            >
+          >
             <n-tab-pane name="not_mute" tab="未禁言"></n-tab-pane>
             <n-tab-pane name="mute" tab="已禁言"></n-tab-pane>
           </n-tabs>
         </aside>
-        <span  v-if="currentTab !== 'onlyMute'">
+        <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">
-        <ManageName class="space-top" :isAuth="isAuth" :isH5="isH5" :data="conversation.groupProfile" @update="updateProfile" style="background-color: #fff" />
+      <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')">
             <label>{{ $t(`TUIChat.manage.群成员`) }}</label>
             <p>
-              <span> {{ groupDetail.memberNum || 0 }}{{ $t(`TUIChat.manage.人`) }} </span>
+              <span>
+                {{ groupDetail.memberNum || 0 }}{{ $t(`TUIChat.manage.人`) }}
+              </span>
               <i class="icon icon-right"></i>
             </p>
           </header>
           <ol>
             <!-- @click="handleMemberProfileShow(item)" -->
-            <dl v-for="(item, index) in userInfo?.list?.slice(0, showUserNum)" :key="index">
+            <dl
+              v-for="(item, index) in userInfo?.list?.slice(0, showUserNum)"
+              :key="index"
+            >
               <dt>
-                <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'" />
+                <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'"
+                />
               </dt>
               <dd>{{ item?.nickname || item?.userID }}</dd>
             </dl>
             <!-- v-if="isShowAddMember" -->
             <dl v-if="currentUserDetail.groupRoleType === 'Owner'">
-              <dt class="avatar" @click="toggleMask('add')">+</dt>
+              <dt @click="toggleMask('add')" style="cursor: pointer">
+                <img src="../../../assets/icon/icon-add.png" class="avatar" />
+              </dt>
+              <dd>添加</dd>
+            </dl>
+            <dl v-if="currentUserDetail.groupRoleType === 'Owner'">
+              <dt @click="toggleMask('remove')" style="cursor: pointer">
+                <img
+                  class="avatar"
+                  src="../../../assets/icon/icon-remove2.png"
+                />
+              </dt>
+              <dd>移除</dd>
             </dl>
-            <!-- <dl v-if="conversation.groupProfile.selfInfo.role === 'Owner'">
-              <dt class="avatar" @click="toggleMask('remove')">-</dt>
-            </dl> -->
           </ol>
         </div>
-        <ul class="content space-top" @click="editLableName = ''" style="background-color: #fff">
+        <ul
+          class="content space-top"
+          @click="editLableName = ''"
+          style="background-color: #fff"
+        >
           <li @click.stop="setTab('notification')">
             <aside>
               <label>{{ $t(`TUIChat.manage.群公告`) }}</label>
@@ -119,45 +190,133 @@
           </li> -->
         </ul>
 
-        <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;">
+        <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" />
+          <Slider
+            :open="groupDetail.configJson.mute"
+            @change="setAllMuteTime"
+          />
         </div>
-        <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')
-        }">
+        <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')">
+        <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;">
+        <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-if="currentUserDetail.groupRoleType === 'Owner'"
+            @click.stop="dismiss(conversation.groupProfile)"
+          >
+            解散群聊
+          </li>
           <li v-else @click.stop="quit(conversation.groupProfile)">退出群组</li>
         </ul>
       </main>
-      <ManageMember v-else-if="currentTab === 'member'" :self="conversation.groupProfile.selfInfo" :list="userInfo.list" :total="~~conversation.groupProfile.memberCount" @more="getMember('more')" @del="submit" @handleMemberProfileShow="handleMemberProfileShow" />
+      <ManageMember
+        v-else-if="currentTab === 'member'"
+        :self="conversation.groupProfile.selfInfo"
+        :list="userInfo.list"
+        :total="~~conversation.groupProfile.memberCount"
+        @more="getMember('more')"
+        @del="submit"
+        @handleMemberProfileShow="handleMemberProfileShow"
+      />
       <!-- :isShowDel="conversation.groupProfile.selfInfo.role === 'Owner'" -->
-      <MemeberProfile v-else-if="currentTab === 'profile'" :userInfo="currentMember" />
-      <ManageNotification ref="manageNotificationRef" v-else-if="currentTab === 'notification' || currentTab === 'notificationAdd' || currentTab === 'notificationUpdate'" :isAuth="isAuth" :data="conversation.groupProfile" @update="updateProfile" @changeStatus="onNotificationChangeStatus" />
-      <ManageNotificationDetail ref="manageNotificationDetailRef" v-else-if="currentTab === 'notificationDetail' || currentTab === 'notificationAddDetail' || currentTab === 'notificationUpdateDetail'" :noticeId="noticeId" :isAuth="isAuth" :data="conversation.groupProfile" @changeStatus="onNotificationChangeStatus" />
+      <MemeberProfile
+        v-else-if="currentTab === 'profile'"
+        :userInfo="currentMember"
+      />
+      <ManageNotification
+        ref="manageNotificationRef"
+        v-else-if="
+          currentTab === 'notification' ||
+          currentTab === 'notificationAdd' ||
+          currentTab === 'notificationUpdate'
+        "
+        :isAuth="isAuth"
+        :data="conversation.groupProfile"
+        @update="updateProfile"
+        @changeStatus="onNotificationChangeStatus"
+      />
+      <ManageNotificationDetail
+        ref="manageNotificationDetailRef"
+        v-else-if="
+          currentTab === 'notificationDetail' ||
+          currentTab === 'notificationAddDetail' ||
+          currentTab === 'notificationUpdateDetail'
+        "
+        :noticeId="noticeId"
+        :isAuth="isAuth"
+        :data="conversation.groupProfile"
+        @changeStatus="onNotificationChangeStatus"
+      />
       <main class="admin" v-else-if="currentTab === 'admin'">
         <div class="admin-list" v-if="isAdmin">
           <label>{{ $t(`TUIChat.manage.群管理员`) }}</label>
           <ol>
             <dl v-for="(item, index) in member.admin" :key="index">
               <dt>
-                <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'" />
+                <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'"
+                />
               </dt>
               <dd>{{ item?.nick || item?.userID }}</dd>
             </dl>
@@ -165,7 +324,13 @@
               <dt class="avatar" @click="toggleMask('addAdmin')">+</dt>
             </dl>
             <dl>
-              <dt class="avatar" v-if="member.admin.length > 0" @click="toggleMask('removeAdmin')">-</dt>
+              <dt
+                class="avatar"
+                v-if="member.admin.length > 0"
+                @click="toggleMask('removeAdmin')"
+              >
+                -
+              </dt>
             </dl>
           </ol>
         </div>
@@ -173,17 +338,29 @@
           <aside>
             <label>{{ $t(`TUIChat.manage.学生禁言`) }}</label>
             <p>
-              {{ $t(`TUIChat.manage.学生禁言开启后,只允许群主和管理员发言。`) }}
+              {{
+                $t(`TUIChat.manage.学生禁言开启后,只允许群主和管理员发言。`)
+              }}
             </p>
           </aside>
-          <Slider :open="conversation.groupProfile.muteAllMembers" @change="setAllMuteTime" />
+          <Slider
+            :open="conversation.groupProfile.muteAllMembers"
+            @change="setAllMuteTime"
+          />
         </div>
         <div class="admin-list last" v-if="isSetMuteTime">
           <label>{{ $t(`TUIChat.manage.单独禁言人员`) }}</label>
           <ol>
             <dl v-for="(item, index) in member.muteMember" :key="index">
               <dt>
-                <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'" />
+                <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'"
+                />
               </dt>
               <dd>{{ item?.nick || item?.userID }}</dd>
             </dl>
@@ -191,34 +368,65 @@
               <dt class="avatar" @click="toggleMask('addMute')">+</dt>
             </dl>
             <dl>
-              <dt class="avatar" v-if="member.muteMember.length > 0" @click="toggleMask('removeMute')">-</dt>
+              <dt
+                class="avatar"
+                v-if="member.muteMember.length > 0"
+                @click="toggleMask('removeMute')"
+              >
+                -
+              </dt>
             </dl>
           </ol>
         </div>
       </main>
-      <ManageMute  v-else-if="currentTab === 'onlyMute'" :currentModel="currentModel" 
-        :total="muteTotal" 
-        :self="conversation.groupProfile.selfInfo" :list="userInfo.muteList" 
-        :conversation="conversation" 
+      <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" />
-     
+        @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" />
+        <Transfer
+          :title="transferTitle"
+          :list="transferList"
+          :isSearch="isSearch"
+          :isRadio="isRadio"
+          :selectedList="selectedList"
+          @submit="submit"
+          @cancel="cancel"
+          @search="handleSearchMember"
+          :isH5="isH5"
+        />
       </MaskTUI>
-      <DialogTUI :title="$t(`TUIChat.manage.删除成员`)" :show="delDialogShow" :isH5="isH5" :center="true" :isHeaderShow="!isH5" @submit="handleManage(userList, 'remove')" @update:show="(e) => (delDialogShow = e)">
+      <DialogTUI
+        :title="$t(`TUIChat.manage.删除成员`)"
+        :show="delDialogShow"
+        :isH5="isH5"
+        :center="true"
+        :isHeaderShow="!isH5"
+        @submit="handleManage(userList, 'remove')"
+        @update:show="(e) => (delDialogShow = e)"
+      >
         <p v-if="userList.length === 1" class="delDialog-title">
           {{ $t(`TUIChat.manage.确定从群聊中删除该成员?`) }}
         </p>
@@ -227,10 +435,26 @@
         </p>
       </DialogTUI>
 
-      <DialogTUI title="退出群聊" :show="quitDialogShow" :isH5="isH5" :center="true" :isHeaderShow="!isH5" @submit="handleManage([], 'quit')" @update:show="(e) => (quitDialogShow = e)">
+      <DialogTUI
+        title="退出群聊"
+        :show="quitDialogShow"
+        :isH5="isH5"
+        :center="true"
+        :isHeaderShow="!isH5"
+        @submit="handleManage([], 'quit')"
+        @update:show="(e) => (quitDialogShow = e)"
+      >
         <p class="delDialog-title">是否退出群聊?</p>
       </DialogTUI>
-      <DialogTUI title="解散群聊" :show="dimmisDialogShow" :isH5="isH5" :center="true" :isHeaderShow="!isH5" @submit="handleManage([], 'dismiss')" @update:show="(e) => (dimmisDialogShow = e)">
+      <DialogTUI
+        title="解散群聊"
+        :show="dimmisDialogShow"
+        :isH5="isH5"
+        :center="true"
+        :isHeaderShow="!isH5"
+        @submit="handleManage([], 'dismiss')"
+        @update:show="(e) => (dimmisDialogShow = e)"
+      >
         <p class="delDialog-title">是否解散群聊?</p>
       </DialogTUI>
     </div>
@@ -239,7 +463,16 @@
 
 <script lang="ts">
 import TUIMessage from "../../../components/messageTUI/index";
-import { defineComponent, watchEffect, reactive, toRefs, computed, watch, ref, onMounted } from "vue";
+import {
+  defineComponent,
+  watchEffect,
+  reactive,
+  toRefs,
+  computed,
+  watch,
+  ref,
+  onMounted,
+} from "vue";
 import { onClickOutside } from "@vueuse/core";
 import MaskTUI from "../../../components/maskTUI/mask.vue";
 import Transfer from "../../../components/transferTUI/index.vue";
@@ -249,9 +482,20 @@ import ManageNotification from "./manage-notification.vue";
 import ManageNotificationDetail from "./manage-notification-detail.vue";
 import ManageMember from "./manage-member.vue";
 import MemeberProfile from "./member-profile.vue";
-import ManageMute from './manage-mute.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 {
+  imGroupChangeGroupOwner,
+  imGroupDetail,
+  imGroupDismiss,
+  imGroupMemberPage,
+  imGroupMemberRemoveMemberList,
+  imGroupMemberSaveImAll,
+  imGroupMemberUserDetail,
+  imGroupMuteAll,
+  imGroupQuit,
+  imUserFriendPage,
+} from "../../../../api";
 import Vuex from "vuex";
 import { handleErrorPrompts } from "../../utils";
 import useClipboard from "vue-clipboard3";
@@ -301,7 +545,7 @@ const manage = defineComponent({
       userInfo: {
         isGroup: false,
         list: [],
-        muteList: []
+        muteList: [],
       },
       isShowMuteTimeInput: false,
       editLableName: "",
@@ -309,7 +553,7 @@ const manage = defineComponent({
       currentTab: "",
       noticeId: "", // 查看公告详情时
       transferType: "",
-      isSearch: false,
+      isSearch: true,
       isRadio: false,
       transferList: [],
       selectedList: [],
@@ -330,6 +574,7 @@ const manage = defineComponent({
       userList: [],
       transferTitle: "",
       member: {
+        friendList: [],
         admin: [],
         member: [],
         muteMember: [],
@@ -339,16 +584,16 @@ const manage = defineComponent({
       page: 1, // 当前默认查询第一页数据
       currentUserDetail: {} as any, // 用户信息
       mutePage: 1,
-      muteSearch: '',
+      muteSearch: "",
       muteLoading: false,
       muteTotal: 0,
       groupDetail: {} as any,
-      currentModel: 'not_mute'
+      currentModel: "not_mute",
     });
 
     const dialog: any = ref();
-    const manageNotificationRef: any = ref()
-    const manageNotificationDetailRef: any = ref()
+    const manageNotificationRef: any = ref();
+    const manageNotificationDetailRef: any = ref();
 
     watchEffect(() => {
       data.conversation = props.conversation;
@@ -356,7 +601,10 @@ const manage = defineComponent({
       data.show = props.show;
     });
 
-    const VuexStore = ((window as any)?.TUIKitTUICore?.isOfficial && (Vuex as any)?.useStore()) || {};
+    const VuexStore =
+      ((window as any)?.TUIKitTUICore?.isOfficial &&
+        (Vuex as any)?.useStore()) ||
+      {};
 
     const TabName = computed(() => {
       let name = "";
@@ -365,17 +613,17 @@ const manage = defineComponent({
           name = "群公告";
           break;
         case "notificationAdd":
-          case "notificationAddDetail":
-          name = '新建群公告';
+        case "notificationAddDetail":
+          name = "新建群公告";
           break;
         case "notificationUpdate":
         case "notificationUpdateDetail":
-          name = '编辑群公告';
+          name = "编辑群公告";
           break;
         case "notificationDetail":
           name = "公告详情";
           break;
-         case "member":
+        case "member":
           name = "群成员";
           break;
         case "profile":
@@ -413,7 +661,9 @@ const manage = defineComponent({
           return item;
         });
         const time: number = new Date().getTime();
-        data.member.muteMember = newValue.filter((item: any) => item?.muteUntil * 1000 - time > 0);
+        data.member.muteMember = newValue.filter(
+          (item: any) => item?.muteUntil * 1000 - time > 0
+        );
       },
       { deep: true }
     );
@@ -522,10 +772,10 @@ const manage = defineComponent({
         const options: any = {
           groupId: conversation?.groupProfile?.groupID,
           rows: 100,
-          groupMute: data.currentModel === 'not_mute' ? false : true,
-          groupRoleTypes: ['Member'],
+          groupMute: data.currentModel === "not_mute" ? false : true,
+          groupRoleTypes: ["Member"],
           page: type && type === "more" ? data.mutePage + 1 : data.mutePage,
-          keyword: data.muteSearch
+          keyword: data.muteSearch,
         };
         await imGroupMemberPage(options).then((res: any) => {
           const rows = res.data.rows || [];
@@ -649,20 +899,34 @@ const manage = defineComponent({
       };
       await GroupServer.setGroupMemberMuteTime(options);
       if (type === "add") {
-        (window as any)?.TUIKitTUICore?.isOfficial && VuexStore?.commit && VuexStore?.commit("handleTask", 4);
+        (window as any)?.TUIKitTUICore?.isOfficial &&
+          VuexStore?.commit &&
+          VuexStore?.commit("handleTask", 4);
       }
       getMember();
     };
 
+    /** 移除成员 */
     const kickedOut = async (userIDList: any) => {
+      console.log("🚀 ~ :909 ~ kickedOut ~ userIDList:", userIDList)
       const { conversation } = data;
-      const options: any = {
-        groupID: conversation.groupProfile.groupID,
-        userIDList,
-        reason: "",
-      };
-      await GroupServer.deleteGroupMember(options);
-      getMember();
+      // const options: any = {
+      //   groupID: conversation.groupProfile.groupID,
+      //   userIDList,
+      //   reason: "",
+      // };
+      // await GroupServer.deleteGroupMember(options);
+
+      try {
+        const options: any = {
+          groupId: conversation.groupProfile.groupID,
+          imUserIds: userIDList,
+        };
+        await imGroupMemberRemoveMemberList(options)
+
+        getGroupDetail()
+        getMember();
+      } catch {}
     };
 
     const edit = (labelName: string) => {
@@ -671,22 +935,22 @@ const manage = defineComponent({
 
     /** 群公告修改 */
     const onNotificationChangeStatus = (type: string) => {
-      if(type === "add") {
-        data.currentTab = 'notificationAdd'
-      } else if(type === "edit") {
-        data.currentTab = 'notificationUpdate'
-      } else if(type === "submit") {
-        data.currentTab = 'notification'
-      } else if(type === "addDetail") {
-        data.currentTab = 'notificationAddDetail'
-      } else if(type === "editDetail") {
-        data.currentTab = 'notificationUpdateDetail'
-      } else if(type === "submitDetail") {
-        data.currentTab = 'notificationDetail'
-      } else if(type === "deleteDetail") {
-        data.currentTab = ""
+      if (type === "add") {
+        data.currentTab = "notificationAdd";
+      } else if (type === "edit") {
+        data.currentTab = "notificationUpdate";
+      } else if (type === "submit") {
+        data.currentTab = "notification";
+      } else if (type === "addDetail") {
+        data.currentTab = "notificationAddDetail";
+      } else if (type === "editDetail") {
+        data.currentTab = "notificationUpdateDetail";
+      } else if (type === "submitDetail") {
+        data.currentTab = "notificationDetail";
+      } else if (type === "deleteDetail") {
+        data.currentTab = "";
       }
-    }
+    };
 
     const updateProfile = async (params: any) => {
       const { key, value } = params;
@@ -703,19 +967,25 @@ const manage = defineComponent({
     };
 
     const setTab = (tabName: string) => {
-      if(data.currentTab === "notificationAdd" || data.currentTab === "notificationUpdate") {
-        manageNotificationRef.value?.onCloseEdit()
-        data.currentTab = 'notification'
-        return
+      if (
+        data.currentTab === "notificationAdd" ||
+        data.currentTab === "notificationUpdate"
+      ) {
+        manageNotificationRef.value?.onCloseEdit();
+        data.currentTab = "notification";
+        return;
       }
-      if(data.currentTab === "notificationAddDetail" || data.currentTab === "notificationUpdateDetail") {
-        manageNotificationDetailRef.value?.onCloseEdit()
-        data.currentTab = 'notificationDetail'
-        return
+      if (
+        data.currentTab === "notificationAddDetail" ||
+        data.currentTab === "notificationUpdateDetail"
+      ) {
+        manageNotificationDetailRef.value?.onCloseEdit();
+        data.currentTab = "notificationDetail";
+        return;
       }
 
-      if(tabName === 'onlyMute') {
-        getUserList()
+      if (tabName === "onlyMute") {
+        getUserList();
       }
       data.currentTab = tabName;
       data.editLableName = "";
@@ -725,56 +995,47 @@ const manage = defineComponent({
       if (!data.currentTab) {
         data.transferType = "";
       }
-      
     };
 
     const handleSearchMember = async (value: string) => {
-      let imResponse: any = {};
-      let imMemberResponse: any = {};
-      const options: any = {
-        groupID: data.conversation.groupProfile.groupID,
-        userIDList: [value],
-      };
+      // let imResponse: any = {};
+      // let imMemberResponse: any = {};
+      // const options: any = {
+      //   groupID: data.conversation.groupProfile.groupID,
+      //   userIDList: [],
+      // };
+      console.log(data.transferType, "data.transferType");
       switch (data.transferType) {
         case "add":
-          try {
-            imMemberResponse = await GroupServer.getGroupMemberProfile(options);
-            data.transferList = data.transferList.filter((item: any) => item.userID !== imResponse.data[0]?.userID);
-            data.transferList = [...data.transferList, ...imResponse.data];
-            if (imMemberResponse?.data?.memberList.length > 0) {
-              data.transferList = data.transferList.map((item: any) => {
-                if (item.userID === imMemberResponse?.data?.memberList[0].userID) {
-                  item.isDisabled = true;
-                }
-                return item;
-              });
-            }
-          } catch (error) {
-            const message = t("TUIChat.manage.该用户不存在");
-            handleErrorPrompts(message, props);
-          }
+          const list = data.member.friendList.filter((item: any) =>
+            item?.nick.includes(value)
+          );
+          data.transferList = list;
           break;
         case "remove":
           try {
-            imResponse = await GroupServer.getGroupMemberProfile(options);
-            if (imResponse.data.memberList.length === 0) {
-              const message = t("TUIChat.manage.该用户不在群组内");
-              return handleErrorPrompts(message, props);
-            }
-            data.transferList = data.transferList.filter(
+            const transferList = data.userInfo.list.filter(
               (item: any) =>
                 // eslint-disable-next-line no-unsafe-optional-chaining
-                item.userID !== imResponse?.data?.memberList[0]?.userID
+                item.imUserId !==
+                  data.conversation?.groupProfile?.selfInfo.userID &&
+                item?.nick.includes(value)
             );
-            data.transferList = [
-              ...data.transferList,
-              // eslint-disable-next-line no-unsafe-optional-chaining
-              ...imResponse?.data?.memberList,
-            ];
-          } catch (error) {
-            const message = t("TUIChat.manage.该用户不存在");
-            handleErrorPrompts(message, props);
-          }
+            data.transferList = transferList;
+          } catch (error) {}
+          break;
+        case "changeOwner":
+          const transferList = [
+            ...data.member.admin,
+            ...data.member.member,
+          ].filter(
+            (item) => item.roleType !== "STUDENT" && item?.nick.includes(value)
+          );
+          console.log(transferList, "transferList", value, [
+            ...data.member.admin,
+            ...data.member.member,
+          ])
+          data.transferList = transferList;
           break;
         default:
           break;
@@ -782,9 +1043,7 @@ const manage = defineComponent({
     };
 
     const submit = (userList: any) => {
-      console.log(userList, data.transferType, " data.transferType");
-      if(userList.length <= 0) return
-      
+      if (userList.length <= 0) return;
       if (data.transferType === "remove") {
         data.userList = userList;
         data.delDialogShow = !data.delDialogShow;
@@ -796,12 +1055,16 @@ const manage = defineComponent({
 
     const friendList = async () => {
       const list = await imUserFriendPage({ page: 1, rows: 999 });
-      console.log(list, "lits");
       const fristList = list.data.rows || [];
       fristList.forEach((item: any) => {
         item.nick = item.friendNickname;
       });
-      return fristList.filter((item: any) => !data.userInfo.list.some((infoItem: any) => infoItem.imUserId === item.imUserId));
+      return fristList.filter(
+        (item: any) =>
+          !data.userInfo.list.some(
+            (infoItem: any) => infoItem.imUserId === item.imUserId
+          )
+      );
     };
 
     const cancel = () => {
@@ -813,17 +1076,19 @@ const manage = defineComponent({
       switch (type) {
         case "add":
           data.isRadio = false;
-          data.transferList = await friendList();
-          data.transferTitle = "添加成员";
+          const list = await friendList();
+          data.member.friendList = list;
+          data.transferList = list;
+          data.transferTitle = "添加群成员";
           break;
         case "remove":
           data.isRadio = false;
           data.transferList = data.userInfo.list.filter(
             (item: any) =>
               // eslint-disable-next-line no-unsafe-optional-chaining
-              item.userID !== data.conversation?.groupProfile?.selfInfo.userID
+              item.imUserId !== data.conversation?.groupProfile?.selfInfo.userID
           );
-          data.transferTitle = "删除成员";
+          data.transferTitle = "移除群成员";
           break;
         case "addAdmin":
           data.isRadio = true;
@@ -837,9 +1102,11 @@ const manage = defineComponent({
           break;
         case "changeOwner":
           data.isRadio = true;
-          data.transferList = [...data.member.admin, ...data.member.member].filter(item => item.roleType !== "STUDENT");
+          data.transferList = [
+            ...data.member.admin,
+            ...data.member.member,
+          ].filter((item) => item.roleType !== "STUDENT");
           data.transferTitle = "转让群组";
-          console.log(data.transferList, "data.transferList");
           break;
         case "addMute":
           data.isRadio = true;
@@ -928,8 +1195,13 @@ const manage = defineComponent({
           imGroupQuit({
             id: data.conversation.groupProfile.groupID,
           }).then(() => {
-            eventGlobal.emit("removeGroup", data.conversation.groupProfile.groupID);
-            manage.TUIServer.deleteConversation(data.conversation.conversationID);
+            eventGlobal.emit(
+              "removeGroup",
+              data.conversation.groupProfile.groupID
+            );
+            manage.TUIServer.deleteConversation(
+              data.conversation.conversationID
+            );
             manage.TUIServer.store.conversation = {};
             toggleShow();
           });
@@ -940,8 +1212,13 @@ const manage = defineComponent({
             id: data.conversation.groupProfile.groupID,
           })
             .then(() => {
-              eventGlobal.emit("removeGroup", data.conversation.groupProfile.groupID);
-              manage.TUIServer.deleteConversation(data.conversation.conversationID);
+              eventGlobal.emit(
+                "removeGroup",
+                data.conversation.groupProfile.groupID
+              );
+              manage.TUIServer.deleteConversation(
+                data.conversation.conversationID
+              );
               manage.TUIServer.store.conversation = {};
               toggleShow();
             })
@@ -980,7 +1257,6 @@ const manage = defineComponent({
         });
 
         data.currentUserDetail = res.data;
-
       } catch {
         //
       }
@@ -989,7 +1265,9 @@ const manage = defineComponent({
     /** 获取群详情 */
     const getGroupDetail = async () => {
       try {
-        const res = await imGroupDetail({ id: data?.conversation?.groupProfile?.groupID });
+        const res = await imGroupDetail({
+          id: data?.conversation?.groupProfile?.groupID,
+        });
         const tdata = res.data;
         tdata.configJson = tdata.configJson ? JSON.parse(tdata.configJson) : {};
         data.groupDetail = tdata;
@@ -1009,20 +1287,20 @@ const manage = defineComponent({
             //
           }
         } else {
-          data.currentTab = ''
+          data.currentTab = "";
         }
       }
     );
 
     onMounted(() => {
-      eventGlobal.on('handleNotice', (id: any) => {
+      eventGlobal.on("handleNotice", (id: any) => {
         data.show = !data.show;
-        data.noticeId = id
-        data.currentTab = "notificationDetail"
+        data.noticeId = id;
+        data.currentTab = "notificationDetail";
         if (data.show) {
           getMember();
         }
-      })
+      });
       try {
         getGroupDetail();
         getGroupMemberUserDetail();

+ 7 - 6
src/TUIKit/TUIComponents/container/TUIChat/manage-components/style/web.scss

@@ -97,7 +97,7 @@
           flex: 0 0 36Px;
           display: flex;
           flex-direction: column;
-          padding-right: 20Px;
+          padding-right: 12Px;
 
           &:last-child {
             padding-right: 0;
@@ -113,6 +113,7 @@
             max-width: 50Px;
             font-size: 12px;
             overflow: hidden;
+            color: #777;
             text-overflow: ellipsis;
             white-space: nowrap;
           }
@@ -177,7 +178,7 @@
         cursor: pointer;
         width: 100%;
         font-size: 14Px;
-        padding: 10px 0;
+        padding: 8px 0;
         font-weight: 600;
         text-align: center;
         background: #198CFE;
@@ -314,8 +315,8 @@
 }
 
 .avatar {
-  width: 36Px;
-  height: 36Px;
+  width: 42Px;
+  height: 42Px;
   border-radius: 4Px;
   font-size: 12Px;
   display: flex;
@@ -353,8 +354,8 @@
 }
 
 .delDialog-title {
-  text-align: center;
-  padding: 20Px 0;
+  text-align: left;
+  padding-left: 28Px;
 }
 
 .icon-chat-setting {

+ 3 - 2
src/TUIKit/TUIComponents/container/TUIChat/plugin-components/replies/style/web.scss

@@ -7,9 +7,9 @@
   border-radius: 8Px 0 0 8Px;
   position: absolute;
   right: 0;
-  height: calc(100% - 40Px);
+  height: calc(100% - 55Px);
   z-index: 2;
-  top: 40Px;
+  top: 55Px;
   background: #ffffff;
   box-shadow: 0 1Px 10Px 0 rgb(2 16 43 / 15%);
 
@@ -33,6 +33,7 @@
 
     &-close {
       width: 20Px;
+      cursor: pointer;
 
       i {
         width: 12Px;

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

@@ -257,6 +257,13 @@
   background-size: contain;
 }
 
+.icon-back2 {
+  width: 24Px;
+  height: 24Px;
+  background: url('../assets/icon/icon-close.png') no-repeat;
+  background-size: contain;
+}
+
 .icon-left-arrow {
   width: 27Px;
   height: 27Px;

+ 9 - 0
src/TUIKit/api.ts

@@ -138,6 +138,15 @@ export const imGroupMemberSaveImAll = (params?: any) => {
 };
 
 /**
+ * 即时通讯 - 批量IM移除
+ */
+export const imGroupMemberRemoveMemberList = (params?: any) => {
+  return request.post(api + "/imGroupMember/removeMemberList", {
+    data: params,
+  });
+};
+
+/**
  * 即时通讯 - 解散群聊
  */
 export const imGroupDismiss = (params?: any) => {

+ 8 - 4
src/main.ts

@@ -17,12 +17,16 @@ import {
   NImage,
   NScrollbar,
   NPopover,
-  NTooltip
+  NTooltip,
+  NCheckbox,
+  NCheckboxGroup,
+  NRadio,
+  NRadioGroup
 } from "naive-ui";
 
 // n-tab-pane
 const naive = create({
-  components: [NButton, NTabs, NTabPane, NInput, NEmpty, NImage, NSpin, NScrollbar, NPopover, NTooltip],
+  components: [NButton, NTabs, NTabPane, NInput, NEmpty, NImage, NSpin, NScrollbar, NPopover, NTooltip, NCheckbox, NCheckboxGroup, NRadio, NRadioGroup],
 });
 
 import qs from "query-string";
@@ -46,9 +50,9 @@ console.log(import.meta.env.DEV, "import.meta.env.DEV");
 
 // 判断是否是测试环境的
 // import.meta.env.DEV ? 1400805079 : 1400799837; // 1400805079; // Your SDKAppID
-const SDKAppID = parseSearch.appId || hashSearch.appId || 1400799837; 
+const SDKAppID = parseSearch.appId || hashSearch.appId || 1400805079; 
 //import.meta.env.DEV ? "c5f4ea6140128a36c842990446a2c89249ab886b5e1ea6893555aa635a0b3c30" : "37bfb220843e25e78768cadd0dc06756e460e55bd631354930a4149565a1d0c9"; //"c5f4ea6140128a36c842990446a2c89249ab886b5e1ea6893555aa635a0b3c30"; // Your secretKey
-const secretKey = parseSearch.secretKey || hashSearch.secretKey || "37bfb220843e25e78768cadd0dc06756e460e55bd631354930a4149565a1d0c9"; 
+const secretKey = parseSearch.secretKey || hashSearch.secretKey || "c5f4ea6140128a36c842990446a2c89249ab886b5e1ea6893555aa635a0b3c30"; 
 const userID = parseSearch.userID; //|| "KT:140:TEACHER"; // User ID
 
 // init TUIKit

+ 1 - 1
vite.config.ts

@@ -12,7 +12,7 @@ function pathResolve(dir: string) {
 // function resolve(dir: string) {
 //   return path.join(__dirname, dir);
 // }
-const proxyUrl = "https://test.kt.colexiu.com";
+const proxyUrl = "https://dev.kt.colexiu.com";
 // https://vitejs.dev/config/
 export default defineConfig({
   base: "./",

Niektóre pliki nie zostały wyświetlone z powodu dużej ilości zmienionych plików