style: prompt input样式修改

This commit is contained in:
zhangyh
2025-11-18 14:44:38 +08:00
parent c50a1d33e5
commit 608182936a
6 changed files with 1933 additions and 1711 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -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;

View File

@@ -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>

View File

@@ -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;

View File

@@ -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: '来跟随动作,在',

View File

@@ -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',