Merge branch 'dev_vite' of ssh://18.167.251.121:10002/aidlab/aida_front into dev_vite

This commit is contained in:
X1627315083
2025-11-11 14:29:48 +08:00
21 changed files with 315 additions and 56 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

@@ -18,6 +18,7 @@ export class FillGroupLayerBackgroundCommand extends Command {
this.canvas = options.canvas; this.canvas = options.canvas;
this.layers = options.layers; this.layers = options.layers;
this.canvasManager = options.canvasManager; this.canvasManager = options.canvasManager;
this.layerManager = options.layerManager;
this.layerId = options.layerId; this.layerId = options.layerId;
this.fillColor = options.fillColor; this.fillColor = options.fillColor;
this.oldFill = null; this.oldFill = null;
@@ -211,6 +212,7 @@ export class FillGroupLayerBackgroundCommand extends Command {
this.group.set({ this.group.set({
id: layerObjects[0]?.id || generateId("group-"), id: layerObjects[0]?.id || generateId("group-"),
layerId: this.layer?.id, layerId: this.layer?.id,
layerName: this.layer?.name,
}); });
// this.group.setCoords(); // this.group.setCoords();
// this.group.setObjectsCoords(); // this.group.setObjectsCoords();
@@ -225,7 +227,7 @@ export class FillGroupLayerBackgroundCommand extends Command {
this.group.clipPath = clipPath; this.group.clipPath = clipPath;
} }
layer.fabricObjects = [ layer.fabricObjects = [
this.group.toObject(["id", "layerId"]) || this.group, this.group.toObject(["id", "layerId", "layerName"]) || this.group,
]; ];
// removeCanvasObjectByObject(this.canvas, layerObjects?.[0]); // removeCanvasObjectByObject(this.canvas, layerObjects?.[0]);
insertObjectAtZIndex(this.canvas, this.group, insertIndex, false, true); insertObjectAtZIndex(this.canvas, this.group, insertIndex, false, true);

View File

@@ -2595,7 +2595,7 @@ export class CreateImageLayerCommand extends Command {
// 生成图层名称 // 生成图层名称
const fileName = const fileName =
this.layerName || `${new Date().toLocaleString()}`; this.layerName || `${new Date().toLocaleTimeString()}`;
this.fabricImage.set({ this.fabricImage.set({
id: this.imageId, id: this.imageId,

View File

@@ -15,6 +15,8 @@ import {
import { createRasterizedImage } from "../utils/rasterizedImage"; import { createRasterizedImage } from "../utils/rasterizedImage";
import { message } from "ant-design-vue"; import { message } from "ant-design-vue";
import { restoreFabricObject } from "../utils/objectHelper"; import { restoreFabricObject } from "../utils/objectHelper";
import i18n from "@/lang/index.ts";
const { t } = i18n.global;
/** /**
* 组合图层命令 * 组合图层命令
@@ -338,7 +340,7 @@ export class RasterizeLayerCommand extends Command {
// 创建新的组合图层 // 创建新的组合图层
this.rasterizedLayer = createLayer({ this.rasterizedLayer = createLayer({
id: this.rasterizedLayerId, id: this.rasterizedLayerId,
name: `${this.layer.name} (组合)`, name: `${this.layer.name} (${t('Canvas.group')})`,
type: LayerType.BITMAP, type: LayerType.BITMAP,
visible: this.layer.visible, visible: this.layer.visible,
locked: this.layer.locked, locked: this.layer.locked,

View File

@@ -1667,6 +1667,7 @@ function stopPressTimer() {
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
height: 22px;
> i{ > i{
font-size: 1.4rem; font-size: 1.4rem;

View File

@@ -52,7 +52,7 @@
<span class="label iconfont icon-angle"></span> <span class="label iconfont icon-angle"></span>
<input <input
type="number" type="number"
:value="v.angle" :value="Number(Number(v.angle).toFixed(3))"
@change="(e) => changeAngle(e, v)" @change="(e) => changeAngle(e, v)"
/> />
</div> </div>
@@ -267,12 +267,7 @@
arrs.forEach((v) => { arrs.forEach((v) => {
activeObjects.value.forEach((item) => { activeObjects.value.forEach((item) => {
if (item.id === v.id) { if (item.id === v.id) {
keys.forEach((k) => { keys.forEach((key) => (item[key] = v[key]));
item[k] =
typeof v[k] === "number"
? Number(v[k].toFixed(3))
: v[k];
});
} }
}); });
activeObjects.value = [...activeObjects.value]; activeObjects.value = [...activeObjects.value];

View File

@@ -531,10 +531,13 @@ function confirmColorPicker() {
width: 100%; width: 100%;
text-align: center; text-align: center;
cursor: pointer; cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
height: 22px;
> i{ > i{
font-size: 1.4rem; font-size: 1.4rem;
display: block;
transform: rotate(270deg); transform: rotate(270deg);
} }
} }

View File

@@ -715,7 +715,10 @@ export default {
width: 100%; width: 100%;
text-align: center; text-align: center;
cursor: pointer; cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
height: 22px;
> i{ > i{
font-size: 1.4rem; font-size: 1.4rem;
display: block; display: block;

View File

@@ -395,9 +395,15 @@ const handleToolClick = (tool) => {
height: 100%; height: 100%;
padding-top: .5rem; padding-top: .5rem;
padding-bottom: 4rem; padding-bottom: 4rem;
position: relative;
/* overflow-y: auto; */ /* overflow-y: auto; */
/* overflow-x: hidden; */ /* overflow-x: hidden; */
} }
.fillColor-input{
position: absolute;
top: 0;
right: 0;
}
.tools-list{ .tools-list{
display: flex; display: flex;
flex-direction: column; flex-direction: column;

View File

@@ -2,11 +2,12 @@ import { FillGroupLayerBackgroundCommand } from "../commands/FillGroupLayerBackg
import { FillLayerBackgroundCommand } from "../commands/FillLayerBackgroundCommand"; import { FillLayerBackgroundCommand } from "../commands/FillLayerBackgroundCommand";
export class BackgroundFillManager { export class BackgroundFillManager {
constructor({ canvas, layers, commandManager, canvasManager }) { constructor({ canvas, layers, commandManager, canvasManager, layerManager }) {
this.canvas = canvas; this.canvas = canvas;
this.layers = layers; this.layers = layers;
this.commandManager = commandManager; this.commandManager = commandManager;
this.canvasManager = canvasManager; this.canvasManager = canvasManager;
this.layerManager = layerManager;
} }
/** /**
@@ -28,6 +29,7 @@ export class BackgroundFillManager {
layerId, layerId,
fillColor, fillColor,
canvasManager: this.canvasManager, canvasManager: this.canvasManager,
layerManager: this.layerManager,
// 是否实时更新 // 是否实时更新
isRetimeUpdate: !undoable, isRetimeUpdate: !undoable,
}); });

View File

@@ -109,6 +109,7 @@ export class LayerManager {
layers: this.layers, layers: this.layers,
commandManager: this.commandManager, commandManager: this.commandManager,
canvasManager: this.canvasManager, canvasManager: this.canvasManager,
layerManager: this,
}); });
// 编辑器模式draw(绘画)、select(选择)、pan(拖拽) // 编辑器模式draw(绘画)、select(选择)、pan(拖拽)

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>
<div class="prompt-container"> <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_border productImg_content_item_generate">
<div class="input_box"> <div class="input_box">
<div class="input_box_btnBox"> <div class="input_box_btnBox">
@@ -171,30 +171,8 @@
</div> </div>
<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>Prompt Assist</span> <span>{{ $t('ProductImg.PromptAssit') }}</span>
</div> </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>
<div class="productImg_content_item_generate_btn input_border"> <div class="productImg_content_item_generate_btn input_border">
<div class="generage_btn_box"> <div class="generage_btn_box">
@@ -342,8 +320,8 @@
}" }"
:isProductimg="true" :isProductimg="true"
></scaleImage> ></scaleImage>
<Prompt v-model:showModal="showPromptAssist" :promptList="promptTextList" />
</div> </div>
<!-- <Prompt /> -->
</template> </template>
<script lang="ts"> <script lang="ts">
@@ -374,6 +352,7 @@ import { useStore } from 'vuex'
import scaleImage from '@/component/HomePage/scaleImage.vue' import scaleImage from '@/component/HomePage/scaleImage.vue'
import generalMenu from '@/component/HomePage/generalMenu.vue' import generalMenu from '@/component/HomePage/generalMenu.vue'
import generalDrag from '@/component/modules/generalDrag.vue' import generalDrag from '@/component/modules/generalDrag.vue'
import Prompt from './Prompt.vue'
import { List } from 'echarts' import { List } from 'echarts'
import { useRouter, useRoute } from 'vue-router' import { useRouter, useRoute } from 'vue-router'
@@ -382,6 +361,7 @@ export default defineComponent({
scaleImage, scaleImage,
generalMenu, generalMenu,
generalDrag, generalDrag,
Prompt
}, },
props: { props: {
setTask: { setTask: {
@@ -1131,7 +1111,6 @@ export default defineComponent({
}) })
const showPromptAssist = ref(false) const showPromptAssist = ref(false)
const showPrompt = ref(false)
const handleClickAssistBtn = () => { const handleClickAssistBtn = () => {
showPromptAssist.value = true showPromptAssist.value = true
} }
@@ -1148,7 +1127,6 @@ export default defineComponent({
RelightDirection, RelightDirection,
promptTextList, promptTextList,
showPromptAssist, showPromptAssist,
showPrompt,
setproduct, setproduct,
fileUploadChange, fileUploadChange,
@@ -1339,7 +1317,7 @@ export default defineComponent({
} }
> .head { > .head {
color: #000; color: #000;
font-weight: 600; // font-weight: 600;
margin-bottom: 2rem; margin-bottom: 2rem;
> .text { > .text {
display: inline-block; display: inline-block;
@@ -1553,18 +1531,7 @@ export default defineComponent({
.designPage { .designPage {
margin-right: 4rem; 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 { .prompt-container {
margin-top: 4rem; margin-top: 4rem;

View File

@@ -279,7 +279,12 @@ export default {
Clear: '清空', Clear: '清空',
jsContent1: '如果您离开此页,您的更改将会丢失。您确定要离开这一页吗?', jsContent1: '如果您离开此页,您的更改将会丢失。您确定要离开这一页吗?',
jsContent2: '请至少选择一张图片', jsContent2: '请至少选择一张图片',
jsContent3: '您有一张图生成失败,请重试。' jsContent3: '您有一张图生成失败,请重试。',
Prompt: '提示词',
PromptAssit: '提示词助手',
AssitSubTitle: '您可以复制并使用以下提示词:',
CopySuccess: '已复制到剪贴板',
CopyFiled: '复制失败'
}, },
poseTransfer: { poseTransfer: {
SelectDesign: '产品图', SelectDesign: '产品图',
@@ -1219,6 +1224,7 @@ export default {
basic: '基础', basic: '基础',
flipHorizontal: '水平翻转', flipHorizontal: '水平翻转',
flipVertical: '垂直翻转', flipVertical: '垂直翻转',
group: '组合',
//长毛笔 //长毛笔
FurSettings: '长毛笔设置', FurSettings: '长毛笔设置',
FurLength: '毛发长度', FurLength: '毛发长度',

View File

@@ -289,7 +289,12 @@ export default {
jsContent1: jsContent1:
'Your changes will be lost if you navigate away from this page. Are you sure you want to leave this page?', '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', 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: { poseTransfer: {
SelectDesign: 'Product image', SelectDesign: 'Product image',
@@ -312,7 +317,7 @@ export default {
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.', // 上传线稿,带模特 '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: 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: { LibraryPage: {
library: 'Library', library: 'Library',
@@ -1254,6 +1259,7 @@ export default {
basic: 'Basic', basic: 'Basic',
flipHorizontal: 'Horizontal Flip', flipHorizontal: 'Horizontal Flip',
flipVertical: 'Vertical Flip', flipVertical: 'Vertical Flip',
group: 'Group',
//长毛笔 //长毛笔
FurSettings: 'FurSettings', FurSettings: 'FurSettings',
FurLength: 'Fur Length', FurLength: 'Fur Length',