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
You need to sign in or sign up before continuing.
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>() {
...
@@ -25,14 +25,19 @@ export function fetchBackgroundImage<T>() {
return
request
.
post
<
T
>
(
'/bizDigitalHumanImageRest/getBackgroundImage.json'
)
return
request
.
post
<
T
>
(
'/bizDigitalHumanImageRest/getBackgroundImage.json'
)
}
}
// 根据人物名称分页获取人物信息
// 上传背景图片
export
function
fetchInfoByImageName
<
T
>
(
imageName
:
string
)
{
export
function
uploadImageFile
<
T
>
(
imageName
:
string
,
formData
:
FormData
)
{
return
request
.
post
<
T
>
(
`/bizDigitalHumanImageRest/findByImageName.json?imageName=
${
imageName
}
`
)
return
request
.
post
<
T
>
(
`/baiduDigitalHumanFileRest/uploadImageFile.json?imageName=
${
imageName
}
`
,
formData
,
{
headers
:
{
'Content-Type'
:
'multipart/form-data'
},
timeout
:
12000
,
})
}
}
// 根据音色ID主键获取音色
// 根据人物名称分页获取人物信息
export
function
fetchDigitalHumanTimbreById
<
T
>
()
{
export
function
fetchInfoByImageName
<
T
>
(
imageName
:
string
)
{
return
request
.
post
<
T
>
(
'/bizDigitalHumanTimbreRest/getDigitalHumanTimbreById.json'
)
return
request
.
post
<
T
>
(
`/bizDigitalHumanImageRest/findByImageName.json?imageName=
${
imageName
}
`
,
{
pagingInfo
:
{
pageNo
:
1
,
pageSize
:
10
},
})
}
}
// 获取音色列表
// 获取音色列表
...
@@ -44,3 +49,11 @@ export function fetchDigitalHumanTimbreList<T>() {
...
@@ -44,3 +49,11 @@ export function fetchDigitalHumanTimbreList<T>() {
export
function
fetchTimbreByExample
<
T
>
(
condition
:
string
)
{
export
function
fetchTimbreByExample
<
T
>
(
condition
:
string
)
{
return
request
.
post
<
T
>
(
`/bizDigitalHumanTimbreRest/getByExample.json?condition=
${
condition
}
`
)
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>()
...
@@ -31,7 +31,7 @@ const emit = defineEmits<Emits>()
</div>
</div>
<div
<div
class=
"absolute h-8 w-20 rounded-full bg-blue-500 transition-transform duration-300"
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>
</div>
</div>
</
template
>
</
template
>
src/router/modules/creation.ts
View file @
4ab1b529
...
@@ -3,7 +3,7 @@ import Creation from '@/views/creation/creation.vue'
...
@@ -3,7 +3,7 @@ import Creation from '@/views/creation/creation.vue'
export
default
[
export
default
[
{
{
path
:
'/
fe/
creation'
,
path
:
'/creation'
,
name
:
'Creation'
,
name
:
'Creation'
,
meta
:
{
meta
:
{
rank
:
1001
,
rank
:
1001
,
...
...
src/store/modules/creation.ts
View file @
4ab1b529
...
@@ -3,49 +3,57 @@ import { DigitalTemplate } from '@/store/types/creation'
...
@@ -3,49 +3,57 @@ import { DigitalTemplate } from '@/store/types/creation'
function
defaultDigitalCreation
():
DigitalTemplate
{
function
defaultDigitalCreation
():
DigitalTemplate
{
return
{
return
{
id
:
1
,
id
:
null
,
templateType
:
'产品营销'
,
coverUrl
:
null
,
videoName
:
'营销视频'
,
demonstrationGifUrl
:
null
,
demonstrationVideoUrl
:
null
,
templateType
:
null
,
templateName
:
null
,
taskType
:
'BASE_VIDEO'
,
taskType
:
'BASE_VIDEO'
,
requestId
:
undefined
,
requestId
:
null
,
inputImageUrl
:
undefined
,
inputImageUrl
:
null
,
driveType
:
'TEXT'
,
driveType
:
'TEXT'
,
text
:
'嗨!我是挥手问候家'
,
text
:
null
,
ttsParams
:
{
ttsParams
:
{
person
:
'5153'
,
person
:
null
,
speed
:
'5'
,
speed
:
'5'
,
volume
:
'5'
,
volume
:
'5'
,
pitch
:
'5'
,
pitch
:
'5'
,
},
},
inputAudioUrl
:
undefined
,
inputAudioUrl
:
null
,
callbackUrl
:
undefined
,
callbackUrl
:
null
,
figureId
:
''
,
figureId
:
null
,
digitalImageUrl
:
null
,
videoParams
:
{
videoParams
:
{
width
:
192
0
,
width
:
0
,
height
:
0
,
height
:
0
,
transparent
:
false
,
transparent
:
false
,
},
},
dhParams
:
{
dhParams
:
{
cameraId
:
undefined
,
cameraId
:
null
,
position
:
{
position
:
{
x
:
10
0
,
x
:
0
,
y
:
111
,
y
:
0
,
w
:
undefined
,
w
:
0
,
h
:
undefined
,
h
:
0
,
},
},
},
},
subtitleParams
:
{
subtitleParams
:
{
subtitlePolicy
:
'SRT'
,
subtitlePolicy
:
'SRT'
,
enabled
:
tru
e
,
enabled
:
fals
e
,
},
},
backgroundImageUrl
:
undefined
,
backgroundImageUrl
:
null
,
autoAnimoji
:
tru
e
,
autoAnimoji
:
fals
e
,
enablePalindrome
:
false
,
enablePalindrome
:
false
,
templateId
:
null
,
templateId
:
null
,
title
:
undefined
,
title
:
null
,
logoParams
:
null
,
logoParams
:
{
bgmParams
:
null
,
logoUrl
:
null
,
materialUrl
:
undefined
,
},
bgmParams
:
{
bgmUrl
:
null
,
},
materialUrl
:
null
,
}
}
}
}
...
@@ -57,14 +65,50 @@ export const useDigitalCreationStore = defineStore('digital-creation-store', {
...
@@ -57,14 +65,50 @@ export const useDigitalCreationStore = defineStore('digital-creation-store', {
state
:
():
DigitalTemplate
=>
getLocalState
(),
state
:
():
DigitalTemplate
=>
getLocalState
(),
actions
:
{
actions
:
{
setFigureId
(
figureId
:
string
|
null
)
{
setFigureId
(
figureId
:
string
)
{
this
.
figureId
=
figureId
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
)
{
setBackgroundImageUrl
(
backgroundImageUrl
:
string
)
{
this
.
backgroundImageUrl
=
backgroundImageUrl
this
.
backgroundImageUrl
=
backgroundImageUrl
},
},
setSubtitleEnabled
(
subtitleEnabled
:
boolean
)
{
this
.
subtitleParams
.
enabled
=
subtitleEnabled
},
updateDigitalCreation
(
digitalCreation
:
DigitalTemplate
)
{
updateDigitalCreation
(
digitalCreation
:
DigitalTemplate
)
{
this
.
$state
=
{
...
this
.
$state
,
...
digitalCreation
}
this
.
$state
=
{
...
this
.
$state
,
...
digitalCreation
}
},
},
...
...
src/store/types/creation.ts
View file @
4ab1b529
export
interface
DigitalTemplate
{
export
interface
DigitalTemplate
{
id
:
number
id
:
number
|
null
templateType
:
string
coverUrl
:
string
|
null
videoName
:
string
demonstrationGifUrl
:
string
|
null
demonstrationVideoUrl
:
string
|
null
templateType
:
string
|
null
templateName
:
string
|
null
taskType
:
string
taskType
:
string
requestId
?:
string
requestId
:
string
|
null
inputImageUrl
?:
string
inputImageUrl
:
string
|
null
driveType
:
string
driveType
:
string
text
:
string
text
:
string
|
null
ttsParams
:
{
ttsParams
:
{
person
:
string
person
:
string
|
null
speed
:
string
speed
:
string
volume
:
string
volume
:
string
pitch
:
string
pitch
:
string
}
}
inputAudioUrl
?:
string
inputAudioUrl
:
string
|
null
callbackUrl
?:
string
callbackUrl
:
string
|
null
figureId
:
string
|
null
figureId
:
string
|
null
digitalImageUrl
?:
string
|
null
videoParams
:
{
videoParams
:
{
width
:
number
width
:
number
|
null
height
:
number
height
:
number
|
null
transparent
:
boolean
transparent
:
boolean
}
}
dhParams
:
{
dhParams
:
{
cameraId
?:
number
cameraId
:
number
|
null
position
:
{
position
:
{
x
:
number
x
:
number
y
:
number
y
:
number
w
?
:
number
w
:
number
h
?
:
number
h
:
number
}
}
}
}
subtitleParams
:
{
subtitleParams
:
{
subtitlePolicy
:
string
subtitlePolicy
:
string
|
null
enabled
:
boolean
enabled
:
boolean
}
}
backgroundImageUrl
?:
string
backgroundImageUrl
:
string
|
null
autoAnimoji
:
boolean
autoAnimoji
:
boolean
enablePalindrome
:
boolean
enablePalindrome
:
boolean
templateId
?:
any
templateId
:
string
|
null
title
?:
string
title
:
string
|
null
logoParams
?:
any
logoParams
:
{
bgmParams
?:
any
logoUrl
:
string
|
null
materialUrl
?:
string
}
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
{
export
interface
ImageItem
{
...
@@ -52,9 +67,12 @@ export interface ImageItem {
...
@@ -52,9 +67,12 @@ export interface ImageItem {
imageUrl
:
string
imageUrl
:
string
}
}
export
enum
ImageType
{
export
interface
TimbreItem
{
THREE_D
=
'THREE_D'
,
id
:
number
TWO_D_BOUTIQUE
=
'TWO_D_BOUTIQUE'
,
name
:
string
TWO_D_FEW_SHOT
=
'TWO_D_FEW_SHOT'
,
timebreId
:
string
BACKGROUND
=
'BACKGROUND'
,
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"
>
<
script
setup
lang=
"ts"
>
import
BackgroundImages
from
'./background-images.vue'
import
{
fetchBackgroundImage
,
fetchInfoByImageName
,
uploadImageFile
}
from
'@/apis/digital-creation'
// import BackgroundPosition from './background-position.vue'
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
>
</
script
>
<
template
>
<
template
>
<n-tabs
type=
"line"
animated
>
<n-tabs
type=
"line"
animated
class=
"h-full"
>
<n-tab-pane
name=
"images"
tab=
"圖片"
>
<n-tab-pane
name=
"images"
tab=
"圖片"
class=
"h-full"
>
<BackgroundImages
/>
<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>
<!--
<n-tab-pane
name=
"position"
tab=
"位置"
>
<BackgroundPosition
/>
</n-tab-pane>
-->
</n-tabs>
</n-tabs>
</template>
</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"
>
<
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
digitalCreationStore
=
useDigitalCreationStore
()
const
intonation
=
ref
(
1
)
const
sexValue
=
ref
(
0
)
const
showAll
=
ref
(
false
)
const
sexList
=
[
const
tabValue
=
ref
(
0
)
{
key
:
0
,
label
:
'女性'
},
const
tablist
=
[
{
key
:
1
,
label
:
'男性'
},
{
label
:
'女性'
,
key
:
0
},
{
label
:
'男性'
,
key
:
1
},
]
]
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
>
</
script
>
<
template
>
<
template
>
<div
v-if=
"!showAll"
>
<div
class=
"h-full overflow-y-auto px-4 py-2"
>
<div
class=
"relative flex items-center gap-2 rounded-2xl border p-2"
>
<div
v-if=
"!showAll"
>
<div
class=
"h-16 w-16 rounded-lg bg-gray-200"
></div>
<DigitalAudioCard
:value=
"digitalTimbreValue"
show-toggle
@
toggle=
"showAll = true"
/>
<div
class=
"flex-1 overflow-hidden"
>
<div
class=
"mb-2 flex items-center gap-2"
>
<div
class=
"mt-4 text-lg"
>
聲音
</div>
<div
class=
"max-w-24 truncate"
>
度清風
</div>
<div
class=
"mt-4 flex items-center gap-2"
>
<CustomIcon
class=
"cursor-pointer text-lg"
icon=
"mingcute:volume-line"
/>
<div
class=
"w-12"
>
語速:
</div>
</div>
<n-slider
v-model:value=
"speed"
class=
"flex-1"
:max=
"15"
:min=
"0"
:step=
"1"
/>
<div
class=
"flex gap-2"
>
<div
class=
"w-10"
>
{{
speed
}}
</div>
<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>
<div
class=
"absolute right-2 top-2"
>
<div
class=
"mt-4 flex items-center gap-2"
>
<CustomIcon
class=
"cursor-pointer text-lg"
icon=
"ant-design:swap-outlined"
@
click=
"showAll = true"
/>
<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>
</div>
<div
class=
"mt-4 text-lg"
>
聲音
</div>
<div
v-else
>
<div
class=
"mt-4 flex items-center gap-2"
>
<div
class=
"flex items-center gap-4 pb-3"
>
<div
class=
"w-12"
>
語速:
</div>
<n-button
text
@
click=
"showAll = false"
>
<n-slider
v-model:value=
"pace"
class=
"flex-1"
:max=
"1.5"
:min=
"0.5"
:step=
"0.25"
/>
<template
#
icon
>
<div
class=
"w-10"
>
{{
pace
}}
x
</div>
<CustomIcon
class=
"text-lg"
icon=
"mingcute:left-line"
/>
</div>
</
template
>
<div
class=
"mt-4 flex items-center gap-2"
>
返回
<div
class=
"w-12"
>
語調:
</div>
</n-button>
<n-slider
v-model:value=
"intonation"
class=
"flex-1"
:max=
"5"
:min=
"1"
:step=
"1"
/>
<n-input
v-model:value=
"searchName"
round
placeholder=
"搜索"
@
input=
"handleSearch"
>
<div
class=
"w-10"
>
{{
intonation
}}
</div>
<
template
#
prefix
>
</div>
<CustomIcon
class=
"text-lg"
icon=
"mingcute:search-line"
/>
</div>
</
template
>
</n-input>
<div
v-else
>
</div>
<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 justify-end pb-3"
>
<div
class=
"flex justify-end pb-3"
>
<HorizontalTabs
v-model:value=
"tabValue"
:list=
"tabl
ist"
/>
<HorizontalTabs
v-model:value=
"sexValue"
:list=
"sexL
ist"
/>
</div>
</div>
<div>
<div>
<div
class=
"flex items-center gap-2 rounded-2xl border p-2"
>
<DigitalAudioCard
<div
class=
"h-16 w-16 rounded-lg bg-gray-200"
></div>
v-for=
"(timbre, index) in sexValue ? digitalTimbreMaleList : digitalTimbreFemaleList"
<div
class=
"flex-1 overflow-hidden"
>
:key=
"index"
<div
class=
"mb-2 flex items-center gap-2"
>
:value=
"timbre"
<div
class=
"max-w-24 truncate"
>
度清風
</div>
@
click=
"handleClickAudioCard"
<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>
</div>
</div>
</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"
>
<
script
setup
lang=
"ts"
>
import
{
ImageItem
}
from
'@/store/types/creation'
import
{
useDigitalCreationStore
}
from
'@/store/modules/creation'
import
{
useDigitalCreationStore
}
from
'@/store/modules/creation'
import
{
ImageItem
}
from
'@/store/types/creation'
interface
Props
{
interface
Props
{
value
:
ImageItem
value
:
ImageItem
}
}
interface
Emits
{
interface
Emits
{
(
e
:
'click'
,
id
:
string
|
null
):
void
(
e
:
'click'
,
value
:
ImageItem
):
void
}
}
defineProps
<
Props
>
()
defineProps
<
Props
>
()
...
@@ -18,18 +18,18 @@ const digitalCreationStore = useDigitalCreationStore()
...
@@ -18,18 +18,18 @@ const digitalCreationStore = useDigitalCreationStore()
<
template
>
<
template
>
<div
<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'"
:class=
"digitalCreationStore.figureId === value.figureId ? 'border-blue' : 'border-transparent'"
@
click=
"emit('click', value
.figureId
)"
@
click=
"emit('click', value)"
>
>
<img
:src=
"value.imageUrl"
/>
<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
}}
{{
value
.
imageName
}}
</div>
</div>
<
!--
<
CustomIcon
<CustomIcon
v-if=
"digitalCreationStore.figureId === value.figureId"
v-if=
"digitalCreationStore.figureId === value.figureId"
icon=
"
teenyicons:tick-circle-soli
d"
icon=
"
si-glyph:checke
d"
class=
"text-blue absolute
-left-2 -top-2 text-lg
"
class=
"text-blue absolute
left-0 top-0 text-xs
"
/>
-->
/>
</div>
</div>
</
template
>
</
template
>
src/views/creation/components/digital/digital-human.vue
View file @
4ab1b529
<
script
setup
lang=
"ts"
>
<
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
{
ImageItem
,
ImageType
}
from
'@/store/types/creation'
import
{
onMounted
,
ref
}
from
'vue'
import
{
onMounted
,
ref
}
from
'vue'
import
DigitalCard
from
'./digital-card.vue'
import
DigitalCard
from
'./digital-human-card.vue'
import
{
useDigitalCreationStore
}
from
'@/store/modules/creation'
const
digitalCreationStore
=
useDigitalCreationStore
()
const
digitalCreationStore
=
useDigitalCreationStore
()
const
threeDImageList
=
ref
<
ImageItem
[]
>
([])
const
threeDImageList
=
ref
<
ImageItem
[]
>
([])
...
@@ -11,6 +16,7 @@ const twoDBoutiqueImageList = ref<ImageItem[]>([])
...
@@ -11,6 +16,7 @@ const twoDBoutiqueImageList = ref<ImageItem[]>([])
const
twoDFewShotImageList
=
ref
<
ImageItem
[]
>
([])
const
twoDFewShotImageList
=
ref
<
ImageItem
[]
>
([])
const
allImageList
=
ref
<
ImageItem
[]
>
([])
const
allImageList
=
ref
<
ImageItem
[]
>
([])
const
showAll
=
ref
(
false
)
const
showAll
=
ref
(
false
)
const
searchName
=
ref
(
''
)
onMounted
(()
=>
{
onMounted
(()
=>
{
getDigitalImageList
()
getDigitalImageList
()
...
@@ -27,8 +33,16 @@ async function getDigitalImageList() {
...
@@ -27,8 +33,16 @@ async function getDigitalImageList() {
res3
.
code
===
0
&&
(
twoDFewShotImageList
.
value
=
res3
.
data
)
res3
.
code
===
0
&&
(
twoDFewShotImageList
.
value
=
res3
.
data
)
}
}
function
handleClickDigitalImage
(
id
:
string
|
null
)
{
async
function
handleSearch
(
value
:
string
)
{
digitalCreationStore
.
setFigureId
(
id
)
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
)
{
function
handleClickAll
(
imageType
:
ImageType
)
{
...
@@ -48,69 +62,71 @@ function handleClickAll(imageType: ImageType) {
...
@@ -48,69 +62,71 @@ function handleClickAll(imageType: ImageType) {
</
script
>
</
script
>
<
template
>
<
template
>
<div
v-if=
"!showAll"
>
<div
class=
"h-full overflow-y-auto px-4 py-2"
>
<div
class=
"pb-4"
>
<div
v-if=
"!showAll"
>
<div
class=
"flex items-center justify-between pb-3"
>
<div
class=
"pb-4"
>
<span>
3D數字人
</span>
<div
class=
"flex items-center justify-between pb-3 leading-8"
>
<span
class=
"text-gray cursor-pointer text-xs"
@
click=
"handleClickAll(ImageType.THREE_D)"
>
全部
</span>
<span>
3D數字人
</span>
</div>
<span
class=
"text-gray cursor-pointer text-xs"
@
click=
"handleClickAll(ImageType.THREE_D)"
>
全部
</span>
<div
class=
"flex flex-wrap gap-3"
>
</div>
<DigitalCard
<div
class=
"flex flex-wrap gap-3"
>
v-for=
"item in threeDImageList.slice(0, 4)"
<DigitalCard
:key=
"item.id"
v-for=
"item in threeDImageList.slice(0, 4)"
:value=
"item"
:key=
"item.id"
@
click=
"handleClickDigitalImage"
:value=
"item"
/>
@
click=
"handleClickDigitalImage"
/>
</div>
</div>
</div>
</div>
<div
class=
"pb-4"
>
<div
class=
"pb-4"
>
<div
class=
"flex items-center justify-between pb-3"
>
<div
class=
"flex items-center justify-between pb-3 leading-8"
>
<span>
2D精品數字人
</span>
<span>
2D精品數字人
</span>
<span
class=
"text-gray cursor-pointer text-xs"
@
click=
"handleClickAll(ImageType.TWO_D_BOUTIQUE)"
>
全部
</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>
<div
class=
"flex flex-wrap gap-3"
>
<DigitalCard
<div
class=
"pb-4"
>
v-for=
"item in twoDBoutiqueImageList.slice(0, 4)"
<div
class=
"flex items-center justify-between pb-3 leading-8"
>
:key=
"item.id"
<span>
2D小樣本數字人
</span>
:value=
"item"
<span
class=
"text-gray cursor-pointer text-xs"
@
click=
"handleClickAll(ImageType.TWO_D_FEW_SHOT)"
>
全部
</span>
@
click=
"handleClickDigitalImage"
</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>
</div>
<div
class=
"pb-4"
>
<div
v-else
>
<div
class=
"flex items-center justify-between pb-3"
>
<div
class=
"flex items-center gap-4 pb-3"
>
<span>
2D小樣本數字人
</span>
<n-button
text
@
click=
"showAll = false"
>
<span
class=
"text-gray cursor-pointer text-xs"
@
click=
"handleClickAll(ImageType.TWO_D_FEW_SHOT)"
>
全部
</span>
<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>
<div
class=
"flex flex-wrap gap-3"
>
<div
class=
"flex flex-wrap gap-3"
>
<DigitalCard
<DigitalCard
v-for=
"item in allImageList"
:key=
"item.id"
:value=
"item"
@
click=
"handleClickDigitalImage"
/>
v-for=
"item in twoDFewShotImageList.slice(0, 4)"
:key=
"item.id"
:value=
"item"
@
click=
"handleClickDigitalImage"
/>
</div>
</div>
</div>
</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>
</template>
src/views/creation/components/digital/digital-position.vue
View file @
4ab1b529
<
script
setup
lang=
"ts"
>
<
script
setup
lang=
"ts"
>
import
{
ref
}
from
'vue'
import
{
useDigitalCreationStore
}
from
'@/store/modules/creation'
import
{
computed
}
from
'vue'
const
x
=
ref
(
0
)
const
digitalCreationStore
=
useDigitalCreationStore
()
const
y
=
ref
(
0
)
const
w
=
ref
(
0
)
const
digitalImagePositionX
=
computed
({
const
h
=
ref
(
0
)
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
>
</
script
>
<
template
>
<
template
>
<div>
數字人位置
</div>
<div
class=
"h-full overflow-y-auto px-4 py-2"
>
<div
class=
"mt-4 flex gap-4"
>
<div>
數字人位置
</div>
<n-input-number
v-model:value=
"x"
>
<div
class=
"mt-4 flex gap-4"
>
<template
#
prefix
>
X
</
template
>
<n-input-number
v-model:value=
"digitalImagePositionX"
>
</n-input-number>
<template
#
prefix
><div
class=
"text-gray w-4 text-center text-xs"
>
X
</div></
template
>
<n-input-number
v-model:value=
"y"
>
</n-input-number>
<
template
#
prefix
>
Y
</
template
>
<n-input-number
v-model:value=
"digitalImagePositionY"
>
</n-input-number>
<
template
#
prefix
><div
class=
"text-gray w-4 text-center text-xs"
>
Y
</div></
template
>
</div>
</n-input-number>
<div
class=
"mt-4 flex gap-4"
>
</div>
<n-input-number
v-model:value=
"w"
>
<div
class=
"mt-4 flex"
>
<
template
#
prefix
>
W
</
template
>
<n-input-number
v-model:value=
"digitalImagePositionW"
class=
"flex-1"
>
</n-input-number>
<
template
#
prefix
><div
class=
"text-gray w-4 text-center text-xs"
>
W
</div></
template
>
<n-input-number
v-model:value=
"h"
>
</n-input-number>
<
template
#
prefix
>
H
</
template
>
<div
class=
"flex w-4 items-center justify-center"
>
</n-input-number>
<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>
</div>
</template>
</template>
src/views/creation/components/digital/digital-setting.vue
View file @
4ab1b529
...
@@ -5,15 +5,21 @@ import DigitalPosition from './digital-position.vue'
...
@@ -5,15 +5,21 @@ import DigitalPosition from './digital-position.vue'
</
script
>
</
script
>
<
template
>
<
template
>
<n-tabs
type=
"line"
animated
>
<n-tabs
type=
"line"
animated
class=
"h-full"
>
<n-tab-pane
name=
"human"
tab=
"選擇"
>
<n-tab-pane
name=
"human"
tab=
"選擇"
class=
"h-full"
>
<DigitalHuman
/>
<DigitalHuman
/>
</n-tab-pane>
</n-tab-pane>
<n-tab-pane
name=
"position"
tab=
"位置"
>
<n-tab-pane
name=
"position"
tab=
"位置"
class=
"h-full"
>
<DigitalPosition
/>
<DigitalPosition
/>
</n-tab-pane>
</n-tab-pane>
<n-tab-pane
name=
"audio"
tab=
"聲音"
>
<n-tab-pane
name=
"audio"
tab=
"聲音"
class=
"h-full"
>
<DigitalAudio
/>
<DigitalAudio
/>
</n-tab-pane>
</n-tab-pane>
</n-tabs>
</n-tabs>
</
template
>
</
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"
>
<
script
setup
lang=
"ts"
>
import
{
useDigitalCreationStore
}
from
'@/store/modules/creation'
import
{
useDigitalCreationStore
}
from
'@/store/modules/creation'
import
{
computed
,
ref
}
from
'vue'
const
digitalCreationStore
=
useDigitalCreationStore
()
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
>
</
script
>
<
template
>
<
template
>
<div
class=
"flex flex-col overflow-hidden rounded-2xl"
>
<div
class=
"flex flex-col overflow-hidden rounded-2xl"
>
<div
class=
"flex-1 overflow-hidden bg-gray-200"
>
<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
<img
v-show=
"digitalCreationStore.backgroundImageUrl"
v-show=
"digitalCreationStore.backgroundImageUrl"
:src=
"digitalCreationStore.backgroundImageUrl"
:src=
"digitalCreationStore.backgroundImageUrl
!
"
class=
"absolute h-full w-full object-cover"
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>
</div>
<div
class=
"flex bg-white p-4"
>
<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"
>
<
script
setup
lang=
"ts"
>
import
{
fetchDigitalHumanTemplateStatus
}
from
'@/apis/digital-creation'
import
{
fetchDigitalHumanTemplateStatus
}
from
'@/apis/digital-creation'
import
{
useDigitalCreationStore
}
from
'@/store/modules/creation'
import
{
DigitalTemplate
}
from
'@/store/types/creation'
import
{
DigitalTemplate
}
from
'@/store/types/creation'
import
{
onMounted
}
from
'vue'
import
HeaderBar
from
'./header-bar.vue'
import
HeaderBar
from
'./header-bar.vue'
import
MainContent
from
'./main-content.vue'
import
MainContent
from
'./main-content.vue'
import
SideBar
from
'./side-bar.vue'
import
SideBar
from
'./side-bar.vue'
import
{
onMounted
}
from
'vue'
import
{
useDigitalCreationStore
}
from
'@/store/modules/creation'
const
digitalCreationStore
=
useDigitalCreationStore
()
const
digitalCreationStore
=
useDigitalCreationStore
()
...
@@ -16,7 +16,15 @@ onMounted(() => {
...
@@ -16,7 +16,15 @@ onMounted(() => {
async
function
getDigitalImageList
(
id
:
number
)
{
async
function
getDigitalImageList
(
id
:
number
)
{
const
res
=
await
fetchDigitalHumanTemplateStatus
<
DigitalTemplate
>
(
id
)
const
res
=
await
fetchDigitalHumanTemplateStatus
<
DigitalTemplate
>
(
id
)
if
(
res
.
code
===
0
)
{
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
>
</
script
>
...
...
src/views/creation/layout/side-bar.vue
View file @
4ab1b529
<
script
setup
lang=
"ts"
>
<
script
setup
lang=
"ts"
>
import
{
ref
}
from
'vue'
import
{
ref
}
from
'vue'
import
BackgroundSetting
from
'../components/background/background-setting.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'
import
DigitalSetting
from
'../components/digital/digital-setting.vue'
const
value
=
ref
(
''
)
const
value
=
ref
(
'
digital
'
)
const
barList
=
[
const
barList
=
[
{
{
key
:
'digital'
,
key
:
'digital'
,
...
@@ -17,7 +17,7 @@ const barList = [
...
@@ -17,7 +17,7 @@ const barList = [
icon
:
'icon-park-outline:background-color'
,
icon
:
'icon-park-outline:background-color'
,
},
},
{
{
key
:
'
caption
'
,
key
:
'
subtitle
'
,
label
:
'字幕'
,
label
:
'字幕'
,
icon
:
'icon-park-outline:text-message'
,
icon
:
'icon-park-outline:text-message'
,
},
},
...
@@ -27,10 +27,10 @@ const barList = [
...
@@ -27,10 +27,10 @@ const barList = [
<
template
>
<
template
>
<section
class=
"h-full pl-4"
>
<section
class=
"h-full pl-4"
>
<div
class=
"flex h-full rounded-2xl bg-white"
>
<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'"
/>
<DigitalSetting
v-if=
"value === 'digital'"
/>
<BackgroundSetting
v-if=
"value === 'background'"
/>
<BackgroundSetting
v-if=
"value === 'background'"
/>
<
CaptionSetting
v-if=
"value === 'caption
'"
/>
<
SubtitleSetting
v-if=
"value === 'subtitle
'"
/>
</div>
</div>
<VerticalTabs
v-model:value=
"value"
class=
"border-l"
:list=
"barList"
></VerticalTabs>
<VerticalTabs
v-model:value=
"value"
class=
"border-l"
:list=
"barList"
></VerticalTabs>
</div>
</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