feat: 弹窗&prompt i18n
This commit is contained in:
4
src/assets/icons/CPause.svg
Normal file
4
src/assets/icons/CPause.svg
Normal file
@@ -0,0 +1,4 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
|
||||
<path fill="currentColor" d="M14 10h-2v12h2zm6 0h-2v12h2z" />
|
||||
<path fill="currentColor" d="M16 4A12 12 0 1 1 4 16A12 12 0 0 1 16 4m0-2a14 14 0 1 0 14 14A14 14 0 0 0 16 2" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 266 B |
BIN
src/assets/images/product/add.png
Normal file
BIN
src/assets/images/product/add.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 5.7 KiB |
BIN
src/assets/images/product/first_frame.png
Normal file
BIN
src/assets/images/product/first_frame.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.1 MiB |
BIN
src/assets/images/product/last_frame.png
Normal file
BIN
src/assets/images/product/last_frame.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.2 MiB |
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<div ref="modalContainer"></div>
|
||||
<a-modal
|
||||
class="prompt-modal generalModel"
|
||||
class="tips-modal generalModel"
|
||||
v-model:visible="showModal"
|
||||
:footer="null"
|
||||
:get-container="() => $refs.modalContainer"
|
||||
@@ -44,17 +44,36 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="title-container">
|
||||
<div class="title">{{ $t('ProductImg.PromptAssit') }}</div>
|
||||
<div class="sub-title">{{ $t('ProductImg.AssitSubTitle') }}</div>
|
||||
<div class="title">{{ $t('poseTransfer.Tips') }}</div>
|
||||
<div class="sub-title">
|
||||
{{ $t('poseTransfer.TipsStart') }}
|
||||
<i class="fi fi-ss-box-open" />
|
||||
{{ $t('poseTransfer.TipsEnd') }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="diliver" />
|
||||
<div class="content">
|
||||
<div class="frame-item">
|
||||
<img class="tips-frame" :src="firstFrame" />
|
||||
<div class="frame-name">{{ $t('poseTransfer.FirstFrame') }}</div>
|
||||
</div>
|
||||
<img :src="add" class="add" />
|
||||
<div class="frame-item">
|
||||
<img class="tips-frame" :src="lastFrame" />
|
||||
<div class="frame-name">{{ $t('poseTransfer.LastFrame') }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</a-modal>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, useTemplateRef } from 'vue'
|
||||
|
||||
import { downloadIamge } from '@/tool/util'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import firstFrame from '@/assets/images/product/first_frame.png'
|
||||
import add from '@/assets/images/product/add.png'
|
||||
import lastFrame from '@/assets/images/product/last_frame.png'
|
||||
|
||||
const { t, locale } = useI18n()
|
||||
|
||||
const modalContainer = useTemplateRef('modalContainer')
|
||||
@@ -66,7 +85,7 @@ const handleClose = () => {
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.prompt-modal {
|
||||
.tips-modal {
|
||||
.title-container {
|
||||
.title {
|
||||
font-size: 3.5rem;
|
||||
@@ -78,5 +97,35 @@ const handleClose = () => {
|
||||
color: #000;
|
||||
}
|
||||
}
|
||||
.diliver {
|
||||
height: 1px;
|
||||
background: #dfdfdf;
|
||||
margin: 4rem 0;
|
||||
}
|
||||
.content {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
column-gap: 4.8rem;
|
||||
.add {
|
||||
width: 3.5rem;
|
||||
height: 3.5rem;
|
||||
}
|
||||
.frame-item {
|
||||
text-align: center;
|
||||
.tips-frame {
|
||||
width: 21.3rem;
|
||||
height: 36.1rem;
|
||||
}
|
||||
.frame-name{
|
||||
margin-top: 1.2rem;
|
||||
font-size: 2.6rem;
|
||||
font-weight: 400;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
:deep(.generalModel .ant-modal-body) {
|
||||
padding: 4rem 11rem 4rem 14rem;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -23,7 +23,11 @@
|
||||
<div class="selectImg">
|
||||
<div class="head">
|
||||
<div class="text">{{ $t('poseTransfer.SelectDesign') }}</div>
|
||||
<i class="fi fi-rs-interrogation tips-icon" />
|
||||
<i
|
||||
v-show="videoType !== 2"
|
||||
class="fi fi-rs-interrogation tips-icon"
|
||||
@click="showTips = true"
|
||||
/>
|
||||
</div>
|
||||
<div class="imgBox" v-mousewheel>
|
||||
<div
|
||||
@@ -59,7 +63,60 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="upload_item item"
|
||||
v-show="!isDesignPage && !(videoType === 3 && fileList.length > 0)"
|
||||
>
|
||||
<div class="upload_file_item">
|
||||
<a-upload
|
||||
key="common"
|
||||
:action="getUploadUrl() + '/api/history/toProductImageElementUpload'"
|
||||
list-type="picture-card"
|
||||
:capture="null"
|
||||
:data="{
|
||||
...upload
|
||||
}"
|
||||
:headers="{ Authorization: token }"
|
||||
:before-upload="beforeUpload"
|
||||
:multiple="!!upload.projectId"
|
||||
accept=".jpg,.png,.jpeg,.bmp"
|
||||
@change="file => fileUploadChange(file)"
|
||||
>
|
||||
<div class="upload_tip_block">
|
||||
<i class="fi fi-br-upload"></i>
|
||||
<!-- <img class="upload_img_icon" src="@/assets/images/homePage/add_file.png"> -->
|
||||
</div>
|
||||
</a-upload>
|
||||
</div>
|
||||
</div>
|
||||
<template v-if="videoType === 3">
|
||||
<div
|
||||
class="upload_item item last_frames"
|
||||
v-show="!isDesignPage && lastFrameList?.length < 1"
|
||||
>
|
||||
<div class="upload_file_item">
|
||||
<a-upload
|
||||
key="lastframes"
|
||||
:action="
|
||||
getUploadUrl() + '/api/history/toProductImageElementUpload'
|
||||
"
|
||||
list-type="picture-card"
|
||||
:capture="null"
|
||||
:data="{
|
||||
...upload
|
||||
}"
|
||||
:headers="{ Authorization: token }"
|
||||
:before-upload="beforeUpload"
|
||||
accept=".jpg,.png,.jpeg,.bmp"
|
||||
@change="file => fileUploadChangeLast(file)"
|
||||
>
|
||||
<div class="upload_tip_block">
|
||||
<i class="fi fi-br-upload"></i>
|
||||
<!-- <img class="upload_img_icon" src="@/assets/images/homePage/add_file.png"> -->
|
||||
</div>
|
||||
</a-upload>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="item lastframe-list"
|
||||
:class="{ active: item.id == selectImg.id }"
|
||||
@@ -94,60 +151,6 @@
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<template v-if="fileList.length < 1">
|
||||
<div class="upload_item item" v-show="!isDesignPage">
|
||||
<div class="upload_file_item">
|
||||
<a-upload
|
||||
key="common"
|
||||
:action="
|
||||
getUploadUrl() + '/api/history/toProductImageElementUpload'
|
||||
"
|
||||
list-type="picture-card"
|
||||
:capture="null"
|
||||
:data="{
|
||||
...upload
|
||||
}"
|
||||
:headers="{ Authorization: token }"
|
||||
:before-upload="beforeUpload"
|
||||
:multiple="!!upload.projectId"
|
||||
accept=".jpg,.png,.jpeg,.bmp"
|
||||
@change="file => fileUploadChange(file)"
|
||||
>
|
||||
<div class="upload_tip_block">
|
||||
<i class="fi fi-br-upload"></i>
|
||||
<!-- <img class="upload_img_icon" src="@/assets/images/homePage/add_file.png"> -->
|
||||
</div>
|
||||
</a-upload>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<template v-if="videoType === 3 && lastFrameList?.length < 1">
|
||||
<div class="upload_item item last_frames" v-show="!isDesignPage">
|
||||
<div class="upload_file_item">
|
||||
<a-upload
|
||||
key="lastframes"
|
||||
:action="
|
||||
getUploadUrl() + '/api/history/toProductImageElementUpload'
|
||||
"
|
||||
list-type="picture-card"
|
||||
:file-list="lastFrameList"
|
||||
:capture="null"
|
||||
:data="{
|
||||
...upload
|
||||
}"
|
||||
:headers="{ Authorization: token }"
|
||||
:before-upload="beforeUpload"
|
||||
accept=".jpg,.png,.jpeg,.bmp"
|
||||
@change="file => fileUploadChangeLast(file)"
|
||||
>
|
||||
<div class="upload_tip_block">
|
||||
<i class="fi fi-br-upload"></i>
|
||||
<!-- <img class="upload_img_icon" src="@/assets/images/homePage/add_file.png"> -->
|
||||
</div>
|
||||
</a-upload>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
<div class="prompt-input-container" v-show="!showMotion">
|
||||
@@ -159,13 +162,13 @@
|
||||
<div class="text">{{ $t('poseTransfer.Selectpose') }}</div>
|
||||
</div>
|
||||
<div class="imgBox" v-mousewheel>
|
||||
<div class="item" v-for="item in poseList" @click="selectPose(item)">
|
||||
<img
|
||||
:src="item.firstFrame"
|
||||
alt=""
|
||||
@mouseenter.stop="gifPlay($event, item)"
|
||||
@mouseleave.stop="gifPause($event, item)"
|
||||
/>
|
||||
<div
|
||||
class="item"
|
||||
v-for="item in poseList"
|
||||
:key="item.id"
|
||||
@click="selectPose(item)"
|
||||
>
|
||||
<video :ref="el => setVideoRef(item.id, el)" :src="item.video" />
|
||||
<div class="btnBox">
|
||||
<div :class="{ active: item.isChecked }">
|
||||
<i class="fi fi-br-check"></i>
|
||||
@@ -174,12 +177,21 @@
|
||||
<div class="control-container">
|
||||
<div class="icon-list">
|
||||
<SvgIcon
|
||||
v-show="!isVideoPlaying(item.id)"
|
||||
class="play-icon"
|
||||
@click="handlePlayMotion(item)"
|
||||
@click.stop="handlePlayMotion(item)"
|
||||
name="CPlay"
|
||||
size="10"
|
||||
color="#fff"
|
||||
/>
|
||||
<SvgIcon
|
||||
v-show="isVideoPlaying(item.id)"
|
||||
class="play-icon pause"
|
||||
@click.stop="handlePlayMotion(item)"
|
||||
name="CPause"
|
||||
size="10"
|
||||
color="#fff"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -267,6 +279,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<Tips v-model:showModal="showTips" />
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
@@ -293,11 +306,14 @@ import { getCookie, setCookie } from '@/tool/cookie'
|
||||
import showViewVideo from '@/tool/mount'
|
||||
import router from '@/router'
|
||||
import promptInput from './promptInput.vue'
|
||||
import Tips from './Tips.vue'
|
||||
import { getFirstFrame, getFirstAndLastFrame } from './prompt'
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
generalDrag,
|
||||
promptInput
|
||||
promptInput,
|
||||
Tips
|
||||
},
|
||||
props: {
|
||||
isDesignPage: {
|
||||
@@ -349,12 +365,14 @@ export default defineComponent({
|
||||
generateTime: null as any,
|
||||
poseList: [],
|
||||
selectPose: null as any,
|
||||
prompt: [
|
||||
{ id: '1', type: 'text', value: '11111' },
|
||||
{ id: '2', type: 'input', value: '222', placeholder: '[请输入内容]' },
|
||||
{ id: '3', type: 'text', value: '333333' },
|
||||
{ id: '4', type: 'input', value: '', placeholder: '[请输入内容]' }
|
||||
]
|
||||
prompt: computed(() => {
|
||||
if (videoType.value === 2) {
|
||||
return getFirstFrame(t)
|
||||
}
|
||||
if (videoType.value === 3) {
|
||||
return getFirstAndLastFrame(t)
|
||||
}
|
||||
})
|
||||
})
|
||||
let speed = reactive({
|
||||
speedList: [
|
||||
@@ -376,6 +394,7 @@ export default defineComponent({
|
||||
value: ''
|
||||
}
|
||||
})
|
||||
const showTips = ref(false)
|
||||
const setIsShowMark: any = inject('setIsShowMark')
|
||||
const createProbject: any = inject('createProbject', () => {})
|
||||
const dataDom = reactive({
|
||||
@@ -431,24 +450,52 @@ export default defineComponent({
|
||||
if (data.poseList.length == 0) {
|
||||
getPoseList()
|
||||
}
|
||||
// setCloudImg()
|
||||
}
|
||||
// const setCloudImg = ()=>{
|
||||
// let arr = store.state.UploadFilesModule.cloudList
|
||||
// let list = JSON.parse(JSON.stringify(arr.poseTransfer)) || []
|
||||
// list.forEach((item:any)=>{
|
||||
// item.url = item.firstFrameUrl
|
||||
// data.noLikeList.unshift(item)
|
||||
// })
|
||||
// store.commit('clearCloudList','poseTransfer')
|
||||
// }
|
||||
const gifPlay = (e: any, item: any) => {
|
||||
e.target.src = item.gif //使用gif图片
|
||||
// 存储视频元素的引用
|
||||
const videoRefs = ref<Record<number, HTMLVideoElement | null>>({})
|
||||
// 存储视频播放状态
|
||||
const videoPlayingStates = ref<Record<number, boolean>>({})
|
||||
|
||||
// 设置视频 ref
|
||||
const setVideoRef = (id: number, el: HTMLVideoElement | null) => {
|
||||
if (el) {
|
||||
videoRefs.value[id] = el
|
||||
// 初始化播放状态
|
||||
videoPlayingStates.value[id] = !el.paused
|
||||
// 监听播放事件
|
||||
el.addEventListener('play', () => {
|
||||
videoPlayingStates.value[id] = true
|
||||
})
|
||||
// 监听暂停事件
|
||||
el.addEventListener('pause', () => {
|
||||
videoPlayingStates.value[id] = false
|
||||
})
|
||||
// 监听结束事件
|
||||
el.addEventListener('ended', () => {
|
||||
videoPlayingStates.value[id] = false
|
||||
})
|
||||
}
|
||||
}
|
||||
const gifPause = (e: any, item: any) => {
|
||||
e.target.src = item.firstFrame //静态图片
|
||||
|
||||
// 检查视频是否正在播放
|
||||
const isVideoPlaying = (id: number) => {
|
||||
return videoPlayingStates.value[id] || false
|
||||
}
|
||||
const handlePlayMotion = item => {}
|
||||
|
||||
// 播放视频
|
||||
const handlePlayMotion = (item: any) => {
|
||||
const video = videoRefs.value[item.id]
|
||||
if (video) {
|
||||
if (video.paused) {
|
||||
video.play().catch(err => {
|
||||
console.error('播放视频失败:', err)
|
||||
})
|
||||
} else {
|
||||
video.pause()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const getPoseList = () => {
|
||||
Https.axiosGet(Https.httpUrls.getAllPose).then(rv => {
|
||||
rv[0].isChecked = true
|
||||
@@ -485,10 +532,7 @@ export default defineComponent({
|
||||
}
|
||||
})
|
||||
})
|
||||
.then(() => {})
|
||||
.catch(() => {
|
||||
return
|
||||
})
|
||||
|
||||
data.isGenerate = true
|
||||
// data.remGenerateTime = setTimeout(()=>{
|
||||
// },10000)
|
||||
@@ -513,36 +557,35 @@ export default defineComponent({
|
||||
const lastFrameProductImage = data.lastSelectImg.minioUrl
|
||||
value.lastFrameProductImage = lastFrameProductImage
|
||||
}
|
||||
console.table('videoType:', videoType.value, '请求参数', value)
|
||||
// Https.axiosPost(Https.httpUrls.poseTransform, value)
|
||||
// .then(rv => {
|
||||
// data.remGenerate = true
|
||||
// data.noLikeList.unshift({
|
||||
// taskId: rv.taskId,
|
||||
// parentId: data.selectImg.parentId
|
||||
// })
|
||||
// setGenerate(rv.taskId)
|
||||
// })
|
||||
// .catch((res: any) => {
|
||||
// data.isGenerate = false
|
||||
// clearInterval(data.remGenerateTime)
|
||||
// data.remGenerate = false
|
||||
// if (res.errCode === 2) {
|
||||
// Modal.confirm({
|
||||
// title: res.errMsg,
|
||||
// icon: createVNode(ExclamationCircleOutlined),
|
||||
// okText: 'Yes',
|
||||
// cancelText: 'No',
|
||||
// mask: false,
|
||||
// zIndex: 99999,
|
||||
// centered: true,
|
||||
// onOk() {
|
||||
// store.commit('setUpgradePlan', true)
|
||||
// },
|
||||
// onCancel() {}
|
||||
// })
|
||||
// }
|
||||
// })
|
||||
Https.axiosPost(Https.httpUrls.poseTransform, value)
|
||||
.then(rv => {
|
||||
data.remGenerate = true
|
||||
data.noLikeList.unshift({
|
||||
taskId: rv.taskId,
|
||||
parentId: data.selectImg.parentId
|
||||
})
|
||||
setGenerate(rv.taskId)
|
||||
})
|
||||
.catch((res: any) => {
|
||||
data.isGenerate = false
|
||||
clearInterval(data.remGenerateTime)
|
||||
data.remGenerate = false
|
||||
if (res.errCode === 2) {
|
||||
Modal.confirm({
|
||||
title: res.errMsg,
|
||||
icon: createVNode(ExclamationCircleOutlined),
|
||||
okText: 'Yes',
|
||||
cancelText: 'No',
|
||||
mask: false,
|
||||
zIndex: 99999,
|
||||
centered: true,
|
||||
onOk() {
|
||||
store.commit('setUpgradePlan', true)
|
||||
},
|
||||
onCancel() {}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
const setGenerate = (dataList: any) => {
|
||||
let list: any = [dataList]
|
||||
@@ -646,7 +689,6 @@ export default defineComponent({
|
||||
if (!isLt2M) {
|
||||
message.info(useI18n().t('MoodboardUpload.jsContent4'))
|
||||
}
|
||||
console.log(data?.upload, isSelectObject)
|
||||
if (!data?.upload?.projectId && !isSelectObject) {
|
||||
isSelectObject = true
|
||||
await createProbject()
|
||||
@@ -654,7 +696,6 @@ export default defineComponent({
|
||||
return (isJpgOrPng && isLt2M && data.upload.projectId) || Upload.LIST_IGNORE
|
||||
}
|
||||
const fileUploadChange = (value: any) => {
|
||||
console.log('普通上传或上传首帧---', value)
|
||||
let file = value.file
|
||||
let bor = true
|
||||
if (file.status === 'done') {
|
||||
@@ -689,7 +730,6 @@ export default defineComponent({
|
||||
}
|
||||
}
|
||||
const fileUploadChangeLast = (value: any) => {
|
||||
console.log('上传尾帧 ---', value)
|
||||
const file = value.file
|
||||
if (file.status === 'done') {
|
||||
const res = JSON.parse(file.xhr.response)
|
||||
@@ -767,7 +807,6 @@ export default defineComponent({
|
||||
if (props.isDesignPage) {
|
||||
emit('unLike', item)
|
||||
}
|
||||
console.log(props.isDesignPage)
|
||||
}
|
||||
Https.axiosPost(Https.httpUrls.poselikeOrDisike, {}, { params: value })
|
||||
.then(rv => {
|
||||
@@ -883,9 +922,9 @@ export default defineComponent({
|
||||
const videoType = ref(2)
|
||||
const showMotion = computed(() => videoType.value === 1)
|
||||
const options = ref([
|
||||
{ vlaue: 2, label: 'First frame' },
|
||||
{ value: 3, label: 'First and last frames' },
|
||||
{ value: 1, label: 'First frame and skeleton' }
|
||||
{ vlaue: 2, label: t('poseTransfer.FirstFrame') },
|
||||
{ value: 3, label: t('poseTransfer.FirstAndLastFrames') },
|
||||
{ value: 1, label: t('poseTransfer.FirstFrameAndSkeleton') }
|
||||
])
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
@@ -904,8 +943,6 @@ export default defineComponent({
|
||||
selectImgItem,
|
||||
setSize,
|
||||
setItemPosition,
|
||||
gifPlay,
|
||||
gifPause,
|
||||
getgenerate,
|
||||
getUploadUrl,
|
||||
beforeUpload,
|
||||
@@ -921,7 +958,11 @@ export default defineComponent({
|
||||
locale,
|
||||
videoType,
|
||||
options,
|
||||
showMotion
|
||||
showMotion,
|
||||
setVideoRef,
|
||||
handlePlayMotion,
|
||||
isVideoPlaying,
|
||||
showTips
|
||||
}
|
||||
},
|
||||
directives: {
|
||||
@@ -1139,7 +1180,7 @@ export default defineComponent({
|
||||
.icon-list {
|
||||
height: 50%;
|
||||
width: calc(100% - 1.6rem);
|
||||
border-top: 1px solid #fff;
|
||||
// border-top: 1px solid #fff;
|
||||
display: flex;
|
||||
box-sizing: border-box;
|
||||
justify-content: flex-start;
|
||||
|
||||
30
src/component/home/tools/poseTransfer/prompt.ts
Normal file
30
src/component/home/tools/poseTransfer/prompt.ts
Normal file
@@ -0,0 +1,30 @@
|
||||
export const getFirstAndLastFrame = (t: (key: string) => string) => [
|
||||
{ id: '1', type: 'text', value: t('poseTransfer.firstAndLastFrameText1') },
|
||||
{
|
||||
id: '2',
|
||||
type: 'input',
|
||||
value: '',
|
||||
placeholder: t('poseTransfer.firstAndLastFramePlaceholder1')
|
||||
},
|
||||
{ id: '3', type: 'text', value: t('poseTransfer.firstAndLastFrameText2') },
|
||||
{ id: '4', type: 'input', value: '', placeholder: t('poseTransfer.firstAndLastFramePlaceholder2') },
|
||||
{
|
||||
id: '5',
|
||||
type: 'text',
|
||||
value: t('poseTransfer.firstAndLastFrameText3')
|
||||
}
|
||||
]
|
||||
|
||||
export const getFirstFrame = (t: (key: string) => string) => [
|
||||
{ id: '1', type: 'text', value: t('poseTransfer.firstFrameText1') },
|
||||
{ id: '2', type: 'input', value: '', placeholder: t('poseTransfer.firstFramePlaceholder1') },
|
||||
{ id: '3', type: 'text', value: t('poseTransfer.firstFrameText2') },
|
||||
{ id: '4', type: 'input', value: '', placeholder: t('poseTransfer.firstFramePlaceholder2') },
|
||||
{ id: '5', type: 'text', value: t('poseTransfer.firstFrameText3') },
|
||||
{ id: '6', type: 'input', value: '', placeholder: t('poseTransfer.firstFramePlaceholder3') },
|
||||
{ id: '7', type: 'text', value: t('poseTransfer.firstFrameText4') },
|
||||
{ id: '8', type: 'input', value: '', placeholder: t('poseTransfer.firstFramePlaceholder4') },
|
||||
{ id: '9', type: 'text', value: t('poseTransfer.firstFrameText5') },
|
||||
{ id: '10', type: 'input', value: '', placeholder: t('poseTransfer.firstFramePlaceholder5') },
|
||||
{ id: '11', type: 'text', value: t('poseTransfer.firstFrameText6') }
|
||||
]
|
||||
@@ -1,5 +1,5 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted, reactive, toRefs, nextTick } from "vue";
|
||||
import { ref, onMounted, reactive, toRefs, nextTick, useTemplateRef, watch } from "vue";
|
||||
|
||||
interface ContentItem {
|
||||
id: string;
|
||||
@@ -336,18 +336,62 @@ const initPlaceholders = () => {
|
||||
}
|
||||
|
||||
const getFullText = () => {
|
||||
return content.value.map(item => {
|
||||
if (item.type === 'text') {
|
||||
return item.value
|
||||
} else {
|
||||
// 只获取用户实际输入的值,即使为空也显示空括号
|
||||
return `${item.value}`
|
||||
}
|
||||
}).join('')
|
||||
if(assistModel.value){
|
||||
return content.value.map(item => {
|
||||
if (item.type === 'text') {
|
||||
return item.value
|
||||
} else {
|
||||
// 如果 input 没有输入 value,则用 placeholder 填充,并去掉首尾的 []
|
||||
if (item.value) {
|
||||
return ` ${item.value} `
|
||||
} else if (item.placeholder) {
|
||||
let placeholderText = item.placeholder
|
||||
// 去掉首尾的 []
|
||||
if (placeholderText.startsWith('[') && placeholderText.endsWith(']')) {
|
||||
placeholderText = placeholderText.slice(1, -1)
|
||||
}
|
||||
return ` ${placeholderText} `
|
||||
}
|
||||
return ''
|
||||
}
|
||||
}).join('')
|
||||
}
|
||||
return textareaValue.value
|
||||
}
|
||||
|
||||
const textareaValue = ref('')
|
||||
const assistModel= ref(false)
|
||||
const handleClickAssistBtn = () => {
|
||||
assistModel.value = !assistModel.value
|
||||
}
|
||||
|
||||
// 监听 assistModel 变化,切换到 textarea 模式时调整高度
|
||||
watch(assistModel, (newVal) => {
|
||||
if (!newVal) {
|
||||
// 切换到 textarea 模式
|
||||
nextTick(() => {
|
||||
handleInputResize()
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
const textareaRef = useTemplateRef<HTMLTextAreaElement>('textareaRef')
|
||||
const handleInputResize = () => {
|
||||
const textarea = textareaRef.value
|
||||
if (textarea) {
|
||||
textarea.style.height = 'auto'
|
||||
textarea.style.height = textarea.scrollHeight + 'px'
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
initPlaceholders()
|
||||
// 如果初始状态是 textarea 模式,设置初始高度
|
||||
if (!assistModel.value) {
|
||||
nextTick(() => {
|
||||
handleInputResize()
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
defineExpose({
|
||||
@@ -357,16 +401,42 @@ defineExpose({
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div ref="editableArea" class="promptInput" @keydown="handleKeydown" @click="handleContainerClick">
|
||||
<div v-show="!assistModel" class="textarea-container">
|
||||
<textarea
|
||||
class="area"
|
||||
v-model="textareaValue"
|
||||
ref="textareaRef"
|
||||
@input="handleInputResize"
|
||||
:placeholder="$t('poseTransfer.PormptPlaceholder')"
|
||||
/>
|
||||
<div class="asistant-btn" @click="handleClickAssistBtn">
|
||||
<i class="fi fi-bs-magic-wand asistant-icon"></i>
|
||||
<span>{{ $t('ProductImg.PromptAssit') }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div v-show="assistModel" ref="editableArea" class="promptInput" @keydown="handleKeydown" @click="handleContainerClick">
|
||||
<template v-for="(item, index) in content" :key="item.id">
|
||||
<span v-if="item.type === 'text'" class="text-field" :data-index="index" contenteditable="plaintext-only">{{
|
||||
item.value }}</span>
|
||||
<span
|
||||
v-if="item.type === 'text'"
|
||||
class="text-field"
|
||||
:data-index="index"
|
||||
contenteditable="plaintext-only">
|
||||
{{item.value }}
|
||||
</span>
|
||||
|
||||
<span v-else class="input-field" :data-index="index">
|
||||
<span class="input-content" contenteditable="plaintext-only" @input="(e) => handleInputChange(index, e)"
|
||||
@keydown="(e) => handleInputKeydown(e, index)" @blur="() => handleInputBlur(index)"></span>
|
||||
<span
|
||||
class="input-content"
|
||||
contenteditable="plaintext-only"
|
||||
@input="(e) => handleInputChange(index, e)"
|
||||
@keydown="(e) => handleInputKeydown(e, index)"
|
||||
@blur="() => handleInputBlur(index)"></span>
|
||||
</span>
|
||||
</template>
|
||||
<div class="asistant-btn" @click="handleClickAssistBtn">
|
||||
<i class="fi fi-bs-magic-wand asistant-icon"></i>
|
||||
<span>{{ $t('ProductImg.PromptAssit') }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -387,6 +457,9 @@ defineExpose({
|
||||
white-space: pre-wrap;
|
||||
user-select: text;
|
||||
cursor: text;
|
||||
position: relative;
|
||||
padding-bottom: 4rem;
|
||||
|
||||
|
||||
.text-field {
|
||||
display: inline;
|
||||
@@ -399,8 +472,8 @@ defineExpose({
|
||||
|
||||
.input-field {
|
||||
display: inline-block;
|
||||
background: #e3f2fd;
|
||||
border: 1px solid #bbdefb;
|
||||
// background: #e3f2fd;
|
||||
// border: 1px solid #bbdefb;
|
||||
margin: 0 .2rem;
|
||||
padding: .2rem 1rem;
|
||||
font-size: 1.8rem;
|
||||
@@ -408,15 +481,62 @@ defineExpose({
|
||||
|
||||
.input-content {
|
||||
outline: none;
|
||||
color: #1976d2;
|
||||
display: inline-block;
|
||||
min-width: 2rem;
|
||||
|
||||
&.has-placeholder {
|
||||
color: #95a5a6;
|
||||
font-style: italic;
|
||||
color: #b9b9b9;
|
||||
// font-style: italic;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.textarea-container{
|
||||
position: relative;
|
||||
border-radius: 10px;
|
||||
border: 2px solid #dcdfe6;
|
||||
padding: 1.5rem 1.5rem 3rem;
|
||||
height: auto;
|
||||
.area{
|
||||
width: 100%;
|
||||
min-height: 12rem;
|
||||
height: auto;
|
||||
background: white;
|
||||
line-height: 1.6;
|
||||
outline: none;
|
||||
white-space: pre-wrap;
|
||||
user-select: text;
|
||||
cursor: text;
|
||||
position: relative;
|
||||
// padding-bottom: 4rem;
|
||||
resize: none;
|
||||
border: none;
|
||||
}
|
||||
}
|
||||
|
||||
.asistant-btn {
|
||||
height: 2.3rem;
|
||||
padding: 0 0.6rem;
|
||||
font-size: 1rem;
|
||||
font-weight: 400;
|
||||
color: #313131;
|
||||
position: absolute;
|
||||
bottom: 1.3rem;
|
||||
left: 1.3rem;
|
||||
display: flex;
|
||||
column-gap: 0.3rem;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
background-color: #f2f2f2;
|
||||
border: 1px solid #dfdfdf;
|
||||
border-radius: 0.5rem;
|
||||
cursor: pointer;
|
||||
.asistant-icon {
|
||||
font-size: 1rem;
|
||||
margin-right: 0;
|
||||
width: initial;
|
||||
// margin-top: -0.2rem;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -311,8 +311,29 @@ export default {
|
||||
'专业产品摄影:服装以自然形态展示,无模特。采用影棚灯光。保留精确细节——图案、色彩、质感、装饰元素。', // 上传线稿,不带模特
|
||||
VideoType: '视频类型',
|
||||
FirstFrame: '首帧',
|
||||
LastFrame:'尾帧',
|
||||
FirstAndLastFrames: '首帧和尾帧',
|
||||
FirstFrameAndSkeleton: '首帧和骨架'
|
||||
FirstFrameAndSkeleton: '首帧和骨架',
|
||||
Tips: '提示',
|
||||
TipsStart: '你可以使用"',
|
||||
TipsEnd: '编辑产品图"来生成首帧或尾帧图片',
|
||||
PormptPlaceholder:'输入你想描述的场景...',
|
||||
firstAndLastFrameText1: '随着视频的进行,使用',
|
||||
firstAndLastFramePlaceholder1: '[相机运动]',
|
||||
firstAndLastFrameText2: '来跟随动作,在',
|
||||
firstAndLastFramePlaceholder2: '[光线]',
|
||||
firstAndLastFrameText3: '下,保持模型身份、风格和服装在所有帧中的完全一致性。',
|
||||
firstFrameText1: '场景设置为',
|
||||
firstFramePlaceholder1: '[场景]',
|
||||
firstFrameText2: ',模特做',
|
||||
firstFramePlaceholder2: '[拍摄动作]',
|
||||
firstFrameText3: ',使用',
|
||||
firstFramePlaceholder3: '[相机运动]',
|
||||
firstFrameText4: '结合',
|
||||
firstFramePlaceholder4: '[相机运动]',
|
||||
firstFrameText5: ',在',
|
||||
firstFramePlaceholder5: '[光线]',
|
||||
firstFrameText6: '下,完善整体视觉效果。'
|
||||
},
|
||||
LibraryPage: {
|
||||
library: '收藏',
|
||||
|
||||
@@ -321,8 +321,29 @@ export default {
|
||||
'Professional product photo: garment displayed with natural shape, no model. Studio lighting. Preserve exact details - patterns, colors, textures, embellishments.', // 上传线稿,不带模特
|
||||
VideoType: 'Video Type',
|
||||
FirstFrame: 'First frame',
|
||||
LastFrame: 'Last frame',
|
||||
FirstAndLastFrames: 'First and last frames',
|
||||
FirstFrameAndSkeleton: 'First frame and skeleton',
|
||||
Tips: 'Tips',
|
||||
TipsStart: 'User can use ‘ ',
|
||||
TipsEnd: 'Edit Product Image ’ to generate first or last frames.',
|
||||
PormptPlaceholder: 'Enter the scene you want to describe...',
|
||||
firstAndLastFrameText1: 'As the video progresses, use',
|
||||
firstAndLastFramePlaceholder1: '[Camera Movement]',
|
||||
firstAndLastFrameText2: 'to follow the motion, under',
|
||||
firstAndLastFramePlaceholder2: '[Light]',
|
||||
firstAndLastFrameText3: ', maintaining full consistency of model identity, styling, and outfit across all frames.',
|
||||
firstFrameText1: 'Set the',
|
||||
firstFramePlaceholder1: '[Scene]',
|
||||
firstFrameText2: ', where the model',
|
||||
firstFramePlaceholder2: '[Motion]',
|
||||
firstFrameText3: 'use a',
|
||||
firstFramePlaceholder3: '[Camera Movement]',
|
||||
firstFrameText4: 'combined with a',
|
||||
firstFramePlaceholder4: '[Camera Movement]',
|
||||
firstFrameText5: ', under',
|
||||
firstFramePlaceholder5: '[Light]',
|
||||
firstFrameText6: ', complementing the look.'
|
||||
},
|
||||
LibraryPage: {
|
||||
library: 'Library',
|
||||
|
||||
@@ -73,18 +73,14 @@ const HomeStoreModule : Module<DesignDetail,RootState> = {
|
||||
state.showSketchList = data
|
||||
},
|
||||
setPoseTransferLastFrameList(state,data){
|
||||
console.log('设置尾帧store-------------')
|
||||
// 支持两种方式:set 替换整个列表;add/删除与 uploadElement 一致
|
||||
if(data.str === 'set'){
|
||||
state.poseTransfer.lastFrameList = data.list || []
|
||||
state.lastFrameList = data.list || []
|
||||
return
|
||||
}
|
||||
if(data.str === 'add'){
|
||||
state.poseTransfer.lastFrameList.unshift(...(data.list || []))
|
||||
}else{
|
||||
state.poseTransfer.lastFrameList.splice(data.index,1)
|
||||
state.lastFrameList = []
|
||||
}
|
||||
},
|
||||
},
|
||||
setDesignCollectionList(state,data){
|
||||
state.designCollectionList = data
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user