Merge remote-tracking branch 'origin/dev_vite' into StableVersion

This commit is contained in:
X1627315083
2026-02-03 10:46:09 +08:00
46 changed files with 1873 additions and 929 deletions

View File

@@ -5,4 +5,3 @@ VITE_USER_NODE_ENV = 'development'
VITE_APP_BASE_URL = 'https://develop.api.aida.com.hk' VITE_APP_BASE_URL = 'https://develop.api.aida.com.hk'
# VITE_APP_BASE_URL = 'http://localhost:22170' # VITE_APP_BASE_URL = 'http://localhost:22170'

Binary file not shown.

Binary file not shown.

View File

@@ -1,31 +1,41 @@
/* 字体定义 */ /* 字体定义 */
@font-face { @font-face {
font-family: 'Arial'; font-family: 'Arial';
src: url('./fonts/ARIAL.ttf') format('ttf'); src: url('./fonts/ARIAL.ttf') format('truetype');
} }
@font-face { @font-face {
font-family: 'ArialBold'; font-family: 'ArialBold';
src: url('./fonts/ARIALBD.ttf') format('ttf'); src: url('./fonts/ARIALBD.ttf') format('truetype');
} }
@font-face { @font-face {
font-family: 'ArialMedium'; font-family: 'ArialMedium';
src: url('./fonts/ArialMdm.ttf') format('ttf'); src: url('./fonts/ArialMdm.ttf') format('truetype');
} }
@font-face { @font-face {
font-family: 'Poppins'; font-family: 'Poppins';
src: url('./fonts/Poppins-Regular.ttf') format('ttf'); src: url('./fonts/Poppins-Regular.ttf') format('truetype');
font-weight: normal; font-weight: normal;
} }
@font-face { @font-face {
font-family: 'PoppinsMedium'; font-family: 'PoppinsMedium';
src: url('./fonts/Poppins-Medium.ttf') format('ttf'); src: url('./fonts/Poppins-Medium.ttf') format('truetype');
} }
@font-face { @font-face {
font-family: 'PoppinsBold'; font-family: 'PoppinsBold';
src: url('./fonts/Poppins-SemiBold.ttf') format('ttf'); src: url('./fonts/Poppins-SemiBold.ttf') format('truetype');
}
@font-face {
font-family: 'Instrument';
src: url('./InstrumentSans-Regular.ttf') format('truetype');
}
@font-face {
font-family: 'InstrumentBold';
src: url('./InstrumentSans-Bold.ttf') format('truetype');
} }

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 MiB

View File

@@ -0,0 +1 @@
<svg focusable="false" class="" data-icon="paper-clip" width="1em" height="1em" fill="#00000073" aria-hidden="true" viewBox="64 64 896 896"><path d="M779.3 196.6c-94.2-94.2-247.6-94.2-341.7 0l-261 260.8c-1.7 1.7-2.6 4-2.6 6.4s.9 4.7 2.6 6.4l36.9 36.9a9 9 0 0012.7 0l261-260.8c32.4-32.4 75.5-50.2 121.3-50.2s88.9 17.8 121.2 50.2c32.4 32.4 50.2 75.5 50.2 121.2 0 45.8-17.8 88.8-50.2 121.2l-266 265.9-43.1 43.1c-40.3 40.3-105.8 40.3-146.1 0-19.5-19.5-30.2-45.4-30.2-73s10.7-53.5 30.2-73l263.9-263.8c6.7-6.6 15.5-10.3 24.9-10.3h.1c9.4 0 18.1 3.7 24.7 10.3 6.7 6.7 10.3 15.5 10.3 24.9 0 9.3-3.7 18.1-10.3 24.7L372.4 653c-1.7 1.7-2.6 4-2.6 6.4s.9 4.7 2.6 6.4l36.9 36.9a9 9 0 0012.7 0l215.6-215.6c19.9-19.9 30.8-46.3 30.8-74.4s-11-54.6-30.8-74.4c-41.1-41.1-107.9-41-149 0L463 364 224.8 602.1A172.22 172.22 0 00174 724.8c0 46.3 18.1 89.8 50.8 122.5 33.9 33.8 78.3 50.7 122.7 50.7 44.4 0 88.8-16.9 122.6-50.7l309.2-309C824.8 492.7 850 432 850 367.5c.1-64.6-25.1-125.3-70.7-170.9z"></path></svg>

After

Width:  |  Height:  |  Size: 985 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 80 KiB

After

Width:  |  Height:  |  Size: 84 KiB

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 MiB

After

Width:  |  Height:  |  Size: 492 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 7.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.7 KiB

View File

@@ -1,93 +1,129 @@
{ {
"eventsList": [ "eventsList": [
{
"id": 1,
"title": "Just post your design work, you could have the chance to come to Hong Kong and interact with industry leaders face-to-face",
"imgUrl": "/image/events/workshop-En.jpg"
},
{ {
"id": 1,
"title":"Just post your design work, you could have the chance to come to Hong Kong and interact with industry leaders face-to-face",
"imgUrl": "/image/events/workshop-En.jpg"
},{
"id": 2, "id": 2,
"title":"AiDA X SFT AI Fashion Award 2024", "title": "AiDA X SFT AI Fashion Award 2024",
"imgUrl": "/image/events/Fashion-Award-2024.png" "imgUrl": "/image/events/Fashion-Award-2024.png"
},
{
"id": 3,
"title": "AiDA Global Design Awards 2026",
"imgUrl": "/image/events/award-poster.gif"
} }
], ],
"eventsItem":[ "eventsItem": [
{ {
"id":1, "id": 3,
"title":"Just post your design work, you could have the chance to come to Hong Kong and interact with industry leaders face-to-face", "title": "AiDA Global Design Awards 2026",
"imgUrl": "/image/events/workshop-En.jpg", "imgUrl": "/image/events/award-poster.gif",
"textList":[ "textList": [
{ {
"paragraph":[ "paragraph": [
{ {
"text":"🎨AiDA Workshop!" "text": "Scan the QR code for more information and to join the competition! The AiDA Global Design Award 2026 is an international design competition hosted by CodeCreate, a globally leading AI fashion solutions provider, celebrating the future of creativity powered by artificial intelligence. Open to designers from Hong Kong, China, Singapore, South Korea, and beyond, the competition brings together global talent, empowering AI as a creative partner—pushing fashion beyond traditional boundaries and unlocking new possibilities where technology amplifies human imagination."
} }
] ]
},{ },
"paragraph":[ {
{ "paragraph": [
"text":"The process is simple: use AiDA to post your design work on the 'Gallery', and the one with the most likes(at least 20 likes) will be invited to the AiDA Workshop offline event in Hong Kong on November 14th, to exchange ideas with the Royal College of Art (RCA), Jae Lim, co-founder of the renowned fashion brand BESFXXK, and outstanding designers! " {
} "text": "Participants have the opportunity to compete for cash prizes totaling up to US$9,000, gain global media exposure showcased by top international platforms, and connect with designers and industry leaders worldwide. Finalists will also attend an exclusive award ceremony in Hong Kong, with travel support provided, allowing them to showcase their talent, network with professionals, and celebrate their achievements on an international stage."
] }
},{ ]
"paragraph":[ }
{ ]
"text":"<b>⚠ATTENTION❗❗</b>" },
} {
] "id": 1,
},{ "title": "Just post your design work, you could have the chance to come to Hong Kong and interact with industry leaders face-to-face",
"paragraph":[ "imgUrl": "/image/events/workshop-En.jpg",
{ "textList": [
"text":"1. Add the tag in the work description #AiDAworkshop_2024" {
},{ "paragraph": [
"text":"2. One winner only" {
} "text": "🎨AiDA Workshop!"
] }
},{ ]
"paragraph":[ },
{ {
"text":"<b>🤩Code-Create will provide (Terms and conditions apply):</b>" "paragraph": [
} {
] "text": "The process is simple: use AiDA to post your design work on the 'Gallery', and the one with the most likes(at least 20 likes) will be invited to the AiDA Workshop offline event in Hong Kong on November 14th, to exchange ideas with the Royal College of Art (RCA), Jae Lim, co-founder of the renowned fashion brand BESFXXK, and outstanding designers! "
},{ }
"paragraph":[ ]
{ },
"text":"✅Round-trip transportation fee (only within China)" {
} "paragraph": [
] {
},{ "text": "<b>⚠ATTENTION❗❗</b>"
"paragraph":[ }
{ ]
"text":"✅One night accommodation fee" },
} {
] "paragraph": [
},{ {
"paragraph":[ "text": "1. Add the tag in the work description #AiDAworkshop_2024"
{ },
"text":"⌛Deadline: October 31, 2024" {
} "text": "2. One winner only"
] }
} ]
] },
}, {
{ "paragraph": [
"id":2, {
"title":"AiDA X SFT AI Fashion Award 2024", "text": "<b>🤩Code-Create will provide (Terms and conditions apply):</b>"
"imgUrl": "/image/events/Fashion-Award-2024.png", }
"textList":[ ]
{ },
"paragraph":[ {
{ "paragraph": [
"text":"With the aim of inspiring students to innovate in fashion design using AI, Code-Create and The Hong Kong Polytechnic University School of Fashion and Textiles (SFT) have jointly launched the 'AiDA X SFT AI Fashion Award 2024'. This competition provides students with valuable practical AiDA experience, laying the foundation for the future fashion design industry and positioning them as pioneers in AI fashion." {
} "text": "✅Round-trip transportation fee (only within China)"
] }
},{ ]
"paragraph":[ },
{ {
"text":"The competition is open to all SFT students, with the winners having the chance to win cash prizes (up to 20,000 HKD), internship opportunity at BESFXXK (will work with the renowned designer, Mr Jae Hyuk Lim, for the BESFXXK collection, that will be featured at NY Fashion Week and Paris Fashion Week) and more surprises! Scan the QR code to learn more." "paragraph": [
} {
] "text": "✅One night accommodation fee"
} }
] ]
},
{
"paragraph": [
{
"text": "⌛Deadline: October 31, 2024"
}
]
}
]
},
{
"id": 2,
"title": "AiDA X SFT AI Fashion Award 2024",
"imgUrl": "/image/events/Fashion-Award-2024.png",
"textList": [
{
"paragraph": [
{
"text": "With the aim of inspiring students to innovate in fashion design using AI, Code-Create and The Hong Kong Polytechnic University School of Fashion and Textiles (SFT) have jointly launched the 'AiDA X SFT AI Fashion Award 2024'. This competition provides students with valuable practical AiDA experience, laying the foundation for the future fashion design industry and positioning them as pioneers in AI fashion."
}
]
},
{
"paragraph": [
{
"text": "The competition is open to all SFT students, with the winners having the chance to win cash prizes (up to 20,000 HKD), internship opportunity at BESFXXK (will work with the renowned designer, Mr Jae Hyuk Lim, for the BESFXXK collection, that will be featured at NY Fashion Week and Paris Fashion Week) and more surprises! Scan the QR code to learn more."
}
]
}
]
} }
] ]
} }

View File

@@ -1,93 +1,129 @@
{ {
"eventsList": [ "eventsList": [
{ {
"id": 1, "id": 1,
"title":"什么?只要发布设计作品就有机会来香港与大佬面对面交流?!", "title": "什么?只要发布设计作品就有机会来香港与大佬面对面交流?!",
"imgUrl": "/image/events/workshop-Cn.jpg" "imgUrl": "/image/events/workshop-Cn.jpg"
},{ },
"id": 2, {
"title":"AiDA X SFT AI时尚设计比赛2024", "id": 2,
"imgUrl": "/image/events/Fashion-Award-2024.png" "title": "AiDA X SFT AI时尚设计比赛2024",
} "imgUrl": "/image/events/Fashion-Award-2024.png"
},
{
"id": 3,
"title": "AiDA全球设计奖 2026",
"imgUrl": "/image/events/award-poster.gif"
}
], ],
"eventsItem":[ "eventsItem": [
{ {
"id":1, "id": 3,
"title":"什么?只要发布设计作品就有机会来香港与大佬面对面交流?!", "title": "AiDA全球设计奖 2026",
"imgUrl": "/image/events/workshop-Cn.jpg", "imgUrl": "/image/events/award-poster.gif",
"textList":[ "textList": [
{ {
"paragraph":[ "paragraph": [
{ {
"text":"🎨这是一趟艺术巅峰之旅AiDA Workshop" "text": "秉承推动 AI 赋能创意设计的初衷CodeCreate 举办了「AiDA 全球设计大奖 2026」面向来自香港、中国、新加坡、韩国及全球的设计师鼓励大家探索 AI 与时尚设计的无限可能,突破传统界限,释放科技与想象力的创新潜能。扫描二维码获取更多比赛信息,抓住成为 AI 时尚先锋的机会吧"
} }
] ]
},{ },
"paragraph":[ {
{ "paragraph": [
"text":"参与过程很简单利用AiDA 在 “Gallery广场 ”发布设计作品,最终获赞最高者(至少20个赞将被邀请至11月14日 举办的AiDA Workshop香港线下活动与英国皇家艺术学院RCA、韩国知名时尚品牌BESFXXK创始人JAE以及优秀设计师一同交流(名额仅限1名" {
} "text": "参赛者将有机会赢取总奖金 9,000 美元,作品还将获得国际媒体展示机会,并与全球设计师和行业领袖建立联系。入围决赛者将受邀参加在香港举办的 专属颁奖典礼,主办方提供差旅支持,让设计师在国际舞台展示才华、拓展人脉,并共同庆祝创意成果。"
] }
},{ ]
"paragraph":[ }
{ ]
"text":"<b>⚠️注意❗❗</b>" },
} {
] "id": 1,
},{ "title": "什么?只要发布设计作品就有机会来香港与大佬面对面交流?!",
"paragraph":[ "imgUrl": "/image/events/workshop-Cn.jpg",
{ "textList": [
"text":"1. 作品描述添加tag: #AiDAworkshop_2024" {
},{ "paragraph": [
"text":"2. 一个冠军名额" {
} "text": "🎨这是一趟艺术巅峰之旅AiDA Workshop"
] }
},{ ]
"paragraph":[ },
{ {
"text":"<b>🤩Code-Create将提供适用条款条规</b>" "paragraph": [
} {
] "text": "参与过程很简单利用AiDA 在 “Gallery广场 ”发布设计作品,最终获赞最高者(至少20个赞将被邀请至11月14日 举办的AiDA Workshop香港线下活动与英国皇家艺术学院RCA、韩国知名时尚品牌BESFXXK创始人JAE以及优秀设计师一同交流(名额仅限1名"
},{ }
"paragraph":[ ]
{ },
"text":"✅往返机票/动车费用(仅限中国地区)" {
} "paragraph": [
] {
},{ "text": "<b>⚠️注意❗❗</b>"
"paragraph":[ }
{ ]
"text":"✅一晚酒店住宿费用" },
} {
] "paragraph": [
},{ {
"paragraph":[ "text": "1. 作品描述添加tag: #AiDAworkshop_2024"
{ },
"text":"⌛截止时间2024.10.31" {
} "text": "2. 一个冠军名额"
] }
} ]
] },
}, {
{ "paragraph": [
"id":2, {
"title":"AiDA X SFT AI时尚设计比赛2024", "text": "<b>🤩Code-Create将提供适用条款条规</b>"
"imgUrl": "/image/events/Fashion-Award-2024.png", }
"textList":[ ]
{ },
"paragraph":[ {
{ "paragraph": [
"text":"秉承着激发学生使用AI进行时尚设计的创新能力的初衷Code-Create和香港理工大学时装及纺织学院SFT共同举办了“AiDA X SFT AI时尚设计比赛2024”让学生们在比赛中获得宝贵的AiDA实践经验为未来的时尚设计行业打下了坚实的基础成为时尚界的AI先锋。" {
} "text": "✅往返机票/动车费用(仅限中国地区)"
] }
},{ ]
"paragraph":[ },
{ {
"text":" 此次比赛面向全体SFT 学生最终获奖者将赢取丰厚奖金最高可达2万港币获得在BESFXXK的实习机会将与著名设计师Lim Jae Hyuk先生合作设计BESFXXK 系列,该系列将在纽约时装周和巴黎时装周上展出)及更多惊喜哦!扫描二维码获取更多比赛信息。" "paragraph": [
} {
] "text": "✅一晚酒店住宿费用"
} }
] ]
} },
] {
} "paragraph": [
{
"text": "⌛截止时间2024.10.31"
}
]
}
]
},
{
"id": 2,
"title": "AiDA X SFT AI时尚设计比赛2024",
"imgUrl": "/image/events/Fashion-Award-2024.png",
"textList": [
{
"paragraph": [
{
"text": "秉承着激发学生使用AI进行时尚设计的创新能力的初衷Code-Create和香港理工大学时装及纺织学院SFT共同举办了“AiDA X SFT AI时尚设计比赛2024”让学生们在比赛中获得宝贵的AiDA实践经验为未来的时尚设计行业打下了坚实的基础成为时尚界的AI先锋。"
}
]
},
{
"paragraph": [
{
"text": " 此次比赛面向全体SFT 学生最终获奖者将赢取丰厚奖金最高可达2万港币获得在BESFXXK的实习机会将与著名设计师Lim Jae Hyuk先生合作设计BESFXXK 系列,该系列将在纽约时装周和巴黎时装周上展出)及更多惊喜哦!扫描二维码获取更多比赛信息。"
}
]
}
]
}
]
}

View File

@@ -2506,4 +2506,7 @@ textarea:focus {
} }
.justify-center { .justify-center {
justify-content: center; justify-content: center;
}
.flex-1{
flex: 1;
} }

View File

@@ -2424,4 +2424,7 @@ textarea:focus{
} }
.justify-center { .justify-center {
justify-content: center; justify-content: center;
}
.flex-1{
flex: 1;
} }

View File

