Skip to content
Toggle navigation
P
Projects
G
Groups
S
Snippets
Help
phsl
/
admin
This project
Loading...
Sign in
Toggle navigation
Go to a project
Project
Repository
Issues
0
Merge Requests
0
Pipelines
Wiki
Snippets
Members
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Commit
b116d823
authored
May 12, 2024
by
cherishsince
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
【增加】增加 chat 聊天列表、删除、复制到粘贴板
parent
73e85160
Hide whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
332 additions
and
165 deletions
+332
-165
src/api/ai/chat/message/index.ts
+41
-0
src/views/ai/chat/components/MessageList.vue
+276
-0
src/views/ai/chat/index.vue
+15
-165
No files found.
src/api/ai/chat/message/index.ts
0 → 100644
View file @
b116d823
import
request
from
'@/config/axios'
// 聊天VO
export
interface
ChatMessageVO
{
id
:
number
// 编号
conversationId
:
string
// 会话编号
type
:
string
// 消息类型
userId
:
string
// 用户编号
roleId
:
string
// 角色编号
model
:
number
// 模型标志
modelId
:
number
// 模型编号
content
:
number
// 聊天内容
tokens
:
number
// 消耗 Token 数量
createTime
:
Date
// 创建时间
}
export
interface
ChatMessageSendVO
{
conversationId
:
string
// 会话编号
content
:
number
// 聊天内容
}
// AI chat 聊天
export
const
ChatMessageApi
=
{
// 消息列表
messageList
:
async
(
conversationId
:
string
)
=>
{
return
await
request
.
get
({
url
:
`/ai/chat/message/list-by-conversation-id?conversationId=
${
conversationId
}
`
})
},
// 发送 send 消息
send
:
async
(
data
:
ChatMessageSendVO
)
=>
{
return
await
request
.
post
({
url
:
`/ai/chat/message/send`
,
data
})
},
// 发送 send 消息
delete
:
async
(
id
:
string
)
=>
{
return
await
request
.
delete
({
url
:
`/ai/chat/message/delete?id=
${
id
}
`
})
},
}
src/views/ai/chat/components/MessageList.vue
0 → 100644
View file @
b116d823
<
template
>
<div
class=
"message-container"
ref=
"messageContainer"
>
<div
class=
"chat-list"
v-for=
"(item, index) in list"
:key=
"index"
>
<!-- 靠左 message -->
<div
class=
"left-message message-item"
v-if=
"item.type === 'system'"
>
<div
class=
"avatar"
>
<el-avatar
src=
"https://cube.elemecdn.com/0/88/03b0d39583f48206768a7534e55bcpng.png"
/>
</div>
<div
class=
"message"
>
<div>
<el-text
class=
"time"
>
{{
formatDate
(
item
.
createTime
)
}}
</el-text>
</div>
<div
class=
"left-text-container"
>
<el-text
class=
"left-text"
>
{{
item
.
content
}}
</el-text>
</div>
<div
class=
"left-btns"
>
<div
class=
"btn-cus"
@
click=
"noCopy(item.content)"
>
<img
class=
"btn-image"
src=
"@/assets/ai/copy.svg"
/>
<el-text
class=
"btn-cus-text"
>
复制
</el-text>
</div>
<div
class=
"btn-cus"
style=
"margin-left: 20px;"
@
click=
"onDelete(item.id)"
>
<img
class=
"btn-image"
src=
"@/assets/ai/delete.svg"
style=
"height: 17px;"
/>
<el-text
class=
"btn-cus-text"
>
删除
</el-text>
</div>
</div>
</div>
</div>
<!-- 靠右 message -->
<div
class=
"right-message message-item"
v-if=
"item.type === 'user'"
>
<div
class=
"avatar"
>
<el-avatar
src=
"https://cube.elemecdn.com/0/88/03b0d39583f48206768a7534e55bcpng.png"
/>
</div>
<div
class=
"message"
>
<div>
<el-text
class=
"time"
>
{{
formatDate
(
item
.
createTime
)
}}
</el-text>
</div>
<div
class=
"right-text-container"
>
<el-text
class=
"right-text"
>
{{
item
.
content
}}
</el-text>
</div>
<div
class=
"right-btns"
>
<div
class=
"btn-cus"
@
click=
"noCopy(item.content)"
>
<img
class=
"btn-image"
src=
"@/assets/ai/copy.svg"
/>
<el-text
class=
"btn-cus-text"
>
复制
</el-text>
</div>
<div
class=
"btn-cus"
style=
"margin-left: 20px;"
@
click=
"onDelete(item.id)"
>
<img
class=
"btn-image"
src=
"@/assets/ai/delete.svg"
style=
"height: 17px;"
/>
<el-text
class=
"btn-cus-text"
>
删除
</el-text>
</div>
</div>
</div>
</div>
</div>
</div>
</
template
>
<
script
setup
lang=
"ts"
>
import
{
useClipboard
}
from
'@vueuse/core'
import
{
ChatMessageApi
,
ChatMessageVO
,
ChatMessageSendVO
}
from
'@/api/ai/chat/message'
import
{
formatDate
}
from
'@/utils/formatTime'
import
{
ApiKeyVO
}
from
"@/api/ai/model/apiKey"
;
// 初始化 copy 到粘贴板
const
{
copy
,
isSupported
}
=
useClipboard
();
/** chat message 列表 */
defineOptions
({
name
:
'chatMessageList'
})
const
list
=
ref
<
ChatMessageVO
[]
>
([])
// 列表的数据
// 对话id TODO @范 先写死
const
conversationId
=
'1781604279872581648'
const
content
=
'苹果是什么颜色?'
/** 查询列表 */
const
messageList
=
async
()
=>
{
try
{
// 获取列表数据
const
res
=
await
ChatMessageApi
.
messageList
(
conversationId
)
list
.
value
=
res
;
// 滚动到最下面
scrollToBottom
();
}
finally
{
}
}
// ref
const
messageContainer
:
any
=
ref
(
null
);
const
isScrolling
=
ref
(
false
)
//用于判断用户是否在滚动
/** send */
const
sendMessage
=
async
()
=>
{
try
{
const
requestParams
=
{
conversationId
,
content
,
}
const
messageSendVO
=
requestParams
as
unknown
as
ChatMessageSendVO
const
res
=
await
ChatMessageApi
.
send
(
messageSendVO
)
as
unknown
as
ChatMessageVO
console
.
log
(
'---'
,
res
.
content
)
}
finally
{
}
}
function
scrollToBottom
()
{
nextTick
(()
=>
{
//注意要使用nexttick以免获取不到dom
console
.
log
(
'isScrolling.value'
,
isScrolling
.
value
)
if
(
!
isScrolling
.
value
)
{
messageContainer
.
value
.
scrollTop
=
messageContainer
.
value
.
scrollHeight
-
messageContainer
.
value
.
offsetHeight
}
})
}
function
handleScroll
()
{
const
scrollContainer
=
messageContainer
.
value
const
scrollTop
=
scrollContainer
.
scrollTop
const
scrollHeight
=
scrollContainer
.
scrollHeight
const
offsetHeight
=
scrollContainer
.
offsetHeight
if
(
scrollTop
+
offsetHeight
<
scrollHeight
)
{
// 用户开始滚动并在最底部之上,取消保持在最底部的效果
isScrolling
.
value
=
true
}
else
{
// 用户停止滚动并滚动到最底部,开启保持到最底部的效果
isScrolling
.
value
=
false
}
}
function
noCopy
(
content
)
{
copy
(
content
)
ElMessage
({
message
:
'复制成功!'
,
type
:
'success'
,
})
}
const
onDelete
=
async
(
id
)
=>
{
// 删除 message
await
ChatMessageApi
.
delete
(
id
)
ElMessage
({
message
:
'删除成功!'
,
type
:
'success'
,
})
// 重新获取 message 列表
await
messageList
();
}
/** 初始化 **/
onMounted
(
async
()
=>
{
// 获取列表数据
messageList
();
// scrollToBottom();
// await nextTick
// 监听滚动事件,判断用户滚动状态
messageContainer
.
value
.
addEventListener
(
'scroll'
,
handleScroll
)
})
</
script
>
<
style
scoped
lang=
"scss"
>
.message-container
{
position
:
absolute
;
top
:
0
;
bottom
:
0
;
overflow-y
:
scroll
;
padding
:
0
15px
;
}
//
中间
.chat-list
{
display
:
flex
;
flex-direction
:
column
;
overflow-y
:
hidden
;
.message-item
{
margin-top
:
50px
;
}
.left-message
{
display
:
flex
;
flex-direction
:
row
;
}
.right-message
{
display
:
flex
;
flex-direction
:
row-reverse
;
justify-content
:
flex-start
;
}
.avatar
{
//
height
:
170px
;
//
width
:
170px
;
}
.message
{
display
:
flex
;
flex-direction
:
column
;
text-align
:
left
;
margin
:
0
15px
;
.time
{
text-align
:
left
;
line-height
:
30px
;
}
.left-text-container
{
display
:
flex
;
flex-direction
:
column
;
overflow-wrap
:
break-word
;
background-color
:
#e4e4e4
;
box-shadow
:
0
0
0
1px
#e4e4e4
;
border-radius
:
10px
;
padding
:
10px
10px
5px
10px
;
.left-text
{
color
:
#393939
;
}
}
.right-text-container
{
display
:
flex
;
flex-direction
:
column
;
overflow-wrap
:
break-word
;
background-color
:
#267fff
;
color
:
#FFF
;
box-shadow
:
0
0
0
1px
#267fff
;
border-radius
:
10px
;
padding
:
10px
;
.right-text
{
color
:
#FFF
;
}
}
.left-btns
,
.right-btns
{
display
:
flex
;
flex-direction
:
row
;
margin-top
:
8px
;
}
}
//
复制、删除按钮
.btn-cus
{
display
:
flex
;
background-color
:
transparent
;
align-items
:
center
;
.btn-image
{
height
:
20px
;
margin-right
:
5px
;
}
.btn-cus-text
{
color
:
#757575
;
}
}
.btn-cus
:hover
{
cursor
:
pointer
;
}
.btn-cus
:focus
{
background-color
:
#8c939d
;
}
}
</
style
>
src/views/ai/chat/index.vue
View file @
b116d823
...
@@ -81,73 +81,10 @@
...
@@ -81,73 +81,10 @@
</el-button>
</el-button>
</div>
</div>
</el-header>
</el-header>
<el-main>
<div
class=
"chat-list"
>
<!-- 靠左 message -->
<div
class=
"left-message message-item"
>
<div
class=
"avatar"
>
<el-avatar
src=
"https://cube.elemecdn.com/0/88/03b0d39583f48206768a7534e55bcpng.png"
/>
</div>
<div
class=
"message"
>
<div>
<el-text
class=
"time"
>
2024-05-10 22:38
</el-text>
</div>
<div
class=
"left-text-container"
>
<el-text
class=
"left-text"
>
如果您想获取某个网页或程序的截图,可以使用浏览器自带的截图功能,或者使用第三方截图工具,如Snipping
Tool、FastStone Capture等。如果您想将屏幕上的某个区域截取下来,可以使用键盘上的“Prt
Sc”键(或“Print Screen”键)来获取整个屏幕的截图,并将其粘贴到图像编辑软件中进行编辑和保存。
如果您需要更具体的帮助,例如如何使用特定的截图工具或如何编辑截图,请提供更多详细信息,我将尽力为您提供帮助。
</el-text>
</div>
<div
class=
"left-btns"
>
<div
class=
"btn-cus"
>
<img
class=
"btn-image"
src=
"@/assets/ai/copy.svg"
/>
<el-text
class=
"btn-cus-text"
>
复制
</el-text>
</div>
<div
class=
"btn-cus"
style=
"margin-left: 20px;"
>
<img
class=
"btn-image"
src=
"@/assets/ai/delete.svg"
style=
"height: 17px;"
/>
<el-text
class=
"btn-cus-text"
>
删除
</el-text>
</div>
</div>
</div>
</div>
<!-- 靠右 message -->
<div
class=
"right-message message-item"
>
<div
class=
"avatar"
>
<el-avatar
src=
"https://cube.elemecdn.com/0/88/03b0d39583f48206768a7534e55bcpng.png"
/>
</div>
<div
class=
"message"
>
<div>
<el-text
class=
"time"
>
2024-05-10 22:38
</el-text>
</div>
<div
class=
"right-text-container"
>
<el-text
class=
"right-text"
>
今天天气
</el-text>
</div>
<div
class=
"right-btns"
>
<div
class=
"btn-cus"
>
<img
class=
"btn-image"
src=
"@/assets/ai/copy.svg"
/>
<el-text
class=
"btn-cus-text"
>
复制
</el-text>
</div>
<div
class=
"btn-cus"
style=
"margin-left: 20px;"
>
<img
class=
"btn-image"
src=
"@/assets/ai/delete.svg"
style=
"height: 17px;"
/>
<el-text
class=
"btn-cus-text"
>
删除
</el-text>
</div>
</div>
</div>
</div>
</div>
<!-- main -->
<el-main
class=
"main-container"
>
<MessageList
/>
</el-main>
</el-main>
<el-footer
class=
"footer-container"
>
<el-footer
class=
"footer-container"
>
<textarea
placeholder=
"问我任何问题...(Shift+Enter 换行,按下 Enter 发送)"
<textarea
placeholder=
"问我任何问题...(Shift+Enter 换行,按下 Enter 发送)"
...
@@ -161,7 +98,10 @@
...
@@ -161,7 +98,10 @@
</el-container>
</el-container>
</el-container>
</el-container>
</template>
</template>
<
script
setup
lang=
"ts"
>
<
script
setup
lang=
"ts"
>
import
MessageList
from
'./components/MessageList.vue'
const
conversationList
=
[
const
conversationList
=
[
{
{
id
:
1
,
id
:
1
,
...
@@ -199,6 +139,10 @@ const deleteConversationTitle = (conversation) => {
...
@@ -199,6 +139,10 @@ const deleteConversationTitle = (conversation) => {
const
searchConversation
=
()
=>
{
const
searchConversation
=
()
=>
{
// TODO 芋艿:待实现
// TODO 芋艿:待实现
}
}
/** 初始化 **/
onMounted
(()
=>
{
})
</
script
>
</
script
>
<
style
lang=
"scss"
scoped
>
<
style
lang=
"scss"
scoped
>
...
@@ -331,107 +275,13 @@ const searchConversation = () => {
...
@@ -331,107 +275,13 @@ const searchConversation = () => {
}
}
}
}
//
中间
//
main
容器
.chat-list
{
.main-container
{
display
:
flex
;
margin
:
0
;
flex-direction
:
column
;
padding
:
0
;
position
:
relative
;
.message-item
{
margin-top
:
50px
;
}
.left-message
{
display
:
flex
;
flex-direction
:
row
;
}
.right-message
{
display
:
flex
;
flex-direction
:
row-reverse
;
justify-content
:
flex-start
;
}
.avatar
{
//
height
:
170px
;
//
width
:
170px
;
}
.message
{
display
:
flex
;
flex-direction
:
column
;
text-align
:
left
;
margin
:
0
15px
;
.time
{
text-align
:
left
;
line-height
:
30px
;
}
.left-text-container
{
display
:
flex
;
flex-direction
:
column
;
overflow-wrap
:
break-word
;
background-color
:
#e4e4e4
;
box-shadow
:
0
0
0
1px
#e4e4e4
;
border-radius
:
10px
;
padding
:
10px
10px
5px
10px
;
.left-text
{
color
:
#393939
;
}
}
.right-text-container
{
display
:
flex
;
flex-direction
:
column
;
overflow-wrap
:
break-word
;
background-color
:
#267fff
;
color
:
#FFF
;
box-shadow
:
0
0
0
1px
#267fff
;
border-radius
:
10px
;
padding
:
10px
;
.right-text
{
color
:
#FFF
;
}
}
.left-btns
,
.right-btns
{
display
:
flex
;
flex-direction
:
row
;
margin-top
:
8px
;
}
}
//
复制、删除按钮
.btn-cus
{
display
:
flex
;
background-color
:
transparent
;
align-items
:
center
;
.btn-image
{
height
:
20px
;
margin-right
:
5px
;
}
.btn-cus-text
{
color
:
#757575
;
}
}
.btn-cus
:hover
{
cursor
:
pointer
;
}
.btn-cus
:focus
{
background-color
:
#8c939d
;
}
}
}
//
底部
//
底部
.footer-container
{
.footer-container
{
display
:
flex
;
display
:
flex
;
...
...
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