Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
M
mengchangaigc
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
姚珂
mengchangaigc
Commits
626b653c
Commit
626b653c
authored
Aug 29, 2026
by
黄同智
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
工具类页面
parent
96e60a33
Hide whitespace changes
Inline
Side-by-side
Showing
13 changed files
with
1671 additions
and
214 deletions
+1671
-214
index.ts
apps/web-vue/src/router/index.ts
+18
-0
CreativeComposer.vue
...e/src/views/quickCreation/components/CreativeComposer.vue
+10
-205
DigitalHumanDialog.vue
...src/views/quickCreation/components/DigitalHumanDialog.vue
+218
-0
InspirationGallery.vue
...src/views/quickCreation/components/InspirationGallery.vue
+2
-2
MediaMaterialDialog.vue
...rc/views/quickCreation/components/MediaMaterialDialog.vue
+238
-0
ModelTryOnDialog.vue
...e/src/views/quickCreation/components/ModelTryOnDialog.vue
+472
-0
Remove-video.vue
...b-vue/src/views/quickCreation/components/Remove-video.vue
+198
-0
ToolGroup.vue
.../web-vue/src/views/quickCreation/components/ToolGroup.vue
+42
-7
remove-quality.vue
apps/web-vue/src/views/quickCreation/remove-quality.vue
+299
-0
remove-subtitles.vue
apps/web-vue/src/views/quickCreation/remove-subtitles.vue
+126
-0
remove-watermark.vue
apps/web-vue/src/views/quickCreation/remove-watermark.vue
+40
-0
types.ts
apps/web-vue/src/views/quickCreation/types.ts
+8
-0
Item.vue
apps/web-vue/src/views/resource/components/Item.vue
+0
-0
No files found.
apps/web-vue/src/router/index.ts
View file @
626b653c
...
...
@@ -51,6 +51,24 @@ const routes = [
name
:
'QuickCreation'
,
component
:
QuickCreation
,
meta
:
{
title
:
'快速创作'
,
icon
:
'add'
,
affix
:
true
}
},
{
path
:
'remove-watermark'
,
name
:
'RemoveWatermark'
,
component
:
()
=>
import
(
'@/views/quickCreation/remove-watermark.vue'
),
meta
:
{
title
:
'视频去水印'
,
hidden
:
true
}
},
{
path
:
'remove-subtitles'
,
name
:
'RemoveSubtitles'
,
component
:
()
=>
import
(
'@/views/quickCreation/remove-subtitles.vue'
),
meta
:
{
title
:
'视频去字幕'
,
hidden
:
true
}
},
{
path
:
'remove-quality'
,
name
:
'RemoveQuality'
,
component
:
()
=>
import
(
'@/views/quickCreation/remove-quality.vue'
),
meta
:
{
title
:
'视频画质增强'
,
hidden
:
true
}
}
]
},
...
...
apps/web-vue/src/views/quickCreation/components/CreativeComposer.vue
View file @
626b653c
...
...
@@ -80,8 +80,8 @@
<div
class=
" lh-20 color-000"
>
1. 视频长度 (20 - 120 秒)
</div>
<div
class=
"flex-items-center gap-10 mt-10"
>
<el-button
v-for=
"item in durationOptions"
:key=
"item"
class=
"flex-center h-30 px-20"
:class=
"{ active: duration === item }"
plain
@
click=
"duration = item"
>
class=
"flex-center h-30 px-20"
:class=
"{ active: duration === item }"
plain
@
click=
"duration = item"
>
{{ item }}s
</el-button>
</div>
...
...
@@ -98,8 +98,8 @@
<span
class=
"phone-icon w-8 h-16 mr-4"
></span>
<span>
9:16 竖屏
</span>
</el-button>
<el-button
class=
"flex-center h-34 px-20"
:class=
"{ active: ratio === 'landscape' }"
plain
@
click=
"ratio = 'landscape'"
>
<el-button
class=
"flex-center h-34 px-20"
:class=
"{ active: ratio === 'landscape' }"
plain
@
click=
"ratio = 'landscape'"
>
<span
class=
"screen-icon w-16 h-10 mr-4"
></span>
<span>
16:9 横屏
</span>
</el-button>
...
...
@@ -169,78 +169,7 @@
</el-button>
</div>
<el-dialog
v-model=
"mediaVisible"
width=
"672"
align-center
append-to-body
class=
"quick-media-dialog"
>
<div
class=
"media-dialog hidden round-12"
>
<div
class=
"flex-between px-18 py-16"
>
<div
class=
"flex-items-center gap-8"
>
<el-icon
class=
"color-main fs-18"
>
<Picture
/>
</el-icon>
<h3
class=
"fs-16 lh-24 color-000"
>
选择参考素材
</h3>
<span
class=
"soft-pill h-22 lh-22 px-10 round-12 color-888"
>
最多选择 5 项
</span>
</div>
<el-button
class=
"close-btn wh-28 p-0 border-none bg-none color-888 pointer"
circle
@
click=
"mediaVisible = false"
>
<el-icon>
<Close
/>
</el-icon>
</el-button>
</div>
<div
class=
"flex-between px-14 pb-14"
>
<div
class=
"media-tabs flex-items-center h-32 p-4 round-8"
>
<el-button
v-for=
"item in mediaTabs"
:key=
"item"
class=
"h-26 px-16 round-6 border-none bg-none pointer"
:class=
"{ active: mediaTab === item }"
text
@
click=
"mediaTab = item"
>
{{ item }}
</el-button>
</div>
<div
class=
"flex-items-center gap-8"
>
<div
class=
"search-mini flex-items-center h-32 w-194 px-10 round-8 bg-fff"
>
<el-icon
class=
"color-888"
>
<Search
/>
</el-icon>
<el-input
v-model=
"mediaKeyword"
class=
"media-search flex-1 minw-0 ml-8 "
placeholder=
"搜索资产..."
/>
</div>
<el-button
class=
"upload-local flex-center gap-6 h-32 px-14 round-8 border-none color-fff pointer"
>
<el-icon>
<Upload
/>
</el-icon>
<span>
本地上传
</span>
</el-button>
</div>
</div>
<div
class=
"media-grid flex flex-wrap gap-12 px-14 pt-14 minh-300"
>
<el-card
v-for=
"item in filteredMaterials"
:key=
"item.id"
class=
"media-card relative w-150 h-152 round-8 hidden border-none pointer text-left"
:class=
"{ selected: selectedMaterials.includes(item.id) }"
shadow=
"never"
@
click=
"toggleMaterial(item.id)"
>
<img
v-if=
"item.image"
:src=
"item.image"
:alt=
"item.name"
class=
"w-full h-full cover"
/>
<span
v-else
class=
"video-placeholder flex-center w-full h-full color-888"
>
VIDEO
</span>
<span
v-if=
"item.kind === '视频'"
class=
"video-tag absolute top-8 right-8 h-18 lh-18 px-6 round-4 color-fff fs-10 "
>
VIDEO
</span>
<span
class=
"media-mask absolute bottom-0 left-0 w-full px-8 py-8 color-fff"
>
<span
class=
"media-name lh-18 ellipsis"
>
{{ item.name }}
</span>
<span
class=
"media-size fs-10"
>
{{ item.size }}
</span>
</span>
</el-card>
</div>
<div
class=
"media-footer flex-between px-18 py-16 mt-18"
>
<span
class=
" color-888"
>
已选择
<strong
class=
"color-main"
>
{{ selectedMaterials.length }}
</strong>
/ 5
个素材
</span>
<div
class=
"flex-items-center gap-10"
>
<el-button
class=
"footer-btn h-36 px-20 round-12 bg-fff pointer"
@
click=
"mediaVisible = false"
>
取消
</el-button>
<el-button
class=
"footer-btn confirm h-36 px-20 round-12 border-none pointer"
type=
"primary"
@
click=
"mediaVisible = false"
>
确认选择
</el-button>
</div>
</div>
</div>
</el-dialog>
<MediaMaterialDialog
v-model=
"mediaVisible"
v-model:selected=
"selectedMaterials"
/>
</div>
</template>
...
...
@@ -256,15 +185,12 @@ import {
Monitor
,
Operation
,
Picture
,
Search
,
Upload
,
User
,
VideoCamera
,
}
from
'@element-plus/icons-vue'
;
import
productImg
from
'@/static/image/huaban.png'
;
import
peopleImg
from
'@/static/image/meinv.png'
;
import
detailImg
from
'@/static/image/long.png'
;
import
type
{
SceneKey
}
from
'../types'
;
import
MediaMaterialDialog
from
'./MediaMaterialDialog.vue'
;
import
type
{
MaterialItem
,
SceneKey
}
from
'../types'
;
type
SceneItem
=
{
key
:
SceneKey
;
...
...
@@ -290,7 +216,6 @@ const emit = defineEmits<{
clearScene
:
[];
}
>
();
const
defaultDesc
=
'参考已上传的图片/视频,生成相关的产品素材。'
;
const
prompt
=
ref
(
''
);
const
mediaVisible
=
ref
(
false
);
const
settingVisible
=
ref
(
false
);
...
...
@@ -299,9 +224,7 @@ const duration = ref(30);
const
ratio
=
ref
<
'portrait'
|
'landscape'
>
(
'portrait'
);
const
autoFix
=
ref
(
true
);
const
outputTarget
=
ref
<
'image'
|
'video'
>
(
'video'
);
const
mediaTab
=
ref
(
'全部'
);
const
mediaKeyword
=
ref
(
''
);
const
selectedMaterials
=
ref
<
string
[]
>
([]);
const
selectedMaterials
=
ref
<
MaterialItem
[]
>
([]);
const
scenes
:
SceneItem
[]
=
[
{
key
:
'product'
,
label
:
'产品素材'
,
desc
:
'参考已上传的图片/视频,生成相关的产品素材。'
,
color
:
'#1877ff'
,
uploads
:
[
'上传图片/视频素材'
]
},
...
...
@@ -334,36 +257,7 @@ const modeOptions: ModeOption[] = [
const
activeMode
=
ref
<
ModeOption
>
(
modeOptions
[
0
]);
const
materials
=
[
{
id
:
'm1'
,
name
:
'雅诗兰黛精华空瓶主图主体.png'
,
kind
:
'图片'
,
size
:
'1.2 MB'
,
image
:
productImg
},
{
id
:
'm2'
,
name
:
'智能蓝牙音箱白色高对比展示.jpg'
,
kind
:
'图片'
,
size
:
'980 KB'
,
image
:
detailImg
},
{
id
:
'm3'
,
name
:
'模特红裙走秀素材_原画.mp4'
,
kind
:
'视频'
,
size
:
'18.4 MB'
,
image
:
''
},
{
id
:
'm4'
,
name
:
'七夕美妆礼盒_烫礼场景成片.mp4'
,
kind
:
'视频'
,
size
:
'42.6 MB'
,
image
:
''
},
{
id
:
'm5'
,
name
:
'AI生成 精华液晨间护肤场景.jpg'
,
kind
:
'图片'
,
size
:
'2.3 MB'
,
image
:
peopleImg
},
];
const
mediaTabs
=
[
'全部'
,
'图片'
,
'视频'
];
const
currentScene
=
computed
(()
=>
scenes
.
find
((
item
)
=>
item
.
key
===
props
.
activeScene
));
const
filteredMaterials
=
computed
(()
=>
{
const
keyword
=
mediaKeyword
.
value
.
trim
().
toLowerCase
();
return
materials
.
filter
((
item
)
=>
{
const
matchTab
=
mediaTab
.
value
===
'全部'
||
item
.
kind
===
mediaTab
.
value
;
const
matchKeyword
=
!
keyword
||
item
.
name
.
toLowerCase
().
includes
(
keyword
);
return
matchTab
&&
matchKeyword
;
});
});
const
toggleMaterial
=
(
id
:
string
)
=>
{
if
(
selectedMaterials
.
value
.
includes
(
id
))
{
selectedMaterials
.
value
=
selectedMaterials
.
value
.
filter
((
item
)
=>
item
!==
id
);
return
;
}
if
(
selectedMaterials
.
value
.
length
<
5
)
{
selectedMaterials
.
value
=
[...
selectedMaterials
.
value
,
id
];
}
};
</
script
>
<
style
lang=
"scss"
scoped
>
...
...
@@ -398,28 +292,10 @@ const toggleMaterial = (id: string) => {
box-shadow
:
none
;
}
:deep
(
.media-search
.el-input__wrapper
)
{
padding
:
0
;
box-shadow
:
none
;
background
:
transparent
;
}
:deep
(
.media-search
.el-input__inner
)
{
height
:
30px
;
font-size
:
12px
;
}
:deep
(
.media-card
.el-card__body
)
{
width
:
100%
;
height
:
100%
;
padding
:
0
;
}
.tool-btn
,
.scene-btn
,
.duration-btn
,
.ratio-btn
,
.footer-btn
{
.ratio-btn
{
border
:
1px
solid
#dfe7f2
;
color
:
#183151
;
box-shadow
:
0
2px
6px
rgba
(
21
,
44
,
76
,
.06
);
...
...
@@ -434,14 +310,8 @@ const toggleMaterial = (id: string) => {
background
:
#fbf4ff
;
}
.upload-btn
{
border
:
1px
solid
#e2bcff
;
background
:
#fbebff
;
}
.target-toggle
,
.soft-pill
,
.media-tabs
{
.soft-pill
{
background
:
#f3f7fc
;
}
...
...
@@ -518,69 +388,4 @@ const toggleMaterial = (id: string) => {
right
:
12px
;
background
:
rgba
(
var
(
--
main-color
)
,
1
);
}
.search-mini
{
border
:
1px
solid
#dfe7f2
;
}
.media-tabs
button
.active
{
color
:
#183151
;
background
:
#fff
;
box-shadow
:
0
1px
4px
rgba
(
17
,
36
,
62
,
.08
);
}
.upload-local
,
.footer-btn.confirm
{
color
:
#fff
;
background
:
rgba
(
var
(
--
main-color
)
,
1
);
}
.media-card
{
background
:
#eef3f8
;
}
.media-card.selected
{
box-shadow
:
0
0
0
2px
rgba
(
var
(
--
main-color
)
,
1
);
}
.video-placeholder
{
background
:
linear-gradient
(
180deg
,
#f4f7fb
0%
,
#dce5ee
100%
);
}
.video-tag
{
background
:
rgba
(
34
,
44
,
58
,
.72
);
}
.media-mask
{
background
:
linear-gradient
(
180deg
,
transparent
0%
,
rgba
(
18
,
25
,
38
,
.72
)
35%
,
rgba
(
18
,
25
,
38
,
.9
)
100%
);
}
.media-name
,
.media-size
{
display
:
block
;
}
.media-footer
{
border-top
:
1px
solid
#edf2f7
;
}
.footer-btn
{
border
:
1px
solid
#dfe7f2
;
}
</
style
>
<
style
lang=
"scss"
>
.quick-media-dialog
{
padding
:
0
;
border-radius
:
12px
;
overflow
:
hidden
;
.el-dialog__header
{
display
:
none
;
}
.el-dialog__body
{
padding
:
0
;
}
}
</
style
>
apps/web-vue/src/views/quickCreation/components/DigitalHumanDialog.vue
0 → 100644
View file @
626b653c
<
template
>
<Dialog
v-model=
"dialogVisible"
bg-color=
"#fff"
width=
"1000"
>
<div
class=
"bg-fff hidden round-10"
>
<header
class=
"dialog-header flex-items-center gap-12 px-24 py-18"
>
<span
class=
"header-icon flex-center wh-32 round-6"
>
<el-icon
class=
"fs-20"
>
<User
/>
</el-icon>
</span>
<div>
<div
class=
"flex-items-center gap-8"
>
<h3
class=
"fs-16 lh-22 color-000"
>
AI 智能数字人分身工作台
</h3>
</div>
<p
class=
"fs-12 lh-20 color-888"
>
高保真音容笑貌克隆,无需实景拍摄,分钟级输出口播带货视频
</p>
</div>
</header>
<section
class=
"dialog-body flex gap-24 px-24 py-24"
>
<div
class=
"left-column flex-col gap-24 flex-1"
>
<div>
<div
class=
"lh-22 mb-14"
>
1 · 选择主播模特分身
</div>
<div
class=
"anchor-list flex flex-wrap gap-12 w-full"
>
<div
v-for=
"item in anchors"
:key=
"item.id"
class=
"anchor-option pointer"
:class=
"
{ active: selectedAnchor === item.id }"
@click="selectAnchor(item.id)">
<div
class=
"anchor-card flex-items-center gap-12 p-14 round-12 bg-fff"
>
<span
class=
"avatar flex-center wh-42 round hidden"
:style=
"
{ background: item.avatarBg }">
<img
v-if=
"item.avatar"
:src=
"item.avatar"
:alt=
"item.name"
class=
"w-full h-full cover"
/>
<span
v-else
class=
"color-fff"
>
{{
item
.
name
.
slice
(
0
,
1
)
}}
</span>
</span>
<span
class=
"anchor-copy flex-col text-left"
>
<span
class=
"anchor-name lh-20 color-000"
>
{{
item
.
name
}}
</span>
<span
class=
"anchor-desc fs-12 lh-20 color-888"
>
{{
item
.
desc
}}
</span>
</span>
</div>
</div>
</div>
</div>
<div>
<div
class=
"lh-22 mb-10"
>
2 · 调节声音音色及音速
</div>
<div
class=
"voice-panel p-16 round-14"
>
<div
class=
"fs-12 lh-20 color-888 mb-8"
>
推荐音色标签
</div>
<el-select
v-model=
"voiceStyle"
class=
"w-full"
size=
"large"
>
<el-option
label=
"热情高亢(带货爆款)"
value=
"hot"
/>
<el-option
label=
"温柔亲和(护肤种草)"
value=
"soft"
/>
<el-option
label=
"专业可信(知识讲解)"
value=
"pro"
/>
</el-select>
<div
class=
"flex-between mt-14"
>
<span
class=
"fs-12 lh-20 color-888"
>
口播语速倍率
</span>
<span
class=
"fs-12 lh-20 speed-value"
>
{{
speed
.
toFixed
(
2
)
}}
x
</span>
</div>
<el-slider
v-model=
"speed"
:min=
"0.8"
:max=
"1.3"
:step=
"0.05"
:show-tooltip=
"false"
/>
</div>
</div>
</div>
<div
class=
"right-column flex-col gap-24 flex-1"
>
<div>
<div
class=
"lh-22 mb-10"
>
3 · 设定数字人播报文案
</div>
<el-input
v-model=
"scriptText"
class=
"script-input"
type=
"textarea"
resize=
"none"
:rows=
"7"
maxlength=
"300"
show-word-limit
/>
<div
class=
"flex-between mt-12 fs-12 color-888"
>
<span>
建议文案字数控制在 300 字以内
</span>
<span>
当前:
{{
scriptText
.
length
}}
字
</span>
</div>
</div>
<div
class=
"cost-card flex-col gap-12 p-18 round-14 mt-auto"
>
<div
class=
"flex-between"
>
<div
class=
" lh-22 color-888"
>
所需积分消耗
</div>
<div
class=
" lh-22 cost-value"
>
40 点算力
</div>
</div>
<el-button
type=
"primary"
class=
"submit-btn h-40 round-10 border-none"
>
<el-icon>
<User
/>
</el-icon>
<span>
一键合成数字人推流视频
</span>
</el-button>
</div>
</div>
</section>
</div>
</Dialog>
</
template
>
<
script
setup
lang=
"ts"
>
import
{
computed
,
ref
}
from
'vue'
;
import
{
User
}
from
'@element-plus/icons-vue'
;
import
peopleImg
from
'@/static/image/meinv.png'
;
import
productImg
from
'@/static/image/huaban.png'
;
type
AnchorItem
=
{
id
:
string
;
name
:
string
;
desc
:
string
;
avatar
?:
string
;
avatarBg
:
string
;
};
const
props
=
defineProps
<
{
modelValue
:
boolean
;
}
>
();
const
emit
=
defineEmits
<
{
'update:modelValue'
:
[
value
:
boolean
];
}
>
();
const
dialogVisible
=
computed
({
get
:
()
=>
props
.
modelValue
,
set
:
(
value
)
=>
emit
(
'update:modelValue'
,
value
),
});
const
anchors
:
AnchorItem
[]
=
[
{
id
:
'xiaomei'
,
name
:
'主播小美'
,
desc
:
'甜美知性 · 亲和力极佳'
,
avatar
:
peopleImg
,
avatarBg
:
'#edf3fb'
},
{
id
:
'daliu'
,
name
:
'主播大刘'
,
desc
:
'专业严谨 · 沉稳有说服力'
,
avatarBg
:
'linear-gradient(135deg, #ba7a43, #16324f)'
},
{
id
:
'luoxi'
,
name
:
'主播露西'
,
desc
:
'外籍超模 · 时尚气息浓郁'
,
avatar
:
productImg
,
avatarBg
:
'#dff4fb'
},
{
id
:
'wang'
,
name
:
'主播王总'
,
desc
:
'中年精英 · 行业大咖气质'
,
avatarBg
:
'linear-gradient(135deg, #d9c4ae, #2e3b32)'
},
];
const
selectedAnchor
=
ref
(
'xiaomei'
);
const
voiceStyle
=
ref
(
'hot'
);
const
speed
=
ref
(
1.05
);
const
scriptText
=
ref
(
'哈罗各位宝宝们!今天给大家带来一款超级好用的黑科技香水。轻轻一喷,香味持久一整天,而且前中后调都特别有层次感。现在下单还享受买一送一哦,心动的宝宝赶紧点击下方链接抢购吧!'
);
const
selectAnchor
=
(
id
:
string
)
=>
{
selectedAnchor
.
value
=
id
;
};
</
script
>
<
style
scoped
lang=
"scss"
>
.dialog-header
{
border-bottom
:
1px
solid
#edf2f7
;
}
.header-icon
{
color
:
#f23594
;
background
:
#fff0f7
;
}
.anchor-list
{
align-items
:
stretch
;
}
.anchor-option
{
flex
:
0
0
calc
((
100%
-
12px
)
/
2
);
border
:
1px
solid
#dfe7f2
;
border-radius
:
14px
;
transition
:
border-color
.2s
,
box-shadow
.2s
;
}
.anchor-option.active
{
border-color
:
#f23594
;
box-shadow
:
0
0
0
1px
#f23594
;
}
.anchor-option
:hover
{
border-color
:
#f23594
;
}
.anchor-card
{
min-height
:
66px
;
}
.voice-panel
,
.script-input
:deep
(
.el-textarea__inner
),
.cost-card
{
border
:
1px
solid
#dfe7f2
;
background
:
#fbfdff
;
}
:deep
(
.el-select__wrapper
)
{
border-radius
:
10px
;
box-shadow
:
0
0
0
1px
#dfe7f2
inset
;
background
:
#fff
;
}
:deep
(
.el-slider__bar
),
:deep
(
.el-slider__button
)
{
background
:
#f23594
;
}
:deep
(
.el-slider__button
)
{
border-color
:
#f23594
;
}
.speed-value
,
.cost-value
{
color
:
#f23594
;
}
.script-input
:deep
(
.el-textarea__inner
)
{
min-height
:
174px
!
important
;
padding
:
16px
;
border-radius
:
14px
;
box-shadow
:
none
;
color
:
#183151
;
line-height
:
24px
;
}
.script-input
:deep
(
.el-input__count
)
{
display
:
none
;
}
.submit-btn
{
width
:
100%
;
background
:
#f23594
;
box-shadow
:
0
8px
18px
rgba
(
242
,
53
,
148
,
.24
);
}
.right-column
{
min-width
:
0
;
}
</
style
>
apps/web-vue/src/views/quickCreation/components/InspirationGallery.vue
View file @
626b653c
...
...
@@ -224,7 +224,7 @@ const openPreview = (index: number) => {
}
.gallery-card
{
flex
:
0
0
calc
((
100%
-
60px
)
/
4
);
flex
:
0
0
calc
((
100%
-
80px
)
/
5
);
background
:
#fff
;
transition
:
border-color
.24s
,
box-shadow
.24s
,
transform
.24s
;
}
...
...
@@ -294,7 +294,7 @@ h3 {
@media
(
max-width
:
1280px
)
{
.gallery-card
{
flex-basis
:
calc
((
100%
-
40px
)
/
3
);
flex-basis
:
calc
((
100%
-
60px
)
/
4
);
}
}
...
...
apps/web-vue/src/views/quickCreation/components/MediaMaterialDialog.vue
0 → 100644
View file @
626b653c
<
template
>
<el-dialog
v-model=
"dialogVisible"
width=
"672"
align-center
append-to-body
class=
"quick-media-dialog"
>
<div
class=
"media-dialog hidden round-12"
>
<div
class=
"flex-between px-18 py-16"
>
<div
class=
"flex-items-center gap-8"
>
<el-icon
class=
"color-main fs-18"
>
<Picture
/>
</el-icon>
<h3
class=
"fs-16 lh-24 color-000"
>
选择参考素材
</h3>
</div>
<el-button
class=
"close-btn wh-28 p-0 border-none bg-none color-888 pointer"
circle
@
click=
"dialogVisible = false"
>
<el-icon>
<Close
/>
</el-icon>
</el-button>
</div>
<div
class=
"flex-between px-14 pb-14"
>
<div
class=
"media-tabs flex-items-center h-32 p-4 round-4"
>
<el-button
v-for=
"item in mediaTabs"
:key=
"item"
class=
"h-26 px-16 round-4 border-none bg-none"
:class=
"
{ active: mediaTab === item }" text @click="mediaTab = item">
{{
item
}}
</el-button>
</div>
<div
class=
"flex-items-center gap-8"
>
<div
class=
"search-mini flex-items-center h-32 w-194 px-10 round-4 bg-fff"
>
<el-icon
class=
"color-888"
>
<Search
/>
</el-icon>
<el-input
v-model=
"mediaKeyword"
class=
"media-search flex-1 minw-0 ml-8"
placeholder=
"搜索资产..."
/>
</div>
<el-button
class=
"upload-local flex-center gap-6 h-32 px-14"
>
<el-icon>
<Upload
/>
</el-icon>
<span>
本地上传
</span>
</el-button>
</div>
</div>
<el-radio-group
v-model=
"selectedId"
class=
"material-list flex flex-wrap gap-12 px-14 pt-14 minh-300"
>
<el-radio
v-for=
"item in filteredMaterials"
:key=
"item.id"
class=
"media-card relative round-8 hidden border-none pointer text-left h-150"
:class=
"
{ selected: selectedId === item.id }" :value="item.id">
<img
v-if=
"item.image"
:src=
"item.image"
:alt=
"item.name"
class=
"w-full h-full cover"
/>
<span
v-else
class=
"video-placeholder flex-center w-full h-full color-888"
>
VIDEO
</span>
<span
v-if=
"item.kind === '视频'"
class=
"video-tag absolute top-8 right-8 h-18 lh-18 px-6 round-4 color-fff fs-10"
>
VIDEO
</span>
<span
class=
"media-mask absolute bottom-0 left-0 w-full px-8 py-8 color-fff"
>
<div
class=
"lh-18 ellipsis fs-12 fw-400"
>
{{
item
.
name
}}
</div>
<div
class=
"fs-10 fw-400"
>
{{
item
.
size
}}
</div>
</span>
</el-radio>
</el-radio-group>
<div
class=
"media-footer flex justify-end px-18 py-16 mt-18"
>
<div
class=
"flex-items-center"
>
<el-button
class=
"h-32 px-16 fs-12"
@
click=
"dialogVisible = false"
>
取消
</el-button>
<el-button
class=
"h-32 px-16 fs-12"
type=
"primary"
@
click=
"handleConfirm"
>
确认选择
</el-button>
</div>
</div>
</div>
</el-dialog>
</
template
>
<
script
setup
lang=
"ts"
>
import
{
computed
,
ref
,
watch
}
from
'vue'
;
import
{
Close
,
Picture
,
Search
,
Upload
}
from
'@element-plus/icons-vue'
;
import
productImg
from
'@/static/image/huaban.png'
;
import
peopleImg
from
'@/static/image/meinv.png'
;
import
detailImg
from
'@/static/image/long.png'
;
import
type
{
MaterialItem
}
from
'../types'
;
const
props
=
defineProps
<
{
modelValue
:
boolean
;
selected
:
MaterialItem
[];
}
>
();
const
emit
=
defineEmits
<
{
'update:modelValue'
:
[
value
:
boolean
];
'update:selected'
:
[
value
:
MaterialItem
[]];
confirm
:
[
value
:
MaterialItem
[]];
}
>
();
const
mediaTabs
=
[
'全部'
,
'图片'
,
'视频'
];
const
mediaTab
=
ref
(
'全部'
);
const
mediaKeyword
=
ref
(
''
);
const
selectedId
=
ref
(
props
.
selected
[
0
]?.
id
??
''
);
const
materials
:
MaterialItem
[]
=
[
{
id
:
'm1'
,
name
:
'雅诗兰黛精华空瓶主图主体.png'
,
kind
:
'图片'
,
size
:
'1.2 MB'
,
image
:
productImg
},
{
id
:
'm2'
,
name
:
'智能蓝牙音箱白色高对比展示.jpg'
,
kind
:
'图片'
,
size
:
'980 KB'
,
image
:
detailImg
},
{
id
:
'm3'
,
name
:
'模特红裙走秀素材_原画.mp4'
,
kind
:
'视频'
,
size
:
'18.4 MB'
,
image
:
''
},
{
id
:
'm4'
,
name
:
'七夕美妆礼盒_烫礼场景成片.mp4'
,
kind
:
'视频'
,
size
:
'42.6 MB'
,
image
:
''
},
{
id
:
'm5'
,
name
:
'AI生成 精华液晨间护肤场景.jpg'
,
kind
:
'图片'
,
size
:
'2.3 MB'
,
image
:
peopleImg
},
];
const
dialogVisible
=
computed
({
get
:
()
=>
props
.
modelValue
,
set
:
(
value
)
=>
emit
(
'update:modelValue'
,
value
),
});
const
selectedMaterials
=
computed
(()
=>
{
const
current
=
materials
.
find
((
item
)
=>
item
.
id
===
selectedId
.
value
);
return
current
?
[
current
]
:
[];
});
const
filteredMaterials
=
computed
(()
=>
{
const
keyword
=
mediaKeyword
.
value
.
trim
().
toLowerCase
();
return
materials
.
filter
((
item
)
=>
{
const
matchTab
=
mediaTab
.
value
===
'全部'
||
item
.
kind
===
mediaTab
.
value
;
const
matchKeyword
=
!
keyword
||
item
.
name
.
toLowerCase
().
includes
(
keyword
);
return
matchTab
&&
matchKeyword
;
});
});
const
handleConfirm
=
()
=>
{
emit
(
'confirm'
,
selectedMaterials
.
value
);
dialogVisible
.
value
=
false
;
};
watch
(
()
=>
props
.
selected
,
(
value
)
=>
{
const
id
=
value
[
0
]?.
id
??
''
;
if
(
id
!==
selectedId
.
value
)
{
selectedId
.
value
=
id
;
}
},
{
deep
:
true
}
);
watch
(
selectedId
,
()
=>
{
const
propId
=
props
.
selected
[
0
]?.
id
??
''
;
if
(
selectedId
.
value
!==
propId
)
{
emit
(
'update:selected'
,
selectedMaterials
.
value
);
}
});
</
script
>
<
style
lang=
"scss"
scoped
>
:deep
(
.media-search
.el-input__wrapper
)
{
padding
:
0
;
box-shadow
:
none
;
background
:
transparent
;
}
:deep
(
.media-search
.el-input__inner
)
{
height
:
30px
;
font-size
:
12px
;
}
:deep
(
.material-list
.el-radio__input
)
{
position
:
absolute
;
top
:
8px
;
left
:
8px
;
z-index
:
1
;
}
:deep
(
.material-list
.el-radio__label
)
{
display
:
block
;
width
:
100%
;
height
:
100%
;
padding-left
:
0
;
}
.soft-pill
,
.media-tabs
{
background
:
#f3f7fc
;
}
.search-mini
{
border
:
1px
solid
#dfe7f2
;
}
.media-tabs
button
.active
{
color
:
#183151
;
background
:
#fff
;
box-shadow
:
0
1px
4px
rgba
(
17
,
36
,
62
,
.08
);
}
.upload-local
{
color
:
#fff
;
background
:
rgba
(
var
(
--
main-color
)
,
1
);
}
.material-list
{
align-items
:
stretch
;
}
.media-card
{
flex
:
0
0
calc
((
100%
-
36px
)
/
4
);
aspect-ratio
:
1
/
1
;
margin
:
0
;
background
:
#eef3f8
;
}
.media-card.selected
{
box-shadow
:
0
0
0
2px
rgba
(
var
(
--
main-color
)
,
1
);
}
.video-placeholder
{
background
:
linear-gradient
(
180deg
,
#f4f7fb
0%
,
#dce5ee
100%
);
}
.video-tag
{
background
:
rgba
(
34
,
44
,
58
,
.72
);
}
.media-mask
{
background
:
linear-gradient
(
180deg
,
transparent
0%
,
rgba
(
18
,
25
,
38
,
.72
)
35%
,
rgba
(
18
,
25
,
38
,
.9
)
100%
);
}
.media-footer
{
border-top
:
1px
solid
#edf2f7
;
}
</
style
>
<
style
lang=
"scss"
>
.quick-media-dialog
{
padding
:
0
;
border-radius
:
12px
;
overflow
:
hidden
;
.el-dialog__header
{
display
:
none
;
}
.el-dialog__body
{
padding
:
0
;
}
}
</
style
>
apps/web-vue/src/views/quickCreation/components/ModelTryOnDialog.vue
0 → 100644
View file @
626b653c
<
template
>
<Dialog
v-model=
"dialogVisible"
bg-color=
"#fff"
width=
"1000"
>
<div
class=
"tryon-dialog bg-fff hidden round-10"
>
<header
class=
"dialog-header flex-items-center gap-12 px-24 py-18"
>
<span
class=
"header-icon flex-center wh-32 round-6"
>
<el-icon
class=
"fs-20"
>
<MagicStick
/>
</el-icon>
</span>
<div>
<div
class=
"flex-items-center gap-8"
>
<h3
class=
"fs-16 lh-22 color-000"
>
模特换衣
</h3>
<span
class=
"batch-tag h-22 lh-22 px-10 round-6 fs-10 fw-700"
>
PRO BATCH
</span>
</div>
<p
class=
"fs-12 lh-20 color-888"
>
将您的服装智能融合到数字模特与写实商用场景中,极速批量生成商用主图
</p>
</div>
</header>
<section
class=
"dialog-body px-24 py-24 over-y-auto"
>
<div
class=
"step-row flex-between mb-14"
>
<div
class=
"step-title flex-items-center gap-8"
>
<span
class=
"step-index flex-center w-20 h-20 round-6 fs-12"
>
1
</span>
<span
class=
"lh-22 fw-700 color-000"
>
选择或上传模特原型(MODEL PROTOTYPE)
</span>
</div>
<div
class=
"switch-tabs flex-items-center h-34 p-4 round-8"
>
<el-button
v-for=
"item in modelSources"
:key=
"item.value"
class=
"h-26 px-12 round-6 border-none fs-12"
:class=
"
{ active: modelSource === item.value }"
text @click="modelSource = item.value">
{{
item
.
label
}}
</el-button>
</div>
</div>
<div
v-if=
"modelSource === 'library'"
class=
"model-list flex gap-16"
>
<div
v-for=
"item in models"
:key=
"item.id"
class=
"model-card flex-1 p-6 round-14 pointer"
:class=
"
{ active: selectedModel === item.id }" @click="selectedModel = item.id">
<div
class=
"model-cover relative h-202 hidden round-10"
>
<img
v-if=
"item.image"
:src=
"item.image"
:alt=
"item.name"
class=
"w-full h-full cover"
/>
<div
v-else
class=
"fake-portrait w-full h-full"
:style=
"
{ background: item.bg }">
</div>
<span
class=
"gender-tag absolute right-8 bottom-8 h-20 lh-20 px-8 round-6 fs-10 color-fff"
>
{{
item
.
gender
}}
</span>
</div>
<div
class=
"text-center mt-8"
>
<div
class=
"fs-12 lh-20 fw-700 color-000"
>
{{
item
.
name
}}
</div>
<div
class=
"fs-12 lh-18 color-888"
>
{{
item
.
desc
}}
</div>
</div>
</div>
</div>
<UploadFile
v-else
listType=
"drap"
/>
<div
class=
"section-line my-24"
></div>
<div
class=
"step-row flex-between mb-12"
>
<div
class=
"step-title flex-items-center gap-8"
>
<span
class=
"step-index flex-center w-20 h-20 round-6 fs-12"
>
2
</span>
<span
class=
"lh-22 fw-700 color-000"
>
调整模特身材类型(MODEL FIGURE TYPE)
</span>
</div>
<div
class=
"switch-tabs flex-items-center h-34 p-4 round-8"
>
<el-button
v-for=
"item in bodyModes"
:key=
"item.value"
class=
"h-26 px-12 round-6 border-none fs-12"
:class=
"
{ active: bodyMode === item.value }"
text @click="bodyMode = item.value">
{{
item
.
label
}}
</el-button>
</div>
</div>
<div
v-if=
"bodyMode === 'origin'"
class=
"tip-box flex-items-center gap-8 h-44 px-16 round-10 fs-12 color-888"
>
<el-icon
class=
"tip-icon"
>
<Opportunity
/>
</el-icon>
<span>
智能引擎将保持上传模特照片中的原本身材形态、比例与线条,仅完成衣物及高级换脸的智能融合。
</span>
</div>
<div
v-else
class=
"figure-list flex flex-wrap gap-12"
>
<div
v-for=
"item in figureTemplates"
:key=
"item.id"
class=
"figure-card flex-col justify-center h-48 px-14 round-10 pointer"
:class=
"
{ active: selectedFigure === item.id }" @click="selectedFigure = item.id">
<div
class=
"fs-12 lh-20 fw-700 color-000"
>
{{
item
.
name
}}
</div>
<div
class=
"fs-10 lh-18 color-888"
>
{{
item
.
desc
}}
</div>
</div>
</div>
<div
class=
"section-line my-24"
></div>
<div
class=
"step-row flex-between mb-12"
>
<div
class=
"step-title flex-items-center gap-8"
>
<span
class=
"step-index flex-center w-20 h-20 round-6 fs-12"
>
3
</span>
<span
class=
"lh-22 fw-700 color-000"
>
高级智能换脸 / 面孔重塑
</span>
</div>
<div
class=
"flex-items-center gap-8"
>
<el-switch
v-model=
"faceEnabled"
size=
"small"
/>
<span
class=
"fs-12 fw-700 color-000"
>
{{
faceEnabled
?
'换脸已开启'
:
'换脸未开启'
}}
</span>
</div>
</div>
<div
v-if=
"!faceEnabled"
class=
"tip-box flex-center h-38 px-16 round-20 fs-12 color-888"
>
<el-icon
class=
"tip-icon mr-6"
>
<Opportunity
/>
</el-icon>
<span>
关闭换脸重塑后,模特将自动保持原型人面孔,或应用内置数字模特默认五官。
</span>
</div>
<div
v-else
class=
"face-panel p-18 round-16"
>
<div
class=
"face-tabs flex-items-center gap-16 mb-14"
>
<el-button
v-for=
"item in faceSources"
:key=
"item.value"
class=
"h-32 px-16 round-8 border-none fs-12"
:class=
"
{ active: faceSource === item.value }" text @click="faceSource = item.value">
{{
item
.
label
}}
</el-button>
</div>
<div
class=
"face-list flex gap-12"
>
<div
v-for=
"item in faceModels"
:key=
"item.id"
class=
"face-card flex-1 flex-col flex-center h-92 round-10 pointer"
:class=
"
{ active: selectedFace === item.id }" @click="selectedFace = item.id">
<span
class=
"face-avatar flex-center wh-40 round hidden mb-6"
:style=
"
{ background: item.avatarBg }">
<img
v-if=
"item.avatar"
:src=
"item.avatar"
:alt=
"item.name"
class=
"w-full h-full cover"
/>
<span
v-else
class=
"color-fff fs-12"
>
{{
item
.
name
.
slice
(
0
,
1
)
}}
</span>
</span>
<div
class=
"fs-12 lh-20 fw-700 color-000"
>
{{
item
.
name
}}
</div>
<div
class=
"fs-10 lh-18 color-888"
>
{{
item
.
desc
}}
</div>
</div>
</div>
</div>
<div
class=
"section-line my-24"
></div>
<div
class=
"step-row flex-between mb-14"
>
<div
class=
"step-title flex-items-center gap-8"
>
<span
class=
"step-index flex-center w-20 h-20 round-6 fs-12"
>
4
</span>
<span
class=
"lh-22 fw-700 color-000"
>
选择服装(一次支持上传 / 添加多张批量生成)
</span>
</div>
<div
class=
"switch-tabs flex-items-center h-34 p-4 round-8"
>
<el-button
v-for=
"item in clothSources"
:key=
"item.value"
class=
"h-26 px-12 round-6 border-none fs-12"
:class=
"
{ active: clothSource === item.value }" text @click="clothSource = item.value">
{{
item
.
label
}}
</el-button>
</div>
</div>
<div
class=
"cloth-list flex gap-14"
>
<div
v-for=
"item in clothes"
:key=
"item.id"
class=
"cloth-card relative h-182 round-10 hidden"
>
<img
:src=
"item.image"
:alt=
"item.name"
class=
"w-full h-full cover"
/>
<span
class=
"cloth-tag absolute left-8 bottom-8 h-22 lh-22 px-8 round-6 fs-10 color-fff"
>
{{
item
.
name
}}
</span>
</div>
<div
class=
"cloth-card add-cloth flex-col flex-center h-182 round-10 pointer"
>
<el-icon
class=
"fs-24 mb-8"
>
<Plus
/>
</el-icon>
<span
class=
"fs-12 color-888"
>
添加上传衣服
</span>
</div>
</div>
<p
class=
"flex-items-center mt-12 fs-12 lh-20 color-888"
>
<el-icon
class=
"tip-icon mr-6"
>
<Opportunity
/>
</el-icon>
一次决定多张衣服照片进行上传,系统将自动对每件衣服与模特、场景进行真实穿戴试衣 Lookbook 生成。
</p>
<div
class=
"section-line my-24"
></div>
<div
class=
"step-row flex-between mb-14"
>
<div
class=
"step-title flex-items-center gap-8"
>
<span
class=
"step-index flex-center w-20 h-20 round-6 fs-12"
>
5
</span>
<span
class=
"lh-22 fw-700 color-000"
>
选择逼真商用融合场景(REQUIRED SCENE)
</span>
</div>
<span
class=
"fs-12 fw-700 required-text"
>
* 必须选择场景
</span>
</div>
<div
class=
"scene-list flex gap-16"
>
<div
v-for=
"item in scenes"
:key=
"item.id"
class=
"scene-card flex-1 p-4 round-12 pointer"
:class=
"
{ active: selectedScene === item.id }" @click="selectedScene = item.id">
<div
class=
"scene-cover relative h-112 hidden round-8"
>
<img
:src=
"item.image"
:alt=
"item.name"
class=
"w-full h-full cover"
/>
<span
v-if=
"selectedScene === item.id"
class=
"check-dot absolute flex-center wh-22 round color-fff"
>
<el-icon>
<Check
/>
</el-icon>
</span>
</div>
<div
class=
"px-8 py-8"
>
<div
class=
"fs-12 lh-20 fw-700 color-000"
>
{{
item
.
name
}}
</div>
<div
class=
"fs-10 lh-18 color-888"
>
{{
item
.
desc
}}
</div>
</div>
</div>
</div>
</section>
<footer
class=
"dialog-footer flex-between px-24 py-14"
>
<div
class=
"fs-14 color-888"
>
算力点数扣除
<strong
class=
"color-main fs-16 mx-4"
>
50
</strong>
点数
<span
class=
"fs-12 ml-6"
>
(每件生成消耗 25 点)
</span>
</div>
<div
class=
"flex-items-center"
>
<el-button
class=
"h-32 px-22"
@
click=
"dialogVisible = false"
>
取消
</el-button>
<el-button
type=
"primary"
class=
"submit-btn h-32 px-28 border-none"
>
<el-icon>
<MagicStick
/>
</el-icon>
<span>
一键启动 AI 模特换衣(2件批量生成)
</span>
</el-button>
</div>
</footer>
</div>
</Dialog>
</
template
>
<
script
setup
lang=
"ts"
>
import
{
computed
,
ref
}
from
'vue'
;
import
{
Check
,
MagicStick
,
Opportunity
,
Plus
}
from
'@element-plus/icons-vue'
;
import
peopleImg
from
'@/static/image/meinv.png'
;
import
productImg
from
'@/static/image/huaban.png'
;
import
UploadFile
from
'@/components/Upload/index.vue'
type
OptionItem
=
{
label
:
string
;
value
:
string
;
};
type
ModelItem
=
{
id
:
string
;
name
:
string
;
desc
:
string
;
gender
:
string
;
image
?:
string
;
bg
?:
string
;
};
type
ImageCardItem
=
{
id
:
string
;
name
:
string
;
desc
?:
string
;
image
:
string
;
};
type
FigureTemplate
=
{
id
:
string
;
name
:
string
;
desc
:
string
;
};
type
FaceItem
=
{
id
:
string
;
name
:
string
;
desc
:
string
;
avatar
?:
string
;
avatarBg
:
string
;
};
const
props
=
defineProps
<
{
modelValue
:
boolean
;
}
>
();
const
emit
=
defineEmits
<
{
'update:modelValue'
:
[
value
:
boolean
];
}
>
();
const
dialogVisible
=
computed
({
get
:
()
=>
props
.
modelValue
,
set
:
(
value
)
=>
emit
(
'update:modelValue'
,
value
),
});
const
modelSources
:
OptionItem
[]
=
[
{
label
:
'数字人库'
,
value
:
'library'
},
{
label
:
'本地自主上传'
,
value
:
'local'
},
];
const
bodyModes
:
OptionItem
[]
=
[
{
label
:
'默认图片身材'
,
value
:
'origin'
},
{
label
:
'指定身材模板'
,
value
:
'template'
},
];
const
faceSources
:
OptionItem
[]
=
[
{
label
:
'选择数字人预设面容'
,
value
:
'library'
},
{
label
:
'用户本地上传人脸'
,
value
:
'local'
},
];
const
clothSources
:
OptionItem
[]
=
[
{
label
:
'多图本地上传'
,
value
:
'multi'
},
{
label
:
'商品链接/ID抓取'
,
value
:
'link'
},
];
const
models
:
ModelItem
[]
=
[
{
id
:
'shiyu'
,
name
:
'诗雨'
,
desc
:
'古典东方优雅'
,
gender
:
'女性'
,
image
:
peopleImg
},
{
id
:
'rose'
,
name
:
'Rose'
,
desc
:
'欧美金发气场'
,
gender
:
'女性'
,
bg
:
'linear-gradient(135deg, #d7b29c, #1d1f28)'
},
{
id
:
'jiale'
,
name
:
'凯特'
,
desc
:
'都市白领知性'
,
gender
:
'女性'
,
image
:
productImg
},
{
id
:
'tuozai'
,
name
:
'拓哉'
,
desc
:
'日韩轻熟形象'
,
gender
:
'男性'
,
bg
:
'linear-gradient(135deg, #101014, #5a4236)'
},
{
id
:
'david'
,
name
:
'David'
,
desc
:
'欧美时尚头'
,
gender
:
'男性'
,
bg
:
'linear-gradient(135deg, #c4d2df, #34445c)'
},
];
const
clothes
:
ImageCardItem
[]
=
[
{
id
:
'cloth1'
,
name
:
'衣服 #1'
,
image
:
peopleImg
},
{
id
:
'cloth2'
,
name
:
'衣服 #2'
,
image
:
productImg
},
];
const
figureTemplates
:
FigureTemplate
[]
=
[
{
id
:
'slim'
,
name
:
'欧美背部超模'
,
desc
:
'高挑比例/肩背挺拔'
},
{
id
:
'sweet'
,
name
:
'日系甜美少女'
,
desc
:
'娇小匀称/元气少女'
},
{
id
:
'athletic'
,
name
:
'国潮复古型男'
,
desc
:
'肩宽腰窄/硬朗有型'
},
{
id
:
'urban'
,
name
:
'资深都市丽人'
,
desc
:
'优雅腰线/黄金比例'
},
{
id
:
'sport'
,
name
:
'阳光运动型男'
,
desc
:
'阳刚轮廓/腿侧线条'
},
{
id
:
'curve'
,
name
:
'微胖丰满身材'
,
desc
:
'丰满肉感/曲线柔和'
},
];
const
faceModels
:
FaceItem
[]
=
[
{
id
:
'face-xiaomei'
,
name
:
'时尚小美'
,
desc
:
'甜美东方'
,
avatar
:
peopleImg
,
avatarBg
:
'#edf3fb'
},
{
id
:
'face-jia'
,
name
:
'千练佳丽'
,
desc
:
'知性丽人'
,
avatar
:
productImg
,
avatarBg
:
'#fff0f7'
},
{
id
:
'face-shuai'
,
name
:
'阳光小帅'
,
desc
:
'活力潮男'
,
avatarBg
:
'linear-gradient(135deg, #262b36, #b47c5b)'
},
{
id
:
'face-han'
,
name
:
'韩系欧巴'
,
desc
:
'极简潮男'
,
avatarBg
:
'linear-gradient(135deg, #a9bfdb, #273449)'
},
];
const
scenes
:
ImageCardItem
[]
=
[
{
id
:
'studio'
,
name
:
'高端室内摄影棚'
,
desc
:
'影棚/静谧高光'
,
image
:
peopleImg
},
{
id
:
'street'
,
name
:
'巴黎春季时尚街头'
,
desc
:
'户外/自然漫射'
,
image
:
productImg
},
{
id
:
'villa'
,
name
:
'奢华居家阳光露台'
,
desc
:
'居家/落日氛围'
,
image
:
peopleImg
},
{
id
:
'beach'
,
name
:
'夏日热带落日海滩'
,
desc
:
'海滩/海风阳光'
,
image
:
productImg
},
];
const
modelSource
=
ref
(
'library'
);
const
bodyMode
=
ref
(
'origin'
);
const
clothSource
=
ref
(
'multi'
);
const
faceEnabled
=
ref
(
false
);
const
selectedModel
=
ref
(
'shiyu'
);
const
selectedScene
=
ref
(
'studio'
);
const
selectedFigure
=
ref
(
'slim'
);
const
faceSource
=
ref
(
'library'
);
const
selectedFace
=
ref
(
'face-xiaomei'
);
</
script
>
<
style
scoped
lang=
"scss"
>
.tryon-dialog
{
max-height
:
92vh
;
}
.dialog-header
,
.dialog-footer
{
border-bottom
:
1px
solid
#edf2f7
;
}
.dialog-footer
{
border-top
:
1px
solid
#edf2f7
;
border-bottom
:
0
;
}
.dialog-body
{
max-height
:
calc
(
92vh
-
148px
);
}
.header-icon
{
color
:
#1b6cff
;
background
:
#edf5ff
;
}
.batch-tag
,
.step-index
{
color
:
#1b6cff
;
background
:
#edf5ff
;
}
.switch-tabs
{
background
:
#f3f7fc
;
border
:
1px
solid
#dfe7f2
;
}
.switch-tabs
button
.active
{
color
:
#1b6cff
;
background
:
#fff
;
box-shadow
:
0
1px
4px
rgba
(
17
,
36
,
62
,
.1
);
}
.model-card
,
.scene-card
,
.figure-card
,
.face-card
{
border
:
1px
solid
#dfe7f2
;
background
:
#fff
;
transition
:
border-color
.2s
,
box-shadow
.2s
;
}
.model-card.active
,
.scene-card.active
,
.figure-card.active
,
.face-card.active
{
border-color
:
#1b6cff
;
box-shadow
:
0
0
0
1px
#1b6cff
;
}
.model-card
:hover
,
.scene-card
:hover
,
.figure-card
:hover
,
.face-card
:hover
{
border-color
:
#1b6cff
;
}
.model-cover
{
background
:
#eef3f8
;
}
.fake-portrait
{
position
:
relative
;
}
.
fake-portrait
:
:
after
{
position
:
absolute
;
inset
:
34px
28px
0
;
border-radius
:
80px
80px
16px
16px
;
background
:
rgba
(
255
,
255
,
255
,
.28
);
content
:
''
;
}
.gender-tag
,
.cloth-tag
{
background
:
rgba
(
9
,
16
,
28
,
.78
);
}
.section-line
{
height
:
1px
;
background
:
#edf2f7
;
}
.tip-box
,
.add-cloth
,
.face-panel
{
border
:
1px
solid
#dfe7f2
;
background
:
#fbfdff
;
}
.figure-card
{
flex
:
0
0
calc
((
100%
-
24px
)
/
3
);
}
.face-tabs
.el-button.active
{
color
:
#1b6cff
;
background
:
#fff
;
box-shadow
:
0
1px
4px
rgba
(
17
,
36
,
62
,
.1
);
}
.face-card
{
background
:
#fff
;
}
.tip-icon
{
color
:
#ffbd2f
;
}
.cloth-card
{
flex
:
0
0
182px
;
border
:
1px
dashed
#d9e4f2
;
background
:
#fbfdff
;
}
.add-cloth
{
color
:
#8aa0bf
;
}
.required-text
{
color
:
#ff2d55
;
}
.check-dot
{
top
:
50%
;
left
:
50%
;
background
:
#1b6cff
;
transform
:
translate
(
-50%
,
-50%
);
}
.submit-btn
{
background
:
#1f67ff
;
box-shadow
:
0
8px
18px
rgba
(
31
,
103
,
255
,
.24
);
}
</
style
>
apps/web-vue/src/views/quickCreation/components/Remove-video.vue
0 → 100644
View file @
626b653c
<
template
>
<div
class=
"flex items-start gap-24"
>
<div
class=
"left-panel flex-col gap-20"
>
<el-card
class=
"panel-card round-10"
shadow=
"never"
>
<template
#
header
>
<div
class=
" lh-22 fw-700 color-000"
>
选择待处理视频
</div>
</
template
>
<div
class=
"upload-area w-full pointer"
@
click=
"mediaVisible = true"
>
<div
class=
"flex-col flex-center h-150"
>
<span
class=
"upload-icon flex-center wh-40 round mb-10"
>
<el-icon
class=
"fs-22"
>
<UploadFilled
/>
</el-icon>
</span>
<div
class=
"lh-22 fs-12"
>
选择待擦除水印视频
</div>
<p
class=
"fs-12 color-999"
>
智能分析格式,支持最高4K源文件输入
</p>
</div>
</div>
</el-card>
<el-card
class=
"panel-card round-10"
shadow=
"never"
>
<
template
#
header
>
<div
class=
"flex-between"
>
<div
class=
" lh-22 fw-700 color-000"
>
标定水印区域 (1)
</div>
<el-button
class=
"h-28 px-8 fs-12 color-main"
text
>
<el-icon>
<Plus
/>
</el-icon>
<span>
添加选区
</span>
</el-button>
</div>
</
template
>
<div
class=
"flex-items-center flex-wrap gap-10"
>
<el-tag
class=
"watermark-tag h-30 lh-30 px-12 round-6 color-main"
closable
effect=
"plain"
>
水印区 1:78,4 / 96,14
</el-tag>
</div>
</el-card>
<el-card
class=
"guide-card round-10 pt-16"
shadow=
"never"
>
<div
class=
"flex-items-center gap-6 mb-10"
>
<el-icon
class=
"color-main fs-16"
>
<QuestionFilled
/>
</el-icon>
<span
class=
" lh-22 fw-700 color-333"
>
智能擦除操作指引
</span>
</div>
<div
class=
"fs-12 lh-26 color-888"
>
<p>
1. 上传视频素材,并在右侧视频静止帧中直接拖动/画框。
</p>
<p>
2. 如果水印在视频的不同位置,可多次点击「添加选区」进行联合擦除。
</p>
<p>
3. 点击执行,系统会提取水印周围相邻多帧的纹理流,智能重构无瑕背景。
</p>
</div>
</el-card>
<el-button
type=
"primary"
class=
"run-btn h-40 round-6 border-none"
>
立即执行智能重绘擦除 (预计 2.50)
</el-button>
</div>
<el-card
class=
"preview-card flex-1 round-10"
shadow=
"never"
>
<
template
#
header
>
<div
class=
"flex-between"
>
<div
class=
" lh-22 fw-700 color-000"
>
画面预览与划定框
</div>
<span
class=
"fs-12 color-999"
>
模拟视频帧排布
</span>
</div>
</
template
>
<div
class=
"preview-frame relative hidden round-12"
>
<img
:src=
"previewImage"
alt=
"待处理视频预览"
class=
"w-full h-full cover"
/>
<div
class=
"preview-mask absolute top-0 left-0 w-full h-full"
></div>
<div
class=
"selection-box absolute top-16 right-26 w-108 h-34"
>
<el-tag
class=
"selection-label m-6 h-22 lh-22 px-8 round-6 color-fff fs-12 border-none"
>
选区
1
</el-tag>
<el-button
class=
"selection-close absolute top-20 right-6 wh-14 p-0 border-none color-fff"
type=
"danger"
circle
>
<el-icon
class=
"fs-10"
>
<Close
/>
</el-icon>
</el-button>
</div>
<div
class=
"empty-state absolute flex-col flex-center text-center"
>
<span
class=
"video-icon flex-center wh-42 round mb-10"
>
<el-icon
class=
"fs-22"
>
<VideoCamera
/>
</el-icon>
</span>
<div
class=
"lh-24"
>
尚未加载工作视频
</div>
<p
class=
"fs-12 lh-20 color-999"
>
请在左侧点击上传视频,以便框选水印或字幕边界
</p>
</div>
</div>
<div
class=
"status-bar flex-between h-38 px-12 mt-16 round-6 fs-12 color-999"
>
<span>
状态:GPU像素缓存区待命
</span>
<span>
分辨率:1920 × 1080px
</span>
</div>
</el-card>
<MediaMaterialDialog
v-model=
"mediaVisible"
v-model:selected=
"selectedMaterials"
/>
</div>
</template>
<
script
setup
lang=
"ts"
>
import
{
computed
,
ref
}
from
'vue'
;
import
{
Close
,
Plus
,
QuestionFilled
,
UploadFilled
,
VideoCamera
}
from
'@element-plus/icons-vue'
;
import
previewImage
from
'@/static/image/meinv.png'
;
import
MediaMaterialDialog
from
'./MediaMaterialDialog.vue'
;
import
type
{
MaterialItem
,
SceneKey
}
from
'../types'
;
const
mediaVisible
=
ref
(
false
);
const
selectedMaterials
=
ref
<
MaterialItem
[]
>
([]);
</
script
>
<
style
scoped
lang=
"scss"
>
.left-panel
{
flex
:
0
0
40%
;
}
.panel-card
,
.preview-card
,
.guide-card
{
border
:
1px
solid
#dfe7f2
;
}
:deep
(
.el-card__header
)
{
padding
:
16px
16px
10px
;
border-bottom
:
0
;
}
:deep
(
.el-card__body
)
{
padding
:
0
20px
20px
;
}
.upload-area
{
width
:
100%
;
border
:
1px
dashed
#ddd
;
border-radius
:
10px
;
}
:deep
(
.upload-area
.el-upload-dragger
)
{
padding
:
0
;
border-color
:
#d9e4f2
;
border-style
:
dashed
;
background
:
#fbfdff
;
}
.upload-icon
,
.video-icon
{
color
:
#708aaf
;
background
:
#edf3fb
;
}
.watermark-tag
{
border-color
:
rgba
(
var
(
--
main-color
)
,
.1
);
background
:
rgba
(
var
(
--
main-color
)
,
.06
);
}
.guide-card
{
background
:
#f8fbff
;
}
.run-btn
{
background
:
linear-gradient
(
90deg
,
#9d19ff
0%
,
#f23594
100%
);
}
.preview-frame
{
height
:
348px
;
background
:
#eef3f8
;
}
.preview-mask
{
background
:
rgba
(
248
,
250
,
252
,
.68
);
backdrop-filter
:
blur
(
1px
);
}
.selection-box
{
border
:
2px
dashed
#c77dff
;
background
:
rgba
(
199
,
125
,
255
,
.08
);
}
.selection-label
{
background
:
#b45cff
;
}
.selection-close
{
background
:
#ff6b8b
;
}
.empty-state
{
top
:
50%
;
left
:
50%
;
transform
:
translate
(
-50%
,
-50%
);
}
.video-icon
{
color
:
rgba
(
var
(
--
main-color
)
,
1
);
background
:
#fff
;
}
.status-bar
{
border
:
1px
solid
#dfe7f2
;
background
:
#f8fbff
;
}
</
style
>
\ No newline at end of file
apps/web-vue/src/views/quickCreation/components/ToolGroup.vue
View file @
626b653c
...
...
@@ -2,16 +2,16 @@
<section>
<div
class=
"fs-18 lh-28 fw-700 mb-16"
>
电商核心创意工具组
</div>
<div
class=
"tool-grid flex gap-16"
>
<
div
v-for=
"item in tools"
:key=
"item.title"
<
el-card
v-for=
"item in tools"
:key=
"item.title"
class=
"core-card flex-col justify-between flex-1 minh-160 p-16 round-14 bg-fff text-left pointer"
:style=
"
{ '--accent': item.color, '--soft': item.bgColor }">
:style=
"
{ '--accent': item.color, '--soft': item.bgColor }"
shadow="never" @click="handleToolClick(item)"
>
<div>
<span
class=
"core-icon flex-center wh-36 round-10 mb-12 fs-20"
>
<el-icon>
<component
:is=
"item.icon"
/>
</el-icon>
</span>
<div
class=
"lh-22 fw-700"
>
{{
item
.
title
}}
</div>
<div
class=
"
core-title
lh-22 fw-700"
>
{{
item
.
title
}}
</div>
<p
class=
"fs-12 lh-20"
>
{{
item
.
desc
}}
</p>
</div>
<div
class=
"card-action flex-items-center gap-4 fs-12 fw-700"
>
...
...
@@ -20,14 +20,19 @@
<ArrowRight
/>
</el-icon>
</div>
</
div
>
</
el-card
>
</div>
<DigitalHumanDialog
v-model=
"digitalHumanVisible"
/>
<ModelTryOnDialog
v-model=
"modelTryOnVisible"
/>
</section>
</
template
>
<
script
setup
lang=
"ts"
>
import
type
{
Component
}
from
'vue'
;
import
{
ref
,
type
Component
}
from
'vue'
;
import
{
useRouter
}
from
'vue-router'
;
import
{
ArrowRight
,
Edit
,
Picture
,
Scissor
,
User
,
ZoomIn
}
from
'@element-plus/icons-vue'
;
import
DigitalHumanDialog
from
'./DigitalHumanDialog.vue'
;
import
ModelTryOnDialog
from
'./ModelTryOnDialog.vue'
;
type
ToolItem
=
{
title
:
string
;
...
...
@@ -36,8 +41,14 @@ type ToolItem = {
color
:
string
;
bgColor
:
string
;
icon
:
Component
;
path
?:
string
;
dialog
?:
'digitalHuman'
|
'modelTryOn'
;
};
const
router
=
useRouter
();
const
digitalHumanVisible
=
ref
(
false
);
const
modelTryOnVisible
=
ref
(
false
);
const
tools
:
ToolItem
[]
=
[
{
title
:
'视频去水印'
,
...
...
@@ -46,6 +57,7 @@ const tools: ToolItem[] = [
color
:
'#ff1f82'
,
bgColor
:
'#fff0f7'
,
icon
:
Edit
,
path
:
'/quickCreation/remove-watermark'
,
},
{
title
:
'字幕擦除'
,
...
...
@@ -54,6 +66,7 @@ const tools: ToolItem[] = [
color
:
'#1877ff'
,
bgColor
:
'#edf5ff'
,
icon
:
Scissor
,
path
:
'/quickCreation/remove-subtitles'
},
{
title
:
'画质增强'
,
...
...
@@ -62,6 +75,7 @@ const tools: ToolItem[] = [
color
:
'#ff9900'
,
bgColor
:
'#fff8e8'
,
icon
:
ZoomIn
,
path
:
'/quickCreation/remove-quality'
,
},
{
title
:
'数字人分身'
,
...
...
@@ -70,6 +84,7 @@ const tools: ToolItem[] = [
color
:
'#f2187d'
,
bgColor
:
'#fff0f7'
,
icon
:
User
,
dialog
:
'digitalHuman'
,
},
{
title
:
'模特换衣'
,
...
...
@@ -78,8 +93,23 @@ const tools: ToolItem[] = [
color
:
'#1877ff'
,
bgColor
:
'#edf5ff'
,
icon
:
Picture
,
dialog
:
'modelTryOn'
,
},
];
const
handleToolClick
=
(
item
:
ToolItem
)
=>
{
if
(
item
.
dialog
===
'digitalHuman'
)
{
digitalHumanVisible
.
value
=
true
;
return
;
}
if
(
item
.
dialog
===
'modelTryOn'
)
{
modelTryOnVisible
.
value
=
true
;
return
;
}
if
(
item
.
path
)
{
router
.
push
(
item
.
path
);
}
};
</
script
>
<
style
lang=
"scss"
scoped
>
...
...
@@ -90,7 +120,7 @@ const tools: ToolItem[] = [
transition
:
border-color
.2s
,
box-shadow
.2s
,
transform
.2s
;
}
:deep
(
.core-card
>
span
)
{
:deep
(
.core-card
.el-card__body
)
{
display
:
flex
;
flex
:
1
;
flex-direction
:
column
;
...
...
@@ -98,6 +128,7 @@ const tools: ToolItem[] = [
justify-content
:
space-between
;
width
:
100%
;
height
:
100%
;
padding
:
0
;
}
.core-card
p
,
...
...
@@ -105,6 +136,10 @@ const tools: ToolItem[] = [
color
:
#86a0bd
;
}
.core-title
{
color
:
#001a3d
;
}
.core-icon
{
color
:
var
(
--
accent
);
background
:
var
(
--
soft
);
...
...
@@ -116,7 +151,7 @@ const tools: ToolItem[] = [
transform
:
translateY
(
-2px
);
}
.core-card
:hover
h3
,
.core-card
:hover
.core-title
,
.core-card
:hover
.card-action
{
color
:
var
(
--
accent
);
}
...
...
apps/web-vue/src/views/quickCreation/remove-quality.vue
0 → 100644
View file @
626b653c
<
template
>
<div
class=
"h-full over-y-auto p-20 bgmr"
>
<div
class=
"remove-content"
>
<div
class=
" mb-20"
>
<div
class=
"fw-600 fs-16 flex-items-center color-000 gap-10"
>
视频画质增强
<span
class=
"color-main fs-10 round-4 px-6 py-2 tagssss"
>
SUPERSAMPLING AI
</span></div>
<div
class=
"fs-12 color-888 mt-6"
>
智能重构低清素材、还原奢华微距材质、平滑运动插帧,挽救失焦素材
</div>
</div>
<div
class=
"quality-main flex items-start gap-24"
>
<div
class=
"left-panel flex-col gap-20"
>
<el-card
class=
"panel-card round-10"
shadow=
"never"
>
<template
#
header
>
<div
class=
"lh-22 fw-700 color-000"
>
选择带货视频
</div>
</
template
>
<div
class=
"upload-area w-full pointer"
@
click=
"mediaVisible = true"
>
<div
class=
"flex-col flex-center h-150"
>
<span
class=
"upload-icon flex-center wh-40 round mb-10"
>
<el-icon
class=
"fs-22"
>
<UploadFilled
/>
</el-icon>
</span>
<div
class=
"lh-22 fs-12 color-000"
>
选择素材库中的电商视频
</div>
<p
class=
"fs-12 color-999"
>
支持选择 5 分钟以内的 mp4 / mov / avi 素材
</p>
</div>
</div>
</el-card>
<el-card
class=
"panel-card round-10"
shadow=
"never"
>
<
template
#
header
>
<div
class=
"lh-22 fw-700 color-000"
>
画质重构设置
</div>
</
template
>
<div
class=
"quality-form flex-col gap-20"
>
<div
class=
"flex gap-16"
>
<div
class=
"flex-1"
>
<div
class=
"form-label fs-12 lh-20 mb-8 color-888"
>
输出分辨率 / RESOLUTION
</div>
<el-select
v-model=
"resolution"
class=
"w-full"
size=
"large"
>
<el-option
label=
"1080P 超高清极速渲染"
value=
"1080p"
/>
<el-option
label=
"2K 商业展示画质"
value=
"2k"
/>
<el-option
label=
"4K 品质重写"
value=
"4k"
/>
</el-select>
</div>
<div
class=
"flex-1"
>
<div
class=
"form-label fs-12 lh-20 color-888 mb-8"
>
目标帧率 / MOTION FRAME
</div>
<el-select
v-model=
"frameRate"
class=
"w-full"
size=
"large"
>
<el-option
label=
"AI 智能插帧 60fps (超流畅)"
value=
"60fps"
/>
<el-option
label=
"保持原帧率"
value=
"source"
/>
<el-option
label=
"电影感 24fps"
value=
"24fps"
/>
</el-select>
</div>
</div>
<div>
<div
class=
"form-label fs-12 lh-20 color-888 mb-8"
>
超分辨率计算引擎 / AI ENGINE
</div>
<el-select
v-model=
"engine"
class=
"w-full"
size=
"large"
>
<el-option
label=
"UltraEnhance-v3.0 (高细节材质深度还原)"
value=
"ultra"
/>
<el-option
label=
"FastRestore-v2.5 (极速清晰增强)"
value=
"fast"
/>
<el-option
label=
"PortraitClean-v1.8 (人像肤质优化)"
value=
"portrait"
/>
</el-select>
</div>
</div>
</el-card>
<el-card
class=
"guide-card round-10 pt-16"
shadow=
"never"
>
<div
class=
"flex-items-center gap-6 mb-10"
>
<el-icon
class=
"color-main fs-16"
>
<QuestionFilled
/>
</el-icon>
<span
class=
"lh-22 fw-700 color-main"
>
超分增强操作指引
</span>
</div>
<div
class=
"fs-12 lh-26 color-888"
>
<p>
• 本算法针对珠宝类纹理、服装皮质、毛发质感有深度重构增强作用。
</p>
<p>
• 超分渲染时间受视频源时长及分辨率选项影响,一般在 1-3 分钟内完成。
</p>
<p>
• 开始增强预计将消耗 5.00 积分点。
</p>
</div>
</el-card>
<el-button
type=
"primary"
class=
"run-btn h-40 round-6 border-none"
>
一键激发 4K 画质重构 (预计 5.00)
</el-button>
</div>
<el-card
class=
"preview-card flex-1 round-10"
shadow=
"never"
>
<
template
#
header
>
<div
class=
"flex-between"
>
<div
class=
"lh-22 fw-700 color-000"
>
算法效果对比(实时微距模拟)
</div>
<span
class=
"fs-12 color-999"
>
左右拖拽中线看清超高清细节
</span>
</div>
</
template
>
<div
ref=
"compareRef"
class=
"compare-stage relative hidden round-10 pointer"
@
pointerdown=
"handlePointerMove"
>
<img
:src=
"previewImage"
alt=
"画质增强后预览"
class=
"compare-img w-full h-full cover"
/>
<div
class=
"before-layer absolute top-0 left-0 w-full h-full hidden"
:style=
"{ clipPath: `inset(0 ${100 - comparePercent}% 0 0)` }"
>
<img
:src=
"previewImage"
alt=
"画质增强前预览"
class=
"before-img w-full h-full cover"
/>
</div>
<el-tag
class=
"compare-tag before-tag absolute top-14 left-14 h-20 lh-20 px-10 round-10 fs-10"
effect=
"dark"
>
BEFORE / 480P 压缩源图
</el-tag>
<el-tag
class=
"compare-tag after-tag absolute top-14 right-14 h-20 lh-20 px-10 round-10 fs-10"
effect=
"dark"
>
AFTER / 4K 质感重写
</el-tag>
<div
class=
"compare-line absolute top-0 h-full"
:style=
"{ left: `${comparePercent}%` }"
>
<span
class=
"handle-dot absolute flex-center wh-28 round"
>
<span
class=
"handle-bar h-12"
></span>
<span
class=
"handle-bar h-12"
></span>
</span>
</div>
</div>
<div
class=
"effect-list flex gap-12 mt-24"
>
<div
class=
"effect-card flex-1 flex-col flex-center h-78 round-10"
>
<el-icon
class=
"color-main fs-18 mb-6"
>
<MagicStick
/>
</el-icon>
<div
class=
"fs-12 lh-20 fw-700 color-000"
>
GPU 硬件加速
</div>
<p
class=
"fs-12 lh-20 color-999"
>
双核 Tensor A100 并行解算
</p>
</div>
<div
class=
"effect-card flex-1 flex-col flex-center h-78 round-10"
>
<el-icon
class=
"quality-pink fs-18 mb-6"
>
<Picture
/>
</el-icon>
<div
class=
"fs-12 lh-20 fw-700 color-000"
>
质感智能补强
</div>
<p
class=
"fs-12 lh-20 color-999"
>
还原奢侈品反射与纯面高光
</p>
</div>
</div>
</el-card>
</div>
<MediaMaterialDialog
v-model=
"mediaVisible"
v-model:selected=
"selectedMaterials"
/>
</div>
</div>
</template>
<
script
setup
lang=
"ts"
>
import
{
onBeforeUnmount
,
ref
}
from
'vue'
;
import
{
BrushFilled
,
MagicStick
,
Picture
,
QuestionFilled
,
UploadFilled
}
from
'@element-plus/icons-vue'
;
import
previewImage
from
'@/static/image/meinv.png'
;
import
MediaMaterialDialog
from
'./components/MediaMaterialDialog.vue'
;
import
type
{
MaterialItem
}
from
'./types'
;
const
mediaVisible
=
ref
(
false
);
const
selectedMaterials
=
ref
<
MaterialItem
[]
>
([]);
const
resolution
=
ref
(
'1080p'
);
const
frameRate
=
ref
(
'60fps'
);
const
engine
=
ref
(
'ultra'
);
const
compareRef
=
ref
<
HTMLElement
>
();
const
comparePercent
=
ref
(
48
);
const
updateComparePercent
=
(
event
:
PointerEvent
)
=>
{
if
(
!
compareRef
.
value
)
{
return
;
}
const
rect
=
compareRef
.
value
.
getBoundingClientRect
();
const
percent
=
((
event
.
clientX
-
rect
.
left
)
/
rect
.
width
)
*
100
;
comparePercent
.
value
=
Math
.
min
(
88
,
Math
.
max
(
12
,
Math
.
round
(
percent
)));
};
const
stopDrag
=
()
=>
{
window
.
removeEventListener
(
'pointermove'
,
updateComparePercent
);
window
.
removeEventListener
(
'pointerup'
,
stopDrag
);
};
const
handlePointerMove
=
(
event
:
PointerEvent
)
=>
{
updateComparePercent
(
event
);
window
.
addEventListener
(
'pointermove'
,
updateComparePercent
);
window
.
addEventListener
(
'pointerup'
,
stopDrag
);
};
onBeforeUnmount
(
stopDrag
);
</
script
>
<
style
scoped
lang=
"scss"
>
.remove-content
{
max-width
:
1600px
;
margin
:
auto
;
}
.tagssss
{
background-color
:
rgba
(
var
(
--
main-color
)
,
.04
);
box-shadow
:
0
0
0
1px
rgba
(
var
(
--
main-color
)
,
.1
);
}
.left-panel
{
flex
:
0
0
49%
;
}
.panel-card
,
.preview-card
,
.guide-card
{
border
:
1px
solid
#dfe7f2
;
}
:deep
(
.el-card__header
)
{
padding
:
16px
16px
10px
;
border-bottom
:
0
;
}
:deep
(
.el-card__body
)
{
padding
:
0
20px
20px
;
}
.upload-area
{
border
:
1px
dashed
#d9e4f2
;
border-radius
:
10px
;
background
:
#fbfdff
;
}
.upload-icon
{
color
:
#708aaf
;
background
:
#edf3fb
;
}
.guide-card
{
background
:
#f8fbff
;
}
.run-btn
{
background
:
linear-gradient
(
90deg
,
#9d19ff
0%
,
#f23594
100%
);
}
.preview-card
{
min-width
:
0
;
}
.compare-stage
{
height
:
292px
;
background
:
#eef3f8
;
touch-action
:
none
;
user-select
:
none
;
}
.compare-img
{
filter
:
saturate
(
1
.12
)
contrast
(
1
.08
);
}
.before-layer
{
filter
:
blur
(
2px
)
saturate
(
.72
)
contrast
(
.78
);
}
.compare-tag
{
border
:
0
;
font-weight
:
700
;
}
.before-tag
{
background
:
#ff4d5f
;
}
.after-tag
{
background
:
#26c486
;
}
.compare-line
{
width
:
2px
;
background
:
rgba
(
255
,
255
,
255
,
.9
);
transform
:
translateX
(
-1px
);
}
.handle-dot
{
top
:
50%
;
left
:
50%
;
gap
:
3px
;
color
:
rgba
(
var
(
--
main-color
)
,
1
);
background
:
#fff
;
border
:
2px
solid
rgba
(
var
(
--
main-color
)
,
1
);
box-shadow
:
0
4px
14px
rgba
(
var
(
--
main-color
)
,
.2
);
transform
:
translate
(
-50%
,
-50%
);
}
.handle-bar
{
width
:
2px
;
background
:
currentColor
;
}
.effect-card
{
border
:
1px
solid
#dfe7f2
;
background
:
#fbfdff
;
}
.quality-pink
{
color
:
#f23594
;
}
@media
(
max-width
:
1180px
)
{
.quality-main
{
flex-wrap
:
wrap
;
}
.left-panel
,
.preview-card
{
flex
:
0
0
100%
;
}
}
</
style
>
\ No newline at end of file
apps/web-vue/src/views/quickCreation/remove-subtitles.vue
0 → 100644
View file @
626b653c
<
template
>
<div
class=
"h-full over-y-auto p-20 bgmr"
>
<div
class=
"remove-content"
>
<div
class=
"flex-between mb-20"
>
<div>
<div
class=
"fw-600 fs-16 flex-items-center color-000 gap-10"
>
视频字幕擦除
<span
class=
"color-main fs-10 round-4 px-6 py-2 tagssss"
>
SUBTITLE ERASE
</span></div>
<div
class=
"fs-12 color-888 mt-6"
>
一键分析字幕所在底条,智能无痕还原带货视频底部像素
</div>
</div>
<el-button
class=
"h-28 px-8 fs-12"
>
<el-icon>
<BrushFilled
/>
</el-icon>
<span>
新建擦除
</span>
</el-button>
</div>
<RemoveVideo
/>
</div>
</div>
</
template
>
<
script
setup
lang=
"ts"
>
import
{
BrushFilled
}
from
'@element-plus/icons-vue'
;
import
RemoveVideo
from
'./components/Remove-video.vue'
;
</
script
>
<
style
scoped
lang=
"scss"
>
.remove-content
{
max-width
:
1600px
;
margin
:
auto
;
}
.tagssss
{
background-color
:
rgba
(
var
(
--
main-color
)
,
.04
);
box-shadow
:
0
0
0
1px
rgba
(
var
(
--
main-color
)
,
.1
);
}
.left-panel
{
flex
:
0
0
40%
;
}
.panel-card
,
.preview-card
,
.guide-card
{
border
:
1px
solid
#dfe7f2
;
}
:deep
(
.el-card__header
)
{
padding
:
16px
16px
10px
;
border-bottom
:
0
;
}
:deep
(
.el-card__body
)
{
padding
:
0
20px
20px
;
}
:deep
(
.upload-area
.el-upload
)
{
width
:
100%
;
}
:deep
(
.upload-area
.el-upload-dragger
)
{
padding
:
0
;
border-color
:
#d9e4f2
;
border-style
:
dashed
;
background
:
#fbfdff
;
}
.upload-icon
,
.video-icon
{
color
:
#708aaf
;
background
:
#edf3fb
;
}
.watermark-tag
{
border-color
:
rgba
(
var
(
--
main-color
)
,
.1
);
background
:
rgba
(
var
(
--
main-color
)
,
.06
);
}
.guide-card
{
background
:
#f8fbff
;
}
.run-btn
{
background
:
linear-gradient
(
90deg
,
#9d19ff
0%
,
#f23594
100%
);
}
.preview-frame
{
height
:
348px
;
background
:
#eef3f8
;
}
.preview-mask
{
background
:
rgba
(
248
,
250
,
252
,
.68
);
backdrop-filter
:
blur
(
1px
);
}
.selection-box
{
border
:
2px
dashed
#c77dff
;
background
:
rgba
(
199
,
125
,
255
,
.08
);
}
.selection-label
{
background
:
#b45cff
;
}
.selection-close
{
background
:
#ff6b8b
;
}
.empty-state
{
top
:
50%
;
left
:
50%
;
transform
:
translate
(
-50%
,
-50%
);
}
.video-icon
{
color
:
rgba
(
var
(
--
main-color
)
,
1
);
background
:
#fff
;
}
.status-bar
{
border
:
1px
solid
#dfe7f2
;
background
:
#f8fbff
;
}
</
style
>
apps/web-vue/src/views/quickCreation/remove-watermark.vue
0 → 100644
View file @
626b653c
<
template
>
<div
class=
"h-full over-y-auto p-20 bgmr"
>
<div
class=
"remove-content"
>
<div
class=
"flex-between mb-20"
>
<div>
<div
class=
"fw-600 fs-16 flex-items-center color-000 gap-10"
>
视频去水印
<span
class=
"color-main fs-10 round-4 px-6 py-2 tagssss"
>
WATERMARK REMOVAL
</span></div>
<div
class=
"fs-12 color-888 mt-6"
>
采用多维光流时间轴补全算法,干净擦除顽固角标,动态品牌浮窗与遮挡物
</div>
</div>
<el-button
class=
"h-28 px-8 fs-12"
>
<el-icon>
<BrushFilled
/>
</el-icon>
<span>
新建擦除
</span>
</el-button>
</div>
<RemoveVideo
/>
</div>
</div>
</
template
>
<
script
setup
lang=
"ts"
>
import
{
BrushFilled
}
from
'@element-plus/icons-vue'
;
import
RemoveVideo
from
'./components/Remove-video.vue'
;
</
script
>
<
style
scoped
lang=
"scss"
>
.remove-content
{
max-width
:
1600px
;
margin
:
auto
;
}
.tagssss
{
background-color
:
rgba
(
var
(
--
main-color
)
,
.04
);
box-shadow
:
0
0
0
1px
rgba
(
var
(
--
main-color
)
,
.1
);
}
</
style
>
apps/web-vue/src/views/quickCreation/types.ts
View file @
626b653c
export
type
SceneKey
=
'product'
|
'pain'
|
'process'
|
'koc'
|
'story'
;
export
type
MaterialItem
=
{
id
:
string
;
name
:
string
;
kind
:
'图片'
|
'视频'
;
size
:
string
;
image
:
string
;
};
apps/web-vue/src/views/resource/components/Item.vue
deleted
100644 → 0
View file @
96e60a33
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