Merge branch 'dev_vite' of http://18.167.251.121:10003/aidlab/aida_front into dev_vite
This commit is contained in:
@@ -1,6 +1 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
|
<svg t="1762937400333" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4603" width="200" height="200"><path d="M817.088 484.96l-512-323.744C295.232 154.976 282.752 154.592 272.576 160.224 262.336 165.856 256 176.608 256 188.256l0 647.328c0 11.648 6.336 22.4 16.576 28.032 4.8 2.656 10.112 3.968 15.424 3.968 5.952 0 11.904-1.664 17.088-4.928l512-323.616C826.368 533.184 832 522.976 832 512 832 501.024 826.368 490.816 817.088 484.96z" fill="currentColor" p-id="4604"></path></svg>
|
||||||
<path fill="currentColor" fill-opacity="0" stroke="currentColor" stroke-dasharray="40" stroke-dashoffset="40" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 6l10 6l-10 6Z">
|
|
||||||
<animate fill="freeze" attributeName="fill-opacity" begin="0.5s" dur="0.5s" values="0;1" />
|
|
||||||
<animate fill="freeze" attributeName="stroke-dashoffset" dur="0.5s" values="40;0" />
|
|
||||||
</path>
|
|
||||||
</svg>
|
|
||||||
|
Before Width: | Height: | Size: 477 B After Width: | Height: | Size: 524 B |
@@ -65,6 +65,9 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div style="width: 100%; height: 100px;">
|
||||||
|
<promptInput :content="prompt" ref="promptInput"></promptInput>
|
||||||
|
</div>
|
||||||
<div class="poses" v-show="showMotion">
|
<div class="poses" v-show="showMotion">
|
||||||
<div class="head">
|
<div class="head">
|
||||||
<div class="text">{{$t('poseTransfer.Selectpose')}}</div>
|
<div class="text">{{$t('poseTransfer.Selectpose')}}</div>
|
||||||
@@ -81,6 +84,17 @@
|
|||||||
<i class="fi fi-br-check"></i>
|
<i class="fi fi-br-check"></i>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="control-container">
|
||||||
|
<div class="icon-list">
|
||||||
|
<SvgIcon
|
||||||
|
class="play-icon"
|
||||||
|
@click="handlePlayMotion(item)"
|
||||||
|
name="CPlay"
|
||||||
|
size="10"
|
||||||
|
color="#fff"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -147,11 +161,12 @@ import { getUploadUrl,isMoible,getMinioUrl } from "@/tool/util";
|
|||||||
import { getCookie,setCookie } from "@/tool/cookie";
|
import { getCookie,setCookie } from "@/tool/cookie";
|
||||||
import showViewVideo from "@/tool/mount";
|
import showViewVideo from "@/tool/mount";
|
||||||
import router from '@/router';
|
import router from '@/router';
|
||||||
|
import promptInput from "./promptInput.vue";
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
components:{
|
components:{
|
||||||
generalDrag,
|
generalDrag,
|
||||||
// selectList,
|
promptInput
|
||||||
},
|
},
|
||||||
props:{
|
props:{
|
||||||
isDesignPage:{
|
isDesignPage:{
|
||||||
@@ -202,6 +217,12 @@ export default defineComponent({
|
|||||||
generateTime:null as any,
|
generateTime:null as any,
|
||||||
poseList:[],
|
poseList:[],
|
||||||
selectPose:null as any,
|
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: '[请输入内容]' }
|
||||||
|
]
|
||||||
})
|
})
|
||||||
let speed = reactive({
|
let speed = reactive({
|
||||||
speedList:[
|
speedList:[
|
||||||
@@ -228,6 +249,7 @@ export default defineComponent({
|
|||||||
generalDragLeft:null as any,
|
generalDragLeft:null as any,
|
||||||
generalDragRight:null as any,
|
generalDragRight:null as any,
|
||||||
scaleVideo:null as any,
|
scaleVideo:null as any,
|
||||||
|
promptInput: null as any,
|
||||||
})
|
})
|
||||||
const selectImgItem = (item:any,)=>{
|
const selectImgItem = (item:any,)=>{
|
||||||
if(item.isChecked){
|
if(item.isChecked){
|
||||||
@@ -269,6 +291,9 @@ export default defineComponent({
|
|||||||
}
|
}
|
||||||
const gifPause = (e:any,item:any)=>{
|
const gifPause = (e:any,item:any)=>{
|
||||||
e.target.src = item.firstFrame//静态图片
|
e.target.src = item.firstFrame//静态图片
|
||||||
|
}
|
||||||
|
const handlePlayMotion = item => {
|
||||||
|
|
||||||
}
|
}
|
||||||
const getPoseList = ()=>{
|
const getPoseList = ()=>{
|
||||||
Https.axiosGet(Https.httpUrls.getAllPose).then((rv)=>{
|
Https.axiosGet(Https.httpUrls.getAllPose).then((rv)=>{
|
||||||
@@ -629,7 +654,7 @@ export default defineComponent({
|
|||||||
{immediate: true }
|
{immediate: true }
|
||||||
)
|
)
|
||||||
|
|
||||||
const videoType = ref('1')
|
const videoType = ref('3')
|
||||||
const showMotion = computed(()=> videoType.value === '3' )
|
const showMotion = computed(()=> videoType.value === '3' )
|
||||||
const options = ref([
|
const options = ref([
|
||||||
{ vlaue: '1', label: 'First frame' },
|
{ vlaue: '1', label: 'First frame' },
|
||||||
@@ -779,6 +804,7 @@ export default defineComponent({
|
|||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
overflow-x: auto;
|
overflow-x: auto;
|
||||||
column-gap: 2.4rem;
|
column-gap: 2.4rem;
|
||||||
|
position: relative;
|
||||||
> .item{
|
> .item{
|
||||||
// width: calc(100% / 2 - .5rem);
|
// width: calc(100% / 2 - .5rem);
|
||||||
// height: 25rem;
|
// height: 25rem;
|
||||||
@@ -875,6 +901,30 @@ export default defineComponent({
|
|||||||
> .upload_item{
|
> .upload_item{
|
||||||
border: none;
|
border: none;
|
||||||
}
|
}
|
||||||
|
.control-container{
|
||||||
|
width: 100%;
|
||||||
|
height: 3.3rem;
|
||||||
|
position: absolute;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
background: linear-gradient(180deg, rgba(8, 9, 13, 0) 0%, rgba(8, 9, 13, 0.27) 80.37%);
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-end;
|
||||||
|
justify-content: center;
|
||||||
|
.icon-list{
|
||||||
|
height: 50%;
|
||||||
|
width: calc(100% - 1.6rem);
|
||||||
|
border-top: 1px solid #fff;
|
||||||
|
display: flex;
|
||||||
|
box-sizing: border-box;
|
||||||
|
justify-content: flex-start;
|
||||||
|
align-items: center;
|
||||||
|
.play-icon{
|
||||||
|
width: initial;
|
||||||
|
height: initial;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
> .head{
|
> .head{
|
||||||
color: #000;
|
color: #000;
|
||||||
|
|||||||
422
src/component/home/tools/poseTransfer/promptInput.vue
Normal file
422
src/component/home/tools/poseTransfer/promptInput.vue
Normal file
@@ -0,0 +1,422 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { ref, onMounted, reactive, toRefs, nextTick } from "vue";
|
||||||
|
|
||||||
|
interface ContentItem {
|
||||||
|
id: string;
|
||||||
|
type: 'text' | 'input';
|
||||||
|
value: string;
|
||||||
|
placeholder?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
content: {
|
||||||
|
type: Array,
|
||||||
|
default: () => []
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const data = reactive({
|
||||||
|
// content: [
|
||||||
|
// { 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: '[请输入内容]' }
|
||||||
|
// ] as ContentItem[]
|
||||||
|
content: props.content
|
||||||
|
})
|
||||||
|
|
||||||
|
const editableArea = ref<HTMLElement>()
|
||||||
|
const { content } = toRefs(data)
|
||||||
|
|
||||||
|
const cursorState = ref({
|
||||||
|
isContainerClick: false
|
||||||
|
})
|
||||||
|
|
||||||
|
// 检查并删除末尾的空文本框
|
||||||
|
const removeLastEmptyTextIfNeeded = () => {
|
||||||
|
const lastItem = content.value[content.value.length - 1]
|
||||||
|
if (lastItem && lastItem.type === 'text' && lastItem.value === '') {
|
||||||
|
content.value.pop()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// 确保末尾有空文本框
|
||||||
|
const ensureEmptyTextAtEnd = () => {
|
||||||
|
const lastItem = content.value[content.value.length - 1]
|
||||||
|
if (!lastItem || lastItem.type !== 'text' || lastItem.value !== '') {
|
||||||
|
const newItem: ContentItem = {
|
||||||
|
id: Date.now().toString(),
|
||||||
|
type: 'text',
|
||||||
|
value: ''
|
||||||
|
}
|
||||||
|
content.value.push(newItem)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// 元素信息获取
|
||||||
|
const getCurrentElementInfo = () => {
|
||||||
|
const selection = window.getSelection()
|
||||||
|
if (!selection || selection.rangeCount === 0) return null
|
||||||
|
|
||||||
|
const range = selection.getRangeAt(0)
|
||||||
|
const node = range.startContainer
|
||||||
|
let element: HTMLElement | null = null
|
||||||
|
|
||||||
|
if (node.nodeType === Node.TEXT_NODE) {
|
||||||
|
element = (node as Text).parentElement
|
||||||
|
} else {
|
||||||
|
element = node as HTMLElement
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!element) return null
|
||||||
|
|
||||||
|
let index = -1
|
||||||
|
let type: 'text' | 'input' = 'text'
|
||||||
|
let isAtStart = false
|
||||||
|
let isAtEnd = false
|
||||||
|
|
||||||
|
if (element.classList.contains('text-field')) {
|
||||||
|
index = parseInt(element.getAttribute('data-index') || '-1')
|
||||||
|
type = 'text'
|
||||||
|
const textContent = element.textContent || ''
|
||||||
|
isAtStart = range.startOffset === 0
|
||||||
|
isAtEnd = range.startOffset === textContent.length
|
||||||
|
} else if (element.classList.contains('input-content')) {
|
||||||
|
const parent = element.parentElement
|
||||||
|
index = parseInt(parent?.getAttribute('data-index') || '-1')
|
||||||
|
type = 'input'
|
||||||
|
|
||||||
|
const item = content.value[index]
|
||||||
|
if (element.classList.contains('has-placeholder')) {
|
||||||
|
// placeholder状态下,光标在任意位置都认为是"在元素内"
|
||||||
|
isAtStart = range.startOffset === 0
|
||||||
|
isAtEnd = true
|
||||||
|
} else {
|
||||||
|
// 正常内容状态
|
||||||
|
const textContent = item.value
|
||||||
|
isAtStart = range.startOffset === 0
|
||||||
|
isAtEnd = range.startOffset === textContent.length
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return { index, type, element, isAtStart, isAtEnd }
|
||||||
|
}
|
||||||
|
|
||||||
|
//光标设置
|
||||||
|
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')) {
|
||||||
|
// placeholder状态的特殊处理
|
||||||
|
range.selectNodeContents(element)
|
||||||
|
range.collapse(position === 'start')
|
||||||
|
} else if (element.childNodes.length > 0) {
|
||||||
|
const targetNode = position === 'start' ? element.firstChild! : element.lastChild!
|
||||||
|
if (targetNode.nodeType === Node.TEXT_NODE) {
|
||||||
|
const offset = position === 'start' ? 0 : (targetNode.textContent || '').length
|
||||||
|
range.setStart(targetNode, offset)
|
||||||
|
range.setEnd(targetNode, offset)
|
||||||
|
} else {
|
||||||
|
range.selectNodeContents(element)
|
||||||
|
range.collapse(position === 'start')
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
range.selectNodeContents(element)
|
||||||
|
range.collapse(position === 'start')
|
||||||
|
}
|
||||||
|
|
||||||
|
selection?.removeAllRanges()
|
||||||
|
selection?.addRange(range)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 键盘事件处理
|
||||||
|
const handleKeydown = (event: KeyboardEvent) => {
|
||||||
|
const elementInfo = getCurrentElementInfo()
|
||||||
|
if (!elementInfo) return
|
||||||
|
|
||||||
|
const { index, type, isAtStart, isAtEnd } = elementInfo
|
||||||
|
|
||||||
|
switch (event.key) {
|
||||||
|
case 'Backspace':
|
||||||
|
if (isAtStart && index > 0) {
|
||||||
|
event.preventDefault()
|
||||||
|
handleCrossElementDelete(index)
|
||||||
|
} else if (type === 'input' && elementInfo.element?.classList.contains('has-placeholder')) {
|
||||||
|
event.preventDefault()
|
||||||
|
}
|
||||||
|
// 其他情况让浏览器正常处理删除
|
||||||
|
break
|
||||||
|
|
||||||
|
case 'ArrowLeft':
|
||||||
|
if (isAtStart && index > 0) {
|
||||||
|
event.preventDefault()
|
||||||
|
navigateToElement(index - 1, 'end')
|
||||||
|
}
|
||||||
|
break
|
||||||
|
|
||||||
|
case 'ArrowRight':
|
||||||
|
if (isAtEnd && index < content.value.length - 1) {
|
||||||
|
event.preventDefault()
|
||||||
|
navigateToElement(index + 1, 'start')
|
||||||
|
} else if (isAtEnd && index === content.value.length - 1) {
|
||||||
|
// 在最后一个元素末尾按右箭头,确保有一个空文本框
|
||||||
|
ensureEmptyTextAtEnd()
|
||||||
|
nextTick(() => {
|
||||||
|
navigateToElement(index + 1, 'start')
|
||||||
|
})
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 跨元素删除逻辑
|
||||||
|
const handleCrossElementDelete = (currentIndex: number) => {
|
||||||
|
const prevIndex = currentIndex - 1
|
||||||
|
const prevItem = content.value[prevIndex]
|
||||||
|
|
||||||
|
if (prevItem.type === 'input') {
|
||||||
|
if (prevItem.value.trim() === '') {
|
||||||
|
// 删除空输入框
|
||||||
|
content.value.splice(prevIndex, 1)
|
||||||
|
nextTick(() => {
|
||||||
|
// 删除输入框后,先删除末尾的空文本框
|
||||||
|
removeLastEmptyTextIfNeeded()
|
||||||
|
// 然后聚焦到正确的位置
|
||||||
|
if (prevIndex < content.value.length) {
|
||||||
|
focusElement(prevIndex, 'end')
|
||||||
|
} else if (content.value.length > 0) {
|
||||||
|
focusElement(content.value.length - 1, 'end')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
// 删除输入框最后一个字符,但保留输入框
|
||||||
|
const newValue = prevItem.value.slice(0, -1)
|
||||||
|
content.value[prevIndex].value = newValue
|
||||||
|
updateInputDisplay(prevIndex)
|
||||||
|
nextTick(() => focusElement(prevIndex, 'end'))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 文本框:移动到前一个文本框末尾,让浏览器正常删除
|
||||||
|
// 先删除末尾的空文本框
|
||||||
|
removeLastEmptyTextIfNeeded()
|
||||||
|
focusElement(prevIndex, 'end')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 导航到元素
|
||||||
|
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
|
||||||
|
|
||||||
|
if (element) setCursorToElement(element, position)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 焦点设置
|
||||||
|
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
|
||||||
|
|
||||||
|
if (element) setCursorToElement(element, position)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 输入框显示管理
|
||||||
|
const updateInputDisplay = (index: number) => {
|
||||||
|
const item = content.value[index]
|
||||||
|
if (item.type !== 'input') return
|
||||||
|
|
||||||
|
const inputElement = editableArea.value?.querySelector(
|
||||||
|
`.input-field[data-index="${index}"] .input-content`
|
||||||
|
) as HTMLElement
|
||||||
|
|
||||||
|
if (!inputElement) return
|
||||||
|
|
||||||
|
const showPlaceholder = item.value.trim() === '' && item.placeholder
|
||||||
|
|
||||||
|
if (showPlaceholder) {
|
||||||
|
inputElement.classList.add('has-placeholder')
|
||||||
|
inputElement.textContent = item.placeholder
|
||||||
|
} else {
|
||||||
|
inputElement.classList.remove('has-placeholder')
|
||||||
|
inputElement.textContent = item.value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 输入框内容变化处理
|
||||||
|
const handleInputChange = (index: number, event: Event) => {
|
||||||
|
const target = event.target as HTMLSpanElement
|
||||||
|
const item = content.value[index]
|
||||||
|
|
||||||
|
// 如果当前显示placeholder,不更新实际值
|
||||||
|
if (!target.classList.contains('has-placeholder')) {
|
||||||
|
const newValue = target.textContent || ''
|
||||||
|
content.value[index].value = newValue
|
||||||
|
|
||||||
|
// 如果内容变空,显示placeholder
|
||||||
|
if (newValue.trim() === '' && item.placeholder) {
|
||||||
|
target.classList.add('has-placeholder')
|
||||||
|
target.textContent = item.placeholder
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 输入框键盘事件
|
||||||
|
const handleInputKeydown = (event: KeyboardEvent, index: number) => {
|
||||||
|
const target = event.target as HTMLSpanElement
|
||||||
|
|
||||||
|
if (event.key === 'Backspace') {
|
||||||
|
// 如果显示placeholder,阻止删除
|
||||||
|
if (target.classList.contains('has-placeholder')) {
|
||||||
|
event.preventDefault()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const selection = window.getSelection()
|
||||||
|
if (selection && selection.rangeCount > 0) {
|
||||||
|
const range = selection.getRangeAt(0)
|
||||||
|
const isAtStart = range.startOffset === 0
|
||||||
|
|
||||||
|
// 如果光标在输入框开头,阻止默认行为,让外部的handleBackspace处理
|
||||||
|
if (isAtStart) {
|
||||||
|
event.preventDefault()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (event.key.length === 1 && !event.ctrlKey && !event.metaKey) {
|
||||||
|
// 普通字符输入
|
||||||
|
if (target.classList.contains('has-placeholder')) {
|
||||||
|
event.preventDefault()
|
||||||
|
target.textContent = event.key
|
||||||
|
target.classList.remove('has-placeholder')
|
||||||
|
content.value[index].value = event.key
|
||||||
|
|
||||||
|
// 移动光标到末尾
|
||||||
|
nextTick(() => {
|
||||||
|
setCursorToElement(target, 'end')
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleInputBlur = (index: number) => {
|
||||||
|
updateInputDisplay(index)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 容器点击处理
|
||||||
|
const handleContainerClick = (event: MouseEvent) => {
|
||||||
|
const target = event.target as HTMLElement
|
||||||
|
|
||||||
|
if (target === editableArea.value) {
|
||||||
|
event.preventDefault()
|
||||||
|
cursorState.value.isContainerClick = true
|
||||||
|
|
||||||
|
// 确保末尾有空文本框并聚焦到它
|
||||||
|
ensureEmptyTextAtEnd()
|
||||||
|
nextTick(() => {
|
||||||
|
focusElement(content.value.length - 1, 'start')
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 初始化
|
||||||
|
const initPlaceholders = () => {
|
||||||
|
nextTick(() => {
|
||||||
|
content.value.forEach((_, index) => updateInputDisplay(index))
|
||||||
|
// 确保初始状态下有一个空文本框
|
||||||
|
ensureEmptyTextAtEnd()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const getFullText = () => {
|
||||||
|
return content.value.map(item => {
|
||||||
|
if (item.type === 'text') {
|
||||||
|
return item.value
|
||||||
|
} else {
|
||||||
|
// 只获取用户实际输入的值,即使为空也显示空括号
|
||||||
|
return `${item.value}`
|
||||||
|
}
|
||||||
|
}).join('')
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
initPlaceholders()
|
||||||
|
})
|
||||||
|
|
||||||
|
defineExpose({
|
||||||
|
getFullText,
|
||||||
|
content
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div 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-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>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.promptInput {
|
||||||
|
--promptInputBorderRadius: 10px;
|
||||||
|
--promptInputBorder: 2px solid #dcdfe6;
|
||||||
|
--promptInputPadding: 1.5rem;
|
||||||
|
|
||||||
|
width: 100%;
|
||||||
|
min-height: 12rem;
|
||||||
|
border-radius: var(--promptInputBorderRadius);
|
||||||
|
border: var(--promptInputBorder);
|
||||||
|
padding: var(--promptInputPadding);
|
||||||
|
background: white;
|
||||||
|
line-height: 1.6;
|
||||||
|
outline: none;
|
||||||
|
white-space: pre-wrap;
|
||||||
|
user-select: text;
|
||||||
|
cursor: text;
|
||||||
|
|
||||||
|
.text-field {
|
||||||
|
display: inline;
|
||||||
|
outline: none;
|
||||||
|
padding: .2rem 0;
|
||||||
|
font-size: 1.8rem;
|
||||||
|
min-width: 2px;
|
||||||
|
/* 确保空文本框也能点击 */
|
||||||
|
}
|
||||||
|
|
||||||
|
.input-field {
|
||||||
|
display: inline-block;
|
||||||
|
background: #e3f2fd;
|
||||||
|
border: 1px solid #bbdefb;
|
||||||
|
margin: 0 .2rem;
|
||||||
|
padding: .2rem 1rem;
|
||||||
|
font-size: 1.8rem;
|
||||||
|
border-radius: 4px;
|
||||||
|
|
||||||
|
.input-content {
|
||||||
|
outline: none;
|
||||||
|
color: #1976d2;
|
||||||
|
display: inline-block;
|
||||||
|
min-width: 2rem;
|
||||||
|
|
||||||
|
&.has-placeholder {
|
||||||
|
color: #95a5a6;
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -720,6 +720,11 @@ export default defineComponent({
|
|||||||
let remPrductimgTime: any = null
|
let remPrductimgTime: any = null
|
||||||
let prductimgTime: any = null
|
let prductimgTime: any = null
|
||||||
let getPrductimg = () => {
|
let getPrductimg = () => {
|
||||||
|
// 未输入prompt时不可生成
|
||||||
|
if (!productImgData.searchName[props.productimgMenu.value]) {
|
||||||
|
message.info(t('ProductImg.noPrompt'))
|
||||||
|
return
|
||||||
|
}
|
||||||
if (productImgData.isProductimg) return
|
if (productImgData.isProductimg) return
|
||||||
clearInterval(remPrductimgTime)
|
clearInterval(remPrductimgTime)
|
||||||
let selectArr: any = []
|
let selectArr: any = []
|
||||||
|
|||||||
@@ -284,7 +284,8 @@ export default {
|
|||||||
PromptAssit: '提示词助手',
|
PromptAssit: '提示词助手',
|
||||||
AssitSubTitle: '您可以复制并使用以下提示词:',
|
AssitSubTitle: '您可以复制并使用以下提示词:',
|
||||||
CopySuccess: '已复制到剪贴板',
|
CopySuccess: '已复制到剪贴板',
|
||||||
CopyFiled: '复制失败'
|
CopyFiled: '复制失败',
|
||||||
|
noPrompt:'请输入提示词'
|
||||||
},
|
},
|
||||||
poseTransfer: {
|
poseTransfer: {
|
||||||
SelectDesign: '产品图',
|
SelectDesign: '产品图',
|
||||||
@@ -1369,7 +1370,7 @@ export default {
|
|||||||
liquefactionEnvironment: '准备液化环境',
|
liquefactionEnvironment: '准备液化环境',
|
||||||
liquefactionEnvironmentLoading: '正在准备液化环境,请稍候...',
|
liquefactionEnvironmentLoading: '正在准备液化环境,请稍候...',
|
||||||
LiqueficationFailed: '液化工具启动失败',
|
LiqueficationFailed: '液化工具启动失败',
|
||||||
TextLayer: '文本图层',
|
TextLayer: '文本图层',
|
||||||
DoubleClickText: '双击编辑文本',
|
DoubleClickText: '双击编辑文本',
|
||||||
LiquidationcuoError: '未选择有效图像或图层不适合液化操作',
|
LiquidationcuoError: '未选择有效图像或图层不适合液化操作',
|
||||||
ErrorMessage: '错误提示',
|
ErrorMessage: '错误提示',
|
||||||
|
|||||||
@@ -294,7 +294,8 @@ export default {
|
|||||||
PromptAssit: 'Prompt Assist',
|
PromptAssit: 'Prompt Assist',
|
||||||
AssitSubTitle: 'You can copy following prompt and try:',
|
AssitSubTitle: 'You can copy following prompt and try:',
|
||||||
CopySuccess: 'Copied to clipboard',
|
CopySuccess: 'Copied to clipboard',
|
||||||
CopyFiled: 'Failed to copy'
|
CopyFiled: 'Failed to copy',
|
||||||
|
noPrompt:'Please enter prompt'
|
||||||
},
|
},
|
||||||
poseTransfer: {
|
poseTransfer: {
|
||||||
SelectDesign: 'Product image',
|
SelectDesign: 'Product image',
|
||||||
|
|||||||
Reference in New Issue
Block a user