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
You need to sign in or sign up before continuing.
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