Merge branch 'wyfMain-dev'

This commit is contained in:
wangyifeng 2025-05-27 11:24:42 +08:00
commit 1edb639ad9
8 changed files with 615 additions and 164 deletions

106
components.d.ts vendored Normal file
View File

@ -0,0 +1,106 @@
/* eslint-disable */
// @ts-nocheck
// Generated by unplugin-vue-components
// Read more: https://github.com/vuejs/core/pull/3399
// biome-ignore lint: disable
export {}
/* prettier-ignore */
declare module 'vue' {
export interface GlobalComponents {
ApplyTab: typeof import('./src/components/group/manage/ApplyTab.vue')['default']
AudioMessage: typeof import('./src/components/talk/message/AudioMessage.vue')['default']
Avatar: typeof import('./src/components/base/Avatar.vue')['default']
AvatarCropper: typeof import('./src/components/base/AvatarCropper.vue')['default']
AvatarModule: typeof import('./src/components/avatar-module/index.vue')['default']
CodeMessage: typeof import('./src/components/talk/message/CodeMessage.vue')['default']
ConfigTab: typeof import('./src/components/group/manage/ConfigTab.vue')['default']
ConfirmBox: typeof import('./src/components/confirm-box/index.vue')['default']
ContactModal: typeof import('./src/components/user/ContactModal.vue')['default']
CustomBtn: typeof import('./src/components/common/customBtn.vue')['default']
CustomModal: typeof import('./src/components/common/customModal.vue')['default']
DetailTab: typeof import('./src/components/group/manage/DetailTab.vue')['default']
DialogApi: typeof import('./src/components/common/DialogApi.vue')['default']
Editor: typeof import('./src/components/editor/Editor.vue')['default']
EditorEmail: typeof import('./src/components/user/EditorEmail.vue')['default']
EditorMobile: typeof import('./src/components/user/EditorMobile.vue')['default']
EditorPassword: typeof import('./src/components/user/EditorPassword.vue')['default']
FileMessage: typeof import('./src/components/talk/message/FileMessage.vue')['default']
Flnindex: typeof import('./src/components/flnlayout/tree/flnindex.vue')['default']
ForwardMessage: typeof import('./src/components/talk/message/ForwardMessage.vue')['default']
ForwardRecord: typeof import('./src/components/talk/ForwardRecord.vue')['default']
GroupApply: typeof import('./src/components/group/GroupApply.vue')['default']
GroupLaunch: typeof import('./src/components/group/GroupLaunch.vue')['default']
GroupNotice: typeof import('./src/components/group/GroupNotice.vue')['default']
GroupNoticeMessage: typeof import('./src/components/talk/message/GroupNoticeMessage.vue')['default']
GroupPanel: typeof import('./src/components/group/GroupPanel.vue')['default']
HighLightText: typeof import('./src/components/search/highLightText.vue')['default']
HistoryRecord: typeof import('./src/components/talk/HistoryRecord.vue')['default']
ImageMessage: typeof import('./src/components/talk/message/ImageMessage.vue')['default']
LinkMessage: typeof import('./src/components/talk/message/LinkMessage.vue')['default']
Loading: typeof import('./src/components/base/Loading.vue')['default']
LoginMessage: typeof import('./src/components/talk/message/LoginMessage.vue')['default']
Manage: typeof import('./src/components/group/manage/index.vue')['default']
MeEditorCode: typeof import('./src/components/editor/MeEditorCode.vue')['default']
MeEditorEmoticon: typeof import('./src/components/editor/MeEditorEmoticon.vue')['default']
MeEditorImage: typeof import('./src/components/editor/MeEditorImage.vue')['default']
MeEditorLocation: typeof import('./src/components/editor/MeEditorLocation.vue')['default']
MeEditorRecorder: typeof import('./src/components/editor/MeEditorRecorder.vue')['default']
MeEditorVote: typeof import('./src/components/editor/MeEditorVote.vue')['default']
MemberTab: typeof import('./src/components/group/manage/MemberTab.vue')['default']
MessageApi: typeof import('./src/components/common/MessageApi.vue')['default']
MixedMessage: typeof import('./src/components/talk/message/MixedMessage.vue')['default']
NAvatar: typeof import('naive-ui')['NAvatar']
NButton: typeof import('naive-ui')['NButton']
NCheckbox: typeof import('naive-ui')['NCheckbox']
NDatePicker: typeof import('naive-ui')['NDatePicker']
NDropdown: typeof import('naive-ui')['NDropdown']
NEmpty: typeof import('naive-ui')['NEmpty']
NIcon: typeof import('naive-ui')['NIcon']
NImage: typeof import('naive-ui')['NImage']
NInput: typeof import('naive-ui')['NInput']
NModal: typeof import('naive-ui')['NModal']
NoticeEditor: typeof import('./src/components/group/manage/NoticeEditor.vue')['default']
NoticeTab: typeof import('./src/components/group/manage/NoticeTab.vue')['default']
NotificationApi: typeof import('./src/components/common/NotificationApi.vue')['default']
NPopover: typeof import('naive-ui')['NPopover']
NRadio: typeof import('naive-ui')['NRadio']
NScrollbar: typeof import('naive-ui')['NScrollbar']
NSpin: typeof import('naive-ui')['NSpin']
NTag: typeof import('naive-ui')['NTag']
NVirtualList: typeof import('naive-ui')['NVirtualList']
RevokeMessage: typeof import('./src/components/talk/message/RevokeMessage.vue')['default']
RouterLink: typeof import('vue-router')['RouterLink']
RouterView: typeof import('vue-router')['RouterView']
SearchByCondition: typeof import('./src/components/search/searchByCondition.vue')['default']
SearchItem: typeof import('./src/components/search/searchItem.vue')['default']
SearchList: typeof import('./src/components/search/searchList.vue')['default']
SysGroupAdminMessage: typeof import('./src/components/talk/message/system/SysGroupAdminMessage.vue')['default']
SysGroupCancelMutedMessage: typeof import('./src/components/talk/message/system/SysGroupCancelMutedMessage.vue')['default']
SysGroupCreateMessage: typeof import('./src/components/talk/message/system/SysGroupCreateMessage.vue')['default']
SysGroupDismissed: typeof import('./src/components/talk/message/system/SysGroupDismissed.vue')['default']
SysGroupInfoChangeMessage: typeof import('./src/components/talk/message/system/SysGroupInfoChangeMessage.vue')['default']
SysGroupJoinMessage: typeof import('./src/components/talk/message/system/SysGroupJoinMessage.vue')['default']
SysGroupMemberCancelMutedMessage: typeof import('./src/components/talk/message/system/SysGroupMemberCancelMutedMessage.vue')['default']
SysGroupMemberKickedMessage: typeof import('./src/components/talk/message/system/SysGroupMemberKickedMessage.vue')['default']
SysGroupMemberMutedMessage: typeof import('./src/components/talk/message/system/SysGroupMemberMutedMessage.vue')['default']
SysGroupMemberQuitMessage: typeof import('./src/components/talk/message/system/SysGroupMemberQuitMessage.vue')['default']
SysGroupMemberRemovedMessage: typeof import('./src/components/talk/message/system/SysGroupMemberRemovedMessage.vue')['default']
SysGroupMutedMessage: typeof import('./src/components/talk/message/system/SysGroupMutedMessage.vue')['default']
SysGroupTransferMessage: typeof import('./src/components/talk/message/system/SysGroupTransferMessage.vue')['default']
SysTextMessage: typeof import('./src/components/talk/message/system/SysTextMessage.vue')['default']
TextMessage: typeof import('./src/components/talk/message/TextMessage.vue')['default']
Treelabel: typeof import('./src/components/flnlayout/tree/treelabel.vue')['default']
UnknownMessage: typeof import('./src/components/talk/message/UnknownMessage.vue')['default']
UploadsModal: typeof import('./src/components/base/UploadsModal.vue')['default']
UserCardModal: typeof import('./src/components/user/UserCardModal.vue')['default']
VideoMessage: typeof import('./src/components/talk/message/VideoMessage.vue')['default']
VoteMessage: typeof import('./src/components/talk/message/VoteMessage.vue')['default']
XAddressSelect: typeof import('./src/components/x-naive-ui/x-address-select/index.vue')['default']
XNDataTable: typeof import('./src/components/x-naive-ui/x-n-data-table/index.vue')['default']
XNModal: typeof import('./src/components/x-naive-ui/x-n-modal/index.vue')['default']
XNUpload: typeof import('./src/components/x-naive-ui/x-n-upload/index.vue')['default']
XSearchForm: typeof import('./src/components/x-naive-ui/x-search-form/index.vue')['default']
Xtime: typeof import('./src/components/base/Xtime.vue')['default']
}
}

