搜索页面新增搜索内容高亮显示,并处理返回数据格式,拼接展示
This commit is contained in:
parent
ad658710f7
commit
4f43f1a001
@ -39,7 +39,7 @@
|
||||
"quill-mention": "^6.0.2",
|
||||
"vconsole": "^3.15.1",
|
||||
"vue": "^3.3.8",
|
||||
"vue-i18n": "^9.6.5"
|
||||
"vue-i18n": "11.0.0-rc.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@dcloudio/types": "^3.4.7",
|
||||
|
@ -93,8 +93,8 @@ importers:
|
||||
specifier: ^3.3.8
|
||||
version: 3.4.35(typescript@5.5.4)
|
||||
vue-i18n:
|
||||
specifier: ^9.6.5
|
||||
version: 9.13.1(vue@3.4.35(typescript@5.5.4))
|
||||
specifier: 11.0.0-rc.1
|
||||
version: 11.0.0-rc.1(vue@3.4.35(typescript@5.5.4))
|
||||
devDependencies:
|
||||
'@dcloudio/types':
|
||||
specifier: ^3.4.7
|
||||
@ -1391,26 +1391,26 @@ packages:
|
||||
'@iconify/utils@2.1.32':
|
||||
resolution: {integrity: sha512-LeifFZPPKu28O3AEDpYJNdEbvS4/ojAPyIW+pF/vUpJTYnbTiXUHkCh0bwgFRzKvdpb8H4Fbfd/742++MF4fPQ==}
|
||||
|
||||
'@intlify/core-base@11.0.0-rc.1':
|
||||
resolution: {integrity: sha512-fnfZoa9pb1dKM3L1UkDUGLLrPFQ2BK98x4/fMqwS/fktUor34vQR/itPtfv652ZTplenXXLCEYjUYTGfDZgMTQ==}
|
||||
engines: {node: '>= 16'}
|
||||
|
||||
'@intlify/core-base@9.1.9':
|
||||
resolution: {integrity: sha512-x5T0p/Ja0S8hs5xs+ImKyYckVkL4CzcEXykVYYV6rcbXxJTe2o58IquSqX9bdncVKbRZP7GlBU1EcRaQEEJ+vw==}
|
||||
engines: {node: '>= 10'}
|
||||
|
||||
'@intlify/core-base@9.13.1':
|
||||
resolution: {integrity: sha512-+bcQRkJO9pcX8d0gel9ZNfrzU22sZFSA0WVhfXrf5jdJOS24a+Bp8pozuS9sBI9Hk/tGz83pgKfmqcn/Ci7/8w==}
|
||||
engines: {node: '>= 16'}
|
||||
|
||||
'@intlify/devtools-if@9.1.9':
|
||||
resolution: {integrity: sha512-oKSMKjttG3Ut/1UGEZjSdghuP3fwA15zpDPcjkf/1FjlOIm6uIBGMNS5jXzsZy593u+P/YcnrZD6cD3IVFz9vQ==}
|
||||
engines: {node: '>= 10'}
|
||||
|
||||
'@intlify/message-compiler@11.0.0-rc.1':
|
||||
resolution: {integrity: sha512-TGw2uBfuTFTegZf/BHtUQBEKxl7Q/dVGLoqRIdw8lFsp9g/53sYn5iD+0HxIzdYjbWL6BTJMXCPUHp9PxDTRPw==}
|
||||
engines: {node: '>= 16'}
|
||||
|
||||
'@intlify/message-compiler@9.1.9':
|
||||
resolution: {integrity: sha512-6YgCMF46Xd0IH2hMRLCssZI3gFG4aywidoWQ3QP4RGYQXQYYfFC54DxhSgfIPpVoPLQ+4AD29eoYmhiHZ+qLFQ==}
|
||||
engines: {node: '>= 10'}
|
||||
|
||||
'@intlify/message-compiler@9.13.1':
|
||||
resolution: {integrity: sha512-SKsVa4ajYGBVm7sHMXd5qX70O2XXjm55zdZB3VeMFCvQyvLew/dLvq3MqnaIsTMF1VkkOb9Ttr6tHcMlyPDL9w==}
|
||||
engines: {node: '>= 16'}
|
||||
|
||||
'@intlify/message-resolver@9.1.9':
|
||||
resolution: {integrity: sha512-Lx/DBpigeK0sz2BBbzv5mu9/dAlt98HxwbG7xLawC3O2xMF9MNWU5FtOziwYG6TDIjNq0O/3ZbOJAxwITIWXEA==}
|
||||
engines: {node: '>= 10'}
|
||||
@ -1419,14 +1419,14 @@ packages:
|
||||
resolution: {integrity: sha512-XgPw8+UlHCiie3fI41HPVa/VDJb3/aSH7bLhY1hJvlvNV713PFtb4p4Jo+rlE0gAoMsMCGcsiT982fImolSltg==}
|
||||
engines: {node: '>= 10'}
|
||||
|
||||
'@intlify/shared@11.0.0-rc.1':
|
||||
resolution: {integrity: sha512-8tR1xe7ZEbkabTuE/tNhzpolygUn9OaYp9yuYAF4MgDNZg06C3Qny80bes2/e9/Wm3aVkPUlCw6WgU7mQd0yEg==}
|
||||
engines: {node: '>= 16'}
|
||||
|
||||
'@intlify/shared@9.1.9':
|
||||
resolution: {integrity: sha512-xKGM1d0EAxdDFCWedcYXOm6V5Pfw/TMudd6/qCdEb4tv0hk9EKeg7lwQF1azE0dP2phvx0yXxrt7UQK+IZjNdw==}
|
||||
engines: {node: '>= 10'}
|
||||
|
||||
'@intlify/shared@9.13.1':
|
||||
resolution: {integrity: sha512-u3b6BKGhE6j/JeRU6C/RL2FgyJfy6LakbtfeVF8fJXURpZZTzfh3e05J0bu0XPw447Q6/WUp3C4ajv4TMS4YsQ==}
|
||||
engines: {node: '>= 16'}
|
||||
|
||||
'@intlify/vue-devtools@9.1.9':
|
||||
resolution: {integrity: sha512-YPehH9uL4vZcGXky4Ev5qQIITnHKIvsD2GKGXgqf+05osMUI6WSEQHaN9USRa318Rs8RyyPCiDfmA0hRu3k7og==}
|
||||
engines: {node: '>= 10'}
|
||||
@ -4819,8 +4819,8 @@ packages:
|
||||
'@vue/composition-api':
|
||||
optional: true
|
||||
|
||||
vue-i18n@9.13.1:
|
||||
resolution: {integrity: sha512-mh0GIxx0wPtPlcB1q4k277y0iKgo25xmDPWioVVYanjPufDBpvu5ySTjP5wOrSvlYQ2m1xI+CFhGdauv/61uQg==}
|
||||
vue-i18n@11.0.0-rc.1:
|
||||
resolution: {integrity: sha512-qbdCbA537HEdr2yXQ4ec/OMDsoHjod1DwnWbrf+l4Cu/O7CYTCKsOyITUm3RmrCJgRnoVycuR6i/JWdNTJvD5g==}
|
||||
engines: {node: '>= 16'}
|
||||
peerDependencies:
|
||||
vue: ^3.0.0
|
||||
@ -6730,6 +6730,11 @@ snapshots:
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@intlify/core-base@11.0.0-rc.1':
|
||||
dependencies:
|
||||
'@intlify/message-compiler': 11.0.0-rc.1
|
||||
'@intlify/shared': 11.0.0-rc.1
|
||||
|
||||
'@intlify/core-base@9.1.9':
|
||||
dependencies:
|
||||
'@intlify/devtools-if': 9.1.9
|
||||
@ -6739,26 +6744,21 @@ snapshots:
|
||||
'@intlify/shared': 9.1.9
|
||||
'@intlify/vue-devtools': 9.1.9
|
||||
|
||||
'@intlify/core-base@9.13.1':
|
||||
dependencies:
|
||||
'@intlify/message-compiler': 9.13.1
|
||||
'@intlify/shared': 9.13.1
|
||||
|
||||
'@intlify/devtools-if@9.1.9':
|
||||
dependencies:
|
||||
'@intlify/shared': 9.1.9
|
||||
|
||||
'@intlify/message-compiler@11.0.0-rc.1':
|
||||
dependencies:
|
||||
'@intlify/shared': 11.0.0-rc.1
|
||||
source-map-js: 1.2.0
|
||||
|
||||
'@intlify/message-compiler@9.1.9':
|
||||
dependencies:
|
||||
'@intlify/message-resolver': 9.1.9
|
||||
'@intlify/shared': 9.1.9
|
||||
source-map: 0.6.1
|
||||
|
||||
'@intlify/message-compiler@9.13.1':
|
||||
dependencies:
|
||||
'@intlify/shared': 9.13.1
|
||||
source-map-js: 1.2.0
|
||||
|
||||
'@intlify/message-resolver@9.1.9': {}
|
||||
|
||||
'@intlify/runtime@9.1.9':
|
||||
@ -6767,9 +6767,9 @@ snapshots:
|
||||
'@intlify/message-resolver': 9.1.9
|
||||
'@intlify/shared': 9.1.9
|
||||
|
||||
'@intlify/shared@9.1.9': {}
|
||||
'@intlify/shared@11.0.0-rc.1': {}
|
||||
|
||||
'@intlify/shared@9.13.1': {}
|
||||
'@intlify/shared@9.1.9': {}
|
||||
|
||||
'@intlify/vue-devtools@9.1.9':
|
||||
dependencies:
|
||||
@ -10890,10 +10890,10 @@ snapshots:
|
||||
dependencies:
|
||||
vue: 3.4.35(typescript@5.5.4)
|
||||
|
||||
vue-i18n@9.13.1(vue@3.4.35(typescript@5.5.4)):
|
||||
vue-i18n@11.0.0-rc.1(vue@3.4.35(typescript@5.5.4)):
|
||||
dependencies:
|
||||
'@intlify/core-base': 9.13.1
|
||||
'@intlify/shared': 9.13.1
|
||||
'@intlify/core-base': 11.0.0-rc.1
|
||||
'@intlify/shared': 11.0.0-rc.1
|
||||
'@vue/devtools-api': 6.6.3
|
||||
vue: 3.4.35(typescript@5.5.4)
|
||||
|
||||
|
70
src/pages/search/components/highLightText.vue
Normal file
70
src/pages/search/components/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>
|
@ -5,17 +5,21 @@
|
||||
</div>
|
||||
<div class="result-info">
|
||||
<div class="info-name">
|
||||
<span class="text-[32rpx] font-medium">
|
||||
{{ resultName }}
|
||||
</span>
|
||||
<HighlightText
|
||||
class="text-[32rpx] font-medium"
|
||||
:text="resultName"
|
||||
:searchText="props.searchText"
|
||||
/>
|
||||
<div class="info-tag" v-if="resultType">
|
||||
<span class="text-[24rpx] font-medium">{{ resultType }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="info-detail" v-if="resultDetail">
|
||||
<span class="text-[28rpx] font-regular">
|
||||
{{ resultDetail }}
|
||||
</span>
|
||||
<HighlightText
|
||||
class="text-[28rpx] font-regular"
|
||||
:text="resultDetail"
|
||||
:searchText="props.searchText"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -24,109 +28,108 @@
|
||||
import zu4992 from '@/static/image/chatList/zu4992@2x.png'
|
||||
import zu4991 from '@/static/image/chatList/zu4991@2x.png'
|
||||
import zu4989 from '@/static/image/chatList/zu4989@2x.png'
|
||||
import {
|
||||
ref,
|
||||
watch,
|
||||
computed,
|
||||
onMounted,
|
||||
onUnmounted,
|
||||
reactive,
|
||||
getCurrentInstance,
|
||||
} from 'vue'
|
||||
const { proxy } = getCurrentInstance()
|
||||
import zu5296 from '@/static/image/chatList/zu5296@2x.png'
|
||||
import { ref, watch, computed, onMounted, onUnmounted, reactive } from 'vue'
|
||||
import HighlightText from './highLightText.vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
const { t } = useI18n()
|
||||
const props = defineProps({
|
||||
searchItem: Object | Number,
|
||||
searchResultKey: String,
|
||||
searchText: String,
|
||||
})
|
||||
// 映射表-查找对应结构下的属性名
|
||||
const keyMapping = {
|
||||
user_infos: { avatar: 'avatar', name: 'nickname' },
|
||||
group_infos: { avatar: 'avatar', name: 'name' },
|
||||
group_member_infos: {
|
||||
avatar: 'group_avatar',
|
||||
name: 'group_name',
|
||||
detailKey: 'user_name',
|
||||
},
|
||||
general_infos: {
|
||||
avatar: 'receiver_avatar',
|
||||
name: 'receiver_name',
|
||||
detailKey: 'count',
|
||||
},
|
||||
}
|
||||
//获取key对应值
|
||||
const getKeyValue = (keys) => {
|
||||
let keyValue = ''
|
||||
if (keys) {
|
||||
keyValue = props.searchItem[keys]
|
||||
}
|
||||
return keyValue
|
||||
}
|
||||
//头像
|
||||
const avatarImg = computed(() => {
|
||||
let srcT = ''
|
||||
switch (props.searchItem?.group_type) {
|
||||
case 0:
|
||||
srcT = zu5296
|
||||
break
|
||||
case 1:
|
||||
srcT = zu4992
|
||||
break
|
||||
case 2:
|
||||
srcT = zu4991
|
||||
break
|
||||
case 3:
|
||||
srcT = zu4989
|
||||
break
|
||||
default:
|
||||
srcT = zu4992
|
||||
let avatar = getKeyValue(keyMapping[props.searchResultKey]?.avatar)
|
||||
if (!avatar) {
|
||||
avatar = groupTypeMapping[props.searchItem?.group_type]?.defaultImg
|
||||
}
|
||||
return srcT
|
||||
return avatar
|
||||
})
|
||||
//名称
|
||||
const resultName = computed(() => {
|
||||
let data_key = ''
|
||||
switch (props.searchResultKey) {
|
||||
case 'user_infos':
|
||||
data_key = props.searchItem?.nickname
|
||||
break
|
||||
case 'group_member_infos':
|
||||
data_key = props.searchItem?.group_name
|
||||
break
|
||||
case 'general_infos':
|
||||
data_key = props.searchItem?.receiver_name
|
||||
break
|
||||
default:
|
||||
data_key = ''
|
||||
}
|
||||
return data_key
|
||||
return getKeyValue(keyMapping[props.searchResultKey]?.name)
|
||||
})
|
||||
// 映射表-根据groupType设置对应值
|
||||
const groupTypeMapping = {
|
||||
0: {},
|
||||
1: {
|
||||
defaultImg: zu4992,
|
||||
},
|
||||
2: {
|
||||
result_type: t('index.mine.department'),
|
||||
defaultImg: zu4989,
|
||||
},
|
||||
3: {
|
||||
result_type: t('index.mine.project'),
|
||||
defaultImg: zu4991,
|
||||
},
|
||||
4: {
|
||||
defaultImg: zu5296,
|
||||
},
|
||||
}
|
||||
//群类型tag
|
||||
const resultType = computed(() => {
|
||||
let result_type = ''
|
||||
switch (props.searchItem?.group_type) {
|
||||
case 0:
|
||||
result_type = ''
|
||||
break
|
||||
case 1:
|
||||
result_type = ''
|
||||
break
|
||||
case 2:
|
||||
result_type = proxy.$t('index.mine.department')
|
||||
break
|
||||
case 3:
|
||||
result_type = proxy.$t('index.mine.project')
|
||||
break
|
||||
case 4:
|
||||
result_type = ''
|
||||
break
|
||||
default:
|
||||
result_type = ''
|
||||
}
|
||||
return result_type
|
||||
return groupTypeMapping[props.searchItem?.group_type]?.result_type
|
||||
})
|
||||
//详细内容
|
||||
const resultDetail = computed(() => {
|
||||
let data_key = ''
|
||||
switch (props.searchResultKey) {
|
||||
case 'user_infos':
|
||||
data_key = ''
|
||||
let result_detail =
|
||||
props.searchItem[keyMapping[props.searchResultKey]?.detailKey]
|
||||
switch (keyMapping[props.searchResultKey]?.detailKey) {
|
||||
case 'count':
|
||||
result_detail = result_detail + t('search.chat.count')
|
||||
break
|
||||
case 'group_member_infos':
|
||||
data_key = ''
|
||||
break
|
||||
case 'general_infos':
|
||||
data_key = props.searchItem?.count + proxy.$t('search.chat.count')
|
||||
case 'user_name':
|
||||
result_detail = t('search.result.include') + result_detail
|
||||
break
|
||||
default:
|
||||
data_key = ''
|
||||
result_detail = ''
|
||||
}
|
||||
return data_key
|
||||
return result_detail
|
||||
})
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.search-item:nth-child(1) {
|
||||
border-top: 1px solid $theme-border-color;
|
||||
}
|
||||
.search-item {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
padding: 22rpx 0 24rpx;
|
||||
border-bottom: 1px solid $theme-border-color;
|
||||
|
||||
.avatar-img {
|
||||
width: 96rpx;
|
||||
height: 96rpx;
|
||||
margin: 0 20rpx 0 0;
|
||||
border-radius: 50%;
|
||||
overflow: hidden;
|
||||
img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
@ -25,7 +25,7 @@
|
||||
!state.searchText ? 'align-items:center;justify-content:center;' : ''
|
||||
"
|
||||
>
|
||||
<div class="search-result-list">
|
||||
<div class="search-result-list" v-if="state.searchText">
|
||||
<div
|
||||
class="search-result-each-part"
|
||||
v-for="(searchResultValue,
|
||||
@ -33,13 +33,16 @@
|
||||
searchResultIndex) in state.searchResult"
|
||||
:key="searchResultKey"
|
||||
>
|
||||
<div class="result-title"></div>
|
||||
<div class="result-title text-[28rpx] font-regular">
|
||||
{{ getResultKeysValue(searchResultKey) }}
|
||||
</div>
|
||||
<div class="result-list">
|
||||
<searchItem
|
||||
v-for="(item, index) in state?.searchResult[searchResultKey]"
|
||||
:key="index"
|
||||
:searchResultKey="searchResultKey"
|
||||
:searchItem="item"
|
||||
:searchText="state.searchText"
|
||||
></searchItem>
|
||||
</div>
|
||||
<div class="result-has-more"></div>
|
||||
@ -61,6 +64,8 @@ import searchItem from './components/searchItem.vue'
|
||||
import { ServeSeachQueryAll } from '@/api/search/index'
|
||||
import { ref, watch, computed, onMounted, onUnmounted, reactive } from 'vue'
|
||||
import { useAuth } from '@/store/auth'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
const { t } = useI18n()
|
||||
const state = reactive({
|
||||
searchText: '', //搜索内容
|
||||
searchResult: null, //搜索结果
|
||||
@ -74,6 +79,28 @@ const inputSearchText = (e) => {
|
||||
queryAllSearch(searchText)
|
||||
}
|
||||
|
||||
//获取key对应值
|
||||
const getResultKeysValue = (keys) => {
|
||||
let resultKey = ''
|
||||
switch (keys) {
|
||||
case 'user_infos':
|
||||
resultKey = t('index.mine.addressBook')
|
||||
break
|
||||
case 'group_infos':
|
||||
resultKey = t('chat.type.group')
|
||||
break
|
||||
case 'group_member_infos':
|
||||
resultKey = t('chat.type.group')
|
||||
break
|
||||
case 'general_infos':
|
||||
resultKey = t('chat.type.record')
|
||||
break
|
||||
default:
|
||||
resultKey = ''
|
||||
}
|
||||
return resultKey
|
||||
}
|
||||
|
||||
// ES搜索聊天记录-主页搜索什么都有
|
||||
const queryAllSearch = (searchText) => {
|
||||
let params = {
|
||||
@ -152,7 +179,18 @@ page {
|
||||
justify-content: flex-start;
|
||||
|
||||
.search-result-list {
|
||||
width: 100%;
|
||||
padding: 10rpx 18rpx;
|
||||
|
||||
.search-result-each-part {
|
||||
margin: 46rpx 0 0;
|
||||
|
||||
.result-title {
|
||||
line-height: 40rpx;
|
||||
padding: 0 0 10rpx;
|
||||
color: $theme-hint-text;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.search-no-result {
|
||||
|
@ -1,3 +1,4 @@
|
||||
$theme-primary: #46299d;
|
||||
$theme-text: #191919;
|
||||
$theme-hint-text: #999999;
|
||||
$theme-border-color: #f8f8f8;
|
||||
|
@ -76,5 +76,8 @@
|
||||
"cancel": "取消",
|
||||
"search.hint": "检索您要查找的内容吧~",
|
||||
"search.chat.count": "条相关聊天记录",
|
||||
"index.mine.project": "项目"
|
||||
"index.mine.project": "项目",
|
||||
"chat.type.group": "群聊",
|
||||
"chat.type.record": "聊天记录",
|
||||
"search.result.include": "包含:"
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user