Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Contribute to GitLab
Sign in
Toggle navigation
P
poc-fe
Project
Project
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
poc
poc-fe
Commits
2bd4cd34
Commit
2bd4cd34
authored
Dec 04, 2024
by
nick zheng
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
fix: 语音交互优化
parent
bd14bf2f
Hide whitespace changes
Inline
Side-by-side
Showing
17 changed files
with
425 additions
and
196 deletions
+425
-196
en.yaml
src/locales/langs/en.yaml
+8
-3
zh-cn.yaml
src/locales/langs/zh-cn.yaml
+8
-3
zh-hk.yaml
src/locales/langs/zh-hk.yaml
+8
-3
web-socket-ctr.ts
src/utils/web-socket-ctr.ts
+0
-1
agent-preview.vue
...g/components/agent-config/agent-preview/agent-preview.vue
+37
-23
footer-input.vue
...ts/agent-config/agent-preview/components/footer-input.vue
+86
-42
message-item.vue
...ts/agent-config/agent-preview/components/message-item.vue
+35
-5
message-list.vue
...ts/agent-config/agent-preview/components/message-list.vue
+5
-5
agent-role-setting.vue
...nt-config/agent-setting/components/agent-role-setting.vue
+18
-5
timbre-setting-modal.vue
...-config/agent-setting/components/timbre-setting-modal.vue
+1
-1
footer-input.vue
src/views/share/components/footer-input.vue
+83
-42
message-item.vue
src/views/share/components/message-item.vue
+50
-20
message-list.vue
src/views/share/components/message-list.vue
+5
-5
mobile-page-header.vue
src/views/share/components/mobile-page-header.vue
+1
-1
share-application-mobile.vue
src/views/share/share-application-mobile.vue
+36
-17
share-application-web.vue
src/views/share/share-application-web.vue
+36
-17
locales.d.ts
types/locales.d.ts
+8
-3
No files found.
src/locales/langs/en.yaml
View file @
2bd4cd34
...
@@ -100,6 +100,8 @@ common_module:
...
@@ -100,6 +100,8 @@ common_module:
voice_auto_play
:
'
Voice
auto
play'
voice_auto_play
:
'
Voice
auto
play'
start_playing
:
'
play'
start_playing
:
'
play'
stop_playing
:
'
stop'
stop_playing
:
'
stop'
unplayable
:
'
unplayable'
unplayable_tip
:
'
The
voice
setting
do
not
match
the
model
output
language'
response_error
:
'
Response
error'
response_error
:
'
Response
error'
agent_exception
:
'
Agent
exception,
please
try
again
later!'
agent_exception
:
'
Agent
exception,
please
try
again
later!'
equity
:
'
Equity'
equity
:
'
Equity'
...
@@ -116,6 +118,8 @@ common_module:
...
@@ -116,6 +118,8 @@ common_module:
cancel_associate_file_tip
:
'
No
longer
answer
around
this
file'
cancel_associate_file_tip
:
'
No
longer
answer
around
this
file'
upload_file_limit
:
'
Only
a
single
file
can
be
uploaded
in
PDF,
DOC,
DOCX,
MD,
TXT
format,
up
to
10MB'
upload_file_limit
:
'
Only
a
single
file
can
be
uploaded
in
PDF,
DOC,
DOCX,
MD,
TXT
format,
up
to
10MB'
overwrite_file_tip
:
'
The
newly
uploaded
file
overwrites
the
original
file,
whether
to
continue
uploading'
overwrite_file_tip
:
'
The
newly
uploaded
file
overwrites
the
original
file,
whether
to
continue
uploading'
stop_playing_and_then_operate
:
'
When
the
audio
is
playing,
stop
playing
and
then
perform
the
operation'
do_not_operate_until_the_reply_is_complete
:
'
Do
not
operate
until
the
reply
is
complete'
data_table_module
:
data_table_module
:
action
:
'
Controls'
action
:
'
Controls'
...
@@ -294,9 +298,10 @@ personal_space_module:
...
@@ -294,9 +298,10 @@ personal_space_module:
memory_fragment_delete_row_tip_content
:
'
After
data
deletion,
it
cannot
be
revoked.
Are
you
sure
you
want
to
delete
it?'
memory_fragment_delete_row_tip_content
:
'
After
data
deletion,
it
cannot
be
revoked.
Are
you
sure
you
want
to
delete
it?'
add_knowledge_successfully
:
'
Data
set
{0}
was
added
successfully'
add_knowledge_successfully
:
'
Data
set
{0}
was
added
successfully'
remove_knowledge_successfully
:
'
Data
set
{0}
was
removed
successfully'
remove_knowledge_successfully
:
'
Data
set
{0}
was
removed
successfully'
setting_timbre
:
'
Setting
timbre'
setting_voice
:
'
Setting
voice'
setting_timbre_message
:
'
You
can
set
the
language
and
timbre'
setting_voice_message
:
'
You
can
set
the
language
and
tone.
If
the
selected
language
is
inconsistent
with
the
model
language,
the
speech
cannot
be
played'
setting_timbre_desc
:
'
You
can
customize
the
voice
and
timbre
for
voice
broadcast,
and
you
can
set
only
one
at
a
time.'
setting_voice_desc
:
'
You
can
customize
the
voice
and
timbre
for
voice
broadcast,
and
you
can
set
only
one
at
a
time.'
currently_only_one_voice_can_be_set
:
'
Currently,
you
can
set
only
one
voice'
memory_variable_modal
:
memory_variable_modal
:
edit_memory_variable
:
'
Edit
memory
variable'
edit_memory_variable
:
'
Edit
memory
variable'
...
...
src/locales/langs/zh-cn.yaml
View file @
2bd4cd34
...
@@ -99,6 +99,8 @@ common_module:
...
@@ -99,6 +99,8 @@ common_module:
voice_auto_play
:
'
语音自动播放'
voice_auto_play
:
'
语音自动播放'
start_playing
:
'
开始播放'
start_playing
:
'
开始播放'
stop_playing
:
'
停止播放'
stop_playing
:
'
停止播放'
unplayable
:
'
不可播放'
unplayable_tip
:
'
语音设置与模型输出语言不匹配'
response_error
:
'
响应错误'
response_error
:
'
响应错误'
agent_exception
:
'
应用异常,请稍后重试!'
agent_exception
:
'
应用异常,请稍后重试!'
equity
:
'
权益'
equity
:
'
权益'
...
@@ -115,6 +117,8 @@ common_module:
...
@@ -115,6 +117,8 @@ common_module:
cancel_associate_file_tip
:
'
不再围绕这个文件回答'
cancel_associate_file_tip
:
'
不再围绕这个文件回答'
upload_file_limit
:
'
仅支持上传单个文件,支持PDF、DOC、DOCX、MD、TXT格式,最大10MB'
upload_file_limit
:
'
仅支持上传单个文件,支持PDF、DOC、DOCX、MD、TXT格式,最大10MB'
overwrite_file_tip
:
'
新上传的文件会覆盖原有文件,是否继续上传'
overwrite_file_tip
:
'
新上传的文件会覆盖原有文件,是否继续上传'
stop_playing_and_then_operate
:
'
音频播放中,请停止播放后再操作'
do_not_operate_until_the_reply_is_complete
:
'
回复完成后再操作'
data_table_module
:
data_table_module
:
action
:
'
操作'
action
:
'
操作'
...
@@ -292,9 +296,10 @@ personal_space_module:
...
@@ -292,9 +296,10 @@ personal_space_module:
memory_fragment_delete_row_tip_content
:
'
数据删除后不可撤销,确定要删除吗?'
memory_fragment_delete_row_tip_content
:
'
数据删除后不可撤销,确定要删除吗?'
add_knowledge_successfully
:
'
数据集
{0}
添加成功'
add_knowledge_successfully
:
'
数据集
{0}
添加成功'
remove_knowledge_successfully
:
'
数据集
{0}
移除成功'
remove_knowledge_successfully
:
'
数据集
{0}
移除成功'
setting_timbre
:
'
设置音色'
setting_voice
:
'
设置语音'
setting_timbre_message
:
'
你可以设置语言和音色'
setting_voice_message
:
'
你可以设置语言和音色,若所选语言与模型语言不一致,则无法播放语音'
setting_timbre_desc
:
'
您可自定义语音及音色,用于语音播报,且每次仅可设置一种。'
setting_voice_desc
:
'
您可自定义语音及音色,用于语音播报,且每次仅可设置一种。'
currently_only_one_voice_can_be_set
:
'
当前仅可设置一种声音'
memory_variable_modal
:
memory_variable_modal
:
edit_memory_variable
:
'
编辑记忆变量'
edit_memory_variable
:
'
编辑记忆变量'
...
...
src/locales/langs/zh-hk.yaml
View file @
2bd4cd34
...
@@ -99,6 +99,8 @@ common_module:
...
@@ -99,6 +99,8 @@ common_module:
voice_auto_play
:
'
語音自動播放'
voice_auto_play
:
'
語音自動播放'
start_playing
:
'
開始播放'
start_playing
:
'
開始播放'
stop_playing
:
'
停止播放'
stop_playing
:
'
停止播放'
unplayable
:
'
不可播放'
unplayable_tip
:
'
語音設置與模型輸出語言不匹配'
response_error
:
'
響應錯誤'
response_error
:
'
響應錯誤'
agent_exception
:
'
應用異常,請稍後重試!'
agent_exception
:
'
應用異常,請稍後重試!'
equity
:
'
权益'
equity
:
'
权益'
...
@@ -115,6 +117,8 @@ common_module:
...
@@ -115,6 +117,8 @@ common_module:
cancel_associate_file_tip
:
'
不再圍繞這個文件回答'
cancel_associate_file_tip
:
'
不再圍繞這個文件回答'
upload_file_limit
:
'
僅支持上傳單個文件,支持PDF、DOC、DOCX、MD、TXT格式,最大10MB'
upload_file_limit
:
'
僅支持上傳單個文件,支持PDF、DOC、DOCX、MD、TXT格式,最大10MB'
overwrite_file_tip
:
'
新上傳的文件會覆蓋原有文件,是否繼續上傳'
overwrite_file_tip
:
'
新上傳的文件會覆蓋原有文件,是否繼續上傳'
stop_playing_and_then_operate
:
'
音頻播放中,請停止播放後再操作'
do_not_operate_until_the_reply_is_complete
:
'
回覆完成後再操作'
data_table_module
:
data_table_module
:
action
:
'
操作'
action
:
'
操作'
...
@@ -292,9 +296,10 @@ personal_space_module:
...
@@ -292,9 +296,10 @@ personal_space_module:
memory_fragment_delete_row_tip_content
:
'
數據删除後不可撤銷,確定要删除嗎?'
memory_fragment_delete_row_tip_content
:
'
數據删除後不可撤銷,確定要删除嗎?'
add_knowledge_successfully
:
'
數據集
{0}
添加成功'
add_knowledge_successfully
:
'
數據集
{0}
添加成功'
remove_knowledge_successfully
:
'
數據集
{0}
移除成功'
remove_knowledge_successfully
:
'
數據集
{0}
移除成功'
setting_timbre
:
'
設置音色'
setting_voice
:
'
設置語音'
setting_timbre_message
:
'
你可以設置語言和音色'
setting_voice_message
:
'
你可以設置語言和音色,若所選語言與模型語言不一致,則無法播放語音'
setting_timbre_desc
:
'
您可自定義語音及音色,用於語音播報,且每次僅可設置一種。'
setting_voice_desc
:
'
您可自定義語音及音色,用於語音播報,且每次僅可設置一種。'
currently_only_one_voice_can_be_set
:
'
當前僅可設置一種聲音'
memory_variable_modal
:
memory_variable_modal
:
edit_memory_variable
:
'
編輯記憶變數'
edit_memory_variable
:
'
編輯記憶變數'
...
...
src/utils/web-socket-ctr.ts
View file @
2bd4cd34
...
@@ -39,7 +39,6 @@ export default class WebSocketCtr {
...
@@ -39,7 +39,6 @@ export default class WebSocketCtr {
this
.
socket
.
close
()
this
.
socket
.
close
()
this
.
socket
=
null
this
.
socket
=
null
}
}
window
.
$message
.
error
(
t
(
'common_module.agent_exception'
))
this
.
onMessageError
()
this
.
onMessageError
()
...
...
src/views/personal-space/personal-app-setting/components/agent-config/agent-preview/agent-preview.vue
View file @
2bd4cd34
...
@@ -5,6 +5,7 @@ import { useI18n } from 'vue-i18n'
...
@@ -5,6 +5,7 @@ import { useI18n } from 'vue-i18n'
import
{
useRouter
}
from
'vue-router'
import
{
useRouter
}
from
'vue-router'
import
{
Emitter
}
from
'mitt'
import
{
Emitter
}
from
'mitt'
import
{
Howl
}
from
'howler'
import
{
Howl
}
from
'howler'
import
{
ValueOf
}
from
'type-fest'
import
MessageList
from
'./components/message-list.vue'
import
MessageList
from
'./components/message-list.vue'
import
FooterInput
from
'./components/footer-input.vue'
import
FooterInput
from
'./components/footer-input.vue'
import
MemoryPreviewModal
from
'./components/memory-preview-modal.vue'
import
MemoryPreviewModal
from
'./components/memory-preview-modal.vue'
...
@@ -26,15 +27,15 @@ const emitter = inject<Emitter<MittEvents>>('emitter')
...
@@ -26,15 +27,15 @@ const emitter = inject<Emitter<MittEvents>>('emitter')
const
messageListRef
=
ref
<
InstanceType
<
typeof
MessageList
>
|
null
>
(
null
)
const
messageListRef
=
ref
<
InstanceType
<
typeof
MessageList
>
|
null
>
(
null
)
const
footerInputRef
=
ref
<
InstanceType
<
typeof
FooterInput
>
|
null
>
(
null
)
const
footerInputRef
=
ref
<
InstanceType
<
typeof
FooterInput
>
|
null
>
(
null
)
const
messageList
=
ref
<
ConversationMessageItem
[]
>
([]
)
const
messageList
=
ref
(
new
Map
<
string
,
ConversationMessageItem
>
()
)
const
continuousQuestionStatus
=
ref
<
'default'
|
'close'
>
(
personalAppConfigStore
.
commConfig
.
continuousQuestionStatus
)
const
continuousQuestionStatus
=
ref
<
'default'
|
'close'
>
(
personalAppConfigStore
.
commConfig
.
continuousQuestionStatus
)
const
continuousQuestionList
=
ref
<
string
[]
>
([])
const
continuousQuestionList
=
ref
<
string
[]
>
([])
const
isShowMemoryPreviewModal
=
ref
(
false
)
const
isShowMemoryPreviewModal
=
ref
(
false
)
const
selectedMemoryTabName
=
ref
(
'memoryVariable'
)
const
selectedMemoryTabName
=
ref
(
'memoryVariable'
)
const
answerAudioAutoPlay
ing
=
ref
(
personalAppConfigStore
.
voiceConfig
.
defaultOpen
===
'Y'
)
const
answerAudioAutoPlay
=
ref
(
personalAppConfigStore
.
voiceConfig
.
defaultOpen
===
'Y'
)
const
answerAudioPlaying
=
ref
(
false
)
// 语音播放中
const
currentPlayMessageItem
=
ref
<
ConversationMessageItem
|
null
>
(
null
)
const
currentPlayMessageItem
=
ref
<
ConversationMessageItem
|
null
>
(
null
)
const
currentPlayAudioFragmentSerialNo
=
ref
(
0
)
const
currentPlayAudioFragmentSerialNo
=
ref
(
0
)
const
currentSoundCtl
=
shallowRef
<
Howl
|
null
>
(
null
)
const
currentSoundCtl
=
shallowRef
<
Howl
|
null
>
(
null
)
...
@@ -48,7 +49,7 @@ onMounted(() => {
...
@@ -48,7 +49,7 @@ onMounted(() => {
emitter
?.
on
(
'resetAgent'
,
()
=>
{
emitter
?.
on
(
'resetAgent'
,
()
=>
{
handleAudioPause
()
handleAudioPause
()
footerInputRef
.
value
?.
blockMessageResponse
()
footerInputRef
.
value
?.
blockMessageResponse
()
messageList
.
value
=
[]
messageList
.
value
.
clear
()
})
})
})
})
...
@@ -56,23 +57,34 @@ onUnmounted(() => {
...
@@ -56,23 +57,34 @@ onUnmounted(() => {
emitter
?.
off
(
'resetAgent'
)
emitter
?.
off
(
'resetAgent'
)
handleAudioPause
()
handleAudioPause
()
footerInputRef
.
value
?.
blockMessageResponse
()
footerInputRef
.
value
?.
blockMessageResponse
()
messageList
.
value
=
[]
messageList
.
value
.
clear
()
})
})
function
handleAddMessageItem
(
messageItem
:
ConversationMessageItem
)
{
function
handleAddMessageItem
(
messageI
d
:
string
,
messageI
tem
:
ConversationMessageItem
)
{
messageList
.
value
.
push
(
messageItem
)
messageList
.
value
.
set
(
messageId
,
messageItem
)
}
}
function
handleUpdateSpecifyMessageItem
(
messageItemIndex
:
number
,
newObj
:
Partial
<
ConversationMessageItem
>
)
{
function
handleUpdateSpecifyMessageItem
(
messageId
:
string
,
newMessageItem
:
Partial
<
ConversationMessageItem
>
)
{
if
(
messageList
.
value
[
messageItemIndex
])
{
const
currentMessageItemInfo
=
messageList
.
value
.
get
(
messageId
)
Object
.
entries
(
newObj
).
forEach
(([
k
,
v
])
=>
{
;(
messageList
.
value
[
messageItemIndex
]
as
any
)[
k
as
keyof
typeof
newObj
]
=
v
if
(
currentMessageItemInfo
)
{
const
updatePropertyLength
=
Object
.
keys
(
newMessageItem
).
length
if
(
updatePropertyLength
>
4
)
{
messageList
.
value
.
set
(
messageId
,
Object
.
assign
({},
currentMessageItemInfo
,
newMessageItem
))
return
}
Object
.
entries
<
ValueOf
<
typeof
newMessageItem
>>
(
newMessageItem
).
forEach
(([
key
,
value
])
=>
{
if
(
Object
.
prototype
.
hasOwnProperty
.
call
(
currentMessageItemInfo
,
key
))
{
;(
currentMessageItemInfo
as
any
)[
key
as
keyof
ConversationMessageItem
]
=
value
}
})
})
}
}
}
}
function
handleDeleteLastMessageItem
()
{
function
handleDeleteLastMessageItem
(
messageId
:
string
)
{
messageList
.
value
.
pop
(
)
messageList
.
value
.
delete
(
messageId
)
}
}
function
handleUpdatePageScroll
()
{
function
handleUpdatePageScroll
()
{
...
@@ -88,7 +100,8 @@ function handleClearAllMessage() {
...
@@ -88,7 +100,8 @@ function handleClearAllMessage() {
.
then
(()
=>
{
.
then
(()
=>
{
handleAudioPause
()
handleAudioPause
()
footerInputRef
.
value
?.
blockMessageResponse
()
footerInputRef
.
value
?.
blockMessageResponse
()
messageList
.
value
=
[]
messageList
.
value
.
clear
()
answerAudioPlaying
.
value
=
false
window
.
$message
.
success
(
t
(
'common_module.clear_success_message'
))
window
.
$message
.
success
(
t
(
'common_module.clear_success_message'
))
})
})
}
}
...
@@ -135,6 +148,7 @@ function howlSoundFactory(url: string) {
...
@@ -135,6 +148,7 @@ function howlSoundFactory(url: string) {
preload
:
true
,
preload
:
true
,
autoplay
:
true
,
autoplay
:
true
,
onplay
:
()
=>
{
onplay
:
()
=>
{
answerAudioPlaying
.
value
=
true
currentSoundCtl
.
value
=
soundCtl
currentSoundCtl
.
value
=
soundCtl
if
(
currentPlayMessageItem
.
value
)
{
if
(
currentPlayMessageItem
.
value
)
{
...
@@ -152,6 +166,7 @@ function howlSoundFactory(url: string) {
...
@@ -152,6 +166,7 @@ function howlSoundFactory(url: string) {
currentPlayAudioFragmentSerialNo
.
value
>
currentPlayMessageItem
.
value
.
voiceFragmentUrlList
.
length
-
1
currentPlayAudioFragmentSerialNo
.
value
>
currentPlayMessageItem
.
value
.
voiceFragmentUrlList
.
length
-
1
)
{
)
{
currentPlayMessageItem
.
value
.
isVoicePlaying
=
false
currentPlayMessageItem
.
value
.
isVoicePlaying
=
false
answerAudioPlaying
.
value
=
false
}
}
let
audioFragmentUrl
=
currentPlayMessageItem
.
value
?.
voiceFragmentUrlList
[
currentPlayAudioFragmentSerialNo
.
value
]
let
audioFragmentUrl
=
currentPlayMessageItem
.
value
?.
voiceFragmentUrlList
[
currentPlayAudioFragmentSerialNo
.
value
]
...
@@ -205,9 +220,10 @@ function handleAudioPause(isClearMessageList = false) {
...
@@ -205,9 +220,10 @@ function handleAudioPause(isClearMessageList = false) {
}
}
currentPlayMessageItem
.
value
&&
(
currentPlayMessageItem
.
value
.
isVoicePlaying
=
false
)
currentPlayMessageItem
.
value
&&
(
currentPlayMessageItem
.
value
.
isVoicePlaying
=
false
)
answerAudioPlaying
.
value
=
false
if
(
isClearMessageList
)
{
if
(
isClearMessageList
)
{
messageList
.
value
=
[]
messageList
.
value
.
clear
()
footerInputRef
.
value
?.
blockMessageResponse
()
footerInputRef
.
value
?.
blockMessageResponse
()
}
}
}
}
...
@@ -231,11 +247,7 @@ function handleAudioPause(isClearMessageList = false) {
...
@@ -231,11 +247,7 @@ function handleAudioPause(isClearMessageList = false) {
</
template
>
</
template
>
<div
class=
"flex items-center gap-2.5"
>
<div
class=
"flex items-center gap-2.5"
>
<span>
{{ t('common_module.voice_auto_play') }}
</span>
<span>
{{ t('common_module.voice_auto_play') }}
</span>
<n-switch
<n-switch
v-model:value=
"answerAudioAutoPlay"
size=
"small"
@
update:value=
"handleUpdateAudioAutoPlaying"
>
v-model:value=
"answerAudioAutoPlaying"
size=
"small"
@
update:value=
"handleUpdateAudioAutoPlaying"
>
<
template
#
checked
>
{{
t
(
'common_module.open'
)
}}
</
template
>
<
template
#
checked
>
{{
t
(
'common_module.open'
)
}}
</
template
>
<
template
#
unchecked
>
{{
t
(
'common_module.close'
)
}}
</
template
>
<
template
#
unchecked
>
{{
t
(
'common_module.close'
)
}}
</
template
>
</n-switch>
</n-switch>
...
@@ -294,11 +306,11 @@ function handleAudioPause(isClearMessageList = false) {
...
@@ -294,11 +306,11 @@ function handleAudioPause(isClearMessageList = false) {
</div>
</div>
<div
class=
"flex w-full flex-1 overflow-hidden"
>
<div
class=
"flex w-full flex-1 overflow-hidden"
>
<div
v-show=
"messageList.
length
=== 0"
class=
"flex w-full"
>
<div
v-show=
"messageList.
size
=== 0"
class=
"flex w-full"
>
<Preamble
/>
<Preamble
/>
</div>
</div>
<div
v-show=
"messageList.
length
> 0"
class=
"w-full"
>
<div
v-show=
"messageList.
size
> 0"
class=
"w-full"
>
<MessageList
<MessageList
ref=
"messageListRef"
ref=
"messageListRef"
:message-list=
"messageList"
:message-list=
"messageList"
...
@@ -314,7 +326,8 @@ function handleAudioPause(isClearMessageList = false) {
...
@@ -314,7 +326,8 @@ function handleAudioPause(isClearMessageList = false) {
ref=
"footerInputRef"
ref=
"footerInputRef"
:continuous-question-status=
"continuousQuestionStatus"
:continuous-question-status=
"continuousQuestionStatus"
:message-list=
"messageList"
:message-list=
"messageList"
:answer-audio-auto-playing=
"answerAudioAutoPlaying"
:answer-audio-auto-play=
"answerAudioAutoPlay"
:answer-audio-playing=
"answerAudioPlaying"
@
add-message-item=
"handleAddMessageItem"
@
add-message-item=
"handleAddMessageItem"
@
update-specify-message-item=
"handleUpdateSpecifyMessageItem"
@
update-specify-message-item=
"handleUpdateSpecifyMessageItem"
@
delete-last-message-item=
"handleDeleteLastMessageItem"
@
delete-last-message-item=
"handleDeleteLastMessageItem"
...
@@ -323,6 +336,7 @@ function handleAudioPause(isClearMessageList = false) {
...
@@ -323,6 +336,7 @@ function handleAudioPause(isClearMessageList = false) {
@
create-continue-questions=
"handleCreateContinueQuestions"
@
create-continue-questions=
"handleCreateContinueQuestions"
@
update-continuous-question-status=
"handleUpdateContinueQuestionStatus"
@
update-continuous-question-status=
"handleUpdateContinueQuestionStatus"
@
audio-play=
"handleAudioPlay"
@
audio-play=
"handleAudioPlay"
@
audio-pause=
"handleAudioPause"
/>
/>
<MemoryPreviewModal
v-model=
"isShowMemoryPreviewModal"
:data=
"selectedMemoryTabName"
/>
<MemoryPreviewModal
v-model=
"isShowMemoryPreviewModal"
:data=
"selectedMemoryTabName"
/>
...
...
src/views/personal-space/personal-app-setting/components/agent-config/agent-preview/components/footer-input.vue
View file @
2bd4cd34
...
@@ -2,6 +2,7 @@
...
@@ -2,6 +2,7 @@
import
{
computed
,
inject
,
onMounted
,
onUnmounted
,
ref
,
useTemplateRef
,
watch
}
from
'vue'
import
{
computed
,
inject
,
onMounted
,
onUnmounted
,
ref
,
useTemplateRef
,
watch
}
from
'vue'
import
{
Emitter
}
from
'mitt'
import
{
Emitter
}
from
'mitt'
import
{
useI18n
}
from
'vue-i18n'
import
{
useI18n
}
from
'vue-i18n'
import
{
nanoid
}
from
'nanoid'
import
OverwriteMessageTipModal
from
'./overwrite-message-tip-modal.vue'
import
OverwriteMessageTipModal
from
'./overwrite-message-tip-modal.vue'
import
{
fetchCustomEventSource
}
from
'@/composables/useEventSource'
import
{
fetchCustomEventSource
}
from
'@/composables/useEventSource'
import
{
usePersonalAppConfigStore
}
from
'@/store/modules/personal-app-config'
import
{
usePersonalAppConfigStore
}
from
'@/store/modules/personal-app-config'
...
@@ -11,9 +12,10 @@ import { TEXTTOSPEECH_WS_URL } from '@/config/base-url'
...
@@ -11,9 +12,10 @@ import { TEXTTOSPEECH_WS_URL } from '@/config/base-url'
import
WebSocketCtr
from
'@/utils/web-socket-ctr'
import
WebSocketCtr
from
'@/utils/web-socket-ctr'
interface
Props
{
interface
Props
{
messageList
:
ConversationMessageItem
[]
messageList
:
Map
<
string
,
ConversationMessageItem
>
continuousQuestionStatus
:
'default'
|
'close'
continuousQuestionStatus
:
'default'
|
'close'
answerAudioAutoPlaying
:
boolean
answerAudioAutoPlay
:
boolean
answerAudioPlaying
:
boolean
}
}
const
{
t
}
=
useI18n
()
const
{
t
}
=
useI18n
()
...
@@ -21,14 +23,15 @@ const { t } = useI18n()
...
@@ -21,14 +23,15 @@ const { t } = useI18n()
const
props
=
defineProps
<
Props
>
()
const
props
=
defineProps
<
Props
>
()
const
emit
=
defineEmits
<
{
const
emit
=
defineEmits
<
{
addMessageItem
:
[
value
:
ConversationMessageItem
]
addMessageItem
:
[
messageId
:
string
,
value
:
ConversationMessageItem
]
updateSpecifyMessageItem
:
[
messageI
temIndex
:
number
,
newObj
:
Partial
<
ConversationMessageItem
>
]
updateSpecifyMessageItem
:
[
messageI
d
:
string
,
newObj
:
Partial
<
ConversationMessageItem
>
]
deleteLastMessageItem
:
[]
deleteLastMessageItem
:
[
messageId
:
string
]
updatePageScroll
:
[]
updatePageScroll
:
[]
clearAllMessage
:
[]
clearAllMessage
:
[]
createContinueQuestions
:
[
value
:
string
]
createContinueQuestions
:
[
value
:
string
]
updateContinuousQuestionStatus
:
[
value
:
'default'
|
'close'
]
updateContinuousQuestionStatus
:
[
value
:
'default'
|
'close'
]
audioPlay
:
[
messageItem
:
ConversationMessageItem
,
requestId
?:
string
]
audioPlay
:
[
messageItem
:
ConversationMessageItem
,
requestId
?:
string
]
audioPause
:
[]
}
>
()
}
>
()
const
personalAppConfigStore
=
usePersonalAppConfigStore
()
const
personalAppConfigStore
=
usePersonalAppConfigStore
()
...
@@ -45,6 +48,10 @@ const currentReplyContentSentenceExtractIndex = ref(0)
...
@@ -45,6 +48,10 @@ const currentReplyContentSentenceExtractIndex = ref(0)
const
sentenceFragmentSerialNo
=
ref
(
0
)
const
sentenceFragmentSerialNo
=
ref
(
0
)
const
sentenceExtractCheckEnabled
=
ref
(
false
)
const
sentenceExtractCheckEnabled
=
ref
(
false
)
const
assistantFullAnswerContent
=
ref
(
''
)
const
assistantFullAnswerContent
=
ref
(
''
)
const
currentAgentTimberId
=
ref
(
''
)
const
sentenceSpeechException
=
ref
(
false
)
const
messageAudioLoading
=
ref
(
false
)
const
currentLatestMessageItemKeyMap
=
ref
(
new
Map
<
'assistant'
|
'user'
,
string
>
())
let
controller
:
AbortController
|
null
=
null
let
controller
:
AbortController
|
null
=
null
...
@@ -57,7 +64,7 @@ const isCreateContinueQuestions = computed(() => {
...
@@ -57,7 +64,7 @@ const isCreateContinueQuestions = computed(() => {
})
})
const
isAllowClearMessage
=
computed
(()
=>
{
const
isAllowClearMessage
=
computed
(()
=>
{
return
props
.
messageList
.
length
>
0
return
props
.
messageList
.
size
>
0
})
})
const
isSendBtnDisabled
=
computed
(()
=>
{
const
isSendBtnDisabled
=
computed
(()
=>
{
...
@@ -121,7 +128,24 @@ function messageItemFactory(): ConversationMessageItem {
...
@@ -121,7 +128,24 @@ function messageItemFactory(): ConversationMessageItem {
}
}
function
handleMessageSend
()
{
function
handleMessageSend
()
{
if
(
!
inputMessageContent
.
value
.
trim
()
||
isAnswerResponseWait
.
value
||
isInputMessageDisabled
.
value
)
return
''
if
(
!
inputMessageContent
.
value
.
trim
()
||
isInputMessageDisabled
.
value
)
{
return
}
if
(
isAnswerResponseWait
.
value
||
messageAudioLoading
.
value
)
{
window
.
$message
.
warning
(
t
(
'common_module.dialogue_module.do_not_operate_until_the_reply_is_complete'
))
return
}
if
(
props
.
answerAudioPlaying
)
{
window
.
$message
.
warning
(
t
(
'common_module.dialogue_module.stop_playing_and_then_operate'
))
return
}
const
latestUserMessageKey
=
nanoid
()
const
latestAssistantMessageKey
=
nanoid
()
currentLatestMessageItemKeyMap
.
value
.
set
(
'user'
,
latestUserMessageKey
)
currentLatestMessageItemKeyMap
.
value
.
set
(
'assistant'
,
latestAssistantMessageKey
)
const
messages
:
{
const
messages
:
{
content
:
{
content
:
{
...
@@ -135,7 +159,7 @@ function handleMessageSend() {
...
@@ -135,7 +159,7 @@ function handleMessageSend() {
}[]
=
[]
}[]
=
[]
emit
(
'updateContinuousQuestionStatus'
,
personalAppConfigStore
.
commConfig
.
continuousQuestionStatus
)
emit
(
'updateContinuousQuestionStatus'
,
personalAppConfigStore
.
commConfig
.
continuousQuestionStatus
)
emit
(
'addMessageItem'
,
{
...
messageItemFactory
(),
textContent
:
inputMessageContent
.
value
})
emit
(
'addMessageItem'
,
latestUserMessageKey
,
{
...
messageItemFactory
(),
textContent
:
inputMessageContent
.
value
})
emit
(
'updatePageScroll'
)
emit
(
'updatePageScroll'
)
props
.
messageList
.
forEach
((
messageItem
)
=>
{
props
.
messageList
.
forEach
((
messageItem
)
=>
{
...
@@ -160,8 +184,11 @@ function handleMessageSend() {
...
@@ -160,8 +184,11 @@ function handleMessageSend() {
sentenceFragmentSerialNo
.
value
=
0
sentenceFragmentSerialNo
.
value
=
0
sentenceExtractCheckEnabled
.
value
=
false
sentenceExtractCheckEnabled
.
value
=
false
assistantFullAnswerContent
.
value
=
''
assistantFullAnswerContent
.
value
=
''
currentAgentTimberId
.
value
=
personalAppConfigStore
.
voiceConfig
.
timbreId
sentenceSpeechException
.
value
=
false
messageAudioLoading
.
value
=
false
emit
(
'addMessageItem'
,
{
emit
(
'addMessageItem'
,
latestAssistantMessageKey
,
{
...
messageItemFactory
(),
...
messageItemFactory
(),
role
:
'assistant'
,
role
:
'assistant'
,
isTextContentLoading
:
true
,
isTextContentLoading
:
true
,
...
@@ -170,7 +197,6 @@ function handleMessageSend() {
...
@@ -170,7 +197,6 @@ function handleMessageSend() {
})
})
emit
(
'updatePageScroll'
)
emit
(
'updatePageScroll'
)
const
currentMessageIndex
=
props
.
messageList
.
length
-
1
let
replyTextContent
=
''
let
replyTextContent
=
''
controller
=
new
AbortController
()
controller
=
new
AbortController
()
...
@@ -185,17 +211,12 @@ function handleMessageSend() {
...
@@ -185,17 +211,12 @@ function handleMessageSend() {
controller
,
controller
,
onMessage
:
(
data
:
any
)
=>
{
onMessage
:
(
data
:
any
)
=>
{
if
(
data
===
'[DONE]'
)
{
if
(
data
===
'[DONE]'
)
{
emit
(
'updateSpecifyMessageItem'
,
currentMessageIndex
,
{
emit
(
'updateSpecifyMessageItem'
,
latestAssistantMessageKey
,
{
isEmptyContent
:
!
replyTextContent
,
isEmptyContent
:
!
replyTextContent
,
isTextContentLoading
:
false
,
isTextContentLoading
:
false
,
isAnswerResponseLoading
:
false
,
isAnswerResponseLoading
:
false
,
})
})
if
(
!
props
.
answerAudioAutoPlaying
)
{
emit
(
'updateSpecifyMessageItem'
,
currentMessageIndex
,
{
isVoiceLoading
:
false
,
})
}
isCreateContinueQuestions
.
value
&&
emit
(
'createContinueQuestions'
,
replyTextContent
)
isCreateContinueQuestions
.
value
&&
emit
(
'createContinueQuestions'
,
replyTextContent
)
emit
(
'updatePageScroll'
)
emit
(
'updatePageScroll'
)
blockMessageResponse
()
blockMessageResponse
()
...
@@ -210,9 +231,13 @@ function handleMessageSend() {
...
@@ -210,9 +231,13 @@ function handleMessageSend() {
''
,
''
,
)
)
!
sentenceExtractCheckEnabled
.
value
&&
isEnableVoice
.
value
&&
sentenceExtract
()
if
(
!
sentenceExtractCheckEnabled
.
value
&&
isEnableVoice
.
value
)
{
sentenceExtract
(
latestAssistantMessageKey
)
sentenceExtractCheckEnabled
.
value
=
true
messageAudioLoading
.
value
=
true
}
emit
(
'updateSpecifyMessageItem'
,
currentMessageIndex
,
{
emit
(
'updateSpecifyMessageItem'
,
latestAssistantMessageKey
,
{
textContent
:
replyTextContent
,
textContent
:
replyTextContent
,
isTextContentLoading
:
false
,
isTextContentLoading
:
false
,
})
})
...
@@ -232,12 +257,13 @@ function handleMessageSend() {
...
@@ -232,12 +257,13 @@ function handleMessageSend() {
}
}
function
errorMessageResponse
()
{
function
errorMessageResponse
()
{
emit
(
'updateSpecifyMessageItem'
,
props
.
messageList
.
length
-
1
,
{
emit
(
'updateSpecifyMessageItem'
,
currentLatestMessageItemKeyMap
.
value
.
get
(
'assistant'
)
!
,
{
isTextContentLoading
:
false
,
isTextContentLoading
:
false
,
textContent
:
''
,
textContent
:
''
,
})
})
emit
(
'deleteLastMessageItem'
)
emit
(
'deleteLastMessageItem'
,
currentLatestMessageItemKeyMap
.
value
.
get
(
'assistant'
)
!
)
emit
(
'deleteLastMessageItem'
)
emit
(
'deleteLastMessageItem'
,
currentLatestMessageItemKeyMap
.
value
.
get
(
'assistant'
)
!
)
emit
(
'audioPause'
)
blockMessageResponse
()
blockMessageResponse
()
}
}
...
@@ -263,18 +289,16 @@ function handleSelectFile(cb: () => void) {
...
@@ -263,18 +289,16 @@ function handleSelectFile(cb: () => void) {
cb
()
cb
()
}
}
function
sentenceExtract
()
{
function
sentenceExtract
(
messageId
:
string
)
{
sentenceExtractCheckEnabled
.
value
=
true
const
symbolRegExp
=
/
[
。!?;.!?;
]
/g
const
symbolRegExp
=
/
[
。!?;.!?;
]
/g
let
sentenceDraft
=
assistantFullAnswerContent
.
value
let
sentenceDraft
=
assistantFullAnswerContent
.
value
.
replace
(
/
\s
/gi
,
''
)
.
replace
(
/
\s
{5,}
/gi
,
''
)
.
slice
(
currentReplyContentSentenceExtractIndex
.
value
)
.
slice
(
currentReplyContentSentenceExtractIndex
.
value
)
let
matchResult
=
symbolRegExp
.
exec
(
sentenceDraft
)
let
matchResult
=
symbolRegExp
.
exec
(
sentenceDraft
)
function
matchExtract
()
{
function
matchExtract
()
{
const
article
=
assistantFullAnswerContent
.
value
.
replace
(
/
\s
/gi
,
''
)
const
article
=
assistantFullAnswerContent
.
value
.
replace
(
/
\s
{5,}
/gi
,
''
)
if
(
matchResult
&&
matchResult
.
index
&&
matchResult
.
index
>
60
)
{
if
(
matchResult
&&
matchResult
.
index
&&
matchResult
.
index
>
60
)
{
sentenceDraft
=
article
.
slice
(
sentenceDraft
=
article
.
slice
(
...
@@ -284,7 +308,7 @@ function sentenceExtract() {
...
@@ -284,7 +308,7 @@ function sentenceExtract() {
currentReplyContentSentenceExtractIndex
.
value
+=
sentenceDraft
.
length
currentReplyContentSentenceExtractIndex
.
value
+=
sentenceDraft
.
length
ttsSocketSendText
(
sentenceDraft
,
sentenceFragmentSerialNo
.
value
)
ttsSocketSendText
(
sentenceDraft
,
sentenceFragmentSerialNo
.
value
,
messageId
)
sentenceFragmentSerialNo
.
value
+=
1
sentenceFragmentSerialNo
.
value
+=
1
if
(
article
.
length
-
currentReplyContentSentenceExtractIndex
.
value
>
60
)
{
if
(
article
.
length
-
currentReplyContentSentenceExtractIndex
.
value
>
60
)
{
...
@@ -292,19 +316,19 @@ function sentenceExtract() {
...
@@ -292,19 +316,19 @@ function sentenceExtract() {
matchResult
=
symbolRegExp
.
exec
(
sentenceDraft
)
matchResult
=
symbolRegExp
.
exec
(
sentenceDraft
)
matchExtract
()
matchExtract
()
}
else
{
}
else
{
setTimeout
(()
=>
sentenceExtract
(),
600
)
setTimeout
(()
=>
sentenceExtract
(
messageId
),
600
)
}
}
}
else
if
(
!
isAnswerResponseWait
.
value
)
{
}
else
if
(
!
isAnswerResponseWait
.
value
)
{
/* 延时避免最后回复内容没有更新全 */
/* 延时避免最后回复内容没有更新全 */
setTimeout
(()
=>
{
setTimeout
(()
=>
{
sentenceDraft
=
article
.
slice
(
currentReplyContentSentenceExtractIndex
.
value
)
sentenceDraft
=
article
.
slice
(
currentReplyContentSentenceExtractIndex
.
value
)
ttsSocketSendText
(
sentenceDraft
,
sentenceFragmentSerialNo
.
value
)
ttsSocketSendText
(
sentenceDraft
,
sentenceFragmentSerialNo
.
value
,
messageId
)
sentenceFragmentSerialNo
.
value
+=
1
sentenceFragmentSerialNo
.
value
+=
1
},
700
)
},
700
)
}
else
{
}
else
{
sentenceDraft
=
assistantFullAnswerContent
.
value
sentenceDraft
=
assistantFullAnswerContent
.
value
.
replace
(
/
\s
/gi
,
''
)
.
replace
(
/
\s
{5,}
/gi
,
''
)
.
slice
(
currentReplyContentSentenceExtractIndex
.
value
)
.
slice
(
currentReplyContentSentenceExtractIndex
.
value
)
matchResult
=
symbolRegExp
.
exec
(
sentenceDraft
)
matchResult
=
symbolRegExp
.
exec
(
sentenceDraft
)
...
@@ -313,38 +337,52 @@ function sentenceExtract() {
...
@@ -313,38 +337,52 @@ function sentenceExtract() {
}
}
if
(
matchResult
)
matchExtract
()
if
(
matchResult
)
matchExtract
()
else
setTimeout
(()
=>
sentenceExtract
(),
600
)
else
setTimeout
(()
=>
sentenceExtract
(
messageId
),
600
)
}
}
function
ttsSocketSendText
(
text
:
string
,
audioUrlSerialNo
:
number
)
{
function
ttsSocketSendText
(
text
:
string
,
audioUrlSerialNo
:
number
,
messageId
:
string
)
{
if
(
sentenceSpeechException
.
value
)
{
return
}
const
ttsSocketCtl
=
new
WebSocketCtr
(
TEXTTOSPEECH_WS_URL
)
const
ttsSocketCtl
=
new
WebSocketCtr
(
TEXTTOSPEECH_WS_URL
)
ttsSocketCtl
.
onMessage
=
(
data
:
{
audio
:
string
;
replyVoiceUrl
:
string
;
final
:
boolean
})
=>
{
ttsSocketCtl
.
onMessage
=
(
data
:
{
audio
:
string
;
replyVoiceUrl
:
string
;
final
:
boolean
})
=>
{
if
(
data
.
replyVoiceUrl
)
{
if
(
data
.
replyVoiceUrl
)
{
const
currentMessageIndex
=
props
.
messageList
.
length
-
1
if
(
props
.
messageList
.
get
(
messageId
)?.
voiceFragmentUrlList
)
{
const
voiceFragmentUrlListDraft
=
[...
props
.
messageList
.
get
(
messageId
)
!
.
voiceFragmentUrlList
]
if
(
props
.
messageList
[
currentMessageIndex
]?.
voiceFragmentUrlList
)
{
const
voiceFragmentUrlListDraft
=
[...
props
.
messageList
[
currentMessageIndex
].
voiceFragmentUrlList
]
voiceFragmentUrlListDraft
[
audioUrlSerialNo
]
=
data
.
replyVoiceUrl
voiceFragmentUrlListDraft
[
audioUrlSerialNo
]
=
data
.
replyVoiceUrl
messageAudioLoading
.
value
=
false
emit
(
'updateSpecifyMessageItem'
,
messageId
,
{
voiceFragmentUrlList
:
voiceFragmentUrlListDraft
})
emit
(
'updateSpecifyMessageItem'
,
currentMessageIndex
,
{
voiceFragmentUrlList
:
voiceFragmentUrlListDraft
})
if
(
props
.
answerAudioAutoPlay
&&
audioUrlSerialNo
===
0
&&
voiceFragmentUrlListDraft
[
audioUrlSerialNo
])
{
emit
(
'audioPlay'
,
props
.
messageList
.
get
(
messageId
)
!
)
}
if
(
props
.
answerAudioAutoPlaying
&&
audioUrlSerialNo
===
0
&&
voiceFragmentUrlListDraft
[
audioUrlSerialNo
]
)
{
if
(
!
props
.
answerAudioAutoPlay
)
{
emit
(
'
audioPlay'
,
props
.
messageList
[
currentMessageIndex
]
)
emit
(
'
updateSpecifyMessageItem'
,
messageId
,
{
isVoiceLoading
:
false
}
)
}
}
}
}
}
}
}
}
ttsSocketCtl
.
onMessageError
=
()
=>
{
emit
(
'updateSpecifyMessageItem'
,
messageId
,
{
isVoiceLoading
:
false
})
sentenceSpeechException
.
value
=
true
messageAudioLoading
.
value
=
false
window
.
$message
.
error
(
t
(
'common_module.unplayable_tip'
))
}
const
content
=
(
text
||
''
).
replace
(
/
\^\[[\d\\
[
\]
-
]
+
?\]\^
/g
,
''
)
const
content
=
(
text
||
''
).
replace
(
/
\^\[[\d\\
[
\]
-
]
+
?\]\^
/g
,
''
)
if
(
content
)
{
if
(
content
&&
currentAgentTimberId
.
value
)
{
ttsSocketCtl
.
connect
(()
=>
{
ttsSocketCtl
.
connect
(()
=>
{
ttsSocketCtl
.
send
({
ttsSocketCtl
.
send
({
codec
:
'wav'
,
codec
:
'wav'
,
sampleRate
:
16000
,
sampleRate
:
16000
,
speed
:
0
,
speed
:
0
,
voiceType
:
personalAppConfigStore
.
voiceConfig
.
timbreId
,
voiceType
:
currentAgentTimberId
.
value
,
volume
:
0
,
volume
:
0
,
content
,
content
,
})
})
...
@@ -417,7 +455,13 @@ defineExpose({
...
@@ -417,7 +455,13 @@ defineExpose({
<div
<div
class=
"bg-px-send-png absolute bottom-2 right-[20px] h-[24px] w-[24px]"
class=
"bg-px-send-png absolute bottom-2 right-[20px] h-[24px] w-[24px]"
:class=
"
:class=
"
isSendBtnDisabled || isAnswerResponseWait || isInputMessageDisabled ? 'opacity-60' : 'cursor-pointer'
isSendBtnDisabled ||
isAnswerResponseWait ||
isInputMessageDisabled ||
answerAudioPlaying ||
messageAudioLoading
? 'opacity-60'
: 'cursor-pointer'
"
"
@
click=
"handleMessageSend"
@
click=
"handleMessageSend"
/>
/>
...
...
src/views/personal-space/personal-app-setting/components/agent-config/agent-preview/components/message-item.vue
View file @
2bd4cd34
...
@@ -31,13 +31,27 @@ const assistantAvatar = computed(() => {
...
@@ -31,13 +31,27 @@ const assistantAvatar = computed(() => {
return
personalAppConfigStore
.
baseInfo
.
agentAvatar
return
personalAppConfigStore
.
baseInfo
.
agentAvatar
})
})
const
timbreEnabled
=
computed
(()
=>
{
return
!!
personalAppConfigStore
.
voiceConfig
.
timbreId
})
const
isShowAudioControl
=
computed
(()
=>
{
const
isShowAudioControl
=
computed
(()
=>
{
return
(
return
props
.
role
===
'assistant'
&&
!
props
.
messageItem
.
isVoiceLoading
props
.
role
===
'assistant'
&&
!
props
.
messageItem
.
isVoiceLoading
&&
!!
props
.
messageItem
.
voiceFragmentUrlList
.
length
})
)
const
isPlayableAudio
=
computed
(()
=>
{
return
isShowAudioControl
.
value
&&
!!
props
.
messageItem
.
voiceFragmentUrlList
.
length
})
const
isShowVoiceLoading
=
computed
(()
=>
{
return
props
.
role
===
'assistant'
&&
props
.
messageItem
.
isVoiceLoading
&&
timbreEnabled
.
value
})
})
function
handleAudioControl
()
{
function
handleAudioControl
()
{
if
(
!
isPlayableAudio
.
value
)
{
return
}
if
(
props
.
messageItem
.
isVoicePlaying
)
{
if
(
props
.
messageItem
.
isVoicePlaying
)
{
emit
(
'audioPause'
)
emit
(
'audioPause'
)
}
else
{
}
else
{
...
@@ -86,14 +100,30 @@ function handleAudioControl() {
...
@@ -86,14 +100,30 @@ function handleAudioControl() {
<div
<div
v-show=
"isShowAudioControl"
v-show=
"isShowAudioControl"
class=
"hover:text-theme-color text-font-color flex cursor-pointer items-center gap-0.5 hover:opacity-80"
class=
"text-font-color flex items-center gap-0.5"
:class=
"isPlayableAudio ? 'hover:text-theme-color cursor-pointer hover:opacity-80' : 'cursor-not-allowed'"
@
click=
"handleAudioControl"
@
click=
"handleAudioControl"
>
>
<i
v-if=
"!messageItem.isVoicePlaying"
class=
"iconfont icon-play text-[24px]"
/>
<i
v-if=
"!messageItem.isVoicePlaying"
class=
"iconfont icon-play text-[24px]"
/>
<div
v-else
class=
"mx-1.5 my-3 h-[12px] w-[12px] bg-[url(@/assets/images/playing.gif)] bg-[length:100%_100%]"
/>
<div
v-else
class=
"mx-1.5 my-3 h-[12px] w-[12px] bg-[url(@/assets/images/playing.gif)] bg-[length:100%_100%]"
/>
<span
class=
"text-[12px]"
:class=
"messageItem.isVoicePlaying ? 'text-theme-color' : ''"
>
<span
v-show=
"isPlayableAudio"
class=
"text-[12px]"
:class=
"messageItem.isVoicePlaying ? 'text-theme-color' : ''"
>
{{
messageItem
.
isVoicePlaying
?
t
(
'common_module.stop_playing'
)
:
t
(
'common_module.start_playing'
)
}}
{{
messageItem
.
isVoicePlaying
?
t
(
'common_module.stop_playing'
)
:
t
(
'common_module.start_playing'
)
}}
</span>
</span>
<n-popover
style=
"max-width: 310px"
>
<template
#
trigger
>
<span
v-show=
"!isPlayableAudio"
class=
"text-[12px]"
>
{{
t
(
'common_module.unplayable'
)
}}
</span>
</
template
>
{{ t('common_module.unplayable_tip') }}
</n-popover>
</div>
<div
v-if=
"isShowVoiceLoading"
class=
"py-3.5 pl-6"
>
<CustomLoading
/>
</div>
</div>
</div>
</div>
</div>
</div>
...
...
src/views/personal-space/personal-app-setting/components/agent-config/agent-preview/components/message-list.vue
View file @
2bd4cd34
...
@@ -5,7 +5,7 @@ import ContinueQuestion from './continue-question.vue'
...
@@ -5,7 +5,7 @@ import ContinueQuestion from './continue-question.vue'
import
{
useScroll
}
from
'@/composables/useScroll'
import
{
useScroll
}
from
'@/composables/useScroll'
interface
Props
{
interface
Props
{
messageList
:
ConversationMessageItem
[]
messageList
:
Map
<
string
,
ConversationMessageItem
>
continuousQuestionStatus
:
'default'
|
'close'
continuousQuestionStatus
:
'default'
|
'close'
continuousQuestionList
:
string
[]
continuousQuestionList
:
string
[]
}
}
...
@@ -22,8 +22,8 @@ const { scrollRef, scrollToBottom } = useScroll()
...
@@ -22,8 +22,8 @@ const { scrollRef, scrollToBottom } = useScroll()
const
isShowContinueQuestion
=
computed
(()
=>
{
const
isShowContinueQuestion
=
computed
(()
=>
{
return
(
return
(
props
.
continuousQuestionStatus
===
'default'
&&
props
.
continuousQuestionStatus
===
'default'
&&
props
.
messageList
.
length
>
1
&&
props
.
messageList
.
size
>
1
&&
!
props
.
messageList
[
props
.
messageList
.
length
-
1
].
isAnswerResponseLoading
!
Array
.
from
(
props
.
messageList
.
entries
()).
pop
()?.[
1
].
isAnswerResponseLoading
)
)
})
})
...
@@ -36,8 +36,8 @@ defineExpose({
...
@@ -36,8 +36,8 @@ defineExpose({
<main
ref=
"scrollRef"
class=
"h-full overflow-y-auto px-5"
>
<main
ref=
"scrollRef"
class=
"h-full overflow-y-auto px-5"
>
<div>
<div>
<MessageItem
<MessageItem
v-for=
"
messageItem
in messageList"
v-for=
"
[key, messageItem]
in messageList"
:key=
"
messageItem.timestamp
"
:key=
"
key
"
:role=
"messageItem.role"
:role=
"messageItem.role"
:message-item=
"messageItem"
:message-item=
"messageItem"
@
audio-play=
"() => $emit('audioPlay', messageItem)"
@
audio-play=
"() => $emit('audioPlay', messageItem)"
...
...
src/views/personal-space/personal-app-setting/components/agent-config/agent-setting/components/agent-role-setting.vue
View file @
2bd4cd34
...
@@ -31,6 +31,8 @@ const timberFullName = computed(() => {
...
@@ -31,6 +31,8 @@ const timberFullName = computed(() => {
return
`
${
timbreInfoDetail
.
value
?.
timbreInfo
?.[
0
]?.
timbreName
||
'--'
}(
$
{
t
(
currentLanguage
?.
label
||
'common_module.sound'
)})
`
return
`
${
timbreInfoDetail
.
value
?.
timbreInfo
?.[
0
]?.
timbreName
||
'--'
}(
$
{
t
(
currentLanguage
?.
label
||
'common_module.sound'
)})
`
})
})
const isHasTimbreId = computed(() => !!voiceConfig.value.timbreId)
onMounted(() => {
onMounted(() => {
voiceConfig.value.timbreId && handleGetTimberInfoDetail()
voiceConfig.value.timbreId && handleGetTimberInfoDetail()
})
})
...
@@ -49,6 +51,10 @@ function handleUpdateRoleConfigExpandedNames(expandedNames: string[]) {
...
@@ -49,6 +51,10 @@ function handleUpdateRoleConfigExpandedNames(expandedNames: string[]) {
}
}
function handleShowAssociatedTimbreModel() {
function handleShowAssociatedTimbreModel() {
if (voiceConfig.value.timbreId) {
return
}
isShowTimbreSettingModal.value = true
isShowTimbreSettingModal.value = true
timbreInfo = { language: 0, matchLang: '', timbreInfo: [] }
timbreInfo = { language: 0, matchLang: '', timbreInfo: [] }
}
}
...
@@ -84,7 +90,7 @@ function handleEditAssociatedTimbreModel() {
...
@@ -84,7 +90,7 @@ function handleEditAssociatedTimbreModel() {
<RightOne
theme=
"filled"
size=
"17"
fill=
"#333"
:stroke-width=
"3"
/>
<RightOne
theme=
"filled"
size=
"17"
fill=
"#333"
:stroke-width=
"3"
/>
</
template
>
</
template
>
<NCollapseItem
:title=
"t('common_module.
sound
')"
name=
"timbre"
class=
"my-[13px]!"
>
<NCollapseItem
:title=
"t('common_module.
voice
')"
name=
"timbre"
class=
"my-[13px]!"
>
<
template
#
header-extra
>
<
template
#
header-extra
>
<NTooltip
trigger=
"hover"
>
<NTooltip
trigger=
"hover"
>
<template
#
trigger
>
<template
#
trigger
>
...
@@ -92,16 +98,23 @@ function handleEditAssociatedTimbreModel() {
...
@@ -92,16 +98,23 @@ function handleEditAssociatedTimbreModel() {
theme=
"outline"
theme=
"outline"
size=
"22"
size=
"22"
:stroke-width=
"3"
:stroke-width=
"3"
class=
"text-theme-color cursor-pointer"
class=
"text-theme-color"
:class=
"isHasTimbreId ? 'cursor-not-allowed' : 'cursor-pointer'"
@
click=
"handleShowAssociatedTimbreModel"
@
click=
"handleShowAssociatedTimbreModel"
/>
/>
</
template
>
</
template
>
{{ t('personal_space_module.agent_module.agent_setting_module.agent_config_module.setting_timbre') }}
{{
isHasTimbreId
? t(
'personal_space_module.agent_module.agent_setting_module.agent_config_module.currently_only_one_voice_can_be_set',
)
: t('personal_space_module.agent_module.agent_setting_module.agent_config_module.setting_voice')
}}
</NTooltip>
</NTooltip>
</template>
</template>
<span
v-show=
"!voiceConfig.timbreId"
class=
"text-xs text-[#84868c]"
>
<span
v-show=
"!voiceConfig.timbreId"
class=
"text-xs text-[#84868c]"
>
{{ t('personal_space_module.agent_module.agent_setting_module.agent_config_module.setting_
timbr
e_desc') }}
{{ t('personal_space_module.agent_module.agent_setting_module.agent_config_module.setting_
voic
e_desc') }}
</span>
</span>
<div
class=
"flex flex-1 flex-wrap items-center gap-[12px] overflow-hidden"
>
<div
class=
"flex flex-1 flex-wrap items-center gap-[12px] overflow-hidden"
>
...
@@ -153,7 +166,7 @@ function handleEditAssociatedTimbreModel() {
...
@@ -153,7 +166,7 @@ function handleEditAssociatedTimbreModel() {
<TimbreSettingModal
<TimbreSettingModal
v-model:is-show-modal=
"isShowTimbreSettingModal"
v-model:is-show-modal=
"isShowTimbreSettingModal"
:btn-loading=
"false"
:btn-loading=
"false"
:modal-title=
"t('personal_space_module.agent_module.agent_setting_module.agent_config_module.setting_
timbr
e')"
:modal-title=
"t('personal_space_module.agent_module.agent_setting_module.agent_config_module.setting_
voic
e')"
:timbre-info=
"timbreInfo"
:timbre-info=
"timbreInfo"
@
confirm=
"handleUpdateTimbreId"
@
confirm=
"handleUpdateTimbreId"
/>
/>
...
...
src/views/personal-space/personal-app-setting/components/agent-config/agent-setting/components/timbre-setting-modal.vue
View file @
2bd4cd34
...
@@ -215,7 +215,7 @@ function handleAudioPause() {
...
@@ -215,7 +215,7 @@ function handleAudioPause() {
>
>
<template
#
content
>
<template
#
content
>
<div
class=
"text-gray-font-color mb-2"
>
<div
class=
"text-gray-font-color mb-2"
>
{{
t
(
'personal_space_module.agent_module.agent_setting_module.agent_config_module.setting_
timbr
e_message'
)
}}
{{
t
(
'personal_space_module.agent_module.agent_setting_module.agent_config_module.setting_
voic
e_message'
)
}}
</div>
</div>
<div
v-show=
"!requestDataLoading"
class=
"flex items-center gap-3"
>
<div
v-show=
"!requestDataLoading"
class=
"flex items-center gap-3"
>
...
...
src/views/share/components/footer-input.vue
View file @
2bd4cd34
...
@@ -2,6 +2,7 @@
...
@@ -2,6 +2,7 @@
import
{
computed
,
inject
,
onMounted
,
onUnmounted
,
ref
}
from
'vue'
import
{
computed
,
inject
,
onMounted
,
onUnmounted
,
ref
}
from
'vue'
import
{
Emitter
}
from
'mitt'
import
{
Emitter
}
from
'mitt'
import
{
useI18n
}
from
'vue-i18n'
import
{
useI18n
}
from
'vue-i18n'
import
{
nanoid
}
from
'nanoid'
import
{
fetchCustomEventSource
}
from
'@/composables/useEventSource'
import
{
fetchCustomEventSource
}
from
'@/composables/useEventSource'
import
{
useUserStore
}
from
'@/store/modules/user'
import
{
useUserStore
}
from
'@/store/modules/user'
import
{
UploadStatus
}
from
'@/enums/upload-status'
import
{
UploadStatus
}
from
'@/enums/upload-status'
...
@@ -13,11 +14,12 @@ import WebSocketCtr from '@/utils/web-socket-ctr'
...
@@ -13,11 +14,12 @@ import WebSocketCtr from '@/utils/web-socket-ctr'
interface
Props
{
interface
Props
{
agentId
:
string
agentId
:
string
dialogsId
:
string
dialogsId
:
string
messageList
:
ConversationMessageItem
[]
messageList
:
Map
<
string
,
ConversationMessageItem
>
continuousQuestionStatus
:
'default'
|
'close'
continuousQuestionStatus
:
'default'
|
'close'
isEnableDocumentParse
:
boolean
isEnableDocumentParse
:
boolean
isEnableVoice
:
boolean
isEnableVoice
:
boolean
answerAudioAutoPlaying
:
boolean
answerAudioAutoPlay
:
boolean
answerAudioPlaying
:
boolean
timbreId
:
string
timbreId
:
string
}
}
...
@@ -26,15 +28,16 @@ const { t } = useI18n()
...
@@ -26,15 +28,16 @@ const { t } = useI18n()
const
props
=
defineProps
<
Props
>
()
const
props
=
defineProps
<
Props
>
()
const
emit
=
defineEmits
<
{
const
emit
=
defineEmits
<
{
addMessageItem
:
[
value
:
ConversationMessageItem
]
addMessageItem
:
[
messageId
:
string
,
value
:
ConversationMessageItem
]
updateSpecifyMessageItem
:
[
messageI
temIndex
:
number
,
newObj
:
Partial
<
ConversationMessageItem
>
]
updateSpecifyMessageItem
:
[
messageI
d
:
string
,
newObj
:
Partial
<
ConversationMessageItem
>
]
deleteLastMessageItem
:
[]
deleteLastMessageItem
:
[
messageId
:
string
]
updatePageScroll
:
[]
updatePageScroll
:
[]
clearAllMessage
:
[]
clearAllMessage
:
[]
toLogin
:
[]
toLogin
:
[]
createContinueQuestions
:
[
value
:
string
]
createContinueQuestions
:
[
value
:
string
]
resetContinueQuestionList
:
[]
resetContinueQuestionList
:
[]
audioPlay
:
[
messageItem
:
ConversationMessageItem
,
requestId
?:
string
]
audioPlay
:
[
messageItem
:
ConversationMessageItem
,
requestId
?:
string
]
audioPause
:
[]
}
>
()
}
>
()
const
{
isMobile
}
=
useLayoutConfig
()
const
{
isMobile
}
=
useLayoutConfig
()
...
@@ -51,6 +54,9 @@ const currentReplyContentSentenceExtractIndex = ref(0)
...
@@ -51,6 +54,9 @@ const currentReplyContentSentenceExtractIndex = ref(0)
const
sentenceFragmentSerialNo
=
ref
(
0
)
const
sentenceFragmentSerialNo
=
ref
(
0
)
const
sentenceExtractCheckEnabled
=
ref
(
false
)
const
sentenceExtractCheckEnabled
=
ref
(
false
)
const
assistantFullAnswerContent
=
ref
(
''
)
const
assistantFullAnswerContent
=
ref
(
''
)
const
sentenceSpeechException
=
ref
(
false
)
const
messageAudioLoading
=
ref
(
false
)
const
currentLatestMessageItemKeyMap
=
ref
(
new
Map
<
'assistant'
|
'user'
,
string
>
())
let
controller
:
AbortController
|
null
=
null
let
controller
:
AbortController
|
null
=
null
...
@@ -59,7 +65,7 @@ const isLogin = computed(() => {
...
@@ -59,7 +65,7 @@ const isLogin = computed(() => {
})
})
const
isAllowClearMessage
=
computed
(()
=>
{
const
isAllowClearMessage
=
computed
(()
=>
{
return
props
.
messageList
.
length
>
0
return
props
.
messageList
.
size
>
0
})
})
const
isSendBtnDisabled
=
computed
(()
=>
{
const
isSendBtnDisabled
=
computed
(()
=>
{
...
@@ -123,13 +129,30 @@ function handleMessageSend() {
...
@@ -123,13 +129,30 @@ function handleMessageSend() {
return
return
}
}
if
(
!
inputMessageContent
.
value
.
trim
()
||
isAnswerResponseWait
.
value
||
isInputMessageDisabled
.
value
)
return
''
if
(
!
inputMessageContent
.
value
.
trim
()
||
isInputMessageDisabled
.
value
)
{
return
}
if
(
isAnswerResponseWait
.
value
||
messageAudioLoading
.
value
)
{
window
.
$message
.
warning
(
t
(
'common_module.dialogue_module.do_not_operate_until_the_reply_is_complete'
))
return
}
if
(
props
.
answerAudioPlaying
)
{
window
.
$message
.
warning
(
t
(
'common_module.dialogue_module.stop_playing_and_then_operate'
))
return
}
const
latestUserMessageKey
=
nanoid
()
const
latestAssistantMessageKey
=
nanoid
()
currentLatestMessageItemKeyMap
.
value
.
set
(
'user'
,
latestUserMessageKey
)
currentLatestMessageItemKeyMap
.
value
.
set
(
'assistant'
,
latestAssistantMessageKey
)
emit
(
'resetContinueQuestionList'
)
emit
(
'resetContinueQuestionList'
)
emit
(
'addMessageItem'
,
{
...
messageItemFactory
(),
textContent
:
inputMessageContent
.
value
})
emit
(
'addMessageItem'
,
latestUserMessageKey
,
{
...
messageItemFactory
(),
textContent
:
inputMessageContent
.
value
})
emit
(
'updatePageScroll'
)
emit
(
'updatePageScroll'
)
emit
(
'addMessageItem'
,
{
emit
(
'addMessageItem'
,
latestAssistantMessageKey
,
{
...
messageItemFactory
(),
...
messageItemFactory
(),
role
:
'assistant'
,
role
:
'assistant'
,
isTextContentLoading
:
true
,
isTextContentLoading
:
true
,
...
@@ -139,7 +162,7 @@ function handleMessageSend() {
...
@@ -139,7 +162,7 @@ function handleMessageSend() {
emit
(
'updatePageScroll'
)
emit
(
'updatePageScroll'
)
const
input
=
inputMessageContent
.
value
const
input
=
inputMessageContent
.
value
const
currentMessageIndex
=
props
.
messageList
.
length
-
1
let
replyTextContent
=
''
let
replyTextContent
=
''
isAnswerResponseWait
.
value
=
true
isAnswerResponseWait
.
value
=
true
inputMessageContent
.
value
=
''
inputMessageContent
.
value
=
''
...
@@ -147,6 +170,8 @@ function handleMessageSend() {
...
@@ -147,6 +170,8 @@ function handleMessageSend() {
sentenceFragmentSerialNo
.
value
=
0
sentenceFragmentSerialNo
.
value
=
0
sentenceExtractCheckEnabled
.
value
=
false
sentenceExtractCheckEnabled
.
value
=
false
assistantFullAnswerContent
.
value
=
''
assistantFullAnswerContent
.
value
=
''
sentenceSpeechException
.
value
=
false
messageAudioLoading
.
value
=
false
controller
=
new
AbortController
()
controller
=
new
AbortController
()
...
@@ -161,18 +186,12 @@ function handleMessageSend() {
...
@@ -161,18 +186,12 @@ function handleMessageSend() {
controller
,
controller
,
onMessage
:
(
data
:
any
)
=>
{
onMessage
:
(
data
:
any
)
=>
{
if
(
data
===
'[DONE]'
)
{
if
(
data
===
'[DONE]'
)
{
emit
(
'updateSpecifyMessageItem'
,
currentMessageIndex
,
{
emit
(
'updateSpecifyMessageItem'
,
latestAssistantMessageKey
,
{
isEmptyContent
:
!
replyTextContent
,
isEmptyContent
:
!
replyTextContent
,
isTextContentLoading
:
false
,
isTextContentLoading
:
false
,
isAnswerResponseLoading
:
false
,
isAnswerResponseLoading
:
false
,
})
})
if
(
!
props
.
answerAudioAutoPlaying
)
{
emit
(
'updateSpecifyMessageItem'
,
currentMessageIndex
,
{
isVoiceLoading
:
false
,
})
}
isCreateContinueQuestions
.
value
&&
emit
(
'createContinueQuestions'
,
replyTextContent
)
isCreateContinueQuestions
.
value
&&
emit
(
'createContinueQuestions'
,
replyTextContent
)
emit
(
'updatePageScroll'
)
emit
(
'updatePageScroll'
)
blockMessageResponse
()
blockMessageResponse
()
...
@@ -187,9 +206,13 @@ function handleMessageSend() {
...
@@ -187,9 +206,13 @@ function handleMessageSend() {
''
,
''
,
)
)
!
sentenceExtractCheckEnabled
.
value
&&
props
.
isEnableVoice
&&
sentenceExtract
()
if
(
!
sentenceExtractCheckEnabled
.
value
&&
props
.
isEnableVoice
)
{
sentenceExtract
(
latestAssistantMessageKey
)
sentenceExtractCheckEnabled
.
value
=
true
messageAudioLoading
.
value
=
true
}
emit
(
'updateSpecifyMessageItem'
,
currentMessageIndex
,
{
emit
(
'updateSpecifyMessageItem'
,
latestAssistantMessageKey
,
{
textContent
:
replyTextContent
,
textContent
:
replyTextContent
,
isTextContentLoading
:
false
,
isTextContentLoading
:
false
,
})
})
...
@@ -209,12 +232,13 @@ function handleMessageSend() {
...
@@ -209,12 +232,13 @@ function handleMessageSend() {
}
}
function
errorMessageResponse
()
{
function
errorMessageResponse
()
{
emit
(
'updateSpecifyMessageItem'
,
props
.
messageList
.
length
-
1
,
{
emit
(
'updateSpecifyMessageItem'
,
currentLatestMessageItemKeyMap
.
value
.
get
(
'assistant'
)
!
,
{
isTextContentLoading
:
false
,
isTextContentLoading
:
false
,
textContent
:
''
,
textContent
:
''
,
})
})
emit
(
'deleteLastMessageItem'
)
emit
(
'deleteLastMessageItem'
,
currentLatestMessageItemKeyMap
.
value
.
get
(
'assistant'
)
!
)
emit
(
'deleteLastMessageItem'
)
emit
(
'deleteLastMessageItem'
,
currentLatestMessageItemKeyMap
.
value
.
get
(
'assistant'
)
!
)
emit
(
'audioPause'
)
blockMessageResponse
()
blockMessageResponse
()
}
}
...
@@ -244,18 +268,16 @@ function handleSelectFile(cb: () => void) {
...
@@ -244,18 +268,16 @@ function handleSelectFile(cb: () => void) {
cb
()
cb
()
}
}
function
sentenceExtract
()
{
function
sentenceExtract
(
messageId
:
string
)
{
sentenceExtractCheckEnabled
.
value
=
true
const
symbolRegExp
=
/
[
。!?;.!?;
]
/g
const
symbolRegExp
=
/
[
。!?;.!?;
]
/g
let
sentenceDraft
=
assistantFullAnswerContent
.
value
let
sentenceDraft
=
assistantFullAnswerContent
.
value
.
replace
(
/
\s
/gi
,
''
)
.
replace
(
/
\s
{5,}
/gi
,
''
)
.
slice
(
currentReplyContentSentenceExtractIndex
.
value
)
.
slice
(
currentReplyContentSentenceExtractIndex
.
value
)
let
matchResult
=
symbolRegExp
.
exec
(
sentenceDraft
)
let
matchResult
=
symbolRegExp
.
exec
(
sentenceDraft
)
function
matchExtract
()
{
function
matchExtract
()
{
const
article
=
assistantFullAnswerContent
.
value
.
replace
(
/
\s
/gi
,
''
)
const
article
=
assistantFullAnswerContent
.
value
.
replace
(
/
\s
{5,}
/gi
,
''
)
if
(
matchResult
&&
matchResult
.
index
&&
matchResult
.
index
>
60
)
{
if
(
matchResult
&&
matchResult
.
index
&&
matchResult
.
index
>
60
)
{
sentenceDraft
=
article
.
slice
(
sentenceDraft
=
article
.
slice
(
...
@@ -265,7 +287,7 @@ function sentenceExtract() {
...
@@ -265,7 +287,7 @@ function sentenceExtract() {
currentReplyContentSentenceExtractIndex
.
value
+=
sentenceDraft
.
length
currentReplyContentSentenceExtractIndex
.
value
+=
sentenceDraft
.
length
ttsSocketSendText
(
sentenceDraft
,
sentenceFragmentSerialNo
.
value
)
ttsSocketSendText
(
sentenceDraft
,
sentenceFragmentSerialNo
.
value
,
messageId
)
sentenceFragmentSerialNo
.
value
+=
1
sentenceFragmentSerialNo
.
value
+=
1
if
(
article
.
length
-
currentReplyContentSentenceExtractIndex
.
value
>
60
)
{
if
(
article
.
length
-
currentReplyContentSentenceExtractIndex
.
value
>
60
)
{
...
@@ -273,19 +295,19 @@ function sentenceExtract() {
...
@@ -273,19 +295,19 @@ function sentenceExtract() {
matchResult
=
symbolRegExp
.
exec
(
sentenceDraft
)
matchResult
=
symbolRegExp
.
exec
(
sentenceDraft
)
matchExtract
()
matchExtract
()
}
else
{
}
else
{
setTimeout
(()
=>
sentenceExtract
(),
600
)
setTimeout
(()
=>
sentenceExtract
(
messageId
),
600
)
}
}
}
else
if
(
!
isAnswerResponseWait
.
value
)
{
}
else
if
(
!
isAnswerResponseWait
.
value
)
{
/* 延时避免最后回复内容没有更新全 */
/* 延时避免最后回复内容没有更新全 */
setTimeout
(()
=>
{
setTimeout
(()
=>
{
sentenceDraft
=
article
.
slice
(
currentReplyContentSentenceExtractIndex
.
value
)
sentenceDraft
=
article
.
slice
(
currentReplyContentSentenceExtractIndex
.
value
)
ttsSocketSendText
(
sentenceDraft
,
sentenceFragmentSerialNo
.
value
)
ttsSocketSendText
(
sentenceDraft
,
sentenceFragmentSerialNo
.
value
,
messageId
)
sentenceFragmentSerialNo
.
value
+=
1
sentenceFragmentSerialNo
.
value
+=
1
},
700
)
},
700
)
}
else
{
}
else
{
sentenceDraft
=
assistantFullAnswerContent
.
value
sentenceDraft
=
assistantFullAnswerContent
.
value
.
replace
(
/
\s
/gi
,
''
)
.
replace
(
/
\s
{5,}
/gi
,
''
)
.
slice
(
currentReplyContentSentenceExtractIndex
.
value
)
.
slice
(
currentReplyContentSentenceExtractIndex
.
value
)
matchResult
=
symbolRegExp
.
exec
(
sentenceDraft
)
matchResult
=
symbolRegExp
.
exec
(
sentenceDraft
)
...
@@ -294,32 +316,46 @@ function sentenceExtract() {
...
@@ -294,32 +316,46 @@ function sentenceExtract() {
}
}
if
(
matchResult
)
matchExtract
()
if
(
matchResult
)
matchExtract
()
else
setTimeout
(()
=>
sentenceExtract
(),
600
)
else
setTimeout
(()
=>
sentenceExtract
(
messageId
),
600
)
}
}
function
ttsSocketSendText
(
text
:
string
,
audioUrlSerialNo
:
number
)
{
function
ttsSocketSendText
(
text
:
string
,
audioUrlSerialNo
:
number
,
messageId
:
string
)
{
if
(
sentenceSpeechException
.
value
)
{
return
}
const
ttsSocketCtl
=
new
WebSocketCtr
(
TEXTTOSPEECH_WS_URL
)
const
ttsSocketCtl
=
new
WebSocketCtr
(
TEXTTOSPEECH_WS_URL
)
ttsSocketCtl
.
onMessage
=
(
data
:
{
audio
:
string
;
replyVoiceUrl
:
string
;
final
:
boolean
})
=>
{
ttsSocketCtl
.
onMessage
=
(
data
:
{
audio
:
string
;
replyVoiceUrl
:
string
;
final
:
boolean
})
=>
{
if
(
data
.
replyVoiceUrl
)
{
if
(
data
.
replyVoiceUrl
)
{
const
currentMessageIndex
=
props
.
messageList
.
length
-
1
if
(
props
.
messageList
.
get
(
messageId
)?.
voiceFragmentUrlList
)
{
const
voiceFragmentUrlListDraft
=
[...
props
.
messageList
.
get
(
messageId
)
!
.
voiceFragmentUrlList
]
if
(
props
.
messageList
[
currentMessageIndex
]?.
voiceFragmentUrlList
)
{
const
voiceFragmentUrlListDraft
=
[...
props
.
messageList
[
currentMessageIndex
].
voiceFragmentUrlList
]
voiceFragmentUrlListDraft
[
audioUrlSerialNo
]
=
data
.
replyVoiceUrl
voiceFragmentUrlListDraft
[
audioUrlSerialNo
]
=
data
.
replyVoiceUrl
messageAudioLoading
.
value
=
false
emit
(
'updateSpecifyMessageItem'
,
messageId
,
{
voiceFragmentUrlList
:
voiceFragmentUrlListDraft
})
emit
(
'updateSpecifyMessageItem'
,
currentMessageIndex
,
{
voiceFragmentUrlList
:
voiceFragmentUrlListDraft
})
if
(
props
.
answerAudioAutoPlay
&&
audioUrlSerialNo
===
0
&&
voiceFragmentUrlListDraft
[
audioUrlSerialNo
])
{
emit
(
'audioPlay'
,
props
.
messageList
.
get
(
messageId
)
!
)
}
if
(
props
.
answerAudioAutoPlaying
&&
audioUrlSerialNo
===
0
&&
voiceFragmentUrlListDraft
[
audioUrlSerialNo
]
)
{
if
(
!
props
.
answerAudioAutoPlay
)
{
emit
(
'
audioPlay'
,
props
.
messageList
[
currentMessageIndex
]
)
emit
(
'
updateSpecifyMessageItem'
,
messageId
,
{
isVoiceLoading
:
false
}
)
}
}
}
}
}
}
}
}
ttsSocketCtl
.
onMessageError
=
()
=>
{
emit
(
'updateSpecifyMessageItem'
,
messageId
,
{
isVoiceLoading
:
false
})
sentenceSpeechException
.
value
=
true
messageAudioLoading
.
value
=
false
window
.
$message
.
error
(
t
(
'common_module.unplayable_tip'
))
}
const
content
=
(
text
||
''
).
replace
(
/
\^\[[\d\\
[
\]
-
]
+
?\]\^
/g
,
''
)
const
content
=
(
text
||
''
).
replace
(
/
\^\[[\d\\
[
\]
-
]
+
?\]\^
/g
,
''
)
if
(
content
)
{
if
(
content
&&
props
.
timbreId
)
{
ttsSocketCtl
.
connect
(()
=>
{
ttsSocketCtl
.
connect
(()
=>
{
ttsSocketCtl
.
send
({
ttsSocketCtl
.
send
({
codec
:
'wav'
,
codec
:
'wav'
,
...
@@ -400,7 +436,12 @@ defineExpose({
...
@@ -400,7 +436,12 @@ defineExpose({
<div
<div
class=
"bg-px-send-png absolute bottom-2 right-[20px] h-[24px] w-[24px]"
class=
"bg-px-send-png absolute bottom-2 right-[20px] h-[24px] w-[24px]"
:class=
"
:class=
"
isSendBtnDisabled || isAnswerResponseWait || !isLogin || isInputMessageDisabled
isSendBtnDisabled ||
isAnswerResponseWait ||
!isLogin ||
isInputMessageDisabled ||
answerAudioPlaying ||
messageAudioLoading
? 'opacity-60'
? 'opacity-60'
: 'cursor-pointer'
: 'cursor-pointer'
"
"
...
...
src/views/share/components/message-item.vue
View file @
2bd4cd34
...
@@ -38,25 +38,27 @@ const assistantAvatar = computed(() => {
...
@@ -38,25 +38,27 @@ const assistantAvatar = computed(() => {
)
)
})
})
const
isShowWebAudioControl
=
computed
(()
=>
{
const
timbreEnabled
=
computed
(()
=>
{
return
(
return
!!
props
.
agentApplicationConfig
.
voiceConfig
.
timbreId
props
.
role
===
'assistant'
&&
!
props
.
messageItem
.
isVoiceLoading
&&
!
isMobile
.
value
&&
!!
props
.
messageItem
.
voiceFragmentUrlList
.
length
)
})
})
const
isShowMobileAudioControl
=
computed
(()
=>
{
const
isShowAudioControl
=
computed
(()
=>
{
return
(
return
props
.
role
===
'assistant'
&&
!
props
.
messageItem
.
isVoiceLoading
&&
timbreEnabled
.
value
props
.
role
===
'assistant'
&&
})
!
props
.
messageItem
.
isVoiceLoading
&&
isMobile
.
value
&&
const
isPlayableAudio
=
computed
(()
=>
{
!!
props
.
messageItem
.
voiceFragmentUrlList
.
length
return
isShowAudioControl
.
value
&&
!!
props
.
messageItem
.
voiceFragmentUrlList
.
length
)
})
const
isShowWebVoiceLoading
=
computed
(()
=>
{
return
props
.
role
===
'assistant'
&&
!
isMobile
.
value
&&
props
.
messageItem
.
isVoiceLoading
&&
timbreEnabled
.
value
})
})
function
handleAudioControl
()
{
function
handleAudioControl
()
{
if
(
!
isPlayableAudio
.
value
)
{
return
}
if
(
props
.
messageItem
.
isVoicePlaying
)
{
if
(
props
.
messageItem
.
isVoicePlaying
)
{
emit
(
'audioPause'
)
emit
(
'audioPause'
)
}
else
{
}
else
{
...
@@ -107,32 +109,60 @@ function handleAudioControl() {
...
@@ -107,32 +109,60 @@ function handleAudioControl() {
/>
/>
</p>
</p>
<div
v-show=
"role === 'assistant' && messageItem.isAnswerResponseLoading"
class=
"mb-[5px] mt-4 px-4"
>
<div
v-show=
"
role === 'assistant' && (messageItem.isAnswerResponseLoading || (isMobile && messageItem.isVoiceLoading))
"
class=
"mb-[5px] mt-4 px-4"
>
<CustomLoading
/>
<CustomLoading
/>
</div>
</div>
</div>
</div>
<div
v-show=
"isShow
MobileAudioControl
"
class=
"mt-[13px] flex items-center gap-2"
>
<div
v-show=
"isShow
AudioControl && isMobile
"
class=
"mt-[13px] flex items-center gap-2"
>
<div
<div
class=
"h-[18px] w-[18px] cursor-pointer"
class=
"h-[18px] w-[18px] cursor-pointer"
:class=
"messageItem.isVoicePlaying ? 'bg-svg-pause' : 'bg-svg-play'"
:class=
"messageItem.isVoicePlaying ? 'bg-svg-pause' : 'bg-svg-play'"
@
click=
"handleAudioControl"
@
click=
"handleAudioControl"
/>
/>
<MusicWavesLoading
v-show=
"messageItem.isVoicePlaying"
bar-bg-color=
"#333"
/>
<MusicWavesLoading
v-show=
"messageItem.isVoicePlaying && isPlayableAudio"
bar-bg-color=
"#333"
/>
<n-popover
style=
"max-width: 310px"
>
<template
#
trigger
>
<span
v-show=
"!isPlayableAudio"
class=
"text-[12px]"
>
{{
t
(
'common_module.unplayable'
)
}}
</span>
</
template
>
{{ t('common_module.unplayable_tip') }}
</n-popover>
</div>
</div>
</div>
</div>
<div
<div
v-show=
"isShowWebAudioControl"
v-show=
"isShowAudioControl && !isMobile"
class=
"hover:text-theme-color text-font-color flex cursor-pointer items-center gap-0.5 hover:opacity-80"
class=
"text-font-color flex items-center gap-0.5"
:class=
"isPlayableAudio ? 'hover:text-theme-color cursor-pointer hover:opacity-80' : 'cursor-not-allowed'"
@
click=
"handleAudioControl"
@
click=
"handleAudioControl"
>
>
<i
v-if=
"!messageItem.isVoicePlaying"
class=
"iconfont icon-play text-[24px]"
/>
<i
v-if=
"!messageItem.isVoicePlaying"
class=
"iconfont icon-play text-[24px]"
/>
<div
v-else
class=
"mx-1.5 my-3 h-[12px] w-[12px] bg-[url(@/assets/images/playing.gif)] bg-[length:100%_100%]"
/>
<div
v-else
class=
"mx-1.5 my-3 h-[12px] w-[12px] bg-[url(@/assets/images/playing.gif)] bg-[length:100%_100%]"
/>
<span
class=
"text-[12px]"
:class=
"messageItem.isVoicePlaying ? 'text-theme-color' : ''"
>
<span
v-show=
"isPlayableAudio"
class=
"text-[12px]"
:class=
"messageItem.isVoicePlaying ? 'text-theme-color' : ''"
>
{{ messageItem.isVoicePlaying ? t('common_module.stop_playing') : t('common_module.start_playing') }}
{{ messageItem.isVoicePlaying ? t('common_module.stop_playing') : t('common_module.start_playing') }}
</span>
</span>
<n-popover
style=
"max-width: 310px"
>
<
template
#
trigger
>
<span
v-show=
"!isPlayableAudio"
class=
"text-[12px]"
>
{{
t
(
'common_module.unplayable'
)
}}
</span>
</
template
>
{{ t('common_module.unplayable_tip') }}
</n-popover>
</div>
<div
v-if=
"isShowWebVoiceLoading"
class=
"py-3.5 pl-5"
>
<CustomLoading
/>
</div>
</div>
</div>
</div>
</div>
</div>
...
...
src/views/share/components/message-list.vue
View file @
2bd4cd34
...
@@ -6,7 +6,7 @@ import { PersonalAppConfigState } from '@/store/types/personal-app-config'
...
@@ -6,7 +6,7 @@ import { PersonalAppConfigState } from '@/store/types/personal-app-config'
import
{
computed
}
from
'vue'
import
{
computed
}
from
'vue'
interface
Props
{
interface
Props
{
messageList
:
ConversationMessageItem
[]
messageList
:
Map
<
string
,
ConversationMessageItem
>
agentApplicationConfig
:
PersonalAppConfigState
agentApplicationConfig
:
PersonalAppConfigState
continuousQuestionStatus
:
'default'
|
'close'
continuousQuestionStatus
:
'default'
|
'close'
continuousQuestionList
:
string
[]
continuousQuestionList
:
string
[]
...
@@ -24,8 +24,8 @@ const { scrollRef, scrollToBottom } = useScroll()
...
@@ -24,8 +24,8 @@ const { scrollRef, scrollToBottom } = useScroll()
const
isShowContinueQuestion
=
computed
(()
=>
{
const
isShowContinueQuestion
=
computed
(()
=>
{
return
(
return
(
props
.
continuousQuestionStatus
===
'default'
&&
props
.
continuousQuestionStatus
===
'default'
&&
props
.
messageList
.
length
>
1
&&
props
.
messageList
.
size
>
1
&&
!
props
.
messageList
[
props
.
messageList
.
length
-
1
].
isAnswerResponseLoading
!
Array
.
from
(
props
.
messageList
.
entries
()).
pop
()?.[
1
].
isAnswerResponseLoading
)
)
})
})
...
@@ -38,8 +38,8 @@ defineExpose({
...
@@ -38,8 +38,8 @@ defineExpose({
<main
ref=
"scrollRef"
class=
"h-full overflow-y-auto px-5"
>
<main
ref=
"scrollRef"
class=
"h-full overflow-y-auto px-5"
>
<div>
<div>
<MessageItem
<MessageItem
v-for=
"
messageItem
in messageList"
v-for=
"
[key, messageItem]
in messageList"
:key=
"
messageItem.timestamp
"
:key=
"
key
"
:role=
"messageItem.role"
:role=
"messageItem.role"
:message-item=
"messageItem"
:message-item=
"messageItem"
:agent-application-config=
"agentApplicationConfig"
:agent-application-config=
"agentApplicationConfig"
...
...
src/views/share/components/mobile-page-header.vue
View file @
2bd4cd34
...
@@ -38,7 +38,7 @@ function handleToLogin() {
...
@@ -38,7 +38,7 @@ function handleToLogin() {
<NButton
<NButton
v-show=
"isLogin"
v-show=
"isLogin"
type=
"primary"
type=
"primary"
class=
"rounded-md! h-[32px]! text-xs! w-[80px]!"
class=
"rounded-md! h-[32px]! text-xs!
min-
w-[80px]!"
@
click=
"handleToCreateApplication"
@
click=
"handleToCreateApplication"
>
>
{{
t
(
'common_module.create_agent_btn_text'
)
}}
{{
t
(
'common_module.create_agent_btn_text'
)
}}
...
...
src/views/share/share-application-mobile.vue
View file @
2bd4cd34
...
@@ -3,6 +3,7 @@ import { computed, onMounted, onUnmounted, ref, shallowRef } from 'vue'
...
@@ -3,6 +3,7 @@ import { computed, onMounted, onUnmounted, ref, shallowRef } from 'vue'
import
{
useRouter
}
from
'vue-router'
import
{
useRouter
}
from
'vue-router'
import
{
useI18n
}
from
'vue-i18n'
import
{
useI18n
}
from
'vue-i18n'
import
{
Howl
}
from
'howler'
import
{
Howl
}
from
'howler'
import
{
ValueOf
}
from
'type-fest'
import
PageHeader
from
'./components/mobile-page-header.vue'
import
PageHeader
from
'./components/mobile-page-header.vue'
import
Preamble
from
'./components/preamble.vue'
import
Preamble
from
'./components/preamble.vue'
import
MessageList
from
'./components/message-list.vue'
import
MessageList
from
'./components/message-list.vue'
...
@@ -37,11 +38,12 @@ const agentApplicationConfig = ref<PersonalAppConfigState>(defaultPersonalAppCon
...
@@ -37,11 +38,12 @@ const agentApplicationConfig = ref<PersonalAppConfigState>(defaultPersonalAppCon
const
messageListRef
=
ref
<
InstanceType
<
typeof
MessageList
>
|
null
>
(
null
)
const
messageListRef
=
ref
<
InstanceType
<
typeof
MessageList
>
|
null
>
(
null
)
const
footerInputRef
=
ref
<
InstanceType
<
typeof
FooterInput
>
|
null
>
(
null
)
const
footerInputRef
=
ref
<
InstanceType
<
typeof
FooterInput
>
|
null
>
(
null
)
const
messageList
=
ref
<
ConversationMessageItem
[]
>
([]
)
const
messageList
=
ref
(
new
Map
<
string
,
ConversationMessageItem
>
()
)
const
continuousQuestionStatus
=
ref
<
'default'
|
'close'
>
(
'default'
)
const
continuousQuestionStatus
=
ref
<
'default'
|
'close'
>
(
'default'
)
const
continueQuestionList
=
ref
<
string
[]
>
([])
const
continueQuestionList
=
ref
<
string
[]
>
([])
const
answerAudioAutoPlaying
=
ref
(
false
)
const
answerAudioAutoPlay
=
ref
(
false
)
// 语音是否自动播放
const
answerAudioPlaying
=
ref
(
false
)
// 语音播放中
const
currentPlayMessageItem
=
ref
<
ConversationMessageItem
|
null
>
(
null
)
const
currentPlayMessageItem
=
ref
<
ConversationMessageItem
|
null
>
(
null
)
const
currentPlayAudioFragmentSerialNo
=
ref
(
0
)
const
currentPlayAudioFragmentSerialNo
=
ref
(
0
)
const
currentSoundCtl
=
shallowRef
<
Howl
|
null
>
(
null
)
const
currentSoundCtl
=
shallowRef
<
Howl
|
null
>
(
null
)
...
@@ -110,7 +112,7 @@ async function handleGetAutoPlayByAgentId() {
...
@@ -110,7 +112,7 @@ async function handleGetAutoPlayByAgentId() {
const
res
=
await
fetchGetAutoPlayByAgentId
<
'Y'
|
'N'
>
(
agentId
.
value
)
const
res
=
await
fetchGetAutoPlayByAgentId
<
'Y'
|
'N'
>
(
agentId
.
value
)
if
(
res
.
code
===
0
)
{
if
(
res
.
code
===
0
)
{
answerAudioAutoPlay
ing
.
value
=
res
.
data
===
'Y'
answerAudioAutoPlay
.
value
=
res
.
data
===
'Y'
}
}
}
}
...
@@ -134,20 +136,31 @@ async function handleUpdateAutoPlaying(isAutoPlaying: boolean) {
...
@@ -134,20 +136,31 @@ async function handleUpdateAutoPlaying(isAutoPlaying: boolean) {
await
fetchUpdateAutoPlay
(
agentId
.
value
,
autoplay
)
await
fetchUpdateAutoPlay
(
agentId
.
value
,
autoplay
)
}
}
function
handleAddMessageItem
(
messageItem
:
ConversationMessageItem
)
{
function
handleAddMessageItem
(
messageI
d
:
string
,
messageI
tem
:
ConversationMessageItem
)
{
messageList
.
value
.
push
(
messageItem
)
messageList
.
value
.
set
(
messageId
,
messageItem
)
}
}
function
handleUpdateSpecifyMessageItem
(
messageItemIndex
:
number
,
newObj
:
Partial
<
ConversationMessageItem
>
)
{
function
handleUpdateSpecifyMessageItem
(
messageId
:
string
,
newMessageItem
:
Partial
<
ConversationMessageItem
>
)
{
if
(
messageList
.
value
[
messageItemIndex
])
{
const
currentMessageItemInfo
=
messageList
.
value
.
get
(
messageId
)
Object
.
entries
(
newObj
).
forEach
(([
k
,
v
])
=>
{
;(
messageList
.
value
[
messageItemIndex
]
as
any
)[
k
as
keyof
typeof
newObj
]
=
v
if
(
currentMessageItemInfo
)
{
const
updatePropertyLength
=
Object
.
keys
(
newMessageItem
).
length
if
(
updatePropertyLength
>
4
)
{
messageList
.
value
.
set
(
messageId
,
Object
.
assign
({},
currentMessageItemInfo
,
newMessageItem
))
return
}
Object
.
entries
<
ValueOf
<
typeof
newMessageItem
>>
(
newMessageItem
).
forEach
(([
key
,
value
])
=>
{
if
(
Object
.
prototype
.
hasOwnProperty
.
call
(
currentMessageItemInfo
,
key
))
{
;(
currentMessageItemInfo
as
any
)[
key
as
keyof
ConversationMessageItem
]
=
value
}
})
})
}
}
}
}
function
handleDeleteLastMessageItem
()
{
function
handleDeleteLastMessageItem
(
messageId
:
string
)
{
messageList
.
value
.
pop
(
)
messageList
.
value
.
delete
(
messageId
)
}
}
function
handleUpdatePageScroll
()
{
function
handleUpdatePageScroll
()
{
...
@@ -163,7 +176,8 @@ function handleClearAllMessage() {
...
@@ -163,7 +176,8 @@ function handleClearAllMessage() {
.
then
(()
=>
{
.
then
(()
=>
{
handleAudioPause
()
handleAudioPause
()
footerInputRef
.
value
?.
blockMessageResponse
()
footerInputRef
.
value
?.
blockMessageResponse
()
messageList
.
value
=
[]
messageList
.
value
.
clear
()
answerAudioPlaying
.
value
=
false
window
.
$message
.
success
(
t
(
'common_module.clear_success_message'
))
window
.
$message
.
success
(
t
(
'common_module.clear_success_message'
))
})
})
}
}
...
@@ -189,6 +203,7 @@ function howlSoundFactory(url: string) {
...
@@ -189,6 +203,7 @@ function howlSoundFactory(url: string) {
preload
:
true
,
preload
:
true
,
autoplay
:
true
,
autoplay
:
true
,
onplay
:
()
=>
{
onplay
:
()
=>
{
answerAudioPlaying
.
value
=
true
currentSoundCtl
.
value
=
soundCtl
currentSoundCtl
.
value
=
soundCtl
if
(
currentPlayMessageItem
.
value
)
{
if
(
currentPlayMessageItem
.
value
)
{
...
@@ -206,6 +221,7 @@ function howlSoundFactory(url: string) {
...
@@ -206,6 +221,7 @@ function howlSoundFactory(url: string) {
currentPlayAudioFragmentSerialNo
.
value
>
currentPlayMessageItem
.
value
.
voiceFragmentUrlList
.
length
-
1
currentPlayAudioFragmentSerialNo
.
value
>
currentPlayMessageItem
.
value
.
voiceFragmentUrlList
.
length
-
1
)
{
)
{
currentPlayMessageItem
.
value
.
isVoicePlaying
=
false
currentPlayMessageItem
.
value
.
isVoicePlaying
=
false
answerAudioPlaying
.
value
=
false
}
}
let
audioFragmentUrl
=
currentPlayMessageItem
.
value
?.
voiceFragmentUrlList
[
currentPlayAudioFragmentSerialNo
.
value
]
let
audioFragmentUrl
=
currentPlayMessageItem
.
value
?.
voiceFragmentUrlList
[
currentPlayAudioFragmentSerialNo
.
value
]
...
@@ -259,9 +275,10 @@ function handleAudioPause(isClearMessageList = false) {
...
@@ -259,9 +275,10 @@ function handleAudioPause(isClearMessageList = false) {
}
}
currentPlayMessageItem
.
value
&&
(
currentPlayMessageItem
.
value
.
isVoicePlaying
=
false
)
currentPlayMessageItem
.
value
&&
(
currentPlayMessageItem
.
value
.
isVoicePlaying
=
false
)
answerAudioPlaying
.
value
=
false
if
(
isClearMessageList
)
{
if
(
isClearMessageList
)
{
messageList
.
value
=
[]
messageList
.
value
.
clear
()
footerInputRef
.
value
?.
blockMessageResponse
()
footerInputRef
.
value
?.
blockMessageResponse
()
}
}
}
}
...
@@ -279,18 +296,18 @@ function handleAudioPause(isClearMessageList = false) {
...
@@ -279,18 +296,18 @@ function handleAudioPause(isClearMessageList = false) {
<div
class=
"mt-5 flex select-none justify-end px-4"
>
<div
class=
"mt-5 flex select-none justify-end px-4"
>
<div
v-show=
"isEnableVoice"
class=
"flex items-center gap-2"
>
<div
v-show=
"isEnableVoice"
class=
"flex items-center gap-2"
>
<span>
{{
t
(
'common_module.voice_auto_play'
)
}}
</span>
<span>
{{
t
(
'common_module.voice_auto_play'
)
}}
</span>
<n-switch
v-model:value=
"answerAudioAutoPlay
ing
"
size=
"small"
@
update:value=
"handleUpdateAutoPlaying"
>
<n-switch
v-model:value=
"answerAudioAutoPlay"
size=
"small"
@
update:value=
"handleUpdateAutoPlaying"
>
<template
#
checked
>
{{
t
(
'common_module.open'
)
}}
</
template
>
<template
#
checked
>
{{
t
(
'common_module.open'
)
}}
</
template
>
<
template
#
unchecked
>
{{
t
(
'common_module.close'
)
}}
</
template
>
<
template
#
unchecked
>
{{
t
(
'common_module.close'
)
}}
</
template
>
</n-switch>
</n-switch>
</div>
</div>
</div>
</div>
<div
v-if=
"messageList.
length
=== 0"
class=
"w-full flex-1 overflow-auto px-4"
>
<div
v-if=
"messageList.
size
=== 0"
class=
"w-full flex-1 overflow-auto px-4"
>
<Preamble
:agent-application-config=
"agentApplicationConfig"
/>
<Preamble
:agent-application-config=
"agentApplicationConfig"
/>
</div>
</div>
<div
v-if=
"messageList.
length
> 0"
class=
"flex w-full flex-1 flex-col overflow-hidden pt-5"
>
<div
v-if=
"messageList.
size
> 0"
class=
"flex w-full flex-1 flex-col overflow-hidden pt-5"
>
<div
class=
"flex-1 overflow-auto"
>
<div
class=
"flex-1 overflow-auto"
>
<MessageList
<MessageList
ref=
"messageListRef"
ref=
"messageListRef"
...
@@ -313,7 +330,8 @@ function handleAudioPause(isClearMessageList = false) {
...
@@ -313,7 +330,8 @@ function handleAudioPause(isClearMessageList = false) {
:continuous-question-status=
"continuousQuestionStatus"
:continuous-question-status=
"continuousQuestionStatus"
:is-enable-document-parse=
"isEnableDocumentParse"
:is-enable-document-parse=
"isEnableDocumentParse"
:is-enable-voice=
"isEnableVoice"
:is-enable-voice=
"isEnableVoice"
:answer-audio-auto-playing=
"answerAudioAutoPlaying"
:answer-audio-auto-play=
"answerAudioAutoPlay"
:answer-audio-playing=
"answerAudioPlaying"
:timbre-id=
"agentApplicationConfig.voiceConfig.timbreId"
:timbre-id=
"agentApplicationConfig.voiceConfig.timbreId"
@
add-message-item=
"handleAddMessageItem"
@
add-message-item=
"handleAddMessageItem"
@
update-specify-message-item=
"handleUpdateSpecifyMessageItem"
@
update-specify-message-item=
"handleUpdateSpecifyMessageItem"
...
@@ -324,6 +342,7 @@ function handleAudioPause(isClearMessageList = false) {
...
@@ -324,6 +342,7 @@ function handleAudioPause(isClearMessageList = false) {
@
create-continue-questions=
"handleCreateContinueQuestions"
@
create-continue-questions=
"handleCreateContinueQuestions"
@
reset-continue-question-list=
"handleResetContinueQuestionList"
@
reset-continue-question-list=
"handleResetContinueQuestionList"
@
audio-play=
"handleAudioPlay"
@
audio-play=
"handleAudioPlay"
@
audio-pause=
"handleAudioPause"
/>
/>
</div>
</div>
</div>
</div>
...
...
src/views/share/share-application-web.vue
View file @
2bd4cd34
...
@@ -3,6 +3,7 @@ import { computed, onMounted, onUnmounted, ref, shallowRef } from 'vue'
...
@@ -3,6 +3,7 @@ import { computed, onMounted, onUnmounted, ref, shallowRef } from 'vue'
import
{
useRouter
}
from
'vue-router'
import
{
useRouter
}
from
'vue-router'
import
{
useI18n
}
from
'vue-i18n'
import
{
useI18n
}
from
'vue-i18n'
import
{
Howl
}
from
'howler'
import
{
Howl
}
from
'howler'
import
{
ValueOf
}
from
'type-fest'
import
PageHeader
from
'./components/web-page-header.vue'
import
PageHeader
from
'./components/web-page-header.vue'
import
Preamble
from
'./components/preamble.vue'
import
Preamble
from
'./components/preamble.vue'
import
MessageList
from
'./components/message-list.vue'
import
MessageList
from
'./components/message-list.vue'
...
@@ -40,11 +41,12 @@ const agentApplicationConfig = ref<PersonalAppConfigState>(defaultPersonalAppCon
...
@@ -40,11 +41,12 @@ const agentApplicationConfig = ref<PersonalAppConfigState>(defaultPersonalAppCon
const
messageListRef
=
ref
<
InstanceType
<
typeof
MessageList
>
|
null
>
(
null
)
const
messageListRef
=
ref
<
InstanceType
<
typeof
MessageList
>
|
null
>
(
null
)
const
footerInputRef
=
ref
<
InstanceType
<
typeof
FooterInput
>
|
null
>
(
null
)
const
footerInputRef
=
ref
<
InstanceType
<
typeof
FooterInput
>
|
null
>
(
null
)
const
messageList
=
ref
<
ConversationMessageItem
[]
>
([]
)
const
messageList
=
ref
(
new
Map
<
string
,
ConversationMessageItem
>
()
)
const
continuousQuestionStatus
=
ref
<
'default'
|
'close'
>
(
'default'
)
const
continuousQuestionStatus
=
ref
<
'default'
|
'close'
>
(
'default'
)
const
continueQuestionList
=
ref
<
string
[]
>
([])
const
continueQuestionList
=
ref
<
string
[]
>
([])
const
answerAudioAutoPlaying
=
ref
(
false
)
const
answerAudioAutoPlay
=
ref
(
false
)
const
answerAudioPlaying
=
ref
(
false
)
// 语音播放中
const
currentPlayMessageItem
=
ref
<
ConversationMessageItem
|
null
>
(
null
)
const
currentPlayMessageItem
=
ref
<
ConversationMessageItem
|
null
>
(
null
)
const
currentPlayAudioFragmentSerialNo
=
ref
(
0
)
const
currentPlayAudioFragmentSerialNo
=
ref
(
0
)
const
currentSoundCtl
=
shallowRef
<
Howl
|
null
>
(
null
)
const
currentSoundCtl
=
shallowRef
<
Howl
|
null
>
(
null
)
...
@@ -123,7 +125,7 @@ async function handleGetAutoPlayByAgentId() {
...
@@ -123,7 +125,7 @@ async function handleGetAutoPlayByAgentId() {
const
res
=
await
fetchGetAutoPlayByAgentId
<
'Y'
|
'N'
>
(
agentId
.
value
)
const
res
=
await
fetchGetAutoPlayByAgentId
<
'Y'
|
'N'
>
(
agentId
.
value
)
if
(
res
.
code
===
0
)
{
if
(
res
.
code
===
0
)
{
answerAudioAutoPlay
ing
.
value
=
res
.
data
===
'Y'
answerAudioAutoPlay
.
value
=
res
.
data
===
'Y'
}
}
}
}
...
@@ -155,20 +157,31 @@ async function handleUpdateAutoPlaying(isAutoPlaying: boolean) {
...
@@ -155,20 +157,31 @@ async function handleUpdateAutoPlaying(isAutoPlaying: boolean) {
await
fetchUpdateAutoPlay
(
agentId
.
value
,
autoplay
)
await
fetchUpdateAutoPlay
(
agentId
.
value
,
autoplay
)
}
}
function
handleAddMessageItem
(
messageItem
:
ConversationMessageItem
)
{
function
handleAddMessageItem
(
messageI
d
:
string
,
messageI
tem
:
ConversationMessageItem
)
{
messageList
.
value
.
push
(
messageItem
)
messageList
.
value
.
set
(
messageId
,
messageItem
)
}
}
function
handleUpdateSpecifyMessageItem
(
messageItemIndex
:
number
,
newObj
:
Partial
<
ConversationMessageItem
>
)
{
function
handleUpdateSpecifyMessageItem
(
messageId
:
string
,
newMessageItem
:
Partial
<
ConversationMessageItem
>
)
{
if
(
messageList
.
value
[
messageItemIndex
])
{
const
currentMessageItemInfo
=
messageList
.
value
.
get
(
messageId
)
Object
.
entries
(
newObj
).
forEach
(([
k
,
v
])
=>
{
;(
messageList
.
value
[
messageItemIndex
]
as
any
)[
k
as
keyof
typeof
newObj
]
=
v
if
(
currentMessageItemInfo
)
{
const
updatePropertyLength
=
Object
.
keys
(
newMessageItem
).
length
if
(
updatePropertyLength
>
4
)
{
messageList
.
value
.
set
(
messageId
,
Object
.
assign
({},
currentMessageItemInfo
,
newMessageItem
))
return
}
Object
.
entries
<
ValueOf
<
typeof
newMessageItem
>>
(
newMessageItem
).
forEach
(([
key
,
value
])
=>
{
if
(
Object
.
prototype
.
hasOwnProperty
.
call
(
currentMessageItemInfo
,
key
))
{
;(
currentMessageItemInfo
as
any
)[
key
as
keyof
ConversationMessageItem
]
=
value
}
})
})
}
}
}
}
function
handleDeleteLastMessageItem
()
{
function
handleDeleteLastMessageItem
(
messageId
:
string
)
{
messageList
.
value
.
pop
(
)
messageList
.
value
.
delete
(
messageId
)
}
}
function
handleUpdatePageScroll
()
{
function
handleUpdatePageScroll
()
{
...
@@ -184,7 +197,8 @@ function handleClearAllMessage() {
...
@@ -184,7 +197,8 @@ function handleClearAllMessage() {
.
then
(()
=>
{
.
then
(()
=>
{
handleAudioPause
()
handleAudioPause
()
footerInputRef
.
value
?.
blockMessageResponse
()
footerInputRef
.
value
?.
blockMessageResponse
()
messageList
.
value
=
[]
messageList
.
value
.
clear
()
answerAudioPlaying
.
value
=
false
window
.
$message
.
success
(
t
(
'common_module.clear_success_message'
))
window
.
$message
.
success
(
t
(
'common_module.clear_success_message'
))
})
})
}
}
...
@@ -210,6 +224,7 @@ function howlSoundFactory(url: string) {
...
@@ -210,6 +224,7 @@ function howlSoundFactory(url: string) {
preload
:
true
,
preload
:
true
,
autoplay
:
true
,
autoplay
:
true
,
onplay
:
()
=>
{
onplay
:
()
=>
{
answerAudioPlaying
.
value
=
true
currentSoundCtl
.
value
=
soundCtl
currentSoundCtl
.
value
=
soundCtl
if
(
currentPlayMessageItem
.
value
)
{
if
(
currentPlayMessageItem
.
value
)
{
...
@@ -227,6 +242,7 @@ function howlSoundFactory(url: string) {
...
@@ -227,6 +242,7 @@ function howlSoundFactory(url: string) {
currentPlayAudioFragmentSerialNo
.
value
>
currentPlayMessageItem
.
value
.
voiceFragmentUrlList
.
length
-
1
currentPlayAudioFragmentSerialNo
.
value
>
currentPlayMessageItem
.
value
.
voiceFragmentUrlList
.
length
-
1
)
{
)
{
currentPlayMessageItem
.
value
.
isVoicePlaying
=
false
currentPlayMessageItem
.
value
.
isVoicePlaying
=
false
answerAudioPlaying
.
value
=
false
}
}
let
audioFragmentUrl
=
currentPlayMessageItem
.
value
?.
voiceFragmentUrlList
[
currentPlayAudioFragmentSerialNo
.
value
]
let
audioFragmentUrl
=
currentPlayMessageItem
.
value
?.
voiceFragmentUrlList
[
currentPlayAudioFragmentSerialNo
.
value
]
...
@@ -280,9 +296,10 @@ function handleAudioPause(isClearMessageList = false) {
...
@@ -280,9 +296,10 @@ function handleAudioPause(isClearMessageList = false) {
}
}
currentPlayMessageItem
.
value
&&
(
currentPlayMessageItem
.
value
.
isVoicePlaying
=
false
)
currentPlayMessageItem
.
value
&&
(
currentPlayMessageItem
.
value
.
isVoicePlaying
=
false
)
answerAudioPlaying
.
value
=
false
if
(
isClearMessageList
)
{
if
(
isClearMessageList
)
{
messageList
.
value
=
[]
messageList
.
value
.
clear
()
footerInputRef
.
value
?.
blockMessageResponse
()
footerInputRef
.
value
?.
blockMessageResponse
()
}
}
}
}
...
@@ -303,17 +320,17 @@ function handleAudioPause(isClearMessageList = false) {
...
@@ -303,17 +320,17 @@ function handleAudioPause(isClearMessageList = false) {
<div
class=
"relative mx-auto flex h-full w-[1000px] flex-col overflow-hidden"
>
<div
class=
"relative mx-auto flex h-full w-[1000px] flex-col overflow-hidden"
>
<div
v-show=
"isEnableVoice"
class=
"absolute right-10 top-7 flex select-none items-center gap-2"
>
<div
v-show=
"isEnableVoice"
class=
"absolute right-10 top-7 flex select-none items-center gap-2"
>
<span>
{{
t
(
'common_module.voice_auto_play'
)
}}
</span>
<span>
{{
t
(
'common_module.voice_auto_play'
)
}}
</span>
<n-switch
v-model:value=
"answerAudioAutoPlay
ing
"
size=
"small"
@
update:value=
"handleUpdateAutoPlaying"
>
<n-switch
v-model:value=
"answerAudioAutoPlay"
size=
"small"
@
update:value=
"handleUpdateAutoPlaying"
>
<template
#
checked
>
{{
t
(
'common_module.open'
)
}}
</
template
>
<template
#
checked
>
{{
t
(
'common_module.open'
)
}}
</
template
>
<
template
#
unchecked
>
{{
t
(
'common_module.close'
)
}}
</
template
>
<
template
#
unchecked
>
{{
t
(
'common_module.close'
)
}}
</
template
>
</n-switch>
</n-switch>
</div>
</div>
<div
v-if=
"messageList.
length
=== 0"
class=
"w-full flex-1 overflow-auto px-5"
>
<div
v-if=
"messageList.
size
=== 0"
class=
"w-full flex-1 overflow-auto px-5"
>
<Preamble
:agent-application-config=
"agentApplicationConfig"
/>
<Preamble
:agent-application-config=
"agentApplicationConfig"
/>
</div>
</div>
<div
v-if=
"messageList.
length
> 0"
class=
"flex w-full flex-1 flex-col overflow-hidden"
>
<div
v-if=
"messageList.
size
> 0"
class=
"flex w-full flex-1 flex-col overflow-hidden"
>
<div
class=
"mt-20 flex-1 overflow-auto"
>
<div
class=
"mt-20 flex-1 overflow-auto"
>
<MessageList
<MessageList
ref=
"messageListRef"
ref=
"messageListRef"
...
@@ -336,7 +353,8 @@ function handleAudioPause(isClearMessageList = false) {
...
@@ -336,7 +353,8 @@ function handleAudioPause(isClearMessageList = false) {
:continuous-question-status=
"continuousQuestionStatus"
:continuous-question-status=
"continuousQuestionStatus"
:is-enable-document-parse=
"isEnableDocumentParse"
:is-enable-document-parse=
"isEnableDocumentParse"
:is-enable-voice=
"isEnableVoice"
:is-enable-voice=
"isEnableVoice"
:answer-audio-auto-playing=
"answerAudioAutoPlaying"
:answer-audio-auto-play=
"answerAudioAutoPlay"
:answer-audio-playing=
"answerAudioPlaying"
:timbre-id=
"agentApplicationConfig.voiceConfig.timbreId"
:timbre-id=
"agentApplicationConfig.voiceConfig.timbreId"
@
add-message-item=
"handleAddMessageItem"
@
add-message-item=
"handleAddMessageItem"
@
update-specify-message-item=
"handleUpdateSpecifyMessageItem"
@
update-specify-message-item=
"handleUpdateSpecifyMessageItem"
...
@@ -347,6 +365,7 @@ function handleAudioPause(isClearMessageList = false) {
...
@@ -347,6 +365,7 @@ function handleAudioPause(isClearMessageList = false) {
@
create-continue-questions=
"handleCreateContinueQuestions"
@
create-continue-questions=
"handleCreateContinueQuestions"
@
reset-continue-question-list=
"handleResetContinueQuestionList"
@
reset-continue-question-list=
"handleResetContinueQuestionList"
@
audio-play=
"handleAudioPlay"
@
audio-play=
"handleAudioPlay"
@
audio-pause=
"handleAudioPause"
/>
/>
</div>
</div>
</div>
</div>
...
...
types/locales.d.ts
View file @
2bd4cd34
...
@@ -100,6 +100,8 @@ declare namespace I18n {
...
@@ -100,6 +100,8 @@ declare namespace I18n {
voice_auto_play
:
string
voice_auto_play
:
string
start_playing
:
string
start_playing
:
string
stop_playing
:
string
stop_playing
:
string
unplayable
:
string
unplayable_tip
:
string
response_error
:
string
response_error
:
string
agent_exception
:
string
agent_exception
:
string
equity
:
string
equity
:
string
...
@@ -116,6 +118,8 @@ declare namespace I18n {
...
@@ -116,6 +118,8 @@ declare namespace I18n {
cancel_associate_file_tip
:
string
cancel_associate_file_tip
:
string
upload_file_limit
:
string
upload_file_limit
:
string
overwrite_file_tip
:
string
overwrite_file_tip
:
string
stop_playing_and_then_operate
:
string
do_not_operate_until_the_reply_is_complete
:
string
}
}
data_table_module
:
{
data_table_module
:
{
...
@@ -292,9 +296,10 @@ declare namespace I18n {
...
@@ -292,9 +296,10 @@ declare namespace I18n {
memory_fragment_delete_row_tip_content
:
string
memory_fragment_delete_row_tip_content
:
string
add_knowledge_successfully
:
string
add_knowledge_successfully
:
string
remove_knowledge_successfully
:
string
remove_knowledge_successfully
:
string
setting_timbre
:
string
setting_voice
:
string
setting_timbre_message
:
string
setting_voice_message
:
string
setting_timbre_desc
:
string
setting_voice_desc
:
string
currently_only_one_voice_can_be_set
:
string
memory_variable_modal
:
{
memory_variable_modal
:
{
edit_memory_variable
:
string
edit_memory_variable
:
string
...
...
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