View File

@ -236,6 +236,8 @@ const handleModalConfirm = (closeLoading) => {
closeLoading() closeLoading()
window['$message'].error(err.message) window['$message'].error(err.message)
}) })
} else if (state.chatSettingOperateType == 'quit'){
//退
} }
} }

View File

@ -1,17 +1,18 @@
<template> <template>
<div class="outer-layer search-by-condition-page"> <div class="outer-layer search-by-condition-page">
<n-infinite-scroll style="height: 455px;" :distance="10"> <n-infinite-scroll style="height: 455px;" :distance="40" @load="loadMore">
<div class="root"> <div class="root">
<div v-if="state.condition === 'dateTimePicker'" class="search-by-date"> <!-- <div v-if="state.condition === 'date'" class="search-by-date"> -->
<n-date-picker <!-- <n-date-picker
:panel="true" :panel="true"
type="datetime" type="datetime"
:clearable="true" :clearable="true"
:first-day-of-week="6" :first-day-of-week="6"
:is-date-disabled="dateDisabled" :is-date-disabled="dateDisabled"
:actions="['clear', 'confirm']" :actions="['clear', 'confirm']"
/> @confirm="onDatePickConfirm"
<!-- <tm-time-picker /> -->
<!-- <tm-time-picker
:show="state.showMonthPicker" :show="state.showMonthPicker"
:showDetail="{ :showDetail="{
year: true, year: true,
@ -41,7 +42,7 @@
<img src="@/static/image/search/down-pointer.png" /> <img src="@/static/image/search/down-pointer.png" />
</div> </div>
</tm-time-picker> --> </tm-time-picker> -->
<!-- <tm-calendar-view <!-- <tm-calendar-view
:show="true" :show="true"
:hideTool="true" :hideTool="true"
:hideButton="true" :hideButton="true"
@ -55,17 +56,24 @@
@getDArray="getDArray" @getDArray="getDArray"
:showDefault="false" :showDefault="false"
></tm-calendar-view> --> ></tm-calendar-view> -->
</div> <!-- </div> -->
<div <div
class="search-by-condition-input-list" class="search-by-condition-input-list"
v-if=" v-if="
state.condition === 'imgAndVideo' || state.condition === 'imgAndVideo' ||
state.condition === 'file' || state.condition === 'file' ||
state.condition === 'link' || state.condition === 'link' ||
state.condition === 'member' state.condition === 'member' ||
state.condition === 'all' ||
state.condition === 'date'
" "
:style="{ :style="{
padding: state.condition === 'imgAndVideo' ? '20px 38px' : '' padding:
state.searchResultList.length > 0
? state.condition === 'imgAndVideo'
? '20px 38px'
: ''
: '0'
}" }"
> >
<!-- <div <!-- <div
@ -117,7 +125,14 @@
padding: state.condition === 'imgAndVideo' ? '' : '' padding: state.condition === 'imgAndVideo' ? '' : ''
}" }"
> >
<div class="condition-result-member" v-if="state.condition === 'member'"> <div
class="condition-result-member"
v-if="
state.condition === 'member' ||
state.condition === 'all' ||
state.condition === 'date'
"
>
<searchItem <searchItem
@click="toDialogueByMember(item)" @click="toDialogueByMember(item)"
:searchResultKey="'search_by_member_condition'" :searchResultKey="'search_by_member_condition'"
@ -216,7 +231,7 @@
<span <span
class="text-[14px] font-regular" class="text-[14px] font-regular"
v-if="state.condition === 'file'" v-if="state.condition === 'file'"
style="color: #999999;" style="color: #999999; flex-shrink: 0; margin: 0 0 0 20px;"
> >
{{ item.dateTime }} {{ item.dateTime }}
</span> </span>
@ -236,7 +251,11 @@
<span class="text-[12px] font-regular" v-if="state.condition === 'file'"> <span class="text-[12px] font-regular" v-if="state.condition === 'file'">
{{ item?.nickname }} {{ item?.nickname }}
</span> </span>
<span class="text-[12px] font-regular" v-if="state.condition === 'file'"> <span
class="text-[12px] font-regular"
v-if="state.condition === 'file'"
style="flex-shrink: 0; margin: 0 0 0 20px;"
>
{{ item?.extra?.fileSize }} {{ item?.extra?.fileSize }}
</span> </span>
</div> </div>
@ -278,6 +297,10 @@
</div> </div>
</teleport> </teleport>
</div> </div>
<div class="search-record-empty" v-if="state.searchResultList.length === 0">
<img src="@/assets/image/chatList/search-empty.png" alt="" />
<span>无内容</span>
</div>
</n-infinite-scroll> </n-infinite-scroll>
</div> </div>
</template> </template>
@ -304,6 +327,8 @@ import { parseTime } from '@/utils/datetime'
import { fileFormatSize, fileSuffix } from '@/utils/strings' import { fileFormatSize, fileSuffix } from '@/utils/strings'
import { NImage, NInfiniteScroll, NScrollbar, NIcon, NDatePicker } from 'naive-ui' import { NImage, NInfiniteScroll, NScrollbar, NIcon, NDatePicker } from 'naive-ui'
const emits = defineEmits(['clearSearchMemberByAlphabet', 'getDisabledDateArray'])
const dialogueStore = useDialogueStore() const dialogueStore = useDialogueStore()
// //
const dialogueParams = reactive({ const dialogueParams = reactive({
@ -318,6 +343,26 @@ const props = defineProps({
// //
type: String, type: String,
default: '' default: ''
},
searchMemberItem: {
//
type: String,
default: ''
},
searchRecordByConditionText: {
//
type: String,
default: ''
},
selectedDateTime: {
//
type: [Number, null],
default: null
},
nowDateTime: {
//
type: Date,
default: new Date()
} }
}) })
@ -338,7 +383,10 @@ const state = reactive({
cursor: 0, // cursor: 0, //
msg_type: 0, // msg_type: 0, //
group_member_id: 0, //id group_member_id: 0, //id
flatList: [] // flatList: [], //
selectedDateTime: null, //
isLoadingChatRecord: false, //
hasNoMoreResults: false //
}) })
const videoContext = ref() const videoContext = ref()
@ -409,6 +457,7 @@ const ServeQueryTalkDate = (month) => {
} else { } else {
state.disabledDateArray = state.dArray state.disabledDateArray = state.dArray
} }
emits('getDisabledDateArray', state.disabledDateArray)
} else { } else {
} }
}) })
@ -416,16 +465,6 @@ const ServeQueryTalkDate = (month) => {
resp.catch(() => {}) resp.catch(() => {})
} }
//
const dateDisabled = (e) => {
const date = new Date(e)
const year = date.getFullYear()
const month = String(date.getMonth() + 1).padStart(2, '0')
const day = String(date.getDate()).padStart(2, '0')
const formattedDate = `${year}/${month}/${day}`
return state.disabledDateArray.includes(formattedDate)
}
// //
const selectDate = async (e) => { const selectDate = async (e) => {
if (e == parseTime(state.nowDate, '{y}/{m}/{d}')) { if (e == parseTime(state.nowDate, '{y}/{m}/{d}')) {
@ -518,8 +557,6 @@ const getDArray = (selectedMonth) => {
// //
const inputSearchText = (e) => { const inputSearchText = (e) => {
state.searchText = e state.searchText = e
state.cursor = 0
queryAllSearch()
} }
// //
@ -546,83 +583,93 @@ const queryAllSearch = () => {
limit: 10, // limit: 10, //
no_limit: '', //1 no_limit: '', //1
direction: 'up', //downup direction: 'up', //downup
start_time: '', start_time: state.selectedDateTime
end_time: '', ? parseTime(new Date(state.selectedDateTime), '{y}-{m}-{d}')
: '',
end_time: state.selectedDateTime
? parseTime(new Date(state.selectedDateTime), '{y}-{m}-{d}')
: '',
group_member_user_id: state.group_member_id, //id group_member_user_id: state.group_member_id, //id
file_name: state.msg_type === 6 ? state.searchText : '' file_name: props?.searchRecordByConditionText
} }
console.log(params) console.log(params)
const resp = ServeFindTalkRecords(params) const resp = ServeFindTalkRecords(params)
console.log(resp) console.log(resp)
resp.then(({ code, data }) => { resp
console.log(data) .then(({ code, data }) => {
if (code == 200) { console.log(data)
// cursor0searchResultList if (code == 200) {
let dateList = state.cursor === 0 ? [] : state.searchResultList // cursor0searchResultList
let noMore = false let dateList = state.cursor === 0 ? [] : state.searchResultList
if (data?.items?.length > 0) { if (data?.items?.length > 0) {
data.items.forEach((item) => { data.items.forEach((item) => {
item.dateTime = parseTime(item?.created_at, '{m}/{d}') item.dateTime = parseTime(item?.created_at, '{m}/{d}')
if (item?.extra) { if (item?.extra && typeof item.extra === 'object') {
item.extra.fileSize = fileFormatSize(item?.extra?.size) item.extra.fileSize = fileFormatSize(item?.extra?.size)
item.extra.typeText = item?.extra?.name ? fileSuffix(item?.extra?.name) : '' item.extra.typeText = item?.extra?.name ? fileSuffix(item?.extra?.name) : ''
item.extra.file_avatar = fileTypeAvatar(item?.extra?.typeText) item.extra.file_avatar = fileTypeAvatar(item?.extra?.typeText)
console.log(item.extra.type) // console.log(item.extra.type)
} }
let year = new Date(item.created_at).getFullYear() let year = new Date(item.created_at).getFullYear()
let month = new Date(item.created_at).getMonth() + 1 let month = new Date(item.created_at).getMonth() + 1
let dateMonth = let dateMonth =
year == state.nowDate.getFullYear() && month == state.nowDate.getMonth() + 1 year == state.nowDate.getFullYear() && month == state.nowDate.getMonth() + 1
? '这个月' ? '这个月'
: year + '年' + month + '月' : year + '年' + month + '月'
if (dateList.length > 0) { if (dateList.length > 0) {
let hasAdd = false let hasAdd = false
dateList.forEach((dateItem) => { dateList.forEach((dateItem) => {
if (dateItem.dateMonth === dateMonth) { if (dateItem.dateMonth === dateMonth) {
dateItem.monthResultList.push(item) dateItem.monthResultList.push(item)
hasAdd = true hasAdd = true
}
})
if (!hasAdd) {
console.log(dateList)
dateList.push({
dateMonth: dateMonth,
monthResultList: [item]
})
} }
}) } else {
if (!hasAdd) {
console.log(dateList)
dateList.push({ dateList.push({
dateMonth: dateMonth, dateMonth: dateMonth,
monthResultList: [item] monthResultList: [item]
}) })
} }
} else { })
dateList.push({ }
dateMonth: dateMonth,
monthResultList: [item] //
}) state.searchResultList = dateList
}
}) // z-paging
state.flatList = dateList.reduce((acc, group) => {
return acc.concat(group.monthResultList)
}, [])
if (state.cursor === 0) {
// zPaging.value?.complete(state.flatList)
} else {
// zPaging.value?.completeByNoMore(state.flatList, noMore)
}
state.cursor = data?.cursor
if (data?.cursor === 0) {
state.hasNoMoreResults = true
} else {
state.hasNoMoreResults = false
}
} else { } else {
noMore = true if (state.cursor === 0) {
state.searchResultList = []
state.flatList = []
}
// zPaging.value?.complete([])
} }
})
// .finally(() => {
state.searchResultList = dateList state.isLoadingChatRecord = false //
})
// z-paging
state.flatList = dateList.reduce((acc, group) => {
return acc.concat(group.monthResultList)
}, [])
if (state.cursor === 0) {
// zPaging.value?.complete(state.flatList)
} else {
// zPaging.value?.completeByNoMore(state.flatList, noMore)
}
state.cursor = data?.cursor
} else {
if (state.cursor === 0) {
state.searchResultList = []
state.flatList = []
}
// zPaging.value?.complete([])
}
})
resp.catch(() => { resp.catch(() => {
if (state.cursor === 0) { if (state.cursor === 0) {
@ -630,6 +677,7 @@ const queryAllSearch = () => {
state.flatList = [] state.flatList = []
} }
// zPaging.value?.complete([]) // zPaging.value?.complete([])
state.isLoadingChatRecord = false //
}) })
} }
@ -699,22 +747,45 @@ const toDialogueByMember = async (msgInfo) => {
}) })
} }
//
const resetSearchConditions = (newVal) => {
state.cursor = 0
state.searchResultList = []
state.flatList = []
if (newVal !== 'member') {
state.group_member_id = 0
emits('clearSearchMemberByAlphabet')
}
}
//
const loadMore = () => {
if (state.isLoadingChatRecord || state.hasNoMoreResults) return //
state.isLoadingChatRecord = true
queryAllSearch()
}
watch( watch(
() => props?.conditionType, () => props?.conditionType,
(newVal, oldVal) => { (newVal, oldVal) => {
console.log(newVal, oldVal) console.log(newVal, oldVal)
state.condition = newVal state.condition = newVal
if (newVal) { if (newVal) {
resetSearchConditions(newVal)
if (newVal === 'member') { if (newVal === 'member') {
//
state.showPageTitle = true state.showPageTitle = true
state.pageTitle = '按群成员查找' state.pageTitle = '按群成员查找'
// state.group_member_id = options.groupMemberId state.msg_type = 0
queryAllSearch() queryAllSearch()
} else if (newVal === 'dateTimePicker') { } else if (newVal === 'date') {
//
state.showPageTitle = true state.showPageTitle = true
state.pageTitle = '按日期查找' state.pageTitle = '按日期查找'
state.msg_type = 0
ServeQueryTalkDate(parseTime(state.nowDate, '{y}{m}')) ServeQueryTalkDate(parseTime(state.nowDate, '{y}{m}'))
getDArray(parseTime(state.nowDate, '{y}-{m}')) getDArray(parseTime(state.nowDate, '{y}-{m}'))
queryAllSearch()
} else if (newVal === 'imgAndVideo') { } else if (newVal === 'imgAndVideo') {
state.showPageTitle = true state.showPageTitle = true
state.pageTitle = '图片与视频' state.pageTitle = '图片与视频'
@ -744,6 +815,10 @@ watch(
) )
state.msg_type = 14 state.msg_type = 14
queryAllSearch() queryAllSearch()
} else if (newVal === 'all') {
//
state.msg_type = 0
queryAllSearch()
} }
} }
}, },
@ -752,30 +827,63 @@ watch(
deep: true deep: true
} }
) )
watch(
() => props?.searchMemberItem,
(newVal, oldVal) => {
const memberItem = newVal ? JSON.parse(decodeURIComponent(newVal)) : ''
state.group_member_id = memberItem?.user_id
resetSearchConditions('member')
queryAllSearch()
},
{
immediate: true,
deep: true
}
)
watch(
() => props?.searchRecordByConditionText,
(newVal, oldVal) => {
resetSearchConditions(state.condition)
queryAllSearch()
}
)
watch(
() => props?.selectedDateTime,
(newVal, oldVal) => {
state.selectedDateTime = newVal
resetSearchConditions('date')
queryAllSearch()
}
)
watch(
() => props?.nowDateTime,
(newVal, oldVal) => {
ServeQueryTalkDate(parseTime(newVal, '{y}{m}'))
getDArray(parseTime(newVal, '{y}-{m}'))
}
)
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
.search-by-date { .search-date-picker {
:deep(.n-date-panel-header) { padding: 10px 16px;
display: none; display: flex;
flex-direction: row;
align-items: center;
justify-content: flex-start;
span {
line-height: 20px;
color: #999999;
} }
.search-date-picker { img {
padding: 10px 16px; width: 9px;
display: flex; height: 6px;
flex-direction: row; margin: 0 0 0 13px;
align-items: center;
justify-content: flex-start;
span {
line-height: 20px;
color: #999999;
}
img {
width: 9px;
height: 6px;
margin: 0 0 0 13px;
}
} }
} }
body:deep(.text-overflow-1) { body:deep(.text-overflow-1) {
color: #666666 !important; color: #666666 !important;
line-height: 22px !important; line-height: 22px !important;
@ -963,11 +1071,6 @@ body:deep(.round-3) {
} }
} }
} }
.condition-dimensionality-each:nth-child(1) {
.condition-each-result-attachments {
padding: 0 0 14px !important;
}
}
} }
} }
} }
@ -990,4 +1093,23 @@ body:deep(.round-3) {
height: 100%; height: 100%;
object-fit: contain; object-fit: contain;
} }
.search-record-empty {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
height: 455px;
box-sizing: border-box;
img {
width: 160px;
height: 104px;
}
span {
font-size: 14px;
color: #999;
font-weight: 400;
margin: 13px 0 0;
}
}
</style> </style>

