Compare commits
	
		
			6 Commits
		
	
	
		
			478336c2fe
			...
			b04d25a243
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | b04d25a243 | ||
|  | 9e31271cc3 | ||
|  | 6d08dbe42f | ||
| 701d878f7d | |||
| 7544b3d324 | |||
| 8c9f634d0b | 
| @ -32,6 +32,8 @@ | |||||||
|     "quill": "^1.3.7", |     "quill": "^1.3.7", | ||||||
|     "quill-image-uploader": "^1.3.0", |     "quill-image-uploader": "^1.3.0", | ||||||
|     "quill-mention": "^4.1.0", |     "quill-mention": "^4.1.0", | ||||||
|  |     "sortablejs": "^1.15.6", | ||||||
|  |     "viewerjs": "^1.11.7", | ||||||
|     "vue": "^3.3.11", |     "vue": "^3.3.11", | ||||||
|     "vue-cropper": "^1.1.1", |     "vue-cropper": "^1.1.1", | ||||||
|     "vue-router": "^4.2.5", |     "vue-router": "^4.2.5", | ||||||
|  | |||||||
| @ -62,6 +62,12 @@ importers: | |||||||
|       quill-mention: |       quill-mention: | ||||||
|         specifier: ^4.1.0 |         specifier: ^4.1.0 | ||||||
|         version: 4.1.0 |         version: 4.1.0 | ||||||
|  |       sortablejs: | ||||||
|  |         specifier: ^1.15.6 | ||||||
|  |         version: 1.15.6 | ||||||
|  |       viewerjs: | ||||||
|  |         specifier: ^1.11.7 | ||||||
|  |         version: 1.11.7 | ||||||
|       vue: |       vue: | ||||||
|         specifier: ^3.3.11 |         specifier: ^3.3.11 | ||||||
|         version: 3.5.13(typescript@5.2.2) |         version: 3.5.13(typescript@5.2.2) | ||||||
| @ -2927,6 +2933,9 @@ packages: | |||||||
|   sortablejs@1.14.0: |   sortablejs@1.14.0: | ||||||
|     resolution: {integrity: sha512-pBXvQCs5/33fdN1/39pPL0NZF20LeRbLQ5jtnheIPN9JQAaufGjKdWduZn4U7wCtVuzKhmRkI0DFYHYRbB2H1w==} |     resolution: {integrity: sha512-pBXvQCs5/33fdN1/39pPL0NZF20LeRbLQ5jtnheIPN9JQAaufGjKdWduZn4U7wCtVuzKhmRkI0DFYHYRbB2H1w==} | ||||||
| 
 | 
 | ||||||
|  |   sortablejs@1.15.6: | ||||||
|  |     resolution: {integrity: sha512-aNfiuwMEpfBM/CN6LY0ibyhxPfPbyFeBTYJKCvzkJ2GkUpazIt3H+QIPAMHwqQ7tMKaHz1Qj+rJJCqljnf4p3A==} | ||||||
|  | 
 | ||||||
|   source-map-js@1.2.1: |   source-map-js@1.2.1: | ||||||
|     resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} |     resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} | ||||||
|     engines: {node: '>=0.10.0'} |     engines: {node: '>=0.10.0'} | ||||||
| @ -3211,6 +3220,9 @@ packages: | |||||||
|     peerDependencies: |     peerDependencies: | ||||||
|       vue: ^3.0.11 |       vue: ^3.0.11 | ||||||
| 
 | 
 | ||||||
|  |   viewerjs@1.11.7: | ||||||
|  |     resolution: {integrity: sha512-0JuVqOmL5v1jmEAlG5EBDR3XquxY8DWFQbFMprOXgaBB0F7Q/X9xWdEaQc59D8xzwkdUgXEMSSknTpriq95igg==} | ||||||
|  | 
 | ||||||
|   vite-hot-client@2.0.4: |   vite-hot-client@2.0.4: | ||||||
|     resolution: {integrity: sha512-W9LOGAyGMrbGArYJN4LBCdOC5+Zwh7dHvOHC0KmGKkJhsOzaKbpo/jEjpPKVHIW0/jBWj8RZG0NUxfgA8BxgAg==} |     resolution: {integrity: sha512-W9LOGAyGMrbGArYJN4LBCdOC5+Zwh7dHvOHC0KmGKkJhsOzaKbpo/jEjpPKVHIW0/jBWj8RZG0NUxfgA8BxgAg==} | ||||||
|     peerDependencies: |     peerDependencies: | ||||||
| @ -6520,6 +6532,8 @@ snapshots: | |||||||
| 
 | 
 | ||||||
|   sortablejs@1.14.0: {} |   sortablejs@1.14.0: {} | ||||||
| 
 | 
 | ||||||
|  |   sortablejs@1.15.6: {} | ||||||
|  | 
 | ||||||
|   source-map-js@1.2.1: {} |   source-map-js@1.2.1: {} | ||||||
| 
 | 
 | ||||||
|   source-map-resolve@0.5.3: |   source-map-resolve@0.5.3: | ||||||
| @ -6808,6 +6822,8 @@ snapshots: | |||||||
|       evtd: 0.2.4 |       evtd: 0.2.4 | ||||||
|       vue: 3.5.13(typescript@5.2.2) |       vue: 3.5.13(typescript@5.2.2) | ||||||
| 
 | 
 | ||||||
|  |   viewerjs@1.11.7: {} | ||||||
|  | 
 | ||||||
|   vite-hot-client@2.0.4(vite@4.5.14(@types/node@18.19.99)(less@4.3.0)(sass@1.88.0)(terser@5.39.0)): |   vite-hot-client@2.0.4(vite@4.5.14(@types/node@18.19.99)(less@4.3.0)(sass@1.88.0)(terser@5.39.0)): | ||||||
|     dependencies: |     dependencies: | ||||||
|       vite: 4.5.14(@types/node@18.19.99)(less@4.3.0)(sass@1.88.0)(terser@5.39.0) |       vite: 4.5.14(@types/node@18.19.99)(less@4.3.0)(sass@1.88.0)(terser@5.39.0) | ||||||
|  | |||||||
| @ -16,7 +16,7 @@ | |||||||
|       </div> |       </div> | ||||||
|     </template> |     </template> | ||||||
|     <slot name="content"></slot> |     <slot name="content"></slot> | ||||||
|     <template #footer> |     <template #footer v-if="actionBtns.cancelBtn || actionBtns.confirmBtn"> | ||||||
|       <div class="custom-modal-btns"> |       <div class="custom-modal-btns"> | ||||||
|         <customBtn |         <customBtn | ||||||
|           color="#C7C7C9" |           color="#C7C7C9" | ||||||
|  | |||||||
| @ -2,14 +2,14 @@ | |||||||
|   <div class="row items-center"> |   <div class="row items-center"> | ||||||
|     <div v-if="state.treeData.edit"> |     <div v-if="state.treeData.edit"> | ||||||
|       <n-input v-model:value="state.editTitle" |       <n-input v-model:value="state.editTitle" | ||||||
|                style="width:120px" /> |                style="max-width:200px" /> | ||||||
|     </div> |     </div> | ||||||
| 
 | 
 | ||||||
|     <n-popover trigger="hover" |     <n-popover trigger="hover" | ||||||
|                v-else> |                v-else> | ||||||
|       <template #trigger> |       <template #trigger> | ||||||
|         <div style="width:120px" |         <div style="max-width:200px" | ||||||
|              class="fl-px-sm sf-text-ellipsis">{{ state.treeData.title }}</div> |              class="fl-px-sm sf-text-ellipsis">{{ state.treeData.title + '(' + state.treeData.staffNum + ')' }}</div> | ||||||
|       </template> |       </template> | ||||||
|       <div>{{ state.treeData.title }}</div> |       <div>{{ state.treeData.title }}</div> | ||||||
|     </n-popover> |     </n-popover> | ||||||
|  | |||||||
							
								
								
									
										70
									
								
								src/components/search/highLightText.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										70
									
								
								src/components/search/highLightText.vue
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,70 @@ | |||||||
|  | <template> | ||||||
|  |   <span> | ||||||
|  |     <template v-for="(part, index) in parts" :key="index"> | ||||||
|  |       <span v-if="part.highlighted" :class="highlightClass"> | ||||||
|  |         {{ part.text }} | ||||||
|  |       </span> | ||||||
|  |       <span v-else>{{ part.text }}</span> | ||||||
|  |     </template> | ||||||
|  |   </span> | ||||||
|  | </template> | ||||||
|  | 
 | ||||||
|  | <script setup> | ||||||
|  | import { computed } from 'vue' | ||||||
|  | 
 | ||||||
|  | const props = defineProps({ | ||||||
|  |   text: { | ||||||
|  |     type: String, | ||||||
|  |     required: true, | ||||||
|  |   }, | ||||||
|  |   searchText: { | ||||||
|  |     type: String, | ||||||
|  |     default: '', | ||||||
|  |   }, | ||||||
|  |   highlightClass: { | ||||||
|  |     type: String, | ||||||
|  |     default: 'highlight', | ||||||
|  |   }, | ||||||
|  | }) | ||||||
|  | 
 | ||||||
