index.vue 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281
  1. <template>
  2. <div
  3. class="transfer"
  4. :class="[!isPC ? 'transfer-h5' : '', isMobile ? 'transfer-h5-wechat' : '']"
  5. >
  6. <header v-if="!isPC" class="transfer-header transfer-h5-header">
  7. <div v-if="!props.isHiddenBackIcon" @click="cancel">
  8. <Icon class="icon" :file="backIcon" :width="'18px'" :height="'18px'" />
  9. </div>
  10. <span class="title">{{ transferTitle }}</span>
  11. <span class="space" />
  12. </header>
  13. <main class="main">
  14. <div class="left">
  15. <header class="transfer-header">
  16. <!-- PC triggers @keyup.enter -->
  17. <input
  18. v-if="isPC && isTransferSearch"
  19. type="text"
  20. :value="searchValue"
  21. :placeholder="TUITranslateService.t('component.请输入userID')"
  22. enterkeyhint="search"
  23. :class="[isUniFrameWork ? 'left-uniapp-input' : '']"
  24. @keyup.enter="handleInput"
  25. />
  26. <!-- not PC triggers blur -->
  27. <input
  28. v-if="!isPC && isTransferSearch"
  29. type="text"
  30. :placeholder="TUITranslateService.t('component.请输入userID')"
  31. enterkeyhint="search"
  32. :value="searchValue"
  33. :class="[isUniFrameWork ? 'left-uniapp-input' : '']"
  34. @blur="handleInput"
  35. @confirm="handleInput"
  36. />
  37. </header>
  38. <main class="transfer-left-main">
  39. <ul class="transfer-list">
  40. <li
  41. v-if="optional.length > 1 && !isRadio"
  42. class="transfer-list-item"
  43. @click="selectedAll"
  44. >
  45. <Icon
  46. v-if="transferSelectedList.length === optional.length"
  47. :file="selectedIcon"
  48. :width="'18px'"
  49. :height="'18px'"
  50. />
  51. <i v-else class="icon-unselected" />
  52. <span class="select-all">{{
  53. TUITranslateService.t("component.全选")
  54. }}</span>
  55. </li>
  56. <li
  57. v-for="item in transferList"
  58. :key="item.userID"
  59. class="transfer-list-item"
  60. @click="selected(item)"
  61. >
  62. <Icon
  63. v-if="transferSelectedList.indexOf(item) > -1"
  64. :file="selectedIcon"
  65. :class="[item.isDisabled && 'disabled']"
  66. :width="'18px'"
  67. :height="'18px'"
  68. />
  69. <i v-else :class="[item.isDisabled && 'disabled', 'icon-unselected']" />
  70. <template v-if="!isTransferCustomItem">
  71. <img
  72. class="avatar"
  73. :src="
  74. item.avatar ||
  75. 'https://bucket.sxdirectpurchase.com/wx/static/img/ImAvatar.png'
  76. "
  77. onerror="this.onerror=null;this.src='https://bucket.sxdirectpurchase.com/wx/static/img/ImAvatar.png'"
  78. />
  79. <span class="name">{{ item.nick || item.userID }}</span>
  80. <span v-if="item.isDisabled"
  81. >({{ TUITranslateService.t("component.已在群中") }})</span
  82. >
  83. </template>
  84. <template v-else>
  85. <slot name="left" :data="item" />
  86. </template>
  87. </li>
  88. <li
  89. v-if="transferTotal > transferList.length"
  90. class="transfer-list-item more"
  91. @click="getMore"
  92. >
  93. {{ TUITranslateService.t("component.查看更多") }}
  94. </li>
  95. </ul>
  96. </main>
  97. </div>
  98. <div class="right">
  99. <header v-if="isPC" class="transfer-header">
  100. {{ transferTitle }}
  101. </header>
  102. <ul v-if="resultShow" class="transfer-list">
  103. <p v-if="transferSelectedList.length > 0 && isPC" class="transfer-text">
  104. {{ TUITranslateService.t("component.已选中") }}{{ transferSelectedList.length
  105. }}{{ TUITranslateService.t("component.人") }}
  106. </p>
  107. <li
  108. v-for="(item, index) in transferSelectedList"
  109. :key="index"
  110. class="transfer-list-item space-between"
  111. >
  112. <aside class="transfer-list-item-content">
  113. <template v-if="!isTransferCustomItem">
  114. <img
  115. class="avatar"
  116. :src="
  117. item.avatar ||
  118. 'https://bucket.sxdirectpurchase.com/wx/static/img/ImAvatar.png'
  119. "
  120. onerror="this.onerror=null;this.src='https://bucket.sxdirectpurchase.com/wx/static/img/ImAvatar.png'"
  121. />
  122. <span v-if="isPC" class="name">{{ item.nick || item.userID }}</span>
  123. </template>
  124. <template v-else>
  125. <slot name="right" :data="item" />
  126. </template>
  127. </aside>
  128. <span v-if="isPC" @click="selected(item)">
  129. <Icon :file="cancelIcon" :width="'18px'" :height="'18px'" />
  130. </span>
  131. </li>
  132. </ul>
  133. <footer class="transfer-right-footer">
  134. <button class="btn btn-cancel" @click="cancel">
  135. {{ TUITranslateService.t("component.取消") }}
  136. </button>
  137. <button v-if="transferSelectedList.length > 0" class="btn" @click="submit">
  138. {{ TUITranslateService.t("component.完成") }}
  139. </button>
  140. <button v-else class="btn btn-no" @click="submit">
  141. {{ TUITranslateService.t("component.完成") }}
  142. </button>
  143. </footer>
  144. </div>
  145. </main>
  146. </div>
  147. </template>
  148. <script lang="ts" setup>
  149. import { ref, watchEffect, computed } from "../../../adapter-vue";
  150. import { TUITranslateService } from "@tencentcloud/chat-uikit-engine";
  151. import { ITransferListItem } from "../../../interface";
  152. import Icon from "../Icon.vue";
  153. import selectedIcon from "../../../assets/icon/selected.svg";
  154. import backIcon from "../../../assets/icon/back.svg";
  155. import cancelIcon from "../../../assets/icon/cancel.svg";
  156. import { isPC, isUniFrameWork, isMobile } from "../../../utils/env";
  157. const props = defineProps({
  158. list: {
  159. type: Array,
  160. default: () => [],
  161. },
  162. selectedList: {
  163. type: Array,
  164. default: () => [],
  165. },
  166. isSearch: {
  167. type: Boolean,
  168. default: true,
  169. },
  170. isRadio: {
  171. type: Boolean,
  172. default: false,
  173. },
  174. isCustomItem: {
  175. type: Boolean,
  176. default: false,
  177. },
  178. title: {
  179. type: String,
  180. default: "",
  181. },
  182. type: {
  183. type: String,
  184. default: "",
  185. },
  186. resultShow: {
  187. type: Boolean,
  188. default: true,
  189. },
  190. total: {
  191. type: Number,
  192. default: 0,
  193. },
  194. isHiddenBackIcon: {
  195. type: Boolean,
  196. default: false,
  197. },
  198. });
  199. const transferList = ref<ITransferListItem[]>([]);
  200. const transferTotal = ref<number>(0);
  201. const transferSelectedList = ref<ITransferListItem[]>([]);
  202. const isTransferSearch = ref(true);
  203. const isTransferCustomItem = ref(false);
  204. const transferTitle = ref("");
  205. const searchValue = ref("");
  206. watchEffect(() => {
  207. if (props.isCustomItem) {
  208. for (let index = 0; index < props.list.length; index++) {
  209. if ((props.list[index] as any).conversationID.indexOf("@TIM#SYSTEM") > -1) {
  210. // eslint-disable-next-line vue/no-mutating-props
  211. props.list.splice(index, 1);
  212. }
  213. transferList.value = props.list as ITransferListItem[];
  214. }
  215. } else {
  216. transferList.value = props.list as ITransferListItem[];
  217. }
  218. transferTotal.value = props.total ? props.total : props.list.length;
  219. transferSelectedList.value = (props.selectedList && props.selectedList.length > 0
  220. ? props.selectedList
  221. : transferSelectedList.value) as any;
  222. isTransferSearch.value = props.isSearch;
  223. isTransferCustomItem.value = props.isCustomItem;
  224. transferTitle.value = props.title;
  225. });
  226. const emit = defineEmits(["search", "submit", "cancel", "getMore"]);
  227. const optional = computed(() =>
  228. transferList.value.filter((item: any) => !item.isDisabled)
  229. );
  230. const handleInput = (e: any) => {
  231. searchValue.value = e.target.value || e.detail.value;
  232. emit("search", searchValue.value);
  233. };
  234. const selected = (item: any) => {
  235. if (item.isDisabled) {
  236. return;
  237. }
  238. let list: ITransferListItem[] = transferSelectedList.value;
  239. const index: number = list.indexOf(item);
  240. if (index > -1) {
  241. return transferSelectedList.value.splice(index, 1);
  242. }
  243. if (props.isRadio) {
  244. list = [];
  245. }
  246. list.push(item);
  247. transferSelectedList.value = list;
  248. };
  249. const selectedAll = () => {
  250. if (transferSelectedList.value.length === optional.value.length) {
  251. transferSelectedList.value = [];
  252. } else {
  253. transferSelectedList.value = [...optional.value];
  254. }
  255. };
  256. const submit = () => {
  257. emit("submit", transferSelectedList.value);
  258. searchValue.value = "";
  259. };
  260. const cancel = () => {
  261. emit("cancel");
  262. searchValue.value = "";
  263. };
  264. const getMore = () => {
  265. emit("getMore");
  266. };
  267. </script>
  268. <style lang="scss" scoped src="./style/transfer.scss"></style>