feat: 提示词助手弹窗

This commit is contained in:
zhangyh
2025-11-11 14:20:04 +08:00
parent f67af2c2a8
commit cb5b4f9b65
11 changed files with 285 additions and 43 deletions

View File

@@ -0,0 +1,2 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" id="Layer_1" data-name="Layer 1" viewBox="0 0 24 24" width="512" height="512"><path d="m16,18.5v1c0,2.481-2.019,4.5-4.5,4.5h-7c-2.481,0-4.5-2.019-4.5-4.5v-7c0-2.481,2.019-4.5,4.5-4.5h1c.276,0,.5.224.5.5s-.224.5-.5.5h-1c-1.93,0-3.5,1.57-3.5,3.5v7c0,1.93,1.57,3.5,3.5,3.5h7c1.93,0,3.5-1.57,3.5-3.5v-1c0-.276.224-.5.5-.5s.5.224.5.5Zm8-14v7c0,2.481-2.019,4.5-4.5,4.5h-7c-2.481,0-4.5-2.019-4.5-4.5v-7c0-2.481,2.019-4.5,4.5-4.5h7c2.481,0,4.5,2.019,4.5,4.5Zm-1,0c0-1.93-1.57-3.5-3.5-3.5h-7c-1.93,0-3.5,1.57-3.5,3.5v7c0,1.93,1.57,3.5,3.5,3.5h7c1.93,0,3.5-1.57,3.5-3.5v-7Z"/></svg>

After

Width:  |  Height:  |  Size: 652 B

View File

@@ -0,0 +1,2 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" id="Layer_1" data-name="Layer 1" viewBox="0 0 24 24" width="512" height="512"><path d="M23.5,24H.5c-.28,0-.5-.22-.5-.5s.22-.5,.5-.5H23.5c.28,0,.5,.22,.5,.5s-.22,.5-.5,.5Zm-8.83-4.09l6.56-7.35c.77-.77,.98-1.88,.57-2.88-.42-1.01-1.36-1.65-2.45-1.65h-2.34V3.47c0-1.92-1.57-3.47-3.51-3.47h-2.99c-1.93,0-3.51,1.56-3.51,3.47v4.55s-2.33,0-2.33,0c-1.1,0-2.04,.64-2.45,1.65s-.19,2.12,.56,2.88l6.59,7.38c.73,.73,1.68,1.09,2.64,1.09s1.93-.37,2.67-1.11ZM7.5,9.03c.13,0,.26-.05,.35-.15s.15-.22,.15-.35V3.47c0-1.36,1.12-2.47,2.5-2.47h2.99c1.38,0,2.51,1.11,2.51,2.47v5.05c0,.28,.22,.5,.5,.5h2.84c.82,0,1.33,.54,1.53,1.03,.2,.5,.21,1.23-.36,1.81,0,0-.01,.01-.02,.02l-6.55,7.34c-1.07,1.07-2.81,1.07-3.86,.02L3.49,11.86c-.58-.58-.57-1.31-.36-1.81,.2-.5,.71-1.03,1.53-1.03h2.84Z"/></svg>

After

Width:  |  Height:  |  Size: 848 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 58 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 101 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 101 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

View File

