Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Contribute to GitLab
Sign in
Toggle navigation
D
digitalPerson-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
digitalPerson
digitalPerson-fe
Commits
27a61770
Commit
27a61770
authored
Sep 26, 2024
by
Dazzle Wu
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
feat: 视频保存联调
parent
74423a94
Hide whitespace changes
Inline
Side-by-side
Showing
14 changed files
with
429 additions
and
159 deletions
+429
-159
digital-creation.ts
src/apis/digital-creation.ts
+12
-2
creation.ts
src/store/modules/creation.ts
+42
-55
creation.ts
src/store/types/creation.ts
+103
-21
background-setting.vue
...ews/creation/components/background/background-setting.vue
+30
-17
digital-audio-card.vue
src/views/creation/components/digital/digital-audio-card.vue
+6
-2
digital-audio.vue
src/views/creation/components/digital/digital-audio.vue
+13
-4
digital-human-card.vue
src/views/creation/components/digital/digital-human-card.vue
+4
-4
digital-human.vue
src/views/creation/components/digital/digital-human.vue
+22
-11
digital-position.vue
src/views/creation/components/digital/digital-position.vue
+4
-4
preview-content.vue
src/views/creation/components/preview-content.vue
+70
-8
script-content.vue
src/views/creation/components/script-content.vue
+12
-3
subtitle-setting.vue
src/views/creation/components/subtitle/subtitle-setting.vue
+1
-1
header-bar.vue
src/views/creation/layout/header-bar.vue
+70
-16
index.vue
src/views/creation/layout/index.vue
+40
-11
No files found.
src/apis/digital-creation.ts
View file @
27a61770
...
...
@@ -22,7 +22,7 @@ export function fetch2DFewShotImageList<T>() {
// 获取背景图
export
function
fetchBackgroundImage
<
T
>
()
{
return
request
.
post
<
T
>
(
'/
bizDigitalHumanImageRest/getBackgroundImage
.json'
)
return
request
.
post
<
T
>
(
'/
aiDigitalHumanImageRest/getBackgroundImageList
.json'
)
}
// 上传背景图片
...
...
@@ -33,10 +33,15 @@ export function uploadImageFile<T>(imageName: string, formData: FormData) {
})
}
// 根据背景图id删除背景图
export
function
deleteBackgroundImageById
<
T
>
(
id
:
number
)
{
return
request
.
post
<
T
>
(
`/bizDigitalHumanMemberImageRest/deletedById.json?id=
${
id
}
`
)
}
// 根据人物名称分页获取人物信息
export
function
fetchInfoByImageName
<
T
>
(
imageName
:
string
)
{
return
request
.
post
<
T
>
(
`/bizDigitalHumanImageRest/findByImageName.json?imageName=
${
imageName
}
`
,
{
pagingInfo
:
{
pageNo
:
1
,
pageSize
:
10
},
pagingInfo
:
{
pageNo
:
1
,
pageSize
:
9999
},
})
}
...
...
@@ -50,6 +55,11 @@ export function fetchTimbreByExample<T>(condition: string) {
return
request
.
post
<
T
>
(
`/bizDigitalHumanTimbreRest/getByExample.json?condition=
${
condition
}
`
)
}
// 保存当前用户的草稿配置
export
function
saveDraftConfig
<
T
>
(
payload
:
object
)
{
return
request
.
post
<
T
>
(
'/bizDigitalHumanMemberDraftConfigRest/saveOrUpdate.json'
,
payload
)
}
// 基础数字人视频
export
function
createBaseVideoDigitalHumanTask
<
T
>
(
callbackUrl
:
string
,
payload
:
object
)
{
return
request
.
post
<
T
>
(
...
...
src/store/modules/creation.ts
View file @
27a61770
import
{
DraftConfig
,
DriveType
,
TaskType
}
from
'@/store/types/creation'
import
{
defineStore
}
from
'pinia'
import
{
DigitalTemplate
}
from
'@/store/types/creation'
function
defaultDigitalCreation
():
D
igitalTemplate
{
function
defaultDigitalCreation
():
D
raftConfig
{
return
{
id
:
null
,
coverUrl
:
null
,
demonstrationGifUrl
:
null
,
demonstrationVideoUrl
:
null
,
templateType
:
null
,
templateName
:
null
,
taskType
:
'BASE_VIDEO'
,
draftName
:
''
,
videoName
:
''
,
taskType
:
TaskType
.
BASE_VIDEO
,
requestId
:
null
,
inputImageUrl
:
null
,
driveType
:
'TEXT'
,
text
:
null
,
ttsParams
:
{
person
:
null
,
speed
:
'5'
,
volume
:
'5'
,
pitch
:
'5'
,
},
driveType
:
DriveType
.
TEXT
,
text
:
''
,
person
:
null
,
speed
:
'5'
,
volume
:
'5'
,
pitch
:
'5'
,
inputAudioUrl
:
null
,
callbackUrl
:
null
,
figureId
:
null
,
digitalImageUrl
:
null
,
videoParams
:
{
width
:
0
,
height
:
0
,
transparent
:
false
,
},
dhParams
:
{
cameraId
:
null
,
position
:
{
x
:
0
,
y
:
0
,
w
:
0
,
h
:
0
,
},
},
subtitleParams
:
{
subtitlePolicy
:
'SRT'
,
enabled
:
false
,
},
width
:
720
,
height
:
1280
,
transparent
:
'N'
,
cameraId
:
null
,
x
:
0
,
y
:
0
,
w
:
0
,
h
:
0
,
subtitlePolicy
:
'SRT'
,
enabled
:
'N'
,
backgroundImageUrl
:
null
,
autoAnimoji
:
false
,
enablePalindrome
:
false
,
autoAnimoji
:
'N'
,
enablePalindrome
:
'N'
,
templateId
:
null
,
title
:
null
,
logoParams
:
{
logoUrl
:
null
,
},
bgmParams
:
{
bgmUrl
:
null
,
},
logoUrl
:
null
,
bgmUrl
:
null
,
materialUrl
:
null
,
}
}
function
getLocalState
():
D
igitalTemplate
{
function
getLocalState
():
D
raftConfig
{
return
defaultDigitalCreation
()
}
export
const
useDigitalCreationStore
=
defineStore
(
'digital-creation-store'
,
{
state
:
():
D
igitalTemplate
=>
getLocalState
(),
state
:
():
D
raftConfig
=>
getLocalState
(),
actions
:
{
setFigureId
(
figureId
:
string
)
{
this
.
figureId
=
figureId
},
setText
(
text
:
string
)
{
this
.
text
=
text
},
setDigitalImageUrl
(
digitalImageUrl
:
string
)
{
this
.
digital
ImageUrl
=
digitalImageUrl
this
.
input
ImageUrl
=
digitalImageUrl
},
setPerson
(
person
:
string
)
{
this
.
ttsParams
.
person
=
person
this
.
person
=
person
},
setSpeed
(
speed
:
string
)
{
this
.
ttsParams
.
speed
=
speed
this
.
speed
=
speed
},
setPitch
(
pitch
:
string
)
{
this
.
ttsParams
.
pitch
=
pitch
this
.
pitch
=
pitch
},
setDigitalImagePositionX
(
x
:
number
)
{
this
.
dhParams
.
position
.
x
=
x
this
.
x
=
x
},
setDigitalImagePositionY
(
y
:
number
)
{
this
.
dhParams
.
position
.
y
=
y
this
.
y
=
y
},
setDigitalImagePositionW
(
w
:
number
)
{
this
.
dhParams
.
position
.
w
=
w
this
.
w
=
w
},
setDigitalImagePositionH
(
h
:
number
)
{
this
.
dhParams
.
position
.
h
=
h
this
.
h
=
h
},
setBackgroundImageUrl
(
backgroundImageUrl
:
string
)
{
this
.
backgroundImageUrl
=
backgroundImageUrl
},
setSubtitleEnabled
(
subtitleEnabled
:
boolean
)
{
this
.
subtitleParams
.
enabled
=
subtitleEnabled
setSubtitleEnabled
(
subtitleEnabled
:
string
)
{
this
.
enabled
=
subtitleEnabled
},
updateDigitalCreation
(
digitalCreation
:
D
igitalTemplate
)
{
updateDigitalCreation
(
digitalCreation
:
D
raftConfig
)
{
this
.
$state
=
{
...
this
.
$state
,
...
digitalCreation
}
},
...
...
src/store/types/creation.ts
View file @
27a61770
export
enum
ImageType
{
THREE_D
=
'THREE_D'
,
TWO_D_BOUTIQUE
=
'TWO_D_BOUTIQUE'
,
TWO_D_FEW_SHOT
=
'TWO_D_FEW_SHOT'
,
}
export
enum
TaskType
{
IMAGE_VIDEO
=
'IMAGE_VIDEO'
,
BASE_VIDEO
=
'BASE_VIDEO'
,
ADVANCED_VIDEO
=
'ADVANCED_VIDEO'
,
}
export
enum
DriveType
{
TEXT
=
'TEXT'
,
VOICE
=
'VOICE'
,
}
export
interface
DigitalTemplate
{
id
:
number
|
null
id
:
number
coverUrl
:
string
|
null
demonstrationGifUrl
:
string
|
null
demonstrationVideoUrl
:
string
|
null
templateType
:
string
|
null
templateName
:
string
|
null
taskType
:
string
templateType
:
string
templateName
:
string
taskType
:
TaskType
requestId
:
string
|
null
inputImageUrl
:
string
|
null
driveType
:
string
text
:
string
|
null
driveType
:
DriveType
text
:
string
ttsParams
:
{
person
:
string
|
null
person
:
string
speed
:
string
volume
:
string
pitch
:
string
}
inputAudioUrl
:
string
|
null
callbackUrl
:
string
|
null
figureId
:
string
|
null
digitalImageUrl
?:
string
|
null
figureId
:
string
videoParams
:
{
width
:
number
|
null
height
:
number
|
null
width
:
number
height
:
number
transparent
:
boolean
}
dhParams
:
{
...
...
@@ -35,7 +51,7 @@ export interface DigitalTemplate {
}
}
subtitleParams
:
{
subtitlePolicy
:
string
|
null
subtitlePolicy
:
string
enabled
:
boolean
}
backgroundImageUrl
:
string
|
null
...
...
@@ -52,18 +68,18 @@ export interface DigitalTemplate {
materialUrl
:
string
|
null
}
export
enum
ImageType
{
THREE_D
=
'THREE_D'
,
TWO_D_BOUTIQUE
=
'TWO_D_BOUTIQUE'
,
TWO_D_FEW_SHOT
=
'TWO_D_FEW_SHOT'
,
BACKGROUND
=
'BACKGROUND'
,
export
interface
DigitalImageItem
{
id
:
number
imageType
:
ImageType
imageName
:
string
figureId
:
string
imageUrl
:
string
}
export
interface
ImageItem
{
export
interface
Background
ImageItem
{
id
:
number
imageType
:
ImageType
imageName
:
string
|
null
figureId
:
string
|
null
imageSource
:
string
imageName
:
string
imageUrl
:
string
}
...
...
@@ -75,4 +91,70 @@ export interface TimbreItem {
style
:
string
[]
applyScene
:
string
[]
audioUrl
:
string
iconUrl
:
string
|
null
}
export
interface
TextScript
{
codec
:
string
sampleRate
:
number
speed
:
number
volume
:
number
voiceType
:
number
content
:
string
}
export
interface
DraftConfig
{
id
:
number
|
null
coverUrl
:
string
|
null
draftName
:
string
videoName
:
string
taskType
:
TaskType
requestId
:
string
|
null
inputImageUrl
:
string
|
null
driveType
:
DriveType
text
:
string
person
:
string
|
null
speed
:
string
volume
:
string
pitch
:
string
inputAudioUrl
:
string
|
null
callbackUrl
:
string
|
null
figureId
:
string
|
null
width
:
number
height
:
number
transparent
:
string
cameraId
:
number
|
null
x
:
number
y
:
number
w
:
number
h
:
number
subtitlePolicy
:
string
enabled
:
string
backgroundImageUrl
:
string
|
null
autoAnimoji
:
string
enablePalindrome
:
string
templateId
:
string
|
null
title
:
string
|
null
logoUrl
:
string
|
null
bgmUrl
:
string
|
null
materialUrl
:
string
|
null
}
export
interface
BaseVideoTask
{
figureId
:
string
|
null
driveType
:
string
|
null
text
:
string
|
null
ttsParams
:
{
preson
:
string
|
null
speed
?:
string
|
null
pitch
?:
string
|
null
volume
?:
string
|
null
}
videoParams
:
{
width
:
number
height
:
number
transparent
?:
string
|
null
}
backgroundImageUrl
:
string
|
null
autoAnimoji
:
string
|
null
}
src/views/creation/components/background/background-setting.vue
View file @
27a61770
<
script
setup
lang=
"ts"
>
import
{
fetchBackgroundImage
,
fetchInfoByImageName
,
uploadImageFile
}
from
'@/apis/digital-creation'
import
{
deleteBackgroundImageById
,
fetchBackgroundImage
,
fetchInfoByImageName
,
uploadImageFile
,
}
from
'@/apis/digital-creation'
import
{
useDigitalCreationStore
}
from
'@/store/modules/creation'
import
{
ImageItem
}
from
'@/store/types/creation'
import
{
ref
}
from
'vue'
import
{
Background
ImageItem
}
from
'@/store/types/creation'
import
{
onMounted
,
ref
}
from
'vue'
const
digitalCreationStore
=
useDigitalCreationStore
()
const
imageList
=
ref
<
ImageItem
[]
>
([])
const
imageList
=
ref
<
Background
ImageItem
[]
>
([])
const
searchName
=
ref
(
''
)
const
uploadLoading
=
ref
(
false
)
const
loaded
=
ref
(
Array
(
imageList
.
value
.
length
).
fill
(
false
))
getBackgroundImageList
()
onMounted
(()
=>
{
getBackgroundImageList
()
})
async
function
getBackgroundImageList
()
{
const
res
=
await
fetchBackgroundImage
<
ImageItem
[]
>
()
const
res
=
await
fetchBackgroundImage
<
Background
ImageItem
[]
>
()
if
(
res
.
code
===
0
)
{
imageList
.
value
=
res
.
data
.
map
((
i
)
=>
({
...
i
,
checked
:
i
.
imageUrl
===
digitalCreationStore
.
backgroundImageUrl
}))
imageList
.
value
=
res
.
data
}
}
async
function
handleSearch
(
value
:
string
)
{
const
res
=
await
fetchInfoByImageName
<
ImageItem
[]
>
(
value
)
if
(
!
value
)
{
getBackgroundImageList
()
return
}
const
res
=
await
fetchInfoByImageName
<
BackgroundImageItem
[]
>
(
value
)
if
(
res
.
code
===
0
)
{
imageList
.
value
=
res
.
data
}
...
...
@@ -60,21 +71,22 @@ async function uploadImage(event: any) {
}
}
function
handleClickImage
(
image
:
ImageItem
)
{
function
handleClickImage
(
image
:
Background
ImageItem
)
{
digitalCreationStore
.
setBackgroundImageUrl
(
image
.
imageUrl
)
}
function
handleDelete
()
{
function
handleDelete
(
id
:
number
)
{
window
.
$dialog
.
warning
({
title
:
'刪除圖片'
,
content
:
'是否刪除該圖片?'
,
positiveText
:
'是'
,
negativeText
:
'否'
,
onPositiveClick
:
()
=>
{
window
.
$message
.
success
(
'刪除成功'
)
},
onNegativeClick
:
()
=>
{
window
.
$message
.
error
(
'刪除失敗'
)
onPositiveClick
:
async
()
=>
{
const
res
=
await
deleteBackgroundImageById
(
id
)
if
(
res
.
code
===
0
)
{
window
.
$message
.
success
(
'刪除成功'
)
getBackgroundImageList
()
}
},
})
}
...
...
@@ -118,13 +130,14 @@ function onImageLoaded(index: number) {
>
<img
class=
"h-full w-full object-contain"
:src=
"image.imageUrl"
@
load=
"onImageLoaded(index)"
/>
<div
class=
"absolute bottom-0 h-5 w-full bg-gradient-to-t from-gray-600 px-1 text-xs leading-5 text-white"
class=
"absolute bottom-0 h-5 w-full
truncate
bg-gradient-to-t from-gray-600 px-1 text-xs leading-5 text-white"
>
{{ image.imageName }}
</div>
<div
v-if=
"image.id"
class=
"absolute right-1 top-1 hidden h-7 w-7 cursor-pointer items-center justify-center rounded-md bg-black/40 p-1 group-hover:flex"
@
click
.
stop=
"handleDelete"
@
click
.
stop=
"handleDelete
(image.id)
"
>
<CustomIcon
icon=
"mi:delete"
class=
"text-lg text-white"
/>
</div>
...
...
src/views/creation/components/digital/digital-audio-card.vue
View file @
27a61770
...
...
@@ -19,7 +19,7 @@ const emit = defineEmits<Emits>()
const
digitalCreationStore
=
useDigitalCreationStore
()
const
digitalAudio
=
ref
<
HTMLAudioElement
>
()
const
person
=
computed
(()
=>
digitalCreationStore
.
ttsParams
.
person
)
const
person
=
computed
(()
=>
digitalCreationStore
.
person
)
function
playAudio
()
{
digitalAudio
.
value
?.
play
()
...
...
@@ -32,7 +32,10 @@ function playAudio() {
:class=
"!showToggle && value?.timebreId === person ? 'border-blue' : 'border-gray-200'"
@
click=
"emit('click', value!)"
>
<div
class=
"h-16 w-16 rounded-lg bg-gray-200"
></div>
<div
class=
"flex h-16 w-16 items-center justify-center overflow-hidden rounded-lg bg-gray-200"
>
<img
v-if=
"value?.iconUrl"
:src=
"value.iconUrl"
class=
"h-full w-full"
/>
<CustomIcon
v-else
class=
"text-gray text-4xl"
:icon=
"value?.sex === '男' ? 'fontisto:male' : 'fontisto:famale'"
/>
</div>
<div
class=
"flex-1 overflow-hidden"
>
<div
class=
"mb-2 flex items-center gap-2"
>
<div
class=
"max-w-32 truncate"
>
{{
value
?.
name
}}
</div>
...
...
@@ -46,5 +49,6 @@ function playAudio() {
<CustomIcon
class=
"cursor-pointer text-lg"
icon=
"ant-design:swap-outlined"
@
click=
"emit('toggle', true)"
/>
</div>
</div>
<audio
ref=
"digitalAudio"
:src=
"value?.audioUrl"
></audio>
</
template
>
src/views/creation/components/digital/digital-audio.vue
View file @
27a61770
...
...
@@ -6,6 +6,11 @@ import { computed, onMounted, ref, watch } from 'vue'
import
DigitalAudioCard
from
'./digital-audio-card.vue'
const
digitalCreationStore
=
useDigitalCreationStore
()
const
lanValue
=
ref
(
0
)
const
lanList
=
ref
([
{
key
:
0
,
label
:
'粵語'
},
{
key
:
1
,
label
:
'普通話'
},
])
const
sexValue
=
ref
(
0
)
const
sexList
=
[
{
key
:
0
,
label
:
'女性'
},
...
...
@@ -20,7 +25,7 @@ const searchName = ref('')
const
speed
=
computed
({
get
()
{
return
Number
(
digitalCreationStore
.
ttsParams
.
speed
)
return
Number
(
digitalCreationStore
.
speed
)
},
set
(
value
)
{
digitalCreationStore
.
setSpeed
(
String
(
value
))
...
...
@@ -29,7 +34,7 @@ const speed = computed({
const
pitch
=
computed
({
get
()
{
return
Number
(
digitalCreationStore
.
ttsParams
.
pitch
)
return
Number
(
digitalCreationStore
.
pitch
)
},
set
(
value
)
{
digitalCreationStore
.
setPitch
(
String
(
value
))
...
...
@@ -37,7 +42,7 @@ const pitch = computed({
})
watch
(
()
=>
[
digitalCreationStore
.
ttsParams
.
person
,
digitalTimbreList
.
value
.
length
],
()
=>
[
digitalCreationStore
.
person
,
digitalTimbreList
.
value
.
length
],
([
person
,
len
])
=>
{
if
(
person
&&
len
)
{
digitalTimbreValue
.
value
=
digitalTimbreList
.
value
.
find
((
i
)
=>
i
.
timebreId
===
person
)
...
...
@@ -75,7 +80,11 @@ function handleClickAudioCard(timbreItem: TimbreItem) {
<
template
>
<div
class=
"h-full overflow-y-auto px-4 py-2"
>
<div
v-if=
"!showAll"
>
<DigitalAudioCard
:value=
"digitalTimbreValue"
show-toggle
@
toggle=
"showAll = true"
/>
<div
class=
"flex justify-end pb-3"
>
<HorizontalTabs
v-model:value=
"lanValue"
:list=
"lanList"
/>
</div>
<DigitalAudioCard
v-if=
"lanValue"
:value=
"digitalTimbreValue"
show-toggle
@
toggle=
"showAll = true"
/>
<div
class=
"mt-4 text-lg"
>
聲音
</div>
<div
class=
"mt-4 flex items-center gap-2"
>
...
...
src/views/creation/components/digital/digital-human-card.vue
View file @
27a61770
<
script
setup
lang=
"ts"
>
import
{
useDigitalCreationStore
}
from
'@/store/modules/creation'
import
{
ImageItem
}
from
'@/store/types/creation'
import
{
Digital
ImageItem
}
from
'@/store/types/creation'
interface
Props
{
value
:
ImageItem
value
:
Digital
ImageItem
}
interface
Emits
{
(
e
:
'click'
,
value
:
ImageItem
):
void
(
e
:
'click'
,
value
:
Digital
ImageItem
):
void
}
defineProps
<
Props
>
()
...
...
@@ -22,7 +22,7 @@ const digitalCreationStore = useDigitalCreationStore()
:class=
"digitalCreationStore.figureId === value.figureId ? 'border-blue' : 'border-transparent'"
@
click=
"emit('click', value)"
>
<img
:src=
"value.imageUrl"
/>
<img
:src=
"value.imageUrl"
class=
"h-full w-full"
/>
<div
class=
"absolute bottom-0 h-5 w-full bg-gradient-to-t from-gray-600 px-1 text-xs leading-5 text-white"
>
{{
value
.
imageName
}}
</div>
...
...
src/views/creation/components/digital/digital-human.vue
View file @
27a61770
...
...
@@ -6,41 +6,52 @@ import {
fetchInfoByImageName
,
}
from
'@/apis/digital-creation'
import
{
useDigitalCreationStore
}
from
'@/store/modules/creation'
import
{
ImageItem
,
ImageType
}
from
'@/store/types/creation'
import
{
onMounted
,
ref
}
from
'vue'
import
{
Digital
ImageItem
,
ImageType
}
from
'@/store/types/creation'
import
{
onMounted
,
ref
,
watch
}
from
'vue'
import
DigitalCard
from
'./digital-human-card.vue'
const
digitalCreationStore
=
useDigitalCreationStore
()
const
threeDImageList
=
ref
<
ImageItem
[]
>
([])
const
twoDBoutiqueImageList
=
ref
<
ImageItem
[]
>
([])
const
twoDFewShotImageList
=
ref
<
ImageItem
[]
>
([])
const
allImageList
=
ref
<
ImageItem
[]
>
([])
const
threeDImageList
=
ref
<
Digital
ImageItem
[]
>
([])
const
twoDBoutiqueImageList
=
ref
<
Digital
ImageItem
[]
>
([])
const
twoDFewShotImageList
=
ref
<
Digital
ImageItem
[]
>
([])
const
allImageList
=
ref
<
Digital
ImageItem
[]
>
([])
const
showAll
=
ref
(
false
)
const
searchName
=
ref
(
''
)
watch
(
()
=>
[
digitalCreationStore
.
figureId
,
allImageList
.
value
.
length
],
([
figureId
,
len
])
=>
{
if
(
!
digitalCreationStore
.
inputImageUrl
&&
figureId
&&
len
)
{
const
imageUrl
=
allImageList
.
value
.
find
((
i
)
=>
i
.
figureId
===
figureId
)?.
imageUrl
imageUrl
&&
digitalCreationStore
.
setDigitalImageUrl
(
imageUrl
)
}
},
)
onMounted
(()
=>
{
getDigitalImageList
()
})
async
function
getDigitalImageList
()
{
const
[
res1
,
res2
,
res3
]
=
await
Promise
.
all
([
fetch3DImageList
<
ImageItem
[]
>
(),
fetch2DBoutiqueImageList
<
ImageItem
[]
>
(),
fetch2DFewShotImageList
<
ImageItem
[]
>
(),
fetch3DImageList
<
Digital
ImageItem
[]
>
(),
fetch2DBoutiqueImageList
<
Digital
ImageItem
[]
>
(),
fetch2DFewShotImageList
<
Digital
ImageItem
[]
>
(),
])
res1
.
code
===
0
&&
(
threeDImageList
.
value
=
res1
.
data
)
res2
.
code
===
0
&&
(
twoDBoutiqueImageList
.
value
=
res2
.
data
)
res3
.
code
===
0
&&
(
twoDFewShotImageList
.
value
=
res3
.
data
)
allImageList
.
value
=
[...
threeDImageList
.
value
,
...
twoDBoutiqueImageList
.
value
,
...
twoDFewShotImageList
.
value
]
}
async
function
handleSearch
(
value
:
string
)
{
const
res
=
await
fetchInfoByImageName
<
ImageItem
[]
>
(
value
)
const
res
=
await
fetchInfoByImageName
<
Digital
ImageItem
[]
>
(
value
)
if
(
res
.
code
===
0
)
{
allImageList
.
value
=
res
.
data
}
}
function
handleClickDigitalImage
(
digitalItem
:
ImageItem
)
{
function
handleClickDigitalImage
(
digitalItem
:
Digital
ImageItem
)
{
digitalCreationStore
.
setFigureId
(
digitalItem
.
figureId
!
)
digitalCreationStore
.
setDigitalImageUrl
(
digitalItem
.
imageUrl
)
}
...
...
src/views/creation/components/digital/digital-position.vue
View file @
27a61770
...
...
@@ -6,7 +6,7 @@ const digitalCreationStore = useDigitalCreationStore()
const
digitalImagePositionX
=
computed
({
get
()
{
return
digitalCreationStore
.
dhParams
.
position
.
x
return
digitalCreationStore
.
x
},
set
(
value
)
{
digitalCreationStore
.
setDigitalImagePositionX
(
value
)
...
...
@@ -15,7 +15,7 @@ const digitalImagePositionX = computed({
const
digitalImagePositionY
=
computed
({
get
()
{
return
digitalCreationStore
.
dhParams
.
position
.
y
return
digitalCreationStore
.
y
},
set
(
value
)
{
digitalCreationStore
.
setDigitalImagePositionY
(
value
)
...
...
@@ -24,7 +24,7 @@ const digitalImagePositionY = computed({
const
digitalImagePositionW
=
computed
({
get
()
{
return
digitalCreationStore
.
dhParams
.
position
.
w
return
digitalCreationStore
.
w
},
set
(
width
)
{
const
height
=
(
width
*
16
)
/
9
...
...
@@ -35,7 +35,7 @@ const digitalImagePositionW = computed({
const
digitalImagePositionH
=
computed
({
get
()
{
return
digitalCreationStore
.
dhParams
.
position
.
h
return
digitalCreationStore
.
h
},
set
(
height
)
{
const
width
=
(
height
*
9
)
/
16
...
...
src/views/creation/components/preview-content.vue
View file @
27a61770
<
script
setup
lang=
"ts"
>
import
{
useDigitalCreationStore
}
from
'@/store/modules/creation'
import
{
TextScript
}
from
'@/store/types/creation'
import
{
computed
,
ref
}
from
'vue'
const
digitalCreationStore
=
useDigitalCreationStore
()
const
previewContent
=
ref
<
HTMLElement
>
()
const
digitalAudio
=
ref
<
HTMLAudioElement
>
()
const
previewContentWidth
=
computed
(()
=>
previewContent
.
value
?.
offsetWidth
)
const
previewContentHeight
=
computed
(()
=>
previewContent
.
value
?.
offsetHeight
)
const
digitalHumanPosition
=
computed
(()
=>
digitalCreationStore
.
dhParams
.
position
)
const
digitalHumanWidth
=
computed
(()
=>
(
digitalHumanPosition
.
value
.
w
*
previewContentWidth
.
value
!
)
/
1080
)
const
digitalHumanHeight
=
computed
(()
=>
(
digitalHumanPosition
.
value
.
h
*
previewContentHeight
.
value
!
)
/
1920
)
const
digitalHumanLeft
=
computed
(()
=>
(
digitalHumanPosition
.
value
.
x
*
previewContentWidth
.
value
!
)
/
1080
)
const
digitalHumanTop
=
computed
(()
=>
(
digitalHumanPosition
.
value
.
y
*
previewContentHeight
.
value
!
)
/
1920
)
const
digitalHumanWidth
=
computed
(()
=>
(
digitalCreationStore
.
w
*
previewContentWidth
.
value
!
)
/
1080
)
const
digitalHumanHeight
=
computed
(()
=>
(
digitalCreationStore
.
h
*
previewContentHeight
.
value
!
)
/
1920
)
const
digitalHumanLeft
=
computed
(()
=>
(
digitalCreationStore
.
x
*
previewContentWidth
.
value
!
)
/
1080
)
const
digitalHumanTop
=
computed
(()
=>
(
digitalCreationStore
.
y
*
previewContentHeight
.
value
!
)
/
1920
)
const
url
=
'wss://ai-api-sit.gsstcloud.com/websocket/textToSpeechTC.ws'
const
isConnected
=
ref
(
false
)
const
audioData
=
ref
(
''
)
const
voiceUrl
=
ref
(
''
)
let
websocket
:
WebSocket
function
connectWebSocket
()
{
websocket
=
new
WebSocket
(
url
)
websocket
.
onopen
=
()
=>
{
isConnected
.
value
=
true
sendDataToWebSocket
()
}
websocket
.
onclose
=
()
=>
{
isConnected
.
value
=
false
}
websocket
.
onmessage
=
(
event
)
=>
{
if
(
event
.
data
)
{
const
data
=
JSON
.
parse
(
event
.
data
)
data
.
audio
&&
(
audioData
.
value
+=
data
.
audio
)
data
.
replyVoiceUrl
&&
(
voiceUrl
.
value
=
data
.
replyVoiceUrl
)
data
.
final
&&
disconnectWebSocket
()
}
}
websocket
.
onerror
=
(
error
)
=>
{
console
.
error
(
'WebSocket error:'
,
error
)
}
}
function
disconnectWebSocket
()
{
console
.
log
(
'audio'
,
audioData
.
value
)
if
(
websocket
)
{
websocket
.
close
()
}
}
function
sendDataToWebSocket
()
{
const
payload
:
TextScript
=
{
codec
:
'mp3'
,
sampleRate
:
16000
,
speed
:
Number
(
digitalCreationStore
.
speed
),
volume
:
Number
(
digitalCreationStore
.
volume
),
voiceType
:
1018
,
content
:
digitalCreationStore
.
text
,
}
websocket
.
send
(
JSON
.
stringify
(
payload
))
}
function
generatePreview
()
{
connectWebSocket
()
}
function
playAudio
()
{
digitalAudio
.
value
?.
play
()
}
</
script
>
<
template
>
...
...
@@ -24,8 +83,8 @@ const digitalHumanTop = computed(() => (digitalHumanPosition.value.y * previewCo
class=
"absolute h-full w-full object-cover"
/>
<img
v-
show=
"digitalCreationStore.digital
ImageUrl"
:src=
"digitalCreationStore.
digitalImageUrl!
"
v-
if=
"digitalCreationStore.input
ImageUrl"
:src=
"digitalCreationStore.
inputImageUrl
"
class=
"absolute max-w-none object-fill"
:style=
"
{
width: `${digitalHumanWidth}px`,
...
...
@@ -39,7 +98,8 @@ const digitalHumanTop = computed(() => (digitalHumanPosition.value.y * previewCo
<div
class=
"flex bg-white p-4"
>
<div
class=
"flex-1 text-lg"
>
00:11:22
</div>
<div
class=
"flex flex-1 justify-center"
>
<CustomIcon
class=
"cursor-pointer text-2xl"
icon=
"ph:play"
/>
<n-button
v-if=
"!audioData"
type=
"info"
:loading=
"isConnected"
@
click=
"generatePreview"
>
生成预览
</n-button>
<CustomIcon
v-else
class=
"cursor-pointer text-2xl"
icon=
"ph:play"
@
click=
"playAudio"
/>
</div>
<div
class=
"flex flex-1 items-center justify-end gap-4"
>
<CustomIcon
class=
"cursor-pointer text-lg"
icon=
"mingcute:volume-line"
/>
...
...
@@ -47,4 +107,6 @@ const digitalHumanTop = computed(() => (digitalHumanPosition.value.y * previewCo
</div>
</div>
</div>
<audio
ref=
"digitalAudio"
:src=
"voiceUrl"
></audio>
</
template
>
src/views/creation/components/script-content.vue
View file @
27a61770
<
script
setup
lang=
"ts"
>
import
{
ref
}
from
'vue'
import
{
useDigitalCreationStore
}
from
'@/store/modules/creation'
import
{
computed
}
from
'vue'
const
value
=
ref
(
''
)
const
digitalCreationStore
=
useDigitalCreationStore
()
const
text
=
computed
({
get
()
{
return
digitalCreationStore
.
text
},
set
(
value
)
{
digitalCreationStore
.
setText
(
value
)
},
})
</
script
>
<
template
>
<div
class=
"rounded-2xl bg-white p-6"
>
<div
class=
"pb-2"
>
脚本
</div>
<n-input
v-model:value=
"
value
"
v-model:value=
"
text
"
type=
"textarea"
:maxlength=
"2000"
show-count
...
...
src/views/creation/components/subtitle/subtitle-setting.vue
View file @
27a61770
...
...
@@ -6,7 +6,7 @@ const digitalCreationStore = useDigitalCreationStore()
const
subtitleEnabled
=
computed
({
get
()
{
return
digitalCreationStore
.
subtitleParams
.
enabled
return
digitalCreationStore
.
enabled
},
set
(
value
)
{
digitalCreationStore
.
setSubtitleEnabled
(
value
)
...
...
src/views/creation/layout/header-bar.vue
View file @
27a61770
<
script
setup
lang=
"ts"
>
import
{
ref
}
from
'vue'
import
{
createBaseVideoDigitalHumanTask
,
saveDraftConfig
}
from
'@/apis/digital-creation'
import
{
useDigitalCreationStore
}
from
'@/store/modules/creation'
import
{
BaseVideoTask
,
DraftConfig
}
from
'@/store/types/creation'
import
{
ref
,
watch
}
from
'vue'
const
digitalCreationStore
=
useDigitalCreationStore
()
const
showExportModal
=
ref
(
false
)
const
exportForm
=
ref
({
name
:
''
,
ratio
:
''
,
range
:
''
,
format
:
''
,
ratio
:
720
,
transparent
:
false
,
format
:
'
mp4
'
,
})
const
ratioList
=
[
{
value
:
'720'
,
label
:
'720p'
},
{
value
:
'1080'
,
label
:
'1080p'
},
{
value
:
720
,
label
:
'720p'
},
{
value
:
1080
,
label
:
'1080p'
},
]
const
rangeLis
t
=
[
{
value
:
'all'
,
label
:
'全部'
},
{
value
:
'digital'
,
label
:
'僅數字人(透明背景)'
},
const
transparen
t
=
[
{
value
:
false
,
label
:
'全部'
},
{
value
:
true
,
label
:
'僅數字人(透明背景)'
},
]
const
formatList
=
[{
value
:
'mp4'
,
label
:
'MP4'
}]
const
formatList
=
[
{
value
:
'mp4'
,
label
:
'MP4'
},
{
value
:
'webm'
,
label
:
'WEBM'
},
]
watch
(
()
=>
exportForm
.
value
.
transparent
,
(
val
)
=>
{
exportForm
.
value
.
format
=
val
?
'webm'
:
'mp4'
},
)
function
confirmExport
()
{
showExportModal
.
value
=
false
if
(
!
exportForm
.
value
.
name
)
{
window
.
$message
.
error
(
'請輸入視頻名稱'
)
return
false
}
createBaseVideoTask
()
}
async
function
createBaseVideoTask
()
{
const
payload
:
BaseVideoTask
=
{
figureId
:
digitalCreationStore
.
figureId
,
driveType
:
digitalCreationStore
.
driveType
,
text
:
digitalCreationStore
.
text
,
ttsParams
:
{
preson
:
digitalCreationStore
.
person
,
speed
:
digitalCreationStore
.
speed
,
pitch
:
digitalCreationStore
.
pitch
,
volume
:
digitalCreationStore
.
volume
,
},
videoParams
:
{
width
:
digitalCreationStore
.
width
,
height
:
digitalCreationStore
.
height
,
transparent
:
digitalCreationStore
.
transparent
,
},
backgroundImageUrl
:
digitalCreationStore
.
backgroundImageUrl
,
autoAnimoji
:
digitalCreationStore
.
autoAnimoji
,
}
const
res
=
await
createBaseVideoDigitalHumanTask
(
''
,
payload
)
if
(
res
.
code
===
0
)
{
window
.
$message
.
success
(
'導出成功'
)
showExportModal
.
value
=
false
}
}
async
function
saveDraft
()
{
const
payload
:
{
draftConfigDto
:
DraftConfig
}
=
{
draftConfigDto
:
digitalCreationStore
.
$state
,
}
const
res
=
await
saveDraftConfig
(
payload
)
if
(
res
.
code
===
0
)
{
window
.
$message
.
success
(
'保存成功'
)
}
}
</
script
>
...
...
@@ -36,7 +90,7 @@ function confirmExport() {
<CustomIcon
class=
"text-green"
icon=
"ep:success-filled"
/>
<span>
已自動保存
</span>
</div>
<n-button
class=
"!rounded-md"
>
保存爲草稿
</n-button>
<n-button
class=
"!rounded-md"
@
click=
"saveDraft"
>
保存爲草稿
</n-button>
<n-button
class=
"!rounded-md"
type=
"info"
@
click=
"showExportModal = true"
>
導出視頻
</n-button>
</div>
</div>
...
...
@@ -65,16 +119,16 @@ function confirmExport() {
</n-radio-group>
</n-form-item>
<n-form-item
label=
"導出範圍"
required
>
<n-radio-group
v-model:value=
"exportForm.
range"
name=
"range
"
>
<n-radio-group
v-model:value=
"exportForm.
transparent"
name=
"transparent
"
>
<n-space>
<n-radio
v-for=
"
range in rangeList"
:key=
"range.value"
:value=
"range
.value"
>
{{
range
.
label
}}
<n-radio
v-for=
"
item in transparent"
:key=
"item.value"
:value=
"item
.value"
>
{{
item
.
label
}}
</n-radio>
</n-space>
</n-radio-group>
</n-form-item>
<n-form-item
label=
"視頻格式"
required
>
<n-radio-group
v-model:value=
"exportForm.format"
name=
"format"
>
<n-radio-group
v-model:value=
"exportForm.format"
name=
"format"
disabled
>
<n-space>
<n-radio
v-for=
"format in formatList"
:key=
"format.value"
:value=
"format.value"
>
{{
format
.
label
}}
...
...
src/views/creation/layout/index.vue
View file @
27a61770
<
script
setup
lang=
"ts"
>
import
{
fetchDigitalHumanTemplateStatus
}
from
'@/apis/digital-creation'
import
{
useDigitalCreationStore
}
from
'@/store/modules/creation'
import
{
DigitalTemplate
}
from
'@/store/types/creation'
import
{
DigitalTemplate
,
DraftConfig
}
from
'@/store/types/creation'
import
{
onMounted
}
from
'vue'
import
HeaderBar
from
'./header-bar.vue'
import
MainContent
from
'./main-content.vue'
...
...
@@ -10,21 +10,50 @@ import SideBar from './side-bar.vue'
const
digitalCreationStore
=
useDigitalCreationStore
()
onMounted
(()
=>
{
getDigital
ImageList
(
1
)
getDigital
Template
(
1
)
})
async
function
getDigital
ImageList
(
id
:
number
)
{
async
function
getDigital
Template
(
id
:
number
)
{
const
res
=
await
fetchDigitalHumanTemplateStatus
<
DigitalTemplate
>
(
id
)
if
(
res
.
code
===
0
)
{
const
digitalTemplate
=
res
.
data
const
{
dhParams
:
{
position
},
}
=
digitalTemplate
position
.
x
=
position
.
x
||
0
position
.
y
=
position
.
y
||
0
position
.
w
=
position
.
w
||
1080
position
.
h
=
position
.
h
||
1920
digitalCreationStore
.
updateDigitalCreation
(
digitalTemplate
)
const
draftConfig
:
DraftConfig
=
{
id
:
null
,
coverUrl
:
digitalTemplate
.
coverUrl
,
draftName
:
''
,
videoName
:
''
,
taskType
:
digitalTemplate
.
taskType
,
requestId
:
digitalTemplate
.
requestId
,
inputImageUrl
:
null
,
driveType
:
digitalTemplate
.
driveType
,
text
:
digitalTemplate
.
text
,
person
:
digitalTemplate
.
ttsParams
.
person
,
speed
:
digitalTemplate
.
ttsParams
.
speed
,
volume
:
digitalTemplate
.
ttsParams
.
volume
,
pitch
:
digitalTemplate
.
ttsParams
.
pitch
,
inputAudioUrl
:
digitalTemplate
.
inputAudioUrl
,
callbackUrl
:
digitalTemplate
.
callbackUrl
,
figureId
:
digitalTemplate
.
figureId
,
width
:
digitalTemplate
.
videoParams
.
width
,
height
:
digitalTemplate
.
videoParams
.
height
,
transparent
:
digitalTemplate
.
videoParams
.
transparent
?
'Y'
:
'N'
,
cameraId
:
digitalTemplate
.
dhParams
.
cameraId
,
x
:
digitalTemplate
.
dhParams
.
position
.
x
,
y
:
digitalTemplate
.
dhParams
.
position
.
y
,
w
:
digitalTemplate
.
dhParams
.
position
.
w
,
h
:
digitalTemplate
.
dhParams
.
position
.
h
,
subtitlePolicy
:
digitalTemplate
.
subtitleParams
.
subtitlePolicy
,
enabled
:
digitalTemplate
.
subtitleParams
.
enabled
?
'Y'
:
'N'
,
backgroundImageUrl
:
digitalTemplate
.
backgroundImageUrl
,
autoAnimoji
:
digitalTemplate
.
autoAnimoji
?
'Y'
:
'N'
,
enablePalindrome
:
digitalTemplate
.
enablePalindrome
?
'Y'
:
'N'
,
templateId
:
String
(
digitalTemplate
.
id
),
title
:
digitalTemplate
.
title
,
logoUrl
:
digitalTemplate
.
logoParams
.
logoUrl
,
bgmUrl
:
digitalTemplate
.
bgmParams
.
bgmUrl
,
materialUrl
:
digitalTemplate
.
materialUrl
,
}
digitalCreationStore
.
updateDigitalCreation
(
draftConfig
)
}
}
</
script
>
...
...
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