@@ -7,12 +7,11 @@ import {
insertObjectAtZIndex, insertObjectAtZIndex,
removeCanvasObjectByObject, removeCanvasObjectByObject,
createPatternTransform, createPatternTransform,
getTransformScaleAngle,
imageAddGapToCanvas, imageAddGapToCanvas,
} from "../utils/helper"; } from "../utils/helper";
import { restoreFabricObject } from "../utils/objectHelper"; import { restoreFabricObject } from "../utils/objectHelper";
const scale = 0.3;// 默认缩放比例
export const FillSourceToBase64 = (source) => { export const FillSourceToBase64 = (source) => {
if (source?.toDataURL) { if (source?.toDataURL) {
return source.toDataURL?.(); return source.toDataURL?.();
@@ -39,7 +38,6 @@ export class FillRepeatCommand extends Command {
this.fillRepeat = options.fillRepeat; this.fillRepeat = options.fillRepeat;
this.oldObjects = null; this.oldObjects = null;
this.oldLocked = null; this.oldLocked = null;
this.oldIsDisableUnlock = null;
} }
async execute() { async execute() {
@@ -64,17 +62,15 @@ export class FillRepeatCommand extends Command {
); );
}); });
image.set({ image.set({
id: object.id, ...this.copyObjectProperties(object),
layerId: object.layerId,
layerName: object.layerName,
...(fill_.originalInfo || { ...(fill_.originalInfo || {
top: object.top, top: object.top,
left: object.left, left: object.left,
}) })
}); });
layer.fabricObjects = [image.toObject(["id", "layerId", "layerName"])]; layer.fabricObjects = [image.toObject(["id", "layerId", "layerName"])];
this.oldLocked = layer.locked; // this.oldLocked = layer.locked;
layer.locked = false; // layer.locked = false;
this.canvas.add(image); this.canvas.add(image);
this.canvas.remove(object); this.canvas.remove(object);
@@ -113,23 +109,32 @@ export class FillRepeatCommand extends Command {
const fdObject = this.canvasManager.getFixedLayerObject(); const fdObject = this.canvasManager.getFixedLayerObject();
const bgObject = this.canvasManager.getBackgroundLayerObject(); const bgObject = this.canvasManager.getBackgroundLayerObject();
const tObject = fdObject || bgObject; const tObject = fdObject || bgObject;
const tWidth = tObject.width;
const tHeight = tObject.height;
// const offsetX = object.fill?.hasOwnProperty("offsetX") ? object.fill.offsetX : tObject.width / 2;
// const offsetY = object.fill?.hasOwnProperty("offsetY") ? object.fill.offsetY : tObject.height / 2;
const scaleX_ = tWidth / img.width / 5;
const scaleY_ = tHeight / img.height / 5;
const scale_ = tWidth > tHeight ? scaleX_ : scaleY_;
const patternTransform = object.fill?.hasOwnProperty("patternTransform") ? object.fill.patternTransform : createPatternTransform(scale_, 0);
const scale = getTransformScaleAngle(patternTransform).scale;
const offsetX = tWidth / 2 - img.width * scale / 2;
const offsetY = tHeight / 2 - img.height * scale / 2;
const pattern = new fabric.Pattern({ const pattern = new fabric.Pattern({
source: img, source: img,
repeat: this.fillRepeat, repeat: this.fillRepeat,
patternTransform: object.fill?.hasOwnProperty("patternTransform") ? object.fill.patternTransform : createPatternTransform(scale, 0), patternTransform,
offsetX: object.fill?.hasOwnProperty("offsetX") ? object.fill.offsetX : tObject.width / 2, // 水平偏移 offsetX, // 水平偏移
offsetY: object.fill?.hasOwnProperty("offsetY") ? object.fill.offsetY : tObject.height / 2, // 垂直偏移 offsetY, // 垂直偏移
}); });
const rect = new fabric.Rect({ const rect = new fabric.Rect({
id: object.id, ...this.copyObjectProperties(object),
layerId: object.layerId,
layerName: object.layerName,
fill_, fill_,
}); });
layer.fabricObjects = [rect.toObject(["id", "layerId", "layerName"])]; layer.fabricObjects = [rect.toObject(["id", "layerId", "layerName"])];
this.oldLocked = layer.locked; // this.oldLocked = layer.locked;
// this.oldIsDisableUnlock = layer.isDisableUnlock;
// layer.isDisableUnlock = true;
if (this.oldObjects.type === "rect") { if (this.oldObjects.type === "rect") {
rect.set({ rect.set({
width: object.width, width: object.width,
@@ -148,14 +153,14 @@ export class FillRepeatCommand extends Command {
let scaleX = tObject.scaleX || 1; let scaleX = tObject.scaleX || 1;
let scaleY = tObject.scaleY || 1; let scaleY = tObject.scaleY || 1;
rect.set({ rect.set({
width: tObject.width, width: tWidth,
height: tObject.height, height: tHeight,
top: tObject.top - tObject.height * scaleY / 2, top: tObject.top - tHeight * scaleY / 2,
left: tObject.left - tObject.width * scaleX / 2, left: tObject.left - tWidth * scaleX / 2,
scaleX, scaleX,
scaleY, scaleY,
}); });
layer.locked = true; // layer.locked = true;
} }
rect.set("fill", pattern); rect.set("fill", pattern);
this.canvas.add(rect); this.canvas.add(rect);
@@ -184,14 +189,23 @@ export class FillRepeatCommand extends Command {
this.canvas.remove(object); this.canvas.remove(object);
this.canvas.add(this.oldObjects); this.canvas.add(this.oldObjects);
layer.fabricObjects = [this.oldObjects.toObject(["id", "layerId", "layerName"])]; layer.fabricObjects = [this.oldObjects.toObject(["id", "layerId", "layerName"])];
layer.locked = this.oldLocked; // layer.locked = this.oldLocked;
// layer.isDisableUnlock = this.oldIsDisableUnlock;
await this.layerManager?.updateLayersObjectsInteractivity(); await this.layerManager?.updateLayersObjectsInteractivity();
await this.layerManager?.sortLayersWithTool?.(); await this.layerManager?.sortLayersWithTool?.();
this.canvas.renderAll(); this.canvas.renderAll();
this.canvasManager.thumbnailManager?.generateLayerThumbnail(this.layerId); this.canvasManager.thumbnailManager?.generateLayerThumbnail(this.layerId);
return true; return true;
} }
// 复制原对象的属性
copyObjectProperties(object) {
return {
id: object.id,
layerId: object.layerId,
layerName: object.layerName,
isPrintTrims: object.isPrintTrims,
}
}
} }
@@ -230,6 +244,10 @@ export class FillRepeatChangeCommand extends Command {
...this.newPattern, ...this.newPattern,
}); });
object.set("fill", pattern); object.set("fill", pattern);
if (object.globalCompositeOperation_) {
object.globalCompositeOperation = object.globalCompositeOperation_;
object.globalCompositeOperation_ = null;
}
this.canvas.renderAll(); this.canvas.renderAll();
return true; return true;
} }
@@ -276,7 +294,7 @@ export class FillRepeatGapChangeCommand extends Command {
this.oldGapY = null; this.oldGapY = null;
} }
async execute(isUndo = false) { async execute(isCommand = true, isUndo = false) {
const { layer } = findLayerRecursively(this.layers.value, this.layerId); const { layer } = findLayerRecursively(this.layers.value, this.layerId);
if (!layer || !layer.fabricObjects || layer.fabricObjects.length === 0) { if (!layer || !layer.fabricObjects || layer.fabricObjects.length === 0) {
console.warn("图层不存在或没有 fabric 对象"); console.warn("图层不存在或没有 fabric 对象");
@@ -315,6 +333,10 @@ export class FillRepeatGapChangeCommand extends Command {
const fill = object.get("fill"); const fill = object.get("fill");
fill.source = imageAddGapToCanvas(image, object.fill_.gapX, object.fill_.gapY); fill.source = imageAddGapToCanvas(image, object.fill_.gapX, object.fill_.gapY);
object.set("fill", new fabric.Pattern(fill)); object.set("fill", new fabric.Pattern(fill));
if (isCommand && object.globalCompositeOperation_) {
object.globalCompositeOperation = object.globalCompositeOperation_;
object.globalCompositeOperation_ = null;
}
this.canvas.renderAll(); this.canvas.renderAll();
return true; return true;
} }
@@ -324,7 +346,7 @@ export class FillRepeatGapChangeCommand extends Command {
console.warn("没有旧间隙可恢复"); console.warn("没有旧间隙可恢复");
return false; return false;
} }
await this.execute(true); await this.execute(true, true);
return true; return true;
} }

View File

@@ -280,8 +280,13 @@ export class PasteLayerCommand extends Command {
isCut: undefined, isCut: undefined,
serializedObjects: undefined, serializedObjects: undefined,
}; };
if(this.newLayer.isPrintTrims){
if (this.insertIndex !== undefined && this.insertIndex !== null) { this.layers.value.forEach((layer) => {
if (layer.isPrintTrimsGroup) {
layer.children.unshift(this.newLayer);
}
})
}else if (this.insertIndex !== undefined && this.insertIndex !== null) {
this.layers.value.splice(this.insertIndex, 0, this.newLayer); this.layers.value.splice(this.insertIndex, 0, this.newLayer);
} else { } else {
this.layers.value.push(this.newLayer); this.layers.value.push(this.newLayer);

View File

@@ -25,7 +25,7 @@ export class TransformCommand extends Command {
this.layerManager = options.layerManager; this.layerManager = options.layerManager;
this.layers = options.layers || null; this.layers = options.layers || null;
this.lastSelectLayerId = options.lastSelectLayerId || null; // 最后选择的图层ID this.lastSelectLayerId = options.lastSelectLayerId || null; // 最后选择的图层ID
this.isCommand = options.isCommand == undefined ? true : options.isCommand
const targetObject = const targetObject =
findObjectById(this.canvas, this.objectId)?.object || null; findObjectById(this.canvas, this.objectId)?.object || null;
@@ -189,6 +189,11 @@ export class TransformCommand extends Command {
object.set(key, value); object.set(key, value);
}); });
if(this.isCommand && object.globalCompositeOperation_){
object.globalCompositeOperation = object.globalCompositeOperation_;
object.globalCompositeOperation_ = null;
}
// 确保对象更新 // 确保对象更新
object.setCoords(); object.setCoords();
} }

View File

@@ -132,8 +132,8 @@
const offsetY = object.fill?.offsetY; const offsetY = object.fill?.offsetY;
const twidth = object.fill_?.width; const twidth = object.fill_?.width;
const theight = object.fill_?.height; const theight = object.fill_?.height;
const x = ((offsetX - (twidth * scale) / 2) * 100) / object.width; const x = ((offsetX + (twidth * scale) / 2) * 100) / object.width;
const y = ((offsetY - (theight * scale) / 2) * 100) / object.height; const y = ((offsetY + (theight * scale) / 2) * 100) / object.height;
return { x, y }; return { x, y };
}); });
const inputFillOffset = (e) => setFillOffset(e, true); const inputFillOffset = (e) => setFillOffset(e, true);
@@ -143,8 +143,8 @@
const object = props.object; const object = props.object;
const patternTransform = object.fill?.patternTransform; const patternTransform = object.fill?.patternTransform;
const scale = getTransformScaleAngle(patternTransform).scale; const scale = getTransformScaleAngle(patternTransform).scale;
const x = (left / 100) * object.width + (object.fill_?.width * scale) / 2; const x = (left / 100) * object.width - (object.fill_?.width * scale) / 2;
const y = (top / 100) * object.height + (object.fill_?.height * scale) / 2; const y = (top / 100) * object.height - (object.fill_?.height * scale) / 2;
emit(isInput ? "inputFillOffset" : "changeFillOffset", { x, y }); emit(isInput ? "inputFillOffset" : "changeFillOffset", { x, y });
}; };
</script> </script>

View File

