Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
C
corporate-culture-qd
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
王立鹏
corporate-culture-qd
Commits
674789ad
Commit
674789ad
authored
Apr 15, 2026
by
lijiabin
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
【需求 21402】 feat: 完成关于私信相关的内容
parent
deea553b
Hide whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
294 additions
and
775 deletions
+294
-775
index.ts
src/api/index.ts
+2
-0
index.ts
src/api/selfMessage/index.ts
+39
-0
types.ts
src/api/selfMessage/types.ts
+80
-0
index.vue
src/components/common/Comment/index.vue
+1
-1
index.vue
src/views/selfMessage/index.vue
+168
-770
index.vue
src/views/userPage/index.vue
+4
-4
No files found.
src/api/index.ts
View file @
674789ad
...
...
@@ -15,6 +15,7 @@ export * from './otherUserPage'
export
*
from
'./auction'
export
*
from
'./dailyLottery'
export
*
from
'./launchCampaign'
export
*
from
'./selfMessage'
// 导出类型
export
*
from
'./task/types'
export
*
from
'./shop/types'
...
...
@@ -32,3 +33,4 @@ export * from './otherUserPage/types'
export
*
from
'./auction/types'
export
*
from
'./dailyLottery/types'
export
*
from
'./launchCampaign/types'
export
*
from
'./selfMessage/types'
src/api/selfMessage/index.ts
0 → 100644
View file @
674789ad
import
service
from
'@/utils/request/index'
import
type
{
SendMessageDto
,
GetMessageListDto
,
ConversationResponseDto
,
GetMessageDetailListDto
,
MessageDetailListItem
,
}
from
'./types'
// 关于私信的相关接口
/**
* 给用户发表私信
*/
export
const
sendMessage
=
(
data
:
SendMessageDto
)
=>
{
return
service
.
request
({
url
:
'/api/cultureDialog/sendDialogMessage'
,
method
:
'POST'
,
data
,
})
}
// 获取私信列表
export
const
getMessageList
=
(
data
:
GetMessageListDto
)
=>
{
return
service
.
request
<
ConversationResponseDto
>
({
url
:
'/api/cultureDialog/getDialogList'
,
method
:
'POST'
,
data
,
})
}
// 查看某个私信详情
export
const
getMessageDetailList
=
(
data
:
GetMessageDetailListDto
)
=>
{
return
service
.
request
<
MessageDetailListItem
[]
>
({
url
:
`/api/cultureDialog/getDialogMessageList`
,
method
:
'POST'
,
data
,
})
}
src/api/selfMessage/types.ts
0 → 100644
View file @
674789ad
import
{
BooleanFlag
}
from
'@/constants'
/**
* 发送私信的参数
*/
export
interface
SendMessageDto
{
content
:
string
chatType
:
BooleanFlag
// 0 实名 1 匿名
senderId
:
string
receiverId
:
string
images
:
string
}
/**
* 获取私信列表的参数
*/
export
interface
GetMessageListDto
{
chatType
?:
BooleanFlag
search
?:
string
}
/**
* 获取私信列表的响应
*/
export
interface
ConversationResponseDto
{
anonymousUnreadCount
:
number
realUnreadCount
:
number
totalUnreadCount
:
number
dialogList
:
ConversationItem
[]
}
export
interface
ConversationItem
{
chat_type
:
string
chat_type_desc
:
string
had_read
:
number
id
:
number
is_self_last_msg
:
boolean
last_message
:
string
last_message_prefix
:
string
last_sender_name
:
string
last_time
:
string
last_time_str
:
string
last_user_id
:
string
other_user_account
:
string
other_user_avatar
:
string
other_user_id
:
string
other_user_name
:
string
status
:
number
un_read_count
:
number
}
/**
* 获取私信详情的列表参数
*/
export
interface
GetMessageDetailListDto
{
receiverId
:
string
dialogId
:
number
chatType
:
BooleanFlag
}
/**
* 获取私信详情的列表响应
*/
export
interface
MessageDetailListItem
{
images
:
string
relation_status
:
number
create_time
:
string
receiver_id
:
string
message_id
:
number
sender_name
:
string
sender_avatar
:
string
content
:
string
sender_id
:
string
relation_id
:
number
create_time_str
:
string
is_anonymous
:
boolean
dialog_id
:
number
is_self
:
boolean
image_list
:
string
[]
}
src/components/common/Comment/index.vue
View file @
674789ad
<
template
>
<div
ref=
"commentRef"
class=
"bg-white rounded-3 shadow-sm border border-gray-100
overflow-hidden
"
>
<div
ref=
"commentRef"
class=
"bg-white rounded-3 shadow-sm border border-gray-100"
>
<!-- 评论筛选 -->
<div
class=
"p-4 border-b border-gray-100"
>
<div
class=
"flex items-center gap-4 justify-between"
>
...
...
src/views/selfMessage/index.vue
View file @
674789ad
<
script
setup
lang=
"ts"
>
import
CommentBox
from
'@/components/common/CommentBox/index.vue'
import
{
useUserStore
}
from
'@/stores'
import
dayjs
from
'dayjs'
import
{
push
}
from
'notivue'
import
{
storeToRefs
}
from
'pinia'
import
BackButton
from
'@/components/common/BackButton/index.vue'
import
type
{
ScrollbarInstance
}
from
'element-plus'
type
MessageCategory
=
'anonymous'
|
'realname'
type
MessageSender
=
'self'
|
'other'
interface
ChatMessage
{
id
:
number
sender
:
MessageSender
text
:
string
images
:
string
[]
createdAt
:
string
}
interface
ConversationItem
{
id
:
number
category
:
MessageCategory
name
:
string
avatar
:
string
badge
:
string
subtitle
:
string
sourceTitle
:
string
sourceTypeLabel
:
string
onlineStatus
:
string
unread
:
number
isPinned
?:
boolean
messages
:
ChatMessage
[]
}
import
{
getMessageList
,
sendMessage
,
getMessageDetailList
}
from
'@/api'
import
type
{
ConversationItem
,
MessageDetailListItem
}
from
'@/api/selfMessage/types'
import
{
parseEmoji
}
from
'@/utils/emoji'
import
{
BooleanFlag
}
from
'@/constants'
const
userStore
=
useUserStore
()
const
{
userInfo
}
=
storeToRefs
(
userStore
)
const
route
=
useRoute
()
const
categoryTabs
=
[
{
key
:
'anonymous'
as
MessageCategory
,
key
:
BooleanFlag
.
YES
,
label
:
'匿名私信'
,
description
:
'适用于除问吧、实践、专访文章以外的内容'
,
},
{
key
:
'realname'
as
MessageCategory
,
key
:
BooleanFlag
.
NO
,
label
:
'实名私信'
,
description
:
'适用于问吧、实践、专访文章'
,
},
]
const
conversationList
=
ref
<
ConversationItem
[]
>
([
{
id
:
1
,
category
:
'realname'
,
name
:
'陈菲'
,
avatar
:
'https://api.dicebear.com/7.x/adventurer/svg?seed=ChengFei'
,
badge
:
'实名'
,
subtitle
:
'你上次提到的培训案例已经补充好了'
,
sourceTitle
:
'实践 · 从复盘到落地的团队协作方法'
,
sourceTypeLabel
:
'实践'
,
onlineStatus
:
'29分钟前活跃'
,
unread
:
2
,
isPinned
:
true
,
messages
:
[
{
id
:
101
,
sender
:
'other'
,
text
:
'你好,这篇实践文章里提到的复盘模板能发我看一下吗?'
,
images
:
[],
createdAt
:
'2026-04-09 09:18'
,
},
{
id
:
102
,
sender
:
'self'
,
text
:
'可以,我整理完私信给你。'
,
images
:
[],
createdAt
:
'2026-04-09 09:22'
,
},
{
id
:
103
,
sender
:
'other'
,
text
:
'好的,我也很想看看你们怎么把共识拆成执行动作。'
,
images
:
[],
createdAt
:
'2026-04-09 09:24'
,
},
],
},
{
id
:
2
,
category
:
'anonymous'
,
name
:
'树洞来信'
,
avatar
:
'https://api.dicebear.com/7.x/shapes/svg?seed=AnonymousA'
,
badge
:
'匿名'
,
subtitle
:
'谢谢你愿意认真回复我的困惑'
,
sourceTitle
:
'案例库 · 一次被看见的小改变'
,
sourceTypeLabel
:
'案例'
,
onlineStatus
:
'刚刚'
,
unread
:
0
,
messages
:
[
{
id
:
201
,
sender
:
'other'
,
text
:
'看到你的评论后想私信你,原来很多人也会经历类似阶段。'
,
images
:
[],
createdAt
:
'2026-04-09 08:31'
,
},
{
id
:
202
,
sender
:
'self'
,
text
:
'会的,所以别急着否定自己,先把能做的小事做起来。'
,
images
:
[],
createdAt
:
'2026-04-09 08:36'
,
},
],
},
{
id
:
3
,
category
:
'realname'
,
name
:
'王守勇'
,
avatar
:
'https://api.dicebear.com/7.x/adventurer/svg?seed=WangShouYong'
,
badge
:
'实名'
,
subtitle
:
'关于专访提到的组织演进,我补了两张图'
,
sourceTitle
:
'专访 · 从宏大叙事到实事求是'
,
sourceTypeLabel
:
'专访'
,
onlineStatus
:
'今天 08:45'
,
unread
:
1
,
messages
:
[
{
id
:
301
,
sender
:
'other'
,
text
:
'这两张图是给你补充的版本,可以一起带给同事看。'
,
images
:
[
'https://images.unsplash.com/photo-1516321318423-f06f85e504b3?auto=format&fit=crop&w=600&q=80'
,
'https://images.unsplash.com/photo-1517245386807-bb43f82c33c4?auto=format&fit=crop&w=600&q=80'
,
],
createdAt
:
'2026-04-09 08:45'
,
},
],
},
{
id
:
4
,
category
:
'anonymous'
,
name
:
'未署名同事'
,
avatar
:
'https://api.dicebear.com/7.x/shapes/svg?seed=AnonymousB'
,
badge
:
'匿名'
,
subtitle
:
'想继续请教你怎么带新人'
,
sourceTitle
:
'视频 · 一次普通晨会为什么能开得更好'
,
sourceTypeLabel
:
'视频'
,
onlineStatus
:
'昨天'
,
unread
:
0
,
messages
:
[
{
id
:
401
,
sender
:
'other'
,
text
:
'我发现自己总在说结论,带新人时对方不太能跟上。'
,
images
:
[],
createdAt
:
'2026-04-08 17:20'
,
},
{
id
:
402
,
sender
:
'self'
,
text
:
'可以先从为什么这么做开始讲,再拆成两个最关键动作。'
,
images
:
[],
createdAt
:
'2026-04-08 17:35'
,
},
],
},
{
id
:
11
,
category
:
'realname'
,
name
:
'陈菲'
,
avatar
:
'https://api.dicebear.com/7.x/adventurer/svg?seed=ChengFei'
,
badge
:
'实名'
,
subtitle
:
'你上次提到的培训案例已经补充好了'
,
sourceTitle
:
'实践 · 从复盘到落地的团队协作方法'
,
sourceTypeLabel
:
'实践'
,
onlineStatus
:
'29分钟前活跃'
,
unread
:
2
,
isPinned
:
true
,
messages
:
[
{
id
:
101
,
sender
:
'other'
,
text
:
'你好,这篇实践文章里提到的复盘模板能发我看一下吗?'
,
images
:
[],
createdAt
:
'2026-04-09 09:18'
,
},
{
id
:
102
,
sender
:
'self'
,
text
:
'可以,我整理完私信给你。'
,
images
:
[],
createdAt
:
'2026-04-09 09:22'
,
},
{
id
:
103
,
sender
:
'other'
,
text
:
'好的,我也很想看看你们怎么把共识拆成执行动作。'
,
images
:
[],
createdAt
:
'2026-04-09 09:24'
,
},
],
},
{
id
:
21
,
category
:
'anonymous'
,
name
:
'树洞来信'
,
avatar
:
'https://api.dicebear.com/7.x/shapes/svg?seed=AnonymousA'
,
badge
:
'匿名'
,
subtitle
:
'谢谢你愿意认真回复我的困惑'
,
sourceTitle
:
'案例库 · 一次被看见的小改变'
,
sourceTypeLabel
:
'案例'
,
onlineStatus
:
'刚刚'
,
unread
:
0
,
messages
:
[
{
id
:
201
,
sender
:
'other'
,
text
:
'看到你的评论后想私信你,原来很多人也会经历类似阶段。'
,
images
:
[],
createdAt
:
'2026-04-09 08:31'
,
},
{
id
:
202
,
sender
:
'self'
,
text
:
'会的,所以别急着否定自己,先把能做的小事做起来。'
,
images
:
[],
createdAt
:
'2026-04-09 08:36'
,
},
],
},
{
id
:
31
,
category
:
'realname'
,
name
:
'王守勇'
,
avatar
:
'https://api.dicebear.com/7.x/adventurer/svg?seed=WangShouYong'
,
badge
:
'实名'
,
subtitle
:
'关于专访提到的组织演进,我补了两张图'
,
sourceTitle
:
'专访 · 从宏大叙事到实事求是'
,
sourceTypeLabel
:
'专访'
,
onlineStatus
:
'今天 08:45'
,
unread
:
1
,
messages
:
[
{
id
:
301
,
sender
:
'other'
,
text
:
'这两张图是给你补充的版本,可以一起带给同事看。'
,
images
:
[
'https://images.unsplash.com/photo-1516321318423-f06f85e504b3?auto=format&fit=crop&w=600&q=80'
,
'https://images.unsplash.com/photo-1517245386807-bb43f82c33c4?auto=format&fit=crop&w=600&q=80'
,
],
createdAt
:
'2026-04-09 08:45'
,
},
],
},
{
id
:
41
,
category
:
'anonymous'
,
name
:
'未署名同事'
,
avatar
:
'https://api.dicebear.com/7.x/shapes/svg?seed=AnonymousB'
,
badge
:
'匿名'
,
subtitle
:
'想继续请教你怎么带新人'
,
sourceTitle
:
'视频 · 一次普通晨会为什么能开得更好'
,
sourceTypeLabel
:
'视频'
,
onlineStatus
:
'昨天'
,
unread
:
0
,
messages
:
[
{
id
:
401
,
sender
:
'other'
,
text
:
'我发现自己总在说结论,带新人时对方不太能跟上。'
,
images
:
[],
createdAt
:
'2026-04-08 17:20'
,
},
{
id
:
402
,
sender
:
'self'
,
text
:
'可以先从为什么这么做开始讲,再拆成两个最关键动作。'
,
images
:
[],
createdAt
:
'2026-04-08 17:35'
,
},
],
},
{
id
:
12
,
category
:
'realname'
,
name
:
'陈菲'
,
avatar
:
'https://api.dicebear.com/7.x/adventurer/svg?seed=ChengFei'
,
badge
:
'实名'
,
subtitle
:
'你上次提到的培训案例已经补充好了'
,
sourceTitle
:
'实践 · 从复盘到落地的团队协作方法'
,
sourceTypeLabel
:
'实践'
,
onlineStatus
:
'29分钟前活跃'
,
unread
:
2
,
isPinned
:
true
,
messages
:
[
{
id
:
101
,
sender
:
'other'
,
text
:
'你好,这篇实践文章里提到的复盘模板能发我看一下吗?'
,
images
:
[],
createdAt
:
'2026-04-09 09:18'
,
},
{
id
:
102
,
sender
:
'self'
,
text
:
'可以,我整理完私信给你。'
,
images
:
[],
createdAt
:
'2026-04-09 09:22'
,
},
{
id
:
103
,
sender
:
'other'
,
text
:
'好的,我也很想看看你们怎么把共识拆成执行动作。'
,
images
:
[],
createdAt
:
'2026-04-09 09:24'
,
},
],
},
{
id
:
22
,
category
:
'anonymous'
,
name
:
'树洞来信'
,
avatar
:
'https://api.dicebear.com/7.x/shapes/svg?seed=AnonymousA'
,
badge
:
'匿名'
,
subtitle
:
'谢谢你愿意认真回复我的困惑'
,
sourceTitle
:
'案例库 · 一次被看见的小改变'
,
sourceTypeLabel
:
'案例'
,
onlineStatus
:
'刚刚'
,
unread
:
0
,
messages
:
[
{
id
:
201
,
sender
:
'other'
,
text
:
'看到你的评论后想私信你,原来很多人也会经历类似阶段。'
,
images
:
[],
createdAt
:
'2026-04-09 08:31'
,
},
{
id
:
202
,
sender
:
'self'
,
text
:
'会的,所以别急着否定自己,先把能做的小事做起来。'
,
images
:
[],
createdAt
:
'2026-04-09 08:36'
,
},
],
},
{
id
:
32
,
category
:
'realname'
,
name
:
'王守勇'
,
avatar
:
'https://api.dicebear.com/7.x/adventurer/svg?seed=WangShouYong'
,
badge
:
'实名'
,
subtitle
:
'关于专访提到的组织演进,我补了两张图'
,
sourceTitle
:
'专访 · 从宏大叙事到实事求是'
,
sourceTypeLabel
:
'专访'
,
onlineStatus
:
'今天 08:45'
,
unread
:
1
,
messages
:
[
{
id
:
301
,
sender
:
'other'
,
text
:
'这两张图是给你补充的版本,可以一起带给同事看。'
,
images
:
[
'https://images.unsplash.com/photo-1516321318423-f06f85e504b3?auto=format&fit=crop&w=600&q=80'
,
'https://images.unsplash.com/photo-1517245386807-bb43f82c33c4?auto=format&fit=crop&w=600&q=80'
,
],
createdAt
:
'2026-04-09 08:45'
,
},
],
},
{
id
:
4
,
category
:
'anonymous'
,
name
:
'未署名同事'
,
avatar
:
'https://api.dicebear.com/7.x/shapes/svg?seed=AnonymousB'
,
badge
:
'匿名'
,
subtitle
:
'想继续请教你怎么带新人'
,
sourceTitle
:
'视频 · 一次普通晨会为什么能开得更好'
,
sourceTypeLabel
:
'视频'
,
onlineStatus
:
'昨天'
,
unread
:
0
,
messages
:
[
{
id
:
401
,
sender
:
'other'
,
text
:
'我发现自己总在说结论,带新人时对方不太能跟上。'
,
images
:
[],
createdAt
:
'2026-04-08 17:20'
,
},
{
id
:
402
,
sender
:
'self'
,
text
:
'可以先从为什么这么做开始讲,再拆成两个最关键动作。'
,
images
:
[],
createdAt
:
'2026-04-08 17:35'
,
},
],
},
{
id
:
13
,
category
:
'realname'
,
name
:
'陈菲'
,
avatar
:
'https://api.dicebear.com/7.x/adventurer/svg?seed=ChengFei'
,
badge
:
'实名'
,
subtitle
:
'你上次提到的培训案例已经补充好了'
,
sourceTitle
:
'实践 · 从复盘到落地的团队协作方法'
,
sourceTypeLabel
:
'实践'
,
onlineStatus
:
'29分钟前活跃'
,
unread
:
2
,
isPinned
:
true
,
messages
:
[
{
id
:
101
,
sender
:
'other'
,
text
:
'你好,这篇实践文章里提到的复盘模板能发我看一下吗?'
,
images
:
[],
createdAt
:
'2026-04-09 09:18'
,
},
{
id
:
102
,
sender
:
'self'
,
text
:
'可以,我整理完私信给你。'
,
images
:
[],
createdAt
:
'2026-04-09 09:22'
,
},
{
id
:
103
,
sender
:
'other'
,
text
:
'好的,我也很想看看你们怎么把共识拆成执行动作。'
,
images
:
[],
createdAt
:
'2026-04-09 09:24'
,
},
],
},
{
id
:
2
,
category
:
'anonymous'
,
name
:
'树洞来信'
,
avatar
:
'https://api.dicebear.com/7.x/shapes/svg?seed=AnonymousA'
,
badge
:
'匿名'
,
subtitle
:
'谢谢你愿意认真回复我的困惑'
,
sourceTitle
:
'案例库 · 一次被看见的小改变'
,
sourceTypeLabel
:
'案例'
,
onlineStatus
:
'刚刚'
,
unread
:
0
,
messages
:
[
{
id
:
201
,
sender
:
'other'
,
text
:
'看到你的评论后想私信你,原来很多人也会经历类似阶段。'
,
images
:
[],
createdAt
:
'2026-04-09 08:31'
,
},
{
id
:
202
,
sender
:
'self'
,
text
:
'会的,所以别急着否定自己,先把能做的小事做起来。'
,
images
:
[],
createdAt
:
'2026-04-09 08:36'
,
},
],
},
{
id
:
3
,
category
:
'realname'
,
name
:
'王守勇'
,
avatar
:
'https://api.dicebear.com/7.x/adventurer/svg?seed=WangShouYong'
,
badge
:
'实名'
,
subtitle
:
'关于专访提到的组织演进,我补了两张图'
,
sourceTitle
:
'专访 · 从宏大叙事到实事求是'
,
sourceTypeLabel
:
'专访'
,
onlineStatus
:
'今天 08:45'
,
unread
:
1
,
messages
:
[
{
id
:
301
,
sender
:
'other'
,
text
:
'这两张图是给你补充的版本,可以一起带给同事看。'
,
images
:
[
'https://images.unsplash.com/photo-1516321318423-f06f85e504b3?auto=format&fit=crop&w=600&q=80'
,
'https://images.unsplash.com/photo-1517245386807-bb43f82c33c4?auto=format&fit=crop&w=600&q=80'
,
],
createdAt
:
'2026-04-09 08:45'
,
},
],
},
{
id
:
4
,
category
:
'anonymous'
,
name
:
'未署名同事'
,
avatar
:
'https://api.dicebear.com/7.x/shapes/svg?seed=AnonymousB'
,
badge
:
'匿名'
,
subtitle
:
'想继续请教你怎么带新人'
,
sourceTitle
:
'视频 · 一次普通晨会为什么能开得更好'
,
sourceTypeLabel
:
'视频'
,
onlineStatus
:
'昨天'
,
unread
:
0
,
messages
:
[
{
id
:
401
,
sender
:
'other'
,
text
:
'我发现自己总在说结论,带新人时对方不太能跟上。'
,
images
:
[],
createdAt
:
'2026-04-08 17:20'
,
},
{
id
:
402
,
sender
:
'self'
,
text
:
'可以先从为什么这么做开始讲,再拆成两个最关键动作。'
,
images
:
[],
createdAt
:
'2026-04-08 17:35'
,
},
],
},
{
id
:
1
,
category
:
'realname'
,
name
:
'陈菲'
,
avatar
:
'https://api.dicebear.com/7.x/adventurer/svg?seed=ChengFei'
,
badge
:
'实名'
,
subtitle
:
'你上次提到的培训案例已经补充好了'
,
sourceTitle
:
'实践 · 从复盘到落地的团队协作方法'
,
sourceTypeLabel
:
'实践'
,
onlineStatus
:
'29分钟前活跃'
,
unread
:
2
,
isPinned
:
true
,
messages
:
[
{
id
:
101
,
sender
:
'other'
,
text
:
'你好,这篇实践文章里提到的复盘模板能发我看一下吗?'
,
images
:
[],
createdAt
:
'2026-04-09 09:18'
,
},
{
id
:
102
,
sender
:
'self'
,
text
:
'可以,我整理完私信给你。'
,
images
:
[],
createdAt
:
'2026-04-09 09:22'
,
},
{
id
:
103
,
sender
:
'other'
,
text
:
'好的,我也很想看看你们怎么把共识拆成执行动作。'
,
images
:
[],
createdAt
:
'2026-04-09 09:24'
,
},
],
},
{
id
:
23
,
category
:
'anonymous'
,
name
:
'树洞来信'
,
avatar
:
'https://api.dicebear.com/7.x/shapes/svg?seed=AnonymousA'
,
badge
:
'匿名'
,
subtitle
:
'谢谢你愿意认真回复我的困惑'
,
sourceTitle
:
'案例库 · 一次被看见的小改变'
,
sourceTypeLabel
:
'案例'
,
onlineStatus
:
'刚刚'
,
unread
:
0
,
messages
:
[
{
id
:
201
,
sender
:
'other'
,
text
:
'看到你的评论后想私信你,原来很多人也会经历类似阶段。'
,
images
:
[],
createdAt
:
'2026-04-09 08:31'
,
},
{
id
:
202
,
sender
:
'self'
,
text
:
'会的,所以别急着否定自己,先把能做的小事做起来。'
,
images
:
[],
createdAt
:
'2026-04-09 08:36'
,
},
],
},
{
id
:
33
,
category
:
'realname'
,
name
:
'王守勇'
,
avatar
:
'https://api.dicebear.com/7.x/adventurer/svg?seed=WangShouYong'
,
badge
:
'实名'
,
subtitle
:
'关于专访提到的组织演进,我补了两张图'
,
sourceTitle
:
'专访 · 从宏大叙事到实事求是'
,
sourceTypeLabel
:
'专访'
,
onlineStatus
:
'今天 08:45'
,
unread
:
1
,
messages
:
[
{
id
:
301
,
sender
:
'other'
,
text
:
'这两张图是给你补充的版本,可以一起带给同事看。'
,
images
:
[
'https://images.unsplash.com/photo-1516321318423-f06f85e504b3?auto=format&fit=crop&w=600&q=80'
,
'https://images.unsplash.com/photo-1517245386807-bb43f82c33c4?auto=format&fit=crop&w=600&q=80'
,
],
createdAt
:
'2026-04-09 08:45'
,
},
],
},
{
id
:
42
,
category
:
'anonymous'
,
name
:
'未署名同事'
,
avatar
:
'https://api.dicebear.com/7.x/shapes/svg?seed=AnonymousB'
,
badge
:
'匿名'
,
subtitle
:
'想继续请教你怎么带新人'
,
sourceTitle
:
'视频 · 一次普通晨会为什么能开得更好'
,
sourceTypeLabel
:
'视频'
,
onlineStatus
:
'昨天'
,
unread
:
0
,
messages
:
[
{
id
:
401
,
sender
:
'other'
,
text
:
'我发现自己总在说结论,带新人时对方不太能跟上。'
,
images
:
[],
createdAt
:
'2026-04-08 17:20'
,
},
{
id
:
402
,
sender
:
'self'
,
text
:
'可以先从为什么这么做开始讲,再拆成两个最关键动作。'
,
images
:
[],
createdAt
:
'2026-04-08 17:35'
,
},
],
},
])
const
conversationList
=
ref
<
ConversationItem
[]
>
([])
const
activeCategory
=
ref
<
MessageCategory
>
(
'anonymous'
)
const
activeConversationId
=
ref
<
number
>
(
2
)
const
activeCategory
=
ref
<
BooleanFlag
>
(
BooleanFlag
.
NO
)
const
activeConversationId
=
ref
<
number
>
(
0
)
const
keyword
=
ref
(
''
)
const
draftText
=
ref
(
''
)
const
draftImages
=
ref
(
''
)
const
sending
=
ref
(
false
)
const
messageListRef
=
useTemplateRef
<
HTMLDivElement
>
(
'messageListRef'
)
const
conversationBoxRef
=
useTemplateRef
<
ScrollbarInstance
>
(
'conversationBoxRef'
)
const
toChatType
=
(
chatType
:
ConversationItem
[
'chat_type'
])
=>
Number
(
chatType
)
as
BooleanFlag
const
listLoading
=
ref
(
false
)
const
getCategoryFromRoute
=
()
=>
{
const
isReal
=
Number
(
route
.
query
.
isReal
)
if
(
isReal
===
1
)
return
BooleanFlag
.
NO
if
(
isReal
===
0
)
return
BooleanFlag
.
YES
return
null
}
const
filteredConversationList
=
computed
(()
=>
{
const
lowerKeyword
=
keyword
.
value
.
trim
().
toLowerCase
()
const
applyCategoryFromRoute
=
()
=>
{
const
routeCategory
=
getCategoryFromRoute
()
if
(
routeCategory
===
null
||
routeCategory
===
activeCategory
.
value
)
return
false
activeCategory
.
value
=
routeCategory
activeConversationId
.
value
=
0
messageDetailList
.
value
=
[]
return
true
}
return
conversationList
.
value
.
filter
((
item
)
=>
item
.
category
===
activeCategory
.
value
)
.
filter
((
item
)
=>
{
if
(
!
lowerKeyword
)
return
true
return
[
item
.
name
,
item
.
subtitle
,
item
.
sourceTitle
].
some
((
field
)
=>
field
.
toLowerCase
().
includes
(
lowerKeyword
),
)
})
.
sort
((
a
,
b
)
=>
{
if
(
!!
a
.
isPinned
!==
!!
b
.
isPinned
)
return
a
.
isPinned
?
-
1
:
1
return
(
dayjs
(
b
.
messages
.
at
(
-
1
)?.
createdAt
).
valueOf
()
-
dayjs
(
a
.
messages
.
at
(
-
1
)?.
createdAt
).
valueOf
()
)
const
refreshConversationList
=
async
(
searchText
=
keyword
.
value
.
trim
())
=>
{
listLoading
.
value
=
true
try
{
const
{
data
}
=
await
getMessageList
({
search
:
searchText
||
undefined
,
})
conversationList
.
value
=
data
?.
dialogList
||
[]
}
finally
{
listLoading
.
value
=
false
}
}
const
filteredConversationList
=
computed
(()
=>
{
const
list
=
conversationList
.
value
.
filter
(
(
item
)
=>
toChatType
(
item
.
chat_type
)
===
activeCategory
.
value
,
)
return
list
})
const
activeConversation
=
computed
(()
=>
{
...
...
@@ -667,37 +77,11 @@ const activeConversation = computed(() => {
return
current
||
filteredConversationList
.
value
[
0
]
||
null
})
const
groupedMessages
=
computed
(()
=>
{
if
(
!
activeConversation
.
value
)
return
[]
const
groups
:
Array
<
{
label
:
string
;
items
:
ChatMessage
[]
}
>
=
[]
activeConversation
.
value
.
messages
.
forEach
((
message
)
=>
{
const
label
=
dayjs
(
message
.
createdAt
).
format
(
'YYYY-MM-DD HH:mm'
)
const
lastGroup
=
groups
.
at
(
-
1
)
if
(
!
lastGroup
||
lastGroup
.
label
!==
label
)
{
groups
.
push
({
label
,
items
:
[
message
]
})
return
}
lastGroup
.
items
.
push
(
message
)
})
return
groups
})
const
currentUserName
=
computed
(()
=>
userInfo
.
value
?.
hiddenName
||
userInfo
.
value
?.
name
||
'我'
)
const
currentUserAvatar
=
computed
(
()
=>
userInfo
.
value
?.
hiddenAvatar
||
userInfo
.
value
?.
avatar
||
'https://api.dicebear.com/7.x/adventurer/svg?seed=Me'
,
)
const
conversationStats
=
computed
(()
=>
({
anonymous
:
conversationList
.
value
.
filter
((
item
)
=>
item
.
category
===
'anonymous'
).
length
,
realname
:
conversationList
.
value
.
filter
((
item
)
=>
item
.
category
===
'realname'
).
length
,
anonymous
:
conversationList
.
value
.
filter
((
item
)
=>
toChatType
(
item
.
chat_type
)
===
BooleanFlag
.
YES
)
.
length
,
realname
:
conversationList
.
value
.
filter
((
item
)
=>
toChatType
(
item
.
chat_type
)
===
BooleanFlag
.
NO
)
.
length
,
}))
const
canSend
=
computed
(()
=>
{
...
...
@@ -708,67 +92,74 @@ const canSend = computed(() => {
)
})
watch
(
filteredConversationList
,
(
list
)
=>
{
if
(
!
list
.
length
)
{
activeConversationId
.
value
=
0
return
}
watch
(
filteredConversationList
,
(
list
)
=>
{
if
(
!
list
.
length
)
{
activeConversationId
.
value
=
0
return
}
if
(
!
list
.
some
((
item
)
=>
item
.
id
===
activeConversationId
.
value
)
&&
list
[
0
])
{
activeConversationId
.
value
=
list
[
0
].
id
}
},
{
immediate
:
true
},
)
if
(
!
list
.
some
((
item
)
=>
item
.
id
===
activeConversationId
.
value
)
&&
list
[
0
])
{
activeConversationId
.
value
=
list
[
0
].
id
}
})
watch
(
()
=>
activeConversation
.
value
?.
id
,
async
(
id
)
=>
{
if
(
!
id
)
return
const
current
=
conversationList
.
value
.
find
((
item
)
=>
item
.
id
===
id
)
if
(
current
)
current
.
unread
=
0
if
(
current
)
current
.
un_read_count
=
0
if
(
!
messageDetailList
.
value
.
length
&&
current
)
{
await
selectConversation
(
current
)
}
await
nextTick
()
scrollToBottom
()
},
{
immediate
:
true
},
)
const
getConversationPreviewTime
=
(
conversation
:
ConversationItem
)
=>
{
const
latest
=
conversation
.
messages
.
at
(
-
1
)?.
createdAt
if
(
!
latest
)
return
'--'
const
now
=
dayjs
()
const
target
=
dayjs
(
latest
)
const
messageDetailList
=
ref
<
MessageDetailListItem
[]
>
([])
if
(
target
.
isSame
(
now
,
'day'
))
return
target
.
format
(
'HH:mm'
)
if
(
target
.
isSame
(
now
.
subtract
(
1
,
'day'
),
'day'
))
return
'昨天'
return
target
.
format
(
'MM-DD'
)
const
selectConversation
=
async
(
item
:
ConversationItem
)
=>
{
activeConversationId
.
value
=
item
.
id
const
{
data
}
=
await
getMessageDetailList
({
receiverId
:
item
.
other_user_id
,
dialogId
:
item
.
id
,
chatType
:
activeCategory
.
value
,
})
messageDetailList
.
value
=
(
data
||
[]).
map
((
message
)
=>
{
const
imageList
=
Array
.
isArray
(
message
.
image_list
)
?
message
.
image_list
:
message
.
images
?
message
.
images
.
split
(
','
).
filter
(
Boolean
)
:
[]
return
{
...
message
,
image_list
:
imageList
,
}
})
}
const
getMessageText
=
(
message
:
ChatMessage
)
=>
{
if
(
message
.
text
.
trim
())
return
message
.
text
if
(
message
.
images
.
length
)
return
'[图片]'
return
'[新消息]'
const
switchCategory
=
(
category
:
BooleanFlag
)
=>
{
if
(
activeCategory
.
value
===
category
)
return
activeCategory
.
value
=
category
activeConversationId
.
value
=
0
messageDetailList
.
value
=
[]
}
const
selectConversation
=
(
id
:
number
)
=>
{
a
ctiveConversationId
.
value
=
id
const
handleSearchBlur
=
async
(
)
=>
{
a
wait
refreshConversationList
()
}
const
switchCategory
=
(
category
:
MessageCategory
)
=>
{
activeCategory
.
value
=
category
const
handleSearchClear
=
()
=>
{
keyword
.
value
=
''
refreshConversationList
(
''
)
}
const
scrollToBottom
=
()
=>
{
const
wrapper
=
conversationBoxRef
.
value
console
.
log
(
wrapper
,
'wrapper'
)
if
(
!
wrapper
)
return
console
.
log
(
wrapper
.
$el
.
scrollHeight
,
'wrapper.$el.scrollHeight'
)
if
(
!
wrapper
?.
wrapRef
)
return
wrapper
.
scrollTo
({
top
:
wrapper
.
$el
.
scrollHeight
,
top
:
wrapper
.
wrapRef
.
scrollHeight
,
behavior
:
'smooth'
,
})
}
...
...
@@ -778,25 +169,18 @@ const handleSend = async () => {
sending
.
value
=
true
try
{
const
newMessage
:
ChatMessage
=
{
id
:
Date
.
now
(),
sender
:
'self'
,
text
:
draftText
.
value
.
trim
(),
images
:
draftImages
.
value
.
split
(
','
)
.
map
((
item
)
=>
item
.
trim
())
.
filter
(
Boolean
),
createdAt
:
dayjs
().
format
(
'YYYY-MM-DD HH:mm'
),
}
activeConversation
.
value
.
messages
.
push
(
newMessage
)
activeConversation
.
value
.
subtitle
=
getMessageText
(
newMessage
)
await
sendMessage
({
content
:
draftText
.
value
,
chatType
:
activeCategory
.
value
,
senderId
:
userInfo
.
value
.
userId
,
receiverId
:
activeConversation
.
value
.
other_user_id
,
images
:
draftImages
.
value
,
})
draftText
.
value
=
''
draftImages
.
value
=
''
await
selectConversation
(
activeConversation
.
value
)
await
nextTick
()
scrollToBottom
()
push
.
success
(
'私信已发送'
)
}
finally
{
sending
.
value
=
false
}
...
...
@@ -808,6 +192,11 @@ const handleInputKeydown = (event: KeyboardEvent) => {
handleSend
()
}
}
onActivated
(
async
()
=>
{
applyCategoryFromRoute
()
await
refreshConversationList
()
})
</
script
>
<
template
>
...
...
@@ -815,7 +204,7 @@ const handleInputKeydown = (event: KeyboardEvent) => {
<BackButton
/>
<section
class=
"flex h-[calc(100vh-6.25rem)] gap-4 overflow-hidden"
>
<aside
class=
"flex-1/4 rounded-lg border border-[#d8e5f2] bg-
[linear-gradient(180deg,#ffffff_0%,#f8fbff_100%)]
p-4 shadow-[0_0.5rem_1.5rem_rgba(134,167,207,0.12)]"
class=
"flex-1/4 rounded-lg border border-[#d8e5f2] bg-
white
p-4 shadow-[0_0.5rem_1.5rem_rgba(134,167,207,0.12)]"
>
<div
class=
"flex h-full flex-col"
>
<div>
...
...
@@ -842,7 +231,7 @@ const handleInputKeydown = (event: KeyboardEvent) => {
class=
"inline-flex h-6 min-w-6 items-center justify-center rounded-full bg-[#e8eff8] px-1.5 text-3 font-700"
>
{{
item
.
key
===
'anonymous'
item
.
key
===
BooleanFlag
.
YES
?
conversationStats
.
anonymous
:
conversationStats
.
realname
}}
...
...
@@ -856,6 +245,9 @@ const handleInputKeydown = (event: KeyboardEvent) => {
clearable
placeholder=
"搜索用户名"
class=
"mb-4"
:loading=
"listLoading"
@
blur=
"handleSearchBlur"
@
clear=
"handleSearchClear"
>
<template
#
prefix
>
<el-icon><IEpSearch
/></el-icon>
...
...
@@ -868,38 +260,45 @@ const handleInputKeydown = (event: KeyboardEvent) => {
<button
v-for=
"item in filteredConversationList"
:key=
"item.id"
class=
"w-full rounded-lg border p-3 text-left transition-all"
class=
"
cursor-pointer
w-full rounded-lg border p-3 text-left transition-all"
:class=
"
activeConversation?.id === item.id
? 'border-[#cbdcff] bg-[linear-gradient(180deg,#f6faff_0%,#edf5ff_100%)] shadow-[0_0.5rem_1.125rem_rgba(134,167,207,0.15)]'
: 'border-transparent bg-transparent hover:border-[#dde8f3] hover:bg-[#f9fbfe]'
"
@
click=
"selectConversation(item
.id
)"
@
click=
"selectConversation(item)"
>
<div
class=
"flex gap-3"
>
<div
class=
"relative shrink-0"
>
<el-avatar
:size=
"46"
:src=
"item.avatar"
/>
<el-avatar
:size=
"46"
:src=
"item.
other_user_
avatar"
/>
</div>
<div
class=
"min-w-0 flex-1"
>
<div
class=
"flex items-center justify-between gap-2"
>
<div
class=
"flex min-w-0 items-center gap-2"
>
<span
class=
"max-w-[6.875rem] truncate text-4 font-700 text-[#17305d]"
>
{{ item.name }}
{{ item.
other_user_
name }}
</span>
</div>
<span
class=
"shrink-0 text-3 text-[#8ca0b8]"
>
{{
getConversationPreviewTime(item)
}}
</span>
<span
class=
"shrink-0 text-3 text-[#8ca0b8]"
>
{{ item.last_time_str }}
</span>
</div>
<div
class=
"mt-2 flex min-w-0 items-center gap-2"
>
<span
class=
"inline-flex h-5 shrink-0 items-center rounded-full bg-[#fff3dc] px-2 text-3 text-[#9a6817]"
>
{{ item.sourceTypeLabel }}
{{ item.chat_type_desc }}
</span>
<span
class=
"truncate text-base text-[#7890aa]"
v-html=
"parseEmoji(item.last_message)"
></span>
<span
v-if=
"item.un_read_count > 0"
class=
"inline-flex h-5 min-w-5 shrink-0 items-center justify-center rounded-full bg-[#ffebeb] px-1.5 text-3 font-700 text-[#df3e3e]"
>
{{ item.un_read_count > 99 ? '99+' : item.un_read_count }}
</span>
<span
class=
"truncate text-3 text-[#7890aa]"
>
{{ item.sourceTitle }}
</span>
</div>
</div>
</div>
...
...
@@ -913,100 +312,97 @@ const handleInputKeydown = (event: KeyboardEvent) => {
</aside>
<main
class=
"overflow-hidden h-full flex-3/4 rounded-lg border border-[#d8e5f2] bg-
[linear-gradient(180deg,#ffffff_0%,#f9fbff_100%)]
shadow-[0_0.5rem_1.5rem_rgba(134,167,207,0.12)]"
class=
"overflow-hidden h-full flex-3/4 rounded-lg border border-[#d8e5f2] bg-
white
shadow-[0_0.5rem_1.5rem_rgba(134,167,207,0.12)]"
>
<
template
v-if=
"activeConversation"
>
<div
class=
"flex h-full min-h-0 flex-col"
>
<header
class=
"
flex flex-col gap-3 border-b border-[#e6eef6] bg-[linear-gradient(180deg,#f6faff_0%,#ffffff_100%)] px-5 py-4
md:flex-row md:items-center md:justify-between"
class=
"
relative z-1 flex flex-col gap-3 border-b border-[#e6eef6] bg-#fff px-5 py-4 shadow-[0_10px_16px_-16px_rgba(15,23,42,0.22)]
md:flex-row md:items-center md:justify-between"
>
<div
class=
"flex min-w-0 items-center gap-3"
>
<el-avatar
:size=
"50"
:src=
"activeConversation.avatar"
/>
<el-avatar
:size=
"50"
:src=
"activeConversation.
other_user_
avatar"
/>
<div
class=
"min-w-0"
>
<div
class=
"flex items-center gap-2"
>
<strong
class=
"text-4.5 font-700 text-[#18305d]"
>
{{
activeConversation
.
name
activeConversation
.
other_user_
name
}}
</strong>
<span
class=
"inline-flex h-6 items-center rounded-full px-2.5 text-3 font-700"
:class=
"
activeConversation.category === 'anonymous'
toChatType(activeConversation.chat_type) === BooleanFlag.NO
? 'bg-[#e7f7ed] text-[#2a7f61]'
: 'bg-[#e8f0ff] text-[#3762c7]'
"
>
{{
activeConversation
.
badge
}}
{{
activeConversation
.
chat_type_desc
}}
</span>
</div>
<p
class=
"mt-1 truncate text-[0.8125rem] leading-[1.375rem] text-[#7588a3]"
>
来源:
{{
activeConversation
.
sourceTitle
}}
·
{{
activeConversation
.
onlineStatus
}}
</p>
</div>
</div>
<div
class=
"text-[0.8125rem] text-[#6f84a4]"
>
当前对话按
<span
class=
"mx-1 font-700 text-[#4a6fd2]"
>
{{
activeConversation
.
badge
}}
</span>
<span
class=
"mx-1 font-700 text-[#4a6fd2]"
>
{{
activeConversation
.
chat_type_desc
}}
</span>
方式展示
</div>
</header>
<div
class=
"min-h-0 flex-1 overflow-hidden"
>
<el-scrollbar
ref=
"conversationBoxRef"
>
<div
class=
"bg-[linear-gradient(180deg,#fcfdff_0%,#f8fbff_100%)] px-5 py-5"
>
<div
v-for=
"group in groupedMessages"
:key=
"group.label"
class=
"pb-3"
>
<div
class=
"mb-4 text-center text-3 text-[#96a6bb]"
>
<span
class=
"rounded-full bg-[#eef4fb] px-3 py-1"
>
{{
group
.
label
}}
</span>
</div>
<div
v-show=
"!messageDetailList.length"
class=
"flex h-full items-center justify-center"
>
<el-empty
description=
"没有找到匹配的会话"
:image-size=
"90"
/>
</div>
<el-scrollbar
v-show=
"messageDetailList.length"
ref=
"conversationBoxRef"
>
<div
v-show=
"messageDetailList.length"
class=
"bg-[linear-gradient(180deg,#fcfdff_0%,#f8fbff_100%)] px-5 py-5"
>
<div
v-for=
"message in messageDetailList"
:key=
"message.message_id"
class=
"pb-3"
>
<div
v-for=
"message in group.items"
:key=
"message.id"
class=
"mb-4 flex items-start gap-3"
:class=
"
{ 'flex-row-reverse': message.
sender === 'self'
}"
:class=
"
{ 'flex-row-reverse': message.
is_self
}"
>
<el-avatar
:size=
"38"
:src=
"
message.sender === 'self' ? currentUserAvatar : activeConversation.avatar
"
class=
"shrink-0"
/>
<el-avatar
:size=
"38"
:src=
"message.sender_avatar"
class=
"shrink-0"
/>
<div
class=
"flex max-w-[min(72%,38.75rem)] flex-col gap-1"
:class=
"
{ 'items-end': message.
sender === 'self'
}"
:class=
"
{ 'items-end': message.
is_self
}"
>
<div
class=
"text-3 text-[#8c9bb0]"
>
{{
message
.
sender
===
'self'
?
currentUserName
:
activeConversation
.
name
}}
<div
class=
"flex items-center gap-2 text-3 text-[#8c9bb0]"
>
<span>
{{
message
.
is_self
?
userInfo
.
name
:
activeConversation
.
other_user_name
}}
</span>
<span
class=
"text-[#9eb0c6]"
>
{{
message
.
create_time_str
}}
</span>
</div>
<div
class=
"rounded-lg border px-
4 py-3
shadow-[0_0.125rem_0.5rem_rgba(39,74,120,0.04)]"
class=
"rounded-lg border px-
3 py-2
shadow-[0_0.125rem_0.5rem_rgba(39,74,120,0.04)]"
:class=
"
message.
sender === 'self'
message.
is_self
? 'rounded-tr-[0.375rem] border-[#d4e4ff] bg-[#dbeaff] text-[#1a3f73]'
: 'rounded-tl-[0.375rem] border-[#e7edf5] bg-white text-[#23364c]'
"
>
<p
v-if=
"message.text"
v-if=
"message.content"
v-html=
"parseEmoji(message.content)"
class=
"m-0 whitespace-pre-wrap break-words text-3.5 leading-[1.625rem]"
></p>
<div
v-if=
"message.image_list?.length"
class=
"mt-2 grid gap-2 sm:grid-cols-2"
>
{{
message
.
text
}}
</p>
<div
v-if=
"message.images.length"
class=
"mt-2 grid gap-2 sm:grid-cols-2"
>
<el-image
v-for=
"image in message.image
s
"
v-for=
"image in message.image
_list
"
:key=
"image"
:src=
"image"
fit=
"cover"
:preview-src-list=
"message.image
s
"
:preview-src-list=
"message.image
_list
"
preview-teleported
class=
"h-30 w-
full overflow-hidden rounded-[0.875rem]
"
class=
"h-30 w-
30 overflow-hidden rounded-lg
"
/>
</div>
</div>
...
...
@@ -1016,7 +412,9 @@ const handleInputKeydown = (event: KeyboardEvent) => {
</div>
</el-scrollbar>
</div>
<footer
class=
"border-t border-[#e6eef6] bg-white px-5 py-4 h-auto"
>
<footer
class=
"relative z-1 h-auto border-t border-[#e6eef6] bg-white px-5 py-4 shadow-[0_-10px_16px_-16px_rgba(15,23,42,0.22)]"
>
<div
class=
"mb-3 flex items-center justify-between gap-3 text-[0.8125rem] text-[#7690ac]"
>
...
...
@@ -1032,7 +430,7 @@ const handleInputKeydown = (event: KeyboardEvent) => {
v-model:inputText=
"draftText"
v-model:inputImg=
"draftImages"
:textAreaHeight=
"72"
placeholder=
"请输入回复内容
,回车发送
"
placeholder=
"请输入回复内容"
>
<template
#
submit
>
<button
...
...
src/views/userPage/index.vue
View file @
674789ad
...
...
@@ -99,7 +99,7 @@
<div
class=
"bg-white rounded-lg shadow-sm mb-4"
>
<div
v-for=
"item in menuUserItems"
:key=
"item.
path
"
:key=
"item.
tab
"
@
click=
"changeMenu(item.path)"
:class=
"[
'flex items-center gap-3 px-4 py-3 cursor-pointer transition-colors border-b border-gray-100 last:border-b-0',
...
...
@@ -202,7 +202,7 @@ const { userInfo } = storeToRefs(userStore)
// 左侧普通用户菜单
const
menuUserItems
=
[
{
path
:
'/selfMessage'
,
path
:
()
=>
`/selfMessage?isReal=
${
isReal
.
value
}
`
,
label
:
'我的私信'
,
icon
:
()
=>
<
IEpMessage
/>
,
tab
:
'我的私信'
,
...
...
@@ -302,8 +302,8 @@ const currentUserInfo = computed(() =>
},
)
const
changeMenu
=
(
key
:
string
)
=>
{
router
.
push
(
key
)
const
changeMenu
=
(
key
:
string
|
(()
=>
string
)
)
=>
{
router
.
push
(
typeof
key
===
'function'
?
key
()
:
key
)
}
const
handleEdit
=
()
=>
{
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment