Compare commits

...

9 Commits

Author SHA1 Message Date
Phoenix
2c1ae41c3e feat(theme): 将主色调从#1890ff更改为#462AA0
统一修改多处UI组件的主色调,从蓝色(#1890ff)变更为紫色(#462AA0),以保持视觉一致性。同时优化了文件上传逻辑和滚动到底部功能。

refactor(dom): 提取滚动相关操作为工具函数
将滚动到底部逻辑封装为可复用的工具函数,并在多处调用位置进行替换,提高代码复用性。

fix(upload): 修复上传中文件点击打开问题
增加上传状态判断,避免在上传过程中点击文件时打开新窗口。

chore(deps): 更新依赖包版本
升级@types/node和watchpack等依赖包版本。
2025-05-28 11:29:13 +08:00
Phoenix
44a1dd0986 fix: 修复用户信息显示逻辑
- 在ForwardRecord.vue中,将用户ID的引用从item.user_id更改为item.erp_user_id,以确保正确显示用户信息。
2025-05-27 11:48:18 +08:00
Phoenix
8ce7d143ce 12 2025-05-27 11:43:58 +08:00
Phoenix
58b70f84d7 1 2025-05-27 11:42:34 +08:00
Phoenix
6d663d3d01 Merge branch 'main' of http://172.16.100.91:3000/scout666/chat-pc 2025-05-27 11:38:24 +08:00
Phoenix
b117765bdc fix: 修复用户信息处理和组件逻辑
- 在useSessionMenu.ts中,修正用户信息处理逻辑,使用正确的用户ID
- 在IndexSider.vue中,移除不必要的watch监听器,简化代码
- 在PanelContent.vue中,优化右键菜单逻辑,确保仅在特定条件下触发
2025-05-27 11:38:22 +08:00
1edb639ad9 Merge branch 'wyfMain-dev' 2025-05-27 11:24:42 +08:00
8ecee15180 完成聊天记录按日期搜索功能 2025-05-27 11:21:55 +08:00
efb410b657 再次重构从聊天app接入的按条件查询聊天记录组件,去除了按群成员查询、按日期查询的特异化,统一调用history接口,根据不同的场景处理参数,并处理空页面和分页等。处理不同交互场景下需要重置搜索条件的情况。目前按日期查询待接入,其他已完成 2025-05-26 18:57:02 +08:00
32 changed files with 604 additions and 237 deletions

View File

@ -883,8 +883,8 @@ packages:
'@types/ms@2.1.0': '@types/ms@2.1.0':
resolution: {integrity: sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==} resolution: {integrity: sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==}
'@types/node@18.19.101': '@types/node@18.19.103':
resolution: {integrity: sha512-Ykg7fcE3+cOQlLUv2Ds3zil6DVjriGQaSN/kEpl5HQ3DIGM6W0F2n9+GkWV4bRt7KjLymgzNdTnSKCbFUUJ7Kw==} resolution: {integrity: sha512-hHTHp+sEz6SxFsp+SA+Tqrua3AbmlAw+Y//aEwdHrdZkYVRWdvWD3y5uPZ0flYOkgskaFWqZ/YGFm3FaFQ0pRw==}
'@types/node@18.19.99': '@types/node@18.19.99':
resolution: {integrity: sha512-tNGqoGjjI4vY5jfm3lnqgR6yS8wyT76SfsWefLWRyh/cEK4UHmPVyqHZdafI/SNu1PQzfo2JLBWfG8eMmD7KrQ==} resolution: {integrity: sha512-tNGqoGjjI4vY5jfm3lnqgR6yS8wyT76SfsWefLWRyh/cEK4UHmPVyqHZdafI/SNu1PQzfo2JLBWfG8eMmD7KrQ==}
@ -3517,8 +3517,8 @@ packages:
warning@4.0.3: warning@4.0.3:
resolution: {integrity: sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==} resolution: {integrity: sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==}
watchpack@2.4.3: watchpack@2.4.4:
resolution: {integrity: sha512-adBYQLivcg1jbdKEJeqScJJFvgm4qY9+3tXw+jdG6lkVeqRJEtiQmSWjmth8GKmDZuX7sYM4YFxQsf0AzMfGGw==} resolution: {integrity: sha512-c5EGNOiyxxV5qmTtAB7rbiXxi1ooX1pQKMLX/MIabJjRA0SJBQOjKF+KSVfHkr9U1cADPon0mRiVe/riyaiDUA==}
engines: {node: '>=10.13.0'} engines: {node: '>=10.13.0'}
web-worker@1.5.0: web-worker@1.5.0:
@ -3528,8 +3528,8 @@ packages:
resolution: {integrity: sha512-BCfKo2YkDe2ByqkEWe1Rw+zko4LsyS75LVr29C6xIrxAg9JHJ4pl8kaIZ396SUSNp6b4815dRZPSTAS8LlURRQ==} resolution: {integrity: sha512-BCfKo2YkDe2ByqkEWe1Rw+zko4LsyS75LVr29C6xIrxAg9JHJ4pl8kaIZ396SUSNp6b4815dRZPSTAS8LlURRQ==}
deprecated: Package no longer supported. Contact Support at https://www.npmjs.com/support for more info. deprecated: Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.
webpack-sources@3.2.3: webpack-sources@3.3.0:
resolution: {integrity: sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==} resolution: {integrity: sha512-77R0RDmJfj9dyv5p3bM5pOHa+X8/ZkO9c7kpDstigkC4nIDobadsfSGCwB4bKhMVxqAok8tajaoR8rirM7+VFQ==}
engines: {node: '>=10.13.0'} engines: {node: '>=10.13.0'}
webpack-virtual-modules@0.6.2: webpack-virtual-modules@0.6.2:
@ -4228,7 +4228,7 @@ snapshots:
'@types/ms@2.1.0': {} '@types/ms@2.1.0': {}
'@types/node@18.19.101': '@types/node@18.19.103':
dependencies: dependencies:
undici-types: 5.26.5 undici-types: 5.26.5
@ -5895,7 +5895,7 @@ snapshots:
jest-worker@27.5.1: jest-worker@27.5.1:
dependencies: dependencies:
'@types/node': 18.19.101 '@types/node': 18.19.103
merge-stream: 2.0.0 merge-stream: 2.0.0
supports-color: 8.1.1 supports-color: 8.1.1
@ -7244,7 +7244,7 @@ snapshots:
dependencies: dependencies:
loose-envify: 1.4.0 loose-envify: 1.4.0
watchpack@2.4.3: watchpack@2.4.4:
dependencies: dependencies:
glob-to-regexp: 0.4.1 glob-to-regexp: 0.4.1
graceful-fs: 4.2.11 graceful-fs: 4.2.11
@ -7256,7 +7256,7 @@ snapshots:
deepmerge: 1.5.2 deepmerge: 1.5.2
javascript-stringify: 1.6.0 javascript-stringify: 1.6.0
webpack-sources@3.2.3: {} webpack-sources@3.3.0: {}
webpack-virtual-modules@0.6.2: {} webpack-virtual-modules@0.6.2: {}
@ -7284,8 +7284,8 @@ snapshots:
schema-utils: 4.3.2 schema-utils: 4.3.2
tapable: 2.2.2 tapable: 2.2.2
terser-webpack-plugin: 5.3.14(webpack@5.99.8) terser-webpack-plugin: 5.3.14(webpack@5.99.8)
watchpack: 2.4.3 watchpack: 2.4.4
webpack-sources: 3.2.3 webpack-sources: 3.3.0
transitivePeerDependencies: transitivePeerDependencies:
- '@swc/core' - '@swc/core'
- esbuild - esbuild

View File

@ -29,7 +29,7 @@ html {
// 黑色主题 // 黑色主题
html[theme-mode='dark'] { html[theme-mode='dark'] {
--im-primary-color: #1890ff; --im-primary-color: #462AA0;
--im-bg-color: #202124; --im-bg-color: #202124;
--line-border-color: rgb(255 255 255 / 9%); --line-border-color: rgb(255 255 255 / 9%);
--border-color: rgb(255 255 255 / 9%); --border-color: rgb(255 255 255 / 9%);

View File

@ -12,7 +12,7 @@
&:hover, &:hover,
&.dropsize-resizing { &.dropsize-resizing {
background-color: #1890ff; background-color: #462AA0;
} }
&.dropsize-line-top { &.dropsize-line-top {

View File

@ -44,7 +44,7 @@
font-feature-settings: 'tnum'; font-feature-settings: 'tnum';
position: absolute; position: absolute;
display: none; display: none;
color: #1890ff; color: #462AA0;
text-align: center; text-align: center;
vertical-align: middle; vertical-align: middle;
opacity: 0; opacity: 0;
@ -177,7 +177,7 @@
display: block; display: block;
width: 9px; width: 9px;
height: 9px; height: 9px;
background-color: #1890ff; background-color: #462AA0;
border-radius: 100%; border-radius: 100%;
-webkit-transform: scale(0.75); -webkit-transform: scale(0.75);
transform: scale(0.75); transform: scale(0.75);

View File

@ -143,6 +143,7 @@ const editorOption = {
}, },
// //
source: function (searchTerm: string, renderList: any) { source: function (searchTerm: string, renderList: any) {
console.log("source")
if (!props.members.length) { if (!props.members.length) {
return renderList([]) return renderList([])
} }

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'){
//退
} }
} }
@ -988,7 +990,7 @@ const handleEditGroupNameConfirm = () => {
margin: 3px 0; margin: 3px 0;
&:hover { &:hover {
.nickname { .nickname {
color: #1890ff; color: #462AA0;
} }
} }
} }

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

@ -89,7 +89,7 @@ const onContextMenu = (e:any,item: ITalkRecord) => {
<Loading v-if="items.length === 0" /> <Loading v-if="items.length === 0" />
<div v-for="item in items" :key="item.msg_id" class="message-item"> <div v-for="item in items" :key="item.msg_id" class="message-item">
<div class="left-box pointer" @click="showUserInfoModal(item.user_id)"> <div class="left-box pointer" @click="showUserInfoModal(item.erp_user_id)">
<im-avatar :src="item.avatar" :size="38" :username="item.nickname" /> <im-avatar :src="item.avatar" :size="38" :username="item.nickname" />
</div> </div>

View File

@ -86,12 +86,14 @@ const strokeDashoffset = computed(() =>
// //
const handleClick = () => { const handleClick = () => {
console.log('handleClick') if(!props.extra.is_uploading){
window.open( window.open(
`${window.location.origin}/office?url=${props.extra.path}`, `${window.location.origin}/office?url=${props.extra.path}`,
'_blank', '_blank',
'width=1200,height=900,left=200,top=200,toolbar=no,menubar=no,scrollbars=yes,resizable=yes,location=no,status=no' 'width=1200,height=900,left=200,top=200,toolbar=no,menubar=no,scrollbars=yes,resizable=yes,location=no,status=no'
); );
}
} }
function downloadFileWithProgress(resourceUrl, filename) { function downloadFileWithProgress(resourceUrl, filename) {

View File

@ -27,7 +27,7 @@ let textContent = props.extra?.content || ''
textContent = textReplaceLink(textContent) textContent = textReplaceLink(textContent)
if (props.data.talk_type == 2) { if (props.data.talk_type == 2) {
textContent = textReplaceMention(textContent, '#1890ff') textContent = textReplaceMention(textContent, '#462AA0')
} }
textContent = textReplaceEmoji(textContent) textContent = textReplaceEmoji(textContent)

View File

@ -17,7 +17,7 @@ let textContent = props.extra?.content || ''
textContent = textReplaceLink(textContent) textContent = textReplaceLink(textContent)
if (props.data.talk_type == 2) { if (props.data.talk_type == 2) {
textContent = textReplaceMention(textContent, '#1890ff') textContent = textReplaceMention(textContent, '#462AA0')
} }
textContent = textReplaceEmoji(textContent) textContent = textReplaceEmoji(textContent)

View File

@ -121,7 +121,7 @@ onMounted(() => {
:height="5" :height="5"
:show-indicator="false" :show-indicator="false"
:percentage="parseInt(option.progress)" :percentage="parseInt(option.progress)"
color="#1890ff" color="#462AA0"
/> />
</p> </p>
</div> </div>

View File

@ -28,7 +28,7 @@
font-weight: 400; font-weight: 400;
&:hover { &:hover {
color: #1890ff; color: #462AA0;
} }
} }
} }

View File

@ -1514,7 +1514,7 @@ const numWidth = computed(() => {
.x-upload-preview-button:hover { .x-upload-preview-button:hover {
background-color: #e6f7ff; background-color: #e6f7ff;
color: #1890ff; color: #462AA0;
} }
.x-upload-download-button:hover { .x-upload-download-button:hover {

View File

@ -45,7 +45,7 @@
font-feature-settings: 'tnum'; font-feature-settings: 'tnum';
position: absolute; position: absolute;
display: none; display: none;
color: #1890ff; color: #462AA0;
text-align: center; text-align: center;
vertical-align: middle; vertical-align: middle;
opacity: 0; opacity: 0;
@ -103,7 +103,7 @@
display: block; display: block;
width: 9px; width: 9px;
height: 9px; height: 9px;
background-color: #1890ff; background-color: #462AA0;
border-radius: 100%; border-radius: 100%;
transform: scale(0.75); transform: scale(0.75);
transform-origin: 50% 50%; transform-origin: 50% 50%;

View File

@ -7,6 +7,7 @@ import { formatTalkItem, palyMusic, formatTalkRecord } from '@/utils/talk'
import { isElectronMode } from '@/utils/common' import { isElectronMode } from '@/utils/common'
import { ServeClearTalkUnreadNum, ServeCreateTalkList } from '@/api/chat' import { ServeClearTalkUnreadNum, ServeCreateTalkList } from '@/api/chat'
import { useTalkStore, useDialogueStore, useSettingsStore } from '@/store' import { useTalkStore, useDialogueStore, useSettingsStore } from '@/store'
import { isScrollAtBottom, scrollToBottom } from '@/utils/dom'
/** /**
* 好友状态事件 * 好友状态事件
@ -189,12 +190,10 @@ class Talk extends Base {
if (!el) return if (!el) return
// 判断的滚动条是否在底部 // 判断的滚动条是否在底部
const isBottom = Math.ceil(el.scrollTop) + el.clientHeight >= el.scrollHeight const isBottom = isScrollAtBottom(el)
if (isBottom || record.user_id == this.getAccountId()) { if (isBottom || record.user_id == this.getAccountId()) {
nextTick(() => { scrollToBottom()
el.scrollTop = el.scrollHeight + 1000
})
} else { } else {
useDialogueStore().setUnreadBubble() useDialogueStore().setUnreadBubble()
} }

View File

@ -108,8 +108,8 @@ export function useSessionMenu() {
const onUserInfo = (item: ISession) => { const onUserInfo = (item: ISession) => {
console.error('item',item) console.error('item',item)
debugger
user(item.receiver_id) user(item.id)
} }
// 移除会话 // 移除会话

View File

@ -3,7 +3,7 @@ import { ServeTalkRecords } from '@/api/chat'
import { useDialogueStore } from '@/store' import { useDialogueStore } from '@/store'
import { ITalkRecord } from '@/types/chat' import { ITalkRecord } from '@/types/chat'
import { formatTalkRecord } from '@/utils/talk' import { formatTalkRecord } from '@/utils/talk'
import { addClass, removeClass } from '@/utils/dom' import { addClass, removeClass, scrollToBottom, isScrollAtBottom } from '@/utils/dom'
interface Params { interface Params {
receiver_id: number receiver_id: number
@ -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 {
@ -136,16 +137,8 @@ export const useTalkRecord = (uid: number) => {
if (el) { if (el) {
if (request.cursor == 0) { if (request.cursor == 0) {
el.scrollTop = el.scrollHeight scrollToBottom()
setTimeout(() => {
console.log('el.scrollHeight',el.scrollHeight)
console.log('request.cursor == 0')
el.scrollTop = el.scrollHeight + 1000
}, 500)
} else { } else {
console.log('request.cursor !== 0')
el.scrollTop = el.scrollHeight - scrollHeight el.scrollTop = el.scrollHeight - scrollHeight
} }
} }
@ -201,9 +194,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(() => {
@ -221,7 +214,7 @@ export const useTalkRecord = (uid: number) => {
addClass(target, 'border') addClass(target, 'border')
setTimeout(() => removeClass(target, 'border'), 3000) setTimeout(() => removeClass(target, 'border'), 3000)
} else if (el) { } else if (el) {
el.scrollTop = el.scrollHeight scrollToBottom()
} }
}, 50) }, 50)
}) })
@ -255,6 +248,7 @@ export const useTalkRecord = (uid: number) => {
specifiedMsg: { specifiedMsg: {
...loadConfig.specialParams, ...loadConfig.specialParams,
direction: 'up', direction: 'up',
sort_sequence: '',
cursor: getMinSequence() cursor: getMinSequence()
} }
} }

View File

@ -119,7 +119,7 @@ const isActive = (menu) => {
<component <component
:is="nav.icon" :is="nav.icon"
:theme="isActive(nav) ? 'filled' : 'outline'" :theme="isActive(nav) ? 'filled' : 'outline'"
:fill="isActive(nav) ? '#1890ff' : color" :fill="isActive(nav) ? '#462AA0' : color"
:strokeWidth="2" :strokeWidth="2"
:size="22" :size="22"
/> />

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

@ -18,7 +18,7 @@ export function isLoggedIn() {
*/ */
export function getAccessToken() { export function getAccessToken() {
// return storage.get(AccessToken) || '' // return storage.get(AccessToken) || ''
return JSON.parse(localStorage.getItem('token'))||'46d71a72d8d845ad7ed23eba9bdde260e635407190c2ce1bf7fd22088e41682ea07773ec65cae8946d2003f264d55961f96e0fc5da10eb96d3a348c1664e9644ce2108c311309f398ae8ea1b8200bfd490e5cb6e8c52c9e5d493cbabb163368f8351420451a631dbfa749829ee4cda49b77b5ed2d3dced5d0f2b7dd9ee76ba5465c84a17c23af040cd92b6b2a4ea48befbb5c729dcdad0a9c9668befe84074cc24f78899c1d947f8e7f94c7eda5325b8ed698df729e76febb98549ef3482ae942fb4f4a1c92d21836fa784728f0c5483aab2760a991b6b36e6b10c84f840a6433a6ecc31dee36e8f1c6158818bc89d22ab23a552e0c3f606946dcb914a52b692e10d823cc7f43027127359e7ee8555d956e7e095946931ceaa3877675584b0a0a4fc690c8018712b306050ebbdea92037aea31d66d65004be26d3c696abc4c29' return JSON.parse(localStorage.getItem('token'))||'46d71a72d8d845ad7ed23eba9bdde260e635407190c2ce1bf7fd22088e41682ea07773ec65cae8946d2003f264d55961f96e0fc5da10eb96d3a348c1664e9644ce2108c311309f398ae8ea1b8200bfd490e5cb6e8c52c9e5d493cbabb163368f8351420451a631dbfa749829ee4cda49b77b5ed2d3dced5d0f2b7dd9ee76ba5465c84a17c23af040cd92b6b2a4ea48befbb5c729dcdad0a9c9668befe84074cc24f78899c1d947f8e7f94c7eda5325b8ed698df729e76febb98549ef3482ae942fb4f4a1c92d21836fa784728f0c5483aab2760a991b6b36e6b10c84f840a6433a6ecc31dee36e8f1c6158818bc89d227de310c4e6f5d7ff11a9e1ea73aba3f6c749f75a50a2aeaed07b14bc0d8b1db6428caf891f0a0b0c84a49697f4a4e7c8b87d972340ecbf02ddbc4d4f1e51b057c822f8351524e19d52a3ec5ce8c83e2f'
} }
/** /**

View File

@ -52,7 +52,12 @@ export function throttle(fn, delay, call = function () {}) {
* @param {Function} callback 复制成功回调方法 * @param {Function} callback 复制成功回调方法
*/ */
export function clipboard(text, callback) { export function clipboard(text, callback) {
navigator.clipboard // 在wujie环境下使用主应用的clipboard
const clipboardObj = window.__POWERED_BY_WUJIE__
? window.parent.navigator.clipboard
: navigator.clipboard
clipboardObj
.writeText(text) .writeText(text)
.then(() => { .then(() => {
callback && callback() callback && callback()

View File

@ -1,3 +1,5 @@
import { nextTick } from 'vue'
function trim(string: string) { function trim(string: string) {
return (string || '').replace(/^[\s\uFEFF]+|[\s\uFEFF]+$/g, '') return (string || '').replace(/^[\s\uFEFF]+|[\s\uFEFF]+$/g, '')
} }
@ -54,3 +56,26 @@ export function removeClass(el: Element, cls: string) {
el.className = trim(curClass) el.className = trim(curClass)
} }
} }
/**
*
* @param el DOM元素
* @returns boolean
*/
export function isScrollAtBottom(el: HTMLElement): boolean {
return Math.ceil(el.scrollTop) + el.clientHeight >= el.scrollHeight
}
/**
*
* @param id DOM id 'imChatPanel'
* @param offset 0
*/
export function scrollToBottom(id = 'imChatPanel', offset = 0) {
nextTick(() => {
const el = document.getElementById(id)
if (el) {
el.scrollTop = el.scrollHeight + offset
}
})
}

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>

View File

@ -298,13 +298,6 @@ const items = computed((): ISession[] => {
return [...topItems, ...normalItems] return [...topItems, ...normalItems]
}) })
watch(
() => talkStore,
(newValue, oldValue) => {
// console.log(newValue)
},
{ deep: true, immediate: true }
)
watch( watch(
() => state.addressBookSearchNickName, () => state.addressBookSearchNickName,
(newValue, oldValue) => { (newValue, oldValue) => {

View File

@ -95,7 +95,6 @@ const onPanelScroll = (e: any) => {
if (!skipBottom.value && dialogueStore.unreadBubble) { if (!skipBottom.value && dialogueStore.unreadBubble) {
dialogueStore.setUnreadBubble(0) dialogueStore.setUnreadBubble(0)
} }
// //
if (skipBottom.value == false) { if (skipBottom.value == false) {
let len = dialogueStore.records.length let len = dialogueStore.records.length
@ -232,7 +231,7 @@ const onClickNickname = (data: ITalkRecord) => {
// //
const onContextMenu = (e: any, item: ITalkRecord) => { const onContextMenu = (e: any, item: ITalkRecord) => {
console.log('item',item)
if (!dialogueStore.isShowEditor || dialogueStore.isOpenMultiSelect) { if (!dialogueStore.isShowEditor || dialogueStore.isOpenMultiSelect) {
return e.preventDefault() return e.preventDefault()
} }
@ -243,7 +242,7 @@ const onContextMenu = (e: any, item: ITalkRecord) => {
} }
const onConvertText = async (data: ITalkRecord) => { const onConvertText = async (data: ITalkRecord) => {
console.log('data', data)
data.is_convert_text = 1 data.is_convert_text = 1
const res = await voiceToText({ msgId: data.msg_id, voiceUrl: data.extra.url }) const res = await voiceToText({ msgId: data.msg_id, voiceUrl: data.extra.url })
if (res.code == 200) { if (res.code == 200) {
@ -324,13 +323,14 @@ const retry=(item:any)=>{
} }
const onContextMenuAvatar=(e:any,item:any)=>{ const onContextMenuAvatar=(e:any,item:any)=>{
console.log('item',item)
e.preventDefault() e.preventDefault()
if(item.float!=='right'){
bus.emit(EditorConst.Mention, { bus.emit(EditorConst.Mention, {
id: item.user_id, id: item.user_id,
value: item.nickname value: item.nickname
}) })
} }
}
</script> </script>

View File

@ -1,5 +1,5 @@
<script lang="ts" setup> <script lang="ts" setup>
import { ref, onMounted } from 'vue' import { ref, onMounted, nextTick } from 'vue'
import { import {
useTalkStore, useTalkStore,
useDialogueStore, useDialogueStore,
@ -15,7 +15,8 @@ import { parseTime } from '@/utils/datetime'
import Editor from '@/components/editor/Editor.vue' import Editor from '@/components/editor/Editor.vue'
import MultiSelectFooter from './MultiSelectFooter.vue' import MultiSelectFooter from './MultiSelectFooter.vue'
import HistoryRecord from '@/components/talk/HistoryRecord.vue' import HistoryRecord from '@/components/talk/HistoryRecord.vue'
import { uploadImg } from '@/api/upload' import {scrollToBottom} from '@/utils/dom.ts'
import SimpleEditorExample from '@/components/editor/SimpleEditorExample.vue'
const userStore = useUserStore() const userStore = useUserStore()
const talkStore = useTalkStore() const talkStore = useTalkStore()
const editorStore = useEditorStore() const editorStore = useEditorStore()
@ -135,6 +136,9 @@ const onSendVideoEvent = async ({ data }) => {
// //
dialogueStore.addDialogueRecord(tempMessage) dialogueStore.addDialogueRecord(tempMessage)
nextTick(()=>{
scrollToBottom()
})
uploadsStore.initUploadFile( uploadsStore.initUploadFile(
data, data,
props.talk_type, props.talk_type,
@ -145,6 +149,7 @@ const onSendVideoEvent = async ({ data }) => {
}, },
async () => { async () => {
dialogueStore.batchDelDialogueRecord([uploadId]) dialogueStore.batchDelDialogueRecord([uploadId])
} }
) )
} }
@ -185,13 +190,16 @@ const onSendFileEvent = ({ data }) => {
float: 'right' float: 'right'
} }
dialogueStore.addDialogueRecord(tempMessage) dialogueStore.addDialogueRecord(tempMessage)
nextTick(()=>{
scrollToBottom()
})
uploadsStore.initUploadFile(data, props.talk_type, props.receiver_id,clientUploadId, uploadsStore.initUploadFile(data, props.talk_type, props.receiver_id,clientUploadId,
async (percentage) => { async (percentage) => {
dialogueStore.updateUploadProgress(clientUploadId, percentage) dialogueStore.updateUploadProgress(clientUploadId, percentage)
}, },
async () => { async () => {
dialogueStore.batchDelDialogueRecord([clientUploadId]) dialogueStore.batchDelDialogueRecord([clientUploadId])
} }
) )
} }
@ -282,6 +290,7 @@ onMounted(() => {
<template> <template>
<footer class="el-footer"> <footer class="el-footer">
<MultiSelectFooter v-if="dialogueStore.isOpenMultiSelect" /> <MultiSelectFooter v-if="dialogueStore.isOpenMultiSelect" />
<Editor v-else @editor-event="onEditorEvent" :vote="talk_type == 2" :members="members" /> <Editor v-else @editor-event="onEditorEvent" :vote="talk_type == 2" :members="members" />
</footer> </footer>

View File

@ -156,7 +156,7 @@ const onSetMenu = () => {
text-align: center; text-align: center;
&.color { &.color {
color: #1890ff; color: #462AA0;
} }
.online-status { .online-status {
@ -168,7 +168,7 @@ const onSetMenu = () => {
vertical-align: middle; vertical-align: middle;
border-radius: 50%; border-radius: 50%;
position: relative; position: relative;
background-color: #1890ff; background-color: #462AA0;
margin-right: 5px; margin-right: 5px;
&:after { &:after {
@ -177,7 +177,7 @@ const onSetMenu = () => {
left: -1px; left: -1px;
width: 100%; width: 100%;
height: 100%; height: 100%;
border: 1px solid #1890ff; border: 1px solid #462AA0;
border-radius: 50%; border-radius: 50%;
-webkit-animation: antStatusProcessing 1.2s ease-in-out infinite; -webkit-animation: antStatusProcessing 1.2s ease-in-out infinite;
animation: antStatusProcessing 1.2s ease-in-out infinite; animation: antStatusProcessing 1.2s ease-in-out infinite;

View File

@ -1,6 +1,8 @@
<script lang="ts" setup> <script lang="ts" setup>
import { useDialogueStore } from '@/store' import { useDialogueStore } from '@/store'
import { DoubleDown } from '@icon-park/vue-next' import { DoubleDown } from '@icon-park/vue-next'
import { scrollToBottom } from '@/utils/dom'
defineProps(['modelValue']) defineProps(['modelValue'])
const dialogueStore = useDialogueStore() const dialogueStore = useDialogueStore()
@ -8,13 +10,7 @@ const dialogueStore = useDialogueStore()
// //
const onSkipBottom = () => { const onSkipBottom = () => {
console.log('onSkipBottom') console.log('onSkipBottom')
let el = document.getElementById('imChatPanel') scrollToBottom()
if (el) {
el.scrollTo({
top: el.scrollHeight + 1000,
behavior: 'smooth'
})
}
} }
</script> </script>
@ -35,7 +31,7 @@ const onSkipBottom = () => {
min-width: 100px; min-width: 100px;
height: 28px; height: 28px;
font-size: 12px; font-size: 12px;
background-color: #1ebafc; background-color: #462AA0;
color: #ffffff; color: #ffffff;
display: flex; display: flex;
align-items: center; align-items: center;

View File

@ -47,7 +47,7 @@ export default defineConfig(({ mode }) => {
compressPlugin(), compressPlugin(),
UnoCSS(), UnoCSS(),
vueDevTools({ vueDevTools({
launchEditor: 'cursor', launchEditor: 'trae',
}) })
], ],
define: { define: {