Skip to content
GitLab
Projects
Groups
Snippets
/
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Sign in / Register
Toggle navigation
Menu
Open sidebar
水玉婷
ai-wechat
Commits
a4284330
Commit
a4284330
authored
Feb 05, 2026
by
水玉婷
Browse files
feat:添加复制功能及发送可以输入新的信息
parent
104f9290
Changes
2
Hide whitespace changes
Inline
Side-by-side
src/views/components/AiChat.vue
View file @
a4284330
...
@@ -50,7 +50,15 @@
...
@@ -50,7 +50,15 @@
/>
/>
</
template
>
</
template
>
<!-- 普通内容块 -->
<!-- 普通内容块 -->
<div
v-else
v-html=
"item.content"
class=
"message-inner-box"
></div>
<div
v-else
class=
"message-inner-box"
>
<div
v-html=
"item.content"
></div>
<!-- 复制按钮 -->
<div
v-if=
"msg.messageType === 'sent'"
class=
"message-actions"
>
<a-button
class=
"copy-button"
@
click=
"copyMessage(msg)"
title=
"复制"
>
<copy-outlined
/>
</a-button>
</div>
</div>
<!-- 思考过程框 -->
<!-- 思考过程框 -->
<div
v-if=
"item.hasThinkBox"
class=
"think-box-wrapper"
>
<div
v-if=
"item.hasThinkBox"
class=
"think-box-wrapper"
>
<div
:class=
"['think-box-toggle', item.thinkBoxExpanded ? 'expanded' : 'collapsed']"
@
click=
"toggleThinkBox(index, i)"
>
<div
:class=
"['think-box-toggle', item.thinkBoxExpanded ? 'expanded' : 'collapsed']"
@
click=
"toggleThinkBox(index, i)"
>
...
@@ -112,7 +120,7 @@
...
@@ -112,7 +120,7 @@
@
error=
"handleVoiceError"
class=
"voice-recognition-wrapper"
/>
@
error=
"handleVoiceError"
class=
"voice-recognition-wrapper"
/>
<textarea
ref=
"textarea"
v-model=
"messageText"
placeholder=
"输入消息..."
@
keypress=
"handleKeyPress"
<textarea
ref=
"textarea"
v-model=
"messageText"
placeholder=
"输入消息..."
@
keypress=
"handleKeyPress"
@
input=
"adjustTextareaHeight"
:disabled=
"loading"
></textarea>
@
input=
"adjustTextareaHeight"
></textarea>
<button
@
click=
"sendMessage()"
:disabled=
"loading"
class=
"send-button"
>
<button
@
click=
"sendMessage()"
:disabled=
"loading"
class=
"send-button"
>
<send-outlined
/>
<send-outlined
/>
</button>
</button>
...
@@ -125,7 +133,8 @@
...
@@ -125,7 +133,8 @@
import
{
ref
,
onMounted
,
onBeforeUnmount
,
nextTick
}
from
'
vue
'
;
import
{
ref
,
onMounted
,
onBeforeUnmount
,
nextTick
}
from
'
vue
'
;
import
dayjs
from
'
dayjs
'
;
import
dayjs
from
'
dayjs
'
;
import
{
markdownTemplate
,
isLastBlockMarkdown
,
getLastMarkdownBlockIndex
,
mergeMarkdownContent
}
from
'
./utils/markdownTemplate
'
;
import
{
markdownTemplate
,
isLastBlockMarkdown
,
getLastMarkdownBlockIndex
,
mergeMarkdownContent
}
from
'
./utils/markdownTemplate
'
;
import
{
SendOutlined
,
UserOutlined
,
ReloadOutlined
}
from
'
@ant-design/icons-vue
'
;
import
{
SendOutlined
,
UserOutlined
,
ReloadOutlined
,
CopyOutlined
}
from
'
@ant-design/icons-vue
'
;
import
{
message
as
antdMessage
}
from
'
ant-design-vue
'
;
import
defaultAvatar
from
'
@/assets/logo.png
'
;
import
defaultAvatar
from
'
@/assets/logo.png
'
;
import
rightIcon
from
'
@/assets/right.svg
'
import
rightIcon
from
'
@/assets/right.svg
'
import
thinkIcon
from
'
@/assets/think.svg
'
import
thinkIcon
from
'
@/assets/think.svg
'
...
@@ -511,6 +520,53 @@ const retrySendMessage = async (messageIndex: number) => {
...
@@ -511,6 +520,53 @@ const retrySendMessage = async (messageIndex: number) => {
});
});
};
};
// 复制消息
const
copyMessage
=
async
(
message
:
Message
)
=>
{
try
{
// 提取消息中的所有文本内容
let
textToCopy
=
''
;
if
(
message
.
contentBlocks
&&
message
.
contentBlocks
.
length
>
0
)
{
message
.
contentBlocks
.
forEach
(
block
=>
{
if
(
block
.
content
)
{
// 移除HTML标签,只保留纯文本
const
textContent
=
block
.
content
.
replace
(
/<
[^
>
]
*>/g
,
''
).
trim
();
if
(
textContent
)
{
textToCopy
+=
textContent
+
'
\n
'
;
}
}
});
}
// 如果没有内容,使用原始内容
if
(
!
textToCopy
.
trim
()
&&
message
.
originalContent
)
{
textToCopy
=
message
.
originalContent
;
}
if
(
textToCopy
.
trim
())
{
await
navigator
.
clipboard
.
writeText
(
textToCopy
.
trim
());
// 复制成功提示
antdMessage
.
success
(
'
消息已复制到剪贴板
'
);
}
else
{
antdMessage
.
warning
(
'
没有可复制的内容
'
);
}
}
catch
(
error
)
{
console
.
error
(
'
复制失败:
'
,
error
);
// 备用方案:使用传统的execCommand方法
try
{
const
textArea
=
document
.
createElement
(
'
textarea
'
);
textArea
.
value
=
message
.
originalContent
||
''
;
document
.
body
.
appendChild
(
textArea
);
textArea
.
select
();
document
.
execCommand
(
'
copy
'
);
document
.
body
.
removeChild
(
textArea
);
antdMessage
.
success
(
'
消息已复制到剪贴板
'
);
}
catch
(
fallbackError
)
{
console
.
error
(
'
备用复制方法也失败:
'
,
fallbackError
);
}
}
};
// 重新连接SSE
// 重新连接SSE
const
reconnectSSE
=
(
newDialogSessionId
:
string
)
=>
{
const
reconnectSSE
=
(
newDialogSessionId
:
string
)
=>
{
console
.
log
(
'
开始重连SSE,新的dialogSessionId:
'
,
newDialogSessionId
);
console
.
log
(
'
开始重连SSE,新的dialogSessionId:
'
,
newDialogSessionId
);
...
@@ -585,7 +641,9 @@ const toggleThinkBox = (messageIndex: number, blockIndex: number) => {
...
@@ -585,7 +641,9 @@ const toggleThinkBox = (messageIndex: number, blockIndex: number) => {
const
handleKeyPress
=
(
e
:
KeyboardEvent
)
=>
{
const
handleKeyPress
=
(
e
:
KeyboardEvent
)
=>
{
if
(
e
.
key
===
'
Enter
'
&&
!
e
.
shiftKey
)
{
if
(
e
.
key
===
'
Enter
'
&&
!
e
.
shiftKey
)
{
e
.
preventDefault
();
e
.
preventDefault
();
sendMessage
();
if
(
!
loading
.
value
){
sendMessage
();
}
}
}
};
};
...
...
src/views/components/style.less
View file @
a4284330
...
@@ -257,7 +257,6 @@ li {
...
@@ -257,7 +257,6 @@ li {
.message-content-wrapper {
.message-content-wrapper {
max-width: 100%;
max-width: 100%;
margin-top: 8px;
margin-top: 8px;
min-width: 150px;
// 当包含图表、表格或iframe时,宽度为100%
// 当包含图表、表格或iframe时,宽度为100%
&:has(.message-table),
&:has(.message-table),
...
@@ -295,6 +294,17 @@ li {
...
@@ -295,6 +294,17 @@ li {
.message.sent .message-content {
.message.sent .message-content {
background: #5B8AFE;
background: #5B8AFE;
color: @white;
color: @white;
.message-inner-box {
display: flex;
.copy-button {
margin-left: 8px;
width: 24px;
height: 24px;
color: #fff;
background: none;
border: none;
}
}
}
}
// 图表块样式
// 图表块样式
...
...
Write
Preview
Supports
Markdown
0%
Try again
or
attach a new 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