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
a37f91fb
Commit
a37f91fb
authored
May 10, 2025
by
nick zheng
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'beta' into 'master'
Beta See merge request
!224
parents
b423f4db
edafe38c
Expand all
Show whitespace changes
Inline
Side-by-side
Showing
33 changed files
with
1748 additions
and
119 deletions
+1748
-119
eslint.config.js
eslint.config.js
+1
-0
execute-plugin-render.vue
...omponents/execute-plugin-render/execute-plugin-render.vue
+49
-46
useEventSource.ts
src/composables/useEventSource.ts
+6
-1
message-item.vue
src/views/home/components/message-item.vue
+1
-1
types.d.ts
src/views/home/components/smart-forms/types.d.ts
+26
-0
smart-forms.ts
src/views/home/utils/smart-forms.ts
+40
-0
footer-operation.vue
...iews/multi-model-dialogue/components/footer-operation.vue
+33
-1
message-item.vue
src/views/multi-model-dialogue/components/message-item.vue
+45
-15
message-list.vue
src/views/multi-model-dialogue/components/message-list.vue
+9
-1
model-dialogue-item.vue
...s/multi-model-dialogue/components/model-dialogue-item.vue
+8
-1
business-trip-form.vue
.../components/smart-forms/components/business-trip-form.vue
+360
-0
business-trip-reimbursement-form.vue
...art-forms/components/business-trip-reimbursement-form.vue
+188
-0
index.vue
...ews/multi-model-dialogue/components/smart-forms/index.vue
+29
-0
types.d.ts
...ti-model-dialogue/components/smart-forms/types/types.d.ts
+28
-0
smart-forms.ts
...odel-dialogue/components/smart-forms/utils/smart-forms.ts
+41
-0
multi-model-dialogue.vue
src/views/multi-model-dialogue/multi-model-dialogue.vue
+16
-4
types.d.ts
src/views/multi-model-dialogue/types.d.ts
+8
-1
fetch-event-stream-source.ts
...s/multi-model-dialogue/utils/fetch-event-stream-source.ts
+1
-1
agent-preview.vue
...g/components/agent-config/agent-preview/agent-preview.vue
+15
-3
footer-input.vue
...ts/agent-config/agent-preview/components/footer-input.vue
+32
-1
message-item.vue
...ts/agent-config/agent-preview/components/message-item.vue
+47
-15
message-list.vue
...ts/agent-config/agent-preview/components/message-list.vue
+10
-3
business-trip-form.vue
.../components/smart-forms/components/business-trip-form.vue
+357
-0
business-trip-reimbursement-form.vue
...art-forms/components/business-trip-reimbursement-form.vue
+187
-0
index.vue
...ent-config/agent-preview/components/smart-forms/index.vue
+28
-0
types.d.ts
...fig/agent-preview/components/smart-forms/types/types.d.ts
+28
-0
smart-forms.ts
...agent-preview/components/smart-forms/utils/smart-forms.ts
+41
-0
types.d.ts
...e/personal-app-setting/components/agent-config/types.d.ts
+2
-0
footer-input.vue
src/views/share/components/footer-input.vue
+32
-1
message-item.vue
src/views/share/components/message-item.vue
+47
-17
message-list.vue
src/views/share/components/message-list.vue
+10
-3
share-application-web.vue
src/views/share/share-application-web.vue
+15
-3
conversation.d.ts
types/conversation.d.ts
+8
-1
No files found.
eslint.config.js
View file @
a37f91fb
...
...
@@ -24,6 +24,7 @@ export default [
AnyObject
:
'readonly'
,
KnowledgeContentResultItem
:
'readonly'
,
DBChainResultItem
:
'readonly'
,
PluginDisplayFormatType
:
'readonly'
,
ConversationMessageItem
:
'readonly'
,
ConversationMessageItemInfo
:
'readonly'
,
MittEvents
:
'readonly'
,
...
...
src/components/execute-plugin-render/execute-plugin-render.vue
View file @
a37f91fb
...
...
@@ -7,7 +7,7 @@ import MarkdownRender from '@/components/markdown-render/markdown-render.vue'
import
{
useLayoutConfig
}
from
'@/composables/useLayoutConfig'
interface
Props
{
displayFormat
:
'json'
|
'markdown'
|
'none'
displayFormat
:
PluginDisplayFormatType
name
:
string
arguments
:
string
content
:
string
...
...
@@ -59,11 +59,12 @@ const parsedMarkdownPluginContent = computed(() => {
<
template
>
<div
class=
"flex w-full flex-col bg-[#f3f5f9]"
:class=
"
isMobile ? 'rounded-[2.66667vw] px-[3.2vw] py-[2.66667vw] text-[3.2vw]' : 'rounded-[10px] px-[12px] py-[10px]'
"
:class=
"isMobile ? 'rounded-[2.66667vw] py-[2.66667vw] text-[3.2vw]' : 'rounded-[10px] py-[10px]'"
>
<div
class=
"flex flex-1 items-center justify-between gap-[10px] overflow-hidden"
:class=
"isMobile ? 'px-[3.2vw]' : 'px-[12px]'"
>
<div
class=
"flex flex-1 items-center justify-between gap-[10px] overflow-hidden"
>
<div
class=
"flex flex-1 items-center gap-[6px] overflow-hidden"
>
<div
v-show=
"pluginLoading"
...
...
@@ -91,13 +92,14 @@ const parsedMarkdownPluginContent = computed(() => {
/>
<
/div
>
<
n
-
scrollbar
style
=
"max-height: 500px"
:
class
=
"{ 'mt-[10px]': !isFolding
}
"
>
<
div
class
=
"flex transform flex-col overflow-x-auto duration-200 ease-in-out"
:
class
=
"isFolding ? 'h-0 overflow-hidden' : 'h-fit'
"
:
class
=
"[isFolding ? 'h-0 overflow-hidden' : 'h-fit', isMobile ? 'px-[3.2vw]' : 'px-[12px]']
"
>
<!--
json
格式
-->
<
div
v
-
if
=
"displayFormat === 'json'"
>
<
div
class
=
"mt-[10px]"
>
<
div
>
<
span
class
=
"rounded-theme inline-block bg-[#E8EAFF] text-[#0072FF]"
:
class
=
"
...
...
@@ -128,16 +130,17 @@ const parsedMarkdownPluginContent = computed(() => {
<!--
markdown
格式
-->
<
div
v
-
else
-
if
=
"displayFormat === 'markdown'"
>
<
div
class
=
"mt-[10px]"
>
<
div
>
<
MarkdownRender
:
raw
-
text
-
content
=
"parsedMarkdownPluginContent"
:
font
-
size
=
"isMobile ? '3.2vw' : '14px'"
/>
<
/div
>
<
/div
>
<!--
其他
格式
-->
<
div
v
-
else
>
<
span
class
=
"mt-[10px]
break-all"
>
{{
content
}}
<
/span
>
<!--
none
格式
-->
<
div
v
-
else
-
if
=
"displayFormat === 'none'"
>
<
span
class
=
"text-font-color
break-all"
>
{{
content
}}
<
/span
>
<
/div
>
<
/div
>
<
/n-scrollbar
>
<
/div
>
<
/template
>
...
...
src/composables/useEventSource.ts
View file @
a37f91fb
...
...
@@ -8,7 +8,12 @@ import { languageKeyTransform } from '@/utils/language-key-transform'
interface
ResponseData
{
message
:
string
reasoningContent
:
string
function
:
{
displayFormat
:
'json'
|
'markdown'
|
'none'
;
name
:
string
;
result
:
string
;
arguments
:
string
}
function
:
{
displayFormat
:
PluginDisplayFormatType
name
:
string
result
:
string
arguments
:
string
}
knowledgeContentResult
:
KnowledgeContentResultItem
[]
dbChainResult
:
DBChainResultItem
[]
}
...
...
src/views/home/components/message-item.vue
View file @
a37f91fb
...
...
@@ -75,7 +75,7 @@ const handleContentEdit = throttle(
<div
class=
"flex"
>
<img
class=
"h-[36px] w-[36px] rounded-[6px] object-cover"
:src=
"avatarUrl"
alt=
"Avatar"
/>
<div
v-if=
"
tru
e"
class=
"ml-[11px] overflow-hidden"
>
<div
v-if=
"
fals
e"
class=
"ml-[11px] overflow-hidden"
>
<AuthorInfo
:is-agent-message=
"isAgentMessage"
:message-item=
"messageItem"
:message-author=
"messageAuthor"
/>
<div
...
...
src/views/home/components/smart-forms/types.d.ts
0 → 100644
View file @
a37f91fb
export
interface
ResponseBusinessTripForm
{
purpose
:
string
place
:
string
departureDate
:
string
returnDate
:
string
vehicle
:
string
transportationFee
:
string
accommodation
:
string
accommodationCost
:
string
advancePaymentAmount
:
string
totalBudget
:
string
}
export
interface
BusinessTripForm
{
objective
:
string
travelLocation
:
string
departureTime
:
number
|
null
returnTime
:
number
|
null
vehicle
:
string
vehicleEstimatedCost
:
number
|
null
residence
:
string
residenceEstimatedCost
:
number
|
null
advancePaymentAmount
:
number
|
null
generalBudget
:
string
email
:
string
}
src/views/home/utils/smart-forms.ts
0 → 100644
View file @
a37f91fb
import
type
{
SmartFormTypes
}
from
'../types'
import
{
ResponseBusinessTripForm
,
BusinessTripForm
}
from
'@/views/home/components/smart-forms/types'
export
function
smartFormTypeConverter
(
type
:
'travelForm'
):
SmartFormTypes
{
switch
(
type
)
{
case
'travelForm'
:
return
'BusinessTripForm'
}
}
export
function
businessTripFormParser
(
resForm
:
Partial
<
ResponseBusinessTripForm
>
)
{
return
{
objective
:
resForm
.
purpose
||
'CustomerVisits'
,
travelLocation
:
resForm
.
place
||
''
,
departureTime
:
resForm
.
departureDate
?
Date
.
parse
(
resForm
.
departureDate
)
:
null
,
returnTime
:
resForm
.
returnDate
?
Date
.
parse
(
resForm
.
returnDate
)
:
null
,
vehicle
:
resForm
.
vehicle
||
''
,
vehicleEstimatedCost
:
resForm
.
transportationFee
?
Number
.
parseInt
(
resForm
.
transportationFee
)
:
null
,
residence
:
resForm
.
accommodation
||
''
,
residenceEstimatedCost
:
resForm
.
accommodationCost
?
Number
.
parseInt
(
resForm
.
accommodationCost
)
:
null
,
advancePaymentAmount
:
resForm
.
advancePaymentAmount
?
Number
.
parseInt
(
resForm
.
advancePaymentAmount
)
:
null
,
generalBudget
:
resForm
.
totalBudget
||
''
,
email
:
''
,
}
}
export
function
businessTripFormReturner
(
form
:
BusinessTripForm
)
{
return
{
purpose
:
form
.
objective
,
place
:
form
.
travelLocation
,
departureDate
:
form
.
departureTime
?
new
Date
(
form
.
departureTime
).
toISOString
()
:
''
,
returnDate
:
form
.
returnTime
?
new
Date
(
form
.
returnTime
).
toISOString
()
:
''
,
vehicle
:
form
.
vehicle
,
transportationFee
:
form
.
vehicleEstimatedCost
,
accommodation
:
form
.
residence
,
accommodationCost
:
form
.
residenceEstimatedCost
,
advancePaymentAmount
:
form
.
advancePaymentAmount
,
totalBudget
:
form
.
generalBudget
,
}
}
src/views/multi-model-dialogue/components/footer-operation.vue
View file @
a37f91fb
...
...
@@ -5,12 +5,14 @@ import { nanoid } from 'nanoid'
import
{
throttle
}
from
'lodash-es'
import
{
CloseSmall
}
from
'@icon-park/vue-next'
import
CMessage
from
'./c-message'
import
{
MessageItemInterface
,
MultiModelDialogueItem
,
QuestionMessageItem
}
from
'../types'
import
{
MessageItemInterface
,
MultiModelDialogueItem
,
QuestionMessageItem
,
SmartFormTypes
}
from
'../types'
import
{
fetchEventStreamSource
}
from
'../utils/fetch-event-stream-source'
import
{
UploadStatus
}
from
'@/enums/upload-status'
import
{
ChannelType
}
from
'@/enums/channel'
import
{
useDialogueFile
}
from
'@/composables/useDialogueFile'
import
{
useUserStore
}
from
'@/store/modules/user'
import
{
smartFormTypeConverter
}
from
'./smart-forms/utils/smart-forms'
import
{
SmartFormDisplayFormat
}
from
'./smart-forms/types/types'
const
{
t
}
=
useI18n
()
...
...
@@ -29,6 +31,7 @@ const emit = defineEmits<{
deleteMessageItem
:
[
messageId
:
string
,
index
:
number
]
messageListScrollToBottom
:
[
autoScrollBottom
?:
boolean
]
clearAllMessage
:
[]
smartFormsStatusFreezeCheck
:
[
value
:
SmartFormTypes
,
index
:
number
]
}
>
()
const
userStore
=
useUserStore
()
...
...
@@ -205,6 +208,35 @@ function handleQuestionSubmit() {
// 插件
if
(
data
.
function
&&
data
.
function
.
name
)
{
// 表单插件,展示表单内容
if
([
'travelForm'
].
includes
(
data
.
function
.
displayFormat
))
{
emit
(
'smartFormsStatusFreezeCheck'
,
smartFormTypeConverter
(
data
.
function
.
displayFormat
as
SmartFormDisplayFormat
),
modelIndex
,
)
emit
(
'updateMessageItem'
,
answerMessageId
,
{
pluginResult
:
{
displayFormat
:
data
.
function
.
displayFormat
,
pluginName
:
data
.
function
.
name
,
arguments
:
data
.
function
.
arguments
,
pluginContent
:
data
.
function
.
result
,
},
smartFormInfo
:
{
type
:
smartFormTypeConverter
(
data
.
function
.
displayFormat
as
SmartFormDisplayFormat
),
isDisabled
:
false
,
params
:
data
.
function
.
result
,
},
},
modelIndex
,
)
emit
(
'messageListScrollToBottom'
)
return
}
emit
(
'updateMessageItem'
,
answerMessageId
,
...
...
src/views/multi-model-dialogue/components/message-item.vue
View file @
a37f91fb
<
script
setup
lang=
"ts"
>
import
{
computed
,
readonly
,
ref
}
from
'vue'
import
{
computed
,
provide
,
readonly
,
ref
}
from
'vue'
import
{
useI18n
}
from
'vue-i18n'
import
{
Down
}
from
'@icon-park/vue-next'
import
type
{
MessageItemInterface
}
from
'../types'
...
...
@@ -7,9 +7,11 @@ import MarkdownRender from '@/components/markdown-render/markdown-render.vue'
import
ExecuteCodeRender
from
'@/components/execute-code-render/execute-code-render.vue'
import
ExecutePluginRender
from
'@/components/execute-plugin-render/execute-plugin-render.vue'
import
MessageBubbleLoading
from
'./message-bubble-loading.vue'
import
SmartForms
from
'./smart-forms/index.vue'
interface
Props
{
messageItem
:
MessageItemInterface
messageItemId
:
string
}
const
props
=
defineProps
<
Props
>
()
...
...
@@ -20,14 +22,33 @@ const emit = defineEmits<{
const
{
t
}
=
useI18n
()
provide
(
'messageItemId'
,
props
.
messageItemId
)
const
isShowReasoningContent
=
ref
(
true
)
const
agentAvatarUrl
=
readonly
({
url
:
'https://gsst-poe-sit.gz.bcebos.com/icon/agent-avatar.png'
})
const
isAssistant
=
computed
(()
=>
{
const
displaySmartFormsList
=
readonly
([
'travelForm'
])
const
isAgentMessage
=
computed
(()
=>
{
return
props
.
messageItem
.
role
===
'assistant'
})
const
currentBubbleTextColor
=
computed
(()
=>
{
return
isAgentMessage
.
value
?
'#192338'
:
'#fff'
})
const
messageAuthor
=
computed
(()
=>
{
return
props
.
messageItem
.
nickName
})
const
isShowSmartForms
=
computed
(()
=>
{
if
(
props
.
messageItem
?.
pluginResult
?.
displayFormat
)
{
return
displaySmartFormsList
.
includes
(
props
.
messageItem
?.
pluginResult
?.
displayFormat
)
}
return
false
})
const
assistantAvatarUrl
=
computed
(()
=>
{
return
props
.
messageItem
.
avatar
||
agentAvatarUrl
.
url
})
...
...
@@ -42,12 +63,13 @@ function handleShowReasoningContentSwitch() {
<div
class=
"flex"
>
<img
class=
"h-[36px] w-[36px] flex-shrink-0 rounded-full object-cover"
:src=
"isA
ssistant
? assistantAvatarUrl : props.messageItem.avatar"
:src=
"isA
gentMessage
? assistantAvatarUrl : props.messageItem.avatar"
alt=
"Avatar"
/>
<div
class=
"ml-[11px] flex flex-col items-start overflow-hidden"
>
<template
v-if=
"isAssistant && messageItem.nickName === 'DeepSeek'"
>
<div
v-if=
"!isShowSmartForms"
class=
"ml-[11px] flex flex-col items-start overflow-hidden"
>
<!-- DeepSeek深度思考 -->
<template
v-if=
"isAgentMessage && messageItem.nickName === 'DeepSeek'"
>
<div
class=
"mb-[7px] select-none text-[14px]"
>
<div
class=
"inline-flex cursor-pointer"
@
click=
"handleShowReasoningContentSwitch"
>
<span
v-if=
"messageItem.isTextContentLoading"
class=
"mr-[6px]"
>
...
...
@@ -89,19 +111,19 @@ function handleShowReasoningContentSwitch() {
<
div
class
=
"min-h-[21px] w-full flex-wrap rounded-[10px] border border-[#9EA3FF] px-[15px] py-[12px] text-justify"
:
class
=
"{
'bg-[#777EF9]':
isAssistant
,
'text-[#fff]':
isAssistant
,
'bg-[#777EF9]':
!isAgentMessage
,
'text-[#fff]':
!isAgentMessage
,
'!min-w-[80px]': messageItem.isTextContentLoading,
}
"
>
<
img
v
-
show
=
"!isA
ssistant
&& messageItem.imageUrl"
v
-
show
=
"!isA
gentMessage
&& messageItem.imageUrl"
:
src
=
"messageItem.imageUrl"
class
=
"max-h-[120px]! mb-[12px] rounded-[10px] object-contain"
/>
<!--
插件返回结果
-->
<
div
v
-
show
=
"isA
ssistant
&& messageItem?.pluginResult?.pluginName"
class
=
"mb-[11px] w-full"
>
<
div
v
-
show
=
"isA
gentMessage
&& messageItem?.pluginResult?.pluginName"
class
=
"mb-[11px] w-full"
>
<
ExecutePluginRender
:
display
-
format
=
"messageItem.pluginResult?.displayFormat || 'none'"
:
name
=
"messageItem.pluginResult?.pluginName!"
...
...
@@ -111,8 +133,8 @@ function handleShowReasoningContentSwitch() {
/>
<
/div
>
<
div
v
-
if
=
"messageItem.isTextContentLoading"
class
=
"flex px-4 py-
1.5
"
>
<
MessageBubbleLoading
:
active
-
color
=
"
isAssistant ? '#fff' : '#192338'
"
/>
<
div
v
-
if
=
"messageItem.isTextContentLoading"
class
=
"flex px-4 py-
[7.5px]
"
>
<
MessageBubbleLoading
:
active
-
color
=
"
currentBubbleTextColor
"
/>
<
/div
>
<
div
v
-
else
>
...
...
@@ -128,21 +150,21 @@ function handleShowReasoningContentSwitch() {
:
raw
-
text
-
content
=
"
messageItem.content ? messageItem.content : t('common_module.dialogue_module.empty_message_content')
"
:
color
=
"
isAssistant ? '#fff' : '#192338'
"
:
color
=
"
currentBubbleTextColor
"
/>
<
div
v
-
show
=
"isA
ssistant
&& messageItem.isAnswerResponseLoading"
v
-
show
=
"isA
gentMessage
&& messageItem.isAnswerResponseLoading"
class
=
"mt-2.5 flex h-[21px] w-[30px] items-center justify-center"
>
<
MessageBubbleLoading
:
active
-
color
=
"
isAssistant ? '#fff' : '#192338'
"
/>
<
MessageBubbleLoading
:
active
-
color
=
"
currentBubbleTextColor
"
/>
<
/div
>
<
/div
>
<
/div
>
<
div
class
=
"flex justify-between py-[2px]"
>
<
div
v
-
show
=
"isA
ssistant
&& messageItem.knowledgeContentResult.length"
v
-
show
=
"isA
gentMessage
&& messageItem.knowledgeContentResult.length"
class
=
"flex-center rounded-theme h-8 cursor-pointer gap-[5px] px-[14px] font-['Microsoft_YaHei_UI'] text-[#0B7DFF] hover:opacity-80"
@
click
=
"emit('showKnowledgeResult')"
>
...
...
@@ -152,6 +174,14 @@ function handleShowReasoningContentSwitch() {
<
/div
>
<
/div
>
<
/div
>
<
SmartForms
v
-
else
:
message
-
item
=
"messageItem"
:
message
-
author
=
"messageAuthor"
:
is
-
agent
-
message
=
"isAgentMessage"
:
current
-
bubble
-
text
-
color
=
"currentBubbleTextColor"
/>
<
/div
>
<
/div
>
<
/template
>
...
...
src/views/multi-model-dialogue/components/message-list.vue
View file @
a37f91fb
<
script
setup
lang=
"ts"
>
import
{
useTemplateRef
}
from
'vue'
import
{
provide
,
useTemplateRef
}
from
'vue'
import
{
ScrollbarInst
}
from
'naive-ui'
import
{
useElementVisibility
}
from
'@vueuse/core'
import
MessageItem
from
'./message-item.vue'
...
...
@@ -13,6 +13,7 @@ defineProps<Props>()
const
emit
=
defineEmits
<
{
showKnowledgeResult
:
[
knowledgeContentResult
:
KnowledgeContentResultItem
[]]
updateSpecifyMessageItem
:
[
messageId
:
string
,
newMessageItem
:
Partial
<
MessageItemInterface
>
]
}
>
()
const
scrollbarRef
=
useTemplateRef
<
ScrollbarInst
|
null
>
(
'scrollbarRef'
)
...
...
@@ -21,6 +22,12 @@ const backBottomBtnFlagRef = useTemplateRef<HTMLDivElement | null>('backBottomBt
const
isNotShowBackBottomBtn
=
useElementVisibility
(
backBottomBtnFlagRef
)
provide
(
'updateSpecifyMessageItem'
,
{
updateSpecifyMessageItem
:
(
messageId
:
string
,
newMessageItem
:
Partial
<
MessageItemInterface
>
)
=>
{
emit
(
'updateSpecifyMessageItem'
,
messageId
,
newMessageItem
)
},
})
function
scrollToBottom
()
{
if
(
scrollbarRef
.
value
)
{
scrollbarRef
.
value
.
scrollTo
({
top
:
999999999
,
behavior
:
'smooth'
})
...
...
@@ -41,6 +48,7 @@ defineExpose({
<MessageItem
v-for=
"[key, messageItem] in messageList"
:key=
"key"
:message-item-id=
"key"
:message-item=
"messageItem"
@
show-knowledge-result=
"emit('showKnowledgeResult', messageItem.knowledgeContentResult)"
/>
...
...
src/views/multi-model-dialogue/components/model-dialogue-item.vue
View file @
a37f91fb
...
...
@@ -5,7 +5,7 @@ import { SelectOption } from 'naive-ui'
import
{
useVModel
}
from
'@vueuse/core'
import
{
useElementSize
}
from
'@vueuse/core'
import
{
useSystemLanguageStore
}
from
'@/store/modules/system-language'
import
{
MultiModelDialogueItem
}
from
'../types'
import
{
MultiModelDialogueItem
,
MessageItemInterface
}
from
'../types'
import
ModelSetting
from
'./model-setting.vue'
import
MessageList
from
'./message-list.vue'
import
HitKnowledgeContent
from
'./hit-knowledge-content.vue'
...
...
@@ -15,6 +15,7 @@ interface Props {
modelListOptions
:
SelectOption
[]
totalNum
:
number
isCurrent
:
boolean
modelDialogueId
:
number
}
const
{
t
}
=
useI18n
()
...
...
@@ -26,6 +27,7 @@ const emit = defineEmits<{
updateConfig
:
[
modelDialogueItem
:
MultiModelDialogueItem
]
replaceConfig
:
[
modelDialogueItem
:
MultiModelDialogueItem
]
resetConversation
:
[]
updateMessageItem
:
[
messageId
:
string
,
messageItem
:
Partial
<
MessageItemInterface
>
,
index
:
number
]
}
>
()
const
systemLanguageStore
=
useSystemLanguageStore
()
...
...
@@ -118,6 +120,10 @@ function handleShowKnowledgeResult(knowledgeContentResult: KnowledgeContentResul
isShowKnowledgeContentResult
.
value
=
true
currentKnowledgeContentResult
.
value
=
knowledgeContentResult
}
function
handleUpdateSpecifyMessageItem
(
messageId
:
string
,
messageItem
:
Partial
<
MessageItemInterface
>
)
{
emit
(
'updateMessageItem'
,
messageId
,
messageItem
,
props
.
modelDialogueId
)
}
</
script
>
<
template
>
...
...
@@ -205,6 +211,7 @@ function handleShowKnowledgeResult(knowledgeContentResult: KnowledgeContentResul
ref=
"messageListRef"
:message-list=
"modelDialogueItem.messageList"
@
show-knowledge-result=
"handleShowKnowledgeResult"
@
update-specify-message-item=
"handleUpdateSpecifyMessageItem"
/>
<HitKnowledgeContent
...
...
src/views/multi-model-dialogue/components/smart-forms/components/business-trip-form.vue
0 → 100644
View file @
a37f91fb
This diff is collapsed.
Click to expand it.
src/views/multi-model-dialogue/components/smart-forms/components/business-trip-reimbursement-form.vue
0 → 100644
View file @
a37f91fb
<
script
setup
lang=
"ts"
>
import
{
readonly
,
ref
}
from
'vue'
import
type
{
MessageItemInterface
}
from
'../../../types'
import
MarkdownRender
from
'@/components/markdown-render/markdown-render.vue'
import
type
{
FormRules
}
from
'naive-ui'
import
MessageBubbleLoading
from
'../../message-bubble-loading.vue'
interface
Props
{
isAgentMessage
:
boolean
messageItem
:
MessageItemInterface
currentBubbleTextColor
:
string
}
defineProps
<
Props
>
()
const
formRules
=
readonly
<
FormRules
>
({
objective
:
{
required
:
true
,
trigger
:
[
'blur'
,
'input'
],
message
:
''
,
},
travelLocation
:
{
required
:
true
,
trigger
:
[
'blur'
,
'input'
],
message
:
''
,
},
departureTime
:
{
required
:
true
,
trigger
:
[
'blur'
,
'input'
],
message
:
''
,
},
returnTime
:
{
required
:
true
,
trigger
:
[
'blur'
,
'input'
],
message
:
''
,
},
email
:
{
required
:
true
,
trigger
:
[
'blur'
,
'input'
],
message
:
''
,
},
})
const
formModel
=
ref
({
objective
:
'khbf'
,
travelLocation
:
''
,
departureTime
:
null
,
returnTime
:
null
,
vehicle
:
''
,
vehicleEstimatedCost
:
''
,
residence
:
''
,
residenceEstimatedCost
:
''
,
estimatedAmount
:
''
,
generalBudget
:
''
,
email
:
''
,
})
const
objectiveOptions
=
readonly
([
{
label
:
'客户拜访'
,
value
:
'khbf'
,
},
{
label
:
'会议'
,
value
:
'hy'
,
},
{
label
:
'培训'
,
value
:
'px'
,
},
])
</
script
>
<
template
>
<div
class=
"ml-[11px] overflow-hidden"
>
<div
class=
"mb-[7px] text-[12px] text-[#999]"
>
作者信息
</div>
<div
class=
"min-w-[420px]"
>
<div
class=
"box-content min-h-[21px] min-w-[10px] max-w-full rounded-[10px] border border-[#9EA3FF] bg-[#777EF9] px-[15px] py-[14px] text-justify"
:class=
"
{
'!bg-[#fff]': isAgentMessage,
'!min-w-[80px]': messageItem.isAnswerResponseLoading,
}"
>
<div
v-if=
"messageItem.isAnswerResponseLoading && !messageItem.content"
class=
"flex h-[21px] items-center justify-center"
>
<MessageBubbleLoading
:active-color=
"currentBubbleTextColor"
/>
</div>
<template
v-else
>
<div
class=
"mb-[10px]"
>
<i
class=
"iconfont icon-tongyi font-600 text-[14px] text-[#6ccb59]"
></i>
<span
class=
"ml-[5px] text-[14px] text-[#999]"
>
出差表单插件执行成功
</span>
</div>
<MarkdownRender
ref=
"markdownRenderRef"
raw-text-content=
"好的,我将为您自动生成出差表单,表单如下"
:color=
"currentBubbleTextColor"
/>
<!--
<div
v-if=
"messageItem.isAnswerLoading"
class=
"ml-[15px] pt-[12px]"
>
<MessageBubbleLoading
active-color=
"#fff"
width=
"5px"
/>
</div>
-->
</
template
>
</div>
<div
class=
"mt-[10px] box-content min-h-[21px] min-w-[10px] max-w-full rounded-[10px] border border-[#9EA3FF] bg-[#777EF9] px-[15px] py-[14px] text-justify"
:class=
"{
'!bg-[#fff]': isAgentMessage,
}"
>
<h2
class=
"font-600 text-[15px] text-[#0B7DFF]"
>
出差表单
</h2>
<div
class=
"mt-[12px]"
>
<n-form
:model=
"formModel"
:rules=
"formRules"
label-width=
"90"
label-placement=
"left"
:show-feedback=
"false"
>
<n-form-item
class=
"mb-[10px]"
label=
"出差目的"
path=
"objective"
>
<n-select
v-model:value=
"formModel.objective"
placeholder=
"Select"
:options=
"objectiveOptions"
/>
</n-form-item>
<n-form-item
class=
"mb-[10px]"
label=
"出差地点"
path=
"travelLocation"
>
<n-input
v-model:value=
"formModel.travelLocation"
placeholder=
"Input"
/>
</n-form-item>
<n-form-item
class=
"mb-[10px]"
label=
"出发时间"
path=
"departureTime"
>
<n-date-picker
v-model:value=
"formModel.departureTime"
type=
"datetime"
/>
</n-form-item>
<n-form-item
label=
"返回时间"
path=
"returnTime"
>
<n-date-picker
v-model:value=
"formModel.returnTime"
type=
"datetime"
/>
</n-form-item>
<n-form-item
label=
""
>
<div
class=
"h-[1px] w-full bg-[#B1B1B1] opacity-45"
></div>
</n-form-item>
<n-form-item
class=
"mb-[10px]"
label=
"交通工具"
path=
"vehicle"
>
<n-input
v-model:value=
"formModel.vehicle"
placeholder=
"Input"
/>
</n-form-item>
<n-form-item
label=
"预计费用"
path=
"vehicleEstimatedCost"
>
<n-input
v-model:value=
"formModel.vehicleEstimatedCost"
placeholder=
"Input"
/>
</n-form-item>
<n-form-item
label=
""
>
<div
class=
"h-[1px] w-full bg-[#B1B1B1] opacity-45"
></div>
</n-form-item>
<n-form-item
class=
"mb-[10px]"
label=
"住宿地点"
path=
"residence"
>
<n-input
v-model:value=
"formModel.residence"
placeholder=
"Input"
/>
</n-form-item>
<n-form-item
label=
"预计费用"
path=
"residenceEstimatedCost"
>
<n-input
v-model:value=
"formModel.residenceEstimatedCost"
placeholder=
"Input"
/>
</n-form-item>
<n-form-item
label=
""
>
<div
class=
"h-[1px] w-full bg-[#B1B1B1] opacity-45"
></div>
</n-form-item>
<n-form-item
class=
"mb-[10px]"
label=
"预计金额"
path=
"residence"
>
<n-input
v-model:value=
"formModel.estimatedAmount"
placeholder=
"Input"
/>
</n-form-item>
<n-form-item
label=
"总预算"
path=
"generalBudget"
>
<n-input
v-model:value=
"formModel.generalBudget"
placeholder=
"Input"
/>
</n-form-item>
<n-form-item
label=
""
>
<div
class=
"h-[1px] w-full bg-[#B1B1B1] opacity-45"
></div>
</n-form-item>
<n-form-item
class=
"mb-[10px]"
label=
"邮箱信息"
path=
"email"
>
<n-input
v-model:value=
"formModel.email"
placeholder=
"Input"
/>
</n-form-item>
<div
class=
"mt-[30px] text-end"
>
<n-button
type=
"primary"
>
确认提交
</n-button>
</div>
</n-form>
</div>
</div>
</div>
</div>
</template>
src/views/multi-model-dialogue/components/smart-forms/index.vue
0 → 100644
View file @
a37f91fb
<
script
setup
lang=
"ts"
>
import
type
{
MessageItemInterface
}
from
'../../types'
import
BusinessTripForm
from
'./components/business-trip-form.vue'
// import BusinessTripReimbursementForm from './business-trip-reimbursement-form.vue'
interface
Props
{
isAgentMessage
:
boolean
messageAuthor
:
string
messageItem
:
MessageItemInterface
currentBubbleTextColor
:
string
}
defineProps
<
Props
>
()
</
script
>
<
template
>
<BusinessTripForm
:message-item=
"messageItem"
:is-agent-message=
"isAgentMessage"
:message-author=
"messageAuthor"
:current-bubble-text-color=
"currentBubbleTextColor"
/>
<!--
<BusinessTripReimbursementForm
:is-agent-message=
"isAgentMessage"
:message-item=
"messageItem"
:current-bubble-text-color=
"currentBubbleTextColor"
/>
-->
</
template
>
src/views/multi-model-dialogue/components/smart-forms/types/types.d.ts
0 → 100644
View file @
a37f91fb
export
interface
ResponseBusinessTripForm
{
purpose
:
string
place
:
string
departureDate
:
string
returnDate
:
string
vehicle
:
string
transportationFee
:
string
accommodation
:
string
accommodationCost
:
string
advancePaymentAmount
:
string
totalBudget
:
string
}
export
interface
BusinessTripForm
{
objective
:
string
travelLocation
:
string
departureTime
:
number
|
null
returnTime
:
number
|
null
vehicle
:
string
vehicleEstimatedCost
:
number
|
null
residence
:
string
residenceEstimatedCost
:
number
|
null
advancePaymentAmount
:
number
|
null
generalBudget
:
string
email
:
string
}
export
type
SmartFormDisplayFormat
=
'travelForm'
src/views/multi-model-dialogue/components/smart-forms/utils/smart-forms.ts
0 → 100644
View file @
a37f91fb
import
{
BusinessTripForm
,
ResponseBusinessTripForm
}
from
'../types/types'
import
type
{
SmartFormTypes
}
from
'../../../types'
export
function
smartFormTypeConverter
(
type
:
'travelForm'
):
SmartFormTypes
{
switch
(
type
)
{
case
'travelForm'
:
return
'BusinessTripForm'
}
}
export
function
businessTripFormParser
(
resForm
:
Partial
<
ResponseBusinessTripForm
>
)
{
return
{
objective
:
resForm
.
purpose
||
'CustomerVisits'
,
travelLocation
:
resForm
.
place
||
''
,
departureTime
:
resForm
.
departureDate
?
Date
.
parse
(
resForm
.
departureDate
)
:
null
,
returnTime
:
resForm
.
returnDate
?
Date
.
parse
(
resForm
.
returnDate
)
:
null
,
vehicle
:
resForm
.
vehicle
||
''
,
vehicleEstimatedCost
:
resForm
.
transportationFee
?
Number
.
parseInt
(
resForm
.
transportationFee
)
:
null
,
residence
:
resForm
.
accommodation
||
''
,
residenceEstimatedCost
:
resForm
.
accommodationCost
?
Number
.
parseInt
(
resForm
.
accommodationCost
)
:
null
,
advancePaymentAmount
:
resForm
.
advancePaymentAmount
?
Number
.
parseInt
(
resForm
.
advancePaymentAmount
)
:
null
,
generalBudget
:
resForm
.
totalBudget
||
''
,
email
:
''
,
}
}
export
function
businessTripFormReturner
(
form
:
BusinessTripForm
)
{
return
{
purpose
:
form
.
objective
,
place
:
form
.
travelLocation
,
departureDate
:
form
.
departureTime
?
new
Date
(
form
.
departureTime
).
toISOString
()
:
''
,
returnDate
:
form
.
returnTime
?
new
Date
(
form
.
returnTime
).
toISOString
()
:
''
,
vehicle
:
form
.
vehicle
,
transportationFee
:
form
.
vehicleEstimatedCost
,
accommodation
:
form
.
residence
,
accommodationCost
:
form
.
residenceEstimatedCost
,
advancePaymentAmount
:
form
.
advancePaymentAmount
,
totalBudget
:
form
.
generalBudget
,
email
:
form
.
email
,
}
}
src/views/multi-model-dialogue/multi-model-dialogue.vue
View file @
a37f91fb
...
...
@@ -9,7 +9,7 @@ import type { ValueOf } from 'type-fest'
import
PageHeader
from
'./components/page-header.vue'
import
ModelDialogueItem
from
'./components/model-dialogue-item.vue'
import
FooterOperation
from
'./components/footer-operation.vue'
import
{
MessageItemInterface
,
MultiModelDialogueItem
,
LargeModelItem
}
from
'./types'
import
{
MessageItemInterface
,
MultiModelDialogueItem
,
LargeModelItem
,
SmartFormTypes
}
from
'./types'
import
{
fetchGetDebugApplicationInfo
,
fetchGetLargeModelInfo
,
...
...
@@ -254,9 +254,7 @@ function handleUpdateSpecifyMessageItem(messageId: string, newObj: Partial<Messa
if
(
currentMessageItemInfo
)
{
Object
.
entries
<
ValueOf
<
typeof
newObj
>>
(
newObj
).
forEach
(([
key
,
value
])
=>
{
if
(
Object
.
prototype
.
hasOwnProperty
.
call
(
currentMessageItemInfo
,
key
))
{
;(
currentMessageItemInfo
as
any
)[
key
as
keyof
MessageItemInterface
]
=
value
}
})
}
}
...
...
@@ -300,6 +298,17 @@ function handleBlockMessageResponse() {
modelDialogueItem
.
messageList
.
clear
()
})
}
function
onSmartFormsStatusFreezeCheck
(
smartFormType
:
SmartFormTypes
,
index
:
number
)
{
/* 重置智能表单项目 */
if
(
smartFormType
)
{
multiModelDialogueList
.
value
[
index
].
messageList
.
forEach
((
item
)
=>
{
if
(
item
.
smartFormInfo
&&
item
.
smartFormInfo
.
type
===
smartFormType
)
{
item
.
smartFormInfo
.
isDisabled
=
true
}
})
}
}
</
script
>
<
template
>
...
...
@@ -328,6 +337,7 @@ function handleBlockMessageResponse() {
v-for=
"(modelDialogueItem, index) in multiModelDialogueList"
:ref=
"(el) => (modelDialogueListRef[index] = el as InstanceType
<typeof
ModelDialogueItem
>
)"
:key="modelDialogueItem.id"
:model-dialogue-id="index"
:model-dialogue-item="modelDialogueItem"
:is-current="modelDialogueItem.id === multiModelDialogueList[0].id"
:model-list-options="modelListOptions"
...
...
@@ -336,6 +346,7 @@ function handleBlockMessageResponse() {
@remove-model-dialogue-item="handleRemoveModelDialogueItem"
@replace-config="handleReplaceAgentConfig"
@reset-conversation="handleResetConversation"
@update-message-item="handleUpdateSpecifyMessageItem"
/>
</div>
</main>
...
...
@@ -352,6 +363,7 @@ function handleBlockMessageResponse() {
@
delete-message-item=
"handleDeleteMessageItem"
@
clear-all-message=
"handleClearAllMessage"
@
message-list-scroll-to-bottom=
"handleMessageListScrollToBottom"
@
smart-forms-status-freeze-check=
"onSmartFormsStatusFreezeCheck"
/>
<Transition
name=
"mask"
mode=
"out-in"
>
...
...
src/views/multi-model-dialogue/types.d.ts
View file @
a37f91fb
...
...
@@ -25,6 +25,8 @@ export interface MultiModelDialogueItem {
messageList
:
Map
<
string
,
MessageItemInterface
>
}
export
type
SmartFormTypes
=
'BusinessTripForm'
export
interface
MessageItemInterface
{
role
:
'user'
|
'assistant'
avatar
:
string
...
...
@@ -38,11 +40,16 @@ export interface MessageItemInterface {
knowledgeContentResult
:
KnowledgeContentResultItem
[]
dbChainSQLContent
:
string
pluginResult
?:
{
displayFormat
:
'json'
|
'markdown'
|
'none'
displayFormat
:
PluginDisplayFormatType
pluginName
:
string
arguments
:
string
pluginContent
:
string
}
smartFormInfo
?:
{
type
:
SmartFormTypes
isDisabled
:
boolean
params
:
string
}
}
export
interface
LargeModelItem
{
...
...
src/views/multi-model-dialogue/utils/fetch-event-stream-source.ts
View file @
a37f91fb
...
...
@@ -8,7 +8,7 @@ import { languageKeyTransform } from '@/utils/language-key-transform'
interface
ResponseData
{
message
:
string
reasoningContent
:
string
function
:
{
displayFormat
:
'json'
|
'markdown'
|
'none'
;
name
:
string
;
result
:
string
;
arguments
:
string
}
function
:
{
displayFormat
:
PluginDisplayFormatType
;
name
:
string
;
result
:
string
;
arguments
:
string
}
knowledgeContentResult
:
KnowledgeContentResultItem
[]
dbChainResult
:
DBChainResultItem
[]
}
...
...
src/views/personal-space/personal-app-setting/components/agent-config/agent-preview/agent-preview.vue
View file @
a37f91fb
...
...
@@ -14,6 +14,7 @@ import { usePersonalAppConfigStore } from '@/store/modules/personal-app-config'
import
{
useSystemLanguageStore
}
from
'@/store/modules/system-language'
import
{
Brain
,
Down
}
from
'@icon-park/vue-next'
import
{
validBrowser
}
from
'@/utils/browser-detection'
import
{
SmartFormTypes
}
from
'../types'
const
{
t
}
=
useI18n
()
...
...
@@ -78,9 +79,7 @@ function handleUpdateSpecifyMessageItem(messageId: string, newMessageItem: Parti
}
Object
.
entries
<
ValueOf
<
typeof
newMessageItem
>>
(
newMessageItem
).
forEach
(([
key
,
value
])
=>
{
if
(
Object
.
prototype
.
hasOwnProperty
.
call
(
currentMessageItemInfo
,
key
))
{
;(
currentMessageItemInfo
as
any
)[
key
as
keyof
ConversationMessageItem
]
=
value
}
})
}
}
...
...
@@ -235,6 +234,17 @@ function handleAudioPause(isClearMessageList = false) {
footerInputRef
.
value
?.
blockMessageResponse
()
}
}
function
onSmartFormsStatusFreezeCheck
(
smartFormType
:
SmartFormTypes
)
{
/* 重置智能表单项目 */
if
(
smartFormType
)
{
messageList
.
value
.
forEach
((
item
)
=>
{
if
(
item
.
smartFormInfo
&&
item
.
smartFormInfo
.
type
===
smartFormType
)
{
item
.
smartFormInfo
.
isDisabled
=
true
}
})
}
}
</
script
>
<
template
>
...
...
@@ -328,6 +338,7 @@ function handleAudioPause(isClearMessageList = false) {
:create-continue-questions-exception=
"createContinueQuestionsException"
@
audio-play=
"handleAudioPlay"
@
audio-pause=
"handleAudioPause"
@
update-specify-message-item=
"handleUpdateSpecifyMessageItem"
/>
</div>
</div>
...
...
@@ -348,6 +359,7 @@ function handleAudioPause(isClearMessageList = false) {
@
update-continuous-question-status=
"handleUpdateContinueQuestionStatus"
@
audio-play=
"handleAudioPlay"
@
audio-pause=
"handleAudioPause"
@
smart-forms-status-freeze-check=
"onSmartFormsStatusFreezeCheck"
/>
<MemoryPreviewModal
v-model=
"isShowMemoryPreviewModal"
:data=
"selectedMemoryTabName"
/>
...
...
src/views/personal-space/personal-app-setting/components/agent-config/agent-preview/components/footer-input.vue
View file @
a37f91fb
...
...
@@ -13,6 +13,9 @@ import { TEXTTOSPEECH_WS_URL } from '@/config/base-url'
import
WebSocketCtr
from
'@/utils/web-socket-ctr'
import
{
ChannelType
}
from
'@/enums/channel'
import
{
useUserStore
}
from
'@/store/modules/user'
import
{
smartFormTypeConverter
}
from
'../components/smart-forms/utils/smart-forms'
import
{
SmartFormTypes
}
from
'../../types'
import
{
SmartFormDisplayFormat
}
from
'./smart-forms/types/types'
interface
Props
{
messageList
:
Map
<
string
,
ConversationMessageItem
>
...
...
@@ -35,6 +38,7 @@ const emit = defineEmits<{
updateContinuousQuestionStatus
:
[
value
:
'default'
|
'close'
]
audioPlay
:
[
messageItem
:
ConversationMessageItem
,
requestId
?:
string
]
audioPause
:
[]
smartFormsStatusFreezeCheck
:
[
value
:
SmartFormTypes
]
}
>
()
const
personalAppConfigStore
=
usePersonalAppConfigStore
()
...
...
@@ -61,6 +65,7 @@ const assistantFullAnswerContent = ref('')
const
currentAgentTimberId
=
ref
(
''
)
const
sentenceSpeechException
=
ref
(
false
)
const
messageAudioLoading
=
ref
(
false
)
const
isSmartFormPlugins
=
ref
(
false
)
// 是否智能表单插件
const
currentLatestMessageItemKeyMap
=
ref
(
new
Map
<
'assistant'
|
'user'
,
string
>
())
let
controller
:
AbortController
|
null
=
null
...
...
@@ -233,6 +238,7 @@ function handleMessageSend() {
currentAgentTimberId
.
value
=
personalAppConfigStore
.
voiceConfig
.
timbreId
sentenceSpeechException
.
value
=
false
messageAudioLoading
.
value
=
false
isSmartFormPlugins
.
value
=
false
const
isVoiceEnabled
=
!!
personalAppConfigStore
.
voiceConfig
.
timbreId
...
...
@@ -271,6 +277,31 @@ function handleMessageSend() {
// 插件
if
(
data
.
function
&&
data
.
function
.
name
)
{
// 表单插件,展示表单内容
if
([
'travelForm'
].
includes
(
data
.
function
.
displayFormat
))
{
emit
(
'smartFormsStatusFreezeCheck'
,
smartFormTypeConverter
(
data
.
function
.
displayFormat
as
SmartFormDisplayFormat
),
)
emit
(
'updateSpecifyMessageItem'
,
latestAssistantMessageKey
,
{
pluginResult
:
{
displayFormat
:
data
.
function
.
displayFormat
,
pluginName
:
data
.
function
.
name
,
arguments
:
data
.
function
.
arguments
,
pluginContent
:
data
.
function
.
result
,
},
smartFormInfo
:
{
type
:
smartFormTypeConverter
(
data
.
function
.
displayFormat
as
SmartFormDisplayFormat
),
isDisabled
:
false
,
params
:
data
.
function
.
result
,
},
})
emit
(
'updatePageScroll'
)
isSmartFormPlugins
.
value
=
true
return
}
emit
(
'updateSpecifyMessageItem'
,
latestAssistantMessageKey
,
{
pluginResult
:
{
displayFormat
:
data
.
function
.
displayFormat
,
...
...
@@ -313,7 +344,7 @@ function handleMessageSend() {
''
,
)
if
(
!
sentenceExtractCheckEnabled
.
value
&&
isVoiceEnabled
)
{
if
(
!
sentenceExtractCheckEnabled
.
value
&&
isVoiceEnabled
&&
!
isSmartFormPlugins
.
value
)
{
sentenceExtract
(
latestAssistantMessageKey
)
sentenceExtractCheckEnabled
.
value
=
true
messageAudioLoading
.
value
=
true
...
...
src/views/personal-space/personal-app-setting/components/agent-config/agent-preview/components/message-item.vue
View file @
a37f91fb
<
script
setup
lang=
"ts"
>
import
{
computed
,
ref
,
useTemplateRef
}
from
'vue'
import
{
computed
,
ref
,
useTemplateRef
,
readonly
,
provide
}
from
'vue'
import
{
useI18n
}
from
'vue-i18n'
import
{
throttle
}
from
'lodash-es'
import
{
Down
}
from
'@icon-park/vue-next'
import
CustomLoading
from
'./custom-loading.vue'
import
SmartForms
from
'./smart-forms/index.vue'
import
{
usePersonalAppConfigStore
}
from
'@/store/modules/personal-app-config'
import
MarkdownRender
from
'@/components/markdown-render/markdown-render.vue'
import
ExecuteCodeRender
from
'@/components/execute-code-render/execute-code-render.vue'
...
...
@@ -14,7 +15,7 @@ import { downloadFile } from '@/utils/download-file'
import
{
copyToClip
}
from
'@/utils/copy'
interface
Props
{
role
:
'user'
|
'assistant'
messageItemId
:
string
messageItem
:
ConversationMessageItem
}
...
...
@@ -33,8 +34,12 @@ const personalAppConfigStore = usePersonalAppConfigStore()
const
markdownRenderRef
=
useTemplateRef
<
InstanceType
<
typeof
MarkdownRender
>>
(
'markdownRenderRef'
)
provide
(
'messageItemId'
,
props
.
messageItemId
)
const
isShowReasoningContent
=
ref
(
true
)
const
displaySmartFormsList
=
readonly
([
'travelForm'
])
const
useAvatar
=
computed
(()
=>
{
return
userStore
.
userInfo
.
avatarUrl
||
'https://gsst-poe-sit.gz.bcebos.com/data/20240910/1725952917468.png'
})
...
...
@@ -43,8 +48,27 @@ const assistantAvatar = computed(() => {
return
personalAppConfigStore
.
baseInfo
.
agentAvatar
})
const
isAgentMessage
=
computed
(()
=>
{
return
props
.
messageItem
.
role
===
'assistant'
})
const
currentBubbleTextColor
=
computed
(()
=>
{
return
isAgentMessage
.
value
?
'#192338'
:
'#fff'
})
const
isShowSmartForms
=
computed
(()
=>
{
if
(
props
.
messageItem
?.
pluginResult
?.
displayFormat
)
{
return
displaySmartFormsList
.
includes
(
props
.
messageItem
?.
pluginResult
?.
displayFormat
)
}
return
false
})
const
messageAuthor
=
computed
(()
=>
{
return
isAgentMessage
.
value
?
props
.
messageItem
.
modelName
||
'AI'
:
userStore
.
userInfo
.
nickName
})
const
isShowAudioControl
=
computed
(()
=>
{
return
props
.
role
===
'assistant'
&&
!
props
.
messageItem
.
isVoiceLoading
return
isAgentMessage
.
value
&&
!
props
.
messageItem
.
isVoiceLoading
})
const
isPlayableAudio
=
computed
(()
=>
{
...
...
@@ -52,7 +76,7 @@ const isPlayableAudio = computed(() => {
})
const
isShowVoiceLoading
=
computed
(()
=>
{
return
props
.
role
===
'assistant'
&&
props
.
messageItem
.
isVoiceLoading
&&
props
.
messageItem
.
isVoiceEnabled
return
isAgentMessage
.
value
&&
props
.
messageItem
.
isVoiceLoading
&&
props
.
messageItem
.
isVoiceEnabled
})
const
isDeepSeekR1
=
computed
(()
=>
{
...
...
@@ -105,7 +129,7 @@ const handleContentCopy = throttle(
<
template
>
<div
class=
"mb-5 flex last:mb-0"
>
<NImage
:src=
"
role === 'user' ? useAvatar : assistant
Avatar"
:src=
"
isAgentMessage ? assistantAvatar : use
Avatar"
preview-disabled
:width=
"32"
:height=
"32"
...
...
@@ -113,9 +137,9 @@ const handleContentCopy = throttle(
class=
"mr-2 mt-1.5 h-8 w-8 flex-shrink-0 rounded-full"
/>
<div
class=
"flex flex-col items-start overflow-x-auto"
>
<div
v-if=
"!isShowSmartForms"
class=
"flex flex-col items-start overflow-x-auto"
>
<!-- DeepSeek大模型思考 -->
<template
v-if=
"
role === 'assistant'
&& isDeepSeekR1"
>
<template
v-if=
"
isAgentMessage
&& isDeepSeekR1"
>
<div
class=
"my-[7px] select-none text-[14px]"
>
<div
class=
"inline-flex cursor-pointer"
@
click=
"handleShowReasoningContentSwitch"
>
<span
v-if=
"messageItem.isTextContentLoading"
class=
"mr-[6px]"
>
...
...
@@ -156,17 +180,17 @@ const handleContentCopy = throttle(
<!--
大模型内容
-->
<
div
class
=
"flex min-w-[80px] max-w-full flex-col items-start overflow-x-auto"
>
<
div
class
=
"w-full flex-wrap rounded-xl border border-[#
e8e9eb
] px-4 py-[11px]"
:
class
=
"
role === 'user' ? 'user-content-container bg-[#777EF9] text-white' : 'bg-white text-[#333]
'"
class
=
"w-full flex-wrap rounded-xl border border-[#
9EA3FF
] px-4 py-[11px]"
:
class
=
"
isAgentMessage ? 'bg-white text-[#333]' : 'user-content-container bg-[#777EF9] text-white
'"
>
<
img
v
-
show
=
"
role === 'user'
&& messageItem.imageUrl"
v
-
show
=
"
!isAgentMessage
&& messageItem.imageUrl"
:
src
=
"messageItem.imageUrl"
class
=
"max-h-[120px]! mb-[11px] rounded-[10px] object-contain"
/>
<!--
插件返回结果
-->
<
div
v
-
show
=
"
role === 'assistant'
&& messageItem?.pluginResult?.pluginName"
class
=
"mb-[11px] w-full"
>
<
div
v
-
show
=
"
isAgentMessage
&& messageItem?.pluginResult?.pluginName"
class
=
"mb-[11px] w-full"
>
<
ExecutePluginRender
:
display
-
format
=
"messageItem.pluginResult?.displayFormat || 'none'"
:
name
=
"messageItem.pluginResult?.pluginName!"
...
...
@@ -197,12 +221,12 @@ const handleContentCopy = throttle(
? t('common_module.dialogue_module.empty_message_content')
: messageItem.textContent
"
:
color
=
"
role === 'user' ? '#fff' : '#192338'
"
:
color
=
"
currentBubbleTextColor
"
/>
<
/p
>
<
div
v
-
show
=
"(
role === 'assistant'
&& messageItem.isAnswerResponseLoading) || isShowVoiceLoading"
v
-
show
=
"(
isAgentMessage
&& messageItem.isAnswerResponseLoading) || isShowVoiceLoading"
class
=
"mt-4 px-4"
:
class
=
"isShowAudioControl ? 'mb-2.5' : 'mb-[5px]'"
>
...
...
@@ -242,7 +266,7 @@ const handleContentCopy = throttle(
<
div
class
=
"flex w-full items-center justify-between py-[5px]"
>
<
div
>
<
div
v
-
show
=
"
role === 'assistant'
&& messageItem.knowledgeContentResult.length"
v
-
show
=
"
isAgentMessage
&& messageItem.knowledgeContentResult.length"
class
=
"flex-center rounded-theme h-8 cursor-pointer gap-[5px] px-[13px] font-['Microsoft_YaHei_UI'] text-[#0B7DFF] hover:opacity-80"
@
click
=
"emit('showKnowledgeResult')"
>
...
...
@@ -252,7 +276,7 @@ const handleContentCopy = throttle(
<
/div
>
<
div
v
-
show
=
"
role === 'assistant'
&& messageItem.textContent && !messageItem.isAnswerResponseLoading"
v
-
show
=
"
isAgentMessage
&& messageItem.textContent && !messageItem.isAnswerResponseLoading"
class
=
"pr-[13px] text-end"
>
<
i
...
...
@@ -267,6 +291,14 @@ const handleContentCopy = throttle(
<
/div
>
<
/div
>
<
/div
>
<
SmartForms
v
-
else
:
message
-
author
=
"messageAuthor"
:
is
-
agent
-
message
=
"isAgentMessage"
:
message
-
item
=
"messageItem"
:
current
-
bubble
-
text
-
color
=
"currentBubbleTextColor"
/>
<
/div
>
<
/template
>
...
...
src/views/personal-space/personal-app-setting/components/agent-config/agent-preview/components/message-list.vue
View file @
a37f91fb
<
script
setup
lang=
"ts"
>
import
{
computed
,
nextTick
,
ref
,
useTemplateRef
,
watch
}
from
'vue'
import
{
computed
,
nextTick
,
provide
,
ref
,
useTemplateRef
,
watch
}
from
'vue'
import
{
useElementVisibility
}
from
'@vueuse/core'
import
MessageItem
from
'./message-item.vue'
import
ContinueQuestion
from
'./continue-question.vue'
...
...
@@ -16,15 +16,22 @@ interface Props {
const
props
=
defineProps
<
Props
>
()
defineEmits
<
{
const
emit
=
defineEmits
<
{
audioPlay
:
[
messageItem
:
ConversationMessageItem
]
audioPause
:
[]
updateSpecifyMessageItem
:
[
messageId
:
string
,
newMessageItem
:
Partial
<
ConversationMessageItem
>
]
}
>
()
const
{
scrollRef
,
scrollToBottom
}
=
useScroll
()
const
backBottomBtnFlagRef
=
useTemplateRef
<
HTMLDivElement
|
null
>
(
'backBottomBtnFlagRef'
)
const
isNotShowBackBottomBtn
=
useElementVisibility
(
backBottomBtnFlagRef
)
provide
(
'updateSpecifyMessageItem'
,
{
updateSpecifyMessageItem
:
(
messageId
:
string
,
newMessageItem
:
Partial
<
ConversationMessageItem
>
)
=>
{
emit
(
'updateSpecifyMessageItem'
,
messageId
,
newMessageItem
)
},
})
const
isShowKnowledgeContent
=
ref
(
false
)
const
currentKnowledgeContentResult
=
ref
<
KnowledgeContentResultItem
[]
>
([])
...
...
@@ -70,7 +77,7 @@ function handleShowKnowledgeResult(knowledgeContentResult: KnowledgeContentResul
<MessageItem
v-for=
"[key, messageItem] in messageList"
:key=
"key"
:
role=
"messageItem.role
"
:
message-item-id=
"key
"
:message-item=
"messageItem"
@
audio-play=
"() => $emit('audioPlay', messageItem)"
@
audio-pause=
"() => $emit('audioPause')"
...
...
src/views/personal-space/personal-app-setting/components/agent-config/agent-preview/components/smart-forms/components/business-trip-form.vue
0 → 100644
View file @
a37f91fb
This diff is collapsed.
Click to expand it.
src/views/personal-space/personal-app-setting/components/agent-config/agent-preview/components/smart-forms/components/business-trip-reimbursement-form.vue
0 → 100644
View file @
a37f91fb
<
script
setup
lang=
"ts"
>
import
{
readonly
,
ref
}
from
'vue'
import
MarkdownRender
from
'@/components/markdown-render/markdown-render.vue'
import
type
{
FormRules
}
from
'naive-ui'
import
CustomLoading
from
'../../custom-loading.vue'
interface
Props
{
isAgentMessage
:
boolean
messageItem
:
ConversationMessageItem
currentBubbleTextColor
:
string
}
defineProps
<
Props
>
()
const
formRules
=
readonly
<
FormRules
>
({
objective
:
{
required
:
true
,
trigger
:
[
'blur'
,
'input'
],
message
:
''
,
},
travelLocation
:
{
required
:
true
,
trigger
:
[
'blur'
,
'input'
],
message
:
''
,
},
departureTime
:
{
required
:
true
,
trigger
:
[
'blur'
,
'input'
],
message
:
''
,
},
returnTime
:
{
required
:
true
,
trigger
:
[
'blur'
,
'input'
],
message
:
''
,
},
email
:
{
required
:
true
,
trigger
:
[
'blur'
,
'input'
],
message
:
''
,
},
})
const
formModel
=
ref
({
objective
:
'khbf'
,
travelLocation
:
''
,
departureTime
:
null
,
returnTime
:
null
,
vehicle
:
''
,
vehicleEstimatedCost
:
''
,
residence
:
''
,
residenceEstimatedCost
:
''
,
estimatedAmount
:
''
,
generalBudget
:
''
,
email
:
''
,
})
const
objectiveOptions
=
readonly
([
{
label
:
'客户拜访'
,
value
:
'khbf'
,
},
{
label
:
'会议'
,
value
:
'hy'
,
},
{
label
:
'培训'
,
value
:
'px'
,
},
])
</
script
>
<
template
>
<div
class=
"ml-[11px] overflow-hidden"
>
<div
class=
"mb-[7px] text-[12px] text-[#999]"
>
作者信息
</div>
<div
class=
"min-w-[420px]"
>
<div
class=
"box-content min-h-[21px] min-w-[10px] max-w-full rounded-[10px] border border-[#9EA3FF] bg-[#777EF9] px-[15px] py-[14px] text-justify"
:class=
"
{
'!bg-[#fff]': isAgentMessage,
'!min-w-[80px]': messageItem.isAnswerResponseLoading,
}"
>
<div
v-if=
"messageItem.isAnswerResponseLoading && !messageItem.textContent"
class=
"flex h-[21px] items-center justify-center"
>
<CustomLoading
:active-color=
"currentBubbleTextColor"
/>
</div>
<template
v-else
>
<div
class=
"mb-[10px]"
>
<i
class=
"iconfont icon-tongyi font-600 text-[14px] text-[#6ccb59]"
></i>
<span
class=
"ml-[5px] text-[14px] text-[#999]"
>
出差表单插件执行成功
</span>
</div>
<MarkdownRender
ref=
"markdownRenderRef"
raw-text-content=
"好的,我将为您自动生成出差表单,表单如下"
:color=
"currentBubbleTextColor"
/>
<!--
<div
v-if=
"messageItem.isAnswerLoading"
class=
"ml-[15px] pt-[12px]"
>
<CustomLoading
active-color=
"#fff"
width=
"5px"
/>
</div>
-->
</
template
>
</div>
<div
class=
"mt-[10px] box-content min-h-[21px] min-w-[10px] max-w-full rounded-[10px] border border-[#9EA3FF] bg-[#777EF9] px-[15px] py-[14px] text-justify"
:class=
"{
'!bg-[#fff]': isAgentMessage,
}"
>
<h2
class=
"font-600 text-[15px] text-[#0B7DFF]"
>
出差表单
</h2>
<div
class=
"mt-[12px]"
>
<n-form
:model=
"formModel"
:rules=
"formRules"
label-width=
"90"
label-placement=
"left"
:show-feedback=
"false"
>
<n-form-item
class=
"mb-[10px]"
label=
"出差目的"
path=
"objective"
>
<n-select
v-model:value=
"formModel.objective"
placeholder=
"Select"
:options=
"objectiveOptions"
/>
</n-form-item>
<n-form-item
class=
"mb-[10px]"
label=
"出差地点"
path=
"travelLocation"
>
<n-input
v-model:value=
"formModel.travelLocation"
placeholder=
"Input"
/>
</n-form-item>
<n-form-item
class=
"mb-[10px]"
label=
"出发时间"
path=
"departureTime"
>
<n-date-picker
v-model:value=
"formModel.departureTime"
type=
"datetime"
/>
</n-form-item>
<n-form-item
label=
"返回时间"
path=
"returnTime"
>
<n-date-picker
v-model:value=
"formModel.returnTime"
type=
"datetime"
/>
</n-form-item>
<n-form-item
label=
""
>
<div
class=
"h-[1px] w-full bg-[#B1B1B1] opacity-45"
></div>
</n-form-item>
<n-form-item
class=
"mb-[10px]"
label=
"交通工具"
path=
"vehicle"
>
<n-input
v-model:value=
"formModel.vehicle"
placeholder=
"Input"
/>
</n-form-item>
<n-form-item
label=
"预计费用"
path=
"vehicleEstimatedCost"
>
<n-input
v-model:value=
"formModel.vehicleEstimatedCost"
placeholder=
"Input"
/>
</n-form-item>
<n-form-item
label=
""
>
<div
class=
"h-[1px] w-full bg-[#B1B1B1] opacity-45"
></div>
</n-form-item>
<n-form-item
class=
"mb-[10px]"
label=
"住宿地点"
path=
"residence"
>
<n-input
v-model:value=
"formModel.residence"
placeholder=
"Input"
/>
</n-form-item>
<n-form-item
label=
"预计费用"
path=
"residenceEstimatedCost"
>
<n-input
v-model:value=
"formModel.residenceEstimatedCost"
placeholder=
"Input"
/>
</n-form-item>
<n-form-item
label=
""
>
<div
class=
"h-[1px] w-full bg-[#B1B1B1] opacity-45"
></div>
</n-form-item>
<n-form-item
class=
"mb-[10px]"
label=
"预计金额"
path=
"residence"
>
<n-input
v-model:value=
"formModel.estimatedAmount"
placeholder=
"Input"
/>
</n-form-item>
<n-form-item
label=
"总预算"
path=
"generalBudget"
>
<n-input
v-model:value=
"formModel.generalBudget"
placeholder=
"Input"
/>
</n-form-item>
<n-form-item
label=
""
>
<div
class=
"h-[1px] w-full bg-[#B1B1B1] opacity-45"
></div>
</n-form-item>
<n-form-item
class=
"mb-[10px]"
label=
"邮箱信息"
path=
"email"
>
<n-input
v-model:value=
"formModel.email"
placeholder=
"Input"
/>
</n-form-item>
<div
class=
"mt-[30px] text-end"
>
<n-button
type=
"primary"
>
确认提交
</n-button>
</div>
</n-form>
</div>
</div>
</div>
</div>
</template>
src/views/personal-space/personal-app-setting/components/agent-config/agent-preview/components/smart-forms/index.vue
0 → 100644
View file @
a37f91fb
<
script
setup
lang=
"ts"
>
import
BusinessTripForm
from
'./components/business-trip-form.vue'
// import BusinessTripReimbursementForm from './business-trip-reimbursement-form.vue'
interface
Props
{
isAgentMessage
:
boolean
messageAuthor
:
string
messageItem
:
ConversationMessageItem
currentBubbleTextColor
:
string
}
defineProps
<
Props
>
()
</
script
>
<
template
>
<BusinessTripForm
:message-item=
"messageItem"
:is-agent-message=
"isAgentMessage"
:message-author=
"messageAuthor"
:current-bubble-text-color=
"currentBubbleTextColor"
/>
<!--
<BusinessTripReimbursementForm
:is-agent-message=
"isAgentMessage"
:message-item=
"messageItem"
:current-bubble-text-color=
"currentBubbleTextColor"
/>
-->
</
template
>
src/views/personal-space/personal-app-setting/components/agent-config/agent-preview/components/smart-forms/types/types.d.ts
0 → 100644
View file @
a37f91fb
export
interface
ResponseBusinessTripForm
{
purpose
:
string
place
:
string
departureDate
:
string
returnDate
:
string
vehicle
:
string
transportationFee
:
string
accommodation
:
string
accommodationCost
:
string
advancePaymentAmount
:
string
totalBudget
:
string
}
export
interface
BusinessTripForm
{
objective
:
string
travelLocation
:
string
departureTime
:
number
|
null
returnTime
:
number
|
null
vehicle
:
string
vehicleEstimatedCost
:
number
|
null
residence
:
string
residenceEstimatedCost
:
number
|
null
advancePaymentAmount
:
number
|
null
generalBudget
:
string
email
:
string
}
export
type
SmartFormDisplayFormat
=
'travelForm'
src/views/personal-space/personal-app-setting/components/agent-config/agent-preview/components/smart-forms/utils/smart-forms.ts
0 → 100644
View file @
a37f91fb
import
type
{
SmartFormTypes
}
from
'../../../../types'
import
{
BusinessTripForm
,
ResponseBusinessTripForm
}
from
'../types/types'
export
function
smartFormTypeConverter
(
type
:
'travelForm'
):
SmartFormTypes
{
switch
(
type
)
{
case
'travelForm'
:
return
'BusinessTripForm'
}
}
export
function
businessTripFormParser
(
resForm
:
Partial
<
ResponseBusinessTripForm
>
)
{
return
{
objective
:
resForm
.
purpose
||
'CustomerVisits'
,
travelLocation
:
resForm
.
place
||
''
,
departureTime
:
resForm
.
departureDate
?
Date
.
parse
(
resForm
.
departureDate
)
:
null
,
returnTime
:
resForm
.
returnDate
?
Date
.
parse
(
resForm
.
returnDate
)
:
null
,
vehicle
:
resForm
.
vehicle
||
''
,
vehicleEstimatedCost
:
resForm
.
transportationFee
?
Number
.
parseInt
(
resForm
.
transportationFee
)
:
null
,
residence
:
resForm
.
accommodation
||
''
,
residenceEstimatedCost
:
resForm
.
accommodationCost
?
Number
.
parseInt
(
resForm
.
accommodationCost
)
:
null
,
advancePaymentAmount
:
resForm
.
advancePaymentAmount
?
Number
.
parseInt
(
resForm
.
advancePaymentAmount
)
:
null
,
generalBudget
:
resForm
.
totalBudget
||
''
,
email
:
''
,
}
}
export
function
businessTripFormReturner
(
form
:
BusinessTripForm
)
{
return
{
purpose
:
form
.
objective
,
place
:
form
.
travelLocation
,
departureDate
:
form
.
departureTime
?
new
Date
(
form
.
departureTime
).
toISOString
()
:
''
,
returnDate
:
form
.
returnTime
?
new
Date
(
form
.
returnTime
).
toISOString
()
:
''
,
vehicle
:
form
.
vehicle
,
transportationFee
:
form
.
vehicleEstimatedCost
,
accommodation
:
form
.
residence
,
accommodationCost
:
form
.
residenceEstimatedCost
,
advancePaymentAmount
:
form
.
advancePaymentAmount
,
totalBudget
:
form
.
generalBudget
,
email
:
form
.
email
,
}
}
src/views/personal-space/personal-app-setting/components/agent-config/types.d.ts
View file @
a37f91fb
...
...
@@ -10,3 +10,5 @@ export interface TimbreLanguageInfoItem {
matchLang
:
string
timbreInfo
:
TimbreInfoItem
[]
}
export
type
SmartFormTypes
=
'BusinessTripForm'
src/views/share/components/footer-input.vue
View file @
a37f91fb
...
...
@@ -13,6 +13,9 @@ import { useLayoutConfig } from '@/composables/useLayoutConfig'
import
{
TEXTTOSPEECH_WS_URL
}
from
'@/config/base-url'
import
WebSocketCtr
from
'@/utils/web-socket-ctr'
import
{
ChannelType
}
from
'@/enums/channel'
import
{
smartFormTypeConverter
}
from
'@/views/personal-space/personal-app-setting/components/agent-config/agent-preview/components/smart-forms/utils/smart-forms'
import
{
SmartFormTypes
}
from
'@/views/personal-space/personal-app-setting/components/agent-config/types'
import
{
SmartFormDisplayFormat
}
from
'@/views/personal-space/personal-app-setting/components/agent-config/agent-preview/components/smart-forms/types/types'
interface
Props
{
agentId
:
string
...
...
@@ -43,6 +46,7 @@ const emit = defineEmits<{
resetContinueQuestionList
:
[]
audioPlay
:
[
messageItem
:
ConversationMessageItem
,
requestId
?:
string
]
audioPause
:
[]
smartFormsStatusFreezeCheck
:
[
value
:
SmartFormTypes
]
}
>
()
const
{
isMobile
}
=
useLayoutConfig
()
...
...
@@ -63,6 +67,7 @@ const sentenceExtractCheckEnabled = ref(false)
const
assistantFullAnswerContent
=
ref
(
''
)
const
sentenceSpeechException
=
ref
(
false
)
const
messageAudioLoading
=
ref
(
false
)
const
isSmartFormPlugins
=
ref
(
false
)
const
currentLatestMessageItemKeyMap
=
ref
(
new
Map
<
'assistant'
|
'user'
,
string
>
())
let
controller
:
AbortController
|
null
=
null
...
...
@@ -221,6 +226,7 @@ function handleMessageSend(lastQuestionContent?: string) {
assistantFullAnswerContent
.
value
=
''
sentenceSpeechException
.
value
=
false
messageAudioLoading
.
value
=
false
isSmartFormPlugins
.
value
=
false
controller
=
new
AbortController
()
...
...
@@ -246,6 +252,31 @@ function handleMessageSend(lastQuestionContent?: string) {
// 插件
if
(
data
.
function
&&
data
.
function
.
name
)
{
// 表单插件,展示表单内容
if
([
'travelForm'
].
includes
(
data
.
function
.
displayFormat
))
{
emit
(
'smartFormsStatusFreezeCheck'
,
smartFormTypeConverter
(
data
.
function
.
displayFormat
as
SmartFormDisplayFormat
),
)
emit
(
'updateSpecifyMessageItem'
,
latestAssistantMessageKey
,
{
pluginResult
:
{
displayFormat
:
data
.
function
.
displayFormat
,
pluginName
:
data
.
function
.
name
,
arguments
:
data
.
function
.
arguments
,
pluginContent
:
data
.
function
.
result
,
},
smartFormInfo
:
{
type
:
smartFormTypeConverter
(
data
.
function
.
displayFormat
as
SmartFormDisplayFormat
),
isDisabled
:
false
,
params
:
data
.
function
.
result
,
},
})
emit
(
'updatePageScroll'
)
isSmartFormPlugins
.
value
=
true
return
}
emit
(
'updateSpecifyMessageItem'
,
latestAssistantMessageKey
,
{
pluginResult
:
{
displayFormat
:
data
.
function
.
displayFormat
,
...
...
@@ -280,7 +311,7 @@ function handleMessageSend(lastQuestionContent?: string) {
''
,
)
if
(
!
sentenceExtractCheckEnabled
.
value
&&
props
.
isEnableVoice
)
{
if
(
!
sentenceExtractCheckEnabled
.
value
&&
props
.
isEnableVoice
&&
!
isSmartFormPlugins
.
value
)
{
sentenceExtract
(
latestAssistantMessageKey
)
sentenceExtractCheckEnabled
.
value
=
true
messageAudioLoading
.
value
=
true
...
...
src/views/share/components/message-item.vue
View file @
a37f91fb
<
script
setup
lang=
"ts"
>
import
{
computed
,
ref
,
useTemplateRef
}
from
'vue'
import
{
computed
,
provide
,
readonly
,
ref
,
useTemplateRef
}
from
'vue'
import
{
useI18n
}
from
'vue-i18n'
import
{
throttle
}
from
'lodash-es'
import
{
Down
}
from
'@icon-park/vue-next'
...
...
@@ -13,9 +13,10 @@ import { PersonalAppConfigState } from '@/store/types/personal-app-config'
import
{
useLayoutConfig
}
from
'@/composables/useLayoutConfig'
import
{
useUserStore
}
from
'@/store/modules/user'
import
{
copyToClip
}
from
'@/utils/copy'
import
SmartForms
from
'@/views/personal-space/personal-app-setting/components/agent-config/agent-preview/components/smart-forms/index.vue'
interface
Props
{
role
:
'user'
|
'assistant'
messageItemId
:
string
messageItem
:
ConversationMessageItem
agentApplicationConfig
:
PersonalAppConfigState
}
...
...
@@ -35,10 +36,14 @@ const { isMobile } = useLayoutConfig()
const
markdownRenderRef
=
useTemplateRef
<
InstanceType
<
typeof
MarkdownRender
>>
(
'markdownRenderRef'
)
provide
(
'messageItemId'
,
props
.
messageItemId
)
const
isShowReasoningContent
=
ref
(
true
)
const
isShowEditorDrawer
=
ref
(
false
)
const
editorDrawerContent
=
ref
(
''
)
const
displaySmartFormsList
=
readonly
([
'travelForm'
])
const
useAvatar
=
computed
(()
=>
{
return
userStore
.
userInfo
.
avatarUrl
||
'https://gsst-poe-sit.gz.bcebos.com/data/20240910/1725952917468.png'
})
...
...
@@ -50,12 +55,31 @@ const assistantAvatar = computed(() => {
)
})
const
isAgentMessage
=
computed
(()
=>
{
return
props
.
messageItem
.
role
===
'assistant'
})
const
currentBubbleTextColor
=
computed
(()
=>
{
return
isAgentMessage
.
value
?
'#192338'
:
'#fff'
})
const
isShowSmartForms
=
computed
(()
=>
{
if
(
props
.
messageItem
?.
pluginResult
?.
displayFormat
)
{
return
displaySmartFormsList
.
includes
(
props
.
messageItem
?.
pluginResult
?.
displayFormat
)
}
return
false
})
const
messageAuthor
=
computed
(()
=>
{
return
isAgentMessage
.
value
?
props
.
messageItem
.
modelName
||
'AI'
:
userStore
.
userInfo
.
nickName
})
const
timbreEnabled
=
computed
(()
=>
{
return
!!
props
.
agentApplicationConfig
.
voiceConfig
.
timbreId
})
const
isShowAudioControl
=
computed
(()
=>
{
return
props
.
role
===
'assistant'
&&
!
props
.
messageItem
.
isVoiceLoading
&&
timbreEnabled
.
value
return
isAgentMessage
.
value
&&
!
props
.
messageItem
.
isVoiceLoading
&&
timbreEnabled
.
value
})
const
isPlayableAudio
=
computed
(()
=>
{
...
...
@@ -64,7 +88,7 @@ const isPlayableAudio = computed(() => {
const
isShowVoiceLoading
=
computed
(()
=>
{
return
(
props
.
role
===
'assistant'
&&
isAgentMessage
.
value
&&
(
props
.
messageItem
.
isAnswerResponseLoading
||
(
props
.
messageItem
.
isVoiceLoading
&&
timbreEnabled
.
value
))
)
})
...
...
@@ -113,14 +137,11 @@ const handleContentCopy = throttle(
<
template
>
<div
class=
"mb-5 flex last:mb-0"
:class=
"[
isMobile ? 'flex-row-reverse' : 'flex-row',
role === 'assistant' && isMobile ? 'justify-end' : 'justify-start',
]"
:class=
"[isMobile ? 'flex-row-reverse' : 'flex-row', isAgentMessage && isMobile ? 'justify-end' : 'justify-start']"
>
<NImage
v-show=
"!isMobile"
:src=
"
role === 'user' ? useAvatar : assistant
Avatar"
:src=
"
isAgentMessage ? assistantAvatar : use
Avatar"
preview-disabled
:width=
"32"
:height=
"32"
...
...
@@ -129,11 +150,12 @@ const handleContentCopy = throttle(
/>
<div
v-if=
"!isShowSmartForms"
class=
"flex flex-col overflow-x-auto"
:class=
"[isMobile &&
role === 'user'
? 'items-end' : 'items-start', isMobile ? 'w-full' : '']"
:class=
"[isMobile &&
!isAgentMessage
? 'items-end' : 'items-start', isMobile ? 'w-full' : '']"
>
<!-- 大模型深度思考 -->
<template
v-if=
"
role === 'assistant'
&& isDeepSeekR1"
>
<template
v-if=
"
isAgentMessage
&& isDeepSeekR1"
>
<div
class=
"my-[7px] select-none text-[14px]"
>
<div
class=
"inline-flex cursor-pointer"
@
click=
"handleShowReasoningContentSwitch"
>
<span
v-if=
"messageItem.isTextContentLoading"
class=
"mr-[6px]"
>
...
...
@@ -174,17 +196,17 @@ const handleContentCopy = throttle(
<!--
模型内容
-->
<
div
class
=
"flex min-w-[80px] flex-col"
:
class
=
"[isMobile ? 'max-w-[calc(100%-20px)]' : 'max-w-full']"
>
<
div
class
=
"w-full flex-wrap rounded-xl border border-[#
e8e9eb
] px-4 py-[11px]"
:
class
=
"[
role === 'user' ? 'user-content-container bg-[#777EF9] text-white' : 'bg-white text-[#333]
']"
class
=
"w-full flex-wrap rounded-xl border border-[#
9EA3FF
] px-4 py-[11px]"
:
class
=
"[
isAgentMessage ? 'bg-white text-[#333]' : 'user-content-container bg-[#777EF9] text-white
']"
>
<
img
v
-
show
=
"
role === 'user'
&& messageItem.imageUrl"
v
-
show
=
"
!isAgentMessage
&& messageItem.imageUrl"
:
src
=
"messageItem.imageUrl"
class
=
"max-h-[120px]! mb-[11px] rounded-[10px] object-contain"
/>
<!--
插件返回结果
-->
<
div
v
-
show
=
"
role === 'assistant'
&& messageItem?.pluginResult?.pluginName"
class
=
"mb-[11px] w-full"
>
<
div
v
-
show
=
"
isAgentMessage
&& messageItem?.pluginResult?.pluginName"
class
=
"mb-[11px] w-full"
>
<
ExecutePluginRender
:
display
-
format
=
"messageItem.pluginResult?.displayFormat || 'none'"
:
name
=
"messageItem.pluginResult?.pluginName!"
...
...
@@ -215,7 +237,7 @@ const handleContentCopy = throttle(
? t('common_module.dialogue_module.empty_message_content')
: messageItem.textContent
"
:
color
=
"
role === 'user' ? '#fff' : '#192338'
"
:
color
=
"
currentBubbleTextColor
"
/>
<
/p
>
...
...
@@ -280,7 +302,7 @@ const handleContentCopy = throttle(
<
div
><
/div
>
<
div
v
-
show
=
"
role === 'assistant'
&& messageItem.textContent && !messageItem.isAnswerResponseLoading"
v
-
show
=
"
isAgentMessage
&& messageItem.textContent && !messageItem.isAnswerResponseLoading"
class
=
"py-[10px] pr-[14px] text-end"
>
<
i
...
...
@@ -295,6 +317,14 @@ const handleContentCopy = throttle(
<
/div
>
<
/div
>
<
/div
>
<
SmartForms
v
-
else
:
message
-
author
=
"messageAuthor"
:
is
-
agent
-
message
=
"isAgentMessage"
:
message
-
item
=
"messageItem"
:
current
-
bubble
-
text
-
color
=
"currentBubbleTextColor"
/>
<
/div
>
<
EditorDrawer
v
-
model
:
is
-
show
-
editor
-
drawer
=
"isShowEditorDrawer"
v
-
model
:
content
-
edit
=
"editorDrawerContent"
/>
...
...
src/views/share/components/message-list.vue
View file @
a37f91fb
<
script
setup
lang=
"ts"
>
import
{
computed
,
nextTick
,
useTemplateRef
,
watch
}
from
'vue'
import
{
computed
,
nextTick
,
provide
,
useTemplateRef
,
watch
}
from
'vue'
import
{
useI18n
}
from
'vue-i18n'
import
{
useElementVisibility
}
from
'@vueuse/core'
import
MessageItem
from
'./message-item.vue'
...
...
@@ -20,9 +20,10 @@ interface Props {
const
props
=
defineProps
<
Props
>
()
defineEmits
<
{
const
emit
=
defineEmits
<
{
audioPlay
:
[
messageItem
:
ConversationMessageItem
]
audioPause
:
[]
updateSpecifyMessageItem
:
[
messageId
:
string
,
newMessageItem
:
Partial
<
ConversationMessageItem
>
]
}
>
()
const
{
t
}
=
useI18n
()
...
...
@@ -33,6 +34,12 @@ const { scrollRef, scrollToBottom } = useScroll()
const
backBottomBtnFlagRef
=
useTemplateRef
<
HTMLDivElement
|
null
>
(
'backBottomBtnFlagRef'
)
const
isNotShowBackBottomBtn
=
useElementVisibility
(
backBottomBtnFlagRef
)
provide
(
'updateSpecifyMessageItem'
,
{
updateSpecifyMessageItem
:
(
messageId
:
string
,
newMessageItem
:
Partial
<
ConversationMessageItem
>
)
=>
{
emit
(
'updateSpecifyMessageItem'
,
messageId
,
newMessageItem
)
},
})
const
isShowContinueQuestion
=
computed
(()
=>
{
return
(
props
.
continuousQuestionStatus
===
'default'
&&
...
...
@@ -71,7 +78,7 @@ function handleScrollToBottom() {
<MessageItem
v-for=
"[key, messageItem] in messageList"
:key=
"key"
:
role=
"messageItem.role
"
:
message-item-id=
"key
"
:message-item=
"messageItem"
:agent-application-config=
"agentApplicationConfig"
@
audio-play=
"() => $emit('audioPlay', messageItem)"
...
...
src/views/share/share-application-web.vue
View file @
a37f91fb
...
...
@@ -22,6 +22,7 @@ import { useLayoutConfig } from '@/composables/useLayoutConfig'
import
{
fetchGetMemberInfoById
}
from
'@/apis/user'
import
{
UserInfo
}
from
'@/store/types/user'
import
{
validBrowser
}
from
'@/utils/browser-detection'
import
{
SmartFormTypes
}
from
'../personal-space/personal-app-setting/components/agent-config/types'
const
{
t
}
=
useI18n
()
...
...
@@ -177,9 +178,7 @@ function handleUpdateSpecifyMessageItem(messageId: string, newMessageItem: Parti
}
Object
.
entries
<
ValueOf
<
typeof
newMessageItem
>>
(
newMessageItem
).
forEach
(([
key
,
value
])
=>
{
if
(
Object
.
prototype
.
hasOwnProperty
.
call
(
currentMessageItemInfo
,
key
))
{
;(
currentMessageItemInfo
as
any
)[
key
as
keyof
ConversationMessageItem
]
=
value
}
})
}
}
...
...
@@ -313,6 +312,17 @@ function handleAudioPause(isClearMessageList = false) {
footerInputRef
.
value
?.
blockMessageResponse
()
}
}
function
onSmartFormsStatusFreezeCheck
(
smartFormType
:
SmartFormTypes
)
{
/* 重置智能表单项目 */
if
(
smartFormType
)
{
messageList
.
value
.
forEach
((
item
)
=>
{
if
(
item
.
smartFormInfo
&&
item
.
smartFormInfo
.
type
===
smartFormType
)
{
item
.
smartFormInfo
.
isDisabled
=
true
}
})
}
}
</
script
>
<
template
>
...
...
@@ -352,6 +362,7 @@ function handleAudioPause(isClearMessageList = false) {
:create-continue-questions-exception=
"createContinueQuestionsException"
@
audio-play=
"handleAudioPlay"
@
audio-pause=
"handleAudioPause"
@
update-specify-message-item=
"handleUpdateSpecifyMessageItem"
/>
</div>
</div>
...
...
@@ -380,6 +391,7 @@ function handleAudioPause(isClearMessageList = false) {
@
reset-continue-question-list=
"handleResetContinueQuestionList"
@
audio-play=
"handleAudioPlay"
@
audio-pause=
"handleAudioPause"
@
smart-forms-status-freeze-check=
"onSmartFormsStatusFreezeCheck"
/>
</div>
</div>
...
...
types/conversation.d.ts
View file @
a37f91fb
...
...
@@ -12,6 +12,8 @@ declare interface DBChainResultItem {
status
:
string
}
declare
type
PluginDisplayFormatType
=
'json'
|
'markdown'
|
'none'
|
'travelForm'
declare
interface
ConversationMessageItem
{
timestamp
:
number
role
:
'user'
|
'assistant'
...
...
@@ -29,9 +31,14 @@ declare interface ConversationMessageItem {
knowledgeContentResult
:
KnowledgeContentResultItem
[]
dbChainSQLContent
:
string
pluginResult
?:
{
displayFormat
:
'json'
|
'markdown'
|
'none'
displayFormat
:
PluginDisplayFormatType
pluginName
:
string
arguments
:
string
pluginContent
:
string
}
smartFormInfo
?:
{
type
:
'BusinessTripForm'
|
''
isDisabled
:
boolean
params
:
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