Commit 626b653c authored by 黄同智's avatar 黄同智

工具类页面

parent 96e60a33
......@@ -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 }
}
]
},
......
<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>
......@@ -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);
}
}
......
<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>
<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
......@@ -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);
}
......
This diff is collapsed.
<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>
<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>
export type SceneKey = 'product' | 'pain' | 'process' | 'koc' | 'story';
export type MaterialItem = {
id: string;
name: string;
kind: '图片' | '视频';
size: string;
image: string;
};
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment