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
4ab1b529
Commit
4ab1b529
authored
Sep 25, 2024
by
Dazzle Wu
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
feat: 视频生成配置
parent
a0b1ca29
Hide whitespace changes
Inline
Side-by-side
Showing
19 changed files
with
655 additions
and
381 deletions
+655
-381
digital-creation.ts
src/apis/digital-creation.ts
+19
-6
horizontal-tabs.vue
src/components/horizontal-tabs/horizontal-tabs.vue
+1
-1
creation.ts
src/router/modules/creation.ts
+1
-1
creation.ts
src/store/modules/creation.ts
+68
-24
creation.ts
src/store/types/creation.ts
+44
-26
background-images.vue
...iews/creation/components/background/background-images.vue
+0
-94
background-position.vue
...ws/creation/components/background/background-position.vue
+0
-33
background-setting.vue
...ews/creation/components/background/background-setting.vue
+137
-8
caption-setting.vue
src/views/creation/components/caption/caption-setting.vue
+0
-16
digital-audio-card.vue
src/views/creation/components/digital/digital-audio-card.vue
+50
-0
digital-audio.vue
src/views/creation/components/digital/digital-audio.vue
+106
-69
digital-human-card.vue
src/views/creation/components/digital/digital-human-card.vue
+9
-9
digital-human.vue
src/views/creation/components/digital/digital-human.vue
+75
-59
digital-position.vue
src/views/creation/components/digital/digital-position.vue
+64
-21
digital-setting.vue
src/views/creation/components/digital/digital-setting.vue
+10
-4
preview-content.vue
src/views/creation/components/preview-content.vue
+23
-2
subtitle-setting.vue
src/views/creation/components/subtitle/subtitle-setting.vue
+32
-0
index.vue
src/views/creation/layout/index.vue
+11
-3
side-bar.vue
src/views/creation/layout/side-bar.vue
+5
-5
No files found.
src/apis/digital-creation.ts
View file @
4ab1b529
...
...
@@ -25,14 +25,19 @@ export function fetchBackgroundImage<T>() {
return
request
.
post
<
T
>
(
'/bizDigitalHumanImageRest/getBackgroundImage.json'
)
}
// 根据人物名称分页获取人物信息
export
function
fetchInfoByImageName
<
T
>
(
imageName
:
string
)
{
return
request
.
post
<
T
>
(
`/bizDigitalHumanImageRest/findByImageName.json?imageName=
${
imageName
}
`
)
// 上传背景图片
export
function
uploadImageFile
<
T
>
(
imageName
:
string
,
formData
:
FormData
)
{
return
request
.
post
<
T
>
(
`/baiduDigitalHumanFileRest/uploadImageFile.json?imageName=
${
imageName
}
`
,
formData
,
{
headers
:
{
'Content-Type'
:
'multipart/form-data'
},
timeout
:
12000
,
})
}
// 根据音色ID主键获取音色
export
function
fetchDigitalHumanTimbreById
<
T
>
()
{
return
request
.
post
<
T
>
(
'/bizDigitalHumanTimbreRest/getDigitalHumanTimbreById.json'
)
// 根据人物名称分页获取人物信息
export
function
fetchInfoByImageName
<
T
>
(
imageName
:
string
)
{
return
request
.
post
<
T
>
(
`/bizDigitalHumanImageRest/findByImageName.json?imageName=
${
imageName
}
`
,
{
pagingInfo
:
{
pageNo
:
1
,
pageSize
:
10
},
})
}
// 获取音色列表
...
...
@@ -44,3 +49,11 @@ export function fetchDigitalHumanTimbreList<T>() {
export
function
fetchTimbreByExample
<
T
>
(
condition
:
string
)
{
return
request
.
post
<
T
>
(
`/bizDigitalHumanTimbreRest/getByExample.json?condition=
${
condition
}
`
)
}
// 基础数字人视频
export
function
createBaseVideoDigitalHumanTask
<
T
>
(
callbackUrl
:
string
,
payload
:
object
)
{
return
request
.
post
<
T
>
(
`/aiDigitalHumanServiceRest/createBaseVideoDigitalHumanTask.json?callbackUrl=
${
callbackUrl
}
`
,
payload
,
)
}
src/components/horizontal-tabs/horizontal-tabs.vue
View file @
4ab1b529
...
...
@@ -31,7 +31,7 @@ const emit = defineEmits<Emits>()
</div>
<div
class=
"absolute h-8 w-20 rounded-full bg-blue-500 transition-transform duration-300"
:
class=
"`translate-x-$
{value * 20}`
"
:
style=
"
{ transform: `translateX(${value * 80}px)` }
"
>
</div>
</div>
</
template
>
src/router/modules/creation.ts
View file @
4ab1b529
...
...
@@ -3,7 +3,7 @@ import Creation from '@/views/creation/creation.vue'
export
default
[
{
path
:
'/
fe/
creation'
,
path
:
'/creation'
,
name
:
'Creation'
,
meta
:
{
rank
:
1001
,
...
...
src/store/modules/creation.ts
View file @
4ab1b529
...
...
@@ -3,49 +3,57 @@ import { DigitalTemplate } from '@/store/types/creation'
function
defaultDigitalCreation
():
DigitalTemplate
{
return
{
id
:
1
,
templateType
:
'产品营销'
,
videoName
:
'营销视频'
,
id
:
null
,
coverUrl
:
null
,
demonstrationGifUrl
:
null
,
demonstrationVideoUrl
:
null
,
templateType
:
null
,
templateName
:
null
,
taskType
:
'BASE_VIDEO'
,
requestId
:
undefined
,
inputImageUrl
:
undefined
,
requestId
:
null
,
inputImageUrl
:
null
,
driveType
:
'TEXT'
,
text
:
'嗨!我是挥手问候家'
,
text
:
null
,
ttsParams
:
{
person
:
'5153'
,
person
:
null
,
speed
:
'5'
,
volume
:
'5'
,
pitch
:
'5'
,
},
inputAudioUrl
:
undefined
,
callbackUrl
:
undefined
,
figureId
:
''
,
inputAudioUrl
:
null
,
callbackUrl
:
null
,
figureId
:
null
,
digitalImageUrl
:
null
,
videoParams
:
{
width
:
192
0
,
width
:
0
,
height
:
0
,
transparent
:
false
,
},
dhParams
:
{
cameraId
:
undefined
,
cameraId
:
null
,
position
:
{
x
:
10
0
,
y
:
111
,
w
:
undefined
,
h
:
undefined
,
x
:
0
,
y
:
0
,
w
:
0
,
h
:
0
,
},
},
subtitleParams
:
{
subtitlePolicy
:
'SRT'
,
enabled
:
tru
e
,
enabled
:
fals
e
,
},
backgroundImageUrl
:
undefined
,
autoAnimoji
:
tru
e
,
backgroundImageUrl
:
null
,
autoAnimoji
:
fals
e
,
enablePalindrome
:
false
,
templateId
:
null
,
title
:
undefined
,
logoParams
:
null
,
bgmParams
:
null
,
materialUrl
:
undefined
,
title
:
null
,
logoParams
:
{
logoUrl
:
null
,
},
bgmParams
:
{
bgmUrl
:
null
,
},
materialUrl
:
null
,
}
}
...
...
@@ -57,14 +65,50 @@ export const useDigitalCreationStore = defineStore('digital-creation-store', {
state
:
():
DigitalTemplate
=>
getLocalState
(),
actions
:
{
setFigureId
(
figureId
:
string
|
null
)
{
setFigureId
(
figureId
:
string
)
{
this
.
figureId
=
figureId
},
setDigitalImageUrl
(
digitalImageUrl
:
string
)
{
this
.
digitalImageUrl
=
digitalImageUrl
},
setPerson
(
person
:
string
)
{
this
.
ttsParams
.
person
=
person
},
setSpeed
(
speed
:
string
)
{
this
.
ttsParams
.
speed
=
speed
},
setPitch
(
pitch
:
string
)
{
this
.
ttsParams
.
pitch
=
pitch
},
setDigitalImagePositionX
(
x
:
number
)
{
this
.
dhParams
.
position
.
x
=
x
},
setDigitalImagePositionY
(
y
:
number
)
{
this
.
dhParams
.
position
.
y
=
y
},
setDigitalImagePositionW
(
w
:
number
)
{
this
.
dhParams
.
position
.
w
=
w
},
setDigitalImagePositionH
(
h
:
number
)
{
this
.
dhParams
.
position
.
h
=
h
},
setBackgroundImageUrl
(
backgroundImageUrl
:
string
)
{
this
.
backgroundImageUrl
=
backgroundImageUrl
},
setSubtitleEnabled
(
subtitleEnabled
:
boolean
)
{
this
.
subtitleParams
.
enabled
=
subtitleEnabled
},
updateDigitalCreation
(
digitalCreation
:
DigitalTemplate
)
{
this
.
$state
=
{
...
this
.
$state
,
...
digitalCreation
}
},
...
...
src/store/types/creation.ts
View file @
4ab1b529
export
interface
DigitalTemplate
{
id
:
number
templateType
:
string
videoName
:
string
id
:
number
|
null
coverUrl
:
string
|
null
demonstrationGifUrl
:
string
|
null
demonstrationVideoUrl
:
string
|
null
templateType
:
string
|
null
templateName
:
string
|
null
taskType
:
string
requestId
?:
string
inputImageUrl
?:
string
requestId
:
string
|
null
inputImageUrl
:
string
|
null
driveType
:
string
text
:
string
text
:
string
|
null
ttsParams
:
{
person
:
string
person
:
string
|
null
speed
:
string
volume
:
string
pitch
:
string
}
inputAudioUrl
?:
string
callbackUrl
?:
string
inputAudioUrl
:
string
|
null
callbackUrl
:
string
|
null
figureId
:
string
|
null
digitalImageUrl
?:
string
|
null
videoParams
:
{
width
:
number
height
:
number
width
:
number
|
null
height
:
number
|
null
transparent
:
boolean
}
dhParams
:
{
cameraId
?:
number
cameraId
:
number
|
null
position
:
{
x
:
number
y
:
number
w
?
:
number
h
?
:
number
w
:
number
h
:
number
}
}
subtitleParams
:
{
subtitlePolicy
:
string
subtitlePolicy
:
string
|
null
enabled
:
boolean
}
backgroundImageUrl
?:
string
backgroundImageUrl
:
string
|
null
autoAnimoji
:
boolean
enablePalindrome
:
boolean
templateId
?:
any
title
?:
string
logoParams
?:
any
bgmParams
?:
any
materialUrl
?:
string
templateId
:
string
|
null
title
:
string
|
null
logoParams
:
{
logoUrl
:
string
|
null
}
bgmParams
:
{
bgmUrl
:
string
|
null
}
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
ImageItem
{
...
...
@@ -52,9 +67,12 @@ export interface ImageItem {
imageUrl
:
string
}
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
TimbreItem
{
id
:
number
name
:
string
timebreId
:
string
sex
:
string
style
:
string
[]
applyScene
:
string
[]
audioUrl
:
string
}
src/views/creation/components/background/background-images.vue
deleted
100644 → 0
View file @
a0b1ca29
<
script
setup
lang=
"ts"
>
import
{
fetchBackgroundImage
}
from
'@/apis/digital-creation'
import
{
useDigitalCreationStore
}
from
'@/store/modules/creation'
import
{
ImageItem
}
from
'@/store/types/creation'
import
{
ref
}
from
'vue'
const
digitalCreationStore
=
useDigitalCreationStore
()
const
imageList
=
ref
<
ImageItem
[]
>
([])
const
uploadLoading
=
ref
(
false
)
const
showCropModal
=
ref
(
false
)
const
loaded
=
ref
(
Array
(
imageList
.
value
.
length
).
fill
(
false
))
getBackgroundImageList
()
async
function
getBackgroundImageList
()
{
const
res
=
await
fetchBackgroundImage
<
ImageItem
[]
>
()
if
(
res
.
code
===
0
)
{
imageList
.
value
=
res
.
data
.
map
((
i
)
=>
({
...
i
,
checked
:
i
.
imageUrl
===
digitalCreationStore
.
backgroundImageUrl
}))
loaded
.
value
=
Array
(
res
.
data
.
length
).
fill
(
false
)
}
}
function
uploadImage
()
{
showCropModal
.
value
=
true
}
function
handleClickImage
(
image
:
ImageItem
)
{
digitalCreationStore
.
setBackgroundImageUrl
(
image
.
imageUrl
)
}
function
handleDelete
()
{
window
.
$dialog
.
warning
({
title
:
'刪除圖片'
,
content
:
'是否刪除該圖片?'
,
positiveText
:
'是'
,
negativeText
:
'否'
,
onPositiveClick
:
()
=>
{
window
.
$message
.
success
(
'刪除成功'
)
},
onNegativeClick
:
()
=>
{
window
.
$message
.
error
(
'刪除失敗'
)
},
})
}
function
onImageLoaded
(
index
:
number
)
{
loaded
.
value
[
index
]
=
true
}
</
script
>
<
template
>
<n-input
round
placeholder=
"搜索"
>
<template
#
prefix
>
<CustomIcon
class=
"text-lg"
icon=
"mingcute:search-line"
/>
</
template
>
</n-input>
<div
class=
"h-4"
></div>
<n-grid
:x-gap=
"12"
:y-gap=
"12"
:cols=
"3"
>
<n-gi>
<n-spin
:show=
"uploadLoading"
>
<label
class=
"h-22 w-22 hover:border-blue flex cursor-pointer flex-col items-center justify-center rounded-lg border border-gray-200"
for=
"upload"
>
<CustomIcon
class=
"text-lg"
icon=
"mingcute:add-line"
/>
</label>
<input
id=
"upload"
type=
"file"
accept=
"image/*"
class=
"hidden"
@
change=
"uploadImage"
/>
<
template
#
description
>
上傳中
</
template
>
</n-spin>
</n-gi>
<n-gi
v-for=
"(image, index) in imageList"
:key=
"index"
>
<n-spin
:show=
"!loaded[index]"
>
<div
class=
"h-22 w-22 group relative cursor-pointer overflow-hidden rounded-lg border border-2"
:class=
"image.imageUrl === digitalCreationStore.backgroundImageUrl ? 'border-blue' : 'border-transparent'"
@
click=
"handleClickImage(image)"
>
<img
class=
"h-full w-full object-contain"
:src=
"image.imageUrl"
@
load=
"onImageLoaded(index)"
/>
<div
class=
"from-gray absolute bottom-0 h-5 w-full bg-gradient-to-t px-1 text-xs leading-5 text-white"
>
{{ image.imageName }}
</div>
<div
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"
>
<CustomIcon
icon=
"mi:delete"
class=
"text-lg text-white"
/>
</div>
</div>
</n-spin>
</n-gi>
</n-grid>
<n-modal
v-model:show=
"showCropModal"
preset=
"card"
title=
"裁剪圖片"
>
内容
</n-modal>
</template>
src/views/creation/components/background/background-position.vue
deleted
100644 → 0
View file @
a0b1ca29
<
script
setup
lang=
"ts"
>
import
{
ref
}
from
'vue'
const
x
=
ref
(
0
)
const
y
=
ref
(
0
)
const
w
=
ref
(
0
)
const
h
=
ref
(
0
)
const
isLock
=
ref
(
false
)
</
script
>
<
template
>
<div>
背景位置
</div>
<div
class=
"mt-4 flex gap-4"
>
<n-input-number
v-model:value=
"x"
>
<template
#
prefix
>
X
</
template
>
</n-input-number>
<n-input-number
v-model:value=
"y"
>
<
template
#
prefix
>
Y
</
template
>
</n-input-number>
</div>
<div
class=
"mt-4 flex gap-4"
>
<n-input-number
v-model:value=
"w"
>
<
template
#
prefix
>
W
</
template
>
</n-input-number>
<n-input-number
v-model:value=
"h"
>
<
template
#
prefix
>
H
</
template
>
</n-input-number>
</div>
<div
class=
"mt-4 flex items-center justify-between"
>
<span>
鎖定背景
</span>
<n-switch
v-model:value=
"isLock"
/>
</div>
</template>
src/views/creation/components/background/background-setting.vue
View file @
4ab1b529
<
script
setup
lang=
"ts"
>
import
BackgroundImages
from
'./background-images.vue'
// import BackgroundPosition from './background-position.vue'
import
{
fetchBackgroundImage
,
fetchInfoByImageName
,
uploadImageFile
}
from
'@/apis/digital-creation'
import
{
useDigitalCreationStore
}
from
'@/store/modules/creation'
import
{
ImageItem
}
from
'@/store/types/creation'
import
{
ref
}
from
'vue'
const
digitalCreationStore
=
useDigitalCreationStore
()
const
imageList
=
ref
<
ImageItem
[]
>
([])
const
searchName
=
ref
(
''
)
const
uploadLoading
=
ref
(
false
)
const
loaded
=
ref
(
Array
(
imageList
.
value
.
length
).
fill
(
false
))
getBackgroundImageList
()
async
function
getBackgroundImageList
()
{
const
res
=
await
fetchBackgroundImage
<
ImageItem
[]
>
()
if
(
res
.
code
===
0
)
{
imageList
.
value
=
res
.
data
.
map
((
i
)
=>
({
...
i
,
checked
:
i
.
imageUrl
===
digitalCreationStore
.
backgroundImageUrl
}))
}
}
async
function
handleSearch
(
value
:
string
)
{
const
res
=
await
fetchInfoByImageName
<
ImageItem
[]
>
(
value
)
if
(
res
.
code
===
0
)
{
imageList
.
value
=
res
.
data
}
}
async
function
uploadImage
(
event
:
any
)
{
const
e
=
window
.
event
||
event
const
file
=
e
.
target
.
files
[
0
]
const
fileName
=
file
.
name
.
substring
(
0
,
file
.
name
.
lastIndexOf
(
'.'
))
const
maxImageSize
=
1024
*
1024
*
3
if
(
!
[
'png'
,
'jpg'
,
'jpeg'
].
includes
(
file
.
type
.
split
(
'/'
)[
1
]))
{
window
.
$message
.
error
(
'必须为png或者jpg格式'
)
return
}
if
(
file
.
size
>
maxImageSize
)
{
window
.
$message
.
error
(
'图片不能超过3MB'
)
return
}
const
URL
=
window
.
URL
||
window
.
webkitURL
const
img
=
new
Image
()
img
.
src
=
URL
.
createObjectURL
(
file
)
img
.
onload
=
async
function
()
{
const
formData
=
new
FormData
()
formData
.
append
(
'file'
,
file
)
uploadLoading
.
value
=
true
const
res
=
await
uploadImageFile
(
fileName
,
formData
).
finally
(()
=>
(
uploadLoading
.
value
=
false
))
if
(
res
.
code
===
0
)
{
getBackgroundImageList
()
}
}
}
function
handleClickImage
(
image
:
ImageItem
)
{
digitalCreationStore
.
setBackgroundImageUrl
(
image
.
imageUrl
)
}
function
handleDelete
()
{
window
.
$dialog
.
warning
({
title
:
'刪除圖片'
,
content
:
'是否刪除該圖片?'
,
positiveText
:
'是'
,
negativeText
:
'否'
,
onPositiveClick
:
()
=>
{
window
.
$message
.
success
(
'刪除成功'
)
},
onNegativeClick
:
()
=>
{
window
.
$message
.
error
(
'刪除失敗'
)
},
})
}
function
onImageLoaded
(
index
:
number
)
{
loaded
.
value
[
index
]
=
true
}
</
script
>
<
template
>
<n-tabs
type=
"line"
animated
>
<n-tab-pane
name=
"images"
tab=
"圖片"
>
<BackgroundImages
/>
<n-tabs
type=
"line"
animated
class=
"h-full"
>
<n-tab-pane
name=
"images"
tab=
"圖片"
class=
"h-full"
>
<div
class=
"h-full overflow-y-auto px-4 py-2"
>
<n-input
v-model:value=
"searchName"
round
placeholder=
"搜索"
@
input=
"handleSearch"
>
<template
#
prefix
>
<CustomIcon
class=
"text-lg"
icon=
"mingcute:search-line"
/>
</
template
>
</n-input>
<div
class=
"h-4"
></div>
<n-grid
:x-gap=
"12"
:y-gap=
"12"
:cols=
"3"
>
<n-gi>
<n-spin
:show=
"uploadLoading"
>
<label
class=
"h-22 w-22 hover:border-blue flex cursor-pointer flex-col items-center justify-center rounded-lg border border-gray-200"
for=
"upload"
>
<CustomIcon
class=
"text-lg"
icon=
"mingcute:add-line"
/>
</label>
<input
id=
"upload"
type=
"file"
accept=
"image/*"
class=
"hidden"
@
change=
"uploadImage"
/>
<
template
#
description
>
上傳中
</
template
>
</n-spin>
</n-gi>
<n-gi
v-for=
"(image, index) in imageList"
:key=
"index"
>
<n-spin
:show=
"!loaded[index]"
>
<div
class=
"h-22 w-22 group relative cursor-pointer overflow-hidden rounded-lg border border-2 bg-gray-100"
:class=
"
image.imageUrl === digitalCreationStore.backgroundImageUrl ? 'border-blue' : 'border-transparent'
"
@
click=
"handleClickImage(image)"
>
<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"
>
{{ image.imageName }}
</div>
<div
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"
>
<CustomIcon
icon=
"mi:delete"
class=
"text-lg text-white"
/>
</div>
</div>
</n-spin>
</n-gi>
</n-grid>
</div>
</n-tab-pane>
<!--
<n-tab-pane
name=
"position"
tab=
"位置"
>
<BackgroundPosition
/>
</n-tab-pane>
-->
</n-tabs>
</template>
<
style
lang=
"scss"
scoped
>
:deep
(
.n-tabs-nav-scroll-wrapper
)
{
padding
:
0
16px
;
}
</
style
>
src/views/creation/components/caption/caption-setting.vue
deleted
100644 → 0
View file @
a0b1ca29
<
script
setup
lang=
"ts"
>
import
{
ref
}
from
'vue'
const
active
=
ref
(
false
)
</
script
>
<
template
>
<n-tabs
type=
"line"
animated
>
<n-tab-pane
name=
"caption"
tab=
"字幕"
>
<div
class=
"flex items-center justify-between"
>
<span>
是否開啓
</span>
<n-switch
v-model:value=
"active"
/>
</div>
</n-tab-pane>
</n-tabs>
</
template
>
src/views/creation/components/digital/digital-audio-card.vue
0 → 100644
View file @
4ab1b529
<
script
setup
lang=
"ts"
>
import
{
useDigitalCreationStore
}
from
'@/store/modules/creation'
import
{
TimbreItem
}
from
'@/store/types/creation'
import
{
computed
,
ref
}
from
'vue'
interface
Props
{
value
?:
TimbreItem
showToggle
?:
boolean
}
interface
Emits
{
(
e
:
'click'
,
value
:
TimbreItem
):
void
(
e
:
'toggle'
,
value
:
boolean
):
void
}
defineProps
<
Props
>
()
const
emit
=
defineEmits
<
Emits
>
()
const
digitalCreationStore
=
useDigitalCreationStore
()
const
digitalAudio
=
ref
<
HTMLAudioElement
>
()
const
person
=
computed
(()
=>
digitalCreationStore
.
ttsParams
.
person
)
function
playAudio
()
{
digitalAudio
.
value
?.
play
()
}
</
script
>
<
template
>
<div
class=
"relative mb-4 flex items-center gap-2 rounded-2xl border p-2 hover:shadow"
: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-1 overflow-hidden"
>
<div
class=
"mb-2 flex items-center gap-2"
>
<div
class=
"max-w-32 truncate"
>
{{
value
?.
name
}}
</div>
<CustomIcon
class=
"cursor-pointer text-lg"
icon=
"mingcute:volume-line"
@
click
.
stop
.
prevent=
"playAudio"
/>
</div>
<div
class=
"flex gap-2"
>
<n-tag
v-for=
"(style, index) in value?.style"
:key=
"index"
type=
"warning"
round
>
{{
style
}}
</n-tag>
</div>
</div>
<div
v-if=
"showToggle"
class=
"absolute right-2 top-2"
>
<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 @
4ab1b529
<
script
setup
lang=
"ts"
>
import
{
ref
}
from
'vue'
import
{
fetchDigitalHumanTimbreList
,
fetchTimbreByExample
}
from
'@/apis/digital-creation'
import
{
useDigitalCreationStore
}
from
'@/store/modules/creation'
import
{
TimbreItem
}
from
'@/store/types/creation'
import
{
computed
,
onMounted
,
ref
,
watch
}
from
'vue'
import
DigitalAudioCard
from
'./digital-audio-card.vue'
const
pace
=
ref
(
1
)
const
intonation
=
ref
(
1
)
const
showAll
=
ref
(
false
)
const
tabValue
=
ref
(
0
)
const
tablist
=
[
{
label
:
'女性'
,
key
:
0
},
{
label
:
'男性'
,
key
:
1
},
const
digitalCreationStore
=
useDigitalCreationStore
()
const
sexValue
=
ref
(
0
)
const
sexList
=
[
{
key
:
0
,
label
:
'女性'
},
{
key
:
1
,
label
:
'男性'
},
]
const
digitalTimbreValue
=
ref
<
TimbreItem
>
()
const
digitalTimbreList
=
ref
<
TimbreItem
[]
>
([])
const
digitalTimbreFemaleList
=
ref
<
TimbreItem
[]
>
([])
const
digitalTimbreMaleList
=
ref
<
TimbreItem
[]
>
([])
const
showAll
=
ref
(
false
)
const
searchName
=
ref
(
''
)
const
speed
=
computed
({
get
()
{
return
Number
(
digitalCreationStore
.
ttsParams
.
speed
)
},
set
(
value
)
{
digitalCreationStore
.
setSpeed
(
String
(
value
))
},
})
const
pitch
=
computed
({
get
()
{
return
Number
(
digitalCreationStore
.
ttsParams
.
pitch
)
},
set
(
value
)
{
digitalCreationStore
.
setPitch
(
String
(
value
))
},
})
watch
(
()
=>
[
digitalCreationStore
.
ttsParams
.
person
,
digitalTimbreList
.
value
.
length
],
([
person
,
len
])
=>
{
if
(
person
&&
len
)
{
digitalTimbreValue
.
value
=
digitalTimbreList
.
value
.
find
((
i
)
=>
i
.
timebreId
===
person
)
}
},
)
onMounted
(()
=>
{
getDigitalTimbreList
()
})
async
function
getDigitalTimbreList
()
{
const
res
=
await
fetchDigitalHumanTimbreList
<
TimbreItem
[]
>
()
if
(
res
.
code
===
0
)
{
digitalTimbreList
.
value
=
res
.
data
digitalTimbreFemaleList
.
value
=
digitalTimbreList
.
value
.
filter
((
i
)
=>
i
.
sex
===
'女'
)
digitalTimbreMaleList
.
value
=
digitalTimbreList
.
value
.
filter
((
i
)
=>
i
.
sex
===
'男'
)
}
}
async
function
handleSearch
(
value
:
string
)
{
const
res
=
await
fetchTimbreByExample
<
TimbreItem
[]
>
(
value
)
if
(
res
.
code
===
0
)
{
digitalTimbreList
.
value
=
res
.
data
digitalTimbreFemaleList
.
value
=
digitalTimbreList
.
value
.
filter
((
i
)
=>
i
.
sex
===
'女'
)
digitalTimbreMaleList
.
value
=
digitalTimbreList
.
value
.
filter
((
i
)
=>
i
.
sex
===
'男'
)
}
}
function
handleClickAudioCard
(
timbreItem
:
TimbreItem
)
{
digitalCreationStore
.
setPerson
(
timbreItem
.
timebreId
)
}
</
script
>
<
template
>
<div
v-if=
"!showAll"
>
<div
class=
"relative flex items-center gap-2 rounded-2xl border p-2"
>
<div
class=
"h-16 w-16 rounded-lg bg-gray-200"
></div>
<div
class=
"flex-1 overflow-hidden"
>
<div
class=
"mb-2 flex items-center gap-2"
>
<div
class=
"max-w-24 truncate"
>
度清風
</div>
<CustomIcon
class=
"cursor-pointer text-lg"
icon=
"mingcute:volume-line"
/>
</div>
<div
class=
"flex gap-2"
>
<n-tag
type=
"warning"
round
>
知性大方
</n-tag>
<n-tag
type=
"success"
round
>
客服助理
</n-tag>
<n-tag
type=
"success"
round
>
知性大方
</n-tag>
<n-tag
type=
"warning"
round
>
客服助理
</n-tag>
</div>
<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=
"mt-4 text-lg"
>
聲音
</div>
<div
class=
"mt-4 flex items-center gap-2"
>
<div
class=
"w-12"
>
語速:
</div>
<n-slider
v-model:value=
"speed"
class=
"flex-1"
:max=
"15"
:min=
"0"
:step=
"1"
/>
<div
class=
"w-10"
>
{{
speed
}}
</div>
</div>
<div
class=
"absolute right-2 top-2"
>
<CustomIcon
class=
"cursor-pointer text-lg"
icon=
"ant-design:swap-outlined"
@
click=
"showAll = true"
/>
<div
class=
"mt-4 flex items-center gap-2"
>
<div
class=
"w-12"
>
語調:
</div>
<n-slider
v-model:value=
"pitch"
class=
"flex-1"
:max=
"15"
:min=
"0"
:step=
"1"
/>
<div
class=
"w-10"
>
{{
pitch
}}
</div>
</div>
</div>
<div
class=
"mt-4 text-lg"
>
聲音
</div>
<div
class=
"mt-4 flex items-center gap-2"
>
<div
class=
"w-12"
>
語速:
</div>
<n-slider
v-model:value=
"pace"
class=
"flex-1"
:max=
"1.5"
:min=
"0.5"
:step=
"0.25"
/>
<div
class=
"w-10"
>
{{
pace
}}
x
</div>
</div>
<div
class=
"mt-4 flex items-center gap-2"
>
<div
class=
"w-12"
>
語調:
</div>
<n-slider
v-model:value=
"intonation"
class=
"flex-1"
:max=
"5"
:min=
"1"
:step=
"1"
/>
<div
class=
"w-10"
>
{{
intonation
}}
</div>
</div>
</div>
<div
v-else
>
<div
class=
"flex items-center gap-4 pb-3"
>
<n-button
text
@
click=
"showAll = false"
>
<template
#
icon
>
<CustomIcon
class=
"text-lg"
icon=
"mingcute:left-line"
/>
</
template
>
返回
</n-button>
<n-input
round
placeholder=
"搜索"
>
<
template
#
prefix
>
<CustomIcon
class=
"text-lg"
icon=
"mingcute:search-line"
/>
</
template
>
</n-input>
</div>
<div
v-else
>
<div
class=
"flex items-center gap-4 pb-3"
>
<n-button
text
@
click=
"showAll = false"
>
<template
#
icon
>
<CustomIcon
class=
"text-lg"
icon=
"mingcute:left-line"
/>
</
template
>
返回
</n-button>
<n-input
v-model:value=
"searchName"
round
placeholder=
"搜索"
@
input=
"handleSearch"
>
<
template
#
prefix
>
<CustomIcon
class=
"text-lg"
icon=
"mingcute:search-line"
/>
</
template
>
</n-input>
</div>
<div
class=
"flex justify-end pb-3"
>
<HorizontalTabs
v-model:value=
"tabValue"
:list=
"tabl
ist"
/>
</div>
<div
class=
"flex justify-end pb-3"
>
<HorizontalTabs
v-model:value=
"sexValue"
:list=
"sexL
ist"
/>
</div>
<div>
<div
class=
"flex items-center gap-2 rounded-2xl border p-2"
>
<div
class=
"h-16 w-16 rounded-lg bg-gray-200"
></div>
<div
class=
"flex-1 overflow-hidden"
>
<div
class=
"mb-2 flex items-center gap-2"
>
<div
class=
"max-w-24 truncate"
>
度清風
</div>
<CustomIcon
class=
"cursor-pointer text-lg"
icon=
"mingcute:volume-line"
/>
</div>
<div
class=
"flex gap-2"
>
<n-tag
type=
"warning"
round
>
知性大方
</n-tag>
<n-tag
type=
"success"
round
>
客服助理
</n-tag>
<n-tag
type=
"success"
round
>
知性大方
</n-tag>
<n-tag
type=
"warning"
round
>
客服助理
</n-tag>
</div>
</div>
<div>
<DigitalAudioCard
v-for=
"(timbre, index) in sexValue ? digitalTimbreMaleList : digitalTimbreFemaleList"
:key=
"index"
:value=
"timbre"
@
click=
"handleClickAudioCard"
/>
</div>
</div>
</div>
...
...
src/views/creation/components/digital/digital-card.vue
→
src/views/creation/components/digital/digital-
human-
card.vue
View file @
4ab1b529
<
script
setup
lang=
"ts"
>
import
{
ImageItem
}
from
'@/store/types/creation'
import
{
useDigitalCreationStore
}
from
'@/store/modules/creation'
import
{
ImageItem
}
from
'@/store/types/creation'
interface
Props
{
value
:
ImageItem
}
interface
Emits
{
(
e
:
'click'
,
id
:
string
|
null
):
void
(
e
:
'click'
,
value
:
ImageItem
):
void
}
defineProps
<
Props
>
()
...
...
@@ -18,18 +18,18 @@ const digitalCreationStore = useDigitalCreationStore()
<
template
>
<div
class=
"hover:border-blue relative h-28 w-16 cursor-pointer
overflow-hidden rounded border border-2
"
class=
"hover:border-blue relative h-28 w-16 cursor-pointer
rounded border border-2 bg-gray-100
"
:class=
"digitalCreationStore.figureId === value.figureId ? 'border-blue' : 'border-transparent'"
@
click=
"emit('click', value
.figureId
)"
@
click=
"emit('click', value)"
>
<img
:src=
"value.imageUrl"
/>
<div
class=
"
from-gray absolute bottom-0 h-5 w-full bg-gradient-to-t
px-1 text-xs leading-5 text-white"
>
<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>
<
!--
<
CustomIcon
<CustomIcon
v-if=
"digitalCreationStore.figureId === value.figureId"
icon=
"
teenyicons:tick-circle-soli
d"
class=
"text-blue absolute
-left-2 -top-2 text-lg
"
/>
-->
icon=
"
si-glyph:checke
d"
class=
"text-blue absolute
left-0 top-0 text-xs
"
/>
</div>
</
template
>
src/views/creation/components/digital/digital-human.vue
View file @
4ab1b529
<
script
setup
lang=
"ts"
>
import
{
fetch2DBoutiqueImageList
,
fetch2DFewShotImageList
,
fetch3DImageList
}
from
'@/apis/digital-creation'
import
{
fetch2DBoutiqueImageList
,
fetch2DFewShotImageList
,
fetch3DImageList
,
fetchInfoByImageName
,
}
from
'@/apis/digital-creation'
import
{
useDigitalCreationStore
}
from
'@/store/modules/creation'
import
{
ImageItem
,
ImageType
}
from
'@/store/types/creation'
import
{
onMounted
,
ref
}
from
'vue'
import
DigitalCard
from
'./digital-card.vue'
import
{
useDigitalCreationStore
}
from
'@/store/modules/creation'
import
DigitalCard
from
'./digital-human-card.vue'
const
digitalCreationStore
=
useDigitalCreationStore
()
const
threeDImageList
=
ref
<
ImageItem
[]
>
([])
...
...
@@ -11,6 +16,7 @@ const twoDBoutiqueImageList = ref<ImageItem[]>([])
const
twoDFewShotImageList
=
ref
<
ImageItem
[]
>
([])
const
allImageList
=
ref
<
ImageItem
[]
>
([])
const
showAll
=
ref
(
false
)
const
searchName
=
ref
(
''
)
onMounted
(()
=>
{
getDigitalImageList
()
...
...
@@ -27,8 +33,16 @@ async function getDigitalImageList() {
res3
.
code
===
0
&&
(
twoDFewShotImageList
.
value
=
res3
.
data
)
}
function
handleClickDigitalImage
(
id
:
string
|
null
)
{
digitalCreationStore
.
setFigureId
(
id
)
async
function
handleSearch
(
value
:
string
)
{
const
res
=
await
fetchInfoByImageName
<
ImageItem
[]
>
(
value
)
if
(
res
.
code
===
0
)
{
allImageList
.
value
=
res
.
data
}
}
function
handleClickDigitalImage
(
digitalItem
:
ImageItem
)
{
digitalCreationStore
.
setFigureId
(
digitalItem
.
figureId
!
)
digitalCreationStore
.
setDigitalImageUrl
(
digitalItem
.
imageUrl
)
}
function
handleClickAll
(
imageType
:
ImageType
)
{
...
...
@@ -48,69 +62,71 @@ function handleClickAll(imageType: ImageType) {
</
script
>
<
template
>
<div
v-if=
"!showAll"
>
<div
class=
"pb-4"
>
<div
class=
"flex items-center justify-between pb-3"
>
<span>
3D數字人
</span>
<span
class=
"text-gray cursor-pointer text-xs"
@
click=
"handleClickAll(ImageType.THREE_D)"
>
全部
</span>
</div>
<div
class=
"flex flex-wrap gap-3"
>
<DigitalCard
v-for=
"item in threeDImageList.slice(0, 4)"
:key=
"item.id"
:value=
"item"
@
click=
"handleClickDigitalImage"
/>
<div
class=
"h-full overflow-y-auto px-4 py-2"
>
<div
v-if=
"!showAll"
>
<div
class=
"pb-4"
>
<div
class=
"flex items-center justify-between pb-3 leading-8"
>
<span>
3D數字人
</span>
<span
class=
"text-gray cursor-pointer text-xs"
@
click=
"handleClickAll(ImageType.THREE_D)"
>
全部
</span>
</div>
<div
class=
"flex flex-wrap gap-3"
>
<DigitalCard
v-for=
"item in threeDImageList.slice(0, 4)"
:key=
"item.id"
:value=
"item"
@
click=
"handleClickDigitalImage"
/>
</div>
</div>
</div>
<div
class=
"pb-4"
>
<div
class=
"flex items-center justify-between pb-3"
>
<span>
2D精品數字人
</span>
<span
class=
"text-gray cursor-pointer text-xs"
@
click=
"handleClickAll(ImageType.TWO_D_BOUTIQUE)"
>
全部
</span>
<div
class=
"pb-4"
>
<div
class=
"flex items-center justify-between pb-3 leading-8"
>
<span>
2D精品數字人
</span>
<span
class=
"text-gray cursor-pointer text-xs"
@
click=
"handleClickAll(ImageType.TWO_D_BOUTIQUE)"
>
全部
</span>
</div>
<div
class=
"flex flex-wrap gap-3"
>
<DigitalCard
v-for=
"item in twoDBoutiqueImageList.slice(0, 4)"
:key=
"item.id"
:value=
"item"
@
click=
"handleClickDigitalImage"
/>
</div>
</div>
<div
class=
"flex flex-wrap gap-3"
>
<DigitalCard
v-for=
"item in twoDBoutiqueImageList.slice(0, 4)"
:key=
"item.id"
:value=
"item"
@
click=
"handleClickDigitalImage"
/>
<div
class=
"pb-4"
>
<div
class=
"flex items-center justify-between pb-3 leading-8"
>
<span>
2D小樣本數字人
</span>
<span
class=
"text-gray cursor-pointer text-xs"
@
click=
"handleClickAll(ImageType.TWO_D_FEW_SHOT)"
>
全部
</span>
</div>
<div
class=
"flex flex-wrap gap-3"
>
<DigitalCard
v-for=
"item in twoDFewShotImageList.slice(0, 4)"
:key=
"item.id"
:value=
"item"
@
click=
"handleClickDigitalImage"
/>
</div>
</div>
</div>
<div
class=
"pb-4"
>
<div
class=
"flex items-center justify-between pb-3"
>
<span>
2D小樣本數字人
</span>
<span
class=
"text-gray cursor-pointer text-xs"
@
click=
"handleClickAll(ImageType.TWO_D_FEW_SHOT)"
>
全部
</span>
<div
v-else
>
<div
class=
"flex items-center gap-4 pb-3"
>
<n-button
text
@
click=
"showAll = false"
>
<template
#
icon
>
<CustomIcon
class=
"text-lg"
icon=
"mingcute:left-line"
/>
</
template
>
返回
</n-button>
<n-input
v-model:value=
"searchName"
round
placeholder=
"搜索"
@
input=
"handleSearch"
>
<
template
#
prefix
>
<CustomIcon
class=
"text-lg"
icon=
"mingcute:search-line"
/>
</
template
>
</n-input>
</div>
<div
class=
"flex flex-wrap gap-3"
>
<DigitalCard
v-for=
"item in twoDFewShotImageList.slice(0, 4)"
:key=
"item.id"
:value=
"item"
@
click=
"handleClickDigitalImage"
/>
<DigitalCard
v-for=
"item in allImageList"
:key=
"item.id"
:value=
"item"
@
click=
"handleClickDigitalImage"
/>
</div>
</div>
</div>
<div
v-else
>
<div
class=
"flex items-center gap-4 pb-3"
>
<n-button
text
@
click=
"showAll = false"
>
<template
#
icon
>
<CustomIcon
class=
"text-lg"
icon=
"mingcute:left-line"
/>
</
template
>
返回
</n-button>
<n-input
round
placeholder=
"搜索"
>
<
template
#
prefix
>
<CustomIcon
class=
"text-lg"
icon=
"mingcute:search-line"
/>
</
template
>
</n-input>
</div>
<div
class=
"flex flex-wrap gap-3"
>
<DigitalCard
v-for=
"item in allImageList"
:key=
"item.id"
:value=
"item"
@
click=
"handleClickDigitalImage"
/>
</div>
</div>
</template>
src/views/creation/components/digital/digital-position.vue
View file @
4ab1b529
<
script
setup
lang=
"ts"
>
import
{
ref
}
from
'vue'
import
{
useDigitalCreationStore
}
from
'@/store/modules/creation'
import
{
computed
}
from
'vue'
const
x
=
ref
(
0
)
const
y
=
ref
(
0
)
const
w
=
ref
(
0
)
const
h
=
ref
(
0
)
const
digitalCreationStore
=
useDigitalCreationStore
()
const
digitalImagePositionX
=
computed
({
get
()
{
return
digitalCreationStore
.
dhParams
.
position
.
x
},
set
(
value
)
{
digitalCreationStore
.
setDigitalImagePositionX
(
value
)
},
})
const
digitalImagePositionY
=
computed
({
get
()
{
return
digitalCreationStore
.
dhParams
.
position
.
y
},
set
(
value
)
{
digitalCreationStore
.
setDigitalImagePositionY
(
value
)
},
})
const
digitalImagePositionW
=
computed
({
get
()
{
return
digitalCreationStore
.
dhParams
.
position
.
w
},
set
(
width
)
{
const
height
=
(
width
*
16
)
/
9
digitalCreationStore
.
setDigitalImagePositionW
(
width
)
digitalCreationStore
.
setDigitalImagePositionH
(
parseInt
(
height
+
''
))
},
})
const
digitalImagePositionH
=
computed
({
get
()
{
return
digitalCreationStore
.
dhParams
.
position
.
h
},
set
(
height
)
{
const
width
=
(
height
*
9
)
/
16
digitalCreationStore
.
setDigitalImagePositionH
(
height
)
digitalCreationStore
.
setDigitalImagePositionW
(
parseInt
(
width
+
''
))
},
})
</
script
>
<
template
>
<div>
數字人位置
</div>
<div
class=
"mt-4 flex gap-4"
>
<n-input-number
v-model:value=
"x"
>
<template
#
prefix
>
X
</
template
>
</n-input-number>
<n-input-number
v-model:value=
"y"
>
<
template
#
prefix
>
Y
</
template
>
</n-input-number>
</div>
<div
class=
"mt-4 flex gap-4"
>
<n-input-number
v-model:value=
"w"
>
<
template
#
prefix
>
W
</
template
>
</n-input-number>
<n-input-number
v-model:value=
"h"
>
<
template
#
prefix
>
H
</
template
>
</n-input-number>
<div
class=
"h-full overflow-y-auto px-4 py-2"
>
<div>
數字人位置
</div>
<div
class=
"mt-4 flex gap-4"
>
<n-input-number
v-model:value=
"digitalImagePositionX"
>
<template
#
prefix
><div
class=
"text-gray w-4 text-center text-xs"
>
X
</div></
template
>
</n-input-number>
<n-input-number
v-model:value=
"digitalImagePositionY"
>
<
template
#
prefix
><div
class=
"text-gray w-4 text-center text-xs"
>
Y
</div></
template
>
</n-input-number>
</div>
<div
class=
"mt-4 flex"
>
<n-input-number
v-model:value=
"digitalImagePositionW"
class=
"flex-1"
>
<
template
#
prefix
><div
class=
"text-gray w-4 text-center text-xs"
>
W
</div></
template
>
</n-input-number>
<div
class=
"flex w-4 items-center justify-center"
>
<CustomIcon
class=
"text-gray text-xs"
icon=
"fa6-solid:lock"
/>
</div>
<n-input-number
v-model:value=
"digitalImagePositionH"
class=
"flex-1"
>
<
template
#
prefix
><div
class=
"text-gray w-4 text-center text-xs"
>
H
</div></
template
>
</n-input-number>
</div>
</div>
</template>
src/views/creation/components/digital/digital-setting.vue
View file @
4ab1b529
...
...
@@ -5,15 +5,21 @@ import DigitalPosition from './digital-position.vue'
</
script
>
<
template
>
<n-tabs
type=
"line"
animated
>
<n-tab-pane
name=
"human"
tab=
"選擇"
>
<n-tabs
type=
"line"
animated
class=
"h-full"
>
<n-tab-pane
name=
"human"
tab=
"選擇"
class=
"h-full"
>
<DigitalHuman
/>
</n-tab-pane>
<n-tab-pane
name=
"position"
tab=
"位置"
>
<n-tab-pane
name=
"position"
tab=
"位置"
class=
"h-full"
>
<DigitalPosition
/>
</n-tab-pane>
<n-tab-pane
name=
"audio"
tab=
"聲音"
>
<n-tab-pane
name=
"audio"
tab=
"聲音"
class=
"h-full"
>
<DigitalAudio
/>
</n-tab-pane>
</n-tabs>
</
template
>
<
style
lang=
"scss"
scoped
>
:deep
(
.n-tabs-nav-scroll-wrapper
)
{
padding
:
0
16px
;
}
</
style
>
src/views/creation/components/preview-content.vue
View file @
4ab1b529
<
script
setup
lang=
"ts"
>
import
{
useDigitalCreationStore
}
from
'@/store/modules/creation'
import
{
computed
,
ref
}
from
'vue'
const
digitalCreationStore
=
useDigitalCreationStore
()
const
previewContent
=
ref
<
HTMLElement
>
()
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
)
</
script
>
<
template
>
<div
class=
"flex flex-col overflow-hidden rounded-2xl"
>
<div
class=
"flex-1 overflow-hidden bg-gray-200"
>
<div
class=
"relative mx-auto aspect-[9/16] h-full bg-green-5
0"
>
<div
ref=
"previewContent"
class=
"relative mx-auto aspect-[9/16] h-full overflow-hidden bg-gray-10
0"
>
<img
v-show=
"digitalCreationStore.backgroundImageUrl"
:src=
"digitalCreationStore.backgroundImageUrl"
:src=
"digitalCreationStore.backgroundImageUrl
!
"
class=
"absolute h-full w-full object-cover"
/>
<img
v-show=
"digitalCreationStore.digitalImageUrl"
:src=
"digitalCreationStore.digitalImageUrl!"
class=
"absolute max-w-none object-fill"
:style=
"
{
width: `${digitalHumanWidth}px`,
height: `${digitalHumanHeight}px`,
left: `${digitalHumanLeft}px`,
top: `${digitalHumanTop}px`,
}"
/>
</div>
</div>
<div
class=
"flex bg-white p-4"
>
...
...
src/views/creation/components/subtitle/subtitle-setting.vue
0 → 100644
View file @
4ab1b529
<
script
setup
lang=
"ts"
>
import
{
useDigitalCreationStore
}
from
'@/store/modules/creation'
import
{
computed
}
from
'vue'
const
digitalCreationStore
=
useDigitalCreationStore
()
const
subtitleEnabled
=
computed
({
get
()
{
return
digitalCreationStore
.
subtitleParams
.
enabled
},
set
(
value
)
{
digitalCreationStore
.
setSubtitleEnabled
(
value
)
},
})
</
script
>
<
template
>
<n-tabs
type=
"line"
animated
class=
"h-full"
>
<n-tab-pane
name=
"subtitle"
tab=
"字幕"
class=
"h-full"
>
<div
class=
"flex h-full items-center justify-between overflow-y-auto px-4 py-2"
>
<span>
是否開啓
</span>
<n-switch
v-model:value=
"subtitleEnabled"
/>
</div>
</n-tab-pane>
</n-tabs>
</
template
>
<
style
lang=
"scss"
scoped
>
:deep
(
.n-tabs-nav-scroll-wrapper
)
{
padding
:
0
16px
;
}
</
style
>
src/views/creation/layout/index.vue
View file @
4ab1b529
<
script
setup
lang=
"ts"
>
import
{
fetchDigitalHumanTemplateStatus
}
from
'@/apis/digital-creation'
import
{
useDigitalCreationStore
}
from
'@/store/modules/creation'
import
{
DigitalTemplate
}
from
'@/store/types/creation'
import
{
onMounted
}
from
'vue'
import
HeaderBar
from
'./header-bar.vue'
import
MainContent
from
'./main-content.vue'
import
SideBar
from
'./side-bar.vue'
import
{
onMounted
}
from
'vue'
import
{
useDigitalCreationStore
}
from
'@/store/modules/creation'
const
digitalCreationStore
=
useDigitalCreationStore
()
...
...
@@ -16,7 +16,15 @@ onMounted(() => {
async
function
getDigitalImageList
(
id
:
number
)
{
const
res
=
await
fetchDigitalHumanTemplateStatus
<
DigitalTemplate
>
(
id
)
if
(
res
.
code
===
0
)
{
digitalCreationStore
.
updateDigitalCreation
(
res
.
data
)
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
)
}
}
</
script
>
...
...
src/views/creation/layout/side-bar.vue
View file @
4ab1b529
<
script
setup
lang=
"ts"
>
import
{
ref
}
from
'vue'
import
BackgroundSetting
from
'../components/background/background-setting.vue'
import
CaptionSetting
from
'../components/caption/caption
-setting.vue'
import
SubtitleSetting
from
'../components/subtitle/subtitle
-setting.vue'
import
DigitalSetting
from
'../components/digital/digital-setting.vue'
const
value
=
ref
(
''
)
const
value
=
ref
(
'
digital
'
)
const
barList
=
[
{
key
:
'digital'
,
...
...
@@ -17,7 +17,7 @@ const barList = [
icon
:
'icon-park-outline:background-color'
,
},
{
key
:
'
caption
'
,
key
:
'
subtitle
'
,
label
:
'字幕'
,
icon
:
'icon-park-outline:text-message'
,
},
...
...
@@ -27,10 +27,10 @@ const barList = [
<
template
>
<section
class=
"h-full pl-4"
>
<div
class=
"flex h-full rounded-2xl bg-white"
>
<div
class=
"flex-1 overflow-hidden p
x-4 p
y-2"
>
<div
class=
"flex-1 overflow-hidden py-2"
>
<DigitalSetting
v-if=
"value === 'digital'"
/>
<BackgroundSetting
v-if=
"value === 'background'"
/>
<
CaptionSetting
v-if=
"value === 'caption
'"
/>
<
SubtitleSetting
v-if=
"value === 'subtitle
'"
/>
</div>
<VerticalTabs
v-model:value=
"value"
class=
"border-l"
:list=
"barList"
></VerticalTabs>
</div>
...
...
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