Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Contribute to GitLab
Sign in / Register
Toggle navigation
C
CharIP-Electron
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
ali
CharIP-Electron
Commits
d9ea3ea9
You need to sign in or sign up before continuing.
Commit
d9ea3ea9
authored
Jan 08, 2024
by
ali
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
feat: 照片数字人展示对话内容
parent
8a378618
Show whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
326 additions
and
2 deletions
+326
-2
AnswerBox.vue
src/renderer/components/common/AnswerBox.vue
+291
-0
Ellipsis.gif
src/renderer/public/images/Ellipsis.gif
+0
-0
ShowPhoto.vue
src/renderer/screens/ShowPhoto.vue
+35
-2
No files found.
src/renderer/components/common/AnswerBox.vue
0 → 100644
View file @
d9ea3ea9
<
script
setup
lang=
"ts"
>
import
{
ref
}
from
'vue'
;
interface
Props
{
visible
?:
boolean
;
question
?:
string
;
loading
?:
boolean
;
answerData
:
{
answerType
:
string
;
data
:
string
[];
subType
:
'text'
|
'image'
|
'video'
|
'relatelist'
|
'guslist'
|
'waitImage'
;
value
:
string
;
}[];
}
interface
EmitType
{
(
e
:
'inputQuestion'
,
value
:
string
):
void
;
}
const
emits
=
defineEmits
<
EmitType
>
();
withDefaults
(
defineProps
<
Props
>
(),
{
answerData
()
{
return
[];
},
visible
:
true
,
loading
:
false
,
question
:
''
});
const
isPreview
=
ref
(
false
);
const
previewUrl
=
ref
(
''
);
function
previewImg
(
url
:
string
)
{
previewUrl
.
value
=
url
;
isPreview
.
value
=
true
;
}
const
handleUrl
=
(
url
:
string
)
=>
{
if
(
url
===
''
)
return
''
;
return
`https://bf-saas.emotibot.com
${
url
}
`
;
};
function
inputQuestion
(
t
:
string
)
{
emits
(
'inputQuestion'
,
t
);
}
const
isPlay
=
ref
(
false
);
function
playVideo
()
{
(
document
.
querySelector
(
'#video'
)
as
HTMLVideoElement
).
play
();
isPlay
.
value
=
true
;
}
const
loadingImg
=
new
URL
(
'/images/Ellipsis.gif'
,
import
.
meta
.
url
).
href
;
</
script
>
<
template
>
<div
:style=
"
{ width: '170px' }"
class="ans-layout"
>
<div
v-show=
"question && question !== ''"
class=
"question-box"
>
<div
class=
"question-con"
>
<div
class=
"scroll"
>
{{
question
}}
</div>
</div>
</div>
<div
v-show=
"answerData.length > 0 && visible"
class=
"answer-box vertical"
>
<div
class=
"scroll"
:style=
"
{ 'max-height': '249.6px' }"
>
<div
v-for=
"(item, index) of answerData"
:key=
"index"
class=
"ans-item"
>
<!-- 文字 -->
<div
v-if=
"item.subType === 'text'"
class=
"text-box"
v-html=
"item.value"
></div>
<!-- 图片 -->
<div
v-else-if=
"item.subType === 'image'"
class=
"img-box"
>
<img
:src=
"handleUrl(item.value)"
@
click=
"previewImg(handleUrl(item.value))"
/>
</div>
<!-- 视频 -->
<div
v-else-if=
"item.subType === 'video'"
class=
"video-box"
>
<video
id=
"video"
controls
:src=
"handleUrl(item.value)"
></video>
<div
v-show=
"!isPlay"
class=
"play-mask"
>
<div
class=
"play"
@
click=
"playVideo()"
>
<img
src=
"@/assets/helper/play.svg"
/>
</div>
</div>
</div>
<!-- 相似问 -->
<div
v-else-if=
"item.subType === 'relatelist' || item.subType === 'guslist'"
class=
"recommend-box"
>
<div
class=
"text"
>
{{
item
.
value
}}
</div>
<ul
class=
"glist"
>
<li
v-for=
"(gitem, gindex) of item.data"
:key=
"gindex"
@
click=
"inputQuestion(gitem)"
>
{{
`${gindex + 1
}
. ${gitem
}
`
}}
<
/li
>
<
/ul
>
<
/div
>
<!--
等待
gif
-->
<
div
v
-
if
=
"loading"
class
=
"text-waitImage"
>
<
img
:
src
=
"loadingImg"
/>
<
/div
>
<
/div
>
<
/div
>
<
/div
>
<
/div
>
<
teleport
to
=
"body"
>
<
div
v
-
if
=
"isPreview"
class
=
"preview-box"
@
click
=
"isPreview = false"
>
<
div
class
=
"img"
:
style
=
"{ backgroundImage: `url(${previewUrl
}
)`
}
"
><
/div
>
<
/div
>
<
/teleport
>
<
/template
>
<
style
scoped
>
.
ans
-
layout
{
position
:
fixed
;
top
:
50
%
;
right
:
30
px
;
transform
:
translateY
(
-
130
px
);
}
.
question
-
box
{
width
:
100
%
;
display
:
flex
;
justify
-
content
:
right
;
color
:
#
FFFFFF
;
margin
-
bottom
:
10
px
;
}
.
question
-
box
.
question
-
con
{
max
-
width
:
100
%
;
background
:
rgba
(
70
,
121
,
254
,
0.7
);
border
-
radius
:
30
px
;
padding
:
15
px
;
}
.
question
-
box
.
question
-
con
.
scroll
{
max
-
height
:
57
px
;
}
.
answer
-
box
{
background
:
rgba
(
255
,
255
,
255
,
0.7
);
border
-
radius
:
30
px
;
border
:
1
px
solid
#
ffffff
;
color
:
#
333333
;
font
-
weight
:
400
;
}
.
answer
-
box
.
horizontal
{
padding
:
14
px
13
px
;
}
.
answer
-
box
.
vertical
{
padding
:
16
px
;
}
.
text
-
box
{
margin
-
bottom
:
15
px
;
}
.
img
-
box
,
.
video
-
box
{
width
:
100
%
;
border
-
radius
:
4
px
4
px
4
px
4
px
;
overflow
:
hidden
;
margin
-
bottom
:
15
px
;
position
:
relative
;
}
.
img
-
box
img
,
.
video
-
box
img
,
.
img
-
box
video
,
.
video
-
box
video
{
width
:
100
%
;
}
.
play
-
mask
{
width
:
100
%
;
height
:
100
%
;
display
:
flex
;
justify
-
content
:
center
;
align
-
items
:
center
;
position
:
absolute
;
top
:
0
;
left
:
0
;
cursor
:
pointer
;
}
.
text
-
waitImage
img
{
width
:
20
%
;
margin
:
0
auto
;
display
:
block
;
}
.
play
{
width
:
44
px
;
height
:
44
px
;
border
:
none
;
border
-
radius
:
50
%
;
opacity
:
0.75
;
}
.
preview
-
box
{
width
:
100
%
;
height
:
100
%
;
position
:
fixed
;
background
:
rgba
(
0
,
0
,
0
,
0.3
);
top
:
0
;
left
:
0
;
z
-
index
:
3
;
display
:
flex
;
align
-
items
:
center
;
justify
-
content
:
center
;
}
.
preview
-
box
.
img
{
width
:
80
%
;
height
:
80
%
;
background
-
repeat
:
no
-
repeat
;
background
-
size
:
contain
;
background
-
position
:
center
center
;
}
.
glist
>
li
{
cursor
:
pointer
;
text
-
decoration
:
underline
;
font
-
weight
:
400
;
color
:
#
6464
f8
;
line
-
height
:
24
px
;
}
.
glist
>
li
:
hover
,
.
glist
>
li
:
active
{
color
:
#
2
eacff
;
}
.
scroll
{
height
:
100
%
;
overflow
-
y
:
auto
;
}
.
scroll
::
-
webkit
-
scrollbar
{
width
:
6
px
;
height
:
8
px
;
}
.
scroll
::
-
webkit
-
scrollbar
-
track
{
background
:
transparent
;
border
-
radius
:
4
px
;
}
.
scroll
::
-
webkit
-
scrollbar
-
thumb
{
background
:
#
d8dee9
;
border
-
radius
:
4
px
;
}
.
scroll
::
-
webkit
-
scrollbar
-
thumb
:
hover
{
background
:
#
bdbdbe
;
}
@
media
(
width
:
254
px
)
{
.
ans
-
layout
{
min
-
width
:
150
px
;
transform
:
translateY
(
-
50
%
)
scale
(
0.78
)
!
important
;
transform
-
origin
:
left
center
;
}
}
<
/style
>
src/renderer/public/images/Ellipsis.gif
0 → 100644
View file @
d9ea3ea9
93.8 KB
src/renderer/screens/ShowPhoto.vue
View file @
d9ea3ea9
<!-- eslint-disable no-unused-vars -->
<!-- eslint-disable camelcase -->
<
script
setup
lang=
"ts"
>
import
{
onMounted
,
ref
}
from
'vue'
import
{
onMounted
,
ref
,
type
Ref
}
from
'vue'
import
{
useRoute
,
useRouter
}
from
'vue-router'
import
type
{
ServerMessagePartialResult
,
...
...
@@ -12,6 +12,7 @@ import { audioAiTTS, localTTS } from '../plugins/tts'
import
useStore
from
'@/renderer/store'
import
flvjs
from
'flv.js'
import
{
PhotoAnswer
,
PhotoRole
}
from
'@/renderer/plugins/live/PhotoRole'
import
AnswerBox
from
"@/renderer/components/common/AnswerBox.vue"
;
const
router
=
useRouter
()
const
route
=
useRoute
()
...
...
@@ -37,10 +38,22 @@ const inputContext: {
asrPartial
:
string
answerArray
:
{
text
:
string
;
isLast
:
boolean
}[]
steps
:
Promise
<
string
>
[]
answerProp
:
Ref
<
{
visible
:
boolean
;
question
:
string
;
loading
:
boolean
answerData
:
{
answerType
:
string
;
data
:
string
[];
subType
:
'text'
|
'image'
|
'video'
|
'relatelist'
|
'guslist'
;
value
:
string
;
}[]
}
>
}
=
{
asrPartial
:
''
,
answerArray
:
[],
steps
:
[]
steps
:
[],
answerProp
:
ref
({
question
:
''
,
loading
:
false
,
visible
:
false
,
answerData
:
[]
})
}
router
.
beforeEach
((
g
)
=>
{
...
...
@@ -102,6 +115,10 @@ async function init() {
}
async function onAsyncAnswer(ans: PhotoAnswer) {
if (inputContext.answerProp.value.answerData[0]) {
inputContext.answerProp.value.answerData[0].value = ans.asyncAnswer;
}
if (ans.playState === 'playing') {
microphoneState.value = 'reply'
return
...
...
@@ -259,6 +276,10 @@ async function endAudioInput() {
inputContext.asrPartial = ''
inputContext.answerArray.length = 0
inputContext.steps.length = 0
inputContext.answerProp.value.answerData.length = 0;
inputContext.answerProp.value.visible = false;
inputContext.answerProp.value.question = '';
inputContext.answerProp.value.loading = false;
// @ts-ignore
photoRole?.off('asyncAnswer', onAsyncAnswer)
await photoRole?.destroy()
...
...
@@ -299,6 +320,8 @@ async function onQ(question: string) {
console.log('----------------> question: ', question)
microphoneState.value = 'loading'
inputContext.answerProp.value.question = question;
inputContext.answerProp.value.loading = true;
const { pose, stepResolve, stepReject } = createStep()
inputContext.steps.length = 0
...
...
@@ -334,6 +357,13 @@ async function llmLoop(question: string) {
}
photoRole
!
.
answerArgs
=
answer
inputContext
.
answerProp
.
value
.
visible
=
true
;
inputContext
.
answerProp
.
value
.
answerData
.
push
({
subType
:
'text'
,
value
:
''
,
answerType
:
''
,
data
:
[]
})
// @ts-ignore
photoRole
!
.
off
(
'asyncAnswer'
,
onAsyncAnswer
)
photoRole
!
.
on
(
'asyncAnswer'
,
onAsyncAnswer
)
...
...
@@ -363,6 +393,7 @@ async function llmLoop(question: string) {
const
audioList
=
results
[
0
].
audio_list
as
string
[]
if
(
audioList
.
length
===
0
)
continue
const
isEnd
=
audioList
.
at
(
-
1
)
===
'stream_end'
inputContext
.
answerProp
.
value
.
loading
=
!
isEnd
if
(
isEnd
)
audioList
.
pop
()
...
...
@@ -482,6 +513,8 @@ async function down() {
<v-btn
color=
"red"
variant=
"text"
@
click=
"errorSnackbar = false"
>
Close
</v-btn>
</
template
>
</v-snackbar>
<answer-box
:loading=
"inputContext.answerProp.value.loading"
:visible=
"inputContext.answerProp.value.visible"
:question=
"inputContext.answerProp.value.question"
:answer-data=
"inputContext.answerProp.value.answerData"
></answer-box>
</template>
<
style
scoped
>
.voice
{
...
...
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