style: prompt input样式修改
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -1505,7 +1505,7 @@ export default defineComponent({
|
|||||||
}
|
}
|
||||||
> .head {
|
> .head {
|
||||||
color: #000;
|
color: #000;
|
||||||
font-weight: 600;
|
font-weight: 500;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
column-gap: 1rem;
|
column-gap: 1rem;
|
||||||
@@ -1526,6 +1526,7 @@ export default defineComponent({
|
|||||||
font-size: 1.8rem;
|
font-size: 1.8rem;
|
||||||
color: #000;
|
color: #000;
|
||||||
margin-bottom: 1.4rem;
|
margin-bottom: 1.4rem;
|
||||||
|
font-weight: 500;
|
||||||
}
|
}
|
||||||
> .poses {
|
> .poses {
|
||||||
margin-top: 3rem;
|
margin-top: 3rem;
|
||||||
@@ -1627,6 +1628,7 @@ export default defineComponent({
|
|||||||
}
|
}
|
||||||
.video-type-container {
|
.video-type-container {
|
||||||
margin-bottom: 4rem;
|
margin-bottom: 4rem;
|
||||||
|
font-weight: 500;
|
||||||
.title {
|
.title {
|
||||||
font-size: 1.8rem;
|
font-size: 1.8rem;
|
||||||
color: #000;
|
color: #000;
|
||||||
|
|||||||
@@ -1,507 +1,518 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, onMounted, reactive, toRefs, nextTick, useTemplateRef, watch } from "vue";
|
import { ref, onMounted, reactive, toRefs, nextTick, useTemplateRef } from 'vue'
|
||||||
|
|
||||||
interface ContentItem {
|
interface ContentItem {
|
||||||
id: string;
|
id: string
|
||||||
type: 'text' | 'input';
|
type: 'text' | 'input'
|
||||||
value: string;
|
value: string
|
||||||
placeholder?: string;
|
placeholder?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
content: {
|
content: {
|
||||||
type: Array,
|
type: Array,
|
||||||
default: () => []
|
default: () => []
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
const data = reactive({
|
const data = reactive({
|
||||||
// content: [
|
// content: [
|
||||||
// { id: '1', type: 'text', value: '11111' },
|
// { id: '1', type: 'text', value: '11111' },
|
||||||
// { id: '2', type: 'input', value: '222', placeholder: '[请输入内容]' },
|
// { id: '2', type: 'input', value: '222', placeholder: '[请输入内容]' },
|
||||||
// { id: '3', type: 'text', value: '333333' },
|
// { id: '3', type: 'text', value: '333333' },
|
||||||
// { id: '4', type: 'input', value: '', placeholder: '[请输入内容]' }
|
// { id: '4', type: 'input', value: '', placeholder: '[请输入内容]' }
|
||||||
// ] as ContentItem[]
|
// ] as ContentItem[]
|
||||||
content: props.content
|
content: props.content
|
||||||
})
|
})
|
||||||
|
|
||||||
const editableArea = ref<HTMLElement>()
|
const editableArea = ref<HTMLElement>()
|
||||||
const { content } = toRefs(data)
|
const { content } = toRefs(data)
|
||||||
|
|
||||||
const cursorState = ref({
|
const cursorState = ref({
|
||||||
isContainerClick: false
|
isContainerClick: false
|
||||||
})
|
})
|
||||||
|
|
||||||
// 检查并删除末尾的空文本框
|
// 检查并删除末尾的空文本框
|
||||||
const removeLastEmptyTextIfNeeded = () => {
|
const removeLastEmptyTextIfNeeded = () => {
|
||||||
const lastItem = content.value[content.value.length - 1]
|
const lastItem = content.value[content.value.length - 1]
|
||||||
if (lastItem && lastItem.type === 'text' && lastItem.value === '') {
|
if (lastItem && lastItem.type === 'text' && lastItem.value === '') {
|
||||||
content.value.pop()
|
content.value.pop()
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// 确保末尾有空文本框
|
// 确保末尾有空文本框
|
||||||
const ensureEmptyTextAtEnd = () => {
|
const ensureEmptyTextAtEnd = () => {
|
||||||
const lastItem = content.value[content.value.length - 1]
|
const lastItem = content.value[content.value.length - 1]
|
||||||
if (!lastItem || lastItem.type !== 'text' || lastItem.value !== '') {
|
if (!lastItem || lastItem.type !== 'text' || lastItem.value !== '') {
|
||||||
const newItem: ContentItem = {
|
const newItem: ContentItem = {
|
||||||
id: Date.now().toString(),
|
id: Date.now().toString(),
|
||||||
type: 'text',
|
type: 'text',
|
||||||
value: ''
|
value: ''
|
||||||
}
|
}
|
||||||
content.value.push(newItem)
|
content.value.push(newItem)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// 元素信息获取
|
// 元素信息获取
|
||||||
const getCurrentElementInfo = () => {
|
const getCurrentElementInfo = () => {
|
||||||
const selection = window.getSelection()
|
const selection = window.getSelection()
|
||||||
if (!selection || selection.rangeCount === 0) return null
|
if (!selection || selection.rangeCount === 0) return null
|
||||||
|
|
||||||
const range = selection.getRangeAt(0)
|
const range = selection.getRangeAt(0)
|
||||||
const node = range.startContainer
|
const node = range.startContainer
|
||||||
let element: HTMLElement | null = null
|
let element: HTMLElement | null = null
|
||||||
|
|
||||||
if (node.nodeType === Node.TEXT_NODE) {
|
if (node.nodeType === Node.TEXT_NODE) {
|
||||||
element = (node as Text).parentElement
|
element = (node as Text).parentElement
|
||||||
} else {
|
} else {
|
||||||
element = node as HTMLElement
|
element = node as HTMLElement
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!element) return null
|
if (!element) return null
|
||||||
|
|
||||||
let index = -1
|
let index = -1
|
||||||
let type: 'text' | 'input' = 'text'
|
let type: 'text' | 'input' = 'text'
|
||||||
let isAtStart = false
|
let isAtStart = false
|
||||||
let isAtEnd = false
|
let isAtEnd = false
|
||||||
|
|
||||||
if (element.classList.contains('text-field')) {
|
if (element.classList.contains('text-field')) {
|
||||||
index = parseInt(element.getAttribute('data-index') || '-1')
|
index = parseInt(element.getAttribute('data-index') || '-1')
|
||||||
type = 'text'
|
type = 'text'
|
||||||
const textContent = element.textContent || ''
|
const textContent = element.textContent || ''
|
||||||
isAtStart = range.startOffset === 0
|
isAtStart = range.startOffset === 0
|
||||||
isAtEnd = range.startOffset === textContent.length
|
isAtEnd = range.startOffset === textContent.length
|
||||||
} else if (element.classList.contains('input-content')) {
|
} else if (element.classList.contains('input-content')) {
|
||||||
const parent = element.parentElement
|
const parent = element.parentElement
|
||||||
index = parseInt(parent?.getAttribute('data-index') || '-1')
|
index = parseInt(parent?.getAttribute('data-index') || '-1')
|
||||||
type = 'input'
|
type = 'input'
|
||||||
|
|
||||||
const item = content.value[index]
|
const item = content.value[index]
|
||||||
if (element.classList.contains('has-placeholder')) {
|
if (element.classList.contains('has-placeholder')) {
|
||||||
// placeholder状态下,光标在任意位置都认为是"在元素内"
|
// placeholder状态下,光标在任意位置都认为是"在元素内"
|
||||||
isAtStart = range.startOffset === 0
|
isAtStart = range.startOffset === 0
|
||||||
isAtEnd = true
|
isAtEnd = true
|
||||||
} else {
|
} else {
|
||||||
// 正常内容状态
|
// 正常内容状态
|
||||||
const textContent = item.value
|
const textContent = item.value
|
||||||
isAtStart = range.startOffset === 0
|
isAtStart = range.startOffset === 0
|
||||||
isAtEnd = range.startOffset === textContent.length
|
isAtEnd = range.startOffset === textContent.length
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return { index, type, element, isAtStart, isAtEnd }
|
return { index, type, element, isAtStart, isAtEnd }
|
||||||
}
|
}
|
||||||
|
|
||||||
//光标设置
|
//光标设置
|
||||||
const setCursorToElement = (element: HTMLElement, position: 'start' | 'end') => {
|
const setCursorToElement = (element: HTMLElement, position: 'start' | 'end') => {
|
||||||
const selection = window.getSelection()
|
const selection = window.getSelection()
|
||||||
const range = document.createRange()
|
const range = document.createRange()
|
||||||
|
|
||||||
if (element.classList.contains('input-content') && element.classList.contains('has-placeholder')) {
|
if (
|
||||||
// placeholder状态的特殊处理
|
element.classList.contains('input-content') &&
|
||||||
range.selectNodeContents(element)
|
element.classList.contains('has-placeholder')
|
||||||
range.collapse(position === 'start')
|
) {
|
||||||
} else if (element.childNodes.length > 0) {
|
// placeholder状态的特殊处理
|
||||||
const targetNode = position === 'start' ? element.firstChild! : element.lastChild!
|
range.selectNodeContents(element)
|
||||||
if (targetNode.nodeType === Node.TEXT_NODE) {
|
range.collapse(position === 'start')
|
||||||
const offset = position === 'start' ? 0 : (targetNode.textContent || '').length
|
} else if (element.childNodes.length > 0) {
|
||||||
range.setStart(targetNode, offset)
|
const targetNode = position === 'start' ? element.firstChild! : element.lastChild!
|
||||||
range.setEnd(targetNode, offset)
|
if (targetNode.nodeType === Node.TEXT_NODE) {
|
||||||
} else {
|
const offset = position === 'start' ? 0 : (targetNode.textContent || '').length
|
||||||
range.selectNodeContents(element)
|
range.setStart(targetNode, offset)
|
||||||
range.collapse(position === 'start')
|
range.setEnd(targetNode, offset)
|
||||||
}
|
} else {
|
||||||
} else {
|
range.selectNodeContents(element)
|
||||||
range.selectNodeContents(element)
|
range.collapse(position === 'start')
|
||||||
range.collapse(position === 'start')
|
}
|
||||||
}
|
} else {
|
||||||
|
range.selectNodeContents(element)
|
||||||
|
range.collapse(position === 'start')
|
||||||
|
}
|
||||||
|
|
||||||
selection?.removeAllRanges()
|
selection?.removeAllRanges()
|
||||||
selection?.addRange(range)
|
selection?.addRange(range)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 键盘事件处理
|
// 键盘事件处理
|
||||||
const handleKeydown = (event: KeyboardEvent) => {
|
const handleKeydown = (event: KeyboardEvent) => {
|
||||||
const elementInfo = getCurrentElementInfo()
|
const elementInfo = getCurrentElementInfo()
|
||||||
if (!elementInfo) return
|
if (!elementInfo) return
|
||||||
|
|
||||||
const { index, type, isAtStart, isAtEnd } = elementInfo
|
const { index, type, isAtStart, isAtEnd } = elementInfo
|
||||||
|
|
||||||
switch (event.key) {
|
switch (event.key) {
|
||||||
case 'Backspace':
|
case 'Backspace':
|
||||||
if (isAtStart && index > 0) {
|
if (isAtStart && index > 0) {
|
||||||
event.preventDefault()
|
event.preventDefault()
|
||||||
handleCrossElementDelete(index)
|
handleCrossElementDelete(index)
|
||||||
} else if (type === 'input' && elementInfo.element?.classList.contains('has-placeholder')) {
|
} else if (
|
||||||
event.preventDefault()
|
type === 'input' &&
|
||||||
}
|
elementInfo.element?.classList.contains('has-placeholder')
|
||||||
// 其他情况让浏览器正常处理删除
|
) {
|
||||||
break
|
event.preventDefault()
|
||||||
|
}
|
||||||
|
// 其他情况让浏览器正常处理删除
|
||||||
|
break
|
||||||
|
|
||||||
case 'ArrowLeft':
|
case 'ArrowLeft':
|
||||||
if (isAtStart && index > 0) {
|
if (isAtStart && index > 0) {
|
||||||
event.preventDefault()
|
event.preventDefault()
|
||||||
navigateToElement(index - 1, 'end')
|
navigateToElement(index - 1, 'end')
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
|
|
||||||
case 'ArrowRight':
|
case 'ArrowRight':
|
||||||
if (isAtEnd && index < content.value.length - 1) {
|
if (isAtEnd && index < content.value.length - 1) {
|
||||||
event.preventDefault()
|
event.preventDefault()
|
||||||
navigateToElement(index + 1, 'start')
|
navigateToElement(index + 1, 'start')
|
||||||
} else if (isAtEnd && index === content.value.length - 1) {
|
} else if (isAtEnd && index === content.value.length - 1) {
|
||||||
// 在最后一个元素末尾按右箭头,确保有一个空文本框
|
// 在最后一个元素末尾按右箭头,确保有一个空文本框
|
||||||
ensureEmptyTextAtEnd()
|
ensureEmptyTextAtEnd()
|
||||||
nextTick(() => {
|
nextTick(() => {
|
||||||
navigateToElement(index + 1, 'start')
|
navigateToElement(index + 1, 'start')
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 跨元素删除逻辑
|
// 跨元素删除逻辑
|
||||||
const handleCrossElementDelete = (currentIndex: number) => {
|
const handleCrossElementDelete = (currentIndex: number) => {
|
||||||
const prevIndex = currentIndex - 1
|
const prevIndex = currentIndex - 1
|
||||||
const prevItem = content.value[prevIndex]
|
const prevItem = content.value[prevIndex]
|
||||||
|
|
||||||
if (prevItem.type === 'input') {
|
if (prevItem.type === 'input') {
|
||||||
if (prevItem.value.trim() === '') {
|
if (prevItem.value.trim() === '') {
|
||||||
// 删除空输入框
|
// 删除空输入框
|
||||||
content.value.splice(prevIndex, 1)
|
content.value.splice(prevIndex, 1)
|
||||||
nextTick(() => {
|
nextTick(() => {
|
||||||
// 删除输入框后,先删除末尾的空文本框
|
// 删除输入框后,先删除末尾的空文本框
|
||||||
removeLastEmptyTextIfNeeded()
|
removeLastEmptyTextIfNeeded()
|
||||||
// 然后聚焦到正确的位置
|
// 然后聚焦到正确的位置
|
||||||
if (prevIndex < content.value.length) {
|
if (prevIndex < content.value.length) {
|
||||||
focusElement(prevIndex, 'end')
|
focusElement(prevIndex, 'end')
|
||||||
} else if (content.value.length > 0) {
|
} else if (content.value.length > 0) {
|
||||||
focusElement(content.value.length - 1, 'end')
|
focusElement(content.value.length - 1, 'end')
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
// 删除输入框最后一个字符,但保留输入框
|
// 删除输入框最后一个字符,但保留输入框
|
||||||
const newValue = prevItem.value.slice(0, -1)
|
const newValue = prevItem.value.slice(0, -1)
|
||||||
content.value[prevIndex].value = newValue
|
content.value[prevIndex].value = newValue
|
||||||
updateInputDisplay(prevIndex)
|
updateInputDisplay(prevIndex)
|
||||||
nextTick(() => focusElement(prevIndex, 'end'))
|
nextTick(() => focusElement(prevIndex, 'end'))
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// 文本框:移动到前一个文本框末尾,让浏览器正常删除
|
// 文本框:移动到前一个文本框末尾,让浏览器正常删除
|
||||||
// 先删除末尾的空文本框
|
// 先删除末尾的空文本框
|
||||||
removeLastEmptyTextIfNeeded()
|
removeLastEmptyTextIfNeeded()
|
||||||
focusElement(prevIndex, 'end')
|
focusElement(prevIndex, 'end')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 导航到元素
|
// 导航到元素
|
||||||
const navigateToElement = (targetIndex: number, position: 'start' | 'end') => {
|
const navigateToElement = (targetIndex: number, position: 'start' | 'end') => {
|
||||||
const targetItem = content.value[targetIndex]
|
const targetItem = content.value[targetIndex]
|
||||||
const element = targetItem.type === 'text'
|
const element =
|
||||||
? editableArea.value?.querySelector(`.text-field[data-index="${targetIndex}"]`) as HTMLElement
|
targetItem.type === 'text'
|
||||||
: editableArea.value?.querySelector(`.input-field[data-index="${targetIndex}"] .input-content`) as HTMLElement
|
? (editableArea.value?.querySelector(
|
||||||
|
`.text-field[data-index="${targetIndex}"]`
|
||||||
|
) as HTMLElement)
|
||||||
|
: (editableArea.value?.querySelector(
|
||||||
|
`.input-field[data-index="${targetIndex}"] .input-content`
|
||||||
|
) as HTMLElement)
|
||||||
|
|
||||||
if (element) setCursorToElement(element, position)
|
if (element) setCursorToElement(element, position)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 焦点设置
|
// 焦点设置
|
||||||
const focusElement = (index: number, position: 'start' | 'end') => {
|
const focusElement = (index: number, position: 'start' | 'end') => {
|
||||||
const item = content.value[index]
|
const item = content.value[index]
|
||||||
const element = item.type === 'text'
|
const element =
|
||||||
? editableArea.value?.querySelector(`.text-field[data-index="${index}"]`) as HTMLElement
|
item.type === 'text'
|
||||||
: editableArea.value?.querySelector(`.input-field[data-index="${index}"] .input-content`) as HTMLElement
|
? (editableArea.value?.querySelector(
|
||||||
|
`.text-field[data-index="${index}"]`
|
||||||
|
) as HTMLElement)
|
||||||
|
: (editableArea.value?.querySelector(
|
||||||
|
`.input-field[data-index="${index}"] .input-content`
|
||||||
|
) as HTMLElement)
|
||||||
|
|
||||||
if (element) setCursorToElement(element, position)
|
if (element) setCursorToElement(element, position)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 输入框显示管理
|
// 输入框显示管理
|
||||||
const updateInputDisplay = (index: number) => {
|
const updateInputDisplay = (index: number) => {
|
||||||
const item = content.value[index]
|
const item = content.value[index]
|
||||||
if (item.type !== 'input') return
|
if (item.type !== 'input') return
|
||||||
|
|
||||||
const inputElement = editableArea.value?.querySelector(
|
const inputElement = editableArea.value?.querySelector(
|
||||||
`.input-field[data-index="${index}"] .input-content`
|
`.input-field[data-index="${index}"] .input-content`
|
||||||
) as HTMLElement
|
) as HTMLElement
|
||||||
|
|
||||||
if (!inputElement) return
|
if (!inputElement) return
|
||||||
|
|
||||||
const showPlaceholder = item.value.trim() === '' && item.placeholder
|
const showPlaceholder = item.value.trim() === '' && item.placeholder
|
||||||
|
|
||||||
if (showPlaceholder) {
|
if (showPlaceholder) {
|
||||||
inputElement.classList.add('has-placeholder')
|
inputElement.classList.add('has-placeholder')
|
||||||
inputElement.textContent = item.placeholder
|
inputElement.textContent = item.placeholder
|
||||||
} else {
|
} else {
|
||||||
inputElement.classList.remove('has-placeholder')
|
inputElement.classList.remove('has-placeholder')
|
||||||
inputElement.textContent = item.value
|
inputElement.textContent = item.value
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 输入框内容变化处理
|
// 输入框内容变化处理
|
||||||
const handleInputChange = (index: number, event: Event) => {
|
const handleInputChange = (index: number, event: Event) => {
|
||||||
const target = event.target as HTMLSpanElement
|
const target = event.target as HTMLSpanElement
|
||||||
const item = content.value[index]
|
const item = content.value[index]
|
||||||
|
|
||||||
// 如果当前显示placeholder,不更新实际值
|
// 如果当前显示placeholder,不更新实际值
|
||||||
if (!target.classList.contains('has-placeholder')) {
|
if (!target.classList.contains('has-placeholder')) {
|
||||||
const newValue = target.textContent || ''
|
const newValue = target.textContent || ''
|
||||||
content.value[index].value = newValue
|
content.value[index].value = newValue
|
||||||
|
|
||||||
// 如果内容变空,显示placeholder
|
// 如果内容变空,显示placeholder
|
||||||
if (newValue.trim() === '' && item.placeholder) {
|
if (newValue.trim() === '' && item.placeholder) {
|
||||||
target.classList.add('has-placeholder')
|
target.classList.add('has-placeholder')
|
||||||
target.textContent = item.placeholder
|
target.textContent = item.placeholder
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 输入框键盘事件
|
// 输入框键盘事件
|
||||||
const handleInputKeydown = (event: KeyboardEvent, index: number) => {
|
const handleInputKeydown = (event: KeyboardEvent, index: number) => {
|
||||||
const target = event.target as HTMLSpanElement
|
const target = event.target as HTMLSpanElement
|
||||||
|
|
||||||
if (event.key === 'Backspace') {
|
if (event.key === 'Backspace') {
|
||||||
// 如果显示placeholder,阻止删除
|
// 如果显示placeholder,阻止删除
|
||||||
if (target.classList.contains('has-placeholder')) {
|
if (target.classList.contains('has-placeholder')) {
|
||||||
event.preventDefault()
|
event.preventDefault()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const selection = window.getSelection()
|
const selection = window.getSelection()
|
||||||
if (selection && selection.rangeCount > 0) {
|
if (selection && selection.rangeCount > 0) {
|
||||||
const range = selection.getRangeAt(0)
|
const range = selection.getRangeAt(0)
|
||||||
const isAtStart = range.startOffset === 0
|
const isAtStart = range.startOffset === 0
|
||||||
|
|
||||||
// 如果光标在输入框开头,阻止默认行为,让外部的handleBackspace处理
|
// 如果光标在输入框开头,阻止默认行为,让外部的handleBackspace处理
|
||||||
if (isAtStart) {
|
if (isAtStart) {
|
||||||
event.preventDefault()
|
event.preventDefault()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (event.key.length === 1 && !event.ctrlKey && !event.metaKey) {
|
} else if (event.key.length === 1 && !event.ctrlKey && !event.metaKey) {
|
||||||
// 普通字符输入
|
// 普通字符输入
|
||||||
if (target.classList.contains('has-placeholder')) {
|
if (target.classList.contains('has-placeholder')) {
|
||||||
event.preventDefault()
|
event.preventDefault()
|
||||||
target.textContent = event.key
|
target.textContent = event.key
|
||||||
target.classList.remove('has-placeholder')
|
target.classList.remove('has-placeholder')
|
||||||
content.value[index].value = event.key
|
content.value[index].value = event.key
|
||||||
|
|
||||||
// 移动光标到末尾
|
// 移动光标到末尾
|
||||||
nextTick(() => {
|
nextTick(() => {
|
||||||
setCursorToElement(target, 'end')
|
setCursorToElement(target, 'end')
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleInputBlur = (index: number) => {
|
const handleInputBlur = (index: number) => {
|
||||||
updateInputDisplay(index)
|
updateInputDisplay(index)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 容器点击处理
|
// 容器点击处理
|
||||||
const handleContainerClick = (event: MouseEvent) => {
|
const handleContainerClick = (event: MouseEvent) => {
|
||||||
const target = event.target as HTMLElement
|
const target = event.target as HTMLElement
|
||||||
|
|
||||||
if (target === editableArea.value) {
|
if (target === editableArea.value) {
|
||||||
event.preventDefault()
|
event.preventDefault()
|
||||||
cursorState.value.isContainerClick = true
|
cursorState.value.isContainerClick = true
|
||||||
|
|
||||||
// 确保末尾有空文本框并聚焦到它
|
// 确保末尾有空文本框并聚焦到它
|
||||||
ensureEmptyTextAtEnd()
|
ensureEmptyTextAtEnd()
|
||||||
nextTick(() => {
|
nextTick(() => {
|
||||||
focusElement(content.value.length - 1, 'start')
|
focusElement(content.value.length - 1, 'start')
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 初始化
|
// 初始化
|
||||||
const initPlaceholders = () => {
|
const initPlaceholders = () => {
|
||||||
nextTick(() => {
|
nextTick(() => {
|
||||||
content.value.forEach((_, index) => updateInputDisplay(index))
|
content.value.forEach((_, index) => updateInputDisplay(index))
|
||||||
// 确保初始状态下有一个空文本框
|
// 确保初始状态下有一个空文本框
|
||||||
ensureEmptyTextAtEnd()
|
ensureEmptyTextAtEnd()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const getFullText = () => {
|
const getFullText = () => {
|
||||||
if(assistModel.value){
|
if (assistModel.value) {
|
||||||
return content.value.map(item => {
|
return content.value
|
||||||
if (item.type === 'text') {
|
.map(item => {
|
||||||
return item.value
|
if (item.type === 'text') {
|
||||||
} else {
|
return item.value
|
||||||
// 如果 input 没有输入 value,则用 placeholder 填充,并去掉首尾的 []
|
} else {
|
||||||
if (item.value) {
|
// 如果 input 没有输入 value,则用 placeholder 填充,并去掉首尾的 []
|
||||||
return ` ${item.value} `
|
if (item.value) {
|
||||||
} else if (item.placeholder) {
|
return ` ${item.value} `
|
||||||
let placeholderText = item.placeholder
|
} else if (item.placeholder) {
|
||||||
// 去掉首尾的 []
|
let placeholderText = item.placeholder
|
||||||
if (placeholderText.startsWith('[') && placeholderText.endsWith(']')) {
|
// 去掉首尾的 []
|
||||||
placeholderText = placeholderText.slice(1, -1)
|
if (placeholderText.startsWith('[') && placeholderText.endsWith(']')) {
|
||||||
}
|
placeholderText = placeholderText.slice(1, -1)
|
||||||
return ` ${placeholderText} `
|
}
|
||||||
}
|
return ` ${placeholderText} `
|
||||||
return ''
|
}
|
||||||
}
|
return ''
|
||||||
}).join('')
|
}
|
||||||
}
|
})
|
||||||
return textareaValue.value
|
.join('')
|
||||||
|
}
|
||||||
|
return textareaValue.value
|
||||||
}
|
}
|
||||||
|
|
||||||
const textareaValue = ref('')
|
const textareaValue = ref('')
|
||||||
const assistModel= ref(false)
|
const assistModel = ref(false)
|
||||||
const handleClickAssistBtn = () => {
|
const handleClickAssistBtn = () => {
|
||||||
assistModel.value = !assistModel.value
|
assistModel.value = !assistModel.value
|
||||||
}
|
}
|
||||||
|
|
||||||
// 监听 assistModel 变化,切换到 textarea 模式时调整高度
|
|
||||||
watch(assistModel, (newVal) => {
|
|
||||||
if (!newVal) {
|
|
||||||
// 切换到 textarea 模式
|
|
||||||
nextTick(() => {
|
|
||||||
handleInputResize()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
const textareaRef = useTemplateRef<HTMLTextAreaElement>('textareaRef')
|
const textareaRef = useTemplateRef<HTMLTextAreaElement>('textareaRef')
|
||||||
const handleInputResize = () => {
|
|
||||||
const textarea = textareaRef.value
|
|
||||||
if (textarea) {
|
|
||||||
textarea.style.height = 'auto'
|
|
||||||
textarea.style.height = textarea.scrollHeight + 'px'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
initPlaceholders()
|
initPlaceholders()
|
||||||
// 如果初始状态是 textarea 模式,设置初始高度
|
|
||||||
if (!assistModel.value) {
|
|
||||||
nextTick(() => {
|
|
||||||
handleInputResize()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
|
|
||||||
defineExpose({
|
defineExpose({
|
||||||
getFullText,
|
getFullText,
|
||||||
content
|
content
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div v-show="!assistModel" class="textarea-container">
|
<div v-show="!assistModel" class="textarea-container">
|
||||||
<textarea
|
<textarea
|
||||||
class="area"
|
class="area"
|
||||||
v-model="textareaValue"
|
v-model="textareaValue"
|
||||||
ref="textareaRef"
|
ref="textareaRef"
|
||||||
@input="handleInputResize"
|
:placeholder="$t('poseTransfer.PormptPlaceholder')"
|
||||||
:placeholder="$t('poseTransfer.PormptPlaceholder')"
|
/>
|
||||||
/>
|
<div class="asistant-btn" @click="handleClickAssistBtn">
|
||||||
<div class="asistant-btn" @click="handleClickAssistBtn">
|
<i class="fi fi-bs-magic-wand asistant-icon"></i>
|
||||||
<i class="fi fi-bs-magic-wand asistant-icon"></i>
|
<span>{{ $t('ProductImg.PromptAssit') }}</span>
|
||||||
<span>{{ $t('ProductImg.PromptAssit') }}</span>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<div
|
||||||
<div v-show="assistModel" ref="editableArea" class="promptInput" @keydown="handleKeydown" @click="handleContainerClick">
|
v-show="assistModel"
|
||||||
<template v-for="(item, index) in content" :key="item.id">
|
ref="editableArea"
|
||||||
<span
|
class="promptInput"
|
||||||
v-if="item.type === 'text'"
|
@keydown="handleKeydown"
|
||||||
class="text-field"
|
@click="handleContainerClick"
|
||||||
:data-index="index"
|
>
|
||||||
contenteditable="plaintext-only">
|
<div class="promptinput-wrapper">
|
||||||
{{item.value }}
|
<template v-for="(item, index) in content" :key="item.id">
|
||||||
</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 v-else class="input-field" :data-index="index">
|
||||||
<span
|
<span
|
||||||
class="input-content"
|
class="input-content"
|
||||||
contenteditable="plaintext-only"
|
contenteditable="plaintext-only"
|
||||||
@input="(e) => handleInputChange(index, e)"
|
@input="e => handleInputChange(index, e)"
|
||||||
@keydown="(e) => handleInputKeydown(e, index)"
|
@keydown="e => handleInputKeydown(e, index)"
|
||||||
@blur="() => handleInputBlur(index)"></span>
|
@blur="() => handleInputBlur(index)"
|
||||||
</span>
|
></span>
|
||||||
</template>
|
</span>
|
||||||
<div class="asistant-btn" @click="handleClickAssistBtn">
|
</template>
|
||||||
<i class="fi fi-bs-magic-wand asistant-icon"></i>
|
</div>
|
||||||
<span>{{ $t('ProductImg.PromptAssit') }}</span>
|
<div class="asistant-btn" @click="handleClickAssistBtn">
|
||||||
</div>
|
<i class="fi fi-bs-magic-wand asistant-icon"></i>
|
||||||
</div>
|
<span>{{ $t('ProductImg.PromptAssit') }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style lang="less" scoped>
|
<style lang="less" scoped>
|
||||||
.promptInput {
|
.promptInput {
|
||||||
--promptInputBorderRadius: 10px;
|
--promptInputBorderRadius: 10px;
|
||||||
--promptInputBorder: 2px solid #dcdfe6;
|
--promptInputBorder: 2px solid #dcdfe6;
|
||||||
--promptInputPadding: 1.5rem;
|
--promptInputPadding: 1.5rem;
|
||||||
|
|
||||||
width: 100%;
|
width: 100%;
|
||||||
min-height: 12rem;
|
font-weight: 500;
|
||||||
border-radius: var(--promptInputBorderRadius);
|
border-radius: var(--promptInputBorderRadius);
|
||||||
border: var(--promptInputBorder);
|
border: var(--promptInputBorder);
|
||||||
padding: var(--promptInputPadding);
|
padding: var(--promptInputPadding);
|
||||||
background: white;
|
background: white;
|
||||||
line-height: 1.6;
|
line-height: 1.6;
|
||||||
outline: none;
|
outline: none;
|
||||||
white-space: pre-wrap;
|
white-space: pre-wrap;
|
||||||
user-select: text;
|
user-select: text;
|
||||||
cursor: text;
|
cursor: text;
|
||||||
position: relative;
|
position: relative;
|
||||||
padding-bottom: 4rem;
|
padding-bottom: 4rem;
|
||||||
|
box-sizing: content-box;
|
||||||
|
|
||||||
.text-field {
|
.promptinput-wrapper {
|
||||||
display: inline;
|
min-height: 12rem;
|
||||||
outline: none;
|
max-height: 14rem;
|
||||||
padding: .2rem 0;
|
overflow-y: auto;
|
||||||
font-size: 1.8rem;
|
|
||||||
min-width: 2px;
|
|
||||||
/* 确保空文本框也能点击 */
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.input-field {
|
.text-field {
|
||||||
display: inline-block;
|
display: inline;
|
||||||
// background: #e3f2fd;
|
outline: none;
|
||||||
// border: 1px solid #bbdefb;
|
padding: 0.2rem 0;
|
||||||
margin: 0 .2rem;
|
font-size: 1.8rem;
|
||||||
padding: .2rem 1rem;
|
min-width: 2px;
|
||||||
font-size: 1.8rem;
|
font-weight: 400;
|
||||||
border-radius: 4px;
|
/* 确保空文本框也能点击 */
|
||||||
|
}
|
||||||
|
|
||||||
.input-content {
|
.input-field {
|
||||||
outline: none;
|
display: inline-block;
|
||||||
display: inline-block;
|
// background: #e3f2fd;
|
||||||
min-width: 2rem;
|
// border: 1px solid #bbdefb;
|
||||||
|
margin: 0 0.2rem;
|
||||||
|
padding: 0.2rem 1rem;
|
||||||
|
font-size: 1.8rem;
|
||||||
|
border-radius: 4px;
|
||||||
|
|
||||||
&.has-placeholder {
|
.input-content {
|
||||||
color: #b9b9b9;
|
outline: none;
|
||||||
// font-style: italic;
|
display: inline-block;
|
||||||
}
|
min-width: 2rem;
|
||||||
}
|
|
||||||
}
|
&.has-placeholder {
|
||||||
|
color: #b9b9b9;
|
||||||
|
// font-style: italic;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.textarea-container{
|
.textarea-container {
|
||||||
position: relative;
|
position: relative;
|
||||||
border-radius: 10px;
|
border-radius: 10px;
|
||||||
border: 2px solid #dcdfe6;
|
border: 2px solid #dcdfe6;
|
||||||
padding: 1.5rem 1.5rem 3rem;
|
padding: 1.5rem 1.5rem 3rem;
|
||||||
height: auto;
|
height: auto;
|
||||||
.area{
|
.area {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
min-height: 12rem;
|
min-height: 12rem;
|
||||||
height: auto;
|
max-height: 14rem;
|
||||||
background: white;
|
background: white;
|
||||||
line-height: 1.6;
|
line-height: 1.6;
|
||||||
outline: none;
|
outline: none;
|
||||||
@@ -509,34 +520,34 @@ defineExpose({
|
|||||||
user-select: text;
|
user-select: text;
|
||||||
cursor: text;
|
cursor: text;
|
||||||
position: relative;
|
position: relative;
|
||||||
// padding-bottom: 4rem;
|
|
||||||
resize: none;
|
resize: none;
|
||||||
border: none;
|
border: none;
|
||||||
|
overflow-y: auto;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.asistant-btn {
|
.asistant-btn {
|
||||||
height: 2.3rem;
|
height: 2.3rem;
|
||||||
padding: 0 0.6rem;
|
padding: 0 0.6rem;
|
||||||
font-size: 1rem;
|
font-size: 1rem;
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
color: #313131;
|
color: #313131;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
bottom: 1.3rem;
|
bottom: 1.3rem;
|
||||||
left: 1.3rem;
|
left: 1.3rem;
|
||||||
display: flex;
|
display: flex;
|
||||||
column-gap: 0.3rem;
|
column-gap: 0.3rem;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
background-color: #f2f2f2;
|
background-color: #f2f2f2;
|
||||||
border: 1px solid #dfdfdf;
|
border: 1px solid #dfdfdf;
|
||||||
border-radius: 0.5rem;
|
border-radius: 0.5rem;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
.asistant-icon {
|
.asistant-icon {
|
||||||
font-size: 1rem;
|
font-size: 1rem;
|
||||||
margin-right: 0;
|
margin-right: 0;
|
||||||
width: initial;
|
width: initial;
|
||||||
// margin-top: -0.2rem;
|
// margin-top: -0.2rem;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -623,11 +623,12 @@ export default defineComponent({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
let isSelectObject = false
|
let isSelectObject = false
|
||||||
watch(() => route.query,
|
watch(
|
||||||
(query:any, oldQuery:any) => {
|
() => route.query,
|
||||||
isSelectObject = false
|
(query: any, oldQuery: any) => {
|
||||||
},
|
isSelectObject = false
|
||||||
);
|
}
|
||||||
|
)
|
||||||
let beforeUpload = async (file: any) => {
|
let beforeUpload = async (file: any) => {
|
||||||
const isJpgOrPng =
|
const isJpgOrPng =
|
||||||
file.type === 'image/jpeg' ||
|
file.type === 'image/jpeg' ||
|
||||||
@@ -641,8 +642,8 @@ export default defineComponent({
|
|||||||
if (!isLt2M) {
|
if (!isLt2M) {
|
||||||
message.info(useI18n().t('MoodboardUpload.jsContent4'))
|
message.info(useI18n().t('MoodboardUpload.jsContent4'))
|
||||||
}
|
}
|
||||||
const objectId = route?.query?.id
|
const objectId = route?.query?.id
|
||||||
if (!objectId && !isSelectObject) {
|
if (!objectId && !isSelectObject) {
|
||||||
isSelectObject = true
|
isSelectObject = true
|
||||||
productImgData.selectObject.id = await createProbject()
|
productImgData.selectObject.id = await createProbject()
|
||||||
upload.value = {
|
upload.value = {
|
||||||
@@ -650,10 +651,7 @@ export default defineComponent({
|
|||||||
CollectionType: props.productimgMenu.value
|
CollectionType: props.productimgMenu.value
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return !!(
|
return !!((isJpgOrPng && isLt2M && objectId) || Upload.LIST_IGNORE)
|
||||||
(isJpgOrPng && isLt2M && objectId) ||
|
|
||||||
Upload.LIST_IGNORE
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
let setGenerate = (item: any) => {
|
let setGenerate = (item: any) => {
|
||||||
item.isChecked = !item.isChecked
|
item.isChecked = !item.isChecked
|
||||||
@@ -1559,8 +1557,8 @@ export default defineComponent({
|
|||||||
:deep(.ant-upload-picture-card-wrapper) {
|
:deep(.ant-upload-picture-card-wrapper) {
|
||||||
.ant-upload-list-picture-card {
|
.ant-upload-list-picture-card {
|
||||||
.ant-upload-select-picture-card {
|
.ant-upload-select-picture-card {
|
||||||
width: 9.6rem;
|
width: 12.7rem;
|
||||||
height: 13.4rem;
|
height: 17.8rem;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1579,9 +1577,14 @@ export default defineComponent({
|
|||||||
.input_box {
|
.input_box {
|
||||||
.input_box_btnBox {
|
.input_box_btnBox {
|
||||||
position: relative;
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: flex-start;
|
||||||
.textarea {
|
.textarea {
|
||||||
|
// flex: 1;
|
||||||
min-height: 12.7rem;
|
min-height: 12.7rem;
|
||||||
max-height: none;
|
max-height: none;
|
||||||
|
width: 100%;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1592,9 +1595,9 @@ export default defineComponent({
|
|||||||
font-size: 1rem;
|
font-size: 1rem;
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
color: #313131;
|
color: #313131;
|
||||||
position: absolute;
|
// position: absolute;
|
||||||
bottom: 1.3rem;
|
// bottom: 1.3rem;
|
||||||
left: 1.3rem;
|
// left: 1.3rem;
|
||||||
display: flex;
|
display: flex;
|
||||||
column-gap: 0.3rem;
|
column-gap: 0.3rem;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
|
|||||||
@@ -295,8 +295,8 @@ export default {
|
|||||||
GeneratedVideo: '生成的视频',
|
GeneratedVideo: '生成的视频',
|
||||||
hint: '将这张图像转化为一张逼真、达到工作室水准的照片。在生活中真实存在的人',
|
hint: '将这张图像转化为一张逼真、达到工作室水准的照片。在生活中真实存在的人',
|
||||||
jsContent1: '生成视频预计需要三分钟,请问是否继续',
|
jsContent1: '生成视频预计需要三分钟,请问是否继续',
|
||||||
NeedFirstFrame:'请选择首帧图片',
|
NeedFirstFrame: '请选择首帧图片',
|
||||||
NeedLastFrame:'请选择尾帧图片',
|
NeedLastFrame: '请选择尾帧图片',
|
||||||
SingleGarment:
|
SingleGarment:
|
||||||
'专业产品摄影:服装展示于隐形模特上,模特不可见。完整保留设计细节——所有图案、颜色、质地及细节特征。', // 单品样衣
|
'专业产品摄影:服装展示于隐形模特上,模特不可见。完整保留设计细节——所有图案、颜色、质地及细节特征。', // 单品样衣
|
||||||
SingleChildTryOn:
|
SingleChildTryOn:
|
||||||
@@ -308,18 +308,18 @@ export default {
|
|||||||
SeriesChildTryOn:
|
SeriesChildTryOn:
|
||||||
'将此图像转化为逼真的真实儿童模特,保持站立姿势并直面镜头,保留服装的原有图案、风格和色彩,呈现工作室级别的照片质量。', // 系列儿童试穿
|
'将此图像转化为逼真的真实儿童模特,保持站立姿势并直面镜头,保留服装的原有图案、风格和色彩,呈现工作室级别的照片质量。', // 系列儿童试穿
|
||||||
UploadWithModel:
|
UploadWithModel:
|
||||||
'创建模特穿着该服装的真实感摄影棚照片。若原有模特存在则保留,否则生成合适模特。采用站立姿势面向镜头。精确保留服装细节——图案、颜色、质地及装饰元素。', // 上传线稿,带模特
|
'生成真实模特穿着该服装的逼真摄影棚照片,采用站立姿势面向镜头。保留服装所有细节——包括图案、颜色、纹理、装饰元素,呈现摄影棚级别的8K分辨率照片,支持HDR效果、景深控制、柔光效果及高细节呈现。请勿返还原始图像。', // 上传线稿,带模特
|
||||||
UploadWithoutModel:
|
UploadWithoutModel:
|
||||||
'专业产品摄影:服装以自然形态展示,无模特。采用影棚灯光。保留精确细节——图案、色彩、质感、装饰元素。', // 上传线稿,不带模特
|
'专业产品摄影:服装以自然形态展示,无模特。采用影棚灯光。保留精确细节——图案、色彩、质感、装饰元素。', // 上传线稿,不带模特
|
||||||
VideoType: '视频类型',
|
VideoType: '视频类型',
|
||||||
FirstFrame: '首帧',
|
FirstFrame: '首帧',
|
||||||
LastFrame:'尾帧',
|
LastFrame: '尾帧',
|
||||||
FirstAndLastFrames: '首帧和尾帧',
|
FirstAndLastFrames: '首帧和尾帧',
|
||||||
FirstFrameAndSkeleton: '首帧和骨架',
|
FirstFrameAndSkeleton: '首帧和骨架',
|
||||||
Tips: '提示',
|
Tips: '提示',
|
||||||
TipsStart: '你可以使用"',
|
TipsStart: '你可以使用"',
|
||||||
TipsEnd: '编辑产品图"来生成首帧或尾帧图片',
|
TipsEnd: '编辑产品图"来生成首帧或尾帧图片',
|
||||||
PormptPlaceholder:'输入你想描述的场景...',
|
PormptPlaceholder: '输入你想描述的场景...',
|
||||||
firstAndLastFrameText1: '随着视频的进行,使用',
|
firstAndLastFrameText1: '随着视频的进行,使用',
|
||||||
firstAndLastFramePlaceholder1: '[相机运动]',
|
firstAndLastFramePlaceholder1: '[相机运动]',
|
||||||
firstAndLastFrameText2: '来跟随动作,在',
|
firstAndLastFrameText2: '来跟随动作,在',
|
||||||
|
|||||||
@@ -318,7 +318,7 @@ export default {
|
|||||||
SeriesChildTryOn:
|
SeriesChildTryOn:
|
||||||
'Transform this image into a realistic, the same real child model , Maintain a standing posture and face the camera directly, keep the same pattern, style and colour of these garment, studio-quality photograph.', // 系列儿童
|
'Transform this image into a realistic, the same real child model , Maintain a standing posture and face the camera directly, keep the same pattern, style and colour of these garment, studio-quality photograph.', // 系列儿童
|
||||||
UploadWithModel:
|
UploadWithModel:
|
||||||
'Create realistic studio photo with model wearing this garment. Keep original model if present, or generate appropriate model. Standing pose, facing camera. Preserve exact garment details - patterns, colors, textures, embellishments.', // 上传线稿,带模特
|
'Generate realistic studio photo of a real model wearing this garment, standing pose, facing camera. Preserve exact garment details - all patterns, colors, textures, embellishments, studio-quality photograph, 8K, HDR, DOF, soft lighting, high detail. Do not return the original image.', // 上传线稿,带模特
|
||||||
UploadWithoutModel:
|
UploadWithoutModel:
|
||||||
'Professional product photo: garment displayed with natural shape, no model. Studio lighting. Preserve exact details - patterns, colors, textures, embellishments.', // 上传线稿,不带模特
|
'Professional product photo: garment displayed with natural shape, no model. Studio lighting. Preserve exact details - patterns, colors, textures, embellishments.', // 上传线稿,不带模特
|
||||||
VideoType: 'Video Type',
|
VideoType: 'Video Type',
|
||||||
|
|||||||
Reference in New Issue
Block a user