Compare commits
6 Commits
478336c2fe
...
b04d25a243
Author | SHA1 | Date | |
---|---|---|---|
|
b04d25a243 | ||
|
9e31271cc3 | ||
|
6d08dbe42f | ||
701d878f7d | |||
7544b3d324 | |||
8c9f634d0b |
@ -32,6 +32,8 @@
|
||||
"quill": "^1.3.7",
|
||||
"quill-image-uploader": "^1.3.0",
|
||||
"quill-mention": "^4.1.0",
|
||||
"sortablejs": "^1.15.6",
|
||||
"viewerjs": "^1.11.7",
|
||||
"vue": "^3.3.11",
|
||||
"vue-cropper": "^1.1.1",
|
||||
"vue-router": "^4.2.5",
|
||||
|
@ -62,6 +62,12 @@ importers:
|
||||
quill-mention:
|
||||
specifier: ^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:
|
||||
specifier: ^3.3.11
|
||||
version: 3.5.13(typescript@5.2.2)
|
||||
@ -2927,6 +2933,9 @@ packages:
|
||||
sortablejs@1.14.0:
|
||||
resolution: {integrity: sha512-pBXvQCs5/33fdN1/39pPL0NZF20LeRbLQ5jtnheIPN9JQAaufGjKdWduZn4U7wCtVuzKhmRkI0DFYHYRbB2H1w==}
|
||||
|
||||
sortablejs@1.15.6:
|
||||
resolution: {integrity: sha512-aNfiuwMEpfBM/CN6LY0ibyhxPfPbyFeBTYJKCvzkJ2GkUpazIt3H+QIPAMHwqQ7tMKaHz1Qj+rJJCqljnf4p3A==}
|
||||
|
||||
source-map-js@1.2.1:
|
||||
resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
@ -3211,6 +3220,9 @@ packages:
|
||||
peerDependencies:
|
||||
vue: ^3.0.11
|
||||
|
||||
viewerjs@1.11.7:
|
||||
resolution: {integrity: sha512-0JuVqOmL5v1jmEAlG5EBDR3XquxY8DWFQbFMprOXgaBB0F7Q/X9xWdEaQc59D8xzwkdUgXEMSSknTpriq95igg==}
|
||||
|
||||
vite-hot-client@2.0.4:
|
||||
resolution: {integrity: sha512-W9LOGAyGMrbGArYJN4LBCdOC5+Zwh7dHvOHC0KmGKkJhsOzaKbpo/jEjpPKVHIW0/jBWj8RZG0NUxfgA8BxgAg==}
|
||||
peerDependencies:
|
||||
@ -6520,6 +6532,8 @@ snapshots:
|
||||
|
||||
sortablejs@1.14.0: {}
|
||||
|
||||
sortablejs@1.15.6: {}
|
||||
|
||||
source-map-js@1.2.1: {}
|
||||
|
||||
source-map-resolve@0.5.3:
|
||||
@ -6808,6 +6822,8 @@ snapshots:
|
||||
evtd: 0.2.4
|
||||
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)):
|
||||
dependencies:
|
||||
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>
|
||||
</template>
|
||||
<slot name="content"></slot>
|
||||
<template #footer>
|
||||
<template #footer v-if="actionBtns.cancelBtn || actionBtns.confirmBtn">
|
||||
<div class="custom-modal-btns">
|
||||
<customBtn
|
||||
color="#C7C7C9"
|
||||
|
@ -2,14 +2,14 @@
|
||||
<div class="row items-center">
|
||||
<div v-if="state.treeData.edit">
|
||||
<n-input v-model:value="state.editTitle"
|
||||
style="width:120px" />
|
||||
style="max-width:200px" />
|
||||
</div>
|
||||
|
||||
<n-popover trigger="hover"
|
||||
v-else>
|
||||
<template #trigger>
|
||||
<div style="width:120px"
|
||||
class="fl-px-sm sf-text-ellipsis">{{ state.treeData.title }}</div>
|
||||
<div style="max-width:200px"
|
||||
class="fl-px-sm sf-text-ellipsis">{{ state.treeData.title + '(' + state.treeData.staffNum + ')' }}</div>
|
||||
</template>
|
||||
<div>{{ state.treeData.title }}</div>
|
||||
</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";
|
||||
import Sortable from "sortablejs";
|
||||
import { debounce } from "lodash-es";
|
||||
import { NDataTable } from "naive-ui";
|
||||
|
||||
// Props 定义
|
||||
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: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
customInputPlaceholder: {
|
||||
type: String,
|
||||
default: '请输入',
|
||||
}
|
||||
})
|
||||
|
||||
@ -205,7 +209,7 @@ initData()
|
||||
<template v-if="item.type === 'input'">
|
||||
<n-input
|
||||
:value="formData[item.key]"
|
||||
placeholder="请输入"
|
||||
:placeholder="customInputPlaceholder"
|
||||
@input="value => handleInputChange(value, item)"
|
||||
clearable
|
||||
v-bind="item.props"
|
||||
|
@ -18,7 +18,7 @@ export function isLoggedIn() {
|
||||
*/
|
||||
export function getAccessToken() {
|
||||
// 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>
|
||||
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 { 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 TalkItem from './TalkItem.vue'
|
||||
import Skeleton from './Skeleton.vue'
|
||||
@ -13,11 +32,13 @@ import { ISession } from '@/types/chat'
|
||||
import { useSessionMenu } from '@/hooks'
|
||||
import customModal from '@/components/common/customModal.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 { processError, processSuccess } from '@/utils/helper/message.js'
|
||||
import chatAppSearchList from '@/components/search/searchList.vue'
|
||||
|
||||
const currentInstance = getCurrentInstance()
|
||||
const { $request } = currentInstance?.appContext.config.globalProperties
|
||||
const $request = currentInstance?.appContext.config.globalProperties?.$request
|
||||
|
||||
const {
|
||||
dropdown,
|
||||
@ -34,6 +55,48 @@ const searchKeyword = ref('')
|
||||
const topItems = computed((): ISession[] => talkStore.topItems)
|
||||
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({
|
||||
isShowAddressBookModal: false, // 是否显示通讯录模态框
|
||||
customModalStyle: {
|
||||
@ -41,20 +104,128 @@ const state = reactive({
|
||||
height: '846px',
|
||||
backgroundColor: '#F9F9FD'
|
||||
}, //自定义模态框样式
|
||||
searchConfig: [
|
||||
addressBookSearchConfig: [
|
||||
{
|
||||
label: '姓名',
|
||||
key: 'name',
|
||||
key: 'nickName',
|
||||
type: 'input',
|
||||
valueType: 'string'
|
||||
}
|
||||
], // 搜索配置
|
||||
|
||||
], // 通讯录搜索配置
|
||||
groupChatListSearchConfig: [
|
||||
{
|
||||
label: '群聊名称',
|
||||
key: 'groupName',
|
||||
type: 'input',
|
||||
valueType: 'string'
|
||||
}
|
||||
], // 群聊列表搜索配置
|
||||
treeData: [],
|
||||
expandedKeys: [],
|
||||
clickKey: '',
|
||||
clickKey: 3,
|
||||
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[] => {
|
||||
@ -68,13 +239,31 @@ const items = computed((): ISession[] => {
|
||||
return keyword.toLowerCase().indexOf(searchKeyword.value.toLowerCase()) != -1
|
||||
})
|
||||
})
|
||||
setTimeout(()=>{
|
||||
console.log('items.value',items.value)
|
||||
},1000)
|
||||
watch(() => talkStore, (newValue, oldValue) => {
|
||||
console.log(newValue);
|
||||
|
||||
},{deep:true,immediate:true})
|
||||
setTimeout(() => {
|
||||
console.log('items.value', items.value)
|
||||
}, 1000)
|
||||
watch(
|
||||
() => talkStore,
|
||||
(newValue, oldValue) => {
|
||||
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)
|
||||
@ -145,8 +334,11 @@ const showAddressBookModal = () => {
|
||||
state.isShowAddressBookModal = true
|
||||
}
|
||||
const handleTreeClick = ({ selectedKey, tree }) => {
|
||||
// console.log(tree)
|
||||
state.clickKey = tree.key
|
||||
state.treeSelectData = tree
|
||||
state.addressBookPage = 1
|
||||
getDepPoisUser()
|
||||
}
|
||||
const calcTreeData = (data) => {
|
||||
for (let item of data) {
|
||||
@ -172,29 +364,8 @@ const getTreeData = () => {
|
||||
let data = res.data.nodes
|
||||
calcTreeData(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.tableConfig.refreshCount++;
|
||||
getDepPoisUser()
|
||||
} else {
|
||||
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>
|
||||
|
||||
<template>
|
||||
@ -224,10 +455,15 @@ const getTreeData = () => {
|
||||
<section class="el-container is-vertical height100">
|
||||
<!-- 工具栏目 -->
|
||||
<header class="el-header header-tools">
|
||||
<n-dropdown
|
||||
trigger="click"
|
||||
:options="state.chatSearchOptions"
|
||||
style="width: 248px; height: 677px; overflow-y: scroll;"
|
||||
>
|
||||
<n-input
|
||||
placeholder="搜索好友 / 群聊"
|
||||
v-model:value.trim="searchKeyword"
|
||||
|
||||
round
|
||||
clearable
|
||||
style="width: 78%;"
|
||||
>
|
||||
@ -235,14 +471,14 @@ const getTreeData = () => {
|
||||
<n-icon :component="Search" />
|
||||
</template>
|
||||
</n-input>
|
||||
|
||||
</n-dropdown>
|
||||
<n-button circle @click="isShowGroup = true">
|
||||
<template #icon>
|
||||
<n-icon :component="Plus" />
|
||||
</template>
|
||||
</n-button>
|
||||
<img
|
||||
style="width: 19px; height: 20px;"
|
||||
style="width: 19px; height: 20px; cursor: pointer;"
|
||||
src="@/assets/image/chatList/addressBook.png"
|
||||
alt=""
|
||||
@click="showAddressBookModal"
|
||||
@ -250,7 +486,7 @@ const getTreeData = () => {
|
||||
</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">
|
||||
<template #trigger>
|
||||
<div
|
||||
@ -273,10 +509,10 @@ const getTreeData = () => {
|
||||
</template>
|
||||
<span> {{ item.remark || item.name }} </span>
|
||||
</n-popover>
|
||||
</header> -->
|
||||
</header>
|
||||
|
||||
<!-- 标题栏目 -->
|
||||
<!-- <header
|
||||
<header
|
||||
v-show="loadStatus == 3 && talkStore.talkItems.length > 0"
|
||||
class="el-header header-badge"
|
||||
:class="{ shadow: false }"
|
||||
@ -285,12 +521,11 @@ const getTreeData = () => {
|
||||
<p>
|
||||
<span class="badge unread" v-show="unreadNum">{{ unreadNum }}未读</span>
|
||||
</p>
|
||||
</header> -->
|
||||
</header>
|
||||
|
||||
<main id="talk-session-list" class="el-main me-scrollbar me-scrollbar-thumb">
|
||||
<template v-if="loadStatus == 2"><Skeleton /></template>
|
||||
<template v-else>
|
||||
|
||||
<TalkItem
|
||||
v-for="item in items"
|
||||
:key="item.index_name"
|
||||
@ -317,14 +552,34 @@ const getTreeData = () => {
|
||||
>
|
||||
<template #content>
|
||||
<div class="custom-modal-content">
|
||||
<n-card>
|
||||
<n-tabs type="line">
|
||||
<n-card style="padding: 0 12px;">
|
||||
<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="groupChatList">群聊列表</n-tab>
|
||||
</n-tabs>
|
||||
<xSearchForm :search-config="state.searchConfig"></xSearchForm>
|
||||
<div class="addressBook-content">
|
||||
<div class="addressBook-tree">
|
||||
<xSearchForm
|
||||
v-if="state.addressBookCurrentTab == 'employeeAddressBook'"
|
||||
: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
|
||||
:data="state.treeData"
|
||||
:expandedKeys="state.expandedKeys"
|
||||
@ -333,6 +588,58 @@ const getTreeData = () => {
|
||||
@triggerTreeClick="handleTreeClick"
|
||||
></fl-tree>
|
||||
</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>
|
||||
</n-card>
|
||||
</div>
|
||||
@ -446,7 +753,13 @@ html[theme-mode='dark'] {
|
||||
box-sizing: border-box;
|
||||
width: 100%;
|
||||
padding: 0 12px;
|
||||
:deep(.n-tabs-tab--active) {
|
||||
color: #46299d !important;
|
||||
}
|
||||
.addressBook-content {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
gap: 20px;
|
||||
.addressBook-tree {
|
||||
width: 328px;
|
||||
height: 524px;
|
||||
@ -456,6 +769,37 @@ html[theme-mode='dark'] {
|
||||
padding: 12px 20px;
|
||||
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>
|
||||
|
Loading…
Reference in New Issue
Block a user