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 {
|
||||
color: #000;
|
||||
font-weight: 600;
|
||||
font-weight: 500;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
column-gap: 1rem;
|
||||
@@ -1526,6 +1526,7 @@ export default defineComponent({
|
||||
font-size: 1.8rem;
|
||||
color: #000;
|
||||
margin-bottom: 1.4rem;
|
||||
font-weight: 500;
|
||||
}
|
||||
> .poses {
|
||||
margin-top: 3rem;
|
||||
@@ -1627,6 +1628,7 @@ export default defineComponent({
|
||||
}
|
||||
.video-type-container {
|
||||
margin-bottom: 4rem;
|
||||
font-weight: 500;
|
||||
.title {
|
||||
font-size: 1.8rem;
|
||||
color: #000;
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
<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 {
|
||||
id: string;
|
||||
type: 'text' | 'input';
|
||||
value: string;
|
||||
placeholder?: string;
|
||||
id: string
|
||||
type: 'text' | 'input'
|
||||
value: string
|
||||
placeholder?: string
|
||||
}
|
||||
|
||||
const props = defineProps({
|
||||
@@ -111,7 +111,10 @@ const setCursorToElement = (element: HTMLElement, position: 'start' | 'end') =>
|
||||
const selection = window.getSelection()
|
||||
const range = document.createRange()
|
||||
|
||||
if (element.classList.contains('input-content') && element.classList.contains('has-placeholder')) {
|
||||
if (
|
||||
element.classList.contains('input-content') &&
|
||||
element.classList.contains('has-placeholder')
|
||||
) {
|
||||
// placeholder状态的特殊处理
|
||||
range.selectNodeContents(element)
|
||||
range.collapse(position === 'start')
|
||||
@@ -146,7 +149,10 @@ const handleKeydown = (event: KeyboardEvent) => {
|
||||
if (isAtStart && index > 0) {
|
||||
event.preventDefault()
|
||||
handleCrossElementDelete(index)
|
||||
} else if (type === 'input' && elementInfo.element?.classList.contains('has-placeholder')) {
|
||||
} else if (
|
||||
type === 'input' &&
|
||||
elementInfo.element?.classList.contains('has-placeholder')
|
||||
) {
|
||||
event.preventDefault()
|
||||
}
|
||||
// 其他情况让浏览器正常处理删除
|
||||
@@ -211,9 +217,14 @@ const handleCrossElementDelete = (currentIndex: number) => {
|
||||
// 导航到元素
|
||||
const navigateToElement = (targetIndex: number, position: 'start' | 'end') => {
|
||||
const targetItem = content.value[targetIndex]
|
||||
const element = targetItem.type === 'text'
|
||||
? editableArea.value?.querySelector(`.text-field[data-index="${targetIndex}"]`) as HTMLElement
|
||||
: editableArea.value?.querySelector(`.input-field[data-index="${targetIndex}"] .input-content`) as HTMLElement
|
||||
const element =
|
||||
targetItem.type === 'text'
|
||||
? (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)
|
||||
}
|
||||
@@ -221,9 +232,14 @@ const navigateToElement = (targetIndex: number, position: 'start' | 'end') => {
|
||||
// 焦点设置
|
||||
const focusElement = (index: number, position: 'start' | 'end') => {
|
||||
const item = content.value[index]
|
||||
const element = item.type === 'text'
|
||||
? editableArea.value?.querySelector(`.text-field[data-index="${index}"]`) as HTMLElement
|
||||
: editableArea.value?.querySelector(`.input-field[data-index="${index}"] .input-content`) as HTMLElement
|
||||
const element =
|
||||
item.type === 'text'
|
||||
? (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)
|
||||
}
|
||||
@@ -336,8 +352,9 @@ const initPlaceholders = () => {
|
||||
}
|
||||
|
||||
const getFullText = () => {
|
||||
if(assistModel.value){
|
||||
return content.value.map(item => {
|
||||
if (assistModel.value) {
|
||||
return content.value
|
||||
.map(item => {
|
||||
if (item.type === 'text') {
|
||||
return item.value
|
||||
} else {
|
||||
@@ -354,44 +371,22 @@ const getFullText = () => {
|
||||
}
|
||||
return ''
|
||||
}
|
||||
}).join('')
|
||||
})
|
||||
.join('')
|
||||
}
|
||||
return textareaValue.value
|
||||
}
|
||||
|
||||
const textareaValue = ref('')
|
||||
const assistModel= ref(false)
|
||||
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({
|
||||
@@ -406,7 +401,6 @@ defineExpose({
|
||||
class="area"
|
||||
v-model="textareaValue"
|
||||
ref="textareaRef"
|
||||
@input="handleInputResize"
|
||||
:placeholder="$t('poseTransfer.PormptPlaceholder')"
|
||||
/>
|
||||
<div class="asistant-btn" @click="handleClickAssistBtn">
|
||||
@@ -414,25 +408,35 @@ defineExpose({
|
||||
<span>{{ $t('ProductImg.PromptAssit') }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div v-show="assistModel" ref="editableArea" class="promptInput" @keydown="handleKeydown" @click="handleContainerClick">
|
||||
<div
|
||||
v-show="assistModel"
|
||||
ref="editableArea"
|
||||
class="promptInput"
|
||||
@keydown="handleKeydown"
|
||||
@click="handleContainerClick"
|
||||
>
|
||||
<div class="promptinput-wrapper">
|
||||
<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 }}
|
||||
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>
|
||||
@input="e => handleInputChange(index, e)"
|
||||
@keydown="e => handleInputKeydown(e, index)"
|
||||
@blur="() => handleInputBlur(index)"
|
||||
></span>
|
||||
</span>
|
||||
</template>
|
||||
</div>
|
||||
<div class="asistant-btn" @click="handleClickAssistBtn">
|
||||
<i class="fi fi-bs-magic-wand asistant-icon"></i>
|
||||
<span>{{ $t('ProductImg.PromptAssit') }}</span>
|
||||
@@ -447,7 +451,7 @@ defineExpose({
|
||||
--promptInputPadding: 1.5rem;
|
||||
|
||||
width: 100%;
|
||||
min-height: 12rem;
|
||||
font-weight: 500;
|
||||
border-radius: var(--promptInputBorderRadius);
|
||||
border: var(--promptInputBorder);
|
||||
padding: var(--promptInputPadding);
|
||||
@@ -459,14 +463,21 @@ defineExpose({
|
||||
cursor: text;
|
||||
position: relative;
|
||||
padding-bottom: 4rem;
|
||||
box-sizing: content-box;
|
||||
|
||||
.promptinput-wrapper {
|
||||
min-height: 12rem;
|
||||
max-height: 14rem;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.text-field {
|
||||
display: inline;
|
||||
outline: none;
|
||||
padding: .2rem 0;
|
||||
padding: 0.2rem 0;
|
||||
font-size: 1.8rem;
|
||||
min-width: 2px;
|
||||
font-weight: 400;
|
||||
/* 确保空文本框也能点击 */
|
||||
}
|
||||
|
||||
@@ -474,8 +485,8 @@ defineExpose({
|
||||
display: inline-block;
|
||||
// background: #e3f2fd;
|
||||
// border: 1px solid #bbdefb;
|
||||
margin: 0 .2rem;
|
||||
padding: .2rem 1rem;
|
||||
margin: 0 0.2rem;
|
||||
padding: 0.2rem 1rem;
|
||||
font-size: 1.8rem;
|
||||
border-radius: 4px;
|
||||
|
||||
@@ -492,16 +503,16 @@ defineExpose({
|
||||
}
|
||||
}
|
||||
|
||||
.textarea-container{
|
||||
.textarea-container {
|
||||
position: relative;
|
||||
border-radius: 10px;
|
||||
border: 2px solid #dcdfe6;
|
||||
padding: 1.5rem 1.5rem 3rem;
|
||||
height: auto;
|
||||
.area{
|
||||
.area {
|
||||
width: 100%;
|
||||
min-height: 12rem;
|
||||
height: auto;
|
||||
max-height: 14rem;
|
||||
background: white;
|
||||
line-height: 1.6;
|
||||
outline: none;
|
||||
@@ -509,9 +520,9 @@ defineExpose({
|
||||
user-select: text;
|
||||
cursor: text;
|
||||
position: relative;
|
||||
// padding-bottom: 4rem;
|
||||
resize: none;
|
||||
border: none;
|
||||
overflow-y: auto;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -623,11 +623,12 @@ export default defineComponent({
|
||||
}
|
||||
}
|
||||
let isSelectObject = false
|
||||
watch(() => route.query,
|
||||
(query:any, oldQuery:any) => {
|
||||
watch(
|
||||
() => route.query,
|
||||
(query: any, oldQuery: any) => {
|
||||
isSelectObject = false
|
||||
},
|
||||
);
|
||||
}
|
||||
)
|
||||
let beforeUpload = async (file: any) => {
|
||||
const isJpgOrPng =
|
||||
file.type === 'image/jpeg' ||
|
||||
@@ -650,10 +651,7 @@ export default defineComponent({
|
||||
CollectionType: props.productimgMenu.value
|
||||
}
|
||||
}
|
||||
return !!(
|
||||
(isJpgOrPng && isLt2M && objectId) ||
|
||||
Upload.LIST_IGNORE
|
||||
)
|
||||
return !!((isJpgOrPng && isLt2M && objectId) || Upload.LIST_IGNORE)
|
||||
}
|
||||
let setGenerate = (item: any) => {
|
||||
item.isChecked = !item.isChecked
|
||||
@@ -1559,8 +1557,8 @@ export default defineComponent({
|
||||
:deep(.ant-upload-picture-card-wrapper) {
|
||||
.ant-upload-list-picture-card {
|
||||
.ant-upload-select-picture-card {
|
||||
width: 9.6rem;
|
||||
height: 13.4rem;
|
||||
width: 12.7rem;
|
||||
height: 17.8rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1579,9 +1577,14 @@ export default defineComponent({
|
||||
.input_box {
|
||||
.input_box_btnBox {
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
.textarea {
|
||||
// flex: 1;
|
||||
min-height: 12.7rem;
|
||||
max-height: none;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1592,9 +1595,9 @@ export default defineComponent({
|
||||
font-size: 1rem;
|
||||
font-weight: 400;
|
||||
color: #313131;
|
||||
position: absolute;
|
||||
bottom: 1.3rem;
|
||||
left: 1.3rem;
|
||||
// position: absolute;
|
||||
// bottom: 1.3rem;
|
||||
// left: 1.3rem;
|
||||
display: flex;
|
||||
column-gap: 0.3rem;
|
||||
justify-content: center;
|
||||
|
||||
@@ -295,8 +295,8 @@ export default {
|
||||
GeneratedVideo: '生成的视频',
|
||||
hint: '将这张图像转化为一张逼真、达到工作室水准的照片。在生活中真实存在的人',
|
||||
jsContent1: '生成视频预计需要三分钟,请问是否继续',
|
||||
NeedFirstFrame:'请选择首帧图片',
|
||||
NeedLastFrame:'请选择尾帧图片',
|
||||
NeedFirstFrame: '请选择首帧图片',
|
||||
NeedLastFrame: '请选择尾帧图片',
|
||||
SingleGarment:
|
||||
'专业产品摄影:服装展示于隐形模特上,模特不可见。完整保留设计细节——所有图案、颜色、质地及细节特征。', // 单品样衣
|
||||
SingleChildTryOn:
|
||||
@@ -308,18 +308,18 @@ export default {
|
||||
SeriesChildTryOn:
|
||||
'将此图像转化为逼真的真实儿童模特,保持站立姿势并直面镜头,保留服装的原有图案、风格和色彩,呈现工作室级别的照片质量。', // 系列儿童试穿
|
||||
UploadWithModel:
|
||||
'创建模特穿着该服装的真实感摄影棚照片。若原有模特存在则保留,否则生成合适模特。采用站立姿势面向镜头。精确保留服装细节——图案、颜色、质地及装饰元素。', // 上传线稿,带模特
|
||||
'生成真实模特穿着该服装的逼真摄影棚照片,采用站立姿势面向镜头。保留服装所有细节——包括图案、颜色、纹理、装饰元素,呈现摄影棚级别的8K分辨率照片,支持HDR效果、景深控制、柔光效果及高细节呈现。请勿返还原始图像。', // 上传线稿,带模特
|
||||
UploadWithoutModel:
|
||||
'专业产品摄影:服装以自然形态展示,无模特。采用影棚灯光。保留精确细节——图案、色彩、质感、装饰元素。', // 上传线稿,不带模特
|
||||
VideoType: '视频类型',
|
||||
FirstFrame: '首帧',
|
||||
LastFrame:'尾帧',
|
||||
LastFrame: '尾帧',
|
||||
FirstAndLastFrames: '首帧和尾帧',
|
||||
FirstFrameAndSkeleton: '首帧和骨架',
|
||||
Tips: '提示',
|
||||
TipsStart: '你可以使用"',
|
||||
TipsEnd: '编辑产品图"来生成首帧或尾帧图片',
|
||||
PormptPlaceholder:'输入你想描述的场景...',
|
||||
PormptPlaceholder: '输入你想描述的场景...',
|
||||
firstAndLastFrameText1: '随着视频的进行,使用',
|
||||
firstAndLastFramePlaceholder1: '[相机运动]',
|
||||
firstAndLastFrameText2: '来跟随动作,在',
|
||||
|
||||
@@ -318,7 +318,7 @@ export default {
|
||||
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.', // 系列儿童
|
||||
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:
|
||||
'Professional product photo: garment displayed with natural shape, no model. Studio lighting. Preserve exact details - patterns, colors, textures, embellishments.', // 上传线稿,不带模特
|
||||
VideoType: 'Video Type',
|
||||
|
||||
Reference in New Issue
Block a user