|  | const escapedSearchText = computed(() => | ||||||
|  |   String(props.searchText).replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&'), | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | const pattern = computed(() => new RegExp(escapedSearchText.value, 'gi')) | ||||||
|  | 
 | ||||||
|  | const parts = computed(() => { | ||||||
|  |   if (!props.searchText || !props.text) | ||||||
|  |     return [{ text: props.text, highlighted: false }]; | ||||||
|  | 
 | ||||||
|  |   const result = []; | ||||||
|  |   let currentIndex = 0; | ||||||
|  |   const escapedSearchTextValue = escapedSearchText.value; | ||||||
|  |   const searchPattern = new RegExp(`(${escapedSearchTextValue})`, 'gi'); | ||||||
|  | 
 | ||||||
|  |   props.text.replace(searchPattern, (match, p1, offset) => { | ||||||
|  |     // 添加非高亮文本 | ||||||
|  |     if (currentIndex < offset) { | ||||||
|  |       result.push({ text: props.text.slice(currentIndex, offset), highlighted: false }); | ||||||
|  |     } | ||||||
|  |     // 添加高亮文本 | ||||||
|  |     result.push({ text: p1, highlighted: true }); | ||||||
|  |     // 更新当前索引 | ||||||
|  |     currentIndex = offset + p1.length; | ||||||
|  |     return p1; // 这个返回值不影响最终结果,只是replace方法的要求 | ||||||
|  |   }); | ||||||
|  | 
 | ||||||
|  |   // 添加剩余的非高亮文本(如果有的话) | ||||||
|  |   if (currentIndex < props.text.length) { | ||||||
|  |     result.push({ text: props.text.slice(currentIndex), highlighted: false }); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   return result; | ||||||
|  | }); | ||||||
|  | </script> | ||||||
|  | 
 | ||||||
|  | <style scoped> | ||||||
|  | .highlight { | ||||||
|  |   color: #7a58de; | ||||||
|  | } | ||||||
|  | </style> | ||||||
							
								
								
									
										337
									
								
								src/components/search/searchItem.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										337
									
								
								src/components/search/searchItem.vue
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,337 @@ | |||||||
|  | <template> | ||||||
|  |   <div | ||||||
|  |     class="search-item" | ||||||
|  |     :class="props?.conditionType ? 'search-item-condition' : ''" | ||||||
|  |     v-if="resultName" | ||||||
|  |     :style="props.searchResultKey === 'talk_record_infos_receiver' ? 'margin: 12px 0 0' : ''" | ||||||
|  |   > | ||||||
|  |     <div class="search-item-avatar"> | ||||||
|  |       <avatarModule | ||||||
|  |         :mode="props.searchItem?.group_type === 0 ? 1 : 2" | ||||||
|  |         :avatar="avatarImg" | ||||||
|  |         :userName="resultName" | ||||||
|  |         :groupType="props.searchItem?.group_type" | ||||||
|  |         :customStyle="{ | ||||||
|  |           width: props?.conditionType ? '32px' : '42px', | ||||||
|  |           height: props?.conditionType ? '32px' : '42px', | ||||||
|  |           margin: props?.conditionType ? '0 9px 0 0' : '0 10px 0 0' | ||||||
|  |         }" | ||||||
|  |         :customTextStyle="{ | ||||||
|  |           fontSize: props?.conditionType ? '10px' : '14px', | ||||||
|  |           fontWeight: 'bold', | ||||||
|  |           color: '#fff', | ||||||
|  |           lineHeight: '24px' | ||||||
|  |         }" | ||||||
|  |       ></avatarModule> | ||||||
|  |       <div | ||||||
|  |         class="info-tag" | ||||||
|  |         v-if="resultType && !searchRecordDetail" | ||||||
|  |         :style="'border-color:' + resultTypeColor" | ||||||
|  |       > | ||||||
|  |         <span class="text-[10px] font-medium" :style="'color:' + resultTypeColor"> | ||||||
|  |           {{ resultType }} | ||||||
|  |         </span> | ||||||
|  |       </div> | ||||||
|  |     </div> | ||||||
|  |     <div class="result-info"> | ||||||
|  |       <div class="info-name" :class="searchRecordDetail ? 'info-name-searchRecordDetail' : ''"> | ||||||
|  |         <HighlightText | ||||||
|  |           :class=" | ||||||
|  |             props?.conditionType | ||||||
|  |               ? 'text-[14px] font-medium' | ||||||
|  |               : searchRecordDetail | ||||||
|  |               ? 'text-[12px] font-medium' | ||||||
|  |               : 'text-[14px] font-bold' | ||||||
|  |           " | ||||||
|  |           :text="resultName" | ||||||
|  |           :searchText="props.searchText" | ||||||
|  |         /> | ||||||
|  |         <div class="info_num" v-if="groupNum"> | ||||||
|  |           <span class="text-[14px] font-medium"> | ||||||
|  |             {{ '(' + groupNum + ')' }} | ||||||
|  |           </span> | ||||||
|  |         </div> | ||||||
|  |         <div v-if="searchRecordDetail && chatRecordCreatedAt"> | ||||||
|  |           <span class="text-[12px] font-medium"> | ||||||
|  |             {{ chatRecordCreatedAt }} | ||||||
|  |           </span> | ||||||
|  |         </div> | ||||||
|  |       </div> | ||||||
|  |       <div | ||||||
|  |         class="info-detail" | ||||||
|  |         v-if="resultDetail" | ||||||
|  |         :class="searchRecordDetail ? 'info-detail-searchRecordDetail' : ''" | ||||||
|  |       > | ||||||
|  |         <HighlightText | ||||||
|  |           class="text-[12px] font-regular" | ||||||
|  |           :text="resultDetail" | ||||||
|  |           :searchText="props.searchText" | ||||||
|  |         /> | ||||||
|  |       </div> | ||||||
|  |     </div> | ||||||
|  |     <div class="search-item-pointer" v-if="pointerIconSrc"> | ||||||
|  |       <img :src="pointerIconSrc" /> | ||||||
|  |     </div> | ||||||
|  |   </div> | ||||||
|  | </template> | ||||||
|  | <script setup> | ||||||
|  | import avatarModule from '@/components/avatar-module/index.vue' | ||||||
|  | import { ref, watch, computed, onMounted, onUnmounted, reactive, defineProps } from 'vue' | ||||||
|  | import HighlightText from './highLightText.vue' | ||||||
|  | import { beautifyTime } from '@/utils/datetime' | ||||||
|  | import { ChatMsgTypeMapping } from '@/constant/message' | ||||||
|  | const props = defineProps({ | ||||||
|  |   searchItem: Object | Number, | ||||||
|  |   searchResultKey: { | ||||||
|  |     type: String, | ||||||
|  |     default: '' | ||||||
|  |   }, | ||||||
|  |   searchText: { | ||||||
|  |     type: String, | ||||||
|  |     default: '' | ||||||
|  |   }, //搜索内容 | ||||||
|  |   searchRecordDetail: { | ||||||
|  |     type: Boolean, | ||||||
|  |     default: false | ||||||
|  |   }, //是否是搜索聊天记录详情 | ||||||
|  |   pointerIconSrc: { | ||||||
|  |     type: String, | ||||||
|  |     default: '' | ||||||
|  |   }, //箭头图标 | ||||||
|  |   conditionType: { | ||||||
|  |     type: Number, | ||||||
|  |     default: 0 | ||||||
|  |   } //搜索类型 | ||||||
|  | }) | ||||||
|  | // 映射表-查找对应结构下的属性名 | ||||||
|  | const keyMapping = { | ||||||
|  |   user_infos: { avatar: 'avatar', name: 'nickname' }, | ||||||
|  |   group_infos: { avatar: 'avatar', name: 'name', group_num: 'group_num' }, | ||||||
|  |   group_member_infos: { | ||||||
|  |     avatar: 'group_avatar', | ||||||
|  |     name: 'group_name', | ||||||
|  |     detailKey: 'user_name', | ||||||
|  |     group_num: 'group_num' | ||||||
|  |   }, | ||||||
|  |   combinedGroup: { | ||||||
|  |     avatar: props.searchItem?.groupTempType | ||||||
|  |       ? props.searchItem?.groupTempType === 'group_infos' | ||||||
|  |         ? 'avatar' | ||||||
|  |         : props.searchItem?.groupTempType === 'group_member_infos' | ||||||
|  |         ? 'group_avatar' | ||||||
|  |         : '' | ||||||
|  |       : '', | ||||||
|  |     name: props.searchItem?.groupTempType | ||||||
|  |       ? props.searchItem?.groupTempType === 'group_infos' | ||||||
|  |         ? 'name' | ||||||
|  |         : props.searchItem?.groupTempType === 'group_member_infos' | ||||||
|  |         ? 'group_name' | ||||||
|  |         : '' | ||||||
|  |       : '', | ||||||
|  |     detailKey: props.searchItem?.groupTempType | ||||||
|  |       ? props.searchItem?.groupTempType === 'group_member_infos' | ||||||
|  |         ? 'user_name' | ||||||
|  |         : '' | ||||||
|  |       : '', | ||||||
|  |     group_num: props.searchItem?.groupTempType | ||||||
|  |       ? props.searchItem?.groupTempType === 'group_infos' | ||||||
|  |         ? 'group_num' | ||||||
|  |         : props.searchItem?.groupTempType === 'group_member_infos' | ||||||
|  |         ? 'group_num' | ||||||
|  |         : '' | ||||||
|  |       : '' | ||||||
|  |   }, | ||||||
|  |   general_infos: { | ||||||
|  |     avatar: 'receiver_avatar', | ||||||
|  |     name: 'receiver_name', | ||||||
|  |     detailKey: 'count', | ||||||
|  |     group_num: 'group_num' | ||||||
|  |   }, | ||||||
|  |   talk_record_infos: { | ||||||
|  |     avatar: 'user_avatar', | ||||||
|  |     name: 'user_name', | ||||||
|  |     detailKey: 'extra', | ||||||
|  |     created_at: 'created_at' | ||||||
|  |   }, | ||||||
|  |   talk_record_infos_receiver: { | ||||||
|  |     avatar: 'receiver_avatar', | ||||||
|  |     name: 'receiver_name', | ||||||
|  |     group_num: 'group_num' | ||||||
|  |   }, | ||||||
|  |   search_by_member_condition: { | ||||||
|  |     avatar: 'avatar', | ||||||
|  |     name: 'nickname', | ||||||
|  |     created_at: 'created_at', | ||||||
|  |     msg_type: 'msg_type', | ||||||
|  |     detailKey: 'chatMessageType' | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | //获取key对应值 | ||||||
|  | const getKeyValue = (keys) => { | ||||||
|  |   let keyValue = '' | ||||||
|  |   if (keys) { | ||||||
|  |     keyValue = props?.searchItem ? props?.searchItem[keys] : '' | ||||||
|  |   } | ||||||
|  |   return keyValue | ||||||
|  | } | ||||||
|  | //头像 | ||||||
|  | const avatarImg = computed(() => { | ||||||
|  |   let avatar = getKeyValue(keyMapping[props.searchResultKey]?.avatar) | ||||||
|  |   if (props?.conditionType) { | ||||||
|  |     avatar = props.searchItem.avatar | ||||||
|  |   } | ||||||
|  |   return avatar | ||||||
|  | }) | ||||||
|  | //名称 | ||||||
|  | const resultName = computed(() => { | ||||||
|  |   let result_name = getKeyValue(keyMapping[props.searchResultKey]?.name) | ||||||
|  |   if (props?.conditionType) { | ||||||
|  |     result_name = props.searchItem.nickname | ||||||
|  |   } | ||||||
|  |   return result_name | ||||||
|  | }) | ||||||
|  | //文字头像 | ||||||
|  | const imgText = computed(() => { | ||||||
|  |   return resultName.value.length >= 2 ? resultName.value.slice(-2) : resultName.value | ||||||
|  | }) | ||||||
|  | // 映射表-根据groupType设置对应值 | ||||||
|  | const groupTypeMapping = { | ||||||
|  |   0: {}, | ||||||
|  |   1: {}, | ||||||
|  |   2: { | ||||||
|  |     result_type: '部门', | ||||||
|  |     result_type_color: '#377EC6' | ||||||
|  |   }, | ||||||
|  |   3: { | ||||||
|  |     result_type: '项目', | ||||||
|  |     result_type_color: '#C1681C' | ||||||
|  |   }, | ||||||
|  |   4: { | ||||||
|  |     result_type: '公司', | ||||||
|  |     result_type_color: '#7A58DE' | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | //群人数 | ||||||
|  | const groupNum = computed(() => { | ||||||
|  |   return getKeyValue(keyMapping[props.searchResultKey]?.group_num) | ||||||
|  | }) | ||||||
|  | //群类型tag | ||||||
|  | const resultType = computed(() => { | ||||||
|  |   return groupTypeMapping[props.searchItem?.group_type]?.result_type | ||||||
|  | }) | ||||||
|  | //群类型tag颜色 | ||||||
|  | const resultTypeColor = computed(() => { | ||||||
|  |   return groupTypeMapping[props.searchItem?.group_type]?.result_type_color | ||||||
|  | }) | ||||||
|  | //搜索聊天记录详情-时间 | ||||||
|  | const chatRecordCreatedAt = computed(() => { | ||||||
|  |   let created_at = getKeyValue(keyMapping[props.searchResultKey]?.created_at) | ||||||
|  |   return beautifyTime(created_at) | ||||||
|  | }) | ||||||
|  | //详细内容 | ||||||
|  | const resultDetail = computed(() => { | ||||||
|  |   let result_detail = props.searchItem[keyMapping[props.searchResultKey]?.detailKey] | ||||||
|  |   switch (keyMapping[props.searchResultKey]?.detailKey) { | ||||||
|  |     case 'count': | ||||||
|  |       result_detail = result_detail + '条聊天记录' | ||||||
|  |       break | ||||||
|  |     case 'user_name': | ||||||
|  |       result_detail = '包含:' + result_detail | ||||||
|  |       break | ||||||
|  |     case 'extra': | ||||||
|  |       result_detail = props.searchItem?.extra | ||||||
|  |       break | ||||||
|  |     case 'chatMessageType': | ||||||
|  |       result_detail = | ||||||
|  |         props.searchItem?.msg_type === 1 | ||||||
|  |           ? props.searchItem?.extra?.content | ||||||
|  |           : ChatMsgTypeMapping[props.searchItem?.msg_type] | ||||||
|  |       break | ||||||
|  |     default: | ||||||
|  |       result_detail = '' | ||||||
|  |   } | ||||||
|  |   return result_detail | ||||||
|  | }) | ||||||
|  | </script> | ||||||
|  | <style lang="scss" scoped> | ||||||
|  | .search-item { | ||||||
|  |   display: flex; | ||||||
|  |   flex-direction: row; | ||||||
|  |   align-items: center; | ||||||
|  |   justify-content: flex-start; | ||||||
|  |   padding: 11px 0 12px; | ||||||
|  |   border-bottom: 1px solid #f8f8f8; | ||||||
|  | 
 | ||||||
|  |   .search-item-avatar{ | ||||||
|  |     position: relative; | ||||||
|  |     .info-tag { | ||||||
|  |         display: flex; | ||||||
|  |         flex-direction: row; | ||||||
|  |         align-items: center; | ||||||
|  |         justify-content: center; | ||||||
|  |         padding: 0px 6px; | ||||||
|  |         border: 1px solid #000; | ||||||
|  |         border-radius: 3px; | ||||||
|  |         flex-shrink: 0; | ||||||
|  |         background-color: #fff; | ||||||
|  |         position: absolute; | ||||||
|  |         bottom: 0; | ||||||
|  |         left: 4px; | ||||||
|  |         span { | ||||||
|  |           line-height: 14px; | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   .result-info { | ||||||
|  |     width: 100%; | ||||||
|  |     .info-name { | ||||||
|  |       display: flex; | ||||||
|  |       flex-direction: row; | ||||||
|  |       align-items: center; | ||||||
|  |       justify-content: flex-start; | ||||||
|  |       span { | ||||||
|  |         color: #191919; | ||||||
|  |         line-height: 22px; | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |     .info-name-searchRecordDetail { | ||||||
|  |       display: flex; | ||||||
|  |       flex-direction: row; | ||||||
|  |       align-items: center; | ||||||
|  |       justify-content: space-between; | ||||||
|  |       span { | ||||||
|  |         color: #999999; | ||||||
|  |         line-height: 17px; | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |     .info-detail { | ||||||
|  |       span { | ||||||
|  |         color: #999999; | ||||||
|  |         line-height: 20px; | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |     .info-detail-searchRecordDetail { | ||||||
|  |       span { | ||||||
|  |         color: #191919; | ||||||
|  |         word-break: break-all; | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |   .search-item-pointer { | ||||||
|  |     width: 5.5px; | ||||||
|  |     height: 9px; | ||||||
|  |     display: flex; | ||||||
|  |     flex-direction: row; | ||||||
|  |     align-items: center; | ||||||
|  |     justify-content: center; | ||||||
|  |     img { | ||||||
|  |       width: 100%; | ||||||
|  |       height: 100%; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | .search-item-condition { | ||||||
|  |   border: 0; | ||||||
|  | } | ||||||
|  | </style> | ||||||
							
								
								
									
										743
									
								
								src/components/search/searchList.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										743
									
								
								src/components/search/searchList.vue
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,743 @@ | |||||||
|  | <template> | ||||||
|  |   <div class="search-list"> | ||||||
|  |     <div class="search-result"> | ||||||
|  |       <div class="search-result-list"> | ||||||
|  |         <div | ||||||
|  |           class="search-result-each-part" | ||||||
|  |           v-for="(searchResultValue, searchResultKey, searchResultIndex) in state.searchResult" | ||||||
|  |           :key="searchResultKey" | ||||||
|  |         > | ||||||
|  |           <div | ||||||
|  |             class="search-result-part" | ||||||
|  |             v-if=" | ||||||
|  |               Array.isArray(state?.searchResult[searchResultKey]) && | ||||||
|  |               state?.searchResult[searchResultKey].length > 0 && | ||||||
|  |               searchResultKey !== 'group_infos' && | ||||||
|  |               searchResultKey !== 'group_member_infos' | ||||||
|  |             " | ||||||
|  |           > | ||||||
|  |             <div class="result-title"> | ||||||
|  |               <span class="text-[14px] font-regular"> | ||||||
|  |                 {{ getResultKeysValue(searchResultKey) }} | ||||||
|  |               </span> | ||||||
|  |             </div> | ||||||
|  |             <div class="result-list"> | ||||||
|  |               <div | ||||||
|  |                 class="result-list-each" | ||||||
|  |                 v-for="(item, index) in state?.searchResult[searchResultKey]" | ||||||
|  |                 :key="index" | ||||||
|  |               > | ||||||
|  |                 <searchItem | ||||||
|  |                   @click="clickSearchItem(searchResultKey, item)" | ||||||
|  |                   v-if="(props.listLimit && index < 3) || !props.listLimit" | ||||||
|  |                   :searchResultKey="searchResultKey" | ||||||
|  |                   :searchItem="item" | ||||||
|  |                   :searchText="state.searchText" | ||||||
|  |                   :searchRecordDetail="props.searchRecordDetail" | ||||||
|  |                 ></searchItem> | ||||||
|  |               </div> | ||||||
|  |             </div> | ||||||
|  |             <div | ||||||
|  |               class="result-has-more" | ||||||
|  |               v-if="getHasMoreResult(searchResultKey)" | ||||||
|  |               @click="toMoreResultPage(searchResultKey)" | ||||||
|  |             > | ||||||
|  |               <span class="text-[14px] font-regular"> | ||||||
|  |                 {{ getHasMoreResult(searchResultKey) }} | ||||||
|  |               </span> | ||||||
|  |             </div> | ||||||
|  |           </div> | ||||||
|  |         </div> | ||||||
|  |       </div> | ||||||
|  |     </div> | ||||||
|  |     <!-- <ZPaging | ||||||
|  |       ref="zPaging" | ||||||
|  |       :show-scrollbar="false" | ||||||
|  |       v-model="state.searchResultList" | ||||||
|  |       @query="queryAllSearch" | ||||||
|  |       :default-page-no="state.pageNum" | ||||||
|  |       :default-page-size="props.searchResultPageSize" | ||||||
|  |       :loading-more-default-as-loading="true" | ||||||
|  |       :inside-more="true" | ||||||
|  |       :empty-view-img="searchNoData" | ||||||
|  |       :empty-view-text="'检索您要查找的内容吧~'" | ||||||
|  |       :empty-view-img-style="{ width: '238px', height: '131px' }" | ||||||
|  |       :empty-view-title-style="{ | ||||||
|  |         color: '#999999', | ||||||
|  |         margin: '-10px 0 0', | ||||||
|  |         'line-height': '20px', | ||||||
|  |         'font-size': '14px', | ||||||
|  |         'font-weight': 400, | ||||||
|  |       }" | ||||||
|  |       :refresher-enabled="false" | ||||||
|  |     > | ||||||
|  |       <template #top> | ||||||
|  |         <div class="searchRoot"> | ||||||
|  |           <customInput | ||||||
|  |             :searchText="state.searchText" | ||||||
|  |             :first_talk_record_infos="state.first_talk_record_infos" | ||||||
|  |             @inputSearchText="inputSearchText" | ||||||
|  |           ></customInput> | ||||||
|  |           <span | ||||||
|  |             class="searchRoot_cancelBtn text-[16px] font-medium" | ||||||
|  |             @click="cancelSearch" | ||||||
|  |           > | ||||||
|  |             取消 | ||||||
|  |           </span> | ||||||
|  |         </div> | ||||||
|  |       </template> | ||||||
|  |       <div | ||||||
|  |         class="search-record-detail" | ||||||
|  |         v-if="props.searchRecordDetail && !props?.hideFirstRecord" | ||||||
|  |       > | ||||||
|  |         <searchItem | ||||||
|  |           @click=" | ||||||
|  |             clickSearchItem( | ||||||
|  |               'talk_record_infos_receiver', | ||||||
|  |               state?.first_talk_record_infos, | ||||||
|  |             ) | ||||||
|  |           " | ||||||
|  |           searchResultKey="talk_record_infos_receiver" | ||||||
|  |           :searchItem="state?.first_talk_record_infos" | ||||||
|  |           :pointerIconSrc="pointerIconSrc" | ||||||
|  |         ></searchItem> | ||||||
|  |       </div> | ||||||
|  |       <div | ||||||
|  |         class="search-result" | ||||||
|  |         :style=" | ||||||
|  |           !state.searchText ? 'align-items:center;justify-content:center;' : '' | ||||||
|  |         " | ||||||
|  |       > | ||||||
|  |          | ||||||
|  |       </div> | ||||||
|  |     </ZPaging> --> | ||||||
|  |   </div> | ||||||
|  | </template> | ||||||
|  | <script setup> | ||||||
|  | // import searchNoData from '@/static/image/search/search-no-data.png' | ||||||
|  | // import customInput from '@/components/custom-input/custom-input.vue' | ||||||
|  | // import pointerIconSrc from '@/static/image/search/search-item-pointer.png' | ||||||
|  | // import lodash from 'lodash' | ||||||
|  | // import { useUserStore } from '@/store' | ||||||
|  | // const userStore = useUserStore() | ||||||
|  | 
 | ||||||
|  | // import ZPaging from '@/uni_modules/z-paging/components/z-paging/z-paging.vue' | ||||||
|  | // import useZPaging from '@/uni_modules/z-paging/components/z-paging/js/hooks/useZPaging.js' | ||||||
|  | // const zPaging = ref() | ||||||
|  | // useZPaging(zPaging) | ||||||
|  | 
 | ||||||
|  | import searchItem from './searchItem.vue' | ||||||
|  | import { ref, reactive, defineEmits, defineProps, onMounted } from 'vue' | ||||||
|  | 
 | ||||||
|  | const emits = defineEmits(['toMoreResultPage', 'lastIdChange', 'clickSearchItem']) | ||||||
|  | 
 | ||||||
|  | const state = reactive({ | ||||||
|  |   searchText: '', //搜索内容 | ||||||
|  |   searchResultList: [], //搜素结果列表 | ||||||
|  |   searchResult: null, //搜索结果 | ||||||
|  |   pageNum: 1, //当前请求数据页数 | ||||||
|  |   uid: 1496 //当前用户id | ||||||
|  | }) | ||||||
|  | 
 | ||||||
|  | const props = defineProps({ | ||||||
|  |   searchResultPageSize: { | ||||||
|  |     type: Number, | ||||||
|  |     default: 0 | ||||||
|  |   }, //搜索结果每页数据量 | ||||||
|  |   listLimit: { | ||||||
|  |     type: Boolean, | ||||||
|  |     default: false | ||||||
|  |   }, //是否限制列表内数据数量 | ||||||
|  |   apiParams: { | ||||||
|  |     type: String, | ||||||
|  |     default: '' | ||||||
|  |   }, //请求参数 | ||||||
|  |   apiRequest: Function, //请求 | ||||||
|  |   searchText: { | ||||||
|  |     type: String, | ||||||
|  |     default: '' | ||||||
|  |   }, //搜索内容 | ||||||
|  |   isPagination: { | ||||||
|  |     type: Boolean, | ||||||
|  |     default: false | ||||||
|  |   }, //是否分页 | ||||||
|  |   searchRecordDetail: { | ||||||
|  |     type: Boolean, | ||||||
|  |     default: false | ||||||
|  |   }, //是否是搜索聊天记录的详情 | ||||||
|  |   first_talk_record_infos: { | ||||||
|  |     type: Object, | ||||||
|  |     default() { | ||||||
|  |       return {} | ||||||
|  |     } | ||||||
|  |   }, //接受者信息 | ||||||
|  |   hideFirstRecord: { | ||||||
|  |     type: Boolean, | ||||||
|  |     default: false | ||||||
|  |   } //是否隐藏前缀及搜索群/用户主体信息 | ||||||
|  | }) | ||||||
|  | 
 | ||||||
|  | onMounted(() => { | ||||||
|  |   if (props.searchText) { | ||||||
|  |     state.searchText = props.searchText | ||||||
|  |     queryAllSearch(1, 10) | ||||||
|  |   } | ||||||
|  | }) | ||||||
|  | 
 | ||||||
|  | //输入搜索文本 | ||||||
|  | const inputSearchText = (e) => { | ||||||
|  |   if (e.trim() != state.searchText.trim()) { | ||||||
|  |     state.pageNum = 1 | ||||||
|  |     state.searchResult = null // 清空搜索结果 | ||||||
|  |     emits('lastIdChange', 0, 0, 0) | ||||||
|  |   } | ||||||
|  |   state.searchText = e.trim() | ||||||
|  |   if (!e.trim()) { | ||||||
|  |     state.searchResult = null // 清空搜索结果 | ||||||
|  |     emits('lastIdChange', 0, 0, 0) | ||||||
|  |   } | ||||||
|  |   // zPaging.value?.reload() | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // ES搜索聊天记录-主页搜索什么都有、指定用户、指定群、群与用户概览 | ||||||
|  | const queryAllSearch = (pageNum, searchResultPageSize) => { | ||||||
|  |   let params = { | ||||||
|  |     key: state.searchText, //关键字 | ||||||
|  |     size: searchResultPageSize | ||||||
|  |   } | ||||||
|  |   if (props.apiParams) { | ||||||
|  |     let apiParams = JSON.parse(decodeURIComponent(props.apiParams)) | ||||||
|  |     params = Object.assign({}, params, apiParams) | ||||||
|  |   } | ||||||
|  |   const data = { | ||||||
|  |     user_infos: [ | ||||||
|  |       { | ||||||
|  |         id: 127, | ||||||
|  |         mobile: '17706200252', | ||||||
|  |         nickname: '测试-王溢韬', | ||||||
|  |         avatar: | ||||||
|  |           'https://dci-file-new.bj.bcebos.com/fonchain-main/test/runtime/image/avatar/40/b8ed6fea-6662-416d-8bb3-1fd8a8197061.jpg', | ||||||
|  |         created_at: '2025-03-28 11:33:13', | ||||||
|  |         erp_user_id: 18282 | ||||||
|  |       }, | ||||||
|  |       { | ||||||
|  |         id: 103, | ||||||
|  |         mobile: '13814969538', | ||||||
|  |         nickname: '王静测试', | ||||||
|  |         avatar: '', | ||||||
|  |         created_at: '2025-03-27 14:44:23', | ||||||
|  |         erp_user_id: 2639 | ||||||
|  |       }, | ||||||
|  |       { | ||||||
|  |         id: 57, | ||||||
|  |         mobile: '13862027511', | ||||||
|  |         nickname: '王西', | ||||||
|  |         avatar: | ||||||
|  |           'https://dci-file-new.bj.bcebos.com/fonchain-main/test/runtime/image/avatar/40/b8ed6fea-6662-416d-8bb3-1fd8a8197061.jpg', | ||||||
|  |         created_at: '2025-03-27 14:44:23', | ||||||
|  |         erp_user_id: 18229 | ||||||
|  |       } | ||||||
|  |     ], | ||||||
|  |     user_count: 9, | ||||||
|  |     group_infos: null, | ||||||
|  |     group_member_infos: [ | ||||||
|  |       { | ||||||
|  |         id: 79, | ||||||
|  |         group_id: 1, | ||||||
|  |         group_name: '泰丰国际', | ||||||
|  |         group_type: 4, | ||||||
|  |         group_avatar: '', | ||||||
|  |         group_num: 103, | ||||||
|  |         user_id: 103, | ||||||
|  |         user_name: '王静', | ||||||
|  |         user_card: '', | ||||||
|  |         created_at: '2025-03-27 14:44:24' | ||||||
|  |       }, | ||||||
|  |       { | ||||||
|  |         id: 55, | ||||||
|  |         group_id: 1, | ||||||
|  |         group_name: '泰丰国际', | ||||||
|  |         group_type: 4, | ||||||
|  |         group_avatar: '', | ||||||
|  |         group_num: 103, | ||||||
|  |         user_id: 57, | ||||||
|  |         user_name: '王西', | ||||||
|  |         user_card: '', | ||||||
|  |         created_at: '2025-03-27 14:44:24' | ||||||
|  |       }, | ||||||
|  |       { | ||||||
|  |         id: 35, | ||||||
|  |         group_id: 1, | ||||||
|  |         group_name: '泰丰国际', | ||||||
|  |         group_type: 4, | ||||||
|  |         group_avatar: '', | ||||||
|  |         group_num: 103, | ||||||
|  |         user_id: 37, | ||||||
|  |         user_name: '王雯婷', | ||||||
|  |         user_card: '', | ||||||
|  |         created_at: '2025-03-27 14:44:24' | ||||||
|  |       } | ||||||
|  |     ], | ||||||
|  |     group_count: 9, | ||||||
|  |     general_infos: [ | ||||||
|  |       { | ||||||
|  |         receiver_id: 40, | ||||||
|  |         receiver_name: '孙志远', | ||||||
|  |         receiver_avatar: | ||||||
|  |           'https://dci-file-new.bj.bcebos.com/fonchain-main/test/runtime/image/avatar/40/b8ed6fea-6662-416d-8bb3-1fd8a8197061.jpg', | ||||||
|  |         group_num: 0, | ||||||
|  |         talk_type: 1, | ||||||
|  |         count: 3, | ||||||
|  |         group_type: 0 | ||||||
|  |       }, | ||||||
|  |       { | ||||||
|  |         receiver_id: 2, | ||||||
|  |         receiver_name: '吹牛个人群', | ||||||
|  |         receiver_avatar: '', | ||||||
|  |         group_num: 4, | ||||||
|  |         talk_type: 2, | ||||||
|  |         count: 2, | ||||||
|  |         group_type: 3 | ||||||
|  |       }, | ||||||
|  |       { | ||||||
|  |         receiver_id: 3, | ||||||
|  |         receiver_name: '丰链30自己人', | ||||||
|  |         receiver_avatar: | ||||||
|  |           'https://e-cdn.fontree.cn/fonchain-main/prod/image/default/fonchain-chat/3d273326-d2a5-4ad4-a974-32d020c6b3f9.jpg?width=1080&height=2412', | ||||||
|  |         group_num: 8, | ||||||
|  |         talk_type: 2, | ||||||
|  |         count: 2, | ||||||
|  |         group_type: 3 | ||||||
|  |       } | ||||||
|  |     ], | ||||||
|  |     record_count: 3 | ||||||
|  |   } | ||||||
|  |   if ((data.user_infos || []).length > 0) { | ||||||
|  |     ;(data.user_infos || []).forEach((item) => { | ||||||
|  |       item.group_type = 0 | ||||||
|  |     }) | ||||||
|  |   } | ||||||
|  |   if ((data.group_infos || []).length > 0) { | ||||||
|  |     ;(data.group_infos || []).forEach((item) => { | ||||||
|  |       item.group_type = item.type | ||||||
|  |       item.groupTempType = 'group_infos' | ||||||
|  |     }) | ||||||
|  |   } | ||||||
|  |   if ((data.group_member_infos || []).length > 0) { | ||||||
|  |     ;(data.group_member_infos || []).forEach((item) => { | ||||||
|  |       item.groupTempType = 'group_member_infos' | ||||||
|  |     }) | ||||||
|  |   } | ||||||
|  |   if ((data.talk_record_infos || []).length > 0) { | ||||||
|  |     let receiverInfo = JSON.parse(JSON.stringify(data.talk_record_infos[0])) | ||||||
|  |     if (receiverInfo.talk_type === 1) { | ||||||
|  |       //单聊才需此判断 | ||||||
|  |       if (receiverInfo.user_id === state.uid) { | ||||||
|  |         //发送人是自己,接收人不需要变 | ||||||
|  |       } | ||||||
|  |       if (receiverInfo.receiver_id === state.uid) { | ||||||
|  |         //接收人是自己,这里需要变成对方 | ||||||
|  |         let temp_id = receiverInfo.receiver_id | ||||||
|  |         let temp_name = receiverInfo.receiver_name | ||||||
|  |         let temp_avatar = receiverInfo.receiver_avatar | ||||||
|  |         receiverInfo.receiver_id = receiverInfo.user_id | ||||||
|  |         receiverInfo.receiver_name = receiverInfo.user_name | ||||||
|  |         receiverInfo.receiver_avatar = receiverInfo.user_avatar | ||||||
|  |         receiverInfo.user_id = temp_id | ||||||
|  |         receiverInfo.user_name = temp_name | ||||||
|  |         receiverInfo.user_avatar = temp_avatar | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |     state.first_talk_record_infos = Object.assign({}, state.first_talk_record_infos, receiverInfo) | ||||||
|  |     ;(data.talk_record_infos || []).forEach((item) => { | ||||||
|  |       item.group_type = 0 | ||||||
|  |     }) | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   let tempGeneral_infos = Array.isArray(data.general_infos) | ||||||
|  |     ? [...data.general_infos] | ||||||
|  |     : data.general_infos | ||||||
|  |   delete data.general_infos | ||||||
|  |   data.combinedGroup = (data.group_infos || []).concat(data.group_member_infos || []) | ||||||
|  |   data.general_infos = tempGeneral_infos | ||||||
|  | 
 | ||||||
|  |   // 检查数据是否为空 | ||||||
|  |   let isEmpty = true | ||||||
|  |   let dataKeys = Object.keys(data) | ||||||
|  |   let paginationKey = '' | ||||||
|  |   dataKeys.forEach((item) => { | ||||||
|  |     if (Array.isArray(data[item]) && data[item].length > 0) { | ||||||
|  |       paginationKey = item | ||||||
|  |       isEmpty = false | ||||||
|  |     } | ||||||
|  |   }) | ||||||
|  | 
 | ||||||
|  |   if (isEmpty) { | ||||||
|  |     if (pageNum === 1) { | ||||||
|  |       // 第一页请求且为空,清空结果 | ||||||
|  |       state.searchResult = null | ||||||
|  |       // zPaging.value?.complete([]) | ||||||
|  |     } else { | ||||||
|  |       // 加载更多且为空,保持原列表不变 | ||||||
|  |       // zPaging.value?.complete(state.searchResult ? [state.searchResult] : []) | ||||||
|  |     } | ||||||
|  |   } else { | ||||||
|  |     if (props.isPagination) { | ||||||
|  |       if (pageNum === 1) { | ||||||
|  |         // 第一页请求,直接设置新数据 | ||||||
|  |         state.searchResult = data | ||||||
|  |       } else { | ||||||
|  |         // 加载更多,合并数据 | ||||||
|  |         if ( | ||||||
|  |           paginationKey && | ||||||
|  |           Array.isArray((state?.searchResult && state?.searchResult[paginationKey]) || []) | ||||||
|  |         ) { | ||||||
|  |           data[paginationKey] = state.searchResult[paginationKey].concat(data[paginationKey]) | ||||||
|  |         } | ||||||
|  |         state.searchResult = data | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |       emits( | ||||||
|  |         'lastIdChange', | ||||||
|  |         data.last_id, | ||||||
|  |         data.last_group_id, | ||||||
|  |         data.last_member_id, | ||||||
|  |         data.last_receiver_user_name, | ||||||
|  |         data.last_receiver_group_name | ||||||
|  |       ) | ||||||
|  |       let total = data.count | ||||||
|  |       if (props.searchRecordDetail) { | ||||||
|  |         if (state?.first_talk_record_infos?.talk_type === 1) { | ||||||
|  |           total = data.user_record_count | ||||||
|  |         } else if (state?.first_talk_record_infos?.talk_type === 2) { | ||||||
|  |           total = data.group_record_count | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |       // zPaging.value?.completeByTotal([data], total) | ||||||
|  |     } else { | ||||||
|  |       state.searchResult = data | ||||||
|  |       // zPaging.value?.complete([data]) | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |   // const resp = props.apiRequest(params) | ||||||
|  |   // resp.then(({ code, data }) => { | ||||||
|  |   //   console.log(data) | ||||||
|  |   //   if (code == 200) { | ||||||
|  |   //     if ((data.user_infos || []).length > 0) { | ||||||
|  |   //       ;(data.user_infos || []).forEach((item) => { | ||||||
|  |   //         item.group_type = 0 | ||||||
|  |   //       }) | ||||||
|  |   //     } | ||||||
|  |   //     if ((data.group_infos || []).length > 0) { | ||||||
|  |   //       ;(data.group_infos || []).forEach((item) => { | ||||||
|  |   //         item.group_type = item.type | ||||||
|  |   //         item.groupTempType = 'group_infos' | ||||||
|  |   //       }) | ||||||
|  |   //     } | ||||||
|  |   //     if ((data.group_member_infos || []).length > 0) { | ||||||
|  |   //       ;(data.group_member_infos || []).forEach((item) => { | ||||||
|  |   //         item.groupTempType = 'group_member_infos' | ||||||
|  |   //       }) | ||||||
|  |   //     } | ||||||
|  |   //     if ((data.talk_record_infos || []).length > 0) { | ||||||
|  |   //       let receiverInfo = JSON.parse(JSON.stringify(data.talk_record_infos[0])) | ||||||
|  |   //       if (receiverInfo.talk_type === 1) { | ||||||
|  |   //         //单聊才需此判断 | ||||||
|  |   //         if (receiverInfo.user_id === state.uid) { | ||||||
|  |   //           //发送人是自己,接收人不需要变 | ||||||
|  |   //         } | ||||||
|  |   //         if (receiverInfo.receiver_id === state.uid) { | ||||||
|  |   //           //接收人是自己,这里需要变成对方 | ||||||
|  |   //           let temp_id = receiverInfo.receiver_id | ||||||
|  |   //           let temp_name = receiverInfo.receiver_name | ||||||
|  |   //           let temp_avatar = receiverInfo.receiver_avatar | ||||||
|  |   //           receiverInfo.receiver_id = receiverInfo.user_id | ||||||
|  |   //           receiverInfo.receiver_name = receiverInfo.user_name | ||||||
|  |   //           receiverInfo.receiver_avatar = receiverInfo.user_avatar | ||||||
|  |   //           receiverInfo.user_id = temp_id | ||||||
|  |   //           receiverInfo.user_name = temp_name | ||||||
|  |   //           receiverInfo.user_avatar = temp_avatar | ||||||
|  |   //         } | ||||||
|  |   //       } | ||||||
|  |   //       state.first_talk_record_infos = Object.assign( | ||||||
|  |   //         {}, | ||||||
|  |   //         state.first_talk_record_infos, | ||||||
|  |   //         receiverInfo, | ||||||
|  |   //       ) | ||||||
|  |   //       ;(data.talk_record_infos || []).forEach((item) => { | ||||||
|  |   //         item.group_type = 0 | ||||||
|  |   //       }) | ||||||
|  |   //     } | ||||||
|  | 
 | ||||||
|  |   //     let tempGeneral_infos = Array.isArray(data.general_infos) | ||||||
|  |   //       ? [...data.general_infos] | ||||||
|  |   //       : data.general_infos | ||||||
|  |   //     delete data.general_infos | ||||||
|  |   //     data.combinedGroup = (data.group_infos || []).concat( | ||||||
|  |   //       data.group_member_infos || [], | ||||||
|  |   //     ) | ||||||
|  |   //     data.general_infos = tempGeneral_infos | ||||||
|  | 
 | ||||||
|  |   //     // 检查数据是否为空 | ||||||
|  |   //     let isEmpty = true | ||||||
|  |   //     let dataKeys = Object.keys(data) | ||||||
|  |   //     let paginationKey = '' | ||||||
|  |   //     dataKeys.forEach((item) => { | ||||||
|  |   //       if (Array.isArray(data[item]) && data[item].length > 0) { | ||||||
|  |   //         paginationKey = item | ||||||
|  |   //         isEmpty = false | ||||||
|  |   //       } | ||||||
|  |   //     }) | ||||||
|  | 
 | ||||||
|  |   //     if (isEmpty) { | ||||||
|  |   //       if (pageNum === 1) { | ||||||
|  |   //         // 第一页请求且为空,清空结果 | ||||||
|  |   //         state.searchResult = null | ||||||
|  |   //         zPaging.value?.complete([]) | ||||||
|  |   //       } else { | ||||||
|  |   //         // 加载更多且为空,保持原列表不变 | ||||||
|  |   //         zPaging.value?.complete(state.searchResult ? [state.searchResult] : []) | ||||||
|  |   //       } | ||||||
|  |   //     } else { | ||||||
|  |   //       if (props.isPagination) { | ||||||
|  |   //         if (pageNum === 1) { | ||||||
|  |   //           // 第一页请求,直接设置新数据 | ||||||
|  |   //           state.searchResult = data | ||||||
|  |   //         } else { | ||||||
|  |   //           // 加载更多,合并数据 | ||||||
|  |   //           if ( | ||||||
|  |   //             paginationKey && | ||||||
|  |   //             Array.isArray( | ||||||
|  |   //               (state?.searchResult && state?.searchResult[paginationKey]) || [], | ||||||
|  |   //             ) | ||||||
|  |   //           ) { | ||||||
|  |   //             data[paginationKey] = state.searchResult[paginationKey].concat( | ||||||
|  |   //               data[paginationKey], | ||||||
|  |   //             ) | ||||||
|  |   //           } | ||||||
|  |   //           state.searchResult = data | ||||||
|  |   //         } | ||||||
|  | 
 | ||||||
|  |   //         emits( | ||||||
|  |   //           'lastIdChange', | ||||||
|  |   //           data.last_id, | ||||||
|  |   //           data.last_group_id, | ||||||
|  |   //           data.last_member_id, | ||||||
|  |   //           data.last_receiver_user_name, | ||||||
|  |   //           data.last_receiver_group_name, | ||||||
|  |   //         ) | ||||||
|  |   //         let total = data.count | ||||||
|  |   //         if (props.searchRecordDetail) { | ||||||
|  |   //           if (state?.first_talk_record_infos?.talk_type === 1) { | ||||||
|  |   //             total = data.user_record_count | ||||||
|  |   //           } else if (state?.first_talk_record_infos?.talk_type === 2) { | ||||||
|  |   //             total = data.group_record_count | ||||||
|  |   //           } | ||||||
|  |   //         } | ||||||
|  |   //         zPaging.value?.completeByTotal([data], total) | ||||||
|  |   //       } else { | ||||||
|  |   //         state.searchResult = data | ||||||
|  |   //         zPaging.value?.complete([data]) | ||||||
|  |   //       } | ||||||
|  |   //     } | ||||||
|  |   //   } else { | ||||||
|  |   //     if (pageNum === 1) { | ||||||
|  |   //       // 第一页请求失败,清空结果 | ||||||
|  |   //       state.searchResult = null | ||||||
|  |   //       zPaging.value?.complete([]) | ||||||
|  |   //     } else { | ||||||
|  |   //       // 加载更多失败,保持原列表不变 | ||||||
|  |   //       zPaging.value?.complete(state.searchResult ? [state.searchResult] : []) | ||||||
|  |   //     } | ||||||
|  |   //   } | ||||||
|  |   // }) | ||||||
|  | 
 | ||||||
|  |   // resp.catch(() => { | ||||||
|  |   //   if (pageNum === 1) { | ||||||
|  |   //     // 第一页请求异常,清空结果 | ||||||
|  |   //     state.searchResult = null | ||||||
|  |   //     zPaging.value?.complete([]) | ||||||
|  |   //   } else { | ||||||
|  |   //     // 加载更多异常,保持原列表不变 | ||||||
|  |   //     zPaging.value?.complete(state.searchResult ? [state.searchResult] : []) | ||||||
|  |   //   } | ||||||
|  |   // }) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | //点击取消搜索 | ||||||
|  | const cancelSearch = () => { | ||||||
|  |   const pages = getCurrentPages() | ||||||
|  |   if (pages.length > 1) { | ||||||
|  |     uni.navigateBack({ | ||||||
|  |       delta: 1 | ||||||
|  |     }) | ||||||
|  |   } else { | ||||||
|  |     uni.reLaunch({ | ||||||
|  |       url: '/pages/index/index' | ||||||
|  |     }) | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | //获取key对应值 | ||||||
|  | const getResultKeysValue = (keys) => { | ||||||
|  |   let resultKey = '' | ||||||
|  |   switch (keys) { | ||||||
|  |     case 'user_infos': | ||||||
|  |       resultKey = '通讯录' | ||||||
|  |       break | ||||||
|  |     case 'group_infos': | ||||||
|  |       resultKey = '群聊' | ||||||
|  |       break | ||||||
|  |     case 'group_member_infos': | ||||||
|  |       resultKey = '群聊' | ||||||
|  |       break | ||||||
|  |     case 'combinedGroup': | ||||||
|  |       resultKey = '群聊' | ||||||
|  |       break | ||||||
|  |     case 'general_infos': | ||||||
|  |       resultKey = '聊天记录' | ||||||
|  |       break | ||||||
|  |     case 'talk_record_infos': | ||||||
|  |       resultKey = '相关聊天记录' | ||||||
|  |       break | ||||||
|  |     default: | ||||||
|  |       resultKey = '' | ||||||
|  |   } | ||||||
|  |   return resultKey | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | //是否还有更多数据 | ||||||
|  | const getHasMoreResult = (searchResultKey) => { | ||||||
|  |   let has_more_result = '' | ||||||
|  |   switch (searchResultKey) { | ||||||
|  |     case 'user_infos': | ||||||
|  |       if (state.searchResult['user_count'] && state.searchResult['user_count'] > 3) { | ||||||
|  |         has_more_result = '更多通讯录' | ||||||
|  |       } | ||||||
|  |       break | ||||||
|  |     case 'group_infos': | ||||||
|  |       if (state.searchResult['group_count'] && state.searchResult['group_count'] > 3) { | ||||||
|  |         has_more_result = '更多群聊' | ||||||
|  |       } | ||||||
|  |       break | ||||||
|  |     case 'group_member_infos': | ||||||
|  |       if (state.searchResult['group_count'] && state.searchResult['group_count'] > 3) { | ||||||
|  |         has_more_result = '更多群聊' | ||||||
|  |       } | ||||||
|  |       break | ||||||
|  |     case 'combinedGroup': | ||||||
|  |       if (state.searchResult['group_count'] && state.searchResult['group_count'] > 3) { | ||||||
|  |         has_more_result = '更多群聊' | ||||||
|  |       } | ||||||
|  |       break | ||||||
|  |     case 'general_infos': | ||||||
|  |       if (state.searchResult['record_count'] && state.searchResult['record_count'] >= 3) { | ||||||
|  |         has_more_result = '更多聊天记录' | ||||||
|  |       } | ||||||
|  |       break | ||||||
|  |     default: | ||||||
|  |   } | ||||||
|  |   return has_more_result | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | //点击跳转到更多结果页面 | ||||||
|  | const toMoreResultPage = (searchResultKey) => { | ||||||
|  |   emits('toMoreResultPage', searchResultKey, state.searchText) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | //点击了搜索结果项 | ||||||
|  | const clickSearchItem = (searchResultKey, searchItem) => { | ||||||
|  |   console.log(searchResultKey, searchItem) | ||||||
|  |   let talk_type = searchItem.talk_type | ||||||
|  |   let receiver_id = searchItem.receiver_id | ||||||
|  |   if (searchResultKey === 'user_infos') { | ||||||
|  |     talk_type = 1 | ||||||
|  |     receiver_id = searchItem.id | ||||||
|  |   } else if (searchResultKey === 'combinedGroup') { | ||||||
|  |     talk_type = searchItem.type || 2 | ||||||
|  |     receiver_id = searchItem.group_id || searchItem.id | ||||||
|  |   } else if (searchResultKey === 'general_infos') { | ||||||
|  |     if (searchItem.talk_type === 1) { | ||||||
|  |       if (searchItem.user_id === state.uid) { | ||||||
|  |         //发送人是自己,接收人不需要变 | ||||||
|  |       } | ||||||
|  |       if (searchItem.receiver_id === state.uid) { | ||||||
|  |         //接收人是自己,这里需要变成对方 | ||||||
|  |         let temp_id = searchItem.receiver_id | ||||||
|  |         let temp_name = searchItem.receiver_name | ||||||
|  |         let temp_avatar = searchItem.receiver_avatar | ||||||
|  |         searchItem.receiver_id = searchItem.user_id | ||||||
|  |         searchItem.receiver_name = searchItem.user_name | ||||||
|  |         searchItem.receiver_avatar = searchItem.user_avatar | ||||||
|  |         searchItem.user_id = temp_id | ||||||
|  |         searchItem.user_name = temp_name | ||||||
|  |         searchItem.user_avatar = temp_avatar | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |   emits( | ||||||
|  |     'clickSearchItem', | ||||||
|  |     state.searchText, | ||||||
|  |     searchResultKey, | ||||||
|  |     talk_type, | ||||||
|  |     receiver_id, | ||||||
|  |     encodeURIComponent(JSON.stringify(searchItem)) | ||||||
|  |   ) | ||||||
|  | } | ||||||
|  | </script> | ||||||
|  | <style lang="scss" scoped> | ||||||
|  | .search-list { | ||||||
|  |   .searchRoot { | ||||||
|  |     padding: 10px 24px; | ||||||
|  |     display: flex; | ||||||
|  |     flex-direction: row; | ||||||
|  |     align-items: center; | ||||||
|  |     justify-content: flex-start; | ||||||
|  | 
 | ||||||
|  |     .searchRoot_cancelBtn { | ||||||
|  |       line-height: 22px; | ||||||
|  |       color: #46299d; | ||||||
|  |       margin: 0 0 0 10px; | ||||||
|  |       flex-shrink: 0; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |   .search-record-detail { | ||||||
|  |     padding: 0 25px; | ||||||
|  |   } | ||||||
|  |   .search-result { | ||||||
|  |     width: 100%; | ||||||
|  |     padding: 0 10px; | ||||||
|  |     display: flex; | ||||||
|  |     flex-direction: row; | ||||||
|  |     align-items: flex-start; | ||||||
|  |     justify-content: flex-start; | ||||||
|  |     box-sizing: border-box; | ||||||
|  | 
 | ||||||
|  |     .search-result-list { | ||||||
|  |       width: 100%; | ||||||
|  |       // padding: 0 9px; | ||||||
|  | 
 | ||||||
|  |       .search-result-part { | ||||||
|  |         margin: 18px 0 0; | ||||||
|  | 
 | ||||||
|  |         .result-title { | ||||||
|  |           padding: 0 0 5px; | ||||||
|  |           border-bottom: 1px solid #f8f8f8; | ||||||
|  |           span { | ||||||
|  |             line-height: 20px; | ||||||
|  |             color: #999999; | ||||||
|  |           } | ||||||
|  |         } | ||||||
|  |         .result-has-more { | ||||||
|  |           padding: 10px 0; | ||||||
|  |           border-bottom: 1px solid #f8f8f8; | ||||||
|  |           span { | ||||||
|  |             color: #191919; | ||||||
|  |             line-height: 20px; | ||||||
|  |           } | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | </style> | ||||||
| @ -12,6 +12,7 @@ import { | |||||||
| } from "vue"; | } from "vue"; | ||||||
| import Sortable from "sortablejs"; | import Sortable from "sortablejs"; | ||||||
| import { debounce } from "lodash-es"; | import { debounce } from "lodash-es"; | ||||||
|  | import { NDataTable } from "naive-ui"; | ||||||
| 
 | 
 | ||||||
| // Props 定义 | // Props 定义 | ||||||
| const props = defineProps({ | const props = defineProps({ | ||||||
|  | |||||||
| @ -1,144 +0,0 @@ | |||||||
| import { createApp, h, ref } from 'vue' |  | ||||||
| import { NImage, NImageGroup } from 'naive-ui' |  | ||||||
| 
 |  | ||||||
| interface PreviewOptions { |  | ||||||
|   onStart?: () => void |  | ||||||
|   onError?: (e: Event) => void |  | ||||||
|   showToolbar?: boolean |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| class ImagePreview { |  | ||||||
|   private static instance: { |  | ||||||
|     app: any |  | ||||||
|     container: HTMLElement |  | ||||||
|   } | null = null |  | ||||||
| 
 |  | ||||||
|   static async preview( |  | ||||||
|     sources: string | File | (string | File)[],  |  | ||||||
|     index = 0, |  | ||||||
|     options: PreviewOptions = {}, |  | ||||||
|   ) { |  | ||||||
|     try { |  | ||||||
|       const urls = await this.normalizeImageSources( |  | ||||||
|         Array.isArray(sources) ? sources : [sources] |  | ||||||
|       ) |  | ||||||
| 
 |  | ||||||
|       if (!urls.length) { |  | ||||||
|         console.warn('[ImagePreview] No valid image sources') |  | ||||||
|         return |  | ||||||
|       } |  | ||||||
| 
 |  | ||||||
|       this.destroy() |  | ||||||
|       options.onStart?.() |  | ||||||
| 
 |  | ||||||
|       const container = document.createElement('div') |  | ||||||
|       container.style.display = 'none' |  | ||||||
|       document.body.appendChild(container) |  | ||||||
| 
 |  | ||||||
|       const app = createApp({ |  | ||||||
|         setup() { |  | ||||||
|           const imageRef = ref<InstanceType<typeof NImage> | null>(null) |  | ||||||
| 
 |  | ||||||
|           return () => { |  | ||||||
|             if (urls.length === 1) { |  | ||||||
|               return h(NImage, { |  | ||||||
|                 ref: imageRef, |  | ||||||
|                 src: urls[0], |  | ||||||
|                 previewDisabled: false, |  | ||||||
|                 preview: true, |  | ||||||
|                 showToolbar: options.showToolbar ?? true, |  | ||||||
|                 style: { |  | ||||||
|                   display: 'none' |  | ||||||
|                 }, |  | ||||||
|                 onLoad: () => { |  | ||||||
|                   imageRef.value?.click() |  | ||||||
|                 } |  | ||||||
|               }) |  | ||||||
|             } else { |  | ||||||
|               return h(NImageGroup, { |  | ||||||
|                 showToolbar: options.showToolbar ?? true, |  | ||||||
|                 currentIndex: index |  | ||||||
|               }, { |  | ||||||
|                 default: () => urls.map((url, i) => { |  | ||||||
|                   const imgRef = ref<InstanceType<typeof NImage> | null>(null) |  | ||||||
|                   return h(NImage, { |  | ||||||
|                     ref: i === index ? imgRef : undefined, |  | ||||||
|                     src: url, |  | ||||||
|                     previewDisabled: false, |  | ||||||
|                     preview: true, |  | ||||||
|                     style: { |  | ||||||
|                       display: 'none' |  | ||||||
|                     }, |  | ||||||
|                     onLoad: i === index ? () => { |  | ||||||
|                       imgRef.value?.click() |  | ||||||
|                     } : undefined |  | ||||||
|                   }) |  | ||||||
|                 }) |  | ||||||
|               }) |  | ||||||
|             } |  | ||||||
|           } |  | ||||||
|         } |  | ||||||
|       }) |  | ||||||
| 
 |  | ||||||
|       app.mount(container) |  | ||||||
|       this.instance = { app, container } |  | ||||||
| 
 |  | ||||||
|     } catch (error) { |  | ||||||
|       console.error('[ImagePreview] Error:', error) |  | ||||||
|       options.onError?.(error as Event) |  | ||||||
|     } |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   private static async normalizeImageSources(sources: (string | File)[]): Promise<string[]> { |  | ||||||
|     const urls: string[] = [] |  | ||||||
| 
 |  | ||||||
|     for (const source of sources) { |  | ||||||
|       try { |  | ||||||
|         if (typeof source === 'string') { |  | ||||||
|           if (source.startsWith('data:') || source.startsWith('http')) { |  | ||||||
|             urls.push(source) |  | ||||||
|           } else { |  | ||||||
|             console.warn('[ImagePreview] Invalid image source:', source) |  | ||||||
|           } |  | ||||||
|         } else if (source instanceof File) { |  | ||||||
|           const url = await this.fileToUrl(source) |  | ||||||
|           urls.push(url) |  | ||||||
|         } |  | ||||||
|       } catch (error) { |  | ||||||
|         console.warn('[ImagePreview] Failed to process source:', source, error) |  | ||||||
|       } |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     return urls |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   private static fileToUrl(file: File): Promise<string> { |  | ||||||
|     return new Promise((resolve, reject) => { |  | ||||||
|       if (!file.type.startsWith('image/')) { |  | ||||||
|         reject(new Error('Not an image file')) |  | ||||||
|         return |  | ||||||
|       } |  | ||||||
| 
 |  | ||||||
|       const reader = new FileReader() |  | ||||||
|       reader.onload = () => resolve(reader.result as string) |  | ||||||
|       reader.onerror = reject |  | ||||||
|       reader.readAsDataURL(file) |  | ||||||
|     }) |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   private static destroy() { |  | ||||||
|     if (this.instance) { |  | ||||||
|       const { app, container } = this.instance |  | ||||||
|       app.unmount() |  | ||||||
|       container.remove() |  | ||||||
|       this.instance = null |  | ||||||
|     } |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   static close() { |  | ||||||
|     this.destroy() |  | ||||||
|   } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| export const previewImage = ImagePreview.preview.bind(ImagePreview) |  | ||||||
| export const closePreview = ImagePreview.close.bind(ImagePreview) |  | ||||||
| @ -34,6 +34,10 @@ const props = defineProps({ | |||||||
|   loading: { |   loading: { | ||||||
|     type: Boolean, |     type: Boolean, | ||||||
|     default: false, |     default: false, | ||||||
|  |   }, | ||||||
|  |   customInputPlaceholder: { | ||||||
|  |     type: String, | ||||||
|  |     default: '请输入', | ||||||
|   } |   } | ||||||
| }) | }) | ||||||
| 
 | 
 | ||||||
| @ -205,7 +209,7 @@ initData() | |||||||
|           <template v-if="item.type === 'input'"> |           <template v-if="item.type === 'input'"> | ||||||
|             <n-input |             <n-input | ||||||
|               :value="formData[item.key]" |               :value="formData[item.key]" | ||||||
|               placeholder="请输入" |               :placeholder="customInputPlaceholder" | ||||||
|               @input="value => handleInputChange(value, item)" |               @input="value => handleInputChange(value, item)" | ||||||
|               clearable |               clearable | ||||||
|               v-bind="item.props" |               v-bind="item.props" | ||||||
|  | |||||||
| @ -18,7 +18,7 @@ export function isLoggedIn() { | |||||||
|  */ |  */ | ||||||
| export function getAccessToken() { | export function getAccessToken() { | ||||||
|   // return storage.get(AccessToken) || ''
 |   // return storage.get(AccessToken) || ''
 | ||||||
|   return JSON.parse(localStorage.getItem('token'))||'46d71a72d8d845ad7ed23eba9bdde260e635407190c2ce1bf7fd22088e41682ea07773ec65cae8946d2003f264d55961f96e0fc5da10eb96d3a348c1664e9644ce2108c311309f398ae8ea1b8200bfd490e5cb6e8c52c9e5d493cbabb163368f8351420451a631dbfa749829ee4cda49b77b5ed2d3dced5d0f2b7dd9ee76ba5465c84a17c23af040cd92b6b2a4ea48befbb5c729dcdad0a9c9668befe84074cc24f78899c1d947f8e7f94c7eda5325b8ed698df729e76febb98549ef3482ae942fb4f4a1c92d21836fa784728f0c5483aab2760a991b6b36e6b10c84f840a6433a6ecc31dee36e8f1c6158818bc89d22851b5e2f6cbf01ab4b3da6e3d06b57c8f750e106226a5a4b9d7fc1d381a54cb92375c09ba1fa8e5fde0392d919c2f2cbcc7e4e2eca8d9860749af00374b249f7d04e2bc43a1fa4e7d7384dde0212f0a5' |   return JSON.parse(localStorage.getItem('token'))||'79b5c732d96d2b27a48a99dfd4a5566c43aaa5796242e854ebe3ffc198d6876b9628e7b764d9af65ab5dbb2d517ced88170491b74b048c0ba827c0d3741462cb89dc59ed46653a449af837a8262941caaef1334d640773710f8cd96473bacfb190cba595a5d6a9c87d70f0999a3ebb41147213b31b4bdccffca66a56acf3baab5af0154f0dce360079f37709f78e13711036899344bddb0fb4cf0f2890287cb62c3fcbe33368caa5e213624577be8b8420ab75b1f50775ee16142a4321c5d56995f37354a66a969da98d95ba6e65d142ed097e04b411c1ebad2f62866d0ec7e1838420530a9941dbbcd00490199f8b8987a3f4c77465504ba46f90370546f2f3656c248bd63549a43410178becfd0d1888ecbb8e82d291e68cf9da8ec1096e2d8abd4bb9d7edc62d38469e56226683693764f82df03eaa47fe6fd21a9cb83e0e' | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  | |||||||
| @ -1,8 +1,27 @@ | |||||||
| <script lang="ts" setup> | <script lang="ts" setup> | ||||||
| import { computed, ref, onMounted, watch, reactive, onBeforeMount, getCurrentInstance } from 'vue' | import { | ||||||
|  |   computed, | ||||||
|  |   ref, | ||||||
|  |   onMounted, | ||||||
|  |   watch, | ||||||
|  |   reactive, | ||||||
|  |   onBeforeMount, | ||||||
|  |   getCurrentInstance, | ||||||
|  |   h | ||||||
|  | } from 'vue' | ||||||
| import { onBeforeRouteUpdate } from 'vue-router' | import { onBeforeRouteUpdate } from 'vue-router' | ||||||
| import { useDialogueStore, useTalkStore } from '@/store' | import { useDialogueStore, useTalkStore } from '@/store' | ||||||
| import { NDropdown, NIcon, NInput, NPopover, NTabs, NTab, NCard } from 'naive-ui' | import { | ||||||
|  |   NDropdown, | ||||||
|  |   NIcon, | ||||||
|  |   NInput, | ||||||
|  |   NPopover, | ||||||
|  |   NTabs, | ||||||
|  |   NTab, | ||||||
|  |   NCard, | ||||||
|  |   NButton, | ||||||
|  |   NPagination | ||||||
|  | } from 'naive-ui' | ||||||
| import { Search, Plus } from '@icon-park/vue-next' | import { Search, Plus } from '@icon-park/vue-next' | ||||||
| import TalkItem from './TalkItem.vue' | import TalkItem from './TalkItem.vue' | ||||||
| import Skeleton from './Skeleton.vue' | import Skeleton from './Skeleton.vue' | ||||||
| @ -13,11 +32,13 @@ import { ISession } from '@/types/chat' | |||||||
| import { useSessionMenu } from '@/hooks' | import { useSessionMenu } from '@/hooks' | ||||||
| import customModal from '@/components/common/customModal.vue' | import customModal from '@/components/common/customModal.vue' | ||||||
| import xSearchForm from '@/components/x-naive-ui/x-search-form/index.vue' | import xSearchForm from '@/components/x-naive-ui/x-search-form/index.vue' | ||||||
|  | import xNDataTable from '@/components/x-naive-ui/x-n-data-table/index.vue' | ||||||
| import flTree from '@/components/flnlayout/tree/flnindex.vue' | import flTree from '@/components/flnlayout/tree/flnindex.vue' | ||||||
| import { processError, processSuccess } from '@/utils/helper/message.js' | import { processError, processSuccess } from '@/utils/helper/message.js' | ||||||
|  | import chatAppSearchList from '@/components/search/searchList.vue' | ||||||
| 
 | 
 | ||||||
| const currentInstance = getCurrentInstance() | const currentInstance = getCurrentInstance() | ||||||
| const { $request } = currentInstance?.appContext.config.globalProperties | const $request = currentInstance?.appContext.config.globalProperties?.$request | ||||||
| 
 | 
 | ||||||
| const { | const { | ||||||
|   dropdown, |   dropdown, | ||||||
| @ -34,6 +55,48 @@ const searchKeyword = ref('') | |||||||
| const topItems = computed((): ISession[] => talkStore.topItems) | const topItems = computed((): ISession[] => talkStore.topItems) | ||||||
| const unreadNum = computed(() => talkStore.talkUnreadNum) | const unreadNum = computed(() => talkStore.talkUnreadNum) | ||||||
| 
 | 
 | ||||||
|  | //自定义搜索 | ||||||
|  | const renderChatAppSearch = () => { | ||||||
|  |   return h( | ||||||
|  |     chatAppSearchList, | ||||||
|  |     { | ||||||
|  |       // searchResultKey: 'user_infos', | ||||||
|  |       // searchItem: { | ||||||
|  |       //   avatar: | ||||||
|  |       //     'https://e-cdn.fontree.cn/fonchain-main/prod/image/18248/avatar/a0b2bee7-947f-465a-986e-10a1b2b87032.png', | ||||||
|  |       //   created_at: '2025-03-27 14:44:23', | ||||||
|  |       //   erp_user_id: 18248, | ||||||
|  |       //   id: 44, | ||||||
|  |       //   mobile: '18994430450', | ||||||
|  |       //   nickname: '周俊耀' | ||||||
|  |       // }, | ||||||
|  |       // searchText: '周' | ||||||
|  |       searchResultPageSize: 3, | ||||||
|  |       listLimit: true, | ||||||
|  |       apiRequest: ServeSeachQueryAll, | ||||||
|  |       searchText: '王', | ||||||
|  |       onClickSearchItem: (searchText, searchResultKey, talk_type, receiver_id, res) => { | ||||||
|  |         console.log(searchText, searchResultKey, talk_type, receiver_id) | ||||||
|  |         const result = JSON.parse(decodeURIComponent(res)) | ||||||
|  |         console.log(result) | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|  |     {} | ||||||
|  |   ) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | //ES搜索聊天记录-主页搜索什么都有、指定用户、指定群、群与用户概览 | ||||||
|  | const ServeSeachQueryAll = () => { | ||||||
|  |   let url = '/api/v1/elasticsearch/query-all' | ||||||
|  |   let params = {} | ||||||
|  |   let config = { | ||||||
|  |     baseURL: import.meta.env.VITE_BASE_API | ||||||
|  |   } | ||||||
|  |   return $request.HTTP.components.postDataByParams(url, params, config).then((res) => { | ||||||
|  |     console.log(res) | ||||||
|  |   }) | ||||||
|  | } | ||||||
|  | 
 | ||||||
| const state = reactive({ | const state = reactive({ | ||||||
|   isShowAddressBookModal: false, // 是否显示通讯录模态框 |   isShowAddressBookModal: false, // 是否显示通讯录模态框 | ||||||
|   customModalStyle: { |   customModalStyle: { | ||||||
| @ -41,20 +104,128 @@ const state = reactive({ | |||||||
|     height: '846px', |     height: '846px', | ||||||
|     backgroundColor: '#F9F9FD' |     backgroundColor: '#F9F9FD' | ||||||
|   }, //自定义模态框样式 |   }, //自定义模态框样式 | ||||||
|   searchConfig: [ |   addressBookSearchConfig: [ | ||||||
|     { |     { | ||||||
|       label: '姓名', |       label: '姓名', | ||||||
|       key: 'name', |       key: 'nickName', | ||||||
|       type: 'input', |       type: 'input', | ||||||
|       valueType: 'string' |       valueType: 'string' | ||||||
|     } |     } | ||||||
|   ], // 搜索配置 |   ], // 通讯录搜索配置 | ||||||
| 
 |   groupChatListSearchConfig: [ | ||||||
|  |     { | ||||||
|  |       label: '群聊名称', | ||||||
|  |       key: 'groupName', | ||||||
|  |       type: 'input', | ||||||
|  |       valueType: 'string' | ||||||
|  |     } | ||||||
|  |   ], // 群聊列表搜索配置 | ||||||
|   treeData: [], |   treeData: [], | ||||||
|   expandedKeys: [], |   expandedKeys: [], | ||||||
|   clickKey: '', |   clickKey: 3, | ||||||
|   treeRefreshCount: 0, |   treeRefreshCount: 0, | ||||||
|   treeSelectData: {} |   treeSelectData: {}, | ||||||
|  |   addressBookColumns: [ | ||||||
|  |     { | ||||||
|  |       title: '姓名 【工号】', | ||||||
|  |       field: 'nickName', | ||||||
|  |       width: 200, | ||||||
|  |       render(row, index) { | ||||||
|  |         return row.nickName + '【' + row.jobNum + '】' | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       title: '岗位名称', | ||||||
|  |       field: 'positionName', | ||||||
|  |       width: 400, | ||||||
|  |       ellipsis: true, | ||||||
|  |       render(row, index) { | ||||||
|  |         let positionNames = Array.isArray(row.depPositions) | ||||||
|  |           ? row.depPositions.flatMap((dep) => | ||||||
|  |               Array.isArray(dep.positions) ? dep.positions.map((pos) => pos.name) : [] | ||||||
|  |             ) | ||||||
|  |           : [] | ||||||
|  |         return positionNames.join(' , ') | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       title: '操作', | ||||||
|  |       field: 'action', | ||||||
|  |       width: 200, | ||||||
|  |       align: 'center', | ||||||
|  |       fixed: 'right', | ||||||
|  |       render(row, index) { | ||||||
|  |         return h( | ||||||
|  |           NButton, | ||||||
|  |           { | ||||||
|  |             size: 'small', | ||||||
|  |             text: true, | ||||||
|  |             color: '#46299d', | ||||||
|  |             onClick: () => handleEnterChat(row) | ||||||
|  |           }, | ||||||
|  |           { default: () => '进入聊天' } | ||||||
|  |         ) | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   ], // 通讯录表格列 | ||||||
|  |   groupChatListColumns: [ | ||||||
|  |     { | ||||||
|  |       title: '群聊名称', | ||||||
|  |       field: 'groupName', | ||||||
|  |       width: 200, | ||||||
|  |       render(row, index) { | ||||||
|  |         return row.groupName | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       title: '群类型', | ||||||
|  |       field: 'groupType', | ||||||
|  |       width: 400, | ||||||
|  |       ellipsis: true, | ||||||
|  |       render(row, index) { | ||||||
|  |         return row.groupType | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       title: '操作', | ||||||
|  |       field: 'action', | ||||||
|  |       width: 200, | ||||||
|  |       align: 'center', | ||||||
|  |       fixed: 'right', | ||||||
|  |       render(row, index) { | ||||||
|  |         return h( | ||||||
|  |           NButton, | ||||||
|  |           { | ||||||
|  |             size: 'small', | ||||||
|  |             text: true, | ||||||
|  |             color: '#46299d', | ||||||
|  |             onClick: () => handleEnterChat(row) | ||||||
|  |           }, | ||||||
|  |           { default: () => '进入聊天' } | ||||||
|  |         ) | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   ], // 群聊列表表格列 | ||||||
|  |   addressBookData: [], // 通讯录表格数据 | ||||||
|  |   groupChatListData: [], // 群聊列表表格数据 | ||||||
|  |   addressBookTableHeight: 524, // 通讯录表格高度 | ||||||
|  |   addressBookTableWidth: 800, // 通讯录表格宽度 | ||||||
|  |   addressBookPage: 1, // 通讯录表格页码 | ||||||
|  |   addressBookPageSize: 10, // 通讯录表格每页条数 | ||||||
|  |   addressBookTotal: 0, // 通讯录表格总条数 | ||||||
|  |   addressBookSearchNickName: '', // 通讯录搜索条件-姓名 | ||||||
|  |   addressBookCurrentTab: 'employeeAddressBook', // 通讯录当前tab | ||||||
|  |   groupChatListPage: 1, // 群聊列表表格页码 | ||||||
|  |   groupChatListPageSize: 10, // 群聊列表表格每页条数 | ||||||
|  |   groupChatListTotal: 0, // 群聊列表表格总条数 | ||||||
|  |   groupChatListSearchGroupName: '', // 群聊列表搜索条件-群聊名称 | ||||||
|  |   chatSearchOptions: [ | ||||||
|  |     { | ||||||
|  |       key: 'chatSearch', | ||||||
|  |       type: 'render', | ||||||
|  |       render: renderChatAppSearch | ||||||
|  |     } | ||||||
|  |   ] // 聊天搜索选项 | ||||||
| }) | }) | ||||||
| 
 | 
 | ||||||
| const items = computed((): ISession[] => { | const items = computed((): ISession[] => { | ||||||
| @ -68,13 +239,31 @@ const items = computed((): ISession[] => { | |||||||
|     return keyword.toLowerCase().indexOf(searchKeyword.value.toLowerCase()) != -1 |     return keyword.toLowerCase().indexOf(searchKeyword.value.toLowerCase()) != -1 | ||||||
|   }) |   }) | ||||||
| }) | }) | ||||||
| setTimeout(()=>{ | setTimeout(() => { | ||||||
|   console.log('items.value',items.value) |   console.log('items.value', items.value) | ||||||
| },1000) | }, 1000) | ||||||
| watch(() => talkStore, (newValue, oldValue) => { | watch( | ||||||
|   console.log(newValue); |   () => talkStore, | ||||||
|    |   (newValue, oldValue) => { | ||||||
| },{deep:true,immediate:true}) |     console.log(newValue) | ||||||
|  |   }, | ||||||
|  |   { deep: true, immediate: true } | ||||||
|  | ) | ||||||
|  | watch( | ||||||
|  |   () => state.addressBookSearchNickName, | ||||||
|  |   (newValue, oldValue) => { | ||||||
|  |     // console.log(newValue, 'newValue') | ||||||
|  |     if (newValue) { | ||||||
|  |       state.addressBookTableWidth = 1142 | ||||||
|  |       state.addressBookPage = 1 | ||||||
|  |     } else { | ||||||
|  |       state.addressBookTableWidth = 800 | ||||||
|  |       state.clickKey = 3 | ||||||
|  |       state.treeRefreshCount++ | ||||||
|  |     } | ||||||
|  |     getDepPoisUser() | ||||||
|  |   } | ||||||
|  | ) | ||||||
| 
 | 
 | ||||||
| // 列表加载状态 | // 列表加载状态 | ||||||
| const loadStatus = computed(() => talkStore.loadStatus) | const loadStatus = computed(() => talkStore.loadStatus) | ||||||
| @ -145,8 +334,11 @@ const showAddressBookModal = () => { | |||||||
|   state.isShowAddressBookModal = true |   state.isShowAddressBookModal = true | ||||||
| } | } | ||||||
| const handleTreeClick = ({ selectedKey, tree }) => { | const handleTreeClick = ({ selectedKey, tree }) => { | ||||||
|  |   // console.log(tree) | ||||||
|   state.clickKey = tree.key |   state.clickKey = tree.key | ||||||
|   state.treeSelectData = tree |   state.treeSelectData = tree | ||||||
|  |   state.addressBookPage = 1 | ||||||
|  |   getDepPoisUser() | ||||||
| } | } | ||||||
| const calcTreeData = (data) => { | const calcTreeData = (data) => { | ||||||
|   for (let item of data) { |   for (let item of data) { | ||||||
| @ -172,29 +364,8 @@ const getTreeData = () => { | |||||||
|         let data = res.data.nodes |         let data = res.data.nodes | ||||||
|         calcTreeData(data) |         calcTreeData(data) | ||||||
|         state.treeData = data |         state.treeData = data | ||||||
|         // // 获取最近点击的部门 |  | ||||||
|         // let localSelect = Local.get("posimanage_treeSelectData"); |  | ||||||
|         // if (localSelect && JSON.stringify(localSelect) !== "{}") { |  | ||||||
|         //   state.treeSelectData = localSelect; |  | ||||||
|         //   state.expandedKeys = localSelect.pathIds; |  | ||||||
|         //   state.clickKey = localSelect.key; |  | ||||||
|         // } else { |  | ||||||
|         //   if (JSON.stringify(state.treeSelectData) === "{}") { |  | ||||||
|         //     state.treeSelectData = data[0]; |  | ||||||
|         //     state.clickKey = data[0].key; |  | ||||||
|         //   } |  | ||||||
|         //   if ( |  | ||||||
|         //     state.clickKey === data[0].key && |  | ||||||
|         //     !state.expandedKeys.includes(data[0].key) |  | ||||||
|         //   ) { |  | ||||||
|         //     state.expandedKeys.push(data[0].key); |  | ||||||
|         //   } |  | ||||||
|         //   if (!state.expandedKeys.includes(state.clickKey)) { |  | ||||||
|         //     state.expandedKeys.push(state.clickKey); |  | ||||||
|         //   } |  | ||||||
|         // } |  | ||||||
|         state.treeRefreshCount++ |         state.treeRefreshCount++ | ||||||
|         // state.tableConfig.refreshCount++; |         getDepPoisUser() | ||||||
|       } else { |       } else { | ||||||
|         processError(res.msg || '获取失败!') |         processError(res.msg || '获取失败!') | ||||||
|       } |       } | ||||||
| @ -207,6 +378,66 @@ const getTreeData = () => { | |||||||
|     } |     } | ||||||
|   ) |   ) | ||||||
| } | } | ||||||
|  | // 获取部门下的人员 | ||||||
|  | const getDepPoisUser = () => { | ||||||
|  |   let url = '/user/v2/list' | ||||||
|  |   let params = { | ||||||
|  |     departmentId: state.addressBookSearchNickName ? undefined : state.clickKey, | ||||||
|  |     page: state.addressBookPage, | ||||||
|  |     pageSize: state.addressBookPageSize, | ||||||
|  |     status: 'notactive', | ||||||
|  |     nickName: state.addressBookSearchNickName | ||||||
|  |   } | ||||||
|  |   $request.HTTP.components.postDataByParams(url, params).then((res) => { | ||||||
|  |     // console.log(res) | ||||||
|  |     if (res.status === 0 && Array.isArray(res.data.data)) { | ||||||
|  |       state.addressBookData = res.data.data || [] | ||||||
|  |       state.addressBookTotal = res.data.count | ||||||
|  |     } | ||||||
|  |   }) | ||||||
|  | } | ||||||
|  | //点击进入对应的聊天 | ||||||
|  | const handleEnterChat = (row) => { | ||||||
|  |   console.log(row) | ||||||
|  | } | ||||||
|  | //处理页数变化 | ||||||
|  | const handleAddressBookPagination = (page) => { | ||||||
|  |   state.addressBookPage = page | ||||||
|  |   getDepPoisUser() | ||||||
|  | } | ||||||
|  | //处理每页条数变化 | ||||||
|  | const handleAddressBookPaginationSize = (pageSize) => { | ||||||
|  |   state.addressBookPageSize = pageSize | ||||||
|  |   state.addressBookPage = 1 | ||||||
|  |   getDepPoisUser() | ||||||
|  | } | ||||||
|  | //处理通讯录搜索 | ||||||
|  | const changeAddressBookSearch = (value) => { | ||||||
|  |   if (!value.nickName?.trim()) { | ||||||
|  |     state.addressBookSearchNickName = '' | ||||||
|  |   } else { | ||||||
|  |     state.addressBookSearchNickName = value.nickName | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | //处理通讯录tab切换 | ||||||
|  | const handleAddressBookTabChange = (value) => { | ||||||
|  |   console.log(value, 'value') | ||||||
|  |   state.addressBookCurrentTab = value | ||||||
|  | } | ||||||
|  | //处理群聊列表搜索 | ||||||
|  | const changeGroupChatListSearch = (value) => { | ||||||
|  |   console.log(value, 'value') | ||||||
|  | } | ||||||
|  | //处理群聊列表页数变化 | ||||||
|  | const handleGroupChatListPagination = (value) => { | ||||||
|  |   console.log(value, 'value') | ||||||
|  |   state.groupChatListPage = value | ||||||
|  | } | ||||||
|  | //处理群聊列表每页条数变化 | ||||||
|  | const handleGroupChatListPaginationSize = (value) => { | ||||||
|  |   console.log(value, 'value') | ||||||
|  |   state.groupChatListPageSize = value | ||||||
|  | } | ||||||
| </script> | </script> | ||||||
| 
 | 
 | ||||||
| <template> | <template> | ||||||
| @ -224,25 +455,30 @@ const getTreeData = () => { | |||||||
|   <section class="el-container is-vertical height100"> |   <section class="el-container is-vertical height100"> | ||||||
|     <!-- 工具栏目 --> |     <!-- 工具栏目 --> | ||||||
|     <header class="el-header header-tools"> |     <header class="el-header header-tools"> | ||||||
|       <n-input |       <n-dropdown | ||||||
|         placeholder="搜索好友 / 群聊" |         trigger="click" | ||||||
|         v-model:value.trim="searchKeyword" |         :options="state.chatSearchOptions" | ||||||
|        |         style="width: 248px; height: 677px; overflow-y: scroll;" | ||||||
|         clearable |  | ||||||
|         style="width: 78%;" |  | ||||||
|       > |       > | ||||||
|         <template #prefix> |         <n-input | ||||||
|           <n-icon :component="Search" /> |           placeholder="搜索好友 / 群聊" | ||||||
|         </template> |           v-model:value.trim="searchKeyword" | ||||||
|       </n-input> |           round | ||||||
| 
 |           clearable | ||||||
|  |           style="width: 78%;" | ||||||
|  |         > | ||||||
|  |           <template #prefix> | ||||||
|  |             <n-icon :component="Search" /> | ||||||
|  |           </template> | ||||||
|  |         </n-input> | ||||||
|  |       </n-dropdown> | ||||||
|       <n-button circle @click="isShowGroup = true"> |       <n-button circle @click="isShowGroup = true"> | ||||||
|         <template #icon> |         <template #icon> | ||||||
|           <n-icon :component="Plus" /> |           <n-icon :component="Plus" /> | ||||||
|         </template> |         </template> | ||||||
|       </n-button> |       </n-button> | ||||||
|       <img |       <img | ||||||
|         style="width: 19px; height: 20px;" |         style="width: 19px; height: 20px; cursor: pointer;" | ||||||
|         src="@/assets/image/chatList/addressBook.png" |         src="@/assets/image/chatList/addressBook.png" | ||||||
|         alt="" |         alt="" | ||||||
|         @click="showAddressBookModal" |         @click="showAddressBookModal" | ||||||
| @ -250,7 +486,7 @@ const getTreeData = () => { | |||||||
|     </header> |     </header> | ||||||
| 
 | 
 | ||||||
|     <!-- 置顶栏目 --> |     <!-- 置顶栏目 --> | ||||||
|     <!-- <header class="el-header header-top" v-show="loadStatus == 3 && topItems.length > 0"> |     <header class="el-header header-top" v-show="loadStatus == 3 && topItems.length > 0"> | ||||||
|       <n-popover v-for="item in topItems" :key="item.index_name" placement="bottom" trigger="hover"> |       <n-popover v-for="item in topItems" :key="item.index_name" placement="bottom" trigger="hover"> | ||||||
|         <template #trigger> |         <template #trigger> | ||||||
|           <div |           <div | ||||||
| @ -273,10 +509,10 @@ const getTreeData = () => { | |||||||
|         </template> |         </template> | ||||||
|         <span> {{ item.remark || item.name }} </span> |         <span> {{ item.remark || item.name }} </span> | ||||||
|       </n-popover> |       </n-popover> | ||||||
|     </header> --> |     </header> | ||||||
| 
 | 
 | ||||||
|     <!-- 标题栏目 --> |     <!-- 标题栏目 --> | ||||||
|     <!-- <header |     <header | ||||||
|       v-show="loadStatus == 3 && talkStore.talkItems.length > 0" |       v-show="loadStatus == 3 && talkStore.talkItems.length > 0" | ||||||
|       class="el-header header-badge" |       class="el-header header-badge" | ||||||
|       :class="{ shadow: false }" |       :class="{ shadow: false }" | ||||||
| @ -285,12 +521,11 @@ const getTreeData = () => { | |||||||
|       <p> |       <p> | ||||||
|         <span class="badge unread" v-show="unreadNum">{{ unreadNum }}未读</span> |         <span class="badge unread" v-show="unreadNum">{{ unreadNum }}未读</span> | ||||||
|       </p> |       </p> | ||||||
|     </header> --> |     </header> | ||||||
| 
 | 
 | ||||||
|     <main id="talk-session-list" class="el-main me-scrollbar me-scrollbar-thumb"> |     <main id="talk-session-list" class="el-main me-scrollbar me-scrollbar-thumb"> | ||||||
|       <template v-if="loadStatus == 2"><Skeleton /></template> |       <template v-if="loadStatus == 2"><Skeleton /></template> | ||||||
|       <template v-else> |       <template v-else> | ||||||
| 
 |  | ||||||
|         <TalkItem |         <TalkItem | ||||||
|           v-for="item in items" |           v-for="item in items" | ||||||
|           :key="item.index_name" |           :key="item.index_name" | ||||||
| @ -317,14 +552,34 @@ const getTreeData = () => { | |||||||
|   > |   > | ||||||
|     <template #content> |     <template #content> | ||||||
|       <div class="custom-modal-content"> |       <div class="custom-modal-content"> | ||||||
|         <n-card> |         <n-card style="padding: 0 12px;"> | ||||||
|           <n-tabs type="line"> |           <n-tabs | ||||||
|  |             type="line" | ||||||
|  |             @update:value="handleAddressBookTabChange" | ||||||
|  |             tab-style="font-size: 16px; font-weight: 600;color: #8B8B8B;" | ||||||
|  |           > | ||||||
|             <n-tab name="employeeAddressBook">员工通讯录</n-tab> |             <n-tab name="employeeAddressBook">员工通讯录</n-tab> | ||||||
|             <n-tab name="groupChatList">群聊列表</n-tab> |             <n-tab name="groupChatList">群聊列表</n-tab> | ||||||
|           </n-tabs> |           </n-tabs> | ||||||
|           <xSearchForm :search-config="state.searchConfig"></xSearchForm> |           <xSearchForm | ||||||
|           <div class="addressBook-content"> |             v-if="state.addressBookCurrentTab == 'employeeAddressBook'" | ||||||
|             <div class="addressBook-tree"> |             :search-config="state.addressBookSearchConfig" | ||||||
|  |             customInputPlaceholder="请输入姓名" | ||||||
|  |             @change="changeAddressBookSearch" | ||||||
|  |             :cols="3" | ||||||
|  |           ></xSearchForm> | ||||||
|  |           <xSearchForm | ||||||
|  |             v-if="state.addressBookCurrentTab == 'groupChatList'" | ||||||
|  |             :search-config="state.groupChatListSearchConfig" | ||||||
|  |             customInputPlaceholder="请输入群聊名称" | ||||||
|  |             @change="changeGroupChatListSearch" | ||||||
|  |             :cols="3" | ||||||
|  |           ></xSearchForm> | ||||||
|  |           <div | ||||||
|  |             class="addressBook-content" | ||||||
|  |             v-if="state.addressBookCurrentTab == 'employeeAddressBook'" | ||||||
|  |           > | ||||||
|  |             <div class="addressBook-tree" v-if="!state.addressBookSearchNickName"> | ||||||
|               <fl-tree |               <fl-tree | ||||||
|                 :data="state.treeData" |                 :data="state.treeData" | ||||||
|                 :expandedKeys="state.expandedKeys" |                 :expandedKeys="state.expandedKeys" | ||||||
| @ -333,6 +588,58 @@ const getTreeData = () => { | |||||||
|                 @triggerTreeClick="handleTreeClick" |                 @triggerTreeClick="handleTreeClick" | ||||||
|               ></fl-tree> |               ></fl-tree> | ||||||
|             </div> |             </div> | ||||||
|  |             <div class="addressBook-table"> | ||||||
|  |               <xNDataTable | ||||||
|  |                 :columns="state.addressBookColumns" | ||||||
|  |                 :data="state.addressBookData" | ||||||
|  |                 :style="{ | ||||||
|  |                   height: `${state.addressBookTableHeight}px`, | ||||||
|  |                   width: `${state.addressBookTableWidth}px` | ||||||
|  |                 }" | ||||||
|  |                 flex-height | ||||||
|  |               ></xNDataTable> | ||||||
|  |               <div class="addressBook-pagination"> | ||||||
|  |                 <n-pagination | ||||||
|  |                   v-model:page="state.addressBookPage" | ||||||
|  |                   v-model:page-size="state.addressBookPageSize" | ||||||
|  |                   :item-count="state.addressBookTotal" | ||||||
|  |                   show-quick-jumper | ||||||
|  |                   show-size-picker | ||||||
|  |                   :page-sizes="[10, 20, 50]" | ||||||
|  |                   :on-update:page="handleAddressBookPagination" | ||||||
|  |                   :on-update:page-size="handleAddressBookPaginationSize" | ||||||
|  |                 > | ||||||
|  |                   <template #prefix="{ itemCount }"> 共 {{ itemCount }} 条记录 </template> | ||||||
|  |                 </n-pagination> | ||||||
|  |               </div> | ||||||
|  |             </div> | ||||||
|  |           </div> | ||||||
|  |           <div class="groupChatList-content" v-if="state.addressBookCurrentTab == 'groupChatList'"> | ||||||
|  |             <div class="groupChatList-table"> | ||||||
|  |               <xNDataTable | ||||||
|  |                 :columns="state.groupChatListColumns" | ||||||
|  |                 :data="state.groupChatListData" | ||||||
|  |                 :style="{ | ||||||
|  |                   height: '523px', | ||||||
|  |                   width: '1148px' | ||||||
|  |                 }" | ||||||
|  |                 flex-height | ||||||
|  |               ></xNDataTable> | ||||||
|  |               <div class="groupChatList-pagination"> | ||||||
|  |                 <n-pagination | ||||||
|  |                   v-model:page="state.groupChatListPage" | ||||||
|  |                   v-model:page-size="state.groupChatListPageSize" | ||||||
|  |                   :item-count="state.groupChatListTotal" | ||||||
|  |                   show-quick-jumper | ||||||
|  |                   show-size-picker | ||||||
|  |                   :page-sizes="[10, 20, 50]" | ||||||
|  |                   :on-update:page="handleGroupChatListPagination" | ||||||
|  |                   :on-update:page-size="handleGroupChatListPaginationSize" | ||||||
|  |                 > | ||||||
|  |                   <template #prefix="{ itemCount }"> 共 {{ itemCount }} 条记录 </template> | ||||||
|  |                 </n-pagination> | ||||||
|  |               </div> | ||||||
|  |             </div> | ||||||
|           </div> |           </div> | ||||||
|         </n-card> |         </n-card> | ||||||
|       </div> |       </div> | ||||||
| @ -446,7 +753,13 @@ html[theme-mode='dark'] { | |||||||
|   box-sizing: border-box; |   box-sizing: border-box; | ||||||
|   width: 100%; |   width: 100%; | ||||||
|   padding: 0 12px; |   padding: 0 12px; | ||||||
|  |   :deep(.n-tabs-tab--active) { | ||||||
|  |     color: #46299d !important; | ||||||
|  |   } | ||||||
|   .addressBook-content { |   .addressBook-content { | ||||||
|  |     display: flex; | ||||||
|  |     flex-direction: row; | ||||||
|  |     gap: 20px; | ||||||
|     .addressBook-tree { |     .addressBook-tree { | ||||||
|       width: 328px; |       width: 328px; | ||||||
|       height: 524px; |       height: 524px; | ||||||
| @ -456,6 +769,37 @@ html[theme-mode='dark'] { | |||||||
|       padding: 12px 20px; |       padding: 12px 20px; | ||||||
|       box-sizing: border-box; |       box-sizing: border-box; | ||||||
|     } |     } | ||||||
|  |     .addressBook-table { | ||||||
|  |       :deep(.n-data-table-th) { | ||||||
|  |         background-color: #46299d; | ||||||
|  |         color: #fff; | ||||||
|  |       } | ||||||
|  |       .addressBook-pagination { | ||||||
|  |         display: flex; | ||||||
|  |         justify-content: flex-end; | ||||||
|  |         align-items: center; | ||||||
|  |         padding: 22px 0 0; | ||||||
|  |         box-sizing: border-box; | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |   .groupChatList-content { | ||||||
|  |     display: flex; | ||||||
|  |     flex-direction: row; | ||||||
|  |     gap: 20px; | ||||||
|  |     .groupChatList-table { | ||||||
|  |       :deep(.n-data-table-th) { | ||||||
|  |         background-color: #46299d; | ||||||
|  |         color: #fff; | ||||||
|  |       } | ||||||
|  |       .groupChatList-pagination { | ||||||
|  |         display: flex; | ||||||
|  |         justify-content: flex-end; | ||||||
|  |         align-items: center; | ||||||
|  |         padding: 22px 0 0; | ||||||
|  |         box-sizing: border-box; | ||||||
|  |       } | ||||||
|  |     } | ||||||
|   } |   } | ||||||
| } | } | ||||||
| </style> | </style> | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user