即梦绘画添加图片特效预览

This commit is contained in:
GeekMaster
2025-07-29 18:34:29 +08:00
parent ff96fada02
commit 9fba68fb14
20 changed files with 4982 additions and 85 deletions

View File

@@ -386,7 +386,7 @@ export const useJimengStore = defineStore('jimeng', () => {
break
case 'image_edit':
Object.assign(requestData, {
image_urls: imageEditParams.image_urls,
image_urls: [imageEditParams.image_urls],
scale: imageEditParams.scale,
seed: imageEditParams.seed,
})
@@ -397,6 +397,7 @@ export const useJimengStore = defineStore('jimeng', () => {
template_id: imageEffectsParams.template_id,
width: parseInt(imageEffectsParams.size.split('x')[0]),
height: parseInt(imageEffectsParams.size.split('x')[1]),
prompt: imageEffectsParams.prompt,
})
break
case 'text_to_video':
@@ -640,3 +641,68 @@ export const videoAspectRatioOptions = [
{ label: '16:9 (横版)', value: '16:9' },
{ label: '9:16 (竖版)', value: '9:16' },
]
export const imageEffectsTemplateOptions = [
{
label: '毛毡3D拍立得风格',
value: 'felt_3d_polaroid',
preview: '/images/jimeng/templates/felt_3d_polaroid.png',
},
{ label: '像素世界风', value: 'my_world', preview: '/images/jimeng/templates/my_world.png' },
{
label: '像素世界-万物通用版',
value: 'my_world_universal',
preview: '/images/jimeng/templates/my_world_universal.png',
},
{
label: '盲盒玩偶风',
value: 'plastic_bubble_figure',
preview: '/images/jimeng/templates/plastic_bubble_figure.png',
},
{
label: '塑料泡罩人偶-文字卡头版',
value: 'plastic_bubble_figure_cartoon_text',
preview: '/images/jimeng/templates/plastic_bubble_figure_cartoon_text.png',
},
{
label: '毛绒玩偶风',
value: 'furry_dream_doll',
preview: '/images/jimeng/templates/furry_dream_doll.png',
},
{
label: '迷你世界玩偶风',
value: 'micro_landscape_mini_world',
preview: '/images/jimeng/templates/micro_landscape_mini_world.png',
},
{
label: '微型景观小世界-职业版',
value: 'micro_landscape_mini_world_professional',
preview: '/images/jimeng/templates/micro_landscape_mini_world_professional.png',
},
{
label: '亚克力挂饰',
value: 'acrylic_ornaments',
preview: '/images/jimeng/templates/acrylic_ornaments.png',
},
{
label: '毛毡钥匙扣',
value: 'felt_keychain',
preview: '/images/jimeng/templates/felt_keychain.png',
},
{
label: 'Lofi 像素人物小卡',
value: 'lofi_pixel_character_mini_card',
preview: '/images/jimeng/templates/lofi_pixel_character_mini_card.png',
},
{
label: '天使形象手办',
value: 'angel_figurine',
preview: '/images/jimeng/templates/angel_figurine.png',
},
{
label: '躺在毛茸茸肚皮里',
value: 'lying_in_fluffy_belly',
preview: '/images/jimeng/templates/lying_in_fluffy_belly.png',
},
{ label: '玻璃球', value: 'glass_ball', preview: '/images/jimeng/templates/glass_ball.png' },
]

View File

@@ -181,10 +181,40 @@
<span class="label">特效模板:</span>
</div>
<div class="param-line">
<el-select v-model="store.imageEffectsParams.template_id" placeholder="选择特效模板">
<el-option label="经典特效" value="classic" />
<el-option label="艺术风格" value="artistic" />
<el-option label="现代科技" value="modern" />
<el-select
v-model="store.imageEffectsParams.template_id"
placeholder="选择特效模板"
popper-class="jimeng-template-select"
@change="handleTemplateChange($event)"
>
<template #prefix>
<div class="flex items-center py-1">
<el-image
v-if="templatePreview"
:src="templatePreview"
class="w-[50px] h-[50px] object-cover rounded-md"
:preview-src-list="[templatePreview]"
:preview-teleported="true"
@click.stop
/>
</div>
</template>
<el-option
v-for="opt in imageEffectsTemplateOptions"
:key="opt.value"
:label="opt.label"
:value="opt.value"
>
<div class="flex flex-row justify-between">
<span class="template-label">{{ opt.label }}</span>
<img
v-if="opt.preview"
:src="opt.preview"
:alt="opt.label"
class="w-[50px] h-[50px] object-cover rounded-md"
/>
</div>
</el-option>
</el-select>
</div>
@@ -444,17 +474,17 @@
></i>
</el-tooltip>
</span>
<span class="ml-1" v-if="item.status === 'failed'">
<el-tooltip content="删除" placement="top">
<i
class="iconfont icon-remove cursor-pointer text-red-500"
@click="store.removeJob(item)"
></i>
</el-tooltip>
</span>
</template>
<span class="ml-1">
<el-tooltip content="删除" placement="top">
<i
class="iconfont icon-remove cursor-pointer text-red-500"
@click="store.removeJob(item)"
></i>
</el-tooltip>
</span>
<span class="ml-1" v-if="item.video_url || item.img_url">
<el-tooltip content="下载" placement="top">
<i
@@ -518,8 +548,14 @@
import '@/assets/css/jimeng.styl'
import loadingIcon from '@/assets/img/loading.gif'
import ImageUpload from '@/components/ImageUpload.vue'
import Generating from '@/components/ui/Generating.vue'
import { imageSizeOptions, useJimengStore, videoAspectRatioOptions } from '@/store/jimeng'
import {
imageEffectsTemplateOptions,
imageSizeOptions,
useJimengStore,
videoAspectRatioOptions,
} from '@/store/jimeng'
import { useSharedStore } from '@/store/sharedata'
import { dateFormat } from '@/utils/libs'
import { Switch } from '@element-plus/icons-vue'
@@ -546,6 +582,8 @@ const store = useJimengStore()
// 新增:瀑布流渲染完成状态
const waterfallRendered = ref(false)
// 新增:模板预览图
const templatePreview = ref('')
onMounted(() => {
store.init()
@@ -574,6 +612,13 @@ watch(
}
)
function handleTemplateChange(value) {
templatePreview.value = imageEffectsTemplateOptions.find((opt) => opt.value === value)?.preview
store.imageEffectsParams.prompt = imageEffectsTemplateOptions.find(
(opt) => opt.value === value
)?.label
}
function onWaterfallAfterRender() {
waterfallRendered.value = true
if (!store.loading && !store.isOver) {
@@ -604,7 +649,7 @@ function copyErrorMsg(msg) {
}
</script>
<style lang="stylus" scoped>
<style lang="scss" scoped>
.task-list {
.task-grid {
display: grid;
@@ -614,8 +659,9 @@ function copyErrorMsg(msg) {
}
// 新增:增强任务项悬停动画
.task-item {
transition: box-shadow 3s cubic-bezier(0.4,0,0.2,1), transform 0.5s cubic-bezier(0.4,0,0.2,1), border-color 0.5s;
box-shadow: 0 2px 8px rgba(0,0,0,0.04);
transition: box-shadow 3s cubic-bezier(0.4, 0, 0.2, 1),
transform 0.5s cubic-bezier(0.4, 0, 0.2, 1), border-color 0.5s;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.04);
border: 1.5px solid transparent;
border-radius: 12px;
background: #fff;
@@ -623,7 +669,7 @@ function copyErrorMsg(msg) {
z-index: 1;
}
.task-item:hover {
box-shadow: 0 8px 32px rgba(0,0,0,0.18), 0 1.5px 8px rgba(0,0,0,0.10);
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.18), 0 1.5px 8px rgba(0, 0, 0, 0.1);
border-color: #a259ff;
transform: scale(1.025) translateY(-2px);
z-index: 10;
@@ -640,49 +686,68 @@ function copyErrorMsg(msg) {
grid-template-columns: 1fr;
}
}
.preview-video-wrapper
position: relative
width: 100%
height: 100%
.video-mask
position: absolute
top: 0
left: 0
width: 100%
height: 100%
background: rgba(0,0,0,0.25)
display: flex
justify-content: center
align-items: center
opacity: 0
transition: opacity 0.2s
z-index: 2
&:hover .video-mask
opacity: 1
.play-btn
width: 64px
height: 64px
background: rgba(255,255,255,0.3)
border-radius: 50%
display: flex
justify-content: center
align-items: center
box-shadow: 0 2px 8px rgba(0,0,0,0.15)
cursor: pointer
z-index: 3
transition: background 0.2s
&:hover
background: rgba(255,255,255,0.4)
.play-btn img
width: 36px
height: 36px
.err-msg-clip
display: -webkit-box
-webkit-line-clamp: 2
-webkit-box-orient: vertical
overflow: hidden
text-overflow: ellipsis
word-break: break-all
white-space: normal
cursor: pointer
.preview-video-wrapper {
position: relative;
width: 100%;
height: 100%;
.video-mask {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.25);
display: flex;
justify-content: center;
align-items: center;
opacity: 0;
transition: opacity 0.2s;
z-index: 2;
}
&:hover .video-mask {
opacity: 1;
}
.play-btn {
width: 64px;
height: 64px;
background: rgba(255, 255, 255, 0.3);
border-radius: 50%;
display: flex;
justify-content: center;
align-items: center;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
cursor: pointer;
z-index: 3;
transition: background 0.2s;
&:hover {
background: rgba(255, 255, 255, 0.4);
}
img {
width: 36px;
height: 36px;
}
}
}
.err-msg-clip {
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
overflow: hidden;
text-overflow: ellipsis;
word-break: break-all;
white-space: normal;
}
.jimeng-template-select {
.el-select-dropdown__item {
height: 60px;
line-height: 60px;
}
}
</style>