View File

@ -334,6 +334,7 @@ const resultDetail = computed(() => {
.searchRecordDetail-fastLocal { .searchRecordDetail-fastLocal {
display: none; display: none;
line-height: 20px; line-height: 20px;
flex-shrink: 0;
span { span {
color: #46299d; color: #46299d;
font-size: 12px; font-size: 12px;

View File

@ -14,7 +14,8 @@ interface Params {
interface SpecialParams extends Params { interface SpecialParams extends Params {
msg_id?: string msg_id?: string
cursor?: number cursor?: number
direction?: 'up' | 'down' direction?: 'up' | 'down',
sort_sequence?: string
} }
interface LoadOptions { interface LoadOptions {
@ -201,9 +202,9 @@ export const useTalkRecord = (uid: number) => {
loadConfig.status = 2 loadConfig.status = 2
return return
} }
dialogueStore.clearDialogueRecord() // dialogueStore.clearDialogueRecord()
const items = (data.items || []).map((item: ITalkRecord) => formatTalkRecord(uid, item)) const items = (data.items || []).map((item: ITalkRecord) => formatTalkRecord(uid, item))
dialogueStore.unshiftDialogueRecord(items.reverse()) dialogueStore.unshiftDialogueRecord(contextParams.direction === 'down' ? items : items.reverse())
loadConfig.status = items.length >= contextParams.limit ? 1 : 2 loadConfig.status = items.length >= contextParams.limit ? 1 : 2
loadConfig.cursor = data.cursor loadConfig.cursor = data.cursor
nextTick(() => { nextTick(() => {
@ -255,6 +256,7 @@ export const useTalkRecord = (uid: number) => {
specifiedMsg: { specifiedMsg: {
...loadConfig.specialParams, ...loadConfig.specialParams,
direction: 'up', direction: 'up',
sort_sequence: '',
cursor: getMinSequence() cursor: getMinSequence()
} }
} }

View File

@ -54,6 +54,8 @@ export const useDialogueStore = defineStore('dialogue', {
groupInfo: {} , groupInfo: {} ,
// 群成员列表 // 群成员列表
members: [], members: [],
// 群成员列表按字母分组
membersByAlphabet: {},
// 对话记录 // 对话记录
items: { items: {
@ -101,6 +103,7 @@ export const useDialogueStore = defineStore('dialogue', {
// } // }
this.members = [] this.members = []
this.membersByAlphabet = []
if (data.talk_type == 2) { if (data.talk_type == 2) {
this.updateGroupMembers() this.updateGroupMembers()
this.getGroupInfo() this.getGroupInfo()
@ -126,6 +129,17 @@ export const useDialogueStore = defineStore('dialogue', {
online: false, online: false,
value: o.nickname value: o.nickname
})) }))
const groupMap = {};
data.sortItems.forEach(member => {
const alpha = (member.key || member.nickname?.[0] || '#').toUpperCase();
if (!groupMap[alpha]) groupMap[alpha] = [];
groupMap[alpha].push(member);
});
const alphabets = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'.split('');
this.membersByAlphabet = alphabets.map(alpha => ({
alphabet: alpha,
members: groupMap[alpha] || []
})).filter(group => group.members.length > 0);
}, },
// 清空对话记录 // 清空对话记录

View File

@ -58,7 +58,7 @@ export function fileFormatSize(value) {
return '0' return '0'
} }
let unitArr = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'] let unitArr = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB']
let index = 0 let index = 0
let srcsize = parseFloat(value) let srcsize = parseFloat(value)
index = Math.floor(Math.log(srcsize) / Math.log(1000)) index = Math.floor(Math.log(srcsize) / Math.log(1000))

View File

@ -1,6 +1,6 @@
<script lang="ts" setup> <script lang="ts" setup>
import { reactive, computed, watch } from 'vue' import { reactive, computed, watch, ref } from 'vue'
import { NDrawer, NCard, NTag } from 'naive-ui' import { NDrawer, NCard, NTag, NInput, NDatePicker } from 'naive-ui'
import { useUserStore, useDialogueStore, useUploadsStore } from '@/store' import { useUserStore, useDialogueStore, useUploadsStore } from '@/store'
import PanelHeader from './panel/PanelHeader.vue' import PanelHeader from './panel/PanelHeader.vue'
import PanelContent from './panel/PanelContent.vue' import PanelContent from './panel/PanelContent.vue'
@ -16,9 +16,31 @@ import avatarModule from '@/components/avatar-module/index.vue'
const userStore = useUserStore() const userStore = useUserStore()
const dialogueStore = useDialogueStore() const dialogueStore = useDialogueStore()
const uploadsStore = useUploadsStore() const uploadsStore = useUploadsStore()
console.log('dialogueStore',dialogueStore); console.log('dialogueStore', dialogueStore)
const members = computed(() => dialogueStore.members) const members = computed(() => dialogueStore.members)
const membersByAlphabet = computed(() => {
if (state.searchMemberByAlphabet) {
//
return (
(dialogueStore.membersByAlphabet as any)
.map((group: any) => {
//
const filteredMembers = group.members.filter(
(member) => member.nickname && member.nickname.includes(state.searchMemberByAlphabet)
)
return {
...group,
members: filteredMembers
}
})
//
.filter((group) => group.members.length > 0)
)
}
return dialogueStore.membersByAlphabet
})
const isShowEditor = computed(() => dialogueStore.isShowEditor) const isShowEditor = computed(() => dialogueStore.isShowEditor)
// //
@ -31,7 +53,7 @@ const talkParams = reactive({
online: computed(() => dialogueStore.online), online: computed(() => dialogueStore.online),
keyboard: computed(() => dialogueStore.keyboard), keyboard: computed(() => dialogueStore.keyboard),
num: computed(() => dialogueStore.members.length), num: computed(() => dialogueStore.members.length),
avatar:computed(() => dialogueStore.talk.avatar), avatar: computed(() => dialogueStore.talk.avatar),
specifiedMsg: computed(() => dialogueStore.specifiedMsg) specifiedMsg: computed(() => dialogueStore.specifiedMsg)
}) })
@ -46,8 +68,8 @@ const state = reactive({
backgroundColor: '#F9F9FD' backgroundColor: '#F9F9FD'
}, // }, //
searchRecordByConditionText: '', // searchRecordByConditionText: '', //
conditionTag: '', // conditionTag: 'all', //
conditionType: '', // conditionType: 'all', //
isShowGroupNoticeModal: false, // isShowGroupNoticeModal: false, //
customGroupNoticeModalStyle: { customGroupNoticeModalStyle: {
width: '997px', width: '997px',
@ -81,7 +103,14 @@ const state = reactive({
content: '' content: ''
}, // }, //
isAdmin: false, // isAdmin: false, //
groupNoticeContentChange: '' // groupNoticeContentChange: '', //
searchMemberByAlphabet: '', //
searchMemberItem: '', //item
showMemberListByAlphabetPopover: false, //
disabledDateArray: [] as string[], //
selectedDateTime: null, //
nowDateTime: new Date(), //
showDateConditionPopover: false //
}) })
const events = { const events = {
@ -118,7 +147,7 @@ const changeConditionTag = (tag) => {
} else if (tag === 'member') { } else if (tag === 'member') {
state.conditionTag = '群成员' state.conditionTag = '群成员'
} else { } else {
state.conditionTag = '' state.conditionTag = 'all'
} }
} }
@ -370,13 +399,94 @@ const handleGroupNoticeModalShow = (isAdmin) => {
state.isShowGroupNoticeModal = true state.isShowGroupNoticeModal = true
getGroupNotices() getGroupNotices()
} }
//item
const handleMemberItemClick = (memberItem) => {
state.searchMemberItem = encodeURIComponent(JSON.stringify(memberItem))
state.showMemberListByAlphabetPopover = false //
state.searchMemberByAlphabet = ''
}
//A-Z
const clearSearchMemberByAlphabet = () => {
state.searchMemberByAlphabet = ''
}
//
const handleSearchRecordByConditionModalClose = () => {
state.isShowSearchRecordByConditionModal = false
resetSearchRecordByCondition()
}
//
const resetSearchRecordByCondition = () => {
state.searchRecordByConditionText = ''
state.conditionType = 'all'
state.conditionTag = 'all'
state.searchMemberItem = ''
state.showMemberListByAlphabetPopover = false
state.searchMemberByAlphabet = ''
}
//
const dateDisabled = (e) => {
const date = new Date(e)
const year = date.getFullYear()
const month = String(date.getMonth() + 1).padStart(2, '0')
const day = String(date.getDate()).padStart(2, '0')
const formattedDate = `${year}/${month}/${day}`
return state.disabledDateArray.includes(formattedDate)
}
//
const getDisabledDateArray = (disabledDateArray) => {
state.disabledDateArray = disabledDateArray
}
//
const onDatePickConfirm = (datePicker) => {
console.log(datePicker)
state.selectedDateTime = datePicker
state.showDateConditionPopover = false
}
//
const onDatePickClear = () => {
state.selectedDateTime = null
state.showDateConditionPopover = false
state.nowDateTime = new Date()
}
//
const onPrevMonth = () => {
state.nowDateTime = new Date(state.nowDateTime.getFullYear(), state.nowDateTime.getMonth() - 1, 1)
}
//
const onNextMonth = () => {
state.nowDateTime = new Date(state.nowDateTime.getFullYear(), state.nowDateTime.getMonth() + 1, 1)
}
//
const onPrevYear = () => {
state.nowDateTime = new Date(state.nowDateTime.getFullYear() - 1, state.nowDateTime.getMonth(), 1)
}
//
const onNextYear = () => {
state.nowDateTime = new Date(state.nowDateTime.getFullYear() + 1, state.nowDateTime.getMonth(), 1)
}
// popover
const onDatePickShow = (show) => {
if (show) {
// state.nowDateTime = new Date()
}
}
</script> </script>
<template> <template>
<section id="drawer-container" class="el-container is-vertical"> <section id="drawer-container" class="el-container is-vertical">
<!-- 头部区域 --> <!-- 头部区域 -->
<header class="el-header bdr-b"> <header class="el-header bdr-b">
<PanelHeader <PanelHeader
:type="talkParams.type" :type="talkParams.type"
:username="talkParams.username" :username="talkParams.username"
@ -468,6 +578,8 @@ const handleGroupNoticeModalShow = (isAdmin) => {
:style="state.customSearchRecordByConditionModalStyle" :style="state.customSearchRecordByConditionModalStyle"
:customCloseBtn="true" :customCloseBtn="true"
:closable="false" :closable="false"
:customCloseEvent="true"
@customCloseModal="handleSearchRecordByConditionModalClose"
> >
<template #content> <template #content>
<div class="search-record-modal-searchArea"> <div class="search-record-modal-searchArea">
@ -484,7 +596,11 @@ const handleGroupNoticeModalShow = (isAdmin) => {
<img src="@/assets/image/icon/close-btn-grey-line.png" alt="close" /> <img src="@/assets/image/icon/close-btn-grey-line.png" alt="close" />
</template> </template>
<template #prefix> <template #prefix>
<n-tag closable v-if="state.conditionTag" @close="changeConditionTag('')"> <n-tag
closable
v-if="state.conditionTag && state.conditionTag !== 'all'"
@close="changeConditionTag('all')"
>
{{ state.conditionTag }} {{ state.conditionTag }}
</n-tag> </n-tag>
</template> </template>
@ -493,23 +609,87 @@ const handleGroupNoticeModalShow = (isAdmin) => {
<div class="search-area-condition"> <div class="search-area-condition">
<span @click="changeConditionTag('file')">文件</span> <span @click="changeConditionTag('file')">文件</span>
<span @click="changeConditionTag('imgAndVideo')">图片与视频</span> <span @click="changeConditionTag('imgAndVideo')">图片与视频</span>
<n-popover trigger="click" placement="bottom-start" style="height: 312px; padding: 0;"> <n-popover
v-model:show="state.showDateConditionPopover"
trigger="click"
placement="bottom-start"
style="height: 312px; padding: 0;"
@update:show="onDatePickShow"
>
<template #trigger> <template #trigger>
<span id="date-condition" @click="changeConditionTag('date')">日期</span> <span id="date-condition" @click="changeConditionTag('date')">日期</span>
</template> </template>
<historyRecord conditionType="dateTimePicker" v-if="state.conditionType === 'date'" /> <div class="search-by-date">
<n-date-picker
:panel="true"
type="datetime"
:clearable="true"
:first-day-of-week="6"
:is-date-disabled="dateDisabled"
:actions="['clear', 'confirm']"
@confirm="onDatePickConfirm"
@clear="onDatePickClear"
@prev-month="onPrevMonth"
@next-month="onNextMonth"
@prev-year="onPrevYear"
@next-year="onNextYear"
v-model:value="state.selectedDateTime"
/>
</div>
</n-popover> </n-popover>
<n-popover <n-popover
v-model:show="state.showMemberListByAlphabetPopover"
trigger="click" trigger="click"
placement="bottom-start" placement="bottom-start"
style="height: 505px; padding: 0;" style="width: 290px; height: 505px; padding: 0;"
v-if="talkParams.type === 2" v-if="talkParams.type === 2"
> >
<template #trigger> <template #trigger>
<span @click="changeConditionTag('member')">群成员</span> <span @click="changeConditionTag('member')">群成员</span>
</template> </template>
<div> <div class="member-list-by-alphabet-container">
<text>这里是memberList</text> <n-input
placeholder="请输入群成员"
style="margin: 0 0 17px;"
v-model:value="state.searchMemberByAlphabet"
/>
<n-scrollbar style="height: 430px;">
<div
class="member-list-by-alphabet"
v-for="(alphabetMembersItem, alphabetMembersIndex) in membersByAlphabet"
:key="alphabetMembersIndex"
>
<div class="member-list-each-alphabet-header">
<span>{{ (alphabetMembersItem as any).alphabet }}</span>
</div>
<div class="member-list-each-alphabet">
<div
class="member-item-each-alphabet"
v-for="(memberItem, memberItemIndex) in (alphabetMembersItem as any).members"
:key="memberItemIndex"
@click="handleMemberItemClick(memberItem)"
>
<avatarModule
:mode="1"
:avatar="memberItem.avatar"
:userName="memberItem.nickname"
:groupType="0"
:customStyle="{
width: '38px',
height: '38px'
}"
:customTextStyle="{
fontSize: '12px',
fontWeight: 'bold',
color: '#fff',
lineHeight: '17px'
}"
></avatarModule>
<span>{{ memberItem.nickname }}</span>
</div>
</div>
</div>
</n-scrollbar>
</div> </div>
</n-popover> </n-popover>
</div> </div>
@ -517,18 +697,16 @@ const handleGroupNoticeModalShow = (isAdmin) => {
</div> </div>
<div class="search-record-modal-content"> <div class="search-record-modal-content">
<n-card> <n-card>
<div <div class="search-record-card">
class="search-record-card" <historyRecord
v-if="state.searchRecordByConditionText || state.conditionType" :conditionType="state.conditionType"
> :searchMemberItem="state.searchMemberItem"
<historyRecord :conditionType="state.conditionType" /> @clearSearchMemberByAlphabet="clearSearchMemberByAlphabet"
</div> :searchRecordByConditionText="state.searchRecordByConditionText"
<div @getDisabledDateArray="getDisabledDateArray"
class="search-record-empty" :selectedDateTime="state.selectedDateTime"
v-if="!state.searchRecordByConditionText && !state.conditionType" :nowDateTime="state.nowDateTime"
> />
<img src="@/assets/image/chatList/search-empty.png" alt="" />
<span>无内容</span>
</div> </div>
</n-card> </n-card>
</div> </div>
@ -663,6 +841,11 @@ const handleGroupNoticeModalShow = (isAdmin) => {
} }
} }
} }
.search-by-date {
:deep(.n-date-panel-header) {
display: none;
}
}
.search-record-modal-content { .search-record-modal-content {
box-sizing: border-box; box-sizing: border-box;
width: 100%; width: 100%;
@ -677,24 +860,6 @@ const handleGroupNoticeModalShow = (isAdmin) => {
} }
.search-record-card { .search-record-card {
} }
.search-record-empty {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
height: 455px;
box-sizing: border-box;
img {
width: 160px;
height: 104px;
}
span {
font-size: 14px;
color: #999;
font-weight: 400;
margin: 13px 0 0;
}
}
} }
.group-notice-modal-content { .group-notice-modal-content {
.group-notice-text-area { .group-notice-text-area {
@ -772,4 +937,43 @@ const handleGroupNoticeModalShow = (isAdmin) => {
color: #1f2225; color: #1f2225;
} }
} }
.member-list-by-alphabet-container {
padding: 12px;
width: 290px;
.member-list-by-alphabet {
.member-list-each-alphabet-header {
border-bottom: 1px solid #e5e5e5;
span {
font-size: 14px;
color: #999;
font-weight: 400;
line-height: 20px;
}
}
.member-list-each-alphabet {
display: flex;
flex-direction: column;
align-items: flex-start;
justify-content: center;
gap: 10px;
padding: 12px 0;
.member-item-each-alphabet {
display: flex;
flex-direction: row;
align-items: center;
justify-content: flex-start;
gap: 10px;
width: 100%;
cursor: pointer;
span {
font-size: 12px;
color: #000;
font-weight: 400;
line-height: 17px;
}
}
}
}
}
</style> </style>