@@ -0,0 +1,261 @@
<template>
<div ref="modalContainer"></div>
<a-modal
class="prompt-modal generalModel"
v-model:visible="showModal"
:footer="null"
:get-container="() => $refs.modalContainer"
width="78%"
:maskClosable="false"
:centered="true"
:closable="false"
wrapClassName="#app"
:keyboard="false"
>
<div class="generalModel_btn">
<div class="generalModel_closeIcon" @click.stop="handleClose()">
<svg
width="100%"
height="100%"
viewBox="0 0 46 46"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<circle cx="23" cy="23" r="23" fill="#000" fill-opacity="0.3" />
<rect
x="32.5063"
y="12"
width="3"
height="29"
rx="1.5"
transform="rotate(45 32.5063 12)"
fill="white"
/>
<rect
x="34.6274"
y="32.5059"
width="3"
height="29"
rx="1.5"
transform="rotate(135 34.6274 32.5059)"
fill="white"
/>
</svg>
</div>
</div>
<div class="title-container">
<div class="title">{{ $t('ProductImg.PromptAssit') }}</div>
<div class="sub-title">{{ $t('ProductImg.AssitSubTitle') }}</div>
</div>
<div class="example-content">
<div
class="example-wrapper"
v-for="item in Object.keys(exampleList)"
:key="item"
:class="item"
>
<div class="example-item" v-for="value in exampleList[item]" :key="value.title">
<img :src="value.src" />
<SvgIcon
v-if="value.isOriginal"
class="download-icon"
name="CDownload"
size="20"
color="#8E8E8E"
@click.stop="handleDownload(value)"
/>
</div>
</div>
</div>
<div class="prompt-list">
<div v-for="item in promptList" :key="item" class="prompt-item">
<SvgIcon
name="CCopy"
size="25"
color="#BCBCBC"
class="copy-icon"
@click.stop="handleCopy(item)"
/>
{{ item }}
</div>
</div>
</a-modal>
</template>
<script lang="ts" setup>
import { ref, useTemplateRef } from 'vue'
import { message } from 'ant-design-vue'
import originalDress from '@/assets/images/product/original_dress.png'
import generatedDress from '@/assets/images/product/geneated_dress.png'
import originalModel from '@/assets/images/product/original_model.png'
import generatedModel from '@/assets/images/product/generated_model.png'
import generatedSketch from '@/assets/images/product/generated_sketch.png'
import { downloadIamge } from '@/tool/util'
import { useI18n } from 'vue-i18n'
const { t, locale } = useI18n()
defineProps<{
promptList: string[]
}>()
const modalContainer = useTemplateRef('modalContainer')
const showModal = defineModel<boolean>('showModal', { required: true })
const garmentExample = [
{
title: 'originalDress',
src: originalDress,
isOriginal: true
},
{
title: 'generatedDress',
src: generatedDress
}
]
const sketchExample = [
{
title: 'origianlDress',
src: originalDress,
isOriginal: true
},
{
title: 'generatedSketch',
src: generatedSketch
}
]
const modelExample = [
{
title: 'originalModel',
src: originalModel,
isOriginal: true
},
{
title: 'generatedModel',
src: generatedModel
}
]
const exampleList = {
garmentExample,
sketchExample,
modelExample
}
const handleClose = () => {
showModal.value = false
}
const handleDownload = value => {
const { title, src } = value
downloadIamge(src, title)
}
const handleCopy = async (value: string) => {
try {
if (navigator.clipboard && navigator.clipboard.writeText) {
await navigator.clipboard.writeText(value)
message.success(t('ProductImg.CopySuccess'))
} else {
// 降级方案:使用传统的 document.execCommand 方法
const textArea = document.createElement('textarea')
textArea.value = value
textArea.style.position = 'fixed'
textArea.style.left = '-999999px'
textArea.style.top = '-999999px'
document.body.appendChild(textArea)
textArea.focus()
textArea.select()
try {
document.execCommand('copy')
message.success(t('ProductImg.CopySuccess'))
} catch (err) {
message.error(t('ProductImg.CopyFailed'))
}
document.body.removeChild(textArea)
}
} catch (err) {
message.error(t('ProductImg.CopyFailed'))
}
}
</script>
<style lang="less" scoped>
.prompt-modal {
.title-container {
.title {
font-size: 3.5rem;
color: #181818;
}
.sub-title {
font-size: 2rem;
font-weight: 400;
color: #000;
}
}
.example-content {
margin-top: 4.3rem;
padding-left: 7.4rem;
display: flex;
.example-wrapper {
display: flex;
gap: 0;
&.garmentExample {
margin-right: 9.6rem;
}
&.sketchExample {
margin-right: 1.6rem;
}
.example-item {
width: 20.3rem;
height: 38.4rem;
margin-left: -2px;
position: relative;
img {
width: 100%;
display: block;
}
.download-icon {
position: absolute;
top: 0.77rem;
right: 0.84rem;
cursor: pointer;
}
}
}
}
.prompt-list {
margin-top: 3.5rem;
display: flex;
column-gap: 6.3rem;
.prompt-item {
height: 12.8rem;
line-height: 2.3rem;
padding: 1.8rem 3.5rem 1.8rem 2rem;
color: #969696;
font-size: 1.9rem;
border: 1px solid #000;
border-radius: 0.8rem;
overflow-y: auto;
position: relative;
// &:first-child{
// width: 53.6rem;
// }
.copy-icon {
position: absolute;
top: 1.1rem;
right: 1rem;
cursor: pointer;
}
}
}
}
:deep(.generalModel .ant-modal-body) {
padding: 5rem 4.4rem 8rem 4.6rem;
}
.c-svg {
width: initial;
height: initial;
}
</style>

