feat: design跳转视频生成弹窗
This commit is contained in:
1
src/assets/icons/CCheck.svg
Normal file
1
src/assets/icons/CCheck.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg t="1763432312095" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4678" width="200" height="200"><path d="M509.92 176C325.504 176 176 325.504 176 509.92c0 184.416 149.504 333.92 333.92 333.92 184.416 0 333.92-149.504 333.92-333.92C843.84 325.504 694.32 176 509.92 176z m166.64 214.848a16 16 0 0 1 22.624 0l11.328 11.312a16 16 0 0 1 0 22.624l-254.08 254.08a16 16 0 0 1-22.624 0l-159.616-159.632a16 16 0 0 1 0-22.624l11.312-11.312a16 16 0 0 1 22.624 0l136.992 136.992z" fill="currentColor" p-id="4679"></path></svg>
|
||||
|
After Width: | Height: | Size: 562 B |
248
src/component/home/tools/poseTransfer/Product.vue
Normal file
248
src/component/home/tools/poseTransfer/Product.vue
Normal file
@@ -0,0 +1,248 @@
|
||||
<template>
|
||||
<a-modal
|
||||
class="product-modal generalModel"
|
||||
v-model:visible="showModal"
|
||||
:footer="null"
|
||||
width="78%"
|
||||
:maskClosable="false"
|
||||
:centered="true"
|
||||
:closable="false"
|
||||
wrapClassName="#app"
|
||||
:keyboard="false"
|
||||
>
|
||||
<div class="generalModel_btn">
|
||||
<div class="generalModel_closeIcon" @click.stop="handleClose()">
|
||||
<svg
|
||||
width="100%"
|
||||
height="100%"
|
||||
viewBox="0 0 46 46"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<circle cx="23" cy="23" r="23" fill="#000" fill-opacity="0.3" />
|
||||
<rect
|
||||
x="32.5063"
|
||||
y="12"
|
||||
width="3"
|
||||
height="29"
|
||||
rx="1.5"
|
||||
transform="rotate(45 32.5063 12)"
|
||||
fill="white"
|
||||
/>
|
||||
<rect
|
||||
x="34.6274"
|
||||
y="32.5059"
|
||||
width="3"
|
||||
height="29"
|
||||
rx="1.5"
|
||||
transform="rotate(135 34.6274 32.5059)"
|
||||
fill="white"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
<div class="content-wrapper">
|
||||
<div class="title-container">
|
||||
<div class="title">{{ type === 'first' ? 'First Frame' : 'Last Frame' }}</div>
|
||||
</div>
|
||||
<div class="content-container">
|
||||
<div class="video-frames-wrapper">
|
||||
<div class="video-frames-scroll">
|
||||
<div
|
||||
v-for="(frame, index) in frameList"
|
||||
:key="index"
|
||||
class="frame-item"
|
||||
@click="selectFrame(index)"
|
||||
>
|
||||
<SvgIcon
|
||||
v-if="selectedFrameIndex === index"
|
||||
class="check-icon"
|
||||
name="CCheck"
|
||||
color="#000000"
|
||||
size="24"
|
||||
/>
|
||||
<img v-if="frame.url" :src="frame.url" alt="" />
|
||||
<div v-else class="frame-placeholder"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="action-container">
|
||||
<button class="confirm-btn" @click="handleConfirm">Confirm</button>
|
||||
</div>
|
||||
</div>
|
||||
</a-modal>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, watch } from 'vue'
|
||||
|
||||
const props = defineProps<{
|
||||
frameList: Array<{ url?: string }>
|
||||
type: String // first last
|
||||
}>()
|
||||
|
||||
const showModal = defineModel<boolean>('showModal', { required: true })
|
||||
const selectedFrameIndex = ref<number | null>(null)
|
||||
|
||||
const emits = defineEmits(['confirm'])
|
||||
|
||||
// 当 type 改变时,重置选中状态
|
||||
watch(() => props.type, () => {
|
||||
selectedFrameIndex.value = null
|
||||
})
|
||||
|
||||
// 当弹窗关闭时,重置选中状态
|
||||
watch(() => showModal.value, (newVal) => {
|
||||
if (!newVal) {
|
||||
selectedFrameIndex.value = null
|
||||
}
|
||||
})
|
||||
|
||||
const handleClose = () => {
|
||||
showModal.value = false
|
||||
}
|
||||
|
||||
const selectFrame = (index: number) => {
|
||||
selectedFrameIndex.value = index
|
||||
}
|
||||
|
||||
const handleConfirm = () => {
|
||||
if (selectedFrameIndex.value !== null) {
|
||||
// 触发确认事件或回调
|
||||
const selected = props.frameList[selectedFrameIndex.value]
|
||||
emits('confirm', { data: selected })
|
||||
}
|
||||
handleClose()
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.product-modal {
|
||||
.content-wrapper {
|
||||
// padding: 0;
|
||||
position: relative;
|
||||
// margin-top: 1.36rem;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
.title-container {
|
||||
.title {
|
||||
font-size: 2.4rem;
|
||||
font-weight: 500;
|
||||
color: rgba(0, 0, 0, 0.85);
|
||||
}
|
||||
}
|
||||
|
||||
.content-container {
|
||||
margin-top: 5.8rem;
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
min-height: 0;
|
||||
|
||||
.video-frames-wrapper {
|
||||
background: #ffffff;
|
||||
border-radius: 2rem;
|
||||
padding: 1.2rem 2.88rem;
|
||||
flex: 1;
|
||||
height: 100%;
|
||||
min-height: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
|
||||
.video-frames-scroll {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 3.1rem;
|
||||
align-content: flex-start;
|
||||
overflow-y: auto;
|
||||
overflow-x: auto;
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
scrollbar-width: thin;
|
||||
scrollbar-color: rgba(0, 0, 0, 0.2) transparent;
|
||||
|
||||
&::-webkit-scrollbar {
|
||||
width: 0.4rem;
|
||||
height: 0.4rem;
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar-thumb {
|
||||
background-color: rgba(0, 0, 0, 0.2);
|
||||
border-radius: 0.2rem;
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar-track {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.frame-item {
|
||||
flex-shrink: 0;
|
||||
width: 16.47rem;
|
||||
height: 24.6rem;
|
||||
border-radius: 0;
|
||||
border: 0.1rem solid #999999;
|
||||
overflow: hidden;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
overflow: visible;
|
||||
|
||||
.check-icon {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
transform: translate(50%, 50%);
|
||||
}
|
||||
|
||||
img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.frame-placeholder {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: #f5f5f5;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.action-container {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
|
||||
.confirm-btn {
|
||||
width: 8.6rem;
|
||||
height: 4rem;
|
||||
background: #000000;
|
||||
color: #ffffff;
|
||||
border: none;
|
||||
border-radius: 2rem;
|
||||
font-size: 1.2rem;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
:deep(.generalModel .ant-modal-body) {
|
||||
padding: 5rem 4.4rem 8rem 4.6rem;
|
||||
}
|
||||
|
||||
.c-svg {
|
||||
width: initial;
|
||||
height: initial;
|
||||
}
|
||||
</style>
|
||||
@@ -31,7 +31,7 @@
|
||||
</div>
|
||||
<div class="imgBox" v-mousewheel>
|
||||
<div
|
||||
class="item"
|
||||
class="item first-frame"
|
||||
:class="{ active: item.id == selectImg.id }"
|
||||
v-for="(item, index) in showFirstFrameList"
|
||||
@click="selectImgItem(item)"
|
||||
@@ -52,23 +52,23 @@
|
||||
v-show="item.designOutfitUrl || item.imgUrl || item.url"
|
||||
class="btnBox"
|
||||
>
|
||||
<div :class="{ active: item.isChecked }">
|
||||
<div :class="{ active: item.isChecked }" v-if="!(isDesignPage && videoType === 3)">
|
||||
<i class="fi fi-br-check"></i>
|
||||
</div>
|
||||
<div
|
||||
@click.stop="setUploadDelete(item, index)"
|
||||
v-if="source != 'design'"
|
||||
v-if="source != 'design' || (isDesignPage && videoType === 3)"
|
||||
>
|
||||
<i class="fi fi-rr-trash icon_delete"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="upload_item item"
|
||||
v-show="
|
||||
!isDesignPage && !(videoType === 3 && showFirstFrameList.length > 0)
|
||||
"
|
||||
>
|
||||
<div class="upload_item item first_frames" v-show="showFirstFrameUpload">
|
||||
<div
|
||||
class="design-mask"
|
||||
v-if="isDesignPage && videoType === 3"
|
||||
@click="handleClickDesignMask('first')"
|
||||
/>
|
||||
<div class="upload_file_item">
|
||||
<a-upload
|
||||
key="common"
|
||||
@@ -93,9 +93,14 @@
|
||||
</div>
|
||||
<template v-if="videoType === 3">
|
||||
<div
|
||||
v-show="lastFrameList?.length < 1"
|
||||
class="upload_item item last_frames"
|
||||
v-show="!isDesignPage && lastFrameList?.length < 1"
|
||||
>
|
||||
<div
|
||||
class="design-mask"
|
||||
v-if="isDesignPage && videoType === 3"
|
||||
@click="handleClickDesignMask('last')"
|
||||
/>
|
||||
<div class="upload_file_item">
|
||||
<a-upload
|
||||
key="lastframes"
|
||||
@@ -142,12 +147,12 @@
|
||||
v-show="item.designOutfitUrl || item.imgUrl || item.url"
|
||||
class="btnBox"
|
||||
>
|
||||
<div :class="{ active: item.isChecked }">
|
||||
<div :class="{ active: item.isChecked }" v-if="!(isDesignPage && videoType === 3)">
|
||||
<i class="fi fi-br-check"></i>
|
||||
</div>
|
||||
<div
|
||||
@click.stop="setUploadDelete(item, index, true)"
|
||||
v-if="source != 'design'"
|
||||
v-if="source != 'design' || (isDesignPage && videoType === 3)"
|
||||
>
|
||||
<i class="fi fi-rr-trash icon_delete"></i>
|
||||
</div>
|
||||
@@ -277,6 +282,13 @@
|
||||
</div>
|
||||
</div>
|
||||
<Tips v-model:showModal="showTips" />
|
||||
<Product
|
||||
v-model:showModal="showProductList"
|
||||
:frameList="productFrameList"
|
||||
:type="productType"
|
||||
:key="productType"
|
||||
@confirm="handleConfirmProduct"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
@@ -304,13 +316,15 @@ import showViewVideo from '@/tool/mount'
|
||||
import router from '@/router'
|
||||
import promptInput from './promptInput.vue'
|
||||
import Tips from './Tips.vue'
|
||||
import Product from './Product.vue'
|
||||
import { getFirstFrame, getFirstAndLastFrame } from './prompt'
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
generalDrag,
|
||||
promptInput,
|
||||
Tips
|
||||
Tips,
|
||||
Product
|
||||
},
|
||||
props: {
|
||||
isDesignPage: {
|
||||
@@ -437,6 +451,7 @@ export default defineComponent({
|
||||
if (props.isDesignPage) {
|
||||
//标识design页面打开的tools
|
||||
data.fileList = designList
|
||||
console.log('11111111111111', data.fileList)
|
||||
} else {
|
||||
data.currentList = store.state.UploadFilesModule.modularData.toProduct
|
||||
data.currentList = data.currentList ? data.currentList : []
|
||||
@@ -881,6 +896,57 @@ export default defineComponent({
|
||||
document.removeEventListener('click', openSpeed)
|
||||
}
|
||||
const setUploadDelete = (item: any, index: any, isLastFrame: boolean = false) => {
|
||||
// 在 isDesignPage 模式且 videoType === 3 时,直接从列表中删除,并将图片重新添加到产品列表中以便重新选择
|
||||
if (props.isDesignPage && videoType.value === 3) {
|
||||
if (isLastFrame) {
|
||||
// 从 lastFrameList 中删除
|
||||
const deleteIndex = lastFrameList.value.findIndex((listItem: any) => listItem.id === item.id)
|
||||
if (deleteIndex >= 0) {
|
||||
lastFrameList.value.splice(deleteIndex, 1)
|
||||
}
|
||||
// 将删除的项重新添加到 lastFrameProductList 中,以便可以重新选择
|
||||
const productItem = { ...item }
|
||||
// 移除 frameType 和 isChecked 属性
|
||||
delete productItem.frameType
|
||||
delete productItem.isChecked
|
||||
// 检查是否已存在,避免重复添加
|
||||
const existingIndex = lastFrameProductList.value.findIndex(
|
||||
(listItem: any) => listItem.id === productItem.id
|
||||
)
|
||||
if (existingIndex < 0) {
|
||||
lastFrameProductList.value.unshift(productItem)
|
||||
}
|
||||
// 清空选中状态
|
||||
if (data.lastSelectImg?.id === item.id) {
|
||||
data.lastSelectImg = {}
|
||||
}
|
||||
} else {
|
||||
// 从 firstFrameList 中删除
|
||||
const deleteIndex = firstFrameList.value.findIndex((listItem: any) => listItem.id === item.id)
|
||||
if (deleteIndex >= 0) {
|
||||
firstFrameList.value.splice(deleteIndex, 1)
|
||||
}
|
||||
// 将删除的项重新添加到 firstFrameProductList 中,以便可以重新选择
|
||||
const productItem = { ...item }
|
||||
// 移除 frameType 和 isChecked 属性
|
||||
delete productItem.frameType
|
||||
delete productItem.isChecked
|
||||
// 检查是否已存在,避免重复添加
|
||||
const existingIndex = firstFrameProductList.value.findIndex(
|
||||
(listItem: any) => listItem.id === productItem.id
|
||||
)
|
||||
if (existingIndex < 0) {
|
||||
firstFrameProductList.value.unshift(productItem)
|
||||
}
|
||||
// 清空选中状态
|
||||
if (data.selectImg?.id === item.id) {
|
||||
data.selectImg = {}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// 非 design 页面的原有逻辑
|
||||
let value = {
|
||||
id: item.id
|
||||
}
|
||||
@@ -920,9 +986,148 @@ export default defineComponent({
|
||||
const firstFrameList = ref([])
|
||||
const lastFrameList = ref([])
|
||||
const showFirstFrameList = computed(() => {
|
||||
if(props.isDesignPage) return data.fileList
|
||||
if (props.isDesignPage) {
|
||||
// 在 design 页面,如果 videoType === 3,显示 firstFrameList,否则显示 data.fileList
|
||||
return videoType.value === 3 ? firstFrameList.value : data.fileList
|
||||
}
|
||||
return videoType.value === 3 ? firstFrameList.value : data.fileList
|
||||
})
|
||||
|
||||
const showProductList = ref(false)
|
||||
const productType = ref('first')
|
||||
// 首帧和尾帧的独立数据源
|
||||
const firstFrameProductList = ref([])
|
||||
const lastFrameProductList = ref([])
|
||||
|
||||
const showFirstFrameUpload = computed(() => {
|
||||
// 只读依赖
|
||||
const isDesign = props.isDesignPage
|
||||
const isFirstAndLast = videoType.value === 3
|
||||
const hasFirstFrames = Array.isArray(showFirstFrameList.value)
|
||||
? showFirstFrameList.value.length > 0
|
||||
: false
|
||||
|
||||
if (isDesign) {
|
||||
// design 页面下,仅 videoType 为 3 时可展示上传,且没有首帧时显示上传按钮
|
||||
return isFirstAndLast && !hasFirstFrames
|
||||
}
|
||||
// 非 design 页面下也是类似逻辑
|
||||
return !(isFirstAndLast && hasFirstFrames)
|
||||
})
|
||||
const showLastFrameUpload = computed(() => {
|
||||
if (videoType.value !== 3) return false
|
||||
if (props.isDesignPage && lastFrameList.length > 0) return false
|
||||
return true
|
||||
})
|
||||
|
||||
// 根据 productType 返回对应的数据源
|
||||
const productFrameList = computed(() => {
|
||||
if (productType.value === 'first') {
|
||||
return firstFrameProductList.value.length > 0
|
||||
? firstFrameProductList.value
|
||||
: data.fileList
|
||||
} else {
|
||||
return lastFrameProductList.value.length > 0
|
||||
? lastFrameProductList.value
|
||||
: data.fileList
|
||||
}
|
||||
})
|
||||
|
||||
const handleClickDesignMask = (type: 'first' | 'last') => {
|
||||
productType.value = type
|
||||
// 初始化独立的数据源,如果为空则从 fileList 复制
|
||||
if (type === 'first') {
|
||||
if (firstFrameProductList.value.length === 0) {
|
||||
firstFrameProductList.value = JSON.parse(JSON.stringify(data.fileList || []))
|
||||
}
|
||||
} else {
|
||||
if (lastFrameProductList.value.length === 0) {
|
||||
lastFrameProductList.value = JSON.parse(JSON.stringify(data.fileList || []))
|
||||
}
|
||||
}
|
||||
showProductList.value = true
|
||||
}
|
||||
const handleConfirmProduct = ({ data: productData }) => {
|
||||
if (productType.value === 'first') {
|
||||
// 准备图片数据
|
||||
const imageItem: any = {
|
||||
...productData,
|
||||
isChecked: true,
|
||||
type: 'ProductElement'
|
||||
}
|
||||
|
||||
// 设置 minioUrl
|
||||
imageItem.minioUrl =
|
||||
productData.miniourl || getMinioUrl(imageItem.url || imageItem.imgUrl)
|
||||
|
||||
// 更新首帧产品列表,移除已选择的项
|
||||
const productIndex = firstFrameProductList.value.findIndex(
|
||||
(item: any) => item.id === productData.id
|
||||
)
|
||||
if (productIndex >= 0) {
|
||||
firstFrameProductList.value.splice(productIndex, 1)
|
||||
}
|
||||
|
||||
// 首尾帧模式,添加到 firstFrameList
|
||||
// 先取消其他项的选中状态
|
||||
firstFrameList.value.forEach((listItem: any) => {
|
||||
listItem.isChecked = false
|
||||
})
|
||||
// 检查是否已存在,如果存在则更新,否则添加
|
||||
const existingIndex = firstFrameList.value.findIndex(
|
||||
(item: any) => item.id === productData.id
|
||||
)
|
||||
if (existingIndex >= 0) {
|
||||
Object.assign(firstFrameList.value[existingIndex], imageItem)
|
||||
} else {
|
||||
imageItem.frameType = 'first'
|
||||
firstFrameList.value.push(imageItem)
|
||||
}
|
||||
|
||||
// 赋值给首帧选中对象
|
||||
data.selectImg = { ...imageItem }
|
||||
} else if (productType.value === 'last') {
|
||||
// 准备图片数据
|
||||
const imageItem: any = {
|
||||
...productData,
|
||||
isChecked: true,
|
||||
type: 'ProductElement',
|
||||
frameType: 'last'
|
||||
}
|
||||
|
||||
// 设置 minioUrl
|
||||
imageItem.minioUrl =
|
||||
productData.miniourl || getMinioUrl(imageItem.url || imageItem.imgUrl)
|
||||
|
||||
// 更新尾帧产品列表,移除已选择的项
|
||||
const productIndex = lastFrameProductList.value.findIndex(
|
||||
(item: any) => item.id === productData.id
|
||||
)
|
||||
if (productIndex >= 0) {
|
||||
lastFrameProductList.value.splice(productIndex, 1)
|
||||
}
|
||||
|
||||
// 先取消其他项的选中状态
|
||||
lastFrameList.value.forEach((listItem: any) => {
|
||||
listItem.isChecked = false
|
||||
})
|
||||
|
||||
// 检查是否已存在,如果存在则更新,否则添加
|
||||
const existingIndex = lastFrameList.value.findIndex(
|
||||
(item: any) => item.id === productData.id
|
||||
)
|
||||
if (existingIndex >= 0) {
|
||||
Object.assign(lastFrameList.value[existingIndex], imageItem)
|
||||
} else {
|
||||
lastFrameList.value.push(imageItem)
|
||||
}
|
||||
|
||||
// 赋值给尾帧选中对象
|
||||
data.lastSelectImg = { ...imageItem }
|
||||
}
|
||||
showProductList.value = false
|
||||
}
|
||||
|
||||
watch(
|
||||
() => store.state.HomeStoreModule.uploadElement.length,
|
||||
(newVal, oldVal) => {
|
||||
@@ -952,6 +1157,20 @@ export default defineComponent({
|
||||
}
|
||||
)
|
||||
|
||||
// 监听 fileList 的变化,如果独立列表为空则重新初始化
|
||||
watch(
|
||||
() => data.fileList,
|
||||
newFileList => {
|
||||
if (firstFrameProductList.value.length === 0 && newFileList?.length > 0) {
|
||||
firstFrameProductList.value = JSON.parse(JSON.stringify(newFileList))
|
||||
}
|
||||
if (lastFrameProductList.value.length === 0 && newFileList?.length > 0) {
|
||||
lastFrameProductList.value = JSON.parse(JSON.stringify(newFileList))
|
||||
}
|
||||
},
|
||||
{ deep: true }
|
||||
)
|
||||
|
||||
watch(
|
||||
() => store.state.HomeStoreModule.lastFrameList,
|
||||
val => {
|
||||
@@ -1039,7 +1258,14 @@ export default defineComponent({
|
||||
setVideoRef,
|
||||
handlePlayMotion,
|
||||
isVideoPlaying,
|
||||
showTips
|
||||
showTips,
|
||||
showProductList,
|
||||
handleClickDesignMask,
|
||||
productType,
|
||||
handleConfirmProduct,
|
||||
productFrameList,
|
||||
showFirstFrameUpload,
|
||||
showLastFrameUpload
|
||||
}
|
||||
},
|
||||
directives: {
|
||||
@@ -1239,6 +1465,15 @@ export default defineComponent({
|
||||
}
|
||||
> .upload_item {
|
||||
border: none;
|
||||
position: relative;
|
||||
.design-mask {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
z-index: 1;
|
||||
}
|
||||
}
|
||||
.control-container {
|
||||
width: 100%;
|
||||
|
||||
Reference in New Issue
Block a user