@@ -126,8 +126,8 @@
:options="selectOptions" :options="selectOptions"
@change="(e) => changeFillRepeat(e, v)" @change="(e) => changeFillRepeat(e, v)"
:disabled=" :disabled="
v.layer?.metadata?.level2Type === v.layer?.metadata?.sourceData?.type ===
'Embroidery' 'trims'
" "
/> />
</div> </div>
@@ -315,6 +315,7 @@
layerManager: props.layerManager, layerManager: props.layerManager,
layers: layers, layers: layers,
lastSelectLayerId: lastSelectLayerId, lastSelectLayerId: lastSelectLayerId,
isCommand,
}); });
if (isCommand) { if (isCommand) {
props.commandManager.execute(cmd); props.commandManager.execute(cmd);
@@ -336,6 +337,7 @@
const finalState = computeAngleState(angle, obj, initialState); const finalState = computeAngleState(angle, obj, initialState);
transformObject(obj, initialState, finalState, false); transformObject(obj, initialState, finalState, false);
if (!obj.hasOwnProperty("oldState")) obj.oldState = initialState; if (!obj.hasOwnProperty("oldState")) obj.oldState = initialState;
props.canvasManager.beforeChangeCanvas([obj]);
}; };
const changeAngle = (angle, obj) => { const changeAngle = (angle, obj) => {
var initialState; var initialState;
@@ -428,6 +430,7 @@
}); });
obj.set("fill", pattern); obj.set("fill", pattern);
props.canvas.renderAll(); props.canvas.renderAll();
props.canvasManager.beforeChangeCanvas([obj]);
}; };
const changeFillAngle = (angle, obj) => { const changeFillAngle = (angle, obj) => {
const fill = obj.get("fill"); const fill = obj.get("fill");
@@ -447,6 +450,7 @@
}); });
obj.set("fill", pattern); obj.set("fill", pattern);
props.canvas.renderAll(); props.canvas.renderAll();
props.canvasManager.beforeChangeCanvas([obj]);
}; };
const changeFillOffset = (value, obj) => { const changeFillOffset = (value, obj) => {
const pattern = new fabric.Pattern({ const pattern = new fabric.Pattern({
@@ -466,6 +470,7 @@
}); });
obj.set("fill", pattern); obj.set("fill", pattern);
props.canvas.renderAll(); props.canvas.renderAll();
props.canvasManager.beforeChangeCanvas([obj]);
}; };
const changeFillScale = (scale, obj) => { const changeFillScale = (scale, obj) => {
const fill = obj.get("fill"); const fill = obj.get("fill");
@@ -498,7 +503,8 @@
newGapY: gapY, newGapY: gapY,
record: true, record: true,
}); });
cmd.execute(); cmd.execute(false);
props.canvasManager.beforeChangeCanvas([obj]);
}; };
const changeFillGap = (gapX, gapY, obj) => { const changeFillGap = (gapX, gapY, obj) => {
if (obj.oldFill_) { if (obj.oldFill_) {

View File

@@ -896,7 +896,7 @@ const changeCanvas = async (command) => {
...command, // 传递完整的命令数据 ...command, // 传递完整的命令数据
}; };
emit("changeCanvas", commandData); emit("changeCanvas", commandData);
canvasManager.changeCanvas(commandData); canvasManager.changeCanvas();
if ((command.canUndo || command.canRedo) && props.enabledRedGreenMode) { if ((command.canUndo || command.canRedo) && props.enabledRedGreenMode) {
setTimeout(async () => { setTimeout(async () => {
try { try {
@@ -991,7 +991,7 @@ defineExpose({
}, },
updateOtherLayers: async (otherData) => { updateOtherLayers: async (otherData) => {
await new Promise((resolve) => optimizeCanvasRendering(canvasManager.canvas, resolve)); await new Promise((resolve) => optimizeCanvasRendering(canvasManager.canvas, resolve));
await canvasManager?.createOtherLayers?.(otherData, true); await canvasManager?.createOtherLayers?.(otherData);
layerManager.activeLayerId.value = "" layerManager.activeLayerId.value = ""
layerManager?.sortLayers(); layerManager?.sortLayers();
await layerManager?.updateLayersObjectsInteractivity?.(true); await layerManager?.updateLayersObjectsInteractivity?.(true);

View File

@@ -74,6 +74,7 @@ export class CanvasManager {
this.props = options.props || {}; this.props = options.props || {};
this.emit = options.emit || (() => {}); this.emit = options.emit || (() => {});
this.awaitCanvasRun = null; this.awaitCanvasRun = null;
this.canvasChangeing = false;
// 初始化画布 // 初始化画布
this.initializeCanvas(); this.initializeCanvas();
} }
@@ -338,6 +339,7 @@ export class CanvasManager {
setupCanvasEvents(activeElementId, layerManager) { setupCanvasEvents(activeElementId, layerManager) {
// 创建画布事件管理器 // 创建画布事件管理器
this.eventManager = new CanvasEventManager(this.canvas, { this.eventManager = new CanvasEventManager(this.canvas, {
canvasManager: this,
toolManager: this.toolManager, toolManager: this.toolManager,
animationManager: this.animationManager, animationManager: this.animationManager,
thumbnailManager: this.thumbnailManager, thumbnailManager: this.thumbnailManager,
@@ -868,9 +870,9 @@ backgroundObject.scaleY,'CanvasManager resetCanvasSizeByFixedLayer')
return layerObjectByLayerId; return layerObjectByLayerId;
} }
getObjectsByIds(ids){ getObjectsByIdOrLayerId(ids){
const objects = this.canvas.getObjects().filter((obj) => { const objects = this.canvas.getObjects().filter((obj) => {
return ids.includes(obj.id); return ids.includes(obj.id) || ids.includes(obj.layerId);
}); });
return objects; return objects;
} }
@@ -1147,7 +1149,7 @@ backgroundObject.scaleY,'CanvasManager resetCanvasSizeByFixedLayer')
const glayer = this.layerManager.getLayerById(SpecialLayerId.SPECIAL_GROUP); const glayer = this.layerManager.getLayerById(SpecialLayerId.SPECIAL_GROUP);
if(!glayer) return Promise.reject("印花和元素图层组不存在"); if(!glayer) return Promise.reject("印花和元素图层组不存在");
const ids = glayer.children.map((v) => v.id); const ids = glayer.children.map((v) => v.id);
const objects = this.getObjectsByIds(ids); const objects = this.getObjectsByIdOrLayerId(ids);
const fixedLayerObj = this.getFixedLayerObject(); const fixedLayerObj = this.getFixedLayerObject();
if(!fixedLayerObj) return Promise.reject("固定图层不存在"); if(!fixedLayerObj) return Promise.reject("固定图层不存在");
const flWidth = fixedLayerObj.width const flWidth = fixedLayerObj.width
@@ -1158,8 +1160,9 @@ backgroundObject.scaleY,'CanvasManager resetCanvasSizeByFixedLayer')
const flScaleY = fixedLayerObj.scaleY const flScaleY = fixedLayerObj.scaleY
const prints = []; const prints = [];
const trims = []; const trims = [];
objects.forEach((v) => { objects.forEach((v, i) => {
const sourceData = glayer.children.find((v_) => v_.id === v.id)?.metadata?.sourceData; const label = glayer.children.find((v_) => (v_.id === v.layerId || v_.id === v.id));
const sourceData = label?.metadata?.sourceData;
if(!sourceData) return; if(!sourceData) return;
const obj = { const obj = {
ifSingle: typeof v.fill === "string", ifSingle: typeof v.fill === "string",
@@ -1171,7 +1174,7 @@ backgroundObject.scaleY,'CanvasManager resetCanvasSizeByFixedLayer')
scale: [0, 0], scale: [0, 0],
angle: v.angle, angle: v.angle,
name: sourceData.name, name: sourceData.name,
priority: sourceData.priority, priority: i + 1,
object:{ object:{
top: 0, top: 0,
left: 0, left: 0,
@@ -1220,8 +1223,8 @@ backgroundObject.scaleY,'CanvasManager resetCanvasSizeByFixedLayer')
let scaleY = scale * 5 * v.fill_.height / flHeight; let scaleY = scale * 5 * v.fill_.height / flHeight;
let scaleXY = flWidth > flHeight ? scaleX : scaleY; let scaleXY = flWidth > flHeight ? scaleX : scaleY;
let left = fill.offsetX - v.fill_.width * scale / 2; let left = fill.offsetX + v.fill_.width * scale / 2;
let top = fill.offsetY - v.fill_.height * scale / 2; let top = fill.offsetY + v.fill_.height * scale / 2;
obj.scale = [scaleXY, scaleXY]; obj.scale = [scaleXY, scaleXY];
obj.angle = angle; obj.angle = angle;
@@ -1237,8 +1240,8 @@ backgroundObject.scaleY,'CanvasManager resetCanvasSizeByFixedLayer')
} }
}) })
// prints.sort((a, b) => a.ifSingle ? 1 : -1); // prints.sort((a, b) => a.ifSingle ? 1 : -1);
prints.forEach((v, i) => v.priority = i + 1); // prints.forEach((v, i) => v.priority = i + 1);
trims.forEach((v, i) => v.priority = i + 1); // trims.forEach((v, i) => v.priority = i + 1);
return {prints, trims}; return {prints, trims};
} }
@@ -1465,9 +1468,12 @@ backgroundObject.scaleY,'CanvasManager resetCanvasSizeByFixedLayer')
} }
/** 修复JSON数据中的ID丢失问题 */ /** 修复JSON数据中的ID丢失问题 */
FixJsonIdLoss(json){ FixJsonIdLoss(json){
const layerIds = [];
const layers = json?.layers || []; const layers = json?.layers || [];
const objects = json?.canvas?.objects || []; const objects = json?.canvas?.objects || [];
layers.forEach((layer) => { layers.forEach((layer) => {
layerIds.push(layer.id);
layer.children?.forEach((child) => layerIds.push(child.id));
if(!layer.fabricObjects?.[0]?.id && !layer.fabricObject?.id){ if(!layer.fabricObjects?.[0]?.id && !layer.fabricObject?.id){
const obj = objects?.find((o) => o.layerId === layer.id); const obj = objects?.find((o) => o.layerId === layer.id);
if(obj) { if(obj) {
@@ -1483,6 +1489,17 @@ backgroundObject.scaleY,'CanvasManager resetCanvasSizeByFixedLayer')
if (a.isBackground) return -1; if (a.isBackground) return -1;
if (b.isBackground) return 1; if (b.isBackground) return 1;
}) })
// 排除的对象id
const excludedObjects = [SpecialLayerId.PART_SELECTOR];
json.canvas.objects = objects.filter((v) => {
// 指定ID排除
if(excludedObjects.includes(v.id)) return false;
if(v.isBackground || v.isFixed) return true;
// 当前图层不存在当前对象
if(!layerIds.includes(v.layerId)) return false;
return true
});
} }
@@ -1490,7 +1507,7 @@ backgroundObject.scaleY,'CanvasManager resetCanvasSizeByFixedLayer')
* 创建其他图层:印花、颜色、元素... * 创建其他图层:印花、颜色、元素...
* @param {Object} otherData - 其他图层数据 * @param {Object} otherData - 其他图层数据
*/ */
async createOtherLayers(otherData, isUpdate = false) { async createOtherLayers(otherData) {
if (!otherData) return console.warn("otherData 为空不需要添加"); if (!otherData) return console.warn("otherData 为空不需要添加");
this.canvas.loading.value = true; this.canvas.loading.value = true;
let resolve = ()=>{}; let resolve = ()=>{};
@@ -1498,17 +1515,8 @@ backgroundObject.scaleY,'CanvasManager resetCanvasSizeByFixedLayer')
const otherData_ = JSON.parse(JSON.stringify(otherData)); const otherData_ = JSON.parse(JSON.stringify(otherData));
console.log("==========创建其他图层", otherData_); console.log("==========创建其他图层", otherData_);
const updateColor = !!otherData_.color;
const updateSpecialGroup = !!otherData_.printObject || !!otherData_.trims;
// 删除颜色图层和特殊组图层 // 删除颜色图层和特殊组图层
const ids = []; const ids = [SpecialLayerId.COLOR, SpecialLayerId.SPECIAL_GROUP];
if(isUpdate){
updateColor && ids.push(SpecialLayerId.COLOR)
updateSpecialGroup && ids.push(SpecialLayerId.SPECIAL_GROUP)
}else{
ids.push(SpecialLayerId.COLOR)
ids.push(SpecialLayerId.SPECIAL_GROUP)
}
this.layers.value = this.layers.value.filter((layer) => { this.layers.value = this.layers.value.filter((layer) => {
if(ids.includes(layer.id)){ if(ids.includes(layer.id)){
ids.push(...layer.children?.map((child) => child.id)); ids.push(...layer.children?.map((child) => child.id));
@@ -1516,11 +1524,15 @@ backgroundObject.scaleY,'CanvasManager resetCanvasSizeByFixedLayer')
} }
return true; return true;
}) })
this.canvas.getObjects().forEach((v) => ids.includes(v.id) && this.canvas.remove(v)) this.canvas.getObjects().forEach((v) => {
if(ids.includes(v.id) || ids.includes(v.layerId)){
this.canvas.remove(v)
}
})
// 创建颜色图层 // 创建颜色图层
otherData_.color && await this.createColorLayer(otherData_.color); await this.createColorLayer(otherData_.color);
const printTrimsLayers = [];// 印花和元素图层 const printTrimsLayers = [];// 印花和元素图层
const singleLayers = [];// 平铺图层 const singleLayers = [];// 平铺图层
@@ -1538,7 +1550,7 @@ backgroundObject.scaleY,'CanvasManager resetCanvasSizeByFixedLayer')
trims.type = "trims"; trims.type = "trims";
printTrimsLayers.unshift({...trims}); printTrimsLayers.unshift({...trims});
}) })
if(isUpdate ? updateSpecialGroup : true){ if(printTrimsLayers.length || singleLayers.length){
await this.createPrintTrimsLayers(printTrimsLayers, singleLayers); await this.createPrintTrimsLayers(printTrimsLayers, singleLayers);
} }
await this.changeCanvas(); await this.changeCanvas();
@@ -1669,8 +1681,8 @@ backgroundObject.scaleY,'CanvasManager resetCanvasSizeByFixedLayer')
let opacity = 1 let opacity = 1
let flipX = false; let flipX = false;
let flipY = false; let flipY = false;
let blendMode = BlendMode.MULTIPLY; let blendMode = BlendMode.NORMAL;
if(item.type === "trims") blendMode = BlendMode.NORMAL;// 元素正常 // if(item.type === "trims") blendMode = BlendMode.NORMAL;// 元素正常
if(item.object){ if(item.object){
opacity = item.object.opacity opacity = item.object.opacity
flipX = item.object.flipX flipX = item.object.flipX
@@ -1695,7 +1707,7 @@ backgroundObject.scaleY,'CanvasManager resetCanvasSizeByFixedLayer')
hasBorders: true, hasBorders: true,
isPrintTrims: true, isPrintTrims: true,
}); });
this.canvas.add(image); // this.canvas.add(image);
let layer = createLayer({ let layer = createLayer({
id: id, id: id,
name: name, name: name,
@@ -1706,7 +1718,8 @@ backgroundObject.scaleY,'CanvasManager resetCanvasSizeByFixedLayer')
isPrintTrims: true, isPrintTrims: true,
blendMode: blendMode, blendMode: blendMode,
fabricObjects: [image.toObject(["id", "layerId", "layerName"])], fabricObjects: [image.toObject(["id", "layerId", "layerName"])],
metadata: {sourceData: item, level2Type: item.level2Type}, metadata: {sourceData: item},
object: image,
}) })
children.push(layer); children.push(layer);
}; };
@@ -1730,8 +1743,8 @@ backgroundObject.scaleY,'CanvasManager resetCanvasSizeByFixedLayer')
let scaleX_ = flWidth / image.width * (item.scale?.[0] || 1) / 5; let scaleX_ = flWidth / image.width * (item.scale?.[0] || 1) / 5;
let scaleY_ = flHeight / image.height * (item.scale?.[1] || 1) / 5; let scaleY_ = flHeight / image.height * (item.scale?.[1] || 1) / 5;
let scale = flWidth > flHeight ? scaleX_ : scaleY_; let scale = flWidth > flHeight ? scaleX_ : scaleY_;
let offsetX = (item.location?.[0] || 0) + image.width * scale / 2 let offsetX = (item.location?.[0] || 0) - image.width * scale / 2
let offsetY = (item.location?.[1] || 0) + image.height * scale / 2 let offsetY = (item.location?.[1] || 0) - image.height * scale / 2
let top = flTop - flHeight * flScaleY / 2 let top = flTop - flHeight * flScaleY / 2
let left = flLeft - flWidth * flScaleX / 2 let left = flLeft - flWidth * flScaleX / 2
let scaleX = flScaleX let scaleX = flScaleX
@@ -1743,7 +1756,7 @@ backgroundObject.scaleY,'CanvasManager resetCanvasSizeByFixedLayer')
let fillSource = image let fillSource = image
let flipX = false; let flipX = false;
let flipY = false; let flipY = false;
let blendMode = BlendMode.MULTIPLY; let blendMode = BlendMode.NORMAL;
let fill_repeat = "repeat" let fill_repeat = "repeat"
if(item.object){ if(item.object){
top += item.object.top * flScaleY top += item.object.top * flScaleY
@@ -1754,7 +1767,7 @@ backgroundObject.scaleY,'CanvasManager resetCanvasSizeByFixedLayer')
angle = item.object.angle angle = item.object.angle
flipX = item.object.flipX flipX = item.object.flipX
flipY = item.object.flipY flipY = item.object.flipY
blendMode = item.object.blendMode || BlendMode.MULTIPLY; if(item.object.blendMode) blendMode = item.object.blendMode;
gapX = item.object.gapX gapX = item.object.gapX
gapY = item.object.gapY gapY = item.object.gapY
fillSource = imageAddGapToCanvas(image, gapX, gapY); fillSource = imageAddGapToCanvas(image, gapX, gapY);
@@ -1791,18 +1804,19 @@ backgroundObject.scaleY,'CanvasManager resetCanvasSizeByFixedLayer')
}, },
isPrintTrims: true, isPrintTrims: true,
}); });
this.canvas.add(rect); // this.canvas.add(rect);
let layer = createLayer({ let layer = createLayer({
id: id, id: id,
name: name, name: name,
type: LayerType.BITMAP, type: LayerType.BITMAP,
visible: true, visible: true,
locked: true, locked: false,
opacity: opacity, opacity: opacity,
isPrintTrims: true, isPrintTrims: true,
blendMode: BlendMode.MULTIPLY, blendMode: blendMode,
fabricObjects: [rect.toObject(["id", "layerId", "layerName"])], fabricObjects: [rect.toObject(["id", "layerId", "layerName"])],
metadata: {sourceData: item}, metadata: {sourceData: item},
object: rect,
}) })
children.push(layer); children.push(layer);
}; };
@@ -1819,6 +1833,13 @@ backgroundObject.scaleY,'CanvasManager resetCanvasSizeByFixedLayer')
// children.push(layer); // children.push(layer);
// } // }
if(children.length === 0) return; if(children.length === 0) return;
// 印花元素排序
if(new Set(children.map(v => v.metadata.sourceData.priority)).size === children.length){
children.sort((a, b) => b.metadata.sourceData.priority - a.metadata.sourceData.priority);
}
children.forEach(layer => {
this.canvas.add(layer.object);
});
const groupRect = new fabric.Rect({}); const groupRect = new fabric.Rect({});
await this.setObjecCliptInfo(groupRect); await this.setObjecCliptInfo(groupRect);
// 插入组图层 // 插入组图层
@@ -1841,12 +1862,13 @@ backgroundObject.scaleY,'CanvasManager resetCanvasSizeByFixedLayer')
/** /**
* 画布事件变更后 * 画布事件变更后
*/ */
async changeCanvas(){ async changeCanvas(fids = [], isBeforeChange = false){
if(!isBeforeChange) this.canvasChangeing = false;
const fixedLayerObj = this.getFixedLayerObject(); const fixedLayerObj = this.getFixedLayerObject();
if(!fixedLayerObj) return console.warn("固定图层对象不存在", fixedLayerObj) if(!fixedLayerObj) return console.warn("固定图层对象不存在", fixedLayerObj)
const colorObject = this.getLayerObjectById(SpecialLayerId.COLOR); const colorObject = this.getLayerObjectById(SpecialLayerId.COLOR);
if(colorObject){ if(colorObject){
const ids = this.layerManager.getBlendModeLayerIds(SpecialLayerId.SPECIAL_GROUP); const ids = this.layerManager.getBlendModeLayerIds(SpecialLayerId.SPECIAL_GROUP).filter(id => !fids.includes(id));
if(ids.length === 0){ if(ids.length === 0){
ids.unshift(SpecialLayerId.SPECIAL_GROUP); ids.unshift(SpecialLayerId.SPECIAL_GROUP);
await this.setObjecCliptInfo(colorObject); await this.setObjecCliptInfo(colorObject);
@@ -1866,6 +1888,24 @@ backgroundObject.scaleY,'CanvasManager resetCanvasSizeByFixedLayer')
this.canvas.renderAll(); this.canvas.renderAll();
} }
} }
/** 画布变更之前 */
async beforeChangeCanvas(objects){
if(this.canvasChangeing) return;
const ids = objects.filter(v => {
return v.isPrintTrims && v.globalCompositeOperation && v.globalCompositeOperation !== BlendMode.NORMAL
}).map(v => v.layerId);
if(ids.length > 0){
this.canvasChangeing = true;
this.canvas.getObjects().forEach(v => {
if(ids.includes(v.layerId)){
v.globalCompositeOperation_ = v.globalCompositeOperation;
v.globalCompositeOperation = BlendMode.NORMAL;
}
})
this.canvas.renderAll();
await this.changeCanvas(ids, true);
}
}
/** /**
* 缩放红绿图模式内容以适应当前画布大小 * 缩放红绿图模式内容以适应当前画布大小

View File

@@ -1750,7 +1750,7 @@ export class LayerManager {
layer.serializedObjects = layer.fabricObjects layer.serializedObjects = layer.fabricObjects
.map((obj) => { .map((obj) => {
if (typeof obj.toObject === "function") { if (typeof obj.toObject === "function") {
return obj.toObject(["id", "layerId", "layerName"]); return obj.toObject(["id", "layerId", "layerName", "fill_"]);
} }
return null; return null;
}) })
@@ -1763,7 +1763,7 @@ export class LayerManager {
if (layer.fabricObject) { if (layer.fabricObject) {
layer.serializedBackgroundObject = layer.serializedBackgroundObject =
typeof layer.fabricObject.toObject === "function" typeof layer.fabricObject.toObject === "function"
? layer.fabricObject.toObject(["id", "layerId", "layerName"]) ? layer.fabricObject.toObject(["id", "layerId", "layerName", "fill_"])
: null; : null;
delete layer.fabricObject; delete layer.fabricObject;
@@ -1793,7 +1793,7 @@ export class LayerManager {
return layer.fabricObjects return layer.fabricObjects
.map((obj) => { .map((obj) => {
const { object } = findObjectById(this.canvas, obj.id); const { object } = findObjectById(this.canvas, obj.id);
if (object) return object.toObject(["id", "layerId", "layerName"]); if (object) return object.toObject(["id", "layerId", "layerName", "fill_"]);
return false; return false;
}) })
.filter(Boolean); .filter(Boolean);
@@ -1839,6 +1839,7 @@ export class LayerManager {
// 存储到剪贴板 // 存储到剪贴板
this.clipboardData = layerCopy; this.clipboardData = layerCopy;
console.log("复制图层:", layerCopy);
const input = document.createElement("input"); const input = document.createElement("input");
input.value = "aida_copy_canvas_layer: " + layer.name; input.value = "aida_copy_canvas_layer: " + layer.name;
document.body.appendChild(input); document.body.appendChild(input);
@@ -1884,7 +1885,7 @@ export class LayerManager {
layerCopy.serializedObjects = layer.fabricObjects layerCopy.serializedObjects = layer.fabricObjects
.map((obj) => .map((obj) =>
typeof obj.toObject === "function" typeof obj.toObject === "function"
? obj.toObject(["id", "layerId", "layerName"]) ? obj.toObject(["id", "layerId", "layerName", "fill_"])
: null : null
) )
.filter(Boolean); .filter(Boolean);
@@ -1935,10 +1936,6 @@ export class LayerManager {
return this.clipboardData; return this.clipboardData;
} }
/**
* 粘贴图层
* @returns {string} 新创建的图层ID
*/
/** /**
* 粘贴图层 * 粘贴图层
* @returns {string} 新创建的图层ID * @returns {string} 新创建的图层ID

View File

@@ -1,6 +1,6 @@
import { fabric } from "fabric-with-all"; import { fabric } from "fabric-with-all";
import { traceImageContour, imageToCanvas } from "../utils/helper"; import { traceImageContour, imageToCanvas } from "../utils/helper";
import { OperationType } from "../utils/layerHelper"; import { OperationType, SpecialLayerId } from "../utils/layerHelper";
import { LassoCutoutCommand } from "../commands/LassoCutoutCommand"; import { LassoCutoutCommand } from "../commands/LassoCutoutCommand";
import addIcon from "@/assets/images/canvas/add.png"; import addIcon from "@/assets/images/canvas/add.png";
import removeIcon from "@/assets/images/canvas/remove.png"; import removeIcon from "@/assets/images/canvas/remove.png";
@@ -72,7 +72,7 @@ export class PartManager {
this.activeTool = this.toolManager.activeTool; this.activeTool = this.toolManager.activeTool;
this.rgba = { r: 0, g: 255, b: 0, a: 200 }; this.rgba = { r: 0, g: 255, b: 0, a: 200 };
this.partId = "part_selector"; this.partId = SpecialLayerId.PART_SELECTOR;
this.partGroup = null; // 当前选区对象 this.partGroup = null; // 当前选区对象
this.partCanvas = null;// 选区画布 this.partCanvas = null;// 选区画布
this.rectangleObject = null; // 矩形对象 this.rectangleObject = null; // 矩形对象

View File

@@ -6,6 +6,7 @@ import { OperationType, OperationTypes } from "../../utils/layerHelper";
export class CanvasEventManager { export class CanvasEventManager {
constructor(canvas, options = {}) { constructor(canvas, options = {}) {
this.canvas = canvas; this.canvas = canvas;
this.canvasManager = options.canvasManager;
this.toolManager = options.toolManager || null; this.toolManager = options.toolManager || null;
this.animationManager = options.animationManager; this.animationManager = options.animationManager;
this.thumbnailManager = options.thumbnailManager; this.thumbnailManager = options.thumbnailManager;
@@ -691,7 +692,9 @@ export class CanvasEventManager {
// 清除临时状态记录 // 清除临时状态记录
delete activeObj._initialTransformState; delete activeObj._initialTransformState;
} }
} }else{
this.canvasManager.changeCanvas();
}
if (this.thumbnailManager && e.target) { if (this.thumbnailManager && e.target) {
if (e.target.id) { if (e.target.id) {
@@ -975,6 +978,13 @@ export class CanvasEventManager {
// 添加调试日志(可选) // 添加调试日志(可选)
// console.log(`捕获对象 ${obj.id} (${obj.type}) 的初始变换状态`); // console.log(`捕获对象 ${obj.id} (${obj.type}) 的初始变换状态`);
} }
const arrs = [];
if (e.target._objects) {
e.target._objects.forEach((v) => arrs.push(v));
} else {
arrs.push(e.target);
}
this.canvasManager.beforeChangeCanvas(arrs);
} }
/** /**

View File

@@ -24,6 +24,7 @@ export const LayerType = {
export const SpecialLayerId = { export const SpecialLayerId = {
SPECIAL_GROUP: "group_special", // 特殊组 SPECIAL_GROUP: "group_special", // 特殊组
COLOR: "special_color", // 颜色图层 COLOR: "special_color", // 颜色图层
PART_SELECTOR: "part_selector", // 部件选择器图层
} }

View File

@@ -184,16 +184,17 @@ const createClippedDataURLByCanvas = async ({
// console.log("🖼️ 使用图像遮罩裁剪方法生成DataURL"); // console.log("🖼️ 使用图像遮罩裁剪方法生成DataURL");
// 使用优化后的边界计算,确保包含描边区域 // 使用优化后的边界计算,确保包含描边区域
// const optimizedBounds = calculateOptimizedBounds( const optimizedBounds = calculateOptimizedBounds(
// clippingObject, clippingObject,
// fabricObjects fabricObjects
// ); );
const optimizedBounds = { console.log("📐 优化后的选区边界框:", optimizedBounds);
left: clippingObject.left - clippingObject.width / 2, // const optimizedBounds = {
top: clippingObject.top - clippingObject.height / 2, // left: clippingObject.left - clippingObject.width / 2,
width: clippingObject.width, // top: clippingObject.top - clippingObject.height / 2,
height: clippingObject.height, // width: clippingObject.width,
} // height: clippingObject.height,
// }
// 使用高分辨率以保证质量 // 使用高分辨率以保证质量
const pixelRatio = window.devicePixelRatio || 1; const pixelRatio = window.devicePixelRatio || 1;

View File

@@ -203,9 +203,9 @@
let scaleY = ((cheight / image.height) * item.scale[1]) / 5; let scaleY = ((cheight / image.height) * item.scale[1]) / 5;
let scale = cwidth > cheight ? scaleX : scaleY; let scale = cwidth > cheight ? scaleX : scaleY;
let offsetX = let offsetX =
(item.location[0] * cwidth) / props.width + (image.width * scale) / 2; (item.location[0] * cwidth) / props.width - (image.width * scale) / 2;
let offsetY = let offsetY =
(item.location[1] * cheight) / props.height + (item.location[1] * cheight) / props.height -
(image.height * scale) / 2; (image.height * scale) / 2;
let angle = item.angle; let angle = item.angle;
let gapX = item.object.gapX; let gapX = item.object.gapX;

View File

@@ -342,32 +342,34 @@
level2Type: "Pattern", level2Type: "Pattern",
designType: "Library", designType: "Library",
path: "/src/assets/images/canvas/yinhua1.jpg", path: "/src/assets/images/canvas/yinhua1.jpg",
location: [250, 780], location: [800, 600],
scale: [1.2, 1.6], scale: [1, 1],
angle: 0, angle: 0,
priority: 1,
object: { object: {
top: 600, top: 300,
left: 800, left: 400,
scaleX: 0.5, scaleX: 0.5,
scaleY: 0.5, scaleY: 0.5,
opacity: 1, opacity: 1,
angle: 45, angle: 0,
flipX: false, flipX: false,
flipY: false, flipY: false,
blendMode: "multiply", // blendMode: "multiply",
gapX: 0, gapX: 0,
gapY: 0, gapY: 0,
}, },
}, },
// { {
// ifSingle: true, ifSingle: true,
// level2Type: "Pattern", level2Type: "Pattern",
// designType: "Library", designType: "Library",
// path: "/src/assets/images/canvas/yinhua1.jpg", path: "/src/assets/images/canvas/yinhua1.jpg",
// location: [550, 650], location: [550, 650],
// scale: [0.15, 0.2], scale: [0.15, 0.2],
// angle: 0, angle: 0,
// }, priority: 2,
},
// { // {
// ifSingle: true, // ifSingle: true,
// level2Type: "Pattern", // level2Type: "Pattern",
@@ -376,6 +378,7 @@
// location: [700, 400], // location: [700, 400],
// scale: [0.1, 0.133], // scale: [0.1, 0.133],
// angle: 0, // angle: 0,
// priority: 3,
// }, // },
], ],
}, },
@@ -411,6 +414,7 @@
:clothingMinIOPath="clothingMinIOPath" :clothingMinIOPath="clothingMinIOPath"
:clothingImageUrl="clothingImageUrl" :clothingImageUrl="clothingImageUrl"
:clothingImageUrl2="clothingImageUrlInit" :clothingImageUrl2="clothingImageUrlInit"
@canvasLoadJsonSuccess="canvasLoadJsonSuccess"
:config="editorConfig" :config="editorConfig"
:clothing-image-opts="{ :clothing-image-opts="{
imageMode: 'contains', // 设置底图包含在画布内 imageMode: 'contains', // 设置底图包含在画布内

View File

@@ -369,6 +369,7 @@ export default defineComponent({
// }else if(isCurrent){ // }else if(isCurrent){
// } // }
console.log(JSON.parse(JSON.stringify(detailData.selectDetail.color)),'=====')
color = list[i].color?.rgba?.r != null?`${list[i].color.rgba.r} ${list[i].color.rgba.g} ${list[i].color.rgba.b}`:'' color = list[i].color?.rgba?.r != null?`${list[i].color.rgba.r} ${list[i].color.rgba.g} ${list[i].color.rgba.b}`:''
gradient = list[i].gradient gradient = list[i].gradient
if((detailData.currentDetailType == 'sketch' && newData?.sketch) || detailData.isEditPattern.value == 'editSketch'){ if((detailData.currentDetailType == 'sketch' && newData?.sketch) || detailData.isEditPattern.value == 'editSketch'){
@@ -588,6 +589,7 @@ export default defineComponent({
segmentImage(detailData.selectDetail.maskUrl,partialDesign,size).then(async (rv)=>{ segmentImage(detailData.selectDetail.maskUrl,partialDesign,size).then(async (rv)=>{
let front = detailData.frontBack.front[detailData.imgDomIndex] let front = detailData.frontBack.front[detailData.imgDomIndex]
let back = detailData.frontBack.back[detailData.imgDomIndex] let back = detailData.frontBack.back[detailData.imgDomIndex]
if(front?.oldImageUrl)front.oldImageUrl = '' if(front?.oldImageUrl)front.oldImageUrl = ''
if(front?.oldMaskUrl)front.oldMaskUrl = '' if(front?.oldMaskUrl)front.oldMaskUrl = ''
if(back?.oldImageUrl)back.oldImageUrl = '' if(back?.oldImageUrl)back.oldImageUrl = ''
@@ -778,7 +780,6 @@ export default defineComponent({
if(canvasColor?.gradient){ if(canvasColor?.gradient){
color.gradient = canvasColor.gradient color.gradient = canvasColor.gradient
} }
console.log(color,'color')
} }
if(detailData.isEditPattern.value == 'canvasEditor'){ if(detailData.isEditPattern.value == 'canvasEditor'){

View File

@@ -12,6 +12,7 @@
is-edit is-edit
:clothingImageUrl="selectDetail.path" :clothingImageUrl="selectDetail.path"
:clothingImageUrl2="selectDetail.maskUrl || selectDetail.layersObject[0].maskUrl" :clothingImageUrl2="selectDetail.maskUrl || selectDetail.layersObject[0].maskUrl"
:clothingMinIOPath="selectDetail.minIOPath"
showFixedLayer showFixedLayer
:canvasJSON="canvasJSON" :canvasJSON="canvasJSON"
@canvasLoadJsonSuccess="canvasLoadJsonSuccess" @canvasLoadJsonSuccess="canvasLoadJsonSuccess"

View File

@@ -101,7 +101,6 @@ export default defineComponent({
}) })
watch(()=>colorData.selectColor,async (newVal,oldVal)=>{ watch(()=>colorData.selectColor,async (newVal,oldVal)=>{
if((newVal.rgba && newVal.rgba?.r != null) || newVal.gradient != null){ if((newVal.rgba && newVal.rgba?.r != null) || newVal.gradient != null){
console.log('=======',123)
let data :any = {} let data :any = {}
if(newVal.rgba?.r != null){ if(newVal.rgba?.r != null){
data = await getColorName(newVal.rgba) data = await getColorName(newVal.rgba)
@@ -119,7 +118,6 @@ export default defineComponent({
} }
store.commit('DesignDetail/setNewDetail',value) store.commit('DesignDetail/setNewDetail',value)
}else{ }else{
console.log('=======',123)
let value = { let value = {
data:{}, data:{},
str:'color' str:'color'
@@ -144,14 +142,13 @@ export default defineComponent({
let color = colorData.allBoardData.colorBoards?.[index] let color = colorData.allBoardData.colorBoards?.[index]
if(!color?.rgba && color?.rgbValue)color.rgba = color.rgbValue if(!color?.rgba && color?.rgbValue)color.rgba = color.rgbValue
if( if(
(colorData.allBoardData.colorBoards?.[index] && (colorData.allBoardData.colorBoards?.[index] && color?.rgba &&
colorData.selectDetail.color.rgba?.r == color?.rgba?.r && colorData.selectDetail.color.rgba?.r == color?.rgba?.r &&
colorData.selectDetail.color.rgba?.g == color?.rgba?.g && colorData.selectDetail.color.rgba?.g == color?.rgba?.g &&
colorData.selectDetail.color.rgba?.b == color?.rgba?.b) || colorData.selectDetail.color.rgba?.b == color?.rgba?.b) ||
(JSON.stringify(colorData.selectDetail.color.gradient) == JSON.stringify(color?.gradient) && colorData.selectDetail.color.gradient) ((JSON.stringify(colorData.selectDetail.color.gradient) == JSON.stringify(color?.gradient) && colorData.selectDetail.color.gradient))
){ ){
isNoSelect = true isNoSelect = true
console.log('=======',123)
colorData.selectColor = item colorData.selectColor = item
colorData.colorList.index = index colorData.colorList.index = index
}else if(color?.rgba?.r){ }else if(color?.rgba?.r){
@@ -175,7 +172,6 @@ export default defineComponent({
} }
colorData.colorList.list[newVal].push(item) colorData.colorList.list[newVal].push(item)
} }
console.log('=======',isNoSelect)
if(!isNoSelect){ if(!isNoSelect){
let color = colorData.selectDetail.newDetail?.color?.rgba?.r != null?colorData.selectDetail.newDetail?.color:colorData.selectDetail.color let color = colorData.selectDetail.newDetail?.color?.rgba?.r != null?colorData.selectDetail.newDetail?.color:colorData.selectDetail.color
let item:any = {} let item:any = {}

View File

@@ -89,8 +89,8 @@
<img crossOrigin="anonymous" :src="item?.path" :style="{transform:`rotateZ(${item.pattern?.transform?.rotateZ}deg)`}" class="designOpenrtion_imgItme" draggable="false"> <img crossOrigin="anonymous" :src="item?.path" :style="{transform:`rotateZ(${item.pattern?.transform?.rotateZ}deg)`}" class="designOpenrtion_imgItme" draggable="false">
</div> </div>
</div> </div>
<!-- <img :src="selectDetail.path" alt="" class="designOpenrtion_sketch" ref="sketchImg"> --> <!-- <img :src="selectDetail.path" alt="" class="designOpenrtion_sketch" ref="sketchImg" @load="()=>isSketchLoad = true"> -->
<img :src="stateOverallSingle == 'single'?(selectDetail.undividedLayer||selectDetail.path):(selectDetail.undividedLayerColor || selectDetail.path)" alt="" class="designOpenrtion_sketch" ref="sketchImg" @load="()=>isSketchLoad = true"> <img :src="(selectDetail.path)" alt="" class="designOpenrtion_sketch" ref="sketchImg" @load="()=>isSketchLoad = true">
<img :src="selectDetail.sketchMask" alt="" class="designOpenrtion_sketchMask" ref="sketchMask"> <img :src="selectDetail.sketchMask" alt="" class="designOpenrtion_sketchMask" ref="sketchMask">
<div class="designOpenrtion_btn" v-if="stateOverallSingle == 'single'" > <div class="designOpenrtion_btn" v-if="stateOverallSingle == 'single'" >
<ul v-for="item,index in printStyleList[type][stateOverallSingle]" :key="item" :class="{active:item?.pattern.designOpenrtionBtn?item?.pattern.designOpenrtionBtn:false}" class="designOpenrtion_Mousingle" :style="item?.pattern.style" @mousedown.stop="itemMoveMousedown(index,getMousePosition($event,false))" @touchstart.passive="itemMoveMousedown(index,getMousePosition($event,true))"> <ul v-for="item,index in printStyleList[type][stateOverallSingle]" :key="item" :class="{active:item?.pattern.designOpenrtionBtn?item?.pattern.designOpenrtionBtn:false}" class="designOpenrtion_Mousingle" :style="item?.pattern.style" @mousedown.stop="itemMoveMousedown(index,getMousePosition($event,false))" @touchstart.passive="itemMoveMousedown(index,getMousePosition($event,true))">
@@ -160,7 +160,6 @@ export default defineComponent({
selectDetail:computed(()=>store.state.DesignDetail.selectDetail), selectDetail:computed(()=>store.state.DesignDetail.selectDetail),
currentDetailType:computed(()=>store.state.DesignDetail.currentDetailType), currentDetailType:computed(()=>store.state.DesignDetail.currentDetailType),
currentPrintElement:computed(()=>store.state.DesignDetail.currentPrintElement), currentPrintElement:computed(()=>store.state.DesignDetail.currentPrintElement),
systemDesignerPercentage:0,
printStyleList:{ printStyleList:{
print:{ print:{
single:[], single:[],
@@ -174,7 +173,6 @@ export default defineComponent({
type:props.type, type:props.type,
imgDomIndex:-1, imgDomIndex:-1,
direction:'',//判断点的那条边 direction:'',//判断点的那条边
printZIndex:2,//印花优先级
sketchWH:{ sketchWH:{
width:0, width:0,
height:0, height:0,
@@ -225,6 +223,7 @@ export default defineComponent({
img.onload = ()=>{ img.onload = ()=>{
let imgScale = img.width / img.height let imgScale = img.width / img.height
let zoom = 2 let zoom = 2
console.log(editPrintElementData.sketchWH)
let width = editPrintElementData.sketchWH.width / zoom let width = editPrintElementData.sketchWH.width / zoom
let height = width / editPrintElementData.sketchWH.height let height = width / editPrintElementData.sketchWH.height
@@ -234,28 +233,46 @@ export default defineComponent({
let sketchH = editPrintElementData.sketchWH.height * editPrintElementData.sketchWH.scale[1] let sketchH = editPrintElementData.sketchWH.height * editPrintElementData.sketchWH.scale[1]
let x = sketchW / 2 - (sketchW * (width / editPrintElementData.sketchWH.width)/2) let x = sketchW / 2 - (sketchW * (width / editPrintElementData.sketchWH.width)/2)
let y = sketchH / 2 -(sketchH * height/2) let y = sketchH / 2 -(sketchH * height/2)
if(!editPrintElementData.stateOverallSingle == 'single'){ if(editPrintElementData.stateOverallSingle !== 'single'){
x = sketchW / 2 x = sketchW / 2
y = sketchH / 2 y = sketchH / 2
} }
let location = [x,y] let location = [x,y]
resolve({scale,location}) resolve({scale,location})
} }
img.src = item.url img.src = item.url || item.path
}) })
} }
const addPrintELement = async (data:any)=>{ const addPrintELement = async (data:any)=>{
if(!editPrintElementData.isSketchLoad)return if(!editPrintElementData.isSketchLoad)return
let {scale,location} = await setScaleLocation(data) let {scale,location} = await setScaleLocation(data)
let printIndex = 1
let allElementPrint = []
if(props.type == 'print'){
allElementPrint = [
...(editPrintElementData.printStyleList.print.single || []),
...(editPrintElementData.printStyleList.print.overall || []),
...(editPrintElementData.selectDetail.trims.prints || []),
]
}else{
allElementPrint = [
...(editPrintElementData.printStyleList.element.single || []),
...(editPrintElementData.selectDetail.printObject.prints || []),
]
}
if(allElementPrint.length >= 1){
printIndex = Math.max(...allElementPrint.map(item => Number(item.priority))) + 1
}
let item = { let item = {
angle:0, angle:0,
designType:data.designType, designType:data.designType,
ifSingle:editPrintElementData.stateOverallSingle == 'single', ifSingle:editPrintElementData.stateOverallSingle == 'single',
level2Type:data.level2Type, level2Type:data.level2Type,
location:editPrintElementData.stateOverallSingle == 'single'?location:[0,0], location:location,
// location:editPrintElementData.stateOverallSingle == 'single'?location:[0,0],
minIOPath:data.minIOPath || data.originalUrl, minIOPath:data.minIOPath || data.originalUrl,
path:data.url, path:data.url,
priority:editPrintElementData.printZIndex, priority:printIndex,
scale, scale,
globalCompositeOperation:'', globalCompositeOperation:'',
} }
@@ -286,7 +303,7 @@ export default defineComponent({
angle : item.pattern.transform.rotateZ, angle : item.pattern.transform.rotateZ,
// angle : !this.overallSingle ? 0:item.pattern.transform.rotateZ, // angle : !this.overallSingle ? 0:item.pattern.transform.rotateZ,
location : location, location : location,
priority:index, priority:item.priority,
scale: scale, scale: scale,
designType:item.designType, designType:item.designType,
level2Type:item.level2Type, level2Type:item.level2Type,
@@ -301,10 +318,14 @@ export default defineComponent({
if(editPrintElementData.printStyleList[props.type].single.length>0){ if(editPrintElementData.printStyleList[props.type].single.length>0){
sort(editPrintElementData.printStyleList[props.type].single) sort(editPrintElementData.printStyleList[props.type].single)
} }
if(editPrintElementData.printStyleList[props.type].overall.length>0){
sort(editPrintElementData.printStyleList[props.type].overall)
}
editPrintElementData.printStyleList[props.type].overall.forEach((item:any)=>{ editPrintElementData.printStyleList[props.type].overall.forEach((item:any)=>{
data.push(setData(item,index)) data.push(setData(item,index))
index++ index++
}) })
console.log(editPrintElementData.printStyleList[props.type].single)
editPrintElementData.printStyleList[props.type].single.forEach((item:any)=>{ editPrintElementData.printStyleList[props.type].single.forEach((item:any)=>{
data.push(setData(item,index)) data.push(setData(item,index))
index++ index++
@@ -336,10 +357,9 @@ export default defineComponent({
top = item.location[1] / editPrintElementData.sketchWH.scale[1] top = item.location[1] / editPrintElementData.sketchWH.scale[1]
}else{ }else{
//overall //overall
editPrintElementData.systemDesignerPercentage = item.scale[0]*1000
left = item.location[0] / editPrintElementData.sketchWH.scale[0] left = item.location[0] / editPrintElementData.sketchWH.scale[0]
top = item.location[1] / editPrintElementData.sketchWH.scale[1] top = item.location[1] / editPrintElementData.sketchWH.scale[1]
editPrintElementData.systemDesignerPercentage = item.scale?.[0]?item.scale[0]*100:30 item.scale = [1,1]
} }
let pattern = { let pattern = {
centers:{left:0,top:0}, centers:{left:0,top:0},
@@ -357,7 +377,6 @@ export default defineComponent({
}, },
designOpenrtionBtn:false designOpenrtionBtn:false
} }
editPrintElementData.printZIndex++
item.pattern = pattern item.pattern = pattern
if(item.object){ if(item.object){
@@ -372,7 +391,8 @@ export default defineComponent({
angle: 0, angle: 0,
flipX: false, flipX: false,
flipY: false, flipY: false,
blendMode: "multiply", // blendMode: "multiply",
blendMode: "source-over",
gapX: 0, gapX: 0,
gapY: 0, gapY: 0,
} }
@@ -399,51 +419,49 @@ export default defineComponent({
} }
} }
} }
const setPosition = ()=>{ const setPosition = async ()=>{
nextTick(()=>{ await new Promise<void>((resolve, reject) => {
let img = new Image nextTick(()=>{
img.onload = ()=>{ let img = new Image
// let sketchScale = editPrintElementData.selectDetail.layersObject[0].scale img.onload = ()=>{
let sketchScale = [1,1] // let sketchScale = editPrintElementData.selectDetail.layersObject[0].scale
let scaleX = img.width * sketchScale[0] / editPrintElementDom.sketchImg.offsetWidth let sketchScale = [1,1]
let scaleY = img.height * sketchScale[1] / editPrintElementDom.sketchImg.offsetHeight let scaleX = img.width * sketchScale[0] / editPrintElementDom.sketchImg.offsetWidth
let scaleY = img.height * sketchScale[1] / editPrintElementDom.sketchImg.offsetHeight
editPrintElementData.sketchWH = { editPrintElementData.sketchWH = {
width:editPrintElementDom.sketchImg.offsetWidth, width:editPrintElementDom.sketchImg.offsetWidth,
height:editPrintElementDom.sketchImg.offsetHeight, height:editPrintElementDom.sketchImg.offsetHeight,
scale:[scaleX,scaleY], scale:[scaleX,scaleY],
}
if(!editPrintElementData.selectDetail.printObject.prints)return
let state = true
// editPrintElementData.stateOverallSingle = 'single'
let arr:any = editPrintElementData.selectDetail.printObject.prints
if(props.type == 'element'){
arr = editPrintElementData.selectDetail.trims.prints
}
// if(editPrintElementData.selectDetail.newDetail?.[editPrintElementData.currentDetailType]){
// arr = editPrintElementData.selectDetail.newDetail[editPrintElementData.currentDetailType]
// }
if(arr && arr.length > 0){
editPrintElementData.printStyleList[props.type].single = []
editPrintElementData.printStyleList[props.type].overall = []
arr.forEach((item:any)=>{
// if(!item.ifSingle){
// editPrintElementData.stateOverallSingle = 'overall',
// state = false
// }
getItemPosition(item)
})
setItemPosition()
}
resolve('')
} }
if(!editPrintElementData.selectDetail.printObject.prints)return img.src = editPrintElementData.selectDetail.path
let state = true })
// editPrintElementData.stateOverallSingle = 'single'
let arr:any = editPrintElementData.selectDetail.printObject.prints
if(props.type == 'element'){
arr = editPrintElementData.selectDetail.trims.prints
}
// if(editPrintElementData.selectDetail.newDetail?.[editPrintElementData.currentDetailType]){
// arr = editPrintElementData.selectDetail.newDetail[editPrintElementData.currentDetailType]
// }
if(arr && arr.length > 0){
editPrintElementData.printStyleList[props.type].single = []
editPrintElementData.printStyleList[props.type].overall = []
arr.forEach((item:any)=>{
// if(!item.ifSingle){
// editPrintElementData.stateOverallSingle = 'overall',
// state = false
// }
getItemPosition(item)
})
setItemPosition()
}
// if(props.type == 'print'){
// editPrintElementData.overallSingle = state
// }
}
// undividedLayer
//计算宽高使用editPrintElementData.selectDetail.path
// img.src = editPrintElementData.selectDetail.path
img.src = editPrintElementData.selectDetail.undividedLayer?editPrintElementData.selectDetail.undividedLayer:editPrintElementData.selectDetail.path
}) })
} }
// watch(()=>editPrintElementData.selectDetail?.id,(newVal)=>{ // watch(()=>editPrintElementData.selectDetail?.id,(newVal)=>{
// if(!newVal)return // if(!newVal)return
@@ -517,7 +535,6 @@ export default defineComponent({
let scale = Number(editPrintElementDom.imgDom.children[0].style.transform?.split('scale(')[1]?.split(')')[0]) let scale = Number(editPrintElementDom.imgDom.children[0].style.transform?.split('scale(')[1]?.split(')')[0])
let rotateZ = Number(editPrintElementDom.imgDom.children[0].style.transform?.split('rotateZ(')[1]?.split('deg')[0]) let rotateZ = Number(editPrintElementDom.imgDom.children[0].style.transform?.split('rotateZ(')[1]?.split('deg')[0])
editPrintElementData.printStyleList[props.type][editPrintElementData.stateOverallSingle][index].pattern.designOpenrtionBtn = true editPrintElementData.printStyleList[props.type][editPrintElementData.stateOverallSingle][index].pattern.designOpenrtionBtn = true
// editPrintElementData.printStyleList[props.type][editPrintElementData.stateOverallSingle][index].pattern.style.zIndex = editPrintElementData.printZIndex++
editPrintElementData.printStyleList[props.type][editPrintElementData.stateOverallSingle][index].pattern.transform = { editPrintElementData.printStyleList[props.type][editPrintElementData.stateOverallSingle][index].pattern.transform = {
scale:scale, scale:scale,
rotateZ:rotateZ?rotateZ:0, rotateZ:rotateZ?rotateZ:0,
@@ -651,7 +668,6 @@ export default defineComponent({
top:editPrintElementDom.imgDom.offsetTop+'px', top:editPrintElementDom.imgDom.offsetTop+'px',
height:editPrintElementDom.imgDom.offsetHeight+'px', height:editPrintElementDom.imgDom.offsetHeight+'px',
width:editPrintElementDom.imgDom.offsetWidth+'px', width:editPrintElementDom.imgDom.offsetWidth+'px',
// zIndex:editPrintElementData.printZIndex
} }
document.removeEventListener('mousemove',sizeMouseMove) document.removeEventListener('mousemove',sizeMouseMove)
document.removeEventListener('touchmove',sizeTouchmove) document.removeEventListener('touchmove',sizeTouchmove)
@@ -803,7 +819,8 @@ export default defineComponent({
}; };
} }
}; };
elList[item.index].sort = moveIndex; let index = elList.findIndex((elListItem:any)=>item.id == elListItem.id)
elList[index].sort = moveIndex;
moveItem(); moveItem();
} }
} }
@@ -834,6 +851,7 @@ export default defineComponent({
collItemSize.elList.forEach((elItem:any)=>{ collItemSize.elList.forEach((elItem:any)=>{
let clothesIndex = arr.findIndex((item:any)=>item.uniqueId == elItem.uniqueId) let clothesIndex = arr.findIndex((item:any)=>item.uniqueId == elItem.uniqueId)
arr[clothesIndex].pattern.style.zIndex = elItem.sort arr[clothesIndex].pattern.style.zIndex = elItem.sort
arr[clothesIndex].priority = elItem.id.split('_')[0]
// let clothesId = editPrintElementData.designDetail.clothes[clothesIndex].id // let clothesId = editPrintElementData.designDetail.clothes[clothesIndex].id
// editPrintElementData.designDetail.clothes[clothesIndex].priority = elItem.sort // editPrintElementData.designDetail.clothes[clothesIndex].priority = elItem.sort
// let frontIndex = editPrintElementData.frontBack_.front.findIndex((item:any)=>item.id == clothesId) // let frontIndex = editPrintElementData.frontBack_.front.findIndex((item:any)=>item.id == clothesId)
@@ -854,7 +872,6 @@ export default defineComponent({
let arr:any = editPrintElementData.printStyleList[props.type][editPrintElementData.stateOverallSingle] let arr:any = editPrintElementData.printStyleList[props.type][editPrintElementData.stateOverallSingle]
arr.forEach((item,index) => {item.uniqueId = `${Date.now()}_${index}`}); arr.forEach((item,index) => {item.uniqueId = `${Date.now()}_${index}`});
const sortedArray = [...arr].sort((a, b) => a.priority - b.priority); const sortedArray = [...arr].sort((a, b) => a.priority - b.priority);
const sortMap = {} as any; const sortMap = {} as any;
sortedArray.forEach((item, index) => { sortedArray.forEach((item, index) => {
@@ -865,7 +882,8 @@ export default defineComponent({
el: elArr[i], el: elArr[i],
// sort: elArr.length - i -1, // sort: elArr.length - i -1,
sort: sortMap[arr[i].priority], sort: sortMap[arr[i].priority],
index: i, id: `${arr[i].priority}_${Date.now() + i}`,
// index: i,
uniqueId:arr[i]?.uniqueId || 99999, uniqueId:arr[i]?.uniqueId || 99999,
}); });
} }

File diff suppressed because one or more lines are too long

View File

@@ -81,7 +81,7 @@ const DesignDetail : Module<DesignDetail,RootState> = {
left:v.layersObject[i].position?.[1], left:v.layersObject[i].position?.[1],
width:v.layersObject[i].imageSize?.[0], width:v.layersObject[i].imageSize?.[0],
height:v.layersObject[i].imageSize?.[1], height:v.layersObject[i].imageSize?.[1],
transform:`scaleX(${v.layersObject[i].transpose?.[0] || 1}) scaleY(${v.layersObject[i].transpose?.[1] || 1}) rotate(${v.layersObject?.[i]?.rotate || 0}deg)`, transform:`rotate(${v.layersObject?.[i]?.rotate || 0}deg) scaleX(${v.layersObject[i].transpose?.[0] || 1}) scaleY(${v.layersObject[i].transpose?.[1] || 1})`,
} }
v.layersObject[i].centers={ v.layersObject[i].centers={
left:0, left:0,

View File

@@ -272,12 +272,12 @@ const navTypeList = (t)=>{
// }, // },
// { {
// icon:'fi fi-rr-puzzle-alt', icon:'fi fi-rr-puzzle-alt',
// value:'deReconstruction', value:'deReconstruction',
// label:t('Header.toolsDeReconstruction'), label:t('Header.toolsDeReconstruction'),
// router:'tools=deReconstruction' router:'tools=deReconstruction'
// }, },
{ {
icon:'fi fi-ss-box-open', icon:'fi fi-ss-box-open',
value:'toProduct', value:'toProduct',
@@ -294,18 +294,18 @@ const navTypeList = (t)=>{
label:t('Header.toolsToTransferPose'), label:t('Header.toolsToTransferPose'),
router:'tools=poseTransfer' router:'tools=poseTransfer'
}, },
// { {
// icon:'fi fi-rr-cubes', icon:'fi fi-rr-cubes',
// value:'patternMaking3D', value:'patternMaking3D',
// label:t('Header.toolsPatternMaking'), label:t('Header.toolsPatternMaking'),
// router:'tools=patternMaking3D' router:'tools=patternMaking3D'
// }, },
// { {
// icon:'fi fi-rr-pen-swirl', icon:'fi fi-rr-pen-swirl',
// value:'canvasUpload', value:'canvasUpload',
// label:t('Header.toolsCanvas'), label:t('Header.toolsCanvas'),
// router:'tools=canvasUpload' router:'tools=canvasUpload'
// }, },
] ]
}, },
library:{ library:{

View File

@@ -13,7 +13,10 @@
<div class="desc">AiDA Users Only</div> <div class="desc">AiDA Users Only</div>
</div> </div>
</div> </div>
<Success :isExpired="isExpired" v-if="isCompleted || isExpired" /> <Success
:isExpired="isExpired"
v-if="isCompleted || isExpired"
/>
<div <div
class="form-container" class="form-container"
v-if="!isCompleted && !isExpired" v-if="!isCompleted && !isExpired"
@@ -36,6 +39,7 @@
<div class="email-wrapper flex align-center"> <div class="email-wrapper flex align-center">
<a-input v-model:value="form.email" /> <a-input v-model:value="form.email" />
<div <div
v-if="!hasValidEmail"
class="code-btn" class="code-btn"
:class="{ disabled: isCountingDown }" :class="{ disabled: isCountingDown }"
@click="handleSendCode" @click="handleSendCode"
@@ -78,19 +82,29 @@
:disabled="readOnly" :disabled="readOnly"
v-model:value="form[item.key]" v-model:value="form[item.key]"
/> />
<a-select <div
class="select-container"
v-if="item.type === 'select'" v-if="item.type === 'select'"
:disabled="readOnly"
v-model:value="form[item.key]"
:options="genderOptions"
> >
<template #suffixIcon> <a-select
:disabled="readOnly"
v-model:value="form[item.key]"
:options="genderOptions"
>
<template #suffixIcon>
<!-- <img
class="arrow-down-icon"
src="@/assets/images/award/arrow_down.svg"
/> -->
</template>
</a-select>
<div class="arrow-wrapper flex flex-center">
<img <img
class="arrow-down-icon" class="arrow-down-icon"
src="@/assets/images/award/arrow_down.svg" src="@/assets/images/award/arrow_down.svg"
/> />
</template> </div>
</a-select> </div>
</a-form-item> </a-form-item>
</template> </template>
</div> </div>
@@ -157,19 +171,15 @@
:validate-trigger="[]" :validate-trigger="[]"
label="How will you use AiDA in your design process?" label="How will you use AiDA in your design process?"
> >
<div <div>
v-if="
pdfUploadStatus === 'idle' ||
pdfUploadStatus === 'error'
"
>
<a-upload-dragger <a-upload-dragger
v-model:fileList="pdfList" v-model:fileList="pdfList"
:disabled="readOnly" :disabled="readOnly"
:showUploadList="false" :maxCount="1"
@change="info => handleFileChange(info, 'pdf')" @change="info => handleFileChange(info, 'pdf')"
:customRequest="handleUploadPdf" :customRequest="handleUploadPdf"
:beforeUpload="beforeUploadPdf" :beforeUpload="beforeUploadPdf"
@remove="handleRemoveFile('pdf')"
accept=".pdf" accept=".pdf"
> >
<img <img
@@ -179,25 +189,48 @@
/> />
<p class="desc">Click to upload or drag and drop</p> <p class="desc">Click to upload or drag and drop</p>
<p class="limit">PDF file, max 20MB</p> <p class="limit">PDF file, max 20MB</p>
<template #itemRender="{ file, actions }">
<div
class="custom-upload-list flex align-center space-between"
>
<div class="flex align-center">
<SvgIcon name="CFile" />
{{ file.name }}
</div>
<div
@click="actions.remove"
class="delete-file"
title="delete file"
>
<SvgIcon
name="CDelete"
color="red"
/>
</div>
</div>
</template>
</a-upload-dragger> </a-upload-dragger>
</div> </div>
<div <div
v-else v-show="
pdfUploadStatus === 'uploading' ||
pdfUploadStatus === 'success'
"
class="uploading-container" class="uploading-container"
> >
<UploadStatus <UploadStatus
:status="pdfUploadStatus" :status="pdfUploadStatus"
type="pdf" type="pdf"
/> />
</div>
<div
class="progress-bar-container"
v-if="pdfUploadStatus === 'uploading'"
>
<div <div
class="progress-bar" class="progress-bar-container"
:style="{ width: `${uploadProgressPdf}%` }" v-if="pdfUploadStatus === 'uploading'"
></div> >
<div
class="progress-bar"
:style="{ width: `${uploadProgressPdf}%` }"
></div>
</div>
</div> </div>
</a-form-item> </a-form-item>
</div> </div>
@@ -209,23 +242,19 @@
:validate-trigger="[]" :validate-trigger="[]"
label="How will you use AiDA in your design process?" label="How will you use AiDA in your design process?"
> >
<div <div>
v-if="
videoUploadStatus === 'idle' ||
videoUploadStatus === 'error'
"
>
<a-upload-dragger <a-upload-dragger
v-model:fileList="videoList" v-model:fileList="videoList"
:showUploadList="false"
:disabled="readOnly" :disabled="readOnly"
:maxCount="1"
@change="info => handleFileChange(info, 'video')" @change="info => handleFileChange(info, 'video')"
:customRequest="handleUploadVideo" :customRequest="handleUploadVideo"
:beforeUpload="beforeUploadVideo" :beforeUpload="beforeUploadVideo"
@remove="handleRemoveFile('video')"
accept=".mp4,.mov" accept=".mp4,.mov"
> >
<img <img
src="@/assets/images/award/upload.png" src="@/assets/images/award/upload_video_icon.png"
alt="" alt=""
class="upload-icon" class="upload-icon"
/> />
@@ -233,25 +262,48 @@
<p class="limit"> <p class="limit">
Video file (MP4, MOV), 1080p, max 100MB Video file (MP4, MOV), 1080p, max 100MB
</p> </p>
<template #itemRender="{ file, actions }">
<div
class="custom-upload-list flex align-center space-between"
>
<div class="flex align-center">
<SvgIcon name="CFile" />
{{ file.name }}
</div>
<div
@click="actions.remove"
class="delete-file"
title="delete file"
>
<SvgIcon
name="CDelete"
color="red"
/>
</div>
</div>
</template>
</a-upload-dragger> </a-upload-dragger>
</div> </div>
<div <div
v-else v-show="
videoUploadStatus === 'success' ||
videoUploadStatus === 'uploading'
"
class="uploading-container" class="uploading-container"
> >
<UploadStatus <UploadStatus
:status="videoUploadStatus" :status="videoUploadStatus"
type="video" type="video"
/> />
</div>
<div
class="progress-bar-container"
v-if="videoUploadStatus === 'uploading'"
>
<div <div
class="progress-bar" class="progress-bar-container"
:style="{ width: `${uploadProgressVideo}%` }" v-if="videoUploadStatus === 'uploading'"
></div> >
<div
class="progress-bar"
:style="{ width: `${uploadProgressVideo}%` }"
></div>
</div>
</div> </div>
</a-form-item> </a-form-item>
</div> </div>
@@ -342,7 +394,7 @@
import { ref, onUnmounted, onMounted, computed } from 'vue' import { ref, onUnmounted, onMounted, computed } from 'vue'
import { debounce } from 'lodash-es' import { debounce } from 'lodash-es'
import type { Rule } from 'ant-design-vue/es/form' import type { Rule } from 'ant-design-vue/es/form'
import { message } from 'ant-design-vue' import { message, Upload } from 'ant-design-vue'
import { Https } from '@/tool/https' import { Https } from '@/tool/https'
import { useRoute } from 'vue-router' import { useRoute } from 'vue-router'
import type { UploadChangeParam } from 'ant-design-vue' import type { UploadChangeParam } from 'ant-design-vue'
@@ -359,7 +411,7 @@
const route = useRoute() const route = useRoute()
const isCompleted = ref(false) const isCompleted = ref(false)
const hasValidEmail = ref(false)
const readOnly = computed(() => { const readOnly = computed(() => {
if (route.query.id && !hasValidEmail.value) { if (route.query.id && !hasValidEmail.value) {
return true return true
@@ -383,7 +435,10 @@
designDescription: '', designDescription: '',
pdfPath: '', pdfPath: '',
videoPath: '', videoPath: '',
secureToken: null secureToken: ''
})
const hasValidEmail = computed(() => {
return !!form.value.secureToken
}) })
// 验证码输入组件引用 // 验证码输入组件引用
@@ -620,7 +675,6 @@
console.log('coderes', res) console.log('coderes', res)
form.value.secureToken = res.data.secureToken form.value.secureToken = res.data.secureToken
hasValidEmail.value = true
message.success('Verification successful!') message.success('Verification successful!')
showModal.value = false showModal.value = false
@@ -675,7 +729,7 @@
const beforeUploadFile = (type: FileType, file: File) => { const beforeUploadFile = (type: FileType, file: File) => {
if (!hasValidEmail.value) { if (!hasValidEmail.value) {
message.error('Please verify your email first') message.error('Please verify your email first')
return false return Upload.LIST_IGNORE
} }
let maxSize: number let maxSize: number
let allowedExtensions: string[] let allowedExtensions: string[]
@@ -693,7 +747,7 @@
allowedMimeTypes = ['video/mp4', 'video/quicktime'] allowedMimeTypes = ['video/mp4', 'video/quicktime']
errorMessage = 'Please upload a MP4 or MOV file only.' errorMessage = 'Please upload a MP4 or MOV file only.'
} else { } else {
return false return Upload.LIST_IGNORE
} }
// 验证文件类型 // 验证文件类型
@@ -705,18 +759,18 @@
if (!isValidType) { if (!isValidType) {
message.error(errorMessage) message.error(errorMessage)
// 从文件列表中移除 // 从文件列表中移除
if (type === 'pdf') { // if (type === 'pdf') {
const index = pdfList.value.findIndex(item => item.uid === file.uid) // const index = pdfList.value.findIndex(item => item.uid === file.uid)
if (index > -1) { // if (index > -1) {
pdfList.value.splice(index, 1) // pdfList.value.splice(index, 1)
} // }
} else { // } else {
const index = videoList.value.findIndex(item => item.uid === file.uid) // const index = videoList.value.findIndex(item => item.uid === file.uid)
if (index > -1) { // if (index > -1) {
videoList.value.splice(index, 1) // videoList.value.splice(index, 1)
} // }
} // }
return false // 阻止上传 return Upload.LIST_IGNORE
} }
// 验证文件大小 // 验证文件大小
@@ -726,21 +780,21 @@
`File size exceeds ${sizeLimit} limit. Please upload a smaller file.` `File size exceeds ${sizeLimit} limit. Please upload a smaller file.`
) )
// 从文件列表中移除 // 从文件列表中移除
if (type === 'pdf') { // if (type === 'pdf') {
const index = pdfList.value.findIndex(item => item.uid === file.uid) // const index = pdfList.value.findIndex(item => item.uid === file.uid)
if (index > -1) { // if (index > -1) {
pdfList.value.splice(index, 1) // pdfList.value.splice(index, 1)
} // }
} else { // } else {
const index = videoList.value.findIndex(item => item.uid === file.uid) // const index = videoList.value.findIndex(item => item.uid === file.uid)
if (index > -1) { // if (index > -1) {
videoList.value.splice(index, 1) // videoList.value.splice(index, 1)
} // }
} // }
return false // 阻止上传 return Upload.LIST_IGNORE
} }
return true // 允许上传 return true
} }
// PDF文件上传前验证 // PDF文件上传前验证
@@ -822,18 +876,22 @@
} }
const completeChunkUpload = async (type: FileType, file: File) => { const completeChunkUpload = async (type: FileType, file: File) => {
const endpoint = try {
type === 'pdf' const endpoint =
? Https.httpUrls.uploadPDFComplete type === 'pdf'
: Https.httpUrls.uploadVideoComplete ? Https.httpUrls.uploadPDFComplete
: Https.httpUrls.uploadVideoComplete
return Https.axiosPost(endpoint, { return Https.axiosPost(endpoint, {
uploadId: chunkUploadState[type].uploadId, uploadId: chunkUploadState[type].uploadId,
email: form.value.email, email: form.value.email,
fileName: file.name, fileName: file.name,
totalSize: file.size, totalSize: file.size,
secureToken: form.value.secureToken secureToken: form.value.secureToken
}) })
} catch (error) {
console.log('complete错误', error)
}
} }
type FileType = 'pdf' | 'video' type FileType = 'pdf' | 'video'
@@ -922,10 +980,12 @@
isUploadingPdf.value = false isUploadingPdf.value = false
uploadProgressPdf.value = 0 uploadProgressPdf.value = 0
pdfUploadStatus.value = 'error' pdfUploadStatus.value = 'error'
pdfList.value = []
} else { } else {
isUploadingVideo.value = false isUploadingVideo.value = false
uploadProgressVideo.value = 0 uploadProgressVideo.value = 0
videoUploadStatus.value = 'error' videoUploadStatus.value = 'error'
videoList.value = []
} }
} }
} }
@@ -940,6 +1000,18 @@
return handleUploadFile(option, 'video') return handleUploadFile(option, 'video')
} }
const handleRemoveFile = (type: 'video' | 'pdf') => {
if (type === 'pdf') {
form.value.pdfPath = ''
pdfUploadStatus.value = 'idle'
uploadProgressPdf.value = 0
} else if (type === 'video') {
form.value.videoPath = ''
videoUploadStatus.value = 'idle'
uploadProgressVideo.value = 0
}
}
const conditionsList = ref([ const conditionsList = ref([
{ {
check: false, check: false,
@@ -1047,8 +1119,7 @@
.desc { .desc {
color: #b10000; color: #b10000;
// font-family: 'Instrument'; font-family: 'Instrument';
font-family: revert-layer;
font-weight: 500; font-weight: 500;
font-size: 2.4rem; font-size: 2.4rem;
} }
@@ -1067,8 +1138,7 @@
.desc { .desc {
color: #b10000; color: #b10000;
// font-family: 'Instrument'; font-family: 'Instrument';
font-family: revert-layer;
font-weight: 500; font-weight: 500;
font-size: 2.4rem; font-size: 2.4rem;
} }
@@ -1139,14 +1209,26 @@
line-height: 6rem; line-height: 6rem;
} }
} }
:deep(.ant-select-arrow) { // :deep(.ant-select-arrow) {
height: 4rem;
width: 6.2rem; // justify-content: center;
justify-content: center; // display: flex;
display: flex; // align-items: center;
align-items: center; // }
border-left: 0.1rem solid #d5d5d5; }
} }
.select-container {
position: relative;
.arrow-wrapper {
position: absolute;
right: 0;
top: 50%;
transform: translateY(-50%);
width: 6rem;
height: 4rem;
box-sizing: border-box;
border-left: 0.1rem solid #d5d5d5;
pointer-events: none;
} }
} }
@@ -1240,9 +1322,10 @@
.upload-container { .upload-container {
margin-top: 6rem; margin-top: 6rem;
position: relative;
:deep(.ant-upload-drag) { :deep(.ant-upload-drag) {
height: 32rem; height: 32rem;
border-radius: 0.8rem;
.ant-upload-btn { .ant-upload-btn {
padding: 0; padding: 0;
@@ -1278,9 +1361,13 @@
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
border: 0.2rem solid #d5d5d5; border: 0.2rem dashed #d5d5d5;
border-radius: 0.8rem; border-radius: 0.8rem;
background-color: #fafafa; background-color: #fafafa;
position: absolute;
top: 0;
left: 0;
width: 100%;
.uploading-text { .uploading-text {
color: #585858; color: #585858;
@@ -1308,7 +1395,20 @@
} }
} }
} }
.custom-upload-list {
padding: 0.4rem 1rem;
margin-top: 1rem;
&:hover {
background-color: #f5f5f5;
border-radius: 0.4rem;
}
.c-svg {
width: fit-content;
}
.delete-file {
cursor: pointer;
}
}
.conditions { .conditions {
margin-top: 12rem; margin-top: 12rem;
@@ -1376,8 +1476,7 @@
} }
.desc { .desc {
// font-family: 'instrument'; font-family: 'Instrument';
font-family: revert-layer;
font-weight: 400; font-weight: 400;
font-size: 1.6rem; font-size: 1.6rem;
color: #6d6d6d; color: #6d6d6d;
@@ -1465,9 +1564,12 @@
</style> </style>
<style lang="less"> <style lang="less">
.code-modal { .code-modal {
.ant-modal-content .ant-modal-body { .ant-modal-content {
padding: 0; width: 60rem;
// width: 60rem; height: 49.4rem;
.ant-modal-body {
padding: 0;
}
} }
} }
</style> </style>

View File

@@ -1,28 +1,170 @@
<template> <template>
<div class="apply-container container flex flex-col" ref="applyRef"> <div
<div class="title" ref="applyTitleRef">How to Apply</div> class="apply-container flex flex-col"
<div class="sub-title" ref="applySubTitleRef">Requirments</div> id="apply"
<div class="requirments-list flex" ref="reqListRef"> ref="applyRef"
<div class="left flex flex-col space-between"> >
<div class="item-box" v-for="item in leftRequirment" :key="item.type"> <div
<div class="item-header flex align-center"> class="title animation-element"
<img src="@/assets/images/award/bloom_logo.png" class="logo" /> ref="applyTitleRef"
>
How to Apply
</div>
<div
class="sub-title animation-element"
ref="applySubTitleRef"
>
Step by step
</div>
<div
class="requirments-list flex flex-col"
ref="reqListRef"
>
<div class="top flex">
<div
class="item-box animation-element"
v-for="(item, index) in leftRequirment"
:key="item.type"
:ref="el => { if(el) itemRefs[index] = el }"
:style="{ background: item.background || '#fff' }"
>
<div class="item-header flex flex-center">
<div class="item-title">{{ item.type }}</div> <div class="item-title">{{ item.type }}</div>
</div> </div>
<div class="context" v-for="el in item.desc"> <div class="context-container flex flex-center">
{{ el }} <div
class="context"
v-for="el in item.desc"
>
{{ el }}
</div>
<div
class="list"
v-if="item.listTitle"
>
<div class="list-title">{{ item.listTitle }}</div>
<ul class="list-items">
<li
class="list-item"
v-for="el in item.list"
>
{{ el }}
</li>
</ul>
</div>
</div> </div>
</div> </div>
</div> </div>
<div class="right"> <div class="bottom flex">
<div class="item-box"> <div class="step-3 flex flex-col animation-element" ref="step3Ref">
<div class="item-box"> <div class="header">Step 3. Prepare Your Submission</div>
<div class="item-header flex align-center"> <div class="content flex">
<img src="@/assets/images/award/bloom_logo.png" class="logo" /> <div class="content-left flex flex-col space-between">
<div class="item-title">{{ rightRequirment.type }}</div> <div class="content-item">
<div class="item-header flex align-center">
<div class="point"></div>
<div>Process Video</div>
</div>
<div class="desc-wrapper flex flex-col space-between">
<div class="item-desc">
Include a screenrecorded video
<br />
your creative process
<br />
using AiDA.
<br />
</div>
<ul class="desc-lists">
<div class="desc-lists-title">
Video requirements:
</div>
<li>Format: MP4</li>
<li>Resolution: 1080×1920 px</li>
<li>Duration: Maximum 1 minute</li>
<li>File size: Maximum 20MB</li>
</ul>
</div>
</div>
<div class="content-item">
<div class="item-header flex align-center">
<div class="point"></div>
<div>File Name</div>
</div>
<div class="item-desc indent">
AiDAGlobalDesignAward
<br />
2026_[Your Full Name]
</div>
</div>
</div> </div>
<div class="context" v-for="el in rightRequirment.desc"> <div class="content-right">
{{ el }} <div class="content-item flex flex-col">
<div class="item-header flex align-center">
<div class="point"></div>
<div>Design Portfolio(PDF)</div>
</div>
<div
class="desc-wrapper flex-1 flex flex-col space-between"
>
<ul class="desc-lists">
<div class="desc-lists-title">
<p>
Submit one single PDF file that includes:
</p>
<p>Required structure:</p>
</div>
<li>Design title</li>
<li>Moodboard</li>
<li>Concept explanation</li>
<div>(How to used AiDA to develop design)</div>
</ul>
<ul class="desc-lists">
<div class="desc-lists-title">
<p>PDF requirements:</p>
</div>
<li>Maximum 15 pages</li>
<li>Maximum file size: 20MB</li>
<li>
Language: English or native language
<br />
with English translation
</li>
</ul>
</div>
</div>
</div>
</div>
</div>
<div class="step-4 animation-element" ref="step4Ref">
<div class="header flex flex-col flex-center">
<p>Step 4. Finalist Requirement</p>
<p class="sub-title">(for top 20 Designers)</p>
</div>
<div class="content">
<div class="content-item">
<div class="desc-wrapper flex-1 flex flex-col space-between">
<ul class="desc-lists">
<div class="desc-lists-title">
The 20 finalists will be required to
<br />
submit physical garments for final
<br />
evaluation
</div>
<li>Number of pieces: 1</li>
<li>
Garments must be produced
<br />
based on the submitted
<br />
AiDA-generated designs
</li>
<li>
Shipping instructions will be provided by
Code-create
</li>
</ul>
</div>
</div> </div>
</div> </div>
</div> </div>
@@ -32,178 +174,375 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { nextTick, onBeforeUnmount, onMounted, ref } from 'vue' import { nextTick, onBeforeUnmount, onMounted, ref } from 'vue'
import { gsap } from 'gsap' import { gsap } from 'gsap'
const leftRequirment = ref([ const leftRequirment = ref([
{ {
type: 'Video', type: 'Step 1. Become an\nAiDA Subscriber',
desc: ['The process of doing design'] desc: [
}, 'All applicants must be active\nAiDA subscribers at the time of\nsubmission. You may subscribe\nunder either a monthly or yearly plan.'
{ ]
type: 'Design', },
desc: [ {
'Structure: design title, moodboard and elaboration (how will you use AiDA to design)', type: 'Step 2. Create Your Design Using AiDA',
'Design sketch: Maximum 4 outfit design with proposed materials' desc: [
] 'Applicants must create their\ndesigns exclusively using the\nAiDA platform. '
],
listTitle: 'Your work shold clearly demonstrate:',
list: [
'· How AiDA is used as a creative tool',
'· Your design concept and creative direction',
'· The intergration of AI and human creativity'
],
background: '#F9F9F9'
}
])
const applyRef = ref()
const applyTitleRef = ref()
const applySubTitleRef = ref()
const reqListRef = ref()
const itemRefs = ref<HTMLElement[]>([])
const step3Ref = ref()
const step4Ref = ref()
const hasPlayedAnim = ref(false)
let timeline: gsap.core.Timeline | null = null
let observer: IntersectionObserver | null = null
const setupApplyInitialState = () => {
// 设置标题和副标题的初始状态
const titleEls = [applyTitleRef.value, applySubTitleRef.value].filter(
Boolean
) as HTMLElement[]
if (titleEls.length) {
gsap.set(titleEls, {
opacity: 0,
scale: 0,
transformOrigin: '50% 50%'
})
}
// 设置步骤元素的初始状态
const allStepElements: HTMLElement[] = []
if (itemRefs.value && itemRefs.value.length > 0) {
allStepElements.push(...itemRefs.value)
}
if (step3Ref.value) {
allStepElements.push(step3Ref.value as HTMLElement)
}
if (step4Ref.value) {
allStepElements.push(step4Ref.value as HTMLElement)
}
if (allStepElements.length > 0) {
gsap.set(allStepElements, {
opacity: 0,
y: 50
})
}
} }
])
const rightRequirment = ref({ const initAnimations = () => {
type: 'Submission Format', if (hasPlayedAnim.value) return
desc: [
'Naming as “AiDA global award 2026_applicantname”',
'Mp4\n(1080x1920pixels/20mb within 1min)',
'Single PDF file\n(within 15 pages, maximum 20mb)',
'English or native language\nwith English translation'
]
})
const applyRef = ref<HTMLElement | null>(null) timeline = gsap.timeline({
const applyTitleRef = ref<HTMLElement | null>(null) defaults: { ease: 'back.out(1.7)' }
const applySubTitleRef = ref<HTMLElement | null>(null)
const reqListRef = ref<HTMLElement | null>(null)
const hasPlayedApplyAnim = ref(false)
let applyObserver: IntersectionObserver | null = null
const setupApplyInitialState = () => {
const titleEls = [applyTitleRef.value, applySubTitleRef.value].filter(
Boolean
) as HTMLElement[]
if (titleEls.length) {
gsap.set(titleEls, {
opacity: 0,
scale: 0,
transformOrigin: '50% 50%'
}) })
}
const headers = reqListRef.value?.querySelectorAll<HTMLElement>('.item-header')
const contexts = reqListRef.value?.querySelectorAll<HTMLElement>('.context')
gsap.set([headers, contexts], { opacity: 0 })
}
const playApplyAnimation = () => { if (applyTitleRef.value && applySubTitleRef.value) {
if (hasPlayedApplyAnim.value) return timeline.to([applyTitleRef.value, applySubTitleRef.value], {
const titleEls = [applyTitleRef.value, applySubTitleRef.value].filter( scale: 1,
Boolean
) as HTMLElement[]
const headers = reqListRef.value?.querySelectorAll<HTMLElement>('.item-header')
const contexts = reqListRef.value?.querySelectorAll<HTMLElement>('.context')
if (!titleEls.length) return
const tl = gsap.timeline({ defaults: { ease: 'power2.out' } })
tl.to(titleEls, {
opacity: 1,
scale: 1,
duration: 0.6,
ease: 'back.out(1.6)',
stagger: 0.1
})
if (headers?.length) {
tl.to(
headers,
{
opacity: 1, opacity: 1,
duration: 0.4, duration: 0.6,
stagger: 0.1 stagger: 0.1
}, })
'-=0.1' }
)
} const allStepElements: HTMLElement[] = []
if (contexts?.length) { if (itemRefs.value && itemRefs.value.length > 0) {
tl.to( allStepElements.push(...itemRefs.value)
contexts, }
{ if (step3Ref.value) {
allStepElements.push(step3Ref.value as HTMLElement)
}
if (step4Ref.value) {
allStepElements.push(step4Ref.value as HTMLElement)
}
if (allStepElements.length > 0) {
timeline.to(allStepElements, {
opacity: 1, opacity: 1,
duration: 0.4, y: 0,
stagger: 0.05 duration: 0.6,
}, stagger: 0.2
'-=0.05' }, '>')
) }
hasPlayedAnim.value = true
} }
hasPlayedApplyAnim.value = true onMounted(() => {
applyObserver?.disconnect() nextTick(() => {
} setupApplyInitialState()
observer = new IntersectionObserver(
onMounted(() => {
nextTick(() => {
setupApplyInitialState()
if ('IntersectionObserver' in window) {
applyObserver = new IntersectionObserver(
(entries) => { (entries) => {
entries.forEach((entry) => { entries.forEach((entry) => {
if (entry.isIntersecting) { if (entry.isIntersecting) {
playApplyAnimation() initAnimations()
observer?.disconnect()
} }
}) })
}, },
{ threshold: 0.25 } {
threshold: 0.3,
rootMargin: '0px 0px -100px 0px'
}
) )
if (applyRef.value) applyObserver.observe(applyRef.value)
} else { // Start observing the component root element
playApplyAnimation() if (applyRef.value) {
observer.observe(applyRef.value)
}
})
})
onBeforeUnmount(() => {
// Cleanup animation timeline
if (timeline) {
timeline.kill()
}
// Cleanup IntersectionObserver
if (observer) {
observer.disconnect()
} }
}) })
})
onBeforeUnmount(() => {
applyObserver?.disconnect()
})
</script> </script>
<style scoped lang="less"> <style scoped lang="less">
.apply-container { p {
flex: 1; margin: 0;
background: url('@/assets/images/award/apply_bg.png') no-repeat; padding: 0;
background-size: 100% 100%;
padding: 12.7rem 0 16.9rem;
.title {
text-align: center;
color: #232323;
font-family: 'PoppinsBold';
font-weight: 600;
font-size: 4rem;
margin-bottom: 3rem;
} }
.sub-title { ul {
text-align: center; margin: 0;
color: #b10000; padding: 0;
font-size: 3rem;
font-family: 'Arial';
font-weight: 400;
} }
.requirments-list { .animation-element{
will-change: opacity transform;
}
.apply-container {
flex: 1; flex: 1;
padding-left: 41.4rem; height: 143.3rem;
column-gap: 17.7rem; background: url('@/assets/images/award/apply_bg.png') no-repeat;
margin-top: 12rem; background-size: 100% 100%;
.left { padding: 12.7rem 21.4rem 12rem;
.title {
text-align: center;
color: #232323; color: #232323;
height: 100%; font-family: 'PoppinsBold';
font-weight: 600;
font-size: 4rem;
margin-bottom: 3rem;
} }
.item-box { .sub-title {
.item-header { text-align: center;
column-gap: 3.2rem; color: #b10000;
.item-title { font-size: 3rem;
color: #232323; font-family: 'Arial';
font-family: 'PoppinsBold'; font-weight: 400;
font-weight: 600; margin-bottom: 8.2rem;
font-size: 2.8rem; }
.requirments-list {
flex: 1;
row-gap: 8.2rem;
.top {
height: 27.4rem;
color: #585858;
column-gap: 4.6rem;
.item-box {
height: 27.4rem;
} }
} }
.context { .item-box {
margin-top: 4rem; border-radius: 0.8rem;
width: 46.8rem; &:nth-of-type(1) {
color: #585858; width: 47rem;
font-family: 'Arial'; flex-grow: initial;
font-weight: 400; }
line-height: 3rem; &:nth-of-type(2) {
font-size: 2.4rem; flex: 1;
padding-left: 5.6rem; }
white-space: pre-line; .item-header {
background-color: #424242;
border-radius: 0.8rem;
height: 7.8rem;
.item-title {
color: #fff;
font-family: 'PoppinsBold';
font-weight: 600;
font-size: 2.4rem;
text-align: center;
white-space: pre-line;
}
}
.context-container {
margin-top: 4rem;
column-gap: 7rem;
.list {
font-family: 'Instrument';
font-weight: 400;
font-size: 2.4rem;
line-height: 3rem;
}
}
.context {
// margin-top: 4rem;
// width: 46.8rem;
text-align: center;
color: #585858;
font-family: 'Arial';
font-weight: 400;
line-height: 3rem;
font-size: 2.4rem;
// padding-left: 5.6rem;
white-space: pre-line;
}
}
.bottom {
column-gap: 4.6rem;
height: 63.4rem;
.step-3 {
flex: 1;
}
.step-3,
.step-4 {
.header {
font-family: 'PoppinsBold';
font-weight: 600;
font-size: 2.4rem;
text-align: center;
height: 7.4rem;
line-height: 7.4rem;
color: #fff;
background-color: #b10000;
border-radius: 0.8rem;
}
.content {
padding: 4rem;
border-radius: 0.8rem;
column-gap: 6.4rem;
background: linear-gradient(
180deg,
#ffe8e8 0%,
#feefef 25%,
#f9f9f9 100%
);
flex: 1;
.content-left {
flex: 1;
}
.content-item {
.item-header {
column-gap: 2rem;
color: #585858;
font-family: 'InstrumentBold';
font-weight: 700;
font-size: 2.4rem;
line-height: 3rem;
.point {
width: 1.2rem;
height: 1.2rem;
background-color: #b10000;
border-radius: 50%;
}
}
.item-desc {
font-family: 'Instrument';
font-weight: 400;
font-size: 2.4rem;
color: #585858;
&.indent {
padding-left: 3.8rem;
line-height: 3rem;
padding-top: 2rem;
}
}
.desc-wrapper {
margin-top: 3rem;
/* 基线行高变量,供子元素计算方块垂直偏移以对齐首行 */
--desc-line-height: 3rem;
font-family: 'Instrument';
font-weight: 400;
color: #585858;
font-size: 2.4rem;
line-height: 3rem;
row-gap: 3rem;
.desc-lists {
/* 使用自定义方块代替浏览器 marker保证大小为 1rem 并与文字垂直居中 */
padding-left: 0;
list-style: none;
li {
list-style: none;
/* 使内容对齐到首行顶部,方块通过 margin-top 调整到首行中间 */
display: flex;
align-items: flex-start;
gap: 1rem;
padding: 0;
margin: 0.4rem 0;
&::before {
content: '';
/* 固定为 1rem 方块 */
width: 0.5rem;
height: 0.5rem;
background-color: #585858;
flex: 0 0 0.5rem;
border-radius: 0;
/* 让方块垂直居中于第一行文字:(line-height - square)/2 */
margin-top: calc(
(var(--desc-line-height, 3rem) - 1rem) / 2
);
}
}
}
}
}
.content-right {
.content-item {
height: 100%;
}
}
}
}
.step-4 {
width: 45.1rem;
.header {
color: #fff;
text-align: center;
background-color: #424242;
border-radius: 0.8rem;
height: 7.8rem;
white-space: pre-line;
font-family: 'PoppinsBold';
font-weight: 600;
font-size: 2.4rem;
line-height: 1.5;
.sub-title {
font-family: 'Poppins';
font-weight: 400;
font-size: 1.8rem;
color: #fff;
margin: 0;
}
}
.content {
background: #fff;
}
}
} }
} }
} }
}
</style> </style>