View File

@@ -150,7 +150,7 @@
/>
</div>
<div class="prompt-container">
<div class="prompt-title">Prompt</div>
<div class="prompt-title">{{ $t('ProductImg.Prompt') }}</div>
<div class="input_border productImg_content_item_generate">
<div class="input_box">
<div class="input_box_btnBox">
@@ -171,30 +171,8 @@
</div>
<div class="asistant-btn" @click="handleClickAssistBtn">
<i class="fi fi-bs-magic-wand asistant-icon"></i>
<span>Prompt Assist</span>
<span>{{ $t('ProductImg.PromptAssit') }}</span>
</div>
<!-- <div
class="selectText"
v-show="productimgMenu.value == 'ToProductImage' && speedData.value"
>
<a-tooltip
v-for="(promptText, index) in promptTextList"
:key="index"
placement="bottom"
>
<template #title>{{ promptText }}</template>
<div
@click="
() => {
searchName[productimgMenu.value] = promptText
ifMaximumLength()
}
"
>
{{ promptText }}
</div>
</a-tooltip>
</div> -->
</div>
<div class="productImg_content_item_generate_btn input_border">
<div class="generage_btn_box">
@@ -342,8 +320,8 @@
}"
:isProductimg="true"
></scaleImage>
<Prompt v-model:showModal="showPromptAssist" :promptList="promptTextList" />
</div>
<Prompt />
</template>
<script lang="ts">
@@ -374,6 +352,7 @@ import { useStore } from 'vuex'
import scaleImage from '@/component/HomePage/scaleImage.vue'
import generalMenu from '@/component/HomePage/generalMenu.vue'
import generalDrag from '@/component/modules/generalDrag.vue'
import Prompt from './Prompt.vue'
import { List } from 'echarts'
import { useRouter, useRoute } from 'vue-router'
@@ -382,6 +361,7 @@ export default defineComponent({
scaleImage,
generalMenu,
generalDrag,
Prompt
},
props: {
setTask: {
@@ -1122,7 +1102,6 @@ export default defineComponent({
})
const showPromptAssist = ref(false)
const showPrompt = ref(false)
const handleClickAssistBtn = () => {
showPromptAssist.value = true
}
@@ -1139,7 +1118,6 @@ export default defineComponent({
RelightDirection,
promptTextList,
showPromptAssist,
showPrompt,
setproduct,
fileUploadChange,
@@ -1330,7 +1308,7 @@ export default defineComponent({
}
> .head {
color: #000;
font-weight: 600;
// font-weight: 600;
margin-bottom: 2rem;
> .text {
display: inline-block;
@@ -1544,18 +1522,7 @@ export default defineComponent({
.designPage {
margin-right: 4rem;
}
.upload_file_item {
// height: 13.4rem;
// margin-top: 2rem;
:deep(.ant-upload-picture-card-wrapper) {
.ant-upload-list-picture-card {
.ant-upload-select-picture-card {
width: 9.6rem;
height: 13.4rem;
}
}
}
}
.prompt-container {
margin-top: 4rem;

View File

@@ -279,7 +279,12 @@ export default {
Clear: '清空',
jsContent1: '如果您离开此页,您的更改将会丢失。您确定要离开这一页吗?',
jsContent2: '请至少选择一张图片',
jsContent3: '您有一张图生成失败,请重试。'
jsContent3: '您有一张图生成失败,请重试。',
Prompt: '提示词',
PromptAssit: '提示词助手',
AssitSubTitle: '您可以复制并使用以下提示词:',
CopySuccess: '已复制到剪贴板',
CopyFiled: '复制失败'
},
poseTransfer: {
SelectDesign: '产品图',

View File

@@ -289,7 +289,12 @@ export default {
jsContent1:
'Your changes will be lost if you navigate away from this page. Are you sure you want to leave this page?',
jsContent2: 'Please select at least one picture',
jsContent3: 'One of your images failed to generate. Please try again.'
jsContent3: 'One of your images failed to generate. Please try again.',
Prompt: 'Prompt',
PromptAssit: 'Prompt Assit',
AssitSubTitle: 'You can copy following prompt and try:',
CopySuccess: 'Copied to clipboard',
CopyFiled: 'Failed to copy'
},
poseTransfer: {
SelectDesign: 'Product image',
@@ -312,7 +317,7 @@ export default {
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.', // 上传线稿,带模特
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.' // 上传线稿,不带模特
},
LibraryPage: {
library: 'Library',