View File

@@ -1,5 +1,5 @@
<template> <template>
<div class="bloom container flex flex-col align-center"> <div class="bloom flex flex-col align-center">
<div <div
class="title" class="title"
ref="titleRef" ref="titleRef"
@@ -17,10 +17,34 @@
class="desc" class="desc"
ref="textRef" ref="textRef"
> >
Where imagination meets innovation, creativity blooms. This theme celebrates <p class="section-1">
AI as a catalyst for fashion design, allowing your vision to flourish beyond The
traditional boundaries. Let your ideas blossom into extraordinary designs that <span class="arial-bold">AiDA Global Design Award 2026</span>
merge human artistry with artificial intelligence. is an
<span class="arial-bold">international design competition</span>
hosted by
<span class="arial-bold">CodeCreate</span>
, a globally leading
<br />
<span class="arial-bold">AI fashion solutions provider,</span>
celebrating the future of creativity powered by artificial intelligence.
<br />
Bringing together designers from around the world, AiDA empowers AI as a
creative partnerpushing fashion beyond
<br />
traditional boundaries and unlocking new possibilities where technology
amplifies human imagination.
</p>
<p class="section-2">
Under the theme
<span class="arial-bold">
Where Imagination Meets Innovation, Creativity Blooms,
</span>
participants are invited to transform bold ideas
<br />
into extraordinary designs, seamlessly merging human artistry with
artificial intelligence to shape the next era of fashion.
</p>
</div> </div>
</div> </div>
</template> </template>
@@ -37,7 +61,9 @@
let bloomObserver: IntersectionObserver | null = null let bloomObserver: IntersectionObserver | null = null
const setupBloomInitialState = () => { const setupBloomInitialState = () => {
const titleEls = [titleRef.value, subtitleRef.value].filter(Boolean) as HTMLElement[] const titleEls = [titleRef.value, subtitleRef.value].filter(
Boolean
) as HTMLElement[]
if (titleEls.length) { if (titleEls.length) {
gsap.set(titleEls, { gsap.set(titleEls, {
opacity: 0, opacity: 0,
@@ -58,7 +84,9 @@
const playBloomAnimation = () => { const playBloomAnimation = () => {
if (hasPlayedBloomAnim.value) return if (hasPlayedBloomAnim.value) return
const titleEls = [titleRef.value, subtitleRef.value].filter(Boolean) as HTMLElement[] const titleEls = [titleRef.value, subtitleRef.value].filter(
Boolean
) as HTMLElement[]
const textEl = textRef.value const textEl = textRef.value
if (!titleEls.length || !textEl) return if (!titleEls.length || !textEl) return
@@ -81,7 +109,7 @@
duration: 0.3, duration: 0.3,
ease: 'power2.out' ease: 'power2.out'
}, },
'+=0.12' '-=0.3'
) )
tl.to( tl.to(
textEl, textEl,
@@ -103,8 +131,8 @@
setupBloomInitialState() setupBloomInitialState()
if ('IntersectionObserver' in window) { if ('IntersectionObserver' in window) {
bloomObserver = new IntersectionObserver( bloomObserver = new IntersectionObserver(
(entries) => { entries => {
entries.forEach((entry) => { entries.forEach(entry => {
if (entry.isIntersecting) { if (entry.isIntersecting) {
playBloomAnimation() playBloomAnimation()
} }
@@ -125,11 +153,19 @@
onBeforeUnmount(() => { onBeforeUnmount(() => {
bloomObserver?.disconnect() bloomObserver?.disconnect()
}) })
</script> </script>
<style scoped lang="less"> <style scoped lang="less">
p {
margin: 0;
padding: 0;
}
.arial-bold {
font-family: 'ArialBold';
font-weight: 700;
}
.bloom { .bloom {
height: 108rem;
padding-top: 12.8rem; padding-top: 12.8rem;
font-family: 'Poppins'; font-family: 'Poppins';
background: url('@/assets/images/award/bloom_bg.png') no-repeat; background: url('@/assets/images/award/bloom_bg.png') no-repeat;
@@ -149,13 +185,16 @@
} }
.desc { .desc {
font-family: 'Arial'; font-family: 'Arial';
font-size: 2.8rem; font-weight: 400;
font-size: 2.4rem;
color: #585858; color: #585858;
text-align: center; text-align: center;
padding: 0 21.5rem; padding: 0 21.5rem;
line-height: 4.5rem; line-height: 4.5rem;
margin-bottom: 12.3rem; margin-bottom: 12.3rem;
.section-2{
margin-top: 4rem;
}
} }
} }
</style> </style>

View File

@@ -24,10 +24,13 @@
> >
<div <div
class="prize-item flex flex-col flex-center" class="prize-item flex flex-col flex-center"
:class="{ smaller: item.smaller }"
v-for="item in prizes" v-for="item in prizes"
:key="item.name" :key="item.name"
> >
<div class="prize-money">{{ item.money }}</div> <div class="prize-money">
{{ item.money }}
</div>
<div class="prize-name">{{ item.name }}</div> <div class="prize-name">{{ item.name }}</div>
<div class="prize-desc flex flex-col flex-center"> <div class="prize-desc flex flex-col flex-center">
<div <div
@@ -49,23 +52,24 @@
const prizes = [ const prizes = [
{ {
money: 'US$5000', money: 'US$5000',
name: 'Grand Prize', name: 'Grand Awards',
desc: ['Cash Award', 'Award Ceritificate', 'Global Media Exposure'] desc: ['Cash Award', 'Award Ceritificate', 'Global Media Exposure']
}, },
{ {
money: 'US$3000', money: 'US$3000',
name: 'First Runner-Up', name: 'Gold Awards',
desc: ['Cash Award', 'Award Ceritificate', 'Global Media Exposure'] desc: ['Cash Award', 'Award Ceritificate', 'Global Media Exposure']
}, },
{ {
money: 'US$2000', money: 'US$2000',
name: 'Second Runner-Up', name: 'Silver Awards',
desc: ['Cash Award', 'Award Ceritificate', 'Global Media Exposure'] desc: ['Cash Award', 'Award Ceritificate', 'Global Media Exposure']
}, },
{ {
money: 'Certification', money: 'Award\nCertification',
name: 'Finalists', name: 'Finalists',
desc: ['Award Ceritificate', 'Global Media Exposure'] desc: ['Award Ceritificate', 'Global Media Exposure'],
smaller: true
} }
] ]
@@ -214,10 +218,22 @@
no-repeat; no-repeat;
background-size: 100% 100%; background-size: 100% 100%;
} }
&.smaller {
.prize-money {
font-size: 3.6rem;
line-height: 3.8rem;
}
}
.prize-money { .prize-money {
font-family: 'PoppinsBold'; font-family: 'PoppinsBold';
font-weight: bold; font-weight: bold;
font-size: 4rem; font-size: 4rem;
white-space: pre-line;
text-align: center;
line-height: 7.6rem;
&.smaller {
font-size: 3.6rem;
}
} }
.prize-name { .prize-name {
font-family: 'PoppinsMedium'; font-family: 'PoppinsMedium';

View File

@@ -32,25 +32,25 @@ const criteriaList = ref([
{ {
icon: criteria1, icon: criteria1,
name: 'Originality', name: 'Originality',
desc: 'Unique perspective and innovative approach to fashion design', desc: 'Unique perspective and\ninnovative approach to\nfashion design',
style: { width: '13rem', height: '17rem' } style: { width: '13rem', height: '17rem' }
}, },
{ {
icon: criteria2, icon: criteria2,
name: 'Creativity', name: 'Creativity',
desc: 'Artistic vision and exceptional design excellence', desc: 'Artistic vision and exceptional\ndesign excellence',
style: { width: '16rem', height: '18rem' } style: { width: '16rem', height: '18rem' }
}, },
{ {
icon: criteria3, icon: criteria3,
name: 'AiDA Integration', name: 'AiDA Integration',
desc: 'Effective application of AI design tools and functions', desc: 'Effective application of\nAiDA functions',
style: { width: '16rem', height: '18rem' } style: { width: '16rem', height: '18rem' }
}, },
{ {
icon: criteria4, icon: criteria4,
name: 'Execution', name: 'Execution',
desc: 'Quality of presentation and technical craftsmanship', desc: 'Quality of presentation and\ntechnical craftsmanship',
style: { width: '18.8rem', height: '18rem' } style: { width: '18.8rem', height: '18rem' }
} }
]) ])
@@ -166,6 +166,7 @@ onBeforeUnmount(() => {
font-size: 2.4rem; font-size: 2.4rem;
color: #e0e0e0; color: #e0e0e0;
text-align: center; text-align: center;
white-space: pre-line;
} }
} }
} }

View File

@@ -1,5 +1,9 @@
<template> <template>
<div class="blocks-list flex" ref="root" :class="{ 'in-view': inView }"> <div
class="blocks-list flex"
ref="root"
:class="{ 'in-view': inView }"
>
<div <div
class="block-item flex flex-col flex-center" class="block-item flex flex-col flex-center"
v-for="(item, idx) in blocksList" v-for="(item, idx) in blocksList"
@@ -17,21 +21,21 @@
import { ref, onMounted, onUnmounted } from 'vue' import { ref, onMounted, onUnmounted } from 'vue'
const blocksList = ref([ const blocksList = ref([
{
number: 'NETWORKING\n OPPORTUNITIES',
label: 'with international\nmedia and designers'
},
{
number: 'INTERNATIONAL\nMEDIA EXPOSE',
label: 'through\nleading outlets'
},
{ {
number: 'UP TO\nUS$9000', number: 'UP TO\nUS$9000',
label: 'in total prize\npool awards' label: 'In total cash prizes'
}, },
{ {
number: 'TRAVEL\NALLOWANCE', number: 'GLOBAL MEDIA EXPOSE',
label: 'for finalists to attend\naward ceremony' label: 'Showcased by top\ninternational media platforms'
},
{
number: 'NETWORKING\n OPPORTUNITIES',
label: 'Build connections with\ndesigners and industry leaders'
},
{
number: 'AWARD CEREMONY\nIN HONG KONG',
label: 'Travel allowance\nprovided for finalists'
} }
]) ])
const root = ref<HTMLElement | null>(null) const root = ref<HTMLElement | null>(null)
@@ -40,7 +44,7 @@
onMounted(() => { onMounted(() => {
io = new IntersectionObserver( io = new IntersectionObserver(
(entries) => { entries => {
for (const entry of entries) { for (const entry of entries) {
if (entry.isIntersecting) { if (entry.isIntersecting) {
// 延迟 0.5s 后触发动画并断开观察 // 延迟 0.5s 后触发动画并断开观察
@@ -112,17 +116,17 @@
/* 当组件进入视口并且等待 0.5s 后,.in-view 会加入根节点,下面规则触发动画 */ /* 当组件进入视口并且等待 0.5s 后,.in-view 会加入根节点,下面规则触发动画 */
.in-view .block-item .number { .in-view .block-item .number {
animation: scaleIn 0.48s cubic-bezier(.2,.9,.2,1) forwards; animation: scaleIn 0.48s cubic-bezier(0.2, 0.9, 0.2, 1) forwards;
animation-delay: var(--delay); animation-delay: var(--delay);
} }
.in-view .block-item .label { .in-view .block-item .label {
animation: scaleIn 0.48s cubic-bezier(.2,.9,.2,1) forwards; animation: scaleIn 0.48s cubic-bezier(0.2, 0.9, 0.2, 1) forwards;
animation-delay: calc(var(--delay) + 0.12s); animation-delay: calc(var(--delay) + 0.12s);
} }
.in-view .block-item .line { .in-view .block-item .line {
animation: growLine 0.7s cubic-bezier(.2,.9,.2,1) forwards; animation: growLine 0.7s cubic-bezier(0.2, 0.9, 0.2, 1) forwards;
animation-delay: calc(var(--delay) + 0.18s); animation-delay: calc(var(--delay) + 0.18s);
} }

View File

@@ -35,7 +35,7 @@
return { return {
icon: expiredIcon, icon: expiredIcon,
title: 'Application Deadline Passed', title: 'Application Deadline Passed',
desc: 'The submission deadline for AIDA Global Fashion Award 2026 has ended.\nWe are no longer accepting new applications. ' desc: 'The submission deadline for AiDA Global Fashion Award 2026 has ended.\nWe are no longer accepting new applications. '
} }
} else { } else {
return { return {

View File

@@ -5,10 +5,14 @@
> >
<div class="timeline-title">Competition Timeline</div> <div class="timeline-title">Competition Timeline</div>
<div class="desc">Shaping the Future</div> <div class="desc">Shaping the Future</div>
<div class="timeline-point"> <div
<div class="labels-row flex align-center"> class="timeline-point"
ref="timelineRef"
>
<!-- 顶部标签行 -->
<div class="grid-row labels-row">
<div <div
class="item-label flex flex-col" class="grid-cell label-cell"
v-for="item in points" v-for="item in points"
:key="'label-' + item.time" :key="'label-' + item.time"
> >
@@ -21,31 +25,35 @@
</div> </div>
</div> </div>
</div> </div>
<!-- Icons row --> <!-- 图标行 -->
<div class="icons-row flex align-center"> <div class="grid-row icons-row">
<div class="timeline-line"></div> <div class="timeline-line"></div>
<img <div
src="@/assets/images/award/point.png" class="grid-cell icon-cell"
class="point-icon"
v-for="item in points" v-for="item in points"
:key="'icon-' + item.time" :key="'icon-' + item.time"
/> >
<img
src="@/assets/images/award/point.png"
class="point-icon"
/>
</div>
</div> </div>
<!-- 时间行 -->
<!-- Times row --> <div class="grid-row times-row">
<div class="times-row flex align-center">
<div <div
class="item-time" class="grid-cell time-cell"
v-for="item in points" v-for="item in points"
:key="'time-' + item.time" :key="'time-' + item.time"
> >
{{ item.time }} {{ item.time }}
</div> </div>
</div> </div>
<!-- Descriptions row -->
<div class="descs-row flex align-center"> <!-- 描述行 -->
<div class="grid-row descs-row">
<div <div
class="item-desc flex justify-center" class="grid-cell desc-cell"
v-for="item in points" v-for="item in points"
:key="'desc-' + item.time" :key="'desc-' + item.time"
> >
@@ -63,30 +71,39 @@
import { gsap } from 'gsap' import { gsap } from 'gsap'
const containerRef = ref<HTMLElement | null>(null) const containerRef = ref<HTMLElement | null>(null)
const timelineRef = ref<HTMLElement | null>(null)
const hasAnimated = ref(false) const hasAnimated = ref(false)
const points = ref([ const points = ref([
{ {
label: 'Select Top 20', label: 'Application',
time: 'May', subLabel: 'Deadline',
desc: 'Submit your design concept, mood board, and initial sketch.' time: 'Jul 15',
desc: 'Application deadline and\nentry review process\nbegins.'
}, },
{ {
label: `Top 20`, label: `20 Finallists`,
subLabel: 'Collections Finalize', subLabel: 'Announced',
time: 'June', time: 'Aug 30',
desc: 'Complete collections, physical garments, and AiDA process videos due.' desc: 'Announcement of 20\nfinalists entering final\nevaluation stage.'
}, },
{ {
label: `Top 3`, label: `Finallist\nSubmission`,
subLabel: 'Finalists Select', subLabel: 'Deadline',
time: 'August', time: 'Sept 30',
desc: 'Complete collections, physical garments, and AiDA process videos due.' desc: 'Finalists submit\ncompleted outfits for\nfinal assessment.'
}, },
{ {
label: 'Award Ceremony', label: 'Receiving Outfits',
time: 'November', subLabel: 'from Finallists',
desc: 'Winners revealed with media coverage and live showcase.' time: 'October',
desc: 'AiDA receives physical\noutfits from all 20\nfinalists.'
},
{
label: 'Award',
subLabel: 'Ceremony',
time: 'Nov 12',
desc: 'Award Ceremony &\nCommunity Gathering\n Soho House.'
} }
]) ])
@@ -98,17 +115,12 @@
const timeline = containerRef.value.querySelector('.timeline-point') const timeline = containerRef.value.querySelector('.timeline-point')
const tl = gsap.timeline() const tl = gsap.timeline()
if (title && subtitle) {
tl.from([title, subtitle], {
scaleX: 0,
autoAlpha: 0,
transformOrigin: '50% 50%',
duration: 0.6,
stagger: 0.1,
ease: 'power2.out'
})
}
// 我们使用一个统一的开始 label使横线、timeline 裁剪与所有文字同时启动,
// 点图标在它们完成后立即开始。
tl.addLabel('start')
// 整体 timeline 的裁剪展开(与 start 同步)
if (timeline) { if (timeline) {
tl.fromTo( tl.fromTo(
timeline, timeline,
@@ -117,12 +129,14 @@
}, },
{ {
clipPath: 'inset(0 0% 0 0)', clipPath: 'inset(0 0% 0 0)',
duration: 1.6, duration: 1.3,
ease: 'power1.out' ease: 'power1.out'
} },
'start'
) )
} }
// 线条动画(与 start 同步)
if (line) { if (line) {
tl.from( tl.from(
line, line,
@@ -132,7 +146,38 @@
duration: 1.3, duration: 1.3,
ease: 'power1.out' ease: 'power1.out'
}, },
'-=1.1' 'start'
)
}
// 标题与副标题(与 start 同步)
if (title && subtitle) {
tl.from(
[title, subtitle],
{
scaleX: 0,
autoAlpha: 0.5,
transformOrigin: '50% 50%',
duration: 0.6,
stagger: 0.1,
ease: 'power2.out'
},
'start'
)
}
// 行内文字(标签、时间、描述、图标)与 start 同步开始
const textItems = containerRef.value.querySelectorAll('.grid-cell')
if (textItems && textItems.length) {
tl.from(
textItems,
{
// autoAlpha: 0.5,
duration: 0.7,
stagger: 0.08,
ease: 'power2.out'
},
'start'
) )
} }
@@ -145,8 +190,8 @@
await nextTick() await nextTick()
if (!containerRef.value) return if (!containerRef.value) return
observer = new IntersectionObserver( observer = new IntersectionObserver(
(entries) => { entries => {
entries.forEach((entry) => { entries.forEach(entry => {
if (entry.isIntersecting) { if (entry.isIntersecting) {
playAnimation() playAnimation()
} }
@@ -170,7 +215,7 @@
background: url('@/assets/images/award/timeline_bg.png') no-repeat; background: url('@/assets/images/award/timeline_bg.png') no-repeat;
background-size: 100% 100%; background-size: 100% 100%;
position: relative; position: relative;
padding-top: 12.8rem; padding: 12.8rem 0 15.9rem;
width: 100%; width: 100%;
color: #fff; color: #fff;
.timeline-title { .timeline-title {
@@ -194,42 +239,45 @@
will-change: clip-path; will-change: clip-path;
flex: 1; flex: 1;
width: 100%; width: 100%;
margin-top: 12rem; margin-top: 11rem;
padding: 0 21.2rem 0 22rem; padding: 0 13.8rem;
position: relative; position: relative;
z-index: 2; z-index: 2;
.labels-row { // 主网格布局5列
position: relative; display: grid;
z-index: 2; grid-template-columns: repeat(5, 1fr);
margin-bottom: 8rem; grid-template-rows: auto auto auto auto;
.item-label { grid-column-gap: 0;
flex: 1; grid-row-gap: 0;
color: #fff;
font-family: 'PoppinsBold'; // 所有 grid 子行的通用样式
font-weight: 600; .grid-row {
font-size: 2.8rem; display: grid;
text-align: center; grid-template-columns: repeat(5, 1fr);
white-space: pre-line; grid-column: 1 / -1;
height: 6rem;
justify-content: center;
}
} }
.grid-cell {
display: flex;
justify-content: center;
align-items: center;
text-align: center;
}
// 图标行
.icons-row { .icons-row {
margin-bottom: 1.6rem; align-items: center;
height: 6.4rem;
position: relative; position: relative;
z-index: 2; z-index: 2;
.point-icon { margin-bottom: 1.6rem;
width: 6.4rem;
height: 6.4rem;
display: block;
margin: 0 auto;
z-index: 2;
}
.timeline-line { .timeline-line {
width: calc(100% + 22rem + 21.2rem); position: absolute;
top: 50%;
left: -22rem; left: -22rem;
right: -21.2rem;
height: 0.15rem; height: 0.15rem;
background: linear-gradient( background: linear-gradient(
90deg, 90deg,
@@ -239,39 +287,78 @@
rgba(199, 52, 44, 0.762376) 75.96%, rgba(199, 52, 44, 0.762376) 75.96%,
rgba(199, 52, 44, 0) 100% rgba(199, 52, 44, 0) 100%
); );
position: absolute;
bottom: 50%;
transform: translateY(-50%); transform: translateY(-50%);
z-index: 1; z-index: 1;
pointer-events: none;
}
.icon-cell {
position: relative;
.point-icon {
width: 6.4rem;
height: 6.4rem;
display: block;
position: relative;
z-index: 2;
}
} }
} }
// 标签行
.labels-row {
margin-bottom: 8rem;
position: relative;
z-index: 2;
.label-cell {
flex-direction: column;
color: #fff;
font-family: 'PoppinsBold';
font-weight: 600;
font-size: 2.8rem;
white-space: pre-line;
justify-content: center;
min-height: 6rem;
// .sub-label {
// font-family: 'Arial';
// font-weight: 400;
// font-size: 1.4rem;
// color: rgba(255, 255, 255, 0.8);
// margin-top: 0.4rem;
// }
}
}
// 时间行
.times-row { .times-row {
margin-bottom: 6rem; margin-bottom: 6rem;
z-index: 2; z-index: 2;
position: relative; position: relative;
.item-time { .time-cell {
flex: 1;
color: #f95750; color: #f95750;
font-family: 'Arial'; font-family: 'Arial';
font-weight: 400; font-weight: 400;
font-size: 2.8rem; font-size: 2.8rem;
line-height: 4.5rem; line-height: 4.5rem;
text-align: center;
} }
} }
// 描述行
.descs-row { .descs-row {
.item-desc { .desc-cell {
flex: 1;
.txt { .txt {
font-family: 'Arial'; font-family: 'Arial';
font-weight: 400; font-weight: 400;
font-size: 2rem; font-size: 2rem;
text-align: center; text-align: center;
color: #e0e0e0; color: #e0e0e0;
width: 31.2rem; width: 100%;
height: 10.2rem; max-width: 31.2rem;
min-height: 10.2rem;
white-space: pre-line;
display: flex;
align-items: center;
justify-content: center;
} }
} }
} }

View File

@@ -1,23 +1,26 @@
<template> <template>
<div class="award-container"> <div class="award-container">
<div class="header flex align-center space-between"> <div class="header-wrapper">
<div class="header-left"> <div class="header flex align-center space-between">
<img <div class="header-left">
src="@/assets/images/award/code_create_logo.png" <img
class="logo" src="@/assets/images/award/code_create_logo.png"
/> class="logo"
</div> />
<div </div>
class="header-right flex align-center" <div
@click="handleBtnClick" class="header-right flex align-center"
> @click="handleBtnClick"
<div class="text">{{ btnText }}</div> >
<img <div class="text">{{ btnText }}</div>
src="@/assets/images/award/arrow.png" <img
alt="" src="@/assets/images/award/arrow.png"
class="arrow" alt=""
/> class="arrow"
/>
</div>
</div> </div>
<div class="header-placeholder"></div>
</div> </div>
<router-view /> <router-view />
<div class="footer flex space-between align-center"> <div class="footer flex space-between align-center">
@@ -142,6 +145,7 @@
height: 100vh; height: 100vh;
// 隐藏滚动条箭头,只显示滚动条本体 // 隐藏滚动条箭头,只显示滚动条本体
box-sizing: border-box; box-sizing: border-box;
&::-webkit-scrollbar { &::-webkit-scrollbar {
display: none; display: none;
width: 0; width: 0;
@@ -150,28 +154,37 @@
scrollbar-width: none; scrollbar-width: none;
-ms-overflow-style: none; -ms-overflow-style: none;
} }
.header { .header-wrapper {
height: 8rem; .header-placeholder {
background-color: #232323; height: 8rem;
padding-left: 21.5rem;
padding-right: 8.6rem;
box-sizing: border-box;
.header-left {
.logo {
width: 13rem;
height: 5rem;
}
} }
.header-right { .header {
column-gap: 1rem; height: 8rem;
cursor: pointer; background-color: #232323;
.text { padding-left: 21.5rem;
font-size: 1.6rem; padding-right: 8.6rem;
color: #fff; box-sizing: border-box;
position: fixed;
top: 0;
width: 100%;
z-index: 9;
.header-left {
.logo {
width: 13rem;
height: 5rem;
}
} }
.arrow { .header-right {
width: 2.4rem; column-gap: 1rem;
height: 2.4rem; cursor: pointer;
.text {
font-size: 1.6rem;
color: #fff;
}
.arrow {
width: 2.4rem;
height: 2.4rem;
}
} }
} }
} }

View File

@@ -1,6 +1,16 @@
<template> <template>
<div class="award-page"> <div class="award-page">
<div class="banner"> <div class="banner">
<video
src="@/assets/images/award/banner.mp4"
autoplay
muted
loop
class="banner-video"
playsinline
webkit-playsinline
x5-playsinline
></video>
<div <div
class="submit-btn flex flex-center" class="submit-btn flex flex-center"
@click="handleSubmitApplication" @click="handleSubmitApplication"
@@ -53,10 +63,15 @@
height: 2.4rem; height: 2.4rem;
} }
.banner { .banner {
height: 108rem; height: 100rem;
background: url('@/assets/images/award/banner.png') no-repeat; // background: url('@/assets/images/award/banner.png') no-repeat;
background-size: cover; // background-size: cover;
position: relative; position: relative;
.banner-video {
width: 100%;
height: 100%;
object-fit: cover;
}
.submit-btn { .submit-btn {
width: 41rem; width: 41rem;
height: 6.394rem; height: 6.394rem;
@@ -91,7 +106,7 @@
font-weight: 400; font-weight: 400;
font-size: 2rem; font-size: 2rem;
line-height: 2.2rem; line-height: 2.2rem;
color: #232323E5; color: #232323e5;
} }
} }
} }